@selvajs/compute 2.1.0-beta.1 → 2.1.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/{chunk-JPXUC3P5.cjs → chunk-7RLHTZZQ.cjs} +3 -3
  2. package/dist/{chunk-JPXUC3P5.cjs.map → chunk-7RLHTZZQ.cjs.map} +1 -1
  3. package/dist/{chunk-MA6YB3YZ.cjs → chunk-JZFH67ES.cjs} +3 -3
  4. package/dist/{chunk-MA6YB3YZ.cjs.map → chunk-JZFH67ES.cjs.map} +1 -1
  5. package/dist/{chunk-Z4TVQVMV.js → chunk-PGOKADJC.js} +2 -2
  6. package/dist/chunk-QAS2VM6Q.js +2 -0
  7. package/dist/chunk-QAS2VM6Q.js.map +1 -0
  8. package/dist/{chunk-GTTKNF4G.js → chunk-XFYFC2DH.js} +3 -3
  9. package/dist/chunk-XFYFC2DH.js.map +1 -0
  10. package/dist/chunk-ZLBFTV7M.cjs +2 -0
  11. package/dist/{chunk-P4SF7AKZ.cjs.map → chunk-ZLBFTV7M.cjs.map} +1 -1
  12. package/dist/core.cjs +1 -1
  13. package/dist/core.d.cts +1 -1
  14. package/dist/core.d.ts +1 -1
  15. package/dist/core.js +1 -1
  16. package/dist/grasshopper.cjs +1 -1
  17. package/dist/grasshopper.d.cts +10 -3
  18. package/dist/grasshopper.d.ts +10 -3
  19. package/dist/grasshopper.js +1 -1
  20. package/dist/{handle-files-DsrxHKHP.d.cts → handle-files-D-_fiXu4.d.cts} +13 -1
  21. package/dist/{handle-files-DsrxHKHP.d.ts → handle-files-D-_fiXu4.d.ts} +13 -1
  22. package/dist/index.cjs +1 -1
  23. package/dist/index.d.cts +1 -1
  24. package/dist/index.d.ts +1 -1
  25. package/dist/index.js +1 -1
  26. package/dist/{visualization-R7QUUUWV.js → visualization-QMK4VTCF.js} +2 -2
  27. package/dist/visualization-YWPVPKCI.cjs +2 -0
  28. package/dist/{visualization-ZDXKGD2L.cjs.map → visualization-YWPVPKCI.cjs.map} +1 -1
  29. package/dist/visualization.cjs +1 -1
  30. package/dist/visualization.cjs.map +1 -1
  31. package/dist/visualization.d.cts +194 -1
  32. package/dist/visualization.d.ts +194 -1
  33. package/dist/visualization.js +1 -1
  34. package/package.json +1 -1
  35. package/dist/chunk-GTTKNF4G.js.map +0 -1
  36. package/dist/chunk-P4SF7AKZ.cjs +0 -2
  37. package/dist/chunk-SKAHIC4G.js +0 -2
  38. package/dist/chunk-SKAHIC4G.js.map +0 -1
  39. package/dist/visualization-ZDXKGD2L.cjs +0 -2
  40. /package/dist/{chunk-Z4TVQVMV.js.map → chunk-PGOKADJC.js.map} +0 -0
  41. /package/dist/{visualization-R7QUUUWV.js.map → visualization-QMK4VTCF.js.map} +0 -0
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunkJPXUC3P5cjs = require('./chunk-JPXUC3P5.cjs');require('./chunk-MA6YB3YZ.cjs');exports.Materials = _chunkJPXUC3P5cjs.q; exports.SCALE_FACTORS = _chunkJPXUC3P5cjs.z; exports.getThreeMeshesFromComputeResponse = _chunkJPXUC3P5cjs.A; exports.initThree = _chunkJPXUC3P5cjs.p; exports.parseDisplayItems = _chunkJPXUC3P5cjs.r; exports.parseMeshBatchBlob = _chunkJPXUC3P5cjs.y; exports.parseMeshBatchObject = _chunkJPXUC3P5cjs.x; exports.updateScene = _chunkJPXUC3P5cjs.a;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunk7RLHTZZQcjs = require('./chunk-7RLHTZZQ.cjs');require('./chunk-JZFH67ES.cjs');exports.EDGE_USERDATA_KIND = _chunk7RLHTZZQcjs.h; exports.Materials = _chunk7RLHTZZQcjs.q; exports.SCALE_FACTORS = _chunk7RLHTZZQcjs.z; exports.addEdges = _chunk7RLHTZZQcjs.i; exports.applyOffset = _chunk7RLHTZZQcjs.c; exports.computeCombinedBoundingBox = _chunk7RLHTZZQcjs.d; exports.createCameraController = _chunk7RLHTZZQcjs.e; exports.createGrid = _chunk7RLHTZZQcjs.f; exports.createLabelLayer = _chunk7RLHTZZQcjs.m; exports.createMeasureTool = _chunk7RLHTZZQcjs.o; exports.createRenderPipeline = _chunk7RLHTZZQcjs.l; exports.createViewGizmo = _chunk7RLHTZZQcjs.g; exports.getThreeMeshesFromComputeResponse = _chunk7RLHTZZQcjs.A; exports.initThree = _chunk7RLHTZZQcjs.p; exports.isEdgeOverlay = _chunk7RLHTZZQcjs.j; exports.parseColor = _chunk7RLHTZZQcjs.b; exports.parseDisplayItems = _chunk7RLHTZZQcjs.r; exports.parseMeshBatchBlob = _chunk7RLHTZZQcjs.y; exports.parseMeshBatchObject = _chunk7RLHTZZQcjs.x; exports.removeEdges = _chunk7RLHTZZQcjs.k; exports.snapToVertex = _chunk7RLHTZZQcjs.n; exports.updateScene = _chunk7RLHTZZQcjs.a;
2
2
  //# sourceMappingURL=visualization.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/selva-compute/selva-compute/dist/visualization.cjs"],"names":[],"mappings":"AAAA,iIAAyF,gCAA6B,iYAA0L","file":"/home/runner/work/selva-compute/selva-compute/dist/visualization.cjs"}
