forgecad 0.9.14 → 0.9.16

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 (239) hide show
  1. package/LICENSE +6 -4
  2. package/README.md +8 -4
  3. package/dist/assets/{AdminPage-eWGs2K6H.js → AdminPage-CXvls4-J.js} +2 -2
  4. package/dist/assets/{BenchmarkPage-CTrLKfpo.js → BenchmarkPage-B27zk8xL.js} +4 -15
  5. package/dist/assets/{BlogPage-5nPesyds.js → BlogPage-CMAVvgQL.js} +2 -2
  6. package/dist/assets/{DocsPage-C4Y3nbYc.js → DocsPage-knf4I4h7.js} +9 -3
  7. package/dist/assets/EditorApp-BHMQlJ-D.js +14686 -0
  8. package/dist/assets/{EditorApp-BAnckbsk.css → EditorApp-BpjZgzk0.css} +846 -0
  9. package/dist/assets/{EmbedViewer-C8fB4n5U.js → EmbedViewer-D7ZGlFjx.js} +3 -3
  10. package/dist/assets/{LandingPageProofDriven-jSz0LaMM.js → LandingPageProofDriven-CnevhTE8.js} +36 -38
  11. package/dist/assets/LegalPage-BPTUmqeg.js +39 -0
  12. package/dist/assets/LegalPage-BRlScr9A.css +91 -0
  13. package/dist/assets/{PricingPage-B83B90zh.js → PricingPage-B0D4goG_.js} +19 -19
  14. package/dist/assets/{PricingPage-BMedqFef.css → PricingPage-BPF6HKyO.css} +25 -0
  15. package/dist/assets/{SettingsPage-DY889pcu.js → SettingsPage-CFF-UgjI.js} +2 -2
  16. package/dist/assets/app-CE3sYcV7.css +3890 -0
  17. package/dist/assets/{app-bEww1ic4.js → app-T0pDcSX4.js} +3382 -1069
  18. package/dist/assets/cli/{render-Cho2uKG_.js → render-C5pcIISc.js} +477 -29
  19. package/dist/assets/{constructionHistoryWorker-HYwzJY4m.js → constructionHistoryWorker-Ba2Hm58b.js} +928 -243
  20. package/dist/assets/{evalWorker-CjQwJSE-.js → evalWorker-vkx310U2.js} +8883 -6040
  21. package/dist/assets/{forgecad_geometry-CH2nvuLA.js → forgecad_geometry-Dgceylq9.js} +43 -1
  22. package/dist/assets/forgecad_geometry_bg-dD4RNQF1.wasm +0 -0
  23. package/dist/assets/{inspectWorker-DeRnMVv1.js → inspectWorker-BuTJDVX6.js} +1179 -273
  24. package/dist/assets/{javascript-70-4uGcz.js → javascript-1kQXfVaz.js} +1 -1
  25. package/dist/assets/{targets-D6PWsv6X.js → jointPose-B_Cgedn9.js} +71 -3
  26. package/dist/assets/landing-proof-driven-DiGqdtWa.js +18 -0
  27. package/dist/assets/{landing-proof-driven-oFYW6mjz.css → landing-proof-driven-ORyigZ6p.css} +13 -7
  28. package/dist/assets/legalContent-ZfFGMmi4.js +251 -0
  29. package/dist/assets/{manifold-rmfAcdwF.js → manifold-BWgsjmAM.js} +1 -1
  30. package/dist/assets/{manifold-uRzgk5O8.js → manifold-D6IFSkhH.js} +2 -2
  31. package/dist/assets/{manifold-CG9Fokx-.js → manifold-rZexZI0G.js} +1 -1
  32. package/dist/assets/{reportWorker-4cW_ZpoS.js → reportWorker-0AGij1Ru.js} +8659 -12771
  33. package/dist/assets/{scalar-sampling-budget-CfDiFvh7.js → scalar-sampling-budget-J5cuzxT1.js} +8050 -6203
  34. package/dist/assets/{scanProxyWorker-Bs2TDgLw.js → scanProxyWorker-Vl4Wxa1y.js} +50 -6
  35. package/dist/assets/{solver-DuJAO8S6.js → solver-BZ9LPTHs.js} +1 -1
  36. package/dist/assets/solver_bg-DAHZJ_rw.wasm +0 -0
  37. package/dist/assets/{vendor-react-Da3A2QmU.js → vendor-react-6j1Kke-Y.js} +6 -5
  38. package/dist/cli/render.html +1 -1
  39. package/dist/docs/index.html +2 -2
  40. package/dist/docs-raw/AI/ai-native-cad.md +50 -0
  41. package/dist/docs-raw/AI/usage.md +5 -12
  42. package/dist/docs-raw/CLI.md +34 -10
  43. package/dist/docs-raw/component-model.md +27 -11
  44. package/dist/docs-raw/generated/assembly.md +374 -187
  45. package/dist/docs-raw/generated/concepts.md +245 -237
  46. package/dist/docs-raw/generated/core.md +283 -6
  47. package/dist/docs-raw/generated/curves.md +274 -361
  48. package/dist/docs-raw/generated/lib.md +9 -19
  49. package/dist/docs-raw/generated/output.md +29 -4
  50. package/dist/docs-raw/generated/runtime-names.md +49 -0
  51. package/dist/docs-raw/generated/sdf.md +31 -0
  52. package/dist/docs-raw/generated/sheet-metal.md +9 -0
  53. package/dist/docs-raw/generated/sketch.md +44 -1
  54. package/dist/docs-raw/generated/viewport.md +11 -3
  55. package/dist/docs-raw/guides/coordinate-system.md +20 -16
  56. package/dist/docs-raw/guides/geometry-conventions.md +2 -2
  57. package/dist/docs-raw/guides/inspection-bundles.md +2 -1
  58. package/dist/docs-raw/guides/joint-design.md +24 -0
  59. package/dist/docs-raw/guides/positioning.md +13 -3
  60. package/dist/docs-raw/legal/privacy.md +63 -0
  61. package/dist/docs-raw/legal/software-license.md +55 -0
  62. package/dist/docs-raw/legal/terms.md +87 -0
  63. package/dist/docs-raw/skills/forgecad-3d-reconstruction.md +1 -1
  64. package/dist/docs-raw/skills/forgecad-blockout-model.md +1 -1
  65. package/dist/docs-raw/skills/forgecad-component-model.md +11 -2
  66. package/dist/docs-raw/skills/forgecad-high-level-spec.md +1 -1
  67. package/dist/docs-raw/skills/forgecad-image-replicator.md +8 -8
  68. package/dist/docs-raw/skills/forgecad-lld.md +1 -1
  69. package/dist/docs-raw/skills/forgecad-make-a-model.md +40 -39
  70. package/dist/docs-raw/skills/forgecad-model-grader.md +2 -2
  71. package/dist/docs-raw/skills/forgecad-prepare-prompt.md +2 -2
  72. package/dist/docs-raw/skills/forgecad-project.md +3 -1
  73. package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +1 -1
  74. package/dist/docs-raw/skills/forgecad-render-inspect.md +4 -2
  75. package/dist/docs-raw/skills/forgecad-visual-spec.md +1 -1
  76. package/dist/docs-raw/skills/forgecad.md +4 -3
  77. package/dist/docs-raw/welcome.md +2 -0
  78. package/dist/index.html +40 -12
  79. package/dist/llms.txt +8 -0
  80. package/dist/site.webmanifest +1 -1
  81. package/dist/sitemap.xml +49 -13
  82. package/dist-cli/{check-compiler-U5SOPN7X.js → check-compiler-SYQ2PWOB.js} +1 -2
  83. package/dist-cli/{check-query-propagation-XOKNSSYU.js → check-query-propagation-HIAGV62W.js} +1 -2
  84. package/dist-cli/{chunk-EXWGNL6K.js → chunk-SPZE3DUY.js} +20659 -17930
  85. package/dist-cli/forgecad.js +3568 -1250
  86. package/dist-cli/{forgecad_geometry-GYVNKPIE.js → forgecad_geometry-QOQIIP53.js} +42 -1
  87. package/dist-cli/forgecad_geometry_bg.wasm +0 -0
  88. package/dist-cli/{solver-46FFSK2U.js → solver-OK4HECRH.js} +0 -1
  89. package/dist-cli/solver_bg.wasm +0 -0
  90. package/dist-skill/CONTEXT.md +1192 -725
  91. package/dist-skill/SKILL.md +3 -2
  92. package/dist-skill/docs/API/core/concepts.md +64 -1
  93. package/dist-skill/docs/CLI.md +34 -10
  94. package/dist-skill/docs/generated/assembly.md +339 -213
  95. package/dist-skill/docs/generated/core.md +283 -6
  96. package/dist-skill/docs/generated/curves.md +272 -362
  97. package/dist-skill/docs/generated/lib.md +9 -19
  98. package/dist-skill/docs/generated/output.md +29 -4
  99. package/dist-skill/docs/generated/runtime-names.md +40 -0
  100. package/dist-skill/docs/generated/sdf.md +31 -0
  101. package/dist-skill/docs/generated/sheet-metal.md +9 -0
  102. package/dist-skill/docs/generated/sketch.md +44 -2
  103. package/dist-skill/docs/generated/viewport.md +2 -87
  104. package/dist-skill/docs/guides/coordinate-system.md +20 -16
  105. package/dist-skill/docs/guides/geometry-conventions.md +2 -2
  106. package/dist-skill/docs/guides/inspection-bundles.md +2 -1
  107. package/dist-skill/docs/guides/joint-design.md +24 -0
  108. package/dist-skill/docs/guides/positioning.md +13 -3
  109. package/dist-skill/library/forgecad-component-model/SKILL.md +10 -1
  110. package/dist-skill/library/forgecad-image-replicator/SKILL.md +6 -6
  111. package/dist-skill/library/forgecad-image-replicator/scripts/compare_images.py +166 -0
  112. package/dist-skill/library/forgecad-make-a-model/SKILL.md +39 -38
  113. package/dist-skill/library/forgecad-model-grader/SKILL.md +1 -1
  114. package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +1 -1
  115. package/dist-skill/library/forgecad-project/SKILL.md +2 -0
  116. package/dist-skill/library/forgecad-render-inspect/SKILL.md +3 -1
  117. package/examples/api/assembly-kinematics-foundation.forge.js +65 -0
  118. package/examples/api/assembly-kinematics-four-bar.forge.js +115 -0
  119. package/examples/api/assembly-kinematics-limb.forge.js +116 -0
  120. package/examples/api/connector-frame-rig-chain.forge.js +102 -0
  121. package/examples/api/exact-sheet-shell-assembly.forge.js +0 -2
  122. package/examples/api/exact-surface-studio.forge.js +6 -8
  123. package/examples/api/helix-basics.forge.js +8 -8
  124. package/examples/api/lean-foundations/README.md +12 -0
  125. package/examples/api/lean-foundations/curve-blend-exact.forge.js +22 -0
  126. package/examples/api/lean-foundations/curve-fit-interpolation.forge.js +18 -0
  127. package/examples/api/lean-foundations/curve-helix-canonicalization.forge.js +27 -0
  128. package/examples/api/lean-foundations/curve-route-canonicalization.forge.js +16 -0
  129. package/examples/api/lean-foundations/curve-trim-reverse.forge.js +24 -0
  130. package/examples/api/lean-foundations/exact-curve-arc.forge.js +36 -0
  131. package/examples/api/mixed-edge-finishes-proof.forge.js +8 -11
  132. package/examples/api/route3d-elbow.forge.js +71 -0
  133. package/examples/api/transition-curves.forge.js +44 -15
  134. package/examples/api/variable-sweep-test.forge.js +3 -1
  135. package/examples/api/y-blend-corner-showcase.forge.js +0 -2
  136. package/examples/generative/coral-vase.forge.js +1 -1
  137. package/examples/nurbs-tube.forge.js +1 -1
  138. package/package.json +17 -13
  139. package/dist/assets/EditorApp-lXv53A1m.js +0 -13610
  140. package/dist/assets/app-CsHnaBWt.css +0 -1789
  141. package/dist/assets/forgecad_geometry_bg-C5_E9Oa9.wasm +0 -0
  142. package/dist/assets/solver_bg-CWvv4lnN.wasm +0 -0
  143. package/dist/docs-raw/API/README.md +0 -16
  144. package/dist/docs-raw/API/core/concepts.md +0 -118
  145. package/dist/docs-raw/INDEX.md +0 -138
  146. package/dist/docs-raw/RELEASING.md +0 -87
  147. package/dist/docs-raw/agent-native-api.md +0 -27
  148. package/dist/docs-raw/beta-deployment.md +0 -304
  149. package/dist/docs-raw/beta-operations.md +0 -325
  150. package/dist/docs-raw/blueprint-first.md +0 -145
  151. package/dist/docs-raw/cli-monetization.md +0 -112
  152. package/dist/docs-raw/coding-best-practices.md +0 -120
  153. package/dist/docs-raw/coding.md +0 -340
  154. package/dist/docs-raw/deployment.md +0 -374
  155. package/dist/docs-raw/guides/skill-maintenance.md +0 -161
  156. package/dist/docs-raw/guides/surface-members.md +0 -82
  157. package/dist/docs-raw/harbor-cli.md +0 -854
  158. package/dist/docs-raw/internals/backend-vocabulary.md +0 -35
  159. package/dist/docs-raw/internals/compiler.md +0 -307
  160. package/dist/docs-raw/internals/constraint-solver-quality.md +0 -161
  161. package/dist/docs-raw/internals/constraint-solver.md +0 -176
  162. package/dist/docs-raw/internals/shape-from-slices.md +0 -152
  163. package/dist/docs-raw/internals/sketch-2d-pipeline.md +0 -108
  164. package/dist/docs-raw/platform/admin.md +0 -45
  165. package/dist/docs-raw/platform/architecture.md +0 -82
  166. package/dist/docs-raw/platform/auth.md +0 -139
  167. package/dist/docs-raw/platform/email.md +0 -67
  168. package/dist/docs-raw/platform/google-oauth-setup.md +0 -88
  169. package/dist/docs-raw/platform/observability.md +0 -197
  170. package/dist/docs-raw/platform/projects.md +0 -111
  171. package/dist/docs-raw/platform/sharing.md +0 -90
  172. package/dist/docs-raw/product/README.md +0 -39
  173. package/dist/docs-raw/product/api-as-product-language.md +0 -13
  174. package/dist/docs-raw/product/business-model.md +0 -15
  175. package/dist/docs-raw/product/competitive-positioning.md +0 -17
  176. package/dist/docs-raw/product/creative-manufacturing.md +0 -15
  177. package/dist/docs-raw/product/founder-story.md +0 -11
  178. package/dist/docs-raw/product/manufacturing-workflows.md +0 -15
  179. package/dist/docs-raw/product/onboarding-first-experience.md +0 -256
  180. package/dist/docs-raw/product/product-loop.md +0 -17
  181. package/dist/docs-raw/product/strategic-decisions.md +0 -22
  182. package/dist/docs-raw/product/user-outreach-email-templates.md +0 -161
  183. package/dist/docs-raw/product/user-segments.md +0 -15
  184. package/dist/docs-raw/product/vision.md +0 -26
  185. package/dist/docs-raw/rl-environments.md +0 -350
  186. package/dist/docs-raw/runbook.md +0 -611
  187. package/dist-cli/check-compiler-U5SOPN7X.js.map +0 -1
  188. package/dist-cli/check-query-propagation-XOKNSSYU.js.map +0 -1
  189. package/dist-cli/chunk-EXWGNL6K.js.map +0 -1
  190. package/dist-cli/forgecad.js.map +0 -1
  191. package/dist-cli/forgecad_geometry-GYVNKPIE.js.map +0 -1
  192. package/dist-cli/solver-46FFSK2U.js.map +0 -1
  193. package/dist-skill/SKILL-dev.md +0 -145
  194. package/dist-skill/docs-dev/API/core/concepts.md +0 -118
  195. package/dist-skill/docs-dev/CLI.md +0 -677
  196. package/dist-skill/docs-dev/agent-native-api.md +0 -27
  197. package/dist-skill/docs-dev/blueprint-first.md +0 -145
  198. package/dist-skill/docs-dev/coding-best-practices.md +0 -120
  199. package/dist-skill/docs-dev/coding.md +0 -340
  200. package/dist-skill/docs-dev/component-model.md +0 -164
  201. package/dist-skill/docs-dev/generated/assembly.md +0 -794
  202. package/dist-skill/docs-dev/generated/core.md +0 -2117
  203. package/dist-skill/docs-dev/generated/curves.md +0 -2583
  204. package/dist-skill/docs-dev/generated/lib.md +0 -169
  205. package/dist-skill/docs-dev/generated/output.md +0 -247
  206. package/dist-skill/docs-dev/generated/sdf.md +0 -446
  207. package/dist-skill/docs-dev/generated/sheet-metal.md +0 -504
  208. package/dist-skill/docs-dev/generated/sketch.md +0 -1811
  209. package/dist-skill/docs-dev/generated/viewport.md +0 -585
  210. package/dist-skill/docs-dev/generated/wood.md +0 -108
  211. package/dist-skill/docs-dev/guides/coordinate-system.md +0 -46
  212. package/dist-skill/docs-dev/guides/geometry-conventions.md +0 -52
  213. package/dist-skill/docs-dev/guides/inspection-bundles.md +0 -485
  214. package/dist-skill/docs-dev/guides/joint-design.md +0 -78
  215. package/dist-skill/docs-dev/guides/modeling-recipes.md +0 -78
  216. package/dist-skill/docs-dev/guides/positioning.md +0 -161
  217. package/dist-skill/docs-dev/guides/skill-maintenance.md +0 -161
  218. package/dist-skill/docs-dev/internals/backend-vocabulary.md +0 -35
  219. package/dist-skill/docs-dev/internals/compiler.md +0 -307
  220. package/dist-skill/docs-dev/internals/constraint-solver-quality.md +0 -161
  221. package/dist-skill/docs-dev/internals/constraint-solver.md +0 -176
  222. package/dist-skill/docs-dev/internals/sketch-2d-pipeline.md +0 -108
  223. package/dist-skill/library/forgecad-image-replicator/scripts/compare_images.mjs +0 -289
  224. package/examples/api/bolted-service-cover.forge.js +0 -17
  225. package/examples/api/cable-gland-anchor.forge.js +0 -14
  226. package/examples/api/captured-cartridge-guide.forge.js +0 -14
  227. package/examples/api/captured-linear-slide.forge.js +0 -13
  228. package/examples/api/clevis-pin-joint.forge.js +0 -13
  229. package/examples/api/datum-enclosure.forge.js +0 -16
  230. package/examples/api/hose-barb-port.forge.js +0 -14
  231. package/examples/api/knuckled-hinge-assembly.forge.js +0 -15
  232. package/examples/api/living-hinge-cover.forge.js +0 -14
  233. package/examples/api/pcb-terminal-block.forge.js +0 -22
  234. package/examples/api/pinned-lever-pivot-stack.forge.js +0 -14
  235. package/examples/api/retained-shaft-knob-stack.forge.js +0 -15
  236. package/examples/api/routed-tube-clip.forge.js +0 -15
  237. package/examples/api/seated-bearing-stack.forge.js +0 -30
  238. package/examples/api/snap-latch-cover.forge.js +0 -14
  239. package/examples/api/thumb-screw-clamp.forge.js +0 -15
