forgecad 0.9.13 → 0.9.15

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 (216) hide show
  1. package/LICENSE +6 -4
  2. package/README.md +8 -4
  3. package/dist/assets/{AdminPage-DramHHDf.js → AdminPage-CDyGUinA.js} +2 -2
  4. package/dist/assets/{BenchmarkPage-Bjgkh5m9.js → BenchmarkPage-DfPMY_-d.js} +4 -15
  5. package/dist/assets/{BlogPage-n_HGP3Qm.js → BlogPage-kF0fkdJT.js} +2 -2
  6. package/dist/assets/{DocsPage-WCIkPmzC.js → DocsPage-B954L3YN.js} +9 -3
  7. package/dist/assets/EditorApp-Beb-IZ0y.js +14014 -0
  8. package/dist/assets/{EditorApp-BAnckbsk.css → EditorApp-CuDLxKqL.css} +698 -0
  9. package/dist/assets/{EmbedViewer-DEZKqdfW.js → EmbedViewer-C77B-TrF.js} +3 -3
  10. package/dist/assets/{LandingPageProofDriven-CeRIctuj.js → LandingPageProofDriven-Cr6fXMDj.js} +35 -37
  11. package/dist/assets/LegalPage-BRlScr9A.css +91 -0
  12. package/dist/assets/LegalPage-Dzklqmmg.js +39 -0
  13. package/dist/assets/{PricingPage-BMedqFef.css → PricingPage-BPF6HKyO.css} +25 -0
  14. package/dist/assets/{PricingPage-rIRa8p4Y.js → PricingPage-zWXkvlwl.js} +19 -19
  15. package/dist/assets/{SettingsPage-BqCUvEXM.js → SettingsPage-Bz0of4KQ.js} +2 -2
  16. package/dist/assets/app-CE3sYcV7.css +3890 -0
  17. package/dist/assets/{app-BUZqJvSO.js → app-D3kDkggg.js} +2305 -960
  18. package/dist/assets/cli/{render-lhGxj50Y.js → render-DSY3mMQa.js} +423 -30
  19. package/dist/assets/{constructionHistoryWorker-ipD1jcIv.js → constructionHistoryWorker-gpDo-uH2.js} +927 -243
  20. package/dist/assets/{evalWorker-CHXSe_-u.js → evalWorker-CU0Ke6DP.js} +7799 -4163
  21. package/dist/assets/{forgecad_geometry-BVnIeXMG.js → forgecad_geometry-Dgceylq9.js} +43 -1
  22. package/dist/assets/{forgecad_geometry_bg-DufhhCBV.wasm → forgecad_geometry_bg-dD4RNQF1.wasm} +0 -0
  23. package/dist/assets/{inspectWorker-DeRnMVv1.js → inspectWorker-COyp8XXA.js} +927 -243
  24. package/dist/assets/{javascript-70-4uGcz.js → javascript-1kQXfVaz.js} +1 -1
  25. package/dist/assets/landing-proof-driven-DiGqdtWa.js +18 -0
  26. package/dist/assets/{landing-proof-driven-oFYW6mjz.css → landing-proof-driven-ORyigZ6p.css} +13 -7
  27. package/dist/assets/legalContent-ZfFGMmi4.js +251 -0
  28. package/dist/assets/{manifold-D1LZIHqn.js → manifold-BRI5prcH.js} +1 -1
  29. package/dist/assets/{manifold-C2fwoTgd.js → manifold-C-3h2M7p.js} +2 -2
  30. package/dist/assets/{manifold-BTkzxi9V.js → manifold-DNkrUWpA.js} +1 -1
  31. package/dist/assets/{reportWorker-Cq1qGmg0.js → reportWorker-CdBz5bNg.js} +7537 -10856
  32. package/dist/assets/{scalar-sampling-budget-D9Qv_UlJ.js → scalar-sampling-budget-wJF98aY9.js} +6943 -4345
  33. package/dist/assets/{scanProxyWorker-Bs2TDgLw.js → scanProxyWorker-B-9VbLIs.js} +32 -1
  34. package/dist/assets/{renderSceneState-Dr0xPq1A.js → targets-B9sGB5nB.js} +27 -1
  35. package/dist/assets/{vendor-react-Da3A2QmU.js → vendor-react-6j1Kke-Y.js} +6 -5
  36. package/dist/cli/render.html +1 -1
  37. package/dist/docs/index.html +2 -2
  38. package/dist/docs-raw/AI/ai-native-cad.md +50 -0
  39. package/dist/docs-raw/AI/usage.md +9 -17
  40. package/dist/docs-raw/CLI.md +71 -21
  41. package/dist/docs-raw/component-model.md +27 -11
  42. package/dist/docs-raw/generated/assembly.md +301 -212
  43. package/dist/docs-raw/generated/concepts.md +238 -240
  44. package/dist/docs-raw/generated/core.md +283 -6
  45. package/dist/docs-raw/generated/curves.md +274 -361
  46. package/dist/docs-raw/generated/lib.md +7 -1
  47. package/dist/docs-raw/generated/output.md +19 -4
  48. package/dist/docs-raw/generated/runtime-names.md +41 -0
  49. package/dist/docs-raw/generated/sdf.md +31 -0
  50. package/dist/docs-raw/generated/sheet-metal.md +9 -0
  51. package/dist/docs-raw/generated/sketch.md +44 -1
  52. package/dist/docs-raw/generated/viewport.md +14 -6
  53. package/dist/docs-raw/guides/coordinate-system.md +20 -16
  54. package/dist/docs-raw/guides/geometry-conventions.md +2 -2
  55. package/dist/docs-raw/guides/inspection-bundles.md +2 -1
  56. package/dist/docs-raw/guides/joint-design.md +24 -0
  57. package/dist/docs-raw/guides/positioning.md +13 -3
  58. package/dist/docs-raw/legal/privacy.md +63 -0
  59. package/dist/docs-raw/legal/software-license.md +55 -0
  60. package/dist/docs-raw/legal/terms.md +87 -0
  61. package/dist/docs-raw/skills/forgecad-3d-reconstruction.md +3 -3
  62. package/dist/docs-raw/skills/forgecad-blockout-model.md +1 -1
  63. package/dist/docs-raw/skills/forgecad-component-model.md +11 -2
  64. package/dist/docs-raw/skills/forgecad-high-level-spec.md +1 -1
  65. package/dist/docs-raw/skills/forgecad-image-replicator.md +8 -8
  66. package/dist/docs-raw/skills/forgecad-lld.md +1 -1
  67. package/dist/docs-raw/skills/forgecad-make-a-model.md +4 -4
  68. package/dist/docs-raw/skills/forgecad-model-grader.md +2 -2
  69. package/dist/docs-raw/skills/forgecad-prepare-prompt.md +2 -2
  70. package/dist/docs-raw/skills/forgecad-project.md +1 -1
  71. package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +4 -4
  72. package/dist/docs-raw/skills/forgecad-render-inspect.md +4 -2
  73. package/dist/docs-raw/skills/forgecad-visual-spec.md +1 -1
  74. package/dist/docs-raw/skills/forgecad.md +4 -3
  75. package/dist/index.html +40 -12
  76. package/dist/llms.txt +8 -0
  77. package/dist/site.webmanifest +1 -1
  78. package/dist/sitemap.xml +49 -13
  79. package/dist-cli/{check-compiler-LOXCPEOI.js → check-compiler-SDX5QIXI.js} +1 -2
  80. package/dist-cli/{check-query-propagation-BAKNVWXR.js → check-query-propagation-EAYEFT77.js} +1 -2
  81. package/dist-cli/{chunk-RY43WF46.js → chunk-N4O47JLF.js} +13772 -9938
  82. package/dist-cli/forgecad.js +2387 -899
  83. package/dist-cli/{forgecad_geometry-GYVNKPIE.js → forgecad_geometry-QOQIIP53.js} +42 -1
  84. package/dist-cli/forgecad_geometry_bg.wasm +0 -0
  85. package/dist-cli/{solver-46FFSK2U.js → solver-OK4HECRH.js} +0 -1
  86. package/dist-skill/CONTEXT.md +1120 -724
  87. package/dist-skill/SKILL.md +3 -2
  88. package/dist-skill/docs/API/core/concepts.md +64 -1
  89. package/dist-skill/docs/CLI.md +71 -21
  90. package/dist-skill/docs/generated/assembly.md +277 -229
  91. package/dist-skill/docs/generated/core.md +283 -6
  92. package/dist-skill/docs/generated/curves.md +272 -362
  93. package/dist-skill/docs/generated/lib.md +7 -1
  94. package/dist-skill/docs/generated/output.md +19 -4
  95. package/dist-skill/docs/generated/runtime-names.md +41 -0
  96. package/dist-skill/docs/generated/sdf.md +31 -0
  97. package/dist-skill/docs/generated/sheet-metal.md +9 -0
  98. package/dist-skill/docs/generated/sketch.md +44 -2
  99. package/dist-skill/docs/generated/viewport.md +5 -90
  100. package/dist-skill/docs/guides/coordinate-system.md +20 -16
  101. package/dist-skill/docs/guides/geometry-conventions.md +2 -2
  102. package/dist-skill/docs/guides/inspection-bundles.md +2 -1
  103. package/dist-skill/docs/guides/joint-design.md +24 -0
  104. package/dist-skill/docs/guides/positioning.md +13 -3
  105. package/dist-skill/library/forgecad-3d-reconstruction/SKILL.md +2 -2
  106. package/dist-skill/library/forgecad-component-model/SKILL.md +10 -1
  107. package/dist-skill/library/forgecad-image-replicator/SKILL.md +6 -6
  108. package/dist-skill/library/forgecad-image-replicator/scripts/compare_images.py +166 -0
  109. package/dist-skill/library/forgecad-make-a-model/SKILL.md +3 -3
  110. package/dist-skill/library/forgecad-model-grader/SKILL.md +1 -1
  111. package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +1 -1
  112. package/dist-skill/library/forgecad-reconstruction-benchmark/SKILL.md +3 -3
  113. package/dist-skill/library/forgecad-render-inspect/SKILL.md +3 -1
  114. package/examples/api/assembly-kinematics-foundation.forge.js +65 -0
  115. package/examples/api/assembly-kinematics-four-bar.forge.js +115 -0
  116. package/examples/api/assembly-kinematics-limb.forge.js +116 -0
  117. package/examples/api/connector-frame-rig-chain.forge.js +102 -0
  118. package/examples/api/exact-sheet-shell-assembly.forge.js +0 -2
  119. package/examples/api/exact-surface-studio.forge.js +6 -8
  120. package/examples/api/helix-basics.forge.js +6 -6
  121. package/examples/api/lean-foundations/README.md +12 -0
  122. package/examples/api/lean-foundations/curve-blend-exact.forge.js +22 -0
  123. package/examples/api/lean-foundations/curve-fit-interpolation.forge.js +18 -0
  124. package/examples/api/lean-foundations/curve-helix-canonicalization.forge.js +27 -0
  125. package/examples/api/lean-foundations/curve-route-canonicalization.forge.js +16 -0
  126. package/examples/api/lean-foundations/curve-trim-reverse.forge.js +24 -0
  127. package/examples/api/lean-foundations/exact-curve-arc.forge.js +36 -0
  128. package/examples/api/mixed-edge-finishes-proof.forge.js +8 -11
  129. package/examples/api/route3d-elbow.forge.js +68 -0
  130. package/examples/api/transition-curves.forge.js +44 -15
  131. package/examples/api/y-blend-corner-showcase.forge.js +0 -2
  132. package/examples/generative/coral-vase.forge.js +1 -1
  133. package/examples/nurbs-tube.forge.js +1 -1
  134. package/package.json +14 -18
  135. package/dist/assets/EditorApp-CP9Za6tm.js +0 -13630
  136. package/dist/assets/app-CsHnaBWt.css +0 -1789
  137. package/dist/docs-raw/API/README.md +0 -16
  138. package/dist/docs-raw/API/core/concepts.md +0 -118
  139. package/dist/docs-raw/INDEX.md +0 -138
  140. package/dist/docs-raw/RELEASING.md +0 -87
  141. package/dist/docs-raw/agent-native-api.md +0 -27
  142. package/dist/docs-raw/beta-deployment.md +0 -304
  143. package/dist/docs-raw/beta-operations.md +0 -325
  144. package/dist/docs-raw/blueprint-first.md +0 -145
  145. package/dist/docs-raw/cli-monetization.md +0 -112
  146. package/dist/docs-raw/coding-best-practices.md +0 -120
  147. package/dist/docs-raw/coding.md +0 -340
  148. package/dist/docs-raw/deployment.md +0 -374
  149. package/dist/docs-raw/guides/skill-maintenance.md +0 -161
  150. package/dist/docs-raw/guides/surface-members.md +0 -82
  151. package/dist/docs-raw/internals/backend-vocabulary.md +0 -35
  152. package/dist/docs-raw/internals/compiler.md +0 -307
  153. package/dist/docs-raw/internals/constraint-solver-quality.md +0 -161
  154. package/dist/docs-raw/internals/constraint-solver.md +0 -176
  155. package/dist/docs-raw/internals/shape-from-slices.md +0 -152
  156. package/dist/docs-raw/internals/sketch-2d-pipeline.md +0 -108
  157. package/dist/docs-raw/platform/admin.md +0 -45
  158. package/dist/docs-raw/platform/architecture.md +0 -82
  159. package/dist/docs-raw/platform/auth.md +0 -139
  160. package/dist/docs-raw/platform/email.md +0 -67
  161. package/dist/docs-raw/platform/google-oauth-setup.md +0 -88
  162. package/dist/docs-raw/platform/observability.md +0 -197
  163. package/dist/docs-raw/platform/projects.md +0 -111
  164. package/dist/docs-raw/platform/sharing.md +0 -90
  165. package/dist/docs-raw/product/README.md +0 -39
  166. package/dist/docs-raw/product/api-as-product-language.md +0 -13
  167. package/dist/docs-raw/product/business-model.md +0 -15
  168. package/dist/docs-raw/product/competitive-positioning.md +0 -17
  169. package/dist/docs-raw/product/creative-manufacturing.md +0 -15
  170. package/dist/docs-raw/product/founder-story.md +0 -11
  171. package/dist/docs-raw/product/manufacturing-workflows.md +0 -15
  172. package/dist/docs-raw/product/onboarding-first-experience.md +0 -256
  173. package/dist/docs-raw/product/product-loop.md +0 -17
  174. package/dist/docs-raw/product/strategic-decisions.md +0 -22
  175. package/dist/docs-raw/product/user-outreach-email-templates.md +0 -161
  176. package/dist/docs-raw/product/user-segments.md +0 -15
  177. package/dist/docs-raw/product/vision.md +0 -26
  178. package/dist/docs-raw/rl-environments.md +0 -508
  179. package/dist/docs-raw/runbook.md +0 -611
  180. package/dist-cli/check-compiler-LOXCPEOI.js.map +0 -1
  181. package/dist-cli/check-query-propagation-BAKNVWXR.js.map +0 -1
  182. package/dist-cli/chunk-RY43WF46.js.map +0 -1
  183. package/dist-cli/forgecad.js.map +0 -1
  184. package/dist-cli/forgecad_geometry-GYVNKPIE.js.map +0 -1
  185. package/dist-cli/solver-46FFSK2U.js.map +0 -1
  186. package/dist-skill/SKILL-dev.md +0 -145
  187. package/dist-skill/docs-dev/API/core/concepts.md +0 -118
  188. package/dist-skill/docs-dev/CLI.md +0 -647
  189. package/dist-skill/docs-dev/agent-native-api.md +0 -27
  190. package/dist-skill/docs-dev/blueprint-first.md +0 -145
  191. package/dist-skill/docs-dev/coding-best-practices.md +0 -120
  192. package/dist-skill/docs-dev/coding.md +0 -340
  193. package/dist-skill/docs-dev/component-model.md +0 -164
  194. package/dist-skill/docs-dev/generated/assembly.md +0 -794
  195. package/dist-skill/docs-dev/generated/core.md +0 -2117
  196. package/dist-skill/docs-dev/generated/curves.md +0 -2583
  197. package/dist-skill/docs-dev/generated/lib.md +0 -169
  198. package/dist-skill/docs-dev/generated/output.md +0 -247
  199. package/dist-skill/docs-dev/generated/sdf.md +0 -446
  200. package/dist-skill/docs-dev/generated/sheet-metal.md +0 -504
  201. package/dist-skill/docs-dev/generated/sketch.md +0 -1811
  202. package/dist-skill/docs-dev/generated/viewport.md +0 -585
  203. package/dist-skill/docs-dev/generated/wood.md +0 -108
  204. package/dist-skill/docs-dev/guides/coordinate-system.md +0 -46
  205. package/dist-skill/docs-dev/guides/geometry-conventions.md +0 -52
  206. package/dist-skill/docs-dev/guides/inspection-bundles.md +0 -485
  207. package/dist-skill/docs-dev/guides/joint-design.md +0 -78
  208. package/dist-skill/docs-dev/guides/modeling-recipes.md +0 -78
  209. package/dist-skill/docs-dev/guides/positioning.md +0 -161
  210. package/dist-skill/docs-dev/guides/skill-maintenance.md +0 -161
  211. package/dist-skill/docs-dev/internals/backend-vocabulary.md +0 -35
  212. package/dist-skill/docs-dev/internals/compiler.md +0 -307
  213. package/dist-skill/docs-dev/internals/constraint-solver-quality.md +0 -161
  214. package/dist-skill/docs-dev/internals/constraint-solver.md +0 -176
  215. package/dist-skill/docs-dev/internals/sketch-2d-pipeline.md +0 -108
  216. package/dist-skill/library/forgecad-image-replicator/scripts/compare_images.mjs +0 -289
