@selvajs/visualization 1.0.0-beta.1 → 1.0.0-beta.3

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 (43) hide show
  1. package/README.md +7 -7
  2. package/dist/gpu-dispose-Dkj-BKt4.js +2 -0
  3. package/dist/gpu-dispose-Dkj-BKt4.js.map +1 -0
  4. package/dist/gpu-dispose-DrN-ryj5.cjs +2 -0
  5. package/dist/gpu-dispose-DrN-ryj5.cjs.map +1 -0
  6. package/dist/index.cjs +0 -1
  7. package/dist/index.d.cts +1 -2
  8. package/dist/index.d.ts +1 -2
  9. package/dist/index.js +1 -1
  10. package/dist/parse.cjs +3 -3
  11. package/dist/parse.cjs.map +1 -1
  12. package/dist/parse.d.cts +117 -110
  13. package/dist/parse.d.ts +117 -110
  14. package/dist/parse.js +3 -3
  15. package/dist/parse.js.map +1 -1
  16. package/dist/render.cjs +5 -6
  17. package/dist/render.cjs.map +1 -1
  18. package/dist/render.d.cts +441 -312
  19. package/dist/render.d.ts +441 -312
  20. package/dist/render.js +5 -6
  21. package/dist/render.js.map +1 -1
  22. package/dist/rolldown-runtime-BocRIvOZ.cjs +1 -0
  23. package/dist/scene.cjs +1 -1
  24. package/dist/scene.cjs.map +1 -1
  25. package/dist/scene.d.cts +94 -89
  26. package/dist/scene.d.ts +94 -89
  27. package/dist/scene.js +1 -1
  28. package/dist/scene.js.map +1 -1
  29. package/dist/types-Di80Y609.d.cts +34 -0
  30. package/dist/types-Di80Y609.d.ts +34 -0
  31. package/package.json +13 -8
  32. package/dist/chunk-5XGN7UAV.js +0 -2
  33. package/dist/chunk-5XGN7UAV.js.map +0 -1
  34. package/dist/chunk-AZ4GBXXL.cjs +0 -2
  35. package/dist/chunk-AZ4GBXXL.cjs.map +0 -1
  36. package/dist/chunk-BYLIBOAU.cjs +0 -2
  37. package/dist/chunk-BYLIBOAU.cjs.map +0 -1
  38. package/dist/chunk-KRA5RVHM.js +0 -2
  39. package/dist/chunk-KRA5RVHM.js.map +0 -1
  40. package/dist/index.cjs.map +0 -1
  41. package/dist/index.js.map +0 -1
  42. package/dist/types-DCuos3gI.d.cts +0 -33
  43. package/dist/types-DCuos3gI.d.ts +0 -33
package/dist/render.d.ts CHANGED
@@ -1,50 +1,52 @@
1
- import * as THREE from 'three';
2
- import { L as Look, a as LookPreset, M as MaterialAppearanceOptions } from './types-DCuos3gI.js';
3
- export { b as LOOKS } from './types-DCuos3gI.js';
4
- import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
5
-
1
+ import { i as MaterialAppearanceOptions, n as Look, r as LookPreset, t as LOOKS } from "./types-Di80Y609.js";
2
+ import * as THREE from "three";
3
+ import { CSS2DObject } from "three/addons/renderers/CSS2DRenderer.js";
4
+ import { OrbitControls } from "three/addons/controls/OrbitControls.js";
5
+ //#region src/shared/errors.d.ts
6
6
  /**
7
7
  * Errors for the visualization package. Replaces `@selvajs/compute`'s `RhinoComputeError`, which
8
8
  * mis-named failures on paths (e.g. the plugin WebSocket) that never touch Rhino.Compute. `code`
9
9
  * values match compute's so existing catch-sites keep working.
10
10
  */
11
11
  declare const ErrorCodes: {
12
- /** Structural check failed: bad magic bytes, out-of-window index, malformed metadata. */
13
- readonly VALIDATION_ERROR: "VALIDATION_ERROR";
14
- readonly INVALID_STATE: "INVALID_STATE";
15
- /** No `DecompressionStream`, no WebGL context, etc. */
16
- readonly ENVIRONMENT_ERROR: "ENVIRONMENT_ERROR";
17
- readonly INVALID_CONFIG: "INVALID_CONFIG";
18
- /** Base64 input could not be decoded. */
19
- readonly ENCODING_ERROR: "ENCODING_ERROR";
20
- readonly UNKNOWN_ERROR: "UNKNOWN_ERROR";
12
+ /** Structural check failed: bad magic bytes, out-of-window index, malformed metadata. */
13
+ readonly VALIDATION_ERROR: "VALIDATION_ERROR";
14
+ readonly INVALID_STATE: "INVALID_STATE";
15
+ /** No `DecompressionStream`, no WebGL context, etc. */
16
+ readonly ENVIRONMENT_ERROR: "ENVIRONMENT_ERROR";
17
+ readonly INVALID_CONFIG: "INVALID_CONFIG";
18
+ /** Base64 input could not be decoded. */
19
+ readonly ENCODING_ERROR: "ENCODING_ERROR";
20
+ readonly UNKNOWN_ERROR: "UNKNOWN_ERROR";
21
21
  };
22
22
  type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];
23
23
  declare class VisualizationError extends Error {
24
- readonly code: ErrorCode;
25
- readonly context?: Record<string, unknown>;
26
- readonly originalError?: Error;
27
- constructor(message: string, code?: ErrorCode, options?: {
28
- context?: Record<string, unknown>;
29
- originalError?: Error;
30
- });
24
+ readonly code: ErrorCode;
25
+ readonly context?: Record<string, unknown>;
26
+ readonly originalError?: Error;
27
+ constructor(message: string, code?: ErrorCode, options?: {
28
+ context?: Record<string, unknown>;
29
+ originalError?: Error;
30
+ });
31
31
  }
