three-cad-viewer 4.3.4 → 4.3.6

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 (59) hide show
  1. package/dist/scene/clipping.d.ts +6 -0
  2. package/dist/three-cad-viewer.esm.js +20 -5
  3. package/dist/three-cad-viewer.esm.js.map +1 -1
  4. package/dist/three-cad-viewer.esm.min.js +1 -1
  5. package/dist/three-cad-viewer.js +20 -5
  6. package/dist/three-cad-viewer.min.js +1 -1
  7. package/package.json +2 -3
  8. package/src/_version.ts +0 -1
  9. package/src/camera/camera.ts +0 -445
  10. package/src/camera/controls/CADOrbitControls.ts +0 -241
  11. package/src/camera/controls/CADTrackballControls.ts +0 -598
  12. package/src/camera/controls.ts +0 -380
  13. package/src/core/patches.ts +0 -16
  14. package/src/core/studio-manager.ts +0 -652
  15. package/src/core/types.ts +0 -892
  16. package/src/core/viewer-state.ts +0 -784
  17. package/src/core/viewer.ts +0 -4821
  18. package/src/index.ts +0 -151
  19. package/src/rendering/environment.ts +0 -840
  20. package/src/rendering/light-detection.ts +0 -327
  21. package/src/rendering/material-factory.ts +0 -735
  22. package/src/rendering/material-presets.ts +0 -289
  23. package/src/rendering/raycast.ts +0 -291
  24. package/src/rendering/room-environment.ts +0 -192
  25. package/src/rendering/studio-composer.ts +0 -577
  26. package/src/rendering/studio-floor.ts +0 -108
  27. package/src/rendering/texture-cache.ts +0 -324
  28. package/src/rendering/tree-model.ts +0 -542
  29. package/src/rendering/triplanar.ts +0 -329
  30. package/src/scene/animation.ts +0 -343
  31. package/src/scene/axes.ts +0 -108
  32. package/src/scene/bbox.ts +0 -223
  33. package/src/scene/clipping.ts +0 -640
  34. package/src/scene/grid.ts +0 -864
  35. package/src/scene/nestedgroup.ts +0 -1444
  36. package/src/scene/objectgroup.ts +0 -866
  37. package/src/scene/orientation.ts +0 -259
  38. package/src/scene/render-shape.ts +0 -634
  39. package/src/tools/cad_tools/measure.ts +0 -811
  40. package/src/tools/cad_tools/select.ts +0 -100
  41. package/src/tools/cad_tools/tools.ts +0 -231
  42. package/src/tools/cad_tools/ui.ts +0 -454
  43. package/src/tools/cad_tools/zebra.ts +0 -369
  44. package/src/types/html.d.ts +0 -5
  45. package/src/types/n8ao.d.ts +0 -28
  46. package/src/types/three-augmentation.d.ts +0 -60
  47. package/src/ui/display.ts +0 -3295
  48. package/src/ui/index.html +0 -505
  49. package/src/ui/info.ts +0 -177
  50. package/src/ui/slider.ts +0 -206
  51. package/src/ui/toolbar.ts +0 -347
  52. package/src/ui/treeview.ts +0 -945
  53. package/src/utils/decode-instances.ts +0 -233
  54. package/src/utils/font.ts +0 -60
  55. package/src/utils/gpu-tracker.ts +0 -265
  56. package/src/utils/logger.ts +0 -92
  57. package/src/utils/sizeof.ts +0 -116
  58. package/src/utils/timer.ts +0 -69
  59. package/src/utils/utils.ts +0 -446
