three-cad-viewer 4.1.2 → 4.2.0

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 (58) hide show
  1. package/Readme.md +12 -5
  2. package/dist/camera/camera.d.ts +14 -2
  3. package/dist/core/studio-manager.d.ts +91 -0
  4. package/dist/core/types.d.ts +260 -9
  5. package/dist/core/viewer-state.d.ts +28 -2
  6. package/dist/core/viewer.d.ts +200 -6
  7. package/dist/index.d.ts +7 -2
  8. package/dist/rendering/environment.d.ts +239 -0
  9. package/dist/rendering/light-detection.d.ts +44 -0
  10. package/dist/rendering/material-factory.d.ts +77 -2
  11. package/dist/rendering/material-presets.d.ts +32 -0
  12. package/dist/rendering/room-environment.d.ts +13 -0
  13. package/dist/rendering/studio-composer.d.ts +130 -0
  14. package/dist/rendering/studio-floor.d.ts +53 -0
  15. package/dist/rendering/texture-cache.d.ts +142 -0
  16. package/dist/rendering/triplanar.d.ts +37 -0
  17. package/dist/scene/animation.d.ts +1 -1
  18. package/dist/scene/clipping.d.ts +31 -0
  19. package/dist/scene/nestedgroup.d.ts +64 -27
  20. package/dist/scene/objectgroup.d.ts +47 -0
  21. package/dist/three-cad-viewer.css +339 -29
  22. package/dist/three-cad-viewer.esm.js +27567 -11874
  23. package/dist/three-cad-viewer.esm.js.map +1 -1
  24. package/dist/three-cad-viewer.esm.min.js +10 -4
  25. package/dist/three-cad-viewer.js +27486 -11787
  26. package/dist/three-cad-viewer.min.js +10 -4
  27. package/dist/ui/display.d.ts +147 -0
  28. package/dist/utils/decode-instances.d.ts +60 -0
  29. package/dist/utils/utils.d.ts +10 -0
  30. package/package.json +4 -2
  31. package/src/_version.ts +1 -1
  32. package/src/camera/camera.ts +27 -10
  33. package/src/core/studio-manager.ts +682 -0
  34. package/src/core/types.ts +328 -9
  35. package/src/core/viewer-state.ts +84 -4
  36. package/src/core/viewer.ts +453 -22
  37. package/src/index.ts +25 -1
  38. package/src/rendering/environment.ts +840 -0
  39. package/src/rendering/light-detection.ts +327 -0
  40. package/src/rendering/material-factory.ts +456 -2
  41. package/src/rendering/material-presets.ts +303 -0
  42. package/src/rendering/raycast.ts +2 -2
  43. package/src/rendering/room-environment.ts +192 -0
  44. package/src/rendering/studio-composer.ts +577 -0
  45. package/src/rendering/studio-floor.ts +108 -0
  46. package/src/rendering/texture-cache.ts +1020 -0
  47. package/src/rendering/triplanar.ts +329 -0
  48. package/src/scene/animation.ts +3 -2
  49. package/src/scene/clipping.ts +59 -0
  50. package/src/scene/nestedgroup.ts +399 -0
  51. package/src/scene/objectgroup.ts +186 -11
  52. package/src/scene/orientation.ts +12 -0
  53. package/src/scene/render-shape.ts +55 -21
  54. package/src/types/n8ao.d.ts +28 -0
  55. package/src/ui/display.ts +1032 -27
  56. package/src/ui/index.html +181 -44
  57. package/src/utils/decode-instances.ts +233 -0
  58. package/src/utils/utils.ts +33 -20