1
+ {"version":3,"sources":["/home/runner/work/selva-compute/selva-compute/dist/visualization.cjs"],"names":[],"mappings":"AAAA,iIAA2L,gCAA6B,khCAA+d","file":"/home/runner/work/selva-compute/selva-compute/dist/visualization.cjs"}
@@ -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-BuRCHPlb.cjs';
4
6
  export { p as DisplayCurve, q as DisplayIdentity, r as DisplayItemBase, s as DisplayPoint, t as DisplayPosition, u as MeshBatch } from './types-BuRCHPlb.cjs';
5
7
  import { RhinoModule } from 'rhino3dm';
@@ -183,7 +185,40 @@ interface CameraController {
183
185
  /** Keep the orthographic frustum aspect in sync on canvas resize. Called by the resize loop. */
184
186
  updateAspect(width: number, height: number): void;
185
187
  }
188
+ interface CameraControllerDeps {
189
+ scene: THREE.Scene;
190
+ perspective: THREE.PerspectiveCamera;
191
+ controls: OrbitControls;
192
+ /** Called whenever the active camera identity changes, so callers can re-point the renderer/raycaster. */
193
+ onActiveCameraChange: (camera: THREE.Camera) => void;
194
+ }
195
+ declare function createCameraController(deps: CameraControllerDeps): CameraController;
186
196
 