32
-
32
+ //#endregion
33
+ //#region src/shared/logger.d.ts
33
34
  /**
34
35
  * Logging facility for the visualization package. Deliberately local rather than imported from
35
36
  * `@selvajs/compute` (logging isn't a compute concern). Mirrors compute's logger shape so a host
36
37
  * wanting one sink for both can call `setLogger(computeLogger.getLogger())`.
37
38
  */
38
39
  interface Logger {
39
- debug(message: string, ...args: unknown[]): void;
40
- info(message: string, ...args: unknown[]): void;
41
- warn(message: string, ...args: unknown[]): void;
42
- error(message: string, ...args: unknown[]): void;
40
+ debug(message: string, ...args: unknown[]): void;
41
+ info(message: string, ...args: unknown[]): void;
42
+ warn(message: string, ...args: unknown[]): void;
43
+ error(message: string, ...args: unknown[]): void;
43
44
  }
44
45
  declare function getLogger(): Logger;
45
46
  declare function setLogger(logger: Logger | Console | null): void;
46
47
  declare function enableDebugLogging(): void;
47
-
48
+ //#endregion
49
+ //#region src/shared/looks.d.ts
48
50
  /** The look applied when the caller passes no `look` option. */
49
51
  declare const DEFAULT_LOOK: Look;
50
52
  /**
@@ -57,205 +59,206 @@ declare const DEFAULT_LOOK: Look;
57
59
  declare const LOOK_PRESETS: Record<Look, LookPreset>;
58
60
  /** Baked at parse time (not toggleable at runtime). */
59
61
  declare function materialAppearanceForLook(look: Look): MaterialAppearanceOptions;
60
-
62
+ //#endregion
63
+ //#region src/render/types.d.ts
61
64
  type CameraConfig = {
62
- position?: THREE.Vector3;
63
- fov?: number;
64
- near?: number;
65
- far?: number;
66
- target?: THREE.Vector3;
67
- /**
68
- * Refit the near plane to the camera↔content gap every frame (default true) — recovers
69
- * depth-buffer precision when zoomed out, preventing distant z-fighting. `near` is only ever
70
- * raised, never lowered below the configured value.
71
- */
72
- dynamicNear?: boolean;
65
+ position?: THREE.Vector3;
66
+ fov?: number;
67
+ near?: number;
68
+ far?: number;
69
+ target?: THREE.Vector3;
70
+ /**
71
+ * Refit the near plane to the camera↔content gap every frame (default true) — recovers
72
+ * depth-buffer precision when zoomed out, preventing distant z-fighting. `near` is only ever
73
+ * raised, never lowered below the configured value.
74
+ */
75
+ dynamicNear?: boolean;
73
76
  };
74
77
  type LightingConfig = {
75
- enableSunlight?: boolean;
76
- sunlightIntensity?: number;
77
- sunlightPosition?: THREE.Vector3;
78
- ambientLightColor?: THREE.Color;
79
- ambientLightIntensity?: number;
80
- sunlightColor?: THREE.Color | number;
81
- /**
82
- * Direction-aware fill (sky color above, ground color below) so surfaces facing away from the
83
- * sun don't collapse to black under a dark HDR. Default false — enabling it shifts the look.
84
- */
85
- enableHemisphereLight?: boolean;
86
- /** Default white. */
87
- hemisphereSkyColor?: THREE.Color | number;
88
- /** Default a mid grey. */
89
- hemisphereGroundColor?: THREE.Color | number;
90
- /** Default 0.6. Only applies when {@link LightingConfig.enableHemisphereLight}. */
91
- hemisphereIntensity?: number;
78
+ enableSunlight?: boolean;
79
+ sunlightIntensity?: number;
80
+ sunlightPosition?: THREE.Vector3;
81
+ ambientLightColor?: THREE.Color;
82
+ ambientLightIntensity?: number;
83
+ sunlightColor?: THREE.Color | number;
84
+ /**
85
+ * Direction-aware fill (sky color above, ground color below) so surfaces facing away from the
86
+ * sun don't collapse to black under a dark HDR. Default false — enabling it shifts the look.
87
+ */
88
+ enableHemisphereLight?: boolean;
89
+ /** Default white. */
90
+ hemisphereSkyColor?: THREE.Color | number;
91
+ /** Default a mid grey. */
92
+ hemisphereGroundColor?: THREE.Color | number;
93
+ /** Default 0.6. Only applies when {@link LightingConfig.enableHemisphereLight}. */
94
+ hemisphereIntensity?: number;
92
95
  };
93
96
  type EnvironmentConfig = {
94
- hdrPath?: string;
95
- backgroundColor?: THREE.Color | string;
96
- enableEnvironmentLighting?: boolean;
97
- /**
98
- * Default `(0, 0, 1)` — Rhino's Z-up. Every orientation default derives from this (see `up-axis.ts`);
99
- * overriding it reorients the viewer only, it does not rotate incoming geometry.
100
- */
101
- sceneUp?: THREE.Vector3;
102
- showEnvironment?: boolean;
103
- /**
104
- * Multiplier on the HDR's image-based lighting contribution — normalizes brightness across HDRs
105
- * of differing exposure. Default 1 (unchanged look).
106
- */
107
- environmentIntensity?: number;
97
+ hdrPath?: string;
98
+ backgroundColor?: THREE.Color | string;
99
+ enableEnvironmentLighting?: boolean;
100
+ /**
101
+ * Default `(0, 0, 1)` — Rhino's Z-up. Every orientation default derives from this (see `up-axis.ts`);
102
+ * overriding it reorients the viewer only, it does not rotate incoming geometry.
103
+ */
104
+ sceneUp?: THREE.Vector3;
105
+ showEnvironment?: boolean;
106
+ /**
107
+ * Multiplier on the HDR's image-based lighting contribution — normalizes brightness across HDRs
108
+ * of differing exposure. Default 1 (unchanged look).
109
+ */
110
+ environmentIntensity?: number;
108
111
  };