@@ -19,6 +19,7 @@ export interface DisplayOptions {
19
19
  explodeTool: boolean;
20
20
  zscaleTool: boolean;
21
21
  zebraTool: boolean;
22
+ studioTool: boolean;
22
23
  glass: boolean;
23
24
  tools: boolean;
24
25
  cadWidth: number;
@@ -27,6 +28,8 @@ export interface DisplayOptions {
27
28
  treeHeight?: number;
28
29
  theme: ThemeInput;
29
30
  pinning: boolean;
31
+ canvas?: HTMLCanvasElement;
32
+ gl?: WebGLRenderingContext | WebGL2RenderingContext;
30
33
  }
31
34
  /**
32
35
  * Options for setSizes method
@@ -100,17 +103,33 @@ declare class Display {
100
103
  cadTree: HTMLElement;
101
104
  cadTreeScrollContainer: HTMLElement;
102
105
  cadTreeToggles: HTMLElement;
106
+ cadClipToggles: HTMLElement;
107
+ cadMaterialToggles: HTMLElement;
108
+ cadZebraToggles: HTMLElement;
109
+ cadStudioToggles: HTMLElement;
103
110
  cadClip: HTMLElement;
104
111
  cadMaterial: HTMLElement;
105
112
  cadZebra: HTMLElement;
113
+ cadStudio: HTMLElement;
114
+ private _spinnerEl;
115
+ private _warningBannerEl;
116
+ private _warningBannerTimer;
117
+ private _spinnerCount;
118
+ private _matEditorPath;
119
+ private _matEditorClones;
120
+ private _savedMatEditorChanges;
121
+ private _matEditorDragAbort;
122
+ private _matEditorInputAbort;
106
123
  cadInfo: HTMLElement;
107
124
  cadAnim: HTMLElement;
125
+ private _animWasVisible;
108
126
  cadTools: HTMLElement;
109
127
  cadHelp: HTMLElement;
110
128
  tabTree: HTMLElement;
111
129
  tabClip: HTMLElement;
112
130
  tabMaterial: HTMLElement;
113
131
  tabZebra: HTMLElement;
132
+ tabStudio: HTMLElement;
114
133
  tickValueElement: HTMLElement;
115
134
  tickInfoElement: HTMLElement;
116
135
  distanceMeasurementPanel: HTMLElement;
@@ -130,6 +149,12 @@ declare class Display {
130
149
  zebraCountSlider: Slider | undefined;
131
150
  zebraOpacitySlider: Slider | undefined;
132
151
  zebraDirectionSlider: Slider | undefined;
152
+ studioEnvIntensitySlider: Slider | undefined;
153
+ studioExposureSlider: Slider | undefined;
154
+ studioEnvRotationSlider: Slider | undefined;
155
+ studioShadowIntensitySlider: Slider | undefined;
156
+ studioShadowSoftnessSlider: Slider | undefined;
157
+ studioAOIntensitySlider: Slider | undefined;
133
158
  viewer: Viewer;
134
159
  state: ViewerState;
135
160
  measureTools: boolean;
@@ -147,6 +172,7 @@ declare class Display {
147
172
  lastPlaneState: boolean;
148
173
  help_shown: boolean;
149
174
  info_shown: boolean;
175
+ tools_shown: boolean;
150
176
  _info: Info;
151
177
  _events: StoredEvent[];
152
178
  _unsubscribers: (() => void)[];
@@ -174,6 +200,10 @@ declare class Display {
174
200
  private setupCheckEvent;
175
201
  private setupClickEvent;
176
202
  private setupRadioEvent;
203
+ /**
204
+ * Wire a select element's change event
205
+ */
206
+ private setupSelectEvent;
177
207
  /**
178
208
  * Get a DOM element by class name (internal use only).
179
209
  * @param name - Name of the DOM element class
@@ -346,6 +376,19 @@ declare class Display {
346
376
  * Show or hides ZScale tool
347
377
  */
348
378
  showZScaleTool: (flag: boolean) => void;
379
+ /**
380
+ * Deactivate any running tool and hide tool buttons for Studio mode.
381
+ * Called when entering Studio mode.
382
+ * @internal
383
+ */
384
+ private _deactivateToolsForStudio;
385
+ /**
386
+ * Restore tool button visibility after leaving Studio mode.
387
+ * Respects original feature flags (measureTools, selectTool).
388
+ * Does not auto-activate any tool.
389
+ * @internal
390
+ */
391
+ private _restoreToolsAfterStudio;
349
392
  /**
350
393
  * Checkbox Handler for setting the clip planes parameter
351
394
  */
@@ -395,6 +438,14 @@ declare class Display {
395
438
  * Switch to a tab (internal, called by activeTab subscription).
396
439
  */
397
440
  private switchToTab;
441
+ /** Show the toolbar spinner (ref-counted for overlapping async ops). */
442
+ private _showSpinner;
443
+ /** Hide the toolbar spinner (only when all pending ops complete). */
444
+ private _hideSpinner;
445
+ /** Show a warning banner in the viewport. Auto-hides after 8 seconds. */
446
+ private _showWarningBanner;
447
+ /** Hide the warning banner. */
448
+ private _hideWarningBanner;
398
449
  /**
399
450
  * Toggle visibility of the clipping tab
400
451
  */
@@ -404,10 +455,82 @@ declare class Display {
404
455
  * Translates button codes to CollapseState and calls viewer
405
456
  */
406
457
  handleCollapseNodes: (e: Event) => void;
458
+ /**
459
+ * Reset clip planes to default normals and slider positions
460
+ */
461
+ handleClipReset: (_e: Event) => void;
407
462
  /**
408
463
  * Reset material values to original values
409
464
  */
410
465
  handleMaterialReset: (_e: Event) => void;
466
+ /**
467
+ * Reset zebra tool to default settings
468
+ */
469
+ handleZebraReset: (_e: Event) => void;
470
+ /**
471
+ * Handler for Studio environment dropdown change
472
+ */
473
+ handleStudioEnvironment: (e: Event) => void;
474
+ /**
475
+ * Handler for Studio env intensity slider change.
476
+ * Slider range 0-200 with percentage=true, so value arrives as 0-2.
477
+ */
478
+ handleStudioEnvIntensity: (value: number) => void;
479
+ handleStudioEnvRotation: (value: number) => void;
480
+ /**
481
+ * Handler for Studio background dropdown change.
482
+ * Validates against the StudioBackground union before setting state.
483
+ */
484
+ handleStudioBackground: (e: Event) => void;
485
+ /**
486
+ * Handler for Studio tone mapping dropdown change.
487
+ * Validates against the StudioToneMapping union before setting state.
488
+ */
489
+ handleStudioToneMapping: (e: Event) => void;
490
+ /**
491
+ * Handler for Studio exposure slider change.
492
+ * Slider range 0-200 with percentage=true, so value arrives as 0-2.
493
+ */
494
+ handleStudioTextureMapping: (e: Event) => void;
495
+ handleStudioExposure: (value: number) => void;
496
+ handleStudioShadowIntensity: (value: number) => void;
497
+ handleStudioShadowSoftness: (value: number) => void;
498
+ /**
499
+ * Handler for Studio AO intensity slider change.
500
+ * Slider range 0-30, divided by 10 → state gets 0-3.0.
501
+ * A value of 0 disables AO.
502
+ */
503
+ handleStudioAOIntensity: (value: number) => void;
504
+ /**
505
+ * Handler for Studio 4K env maps checkbox change.
506
+ * Shows a "Loading…" indicator while the new resolution downloads.
507
+ */
508
+ handleStudio4kEnvMaps: (e: Event) => void;
509
+ /**
510
+ * Reset Studio tab values to defaults.
511
+ * Delegates to viewer.resetStudio() (same pattern as handleMaterialReset -> resetMaterial()).
512
+ */
513
+ handleStudioReset: (_e: Event) => void;
514
+ handleMatEditorToggle: (_e: Event) => void;
515
+ private _showMatEditorHint;
516
+ private openMatEditor;
517
+ closeMatEditor(): void;
518
+ /** Dispose all cloned materials, restoring originals first (call on Studio mode exit) */
519
+ disposeMatEditorClones(): void;
520
+ /** Save material editor property deltas so they survive a Studio mode leave/enter cycle. */
521
+ private _saveMatEditorChanges;
522
+ /** Reapply saved material editor changes after re-entering Studio mode. */
523
+ private _reapplyMatEditorChanges;
524
+ /**
525
+ * Called by viewer.ts when the selected object changes.
526
+ * Updates or closes the material editor if it's open.
527
+ */
528
+ onSelectionChanged(newObjectId: string | null): void;
529
+ handleMatEditorReset: (_e: Event) => void;
530
+ /** Make the material editor dialog draggable by its titlebar */
531
+ private _initMatEditorDrag;
532
+ private _buildMatEditorContent;
533
+ private _buildMatEditorRow;
411
534
  /**
412
535
  * Set zebra stripe count in the UI
413
536
  */
@@ -456,6 +579,21 @@ declare class Display {
456
579
  * Called from updateUI after viewer options are applied to state.
457
580
  */
458
581
  syncZebraSlidersFromState(): void;
582
+ /**
583
+ * Sync Studio slider/control UI from current state values.
584
+ */
585
+ syncStudioSlidersFromState(): void;
586
+ /**
587
+ * Ensure the environment dropdown can display a custom HDR URL.
588
+ * If envName isn't already an option, adds a "Custom HDR" entry.
589
+ * Removes stale custom entries when switching back to a built-in preset.
590
+ */
591
+ private _syncEnvDropdown;
592
+ /**
593
+ * Enable/disable the 4K checkbox based on whether the current environment
594
+ * is a Poly Haven preset (resolution-switchable) or a custom URL / "studio".
595
+ */
596
+ private _update4kCheckboxEnabled;
459
597
  /**
460
598
  * Refresh clipping plane position
461
599
  */
@@ -517,6 +655,15 @@ declare class Display {
517
655
  * Toggle info dialog visibility
518
656
  */
519
657
  toggleInfo: () => void;
658
+ /**
659
+ * Show or hide tools panel (tabs + content) in glass mode.
660
+ * Also toggles the orientation marker and animation/explode slider.
661
+ */
662
+ showToolsPanel: (flag: boolean) => void;
663
+ /**
664
+ * Toggle tools panel visibility
665
+ */
666
+ toggleToolsPanel: () => void;
520
667
  /**
521
668
  * Auto collapse tree nodes when cad width < 600
522
669
  */
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Decode the compressed/instanced shape format.
3
+ *
4
+ * The instanced format wraps a standard Shapes object:
5
+ * ```
6
+ * { instances: [ { vertices: {shape,dtype,buffer,codec}, ... }, ... ],
7
+ * shapes: { version, parts, loc, name, id, bb, ... } }
8
+ * ```
9
+ *
10
+ * Each instance contains base64-encoded geometry buffers. Parts reference
11
+ * instances via `"shape": { "ref": N }`. After decoding, the result is a
12
+ * standard Shapes tree with TypedArrays — identical to the existing format.
13
+ */
14
+ import type { Shapes } from "../core/types.js";
15
+ /** A single base64-encoded buffer entry. */
16
+ interface EncodedBuffer {
17
+ shape: number[];
18
+ dtype: "float32" | "int32" | "uint32";
19
+ buffer: string;
20
+ codec: "b64";
21
+ }
22
+ /** An encoded geometry instance (all 9 buffer fields + optional uvs). */
23
+ interface EncodedInstance {
24
+ vertices: EncodedBuffer;
25
+ triangles: EncodedBuffer;
26
+ normals: EncodedBuffer;
27
+ edges: EncodedBuffer;
28
+ obj_vertices: EncodedBuffer;
29
+ face_types: EncodedBuffer;
30
+ edge_types: EncodedBuffer;
31
+ triangles_per_face: EncodedBuffer;
32
+ segments_per_edge: EncodedBuffer;
33
+ uvs?: EncodedBuffer;
34
+ }
35
+ /** Top-level structure of the instanced format. */
36
+ interface InstancedData {
37
+ instances: EncodedInstance[];
38
+ shapes: Shapes;
39
+ }
40
+ /**
41
+ * Walk the shapes tree and decode any inline encoded buffers found in
42
+ * part.shape objects. This handles edge/vertex-only objects that embed
43
+ * encoded buffers directly (not via instance refs).
44
+ */
45
+ declare function decodeInlineBuffers(shapes: Shapes): void;
46
+ /**
47
+ * Type guard: check if data is in the instanced format.
48
+ * Detected by the presence of an `instances` array and a `shapes` object.
49
+ */
50
+ declare function isInstancedFormat(data: unknown): data is InstancedData;
51
+ /**
52
+ * Decode the instanced format into a standard Shapes object.
53
+ *
54
+ * 1. Decode all instance buffers from base64 → TypedArrays
55
+ * 2. Walk the shapes tree and replace { ref: N } with decoded instances
56
+ * 3. Return the unwrapped Shapes object
57
+ */
58
+ declare function decodeInstancedFormat(data: InstancedData): Shapes;
59
+ export { isInstancedFormat, decodeInstancedFormat, decodeInlineBuffers };
60
+ export type { InstancedData, EncodedBuffer, EncodedInstance };
@@ -40,6 +40,16 @@ interface MaterialLike {
40
40
  emissiveMap?: Disposable | null;
41
41
  alphaMap?: Disposable | null;
42
42
  bumpMap?: Disposable | null;
43
+ transmissionMap?: Disposable | null;
44
+ clearcoatMap?: Disposable | null;
45
+ clearcoatRoughnessMap?: Disposable | null;
46
+ clearcoatNormalMap?: Disposable | null;
47
+ thicknessMap?: Disposable | null;
48
+ specularIntensityMap?: Disposable | null;
49
+ specularColorMap?: Disposable | null;
50
+ sheenColorMap?: Disposable | null;
51
+ sheenRoughnessMap?: Disposable | null;
52
+ anisotropyMap?: Disposable | null;
43
53
  }
44
54
  interface MeshLike {
45
55
  geometry?: GeometryLike | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "three-cad-viewer",
3
- "version": "4.1.2",
3
+ "version": "4.2.0",
4
4
  "type": "module",
5
5
  "description": "WebGL-based CAD viewer built on Three.js with clipping planes, measurement tools, and tree navigation",
6
6
  "repository": {
@@ -49,7 +49,9 @@
49
49
  "author": "Bernhard Walter",
50
50
  "license": "MIT",
51
51
  "dependencies": {
52
- "three": "0.180.0"
52
+ "three": "0.183.0",
53
+ "n8ao": "1.10.1",
54
+ "postprocessing": "6.38.3"
53
55
  },
54
56
  "devDependencies": {
55
57
  "@babel/core": "^7.28.0",
package/src/_version.ts CHANGED
@@ -1 +1 @@
1
- export const version: string = "4.1.2";
1
+ export const version: string = "4.2.0";
@@ -73,6 +73,14 @@ const cameraUp: Record<UpMode, [number, number, number]> = {
73
73
  class Camera {
74
74
  private static readonly DISTANCE_FACTOR = 5;
75
75
 
76
+ /**
77
+ * Near plane factor: near = max(0.1, NEAR_FACTOR * distance).
78
+ * With far = 100 * distance, this gives a far/near ratio of 10,000:1,
79
+ * which is comfortable for 24-bit depth buffers and avoids z-fighting
80
+ * on large models (e.g. toycar at ~1100 unit bounding radius).
81
+ */
82
+ private static readonly NEAR_FACTOR = 0.01;
83
+
76
84
  target: THREE.Vector3;
77
85
  ortho: boolean;
78
86
  up: UpMode;
@@ -83,6 +91,14 @@ class Camera {
83
91
  oCamera!: THREE.OrthographicCamera; // Initialized in constructor
84
92
  camera!: THREE.PerspectiveCamera | THREE.OrthographicCamera; // Set in constructor
85
93
 
94
+ /**
95
+ * Compute the near clipping plane from the bounding radius.
96
+ * Keeps the far/near ratio bounded for depth buffer precision.
97
+ */
98
+ private static _computeNear(distance: number): number {
99
+ return Math.max(0.1, Camera.NEAR_FACTOR * distance);
100
+ }
101
+
86
102
  /**
87
103
  * Create a combined camera (orthographic and perspective).
88
104
  * @param width - canvas width.
@@ -120,12 +136,10 @@ class Camera {
120
136
 
121
137
  this.camera_distance = Camera.DISTANCE_FACTOR * distance;
122
138
 
123
- this.pCamera = new THREE.PerspectiveCamera(
124
- fov,
125
- aspect,
126
- 0.1,
127
- 100 * distance,
128
- );
139
+ const near = Camera._computeNear(distance);
140
+ const far = 100 * distance;
141
+
142
+ this.pCamera = new THREE.PerspectiveCamera(fov, aspect, near, far);
129
143
  this.pCamera.up.set(...cameraUp[this.up]);
130
144
  this.pCamera.lookAt(this.target);
131
145
 
@@ -137,8 +151,8 @@ class Camera {
137
151
  pSize[0],
138
152
  pSize[1],
139
153
  -pSize[1],
140
- 0.1,
141
- 100 * distance,
154
+ near,
155
+ far,
142
156
  );
143
157
  this.oCamera.up.set(...cameraUp[this.up]);
144
158
  this.oCamera.lookAt(this.target);
@@ -148,12 +162,15 @@ class Camera {
148
162
  }
149
163
 
150
164
  /**
151
- * Update the far clipping plane for both cameras.
152
- * @param distance - The new bounding radius to base the far plane on.
165
+ * Update the near/far clipping planes for both cameras.
166
+ * @param distance - The new bounding radius to base the clipping planes on.
153
167
  */
154
168
  updateFarPlane(distance: number): void {
169
+ const near = Camera._computeNear(distance);
155
170
  const far = 100 * distance;
171
+ this.pCamera.near = near;
156
172
  this.pCamera.far = far;
173
+ this.oCamera.near = near;
157
174
  this.oCamera.far = far;
158
175
  this.camera.updateProjectionMatrix();
159
176
  }