tosijs-3d 0.7.2 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +900 -0
- package/dist/b3d-aircraft.d.ts +14 -0
- package/dist/b3d-aircraft.d.ts.map +1 -1
- package/dist/b3d-aircraft.js +45 -0
- package/dist/b3d-aircraft.js.map +1 -1
- package/dist/b3d-ambient.d.ts +12 -0
- package/dist/b3d-ambient.d.ts.map +1 -1
- package/dist/b3d-ambient.js +83 -1
- package/dist/b3d-ambient.js.map +1 -1
- package/dist/b3d-biped.d.ts +290 -0
- package/dist/b3d-biped.d.ts.map +1 -1
- package/dist/b3d-biped.js +1366 -22
- package/dist/b3d-biped.js.map +1 -1
- package/dist/b3d-collisions.d.ts.map +1 -1
- package/dist/b3d-collisions.js +45 -7
- package/dist/b3d-collisions.js.map +1 -1
- package/dist/b3d-controllable.d.ts +50 -0
- package/dist/b3d-controllable.d.ts.map +1 -1
- package/dist/b3d-controllable.js +61 -0
- package/dist/b3d-controllable.js.map +1 -1
- package/dist/b3d-death.d.ts +26 -0
- package/dist/b3d-death.d.ts.map +1 -1
- package/dist/b3d-death.js +224 -2
- package/dist/b3d-death.js.map +1 -1
- package/dist/b3d-interactive.d.ts +80 -0
- package/dist/b3d-interactive.d.ts.map +1 -0
- package/dist/b3d-interactive.js +284 -0
- package/dist/b3d-interactive.js.map +1 -0
- package/dist/b3d-library.d.ts +39 -0
- package/dist/b3d-library.d.ts.map +1 -1
- package/dist/b3d-library.js +67 -0
- package/dist/b3d-library.js.map +1 -1
- package/dist/b3d-svg-plane.d.ts +35 -0
- package/dist/b3d-svg-plane.d.ts.map +1 -1
- package/dist/b3d-svg-plane.js +96 -8
- package/dist/b3d-svg-plane.js.map +1 -1
- package/dist/b3d-water.d.ts.map +1 -1
- package/dist/b3d-water.js +32 -6
- package/dist/b3d-water.js.map +1 -1
- package/dist/buoyancy.d.ts +90 -0
- package/dist/buoyancy.d.ts.map +1 -0
- package/dist/buoyancy.js +150 -0
- package/dist/buoyancy.js.map +1 -0
- package/dist/curve-field.d.ts +39 -0
- package/dist/curve-field.d.ts.map +1 -0
- package/dist/curve-field.js +491 -0
- package/dist/curve-field.js.map +1 -0
- package/dist/curve.d.ts +241 -0
- package/dist/curve.d.ts.map +1 -0
- package/dist/curve.js +559 -0
- package/dist/curve.js.map +1 -0
- package/dist/dialog-placement.d.ts +69 -7
- package/dist/dialog-placement.d.ts.map +1 -1
- package/dist/dialog-placement.js +90 -9
- package/dist/dialog-placement.js.map +1 -1
- package/dist/embed-font.d.ts +48 -0
- package/dist/embed-font.d.ts.map +1 -0
- package/dist/embed-font.js +135 -0
- package/dist/embed-font.js.map +1 -0
- package/dist/footprint-field.d.ts +30 -0
- package/dist/footprint-field.d.ts.map +1 -0
- package/dist/footprint-field.js +256 -0
- package/dist/footprint-field.js.map +1 -0
- package/dist/frame-panel.d.ts.map +1 -1
- package/dist/frame-panel.js +15 -12
- package/dist/frame-panel.js.map +1 -1
- package/dist/glb-manifest.d.ts +75 -0
- package/dist/glb-manifest.d.ts.map +1 -0
- package/dist/glb-manifest.js +198 -0
- package/dist/glb-manifest.js.map +1 -0
- package/dist/icon-data.d.ts.map +1 -1
- package/dist/icon-data.js +20 -1
- package/dist/icon-data.js.map +1 -1
- package/dist/index.d.ts +33 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -3
- package/dist/index.js.map +1 -1
- package/dist/interaction.d.ts +66 -0
- package/dist/interaction.d.ts.map +1 -0
- package/dist/interaction.js +117 -0
- package/dist/interaction.js.map +1 -0
- package/dist/interactive-behavior.d.ts +105 -0
- package/dist/interactive-behavior.d.ts.map +1 -0
- package/dist/interactive-behavior.js +253 -0
- package/dist/interactive-behavior.js.map +1 -0
- package/dist/key-layout.d.ts +70 -0
- package/dist/key-layout.d.ts.map +1 -1
- package/dist/key-layout.js +122 -0
- package/dist/key-layout.js.map +1 -1
- package/dist/keyboard-gamepad.d.ts.map +1 -1
- package/dist/keyboard-gamepad.js +19 -6
- package/dist/keyboard-gamepad.js.map +1 -1
- package/dist/keyboard.d.ts +100 -1
- package/dist/keyboard.d.ts.map +1 -1
- package/dist/keyboard.js +280 -35
- package/dist/keyboard.js.map +1 -1
- package/dist/mantle.d.ts +56 -0
- package/dist/mantle.d.ts.map +1 -0
- package/dist/mantle.js +115 -0
- package/dist/mantle.js.map +1 -0
- package/dist/popup-surface.d.ts +5 -1
- package/dist/popup-surface.d.ts.map +1 -1
- package/dist/popup-surface.js +181 -14
- package/dist/popup-surface.js.map +1 -1
- package/dist/surface.d.ts.map +1 -1
- package/dist/surface.js +111 -15
- package/dist/surface.js.map +1 -1
- package/dist/svg-icons.d.ts.map +1 -1
- package/dist/svg-icons.js +13 -1
- package/dist/svg-icons.js.map +1 -1
- package/dist/svg-texture.d.ts.map +1 -1
- package/dist/svg-texture.js +40 -1
- package/dist/svg-texture.js.map +1 -1
- package/dist/swim-aim.d.ts +78 -0
- package/dist/swim-aim.d.ts.map +1 -0
- package/dist/swim-aim.js +140 -0
- package/dist/swim-aim.js.map +1 -0
- package/dist/theme-editor.d.ts +41 -0
- package/dist/theme-editor.d.ts.map +1 -0
- package/dist/theme-editor.js +303 -0
- package/dist/theme-editor.js.map +1 -0
- package/dist/tosi-b3d.d.ts +24 -4
- package/dist/tosi-b3d.d.ts.map +1 -1
- package/dist/tosi-b3d.js +238 -37
- package/dist/tosi-b3d.js.map +1 -1
- package/dist/vector-field.d.ts +58 -0
- package/dist/vector-field.d.ts.map +1 -0
- package/dist/vector-field.js +321 -0
- package/dist/vector-field.js.map +1 -0
- package/dist/virtual-gamepad.d.ts.map +1 -1
- package/dist/virtual-gamepad.js +46 -9
- package/dist/virtual-gamepad.js.map +1 -1
- package/dist/w3d-theme.d.ts +115 -17
- package/dist/w3d-theme.d.ts.map +1 -1
- package/dist/w3d-theme.js +311 -0
- package/dist/w3d-theme.js.map +1 -1
- package/dist/water-normal.d.ts +26 -0
- package/dist/water-normal.d.ts.map +1 -0
- package/dist/water-normal.js +105 -0
- package/dist/water-normal.js.map +1 -0
- package/dist/widgets3d-layout.d.ts +58 -0
- package/dist/widgets3d-layout.d.ts.map +1 -1
- package/dist/widgets3d-layout.js +79 -1
- package/dist/widgets3d-layout.js.map +1 -1
- package/dist/widgets3d.d.ts +51 -2
- package/dist/widgets3d.d.ts.map +1 -1
- package/dist/widgets3d.js +418 -112
- package/dist/widgets3d.js.map +1 -1
- package/dist/wreck-fall.d.ts +86 -0
- package/dist/wreck-fall.d.ts.map +1 -0
- package/dist/wreck-fall.js +129 -0
- package/dist/wreck-fall.js.map +1 -0
- package/package.json +9 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buoyancy.js","sourceRoot":"","sources":["../src/buoyancy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkCE;AACF,2BAA2B;AAsB3B;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAa,EACb,MAAc,EACd,QAAgB;IAEhB,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO,KAAK,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACjD,MAAM,KAAK,GAAG,CAAC,QAAQ,GAAG,KAAK,CAAC,GAAG,MAAM,CAAA;IACzC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;AAChD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CACzB,EAAU,EACV,SAAiB,EACjB,EAAU,EACV,SAAyB,EAAE;IAE3B,MAAM,EACJ,OAAO,GAAG,CAAC,IAAI,EACf,QAAQ,GAAG,IAAI,EACf,SAAS,GAAG,CAAC,EACb,OAAO,GAAG,IAAI,EACd,MAAM,GAAG,CAAC,GACX,GAAG,MAAM,CAAA;IACV,IAAI,EAAE,IAAI,CAAC;QAAE,OAAO,EAAE,CAAA;IACtB,MAAM,CAAC,GAAG,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAC7D,8EAA8E;IAC9E,8EAA8E;IAC9E,4CAA4C;IAC5C,MAAM,KAAK,GAAG,OAAO,GAAG,CAAC,OAAO,GAAG,QAAQ,GAAG,CAAC,GAAG,MAAM,GAAG,CAAC,CAAA;IAC5D,MAAM,IAAI,GAAG,OAAO,GAAG,CAAC,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;IAChD,MAAM,IAAI,GAAG,EAAE,GAAG,KAAK,GAAG,EAAE,CAAA;IAC5B,6EAA6E;IAC7E,2DAA2D;IAC3D,OAAO,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,EAAE,CAAA;AACjD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAQ,GAAG,IAAI;IACnD,OAAO,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAA;AACzC,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,UAAU,CACxB,SAAiB,EACjB,cAAuB,EACvB,WAAW,GAAG,KAAK;IAEnB,IAAI,cAAc;QAAE,OAAO,KAAK,CAAA;IAChC;;;;;;;;;;;;;;;MAeE;IACF,OAAO,SAAS,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;AAChD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,YAAY,CAC1B,SAAiB,EACjB,SAAkE,EAAE;IAEpE,MAAM,EAAE,QAAQ,GAAG,IAAI,EAAE,OAAO,GAAG,IAAI,EAAE,KAAK,GAAG,GAAG,EAAE,GAAG,MAAM,CAAA;IAC/D,IAAI,SAAS,IAAI,CAAC;QAAE,OAAO,QAAQ,CAAA;IACnC,MAAM,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC,CAAA;IACzD,OAAO,QAAQ,GAAG,CAAC,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAA;AAC5C,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type ControlPoint, type CurveKind } from './curve';
|
|
2
|
+
import type { Widget3d } from './widgets3d';
|
|
3
|
+
export interface Curve3dOptions {
|
|
4
|
+
/** `profile` (both ends free) or `falloff` (pinned to 0 at x = 1). */
|
|
5
|
+
kind?: CurveKind;
|
|
6
|
+
/** Starting points, or the name of a preset (`'constant'`, `'ease in'`, …). */
|
|
7
|
+
value?: ControlPoint[] | string;
|
|
8
|
+
/** Caption drawn above the plot. */
|
|
9
|
+
label?: string;
|
|
10
|
+
/** Plot height as a fraction of width. Default 0.62. */
|
|
11
|
+
aspect?: number;
|
|
12
|
+
/** Fired after any edit that changes the curve. */
|
|
13
|
+
onChange?: (points: ControlPoint[]) => void;
|
|
14
|
+
}
|
|
15
|
+
export interface CurveField extends Widget3d {
|
|
16
|
+
readonly points: ControlPoint[];
|
|
17
|
+
setPoints: (p: ControlPoint[]) => void;
|
|
18
|
+
/** Sample it — what a consumer actually wants. */
|
|
19
|
+
evaluate: (t: number) => number;
|
|
20
|
+
/** Index of the selected point, or -1. */
|
|
21
|
+
readonly selected: number;
|
|
22
|
+
/** Remove the selected point. Ends and the last two are protected. */
|
|
23
|
+
deleteSelected: () => void;
|
|
24
|
+
/** Apply a preset by name; unknown names are ignored. */
|
|
25
|
+
applyPreset: (name: string) => void;
|
|
26
|
+
/** Settable so a demo can wire it after construction. */
|
|
27
|
+
onChange?: (points: ControlPoint[]) => void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* An editable curve.
|
|
31
|
+
*
|
|
32
|
+
* ```js
|
|
33
|
+
* const falloff = curve3d({ kind: 'falloff', label: 'falloff' })
|
|
34
|
+
* falloff.onChange = () => rebuildTerrain()
|
|
35
|
+
* falloff.evaluate(0.5)
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare function curve3d(config?: Curve3dOptions): CurveField;
|
|
39
|
+
//# sourceMappingURL=curve-field.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"curve-field.d.ts","sourceRoot":"","sources":["../src/curve-field.ts"],"names":[],"mappings":"AA4NA,OAAO,EASL,KAAK,YAAY,EACjB,KAAK,SAAS,EACf,MAAM,SAAS,CAAA;AAEhB,OAAO,KAAK,EAAe,QAAQ,EAAE,MAAM,aAAa,CAAA;AAIxD,MAAM,WAAW,cAAc;IAC7B,sEAAsE;IACtE,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,+EAA+E;IAC/E,KAAK,CAAC,EAAE,YAAY,EAAE,GAAG,MAAM,CAAA;IAC/B,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,mDAAmD;IACnD,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,IAAI,CAAA;CAC5C;AAED,MAAM,WAAW,UAAW,SAAQ,QAAQ;IAC1C,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,CAAA;IAC/B,SAAS,EAAE,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,IAAI,CAAA;IACtC,kDAAkD;IAClD,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IAC/B,0CAA0C;IAC1C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,sEAAsE;IACtE,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B,yDAAyD;IACzD,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,yDAAyD;IACzD,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,IAAI,CAAA;CAC5C;AAeD;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,MAAM,GAAE,cAAmB,GAAG,UAAU,CA2Q/D"}
|
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
/*#
|
|
2
|
+
# curve-field
|
|
3
|
+
|
|
4
|
+
**`curve3d` — an editor for a continuous `[0,1] → [0,1]`.** A `Widget3d`, so it
|
|
5
|
+
works as a flat DOM overlay, on an in-scene panel, and in a headset without
|
|
6
|
+
changing. The rules live in [[curve|curve.ts]]; this draws them and routes the
|
|
7
|
+
pointer.
|
|
8
|
+
|
|
9
|
+
Its reason for existing is terrain **provinces**: a province is a footprint plus
|
|
10
|
+
one curve per layer, so this is how you author a plateau, a crater rim or a
|
|
11
|
+
treeline without writing code. See `PROVINCE-DESIGN.md` → "every one of those
|
|
12
|
+
responses IS a curve", and the [[b3d-terrain|province editor]] demo below.
|
|
13
|
+
|
|
14
|
+
## Deleting a point is a BUTTON, not a gesture
|
|
15
|
+
|
|
16
|
+
Adding is a tap on empty space and moving is a drag, which leaves deleting with
|
|
17
|
+
no obvious third gesture. The usual answers all fail somewhere that matters
|
|
18
|
+
here: right-click does not exist on a controller, double-tap is unreliable when
|
|
19
|
+
the pointer is a ray from two metres away, and drag-off-the-edge collides with
|
|
20
|
+
the clamp that keeps the curve in range.
|
|
21
|
+
|
|
22
|
+
So the widget exposes `selected` and `deleteSelected()`, and the host puts a
|
|
23
|
+
button somewhere honest. A discoverable button beats a gesture you have to be
|
|
24
|
+
told about — which is the same argument the popup title bar makes for its grip.
|
|
25
|
+
|
|
26
|
+
## Demo — a province editor
|
|
27
|
+
|
|
28
|
+
Two curves and a terrain block. The **shape** says what height the province wants
|
|
29
|
+
at each distance from its centre; the **falloff** says how strongly it overrides
|
|
30
|
+
the terrain around it. Drag the points, or pick a preset.
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
import { b3d, b3dLight, panel3d, label3d, button3d, toggle3d, curve3d, footprint3d, slider3d, select3d, presetsFor, PerlinNoise, attachBiomePlugin, blendSample } from 'tosijs-3d'
|
|
34
|
+
import { orbitCam } from 'tosijs-3d/demo-utils'
|
|
35
|
+
import { elements } from 'tosijs'
|
|
36
|
+
const { div } = elements
|
|
37
|
+
|
|
38
|
+
const SIZE = 24 // metres across
|
|
39
|
+
const SUBS = 110 // grid resolution
|
|
40
|
+
// `height` scales the whole BLOCK, not the province inside it — see `rebuild`.
|
|
41
|
+
// 4.5 over a 24 m tile: the field now spans the whole block, so the old 9 (tuned
|
|
42
|
+
// when the base was squeezed into a third of the range) came out as alps.
|
|
43
|
+
const state = { height: 4.5, extent: 0.7, noise: 1 }
|
|
44
|
+
|
|
45
|
+
// A province is a footprint plus one curve per layer. SHAPE says what height it
|
|
46
|
+
// wants at each distance from its centre; FALLOFF says how strongly it overrides
|
|
47
|
+
// the terrain around it. Both are [0,1] -> [0,1] over normalised distance: 0 at
|
|
48
|
+
// the centre, 1 at the extent.
|
|
49
|
+
// SHAPE is a levels adjustment: it maps the height SAMPLE to a height, exactly
|
|
50
|
+
// like slope-profile's cliff/beach/mesa. It starts as the IDENTITY, so a fresh
|
|
51
|
+
// province is invisible — drag it off the diagonal and the terrain responds.
|
|
52
|
+
// Try `constant` (flattens whatever is there: a plateau) or drag it the other
|
|
53
|
+
// way up (maps low ground high, which lifts the whole province).
|
|
54
|
+
const shape = curve3d({ kind: 'profile', label: 'shape — remaps the height sample', value: 'no change', aspect: 0.45 })
|
|
55
|
+
const falloff = curve3d({ kind: 'falloff', label: 'falloff — weight by distance', aspect: 0.45 })
|
|
56
|
+
// The FOOTPRINT, edited as the shape it is rather than as extent-against-angle
|
|
57
|
+
// on a graph: a hexagon looks like a hexagon, and a corner is where the corner
|
|
58
|
+
// is. The square is the province's bounds.
|
|
59
|
+
const footprint = footprint3d({ value: 'hexagon', label: 'footprint — drag the corners' })
|
|
60
|
+
|
|
61
|
+
let ground = null
|
|
62
|
+
let biome = null
|
|
63
|
+
|
|
64
|
+
// Base terrain: seeded fBm, NORMALISED to [0,1] like everything else here.
|
|
65
|
+
//
|
|
66
|
+
// Four octaves rather than a couple of sine waves — smooth ground gives the eye
|
|
67
|
+
// no scale reference, so the province's edge has nothing to be crisp against and
|
|
68
|
+
// the whole thing reads as a bulge in a bedsheet. Detail in the base is what
|
|
69
|
+
// makes the blend legible.
|
|
70
|
+
const noise = new PerlinNoise(1337)
|
|
71
|
+
const fbm = (x, z) => {
|
|
72
|
+
let sum = 0, amp = 1, freq = 0.055 * state.noise, norm = 0
|
|
73
|
+
for (let o = 0; o < 4; o++) {
|
|
74
|
+
sum += noise.noise2D(x * freq, z * freq) * amp
|
|
75
|
+
norm += amp
|
|
76
|
+
amp *= 0.5
|
|
77
|
+
freq *= 2.1 // not exactly 2, so octaves do not line up into visible grain
|
|
78
|
+
}
|
|
79
|
+
return sum / norm // roughly [-1, 1]
|
|
80
|
+
}
|
|
81
|
+
// The FULL [0,1], because that is the shape curve's DOMAIN.
|
|
82
|
+
//
|
|
83
|
+
// This was banded to 0.12 … 0.5 for legibility, which quietly broke the remap:
|
|
84
|
+
// the editor offers you the whole domain while the terrain only ever asks about
|
|
85
|
+
// the bottom third, so a threshold drawn at 0.5 answered 0 for every sample and
|
|
86
|
+
// the province went flat. Tonio: "[0,0]-[0.5,0][0.5,1]-[1,1] does NOT work as
|
|
87
|
+
// expected" — the curve was right and could not be reached.
|
|
88
|
+
//
|
|
89
|
+
// A control whose input range does not match its data is worse than a coarse
|
|
90
|
+
// one, because it fails silently and looks like the model being wrong.
|
|
91
|
+
// …and normalised against its OWN measured range, not against fBm's theoretical
|
|
92
|
+
// one. Four octaves of Perlin almost never reach +/-1, so `fbm * 0.5 + 0.5`
|
|
93
|
+
// spans about 0.28 to 0.62 — the same domain mismatch, just smaller and harder
|
|
94
|
+
// to notice. Measuring the field costs one extra pass over a grid we are
|
|
95
|
+
// building anyway.
|
|
96
|
+
const base = (x, z) => fbm(x, z)
|
|
97
|
+
const normalise = (raw, lo, hi) => (hi - lo < 1e-6 ? 0.5 : (raw - lo) / (hi - lo))
|
|
98
|
+
|
|
99
|
+
const rebuild = () => {
|
|
100
|
+
if (ground == null) return
|
|
101
|
+
// Read x/z back out of the buffer and write y: order-independent, so it does
|
|
102
|
+
// not matter how CreateGround laid the grid out.
|
|
103
|
+
const pos = ground.getVerticesData('position')
|
|
104
|
+
const reach = SIZE * 0.5 * state.extent
|
|
105
|
+
// Pass one: the raw field and its extremes, so the shape curve's [0,1] domain
|
|
106
|
+
// maps onto terrain that actually spans [0,1].
|
|
107
|
+
const raw = new Float32Array(pos.length / 3)
|
|
108
|
+
let lo = Infinity
|
|
109
|
+
let hi = -Infinity
|
|
110
|
+
for (let i = 0, k = 0; i < pos.length; i += 3, k++) {
|
|
111
|
+
raw[k] = base(pos[i], pos[i + 2])
|
|
112
|
+
if (raw[k] < lo) lo = raw[k]
|
|
113
|
+
if (raw[k] > hi) hi = raw[k]
|
|
114
|
+
}
|
|
115
|
+
for (let i = 0, k = 0; i < pos.length; i += 3, k++) {
|
|
116
|
+
const x = pos[i], z = pos[i + 2]
|
|
117
|
+
// Direction first: the footprint says how far the province reaches THIS way,
|
|
118
|
+
// and distance is normalised against that. Direction lives in the footprint;
|
|
119
|
+
// response lives in the other two curves.
|
|
120
|
+
const theta = (Math.atan2(z, x) / (Math.PI * 2) + 1) % 1
|
|
121
|
+
const spread = Math.max(0.05, footprint.evaluate(theta))
|
|
122
|
+
const r = Math.min(1, Math.hypot(x, z) / (reach * spread))
|
|
123
|
+
const w = falloff.evaluate(r)
|
|
124
|
+
// THE HEIGHT SAMPLE goes through the shape curve — a levels adjustment, not
|
|
125
|
+
// a function of distance. Tonio: "shape isn't working properly. It's being
|
|
126
|
+
// treated as an output constant NOT as a map from height field to terrain
|
|
127
|
+
// height." It was `shape.evaluate(r)`, which made a profile into a second
|
|
128
|
+
// radial curve and quietly threw away the terrain underneath it.
|
|
129
|
+
//
|
|
130
|
+
// The falloff still works on DISTANCE — that is the split: what the province
|
|
131
|
+
// does to a sample, versus how far its say extends.
|
|
132
|
+
//
|
|
133
|
+
// `blendSample` is convex, so two values in [0,1] mixed by a weight in [0,1]
|
|
134
|
+
// cannot leave [0,1]: the tile's bounds are known before anything is
|
|
135
|
+
// evaluated, and `height` scales the whole block rather than pushing one
|
|
136
|
+
// province through the top of it.
|
|
137
|
+
const sample = normalise(raw[k], lo, hi)
|
|
138
|
+
const h = blendSample(sample, shape.evaluate(sample), w)
|
|
139
|
+
pos[i + 1] = h * state.height
|
|
140
|
+
}
|
|
141
|
+
ground.updateVerticesData('position', pos)
|
|
142
|
+
ground.createNormals(true)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
shape.onChange = rebuild
|
|
146
|
+
falloff.onChange = rebuild
|
|
147
|
+
footprint.onChange = rebuild
|
|
148
|
+
|
|
149
|
+
const scene = b3d(
|
|
150
|
+
{
|
|
151
|
+
// `flex:1` on the ELEMENT, not just its wrapper: a <tosi-b3d> in a flex row
|
|
152
|
+
// has no flex-grow of its own, so it shrinks to content width — which is 0,
|
|
153
|
+
// and renders a 0x296 canvas that looks exactly like a broken scene.
|
|
154
|
+
style: 'flex:1;min-width:0;border-radius:8px;overflow:hidden',
|
|
155
|
+
sceneCreated(el) {
|
|
156
|
+
orbitCam(el, { radius: 32, beta: 1.02, alpha: -1.15, target: [0, 1.5, 0] })
|
|
157
|
+
ground = el.make.ground({
|
|
158
|
+
width: SIZE,
|
|
159
|
+
height: SIZE,
|
|
160
|
+
subdivisions: SUBS,
|
|
161
|
+
updatable: true,
|
|
162
|
+
color: '#6d7a58',
|
|
163
|
+
})
|
|
164
|
+
rebuild()
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
b3dLight({ intensity: 0.95 })
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
// A preset menu per curve. Presets are the fastest way to learn what a curve
|
|
171
|
+
// DOES — you pick "desert terraces", see terraces, then drag from there.
|
|
172
|
+
const menu = (widget, kind, value) =>
|
|
173
|
+
select3d({
|
|
174
|
+
value,
|
|
175
|
+
options: presetsFor(kind).map((p) => p.name),
|
|
176
|
+
onChange: (name) => { widget.applyPreset(name); rebuild() },
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
const panel = panel3d(
|
|
180
|
+
{ width: 320 },
|
|
181
|
+
label3d({ text: 'province editor', bold: true }),
|
|
182
|
+
shape,
|
|
183
|
+
menu(shape, 'profile', 'no change'),
|
|
184
|
+
button3d({ label: 'delete selected point', onClick: () => shape.deleteSelected() }),
|
|
185
|
+
falloff,
|
|
186
|
+
menu(falloff, 'falloff', 'linear'),
|
|
187
|
+
button3d({ label: 'delete selected point', onClick: () => falloff.deleteSelected() }),
|
|
188
|
+
footprint,
|
|
189
|
+
menu(footprint, 'radial', 'hexagon'),
|
|
190
|
+
slider3d({ label: 'block height', min: 1, max: 16, value: state.height, onChange: (v) => { state.height = v; rebuild() } }),
|
|
191
|
+
slider3d({ label: 'extent', min: 0.2, max: 1, value: state.extent, onChange: (v) => { state.extent = v; rebuild() } }),
|
|
192
|
+
slider3d({ label: 'noise scale', min: 0.2, max: 4, value: state.noise, onChange: (v) => { state.noise = v; rebuild() } }),
|
|
193
|
+
toggle3d({ label: 'wireframe', value: false, onChange: (v) => { if (ground?.material) ground.material.wireframe = v } }),
|
|
194
|
+
// The real biome shader, on this block's material — the same one b3d-terrain
|
|
195
|
+
// puts on a tile, so the province is judged against how it will actually look
|
|
196
|
+
// rather than against a flat green.
|
|
197
|
+
toggle3d({ label: 'terrain shader', value: false, onChange: (v) => {
|
|
198
|
+
if (ground?.material == null) return
|
|
199
|
+
if (biome == null) biome = attachBiomePlugin(ground.material)
|
|
200
|
+
biome.isEnabled = v
|
|
201
|
+
} })
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
preview.append(
|
|
205
|
+
div(
|
|
206
|
+
{ style: 'display:flex;gap:16px;height:100%;padding:12px;background:#0c0e14;box-sizing:border-box' },
|
|
207
|
+
div({ style: 'flex:0 0 320px;overflow:auto' }, panel),
|
|
208
|
+
div({ style: 'flex:1;min-width:0;display:flex' }, scene)
|
|
209
|
+
)
|
|
210
|
+
)
|
|
211
|
+
```
|
|
212
|
+
```css
|
|
213
|
+
.preview {
|
|
214
|
+
height: 100%;
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
*/
|
|
218
|
+
/*{ "parent": "UI", "order": 260 }*/
|
|
219
|
+
import { svgElements } from 'tosijs';
|
|
220
|
+
import { deletePoint, evaluateCurve, insertPoint, linear, movePoint, normalizeCurve, presetsFor, falloffDefault, } from './curve';
|
|
221
|
+
import { w3dTheme } from './w3d-theme';
|
|
222
|
+
const { g, rect, path, circle, text } = svgElements;
|
|
223
|
+
/** Resolve the `value` option, which may name a preset. */
|
|
224
|
+
function initialPoints(value, kind) {
|
|
225
|
+
if (Array.isArray(value))
|
|
226
|
+
return normalizeCurve(value, kind);
|
|
227
|
+
if (typeof value === 'string') {
|
|
228
|
+
const preset = presetsFor(kind).find((p) => p.name === value);
|
|
229
|
+
if (preset != null)
|
|
230
|
+
return normalizeCurve(preset.build(), kind);
|
|
231
|
+
}
|
|
232
|
+
return normalizeCurve(kind === 'falloff' ? falloffDefault() : linear(), kind);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* An editable curve.
|
|
236
|
+
*
|
|
237
|
+
* ```js
|
|
238
|
+
* const falloff = curve3d({ kind: 'falloff', label: 'falloff' })
|
|
239
|
+
* falloff.onChange = () => rebuildTerrain()
|
|
240
|
+
* falloff.evaluate(0.5)
|
|
241
|
+
* ```
|
|
242
|
+
*/
|
|
243
|
+
export function curve3d(config = {}) {
|
|
244
|
+
const kind = config.kind ?? 'profile';
|
|
245
|
+
const aspect = config.aspect ?? 0.62;
|
|
246
|
+
let points = initialPoints(config.value, kind);
|
|
247
|
+
let selected = -1;
|
|
248
|
+
let dragging = -1;
|
|
249
|
+
const el = g();
|
|
250
|
+
const bg = rect({ 'data-curve-bg': '', x: 0, y: 0, rx: 4, ry: 4 });
|
|
251
|
+
const grid = path({ 'data-curve-grid': '', fill: 'none' });
|
|
252
|
+
/*
|
|
253
|
+
THE IDENTITY, drawn faintly — the single most useful mark on a profile plot.
|
|
254
|
+
|
|
255
|
+
Without it the plot has no orientation cue at all: which corner is (0,0) is a
|
|
256
|
+
guess, and a curve drawn from top-left to bottom-right looks as reasonable as
|
|
257
|
+
its mirror while meaning the opposite (it maps low ground HIGH). Tonio drew
|
|
258
|
+
exactly that, expected a no-op, and got a raised province — a reading error the
|
|
259
|
+
plot invited by saying nothing.
|
|
260
|
+
|
|
261
|
+
With the diagonal there, "no change" has a picture: on the line. Only for a
|
|
262
|
+
profile — a falloff is not a remap, so its diagonal would mean nothing.
|
|
263
|
+
*/
|
|
264
|
+
const identityLine = path({ 'data-curve-identity': '', fill: 'none' });
|
|
265
|
+
const line = path({ 'data-curve-line': '', fill: 'none' });
|
|
266
|
+
const caption = text({
|
|
267
|
+
'font-family': w3dTheme.fontFamily,
|
|
268
|
+
'font-size': String(Math.round(w3dTheme.fontSize * 0.85)),
|
|
269
|
+
fill: w3dTheme.muted,
|
|
270
|
+
}, config.label ?? '');
|
|
271
|
+
el.appendChild(bg);
|
|
272
|
+
el.appendChild(grid);
|
|
273
|
+
if (kind === 'profile')
|
|
274
|
+
el.appendChild(identityLine);
|
|
275
|
+
el.appendChild(line);
|
|
276
|
+
el.appendChild(caption);
|
|
277
|
+
const handles = g({ 'data-curve-points': '' });
|
|
278
|
+
el.appendChild(handles);
|
|
279
|
+
// Plot geometry from the last layout, so the pointer maps through exactly what
|
|
280
|
+
// was drawn — the rule row3d and vector-field both follow.
|
|
281
|
+
//
|
|
282
|
+
// FRAME is the drawn box; PLOT is inset within it, so the x = 0 and x = 1
|
|
283
|
+
// points sit INSIDE the interactive area instead of straddling its edge.
|
|
284
|
+
// Tonio: "I'm finding it very hard to move the edge points or even select
|
|
285
|
+
// them." Half of each end handle's grab area fell outside `hitTest`, and a
|
|
286
|
+
// near-miss did not just fail — it INSERTED a point, which is the worst
|
|
287
|
+
// available outcome for a mis-aimed press.
|
|
288
|
+
let frame = { x: 0, y: 0, w: 1, h: 1 };
|
|
289
|
+
let plot = { x: 0, y: 0, w: 1, h: 1 };
|
|
290
|
+
let rowHeight = 0;
|
|
291
|
+
/** Handle radius, and the inset that keeps a whole handle inside the frame. */
|
|
292
|
+
const HANDLE = 5;
|
|
293
|
+
const INSET = HANDLE + 4;
|
|
294
|
+
/** Grab radius in PIXELS — see `nearestPx`. */
|
|
295
|
+
const GRAB = 16;
|
|
296
|
+
// Curve space ↔ widget space. y is flipped: 1 is the TOP of the plot.
|
|
297
|
+
const toPx = (p) => ({
|
|
298
|
+
x: plot.x + p.x * plot.w,
|
|
299
|
+
y: plot.y + (1 - p.y) * plot.h,
|
|
300
|
+
});
|
|
301
|
+
const toCurve = (x, y) => ({
|
|
302
|
+
x: (x - plot.x) / Math.max(1, plot.w),
|
|
303
|
+
y: 1 - (y - plot.y) / Math.max(1, plot.h),
|
|
304
|
+
});
|
|
305
|
+
/**
|
|
306
|
+
* Nearest point within `GRAB` PIXELS, or -1.
|
|
307
|
+
*
|
|
308
|
+
* Deliberately not `curve.pointAt`, which measures in curve units: the plot is
|
|
309
|
+
* wider than it is tall, so one radius in curve space is an ELLIPSE on screen —
|
|
310
|
+
* generous horizontally, mean vertically, and the asymmetry is invisible until
|
|
311
|
+
* you try to grab something. Pixels are what the finger and the ray both work
|
|
312
|
+
* in.
|
|
313
|
+
*/
|
|
314
|
+
const nearestPx = (px, py) => {
|
|
315
|
+
let best = -1;
|
|
316
|
+
let bestD = GRAB * GRAB;
|
|
317
|
+
points.forEach((p, i) => {
|
|
318
|
+
const c = toPx(p);
|
|
319
|
+
const d = (c.x - px) * (c.x - px) + (c.y - py) * (c.y - py);
|
|
320
|
+
if (d <= bestD) {
|
|
321
|
+
bestD = d;
|
|
322
|
+
best = i;
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
return best;
|
|
326
|
+
};
|
|
327
|
+
const emit = () => {
|
|
328
|
+
api.onChange?.(points.map((p) => ({ ...p })));
|
|
329
|
+
};
|
|
330
|
+
const drawHandles = () => {
|
|
331
|
+
while (handles.firstChild)
|
|
332
|
+
handles.removeChild(handles.firstChild);
|
|
333
|
+
points.forEach((p, i) => {
|
|
334
|
+
const c = toPx(p);
|
|
335
|
+
handles.appendChild(circle({
|
|
336
|
+
cx: c.x,
|
|
337
|
+
cy: c.y,
|
|
338
|
+
r: i === selected ? HANDLE : HANDLE - 1.5,
|
|
339
|
+
fill: i === selected ? w3dTheme.accent : w3dTheme.panelBg,
|
|
340
|
+
stroke: w3dTheme.accent,
|
|
341
|
+
'stroke-width': String(w3dTheme.strokeWidth),
|
|
342
|
+
}));
|
|
343
|
+
});
|
|
344
|
+
};
|
|
345
|
+
const draw = () => {
|
|
346
|
+
bg.setAttribute('x', String(frame.x));
|
|
347
|
+
bg.setAttribute('y', String(frame.y));
|
|
348
|
+
bg.setAttribute('width', String(frame.w));
|
|
349
|
+
bg.setAttribute('height', String(frame.h));
|
|
350
|
+
bg.setAttribute('fill', w3dTheme.rowBg);
|
|
351
|
+
bg.setAttribute('stroke', w3dTheme.divider);
|
|
352
|
+
bg.setAttribute('stroke-width', String(w3dTheme.strokeWidth));
|
|
353
|
+
// Quarters, so you can read a value off the plot without a scale.
|
|
354
|
+
const lines = [];
|
|
355
|
+
for (let i = 1; i < 4; i++) {
|
|
356
|
+
const gx = plot.x + (plot.w * i) / 4;
|
|
357
|
+
const gy = plot.y + (plot.h * i) / 4;
|
|
358
|
+
lines.push(`M${gx} ${plot.y}V${plot.y + plot.h}`);
|
|
359
|
+
lines.push(`M${plot.x} ${gy}H${plot.x + plot.w}`);
|
|
360
|
+
}
|
|
361
|
+
grid.setAttribute('d', lines.join(''));
|
|
362
|
+
grid.setAttribute('stroke', w3dTheme.divider);
|
|
363
|
+
grid.setAttribute('stroke-width', '1');
|
|
364
|
+
if (kind === 'profile') {
|
|
365
|
+
const a = toPx({ x: 0, y: 0 });
|
|
366
|
+
const b = toPx({ x: 1, y: 1 });
|
|
367
|
+
identityLine.setAttribute('d', `M${a.x} ${a.y}L${b.x} ${b.y}`);
|
|
368
|
+
identityLine.setAttribute('stroke', w3dTheme.muted);
|
|
369
|
+
identityLine.setAttribute('stroke-width', '1');
|
|
370
|
+
identityLine.setAttribute('stroke-dasharray', '4 4');
|
|
371
|
+
identityLine.setAttribute('opacity', '0.5');
|
|
372
|
+
}
|
|
373
|
+
// The curve IS piecewise linear, so the control points are the polyline —
|
|
374
|
+
// no sampling, and what you see is exactly what `evaluate` returns.
|
|
375
|
+
const d = points
|
|
376
|
+
.map((p, i) => {
|
|
377
|
+
const c = toPx(p);
|
|
378
|
+
return `${i === 0 ? 'M' : 'L'}${c.x} ${c.y}`;
|
|
379
|
+
})
|
|
380
|
+
.join('');
|
|
381
|
+
line.setAttribute('d', d);
|
|
382
|
+
line.setAttribute('stroke', w3dTheme.accent);
|
|
383
|
+
line.setAttribute('stroke-width', String(Math.max(1.5, w3dTheme.strokeWidth)));
|
|
384
|
+
drawHandles();
|
|
385
|
+
};
|
|
386
|
+
const api = {
|
|
387
|
+
el,
|
|
388
|
+
onChange: config.onChange,
|
|
389
|
+
layout(width) {
|
|
390
|
+
const pad = Math.max(2, Math.round(w3dTheme.spacing * 0.5));
|
|
391
|
+
const capH = config.label ? Math.round(w3dTheme.fontSize * 1.2) : 0;
|
|
392
|
+
const plotH = Math.round(width * aspect);
|
|
393
|
+
frame = {
|
|
394
|
+
x: pad,
|
|
395
|
+
y: capH,
|
|
396
|
+
w: Math.max(8, width - pad * 2),
|
|
397
|
+
h: Math.max(8, plotH),
|
|
398
|
+
};
|
|
399
|
+
plot = {
|
|
400
|
+
x: frame.x + INSET,
|
|
401
|
+
y: frame.y + INSET,
|
|
402
|
+
w: Math.max(8, frame.w - INSET * 2),
|
|
403
|
+
h: Math.max(8, frame.h - INSET * 2),
|
|
404
|
+
};
|
|
405
|
+
if (config.label) {
|
|
406
|
+
caption.setAttribute('x', String(pad));
|
|
407
|
+
caption.setAttribute('y', String(Math.round(w3dTheme.fontSize * 0.9)));
|
|
408
|
+
}
|
|
409
|
+
rowHeight = capH + plotH + pad;
|
|
410
|
+
draw();
|
|
411
|
+
return rowHeight;
|
|
412
|
+
},
|
|
413
|
+
handle(kind_, x, y) {
|
|
414
|
+
if (kind_ === 'down') {
|
|
415
|
+
const hit = nearestPx(x, y);
|
|
416
|
+
const c = toCurve(x, y);
|
|
417
|
+
if (hit >= 0) {
|
|
418
|
+
selected = hit;
|
|
419
|
+
dragging = hit;
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
const added = insertPoint(points, c.x, c.y, kind);
|
|
423
|
+
points = added.points;
|
|
424
|
+
selected = added.index;
|
|
425
|
+
dragging = added.index;
|
|
426
|
+
emit();
|
|
427
|
+
}
|
|
428
|
+
draw();
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
if (kind_ === 'move' && dragging >= 0) {
|
|
432
|
+
const c = toCurve(x, y);
|
|
433
|
+
const moved = movePoint(points, dragging, c.x, c.y, kind);
|
|
434
|
+
points = moved.points;
|
|
435
|
+
// The index can change mid-drag when a point crosses a neighbour —
|
|
436
|
+
// keeping the old one would silently start dragging a different point.
|
|
437
|
+
dragging = moved.index;
|
|
438
|
+
selected = moved.index;
|
|
439
|
+
draw();
|
|
440
|
+
emit();
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
if (kind_ === 'up' || kind_ === 'leave')
|
|
444
|
+
dragging = -1;
|
|
445
|
+
},
|
|
446
|
+
hitTest(x, y) {
|
|
447
|
+
// The FRAME, not the plot: an end handle sits on the plot boundary, so a
|
|
448
|
+
// plot-bounded test rejects half of every press aimed at one.
|
|
449
|
+
return (x >= frame.x &&
|
|
450
|
+
x <= frame.x + frame.w &&
|
|
451
|
+
y >= frame.y &&
|
|
452
|
+
y <= frame.y + frame.h);
|
|
453
|
+
},
|
|
454
|
+
get points() {
|
|
455
|
+
return points.map((p) => ({ ...p }));
|
|
456
|
+
},
|
|
457
|
+
setPoints(p) {
|
|
458
|
+
points = normalizeCurve(p, kind);
|
|
459
|
+
selected = -1;
|
|
460
|
+
draw();
|
|
461
|
+
},
|
|
462
|
+
evaluate(t) {
|
|
463
|
+
return evaluateCurve(points, t);
|
|
464
|
+
},
|
|
465
|
+
get selected() {
|
|
466
|
+
return selected;
|
|
467
|
+
},
|
|
468
|
+
deleteSelected() {
|
|
469
|
+
if (selected < 0)
|
|
470
|
+
return;
|
|
471
|
+
const next = deletePoint(points, selected, kind);
|
|
472
|
+
if (next === points)
|
|
473
|
+
return;
|
|
474
|
+
points = next;
|
|
475
|
+
selected = -1;
|
|
476
|
+
draw();
|
|
477
|
+
emit();
|
|
478
|
+
},
|
|
479
|
+
applyPreset(name) {
|
|
480
|
+
const preset = presetsFor(kind).find((p) => p.name === name);
|
|
481
|
+
if (preset == null)
|
|
482
|
+
return;
|
|
483
|
+
points = normalizeCurve(preset.build(), kind);
|
|
484
|
+
selected = -1;
|
|
485
|
+
draw();
|
|
486
|
+
emit();
|
|
487
|
+
},
|
|
488
|
+
};
|
|
489
|
+
return api;
|
|
490
|
+
}
|
|
491
|
+
//# sourceMappingURL=curve-field.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"curve-field.js","sourceRoot":"","sources":["../src/curve-field.ts"],"names":[],"mappings":"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"}
|