@@ -9,15 +9,13 @@ Smooth curves, lofted surfaces, swept solids, splines, and high-level product sk
9
9
 
10
10
  ## Contents
11
11
 
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`, `Helix.path`, `Helix.coil`, `hermiteTransitionG2`, `nurbs3d`, `spline2d`, `spline3d`, `loft`, `loftAlongSpine`, `sweep`, `variableSweep`, `nurbsSurface`, `surfacePatch`, `transitionCurve`, `transitionSurface`, `connectEdges`
12
+ - [Curves & Surfacing](#curves-surfacing) — `Curve.Blend`, `Curve.BlendG2`, `Curve.Arc`, `Curve.Line`, `Curve.Polyline`, `Curve.Spline`, `Curve.Nurbs`, `Curve.Fit`, `Curve.Trim`, `Curve.Reverse`, `Curve.Route`, `Curve.Helix`, `Loft.station`, `Loft.leftRail`, `Loft.rightRail`, `Loft.frontRail`, `Loft.backRail`, `Loft.centerRail`, `Loft.pathOnXz`, `Loft.pathOnYz`, `Loft.pathOnXy`, `Loft.withGuideRails`, `spline2d`, `loft`, `sweep`, `variableSweep`, `nurbsSurface`, `surfacePatch`
13
13
  - [Surface Members](#surface-members) — `surfaceBand`, `SurfaceBody`
14
14
  - [Curve3D](#curve3d)
15
- - [HelixCurve](#helixcurve)
15
+ - [Route3D](#route3d)
16
16
  - [NurbsCurve3D](#nurbscurve3d)
17
17
  - [NurbsSurface](#nurbssurface)
18
18
  - [PathBuilder](#pathbuilder) — Line Segments, Arcs, Curves, Closing & Output
19
- - [HermiteCurve3D](#hermitecurve3d)
20
- - [QuinticHermiteCurve3D](#quintichermitecurve3d)
21
19
  - [ProductSkin](#productskin)
22
20
  - [ProductSurfaceRef](#productsurfaceref)
23
21
  - [ProductSurfaceBuilder](#productsurfacebuilder)
@@ -39,6 +37,7 @@ Smooth curves, lofted surfaces, swept solids, splines, and high-level product sk
39
37
  - [SurfaceJoinBuilder](#surfacejoinbuilder)
40
38
  - [CounterboreBuilder](#counterborebuilder)
41
39
  - [RoundedSlotBuilder](#roundedslotbuilder)
40
+ - [Curve](#curve)
42
41
  - [Surface](#surface)
43
42
  - [Blend](#blend)
44
43
  - [Analysis](#analysis)
@@ -48,97 +47,187 @@ Smooth curves, lofted surfaces, swept solids, splines, and high-level product sk
48
47
  - [Slot](#slot)
49
48
  - [Counterbore](#counterbore)
50
49
  - [Ribs](#ribs)
51
- - [Helix](#helix)
52
50
 
53
51
  ## Functions
54
52
 
55
53
  ### Curves & Surfacing
56
54
 
57
- #### `Loft.station()` — Create a loft station from a 2D profile and an axis position.
55
+ #### `Curve.Blend()` — Create an exact G1 blend curve between two directed endpoints.
56
+
57
+ The returned curve is a cubic non-rational `NurbsCurve3D`: ForgeCAD converts the endpoint positions and tangents into Bezier control points, so the curve can feed `sweep` and exact surface boundaries through the existing `nurbs` IR rather than a sampled polyline.
58
+
59
+ ```js
60
+ const rail = Curve.Blend(
61
+ { point: [0, 0, 0], tangent: [1, 0, 0], weight: 0.8 },
62
+ { point: [40, 20, 8], tangent: [0, 1, 0], weight: 0.8 },
63
+ );
64
+ const tube = sweep(circle2d(2), rail);
65
+ ```
58
66
 
59
67
  ```ts