109
112
  type FloorConfig = {
110
- enabled?: boolean;
111
- size?: number;
112
- color?: THREE.Color | string;
113
- roughness?: number;
114
- metalness?: number;
115
- receiveShadow?: boolean;
113
+ enabled?: boolean;
114
+ size?: number;
115
+ color?: THREE.Color | string;
116
+ roughness?: number;
117
+ metalness?: number;
118
+ receiveShadow?: boolean;
116
119
  };
117
120
  type RenderConfig = {
118
- enableShadows?: boolean;
119
- shadowMapSize?: number;
120
- antialias?: boolean;
121
- pixelRatio?: number;
122
- toneMapping?: THREE.ToneMapping;
123
- toneMappingExposure?: number;
124
- preserveDrawingBuffer?: boolean;
125
- /** Default false — switches rendering from `renderer.render` to an EffectComposer, which costs more. */
126
- ambientOcclusion?: boolean;
127
- /** AO strength 0–1 when {@link RenderConfig.ambientOcclusion} is on. Default 1. */
128
- aoIntensity?: number;
129
- /**
130
- * DPR cap for AO buffers — AO is low-frequency, so sampling below display DPR is nearly invisible
131
- * but much cheaper (a DPR-2 display would otherwise push 4× the pixels through GTAO's per-pixel
132
- * sample loop). Default 1; only relevant when AO is enabled.
133
- */
134
- aoPixelRatio?: number;
135
- /**
136
- * Render only on change (camera motion, invalidate(), pointer input, resize) plus a ~500ms safety
137
- * repaint, instead of every frame. Default true — cuts idle GPU/battery use. Set false to restore
138
- * a continuous loop.
139
- */
140
- onDemand?: boolean;
121
+ enableShadows?: boolean;
122
+ shadowMapSize?: number;
123
+ antialias?: boolean;
124
+ pixelRatio?: number;
125
+ toneMapping?: THREE.ToneMapping;
126
+ toneMappingExposure?: number;
127
+ preserveDrawingBuffer?: boolean;
128
+ /** Default false — switches rendering from `renderer.render` to an EffectComposer, which costs more. */
129
+ ambientOcclusion?: boolean;
130
+ /** AO strength 0–1 when {@link RenderConfig.ambientOcclusion} is on. Default 1. */
131
+ aoIntensity?: number;
132
+ /**
133
+ * DPR cap for AO buffers — AO is low-frequency, so sampling below display DPR is nearly invisible
134
+ * but much cheaper (a DPR-2 display would otherwise push 4× the pixels through GTAO's per-pixel
135
+ * sample loop). Default 1; only relevant when AO is enabled.
136
+ */
137
+ aoPixelRatio?: number;
138
+ /**
139
+ * Render only on change (camera motion, invalidate(), pointer input, resize) plus a ~500ms safety
140
+ * repaint, instead of every frame. Default true — cuts idle GPU/battery use. Set false to restore
141
+ * a continuous loop.
142
+ */
143
+ onDemand?: boolean;
141
144
  };
142
-
143
145
  /** Crisp boundary/crease edge overlays on meshes. Field rationale: see `EdgeOptions` in `edges/options.ts`. */
144
146
  type EdgesConfig = {
145
- /** Default false (opt-in). */
146
- enabled?: boolean;
147
- color?: THREE.ColorRepresentation;
148
- /** 0–1, default 0.75. Ignored when `color` is set. */
149
- darken?: number;
150
- /** CSS px. Default 1.5. */
151
- width?: number;
152
- /** Degrees. Default 44. */
153
- thresholdAngle?: number;
154
- /** Default true. */
155
- distanceFade?: boolean;
156
- /** Default 4M. */
157
- maxTriangles?: number;
158
- /** Default 2M. */
159
- maxSegments?: number;
160
- /** Fall back to the screen-space edge-detection pass for meshes skipped by `maxTriangles`. Default true. */
161
- screenSpaceFallback?: boolean;
147
+ /** Default false (opt-in). */
148
+ enabled?: boolean;
149
+ color?: THREE.ColorRepresentation;
150
+ /** 0–1, default 0.75. Ignored when `color` is set. */
151
+ darken?: number;
152
+ /** CSS px. Default 1.5. */
153
+ width?: number;
154
+ /** Degrees. Default 44. */
155
+ thresholdAngle?: number;
156
+ /** Default true. */
157
+ distanceFade?: boolean;
158
+ /** Default 4M. */
159
+ maxTriangles?: number;
160
+ /** Default 2M. */
161
+ maxSegments?: number;
162
+ /** Fall back to the screen-space edge-detection pass for meshes skipped by `maxTriangles`. Default true. */
163
+ screenSpaceFallback?: boolean;
162
164
  };
163
165
  type ControlsConfig = {
164
- enableDamping?: boolean;
165
- dampingFactor?: number;
166
- autoRotate?: boolean;
167
- autoRotateSpeed?: number;
168
- enableZoom?: boolean;
169
- enablePan?: boolean;
170
- minDistance?: number;
171
- maxDistance?: number;
166
+ enableDamping?: boolean;
167
+ dampingFactor?: number;
168
+ autoRotate?: boolean;
169
+ autoRotateSpeed?: number;
170
+ enableZoom?: boolean;
171
+ enablePan?: boolean;
172
+ minDistance?: number;
173
+ maxDistance?: number;
172
174
  };
173
175
  /** Infinite distance-fading reference grid. See `createGrid`. */
