forgecad 0.9.4 → 0.9.5

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 (82) hide show
  1. package/dist/assets/{AdminPage-jwoEgwE_.js → AdminPage-uTtcSXtn.js} +1 -1
  2. package/dist/assets/{BlogPage-Ck7g3ue2.js → BlogPage-DYJMjWx3.js} +1 -1
  3. package/dist/assets/{DocsPage-9WaRC14b.js → DocsPage-C58f0K5v.js} +1 -6
  4. package/dist/assets/{EditorApp-Dja2jMmW.js → EditorApp-DNH1TEz1.js} +282 -62
  5. package/dist/assets/{EmbedViewer-37_PfMwv.js → EmbedViewer-CMXWA2LX.js} +2 -2
  6. package/dist/assets/{LandingPageProofDriven-CO8WL0CY.js → LandingPageProofDriven-CAu2OZFn.js} +1 -1
  7. package/dist/assets/{PricingPage-DADKGuOa.js → PricingPage-BIgW7m3X.js} +1 -1
  8. package/dist/assets/{SettingsPage-DKKI4W49.js → SettingsPage-N1l1tMXO.js} +1 -1
  9. package/dist/assets/{app-CwI02pTA.js → app-CFy7g5WP.js} +74 -12
  10. package/dist/assets/cli/{render-Kw5hLEcL.js → render-BrVVdj_T.js} +453 -41
  11. package/dist/assets/{evalWorker-D6ub3kfS.js → evalWorker-c_SB9gg3.js} +2057 -446
  12. package/dist/assets/{manifold-lru0jwVw.js → manifold-CRoBhJKH.js} +2 -2
  13. package/dist/assets/{manifold-CwDdMKyc.js → manifold-Cjk7WhRs.js} +1 -1
  14. package/dist/assets/{manifold-DTvmxSDf.js → manifold-Dp6pvFr6.js} +1 -1
  15. package/dist/assets/{renderSceneState-tvtNKNRi.js → renderSceneState-3DfsSASX.js} +1 -1
  16. package/dist/assets/{reportWorker-DeqktDGt.js → reportWorker-BLkuIoS8.js} +2052 -443
  17. package/dist/assets/{sectionPlaneMath-C8N0w8o3.js → sectionPlaneMath-CykEnkvQ.js} +2258 -518
  18. package/dist/cli/render.html +1 -1
  19. package/dist/docs/index.html +2 -2
  20. package/dist/docs-raw/AI/usage.md +0 -1
  21. package/dist/docs-raw/API/core/concepts.md +11 -1
  22. package/dist/docs-raw/CLI.md +64 -13
  23. package/dist/docs-raw/generated/assembly.md +8 -3
  24. package/dist/docs-raw/generated/concepts.md +44 -41
  25. package/dist/docs-raw/generated/core.md +97 -47
  26. package/dist/docs-raw/generated/curves.md +6 -580
  27. package/dist/docs-raw/generated/lib.md +40 -3
  28. package/dist/docs-raw/generated/output.md +6 -1
  29. package/dist/docs-raw/generated/sdf.md +50 -4
  30. package/dist/docs-raw/generated/viewport.md +1 -9
  31. package/dist/docs-raw/guides/inspection-bundles.md +31 -6
  32. package/dist/docs-raw/skills/forgecad-blockout-model.md +1 -0
  33. package/dist/docs-raw/skills/forgecad-image-replicator.md +3 -1
  34. package/dist/docs-raw/skills/forgecad-make-a-model.md +48 -4
  35. package/dist/docs-raw/skills/forgecad-render-inspect.md +3 -1
  36. package/dist/docs-raw/skills/forgecad-visual-spec.md +2 -0
  37. package/dist/docs-raw/skills/forgecad.md +2 -1
  38. package/dist/docs-raw/skills/index.md +0 -1
  39. package/dist/index.html +1 -1
  40. package/dist/sitemap.xml +6 -6
  41. package/dist-cli/blender/render.py +43 -8
  42. package/dist-cli/forgecad.js +4941 -1758
  43. package/dist-cli/forgecad.js.map +1 -1
  44. package/dist-skill/CONTEXT.md +255 -656
  45. package/dist-skill/SKILL-dev.md +2 -1
  46. package/dist-skill/SKILL.md +2 -1
  47. package/dist-skill/docs/API/core/concepts.md +11 -1
  48. package/dist-skill/docs/CLI.md +64 -13
  49. package/dist-skill/docs/generated/assembly.md +8 -3
  50. package/dist-skill/docs/generated/core.md +97 -47
  51. package/dist-skill/docs/generated/curves.md +6 -580
  52. package/dist-skill/docs/generated/lib.md +40 -3
  53. package/dist-skill/docs/generated/output.md +6 -1
  54. package/dist-skill/docs/generated/sdf.md +50 -4
  55. package/dist-skill/docs/generated/viewport.md +1 -9
  56. package/dist-skill/docs/guides/inspection-bundles.md +31 -6
  57. package/dist-skill/docs-dev/API/core/concepts.md +11 -1
  58. package/dist-skill/docs-dev/CLI.md +64 -13
  59. package/dist-skill/docs-dev/generated/assembly.md +8 -3
  60. package/dist-skill/docs-dev/generated/core.md +97 -47
  61. package/dist-skill/docs-dev/generated/curves.md +6 -580
  62. package/dist-skill/docs-dev/generated/lib.md +40 -3
  63. package/dist-skill/docs-dev/generated/output.md +6 -1
  64. package/dist-skill/docs-dev/generated/sdf.md +50 -4
  65. package/dist-skill/docs-dev/generated/viewport.md +1 -9
  66. package/dist-skill/docs-dev/guides/inspection-bundles.md +31 -6
  67. package/dist-skill/library/README.md +0 -1
  68. package/dist-skill/library/forgecad-blockout-model/SKILL.md +1 -0
  69. package/dist-skill/library/forgecad-image-replicator/SKILL.md +3 -1
  70. package/dist-skill/library/forgecad-make-a-model/SKILL.md +48 -4
  71. package/dist-skill/library/forgecad-render-inspect/SKILL.md +3 -1
  72. package/dist-skill/library/forgecad-visual-spec/SKILL.md +2 -0
  73. package/examples/api/drive-wheel-regions.forge.js +43 -0
  74. package/examples/api/sdf-circular-array-knurling.forge.js +19 -0
  75. package/examples/api/sdf-pattern2d-ceramic-ripple-set.forge.js +83 -0
  76. package/examples/api/sdf-pattern2d-grip-tread.forge.js +72 -0
  77. package/examples/api/sdf-pattern2d-orbital-jewelry.forge.js +62 -0
  78. package/examples/api/sdf-surface-basket-weave.forge.js +67 -0
  79. package/examples/api/sector-gear-body.forge.js +34 -0
  80. package/package.json +1 -1
  81. package/dist/docs-raw/skills/forgecad-api-dogfood.md +0 -130
  82. package/dist-skill/library/forgecad-api-dogfood/SKILL.md +0 -125
@@ -25,6 +25,7 @@ Prefer documented primitives, import rules, and placement strategies over invent
25
25
 
26
26
  - `.forge.js` — parametric part or assembly script; return a `Shape`, `Sketch`, `ShapeGroup`, `Assembly`, `SolvedAssembly`, array of renderables, or metadata object. Assemblies render directly; do not add `.toGroup()` unless you need `ShapeGroup` behavior.
27
27
  - Model the physical artifact, not an educational diagram. Do not add explanatory labels, arrows, legends, or text plaques unless the user explicitly asks for a presentation or teaching view. Product markings are allowed only when they would exist on the real object.
28
+ - Build the real closed CAD first. Do not bake cutaways, sectioned shells, permanently exploded layouts, or hidden-parts views into the default model just to show internals. Use viewer-only cut planes, `explodeView`, object hiding, transparency, or `render inspect` section channels after the artifact exists.
28
29
 
29
30
  ### Import and composition
30
31
 
@@ -58,10 +59,12 @@ Use the CLI to validate, inspect, and export the model the AI is editing. Keep c
58
59
  forgecad run path/to/model.forge.js
59
60
  forgecad run path/to/model.forge.js --debug-imports
60
61
  forgecad run path/to/model.forge.js --backend occt
62
+ forgecad check print path/to/model.forge.js --json
61
63
  forgecad check params path/to/model.forge.js --samples 12
62
64
  ```
63
65
 
64
66
  - `forgecad run` prints geometry diagnostics, object summaries, collisions, verification results, and solver info.
67
+ - `forgecad check print` reports collisions, mesh health, sampled walls, overhangs, and bed contact.
65
68
  - `forgecad check params` sweeps declared parameter ranges and reports crashes, degenerates, and new collisions.
66
69
 
67
70
  ## Visual Checks
@@ -126,7 +129,7 @@ Top-level declarations such as `const bom = ...`, `let scene = ...`, or `class S
126
129
 
127
130
  - Scripts re-execute on every parameter change (400ms debounce)
128
131
  - Geometry operations are **immutable** — shapes, sketches, groups, imported assemblies, and wood boards return new values instead of modifying in place
129
- - Must return one of: `Shape`, `Sketch`, `ShapeGroup`, `Assembly`, `SolvedAssembly`, `SdfShape`, `Array` of renderables, `Array` of `{ name, shape?, sketch?, group?, color? }`, or a **metadata object** (see below)
132
+ - Must return one of: `Shape`, `Sketch`, `ShapeGroup`, `Assembly`, `SolvedAssembly`, `SdfShape`, `Array` of renderables, `Array` of `{ name, tags?, shape?, sketch?, group?, color? }`, or a **metadata object** (see below)
130
133
 
131
134
  Top-level assembly scripts can return an unsolved `Assembly` directly; ForgeCAD solves it at default joint values for display. Return `assembly.solve(state)` when you want a specific pose. Do not call `.toGroup()` just to make an assembly render — use `.toGroup()` only when you specifically need `ShapeGroup` composition, group-style transforms, or named-child lookup.
132
135
 
@@ -159,6 +162,16 @@ return {
159
162
  };
160
163
  ```
161
164
 
165
+ Named return objects and named `group(...)` children can include `tags`. Tags are viewport metadata: they do not affect geometry, exports, face labels, or BOM rows, but the command palette can hide, show only, or focus every object with a selected tag.
166
+
167
+ ```javascript
168
+ return [
169
+ { name: 'Base Plate', tags: ['printed', 'structural'], shape: base },
170
+ { name: 'M4 Bolt A', tags: 'fastener', shape: boltA },
171
+ { name: 'M4 Bolt B', tags: 'fastener', shape: boltB },
172
+ ];
173
+ ```
174
+
162
175
  ## Coordinate System
163
176
 
164
177
  Z-up right-handed: X = left/right, Y = forward/back, Z = up/down.
@@ -218,15 +231,15 @@ For organic shapes, smooth blending, TPMS lattices, and surface deformations. Re
218
231
  - [Grouping & Local Coordinates](#grouping-local-coordinates) — `group`
219
232
  - [Section & Projection](#section-projection) — `intersectWithPlane`, `faceProfile`, `projectToPlane`
220
233
  - [Transforms](#transforms) — `composeChain`
221
- - [Backend Runtime](#backend-runtime) — `initKernel`, `setActiveBackend`, `activateBackend`, `getActiveBackend`
222
234
  - [Verification](#verification) — `spec`
223
235
  - [Shape](#shape) — Appearance, Face Topology, Edge Topology, Transforms, Booleans & Cutting, Features, Placement, Connectors, References, Measurement
224
236
  - [Transform](#transform)
225
237
  - [ShapeGroup](#shapegroup) — Children, Transforms, Placement, Connectors, References
226
238
  - [SurfacePattern](#surfacepattern)
239
+ - [Pattern2D](#pattern2d)
240
+ - [Pattern2DBuilder](#pattern2dbuilder)
227
241
  - [ShapeRef](#shaperef)
228
242
  - [ANCHOR3D_NAMES](#anchor3d-names)
229
- - [DEFAULT_ACTIVE_BACKEND](#default-active-backend)
230
243
  - [verify](#verify)
231
244
  - [Constraint](#constraint)
232
245
  - [Points](#points)
@@ -321,9 +334,11 @@ intersection(...inputs: ShapeOperandInput[]): Shape
321
334
 
322
335
  ### Edge Features
323
336
 
324
- #### `fillet()` — Apply fillets (rounded edges) to one or more edges of a shape.
337
+ #### `fillet()` — Apply experimental fillets (rounded edges) to one or more edges of a shape.
325
338
 
326
- Works on edge selections from any active backend. Truck and OCCT route edge finishes through native kernel operations; unsupported selections fail as explicit kernel gaps instead of using TypeScript geometry fallbacks.
339
+ **Experimental**: fillets are still backend-sensitive. The Manifold backend is known to produce incorrect results for some edge-finish cases, and the OCCT backend can be very slow, especially with broad edge selections. Prefer targeted edge selectors and inspect the result before treating it as production-ready geometry.
340
+
341
+ Edge selections compile into backend operations; unsupported selections fail as explicit kernel gaps instead of using TypeScript geometry fallbacks.
327
342
 
328
343
  The `edges` parameter is flexible:
329
344
 
@@ -349,9 +364,11 @@ fillet(myShape, 3, edges)
349
364
  fillet(shape: Shape, radius: number, edges?: EdgeSelector, segments?: number): Shape
350
365
  ```
