phoria2 1.3.0 → 1.4.1

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.
Files changed (56) hide show
  1. package/README.md +9 -6
  2. package/dist/bloom.js +4 -4
  3. package/dist/bloom.js.map +1 -1
  4. package/dist/charts.js +436 -436
  5. package/dist/charts.js.map +1 -1
  6. package/dist/core.js +1240 -862
  7. package/dist/core.js.map +1 -1
  8. package/dist/debug.js +297 -0
  9. package/dist/debug.js.map +1 -0
  10. package/dist/edit.js +40 -40
  11. package/dist/edit.js.map +1 -1
  12. package/dist/explain.js +32 -13
  13. package/dist/explain.js.map +1 -1
  14. package/dist/gltf.js +128 -128
  15. package/dist/gltf.js.map +1 -1
  16. package/dist/keyboard.js +19 -19
  17. package/dist/keyboard.js.map +1 -1
  18. package/dist/motion.js +35 -35
  19. package/dist/motion.js.map +1 -1
  20. package/dist/particles.js +36 -36
  21. package/dist/particles.js.map +1 -1
  22. package/dist/passes.js +116 -104
  23. package/dist/passes.js.map +1 -1
  24. package/dist/phoria2.js +1 -1
  25. package/dist/picking.js +90 -71
  26. package/dist/picking.js.map +1 -1
  27. package/dist/snapshot.js +1 -1
  28. package/dist/tooltip.js +1 -1
  29. package/dist/types/View.d.ts +16 -0
  30. package/dist/types/debug.d.ts +74 -0
  31. package/dist/types/explain.d.ts +4 -0
  32. package/dist/types/frame.d.ts +5 -0
  33. package/dist/types/material/Material.d.ts +52 -0
  34. package/dist/types/watch.d.ts +8 -1
  35. package/dist/types/webgl/hooks.d.ts +48 -0
  36. package/dist/types/webgl/passes.d.ts +4 -0
  37. package/dist/types/webgl/programs.d.ts +15 -5
  38. package/dist/types/webgl/shaders.d.ts +36 -5
  39. package/llms-full.txt +227 -0
  40. package/llms.txt +1 -1
  41. package/package.json +1 -1
  42. package/schema/scene.schema.json +27 -0
  43. package/src/View.js +105 -7
  44. package/src/debug.js +283 -0
  45. package/src/explain.js +19 -0
  46. package/src/frame.js +7 -2
  47. package/src/material/Material.js +121 -2
  48. package/src/scene/drawables.js +21 -4
  49. package/src/watch.js +18 -2
  50. package/src/webgl/WebGLRenderer.js +19 -2
  51. package/src/webgl/hooks.js +121 -0
  52. package/src/webgl/passes.js +17 -2
  53. package/src/webgl/picking.js +23 -2
  54. package/src/webgl/programs.js +20 -9
  55. package/src/webgl/resources.js +1 -1
  56. package/src/webgl/shaders.js +443 -35