174
176
  type GridConfig = {
175
- /** Default false (opt-in). */
176
- enabled?: boolean;
177
- /** World units (meters). Default 1. */
178
- cellSize?: number;
179
- /** Minor cells per major line. Default 10. */
180
- majorEvery?: number;
181
- cellColor?: THREE.ColorRepresentation;
182
- majorColor?: THREE.ColorRepresentation;
183
- /** World radius at which the grid fully fades. Default 100. */
184
- fadeDistance?: number;
185
- /**
186
- * Axis the grid lies perpendicular to. Defaults to whichever axis `sceneUp` points along
187
- * (`'z'` unless `sceneUp` is overridden); set explicitly to force an orientation that ignores it.
188
- */
189
- plane?: 'x' | 'y' | 'z';
177
+ /** Default false (opt-in). */
178
+ enabled?: boolean;
179
+ /** World units (meters). Default 1. */
180
+ cellSize?: number;
181
+ /** Minor cells per major line. Default 10. */
182
+ majorEvery?: number;
183
+ cellColor?: THREE.ColorRepresentation;
184
+ majorColor?: THREE.ColorRepresentation;
185
+ /** World radius at which the grid fully fades. Default 100. */
186
+ fadeDistance?: number;
187
+ /**
188
+ * Axis the grid lies perpendicular to. Defaults to whichever axis `sceneUp` points along
189
+ * (`'z'` unless `sceneUp` is overridden); set explicitly to force an orientation that ignores it.
190
+ */
191
+ plane?: 'x' | 'y' | 'z';
190
192
  };
191
193
  /** Corner nav-cube/axis gizmo that snaps to preset views. See `createViewGizmo`. */
192
194
  type GizmoConfig = {
193
- /** Default false (opt-in). */
194
- enabled?: boolean;
195
+ /** Default false (opt-in). */
196
+ enabled?: boolean;
195
197
  };
196
198
  /** Two-click distance measurement tool. See `createMeasureTool`. */
197
199
  type MeasureConfig = {
198
- /** Default false. Only builds the tool; start measuring via `measureTool.setEnabled(true)` on the init result. */
199
- enabled?: boolean;
200
- /** Snap to a vertex within this many screen px. Default 12. */
201
- snapPixels?: number;
202
- /** Default yellow. */
203
- color?: THREE.ColorRepresentation;
204
- /** CSS class for the distance label. */
205
- labelClassName?: string;
206
- /** Scene is in meters; pass the response's `modelunits` to convert the label (e.g. "25.0 mm"). Default meters. Ignored if `format` is set. */
207
- displayUnit?: string;
208
- /** Receives the straight-line `distance` and per-axis `delta`. Default renders the total plus a Δx/Δy/Δz breakdown. */
209
- format?: (distance: number, delta: THREE.Vector3) => string;
200
+ /** Default false. Only builds the tool; start measuring via `measureTool.setEnabled(true)` on the init result. */
201
+ enabled?: boolean;
202
+ /** Snap to a vertex within this many screen px. Default 12. */
203
+ snapPixels?: number;
204
+ /** Default yellow. */
205
+ color?: THREE.ColorRepresentation;
206
+ /** CSS class for the distance label. */
207
+ labelClassName?: string;
208
+ /** Scene is in meters; pass the response's `modelunits` to convert the label (e.g. "25.0 mm"). Default meters. Ignored if `format` is set. */
209
+ displayUnit?: string;
210
+ /** Receives the straight-line `distance` and per-axis `delta`. Default renders the total plus a Δx/Δy/Δz breakdown. */
211
+ format?: (distance: number, delta: THREE.Vector3) => string;
210
212
  };
211
213
  type ThreeInitializerOptions = {
212
- sceneScale?: 'mm' | 'cm' | 'm' | 'inches' | 'feet';
213
- /**
214
- * Seeds lighting/material defaults (tone mapping, AO, IBL strength, hemisphere fill); explicit
215
- * `lighting`/`environment`/`render` options still win. Does NOT touch edges/grid. Default
216
- * 'technical'. Re-apply later via the init result's `setLook`.
217
- */
218
- look?: Look;
219
- camera?: CameraConfig;
220
- lighting?: LightingConfig;
221
- environment?: EnvironmentConfig;
222
- floor?: FloorConfig;
223
- render?: RenderConfig;
224
- controls?: ControlsConfig;
225
- grid?: GridConfig;
226
- gizmo?: GizmoConfig;
227
- edges?: EdgesConfig;
228
- measure?: MeasureConfig;
229
- events?: EventConfig;
230
- /**
231
- * Called once at init with the GPU's max anisotropy. Not needed for sharp textures — `parse/`'s
232
- * texture cache subscribes to this value on its own (see README's render↔parse seam). Only for
233
- * hosts doing their own texture work.
234
- */
235
- onMaxAnisotropy?: (value: number) => void;
214
+ sceneScale?: 'mm' | 'cm' | 'm' | 'inches' | 'feet';
215
+ /**
216
+ * Seeds lighting/material defaults (tone mapping, AO, IBL strength, hemisphere fill); explicit
217
+ * `lighting`/`environment`/`render` options still win. Does NOT touch edges/grid. Default
218
+ * 'technical'. Re-apply later via the init result's `setLook`.
219
+ */
220
+ look?: Look;
221
+ camera?: CameraConfig;
222
+ lighting?: LightingConfig;
223
+ environment?: EnvironmentConfig;
224
+ floor?: FloorConfig;
225
+ render?: RenderConfig;
226
+ controls?: ControlsConfig;
227
+ grid?: GridConfig;
228
+ gizmo?: GizmoConfig;
229
+ edges?: EdgesConfig;
230
+ measure?: MeasureConfig;
231
+ events?: EventConfig;
232
+ /**
233
+ * Called once at init with the GPU's max anisotropy. Not needed for sharp textures — `parse/`'s
234
+ * texture cache subscribes to this value on its own (see README's render↔parse seam). Only for
235
+ * hosts doing their own texture work.
236
+ */
237
+ onMaxAnisotropy?: (value: number) => void;
236
238
  };