60
- Loft.station(profile: Sketch, position: number): LoftStation
68
+ Curve.Blend(start: CurveBlendEndpoint, end: CurveBlendEndpoint): NurbsCurve3D
61
69
  ```
62
70
 
63
- `LoftStation`: `{ profile: Sketch, position: number }`
71
+ **`CurveBlendEndpoint`**
72
+ - `point: Vec3` — Endpoint position.
73
+ - `tangent: Vec3` — Tangent direction at this endpoint. Magnitude is ignored.
74
+ - `weight?: number` — Tangent reach relative to the endpoint chord length. Default 1.
64
75
 
65
- #### `Loft.leftRail()` — Create a guide rail that constrains the section-local negative-X side.
76
+ #### `Curve.BlendG2()` — Create an exact G2 blend curve between two directed endpoints.
77
+
78
+ This is the curvature-aware companion to `Curve.Blend()`. It returns a degree-5 non-rational `NurbsCurve3D` that matches endpoint position, tangent direction, and optional curvature/second-derivative vectors.
79
+
80
+ ```js
81
+ const rail = Curve.BlendG2(
82
+ { point: [0, 0, 0], tangent: [1, 0, 0], curvature: [0, 0.02, 0] },
83
+ { point: [50, 20, 0], tangent: [0, 1, 0], curvature: [-0.02, 0, 0] },
84
+ );
85
+ ```
66
86
 
67
87
  ```ts
68
- Loft.leftRail(path: LoftGuideRailPath): LoftGuideRail
88
+ Curve.BlendG2(start: CurveBlendG2Endpoint, end: CurveBlendG2Endpoint): NurbsCurve3D
69
89
  ```
70
90
 
71
- `LoftGuideRail`: `{ side: LoftGuideRailSide, path: LoftGuideRailPath }`
72
91
 
73
- #### `Loft.rightRail()` — Create a guide rail that constrains the section-local positive-X side.
92
+ **`CurveBlendG2Endpoint`** extends CurveBlendEndpoint
93
+ - `curvature?: Vec3` — Optional endpoint curvature/second-derivative vector. Default is zero.
94
+
95
+ #### `Curve.Arc()` — Create an exact circular 3D arc from start, end, and start tangent.
96
+
97
+ The returned curve is a rational quadratic `NurbsCurve3D`, split into stable spans when needed, so it can feed `sweep` without sampling the authoring intent away.
98
+
99
+ ```js
100
+ const rail = Curve.Arc({
101
+ start: [40, 0, 0],
102
+ end: [0, 40, 0],
103
+ tangent: [0, 1, 0],
104
+ });
105
+ const tube = sweep(circle2d(2), rail);
106
+ ```
74
107
 
75
108
  ```ts
76
- Loft.rightRail(path: LoftGuideRailPath): LoftGuideRail
109
+ Curve.Arc(options: CurveArcOptions): NurbsCurve3D
77
110
  ```
78
111
 
79
- #### `Loft.frontRail()` — Create a guide rail that constrains the section-local positive-Y side.
112
+ **`CurveArcOptions`**
113
+ - `start: Vec3` — Arc start point.
114
+ - `end: Vec3` — Arc end point.
115
+ - `tangent: Vec3` — Tangent direction at the start point. Magnitude is ignored.
116
+
117
+ #### `Curve.Line()` — Create an exact straight 3D NURBS line segment.
118
+
119
+ ```js
120
+ const rail = Curve.Line([0, 0, 0], [80, 0, 15]);
121
+ const rib = sweep(circle2d(2), rail);
122
+ ```
80
123
 
81
124
  ```ts