@@ -1,542 +0,0 @@
1
- /**
2
- * Tree node visibility states.
3
- */
4
- const States = {
5
- unselected: 0,
6
- selected: 1,
7
- mixed: 2,
8
- disabled: 3,
9
- } as const;
10
-
11
- type StateValue = typeof States[keyof typeof States];
12
-
13
- /** Icon index: 0 for shapes, 1 for edges */
14
- type IconIndex = 0 | 1;
15
-
16
- /**
17
- * Represents a node in the tree structure.
18
- */
19
- interface TreeNode {
20
- name: string;
21
- state: [StateValue, StateValue];
22
- path: string;
23
- rendered: boolean;
24
- level: number;
25
- children?: Record<string, TreeNode>;
26
- expanded?: boolean;
27
- }
28
-
29
- /**
30
- * Raw tree data input format.
31
- */
32
- interface TreeData {
33
- [key: string]: [StateValue, StateValue] | TreeData;
34
- }
35
-
36
- /**
37
- * Type guard to check if a tree data value is a leaf state array.
38
- */
39
- function isLeafState(value: [StateValue, StateValue] | TreeData): value is [StateValue, StateValue] {
40
- return Array.isArray(value) && value.length === 2;
41
- }
42
-
43
- /**
44
- * Type guard to check if a tree data value is a nested TreeData object.
45
- */
46
- function isTreeData(value: [StateValue, StateValue] | TreeData): value is TreeData {
47
- return typeof value === "object" && !Array.isArray(value);
48
- }
49
-
50
- /**
51
- * Options for configuring a TreeModel instance.
52
- */
53
- interface TreeModelOptions {
54
- linkIcons?: boolean;
55
- onStateChange?: ((node: TreeNode, iconNumber: IconIndex) => void) | null;
56
- }
57
-
58
- /**
59
- * Manages tree data structure, traversal, and state for the TreeView.
60
- * Separates data/logic concerns from DOM rendering.
61
- */
62
- class TreeModel {
63
- linkIcons: boolean;
64
- onStateChange: ((node: TreeNode, iconNumber: IconIndex) => void) | null;
65
- maxLevel: number;
66
- root: TreeNode | null;
67
-
68
- /**
69
- * Create a TreeModel instance.
70
- * @param treeData - The raw tree structure data.
71
- * @param options - Configuration options.
72
- */
73
- constructor(treeData: TreeData, options: TreeModelOptions = {}) {
74
- this.linkIcons = options.linkIcons !== undefined ? options.linkIcons : true;
75
- this.onStateChange = options.onStateChange || null;
76
-
77
- this.maxLevel = 0;
78
- this.root = this._buildTreeStructure(treeData);
79
- }
80
-
81
- /**
82
- * Builds the internal tree structure from raw data.
83
- * @param data - Raw tree data.
84
- * @returns The root node of the built tree.
85
- */
86
- private _buildTreeStructure(data: TreeData): TreeNode {
87
- const build = (
88
- data: TreeData,
89
- path: string | null,
90
- level: number
91
- ): [Record<string, TreeNode>, [StateValue, StateValue]] => {
92
- const result: [StateValue, StateValue] = [States.unselected, States.unselected];
93
-
94
- const calcState = (
95
- states: [[boolean, boolean], [boolean, boolean], [boolean, boolean], [boolean, boolean]]
96
- ): [StateValue, StateValue] => {
97
- for (const s of [0, 1] as const) {
98
- if (
99
- states[States.mixed][s] ||
100
- (states[States.selected][s] && states[States.unselected][s])
101
- ) {
102
- result[s] = States.mixed;
103
- } else if (states[States.selected][s]) {
104
- result[s] = States.selected;
105
- } else if (states[States.unselected][s]) {
106
- result[s] = States.unselected;
107
- } else if (states[States.disabled][s]) {
108
- result[s] = States.disabled;
109
- }
110
- }
111
- return result;
112
- };
113
-
114
- const tree: Record<string, TreeNode> = {};
115
-
116
- if (this.maxLevel < level) {
117
- this.maxLevel = level;
118
- }
119
-
120
- const trackStates: [[boolean, boolean], [boolean, boolean], [boolean, boolean], [boolean, boolean]] = [
121
- [false, false],
122
- [false, false],
123
- [false, false],
124
- [false, false],
125
- ];
126
-
127
- for (const key in data) {
128
- let currentPath = "";
129
-
130
- if (path == null) {
131
- currentPath = key;
132
- } else {
133
- currentPath = `${path}/${key}`;
134
- }
135
-
136
- let childStates: [StateValue, StateValue];
137
- const value = data[key];
138
-
139
- if (isLeafState(value)) {
140
- // Leaf node with state array
141
- childStates = value;
142
- trackStates[value[0]][0] = true;
143
- trackStates[value[1]][1] = true;
144
- tree[key] = {
145
- name: key,
146
- state: childStates,
147
- path: currentPath,
148
- rendered: false,
149
- level: level,
150
- };
151
- } else if (isTreeData(value) && Object.keys(value).length > 0) {
152
- // Non-empty object - recurse into children
153
- let children: Record<string, TreeNode>;
154
- [children, childStates] = build(value, currentPath, level + 1);
155
- trackStates[childStates[0]][0] = true;
156
- trackStates[childStates[1]][1] = true;
157
- tree[key] = {
158
- name: key,
159
- state: childStates,
160
- path: currentPath,
161
- rendered: false,
162
- level: level,
163
- children,
164
- expanded: false,
165
- };
166
- } else {
167
- // Empty object or unexpected value - treat as disabled leaf
168
- childStates = [States.disabled, States.disabled];
169
- trackStates[States.disabled][0] = true;
170
- trackStates[States.disabled][1] = true;
171
- tree[key] = {
172
- name: key,
173
- state: childStates,
174
- path: currentPath,
175
- rendered: false,
176
- level: level,
177
- };
178
- }
179
- }
180
-
181
- const newState = calcState(trackStates);
182
- return [tree, newState];
183
- };
184
-
185
- const root = build(data, null, 0)[0];
186
- return root[Object.keys(root)[0]];
187
- }
188
-
189
- // ============================================================================
190
- // Tree Traversal Methods
191
- // ============================================================================
192
-
193
- /**
194
- * Traverse the tree and call a callback for each node.
195
- * @param node - Starting node (usually root).
196
- * @param callback - Function to call for each node.
197
- */
198
- traverse(node: TreeNode, callback: (node: TreeNode) => void): void {
199
- callback(node);
200
- if (node.children) {
201
- for (const key of Object.keys(node.children)) {
202
- this.traverse(node.children[key], callback);
203
- }
204
- }
205
- }
206
-
207
- /**
208
- * Check if a node is a leaf (has no children).
209
- * @param node - The node to check.
210
- * @returns True if the node is a leaf.
211
- */
212
- isLeaf(node: TreeNode): boolean {
213
- return node.children == null;
214
- }
215
-
216
- /**
217
- * Get the full path of a node (with leading slash).
218
- * @param node - The node.
219
- * @returns The full path (e.g., "/root/child").
220
- */
221
- getNodePath(node: TreeNode | null): string {
222
- if (node == null) {
223
- return "";
224
- }
225
- return "/" + node.path;
226
- }
227
-
228
- /**
229
- * Find a node by its path.
230
- * @param path - Path string or array of path parts.
231
- * @returns The found node or null.
232
- */
233
- findNodeByPath(path: string | string[]): TreeNode | null {
234
- if (!this.root) return null;
235
- const parts = Array.isArray(path) ? path : path.split("/").filter(Boolean);
236
-
237
- let current: TreeNode = this.root;
238
- for (let i = 1; i < parts.length; i++) {
239
- const part = parts[i];
240
- if (current.children && current.children[part]) {
241
- current = current.children[part];
242
- } else {
243
- return null;
244
- }
245
- }
246
- return current;
247
- }
248
-
249
- /**
250
- * Get the parent node of a given node.
251
- * @param node - The child node.
252
- * @returns The parent node or null if node is root.
253
- */
254
- getParent(node: TreeNode): TreeNode | null {
255
- const parentPath = node.path.substring(0, node.path.lastIndexOf("/"));
256
- if (parentPath.length === 0) {
257
- return null;
258
- }
259
- return this.findNodeByPath(parentPath);
260
- }
261
-
262
- // ============================================================================
263
- // State Management Methods
264
- // ============================================================================
265
-
266
- /**
267
- * Get the state of a node by path.
268
- * @param path - The node path.
269
- * @returns The state array [icon0State, icon1State] or null.
270
- */
271
- getState(path: string): [StateValue, StateValue] | null {
272
- const node = this.findNodeByPath(path);
273
- return node ? node.state : null;
274
- }
275
-
276
- /**
277
- * Get all leaf node states.
278
- * @returns Map of path -> state array.
279
- */
280
- getStates(): Record<string, [StateValue, StateValue]> {
281
- const states: Record<string, [StateValue, StateValue]> = {};
282
- if (this.root) {
283
- this.traverse(this.root, (node) => {
284
- if (this.isLeaf(node)) {
285
- states[this.getNodePath(node)] = node.state;
286
- }
287
- });
288
- }
289
- return states;
290
- }
291
-
292
- /**
293
- * Set the state of a node by path.
294
- * @param path - The node path.
295
- * @param state - The new state [icon0State, icon1State].
296
- */
297
- setState(path: string, state: [StateValue, StateValue]): void {
298
- const node = this.findNodeByPath(path);
299
- if (node) {
300
- this.toggleNodeState(node, 0, state[0] === States.selected);
301
- this.toggleNodeState(node, 1, state[1] === States.selected);
302
- }
303
- }
304
-
305
- /**
306
- * Set multiple node states at once.
307
- * @param states - Map of path -> state array.
308
- */
309
- setStates(states: Record<string, [StateValue, StateValue]>): void {
310
- for (const path in states) {
311
- this.setState(path, states[path]);
312
- }
313
- }
314
-
315
- /**
316
- * Toggle the state of a node's icon.
317
- * @param node - The node to toggle.
318
- * @param iconNumber - Which icon (0 or 1).
319
- * @param force - Force to specific state (true=selected, false=unselected, null=toggle).
320
- * @returns True if state was changed.
321
- */
322
- toggleNodeState(node: TreeNode, iconNumber: IconIndex, force: boolean | null = null): boolean {
323
- const currentState = node.state[iconNumber];
324
- if (currentState === States.disabled) {
325
- return false;
326
- }
327
-
328
- // Determine which icons to update (linked mode affects icon 0)
329
- const icons: IconIndex[] = iconNumber === 0 ? (this.linkIcons ? [0, 1] : [0]) : [1];
330
-
331
- for (const i of icons) {
332
- if (node.state[i] !== States.disabled) {
333
- if (force !== null) {
334
- node.state[i] = force ? States.selected : States.unselected;
335
- } else {
336
- node.state[i] =
337
- currentState === States.selected
338
- ? States.unselected
339
- : States.selected;
340
- }
341
-
342
- // Notify of state change
343
- if (this.onStateChange) {
344
- this.onStateChange(node, i);
345
- }
346
-
347
- // Update parent and children states
348
- this._updateParentStates(node, i);
349
- this._updateChildrenStates(node, i);
350
- }
351
- }
352
-
353
- return true;
354
- }
355
-
356
- /**
357
- * Update parent states based on children states.
358
- * @param node - The node whose parents should be updated.
359
- * @param iconNumber - Which icon to update.
360
- */
361
- private _updateParentStates(node: TreeNode, iconNumber: IconIndex): void {
362
- let current = this.getParent(node);
363
- while (current) {
364
- const children = Object.values(current.children!);
365
- const allSelected = children.every(
366
- (child) =>
367
- child.state[iconNumber] === States.selected ||
368
- child.state[iconNumber] === States.disabled
369
- );
370
- const allUnselected = children.every(
371
- (child) =>
372
- child.state[iconNumber] === States.unselected ||
373
- child.state[iconNumber] === States.disabled
374
- );
375
-
376
- const newState: StateValue = allSelected
377
- ? States.selected
378
- : allUnselected
379
- ? States.unselected
380
- : States.mixed;
381
-
382
- if (current.state[iconNumber] !== newState) {
383
- if (current.state[iconNumber] !== States.disabled) {
384
- current.state[iconNumber] = newState;
385
- if (this.onStateChange) {
386
- this.onStateChange(current, iconNumber);
387
- }
388
- }
389
- }
390
- current = this.getParent(current);
391
- }
392
- }
393
-
394
- /**
395
- * Update children states to match parent state.
396
- * @param node - The parent node.
397
- * @param iconNumber - Which icon to update.
398
- */
399
- private _updateChildrenStates(node: TreeNode, iconNumber: IconIndex): void {
400
- const newState = node.state[iconNumber];
401
-
402
- const updateChildren = (current: TreeNode): void => {
403
- if (current.state[iconNumber] !== newState) {
404
- if (current.state[iconNumber] !== States.disabled) {
405
- current.state[iconNumber] = newState;
406
- if (this.onStateChange) {
407
- this.onStateChange(current, iconNumber);
408
- }
409
- }
410
- }
411
- if (current.children) {
412
- for (const child in current.children) {
413
- updateChildren(current.children[child]);
414
- }
415
- }
416
- };
417
-
418
- updateChildren(node);
419
- }
420
-
421
- // ============================================================================
422
- // Convenience Methods
423
- // ============================================================================
424
-
425
- /**
426
- * Hide all nodes (set state to unselected).
427
- */
428
- hideAll(): void {
429
- if (!this.root) return;
430
- this.toggleNodeState(this.root, 0, false);
431
- // Also handle case when all objects are edges and state[0] is disabled
432
- if (!this.linkIcons || this.root.state[0] === States.disabled) {
433
- this.toggleNodeState(this.root, 1, false);
434
- }
435
- }
436
-
437
- /**
438
- * Show all nodes (set state to selected).
439
- */
440
- showAll(): void {
441
- if (!this.root) return;
442
- this.toggleNodeState(this.root, 0, true);
443
- if (!this.linkIcons) {
444
- this.toggleNodeState(this.root, 1, true);
445
- }
446
- }
447
-
448
- /**
449
- * Show a specific node by path.
450
- * @param path - The node path.
451
- */
452
- show(path: string): void {
453
- const node = this.findNodeByPath(path);
454
- if (node) {
455
- this.toggleNodeState(node, 0, true);
456
- }
457
- }
458
-
459
- /**
460
- * Hide a specific node by path.
461
- * @param path - The node path.
462
- */
463
- hide(path: string): void {
464
- const node = this.findNodeByPath(path);
465
- if (node) {
466
- this.toggleNodeState(node, 0, false);
467
- }
468
- }
469
-
470
- /**
471
- * Set expanded state for all nodes up to a certain level.
472
- * @param level - The level to expand to (-1 for smart expand).
473
- */
474
- setExpandedLevel(level: number): void {
475
- if (!this.root) return;
476
- this.traverse(this.root, (node) => {
477
- if (this.isLeaf(node)) return;
478
-
479
- if (level === -1) {
480
- // Smart expand: expand unless single leaf child
481
- node.expanded =
482
- node.children != null &&
483
- !(
484
- Object.keys(node.children).length === 1 &&
485
- this.isLeaf(Object.values(node.children)[0])
486
- );
487
- } else {
488
- node.expanded = node.level < level;
489
- }
490
- });
491
- }
492
-
493
- /**
494
- * Expand all nodes along a path.
495
- * @param path - The path to expand to.
496
- * @returns The final node in the path, or null if not found.
497
- */
498
- expandPath(path: string): TreeNode | null {
499
- const parts = path.split("/").filter(Boolean);
500
- let current = "";
501
- let node: TreeNode | null = null;
502
-
503
- for (const part of parts) {
504
- current += "/" + part;
505
- node = this.findNodeByPath(current);
506
-
507
- if (node) {
508
- if (node.children) {
509
- node.expanded = true;
510
- }
511
- } else {
512
- return null;
513
- }
514
- }
515
-
516
- return node;
517
- }
518
-
519
- /**
520
- * Collapse a node by path.
521
- * @param path - The path to collapse.
522
- * @returns The collapsed node, or null if not found.
523
- */
524
- collapsePath(path: string): TreeNode | null {
525
- const node = this.findNodeByPath(path);
526
- if (node) {
527
- node.expanded = false;
528
- }
529
- return node;
530
- }
531
-
532
- /**
533
- * Dispose of resources.
534
- */
535
- dispose(): void {
536
- this.root = null;
537
- this.onStateChange = null;
538
- }
539
- }
540
-
541
- export { TreeModel, States };
542
- export type { TreeNode, TreeData, StateValue, IconIndex };