forgecad 0.9.15 → 0.10.0

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 (166) hide show
  1. package/dist/assets/{AdminPage-CDyGUinA.js → AdminPage-DwYHz72L.js} +1 -1
  2. package/dist/assets/{BenchmarkPage-DfPMY_-d.js → BenchmarkPage-a9_f-1US.js} +1 -1
  3. package/dist/assets/{BlogPage-kF0fkdJT.js → BlogPage-DodHpvmf.js} +1 -1
  4. package/dist/assets/{DocsPage-B954L3YN.js → DocsPage-B5LePEuj.js} +8 -858
  5. package/dist/assets/{EditorApp-CuDLxKqL.css → EditorApp-BpjZgzk0.css} +148 -0
  6. package/dist/assets/EditorApp-QXsAISLR.js +16307 -0
  7. package/dist/assets/{EmbedViewer-C77B-TrF.js → EmbedViewer-DdEHGUMU.js} +2 -2
  8. package/dist/assets/{LandingPageProofDriven-Cr6fXMDj.js → LandingPageProofDriven-yhhOodbf.js} +2 -2
  9. package/dist/assets/{LegalPage-Dzklqmmg.js → LegalPage-5RbKRGYK.js} +1 -1
  10. package/dist/assets/{PricingPage-zWXkvlwl.js → PricingPage-E3Rma7aV.js} +1 -1
  11. package/dist/assets/{SettingsPage-Bz0of4KQ.js → SettingsPage-BJZcM97j.js} +1 -1
  12. package/dist/assets/{app-D3kDkggg.js → app-DSYrDg0V.js} +1846 -352
  13. package/dist/assets/cli/{render-DSY3mMQa.js → render-ZMHR9HkV.js} +161 -70
  14. package/dist/assets/{constructionHistoryWorker-gpDo-uH2.js → constructionHistoryWorker-AwMMWSxg.js} +1104 -349
  15. package/dist/assets/{evalWorker-CU0Ke6DP.js → evalWorker-DbNs7Dkp.js} +5155 -3772
  16. package/dist/assets/{inspectWorker-COyp8XXA.js → inspectWorker-CZsCFtQT.js} +1415 -439
  17. package/dist/assets/{targets-B9sGB5nB.js → jointPose-DO6mnXn_.js} +71 -3
  18. package/dist/assets/{manifold-DNkrUWpA.js → manifold-BGlQBBH9.js} +1 -1
  19. package/dist/assets/{manifold-BRI5prcH.js → manifold-BU-tJwQh.js} +1 -1
  20. package/dist/assets/{manifold-C-3h2M7p.js → manifold-fy2MV7K1.js} +2 -2
  21. package/dist/assets/{reportWorker-CdBz5bNg.js → reportWorker-DO6hcQbh.js} +8474 -4549
  22. package/dist/assets/{scalar-sampling-budget-wJF98aY9.js → scalar-sampling-budget-o90NSNmF.js} +5347 -3906
  23. package/dist/assets/{scanProxyWorker-B-9VbLIs.js → scanProxyWorker-2GtDLk-R.js} +19 -6
  24. package/dist/assets/{javascript-1kQXfVaz.js → typescript-DBQ6RN5l.js} +874 -22
  25. package/dist/cli/render.html +1 -1
  26. package/dist/docs/index.html +3 -3
  27. package/dist/docs-raw/AI/usage.md +3 -1
  28. package/dist/docs-raw/CLI.md +65 -239
  29. package/dist/docs-raw/README.md +6 -0
  30. package/dist/docs-raw/component-model.md +17 -150
  31. package/dist/docs-raw/generated/assembly.md +159 -520
  32. package/dist/docs-raw/generated/concepts.md +245 -3491
  33. package/dist/docs-raw/generated/core.md +277 -1251
  34. package/dist/docs-raw/generated/curves.md +387 -1608
  35. package/dist/docs-raw/generated/legacy.md +162 -0
  36. package/dist/docs-raw/generated/lib.md +238 -112
  37. package/dist/docs-raw/generated/output.md +51 -76
  38. package/dist/docs-raw/generated/runtime-names.md +30 -22
  39. package/dist/docs-raw/generated/sdf.md +68 -284
  40. package/dist/docs-raw/generated/sheet-metal.md +68 -335
  41. package/dist/docs-raw/generated/sketch.md +240 -1161
  42. package/dist/docs-raw/generated/viewport.md +75 -316
  43. package/dist/docs-raw/generated/wood.md +21 -49
  44. package/dist/docs-raw/guides/coordinate-system.md +4 -42
  45. package/dist/docs-raw/guides/inspection-bundles.md +44 -442
  46. package/dist/docs-raw/guides/joint-design.md +18 -79
  47. package/dist/docs-raw/guides/positioning.md +21 -143
  48. package/dist/docs-raw/guides/scene-presentation.md +89 -0
  49. package/dist/docs-raw/skills/forgecad-3d-reconstruction.md +25 -111
  50. package/dist/docs-raw/skills/forgecad-blockout-model.md +20 -117
  51. package/dist/docs-raw/skills/forgecad-component-model.md +23 -107
  52. package/dist/docs-raw/skills/forgecad-high-level-spec.md +47 -155
  53. package/dist/docs-raw/skills/forgecad-image-replicator.md +26 -143
  54. package/dist/docs-raw/skills/forgecad-lld.md +19 -113
  55. package/dist/docs-raw/skills/forgecad-make-a-model.md +113 -532
  56. package/dist/docs-raw/skills/forgecad-model-grader.md +38 -108
  57. package/dist/docs-raw/skills/forgecad-prepare-prompt.md +24 -211
  58. package/dist/docs-raw/skills/forgecad-project.md +13 -129
  59. package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +42 -134
  60. package/dist/docs-raw/skills/forgecad-render-inspect.md +27 -174
  61. package/dist/docs-raw/skills/forgecad-visual-spec.md +32 -112
  62. package/dist/docs-raw/skills/forgecad.md +19 -18
  63. package/dist/docs-raw/skills/index.md +2 -0
  64. package/dist/docs-raw/welcome.md +4 -2
  65. package/dist/index.html +1 -1
  66. package/dist/llms.txt +1 -2
  67. package/dist/sitemap.xml +13 -13
  68. package/dist-cli/{check-compiler-SDX5QIXI.js → check-compiler-JTVBITCR.js} +1 -1
  69. package/dist-cli/{check-query-propagation-EAYEFT77.js → check-query-propagation-3FFLSMVN.js} +1 -1
  70. package/dist-cli/{chunk-N4O47JLF.js → chunk-OAN5T4XD.js} +5722 -4287
  71. package/dist-cli/forgecad.js +2195 -656
  72. package/dist-skill/CONTEXT.md +1778 -7912
  73. package/dist-skill/SKILL.md +15 -15
  74. package/dist-skill/docs/API/core/concepts.md +27 -157
  75. package/dist-skill/docs/CLI.md +65 -239
  76. package/dist-skill/docs/generated/assembly.md +160 -493
  77. package/dist-skill/docs/generated/core.md +277 -1251
  78. package/dist-skill/docs/generated/curves.md +387 -1609
  79. package/dist-skill/docs/generated/lib.md +238 -112
  80. package/dist-skill/docs/generated/output.md +51 -76
  81. package/dist-skill/docs/generated/runtime-names.md +16 -22
  82. package/dist-skill/docs/generated/sdf.md +68 -284
  83. package/dist-skill/docs/generated/sheet-metal.md +68 -335
  84. package/dist-skill/docs/generated/sketch.md +240 -1160
  85. package/dist-skill/docs/generated/viewport.md +75 -223
  86. package/dist-skill/docs/generated/wood.md +21 -49
  87. package/dist-skill/docs/guides/coordinate-system.md +4 -42
  88. package/dist-skill/docs/guides/inspection-bundles.md +44 -442
  89. package/dist-skill/docs/guides/joint-design.md +18 -79
  90. package/dist-skill/docs/guides/positioning.md +21 -143
  91. package/dist-skill/docs/guides/scene-presentation.md +89 -0
  92. package/dist-skill/docs/guides/surface-members.md +26 -0
  93. package/dist-skill/library/forgecad-3d-reconstruction/SKILL.md +23 -111
  94. package/dist-skill/library/forgecad-blockout-model/SKILL.md +18 -117
  95. package/dist-skill/library/forgecad-component-model/SKILL.md +21 -107
  96. package/dist-skill/library/forgecad-high-level-spec/SKILL.md +45 -155
  97. package/dist-skill/library/forgecad-image-replicator/SKILL.md +24 -143
  98. package/dist-skill/library/forgecad-lld/SKILL.md +17 -113
  99. package/dist-skill/library/forgecad-make-a-model/SKILL.md +111 -532
  100. package/dist-skill/library/forgecad-model-grader/SKILL.md +36 -108
  101. package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +35 -224
  102. package/dist-skill/library/forgecad-prepare-prompt/references/default-profiles.md +43 -271
  103. package/dist-skill/library/forgecad-prepare-prompt/references/master-prompt.md +30 -99
  104. package/dist-skill/library/forgecad-project/SKILL.md +13 -131
  105. package/dist-skill/library/forgecad-reconstruction-benchmark/SKILL.md +29 -123
  106. package/dist-skill/library/forgecad-render-inspect/SKILL.md +25 -174
  107. package/dist-skill/library/forgecad-visual-spec/SKILL.md +30 -111
  108. package/dist-skill/website/skills/forgecad-3d-reconstruction.md +58 -0
  109. package/dist-skill/website/skills/forgecad-blockout-model.md +49 -0
  110. package/dist-skill/website/skills/forgecad-component-model.md +53 -0
  111. package/dist-skill/website/skills/forgecad-high-level-spec.md +101 -0
  112. package/dist-skill/website/skills/forgecad-image-replicator.md +63 -0
  113. package/dist-skill/website/skills/forgecad-lld.md +41 -0
  114. package/dist-skill/website/skills/forgecad-make-a-model.md +186 -0
  115. package/dist-skill/website/skills/forgecad-model-grader.md +82 -0
  116. package/dist-skill/website/skills/forgecad-prepare-prompt.md +63 -0
  117. package/dist-skill/website/skills/forgecad-project.md +26 -0
  118. package/dist-skill/website/skills/forgecad-reconstruction-benchmark.md +60 -0
  119. package/dist-skill/website/skills/forgecad-render-inspect.md +80 -0
  120. package/dist-skill/website/skills/forgecad-visual-spec.md +71 -0
  121. package/dist-skill/website/skills/forgecad.md +122 -0
  122. package/dist-skill/website/skills/index.md +26 -0
  123. package/examples/api/comparison-imported-sphere-candidate.forge.js +1 -1
  124. package/examples/api/conformal-product-ribbon.forge.js +1 -1
  125. package/examples/api/exact-sheet-shell-assembly.forge.js +1 -1
  126. package/examples/api/extrude-options.forge.js +4 -2
  127. package/examples/api/field-loft-drive-tip.forge.js +40 -0
  128. package/examples/api/guided-loft-olive-oil-bottle.forge.js +1 -1
  129. package/examples/api/helix-basics.forge.js +2 -2
  130. package/examples/api/highlight-debug.forge.js +10 -10
  131. package/examples/api/mesh-import-slats.forge.js +1 -1
  132. package/examples/api/real-product-curves.forge.js +1 -1
  133. package/examples/api/route3d-elbow.forge.js +3 -0
  134. package/examples/api/sculpt-box-circle-booleans.forge.js +1 -1
  135. package/examples/api/sdf-shapes.forge.js +2 -5
  136. package/examples/api/sketch-rounding-strategies.forge.js +6 -6
  137. package/examples/api/surface-member-bottle-cage.forge.js +3 -3
  138. package/examples/api/surface-member-conformal-product-ribbon.forge.js +3 -3
  139. package/examples/api/surface-member-razor-inlay.forge.js +1 -1
  140. package/examples/api/variable-sweep-test.forge.js +4 -2
  141. package/examples/mechanical/airplane-propeller.forge.js +74 -39
  142. package/examples/nurbs-surface.forge.js +1 -1
  143. package/examples/products/iphone.forge.js +1 -1
  144. package/package.json +4 -1
  145. package/dist/assets/EditorApp-Beb-IZ0y.js +0 -14014
  146. package/dist/docs-raw/guides/geometry-conventions.md +0 -52
  147. package/dist/docs-raw/guides/modeling-recipes.md +0 -78
  148. package/dist-skill/docs/guides/geometry-conventions.md +0 -52
  149. package/dist-skill/docs/guides/modeling-recipes.md +0 -78
  150. package/dist-skill/library/forgecad-visual-spec/references/prompt-template.md +0 -79
  151. package/examples/api/bolted-service-cover.forge.js +0 -17
  152. package/examples/api/cable-gland-anchor.forge.js +0 -14
  153. package/examples/api/captured-cartridge-guide.forge.js +0 -14
  154. package/examples/api/captured-linear-slide.forge.js +0 -13
  155. package/examples/api/clevis-pin-joint.forge.js +0 -13
  156. package/examples/api/datum-enclosure.forge.js +0 -16
  157. package/examples/api/hose-barb-port.forge.js +0 -14
  158. package/examples/api/knuckled-hinge-assembly.forge.js +0 -15
  159. package/examples/api/living-hinge-cover.forge.js +0 -14
  160. package/examples/api/pcb-terminal-block.forge.js +0 -22
  161. package/examples/api/pinned-lever-pivot-stack.forge.js +0 -14
  162. package/examples/api/retained-shaft-knob-stack.forge.js +0 -15
  163. package/examples/api/routed-tube-clip.forge.js +0 -15
  164. package/examples/api/seated-bearing-stack.forge.js +0 -30
  165. package/examples/api/snap-latch-cover.forge.js +0 -14
  166. package/examples/api/thumb-screw-clamp.forge.js +0 -15
@@ -1,23 +1,23 @@
1
1
  # API by Concept
2
2
 
3
- Every public API function belongs to one of 16 fundamental concepts. This document groups them by concept rather than by module, making it easy to see the full set of operations for each idea.
3
+ Every public API function belongs to one of 16 fundamental concepts. This is an index grouped by concept rather than by module — full signatures, behavioral details, and examples live in the per-module reference each entry links to.
4
4
 
5
5
  ## Concepts
6
6
 
