forgecad 0.9.5 → 0.9.7

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 (86) hide show
  1. package/dist/assets/{AdminPage-uTtcSXtn.js → AdminPage-DX0mpSZT.js} +1 -1
  2. package/dist/assets/{BlogPage-DYJMjWx3.js → BlogPage-CI_P0_Pf.js} +1 -1
  3. package/dist/assets/{DocsPage-C58f0K5v.js → DocsPage-DLhIIZyJ.js} +3 -3
  4. package/dist/assets/EditorApp-BujZvuwX.js +12874 -0
  5. package/dist/assets/{EditorApp-DS0AIUrZ.css → EditorApp-DfFT2Dn8.css} +1 -0
  6. package/dist/assets/{EmbedViewer-CMXWA2LX.js → EmbedViewer-0S0qXKog.js} +2 -2
  7. package/dist/assets/{LandingPageProofDriven-CAu2OZFn.js → LandingPageProofDriven-O_yMtAri.js} +1 -1
  8. package/dist/assets/{PricingPage-BIgW7m3X.js → PricingPage-DGkX3Ahr.js} +1 -1
  9. package/dist/assets/{SettingsPage-N1l1tMXO.js → SettingsPage-DBsqTB_y.js} +82 -22
  10. package/dist/assets/{app-CFy7g5WP.js → app-BE2nD6Yz.js} +1246 -191
  11. package/dist/assets/cli/{render-BrVVdj_T.js → render-iP9qh475.js} +841 -586
  12. package/dist/assets/{evalWorker-c_SB9gg3.js → evalWorker-Ds5U4xtN.js} +2732 -112
  13. package/dist/assets/inspectWorker-Dll4eVyD.js +12620 -0
  14. package/dist/assets/{manifold-Dp6pvFr6.js → manifold-Bk26ViCr.js} +1 -1
  15. package/dist/assets/{manifold-CRoBhJKH.js → manifold-DjYsd7A_.js} +2 -2
  16. package/dist/assets/{manifold-Cjk7WhRs.js → manifold-sJ-axdXM.js} +1 -1
  17. package/dist/assets/{renderSceneState-3DfsSASX.js → renderSceneState-Bngp5MrQ.js} +1 -1
  18. package/dist/assets/{reportWorker-BLkuIoS8.js → reportWorker-CU8RZ4O0.js} +2715 -112
  19. package/dist/assets/{sectionPlaneMath-CykEnkvQ.js → sectionPlaneMath-BdTjyVfs.js} +3213 -252
  20. package/dist/cli/render.html +1 -1
  21. package/dist/docs/index.html +1 -1
  22. package/dist/docs-raw/AI/usage.md +7 -2
  23. package/dist/docs-raw/CLI.md +82 -53
  24. package/dist/docs-raw/beta-operations.md +9 -0
  25. package/dist/docs-raw/coding.md +1 -1
  26. package/dist/docs-raw/deployment.md +38 -23
  27. package/dist/docs-raw/generated/concepts.md +141 -7
  28. package/dist/docs-raw/generated/core.md +206 -1
  29. package/dist/docs-raw/generated/curves.md +97 -5
  30. package/dist/docs-raw/generated/lib.md +17 -1
  31. package/dist/docs-raw/generated/sketch.md +9 -1
  32. package/dist/docs-raw/generated/viewport.md +1 -1
  33. package/dist/docs-raw/guides/inspection-bundles.md +45 -16
  34. package/dist/docs-raw/platform/auth.md +2 -0
  35. package/dist/docs-raw/platform/google-oauth-setup.md +4 -0
  36. package/dist/docs-raw/runbook.md +3 -3
  37. package/dist/docs-raw/skills/forgecad-make-a-model.md +87 -8
  38. package/dist/docs-raw/skills/forgecad-prepare-prompt.md +14 -6
  39. package/dist/docs-raw/skills/forgecad-render-inspect.md +1 -1
  40. package/dist/docs-raw/skills/index.md +2 -2
  41. package/dist/index.html +1 -1
  42. package/dist/sitemap.xml +6 -6
  43. package/dist-cli/forgecad.js +8725 -4747
  44. package/dist-cli/forgecad.js.map +1 -1
  45. package/dist-skill/CONTEXT.md +375 -25
  46. package/dist-skill/docs/CLI.md +82 -53
  47. package/dist-skill/docs/generated/core.md +206 -1
  48. package/dist-skill/docs/generated/curves.md +97 -5
  49. package/dist-skill/docs/generated/lib.md +17 -1
  50. package/dist-skill/docs/generated/sketch.md +9 -1
  51. package/dist-skill/docs/generated/viewport.md +1 -1
  52. package/dist-skill/docs/guides/inspection-bundles.md +45 -16
  53. package/dist-skill/docs-dev/CLI.md +82 -53
  54. package/dist-skill/docs-dev/coding.md +1 -1
  55. package/dist-skill/docs-dev/generated/core.md +206 -1
  56. package/dist-skill/docs-dev/generated/curves.md +97 -5
  57. package/dist-skill/docs-dev/generated/lib.md +17 -1
  58. package/dist-skill/docs-dev/generated/sketch.md +9 -1
  59. package/dist-skill/docs-dev/generated/viewport.md +1 -1
  60. package/dist-skill/docs-dev/guides/inspection-bundles.md +45 -16
  61. package/dist-skill/library/forgecad-make-a-model/SKILL.md +87 -8
  62. package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +14 -6
  63. package/dist-skill/library/forgecad-prepare-prompt/references/default-profiles.md +5 -3
  64. package/dist-skill/library/forgecad-prepare-prompt/references/master-prompt.md +7 -5
  65. package/dist-skill/library/forgecad-render-inspect/SKILL.md +1 -1
  66. package/examples/api/bolted-service-cover.forge.js +17 -0
  67. package/examples/api/cable-gland-anchor.forge.js +14 -0
  68. package/examples/api/captured-cartridge-guide.forge.js +14 -0
  69. package/examples/api/captured-linear-slide.forge.js +13 -0
  70. package/examples/api/clevis-pin-joint.forge.js +13 -0
  71. package/examples/api/datum-enclosure.forge.js +16 -0
  72. package/examples/api/guided-loft-olive-oil-bottle.forge.js +135 -0
  73. package/examples/api/hose-barb-port.forge.js +14 -0
  74. package/examples/api/intentional-overlap-overmold.forge.js +16 -0
  75. package/examples/api/knuckled-hinge-assembly.forge.js +15 -0
  76. package/examples/api/living-hinge-cover.forge.js +14 -0
  77. package/examples/api/pcb-terminal-block.forge.js +22 -0
  78. package/examples/api/pinned-lever-pivot-stack.forge.js +14 -0
  79. package/examples/api/retained-shaft-knob-stack.forge.js +15 -0
  80. package/examples/api/routed-tube-clip.forge.js +15 -0
  81. package/examples/api/seated-bearing-stack.forge.js +30 -0
  82. package/examples/api/snap-latch-cover.forge.js +14 -0
  83. package/examples/api/static-assembly-connectors.forge.js +14 -16
  84. package/examples/api/thumb-screw-clamp.forge.js +15 -0
  85. package/package.json +20 -2
  86. package/dist/assets/EditorApp-DNH1TEz1.js +0 -12729