82
- Loft.frontRail(path: LoftGuideRailPath): LoftGuideRail
125
+ Curve.Line(start: Vec3, end: Vec3): NurbsCurve3D
83
126
  ```
84
127
 
85
- #### `Loft.backRail()` — Create a guide rail that constrains the section-local negative-Y side.
128
+ #### `Curve.Polyline()` — Create a polyline path as cloned 3D points.
129
+
130
+ Polylines are exact as route/path input to `sweep`. Use `Curve.Route` when the centerline needs bend and endpoint-frame metadata.
86
131
 
87
132
  ```ts
88
- Loft.backRail(path: LoftGuideRailPath): LoftGuideRail
133
+ Curve.Polyline(points: Vec3[]): Vec3[]
89
134
  ```
90
135
 
91
- #### `Loft.centerRail()` — Create a guide rail that moves section centers along the loft.
136
+ #### `Curve.Spline()` — Create a smooth Catmull-Rom spline path.
137
+
138
+ This is a smooth sampled curve object. Use `Curve.Nurbs` when the path must preserve exact control-point and knot data.
92
139
 
93
140
  ```ts
94
- Loft.centerRail(path: LoftGuideRailPath): LoftGuideRail
141
+ Curve.Spline(points: Vec3[], options?: Spline3DOptions): Curve3D
95
142
  ```
96
143
 
97
- #### `Loft.pathOnXz()` — Place a 2D guide path onto the XZ plane.
144
+ **`Spline3DOptions`**
145
+ - `closed?: boolean` — Closed loop (default false).
146
+ - `tension?: number` — Catmull-Rom tension in [0, 1]. 0 = very round, 1 = linear-ish. Default 0.5.
98
147
 
99
- 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).
148
+ #### `Curve.Nurbs()` Create an exact NURBS 3D curve from control points, weights, knots, and degree.
149
+
150
+ ```js
151
+ const rail = Curve.Nurbs([[0, 0, 0], [30, 4, 12], [60, -4, 12], [90, 0, 0]]);
152
+ const tube = sweep(circle2d(2), rail);
153
+ ```
100
154
 
101
155
  ```ts
102
- Loft.pathOnXz(path: LoftPath2D, y?: number): Vec3[]
156
+ Curve.Nurbs(points: Vec3[], options?: NurbsCurve3DOptions): NurbsCurve3D
103
157
  ```
104
158
 
105
- #### `Loft.pathOnYz()` — Place a 2D guide path onto the YZ plane.
159
+ **`NurbsCurve3DOptions`**
106
160
 
107
- 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).
161
+ | Option | Type | Description |
162
+ |--------|------|-------------|
163
+ | `degree?` | `number` | Polynomial degree (default 3 = cubic). Must be ≥ 1. |
164
+ | `weights?` | `number[]` | Rational weights, one per control point (default: all 1.0 = non-rational). |
165
+ | `knots?` | `number[]` | Knot vector (default: uniform clamped). Must have length = controlPoints.length + degree + 1. |
166
+ | `closed?` | `boolean` | Whether the curve is closed/periodic (default false). |
167
+
168
+ #### `Curve.Fit()` — Fit a non-rational NURBS curve that interpolates every input point.
169
+
170
+ This is global B-spline interpolation, not approximate curve reduction: ForgeCAD computes chord-length parameters, averaged clamped knots, solves the control points, then verifies the interpolation residual against `tolerance`.
171
+
172
+ ```js
173
+ const rail = Curve.Fit(
174
+ [[0, 0, 0], [20, 8, 12], [50, -4, 18], [80, 0, 0]],
175
+ { degree: 3, tolerance: 0.001 },
176
+ );
177
+ const tube = sweep(circle2d(2), rail);
178
+ ```
108
179
 
109
180
  ```ts
110
- Loft.pathOnYz(path: LoftPath2D, x?: number): Vec3[]
181
+ Curve.Fit(points: Vec3[], options?: CurveFitOptions): NurbsCurve3D
111
182
  ```
112
183
 
113
- #### `Loft.pathOnXy()` — Place a 2D guide path onto the XY plane.
184
+ **`CurveFitOptions`**
185
+ - `degree?: number` — Polynomial degree. Default is cubic, reduced automatically for short point lists.
186
+ - `tolerance?: number` — Maximum allowed interpolation residual in model units. Default 1e-7.
114
187
 
115
- 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.
188
+ #### `Curve.Trim()` Extract an exact curve segment from normalized parameter `start` to `end`.
189
+
190
+ `NurbsCurve3D` inputs are trimmed with exact knot insertion/subdomain extraction. Polyline point arrays are trimmed by arclength over their exact line segments. Sampled `Curve3D` splines are rejected until ForgeCAD has a tolerance-controlled rebuild path.
116
191
 
117
192
  ```ts
118
- Loft.pathOnXy(path: LoftPath2D, z?: number): Vec3[]
193
+ Curve.Trim<T extends CurveTrimInput>(curve: T, start: number, end: number): CurveTrimOutput<T>
119
194
  ```
120
195
 
121
- #### `Loft.withGuideRails()` — Loft through profile stations while forcing generated sections to follow guide rails.
196
+ #### `Curve.Reverse()` — Reverse an exact curve without changing its geometry.
122
197
 
123
- 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.
198
+ `NurbsCurve3D` inputs reverse control points, weights, and knots. Polyline point arrays are cloned and reversed. Sampled `Curve3D` splines are rejected until ForgeCAD has a tolerance-controlled rebuild path.
124
199
 
125
200
  ```ts
126
- Loft.withGuideRails(stations: LoftStation[], rails: LoftGuideRail[], options?: LoftWithGuideRailsOptions): Shape
201
+ Curve.Reverse<T extends CurveTrimInput>(curve: T): CurveTrimOutput<T>
127
202
  ```
128
203
 
129
- **`LoftOptions`**
130
- - `edgeLength?: number` — Marching-grid edge length for level-set meshing. Smaller = finer.
131
- - `boundsPadding?: number` — Optional extra bounds padding.
204
+ #### `Curve.Route()` — Build analytic 3D line/arc routes for sweeps.
132
205
 
133
- **`LoftWithGuideRailsOptions`** extends LoftOptions
134
- - `axis?: LoftAxis` — Primary station axis. Default Z.
135
- - `samples?: number` — Number of generated loft stations including ends. Default scales with station count.
136
- - `railSamples?: number` — Number of points sampled from curve-backed rails before axis interpolation. Default 64.
206
+ `Curve.Route.fromPolyline()` is the canonical route API. It returns a `Route3D` value object, preserving exact route segments, named port frames, and the lowerable `route3d` sweep compile plan.
207
+
208
+ ```js
209
+ const route = Curve.Route.fromPolyline(
210
+ [[0, 0, 0], [0, 0, 50], [40, 0, 50]],
211
+ { cornerRadius: 12, startPort: 'inlet', endPort: 'outlet' },
212
+ );
213
+ const tube = sweep(circle2d(4), route);
214
+ ```
215
+
216
+ ```ts
217
+ Curve.Route: typeof Route3D
218
+ ```
219
+
220
+ #### `Curve.Helix()` — Build helical paths and swept coils.
221
+
222
+ `Curve.Helix` is the canonical namespace for helical paths and coils. It uses the same sweep-based lowering as other curve paths.
137
223
 
138
- #### `Helix.path()` — Create a metadata-bearing helical centerline around the Z axis.
224
+ ```js
225
+ const guide = Curve.Helix.path({ radius: 20, pitch: 6, turns: 4 });
226
+ const spring = Curve.Helix.coil({ radius: 20, pitch: 6, turns: 4, wireRadius: 1 });
227
+ ```
139
228
 
140
229
  ```ts
