@nice2dev/ui-3d 1.3.14 → 1.3.16

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 (108) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cjs/collaborative/collaborativeScene.js +160 -206
  3. package/dist/cjs/core/editorAsk.js +51 -41
  4. package/dist/cjs/core/i18n.js +13 -14
  5. package/dist/cjs/core/threeCompat.js +29 -42
  6. package/dist/cjs/dance/DanceBridge.js +148 -177
  7. package/dist/cjs/dance/DanceScoreEngine.js +209 -221
  8. package/dist/cjs/dance/PoseDetector.js +302 -275
  9. package/dist/cjs/index.js +110 -113
  10. package/dist/cjs/material/MaterialEditor.module.css.js +242 -5
  11. package/dist/cjs/material/NiceMaterialEditor.js +1213 -757
  12. package/dist/cjs/material/materialEditorTypes.js +35 -49
  13. package/dist/cjs/material/materialEditorUtils.js +684 -747
  14. package/dist/cjs/material/materialNodeDefinitions.js +1411 -1459
  15. package/dist/cjs/model/ModelEditor.js +90 -40
  16. package/dist/cjs/model/ModelEditor.module.css.js +257 -5
  17. package/dist/cjs/model/ModelEditorLeftPanel.js +126 -47
  18. package/dist/cjs/model/ModelEditorMenuBar.js +359 -32
  19. package/dist/cjs/model/ModelEditorRightPanel.js +647 -131
  20. package/dist/cjs/model/ModelEditorSubComponents.js +277 -112
  21. package/dist/cjs/model/ModelEditorTimeline.js +232 -34
  22. package/dist/cjs/model/ModelEditorToolbar.js +170 -14
  23. package/dist/cjs/model/ModelEditorViewport.js +231 -39
  24. package/dist/cjs/model/ModelViewer.js +388 -63
  25. package/dist/cjs/model/ModelViewer.module.css.js +77 -5
  26. package/dist/cjs/model/NiceArmatureEditor.js +473 -274
  27. package/dist/cjs/model/NiceMorphTargetEditor.js +333 -227
  28. package/dist/cjs/model/NiceOctree.js +372 -320
  29. package/dist/cjs/model/NicePhysicsSimulation.js +528 -298
  30. package/dist/cjs/model/NiceProceduralGeometry.js +673 -300
  31. package/dist/cjs/model/NiceTerrainEditor.js +595 -385
  32. package/dist/cjs/model/NiceWeightPainter.js +384 -261
  33. package/dist/cjs/model/NiceXRPreview.js +385 -308
  34. package/dist/cjs/model/cadModeUtils.js +103 -106
  35. package/dist/cjs/model/editorShortcuts.js +171 -194
  36. package/dist/cjs/model/modelEditorTypes.js +100 -118
  37. package/dist/cjs/model/modelEditorUtils.js +1061 -1146
  38. package/dist/cjs/model/simsModeUtils.js +1023 -1053
  39. package/dist/cjs/model/useModelEditor.js +1977 -1933
  40. package/dist/cjs/model/useModelViewer.js +633 -665
  41. package/dist/cjs/nice2dev-ui-3d.css +1 -1
  42. package/dist/cjs/particle/NiceParticleEditor.js +1186 -571
  43. package/dist/cjs/particle/ParticleEditor.module.css.js +122 -5
  44. package/dist/cjs/particle/particleEditorTypes.js +62 -70
  45. package/dist/cjs/particle/particleEditorUtils.js +998 -1013
  46. package/dist/cjs/rendering/NiceCascadedShadows.js +381 -259
  47. package/dist/cjs/rendering/NiceRenderExport.js +424 -337
  48. package/dist/cjs/rendering/NiceSSAO.js +454 -337
  49. package/dist/cjs/rendering/NiceSSR.js +387 -261
  50. package/dist/cjs/rendering/NiceWebGPURenderer.js +359 -230
  51. package/dist/cjs/uv/NiceUVEditor.js +787 -550
  52. package/dist/cjs/uv/UVEditor.module.css.js +53 -5
  53. package/dist/cjs/uv/uvEditorTypes.js +59 -76
  54. package/dist/cjs/uv/uvEditorUtils.js +501 -575
  55. package/dist/esm/collaborative/collaborativeScene.js +163 -205
  56. package/dist/esm/core/editorAsk.js +53 -40
  57. package/dist/esm/core/i18n.js +15 -13
  58. package/dist/esm/core/threeCompat.js +18 -26
  59. package/dist/esm/dance/DanceBridge.js +151 -176
  60. package/dist/esm/dance/DanceScoreEngine.js +211 -220
  61. package/dist/esm/dance/PoseDetector.js +304 -274
  62. package/dist/esm/index.js +258 -36
  63. package/dist/esm/material/MaterialEditor.module.css.js +242 -3
  64. package/dist/esm/material/NiceMaterialEditor.js +1215 -756
  65. package/dist/esm/material/materialEditorTypes.js +45 -48
  66. package/dist/esm/material/materialEditorUtils.js +713 -746
  67. package/dist/esm/material/materialNodeDefinitions.js +1415 -1458
  68. package/dist/esm/model/ModelEditor.js +91 -39
  69. package/dist/esm/model/ModelEditor.module.css.js +257 -3
  70. package/dist/esm/model/ModelEditorLeftPanel.js +126 -45
  71. package/dist/esm/model/ModelEditorMenuBar.js +359 -30
  72. package/dist/esm/model/ModelEditorRightPanel.js +634 -114
  73. package/dist/esm/model/ModelEditorSubComponents.js +265 -94
  74. package/dist/esm/model/ModelEditorTimeline.js +232 -32
  75. package/dist/esm/model/ModelEditorToolbar.js +170 -12
  76. package/dist/esm/model/ModelEditorViewport.js +231 -37
  77. package/dist/esm/model/ModelViewer.js +389 -62
  78. package/dist/esm/model/ModelViewer.module.css.js +77 -3
  79. package/dist/esm/model/NiceArmatureEditor.js +462 -258
  80. package/dist/esm/model/NiceMorphTargetEditor.js +322 -211
  81. package/dist/esm/model/NiceOctree.js +361 -304
  82. package/dist/esm/model/NicePhysicsSimulation.js +517 -282
  83. package/dist/esm/model/NiceProceduralGeometry.js +667 -284
  84. package/dist/esm/model/NiceTerrainEditor.js +584 -369
  85. package/dist/esm/model/NiceWeightPainter.js +373 -245
  86. package/dist/esm/model/NiceXRPreview.js +374 -292
  87. package/dist/esm/model/cadModeUtils.js +95 -88
  88. package/dist/esm/model/editorShortcuts.js +172 -193
  89. package/dist/esm/model/modelEditorTypes.js +90 -100
  90. package/dist/esm/model/modelEditorUtils.js +1056 -1128
  91. package/dist/esm/model/simsModeUtils.js +1021 -1035
  92. package/dist/esm/model/useModelEditor.js +1946 -1920
  93. package/dist/esm/model/useModelViewer.js +619 -647
  94. package/dist/esm/nice2dev-ui-3d.css +1 -1
  95. package/dist/esm/particle/NiceParticleEditor.js +1188 -570
  96. package/dist/esm/particle/ParticleEditor.module.css.js +122 -3
  97. package/dist/esm/particle/particleEditorTypes.js +69 -69
  98. package/dist/esm/particle/particleEditorUtils.js +1027 -1012
  99. package/dist/esm/rendering/NiceCascadedShadows.js +370 -243
  100. package/dist/esm/rendering/NiceRenderExport.js +413 -321
  101. package/dist/esm/rendering/NiceSSAO.js +450 -328
  102. package/dist/esm/rendering/NiceSSR.js +383 -252
  103. package/dist/esm/rendering/NiceWebGPURenderer.js +326 -214
  104. package/dist/esm/uv/NiceUVEditor.js +788 -549
  105. package/dist/esm/uv/UVEditor.module.css.js +53 -3
  106. package/dist/esm/uv/uvEditorTypes.js +68 -75
  107. package/dist/esm/uv/uvEditorUtils.js +515 -557
  108. package/package.json +7 -12