197
+ /**
198
+ * An "infinite", distance-fading reference grid — the single strongest visual cue that reads as CAD.
199
+ *
200
+ * Why not `GridHelper`: it's a fixed-size square of line segments that visibly ends and looks wrong
201
+ * once you pan or zoom past it. Instead we draw one large screen-facing plane and compute the grid
202
+ * in the fragment shader from world coordinates, fading lines out with distance so the edge is never
203
+ * a hard cutoff. The plane is big enough to cover any reasonable view; the fade hides its bounds.
204
+ *
205
+ * Two line frequencies (minor + major every 10th) give the usual graph-paper depth read. Spacing is
206
+ * in world units (meters — the scene's normalized unit), so a `cellSize` of 1 = 1m minor cells.
207
+ */
208
+ interface GridOptions {
209
+ /** Minor cell size in world units (meters). Default 1. */
210
+ cellSize?: number;
211
+ /** How many minor cells per major line. Default 10. */
212
+ majorEvery?: number;
213
+ /** Minor line color. */
214
+ cellColor?: THREE.ColorRepresentation;
215
+ /** Major line color. */
216
+ majorColor?: THREE.ColorRepresentation;
217
+ /** World-space radius at which the grid has fully faded out. Default 100. */
218
+ fadeDistance?: number;
219
+ /** Plane to lay the grid on. 'y' = horizontal ground (Three Y-up). Default 'y'. */
220
+ plane?: 'x' | 'y' | 'z';
221
+ }
187
222
  interface Grid {
188
223
  /** The grid mesh; add to the scene. Tagged `userData.id = 'grid'` so pick/fit code skips it. */
189
224
  readonly object: THREE.Mesh;
@@ -192,6 +227,7 @@ interface Grid {
192
227
  setVisible(visible: boolean): void;
193
228
  dispose(): void;
194
229
  }
230
+ declare function createGrid(options?: GridOptions): Grid;
195
231
 
196
232
  /**
197
233
  * The corner nav-cube/axis gizmo. Wraps three's {@link ViewHelper} (the standard, well-tested
@@ -222,6 +258,49 @@ interface ViewGizmo {
222
258
  isVisible(): boolean;
223
259
  dispose(): void;
224
260
  }
261
+ interface ViewGizmoDeps {
262
+ /** The perspective (primary) camera ViewHelper orients and animates. */
263
+ camera: THREE.PerspectiveCamera;
264
+ domElement: HTMLElement;
265
+ controls: OrbitControls;
266
+ controller: CameraController;
267
+ }
268
+ declare function createViewGizmo(deps: ViewGizmoDeps): ViewGizmo;
269
+
270
+ /**
271
+ * An HTML label layer that tracks 3D positions, via three's {@link CSS2DRenderer}. Labels are real
272
+ * DOM nodes (crisp text, CSS-stylable) positioned each frame to follow points in the scene — the
273
+ * foundation for measurement readouts, dimension annotations, and point tags.
274
+ *
275
+ * The CSS2D renderer draws into its own absolutely-positioned DOM overlay stacked on top of the
276
+ * WebGL canvas (pointer-events disabled so it never steals clicks from the viewer). The viewer owns
277
+ * one of these; features like the measure tool add/remove labels through it.
278
+ */
279
+ interface LabelHandle {
280
+ /** The CSS2DObject in the scene graph. */
281
+ readonly object: CSS2DObject;
282
+ /** Move the label to a new world position. */
283
+ setPosition(position: THREE.Vector3): void;
284
+ /** Replace the label's text/HTML. */
285
+ setText(text: string): void;
286
+ /** Remove the label from the scene and the DOM. */
287
+ remove(): void;
288
+ }
289
+ interface LabelLayer {
290
+ /** Create a label at a world position. `className` lets callers theme groups of labels. */
291
+ addLabel(text: string, position: THREE.Vector3, className?: string): LabelHandle;
292
+ /** Render the DOM overlay. Call each frame after the WebGL render, with the active camera. */
293
+ render(scene: THREE.Scene, camera: THREE.Camera): void;
294
+ setSize(width: number, height: number): void;
295
+ dispose(): void;
296
+ }
297
+ /**
298
+ * @param container element to overlay labels onto — normally the canvas's parent, so the overlay and
299
+ * canvas share a positioning context. The overlay is appended here and absolutely positioned.
300
+ * @param scene labels are parented to a group added to this scene, so they render and follow the
301
+ * camera without the caller wiring scene-graph parenting.
302
+ */
303
+ declare function createLabelLayer(container: HTMLElement, scene: THREE.Scene): LabelLayer;
225
304
 