141
- Helix.path(options: HelixOptions): HelixCurve
230
+ Curve.Helix: { path(options: HelixOptions): CurveHelixPath; coil: CurveHelixCoil; }
142
231
  ```
143
232
 
144
233
  **`HelixOptions`**
@@ -153,66 +242,89 @@ Helix.path(options: HelixOptions): HelixCurve
153
242
  | `clockwise?` | `boolean` | Reverse winding direction when viewed from +Z. |
154
243
  | `samplesPerTurn?` | `number` | Point samples per turn for the metadata path. Default 32. |
155
244
 
156
- #### `Helix.coil()` — Create a solid helical coil by sweeping a profile through helix-local frames.
157
245
 
158
- Overloads:
246
+ `CurveHelixPath`: `{ radius: number, pitch: number, turns: number, height: number, startAngle: number, clockwise: boolean }`
159
247
 
160
- - `Helix.coil(options: HelixCoilOptions): Shape`
161
- - `Helix.coil(profile: Sketch, options: HelixCoilOptions): Shape`
248
+ #### `Loft.station()` — Create a loft station from a 2D profile and an axis position.
162
249
 
250
+ ```ts
251
+ Loft.station(profile: Sketch, position: number): LoftStation
252
+ ```
163
253
 
164
- **`HelixCoilOptions`** extends HelixOptions
165
- - `wireRadius?: number` — Radius of the circular wire profile. Required unless a custom profile is passed.
166
- - `profileSegments?: number` — Segment count for the default circular wire profile. Default 24.
167
- - `divisionsPerTurn?: number` — Sweep path samples per turn. Default 32.
254
+ `LoftStation`: `{ profile: Sketch, position: number }`
168
255
 
169
- #### `hermiteTransitionG2()` — Create a quintic Hermite transition curve between two edge endpoints (G2 continuity).
256
+ #### `Loft.leftRail()` — Create a guide rail that constrains the section-local negative-X side.
170
257
 
171
- 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.
258
+ ```ts
259
+ Loft.leftRail(path: LoftGuideRailPath): LoftGuideRail
260
+ ```
261
+
262
+ `LoftGuideRail`: `{ side: LoftGuideRailSide, path: LoftGuideRailPath }`
263
+
264
+ #### `Loft.rightRail()` — Create a guide rail that constrains the section-local positive-X side.
172
265
 
173
266
  ```ts
174
- hermiteTransitionG2(a: QuinticHermiteCurveEndpoint, b: QuinticHermiteCurveEndpoint): QuinticHermiteCurve3D
267
+ Loft.rightRail(path: LoftGuideRailPath): LoftGuideRail
175
268
  ```
176
269
 
177
- **`QuinticHermiteCurveEndpoint`**
270
+ #### `Loft.frontRail()` — Create a guide rail that constrains the section-local positive-Y side.
178
271
 
179
- | Option | Type | Description |
180
- |--------|------|-------------|
181
- | `point` | `Vec3` | Position |
182
- | `tangent` | `Vec3` | Tangent direction (will be normalized internally) |
183
- | `curvature?` | `Vec3` | Second derivative / curvature vector. Default [0, 0, 0]. |
184
- | `weight?` | `number` | Weight: scales tangent magnitude relative to chord length. Default 1.0. |
272
+ ```ts
273
+ Loft.frontRail(path: LoftGuideRailPath): LoftGuideRail
274
+ ```
185
275
 
186
- #### `nurbs3d()` — Create a NURBS curve from control points.
276
+ #### `Loft.backRail()` — Create a guide rail that constrains the section-local negative-Y side.
187
277
 
188
- With default options, creates a cubic non-rational B-spline with uniform clamped knots. Set `weights` for rational curves (exact circles, conics). Set `degree` for linear (1), quadratic (2), cubic (3), or higher-order curves.
278
+ ```ts
279
+ Loft.backRail(path: LoftGuideRailPath): LoftGuideRail
280
+ ```
189
281
 
190
- ```js
191
- // Simple cubic B-spline through control points
192
- const curve = nurbs3d([[0,0,0], [10,5,0], [20,-5,10], [30,0,5]]);
193
- const tube = sweep(circle(2), curve);
282
+ #### `Loft.centerRail()` — Create a guide rail that moves section centers along the loft.
283
+
284
+ ```ts
285
+ Loft.centerRail(path: LoftGuideRailPath): LoftGuideRail
194
286
  ```
195
287
 
196
- ```js
197
- // Rational quadratic — exact circular arc
198
- const arc = nurbs3d(
199
- [[10,0,0], [10,10,0], [0,10,0]],
200
- { degree: 2, weights: [1, Math.SQRT1_2, 1] }
201
- );
288
+ #### `Loft.pathOnXz()` — Place a 2D guide path onto the XZ plane.
289
+
290
+ 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).
291
+
292
+ ```ts
293
+ Loft.pathOnXz(path: LoftPath2D, y?: number): Vec3[]
202
294
  ```
203
295
 
296
+ #### `Loft.pathOnYz()` — Place a 2D guide path onto the YZ plane.
297
+
298
+ 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).
299
+
204
300
  ```ts
205
- nurbs3d(points: Vec3[], options?: NurbsCurve3DOptions): NurbsCurve3D
301
+ Loft.pathOnYz(path: LoftPath2D, x?: number): Vec3[]
206
302
  ```
207
303
 
208
- **`NurbsCurve3DOptions`**
304
+ #### `Loft.pathOnXy()` — Place a 2D guide path onto the XY plane.
209
305
 
210
- | Option | Type | Description |
211
- |--------|------|-------------|
212
- | `degree?` | `number` | Polynomial degree (default 3 = cubic). Must be ≥ 1. |
213
- | `weights?` | `number[]` | Rational weights, one per control point (default: all 1.0 = non-rational). |
214
- | `knots?` | `number[]` | Knot vector (default: uniform clamped). Must have length = controlPoints.length + degree + 1. |
215
- | `closed?` | `boolean` | Whether the curve is closed/periodic (default false). |
306
+ 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.
307
+
308
+ ```ts
309
+ Loft.pathOnXy(path: LoftPath2D, z?: number): Vec3[]
310
+ ```
311
+
312
+ #### `Loft.withGuideRails()` — Loft through profile stations while forcing generated sections to follow guide rails.
313
+
314
+ 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.
315
+
316
+ ```ts
317
+ Loft.withGuideRails(stations: LoftStation[], rails: LoftGuideRail[], options?: LoftWithGuideRailsOptions): Shape
318
+ ```
319
+
320
+ **`LoftOptions`**
321
+ - `edgeLength?: number` — Marching-grid edge length for level-set meshing. Smaller = finer.
322
+ - `boundsPadding?: number` — Optional extra bounds padding.
323
+
324
+ **`LoftWithGuideRailsOptions`** extends LoftOptions
325
+ - `axis?: LoftAxis` — Primary station axis. Default Z.
326
+ - `samples?: number` — Number of generated loft stations including ends. Default scales with station count.
327
+ - `railSamples?: number` — Number of points sampled from curve-backed rails before axis interpolation. Default 64.
216
328
 
217
329
  #### `spline2d()` — Build a smooth Catmull-Rom spline sketch from 2D control points.
218
330
 
@@ -232,18 +344,6 @@ spline2d(points: Vec2[], options?: Spline2DOptions): Sketch
232
344
  | `strokeWidth?` | `number` | For open splines, provide stroke width to return a solid Sketch. If omitted for open splines, an error is thrown. |
233
345
  | `join?` | `"Round" \| "Square"` | Stroke join for open splines. Default 'Round'. |
234
346
 
235
- #### `spline3d()` — Create a reusable 3D spline curve object (Catmull-Rom).
236
-
237
- The returned Curve3D provides sample(), pointAt(t), tangentAt(t), and length() for downstream use in sweep() or manual path operations.
238
-
239
- ```ts
240
- spline3d(points: Vec3[], options?: Spline3DOptions): Curve3D
241
- ```
242
-
243
- **`Spline3DOptions`**
244
- - `closed?: boolean` — Closed loop (default false).
245
- - `tension?: number` — Catmull-Rom tension in [0, 1]. 0 = very round, 1 = linear-ish. Default 0.5.
246
-
247
347
  #### `loft()` — Loft between multiple sketches along Z stations.
248
348
 
249
349
  Profiles can differ in topology and vertex count: interpolation is done on signed-distance fields and meshed with level-set extraction. Heights must be strictly increasing. Compatible loft stacks can also stay on the maintained export-backend path.
@@ -254,29 +354,6 @@ Performance note: loft is significantly heavier than primitive/extrude/revolve.
254
354
  loft(profiles: Sketch[], heights: number[], options?: LoftOptions): Shape
255
355
  ```
256
356
 