351
366
 
352
- #### `chamfer()` — Apply chamfers (beveled edges) to one or more edges of a shape.
367
+ #### `chamfer()` — Apply experimental chamfers (beveled edges) to one or more edges of a shape.
368
+
369
+ **Experimental**: chamfers are still backend-sensitive. The Manifold backend is known to produce incorrect results for some edge-finish cases, and the OCCT backend can be very slow, especially with broad edge selections. Prefer targeted edge selectors and inspect the result before treating it as production-ready geometry.
353
370
 
354
- Produces a 45° bevel at the specified `size` (distance from edge). Works on edge selections from any active backend. Truck and OCCT route chamfers through native kernel operations; unsupported selections fail as explicit kernel gaps instead of using TypeScript geometry fallbacks.
371
+ Produces a 45° bevel at the specified `size` (distance from edge). Edge selections compile into backend operations; unsupported selections fail as explicit kernel gaps instead of using TypeScript geometry fallbacks.
355
372
 
356
373
  The `edges` parameter accepts the same options as `fillet()`: inline `EdgeQuery`, pre-selected `EdgeSegment`/`EdgeSegment[]`, or `undefined` (all sharp edges).
357
374
 
@@ -576,11 +593,8 @@ selectEdges(shape: Shape, query?: EdgeQuery): EdgeSegment[]
576
593
  | `normalA` | `Vec3` | Normal of first adjacent face. |
577
594
  | `normalB` | `Vec3` | Normal of second adjacent face (same as normalA for boundary edges). |
578
595
  | `boundary` | `boolean` | true if this is a boundary (unmatched) edge — unusual for closed solids. |
579
- | `nativeTopology?` | `EdgeNativeTopologyRef` | Native kernel topology identity when the active backend can provide one. |
580
596
  | `start`, `end`, `midpoint`, `length` | | — |
581
597
 
582
- `EdgeNativeTopologyRef`: `{ backend: "truck", edge: number }`
583
-
584
598
  #### `selectEdge()` — Select the single best-matching edge from a shape.
585
599
 
586
600
  When `near` is specified, returns the edge whose midpoint is closest to that point. Otherwise returns the first matching edge in mesh order. Throws if no edges match the query — useful as a guard when you expect exactly one result.
@@ -681,7 +695,7 @@ importSvgSketch(fileName: string, options?: SvgImportOptions): Sketch
681
695
  importMesh(fileName: string, options?: { scale?: number; center?: boolean; }): Shape
682
696
  ```
683
697
 
684
- #### `importStep()` — Import a STEP file (.step, .stp) as an exact OCCT-backed Shape. Preserves NURBS curves, B-spline surfaces, and exact topology. Requires `setActiveBackend('occt')`.
698
+ #### `importStep()` — Import a STEP file (.step, .stp) as an exact OCCT-backed Shape. Preserves NURBS curves, B-spline surfaces, and exact topology. Requires running with the OCCT backend.
685
699
 
686
700
  ```ts
687
701
  importStep(fileName: string): Shape