package/dist/debug.js ADDED
@@ -0,0 +1,297 @@
1
+ import { $ as e, B as t, Dt as n, Et as r, Ot as i, pt as a, rt as o } from "./core.js";
2
+ // src/debug.js
3
+ var s = {
4
+ shadow: [
5
+ 1,
6
+ .8,
7
+ 0
8
+ ],
9
+ bounds: [
10
+ 0,
11
+ .9,
12
+ 1
13
+ ],
14
+ normal: [
15
+ 1,
16
+ .31,
17
+ .85
18
+ ],
19
+ camera: [
20
+ 1,
21
+ 1,
22
+ 1
23
+ ]
24
+ }, c = 5e3, l = 4e4, u = class {
25
+ #e = new t();
26
+ #t = new a({ positions: [
27
+ 0,
28
+ 0,
29
+ 0,
30
+ 0,
31
+ 0,
32
+ 0
33
+ ] });
34
+ #n = this.#e.add(new o({
35
+ geometry: this.#t,
36
+ pickable: !1,
37
+ frustumCulled: !1,
38
+ material: {
39
+ vertexColors: !0,
40
+ width: 1.5,
41
+ fog: !1
42
+ }
43
+ }));
44
+ #r = this.#e.add(new o({
45
+ geometry: this.#t,
46
+ pickable: !1,
47
+ frustumCulled: !1,
48
+ renderOrder: 1e9,
49
+ material: {
50
+ vertexColors: !0,
51
+ width: 1,
52
+ opacity: .22,
53
+ depthTest: !1,
54
+ depthWrite: !1,
55
+ fog: !1
56
+ }
57
+ }));
58
+ #i = null;
59
+ #a = 0;
60
+ #o = [];
61
+ #s = [];
62
+ constructor(e) {
63
+ this.overlay = e;
64
+ }
65
+ add(t, n, r) {
66
+ this.#o = [], this.#s = [];
67
+ let o = [...t.opaque, ...t.transparent], c = r.bounds.isEmpty() ? 1 : Math.max(r.bounds.getRadius(), .001), l = r.bounds.isEmpty() ? new i() : r.bounds.getCenter();
68
+ n.lights && this.#p(t, c, l), n.shadows && this.#m(t), n.bounds && this.#h(o, n.bounds === "hovered" ? r.hovered : null, n.bounds === "hovered"), n.normals && this.#g(o, n.normals === "hovered" ? r.hovered : null, n.normals === "hovered", c * .025), n.axes && (this.#c([
69
+ 0,
70
+ 0,
71
+ 0
72
+ ], [
73
+ c * .5,
74
+ 0,
75
+ 0
76
+ ], [
77
+ 1,
78
+ .2,
79
+ .2
80
+ ]), this.#c([
81
+ 0,
82
+ 0,
83
+ 0
84
+ ], [
85
+ 0,
86
+ c * .5,
87
+ 0
88
+ ], [
89
+ .3,
90
+ 1,
91
+ .3
92
+ ]), this.#c([
93
+ 0,
94
+ 0,
95
+ 0
96
+ ], [
97
+ 0,
98
+ 0,
99
+ c * .5
100
+ ], [
101
+ .3,
102
+ .5,
103
+ 1
104
+ ]));
105
+ let u = r.camera.target;
106
+ if (n.camera && u && this.#l(u, c * .04, s.camera), !this.#o.length) return {
107
+ opaque: 0,
108
+ transparent: 0
109
+ };
110
+ let d = this.#o.length, f = this.#t.colors;
111
+ this.#t.positions.length === d && f ? (this.#t.positions.set(this.#o), f.set(this.#s), this.#t.needsUpdate()) : (this.#t.dispose(), this.#t = new a({
112
+ positions: this.#o,
113
+ colors: this.#s
114
+ }), this.#n.geometry = this.#r.geometry = this.#t), this.#e.updateWorldTree();
115
+ let p = e(this.#e, r.camera);
116
+ return t.opaque.push(...p.opaque), t.transparent.push(...p.transparent), {
117
+ opaque: p.opaque.length,
118
+ transparent: p.transparent.length
119
+ };
120
+ }
121
+ stats(e, t, n, r) {
122
+ if (!e || !this.overlay || typeof document > "u") {
123
+ this.#i?.remove(), this.#i = null;
124
+ return;
125
+ }
126
+ let i = e === !0 ? "top-left" : e;
127
+ this.#i || (this.#i = document.createElement("div"), this.#i.className = "phoria-debug-stats", Object.assign(this.#i.style, {
128
+ position: "absolute",
129
+ zIndex: "3",
130
+ pointerEvents: "none",
131
+ padding: "6px 8px",
132
+ borderRadius: "6px",
133
+ background: "rgba(10, 12, 16, 0.72)",
134
+ color: "#e8ecf2",
135
+ font: "11px/1.45 ui-monospace, SFMono-Regular, Consolas, monospace",
136
+ whiteSpace: "pre"
137
+ }), this.overlay.append(this.#i));
138
+ let [a, o] = i.split("-");
139
+ Object.assign(this.#i.style, {
140
+ top: a === "top" ? "8px" : "",
141
+ bottom: a === "bottom" ? "8px" : "",
142
+ left: o === "left" ? "8px" : "",
143
+ right: o === "right" ? "8px" : ""
144
+ });
145
+ let s = performance.now();
146
+ if (s - this.#a < 250) return;
147
+ this.#a = s;
148
+ let c = (e) => e >= 1e6 ? `${(e / 1e6).toFixed(1)}M` : e >= 1e4 ? `${Math.round(e / 1e3)}k` : String(e), l = n > 0 ? Math.round(1e3 / n) : 0;
149
+ this.#i.textContent = [
150
+ `${l ? `${l} fps ${n.toFixed(1)} ms` : "idle"} cpu ${r.toFixed(1)} ms`,
151
+ `${t.drawCalls} draws ${c(t.triangles)} triangles`,
152
+ `${t.programs} programs${t.compiling ? ` (${t.compiling} compiling)` : ""} ${t.textures} textures ${t.geometries} geometries`
153
+ ].join("\n");
154
+ }
155
+ dispose() {
156
+ this.#i?.remove(), this.#i = null, this.#t.dispose();
157
+ }
158
+ #c(e, t, n) {
159
+ this.#o.push(e[0], e[1], e[2], t[0], t[1], t[2]), this.#s.push(n[0], n[1], n[2], n[0], n[1], n[2]);
160
+ }
161
+ #l(e, t, n) {
162
+ for (let r = 0; r < 3; r++) {
163
+ let i = [
164
+ e[0],
165
+ e[1],
166
+ e[2]
167
+ ], a = [
168
+ e[0],
169
+ e[1],
170
+ e[2]
171
+ ];
172
+ i[r] -= t, a[r] += t, this.#c(i, a, n);
173
+ }
174
+ }
175
+ #u(e, t, n, r, i, a = 32) {
176
+ let o = (i) => {
177
+ let o = i / a * Math.PI * 2, s = Math.cos(o) * r, c = Math.sin(o) * r;
178
+ return [
179
+ e[0] + t[0] * s + n[0] * c,
180
+ e[1] + t[1] * s + n[1] * c,
181
+ e[2] + t[2] * s + n[2] * c
182
+ ];
183
+ };
184
+ for (let e = 0; e < a; e++) this.#c(o(e), o(e + 1), i);
185
+ }
186
+ #d(e, t) {
187
+ let { min: n, max: r } = e, i = (e) => [
188
+ e & 1 ? r[0] : n[0],
189
+ e & 2 ? r[1] : n[1],
190
+ e & 4 ? r[2] : n[2]
191
+ ];
192
+ for (let [e, n] of m) this.#c(i(e), i(n), t);
193
+ }
194
+ #f(e, t) {
195
+ let r = new n().copy(e).invert(), a = (e) => new i(e & 1 ? 1 : -1, e & 2 ? 1 : -1, e & 4 ? 1 : -1).applyMatrix4(r);
196
+ for (let [e, n] of m) this.#c(a(e), a(n), t);
197
+ }
198
+ #p(e, t, n) {
199
+ for (let r of e.lights) {
200
+ let e = r.color.toSRGB().map((e) => Math.min(1, e));
201
+ if (r.kind === "directional" && r.direction) {
202
+ let a = new i().copy(r.direction).normalize(), o = g(n, a, -t * 1.1), s = g(n, a, -t * .45);
203
+ this.#c(o, s, e);
204
+ let [c, l] = h(a);
205
+ for (let n of [
206
+ c,
207
+ l,
208
+ c.clone().negate(),
209
+ l.clone().negate()
210
+ ]) this.#c(s, g(g(s, a, -t * .08), n, t * .04), e);
211
+ } else if (r.kind === "point" && r.position) {
212
+ let n = new i().copy(r.position), a = t * .06;
213
+ this.#u(n, d, f, a, e, 16), this.#u(n, d, p, a, e, 16), this.#u(n, f, p, a, e, 16), r.range && this.#u(n, d, p, r.range, e, 48);
214
+ } else if (r.kind === "spot" && r.position && r.direction) {
215
+ let n = new i().copy(r.position), a = new i().copy(r.direction).normalize(), o = r.range ? Math.min(r.range, t * 2) : t, s = o * Math.tan(r.angle ?? Math.PI / 4), c = g(n, a, o), [l, u] = h(a);
216
+ this.#u(c, l, u, s, e, 32);
217
+ for (let t of [
218
+ l,
219
+ u,
220
+ l.clone().negate(),
221
+ u.clone().negate()
222
+ ]) this.#c(n, g(c, t, s), e);
223
+ this.#c(n, c, e);
224
+ }
225
+ }
226
+ }
227
+ #m(e) {
228
+ e.shadow && this.#f(e.shadow.viewProjection, s.shadow);
229
+ for (let t of e.spotShadows) this.#f(t.viewProjection, s.shadow);
230
+ }
231
+ #h(e, t, n) {
232
+ if (n) {
233
+ if (!t) return;
234
+ let e = t.node;
235
+ if (t.instance !== null && typeof e.eachInstanceBox == "function") {
236
+ let n = 0;
237
+ e.eachInstanceBox((e, i, a, o, c, l) => {
238
+ n++ === t.instance && this.#d(new r([
239
+ e,
240
+ i,
241
+ a
242
+ ], [
243
+ o,
244
+ c,
245
+ l
246
+ ]), s.bounds);
247
+ });
248
+ } else this.#d(e.expandBounds(new r()), s.bounds);
249
+ return;
250
+ }
251
+ let i = /* @__PURE__ */ new Set();
252
+ for (let t of e) {
253
+ if (i.has(t.node)) continue;
254
+ i.add(t.node);
255
+ let e = t.node.expandBounds(new r());
256
+ e.isEmpty() || this.#d(e, s.bounds);
257
+ }
258
+ }
259
+ #g(e, t, n, r) {
260
+ let a = n ? t && t.node.drawMode === "triangles" ? [t.node] : [] : [...new Set(e.map((e) => e.node))].filter((e) => e.drawMode === "triangles"), o = l;
261
+ for (let e of a) {
262
+ if (e.instanceMatrices) continue;
263
+ let t = e.geometry, a = t.vertexCount;
264
+ if (!t.normals || !n && (a > c || a > o)) continue;
265
+ o -= a;
266
+ let l = e.worldMatrix, u = t.positions, d = t.normals, f = new i(), p = new i();
267
+ for (let e = 0; e < a; e++) f.set(u[e * 3], u[e * 3 + 1], u[e * 3 + 2]).applyMatrix4(l), p.set(l[0] * d[e * 3] + l[4] * d[e * 3 + 1] + l[8] * d[e * 3 + 2], l[1] * d[e * 3] + l[5] * d[e * 3 + 1] + l[9] * d[e * 3 + 2], l[2] * d[e * 3] + l[6] * d[e * 3 + 1] + l[10] * d[e * 3 + 2]).normalize(), this.#c(f, [
268
+ f[0] + p[0] * r,
269
+ f[1] + p[1] * r,
270
+ f[2] + p[2] * r
271
+ ], s.normal);
272
+ }
273
+ }
274
+ }, d = new i(1, 0, 0), f = new i(0, 1, 0), p = new i(0, 0, 1), m = [
275
+ [0, 1],
276
+ [2, 3],
277
+ [4, 5],
278
+ [6, 7],
279
+ [0, 2],
280
+ [1, 3],
281
+ [4, 6],
282
+ [5, 7],
283
+ [0, 4],
284
+ [1, 5],
285
+ [2, 6],
286
+ [3, 7]
287
+ ];
288
+ function h(e) {
289
+ let t = Math.abs(e[1]) < .9 ? f : d, n = e.clone().cross(t).normalize();
290
+ return [n, e.clone().cross(n).normalize()];
291
+ }
292
+ function g(e, t, n) {
293
+ return new i(e[0] + t[0] * n, e[1] + t[1] * n, e[2] + t[2] * n);
294
+ }
295
+ export { u as DebugDraw };
296
+
297
+ //# sourceMappingURL=debug.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug.js","names":["collect","Scene","Mat4","Box3","Vec3","Geometry","Lines","COLORS","NORMALS_PER_MESH","NORMALS_IN_ALL","DebugDraw","#scene","#geometry","overlay","#positions","#colors","items","frame","size","at","middle","o","#lights","#shadows","#bounds","#normals","#segment","target","#cross","n","colors","#solid","#faint","drawn","where","#stats","corner","v","h","now","#statsAt","k","x","fps","frameMs","cpuMs","info","a","b","c","p","r","segments","y","centre","u","i","box","j","EDGES","inverse","viewProjection","l","d","from","moved","to","across","s","#circle","X","Y","Z","length","end","#frustum","onlyHovered","hovered","node","#box","x0","y0","z0","x1","y1","z1","seen","item","meshes","budget","g","count","m","up"],"sources":["../src/debug.js"],"sourcesContent":["// view.debug: pictures for a person checking a scene - each light and the way it shines, the shadow\n// cameras, bounding boxes, normals, the axes, the orbit target - and a corner readout of what a frame\n// costs. view.explain() is the text an agent reads; this is what a person looks at. Loaded only when\n// debug is turned on. The helpers are drawn by the View after the scene, not added to it, so they\n// are never picked, saved, framed or counted: lines in the scene's depth, and faintly through\n// whatever is in front of them, so a light inside a lamp can still be found.\nimport { collect } from './frame.js'\nimport { Geometry } from './geometry/Geometry.js'\nimport { Lines } from './scene/drawables.js'\nimport { Scene } from './scene/Scene.js'\nimport { Box3 } from './value/Box3.js'\nimport { Mat4 } from './value/Mat4.js'\nimport { Vec3 } from './value/Vec3.js'\n\n/** @import { DrawItem, FrameDescription } from './frame.js' */\n/** @import { Hit } from './Raycaster.js' */\n/** @import { Camera } from './scene/cameras.js' */\n\n/**\n * What debug draws. `true` for a helper shows it for everything; `'hovered'` (bounds and normals) only\n * for what is under the pointer.\n * @typedef {object} DebugOptions\n * @property {boolean} [lights] each light: an arrow for directional ones, a small sphere and its range\n * for point lights, a cone for spot lights - in the light's colour\n * @property {boolean} [shadows] the box or cone each shadow is drawn from - the usual reason a shadow\n * is cut off or soft\n * @property {boolean | 'hovered'} [bounds] world bounding boxes - what culling and view.frame() see\n * @property {boolean | 'hovered'} [normals] short lines along vertex normals (meshes up to 5,000\n * vertices each when all are shown)\n * @property {boolean} [axes] x, y and z at the origin, red, green and blue\n * @property {boolean} [camera] the point the camera orbits round\n * @property {boolean | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'} [stats] a readout of\n * frame time, draw calls, triangles, programs, textures and geometries, in a corner of the canvas\n */\n\nconst COLORS = { shadow: [1, 0.8, 0], bounds: [0, 0.9, 1], normal: [1, 0.31, 0.85], camera: [1, 1, 1] }\n// normals, when shown for everything: past these a model would be a hedgehog\nconst NORMALS_PER_MESH = 5000, NORMALS_IN_ALL = 40000\n\nexport class DebugDraw {\n #scene = new Scene()\n #geometry = new Geometry({ positions: [0, 0, 0, 0, 0, 0] })\n // the same lines twice: in the scene's depth, and faintly through what is in front of them\n #solid = /** @type {Lines} */ (this.#scene.add(new Lines({ geometry: this.#geometry, pickable: false, frustumCulled: false,\n material: { vertexColors: true, width: 1.5, fog: false } })))\n #faint = /** @type {Lines} */ (this.#scene.add(new Lines({ geometry: this.#geometry, pickable: false, frustumCulled: false, renderOrder: 1e9,\n material: { vertexColors: true, width: 1, opacity: 0.22, depthTest: false, depthWrite: false, fog: false } })))\n /** @type {HTMLDivElement | null} */ #stats = null\n #statsAt = 0\n /** @type {number[]} */ #positions = []\n /** @type {number[]} */ #colors = []\n\n /** @param {HTMLElement | null} overlay where the stats readout goes - the layer over the canvas */\n constructor(overlay) {\n this.overlay = overlay\n }\n\n /**\n * Add the helpers to a frame about to be drawn, after everything in it. Returns how many items were\n * added to each list, to take out again once it is drawn.\n * @param {FrameDescription} frame @param {DebugOptions} o\n * @param {{ camera: Camera, hovered: Hit | null, bounds: Box3 }} at\n * @returns {{ opaque: number, transparent: number }}\n */\n add(frame, o, at) {\n this.#positions = []\n this.#colors = []\n const items = [...frame.opaque, ...frame.transparent]\n const size = at.bounds.isEmpty() ? 1 : Math.max(at.bounds.getRadius(), 1e-3)\n const middle = at.bounds.isEmpty() ? new Vec3() : at.bounds.getCenter()\n if (o.lights) this.#lights(frame, size, middle)\n if (o.shadows) this.#shadows(frame)\n if (o.bounds) this.#bounds(items, o.bounds === 'hovered' ? at.hovered : null, o.bounds === 'hovered')\n if (o.normals) this.#normals(items, o.normals === 'hovered' ? at.hovered : null, o.normals === 'hovered', size * 0.025)\n if (o.axes) {\n this.#segment([0, 0, 0], [size * 0.5, 0, 0], [1, 0.2, 0.2])\n this.#segment([0, 0, 0], [0, size * 0.5, 0], [0.3, 1, 0.3])\n this.#segment([0, 0, 0], [0, 0, size * 0.5], [0.3, 0.5, 1])\n }\n const target = /** @type {any} */ (at.camera).target\n if (o.camera && target) this.#cross(target, size * 0.04, COLORS.camera)\n if (!this.#positions.length) return { opaque: 0, transparent: 0 }\n\n // one buffer for all of it, made again only when it changes length\n const n = this.#positions.length\n const colors = this.#geometry.colors\n if (this.#geometry.positions.length === n && colors) {\n this.#geometry.positions.set(this.#positions)\n colors.set(this.#colors)\n this.#geometry.needsUpdate()\n } else {\n this.#geometry.dispose()\n this.#geometry = new Geometry({ positions: this.#positions, colors: this.#colors })\n this.#solid.geometry = this.#faint.geometry = this.#geometry\n }\n this.#scene.updateWorldTree()\n const drawn = collect(this.#scene, at.camera)\n frame.opaque.push(...drawn.opaque)\n frame.transparent.push(...drawn.transparent)\n return { opaque: drawn.opaque.length, transparent: drawn.transparent.length }\n }\n\n /**\n * The corner readout, refreshed a few times a second.\n * @param {DebugOptions['stats']} where\n * @param {{ drawCalls: number, triangles: number, programs: number, textures: number, geometries: number, compiling: number }} info\n * @param {number} frameMs time between drawn frames @param {number} cpuMs time the last frame took to draw\n */\n stats(where, info, frameMs, cpuMs) {\n if (!where || !this.overlay || typeof document === 'undefined') {\n this.#stats?.remove()\n this.#stats = null\n return\n }\n const corner = where === true ? 'top-left' : where\n if (!this.#stats) {\n this.#stats = document.createElement('div')\n this.#stats.className = 'phoria-debug-stats'\n Object.assign(this.#stats.style, { position: 'absolute', zIndex: '3', pointerEvents: 'none', padding: '6px 8px', borderRadius: '6px',\n background: 'rgba(10, 12, 16, 0.72)', color: '#e8ecf2', font: '11px/1.45 ui-monospace, SFMono-Regular, Consolas, monospace', whiteSpace: 'pre' })\n this.overlay.append(this.#stats)\n }\n const [v, h] = corner.split('-')\n Object.assign(this.#stats.style, { top: v === 'top' ? '8px' : '', bottom: v === 'bottom' ? '8px' : '', left: h === 'left' ? '8px' : '', right: h === 'right' ? '8px' : '' })\n const now = performance.now()\n if (now - this.#statsAt < 250) return\n this.#statsAt = now\n const k = (x) => (x >= 1e6 ? `${(x / 1e6).toFixed(1)}M` : x >= 1e4 ? `${Math.round(x / 1e3)}k` : String(x))\n const fps = frameMs > 0 ? Math.round(1000 / frameMs) : 0\n this.#stats.textContent = [\n `${fps ? `${fps} fps ${frameMs.toFixed(1)} ms` : 'idle'} cpu ${cpuMs.toFixed(1)} ms`,\n `${info.drawCalls} draws ${k(info.triangles)} triangles`,\n `${info.programs} programs${info.compiling ? ` (${info.compiling} compiling)` : ''} ${info.textures} textures ${info.geometries} geometries`,\n ].join('\\n')\n }\n\n dispose() {\n this.#stats?.remove()\n this.#stats = null\n this.#geometry.dispose()\n }\n\n // ---- the helpers, as line segments in world space, colours sRGB\n\n /** @param {ArrayLike<number>} a @param {ArrayLike<number>} b @param {ArrayLike<number>} c */\n #segment(a, b, c) {\n this.#positions.push(a[0], a[1], a[2], b[0], b[1], b[2])\n this.#colors.push(c[0], c[1], c[2], c[0], c[1], c[2])\n }\n\n /** @param {ArrayLike<number>} p @param {number} r @param {ArrayLike<number>} c */\n #cross(p, r, c) {\n for (let k = 0; k < 3; k++) {\n const a = [p[0], p[1], p[2]], b = [p[0], p[1], p[2]]\n a[k] -= r\n b[k] += r\n this.#segment(a, b, c)\n }\n }\n\n /** A circle round a centre, in the plane of two unit axes. @param {Vec3} centre @param {Vec3} u @param {Vec3} v @param {number} r @param {ArrayLike<number>} c */\n #circle(centre, u, v, r, c, segments = 32) {\n const at = (i) => {\n const a = i / segments * Math.PI * 2, x = Math.cos(a) * r, y = Math.sin(a) * r\n return [centre[0] + u[0] * x + v[0] * y, centre[1] + u[1] * x + v[1] * y, centre[2] + u[2] * x + v[2] * y]\n }\n for (let i = 0; i < segments; i++) this.#segment(at(i), at(i + 1), c)\n }\n\n /** The twelve edges of a box. @param {Box3} box @param {ArrayLike<number>} c */\n #box(box, c) {\n const { min: a, max: b } = box\n const corner = (i) => [i & 1 ? b[0] : a[0], i & 2 ? b[1] : a[1], i & 4 ? b[2] : a[2]]\n for (const [i, j] of EDGES) this.#segment(corner(i), corner(j), c)\n }\n\n /** The edges of what a view-projection matrix sees: a box for an orthographic one, a frustum for a perspective one. @param {ArrayLike<number>} viewProjection @param {ArrayLike<number>} c */\n #frustum(viewProjection, c) {\n const inverse = new Mat4().copy(viewProjection).invert()\n const corner = (i) => new Vec3(i & 1 ? 1 : -1, i & 2 ? 1 : -1, i & 4 ? 1 : -1).applyMatrix4(inverse)\n for (const [i, j] of EDGES) this.#segment(corner(i), corner(j), c)\n }\n\n /** @param {FrameDescription} frame @param {number} size @param {Vec3} middle */\n #lights(frame, size, middle) {\n for (const l of frame.lights) {\n const c = l.color.toSRGB().map((x) => Math.min(1, x))\n if (l.kind === 'directional' && l.direction) {\n // an arrow coming in over the scene's middle, the way the light travels\n const d = new Vec3().copy(l.direction).normalize()\n const from = moved(middle, d, -size * 1.1), to = moved(middle, d, -size * 0.45)\n this.#segment(from, to, c)\n const [u, v] = across(d)\n for (const s of [u, v, u.clone().negate(), v.clone().negate()]) {\n this.#segment(to, moved(moved(to, d, -size * 0.08), s, size * 0.04), c)\n }\n } else if (l.kind === 'point' && l.position) {\n const p = new Vec3().copy(l.position), r = size * 0.06\n this.#circle(p, X, Y, r, c, 16)\n this.#circle(p, X, Z, r, c, 16)\n this.#circle(p, Y, Z, r, c, 16)\n if (l.range) this.#circle(p, X, Z, l.range, c, 48)\n } else if (l.kind === 'spot' && l.position && l.direction) {\n // the cone out to its range (or the scene's size), with a ring where its edge is\n const p = new Vec3().copy(l.position), d = new Vec3().copy(l.direction).normalize()\n const length = l.range ? Math.min(l.range, size * 2) : size, r = length * Math.tan(l.angle ?? Math.PI / 4)\n const end = moved(p, d, length)\n const [u, v] = across(d)\n this.#circle(end, u, v, r, c, 32)\n for (const s of [u, v, u.clone().negate(), v.clone().negate()]) this.#segment(p, moved(end, s, r), c)\n this.#segment(p, end, c)\n }\n }\n }\n\n /** @param {FrameDescription} frame */\n #shadows(frame) {\n if (frame.shadow) this.#frustum(frame.shadow.viewProjection, COLORS.shadow)\n for (const s of frame.spotShadows) this.#frustum(s.viewProjection, COLORS.shadow)\n }\n\n /** @param {DrawItem[]} items @param {Hit | null} hovered @param {boolean} onlyHovered */\n #bounds(items, hovered, onlyHovered) {\n if (onlyHovered) {\n if (!hovered) return\n const node = /** @type {any} */ (hovered.node)\n // an instance's own box, or the whole node's\n if (hovered.instance !== null && typeof node.eachInstanceBox === 'function') {\n let i = 0\n node.eachInstanceBox((x0, y0, z0, x1, y1, z1) => {\n if (i++ === hovered.instance) this.#box(new Box3([x0, y0, z0], [x1, y1, z1]), COLORS.bounds)\n })\n } else this.#box(node.expandBounds(new Box3()), COLORS.bounds)\n return\n }\n const seen = new Set()\n for (const item of items) {\n if (seen.has(item.node)) continue\n seen.add(item.node)\n const box = /** @type {any} */ (item.node).expandBounds(new Box3())\n if (!box.isEmpty()) this.#box(box, COLORS.bounds)\n }\n }\n\n /** @param {DrawItem[]} items @param {Hit | null} hovered @param {boolean} onlyHovered @param {number} length */\n #normals(items, hovered, onlyHovered, length) {\n const meshes = onlyHovered\n ? (hovered && /** @type {any} */ (hovered.node).drawMode === 'triangles' ? [/** @type {any} */ (hovered.node)] : [])\n : [...new Set(items.map((i) => i.node))].filter((n) => /** @type {any} */ (n).drawMode === 'triangles')\n let budget = NORMALS_IN_ALL\n for (const node of /** @type {any[]} */ (meshes)) {\n if (node.instanceMatrices) continue\n const g = node.geometry, count = g.vertexCount\n if (!g.normals || (!onlyHovered && (count > NORMALS_PER_MESH || count > budget))) continue\n budget -= count\n const m = node.worldMatrix, p = g.positions, n = g.normals\n const a = new Vec3(), d = new Vec3()\n for (let i = 0; i < count; i++) {\n a.set(p[i * 3], p[i * 3 + 1], p[i * 3 + 2]).applyMatrix4(m)\n // the normal turned with the node (the upper 3x3: right for rotation and even scale)\n d.set(m[0] * n[i * 3] + m[4] * n[i * 3 + 1] + m[8] * n[i * 3 + 2], m[1] * n[i * 3] + m[5] * n[i * 3 + 1] + m[9] * n[i * 3 + 2],\n m[2] * n[i * 3] + m[6] * n[i * 3 + 1] + m[10] * n[i * 3 + 2]).normalize()\n this.#segment(a, [a[0] + d[0] * length, a[1] + d[1] * length, a[2] + d[2] * length], COLORS.normal)\n }\n }\n }\n}\n\nconst X = new Vec3(1, 0, 0), Y = new Vec3(0, 1, 0), Z = new Vec3(0, 0, 1)\n// a box's corners by bit (x, y, z), and the twelve edges between them\nconst EDGES = [[0, 1], [2, 3], [4, 5], [6, 7], [0, 2], [1, 3], [4, 6], [5, 7], [0, 4], [1, 5], [2, 6], [3, 7]]\n\n/** Two unit axes across a direction. @param {Vec3} d @returns {[Vec3, Vec3]} */\nfunction across(d) {\n const up = Math.abs(d[1]) < 0.9 ? Y : X\n const u = d.clone().cross(up).normalize()\n return [u, d.clone().cross(u).normalize()]\n}\n\n/** p moved s along d, as a new point. @param {ArrayLike<number>} p @param {ArrayLike<number>} d @param {number} s */\nfunction moved(p, d, s) {\n return new Vec3(p[0] + d[0] * s, p[1] + d[1] * s, p[2] + d[2] * s)\n}\n"],"mappings":";;AAmCA,IAAMO,IAAS;CAAE,QAAQ;EAAC;EAAG;EAAK;CAAC;CAAG,QAAQ;EAAC;EAAG;EAAK;CAAC;CAAG,QAAQ;EAAC;EAAG;EAAM;CAAI;CAAG,QAAQ;EAAC;EAAG;EAAG;CAAC;AAAE,GAEhGC,IAAmB,KAAMC,IAAiB,KAEnCC,IAAb,MAAuB;CACrB,KAAS,IAAIT,EAAM;CACnB,KAAY,IAAII,EAAS,EAAE,WAAW;EAAC;EAAG;EAAG;EAAG;EAAG;EAAG;CAAC,EAAE,CAAC;CAE1D,KAA+B,KAAKM,GAAO,IAAI,IAAIL,EAAM;EAAE,UAAU,KAAKM;EAAW,UAAU;EAAO,eAAe;EACnH,UAAU;GAAE,cAAc;GAAM,OAAO;GAAK,KAAK;EAAM;CAAE,CAAC,CAAC;CAC7D,KAA+B,KAAKD,GAAO,IAAI,IAAIL,EAAM;EAAE,UAAU,KAAKM;EAAW,UAAU;EAAO,eAAe;EAAO,aAAa;EACvI,UAAU;GAAE,cAAc;GAAM,OAAO;GAAG,SAAS;GAAM,WAAW;GAAO,YAAY;GAAO,KAAK;EAAM;CAAE,CAAC,CAAC;CAC1E,KAAS;CAC9C,KAAW;CACa,KAAa,CAAC;CACd,KAAU,CAAC;CAGnC,YAAY,GAAS;EACnB,KAAK,UAAUC;CACjB;CASA,IAAI,GAAO,GAAG,GAAI;EAEhB,AADA,KAAKC,KAAa,CAAC,GACnB,KAAKC,KAAU,CAAC;EAChB,IAAMC,IAAQ,CAAC,GAAGC,EAAM,QAAQ,GAAGA,EAAM,WAAW,GAC9CC,IAAOC,EAAG,OAAO,QAAQ,IAAI,IAAI,KAAK,IAAIA,EAAG,OAAO,UAAU,GAAG,IAAI,GACrEC,IAASD,EAAG,OAAO,QAAQ,IAAI,IAAIf,EAAK,IAAIe,EAAG,OAAO,UAAU;EAKtE,AAJIE,EAAE,UAAQ,KAAKC,GAAQL,GAAOC,GAAME,CAAM,GAC1CC,EAAE,WAAS,KAAKE,GAASN,CAAK,GAC9BI,EAAE,UAAQ,KAAKG,GAAQR,GAAOK,EAAE,WAAW,YAAYF,EAAG,UAAU,MAAME,EAAE,WAAW,SAAS,GAChGA,EAAE,WAAS,KAAKI,GAAST,GAAOK,EAAE,YAAY,YAAYF,EAAG,UAAU,MAAME,EAAE,YAAY,WAAWH,IAAO,IAAK,GAClHG,EAAE,SACJ,KAAKK,GAAS;GAAC;GAAG;GAAG;EAAC,GAAG;GAACR,IAAO;GAAK;GAAG;EAAC,GAAG;GAAC;GAAG;GAAK;EAAG,CAAC,GAC1D,KAAKQ,GAAS;GAAC;GAAG;GAAG;EAAC,GAAG;GAAC;GAAGR,IAAO;GAAK;EAAC,GAAG;GAAC;GAAK;GAAG;EAAG,CAAC,GAC1D,KAAKQ,GAAS;GAAC;GAAG;GAAG;EAAC,GAAG;GAAC;GAAG;GAAGR,IAAO;EAAG,GAAG;GAAC;GAAK;GAAK;EAAC,CAAC;EAE5D,IAAMS,IAA6BR,EAAG,OAAQ;EAE9C,IADIE,EAAE,UAAUM,KAAQ,KAAKC,GAAOD,GAAQT,IAAO,KAAMX,EAAO,MAAM,GAClE,CAAC,KAAKO,GAAW,QAAQ,OAAO;GAAE,QAAQ;GAAG,aAAa;EAAE;EAGhE,IAAMe,IAAI,KAAKf,GAAW,QACpBgB,IAAS,KAAKlB,GAAU;EAU9B,AATI,KAAKA,GAAU,UAAU,WAAWiB,KAAKC,KAC3C,KAAKlB,GAAU,UAAU,IAAI,KAAKE,EAAU,GAC5CgB,EAAO,IAAI,KAAKf,EAAO,GACvB,KAAKH,GAAU,YAAY,MAE3B,KAAKA,GAAU,QAAQ,GACvB,KAAKA,KAAY,IAAIP,EAAS;GAAE,WAAW,KAAKS;GAAY,QAAQ,KAAKC;EAAQ,CAAC,GAClF,KAAKgB,GAAO,WAAW,KAAKC,GAAO,WAAW,KAAKpB,KAErD,KAAKD,GAAO,gBAAgB;EAC5B,IAAMsB,IAAQjC,EAAQ,KAAKW,IAAQQ,EAAG,MAAM;EAG5C,OAFAF,EAAM,OAAO,KAAK,GAAGgB,EAAM,MAAM,GACjChB,EAAM,YAAY,KAAK,GAAGgB,EAAM,WAAW,GACpC;GAAE,QAAQA,EAAM,OAAO;GAAQ,aAAaA,EAAM,YAAY;EAAO;CAC9E;CAQA,MAAM,GAAO,GAAM,GAAS,GAAO;EACjC,IAAI,CAACC,KAAS,CAAC,KAAK,WAAW,OAAO,WAAa,KAAa;GAE9D,AADA,KAAKC,IAAQ,OAAO,GACpB,KAAKA,KAAS;GACd;EACF;EACA,IAAMC,IAASF,MAAU,KAAO,aAAaA;EAC7C,AAAK,KAAKC,OACR,KAAKA,KAAS,SAAS,cAAc,KAAK,GAC1C,KAAKA,GAAO,YAAY,sBACxB,OAAO,OAAO,KAAKA,GAAO,OAAO;GAAE,UAAU;GAAY,QAAQ;GAAK,eAAe;GAAQ,SAAS;GAAW,cAAc;GAC7H,YAAY;GAA0B,OAAO;GAAW,MAAM;GAA+D,YAAY;EAAM,CAAC,GAClJ,KAAK,QAAQ,OAAO,KAAKA,EAAM;EAEjC,IAAM,CAACE,GAAGC,KAAKF,EAAO,MAAM,GAAG;EAC/B,OAAO,OAAO,KAAKD,GAAO,OAAO;GAAE,KAAKE,MAAM,QAAQ,QAAQ;GAAI,QAAQA,MAAM,WAAW,QAAQ;GAAI,MAAMC,MAAM,SAAS,QAAQ;GAAI,OAAOA,MAAM,UAAU,QAAQ;EAAG,CAAC;EAC3K,IAAMC,IAAM,YAAY,IAAI;EAC5B,IAAIA,IAAM,KAAKC,KAAW,KAAK;EAC/B,KAAKA,KAAWD;EAChB,IAAME,KAAK,MAAOC,KAAK,MAAM,IAAIA,IAAI,IAAA,CAAK,QAAQ,CAAC,EAAE,KAAKA,KAAK,MAAM,GAAG,KAAK,MAAMA,IAAI,GAAG,EAAE,KAAK,OAAOA,CAAC,GACnGC,IAAMC,IAAU,IAAI,KAAK,MAAM,MAAOA,CAAO,IAAI;EACvD,KAAKT,GAAO,cAAc;GACxB,GAAGQ,IAAM,GAAGA,EAAI,QAAQC,EAAQ,QAAQ,CAAC,EAAE,OAAO,OAAO,QAAQC,EAAM,QAAQ,CAAC,EAAE;GAClF,GAAGC,EAAK,UAAU,UAAUL,EAAEK,EAAK,SAAS,EAAE;GAC9C,GAAGA,EAAK,SAAS,WAAWA,EAAK,YAAY,KAAKA,EAAK,UAAU,eAAe,GAAG,IAAIA,EAAK,SAAS,aAAaA,EAAK,WAAW;EACpI,CAAC,CAAC,KAAK,IAAI;CACb;CAEA,UAAU;EAGR,AAFA,KAAKX,IAAQ,OAAO,GACpB,KAAKA,KAAS,MACd,KAAKvB,GAAU,QAAQ;CACzB;CAKA,GAAS,GAAG,GAAG,GAAG;EAEhB,AADA,KAAKE,GAAW,KAAKiC,EAAE,IAAIA,EAAE,IAAIA,EAAE,IAAIC,EAAE,IAAIA,EAAE,IAAIA,EAAE,EAAE,GACvD,KAAKjC,GAAQ,KAAKkC,EAAE,IAAIA,EAAE,IAAIA,EAAE,IAAIA,EAAE,IAAIA,EAAE,IAAIA,EAAE,EAAE;CACtD;CAGA,GAAO,GAAG,GAAG,GAAG;EACd,KAAK,IAAIR,IAAI,GAAGA,IAAI,GAAG,KAAK;GAC1B,IAAMM,IAAI;IAACG,EAAE;IAAIA,EAAE;IAAIA,EAAE;GAAE,GAAGF,IAAI;IAACE,EAAE;IAAIA,EAAE;IAAIA,EAAE;GAAE;GAGnD,AAFA,EAAET,MAAMU,GACR,EAAEV,MAAMU,GACR,KAAKzB,GAASqB,GAAGC,GAAGC,CAAC;EACvB;CACF;CAGA,GAAQ,GAAQ,GAAG,GAAG,GAAG,GAAG,IAAW,IAAI;EACzC,IAAM9B,KAAM,MAAM;GAChB,IAAM4B,IAAI,IAAIK,IAAW,KAAK,KAAK,GAAGV,IAAI,KAAK,IAAIK,CAAC,IAAI,GAAGM,IAAI,KAAK,IAAIN,CAAC,IAAI;GAC7E,OAAO;IAACO,EAAO,KAAKC,EAAE,KAAKb,IAAIL,EAAE,KAAKgB;IAAGC,EAAO,KAAKC,EAAE,KAAKb,IAAIL,EAAE,KAAKgB;IAAGC,EAAO,KAAKC,EAAE,KAAKb,IAAIL,EAAE,KAAKgB;GAAC;EAC3G;EACA,KAAK,IAAIG,IAAI,GAAGA,IAAIJ,GAAU,KAAK,KAAK1B,GAASP,EAAGqC,CAAC,GAAGrC,EAAGqC,IAAI,CAAC,GAAGP,CAAC;CACtE;CAGA,GAAK,GAAK,GAAG;EACX,IAAM,EAAE,KAAKF,GAAG,KAAKC,MAAMS,GACrBrB,KAAU,MAAM;GAACoB,IAAI,IAAIR,EAAE,KAAKD,EAAE;GAAIS,IAAI,IAAIR,EAAE,KAAKD,EAAE;GAAIS,IAAI,IAAIR,EAAE,KAAKD,EAAE;EAAE;EACpF,KAAK,IAAM,CAACS,GAAGE,MAAMC,GAAO,KAAKjC,GAASU,EAAOoB,CAAC,GAAGpB,EAAOsB,CAAC,GAAGT,CAAC;CACnE;CAGA,GAAS,GAAgB,GAAG;EAC1B,IAAMW,IAAU,IAAI1D,EAAK,CAAC,CAAC,KAAK2D,CAAc,CAAC,CAAC,OAAO,GACjDzB,KAAU,MAAM,IAAIhC,EAAKoD,IAAI,IAAI,IAAI,IAAIA,IAAI,IAAI,IAAI,IAAIA,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,aAAaI,CAAO;EACnG,KAAK,IAAM,CAACJ,GAAGE,MAAMC,GAAO,KAAKjC,GAASU,EAAOoB,CAAC,GAAGpB,EAAOsB,CAAC,GAAGT,CAAC;CACnE;CAGA,GAAQ,GAAO,GAAM,GAAQ;EAC3B,KAAK,IAAMa,KAAK7C,EAAM,QAAQ;GAC5B,IAAMgC,IAAIa,EAAE,MAAM,OAAO,CAAC,CAAC,KAAK,MAAM,KAAK,IAAI,GAAGpB,CAAC,CAAC;GACpD,IAAIoB,EAAE,SAAS,iBAAiBA,EAAE,WAAW;IAE3C,IAAMC,IAAI,IAAI3D,EAAK,CAAC,CAAC,KAAK0D,EAAE,SAAS,CAAC,CAAC,UAAU,GAC3CE,IAAOC,EAAM7C,GAAQ2C,GAAG,CAAC7C,IAAO,GAAG,GAAGgD,IAAKD,EAAM7C,GAAQ2C,GAAG,CAAC7C,IAAO,GAAI;IAC9E,KAAKQ,GAASsC,GAAME,GAAIjB,CAAC;IACzB,IAAM,CAACM,GAAGlB,KAAK8B,EAAOJ,CAAC;IACvB,KAAK,IAAMK,KAAK;KAACb;KAAGlB;KAAGkB,EAAE,MAAM,CAAC,CAAC,OAAO;KAAGlB,EAAE,MAAM,CAAC,CAAC,OAAO;IAAC,GAC3D,KAAKX,GAASwC,GAAID,EAAMA,EAAMC,GAAIH,GAAG,CAAC7C,IAAO,GAAI,GAAGkD,GAAGlD,IAAO,GAAI,GAAG+B,CAAC;GAE1E,OAAO,IAAIa,EAAE,SAAS,WAAWA,EAAE,UAAU;IAC3C,IAAMZ,IAAI,IAAI9C,EAAK,CAAC,CAAC,KAAK0D,EAAE,QAAQ,GAAGX,IAAIjC,IAAO;IAIlD,AAHA,KAAKmD,GAAQnB,GAAGoB,GAAGC,GAAGpB,GAAGF,GAAG,EAAE,GAC9B,KAAKoB,GAAQnB,GAAGoB,GAAGE,GAAGrB,GAAGF,GAAG,EAAE,GAC9B,KAAKoB,GAAQnB,GAAGqB,GAAGC,GAAGrB,GAAGF,GAAG,EAAE,GAC1Ba,EAAE,SAAO,KAAKO,GAAQnB,GAAGoB,GAAGE,GAAGV,EAAE,OAAOb,GAAG,EAAE;GACnD,OAAO,IAAIa,EAAE,SAAS,UAAUA,EAAE,YAAYA,EAAE,WAAW;IAEzD,IAAMZ,IAAI,IAAI9C,EAAK,CAAC,CAAC,KAAK0D,EAAE,QAAQ,GAAGC,IAAI,IAAI3D,EAAK,CAAC,CAAC,KAAK0D,EAAE,SAAS,CAAC,CAAC,UAAU,GAC5EW,IAASX,EAAE,QAAQ,KAAK,IAAIA,EAAE,OAAO5C,IAAO,CAAC,IAAIA,GAAMiC,IAAIsB,IAAS,KAAK,IAAIX,EAAE,SAAS,KAAK,KAAK,CAAC,GACnGY,IAAMT,EAAMf,GAAGa,GAAGU,CAAM,GACxB,CAAClB,GAAGlB,KAAK8B,EAAOJ,CAAC;IACvB,KAAKM,GAAQK,GAAKnB,GAAGlB,GAAGc,GAAGF,GAAG,EAAE;IAChC,KAAK,IAAMmB,KAAK;KAACb;KAAGlB;KAAGkB,EAAE,MAAM,CAAC,CAAC,OAAO;KAAGlB,EAAE,MAAM,CAAC,CAAC,OAAO;IAAC,GAAG,KAAKX,GAASwB,GAAGe,EAAMS,GAAKN,GAAGjB,CAAC,GAAGF,CAAC;IACpG,KAAKvB,GAASwB,GAAGwB,GAAKzB,CAAC;GACzB;EACF;CACF;CAGA,GAAS,GAAO;EACd,AAAIhC,EAAM,UAAQ,KAAK0D,GAAS1D,EAAM,OAAO,gBAAgBV,EAAO,MAAM;EAC1E,KAAK,IAAM6D,KAAKnD,EAAM,aAAa,KAAK0D,GAASP,EAAE,gBAAgB7D,EAAO,MAAM;CAClF;CAGA,GAAQ,GAAO,GAAS,GAAa;EACnC,IAAIqE,GAAa;GACf,IAAI,CAACC,GAAS;GACd,IAAMC,IAA2BD,EAAQ;GAEzC,IAAIA,EAAQ,aAAa,QAAQ,OAAOC,EAAK,mBAAoB,YAAY;IAC3E,IAAItB,IAAI;IACR,EAAK,iBAAiB,GAAI,GAAI,GAAI,GAAI,GAAI,MAAO;KAC/C,AAAI,QAAQqB,EAAQ,YAAU,KAAKE,GAAK,IAAI5E,EAAK;MAAC6E;MAAIC;MAAIC;KAAE,GAAG;MAACC;MAAIC;MAAIC;KAAE,CAAC,GAAG9E,EAAO,MAAM;IAC7F,CAAC;GACH,OAAO,KAAKwE,GAAKD,EAAK,aAAa,IAAI3E,EAAK,CAAC,GAAGI,EAAO,MAAM;GAC7D;EACF;EACA,IAAM+E,oBAAO,IAAI,IAAI;EACrB,KAAK,IAAMC,KAAQvE,GAAO;GACxB,IAAIsE,EAAK,IAAIC,EAAK,IAAI,GAAG;GACzB,EAAK,IAAIA,EAAK,IAAI;GAClB,IAAM9B,IAA0B8B,EAAK,KAAM,aAAa,IAAIpF,EAAK,CAAC;GAClE,AAAKsD,EAAI,QAAQ,KAAG,KAAKsB,GAAKtB,GAAKlD,EAAO,MAAM;EAClD;CACF;CAGA,GAAS,GAAO,GAAS,GAAa,GAAQ;EAC5C,IAAMiF,IAASZ,IACVC,KAA+BA,EAAQ,KAAM,aAAa,cAAc,CAAqBA,EAAQ,IAAK,IAAI,CAAC,IAChH,CAAC,GAAG,IAAI,IAAI7D,EAAM,KAAK,MAAMwC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,MAA0B3B,EAAG,aAAa,WAAW,GACpG4D,IAAShF;EACb,KAAK,IAAMqE,KAA8BU,GAAS;GAChD,IAAIV,EAAK,kBAAkB;GAC3B,IAAMY,IAAIZ,EAAK,UAAUa,IAAQD,EAAE;GACnC,IAAI,CAACA,EAAE,WAAY,CAACd,MAAgBe,IAAQnF,KAAoBmF,IAAQF,IAAU;GAClF,KAAUE;GACV,IAAMC,IAAId,EAAK,aAAa5B,IAAIwC,EAAE,WAAW7D,IAAI6D,EAAE,SAC7C3C,IAAI,IAAI3C,EAAK,GAAG2D,IAAI,IAAI3D,EAAK;GACnC,KAAK,IAAIoD,IAAI,GAAGA,IAAImC,GAAO,KAKzB,AAJA5C,EAAE,IAAIG,EAAEM,IAAI,IAAIN,EAAEM,IAAI,IAAI,IAAIN,EAAEM,IAAI,IAAI,EAAE,CAAC,CAAC,aAAaoC,CAAC,GAE1D7B,EAAE,IAAI6B,EAAE,KAAK/D,EAAE2B,IAAI,KAAKoC,EAAE,KAAK/D,EAAE2B,IAAI,IAAI,KAAKoC,EAAE,KAAK/D,EAAE2B,IAAI,IAAI,IAAIoC,EAAE,KAAK/D,EAAE2B,IAAI,KAAKoC,EAAE,KAAK/D,EAAE2B,IAAI,IAAI,KAAKoC,EAAE,KAAK/D,EAAE2B,IAAI,IAAI,IAC1HoC,EAAE,KAAK/D,EAAE2B,IAAI,KAAKoC,EAAE,KAAK/D,EAAE2B,IAAI,IAAI,KAAKoC,EAAE,MAAM/D,EAAE2B,IAAI,IAAI,EAAE,CAAC,CAAC,UAAU,GAC1E,KAAK9B,GAASqB,GAAG;IAACA,EAAE,KAAKgB,EAAE,KAAKU;IAAQ1B,EAAE,KAAKgB,EAAE,KAAKU;IAAQ1B,EAAE,KAAKgB,EAAE,KAAKU;GAAM,GAAGlE,EAAO,MAAM;EAEtG;CACF;AACF,GAEM+D,IAAI,IAAIlE,EAAK,GAAG,GAAG,CAAC,GAAGmE,IAAI,IAAInE,EAAK,GAAG,GAAG,CAAC,GAAGoE,IAAI,IAAIpE,EAAK,GAAG,GAAG,CAAC,GAElEuD,IAAQ;CAAC,CAAC,GAAG,CAAC;CAAG,CAAC,GAAG,CAAC;CAAG,CAAC,GAAG,CAAC;CAAG,CAAC,GAAG,CAAC;CAAG,CAAC,GAAG,CAAC;CAAG,CAAC,GAAG,CAAC;CAAG,CAAC,GAAG,CAAC;CAAG,CAAC,GAAG,CAAC;CAAG,CAAC,GAAG,CAAC;CAAG,CAAC,GAAG,CAAC;CAAG,CAAC,GAAG,CAAC;CAAG,CAAC,GAAG,CAAC;AAAC;AAG7G,SAASQ,EAAO,GAAG;CACjB,IAAM0B,IAAK,KAAK,IAAI9B,EAAE,EAAE,IAAI,KAAMQ,IAAID,GAChCf,IAAIQ,EAAE,MAAM,CAAC,CAAC,MAAM8B,CAAE,CAAC,CAAC,UAAU;CACxC,OAAO,CAACtC,GAAGQ,EAAE,MAAM,CAAC,CAAC,MAAMR,CAAC,CAAC,CAAC,UAAU,CAAC;AAC3C;AAGA,SAASU,EAAM,GAAG,GAAG,GAAG;CACtB,OAAO,IAAI7D,EAAK8C,EAAE,KAAKa,EAAE,KAAKK,GAAGlB,EAAE,KAAKa,EAAE,KAAKK,GAAGlB,EAAE,KAAKa,EAAE,KAAKK,CAAC;AACnE"}
package/dist/edit.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Dt as e, Et as t, Ot as n, et as r, jt as i, kt as a, rt as o, u as s, ut as c, v as l, wt as u } from "./core.js";
1
+ import { At as e, Ft as t, Mt as n, Nt as r, Ot as i, it as a, jt as o, mt as s, st as c, u as l, v as u } from "./core.js";
2
2
  // src/edit/TransformHandle.js