257
- #### `loftAlongSpine()` — Loft between multiple profiles positioned along an arbitrary 3D spine curve.
258
-
259
- 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.
260
-
261
- The tValues array specifies where each profile sits along the spine (0 = start, 1 = end). Must have the same length as profiles and be in [0, 1].
262
-
263
- Internally uses variableSweep infrastructure with SDF interpolation.
264
-
265
- Performance note: uses level-set meshing, heavier than simple loft().
266
-
267
- ```ts
268
- loftAlongSpine(profiles: Sketch[], spine: Curve3D | Vec3[], tValues: number[], options?: LoftAlongSpineOptions): Shape
269
- ```
270
-
271
- **`LoftAlongSpineOptions`**
272
-
273
- | Option | Type | Description |
274
- |--------|------|-------------|
275
- | `samples?` | `number` | Number of samples when spine is a Curve3D. Default 48. |
276
- | `edgeLength?` | `number` | Marching-grid edge length for level-set meshing. Smaller = finer. |
277
- | `boundsPadding?` | `number` | Optional extra bounds padding. |
278
- | `up?` | `Vec3` | Preferred "up" vector for local profile frame. Auto fallback is used near parallel segments. |
279
-
280
357
  #### `sweep()`
281
358
 
282
359
  ```ts
@@ -400,106 +477,6 @@ surfacePatch(curves: { ... }, options?: SurfacePatchOptions): Shape
400
477
  - `thickness?: number` — Thickness of the generated solid. Default 0 for an open exact sheet.
401
478
  - `approximate?: boolean` — Allow explicit approximation for non-exact curve inputs such as Curve3D samples.
402
479
 
403
- #### `transitionCurve()` — Create a smooth transition curve between two edges.
404
-
405
- Returns a `HermiteCurve3D` that starts at `edgeA.point` tangent to `edgeA.tangent` and ends at `edgeB.point` tangent to `edgeB.tangent`.
406
-
407
- The curve maintains G1 continuity (matching tangent direction) at both endpoints. Weight parameters control the shape of the transition.
408
-
409
- ```js
410
- // Connect two edges with a balanced transition
411
- const curve = transitionCurve(
412
- { point: [0, 0, 0], tangent: [1, 0, 0] },
413
- { point: [10, 5, 0], tangent: [1, 0, 0] },
414
- );
415
- ```
416
-
417
- // Weighted: curve hugs edge A longer const weighted = transitionCurve( { point: [0, 0, 0], tangent: [1, 0, 0] }, { point: [10, 5, 0], tangent: [1, 0, 0] }, { weightA: 2.0, weightB: 0.5 }, );
418
-
419
- ```
420
-
421
- ```ts
422
- transitionCurve(edgeA: TransitionEdge, edgeB: TransitionEdge, options?: TransitionCurveOptions): HermiteCurve3D
423
- ```
424
-
425
- **`TransitionEdge`**
426
- - `point: Vec3` — Connection point on the edge. Can be any point along the edge where the transition should connect.
427
- - `tangent: Vec3` — Tangent direction at the connection point. This is the direction the curve should initially follow when leaving this edge. For a straight edge, this is typically the edge direction pointing "outward" (away from the body of the edge, toward the other edge).
428
- - `normal?: Vec3` — Surface normal at the connection point (optional). Used as a hint for the sweep frame's up vector.
429
-
430
- **`TransitionCurveOptions`**
431
- - `weightA?: number` — Weight for the start edge. Controls tangent magnitude at the start. - 1.0 (default): balanced transition - > 1.0: curve follows start edge longer before turning - < 1.0: curve turns sooner at the start
432
- - `weightB?: number` — Weight for the end edge. Controls tangent magnitude at the end. - 1.0 (default): balanced transition - > 1.0: curve follows end edge longer before turning - < 1.0: curve turns sooner at the end
433
- - `samples?: number` — Number of sample points for the output polyline. Default 64. Higher values give smoother curves at the cost of more geometry.
434
-
435
- #### `transitionSurface()` — Create a solid transition surface between two edges by sweeping a profile along a Hermite transition curve.
436
-
437
- This produces a watertight solid that smoothly connects the two edges. Works with both Manifold and OCCT backends.
438
-
439
- ```js
440
- // Circular tube connecting two edges
441
- const tube = transitionSurface(
442
- { point: [0, 0, 0], tangent: [1, 0, 0] },
443
- { point: [10, 5, 3], tangent: [0, 1, 0] },
444
- { radius: 0.5 },
445
- );
446
- ```
447
-
448
- // Custom profile with weights const custom = transitionSurface( { point: [0, 0, 0], tangent: [1, 0, 0] }, { point: [10, 5, 3], tangent: [0, 1, 0] }, { profile: mySketch, weightA: 1.5, weightB: 0.8 }, );
449
-
450
- ```
451
-
452
- ```ts
453
- transitionSurface(edgeA: TransitionEdge, edgeB: TransitionEdge, options?: TransitionSurfaceOptions): Shape
454
- ```
455
-
456
-
457
- **`TransitionSurfaceOptions`** extends TransitionCurveOptions
458
-
459
- | Option | Type | Description |
460
- |--------|------|-------------|
461
- | `profile?` | `Sketch` | Cross-section profile to sweep along the transition curve. If omitted, a circular profile with `radius` is used. |
462
- | `radius?` | `number` | Radius of circular cross-section (used when `profile` is omitted). Default: 5% of chord length. |
463
- | `rectangleSection?` | `{ width: number; height: number; }` | Width and height for rectangular cross-section. Alternative to `radius` when `profile` is omitted. |
464
- | `up?` | `Vec3` | Preferred up vector for the sweep frame. Default: auto-detected. |
465
- | `edgeLength?` | `number` | Edge length for level-set meshing. Smaller = finer. |
466
- | `boundsPadding?` | `number` | Extra bounds padding for level-set meshing. |
467
-
468
- #### `connectEdges()` — Create a transition surface or solid bridge between two edge segments.
469
-
470
- Tangents can be inferred from neighboring geometry or supplied explicitly through `options`. This is useful for loft-like blends where you want a direct connection between two edge spans.
471
-
472
- ```ts
473
- connectEdges(edgeA: EdgeSegment, edgeB: EdgeSegment, options?: ConnectEdgesOptions): Shape
474
- ```
475
-
476
- **`EdgeSegment`**
477
-
478
- | Option | Type | Description |
479
- |--------|------|-------------|
480
- | `index` | `number` | Stable index within the extraction (deterministic for a given mesh). |
481
- | `direction` | `Vec3` | Normalized direction from start → end. |
482
- | `dihedralAngle` | `number` | Dihedral angle in degrees (0 = coplanar, 180 = knife edge). |
483
- | `convex` | `boolean` | true = outside corner (convex), false = inside corner (concave). |
484
- | `normalA` | `Vec3` | Normal of first adjacent face. |
485
- | `normalB` | `Vec3` | Normal of second adjacent face (same as normalA for boundary edges). |
486
- | `boundary` | `boolean` | true if this is a boundary (unmatched) edge — unusual for closed solids. |
487
- | `start`, `end`, `midpoint`, `length` | | — |
488
-
489
-
490
- **`ConnectEdgesOptions`** extends TransitionSurfaceOptions
491
-
492
- | Option | Type | Description |
493
- |--------|------|-------------|
494
- | `endA?` | `EdgeEnd` | Which end of edge A to connect. Default: 'start'. |
495
- | `endB?` | `EdgeEnd` | Which end of edge B to connect. Default: 'start'. |
496
- | `tangentModeA?` | `TangentMode` | Tangent mode for edge A. Default: 'along'. |
497
- | `tangentModeB?` | `TangentMode` | Tangent mode for edge B. Default: 'along'. |
498
- | `tangentA?` | `Vec3` | Explicit tangent for edge A. |
499
- | `tangentB?` | `Vec3` | Explicit tangent for edge B. |
500
- | `flipA?` | `boolean` | Flip tangent A. |
501
- | `flipB?` | `boolean` | Flip tangent B. |
502
-
503
480
  ### Surface Members
504
481
 
505
482
  #### `surfaceBand()`
@@ -579,47 +556,76 @@ tangentAt(t: number): Vec3
579
556
  length(samples?: number): number
580
557
  ```
581
558
 
582
- ### `HelixCurve`
559
+ ### `Route3D`
583
560
 
584
- Metadata-bearing helical curve around the Z axis. Use `Helix.path(...)` for sampling, placement, or `sweep()`, and `Helix.coil(...)` for helix-oriented solids.
561
+ Metadata-bearing analytic 3D route made from line and arc segments.
585
562
 
586
- **Properties:**
563
+ Use `Curve.Route.fromPolyline()` when you know the virtual design skeleton points and bend radius. ForgeCAD computes tangent trim points, bend arcs, total length, and named start/end port frames. Pass the route directly to `sweep()`.
587
564
 