@@ -67,50 +67,63 @@ Execute scripts and produce images headless — no browser window. Renders use C
67
67
 
68
68
  ### `forgecad run`
69
69
 
70
- Execute a Forge script and print full geometry diagnostics — object summary, collision detection, spatial analysis, verification results, and solver profiling.
70
+ Execute a Forge script quickly and print the inner-loop build summary: returned objects, verification results, parameters, and timing.
71
71
 
72
- The primary validation command. Runs your script with the real geometry kernel (no browser needed) and prints a comprehensive report:
72
+ The fast validation command. Runs your script with the real geometry kernel (no browser needed) and reports whether it built, which objects came back, any `verify.*` results, parameter values, script logs, and elapsed script time. This is the command agents should run frequently while editing a model.
73
73
 
74
- **Object summary** — lists every named shape with its volume, bounding box, and body count. For constrained sketches, shows solver status (FULLY / UNDER / OVER constrained), DOF, and error residuals. Problematic constraints (conflicting, redundant, or high-residual) are flagged individually.
74
+ **Fast by default** — a bare `forgecad run model.forge.js` does not compute per-object volumes, bounding boxes, construction history, feature tallies, spatial relationships, collision intersections, or solver profiles. Those diagnostics are useful, but they are no longer part of the hot path.
75
75
 
76
- **Construction history** — shows the build sequence for each shape (primitives, operations, modifications) so you can verify the modeling intent.
76
+ **Opt-in diagnostics** — use `--details` for volume/bounding-box/object geometry summaries, `--history` for the construction tree, `--features` for feature tallies, `--solver-profile` for constraint solver timing, or `--full` for the legacy rich report. Use `--spatial bounded|exact` only when you want directional relationships and collision intersections from the run command itself.
77
77
 
78
- **Feature summary** — tallies geometry features across all objects (e.g. `3 extrude, 2 fillet, 1 chamfer`).
78
+ **Verification results** — runs any `verify.*` checks in the script and reports pass/fail with expected vs actual values. Verification failures remain non-fatal so the model can still render and be inspected.
79
79
 
80
- **Verification results** — runs any `verify.*` checks in the script and reports pass/fail with expected vs actual values.
80
+ **Physical connectivity** — pass `--connectivity` to list physically connected components across visible objects. Overlapping bbox candidates are checked with exact geometry by default, while bbox-only contact is treated as evidence rather than proof of one connected component. This helps answer whether the model is one continuous assembly or several separate islands.
81
81
 
82
- **Automatic collision detection** — performs an all-pairs collision check on every named shape. For each pair whose bounding boxes overlap, computes the boolean intersection and reports overlap above 0.1 mm³:
83
-
84
- ```
85
- ⚠ COLLISION: bolt ∩ base (shared vol: 42.3mm³)
86
- ```
87
-
88
- Intra-group pairs (same assembly group) and mock-to-mock pairs are skipped. If a part passes through a boolean-subtracted hole, no collision is reported — the material is gone.
82
+ **Quality preset** — pass `--quality live|default|high` to select the same geometry quality profile used by the editor and export tools. `live` is the fastest preset for large audit models.
89
83
 
90
- **Spatial analysis** reports directional relationships and gap distances between nearby objects (e.g. `bracket is ABOVE base (gap: 5mm)`). Exact pairwise collision intersections run by default only for bounded scenes; use `--spatial exact` for exhaustive collision checks or `--spatial off` to skip this section.
84
+ For deeper confidence gates, prefer `forgecad inspect mechanical-integrity`, `forgecad check print`, `forgecad check params`, or `forgecad render inspect` instead of turning `run` back into a catch-all audit command.
91
85
 
92
- **Physical connectivity** — pass `--connectivity` to list physically connected components across visible objects. Overlapping or touching bboxes are joined within `--connectivity-tolerance` (default `0.05` model units); use collision inspection for exact positive-volume overlaps. This helps answer whether the model is one continuous assembly or several separate islands.
86
+ ```bash
87
+ forgecad run examples/api/static-assembly-connectors.forge.js
88
+ forgecad run examples/api/static-assembly-connectors.forge.js --focus
89
+ forgecad run examples/api/static-assembly-connectors.forge.js --focus "Bench.Slat*"
90
+ forgecad run examples/api/static-assembly-connectors.forge.js --hide "Bench.Slat0,Bench.Slat1"
91
+ forgecad run examples/api/static-assembly-connectors.forge.js --details --history
92
+ forgecad run examples/api/static-assembly-connectors.forge.js --spatial bounded
93
+ forgecad run examples/api/static-assembly-connectors.forge.js --full
94
+ forgecad run examples/products/cup.forge.js --connectivity
95
+ forgecad run examples/products/cup.forge.js --journeys
96
+ forgecad run examples/products/cup.forge.js --backend occt
97
+ forgecad run examples/products/cup.forge.js --backend truck --quality live
98
+ forgecad run examples/products/cup.forge.js --debug-imports
99
+ forgecad run examples/products/cup.forge.js -p "Wall Thickness=3" -p "Body Height=200"
100
+ forgecad run examples/constraints/06-complex-spectrogram.forge.js --solver-debug-out tmp/spectrogram-debug
101
+ ```
93
102
 
94
- **Quality preset** pass `--quality live|default|high` to select the same geometry quality profile used by the editor and export tools. `live` is the fastest preset for large audit models.
103
+ ### Object Filtering: `--focus` and `--hide`
95
104
 
96
- **Parameters** lists all declared parameters with their current values. Overridden values are marked with `*`.
105
+ Several CLI commands can filter the visible object set without changing model code: `run`, `render 3d`, `render wireframe`, `render inspect`, `capture`, and `check print`.
97
106
 
98
- **Solver profiling** when constraint solving occurs, shows timing breakdown (clone, solve, redundancy detection, surface building) and solver internals.
107
+ Use `forgecad run model.forge.js --quality live` to list returned object names quickly, then pass those names to `--focus` or `--hide`.
99
108
 