237
239
  type EventConfig = {
238
- onBackgroundClicked?: (event: {
239
- x: number;
240
- y: number;
241
- }) => void;
242
- onObjectSelected?: (object: THREE.Object3D) => void;
243
- /** Receives the clicked mesh's `userData`; only fires for meshes with non-empty `userData`. */
244
- onMeshMetadataClicked?: (metadata: Record<string, unknown>) => void;
245
- onMeshDoubleClicked?: (object: THREE.Object3D) => void;
246
- /** Default red (#ff0000). */
247
- selectionColor?: THREE.Color | string;
248
- /** Enable all event handlers (click/selection/metadata). Default true. */
249
- enableEventHandlers?: boolean;
250
- enableKeyboardControls?: boolean;
251
- enableClickToFocus?: boolean;
252
- /** Default true. */
253
- enableDoubleClickZoom?: boolean;
254
- onReady?: () => void;
255
- /** Fires every animation frame, after controls update and before render. */
256
- onFrame?: (delta: number) => void;
240
+ onBackgroundClicked?: (event: {
241
+ x: number;
242
+ y: number;
243
+ }) => void;
244
+ onObjectSelected?: (object: THREE.Object3D) => void;
245
+ /** Receives the clicked mesh's `userData`; only fires for meshes with non-empty `userData`. */
246
+ onMeshMetadataClicked?: (metadata: Record<string, unknown>) => void;
247
+ onMeshDoubleClicked?: (object: THREE.Object3D) => void;
248
+ /** Default red (#ff0000). */
249
+ selectionColor?: THREE.Color | string;
250
+ /** Enable all event handlers (click/selection/metadata). Default true. */
251
+ enableEventHandlers?: boolean;
252
+ enableKeyboardControls?: boolean;
253
+ enableClickToFocus?: boolean;
254
+ /** Default true. */
255
+ enableDoubleClickZoom?: boolean;
256
+ onReady?: () => void;
257
+ /** Fires every animation frame, after controls update and before render. */
258
+ onFrame?: (delta: number) => void;
257
259
  };
258
-
260
+ //#endregion
261
+ //#region src/render/camera-controller.d.ts
259
262
  /**
260
263
  * Runtime camera control: preset views, perspective⇄orthographic toggle, rotate lock.
261
264
  *
@@ -269,40 +272,57 @@ type EventConfig = {
269
272
  type ViewPreset = 'top' | 'bottom' | 'front' | 'back' | 'left' | 'right' | 'iso';
270
273
  type CameraProjection = 'perspective' | 'orthographic';
271
274
  interface CameraController {
272
- /** Swaps identity on {@link setProjection}. */
273
- getActiveCamera(): THREE.Camera;
274
- getProjection(): CameraProjection;
275
- setProjection(projection: CameraProjection): void;
276
- toggleProjection(): CameraProjection;
277
- setView(preset: ViewPreset, animate?: boolean): void;
278
- /**
279
- * Frame current content from an explicit world-space direction (target → camera) instead of a
280
- * named preset — used by the nav-cube, whose clicked axis is a world axis.
281
- */
282
- setViewDirection(direction: THREE.Vector3, animate?: boolean): void;
283
- /** Frame a world-space box from the current view direction. No-op on an empty box. */
284
- frameBounds(box: THREE.Box3, animate?: boolean): void;
285
- setRotateEnabled(enabled: boolean): void;
286
- isRotateEnabled(): boolean;
287
- updateAspect(width: number, height: number): void;
288
- /** Cancel any in-flight camera tween. Call on viewer teardown so ticks can't touch disposed controls. */
289
- dispose(): void;
275
+ /** Swaps identity on {@link setProjection}. */
276
+ getActiveCamera(): THREE.Camera;
277
+ getProjection(): CameraProjection;
278
+ setProjection(projection: CameraProjection): void;
279
+ toggleProjection(): CameraProjection;
280
+ setView(preset: ViewPreset, animate?: boolean): void;
281
+ /**
282
+ * Frame current content from an explicit world-space direction (target → camera) instead of a
283
+ * named preset — used by the nav-cube, whose clicked axis is a world axis.
284
+ */
285
+ setViewDirection(direction: THREE.Vector3, animate?: boolean): void;
286
+ /** Frame a world-space box from the current view direction. No-op on an empty box. */
287
+ frameBounds(box: THREE.Box3, animate?: boolean): void;
288
+ setRotateEnabled(enabled: boolean): void;
289
+ isRotateEnabled(): boolean;
290
+ updateAspect(width: number, height: number): void;
291
+ /** Cancel any in-flight camera tween. Call on viewer teardown so ticks can't touch disposed controls. */
292
+ dispose(): void;
290
293
  }
291
-
294
+ //#endregion
295
+ //#region src/render/grid.d.ts
292
296
  interface Grid {
293
- /** Tagged `userData.id = 'grid'` so pick/fit code skips it. */
294
- readonly object: THREE.Mesh;
295
- /** Re-centers the fade on the camera so the grid feels infinite as you move. Call per frame. */
296
- update(cameraPosition: THREE.Vector3): void;
297
- /**
298
- * Rescales cell spacing and fade radius to the content's extent, so a 3-unit or 3000-unit part
299
- * both get sensible cells. No-op for empty/degenerate bounds.
300
- */
301
- fitToContent(bounds: THREE.Box3): void;
302
- setVisible(visible: boolean): void;
303
- dispose(): void;
297
+ /** Tagged `userData.id = 'grid'` so pick/fit code skips it. */
298
+ readonly object: THREE.Mesh;
299
+ /** Re-centers the fade on the camera so the grid feels infinite as you move. Call per frame. */
300
+ update(cameraPosition: THREE.Vector3): void;
301
+ /**
302
+ * Rescales cell spacing and fade radius to the content's extent, so a 3-unit or 3000-unit part
303
+ * both get sensible cells. No-op for empty/degenerate bounds.
304
+ */
305
+ fitToContent(bounds: THREE.Box3): void;
306
+ setVisible(visible: boolean): void;
307
+ dispose(): void;
308
+ }
309
+ //#endregion
310
+ //#region src/render/label-layer.d.ts
311
+ interface LabelHandle {
312
+ readonly object: CSS2DObject;
313
+ setPosition(position: THREE.Vector3): void;
314
+ setText(text: string): void;
315
+ remove(): void;
304
316
  }