226
305
  /**
227
306
  * A two-click distance measurement tool — the CAD verb users expect. Click a point, click a second,
@@ -246,6 +325,35 @@ interface MeasureTool {
246
325
  clear(): void;
247
326
  dispose(): void;
248
327
  }
328
+ interface MeasureOptions {
329
+ /** Snap to a vertex when the cursor is within this many screen pixels of it. Default 12. */
330
+ snapPixels?: number;
331
+ /** Marker + line color. Default yellow. */
332
+ color?: THREE.ColorRepresentation;
333
+ /** CSS class applied to the distance label, for styling. */
334
+ labelClassName?: string;
335
+ /** Format the distance number → label text. Default: 3 significant digits + " m". */
336
+ format?: (distance: number) => string;
337
+ }
338
+ interface MeasureDeps {
339
+ canvas: HTMLCanvasElement;
340
+ scene: THREE.Scene;
341
+ getActiveCamera: () => THREE.Camera;
342
+ labelLayer: LabelLayer;
343
+ options?: MeasureOptions;
344
+ }
345
+ /**
346
+ * Snap a raycast hit to the nearest vertex of the struck triangle if it's within `snapPixels` on
347
+ * screen; otherwise return the raw hit point. Pure (no DOM) so it's unit-testable: it takes the
348
+ * screen size explicitly rather than reading the canvas. Exported for that reason.
349
+ *
350
+ * Falls back to the raw point for non-mesh hits (e.g. a curve) or geometry without a face/positions.
351
+ */
352
+ declare function snapToVertex(hit: THREE.Intersection, camera: THREE.Camera, screenSize: {
353
+ width: number;
354
+ height: number;
355
+ }, snapPixels: number): THREE.Vector3;
356
+ declare function createMeasureTool(deps: MeasureDeps): MeasureTool;
249
357
 
250
358
  /**
251
359
  * Initializes a Three.js environment with scene, camera, renderer, and event handling.
@@ -282,6 +390,22 @@ declare const initThree: (canvas: HTMLCanvasElement, options?: ThreeInitializerO
282
390
  * @param initialPositionSet - A boolean indicating whether the initial position of the camera and controls have been set.
283
391
  */
284
392
  declare function updateScene(scene: THREE.Scene, meshes: THREE.Object3D[], camera: THREE.PerspectiveCamera, controls: OrbitControls, initialPositionSet: boolean): void;
393
+ /**
394
+ * Parses a color string in multiple formats to a THREE.Color object.
395
+ * Supported formats:
396
+ * - Hex: "#C7A5A5", "C7A5A5"
397
+ * - RGB: "199, 165, 165"
398
+ * - CSS named colors: "red", "blue", etc.
399
+ * @param colorString - The color string to parse.
400
+ * @returns A THREE.Color object.
401
+ */
402
+ declare function parseColor(colorString: string): THREE.Color;
403
+ declare function applyOffset(meshes: THREE.Object3D[], offsetY: number): void;
404
+ /**
405
+ * Computes the combined world-axis-aligned bounding box of a set of objects (meshes, lines, points).
406
+ * Correctly accounts for transformations (rotation, position, scale).
407
+ */
408
+ declare function computeCombinedBoundingBox(meshes: THREE.Object3D[]): THREE.Box3;
285
409
 
286
410
  declare const EMISSIVE_MATERIAL: THREE.MeshPhysicalMaterial;
287
411
  declare const METAL_MATERIAL: THREE.MeshPhysicalMaterial;
@@ -302,6 +426,75 @@ declare namespace threeMaterials {
302
426
  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
427
  }
304
428
 
