@selvajs/compute 3.1.0-beta.12 → 3.1.0-beta.14

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 (35) hide show
  1. package/dist/{chunk-44HGB4C4.cjs → chunk-25TNL6LY.cjs} +2 -2
  2. package/dist/{chunk-44HGB4C4.cjs.map → chunk-25TNL6LY.cjs.map} +1 -1
  3. package/dist/chunk-3QSC23Y2.cjs +120 -0
  4. package/dist/chunk-3QSC23Y2.cjs.map +1 -0
  5. package/dist/{chunk-3SEQKFGD.js → chunk-J4NKIVF3.js} +2 -2
  6. package/dist/chunk-KJVV3L5H.js +120 -0
  7. package/dist/chunk-KJVV3L5H.js.map +1 -0
  8. package/dist/grasshopper.cjs +1 -1
  9. package/dist/grasshopper.d.cts +2 -2
  10. package/dist/grasshopper.d.ts +2 -2
  11. package/dist/grasshopper.js +1 -1
  12. package/dist/index.cjs +1 -1
  13. package/dist/index.d.cts +1 -1
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.js +1 -1
  16. package/dist/{types-Dtok-mVq.d.ts → types-4FMvnx2j.d.ts} +14 -1
  17. package/dist/{types-C8XWc4Ab.d.cts → types-BpOl6U02.d.cts} +14 -1
  18. package/dist/visualization-2TKA3LVK.cjs +2 -0
  19. package/dist/visualization-2TKA3LVK.cjs.map +1 -0
  20. package/dist/visualization-XWX3PR4N.js +2 -0
  21. package/dist/visualization.cjs +1 -1
  22. package/dist/visualization.cjs.map +1 -1
  23. package/dist/visualization.d.cts +257 -25
  24. package/dist/visualization.d.ts +257 -25
  25. package/dist/visualization.js +1 -1
  26. package/package.json +5 -5
  27. package/dist/chunk-INP5HVA2.js +0 -62
  28. package/dist/chunk-INP5HVA2.js.map +0 -1
  29. package/dist/chunk-KTKY5M5S.cjs +0 -62
  30. package/dist/chunk-KTKY5M5S.cjs.map +0 -1
  31. package/dist/visualization-5RH6RSLV.js +0 -2
  32. package/dist/visualization-KRP5OPYW.cjs +0 -2
  33. package/dist/visualization-KRP5OPYW.cjs.map +0 -1
  34. /package/dist/{chunk-3SEQKFGD.js.map → chunk-J4NKIVF3.js.map} +0 -0
  35. /package/dist/{visualization-5RH6RSLV.js.map → visualization-XWX3PR4N.js.map} +0 -0
@@ -1,9 +1,10 @@
1
1
  import * as THREE from 'three';
2
2
  import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