588
- | Property | Type | Description |
589
- |----------|------|-------------|
590
- | `radius` | `number` | |
591
- | `pitch` | `number` | |
592
- | `turns` | `number` | — |
593
- | `height` | `number` | — |
594
- | `startAngle` | `number` | — |
595
- | `clockwise` | `boolean` | — |
565
+ ```js
566
+ const route = Curve.Route.fromPolyline(
567
+ [[0, 0, 0], [0, 0, 80], [60, 0, 80]],
568
+ { cornerRadius: 24, startPort: "inlet", endPort: "outlet" },
569
+ );
570
+ const pipe = sweep(difference2d(circle2d(8), circle2d(6)), route);
571
+ const outlet = route.port("outlet");
572
+ ```
596
573
 
597
- **Methods:**
574
+ #### `fromPolyline()` — Build a line/arc route from virtual polyline corner points.
598
575
 
599
- #### `pointAt()`
576
+ ```ts
577
+ static fromPolyline(points: Route3DVec3[], options?: Route3DFromPolylineOptions): Route3D
578
+ ```
579
+
580
+ **`Route3DFromPolylineOptions`**
581
+
582
+ | Option | Type | Description |
583
+ |--------|------|-------------|
584
+ | `cornerRadius?` | `number` | Bend radius applied to every virtual interior corner. Default 0 keeps sharp polyline corners. |
585
+ | `startPort?` | `string` | Name for the start port. Default "start". |
586
+ | `endPort?` | `string` | Name for the end port. Default "end". |
587
+ | `up?` | `Vec3` | Preferred up vector for deterministic port frames. Default [0, 0, 1]. |
588
+
589
+ #### `length()` — Total centerline length, including line and bend arc segments.
600
590
 
601
591
  ```ts
602
- pointAt(t: number): Vec3
592
+ get length(): number
603
593
  ```
604
594
 
605
- #### `tangentAt()`
595
+ #### `segments()` — Exact line and arc segments that make up this route.
606
596
 
607
597
  ```ts
608
- tangentAt(t: number): Vec3
598
+ get segments(): Route3DSegment[]
609
599
  ```
610
600
 
611
- #### `sample()`
601
+ #### `ports()` — Named port frames, keyed by port name.
612
602
 
613
603
  ```ts
614
- sample(count?: number): Vec3[]
604
+ get ports(): Record<string, RoutePortFrame>
615
605
  ```
616
606
 
617
- #### `length()`
607
+ #### `port()` — Return one named route port frame.
618
608
 
619
609
  ```ts
620
- length(): number
610
+ port(name: string): RoutePortFrame
621
611
  ```
622
612
 
613
+ #### `toSweepPathPlan()` — Convert this route to the compile plan consumed by sweep().
614
+
615
+ ```ts
616
+ toSweepPathPlan(): SweepPathCompilePlan
617
+ ```
618
+
619
+ #### `toPolyline()` — Sample this analytic route as a polyline for inspection or backend lowering.
620
+
621
+ ```ts
622
+ toPolyline(options?: number | Route3DToPolylineOptions): Route3DVec3[]
623
+ ```
624
+
625
+ **`Route3DToPolylineOptions`**
626
+ - `samples?: number` — Approximate target point count for the full route.
627
+ - `maxAngleDeg?: number` — Maximum angular spacing on arc segments. Default 6 degrees.
628
+
623
629
  ### `NurbsCurve3D`
624
630
 
625
631
  **Properties:**
@@ -993,120 +999,6 @@ toPolyline(): [ number, number ][]
993
999
  closeOffset(delta: number, join?: "Round" | "Square" | "Miter"): Sketch
994
1000
  ```
995
1001
 
996
- ### `HermiteCurve3D`
997
-
998
- **Properties:**
999
-
1000
- | Property | Type | Description |
1001
- |----------|------|-------------|
1002
- | `p0` | `Vec3` | Start position |
1003
- | `p1` | `Vec3` | End position |
1004
- | `t0` | `Vec3` | Scaled tangent at start (direction * weight * chordLength) |
1005
- | `t1` | `Vec3` | Scaled tangent at end (direction * weight * chordLength) |
1006
- | `chordLength` | `number` | Chord length (straight-line distance between endpoints) |
1007
-
1008
- **Methods:**
1009
-
1010
- #### `pointAt()` — Evaluate position at parameter t ∈ [0, 1]
1011
-
1012
- ```ts
1013
- pointAt(t: number): Vec3
1014
- ```
1015
-
1016
- #### `tangentAt()` — Evaluate tangent (first derivative) at parameter t ∈ [0, 1]
1017
-
1018
- ```ts
1019
- tangentAt(t: number): Vec3
1020
- ```
1021
-
1022
- #### `curvatureAt()` — Evaluate curvature vector (second derivative) at parameter t ∈ [0, 1]
1023
-
1024
- ```ts
1025
- curvatureAt(t: number): Vec3
1026
- ```
1027
-
1028
- #### `sample()` — Sample the curve as a polyline of evenly-spaced parameter values.
1029
-
1030
- ```ts
1031
- sample(count?: number): Vec3[]
1032
- ```
1033
-
1034
- #### `length()` — Approximate arc length by sampling.
1035
-
1036
- ```ts
1037
- length(samples?: number): number
1038
- ```
1039
-
1040
- #### `sampleAdaptive()` — Sample with adaptive density — more points where curvature is higher. Returns at least `minCount` points, up to `maxCount`.
1041
-
1042
- ```ts
1043
- sampleAdaptive(minCount?: number, maxCount?: number): Vec3[]
1044
- ```
1045
-
1046
- #### `toPolyline()` — Convert to a format compatible with sweep() path input.
1047
-
1048
- ```ts
1049
- toPolyline(samples?: number): Vec3[]
1050
- ```
1051
-
1052
- ### `QuinticHermiteCurve3D`
1053
-
1054
- **Properties:**
1055
-
1056
- | Property | Type | Description |
1057
- |----------|------|-------------|
1058
- | `p0` | `Vec3` | Start position |
1059
- | `p1` | `Vec3` | End position |
1060
- | `t0` | `Vec3` | Scaled tangent at start (direction * weight * chordLength) |
1061
- | `t1` | `Vec3` | Scaled tangent at end (direction * weight * chordLength) |
1062
- | `c0` | `Vec3` | Scaled second derivative at start (curvature * weight² * chordLength²) |
1063
- | `c1` | `Vec3` | Scaled second derivative at end (curvature * weight² * chordLength²) |
1064
- | `chordLength` | `number` | Chord length (straight-line distance between endpoints) |
1065
-
1066
- **Methods:**
1067
-
1068
- #### `pointAt()` — Evaluate position at parameter t ∈ [0, 1]
1069
-
1070
- ```ts
1071
- pointAt(t: number): Vec3
1072
- ```
1073
-
1074
- #### `tangentAt()` — Evaluate tangent (first derivative, normalized) at parameter t ∈ [0, 1]
1075
-
1076
- ```ts
1077
- tangentAt(t: number): Vec3
1078
- ```
1079
-
1080
- #### `curvatureAt()` — Evaluate curvature vector (second derivative) at parameter t ∈ [0, 1]
1081
-
1082
- ```ts
1083
- curvatureAt(t: number): Vec3
1084
- ```
1085
-
1086
- #### `sample()` — Sample the curve as a polyline of evenly-spaced parameter values.
1087
-
1088
- ```ts
1089
- sample(count?: number): Vec3[]
1090
- ```
1091
-
1092
- #### `length()` — Approximate arc length by sampling.
1093
-
1094
- ```ts
1095
- length(samples?: number): number
1096
- ```
1097
-
1098
- #### `sampleAdaptive()` — Sample with adaptive density — more points where curvature is higher. Returns at least `minCount` points, up to `maxCount`.
1099
-
1100
- ```ts
1101
- sampleAdaptive(minCount?: number, maxCount?: number): Vec3[]
1102
- ```
1103
-
1104
- #### `toPolyline()` — Convert to a format compatible with sweep() path input.
1105
-
1106
- ```ts
1107
- toPolyline(samples?: number): Vec3[]
1108
- ```
1109
-
1110
1002
  ### `ProductSkin`
1111
1003
 
1112
1004
  **Properties:**
@@ -1863,6 +1755,8 @@ bottom(options?: { angle?: number; offset?: number; }): SurfaceAnchor<CylinderSu
1863
1755
  pointAt(coordinate: CylinderSurfaceCoordinate): Vec3
1864
1756
  ```
1865
1757
 