305
-
317
+ interface LabelLayer {
318
+ addLabel(text: string, position: THREE.Vector3, className?: string): LabelHandle;
319
+ /** Call each frame after the WebGL render, with the active camera. */
320
+ render(scene: THREE.Scene, camera: THREE.Camera): void;
321
+ setSize(width: number, height: number): void;
322
+ dispose(): void;
323
+ }
324
+ //#endregion
325
+ //#region src/render/measure.d.ts
306
326
  /**
307
327
  * Two-click distance measurement. Click a point, click a second, read the distance off a label on
308
328
  * the connecting line; a third click starts fresh.
@@ -316,16 +336,85 @@ interface Grid {
316
336
  * measuring doesn't also select objects.
317
337
  */
318
338
  interface MeasureTool {
319
- setEnabled(enabled: boolean): void;
320
- isEnabled(): boolean;
321
- /** Returns true if the tool consumed the click (caller should not also select). */
322
- handleClick(event: MouseEvent): boolean;
323
- /** Preview the next snap point via a ghost marker. No-op when disabled; never consumes the event. */
324
- handleMove(event: MouseEvent): void;
325
- clear(): void;
326
- dispose(): void;
339
+ setEnabled(enabled: boolean): void;
340
+ isEnabled(): boolean;
341
+ /** Returns true if the tool consumed the click (caller should not also select). */
342
+ handleClick(event: MouseEvent): boolean;
343
+ /** Preview the next snap point via a ghost marker. No-op when disabled; never consumes the event. */
344
+ handleMove(event: MouseEvent): void;
345
+ clear(): void;
346
+ dispose(): void;
347
+ }
348
+ /**
349
+ * Raycast threshold for picking lines/points, as a fixed fraction of view size so the grab band
350
+ * stays constant on screen while zooming. Perspective uses camera→target distance (see
351
+ * `MeasureDeps.getViewTarget`); orthographic uses frustum height `(top − bottom) / zoom`, since
352
+ * ortho zoom changes `camera.zoom` rather than position.
353
+ *
354
+ * Shared with any tool doing its own picking, so grab bands stay consistent across tools.
355
+ */
356
+ declare function pickThreshold(camera: THREE.Camera, viewTarget?: THREE.Vector3): number;
357
+ /** Snap a raycast hit to the nearest geometry vertex within `snapPixels` on screen, else the raw hit point. */
358
+ declare function snapToVertex(hit: THREE.Intersection, camera: THREE.Camera, screenSize: {
359
+ width: number;
360
+ height: number;
361
+ }, snapPixels: number): THREE.Vector3;
362
+ //#endregion
363
+ //#region src/render/tool-registry.d.ts
364
+ /**
365
+ * A tool that can claim canvas pointer input.
366
+ *
367
+ * `handleClick` returning true means the tool consumed the event and the host stops dispatching —
368
+ * no further tool sees it, and object selection doesn't run. `handleMove` never consumes: it runs
369
+ * on every move regardless of which tool is active, so previews can't block orbit or pan.
370
+ */
371
+ interface PointerTool {
372
+ setEnabled?(enabled: boolean): void;
373
+ isEnabled?(): boolean;
374
+ /** Returns true if this tool consumed the click. */
375
+ handleClick(event: MouseEvent): boolean;
376
+ /** Preview only — must not consume. */
377
+ handleMove?(event: MouseEvent): void;
378
+ clear?(): void;
379
+ dispose?(): void;
380
+ }
381
+ interface ToolRegistration {
382
+ /** Unique within the registry; registering the same id twice replaces the earlier tool. */
383
+ id: string;
384
+ tool: PointerTool;
385
+ /**
386
+ * Higher runs first. The built-ins sit at 0 (measure) and -100 (gizmo); register above 0 to
387
+ * claim clicks before measuring, below -100 to act only as a fallback.
388
+ */
389
+ priority?: number;
327
390
  }
328
-
391
+ interface ToolRegistry {
392
+ /** Returns an unregister function. Does not dispose the tool — the registrant still owns it. */
393
+ register(registration: ToolRegistration): () => void;
394
+ unregister(id: string): void;
395
+ get(id: string): PointerTool | null;
396
+ /**
397
+ * Enables one tool and disables every other registered one. Pass null to disable all.
398
+ * Tools without `setEnabled` are always live and unaffected.
399
+ */
400
+ setActive(id: string | null): void;
401
+ /** The id passed to the last `setActive`, or null. */
402
+ getActive(): string | null;
403
+ /** @internal — `initThree` forwards DOM events here. */
404
+ handleClick(event: MouseEvent): boolean;
405
+ /** @internal */
406
+ handleMove(event: MouseEvent): void;
407
+ }
408
+ /**
409
+ * Screen-space ray from a canvas mouse event, for tools doing their own picking. Handles the
410
+ * canvas's position and size, so it stays correct under CSS scaling and in fullscreen.
411
+ */
412
+ declare function pointerToNdc(event: MouseEvent, canvas: HTMLCanvasElement): {
413
+ x: number;
414
+ y: number;
415
+ };
416
+ //#endregion
417
+ //#region src/render/view-gizmo.d.ts
329
418
  /**
330
419
  * Corner nav-cube/axis gizmo. Uses three's {@link ViewHelper} only as the rendered widget, not its
331
420
  * click→animate behavior: ViewHelper's snap assumes Y-up and animates straight onto the up axis,
@@ -340,83 +429,123 @@ interface MeasureTool {
340
429
  * render each frame, and forward pointer clicks to {@link ViewGizmo.handleClick}.
341
430
  */