100
109
  ```bash
101
- forgecad run examples/cup.forge.js
102
- forgecad run examples/cup.forge.js --focus
103
- forgecad run examples/cup.forge.js --focus bracket,hinge
104
- forgecad run examples/cup.forge.js --hide "wall,bolt"
105
- forgecad run examples/cup.forge.js --connectivity
106
- forgecad run examples/cup.forge.js --journeys
107
- forgecad run examples/cup.forge.js --backend occt
108
- forgecad run examples/cup.forge.js --backend truck --quality live
109
- forgecad run examples/cup.forge.js --debug-imports
110
- forgecad run examples/cup.forge.js -p "Wall Thickness=3" -p "Body Height=200"
111
- forgecad run examples/constraints/06-complex-spectrogram.forge.js --solver-debug-out tmp/spectrogram-debug
110
+ forgecad run examples/api/static-assembly-connectors.forge.js --quality live
111
+ forgecad render 3d examples/api/static-assembly-connectors.forge.js bench.png --focus "Bench.*"
112
+ forgecad render 3d examples/api/static-assembly-connectors.forge.js slats.png --focus "Bench.Slat*"
113
+ forgecad render inspect examples/api/static-assembly-connectors.forge.js out/bench-inspect --channels rgb,mask --hide "Bench.Slat0,Bench.Slat1" --force
112
114
  ```
113
115
 
116
+ Rules:
117
+
118
+ - A bare `--focus` hides mock objects and keeps real objects.
119
+ - `--focus name1,name2` renders only matching objects.
120
+ - `--hide name1,name2` removes matching objects from the visible scene.
121
+ - Matching is case-insensitive and supports `*` / `?` globs.
122
+ - `--focus` and `--hide` are mutually exclusive.
123
+ - Filtering happens after the script runs; it does not avoid evaluating the full model.
124
+ - Filtering works on returned object names. For grouped children, use concrete child names or globs like `Bench.*`.
125
+ - If a model unions many parts into one returned shape, the original parts cannot be isolated by CLI filtering.
126
+
114
127
  ### `forgecad render`
115
128
 
116
129
  Render a Forge scene. Use a subcommand — `3d`, `inspect`, `views`, `section`, `wireframe`, `sketch`, or `hq`.
@@ -127,13 +140,13 @@ Render a Forge scene. Use a subcommand — `3d`, `inspect`, `views`, `section`,
127
140
  - `render hq` — path-traced via Blender Cycles, for documentation and marketing shots
128
141
 
129
142
  ```bash
130
- forgecad render 3d examples/cup.forge.js
143
+ forgecad render 3d examples/products/cup.forge.js
131
144
  forgecad render inspect examples/api/static-assembly-connectors.forge.js --channels rgb,mask
132
145
  forgecad render inspect channels
133
- forgecad render views examples/cup.forge.js
134
- forgecad render wireframe examples/cup.forge.js
146
+ forgecad render views examples/products/cup.forge.js
147
+ forgecad render wireframe examples/products/cup.forge.js
135
148
  forgecad render section examples/furniture/01-table.forge.js --plane XZ
136
- forgecad render hq examples/cup.forge.js --preset dramatic
149
+ forgecad render hq examples/products/cup.forge.js --preset dramatic
137
150
  ```
138
151
 
139
152
  ### `forgecad render 3d`
@@ -149,10 +162,10 @@ Use `--edges=<off|thin|bold>` to control the edge overlay. For a pure wireframe
149
162
  This is the standard way to visually verify geometry from the CLI or in agent workflows. For higher quality (path-traced, materials, HDRI lighting), use `render hq` instead.
150
163
 
151
164
  ```bash
152
- forgecad render 3d examples/cup.forge.js
153
- forgecad render 3d examples/cup.forge.js --focus
154
- forgecad render 3d examples/cup.forge.js --focus bracket
155
- forgecad render 3d examples/cup.forge.js --hide "wall,bolt"
165
+ forgecad render 3d examples/products/cup.forge.js
166
+ forgecad render 3d examples/api/static-assembly-connectors.forge.js --focus
167
+ forgecad render 3d examples/api/static-assembly-connectors.forge.js --focus "Bench.Slat*"
168
+ forgecad render 3d examples/api/static-assembly-connectors.forge.js --hide "Bench.Slat0,Bench.Slat1"
156
169
  forgecad render 3d model.forge.js --view hero
157
170
  forgecad render 3d model.forge.js --camera 45:30
158
171
  forgecad render 3d model.forge.js --camera "proj=perspective;pos=200,-160,120;target=0,0,20;up=0,0,1;fov=38"
@@ -186,7 +199,7 @@ For bundle layout, channel encodings, and manifest semantics, see [Inspection Bu
186
199
 
187
200
  ```bash
188
201
  forgecad render inspect examples/api/static-assembly-connectors.forge.js --channels rgb,mask
189
- forgecad render inspect examples/api/static-assembly-connectors.forge.js out/bench-inspect --channels collisions --focus Bench
202
+ forgecad render inspect examples/api/static-assembly-connectors.forge.js out/bench-inspect --channels collisions --focus "Bench.*"
190
203
  forgecad render inspect examples/api/static-assembly-connectors.forge.js --channels rgb,mask,collisions --hide "Bench.Slat0" --force
191
204
  ```
192
205
 
@@ -206,8 +219,8 @@ Render a Forge scene as a wireframe (edges only, no shading).
206
219
  Same as `render 3d` but renders only the edge geometry — no shaded surfaces. Useful for construction-style documentation or highlighting structural features without material detail.
207
220
 
208
221
  ```bash
209
- forgecad render wireframe examples/cup.forge.js
210
- forgecad render wireframe examples/cup.forge.js --camera iso
222
+ forgecad render wireframe examples/products/cup.forge.js
223
+ forgecad render wireframe examples/products/cup.forge.js --camera iso
211
224
  ```
212
225
 
213
226
  ### `forgecad render hq` **\[Pro\]**
@@ -221,12 +234,12 @@ Choose a `--preset` for the look: `studio` (neutral product shot), `dramatic` (h
221
234
  Output defaults to `<script-name>-hq.png`. Great for documentation, marketing renders, and social media.
222
235
 
223
236
  ```bash