3
- import { M as MaterialAppearanceOptions, f as GrasshopperComputeResponse, m as MeshExtractionOptions, n as MeshBatchParsingOptions, o as DisplayBatch, p as DisplayItem } from './types-Dtok-mVq.js';
4
- export { q as DisplayCurve, r as DisplayIdentity, s as DisplayItemBase, t as DisplayPoint, u as DisplayPosition, v as MeshBatch } from './types-Dtok-mVq.js';
3
+ import { M as MaterialAppearanceOptions, f as GrasshopperComputeResponse, m as MeshExtractionOptions, n as MeshBatchParsingOptions, o as DisplayBatch, p as DisplayItem } from './types-4FMvnx2j.js';
4
+ export { q as DisplayCurve, r as DisplayIdentity, s as DisplayItemBase, t as DisplayPoint, u as DisplayPosition, v as MeshBatch } from './types-4FMvnx2j.js';
5
5
  import { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
6
6
  import { LineSegments2 } from 'three/addons/lines/LineSegments2.js';
7
+ import { Pass } from 'three/addons/postprocessing/Pass.js';
7
8
  import { RhinoModule } from 'rhino3dm';
8
9
  import './types-BGhtYxZ-.js';
9
10
 
@@ -47,6 +48,15 @@ type EnvironmentConfig = {
47
48
  hdrPath?: string;
48
49
  backgroundColor?: THREE.Color | string;
49
50
  enableEnvironmentLighting?: boolean;
51
+ /**
52
+ * The scene's up axis. **Defaults to `(0, 0, 1)` — Rhino's Z-up**, not Three's native Y-up,
53
+ * because geometry arrives in Rhino's frame and is never rotated on ingress (see
54
+ * `coordinate-transform.ts`). Everything orientation-dependent derives from this: view presets,
55
+ * the default iso camera, sun position, grid plane, floor normal, and the hemisphere light.
56
+ *
57
+ * Overriding it reorients the viewer but does NOT rotate incoming geometry, so a Y-up value only
58
+ * makes sense if the host also feeds Y-up geometry.
59
+ */
50
60
  sceneUp?: THREE.Vector3;
51
61
  showEnvironment?: boolean;
52
62
  /**
@@ -88,6 +98,13 @@ type RenderConfig = {
88
98
  * toward the display DPR for sharper AO at higher cost; only relevant when AO is enabled.
89
99
  */
90
100
  aoPixelRatio?: number;
101
+ /**
102
+ * Render only when something changed (camera motion, invalidate(), pointer input, resize), with
103
+ * a ~500 ms safety repaint, instead of every animation frame. Default true — cuts idle GPU/battery
104
+ * to ~2 fps worth of work, which matters especially with ambient occlusion on. Set false to
105
+ * restore the legacy continuous loop.
106
+ */
107
+ onDemand?: boolean;
91
108
  };
92
109
  /**
93
110
  * A ready-to-go visual look. A `look` is a named bundle of LIGHTING/MATERIAL defaults only — it is
@@ -130,6 +147,15 @@ type EdgesConfig = {
130
147
  thresholdAngle?: number;
131
148
  /** Fade an overlay out as its mesh shrinks on screen, so far zoom-outs stay clean. Default true. */
132
149
  distanceFade?: boolean;
150
+ /** Skip overlay extraction for meshes above this triangle count. Default 4M. See `EdgeOptions`. */
151
+ maxTriangles?: number;
152
+ /** Overlays above this segment count render opaque (no distance fade). Default 2M. */
153
+ maxSegments?: number;
154
+ /**
155
+ * When a mesh is skipped for exceeding `maxTriangles`, approximate its edges with the
156
+ * screen-space edge-detection pass (constant cost in triangle count). Default true.
157
+ */
158
+ screenSpaceFallback?: boolean;
133
159
  };