3
3
  var d = [
4
4
  "view",
@@ -26,7 +26,7 @@ var d = [
26
26
  ring: "#ffcc33",
27
27
  disc: "#f2f2f2"
28
28
  }, m = 1e5, h = class {
29
- node = new o({
29
+ node = new c({
30
30
  name: "transform-handle",
31
31
  highlight: "#ffffff"
32
32
  });
@@ -43,12 +43,12 @@ var d = [
43
43
  #n = null;
44
44
  #r = {};
45
45
  #i = null;
46
- constructor(r) {
47
- if (n("TransformHandle", r, d, f), r?.view?.scene || i("TransformHandle", "'view' is required - the View it is used in"), this.#e = r.view, r.move !== void 0 && (this.move = r.move !== !1 && e("TransformHandle", "move", r.move, ["xz", "xyz"])), r.turn !== void 0 && (this.turn = _("turn", r.turn)), r.dragTarget !== void 0 && (this.dragTarget = _("dragTarget", r.dragTarget)), r.size !== void 0 && (this.size = t("TransformHandle", "size", r.size), this.size > 0 || i("TransformHandle", `'size' is in pixels, above 0, got ${this.size}`)), r.snap != null) {
48
- n("TransformHandle", r.snap, ["move", "turn"], {}, "snap");
46
+ constructor(i) {
47
+ if (n("TransformHandle", i, d, f), i?.view?.scene || t("TransformHandle", "'view' is required - the View it is used in"), this.#e = i.view, i.move !== void 0 && (this.move = i.move !== !1 && o("TransformHandle", "move", i.move, ["xz", "xyz"])), i.turn !== void 0 && (this.turn = _("turn", i.turn)), i.dragTarget !== void 0 && (this.dragTarget = _("dragTarget", i.dragTarget)), i.size !== void 0 && (this.size = e("TransformHandle", "size", i.size), this.size > 0 || t("TransformHandle", `'size' is in pixels, above 0, got ${this.size}`)), i.snap != null) {
48
+ n("TransformHandle", i.snap, ["move", "turn"], {}, "snap");
49
49
  let e = (e) => {
50
- let t = r.snap?.[e];
51
- return t === void 0 ? 0 : (typeof t == "number" && t >= 0 || i("TransformHandle", `'snap.${e}' must be a step of 0 or more, got ${a(t)}`), t);
50
+ let n = i.snap?.[e];
51
+ return n === void 0 ? 0 : (typeof n == "number" && n >= 0 || t("TransformHandle", `'snap.${e}' must be a step of 0 or more, got ${r(n)}`), n);
52
52
  };
53
53
  this.snap = {
54
54
  move: e("move"),
@@ -56,10 +56,10 @@ var d = [
56
56
  };
57
57
  }
58
58
  for (let e of ["constrain", "onChange"]) {
59
- let t = r[e];
60
- t != null && (typeof t != "function" && i("TransformHandle", `'${e}' must be a function, got ${a(t)}`), this[e] = t);
59
+ let n = i[e];
60
+ n != null && (typeof n != "function" && t("TransformHandle", `'${e}' must be a function, got ${r(n)}`), this[e] = n);
61
61
  }
62
- r.history != null && (typeof r.history.transform != "function" && i("TransformHandle", `'history' must be a History, got ${a(r.history)}`), this.history = r.history), this.#a(), this.node.visible = !1, this.node.onUpdate(() => this.#o()), this.#e.scene.add(this.node), r.target != null && (this.target = r.target);
62
+ i.history != null && (typeof i.history.transform != "function" && t("TransformHandle", `'history' must be a History, got ${r(i.history)}`), this.history = i.history), this.#a(), this.node.visible = !1, this.node.onUpdate(() => this.#o()), this.#e.scene.add(this.node), i.target != null && (this.target = i.target);
63
63
  }
64
64
  get target() {
65
65
  return this.#t;
@@ -80,22 +80,22 @@ var d = [
80
80
  depthTest: !1,
81
81
  depthWrite: !1,
82
82
  opacity: .95
83
- }), t = (t, n, i, a = [
83
+ }), t = (t, n, r, i = [
84
84
  0,
85
85
  0,
86
86
  0
87
87
  ], o = t) => {
88
- let s = this.node.add(new r({
88
+ let s = this.node.add(new a({
89
89
  name: t,
90
90
  geometry: n,
91
- material: e(i),
92
- position: a,
91
+ material: e(r),
92
+ position: i,
93
93
  renderOrder: m,
94
94
  castShadow: !1
95
95
  }));
96
96
  return s.onDrag = (e) => this.#s(o, e), this.#r[t] = s, s;
97
97
  };
98
- t("ring", l({
98
+ t("ring", u({
99
99
  radius: 1,
100
100
  tube: .035,
101
101
  segments: 72,
@@ -115,12 +115,12 @@ var d = [
115
115
  0,
116
116
  t
117
117
  ];
118
- t(`${e}-shaft`, s({
118
+ t(`${e}-shaft`, l({
119
119
  radius: .035,
120
120
  height: .4,
121
121
  segments: 10,
122
122
  axis: e
123
- }), n, r(1.3), e), t(`${e}-tip`, s({
123
+ }), n, r(1.3), e), t(`${e}-tip`, l({
124
124
  radius: .1,
125
125
  radiusTop: 0,
126
126
  height: .22,
@@ -128,7 +128,7 @@ var d = [
128
128
  axis: e
129
129
  }), n, r(1.6), e);
130
130
  };
131
- n("x", p.x), n("z", p.z), n("y", p.y), t("disc", s({
131
+ n("x", p.x), n("z", p.z), n("y", p.y), t("disc", l({
132
132
  radius: .16,
133
133
  height: .02,
134
134
  segments: 32
@@ -157,21 +157,21 @@ var d = [
157
157
  if (t.phase === "start") {
158
158
  let r = n.worldPosition;
159
159
  if (e === "y") {
160
- let e = this.#e.camera.position, n = new u(e[0] - r[0], 0, e[2] - r[2]);
160
+ let e = this.#e.camera.position, n = new i(e[0] - r[0], 0, e[2] - r[2]);
161
161
  t.plane = {
162
- normal: n.len() > 1e-6 ? n.normalize() : new u(0, 0, 1),
162
+ normal: n.len() > 1e-6 ? n.normalize() : new i(0, 0, 1),
163
163
  point: r.clone()
164
164
  };
165
165
  } else t.plane = {
166
- normal: new u(0, 1, 0),
166
+ normal: new i(0, 1, 0),
167
167
  point: r.clone()
168
168
  };
169
- let i = t.ray.intersectPlane(t.plane.normal, t.plane.point), a = i === null ? r : t.ray.at(i);
169
+ let a = t.ray.intersectPlane(t.plane.normal, t.plane.point), o = a === null ? r : t.ray.at(a);
170
170
  this.#i = {
171
171
  position: n.position.clone(),
172
172
  rotation: g(n.quaternion),
173
173
  quaternion: n.quaternion.clone(),
174
- angle: Math.atan2(a[2] - r[2], a[0] - r[0]),
174
+ angle: Math.atan2(o[2] - r[2], o[0] - r[0]),
175
175
  done: this.history ? this.history.transform(n) : null
176
176
  }, this.onChange?.(n, "start");
177
177
  return;
@@ -183,13 +183,13 @@ var d = [
183
183
  return;
184
184
  }
185
185
  if (!t.point) return;
186
- let i = {
186
+ let a = {
187
187
  position: r.position.clone(),
188
188
  rotation: r.rotation
189
189
  };
190
190
  if (e === "ring") {
191
- let e = this.node.position, n = Math.atan2(t.point[2] - e[2], t.point[0] - e[0]), a = r.rotation - (n - r.angle);
192
- this.snap?.turn && (a = Math.round(a / this.snap.turn) * this.snap.turn), i.rotation = a;
191
+ let e = this.node.position, n = Math.atan2(t.point[2] - e[2], t.point[0] - e[0]), i = r.rotation - (n - r.angle);
192
+ this.snap?.turn && (i = Math.round(i / this.snap.turn) * this.snap.turn), a.rotation = i;
193
193
  } else {
194
194
  let n = t.delta, r = e === "x" ? [
195
195
  n[0],
@@ -208,28 +208,28 @@ var d = [
208
208
  0,
209
209
  n[2]
210
210
  ];
211
- i.position.add(r);
212
- let a = this.snap?.move;
213
- if (a) for (let e = 0; e < 3; e++) r[e] !== 0 && (i.position[e] = Math.round(i.position[e] / a) * a);
211
+ a.position.add(r);
212
+ let i = this.snap?.move;
213
+ if (i) for (let e = 0; e < 3; e++) r[e] !== 0 && (a.position[e] = Math.round(a.position[e] / i) * i);
214
214
  }
215
- let a = this.constrain ? this.constrain(i, n) : i;
216
- if (a === null) return;
217
- let o = a ?? i;
218
- n.position.copy(o.position);
219
- let s = new c().setFromAxisAngle([
215
+ let o = this.constrain ? this.constrain(a, n) : a;
216
+ if (o === null) return;
217
+ let c = o ?? a;
218
+ n.position.copy(c.position);
219
+ let l = new s().setFromAxisAngle([
220
220
  0,
221
221
  1,
222
222
  0
223
- ], o.rotation - r.rotation);
224
- n.quaternion.copy(s.multiply(r.quaternion)), this.#o(), this.onChange?.(n, "move");
223
+ ], c.rotation - r.rotation);
224
+ n.quaternion.copy(l.multiply(r.quaternion)), this.#o(), this.onChange?.(n, "move");
225
225
  }
226
226
  };
227
227
  function g(e) {
228
228
  let [t, n, r, i] = e;
229
229
  return Math.atan2(-2 * (t * r - n * i), 1 - 2 * (n * n + r * r));
230
230
  }
231
- function _(e, t) {
232
- return typeof t != "boolean" && i("TransformHandle", `'${e}' must be true or false, got ${a(t)}`), t;
231
+ function _(e, n) {
232
+ return typeof n != "boolean" && t("TransformHandle", `'${e}' must be true or false, got ${r(n)}`), n;
233
233
  }
234
234
  // src/edit/History.js
235
235
  var v = ["limit", "onChange"], y = {
@@ -242,10 +242,10 @@ var v = ["limit", "onChange"], y = {
242
242
  #e = [];
243
243
  #t = [];
244
244
  constructor(e = {}) {
245
- n("History", e, v, y), e.limit !== void 0 && ((!Number.isInteger(e.limit) || e.limit < 1) && i("History", `'limit' must be a whole number 1 or more, got ${a(e.limit)}`), this.limit = e.limit), e.onChange != null && (typeof e.onChange != "function" && i("History", `'onChange' must be a function, got ${a(e.onChange)}`), this.onChange = e.onChange);
245
+ n("History", e, v, y), e.limit !== void 0 && ((!Number.isInteger(e.limit) || e.limit < 1) && t("History", `'limit' must be a whole number 1 or more, got ${r(e.limit)}`), this.limit = e.limit), e.onChange != null && (typeof e.onChange != "function" && t("History", `'onChange' must be a function, got ${r(e.onChange)}`), this.onChange = e.onChange);
246
246
  }
247
247
  push(e) {
248
- return e && typeof e.undo == "function" && typeof e.redo == "function" || i("History", `push() takes { label, undo, redo } with undo and redo functions, got ${a(e)}`), this.#e.push(e), this.#e.length > this.limit && this.#e.shift(), this.#t = [], this.onChange?.(this), this;
248
+ return e && typeof e.undo == "function" && typeof e.redo == "function" || t("History", `push() takes { label, undo, redo } with undo and redo functions, got ${r(e)}`), this.#e.push(e), this.#e.length > this.limit && this.#e.shift(), this.#t = [], this.onChange?.(this), this;
249
249
  }
250
250
  undo() {
251
251
  let e = this.#e.pop();