342
431
  interface ViewGizmo {
343
- render(renderer: THREE.WebGLRenderer): void;
344
- /** Returns true if it hit the gizmo (and a view change started). */
345
- handleClick(event: MouseEvent): boolean;
346
- setVisible(visible: boolean): void;
347
- isVisible(): boolean;
348
- dispose(): void;
432
+ render(renderer: THREE.WebGLRenderer): void;
433
+ /** Returns true if it hit the gizmo (and a view change started). */
434
+ handleClick(event: MouseEvent): boolean;
435
+ setVisible(visible: boolean): void;
436
+ isVisible(): boolean;
437
+ dispose(): void;
349
438
  }
350
-
439
+ //#endregion
440
+ //#region src/render/scene-setup/viewer.d.ts
351
441
  interface ThreeViewer {
352
- scene: THREE.Scene;
353
- camera: THREE.PerspectiveCamera;
354
- controls: OrbitControls;
355
- renderer: THREE.WebGLRenderer;
356
- cameraController: CameraController;
357
- grid: Grid | null;
358
- gizmo: ViewGizmo | null;
359
- /** Null unless `measure.enabled`. */
360
- measureTool: MeasureTool | null;
361
- /**
362
- * No-op unless `edges.enabled`. Extraction runs off-thread for large meshes, so overlays can
363
- * attach a beat later; meshes over `edges.maxTriangles` fall back to the screen-space edge shader.
364
- */
365
- applyEdges: (root: THREE.Object3D) => void;
366
- /**
367
- * Prefer over `removeEdges` directly — also cancels in-flight async attaches and stands down the
368
- * screen-space edge fallback if active.
369
- */
370
- clearEdges: (root: THREE.Object3D) => void;
371
- /**
372
- * Request a repaint from the on-demand render loop. Built-in setters and input invalidate
373
- * automatically; call this after mutating the scene externally. No-op when `render.onDemand` is false.
374
- */
375
- invalidate: () => void;
376
- setAmbientOcclusion: (enabled: boolean) => void;
377
- /**
378
- * Retunes lighting/material (tone mapping, fill, IBL, AO) only — never edges/grid. Overwrites
379
- * any granular lighting dials set earlier with the preset's values.
380
- */
381
- setLook: (look: 'studio' | 'technical' | 'showcase') => void;
382
- /**
383
- * Raising `hemisphereIntensity` is the most effective way to lift shadowed surfaces a dark HDR
384
- * leaves black. Lazily creates the hemisphere light if the viewer was built without one; `0` turns
385
- * it back off.
386
- */
387
- setFillLights: (opts: {
388
- hemisphereIntensity?: number;
389
- hemisphereSkyColor?: THREE.Color | number;
390
- hemisphereGroundColor?: THREE.Color | number;
391
- ambientIntensity?: number;
392
- }) => void;
393
- /**
394
- * Normalizes IBL brightness across HDRs of differing exposure. Applies even before the HDR
395
- * finishes decoding.
396
- */
397
- setEnvironmentIntensity: (intensity: number) => void;
398
- setToneMappingExposure: (exposure: number) => void;
399
- /** GTAO strength (0-1). No-op when ambient occlusion isn't active. */
400
- setAoIntensity: (intensity: number) => void;
401
- /** Feed into the batch parser's `material` option so freshly-loaded meshes match the active look. */
402
- getMaterialAppearance: () => MaterialAppearanceOptions;
403
- /** Call after loading or replacing geometry. No-op when sunlight/shadows are off. */
404
- updateShadowBounds: () => void;
405
- /** Call after loading or replacing geometry. No-op when the grid is off or empty. */
406
- updateGridScale: () => void;
407
- dispose: () => void;
408
- fitToView: () => void;
409
- clearSelection: () => void;
410
- /** Tagged `userData.source = 'user'` so it survives `updateScene` solves and counts for fit-to-view. */
411
- addUserGeometry: (object: THREE.Object3D) => void;
412
- removeUserGeometry: (object: THREE.Object3D) => void;
413
- /** Removes and disposes everything added via `addUserGeometry`. */
414
- clearUserGeometry: () => void;
442
+ scene: THREE.Scene;
443
+ camera: THREE.PerspectiveCamera;
444
+ controls: OrbitControls;
445
+ renderer: THREE.WebGLRenderer;
446
+ cameraController: CameraController;
447
+ grid: Grid | null;
448
+ gizmo: ViewGizmo | null;
449
+ /** Null unless `measure.enabled`. */
450
+ measureTool: MeasureTool | null;
451
+ /** CSS2D annotation overlay. Always present — labels render above the canvas and don't take input. */
452
+ labelLayer: LabelLayer;
453
+ /**
454
+ * Pointer tools competing for canvas clicks, ahead of object selection. Built-ins are already
455
+ * registered as `'measure'` and `'gizmo'`; register your own and drive it with `setActive`.
456
+ */
457
+ tools: ToolRegistry;
458
+ /**
459
+ * No-op unless `edges.enabled`. Extraction runs off-thread for large meshes, so overlays can
460
+ * attach a beat later; meshes over `edges.maxTriangles` fall back to the screen-space edge shader.
461
+ */
462
+ applyEdges: (root: THREE.Object3D) => void;
463
+ /**
464
+ * Prefer over `removeEdges` directly — also cancels in-flight async attaches and stands down the
465
+ * screen-space edge fallback if active.
466
+ */
467
+ clearEdges: (root: THREE.Object3D) => void;
468
+ /**
469
+ * Request a repaint from the on-demand render loop. Built-in setters and input invalidate
470
+ * automatically; call this after mutating the scene externally. No-op when `render.onDemand` is false.
471
+ */
472
+ invalidate: () => void;
473
+ setAmbientOcclusion: (enabled: boolean) => void;
474
+ /**
475
+ * Retunes lighting/material (tone mapping, fill, IBL, AO) only — never edges/grid. Overwrites
476
+ * any granular lighting dials set earlier with the preset's values.
477
+ */
478
+ setLook: (look: 'studio' | 'technical' | 'showcase') => void;
479
+ /**
480
+ * Raising `hemisphereIntensity` is the most effective way to lift shadowed surfaces a dark HDR
481
+ * leaves black. Lazily creates the hemisphere light if the viewer was built without one; `0` turns
482
+ * it back off.
483
+ */
484
+ setFillLights: (opts: {
485
+ hemisphereIntensity?: number;
486
+ hemisphereSkyColor?: THREE.Color | number;
487
+ hemisphereGroundColor?: THREE.Color | number;
488
+ ambientIntensity?: number;
489
+ }) => void;
490
+ /**
491
+ * Normalizes IBL brightness across HDRs of differing exposure. Applies even before the HDR
492
+ * finishes decoding.
493
+ */
494
+ setEnvironmentIntensity: (intensity: number) => void;
495
+ setToneMappingExposure: (exposure: number) => void;
496
+ /** GTAO strength (0-1). No-op when ambient occlusion isn't active. */
497
+ setAoIntensity: (intensity: number) => void;
498
+ /** Feed into the batch parser's `material` option so freshly-loaded meshes match the active look. */
499
+ getMaterialAppearance: () => MaterialAppearanceOptions;
500
+ /** Call after loading or replacing geometry. No-op when sunlight/shadows are off. */
501
+ updateShadowBounds: () => void;
502
+ /** Call after loading or replacing geometry. No-op when the grid is off or empty. */
503
+ updateGridScale: () => void;
504
+ dispose: () => void;
505
+ fitToView: () => void;
506
+ clearSelection: () => void;
507
+ /**
508
+ * Adds host-owned geometry that survives `updateScene` solves and counts for fit-to-view.
509
+ * Pass `appId` to scope it to one app (`userData.source = 'app:<id>'`) so `clearUserGeometry`
510
+ * can clear that app alone; without one it's tagged `'user'` and only a global clear removes it.
511
+ *
512
+ * Not restyled by `setLook` — the caller owns these materials.
513
+ */
514
+ addUserGeometry: (object: THREE.Object3D, appId?: string) => void;
515
+ removeUserGeometry: (object: THREE.Object3D) => void;
516
+ /** Removes and disposes geometry added via `addUserGeometry` — one app's, or all of it. */
517
+ clearUserGeometry: (appId?: string) => void;
415
518
  }
