@selvajs/compute 2.1.0-beta.2 → 2.1.0-beta.4

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.
@@ -1,5 +1,7 @@
1
1
  import * as THREE from 'three';
2
2
  import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
3
+ import { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
4
+ import { LineSegments2 } from 'three/addons/lines/LineSegments2.js';
3
5
  import { f as GrasshopperComputeResponse, M as MeshExtractionOptions, m as MeshBatchParsingOptions, n as DisplayBatch, o as DisplayItem } from './types-jPuWWtnU.js';
4
6
  export { p as DisplayCurve, q as DisplayIdentity, r as DisplayItemBase, s as DisplayPoint, t as DisplayPosition, u as MeshBatch } from './types-jPuWWtnU.js';
5
7
  import { RhinoModule } from 'rhino3dm';
@@ -107,8 +109,11 @@ type MeasureConfig = {
107
109
  color?: THREE.ColorRepresentation;
108
110
  /** CSS class for the distance label. */
109
111
  labelClassName?: string;
110
- /** Format the distance number → label text. Default 3 sig-digits + " m". */
111
- format?: (distance: number) => string;
112
+ /**
113
+ * Format the measurement → label text. Receives the straight-line `distance` and per-axis `delta`.
114
+ * Default renders the total plus a Δx/Δy/Δz breakdown.
115
+ */
116
+ format?: (distance: number, delta: THREE.Vector3) => string;
112
117
  };
113
118
  type ThreeInitializerOptions = {
114
119
  sceneScale?: 'mm' | 'cm' | 'm' | 'inches' | 'feet';
@@ -176,6 +181,13 @@ interface CameraController {
176
181
  toggleProjection(): CameraProjection;
177
182
  /** Move the camera to a preset orientation, framing current scene content. Animated. */
178
183
  setView(preset: ViewPreset, animate?: boolean): void;
184
+ /**
185
+ * Frame current content viewed from `direction` (a world-space vector from target toward camera).
186
+ * Like {@link setView} but takes an explicit direction — used by the nav-cube, whose clicked axis
187
+ * is a world axis, not a named preset. Pole directions are nudged off-axis to avoid the orbit
188
+ * singularity.
189
+ */
190
+ setViewDirection(direction: THREE.Vector3, animate?: boolean): void;
179
191
  /** Enable/disable orbit rotation at runtime (pan/zoom unaffected). */
180
192
  setRotateEnabled(enabled: boolean): void;
181
193
  /** Whether rotation is currently enabled. */
@@ -183,7 +195,46 @@ interface CameraController {
183
195
  /** Keep the orthographic frustum aspect in sync on canvas resize. Called by the resize loop. */
184
196
  updateAspect(width: number, height: number): void;
185
197
  }
198
+ interface CameraControllerDeps {
199
+ scene: THREE.Scene;
200
+ perspective: THREE.PerspectiveCamera;
201
+ controls: OrbitControls;
202
+ /** Called whenever the active camera identity changes, so callers can re-point the renderer/raycaster. */
203
+ onActiveCameraChange: (camera: THREE.Camera) => void;
204
+ /**
205
+ * The scene's up axis. Presets, the orthographic camera's up, and the iso direction are all derived
206
+ * from this so the controller is correct in any up convention (Three's native Y-up, Rhino's Z-up, …)
207
+ * without hardcoding an axis. Defaults to Y-up.
208
+ */
209
+ up?: THREE.Vector3;
210
+ }
211
+ declare function createCameraController(deps: CameraControllerDeps): CameraController;
186
212
 
213
+ /**
214
+ * An "infinite", distance-fading reference grid — the single strongest visual cue that reads as CAD.
215
+ *
216
+ * Why not `GridHelper`: it's a fixed-size square of line segments that visibly ends and looks wrong
217
+ * once you pan or zoom past it. Instead we draw one large screen-facing plane and compute the grid
218
+ * in the fragment shader from world coordinates, fading lines out with distance so the edge is never
219
+ * a hard cutoff. The plane is big enough to cover any reasonable view; the fade hides its bounds.
220
+ *
221
+ * Two line frequencies (minor + major every 10th) give the usual graph-paper depth read. Spacing is
222
+ * in world units (meters — the scene's normalized unit), so a `cellSize` of 1 = 1m minor cells.
223
+ */
224
+ interface GridOptions {
225
+ /** Minor cell size in world units (meters). Default 1. */
226
+ cellSize?: number;
227
+ /** How many minor cells per major line. Default 10. */
228
+ majorEvery?: number;
229
+ /** Minor line color. */
230
+ cellColor?: THREE.ColorRepresentation;
231
+ /** Major line color. */
232
+ majorColor?: THREE.ColorRepresentation;
233
+ /** World-space radius at which the grid has fully faded out. Default 100. */
234
+ fadeDistance?: number;
235
+ /** Plane to lay the grid on. 'y' = horizontal ground (Three Y-up). Default 'y'. */
236
+ plane?: 'x' | 'y' | 'z';
237
+ }
187
238
  interface Grid {
188
239
  /** The grid mesh; add to the scene. Tagged `userData.id = 'grid'` so pick/fit code skips it. */
189
240
  readonly object: THREE.Mesh;
@@ -192,23 +243,24 @@ interface Grid {
192
243
  setVisible(visible: boolean): void;
193
244
  dispose(): void;
194
245
  }
246
+ declare function createGrid(options?: GridOptions): Grid;
195
247
 
196
248
  /**
197
- * The corner nav-cube/axis gizmo. Wraps three's {@link ViewHelper} (the standard, well-tested
198
- * widget) and uses its built-in click animate behavior, which we keep rather than reimplement:
199
- * ViewHelper's hit-test depends on private internals (`dim`, `interactiveObjects`, viewport math),
200
- * so replicating it is fragile. We let it drive the perspective camera directly.
249
+ * The corner nav-cube/axis gizmo. Uses three's {@link ViewHelper} purely as the rendered widget, but
250
+ * NOT its click→animate behavior: ViewHelper's built-in snap assumes a Y-up world and animates the
251
+ * camera straight onto the up axis, which in our Z-up scene rolls the view and makes the gizmo jitter
252
+ * at the pole. Instead we hit-test the axis sprites ourselves and drive the viewer's up-aware camera
253
+ * controller, which snaps (no animation) with a pole nudge so the orbit basis never degenerates.
201
254
  *
202
- * Two integration points with the viewer's dual-camera setup:
203
- * 1. Before each click we point `helper.center` at the live orbit target, so the snap rotates about
204
- * what the user is looking at (not the world origin).
205
- * 2. ViewHelper only drives the perspective camera. The nav cube is inherently a 3D-orientation
206
- * tool, so if the viewer is in orthographic (2D) mode when the gizmo is clicked, we first flip
207
- * back to perspective — then ViewHelper animates as usual. Using the cube returns you to 3D.
255
+ * Integration points with the viewer's dual-camera setup:
256
+ * 1. The snap frames the current orbit target via the controller, so it rotates about what the user
257
+ * is looking at (not the world origin).
258
+ * 2. The nav cube is inherently a 3D-orientation tool, so if the viewer is in orthographic (2D) mode
259
+ * when the gizmo is clicked, we first flip back to perspective.
208
260
  *
209
261
  * Caller responsibilities (mirror ViewHelper's own contract):
210
262
  * - call {@link ViewGizmo.render} *after* the main scene render each frame (overlay viewport),
211
- * - call {@link ViewGizmo.update} each frame with the frame delta (drives the snap animation),
263
+ * - call {@link ViewGizmo.update} each frame (no-op now; kept for API symmetry),
212
264
  * - forward pointer clicks to {@link ViewGizmo.handleClick}.
213
265
  */
214
266
  interface ViewGizmo {
@@ -222,6 +274,48 @@ interface ViewGizmo {
222
274
  isVisible(): boolean;
223
275
  dispose(): void;
224
276
  }
277
+ interface ViewGizmoDeps {
278
+ /** The perspective (primary) camera the gizmo visualizes and re-orients. */
279
+ camera: THREE.PerspectiveCamera;
280
+ domElement: HTMLElement;
281
+ controller: CameraController;
282
+ }
283
+ declare function createViewGizmo(deps: ViewGizmoDeps): ViewGizmo;
284
+
285
+ /**
286
+ * An HTML label layer that tracks 3D positions, via three's {@link CSS2DRenderer}. Labels are real
287
+ * DOM nodes (crisp text, CSS-stylable) positioned each frame to follow points in the scene — the
288
+ * foundation for measurement readouts, dimension annotations, and point tags.
289
+ *
290
+ * The CSS2D renderer draws into its own absolutely-positioned DOM overlay stacked on top of the
291
+ * WebGL canvas (pointer-events disabled so it never steals clicks from the viewer). The viewer owns
292
+ * one of these; features like the measure tool add/remove labels through it.
293
+ */
294
+ interface LabelHandle {
295
+ /** The CSS2DObject in the scene graph. */
296
+ readonly object: CSS2DObject;
297
+ /** Move the label to a new world position. */
298
+ setPosition(position: THREE.Vector3): void;
299
+ /** Replace the label's text/HTML. */
300
+ setText(text: string): void;
301
+ /** Remove the label from the scene and the DOM. */
302
+ remove(): void;
303
+ }
304
+ interface LabelLayer {
305
+ /** Create a label at a world position. `className` lets callers theme groups of labels. */
306
+ addLabel(text: string, position: THREE.Vector3, className?: string): LabelHandle;
307
+ /** Render the DOM overlay. Call each frame after the WebGL render, with the active camera. */
308
+ render(scene: THREE.Scene, camera: THREE.Camera): void;
309
+ setSize(width: number, height: number): void;
310
+ dispose(): void;
311
+ }
312
+ /**
313
+ * @param container element to overlay labels onto — normally the canvas's parent, so the overlay and
314
+ * canvas share a positioning context. The overlay is appended here and absolutely positioned.
315
+ * @param scene labels are parented to a group added to this scene, so they render and follow the
316
+ * camera without the caller wiring scene-graph parenting.
317
+ */
318
+ declare function createLabelLayer(container: HTMLElement, scene: THREE.Scene): LabelLayer;
225
319
 
226
320
  /**
227
321
  * A two-click distance measurement tool — the CAD verb users expect. Click a point, click a second,
@@ -242,10 +336,49 @@ interface MeasureTool {
242
336
  isEnabled(): boolean;
243
337
  /** Process a click. Returns true if the tool consumed it (caller should not also select). */
244
338
  handleClick(event: MouseEvent): boolean;
339
+ /**
340
+ * Process pointer movement to preview the next snap point — a ghost marker tracks the cursor and
341
+ * jumps to the vertex a click would snap to, so users can aim before committing. No-op when the
342
+ * tool is disabled. The caller forwards mousemove; nothing is consumed.
343
+ */
344
+ handleMove(event: MouseEvent): void;
245
345
  /** Clear the current measurement (markers, line, label). */
246
346
  clear(): void;
247
347
  dispose(): void;
248
348
  }
349
+ interface MeasureOptions {
350
+ /** Snap to a vertex when the cursor is within this many screen pixels of it. Default 12. */
351
+ snapPixels?: number;
352
+ /** Marker + line color. Default yellow. */
353
+ color?: THREE.ColorRepresentation;
354
+ /** CSS class applied to the distance label, for styling. */
355
+ labelClassName?: string;
356
+ /**
357
+ * Format the measurement → label text. Receives the straight-line `distance` and the per-axis
358
+ * `delta` (|b − a| on each axis). May return multi-line text or HTML; the default renders the
359
+ * total plus a Δx/Δy/Δz breakdown. Old `(distance) => string` callbacks remain valid.
360
+ */
361
+ format?: (distance: number, delta: THREE.Vector3) => string;
362
+ }
363
+ interface MeasureDeps {
364
+ canvas: HTMLCanvasElement;
365
+ scene: THREE.Scene;
366
+ getActiveCamera: () => THREE.Camera;
367
+ labelLayer: LabelLayer;
368
+ options?: MeasureOptions;
369
+ }
370
+ /**
371
+ * Snap a raycast hit to the nearest vertex of the struck triangle if it's within `snapPixels` on
372
+ * screen; otherwise return the raw hit point. Pure (no DOM) so it's unit-testable: it takes the
373
+ * screen size explicitly rather than reading the canvas. Exported for that reason.
374
+ *
375
+ * Falls back to the raw point for non-mesh hits (e.g. a curve) or geometry without a face/positions.
376
+ */
377
+ declare function snapToVertex(hit: THREE.Intersection, camera: THREE.Camera, screenSize: {
378
+ width: number;
379
+ height: number;
380
+ }, snapPixels: number): THREE.Vector3;
381
+ declare function createMeasureTool(deps: MeasureDeps): MeasureTool;
249
382
 
250
383
  /**
251
384
  * Initializes a Three.js environment with scene, camera, renderer, and event handling.
@@ -282,6 +415,22 @@ declare const initThree: (canvas: HTMLCanvasElement, options?: ThreeInitializerO
282
415
  * @param initialPositionSet - A boolean indicating whether the initial position of the camera and controls have been set.
283
416
  */
284
417
  declare function updateScene(scene: THREE.Scene, meshes: THREE.Object3D[], camera: THREE.PerspectiveCamera, controls: OrbitControls, initialPositionSet: boolean): void;
418
+ /**
419
+ * Parses a color string in multiple formats to a THREE.Color object.
420
+ * Supported formats:
421
+ * - Hex: "#C7A5A5", "C7A5A5"
422
+ * - RGB: "199, 165, 165"
423
+ * - CSS named colors: "red", "blue", etc.
424
+ * @param colorString - The color string to parse.
425
+ * @returns A THREE.Color object.
426
+ */
427
+ declare function parseColor(colorString: string): THREE.Color;
428
+ declare function applyOffset(meshes: THREE.Object3D[], offsetY: number): void;
429
+ /**
430
+ * Computes the combined world-axis-aligned bounding box of a set of objects (meshes, lines, points).
431
+ * Correctly accounts for transformations (rotation, position, scale).
432
+ */
433
+ declare function computeCombinedBoundingBox(meshes: THREE.Object3D[]): THREE.Box3;
285
434
 
286
435
  declare const EMISSIVE_MATERIAL: THREE.MeshPhysicalMaterial;
287
436
  declare const METAL_MATERIAL: THREE.MeshPhysicalMaterial;
@@ -302,6 +451,75 @@ declare namespace threeMaterials {
302
451
  export { threeMaterials_CONCRETE_MATERIAL as CONCRETE_MATERIAL, threeMaterials_EMISSIVE_MATERIAL as EMISSIVE_MATERIAL, threeMaterials_GLASS_MATERIAL as GLASS_MATERIAL, threeMaterials_METAL_MATERIAL as METAL_MATERIAL, threeMaterials_PLASTIC_MATERIAL as PLASTIC_MATERIAL, threeMaterials_RUBBER_MATERIAL as RUBBER_MATERIAL, threeMaterials_WOOD_MATERIAL as WOOD_MATERIAL };
303
452
  }
304
453
 
454
+ /**
455
+ * Crisp boundary/crease edges overlaid on meshes — the defining "technical drawing" look that makes
456
+ * shaded geometry read as discrete objects rather than blobs.
457
+ *
458
+ * Built with `EdgesGeometry` (which keeps only edges whose adjacent faces meet above a threshold
459
+ * angle, so flat tessellation noise is dropped) rendered as a fat `LineSegments2` using the same
460
+ * `LineMaterial` family as curves (Phase 1) — so edges get controllable thickness, not the 1px cap
461
+ * of `THREE.LineSegments`. The overlay is added as a *child* of each mesh, so it inherits the mesh's
462
+ * transform and is disposed when the mesh subtree is cleared.
463
+ */
464
+ interface EdgeOptions {
465
+ /** Edge color. Default near-black. */
466
+ color?: THREE.ColorRepresentation;
467
+ /** Edge thickness in CSS px. Default 1.5. */
468
+ width?: number;
469
+ /**
470
+ * Crease angle in degrees: an edge is kept only where its two faces differ by more than this.
471
+ * Default 30. Higher = fewer edges (only sharp creases); lower = more (catches gentle bends).
472
+ */
473
+ thresholdAngle?: number;
474
+ }
475
+ /** Tag on edge overlays so pick/fit/clear logic can recognize and skip or dispose them. */
476
+ declare const EDGE_USERDATA_KIND = "edge-overlay";
477
+ /**
478
+ * Walk an object subtree and attach an edge overlay to every `Mesh` found, returning the created
479
+ * overlays (so callers can dispose them explicitly if they don't clear the whole subtree). Meshes
480
+ * that already carry an overlay are skipped, so this is safe to call more than once.
481
+ *
482
+ * Skips the floor and the grid (they're aids, not content) and anything already tagged as an edge.
483
+ */
484
+ declare function addEdges(root: THREE.Object3D, options?: EdgeOptions): LineSegments2[];
485
+ /** Whether an object is an edge overlay (for pick/fit filters elsewhere). */
486
+ declare function isEdgeOverlay(object: THREE.Object3D): boolean;
487
+ /**
488
+ * Remove every edge overlay under `root`, disposing its geometry and material. The inverse of
489
+ * {@link addEdges}; together they make edges a live on/off toggle. Returns how many were removed.
490
+ */
491
+ declare function removeEdges(root: THREE.Object3D): number;
492
+
493
+ /**
494
+ * Optional postprocessing pipeline for ambient occlusion. Default-OFF: the viewer only constructs
495
+ * this when `render.ambientOcclusion` is enabled, and otherwise renders with a plain
496
+ * `renderer.render` so the cheap path stays cheap (the chosen tradeoff — see cad-viewer-plan.md).
497
+ *
498
+ * Pipeline: RenderPass → GTAOPass → OutputPass. GTAO (ground-truth AO) is the modern replacement for
499
+ * SSAO/SAO — better contact shadows in crevices, the "engineered" depth cue. `screenSpaceRadius` is
500
+ * on so the AO radius is in screen space, which keeps it scale-robust across the viewer's mm→m
501
+ * scenes without per-scene tuning. OutputPass applies tone mapping + color space (taking over the
502
+ * roles the renderer did directly in the non-composer path).
503
+ *
504
+ * Camera swaps: the active camera can flip perspective↔ortho. Rather than rebuild the composer, we
505
+ * retarget the passes' `camera` each render via {@link setCamera}.
506
+ */
507
+ interface RenderPipeline {
508
+ render(deltaTime: number): void;
509
+ setSize(width: number, height: number, pixelRatio: number): void;
510
+ /** Point the passes at the currently active camera (call when projection changes). */
511
+ setCamera(camera: THREE.Camera): void;
512
+ dispose(): void;
513
+ }
514
+ interface RenderPipelineOptions {
515
+ /** Tone mapping to apply in OutputPass (mirror the renderer's). */
516
+ toneMapping: THREE.ToneMapping;
517
+ toneMappingExposure: number;
518
+ /** AO strength 0–1. Default 1. */
519
+ aoIntensity?: number;
520
+ }
521
+ declare function createRenderPipeline(renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.Camera, width: number, height: number, options: RenderPipelineOptions): RenderPipeline;
522
+
305
523
  declare const SCALE_FACTORS: Record<string, number>;
306
524
  /**
307
525
  * Extracts and processes display meshes from a ComputePointerResponse using the Grasshopper WebDisplay component.
@@ -405,4 +623,4 @@ interface DisplayItemParseOptions {
405
623
  */
406
624
  declare function parseDisplayItems(items: DisplayItem[] | undefined, options?: DisplayItemParseOptions): THREE.Object3D[];
407
625
 
408
- export { type CameraConfig, type ControlsConfig, DisplayBatch, DisplayItem, type DisplayItemParseOptions, type EnvironmentConfig, type EventConfig, type FloorConfig, type LightingConfig, threeMaterials as Materials, MeshExtractionOptions, type RenderConfig, SCALE_FACTORS, type ThreeInitializerOptions, getThreeMeshesFromComputeResponse, initThree, parseDisplayItems, parseMeshBatchBlob, parseMeshBatchObject, updateScene };
626
+ export { type CameraConfig, type CameraController, type CameraProjection, type ControlsConfig, DisplayBatch, DisplayItem, type DisplayItemParseOptions, EDGE_USERDATA_KIND, type EdgeOptions, type EdgesConfig, type EnvironmentConfig, type EventConfig, type FloorConfig, type GizmoConfig, type Grid, type GridConfig, type GridOptions, type LabelHandle, type LabelLayer, type LightingConfig, threeMaterials as Materials, type MeasureConfig, type MeasureOptions, type MeasureTool, MeshExtractionOptions, type RenderConfig, type RenderPipeline, type RenderPipelineOptions, SCALE_FACTORS, type ThreeInitializerOptions, type ViewGizmo, type ViewPreset, addEdges, applyOffset, computeCombinedBoundingBox, createCameraController, createGrid, createLabelLayer, createMeasureTool, createRenderPipeline, createViewGizmo, getThreeMeshesFromComputeResponse, initThree, isEdgeOverlay, parseColor, parseDisplayItems, parseMeshBatchBlob, parseMeshBatchObject, removeEdges, snapToVertex, updateScene };
@@ -1,2 +1,2 @@
1
- import{A as p,a as e,p as t,q as i,r as s,x as o,y as a,z as r}from"./chunk-PGOKADJC.js";import"./chunk-XFYFC2DH.js";export{i as Materials,r as SCALE_FACTORS,p as getThreeMeshesFromComputeResponse,t as initThree,s as parseDisplayItems,a as parseMeshBatchBlob,o as parseMeshBatchObject,e as updateScene};
1
+ import{A as j,a as e,b as r,c as t,d as o,e as i,f as a,g as s,h as p,i as n,j as l,k as f,l as m,m as y,n as u,o as d,p as x,q as C,r as g,x as h,y as c,z as v}from"./chunk-45AYUHMT.js";import"./chunk-XFYFC2DH.js";export{p as EDGE_USERDATA_KIND,C as Materials,v as SCALE_FACTORS,n as addEdges,t as applyOffset,o as computeCombinedBoundingBox,i as createCameraController,a as createGrid,y as createLabelLayer,d as createMeasureTool,m as createRenderPipeline,s as createViewGizmo,j as getThreeMeshesFromComputeResponse,x as initThree,l as isEdgeOverlay,r as parseColor,g as parseDisplayItems,c as parseMeshBatchBlob,h as parseMeshBatchObject,f as removeEdges,u as snapToVertex,e as updateScene};
2
2
  //# sourceMappingURL=visualization.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@selvajs/compute",
3
- "version": "2.1.0-beta.2",
3
+ "version": "2.1.0-beta.4",
4
4
  "description": "TypeScript library for Rhino Compute Server - Grasshopper and RhinoCommon",
5
5
  "author": "VektorNode",
6
6
  "license": "MIT",
@@ -1,54 +0,0 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _chunkJZFH67EScjs = require('./chunk-JZFH67ES.cjs');var _three = require('three'); var P = _interopRequireWildcard(_three); var B = _interopRequireWildcard(_three); var D = _interopRequireWildcard(_three); var j = _interopRequireWildcard(_three); var se = _interopRequireWildcard(_three); var A = _interopRequireWildcard(_three); var f = _interopRequireWildcard(_three); var x = _interopRequireWildcard(_three); var V = _interopRequireWildcard(_three); var I = _interopRequireWildcard(_three);var F={HUGE_THRESHOLD:1e4,LARGE_THRESHOLD:1e3,SCALE_RATIO_THRESHOLD:100,NEAR_PLANE_FACTOR:{TINY:1e-4,SMALL:.001,NORMAL:.01},FAR_PLANE_FACTOR:{HUGE:100,LARGE:50,NORMAL:20},INITIAL_DISTANCE_MULTIPLIER:4};function dn(e,r,t,n,i){if(He(e),r.length===0)return;r.forEach(d=>{e.add(d)});let o=_(r),a=o.getCenter(new P.Vector3),c=o.getSize(new P.Vector3),s=Math.max(c.x,c.y,c.z);if(s/Math.min(c.x||1,c.y||1,c.z||1)>F.SCALE_RATIO_THRESHOLD||s>F.HUGE_THRESHOLD?(t.near=s*F.NEAR_PLANE_FACTOR.TINY,t.far=s*F.FAR_PLANE_FACTOR.HUGE):s>F.LARGE_THRESHOLD?(t.near=s*F.NEAR_PLANE_FACTOR.SMALL,t.far=s*F.FAR_PLANE_FACTOR.LARGE):(t.near=Math.max(.01,s*F.NEAR_PLANE_FACTOR.NORMAL),t.far=Math.max(2e3,s*F.FAR_PLANE_FACTOR.NORMAL)),t.updateProjectionMatrix(),i)n.minDistance=t.near*2,n.maxDistance=t.far*.9;else{let d=s*F.INITIAL_DISTANCE_MULTIPLIER;t.position.set(a.x+d*.8,a.y+d,a.z+d*1.2),n.target.copy(a),n.minDistance=t.near*2,n.maxDistance=t.far*.9,n.update()}}function Q(e){if(!e||typeof e!="string")return _chunkJZFH67EScjs.e.call(void 0, ).warn(`Invalid color input: ${e}, using white`),new P.Color(16777215);let r=e.trim();if(/^#?[0-9A-Fa-f]{6}$/.test(r))try{let t=r.startsWith("#")?r:`#${r}`;return new P.Color(t)}catch (e2){return _chunkJZFH67EScjs.e.call(void 0, ).warn(`Invalid hex color: ${e}, using white`),new P.Color(16777215)}if(r.includes(",")){let t=r.split(",").map(n=>parseInt(n.trim(),10));if(t.length===3&&t.every(n=>!isNaN(n)&&n>=0&&n<=255))return new P.Color(t[0]/255,t[1]/255,t[2]/255)}try{return new P.Color(r.toLowerCase())}catch (e3){return _chunkJZFH67EScjs.e.call(void 0, ).warn(`Invalid color string: ${e}, using white`),new P.Color(16777215)}}function ee(e,r){e.forEach(t=>{t.position.y-=r})}function _(e){let r=new P.Box3;return e.length===0||e.forEach(t=>{t.updateMatrixWorld(!0);let n=new P.Box3().setFromObject(t);r.union(n)}),r}function He(e){[...e.children].forEach(t=>{t.userData.id!=="floor"&&(t.traverse(n=>{let i=n;if(!i.geometry&&!i.material)return;_optionalChain([i, 'access', _2 => _2.geometry, 'optionalAccess', _3 => _3.dispose, 'call', _4 => _4()]);let o=i.material;if(!o)return;(Array.isArray(o)?o:[o]).forEach(c=>{for(let s of Object.values(c))s instanceof P.Texture&&s.dispose();c.dispose()})}),t.removeFromParent())})}var Ce={top:new B.Vector3(0,1,0),bottom:new B.Vector3(0,-1,0),front:new B.Vector3(0,0,1),back:new B.Vector3(0,0,-1),right:new B.Vector3(1,0,0),left:new B.Vector3(-1,0,0),iso:new B.Vector3(.8,1,1.2).normalize()};function te(e){let{scene:r,perspective:t,controls:n,onActiveCameraChange:i}=e,o=new B.OrthographicCamera(-1,1,1,-1,t.near,t.far);o.up.copy(t.up);let a="perspective",c=t.aspect,s=()=>a==="perspective"?t:o,l=()=>{let v=t.position.distanceTo(n.target)*Math.tan(t.fov*Math.PI/360),S=v*c;o.left=-S,o.right=S,o.top=v,o.bottom=-v,o.near=t.near,o.far=t.far,o.updateProjectionMatrix()},d=m=>{m!==a&&(m==="orthographic"?(o.position.copy(t.position),o.up.copy(t.up),o.lookAt(n.target),l()):t.position.copy(o.position),a=m,n.object=s(),n.update(),i(s()))};return{getActiveCamera:s,getProjection:()=>a,setProjection:d,toggleProjection:()=>(d(a==="perspective"?"orthographic":"perspective"),a),setView:(m,v=!0)=>{let S=xe(r),T=S.isEmpty()?n.target.clone():S.getCenter(new B.Vector3),b=S.isEmpty()?new B.Vector3(1,1,1):S.getSize(new B.Vector3),H=Math.max(b.x,b.y,b.z)||1,h=t.fov*(Math.PI/180),g=H/(2*Math.tan(h/2))*1.5,R=Ce[m],M=T.clone().add(R.clone().multiplyScalar(g)),y=s();v?Ae(y,n,M,T,()=>{a==="orthographic"&&l()}):(y.position.copy(M),n.target.copy(T),a==="orthographic"&&l(),n.update())},setRotateEnabled:m=>{n.enableRotate=m},isRotateEnabled:()=>n.enableRotate,updateAspect:(m,v)=>{c=v===0?c:m/v,a==="orthographic"&&l()}}}var ve=new Set(["grid","floor","label-layer","measure"]);function Me(e){let r=e;for(;r;){if(typeof r.userData.id=="string"&&ve.has(r.userData.id))return!0;r=r.parent}return!1}function xe(e){let r=[];return e.traverse(t=>{let n=t;t.visible&&!Me(t)&&n.geometry&&r.push(t)}),_(r)}var Se=e=>1-Math.pow(1-e,3);function Ae(e,r,t,n,i,o=250){let a=e.position.clone(),c=r.target.clone(),s=performance.now(),l=()=>{let d=Se(Math.min((performance.now()-s)/o,1));e.position.lerpVectors(a,t,d),r.target.lerpVectors(c,n,d),i(),r.update(),d<1&&requestAnimationFrame(l)};requestAnimationFrame(l)}var De=`
2
- varying vec3 vWorldPos;
3
- void main() {
4
- vec4 world = modelMatrix * vec4(position, 1.0);
5
- vWorldPos = world.xyz;
6
- gl_Position = projectionMatrix * viewMatrix * world;
7
- }
8
- `,Le=`
9
- precision highp float;
10
- varying vec3 vWorldPos;
11
-
12
- uniform vec2 uAxes; // indices (0=x,1=y,2=z) of the two in-plane world axes
13
- uniform float uCell;
14
- uniform float uMajor;
15
- uniform vec3 uCellColor;
16
- uniform vec3 uMajorColor;
17
- uniform vec3 uCenter; // fade center (camera position), projected onto plane
18
- uniform float uFade;
19
-
20
- // Antialiased grid line intensity for a given spacing, using screen-space derivatives so lines
21
- // stay ~1px regardless of zoom (the standard "pristine grid" technique).
22
- float gridLine(vec2 coord, float spacing) {
23
- vec2 c = coord / spacing;
24
- vec2 d = fwidth(c);
25
- vec2 g = abs(fract(c - 0.5) - 0.5) / max(d, 1e-6);
26
- float line = min(g.x, g.y);
27
- return 1.0 - clamp(line, 0.0, 1.0);
28
- }
29
-
30
- // Index a vec3 by a float axis id (0/1/2) without dynamic indexing (WebGL1-safe).
31
- float axis(vec3 v, float i) {
32
- return i < 0.5 ? v.x : (i < 1.5 ? v.y : v.z);
33
- }
34
-
35
- void main() {
36
- // Pick the two in-plane world coordinates.
37
- vec2 coord = vec2(axis(vWorldPos, uAxes.x), axis(vWorldPos, uAxes.y));
38
-
39
- float minor = gridLine(coord, uCell);
40
- float major = gridLine(coord, uCell * uMajor);
41
-
42
- vec3 color = mix(uCellColor, uMajorColor, major);
43
- float alpha = max(minor, major);
44
-
45
- // Radial fade from the camera-projected center.
46
- float dist = distance(vWorldPos, uCenter);
47
- float fade = 1.0 - clamp(dist / uFade, 0.0, 1.0);
48
- alpha *= fade * fade;
49
-
50
- if (alpha < 0.001) discard;
51
- gl_FragColor = vec4(color, alpha);
52
- }
53
- `;function ne(e={}){let{cellSize:r=1,majorEvery:t=10,cellColor:n=8947848,majorColor:i=4473924,fadeDistance:o=100,plane:a="y"}=e,c=a==="y"?new D.Vector2(0,2):a==="z"?new D.Vector2(0,1):new D.Vector2(1,2),s=o*2.5,l=new D.PlaneGeometry(s,s);a==="y"?l.rotateX(-Math.PI/2):a==="x"&&l.rotateY(Math.PI/2);let d=new D.ShaderMaterial({vertexShader:De,fragmentShader:Le,transparent:!0,depthWrite:!1,side:D.DoubleSide,uniforms:{uAxes:{value:c},uCell:{value:r},uMajor:{value:t},uCellColor:{value:new D.Color(n)},uMajorColor:{value:new D.Color(i)},uCenter:{value:new D.Vector3},uFade:{value:o}}}),u=new D.Mesh(l,d);u.name="grid",u.userData.id="grid",u.renderOrder=-1;let p=new D.Vector3;return{object:u,update:E=>{a==="y"?(u.position.set(E.x,0,E.z),p.set(E.x,0,E.z)):a==="z"?(u.position.set(E.x,E.y,0),p.set(E.x,E.y,0)):(u.position.set(0,E.y,E.z),p.set(0,E.y,E.z)),d.uniforms.uCenter.value.copy(p)},setVisible:E=>{u.visible=E},dispose:()=>{l.dispose(),d.dispose()}}}var _ViewHelperjs = require('three/addons/helpers/ViewHelper.js');function re(e){let{camera:r,domElement:t,controls:n,controller:i}=e,o=new (0, _ViewHelperjs.ViewHelper)(r,t);o.setLabels("X","Y","Z");let a=!0;return{render:s=>{if(!a)return;let l=s.autoClear;s.autoClear=!1,o.render(s),s.autoClear=l},update:s=>{o.animating&&o.update(s)},handleClick:s=>!a||(o.center.copy(n.target),!o.handleClick(s))?!1:(i.getProjection()==="orthographic"&&i.setProjection("perspective"),!0),get isAnimating(){return o.animating},setVisible:s=>{a=s},isVisible:()=>a,dispose:()=>o.dispose()}}var _LineSegmentsGeometryjs = require('three/addons/lines/LineSegmentsGeometry.js');var _LineSegments2js = require('three/addons/lines/LineSegments2.js');var _LineMaterialjs = require('three/addons/lines/LineMaterial.js');var N="edge-overlay",Be=2236962,Ve=1.5,Fe=30;function ae(e,r={}){let t=new j.Color(_nullishCoalesce(r.color, () => (Be))),n=_nullishCoalesce(r.width, () => (Ve)),i=_nullishCoalesce(r.thresholdAngle, () => (Fe)),o=[];return e.traverse(a=>{if(!(a instanceof j.Mesh)||a.userData.id==="floor"||a.userData.id==="grid"||a.userData.kind===N||a.children.some(s=>_optionalChain([s, 'access', _5 => _5.userData, 'optionalAccess', _6 => _6.kind])===N)||!a.geometry)return;let c=ze(a.geometry,t,n,i);a.add(c),o.push(c)}),o}function ze(e,r,t,n){let i=new j.EdgesGeometry(e,n),o=new _LineSegmentsGeometryjs.LineSegmentsGeometry;o.setPositions(Array.from(i.attributes.position.array)),i.dispose();let a=new (0, _LineMaterialjs.LineMaterial)({color:r});a.linewidth=t,a.polygonOffset=!0,a.polygonOffsetFactor=-1,a.polygonOffsetUnits=-1;let c=new (0, _LineSegments2js.LineSegments2)(o,a);return c.userData.kind=N,c.raycast=()=>{},c}function ke(e){return _optionalChain([e, 'access', _7 => _7.userData, 'optionalAccess', _8 => _8.kind])===N}function Rn(e){let r=[];e.traverse(t=>{t instanceof _LineSegments2js.LineSegments2&&ke(t)&&r.push(t)});for(let t of r)t.geometry.dispose(),t.material.dispose(),t.removeFromParent();return r.length}var _EffectComposerjs = require('three/addons/postprocessing/EffectComposer.js');var _RenderPassjs = require('three/addons/postprocessing/RenderPass.js');var _GTAOPassjs = require('three/addons/postprocessing/GTAOPass.js');var _OutputPassjs = require('three/addons/postprocessing/OutputPass.js');function ie(e,r,t,n,i,o){let a=new (0, _EffectComposerjs.EffectComposer)(e),c=new (0, _RenderPassjs.RenderPass)(r,t);a.addPass(c);let s=new (0, _GTAOPassjs.GTAOPass)(r,t,n,i);s.blendIntensity=_nullishCoalesce(o.aoIntensity, () => (1)),s.updateGtaoMaterial({screenSpaceRadius:!0}),a.addPass(s);let l=new _OutputPassjs.OutputPass;return a.addPass(l),e.toneMapping=o.toneMapping,e.toneMappingExposure=o.toneMappingExposure,a.setSize(n,i),{render:d=>a.render(d),setSize:(d,u,p)=>{a.setPixelRatio(p),a.setSize(d,u),s.setSize(d,u)},setCamera:d=>{c.camera=d,s.camera=d},dispose:()=>a.dispose()}}var _CSS2DRendererjs = require('three/addons/renderers/CSS2DRenderer.js');function le(e,r){let t=new _CSS2DRendererjs.CSS2DRenderer,n=t.domElement;n.style.position="absolute",n.style.top="0",n.style.left="0",n.style.width="100%",n.style.height="100%",n.style.overflow="hidden",n.style.pointerEvents="none",getComputedStyle(e).position==="static"&&(e.style.position="relative"),e.appendChild(n);let i={width:e.clientWidth||1,height:e.clientHeight||1};t.setSize(i.width,i.height);let o=new se.Group;o.name="label-layer",o.userData.id="label-layer",r.add(o);let a=new Set;return{addLabel:(s,l,d)=>{let u=document.createElement("div");u.textContent=s,d&&(u.className=d),u.style.pointerEvents="none";let p=new (0, _CSS2DRendererjs.CSS2DObject)(u);return p.position.copy(l),o.add(p),a.add(p),{object:p,setPosition:E=>p.position.copy(E),setText:E=>{u.textContent=E},remove:()=>{p.removeFromParent(),u.remove(),a.delete(p)}}},render:(s,l)=>t.render(s,l),setSize:(s,l)=>t.setSize(s,l),dispose:()=>{a.forEach(s=>{s.removeFromParent(),s.element.remove()}),a.clear(),o.removeFromParent(),n.remove()}}}var _Line2js = require('three/addons/lines/Line2.js');var _LineGeometryjs = require('three/addons/lines/LineGeometry.js');var qe=12,Ze=16763904,Je=e=>`${e.toPrecision(3)} m`;function Ke(e,r,t,n){let i=e.point.clone(),o=e.object;if(!(o instanceof A.Mesh)||e.face==null||!o.geometry)return i;let a=o.geometry.attributes.position;if(!a)return i;let c=u=>{let p=u.clone().project(r);return new A.Vector2((p.x+1)/2*t.width,(1-p.y)/2*t.height)},s=c(i),l=i,d=n;for(let u of[e.face.a,e.face.b,e.face.c]){let E=new A.Vector3().fromBufferAttribute(a,u).applyMatrix4(o.matrixWorld),m=c(E).distanceTo(s);m<d&&(d=m,l=E)}return l}function ce(e){let{canvas:r,scene:t,getActiveCamera:n,labelLayer:i,options:o={}}=e,a=_nullishCoalesce(o.snapPixels, () => (qe)),c=new A.Color(_nullishCoalesce(o.color, () => (Ze))),s=_nullishCoalesce(o.format, () => (Je)),l=new A.Raycaster,d=new A.Vector2,u=!1,p=[],E=[],m=null,v=null,S=new A.PointsMaterial({color:c,size:8,sizeAttenuation:!1,depthTest:!1}),T=g=>{let R=new A.BufferGeometry;R.setAttribute("position",new A.Float32BufferAttribute([g.x,g.y,g.z],3));let M=new A.Points(R,S);return M.renderOrder=999,M.userData.id="measure",M.raycast=()=>{},t.add(M),M},b=()=>{p.length=0,E.forEach(g=>{g.geometry.dispose(),g.removeFromParent()}),E.length=0,m&&(m.geometry.dispose(),m.material.dispose(),m.removeFromParent(),m=null),_optionalChain([v, 'optionalAccess', _9 => _9.remove, 'call', _10 => _10()]),v=null},H=()=>{if(p.length!==2)return;let[g,R]=p,M=new _LineGeometryjs.LineGeometry;M.setPositions([g.x,g.y,g.z,R.x,R.y,R.z]);let y=new (0, _LineMaterialjs.LineMaterial)({color:c});y.linewidth=2,y.depthTest=!1,m=new (0, _Line2js.Line2)(M,y),m.renderOrder=998,m.userData.id="measure",m.raycast=()=>{},t.add(m);let L=g.clone().add(R).multiplyScalar(.5);v=i.addLabel(s(g.distanceTo(R)),L,o.labelClassName)};return{setEnabled:g=>{u=g,g||b()},isEnabled:()=>u,handleClick:g=>{if(!u)return!1;p.length===2&&b();let R=r.getBoundingClientRect();d.x=(g.clientX-R.left)/R.width*2-1,d.y=-((g.clientY-R.top)/R.height)*2+1;let M=n();l.setFromCamera(d,M);let y=l.intersectObjects(t.children,!0).filter(C=>C.object.userData.id!=="measure"&&C.object.userData.id!=="grid");if(y.length===0)return!0;let L=Ke(y[0],M,{width:R.width,height:R.height},a);return p.push(L),E.push(T(L)),p.length===2&&H(),!0},clear:b,dispose:()=>{b(),S.dispose()}}}var _OrbitControlsjs = require('three/addons/controls/OrbitControls.js');var _HDRLoaderjs = require('three/addons/loaders/HDRLoader.js');var de=new f.Vector3(0,0,1),Y=(e,r,t,n)=>{switch(e){case"mm":return r;case"cm":return t;default:return n}},Nn= exports.p =function(e,r){let t=tt(r||{}),n=ot(t),i=dt(t,e),o=ut(e,t),a=pt(i,e,t),c=te({scene:n,perspective:i,controls:a,onActiveCameraChange:()=>{}}),s=()=>c.getActiveCamera();st(n,t),lt(n,t),_optionalChain([t, 'access', _11 => _11.floor, 'optionalAccess', _12 => _12.enabled])&&ct(n,t);let l=t.grid.enabled?ne({cellSize:t.grid.cellSize,majorEvery:t.grid.majorEvery,cellColor:t.grid.cellColor,majorColor:t.grid.majorColor,fadeDistance:t.grid.fadeDistance,plane:t.grid.plane}):null;l&&n.add(l.object);let d=t.gizmo.enabled?re({camera:i,domElement:e,controls:a,controller:c}):null,u=_nullishCoalesce(e.parentElement, () => (e)),p=t.measure.enabled?le(u,n):null,E=t.measure.enabled&&p?ce({canvas:e,scene:n,getActiveCamera:s,labelLayer:p,options:{snapPixels:t.measure.snapPixels,color:t.measure.color,labelClassName:t.measure.labelClassName,format:t.measure.format}}):null,m=t.events.enableEventHandlers!==!1?mt(e,n,s,i,a,t):{dispose:()=>{},fitToView:()=>{},clearSelection:()=>{}},v=C=>{if(_optionalChain([E, 'optionalAccess', _13 => _13.handleClick, 'call', _14 => _14(C)])){C.stopImmediatePropagation();return}_optionalChain([d, 'optionalAccess', _15 => _15.handleClick, 'call', _16 => _16(C)])&&C.stopImmediatePropagation()};(d||E)&&e.addEventListener("click",v,{capture:!0});let S=C=>{ae(C,{color:t.edges.color,width:t.edges.width,thresholdAngle:t.edges.thresholdAngle})},T=e.parentElement,b=()=>T?{width:T.clientWidth,height:T.clientHeight}:{width:window.innerWidth,height:window.innerHeight},H=null,h=()=>{let{width:C,height:O}=b(),k=Math.min(window.devicePixelRatio,2),G=ie(o,n,s(),Math.max(1,C),Math.max(1,O),{toneMapping:_nullishCoalesce(t.render.toneMapping, () => (f.NeutralToneMapping)),toneMappingExposure:_nullishCoalesce(t.render.toneMappingExposure, () => (1)),aoIntensity:t.render.aoIntensity});return G.setSize(Math.max(1,C),Math.max(1,O),k),G},g=C=>{C&&!H?H=h():!C&&H&&(H.dispose(),H=null)};t.render.ambientOcclusion&&(H=h());let{animate:R,dispose:M}=it(o,n,i,s,c,a,b,t.events.onFrame,l,d,()=>H,p);R();let y=_optionalChain([t, 'access', _17 => _17.environment, 'optionalAccess', _18 => _18.sceneUp])||de;return n.up.set(y.x,y.y,y.z),{scene:n,camera:i,controls:a,renderer:o,cameraController:c,grid:l,gizmo:d,measureTool:E,applyEdges:S,setAmbientOcclusion:g,dispose:()=>{M(),m.dispose(),(d||E)&&e.removeEventListener("click",v,{capture:!0}),_optionalChain([E, 'optionalAccess', _19 => _19.dispose, 'call', _20 => _20()]),_optionalChain([p, 'optionalAccess', _21 => _21.dispose, 'call', _22 => _22()]),_optionalChain([d, 'optionalAccess', _23 => _23.dispose, 'call', _24 => _24()]),_optionalChain([l, 'optionalAccess', _25 => _25.dispose, 'call', _26 => _26()]),_optionalChain([H, 'optionalAccess', _27 => _27.dispose, 'call', _28 => _28()]),a.dispose(),o.dispose(),n.traverse(C=>{let O=C;!O.geometry&&!O.material||(_optionalChain([O, 'access', _29 => _29.geometry, 'optionalAccess', _30 => _30.dispose, 'call', _31 => _31()]),Array.isArray(O.material)?O.material.forEach(k=>k.dispose()):_optionalChain([O, 'access', _32 => _32.material, 'optionalAccess', _33 => _33.dispose, 'call', _34 => _34()]))})},fitToView:m.fitToView,clearSelection:m.clearSelection}};function tt(e){let r=e.sceneScale||"m",n={mm:{cameraDistance:20,near:.1,far:2e3,floorSize:100,lightDistance:10,lightHeight:20,minDistance:.1,shadowSize:100,scaleFactor:1e3},cm:{cameraDistance:20,near:.1,far:2e3,floorSize:100,lightDistance:25,lightHeight:50,minDistance:.1,shadowSize:100,scaleFactor:100},m:{cameraDistance:10,near:.01,far:2e3,floorSize:50,lightDistance:25,lightHeight:50,minDistance:.001,shadowSize:100,scaleFactor:1},inches:{cameraDistance:15,near:.1,far:2e3,floorSize:80,lightDistance:20,lightHeight:40,minDistance:.1,shadowSize:80,scaleFactor:39.37},feet:{cameraDistance:8,near:.1,far:2e3,floorSize:40,lightDistance:15,lightHeight:30,minDistance:.1,shadowSize:60,scaleFactor:3.28084}}[r];return{sceneScale:r,camera:{position:_optionalChain([e, 'access', _35 => _35.camera, 'optionalAccess', _36 => _36.position])||new f.Vector3(-n.cameraDistance,n.cameraDistance,n.cameraDistance),fov:_optionalChain([e, 'access', _37 => _37.camera, 'optionalAccess', _38 => _38.fov])||20,near:_optionalChain([e, 'access', _39 => _39.camera, 'optionalAccess', _40 => _40.near])||n.near,far:_optionalChain([e, 'access', _41 => _41.camera, 'optionalAccess', _42 => _42.far])||n.far,target:_optionalChain([e, 'access', _43 => _43.camera, 'optionalAccess', _44 => _44.target])||new f.Vector3(0,0,0)},lighting:{enableSunlight:_nullishCoalesce(_optionalChain([e, 'access', _45 => _45.lighting, 'optionalAccess', _46 => _46.enableSunlight]), () => (!0)),sunlightIntensity:_optionalChain([e, 'access', _47 => _47.lighting, 'optionalAccess', _48 => _48.sunlightIntensity])||1,sunlightPosition:_optionalChain([e, 'access', _49 => _49.lighting, 'optionalAccess', _50 => _50.sunlightPosition])||new f.Vector3(n.lightDistance,n.lightHeight,n.lightDistance),ambientLightColor:_optionalChain([e, 'access', _51 => _51.lighting, 'optionalAccess', _52 => _52.ambientLightColor])||new f.Color(4210752),ambientLightIntensity:_optionalChain([e, 'access', _53 => _53.lighting, 'optionalAccess', _54 => _54.ambientLightIntensity])||1,sunlightColor:_optionalChain([e, 'access', _55 => _55.lighting, 'optionalAccess', _56 => _56.sunlightColor])||16777215},environment:{hdrPath:_optionalChain([e, 'access', _57 => _57.environment, 'optionalAccess', _58 => _58.hdrPath])||"/baseHDR.hdr",backgroundColor:_optionalChain([e, 'access', _59 => _59.environment, 'optionalAccess', _60 => _60.backgroundColor])||new f.Color(15790320),enableEnvironmentLighting:_nullishCoalesce(_optionalChain([e, 'access', _61 => _61.environment, 'optionalAccess', _62 => _62.enableEnvironmentLighting]), () => (!0)),sceneUp:_optionalChain([e, 'access', _63 => _63.environment, 'optionalAccess', _64 => _64.sceneUp])||de,showEnvironment:_nullishCoalesce(_optionalChain([e, 'access', _65 => _65.environment, 'optionalAccess', _66 => _66.showEnvironment]), () => (!1))},floor:{enabled:_nullishCoalesce(_optionalChain([e, 'access', _67 => _67.floor, 'optionalAccess', _68 => _68.enabled]), () => (!1)),size:_optionalChain([e, 'access', _69 => _69.floor, 'optionalAccess', _70 => _70.size])||n.floorSize,color:_optionalChain([e, 'access', _71 => _71.floor, 'optionalAccess', _72 => _72.color])||new f.Color(8421504),roughness:_optionalChain([e, 'access', _73 => _73.floor, 'optionalAccess', _74 => _74.roughness])||.7,metalness:_optionalChain([e, 'access', _75 => _75.floor, 'optionalAccess', _76 => _76.metalness])||0,receiveShadow:_nullishCoalesce(_optionalChain([e, 'access', _77 => _77.floor, 'optionalAccess', _78 => _78.receiveShadow]), () => (!0))},render:{enableShadows:_nullishCoalesce(_optionalChain([e, 'access', _79 => _79.render, 'optionalAccess', _80 => _80.enableShadows]), () => (!0)),shadowMapSize:_optionalChain([e, 'access', _81 => _81.render, 'optionalAccess', _82 => _82.shadowMapSize])||2048,antialias:_nullishCoalesce(_optionalChain([e, 'access', _83 => _83.render, 'optionalAccess', _84 => _84.antialias]), () => (!0)),pixelRatio:_optionalChain([e, 'access', _85 => _85.render, 'optionalAccess', _86 => _86.pixelRatio])||Math.min(window.devicePixelRatio,2),toneMapping:_optionalChain([e, 'access', _87 => _87.render, 'optionalAccess', _88 => _88.toneMapping])||f.NeutralToneMapping,toneMappingExposure:_optionalChain([e, 'access', _89 => _89.render, 'optionalAccess', _90 => _90.toneMappingExposure])||1,preserveDrawingBuffer:_nullishCoalesce(_optionalChain([e, 'access', _91 => _91.render, 'optionalAccess', _92 => _92.preserveDrawingBuffer]), () => (!1)),ambientOcclusion:_nullishCoalesce(_optionalChain([e, 'access', _93 => _93.render, 'optionalAccess', _94 => _94.ambientOcclusion]), () => (!1)),aoIntensity:_nullishCoalesce(_optionalChain([e, 'access', _95 => _95.render, 'optionalAccess', _96 => _96.aoIntensity]), () => (1))},controls:{enableDamping:_nullishCoalesce(_optionalChain([e, 'access', _97 => _97.controls, 'optionalAccess', _98 => _98.enableDamping]), () => (!1)),dampingFactor:_optionalChain([e, 'access', _99 => _99.controls, 'optionalAccess', _100 => _100.dampingFactor])||.05,autoRotate:_nullishCoalesce(_optionalChain([e, 'access', _101 => _101.controls, 'optionalAccess', _102 => _102.autoRotate]), () => (!1)),autoRotateSpeed:_optionalChain([e, 'access', _103 => _103.controls, 'optionalAccess', _104 => _104.autoRotateSpeed])||.5,enableZoom:_nullishCoalesce(_optionalChain([e, 'access', _105 => _105.controls, 'optionalAccess', _106 => _106.enableZoom]), () => (!0)),enablePan:_nullishCoalesce(_optionalChain([e, 'access', _107 => _107.controls, 'optionalAccess', _108 => _108.enablePan]), () => (!0)),minDistance:_optionalChain([e, 'access', _109 => _109.controls, 'optionalAccess', _110 => _110.minDistance])||n.minDistance,maxDistance:_optionalChain([e, 'access', _111 => _111.controls, 'optionalAccess', _112 => _112.maxDistance])||1/0},grid:{enabled:_nullishCoalesce(_optionalChain([e, 'access', _113 => _113.grid, 'optionalAccess', _114 => _114.enabled]), () => (!1)),cellSize:_nullishCoalesce(_optionalChain([e, 'access', _115 => _115.grid, 'optionalAccess', _116 => _116.cellSize]), () => (1)),majorEvery:_nullishCoalesce(_optionalChain([e, 'access', _117 => _117.grid, 'optionalAccess', _118 => _118.majorEvery]), () => (10)),cellColor:_nullishCoalesce(_optionalChain([e, 'access', _119 => _119.grid, 'optionalAccess', _120 => _120.cellColor]), () => (8947848)),majorColor:_nullishCoalesce(_optionalChain([e, 'access', _121 => _121.grid, 'optionalAccess', _122 => _122.majorColor]), () => (4473924)),fadeDistance:_nullishCoalesce(_optionalChain([e, 'access', _123 => _123.grid, 'optionalAccess', _124 => _124.fadeDistance]), () => (100)),plane:_nullishCoalesce(_optionalChain([e, 'access', _125 => _125.grid, 'optionalAccess', _126 => _126.plane]), () => ("y"))},gizmo:{enabled:_nullishCoalesce(_optionalChain([e, 'access', _127 => _127.gizmo, 'optionalAccess', _128 => _128.enabled]), () => (!1))},edges:{enabled:_nullishCoalesce(_optionalChain([e, 'access', _129 => _129.edges, 'optionalAccess', _130 => _130.enabled]), () => (!1)),color:_nullishCoalesce(_optionalChain([e, 'access', _131 => _131.edges, 'optionalAccess', _132 => _132.color]), () => (2236962)),width:_nullishCoalesce(_optionalChain([e, 'access', _133 => _133.edges, 'optionalAccess', _134 => _134.width]), () => (1.5)),thresholdAngle:_nullishCoalesce(_optionalChain([e, 'access', _135 => _135.edges, 'optionalAccess', _136 => _136.thresholdAngle]), () => (30))},measure:{enabled:_nullishCoalesce(_optionalChain([e, 'access', _137 => _137.measure, 'optionalAccess', _138 => _138.enabled]), () => (!1)),snapPixels:_optionalChain([e, 'access', _139 => _139.measure, 'optionalAccess', _140 => _140.snapPixels]),color:_optionalChain([e, 'access', _141 => _141.measure, 'optionalAccess', _142 => _142.color]),labelClassName:_optionalChain([e, 'access', _143 => _143.measure, 'optionalAccess', _144 => _144.labelClassName]),format:_optionalChain([e, 'access', _145 => _145.measure, 'optionalAccess', _146 => _146.format])},events:{onBackgroundClicked:_optionalChain([e, 'access', _147 => _147.events, 'optionalAccess', _148 => _148.onBackgroundClicked]),onObjectSelected:_optionalChain([e, 'access', _149 => _149.events, 'optionalAccess', _150 => _150.onObjectSelected]),onMeshMetadataClicked:_optionalChain([e, 'access', _151 => _151.events, 'optionalAccess', _152 => _152.onMeshMetadataClicked]),onMeshDoubleClicked:_optionalChain([e, 'access', _153 => _153.events, 'optionalAccess', _154 => _154.onMeshDoubleClicked]),selectionColor:_optionalChain([e, 'access', _155 => _155.events, 'optionalAccess', _156 => _156.selectionColor])||"#ff0000",enableEventHandlers:_nullishCoalesce(_optionalChain([e, 'access', _157 => _157.events, 'optionalAccess', _158 => _158.enableEventHandlers]), () => (!0)),enableKeyboardControls:_nullishCoalesce(_optionalChain([e, 'access', _159 => _159.events, 'optionalAccess', _160 => _160.enableKeyboardControls]), () => (!0)),enableClickToFocus:_nullishCoalesce(_optionalChain([e, 'access', _161 => _161.events, 'optionalAccess', _162 => _162.enableClickToFocus]), () => (!0)),enableDoubleClickZoom:_nullishCoalesce(_optionalChain([e, 'access', _163 => _163.events, 'optionalAccess', _164 => _164.enableDoubleClickZoom]), () => (!0)),onReady:_optionalChain([e, 'access', _165 => _165.events, 'optionalAccess', _166 => _166.onReady]),onFrame:_optionalChain([e, 'access', _167 => _167.events, 'optionalAccess', _168 => _168.onFrame])}}}var nt=new Set(["grid","floor","label-layer","measure"]);function rt(e){let r=e;for(;r;){if(typeof r.userData.id=="string"&&nt.has(r.userData.id))return!0;r=r.parent}return!1}function ot(e){let r=new f.Scene,t=typeof e.environment.backgroundColor=="string"?new f.Color(e.environment.backgroundColor):e.environment.backgroundColor;return r.background=t||null,r}function at(e,r,t,n,i=200){let o=e.position.clone(),a=r.target.clone(),c=performance.now(),s=d=>1-Math.pow(1-d,3),l=()=>{let d=performance.now()-c,u=s(Math.min(d/i,1));e.position.lerpVectors(o,t,u),r.target.lerpVectors(a,n,u),r.update(),u<1&&requestAnimationFrame(l)};requestAnimationFrame(l)}function it(e,r,t,n,i,o,a,c,s,l,d,u){let p=null,E=performance.now(),m=()=>{let{width:T,height:b}=a();if(T===0||b===0)return;let H=Math.min(window.devicePixelRatio,2),h=Math.round(T*H),g=Math.round(b*H);(e.domElement.width!==h||e.domElement.height!==g)&&(e.setPixelRatio(H),e.setSize(T,b,!1),t.aspect=T/b,t.updateProjectionMatrix(),i.updateAspect(T,b),_optionalChain([d, 'optionalCall', _169 => _169(), 'optionalAccess', _170 => _170.setSize, 'call', _171 => _171(T,b,H)]),_optionalChain([u, 'optionalAccess', _172 => _172.setSize, 'call', _173 => _173(T,b)]))},v=function(){p=requestAnimationFrame(v);let T=performance.now(),b=(T-E)/1e3;E=T,m(),(o.enableDamping||o.autoRotate)&&o.update(),s&&s.update(n().position),l&&l.update(b),_optionalChain([c, 'optionalCall', _174 => _174(b)]);let H=n(),h=_optionalChain([d, 'optionalCall', _175 => _175()]);h?(h.setCamera(H),h.render(b)):e.render(r,H),u&&u.render(r,H),l&&l.render(e)};return{animate:v,dispose:()=>{p!==null&&(cancelAnimationFrame(p),p=null)}}}function st(e,r){r.environment.enableEnvironmentLighting?new (0, _HDRLoaderjs.HDRLoader)().load(r.environment.hdrPath||"/baseHDR.hdr",function(t){if(!_optionalChain([t, 'optionalAccess', _176 => _176.image])){_chunkJZFH67EScjs.e.call(void 0, ).warn("HDR loaded without image data; skipping environment map."),_optionalChain([r, 'access', _177 => _177.events, 'access', _178 => _178.onReady, 'optionalCall', _179 => _179()]);return}t.mapping=f.EquirectangularReflectionMapping,e.environment=t,r.environment.showEnvironment&&(e.background=t),_optionalChain([r, 'access', _180 => _180.events, 'access', _181 => _181.onReady, 'optionalCall', _182 => _182()])},void 0,function(t){_chunkJZFH67EScjs.e.call(void 0, ).warn("HDR texture could not be loaded, falling back to basic lighting:",t),_optionalChain([r, 'access', _183 => _183.events, 'access', _184 => _184.onReady, 'optionalCall', _185 => _185()])}):_optionalChain([r, 'access', _186 => _186.events, 'access', _187 => _187.onReady, 'optionalCall', _188 => _188()])}function lt(e,r){let t=new f.AmbientLight(r.lighting.ambientLightColor,r.lighting.ambientLightIntensity);if(e.add(t),r.lighting.enableSunlight){let n=new f.DirectionalLight(_nullishCoalesce(r.lighting.sunlightColor, () => (16777215)),r.lighting.sunlightIntensity),i=r.lighting.sunlightPosition;if(i&&n.position.set(i.x,i.y,i.z),r.render.enableShadows){n.castShadow=!0;let o=Y(r.sceneScale,.1,10,100);n.shadow.camera.left=-o,n.shadow.camera.right=o,n.shadow.camera.top=o,n.shadow.camera.bottom=-o;let a=Y(r.sceneScale,.001,.1,.5),c=Y(r.sceneScale,1,100,500);n.shadow.camera.near=a,n.shadow.camera.far=c,n.shadow.mapSize.width=r.render.shadowMapSize||2048,n.shadow.mapSize.height=r.render.shadowMapSize||2048,n.shadow.bias=-1e-4,n.shadow.normalBias=.02}e.add(n)}}function ct(e,r){let t=r.floor.size,n=new f.PlaneGeometry(t,t),i=typeof r.floor.color=="string"?new f.Color(r.floor.color):r.floor.color,o=new f.MeshStandardMaterial({color:i,roughness:r.floor.roughness,metalness:r.floor.metalness,side:f.DoubleSide}),a=new f.Mesh(n,o);a.userData.id="floor",a.name="floor",a.rotation.x=-Math.PI/2,a.position.y=0,r.floor.receiveShadow&&r.render.enableShadows&&(a.receiveShadow=!0),e.add(a)}function dt(e,r){let t=r.parentElement,n=t?t.clientWidth:window.innerWidth,i=t?t.clientHeight:window.innerHeight,o=new f.PerspectiveCamera(e.camera.fov,n/i,e.camera.near,e.camera.far),a=e.camera.position;return a&&o.position.set(a.x,a.y,a.z),o}function ut(e,r){let t=new f.WebGLRenderer({antialias:r.render.antialias,canvas:e,alpha:!0,powerPreference:"high-performance",preserveDrawingBuffer:r.render.preserveDrawingBuffer,logarithmicDepthBuffer:!0}),n=e.parentElement,i=n?n.clientWidth:window.innerWidth,o=n?n.clientHeight:window.innerHeight;return n&&(e.style.width="100%",e.style.height="100%",e.style.display="block"),t.setSize(i,o,!1),t.setPixelRatio(r.render.pixelRatio||Math.min(window.devicePixelRatio,2)),r.render.enableShadows&&(t.shadowMap.enabled=!0,t.shadowMap.type=f.VSMShadowMap),t.toneMapping=r.render.toneMapping,t.toneMappingExposure=r.render.toneMappingExposure||1,t.outputColorSpace=f.SRGBColorSpace,t.sortObjects=!0,t}function mt(e,r,t,n,i,o){let a=new Set,c=new Map,s=new f.Raycaster,l=new f.Vector2,d=new f.Vector2,u=h=>{let g=h;for(;g;){if(!g.visible)return!1;g=g.parent}return!0},p=()=>{let h=new f.Box3;if(r.traverse(O=>{let k=O;O.visible&&!rt(O)&&k.geometry&&h.expandByObject(O)}),h.isEmpty()){_chunkJZFH67EScjs.e.call(void 0, ).warn("No objects to fit to view");return}let g=h.getCenter(new f.Vector3),R=h.getSize(new f.Vector3),M=Math.max(R.x,R.y,R.z),y=n.fov*(Math.PI/180),L=M/(2*Math.tan(y/2));L*=1.5;let C=n.position.clone().sub(i.target);C.lengthSq()<1e-12&&C.set(.8,1,1.2),C.normalize(),n.position.copy(g.clone().add(C.multiplyScalar(L))),i.target.copy(g),i.update()},E=typeof o.events.selectionColor=="string"?new f.Color(o.events.selectionColor):o.events.selectionColor instanceof f.Color?o.events.selectionColor:new f.Color("#ff0000"),m=()=>{a.forEach(h=>{h instanceof f.Mesh&&c.has(h)&&(h.material=c.get(h),c.delete(h))}),a.clear()},v=h=>{d.set(h.clientX,h.clientY)},S=h=>{let g=new f.Vector2(h.clientX,h.clientY);if(d.distanceTo(g)>5)return;let R=e.getBoundingClientRect();l.x=(h.clientX-R.left)/R.width*2-1,l.y=-((h.clientY-R.top)/R.height)*2+1,s.setFromCamera(l,t());let M=s.intersectObjects(r.children,!0).filter(y=>u(y.object));if(M.length>0){let y=M[0].object;if(!a.has(y)){if(m(),a.add(y),y instanceof f.Mesh&&y.material instanceof f.Material){c.set(y,y.material);let L=y.material.clone();L.emissive=E.clone(),y.material=L}_optionalChain([o, 'access', _189 => _189.events, 'optionalAccess', _190 => _190.onObjectSelected, 'optionalCall', _191 => _191(y)]),y instanceof f.Mesh&&Object.keys(y.userData).length>0&&_optionalChain([o, 'access', _192 => _192.events, 'optionalAccess', _193 => _193.onMeshMetadataClicked, 'optionalCall', _194 => _194(y.userData)])}}else m(),_optionalChain([o, 'access', _195 => _195.events, 'optionalAccess', _196 => _196.onBackgroundClicked, 'optionalCall', _197 => _197({x:l.x,y:l.y})])},T=h=>{let g=e.getBoundingClientRect();l.x=(h.clientX-g.left)/g.width*2-1,l.y=-((h.clientY-g.top)/g.height)*2+1,s.setFromCamera(l,t());let R=s.intersectObjects(r.children,!0).filter(Te=>u(Te.object));if(R.length===0)return;let M=R[0].object;if(_optionalChain([o, 'access', _198 => _198.events, 'optionalAccess', _199 => _199.onMeshDoubleClicked, 'optionalCall', _200 => _200(M)]),!_optionalChain([o, 'access', _201 => _201.events, 'optionalAccess', _202 => _202.enableDoubleClickZoom]))return;let y=new f.Box3().setFromObject(M);if(y.isEmpty())return;let L=y.getCenter(new f.Vector3),C=y.getSize(new f.Vector3),O=Math.max(C.x,C.y,C.z),k=n.fov*(Math.PI/180),G=O/(2*Math.tan(k/2))*1.5,ye=n.position.clone().sub(i.target).normalize(),Re=L.clone().add(ye.multiplyScalar(G));at(n,i,Re,L)},b=h=>{if(_optionalChain([o, 'access', _203 => _203.events, 'optionalAccess', _204 => _204.enableKeyboardControls]))switch(h.key.toLowerCase()){case"f":h.preventDefault(),p();break;case"escape":h.preventDefault(),m();break;case" ":h.preventDefault(),p();break}};return _optionalChain([o, 'access', _205 => _205.events, 'optionalAccess', _206 => _206.enableClickToFocus])&&(e.addEventListener("mousedown",v),e.addEventListener("click",S),e.addEventListener("dblclick",T)),_optionalChain([o, 'access', _207 => _207.events, 'optionalAccess', _208 => _208.enableKeyboardControls])&&(e.setAttribute("tabindex","0"),e.addEventListener("keydown",b)),{dispose:()=>{e.removeEventListener("mousedown",v),e.removeEventListener("click",S),e.removeEventListener("dblclick",T),e.removeEventListener("keydown",b),m()},fitToView:p,clearSelection:m}}function pt(e,r,t){let n=new (0, _OrbitControlsjs.OrbitControls)(e,r),i=t.camera.target;return i&&n.target.set(i.x,i.y,i.z),n.enableDamping=t.controls.enableDamping||!1,n.dampingFactor=t.controls.dampingFactor||.05,n.autoRotate=t.controls.autoRotate||!1,n.autoRotateSpeed=t.controls.autoRotateSpeed||.5,n.enableZoom=_nullishCoalesce(t.controls.enableZoom, () => (!0)),n.enablePan=_nullishCoalesce(t.controls.enablePan, () => (!0)),n.minDistance=t.controls.minDistance||.001,n.maxDistance=t.controls.maxDistance||1/0,n.screenSpacePanning=!1,n.maxPolarAngle=Math.PI,n.update(),n}var Tt={};_chunkJZFH67EScjs.a.call(void 0, Tt,{CONCRETE_MATERIAL:()=>ht,EMISSIVE_MATERIAL:()=>ft,GLASS_MATERIAL:()=>bt,METAL_MATERIAL:()=>Et,PLASTIC_MATERIAL:()=>gt,RUBBER_MATERIAL:()=>yt,WOOD_MATERIAL:()=>Rt});var ft=new x.MeshPhysicalMaterial({color:0,emissive:new x.Color(16777215),emissiveIntensity:5,metalness:0,roughness:.2,clearcoat:.3,clearcoatRoughness:.2,depthWrite:!0,depthTest:!0,transparent:!1,alphaTest:0,polygonOffset:!0,side:x.FrontSide,dithering:!0}),Et=new x.MeshPhysicalMaterial({color:new x.Color(0),metalness:.9,roughness:.3,envMapIntensity:1.2,clearcoat:.3,clearcoatRoughness:.2,reflectivity:1,ior:2.5,thickness:1,depthWrite:!0,transparent:!1,alphaTest:0,depthTest:!0,polygonOffset:!0,side:x.FrontSide,dithering:!0}),ht=new x.MeshPhysicalMaterial({color:new x.Color(13421772),metalness:0,roughness:.92,envMapIntensity:.15,clearcoat:.05,clearcoatRoughness:.9,reflectivity:.15,transmission:0,ior:1.45,thickness:0,depthWrite:!0,transparent:!1,alphaTest:.5,depthTest:!0,polygonOffset:!0,side:x.FrontSide,dithering:!0}),gt=new x.MeshPhysicalMaterial({color:new x.Color(16777215),metalness:0,roughness:.3,envMapIntensity:.5,clearcoat:.5,clearcoatRoughness:.1,reflectivity:.5,ior:1.4,transmission:0,transparent:!1,depthWrite:!0,side:x.FrontSide,dithering:!0,polygonOffset:!0,polygonOffsetFactor:1,polygonOffsetUnits:1}),bt=new x.MeshPhysicalMaterial({color:new x.Color(16777215),metalness:0,roughness:0,transmission:.95,transparent:!0,opacity:.3,envMapIntensity:1,clearcoat:1,clearcoatRoughness:0,ior:1.52,reflectivity:.9,thickness:1,side:x.DoubleSide,polygonOffset:!0,polygonOffsetFactor:1,polygonOffsetUnits:1}),yt=new x.MeshPhysicalMaterial({color:new x.Color(1710618),metalness:0,roughness:.9,envMapIntensity:.2,clearcoat:.1,clearcoatRoughness:.8,reflectivity:.2,ior:1.3,transmission:0,depthWrite:!0,side:x.FrontSide,polygonOffset:!0,polygonOffsetFactor:1,polygonOffsetUnits:1}),Rt=new x.MeshPhysicalMaterial({color:new x.Color(8934707),metalness:0,roughness:.7,envMapIntensity:.3,clearcoat:.3,clearcoatRoughness:.4,reflectivity:.3,ior:1.3,transmission:0,depthWrite:!0,side:x.FrontSide,dithering:!0,polygonOffset:!0,polygonOffsetFactor:1,polygonOffsetUnits:1});function U(e,r,t,n=!0){return n?{x:e,y:t,z:-r}:{x:e,y:r,z:t}}var vt="#ffffff",X=12,Mt=4e-4,xt=12,St=.05;function ue(e,r={}){if(!e||e.length===0)return[];let{rhino:t,applyTransforms:n=!0}=r,i=[];for(let o of e)switch(o.kind){case"curve":{let a=Dt(o,t,n);a&&i.push(a);break}case"point":{i.push(Lt(o,n));break}default:{let a=o;_chunkJZFH67EScjs.e.call(void 0, ).warn(`Skipping unknown display item kind: ${String(a.kind)}`);break}}return i}var At=2;function Dt(e,r,t){if(!r)return _chunkJZFH67EScjs.e.call(void 0, ).warn("No rhino3dm instance provided; skipping curve display item."),null;let n=Ot(e.json,r);if(!n)return null;let i=Pt(n,t);if(i.length<2)return null;let o=[];for(let u of i)o.push(u.x,u.y,u.z);let a=new _LineGeometryjs.LineGeometry;a.setPositions(o);let c=me(e.color,e.opacity),s=new (0, _LineMaterialjs.LineMaterial)({color:c.color}),l=s;l.linewidth=_nullishCoalesce(e.width, () => (At)),l.transparent=c.transparent,l.opacity=c.opacity;let d=new (0, _Line2js.Line2)(a,s);return d.computeLineDistances(),d.name=e.name,d.userData={id:e.id,layer:e.layer,kind:"curve",metadata:e.metadata},d}function Lt(e,r){let{x:t,y:n,z:i}=U(e.position.X,e.position.Y,e.position.Z,r),o=new V.BufferGeometry;o.setAttribute("position",new V.Float32BufferAttribute([t,n,i],3));let a=new V.PointsMaterial({...me(e.color,e.opacity),size:6,sizeAttenuation:!1}),c=new V.Points(o,a);return c.name=e.name,c.userData={id:e.id,layer:e.layer,kind:"point",metadata:e.metadata},c}function Ot(e,r){try{let t=JSON.parse(e),n=r.CommonObject.decode(t);return n&&typeof n.pointAt=="function"?n:(_chunkJZFH67EScjs.e.call(void 0, ).warn("Decoded display-item JSON is not a curve; skipping."),null)}catch(t){return _chunkJZFH67EScjs.e.call(void 0, ).warn("Failed to decode curve display item JSON:",t),null}}function Pt(e,r){let t=It(e,r);return t||Bt(e,r)}function It(e,r){if(!e.isPolyline())return null;let t=e.tryGetPolyline(),n=Array.isArray(t)?t[1]:t;if(!n||typeof n.count!="number"||n.count<2)return null;let i=[];for(let o=0;o<n.count;o++){let a=n.get(o),{x:c,y:s,z:l}=U(a[0],a[1],a[2],r);i.push(new V.Vector3(c,s,l))}return i}function Bt(e,r){let t=e.domain,n=t[0],o=t[1]-n,a=l=>{let d=e.pointAt(l),{x:u,y:p,z:E}=U(d[0],d[1],d[2],r);return new V.Vector3(u,p,E)},c=Vt(e),s=[a(n)];for(let l=0;l<X;l++){let d=n+o*l/X,u=n+o*(l+1)/X;q(d,a(d),u,a(u),a,c,xt,s),s.push(a(u))}return s}function q(e,r,t,n,i,o,a,c){if(a<=0)return;let s=(e+t)/2,l=i(s),d=zt(l,r,n),u=Ft(r,l,n);d<=o&&u<=St||(q(e,r,s,l,i,o,a-1,c),c.push(l),q(s,l,t,n,i,o,a-1,c))}function Vt(e){let r=e.getBoundingBox(),t=r.min,n=r.max,i=Math.hypot(n[0]-t[0],n[1]-t[1],n[2]-t[2]);return Math.max(i*Mt,1e-6)}function Ft(e,r,t){let n=r.clone().sub(e),i=t.clone().sub(r),o=n.length(),a=i.length();if(o===0||a===0)return 0;let c=Math.max(-1,Math.min(1,n.dot(i)/(o*a)));return Math.acos(c)}function zt(e,r,t){let n=t.clone().sub(r),i=n.lengthSq();if(i===0)return e.distanceTo(r);let o=Math.max(0,Math.min(1,e.clone().sub(r).dot(n)/i)),a=r.clone().addScaledVector(n,o);return e.distanceTo(a)}function me(e,r){let t=_nullishCoalesce(r, () => (1));return{color:new V.Color(_nullishCoalesce(e, () => (vt))),transparent:t<1,opacity:t}}var pe=1096174675,fe= exports.t =1,Z= exports.u =1,Ee=12,he=56;function J(e){let r=kt(e),t=new DataView(r.buffer,r.byteOffset,r.byteLength);if(r.byteLength<Ee)throw z("Blob too small to contain SLVA header.",{expectedBytes:Ee,availableBytes:r.byteLength});let n=0,i=t.getUint32(n,!0);if(n+=4,i!==pe)throw z(`Invalid SLVA magic: 0x${i.toString(16)}`,{expectedMagic:`0x${pe.toString(16)}`,actualMagic:`0x${i.toString(16)}`});let o=t.getUint32(n,!0);if(n+=4,o!==fe)throw z(`Unsupported SLVA version: ${o}`,{expectedVersion:fe,actualVersion:o});let a=t.getUint32(n,!0);if(n+=4,n+a>r.byteLength)throw z("Insufficient data to read metadata JSON.",{expectedBytes:a,availableBytes:r.byteLength-n,offset:n});let c=r.subarray(n,n+a);n+=a;let s;try{s=JSON.parse(jt(c))}catch(C){throw z(`Failed to parse metadata JSON: ${C instanceof Error?C.message:String(C)}`,{metadataLen:a})}if(n+he>r.byteLength)throw z("Insufficient data to read geometry header.",{expectedBytes:he,availableBytes:r.byteLength-n,offset:n});let l=t.getUint32(n,!0);n+=4;let d=t.getFloat64(n,!0);n+=8;let u=t.getFloat64(n,!0);n+=8;let p=t.getFloat64(n,!0);n+=8;let E=t.getFloat64(n,!0);n+=8;let m=t.getFloat64(n,!0);n+=8;let v=t.getFloat64(n,!0);n+=8;let S=t.getUint32(n,!0);n+=4;let T=(l&Z)!==0,b=S*3,h=b*(T?4:2);if(n+h>r.byteLength)throw z("Insufficient data to read vertices.",{expectedBytes:h,availableBytes:r.byteLength-n,offset:n,useFloat32:T,vertexCount:S});let g=r.byteOffset+n,R=T?Gt(r.buffer,g,b):_t(r.buffer,g,b);if(n+=h,n+4>r.byteLength)throw z("Insufficient data to read index count.",{expectedBytes:4,availableBytes:r.byteLength-n,offset:n});let M=t.getUint32(n,!0);n+=4;let y=M*4;if(n+y>r.byteLength)throw z("Insufficient data to read indices.",{expectedBytes:y,availableBytes:r.byteLength-n,offset:n,indexCount:M});let L=Nt(r.buffer,r.byteOffset+n,M);return{metadata:s,flags:l,vertices:R,indices:L,origin:[d,u,p],scale:[E,m,v]}}function kt(e){return typeof e=="string"?_chunkJZFH67EScjs.o.call(void 0, e):e instanceof Uint8Array?e:new Uint8Array(e)}function jt(e){if(typeof TextDecoder<"u")return new TextDecoder("utf-8").decode(e);if(typeof globalThis.Buffer<"u")return globalThis.Buffer.from(e).toString("utf-8");throw new (0, _chunkJZFH67EScjs.d)("No UTF-8 decoder available in this environment.",_chunkJZFH67EScjs.c.INVALID_STATE)}function _t(e,r,t){if(t===0)return new Int16Array(0);if(r%2===0)return new Int16Array(e,r,t);let n=new Uint8Array(t*2);return n.set(new Uint8Array(e,r,t*2)),new Int16Array(n.buffer)}function Gt(e,r,t){if(t===0)return new Float32Array(0);if(r%4===0)return new Float32Array(e,r,t);let n=new Uint8Array(t*4);return n.set(new Uint8Array(e,r,t*4)),new Float32Array(n.buffer)}function Nt(e,r,t){if(t===0)return new Uint32Array(0);if(r%4===0)return new Uint32Array(e,r,t);let n=new Uint8Array(t*4);return n.set(new Uint8Array(e,r,t*4)),new Uint32Array(n.buffer)}function z(e,r){return new (0, _chunkJZFH67EScjs.d)(e,_chunkJZFH67EScjs.c.VALIDATION_ERROR,{context:r})}async function ge(e,r){let{debug:t=!1}=_nullishCoalesce(r, () => ({})),n=t?performance.now():0;try{let i=performance.now(),o=JSON.parse(e),a=performance.now()-i;return await Ut(o,r,{parseTime:a,perfStart:n})}catch(i){return _chunkJZFH67EScjs.e.call(void 0, ).error("Error parsing mesh batch:",i),[]}}async function Ut(e,r,t){let{mergeByMaterial:n=!0,applyTransforms:i=!0,scaleFactor:o=1,debug:a=!1}=_nullishCoalesce(r, () => ({})),{parseTime:c=0,perfStart:s=a?performance.now():0}=_nullishCoalesce(t, () => ({}));try{let l=performance.now(),d=J(e.compressedData),u=performance.now()-l,p=a?Zt(e.compressedData):0;return be(d,{mergeByMaterial:n,applyTransforms:i,scaleFactor:o,debug:a,parseTime:c,decodeTime:u,perfStart:s,blobBytes:p,fallback:{materials:e.materials,groups:e.groups,sourceComponentId:e.sourceComponentId}})}catch(l){return _chunkJZFH67EScjs.e.call(void 0, ).error("Error parsing mesh batch object:",l),[]}}async function or(e,r){let{mergeByMaterial:t=!0,applyTransforms:n=!0,scaleFactor:i=1,debug:o=!1}=_nullishCoalesce(r, () => ({})),a=o?performance.now():0;try{let c=performance.now(),s=J(e),l=performance.now()-c,d=e.byteLength;return be(s,{mergeByMaterial:t,applyTransforms:n,scaleFactor:i,debug:o,parseTime:0,decodeTime:l,perfStart:a,blobBytes:d})}catch(c){return _chunkJZFH67EScjs.e.call(void 0, ).error("Error parsing mesh batch blob:",c),[]}}function be(e,r){let{mergeByMaterial:t,applyTransforms:n,scaleFactor:i,debug:o,parseTime:a,decodeTime:c,perfStart:s,blobBytes:l,fallback:d}=r,u=_nullishCoalesce(_nullishCoalesce(e.metadata.materials, () => (_optionalChain([d, 'optionalAccess', _209 => _209.materials]))), () => ([])),p=_nullishCoalesce(_nullishCoalesce(e.metadata.groups, () => (_optionalChain([d, 'optionalAccess', _210 => _210.groups]))), () => ([])),E=_nullishCoalesce(e.metadata.sourceComponentId, () => (_optionalChain([d, 'optionalAccess', _211 => _211.sourceComponentId]))),m=(e.flags&Z)!==0,v=m?$t(e.vertices,n):Wt(e.vertices,e.origin,e.scale,n);if(o){let h=e.vertices.byteLength+e.indices.byteLength;_chunkJZFH67EScjs.e.call(void 0, ).debug("Mesh Batch Stats:"),_chunkJZFH67EScjs.e.call(void 0, ).debug(` Materials: ${u.length} | Groups: ${p.length}`),_chunkJZFH67EScjs.e.call(void 0, ).debug(` Vertices: ${e.vertices.length/3} | Indices: ${e.indices.length}`),_chunkJZFH67EScjs.e.call(void 0, ).debug(` Format: ${m?"float32":"int16 quantized"}`),_chunkJZFH67EScjs.e.call(void 0, ).debug(` Blob: ${(l/1024/1024).toFixed(2)} MB | Geometry on wire: ${(h/1024/1024).toFixed(2)} MB`)}let S=performance.now(),T=u.map(Yt),b=[];for(let h of p)if(t&&h.meshes.length>1){let g=Xt(h,v,e.indices,T);g.userData.sourceComponentId=_nullishCoalesce(E, () => (null)),b.push(g)}else{let g=qt(h,v,e.indices,T);for(let R of g)R.userData.sourceComponentId=_nullishCoalesce(E, () => (null));b.push(...g)}if(i!==1)for(let h of b)h.scale.set(i,i,i);let H=performance.now()-S;if(o){let h=performance.now()-s;_chunkJZFH67EScjs.e.call(void 0, ).debug("Performance:"),a>0&&_chunkJZFH67EScjs.e.call(void 0, ).debug(` Parse JSON: ${a.toFixed(2)}ms`),_chunkJZFH67EScjs.e.call(void 0, ).debug(` Decode binary: ${c.toFixed(2)}ms`),_chunkJZFH67EScjs.e.call(void 0, ).debug(` Create Meshes: ${H.toFixed(2)}ms`),_chunkJZFH67EScjs.e.call(void 0, ).debug(` Total: ${h.toFixed(2)}ms`)}return Promise.resolve(b)}function Wt(e,r,t,n){let i=new Float32Array(e.length),o=r[0],a=r[1],c=r[2],s=t[0],l=t[1],d=t[2];if(n)for(let u=0;u<e.length;u+=3){let p=o+(e[u]+32767)*s,E=a+(e[u+1]+32767)*l,m=c+(e[u+2]+32767)*d;i[u]=p,i[u+1]=m,i[u+2]=-E}else for(let u=0;u<e.length;u+=3)i[u]=o+(e[u]+32767)*s,i[u+1]=a+(e[u+1]+32767)*l,i[u+2]=c+(e[u+2]+32767)*d;return i}function $t(e,r){if(!r)return e;let t=new Float32Array(e.length);for(let n=0;n<e.length;n+=3){let i=e[n],o=e[n+1],a=e[n+2];t[n]=i,t[n+1]=a,t[n+2]=-o}return t}function Yt(e){let r=Q(e.color);return new I.MeshPhysicalMaterial({color:r,metalness:e.metalness,roughness:e.roughness,opacity:e.opacity,transparent:e.transparent,side:I.DoubleSide,polygonOffset:!0,polygonOffsetFactor:.5,polygonOffsetUnits:.5,depthWrite:!0,depthTest:!0})}function Xt(e,r,t,n){let i=0,o=0;for(let m of e.meshes)i+=m.vertexCount,o+=m.indexCount;let a=new Float32Array(i*3),c=new Uint32Array(o),s=0,l=0;for(let m of e.meshes){let v=m.vertexStart*3,S=m.vertexCount*3;a.set(r.subarray(v,v+S),s*3);let T=t.subarray(m.indexStart,m.indexStart+m.indexCount),b=s-m.vertexStart;if(b===0)c.set(T,l);else for(let H=0;H<T.length;H++)c[l+H]=T[H]+b;s+=m.vertexCount,l+=m.indexCount}let d=new I.BufferGeometry;d.setAttribute("position",new I.BufferAttribute(a,3)),d.setIndex(new I.BufferAttribute(c,1)),d.computeVertexNormals();let u=new I.Mesh(d,n[e.materialId]),p=e.meshes[0],E=e.meshes.map(m=>m.name).filter(m=>m&&m.length>0);return u.name=E.length>0?E[0]:`merged_material_${e.materialId}`,u.castShadow=!0,u.receiveShadow=!0,u.userData={name:u.name,layer:_nullishCoalesce(_optionalChain([p, 'optionalAccess', _212 => _212.layer]), () => ("")),originalIndex:_nullishCoalesce(_optionalChain([p, 'optionalAccess', _213 => _213.originalIndex]), () => (0)),metadata:_nullishCoalesce(_optionalChain([p, 'optionalAccess', _214 => _214.metadata]), () => ({})),mergedFrom:e.meshes.slice(1).map(m=>({name:m.name,layer:m.layer,originalIndex:m.originalIndex}))},u}function qt(e,r,t,n){let i=[];for(let o of e.meshes){let a=o.vertexStart*3,c=o.vertexCount*3,s=r.slice(a,a+c),l=t.subarray(o.indexStart,o.indexStart+o.indexCount),d=new Uint32Array(l.length),u=o.vertexStart;for(let m=0;m<l.length;m++)d[m]=l[m]-u;let p=new I.BufferGeometry;p.setAttribute("position",new I.BufferAttribute(s,3)),p.setIndex(new I.BufferAttribute(d,1)),p.computeVertexNormals();let E=new I.Mesh(p,n[e.materialId]);E.name=o.name,E.userData={name:o.name,layer:_nullishCoalesce(o.layer, () => ("")),originalIndex:o.originalIndex,metadata:_nullishCoalesce(o.metadata, () => ({}))},E.castShadow=!0,E.receiveShadow=!0,i.push(E)}return i}function Zt(e){return Math.floor(e.length*3/4)}var Jt={Millimeters:1/1e3,Centimeters:1/100,Meters:1,Inches:1/39.37,Feet:1/3.28084},Kt="Display";async function dr(e,r){let t=performance.now(),n=[],{allowScaling:i=!0,allowAutoPosition:o=!0,rhino:a,debug:c=!1,parsing:s={}}=_nullishCoalesce(r, () => ({}));try{let l=i?Qt(e.modelunits):1;return await en(e,n,l,s,a,c),o&&on(n),n}catch(l){throw an(l,n),l}finally{c&&ln(t)}}function Qt(e){return _nullishCoalesce(Jt[e], () => (1))}async function en(e,r,t,n,i,o){for(let a of e.values){let c=a.InnerTree;for(let s in c){let l=c[s];l&&await tn(l,r,t,n,i,o)}}}async function tn(e,r,t,n,i,o){for(let a of e){if(!a.type.includes(Kt))continue;let c={mergeByMaterial:!0,applyTransforms:!0,debug:!1,...n},s=await ge(a.data,c),l=ue(nn(a.data),{rhino:i,applyTransforms:c.applyTransforms}),d=[...s,...l];if(t!==1)for(let u of d)u.scale.set(t,t,t);r.push(...d),o&&_chunkJZFH67EScjs.e.call(void 0, ).debug(`Extracted ${s.length} meshes and ${l.length} items from batch`)}}function nn(e){return_optionalChain([(typeof e=="string"?rn(e):e), 'optionalAccess', _215 => _215.items])}function rn(e){try{return JSON.parse(e)}catch (e4){return}}function on(e){if(e.length===0)return;let t=_(e).min.y;ee(e,t)}function an(e,r){_chunkJZFH67EScjs.e.call(void 0, ).error("An unexpected error occurred:",e),sn(r)}function sn(e){for(let r of e){let t=r;t.geometry&&t.geometry.dispose(),t.material&&(Array.isArray(t.material)?t.material.forEach(n=>n.dispose()):t.material.dispose())}}function ln(e){let r=performance.now()-e;_chunkJZFH67EScjs.e.call(void 0, ).info("Time to process meshes:",`${r.toFixed(2)}ms`)}exports.a = dn; exports.b = Q; exports.c = ee; exports.d = _; exports.e = te; exports.f = ne; exports.g = re; exports.h = N; exports.i = ae; exports.j = ke; exports.k = Rn; exports.l = ie; exports.m = le; exports.n = Ke; exports.o = ce; exports.p = Nn; exports.q = Tt; exports.r = ue; exports.s = pe; exports.t = fe; exports.u = Z; exports.v = J; exports.w = ge; exports.x = Ut; exports.y = or; exports.z = Jt; exports.A = dr;
54
- //# sourceMappingURL=chunk-7RLHTZZQ.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/home/runner/work/selva-compute/selva-compute/dist/chunk-7RLHTZZQ.cjs","../src/features/visualization/threejs/three-helpers.ts","../src/features/visualization/threejs/grid.ts","../src/features/visualization/threejs/view-gizmo.ts"],"names":["CAMERA_CONFIG","updateScene","scene","meshes","camera","controls","initialPositionSet","clearScene","mesh","unionBoundingBox","computeCombinedBoundingBox","center","size","maxDim","distance","parseColor","colorString","getLogger","trimmed","hex"],"mappings":"AAAA,2/BAAqE,ybCA9C,IAKjBA,CAAAA,CAAgB,CACrB,cAAA,CAAgB,GAAA,CAChB,eAAA,CAAiB,GAAA,CACjB,qBAAA,CAAuB,GAAA,CACvB,iBAAA,CAAmB,CAClB,IAAA,CAAM,IAAA,CACN,KAAA,CAAO,IAAA,CACP,MAAA,CAAQ,GACT,CAAA,CACA,gBAAA,CAAkB,CACjB,IAAA,CAAM,GAAA,CACN,KAAA,CAAO,EAAA,CACP,MAAA,CAAQ,EACT,CAAA,CACA,2BAAA,CAA6B,CAC9B,CAAA,CAWO,SAASC,EAAAA,CACfC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACC,CAGD,EAAA,CAFAC,EAAAA,CAAWL,CAAK,CAAA,CAEZC,CAAAA,CAAO,MAAA,GAAW,CAAA,CAAG,MAAA,CAGzBA,CAAAA,CAAO,OAAA,CAASK,CAAAA,EAAS,CACxBN,CAAAA,CAAM,GAAA,CAAIM,CAAI,CACf,CAAC,CAAA,CAGD,IAAMC,CAAAA,CAAmBC,CAAAA,CAA2BP,CAAM,CAAA,CAGpDQ,CAAAA,CAASF,CAAAA,CAAiB,SAAA,CAAU,IAAU,CAAA,CAAA,OAAS,CAAA,CACvDG,CAAAA,CAAOH,CAAAA,CAAiB,OAAA,CAAQ,IAAU,CAAA,CAAA,OAAS,CAAA,CAGnDI,CAAAA,CAAS,IAAA,CAAK,GAAA,CAAID,CAAAA,CAAK,CAAA,CAAGA,CAAAA,CAAK,CAAA,CAAGA,CAAAA,CAAK,CAAC,CAAA,CAuB9C,EAAA,CAnBmBC,CAAAA,CAAS,IAAA,CAAK,GAAA,CAAID,CAAAA,CAAK,CAAA,EAAK,CAAA,CAAGA,CAAAA,CAAK,CAAA,EAAK,CAAA,CAAGA,CAAAA,CAAK,CAAA,EAAK,CAAC,CAAA,CAEzDZ,CAAAA,CAAc,qBAAA,EAAyBa,CAAAA,CAASb,CAAAA,CAAc,cAAA,CAAA,CAE9EI,CAAAA,CAAO,IAAA,CAAOS,CAAAA,CAASb,CAAAA,CAAc,iBAAA,CAAkB,IAAA,CACvDI,CAAAA,CAAO,GAAA,CAAMS,CAAAA,CAASb,CAAAA,CAAc,gBAAA,CAAiB,IAAA,CAAA,CAC3Ca,CAAAA,CAASb,CAAAA,CAAc,eAAA,CAAA,CAEjCI,CAAAA,CAAO,IAAA,CAAOS,CAAAA,CAASb,CAAAA,CAAc,iBAAA,CAAkB,KAAA,CACvDI,CAAAA,CAAO,GAAA,CAAMS,CAAAA,CAASb,CAAAA,CAAc,gBAAA,CAAiB,KAAA,CAAA,CAAA,CAGrDI,CAAAA,CAAO,IAAA,CAAO,IAAA,CAAK,GAAA,CAAI,GAAA,CAAMS,CAAAA,CAASb,CAAAA,CAAc,iBAAA,CAAkB,MAAM,CAAA,CAC5EI,CAAAA,CAAO,GAAA,CAAM,IAAA,CAAK,GAAA,CAAI,GAAA,CAAMS,CAAAA,CAASb,CAAAA,CAAc,gBAAA,CAAiB,MAAM,CAAA,CAAA,CAG3EI,CAAAA,CAAO,sBAAA,CAAuB,CAAA,CAGzBE,CAAAA,CAWJD,CAAAA,CAAS,WAAA,CAAcD,CAAAA,CAAO,IAAA,CAAO,CAAA,CACrCC,CAAAA,CAAS,WAAA,CAAcD,CAAAA,CAAO,GAAA,CAAM,EAAA,CAAA,IAZZ,CACxB,IAAMU,CAAAA,CAAWD,CAAAA,CAASb,CAAAA,CAAc,2BAAA,CAExCI,CAAAA,CAAO,QAAA,CAAS,GAAA,CAAIO,CAAAA,CAAO,CAAA,CAAIG,CAAAA,CAAW,EAAA,CAAKH,CAAAA,CAAO,CAAA,CAAIG,CAAAA,CAAUH,CAAAA,CAAO,CAAA,CAAIG,CAAAA,CAAW,GAAG,CAAA,CAC7FT,CAAAA,CAAS,MAAA,CAAO,IAAA,CAAKM,CAAM,CAAA,CAC3BN,CAAAA,CAAS,WAAA,CAAcD,CAAAA,CAAO,IAAA,CAAO,CAAA,CACrCC,CAAAA,CAAS,WAAA,CAAcD,CAAAA,CAAO,GAAA,CAAM,EAAA,CAEpCC,CAAAA,CAAS,MAAA,CAAO,CACjB,CAKD,CAeO,SAASU,CAAAA,CAAWC,CAAAA,CAAkC,CAC5D,EAAA,CAAI,CAACA,CAAAA,EAAe,OAAOA,CAAAA,EAAgB,QAAA,CAC1C,OAAAC,iCAAAA,CAAU,CAAE,IAAA,CAAK,CAAA,qBAAA,EAAwBD,CAAW,CAAA,aAAA,CAAe,CAAA,CAC5D,IAAU,CAAA,CAAA,KAAA,CAAM,QAAQ,CAAA,CAGhC,IAAME,CAAAA,CAAUF,CAAAA,CAAY,IAAA,CAAK,CAAA,CAGjC,EAAA,CAAI,oBAAA,CAAqB,IAAA,CAAKE,CAAO,CAAA,CACpC,GAAI,CACH,IAAMC,CAAAA,CAAMD,CAAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,CAAIA,CAAAA,CAAU,CAAA,CAAA,EAAIA,CAAO,CAAA,CAAA;AChF/B;AAAA;AAAA;AAAA;AAAA;AAAA;AASE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACwCnB","file":"/home/runner/work/selva-compute/selva-compute/dist/chunk-7RLHTZZQ.cjs","sourcesContent":[null,"import * as THREE from 'three';\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\nimport { getLogger } from '@/core';\n\n// Camera configuration constants\nconst CAMERA_CONFIG = {\n\tHUGE_THRESHOLD: 10000,\n\tLARGE_THRESHOLD: 1000,\n\tSCALE_RATIO_THRESHOLD: 100,\n\tNEAR_PLANE_FACTOR: {\n\t\tTINY: 0.0001,\n\t\tSMALL: 0.001,\n\t\tNORMAL: 0.01\n\t},\n\tFAR_PLANE_FACTOR: {\n\t\tHUGE: 100,\n\t\tLARGE: 50,\n\t\tNORMAL: 20\n\t},\n\tINITIAL_DISTANCE_MULTIPLIER: 4\n};\n\n/**\n * Updates the scene with the given meshes and camera settings.\n * If initialPositionSet is false, it positions the camera and sets the controls target based on the bounding boxes of the meshes.\n * @param scene - The THREE.Scene object to update.\n * @param meshes - An array of THREE.Mesh objects to add to the scene.\n * @param camera - The THREE.PerspectiveCamera object to position.\n * @param controls - The OrbitControls object to update.\n * @param initialPositionSet - A boolean indicating whether the initial position of the camera and controls have been set.\n */\nexport function updateScene(\n\tscene: THREE.Scene,\n\tmeshes: THREE.Object3D[],\n\tcamera: THREE.PerspectiveCamera,\n\tcontrols: OrbitControls,\n\tinitialPositionSet: boolean\n) {\n\tclearScene(scene);\n\n\tif (meshes.length === 0) return;\n\n\t// Add new objects (meshes, lines, points) to scene\n\tmeshes.forEach((mesh) => {\n\t\tscene.add(mesh);\n\t});\n\n\t// Calculate bounds of the new content\n\tconst unionBoundingBox = computeCombinedBoundingBox(meshes);\n\n\t// Get the center of the union bounding box\n\tconst center = unionBoundingBox.getCenter(new THREE.Vector3());\n\tconst size = unionBoundingBox.getSize(new THREE.Vector3());\n\n\t// Calculate a distance that is slightly larger than the largest dimension of the union bounding box\n\tconst maxDim = Math.max(size.x, size.y, size.z);\n\n\t// Always update camera frustum to ensure geometry is visible\n\t// This prevents clipping when geometry size changes significantly\n\tconst scaleRatio = maxDim / Math.min(size.x || 1, size.y || 1, size.z || 1);\n\n\tif (scaleRatio > CAMERA_CONFIG.SCALE_RATIO_THRESHOLD || maxDim > CAMERA_CONFIG.HUGE_THRESHOLD) {\n\t\t// Large scale range detected - use logarithmic depth buffer approach\n\t\tcamera.near = maxDim * CAMERA_CONFIG.NEAR_PLANE_FACTOR.TINY;\n\t\tcamera.far = maxDim * CAMERA_CONFIG.FAR_PLANE_FACTOR.HUGE;\n\t} else if (maxDim > CAMERA_CONFIG.LARGE_THRESHOLD) {\n\t\t// Large scene\n\t\tcamera.near = maxDim * CAMERA_CONFIG.NEAR_PLANE_FACTOR.SMALL;\n\t\tcamera.far = maxDim * CAMERA_CONFIG.FAR_PLANE_FACTOR.LARGE;\n\t} else {\n\t\t// Normal scene\n\t\tcamera.near = Math.max(0.01, maxDim * CAMERA_CONFIG.NEAR_PLANE_FACTOR.NORMAL);\n\t\tcamera.far = Math.max(2000, maxDim * CAMERA_CONFIG.FAR_PLANE_FACTOR.NORMAL);\n\t}\n\n\tcamera.updateProjectionMatrix();\n\n\t// Only reposition camera and controls on first frame\n\tif (!initialPositionSet) {\n\t\tconst distance = maxDim * CAMERA_CONFIG.INITIAL_DISTANCE_MULTIPLIER;\n\n\t\tcamera.position.set(center.x + distance * 0.8, center.y + distance, center.z + distance * 1.2);\n\t\tcontrols.target.copy(center);\n\t\tcontrols.minDistance = camera.near * 2;\n\t\tcontrols.maxDistance = camera.far * 0.9;\n\n\t\tcontrols.update();\n\t} else {\n\t\t// Update control constraints to match new frustum\n\t\tcontrols.minDistance = camera.near * 2;\n\t\tcontrols.maxDistance = camera.far * 0.9;\n\t}\n}\n\n// =========================\n// Helper functions\n// =========================\n\n/**\n * Parses a color string in multiple formats to a THREE.Color object.\n * Supported formats:\n * - Hex: \"#C7A5A5\", \"C7A5A5\"\n * - RGB: \"199, 165, 165\"\n * - CSS named colors: \"red\", \"blue\", etc.\n * @param colorString - The color string to parse.\n * @returns A THREE.Color object.\n */\nexport function parseColor(colorString: string): THREE.Color {\n\tif (!colorString || typeof colorString !== 'string') {\n\t\tgetLogger().warn(`Invalid color input: ${colorString}, using white`);\n\t\treturn new THREE.Color(0xffffff);\n\t}\n\n\tconst trimmed = colorString.trim();\n\n\t// Try hex format (#C7A5A5 or C7A5A5) — require exactly 6 hex chars\n\tif (/^#?[0-9A-Fa-f]{6}$/.test(trimmed)) {\n\t\ttry {\n\t\t\tconst hex = trimmed.startsWith('#') ? trimmed : `#${trimmed}`;\n\t\t\treturn new THREE.Color(hex);\n\t\t} catch {\n\t\t\tgetLogger().warn(`Invalid hex color: ${colorString}, using white`);\n\t\t\treturn new THREE.Color(0xffffff);\n\t\t}\n\t}\n\n\t// Try RGB format (R, G, B)\n\tif (trimmed.includes(',')) {\n\t\tconst rgb = trimmed.split(',').map((c) => parseInt(c.trim(), 10));\n\t\tif (rgb.length === 3 && rgb.every((n) => !isNaN(n) && n >= 0 && n <= 255)) {\n\t\t\treturn new THREE.Color(rgb[0] / 255, rgb[1] / 255, rgb[2] / 255);\n\t\t}\n\t}\n\n\t// Try CSS named color\n\ttry {\n\t\treturn new THREE.Color(trimmed.toLowerCase());\n\t} catch {\n\t\tgetLogger().warn(`Invalid color string: ${colorString}, using white`);\n\t\treturn new THREE.Color(0xffffff);\n\t}\n}\n\nexport function applyOffset(meshes: THREE.Object3D[], offsetY: number): void {\n\tmeshes.forEach((mesh) => {\n\t\tmesh.position.y -= offsetY;\n\t});\n}\n\n/**\n * Computes the combined world-axis-aligned bounding box of a set of objects (meshes, lines, points).\n * Correctly accounts for transformations (rotation, position, scale).\n */\nexport function computeCombinedBoundingBox(meshes: THREE.Object3D[]): THREE.Box3 {\n\tconst combinedBoundingBox = new THREE.Box3();\n\tif (meshes.length === 0) return combinedBoundingBox;\n\tmeshes.forEach((mesh) => {\n\t\t// Ensure the world matrix is up to date before calculating the box\n\t\tmesh.updateMatrixWorld(true);\n\t\tconst bbox = new THREE.Box3().setFromObject(mesh);\n\t\tcombinedBoundingBox.union(bbox);\n\t});\n\treturn combinedBoundingBox;\n}\n\n/**\n * Clears the given THREE.Scene by removing all non-floor top-level children and\n * recursively disposing of their geometry and materials.\n *\n * Removes at the top level rather than traversing for meshes, so parent Groups\n * don't accumulate as ghost nodes after their mesh children are disposed.\n */\nfunction clearScene(scene: THREE.Scene): void {\n\t// Snapshot children — we mutate the array via removeFromParent during iteration\n\tconst topLevel = [...scene.children];\n\n\ttopLevel.forEach((object) => {\n\t\tif (object.userData.id === 'floor') return;\n\n\t\t// Recursively dispose all renderable objects (meshes, lines, points) in this subtree.\n\t\tobject.traverse((child) => {\n\t\t\tconst renderable = child as Partial<THREE.Mesh> & THREE.Object3D;\n\t\t\tif (!renderable.geometry && !renderable.material) return;\n\n\t\t\trenderable.geometry?.dispose();\n\n\t\t\tconst material = renderable.material;\n\t\t\tif (!material) return;\n\t\t\tconst materials = Array.isArray(material) ? material : [material];\n\t\t\tmaterials.forEach((material) => {\n\t\t\t\t// Walk only own enumerable properties — `for...in` on a Three.js material\n\t\t\t\t// also iterates the prototype chain, which is needlessly expensive.\n\t\t\t\tfor (const value of Object.values(material)) {\n\t\t\t\t\tif (value instanceof THREE.Texture) {\n\t\t\t\t\t\tvalue.dispose();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tmaterial.dispose();\n\t\t\t});\n\t\t});\n\n\t\tobject.removeFromParent();\n\t});\n}\n","import * as THREE from 'three';\n\n/**\n * An \"infinite\", distance-fading reference grid — the single strongest visual cue that reads as CAD.\n *\n * Why not `GridHelper`: it's a fixed-size square of line segments that visibly ends and looks wrong\n * once you pan or zoom past it. Instead we draw one large screen-facing plane and compute the grid\n * in the fragment shader from world coordinates, fading lines out with distance so the edge is never\n * a hard cutoff. The plane is big enough to cover any reasonable view; the fade hides its bounds.\n *\n * Two line frequencies (minor + major every 10th) give the usual graph-paper depth read. Spacing is\n * in world units (meters — the scene's normalized unit), so a `cellSize` of 1 = 1m minor cells.\n */\n\nexport interface GridOptions {\n\t/** Minor cell size in world units (meters). Default 1. */\n\tcellSize?: number;\n\t/** How many minor cells per major line. Default 10. */\n\tmajorEvery?: number;\n\t/** Minor line color. */\n\tcellColor?: THREE.ColorRepresentation;\n\t/** Major line color. */\n\tmajorColor?: THREE.ColorRepresentation;\n\t/** World-space radius at which the grid has fully faded out. Default 100. */\n\tfadeDistance?: number;\n\t/** Plane to lay the grid on. 'y' = horizontal ground (Three Y-up). Default 'y'. */\n\tplane?: 'x' | 'y' | 'z';\n}\n\nexport interface Grid {\n\t/** The grid mesh; add to the scene. Tagged `userData.id = 'grid'` so pick/fit code skips it. */\n\treadonly object: THREE.Mesh;\n\t/** Keep the fade centered on the camera so the grid feels infinite as you move. Call per frame. */\n\tupdate(cameraPosition: THREE.Vector3): void;\n\tsetVisible(visible: boolean): void;\n\tdispose(): void;\n}\n\nconst GRID_VERTEX = /* glsl */ `\n\tvarying vec3 vWorldPos;\n\tvoid main() {\n\t\tvec4 world = modelMatrix * vec4(position, 1.0);\n\t\tvWorldPos = world.xyz;\n\t\tgl_Position = projectionMatrix * viewMatrix * world;\n\t}\n`;\n\nconst GRID_FRAGMENT = /* glsl */ `\n\tprecision highp float;\n\tvarying vec3 vWorldPos;\n\n\tuniform vec2 uAxes; // indices (0=x,1=y,2=z) of the two in-plane world axes\n\tuniform float uCell;\n\tuniform float uMajor;\n\tuniform vec3 uCellColor;\n\tuniform vec3 uMajorColor;\n\tuniform vec3 uCenter; // fade center (camera position), projected onto plane\n\tuniform float uFade;\n\n\t// Antialiased grid line intensity for a given spacing, using screen-space derivatives so lines\n\t// stay ~1px regardless of zoom (the standard \"pristine grid\" technique).\n\tfloat gridLine(vec2 coord, float spacing) {\n\t\tvec2 c = coord / spacing;\n\t\tvec2 d = fwidth(c);\n\t\tvec2 g = abs(fract(c - 0.5) - 0.5) / max(d, 1e-6);\n\t\tfloat line = min(g.x, g.y);\n\t\treturn 1.0 - clamp(line, 0.0, 1.0);\n\t}\n\n\t// Index a vec3 by a float axis id (0/1/2) without dynamic indexing (WebGL1-safe).\n\tfloat axis(vec3 v, float i) {\n\t\treturn i < 0.5 ? v.x : (i < 1.5 ? v.y : v.z);\n\t}\n\n\tvoid main() {\n\t\t// Pick the two in-plane world coordinates.\n\t\tvec2 coord = vec2(axis(vWorldPos, uAxes.x), axis(vWorldPos, uAxes.y));\n\n\t\tfloat minor = gridLine(coord, uCell);\n\t\tfloat major = gridLine(coord, uCell * uMajor);\n\n\t\tvec3 color = mix(uCellColor, uMajorColor, major);\n\t\tfloat alpha = max(minor, major);\n\n\t\t// Radial fade from the camera-projected center.\n\t\tfloat dist = distance(vWorldPos, uCenter);\n\t\tfloat fade = 1.0 - clamp(dist / uFade, 0.0, 1.0);\n\t\talpha *= fade * fade;\n\n\t\tif (alpha < 0.001) discard;\n\t\tgl_FragColor = vec4(color, alpha);\n\t}\n`;\n\nexport function createGrid(options: GridOptions = {}): Grid {\n\tconst {\n\t\tcellSize = 1,\n\t\tmajorEvery = 10,\n\t\tcellColor = 0x888888,\n\t\tmajorColor = 0x444444,\n\t\tfadeDistance = 100,\n\t\tplane = 'y'\n\t} = options;\n\n\t// The two in-plane world axes (0=x,1=y,2=z): ground 'y' grids over x,z; 'z' over x,y; 'x' over y,z.\n\tconst axes =\n\t\tplane === 'y'\n\t\t\t? new THREE.Vector2(0, 2) // x, z\n\t\t\t: plane === 'z'\n\t\t\t\t? new THREE.Vector2(0, 1) // x, y\n\t\t\t\t: new THREE.Vector2(1, 2); // y, z\n\n\tconst size = fadeDistance * 2.5; // comfortably larger than the fade radius\n\tconst geometry = new THREE.PlaneGeometry(size, size);\n\n\t// PlaneGeometry is in the XY plane by default; rotate it onto the requested world plane.\n\tif (plane === 'y') geometry.rotateX(-Math.PI / 2);\n\telse if (plane === 'x') geometry.rotateY(Math.PI / 2);\n\n\tconst material = new THREE.ShaderMaterial({\n\t\tvertexShader: GRID_VERTEX,\n\t\tfragmentShader: GRID_FRAGMENT,\n\t\ttransparent: true,\n\t\tdepthWrite: false,\n\t\tside: THREE.DoubleSide,\n\t\tuniforms: {\n\t\t\tuAxes: { value: axes },\n\t\t\tuCell: { value: cellSize },\n\t\t\tuMajor: { value: majorEvery },\n\t\t\tuCellColor: { value: new THREE.Color(cellColor) },\n\t\t\tuMajorColor: { value: new THREE.Color(majorColor) },\n\t\t\tuCenter: { value: new THREE.Vector3() },\n\t\t\tuFade: { value: fadeDistance }\n\t\t}\n\t});\n\n\tconst mesh = new THREE.Mesh(geometry, material);\n\tmesh.name = 'grid';\n\tmesh.userData.id = 'grid'; // excluded from raycasting/fit-to-view, like the floor\n\tmesh.renderOrder = -1; // draw before content so transparent geometry blends over it\n\n\tconst center = new THREE.Vector3();\n\n\treturn {\n\t\tobject: mesh,\n\t\tupdate: (cameraPosition) => {\n\t\t\t// Re-center the plane and the fade on the camera so the grid tracks the view \"infinitely\".\n\t\t\t// Keep the plane's own axis fixed (don't lift the ground grid up to the camera height).\n\t\t\tif (plane === 'y') {\n\t\t\t\tmesh.position.set(cameraPosition.x, 0, cameraPosition.z);\n\t\t\t\tcenter.set(cameraPosition.x, 0, cameraPosition.z);\n\t\t\t} else if (plane === 'z') {\n\t\t\t\tmesh.position.set(cameraPosition.x, cameraPosition.y, 0);\n\t\t\t\tcenter.set(cameraPosition.x, cameraPosition.y, 0);\n\t\t\t} else {\n\t\t\t\tmesh.position.set(0, cameraPosition.y, cameraPosition.z);\n\t\t\t\tcenter.set(0, cameraPosition.y, cameraPosition.z);\n\t\t\t}\n\t\t\tmaterial.uniforms.uCenter.value.copy(center);\n\t\t},\n\t\tsetVisible: (visible) => {\n\t\t\tmesh.visible = visible;\n\t\t},\n\t\tdispose: () => {\n\t\t\tgeometry.dispose();\n\t\t\tmaterial.dispose();\n\t\t}\n\t};\n}\n","import * as THREE from 'three';\nimport { ViewHelper } from 'three/addons/helpers/ViewHelper.js';\nimport type { OrbitControls } from 'three/addons/controls/OrbitControls.js';\n\nimport type { CameraController } from './camera-controller';\n\n/**\n * The corner nav-cube/axis gizmo. Wraps three's {@link ViewHelper} (the standard, well-tested\n * widget) and uses its built-in click → animate behavior, which we keep rather than reimplement:\n * ViewHelper's hit-test depends on private internals (`dim`, `interactiveObjects`, viewport math),\n * so replicating it is fragile. We let it drive the perspective camera directly.\n *\n * Two integration points with the viewer's dual-camera setup:\n * 1. Before each click we point `helper.center` at the live orbit target, so the snap rotates about\n * what the user is looking at (not the world origin).\n * 2. ViewHelper only drives the perspective camera. The nav cube is inherently a 3D-orientation\n * tool, so if the viewer is in orthographic (2D) mode when the gizmo is clicked, we first flip\n * back to perspective — then ViewHelper animates as usual. Using the cube returns you to 3D.\n *\n * Caller responsibilities (mirror ViewHelper's own contract):\n * - call {@link ViewGizmo.render} *after* the main scene render each frame (overlay viewport),\n * - call {@link ViewGizmo.update} each frame with the frame delta (drives the snap animation),\n * - forward pointer clicks to {@link ViewGizmo.handleClick}.\n */\nexport interface ViewGizmo {\n\trender(renderer: THREE.WebGLRenderer): void;\n\tupdate(delta: number): void;\n\t/** Hit-test a click. Returns true if it hit the gizmo (and a view change started). */\n\thandleClick(event: MouseEvent): boolean;\n\treadonly isAnimating: boolean;\n\t/** Show/hide the gizmo at runtime. Hidden = not rendered and not click-hittable. */\n\tsetVisible(visible: boolean): void;\n\tisVisible(): boolean;\n\tdispose(): void;\n}\n\ninterface ViewGizmoDeps {\n\t/** The perspective (primary) camera ViewHelper orients and animates. */\n\tcamera: THREE.PerspectiveCamera;\n\tdomElement: HTMLElement;\n\tcontrols: OrbitControls;\n\tcontroller: CameraController;\n}\n\nexport function createViewGizmo(deps: ViewGizmoDeps): ViewGizmo {\n\tconst { camera, domElement, controls, controller } = deps;\n\n\tconst helper = new ViewHelper(camera, domElement);\n\thelper.setLabels('X', 'Y', 'Z');\n\n\tlet visible = true;\n\n\tconst handleClick = (event: MouseEvent): boolean => {\n\t\tif (!visible) return false;\n\n\t\t// Rotate the snap about what the user is looking at.\n\t\thelper.center.copy(controls.target);\n\n\t\t// ViewHelper hit-tests and (on a hit) animates the perspective camera. Run it first so a click\n\t\t// that MISSES the cube changes nothing.\n\t\tconst hit = helper.handleClick(event);\n\t\tif (!hit) return false;\n\n\t\t// On a hit: the cube orients in 3D, so if we were in 2D, switch to perspective — the camera\n\t\t// ViewHelper just started animating — so the snap is what gets rendered.\n\t\tif (controller.getProjection() === 'orthographic') {\n\t\t\tcontroller.setProjection('perspective');\n\t\t}\n\t\treturn true;\n\t};\n\n\treturn {\n\t\trender: (renderer) => {\n\t\t\tif (!visible) return;\n\t\t\t// ViewHelper.render() calls renderer.render(this, orthoCamera), which with the default\n\t\t\t// autoClear=true clears the FULL framebuffer (to the scene's grey clear color) before drawing\n\t\t\t// the cube in its corner viewport — wiping the just-rendered scene. It only needs the depth\n\t\t\t// clear it does internally (clearDepth). So suppress the automatic color/depth clear here.\n\t\t\tconst prevAutoClear = renderer.autoClear;\n\t\t\trenderer.autoClear = false;\n\t\t\thelper.render(renderer);\n\t\t\trenderer.autoClear = prevAutoClear;\n\t\t},\n\t\t// ViewHelper.update() unconditionally rewrites camera.position from (center, radius, q1) — at\n\t\t// rest (radius 0, center origin) that pins the camera to the origin every frame, blanking the\n\t\t// view. It's only meant to run while a click-snap is animating, so guard on `animating`.\n\t\tupdate: (delta) => {\n\t\t\tif (helper.animating) helper.update(delta);\n\t\t},\n\t\thandleClick,\n\t\tget isAnimating() {\n\t\t\treturn helper.animating;\n\t\t},\n\t\tsetVisible: (value) => {\n\t\t\tvisible = value;\n\t\t},\n\t\tisVisible: () => visible,\n\t\tdispose: () => helper.dispose()\n\t};\n}\n"]}