224
- forgecad render hq examples/cup.forge.js
225
- forgecad render hq examples/cup.forge.js hero.png --preset dramatic --samples 1024
226
- forgecad render hq examples/cup.forge.js hero.png --view hero
227
- forgecad render hq examples/cup.forge.js hero.png --camera-json camera.json
228
- forgecad render hq examples/cup.forge.js --preset clay --size 2048
229
- forgecad render hq examples/cup.forge.js --transparent --preset glass
237
+ forgecad render hq examples/products/cup.forge.js
238
+ forgecad render hq examples/products/cup.forge.js hero.png --preset dramatic --samples 1024
239
+ forgecad render hq examples/products/cup.forge.js hero.png --view hero
240
+ forgecad render hq examples/products/cup.forge.js hero.png --camera-json camera.json
241
+ forgecad render hq examples/products/cup.forge.js --preset clay --size 2048
242
+ forgecad render hq examples/products/cup.forge.js --transparent --preset glass
230
243
  ```
231
244
 
232
245
  ### `forgecad capture gif|mp4` **\[Pro\]**
@@ -236,13 +249,13 @@ Animated orbit or joint playback.
236
249
  Renders an animated sequence by either orbiting the camera around the model or playing back a `jointsView` animation. Use `--capture orbit` (default) for a turntable rotation, `--capture animation --animation <name>` to play a named joints clip, or `--capture section-sweep` to move a clipping plane through the model. Supports `--cut-plane` to animate with a static cross-section visible. Use `--view`, `--camera`, `--camera-json`, or `--scene <file>` to choose the orbit base camera or the fixed camera for animations and section sweeps.
237
250
 
238
251
  ```bash
239
- forgecad capture gif examples/cup.forge.js
252
+ forgecad capture gif examples/products/cup.forge.js
240
253
  forgecad capture gif examples/3d-printer.forge.js out/section.gif --cut-plane "Front Section"
241
254
  forgecad capture gif model.forge.js out/raw.gif --param "Output=raw-sdf"
242
255
  forgecad capture gif model.forge.js out/front.gif --camera front
243
256
  forgecad capture gif model.forge.js out/hero.gif --view hero
244
257
  forgecad capture gif examples/3d-printer.forge.js out/sweep.gif --capture section-sweep --sweep-plane YZ
245
- forgecad capture mp4 examples/cup.forge.js
258
+ forgecad capture mp4 examples/products/cup.forge.js
246
259
  forgecad capture mp4 examples/api/runtime-joints-view.forge.js out/step.mp4 --capture animation --animation Step
247
260
  forgecad capture mp4 model.forge.js out/raw.mp4 --param "Output=raw-sdf"
248
261
  forgecad capture mp4 model.forge.js out/front.mp4 --camera front
@@ -276,8 +289,8 @@ forgecad render section examples/furniture/01-table.forge.js out/bold.svg --edge
276
289
 
277
290
  | Option | Description |
278
291
  |--------|-------------|
279
- | `--focus <names>` | Focus: no arg hides mocks; comma-separated names shows only those |
280
- | `--hide <names>` | Hide comma-separated object names |
292
+ | `--focus <names>` | Focus: no arg hides mocks; comma-separated names/globs show only those |
293
+ | `--hide <names>` | Hide comma-separated object names/globs |
281
294
  | `--camera <front\|back\|side\|right\|top\|iso\|az:el\|az:el:dist\|spec>` | Camera preset, spherical (az:el), or full spec such as `proj=perspective;pos=x,y,z;target=x,y,z;up=x,y,z;fov=45`. Repeatable. |
282
295
  | `--camera-json <file>` | Exact viewport camera JSON file |
283
296
  | `--view <name>` | Named camera view declared by the model with scene({ views }) |
@@ -299,7 +312,8 @@ forgecad render section examples/furniture/01-table.forge.js out/bold.svg --edge
299
312
  | `--min-thickness <mm>` | Critical thickness threshold in model units |
300
313
  | `--warn-thickness <mm>` | Warning thickness threshold in model units |
301
314
  | `--max-thickness <mm>` | Thick/blue heatmap threshold in model units |
302
- | `--thickness-samples <n>` | Maximum sampled triangles per object |
315
+ | `--thickness-samples <n>` | Maximum thickness point samples per object |
316
+ | `--roughness-samples <n>` | Maximum roughness point samples per object |
303
317
  | `--preset <name>` | Material/lighting preset |
304
318
  | `--width <px>` | Output width in pixels |
305
319
  | `--height <px>` | Output height in pixels |
@@ -530,7 +544,7 @@ forgecad skill one-file ~/Desktop/forgecad-context.md
530
544
  forgecad skill flattened-files ~/Desktop/forgecad-skills
531
545
  ```
532
546
 
533
- > **Workflow:** Agent writes the model -> `forgecad run` validates it -> `forgecad check print` catches printability risks -> `forgecad render inspect` produces visual evidence -> `forgecad check params` sweeps parameter robustness -> export ships the result. All in the terminal.
547
+ > **Workflow:** Agent writes the model -> `forgecad run` validates it -> `forgecad inspect mechanical-integrity` catches disconnected AI-slop patterns -> `forgecad check print` catches printability risks -> `forgecad render inspect` produces visual evidence -> `forgecad check params` sweeps parameter robustness -> export ships the result. All in the terminal.
534
548
 
535
549
  ## Validation
536
550
 
@@ -551,6 +565,20 @@ forgecad check print model.forge.js --min-wall 1.2 --warn-wall 2
551
565
  forgecad check print model.forge.js --expect-components 1 -p "Wall Thickness=3"
552
566
  ```
553
567
 
568
+ ### `forgecad inspect mechanical-integrity`
569
+
570
+ Inspect generated ForgeCAD models for mechanical integrity failures.
571
+
572
+ Scans a Forge script or a folder of generated projects and runs a mechanical integrity inspection. The inspection flags timeouts, runtime errors, missing `verify.*` checks, missing executed mechanical-interface checks, fragmented named groups, uncontracted manual assemblies, optional positive-volume object collisions, and excessive physical component counts when requested. Markdown details include suggested repair patterns such as connector-authored mates, bolted service covers, pinned levers, captured slides, hinges, clevis joints, retained shafts, and seated bearings. When `--collisions` is enabled, the Markdown details list the largest overlapping object pairs by volume so agents can repair the highest-risk interfaces first. Exact collision checks use a default 40-pair bbox-overlap budget and 30s exact-check time budget; exhausting either budget fails the file instead of silently passing a partial check.
573
+
574
+ ```bash
575
+ forgecad inspect mechanical-integrity path/to/generated-models
576
+ forgecad inspect mechanical-integrity path/to/model/main.forge.js --min-verifications 2
577
+ forgecad inspect mechanical-integrity path/to/model/main.forge.js --collisions
578
+ forgecad inspect mechanical-integrity path/to/generated-models --collisions --collision-pair-limit 250
579
+ forgecad inspect mechanical-integrity path/to/generated-models --json --timeout-ms 40000 --jobs 4
580
+ ```
581
+
554
582
  ### `forgecad check params`