416
-
519
+ //#endregion
520
+ //#region src/render/scene-setup/init-three.d.ts
417
521
  declare const initThree: (canvas: HTMLCanvasElement, options?: ThreeInitializerOptions) => ThreeViewer;
418
-
522
+ //#endregion
523
+ //#region src/render/scene-ownership.d.ts
524
+ /** Geometry produced by a solve. Replaced wholesale on the next one. */
525
+ declare const SOURCE_COMPUTE = "compute";
526
+ /**
527
+ * Host-added geometry with no owner id. Predates scoped ownership and still honoured everywhere
528
+ * an `app:` scope is — new code should prefer {@link appSource}.
529
+ */
530
+ declare const SOURCE_USER = "user";
531
+ /** The `userData.source` tag for geometry owned by app `id` (`'pointcloud'` → `'app:pointcloud'`). */
532
+ declare function appSource(id: string): string;
533
+ /** The app id from a source tag, or null if the tag isn't app-owned. */
534
+ declare function appIdFromSource(source: unknown): string | null;
535
+ /**
536
+ * True for anything a host added rather than the solve — `'user'` or any `app:` scope. This is
537
+ * the predicate `clearScene` uses, so it decides what survives a solve.
538
+ */
539
+ declare function isHostOwned(object: THREE.Object3D): boolean;
540
+ /**
541
+ * True for objects owned by `id`. Passing no id matches every host-owned object, which is what
542
+ * `clearUserGeometry()` does.
543
+ */
544
+ declare function isOwnedBy(object: THREE.Object3D, id?: string): boolean;
545
+ //#endregion
546
+ //#region src/render/three-helpers.d.ts
419
547
  /** Replaces scene content with `meshes`, rescales the camera frustum to fit, and (first call only) positions the camera/controls. */
420
548
  declare function updateScene(scene: THREE.Scene, meshes: THREE.Object3D[], camera: THREE.PerspectiveCamera, controls: OrbitControls, initialPositionSet: boolean): void;
421
-
422
- export { type CameraConfig, type CameraController, type CameraProjection, type ControlsConfig, DEFAULT_LOOK, type EdgesConfig, type EnvironmentConfig, type ErrorCode, ErrorCodes, type EventConfig, type FloorConfig, type GizmoConfig, type Grid, type GridConfig, LOOK_PRESETS, type LightingConfig, type Logger, Look, LookPreset, MaterialAppearanceOptions, type MeasureConfig, type MeasureTool, type RenderConfig, type ThreeInitializerOptions, type ThreeViewer, type ViewGizmo, type ViewPreset, VisualizationError, enableDebugLogging, getLogger, initThree, materialAppearanceForLook, setLogger, updateScene };
549
+ //#endregion
550
+ export { type CameraConfig, type CameraController, type CameraProjection, type ControlsConfig, DEFAULT_LOOK, type EdgesConfig, type EnvironmentConfig, type ErrorCode, ErrorCodes, type EventConfig, type FloorConfig, type GizmoConfig, type Grid, type GridConfig, LOOKS, LOOK_PRESETS, type LabelHandle, type LabelLayer, type LightingConfig, type Logger, type Look, type LookPreset, type MaterialAppearanceOptions, type MeasureConfig, type MeasureTool, type PointerTool, type RenderConfig, SOURCE_COMPUTE, SOURCE_USER, type ThreeInitializerOptions, type ThreeViewer, type ToolRegistration, type ToolRegistry, type ViewGizmo, type ViewPreset, VisualizationError, appIdFromSource, appSource, enableDebugLogging, getLogger, initThree, isHostOwned, isOwnedBy, materialAppearanceForLook, pickThreshold, pointerToNdc, setLogger, snapToVertex, updateScene };
551
+ //# sourceMappingURL=render.d.ts.map