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,866 +0,0 @@
1
- import * as THREE from "three";
2
- import { LineSegments2 } from "three/examples/jsm/lines/LineSegments2.js";
3
- import { deepDispose, disposeGeometry, isMesh } from "../utils/utils.js";
4
- import { ZebraTool } from "../tools/cad_tools/zebra.js";
5
- import type { ZebraColorScheme, ZebraMappingMode, ColorValue, ColoredMaterial } from "../core/types";
6
-
7
-
8
- /** Highlight color when object is selected */
9
- const HIGHLIGHT_COLOR_SELECTED = 0x53a0e3;
10
-
11
- /** Highlight color when object is hovered but not selected */
12
- const HIGHLIGHT_COLOR_HOVER = 0x89b9e3;
13
-
14
- interface ShapeInfo {
15
- topo: string;
16
- geomtype: number | string | null;
17
- }
18
-
19
- // Typed mesh/line/points
20
- // FaceMesh accepts MeshStandardMaterial, MeshBasicMaterial, or any material with color
21
- type FaceMesh = THREE.Mesh<THREE.BufferGeometry, ColoredMaterial>;
22
- type VertexPoints = THREE.Points<THREE.BufferGeometry, THREE.PointsMaterial>;
23
-
24
- // Edge material interface - properties common to LineMaterial and LineBasicMaterial
25
- interface EdgeMaterial extends THREE.Material {
26
- color: THREE.Color;
27
- linewidth: number;
28
- }
29
-
30
- /**
31
- * Type guard to check if a material has color and linewidth properties.
32
- * Parameter is THREE.Material or LineMaterial (which has incompatible vertexColors type).
33
- */
34
- function isEdgeMaterial(mat: THREE.Material | import("three/examples/jsm/lines/LineMaterial.js").LineMaterial): mat is EdgeMaterial {
35
- return "color" in mat && "linewidth" in mat;
36
- }
37
-
38
- // Edges: LineSegments2 (fat lines) or THREE.LineSegments (polygon edges)
39
- // We store the material separately for type safety since Three.js types LineSegments.material as Material | Material[]
40
- type Edges = LineSegments2 | THREE.LineSegments;
41
-
42
- /**
43
- * Encapsulates material, visibility, and interaction state for a renderable CAD object.
44
- *
45
- * ObjectGroup is the leaf node in the scene graph, managing a single CAD entity:
46
- * - Front mesh (visible face)
47
- * - Back mesh (for clipping visualization)
48
- * - Edges (wireframe/boundary lines)
49
- * - Vertices (point cloud)
50
- *
51
- * ## Responsibilities
52
- * - Material management (color, opacity, metalness, roughness)
53
- * - Visibility toggling (shapes, edges independently)
54
- * - Selection/hover highlighting
55
- * - Zebra stripe tool integration
56
- * - Clipping plane caps
57
- *
58
- * ## Usage
59
- * ObjectGroups are created by NestedGroup and accessed via `nestedGroup.groups[path]`.
60
- *
61
- * @example
62
- * ```typescript
63
- * const obj = viewer.nestedGroup.groups["/assembly/part"];
64
- * if (isObjectGroup(obj)) {
65
- * obj.setShapeVisible(true);
66
- * obj.setEdgesVisible(false);
67
- * }
68
- * ```
69
- *
70
- * @internal - This is an internal class used by NestedGroup
71
- */
72
- class ObjectGroup extends THREE.Group {
73
- [key: string]: unknown; // Allow dynamic method access
74
-
75
- /** Type identifier following Three.js convention */
76
- override readonly type = "ObjectGroup";
77
- /** Type guard property following Three.js convention */
78
- readonly isObjectGroup = true;
79
- opacity: number;
80
- alpha: number;
81
- transparent: boolean;
82
- edge_color: ColorValue;
83
- shapeInfo: ShapeInfo | null;
84
- subtype: string | null;
85
- renderback: boolean;
86
-
87
- // Typed geometry references
88
- front: FaceMesh | null;
89
- back: FaceMesh | null;
90
- edges: Edges | null;
91
- edgeMaterial: EdgeMaterial | null; // Stored separately for type safety
92
- vertices: VertexPoints | null;
93
- clipping: Map<number, THREE.Group>;
94
-
95
- isSelected: boolean;
96
- originalColor: THREE.Color | null;
97
- originalBackColor: THREE.Color | null;
98
- originalWidth: number | null;
99
- vertexFocusSize: number;
100
- edgeFocusWidth: number;
101
- shapeGeometry?: THREE.BufferGeometry | null;
102
- /** Material tag from shapes data, used for Studio mode material lookup */
103
- materialTag: string;
104
- minZ?: number;
105
- height?: number;
106
- private _zebra: ZebraTool | null;
107
-
108
- // Studio mode state
109
- private _cadFrontMaterial: ColoredMaterial | null;
110
- private _cadBackMaterial: ColoredMaterial | null;
111
- private _cadOriginalColor: THREE.Color | null;
112
- private _cadOriginalBackColor: THREE.Color | null;
113
- private _isStudioMode: boolean;
114
- private _cadEdgesVisible: boolean | null;
115
-
116
- /**
117
- * Create an ObjectGroup for managing a CAD object's visual representation.
118
- * @param opacity - Default opacity value (0.0 to 1.0).
119
- * @param alpha - Transparency alpha value (0.0 to 1.0).
120
- * @param edge_color - Edge color as hex value.
121
- * @param shapeInfo - Shape metadata with topo and geomtype fields.
122
- * @param subtype - Shape subtype (e.g., "solid", "edges", "vertices").
123
- * @param renderback - Whether back faces should be rendered.
124
- */
125
- constructor(
126
- opacity: number,
127
- alpha: number,
128
- edge_color: ColorValue,
129
- shapeInfo: ShapeInfo | null,
130
- subtype: string | null,
131
- renderback: boolean = false,
132
- ) {
133
- super();
134
- this.opacity = opacity;
135
- this.alpha = alpha == null ? 1.0 : alpha;
136
- this.transparent = false;
137
- this.edge_color = edge_color;
138
- this.shapeInfo = shapeInfo;
139
- this.subtype = subtype;
140
- this.renderback = renderback;
141
-
142
- // Initialize typed geometry references
143
- this.front = null;
144
- this.back = null;
145
- this.edges = null;
146
- this.edgeMaterial = null;
147
- this.vertices = null;
148
- this.clipping = new Map();
149
-
150
- this.isSelected = false;
151
- this.originalColor = null;
152
- this.originalBackColor = null;
153
- this.originalWidth = null;
154
- this.vertexFocusSize = 8; // Size of the points when highlighted
155
- this.edgeFocusWidth = 5; // Size of the edges when highlighted
156
-
157
- this._zebra = null; // Lazy-initialized zebra tool
158
- this.materialTag = "";
159
-
160
- // Studio mode state
161
- this._cadFrontMaterial = null;
162
- this._cadBackMaterial = null;
163
- this._cadOriginalColor = null;
164
- this._cadOriginalBackColor = null;
165
- this._isStudioMode = false;
166
- this._cadEdgesVisible = null;
167
- }
168
-
169
- /**
170
- * Get the zebra tool, creating it on first access.
171
- */
172
- get zebra(): ZebraTool {
173
- if (!this._zebra) {
174
- this._zebra = new ZebraTool();
175
- }
176
- return this._zebra;
177
- }
178
-
179
- /**
180
- * Whether this ObjectGroup is currently in Studio mode.
181
- */
182
- get isStudioMode(): boolean {
183
- return this._isStudioMode;
184
- }
185
-
186
- /**
187
- * Dispose of all resources and clean up memory.
188
- * Releases geometry, materials, children, and zebra tool.
189
- */
190
- dispose(): void {
191
- if (this.shapeGeometry) {
192
- disposeGeometry(this.shapeGeometry);
193
- this.shapeGeometry = null;
194
- }
195
- // Dispose all children (includes front, back, edges, vertices, clipping groups)
196
- if (this.children) {
197
- deepDispose(this.children);
198
- this.clear();
199
- }
200
- if (this._zebra) {
201
- this._zebra.dispose();
202
- this._zebra = null;
203
- }
204
- // Release studio material references (do NOT dispose -- NestedGroup owns them)
205
- this._cadFrontMaterial = null;
206
- this._cadBackMaterial = null;
207
- this._cadOriginalColor = null;
208
- this._cadOriginalBackColor = null;
209
- this._isStudioMode = false;
210
- this._cadEdgesVisible = null;
211
- }
212
-
213
- /**
214
- * Set the front face mesh.
215
- */
216
- setFront(mesh: FaceMesh): void {
217
- this.add(mesh);
218
- this.front = mesh;
219
- this.originalColor = mesh.material.color.clone();
220
- }
221
-
222
- /**
223
- * Set the back face mesh.
224
- */
225
- setBack(mesh: FaceMesh): void {
226
- this.add(mesh);
227
- this.back = mesh;
228
- if (!this.front) {
229
- this.originalBackColor = mesh.material.color.clone();
230
- }
231
- }
232
-
233
- /**
234
- * Set the edges geometry.
235
- * Extracts and stores the material separately for type-safe access.
236
- */
237
- setEdges(edges: Edges): void {
238
- this.add(edges);
239
- this.edges = edges;
240
- // Extract material - both LineMaterial and LineBasicMaterial have color and linewidth
241
- const mat = edges.material;
242
- if (Array.isArray(mat)) {
243
- throw new Error("Multi-material edges are not supported");
244
- } else if (!isEdgeMaterial(mat)) {
245
- throw new Error("Edge material must have color and linewidth properties");
246
- }
247
- this.edgeMaterial = mat;
248
- // Only cache edge color/width if this is an edge-only object (no faces)
249
- if (!this.front) {
250
- this.originalColor = this.edgeMaterial.color.clone();
251
- this.originalWidth = this.edgeMaterial.linewidth;
252
- }
253
- }
254
-
255
- /**
256
- * Set the vertices points.
257
- */
258
- setVertices(points: VertexPoints): void {
259
- this.add(points);
260
- this.vertices = points;
261
- this.originalColor = points.material.color.clone();
262
- this.originalWidth = points.material.size;
263
- }
264
-
265
- /**
266
- * Add a clipping group for a plane index.
267
- */
268
- addClipping(group: THREE.Group, index: number): void {
269
- this.add(group);
270
- this.clipping.set(index, group);
271
- }
272
-
273
- /**
274
- * Widen or restore point/edge size for visual emphasis.
275
- * @param flag - Whether to widen (true) or restore original size (false).
276
- */
277
- widen(flag: boolean): void {
278
- if (this.vertices) {
279
- this.vertices.material.size = flag
280
- ? this.vertexFocusSize
281
- : this.isSelected
282
- ? this.vertexFocusSize - 2
283
- : this.originalWidth!;
284
- } else if (this.edgeMaterial) {
285
- this.edgeMaterial.linewidth = flag
286
- ? this.edgeFocusWidth
287
- : this.isSelected
288
- ? this.edgeFocusWidth - 2
289
- : this.originalWidth!;
290
- }
291
- }
292
-
293
- /**
294
- * Toggle the selection state of this object.
295
- * Updates highlight and resets widening.
296
- */
297
- toggleSelection(): void {
298
- const flag = !this.isSelected;
299
- this.isSelected = flag;
300
- this.highlight(flag);
301
- this.widen(false);
302
- }
303
-
304
- /**
305
- * Remove highlight from this object.
306
- * @param keepSelection - If true, preserve selection state.
307
- */
308
- unhighlight(keepSelection: boolean): void {
309
- if (!keepSelection || !this.isSelected) {
310
- this.isSelected = false;
311
- this.highlight(false);
312
- }
313
- this.widen(false);
314
- }
315
-
316
- /**
317
- * Get the highlight color based on selection state.
318
- */
319
- private _getHighlightColor(): THREE.Color {
320
- return new THREE.Color(
321
- this.isSelected ? HIGHLIGHT_COLOR_SELECTED : HIGHLIGHT_COLOR_HOVER,
322
- );
323
- }
324
-
325
- /**
326
- * Apply color to a mesh and mark material for update.
327
- */
328
- private _applyColorToMaterial(
329
- material: { color: THREE.Color; needsUpdate: boolean },
330
- color: THREE.Color,
331
- ): void {
332
- material.color = color;
333
- material.needsUpdate = true;
334
- }
335
-
336
- /**
337
- * Iterate over all child materials, excluding clipping planes.
338
- */
339
- private _forEachMaterial(callback: (material: THREE.Material) => void): void {
340
- for (const child of this.children) {
341
- if (!child.name.startsWith("clipping") && isMesh(child)) {
342
- if (Array.isArray(child.material)) {
343
- throw new Error("Multi-material meshes are not supported");
344
- }
345
- callback(child.material);
346
- }
347
- }
348
- }
349
-
350
- /**
351
- * Iterate over face materials that are MeshStandardMaterial (have PBR properties).
352
- * Skips MeshBasicMaterial and other non-PBR materials.
353
- */
354
- private _forEachStandardMaterial(callback: (material: THREE.MeshStandardMaterial) => void): void {
355
- if (this.front && this.front.material instanceof THREE.MeshStandardMaterial) {
356
- callback(this.front.material);
357
- }
358
- // back can also be MeshStandardMaterial (e.g., for polygon rendering)
359
- if (this.back && this.back.material instanceof THREE.MeshStandardMaterial) {
360
- callback(this.back.material);
361
- }
362
- }
363
-
364
- /**
365
- * Apply or remove highlight color to this object.
366
- * @param flag - Whether to apply highlight (true) or restore original color (false).
367
- */
368
- highlight(flag: boolean): void {
369
- const hColor = this._getHighlightColor();
370
-
371
- // Find primary material (front face, vertices, or edges)
372
- const primaryMaterial =
373
- this.front?.material ||
374
- this.vertices?.material ||
375
- this.edgeMaterial;
376
-
377
- if (primaryMaterial) {
378
- this.widen(flag);
379
- this._applyColorToMaterial(primaryMaterial, flag ? hColor : this.originalColor!);
380
- }
381
-
382
- // Handle back face separately (uses originalBackColor)
383
- if (this.back) {
384
- this._applyColorToMaterial(
385
- this.back.material,
386
- flag ? hColor : this.originalBackColor!,
387
- );
388
- }
389
- }
390
-
391
- /**
392
- * Clear all highlights and selection state.
393
- */
394
- clearHighlights(): void {
395
- this.highlight(false);
396
- this.isSelected = false;
397
- this.widen(false);
398
- }
399
-
400
- /**
401
- * Get metrics about this object's topology type.
402
- */
403
- metrics(): { name: string; value: number } | null {
404
- if (this.front) {
405
- return { name: "face", value: 0 };
406
- } else if (this.vertices) {
407
- return { name: "vertex", value: 0 };
408
- } else if (this.edges) {
409
- return { name: "edge", value: 0 };
410
- }
411
- return null;
412
- }
413
-
414
- /**
415
- * Set metalness value for front face materials.
416
- */
417
- setMetalness(value: number): void {
418
- this._forEachStandardMaterial((material) => {
419
- material.metalness = value;
420
- material.needsUpdate = true;
421
- });
422
- }
423
-
424
- /**
425
- * Set roughness value for front face materials.
426
- */
427
- setRoughness(value: number): void {
428
- this._forEachStandardMaterial((material) => {
429
- material.roughness = value;
430
- material.needsUpdate = true;
431
- });
432
- }
433
-
434
- /**
435
- * Enable or disable transparency mode.
436
- * Adjusts opacity and depth write settings.
437
- */
438
- setTransparent(flag: boolean): void {
439
- this.transparent = flag;
440
- const newOpacity = flag ? this.opacity * this.alpha : this.alpha;
441
- if (this.back) {
442
- this.back.material.opacity = newOpacity;
443
- }
444
- if (this.front) {
445
- this.front.material.opacity = newOpacity;
446
- }
447
- this._forEachMaterial((material) => {
448
- // turn depth write off for transparent objects
449
- material.depthWrite = this.alpha < 1.0 ? false : !flag;
450
- // but keep depth test
451
- material.depthTest = true;
452
- material.needsUpdate = true;
453
- });
454
- }
455
-
456
- /**
457
- * Set whether edges should be rendered in black or original color.
458
- * Skips edges with vertexColors enabled (e.g., trihedron axes).
459
- */
460
- setBlackEdges(flag: boolean): void {
461
- if (this.edgeMaterial && !this.edgeMaterial.vertexColors) {
462
- const color = flag ? 0x000000 : this.edge_color;
463
- // Only update originalColor for edge-only objects (no face mesh).
464
- // For face+edge objects, originalColor tracks the face color.
465
- if (!this.front) {
466
- this.originalColor = new THREE.Color(color);
467
- }
468
- this.edgeMaterial.color = new THREE.Color(color);
469
- this.edgeMaterial.needsUpdate = true;
470
- }
471
- }
472
-
473
- /**
474
- * Set the edge color.
475
- * Skips edges with vertexColors enabled (e.g., trihedron axes).
476
- */
477
- setEdgeColor(color: number): void {
478
- if (this.edgeMaterial && !this.edgeMaterial.vertexColors) {
479
- this.edge_color = color;
480
- this.edgeMaterial.color = new THREE.Color(color);
481
- this.edgeMaterial.needsUpdate = true;
482
- }
483
- }
484
-
485
- /**
486
- * Set the opacity value. Only applied visually when transparent mode is enabled.
487
- */
488
- setOpacity(opacity: number): void {
489
- this.opacity = opacity;
490
- // Only apply visually if transparent mode is enabled
491
- if (this.transparent) {
492
- const newOpacity = this.opacity * this.alpha;
493
- if (this.front) {
494
- this.front.material.opacity = newOpacity;
495
- this.front.material.needsUpdate = true;
496
- }
497
- if (this.back) {
498
- this.back.material.opacity = newOpacity;
499
- this.back.material.needsUpdate = true;
500
- }
501
- }
502
- }
503
-
504
- /**
505
- * Set visibility of the shape (front face and clipping caps).
506
- */
507
- setShapeVisible(flag: boolean): void {
508
- if (this.front) {
509
- if (this._isStudioMode) {
510
- // Studio materials are shared — use mesh.visible for per-object visibility
511
- this.front.visible = flag;
512
- } else {
513
- this.front.material.visible = flag;
514
- }
515
- }
516
- for (const clippingGroup of this.clipping.values()) {
517
- const child0 = clippingGroup.children[0];
518
- const child1 = clippingGroup.children[1];
519
- if (isMesh(child0)) {
520
- if (Array.isArray(child0.material)) {
521
- throw new Error("Multi-material meshes are not supported");
522
- }
523
- child0.material.visible = flag;
524
- }
525
- if (isMesh(child1)) {
526
- if (Array.isArray(child1.material)) {
527
- throw new Error("Multi-material meshes are not supported");
528
- }
529
- child1.material.visible = flag;
530
- }
531
- }
532
- if (this.back && this.renderback) {
533
- if (this._isStudioMode) {
534
- this.back.visible = flag;
535
- } else {
536
- this.back.material.visible = flag;
537
- }
538
- }
539
- }
540
-
541
- /**
542
- * Set visibility of edges and vertices.
543
- */
544
- setEdgesVisible(flag: boolean): void {
545
- if (this.edgeMaterial) {
546
- this.edgeMaterial.visible = flag;
547
- }
548
- if (this.vertices) {
549
- this.vertices.material.visible = flag;
550
- }
551
- }
552
-
553
- /**
554
- * Set visibility of back faces.
555
- */
556
- setBackVisible(flag: boolean): void {
557
- if (this.back && this.front) {
558
- const frontVisible = this._isStudioMode
559
- ? this.front.visible
560
- : this.front.material.visible;
561
- if (frontVisible) {
562
- if (this._isStudioMode) {
563
- this.back.visible = this.renderback || flag;
564
- } else {
565
- this.back.material.visible = this.renderback || flag;
566
- }
567
- }
568
- }
569
- }
570
-
571
- /**
572
- * Get the current visibility state.
573
- */
574
- getVisibility(): boolean {
575
- if (this.front) {
576
- const frontVisible = this._isStudioMode
577
- ? this.front.visible
578
- : this.front.material.visible;
579
- if (this.edgeMaterial) {
580
- return frontVisible || this.edgeMaterial.visible;
581
- } else {
582
- return frontVisible;
583
- }
584
- } else if (this.edgeMaterial) {
585
- return this.edgeMaterial.visible;
586
- } else if (this.vertices) {
587
- return this.vertices.material.visible;
588
- }
589
- return false;
590
- }
591
-
592
- /**
593
- * Set clip intersection mode for all materials.
594
- */
595
- setClipIntersection(flag: boolean): void {
596
- this._forEachMaterial((material) => {
597
- material.clipIntersection = flag;
598
- });
599
- }
600
-
601
- /**
602
- * Set clipping planes for all materials.
603
- */
604
- setClipPlanes(planes: THREE.Plane[]): void {
605
- if (this.back) {
606
- this.back.material.clippingPlanes = planes;
607
- }
608
- if (this.front) {
609
- this.front.material.clippingPlanes = planes;
610
- }
611
- if (this.edgeMaterial) {
612
- this.edgeMaterial.clippingPlanes = planes;
613
- }
614
- if (this.vertices) {
615
- this.vertices.material.clippingPlanes = planes;
616
- }
617
- this.updateMaterials(true);
618
- }
619
-
620
- /**
621
- * Set polygon offset for depth sorting of back faces.
622
- */
623
- setPolygonOffset(offset: number): void {
624
- if (this.back) {
625
- this.back.material.polygonOffsetUnits = offset;
626
- }
627
- }
628
-
629
- /**
630
- * Set Z-axis scale for GDS extrusion visualization.
631
- * Recursively scales all meshes and adjusts positions.
632
- */
633
- setZScale(value: number): void {
634
- const walk = (
635
- obj: THREE.Object3D,
636
- minZ: number,
637
- height: number,
638
- scalePos: boolean = true,
639
- ) => {
640
- for (const child of obj.children) {
641
- if ("isMesh" in child || "isLine" in child) {
642
- child.scale.z = value;
643
- if (scalePos && child.parent) {
644
- child.parent.position.z = minZ * value;
645
- }
646
- } else if ("isGroup" in child) {
647
- // don't scale position of clipping planes
648
- walk(child, minZ, height, !child.name.startsWith("clipping"));
649
- }
650
- }
651
- };
652
- if (this.front || this.back || this.edges) {
653
- walk(this, this.minZ!, this.height!);
654
- }
655
- }
656
-
657
- /**
658
- * Mark all materials as needing update.
659
- */
660
- updateMaterials(flag: boolean): void {
661
- if (this.back) {
662
- this.back.material.needsUpdate = flag;
663
- }
664
- if (this.front) {
665
- this.front.material.needsUpdate = flag;
666
- }
667
- if (this.edgeMaterial) {
668
- this.edgeMaterial.needsUpdate = flag;
669
- }
670
- if (this.vertices) {
671
- this.vertices.material.needsUpdate = flag;
672
- }
673
- }
674
-
675
- /**
676
- * Remove and dispose all clipping groups from this object.
677
- */
678
- clearClipping(): void {
679
- for (const [, group] of this.clipping) {
680
- this.remove(group);
681
- deepDispose(group);
682
- }
683
- this.clipping.clear();
684
- }
685
-
686
- /**
687
- * Enable or disable zebra stripe visualization on front faces.
688
- */
689
- setZebra(flag: boolean): void {
690
- if (this.front) {
691
- const visible = this.front.material.visible;
692
- if (flag) {
693
- this.zebra.applyToMesh(this.front, visible);
694
- } else {
695
- this.zebra.restoreMesh(this.front, visible);
696
- }
697
- }
698
- }
699
-
700
- /**
701
- * Set the number of zebra stripes.
702
- */
703
- setZebraCount(value: number): void {
704
- this.zebra.setStripeCount(value);
705
- }
706
-
707
- /**
708
- * Set the opacity of zebra stripes.
709
- */
710
- setZebraOpacity(value: number): void {
711
- this.zebra.setStripeOpacity(value);
712
- }
713
-
714
- /**
715
- * Set the direction/angle of zebra stripes.
716
- */
717
- setZebraDirection(value: number): void {
718
- this.zebra.setStripeDirection(value);
719
- }
720
-
721
- /**
722
- * Set the color scheme for zebra stripes.
723
- */
724
- setZebraColorScheme(value: ZebraColorScheme): void {
725
- this.zebra.setColorScheme(value);
726
- }
727
-
728
- /**
729
- * Set the mapping mode for zebra stripes.
730
- */
731
- setZebraMappingMode(value: ZebraMappingMode): void {
732
- this.zebra.setMappingMode(value);
733
- }
734
-
735
- // ===========================================================================
736
- // Studio Mode
737
- // ===========================================================================
738
-
739
- /**
740
- * Enter Studio mode: swap CAD materials for pre-built Studio materials.
741
- *
742
- * The caller (NestedGroup) is responsible for resolving material tags and
743
- * building MeshPhysicalMaterial instances via MaterialFactory. ObjectGroup
744
- * just receives the finished materials and performs the swap.
745
- *
746
- * On first call, saves the current CAD material references so they can be
747
- * restored by `leaveStudioMode()`. Copies the `material.visible` flag from
748
- * CAD to Studio material to preserve tree-view hide/show state. Updates
749
- * `originalColor` / `originalBackColor` so highlight/unhighlight works
750
- * correctly in Studio mode.
751
- *
752
- * @param studioFront - Studio material for front face, or null if this object has no front mesh
753
- * @param studioBack - Studio material for back face, or null if back face should not be swapped
754
- */
755
- enterStudioMode(
756
- studioFront: THREE.MeshPhysicalMaterial | null,
757
- studioBack: THREE.MeshPhysicalMaterial | null,
758
- ): void {
759
- if (this._isStudioMode) return;
760
-
761
- // --- Save CAD state ---
762
- if (this.front) {
763
- this._cadFrontMaterial = this.front.material;
764
- }
765
- if (this.back) {
766
- this._cadBackMaterial = this.back.material;
767
- }
768
- // Save original colors used by highlight/unhighlight
769
- this._cadOriginalColor = this.originalColor ? this.originalColor.clone() : null;
770
- this._cadOriginalBackColor = this.originalBackColor ? this.originalBackColor.clone() : null;
771
-
772
- // Save edge visibility state
773
- this._cadEdgesVisible = this.edgeMaterial ? this.edgeMaterial.visible : null;
774
-
775
- // --- Swap front material ---
776
- if (this.front && studioFront) {
777
- // Transfer per-object visibility to mesh.visible (NOT material.visible)
778
- // because studio materials are shared across objects via cache.
779
- // Writing to a shared material's .visible would affect all users.
780
- this.front.visible = this.front.material.visible;
781
- this.front.material = studioFront;
782
- // Update originalColor to studio material's color for correct highlight
783
- this.originalColor = studioFront.color;
784
- }
785
-
786
- // --- Swap back material ---
787
- if (this.back && studioBack && this.renderback) {
788
- // Same: per-object visibility via mesh.visible, not shared material
789
- this.back.visible = this.back.material.visible;
790
- this.back.material = studioBack;
791
- // Update originalBackColor for correct highlight on back face
792
- this.originalBackColor = studioBack.color;
793
- }
794
-
795
- this._isStudioMode = true;
796
- }
797
-
798
- /**
799
- * Leave Studio mode: restore CAD materials.
800
- *
801
- * Copies `material.visible` from Studio back to CAD material to preserve
802
- * any visibility changes made while in Studio mode (e.g., tree-view toggle).
803
- * Restores `originalColor` / `originalBackColor` to CAD material colors.
804
- * Restores edge visibility to the state saved when entering Studio mode.
805
- */
806
- leaveStudioMode(): void {
807
- if (!this._isStudioMode) return;
808
-
809
- // --- Restore front material ---
810
- if (this.front && this._cadFrontMaterial) {
811
- // Copy visibility from mesh.visible back to CAD material
812
- // (studio mode uses mesh.visible for per-object visibility)
813
- this._cadFrontMaterial.visible = this.front.visible;
814
- this.front.material = this._cadFrontMaterial;
815
- this.front.visible = true; // Reset mesh visibility
816
- }
817
-
818
- // --- Restore back material ---
819
- if (this.back && this._cadBackMaterial && this.renderback) {
820
- // Copy visibility from mesh.visible back to CAD material
821
- this._cadBackMaterial.visible = this.back.visible;
822
- this.back.material = this._cadBackMaterial;
823
- this.back.visible = true; // Reset mesh visibility
824
- }
825
-
826
- // --- Restore original colors for highlight ---
827
- if (this._cadOriginalColor) {
828
- this.originalColor = this._cadOriginalColor.clone();
829
- }
830
- if (this._cadOriginalBackColor) {
831
- this.originalBackColor = this._cadOriginalBackColor.clone();
832
- }
833
-
834
- // --- Restore edge visibility ---
835
- if (this.edgeMaterial && this._cadEdgesVisible !== null) {
836
- this.edgeMaterial.visible = this._cadEdgesVisible;
837
- }
838
-
839
- this._isStudioMode = false;
840
- }
841
-
842
- /**
843
- * Toggle edge visibility while in Studio mode.
844
- *
845
- * Only affects edges (not vertices). Should only be called while in
846
- * Studio mode; the saved CAD edge visibility is not affected.
847
- *
848
- * @param visible - Whether edges should be visible
849
- */
850
- setStudioShowEdges(visible: boolean): void {
851
- if (this.edgeMaterial) {
852
- this.edgeMaterial.visible = visible;
853
- }
854
- }
855
- }
856
-
857
- /**
858
- * Type guard to check if an object is an ObjectGroup instance.
859
- * Uses the isObjectGroup property following Three.js convention.
860
- */
861
- function isObjectGroup(obj: THREE.Object3D | null): obj is ObjectGroup {
862
- return obj != null && "isObjectGroup" in obj && obj.isObjectGroup === true;
863
- }
864
-
865
- export { ObjectGroup, isObjectGroup };
866
- export type { ShapeInfo, FaceMesh, Edges, EdgeMaterial, VertexPoints };