134
160
  type ControlsConfig = {
135
161
  enableDamping?: boolean;
@@ -155,7 +181,11 @@ type GridConfig = {
155
181
  majorColor?: THREE.ColorRepresentation;
156
182
  /** World radius at which the grid fully fades. Default 100. */
157
183
  fadeDistance?: number;
158
- /** Plane the grid lies on. 'y' = horizontal ground. Default 'y'. */
184
+ /**
185
+ * Axis the grid lies perpendicular to — the scene's up axis. Defaults to whichever axis
186
+ * `sceneUp` points along, so it is `'z'` (Rhino's horizontal ground) unless you override
187
+ * `sceneUp`. Set explicitly only to force a grid orientation that ignores the scene up.
188
+ */
159
189
  plane?: 'x' | 'y' | 'z';
160
190
  };
161
191
  /** Corner nav-cube/axis gizmo that snaps to preset views. See `createViewGizmo`. */
@@ -292,7 +322,8 @@ interface CameraControllerDeps {
292
322
  /**
293
323
  * The scene's up axis. Presets, the orthographic camera's up, and the iso direction are all derived
294
324
  * from this so the controller is correct in any up convention (Three's native Y-up, Rhino's Z-up, …)
295
- * without hardcoding an axis. Defaults to Y-up.
325
+ * without hardcoding an axis. Falls back to `perspective.up` when omitted; `initThree` always
326
+ * passes the configured `sceneUp`, which itself defaults to Rhino's Z-up.
296
327
  */
297
328
  up?: THREE.Vector3;
298
329
  }
@@ -320,7 +351,13 @@ interface GridOptions {
320
351
  majorColor?: THREE.ColorRepresentation;
321
352
  /** World-space radius at which the grid has fully faded out. Default 100. */
322
353
  fadeDistance?: number;
323
- /** Plane to lay the grid on. 'y' = horizontal ground (Three Y-up). Default 'y'. */
354
+ /**
355
+ * Axis the grid is laid perpendicular to — i.e. the scene's up axis. `'z'` is the horizontal
356
+ * ground in the Rhino Z-up frame the viewer uses; `'y'` is Three's native Y-up.
357
+ *
358
+ * This standalone default is `'y'`, but `initThree` always passes a plane derived from the
359
+ * configured `sceneUp`, so a viewer-created grid defaults to `'z'`.
360
+ */
324
361
  plane?: 'x' | 'y' | 'z';
325
362
  }
326
363
  interface Grid {
@@ -515,9 +552,24 @@ declare const initThree: (canvas: HTMLCanvasElement, options?: ThreeInitializerO
515
552
  measureTool: MeasureTool | null;
516
553
  /**
517
554
  * Attach edge overlays to the meshes under `root` (no-op unless `edges.enabled`). Call after
518
- * loading meshes via `updateScene`, since meshes arrive after init.
555
+ * loading meshes via `updateScene`, since meshes arrive after init. Extraction for large meshes
556
+ * runs off-thread — their overlays attach a beat later; meshes over `edges.maxTriangles` are
557
+ * skipped and (by default) covered by the screen-space edge fallback instead.
519
558
  */
520
559
  applyEdges: (root: THREE.Object3D) => void;
560
+ /**
561
+ * Remove edge overlays under `root` — the toggle-off counterpart of `applyEdges`. Prefer this
562
+ * over calling `removeEdges` directly: it also cancels in-flight async attaches' fallout and
563
+ * stands down the screen-space edge fallback if it was active.
564
+ */
565
+ clearEdges: (root: THREE.Object3D) => void;
566
+ /**
567
+ * Request a repaint from the on-demand render loop. Camera motion, resizes, pointer input, and
568
+ * the built-in setters invalidate automatically; call this after mutating the scene externally
569
+ * (e.g. `updateScene`, adding user geometry) so the change shows immediately rather than on the
570
+ * next safety repaint. No-op (harmless) when `render.onDemand` is false.
571
+ */
572
+ invalidate: () => void;
521
573
  /** Toggle ambient occlusion at runtime — builds or tears down the postprocessing pipeline. */
522
574
  setAmbientOcclusion: (enabled: boolean) => void;
523
575
  /**
@@ -592,7 +644,8 @@ declare function updateScene(scene: THREE.Scene, meshes: THREE.Object3D[], camer
592
644
  declare function parseColor(colorString: string): THREE.Color;
593
645
  /**
594
646
  * Shift objects along one world axis. Defaults to `z` — the up axis of the unified Z-up scene
595
- * frame (see `../coordinate-transform.ts`), so grounding subtracts the content's lowest z.
647
+ * frame (see `../coordinate-transform.ts`), so grounding subtracts the content's lowest z. Pass an
648
+ * explicit axis when the scene is configured with a different `sceneUp`.
596
649
  */
597
650
  declare function applyOffset(meshes: THREE.Object3D[], offset: number, axis?: 'x' | 'y' | 'z'): void;
598
651
  /**
@@ -623,15 +676,97 @@ declare namespace threeMaterials {
623
676
  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_SHARED_MATERIALS as SHARED_MATERIALS, threeMaterials_WOOD_MATERIAL as WOOD_MATERIAL };
624
677
  }
625
678
 
679
+ /**
680
+ * The single source of truth for "which way is up, and what do front/right mean" in the viewer.
681
+ *
682
+ * Every up-dependent default (camera framing, sun position, ground offset, view presets) derives
683
+ * from {@link buildUpBasis} instead of hardcoding an axis. Before this module those defaults each
684
+ * spelled out a Z-up vector inline, so a scene configured Y-up got a below-horizon camera and a
685
+ * near-horizontal sun while the presets and floor correctly followed `sceneUp`.
686
+ *
687
+ * ## Why front is -forward
688
+ *
689
+ * The basis returns `forward` = the direction the camera LOOKS (from camera toward model), and the
690
+ * preset positions are the opposite (from target toward camera). Rhino's convention fixes the signs:
691
+ * its Front view looks along +Y, so the camera sits at -Y; its Right view looks along -X, so the
692
+ * camera sits at +X. Deriving `right` as `seed x up` (rather than `up x seed`) is what makes the
693
+ * handedness come out matching Rhino rather than mirrored.
694
+ */
695
+ /** An orthonormal frame derived from a scene up axis. All vectors are unit length. */
696
+ interface UpBasis {
697
+ /** The scene up axis, normalized. */
698
+ up: THREE.Vector3;
699
+ /** The direction a "front" view looks along (from camera toward the model). */
700
+ forward: THREE.Vector3;
701
+ /** Completes a right-handed frame with `up` and `forward`. */
702
+ right: THREE.Vector3;
703
+ }
704
+ /**
705
+ * Derive the ground-plane axes for a given up vector.
706
+ *
707
+ * The seed picks whichever world axis is least parallel to `up`, so the cross products stay
708
+ * well-conditioned in any up convention. For Rhino's Z-up this yields forward = +Y and right = +X,
709
+ * matching Rhino's own Front/Right views; for Three's native Y-up it yields forward = -Z and
710
+ * right = +X, matching Three's convention that the default camera looks down -Z.
711
+ */
712
+ declare function buildUpBasis(up: THREE.Vector3): UpBasis;
713
+ /**
714
+ * The default 3/4 iso camera OFFSET from the target, scaled to `distance`.
715
+ *
716
+ * Placed behind-left and above the model (the conventional architectural 3/4), expressed in the
717
+ * scene's own basis so it reads the same in any up convention. Callers add this to the framing
718
+ * target to get a camera position.
719
+ */
720
+ declare function isoOffset(up: THREE.Vector3, distance: number): THREE.Vector3;
721
+ /**
722
+ * A default sun position: high above the model, offset to one side so surfaces get a directional
723
+ * gradient instead of flat top-down light. Expressed in the scene basis rather than a literal
724
+ * Z-up vector, so a Y-up scene still gets an overhead sun rather than a horizontal one.
725
+ */
726
+ declare function sunOffset(up: THREE.Vector3, sideDistance: number, height: number): THREE.Vector3;
727
+ /**
728
+ * The rotation that puts an equirectangular environment map's horizon on the scene's ground plane.
729
+ *
730
+ * Three's equirect mapping is hardcoded to Y-up: it assumes the HDR's horizon lies in the XZ plane
731
+ * and its zenith points along +Y. In a Z-up scene that leaves the environment on its side — the
732
+ * horizon runs vertically and image-based lighting arrives from +Y rather than from overhead. The
733
+ * result is a subtly wrong key direction that a neutral studio HDR hides but any sky/ground HDR
734
+ * makes obvious.
735
+ *
736
+ * Returns the Euler that rotates the map's native +Y zenith onto `up`. Identity for a Y-up scene.
737
+ * Apply to BOTH `scene.environmentRotation` and `scene.backgroundRotation` — they are independent
738
+ * properties, and setting only one makes the visible background disagree with the lighting.
739
+ */
740
+ declare function environmentRotationFor(up: THREE.Vector3): THREE.Euler;
741
+ /**
742
+ * Which world axis (`'x' | 'y' | 'z'`) the up vector most closely aligns with.
743
+ *
744
+ * Used for the grid plane and for the ground-offset axis — both need a single component index
745
+ * rather than a full vector. An off-axis up vector resolves to its dominant component.
746
+ */
747
+ declare function upToAxis(up: THREE.Vector3): 'x' | 'y' | 'z';
748
+
626
749
  /**
627
750
  * Crisp boundary/crease edges overlaid on meshes — the defining "technical drawing" look that makes
628
751
  * shaded geometry read as discrete objects rather than blobs.
629
752
  *
630
- * Built with `EdgesGeometry` (which keeps only edges whose adjacent faces meet above a threshold
631
- * angle, so flat tessellation noise is dropped) rendered as a fat `LineSegments2` using the same
632
- * `LineMaterial` family as curves (Phase 1) — so edges get controllable thickness, not the 1px cap
633
- * of `THREE.LineSegments`. The overlay is added as a *child* of each mesh, so it inherits the mesh's
634
- * transform and is disposed when the mesh subtree is cleared.
753
+ * Edge segments are extracted by `extractEdgeSegments` (see edge-extract.ts — an
754
+ * `EdgesGeometry`-equivalent that is ~an order of magnitude faster and worker-portable) and
755
+ * rendered as fat `LineSegments2` using the same `LineMaterial` family as curves — so edges get
756
+ * controllable thickness, not the 1px cap of `THREE.LineSegments`. The overlay is added as a
757
+ * *child* of each mesh, so it inherits the mesh's transform and is disposed when the mesh subtree
758
+ * is cleared.
759
+ *
760
+ * Performance model (docs/plans/4.edge-overlay-performance.md):
761
+ * - Extraction results are cached at two levels: per source `BufferGeometry` (object identity,
762
+ * helps instanced parts within one solve) and by *content fingerprint* in an LRU — the viewer
763
+ * rebuilds all geometry objects every solve, so only a content key survives re-solves.
764
+ * - {@link addEdgesAsync} runs extraction for large meshes in a Worker built from a blob URL
765
+ * (bundler-agnostic; falls back to inline extraction when Workers are unavailable), keeping the
766
+ * main thread free of the multi-second stalls extraction causes at millions of triangles.
767
+ * - Caps bound the pathological cases: meshes above `maxTriangles` are skipped outright (tagged
768
+ * `userData.edgesSkipped = 'triangle-cap'`), overlays above `maxSegments` lose the distance
769
+ * fade so they render in the cheaper opaque pass.
635
770
  *
636
771
  * Depth strategy: edges render at TRUE depth; the mesh's own surface is pushed back a hair with
637
772
  * polygonOffset instead (see {@link setSurfaceDepthOffset}). Biasing the lines toward the camera —
@@ -667,34 +802,120 @@ interface EdgeOptions {
667
802
  * covering only tens of pixels alias into dark noise; fading them keeps far zoom-outs clean.
668
803
  */
669
804
  distanceFade?: boolean;
805
+ /**
806
+ * Skip meshes above this triangle count entirely (default 4M) — extraction time is linear in
807
+ * triangles, and past this bound even the worker path burns seconds for a look the screen-space
808
+ * fallback approximates at constant cost. Skipped meshes are tagged
809
+ * `userData.edgesSkipped = 'triangle-cap'` so hosts can react (e.g. enable the render-pipeline
810
+ * edge pass).
811
+ */
812
+ maxTriangles?: number;
813
+ /**
814
+ * Above this many extracted segments (default 2M), an overlay drops the distance fade so it
815
+ * renders opaque — millions of blended fat-line quads are a fill-rate cliff; opaque ones aren't.
816
+ */
817
+ maxSegments?: number;
670
818
  }
671
819
  /** Tag on edge overlays so pick/fit/clear logic can recognize and skip or dispose them. */
672
820
  declare const EDGE_USERDATA_KIND = "edge-overlay";
821
+ /** `userData.edgesSkipped` value on meshes whose triangle count exceeded {@link EdgeOptions.maxTriangles}. */
822
+ declare const EDGES_SKIPPED_TRIANGLE_CAP = "triangle-cap";
823
+ /** Whether an object is an edge overlay (for pick/fit filters elsewhere). */
824
+ declare function isEdgeOverlay(object: THREE.Object3D): boolean;
673
825
  /**
674
826
  * Walk an object subtree and attach an edge overlay to every `Mesh` found, returning the created
675
827
  * overlays (so callers can dispose them explicitly if they don't clear the whole subtree). Meshes
676
828
  * that already carry an overlay are skipped, so this is safe to call more than once.
677
829
  *
678
830
  * Skips the floor and the grid (they're aids, not content) and anything already tagged as an edge.
831
+ *
832
+ * Fully synchronous — extraction for every mesh runs on the calling thread. Interactive hosts with
833
+ * potentially-large meshes should prefer {@link addEdgesAsync}, which offloads big extractions to
834
+ * a worker.
679
835
  */
680
836
  declare function addEdges(root: THREE.Object3D, options?: EdgeOptions): LineSegments2[];
681
- /** Whether an object is an edge overlay (for pick/fit filters elsewhere). */
682
- declare function isEdgeOverlay(object: THREE.Object3D): boolean;
837
+ /**
838
+ * Like {@link addEdges}, but extraction for large meshes runs in a Worker so the main thread never
839
+ * stalls. Small meshes (< ~25k triangles) still attach synchronously before this returns — only
840
+ * heavy extractions arrive a beat later. Resolves with every overlay actually attached.
841
+ *
842
+ * Late results are dropped (never attached) when the mesh has left the root's subtree (scene was
843
+ * cleared by a newer solve), the root's edges were removed via {@link removeEdges}, or another
844
+ * apply already attached an overlay to that mesh.
845
+ */
846
+ declare function addEdgesAsync(root: THREE.Object3D, options?: EdgeOptions): Promise<LineSegments2[]>;
683
847
  /**
684
848
  * Remove every edge overlay under `root`, disposing its geometry and material. The inverse of
685
- * {@link addEdges}; together they make edges a live on/off toggle. Returns how many were removed.
849
+ * {@link addEdges}/{@link addEdgesAsync}; together they make edges a live on/off toggle — this
850
+ * also cancels any in-flight async attaches for `root`. Returns how many were removed.
686
851
  */
687
852
  declare function removeEdges(root: THREE.Object3D): number;
688
853
 
689
854
  /**
690
- * Optional postprocessing pipeline for ambient occlusion. Default-OFF: the viewer only constructs
691
- * this when `render.ambientOcclusion` is enabled, and otherwise renders with a plain
692
- * `renderer.render` so the cheap path stays cheap (the chosen tradeoff — see cad-viewer-plan.md).
855
+ * Screen-space edge detection: draws crease/silhouette lines by finding depth and normal
856
+ * discontinuities, as a fullscreen pass — cost is O(pixels), independent of triangle count.
693
857
  *
694
- * Pipeline: RenderPass → GTAOPass → SMAAPass → OutputPass. GTAO (ground-truth AO) is the modern
695
- * replacement for SSAO/SAO — better contact shadows in crevices, the "engineered" depth cue.
696
- * `screenSpaceRadius` is on so the AO radius is in screen space, which keeps it scale-robust across
697
- * the viewer's mm→m scenes without per-scene tuning.
858
+ * This is the fallback look for scenes too heavy for geometry edge overlays (see
859
+ * docs/plans/4.edge-overlay-performance.md Phase 3): meshes over `EdgeOptions.maxTriangles` skip
860
+ * extraction, and this pass approximates the technical-drawing read at constant cost. Trade-offs
861
+ * vs geometry edges: uniform pixel width, one global color (no per-object tinting), view-dependent
862
+ * response, and gentle creases below the normal threshold don't register.
863
+ *
864
+ * Works like GTAOPass internally: the scene is re-rendered with a `MeshNormalMaterial` override
865
+ * into a normal target carrying a depth texture (a pattern this scene already tolerates for AO),
866
+ * then a fullscreen shader Roberts-crosses depth (relative view-Z difference, so far scenes don't
867
+ * dissolve into noise) and normals, and blends the edge color over the composed image.
868
+ *
869
+ * The render target is allocated lazily on first enabled render, so constructing the pass
870
+ * disabled costs nothing but the material.
871
+ */
872
+ interface EdgeDetectionOptions {
873
+ /** Edge line color. Default 0x222222 — matches the geometry overlays' default. */
874
+ color?: THREE.ColorRepresentation;
875
+ /** Edge blend strength 0–1. Default 1. */
876
+ opacity?: number;
877
+ /**
878
+ * Normal discontinuity threshold, in summed `1 - dot(n₁, n₂)` across the two diagonal pairs.
879
+ * Lower catches gentler creases. Default 0.4 (≈ the 44° crease default of geometry edges).
880
+ */
881
+ normalThreshold?: number;
882
+ /** Relative view-depth discontinuity threshold. Default 0.02 (2% of the center depth). */
883
+ depthThreshold?: number;
884
+ /** Sample offset in device px — line thickness. Default 1. */
885
+ thickness?: number;
886
+ }
887
+ declare class EdgeDetectionPass extends Pass {
888
+ camera: THREE.Camera;
889
+ private readonly scene;
890
+ private readonly normalMaterial;
891
+ private readonly edgeMaterial;
892
+ private readonly fsQuad;
893
+ private normalTarget;
894
+ private width;
895
+ private height;
896
+ constructor(scene: THREE.Scene, camera: THREE.Camera, width: number, height: number, options?: EdgeDetectionOptions);
897
+ private acquireNormalTarget;
898
+ setSize(width: number, height: number): void;
899
+ render(renderer: THREE.WebGLRenderer, writeBuffer: THREE.WebGLRenderTarget, readBuffer: THREE.WebGLRenderTarget): void;
900
+ dispose(): void;
901
+ }
902
+
903
+ /**
904
+ * Optional postprocessing pipeline. Default-OFF: the viewer only constructs this when a
905
+ * postprocessed feature (ambient occlusion, screen-space edges) is enabled, and otherwise renders
906
+ * with a plain `renderer.render` so the cheap path stays cheap (the chosen tradeoff — see
907
+ * cad-viewer-plan.md).
908
+ *
909
+ * Pipeline: RenderPass → GTAOPass? → EdgeDetectionPass? → SMAAPass → OutputPass. GTAO
910
+ * (ground-truth AO) is the modern replacement for SSAO/SAO — better contact shadows in crevices,
911
+ * the "engineered" depth cue. `screenSpaceRadius` is on so the AO radius is in screen space, which
912
+ * keeps it scale-robust across the viewer's mm→m scenes without per-scene tuning.
913
+ *
914
+ * EdgeDetectionPass draws crease/silhouette lines in screen space at O(pixels) — the fallback for
915
+ * scenes too triangle-heavy for geometry edge overlays (docs/plans/4.edge-overlay-performance.md).
916
+ * It sits before SMAA so its 1px lines get antialiased. Always constructed (its render target is
917
+ * lazy), so it can be toggled at runtime via {@link RenderPipeline.setEdgeDetection} without a
918
+ * pipeline rebuild.
698
919
  *
699
920
  * SMAA restores antialiasing on this path. The EffectComposer renders into an offscreen target, so
700
921
  * the WebGLRenderer's own MSAA (`antialias: true`) does nothing here — without a dedicated AA pass,
@@ -712,12 +933,18 @@ interface RenderPipeline {
712
933
  setSize(width: number, height: number, pixelRatio: number): void;
713
934
  /** Point the passes at the currently active camera (call when projection changes). */
714
935
  setCamera(camera: THREE.Camera): void;
936
+ /** Toggle the screen-space edge pass at runtime (no pipeline rebuild). */
937
+ setEdgeDetection(enabled: boolean): void;
938
+ /** Whether the screen-space edge pass currently runs. */
939
+ edgeDetectionEnabled(): boolean;
715
940
  dispose(): void;
716
941
  }
717
942
  interface RenderPipelineOptions {
718
943
  /** Tone mapping to apply in OutputPass (mirror the renderer's). */
719
944
  toneMapping: THREE.ToneMapping;
720
945
  toneMappingExposure: number;
946
+ /** Build the pipeline with the GTAO ambient-occlusion pass. Default true. */
947
+ ambientOcclusion?: boolean;
721
948
  /** AO strength 0–1. Default 1. */
722
949
  aoIntensity?: number;
723
950
  /**
@@ -726,6 +953,11 @@ interface RenderPipelineOptions {
726
953
  * pixelRatio is clamped to this. Default 1.
727
954
  */
728
955
  aoPixelRatio?: number;
956
+ /**
957
+ * Start with the screen-space edge pass enabled; pass an object to tune it. The pass itself is
958
+ * always constructed (cheap while disabled) so it can be toggled later via `setEdgeDetection`.
959
+ */
960
+ edgeDetection?: boolean | EdgeDetectionOptions;
729
961
  }
730
962
  declare function createRenderPipeline(renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.Camera, width: number, height: number, options: RenderPipelineOptions): RenderPipeline;
731
963
 
@@ -769,11 +1001,11 @@ declare const SCALE_FACTORS: Record<string, number>;
769
1001
  * // With debugging enabled
770
1002
  * const meshes = await getThreeMeshesFromComputeResponse(response, { debug: true });
771
1003
  *
772
- * // With advanced options
1004
+ * // Opt in to seating the model on the grid instead of using its Rhino coordinates
773
1005
  * const meshes = await getThreeMeshesFromComputeResponse(response, {
774
1006
  * debug: true,
775
1007
  * allowScaling: true,
776
- * allowAutoPosition: false,
1008
+ * allowAutoPosition: true,
777
1009
  * parsing: {
778
1010
  * mergeByMaterial: false,
779
1011
  * applyTransforms: true,
@@ -842,4 +1074,4 @@ interface DisplayItemParseOptions {
842
1074
  */
843
1075
  declare function parseDisplayItems(items: DisplayItem[] | undefined, options?: DisplayItemParseOptions): THREE.Object3D[];
844
1076
 
845
- 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, LOOKS, type LabelHandle, type LabelLayer, type LightingConfig, type Look, 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 };
1077
+ export { type CameraConfig, type CameraController, type CameraProjection, type ControlsConfig, DisplayBatch, DisplayItem, type DisplayItemParseOptions, EDGES_SKIPPED_TRIANGLE_CAP, EDGE_USERDATA_KIND, type EdgeDetectionOptions, EdgeDetectionPass, type EdgeOptions, type EdgesConfig, type EnvironmentConfig, type EventConfig, type FloorConfig, type GizmoConfig, type Grid, type GridConfig, type GridOptions, LOOKS, type LabelHandle, type LabelLayer, type LightingConfig, type Look, threeMaterials as Materials, type MeasureConfig, type MeasureOptions, type MeasureTool, MeshExtractionOptions, type RenderConfig, type RenderPipeline, type RenderPipelineOptions, SCALE_FACTORS, type ThreeInitializerOptions, type UpBasis, type ViewGizmo, type ViewPreset, addEdges, addEdgesAsync, applyOffset, buildUpBasis, computeCombinedBoundingBox, createCameraController, createGrid, createLabelLayer, createMeasureTool, createRenderPipeline, createViewGizmo, environmentRotationFor, getThreeMeshesFromComputeResponse, initThree, isEdgeOverlay, isoOffset, parseColor, parseDisplayItems, parseMeshBatchBlob, parseMeshBatchObject, removeEdges, snapToVertex, sunOffset, upToAxis, updateScene };
@@ -1,2 +1,2 @@
1
- import{K as v,L as c,M as j,N as z,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 x,p as d,u as C,v as g,w as h}from"./chunk-INP5HVA2.js";import"./chunk-Y3M7P3BW.js";export{n as EDGE_USERDATA_KIND,g as LOOKS,e as Materials,j as SCALE_FACTORS,l as addEdges,o as applyOffset,i as computeCombinedBoundingBox,a as createCameraController,s as createGrid,u as createLabelLayer,d as createMeasureTool,y as createRenderPipeline,p as createViewGizmo,z as getThreeMeshesFromComputeResponse,C as initThree,f as isEdgeOverlay,t as parseColor,h as parseDisplayItems,c as parseMeshBatchBlob,v as parseMeshBatchObject,m as removeEdges,x as snapToVertex,r as updateScene};
1
+ import{C as D,D as O,E as P,S as B,T as G,U as L,V as M,a as e,b as t,c as r,d as o,e as i,f as s,g as a,h as p,i as n,j as f,k as l,l as m,m as u,n as y,o as d,p as x,q as g,r as C,s as h,t as c,u as j,v,w as z,x as E}from"./chunk-KJVV3L5H.js";import"./chunk-Y3M7P3BW.js";export{d as EDGES_SKIPPED_TRIANGLE_CAP,y as EDGE_USERDATA_KIND,c as EdgeDetectionPass,O as LOOKS,e as Materials,L as SCALE_FACTORS,g as addEdges,C as addEdgesAsync,n as applyOffset,t as buildUpBasis,f as computeCombinedBoundingBox,l as createCameraController,m as createGrid,v as createLabelLayer,E as createMeasureTool,j as createRenderPipeline,u as createViewGizmo,i as environmentRotationFor,M as getThreeMeshesFromComputeResponse,D as initThree,x as isEdgeOverlay,r as isoOffset,p as parseColor,P as parseDisplayItems,G as parseMeshBatchBlob,B as parseMeshBatchObject,h as removeEdges,z as snapToVertex,o as sunOffset,s as upToAxis,a as updateScene};
2
2
  //# sourceMappingURL=visualization.js.map
package/package.json CHANGED
@@ -1,9 +1,11 @@
1
1
  {
2
2
  "name": "@selvajs/compute",
3
- "version": "3.1.0-beta.12",
3
+ "version": "3.1.0-beta.14",
4
4
  "description": "TypeScript library for Rhino Compute Server - Grasshopper and RhinoCommon",
5
5
  "author": "VektorNode",
6
6
  "license": "MIT",
7
+ "homepage": "https://github.com/VektorNode/selva#readme",
8
+ "bugs": "https://github.com/VektorNode/selva/issues",
7
9
  "repository": {
8
10
  "type": "git",
9
11
  "url": "git+https://github.com/VektorNode/selva.git",
@@ -103,7 +105,6 @@
103
105
  }
104
106
  },
105
107
  "devDependencies": {
106
- "@changesets/cli": "^2.30.0",
107
108
  "@eslint/js": "^10.0.1",
108
109
  "@types/node": "^22.20.1",
109
110
  "@types/three": "^0.184.0",
@@ -112,7 +113,6 @@
112
113
  "eslint-config-prettier": "^10.1.8",
113
114
  "eslint-plugin-svelte": "^3.20.0",
114
115
  "globals": "^17.5.0",
115
- "husky": "^9.1.7",
116
116
  "lint-staged": "^17.0.7",
117
117
  "prettier": "^3.8.3",
118
118
  "prettier-plugin-svelte": "^3.5.1",
@@ -139,13 +139,13 @@
139
139
  "format:check": "prettier --check .",
140
140
  "test": "vitest",
141
141
  "test:watch": "vitest watch",
142
+ "bench": "vitest bench --run",
142
143
  "contract:snapshot": "node scripts/fetch-server-contract.mjs",
143
144
  "contract:check": "node scripts/fetch-server-contract.mjs --check",
144
145
  "test:seams": "vitest run tests/contract",
145
146
  "test:seams:live": "RUN_LIVE_CONTRACT=1 vitest run tests/contract/server-contract.live.test.ts",
146
147
  "test:stats:live": "vitest run tests/contract/compute-server-stats.live.test.ts",
147
148
  "type-check": "tsc --noEmit",
148
- "build-docs": "typedoc",
149
- "release": "changeset publish"
149
+ "build-docs": "typedoc"
150
150
  }
151
151
  }