429
+ /**
430
+ * Crisp boundary/crease edges overlaid on meshes — the defining "technical drawing" look that makes
431
+ * shaded geometry read as discrete objects rather than blobs.
432
+ *
433
+ * Built with `EdgesGeometry` (which keeps only edges whose adjacent faces meet above a threshold
434
+ * angle, so flat tessellation noise is dropped) rendered as a fat `LineSegments2` using the same
435
+ * `LineMaterial` family as curves (Phase 1) — so edges get controllable thickness, not the 1px cap
436
+ * of `THREE.LineSegments`. The overlay is added as a *child* of each mesh, so it inherits the mesh's
437
+ * transform and is disposed when the mesh subtree is cleared.
438
+ */
439
+ interface EdgeOptions {
440
+ /** Edge color. Default near-black. */
441
+ color?: THREE.ColorRepresentation;
442
+ /** Edge thickness in CSS px. Default 1.5. */
443
+ width?: number;
444
+ /**
445
+ * Crease angle in degrees: an edge is kept only where its two faces differ by more than this.
446
+ * Default 30. Higher = fewer edges (only sharp creases); lower = more (catches gentle bends).
447
+ */
448
+ thresholdAngle?: number;
449
+ }
450
+ /** Tag on edge overlays so pick/fit/clear logic can recognize and skip or dispose them. */
451
+ declare const EDGE_USERDATA_KIND = "edge-overlay";
452
+ /**
453
+ * Walk an object subtree and attach an edge overlay to every `Mesh` found, returning the created
454
+ * overlays (so callers can dispose them explicitly if they don't clear the whole subtree). Meshes
455
+ * that already carry an overlay are skipped, so this is safe to call more than once.
456
+ *
457
+ * Skips the floor and the grid (they're aids, not content) and anything already tagged as an edge.
458
+ */
459
+ declare function addEdges(root: THREE.Object3D, options?: EdgeOptions): LineSegments2[];
460
+ /** Whether an object is an edge overlay (for pick/fit filters elsewhere). */
461
+ declare function isEdgeOverlay(object: THREE.Object3D): boolean;
462
+ /**
463
+ * Remove every edge overlay under `root`, disposing its geometry and material. The inverse of
464
+ * {@link addEdges}; together they make edges a live on/off toggle. Returns how many were removed.
465
+ */
466
+ declare function removeEdges(root: THREE.Object3D): number;
467
+
468
+ /**
469
+ * Optional postprocessing pipeline for ambient occlusion. Default-OFF: the viewer only constructs
470
+ * this when `render.ambientOcclusion` is enabled, and otherwise renders with a plain
471
+ * `renderer.render` so the cheap path stays cheap (the chosen tradeoff — see cad-viewer-plan.md).
472
+ *
473
+ * Pipeline: RenderPass → GTAOPass → OutputPass. GTAO (ground-truth AO) is the modern replacement for
474
+ * SSAO/SAO — better contact shadows in crevices, the "engineered" depth cue. `screenSpaceRadius` is
475
+ * on so the AO radius is in screen space, which keeps it scale-robust across the viewer's mm→m
476
+ * scenes without per-scene tuning. OutputPass applies tone mapping + color space (taking over the
477
+ * roles the renderer did directly in the non-composer path).
478
+ *
479
+ * Camera swaps: the active camera can flip perspective↔ortho. Rather than rebuild the composer, we
480
+ * retarget the passes' `camera` each render via {@link setCamera}.
481
+ */
482
+ interface RenderPipeline {
483
+ render(deltaTime: number): void;
484
+ setSize(width: number, height: number, pixelRatio: number): void;
485
+ /** Point the passes at the currently active camera (call when projection changes). */
486
+ setCamera(camera: THREE.Camera): void;
487
+ dispose(): void;
488
+ }
489
+ interface RenderPipelineOptions {
490
+ /** Tone mapping to apply in OutputPass (mirror the renderer's). */
491
+ toneMapping: THREE.ToneMapping;
492
+ toneMappingExposure: number;
493
+ /** AO strength 0–1. Default 1. */
494
+ aoIntensity?: number;
495
+ }
496
+ declare function createRenderPipeline(renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.Camera, width: number, height: number, options: RenderPipelineOptions): RenderPipeline;
497
+
305
498
  declare const SCALE_FACTORS: Record<string, number>;
306
499
  /**
307
500
  * Extracts and processes display meshes from a ComputePointerResponse using the Grasshopper WebDisplay component.
@@ -405,4 +598,4 @@ interface DisplayItemParseOptions {
405
598
  */
406
599
  declare function parseDisplayItems(items: DisplayItem[] | undefined, options?: DisplayItemParseOptions): THREE.Object3D[];
407
600
 
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 };
601
+ 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,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';
@@ -183,7 +185,40 @@ interface CameraController {
183
185
  /** Keep the orthographic frustum aspect in sync on canvas resize. Called by the resize loop. */
184
186
  updateAspect(width: number, height: number): void;
185
187
  }
188
+ interface CameraControllerDeps {
189
+ scene: THREE.Scene;
190
+ perspective: THREE.PerspectiveCamera;
191
+ controls: OrbitControls;
192
+ /** Called whenever the active camera identity changes, so callers can re-point the renderer/raycaster. */
193
+ onActiveCameraChange: (camera: THREE.Camera) => void;
194
+ }
195
+ declare function createCameraController(deps: CameraControllerDeps): CameraController;
186
196
 