7
- - **[C1: Primitive Construction](#c1-primitive-construction)** — Create geometry from parameters — no input geometry required. *(76 functions)*
7
+ - **[C1: Primitive Construction](#c1-primitive-construction)** — Create geometry from parameters — no input geometry required. *(66 functions)*
8
8
  - **[C2: Boolean Combination](#c2-boolean-combination)** — Combine same-dimension geometry using CSG set operations. *(6 functions)*
9
- - **[C3: Rigid Transform](#c3-rigid-transform)** — Reposition or reorient geometry without changing its shape. *(3 functions)*
10
- - **[C4: Dimensional Promotion](#c4-dimensional-promotion)** — Convert a 2D profile into a 3D solid (extrude, revolve, loft, sweep). *(52 functions)*
9
+ - **[C3: Rigid Transform](#c3-rigid-transform)** — Reposition or reorient geometry without changing its shape. *(0 functions)*
10
+ - **[C4: Dimensional Promotion](#c4-dimensional-promotion)** — Convert a 2D profile into a 3D solid (extrude, revolve, loft, sweep). *(47 functions)*
11
11
  - **[C5: Topology Query](#c5-topology-query)** — Select or inspect named faces and edges on a shape. *(3 functions)*
12
- - **[C6: Edge Feature](#c6-edge-feature)** — Modify edges of a solid — fillets, chamfers, draft, offset. *(7 functions)*
12
+ - **[C6: Edge Feature](#c6-edge-feature)** — Modify edges of a solid — fillets, chamfers, draft, offset. *(4 functions)*
13
13
  - **[C7: Pattern Replication](#c7-pattern-replication)** — Duplicate geometry in regular arrangements (linear, circular, mirror). *(6 functions)*
14
- - **[C8: Constraint Solving](#c8-constraint-solving)** — Define geometry by relationships and let a solver find positions. *(17 functions)*
14
+ - **[C8: Constraint Solving](#c8-constraint-solving)** — Define geometry by relationships and let a solver find positions. *(1 functions)*
15
15
  - **[C9: Spatial Placement](#c9-spatial-placement)** — Position geometry relative to other geometry using semantic anchors. *(6 functions)*
16
- - **[C10: Assembly & Kinematics](#c10-assembly-kinematics)** — Compose parts with joints for kinematic simulation. *(3 functions)*
17
- - **[C11: Parameterization & UI](#c11-parameterization-ui)** — Declare user-facing controls that drive model geometry. *(7 functions)*
16
+ - **[C10: Assembly & Kinematics](#c10-assembly-kinematics)** — Compose parts with joints for kinematic simulation. *(1 functions)*
17
+ - **[C11: Parameterization & UI](#c11-parameterization-ui)** — Declare user-facing controls that drive model geometry. *(6 functions)*
18
18
  - **[C12: Dimensional Demotion](#c12-dimensional-demotion)** — Extract 2D geometry from a 3D solid (section, projection). *(3 functions)*
19
- - **[C13: Export & Output](#c13-export-output)** — Convert geometry to external formats (STL, 3MF, SVG, DXF, G-code, PDF). *(5 functions)*
20
- - **[C14: Visual & Debugging](#c14-visual-debugging)** — Control viewport appearance and debugging aids. *(36 functions)*
19
+ - **[C13: Export & Output](#c13-export-output)** — Convert geometry to external formats (STL, 3MF, SVG, DXF, G-code, PDF). *(15 functions)*
20
+ - **[C14: Visual & Debugging](#c14-visual-debugging)** — Control viewport appearance and debugging aids. *(35 functions)*
21
21
  - **[C15: Import & Composition](#c15-import-composition)** — Bring external geometry or other ForgeCAD modules into the current script. *(1 functions)*
22
22
  - **[C16: Part Library](#c16-part-library)** — Pre-built parametric parts accessible via `lib.*`. *(4 functions)*
23
23
 
@@ -27,2613 +27,223 @@ Every public API function belongs to one of 16 fundamental concepts. This docume
27
27
 
28
28
  Create geometry from parameters — no input geometry required.
29
29
 
30
- #### `sdf.sphere()` — Create an SDF sphere centered at the origin.
30
+ - `sdf.sphere(radius)` — Create an SDF sphere centered at the origin. → [sdf](/docs/sdf#sdf)
31
+ - `sdf.box(x, y, z)` — Create an SDF box centered at the origin with given full dimensions (not half-extents). → [sdf](/docs/sdf#sdf)
32
+ - `sdf.cylinder(height, radius)` — Create an SDF cylinder centered at the origin, axis along Z. → [sdf](/docs/sdf#sdf)
33
+ - `sdf.torus(majorRadius, minorRadius)` — Create an SDF torus centered at the origin, lying in the XY plane. → [sdf](/docs/sdf#sdf)
34
+ - `sdf.capsule(height, radius)` — Create an SDF capsule centered at the origin, axis along Z. → [sdf](/docs/sdf#sdf)
35
+ - `sdf.cone(height, radius)` — Create an SDF cone with base at z=0 and tip at z=height. → [sdf](/docs/sdf#sdf)
36
+ - `sdf.smoothUnion(a, b, options)` — Smooth union — blends shapes together with a smooth transition radius. → [sdf](/docs/sdf#sdf)
37
+ - `sdf.smoothDifference(a, b, options)` — Smooth difference — smoothly subtracts b from a. → [sdf](/docs/sdf#sdf)
38
+ - `sdf.smoothIntersection(a, b, options)` — Smooth intersection — smoothly intersects a and b. → [sdf](/docs/sdf#sdf)
39
+ - `sdf.blend(a, b, fn, options?)` — Spatially blend between two SDF patterns. → [sdf](/docs/sdf#sdf)
40
+ - `sdf.gyroid(options)` — Gyroid TPMS lattice — the most common lattice for additive manufacturing. → [sdf](/docs/sdf#sdf)
41
+ - `sdf.schwarzP(options)` — Schwarz-P TPMS lattice — isotropic pore structure. → [sdf](/docs/sdf#sdf)
42
+ - `sdf.diamond(options)` — Diamond TPMS lattice — stiffest TPMS structure. → [sdf](/docs/sdf#sdf)
43
+ - `sdf.lidinoid(options)` — Lidinoid TPMS lattice — visually distinct from gyroid, popular in research and art. → [sdf](/docs/sdf#sdf)
44
+ - `sdf.noise(options?)` — 3D Simplex noise field — produces organic, natural-looking displacements. → [sdf](/docs/sdf#sdf)
45
+ - `sdf.voronoi(options?)` — 3D Voronoi pattern — organic cellular structures like bone, coral, or soap bubbles. → [sdf](/docs/sdf#sdf)
46
+ - `sdf.honeycomb(options?)` — Honeycomb (hexagonal) lattice pattern. → [sdf](/docs/sdf#sdf)
47
+ - `sdf.waves(options?)` — Sinusoidal wave ridges — parallel ridges along an axis. → [sdf](/docs/sdf#sdf)
48
+ - `sdf.knurl(options?)` — Knurl pattern — crossed helical grooves for grips and handles. → [sdf](/docs/sdf#sdf)
49
+ - `sdf.perforated(options?)` — Perforated plate pattern — regular array of cylindrical holes. → [sdf](/docs/sdf#sdf)
50
+ - `sdf.scales(options?)` — Fish/dragon scale pattern — overlapping circular scales in hex-packed rows. → [sdf](/docs/sdf#sdf)
51
+ - `sdf.brick(options?)` — Brick/stone wall pattern — running bond with mortar grooves. → [sdf](/docs/sdf#sdf)
52
+ - `sdf.weave(options?)` — Grid lattice pattern — two families of infinite slabs crossing at 90°. → [sdf](/docs/sdf#sdf)
53
+ - `sdf.basketWeave(options?)` — Basket weave surface pattern — threads with over-under crossings in UV space. → [sdf](/docs/sdf#sdf)
54
+ - `sdf.pattern2d()` — Create typed, composable 2D surface patterns for `.surfaceDisplace()`. → [sdf](/docs/sdf#sdf)
55
+ - `sdf.SurfacePattern` — A 2D surface pattern — a heightmap function for use with `.surfaceDisplace()`. → [sdf](/docs/sdf#sdf)
56
+ - `sdf.fromFunction(fn, options)` — Create a custom SDF from one expression; shader-safe expressions raymarch directly. → [sdf](/docs/sdf#sdf)
57
+ - `sdf.combine(value, options?)` — Collapse a plain object/array tree of SDF leaves into one continuous implicit field. → [sdf](/docs/sdf#sdf)
58
+ - `sdf.Sculpt` — Sculpt-like facade: friendly liquid-modeling verbs backed by the same SDF kernel. → [sdf](/docs/sdf#sdf)
59
+ - `Sculpt.sphere(radius)` — Create a liquid SDF sphere centered at the origin. → [sdf](/docs/sdf#sculpt)
60
+ - `Sculpt.box(x, y, z, options?)` — Create a liquid SDF box; pass `{ radius }` for a rounded box. → [sdf](/docs/sdf#sculpt)
61
+ - `Sculpt.cylinder(height, radius)` — Create a liquid SDF cylinder centered at the origin, axis along Z. → [sdf](/docs/sdf#sculpt)
62
+ - `Sculpt.disk(radius, thickness?)` — Create a thin circular disk centered at the origin, axis along Z. → [sdf](/docs/sdf#sculpt)
63
+ - `Sculpt.capsule(height, radius)` — Create a liquid SDF capsule centered at the origin, axis along Z. → [sdf](/docs/sdf#sculpt)
64
+ - `Sculpt.torus(majorRadius, minorRadius)` — Create a liquid SDF torus lying in the XY plane. → [sdf](/docs/sdf#sculpt)
65
+ - `Sculpt.cone(height, radius)` — Create a liquid SDF cone. → [sdf](/docs/sdf#sculpt)
66
+ - `Sculpt.tube(points, options?)` — Create a smooth tube through a list of 3D points. → [sdf](/docs/sdf#sculpt)
67
+ - `Sculpt.curve(points, options?)` — Create a smooth variable-thickness sweep through 3D control points. → [sdf](/docs/sdf#sculpt)
68
+ - `Sculpt.blend(first?, optionsOrShape?, ...rest)` — Smoothly blend one or more SDF shapes into a continuous body. → [sdf](/docs/sdf#sculpt)
69
+ - `Sculpt.union(first?, ...rest)` — Sharply union one or more SDF shapes. → [sdf](/docs/sdf#sculpt)
70
+ - `Sculpt.carve(base, cutters, options?)` — Smoothly subtract one or more cutter shapes from a base shape. → [sdf](/docs/sdf#sculpt)
71
+ - `Sculpt.keep(first?, optionsOrShape?, ...rest)` — Smoothly intersect one or more SDF shapes. → [sdf](/docs/sdf#sculpt)
72
+ - `Sculpt.polish(shape, input?)` — Apply a Sculpt material preset or direct material properties. → [sdf](/docs/sdf#sculpt)
73
+ - `Sculpt.material(input?)` — Resolve a Sculpt material preset to ForgeCAD material properties. → [sdf](/docs/sdf#sculpt)
74
+ - `Sculpt.look(preset?)` — Return a polished scene preset tuned for liquid SDF preview. → [sdf](/docs/sdf#sculpt)
75
+ - `Sculpt.knownMaterials()` — List the built-in Sculpt material preset names. → [sdf](/docs/sdf#sculpt)
76
+ - `toShape(value, options?)` — Materialize one SDF leaf or all SDF leaves in a renderable tree. → [sdf](/docs/sdf#toshape)
77
+ - `circle2d(radius, segments?)` — Create a 2D circle centered at the origin. → [sketch](/docs/sketch#circle2d)
78
+ - `ellipse(rx, ry, segments?)` — Create a 2D ellipse centered at the origin. → [sketch](/docs/sketch#ellipse)
79
+ - `loadFont(source, cacheKey?)` — Pre-load and cache a font for use with `text2d()`. → [sketch](/docs/sketch#loadfont)
80
+ - `ngon(sides, radius)` — Create a regular polygon inscribed in a circle of the given radius. → [sketch](/docs/sketch#ngon)
81
+ - `path()` — Create a new `PathBuilder` for tracing a 2D outline point by point. → [sketch](/docs/sketch#path)
82
+ - `polygon(points)` — Create a 2D polygon from an array of `[x, y]` points or `Point2D` objects. → [sketch](/docs/sketch#polygon)
83
+ - `polygonVertices(sides, radius, options?)` — Compute the vertex positions of a regular polygon. → [core](/docs/core#polygonvertices)
84
+ - `rect(width, height)` — Create a 2D rectangle centered at the origin. → [sketch](/docs/sketch#rect)
85
+ - `arcSlot(pitchRadius, sweepDeg, thickness)` — Create an arc-shaped slot (banana / annular sector) centered at the origin. → [sketch](/docs/sketch#arcslot)
86
+ - `roundedRect(width, height, radius)` — Create a 2D rectangle with rounded corners, centered at the origin. → [sketch](/docs/sketch#roundedrect)
87
+ - `slot(length, width)` — Create a slot (oblong / stadium shape) — a rectangle with semicircular ends, centered at the origin. → [sketch](/docs/sketch#slot)
88
+ - `spline2d(points, options?)` — Build a smooth Catmull-Rom spline sketch from 2D control points. → [curves](/docs/curves#spline2d)
89
+ - `stroke(points, width, join?)` — Thicken a 2D polyline (centerline) into a solid filled profile of uniform width. → [sketch](/docs/sketch#stroke)
90
+ - `text2d(content, options?)` — Build a filled 2D Sketch from a text string. → [sketch](/docs/sketch#text2d)
91
+ - `textWidth(content, options?)` — Measure the rendered advance width of a string without creating any geometry. → [sketch](/docs/sketch#textwidth)
92
+ - `box(width, depth, height)` — Create a rectangular box. → [core](/docs/core#box)
93
+ - `cylinder(height, radius, radiusTop?, segments?)` — Create a cylinder or cone with named faces and edges. → [core](/docs/core#cylinder)
94
+ - `sphere(radius, segments?)` — Create a sphere centered at the origin. → [core](/docs/core#sphere)
95
+ - `torus(majorRadius, minorRadius, segments?)` — Create a torus (donut shape) lying in the XY plane. → [core](/docs/core#torus)
31
96
 
32
- ```ts
33
- sdf.sphere(radius: number): SdfShape
34
- ```
35
-
36
- #### `sdf.box()` — Create an SDF box centered at the origin with given full dimensions (not half-extents).
37
-
38
- ```ts
39
- sdf.box(x: number, y: number, z: number): SdfShape
40
- ```
41
-
42
- #### `sdf.cylinder()` — Create an SDF cylinder centered at the origin, axis along Z.
43
-
44
- ```ts
45
- sdf.cylinder(height: number, radius: number): SdfShape
46
- ```
47
-
48
- #### `sdf.torus()` — Create an SDF torus centered at the origin, lying in the XY plane.
49
-
50
- ```ts
51
- sdf.torus(majorRadius: number, minorRadius: number): SdfShape
52
- ```
53
-
54
- #### `sdf.capsule()` — Create an SDF capsule centered at the origin, axis along Z.
55
-
56
- ```ts
57
- sdf.capsule(height: number, radius: number): SdfShape
58
- ```
59
-
60
- #### `sdf.cone()` — Create an SDF cone with base at z=0 and tip at z=height.
61
-
62
- ```ts
63
- sdf.cone(height: number, radius: number): SdfShape
64
- ```
65
-
66
- #### `sdf.smoothUnion()` — Smooth union — blends shapes together with a smooth transition radius.
67
-
68
- ```ts
69
- sdf.smoothUnion(a: SdfShape, b: SdfShape, options: { radius: number; }): SdfShape
70
- ```
71
-
72
- #### `sdf.smoothDifference()` — Smooth difference — smoothly subtracts b from a.
73
-
74
- ```ts
75
- sdf.smoothDifference(a: SdfShape, b: SdfShape, options: { radius: number; }): SdfShape
76
- ```
77
-
78
- #### `sdf.smoothIntersection()` — Smooth intersection — smoothly intersects a and b.
79
-
80
- ```ts
81
- sdf.smoothIntersection(a: SdfShape, b: SdfShape, options: { radius: number; }): SdfShape
82
- ```
83
-
84
- #### `sdf.morph()` — Morph between two SDF shapes. t=0 → a, t=1 → b.
85
-
86
- ```ts
87
- sdf.morph(a: SdfShape, b: SdfShape, t: number): SdfShape
88
- ```
89
-
90
- #### `sdf.blend()` — Spatially blend between two SDF patterns. The blend function receives (x, y, z) and returns 0..1: 0 = fully pattern `a`, 1 = fully pattern `b`.
91
-
92
- ```ts
93
- sdf.blend(a: SdfShape, b: SdfShape, fn: (x: number, y: number, z: number) => number, options?: BlendOptions): SdfShape
94
- ```
95
-
96
- **`BlendOptions`**
97
- - `constants?: Record<string, number>` — Optional named constants accessible in the blend function.
98
-
99
- #### `sdf.gyroid()` — Gyroid TPMS lattice — the most common lattice for additive manufacturing.
100
-
101
- ```ts
102
- sdf.gyroid(options: TpmsOptions): SdfShape
103
- ```
104
-
105
- **`TpmsOptions`**
106
- - `thickness?: number` — Dimensionless field threshold kept for compatibility. Prefer `wallThickness` for approximate millimeter units.
107
- - `wallThickness?: number` — Approximate physical wall thickness in millimeters.
108
- - `tpmsThicknessMode?: TpmsThicknessMode` — Override TPMS thickness interpretation. Defaults to metric when `wallThickness` is used, field-threshold when `thickness` is used.
109
- - Also: `cellSize: number`
110
-
111
- #### `sdf.schwarzP()` — Schwarz-P TPMS lattice — isotropic pore structure.
112
-
113
- ```ts
114
- sdf.schwarzP(options: TpmsOptions): SdfShape
115
- ```
116
-
117
- #### `sdf.diamond()` — Diamond TPMS lattice — stiffest TPMS structure.
118
-
119
- ```ts
120
- sdf.diamond(options: TpmsOptions): SdfShape
121
- ```
122
-
123
- #### `sdf.lidinoid()` — Lidinoid TPMS lattice — visually distinct from gyroid, popular in research and art.
124
-
125
- ```ts
126
- sdf.lidinoid(options: TpmsOptions): SdfShape
127
- ```
128
-
129
- #### `sdf.tpmsBlock()` — TPMS block preset clipped to an explicit design space.
130
-
131
- ```ts
132
- sdf.tpmsBlock(options: TpmsBlockOptions): SdfShape
133
- ```
134
-
135
-
136
- `TpmsBlockOptions`: `{ type?: "gyroid" | "schwarzP" | "diamond" | "lidinoid", size: Vec3 }`
137
-
138
- #### `sdf.withinBox()` — Clip an SDF shape to a box-shaped design space.
139
-
140
- ```ts
141
- sdf.withinBox(shape: SdfShape, options: { size: Vec3; }): SdfShape
142
- ```
143
-
144
- #### `sdf.noise()` — 3D Simplex noise field — produces organic, natural-looking displacements.
145
-
146
- ```ts
147
- sdf.noise(options?: NoiseOptions): SdfShape
148
- ```
149
-
150
- **`NoiseOptions`**
151
-
152
- | Option | Type | Description |
153
- |--------|------|-------------|
154
- | `scale?` | `number` | Spatial frequency — smaller = larger features. Default: 0.1 |
155
- | `amplitude?` | `number` | Peak displacement amplitude. Default: 1 |
156
- | `octaves?` | `number` | fBm octaves (1 = plain simplex, higher = more detail). Default: 1 |
157
- | `seed?` | `number` | Seed for deterministic variation. Default: 0 |
158
-
159
- #### `sdf.voronoi()` — 3D Voronoi pattern — organic cellular structures like bone, coral, or soap bubbles.
160
-
161
- ```ts
162
- sdf.voronoi(options?: VoronoiOptions): SdfShape
163
- ```
164
-
165
- **`VoronoiOptions`**
166
-
167
- | Option | Type | Description |
168
- |--------|------|-------------|
169
- | `cellSize?` | `number` | Size of each Voronoi cell in world units. Default: 10 |
170
- | `wallThickness?` | `number` | Wall thickness between cells. Default: 1 |
171
- | `seed?` | `number` | Seed for deterministic variation. Default: 0 |
172
- | `suppressionThreshold?` | `number` | Projection weight for membrane suppression (0..1). Controls how much of the surface-normal distance component is removed from Voronoi cell distances. 0 = no projection (classic 3D voronoi with membranes). 1 = full tangent-plane projection (pure 2D pattern on surface). Default: 0.85. Only active when voronoi is intersected with another shape. |
173
-
174
- #### `sdf.honeycomb()` — Honeycomb (hexagonal) lattice pattern. Intersect with your shape to apply.
175
-
176
- ```ts
177
- sdf.honeycomb(options?: HoneycombOptions): SdfShape
178
- ```
179
-
180
- **`HoneycombOptions`**
181
- - `cellSize?: number` — Size of each hex cell. Default: 8
182
- - `wallThickness?: number` — Wall thickness. Default: 1
183
-
184
- #### `sdf.waves()` — Sinusoidal wave ridges — parallel ridges along an axis.
185
-
186
- ```ts
187
- sdf.waves(options?: WavesOptions): SdfShape
188
- ```
189
-
190
- **`WavesOptions`**
191
- - `wavelength?: number` — Distance between wave peaks. Default: 10
192
- - `amplitude?: number` — Height of waves. Default: 1
193
- - `axis?: "x" | "y" | "z"` — Axis along which waves propagate: 'x', 'y', or 'z'. Default: 'x'
194
-
195
- #### `sdf.knurl()` — Knurl pattern — crossed helical grooves for grips and handles.
196
-
197
- ```ts
198
- sdf.knurl(options?: KnurlOptions): SdfShape
199
- ```
200
-
201
- **`KnurlOptions`**
202
- - `pitch?: number` — Distance between knurl ridges. Default: 3
203
- - `depth?: number` — Depth of knurl grooves. Default: 0.5
204
- - `angle?: number` — Helix angle in degrees. Default: 30
205
-
206
- #### `sdf.perforated()` — Perforated plate pattern — regular array of cylindrical holes.
207
-
208
- ```ts
209
- sdf.perforated(options?: PerforatedOptions): SdfShape
210
- ```
211
-
212
- **`PerforatedOptions`**
213
- - `radius?: number` — Hole radius. Default: 3
214
- - `spacing?: number` — Center-to-center spacing. Default: 8
215
-
216
- #### `sdf.scales()` — Fish/dragon scale pattern — overlapping circular scales in hex-packed rows.
217
-
218
- ```ts
219
- sdf.scales(options?: ScalesOptions): SdfShape
220
- ```
221
-
222
- **`ScalesOptions`**
223
- - `size?: number` — Scale diameter. Default: 5
224
- - `depth?: number` — How much scales protrude. Default: 0.8
225
-
226
- #### `sdf.brick()` — Brick/stone wall pattern — running bond with mortar grooves.
227
-
228
- ```ts
229
- sdf.brick(options?: BrickOptions): SdfShape
230
- ```
231
-
232
- **`BrickOptions`**
233
-
234
- | Option | Type | Description |
235
- |--------|------|-------------|
236
- | `width?` | `number` | Brick width. Default: 10 |
237
- | `height?` | `number` | Brick height. Default: 5 |
238
- | `depth?` | `number` | Mortar groove depth. Default: 0.5 |
239
- | `mortar?` | `number` | Mortar gap width. Default: 1 |
240
-
241
- #### `sdf.weave()` — Grid lattice pattern — two families of infinite slabs crossing at 90°.
242
-
243
- ```ts
244
- sdf.weave(options?: WeaveOptions): SdfShape
245
- ```
246
-
247
- **`WeaveOptions`**
248
- - `spacing?: number` — Thread center-to-center spacing (for intersection patterns). Default: 5
249
- - `threadRadius?: number` — Thread half-width. Default: 1
250
-
251
- #### `sdf.basketWeave()` — Basket weave surface pattern — threads with over-under crossings in UV space. Returns a SurfacePattern for use with `.surfaceDisplace()`.
252
-
253
- ```ts
254
- sdf.basketWeave(options?: BasketWeaveOptions): SurfacePattern
255
- ```
256
-
257
- **`BasketWeaveOptions`**
258
- - `spacing?: number` — Spacing between threads in mm (both directions). Default: 3
259
- - `threadWidth?: number` — Thread width in mm. Default: 1.5
260
- - `depth?: number` — Thread protrusion depth in mm. Default: 0.8
261
-
262
- #### `sdf.pattern2d()` — Create typed, composable 2D surface patterns for `.surfaceDisplace()`.
263
-
264
- ```ts
265
- sdf.pattern2d(): Pattern2DBuilder
266
- ```
267
-
268
- #### `sdf.twist()` — Twist an SDF shape around the Z axis.
269
-
270
- ```ts
271
- sdf.twist(shape: SdfShape, degreesPerUnit: number): SdfShape
272
- ```
273
-
274
- #### `sdf.bend()` — Bend an SDF shape around the Z axis.
275
-
276
- ```ts
277
- sdf.bend(shape: SdfShape, radius: number): SdfShape
278
- ```
279
-
280
- #### `sdf.repeat()` — Repeat an SDF shape in space.
281
-
282
- ```ts
283
- sdf.repeat(shape: SdfShape, spacing: Vec3, count?: Vec3): SdfShape
284
- ```
285
-
286
- #### `sdf.circularArray()` — Arrange an SDF shape in a circular array around the Z axis with O(1) folded-domain evaluation.
287
-
288
- ```ts
289
- sdf.circularArray(shape: SdfShape, count: number, offset?: number): SdfShape
290
- ```
291
-
292
- #### `sdf.SurfacePattern()` — A 2D surface pattern — a heightmap function for use with `.surfaceDisplace()`.
293
-
294
- ```ts
295
- sdf.SurfacePattern: typeof SurfacePattern
296
- ```
297
-
298
- #### `sdf.fromFunction()` — Create a custom SDF from one expression; shader-safe expressions raymarch directly.
299
-
300
- ```ts
301
- sdf.fromFunction(fn: SdfFunctionSource, options: SdfFunctionOptions): SdfShape
302
- ```
303
-
304
- **`SdfFunctionOptions`**
305
-
306
- | Option | Type | Description |
307
- |--------|------|-------------|
308
- | `bounds` | `SdfBounds \| [ Vec3, Vec3 ]` | Finite design bounds for this opaque custom field. |
309
- | `constants?` | `SdfFunctionConstants` | Named finite constants referenced from the expression body. |
310
- | `maxStep?` | `number` | Conservative maximum raymarch step for native preview. |
311
- | `lipschitz?` | `number` | Optional divisor for non-distance fields. Values above 1 slow preview marching. |
312
-
313
- `SdfBounds`: `{ min: Vec3, max: Vec3 }`
314
-
315
- #### `sdf.Sculpt()` — Sculpt-like facade: friendly liquid-modeling verbs backed by the same SDF kernel.
316
-
317
- ```ts
318
- sdf.Sculpt: { sphere: (radius: number) => SdfShape; box: (x: number, y: number, z: number, options?: SculptBoxOptions) => SdfShape; cylinder: (height: number, radius: number) => SdfShape; disk: (radius: number, thickness?: number) => SdfShape; circle: (radius: number, thickness?: number) => SdfShape; capsule: (height: number, radius: number) => SdfShape; torus: (majorRadius: number, minorRadius: number) => SdfShape; cone: (height: number, radius: number) => SdfShape; tube: (points: SculptPointList, options?: SculptTubeOptions) => SdfShape; curve: (points: SculptPointList, options?: SculptTubeOptions) => SdfShape; path: (points: SculptPointList, options?: SculptTubeOptions) => SdfShape; blend: (first?: SculptBlendInput | SculptBlendOptions, optionsOrShape?: SculptBlendInput | SculptBlendOptions, ...rest: (SculptBlendInput | SculptBlendOptions)[]) => SdfShape; union: (first?: SculptBlendInput, ...rest: SculptBlendInput[]) => SdfShape; carve: (base: SdfShape, cutters: SculptBlendInput, options?: SculptBlendOptions) => SdfShape; keep: (first?: SculptBlendInput | SculptBlendOptions, optionsOrShape?: SculptBlendInput | SculptBlendOptions, ...rest: (SculptBlendInput | SculptBlendOptions)[]) => SdfShape; polish: (shape: SdfShape, input?: SculptPolishInput) => SdfShape; material: (input?: SculptPolishInput) => ShapeMaterialProps & { color?: string; }; look: (preset?: SculptLookPreset) => SceneOptions; knownMaterials: typeof knownSculptMaterialPresets; }
319
- ```
320
-
321
- **`SculptBoxOptions`**
322
- - `radius?: number` — Rounded-box radius. Omit for a sharp SDF box.
323
-
324
- **`SculptTubeOptions`**
325
-
326
- | Option | Type | Description |
327
- |--------|------|-------------|
328
- | `radius?` | `number` | Tube radius in model units. Used as the default when points do not carry their own radius. Default: 3. |
329
- | `blend?` | `number` | Smooth blend radius between tube segments and joints. Default: smallest point radius. |
330
- | `curve?` | `boolean \| "linear" \| "catmull-rom"` | Smooth the control polyline into a Catmull-Rom curve before sweeping. Default: false for tube/path, true for curve. |
331
- | `segments?` | `number` | Curve samples per control-point interval when curve smoothing is enabled. Default: 8, capped at 8 for realtime preview. |
332
- | `tension?` | `number` | Catmull-Rom tangent scale when curve smoothing is enabled. Default: 0.5. |
333
- | `polish?` | `SculptPolishInput` | Material/color preset applied to the final tube. |
334
-
335
- **`SculptBlendOptions`**
336
- - `radius?: number` — Smooth blend radius in model units. Default: 4.
337
-
338
- **`ShapeMaterialProps`**
339
-
340
- | Option | Type | Description |
341
- |--------|------|-------------|
342
- | `metalness?` | `number` | Metalness factor (0 = dielectric, 1 = metal). Default: 0.05 |
343
- | `roughness?` | `number` | Roughness factor (0 = mirror, 1 = fully diffuse). Default: 0.35 |
344
- | `emissive?` | `string` | Emissive glow color (hex string, e.g. "#ff6b35"). |
345
- | `emissiveIntensity?` | `number` | Emissive intensity multiplier. Default: 1 |
346
- | `opacity?` | `number` | Opacity (0 = fully transparent, 1 = fully opaque). Default: 1 |
347
- | `wireframe?` | `boolean` | Render as wireframe. Default: false |
348
- | `clearcoat?` | `number` | Clearcoat intensity (0–1). Default: 0.1 |
349
- | `clearcoatRoughness?` | `number` | Clearcoat roughness (0–1). Default: 0.4 |
350
- | `transmission?` | `number` | Glass/translucency transmission factor (0–1). Renderer support depends on target. |
351
- | `ior?` | `number` | Index of refraction for transmissive materials. Typical glass is ~1.45. |
352
- | `thickness?` | `number` | Approximate transmissive volume thickness in model units. |
353
- | `specularIntensity?` | `number` | Specular highlight intensity (0–1). |
354
- | `specularColor?` | `string` | Specular highlight tint. |
355
- | `reflectivity?` | `number` | Reflection strength for supported renderers (0–1). |
356
-
357
- **`SceneOptions`**
358
-
359
- | Option | Type | Description |
360
- |--------|------|-------------|
361
- | `capture?` | `SceneCaptureConfig` | Default capture parameters for `forgecad capture` — CLI flags override these. |
362
- | `background?`, `camera?`, `views?`, `journeys?`, `lights?`, `environment?`, `fog?`, `postProcessing?`, `ground?` | | — |
363
-
364
- `SceneBackgroundGradient`: `{ top: string, bottom: string }`
365
-
366
- **`SceneCameraConfig`**: `position?: [ number, number, number ]`, `target?: [ number, number, number ]`, `up?: [ number, number, number ]`, `fov?: number`, `type?: "perspective" | "orthographic"`
367
-
368
- **`SceneJourneyConfig`**
369
-
370
- | Option | Type | Description |
371
- |--------|------|-------------|
372
- | `title?` | `string` | Viewer-facing journey title. Defaults to the journey id. |
373
- | `startsAt?` | `string` | Optional starting step id. Defaults to the first step. |
374
- | `behavior?` | `"opt-in" \| "auto"` | Whether the viewer should offer or auto-open the journey. First slice supports opt-in. |
375
- | `steps` | `SceneJourneyStepConfig[]` | Ordered journey spine. Branches can be added later without changing this core contract. |
376
- | `valid?` | `boolean` | True unless any journey or step diagnostic has level "error". |
377
-
378
- **`SceneJourneyStepConfig`**
379
-
380
- | Option | Type | Description |
381
- |--------|------|-------------|
382
- | `id` | `string` | Stable step id used by viewer links and Next/Back state. |
383
- | `title?` | `string` | Viewer-facing title. Defaults to the step id. |
384
- | `focus?` | `string` | Object name or slash-separated tree path to focus. |
385
- | `caption?` | `string` | Short optional viewer caption. |
386
- | `camera?` | `SceneViewCameraConfig` | Optional explicit camera for this step. When omitted, the viewer fits `focus`. |
387
- | `resolvedFocusId?` | `string \| null` | Resolved object id after script execution, when `focus` matched exactly one object. |
388
- | `resolvedFocusPath?` | `string \| null` | Resolved object tree path or name after script execution. |
389
-
390
- **`SceneLightConfig`**
391
-
392
- | Option | Type | Description |
393
- |--------|------|-------------|
394
- | `target?` | `[ number, number, number ]` | Target for directional/spot lights |
395
- | `groundColor?` | `string` | Ground color for hemisphere lights |
396
- | `skyColor?` | `string` | Sky color alias for hemisphere lights (same as color) |
397
- | `angle?` | `number` | Spot light cone angle in radians |
398
- | `penumbra?` | `number` | Spot light penumbra (0–1) |
399
- | `decay?` | `number` | Point/spot light decay |
400
- | `distance?` | `number` | Point/spot light distance (0 = infinite) |
401
- | `castShadow?` | `boolean` | Whether this light casts shadows |
402
- | `type`, `color?`, `intensity?`, `position?` | | — |
403
-
404
- **`SceneEnvironmentConfig`**
405
- - `preset?: "studio" | "sunset" | "dawn" | "warehouse" | "forest" | "apartment" | "lobby" | "city" | "park" | "night" | "none"` — Built-in preset name or 'none' to disable
406
- - `intensity?: number` — Environment map intensity
407
- - `background?: boolean` — Use environment map as scene background
408
-
409
- **`SceneFogConfig`**
410
- - `near?: number` — Linear fog near distance
411
- - `far?: number` — Linear fog far distance
412
- - `density?: number` — Exponential fog density (if set, uses FogExp2 instead of linear Fog)
413
- - Also: `color?: string`
414
-
415
- `ScenePostProcessingConfig`: `{ bloom?: SceneBloomConfig, vignette?: SceneVignetteConfig, grain?: SceneGrainConfig, toneMappingExposure?: number }`
416
-
417
- `SceneBloomConfig`: `{ intensity?: number, threshold?: number, radius?: number }`
418
-
419
- `SceneVignetteConfig`: `{ darkness?: number, offset?: number }`
420
-
421
- `SceneGrainConfig`: `{ intensity?: number }`
422
-
423
- **`SceneGroundConfig`**
424
-
425
- | Option | Type | Description |
426
- |--------|------|-------------|
427
- | `visible?` | `boolean` | Show a ground plane |
428
- | `color?` | `string` | Ground color |
429
- | `offset?` | `number` | Offset below the model's bounding box minimum Z. Default 0 (flush with model bottom). |
430
- | `receiveShadow?` | `boolean` | Receive shadows on the ground |
431
-
432
- **`SceneCaptureConfig`**
433
-
434
- | Option | Type | Description |
435
- |--------|------|-------------|
436
- | `framesPerTurn?` | `number` | Frames for one full orbit rotation (default: 72) |
437
- | `holdFrames?` | `number` | Frozen frames before motion starts (default: 6) |
438
- | `pitchDeg?` | `number` | Orbit pitch angle in degrees (default: auto from camera) |
439
- | `fps?` | `number` | Output frame rate (default: 24) |
440
- | `size?` | `number` | Output frame size in pixels (default: 960) |
441
- | `background?` | `string` | Canvas background color for capture (default: '#252526') |
442
-
443
- #### `Sculpt.sphere()` — Create a liquid SDF sphere centered at the origin.
444
-
445
- ```ts
446
- Sculpt.sphere(radius: number): SdfShape
447
- ```
448
-
449
- #### `Sculpt.box()` — Create a liquid SDF box; pass `{ radius }` for a rounded box.
450
-
451
- ```ts
452
- Sculpt.box(x: number, y: number, z: number, options?: SculptBoxOptions): SdfShape
453
- ```
454
-
455
- #### `Sculpt.cylinder()` — Create a liquid SDF cylinder centered at the origin, axis along Z.
456
-
457
- ```ts
458
- Sculpt.cylinder(height: number, radius: number): SdfShape
459
- ```
460
-
461
- #### `Sculpt.disk()` — Create a thin circular disk centered at the origin, axis along Z. Useful as a circular cutter or insert.
462
-
463
- ```ts
464
- Sculpt.disk(radius: number, thickness?: number): SdfShape
465
- ```
466
-
467
- #### `Sculpt.circle()` — Alias for `Sculpt.disk()`.
468
-
469
- ```ts
470
- Sculpt.circle(radius: number, thickness?: number): SdfShape
471
- ```
472
-
473
- #### `Sculpt.capsule()` — Create a liquid SDF capsule centered at the origin, axis along Z.
474
-
475
- ```ts
476
- Sculpt.capsule(height: number, radius: number): SdfShape
477
- ```
478
-
479
- #### `Sculpt.torus()` — Create a liquid SDF torus lying in the XY plane.
480
-
481
- ```ts
482
- Sculpt.torus(majorRadius: number, minorRadius: number): SdfShape
483
- ```
484
-
485
- #### `Sculpt.cone()` — Create a liquid SDF cone.
486
-
487
- ```ts
488
- Sculpt.cone(height: number, radius: number): SdfShape
489
- ```
490
-
491
- #### `Sculpt.tube()` — Create a smooth tube through a list of 3D points.
492
-
493
- ```ts
494
- Sculpt.tube(points: SculptPointList, options?: SculptTubeOptions): SdfShape
495
- ```
496
-
497
- #### `Sculpt.curve()` — Create a smooth variable-thickness sweep through 3D control points.
498
-
499
- ```ts
500
- Sculpt.curve(points: SculptPointList, options?: SculptTubeOptions): SdfShape
501
- ```
502
-
503
- #### `Sculpt.path()` — Alias for `Sculpt.tube()`; points may use [x, y, z, radius] for variable thickness.
504
-
505
- ```ts
506
- Sculpt.path(points: SculptPointList, options?: SculptTubeOptions): SdfShape
507
- ```
508
-
509
- #### `Sculpt.blend()` — Smoothly blend one or more SDF shapes into a continuous body.
510
-
511
- ```ts
512
- Sculpt.blend(first?: SculptBlendArg, optionsOrShape?: SculptBlendArg, ...rest: SculptBlendArg[]): SdfShape
513
- ```
514
-
515
- #### `Sculpt.union()` — Sharply union one or more SDF shapes.
516
-
517
- ```ts
518
- Sculpt.union(first?: SculptBlendInput, ...rest: SculptBlendInput[]): SdfShape
519
- ```
520
-
521
- #### `Sculpt.carve()` — Smoothly subtract one or more cutter shapes from a base shape.
522
-
523
- ```ts
524
- Sculpt.carve(base: SdfShape, cutters: SculptBlendInput, options?: SculptBlendOptions): SdfShape
525
- ```
526
-
527
- #### `Sculpt.keep()` — Smoothly intersect one or more SDF shapes.
528
-
529
- ```ts
530
- Sculpt.keep(first?: SculptBlendArg, optionsOrShape?: SculptBlendArg, ...rest: SculptBlendArg[]): SdfShape
531
- ```
532
-
533
- #### `Sculpt.polish()` — Apply a Sculpt material preset or direct material properties.
534
-
535
- ```ts
536
- Sculpt.polish(shape: SdfShape, input?: SculptPolishInput): SdfShape
537
- ```
538
-
539
- #### `Sculpt.material()` — Resolve a Sculpt material preset to ForgeCAD material properties.
540
-
541
- ```ts
542
- Sculpt.material(input?: SculptPolishInput): ShapeMaterialProps & { color?: string; }
543
- ```
544
-
545
- #### `Sculpt.look()` — Return a polished scene preset tuned for liquid SDF preview.
546
-
547
- ```ts
548
- Sculpt.look(preset?: SculptLookPreset): SceneOptions
549
- ```
550
-
551
- #### `Sculpt.knownMaterials()` — List the built-in Sculpt material preset names.
552
-
553
- ```ts
554
- Sculpt.knownMaterials(): SculptMaterialPreset[]
555
- ```
556
-
557
- #### `toShape()` — Materialize one SDF leaf or all SDF leaves in a renderable tree.
558
-
559
- Raw `SdfShape` values become mesh-backed `Shape`s. Plain objects and arrays preserve their renderable children as a `ShapeGroup` when more than one leaf is found. Non-renderable metadata is ignored for materialization and remains available to callers through normal `require()` return values.
560
-
561
- ```ts
562
- toShape(value: unknown, options?: SdfToShapeOptions): ToShapeTreeResult
563
- ```
564
-
565
- **`SdfToShapeOptions`**
566
-
567
- | Option | Type | Description |
568
- |--------|------|-------------|
569
- | `edgeLength?` | `number` | Target mesh edge length. Smaller = finer mesh. Overrides quality-derived resolution. |
570
- | `bounds?` | `{ min: Vec3; max: Vec3; }` | Override auto-computed bounds. Strongly recommended for infinite/repeated fields. |
571
- | `quality?` | `SdfMeshingQuality` | Coarse quality preset. Default: 'preview'. |
572
- | `tolerance?` | `number` | Preferred absolute surface tolerance in millimeters. |
573
- | `minFeatureSize?` | `number` | Smallest feature that should survive meshing, in millimeters. |
574
- | `simplify?` | `boolean \| "safe"` | Simplification control. `false` disables, `true` and `'safe'` use topology-validated simplification. |
575
- | `maxTriangles?` | `number` | Optional post-extraction triangle budget. |
576
- | `maxGridPoints?` | `number` | Optional pre-extraction grid-point budget. Default is browser-safe. |
577
- | `minEdgeLength?` | `number` | Lower clamp for resolved edge length. Default: 0.15mm. |
578
- | `diagnostics?` | `boolean` | Log resolved meshing settings and backend extraction timings. |
579
-
580
- #### `combine()` — Collapse a tree of SDF leaves into one continuous SDF field.
581
-
582
- This intentionally discards per-leaf color/material identity because the result is one scalar field. Use plain object returns for multi-material SDF preview, and use `combine(...)` only when you want one implicit body.
583
-
584
- ```ts
585
- combine(value: unknown, options?: CombineOptions): SdfShape
586
- ```
587
-
588
- `CombineOptions`: `{ op?: "union" | "intersection" }`
589
-
590
- #### `circle2d()` — Create a 2D circle centered at the origin.
591
-
592
- Omit `segments` for a smooth (auto-tessellated) circle. Pass an integer to get a regular polygon approximation — e.g. `6` for a hexagon, `8` for an octagon.
593
-
594
- ```ts
595
- circle2d(25).extrude(10); // smooth cylinder
596
- circle2d(25, 6).extrude(10); // hexagonal prism
597
- ```
598
-
599
- ```ts
600
- circle2d(radius: number, segments?: number): Sketch
601
- ```
602
-
603
- #### `ellipse()` — Create a 2D ellipse centered at the origin.
604
-
605
- ```ts
606
- ellipse(30, 15).extrude(5);
607
- ellipse(30, 15, 32).extrude(5); // lower-resolution approximation
608
- ```
609
-
610
- ```ts
611
- ellipse(rx: number, ry: number, segments?: number): Sketch
612
- ```
613
-
614
- #### `loadFont()` — Pre-load and cache a font for use with `text2d()`.
615
-
616
- Fonts are cached by their source string (or `cacheKey` for `ArrayBuffer` sources), so repeated calls with the same path are free. Pre-loading is useful when you call `text2d()` many times with the same font — it avoids repeated disk reads.
617
-
618
- Built-in font names that work everywhere (browser + CLI):
619
-
620
- - `'sans-serif'` or `'inter'` — bundled Inter Regular
621
-
622
- ```ts
623
- const font = loadFont('/path/to/Arial Bold.ttf');
624
- text2d('Title', { size: 12, font }).extrude(1.5);
625
- text2d('Subtitle', { size: 8, font }).extrude(1);
626
- ```
627
-
628
- ```ts
629
- loadFont(source: string | ArrayBuffer, cacheKey?: string): opentype.Font
630
- ```
631
-
632
- #### `ngon()` — Create a regular polygon inscribed in a circle of the given radius.
633
-
634
- `radius` is the center-to-vertex (circumradius) distance. Use `sides` of `3` for a triangle, `6` for a hexagon, etc. The first vertex is at the top (−90° from +X).
635
-
636
- ```ts
637
- ngon(6, 20).extrude(10); // hexagonal prism, circumradius 20
638
- ```
639
-
640
- ```ts
641
- ngon(sides: number, radius: number): Sketch
642
- ```
643
-
644
- #### `path()` — Create a new `PathBuilder` for tracing a 2D outline point by point.
645
-
646
- `PathBuilder` is a fluent API for constructing 2D profiles using a mix of line segments, arcs, bezier curves, and splines. Always start with `.moveTo(x, y)` to set the starting point. Call `.close()` to get a filled `Sketch`, or `.stroke(width)` to thicken an open polyline into a solid profile.
647
-
648
- Edge labels can be assigned with `.label('name')` after any segment — they propagate through extrusion, revolve, loft, and sweep into named faces on the resulting `Shape`.
649
-
650
- ```ts
651
- // Closed triangle
652
- const triangle = path().moveTo(0, 0).lineH(50).lineV(30).close();
653
-
654
- // L-shaped bracket as a stroke
655
- const bracket = path().moveTo(0, 0).lineH(50).lineV(-70).lineAngled(20, 235).stroke(4);
656
-
657
- // Labeled edges for downstream face references
658
- const slot = path()
659
- .moveTo(0, 0)
660
- .lineTo(30, 0).label('bottom')
661
- .lineTo(30, 10)
662
- .lineTo(0, 10).label('top')
663
- .close();
664
- ```
665
-
666
- ```ts
667
- path(): PathBuilder
668
- ```
669
-
670
- #### `polygon()` — Create a 2D polygon from an array of `[x, y]` points or `Point2D` objects.
671
-
672
- Winding order is normalized automatically — clockwise (CW) input is silently reversed to CCW before being passed to the geometry kernel.
673
-
674
- ```ts
675
- polygon([[0, 0], [50, 0], [25, 40]]).extrude(5); // triangle
676
- ```
677
-
678
- ```ts
679
- polygon(points: ([ number, number ] | Point2D)[]): Sketch
680
- ```
681
-
682
- #### `polygonVertices()` — Compute the vertex positions of a regular polygon.
683
-
684
- Default orientation places the first vertex at the top (90 degrees), matching the convention used by `ngon()`.
685
-
686
- Eliminates manual Math.sqrt(3) for triangles, pentagon vertex math, etc:
687
-
688
- ```js
689
- // Before — manual equilateral triangle
690
- const v1 = [center.x - r/2, center.y + r * Math.sqrt(3)/2];
691
- const v2 = [center.x - r/2, center.y - r * Math.sqrt(3)/2];
692
- const v3 = [center.x + r, center.y];
693
-
694
- // After — declarative
695
- const [v1, v2, v3] = polygonVertices(3, r);
696
- ```
697
-
698
- ```ts
699
- polygonVertices(sides: number, radius: number, options?: PolygonVerticesOptions): LayoutPoint[]
700
- ```
701
-
702
- **`PolygonVerticesOptions`**
703
- - `startDeg?: number` — Angle of the first vertex in degrees (default: 90 = top).
704
- - `centerX?: number` — Center X coordinate (default: 0).
705
- - `centerY?: number` — Center Y coordinate (default: 0).
706
-
707
- `LayoutPoint`: `{ x: number, y: number }`
708
-
709
- #### `rect()` — Create a 2D rectangle centered at the origin.
710
-
711
- ```ts
712
- rect(40, 20).extrude(5);
713
- ```
714
-
715
- ```ts
716
- rect(width: number, height: number): Sketch
717
- ```
718
-
719
- #### `arcSlot()` — Create an arc-shaped slot (banana / annular sector) centered at the origin.
720
-
721
- The slot is symmetric about the +X axis. The two ends are closed with semicircular caps. `pitchRadius` is the distance from the origin to the centerline of the slot, and `thickness` is the radial width of the slot.
722
-
723
- ```ts
724
- arcSlot(135, 74, 40).extrude(5); // pitch R135, 74° sweep, 40mm wide
725
- ```
726
-
727
- ```ts
728
- arcSlot(pitchRadius: number, sweepDeg: number, thickness: number): Sketch
729
- ```
730
-
731
- #### `roundedRect()` — Create a 2D rectangle with rounded corners, centered at the origin.
732
-
733
- The corner radius is automatically clamped to `min(width/2, height/2)` so it can never exceed the shape dimensions.
734
-
735
- ```ts
736
- roundedRect(60, 30, 5).extrude(3);
737
- ```
738
-
739
- ```ts
740
- roundedRect(width: number, height: number, radius: number): Sketch
741
- ```
742
-
743
- #### `slot()` — Create a slot (oblong / stadium shape) — a rectangle with semicircular ends, centered at the origin.
744
-
745
- ```ts
746
- slot(40, 10).extrude(3); // 40mm long, 10mm wide slot
747
- ```
748
-
749
- ```ts
750
- slot(length: number, width: number): Sketch
751
- ```
752
-
753
- #### `spline2d()` — Build a smooth Catmull-Rom spline sketch from 2D control points.
754
-
755
- A closed spline (default) returns a filled profile. An open spline requires a strokeWidth option to produce a solid sketch. Use tension (0..1, default 0.5) to control curve tightness.
756
-
757
- ```ts
758
- spline2d(points: Vec2[], options?: Spline2DOptions): Sketch
759
- ```
760
-
761
- **`Spline2DOptions`**
762
-
763
- | Option | Type | Description |
764
- |--------|------|-------------|
765
- | `closed?` | `boolean` | Closed loop (default true). |
766
- | `tension?` | `number` | Catmull-Rom tension in [0, 1]. 0 = very round, 1 = linear-ish. Default 0.5. |
767
- | `samplesPerSegment?` | `number` | Samples per segment (minimum 3). Default 16. |
768
- | `strokeWidth?` | `number` | For open splines, provide stroke width to return a solid Sketch. If omitted for open splines, an error is thrown. |
769
- | `join?` | `"Round" \| "Square"` | Stroke join for open splines. Default 'Round'. |
770
-
771
- #### `star()` — Create a star shape with alternating outer and inner radii.
772
-
773
- ```ts
774
- star(5, 30, 12).extrude(4); // five-pointed star
775
- ```
776
-
777
- ```ts
778
- star(points: number, outerR: number, innerR: number): Sketch
779
- ```
780
-
781
- #### `stroke()` — Create a stroked polyline sketch from an array of 2D points.
782
-
783
- ```ts
784
- stroke(points: [ number, number ][], width: number, join?: "Round" | "Square"): Sketch
785
- ```
786
-
787
- #### `text2d()` — Build a filled 2D Sketch from a text string.
788
-
789
- The Sketch origin is at the left end of the text baseline by default. Use `align` and `baseline` options to adjust placement. Text is rendered using the bundled Inter font by default, or any TTF/OTF/WOFF font you provide.
790
-
791
- `text2d()` creates real geometry. For temporary viewport annotations, prefer `Viewport.label()` so the text stays off the geometry and OCCT compile paths. Do not use either form of text to make unclear production geometry readable; model the physical artifact clearly instead.
792
-
793
- Alignment reference table:
794
-
795
- | `align` | `baseline` | Origin |
796
- |------------|--------------|-------------------------------------|
797
- | `'left'` | `'baseline'` | Bottom-left of first char (default) |
798
- | `'center'` | `'center'` | Dead center of text block |
799
- | `'right'` | `'top'` | Top-right corner |
800
-
801
- ```ts
802
- // Extruded nameplate
803
- text2d('FORGE CAD', { size: 8 }).extrude(1.2);
804
-
805
- // Centered label on the XY plane
806
- text2d('V 2.0', { size: 6, align: 'center', baseline: 'center' });
807
-
808
- // Engraved text cut into the top face of a box
809
- const label = text2d('REV A', { size: 5, align: 'center', baseline: 'center' });
810
- plate.subtract(label.onFace(plate, 'top', { protrude: -0.5 }).extrude(1));
811
-
812
- // Custom TTF font
813
- text2d('Hello', { size: 10, font: '/path/to/Arial.ttf' }).extrude(1);
814
-
815
- // Pre-loaded font for reuse
816
- const font = loadFont('/path/to/Arial Bold.ttf');
817
- text2d('Title', { size: 12, font }).extrude(1.5);
818
- ```
819
-
820
- ```ts
821
- text2d(content: string, options?: TextOptions): Sketch
822
- ```
823
-
824
- **`TextOptions`**
825
-
826
- | Option | Type | Description |
827
- |--------|------|-------------|
828
- | `size?` | `number` | Cap height of the text in model units. All other dimensions (stroke weight, spacing) scale proportionally. |
829
- | `letterSpacing?` | `number` | Extra space between characters in model units. Negative values tighten the tracking. |
830
- | `align?` | `"left" \| "center" \| "right"` | Horizontal alignment relative to x = 0. - `'left'` — left edge at x = 0 (default) - `'center'` — centred on x = 0 - `'right'` — right edge at x = 0 |
831
- | `baseline?` | `"baseline" \| "center" \| "top"` | Vertical alignment relative to y = 0. - `'baseline'` — y = 0 is the text baseline (bottom of capital letters) - `'center'` — y = 0 is the vertical midpoint of the cap height - `'top'` — y = 0 is the top of capital letters |
832
- | `font?` | `string \| opentype.Font` | Font to use for text rendering. - `'sans-serif'` or `'inter'` — bundled Inter font (works everywhere, including browser) - **file path** — path to a TTF, OTF, or WOFF font file (CLI/Node only) - **Font object** — a previously loaded opentype.js Font (from `loadFont()`) - **omitted** — uses the bundled Inter font (same as `'sans-serif'`) |
833
- | `flattenTolerance?` | `number` | Bezier flattening tolerance in model units. Smaller = more polygon segments = smoother curves. |
834
-
835
- #### `textWidth()` — Measure the rendered advance width of a string without creating any geometry.
836
-
837
- Uses the same font metrics as `text2d()`. Useful for computing layout dimensions before building the actual sketch — e.g. sizing a plate to fit a label.
838
-
839
- ```ts
840
- const w = textWidth('SERIAL: 001', { size: 6 });
841
- const plate = box(w + 10, 12, 2);
842
- ```
843
-
844
- ```ts
845
- textWidth(content: string, options?: Pick<TextOptions, "size" | "letterSpacing" | "font">): number
846
- ```
847
-
848
- #### `box()` — Create a rectangular box. Centered on XY, base at Z=0.
849
-
850
- Extents:
851
-
852
- - X: `[-width/2, width/2]`
853
- - Y: `[-depth/2, depth/2]`
854
- - Z: `[0, height]`
855
-
856
- For named faces, build from a labeled sketch: `rect(width, depth).labelEdges('s', 'e', 'n', 'w').extrude(height, { labels: { start: 'bottom', end: 'top' } })`.
857
-
858
- ```ts
859
- box(width: number, depth: number, height: number): Shape
860
- ```
861
-
862
- #### `cylinder()` — Create a cylinder or cone with named faces and edges. Centered on XY, base at Z=0.
863
-
864
- Extents:
865
-
866
- - X/Y: centered at the origin
867
- - Z: `[0, height]`
868
-
869
- `radiusTop` defaults to `radius`. Set `radiusTop` smaller to taper the side, or `0` for a pointy cone. Use `segments` to create regular prisms (for example `6` for a hexagonal prism).
870
-
871
- Named faces: `top`, `bottom`, `side` Named edges: `top-rim`, `bottom-rim`
872
-
873
- ```ts
874
- cylinder(height: number, radius: number, radiusTop?: number, segments?: number): Shape
875
- ```
876
-
877
- #### `sphere()` — Create a sphere centered at the origin.
878
-
879
- Extents:
880
-
881
- - X: `[-radius, radius]`
882
- - Y: `[-radius, radius]`
883
- - Z: `[-radius, radius]`
884
-
885
- Use `segments` for lower-poly approximations.
886
-
887
- ```ts
888
- sphere(radius: number, segments?: number): Shape
889
- ```
890
-
891
- #### `torus()` — Create a torus (donut shape) lying in the XY plane. Centered on all axes.
892
-
893
- Extents:
894
-
895
- - X: `[-(majorRadius + minorRadius), +(majorRadius + minorRadius)]`
896
- - Y: `[-(majorRadius + minorRadius), +(majorRadius + minorRadius)]`
897
- - Z: `[-minorRadius, minorRadius]`
898
-
899
- The origin is the center of the ring.
900
-
901
- ```ts
902
- torus(majorRadius: number, minorRadius: number, segments?: number): Shape
903
- ```
904
-
905
- ---
906
-
907
- ## C2: Boolean Combination
908
-
909
- Combine same-dimension geometry using CSG set operations.
910
-
911
- #### `difference2d()` — Subtract one or more 2D sketches from a base sketch.
912
-
913
- The first sketch is the base; all subsequent sketches are subtracted from it. Accepts individual sketches or arrays: `difference2d(base, c1, c2)` or `difference2d([base, c1, c2])`. Uses Manifold's batch operation — faster than chaining `.subtract()` one by one.
914
-
915
- ```ts
916
- const donut = difference2d(circle2d(50), circle2d(30));
917
- ```
918
-
919
- ```ts
920
- difference2d(...inputs: SketchOperandInput[]): Sketch
921
- ```
922
-
923
- #### `intersection2d()` — Keep only the area where all input sketches overlap (intersection boolean).
924
-
925
- Accepts individual sketches or arrays: `intersection2d(a, b)` or `intersection2d([a, b, c])`. Uses Manifold's batch operation — faster than chaining `.intersect()` one by one.
926
-
927
- ```ts
928
- const lens = intersection2d(circle2d(30).translate(-10, 0), circle2d(30).translate(10, 0));
929
- ```
930
-
931
- ```ts
932
- intersection2d(...inputs: SketchOperandInput[]): Sketch
933
- ```
934
-
935
- #### `union2d()` — Combine 2D sketches into a single profile using an additive boolean union.
936
-
937
- Accepts individual sketches or arrays: `union2d(a, b, c)` or `union2d([a, b, c])`. Uses Manifold's batch operation — faster than chaining `.add()` one by one when combining many sketches.
938
-
939
- ```ts
940
- const cross = union2d(rect(60, 10), rect(10, 60));
941
- ```
942
-
943
- ```ts
944
- union2d(...inputs: SketchOperandInput[]): Sketch
945
- ```
946
-
947
- #### `union()` — Combine shapes into a single solid (additive boolean).
948
-
949
- Accepts individual shapes, or an array of shapes. `union()` returns one solid, so only the first operand's color is preserved in the result. Use `group()` when you want separate child colors or identities.
950
-
951
- ```ts
952
- union(...inputs: ShapeOperandInput[]): Shape
953
- ```
954
-
955
- #### `difference()` — Subtract shapes from a base shape (subtractive boolean).
956
-
957
- The first shape is the base; all subsequent shapes are subtracted from it. Accepts individual shapes, or an array of shapes.
958
-
959
- ```ts
960
- difference(...inputs: ShapeOperandInput[]): Shape
961
- ```
962
-
963
- #### `intersection()` — Keep only the overlapping volume of the input shapes (intersection boolean).
964
-
965
- Requires at least two shapes. Accepts individual shapes, or an array.
966
-
967
- ```ts
968
- intersection(...inputs: ShapeOperandInput[]): Shape
969
- ```
970
-
971
- ---
972
-
973
- ## C3: Rigid Transform
974
-
975
- Reposition or reorient geometry without changing its shape.
976
-
977
- #### `degrees()` — Identity function that returns degrees unchanged.
978
-
979
- Use for clarity when the unit of an angle value would otherwise be ambiguous — e.g. `param("Angle", degrees(45))`.
980
-
981
- ```ts
982
- degrees(deg: number): number
983
- ```
984
-
985
- #### `radians()` — Convert radians to degrees.
986
-
987
- ForgeCAD's public API uses degrees throughout. Use this when you have a radian value (e.g. from `Math.atan2`) that you want to express in degrees.
988
-
989
- ```ts
990
- radians(rad: number): number
991
- ```
992
-
993
- #### `composeChain()` — Compose transforms in chain order. Equivalent to Transform.identity().mul(a).mul(b).mul(c)...
994
-
995
- ```ts
996
- composeChain(...steps: TransformInput[]): Transform
997
- ```
998
-
999
- ---
1000
-
1001
- ## C4: Dimensional Promotion
1002
-
1003
- Convert a 2D profile into a 3D solid (extrude, revolve, loft, sweep).
1004
-
1005
- #### `Curve.Blend()` — Create an exact G1 blend curve between two directed endpoints.
1006
-
1007
- 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.
1008
-
1009
- ```js
1010
- const rail = Curve.Blend(
1011
- { point: [0, 0, 0], tangent: [1, 0, 0], weight: 0.8 },
1012
- { point: [40, 20, 8], tangent: [0, 1, 0], weight: 0.8 },
1013
- );
1014
- const tube = sweep(circle2d(2), rail);
1015
- ```
1016
-
1017
- ```ts
1018
- Curve.Blend(start: CurveBlendEndpoint, end: CurveBlendEndpoint): NurbsCurve3D
1019
- ```
1020
-
1021
- **`CurveBlendEndpoint`**
1022
- - `point: Vec3` — Endpoint position.
1023
- - `tangent: Vec3` — Tangent direction at this endpoint. Magnitude is ignored.
1024
- - `weight?: number` — Tangent reach relative to the endpoint chord length. Default 1.
1025
-
1026
- #### `Curve.BlendG2()` — Create an exact G2 blend curve between two directed endpoints.
1027
-
1028
- 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.
1029
-
1030
- ```js
1031
- const rail = Curve.BlendG2(
1032
- { point: [0, 0, 0], tangent: [1, 0, 0], curvature: [0, 0.02, 0] },
1033
- { point: [50, 20, 0], tangent: [0, 1, 0], curvature: [-0.02, 0, 0] },
1034
- );
1035
- ```
1036
-
1037
- ```ts
1038
- Curve.BlendG2(start: CurveBlendG2Endpoint, end: CurveBlendG2Endpoint): NurbsCurve3D
1039
- ```
1040
-
1041
-
1042
- **`CurveBlendG2Endpoint`** extends CurveBlendEndpoint
1043
- - `curvature?: Vec3` — Optional endpoint curvature/second-derivative vector. Default is zero.
1044
-
1045
- #### `Curve.Arc()` — Create an exact circular 3D arc from start, end, and start tangent.
1046
-
1047
- 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.
1048
-
1049
- ```js
1050
- const rail = Curve.Arc({
1051
- start: [40, 0, 0],
1052
- end: [0, 40, 0],
1053
- tangent: [0, 1, 0],
1054
- });
1055
- const tube = sweep(circle2d(2), rail);
1056
- ```
1057
-
1058
- ```ts
1059
- Curve.Arc(options: CurveArcOptions): NurbsCurve3D
1060
- ```
1061
-
1062
- **`CurveArcOptions`**
1063
- - `start: Vec3` — Arc start point.
1064
- - `end: Vec3` — Arc end point.
1065
- - `tangent: Vec3` — Tangent direction at the start point. Magnitude is ignored.
1066
-
1067
- #### `Curve.Line()` — Create an exact straight 3D NURBS line segment.
1068
-
1069
- ```js
1070
- const rail = Curve.Line([0, 0, 0], [80, 0, 15]);
1071
- const rib = sweep(circle2d(2), rail);
1072
- ```
1073
-
1074
- ```ts
1075
- Curve.Line(start: Vec3, end: Vec3): NurbsCurve3D
1076
- ```
1077
-
1078
- #### `Curve.Polyline()` — Create a polyline path as cloned 3D points.
1079
-
1080
- Polylines are exact as route/path input to `sweep`. Use `Curve.Route` when the centerline needs bend and endpoint-frame metadata.
1081
-
1082
- ```ts
1083
- Curve.Polyline(points: Vec3[]): Vec3[]
1084
- ```
1085
-
1086
- #### `Curve.Spline()` — Create a smooth Catmull-Rom spline path.
1087
-
1088
- This is a smooth sampled curve object. Use `Curve.Nurbs` when the path must preserve exact control-point and knot data.
1089
-
1090
- ```ts
1091
- Curve.Spline(points: Vec3[], options?: Spline3DOptions): Curve3D
1092
- ```
1093
-
1094
- **`Spline3DOptions`**
1095
- - `closed?: boolean` — Closed loop (default false).
1096
- - `tension?: number` — Catmull-Rom tension in [0, 1]. 0 = very round, 1 = linear-ish. Default 0.5.
1097
-
1098
- #### `Curve.Nurbs()` — Create an exact NURBS 3D curve from control points, weights, knots, and degree.
1099
-
1100
- ```js
1101
- const rail = Curve.Nurbs([[0, 0, 0], [30, 4, 12], [60, -4, 12], [90, 0, 0]]);
1102
- const tube = sweep(circle2d(2), rail);
1103
- ```
1104
-
1105
- ```ts
1106
- Curve.Nurbs(points: Vec3[], options?: NurbsCurve3DOptions): NurbsCurve3D
1107
- ```
1108
-
1109
- **`NurbsCurve3DOptions`**
1110
-
1111
- | Option | Type | Description |
1112
- |--------|------|-------------|
1113
- | `degree?` | `number` | Polynomial degree (default 3 = cubic). Must be ≥ 1. |
1114
- | `weights?` | `number[]` | Rational weights, one per control point (default: all 1.0 = non-rational). |
1115
- | `knots?` | `number[]` | Knot vector (default: uniform clamped). Must have length = controlPoints.length + degree + 1. |
1116
- | `closed?` | `boolean` | Whether the curve is closed/periodic (default false). |
1117
-
1118
- #### `Curve.Fit()` — Fit a non-rational NURBS curve that interpolates every input point.
1119
-
1120
- 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`.
1121
-
1122
- ```js
1123
- const rail = Curve.Fit(
1124
- [[0, 0, 0], [20, 8, 12], [50, -4, 18], [80, 0, 0]],
1125
- { degree: 3, tolerance: 0.001 },
1126
- );
1127
- const tube = sweep(circle2d(2), rail);
1128
- ```
1129
-
1130
- ```ts
1131
- Curve.Fit(points: Vec3[], options?: CurveFitOptions): NurbsCurve3D
1132
- ```
1133
-
1134
- **`CurveFitOptions`**
1135
- - `degree?: number` — Polynomial degree. Default is cubic, reduced automatically for short point lists.
1136
- - `tolerance?: number` — Maximum allowed interpolation residual in model units. Default 1e-7.
1137
-
1138
- #### `Curve.Trim()` — Extract an exact curve segment from normalized parameter `start` to `end`.
1139
-
1140
- `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.
1141
-
1142
- ```ts
1143
- Curve.Trim<T extends CurveTrimInput>(curve: T, start: number, end: number): CurveTrimOutput<T>
1144
- ```
1145
-
1146
- #### `Curve.Reverse()` — Reverse an exact curve without changing its geometry.
1147
-
1148
- `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.
1149
-
1150
- ```ts
1151
- Curve.Reverse<T extends CurveTrimInput>(curve: T): CurveTrimOutput<T>
1152
- ```
1153
-
1154
- #### `Curve.Route()` — Build analytic 3D line/arc routes for sweeps.
1155
-
1156
- `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.
1157
-
1158
- ```js
1159
- const route = Curve.Route.fromPolyline(
1160
- [[0, 0, 0], [0, 0, 50], [40, 0, 50]],
1161
- { cornerRadius: 12, startPort: 'inlet', endPort: 'outlet' },
1162
- );
1163
- const tube = sweep(circle2d(4), route);
1164
- ```
1165
-
1166
- ```ts
1167
- Curve.Route: typeof Route3D
1168
- ```
1169
-
1170
- #### `Curve.Helix()` — Build helical paths and swept coils.
1171
-
1172
- `Curve.Helix` is the canonical namespace for helical paths and coils. It uses the same sweep-based lowering as other curve paths.
1173
-
1174
- ```js
1175
- const guide = Curve.Helix.path({ radius: 20, pitch: 6, turns: 4 });
1176
- const spring = Curve.Helix.coil({ radius: 20, pitch: 6, turns: 4, wireRadius: 1 });
1177
- ```
1178
-
1179
- ```ts
1180
- Curve.Helix: { path(options: HelixOptions): CurveHelixPath; coil: CurveHelixCoil; }
1181
- ```
1182
-
1183
- **`HelixOptions`**
1184
-
1185
- | Option | Type | Description |
1186
- |--------|------|-------------|
1187
- | `radius` | `number` | Radius from the central Z axis to the helix centerline. |
1188
- | `pitch?` | `number` | Axial distance per full turn. Provide any two of `pitch`, `turns`, and `height`. |
1189
- | `turns?` | `number` | Number of full rotations around the axis. Provide any two of `pitch`, `turns`, and `height`. |
1190
- | `height?` | `number` | Total height along +Z. Provide any two of `pitch`, `turns`, and `height`. |
1191
- | `startAngle?` | `number` | Start angle in degrees. Default 0 starts on +X. |
1192
- | `clockwise?` | `boolean` | Reverse winding direction when viewed from +Z. |
1193
- | `samplesPerTurn?` | `number` | Point samples per turn for the metadata path. Default 32. |
1194
-
1195
-
1196
- `CurveHelixPath`: `{ radius: number, pitch: number, turns: number, height: number, startAngle: number, clockwise: boolean }`
1197
-
1198
- #### `Loft.station()` — Create a loft station from a 2D profile and an axis position.
1199
-
1200
- ```ts
1201
- Loft.station(profile: Sketch, position: number): LoftStation
1202
- ```
1203
-
1204
- `LoftStation`: `{ profile: Sketch, position: number }`
1205
-
1206
- #### `Loft.leftRail()` — Create a guide rail that constrains the section-local negative-X side.
1207
-
1208
- ```ts
1209
- Loft.leftRail(path: LoftGuideRailPath): LoftGuideRail
1210
- ```
1211
-
1212
- `LoftGuideRail`: `{ side: LoftGuideRailSide, path: LoftGuideRailPath }`
1213
-
1214
- #### `Loft.rightRail()` — Create a guide rail that constrains the section-local positive-X side.
1215
-
1216
- ```ts
1217
- Loft.rightRail(path: LoftGuideRailPath): LoftGuideRail
1218
- ```
1219
-
1220
- #### `Loft.frontRail()` — Create a guide rail that constrains the section-local positive-Y side.
1221
-
1222
- ```ts
1223
- Loft.frontRail(path: LoftGuideRailPath): LoftGuideRail
1224
- ```
1225
-
1226
- #### `Loft.backRail()` — Create a guide rail that constrains the section-local negative-Y side.
1227
-
1228
- ```ts
1229
- Loft.backRail(path: LoftGuideRailPath): LoftGuideRail
1230
- ```
1231
-
1232
- #### `Loft.centerRail()` — Create a guide rail that moves section centers along the loft.
1233
-
1234
- ```ts
1235
- Loft.centerRail(path: LoftGuideRailPath): LoftGuideRail
1236
- ```
1237
-
1238
- #### `Loft.pathOnXz()` — Place a 2D guide path onto the XZ plane.
1239
-
1240
- The path's first coordinate becomes X and its second coordinate becomes Z. Use this for left/right silhouette rails authored with `path()` or `constrainedSketch()`.
1241
-
1242
- ```ts
1243
- Loft.pathOnXz(path: LoftPath2D, y?: number): Vec3[]
1244
- ```
1245
-
1246
- #### `Loft.pathOnYz()` — Place a 2D guide path onto the YZ plane.
1247
-
1248
- The path's first coordinate becomes Y and its second coordinate becomes Z. Use this for front/back crown rails authored with `path()` or `constrainedSketch()`.
1249
-
1250
- ```ts
1251
- Loft.pathOnYz(path: LoftPath2D, x?: number): Vec3[]
1252
- ```
1253
-
1254
- #### `Loft.pathOnXy()` — Place a 2D guide path onto the XY plane.
1255
-
1256
- 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.
1257
-
1258
- ```ts
1259
- Loft.pathOnXy(path: LoftPath2D, z?: number): Vec3[]
1260
- ```
1261
-
1262
- #### `Loft.withGuideRails()` — Loft through profile stations while forcing generated sections to follow guide rails.
1263
-
1264
- 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.
1265
-
1266
- ```ts
1267
- Loft.withGuideRails(stations: LoftStation[], rails: LoftGuideRail[], options?: LoftWithGuideRailsOptions): Shape
1268
- ```
1269
-
1270
- **`LoftOptions`**
1271
- - `edgeLength?: number` — Marching-grid edge length for level-set meshing. Smaller = finer.
1272
- - `boundsPadding?: number` — Optional extra bounds padding.
1273
-
1274
- **`LoftWithGuideRailsOptions`** extends LoftOptions
1275
- - `axis?: LoftAxis` — Primary station axis. Default Z.
1276
- - `samples?: number` — Number of generated loft stations including ends. Default scales with station count.
1277
- - `railSamples?: number` — Number of points sampled from curve-backed rails before axis interpolation. Default 64.
1278
-
1279
- #### `Analysis.EdgeContinuity()`
1280
-
1281
- ```ts
1282
- Analysis.EdgeContinuity(shape: Shape, options?: EdgeContinuityThresholds): EdgeContinuityReport
1283
- ```
1284
-
1285
- **`EdgeContinuityThresholds`**: `continuity?: SurfaceContinuity`, `samples?: number`, `positionTolerance?: number`, `tangentToleranceDeg?: number`, `curvatureTolerance?: number`
1286
-
1287
- **`EdgeContinuityReport`**: `requested: SurfaceContinuity`, `passed: boolean`, `sampledEdges: number`, `edges: EdgeContinuityEdgeReport[]`, `worst: { continuity: SurfaceContinuity; maxPositionalGap: number; maxTangentAngleDeg: number; maxCurvatureGap: number; }`
1288
-
1289
- **`EdgeContinuityEdgeReport`**: `edgeIndex: number`, `continuity: SurfaceContinuity`, `maxPositionalGap: number`, `maxTangentAngleDeg: number`, `maxCurvatureGap: number`
1290
-
1291
- #### `Analysis.SurfaceContinuity()`
1292
-
1293
- ```ts
1294
- Analysis.SurfaceContinuity(shape: Shape, options?: EdgeContinuityThresholds): EdgeContinuityReport
1295
- ```
1296
-
1297
- #### `Analysis.CurvatureComb()`
1298
-
1299
- ```ts
1300
- Analysis.CurvatureComb(input: NurbsCurve3D | EdgeRef, options?: { samples?: number; }): CurvatureSample[]
1301
- ```
1302
-
1303
- **`EdgeRef`**
1304
-
1305
- | Option | Type | Description |
1306
- |--------|------|-------------|
1307
- | `start` | `[ number, number, number ]` | Start point |
1308
- | `end` | `[ number, number, number ]` | End point |
1309
- | `query?` | `EdgeQueryRef` | Compiler-owned edge query when available. |
1310
- | `curve?` | `EdgeCurve` | Exact or parametric curve family when the backend/source can identify one. |
1311
- | `faceName?` | `string` | Owning face name when the edge is associated with one face in a larger topology. |
1312
- | `name` | | — |
1313
-
1314
- `CurvatureSample`: `{ t: number, curvature: number, point: Vec3 }`
1315
-
1316
- #### `Analysis.SurfaceHealth()`
1317
-
1318
- ```ts
1319
- Analysis.SurfaceHealth(shape: Shape, options?: { tinyEdgeThreshold?: number; sliverThreshold?: number; }): SurfaceHealthReport
1320
- ```
1321
-
1322
- **`SurfaceHealthReport`**: `tinyEdges: Array<{ index: number; length: number; }>`, `sliverFaces: Array<{ index: number; area: number; sliverScore: number; }>`, `valid: boolean`
1323
-
1324
- #### `Analysis.BRepValidity()` — Validate B-rep/shell/solid structure and return closedness, manifoldness, orientation, and issue diagnostics.
1325
-
1326
- ```ts
1327
- Analysis.BRepValidity(shape: Shape, options?: BRepValidityOptions): BRepValidityReport
1328
- ```
1329
-
1330
- `BRepValidityOptions`: `{ tolerance?: number, requireClosed?: boolean, requireManifold?: boolean, requireSolid?: boolean, minVolume?: number }`
1331
-
1332
- **`BRepValidityReport`**: `ok: boolean`, `valid: boolean`, `closed: boolean`, `manifold: boolean`, `solid: boolean`, `oriented: boolean`, `volume: number`, `edgeCount: number`, `faceCount: number`, `openEdges: number`, `nonManifoldEdges: number`, `degenerateFaces: number`, `errors: BRepValidityIssue[]`
1333
-
1334
- **`BRepValidityIssue`**: `kind: BRepValidityIssueKind`, `message: string`, `index?: number`, `faceCount?: number`, `length?: number`, `volume?: number`, `area?: number`
1335
-
1336
- #### `Blend.Edge()`
1337
-
1338
- ```ts
1339
- Blend.Edge(options: BlendEdgeOptions): Shape
1340
- ```
1341
-
1342
- `BlendEdgeOptions`: `{ shape?: Shape, edges: EdgeRef[], radius: number, continuity?: SurfaceContinuity, segments?: number }`
1343
-
1344
- #### `Blend.Surface()`
1345
-
1346
- ```ts
1347
- Blend.Surface(options: BlendSurfaceOptions): Shape
1348
- ```
1349
-
1350
- `SurfaceCommonOptions`: `{ resolution?: number, approximate?: boolean }`
1351
-
1352
- **`SurfaceFillInput`** extends SurfaceCommonOptions: `boundaries: Array<{ name: string; curve: ExactCurveInput; }>`, `match?: Record<string, SurfaceMatchConstraintInput>`, `style?: SurfaceFillStyle`
1353
-
1354
- `SurfaceMatchConstraintInput`: `{ target: EdgeRef, continuity?: SurfaceContinuity }`
1355
-
1356
- #### `Surface.Plane()` — Create a finite analytic plane sheet that can be trimmed, sewn, thickened, or used as a low-level face.
1357
-
1358
- ```ts
1359
- Surface.Plane(options: SurfacePlaneOptions): Shape
1360
- ```
1361
-
1362
- **`SurfaceAnalyticCommonOptions`**
1363
- - `resolution?: number` — Tessellation/sample hint for non-OCCT previews and diagnostics.
1364
- - `trim?: SurfaceTrimOptions` — Optional normalized UV trims in this surface's bounded parameter domain.
1365
-
1366
- **`SurfaceTrimOptions`**
1367
- - `outer: SurfaceTrimLoopInput` — Outer trim loop in normalized post-domain UV space.
1368
- - `holes?: SurfaceTrimLoopInput[]` — Optional hole loops in normalized post-domain UV space.
1369
-
1370
- **`SurfacePlaneOptions`** extends SurfaceAnalyticCommonOptions
1371
-
1372
- | Option | Type | Description |
1373
- |--------|------|-------------|
1374
- | `origin?` | `Vec3` | Center of the finite plane patch. Defaults to the origin. |
1375
- | `normal?` | `Vec3` | Plane normal. Defaults to +Z. |
1376
- | `xAxis?` | `Vec3` | Plane-local +U direction. Defaults to a stable perpendicular axis. |
1377
- | `width` | `number` | Physical width along the +U axis. |
1378
- | `height` | `number` | Physical height along the +V axis. |
1379
-
1380
- #### `Surface.Cylinder()` — Create a finite analytic cylindrical sheet, optionally bounded by start/end angles.
1381
-
1382
- ```ts
1383
- Surface.Cylinder(options: SurfaceCylinderOptions): Shape
1384
- ```
1385
-
1386
-
1387
- **`SurfaceCylinderOptions`** extends SurfaceAnalyticCommonOptions
1388
-
1389
- | Option | Type | Description |
1390
- |--------|------|-------------|
1391
- | `origin?` | `Vec3` | Center of the cylinder base. Defaults to the origin. |
1392
- | `axis?` | `Vec3` | Cylinder axis from base toward top. Defaults to +Z. |
1393
- | `xAxis?` | `Vec3` | Direction for angle 0 around the axis. Defaults to a stable perpendicular axis. |
1394
- | `radius`, `height`, `startDeg?`, `endDeg?` | | — |
1395
-
1396
- #### `Surface.Cone()` — Create a finite analytic conical or frustum sheet, optionally bounded by start/end angles.
1397
-
1398
- ```ts
1399
- Surface.Cone(options: SurfaceConeOptions): Shape
1400
- ```
1401
-
1402
-
1403
- **`SurfaceConeOptions`** extends SurfaceAnalyticCommonOptions
1404
-
1405
- | Option | Type | Description |
1406
- |--------|------|-------------|
1407
- | `origin?` | `Vec3` | Center of the cone/frustum base. Defaults to the origin. |
1408
- | `axis?` | `Vec3` | Cone axis from base toward top. Defaults to +Z. |
1409
- | `xAxis?` | `Vec3` | Direction for angle 0 around the axis. Defaults to a stable perpendicular axis. |
1410
- | `radiusBottom`, `radiusTop`, `height`, `startDeg?`, `endDeg?` | | — |
1411
-
1412
- #### `Surface.Sphere()` — Create a finite analytic spherical sheet bounded by longitude and latitude ranges.
1413
-
1414
- ```ts
1415
- Surface.Sphere(options: SurfaceSphereOptions): Shape
1416
- ```
1417
-
1418
-
1419
- **`SurfaceSphereOptions`** extends SurfaceAnalyticCommonOptions
1420
-
1421
- | Option | Type | Description |
1422
- |--------|------|-------------|
1423
- | `axis?` | `Vec3` | Polar axis. Defaults to +Z. |
1424
- | `xAxis?` | `Vec3` | Direction for longitude 0 around the axis. Defaults to a stable perpendicular axis. |
1425
- | `center?`, `radius`, `startDeg?`, `endDeg?`, `minLatitudeDeg?`, `maxLatitudeDeg?` | | — |
1426
-
1427
- #### `Surface.Torus()` — Create a finite analytic torus sheet bounded by major and tube angle ranges.
1428
-
1429
- ```ts
1430
- Surface.Torus(options: SurfaceTorusOptions): Shape
1431
- ```
1432
-
1433
-
1434
- **`SurfaceTorusOptions`** extends SurfaceAnalyticCommonOptions
1435
-
1436
- | Option | Type | Description |
1437
- |--------|------|-------------|
1438
- | `axis?` | `Vec3` | Axis through the torus hole. Defaults to +Z. |
1439
- | `xAxis?` | `Vec3` | Direction for major angle 0 around the axis. Defaults to a stable perpendicular axis. |
1440
- | `center?`, `majorRadius`, `minorRadius`, `startDeg?`, `endDeg?`, `tubeStartDeg?`, `tubeEndDeg?` | | — |
1441
-
1442
- #### `Surface.Nurbs()`
1443
-
1444
- ```ts
1445
- Surface.Nurbs(controlGrid: Vec3[][], options?: NurbsSurfaceOptions): Shape
1446
- ```
1447
-
1448
- **`NurbsSurfaceOptions`**
1449
-
1450
- | Option | Type | Description |
1451
- |--------|------|-------------|
1452
- | `degreeU?` | `number` | Degree in U direction (default 3). |
1453
- | `degreeV?` | `number` | Degree in V direction (default 3). |
1454
- | `weights?` | `number[][]` | Weights grid — same dimensions as controlGrid (default: all 1.0). |
1455
- | `knotsU?` | `number[]` | Knot vector in U direction (default: uniform clamped). |
1456
- | `knotsV?` | `number[]` | Knot vector in V direction (default: uniform clamped). |
1457
- | `thickness?` | `number` | Sheet thickness — if > 0, thickens the surface into a solid (default 0 = surface only). |
1458
- | `resolution?` | `number` | Tessellation resolution — points per direction (default 32). |
1459
- | `domain?` | `SurfaceDomainOptions` | Optional rectangular parameter domain in normalized [0, 1] U/V space. |
1460
- | `trim?` | `SurfaceTrimOptions` | Optional polygonal or NURBS-curve UV trim loops. Truck and OCCT support open trimmed surfaces; Manifold supports sampled thickened trimmed solids. |
1461
- | `tessellation?` | `SurfaceTessellationOptions` | Optional Truck kernel tessellation controls for render mesh generation. |
1462
- | `approximate?` | `boolean` | Explicit opt-in for sampled approximation paths on non-exact backends. |
1463
-
1464
- **`SurfaceDomainOptions`**
1465
-
1466
- | Option | Type | Description |
1467
- |--------|------|-------------|
1468
- | `uMin?` | `number` | Lower U parameter bound in normalized surface space (default 0). |
1469
- | `uMax?` | `number` | Upper U parameter bound in normalized surface space (default 1). |
1470
- | `vMin?` | `number` | Lower V parameter bound in normalized surface space (default 0). |
1471
- | `vMax?` | `number` | Upper V parameter bound in normalized surface space (default 1). |
1472
-
1473
- **`SurfaceTessellationOptions`**
1474
-
1475
- | Option | Type | Description |
1476
- |--------|------|-------------|
1477
- | `mode?` | `"uniform" \| "adaptive"` | `uniform` uses resolution directly; `adaptive` lets the Truck kernel refine open sheets from chord error. |
1478
- | `tolerance?` | `number` | Target chord-error tolerance in model units for adaptive Truck tessellation. |
1479
- | `minResolution?` | `number` | Minimum adaptive samples per direction. |
1480
- | `maxResolution?` | `number` | Maximum adaptive samples per direction. Defaults to `resolution` when omitted. |
1481
-
1482
- #### `Surface.Ruled()`
1483
-
1484
- ```ts
1485
- Surface.Ruled(curveA: ExactCurveInput, curveB: ExactCurveInput, options?: SurfaceCommonOptions): Shape
1486
- ```
1487
-
1488
- #### `Surface.Patch()`
1489
-
1490
- ```ts
1491
- Surface.Patch(curves: { bottom: ExactCurveInput; top: ExactCurveInput; left: ExactCurveInput; right: ExactCurveInput; }, options?: SurfacePatchOptions): Shape
1492
- ```
1493
-
1494
- **`SurfacePatchOptions`**
1495
- - `resolution?: number` — Number of samples along each direction. Default 24.
1496
- - `thickness?: number` — Thickness of the generated solid. Default 0 for an open exact sheet.
1497
- - `approximate?: boolean` — Allow explicit approximation for non-exact curve inputs such as Curve3D samples.
1498
-
1499
- #### `Surface.Boundary()`
1500
-
1501
- ```ts
1502
- Surface.Boundary(input: SurfaceBoundaryInput): Shape
1503
- ```
1504
-
1505
-
1506
- **`SurfaceBoundaryInput`** extends SurfaceCommonOptions: `u: [ ExactCurveInput, ExactCurveInput ]`, `v: [ ExactCurveInput, ExactCurveInput ]`, `match?: Partial<Record<"u0" | "u1" | "v0" | "v1", SurfaceMatchConstraintInput>>`, `style?: SurfaceFillStyle`
1507
-
1508
- #### `Surface.Fill()`
1509
-
1510
- ```ts
1511
- Surface.Fill(input: SurfaceFillInput): Shape
1512
- ```
1513
-
1514
- #### `Surface.Sew()`
1515
-
1516
- ```ts
1517
- Surface.Sew(shapes: Shape[], options?: { tolerance?: number; }): Shape
1518
- ```
1519
-
1520
- #### `Surface.Solid()` — Sew surface faces or consume an existing sewn shell and make a solid B-rep.
1521
-
1522
- ```ts
1523
- Surface.Solid(input: Shape | Shape[], options?: SurfaceSolidOptions): Shape
1524
- ```
1525
-
1526
- `SurfaceSolidOptions`: `{ tolerance?: number, validate?: boolean }`
1527
-
1528
- #### `Surface.Extend()`
1529
-
1530
- ```ts
1531
- Surface.Extend(shape: Shape, options: SurfaceExtendOptions): Shape
1532
- ```
1533
-
1534
- `SurfaceExtendOptions`: `{ edge: "u0" | "u1" | "v0" | "v1", length: number, continuity?: SurfaceContinuity }`
1535
-
1536
- #### `Surface.Trim()`
1537
-
1538
- ```ts
1539
- Surface.Trim(shape: Shape, tool: Shape | SurfacePlaneOp): Shape
1540
- ```
1541
-
1542
- `SurfacePlaneOp`: `{ normal: Vec3, offset?: number }`
1543
-
1544
- #### `Surface.Split()`
1545
-
1546
- ```ts
1547
- Surface.Split(shape: Shape, tool: Shape | SurfacePlaneOp): [ Shape, Shape ]
1548
- ```
1549
-
1550
- #### `Surface.Match()`
1551
-
1552
- ```ts
1553
- Surface.Match(shape: Shape, options: { edge: "u0" | "u1" | "v0" | "v1"; target: EdgeRef; continuity?: SurfaceContinuity; }): Shape
1554
- ```
1555
-
1556
- #### `Surface.MatchEdge()`
1557
-
1558
- ```ts
1559
- Surface.MatchEdge(shape: Shape, options: { edge: "u0" | "u1" | "v0" | "v1"; target: EdgeRef; continuity?: SurfaceContinuity; }): Shape
1560
- ```
1561
-
1562
- #### `nurbsSurface()` — Create a NURBS surface from a grid of control points.
1563
-
1564
- The control grid is indexed as `controlGrid[u][v]` — each row is a curve in the V direction, and columns trace curves in the U direction.
1565
-
1566
- With default options, creates a bicubic non-rational B-spline surface with uniform clamped knots.
1567
-
1568
- ```js
1569
- // Simple 4×4 control grid — a gently curved surface
1570
- const grid = [
1571
- [[0,0,0], [10,0,2], [20,0,2], [30,0,0]],
1572
- [[0,10,1], [10,10,5], [20,10,5], [30,10,1]],
1573
- [[0,20,1], [10,20,5], [20,20,5], [30,20,1]],
1574
- [[0,30,0], [10,30,2], [20,30,2], [30,30,0]],
1575
- ];
1576
- const surface = nurbsSurface(grid, { thickness: 2 });
1577
- ```
1578
-
1579
- ```ts
1580
- nurbsSurface(controlGrid: Vec3[][], options?: NurbsSurfaceOptions): Shape
1581
- ```
1582
-
1583
- #### `loft()` — Loft between multiple sketches along Z stations.
1584
-
1585
- 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.
1586
-
1587
- Performance note: loft is significantly heavier than primitive/extrude/revolve. If the part is axis-symmetric (bottles, vases, knobs), prefer revolve().
1588
-
1589
- ```ts
1590
- loft(profiles: Sketch[], heights: number[], options?: LoftOptions): Shape
1591
- ```
1592
-
1593
- #### `surfacePatch()` — Create a smooth surface patch from 4 boundary curves (Coons patch).
1594
-
1595
- The four curves form the boundary of a quadrilateral patch:
1596
-
1597
- - bottom: u=0..1 at v=0 (from corner00 to corner10)
1598
- - top: u=0..1 at v=1 (from corner01 to corner11)
1599
- - left: v=0..1 at u=0 (from corner00 to corner01)
1600
- - right: v=0..1 at u=1 (from corner10 to corner11)
1601
-
1602
- The interior is filled using bilinear Coons patch interpolation: P(u,v) = Lc(u,v) + Ld(u,v) - B(u,v)
1603
-
1604
- The result is a thin solid created by offsetting the surface mesh along its normals by the specified thickness.
1605
-
1606
- Note: curves should meet at corners. Small gaps are tolerated.
1607
-
1608
- ```ts
1609
- surfacePatch(curves: { ... }, options?: SurfacePatchOptions): Shape
1610
- ```
1611
-
1612
- #### `sweep()`
1613
-
1614
- ```ts
1615
- sweep(profile: Sketch, path: SweepPathInput, options?: SweepOptions): Shape
1616
- ```
1617
-
1618
- **`SweepOptions`**
1619
-
1620
- | Option | Type | Description |
1621
- |--------|------|-------------|
1622
- | `samples?` | `number` | Number of samples when path is a Curve3D. Default 48. |
1623
- | `edgeLength?` | `number` | Marching-grid edge length for level-set meshing. Smaller = finer. |
1624
- | `boundsPadding?` | `number` | Optional extra bounds padding. |
1625
- | `up?` | `Vec3` | Preferred "up" vector for local profile frame. Auto fallback is used near parallel segments. |
1626
-
1627
- #### `variableSweep()` — Sweep a variable cross-section along a 3D spine curve.
1628
-
1629
- Unlike sweep(), which uses a single constant profile, variableSweep() interpolates between multiple profiles at different stations along the spine. This enables organic shapes like tapering tubes, bone-like structures, and sculptural forms.
1630
-
1631
- Each section specifies a t parameter (0 = start, 1 = end of spine) and a 2D profile sketch. The SDF-based level-set mesher smoothly blends between profiles at intermediate positions.
1632
-
1633
- Performance note: like sweep(), this uses level-set meshing internally.
1634
-
1635
- ```ts
1636
- variableSweep(spine: SweepPathInput, sections: VariableSweepSection[], options?: VariableSweepOptions): Shape
1637
- ```
1638
-
1639
- **`VariableSweepSection`**
1640
- - `t: number` — Parameter along the spine (0 = start, 1 = end).
1641
- - `profile: Sketch` — Cross-section profile at this station.
1642
-
1643
- **`VariableSweepOptions`**
1644
-
1645
- | Option | Type | Description |
1646
- |--------|------|-------------|
1647
- | `samples?` | `number` | Number of samples when spine is a Curve3D. Default 48. |
1648
- | `edgeLength?` | `number` | Marching-grid edge length for level-set meshing. Smaller = finer. |
1649
- | `boundsPadding?` | `number` | Optional extra bounds padding. |
1650
- | `up?` | `Vec3` | Preferred "up" vector for local profile frame. Auto fallback is used near parallel segments. |
1651
-
1652
- ---
1653
-
1654
- ## C5: Topology Query
1655
-
1656
- Select or inspect named faces and edges on a shape.
1657
-
1658
- #### `coalesceEdges()` — Merge collinear edge segments into longer logical edges.
1659
-
1660
- Tessellation often splits one geometric edge into multiple short segments. `coalesceEdges` groups adjacent collinear segments and merges each group into a single `EdgeSegment` spanning the full extent. This is usually needed before passing edges to `fillet()` or `chamfer()` on non-primitive shapes.
1661
-
1662
- The `tolerance` controls the maximum perpendicular distance from collinearity before two segments are considered non-collinear. Default: `0.01`.
1663
-
1664
- ```ts
1665
- const topEdges = selectEdges(part, { atZ: 20 });
1666
- for (const edge of coalesceEdges(topEdges)) {
1667
- result = fillet(result, 2, edge);
1668
- }
1669
- ```
1670
-
1671
- ```ts
1672
- coalesceEdges(segments: EdgeSegment[], tolerance?: number): EdgeSegment[]
1673
- ```
1674
-
1675
- **`EdgeSegment`**
1676
-
1677
- | Option | Type | Description |
1678
- |--------|------|-------------|
1679
- | `index` | `number` | Stable index within the extraction (deterministic for a given mesh). |
1680
- | `direction` | `Vec3` | Normalized direction from start → end. |
1681
- | `dihedralAngle` | `number` | Dihedral angle in degrees (0 = coplanar, 180 = knife edge). |
1682
- | `convex` | `boolean` | true = outside corner (convex), false = inside corner (concave). |
1683
- | `normalA` | `Vec3` | Normal of first adjacent face. |
1684
- | `normalB` | `Vec3` | Normal of second adjacent face (same as normalA for boundary edges). |
1685
- | `boundary` | `boolean` | true if this is a boundary (unmatched) edge — unusual for closed solids. |
1686
- | `start`, `end`, `midpoint`, `length` | | — |
1687
-
1688
- #### `selectEdge()` — Select the single best-matching edge from a shape.
1689
-
1690
- When `near` is specified, returns the edge whose midpoint is closest to that point. Otherwise returns the first matching edge in mesh order. Throws if no edges match the query — useful as a guard when you expect exactly one result.
1691
-
1692
- ```ts
1693
- // Chamfer one specific edge near a known point
1694
- const bottomEdge = selectEdge(part, { near: [25, 0, 0], atZ: 0 });
1695
- result = chamfer(result, 1.5, bottomEdge);
1696
- ```
1697
-
1698
- ```ts
1699
- selectEdge(shape: Shape, query?: EdgeQuery): EdgeSegment
1700
- ```
1701
-
1702
- **`EdgeQuery`**
1703
-
1704
- | Option | Type | Description |
1705
- |--------|------|-------------|
1706
- | `near?` | `Vec3` | Sort by proximity to this point (closest first). When used with `selectEdge`, picks the closest match. |
1707
- | `parallel?` | `Vec3` | Filter: edge direction approximately parallel to this vector. |
1708
- | `perpendicular?` | `Vec3` | Filter: edge direction approximately perpendicular to this vector. |
1709
- | `convex?` | `boolean` | Filter: only convex (outside corner) edges. |
1710
- | `concave?` | `boolean` | Filter: only concave (inside corner) edges. |
1711
- | `minAngle?` | `number` | Filter: minimum dihedral angle in degrees. |
1712
- | `maxAngle?` | `number` | Filter: maximum dihedral angle in degrees. |
1713
- | `minLength?` | `number` | Filter: minimum edge length. |
1714
- | `maxLength?` | `number` | Filter: maximum edge length. |
1715
- | `within?` | `BoundingRegion` | Filter: edge midpoint must be within this bounding region. |
1716
- | `atZ?` | `number` | Shorthand: edge midpoint Z is approximately this value within `tolerance`. |
1717
- | `tolerance?` | `number` | Position tolerance for approximate matches. Used by `atZ` and `near`. Default: `1.0`. |
1718
- | `angleTolerance?` | `number` | Angular tolerance in degrees for `parallel`/`perpendicular` filters. Default: `10`. |
1719
-
1720
- `BoundingRegion`: `{ xMin?: number, xMax?: number, yMin?: number, yMax?: number, zMin?: number, zMax?: number }`
1721
-
1722
- #### `selectEdges()` — Select all edges from a shape that match the given query.
1723
-
1724
- Uses the active kernel's native topology query when available (Truck), otherwise extracts sharp edges from the mesh (dihedral angle > 1°), applies all filters in the query, and returns the matching `EdgeSegment[]`. When `near` is specified the results are sorted closest-first.
1725
-
1726
- Works on any shape — primitives, booleans, shells, and imported meshes. Use this when tracked topology is unavailable (e.g. after a difference or on imported geometry). For simpler cases, pass an `EdgeQuery` directly to `fillet()` or `chamfer()` instead of calling `selectEdges` separately.
1727
-
1728
- ```ts
1729
- // Fillet all top edges of a box
1730
- const topEdges = selectEdges(part, { atZ: 20, perpendicular: [0, 0, 1] });
1731
- let result = part;
1732
- for (const edge of coalesceEdges(topEdges)) {
1733
- result = fillet(result, 2, edge);
1734
- }
1735
- ```
1736
-
1737
- ```ts
1738
- selectEdges(shape: Shape, query?: EdgeQuery): EdgeSegment[]
1739
- ```
1740
-
1741
- ---
1742
-
1743
- ## C6: Edge Feature
1744
-
1745
- Modify edges of a solid — fillets, chamfers, draft, offset.
1746
-
1747
- #### `chamfer()` — Apply experimental chamfers (beveled edges) to one or more edges of a shape.
1748
-
1749
- **Experimental**: chamfers are still backend-sensitive. The Manifold backend is known to produce incorrect results for some edge-finish cases, and the OCCT backend can be very slow, especially with broad edge selections. Prefer targeted edge selectors and inspect the result before treating it as production-ready geometry.
1750
-
1751
- Produces a 45° bevel at the specified `size` (distance from edge). Edge selections compile into backend operations; unsupported selections fail as explicit kernel gaps instead of using TypeScript geometry fallbacks.
1752
-
1753
- The `edges` parameter accepts the same options as `fillet()`: inline `EdgeQuery`, pre-selected `EdgeSegment`/`EdgeSegment[]`, or `undefined` (all sharp edges).
1754
-
1755
- ```ts
1756
- // Chamfer all edges
1757
- chamfer(myShape, 1)
1758
-
1759
- // Chamfer only vertical edges
1760
- chamfer(myShape, 2, { parallel: [0, 0, 1] })
1761
- ```
1762
-
1763
- ```ts
1764
- chamfer(shape: Shape, size: number, edges?: EdgeSelector): Shape
1765
- ```
1766
-
1767
- #### `draft()` — Apply a draft angle (taper) to vertical faces for mold extraction.
1768
-
1769
- Adds a taper angle to the vertical faces of a solid so that it can be extracted from a mold. The neutral plane is the Z position where the draft angle is zero — faces above and below are tapered symmetrically. Typical values for injection molding are 1–5°.
1770
-
1771
- Truck supports vertical-prism solids with Z-axis pull directions. OCCT uses its native draft operation when available. Manifold throws.
1772
-
1773
- ```ts
1774
- // Add 3° draft to a box for injection molding
1775
- draft(myBox, 3)
1776
-
1777
- // Draft with custom pull direction and neutral plane
1778
- draft(myShape, 2, [0, 0, 1], 10)
1779
- ```
1780
-
1781
- ```ts
1782
- draft(shape: Shape, angleDeg: number, pullDirection?: [ number, number, number ], neutralPlaneOffset?: number): Shape
1783
- ```
1784
-
1785
- #### `fillet()` — Apply experimental fillets (rounded edges) to one or more edges of a shape.
1786
-
1787
- **Experimental**: fillets are still backend-sensitive. The Manifold backend is known to produce incorrect results for some edge-finish cases, and the OCCT backend can be very slow, especially with broad edge selections. Prefer targeted edge selectors and inspect the result before treating it as production-ready geometry.
1788
-
1789
- Edge selections compile into backend operations; unsupported selections fail as explicit kernel gaps instead of using TypeScript geometry fallbacks.
1790
-
1791
- The `edges` parameter is flexible:
1792
-
1793
- - Omit to fillet **all** sharp edges
1794
- - Pass an `EdgeQuery` for an inline filter (most common)
1795
- - Pass an `EdgeSegment` or `EdgeSegment[]` from `selectEdges()` for pre-selected edges
1796
-
1797
- Throws if no edges match the selection, or if `radius` is not a positive finite number.
1798
-
1799
- ```ts
1800
- // Fillet all edges
1801
- fillet(myShape, 2)
1802
-
1803
- // Fillet only top convex edges
1804
- fillet(myShape, 1.5, { atZ: 20, convex: true })
1805
-
1806
- // Fillet vertical edges selected beforehand
1807
- const edges = selectEdges(myShape, { parallel: [0, 0, 1] })
1808
- fillet(myShape, 3, edges)
1809
- ```
1810
-
1811
- ```ts
1812
- fillet(shape: Shape, radius: number, edges?: EdgeSelector, segments?: number): Shape
1813
- ```
1814
-
1815
- #### `offsetSolid()` — Uniformly offset all surfaces of a solid inward or outward.
1816
-
1817
- Unlike `shell()`, which hollows a solid by removing one face, `offsetSolid()` produces a new solid whose every surface is shifted by `thickness`. Positive values grow the shape outward; negative values shrink it inward.
1818
-
1819
- Requires the OCCT backend. Throws on Manifold.
1820
-
1821
- ```ts
1822
- // Grow a box outward by 1mm on all sides
1823
- offsetSolid(myBox, 1)
1824
-
1825
- // Shrink a shape inward by 0.5mm
1826
- offsetSolid(myShape, -0.5)
1827
- ```
1828
-
1829
- ```ts
1830
- offsetSolid(shape: Shape, thickness: number): Shape
1831
- ```
1832
-
1833
- #### `chamferTrackedEdge()` — Bevel a tracked vertical solid edge with a 45° chamfer.
1834
-
1835
- Compiler-owned chamfer for tracked vertical edges. Requires a compile-plan-covered target. This is not a general 2D sketch-corner tool; supported subset and quadrant semantics are the same as `filletTrackedEdge()` - see that function for details.
1836
-
1837
- ```ts
1838
- const base = Rectangle2D.fromDimensions(0, 0, 50, 50).extrude(20);
1839
- const chamfered = chamferTrackedEdge(base, base.edge('vert-br'), 3, [-1, -1]);
1840
- ```
1841
-
1842
- ```ts
1843
- chamferTrackedEdge(shape: Shape, edge: EdgeRef, size: number, quadrant?: [ number, number ]): Shape
1844
- ```
1845
-
1846
- **`EdgeRef`**
1847
-
1848
- | Option | Type | Description |
1849
- |--------|------|-------------|
1850
- | `start` | `[ number, number, number ]` | Start point |
1851
- | `end` | `[ number, number, number ]` | End point |
1852
- | `query?` | `EdgeQueryRef` | Compiler-owned edge query when available. |
1853
- | `curve?` | `EdgeCurve` | Exact or parametric curve family when the backend/source can identify one. |
1854
- | `faceName?` | `string` | Owning face name when the edge is associated with one face in a larger topology. |
1855
- | `name` | | — |
1856
-
1857
- #### `filletTrackedEdge()` — Round a tracked vertical solid edge with a circular fillet.
1858
-
1859
- Compiler-owned fillet for a narrow tracked-edge subset on solids.
1860
-
1861
- This is **not** a general 2D sketch-corner fillet. It currently works only on tracked vertical edges from `box()` or `Rectangle2D` extrusions (plus rigid transforms and supported preserved descendants of those). Generic sketch extrudes, including `rect(...).extrude(...)`, are outside the supported subset right now.
1862
-
1863
- **Supported edges:**
1864
-
1865
- - Tracked vertical edges from `box()` or `Rectangle2D.extrude()`
1866
- - Rigid transforms between tracked source and target
1867
- - Untouched sibling tracked vertical edges after earlier `filletTrackedEdge`/`chamferTrackedEdge`
1868
-
1869
- **Not supported:** edges after shell, hole, cut, trim, difference, intersection, generic sketch extrudes, or tapered extrudes.
1870
-
1871
- Canonical quadrants: `vert-bl → [1,-1]`, `vert-br → [-1,-1]`, `vert-tr → [-1,1]`, `vert-tl → [1,1]`
1872
-
1873
- ```ts
1874
- const base = Rectangle2D.fromDimensions(0, 0, 50, 50).extrude(20);
1875
- const filleted = filletTrackedEdge(base, base.edge('vert-br'), 5, [-1, -1]);
1876
- ```
1877
-
1878
- ```ts
1879
- filletTrackedEdge(shape: Shape, edge: EdgeRef, radius: number, quadrant?: [ number, number ], segments?: number): Shape
1880
- ```
1881
-
1882
- #### `filletCorners()` — Create a polygon from points with specific corners rounded to arc fillets.
1883
-
1884
- Each corner spec identifies a vertex by its index in the `points` array and the desired fillet `radius`. Both convex and concave corners are supported.
1885
-
1886
- Constraints:
1887
-
1888
- - Collinear corners cannot be filleted (throws an error)
1889
- - Two neighboring fillets whose tangent lengths overlap the same edge will throw
1890
- - Radius must be positive and small enough to fit within the adjacent edge lengths
1891
-
1892
- Use `offset(-r).offset(+r)` instead if you want to round **all** convex corners uniformly. Use `filletCorners` when you need selective or mixed sharp/rounded profiles.
1893
-
1894
- ```ts
1895
- const roof = filletCorners(roofPoints, [
1896
- { index: 3, radius: 19 },
1897
- { index: 4, radius: 19 },
1898
- { index: 5, radius: 19 },
1899
- ]);
1900
- ```
1901
-
1902
- ```ts
1903
- filletCorners(points: PointInput[], corners: FilletCornerSpec[]): Sketch
1904
- ```
1905
-
1906
- `FilletCornerSpec`: `{ index: number, radius: number, segments?: number }`
1907
-
1908
- ---
1909
-
1910
- ## C7: Pattern Replication
1911
-
1912
- Duplicate geometry in regular arrangements (linear, circular, mirror).
1913
-
1914
- #### `circularLayout()` — Compute evenly-spaced positions around a circle.
1915
-
1916
- Eliminates the most common trig pattern in CAD scripts:
1917
-
1918
- ```js
1919
- // Before — manual trig
1920
- for (let i = 0; i < 12; i++) {
1921
- const angle = i * 30 * Math.PI / 180;
1922
- markers.push(marker.translate(r * Math.cos(angle), r * Math.sin(angle), 0));
1923
- }
1924
-
1925
- // After — declarative
1926
- for (const {x, y} of circularLayout(12, r)) {
1927
- markers.push(marker.translate(x, y, 0));
1928
- }
1929
- ```
1930
-
1931
- ```ts
1932
- circularLayout(count: number, radius: number, options?: CircularLayoutOptions): LayoutPoint[]
1933
- ```
1934
-
1935
- **`CircularLayoutOptions`**
1936
- - `startDeg?: number` — Angle of the first element in degrees (default: 0 = +X axis).
1937
- - `centerX?: number` — Center X coordinate (default: 0).
1938
- - `centerY?: number` — Center Y coordinate (default: 0).
1939
-
1940
- `LayoutPoint`: `{ x: number, y: number }`
1941
-
1942
- #### `circularPattern()` — Repeat a shape in a circular pattern around an axis and union the copies.
1943
-
1944
- Distributes `count` copies evenly around the rotation axis (360° / count per step). All copies are unioned into a single `Shape`. Distinct compiler ownership is assigned to each copy — post-merge face identity via owner-scoped canonical queries still works for pattern descendants.
1945
-
1946
- Two calling conventions:
1947
-
1948
- - **Simple** (Z axis): `circularPattern(shape, 6)` or `circularPattern(shape, 6, centerX, centerY)`
1949
- - **Advanced** (arbitrary axis): `circularPattern(shape, 6, { axis, origin })`
1950
-
1951
- ```ts
1952
- // 8 holes evenly spaced around origin
1953
- circularPattern(cylinder(12, 4).translate(30, 0, -1), 8)
1954
-
1955
- // Circular pattern around X axis
1956
- circularPattern(myFeature, 4, { axis: [1, 0, 0], origin: [0, 0, 50] })
1957
- ```
1958
-
1959
- ```ts
1960
- circularPattern(shape: Shape, count: number, centerXOrOpts?: number | CircularPatternOptions, centerY?: number): Shape
1961
- ```
1962
-
1963
- **`CircularPatternOptions`**
1964
-
1965
- | Option | Type | Description |
1966
- |--------|------|-------------|
1967
- | `centerX?` | `number` | Center X of the rotation (default: 0). Used when the rotation axis is Z. |
1968
- | `centerY?` | `number` | Center Y of the rotation (default: 0). Used when the rotation axis is Z. |
1969
- | `axis?` | `[ number, number, number ]` | Rotation axis direction (default: [0, 0, 1] = Z axis). |
1970
- | `origin?` | `[ number, number, number ]` | Pivot point for the rotation (default: [0, 0, 0]). Overrides centerX/centerY when set. |
1971
-
1972
- #### `circularPattern2d()` — Repeat a 2D sketch in a circular pattern around a center point and union the copies.
1973
-
1974
- ```ts
1975
- circularPattern2d(sketch: Sketch, count: number, centerXOrOpts?: number | { centerX?: number; centerY?: number; startDeg?: number; }, centerY?: number): Sketch
1976
- ```
1977
-
1978
- #### `linearPattern()` — Repeat a shape in a linear pattern along a direction vector and union the copies.
1979
-
1980
- Creates `count` copies of `shape`, each offset by `(dx*i, dy*i, dz*i)` from the original. All copies are unioned into a single `Shape`. Distinct compiler ownership is assigned to each copy so face identity via owner-scoped canonical queries still works post-merge.
1981
-
1982
- ```ts
1983
- // 5 cylinders, 20mm apart along X
1984
- linearPattern(cylinder(10, 3), 5, 20, 0)
1985
- ```
1986
-
1987
- ```ts
1988
- linearPattern(shape: Shape, count: number, dx: number, dy: number, dz?: number): Shape
1989
- ```
1990
-
1991
- #### `linearPattern2d()` — Repeat a 2D sketch in a linear pattern and union the copies.
1992
-
1993
- ```ts
1994
- linearPattern2d(sketch: Sketch, count: number, dx: number, dy?: number): Sketch
1995
- ```
1996
-
1997
- #### `mirrorCopy()` — Mirror a shape across a plane and union the mirror with the original.
1998
-
1999
- The mirror plane passes through the origin and is defined by its normal vector. The mirrored copy is unioned with the original to produce a single symmetric Shape.
2000
-
2001
- ```ts
2002
- // Mirror across the YZ plane (X=0)
2003
- mirrorCopy(box(50, 30, 10), [1, 0, 0])
2004
- ```
2005
-
2006
- ```ts
2007
- mirrorCopy(shape: Shape, normal: [ number, number, number ]): Shape
2008
- ```
2009
-
2010
- ---
2011
-
2012
- ## C8: Constraint Solving
2013
-
2014
- Define geometry by relationships and let a solver find positions.
2015
-
2016
- #### `Constraint.makeParallel()` — Constrain two lines to be parallel.
2017
-
2018
- ```ts
2019
- Constraint.makeParallel(builder: ConstrainedSketchBuilder, a: LineArg, b: LineArg): ConstrainedSketchBuilder
2020
- ```
2021
-
2022
- #### `Constraint.enforceAngle()` — Constrain the signed angle from line `a` to line `b`.
2023
-
2024
- ```ts
2025
- Constraint.enforceAngle(builder: ConstrainedSketchBuilder, a: LineArg, b: LineArg, angleDeg: number): ConstrainedSketchBuilder
2026
- ```
2027
-
2028
- #### `Constraint.horizontal()` — Constrain a line to be horizontal.
2029
-
2030
- ```ts
2031
- Constraint.horizontal(builder: ConstrainedSketchBuilder, line: LineArg): ConstrainedSketchBuilder
2032
- ```
2033
-
2034
- #### `Constraint.vertical()` — Constrain a line to be vertical.
2035
-
2036
- ```ts
2037
- Constraint.vertical(builder: ConstrainedSketchBuilder, line: LineArg): ConstrainedSketchBuilder
2038
- ```
2039
-
2040
- #### `Constraint.equalLength()` — Constrain two lines to have equal length.
2041
-
2042
- ```ts
2043
- Constraint.equalLength(builder: ConstrainedSketchBuilder, a: LineArg, b: LineArg): ConstrainedSketchBuilder
2044
- ```
2045
-
2046
- #### `Constraint.distance()` — Constrain the distance between two points.
2047
-
2048
- ```ts
2049
- Constraint.distance(builder: ConstrainedSketchBuilder, a: PointArg, b: PointArg, value: number): ConstrainedSketchBuilder
2050
- ```
2051
-
2052
- #### `Constraint.fix()` — Fix a point at a specific coordinate.
2053
-
2054
- ```ts
2055
- Constraint.fix(builder: ConstrainedSketchBuilder, pt: PointArg, x: number, y: number): ConstrainedSketchBuilder
2056
- ```
2057
-
2058
- #### `Constraint.coincident()` — Constrain two points to occupy the same location.
2059
-
2060
- ```ts
2061
- Constraint.coincident(builder: ConstrainedSketchBuilder, a: PointArg, b: PointArg): ConstrainedSketchBuilder
2062
- ```
2063
-
2064
- #### `Constraint.perpendicular()` — Constrain two lines to be perpendicular.
2065
-
2066
- ```ts
2067
- Constraint.perpendicular(builder: ConstrainedSketchBuilder, a: LineArg, b: LineArg): ConstrainedSketchBuilder
2068
- ```
2069
-
2070
- #### `Constraint.length()` — Constrain the length of a line.
2071
-
2072
- ```ts
2073
- Constraint.length(builder: ConstrainedSketchBuilder, line: LineArg, value: number): ConstrainedSketchBuilder
2074
- ```
2075
-
2076
- #### `addPolygon()` — Add a general polygon concept to the builder.
2077
-
2078
- Creates n vertices and n sides (CCW: `sides[i]` from `vertices[i]` → `vertices[(i+1) % n]`). Applies a `ccw` constraint to enforce winding. All dimensional constraints (lengths, angles, position) are left to the caller.
2079
-
2080
- Use `sk.addPolygon()` as the shorthand builder method.
2081
-
2082
- ```ts
2083
- const sk = constrainedSketch();
2084
- const tri = sk.addPolygon({ points: [[0,0],[100,0],[50,80]] });
2085
- sk.fix(tri.vertex(0), 0, 0);
2086
- sk.length(tri.side(0), 100);
2087
- return sk.solve().extrude(5);
2088
- ```
2089
-
2090
- ```ts
2091
- addPolygon(sk: ConstrainedSketchBuilder, options: PolygonOptions): ConstrainedPolygon
2092
- ```
2093
-
2094
- **`PolygonOptions`**
2095
- - `points: ReadonlyArray<readonly [ number, number ]>` — Initial vertex coordinates. Minimum 3 points.
2096
- - `addLoop?: boolean` — Whether to register a closed loop for sketch generation. Default: true.
2097
- - `blockRotation?: boolean` — Prevent 180° rotation (ensures first edge maintains its initial direction). Default: false.
2098
-
2099
- **`ConstrainedPolygon`**
2100
- - `vertices: PointId[]` — CCW-ordered PointIds.
2101
- - `sides: LineId[]` — CCW-ordered LineIds. `sides[i]` runs from `vertices[i]` → `vertices[(i+1) % n]`.
2102
- - `shape: ShapeId` — ShapeId for `shapeWidth`, `shapeHeight`, `shapeArea`, `shapeCentroidX/Y`.
2103
-
2104
- #### `addRect()` — Add an axis-aligned rectangle concept to the builder.
2105
-
2106
- Creates 4 vertices (CCW: bl→br→tr→tl), 4 sides, 4 structural constraints (`horizontal`/`vertical` on each side), CCW winding, a center point, a loop, and a shape. Returns a `ConstrainedRect` handle with 4 DOF (x, y, width, height).
2107
-
2108
- Use `sk.rect()` as the shorthand builder method.
2109
-
2110
- ```ts
2111
- const sk = constrainedSketch();
2112
- const r = sk.rect({ x: 0, y: 0, width: 100, height: 50 });
2113
- sk.fix(r.bottomLeft, 0, 0);
2114
- sk.length(r.bottom, 120); // override initial width
2115
- return sk.solve().extrude(10);
2116
- ```
2117
-
2118
- ```ts
2119
- addRect(sk: ConstrainedSketchBuilder, options?: RectOptions): ConstrainedRect
2120
- ```
2121
-
2122
- **`RectOptions`**
2123
-
2124
- | Option | Type | Description |
2125
- |--------|------|-------------|
2126
- | `x?` | `number` | Bottom-left x coordinate. Default: 0. |
2127
- | `y?` | `number` | Bottom-left y coordinate. Default: 0. |
2128
- | `width?` | `number` | Width (along x). Default: 10. |
2129
- | `height?` | `number` | Height (along y). Default: 10. |
2130
- | `blockRotation?` | `boolean` | Prevent 180° rotation (ensures bottom edge points rightward). Default: false. |
2131
-
2132
- **`ConstrainedRect`**
2133
-
2134
- | Option | Type | Description |
2135
- |--------|------|-------------|
2136
- | `bottom` | `LineId` | bottom-left → bottom-right |
2137
- | `right` | `LineId` | bottom-right → top-right |
2138
- | `top` | `LineId` | top-right → top-left |
2139
- | `left` | `LineId` | top-left → bottom-left |
2140
- | `center` | `PointId` | Center point constrained to the geometric center via `midpoint` on the diagonal. Can be used in further constraints: `sk.fix(rect.center, 0, 0)`, `sk.coincident(rect.center, other)`. |
2141
- | `shape` | `ShapeId` | ShapeId for `shapeWidth`, `shapeHeight`, `shapeArea`, `shapeCentroidX/Y`. |
2142
- | `vertices` | `[ PointId, PointId, PointId, PointId ]` | CCW-ordered vertex array: [bottomLeft, bottomRight, topRight, topLeft]. |
2143
- | `sides` | `[ LineId, LineId, LineId, LineId ]` | CCW-ordered side array: [bottom, right, top, left]. |
2144
- | `bottomLeft`, `bottomRight`, `topRight`, `topLeft` | | — |
2145
-
2146
- #### `addRegularPolygon()` — Add a regular n-gon concept to the builder.
2147
-
2148
- Vertices are placed at `(cx + r·cos(startAngle + i·2π/n), cy + r·sin(...))`. Equal-radius and equal-side constraints enforce regularity (4 DOF: center x/y, radius, rotation). The center point is tracked by the solver and exposed via the returned handle.
2149
-
2150
- Use `sk.regularPolygon()` as the shorthand builder method.
2151
-
2152
- ```ts
2153
- const sk = constrainedSketch();
2154
- const hex = sk.regularPolygon({ sides: 6, radius: 25 });
2155
- sk.fix(hex.center, 0, 0);
2156
- sk.length(hex.side(0), 30); // all sides change (equal constraint)
2157
- return sk.solve().extrude(5);
2158
- ```
2159
-
2160
- ```ts
2161
- addRegularPolygon(sk: ConstrainedSketchBuilder, options: RegularPolygonOptions): ConstrainedRegularPolygon
2162
- ```
2163
-
2164
- **`RegularPolygonOptions`**
2165
-
2166
- | Option | Type | Description |
2167
- |--------|------|-------------|
2168
- | `sides` | `number` | Number of sides (minimum 3). |
2169
- | `radius?` | `number` | Circumradius — distance from center to vertex. Default: 10. |
2170
- | `cx?` | `number` | Center x coordinate. Default: 0. |
2171
- | `cy?` | `number` | Center y coordinate. Default: 0. |
2172
- | `startAngle?` | `number` | Angle (in degrees) of vertex[0] measured from the +X axis (CCW positive). Default: 0 (rightmost vertex). |
2173
- | `blockRotation?` | `boolean` | Prevent 180° rotation (ensures first edge maintains its initial direction). Default: false. |
2174
-
2175
-
2176
- **`ConstrainedRegularPolygon`** extends ConstrainedPolygon
2177
- - `center: PointId` — Center point. Use `sk.fix(poly.center, x, y)` to pin location, or `sk.coincident(poly.center, other)` to align with other geometry.
2178
-
2179
- #### `circle()` — Create an analytic 2D circle for measurement, construction, and extrusion.
2180
-
2181
- ```ts
2182
- const c = circle(0, 0, 25);
2183
- c.diameter; c.circumference; c.area;
2184
- c.pointAtAngle(90); // Point2D at top (90° CCW from +X)
2185
-
2186
- // Extrude to cylinder with named faces
2187
- const cyl = c.extrude(30);
2188
- cyl.face('top'); // FaceRef (planar)
2189
- cyl.face('side'); // FaceRef (curved)
2190
-
2191
- Circle2D.fromDiameter(point(0, 0), 50);
2192
- ```
2193
-
2194
- ```ts
2195
- circle(cx: number, cy: number, radius: number): Circle2D
2196
- ```
2197
-
2198
- #### `constrainedSketch()` — Create a parametric 2D sketch driven by geometric constraints and a nonlinear solver.
2199
-
2200
- **Workflow**
2201
-
2202
- 1. Create a builder with `constrainedSketch()`.
2203
- 2. Add geometry — points, lines, circles, arcs — using the builder methods.
2204
- 3. Add constraints (`horizontal`, `length`, `fix`, etc.) to drive the geometry.
2205
- 4. Call `.solve()` to run the solver and get a `ConstraintSketch` (which extends `Sketch`).
2206
-
2207
- ```ts
2208
- const sk = constrainedSketch();
2209
- const p1 = sk.point(0, 0);
2210
- const p2 = sk.point(50, 0);
2211
- const l1 = sk.line(p1, p2);
2212
- sk.fix(p1, 0, 0);
2213
- sk.horizontal(l1);
2214
- sk.length(l1, 50);
2215
- return sk.solve().extrude(10);
2216
- ```
2217
-
2218
- **Solver status**
2219
-
2220
- ```ts
2221
- const result = sk.solve();
2222
- result.constraintMeta.status; // 'fully' | 'under' | 'over' | 'over-redundant'
2223
- result.constraintMeta.dof; // 0 = fully constrained
2224
- result.constraintMeta.maxError; // residual — should be < 1e-6
2225
- result.inspect(); // human-readable summary
2226
- result.withUpdatedConstraint('cst-5', 120); // update a dimension without rebuilding
2227
- ```
2228
-
2229
- ```ts
2230
- constrainedSketch(options?: ConstrainedSketchOptions): ConstrainedSketchBuilder
2231
- ```
2232
-
2233
- **`ConstrainedSketchOptions`**
2234
- - `strict?: boolean` — When true, adding a constraint that cannot be satisfied throws instead of silently discarding it.
2235
-
2236
- #### `line()` — Create an analytic 2D line segment between two points.
2237
-
2238
- ```ts
2239
- const l = line(0, 0, 50, 0);
2240
- l.length; l.midpoint; l.angle; l.direction;
2241
- l.parallel(10); // parallel line offset 10 (positive = left)
2242
- l.intersect(l2); // Point2D — treats lines as infinite
2243
- l.intersectSegment(l2); // Point2D or null — segments only
2244
-
2245
- Line2D.fromPointAndAngle(point(0, 0), 45, 100);
2246
- Line2D.fromPointAndDirection(point(0, 0), [1, 1], 50);
2247
- ```
2248
-
2249
- ```ts
2250
- line(x1: number, y1: number, x2: number, y2: number): Line2D
2251
- ```
2252
-
2253
- #### `point()` — Create an analytic 2D point for measurement and construction geometry.
2254
-
2255
- ```ts
2256
- const p = point(10, 20);
2257
- p.distanceTo(point(30, 40)); // Euclidean distance
2258
- p.midpointTo(point(30, 40)); // midpoint
2259
- p.translate(5, 5); // new shifted point
2260
- p.toTuple(); // [10, 20]
2261
- ```
2262
-
2263
- ```ts
2264
- point(x: number, y: number): Point2D
2265
- ```
2266
-
2267
- ---
2268
-
2269
- ## C9: Spatial Placement
2270
-
2271
- Position geometry relative to other geometry using semantic anchors.
2272
-
2273
-
2274
- #### `Points.distance()` — Euclidean distance between two 3D points.
2275
-
2276
- ```ts
2277
- Points.distance(a: Vec3, b: Vec3): number
2278
- ```
2279
-
2280
- #### `Points.midpoint()` — Center point between two 3D points.
2281
-
2282
- ```ts
2283
- Points.midpoint(a: Vec3, b: Vec3): Vec3
2284
- ```
2285
-
2286
- #### `Points.lerp()` — Linearly interpolate between two 3D points. t=0 returns a, t=1 returns b.
2287
-
2288
- ```ts
2289
- Points.lerp(a: Vec3, b: Vec3, t: number): Vec3
2290
- ```
2291
-
2292
- #### `Points.direction()` — Unit direction vector from a to b. Throws if a and b are the same point.
2293
-
2294
- ```ts
2295
- Points.direction(a: Vec3, b: Vec3): Vec3
2296
- ```
2297
-
2298
- #### `Points.offset()` — Move a point along a direction vector by a given amount.
2299
-
2300
- ```ts
2301
- Points.offset(point: Vec3, dir: Vec3, amount: number): Vec3
2302
- ```
2303
-
2304
- #### `Points.polar()` — Compute a 2D point at distance and angle (degrees) from an optional origin.
2305
-
2306
- ```ts
2307
- Points.polar(length: number, angleDeg: number, from?: [ number, number ]): [ number, number ]
2308
- ```
2309
-
2310
- ---
2311
-
2312
- ## C10: Assembly & Kinematics
2313
-
2314
- Compose parts with joints for kinematic simulation.
2315
-
2316
- #### `assembly()` — Create an assembly container with named parts, connectors, and kinematic links.
2317
-
2318
- **Use this from iteration 1 for any model with moving parts.** Do not build one static pose and retrofit motion later.
2319
-
2320
- Links are named kinematic markers in the assembly. `edgeBetweenLinks()` records structural distances or visual relationships. `addAngleBetweenLinks()` records measured, limited, or controlled angles. These APIs solve point positions, not rigid-body frames.
2321
-
2322
- `addPart(..., { mate })` attaches a part connector origin to a solved link point by translation only. It is right for markers and point-following geometry. Use `connect()` / `match()` for physical articulated parts that need full connector frame alignment and deterministic rest orientation.
2323
-
2324
- Return an `Assembly` directly to expose its connector joints and driven link controls in the editor. Moving those controls re-runs the assembly solve with the new state, so closed-loop link/edge mechanisms move through the real kinematic solver instead of a viewport-only forward-kinematics approximation.
2325
-
2326
- If no link in a connected kinematic component is fixed, ForgeCAD chooses a deterministic gauge link for solving and reports a floating-component warning.
2327
-
2328
- The legacy joint-chain APIs still exist for compatibility and exporter plumbing. New work should choose between point-link kinematics and connector-frame joints based on whether the part needs orientation.
2329
-
2330
- For multi-file assemblies, a file that returns an `Assembly` is importable via `require()` and yields an `ImportedAssembly`. Use `mergeInto()` to flatten a sub-assembly into a parent assembly.
2331
-
2332
- **Point-link example**
2333
-
2334
- This snippet mates a marker to the solved `tip` point. It does not orient a bar along `ground -> tip`.
2335
-
2336
- ```ts
2337
- const marker = box(8, 8, 4).withConnectors({
2338
- center: connector({ origin: [0, 0, 0], axis: [0, 0, 1] }),
2339
- });
2340
-
2341
- const mech = assembly("Linkage")
2342
- .link("ground", { at: [0, 0, 0], fixed: true })
2343
- .link("worldX", { at: [10, 0, 0], fixed: true })
2344
- .link("tip", { at: [40, 0, 0] })
2345
- .edgeBetweenLinks("ground", "tip", { name: "bar" })
2346
- .addAngleBetweenLinks("worldX", "ground", "tip", {
2347
- name: "theta",
2348
- control: { min: 0, max: 120, default: 30 },
2349
- })
2350
- .addPart("Tip marker", marker, { mate: { connector: "center", toLink: "tip" } });
2351
-
2352
- return mech;
2353
- ```
2354
-
2355
- ```ts
2356
- assembly(name?: string): Assembly
2357
- ```
2358
-
2359
- #### `bomToCsv()` — Convert an array of BOM rows into a CSV string.
2360
-
2361
- Produces a CSV with columns: `part`, `qty`, `material`, `process`, `tolerance`, `notes`. String values are quoted and internal double-quotes are escaped. Prefer calling `solvedAssembly.bomCsv()` directly — this function is exposed for custom BOM processing.
2362
-
2363
- ```ts
2364
- bomToCsv(rows: BomRow[]): string
2365
- ```
2366
-
2367
- **`BomRow`**: `part: string`, `qty: number`, `material?: string`, `process?: string`, `tolerance?: string`, `notes?: string`, `metadata?: PartMetadata`
2368
-
2369
- **`PartMetadata`**
2370
-
2371
- | Option | Type | Description |
2372
- |--------|------|-------------|
2373
- | `tags?` | `string \| readonly string[]` | Viewport organization tags applied to scene objects produced from this part. |
2374
- | `material?`, `process?`, `tolerance?`, `qty?`, `notes?`, `densityKgM3?`, `massKg?` | | — |
2375
-
2376
- <!-- forgecad-skill:exclude-start symbol="jointsView" reason="Compatibility-only viewport FK API. Prefer returning `Assembly` directly so controls move through the solver-backed link/edge kinematics model." -->
2377
- #### `jointsView()` — Register legacy viewport-only mechanism controls that animate returned objects without re-running the script.
2378
-
2379
- > **Not included in ForgeCAD AI skill context yet.** This API remains visible in human docs, but is intentionally omitted from shipped agent skills until it is ready for agent-first use. Compatibility-only viewport FK API. Prefer returning `Assembly` directly so controls move through the solver-backed link/edge kinematics model.
2380
-
2381
- Defines joints (revolute or prismatic), optional gear/rack couplings, and named animations. The viewport resolves transforms through the joint chain at display time — the script geometry is computed only once at rest pose.
2382
-
2383
- For `Assembly` mechanisms, prefer returning the `Assembly` directly. Returned assemblies expose solver-backed controls automatically, so link/edge closed loops move through the real assembly solver instead of this viewport-only FK layer.
2384
-
2385
- **Critical:** Solve the assembly at **rest pose** (all animated joints = 0). The viewport applies `jointsView` transforms on top of the returned scene. If geometry is already solved at non-zero angles, animation will double-rotate everything.
2386
-
2387
- ```js
2388
- // BAD — double rotation
2389
- const solved = mech.solve({ shoulder: 45, elbow: 30 });
2390
- jointsView({ joints: [{ name: 'shoulder', ... }] });
2391
- return solved;
2392
-
2393
- // GOOD — rest pose, jointsView controls all posing
2394
- const solved = mech.solve({ shoulder: 0, elbow: 0 });
2395
- jointsView({
2396
- joints: [
2397
- { name: 'shoulder', child: 'Upper Arm', default: 45, ... },
2398
- { name: 'elbow', child: 'Forearm', parent: 'Upper Arm', default: 30, ... },
2399
- ],
2400
- });
2401
- return solved;
2402
- ```
2403
-
2404
- **Pivot coordinates** are world-space positions of each joint origin at rest pose. For `addRevolute('shoulder', 'Base', 'Link', { frame: Transform.identity().translate(0, 0, 20) })` where "Base" is at world origin, the pivot is `[0, 0, 20]`.
2405
-
2406
- **Fixed attachments** that must follow a parent during animation need a zero-angle revolute joint in the chain:
2407
-
2408
- ```js
2409
- { name: 'EE_Follow', child: 'End Effector', parent: 'Last Link',
2410
- type: 'revolute', axis: [0, 0, 1], pivot: [linkLength, 0, 0],
2411
- min: 0, max: 0, default: 0 }
2412
- ```
2413
-
2414
- Animation values are interpolated linearly between keyframes. ForgeCAD does **not** auto-wrap revolute values across `-180/180`. Keep keyframe values continuous — a `-180 -> 171` jump spins the part the long way around. Use `-180 -> -189` instead. Author high-speed multi-turn joints as accumulating angles (`0, 360, 720, ...`) with `continuous: true`.
2415
-
2416
- **Mirrored revolute axes:** `default` values and animation keyframes are physical joint values, signed by the joint axis. In a bilateral mechanism, mirrored hinge axes such as `[1, 0, 0]` and `[-1, 0, 0]` need opposite physical values for the same mirrored pose. Negate the mirrored revolute track and mirror physical limits as `[min, max] -> [-max, -min]`. Prismatic tracks do not have this handedness flip.
2417
-
2418
- **Tick-based keyframes:** Omit `at` from all keyframes to auto-distribute by tick weight:
2419
-
2420
- ```js
2421
- keyframes: [
2422
- { ticks: 3, values: { Shoulder: 20 } }, // slow segment (3x weight)
2423
- { ticks: 1, values: { Shoulder: -10 } }, // fast segment (1x weight)
2424
- { values: { Shoulder: 20 } }, // last keyframe; ticks ignored
2425
- ]
2426
- // positions: 0, 0.75, 1.0
2427
- ```
2428
-
2429
- Mixing explicit `at` and omitted `at` in the same animation is not allowed.
2430
-
2431
- ```js
2432
- jointsView({
2433
- joints: [{
2434
- name: 'Shoulder', child: 'Upper Arm', parent: 'Base',
2435
- type: 'revolute', axis: [0, -1, 0], pivot: [0, 0, 46],
2436
- min: -30, max: 110, default: 15,
2437
- }],
2438
- animations: [{
2439
- name: 'Walk Cycle', duration: 1.6, loop: true,
2440
- keyframes: [
2441
- { values: { Shoulder: 20 } },
2442
- { values: { Shoulder: -10 } },
2443
- { values: { Shoulder: 20 } },
2444
- ],
2445
- }],
2446
- });
2447
- ```
2448
-
2449
- ```ts
2450
- jointsView(options?: JointsViewOptions): void
2451
- ```
2452
-
2453
- **`JointsViewOptions`**: `enabled?: boolean`, `joints?: JointViewInput[]`, `couplings?: JointViewCouplingInput[]`, `animations?: JointViewAnimationInput[]`, `defaultAnimation?: string`
2454
-
2455
- **`JointViewInput`**: `name: string`, `child: string`, `parent?: string`, `type?: JointViewType`, `axis?: JointViewAxis`, `pivot?: [ number, number, number ]`, `min?: number`, `max?: number`, `default?: number`, `unit?: string`, `hidden?: boolean`
2456
-
2457
- `JointViewCouplingInput`: `{ joint: string, terms: JointViewCouplingTermInput[], offset?: number }`
2458
-
2459
- `JointViewCouplingTermInput`: `{ joint: string, ratio?: number }`
2460
-
2461
- `JointViewAnimationInput`: `{ name: string, duration?: number, loop?: boolean, continuous?: boolean, keyframes: JointViewAnimationKeyframeInput[] }`
2462
-
2463
- **`JointViewAnimationKeyframeInput`**
2464
- - `at?: number` — Timeline position [0, 1]. If omitted from ALL keyframes, positions are auto-computed from tick weights.
2465
- - `ticks?: number` — Relative weight of the segment from this keyframe to the next (default 1). Only used in tick-based mode (when `at` is omitted). Last keyframe's ticks value is ignored.
2466
- - Also: `values: Record<string, number>`
2467
- <!-- forgecad-skill:exclude-end -->
2468
-
2469
- ---
2470
-
2471
- ## C11: Parameterization & UI
2472
-
2473
- Declare user-facing controls that drive model geometry.
2474
-
2475
- #### `Param.number()` — Declare a numeric parameter that renders as a slider in the UI.
2476
-
2477
- Each call registers a slider control. When the user moves the slider the entire script re-executes with the new value. Parameter values are also overridable from `require()` imports or the CLI `--param` flag — the `name` string is the key used in both cases.
2478
-
2479
- Default range rules when options are omitted:
2480
-
2481
- - `min` defaults to `0`
2482
- - `max` defaults to `defaultValue * 4`
2483
- - `step` is auto-calculated: `1` for integer params, `0.1` for ranges ≤ 100, `1` for larger ranges
2484
-
2485
- The `unit` option is cosmetic only — no conversion is performed. Use `integer: true` for counts, sides, quantities (rounds to whole numbers; step defaults to `1`).
2486
-
2487
- ```ts
2488
- const width = Param.number("Width", 50);
2489
- const angle = Param.number("Angle", 45, { min: 0, max: 180, unit: "°" });
2490
- const sides = Param.number("Sides", 6, { min: 3, max: 12, integer: true });
2491
- ```
2492
-
2493
- **Parameter overrides** — key must match `name` exactly:
2494
-
2495
- ```ts
2496
- // Via require()
2497
- const bracket = require("./bracket.forge.js", { Width: 80 });
2498
-
2499
- // Via CLI
2500
- // forgecad run model.forge.js --param "Wall Thickness=3"
2501
- ```
2502
-
2503
- Also available as the shorthand alias `param()`.
2504
-
2505
- ```ts
2506
- Param.number(name: string, defaultValue: number, opts?: { min?: number; max?: number; step?: number; unit?: string; integer?: boolean; reverse?: boolean; }): number
2507
- ```
2508
-
2509
- #### `Param.string()` — Declare a string parameter that renders as a text input in the UI.
2510
-
2511
- String parameters let users type free-form text — labels, names, inscriptions, file paths, etc. The `name` string is the override key.
2512
-
2513
- ```ts
2514
- const label = Param.string("Label", "Hello World");
2515
- const name = Param.string("Name", "Part-001", { maxLength: 20 });
2516
- ```
2517
-
2518
- Override via import:
97
+ ---
2519
98
 
2520
- ```ts
2521
- const tag = require("./tag.forge.js", { Label: "Custom Text" });
2522
- ```
99
+ ## C2: Boolean Combination
2523
100
 
2524
- Only available as `Param.string()` no standalone alias.
101
+ Combine same-dimension geometry using CSG set operations.
2525
102
 
2526
- ```ts
2527
- Param.string(name: string, defaultValue: string, opts?: { maxLength?: number; }): string
2528
- ```
103
+ - `difference2d(...inputs)` — Subtract one or more 2D sketches from a base sketch. → [sketch](/docs/sketch#difference2d)
104
+ - `intersection2d(...inputs)` Keep only the area where all input sketches overlap (intersection boolean). → [sketch](/docs/sketch#intersection2d)
105
+ - `union2d(...inputs)` — Combine 2D sketches into a single profile using an additive boolean union. → [sketch](/docs/sketch#union2d)
106
+ - `union(...inputs)` — Combine shapes into a single solid (additive boolean). → [core](/docs/core#union)
107
+ - `difference(...inputs)` — Subtract shapes from a base shape (subtractive boolean). → [core](/docs/core#difference)
108
+ - `intersection(...inputs)` — Keep only the overlapping volume of the input shapes (intersection boolean). → [core](/docs/core#intersection)
2529
109
 
2530
- #### `Param.bool()` — Declare a boolean parameter that renders as a checkbox in the UI.
110
+ ---
2531
111
 
2532
- Internally stored as `0`/`1`. When overriding from CLI or `require()`, pass `1` for true and `0` for false. The `name` string is the override key.
112
+ ## C3: Rigid Transform
2533
113
 
2534
- ```ts
2535
- const showHoles = Param.bool("Show Holes", true);
2536
- if (showHoles) return difference(plate, cylinder(10, 5).translate(50, 30, 0));
2537
- return plate;
2538
- ```
114
+ Reposition or reorient geometry without changing its shape.
2539
115
 
2540
- Override via import:
116
+ *No free functions — see class methods (Shape, Sketch, ConstrainedSketchBuilder).*
2541
117
 
2542
- ```ts
2543
- const pan = require("./pan.forge.js", { "Show Lid": 0 });
2544
- ```
118
+ ---
2545
119
 
2546
- Also available as the shorthand alias `boolParam()`.
120
+ ## C4: Dimensional Promotion
2547
121
 
2548
- ```ts
2549
- Param.bool(name: string, defaultValue: boolean): boolean
2550
- ```
122
+ Convert a 2D profile into a 3D solid (extrude, revolve, loft, sweep).
2551
123
 
2552
- #### `Param.choice()` — Declare a choice parameter that renders as a dropdown in the UI.
124
+ - `Curve.Blend(start, end)` — Create an exact G1 blend curve between two directed endpoints. [curves](/docs/curves#curve)
125
+ - `Curve.BlendG2(start, end)` — Create an exact G2 blend curve between two directed endpoints. → [curves](/docs/curves#curve)
126
+ - `Curve.Arc(options)` — Create an exact circular 3D arc from start, end, and start tangent. → [curves](/docs/curves#curve)
127
+ - `Curve.Line(start, end)` — Create an exact straight 3D NURBS line segment. → [curves](/docs/curves#curve)
128
+ - `Curve.Nurbs(points, options?)` — Create an exact NURBS 3D curve from control points, weights, knots, and degree. → [curves](/docs/curves#curve)
129
+ - `Curve.Fit(points, options?)` — Fit a non-rational NURBS curve that interpolates every input point. → [curves](/docs/curves#curve)
130
+ - `Curve.Trim(curve, start, end)` — Extract an exact curve segment from normalized parameter `start` to `end`. → [curves](/docs/curves#curve)
131
+ - `Curve.Reverse(curve)` — Reverse an exact curve without changing its geometry. → [curves](/docs/curves#curve)
132
+ - `Curve.Route` — Build analytic 3D line/arc routes for sweeps. → [curves](/docs/curves#curve)
133
+ - `Curve.Helix(options)` — Build helical paths and swept coils. → [curves](/docs/curves#curve)
134
+ - `Loft.station(profile, position)` — Create a loft station from a 2D profile and an axis position. → [curves](/docs/curves#curves-surfacing)
135
+ - `Loft.field(profiles, heights, options?)` — Loft by interpolating signed-distance fields instead of matching vertices. → [curves](/docs/curves#curves-surfacing)
136
+ - `Loft.leftRail(path)` — Create a guide rail that constrains the section-local negative-X side. → [curves](/docs/curves#curves-surfacing)
137
+ - `Loft.rightRail(path)` — Create a guide rail that constrains the section-local positive-X side. → [curves](/docs/curves#curves-surfacing)
138
+ - `Loft.frontRail(path)` — Create a guide rail that constrains the section-local positive-Y side. → [curves](/docs/curves#curves-surfacing)
139
+ - `Loft.backRail(path)` — Create a guide rail that constrains the section-local negative-Y side. → [curves](/docs/curves#curves-surfacing)
140
+ - `Loft.centerRail(path)` — Create a guide rail that moves section centers along the loft. → [curves](/docs/curves#curves-surfacing)
141
+ - `Loft.pathOnXz(path, y?)` — Place a 2D guide path onto the XZ plane. → [curves](/docs/curves#curves-surfacing)
142
+ - `Loft.pathOnYz(path, x?)` — Place a 2D guide path onto the YZ plane. → [curves](/docs/curves#curves-surfacing)
143
+ - `Loft.pathOnXy(path, z?)` — Place a 2D guide path onto the XY plane. → [curves](/docs/curves#curves-surfacing)
144
+ - `Loft.withGuideRails(stations, rails, options?)` — Loft through profile stations while forcing generated sections to follow guide rails. → [curves](/docs/curves#curves-surfacing)
145
+ - `Analysis.EdgeContinuity(shape, options?)` → [curves](/docs/curves#analysis)
146
+ - `Analysis.SurfaceContinuity(shape, options?)` → [curves](/docs/curves#analysis)
147
+ - `Analysis.CurvatureComb(input, options?)` → [curves](/docs/curves#analysis)
148
+ - `Analysis.SurfaceHealth(shape, options?)` → [curves](/docs/curves#analysis)
149
+ - `Analysis.BRepValidity(shape, options?)` — Validate B-rep/shell/solid structure and return closedness, manifoldness, orientation, and issue diagnostics. → [curves](/docs/curves#analysis)
150
+ - `Blend.Edge(options)` → [curves](/docs/curves#blend)
151
+ - `Blend.Surface(options)` → [curves](/docs/curves#blend)
152
+ - `Surface.Plane(options)` — Create a finite analytic plane sheet that can be trimmed, sewn, thickened, or used as a low-level face. → [curves](/docs/curves#surface)
153
+ - `Surface.Cylinder(options)` — Create a finite analytic cylindrical sheet, optionally bounded by start/end angles. → [curves](/docs/curves#surface)
154
+ - `Surface.Cone(options)` — Create a finite analytic conical or frustum sheet, optionally bounded by start/end angles. → [curves](/docs/curves#surface)
155
+ - `Surface.Sphere(options)` — Create a finite analytic spherical sheet bounded by longitude and latitude ranges. → [curves](/docs/curves#surface)
156
+ - `Surface.Torus(options)` — Create a finite analytic torus sheet bounded by major and tube angle ranges. → [curves](/docs/curves#surface)
157
+ - `Surface.Nurbs(controlGrid, options?)` — Create an exact NURBS surface from a grid of control points. → [curves](/docs/curves#surface)
158
+ - `Surface.Ruled(curveA, curveB, options?)` → [curves](/docs/curves#surface)
159
+ - `Surface.Patch(curves, options?)` — Create a smooth open surface sheet from 4 boundary curves (Coons patch). → [curves](/docs/curves#surface)
160
+ - `Surface.Boundary(input)` → [curves](/docs/curves#surface)
161
+ - `Surface.Fill(input)` → [curves](/docs/curves#surface)
162
+ - `Surface.Sew(shapes, options?)` → [curves](/docs/curves#surface)
163
+ - `Surface.Solid(input, options?)` — Sew surface faces or consume an existing sewn shell and make a solid B-rep. → [curves](/docs/curves#surface)
164
+ - `Surface.Extend(shape, options)` → [curves](/docs/curves#surface)
165
+ - `Surface.Trim(shape, tool)` → [curves](/docs/curves#surface)
166
+ - `Surface.Split(shape, tool)` → [curves](/docs/curves#surface)
167
+ - `Surface.Match(shape, options)` → [curves](/docs/curves#surface)
168
+ - `loft(profiles, heights, options?)` — Loft between multiple sketches along Z stations. → [curves](/docs/curves#loft)
169
+ - `sweep(profile, path, options?)` → [curves](/docs/curves#sweep)
170
+ - `variableSweep(spine, sections, options?)` — Sweep a variable cross-section along a 3D spine curve. → [curves](/docs/curves#variablesweep)
2553
171
 
2554
- `defaultValue` must exactly match one entry in `choices`. Returns the selected string label. Prefer `Param.choice` over `Param.number` when a slider would hide intent — named choices like `"wok"` are self-describing.
172
+ ---
2555
173
 
2556
- Overrides may be passed as the choice label string (preferred) or as a numeric index. The `name` string is the override key.
174
+ ## C5: Topology Query
2557
175
 
2558
- ```ts
2559
- const panStyle = Param.choice("Pan Style", "frying-pan", ["frying-pan", "saute-pan", "wok"]);
2560
- if (panStyle === "wok") return buildWok();
2561
- ```
176
+ Select or inspect named faces and edges on a shape.
2562
177
 
2563
- Override via import:
178
+ - `coalesceEdges(segments, tolerance?)` — Merge collinear edge segments into longer logical edges. → [core](/docs/core#coalesceedges)
179
+ - `selectEdge(shape, query?)` — Select the single best-matching edge from a shape. → [core](/docs/core#selectedge)
180
+ - `selectEdges(shape, query?)` — Select all edges from a shape that match the given query. → [core](/docs/core#selectedges)
2564
181
 
2565
- ```ts
2566
- const pan = require("./pan.forge.js", { "Pan Style": "wok" });
2567
- ```
182
+ ---
2568
183
 
2569
- Override via CLI:
184
+ ## C6: Edge Feature
2570
185
 
2571
- ```bash
2572
- forgecad run model.forge.js --param "Pan Style=wok"
2573
- ```
186
+ Modify edges of a solid — fillets, chamfers, draft, offset.
2574
187
 
2575
- Also available as the shorthand alias `choiceParam()`.
188
+ - `chamfer(shape, size, edges?)` Apply experimental chamfers (beveled edges) to one or more edges of a shape. → [core](/docs/core#chamfer)
189
+ - `draft(shape, angleDeg, pullDirection?, neutralPlaneOffset?)` — Apply a draft angle (taper) to vertical faces for mold extraction. → [core](/docs/core#draft)
190
+ - `fillet(shape, radius, edges?, segments?)` — Apply experimental fillets (rounded edges) to one or more edges of a shape. → [core](/docs/core#fillet)
191
+ - `offsetSolid(shape, thickness)` — Uniformly offset all surfaces of a solid inward or outward. → [core](/docs/core#offsetsolid)
2576
192
 
2577
- ```ts
2578
- Param.choice(name: string, defaultValue: string, choices: string[]): string
2579
- ```
193
+ ---
2580
194
 
2581
- #### `Param.list()` Declare a list parameter — an array of struct items with per-field UI controls.
195
+ ## C7: Pattern Replication
2582
196
 
2583
- Each item in the list is a struct whose fields each render as their own control (slider, checkbox, or dropdown). The user can add/remove rows up to `minItems`/`maxItems` bounds.
197
+ Duplicate geometry in regular arrangements (linear, circular, mirror).
2584
198
 
2585
- Field types:
199
+ - `circularLayout(count, radius, options?)` — Compute evenly-spaced positions around a circle. → [core](/docs/core#circularlayout)
200
+ - `circularPattern(shape, count, centerXOrOpts?, centerY?)` — Repeat a shape in a circular pattern around an axis and union the copies. → [core](/docs/core#circularpattern)
201
+ - `circularPattern2d(sketch, count, centerXOrOpts?, centerY?)` — Repeat a 2D sketch in a circular pattern around a center point and union the copies. → [core](/docs/core#circularpattern2d)
202
+ - `linearPattern(shape, count, dx, dy, dz?)` — Repeat a shape in a linear pattern along a direction vector and union the copies. → [core](/docs/core#linearpattern)
203
+ - `linearPattern2d(sketch, count, dx, dy?)` — Repeat a 2D sketch in a linear pattern and union the copies. → [core](/docs/core#linearpattern2d)
204
+ - `mirrorCopy(shape, normal)` — Mirror a shape across a plane and union the mirror with the original. → [core](/docs/core#mirrorcopy)
2586
205
 
2587
- - Boolean fields (`boolean: true` in field defs) return as `boolean`
2588
- - Choice fields (`choices: [...]` in field defs) return as `string`
2589
- - All other fields return as `number`
206
+ ---
2590
207
 
2591
- ```ts
2592
- Param.list<T extends Record<string, number | boolean | string>>(name: string, defaultItems: T[], opts: { ... }): T[]
2593
- ```
208
+ ## C8: Constraint Solving
2594
209
 
2595
- `ListParamFieldDef`: `{ min?: number, max?: number, step?: number, unit?: string, integer?: boolean, boolean?: boolean, choices?: string[] }`
210
+ Define geometry by relationships and let a solver find positions.
2596
211
 
2597
- #### `dim()` — Add a dimension annotation between two points.
212
+ - `constrainedSketch(options?)` — Create a parametric 2D sketch driven by geometric constraints and a nonlinear solver. → [sketch](/docs/sketch#constrainedsketch)
2598
213
 
2599
- Dimension annotations are purely visual callouts rendered in the viewport and report export. They do not affect geometry or constrain the model.
214
+ ---
2600
215
 
2601
- Point arguments accept 2D tuples `[x, y]`, 3D tuples `[x, y, z]`, or `Point2D` objects (Z is treated as 0 for 2D inputs).
216
+ ## C9: Spatial Placement
2602
217
 
2603
- **Ownership Rules (Report Pages)**
218
+ Position geometry relative to other geometry using semantic anchors.
2604
219
 
2605
- - `currentComponent: true` — deterministic ownership by the calling import instance. Use when authoring reusable imported parts.
2606
- - `component: "Part Name"` — route dimension to another named returned object.
2607
- - Multiple owners: dimension is shared and appears on the assembly overview page.
2608
- - No ownership set: report export infers ownership via endpoint-in-bbox.
220
+ - `Points.distance(a, b)` — Euclidean distance between two 3D points. [core](/docs/core#points)
221
+ - `Points.midpoint(a, b)` — Center point between two 3D points. → [core](/docs/core#points)
222
+ - `Points.lerp(a, b, t)` Linearly interpolate between two 3D points. [core](/docs/core#points)
223
+ - `Points.direction(a, b)` Unit direction vector from a to b. → [core](/docs/core#points)
224
+ - `Points.offset(point, dir, amount)` — Move a point along a direction vector by a given amount. → [core](/docs/core#points)
225
+ - `Points.polar(length, angleDeg, from?)` — Compute a 2D point at distance and angle (degrees) from an optional origin. → [core](/docs/core#points)
2609
226
 
2610
- ```ts
2611
- dim([-w / 2, 0, 0], [w / 2, 0, 0], { label: "Width" });
2612
- dim([0, 0, -h / 2], [0, 0, h / 2], { label: "Height", offset: 14 });
2613
- dim([0, 0, 0], [100, 0, 0], { component: "Base", color: "#00AAFF" });
2614
- ```
227
+ ---
2615
228
 
2616
- `component` (string or string[] — report ownership), `currentComponent` (boolean)
229
+ ## C10: Assembly & Kinematics
2617
230
 
2618
- ```ts
2619
- dim(from: PointArg, to: PointArg, opts?: DimOpts): void
2620
- ```
231
+ Compose parts with joints for kinematic simulation.
2621
232
 
2622
- `DimOpts`: `{ offset?: number, label?: string, color?: string, component?: string | string[], currentComponent?: boolean }`
233
+ - `assembly(name?)` Create an assembly container with named parts, connectors, and kinematic links. → [assembly](/docs/assembly#assembly)
2623
234
 
2624
- #### `dimLine()` — Add a dimension annotation along a `Line2D`.
235
+ ---
2625
236
 
2626
- Convenience wrapper around { points from a constrained-sketch `Line2D` entity. All `opts` are forwarded unchanged.
237
+ ## C11: Parameterization & UI
2627
238
 
2628
- ```ts
2629
- const a = point(0, 0);
2630
- const b = point(100, 0);
2631
- dimLine(line(a, b), { label: "Span", offset: -8 });
2632
- ```
239
+ Declare user-facing controls that drive model geometry.
2633
240
 
2634
- ```ts
2635
- dimLine(l: Line2D, opts?: DimOpts): void
2636
- ```
241
+ - `Param.number(name, defaultValue, opts?)` — Declare a numeric parameter that renders as a slider in the UI. → [core](/docs/core#parameters)
242
+ - `Param.string(name, defaultValue, opts?)` — Declare a string parameter that renders as a text input in the UI. → [core](/docs/core#parameters)
243
+ - `Param.bool(name, defaultValue)` — Declare a boolean parameter that renders as a checkbox in the UI. → [core](/docs/core#parameters)
244
+ - `Param.choice(name, defaultValue, choices)` — Declare a choice parameter that renders as a dropdown in the UI. → [core](/docs/core#parameters)
245
+ - `Param.list(name, defaultItems, opts)` — Declare a list parameter — an array of struct items with per-field UI controls. → [core](/docs/core#parameters)
246
+ - `dim(line, opts?)` — Add a dimension annotation between two points, or along an entity. → [output](/docs/output#dim)
2637
247
 
2638
248
  ---
2639
249
 
@@ -2641,25 +251,9 @@ dimLine(l: Line2D, opts?: DimOpts): void
2641
251
 
2642
252
  Extract 2D geometry from a 3D solid (section, projection).
2643
253
 
2644
- #### `faceProfile()` — Extract the boundary profile of a named face as a 2D sketch.
2645
-
2646
- The result is returned in the face's local 2D coordinate system, making it convenient for offsets, pocket profiles, or follow-up sketch operations driven by an existing face.
2647
-
2648
- ```ts
2649
- faceProfile(shape: Shape, face: FaceSelector): Sketch
2650
- ```
2651
-
2652
- #### `intersectWithPlane()` — Cross-section: slice a 3D shape with a plane and return the intersection as a 2D Sketch.
2653
-
2654
- ```ts
2655
- intersectWithPlane(shape: Shape, plane: PlaneSpec): Sketch
2656
- ```
2657
-
2658
- #### `projectToPlane()` — Orthographically project a 3D shape onto a plane and return the silhouette as a 2D Sketch.
2659
-
2660
- ```ts
2661
- projectToPlane(shape: Shape, plane: PlaneSpec): Sketch
2662
- ```
254
+ - `faceProfile(shape, face)` — Extract the boundary profile of a named face as a 2D sketch. → [core](/docs/core#faceprofile)
255
+ - `intersectWithPlane(shape, plane)` — Cross-section: slice a 3D shape with a plane and return the intersection as a 2D Sketch. → [core](/docs/core#intersectwithplane)
256
+ - `projectToPlane(shape, plane)` Orthographically project a 3D shape onto a plane and return the silhouette as a 2D Sketch. [core](/docs/core#projecttoplane)
2663
257
 
2664
258
  ---
2665
259
 
@@ -2667,253 +261,21 @@ projectToPlane(shape: Shape, plane: PlaneSpec): Sketch
2667
261
 
2668
262
  Convert geometry to external formats (STL, 3MF, SVG, DXF, G-code, PDF).
2669
263
 
2670
- #### `bom()` — Register a Bill of Materials entry for report export.
2671
-
2672
- BOM entries are accumulated during script execution and exported alongside the model in report views. Rows are grouped by normalized `description + unit`. Pass an explicit `key` to force multiple descriptions to collapse into a single line item.
2673
-
2674
- - `quantity` must be a finite number `>= 0`. A quantity of `0` is silently ignored (useful for conditional scripting with `param()`-driven counts).
2675
- - `unit` defaults to `"pieces"` when omitted or empty.
2676
- - The assembly `solved.bom()` / `solved.bomCsv()` API is separate and covers per-part assembly metadata; this function is for free-form purchased-item annotation.
2677
- - `bom()` is injected into every `.forge.js` script. Call it directly; do not write `const { bom } = require(...)`, because top-level declarations named `bom` collide with the built-in runtime name.
2678
-
2679
- ```ts
2680
- const tubeLen = param("Tube Length", 1200, { min: 300, max: 4000, unit: "mm" });
2681
- const boltCount = param("Bolt Count", 16, { min: 0, max: 200, integer: true });
2682
-
2683
- bom(tubeLen, "iron tube 30 x 20", { unit: "mm" });
2684
- bom(boltCount, "M4 bolt, 16 mm length");
2685
- bom(4, "rubber foot", { key: "foot-rubber" }); // explicit aggregation key
2686
-
2687
- // Structured metadata for richer reports:
2688
- bom(tubeLen, "rectangular steel tube", {
2689
- unit: "mm",
2690
- material: "steel",
2691
- section: [30, 20],
2692
- wall: 3,
2693
- });
2694
- ```
2695
-
2696
- ```ts
2697
- bom(quantity: number, description: string, opts?: BomOpts): void
2698
- ```
2699
-
2700
- **`BomOpts`**
2701
-
2702
- | Option | Type | Description |
2703
- |--------|------|-------------|
2704
- | `unit?` | `string` | Quantity unit label, e.g. "mm", "pieces", "kg". Default: "pieces" |
2705
- | `key?` | `string` | Optional explicit grouping key used during report aggregation. |
2706
- | `material?` | `string` | Material name, e.g. "steel", "birch plywood", "nylon" |
2707
- | `dimensions?` | `number[]` | Overall dimensions `[width, height]` or `[width, height, thickness]` in the entry's unit |
2708
- | `section?` | `number[]` | Cross-section dimensions `[w, h]` for tubes and profiles |
2709
- | `wall?` | `number` | Wall thickness for hollow sections (mm) |
2710
- | `diameter?` | `number` | Diameter for round stock, bolts, dowels (mm) |
2711
- | `length?` | `number` | Length for fasteners (mm) |
2712
- | `process?` | `string` | Manufacturing process, e.g. "laser cut", "CNC", "welded" |
2713
- | `notes?` | `string` | Free-form notes |
2714
- | `grain?` | `string` | Wood grain direction, e.g. "long", "cross" |
2715
-
2716
- #### `robotExport()` — Declare that this script should export the assembly as a SDF/URDF robot package.
2717
-
2718
- Call `robotExport()` alongside your assembly definition. The CLI commands `forgecad export sdf` and `forgecad export urdf` pick up the declaration and produce a robot package with:
2719
-
2720
- - Mesh-based inertia tensors (full 6-component, not bounding-box approximations)
2721
- - Separate collision meshes (convex hull by default — ~50–80% smaller)
2722
- - Joint limits, effort/velocity/damping/friction metadata from assembly joints
2723
-
2724
- **Collision mesh modes** (set per-link via `links["PartName"].collision`):
2725
-
2726
- | Mode | Description | Default |
2727
- |------|-------------|---------|
2728
- | `'convex'` | Convex hull (separate `_collision.stl`) | Yes |
2729
- | `'box'` | AABB primitive — fastest physics | |
2730
- | `'visual'` | Same mesh as visual — exact but slow | |
2731
- | `'none'` | No collision geometry | |
2732
-
2733
- **Unit conventions:**
2734
-
2735
- - Revolute `velocity` is in degrees/second in Forge; exporters convert to rad/s.
2736
- - Prismatic distances are in mm in Forge; exported in meters.
2737
- - `massKg` is preferred; `densityKgM3` is used when mass is unknown.
2738
- - Compatibility coupling metadata, when present, maps only the primary term (largest ratio) to `<mimic>` — SDF/URDF support single-leader mimic only. Dropped terms emit a warning.
2739
-
2740
- ```ts
2741
- const rover = assembly("Scout")
2742
- .addPart("Chassis", box(300, 220, 50).translate(0, 0, -25))
2743
- .addPart("Left Wheel", cylinder(30, 60, undefined, 48).translate(0, 0, -15))
2744
- .addRevolute("leftWheel", "Chassis", "Left Wheel", {
2745
- axis: [0, 1, 0],
2746
- frame: Transform.identity().translate(90, 140, 60),
2747
- effort: 20, velocity: 1080,
2748
- });
2749
-
2750
- robotExport({
2751
- assembly: rover,
2752
- modelName: "Scout",
2753
- links: {
2754
- Chassis: { massKg: 10 },
2755
- "Left Wheel": { massKg: 0.8 },
2756
- },
2757
- plugins: {
2758
- diffDrive: {
2759
- leftJoints: ["leftWheel"], rightJoints: ["rightWheel"],
2760
- wheelSeparationMm: 280, wheelRadiusMm: 60,
2761
- },
2762
- },
2763
- world: { generateDemoWorld: true },
2764
- });
2765
- ```
2766
-
2767
- **CLI usage**
2768
-
2769
- ```bash
2770
- forgecad export sdf model.forge.js # SDF package (Gazebo/Ignition)
2771
- forgecad export urdf model.forge.js # URDF package (ROS/PyBullet/MuJoCo)
2772
- ```
2773
-
2774
- ```ts
2775
- robotExport(options: RobotExportOptions): CollectedRobotExport
2776
- ```
2777
-
2778
- **`RobotExportOptions`**: `assembly: Assembly`, `modelName?: string`, `state?: JointState`, `static?: boolean`, `selfCollide?: boolean`, `allowAutoDisable?: boolean`, `links?: Record<string, RobotLinkExportOptions>`, `joints?: Record<string, RobotJointExportOptions>`, `plugins?: { diffDrive?: RobotDiffDrivePluginOptions; jointStatePublisher?: RobotJointStatePublisherOptions; }`, `world?: RobotWorldOptions`
2779
-
2780
- `RobotLinkExportOptions`: `{ massKg?: number, densityKgM3?: number, collision?: "visual" | "convex" | "box" | "none" }`
2781
-
2782
- `RobotJointExportOptions`: `{ effort?: number, velocity?: number, damping?: number, friction?: number }`
2783
-
2784
- **`RobotDiffDrivePluginOptions`**: `leftJoints: string[]`, `rightJoints: string[]`, `wheelSeparationMm: number`, `wheelRadiusMm: number`, `topic?: string`, `odomTopic?: string`, `tfTopic?: string`, `frameId?: string`, `odomFrameId?: string`, `maxLinearVelocity?: number`, `maxAngularVelocity?: number`, `linearAcceleration?: number`, `angularAcceleration?: number`
2785
-
2786
- `RobotJointStatePublisherOptions`: `{ enabled?: boolean, joints?: string[], topic?: string, updateRate?: number }`
2787
-
2788
- `RobotWorldOptions`: `{ name?: string, generateDemoWorld?: boolean, spawnPose?: RobotPose6, keyboardTeleop?: RobotWorldKeyboardTeleopOptions }`
2789
-
2790
- `RobotWorldKeyboardTeleopOptions`: `{ enabled?: boolean, linearStep?: number, angularStep?: number }`
2791
-
2792
- **`CollectedRobotExport`**: `modelName: string`, `assembly: AssemblyDefinition`, `state: JointState`, `static: boolean`, `selfCollide: boolean`, `allowAutoDisable: boolean`, `links: Record<string, RobotLinkExportOptions>`, `joints: Record<string, RobotJointExportOptions>`, `plugins: { diffDrive?: RobotDiffDrivePluginOptions; jointStatePublisher?: RobotJointStatePublisherOptions; }`, `world: RobotWorldOptions | null`
2793
-
2794
- **`AssemblyDefinition`**: `name: string`, `parts: AssemblyPartDef[]`, `joints: AssemblyJointDef[]`, `jointCouplings: AssemblyJointCouplingDef[]`, `kinematics: AssemblyKinematicGraphDef`
2795
-
2796
- `AssemblyPartDef`: `{ name: string, part: AssemblyPart, base: Transform, metadata?: PartMetadata, mates: AssemblyPartMateInput[] }`
2797
-
2798
- **`PartMetadata`**
2799
-
2800
- | Option | Type | Description |
2801
- |--------|------|-------------|
2802
- | `tags?` | `string \| readonly string[]` | Viewport organization tags applied to scene objects produced from this part. |
2803
- | `material?`, `process?`, `tolerance?`, `qty?`, `notes?`, `densityKgM3?`, `massKg?` | | — |
2804
-
2805
- **`AssemblyPartMateInput`**
2806
- - `connector: string` — Name of a connector declared on the part (via `withConnectors()`).
2807
- - `toLink: string` — Name of the link this connector's origin is pinned to.
2808
- - `aimLink?: string` — Optional second link to orient toward. When set, the part is rotated so the connector's **axis** aims from `toLink` toward `aimLink`, posing an oriented bone instead of only translating it. For full pose without relying on a connector axis, declare a second mate (two connectors → two links).
2809
-
2810
- **`AssemblyJointDef`**: `name: string`, `type: JointType`, `parent: string`, `child: string`, `frame: Transform`, `axis: Vec3`, `min?: number`, `max?: number`, `defaultValue: number`, `unit?: string`, `effort?: number`, `velocity?: number`, `damping?: number`, `friction?: number`, `connectorRefs?: JointConnectorRefs`
2811
-
2812
- `JointConnectorRefs`: `{ parent: string, child: string, parentAlign?: PortAlign, childAlign?: PortAlign }`
2813
-
2814
- `AssemblyJointCouplingDef`: `{ joint: string, terms: JointCouplingTermRecord[], offset: number }`
2815
-
2816
- `JointCouplingTermRecord`: `{ joint: string, ratio: number }`
2817
-
2818
- `AssemblyKinematicGraphDef`: `{ links: AssemblyLinkDef[], edges: AssemblyEdgeBetweenLinksDef[], angles: AssemblyAngleBetweenLinksDef[] }`
2819
-
2820
- `AssemblyLinkDef`: `{ name: string, at: Vec3, fixed: boolean, metadata?: Record<string, unknown> }`
2821
-
2822
- **`AssemblyEdgeBetweenLinksDef`**: `name: string`, `a: string`, `b: string`, `length: number | null`, `min?: number`, `max?: number`, `visualOnly: boolean`, `control?: AssemblyKinematicControlOptions`, `metadata?: Record<string, unknown>`
2823
-
2824
- `AssemblyKinematicControlOptions`: `{ min?: number, max?: number, default?: number, unit?: string }`
2825
-
2826
- **`AssemblyAngleBetweenLinksDef`**: `name: string`, `a: string`, `b: string`, `c: string`, `target?: number`, `min?: number`, `max?: number`, `control?: AssemblyKinematicControlOptions`, `metadata?: Record<string, unknown>`
2827
-
2828
- #### `sheetMetal()` — Create a parametric sheet metal part with flanges, bend allowances, and flat-pattern unfolding.
2829
-
2830
- `sheetMetal()` keeps one semantic model and derives both a folded 3D solid and an accurate flat pattern from it. The K-factor bend allowance is applied during unfolding. This is a strict v1 subset — it does not infer sheet metal from arbitrary solids.
2831
-
2832
- **Recommended authoring order:**
2833
-
2834
- 1. Define the base panel + thickness + bend parameters.
2835
- 2. Chain `.flange()` calls for each edge. Validate with `.folded()` and `.flatPattern()` before adding cutouts.
2836
- 3. Add panel cutouts, then flange cutouts one region at a time.
2837
- 4. Validate after each new cutout region.
2838
-
2839
- **v1 limitations:** one base panel, up to four 90° edge flanges, constant thickness, explicit K-factor, rectangular corner reliefs, planar cutouts only. No hems, jogs, lofted bends, non-90° flanges, or bend-region cutouts.
2840
-
2841
- ```ts
2842
- const cover = sheetMetal({
2843
- panel: { width: 180, height: 110 },
2844
- thickness: 1.5,
2845
- bendRadius: 2,
2846
- bendAllowance: { kFactor: 0.42 },
2847
- cornerRelief: { size: 4 },
2848
- })
2849
- .flange('top', { length: 18 })
2850
- .flange('right', { length: 18 })
2851
- .flange('bottom', { length: 18 })
2852
- .flange('left', { length: 18 })
2853
- .cutout('panel', rect(72, 36), { selfAnchor: 'center' })
2854
- .cutout('flange-right', roundedRect(26, 10, 5), { selfAnchor: 'center' });
2855
-
2856
- const folded = cover.folded();
2857
- const flat = cover.flatPattern();
2858
- ```
2859
-
2860
- ```ts
2861
- sheetMetal(options: SheetMetalOptions): SheetMetalPart
2862
- ```
2863
-
2864
- **`SheetMetalOptions`**
2865
-
2866
- | Option | Type | Description |
2867
- |--------|------|-------------|
2868
- | `panel` | `{ width: number; height: number; }` | Base panel dimensions. This is the flat blank before flanges are applied. |
2869
- | `thickness` | `number` | Sheet thickness in mm. Applied uniformly across the panel and all flanges. |
2870
- | `bendRadius` | `number` | Inside bend radius in mm. Must be ≥ 0. Typically 0.5–2× the sheet thickness. |
2871
- | `bendAllowance` | `{ kFactor: number; }` | Bend allowance model used when computing the flat-pattern developed length. Currently only K-factor is supported. The K-factor (0–1) describes how far the neutral axis sits from the inner bend surface. Typical values: - Soft materials / large radius: 0.50 - General sheet steel: 0.42–0.44 - Hard materials / tight radius: 0.30–0.38 |
2872
- | `cornerRelief?` | `{ kind?: "rect"; size: number; }` | Corner relief cut at each bend intersection. Prevents material overlap when two flanges meet at a corner. Defaults to a rectangular relief sized to `bendRadius + thickness` if omitted. |
2873
-
2874
- #### `sketchToDxf()` — Export a 2D sketch as a DXF string (R12/AC1009 — maximally compatible).
2875
-
2876
- For regular sketches, each polygon loop becomes a closed `LWPOLYLINE`. For constrained sketches, exports raw `LINE`, `CIRCLE`, and `ARC` entities from the constraint edge geometry, which preserves internal/shared edges that `toPolygons()` would merge away.
2877
-
2878
- The R12 format is chosen for maximum compatibility with CAM tools, laser-cutter software, and older CAD readers.
2879
-
2880
- ```ts
2881
- const s = rect(100, 60);
2882
- const dxf = sketchToDxf(s, { layer: 'cut' });
2883
- ```
2884
-
2885
- ```ts
2886
- sketchToDxf(sketch: Sketch, options?: SketchDxfOptions): string
2887
- ```
2888
-
2889
- **`SketchDxfOptions`**
2890
- - `layer?: string` — DXF layer name. Default: "0"
2891
- - `colorIndex?: number` — DXF color index (1–255, AutoCAD ACI). Default: 7 (white/black)
2892
-
2893
- #### `sketchToSvg()` — Export a 2D sketch as an SVG string.
2894
-
2895
- For regular sketches, exports filled polygon regions. For constrained sketches, exports raw edge geometry (LINE, ARC, CIRCLE) which preserves internal/shared edges that `toPolygons()` would merge away.
2896
-
2897
- The SVG uses the sketch's native coordinate system (Y-up) with a CSS transform that flips Y so the output renders correctly in SVG's Y-down space. Coordinates are in sketch units (typically mm).
2898
-
2899
- ```ts
2900
- const s = rect(100, 60);
2901
- const svg = sketchToSvg(s, { stroke: '#333', strokeWidth: 0.8 });
2902
- ```
2903
-
2904
- ```ts
2905
- sketchToSvg(sketch: Sketch, options?: SketchSvgOptions): string
2906
- ```
2907
-
2908
- **`SketchSvgOptions`**
2909
-
2910
- | Option | Type | Description |
2911
- |--------|------|-------------|
2912
- | `stroke?` | `string` | Stroke color. Default: "black" |
2913
- | `strokeWidth?` | `number` | Stroke width in sketch units. Default: 0.5 |
2914
- | `fill?` | `string` | Fill color. Default: "none" |
2915
- | `padding?` | `number` | Padding around the sketch bounding box in sketch units. Default: 2 |
2916
- | `pixelsPerUnit?` | `number` | If set, scale so 1 sketch-unit = this many px. Otherwise auto-fit. |
264
+ - `Laser.panel(name, width, height, thickness, options?)` — Create a rectangular flat panel with 4 named edges. → [sheet-metal](/docs/sheet-metal#laser)
265
+ - `Laser.part(name, profile, thickness, edges?, options?)` — Create a flat part from an arbitrary 2D profile with user-named edges. → [sheet-metal](/docs/sheet-metal#laser)
266
+ - `Laser.fingerJoint(partA, edgeNameA, partB, edgeNameB, options?)` Connect two parts with finger joints along the named edges. [sheet-metal](/docs/sheet-metal#laser)
267
+ - `Laser.tabSlot(partA, edgeNameA, partB, edgeNameB, options?)` — Connect two parts with tab-and-slot joints along the named edges. → [sheet-metal](/docs/sheet-metal#laser)
268
+ - `Laser.kit(options?)` Create a LaserKit container for a flat-pack project. [sheet-metal](/docs/sheet-metal#laser)
269
+ - `Laser.assemblyPreview(parts, joints, options?)` Generate a 3D assembly preview from flat parts and their joint records. → [sheet-metal](/docs/sheet-metal#laser)
270
+ - `Laser.instructions(parts, joints, options?)` Generate step-by-step assembly instructions from flat parts and joints. → [sheet-metal](/docs/sheet-metal#laser)
271
+ - `Laser.formatInstructions(result)` Format assembly instructions as a human-readable text document. [sheet-metal](/docs/sheet-metal#laser)
272
+ - `Laser.lookupKerf(material, thickness, laserType?)` — Look up kerf for a material + thickness + laser combo in `Laser.COMMON_KERFS`. → [sheet-metal](/docs/sheet-metal#laser)
273
+ - `Laser.COMMON_KERFS` — Common full-kerf values by material, thickness, and laser type. → [sheet-metal](/docs/sheet-metal#laser)
274
+ - `bom(quantity, description, opts?)` Register a Bill of Materials entry for report export. → [output](/docs/output#bom)
275
+ - `robotExport(options)` Declare that this script should export the assembly as a SDF/URDF robot package. → [output](/docs/output#robotexport)
276
+ - `sheetMetal(options)` — Create a parametric sheet metal part with flanges, bend allowances, and flat-pattern unfolding. → [sheet-metal](/docs/sheet-metal#sheetmetal)
277
+ - `sketchToDxf(sketch, options?)` Export a 2D sketch as a DXF string (R12/AC1009 — maximally compatible). → [output](/docs/output#sketchtodxf)
278
+ - `sketchToSvg(sketch, options?)` Export a 2D sketch as an SVG string. → [output](/docs/output#sketchtosvg)
2917
279
 
2918
280
  ---
2919
281
 
@@ -2921,562 +283,41 @@ sketchToSvg(sketch: Sketch, options?: SketchSvgOptions): string
2921
283
 
2922
284
  Control viewport appearance and debugging aids.
2923
285
 
2924
- #### `Viewport.label()` — Add a render-only viewport label at a world-space point.
2925
-
2926
- `Viewport.label()` is for temporary review, debug, tutorial, or explicitly requested presentation overlays. It does not create sketches, meshes, B-rep topology, exported text, or face labels, so it stays off the OCCT path. Default production models should be understandable from physical geometry, materials, part boundaries, and named objects, not viewport annotations.
2927
-
2928
- Use `text2d()` only when the letters should become manufactured geometry, such as raised lettering, engraved serial numbers, or exported nameplates.
2929
-
2930
- Labels are collected during script execution and rendered by the viewport as lightweight overlay annotations. They are ignored by exports and do not appear in `objects`.
2931
-
2932
- ```js
2933
- Viewport.label('Bearing bore', [0, 0, 18], {
2934
- color: '#f8fafc',
2935
- background: '#0f172acc',
2936
- offset: [0, 0, 8],
2937
- anchor: 'bottom',
2938
- });
2939
-
2940
- return box(40, 30, 12);
2941
- ```
2942
-
2943
- ```ts
2944
- Viewport.label(text: string, at: [ number, number, number ], options?: RenderLabelOptions): void
2945
- ```
2946
-
2947
- **`RenderLabelOptions`**
2948
-
2949
- | Option | Type | Description |
2950
- |--------|------|-------------|
2951
- | `color?` | `string` | Text color as any CSS color string. |
2952
- | `background?` | `string` | Background color as any CSS color string. Use `'transparent'` for no pill background. |
2953
- | `size?` | `number` | Font size in CSS pixels. Defaults to 12. |
2954
- | `offset?` | `[ number, number, number ]` | Additional world-space offset from `at`. |
2955
- | `anchor?` | `RenderLabelAnchor` | Which point of the label box is anchored to `at`. Defaults to `'center'`. |
2956
- | `alwaysOnTop?` | `boolean` | When false, the label is hidden when occluded by scene geometry. Defaults to true. |
2957
-
2958
- #### `verify.that()` — Custom predicate check.
2959
-
2960
- ```ts
2961
- verify.that(label: string, check: () => boolean, message?: string): void
2962
- ```
2963
-
2964
- #### `verify.equal()` — Check that two numbers are approximately equal (within tolerance).
2965
-
2966
- ```ts
2967
- verify.equal(label: string, actual: number, expected: number, tolerance?: number, message?: string): void
2968
- ```
2969
-
2970
- #### `verify.notEqual()` — Check that two numbers are NOT equal (differ by more than tolerance).
2971
-
2972
- ```ts
2973
- verify.notEqual(label: string, actual: number, unexpected: number, tolerance?: number, message?: string): void
2974
- ```
2975
-
2976
- #### `verify.greaterThan()` — Check that actual > min.
2977
-
2978
- ```ts
2979
- verify.greaterThan(label: string, actual: number, min: number, message?: string): void
2980
- ```
2981
-
2982
- #### `verify.lessThan()` — Check that actual < max.
2983
-
2984
- ```ts
2985
- verify.lessThan(label: string, actual: number, max: number, message?: string): void
2986
- ```
2987
-
2988
- #### `verify.inRange()` — Check that min <= actual <= max.
2989
-
2990
- ```ts
2991
- verify.inRange(label: string, actual: number, min: number, max: number, message?: string): void
2992
- ```
2993
-
2994
- #### `verify.centersCoincide()` — Check that the bounding-box centers of two shapes coincide within tolerance (mm).
2995
-
2996
- ```ts
2997
- verify.centersCoincide(label: string, a: ShapeLike, b: ShapeLike, tolerance?: number): void
2998
- ```
2999
-
3000
- `ShapeLike`: `{ min: number[], max: number[] }`
3001
-
3002
- #### `verify.connectorDistance()` — Check the distance between two named connectors on a shape or group.
3003
-
3004
- 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.
3005
-
3006
- ```ts
3007
- verify.connectorDistance("leg is seated", bench, "Rail.leg_0", "Leg0.head", 0, 0.01);
3008
- ```
3009
-
3010
- ```ts
3011
- verify.connectorDistance(label: string, target: ConnectorDistanceLike, connectorA: string, connectorB: string, expected?: number, tolerance?: number): void
3012
- ```
3013
-
3014
- #### `verify.physicalComponentCount()` — Declare the expected physical connectivity component count for the returned visible model.
3015
-
3016
- 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.
3017
-
3018
- 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.
3019
-
3020
- ```ts
3021
- verify.physicalComponentCount("vise is one connected installed assembly", 1);
3022
- ```
3023
-
3024
- ```ts
3025
- verify.physicalComponentCount(label: string, expected: number): void
3026
- ```
3027
-
3028
- #### `verify.intentionalOverlap()` — Declare that two visible objects intentionally overlap because the overlap is real manufacturing intent.
3029
-
3030
- 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.
3031
-
3032
- `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.
3033
-
3034
- ```ts
3035
- verify.intentionalOverlap("rubber grip is overmolded on handle", rubberGrip, handleCore, "overmolded insert");
3036
- ```
3037
-
3038
- ```ts
3039
- verify.intentionalOverlap(label: string, a: ShapeLike, b: ShapeLike, reason: string): void
3040
- ```
3041
-
3042
- #### `verify.notColliding()` — Check that two shapes do not share positive volume.
3043
-
3044
- Face-to-face contact is allowed; use `verify.minClearance()` when an actual running gap is required.
3045
-
3046
- ```ts
3047
- verify.notColliding(label: string, a: ShapeLike, b: ShapeLike, searchLength?: number): void
3048
- ```
3049
-
3050
- #### `verify.minClearance()` — Check that a minimum clearance gap exists between two shapes.
3051
-
3052
- ```ts
3053
- verify.minClearance(label: string, a: ShapeLike, b: ShapeLike, minGap: number, searchLength?: number): void
3054
- ```
3055
-
3056
- #### `verify.clearanceBetween()` — Check that the clearance gap between two shapes is inside an allowed range.
3057
-
3058
- 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.
3059
-
3060
- 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.
3061
-
3062
- 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.
3063
-
3064
- ```ts
3065
- verify.clearanceBetween("cover is seated on gasket", cover, gasket, -0.01, 0.05);
3066
- verify.clearanceBetween("carriage runs inside rail", carriage, rail, 0.2, 0.5);
3067
- ```
3068
-
3069
- ```ts
3070
- verify.clearanceBetween(label: string, a: ShapeLike, b: ShapeLike, minGap: number, maxGap: number, searchLength?: number): void
3071
- ```
3072
-
3073
- #### `verify.parallel()` — Check that two face normals are parallel (within toleranceDeg degrees).
3074
-
3075
- ```ts
3076
- verify.parallel(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void
3077
- ```
3078
-
3079
- `FaceRefLike`: `{ normal: [ number, number, number ], center: [ number, number, number ] }`
3080
-
3081
- #### `verify.perpendicular()` — Check that two face normals are perpendicular (within toleranceDeg degrees).
3082
-
3083
- ```ts
3084
- verify.perpendicular(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void
3085
- ```
3086
-
3087
- #### `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.
3088
-
3089
- ```ts
3090
- verify.coplanar(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number, toleranceMm?: number): void
3091
- ```
3092
-
3093
- #### `verify.faceAt()` — Check that a face center lies at a specific position (within toleranceMm).
3094
-
3095
- ```ts
3096
- verify.faceAt(label: string, face: FaceRefLike, expectedPos: [ number, number, number ], toleranceMm?: number): void
3097
- ```
3098
-
3099
- #### `verify.sameDirection()` — Check that two face normals point in the same direction (not antiparallel). Stricter than parallel — both |angle| AND sign must match.
3100
-
3101
- ```ts
3102
- verify.sameDirection(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void
3103
- ```
3104
-
3105
- #### `verify.isEmpty()` — Check that a shape is empty.
3106
-
3107
- ```ts
3108
- verify.isEmpty(label: string, shape: ShapeLike, message?: string): void
3109
- ```
3110
-
3111
- #### `verify.notEmpty()` — Check that a shape is NOT empty.
3112
-
3113
- ```ts
3114
- verify.notEmpty(label: string, shape: ShapeLike, message?: string): void
3115
- ```
3116
-
3117
- #### `verify.volumeApprox()` — Check that a shape's volume is approximately equal to expected (mm³).
3118
-
3119
- ```ts
3120
- verify.volumeApprox(label: string, shape: ShapeLike, expected: number, tolerance?: number): void
3121
- ```
3122
-
3123
- #### `verify.areaApprox()` — Check that a shape's surface area is approximately equal to expected (mm²).
3124
-
3125
- ```ts
3126
- verify.areaApprox(label: string, shape: ShapeLike, expected: number, tolerance?: number): void
3127
- ```
3128
-
3129
- #### `verify.boundingBoxSize()` — Check that a shape's bounding box has approximately the given size.
3130
-
3131
- ```ts
3132
- verify.boundingBoxSize(label: string, shape: ShapeLike, expectedSize: [ number, number, number ], tolerance?: number): void
3133
- ```
3134
-
3135
- #### `verify.edgeContinuity()` — Check that every sampled seam on a shape meets a requested continuity threshold.
3136
-
3137
- ```ts
3138
- verify.edgeContinuity(label: string, shape: ShapeLike, options?: EdgeContinuityThresholds): void
3139
- ```
3140
-
3141
- **`EdgeContinuityThresholds`**: `continuity?: SurfaceContinuity`, `samples?: number`, `positionTolerance?: number`, `tangentToleranceDeg?: number`, `curvatureTolerance?: number`
3142
-
3143
- #### `verify.noTinyEdges()` — Check that a shape has no tiny edges below the requested threshold.
3144
-
3145
- ```ts
3146
- verify.noTinyEdges(label: string, shape: ShapeLike, threshold?: number): void
3147
- ```
3148
-
3149
- #### `verify.noSliverFaces()` — Check that a shape has no sliver faces below the requested score threshold.
3150
-
3151
- ```ts
3152
- verify.noSliverFaces(label: string, shape: ShapeLike, threshold?: number): void
3153
- ```
3154
-
3155
- #### `verify.noSelfIntersection()` — Best-effort exact-shape validity guard for self-intersections or broken B-Rep topology.
3156
-
3157
- ```ts
3158
- verify.noSelfIntersection(label: string, shape: ShapeLike): void
3159
- ```
3160
-
3161
- #### `explodeView()` — Configure how the viewport explode slider offsets returned objects.
3162
-
3163
- Offsets are resolved from the returned object tree, not a flat list. In `radial` mode each node follows its parent branch direction, then fans locally from the immediate parent center — nested assemblies peel apart level by level. In fixed-axis or fixed-vector modes, the branch follows that axis/vector but nested descendants fan out perpendicular by default.
3164
-
3165
- Multiple calls merge — later values override earlier ones on a per-key basis. `byName` and `byPath` maps are merged entry-by-entry.
3166
-
3167
- For programmatic explode applied before returning (without the slider), use `lib.explode()` instead.
3168
-
3169
- ```js
3170
- explodeView({
3171
- amountScale: 1.2,
3172
- stages: [0.35, 0.8],
3173
- mode: 'radial',
3174
- byPath: { 'Drive/Shaft': { direction: [1, 0, 0], stage: 1.6 } },
3175
- });
3176
- ```
3177
-
3178
- ```ts
3179
- explodeView(options?: ExplodeViewOptions): void
3180
- ```
3181
-
3182
- **`ExplodeViewOptions`**
3183
-
3184
- | Option | Type | Description |
3185
- |--------|------|-------------|
3186
- | `enabled?` | `boolean` | Set false to disable viewport explode offsets for this script output. |
3187
- | `amountScale?` | `number` | Scales the UI explode amount. Default: 1 |
3188
- | `stages?` | `number[]` | Per-depth stage multipliers (depth 1 = first level). If depth exceeds this array, the last value is reused. Default when omitted: reciprocal depth (1, 1/2, 1/3, ...) |
3189
- | `mode?` | `ExplodeViewDirection` | Global direction mode fallback. Default: 'radial' |
3190
- | `axisLock?` | `ExplodeAxis` | Global axis lock fallback. |
3191
- | `byName?` | `Record<string, ExplodeViewDirective>` | Per-object overrides by final object name. |
3192
- | `byPath?` | `Record<string, ExplodeViewDirective>` | Per-tree-path overrides using slash-separated object tree segments. |
3193
-
3194
- **`ExplodeDirective`**
3195
- - `stage?: number` — Multiplier applied to `amount` for this node
3196
- - `direction?: ExplodeDirection` — Direction mode for this node
3197
- - `axisLock?: ExplodeAxis` — Optional axis lock after direction is resolved
3198
-
3199
- #### `cutPlane()` — Define a named section plane for inspecting internal geometry.
3200
-
3201
- Registers a cut plane that appears as a toggle in the viewport View Panel. When enabled, geometry on the positive side of the plane (the side the normal points toward) is clipped away, revealing the internal cross-section. The newly exposed section faces render with a hatched overlay; pre-existing coplanar boundary faces are left unhatched.
3202
-
3203
- Planes are registered once per script run. The viewport toggle state (on/off) persists across parameter changes without re-running the script. The `exclude` option only works correctly when the excluded object names are stable across parameter changes.
3204
-
3205
- Accepts two overloads: `cutPlane(name, normal, offset?, options?)` or `cutPlane(name, normal, options?)` where options may include `offset`.
3206
-
3207
- ```js
3208
- const cutZ = param('Cut Height', 10, { min: -50, max: 50, unit: 'mm' });
3209
- cutPlane('Inspection', [0, 0, 1], cutZ, { exclude: ['Probe', 'Fasteners'] });
3210
- ```
3211
-
3212
- Overloads:
3213
-
3214
- - `cutPlane(name: string, normal: [ number, number, number ], offset?: number, options?: CutPlaneOptions): void`
3215
- - `cutPlane(name: string, normal: [ number, number, number ], options?: CutPlaneOptions): void`
3216
-
3217
- **`CutPlaneOptions`**
3218
- - `offset?: number` — Optional offset along the plane normal (primarily for object-form overload).
3219
- - `exclude?: CutPlaneExcludeInput` — Object names to keep uncut for this plane.
3220
-
3221
- #### `compareWith()` — Declare a reference model for comparison inspection.
3222
-
3223
- `compareWith()` lets a model carry its own comparison target for inspection workflows. `forgecad inspect compare overlay model.forge.js` uses this reference to render the same Difference Only comparison overlay as the live viewport. Amber marks candidate mismatch evidence, cyan marks reference mismatch evidence, and faint model context keeps the overlay readable. When the CLI can resolve the referenced file, the manifest also includes the same geometric score produced by `forgecad compare 3d`.
3224
-
3225
- The path is resolved relative to the file that calls `compareWith()`. It may point to another `.forge.js` file or an imported CAD asset such as `.stl`, `.obj`, `.3mf`, `.step`, or `.stp`.
3226
-
3227
- ```js
3228
- compareWith('./reference.3mf', { align: 'center', toleranceMm: 0.25, samples: 3000 });
3229
- return rebuiltBearing;
3230
- ```
3231
-
3232
- ```ts
3233
- compareWith(path: string, options?: CompareWithOptions): void
3234
- ```
3235
-
3236
- **`CompareWithOptions`**
3237
-
3238
- | Option | Type | Description |
3239
- |--------|------|-------------|
3240
- | `align?` | `CompareAlignMode` | Candidate alignment before scoring. Defaults to no automatic alignment. |
3241
- | `toleranceMm?` | `number` | Distance tolerance in model units for coverage scoring. Defaults to the comparison scorer's auto tolerance. |
3242
- | `samples?` | `number` | Surface samples per direction for numeric scoring. Defaults to the comparison scorer's standard sample count. |
3243
- | `label?` | `string` | Human label for the reference model in inspection manifests. |
3244
-
3245
- #### `mock()` — Register a mock (context) object for visualization and inspection.
3246
-
3247
- Mock objects appear in the viewport and inspection analysis when you run a file directly, but are excluded when the file is imported via `require()`. This lets you model the surrounding context — walls, bolts, mating parts — without polluting the module's exports.
3248
-
3249
- The shape is returned unchanged, so you can reference it for alignment, dimensioning, and `verify` checks.
3250
-
3251
- Mock objects participate in focused inspection commands such as `forgecad inspect fit interference` and `forgecad inspect physical gaps`. Their names appear with a `(mock)` suffix in reports.
3252
-
3253
- In the viewport, mock objects render at reduced opacity so they are visually distinct from real geometry.
3254
-
3255
- ```ts
3256
- // bracket.forge.js
3257
- const wall = mock(box(100, 200, 10).translate(0, 0, -5), "wall");
3258
- const bolt = mock(cylinder(3, 15).translate(10, 15, 0), "bolt");
3259
-
3260
- const bracket = box(20, 30, 5);
3261
- verify.notColliding("bracket vs wall", bracket, wall);
3262
-
3263
- return bracket;
3264
- // When imported: only bracket is exported
3265
- // When run directly: bracket + wall + bolt all visible
3266
- ```
3267
-
3268
- ```ts
3269
- mock<T extends Shape>(shape: T, name?: string): T
3270
- ```
3271
-
3272
- #### `scene()` — Configure the scene environment for the current script execution.
3273
-
3274
- Controls camera position, named render views, optional model journeys, lighting rig, background color or gradient, atmospheric fog, environment maps, post-processing effects, and capture parameters for the `forgecad capture` command. Multiple calls merge — later values override earlier ones on a per-key basis, so you can split configuration across multiple `scene()` calls.
3275
-
3276
- When `lights` is specified, **all** default lights are removed. You must include your own ambient light or the scene will be fully dark.
3277
-
3278
- Setting `camera.position` overrides auto-framing — the viewport will no longer auto-fit the geometry on script reload.
3279
-
3280
- Named render views let scripts check in repeatable cameras next to the model code. The canonical shape is `{ camera: { position, target } }`, and a direct camera shorthand `{ position, target }` is also accepted. Use the canonical shape when you may add view metadata later. Use it from the CLI with `--view hero` on `forgecad render 3d`, `forgecad render hq`, or `forgecad capture`.
3281
-
3282
- Model journeys let scripts check in a compact guided path through named objects. Each journey has ordered `steps`; each step can name a `focus` target by object name/tree path, provide a caption, and optionally provide an explicit camera. In the viewer, journeys are opt-in: they appear as a small Explore control and do not move the camera until the user starts them. Use `forgecad run model.forge.js --journeys` or `--journeys-json` to inspect resolved targets.
3283
-
3284
- Post-processing effects (`bloom`, `vignette`, `grain`) work in the browser viewport only. The CLI applies camera, lights, background, fog, and `toneMappingExposure` but skips shader effects.
3285
-
3286
- All numeric values accept `param()` expressions.
3287
-
3288
- ```js
3289
- scene({
3290
- background: { top: '#000814', bottom: '#001d3d' },
3291
- camera: { position: [160, -120, 100], target: [0, 0, 50], fov: 52 },
3292
- views: {
3293
- hero: {
3294
- camera: { position: [180, -140, 90], target: [0, 0, 25], up: [0, 0, 1], fov: 38 },
3295
- },
3296
- side: { position: [240, 0, 70], target: [0, 0, 25], fov: 34 },
3297
- },
3298
- journeys: {
3299
- grandTour: {
3300
- title: 'Grand Tour',
3301
- startsAt: 'overview',
3302
- steps: [
3303
- { id: 'overview', focus: 'Solar System', caption: 'Start with the whole model.' },
3304
- { id: 'earth', focus: 'Earth', caption: 'Fit and inspect Earth.' },
3305
- ],
3306
- },
3307
- },
3308
- lights: [
3309
- { type: 'ambient', color: '#001233', intensity: 0.08 },
3310
- { type: 'point', position: [120, -80, 130], color: '#00f5d4', intensity: 4, distance: 400, decay: 1 },
3311
- { type: 'point', position: [-100, 60, 20], color: '#f72585', intensity: 3, distance: 350 },
3312
- { type: 'directional', position: [50, -30, 200], color: '#ffd60a', intensity: 1.2 },
3313
- { type: 'hemisphere', skyColor: '#003566', groundColor: '#000814', intensity: 0.2 },
3314
- ],
3315
- fog: { color: '#000814', near: 100, far: 450 },
3316
- postProcessing: {
3317
- bloom: { intensity: param('bloom', 1.5, 0, 4), threshold: 0.5, radius: 0.7 },
3318
- vignette: { darkness: 0.8, offset: 0.25 },
3319
- grain: { intensity: 0.08 },
3320
- toneMappingExposure: param('exposure', 1.5, 0.5, 4),
3321
- },
3322
- });
3323
- ```
3324
-
3325
- ```ts
3326
- scene(options: SceneOptions): void
3327
- ```
3328
-
3329
- **`SceneOptions`**
3330
-
3331
- | Option | Type | Description |
3332
- |--------|------|-------------|
3333
- | `capture?` | `SceneCaptureConfig` | Default capture parameters for `forgecad capture` — CLI flags override these. |
3334
- | `background?`, `camera?`, `views?`, `journeys?`, `lights?`, `environment?`, `fog?`, `postProcessing?`, `ground?` | | — |
3335
-
3336
- `SceneBackgroundGradient`: `{ top: string, bottom: string }`
3337
-
3338
- **`SceneCameraConfig`**: `position?: [ number, number, number ]`, `target?: [ number, number, number ]`, `up?: [ number, number, number ]`, `fov?: number`, `type?: "perspective" | "orthographic"`
3339
-
3340
- **`SceneJourneyConfig`**
3341
-
3342
- | Option | Type | Description |
3343
- |--------|------|-------------|
3344
- | `title?` | `string` | Viewer-facing journey title. Defaults to the journey id. |
3345
- | `startsAt?` | `string` | Optional starting step id. Defaults to the first step. |
3346
- | `behavior?` | `"opt-in" \| "auto"` | Whether the viewer should offer or auto-open the journey. First slice supports opt-in. |
3347
- | `steps` | `SceneJourneyStepConfig[]` | Ordered journey spine. Branches can be added later without changing this core contract. |
3348
- | `valid?` | `boolean` | True unless any journey or step diagnostic has level "error". |
3349
-
3350
- **`SceneJourneyStepConfig`**
3351
-
3352
- | Option | Type | Description |
3353
- |--------|------|-------------|
3354
- | `id` | `string` | Stable step id used by viewer links and Next/Back state. |
3355
- | `title?` | `string` | Viewer-facing title. Defaults to the step id. |
3356
- | `focus?` | `string` | Object name or slash-separated tree path to focus. |
3357
- | `caption?` | `string` | Short optional viewer caption. |
3358
- | `camera?` | `SceneViewCameraConfig` | Optional explicit camera for this step. When omitted, the viewer fits `focus`. |
3359
- | `resolvedFocusId?` | `string \| null` | Resolved object id after script execution, when `focus` matched exactly one object. |
3360
- | `resolvedFocusPath?` | `string \| null` | Resolved object tree path or name after script execution. |
3361
-
3362
- **`SceneLightConfig`**
3363
-
3364
- | Option | Type | Description |
3365
- |--------|------|-------------|
3366
- | `target?` | `[ number, number, number ]` | Target for directional/spot lights |
3367
- | `groundColor?` | `string` | Ground color for hemisphere lights |
3368
- | `skyColor?` | `string` | Sky color alias for hemisphere lights (same as color) |
3369
- | `angle?` | `number` | Spot light cone angle in radians |
3370
- | `penumbra?` | `number` | Spot light penumbra (0–1) |
3371
- | `decay?` | `number` | Point/spot light decay |
3372
- | `distance?` | `number` | Point/spot light distance (0 = infinite) |
3373
- | `castShadow?` | `boolean` | Whether this light casts shadows |
3374
- | `type`, `color?`, `intensity?`, `position?` | | — |
3375
-
3376
- **`SceneEnvironmentConfig`**
3377
- - `preset?: "studio" | "sunset" | "dawn" | "warehouse" | "forest" | "apartment" | "lobby" | "city" | "park" | "night" | "none"` — Built-in preset name or 'none' to disable
3378
- - `intensity?: number` — Environment map intensity
3379
- - `background?: boolean` — Use environment map as scene background
3380
-
3381
- **`SceneFogConfig`**
3382
- - `near?: number` — Linear fog near distance
3383
- - `far?: number` — Linear fog far distance
3384
- - `density?: number` — Exponential fog density (if set, uses FogExp2 instead of linear Fog)
3385
- - Also: `color?: string`
3386
-
3387
- `ScenePostProcessingConfig`: `{ bloom?: SceneBloomConfig, vignette?: SceneVignetteConfig, grain?: SceneGrainConfig, toneMappingExposure?: number }`
3388
-
3389
- `SceneBloomConfig`: `{ intensity?: number, threshold?: number, radius?: number }`
3390
-
3391
- `SceneVignetteConfig`: `{ darkness?: number, offset?: number }`
3392
-
3393
- `SceneGrainConfig`: `{ intensity?: number }`
3394
-
3395
- **`SceneGroundConfig`**
3396
-
3397
- | Option | Type | Description |
3398
- |--------|------|-------------|
3399
- | `visible?` | `boolean` | Show a ground plane |
3400
- | `color?` | `string` | Ground color |
3401
- | `offset?` | `number` | Offset below the model's bounding box minimum Z. Default 0 (flush with model bottom). |
3402
- | `receiveShadow?` | `boolean` | Receive shadows on the ground |
3403
-
3404
- **`SceneCaptureConfig`**
3405
-
3406
- | Option | Type | Description |
3407
- |--------|------|-------------|
3408
- | `framesPerTurn?` | `number` | Frames for one full orbit rotation (default: 72) |
3409
- | `holdFrames?` | `number` | Frozen frames before motion starts (default: 6) |
3410
- | `pitchDeg?` | `number` | Orbit pitch angle in degrees (default: auto from camera) |
3411
- | `fps?` | `number` | Output frame rate (default: 24) |
3412
- | `size?` | `number` | Output frame size in pixels (default: 960) |
3413
- | `background?` | `string` | Canvas background color for capture (default: '#252526') |
3414
-
3415
- #### `showLabels()` — Highlight all user-labeled faces on a shape for visual debugging.
3416
-
3417
- Shows each user-authored label name in the viewport for visual debugging. Returns the shape unchanged for chaining: `return showLabels(myShape)`.
3418
-
3419
- ```ts
3420
- showLabels(shape: Shape): Shape
3421
- ```
3422
-
3423
- #### `viewConfig()` — Configure viewport helper visuals for the current script execution.
3424
-
3425
- Controls renderer-only overlays that appear in the viewport but are not part of the geometry. Currently supports the joint overlay that renders axis arrows and arc indicators when joint controls are active. Multiple calls merge — later values override earlier ones per key.
3426
-
3427
- This does **not** trigger a geometry recompute; it only affects the visual helpers drawn on top of the 3D scene.
3428
-
3429
- ```js
3430
- viewConfig({
3431
- jointOverlay: {
3432
- axisColor: '#13dfff',
3433
- arcColor: '#ff7a1a',
3434
- axisLineRadiusScale: 0.03,
3435
- arcLineRadiusScale: 0.022,
3436
- },
3437
- });
3438
- ```
3439
-
3440
- ```ts
3441
- viewConfig(options?: ViewConfigOptions): void
3442
- ```
3443
-
3444
- #### `spec()` — Create a named, reusable bundle of verification checks.
3445
-
3446
- 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.
3447
-
3448
- Specs can be defined in separate `.forge.js` files and imported via `require()` to share them across models.
3449
-
3450
- `spec.check()` returns a `SpecResult` — you can inspect it programmatically or ignore the return value and let the Checks panel show results.
3451
-
3452
- ```ts
3453
- const printable = spec("Fits printer bed", (shape) => {
3454
- verify.notEmpty("Has geometry", shape);
3455
- const bb = shape.boundingBox();
3456
- verify.lessThan("Width < 220mm", bb.max[0] - bb.min[0], 220);
3457
- verify.lessThan("Depth < 220mm", bb.max[1] - bb.min[1], 220);
3458
- verify.lessThan("Height < 250mm", bb.max[2] - bb.min[2], 250);
3459
- });
3460
-
3461
- // Reuse on multiple shapes
3462
- printable.check(bracket);
3463
- printable.check(standoff);
3464
-
3465
- // Check relationships between parts
3466
- const fitSpec = spec("Assembly fit", (partA, partB) => {
3467
- verify.notColliding("No interference", partA, partB, 10);
3468
- });
3469
- fitSpec.check(bracket, standoff);
3470
- ```
3471
-
3472
- **Spec-first workflow:** Write specs before building geometry. Checks go from red to green as you build — effectively TDD for CAD.
3473
-
3474
- ```ts
3475
- spec(name: string, checkFn: (...args: any[]) => void): Spec
3476
- ```
3477
-
3478
- **`Spec`**
3479
- - `name: string` — The display name of this spec
286
+ - `Viewport.label(text, at, options?)` — Add a render-only viewport label at a world-space point. → [viewport](/docs/viewport#viewport-runtime)
287
+ - `Viewport.highlight(target, options?)` — Highlight any geometry for visual debugging in the viewport. → [viewport](/docs/viewport#viewport-runtime)
288
+ - `verify.that(label, check, message?)` Custom predicate check. [core](/docs/core#verify)
289
+ - `verify.equal(label, actual, expected, tolerance?, message?)` — Check that two numbers are approximately equal (within tolerance). → [core](/docs/core#verify)
290
+ - `verify.notEqual(label, actual, unexpected, tolerance?, message?)` Check that two numbers are NOT equal (differ by more than tolerance). → [core](/docs/core#verify)
291
+ - `verify.greaterThan(label, actual, min, message?)` — Check that actual > min. → [core](/docs/core#verify)
292
+ - `verify.lessThan(label, actual, max, message?)` Check that actual < max. [core](/docs/core#verify)
293
+ - `verify.inRange(label, actual, min, max, message?)` — Check that min <= actual <= max. → [core](/docs/core#verify)
294
+ - `verify.centersCoincide(label, a, b, tolerance?)` — Check that the bounding-box centers of two shapes coincide within tolerance (mm). → [core](/docs/core#verify)
295
+ - `verify.connectorDistance(label, target, connectorA, connectorB, expected?, tolerance?)` — Check the distance between two named connectors on a shape or group. → [core](/docs/core#verify)
296
+ - `verify.physicalComponentCount(label, expected)` — Declare the expected physical connectivity component count for the returned visible model. → [core](/docs/core#verify)
297
+ - `verify.intentionalOverlap(label, a, b, reason)` — Declare that two visible objects intentionally overlap because the overlap is real manufacturing intent. → [core](/docs/core#verify)
298
+ - `verify.notColliding(label, a, b, searchLength?)` — Check that two shapes do not share positive volume. → [core](/docs/core#verify)
299
+ - `verify.minClearance(label, a, b, minGap, searchLength?)` — Check that a minimum clearance gap exists between two shapes. → [core](/docs/core#verify)
300
+ - `verify.clearanceBetween(label, a, b, minGap, maxGap, searchLength?)` — Check that the clearance gap between two shapes is inside an allowed range. → [core](/docs/core#verify)
301
+ - `verify.parallel(label, faceA, faceB, toleranceDeg?)` — Check that two face normals are parallel (within toleranceDeg degrees). → [core](/docs/core#verify)
302
+ - `verify.perpendicular(label, faceA, faceB, toleranceDeg?)` — Check that two face normals are perpendicular (within toleranceDeg degrees). → [core](/docs/core#verify)
303
+ - `verify.coplanar(label, faceA, faceB, toleranceDeg?, toleranceMm?)` — Check that a face is coplanar with (same plane as) another face, meaning they are parallel AND their centers lie on the same plane. → [core](/docs/core#verify)
304
+ - `verify.faceAt(label, face, expectedPos, toleranceMm?)` — Check that a face center lies at a specific position (within toleranceMm). → [core](/docs/core#verify)
305
+ - `verify.sameDirection(label, faceA, faceB, toleranceDeg?)` — Check that two face normals point in the same direction (not antiparallel). → [core](/docs/core#verify)
306
+ - `verify.isEmpty(label, shape, message?)` Check that a shape is empty. → [core](/docs/core#verify)
307
+ - `verify.notEmpty(label, shape, message?)` — Check that a shape is NOT empty. → [core](/docs/core#verify)
308
+ - `verify.volumeApprox(label, shape, expected, tolerance?)` — Check that a shape's volume is approximately equal to expected (mm³). → [core](/docs/core#verify)
309
+ - `verify.areaApprox(label, shape, expected, tolerance?)` — Check that a shape's surface area is approximately equal to expected (mm²). → [core](/docs/core#verify)
310
+ - `verify.boundingBoxSize(label, shape, expectedSize, tolerance?)` — Check that a shape's bounding box has approximately the given size. → [core](/docs/core#verify)
311
+ - `verify.edgeContinuity(label, shape, options?)` Check that every sampled seam on a shape meets a requested continuity threshold. → [core](/docs/core#verify)
312
+ - `verify.noTinyEdges(label, shape, threshold?)` — Check that a shape has no tiny edges below the requested threshold. → [core](/docs/core#verify)
313
+ - `verify.noSliverFaces(label, shape, threshold?)` Check that a shape has no sliver faces below the requested score threshold. → [core](/docs/core#verify)
314
+ - `verify.noSelfIntersection(label, shape)` Best-effort exact-shape validity guard for self-intersections or broken B-Rep topology. [core](/docs/core#verify)
315
+ - `explodeView(options?)` Configure how the viewport explode slider offsets returned objects. → [viewport](/docs/viewport#explodeview)
316
+ - `cutPlane(name, normal, offset?, options?)` Define a named section plane for inspecting internal geometry. → [viewport](/docs/viewport#cutplane)
317
+ - `compareWith(path, options?)` Declare a reference model for comparison inspection. [viewport](/docs/viewport#comparewith)
318
+ - `mock(shape, name?)` Register a mock (context) object for visualization and inspection. [viewport](/docs/viewport#mock)
319
+ - `scene(options)` — Configure the scene environment for the current script execution. → [viewport](/docs/viewport#scene)
320
+ - `spec(name, checkFn)` — Create a named, reusable bundle of verification checks. → [core](/docs/core#spec)
3480
321
 
3481
322
  ---
3482
323
 
@@ -3484,28 +325,7 @@ spec(name: string, checkFn: (...args: any[]) => void): Spec
3484
325
 
3485
326
  Bring external geometry or other ForgeCAD modules into the current script.
3486
327
 
3487
- #### `group()` — Group multiple shapes/sketches for joint transforms without merging into a single mesh.
3488
-
3489
- Unlike union(), child colors and individual identities are preserved. Children can be plain shapes, named descriptors ({ name, shape/sketch/group }), or nested groups. The returned ShapeGroup supports all Shape transforms (translate, rotate, etc.).
3490
-
3491
- Named descriptors can include `tags` for viewport organization. Tags do not affect geometry; they let the command palette hide, show only, or focus all objects with the same tag.
3492
-
3493
- **Local coordinate pattern:** Build child parts at the origin (local coordinates), then group and translate once to place the whole assembly. This eliminates the error-prone pattern of manually adding parent offsets to every sub-part.
3494
-
3495
- ```js
3496
- const body = roundedBox(100, 20, 32, 4);
3497
- const panel = box(98, 2, 18).translate(0, -12, 4);
3498
- const louver = box(88, 2, 6).translate(0, -14, -11);
3499
- const indoorUnit = group(
3500
- { name: 'Body', shape: body },
3501
- { name: 'Panel', tags: 'cover', shape: panel },
3502
- { name: 'Louver', tags: ['cover', 'moving'], shape: louver },
3503
- ).translate(0, -18, 70);
3504
- ```
3505
-
3506
- ```ts
3507
- group(...items: GroupInput[]): ShapeGroup
3508
- ```
328
+ - `group(...items)` — Group multiple shapes/sketches for joint transforms without merging into a single mesh. → [core](/docs/core#group)
3509
329
 
3510
330
  ---
3511
331
 
@@ -3513,75 +333,9 @@ group(...items: GroupInput[]): ShapeGroup
3513
333
 
3514
334
  Pre-built parametric parts accessible via `lib.*`.
3515
335
 
3516
- #### `Wood.board()` — Create a wood board with metadata for manufacturing.
3517
-
3518
- The board is a box(width, height, thickness) centered on XY, base at Z=0. Width along X, height along Y, thickness along Z (0 to thickness).
3519
-
3520
- ```ts
3521
- Wood.readonly board: (width: number, height: number, thickness: number, opts?: WoodBoardOptions) => WoodBoard
3522
- ```
3523
-
3524
- **`WoodBoardOptions`**
3525
-
3526
- | Option | Type | Description |
3527
- |--------|------|-------------|
3528
- | `species?` | `string` | Wood species, e.g. "birch", "oak", "plywood". Default: "wood" |
3529
- | `material?` | `string` | Material description for BOM, e.g. "birch plywood". Default: species value |
3530
- | `grain?` | `string` | Grain direction: "long" (along width) or "cross" (along height). Default: "long" |
3531
- | `color?` | `string` | Color hex string for visualization. Default: "#d2b48c" (tan/wood) |
3532
- | `autoBom?` | `boolean` | If false, skip automatic BOM registration. Default: true |
3533
-
3534
- #### `Wood.dado()` — Cut a dado (channel) across the face of a host board for a guest board to sit in.
3535
-
3536
- Returns a new host board with the dado cut applied.
3537
-
3538
- ```ts
3539
- Wood.dado(host: WoodBoard, guest: WoodBoard, opts: DadoOptions): WoodBoard
3540
- ```
3541
-
3542
- **`DadoOptions`**
3543
-
3544
- | Option | Type | Description |
3545
- |--------|------|-------------|
3546
- | `fromBottom?` | `number` | Distance from the bottom edge of the host to the bottom of the channel (mm). |
3547
- | `fromTop?` | `number` | Distance from the top edge of the host to the top of the channel (mm). Alternative to fromBottom. |
3548
- | `depth?` | `number` | Channel depth into the board thickness (mm). Default: 1/3 of host thickness. |
3549
- | `fit?` | `"friction" \| "snug" \| "slip"` | Fit tolerance. Default: 'snug'. |
3550
- | `stopped?` | `number` | If set, the dado stops this far from the front edge (mm), creating a stopped dado. |
3551
-
3552
- #### `Wood.rabbet()` — Cut a rabbet (L-shaped step) along an edge of a board.
3553
-
3554
- Returns a new board with the rabbet cut applied.
3555
-
3556
- ```ts
3557
- Wood.rabbet(board: WoodBoard, opts: RabbetOptions): WoodBoard
3558
- ```
3559
-
3560
- **`RabbetOptions`**
3561
- - `edge: "top" | "bottom" | "left" | "right" | "back"` — Which edge to cut the rabbet on.
3562
- - `width: number` — How far into the board face the rabbet extends (mm).
3563
- - `depth: number` — How deep the step is cut into the thickness (mm).
3564
-
3565
- #### `Wood.mortiseAndTenon()` — Cut a mortise in one board and shape a tenon on another.
3566
-
3567
- Returns new boards with the mortise pocket and tenon cuts applied.
3568
-
3569
- ```ts
3570
- Wood.mortiseAndTenon(mortiseBoard: WoodBoard, tenonBoard: WoodBoard, opts?: MortiseAndTenonOptions): MortiseAndTenonResult
3571
- ```
3572
-
3573
- **`MortiseAndTenonOptions`**
3574
-
3575
- | Option | Type | Description |
3576
- |--------|------|-------------|
3577
- | `style?` | `"blind" \| "through"` | 'blind' (default): mortise doesn't go through. 'through': mortise goes all the way. |
3578
- | `position?` | `{ fromTop?: number; fromBottom?: number; }` | Position of the mortise center along the mortise board height. |
3579
- | `tenonThickness?` | `number` | Tenon thickness (mm). Default: 1/3 of tenon board thickness. |
3580
- | `tenonWidth?` | `number` | Tenon width (mm). Default: 60% of tenon board height. |
3581
- | `tenonLength?` | `number` | Tenon length (mm). Default: 2/3 of mortise board thickness for blind, full for through. |
3582
- | `cornerRadius?` | `number` | Corner radius for mortise (mm). Default: 0 (square corners, hand tools). |
3583
- | `fit?` | `"friction" \| "snug" \| "slip" \| "knockdown"` | Fit tolerance. Default: 'snug'. |
3584
-
3585
- `MortiseAndTenonResult`: `{ mortiseBoard: WoodBoard, tenonBoard: WoodBoard }`
336
+ - `Wood.board(width, height, thickness, opts?)` — Create a wood board with metadata for manufacturing. → [wood](/docs/wood#wood)
337
+ - `Wood.dado(host, guest, opts)` — Cut a dado (channel) across the face of a host board for a guest board to sit in. → [wood](/docs/wood#wood)
338
+ - `Wood.rabbet(board, opts)` Cut a rabbet (L-shaped step) along an edge of a board. [wood](/docs/wood#wood)
339
+ - `Wood.mortiseAndTenon(mortiseBoard, tenonBoard, opts?)` — Cut a mortise in one board and shape a tenon on another. → [wood](/docs/wood#wood)
3586
340
 
3587
341
  ---