@@ -1,2117 +0,0 @@
1
- ---
2
- skill-group: core
3
- skill-order: 100
4
- ---
5
-
6
- # Core API
7
-
8
- 3D primitives, boolean operations, transforms, patterns, imports, and parameters.
9
-
10
- ## Contents
11
-
12
- - [3D Primitives](#3d-primitives) — `box`, `cylinder`, `sphere`, `torus`
13
- - [Boolean Operations](#boolean-operations) — `union`, `difference`, `intersection`
14
- - [Edge Features](#edge-features) — `fillet`, `chamfer`, `draft`, `offsetSolid`
15
- - [Patterns & Layout](#patterns-layout) — `circularLayout`, `polygonVertices`, `linearPattern`, `circularPattern`, `linearPattern2d`, `circularPattern2d`, `mirrorCopy`, `selectEdges`, `selectEdge`, `coalesceEdges`
16
- - [Imports & Composition](#imports-composition) — `require`, `importSvgSketch`, `importMesh`, `importStep`
17
- - [Parameters](#parameters) — `Param.number`, `Param.string`, `Param.bool`, `Param.choice`, `Param.list`
18
- - [Grouping & Local Coordinates](#grouping-local-coordinates) — `group`
19
- - [Section & Projection](#section-projection) — `intersectWithPlane`, `faceProfile`, `projectToPlane`
20
- - [Transforms](#transforms) — `composeChain`
21
- - [Verification](#verification) — `verify.that`, `verify.equal`, `verify.notEqual`, `verify.greaterThan`, `verify.lessThan`, `verify.inRange`, `verify.centersCoincide`, `verify.connectorDistance`, `verify.physicalComponentCount`, `verify.intentionalOverlap`, `verify.notColliding`, `verify.minClearance`, `verify.clearanceBetween`, `verify.parallel`, `verify.perpendicular`, `verify.coplanar`, `verify.faceAt`, `verify.sameDirection`, `verify.isEmpty`, `verify.notEmpty`, `verify.volumeApprox`, `verify.areaApprox`, `verify.boundingBoxSize`, `verify.edgeContinuity`, `verify.noTinyEdges`, `verify.noSliverFaces`, `verify.noSelfIntersection`, `spec`
22
- - [Shape](#shape) — Appearance, Face Topology, Edge Topology, Transforms, Booleans & Cutting, Features, Placement, Connectors, References, Measurement
23
- - [Transform](#transform)
24
- - [ShapeGroup](#shapegroup) — Children, Transforms, Placement, Connectors, References
25
- - [SurfacePattern](#surfacepattern)
26
- - [Pattern2D](#pattern2d)
27
- - [Pattern2DBuilder](#pattern2dbuilder)
28
- - [ShapeRef](#shaperef)
29
- - [ANCHOR3D_NAMES](#anchor3d-names)
30
- - [verify](#verify)
31
- - [Constraint](#constraint)
32
- - [Points](#points)
33
- - [connector](#connector)
34
- - [Import](#import)
35
-
36
- ## Functions
37
-
38
- ### 3D Primitives
39
-
40
- #### `box()` — Create a rectangular box. Centered on XY, base at Z=0.
41
-
42
- Extents:
43
-
44
- - X: `[-width/2, width/2]`
45
- - Y: `[-depth/2, depth/2]`
46
- - Z: `[0, height]`
47
-
48
- For named faces, build from a labeled sketch: `rect(width, depth).labelEdges('s', 'e', 'n', 'w').extrude(height, { labels: { start: 'bottom', end: 'top' } })`.
49
-
50
- ```ts
51
- box(width: number, depth: number, height: number): Shape
52
- ```
53
-
54
- #### `cylinder()` — Create a cylinder or cone with named faces and edges. Centered on XY, base at Z=0.
55
-
56
- Extents:
57
-
58
- - X/Y: centered at the origin
59
- - Z: `[0, height]`
60
-
61
- `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).
62
-
63
- Named faces: `top`, `bottom`, `side` Named edges: `top-rim`, `bottom-rim`
64
-
65
- ```ts
66
- cylinder(height: number, radius: number, radiusTop?: number, segments?: number): Shape
67
- ```
68
-
69
- #### `sphere()` — Create a sphere centered at the origin.
70
-
71
- Extents:
72
-
73
- - X: `[-radius, radius]`
74
- - Y: `[-radius, radius]`
75
- - Z: `[-radius, radius]`
76
-
77
- Use `segments` for lower-poly approximations.
78
-
79
- ```ts
80
- sphere(radius: number, segments?: number): Shape
81
- ```
82
-
83
- #### `torus()` — Create a torus (donut shape) lying in the XY plane. Centered on all axes.
84
-
85
- Extents:
86
-
87
- - X: `[-(majorRadius + minorRadius), +(majorRadius + minorRadius)]`
88
- - Y: `[-(majorRadius + minorRadius), +(majorRadius + minorRadius)]`
89
- - Z: `[-minorRadius, minorRadius]`
90
-
91
- The origin is the center of the ring.
92
-
93
- ```ts
94
- torus(majorRadius: number, minorRadius: number, segments?: number): Shape
95
- ```
96
-
97
- ### Boolean Operations
98
-
99
- #### `union()` — Combine shapes into a single solid (additive boolean).
100
-
101
- 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.
102
-
103
- ```ts
104
- union(...inputs: ShapeOperandInput[]): Shape
105
- ```
106
-
107
- #### `difference()` — Subtract shapes from a base shape (subtractive boolean).
108
-
109
- The first shape is the base; all subsequent shapes are subtracted from it. Accepts individual shapes, or an array of shapes.
110
-
111
- ```ts
112
- difference(...inputs: ShapeOperandInput[]): Shape
113
- ```
114
-
115
- #### `intersection()` — Keep only the overlapping volume of the input shapes (intersection boolean).
116
-
117
- Requires at least two shapes. Accepts individual shapes, or an array.
118
-
119
- ```ts
120
- intersection(...inputs: ShapeOperandInput[]): Shape
121
- ```
122
-
123
- ### Edge Features
124
-
125
- #### `fillet()` — Apply experimental fillets (rounded edges) to one or more edges of a shape.
126
-
127
- **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.
128
-
129
- Edge selections compile into backend operations; unsupported selections fail as explicit kernel gaps instead of using TypeScript geometry fallbacks.
130
-
131
- The `edges` parameter is flexible:
132
-
133
- - Omit to fillet **all** sharp edges
134
- - Pass an `EdgeQuery` for an inline filter (most common)
135
- - Pass an `EdgeSegment` or `EdgeSegment[]` from `selectEdges()` for pre-selected edges
136
-
137
- Throws if no edges match the selection, or if `radius` is not a positive finite number.
138
-
139
- ```ts
140
- // Fillet all edges
141
- fillet(myShape, 2)
142
-
143
- // Fillet only top convex edges
144
- fillet(myShape, 1.5, { atZ: 20, convex: true })
145
-
146
- // Fillet vertical edges selected beforehand
147
- const edges = selectEdges(myShape, { parallel: [0, 0, 1] })
148
- fillet(myShape, 3, edges)
149
- ```
150
-
151
- ```ts
152
- fillet(shape: Shape, radius: number, edges?: EdgeSelector, segments?: number): Shape
153
- ```
154
-
155
- #### `chamfer()` — Apply experimental chamfers (beveled edges) to one or more edges of a shape.
156
-
157
- **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.
158
-
159
- 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.
160
-
161
- The `edges` parameter accepts the same options as `fillet()`: inline `EdgeQuery`, pre-selected `EdgeSegment`/`EdgeSegment[]`, or `undefined` (all sharp edges).
162
-
163
- ```ts
164
- // Chamfer all edges
165
- chamfer(myShape, 1)
166
-
167
- // Chamfer only vertical edges
168
- chamfer(myShape, 2, { parallel: [0, 0, 1] })
169
- ```
170
-
171
- ```ts
172
- chamfer(shape: Shape, size: number, edges?: EdgeSelector): Shape
173
- ```
174
-
175
- #### `draft()` — Apply a draft angle (taper) to vertical faces for mold extraction.
176
-
177
- 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°.
178
-
179
- Truck supports vertical-prism solids with Z-axis pull directions. OCCT uses its native draft operation when available. Manifold throws.
180
-
181
- ```ts
182
- // Add 3° draft to a box for injection molding
183
- draft(myBox, 3)
184
-
185
- // Draft with custom pull direction and neutral plane
186
- draft(myShape, 2, [0, 0, 1], 10)
187
- ```
188
-
189
- ```ts
190
- draft(shape: Shape, angleDeg: number, pullDirection?: [ number, number, number ], neutralPlaneOffset?: number): Shape
191
- ```
192
-
193
- #### `offsetSolid()` — Uniformly offset all surfaces of a solid inward or outward.
194
-
195
- 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.
196
-
197
- Requires the OCCT backend. Throws on Manifold.
198
-
199
- ```ts
200
- // Grow a box outward by 1mm on all sides
201
- offsetSolid(myBox, 1)
202
-
203
- // Shrink a shape inward by 0.5mm
204
- offsetSolid(myShape, -0.5)
205
- ```
206
-
207
- ```ts
208
- offsetSolid(shape: Shape, thickness: number): Shape
209
- ```
210
-
211
- ### Patterns & Layout
212
-
213
- #### `circularLayout()` — Compute evenly-spaced positions around a circle.
214
-
215
- Eliminates the most common trig pattern in CAD scripts:
216
-
217
- ```js
218
- // Before — manual trig
219
- for (let i = 0; i < 12; i++) {
220
- const angle = i * 30 * Math.PI / 180;
221
- markers.push(marker.translate(r * Math.cos(angle), r * Math.sin(angle), 0));
222
- }
223
-
224
- // After — declarative
225
- for (const {x, y} of circularLayout(12, r)) {
226
- markers.push(marker.translate(x, y, 0));
227
- }
228
- ```
229
-
230
- ```ts
231
- circularLayout(count: number, radius: number, options?: CircularLayoutOptions): LayoutPoint[]
232
- ```
233
-
234
- **`CircularLayoutOptions`**
235
- - `startDeg?: number` — Angle of the first element in degrees (default: 0 = +X axis).
236
- - `centerX?: number` — Center X coordinate (default: 0).
237
- - `centerY?: number` — Center Y coordinate (default: 0).
238
-
239
- `LayoutPoint`: `{ x: number, y: number }`
240
-
241
- #### `polygonVertices()` — Compute the vertex positions of a regular polygon.
242
-
243
- Default orientation places the first vertex at the top (90 degrees), matching the convention used by [`ngon()`](/docs/sketch#ngon).
244
-
245
- Eliminates manual Math.sqrt(3) for triangles, pentagon vertex math, etc:
246
-
247
- ```js
248
- // Before — manual equilateral triangle
249
- const v1 = [center.x - r/2, center.y + r * Math.sqrt(3)/2];
250
- const v2 = [center.x - r/2, center.y - r * Math.sqrt(3)/2];
251
- const v3 = [center.x + r, center.y];
252
-
253
- // After — declarative
254
- const [v1, v2, v3] = polygonVertices(3, r);
255
- ```
256
-
257
- ```ts
258
- polygonVertices(sides: number, radius: number, options?: PolygonVerticesOptions): LayoutPoint[]
259
- ```
260
-
261
- **`PolygonVerticesOptions`**
262
- - `startDeg?: number` — Angle of the first vertex in degrees (default: 90 = top).
263
- - `centerX?: number` — Center X coordinate (default: 0).
264
- - `centerY?: number` — Center Y coordinate (default: 0).
265
-
266
- #### `linearPattern()` — Repeat a shape in a linear pattern along a direction vector and union the copies.
267
-
268
- 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.
269
-
270
- ```ts
271
- // 5 cylinders, 20mm apart along X
272
- linearPattern(cylinder(10, 3), 5, 20, 0)
273
- ```
274
-
275
- ```ts
276
- linearPattern(shape: Shape, count: number, dx: number, dy: number, dz?: number): Shape
277
- ```
278
-
279
- #### `circularPattern()` — Repeat a shape in a circular pattern around an axis and union the copies.
280
-
281
- 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.
282
-
283
- Two calling conventions:
284
-
285
- - **Simple** (Z axis): `circularPattern(shape, 6)` or `circularPattern(shape, 6, centerX, centerY)`
286
- - **Advanced** (arbitrary axis): `circularPattern(shape, 6, { axis, origin })`
287
-
288
- ```ts
289
- // 8 holes evenly spaced around origin
290
- circularPattern(cylinder(12, 4).translate(30, 0, -1), 8)
291
-
292
- // Circular pattern around X axis
293
- circularPattern(myFeature, 4, { axis: [1, 0, 0], origin: [0, 0, 50] })
294
- ```
295
-
296
- ```ts
297
- circularPattern(shape: Shape, count: number, centerXOrOpts?: number | CircularPatternOptions, centerY?: number): Shape
298
- ```
299
-
300
- **`CircularPatternOptions`**
301
-
302
- | Option | Type | Description |
303
- |--------|------|-------------|
304
- | `centerX?` | `number` | Center X of the rotation (default: 0). Used when the rotation axis is Z. |
305
- | `centerY?` | `number` | Center Y of the rotation (default: 0). Used when the rotation axis is Z. |
306
- | `axis?` | `[ number, number, number ]` | Rotation axis direction (default: [0, 0, 1] = Z axis). |
307
- | `origin?` | `[ number, number, number ]` | Pivot point for the rotation (default: [0, 0, 0]). Overrides centerX/centerY when set. |
308
-
309
- #### `linearPattern2d()` — Repeat a 2D sketch in a linear pattern and union the copies.
310
-
311
- ```ts
312
- linearPattern2d(sketch: Sketch, count: number, dx: number, dy?: number): Sketch
313
- ```
314
-
315
- #### `circularPattern2d()` — Repeat a 2D sketch in a circular pattern around a center point and union the copies.
316
-
317
- ```ts
318
- circularPattern2d(sketch: Sketch, count: number, centerXOrOpts?: number | { centerX?: number; centerY?: number; startDeg?: number; }, centerY?: number): Sketch
319
- ```
320
-
321
- #### `mirrorCopy()` — Mirror a shape across a plane and union the mirror with the original.
322
-
323
- 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.
324
-
325
- ```ts
326
- // Mirror across the YZ plane (X=0)
327
- mirrorCopy(box(50, 30, 10), [1, 0, 0])
328
- ```
329
-
330
- ```ts
331
- mirrorCopy(shape: Shape, normal: [ number, number, number ]): Shape
332
- ```
333
-
334
- #### `selectEdges()` — Select all edges from a shape that match the given query.
335
-
336
- 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.
337
-
338
- 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.
339
-
340
- ```ts
341
- // Fillet all top edges of a box
342
- const topEdges = selectEdges(part, { atZ: 20, perpendicular: [0, 0, 1] });
343
- let result = part;
344
- for (const edge of coalesceEdges(topEdges)) {
345
- result = fillet(result, 2, edge);
346
- }
347
- ```
348
-
349
- ```ts
350
- selectEdges(shape: Shape, query?: EdgeQuery): EdgeSegment[]
351
- ```
352
-
353
- **`EdgeQuery`**
354
-
355
- | Option | Type | Description |
356
- |--------|------|-------------|
357
- | `near?` | `Vec3` | Sort by proximity to this point (closest first). When used with `selectEdge`, picks the closest match. |
358
- | `parallel?` | `Vec3` | Filter: edge direction approximately parallel to this vector. |
359
- | `perpendicular?` | `Vec3` | Filter: edge direction approximately perpendicular to this vector. |
360
- | `convex?` | `boolean` | Filter: only convex (outside corner) edges. |
361
- | `concave?` | `boolean` | Filter: only concave (inside corner) edges. |
362
- | `minAngle?` | `number` | Filter: minimum dihedral angle in degrees. |
363
- | `maxAngle?` | `number` | Filter: maximum dihedral angle in degrees. |
364
- | `minLength?` | `number` | Filter: minimum edge length. |
365
- | `maxLength?` | `number` | Filter: maximum edge length. |
366
- | `within?` | `BoundingRegion` | Filter: edge midpoint must be within this bounding region. |
367
- | `atZ?` | `number` | Shorthand: edge midpoint Z ≈ this value (within `tolerance`). Equivalent to `within: { zMin: atZ - tol, zMax: atZ + tol }`. |
368
- | `tolerance?` | `number` | Position tolerance for approximate matches (default: `1.0`). Used by `atZ` and `near`. |
369
- | `angleTolerance?` | `number` | Angular tolerance in degrees for `parallel`/`perpendicular` filters (default: `10`). |
370
-
371
- `BoundingRegion`: `{ xMin?: number, xMax?: number, yMin?: number, yMax?: number, zMin?: number, zMax?: number }`
372
-
373
- **`EdgeSegment`**
374
-
375
- | Option | Type | Description |
376
- |--------|------|-------------|
377
- | `index` | `number` | Stable index within the extraction (deterministic for a given mesh). |
378
- | `direction` | `Vec3` | Normalized direction from start → end. |
379
- | `dihedralAngle` | `number` | Dihedral angle in degrees (0 = coplanar, 180 = knife edge). |
380
- | `convex` | `boolean` | true = outside corner (convex), false = inside corner (concave). |
381
- | `normalA` | `Vec3` | Normal of first adjacent face. |
382
- | `normalB` | `Vec3` | Normal of second adjacent face (same as normalA for boundary edges). |
383
- | `boundary` | `boolean` | true if this is a boundary (unmatched) edge — unusual for closed solids. |
384
- | `start`, `end`, `midpoint`, `length` | | — |
385
-
386
- #### `selectEdge()` — Select the single best-matching edge from a shape.
387
-
388
- 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.
389
-
390
- ```ts
391
- // Chamfer one specific edge near a known point
392
- const bottomEdge = selectEdge(part, { near: [25, 0, 0], atZ: 0 });
393
- result = chamfer(result, 1.5, bottomEdge);
394
- ```
395
-
396
- ```ts
397
- selectEdge(shape: Shape, query?: EdgeQuery): EdgeSegment
398
- ```
399
-
400
- #### `coalesceEdges()` — Merge collinear edge segments into longer logical edges.
401
-
402
- 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.
403
-
404
- The `tolerance` controls the maximum perpendicular distance from collinearity before two segments are considered non-collinear. Default: `0.01`.
405
-
406
- ```ts
407
- const topEdges = selectEdges(part, { atZ: 20 });
408
- for (const edge of coalesceEdges(topEdges)) {
409
- result = fillet(result, 2, edge);
410
- }
411
- ```
412
-
413
- ```ts
414
- coalesceEdges(segments: EdgeSegment[], tolerance?: number): EdgeSegment[]
415
- ```
416
-
417
- ### Imports & Composition
418
-
419
- #### `require()` — Import a module with optional ForgeCAD parameter overrides. Returns the module's exports.
420
-
421
- When importing a `.forge.js` file, the return value is what the script returns. If the script returns a metadata object (e.g. `{ shape: myShape, bolts: {...} }`), the caller receives the full object — renderable values and metadata together.
422
-
423
- **Path rule:** Always include the file extension in relative imports: use `require("./part.forge.js")` for model files and `require("./helpers.js")` for plain helper modules. ForgeCAD does not apply Node-style extension inference, so `require("./part")` will not find `part.forge.js` or `part.js`.
424
-
425
- **Parameter scoping:** Parameters declared in required files are automatically namespaced with a `"filename#N / "` prefix (e.g. `"bracket.forge.js#1 / Width"`). This prevents collisions when multiple files declare same-named params. Each file's params appear as separate sliders.
426
-
427
- **Parameter overrides:** When passing overrides, use the bare param name (not the scoped name). Overrides are type-checked — unrecognized keys throw an error with typo suggestions.
428
-
429
- **Multi-file assembly pattern** — pass cross-cutting design values from the assembly to parts:
430
-
431
- ```js
432
- // assembly.forge.js — owns cross-cutting params, passes to parts
433
- const wall = param("Wall", 3);
434
- const baseH = param("Base Height", 20);
435
-
436
- const mount = require('./motor-mount.forge.js', { Wall: wall });
437
- const base = require('./base-body.forge.js', { Wall: wall, Height: baseH });
438
- ```
439
-
440
- **Metadata pattern** — parts publish interface data alongside geometry:
441
-
442
- ```js
443
- // motor-mount.forge.js
444
- return { shape: mount, bolts: { dia: 5.3, pos: holePositions } };
445
-
446
- // base-body.forge.js
447
- const mount = require('./motor-mount.forge.js');
448
- mount.bolts.pos // access the metadata
449
- mount.shape // access the geometry
450
- ```
451
-
452
- ```ts
453
- require(path: string, paramOverrides?: Record<string, number | string>): any
454
- ```
455
-
456
- #### `importSvgSketch()` — Parse an SVG file and return it as a Sketch with options for region filtering, scaling, and simplification.
457
-
458
- ```ts
459
- importSvgSketch(fileName: string, options?: SvgImportOptions): Sketch
460
- ```
461
-
462
- **`SvgImportOptions`**
463
-
464
- | Option | Type | Description |
465
- |--------|------|-------------|
466
- | `include?` | `"auto" \| "fill" \| "stroke" \| "fill-and-stroke"` | Which geometry channels to include: - `auto`: prefer fills; if no fill geometry exists, fall back to strokes - `fill`: import only filled regions - `stroke`: import only stroke geometry - `fill-and-stroke`: include both |
467
- | `regionSelection?` | `"all" \| "largest"` | Keep all disconnected regions, or only the largest. |
468
- | `maxRegions?` | `number` | Keep at most this many regions (largest-first). |
469
- | `minRegionArea?` | `number` | Drop regions below this absolute area threshold. |
470
- | `minRegionAreaRatio?` | `number` | Drop regions below this ratio of largest-region area. |
471
- | `flattenTolerance?` | `number` | Curve flattening tolerance in SVG user units. Smaller = more segments, higher fidelity. |
472
- | `arcSegments?` | `number` | Minimum segment count for arc discretization. |
473
- | `scale?` | `number` | Global scale applied after SVG parsing. |
474
- | `maxWidth?` | `number` | Maximum imported sketch width. If exceeded, geometry is uniformly downscaled to fit. |
475
- | `maxHeight?` | `number` | Maximum imported sketch height. If exceeded, geometry is uniformly downscaled to fit. |
476
- | `centerOnOrigin?` | `boolean` | Recenter imported geometry so its 2D bounds center is at CAD origin. |
477
- | `simplify?` | `number` | Simplification tolerance for final sketch cleanup. |
478
- | `invertY?` | `boolean` | Flip SVG Y-down coordinates to CAD Y-up. Enabled by default. |
479
-
480
- #### `importMesh()` — Import an external mesh file (STL, OBJ, 3MF) as a Shape.
481
-
482
- ```ts
483
- importMesh(fileName: string, options?: { scale?: number; center?: boolean; }): Shape
484
- ```
485
-
486
- #### `importStep()` — Import a STEP file (.step, .stp) as an exact OCCT-backed Shape. Preserves NURBS curves, B-spline surfaces, and exact topology. Requires running with the OCCT backend.
487
-
488
- ```ts
489
- importStep(fileName: string): Shape
490
- ```
491
-
492
- ### Parameters
493
-
494
- #### `Param.number()` — Declare a numeric parameter that renders as a slider in the UI.
495
-
496
- 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.
497
-
498
- Default range rules when options are omitted:
499
-
500
- - `min` defaults to `0`
501
- - `max` defaults to `defaultValue * 4`
502
- - `step` is auto-calculated: `1` for integer params, `0.1` for ranges ≤ 100, `1` for larger ranges
503
-
504
- 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`).
505
-
506
- ```ts
507
- const width = Param.number("Width", 50);
508
- const angle = Param.number("Angle", 45, { min: 0, max: 180, unit: "°" });
509
- const sides = Param.number("Sides", 6, { min: 3, max: 12, integer: true });
510
- ```
511
-
512
- **Parameter overrides** — key must match `name` exactly:
513
-
514
- ```ts
515
- // Via require()
516
- const bracket = require("./bracket.forge.js", { Width: 80 });
517
-
518
- // Via CLI
519
- // forgecad run model.forge.js --param "Wall Thickness=3"
520
- ```
521
-
522
- Also available as the shorthand alias `param()`.
523
-
524
- ```ts
525
- Param.number(name: string, defaultValue: number, opts?: { min?: number; max?: number; step?: number; unit?: string; integer?: boolean; reverse?: boolean; }): number
526
- ```
527
-
528
- #### `Param.string()` — Declare a string parameter that renders as a text input in the UI.
529
-
530
- String parameters let users type free-form text — labels, names, inscriptions, file paths, etc. The `name` string is the override key.
531
-
532
- ```ts
533
- const label = Param.string("Label", "Hello World");
534
- const name = Param.string("Name", "Part-001", { maxLength: 20 });
535
- ```
536
-
537
- Override via import:
538
-
539
- ```ts
540
- const tag = require("./tag.forge.js", { Label: "Custom Text" });
541
- ```
542
-
543
- Only available as `Param.string()` — no standalone alias.
544
-
545
- ```ts
546
- Param.string(name: string, defaultValue: string, opts?: { maxLength?: number; }): string
547
- ```
548
-
549
- #### `Param.bool()` — Declare a boolean parameter that renders as a checkbox in the UI.
550
-
551
- 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.
552
-
553
- ```ts
554
- const showHoles = Param.bool("Show Holes", true);
555
- if (showHoles) return difference(plate, cylinder(10, 5).translate(50, 30, 0));
556
- return plate;
557
- ```
558
-
559
- Override via import:
560
-
561
- ```ts
562
- const pan = require("./pan.forge.js", { "Show Lid": 0 });
563
- ```
564
-
565
- Also available as the shorthand alias `boolParam()`.
566
-
567
- ```ts
568
- Param.bool(name: string, defaultValue: boolean): boolean
569
- ```
570
-
571
- #### `Param.choice()` — Declare a choice parameter that renders as a dropdown in the UI.
572
-
573
- `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.
574
-
575
- Overrides may be passed as the choice label string (preferred) or as a numeric index. The `name` string is the override key.
576
-
577
- ```ts
578
- const panStyle = Param.choice("Pan Style", "frying-pan", ["frying-pan", "saute-pan", "wok"]);
579
- if (panStyle === "wok") return buildWok();
580
- ```
581
-
582
- Override via import:
583
-
584
- ```ts
585
- const pan = require("./pan.forge.js", { "Pan Style": "wok" });
586
- ```
587
-
588
- Override via CLI:
589
-
590
- ```bash
591
- forgecad run model.forge.js --param "Pan Style=wok"
592
- ```
593
-
594
- Also available as the shorthand alias `choiceParam()`.
595
-
596
- ```ts
597
- Param.choice(name: string, defaultValue: string, choices: string[]): string
598
- ```
599
-
600
- #### `Param.list()` — Declare a list parameter — an array of struct items with per-field UI controls.
601
-
602
- 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.
603
-
604
- Field types:
605
-
606
- - Boolean fields (`boolean: true` in field defs) return as `boolean`
607
- - Choice fields (`choices: [...]` in field defs) return as `string`
608
- - All other fields return as `number`
609
-
610
- ```ts
611
- Param.list<T extends Record<string, number | boolean | string>>(name: string, defaultItems: T[], opts: { ... }): T[]
612
- ```
613
-
614
- `ListParamFieldDef`: `{ min?: number, max?: number, step?: number, unit?: string, integer?: boolean, boolean?: boolean, choices?: string[] }`
615
-
616
- ### Grouping & Local Coordinates
617
-
618
- #### `group()` — Group multiple shapes/sketches for joint transforms without merging into a single mesh.
619
-
620
- 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.).
621
-
622
- 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.
623
-
624
- **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.
625
-
626
- ```js
627
- const body = roundedBox(100, 20, 32, 4);
628
- const panel = box(98, 2, 18).translate(0, -12, 4);
629
- const louver = box(88, 2, 6).translate(0, -14, -11);
630
- const indoorUnit = group(
631
- { name: 'Body', shape: body },
632
- { name: 'Panel', tags: 'cover', shape: panel },
633
- { name: 'Louver', tags: ['cover', 'moving'], shape: louver },
634
- ).translate(0, -18, 70);
635
- ```
636
-
637
- ```ts
638
- group(...items: GroupInput[]): ShapeGroup
639
- ```
640
-
641
- ### Section & Projection
642
-
643
- #### `intersectWithPlane()` — Cross-section: slice a 3D shape with a plane and return the intersection as a 2D Sketch.
644
-
645
- ```ts
646
- intersectWithPlane(shape: Shape, plane: PlaneSpec): Sketch
647
- ```
648
-
649
- #### `faceProfile()` — Extract the boundary profile of a named face as a 2D sketch.
650
-
651
- 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.
652
-
653
- ```ts
654
- faceProfile(shape: Shape, face: FaceSelector): Sketch
655
- ```
656
-
657
- #### `projectToPlane()` — Orthographically project a 3D shape onto a plane and return the silhouette as a 2D Sketch.
658
-
659
- ```ts
660
- projectToPlane(shape: Shape, plane: PlaneSpec): Sketch
661
- ```
662
-
663
- ### Transforms
664
-
665
- #### `composeChain()` — Compose transforms in chain order. Equivalent to Transform.identity().mul(a).mul(b).mul(c)...
666
-
667
- ```ts
668
- composeChain(...steps: TransformInput[]): Transform
669
- ```
670
-
671
- ### Verification
672
-
673
- #### `verify.that()` — Custom predicate check.
674
-
675
- ```ts
676
- verify.that(label: string, check: () => boolean, message?: string): void
677
- ```
678
-
679
- #### `verify.equal()` — Check that two numbers are approximately equal (within tolerance).
680
-
681
- ```ts
682
- verify.equal(label: string, actual: number, expected: number, tolerance?: number, message?: string): void
683
- ```
684
-
685
- #### `verify.notEqual()` — Check that two numbers are NOT equal (differ by more than tolerance).
686
-
687
- ```ts
688
- verify.notEqual(label: string, actual: number, unexpected: number, tolerance?: number, message?: string): void
689
- ```
690
-
691
- #### `verify.greaterThan()` — Check that actual > min.
692
-
693
- ```ts
694
- verify.greaterThan(label: string, actual: number, min: number, message?: string): void
695
- ```
696
-
697
- #### `verify.lessThan()` — Check that actual < max.
698
-
699
- ```ts
700
- verify.lessThan(label: string, actual: number, max: number, message?: string): void
701
- ```
702
-
703
- #### `verify.inRange()` — Check that min <= actual <= max.
704
-
705
- ```ts
706
- verify.inRange(label: string, actual: number, min: number, max: number, message?: string): void
707
- ```
708
-
709
- #### `verify.centersCoincide()` — Check that the bounding-box centers of two shapes coincide within tolerance (mm).
710
-
711
- ```ts
712
- verify.centersCoincide(label: string, a: ShapeLike, b: ShapeLike, tolerance?: number): void
713
- ```
714
-
715
- `ShapeLike`: `{ min: number[], max: number[] }`
716
-
717
- #### `verify.connectorDistance()` — Check the distance between two named connectors on a shape or group.
718
-
719
- 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.
720
-
721
- ```ts
722
- verify.connectorDistance("leg is seated", bench, "Rail.leg_0", "Leg0.head", 0, 0.01);
723
- ```
724
-
725
- ```ts
726
- verify.connectorDistance(label: string, target: ConnectorDistanceLike, connectorA: string, connectorB: string, expected?: number, tolerance?: number): void
727
- ```
728
-
729
- #### `verify.physicalComponentCount()` — Declare the expected physical connectivity component count for the returned visible model.
730
-
731
- 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.
732
-
733
- 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.
734
-
735
- ```ts
736
- verify.physicalComponentCount("vise is one connected installed assembly", 1);
737
- ```
738
-
739
- ```ts
740
- verify.physicalComponentCount(label: string, expected: number): void
741
- ```
742
-
743
- #### `verify.intentionalOverlap()` — Declare that two visible objects intentionally overlap because the overlap is real manufacturing intent.
744
-
745
- 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.
746
-
747
- `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.
748
-
749
- ```ts
750
- verify.intentionalOverlap("rubber grip is overmolded on handle", rubberGrip, handleCore, "overmolded insert");
751
- ```
752
-
753
- ```ts
754
- verify.intentionalOverlap(label: string, a: ShapeLike, b: ShapeLike, reason: string): void
755
- ```
756
-
757
- #### `verify.notColliding()` — Check that two shapes do not share positive volume.
758
-
759
- Face-to-face contact is allowed; use `verify.minClearance()` when an actual running gap is required.
760
-
761
- ```ts
762
- verify.notColliding(label: string, a: ShapeLike, b: ShapeLike, searchLength?: number): void
763
- ```
764
-
765
- #### `verify.minClearance()` — Check that a minimum clearance gap exists between two shapes.
766
-
767
- ```ts
768
- verify.minClearance(label: string, a: ShapeLike, b: ShapeLike, minGap: number, searchLength?: number): void
769
- ```
770
-
771
- #### `verify.clearanceBetween()` — Check that the clearance gap between two shapes is inside an allowed range.
772
-
773
- 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.
774
-
775
- 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.
776
-
777
- 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.
778
-
779
- ```ts
780
- verify.clearanceBetween("cover is seated on gasket", cover, gasket, -0.01, 0.05);
781
- verify.clearanceBetween("carriage runs inside rail", carriage, rail, 0.2, 0.5);
782
- ```
783
-
784
- ```ts
785
- verify.clearanceBetween(label: string, a: ShapeLike, b: ShapeLike, minGap: number, maxGap: number, searchLength?: number): void
786
- ```
787
-
788
- #### `verify.parallel()` — Check that two face normals are parallel (within toleranceDeg degrees).
789
-
790
- ```ts
791
- verify.parallel(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void
792
- ```
793
-
794
- `FaceRefLike`: `{ normal: [ number, number, number ], center: [ number, number, number ] }`
795
-
796
- #### `verify.perpendicular()` — Check that two face normals are perpendicular (within toleranceDeg degrees).
797
-
798
- ```ts
799
- verify.perpendicular(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void
800
- ```
801
-
802
- #### `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.
803
-
804
- ```ts
805
- verify.coplanar(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number, toleranceMm?: number): void
806
- ```
807
-
808
- #### `verify.faceAt()` — Check that a face center lies at a specific position (within toleranceMm).
809
-
810
- ```ts
811
- verify.faceAt(label: string, face: FaceRefLike, expectedPos: [ number, number, number ], toleranceMm?: number): void
812
- ```
813
-
814
- #### `verify.sameDirection()` — Check that two face normals point in the same direction (not antiparallel). Stricter than parallel — both |angle| AND sign must match.
815
-
816
- ```ts
817
- verify.sameDirection(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void
818
- ```
819
-
820
- #### `verify.isEmpty()` — Check that a shape is empty.
821
-
822
- ```ts
823
- verify.isEmpty(label: string, shape: ShapeLike, message?: string): void
824
- ```
825
-
826
- #### `verify.notEmpty()` — Check that a shape is NOT empty.
827
-
828
- ```ts
829
- verify.notEmpty(label: string, shape: ShapeLike, message?: string): void
830
- ```
831
-
832
- #### `verify.volumeApprox()` — Check that a shape's volume is approximately equal to expected (mm³).
833
-
834
- ```ts
835
- verify.volumeApprox(label: string, shape: ShapeLike, expected: number, tolerance?: number): void
836
- ```
837
-
838
- #### `verify.areaApprox()` — Check that a shape's surface area is approximately equal to expected (mm²).
839
-
840
- ```ts
841
- verify.areaApprox(label: string, shape: ShapeLike, expected: number, tolerance?: number): void
842
- ```
843
-
844
- #### `verify.boundingBoxSize()` — Check that a shape's bounding box has approximately the given size.
845
-
846
- ```ts
847
- verify.boundingBoxSize(label: string, shape: ShapeLike, expectedSize: [ number, number, number ], tolerance?: number): void
848
- ```
849
-
850
- #### `verify.edgeContinuity()` — Check that every sampled seam on a shape meets a requested continuity threshold.
851
-
852
- ```ts
853
- verify.edgeContinuity(label: string, shape: ShapeLike, options?: EdgeContinuityThresholds): void
854
- ```
855
-
856
- **`EdgeContinuityThresholds`**: `continuity?: SurfaceContinuity`, `samples?: number`, `positionTolerance?: number`, `tangentToleranceDeg?: number`, `curvatureTolerance?: number`
857
-
858
- #### `verify.noTinyEdges()` — Check that a shape has no tiny edges below the requested threshold.
859
-
860
- ```ts
861
- verify.noTinyEdges(label: string, shape: ShapeLike, threshold?: number): void
862
- ```
863
-
864
- #### `verify.noSliverFaces()` — Check that a shape has no sliver faces below the requested score threshold.
865
-
866
- ```ts
867
- verify.noSliverFaces(label: string, shape: ShapeLike, threshold?: number): void
868
- ```
869
-
870
- #### `verify.noSelfIntersection()` — Best-effort exact-shape validity guard for self-intersections or broken B-Rep topology.
871
-
872
- ```ts
873
- verify.noSelfIntersection(label: string, shape: ShapeLike): void
874
- ```
875
-
876
- #### `spec()` — Create a named, reusable bundle of verification checks.
877
-
878
- 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.
879
-
880
- Specs can be defined in separate `.forge.js` files and imported via `require()` to share them across models.
881
-
882
- `spec.check()` returns a `SpecResult` — you can inspect it programmatically or ignore the return value and let the Checks panel show results.
883
-
884
- ```ts
885
- const printable = spec("Fits printer bed", (shape) => {
886
- verify.notEmpty("Has geometry", shape);
887
- const bb = shape.boundingBox();
888
- verify.lessThan("Width < 220mm", bb.max[0] - bb.min[0], 220);
889
- verify.lessThan("Depth < 220mm", bb.max[1] - bb.min[1], 220);
890
- verify.lessThan("Height < 250mm", bb.max[2] - bb.min[2], 250);
891
- });
892
-
893
- // Reuse on multiple shapes
894
- printable.check(bracket);
895
- printable.check(standoff);
896
-
897
- // Check relationships between parts
898
- const fitSpec = spec("Assembly fit", (partA, partB) => {
899
- verify.notColliding("No interference", partA, partB, 10);
900
- });
901
- fitSpec.check(bracket, standoff);
902
- ```
903
-
904
- **Spec-first workflow:** Write specs before building geometry. Checks go from red to green as you build — effectively TDD for CAD.
905
-
906
- ```ts
907
- spec(name: string, checkFn: (...args: any[]) => void): Spec
908
- ```
909
-
910
- **`Spec`**
911
- - `name: string` — The display name of this spec
912
-
913
- ---
914
-
915
- ## Classes
916
-
917
- ### `Shape`
918
-
919
- Core 3D solid shape. All operations are immutable and return new shapes.
920
-
921
- Supports transforms (translate, rotate, scale, mirror, transform, rotateAround, pointAlong), booleans (add, subtract, intersect), cutting (split, splitByPlane, trimByPlane), shelling, anchor positioning (attachTo, onFace), placement references, and queries (volume, surfaceArea, boundingBox, isEmpty, numTri, geometryInfo).
922
-
923
- **Properties:**
924
-
925
- | Property | Type | Description |
926
- |----------|------|-------------|
927
- | `materialProps` | `ShapeMaterialProps | undefined` | — |
928
-
929
- **Appearance**
930
-
931
- #### `color()` — Set the color of this shape (hex string, e.g. "#ff0000"). Returns a new Shape with the color applied.
932
-
933
- ```ts
934
- color(value: string | undefined): Shape
935
- ```
936
-
937
- #### `material()` — Set PBR material properties for this shape's visual appearance.
938
-
939
- Returns a new Shape with the specified material properties merged on top of any previously set properties. All properties are optional — omitted keys retain their current value. Material properties survive transforms and boolean operations.
940
-
941
- Use `.color()` to set the base diffuse color; `.material()` controls how that color behaves under light (metalness, roughness, clearcoat) and can add emissive glow independent of lighting. Emissive glow pairs naturally with the `postProcessing.bloom` effect in [`scene()`](/docs/viewport#scene).
942
-
943
- ```js
944
- box(50, 50, 50).material({ metalness: 0.9, roughness: 0.1 }); // polished metal
945
- sphere(30).material({ emissive: '#ff6b35', emissiveIntensity: 2 }); // glowing
946
- cylinder(40, 20).material({ opacity: 0.4, clearcoat: 1.0, clearcoatRoughness: 0.02 }); // ice
947
-
948
- // Chainable with other shape methods
949
- box(100, 100, 10).color('#gold').material({ metalness: 0.95, roughness: 0.05 }).translate(0, 0, 50);
950
- ```
951
-
952
- ```ts
953
- material(props: ShapeMaterialProps): Shape
954
- ```
955
-
956
- **Face Topology**
957
-
958
- #### `face()` — Resolve a face by user-authored label or compiler-owned name. Returns a `FaceRef` that can be passed to `.onFace()`, `projectToPlane()`, or used directly in placement.
959
-
960
- `.face(name)` is a pure label lookup — it finds faces by user-authored labels, not by geometric queries. Labels are born in sketches via `.label()` / `.labelEdges()` and grow into face names through extrude, loft, revolve, and sweep. They are stable references that travel with the geometry.
961
-
962
- Labels must be unique within a shape. Use `.prefixLabels()` before combining shapes with `union()` / `difference()` to avoid collisions. Collision detection throws a clear error with a fix suggestion.
963
-
964
- For compile-covered shapes (extrude, loft, etc.) the lookup resolves via the shape's compile plan. As a fallback, planar-faced mesh shapes (e.g. results of boolean ops) are resolved via coplanar triangle clustering.
965
-
966
- ```ts
967
- // Edge labels become side face names after extrude
968
- const profile = path()
969
- .moveTo(0, 0)
970
- .lineTo(100, 0).label('floor')
971
- .lineTo(100, 50).label('wall')
972
- .lineTo(0, 50).label('ceiling')
973
- .closeLabel('left-wall');
974
- const room = profile.extrude(30, { labels: { start: 'base', end: 'top' } });
975
- room.face('floor'); // side face from the labeled edge
976
- room.face('base'); // base cap (user-specified)
977
-
978
- // .labelEdges() shorthand for sequential edge labeling
979
- const plate = rect(100, 50).labelEdges('south', 'east', 'north', 'west');
980
- const solid = plate.extrude(20, { labels: { start: 'bottom', end: 'top' } });
981
- solid.face('south'); // side face
982
-
983
- // Prefix before combining to avoid collisions
984
- const left = wing.prefixLabels('l/');
985
- const right = wing.mirror([1, 0, 0]).prefixLabels('r/');
986
- const full = union(left, right);
987
- full.face('l/upper'); // left wing upper surface
988
- ```
989
-
990
- ```ts
991
- face(selector: FaceSelector): FaceRef
992
- ```
993
-
994
- #### `faces()` — Return faces matching a query, or label semantic faces when passed a mapping.
995
-
996
- Mapping form returns a new shape: `shape.faces({ lid: 'top', walls: ['front', 'back', 'left', 'right'] })`.
997
-
998
- ```ts
999
- faces(): FaceRef[]
1000
- ```
1001
-
1002
- #### `faceNames()` — List defined semantic face names currently available on this shape.
1003
-
1004
- ```ts
1005
- faceNames(): string[]
1006
- ```
1007
-
1008
- #### `prefixLabels()` — Prefix all user-authored face labels, including semantic labels from `faces(mapping)`. Returns a new shape with modified labels.
1009
-
1010
- ```ts
1011
- prefixLabels(prefix: string): Shape
1012
- ```
1013
-
1014
- #### `renameLabel()` — Rename a single face label. Returns a new shape.
1015
-
1016
- ```ts
1017
- renameLabel(from: string, to: string): Shape
1018
- ```
1019
-
1020
- #### `dropLabels()` — Remove specific face labels. Returns a new shape.
1021
-
1022
- ```ts
1023
- dropLabels(...names: string[]): Shape
1024
- ```
1025
-
1026
- #### `dropAllLabels()` — Remove all face labels. Returns a new shape.
1027
-
1028
- ```ts
1029
- dropAllLabels(): Shape
1030
- ```
1031
-
1032
- #### `faceHistory()` — Get the transformation history for a specific face.
1033
-
1034
- ```ts
1035
- faceHistory(name: string): FaceTransformationHistory
1036
- ```
1037
-
1038
- **Edge Topology**
1039
-
1040
- #### `edge()` — Get a named topology edge. Only available on shapes with tracked topology (from box/cylinder/extrude).
1041
-
1042
- ```ts
1043
- edge(name: string): EdgeRef
1044
- ```
1045
-
1046
- #### `edgeNames()` — List named topology edge names. Returns empty array if shape has no tracked topology.
1047
-
1048
- ```ts
1049
- edgeNames(): string[]
1050
- ```
1051
-
1052
- #### `edgesOf()` — Return all boundary edges of a named face.
1053
-
1054
- Finds edges where one adjacent mesh face belongs to the target face and the other belongs to a different face. The result is coalesced (tessellation fragments merged) and can be passed directly to `fillet()` or `chamfer()`.
1055
-
1056
- This is a topological query — no coordinates, no tolerances, no minimum-length hacks. It works because an edge is the boundary between two faces.
1057
-
1058
- ```js
1059
- // Fillet all top edges of a mounting plate
1060
- let plate = box(120, 80, 6).faces({ workSurface: 'top' })
1061
- plate = fillet(plate, 3, plate.edgesOf('workSurface'))
1062
-
1063
- // Shelled enclosure — fillet the outer lip
1064
- let body = box(80, 50, 35).faces({ opening: 'top' })
1065
- body = body.shell(2, { openFaces: ['top'] })
1066
- body = fillet(body, 1.5, body.edgesOf('opening'))
1067
-
1068
- // Filter: only concave edges (after a boolean subtraction)
1069
- body.edgesOf('top', { concave: true })
1070
- ```
1071
-
1072
- ```ts
1073
- edgesOf(faceLabel: string, options?: EdgesOfOptions): EdgeSegment[]
1074
- ```
1075
-
1076
- #### `edgesBetween()` — Return edges shared between two named faces.
1077
-
1078
- An edge is "between" faces A and B when one of its adjacent mesh triangles belongs to A and the other belongs to B. This is the most precise topological edge selection — "fillet the edges where the top meets the wall."
1079
-
1080
- The second argument can be a single face name or an array (edges between A and any of B1, B2, ...).
1081
-
1082
- ```js
1083
- // Fillet the edge where lid meets one wall
1084
- let body = box(100, 60, 30).faces({ lid: 'top', wall: 'side-left' })
1085
- body = fillet(body, 2, body.edgesBetween('lid', 'wall'))
1086
-
1087
- // Fillet a cylinder rim — where the flat cap meets the curved barrel
1088
- let tube = cylinder(30, 10).faces({ cap: 'top', barrel: 'side' })
1089
- tube = fillet(tube, 1, tube.edgesBetween('cap', 'barrel'))
1090
-
1091
- // Multiple target faces at once
1092
- body.edgesBetween('lid', ['left-wall', 'right-wall', 'front-wall', 'back-wall'])
1093
- ```
1094
-
1095
- ```ts
1096
- edgesBetween(faceA: string, faceB: string | string[]): EdgeSegment[]
1097
- ```
1098
-
1099
- **Transforms**
1100
-
1101
- #### `translate()` — Move the shape relative to its current position. All transforms are immutable and return new shapes.
1102
-
1103
- ```ts
1104
- translate(x: number, y: number, z: number): Shape
1105
- ```
1106
-
1107
- #### `translatePolar()` — Translate using polar coordinates (radius + angle in degrees). Eliminates manual `r * Math.cos(angle * PI/180)` calculations.
1108
-
1109
- Example: `shape.translatePolar(50, 30)` moves 50mm at 30 degrees from +X.
1110
-
1111
- ```ts
1112
- translatePolar(radius: number, angleDeg: number, z?: number): Shape
1113
- ```
1114
-
1115
- #### `moveTo()` — Position the shape so its bounding box min corner is at the given global coordinate.
1116
-
1117
- ```ts
1118
- moveTo(x: number, y: number, z: number): Shape
1119
- ```
1120
-
1121
- #### `moveToLocal()` — Position the shape relative to another shape's local coordinate system (bounding box min corner).
1122
-
1123
- ```ts
1124
- moveToLocal(target: Shape | { toShape(): Shape; }, x: number, y: number, z: number): Shape
1125
- ```
1126
-
1127
- #### `rotate()` — Rotate around an arbitrary axis through the origin.
1128
-
1129
- ```ts
1130
- rotate(axis: [ number, number, number ], angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape
1131
- ```
1132
-
1133
- #### `rotateX()` — Rotate around the X axis by the given angle in degrees.
1134
-
1135
- ```ts
1136
- rotateX(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape
1137
- ```
1138
-
1139
- #### `rotateY()` — Rotate around the Y axis by the given angle in degrees.
1140
-
1141
- ```ts
1142
- rotateY(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape
1143
- ```
1144
-
1145
- #### `rotateZ()` — Rotate around the Z axis by the given angle in degrees.
1146
-
1147
- ```ts
1148
- rotateZ(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape
1149
- ```
1150
-
1151
- #### `rotateAroundTo()` — Rotate around an axis until a moving point reaches the target line/plane defined by the axis and target point. `movingPoint` / `targetPoint` may be raw world points or this shape's anchors/references.
1152
-
1153
- ```ts
1154
- rotateAroundTo(axis: [ number, number, number ], pivot: [ number, number, number ], movingPoint: RotationPointLike, targetPoint: RotationPointLike, options?: RotateAroundToOptions): Shape
1155
- ```
1156
-
1157
- #### `transform()` — Apply a 4x4 affine transform matrix (column-major) or a Transform object.
1158
-
1159
- ```ts
1160
- transform(m: Mat4 | Transform): Shape
1161
- ```
1162
-
1163
- #### `scale()` — Scale the shape uniformly or per-axis from the shape's bounding box center. Accepts a single number or [x, y, z] array.
1164
-
1165
- ```ts
1166
- scale(v: number | [ number, number, number ]): Shape
1167
- ```
1168
-
1169
- #### `scaleAround()` — Scale the shape uniformly or per-axis from an explicit pivot point.
1170
-
1171
- ```ts
1172
- scaleAround(pivot: [ number, number, number ], v: number | [ number, number, number ]): Shape
1173
- ```
1174
-
1175
- #### `mirror()` — Mirror across a plane through the shape's bounding box center, defined by its normal vector.
1176
-
1177
- ```ts
1178
- mirror(normal: [ number, number, number ]): Shape
1179
- ```
1180
-
1181
- #### `mirrorThrough()` — Mirror across a plane through an explicit point, defined by its normal vector.
1182
-
1183
- ```ts
1184
- mirrorThrough(point: [ number, number, number ], normal: [ number, number, number ]): Shape
1185
- ```
1186
-
1187
- #### `pointAlong()` — Reorient a shape so its primary axis (Z) points along the given direction. Useful for laying cylinders/extrusions along X or Y without thinking about Euler angles. The shape's origin stays at [0,0,0] — translate after pointAlong to position it.
1188
-
1189
- Example: cylinder(40, 5).pointAlong([1, 0, 0]) — lays cylinder along X, starting at origin
1190
-
1191
- ```ts
1192
- pointAlong(direction: [ number, number, number ]): Shape
1193
- ```
1194
-
1195
- **Booleans & Cutting**
1196
-
1197
- #### `add()` — Union this shape with others (additive boolean). Method form of union().
1198
-
1199
- ```ts
1200
- add(...others: ShapeOperandInput[]): Shape
1201
- ```
1202
-
1203
- #### `subtract()` — Subtract other shapes from this one. Method form of difference().
1204
-
1205
- ```ts
1206
- subtract(...others: ShapeOperandInput[]): Shape
1207
- ```
1208
-
1209
- #### `intersect()` — Keep only the overlap with other shapes. Method form of intersection().
1210
-
1211
- ```ts
1212
- intersect(...others: ShapeOperandInput[]): Shape
1213
- ```
1214
-
1215
- #### `split()` — Split into [inside, outside] by another shape.
1216
-
1217
- ```ts
1218
- split(cutter: Shape | { toShape(): Shape; }): [ Shape, Shape ]
1219
- ```
1220
-
1221
- #### `splitByPlane()` — Split by infinite plane. Returns [positive-side, negative-side].
1222
-
1223
- ```ts
1224
- splitByPlane(normal: [ number, number, number ], originOffset?: number): [ Shape, Shape ]
1225
- ```
1226
-
1227
- #### `trimByPlane()` — Keep the positive side of the plane and discard the opposite side.
1228
-
1229
- ```ts
1230
- trimByPlane(normal: [ number, number, number ], originOffset?: number): Shape
1231
- ```
1232
-
1233
- **Features**
1234
-
1235
- #### `shell()` — Hollow out compile-covered boxes, cylinders, and straight extrudes. `openFaces` names any subset of the base shape's labeled faces to leave open (no wall).
1236
-
1237
- ```ts
1238
- shell(thickness: number, opts?: { openFaces?: string[]; }): Shape
1239
- ```
1240
-
1241
- #### `pocket()` — Cut a pocket (cavity) into this solid through the named face.
1242
-
1243
- ```js
1244
- box(100, 100, 20).pocket('top', 8)
1245
- box(100, 100, 20).pocket('top', 8, { inset: 5 })
1246
- box(100, 100, 20).pocket('top', 8, { scale: 0.8 })
1247
- ```
1248
-
1249
- ```ts
1250
- pocket(face: FaceSelector, depth: number, opts?: PocketOptions): Shape
1251
- ```
1252
-
1253
- #### `boss()` — Add a boss (protrusion) from the named face.
1254
-
1255
- ```js
1256
- box(100, 100, 20).boss('top', 5)
1257
- box(100, 100, 20).boss('top', 10, { scale: 0.6 })
1258
- ```
1259
-
1260
- ```ts
1261
- boss(face: FaceSelector, height: number, opts?: BossOptions): Shape
1262
- ```
1263
-
1264
- #### `hole()` — Drill a hole into this solid at a face.
1265
-
1266
- ```js
1267
- box(50, 50, 20).hole('top', { diameter: 8, depth: 10 })
1268
- box(50, 50, 20).hole('top', { diameter: 6, counterbore: { diameter: 12, depth: 3 } })
1269
- ```
1270
-
1271
- ```ts
1272
- hole(faceOrRef: SketchFaceTarget | FaceRef, opts: ShapeHoleOptions): Shape
1273
- ```
1274
-
1275
- #### `cutout()` — Cut a profile-shaped pocket through a face using a placed sketch.
1276
-
1277
- The sketch must be placed on a face with `Sketch.onFace(...)`. The cut follows the sketch's 2D profile.
1278
-
1279
- ```js
1280
- const profile = circle2d(10).onFace(body, 'top');
1281
- body.cutout(profile, { depth: 5 })
1282
- ```
1283
-
1284
- ```ts
1285
- cutout(sketch: Sketch, opts?: ShapeCutoutOptions): Shape
1286
- ```
1287
-
1288
- **Placement**
1289
-
1290
- #### `placeReference()` — Translate the shape so the given anchor or reference lands on the target coordinate.
1291
-
1292
- Accepts any built-in anchor name (`'bottom'`, `'center'`, `'top-front-left'`, etc.) or a custom placement reference attached via `withReferences()`.
1293
-
1294
- ```javascript
1295
- // Ground a shape — put its bottom face center at Z = 0
1296
- shape.placeReference('bottom', [0, 0, 0])
1297
-
1298
- // Center at the world origin
1299
- shape.placeReference('center', [0, 0, 0])
1300
-
1301
- // Align left edge to X = 10
1302
- shape.placeReference('left', [10, 0, 0])
1303
- ```
1304
-
1305
- ```ts
1306
- placeReference(ref: PlacementAnchorLike, target: [ number, number, number ], offset?: [ number, number, number ]): Shape
1307
- ```
1308
-
1309
- #### `attachTo()` — Position this shape relative to another using named 3D anchor points.
1310
-
1311
- Anchors are bounding-box-relative: 'center', face centers ('top', 'front', ...), edge midpoints ('top-front', 'back-left', ...), and corners ('top-front-left', ...). Anchor word order is flexible: 'front-left' and 'left-front' are equivalent. Named placement references (from withReferences) can also be used as anchors.
1312
-
1313
- ```ts
1314
- attachTo(target: ShapeAnchorTarget, targetAnchor: PlacementAnchorLike, selfAnchor?: PlacementAnchorLike, offset?: [ number, number, number ]): Shape
1315
- ```
1316
-
1317
- #### `onFace()` — Place this shape on a face of a parent shape.
1318
-
1319
- Think of it like sticking a label on a box surface:
1320
-
1321
- - `face` picks which surface ('front', 'back', 'top', etc.)
1322
- - `u, v` position within that face's 2D plane (from center)
1323
- - front/back: u = left/right (X), v = up/down (Z)
1324
- - left/right: u = forward/back (Y), v = up/down (Z)
1325
- - top/bottom: u = left/right (X), v = forward/back (Y)
1326
- - `protrude` = how far the child sticks out (positive = outward from face)
1327
-
1328
- ```ts
1329
- onFace(parent: ShapeAnchorTarget, face: "front" | "back" | "left" | "right" | "top" | "bottom", opts?: { u?: number; v?: number; protrude?: number; }): Shape
1330
- ```
1331
-
1332
- #### `seatInto()` — Slide this shape along an axis until a labeled face is embedded in the target body.
1333
-
1334
- Position the shape roughly first (translate/rotate), then call seatInto to auto-adjust the penetration depth. No manual coordinate math needed.
1335
-
1336
- ```js
1337
- // Wing root embeds into fuselage — adapts to any fuselage shape
1338
- wing.translate(0, wingY, 0).seatInto(fuselage, 'root');
1339
-
1340
- // Sensor pod sits flush on fuselage surface
1341
- pod.translate(0, station, radius + 20).seatInto(fuselage, 'base', { depth: 'flush' });
1342
-
1343
- // Antenna with 3mm gasket standoff
1344
- mast.translate(0, station, radius + 50).seatInto(fuselage, 'mount', { depth: 'flush', gap: 3 });
1345
- ```
1346
-
1347
- ```ts
1348
- seatInto(target: Shape, surface: string, options?: SeatIntoOptions): Shape
1349
- ```
1350
-
1351
- #### `seatOver()` — Slide this shape until a target's labeled face is fully covered (inside this shape).
1352
-
1353
- The inverse of `seatInto`: instead of embedding *your* face into the target, you move until the *target's* face is embedded inside you.
1354
-
1355
- ```js
1356
- // Nacelle moves up until pylon's bottom face is inside the nacelle
1357
- nacelle.translate(rough).seatOver(pylon, 'bottom');
1358
-
1359
- // Cap slides down over a post until post's top face is covered
1360
- cap.translate(rough).seatOver(post, 'top');
1361
- ```
1362
-
1363
- ```ts
1364
- seatOver(target: Shape, targetSurface: string, options?: SeatIntoOptions): Shape
1365
- ```
1366
-
1367
- **Connectors**
1368
-
1369
- #### `withConnectors()` — Attach named connectors — attachment points that survive transforms and imports. Connectors can be bare (position + orientation) or typed (with connectorType/gender for compatibility matching).
1370
-
1371
- ```ts
1372
- withConnectors(connectors: Record<string, ConnectorInput>): Shape
1373
- ```
1374
-
1375
- #### `connectorNames()` — List all connector names on this shape.
1376
-
1377
- ```ts
1378
- connectorNames(): string[]
1379
- ```
1380
-
1381
- #### `connectorsByType()` — Get all connectors of a given type.
1382
-
1383
- ```ts
1384
- connectorsByType(type: string): Array<{ name: string; port: ConnectorDef; }>
1385
- ```
1386
-
1387
- #### `connectorDistance()` — Distance between two connector origins on this shape.
1388
-
1389
- ```ts
1390
- connectorDistance(nameA: string, nameB: string): number
1391
- ```
1392
-
1393
- #### `connectorMeasurements()` — Get measurements metadata from a connector.
1394
-
1395
- ```ts
1396
- connectorMeasurements(name: string): Record<string, number | string>
1397
- ```
1398
-
1399
- #### `matchTo()` — Position this shape by matching connectors to a target.
1400
-
1401
- Overloads:
1402
-
1403
- - Single pair: `matchTo(target, selfConn, targetConn, options?)`
1404
- - Dictionary (same target): `matchTo(target, { selfConn: targetConn, ... }, options?)`
1405
- - Multi-target: `matchTo([ [target1, selfConn1, targetConn1], ... ], options?)`
1406
-
1407
- ```ts
1408
- matchTo(targetOrPairs: Shape | MatchTarget | Array<[ Shape | MatchTarget, string, string ]>, selfConnOrDict?: string | Record<string, string>, targetConnOrOptions?: string | MatchToOptions, maybeOptions?: MatchToOptions): Shape
1409
- ```
1410
-
1411
- **References**
1412
-
1413
- #### `withReferences()` — Attach named placement references that survive normal transforms and imports.
1414
-
1415
- ```ts
1416
- withReferences(refs: PlacementReferenceInput): Shape
1417
- ```
1418
-
1419
- #### `referenceNames()` — List named placement references carried by this shape.
1420
-
1421
- ```ts
1422
- referenceNames(kind?: PlacementReferenceKind): string[]
1423
- ```
1424
-
1425
- #### `referencePoint()` — Resolve a named placement reference or built-in anchor to a 3D point.
1426
-
1427
- ```ts
1428
- referencePoint(ref: PlacementAnchorLike): [ number, number, number ]
1429
- ```
1430
-
1431
- **Measurement**
1432
-
1433
- #### `boundingBox()` — Get the axis-aligned bounding box as { min: [x,y,z], max: [x,y,z] }.
1434
-
1435
- ```ts
1436
- boundingBox(): ShapeRuntimeBounds
1437
- ```
1438
-
1439
- #### `volume()` — Volume in mm cubed.
1440
-
1441
- ```ts
1442
- volume(): number
1443
- ```
1444
-
1445
- #### `surfaceArea()` — Surface area in mm squared.
1446
-
1447
- ```ts
1448
- surfaceArea(): number
1449
- ```
1450
-
1451
- #### `isEmpty()` — True if the shape contains no geometry.
1452
-
1453
- ```ts
1454
- isEmpty(): boolean
1455
- ```
1456
-
1457
- #### `numBodies()` — Number of disconnected solid bodies in this shape.
1458
-
1459
- ```ts
1460
- numBodies(): number
1461
- ```
1462
-
1463
- #### `numTri()` — Triangle count of the mesh representation.
1464
-
1465
- ```ts
1466
- numTri(): number
1467
- ```
1468
-
1469
- **Other**
1470
-
1471
- #### `clone()` — Return a new Shape wrapper for explicit duplication in scripts.
1472
-
1473
- ```ts
1474
- clone(): Shape
1475
- ```
1476
-
1477
- #### `geometryInfo()` — Inspect which backend/representation produced this solid.
1478
-
1479
- ```ts
1480
- geometryInfo(): GeometryInfo
1481
- ```
1482
-
1483
- #### `as()` — Name this shape as a reference namespace for diagnostics and future published refs.
1484
-
1485
- ```ts
1486
- as(name: string): Shape
1487
- ```
1488
-
1489
- #### `ref()` — Resolve a semantic reference path like `lid`, `lid/back`, or a midpoint selector on `lid/back`.
1490
-
1491
- ```ts
1492
- ref(path: string): ShapeRef
1493
- ```
1494
-
1495
- #### `thicken()` — Offset-thicken an exact open surface or shell into a solid.
1496
-
1497
- ```ts
1498
- thicken(thickness: number): Shape
1499
- ```
1500
-
1501
- #### `getMesh()` — Extract triangle mesh for Three.js rendering
1502
-
1503
- ```ts
1504
- getMesh(): ShapeRuntimeMesh
1505
- ```
1506
-
1507
- #### `slice()` — Slice the runtime solid by a plane normal to local Z at the given offset.
1508
-
1509
- ```ts
1510
- slice(offset?: number): any
1511
- ```
1512
-
1513
- #### `project()` — Orthographically project the runtime solid onto the local XY plane.
1514
-
1515
- ```ts
1516
- project(): any
1517
- ```
1518
-
1519
- **Compatibility Aliases**
1520
-
1521
- - `withPorts()` -> `withConnectors()`
1522
- - `portNames()` -> `connectorNames()`
1523
-
1524
- ### `Transform`
1525
-
1526
- #### `identity()` — Return the identity transform.
1527
-
1528
- ```ts
1529
- static identity(): Transform
1530
- ```
1531
-
1532
- #### `from()` — Wrap an existing `Transform` or raw 4x4 matrix as a `Transform`.
1533
-
1534
- ```ts
1535
- static from(input: TransformInput): Transform
1536
- ```
1537
-
1538
- #### `translation()` — Create a translation transform.
1539
-
1540
- ```ts
1541
- static translation(x: number, y: number, z: number): Transform
1542
- ```
1543
-
1544
- #### `scale()` — Create a uniform or per-axis scale transform.
1545
-
1546
- ```ts
1547
- static scale(v: number | Vec3): Transform
1548
- ```
1549
-
1550
- #### `rotationAxis()` — Create a rotation around an arbitrary axis, optionally about a pivot.
1551
-
1552
- ```ts
1553
- static rotationAxis(axis: Vec3, angleDeg: number, pivot?: Vec3): Transform
1554
- ```
1555
-
1556
- #### `rotateAroundTo()` — Solve the rotation needed to move one point onto a target line or plane.
1557
-
1558
- ```ts
1559
- static rotateAroundTo(axis: Vec3, pivot: Vec3, movingPoint: Vec3, targetPoint: Vec3, options?: RotateAroundToOptions): Transform
1560
- ```
1561
-
1562
- #### `mul()` — Compose transforms in chain order: `a.mul(b)` applies `a`, then `b`.
1563
-
1564
- ```ts
1565
- mul(other: TransformInput): Transform
1566
- ```
1567
-
1568
- #### `translate()` — Translate after the current transform.
1569
-
1570
- ```ts
1571
- translate(x: number, y: number, z: number): Transform
1572
- ```
1573
-
1574
- #### `rotateAxis()` — Rotate after the current transform.
1575
-
1576
- ```ts
1577
- rotateAxis(axis: Vec3, angleDeg: number, pivot?: Vec3): Transform
1578
- ```
1579
-
1580
- #### `inverse()` — Return the inverse transform.
1581
-
1582
- ```ts
1583
- inverse(): Transform
1584
- ```
1585
-
1586
- #### [`point()`](/docs/sketch#point) — Transform a point using homogeneous coordinates.
1587
-
1588
- ```ts
1589
- point(p: Vec3): Vec3
1590
- ```
1591
-
1592
- #### `vector()` — Transform a direction vector without translation.
1593
-
1594
- ```ts
1595
- vector(v: Vec3): Vec3
1596
- ```
1597
-
1598
- #### `toArray()` — Return the transform as a raw 4x4 matrix array.
1599
-
1600
- ```ts
1601
- toArray(): Mat4
1602
- ```
1603
-
1604
- ### `ShapeGroup`
1605
-
1606
- **Properties:**
1607
-
1608
- | Property | Type | Description |
1609
- |----------|------|-------------|
1610
- | `children` | `GroupChild[]` | — |
1611
- | `childNames` | `Array<string | undefined>` | — |
1612
-
1613
- **Children**
1614
-
1615
- #### `child()` — Return the named child by name. Throws if not found. Useful when importing a multipart group and working on components individually.
1616
-
1617
- ```ts
1618
- child(name: string): GroupChild
1619
- ```
1620
-
1621
- #### `childName()` — Return the optional name of the child at `index`.
1622
-
1623
- ```ts
1624
- childName(index: number): string | undefined
1625
- ```
1626
-
1627
- **Transforms**
1628
-
1629
- #### `translate()` — Move the entire group by (x, y, z). All children move together as a unit.
1630
-
1631
- ```ts
1632
- translate(x: number, y: number, z: number): ShapeGroup
1633
- ```
1634
-
1635
- #### `moveTo()` — Move the group so its bounding-box min corner lands at the given coordinate.
1636
-
1637
- ```ts
1638
- moveTo(x: number, y: number, z: number): ShapeGroup
1639
- ```
1640
-
1641
- #### `moveToLocal()` — Move the group relative to another part's bounding-box min corner.
1642
-
1643
- ```ts
1644
- moveToLocal(target: Shape | ShapeGroup, x: number, y: number, z: number): ShapeGroup
1645
- ```
1646
-
1647
- #### `rotate()` — Rotate the group around an arbitrary axis through the origin.
1648
-
1649
- ```ts
1650
- rotate(axis: [ number, number, number ], angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
1651
- ```
1652
-
1653
- #### `rotateX()` — Rotate the group around the X axis.
1654
-
1655
- ```ts
1656
- rotateX(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
1657
- ```
1658
-
1659
- #### `rotateY()` — Rotate the group around the Y axis.
1660
-
1661
- ```ts
1662
- rotateY(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
1663
- ```
1664
-
1665
- #### `rotateZ()` — Rotate the group around the Z axis.
1666
-
1667
- ```ts
1668
- rotateZ(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
1669
- ```
1670
-
1671
- #### `rotateAroundAxis()` — Rotate around an arbitrary axis, optionally through a pivot point.
1672
-
1673
- ```ts
1674
- rotateAroundAxis(axis: [ number, number, number ], angleDeg: number, pivot?: [ number, number, number ]): ShapeGroup
1675
- ```
1676
-
1677
- #### `rotateAroundTo()` — Rotate around an axis until a moving point reaches the target line/plane defined by the axis and target point. ShapeGroup string points use built-in anchors only.
1678
-
1679
- ```ts
1680
- rotateAroundTo(axis: [ number, number, number ], pivot: [ number, number, number ], movingPoint: Anchor3D | [ number, number, number ], targetPoint: Anchor3D | [ number, number, number ], options?: RotateAroundToOptions): ShapeGroup
1681
- ```
1682
-
1683
- #### `pointAlong()` — Reorient the group so its local Z axis points along `direction`.
1684
-
1685
- ```ts
1686
- pointAlong(direction: [ number, number, number ]): ShapeGroup
1687
- ```
1688
-
1689
- #### `transform()` — Apply a 4x4 transform matrix or `Transform` to all 3D children.
1690
-
1691
- ```ts
1692
- transform(m: Mat4 | Transform): ShapeGroup
1693
- ```
1694
-
1695
- #### `scale()` — Scale uniformly or per-axis from the group's bounding-box center.
1696
-
1697
- ```ts
1698
- scale(v: number | [ number, number, number ]): ShapeGroup
1699
- ```
1700
-
1701
- #### `scaleAround()` — Scale uniformly or per-axis from an explicit pivot point.
1702
-
1703
- ```ts
1704
- scaleAround(pivot: [ number, number, number ], v: number | [ number, number, number ]): ShapeGroup
1705
- ```
1706
-
1707
- #### `mirror()` — Mirror across a plane through the group's bounding-box center.
1708
-
1709
- ```ts
1710
- mirror(normal: [ number, number, number ]): ShapeGroup
1711
- ```
1712
-
1713
- #### `mirrorThrough()` — Mirror across a plane through an explicit point.
1714
-
1715
- ```ts
1716
- mirrorThrough(point: [ number, number, number ], normal: [ number, number, number ]): ShapeGroup
1717
- ```
1718
-
1719
- **Placement**
1720
-
1721
- #### `placeReference()` — Translate the group so the given anchor or reference lands on the target coordinate.
1722
-
1723
- Accepts any built-in anchor name (`'bottom'`, `'center'`, `'top-front-left'`, etc.) or a custom placement reference attached via `withReferences()`.
1724
-
1725
- ```javascript
1726
- // Ground a group — put its bottom at Z = 0
1727
- assembly.placeReference('bottom', [0, 0, 0])
1728
-
1729
- // Use a custom reference from a multi-file part
1730
- const placed = require('./bracket-assembly.forge.js').group
1731
- .placeReference('mountCenter', [0, 0, 50]);
1732
- ```
1733
-
1734
- ```ts
1735
- placeReference(ref: PlacementAnchorLike, target: [ number, number, number ], offset?: [ number, number, number ]): ShapeGroup
1736
- ```
1737
-
1738
- #### `attachTo()` — Attach this group to a face or anchor on another part.
1739
-
1740
- `targetAnchor` can be a built-in anchor name or a custom reference name on the target. `selfAnchor` selects the anchor on this group to align.
1741
-
1742
- ```ts
1743
- attachTo(target: Shape | ShapeGroup, targetAnchor: Anchor3D | string, selfAnchor?: Anchor3D, offset?: [ number, number, number ]): ShapeGroup
1744
- ```
1745
-
1746
- #### `onFace()` — Place this group on a face of a parent shape. See Shape.onFace() for full documentation.
1747
-
1748
- ```ts
1749
- onFace(parent: Shape | ShapeGroup, face: "front" | "back" | "left" | "right" | "top" | "bottom", opts?: { u?: number; v?: number; protrude?: number; }): ShapeGroup
1750
- ```
1751
-
1752
- **Connectors**
1753
-
1754
- #### `withConnectors()` — Attach named connectors — attachment points that survive transforms. Connectors can be bare (position + orientation) or typed (with connectorType/gender for compatibility matching).
1755
-
1756
- ```ts
1757
- withConnectors(connectors: Record<string, ConnectorInput>): ShapeGroup
1758
- ```
1759
-
1760
- #### `connectorNames()` — List all connector names, including "ChildName.connectorName" from named children.
1761
-
1762
- ```ts
1763
- connectorNames(): string[]
1764
- ```
1765
-
1766
- #### `connectorsByType()` — Get all connectors of a given type, including from named children.
1767
-
1768
- ```ts
1769
- connectorsByType(type: string): Array<{ name: string; port: ConnectorDef; }>
1770
- ```
1771
-
1772
- #### `connectorDistance()` — Distance between two connector origins on this group (supports dotted child paths).
1773
-
1774
- ```ts
1775
- connectorDistance(nameA: string, nameB: string): number
1776
- ```
1777
-
1778
- #### `connectorMeasurements()` — Get measurements metadata from a connector (supports dotted child paths).
1779
-
1780
- ```ts
1781
- connectorMeasurements(name: string): Record<string, number | string>
1782
- ```
1783
-
1784
- #### `matchTo()` — Position this group by matching connectors to a target. Connector names support dotted paths into named children: "ChildName.connectorName".
1785
-
1786
- Overloads:
1787
-
1788
- - Single pair: `matchTo(target, selfConn, targetConn, options?)`
1789
- - Dictionary (same target): `matchTo(target, { selfConn: targetConn, ... }, options?)`
1790
- - Multi-target: `matchTo([ [target1, selfConn1, targetConn1], ... ], options?)`
1791
-
1792
- ```ts
1793
- matchTo(targetOrPairs: Shape | ShapeGroup | Array<[ Shape | ShapeGroup, string, string ]>, selfConnOrDict?: string | Record<string, string>, targetConnOrOptions?: string | MatchToOptions, maybeOptions?: MatchToOptions): ShapeGroup
1794
- ```
1795
-
1796
- **References**
1797
-
1798
- #### `withReferences()` — Attach named placement references to this group. References survive normal transforms (translate/rotate/scale/mirror/transform).
1799
-
1800
- ```javascript
1801
- const bracket = group(
1802
- { name: 'Left', shape: leftShape },
1803
- { name: 'Right', shape: rightShape },
1804
- ).withReferences({
1805
- points: { mountCenter: [0, 0, 0] },
1806
- });
1807
- ```
1808
-
1809
- ```ts
1810
- withReferences(refs: PlacementReferenceInput): ShapeGroup
1811
- ```
1812
-
1813
- #### `referenceNames()` — List named placement references carried by this group.
1814
-
1815
- ```ts
1816
- referenceNames(kind?: PlacementReferenceKind): string[]
1817
- ```
1818
-
1819
- #### `referencePoint()` — Resolve a named placement reference or built-in Anchor3D to a 3D point. Named refs take priority over built-in anchors.
1820
-
1821
- ```ts
1822
- referencePoint(ref: PlacementAnchorLike): [ number, number, number ]
1823
- ```
1824
-
1825
- **Other**
1826
-
1827
- #### `clone()` — Return a deep-cloned ShapeGroup tree (refs copied).
1828
-
1829
- ```ts
1830
- clone(): ShapeGroup
1831
- ```
1832
-
1833
- #### `boundingBox()` — Return the combined 3D bounding box of all children.
1834
-
1835
- ```ts
1836
- boundingBox(): { min: [ number, number, number ]; max: [ number, number, number ]; }
1837
- ```
1838
-
1839
- #### `color()` — Return a copy of the group with the given display color applied to each child.
1840
-
1841
- ```ts
1842
- color(hex: string): ShapeGroup
1843
- ```
1844
-
1845
- **Compatibility Aliases**
1846
-
1847
- - `withPorts()` -> `withConnectors()`
1848
- - `portNames()` -> `connectorNames()`
1849
-
1850
- ### `SurfacePattern`
1851
-
1852
- **Properties:**
1853
-
1854
- | Property | Type | Description |
1855
- |----------|------|-------------|
1856
- | `body` | `string` | Function body: receives (u, v) in surface mm, returns height displacement. |
1857
- | `constants` | `Record<string, number>` | Named constants injected into the function. |
1858
-
1859
- ### `Pattern2D`
1860
-
1861
- #### `add()` — Add this pattern to one or more patterns or constant height offsets.
1862
-
1863
- ```ts
1864
- add(...patterns: Pattern2DInput[]): Pattern2D
1865
- ```
1866
-
1867
- #### `subtract()` — Subtract another pattern or constant height offset from this pattern.
1868
-
1869
- ```ts
1870
- subtract(pattern: Pattern2DInput): Pattern2D
1871
- ```
1872
-
1873
- #### `multiply()` — Multiply this pattern by one or more patterns or numeric scale factors.
1874
-
1875
- ```ts
1876
- multiply(...patterns: Pattern2DInput[]): Pattern2D
1877
- ```
1878
-
1879
- #### `min()` — Keep the lower height between this pattern and one or more other patterns.
1880
-
1881
- ```ts
1882
- min(...patterns: Pattern2DInput[]): Pattern2D
1883
- ```
1884
-
1885
- #### `max()` — Keep the higher height between this pattern and one or more other patterns.
1886
-
1887
- ```ts
1888
- max(...patterns: Pattern2DInput[]): Pattern2D
1889
- ```
1890
-
1891
- #### `clamp()` — Limit pattern height to the inclusive `[min, max]` range in millimeters.
1892
-
1893
- ```ts
1894
- clamp(min: number, max: number): Pattern2D
1895
- ```
1896
-
1897
- #### `abs()` — Convert negative heights to positive heights.
1898
-
1899
- ```ts
1900
- abs(): Pattern2D
1901
- ```
1902
-
1903
- #### `negate()` — Flip the pattern height sign.
1904
-
1905
- ```ts
1906
- negate(): Pattern2D
1907
- ```
1908
-
1909
- ### `Pattern2DBuilder`
1910
-
1911
- #### `constant()` — Create a constant-height pattern in millimeters.
1912
-
1913
- ```ts
1914
- constant(value?: number): Pattern2D
1915
- ```
1916
-
1917
- #### `sineWave()` — Create a sinusoidal wave pattern in UV space.
1918
-
1919
- ```ts
1920
- sineWave(options: Pattern2DSineWaveOptions): Pattern2D
1921
- ```
1922
-
1923
- #### `stripes()` — Create recessed stripe bands in UV space.
1924
-
1925
- ```ts
1926
- stripes(options: Pattern2DStripesOptions): Pattern2D
1927
- ```
1928
-
1929
- #### `overUnderWeave()` — Create an over-under woven relief pattern in UV space.
1930
-
1931
- ```ts
1932
- overUnderWeave(options: Pattern2DOverUnderWeaveOptions): Pattern2D
1933
- ```
1934
-
1935
- ### `ShapeRef`
1936
-
1937
- A first-class reference path over a shape's semantic faces and face relationships.
1938
-
1939
- Created with `shape.ref("lid/back")`, then refined through methods such as `.point()` or `.edges()`. The reference stores intent as a readable path and resolves lazily against the current shape metadata.
1940
-
1941
- **Properties:**
1942
-
1943
- | Property | Type | Description |
1944
- |----------|------|-------------|
1945
- | `path` | `string` | — |
1946
-
1947
- **Methods:**
1948
-
1949
- #### `resolve()` — Resolve this reference into its current faces, edges, or points.
1950
-
1951
- ```ts
1952
- resolve(): ShapeReferenceResolution
1953
- ```
1954
-
1955
- #### `kind()` — The resolved reference kind, such as `face`, `edge-set`, or [`point`](/docs/sketch#point).
1956
-
1957
- ```ts
1958
- get kind(): ShapeReferenceKind
1959
- ```
1960
-
1961
- #### `cardinality()` — Whether the reference currently resolves to zero, one, or many matches.
1962
-
1963
- ```ts
1964
- get cardinality(): ShapeReferenceCardinality
1965
- ```
1966
-
1967
- #### `status()` — Return the reference lifecycle status for the current shape state.
1968
-
1969
- ```ts
1970
- status(): ShapeReferenceStatus
1971
- ```
1972
-
1973
- #### `explain()` — Return a human-readable explanation of how this reference resolved.
1974
-
1975
- ```ts
1976
- explain(): string
1977
- ```
1978
-
1979
- #### `as()` — Name this derived reference so the same shape can resolve it by `shape.ref(name)`.
1980
-
1981
- ```ts
1982
- as(name: string): ShapeRef
1983
- ```
1984
-
1985
- #### `maybe()` — Return an optional reference that resolves to zero matches instead of throwing when missing.
1986
-
1987
- ```ts
1988
- maybe(): ShapeRef
1989
- ```
1990
-
1991
- #### `all()` — Mark that a multi-match reference is intentionally being used as a set.
1992
-
1993
- ```ts
1994
- all(): ShapeRef
1995
- ```
1996
-
1997
- #### `one()` — Require this reference to resolve to exactly one match.
1998
-
1999
- ```ts
2000
- one(): ShapeRef
2001
- ```
2002
-
2003
- #### `faces()` — Resolve this reference as one or more faces.
2004
-
2005
- ```ts
2006
- faces(): FaceRef[]
2007
- ```
2008
-
2009
- #### `face()` — Resolve this reference as exactly one face.
2010
-
2011
- ```ts
2012
- face(): FaceRef
2013
- ```
2014
-
2015
- #### `edges()` — Resolve this reference as one or more edges. Face references return boundary edges.
2016
-
2017
- ```ts
2018
- edges(): EdgeSegment[]
2019
- ```
2020
-
2021
- #### `edge()` — Resolve this reference as exactly one edge.
2022
-
2023
- ```ts
2024
- edge(): EdgeSegment
2025
- ```
2026
-
2027
- #### `points()` — Resolve this reference as one or more points. Faces use centers and edges use midpoints.
2028
-
2029
- ```ts
2030
- points(): Vec3[]
2031
- ```
2032
-
2033
- #### [`point()`](/docs/sketch#point) — Resolve this reference as exactly one point.
2034
-
2035
- ```ts
2036
- point(): Vec3
2037
- ```
2038
-
2039
- #### `toJSON()` — Return the structured JSON-friendly reference resolution.
2040
-
2041
- ```ts
2042
- toJSON(): ShapeReferenceResolution
2043
- ```
2044
-
2045
- #### `toString()` — Return a compact display form for this reference path.
2046
-
2047
- ```ts
2048
- toString(): string
2049
- ```
2050
-
2051
- ---
2052
-
2053
- ## Constants
2054
-
2055
- ### `ANCHOR3D_NAMES`
2056
-
2057
- ### `verify`
2058
-
2059
- - `that(label: string, check: () => boolean, message?: string): void` — Custom predicate check.
2060
- - `equal(label: string, actual: number, expected: number, tolerance?: number, message?: string): void` — Check that two numbers are approximately equal (within tolerance).
2061
- - `notEqual(label: string, actual: number, unexpected: number, tolerance?: number, message?: string): void` — Check that two numbers are NOT equal (differ by more than tolerance).
2062
- - `greaterThan(label: string, actual: number, min: number, message?: string): void` — Check that actual > min.
2063
- - `lessThan(label: string, actual: number, max: number, message?: string): void` — Check that actual < max.
2064
- - `inRange(label: string, actual: number, min: number, max: number, message?: string): void` — Check that min <= actual <= max.
2065
- - `centersCoincide(label: string, a: ShapeLike, b: ShapeLike, tolerance?: number): void` — Check that the bounding-box centers of two shapes coincide within tolerance (mm).
2066
- - `connectorDistance(label: string, target: ConnectorDistanceLike, connectorA: string, connectorB: string, expected?: number, tolerance?: number): void` — Check the distance between two named connectors on a shape or group. Use this when connectors + `matchTo()` define a static assembly interface. It proves the mate at runtime, unlike a plain source-level connector declaration. The common case is `expected = 0`, meaning the two connector origins should coincide after placement. **Example** ```ts verify.connectorDistance("leg is seated", bench, "Rail.leg_0", "Leg0.head", 0, 0.01); ```
2067
- - `physicalComponentCount(label: string, expected: number): void` — Declare the expected physical connectivity component count for the returned visible model. **Details** Use this for generated mechanical models that should have a clear component graph: one connected fixture, a purchased part plus a removable cartridge, a root assembly plus named intentional ghosts, and so on. `forgecad inspect mechanical-integrity` resolves the returned visible objects with the same physical-connectivity analysis used in the quality gate and fails if the actual component count differs. This catches the common generated-CAD failure where a script returns a visually plausible artifact but the handle, screw, washer, cover, or terminal block is actually a separate island. **Example** ```ts verify.physicalComponentCount("vise is one connected installed assembly", 1); ```
2068
- - `intentionalOverlap(label: string, a: ShapeLike, b: ShapeLike, reason: string): void` — Declare that two visible objects intentionally overlap because the overlap is real manufacturing intent. **Details** Use this only for overlaps that a mechanical reviewer would accept as actual matter sharing volume: welded/fused regions, overmolded inserts, potted electronics, cast-in hardware, or deliberately bonded laminations. This is not a shortcut for screws without holes, shafts without bores, covers without pockets, or parts placed with collision as a positioning hack. `forgecad inspect mechanical-integrity --collisions` only honors this declaration when both shapes are returned as visible objects and the exact collision report finds that same object pair. Unused or non-visible declarations fail the quality gate so annotations cannot hide unrelated collisions. **Example** ```ts verify.intentionalOverlap("rubber grip is overmolded on handle", rubberGrip, handleCore, "overmolded insert"); ```
2069
- - `notColliding(label: string, a: ShapeLike, b: ShapeLike, searchLength?: number): void` — Check that two shapes do not share positive volume. Face-to-face contact is allowed; use `verify.minClearance()` when an actual running gap is required.
2070
- - `minClearance(label: string, a: ShapeLike, b: ShapeLike, minGap: number, searchLength?: number): void` — Check that a minimum clearance gap exists between two shapes.
2071
- - `clearanceBetween(label: string, a: ShapeLike, b: ShapeLike, minGap: number, maxGap: number, searchLength?: number): void` — Check that the clearance gap between two shapes is inside an allowed range. **Details** Use this for seated and retained interfaces where a part must be close enough to be mechanically accountable, but must not collide beyond the allowed minimum. It catches both failure modes that make generated CAD look fake: parts floating away from their receiver, and parts intersecting their receiver because the pocket, bore, or running clearance was not modeled. For contact, use a narrow range such as `[-0.01, 0.05]` to tolerate tiny numerical noise. For a running fit, use the intended clearance band. Manifold-backed shapes use exact min-gap distance. Other backends use a mesh-derived min-gap check and say so in the verification message; keep `forgecad inspect mechanical-integrity --collisions` in the acceptance gate for positive-volume interference. **Example** ```ts verify.clearanceBetween("cover is seated on gasket", cover, gasket, -0.01, 0.05); verify.clearanceBetween("carriage runs inside rail", carriage, rail, 0.2, 0.5); ```
2072
- - `parallel(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void` — Check that two face normals are parallel (within toleranceDeg degrees).
2073
- - `perpendicular(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void` — Check that two face normals are perpendicular (within toleranceDeg degrees).
2074
- - `coplanar(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number, toleranceMm?: number): void` — Check that a face is coplanar with (same plane as) another face, meaning they are parallel AND their centers lie on the same plane.
2075
- - `faceAt(label: string, face: FaceRefLike, expectedPos: [ number, number, number ], toleranceMm?: number): void` — Check that a face center lies at a specific position (within toleranceMm).
2076
- - `sameDirection(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void` — Check that two face normals point in the same direction (not antiparallel). Stricter than parallel — both |angle| AND sign must match.
2077
- - `isEmpty(label: string, shape: ShapeLike, message?: string): void` — Check that a shape is empty.
2078
- - `notEmpty(label: string, shape: ShapeLike, message?: string): void` — Check that a shape is NOT empty.
2079
- - `volumeApprox(label: string, shape: ShapeLike, expected: number, tolerance?: number): void` — Check that a shape's volume is approximately equal to expected (mm³).
2080
- - `areaApprox(label: string, shape: ShapeLike, expected: number, tolerance?: number): void` — Check that a shape's surface area is approximately equal to expected (mm²).
2081
- - `boundingBoxSize(label: string, shape: ShapeLike, expectedSize: [ number, number, number ], tolerance?: number): void` — Check that a shape's bounding box has approximately the given size.
2082
- - `edgeContinuity(label: string, shape: ShapeLike, options?: EdgeContinuityThresholds): void` — Check that every sampled seam on a shape meets a requested continuity threshold.
2083
- - `noTinyEdges(label: string, shape: ShapeLike, threshold?: number): void` — Check that a shape has no tiny edges below the requested threshold.
2084
- - `noSliverFaces(label: string, shape: ShapeLike, threshold?: number): void` — Check that a shape has no sliver faces below the requested score threshold.
2085
- - `noSelfIntersection(label: string, shape: ShapeLike): void` — Best-effort exact-shape validity guard for self-intersections or broken B-Rep topology.
2086
-
2087
- ### `Constraint`
2088
-
2089
- - `makeParallel(builder: ConstrainedSketchBuilder, a: LineArg, b: LineArg): ConstrainedSketchBuilder` — Constrain two lines to be parallel.
2090
- - `enforceAngle(builder: ConstrainedSketchBuilder, a: LineArg, b: LineArg, angleDeg: number): ConstrainedSketchBuilder` — Constrain the signed angle from line `a` to line `b`.
2091
- - `horizontal(builder: ConstrainedSketchBuilder, line: LineArg): ConstrainedSketchBuilder` — Constrain a line to be horizontal.
2092
- - `vertical(builder: ConstrainedSketchBuilder, line: LineArg): ConstrainedSketchBuilder` — Constrain a line to be vertical.
2093
- - `equalLength(builder: ConstrainedSketchBuilder, a: LineArg, b: LineArg): ConstrainedSketchBuilder` — Constrain two lines to have equal length.
2094
- - `distance(builder: ConstrainedSketchBuilder, a: PointArg, b: PointArg, value: number): ConstrainedSketchBuilder` — Constrain the distance between two points.
2095
- - `fix(builder: ConstrainedSketchBuilder, pt: PointArg, x: number, y: number): ConstrainedSketchBuilder` — Fix a point at a specific coordinate.
2096
- - `coincident(builder: ConstrainedSketchBuilder, a: PointArg, b: PointArg): ConstrainedSketchBuilder` — Constrain two points to occupy the same location.
2097
- - `perpendicular(builder: ConstrainedSketchBuilder, a: LineArg, b: LineArg): ConstrainedSketchBuilder` — Constrain two lines to be perpendicular.
2098
- - `length(builder: ConstrainedSketchBuilder, line: LineArg, value: number): ConstrainedSketchBuilder` — Constrain the length of a line.
2099
-
2100
- ### `Points`
2101
-
2102
- - `distance(a: Vec3, b: Vec3): number` — Euclidean distance between two 3D points.
2103
- - `midpoint(a: Vec3, b: Vec3): Vec3` — Center point between two 3D points.
2104
- - `lerp(a: Vec3, b: Vec3, t: number): Vec3` — Linearly interpolate between two 3D points. t=0 returns a, t=1 returns b.
2105
- - `direction(a: Vec3, b: Vec3): Vec3` — Unit direction vector from a to b. Throws if a and b are the same point.
2106
- - `offset(point: Vec3, dir: Vec3, amount: number): Vec3` — Move a point along a direction vector by a given amount.
2107
- - `polar(length: number, angleDeg: number, from?: [ number, number ]): [ number, number ]` — Compute a 2D point at distance and angle (degrees) from an optional origin.
2108
-
2109
- ### `connector`
2110
-
2111
- Connector factory. Create attachment points: `connector({...})`, `connector.male(type, {...})`, etc.
2112
-
2113
- ### `Import`
2114
-
2115
- Namespaced file import helpers for formats that should not add new lowercase globals.
2116
-
2117
- - `dxfSketch(fileName: string, options?: DxfImportOptions): Sketch` — Parse a DXF file and return closed 2D profile geometry as a Sketch. The result can be extruded directly.