197
+ /**
198
+ * An "infinite", distance-fading reference grid — the single strongest visual cue that reads as CAD.
199
+ *
200
+ * Why not `GridHelper`: it's a fixed-size square of line segments that visibly ends and looks wrong
201
+ * once you pan or zoom past it. Instead we draw one large screen-facing plane and compute the grid
202
+ * in the fragment shader from world coordinates, fading lines out with distance so the edge is never
203
+ * a hard cutoff. The plane is big enough to cover any reasonable view; the fade hides its bounds.
204
+ *
205
+ * Two line frequencies (minor + major every 10th) give the usual graph-paper depth read. Spacing is
206
+ * in world units (meters — the scene's normalized unit), so a `cellSize` of 1 = 1m minor cells.
207
+ */
208
+ interface GridOptions {
209
+ /** Minor cell size in world units (meters). Default 1. */
210
+ cellSize?: number;
211
+ /** How many minor cells per major line. Default 10. */
212
+ majorEvery?: number;
213
+ /** Minor line color. */
214
+ cellColor?: THREE.ColorRepresentation;
215
+ /** Major line color. */
216
+ majorColor?: THREE.ColorRepresentation;
217
+ /** World-space radius at which the grid has fully faded out. Default 100. */
218
+ fadeDistance?: number;
219
+ /** Plane to lay the grid on. 'y' = horizontal ground (Three Y-up). Default 'y'. */
220
+ plane?: 'x' | 'y' | 'z';
221
+ }
187
222
  interface Grid {
188
223
  /** The grid mesh; add to the scene. Tagged `userData.id = 'grid'` so pick/fit code skips it. */
189
224
  readonly object: THREE.Mesh;
@@ -192,6 +227,7 @@ interface Grid {
192
227
  setVisible(visible: boolean): void;
193
228
  dispose(): void;
194
229
  }
230
+ declare function createGrid(options?: GridOptions): Grid;
195
231
 
196
232
  /**
197
233
  * The corner nav-cube/axis gizmo. Wraps three's {@link ViewHelper} (the standard, well-tested
@@ -222,6 +258,49 @@ interface ViewGizmo {
222
258
  isVisible(): boolean;
223
259
  dispose(): void;
224
260
  }
261
+ interface ViewGizmoDeps {
262
+ /** The perspective (primary) camera ViewHelper orients and animates. */
263
+ camera: THREE.PerspectiveCamera;
264
+ domElement: HTMLElement;
265
+ controls: OrbitControls;
266
+ controller: CameraController;
267
+ }
268
+ declare function createViewGizmo(deps: ViewGizmoDeps): ViewGizmo;
269
+
270
+ /**
271
+ * An HTML label layer that tracks 3D positions, via three's {@link CSS2DRenderer}. Labels are real
272
+ * DOM nodes (crisp text, CSS-stylable) positioned each frame to follow points in the scene — the
273
+ * foundation for measurement readouts, dimension annotations, and point tags.
274
+ *
275
+ * The CSS2D renderer draws into its own absolutely-positioned DOM overlay stacked on top of the
276
+ * WebGL canvas (pointer-events disabled so it never steals clicks from the viewer). The viewer owns
277
+ * one of these; features like the measure tool add/remove labels through it.
278
+ */
279
+ interface LabelHandle {
280
+ /** The CSS2DObject in the scene graph. */
281
+ readonly object: CSS2DObject;
282
+ /** Move the label to a new world position. */
283
+ setPosition(position: THREE.Vector3): void;
284
+ /** Replace the label's text/HTML. */
285
+ setText(text: string): void;
286
+ /** Remove the label from the scene and the DOM. */
287
+ remove(): void;
288
+ }
289
+ interface LabelLayer {
290
+ /** Create a label at a world position. `className` lets callers theme groups of labels. */
291
+ addLabel(text: string, position: THREE.Vector3, className?: string): LabelHandle;
292
+ /** Render the DOM overlay. Call each frame after the WebGL render, with the active camera. */
293
+ render(scene: THREE.Scene, camera: THREE.Camera): void;
294
+ setSize(width: number, height: number): void;
295
+ dispose(): void;
296
+ }
297
+ /**
298
+ * @param container element to overlay labels onto — normally the canvas's parent, so the overlay and
299
+ * canvas share a positioning context. The overlay is appended here and absolutely positioned.
300
+ * @param scene labels are parented to a group added to this scene, so they render and follow the
301
+ * camera without the caller wiring scene-graph parenting.
302
+ */
303
+ declare function createLabelLayer(container: HTMLElement, scene: THREE.Scene): LabelLayer;
225
304
 