@@ -817,18 +831,21 @@ Param.list<T extends Record<string, number | boolean | string>>(name: string, de
817
831
 
818
832
  Unlike union(), child colors and individual identities are preserved. Children can be plain shapes, named descriptors ({ name, shape/sketch/group }), or nested groups. The returned ShapeGroup supports all Shape transforms (translate, rotate, etc.).
819
833
 
834
+ Named descriptors can include `tags` for viewport organization. Tags do not affect geometry; they let the command palette hide, show only, or focus all objects with the same tag.
835
+
820
836
  **Local coordinate pattern:** Build child parts at the origin (local coordinates), then group and translate once to place the whole assembly. This eliminates the error-prone pattern of manually adding parent offsets to every sub-part.
821
837
 
822
838
  ```js
823
- // BAD every sub-part repeats the parent's global offset
824
- const unitX = 0, unitY = -18, unitZ = 70;
825
- const body = roundedBox(100, 20, 32, 4).translate(unitX, unitY, unitZ);
826
- const panel = box(98, 2, 18).translate(unitX, unitY - 12, unitZ + 4);
827
- const louver = box(88, 2, 6).translate(unitX, unitY - 14, unitZ - 11);
839
+ const body = roundedBox(100, 20, 32, 4);
840
+ const panel = box(98, 2, 18).translate(0, -12, 4);
841
+ const louver = box(88, 2, 6).translate(0, -14, -11);
842
+ const indoorUnit = group(
843
+ { name: 'Body', shape: body },
844
+ { name: 'Panel', tags: 'cover', shape: panel },
845
+ { name: 'Louver', tags: ['cover', 'moving'], shape: louver },
846
+ ).translate(0, -18, 70);
828
847
  ```
829
848
 
830
- // GOOD — build at origin, group, translate once const body = roundedBox(100, 20, 32, 4); const panel = box(98, 2, 18).translate(0, -12, 4); const louver = box(88, 2, 6).translate(0, -14, -11); const indoorUnit = group( { name: 'Body', shape: body }, { name: 'Panel', shape: panel }, { name: 'Louver', shape: louver }, ).translate(0, -18, 70);
831
-
832
849
  ```ts
833
850
  group(...items: GroupInput[]): ShapeGroup
834
851
  ```
@@ -863,32 +880,6 @@ projectToPlane(shape: Shape, plane: PlaneSpec): Sketch
863
880
  composeChain(...steps: TransformInput[]): Transform
864
881
  ```
865
882
 
866
- ### Backend Runtime
867
-
868
- #### `initKernel()`
869
-
870
- ```ts
871
- initKernel(): Promise<unknown>
872
- ```
873
-
874
- #### `setActiveBackend()`
875
-
876
- ```ts
877
- setActiveBackend(backend: ActiveBackend): void
878
- ```
879
-
880
- #### `activateBackend()` — Set the active backend and ensure its WASM module is initialized. Call this instead of `setActiveBackend` when you're about to execute code — it guarantees the backend is ready, not just selected.
881
-
882
- ```ts
883
- activateBackend(backend: ActiveBackend): Promise<void>
884
- ```
885
-
886
- #### `getActiveBackend()`
887
-
888
- ```ts
889
- getActiveBackend(): ActiveBackend
890
- ```
891
-
892
883
  ### Verification
893
884
 
894
885
  #### `spec()` — Create a named, reusable bundle of verification checks.
@@ -1874,6 +1865,82 @@ color(hex: string): ShapeGroup
1874
1865
  | `body` | `string` | Function body: receives (u, v) in surface mm, returns height displacement. |
1875
1866
  | `constants` | `Record<string, number>` | Named constants injected into the function. |
1876
1867
 
1868
+ ### `Pattern2D`
1869
+
1870
+ #### `add()` — Add this pattern to one or more patterns or constant height offsets.
1871
+
1872
+ ```ts
1873
+ add(...patterns: Pattern2DInput[]): Pattern2D
1874
+ ```
1875
+
1876
+ #### `subtract()` — Subtract another pattern or constant height offset from this pattern.
1877
+
1878
+ ```ts
1879
+ subtract(pattern: Pattern2DInput): Pattern2D
1880
+ ```
1881
+
1882
+ #### `multiply()` — Multiply this pattern by one or more patterns or numeric scale factors.
1883
+
1884
+ ```ts
1885
+ multiply(...patterns: Pattern2DInput[]): Pattern2D
1886
+ ```
1887
+
1888
+ #### `min()` — Keep the lower height between this pattern and one or more other patterns.
1889
+
1890
+ ```ts
1891
+ min(...patterns: Pattern2DInput[]): Pattern2D
1892
+ ```
1893
+
1894
+ #### `max()` — Keep the higher height between this pattern and one or more other patterns.
1895
+
1896
+ ```ts
1897
+ max(...patterns: Pattern2DInput[]): Pattern2D
1898
+ ```
1899
+
1900
+ #### `clamp()` — Limit pattern height to the inclusive `[min, max]` range in millimeters.
1901
+
1902
+ ```ts
1903
+ clamp(min: number, max: number): Pattern2D
1904
+ ```
1905
+
1906
+ #### `abs()` — Convert negative heights to positive heights.
1907
+
1908
+ ```ts
1909
+ abs(): Pattern2D
1910
+ ```
1911
+
1912
+ #### `negate()` — Flip the pattern height sign.
1913
+
1914
+ ```ts
1915
+ negate(): Pattern2D
1916
+ ```
1917
+
1918
+ ### `Pattern2DBuilder`
1919
+
1920
+ #### `constant()` — Create a constant-height pattern in millimeters.
1921
+
1922
+ ```ts
1923
+ constant(value?: number): Pattern2D
1924
+ ```
1925
+
1926
+ #### `sineWave()` — Create a sinusoidal wave pattern in UV space.
1927
+
1928
+ ```ts
1929
+ sineWave(options: Pattern2DSineWaveOptions): Pattern2D
1930
+ ```
1931
+
1932
+ #### `stripes()` — Create recessed stripe bands in UV space.
1933
+
1934
+ ```ts
1935
+ stripes(options: Pattern2DStripesOptions): Pattern2D
1936
+ ```
1937
+
1938
+ #### `overUnderWeave()` — Create an over-under woven relief pattern in UV space.
1939
+
1940
+ ```ts
1941
+ overUnderWeave(options: Pattern2DOverUnderWeaveOptions): Pattern2D
1942
+ ```
1943
+
1877
1944
  ### `ShapeRef`
1878
1945
 
1879
1946
  A first-class reference path over a shape's semantic faces and face relationships.
@@ -1996,10 +2063,6 @@ toString(): string
1996
2063
 
1997
2064
  ### `ANCHOR3D_NAMES`
1998
2065
 
1999
- ### `DEFAULT_ACTIVE_BACKEND`
2000
-
2001
- Default geometry backend when no explicit backend is selected.
2002
-
2003
2066
  ### `verify`
2004
2067
 
2005
2068
  - `that(label: string, check: () => boolean, message?: string): void` — Custom predicate check.
@@ -4125,7 +4188,7 @@ Smooth curves, lofted surfaces, swept solids, splines, and high-level product sk
4125
4188
  ## Contents
4126
4189
 
4127
4190
  - [Curves & Surfacing](#curves-surfacing) — `hermiteTransitionG2`, `nurbs3d`, `spline2d`, `spline3d`, `loft`, `loftAlongSpine`, `sweep`, `variableSweep`, `nurbsSurface`, `surfacePatch`, `transitionCurve`, `transitionSurface`, `connectEdges`
4128
- - [Surface Members](#surface-members) — `surfaceBand`, `compileSurfaceMember`, `SurfaceBody`
4191
+ - [Surface Members](#surface-members) — `surfaceBand`, `SurfaceBody`
4129
4192
  - [Curve3D](#curve3d)
4130
4193
  - [NurbsCurve3D](#nurbscurve3d)
4131
4194
  - [NurbsSurface](#nurbssurface)
@@ -4142,7 +4205,6 @@ Smooth curves, lofted surfaces, swept solids, splines, and high-level product sk
4142
4205
  - [ProductSpoutBuilder](#productspoutbuilder)
4143
4206
  - [ProductHandleBuilder](#producthandlebuilder)
4144
4207
  - [ProductHandleFeature](#producthandlefeature)
4145
- - [ProductRibbonBuilder](#productribbonbuilder)
4146
4208
  - [CylinderCarrier](#cylindercarrier)
4147
4209
  - [PlaneCarrier](#planecarrier)
4148
4210
  - [ProductSkinCarrier](#productskincarrier)
@@ -4490,11 +4552,8 @@ connectEdges(edgeA: EdgeSegment, edgeB: EdgeSegment, options?: ConnectEdgesOptio
4490
4552
  | `normalA` | `Vec3` | Normal of first adjacent face. |
4491
4553
  | `normalB` | `Vec3` | Normal of second adjacent face (same as normalA for boundary edges). |
4492
4554
  | `boundary` | `boolean` | true if this is a boundary (unmatched) edge — unusual for closed solids. |
4493
- | `nativeTopology?` | `EdgeNativeTopologyRef` | Native kernel topology identity when the active backend can provide one. |
4494
4555
  | `start`, `end`, `midpoint`, `length` | | — |
4495
4556
 
4496
- `EdgeNativeTopologyRef`: `{ backend: "truck", edge: number }`
4497
-
4498
4557
 
4499
4558
  **`ConnectEdgesOptions`** extends TransitionSurfaceOptions
4500
4559
 
@@ -4517,63 +4576,6 @@ connectEdges(edgeA: EdgeSegment, edgeB: EdgeSegment, options?: ConnectEdgesOptio
4517
4576
  surfaceBand<C extends SurfaceCoordinate>(path: SurfacePath<C> | SurfacePathBuilder<C>, width: WidthProfile, cap?: SurfaceBandCap): SurfaceBand<C>
4518
4577
  ```
4519
4578
 
4520
- #### `compileSurfaceMember()`
4521
-
4522
- ```ts
4523
- compileSurfaceMember<C extends SurfaceCoordinate>(input: Omit<SurfaceMemberSpec<C>, "section"> & { section: MemberSection | MemberSectionInput; }): CompiledSurfaceMember
4524
- ```
4525
-
4526
- **`SurfaceMemberSpec`**: `name: string`, `kind: SurfaceMemberKind`, `path?: SurfacePath<C> | SurfacePathBuilder<C>`, `anchor?: SurfaceAnchor<C>`, `size?: { width: number; height: number; }`, `section: MemberSection`, `features: MemberFeature[]`, `capStyle?: SurfaceBandCap`, `explicitAnchors?: SurfaceMemberExplicitAnchor<C>[]`
4527
-
4528
- `SurfaceAnchor`: `{ carrier: CarrierSurface<C>, coordinate: C }`
4529
-
4530
- `CarrierSurface`: `{ name: string, kind: SurfaceCarrierKind }`
4531
-
4532
- **`MemberSection`**: `width?: number`, `thickness: number`, `edgeRadius: number`, `direction: MemberOutwardDirection`, `material?: ProductMaterial`, `stations: MemberSectionStation[]`
4533
-
4534
- `ProductMaterial`: `{ color?: string, material?: ShapeMaterialProps }`
4535
-
4536
- **`ShapeMaterialProps`**
4537
-
4538
- | Option | Type | Description |
4539
- |--------|------|-------------|
4540
- | `metalness?` | `number` | Metalness factor (0 = dielectric, 1 = metal). Default: 0.05 |
4541
- | `roughness?` | `number` | Roughness factor (0 = mirror, 1 = fully diffuse). Default: 0.35 |
4542
- | `emissive?` | `string` | Emissive glow color (hex string, e.g. "#ff6b35"). |
4543
- | `emissiveIntensity?` | `number` | Emissive intensity multiplier. Default: 1 |
4544
- | `opacity?` | `number` | Opacity (0 = fully transparent, 1 = fully opaque). Default: 1 |
4545
- | `wireframe?` | `boolean` | Render as wireframe. Default: false |
4546
- | `clearcoat?` | `number` | Clearcoat intensity (0–1). Default: 0.1 |
4547
- | `clearcoatRoughness?` | `number` | Clearcoat roughness (0–1). Default: 0.4 |
4548
- | `transmission?` | `number` | Glass/translucency transmission factor (0–1). Renderer support depends on target. |
4549
- | `ior?` | `number` | Index of refraction for transmissive materials. Typical glass is ~1.45. |
4550
- | `thickness?` | `number` | Approximate transmissive volume thickness in model units. |
4551
- | `specularIntensity?` | `number` | Specular highlight intensity (0–1). |
4552
- | `specularColor?` | `string` | Specular highlight tint. |
4553
- | `reflectivity?` | `number` | Reflection strength for supported renderers (0–1). |
4554
-
4555
- `MemberSectionStation`: `{ t: number, width?: number, thickness?: number }`
4556
-
4557
- **`MemberFeature`**: `type: MemberFeatureType`, `name?: string`, `length?: number`, `width?: number`, `diameter?: number`, `counterboreDiameter?: number`, `clearanceDiameter?: number`, `height?: number`, `depth?: number`, `count?: number`, `along?: number`, `across?: number`, `verticalTravel?: number`
4558
-
4559
- `SurfaceMemberExplicitAnchor`: `{ name: string, coordinate: C, carrierName?: string }`
4560
-
4561
- **`MemberSectionInput`**: `width?: number`, `thickness: number`, `edgeRadius?: number`, `direction?: MemberOutwardDirection`, `material?: ProductMaterial`, `stations?: MemberSectionStation[]`
4562
-
4563
- **`CompiledSurfaceMember`**: `name: string`, `shape: Shape`, `mesh: MemberMesh`, `diagnostics: SurfaceDiagnostic[]`, `anchors: { start?: CompiledSurfaceMemberAnchor; end?: CompiledSurfaceMemberAnchor; center: CompiledSurfaceMemberAnchor; landings?: CompiledSurfaceMemberAnchor[]; boundaries?: CompiledSurfaceMemberAnchor[]; features?: CompiledSurfaceMemberAnchor[]; explicit?: CompiledSurfaceMemberAnchor[]; }`
4564
-
4565
- `MemberMesh`: `{ vertices: Vec3[], triangles: Array<[ number, number, number ]> }`
4566
-
4567
- **`SurfaceDiagnostic`**
4568
- - `code?: SurfaceDiagnosticCode` — Stable machine-readable repair hook. Messages may change; codes should not.
4569
- - Also: `category: "carrier" | "path" | "region" | "member" | "feature" | "join" | "compiler", level: "info" | "warning" | "error", message: string, subject?: string`
4570
-
4571
- **`SurfaceMemberAnchorIR`**: `role: SurfaceMemberAnchorRole`, `kind: "member-end" | "member-center" | "member-landing" | "member-boundary" | "plate-edge" | "plate-center" | "plate-landing" | "feature-center" | "surface-coordinate"`, `name?: string`, `point: Vec3`, `normal?: Vec3`
4572
-
4573
- `CompiledSurfaceMemberAnchor`: `{ frame?: SurfaceFrame }`
4574
-
4575
- **`SurfaceFrame`**: `point: Vec3`, `normal: Vec3`, `tangentAlong: Vec3`, `tangentAcross: Vec3`, `matrix: Mat4`, `carrier: string`, `representation: SurfaceCarrierKind | string`, `coordinate: SurfaceCoordinate`
4576
-
4577
4579
  #### `SurfaceBody()` — Start a surface-member body builder for straps, inlays, guards, braces, cuffs, and similar physical members that live on a carrier surface.
4578
4580
 
4579
4581
  ```js
@@ -5149,18 +5151,6 @@ with(...children: GroupInput[]): ShapeGroup
5149
5151
  integrate(...details: Shape[]): Shape
5150
5152
  ```
5151
5153
 
5152
- #### `diagnostics()` — Return lowering representation, station names, rail names, and warnings.
5153
-
5154
- ```ts
5155
- diagnostics(): ProductSkinDiagnostics
5156
- ```
5157
-
5158
- **`ProductSkinDiagnostics`**: `representation: ProductSkinRepresentation`, `lowering: string[]`, `warnings: string[]`, `stationNames: string[]`, `railNames: string[]`
5159
-
5160
- **`ProductSkinRepresentation`** — Reported lowering mode for ProductSkin and conformal feature diagnostics.
5161
-
5162
- `"exact" | "sampled" | "mixed" | "fallback"`
5163
-
5164
5154
  #### `uv()` — Create a side/u/v surface-ref query on this skin.
5165
5155
 
5166
5156
  ```ts
@@ -5216,7 +5206,7 @@ stationAt(vOrAxis: number): { ... }
5216
5206
  frame(query: ProductSkinRefQuery): ProductSurfaceFrame
5217
5207
  ```
5218
5208
 
5219
- **`ProductSurfaceFrame`**: `point: Vec3`, `normal: Vec3`, `tangentU: Vec3`, `tangentV: Vec3`, `matrix: Mat4`, `skin: string`, `representation: ProductSkinRepresentation`
5209
+ `ProductSurfaceFrame`: `{ point: Vec3, normal: Vec3, tangentU: Vec3, tangentV: Vec3, matrix: Mat4, skin: string }`
5220
5210
 
5221
5211
  ### `ProductSurfaceRef`
5222
5212
 
@@ -5234,25 +5224,6 @@ frame(query: ProductSkinRefQuery): ProductSurfaceFrame
5234
5224
  frame(overrides?: Partial<ProductSkinRefQuery>): ProductSurfaceFrame
5235
5225
  ```
5236
5226
 
5237
- **`ProductSkinRefQuery`**
5238
-
5239
- | Option | Type | Description |
5240
- |--------|------|-------------|
5241
- | `side` | `ProductSkinSide` | Side of the product skin. `front` is the minimum axis cap, `rear`/`back` is the maximum axis cap. |
5242
- | `u?` | `number` | Across-side parameter for side refs. Defaults to 0.5. |
5243
- | `v?` | `number` | Along-axis parameter, 0 at the first cap and 1 at the rear/back cap. Defaults to 0.5. |
5244
- | `offset?` | `number` | Positive distance away from the surface along the resolved normal. |
5245
-
5246
- **`ProductSkinSide`** — Semantic side of a ProductSkin. `back` is accepted as an alias for `rear`.
5247
-
5248
- `"left" | "right" | "top" | "bottom" | "front" | "rear" | "back"`
5249
-
5250
- **`ProductSurfaceFrame`**: `point: Vec3`, `normal: Vec3`, `tangentU: Vec3`, `tangentV: Vec3`, `matrix: Mat4`, `skin: string`, `representation: ProductSkinRepresentation`
5251
-
5252
- **`ProductSkinRepresentation`** — Reported lowering mode for ProductSkin and conformal feature diagnostics.
5253
-
5254
- `"exact" | "sampled" | "mixed" | "fallback"`
5255
-
5256
5227
  #### `with()` — Return a copy of this ref with side/u/v/offset overrides.
5257
5228
 
5258
5229
  ```ts
@@ -5297,31 +5268,12 @@ ref(u?: number, v?: number, offset?: number): ProductSurfaceRef
5297
5268
  uv(u?: number, v?: number, offset?: number): ProductSkinRefQuery
5298
5269
  ```
5299
5270
 
5300
- **`ProductSkinRefQuery`**
5301
-
5302
- | Option | Type | Description |
5303
- |--------|------|-------------|
5304
- | `side` | `ProductSkinSide` | Side of the product skin. `front` is the minimum axis cap, `rear`/`back` is the maximum axis cap. |
5305
- | `u?` | `number` | Across-side parameter for side refs. Defaults to 0.5. |
5306
- | `v?` | `number` | Along-axis parameter, 0 at the first cap and 1 at the rear/back cap. Defaults to 0.5. |
5307
- | `offset?` | `number` | Positive distance away from the surface along the resolved normal. |
5308
-
5309
- **`ProductSkinSide`** — Semantic side of a ProductSkin. `back` is accepted as an alias for `rear`.
5310
-
5311
- `"left" | "right" | "top" | "bottom" | "front" | "rear" | "back"`
5312
-
5313
5271
  #### `frame()` — Resolve a point/frame on this surface using the builder's side.
5314
5272
 
5315
5273
  ```ts
5316
5274
  frame(query?: Partial<ProductSkinRefQuery>): ProductSurfaceFrame
5317
5275
  ```
5318
5276
 
5319
- **`ProductSurfaceFrame`**: `point: Vec3`, `normal: Vec3`, `tangentU: Vec3`, `tangentV: Vec3`, `matrix: Mat4`, `skin: string`, `representation: ProductSkinRepresentation`
5320
-
5321
- **`ProductSkinRepresentation`** — Reported lowering mode for ProductSkin and conformal feature diagnostics.
5322
-
5323
- `"exact" | "sampled" | "mixed" | "fallback"`
5324
-
5325
5277
  #### `ribbon()` — Start a conformal ribbon on this skin side.
5326
5278
 
5327
5279
  Path points use side-local `u`/`v` coordinates; this builder supplies the side. The returned ProductRibbonBuilder is already bound to the source skin and can be further configured before build(). Use `widthSamples` >= 3 when the ribbon must visibly wrap over curved product sections instead of behaving like a flat strip.
@@ -5399,11 +5351,7 @@ stations(stations: Array<ProductStationBuilder | ProductStationSpec>): this
5399
5351
 
5400
5352
  `ProductStationProfile`: `{ sketch: Sketch, width: number, depth: number, kind: ProductProfileKind, radius?: number, exponent?: number }`
5401
5353
 
5402
- **`ProductProfileKind`**
5403
-
5404
- `"oval" | "roundedRect" | "circle" | "superEllipse" | "custom"`
5405
-
5406
- #### `rails()` — Attach guide rails as ProductSkin IR metadata and diagnostics.
5354
+ #### `rails()` — Attach named guide rails for product-skin construction and downstream surface references.
5407
5355
 
5408
5356
  ```ts
5409
5357
  rails(rails: Record<string, ProductRailSpec>): this
@@ -5421,19 +5369,6 @@ rails(rails: Record<string, ProductRailSpec>): this
5421
5369
  ref(name: string, query: ProductSkinRefQuery): this
5422
5370
  ```
5423
5371
 
5424
- **`ProductSkinRefQuery`**
5425
-
5426
- | Option | Type | Description |
5427
- |--------|------|-------------|
5428
- | `side` | `ProductSkinSide` | Side of the product skin. `front` is the minimum axis cap, `rear`/`back` is the maximum axis cap. |
5429
- | `u?` | `number` | Across-side parameter for side refs. Defaults to 0.5. |
5430
- | `v?` | `number` | Along-axis parameter, 0 at the first cap and 1 at the rear/back cap. Defaults to 0.5. |
5431
- | `offset?` | `number` | Positive distance away from the surface along the resolved normal. |
5432
-
5433
- **`ProductSkinSide`** — Semantic side of a ProductSkin. `back` is accepted as an alias for `rear`.
5434
-
5435
- `"left" | "right" | "top" | "bottom" | "front" | "rear" | "back"`
5436
-
5437
5372
  #### `refs()` — Publish multiple named semantic surface refs on the skin.
5438
5373
 
5439
5374
  ```ts
@@ -5452,27 +5387,6 @@ uv(side: ProductSkinSide, u?: number, v?: number): ProductSkinRefQuery
5452
5387
  material(material: ProductMaterial): this
5453
5388
  ```
5454
5389
 
5455
- `ProductMaterial`: `{ color?: string, material?: ShapeMaterialProps }`
5456
-
5457
- **`ShapeMaterialProps`**
5458
-
5459
- | Option | Type | Description |
5460
- |--------|------|-------------|
5461
- | `metalness?` | `number` | Metalness factor (0 = dielectric, 1 = metal). Default: 0.05 |
5462
- | `roughness?` | `number` | Roughness factor (0 = mirror, 1 = fully diffuse). Default: 0.35 |
5463
- | `emissive?` | `string` | Emissive glow color (hex string, e.g. "#ff6b35"). |
5464
- | `emissiveIntensity?` | `number` | Emissive intensity multiplier. Default: 1 |
5465
- | `opacity?` | `number` | Opacity (0 = fully transparent, 1 = fully opaque). Default: 1 |
5466
- | `wireframe?` | `boolean` | Render as wireframe. Default: false |
5467
- | `clearcoat?` | `number` | Clearcoat intensity (0–1). Default: 0.1 |
5468
- | `clearcoatRoughness?` | `number` | Clearcoat roughness (0–1). Default: 0.4 |
5469
- | `transmission?` | `number` | Glass/translucency transmission factor (0–1). Renderer support depends on target. |
5470
- | `ior?` | `number` | Index of refraction for transmissive materials. Typical glass is ~1.45. |
5471
- | `thickness?` | `number` | Approximate transmissive volume thickness in model units. |
5472
- | `specularIntensity?` | `number` | Specular highlight intensity (0–1). |
5473
- | `specularColor?` | `string` | Specular highlight tint. |
5474
- | `reflectivity?` | `number` | Reflection strength for supported renderers (0–1). |
5475
-
5476
5390
  #### `color()` — Apply a simple color override to the lowered skin.
5477
5391
 
5478
5392
  ```ts
@@ -5485,7 +5399,7 @@ color(color: string): this
5485
5399
  edgeLength(value: number): this
5486
5400
  ```
5487
5401
 
5488
- #### `wall()` — Records a target wall thickness; v1 keeps exterior skin lowering sampled and reports wall as a diagnostic.
5402
+ #### `wall()` — Record intended wall thickness for product design metadata. Use explicit shelling when the model needs real inner-wall geometry.
5489
5403
 
5490
5404
  ```ts
5491
5405
  wall(thickness: number): this
@@ -5563,7 +5477,7 @@ circle(diameter: number, options?: { segments?: number; }): this
5563
5477
  custom(sketch: Sketch, width: number, depth: number): this
5564
5478
  ```
5565
5479
 
5566
- #### `crown()` — Stores a semantic crown amount for diagnostics and future rail solving.
5480
+ #### `crown()` — Set the station crown amount for soft product-section intent.
5567
5481
 
5568
5482
  ```ts
5569
5483
  crown(amount: number): this
@@ -5575,14 +5489,6 @@ crown(amount: number): this
5575
5489
  toSpec(): ProductStationSpec
5576
5490
  ```
5577
5491
 
5578
- `ProductStationSpec`: `{ name: string, center: Vec3, profile: ProductStationProfile, crown?: number }`
5579
-
5580
- `ProductStationProfile`: `{ sketch: Sketch, width: number, depth: number, kind: ProductProfileKind, radius?: number, exponent?: number }`
5581
-
5582
- **`ProductProfileKind`**
5583
-
5584
- `"oval" | "roundedRect" | "circle" | "superEllipse" | "custom"`
5585
-
5586
5492
  ### `ProductPanelBuilder`
5587
5493
 
5588
5494
  **Properties:**
@@ -5623,27 +5529,6 @@ thickness(thickness: number): this
5623
5529
  material(material: ProductMaterial): this
5624
5530
  ```
5625
5531
 
5626
- `ProductMaterial`: `{ color?: string, material?: ShapeMaterialProps }`
5627
-
5628
- **`ShapeMaterialProps`**
5629
-
5630
- | Option | Type | Description |
5631
- |--------|------|-------------|
5632
- | `metalness?` | `number` | Metalness factor (0 = dielectric, 1 = metal). Default: 0.05 |
5633
- | `roughness?` | `number` | Roughness factor (0 = mirror, 1 = fully diffuse). Default: 0.35 |
5634
- | `emissive?` | `string` | Emissive glow color (hex string, e.g. "#ff6b35"). |
5635
- | `emissiveIntensity?` | `number` | Emissive intensity multiplier. Default: 1 |
5636
- | `opacity?` | `number` | Opacity (0 = fully transparent, 1 = fully opaque). Default: 1 |
5637
- | `wireframe?` | `boolean` | Render as wireframe. Default: false |
5638
- | `clearcoat?` | `number` | Clearcoat intensity (0–1). Default: 0.1 |
5639
- | `clearcoatRoughness?` | `number` | Clearcoat roughness (0–1). Default: 0.4 |
5640
- | `transmission?` | `number` | Glass/translucency transmission factor (0–1). Renderer support depends on target. |
5641
- | `ior?` | `number` | Index of refraction for transmissive materials. Typical glass is ~1.45. |
5642
- | `thickness?` | `number` | Approximate transmissive volume thickness in model units. |
5643
- | `specularIntensity?` | `number` | Specular highlight intensity (0–1). |
5644
- | `specularColor?` | `string` | Specular highlight tint. |
5645
- | `reflectivity?` | `number` | Reflection strength for supported renderers (0–1). |
5646
-
5647
5532
  #### `color()` — Apply a simple color override to the panel.
5648
5533
 
5649
5534
  ```ts
@@ -5666,23 +5551,9 @@ attachTo(ref: ProductRefInput, options?: ProductPanelAttachOptions): Shape
5666
5551
 
5667
5552
  `ProductSurfaceRef`
5668
5553
 
5669
- `ProductAttachOptions`: `{ offset?: number, inset?: number }`
5670
5554
 
5671
5555
  `ProductPanelAttachOptions`: `{ at?: Partial<ProductSkinRefQuery>, thickness?: number, material?: ProductMaterial, color?: string }`
5672
5556
 
5673
- **`ProductSkinRefQuery`**
5674
-
5675
- | Option | Type | Description |
5676
- |--------|------|-------------|
5677
- | `side` | `ProductSkinSide` | Side of the product skin. `front` is the minimum axis cap, `rear`/`back` is the maximum axis cap. |
5678
- | `u?` | `number` | Across-side parameter for side refs. Defaults to 0.5. |
5679
- | `v?` | `number` | Along-axis parameter, 0 at the first cap and 1 at the rear/back cap. Defaults to 0.5. |
5680
- | `offset?` | `number` | Positive distance away from the surface along the resolved normal. |
5681
-
5682
- **`ProductSkinSide`** — Semantic side of a ProductSkin. `back` is accepted as an alias for `rear`.
5683
-
5684
- `"left" | "right" | "top" | "bottom" | "front" | "rear" | "back"`
5685
-
5686
5557
  ### `ProductRibbonBuilder`
5687
5558
 
5688
5559
  Builder for thin trim, label, grip, and split-line features that bend with a ProductSkin surface.
@@ -5707,56 +5578,9 @@ on(skin: ProductSkin, points: ProductRibbonPathPoint[], options?: ProductRibbonB
5707
5578
 
5708
5579
  `ProductSkinRefQuery | ProductSurfaceRef`
5709
5580
 
5710
- **`ProductSkinRefQuery`**
5711
-
5712
- | Option | Type | Description |
5713
- |--------|------|-------------|
5714
- | `side` | `ProductSkinSide` | Side of the product skin. `front` is the minimum axis cap, `rear`/`back` is the maximum axis cap. |
5715
- | `u?` | `number` | Across-side parameter for side refs. Defaults to 0.5. |
5716
- | `v?` | `number` | Along-axis parameter, 0 at the first cap and 1 at the rear/back cap. Defaults to 0.5. |
5717
- | `offset?` | `number` | Positive distance away from the surface along the resolved normal. |
5581
+ #### `fromRefs()` — Follow explicit surface refs.
5718
5582
 
5719
- **`ProductSkinSide`** Semantic side of a ProductSkin. `back` is accepted as an alias for `rear`.
5720
-
5721
- `"left" | "right" | "top" | "bottom" | "front" | "rear" | "back"`
5722
-
5723
- **`ProductRibbonBuildOptions`** — Options shared by Product.ribbon() builders and Product.surface(...).ribbon(...).
5724
-
5725
- | Option | Type | Description |
5726
- |--------|------|-------------|
5727
- | `width?` | `number` | Width across the surface in millimeters. |
5728
- | `thickness?` | `number` | Solid thickness outward from the source surface in millimeters. |
5729
- | `offset?` | `number` | Positive clearance between the source surface and the ribbon's inner face. |
5730
- | `samples?` | `number` | Samples along the ribbon path. Higher values bend more smoothly. |
5731
- | `widthSamples?` | `number` | Samples across the ribbon width. Use 3+ to visibly wrap over curved cross-sections. |
5732
- | `resolution?` | `number` | Tessellation resolution passed to the lowered NURBS surface. |
5733
- | `material?` | `ProductMaterial` | Apply a product material preset to the ribbon. |
5734
- | `color?` | `string` | Apply a simple color override. |
5735
-
5736
- `ProductMaterial`: `{ color?: string, material?: ShapeMaterialProps }`
5737
-
5738
- **`ShapeMaterialProps`**
5739
-
5740
- | Option | Type | Description |
5741
- |--------|------|-------------|
5742
- | `metalness?` | `number` | Metalness factor (0 = dielectric, 1 = metal). Default: 0.05 |
5743
- | `roughness?` | `number` | Roughness factor (0 = mirror, 1 = fully diffuse). Default: 0.35 |
5744
- | `emissive?` | `string` | Emissive glow color (hex string, e.g. "#ff6b35"). |
5745
- | `emissiveIntensity?` | `number` | Emissive intensity multiplier. Default: 1 |
5746
- | `opacity?` | `number` | Opacity (0 = fully transparent, 1 = fully opaque). Default: 1 |
5747
- | `wireframe?` | `boolean` | Render as wireframe. Default: false |
5748
- | `clearcoat?` | `number` | Clearcoat intensity (0–1). Default: 0.1 |
5749
- | `clearcoatRoughness?` | `number` | Clearcoat roughness (0–1). Default: 0.4 |
5750
- | `transmission?` | `number` | Glass/translucency transmission factor (0–1). Renderer support depends on target. |
5751
- | `ior?` | `number` | Index of refraction for transmissive materials. Typical glass is ~1.45. |
5752
- | `thickness?` | `number` | Approximate transmissive volume thickness in model units. |
5753
- | `specularIntensity?` | `number` | Specular highlight intensity (0–1). |
5754
- | `specularColor?` | `string` | Specular highlight tint. |
5755
- | `reflectivity?` | `number` | Reflection strength for supported renderers (0–1). |
5756
-
5757
- #### `fromRefs()` — Follow explicit surface refs.
5758
-
5759
- Useful for named refs or paths assembled elsewhere. The builder resolves each ref frame and interpolates between those frames; use on(skin, points) when you need full skin-side sampling between sparse control points.
5583
+ Useful for named refs or paths assembled elsewhere. The builder resolves each ref frame and interpolates between those frames; use on(skin, points) when you need full skin-side sampling between sparse control points.
5760
5584
 
5761
5585
  ```ts
5762
5586
  fromRefs(points: ProductSurfaceRef[], options?: ProductRibbonBuildOptions): this
@@ -5816,48 +5640,6 @@ color(color: string): this
5816
5640
  build(options?: ProductRibbonBuildOptions): Shape
5817
5641
  ```
5818
5642
 
5819
- #### `buildWithDiagnostics()` — Build a conformal ribbon and return surface-feature diagnostics.
5820
-
5821
- Use this while validating API usage or model fidelity; diagnostics report sampling counts, side-span clamping, lowering mode, and warnings that should be visible in reviews.
5822
-
5823
- ```ts
5824
- buildWithDiagnostics(options?: ProductRibbonBuildOptions): ProductRibbonResult
5825
- ```
5826
-
5827
- **`ProductRibbonResult`** — Shape plus diagnostics returned by ProductRibbonBuilder.buildWithDiagnostics().
5828
- - `shape: Shape` — Lowered conformal ribbon shape.
5829
- - `diagnostics: ProductRibbonDiagnostics` — Sampling and lowering diagnostics for the returned shape.
5830
-
5831
- **`ProductRibbonDiagnostics`** — Diagnostics describing how a conformal ribbon was sampled and lowered.
5832
-
5833
- | Option | Type | Description |
5834
- |--------|------|-------------|
5835
- | `name` | `string` | Ribbon shape name. |
5836
- | `skin?` | `string` | Source skin name when the ribbon follows a ProductSkin directly. |
5837
- | `side?` | `ProductSkinSide` | Source skin side when all path points are on one semantic side. |
5838
- | `pathPointCount` | `number` | Number of control path points supplied before interpolation. |
5839
- | `width` | `number` | Final ribbon width in millimeters. |
5840
- | `thickness` | `number` | Final ribbon solid thickness in millimeters. |
5841
- | `offset` | `number` | Final normal offset from the source surface in millimeters. |
5842
- | `samples` | `number` | Final sample count along the ribbon path. |
5843
- | `widthSamples` | `number` | Final sample count across the ribbon width. |
5844
- | `resolution` | `number` | NURBS tessellation resolution used for the lowered surface. |
5845
- | `lowering` | `"nurbsSurface"` | Lowering primitive used for the ribbon shape. |
5846
- | `expectedFidelity` | `ProductSkinRepresentation` | Expected fidelity inherited from the source skin/ref sampling mode. |
5847
- | `clampedUCount` | `number` | Number of generated width samples clamped to the valid side span. |
5848
- | `maxUClampDistance` | `number` | Largest absolute u-distance lost to side-span clamping. |
5849
- | `warnings` | `string[]` | Non-fatal sampling and lowering warnings. |
5850
-
5851
- **`ProductSkinRepresentation`** — Reported lowering mode for ProductSkin and conformal feature diagnostics.
5852
-
5853
- `"exact" | "sampled" | "mixed" | "fallback"`
5854
-
5855
- #### `diagnostics()` — Return diagnostics from the most recent build, if this builder has been built.
5856
-
5857
- ```ts
5858
- diagnostics(): ProductRibbonDiagnostics | undefined
5859
- ```
5860
-
5861
5643
  ### `ProductSpoutBuilder`
5862
5644
 
5863
5645
  **Properties:**
@@ -5880,14 +5662,6 @@ from(ref: ProductSurfaceRef): this
5880
5662
  sections(sections: Array<Sketch | ProductStationBuilder | ProductStationSpec>): this
5881
5663
  ```
5882
5664
 
5883
- `ProductStationSpec`: `{ name: string, center: Vec3, profile: ProductStationProfile, crown?: number }`
5884
-
5885
- `ProductStationProfile`: `{ sketch: Sketch, width: number, depth: number, kind: ProductProfileKind, radius?: number, exponent?: number }`
5886
-
5887
- **`ProductProfileKind`**
5888
-
5889
- `"oval" | "roundedRect" | "circle" | "superEllipse" | "custom"`
5890
-
5891
5665
  #### `projection()` — Set the projection length along the source ref normal.
5892
5666
 
5893
5667
  ```ts
@@ -5906,27 +5680,6 @@ edgeLength(value: number): this
5906
5680
  material(material: ProductMaterial): this
5907
5681
  ```
5908
5682
 
5909
- `ProductMaterial`: `{ color?: string, material?: ShapeMaterialProps }`
5910
-
5911
- **`ShapeMaterialProps`**
5912
-
5913
- | Option | Type | Description |
5914
- |--------|------|-------------|
5915
- | `metalness?` | `number` | Metalness factor (0 = dielectric, 1 = metal). Default: 0.05 |
5916
- | `roughness?` | `number` | Roughness factor (0 = mirror, 1 = fully diffuse). Default: 0.35 |
5917
- | `emissive?` | `string` | Emissive glow color (hex string, e.g. "#ff6b35"). |
5918
- | `emissiveIntensity?` | `number` | Emissive intensity multiplier. Default: 1 |
5919
- | `opacity?` | `number` | Opacity (0 = fully transparent, 1 = fully opaque). Default: 1 |
5920
- | `wireframe?` | `boolean` | Render as wireframe. Default: false |
5921
- | `clearcoat?` | `number` | Clearcoat intensity (0–1). Default: 0.1 |
5922
- | `clearcoatRoughness?` | `number` | Clearcoat roughness (0–1). Default: 0.4 |
5923
- | `transmission?` | `number` | Glass/translucency transmission factor (0–1). Renderer support depends on target. |
5924
- | `ior?` | `number` | Index of refraction for transmissive materials. Typical glass is ~1.45. |
5925
- | `thickness?` | `number` | Approximate transmissive volume thickness in model units. |
5926
- | `specularIntensity?` | `number` | Specular highlight intensity (0–1). |
5927
- | `specularColor?` | `string` | Specular highlight tint. |
5928
- | `reflectivity?` | `number` | Reflection strength for supported renderers (0–1). |
5929
-
5930
5683
  #### `color()` — Apply a simple color override to the spout.
5931
5684
 
5932
5685
  ```ts
@@ -5945,8 +5698,6 @@ build(): Shape
5945
5698
  attach(options?: ProductAttachOptions): Shape
5946
5699
  ```
5947
5700
 
5948
- `ProductAttachOptions`: `{ offset?: number, inset?: number }`
5949
-
5950
5701
  ### `ProductHandleBuilder`
5951
5702
 
5952
5703
  **Properties:**
@@ -5969,12 +5720,6 @@ between(upper: ProductSurfaceRef, lower: Vec3): this
5969
5720
  spine(points: Vec3[] | ProductRailSpec): this
5970
5721
  ```
5971
5722
 
5972
- `ProductRailSpec`: `{ kind: ProductRailKind, points: Vec3[], degree?: number, name?: string }`
5973
-
5974
- **`ProductRailKind`**
5975
-
5976
- `"bezier" | "nurbs" | "polyline"`
5977
-
5978
5723
  #### `grip()` — Set the grip cross-section profile.
5979
5724
 
5980
5725
  ```ts
@@ -5987,27 +5732,6 @@ grip(profile: Sketch): this
5987
5732
  material(material: ProductMaterial): this
5988
5733
  ```
5989
5734
 
5990
- `ProductMaterial`: `{ color?: string, material?: ShapeMaterialProps }`
5991
-
5992
- **`ShapeMaterialProps`**
5993
-
5994
- | Option | Type | Description |
5995
- |--------|------|-------------|
5996
- | `metalness?` | `number` | Metalness factor (0 = dielectric, 1 = metal). Default: 0.05 |
5997
- | `roughness?` | `number` | Roughness factor (0 = mirror, 1 = fully diffuse). Default: 0.35 |
5998
- | `emissive?` | `string` | Emissive glow color (hex string, e.g. "#ff6b35"). |
5999
- | `emissiveIntensity?` | `number` | Emissive intensity multiplier. Default: 1 |
6000
- | `opacity?` | `number` | Opacity (0 = fully transparent, 1 = fully opaque). Default: 1 |
6001
- | `wireframe?` | `boolean` | Render as wireframe. Default: false |
6002
- | `clearcoat?` | `number` | Clearcoat intensity (0–1). Default: 0.1 |
6003
- | `clearcoatRoughness?` | `number` | Clearcoat roughness (0–1). Default: 0.4 |
6004
- | `transmission?` | `number` | Glass/translucency transmission factor (0–1). Renderer support depends on target. |
6005
- | `ior?` | `number` | Index of refraction for transmissive materials. Typical glass is ~1.45. |
6006
- | `thickness?` | `number` | Approximate transmissive volume thickness in model units. |
6007
- | `specularIntensity?` | `number` | Specular highlight intensity (0–1). |
6008
- | `specularColor?` | `string` | Specular highlight tint. |
6009
- | `reflectivity?` | `number` | Reflection strength for supported renderers (0–1). |
6010
-
6011
5735
  #### `padMaterial()` — Apply a product material preset to handle landing pads.
6012
5736
 
6013
5737
  ```ts
@@ -6056,181 +5780,6 @@ toShape(): Shape
6056
5780
  toGroup(): ShapeGroup
6057
5781
  ```
6058
5782
 
6059
- ### `ProductRibbonBuilder`
6060
-
6061
- Builder for thin trim, label, grip, and split-line features that bend with a ProductSkin surface.
6062
-
6063
- **Properties:**
6064
-
6065
- | Property | Type | Description |
6066
- |----------|------|-------------|
6067
- | `name` | `string` | — |
6068
-
6069
- **Methods:**
6070
-
6071
- #### `on()` — Follow a ProductSkin with side/u/v path queries or refs.
6072
-
6073
- This is the highest-fidelity mode because every interpolated sample is resolved through ProductSkin.frame(), so the ribbon bends along the selected side as station width/depth changes. All query path points must stay on one side; split side transitions into separate ribbons.
6074
-
6075
- ```ts
6076
- on(skin: ProductSkin, points: ProductRibbonPathPoint[], options?: ProductRibbonBuildOptions): this
6077
- ```
6078
-
6079
- **`ProductRibbonPathPoint`** — Path point for Product.ribbon().on(...): either a side/u/v query or a resolved surface ref.
6080
-
6081
- `ProductSkinRefQuery | ProductSurfaceRef`
6082
-
6083
- **`ProductSkinRefQuery`**
6084
-
6085
- | Option | Type | Description |
6086
- |--------|------|-------------|
6087
- | `side` | `ProductSkinSide` | Side of the product skin. `front` is the minimum axis cap, `rear`/`back` is the maximum axis cap. |
6088
- | `u?` | `number` | Across-side parameter for side refs. Defaults to 0.5. |
6089
- | `v?` | `number` | Along-axis parameter, 0 at the first cap and 1 at the rear/back cap. Defaults to 0.5. |
6090
- | `offset?` | `number` | Positive distance away from the surface along the resolved normal. |
6091
-
6092
- **`ProductSkinSide`** — Semantic side of a ProductSkin. `back` is accepted as an alias for `rear`.
6093
-
6094
- `"left" | "right" | "top" | "bottom" | "front" | "rear" | "back"`
6095
-
6096
- **`ProductRibbonBuildOptions`** — Options shared by Product.ribbon() builders and Product.surface(...).ribbon(...).
6097
-
6098
- | Option | Type | Description |
6099
- |--------|------|-------------|
6100
- | `width?` | `number` | Width across the surface in millimeters. |
6101
- | `thickness?` | `number` | Solid thickness outward from the source surface in millimeters. |
6102
- | `offset?` | `number` | Positive clearance between the source surface and the ribbon's inner face. |
6103
- | `samples?` | `number` | Samples along the ribbon path. Higher values bend more smoothly. |
6104
- | `widthSamples?` | `number` | Samples across the ribbon width. Use 3+ to visibly wrap over curved cross-sections. |
6105
- | `resolution?` | `number` | Tessellation resolution passed to the lowered NURBS surface. |
6106
- | `material?` | `ProductMaterial` | Apply a product material preset to the ribbon. |
6107
- | `color?` | `string` | Apply a simple color override. |
6108
-
6109
- `ProductMaterial`: `{ color?: string, material?: ShapeMaterialProps }`
6110
-
6111
- **`ShapeMaterialProps`**
6112
-
6113
- | Option | Type | Description |
6114
- |--------|------|-------------|
6115
- | `metalness?` | `number` | Metalness factor (0 = dielectric, 1 = metal). Default: 0.05 |
6116
- | `roughness?` | `number` | Roughness factor (0 = mirror, 1 = fully diffuse). Default: 0.35 |
6117
- | `emissive?` | `string` | Emissive glow color (hex string, e.g. "#ff6b35"). |
6118
- | `emissiveIntensity?` | `number` | Emissive intensity multiplier. Default: 1 |
6119
- | `opacity?` | `number` | Opacity (0 = fully transparent, 1 = fully opaque). Default: 1 |
6120
- | `wireframe?` | `boolean` | Render as wireframe. Default: false |
6121
- | `clearcoat?` | `number` | Clearcoat intensity (0–1). Default: 0.1 |
6122
- | `clearcoatRoughness?` | `number` | Clearcoat roughness (0–1). Default: 0.4 |
6123
- | `transmission?` | `number` | Glass/translucency transmission factor (0–1). Renderer support depends on target. |
6124
- | `ior?` | `number` | Index of refraction for transmissive materials. Typical glass is ~1.45. |
6125
- | `thickness?` | `number` | Approximate transmissive volume thickness in model units. |
6126
- | `specularIntensity?` | `number` | Specular highlight intensity (0–1). |
6127
- | `specularColor?` | `string` | Specular highlight tint. |
6128
- | `reflectivity?` | `number` | Reflection strength for supported renderers (0–1). |
6129
-
6130
- #### `fromRefs()` — Follow explicit surface refs.
6131
-
6132
- Useful for named refs or paths assembled elsewhere. The builder resolves each ref frame and interpolates between those frames; use on(skin, points) when you need full skin-side sampling between sparse control points.
6133
-
6134
- ```ts
6135
- fromRefs(points: ProductSurfaceRef[], options?: ProductRibbonBuildOptions): this
6136
- ```
6137
-
6138
- #### `width()` — Set ribbon width in millimeters.
6139
-
6140
- ```ts
6141
- width(width: number): this
6142
- ```
6143
-
6144
- #### `thickness()` — Set solid thickness outward from the source surface in millimeters.
6145
-
6146
- ```ts
6147
- thickness(thickness: number): this
6148
- ```
6149
-
6150
- #### `offset()` — Set positive clearance between the source surface and the ribbon's inner face.
6151
-
6152
- ```ts
6153
- offset(offset: number): this
6154
- ```
6155
-
6156
- #### `samples()` — Set samples along the path.
6157
-
6158
- ```ts
6159
- samples(samples: number): this
6160
- ```
6161
-
6162
- #### `widthSamples()` — Set samples across the width. Use 3+ to bend over curved cross-sections.
6163
-
6164
- ```ts
6165
- widthSamples(samples: number): this
6166
- ```
6167
-
6168
- #### `resolution()` — Set NURBS tessellation resolution.
6169
-
6170
- ```ts
6171
- resolution(resolution: number): this
6172
- ```
6173
-
6174
- #### `material()` — Apply a product material preset.
6175
-
6176
- ```ts
6177
- material(material: ProductMaterial): this
6178
- ```
6179
-
6180
- #### `color()` — Apply a simple color override.
6181
-
6182
- ```ts
6183
- color(color: string): this
6184
- ```
6185
-
6186
- #### `build()` — Build a conformal ribbon as a thin NURBS surface solid.
6187
-
6188
- ```ts
6189
- build(options?: ProductRibbonBuildOptions): Shape
6190
- ```
6191
-
6192
- #### `buildWithDiagnostics()` — Build a conformal ribbon and return surface-feature diagnostics.
6193
-
6194
- Use this while validating API usage or model fidelity; diagnostics report sampling counts, side-span clamping, lowering mode, and warnings that should be visible in reviews.
6195
-
6196
- ```ts
6197
- buildWithDiagnostics(options?: ProductRibbonBuildOptions): ProductRibbonResult
6198
- ```
6199
-
6200
- **`ProductRibbonResult`** — Shape plus diagnostics returned by ProductRibbonBuilder.buildWithDiagnostics().
6201
- - `shape: Shape` — Lowered conformal ribbon shape.
6202
- - `diagnostics: ProductRibbonDiagnostics` — Sampling and lowering diagnostics for the returned shape.
6203
-
6204
- **`ProductRibbonDiagnostics`** — Diagnostics describing how a conformal ribbon was sampled and lowered.
6205
-
6206
- | Option | Type | Description |
6207
- |--------|------|-------------|
6208
- | `name` | `string` | Ribbon shape name. |
6209
- | `skin?` | `string` | Source skin name when the ribbon follows a ProductSkin directly. |
6210
- | `side?` | `ProductSkinSide` | Source skin side when all path points are on one semantic side. |
6211
- | `pathPointCount` | `number` | Number of control path points supplied before interpolation. |
6212
- | `width` | `number` | Final ribbon width in millimeters. |
6213
- | `thickness` | `number` | Final ribbon solid thickness in millimeters. |
6214
- | `offset` | `number` | Final normal offset from the source surface in millimeters. |
6215
- | `samples` | `number` | Final sample count along the ribbon path. |
6216
- | `widthSamples` | `number` | Final sample count across the ribbon width. |
6217
- | `resolution` | `number` | NURBS tessellation resolution used for the lowered surface. |
6218
- | `lowering` | `"nurbsSurface"` | Lowering primitive used for the ribbon shape. |
6219
- | `expectedFidelity` | `ProductSkinRepresentation` | Expected fidelity inherited from the source skin/ref sampling mode. |
6220
- | `clampedUCount` | `number` | Number of generated width samples clamped to the valid side span. |
6221
- | `maxUClampDistance` | `number` | Largest absolute u-distance lost to side-span clamping. |
6222
- | `warnings` | `string[]` | Non-fatal sampling and lowering warnings. |
6223
-
6224
- **`ProductSkinRepresentation`** — Reported lowering mode for ProductSkin and conformal feature diagnostics.
6225
-
6226
- `"exact" | "sampled" | "mixed" | "fallback"`
6227
-
6228
- #### `diagnostics()` — Return diagnostics from the most recent build, if this builder has been built.
6229
-
6230
- ```ts
6231
- diagnostics(): ProductRibbonDiagnostics | undefined
6232
- ```
6233
-
6234
5783
  ### `CylinderCarrier`
6235
5784
 
6236
5785
  **Properties:**
@@ -6362,12 +5911,6 @@ bounds(): SurfaceBounds
6362
5911
  offset(distance: number): CylinderCarrier
6363
5912
  ```
6364
5913
 
6365
- #### `diagnostics()`
6366
-
6367
- ```ts
6368
- diagnostics(): SurfaceDiagnostic[]
6369
- ```
6370
-
6371
5914
  #### `mirrorCoordinate()`
6372
5915
 
6373
5916
  ```ts
@@ -6487,12 +6030,6 @@ bounds(): SurfaceBounds
6487
6030
  offset(distance: number): PlaneCarrier
6488
6031
  ```
6489
6032
 
6490
- #### `diagnostics()`
6491
-
6492
- ```ts
6493
- diagnostics(): SurfaceDiagnostic[]
6494
- ```
6495
-
6496
6033
  #### `mirrorCoordinate()`
6497
6034
 
6498
6035
  ```ts
@@ -6517,10 +6054,6 @@ mirrorCoordinate(coordinate: PlaneSurfaceCoordinate): PlaneSurfaceCoordinate
6517
6054
  surface(side: ProductSkinSide): ProductSkinCarrier
6518
6055
  ```
6519
6056
 
6520
- **`ProductSkinSide`** — Semantic side of a ProductSkin. `back` is accepted as an alias for `rear`.
6521
-
6522
- `"left" | "right" | "top" | "bottom" | "front" | "rear" | "back"`
6523
-
6524
6057
  #### [`path()`](/docs/sketch#path)
6525
6058
 
6526
6059
  ```ts
@@ -6609,16 +6142,6 @@ bounds(): SurfaceBounds
6609
6142
  offset(distance: number): ProductSkinCarrier
6610
6143
  ```
6611
6144
 
6612
- #### `diagnostics()`
6613
-
6614
- ```ts
6615
- diagnostics(): SurfaceDiagnostic[]
6616
- ```
6617
-
6618
- **`SurfaceDiagnostic`**
6619
- - `code?: SurfaceDiagnosticCode` — Stable machine-readable repair hook. Messages may change; codes should not.
6620
- - Also: `category: "carrier" | "path" | "region" | "member" | "feature" | "join" | "compiler", level: "info" | "warning" | "error", message: string, subject?: string`
6621
-
6622
6145
  #### `mirrorCoordinate()`
6623
6146
 
6624
6147
  ```ts
@@ -6761,12 +6284,6 @@ withHole(name: string, input: SurfaceBandHoleInput): SurfaceBand<C>
6761
6284
  holes(): SurfaceBandHoleRegion[]
6762
6285
  ```
6763
6286
 
6764
- #### `diagnostics()`
6765
-
6766
- ```ts
6767
- diagnostics(samples?: number): SurfaceDiagnostic[]
6768
- ```
6769
-
6770
6287
  ### `SurfaceBodyBuilder`
6771
6288
 
6772
6289
  **Properties:**
@@ -6807,18 +6324,6 @@ autoJoinAtSharedAnchors(): this
6807
6324
  build(): Shape | ShapeGroup
6808
6325
  ```
6809
6326
 
6810
- #### `buildWithDiagnostics()`
6811
-
6812
- ```ts
6813
- buildWithDiagnostics(): SurfaceBodyBuildResult
6814
- ```
6815
-
6816
- #### `buildDebug()`
6817
-
6818
- ```ts
6819
- buildDebug(): SurfaceBodyBuildResult
6820
- ```
6821
-
6822
6327
  ### `SurfaceMemberBuilder`
6823
6328
 
6824
6329
  #### `plate()`
@@ -6881,7 +6386,7 @@ cutout(name: string, feature: MemberFeature | RoundedSlotBuilder): this
6881
6386
  counterbore(name: string, feature: MemberFeature | CounterboreBuilder): this
6882
6387
  ```
6883
6388
 
6884
- #### `anchorAt()` — Add a named anchor at a carrier surface coordinate for diagnostics, debug views, and future named-anchor joins.
6389
+ #### `anchorAt()` — Add a named anchor at a carrier surface coordinate for explicit member joins.
6885
6390
 
6886
6391
  ```ts
6887
6392
  anchorAt(name: string, coordinate: C | SurfaceAnchor<C>): this
@@ -6929,18 +6434,6 @@ autoJoinAtSharedAnchors(): SurfaceBodyBuilder
6929
6434
  build(): Shape | ShapeGroup
6930
6435
  ```
6931
6436
 
6932
- #### `buildWithDiagnostics()`
6933
-
6934
- ```ts
6935
- buildWithDiagnostics(): SurfaceBodyBuildResult
6936
- ```
6937
-
6938
- #### `buildDebug()`
6939
-
6940
- ```ts
6941
- buildDebug(): SurfaceBodyBuildResult
6942
- ```
6943
-
6944
6437
  ### `SurfaceJoinBuilder`
6945
6438
 
6946
6439
  #### `betweenAnchors()` — Select named anchors on the source and target members before lowering this join.
@@ -7023,7 +6516,6 @@ toFeature(name?: string): MemberFeature
7023
6516
 
7024
6517
  - `Edge(options: BlendEdgeOptions): Shape`
7025
6518
  - `Surface(options: BlendSurfaceOptions): Shape`
7026
- - `CornerY(options: BlendCornerYOptions): Shape` — Current implementation uses continuity-controlled edge fillets on solid edges. It does not yet provide a dedicated open-sheet or multi-patch Y-corner solver. Follow progress: https://github.com/KoStard/forgecad-private/issues/162
7027
6519
 
7028
6520
  ### `Analysis`
7029
6521
 
@@ -7062,15 +6554,12 @@ toFeature(name?: string): MemberFeature
7062
6554
  - `cylinder(name: string): CylinderCarrier` — Create an analytic cylinder carrier for bottles, limbs, tubes, guards, and cuffs.
7063
6555
  - `plane(name: string): PlaneCarrier` — Create an analytic plane carrier for plates and local flat construction surfaces.
7064
6556
  - `productSkin(skin: ProductSkin): ProductSkinCarrier` — Adapt an existing ProductSkin into the general surface-member carrier protocol.
7065
- - `mesh(name: string): never` — Reserved stub for future parameterized mesh carriers; arbitrary mesh parameterization is not implemented yet.
7066
- - `scan(name: string): never` — Reserved stub for future scan/body-fit carriers; arbitrary scan parameterization is not implemented yet.
7067
6557
 
7068
6558
  ### `SurfaceMembers`
7069
6559
 
7070
6560
  - `Body(name: string): SurfaceBodyBuilder` — Start a surface-member body builder for straps, inlays, guards, braces, cuffs, and similar physical members that live on a carrier surface.
7071
6561
  - `Band: typeof SurfaceBand`
7072
6562
  - `band<C extends SurfaceCoordinate>(path: SurfacePath<C> | SurfacePathBuilder<C>, width: WidthProfile, cap?: SurfaceBandCap): SurfaceBand<C>`
7073
- - `compileMember<C extends SurfaceCoordinate>(input: Omit<SurfaceMemberSpec<C>, "section"> & { section: MemberSection | MemberSectionInput; }): CompiledSurfaceMember`
7074
6563
 
7075
6564
  ### `Slot`
7076
6565
 
@@ -7114,7 +6603,12 @@ bomToCsv(rows: BomRow[]): string
7114
6603
 
7115
6604
  **`BomRow`**: `part: string`, `qty: number`, `material?: string`, `process?: string`, `tolerance?: string`, `notes?: string`, `metadata?: PartMetadata`
7116
6605
 
7117
- **`PartMetadata`**: `material?: string`, `process?: string`, `tolerance?: string`, `qty?: number`, `notes?: string`, `densityKgM3?: number`, `massKg?: number`
6606
+ **`PartMetadata`**
6607
+
6608
+ | Option | Type | Description |
6609
+ |--------|------|-------------|
6610
+ | `tags?` | `string \| readonly string[]` | Viewport organization tags applied to scene objects produced from this part. |
6611
+ | `material?`, `process?`, `tolerance?`, `qty?`, `notes?`, `densityKgM3?`, `massKg?` | | — |
7118
6612
 
7119
6613
  #### `assembly()` — Create an assembly container with named parts and joints for kinematic mechanisms.
7120
6614
 
@@ -7755,13 +7249,13 @@ toGroup(): ShapeGroup
7755
7249
  Each part becomes `{ name, shape }` or `{ name, group: [...] }` if the part is a [`ShapeGroup`](/docs/core#shapegroup). Top-level scripts should normally return the `SolvedAssembly` directly. Use `toGroup()` when you need [`ShapeGroup`](/docs/core#shapegroup) behavior; use this method only for advanced scene-graph control where you need access to the flat per-part array with metadata.
7756
7250
 
7757
7251
  ```ts
7758
- toSceneObjects(): Array<{ name: string; shape?: Shape; group?: Array<{ name: string; shape: Shape; }>; metadata?: PartMetadata; }>
7252
+ toSceneObjects(): Array<{ name: string; shape?: Shape; group?: Array<{ name: string; shape: Shape; tags?: string[]; }>; metadata?: PartMetadata; }>
7759
7253
  ```
7760
7254
 
7761
7255
  #### `toScene()` — Backward-compatible alias for `toSceneObjects()`.
7762
7256
 
7763
7257
  ```ts
7764
- toScene(): Array<{ name: string; shape?: Shape; group?: Array<{ name: string; shape: Shape; }>; metadata?: PartMetadata; }>
7258
+ toScene(): Array<{ name: string; shape?: Shape; group?: Array<{ name: string; shape: Shape; tags?: string[]; }>; metadata?: PartMetadata; }>
7765
7259
  ```
7766
7260
 
7767
7261
  #### [`bom()`](/docs/output#bom) — Generate a bill of materials for all parts in the solved assembly.
@@ -8522,7 +8016,12 @@ robotExport(options: RobotExportOptions): CollectedRobotExport
8522
8016
 
8523
8017
  `AssemblyPartDef`: `{ name: string, part: AssemblyPart, base: Transform, metadata?: PartMetadata }`
8524
8018
 
8525
- **`PartMetadata`**: `material?: string`, `process?: string`, `tolerance?: string`, `qty?: number`, `notes?: string`, `densityKgM3?: number`, `massKg?: number`
8019
+ **`PartMetadata`**
8020
+
8021
+ | Option | Type | Description |
8022
+ |--------|------|-------------|
8023
+ | `tags?` | `string \| readonly string[]` | Viewport organization tags applied to scene objects produced from this part. |
8024
+ | `material?`, `process?`, `tolerance?`, `qty?`, `notes?`, `densityKgM3?`, `massKg?` | | — |
8526
8025
 
8527
8026
  **`AssemblyJointDef`**: `name: string`, `type: JointType`, `parent: string`, `child: string`, `frame: Transform`, `axis: Vec3`, `min?: number`, `max?: number`, `defaultValue: number`, `unit?: string`, `effort?: number`, `velocity?: number`, `damping?: number`, `friction?: number`, `connectorRefs?: JointConnectorRefs`
8528
8027
 
@@ -8630,6 +8129,7 @@ Pre-built fasteners, gears, pipes, structural profiles, and utility shapes. Acce
8630
8129
  ## Contents
8631
8130
 
8632
8131
  - [TangentLoop2D](#tangentloop2d)
8132
+ - [DriveWheelBuilder](#drivewheelbuilder)
8633
8133
  - [lib](#lib)
8634
8134
 
8635
8135
  ---
@@ -8670,6 +8170,32 @@ toProfile(): Sketch
8670
8170
  offsetBand(thickness: number): Sketch
8671
8171
  ```
8672
8172
 
8173
+ ### `DriveWheelBuilder`
8174
+
8175
+ #### `addSpurTeethBetween()` — Add an involute spur-tooth window on part of the pitch circle.
8176
+
8177
+ ```ts
8178
+ addSpurTeethBetween(options: DriveWheelSpurTeethRegionOptions): this
8179
+ ```
8180
+
8181
+ #### `addSolidArcBetween()` — Add a constant-radius solid arc region such as a dwell, stop, or pusher.
8182
+
8183
+ ```ts
8184
+ addSolidArcBetween(options: DriveWheelSolidArcRegionOptions): this
8185
+ ```
8186
+
8187
+ #### `addShapeRegion()` — Add a fully custom region shape while preserving region metadata.
8188
+
8189
+ ```ts
8190
+ addShapeRegion(name: string, shape: Shape, options?: DriveWheelShapeRegionOptions): this
8191
+ ```
8192
+
8193
+ #### `build()` — Build the final wheel shape with a bore connector and region metadata.
8194
+
8195
+ ```ts
8196
+ build(): Shape
8197
+ ```
8198
+
8673
8199
  ---
8674
8200
 
8675
8201
  ## Constants
@@ -8678,7 +8204,7 @@ offsetBand(thickness: number): Sketch
8678
8204
 
8679
8205
  Pre-built parametric parts available in user scripts as `lib.*`.
8680
8206
 
8681
- Every key in this object becomes a method on the `lib` namespace exposed to `.forge.js` scripts. The catalog includes:
8207
+ Every key in this object becomes a method or namespace on the `lib` object exposed to `.forge.js` scripts. The catalog includes:
8682
8208
 
8683
8209
  **Fasteners:** `bolt`, `nut`, `washer`, `fastenerSet`, `fastenerHole`, `boltHole`, `counterbore`, `hexNut`, `holePattern`
8684
8210
 
@@ -8688,7 +8214,9 @@ Every key in this object becomes a method on the `lib` namespace exposed to `.fo
8688
8214
 
8689
8215
  **Threads:** `thread`
8690
8216
 
8691
- **Gears:** `spurGear`, `bevelGear`, `faceGear`, `sideGear`, `ringGear`, `rackGear`, `gearPair`, `bevelGearPair`, `faceGearPair`, `sideGearPair`
8217
+ **Gears:** `spurGear`, `sectorGear`, `driveWheel`, `bevelGear`, `faceGear`, `sideGear`, `ringGear`, `rackGear`, `gearPair`, `bevelGearPair`, `faceGearPair`, `sideGearPair`
8218
+
8219
+ **Gear bodies:** `gearBodies.disk`, `gearBodies.diskWithHub`, `gearBodies.spoked`, `gearBodies.fromProfile` plus direct aliases `gearBodyDisk`, `gearBodyDiskWithHub`, `gearBodySpoked`, `gearBodyFromProfile`
8692
8220
 
8693
8221
  **Gear ratios (pure math helpers):** `gearRatio`, `rackRatio`, `planetaryRatio`
8694
8222
 
@@ -8696,7 +8224,7 @@ Every key in this object becomes a method on the `lib` namespace exposed to `.fo
8696
8224
 
8697
8225
  **Utilities:** `explode`
8698
8226
 
8699
- Extend this by adding new entries here and registering the corresponding runner binding in `runner.ts`. Sizes outside the supported ranges will throw at runtime with a descriptive error.
8227
+ Sizes outside the supported ranges will throw at runtime with a descriptive error.
8700
8228
 
8701
8229
  - `boltHole(diameter: number, depth: number): Shape` — Simple cylindrical through-hole cutter centered on Z=0. Subtract the result from a part to produce a plain cylindrical clearance hole. For ISO metric sizes with fit classes and counterbore/countersink, use {
8702
8230
  - `fastenerHole(opts: FastenerHoleOptions): Shape` — ISO metric fastener hole cutter with optional counterbore or countersink. **Details** Returns a cutter shape (subtract from a solid to produce the hole). Sizes outside M2–M10 will throw. Extend `METRIC_HOLE_TABLE` in this file to add new sizes. **Example** ```ts const plate = box(60, 40, 8) .subtract(lib.fastenerHole({ size: 'M5', fit: 'normal', depth: 8 }) .translate(15, 10, 4)); ```
@@ -8734,6 +8262,14 @@ Extend this by adding new entries here and registering the corresponding runner
8734
8262
  - `rackRatio(module: number, pinionTeeth: number): number` — Coupling ratio between a pinion and a rack. When the pinion rotates by `θ` degrees, the rack slides by `θ × (π × module × teeth / 360)` mm. Equivalently, 1mm of rack travel = `180 / (π × pitchRadius)` degrees of pinion rotation.
8735
8263
  - `planetaryRatio(sunTeeth: number, ringTeeth: number): number` — Planetary gear reduction ratio when the ring is held fixed. Input: sun. Output: carrier. Ratio: `1 + ringTeeth / sunTeeth`. One turn of the sun produces `1 / ratio` turns of the carrier.
8736
8264
  - `boltPattern(options: BoltPatternOptions): BoltPattern` — Define a bolt pattern once and cut it from multiple parts. const base = bolts.cut(box(60, 50, 10), 12, { from: -1 }); const cover = bolts.cut(box(60, 50, 3), 5, { from: -1 }); // Same positions in both parts — guaranteed aligned. ```
8265
+ - `driveWheel(options?: DriveWheelOptions): DriveWheelBuilder` — Start a composable exceptional gear or drive wheel.
8266
+ - `readDriveWheelMeta(shape: Shape): DriveWheelMeta | null` — Read the functional-region metadata attached by `driveWheel().build()`.
8267
+ - `sectorGear(options: SectorGearOptions): Shape` — Involute sector gear with teeth on only part of the pitch circle. Specify the full-circle pitch as `teethOnFullCircle`, then choose the active tooth window with `firstTooth` and `toothCount`. The body is separate from the tooth region: pass a `gearBody...` shape for spokes, hubs, and product styling, or omit it for a simple root-radius disk. **Example** ```ts const body = lib.gearBodies.spoked({ outerRadius: 22, rimWidth: 3, hubDiameter: 10, spokeCount: 5, spokeWidth: 2.5, faceWidth: 8, boreDiameter: 5, }); const sector = lib.sectorGear({ module: 1.25, teethOnFullCircle: 36, toothCount: 10, faceWidth: 8, body, }); ```
8268
+ - `gearBodies: { ... }` — Gear body preset namespace: disk, diskWithHub, spoked, and fromProfile.
8269
+ - `gearBodyDisk(options: GearBodyDiskOptions): Shape` — Solid disk/ring gear body, independent from any tooth geometry.
8270
+ - `gearBodyDiskWithHub(options: GearBodyDiskWithHubOptions): Shape` — Disk gear body with a raised center hub.
8271
+ - `gearBodySpoked(options: GearBodySpokedOptions): Shape` — Spoked gear body with an outer rim, center hub, and radial spokes.
8272
+ - `gearBodyFromProfile(profile: Sketch, options: GearBodyFromProfileOptions): Shape` — Extrude a custom 2D profile into a gear body.
8737
8273
 
8738
8274
  ---
8739
8275
 
@@ -8903,7 +8439,7 @@ When `lights` is specified, **all** default lights are removed. You must include
8903
8439
 
8904
8440
  Setting `camera.position` overrides auto-framing — the viewport will no longer auto-fit the geometry on script reload.
8905
8441
 
8906
- Named render views let scripts check in repeatable cameras next to the model code. The canonical shape is `{ camera: { position, target } }`, and a direct camera shorthand `{ position, target }` is also accepted. Use the canonical shape when you may add view metadata later. Use it from the CLI with `forgecad render 3d model.forge.js --view hero`.
8442
+ Named render views let scripts check in repeatable cameras next to the model code. The canonical shape is `{ camera: { position, target } }`, and a direct camera shorthand `{ position, target }` is also accepted. Use the canonical shape when you may add view metadata later. Use it from the CLI with `--view hero` on `forgecad render 3d`, `forgecad render hq`, or `forgecad capture`.
8907
8443
 
8908
8444
  Model journeys let scripts check in a compact guided path through named objects. Each journey has ordered `steps`; each step can name a `focus` target by object name/tree path, provide a caption, and optionally provide an explicit camera. In the viewer, journeys are opt-in: they appear as a small Explore control and do not move the camera until the user starts them. Use `forgecad run model.forge.js --journeys` or `--journeys-json` to inspect resolved targets.
8909
8445
 
@@ -8972,7 +8508,6 @@ scene(options: SceneOptions): void
8972
8508
  | `behavior?` | `"opt-in" \| "auto"` | Whether the viewer should offer or auto-open the journey. First slice supports opt-in. |
8973
8509
  | `steps` | `SceneJourneyStepConfig[]` | Ordered journey spine. Branches can be added later without changing this core contract. |
8974
8510
  | `valid?` | `boolean` | True unless any journey or step diagnostic has level "error". |
8975
- | `diagnostics?` | `SceneJourneyDiagnostic[]` | Whole-journey diagnostics, including unresolved startsAt and step target diagnostics. |
8976
8511
 
8977
8512
  **`SceneJourneyStepConfig`**
8978
8513
 
@@ -8985,9 +8520,6 @@ scene(options: SceneOptions): void
8985
8520
  | `camera?` | `SceneViewCameraConfig` | Optional explicit camera for this step. When omitted, the viewer fits `focus`. |
8986
8521
  | `resolvedFocusId?` | `string \| null` | Resolved object id after script execution, when `focus` matched exactly one object. |
8987
8522
  | `resolvedFocusPath?` | `string \| null` | Resolved object tree path or name after script execution. |
8988
- | `diagnostics?` | `SceneJourneyDiagnostic[]` | Resolution diagnostics for this step. |
8989
-
8990
- `SceneJourneyDiagnostic`: `{ level: SceneJourneyDiagnosticLevel, message: string, stepId?: string, suggestions?: string[] }`
8991
8523
 
8992
8524
  **`SceneLightConfig`**
8993
8525
 
@@ -9063,10 +8595,6 @@ viewConfig({
9063
8595
  viewConfig(options?: ViewConfigOptions): void
9064
8596
  ```
9065
8597
 
9066
- `ViewConfigOptions`: `{ jointOverlay?: JointOverlayViewConfigOptions }`
9067
-
9068
- **`JointOverlayViewConfigOptions`**: `enabled?: boolean`, `axisColor?: string`, `axisCoreColor?: string`, `arcColor?: string`, `zeroColor?: string`, `arcVisualLimitDeg?: number`, `axisLengthScale?: number`, `axisLengthMin?: number`, `axisLineRadiusScale?: number`, `axisLineRadiusMin?: number`, `axisLineRadiusMax?: number`, `spokeLineRadiusScale?: number`, `spokeLineRadiusMin?: number`, `spokeLineRadiusMax?: number`, `arcLineRadiusScale?: number`, `arcLineRadiusMin?: number`, `arcLineRadiusMax?: number`, `axisDotRadiusScale?: number`, `axisDotRadiusMin?: number`, `axisArrowRadiusScale?: number`, `axisArrowRadiusMin?: number`, `axisArrowLengthScale?: number`, `axisArrowLengthMin?: number`, `axisArrowOffsetFactor?: number`, `arcRadiusScale?: number`, `arcRadiusMin?: number`, `arcDotRadiusScale?: number`, `arcDotRadiusMin?: number`, `arcArrowRadiusScale?: number`, `arcArrowRadiusMin?: number`, `arcArrowLengthScale?: number`, `arcArrowLengthMin?: number`, `arcArrowOffsetFactor?: number`, `arcStepDeg?: number`, `arcMinSteps?: number`, `arcTubeSegmentsMin?: number`, `arcTubeSegmentsFactor?: number`, `arcTubeRadialSegments?: number`
9069
-
9070
8598
  #### `explodeView()` — Configure how the viewport explode slider offsets returned objects.
9071
8599
 
9072
8600
  Offsets are resolved from the returned object tree, not a flat list. In `radial` mode each node follows its parent branch direction, then fans locally from the immediate parent center — nested assemblies peel apart level by level. In fixed-axis or fixed-vector modes, the branch follows that axis/vector but nested descendants fan out perpendicular by default.
@@ -9576,8 +9104,9 @@ Setting `KNUCK_R = H / 2` exactly makes the body cross-section a stadium that pe
9576
9104
 
9577
9105
  `forgecad render inspect` writes a deterministic directory bundle for agents,
9578
9106
  tests, and automation. Use it when a single shaded PNG is too ambiguous and the
9579
- consumer needs geometry-aware signals such as depth, normals, part identity,
9580
- physical connected components, collisions, local thickness, or cross-sections.
9107
+ consumer needs geometry-aware signals such as depth, normals, surface roughness,
9108
+ part identity, physical connected components, collisions, local thickness, or
9109
+ cross-sections.
9581
9110
 
9582
9111
  ## When To Use It
9583
9112
 
@@ -9597,14 +9126,16 @@ forgecad render inspect model.forge.js --channels rgb,mask,section
9597
9126
  forgecad render inspect model.forge.js --channels collisions --focus Bench
9598
9127
  forgecad render inspect model.forge.js --channels rgb,mask --hide "Bench.Slat0,Bench.Slat1"
9599
9128
  forgecad render inspect model.forge.js --channels thickness --min-thickness 1.2 --warn-thickness 2.0
9129
+ forgecad render inspect channels
9600
9130
  ```
9601
9131
 
9602
9132
  The default output directory is `<script-name>-inspect/` next to the input file.
9603
9133
  Pass `--force` to replace an existing bundle directory.
9604
9134
 
9605
9135
  There are no default channels. Pass `--channels` every time as a
9606
- comma-separated subset. Keep bundles targeted to the current question so heavy
9607
- analyses do not run unnecessarily.
9136
+ comma-separated subset. Run `forgecad render inspect channels` to list the
9137
+ supported channels in the installed CLI. Keep bundles targeted to the current
9138
+ question so heavy analyses do not run unnecessarily.
9608
9139
 
9609
9140
  `--focus` and `--hide` use the same object-name filtering semantics as
9610
9141
  `forgecad run` and `forgecad render 3d`. A bare `--focus` hides mock objects;
@@ -9660,12 +9191,13 @@ implemented channel in one bundle:
9660
9191
 
9661
9192
  ```bash
9662
9193
  forgecad render inspect model.forge.js --channels depth,normals
9194
+ forgecad render inspect model.forge.js --channels rgb,roughness
9663
9195
  forgecad render inspect model.forge.js --channels rgb,mask,collisions
9664
9196
  forgecad render inspect model.forge.js --channels rgb,section,thickness
9665
9197
  ```
9666
9198
 
9667
- Supported channels are `rgb`, `depth`, `normals`, `mask`, `connectivity`,
9668
- `distance`, `collisions`, `thickness`, and `section`.
9199
+ Supported channels are `rgb`, `depth`, `normals`, `roughness`, `mask`,
9200
+ `connectivity`, `distance`, `collisions`, `thickness`, and `section`.
9669
9201
 
9670
9202
  ## Channel Semantics
9671
9203
 
@@ -9692,6 +9224,27 @@ normal = normalize((rgb / 255) * 2 - 1)
9692
9224
 
9693
9225
  Background pixels are black and should be treated as `null`.
9694
9226
 
9227
+ `roughness` emits a mesh-dihedral surface-quality heatmap. Smooth and gently
9228
+ curved triangles render as a faint translucent shadow over black, while
9229
+ triangles adjacent to sharp, harsh, boundary, or non-manifold mesh edges render
9230
+ in orange or magenta:
9231
+
9232
+ ```text
9233
+ shadow = max adjacent angle < sharpAngleDeg
9234
+ orange = sharpAngleDeg <= angle < harshAngleDeg
9235
+ magenta = angle >= harshAngleDeg, boundary, or non-manifold
9236
+ ```
9237
+
9238
+ The default thresholds are `smoothAngleDeg=5`, `sharpAngleDeg=30`, and
9239
+ `harshAngleDeg=90`. The manifest stores the method, thresholds, palette, object
9240
+ list, per-object triangle and edge counts, area percentages by smooth,
9241
+ moderate, sharp, and harsh classes, angle percentiles, maximum angle, quality
9242
+ score, and warnings. Moderate angles are reported in the manifest but stay in
9243
+ the shadow layer by default so intentionally curved surfaces do not light up as
9244
+ defects. Use this channel to spot spiky tessellation, accidental faceting,
9245
+ jagged boolean residue, and dense sharp-corner regions without losing the
9246
+ silhouette of otherwise smooth surfaces.
9247
+
9695
9248
  `mask` emits one object-color image per view. Black is background. Non-black
9696
9249
  pixels resolve through `manifest.channels.mask.objects`, which includes object
9697
9250
  index, RGB color, object id, name, group, tree path, and mock flag. Edge pixels
@@ -10026,6 +9579,36 @@ intersect(...others: SdfShape[]): SdfShape
10026
9579
  clipBox(x: number, y: number, z: number): SdfShape
10027
9580
  ```
10028
9581
 
9582
+ #### `fillWith()` — Keep only the material where this shape overlaps another SDF pattern.
9583
+
9584
+ ```ts
9585
+ fillWith(pattern: SdfShape): SdfShape
9586
+ ```
9587
+
9588
+ #### `fillWithGyroid()` — Keep only the gyroid lattice inside this shape.
9589
+
9590
+ ```ts
9591
+ fillWithGyroid(options: TpmsOptions): SdfShape
9592
+ ```
9593
+
9594
+ #### `fillWithSchwarzP()` — Keep only the Schwarz-P lattice inside this shape.
9595
+
9596
+ ```ts
9597
+ fillWithSchwarzP(options: TpmsOptions): SdfShape
9598
+ ```
9599
+
9600
+ #### `fillWithDiamond()` — Keep only the diamond TPMS lattice inside this shape.
9601
+
9602
+ ```ts
9603
+ fillWithDiamond(options: TpmsOptions): SdfShape
9604
+ ```
9605
+
9606
+ #### `fillWithLidinoid()` — Keep only the lidinoid TPMS lattice inside this shape.
9607
+
9608
+ ```ts
9609
+ fillWithLidinoid(options: TpmsOptions): SdfShape
9610
+ ```
9611
+
10029
9612
  #### `smoothUnion()` — Smooth union — blends shapes together with a smooth radius.
10030
9613
 
10031
9614
  ```ts
@@ -10104,6 +9687,14 @@ bend(radius: number): SdfShape
10104
9687
  repeat(spacing: Vec3, count?: Vec3): SdfShape
10105
9688
  ```
10106
9689
 
9690
+ #### `circularArray()` — Arrange this SDF in a circular array around the Z axis.
9691
+
9692
+ The source shape is translated by `offset` in +X before arraying. This uses angular domain folding, so evaluation stays O(1): the source SDF is sampled twice no matter how many copies are requested.
9693
+
9694
+ ```ts
9695
+ circularArray(count: number, offset?: number): SdfShape
9696
+ ```
9697
+
10107
9698
  #### `shell()` — Hollow out, keeping only a shell of given thickness.
10108
9699
 
10109
9700
  ```ts
@@ -10116,8 +9707,8 @@ shell(thickness: number): SdfShape
10116
9707
  // Function displacement
10117
9708
  shape.displace((x, y, z) => Math.sin(x) * 0.5)
10118
9709
 
10119
- // Pattern displacement (e.g. basketWeave)
10120
- shape.displace(sdf.basketWeave({ threads: 16, spacing: 3 }))
9710
+ // Pattern displacement from a 3D SDF field
9711
+ shape.displace(sdf.knurl({ pitch: 2, depth: 0.3 }))
10121
9712
  ```
10122
9713
 
10123
9714
  ```ts
@@ -10130,10 +9721,16 @@ Automatically detects the shape's UV parametrization (sphere, cylinder, torus) f
10130
9721
 
10131
9722
  UV coordinates are in **surface millimeters** — patterns defined with `spacing: 3` always produce 3mm spacing, regardless of shape size.
10132
9723
 
9724
+ Prefer `sdf.pattern2d()` or built-in surface patterns when the relief should stay on the native shader and meshing path. Callback functions are supported for experimentation, but they are opaque to the typed pattern optimizer.
9725
+
10133
9726
  ```js
10134
- // Surface-following basket weave — auto-detects sphere UV
9727
+ // Native typed pattern — auto-detects sphere UV
9728
+ const p = sdf.pattern2d()
9729
+ const ribs = p.stripes({ spacing: 3, width: 0.8, depth: 0.35 })
9730
+ .add(p.sineWave({ direction: [0, 1], wavelength: 14, amplitude: 0.08 }))
9731
+
10135
9732
  sdf.sphere(27).shell(3)
10136
- .surfaceDisplace(sdf.basketWeave({ spacing: 3, depth: 0.8 }))
9733
+ .surfaceDisplace(ribs)
10137
9734
  .toShape()
10138
9735
 
10139
9736
  // Custom 2D pattern via function
@@ -10205,9 +9802,11 @@ return {
10205
9802
  - `brick(options?: BrickOptions): SdfShape` — Brick/stone wall pattern — running bond with mortar grooves.
10206
9803
  - `weave(options?: WeaveOptions): SdfShape` — Grid lattice pattern — two families of infinite slabs crossing at 90°.
10207
9804
  - `basketWeave(options?: BasketWeaveOptions): SurfacePattern` — Basket weave surface pattern — threads with over-under crossings in UV space. Returns a SurfacePattern for use with `.surfaceDisplace()`.
9805
+ - `pattern2d(): Pattern2DBuilder` — Create typed, composable 2D surface patterns for `.surfaceDisplace()`.
10208
9806
  - `twist(shape: SdfShape, degreesPerUnit: number): SdfShape` — Twist an SDF shape around the Z axis.
10209
9807
  - `bend(shape: SdfShape, radius: number): SdfShape` — Bend an SDF shape around the Z axis.
10210
9808
  - `repeat(shape: SdfShape, spacing: Vec3, count?: Vec3): SdfShape` — Repeat an SDF shape in space.
9809
+ - `circularArray(shape: SdfShape, count: number, offset?: number): SdfShape` — Arrange an SDF shape in a circular array around the Z axis with O(1) folded-domain evaluation.
10211
9810
  - `SurfacePattern: typeof SurfacePattern` — A 2D surface pattern — a heightmap function for use with `.surfaceDisplace()`.
10212
9811
  - `fromFunction(fn: SdfFunctionSource, options: SdfFunctionOptions): SdfShape` — Create a custom SDF from one expression; shader-safe expressions raymarch directly.
10213
9812
  - `Sculpt: { sphere: (radius: number) => SdfShape; box: (x: number, y: number, z: number, options?: SculptBoxOptions) => SdfShape; cylinder: (height: number, radius: number) => SdfShape; disk: (radius: number, thickness?: number) => SdfShape; circle: (radius: number, thickness?: number) => SdfShape; capsule: (height: number, radius: number) => SdfShape; torus: (majorRadius: number, minorRadius: number) => SdfShape; cone: (height: number, radius: number) => SdfShape; tube: (points: SculptPointList, options?: SculptTubeOptions) => SdfShape; curve: (points: SculptPointList, options?: SculptTubeOptions) => SdfShape; path: (points: SculptPointList, options?: SculptTubeOptions) => SdfShape; blend: (first?: SculptBlendInput | SculptBlendOptions, optionsOrShape?: SculptBlendInput | SculptBlendOptions, ...rest: (SculptBlendInput | SculptBlendOptions)[]) => SdfShape; union: (first?: SculptBlendInput, ...rest: SculptBlendInput[]) => SdfShape; carve: (base: SdfShape, cutters: SculptBlendInput, options?: SculptBlendOptions) => SdfShape; keep: (first?: SculptBlendInput | SculptBlendOptions, optionsOrShape?: SculptBlendInput | SculptBlendOptions, ...rest: (SculptBlendInput | SculptBlendOptions)[]) => SdfShape; polish: (shape: SdfShape, input?: SculptPolishInput) => SdfShape; material: (input?: SculptPolishInput) => ShapeMaterialProps & { color?: string; }; look: (preset?: SculptLookPreset) => SceneOptions; knownMaterials: typeof knownSculptMaterialPresets; }` — Sculpt-like facade: friendly liquid-modeling verbs backed by the same SDF kernel.