555
583
 
556
584
  Sweep parameter ranges and report runtime failures, degeneracy, and new collisions.
@@ -574,6 +602,7 @@ forgecad check params path/to/model.forge.js --samples 12
574
602
  | Command | Description |
575
603
  |---------|-------------|
576
604
  | `check suite` | Run the repo invariant suite, with smoke and full profiles for the fast merge lane vs the broader regression sweep. |
605
+ | `inspect mechanical-integrity` | Inspect generated ForgeCAD models for mechanical integrity failures. |
577
606
  | `check runtime-globals` | Ensure the script sandbox does not add new lowercase injected globals. |
578
607
  | `check transforms` | Run transform and assembly invariants. |
579
608
  | `check dimensions` | Run dimension propagation invariants. |
@@ -617,7 +646,7 @@ The CLI is free for core workflows and exports. Production outputs are free to r
617
646
 
618
647
  | Free | Production outputs | Pro |
619
648
  |------|--------------------|-----|
620
- | `run`, `dev`, `studio`, `render 3d`, `export stl`, `export 3mf`, `export svg`, `check print`, `check params`, `check suite` | `cut-list`, `export sketch-pdf`, `export step`, `export brep`, `export gcode`, `export sdf`, `export urdf`, `export report`, `export cutting-layout` are free to run; Pro covers commercial use. | `render hq`, `capture gif`, `capture mp4` |
649
+ | `run`, `dev`, `studio`, `render 3d`, `export stl`, `export 3mf`, `export svg`, `check print`, `check params`, `inspect mechanical-integrity`, `check suite` | `cut-list`, `export sketch-pdf`, `export step`, `export brep`, `export gcode`, `export sdf`, `export urdf`, `export report`, `export cutting-layout` are free to run; Pro covers commercial use. | `render hq`, `capture gif`, `capture mp4` |
621
650
 