226
305
  /**
227
306
  * A two-click distance measurement tool — the CAD verb users expect. Click a point, click a second,
@@ -246,6 +325,35 @@ interface MeasureTool {
246
325
  clear(): void;
247
326
  dispose(): void;
248
327
  }
328
+ interface MeasureOptions {
329
+ /** Snap to a vertex when the cursor is within this many screen pixels of it. Default 12. */
330
+ snapPixels?: number;
331
+ /** Marker + line color. Default yellow. */
332
+ color?: THREE.ColorRepresentation;
333
+ /** CSS class applied to the distance label, for styling. */
334
+ labelClassName?: string;
335
+ /** Format the distance number → label text. Default: 3 significant digits + " m". */
336
+ format?: (distance: number) => string;
337
+ }
338
+ interface MeasureDeps {
339
+ canvas: HTMLCanvasElement;
340
+ scene: THREE.Scene;
341
+ getActiveCamera: () => THREE.Camera;
342
+ labelLayer: LabelLayer;
343
+ options?: MeasureOptions;
344
+ }
345
+ /**
346
+ * Snap a raycast hit to the nearest vertex of the struck triangle if it's within `snapPixels` on
347
+ * screen; otherwise return the raw hit point. Pure (no DOM) so it's unit-testable: it takes the
348
+ * screen size explicitly rather than reading the canvas. Exported for that reason.
349
+ *
350
+ * Falls back to the raw point for non-mesh hits (e.g. a curve) or geometry without a face/positions.
351
+ */
352
+ declare function snapToVertex(hit: THREE.Intersection, camera: THREE.Camera, screenSize: {
353
+ width: number;
354
+ height: number;
355
+ }, snapPixels: number): THREE.Vector3;
356
+ declare function createMeasureTool(deps: MeasureDeps): MeasureTool;
249
357
 
250
358
  /**
251
359
  * Initializes a Three.js environment with scene, camera, renderer, and event handling.
@@ -282,6 +390,22 @@ declare const initThree: (canvas: HTMLCanvasElement, options?: ThreeInitializerO
282
390
  * @param initialPositionSet - A boolean indicating whether the initial position of the camera and controls have been set.
283
391
  */
284
392
  declare function updateScene(scene: THREE.Scene, meshes: THREE.Object3D[], camera: THREE.PerspectiveCamera, controls: OrbitControls, initialPositionSet: boolean): void;
393
+ /**
394
+ * Parses a color string in multiple formats to a THREE.Color object.
395
+ * Supported formats:
396
+ * - Hex: "#C7A5A5", "C7A5A5"
397
+ * - RGB: "199, 165, 165"
398
+ * - CSS named colors: "red", "blue", etc.
399
+ * @param colorString - The color string to parse.
400
+ * @returns A THREE.Color object.
401
+ */
402
+ declare function parseColor(colorString: string): THREE.Color;
403
+ declare function applyOffset(meshes: THREE.Object3D[], offsetY: number): void;
404
+ /**
405
+ * Computes the combined world-axis-aligned bounding box of a set of objects (meshes, lines, points).
406
+ * Correctly accounts for transformations (rotation, position, scale).
407
+ */
408
+ declare function computeCombinedBoundingBox(meshes: THREE.Object3D[]): THREE.Box3;
285
409
 
286
410
  declare const EMISSIVE_MATERIAL: THREE.MeshPhysicalMaterial;
287
411
  declare const METAL_MATERIAL: THREE.MeshPhysicalMaterial;
@@ -302,6 +426,75 @@ declare namespace threeMaterials {
302
426
  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
427
  }
304
428
 