1758
+ `CylinderSurfaceCoordinate`: `{ kind?: "cylinder", angle: number, z: number, offset?: number }`
1759
+
1866
1760
  #### `mirrorPoint()`
1867
1761
 
1868
1762
  ```ts
@@ -1982,6 +1876,8 @@ bottom(options?: { x?: number; offset?: number; }): SurfaceAnchor<PlaneSurfaceCo
1982
1876
  pointAt(coordinate: PlaneSurfaceCoordinate): Vec3
1983
1877
  ```
1984
1878
 
1879
+ `PlaneSurfaceCoordinate`: `{ kind?: "plane", x: number, y: number, offset?: number }`
1880
+
1985
1881
  #### `mirrorPoint()`
1986
1882
 
1987
1883
  ```ts
@@ -2266,6 +2162,8 @@ boundaries(samples?: number): SurfaceBandBoundarySample[]
2266
2162
  withHole(name: string, input: SurfaceBandHoleInput): SurfaceBand<C>
2267
2163
  ```
2268
2164
 
2165
+ `SurfaceBandHoleInput`: `{ length: number, width: number, along?: number, across?: number }`
2166
+
2269
2167
  #### `holes()` — Resolve recorded hole regions into member-local across/along loops.
2270
2168
 
2271
2169
  ```ts
@@ -2288,6 +2186,8 @@ holes(): SurfaceBandHoleRegion[]
2288
2186
  carrier(carrier: CarrierSurface): this
2289
2187
  ```
2290
2188
 
2189
+ `CarrierSurface`: `{ name: string, kind: SurfaceCarrierKind }`
2190
+
2291
2191
  #### `member()`
2292
2192
 
2293
2193
  ```ts
@@ -2332,6 +2232,8 @@ band(): this
2332
2232
  at(anchor: SurfaceAnchor<C>): this
2333
2233
  ```
2334
2234
 
2235
+ `SurfaceAnchor`: `{ carrier: CarrierSurface<C>, coordinate: C }`
2236
+
2335
2237
  #### `size()`
2336
2238
 
2337
2239
  ```ts
@@ -2350,6 +2252,10 @@ path(path: SurfacePath<C> | SurfacePathBuilder<C>): this
2350
2252
  section(section: MemberSectionInput): this
2351
2253
  ```
2352
2254
 
2255
+ **`MemberSectionInput`**: `width?: number`, `thickness: number`, `edgeRadius?: number`, `direction?: MemberOutwardDirection`, `material?: ProductMaterial`, `stations?: MemberSectionStation[]`
2256
+
2257
+ `MemberSectionStation`: `{ t: number, width?: number, thickness?: number }`
2258
+
2353
2259
  #### `cap()`
2354
2260
 
2355
2261
  ```ts
@@ -2362,6 +2268,8 @@ cap(style: SurfaceBandCap): this
2362
2268
  slot(name: string, feature: MemberFeature | RoundedSlotBuilder): this
2363
2269
  ```
2364
2270
 
2271
+ **`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`
2272
+
2365
2273
  #### `cutout()`
2366
2274
 
2367
2275
  ```ts
@@ -2486,6 +2394,25 @@ toFeature(name?: string): MemberFeature
2486
2394
 
2487
2395
  ## Constants
2488
2396
 
2397
+ ### `Curve`
2398
+
2399
+ Canonical exact/smooth 3D curve constructors.
2400
+
2401
+ `Curve.*` is the public home for reference curves and route centerlines that feed `sweep`, `variableSweep`, route visualization, and future path consumers. Standalone 3D curve constructors have been collapsed into this namespace.
2402
+
2403
+ - `Blend(start: CurveBlendEndpoint, end: CurveBlendEndpoint): NurbsCurve3D` — Create an exact G1 blend curve between two directed endpoints. The returned curve is a cubic non-rational `NurbsCurve3D`: ForgeCAD converts the endpoint positions and tangents into Bezier control points, so the curve can feed `sweep` and exact surface boundaries through the existing `nurbs` IR rather than a sampled polyline.
2404
+ - `BlendG2(start: CurveBlendG2Endpoint, end: CurveBlendG2Endpoint): NurbsCurve3D` — Create an exact G2 blend curve between two directed endpoints. This is the curvature-aware companion to `Curve.Blend()`. It returns a degree-5 non-rational `NurbsCurve3D` that matches endpoint position, tangent direction, and optional curvature/second-derivative vectors.
2405
+ - `Arc(options: CurveArcOptions): NurbsCurve3D` — Create an exact circular 3D arc from start, end, and start tangent. The returned curve is a rational quadratic `NurbsCurve3D`, split into stable spans when needed, so it can feed `sweep` without sampling the authoring intent away.
2406
+ - `Line(start: Vec3, end: Vec3): NurbsCurve3D` — Create an exact straight 3D NURBS line segment.
2407
+ - `Polyline(points: Vec3[]): Vec3[]` — Create a polyline path as cloned 3D points. Polylines are exact as route/path input to `sweep`. Use `Curve.Route` when the centerline needs bend and endpoint-frame metadata.
2408
+ - `Spline(points: Vec3[], options?: Spline3DOptions): Curve3D` — Create a smooth Catmull-Rom spline path. This is a smooth sampled curve object. Use `Curve.Nurbs` when the path must preserve exact control-point and knot data.
2409
+ - `Nurbs(points: Vec3[], options?: NurbsCurve3DOptions): NurbsCurve3D` — Create an exact NURBS 3D curve from control points, weights, knots, and degree.
2410
+ - `Fit(points: Vec3[], options?: CurveFitOptions): NurbsCurve3D` — Fit a non-rational NURBS curve that interpolates every input point. This is global B-spline interpolation, not approximate curve reduction: ForgeCAD computes chord-length parameters, averaged clamped knots, solves the control points, then verifies the interpolation residual against `tolerance`.
2411
+ - `Trim<T extends CurveTrimInput>(curve: T, start: number, end: number): CurveTrimOutput<T>` — Extract an exact curve segment from normalized parameter `start` to `end`. `NurbsCurve3D` inputs are trimmed with exact knot insertion/subdomain extraction. Polyline point arrays are trimmed by arclength over their exact line segments. Sampled `Curve3D` splines are rejected until ForgeCAD has a tolerance-controlled rebuild path.
2412
+ - `Reverse<T extends CurveTrimInput>(curve: T): CurveTrimOutput<T>` — Reverse an exact curve without changing its geometry. `NurbsCurve3D` inputs reverse control points, weights, and knots. Polyline point arrays are cloned and reversed. Sampled `Curve3D` splines are rejected until ForgeCAD has a tolerance-controlled rebuild path.
2413
+ - `Route: typeof Route3D` — Build analytic 3D line/arc routes for sweeps. `Curve.Route.fromPolyline()` is the canonical route API. It returns a `Route3D` value object, preserving exact route segments, named port frames, and the lowerable `route3d` sweep compile plan.
2414
+ - `Helix: { path(options: HelixOptions): CurveHelixPath; coil: CurveHelixCoil; }` — Build helical paths and swept coils. `Curve.Helix` is the canonical namespace for helical paths and coils. It uses the same sweep-based lowering as other curve paths.
2415
+
2489
2416
  ### `Surface`
2490
2417
 
2491
2418
  - `Plane(options: SurfacePlaneOptions): Shape` — Create a finite analytic plane sheet that can be trimmed, sewn, thickened, or used as a low-level face.
@@ -2567,17 +2494,3 @@ toFeature(name?: string): MemberFeature
2567
2494
  ### `Ribs`
2568
2495
 
2569
2496
  - `repeated(input: { count: number; height: number; }): MemberFeature` — Create repeated ribs that belong to a surface member before lowering.
2570
-
2571
- ### `Helix`
2572
-
2573
- Helical curve helpers.
2574
-
2575
- ```ts
2576
- const guide = Helix.path({ radius: 20, pitch: 6, turns: 4 });
2577
- const wire = Helix.coil({ radius: 20, pitch: 6, turns: 4, wireRadius: 1 });
2578
- const rectangular = Helix.coil(rect(2, 1), { radius: 20, height: 30, turns: 5 });
2579
- ```
2580
-
2581
- - `path(options: HelixOptions): HelixCurve` — Create a metadata-bearing helical centerline around the Z axis.
2582
- - `coil(options: HelixCoilOptions): Shape` — Create a solid helical coil by sweeping a circular profile through helix-local frames.
2583
- - `coil(profile: Sketch, options: HelixCoilOptions): Shape` — Create a solid helical coil by sweeping a profile through helix-local frames.