@selvajs/visualization 1.6.0-beta.8 → 1.6.0-beta.9

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 (44) hide show
  1. package/dist/gpu-dispose-Cl3RXm-v.js +355 -0
  2. package/dist/gpu-dispose-Cl3RXm-v.js.map +1 -0
  3. package/dist/gpu-dispose-fRI12OCL.cjs +469 -0
  4. package/dist/gpu-dispose-fRI12OCL.cjs.map +1 -0
  5. package/dist/gpu-ownership-ADRZBxxs.cjs +102 -0
  6. package/dist/gpu-ownership-ADRZBxxs.cjs.map +1 -0
  7. package/dist/gpu-ownership-Bs9l7hrU.js +40 -0
  8. package/dist/gpu-ownership-Bs9l7hrU.js.map +1 -0
  9. package/dist/index.js +1 -1
  10. package/dist/merged-draw-CO4rr2Co.js +129 -0
  11. package/dist/merged-draw-CO4rr2Co.js.map +1 -0
  12. package/dist/merged-draw-ma_2r4N8.cjs +171 -0
  13. package/dist/merged-draw-ma_2r4N8.cjs.map +1 -0
  14. package/dist/parse.cjs +2789 -11
  15. package/dist/parse.cjs.map +1 -1
  16. package/dist/parse.d.cts +8 -2
  17. package/dist/parse.d.ts +8 -2
  18. package/dist/parse.js +2780 -11
  19. package/dist/parse.js.map +1 -1
  20. package/dist/render.cjs +3713 -6
  21. package/dist/render.cjs.map +1 -1
  22. package/dist/render.d.cts +23 -3
  23. package/dist/render.d.ts +23 -3
  24. package/dist/render.js +3692 -6
  25. package/dist/render.js.map +1 -1
  26. package/dist/scene.cjs +343 -1
  27. package/dist/scene.cjs.map +1 -1
  28. package/dist/scene.js +336 -1
  29. package/dist/scene.js.map +1 -1
  30. package/dist/{types-P5PyfYYE.d.ts → types-DW_qU4-Y.d.cts} +22 -3
  31. package/dist/{types-P5PyfYYE.d.cts → types-DW_qU4-Y.d.ts} +22 -3
  32. package/package.json +2 -2
  33. package/dist/gpu-dispose-Cestx__L.cjs +0 -2
  34. package/dist/gpu-dispose-Cestx__L.cjs.map +0 -1
  35. package/dist/gpu-dispose-gTGxGTL4.js +0 -2
  36. package/dist/gpu-dispose-gTGxGTL4.js.map +0 -1
  37. package/dist/gpu-ownership-DcHWbATk.js +0 -2
  38. package/dist/gpu-ownership-DcHWbATk.js.map +0 -1
  39. package/dist/gpu-ownership-ilj3JEdn.cjs +0 -2
  40. package/dist/gpu-ownership-ilj3JEdn.cjs.map +0 -1
  41. package/dist/merged-draw-DXJK-rdo.js +0 -2
  42. package/dist/merged-draw-DXJK-rdo.js.map +0 -1
  43. package/dist/merged-draw-oKAKlXCQ.cjs +0 -2
  44. package/dist/merged-draw-oKAKlXCQ.cjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gpu-ownership-Bs9l7hrU.js","names":[],"sources":["../src/shared/gpu-ownership.ts"],"sourcesContent":["import type * as THREE from 'three';\n\n// ============================================================================\n// GPU resource ownership — the single rule every disposal path obeys\n// ============================================================================\n\n/**\n * A GPU resource has exactly one owner: a **module singleton** (nobody ever frees it), a **cache**\n * (frees it on eviction) or the **scene** (freed on teardown). Every disposal path asks\n * {@link canDisposeMaterial} before freeing a material. Textures are always scene-owned.\n *\n * A cached geometry's data belongs to the cache, but its GPU copy lives only while a scene draws it.\n * Scene teardown resolves each geometry with {@link cachedSourceOf} and frees the GPU copy of every\n * cached geometry the next scene does not draw; three uploads it again if it comes back. A cached\n * geometry can back several meshes at once, so nothing may write to its arrays.\n */\n\n// Module-scope singletons from `render/three-materials.ts`, shared across meshes and solves —\n// disposing one would free textures still in use and force a recompile. Held here (not imported\n// there) to avoid an import cycle.\nconst protectedMaterials = new Set<THREE.Material>();\n\n/** Register materials as never-disposable. Called once by `render/three-materials.ts`. */\nexport function protectMaterials(materials: Iterable<THREE.Material>): void {\n\tfor (const material of materials) protectedMaterials.add(material);\n}\n\n/** True when `material` is scene-owned (not one of the protected singletons). */\nexport function canDisposeMaterial(material: THREE.Material | undefined): boolean {\n\tif (!material) return false;\n\treturn !protectedMaterials.has(material);\n}\n\n// Geometries the parser's finished-geometry cache hands out across solves.\nconst sharedGeometries = new WeakSet<THREE.BufferGeometry>();\n\nexport function shareGeometry(geometry: THREE.BufferGeometry): void {\n\tsharedGeometries.add(geometry);\n}\n\nexport function unshareGeometry(geometry: THREE.BufferGeometry): void {\n\tsharedGeometries.delete(geometry);\n}\n\nexport function isCachedGeometry(geometry: THREE.BufferGeometry): boolean {\n\treturn sharedGeometries.has(geometry);\n}\n\n// Scene-owned geometries drawing a cached geometry's attributes under their own groups. Disposing\n// one as is would free those attributes' GPU buffers out from under the cache.\nconst borrowers = new WeakMap<THREE.BufferGeometry, THREE.BufferGeometry>();\n\nexport function borrowGeometry(borrower: THREE.BufferGeometry, source: THREE.BufferGeometry): void {\n\tborrowers.set(borrower, source);\n}\n\n/** The cached geometry whose GPU buffers `geometry` draws, or null when it owns its own. */\nexport function cachedSourceOf(geometry: THREE.BufferGeometry): THREE.BufferGeometry | null {\n\tif (sharedGeometries.has(geometry)) return geometry;\n\t// Once evicted, the source is scene-owned like any other geometry, and the borrower frees it.\n\tconst source = borrowers.get(geometry);\n\treturn source && sharedGeometries.has(source) ? source : null;\n}\n"],"mappings":";;;;;;;;;;;AAoBA,MAAM,qCAAqB,IAAI,IAAoB;;AAQnD,SAAgB,mBAAmB,UAA+C;CACjF,IAAI,CAAC,UAAU,OAAO;CACtB,OAAO,CAAC,mBAAmB,IAAI,QAAQ;AACxC;AAGA,MAAM,mCAAmB,IAAI,QAA8B;AAE3D,SAAgB,cAAc,UAAsC;CACnE,iBAAiB,IAAI,QAAQ;AAC9B;AAEA,SAAgB,gBAAgB,UAAsC;CACrE,iBAAiB,OAAO,QAAQ;AACjC;AAEA,SAAgB,iBAAiB,UAAyC;CACzE,OAAO,iBAAiB,IAAI,QAAQ;AACrC;AAIA,MAAM,4BAAY,IAAI,QAAoD;AAE1E,SAAgB,eAAe,UAAgC,QAAoC;CAClG,UAAU,IAAI,UAAU,MAAM;AAC/B;;AAGA,SAAgB,eAAe,UAA6D;CAC3F,IAAI,iBAAiB,IAAI,QAAQ,GAAG,OAAO;CAE3C,MAAM,SAAS,UAAU,IAAI,QAAQ;CACrC,OAAO,UAAU,iBAAiB,IAAI,MAAM,IAAI,SAAS;AAC1D"}
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export{};
1
+ export {};
@@ -0,0 +1,129 @@
1
+ import { i as isCachedGeometry, t as borrowGeometry } from "./gpu-ownership-Bs9l7hrU.js";
2
+ import * as THREE from "three";
3
+ // src/shared/merged-draw.ts
4
+ const states = /* @__PURE__ */ new WeakMap();
5
+ const listeners = /* @__PURE__ */ new WeakMap();
6
+ const tintOrigins = /* @__PURE__ */ new WeakMap();
7
+ const NONE = /* @__PURE__ */ new Set();
8
+ function windowsOf(mesh) {
9
+ const members = mesh.userData?.members;
10
+ if (!Array.isArray(members) || members.length === 0) return null;
11
+ if (members.some((m) => m.indexStart == null || m.indexCount == null)) return null;
12
+ return members;
13
+ }
14
+ function stateOf(mesh) {
15
+ let state = states.get(mesh);
16
+ if (!state) {
17
+ state = {
18
+ base: Array.isArray(mesh.material) ? mesh.material[0] : mesh.material,
19
+ hidden: NONE,
20
+ selection: null,
21
+ hover: null
22
+ };
23
+ states.set(mesh, state);
24
+ }
25
+ return state;
26
+ }
27
+ function ownGeometry(mesh) {
28
+ const shared = mesh.geometry;
29
+ if (!isCachedGeometry(shared)) return shared;
30
+ const own = new THREE.BufferGeometry();
31
+ own.setIndex(shared.index);
32
+ for (const [name, attribute] of Object.entries(shared.attributes)) own.setAttribute(name, attribute);
33
+ own.boundingBox = shared.boundingBox?.clone() ?? null;
34
+ own.boundingSphere = shared.boundingSphere?.clone() ?? null;
35
+ borrowGeometry(own, shared);
36
+ mesh.geometry = own;
37
+ return own;
38
+ }
39
+ function rebuild(mesh, windows, state) {
40
+ const geometry = ownGeometry(mesh);
41
+ geometry.clearGroups();
42
+ const { selection, hover } = state;
43
+ if (state.hidden.size === 0 && !selection && !hover) {
44
+ mesh.material = state.base;
45
+ states.delete(mesh);
46
+ return;
47
+ }
48
+ const materials = [state.base];
49
+ const selectionSlot = selection ? materials.push(selection.material) - 1 : 0;
50
+ const hoverSlot = hover ? materials.push(hover.material) - 1 : 0;
51
+ let runStart = -1;
52
+ let runEnd = 0;
53
+ let runMaterial = 0;
54
+ const flush = () => {
55
+ if (runStart >= 0) geometry.addGroup(runStart, runEnd - runStart, runMaterial);
56
+ runStart = -1;
57
+ };
58
+ for (let i = 0; i < windows.length; i++) {
59
+ if (state.hidden.has(i)) {
60
+ flush();
61
+ continue;
62
+ }
63
+ const { indexStart, indexCount } = windows[i];
64
+ const material = selection?.members.has(i) ? selectionSlot : hover?.members.has(i) ? hoverSlot : 0;
65
+ if (runStart < 0 || material !== runMaterial || indexStart !== runEnd) {
66
+ flush();
67
+ runStart = indexStart;
68
+ runMaterial = material;
69
+ }
70
+ runEnd = indexStart + indexCount;
71
+ }
72
+ flush();
73
+ mesh.material = materials;
74
+ }
75
+ function update(mesh, change) {
76
+ const windows = windowsOf(mesh);
77
+ if (!windows) return;
78
+ const state = stateOf(mesh);
79
+ change(state);
80
+ rebuild(mesh, windows, state);
81
+ listeners.get(mesh)?.forEach((listener) => listener());
82
+ }
83
+ /** Members of `mesh` not drawn, by index into `userData.members`. */
84
+ function getHiddenMembers(mesh) {
85
+ return states.get(mesh)?.hidden ?? NONE;
86
+ }
87
+ function setHiddenMembers(mesh, hidden) {
88
+ const current = getHiddenMembers(mesh);
89
+ if (current.size === hidden.size && [...hidden].every((i) => current.has(i))) return;
90
+ update(mesh, (state) => {
91
+ state.hidden = new Set(hidden);
92
+ });
93
+ }
94
+ /** The material `mesh` draws with when nothing is highlighted. */
95
+ function getBaseMaterial(mesh) {
96
+ const material = states.get(mesh)?.base ?? (Array.isArray(mesh.material) ? mesh.material[0] : mesh.material);
97
+ return tintOrigins.get(material) ?? material;
98
+ }
99
+ /** A clone to tint and swap onto a mesh. `getBaseMaterial` keeps answering with `material`. */
100
+ function cloneForTint(material) {
101
+ const clone = material.clone();
102
+ tintOrigins.set(clone, tintOrigins.get(material) ?? material);
103
+ return clone;
104
+ }
105
+ /** Draws `members` with `material`. The caller owns `material` and disposes it. */
106
+ function setMemberHighlight(mesh, members, material, kind = "selection") {
107
+ update(mesh, (state) => {
108
+ state[kind] = {
109
+ members,
110
+ material
111
+ };
112
+ });
113
+ }
114
+ function clearMemberHighlight(mesh, kind = "selection") {
115
+ if (!states.get(mesh)?.[kind]) return;
116
+ update(mesh, (state) => {
117
+ state[kind] = null;
118
+ });
119
+ }
120
+ /** Calls `listener` after the hidden set or highlight of `mesh` changes. Returns an unsubscribe. */
121
+ function onMemberDrawChange(mesh, listener) {
122
+ let set = listeners.get(mesh);
123
+ if (!set) listeners.set(mesh, set = /* @__PURE__ */ new Set());
124
+ set.add(listener);
125
+ return () => set.delete(listener);
126
+ }
127
+ export { onMemberDrawChange as a, getHiddenMembers as i, cloneForTint as n, setHiddenMembers as o, getBaseMaterial as r, setMemberHighlight as s, clearMemberHighlight as t };
128
+
129
+ //# sourceMappingURL=merged-draw-CO4rr2Co.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merged-draw-CO4rr2Co.js","names":[],"sources":["../src/shared/merged-draw.ts"],"sourcesContent":["// ============================================================================\n// Which parts of a merged mesh draw, and with what material\n// ============================================================================\n//\n// A merged mesh draws many source members in one call. Two features draw only part of it: hiding\n// a member (`scene/`) and tinting a selected or hovered member (`render/`). Both need geometry groups, and\n// three honours groups only when `material` is an array: with a single material it draws the whole\n// index range and ignores them. So one owner builds the groups and the material array from both\n// states together. Two writers each replacing the groups would undo each other.\n//\n// Members are addressed by their index in `userData.members`, which carries each member's window\n// into the index buffer (`finalizeMergedMesh`).\n\nimport * as THREE from 'three';\n\nimport { borrowGeometry, isCachedGeometry } from './gpu-ownership.js';\n\ninterface MemberWindow {\n\tindexStart?: number;\n\tindexCount?: number;\n}\n\nexport type HighlightKind = 'selection' | 'hover';\n\ninterface Highlight {\n\tmembers: ReadonlySet<number>;\n\tmaterial: THREE.Material;\n}\n\ninterface DrawState extends Record<HighlightKind, Highlight | null> {\n\t/** The mesh's own material, restored once nothing is hidden or highlighted. */\n\tbase: THREE.Material;\n\thidden: ReadonlySet<number>;\n}\n\nconst states = new WeakMap<THREE.Mesh, DrawState>();\nconst listeners = new WeakMap<THREE.Mesh, Set<() => void>>();\n\nconst tintOrigins = new WeakMap<THREE.Material, THREE.Material>();\n\nconst NONE: ReadonlySet<number> = new Set();\n\nfunction windowsOf(mesh: THREE.Mesh): Required<MemberWindow>[] | null {\n\tconst members = mesh.userData?.members as MemberWindow[] | undefined;\n\tif (!Array.isArray(members) || members.length === 0) return null;\n\t// One member without a window means the ranges can't be rebuilt without dropping geometry.\n\tif (members.some((m) => m.indexStart == null || m.indexCount == null)) return null;\n\treturn members as Required<MemberWindow>[];\n}\n\nfunction stateOf(mesh: THREE.Mesh): DrawState {\n\tlet state = states.get(mesh);\n\tif (!state) {\n\t\tconst base = Array.isArray(mesh.material) ? mesh.material[0]! : mesh.material;\n\t\tstate = { base, hidden: NONE, selection: null, hover: null };\n\t\tstates.set(mesh, state);\n\t}\n\treturn state;\n}\n\n// Groups live on the geometry, and a cached geometry can back two meshes at once (two identical\n// branches), so a mesh gets its own geometry over the same attributes before writing any.\nfunction ownGeometry(mesh: THREE.Mesh): THREE.BufferGeometry {\n\tconst shared = mesh.geometry;\n\tif (!isCachedGeometry(shared)) return shared;\n\n\tconst own = new THREE.BufferGeometry();\n\town.setIndex(shared.index);\n\tfor (const [name, attribute] of Object.entries(shared.attributes))\n\t\town.setAttribute(name, attribute);\n\town.boundingBox = shared.boundingBox?.clone() ?? null;\n\town.boundingSphere = shared.boundingSphere?.clone() ?? null;\n\tborrowGeometry(own, shared);\n\tmesh.geometry = own;\n\treturn own;\n}\n\nfunction rebuild(mesh: THREE.Mesh, windows: Required<MemberWindow>[], state: DrawState): void {\n\tconst geometry = ownGeometry(mesh);\n\tgeometry.clearGroups();\n\n\tconst { selection, hover } = state;\n\tif (state.hidden.size === 0 && !selection && !hover) {\n\t\tmesh.material = state.base;\n\t\tstates.delete(mesh);\n\t\treturn;\n\t}\n\n\tconst materials = [state.base];\n\tconst selectionSlot = selection ? materials.push(selection.material) - 1 : 0;\n\tconst hoverSlot = hover ? materials.push(hover.material) - 1 : 0;\n\n\t// Coalesce neighbours with the same material, so hiding one member costs two draws, not N.\n\tlet runStart = -1;\n\tlet runEnd = 0;\n\tlet runMaterial = 0;\n\tconst flush = () => {\n\t\tif (runStart >= 0) geometry.addGroup(runStart, runEnd - runStart, runMaterial);\n\t\trunStart = -1;\n\t};\n\tfor (let i = 0; i < windows.length; i++) {\n\t\tif (state.hidden.has(i)) {\n\t\t\tflush();\n\t\t\tcontinue;\n\t\t}\n\t\tconst { indexStart, indexCount } = windows[i]!;\n\t\t// Selection wins, so pointing at a selected member doesn't make it look unselected.\n\t\tconst material = selection?.members.has(i)\n\t\t\t? selectionSlot\n\t\t\t: hover?.members.has(i)\n\t\t\t\t? hoverSlot\n\t\t\t\t: 0;\n\t\tif (runStart < 0 || material !== runMaterial || indexStart !== runEnd) {\n\t\t\tflush();\n\t\t\trunStart = indexStart;\n\t\t\trunMaterial = material;\n\t\t}\n\t\trunEnd = indexStart + indexCount;\n\t}\n\tflush();\n\n\tmesh.material = materials;\n}\n\nfunction update(mesh: THREE.Mesh, change: (state: DrawState) => void): void {\n\tconst windows = windowsOf(mesh);\n\tif (!windows) return;\n\tconst state = stateOf(mesh);\n\tchange(state);\n\trebuild(mesh, windows, state);\n\tlisteners.get(mesh)?.forEach((listener) => listener());\n}\n\n/** Members of `mesh` not drawn, by index into `userData.members`. */\nexport function getHiddenMembers(mesh: THREE.Mesh): ReadonlySet<number> {\n\treturn states.get(mesh)?.hidden ?? NONE;\n}\n\nexport function setHiddenMembers(mesh: THREE.Mesh, hidden: ReadonlySet<number>): void {\n\tconst current = getHiddenMembers(mesh);\n\tif (current.size === hidden.size && [...hidden].every((i) => current.has(i))) return;\n\tupdate(mesh, (state) => {\n\t\tstate.hidden = new Set(hidden);\n\t});\n}\n\n/** The material `mesh` draws with when nothing is highlighted. */\nexport function getBaseMaterial(mesh: THREE.Mesh): THREE.Material {\n\tconst material =\n\t\tstates.get(mesh)?.base ?? (Array.isArray(mesh.material) ? mesh.material[0]! : mesh.material);\n\treturn tintOrigins.get(material) ?? material;\n}\n\n/** A clone to tint and swap onto a mesh. `getBaseMaterial` keeps answering with `material`. */\nexport function cloneForTint(material: THREE.Material): THREE.Material {\n\tconst clone = material.clone();\n\ttintOrigins.set(clone, tintOrigins.get(material) ?? material);\n\treturn clone;\n}\n\n/** Draws `members` with `material`. The caller owns `material` and disposes it. */\nexport function setMemberHighlight(\n\tmesh: THREE.Mesh,\n\tmembers: ReadonlySet<number>,\n\tmaterial: THREE.Material,\n\tkind: HighlightKind = 'selection'\n): void {\n\tupdate(mesh, (state) => {\n\t\tstate[kind] = { members, material };\n\t});\n}\n\nexport function clearMemberHighlight(mesh: THREE.Mesh, kind: HighlightKind = 'selection'): void {\n\tif (!states.get(mesh)?.[kind]) return;\n\tupdate(mesh, (state) => {\n\t\tstate[kind] = null;\n\t});\n}\n\n/** Calls `listener` after the hidden set or highlight of `mesh` changes. Returns an unsubscribe. */\nexport function onMemberDrawChange(mesh: THREE.Mesh, listener: () => void): () => void {\n\tlet set = listeners.get(mesh);\n\tif (!set) listeners.set(mesh, (set = new Set()));\n\tset.add(listener);\n\treturn () => set.delete(listener);\n}\n"],"mappings":"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"}
@@ -0,0 +1,171 @@
1
+ const require_gpu_ownership = require("./gpu-ownership-ADRZBxxs.cjs");
2
+ let three = require("three");
3
+ three = require_gpu_ownership.__toESM(three, 1);
4
+ // src/shared/merged-draw.ts
5
+ const states = /* @__PURE__ */ new WeakMap();
6
+ const listeners = /* @__PURE__ */ new WeakMap();
7
+ const tintOrigins = /* @__PURE__ */ new WeakMap();
8
+ const NONE = /* @__PURE__ */ new Set();
9
+ function windowsOf(mesh) {
10
+ const members = mesh.userData?.members;
11
+ if (!Array.isArray(members) || members.length === 0) return null;
12
+ if (members.some((m) => m.indexStart == null || m.indexCount == null)) return null;
13
+ return members;
14
+ }
15
+ function stateOf(mesh) {
16
+ let state = states.get(mesh);
17
+ if (!state) {
18
+ state = {
19
+ base: Array.isArray(mesh.material) ? mesh.material[0] : mesh.material,
20
+ hidden: NONE,
21
+ selection: null,
22
+ hover: null
23
+ };
24
+ states.set(mesh, state);
25
+ }
26
+ return state;
27
+ }
28
+ function ownGeometry(mesh) {
29
+ const shared = mesh.geometry;
30
+ if (!require_gpu_ownership.isCachedGeometry(shared)) return shared;
31
+ const own = new three.BufferGeometry();
32
+ own.setIndex(shared.index);
33
+ for (const [name, attribute] of Object.entries(shared.attributes)) own.setAttribute(name, attribute);
34
+ own.boundingBox = shared.boundingBox?.clone() ?? null;
35
+ own.boundingSphere = shared.boundingSphere?.clone() ?? null;
36
+ require_gpu_ownership.borrowGeometry(own, shared);
37
+ mesh.geometry = own;
38
+ return own;
39
+ }
40
+ function rebuild(mesh, windows, state) {
41
+ const geometry = ownGeometry(mesh);
42
+ geometry.clearGroups();
43
+ const { selection, hover } = state;
44
+ if (state.hidden.size === 0 && !selection && !hover) {
45
+ mesh.material = state.base;
46
+ states.delete(mesh);
47
+ return;
48
+ }
49
+ const materials = [state.base];
50
+ const selectionSlot = selection ? materials.push(selection.material) - 1 : 0;
51
+ const hoverSlot = hover ? materials.push(hover.material) - 1 : 0;
52
+ let runStart = -1;
53
+ let runEnd = 0;
54
+ let runMaterial = 0;
55
+ const flush = () => {
56
+ if (runStart >= 0) geometry.addGroup(runStart, runEnd - runStart, runMaterial);
57
+ runStart = -1;
58
+ };
59
+ for (let i = 0; i < windows.length; i++) {
60
+ if (state.hidden.has(i)) {
61
+ flush();
62
+ continue;
63
+ }
64
+ const { indexStart, indexCount } = windows[i];
65
+ const material = selection?.members.has(i) ? selectionSlot : hover?.members.has(i) ? hoverSlot : 0;
66
+ if (runStart < 0 || material !== runMaterial || indexStart !== runEnd) {
67
+ flush();
68
+ runStart = indexStart;
69
+ runMaterial = material;
70
+ }
71
+ runEnd = indexStart + indexCount;
72
+ }
73
+ flush();
74
+ mesh.material = materials;
75
+ }
76
+ function update(mesh, change) {
77
+ const windows = windowsOf(mesh);
78
+ if (!windows) return;
79
+ const state = stateOf(mesh);
80
+ change(state);
81
+ rebuild(mesh, windows, state);
82
+ listeners.get(mesh)?.forEach((listener) => listener());
83
+ }
84
+ /** Members of `mesh` not drawn, by index into `userData.members`. */
85
+ function getHiddenMembers(mesh) {
86
+ return states.get(mesh)?.hidden ?? NONE;
87
+ }
88
+ function setHiddenMembers(mesh, hidden) {
89
+ const current = getHiddenMembers(mesh);
90
+ if (current.size === hidden.size && [...hidden].every((i) => current.has(i))) return;
91
+ update(mesh, (state) => {
92
+ state.hidden = new Set(hidden);
93
+ });
94
+ }
95
+ /** The material `mesh` draws with when nothing is highlighted. */
96
+ function getBaseMaterial(mesh) {
97
+ const material = states.get(mesh)?.base ?? (Array.isArray(mesh.material) ? mesh.material[0] : mesh.material);
98
+ return tintOrigins.get(material) ?? material;
99
+ }
100
+ /** A clone to tint and swap onto a mesh. `getBaseMaterial` keeps answering with `material`. */
101
+ function cloneForTint(material) {
102
+ const clone = material.clone();
103
+ tintOrigins.set(clone, tintOrigins.get(material) ?? material);
104
+ return clone;
105
+ }
106
+ /** Draws `members` with `material`. The caller owns `material` and disposes it. */
107
+ function setMemberHighlight(mesh, members, material, kind = "selection") {
108
+ update(mesh, (state) => {
109
+ state[kind] = {
110
+ members,
111
+ material
112
+ };
113
+ });
114
+ }
115
+ function clearMemberHighlight(mesh, kind = "selection") {
116
+ if (!states.get(mesh)?.[kind]) return;
117
+ update(mesh, (state) => {
118
+ state[kind] = null;
119
+ });
120
+ }
121
+ /** Calls `listener` after the hidden set or highlight of `mesh` changes. Returns an unsubscribe. */
122
+ function onMemberDrawChange(mesh, listener) {
123
+ let set = listeners.get(mesh);
124
+ if (!set) listeners.set(mesh, set = /* @__PURE__ */ new Set());
125
+ set.add(listener);
126
+ return () => set.delete(listener);
127
+ }
128
+ Object.defineProperty(exports, "clearMemberHighlight", {
129
+ enumerable: true,
130
+ get: function() {
131
+ return clearMemberHighlight;
132
+ }
133
+ });
134
+ Object.defineProperty(exports, "cloneForTint", {
135
+ enumerable: true,
136
+ get: function() {
137
+ return cloneForTint;
138
+ }
139
+ });
140
+ Object.defineProperty(exports, "getBaseMaterial", {
141
+ enumerable: true,
142
+ get: function() {
143
+ return getBaseMaterial;
144
+ }
145
+ });
146
+ Object.defineProperty(exports, "getHiddenMembers", {
147
+ enumerable: true,
148
+ get: function() {
149
+ return getHiddenMembers;
150
+ }
151
+ });
152
+ Object.defineProperty(exports, "onMemberDrawChange", {
153
+ enumerable: true,
154
+ get: function() {
155
+ return onMemberDrawChange;
156
+ }
157
+ });
158
+ Object.defineProperty(exports, "setHiddenMembers", {
159
+ enumerable: true,
160
+ get: function() {
161
+ return setHiddenMembers;
162
+ }
163
+ });
164
+ Object.defineProperty(exports, "setMemberHighlight", {
165
+ enumerable: true,
166
+ get: function() {
167
+ return setMemberHighlight;
168
+ }
169
+ });
170
+
171
+ //# sourceMappingURL=merged-draw-ma_2r4N8.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merged-draw-ma_2r4N8.cjs","names":["isCachedGeometry","THREE"],"sources":["../src/shared/merged-draw.ts"],"sourcesContent":["// ============================================================================\n// Which parts of a merged mesh draw, and with what material\n// ============================================================================\n//\n// A merged mesh draws many source members in one call. Two features draw only part of it: hiding\n// a member (`scene/`) and tinting a selected or hovered member (`render/`). Both need geometry groups, and\n// three honours groups only when `material` is an array: with a single material it draws the whole\n// index range and ignores them. So one owner builds the groups and the material array from both\n// states together. Two writers each replacing the groups would undo each other.\n//\n// Members are addressed by their index in `userData.members`, which carries each member's window\n// into the index buffer (`finalizeMergedMesh`).\n\nimport * as THREE from 'three';\n\nimport { borrowGeometry, isCachedGeometry } from './gpu-ownership.js';\n\ninterface MemberWindow {\n\tindexStart?: number;\n\tindexCount?: number;\n}\n\nexport type HighlightKind = 'selection' | 'hover';\n\ninterface Highlight {\n\tmembers: ReadonlySet<number>;\n\tmaterial: THREE.Material;\n}\n\ninterface DrawState extends Record<HighlightKind, Highlight | null> {\n\t/** The mesh's own material, restored once nothing is hidden or highlighted. */\n\tbase: THREE.Material;\n\thidden: ReadonlySet<number>;\n}\n\nconst states = new WeakMap<THREE.Mesh, DrawState>();\nconst listeners = new WeakMap<THREE.Mesh, Set<() => void>>();\n\nconst tintOrigins = new WeakMap<THREE.Material, THREE.Material>();\n\nconst NONE: ReadonlySet<number> = new Set();\n\nfunction windowsOf(mesh: THREE.Mesh): Required<MemberWindow>[] | null {\n\tconst members = mesh.userData?.members as MemberWindow[] | undefined;\n\tif (!Array.isArray(members) || members.length === 0) return null;\n\t// One member without a window means the ranges can't be rebuilt without dropping geometry.\n\tif (members.some((m) => m.indexStart == null || m.indexCount == null)) return null;\n\treturn members as Required<MemberWindow>[];\n}\n\nfunction stateOf(mesh: THREE.Mesh): DrawState {\n\tlet state = states.get(mesh);\n\tif (!state) {\n\t\tconst base = Array.isArray(mesh.material) ? mesh.material[0]! : mesh.material;\n\t\tstate = { base, hidden: NONE, selection: null, hover: null };\n\t\tstates.set(mesh, state);\n\t}\n\treturn state;\n}\n\n// Groups live on the geometry, and a cached geometry can back two meshes at once (two identical\n// branches), so a mesh gets its own geometry over the same attributes before writing any.\nfunction ownGeometry(mesh: THREE.Mesh): THREE.BufferGeometry {\n\tconst shared = mesh.geometry;\n\tif (!isCachedGeometry(shared)) return shared;\n\n\tconst own = new THREE.BufferGeometry();\n\town.setIndex(shared.index);\n\tfor (const [name, attribute] of Object.entries(shared.attributes))\n\t\town.setAttribute(name, attribute);\n\town.boundingBox = shared.boundingBox?.clone() ?? null;\n\town.boundingSphere = shared.boundingSphere?.clone() ?? null;\n\tborrowGeometry(own, shared);\n\tmesh.geometry = own;\n\treturn own;\n}\n\nfunction rebuild(mesh: THREE.Mesh, windows: Required<MemberWindow>[], state: DrawState): void {\n\tconst geometry = ownGeometry(mesh);\n\tgeometry.clearGroups();\n\n\tconst { selection, hover } = state;\n\tif (state.hidden.size === 0 && !selection && !hover) {\n\t\tmesh.material = state.base;\n\t\tstates.delete(mesh);\n\t\treturn;\n\t}\n\n\tconst materials = [state.base];\n\tconst selectionSlot = selection ? materials.push(selection.material) - 1 : 0;\n\tconst hoverSlot = hover ? materials.push(hover.material) - 1 : 0;\n\n\t// Coalesce neighbours with the same material, so hiding one member costs two draws, not N.\n\tlet runStart = -1;\n\tlet runEnd = 0;\n\tlet runMaterial = 0;\n\tconst flush = () => {\n\t\tif (runStart >= 0) geometry.addGroup(runStart, runEnd - runStart, runMaterial);\n\t\trunStart = -1;\n\t};\n\tfor (let i = 0; i < windows.length; i++) {\n\t\tif (state.hidden.has(i)) {\n\t\t\tflush();\n\t\t\tcontinue;\n\t\t}\n\t\tconst { indexStart, indexCount } = windows[i]!;\n\t\t// Selection wins, so pointing at a selected member doesn't make it look unselected.\n\t\tconst material = selection?.members.has(i)\n\t\t\t? selectionSlot\n\t\t\t: hover?.members.has(i)\n\t\t\t\t? hoverSlot\n\t\t\t\t: 0;\n\t\tif (runStart < 0 || material !== runMaterial || indexStart !== runEnd) {\n\t\t\tflush();\n\t\t\trunStart = indexStart;\n\t\t\trunMaterial = material;\n\t\t}\n\t\trunEnd = indexStart + indexCount;\n\t}\n\tflush();\n\n\tmesh.material = materials;\n}\n\nfunction update(mesh: THREE.Mesh, change: (state: DrawState) => void): void {\n\tconst windows = windowsOf(mesh);\n\tif (!windows) return;\n\tconst state = stateOf(mesh);\n\tchange(state);\n\trebuild(mesh, windows, state);\n\tlisteners.get(mesh)?.forEach((listener) => listener());\n}\n\n/** Members of `mesh` not drawn, by index into `userData.members`. */\nexport function getHiddenMembers(mesh: THREE.Mesh): ReadonlySet<number> {\n\treturn states.get(mesh)?.hidden ?? NONE;\n}\n\nexport function setHiddenMembers(mesh: THREE.Mesh, hidden: ReadonlySet<number>): void {\n\tconst current = getHiddenMembers(mesh);\n\tif (current.size === hidden.size && [...hidden].every((i) => current.has(i))) return;\n\tupdate(mesh, (state) => {\n\t\tstate.hidden = new Set(hidden);\n\t});\n}\n\n/** The material `mesh` draws with when nothing is highlighted. */\nexport function getBaseMaterial(mesh: THREE.Mesh): THREE.Material {\n\tconst material =\n\t\tstates.get(mesh)?.base ?? (Array.isArray(mesh.material) ? mesh.material[0]! : mesh.material);\n\treturn tintOrigins.get(material) ?? material;\n}\n\n/** A clone to tint and swap onto a mesh. `getBaseMaterial` keeps answering with `material`. */\nexport function cloneForTint(material: THREE.Material): THREE.Material {\n\tconst clone = material.clone();\n\ttintOrigins.set(clone, tintOrigins.get(material) ?? material);\n\treturn clone;\n}\n\n/** Draws `members` with `material`. The caller owns `material` and disposes it. */\nexport function setMemberHighlight(\n\tmesh: THREE.Mesh,\n\tmembers: ReadonlySet<number>,\n\tmaterial: THREE.Material,\n\tkind: HighlightKind = 'selection'\n): void {\n\tupdate(mesh, (state) => {\n\t\tstate[kind] = { members, material };\n\t});\n}\n\nexport function clearMemberHighlight(mesh: THREE.Mesh, kind: HighlightKind = 'selection'): void {\n\tif (!states.get(mesh)?.[kind]) return;\n\tupdate(mesh, (state) => {\n\t\tstate[kind] = null;\n\t});\n}\n\n/** Calls `listener` after the hidden set or highlight of `mesh` changes. Returns an unsubscribe. */\nexport function onMemberDrawChange(mesh: THREE.Mesh, listener: () => void): () => void {\n\tlet set = listeners.get(mesh);\n\tif (!set) listeners.set(mesh, (set = new Set()));\n\tset.add(listener);\n\treturn () => set.delete(listener);\n}\n"],"mappings":"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"}