429
+ /**
430
+ * Crisp boundary/crease edges overlaid on meshes — the defining "technical drawing" look that makes
431
+ * shaded geometry read as discrete objects rather than blobs.
432
+ *
433
+ * Built with `EdgesGeometry` (which keeps only edges whose adjacent faces meet above a threshold
434
+ * angle, so flat tessellation noise is dropped) rendered as a fat `LineSegments2` using the same
435
+ * `LineMaterial` family as curves (Phase 1) — so edges get controllable thickness, not the 1px cap
436
+ * of `THREE.LineSegments`. The overlay is added as a *child* of each mesh, so it inherits the mesh's
437
+ * transform and is disposed when the mesh subtree is cleared.
438
+ */
439
+ interface EdgeOptions {
440
+ /** Edge color. Default near-black. */
441
+ color?: THREE.ColorRepresentation;
442
+ /** Edge thickness in CSS px. Default 1.5. */
443
+ width?: number;
444
+ /**
445
+ * Crease angle in degrees: an edge is kept only where its two faces differ by more than this.
446
+ * Default 30. Higher = fewer edges (only sharp creases); lower = more (catches gentle bends).
447
+ */
448
+ thresholdAngle?: number;
449
+ }
450
+ /** Tag on edge overlays so pick/fit/clear logic can recognize and skip or dispose them. */
451
+ declare const EDGE_USERDATA_KIND = "edge-overlay";
452
+ /**
453
+ * Walk an object subtree and attach an edge overlay to every `Mesh` found, returning the created
454
+ * overlays (so callers can dispose them explicitly if they don't clear the whole subtree). Meshes
455
+ * that already carry an overlay are skipped, so this is safe to call more than once.
456
+ *
457
+ * Skips the floor and the grid (they're aids, not content) and anything already tagged as an edge.
458
+ */
459
+ declare function addEdges(root: THREE.Object3D, options?: EdgeOptions): LineSegments2[];
460
+ /** Whether an object is an edge overlay (for pick/fit filters elsewhere). */
461
+ declare function isEdgeOverlay(object: THREE.Object3D): boolean;
462
+ /**
463
+ * Remove every edge overlay under `root`, disposing its geometry and material. The inverse of
464
+ * {@link addEdges}; together they make edges a live on/off toggle. Returns how many were removed.
465
+ */
466
+ declare function removeEdges(root: THREE.Object3D): number;
467
+
468
+ /**
469
+ * Optional postprocessing pipeline for ambient occlusion. Default-OFF: the viewer only constructs
470
+ * this when `render.ambientOcclusion` is enabled, and otherwise renders with a plain
471
+ * `renderer.render` so the cheap path stays cheap (the chosen tradeoff — see cad-viewer-plan.md).
472
+ *
473
+ * Pipeline: RenderPass → GTAOPass → OutputPass. GTAO (ground-truth AO) is the modern replacement for
474
+ * SSAO/SAO — better contact shadows in crevices, the "engineered" depth cue. `screenSpaceRadius` is
475
+ * on so the AO radius is in screen space, which keeps it scale-robust across the viewer's mm→m
476
+ * scenes without per-scene tuning. OutputPass applies tone mapping + color space (taking over the
477
+ * roles the renderer did directly in the non-composer path).
478
+ *
479
+ * Camera swaps: the active camera can flip perspective↔ortho. Rather than rebuild the composer, we
480
+ * retarget the passes' `camera` each render via {@link setCamera}.
481
+ */
482
+ interface RenderPipeline {
483
+ render(deltaTime: number): void;
484
+ setSize(width: number, height: number, pixelRatio: number): void;
485
+ /** Point the passes at the currently active camera (call when projection changes). */
486
+ setCamera(camera: THREE.Camera): void;
487
+ dispose(): void;
488
+ }
489
+ interface RenderPipelineOptions {
490
+ /** Tone mapping to apply in OutputPass (mirror the renderer's). */
491
+ toneMapping: THREE.ToneMapping;
492
+ toneMappingExposure: number;
493
+ /** AO strength 0–1. Default 1. */
494
+ aoIntensity?: number;
495
+ }
496
+ declare function createRenderPipeline(renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.Camera, width: number, height: number, options: RenderPipelineOptions): RenderPipeline;
497
+
305
498
  declare const SCALE_FACTORS: Record<string, number>;
306
499
  /**
307
500
  * Extracts and processes display meshes from a ComputePointerResponse using the Grasshopper WebDisplay component.
@@ -405,4 +598,4 @@ interface DisplayItemParseOptions {
405
598
  */
406
599
  declare function parseDisplayItems(items: DisplayItem[] | undefined, options?: DisplayItemParseOptions): THREE.Object3D[];
407
600
 
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 };
601
+ 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-Z4TVQVMV.js";import"./chunk-GTTKNF4G.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-PGOKADJC.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.1",
3
+ "version": "2.1.0-beta.3",
4
4
  "description": "TypeScript library for Rhino Compute Server - Grasshopper and RhinoCommon",
5
5
  "author": "VektorNode",
6
6
  "license": "MIT",