@@ -1,370 +1,422 @@
1
- 'use strict';
2
-
3
- var jsxRuntime = require('react/jsx-runtime');
4
- var ui = require('@nice2dev/ui');
5
- var React = require('react');
6
- var THREE = require('three');
7
- var i18n = require('../core/i18n.js');
8
-
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const ui = require("@nice2dev/ui");
5
+ const React = require("react");
6
+ const THREE = require("three");
7
+ const i18n = require("../core/i18n.js");
9
8
  function _interopNamespaceDefault(e) {
10
- var n = Object.create(null);
11
- if (e) {
12
- Object.keys(e).forEach(function (k) {
13
- if (k !== 'default') {
14
- var d = Object.getOwnPropertyDescriptor(e, k);
15
- Object.defineProperty(n, k, d.get ? d : {
16
- enumerable: true,
17
- get: function () { return e[k]; }
18
- });
19
- }
9
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
10
+ if (e) {
11
+ for (const k in e) {
12
+ if (k !== "default") {
13
+ const d = Object.getOwnPropertyDescriptor(e, k);
14
+ Object.defineProperty(n, k, d.get ? d : {
15
+ enumerable: true,
16
+ get: () => e[k]
20
17
  });
18
+ }
21
19
  }
22
- n.default = e;
23
- return Object.freeze(n);
20
+ }
21
+ n.default = e;
22
+ return Object.freeze(n);
24
23
  }
25
-
26
- var THREE__namespace = /*#__PURE__*/_interopNamespaceDefault(THREE);
27
-
28
- /* ── Octree implementation ── */
24
+ const THREE__namespace = /* @__PURE__ */ _interopNamespaceDefault(THREE);
29
25
  const DEFAULT_CONFIG = {
30
- maxDepth: 8,
31
- maxObjectsPerLeaf: 8,
32
- looseness: 1.0,
26
+ maxDepth: 8,
27
+ maxObjectsPerLeaf: 8,
28
+ looseness: 1
33
29
  };
34
30
  function createNode(bounds, depth) {
35
- return { bounds, children: null, objects: [], depth };
31
+ return { bounds, children: null, objects: [], depth };
36
32
  }
37
33
  function splitNode(node, config) {
38
- if (node.depth >= config.maxDepth) {
39
- return;
34
+ if (node.depth >= config.maxDepth) {
35
+ return;
36
+ }
37
+ const center = new THREE__namespace.Vector3();
38
+ node.bounds.getCenter(center);
39
+ const min = node.bounds.min;
40
+ const max = node.bounds.max;
41
+ node.children = [];
42
+ for (let i = 0; i < 8; i++) {
43
+ const childMin = new THREE__namespace.Vector3(
44
+ i & 1 ? center.x : min.x,
45
+ i & 2 ? center.y : min.y,
46
+ i & 4 ? center.z : min.z
47
+ );
48
+ const childMax = new THREE__namespace.Vector3(
49
+ i & 1 ? max.x : center.x,
50
+ i & 2 ? max.y : center.y,
51
+ i & 4 ? max.z : center.z
52
+ );
53
+ if (config.looseness > 1) {
54
+ const size = new THREE__namespace.Vector3().subVectors(childMax, childMin);
55
+ const expansion = size.clone().multiplyScalar((config.looseness - 1) * 0.5);
56
+ childMin.sub(expansion);
57
+ childMax.add(expansion);
40
58
  }
41
- const center = new THREE__namespace.Vector3();
42
- node.bounds.getCenter(center);
43
- const min = node.bounds.min;
44
- const max = node.bounds.max;
45
- node.children = [];
46
- for (let i = 0; i < 8; i++) {
47
- const childMin = new THREE__namespace.Vector3(i & 1 ? center.x : min.x, i & 2 ? center.y : min.y, i & 4 ? center.z : min.z);
48
- const childMax = new THREE__namespace.Vector3(i & 1 ? max.x : center.x, i & 2 ? max.y : center.y, i & 4 ? max.z : center.z);
49
- // Apply looseness
50
- if (config.looseness > 1) {
51
- const size = new THREE__namespace.Vector3().subVectors(childMax, childMin);
52
- const expansion = size.clone().multiplyScalar((config.looseness - 1) * 0.5);
53
- childMin.sub(expansion);
54
- childMax.add(expansion);
55
- }
56
- node.children.push(createNode(new THREE__namespace.Box3(childMin, childMax), node.depth + 1));
59
+ node.children.push(createNode(new THREE__namespace.Box3(childMin, childMax), node.depth + 1));
60
+ }
61
+ const orphans = [];
62
+ for (const obj of node.objects) {
63
+ const objBounds = new THREE__namespace.Box3().setFromObject(obj);
64
+ let placed = false;
65
+ for (const child of node.children) {
66
+ if (child.bounds.containsBox(objBounds)) {
67
+ child.objects.push(obj);
68
+ placed = true;
69
+ break;
70
+ }
57
71
  }
58
- // Redistribute objects to children
59
- const orphans = [];
60
- for (const obj of node.objects) {
61
- const objBounds = new THREE__namespace.Box3().setFromObject(obj);
62
- let placed = false;
63
- for (const child of node.children) {
64
- if (child.bounds.containsBox(objBounds)) {
65
- child.objects.push(obj);
66
- placed = true;
67
- break;
68
- }
69
- }
70
- if (!placed) {
71
- orphans.push(obj);
72
- }
72
+ if (!placed) {
73
+ orphans.push(obj);
73
74
  }
74
- node.objects = orphans;
75
+ }
76
+ node.objects = orphans;
75
77
  }
76
78
  function insertObject(node, object, config) {
77
- const objBounds = new THREE__namespace.Box3().setFromObject(object);
78
- if (!node.bounds.intersectsBox(objBounds)) {
79
- return;
80
- }
81
- if (node.children) {
82
- for (const child of node.children) {
83
- if (child.bounds.containsBox(objBounds)) {
84
- insertObject(child, object, config);
85
- return;
86
- }
87
- }
88
- // Doesn't fit in any child — store in this node
89
- node.objects.push(object);
79
+ const objBounds = new THREE__namespace.Box3().setFromObject(object);
80
+ if (!node.bounds.intersectsBox(objBounds)) {
81
+ return;
82
+ }
83
+ if (node.children) {
84
+ for (const child of node.children) {
85
+ if (child.bounds.containsBox(objBounds)) {
86
+ insertObject(child, object, config);
90
87
  return;
88
+ }
91
89
  }
92
90
  node.objects.push(object);
93
- if (node.objects.length > config.maxObjectsPerLeaf && node.depth < config.maxDepth) {
94
- splitNode(node, config);
95
- }
91
+ return;
92
+ }
93
+ node.objects.push(object);
94
+ if (node.objects.length > config.maxObjectsPerLeaf && node.depth < config.maxDepth) {
95
+ splitNode(node, config);
96
+ }
96
97
  }
97
98
  function removeObject(node, object) {
98
- const idx = node.objects.indexOf(object);
99
- if (idx !== -1) {
100
- node.objects.splice(idx, 1);
99
+ const idx = node.objects.indexOf(object);
100
+ if (idx !== -1) {
101
+ node.objects.splice(idx, 1);
102
+ return true;
103
+ }
104
+ if (node.children) {
105
+ for (const child of node.children) {
106
+ if (removeObject(child, object)) {
101
107
  return true;
108
+ }
102
109
  }
103
- if (node.children) {
104
- for (const child of node.children) {
105
- if (removeObject(child, object)) {
106
- return true;
107
- }
108
- }
109
- }
110
- return false;
110
+ }
111
+ return false;
111
112
  }
112
113
  function raycastNode(node, raycaster, results) {
113
- var _a;
114
- if (!raycaster.ray.intersectsBox(node.bounds)) {
115
- return;
116
- }
117
- // Test objects in this node
118
- for (const obj of node.objects) {
119
- if (obj instanceof THREE__namespace.Mesh) {
120
- const intersects = raycaster.intersectObject(obj, false);
121
- for (const hit of intersects) {
122
- results.push({
123
- object: obj,
124
- point: hit.point,
125
- distance: hit.distance,
126
- face: (_a = hit.face) !== null && _a !== void 0 ? _a : null,
127
- });
128
- }
129
- }
114
+ if (!raycaster.ray.intersectsBox(node.bounds)) {
115
+ return;
116
+ }
117
+ for (const obj of node.objects) {
118
+ if (obj instanceof THREE__namespace.Mesh) {
119
+ const intersects = raycaster.intersectObject(obj, false);
120
+ for (const hit of intersects) {
121
+ results.push({
122
+ object: obj,
123
+ point: hit.point,
124
+ distance: hit.distance,
125
+ face: hit.face ?? null
126
+ });
127
+ }
130
128
  }
131
- // Recurse into children
132
- if (node.children) {
133
- for (const child of node.children) {
134
- raycastNode(child, raycaster, results);
135
- }
129
+ }
130
+ if (node.children) {
131
+ for (const child of node.children) {
132
+ raycastNode(child, raycaster, results);
136
133
  }
134
+ }
137
135
  }
138
136
  function frustumCullNode(node, frustum, results) {
139
- if (!frustum.intersectsBox(node.bounds)) {
140
- return;
137
+ if (!frustum.intersectsBox(node.bounds)) {
138
+ return;
139
+ }
140
+ for (const obj of node.objects) {
141
+ const objBounds = new THREE__namespace.Box3().setFromObject(obj);
142
+ if (frustum.intersectsBox(objBounds)) {
143
+ results.push(obj);
141
144
  }
142
- for (const obj of node.objects) {
143
- const objBounds = new THREE__namespace.Box3().setFromObject(obj);
144
- if (frustum.intersectsBox(objBounds)) {
145
- results.push(obj);
146
- }
147
- }
148
- if (node.children) {
149
- for (const child of node.children) {
150
- frustumCullNode(child, frustum, results);
151
- }
145
+ }
146
+ if (node.children) {
147
+ for (const child of node.children) {
148
+ frustumCullNode(child, frustum, results);
152
149
  }
150
+ }
153
151
  }
154
152
  function nearestInNode(node, point, maxDist, best) {
155
- if (!node.bounds.distanceToPoint(point)) {
156
- return;
153
+ if (!node.bounds.distanceToPoint(point)) {
154
+ return;
155
+ }
156
+ if (node.bounds.distanceToPoint(point) > best.dist) {
157
+ return;
158
+ }
159
+ for (const obj of node.objects) {
160
+ const pos = new THREE__namespace.Vector3();
161
+ obj.getWorldPosition(pos);
162
+ const dist = pos.distanceTo(point);
163
+ if (dist < best.dist && dist <= maxDist) {
164
+ best.dist = dist;
165
+ best.object = obj;
157
166
  }
158
- if (node.bounds.distanceToPoint(point) > best.dist) {
159
- return;
160
- }
161
- for (const obj of node.objects) {
162
- const pos = new THREE__namespace.Vector3();
163
- obj.getWorldPosition(pos);
164
- const dist = pos.distanceTo(point);
165
- if (dist < best.dist && dist <= maxDist) {
166
- best.dist = dist;
167
- best.object = obj;
168
- }
169
- }
170
- if (node.children) {
171
- // Sort children by distance to point for early termination
172
- const sorted = [...node.children].sort((a, b) => {
173
- const da = a.bounds.distanceToPoint(point);
174
- const db = b.bounds.distanceToPoint(point);
175
- return da - db;
176
- });
177
- for (const child of sorted) {
178
- nearestInNode(child, point, maxDist, best);
179
- }
167
+ }
168
+ if (node.children) {
169
+ const sorted = [...node.children].sort((a, b) => {
170
+ const da = a.bounds.distanceToPoint(point);
171
+ const db = b.bounds.distanceToPoint(point);
172
+ return da - db;
173
+ });
174
+ for (const child of sorted) {
175
+ nearestInNode(child, point, maxDist, best);
180
176
  }
177
+ }
181
178
  }
182
179
  function countNodes(node) {
183
- let count = 1;
184
- if (node.children) {
185
- for (const child of node.children) {
186
- count += countNodes(child);
187
- }
180
+ let count = 1;
181
+ if (node.children) {
182
+ for (const child of node.children) {
183
+ count += countNodes(child);
188
184
  }
189
- return count;
185
+ }
186
+ return count;
190
187
  }
191
188
  function countObjects(node) {
192
- let count = node.objects.length;
193
- if (node.children) {
194
- for (const child of node.children) {
195
- count += countObjects(child);
196
- }
189
+ let count = node.objects.length;
190
+ if (node.children) {
191
+ for (const child of node.children) {
192
+ count += countObjects(child);
197
193
  }
198
- return count;
194
+ }
195
+ return count;
199
196
  }
200
197
  function buildDebugVisualization(node, group, maxDepth) {
201
- const size = new THREE__namespace.Vector3();
202
- node.bounds.getSize(size);
203
- const center = new THREE__namespace.Vector3();
204
- node.bounds.getCenter(center);
205
- const hue = node.depth / maxDepth;
206
- const color = new THREE__namespace.Color().setHSL(hue, 0.8, 0.5);
207
- const geo = new THREE__namespace.BoxGeometry(size.x, size.y, size.z);
208
- const wireframe = new THREE__namespace.LineSegments(new THREE__namespace.WireframeGeometry(geo), new THREE__namespace.LineBasicMaterial({ color, opacity: 0.3, transparent: true }));
209
- wireframe.position.copy(center);
210
- group.add(wireframe);
211
- if (node.children) {
212
- for (const child of node.children) {
213
- if (child.objects.length > 0 || child.children) {
214
- buildDebugVisualization(child, group, maxDepth);
215
- }
216
- }
198
+ const size = new THREE__namespace.Vector3();
199
+ node.bounds.getSize(size);
200
+ const center = new THREE__namespace.Vector3();
201
+ node.bounds.getCenter(center);
202
+ const hue = node.depth / maxDepth;
203
+ const color = new THREE__namespace.Color().setHSL(hue, 0.8, 0.5);
204
+ const geo = new THREE__namespace.BoxGeometry(size.x, size.y, size.z);
205
+ const wireframe = new THREE__namespace.LineSegments(
206
+ new THREE__namespace.WireframeGeometry(geo),
207
+ new THREE__namespace.LineBasicMaterial({ color, opacity: 0.3, transparent: true })
208
+ );
209
+ wireframe.position.copy(center);
210
+ group.add(wireframe);
211
+ if (node.children) {
212
+ for (const child of node.children) {
213
+ if (child.objects.length > 0 || child.children) {
214
+ buildDebugVisualization(child, group, maxDepth);
215
+ }
217
216
  }
217
+ }
218
218
  }
219
- /* ── Hook ── */
220
219
  function useOctree(scene, configOverride) {
221
- const config = React.useMemo(() => ({ ...DEFAULT_CONFIG, ...configOverride }), [configOverride]);
222
- const [root, setRoot] = React.useState(null);
223
- const [showDebug, setShowDebug] = React.useState(false);
224
- const [stats, setStats] = React.useState({ nodeCount: 0, objectCount: 0 });
225
- const debugGroup = React.useRef(new THREE__namespace.Group());
226
- React.useEffect(() => {
227
- if (!scene) {
228
- return;
229
- }
230
- const group = debugGroup.current;
231
- group.name = '__octree_debug__';
232
- if (showDebug) {
233
- scene.add(group);
234
- }
235
- return () => {
236
- scene.remove(group);
237
- };
238
- }, [scene, showDebug]);
239
- const refreshDebug = React.useCallback((rootNode) => {
240
- // Clear existing debug
241
- while (debugGroup.current.children.length) {
242
- const child = debugGroup.current.children[0];
243
- debugGroup.current.remove(child);
244
- }
245
- if (showDebug) {
246
- buildDebugVisualization(rootNode, debugGroup.current, config.maxDepth);
247
- }
248
- }, [showDebug, config.maxDepth]);
249
- const build = React.useCallback(() => {
250
- if (!scene) {
251
- return;
252
- }
253
- // Compute scene bounds
254
- const sceneBounds = new THREE__namespace.Box3();
255
- scene.traverse((child) => {
256
- if (child instanceof THREE__namespace.Mesh && !child.name.startsWith('__')) {
257
- sceneBounds.expandByObject(child);
258
- }
259
- });
260
- if (sceneBounds.isEmpty()) {
261
- return;
262
- }
263
- // Make bounds cubic
264
- const size = new THREE__namespace.Vector3();
265
- sceneBounds.getSize(size);
266
- const maxDim = Math.max(size.x, size.y, size.z) * 1.1;
267
- const center = new THREE__namespace.Vector3();
268
- sceneBounds.getCenter(center);
269
- const halfSize = maxDim / 2;
270
- const cubicBounds = new THREE__namespace.Box3(new THREE__namespace.Vector3(center.x - halfSize, center.y - halfSize, center.z - halfSize), new THREE__namespace.Vector3(center.x + halfSize, center.y + halfSize, center.z + halfSize));
271
- const rootNode = createNode(cubicBounds, 0);
272
- scene.traverse((child) => {
273
- if (child instanceof THREE__namespace.Mesh && !child.name.startsWith('__')) {
274
- insertObject(rootNode, child, config);
275
- }
276
- });
277
- setRoot(rootNode);
278
- setStats({ nodeCount: countNodes(rootNode), objectCount: countObjects(rootNode) });
279
- refreshDebug(rootNode);
280
- }, [scene, config, refreshDebug]);
281
- const insert = React.useCallback((object) => {
282
- if (!root) {
283
- return;
284
- }
285
- insertObject(root, object, config);
286
- setStats({ nodeCount: countNodes(root), objectCount: countObjects(root) });
287
- refreshDebug(root);
288
- }, [root, config, refreshDebug]);
289
- const remove = React.useCallback((object) => {
290
- if (!root) {
291
- return;
292
- }
293
- removeObject(root, object);
294
- setStats({ nodeCount: countNodes(root), objectCount: countObjects(root) });
295
- }, [root]);
296
- const update = React.useCallback((object) => {
297
- if (!root) {
298
- return;
299
- }
300
- removeObject(root, object);
301
- insertObject(root, object, config);
302
- }, [root, config]);
303
- const raycast = React.useCallback((raycaster) => {
304
- if (!root) {
305
- return [];
306
- }
307
- const results = [];
308
- raycastNode(root, raycaster, results);
309
- results.sort((a, b) => a.distance - b.distance);
310
- return results;
311
- }, [root]);
312
- const frustumCull = React.useCallback((camera) => {
313
- if (!root) {
314
- return [];
315
- }
316
- const frustum = new THREE__namespace.Frustum();
317
- const projScreenMatrix = new THREE__namespace.Matrix4();
318
- projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
319
- frustum.setFromProjectionMatrix(projScreenMatrix);
320
- const results = [];
321
- frustumCullNode(root, frustum, results);
322
- return results;
323
- }, [root]);
324
- const nearestNeighbor = React.useCallback((point, maxDistance = Infinity) => {
325
- if (!root) {
326
- return null;
327
- }
328
- const best = { object: null, dist: maxDistance };
329
- nearestInNode(root, point, maxDistance, best);
330
- return best.object;
331
- }, [root]);
332
- return {
333
- build,
334
- insert,
335
- remove,
336
- update,
337
- raycast,
338
- frustumCull,
339
- nearestNeighbor,
340
- nodeCount: stats.nodeCount,
341
- objectCount: stats.objectCount,
342
- showDebug,
343
- setShowDebug,
344
- root,
220
+ const config = React.useMemo(() => ({ ...DEFAULT_CONFIG, ...configOverride }), [configOverride]);
221
+ const [root, setRoot] = React.useState(null);
222
+ const [showDebug, setShowDebug] = React.useState(false);
223
+ const [stats, setStats] = React.useState({ nodeCount: 0, objectCount: 0 });
224
+ const debugGroup = React.useRef(new THREE__namespace.Group());
225
+ React.useEffect(() => {
226
+ if (!scene) {
227
+ return;
228
+ }
229
+ const group = debugGroup.current;
230
+ group.name = "__octree_debug__";
231
+ if (showDebug) {
232
+ scene.add(group);
233
+ }
234
+ return () => {
235
+ scene.remove(group);
345
236
  };
237
+ }, [scene, showDebug]);
238
+ const refreshDebug = React.useCallback(
239
+ (rootNode) => {
240
+ while (debugGroup.current.children.length) {
241
+ const child = debugGroup.current.children[0];
242
+ debugGroup.current.remove(child);
243
+ }
244
+ if (showDebug) {
245
+ buildDebugVisualization(rootNode, debugGroup.current, config.maxDepth);
246
+ }
247
+ },
248
+ [showDebug, config.maxDepth]
249
+ );
250
+ const build = React.useCallback(() => {
251
+ if (!scene) {
252
+ return;
253
+ }
254
+ const sceneBounds = new THREE__namespace.Box3();
255
+ scene.traverse((child) => {
256
+ if (child instanceof THREE__namespace.Mesh && !child.name.startsWith("__")) {
257
+ sceneBounds.expandByObject(child);
258
+ }
259
+ });
260
+ if (sceneBounds.isEmpty()) {
261
+ return;
262
+ }
263
+ const size = new THREE__namespace.Vector3();
264
+ sceneBounds.getSize(size);
265
+ const maxDim = Math.max(size.x, size.y, size.z) * 1.1;
266
+ const center = new THREE__namespace.Vector3();
267
+ sceneBounds.getCenter(center);
268
+ const halfSize = maxDim / 2;
269
+ const cubicBounds = new THREE__namespace.Box3(
270
+ new THREE__namespace.Vector3(center.x - halfSize, center.y - halfSize, center.z - halfSize),
271
+ new THREE__namespace.Vector3(center.x + halfSize, center.y + halfSize, center.z + halfSize)
272
+ );
273
+ const rootNode = createNode(cubicBounds, 0);
274
+ scene.traverse((child) => {
275
+ if (child instanceof THREE__namespace.Mesh && !child.name.startsWith("__")) {
276
+ insertObject(rootNode, child, config);
277
+ }
278
+ });
279
+ setRoot(rootNode);
280
+ setStats({ nodeCount: countNodes(rootNode), objectCount: countObjects(rootNode) });
281
+ refreshDebug(rootNode);
282
+ }, [scene, config, refreshDebug]);
283
+ const insert = React.useCallback(
284
+ (object) => {
285
+ if (!root) {
286
+ return;
287
+ }
288
+ insertObject(root, object, config);
289
+ setStats({ nodeCount: countNodes(root), objectCount: countObjects(root) });
290
+ refreshDebug(root);
291
+ },
292
+ [root, config, refreshDebug]
293
+ );
294
+ const remove = React.useCallback(
295
+ (object) => {
296
+ if (!root) {
297
+ return;
298
+ }
299
+ removeObject(root, object);
300
+ setStats({ nodeCount: countNodes(root), objectCount: countObjects(root) });
301
+ },
302
+ [root]
303
+ );
304
+ const update = React.useCallback(
305
+ (object) => {
306
+ if (!root) {
307
+ return;
308
+ }
309
+ removeObject(root, object);
310
+ insertObject(root, object, config);
311
+ },
312
+ [root, config]
313
+ );
314
+ const raycast = React.useCallback(
315
+ (raycaster) => {
316
+ if (!root) {
317
+ return [];
318
+ }
319
+ const results = [];
320
+ raycastNode(root, raycaster, results);
321
+ results.sort((a, b) => a.distance - b.distance);
322
+ return results;
323
+ },
324
+ [root]
325
+ );
326
+ const frustumCull = React.useCallback(
327
+ (camera) => {
328
+ if (!root) {
329
+ return [];
330
+ }
331
+ const frustum = new THREE__namespace.Frustum();
332
+ const projScreenMatrix = new THREE__namespace.Matrix4();
333
+ projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
334
+ frustum.setFromProjectionMatrix(projScreenMatrix);
335
+ const results = [];
336
+ frustumCullNode(root, frustum, results);
337
+ return results;
338
+ },
339
+ [root]
340
+ );
341
+ const nearestNeighbor = React.useCallback(
342
+ (point, maxDistance = Infinity) => {
343
+ if (!root) {
344
+ return null;
345
+ }
346
+ const best = { object: null, dist: maxDistance };
347
+ nearestInNode(root, point, maxDistance, best);
348
+ return best.object;
349
+ },
350
+ [root]
351
+ );
352
+ return {
353
+ build,
354
+ insert,
355
+ remove,
356
+ update,
357
+ raycast,
358
+ frustumCull,
359
+ nearestNeighbor,
360
+ nodeCount: stats.nodeCount,
361
+ objectCount: stats.objectCount,
362
+ showDebug,
363
+ setShowDebug,
364
+ root
365
+ };
346
366
  }
347
- /* ── Component ── */
348
- const NiceOctree = ({ scene, config, showDebug: initialShowDebug, className = '', }) => {
349
- const { t } = i18n.useNiceTranslation();
350
- const octree = useOctree(scene, config);
351
- const { setShowDebug } = octree;
352
- React.useEffect(() => {
353
- if (initialShowDebug !== undefined) {
354
- setShowDebug(initialShowDebug);
367
+ const NiceOctree = ({
368
+ scene,
369
+ config,
370
+ showDebug: initialShowDebug,
371
+ className = ""
372
+ }) => {
373
+ const { t } = i18n.useNiceTranslation();
374
+ const octree = useOctree(scene, config);
375
+ const { setShowDebug } = octree;
376
+ React.useEffect(() => {
377
+ if (initialShowDebug !== void 0) {
378
+ setShowDebug(initialShowDebug);
379
+ }
380
+ }, [initialShowDebug, setShowDebug]);
381
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `nice-octree ${className}`, style: { display: "grid", gap: 8, fontSize: 13 }, children: [
382
+ /* @__PURE__ */ jsxRuntime.jsxs("strong", { children: [
383
+ /* @__PURE__ */ jsxRuntime.jsx(ui.NiceIcon, { name: "tree", size: 14 }),
384
+ " ",
385
+ t("octree.spatialIndexOctree", "Spatial Index (Octree)")
386
+ ] }),
387
+ /* @__PURE__ */ jsxRuntime.jsxs(
388
+ "button",
389
+ {
390
+ type: "button",
391
+ onClick: octree.build,
392
+ style: {
393
+ background: "var(--nice-color-accent, #6366f1)",
394
+ color: "var(--nice-text-on-primary, #fff)",
395
+ border: "none",
396
+ borderRadius: 4,
397
+ padding: "6px 12px",
398
+ cursor: "pointer"
399
+ },
400
+ children: [
401
+ /* @__PURE__ */ jsxRuntime.jsx(ui.NiceIcon, { name: "zap", size: 14 }),
402
+ " ",
403
+ t("octree.buildOctree", "Build Octree")
404
+ ]
405
+ }
406
+ ),
407
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, opacity: 0.7 }, children: t("octree.nodesObjects", "Nodes: {nodeCount} · Objects: {objectCount}").replace("{nodeCount}", String(octree.nodeCount)).replace("{objectCount}", String(octree.objectCount)) }),
408
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
409
+ /* @__PURE__ */ jsxRuntime.jsx(
410
+ "input",
411
+ {
412
+ type: "checkbox",
413
+ checked: octree.showDebug,
414
+ onChange: (e) => octree.setShowDebug(e.target.checked)
355
415
  }
356
- }, [initialShowDebug, setShowDebug]);
357
- return (jsxRuntime.jsxs("div", { className: `nice-octree ${className}`, style: { display: 'grid', gap: 8, fontSize: 13 }, children: [jsxRuntime.jsxs("strong", { children: [jsxRuntime.jsx(ui.NiceIcon, { name: "tree", size: 14 }), ' ', t('octree.spatialIndexOctree', 'Spatial Index (Octree)')] }), jsxRuntime.jsxs("button", { type: "button", onClick: octree.build, style: {
358
- background: 'var(--nice-color-accent, #6366f1)',
359
- color: 'var(--nice-text-on-primary, #fff)',
360
- border: 'none',
361
- borderRadius: 4,
362
- padding: '6px 12px',
363
- cursor: 'pointer',
364
- }, children: [jsxRuntime.jsx(ui.NiceIcon, { name: "zap", size: 14 }), " ", t('octree.buildOctree', 'Build Octree')] }), jsxRuntime.jsx("div", { style: { fontSize: 11, opacity: 0.7 }, children: t('octree.nodesObjects', 'Nodes: {nodeCount} · Objects: {objectCount}')
365
- .replace('{nodeCount}', String(octree.nodeCount))
366
- .replace('{objectCount}', String(octree.objectCount)) }), jsxRuntime.jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 6 }, children: [jsxRuntime.jsx("input", { type: "checkbox", checked: octree.showDebug, onChange: (e) => octree.setShowDebug(e.target.checked) }), t('octree.showDebugWireframes', 'Show debug wireframes')] })] }));
416
+ ),
417
+ t("octree.showDebugWireframes", "Show debug wireframes")
418
+ ] })
419
+ ] });
367
420
  };
368
-
369
421
  exports.NiceOctree = NiceOctree;
370
422
  exports.useOctree = useOctree;