622
651
  ```bash
623
652
  forgecad license # Check signed-in account status
@@ -18,7 +18,7 @@ skill-order: 100
18
18
  - [Grouping & Local Coordinates](#grouping-local-coordinates) — `group`
19
19
  - [Section & Projection](#section-projection) — `intersectWithPlane`, `faceProfile`, `projectToPlane`
20
20
  - [Transforms](#transforms) — `composeChain`
21
- - [Verification](#verification) — `spec`
21
+ - [Verification](#verification) — `verify.that`, `verify.equal`, `verify.notEqual`, `verify.greaterThan`, `verify.lessThan`, `verify.inRange`, `verify.centersCoincide`, `verify.connectorDistance`, `verify.physicalComponentCount`, `verify.intentionalOverlap`, `verify.notColliding`, `verify.minClearance`, `verify.clearanceBetween`, `verify.parallel`, `verify.perpendicular`, `verify.coplanar`, `verify.faceAt`, `verify.sameDirection`, `verify.isEmpty`, `verify.notEmpty`, `verify.volumeApprox`, `verify.areaApprox`, `verify.boundingBoxSize`, `verify.edgeContinuity`, `verify.noTinyEdges`, `verify.noSliverFaces`, `verify.noSelfIntersection`, `spec`
22
22
  - [Shape](#shape) — Appearance, Face Topology, Edge Topology, Transforms, Booleans & Cutting, Features, Placement, Connectors, References, Measurement
23
23
  - [Transform](#transform)
24
24
  - [ShapeGroup](#shapegroup) — Children, Transforms, Placement, Connectors, References
@@ -669,6 +669,207 @@ composeChain(...steps: TransformInput[]): Transform
669
669
 
670
670
  ### Verification
671
671
 
672
+ #### `verify.that()` — Custom predicate check.
673
+
674
+ ```ts
675
+ verify.that(label: string, check: () => boolean, message?: string): void
676
+ ```
677
+
678
+ #### `verify.equal()` — Check that two numbers are approximately equal (within tolerance).
679
+
680
+ ```ts
681
+ verify.equal(label: string, actual: number, expected: number, tolerance?: number, message?: string): void
682
+ ```
683
+
684
+ #### `verify.notEqual()` — Check that two numbers are NOT equal (differ by more than tolerance).
685
+
686
+ ```ts
687
+ verify.notEqual(label: string, actual: number, unexpected: number, tolerance?: number, message?: string): void
688
+ ```
689
+
690
+ #### `verify.greaterThan()` — Check that actual > min.
691
+
692
+ ```ts
693
+ verify.greaterThan(label: string, actual: number, min: number, message?: string): void
694
+ ```
695
+
696
+ #### `verify.lessThan()` — Check that actual < max.
697
+
698
+ ```ts
699
+ verify.lessThan(label: string, actual: number, max: number, message?: string): void
700
+ ```
701
+
702
+ #### `verify.inRange()` — Check that min <= actual <= max.
703
+
704
+ ```ts
705
+ verify.inRange(label: string, actual: number, min: number, max: number, message?: string): void
706
+ ```
707
+
708
+ #### `verify.centersCoincide()` — Check that the bounding-box centers of two shapes coincide within tolerance (mm).
709
+
710
+ ```ts
711
+ verify.centersCoincide(label: string, a: ShapeLike, b: ShapeLike, tolerance?: number): void
712
+ ```
713
+
714
+ `ShapeLike`: `{ min: number[], max: number[] }`
715
+
716
+ #### `verify.connectorDistance()` — Check the distance between two named connectors on a shape or group.
717
+
718
+ Use this when connectors + `matchTo()` define a static assembly interface. It proves the mate at runtime, unlike a plain source-level connector declaration. The common case is `expected = 0`, meaning the two connector origins should coincide after placement.
719
+
720
+ ```ts
721
+ verify.connectorDistance("leg is seated", bench, "Rail.leg_0", "Leg0.head", 0, 0.01);
722
+ ```
723
+
724
+ ```ts
725
+ verify.connectorDistance(label: string, target: ConnectorDistanceLike, connectorA: string, connectorB: string, expected?: number, tolerance?: number): void
726
+ ```
727
+
728
+ #### `verify.physicalComponentCount()` — Declare the expected physical connectivity component count for the returned visible model.
729
+
730
+ Use this for generated mechanical models that should have a clear component graph: one connected fixture, a purchased part plus a removable cartridge, a root assembly plus named intentional ghosts, and so on. `forgecad inspect mechanical-integrity` resolves the returned visible objects with the same physical-connectivity analysis used in the quality gate and fails if the actual component count differs.
731
+
732
+ This catches the common generated-CAD failure where a script returns a visually plausible artifact but the handle, screw, washer, cover, or terminal block is actually a separate island.
733
+
734
+ ```ts
735
+ verify.physicalComponentCount("vise is one connected installed assembly", 1);
736
+ ```
737
+
738
+ ```ts
739
+ verify.physicalComponentCount(label: string, expected: number): void
740
+ ```
741
+
742
+ #### `verify.intentionalOverlap()` — Declare that two visible objects intentionally overlap because the overlap is real manufacturing intent.
743
+
744
+ Use this only for overlaps that a mechanical reviewer would accept as actual matter sharing volume: welded/fused regions, overmolded inserts, potted electronics, cast-in hardware, or deliberately bonded laminations. This is not a shortcut for screws without holes, shafts without bores, covers without pockets, or parts placed with collision as a positioning hack.
745
+
746
+ `forgecad inspect mechanical-integrity --collisions` only honors this declaration when both shapes are returned as visible objects and the exact collision report finds that same object pair. Unused or non-visible declarations fail the quality gate so annotations cannot hide unrelated collisions.
747
+
748
+ ```ts
749
+ verify.intentionalOverlap("rubber grip is overmolded on handle", rubberGrip, handleCore, "overmolded insert");
750
+ ```
751
+
752
+ ```ts
753
+ verify.intentionalOverlap(label: string, a: ShapeLike, b: ShapeLike, reason: string): void
754
+ ```
755
+
756
+ #### `verify.notColliding()` — Check that two shapes do not collide (minGap > 0).
757
+
758
+ ```ts
759
+ verify.notColliding(label: string, a: ShapeLike, b: ShapeLike, searchLength?: number): void
760
+ ```
761
+
762
+ #### `verify.minClearance()` — Check that a minimum clearance gap exists between two shapes.
763
+
764
+ ```ts
765
+ verify.minClearance(label: string, a: ShapeLike, b: ShapeLike, minGap: number, searchLength?: number): void
766
+ ```
767
+
768
+ #### `verify.clearanceBetween()` — Check that the clearance gap between two shapes is inside an allowed range.
769
+
770
+ Use this for seated and retained interfaces where a part must be close enough to be mechanically accountable, but must not collide beyond the allowed minimum. It catches both failure modes that make generated CAD look fake: parts floating away from their receiver, and parts intersecting their receiver because the pocket, bore, or running clearance was not modeled.
771
+
772
+ For contact, use a narrow range such as `[-0.01, 0.05]` to tolerate tiny numerical noise. For a running fit, use the intended clearance band.
773
+
774
+ Manifold-backed shapes use exact min-gap distance. Other backends use a mesh-derived min-gap check and say so in the verification message; keep `forgecad inspect mechanical-integrity --collisions` in the acceptance gate for positive-volume interference.
775
+
776
+ ```ts
777
+ verify.clearanceBetween("cover is seated on gasket", cover, gasket, -0.01, 0.05);
778
+ verify.clearanceBetween("carriage runs inside rail", carriage, rail, 0.2, 0.5);
779
+ ```
780
+
781
+ ```ts
782
+ verify.clearanceBetween(label: string, a: ShapeLike, b: ShapeLike, minGap: number, maxGap: number, searchLength?: number): void
783
+ ```
784
+
785
+ #### `verify.parallel()` — Check that two face normals are parallel (within toleranceDeg degrees).
786
+
787
+ ```ts
788
+ verify.parallel(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void
789
+ ```
790
+
791
+ `FaceRefLike`: `{ normal: [ number, number, number ], center: [ number, number, number ] }`
792
+
793
+ #### `verify.perpendicular()` — Check that two face normals are perpendicular (within toleranceDeg degrees).
794
+
795
+ ```ts
796
+ verify.perpendicular(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void
797
+ ```
798
+
799
+ #### `verify.coplanar()` — Check that a face is coplanar with (same plane as) another face, meaning they are parallel AND their centers lie on the same plane.
800
+
801
+ ```ts
802
+ verify.coplanar(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number, toleranceMm?: number): void
803
+ ```
804
+
805
+ #### `verify.faceAt()` — Check that a face center lies at a specific position (within toleranceMm).
806
+
807
+ ```ts
808
+ verify.faceAt(label: string, face: FaceRefLike, expectedPos: [ number, number, number ], toleranceMm?: number): void
809
+ ```
810
+
811
+ #### `verify.sameDirection()` — Check that two face normals point in the same direction (not antiparallel). Stricter than parallel — both |angle| AND sign must match.
812
+
813
+ ```ts
814
+ verify.sameDirection(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void
815
+ ```
816
+
817
+ #### `verify.isEmpty()` — Check that a shape is empty.
818
+
819
+ ```ts
820
+ verify.isEmpty(label: string, shape: ShapeLike, message?: string): void
821
+ ```
822
+
823
+ #### `verify.notEmpty()` — Check that a shape is NOT empty.
824
+
825
+ ```ts
826
+ verify.notEmpty(label: string, shape: ShapeLike, message?: string): void
827
+ ```
828
+
829
+ #### `verify.volumeApprox()` — Check that a shape's volume is approximately equal to expected (mm³).
830
+
831
+ ```ts
832
+ verify.volumeApprox(label: string, shape: ShapeLike, expected: number, tolerance?: number): void
833
+ ```
834
+
835
+ #### `verify.areaApprox()` — Check that a shape's surface area is approximately equal to expected (mm²).
836
+
837
+ ```ts
838
+ verify.areaApprox(label: string, shape: ShapeLike, expected: number, tolerance?: number): void
839
+ ```
840
+
841
+ #### `verify.boundingBoxSize()` — Check that a shape's bounding box has approximately the given size.
842
+
843
+ ```ts
844
+ verify.boundingBoxSize(label: string, shape: ShapeLike, expectedSize: [ number, number, number ], tolerance?: number): void
845
+ ```
846
+
847
+ #### `verify.edgeContinuity()` — Check that every sampled seam on a shape meets a requested continuity threshold.
848
+
849
+ ```ts
850
+ verify.edgeContinuity(label: string, shape: ShapeLike, options?: EdgeContinuityThresholds): void
851
+ ```
852
+
853
+ **`EdgeContinuityThresholds`**: `continuity?: SurfaceContinuity`, `samples?: number`, `positionTolerance?: number`, `tangentToleranceDeg?: number`, `curvatureTolerance?: number`
854
+
855
+ #### `verify.noTinyEdges()` — Check that a shape has no tiny edges below the requested threshold.
856
+
857
+ ```ts
858
+ verify.noTinyEdges(label: string, shape: ShapeLike, threshold?: number): void
859
+ ```
860
+
861
+ #### `verify.noSliverFaces()` — Check that a shape has no sliver faces below the requested score threshold.
862
+
863
+ ```ts
864
+ verify.noSliverFaces(label: string, shape: ShapeLike, threshold?: number): void
865
+ ```
866
+
867
+ #### `verify.noSelfIntersection()` — Best-effort exact-shape validity guard for self-intersections or broken B-Rep topology.
868
+
869
+ ```ts
870
+ verify.noSelfIntersection(label: string, shape: ShapeLike): void
871
+ ```
872
+
672
873
  #### `spec()` — Create a named, reusable bundle of verification checks.
673
874
 
674
875
  A spec groups related `verify.*` calls under a collapsible header in the Checks panel. This makes large check suites scannable. Specs can be applied to multiple shapes and can check relationships between parts.
@@ -1859,8 +2060,12 @@ toString(): string
1859
2060
  - `lessThan(label: string, actual: number, max: number, message?: string): void` — Check that actual < max.
1860
2061
  - `inRange(label: string, actual: number, min: number, max: number, message?: string): void` — Check that min <= actual <= max.
1861
2062
  - `centersCoincide(label: string, a: ShapeLike, b: ShapeLike, tolerance?: number): void` — Check that the bounding-box centers of two shapes coincide within tolerance (mm).
2063
+ - `connectorDistance(label: string, target: ConnectorDistanceLike, connectorA: string, connectorB: string, expected?: number, tolerance?: number): void` — Check the distance between two named connectors on a shape or group. Use this when connectors + `matchTo()` define a static assembly interface. It proves the mate at runtime, unlike a plain source-level connector declaration. The common case is `expected = 0`, meaning the two connector origins should coincide after placement. **Example** ```ts verify.connectorDistance("leg is seated", bench, "Rail.leg_0", "Leg0.head", 0, 0.01); ```
2064
+ - `physicalComponentCount(label: string, expected: number): void` — Declare the expected physical connectivity component count for the returned visible model. **Details** Use this for generated mechanical models that should have a clear component graph: one connected fixture, a purchased part plus a removable cartridge, a root assembly plus named intentional ghosts, and so on. `forgecad inspect mechanical-integrity` resolves the returned visible objects with the same physical-connectivity analysis used in the quality gate and fails if the actual component count differs. This catches the common generated-CAD failure where a script returns a visually plausible artifact but the handle, screw, washer, cover, or terminal block is actually a separate island. **Example** ```ts verify.physicalComponentCount("vise is one connected installed assembly", 1); ```
2065
+ - `intentionalOverlap(label: string, a: ShapeLike, b: ShapeLike, reason: string): void` — Declare that two visible objects intentionally overlap because the overlap is real manufacturing intent. **Details** Use this only for overlaps that a mechanical reviewer would accept as actual matter sharing volume: welded/fused regions, overmolded inserts, potted electronics, cast-in hardware, or deliberately bonded laminations. This is not a shortcut for screws without holes, shafts without bores, covers without pockets, or parts placed with collision as a positioning hack. `forgecad inspect mechanical-integrity --collisions` only honors this declaration when both shapes are returned as visible objects and the exact collision report finds that same object pair. Unused or non-visible declarations fail the quality gate so annotations cannot hide unrelated collisions. **Example** ```ts verify.intentionalOverlap("rubber grip is overmolded on handle", rubberGrip, handleCore, "overmolded insert"); ```
1862
2066
  - `notColliding(label: string, a: ShapeLike, b: ShapeLike, searchLength?: number): void` — Check that two shapes do not collide (minGap > 0).
1863
2067
  - `minClearance(label: string, a: ShapeLike, b: ShapeLike, minGap: number, searchLength?: number): void` — Check that a minimum clearance gap exists between two shapes.
2068
+ - `clearanceBetween(label: string, a: ShapeLike, b: ShapeLike, minGap: number, maxGap: number, searchLength?: number): void` — Check that the clearance gap between two shapes is inside an allowed range. **Details** Use this for seated and retained interfaces where a part must be close enough to be mechanically accountable, but must not collide beyond the allowed minimum. It catches both failure modes that make generated CAD look fake: parts floating away from their receiver, and parts intersecting their receiver because the pocket, bore, or running clearance was not modeled. For contact, use a narrow range such as `[-0.01, 0.05]` to tolerate tiny numerical noise. For a running fit, use the intended clearance band. Manifold-backed shapes use exact min-gap distance. Other backends use a mesh-derived min-gap check and say so in the verification message; keep `forgecad inspect mechanical-integrity --collisions` in the acceptance gate for positive-volume interference. **Example** ```ts verify.clearanceBetween("cover is seated on gasket", cover, gasket, -0.01, 0.05); verify.clearanceBetween("carriage runs inside rail", carriage, rail, 0.2, 0.5); ```
1864
2069
  - `parallel(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void` — Check that two face normals are parallel (within toleranceDeg degrees).
1865
2070
  - `perpendicular(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void` — Check that two face normals are perpendicular (within toleranceDeg degrees).
1866
2071
  - `coplanar(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number, toleranceMm?: number): void` — Check that a face is coplanar with (same plane as) another face, meaning they are parallel AND their centers lie on the same plane.
@@ -9,7 +9,7 @@ Smooth curves, lofted surfaces, swept solids, splines, and high-level product sk
9
9
 
10
10
  ## Contents
11
11
 
12
- - [Curves & Surfacing](#curves-surfacing) — `hermiteTransitionG2`, `nurbs3d`, `spline2d`, `spline3d`, `loft`, `loftAlongSpine`, `sweep`, `variableSweep`, `nurbsSurface`, `surfacePatch`, `transitionCurve`, `transitionSurface`, `connectEdges`
12
+ - [Curves & Surfacing](#curves-surfacing) — `Loft.station`, `Loft.leftRail`, `Loft.rightRail`, `Loft.frontRail`, `Loft.backRail`, `Loft.centerRail`, `Loft.pathOnXz`, `Loft.pathOnYz`, `Loft.pathOnXy`, `Loft.withGuideRails`, `hermiteTransitionG2`, `nurbs3d`, `spline2d`, `spline3d`, `loft`, `loftAlongSpine`, `sweep`, `variableSweep`, `nurbsSurface`, `surfacePatch`, `transitionCurve`, `transitionSurface`, `connectEdges`
13
13
  - [Surface Members](#surface-members) — `surfaceBand`, `SurfaceBody`
14
14
  - [Curve3D](#curve3d)
15
15
  - [NurbsCurve3D](#nurbscurve3d)
@@ -52,6 +52,87 @@ Smooth curves, lofted surfaces, swept solids, splines, and high-level product sk
52
52
 
53
53
  ### Curves & Surfacing
54
54
 
55
+ #### `Loft.station()` — Create a loft station from a 2D profile and an axis position.
56
+
57
+ ```ts
58
+ Loft.station(profile: Sketch, position: number): LoftStation
59
+ ```
60
+
61
+ `LoftStation`: `{ profile: Sketch, position: number }`
62
+
63
+ #### `Loft.leftRail()` — Create a guide rail that constrains the section-local negative-X side.
64
+
65
+ ```ts
66
+ Loft.leftRail(path: LoftGuideRailPath): LoftGuideRail
67
+ ```
68
+
69
+ `LoftGuideRail`: `{ side: LoftGuideRailSide, path: LoftGuideRailPath }`
70
+
71
+ #### `Loft.rightRail()` — Create a guide rail that constrains the section-local positive-X side.
72
+
73
+ ```ts
74
+ Loft.rightRail(path: LoftGuideRailPath): LoftGuideRail
75
+ ```
76
+
77
+ #### `Loft.frontRail()` — Create a guide rail that constrains the section-local positive-Y side.
78
+
79
+ ```ts
80
+ Loft.frontRail(path: LoftGuideRailPath): LoftGuideRail
81
+ ```
82
+
83
+ #### `Loft.backRail()` — Create a guide rail that constrains the section-local negative-Y side.
84
+
85
+ ```ts
86
+ Loft.backRail(path: LoftGuideRailPath): LoftGuideRail
87
+ ```
88
+
89
+ #### `Loft.centerRail()` — Create a guide rail that moves section centers along the loft.
90
+
91
+ ```ts
92
+ Loft.centerRail(path: LoftGuideRailPath): LoftGuideRail
93
+ ```
94
+
95
+ #### `Loft.pathOnXz()` — Place a 2D guide path onto the XZ plane.
96
+
97
+ The path's first coordinate becomes X and its second coordinate becomes Z. Use this for left/right silhouette rails authored with [`path()`](/docs/sketch#path) or [`constrainedSketch()`](/docs/sketch#constrainedsketch).
98
+
99
+ ```ts
100
+ Loft.pathOnXz(path: LoftPath2D, y?: number): Vec3[]
101
+ ```
102
+
103
+ #### `Loft.pathOnYz()` — Place a 2D guide path onto the YZ plane.
104
+
105
+ The path's first coordinate becomes Y and its second coordinate becomes Z. Use this for front/back crown rails authored with [`path()`](/docs/sketch#path) or [`constrainedSketch()`](/docs/sketch#constrainedsketch).
106
+
107
+ ```ts
108
+ Loft.pathOnYz(path: LoftPath2D, x?: number): Vec3[]
109
+ ```
110
+
111
+ #### `Loft.pathOnXy()` — Place a 2D guide path onto the XY plane.
112
+
113
+ The path's first coordinate becomes X and its second coordinate becomes Y. Use this when lofting along X or Y and a rail lives in a horizontal sketch plane.
114
+
115
+ ```ts
116
+ Loft.pathOnXy(path: LoftPath2D, z?: number): Vec3[]
117
+ ```
118
+
119
+ #### `Loft.withGuideRails()` — Loft through profile stations while forcing generated sections to follow guide rails.
120
+
121
+ Stations define the cross-section family. Guide rails define the side or center paths the loft must pass through. With opposite side rails, the section is scaled to touch both rails. With one side rail, the section keeps its interpolated size unless a center rail is also present.
122
+
123
+ ```ts
124
+ Loft.withGuideRails(stations: LoftStation[], rails: LoftGuideRail[], options?: LoftWithGuideRailsOptions): Shape
125
+ ```
126
+
127
+ **`LoftOptions`**
128
+ - `edgeLength?: number` — Marching-grid edge length for level-set meshing. Smaller = finer.
129
+ - `boundsPadding?: number` — Optional extra bounds padding.
130
+
131
+ **`LoftWithGuideRailsOptions`** extends LoftOptions
132
+ - `axis?: LoftAxis` — Primary station axis. Default Z.
133
+ - `samples?: number` — Number of generated loft stations including ends. Default scales with station count.
134
+ - `railSamples?: number` — Number of points sampled from curve-backed rails before axis interpolation. Default 64.
135
+
55
136
  #### `hermiteTransitionG2()` — Create a quintic Hermite transition curve between two edge endpoints (G2 continuity).
56
137
 
57
138
  The curve starts at `a.point` tangent to `a.tangent` with curvature `a.curvature`, and ends at `b.point` tangent to `b.tangent` with curvature `b.curvature`, with smooth G2-continuous interpolation matching position, tangent, and curvature.
@@ -140,10 +221,6 @@ Performance note: loft is significantly heavier than primitive/extrude/revolve.
140
221
  loft(profiles: Sketch[], heights: number[], options?: LoftOptions): Shape
141
222
  ```
142
223
 
143
- **`LoftOptions`**
144
- - `edgeLength?: number` — Marching-grid edge length for level-set meshing. Smaller = finer.
145
- - `boundsPadding?: number` — Optional extra bounds padding.
146
-
147
224
  #### `loftAlongSpine()` — Loft between multiple profiles positioned along an arbitrary 3D spine curve.
148
225
 
149
226
  Unlike loft() which only supports Z heights, loftAlongSpine() places each profile at a position along a 3D spine, oriented perpendicular to the spine tangent. This enables lofting along curved paths — e.g., a wing root-to-tip transition that follows a swept-back leading edge.
@@ -821,6 +898,21 @@ path().moveTo(0,0).lineTo(10,0).lineTo(10,5).mirror('x').close()
821
898
  mirror(axis: "x" | "y" | [ number, number ]): this
822
899
  ```
823
900
 
901
+ #### `toPolyline()` — Return the open path as a sampled 2D polyline.
902
+
903
+ This is for construction geometry such as guide rails, measured centerlines, and curve-driven helpers where the authored path should stay open instead of becoming a filled sketch or stroked profile.
904
+
905
+ ```ts
906
+ const rail = path()
907
+ .moveTo(24, 0)
908
+ .bezierTo(32, 44, 28, 92, 18, 120)
909
+ .toPolyline();
910
+ ```
911
+
912
+ ```ts
913
+ toPolyline(): [ number, number ][]
914
+ ```
915
+
824
916
  #### `closeOffset()` — Close the path and return an offset version of the filled Sketch. Positive delta expands outward, negative shrinks inward.
825
917
 
826
918
  ```ts