forgecad 0.9.16 → 0.10.1

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 (162) hide show
  1. package/dist/assets/{AdminPage-CXvls4-J.js → AdminPage-DcCnj0qo.js} +1 -1
  2. package/dist/assets/{BenchmarkPage-B27zk8xL.js → BenchmarkPage-BVEpJSVk.js} +1 -1
  3. package/dist/assets/{BlogPage-CMAVvgQL.js → BlogPage-DHaGP50_.js} +1 -1
  4. package/dist/assets/{DocsPage-knf4I4h7.js → DocsPage-CDoxHkz8.js} +40 -859
  5. package/dist/assets/EditorApp-BJ0Dloyh.js +16446 -0
  6. package/dist/assets/{EmbedViewer-D7ZGlFjx.js → EmbedViewer-CRKZbY0y.js} +2 -2
  7. package/dist/assets/{LandingPageProofDriven-CnevhTE8.js → LandingPageProofDriven-BxHkYRE7.js} +1 -1
  8. package/dist/assets/{LegalPage-BPTUmqeg.js → LegalPage-B-u6FrVv.js} +1 -1
  9. package/dist/assets/{PricingPage-B0D4goG_.js → PricingPage-CzpZ6-Ce.js} +1 -1
  10. package/dist/assets/{SettingsPage-CFF-UgjI.js → SettingsPage-CIZSSAd0.js} +1 -1
  11. package/dist/assets/{app-CE3sYcV7.css → app-CjsbDlb7.css} +143 -0
  12. package/dist/assets/{app-T0pDcSX4.js → app-DaTMg3nH.js} +1310 -290
  13. package/dist/assets/cli/{render-C5pcIISc.js → render-DPf4AYJK.js} +55 -60
  14. package/dist/assets/{constructionHistoryWorker-Ba2Hm58b.js → constructionHistoryWorker-AwMMWSxg.js} +1103 -349
  15. package/dist/assets/{evalWorker-vkx310U2.js → evalWorker-CjZZWRWW.js} +5209 -2643
  16. package/dist/assets/{inspectWorker-BuTJDVX6.js → inspectWorker-CZsCFtQT.js} +1163 -409
  17. package/dist/assets/{jointPose-B_Cgedn9.js → jointPose-DzQOViQH.js} +1 -1
  18. package/dist/assets/{manifold-BWgsjmAM.js → manifold-BYlzU521.js} +1 -1
  19. package/dist/assets/{manifold-D6IFSkhH.js → manifold-DgXo0T5P.js} +2 -2
  20. package/dist/assets/{manifold-rZexZI0G.js → manifold-K1SkarlQ.js} +1 -1
  21. package/dist/assets/{reportWorker-0AGij1Ru.js → reportWorker-B9nWwSrB.js} +8501 -3393
  22. package/dist/assets/{scalar-sampling-budget-J5cuzxT1.js → scalar-sampling-budget-prBw_s8t.js} +6067 -3479
  23. package/dist/assets/{scanProxyWorker-Vl4Wxa1y.js → scanProxyWorker-2GtDLk-R.js} +1 -1
  24. package/dist/assets/{javascript-1kQXfVaz.js → typescript-DBQ6RN5l.js} +874 -22
  25. package/dist/cli/render.html +1 -1
  26. package/dist/docs/index.html +3 -3
  27. package/dist/docs-raw/AI/usage.md +1 -1
  28. package/dist/docs-raw/CLI.md +77 -240
  29. package/dist/docs-raw/README.md +6 -0
  30. package/dist/docs-raw/component-model.md +17 -150
  31. package/dist/docs-raw/generated/assembly.md +188 -582
  32. package/dist/docs-raw/generated/concepts.md +259 -3501
  33. package/dist/docs-raw/generated/core.md +283 -1250
  34. package/dist/docs-raw/generated/curves.md +387 -1608
  35. package/dist/docs-raw/generated/legacy.md +162 -0
  36. package/dist/docs-raw/generated/lib.md +227 -85
  37. package/dist/docs-raw/generated/output.md +35 -99
  38. package/dist/docs-raw/generated/runtime-names.md +23 -23
  39. package/dist/docs-raw/generated/sdf.md +68 -284
  40. package/dist/docs-raw/generated/sheet-metal.md +68 -335
  41. package/dist/docs-raw/generated/sketch.md +240 -1161
  42. package/dist/docs-raw/generated/viewport.md +75 -316
  43. package/dist/docs-raw/generated/wood.md +21 -49
  44. package/dist/docs-raw/guides/coordinate-system.md +4 -42
  45. package/dist/docs-raw/guides/inspection-bundles.md +44 -442
  46. package/dist/docs-raw/guides/joint-design.md +18 -79
  47. package/dist/docs-raw/guides/positioning.md +21 -143
  48. package/dist/docs-raw/guides/scene-presentation.md +89 -0
  49. package/dist/docs-raw/guides/simready-quickstart.md +171 -0
  50. package/dist/docs-raw/simulation-workflow.md +273 -0
  51. package/dist/docs-raw/skills/forgecad-3d-reconstruction.md +25 -111
  52. package/dist/docs-raw/skills/forgecad-blockout-model.md +20 -117
  53. package/dist/docs-raw/skills/forgecad-component-model.md +23 -107
  54. package/dist/docs-raw/skills/forgecad-high-level-spec.md +47 -155
  55. package/dist/docs-raw/skills/forgecad-image-replicator.md +26 -143
  56. package/dist/docs-raw/skills/forgecad-lld.md +19 -113
  57. package/dist/docs-raw/skills/forgecad-make-a-model.md +112 -532
  58. package/dist/docs-raw/skills/forgecad-model-grader.md +38 -108
  59. package/dist/docs-raw/skills/forgecad-prepare-prompt.md +24 -211
  60. package/dist/docs-raw/skills/forgecad-project.md +13 -131
  61. package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +42 -134
  62. package/dist/docs-raw/skills/forgecad-render-inspect.md +27 -174
  63. package/dist/docs-raw/skills/forgecad-visual-spec.md +32 -112
  64. package/dist/docs-raw/skills/forgecad.md +19 -18
  65. package/dist/docs-raw/skills/index.md +2 -0
  66. package/dist/docs-raw/welcome.md +2 -2
  67. package/dist/index.html +2 -2
  68. package/dist/llms.txt +1 -2
  69. package/dist/sitemap.xml +25 -13
  70. package/dist-cli/{check-compiler-SYQ2PWOB.js → check-compiler-II7NLPAB.js} +1 -1
  71. package/dist-cli/{check-query-propagation-HIAGV62W.js → check-query-propagation-7462TR3R.js} +1 -1
  72. package/dist-cli/{chunk-SPZE3DUY.js → chunk-UWTJCGXF.js} +5848 -2915
  73. package/dist-cli/forgecad.js +3496 -703
  74. package/dist-skill/CONTEXT.md +1797 -7963
  75. package/dist-skill/SKILL.md +15 -15
  76. package/dist-skill/docs/API/core/concepts.md +27 -157
  77. package/dist-skill/docs/CLI.md +77 -240
  78. package/dist-skill/docs/generated/assembly.md +182 -532
  79. package/dist-skill/docs/generated/core.md +283 -1250
  80. package/dist-skill/docs/generated/curves.md +387 -1609
  81. package/dist-skill/docs/generated/lib.md +227 -85
  82. package/dist-skill/docs/generated/output.md +35 -99
  83. package/dist-skill/docs/generated/runtime-names.md +16 -21
  84. package/dist-skill/docs/generated/sdf.md +68 -284
  85. package/dist-skill/docs/generated/sheet-metal.md +68 -335
  86. package/dist-skill/docs/generated/sketch.md +240 -1160
  87. package/dist-skill/docs/generated/viewport.md +75 -223
  88. package/dist-skill/docs/generated/wood.md +21 -49
  89. package/dist-skill/docs/guides/coordinate-system.md +4 -42
  90. package/dist-skill/docs/guides/inspection-bundles.md +44 -442
  91. package/dist-skill/docs/guides/joint-design.md +18 -79
  92. package/dist-skill/docs/guides/positioning.md +21 -143
  93. package/dist-skill/docs/guides/scene-presentation.md +89 -0
  94. package/dist-skill/docs/guides/surface-members.md +26 -0
  95. package/dist-skill/library/forgecad-3d-reconstruction/SKILL.md +23 -111
  96. package/dist-skill/library/forgecad-blockout-model/SKILL.md +18 -117
  97. package/dist-skill/library/forgecad-component-model/SKILL.md +21 -107
  98. package/dist-skill/library/forgecad-high-level-spec/SKILL.md +45 -155
  99. package/dist-skill/library/forgecad-image-replicator/SKILL.md +24 -143
  100. package/dist-skill/library/forgecad-lld/SKILL.md +17 -113
  101. package/dist-skill/library/forgecad-make-a-model/SKILL.md +110 -532
  102. package/dist-skill/library/forgecad-model-grader/SKILL.md +36 -108
  103. package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +35 -224
  104. package/dist-skill/library/forgecad-prepare-prompt/references/default-profiles.md +43 -271
  105. package/dist-skill/library/forgecad-prepare-prompt/references/master-prompt.md +30 -99
  106. package/dist-skill/library/forgecad-project/SKILL.md +13 -133
  107. package/dist-skill/library/forgecad-reconstruction-benchmark/SKILL.md +29 -123
  108. package/dist-skill/library/forgecad-render-inspect/SKILL.md +25 -174
  109. package/dist-skill/library/forgecad-visual-spec/SKILL.md +30 -111
  110. package/dist-skill/website/skills/forgecad-3d-reconstruction.md +58 -0
  111. package/dist-skill/website/skills/forgecad-blockout-model.md +49 -0
  112. package/dist-skill/website/skills/forgecad-component-model.md +53 -0
  113. package/dist-skill/website/skills/forgecad-high-level-spec.md +101 -0
  114. package/dist-skill/website/skills/forgecad-image-replicator.md +63 -0
  115. package/dist-skill/website/skills/forgecad-lld.md +41 -0
  116. package/dist-skill/website/skills/forgecad-make-a-model.md +186 -0
  117. package/dist-skill/website/skills/forgecad-model-grader.md +82 -0
  118. package/dist-skill/website/skills/forgecad-prepare-prompt.md +63 -0
  119. package/dist-skill/website/skills/forgecad-project.md +26 -0
  120. package/dist-skill/website/skills/forgecad-reconstruction-benchmark.md +60 -0
  121. package/dist-skill/website/skills/forgecad-render-inspect.md +80 -0
  122. package/dist-skill/website/skills/forgecad-visual-spec.md +71 -0
  123. package/dist-skill/website/skills/forgecad.md +122 -0
  124. package/dist-skill/website/skills/index.md +26 -0
  125. package/examples/api/comparison-imported-sphere-candidate.forge.js +1 -1
  126. package/examples/api/conformal-product-ribbon.forge.js +1 -1
  127. package/examples/api/exact-sheet-shell-assembly.forge.js +1 -1
  128. package/examples/api/extrude-options.forge.js +4 -2
  129. package/examples/api/field-loft-drive-tip.forge.js +40 -0
  130. package/examples/api/guided-loft-olive-oil-bottle.forge.js +1 -1
  131. package/examples/api/highlight-debug.forge.js +10 -10
  132. package/examples/api/mesh-import-slats.forge.js +1 -1
  133. package/examples/api/real-product-curves.forge.js +1 -1
  134. package/examples/api/sculpt-box-circle-booleans.forge.js +1 -1
  135. package/examples/api/sdf-shapes.forge.js +2 -5
  136. package/examples/api/sketch-rounding-strategies.forge.js +6 -6
  137. package/examples/api/surface-member-bottle-cage.forge.js +3 -3
  138. package/examples/api/surface-member-conformal-product-ribbon.forge.js +3 -3
  139. package/examples/api/surface-member-razor-inlay.forge.js +1 -1
  140. package/examples/api/variable-sweep-test.forge.js +3 -3
  141. package/examples/mechanical/airplane-propeller.forge.js +74 -39
  142. package/examples/nurbs-surface.forge.js +1 -1
  143. package/examples/products/iphone.forge.js +1 -1
  144. package/examples/robotics/README.md +46 -0
  145. package/examples/robotics/scout-cam-rover-simready/README.md +119 -0
  146. package/examples/robotics/scout-cam-rover-simready/lib/dims.js +140 -0
  147. package/examples/robotics/scout-cam-rover-simready/main.forge.js +343 -0
  148. package/examples/robotics/scout-cam-rover-simready/parts/body.forge.js +304 -0
  149. package/examples/robotics/scout-cam-rover-simready/parts/chassis.forge.js +320 -0
  150. package/examples/robotics/scout-cam-rover-simready/parts/hardware.forge.js +21 -0
  151. package/examples/robotics/scout-cam-rover-simready/parts/turret.forge.js +70 -0
  152. package/examples/robotics/scout-cam-rover-simready/parts/wheel.forge.js +116 -0
  153. package/examples/robotics/simready-asset-crate.forge.js +79 -0
  154. package/examples/robotics/simready-diff-drive-rover.forge.js +141 -0
  155. package/examples/robotics/simready-parallel-gripper.forge.js +102 -0
  156. package/package.json +1 -1
  157. package/dist/assets/EditorApp-BHMQlJ-D.js +0 -14686
  158. package/dist/docs-raw/guides/geometry-conventions.md +0 -52
  159. package/dist/docs-raw/guides/modeling-recipes.md +0 -78
  160. package/dist-skill/docs/guides/geometry-conventions.md +0 -52
  161. package/dist-skill/docs/guides/modeling-recipes.md +0 -78
  162. package/dist-skill/library/forgecad-visual-spec/references/prompt-template.md +0 -79
@@ -9,28 +9,24 @@ skill-order: 100
9
9
 
10
10
  ## Contents
11
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`
12
+ - [3D Primitives](#3d-primitives)
13
+ - [Boolean Operations](#boolean-operations)
14
+ - [Edge Features](#edge-features)
15
+ - [Patterns & Layout](#patterns-layout)
16
+ - [Imports & Composition](#imports-composition)
17
+ - [Parameters](#parameters)
18
+ - [Grouping & Local Coordinates](#grouping-local-coordinates)
19
+ - [Section & Projection](#section-projection)
20
+ - [Verification](#verification)
22
21
  - [Shape](#shape) — Appearance, Face Topology, Edge Topology, Transforms, Booleans & Cutting, Features, Placement, Connectors, References, Measurement
23
22
  - [Transform](#transform)
24
23
  - [ShapeGroup](#shapegroup) — Children, Transforms, Placement, Connectors, References
25
24
  - [SurfacePattern](#surfacepattern)
26
25
  - [Pattern2D](#pattern2d)
27
26
  - [Pattern2DBuilder](#pattern2dbuilder)
28
- - [HermiteCurve3D](#hermitecurve3d)
29
- - [QuinticHermiteCurve3D](#quintichermitecurve3d)
30
27
  - [ShapeRef](#shaperef)
31
28
  - [ANCHOR3D_NAMES](#anchor3d-names)
32
29
  - [verify](#verify)
33
- - [Constraint](#constraint)
34
30
  - [Points](#points)
35
31
  - [connector](#connector)
36
32
  - [Import](#import)
@@ -39,7 +35,9 @@ skill-order: 100
39
35
 
40
36
  ### 3D Primitives
41
37
 
42
- #### `box()` — Create a rectangular box. Centered on XY, base at Z=0.
38
+ #### `box(width: number, depth: number, height: number): Shape` — Create a rectangular box. Centered on XY, base at Z=0.
39
+
40
+ All ForgeCAD dimensions are millimeters; all angles are degrees (applies to every API, not just `box`).
43
41
 
44
42
  Extents:
45
43
 
@@ -47,13 +45,11 @@ Extents:
47
45
  - Y: `[-depth/2, depth/2]`
48
46
  - Z: `[0, height]`
49
47
 
50
- For named faces, build from a labeled sketch: `rect(width, depth).labelEdges('s', 'e', 'n', 'w').extrude(height, { labels: { start: 'bottom', end: 'top' } })`.
48
+ This origin convention (centered on XY, base at Z=0) applies to all volumetric primitives that have a base. There is no `center: true` option recenter with `.translate(0, 0, -height/2)` or `.placeReference('center', [0, 0, 0])`.
51
49
 
52
- ```ts
53
- box(width: number, depth: number, height: number): Shape
54
- ```
50
+ For named faces, build from a labeled sketch: `rect(width, depth).labelEdges('s', 'e', 'n', 'w').extrude(height, { labels: { start: 'bottom', end: 'top' } })`.
55
51
 
56
- #### `cylinder()` — Create a cylinder or cone with named faces and edges. Centered on XY, base at Z=0.
52
+ #### `cylinder(height: number, radius: number, radiusTop?: number, segments?: number): Shape` — Create a cylinder or cone with named faces and edges. Centered on XY, base at Z=0.
57
53
 
58
54
  Extents:
59
55
 
@@ -64,11 +60,7 @@ Extents:
64
60
 
65
61
  Named faces: `top`, `bottom`, `side` Named edges: `top-rim`, `bottom-rim`
66
62
 
67
- ```ts
68
- cylinder(height: number, radius: number, radiusTop?: number, segments?: number): Shape
69
- ```
70
-
71
- #### `sphere()` — Create a sphere centered at the origin.
63
+ #### `sphere(radius: number, segments?: number): Shape` — Create a sphere centered at the origin.
72
64
 
73
65
  Extents:
74
66
 
@@ -78,11 +70,7 @@ Extents:
78
70
 
79
71
  Use `segments` for lower-poly approximations.
80
72
 
81
- ```ts
82
- sphere(radius: number, segments?: number): Shape
83
- ```
84
-
85
- #### `torus()` — Create a torus (donut shape) lying in the XY plane. Centered on all axes.
73
+ #### `torus(majorRadius: number, minorRadius: number, segments?: number): Shape` — Create a torus (donut shape) lying in the XY plane. Centered on all axes.
86
74
 
87
75
  Extents:
88
76
 
@@ -92,41 +80,25 @@ Extents:
92
80
 
93
81
  The origin is the center of the ring.
94
82
 
95
- ```ts
96
- torus(majorRadius: number, minorRadius: number, segments?: number): Shape
97
- ```
98
-
99
83
  ### Boolean Operations
100
84
 
101
- #### `union()` — Combine shapes into a single solid (additive boolean).
85
+ #### `union(...inputs: ShapeOperandInput[]): Shape` — Combine shapes into a single solid (additive boolean).
102
86
 
103
87
  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.
104
88
 
105
- ```ts
106
- union(...inputs: ShapeOperandInput[]): Shape
107
- ```
108
-
109
- #### `difference()` — Subtract shapes from a base shape (subtractive boolean).
89
+ #### `difference(...inputs: ShapeOperandInput[]): Shape` — Subtract shapes from a base shape (subtractive boolean).
110
90
 
111
91
  The first shape is the base; all subsequent shapes are subtracted from it. Accepts individual shapes, or an array of shapes.
112
92
 
113
- ```ts
114
- difference(...inputs: ShapeOperandInput[]): Shape
115
- ```
116
-
117
- #### `intersection()` — Keep only the overlapping volume of the input shapes (intersection boolean).
93
+ #### `intersection(...inputs: ShapeOperandInput[]): Shape` — Keep only the overlapping volume of the input shapes (intersection boolean).
118
94
 
119
95
  Requires at least two shapes. Accepts individual shapes, or an array.
120
96
 
121
- ```ts
122
- intersection(...inputs: ShapeOperandInput[]): Shape
123
- ```
124
-
125
97
  ### Edge Features
126
98
 
127
- #### `fillet()` — Apply experimental fillets (rounded edges) to one or more edges of a shape.
99
+ #### `fillet(shape: Shape, radius: number, edges?: EdgeSelector, segments?: number): Shape` — Apply experimental fillets (rounded edges) to one or more edges of a shape.
128
100
 
129
- **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.
101
+ **Experimental**: edge finishes (fillet and chamfer) are 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 profile-level rounding where the design allows (`sketch.filletCorners(radius)` before extruding — exact and fast); otherwise use targeted edge selectors and inspect the result before treating it as production-ready geometry.
130
102
 
131
103
  Edge selections compile into backend operations; unsupported selections fail as explicit kernel gaps instead of using TypeScript geometry fallbacks.
132
104
 
@@ -135,9 +107,12 @@ The `edges` parameter is flexible:
135
107
  - Omit to fillet **all** sharp edges
136
108
  - Pass an `EdgeQuery` for an inline filter (most common)
137
109
  - Pass an `EdgeSegment` or `EdgeSegment[]` from `selectEdges()` for pre-selected edges
110
+ - Pass a tracked `EdgeRef` from `shape.edge('vert-br')` (vertical edges of `box()` / [`Rectangle2D`](/docs/sketch#rectangle2d) extrusions) — this takes the **exact** compiler-owned path, not the mesh-approximate one
138
111
 
139
112
  Throws if no edges match the selection, or if `radius` is not a positive finite number.
140
113
 
114
+ Selectorless (all-edges) calls draw from a per-run broad edge-feature budget. Exceeding it throws — except in live preview, which skips the finish with a warning for responsiveness. Explicit edge selectors are never budgeted; `FORGECAD_BROAD_EDGE_FEATURE_BUDGET` / `FORGECAD_ALLOW_BROAD_EDGE_FEATURES=1` raise or lift the budget.
115
+
141
116
  ```ts
142
117
  // Fillet all edges
143
118
  fillet(myShape, 2)
@@ -148,19 +123,21 @@ fillet(myShape, 1.5, { atZ: 20, convex: true })
148
123
  // Fillet vertical edges selected beforehand
149
124
  const edges = selectEdges(myShape, { parallel: [0, 0, 1] })
150
125
  fillet(myShape, 3, edges)
151
- ```
152
126
 
153
- ```ts
154
- fillet(shape: Shape, radius: number, edges?: EdgeSelector, segments?: number): Shape
127
+ // Exact compiler-owned fillet on a tracked box edge
128
+ const base = box(50, 50, 20)
129
+ fillet(base, 5, base.edge('vert-br'))
155
130
  ```
156
131
 
157
- #### `chamfer()` — Apply experimental chamfers (beveled edges) to one or more edges of a shape.
132
+ #### `chamfer(shape: Shape, size: number, edges?: EdgeSelector): Shape` — Apply experimental chamfers (beveled edges) to one or more edges of a shape.
158
133
 
159
- **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.
134
+ **Experimental**: same backend caveats as `fillet` Manifold may be incorrect for some edge-finish cases, OCCT can be very slow on broad selections; prefer profile-level rounding or targeted selectors and inspect the result.
160
135
 
161
136
  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.
162
137
 
163
- The `edges` parameter accepts the same options as `fillet()`: inline `EdgeQuery`, pre-selected `EdgeSegment`/`EdgeSegment[]`, or `undefined` (all sharp edges).
138
+ Selectorless (all-edges) calls draw from a per-run broad edge-feature budget. Exceeding it throws — except in live preview, which skips the finish with a warning for responsiveness. Explicit edge selectors are never budgeted; `FORGECAD_BROAD_EDGE_FEATURE_BUDGET` / `FORGECAD_ALLOW_BROAD_EDGE_FEATURES=1` raise or lift the budget.
139
+
140
+ The `edges` parameter accepts the same options as `fillet()`: inline `EdgeQuery`, pre-selected `EdgeSegment`/`EdgeSegment[]`, a tracked `EdgeRef` from `shape.edge('vert-br')` (exact compiler-owned path), or `undefined` (all sharp edges).
164
141
 
165
142
  ```ts
166
143
  // Chamfer all edges
@@ -168,13 +145,13 @@ chamfer(myShape, 1)
168
145
 
169
146
  // Chamfer only vertical edges
170
147
  chamfer(myShape, 2, { parallel: [0, 0, 1] })
171
- ```
172
148
 
173
- ```ts
174
- chamfer(shape: Shape, size: number, edges?: EdgeSelector): Shape
149
+ // Exact compiler-owned chamfer on a tracked box edge
150
+ const base = box(50, 50, 20)
151
+ chamfer(base, 3, base.edge('vert-br'))
175
152
  ```
176
153
 
177
- #### `draft()` — Apply a draft angle (taper) to vertical faces for mold extraction.
154
+ #### `draft(shape: Shape, angleDeg: number, pullDirection?: Vec3, neutralPlaneOffset?: number): Shape` — Apply a draft angle (taper) to vertical faces for mold extraction.
178
155
 
179
156
  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°.
180
157
 
@@ -188,11 +165,7 @@ draft(myBox, 3)
188
165
  draft(myShape, 2, [0, 0, 1], 10)
189
166
  ```
190
167
 
191
- ```ts
192
- draft(shape: Shape, angleDeg: number, pullDirection?: [ number, number, number ], neutralPlaneOffset?: number): Shape
193
- ```
194
-
195
- #### `offsetSolid()` — Uniformly offset all surfaces of a solid inward or outward.
168
+ #### `offsetSolid(shape: Shape, thickness: number): Shape` — Uniformly offset all surfaces of a solid inward or outward.
196
169
 
197
170
  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.
198
171
 
@@ -206,13 +179,9 @@ offsetSolid(myBox, 1)
206
179
  offsetSolid(myShape, -0.5)
207
180
  ```
208
181
 
209
- ```ts
210
- offsetSolid(shape: Shape, thickness: number): Shape
211
- ```
212
-
213
182
  ### Patterns & Layout
214
183
 
215
- #### `circularLayout()` — Compute evenly-spaced positions around a circle.
184
+ #### `circularLayout(count: number, radius: number, options?: CircularLayoutOptions): LayoutPoint[]` — Compute evenly-spaced positions around a circle.
216
185
 
217
186
  Eliminates the most common trig pattern in CAD scripts:
218
187
 
@@ -229,10 +198,6 @@ for (const {x, y} of circularLayout(12, r)) {
229
198
  }
230
199
  ```
231
200
 
232
- ```ts
233
- circularLayout(count: number, radius: number, options?: CircularLayoutOptions): LayoutPoint[]
234
- ```
235
-
236
201
  **`CircularLayoutOptions`**
237
202
  - `startDeg?: number` — Angle of the first element in degrees (default: 0 = +X axis).
238
203
  - `centerX?: number` — Center X coordinate (default: 0).
@@ -240,7 +205,7 @@ circularLayout(count: number, radius: number, options?: CircularLayoutOptions):
240
205
 
241
206
  `LayoutPoint`: `{ x: number, y: number }`
242
207
 
243
- #### `polygonVertices()` — Compute the vertex positions of a regular polygon.
208
+ #### `polygonVertices(sides: number, radius: number, options?: PolygonVerticesOptions): LayoutPoint[]` — Compute the vertex positions of a regular polygon.
244
209
 
245
210
  Default orientation places the first vertex at the top (90 degrees), matching the convention used by [`ngon()`](/docs/sketch#ngon).
246
211
 
@@ -256,16 +221,12 @@ const v3 = [center.x + r, center.y];
256
221
  const [v1, v2, v3] = polygonVertices(3, r);
257
222
  ```
258
223
 
259
- ```ts
260
- polygonVertices(sides: number, radius: number, options?: PolygonVerticesOptions): LayoutPoint[]
261
- ```
262
-
263
224
  **`PolygonVerticesOptions`**
264
225
  - `startDeg?: number` — Angle of the first vertex in degrees (default: 90 = top).
265
226
  - `centerX?: number` — Center X coordinate (default: 0).
266
227
  - `centerY?: number` — Center Y coordinate (default: 0).
267
228
 
268
- #### `linearPattern()` — Repeat a shape in a linear pattern along a direction vector and union the copies.
229
+ #### `linearPattern(shape: Shape, count: number, dx: number, dy: number, dz?: number): Shape` — Repeat a shape in a linear pattern along a direction vector and union the copies.
269
230
 
270
231
  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.
271
232
 
@@ -274,11 +235,7 @@ Creates `count` copies of `shape`, each offset by `(dx*i, dy*i, dz*i)` from the
274
235
  linearPattern(cylinder(10, 3), 5, 20, 0)
275
236
  ```
276
237
 
277
- ```ts
278
- linearPattern(shape: Shape, count: number, dx: number, dy: number, dz?: number): Shape
279
- ```
280
-
281
- #### `circularPattern()` — Repeat a shape in a circular pattern around an axis and union the copies.
238
+ #### `circularPattern(shape: Shape, count: number, centerXOrOpts?: number | CircularPatternOptions, centerY?: number): Shape` — Repeat a shape in a circular pattern around an axis and union the copies.
282
239
 
283
240
  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.
284
241
 
@@ -295,32 +252,20 @@ circularPattern(cylinder(12, 4).translate(30, 0, -1), 8)
295
252
  circularPattern(myFeature, 4, { axis: [1, 0, 0], origin: [0, 0, 50] })
296
253
  ```
297
254
 
298
- ```ts
299
- circularPattern(shape: Shape, count: number, centerXOrOpts?: number | CircularPatternOptions, centerY?: number): Shape
300
- ```
301
-
302
255
  **`CircularPatternOptions`**
303
256
 
304
257
  | Option | Type | Description |
305
258
  |--------|------|-------------|
306
259
  | `centerX?` | `number` | Center X of the rotation (default: 0). Used when the rotation axis is Z. |
307
260
  | `centerY?` | `number` | Center Y of the rotation (default: 0). Used when the rotation axis is Z. |
308
- | `axis?` | `[ number, number, number ]` | Rotation axis direction (default: [0, 0, 1] = Z axis). |
309
- | `origin?` | `[ number, number, number ]` | Pivot point for the rotation (default: [0, 0, 0]). Overrides centerX/centerY when set. |
310
-
311
- #### `linearPattern2d()` — Repeat a 2D sketch in a linear pattern and union the copies.
261
+ | `axis?` | `Vec3` | Rotation axis direction (default: [0, 0, 1] = Z axis). |
262
+ | `origin?` | `Vec3` | Pivot point for the rotation (default: [0, 0, 0]). Overrides centerX/centerY when set. |
312
263
 
313
- ```ts
314
- linearPattern2d(sketch: Sketch, count: number, dx: number, dy?: number): Sketch
315
- ```
264
+ #### `linearPattern2d(sketch: Sketch, count: number, dx: number, dy?: number): Sketch` — Repeat a 2D sketch in a linear pattern and union the copies.
316
265
 
317
- #### `circularPattern2d()` — Repeat a 2D sketch in a circular pattern around a center point and union the copies.
318
-
319
- ```ts
320
- circularPattern2d(sketch: Sketch, count: number, centerXOrOpts?: number | { centerX?: number; centerY?: number; startDeg?: number; }, centerY?: number): Sketch
321
- ```
266
+ #### `circularPattern2d(sketch: Sketch, count: number, centerXOrOpts?: number | { centerX?: number; centerY?: number; startDeg?: number; }, centerY?: number): Sketch` — Repeat a 2D sketch in a circular pattern around a center point and union the copies.
322
267
 
323
- #### `mirrorCopy()` — Mirror a shape across a plane and union the mirror with the original.
268
+ #### `mirrorCopy(shape: Shape, normal: Vec3): Shape` — Mirror a shape across a plane and union the mirror with the original.
324
269
 
325
270
  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.
326
271
 
@@ -329,11 +274,7 @@ The mirror plane passes through the origin and is defined by its normal vector.
329
274
  mirrorCopy(box(50, 30, 10), [1, 0, 0])
330
275
  ```
331
276
 
332
- ```ts
333
- mirrorCopy(shape: Shape, normal: [ number, number, number ]): Shape
334
- ```
335
-
336
- #### `selectEdges()` — Select all edges from a shape that match the given query.
277
+ #### `selectEdges(shape: Shape, query?: EdgeQuery): EdgeSegment[]` — Select all edges from a shape that match the given query.
337
278
 
338
279
  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.
339
280
 
@@ -348,10 +289,6 @@ for (const edge of coalesceEdges(topEdges)) {
348
289
  }
349
290
  ```
350
291
 
351
- ```ts
352
- selectEdges(shape: Shape, query?: EdgeQuery): EdgeSegment[]
353
- ```
354
-
355
292
  **`EdgeQuery`**
356
293
 
357
294
  | Option | Type | Description |
@@ -383,9 +320,10 @@ selectEdges(shape: Shape, query?: EdgeQuery): EdgeSegment[]
383
320
  | `normalA` | `Vec3` | Normal of first adjacent face. |
384
321
  | `normalB` | `Vec3` | Normal of second adjacent face (same as normalA for boundary edges). |
385
322
  | `boundary` | `boolean` | true if this is a boundary (unmatched) edge — unusual for closed solids. |
386
- | `start`, `end`, `midpoint`, `length` | | — |
387
323
 
388
- #### `selectEdge()` Select the single best-matching edge from a shape.
324
+ Also: `start: Vec3`, `end: Vec3`, `midpoint: Vec3`, `length: number`.
325
+
326
+ #### `selectEdge(shape: Shape, query?: EdgeQuery): EdgeSegment` — Select the single best-matching edge from a shape.
389
327
 
390
328
  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.
391
329
 
@@ -395,11 +333,7 @@ const bottomEdge = selectEdge(part, { near: [25, 0, 0], atZ: 0 });
395
333
  result = chamfer(result, 1.5, bottomEdge);
396
334
  ```
397
335
 
398
- ```ts
399
- selectEdge(shape: Shape, query?: EdgeQuery): EdgeSegment
400
- ```
401
-
402
- #### `coalesceEdges()` — Merge collinear edge segments into longer logical edges.
336
+ #### `coalesceEdges(segments: EdgeSegment[], tolerance?: number): EdgeSegment[]` — Merge collinear edge segments into longer logical edges.
403
337
 
404
338
  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.
405
339
 
@@ -412,16 +346,14 @@ for (const edge of coalesceEdges(topEdges)) {
412
346
  }
413
347
  ```
414
348
 
415
- ```ts
416
- coalesceEdges(segments: EdgeSegment[], tolerance?: number): EdgeSegment[]
417
- ```
418
-
419
349
  ### Imports & Composition
420
350
 
421
- #### `require()` — Import a module with optional ForgeCAD parameter overrides. Returns the module's exports.
351
+ #### `require(path: string, paramOverrides?: Record<string, number | string>): any` — Import a module with optional ForgeCAD parameter overrides. Returns the module's exports.
422
352
 
423
353
  When importing a `.forge.js` file, most return values are passed through exactly as the script returns them. Assembly returns have one extra composition rule: an unsolved [`Assembly`](/docs/assembly#assembly) is wrapped as an [`ImportedAssembly`](/docs/assembly#importedassembly), preserving `solve(state)` and `mergeInto()` across file boundaries, while a returned [`SolvedAssembly`](/docs/assembly#solvedassembly) stays a [`SolvedAssembly`](/docs/assembly#solvedassembly). If the script returns a metadata object (e.g. `{ shape: myShape, bolts: {...} }`), the caller receives the full object — renderable values and metadata together.
424
354
 
355
+ **Script return contract:** a `.forge.js` script returns one of three shapes: a single renderable (Shape, ShapeGroup, Sketch, SdfShape, Assembly), an array of renderables or named descriptors (`{ name, shape|sketch|group }`), or a metadata object mixing renderable values with plain data. When a script runs directly, renderable entries of a metadata object are rendered under their key names and non-renderable entries are silently skipped — both halves of the metadata contract: one return value serves the viewport and `require()` callers.
356
+
425
357
  **Assembly return contract**
426
358
 
427
359
  | `.forge.js` return value | `require()` result |
@@ -480,57 +412,20 @@ const wheel = profiles.make.wheelProfile().extrude(8);
480
412
 
481
413
  Keep exported builders pure over top-level constants, top-level `param()` values, or explicit function arguments. Do not declare new `param()` values inside an exported builder if callers need `require('./profiles.forge.js', { Width: 80 })` overrides: import overrides are validated while the module loads, before any exported builder is called. Use plain `.js` modules only for pure constants, tables, math helpers, and formatting code that does not construct ForgeCAD geometry.
482
414
 
483
- ```ts
484
- require(path: string, paramOverrides?: Record<string, number | string>): any
485
- ```
486
-
487
- #### `importSvgSketch()` — Parse an SVG file and return it as a Sketch with options for region filtering, scaling, and simplification.
488
-
489
- ```ts
490
- importSvgSketch(fileName: string, options?: SvgImportOptions): Sketch
491
- ```
492
-
493
- **`SvgImportOptions`**
494
-
495
- | Option | Type | Description |
496
- |--------|------|-------------|
497
- | `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 |
498
- | `regionSelection?` | `"all" \| "largest"` | Keep all disconnected regions, or only the largest. |
499
- | `maxRegions?` | `number` | Keep at most this many regions (largest-first). |
500
- | `minRegionArea?` | `number` | Drop regions below this absolute area threshold. |
501
- | `minRegionAreaRatio?` | `number` | Drop regions below this ratio of largest-region area. |
502
- | `flattenTolerance?` | `number` | Curve flattening tolerance in SVG user units. Smaller = more segments, higher fidelity. |
503
- | `arcSegments?` | `number` | Minimum segment count for arc discretization. |
504
- | `scale?` | `number` | Global scale applied after SVG parsing. |
505
- | `maxWidth?` | `number` | Maximum imported sketch width. If exceeded, geometry is uniformly downscaled to fit. |
506
- | `maxHeight?` | `number` | Maximum imported sketch height. If exceeded, geometry is uniformly downscaled to fit. |
507
- | `centerOnOrigin?` | `boolean` | Recenter imported geometry so its 2D bounds center is at CAD origin. |
508
- | `simplify?` | `number` | Simplification tolerance for final sketch cleanup. |
509
- | `invertY?` | `boolean` | Flip SVG Y-down coordinates to CAD Y-up. Enabled by default. |
510
-
511
- #### `importMesh()` — Import an external mesh file (STL, OBJ, 3MF).
512
-
513
- By default, 3MF build items are flattened into one Shape for compatibility. Use `separateObjects: true` to import 3MF build items/resource objects as a named ShapeGroup whose children are targetable by `forgecad ls`. Use `object` to import one item by the stable ref/name reported by `forgecad run`.
415
+ **Entry detection (Node semantics):** `require.main` is the entry script's module object, so `require.main === module` is true only in the file being run directly. Part files use it to build standalone preview geometry only when opened directly — importers then skip that work entirely:
514
416
 
515
417
  ```js
516
- const all = importMesh("./assembly.3mf", { separateObjects: true });
517
- const pin = all.child("Pin #001");
518
- const plate = importMesh("./assembly.3mf", { object: "3mf:build:001:object:7" });
519
- ```
520
-
521
- ```ts
522
- importMesh(fileName: string, options?: { scale?: number; center?: boolean; object?: string; separateObjects?: boolean; }): Shape | ShapeGroup
523
- ```
524
-
525
- #### `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.
526
-
527
- ```ts
528
- importStep(fileName: string): Shape
418
+ // part.forge.js
419
+ function bracket() { ... }
420
+ if (require.main === module) {
421
+ return { preview: [{ name: 'Bracket', shape: bracket() }] }; // direct run: render it
422
+ }
423
+ return { make: { bracket } }; // imported: builders only
529
424
  ```
530
425
 
531
426
  ### Parameters
532
427
 
533
- #### `Param.number()` — Declare a numeric parameter that renders as a slider in the UI.
428
+ #### `Param.number(name: string, defaultValue: number, opts?: { min?: number; max?: number; step?: number; unit?: string; integer?: boolean; reverse?: boolean; }): number` — Declare a numeric parameter that renders as a slider in the UI.
534
429
 
535
430
  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.
536
431
 
@@ -560,11 +455,7 @@ const bracket = require("./bracket.forge.js", { Width: 80 });
560
455
 
561
456
  Also available as the shorthand alias `param()`.
562
457
 
563
- ```ts
564
- Param.number(name: string, defaultValue: number, opts?: { min?: number; max?: number; step?: number; unit?: string; integer?: boolean; reverse?: boolean; }): number
565
- ```
566
-
567
- #### `Param.string()` — Declare a string parameter that renders as a text input in the UI.
458
+ #### `Param.string(name: string, defaultValue: string, opts?: { maxLength?: number; }): string` — Declare a string parameter that renders as a text input in the UI.
568
459
 
569
460
  String parameters let users type free-form text — labels, names, inscriptions, file paths, etc. The `name` string is the override key.
570
461
 
@@ -581,11 +472,7 @@ const tag = require("./tag.forge.js", { Label: "Custom Text" });
581
472
 
582
473
  Only available as `Param.string()` — no standalone alias.
583
474
 
584
- ```ts
585
- Param.string(name: string, defaultValue: string, opts?: { maxLength?: number; }): string
586
- ```
587
-
588
- #### `Param.bool()` — Declare a boolean parameter that renders as a checkbox in the UI.
475
+ #### `Param.bool(name: string, defaultValue: boolean): boolean` — Declare a boolean parameter that renders as a checkbox in the UI.
589
476
 
590
477
  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.
591
478
 
@@ -601,13 +488,7 @@ Override via import:
601
488
  const pan = require("./pan.forge.js", { "Show Lid": 0 });
602
489
  ```
603
490
 
604
- Also available as the shorthand alias `boolParam()`.
605
-
606
- ```ts
607
- Param.bool(name: string, defaultValue: boolean): boolean
608
- ```
609
-
610
- #### `Param.choice()` — Declare a choice parameter that renders as a dropdown in the UI.
491
+ #### `Param.choice(name: string, defaultValue: string, choices: string[]): string` — Declare a choice parameter that renders as a dropdown in the UI.
611
492
 
612
493
  `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.
613
494
 
@@ -630,13 +511,7 @@ Override via CLI:
630
511
  forgecad run model.forge.js --param "Pan Style=wok"
631
512
  ```
632
513
 
633
- Also available as the shorthand alias `choiceParam()`.
634
-
635
- ```ts
636
- Param.choice(name: string, defaultValue: string, choices: string[]): string
637
- ```
638
-
639
- #### `Param.list()` — Declare a list parameter — an array of struct items with per-field UI controls.
514
+ #### `Param.list<T extends Record<string, number | boolean | string>>(name: string, defaultItems: T[], opts: { ... }): T[]` — Declare a list parameter — an array of struct items with per-field UI controls.
640
515
 
641
516
  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.
642
517
 
@@ -646,15 +521,11 @@ Field types:
646
521
  - Choice fields (`choices: [...]` in field defs) return as `string`
647
522
  - All other fields return as `number`
648
523
 
649
- ```ts
650
- Param.list<T extends Record<string, number | boolean | string>>(name: string, defaultItems: T[], opts: { ... }): T[]
651
- ```
652
-
653
524
  `ListParamFieldDef`: `{ min?: number, max?: number, step?: number, unit?: string, integer?: boolean, boolean?: boolean, choices?: string[] }`
654
525
 
655
526
  ### Grouping & Local Coordinates
656
527
 
657
- #### `group()` — Group multiple shapes/sketches for joint transforms without merging into a single mesh.
528
+ #### `group(...items: GroupInput[]): ShapeGroup` — Group multiple shapes/sketches for joint transforms without merging into a single mesh.
658
529
 
659
530
  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.).
660
531
 
@@ -673,87 +544,35 @@ const indoorUnit = group(
673
544
  ).translate(0, -18, 70);
674
545
  ```
675
546
 
676
- ```ts
677
- group(...items: GroupInput[]): ShapeGroup
678
- ```
679
-
680
547
  ### Section & Projection
681
548
 
682
- #### `intersectWithPlane()` — Cross-section: slice a 3D shape with a plane and return the intersection as a 2D Sketch.
683
-
684
- ```ts
685
- intersectWithPlane(shape: Shape, plane: PlaneSpec): Sketch
686
- ```
549
+ #### `intersectWithPlane(shape: Shape, plane: PlaneSpec): Sketch` — Cross-section: slice a 3D shape with a plane and return the intersection as a 2D Sketch.
687
550
 
688
- #### `faceProfile()` — Extract the boundary profile of a named face as a 2D sketch.
551
+ #### `faceProfile(shape: Shape, face: FaceSelector): Sketch` — Extract the boundary profile of a named face as a 2D sketch.
689
552
 
690
553
  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.
691
554
 
692
- ```ts
693
- faceProfile(shape: Shape, face: FaceSelector): Sketch
694
- ```
695
-
696
- #### `projectToPlane()` — Orthographically project a 3D shape onto a plane and return the silhouette as a 2D Sketch.
697
-
698
- ```ts
699
- projectToPlane(shape: Shape, plane: PlaneSpec): Sketch
700
- ```
701
-
702
- ### Transforms
703
-
704
- #### `composeChain()` — Compose transforms in chain order. Equivalent to Transform.identity().mul(a).mul(b).mul(c)...
705
-
706
- ```ts
707
- composeChain(...steps: TransformInput[]): Transform
708
- ```
555
+ #### `projectToPlane(shape: Shape, plane: PlaneSpec): Sketch` — Orthographically project a 3D shape onto a plane and return the silhouette as a 2D Sketch.
709
556
 
710
557
  ### Verification
711
558
 
712
- #### `verify.that()` — Custom predicate check.
713
-
714
- ```ts
715
- verify.that(label: string, check: () => boolean, message?: string): void
716
- ```
717
-
718
- #### `verify.equal()` — Check that two numbers are approximately equal (within tolerance).
719
-
720
- ```ts
721
- verify.equal(label: string, actual: number, expected: number, tolerance?: number, message?: string): void
722
- ```
723
-
724
- #### `verify.notEqual()` — Check that two numbers are NOT equal (differ by more than tolerance).
725
-
726
- ```ts
727
- verify.notEqual(label: string, actual: number, unexpected: number, tolerance?: number, message?: string): void
728
- ```
729
-
730
- #### `verify.greaterThan()` — Check that actual > min.
731
-
732
- ```ts
733
- verify.greaterThan(label: string, actual: number, min: number, message?: string): void
734
- ```
559
+ #### `verify.that(label: string, check: () => boolean, message?: string): void` — Custom predicate check.
735
560
 
736
- #### `verify.lessThan()` — Check that actual < max.
561
+ #### `verify.equal(label: string, actual: number, expected: number, tolerance?: number, message?: string): void` — Check that two numbers are approximately equal (within tolerance).
737
562
 
738
- ```ts
739
- verify.lessThan(label: string, actual: number, max: number, message?: string): void
740
- ```
563
+ #### `verify.notEqual(label: string, actual: number, unexpected: number, tolerance?: number, message?: string): void` — Check that two numbers are NOT equal (differ by more than tolerance).
741
564
 
742
- #### `verify.inRange()` — Check that min <= actual <= max.
565
+ #### `verify.greaterThan(label: string, actual: number, min: number, message?: string): void` — Check that actual > min.
743
566
 
744
- ```ts
745
- verify.inRange(label: string, actual: number, min: number, max: number, message?: string): void
746
- ```
567
+ #### `verify.lessThan(label: string, actual: number, max: number, message?: string): void` — Check that actual < max.
747
568
 
748
- #### `verify.centersCoincide()` — Check that the bounding-box centers of two shapes coincide within tolerance (mm).
569
+ #### `verify.inRange(label: string, actual: number, min: number, max: number, message?: string): void` — Check that min <= actual <= max.
749
570
 
750
- ```ts
751
- verify.centersCoincide(label: string, a: ShapeLike, b: ShapeLike, tolerance?: number): void
752
- ```
571
+ #### `verify.centersCoincide(label: string, a: ShapeLike, b: ShapeLike, tolerance?: number): void` — Check that the bounding-box centers of two shapes coincide within tolerance (mm).
753
572
 
754
573
  `ShapeLike`: `{ min: number[], max: number[] }`
755
574
 
756
- #### `verify.connectorDistance()` — Check the distance between two named connectors on a shape or group.
575
+ #### `verify.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.
757
576
 
758
577
  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.
759
578
 
@@ -761,11 +580,7 @@ Use this when connectors + `matchTo()` define a static assembly interface. It pr
761
580
  verify.connectorDistance("leg is seated", bench, "Rail.leg_0", "Leg0.head", 0, 0.01);
762
581
  ```
763
582
 
764
- ```ts
765
- verify.connectorDistance(label: string, target: ConnectorDistanceLike, connectorA: string, connectorB: string, expected?: number, tolerance?: number): void
766
- ```
767
-
768
- #### `verify.physicalComponentCount()` — Declare the expected physical connectivity component count for the returned visible model.
583
+ #### `verify.physicalComponentCount(label: string, expected: number): void` — Declare the expected physical connectivity component count for the returned visible model.
769
584
 
770
585
  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.
771
586
 
@@ -775,11 +590,7 @@ This catches the common generated-CAD failure where a script returns a visually
775
590
  verify.physicalComponentCount("vise is one connected installed assembly", 1);
776
591
  ```
777
592
 
778
- ```ts
779
- verify.physicalComponentCount(label: string, expected: number): void
780
- ```
781
-
782
- #### `verify.intentionalOverlap()` — Declare that two visible objects intentionally overlap because the overlap is real manufacturing intent.
593
+ #### `verify.intentionalOverlap(label: string, a: ShapeLike, b: ShapeLike, reason: string): void` — Declare that two visible objects intentionally overlap because the overlap is real manufacturing intent.
783
594
 
784
595
  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.
785
596
 
@@ -789,25 +600,13 @@ Use this only for overlaps that a mechanical reviewer would accept as actual mat
789
600
  verify.intentionalOverlap("rubber grip is overmolded on handle", rubberGrip, handleCore, "overmolded insert");
790
601
  ```
791
602
 
792
- ```ts
793
- verify.intentionalOverlap(label: string, a: ShapeLike, b: ShapeLike, reason: string): void
794
- ```
795
-
796
- #### `verify.notColliding()` — Check that two shapes do not share positive volume.
603
+ #### `verify.notColliding(label: string, a: ShapeLike, b: ShapeLike, searchLength?: number): void` — Check that two shapes do not share positive volume.
797
604
 
798
605
  Face-to-face contact is allowed; use `verify.minClearance()` when an actual running gap is required.
799
606
 
800
- ```ts
801
- verify.notColliding(label: string, a: ShapeLike, b: ShapeLike, searchLength?: number): void
802
- ```
803
-
804
- #### `verify.minClearance()` — Check that a minimum clearance gap exists between two shapes.
607
+ #### `verify.minClearance(label: string, a: ShapeLike, b: ShapeLike, minGap: number, searchLength?: number): void` — Check that a minimum clearance gap exists between two shapes.
805
608
 
806
- ```ts
807
- verify.minClearance(label: string, a: ShapeLike, b: ShapeLike, minGap: number, searchLength?: number): void
808
- ```
809
-
810
- #### `verify.clearanceBetween()` — Check that the clearance gap between two shapes is inside an allowed range.
609
+ #### `verify.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.
811
610
 
812
611
  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.
813
612
 
@@ -820,99 +619,39 @@ verify.clearanceBetween("cover is seated on gasket", cover, gasket, -0.01, 0.05)
820
619
  verify.clearanceBetween("carriage runs inside rail", carriage, rail, 0.2, 0.5);
821
620
  ```
822
621
 
823
- ```ts
824
- verify.clearanceBetween(label: string, a: ShapeLike, b: ShapeLike, minGap: number, maxGap: number, searchLength?: number): void
825
- ```
826
-
827
- #### `verify.parallel()` — Check that two face normals are parallel (within toleranceDeg degrees).
828
-
829
- ```ts
830
- verify.parallel(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void
831
- ```
832
-
833
- `FaceRefLike`: `{ normal: [ number, number, number ], center: [ number, number, number ] }`
834
-
835
- #### `verify.perpendicular()` — Check that two face normals are perpendicular (within toleranceDeg degrees).
836
-
837
- ```ts
838
- verify.perpendicular(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void
839
- ```
840
-
841
- #### `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.
842
-
843
- ```ts
844
- verify.coplanar(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number, toleranceMm?: number): void
845
- ```
846
-
847
- #### `verify.faceAt()` — Check that a face center lies at a specific position (within toleranceMm).
848
-
849
- ```ts
850
- verify.faceAt(label: string, face: FaceRefLike, expectedPos: [ number, number, number ], toleranceMm?: number): void
851
- ```
852
-
853
- #### `verify.sameDirection()` — Check that two face normals point in the same direction (not antiparallel). Stricter than parallel — both |angle| AND sign must match.
854
-
855
- ```ts
856
- verify.sameDirection(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void
857
- ```
858
-
859
- #### `verify.isEmpty()` — Check that a shape is empty.
622
+ #### `verify.parallel(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void` — Check that two face normals are parallel (within toleranceDeg degrees).
860
623
 
861
- ```ts
862
- verify.isEmpty(label: string, shape: ShapeLike, message?: string): void
863
- ```
624
+ `FaceRefLike`: `{ normal: Vec3, center: Vec3 }`
864
625
 
865
- #### `verify.notEmpty()` — Check that a shape is NOT empty.
626
+ #### `verify.perpendicular(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void` — Check that two face normals are perpendicular (within toleranceDeg degrees).
866
627
 
867
- ```ts
868
- verify.notEmpty(label: string, shape: ShapeLike, message?: string): void
869
- ```
628
+ #### `verify.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.
870
629
 
871
- #### `verify.volumeApprox()` — Check that a shape's volume is approximately equal to expected (mm³).
630
+ #### `verify.faceAt(label: string, face: FaceRefLike, expectedPos: Vec3, toleranceMm?: number): void` — Check that a face center lies at a specific position (within toleranceMm).
872
631
 
873
- ```ts
874
- verify.volumeApprox(label: string, shape: ShapeLike, expected: number, tolerance?: number): void
875
- ```
632
+ #### `verify.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.
876
633
 
877
- #### `verify.areaApprox()` — Check that a shape's surface area is approximately equal to expected (mm²).
634
+ #### `verify.isEmpty(label: string, shape: ShapeLike, message?: string): void` — Check that a shape is empty.
878
635
 
879
- ```ts
880
- verify.areaApprox(label: string, shape: ShapeLike, expected: number, tolerance?: number): void
881
- ```
636
+ #### `verify.notEmpty(label: string, shape: ShapeLike, message?: string): void` — Check that a shape is NOT empty.
882
637
 
883
- #### `verify.boundingBoxSize()` — Check that a shape's bounding box has approximately the given size.
638
+ #### `verify.volumeApprox(label: string, shape: ShapeLike, expected: number, tolerance?: number): void` — Check that a shape's volume is approximately equal to expected (mm³).
884
639
 
885
- ```ts
886
- verify.boundingBoxSize(label: string, shape: ShapeLike, expectedSize: [ number, number, number ], tolerance?: number): void
887
- ```
640
+ #### `verify.areaApprox(label: string, shape: ShapeLike, expected: number, tolerance?: number): void` — Check that a shape's surface area is approximately equal to expected (mm²).
888
641
 
889
- #### `verify.edgeContinuity()` — Check that every sampled seam on a shape meets a requested continuity threshold.
642
+ #### `verify.boundingBoxSize(label: string, shape: ShapeLike, expectedSize: Vec3, tolerance?: number): void` — Check that a shape's bounding box has approximately the given size.
890
643
 
891
- ```ts
892
- verify.edgeContinuity(label: string, shape: ShapeLike, options?: EdgeContinuityThresholds): void
893
- ```
644
+ #### `verify.edgeContinuity(label: string, shape: ShapeLike, options?: EdgeContinuityThresholds): void` — Check that every sampled seam on a shape meets a requested continuity threshold.
894
645
 
895
646
  **`EdgeContinuityThresholds`**: `continuity?: SurfaceContinuity`, `samples?: number`, `positionTolerance?: number`, `tangentToleranceDeg?: number`, `curvatureTolerance?: number`
896
647
 
897
- #### `verify.noTinyEdges()` — Check that a shape has no tiny edges below the requested threshold.
898
-
899
- ```ts
900
- verify.noTinyEdges(label: string, shape: ShapeLike, threshold?: number): void
901
- ```
902
-
903
- #### `verify.noSliverFaces()` — Check that a shape has no sliver faces below the requested score threshold.
904
-
905
- ```ts
906
- verify.noSliverFaces(label: string, shape: ShapeLike, threshold?: number): void
907
- ```
648
+ #### `verify.noTinyEdges(label: string, shape: ShapeLike, threshold?: number): void` — Check that a shape has no tiny edges below the requested threshold.
908
649
 
909
- #### `verify.noSelfIntersection()` — Best-effort exact-shape validity guard for self-intersections or broken B-Rep topology.
650
+ #### `verify.noSliverFaces(label: string, shape: ShapeLike, threshold?: number): void` — Check that a shape has no sliver faces below the requested score threshold.
910
651
 
911
- ```ts
912
- verify.noSelfIntersection(label: string, shape: ShapeLike): void
913
- ```
652
+ #### `verify.noSelfIntersection(label: string, shape: ShapeLike): void` — Best-effort exact-shape validity guard for self-intersections or broken B-Rep topology.
914
653
 
915
- #### `spec()` — Create a named, reusable bundle of verification checks.
654
+ #### `spec(name: string, checkFn: (...args: any[]) => void): Spec` — Create a named, reusable bundle of verification checks.
916
655
 
917
656
  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.
918
657
 
@@ -942,10 +681,6 @@ fitSpec.check(bracket, standoff);
942
681
 
943
682
  **Spec-first workflow:** Write specs before building geometry. Checks go from red to green as you build — effectively TDD for CAD.
944
683
 
945
- ```ts
946
- spec(name: string, checkFn: (...args: any[]) => void): Spec
947
- ```
948
-
949
684
  **`Spec`**
950
685
  - `name: string` — The display name of this spec
951
686
 
@@ -963,17 +698,13 @@ Supports transforms (translate, rotate, scale, mirror, transform, rotateAround,
963
698
 
964
699
  | Property | Type | Description |
965
700
  |----------|------|-------------|
966
- | `materialProps` | `ShapeMaterialProps | undefined` | — |
701
+ | `materialProps` | `ShapeMaterialProps \| undefined` | — |
967
702
 
968
703
  **Appearance**
969
704
 
970
- #### `color()` — Set the color of this shape (hex string, e.g. "#ff0000"). Returns a new Shape with the color applied.
971
-
972
- ```ts
973
- color(value: string | undefined): Shape
974
- ```
705
+ #### `color(value: string | undefined): Shape` — Set the color of this shape (hex string, e.g. "#ff0000"). Returns a new Shape with the color applied.
975
706
 
976
- #### `material()` — Set PBR material properties for this shape's visual appearance.
707
+ #### `material(props: ShapeMaterialProps): Shape` — Set PBR material properties for this shape's visual appearance.
977
708
 
978
709
  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.
979
710
 
@@ -988,10 +719,6 @@ cylinder(40, 20).material({ opacity: 0.4, clearcoat: 1.0, clearcoatRoughness: 0.
988
719
  box(100, 100, 10).color('#gold').material({ metalness: 0.95, roughness: 0.05 }).translate(0, 0, 50);
989
720
  ```
990
721
 
991
- ```ts
992
- material(props: ShapeMaterialProps): Shape
993
- ```
994
-
995
722
  **`ShapeMaterialProps`**
996
723
 
997
724
  | Option | Type | Description |
@@ -1013,12 +740,14 @@ material(props: ShapeMaterialProps): Shape
1013
740
 
1014
741
  **Face Topology**
1015
742
 
1016
- #### `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.
743
+ #### `face(selector: FaceSelector): FaceRef` — 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.
1017
744
 
1018
745
  `.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.
1019
746
 
1020
747
  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.
1021
748
 
749
+ Boolean survival: `union()` and `intersection()` carry labels from every operand; `difference()` carries only the base (first) operand's labels — cutter labels are dropped. A surviving label addresses whatever portion of its face survives the boolean; cutters may split or erase it, and a lineage shared by multiple union operands resolves as a face set rather than a single face.
750
+
1022
751
  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.
1023
752
 
1024
753
  ```ts
@@ -1045,69 +774,29 @@ const full = union(left, right);
1045
774
  full.face('l/upper'); // left wing upper surface
1046
775
  ```
1047
776
 
1048
- ```ts
1049
- face(selector: FaceSelector): FaceRef
1050
- ```
1051
-
1052
- #### `faces()` — Return faces matching a query, or label semantic faces when passed a mapping.
777
+ #### `faces(): FaceRef[]` — Return faces matching a query, or label semantic faces when passed a mapping.
1053
778
 
1054
779
  Mapping form returns a new shape: `shape.faces({ lid: 'top', walls: ['front', 'back', 'left', 'right'] })`.
1055
780
 
1056
- ```ts
1057
- faces(): FaceRef[]
1058
- ```
1059
-
1060
- #### `faceNames()` — List defined semantic face names currently available on this shape.
1061
-
1062
- ```ts
1063
- faceNames(): string[]
1064
- ```
1065
-
1066
- #### `prefixLabels()` — Prefix all user-authored face labels, including semantic labels from `faces(mapping)`. Returns a new shape with modified labels.
1067
-
1068
- ```ts
1069
- prefixLabels(prefix: string): Shape
1070
- ```
1071
-
1072
- #### `renameLabel()` — Rename a single face label. Returns a new shape.
781
+ #### `faceNames(): string[]` — List defined semantic face names currently available on this shape.
1073
782
 
1074
- ```ts
1075
- renameLabel(from: string, to: string): Shape
1076
- ```
1077
-
1078
- #### `dropLabels()` — Remove specific face labels. Returns a new shape.
1079
-
1080
- ```ts
1081
- dropLabels(...names: string[]): Shape
1082
- ```
783
+ #### `prefixLabels(prefix: string): Shape` — Prefix all user-authored face labels, including semantic labels from `faces(mapping)`. Returns a new shape with modified labels.
1083
784
 
1084
- #### `dropAllLabels()` — Remove all face labels. Returns a new shape.
785
+ #### `renameLabel(from: string, to: string): Shape` — Rename a single face label. Returns a new shape.
1085
786
 
1086
- ```ts
1087
- dropAllLabels(): Shape
1088
- ```
787
+ #### `dropLabels(...names: string[]): Shape` — Remove specific face labels. Returns a new shape.
1089
788
 
1090
- #### `faceHistory()` — Get the transformation history for a specific face.
789
+ #### `dropAllLabels(): Shape` — Remove all face labels. Returns a new shape.
1091
790
 
1092
- ```ts
1093
- faceHistory(name: string): FaceTransformationHistory
1094
- ```
791
+ #### `faceHistory(name: string): FaceTransformationHistory` — Get the transformation history for a specific face.
1095
792
 
1096
793
  **Edge Topology**
1097
794
 
1098
- #### `edge()` — Get a named topology edge. Only available on shapes with tracked topology (from box/cylinder/extrude).
1099
-
1100
- ```ts
1101
- edge(name: string): EdgeRef
1102
- ```
1103
-
1104
- #### `edgeNames()` — List named topology edge names. Returns empty array if shape has no tracked topology.
795
+ #### `edge(name: string): EdgeRef` — Get a named topology edge. Only available on shapes with tracked topology (from box/cylinder/extrude).
1105
796
 
1106
- ```ts
1107
- edgeNames(): string[]
1108
- ```
797
+ #### `edgeNames(): string[]` — List named topology edge names. Returns empty array if shape has no tracked topology.
1109
798
 
1110
- #### `edgesOf()` — Return all boundary edges of a named face.
799
+ #### `edgesOf(faceLabel: string, options?: EdgesOfOptions): EdgeSegment[]` — Return all boundary edges of a named face.
1111
800
 
1112
801
  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()`.
1113
802
 
@@ -1127,10 +816,6 @@ body = fillet(body, 1.5, body.edgesOf('opening'))
1127
816
  body.edgesOf('top', { concave: true })
1128
817
  ```
1129
818
 
1130
- ```ts
1131
- edgesOf(faceLabel: string, options?: EdgesOfOptions): EdgeSegment[]
1132
- ```
1133
-
1134
819
  **`EdgesOfOptions`**
1135
820
 
1136
821
  | Option | Type | Description |
@@ -1140,7 +825,7 @@ edgesOf(faceLabel: string, options?: EdgesOfOptions): EdgeSegment[]
1140
825
  | `concave?` | `boolean` | Additional geometric filter: only concave edges. |
1141
826
  | `minLength?` | `number` | Minimum edge length filter. |
1142
827
 
1143
- #### `edgesBetween()` — Return edges shared between two named faces.
828
+ #### `edgesBetween(faceA: string, faceB: string | string[]): EdgeSegment[]` — Return edges shared between two named faces.
1144
829
 
1145
830
  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."
1146
831
 
@@ -1159,155 +844,63 @@ tube = fillet(tube, 1, tube.edgesBetween('cap', 'barrel'))
1159
844
  body.edgesBetween('lid', ['left-wall', 'right-wall', 'front-wall', 'back-wall'])
1160
845
  ```
1161
846
 
1162
- ```ts
1163
- edgesBetween(faceA: string, faceB: string | string[]): EdgeSegment[]
1164
- ```
1165
-
1166
847
  **Transforms**
1167
848
 
1168
- #### `translate()` — Move the shape relative to its current position. All transforms are immutable and return new shapes.
1169
-
1170
- ```ts
1171
- translate(x: number, y: number, z: number): Shape
1172
- ```
849
+ #### `translate(x: number, y: number, z: number): Shape` — Move the shape relative to its current position. All transforms are immutable and return new shapes.
1173
850
 
1174
- #### `translatePolar()` — Translate using polar coordinates (radius + angle in degrees). Eliminates manual `r * Math.cos(angle * PI/180)` calculations.
851
+ #### `translatePolar(radius: number, angleDeg: number, z?: number): Shape` — Translate using polar coordinates (radius + angle in degrees). Eliminates manual `r * Math.cos(angle * PI/180)` calculations.
1175
852
 
1176
853
  Example: `shape.translatePolar(50, 30)` moves 50mm at 30 degrees from +X.
1177
854
 
1178
- ```ts
1179
- translatePolar(radius: number, angleDeg: number, z?: number): Shape
1180
- ```
855
+ #### `moveTo(x: number, y: number, z: number): Shape` — Position the shape so its bounding box min corner is at the given global coordinate.
1181
856
 
1182
- #### `moveTo()` — Position the shape so its bounding box min corner is at the given global coordinate.
857
+ #### `moveToLocal(target: Shape | { toShape(): Shape; }, x: number, y: number, z: number): Shape` — Position the shape relative to another shape's local coordinate system (bounding box min corner).
1183
858
 
1184
- ```ts
1185
- moveTo(x: number, y: number, z: number): Shape
1186
- ```
859
+ #### `rotate(axis: Vec3, angleDeg: number, options?: { pivot?: Vec3; }): Shape` — Rotate around an arbitrary axis through the origin. Unlike `Sketch.rotate()` (bounding-box center), this pivots at the world origin — pass `options.pivot` to rotate in place.
1187
860
 
1188
- #### `moveToLocal()` — Position the shape relative to another shape's local coordinate system (bounding box min corner).
861
+ #### `rotateX(angleDeg: number, options?: { pivot?: Vec3; }): Shape` — Rotate around the X axis by the given angle in degrees.
1189
862
 
1190
- ```ts
1191
- moveToLocal(target: Shape | { toShape(): Shape; }, x: number, y: number, z: number): Shape
1192
- ```
863
+ #### `rotateY(angleDeg: number, options?: { pivot?: Vec3; }): Shape` — Rotate around the Y axis by the given angle in degrees.
1193
864
 
1194
- #### `rotate()` — Rotate around an arbitrary axis through the origin.
865
+ #### `rotateZ(angleDeg: number, options?: { pivot?: Vec3; }): Shape` — Rotate around the Z axis by the given angle in degrees.
1195
866
 
1196
- ```ts
1197
- rotate(axis: [ number, number, number ], angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape
1198
- ```
1199
-
1200
- #### `rotateX()` — Rotate around the X axis by the given angle in degrees.
1201
-
1202
- ```ts
1203
- rotateX(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape
1204
- ```
1205
-
1206
- #### `rotateY()` — Rotate around the Y axis by the given angle in degrees.
1207
-
1208
- ```ts
1209
- rotateY(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape
1210
- ```
1211
-
1212
- #### `rotateZ()` — Rotate around the Z axis by the given angle in degrees.
1213
-
1214
- ```ts
1215
- rotateZ(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape
1216
- ```
1217
-
1218
- #### `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.
1219
-
1220
- ```ts
1221
- rotateAroundTo(axis: [ number, number, number ], pivot: [ number, number, number ], movingPoint: RotationPointLike, targetPoint: RotationPointLike, options?: RotateAroundToOptions): Shape
1222
- ```
867
+ #### `rotateAroundTo(axis: Vec3, pivot: Vec3, movingPoint: RotationPointLike, targetPoint: RotationPointLike, options?: RotateAroundToOptions): Shape` — 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.
1223
868
 
1224
869
  `RotateAroundToOptions`: `{ mode?: RotateAroundToMode }`
1225
870
 
1226
- #### `transform()` — Apply a 4x4 affine transform matrix (column-major) or a Transform object.
1227
-
1228
- ```ts
1229
- transform(m: Mat4 | Transform): Shape
1230
- ```
1231
-
1232
- #### `scale()` — Scale the shape uniformly or per-axis from the shape's bounding box center. Accepts a single number or [x, y, z] array.
1233
-
1234
- ```ts
1235
- scale(v: number | [ number, number, number ]): Shape
1236
- ```
871
+ #### `transform(m: Mat4 | Transform): Shape` — Apply a 4x4 affine transform matrix (column-major) or a Transform object.
1237
872
 
1238
- #### `scaleAround()` — Scale the shape uniformly or per-axis from an explicit pivot point.
873
+ #### `scale(v: number | Vec3): Shape` — Scale the shape uniformly or per-axis from the shape's bounding box center. Accepts a single number or [x, y, z] array.
1239
874
 
1240
- ```ts
1241
- scaleAround(pivot: [ number, number, number ], v: number | [ number, number, number ]): Shape
1242
- ```
875
+ #### `scaleAround(pivot: Vec3, v: number | Vec3): Shape` — Scale the shape uniformly or per-axis from an explicit pivot point.
1243
876
 
1244
- #### `mirror()` — Mirror across a plane through the shape's bounding box center, defined by its normal vector.
877
+ #### `mirror(normal: Vec3): Shape` — Mirror across a plane through the shape's bounding box center, defined by its normal vector.
1245
878
 
1246
- ```ts
1247
- mirror(normal: [ number, number, number ]): Shape
1248
- ```
879
+ #### `mirrorThrough(point: Vec3, normal: Vec3): Shape` — Mirror across a plane through an explicit point, defined by its normal vector.
1249
880
 
1250
- #### `mirrorThrough()` — Mirror across a plane through an explicit point, defined by its normal vector.
1251
-
1252
- ```ts
1253
- mirrorThrough(point: [ number, number, number ], normal: [ number, number, number ]): Shape
1254
- ```
1255
-
1256
- #### `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.
881
+ #### `pointAlong(direction: Vec3): Shape` — 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.
1257
882
 
1258
883
  Example: cylinder(40, 5).pointAlong([1, 0, 0]) — lays cylinder along X, starting at origin
1259
884
 
1260
- ```ts
1261
- pointAlong(direction: [ number, number, number ]): Shape
1262
- ```
1263
-
1264
885
  **Booleans & Cutting**
1265
886
 
1266
- #### `add()` — Union this shape with others (additive boolean). Method form of union().
1267
-
1268
- ```ts
1269
- add(...others: ShapeOperandInput[]): Shape
1270
- ```
1271
-
1272
- #### `subtract()` — Subtract other shapes from this one. Method form of difference().
1273
-
1274
- ```ts
1275
- subtract(...others: ShapeOperandInput[]): Shape
1276
- ```
1277
-
1278
- #### `intersect()` — Keep only the overlap with other shapes. Method form of intersection().
887
+ #### `add(...others: ShapeOperandInput[]): Shape` — Union this shape with others (additive boolean). Method form of union().
1279
888
 
1280
- ```ts
1281
- intersect(...others: ShapeOperandInput[]): Shape
1282
- ```
889
+ #### `subtract(...others: ShapeOperandInput[]): Shape` — Subtract other shapes from this one. Method form of difference().
1283
890
 
1284
- #### `split()` — Split into [inside, outside] by another shape.
891
+ #### `intersect(...others: ShapeOperandInput[]): Shape` — Keep only the overlap with other shapes. Method form of intersection().
1285
892
 
1286
- ```ts
1287
- split(cutter: Shape | { toShape(): Shape; }): [ Shape, Shape ]
1288
- ```
1289
-
1290
- #### `splitByPlane()` — Split by infinite plane. Returns [positive-side, negative-side].
1291
-
1292
- ```ts
1293
- splitByPlane(normal: [ number, number, number ], originOffset?: number): [ Shape, Shape ]
1294
- ```
893
+ #### `split(cutter: Shape | { toShape(): Shape; }): [ Shape, Shape ]` — Split into [inside, outside] by another shape.
1295
894
 
1296
- #### `trimByPlane()` Keep the positive side of the plane and discard the opposite side.
895
+ #### `splitByPlane(normal: Vec3, originOffset?: number): [ Shape, Shape ]` Split by infinite plane. Returns [positive-side, negative-side].
1297
896
 
1298
- ```ts
1299
- trimByPlane(normal: [ number, number, number ], originOffset?: number): Shape
1300
- ```
897
+ #### `trimByPlane(normal: Vec3, originOffset?: number): Shape` — Keep the positive side of the plane and discard the opposite side.
1301
898
 
1302
899
  **Features**
1303
900
 
1304
- #### `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).
901
+ #### `shell(thickness: number, opts?: { openFaces?: string[]; }): Shape` — 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).
1305
902
 
1306
- ```ts
1307
- shell(thickness: number, opts?: { openFaces?: string[]; }): Shape
1308
- ```
1309
-
1310
- #### `pocket()` — Cut a pocket (cavity) into this solid through the named face.
903
+ #### `pocket(face: FaceSelector, depth: number, opts?: PocketOptions): Shape` — Cut a pocket (cavity) into this solid through the named face.
1311
904
 
1312
905
  ```js
1313
906
  box(100, 100, 20).pocket('top', 8)
@@ -1315,50 +908,39 @@ box(100, 100, 20).pocket('top', 8, { inset: 5 })
1315
908
  box(100, 100, 20).pocket('top', 8, { scale: 0.8 })
1316
909
  ```
1317
910
 
1318
- ```ts
1319
- pocket(face: FaceSelector, depth: number, opts?: PocketOptions): Shape
1320
- ```
1321
-
1322
911
  **`PocketOptions`**
1323
912
  - `inset?: number` — Shrink the face boundary inward by this many mm before extruding. Produces angled walls when combined with depth. Default: 0 (full face).
1324
913
  - `scale?: number` — Scale the face profile uniformly (e.g. 0.8 = 80% of the face area). Mutually exclusive with `inset`; `inset` takes precedence if both are set.
1325
914
  - `join?: "Square" | "Round" | "Miter"` — Corner join style when using `inset`. Default: 'Round'.
1326
915
 
1327
- #### `boss()` — Add a boss (protrusion) from the named face.
916
+ #### `boss(face: FaceSelector, height: number, opts?: BossOptions): Shape` — Add a boss (protrusion) from the named face.
1328
917
 
1329
918
  ```js
1330
919
  box(100, 100, 20).boss('top', 5)
1331
920
  box(100, 100, 20).boss('top', 10, { scale: 0.6 })
1332
921
  ```
1333
922
 
1334
- ```ts
1335
- boss(face: FaceSelector, height: number, opts?: BossOptions): Shape
1336
- ```
1337
-
1338
- #### `hole()` — Drill a hole into this solid at a face.
923
+ #### `hole(faceOrRef: SketchFaceTarget | FaceRef, opts: ShapeHoleOptions): Shape` — Drill a hole into this solid at a face.
1339
924
 
1340
925
  ```js
1341
926
  box(50, 50, 20).hole('top', { diameter: 8, depth: 10 })
1342
927
  box(50, 50, 20).hole('top', { diameter: 6, counterbore: { diameter: 12, depth: 3 } })
1343
928
  ```
1344
929
 
1345
- ```ts
1346
- hole(faceOrRef: SketchFaceTarget | FaceRef, opts: ShapeHoleOptions): Shape
1347
- ```
1348
-
1349
930
  **`FaceRef`**
1350
931
 
1351
932
  | Option | Type | Description |
1352
933
  |--------|------|-------------|
1353
- | `normal` | `[ number, number, number ]` | Normal direction of the face |
1354
- | `center` | `[ number, number, number ]` | Center point of the face |
934
+ | `normal` | `Vec3` | Normal direction of the face |
935
+ | `center` | `Vec3` | Center point of the face |
1355
936
  | `query?` | `FaceQueryRef` | Compiler-owned face query when available. |
1356
937
  | `planar?` | `boolean` | True when the face can host a 2D sketch placement frame |
1357
- | `uAxis?` | `[ number, number, number ]` | Face-local horizontal axis for planar faces |
1358
- | `vAxis?` | `[ number, number, number ]` | Face-local vertical axis for planar faces |
938
+ | `uAxis?` | `Vec3` | Face-local horizontal axis for planar faces |
939
+ | `vAxis?` | `Vec3` | Face-local vertical axis for planar faces |
1359
940
  | `surface?` | `FaceSurface` | Analytic surface family when the backend can identify one. |
1360
941
  | `descendant?` | `FaceDescendantMetadata` | Shared descendant-resolution metadata when this face is a semantic region/set. |
1361
- | `name` | | — |
942
+
943
+ Also: `name: FaceName`.
1362
944
 
1363
945
  **`FaceDescendantMetadata`**: `kind: "single" | "face-set"`, `semantic: FaceDescendantSemantic`, `memberCount: number`, `memberNames: string[]`, `coplanar: boolean`
1364
946
 
@@ -1370,7 +952,7 @@ hole(faceOrRef: SketchFaceTarget | FaceRef, opts: ShapeHoleOptions): Shape
1370
952
 
1371
953
  **`ShapeHoleThreadOptions`**: `designation?: string`, `pitch?: number`, `class?: string`, `handedness?: "right" | "left"`, `depth?: number`, `modeled?: boolean`
1372
954
 
1373
- #### `cutout()` — Cut a profile-shaped pocket through a face using a placed sketch.
955
+ #### `cutout(sketch: Sketch, opts?: ShapeCutoutOptions): Shape` — Cut a profile-shaped pocket through a face using a placed sketch.
1374
956
 
1375
957
  The sketch must be placed on a face with `Sketch.onFace(...)`. The cut follows the sketch's 2D profile.
1376
958
 
@@ -1379,15 +961,11 @@ const profile = circle2d(10).onFace(body, 'top');
1379
961
  body.cutout(profile, { depth: 5 })
1380
962
  ```
1381
963
 
1382
- ```ts
1383
- cutout(sketch: Sketch, opts?: ShapeCutoutOptions): Shape
1384
- ```
1385
-
1386
- **`ShapeCutoutOptions`**: `depth?: number`, `upToFace?: SketchFaceTarget | FaceRef`, `extent?: ShapeFeatureExtentOptions`, `taperScale?: number | [ number, number ]`
964
+ **`ShapeCutoutOptions`**: `depth?: number`, `upToFace?: SketchFaceTarget | FaceRef`, `extent?: ShapeFeatureExtentOptions`, `taperScale?: number | Vec2`
1387
965
 
1388
966
  **Placement**
1389
967
 
1390
- #### `placeReference()` — Translate the shape so the given anchor or reference lands on the target coordinate.
968
+ #### `placeReference(ref: PlacementAnchorLike, target: Vec3, offset?: Vec3): Shape` — Translate the shape so the given anchor or reference lands on the target coordinate.
1391
969
 
1392
970
  Accepts any built-in anchor name (`'bottom'`, `'center'`, `'top-front-left'`, etc.) or a custom placement reference attached via `withReferences()`.
1393
971
 
@@ -1402,19 +980,11 @@ shape.placeReference('center', [0, 0, 0])
1402
980
  shape.placeReference('left', [10, 0, 0])
1403
981
  ```
1404
982
 
1405
- ```ts
1406
- placeReference(ref: PlacementAnchorLike, target: [ number, number, number ], offset?: [ number, number, number ]): Shape
1407
- ```
1408
-
1409
- #### `attachTo()` — Position this shape relative to another using named 3D anchor points.
983
+ #### `attachTo(target: ShapeAnchorTarget, targetAnchor: PlacementAnchorLike, selfAnchor?: PlacementAnchorLike, offset?: Vec3): Shape` — Position this shape relative to another using named 3D anchor points.
1410
984
 
1411
985
  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.
1412
986
 
1413
- ```ts
1414
- attachTo(target: ShapeAnchorTarget, targetAnchor: PlacementAnchorLike, selfAnchor?: PlacementAnchorLike, offset?: [ number, number, number ]): Shape
1415
- ```
1416
-
1417
- #### `onFace()` — Place this shape on a face of a parent shape.
987
+ #### `onFace(parent: ShapeAnchorTarget, face: "front" | "back" | "left" | "right" | "top" | "bottom", opts?: { u?: number; v?: number; protrude?: number; }): Shape` — Place this shape on a face of a parent shape.
1418
988
 
1419
989
  Think of it like sticking a label on a box surface:
1420
990
 
@@ -1425,11 +995,7 @@ Think of it like sticking a label on a box surface:
1425
995
  - top/bottom: u = left/right (X), v = forward/back (Y)
1426
996
  - `protrude` = how far the child sticks out (positive = outward from face)
1427
997
 
1428
- ```ts
1429
- onFace(parent: ShapeAnchorTarget, face: "front" | "back" | "left" | "right" | "top" | "bottom", opts?: { u?: number; v?: number; protrude?: number; }): Shape
1430
- ```
1431
-
1432
- #### `seatInto()` — Slide this shape along an axis until a labeled face is embedded in the target body.
998
+ #### `seatInto(target: Shape, surface: string, options?: SeatIntoOptions): Shape` — Slide this shape along an axis until a labeled face is embedded in the target body.
1433
999
 
1434
1000
  Position the shape roughly first (translate/rotate), then call seatInto to auto-adjust the penetration depth. No manual coordinate math needed.
1435
1001
 
@@ -1444,16 +1010,12 @@ pod.translate(0, station, radius + 20).seatInto(fuselage, 'base', { depth: 'flus
1444
1010
  mast.translate(0, station, radius + 50).seatInto(fuselage, 'mount', { depth: 'flush', gap: 3 });
1445
1011
  ```
1446
1012
 
1447
- ```ts
1448
- seatInto(target: Shape, surface: string, options?: SeatIntoOptions): Shape
1449
- ```
1450
-
1451
1013
  **`SeatIntoOptions`**
1452
- - `along?: [ number, number, number ]` — Movement axis. Default: inverted face normal (points into target).
1014
+ - `along?: Vec3` — Movement axis. Default: inverted face normal (points into target).
1453
1015
  - `depth?: "full" | "flush" | number` — How deep to embed. 'full' = entire face inside. 'flush' = nearest point touches. number = mm past flush. Default: 'full'.
1454
1016
  - `gap?: number` — Standoff gap in mm. Positive = gap between face and target. Negative = extra penetration. Default: 0.
1455
1017
 
1456
- #### `seatOver()` — Slide this shape until a target's labeled face is fully covered (inside this shape).
1018
+ #### `seatOver(target: Shape, targetSurface: string, options?: SeatIntoOptions): Shape` — Slide this shape until a target's labeled face is fully covered (inside this shape).
1457
1019
 
1458
1020
  The inverse of `seatInto`: instead of embedding *your* face into the target, you move until the *target's* face is embedded inside you.
1459
1021
 
@@ -1465,47 +1027,25 @@ nacelle.translate(rough).seatOver(pylon, 'bottom');
1465
1027
  cap.translate(rough).seatOver(post, 'top');
1466
1028
  ```
1467
1029
 
1468
- ```ts
1469
- seatOver(target: Shape, targetSurface: string, options?: SeatIntoOptions): Shape
1470
- ```
1471
-
1472
1030
  **Connectors**
1473
1031
 
1474
- #### `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).
1032
+ #### `withConnectors(connectors: Record<string, ConnectorInput>): Shape` — Attach named connectors — attachment points that survive transforms and imports. Connectors can be bare (position + orientation) or typed (with connectorType/gender for compatibility matching).
1475
1033
 
1476
- ```ts
1477
- withConnectors(connectors: Record<string, ConnectorInput>): Shape
1478
- ```
1479
-
1480
- **`PortInput`**: `origin?: [ number, number, number ]`, `axis?: [ number, number, number ]`, `start?: [ number, number, number ]`, `end?: [ number, number, number ]`, `up?: [ number, number, number ]`, `kind?: JointType`, `min?: number`, `max?: number`
1034
+ `PortInput`: `{ origin?: Vec3, axis?: Vec3, start?: Vec3, end?: Vec3, up?: Vec3, kind?: JointType, min?: number, max?: number }`
1481
1035
 
1482
1036
  `ConnectorInput`: `{ connectorType?: string, gender?: ConnectorGender, measurements?: Record<string, number | string> }`
1483
1037
 
1484
- #### `connectorNames()` — List all connector names on this shape.
1485
-
1486
- ```ts
1487
- connectorNames(): string[]
1488
- ```
1489
-
1490
- #### `connectorsByType()` — Get all connectors of a given type.
1491
-
1492
- ```ts
1493
- connectorsByType(type: string): Array<{ name: string; port: ConnectorDef; }>
1494
- ```
1038
+ #### `connectorNames(): string[]` — List all connector names on this shape.
1495
1039
 
1496
- #### `connectorDistance()`Distance between two connector origins on this shape.
1040
+ #### `connectorsByType(type: string): Array<{ name: string; port: ConnectorDef; }>` Get all connectors of a given type.
1497
1041
 
1498
- ```ts
1499
- connectorDistance(nameA: string, nameB: string): number
1500
- ```
1042
+ #### `connectorDistance(nameA: string, nameB: string): number` — Distance between two connector origins on this shape.
1501
1043
 
1502
- #### `connectorMeasurements()` — Get measurements metadata from a connector.
1044
+ #### `connectorMeasurements(name: string): Record<string, number | string>` — Get measurements metadata from a connector.
1503
1045
 
1504
- ```ts
1505
- connectorMeasurements(name: string): Record<string, number | string>
1506
- ```
1046
+ #### `matchTo(targetOrPairs: Shape | MatchTarget | Array<[ Shape | MatchTarget, string, string ]>, selfConnOrDict?: string | Record<string, string>, targetConnOrOptions?: string | MatchToOptions, maybeOptions?: MatchToOptions): Shape` — Position this shape by matching connectors to a target.
1507
1047
 
1508
- #### `matchTo()` — Position this shape by matching connectors to a target.
1048
+ Alignment: with a single connector pair, the shape translates and rotates so the connector origins coincide and the axes oppose (plug-in model); `up` pins the roll. With multiple pairs, the connector origins define the rigid transform still author meaningful `axis`/`up` values so the same connectors remain useful for `connect()`, audits, and future matching.
1509
1049
 
1510
1050
  Overloads:
1511
1051
 
@@ -1513,125 +1053,53 @@ Overloads:
1513
1053
  - Dictionary (same target): `matchTo(target, { selfConn: targetConn, ... }, options?)`
1514
1054
  - Multi-target: `matchTo([ [target1, selfConn1, targetConn1], ... ], options?)`
1515
1055
 
1516
- ```ts
1517
- matchTo(targetOrPairs: Shape | MatchTarget | Array<[ Shape | MatchTarget, string, string ]>, selfConnOrDict?: string | Record<string, string>, targetConnOrOptions?: string | MatchToOptions, maybeOptions?: MatchToOptions): Shape
1518
- ```
1519
-
1520
1056
  `MatchToOptions`: `{ force?: boolean, angle?: number, distance?: number }`
1521
1057
 
1522
1058
  **References**
1523
1059
 
1524
- #### `withReferences()` — Attach named placement references that survive normal transforms and imports.
1525
-
1526
- ```ts
1527
- withReferences(refs: PlacementReferenceInput): Shape
1528
- ```
1060
+ #### `withReferences(refs: PlacementReferenceInput): Shape` — Attach named placement references that survive normal transforms and imports.
1529
1061
 
1530
- **`PlacementReferenceInput`**: `points?: Record<string, [ number, number, number ]>`, `edges?: Record<string, PlacementEdgeRef>`, `surfaces?: Record<string, PlacementSurfaceRef>`, `objects?: Record<string, PlacementObjectInput>`
1062
+ **`PlacementReferenceInput`**: `points?: Record<string, Vec3>`, `edges?: Record<string, PlacementEdgeRef>`, `surfaces?: Record<string, PlacementSurfaceRef>`, `objects?: Record<string, PlacementObjectInput>`
1531
1063
 
1532
1064
  `PlacementEdgeRef`: `{ start: Vec3, end: Vec3 }`
1533
1065
 
1534
1066
  `PlacementSurfaceRef`: `{ center: Vec3, normal: Vec3 }`
1535
1067
 
1536
- #### `referenceNames()` — List named placement references carried by this shape.
1537
-
1538
- ```ts
1539
- referenceNames(kind?: PlacementReferenceKind): string[]
1540
- ```
1541
-
1542
- #### `referencePoint()` — Resolve a named placement reference or built-in anchor to a 3D point.
1068
+ #### `referenceNames(kind?: PlacementReferenceKind): string[]` — List named placement references carried by this shape.
1543
1069
 
1544
- ```ts
1545
- referencePoint(ref: PlacementAnchorLike): [ number, number, number ]
1546
- ```
1070
+ #### `referencePoint(ref: PlacementAnchorLike): Vec3` — Resolve a named placement reference or built-in anchor to a 3D point.
1547
1071
 
1548
1072
  **Measurement**
1549
1073
 
1550
- #### `boundingBox()` — Get the axis-aligned bounding box as { min: [x,y,z], max: [x,y,z] }.
1074
+ #### `boundingBox(): ShapeRuntimeBounds` — Get the axis-aligned bounding box as { min: [x,y,z], max: [x,y,z] }.
1551
1075
 
1552
- ```ts
1553
- boundingBox(): ShapeRuntimeBounds
1554
- ```
1076
+ #### `volume(): number` — Volume in mm cubed.
1555
1077
 
1556
- #### `volume()` — Volume in mm cubed.
1078
+ #### `surfaceArea(): number` — Surface area in mm squared.
1557
1079
 
1558
- ```ts
1559
- volume(): number
1560
- ```
1080
+ #### `isEmpty(): boolean` — True if the shape contains no geometry.
1561
1081
 
1562
- #### `surfaceArea()` — Surface area in mm squared.
1082
+ #### `numBodies(): number` — Number of disconnected solid bodies in this shape.
1563
1083
 
1564
- ```ts
1565
- surfaceArea(): number
1566
- ```
1567
-
1568
- #### `isEmpty()` — True if the shape contains no geometry.
1569
-
1570
- ```ts
1571
- isEmpty(): boolean
1572
- ```
1573
-
1574
- #### `numBodies()` — Number of disconnected solid bodies in this shape.
1575
-
1576
- ```ts
1577
- numBodies(): number
1578
- ```
1579
-
1580
- #### `numTri()` — Triangle count of the mesh representation.
1581
-
1582
- ```ts
1583
- numTri(): number
1584
- ```
1084
+ #### `numTri(): number` — Triangle count of the mesh representation.
1585
1085
 
1586
1086
  **Other**
1587
1087
 
1588
- #### `clone()` — Return a new Shape wrapper for explicit duplication in scripts.
1088
+ #### `clone(): Shape` — Return a new Shape wrapper for explicit duplication in scripts.
1589
1089
 
1590
- ```ts
1591
- clone(): Shape
1592
- ```
1593
-
1594
- #### `geometryInfo()` — Inspect which backend/representation produced this solid.
1090
+ #### `geometryInfo(): GeometryInfo` — Inspect which backend/representation produced this solid.
1595
1091
 
1596
- ```ts
1597
- geometryInfo(): GeometryInfo
1598
- ```
1599
-
1600
- #### `as()` — Name this shape as a reference namespace for diagnostics and future published refs.
1601
-
1602
- ```ts
1603
- as(name: string): Shape
1604
- ```
1092
+ #### `as(name: string): Shape` — Name this shape as a reference namespace for diagnostics and future published refs.
1605
1093
 
1606
- #### `ref()` — Resolve a semantic reference path like `lid`, `lid/back`, or a midpoint selector on `lid/back`.
1094
+ #### `ref(path: string): ShapeRef` — Resolve a semantic reference path like `lid`, `lid/back`, or a midpoint selector on `lid/back`.
1607
1095
 
1608
- ```ts
1609
- ref(path: string): ShapeRef
1610
- ```
1096
+ #### `thicken(thickness: number): Shape` — Offset-thicken an exact open surface or shell into a solid.
1611
1097
 
1612
- #### `thicken()` — Offset-thicken an exact open surface or shell into a solid.
1098
+ #### `getMesh(): ShapeRuntimeMesh` — Extract triangle mesh for Three.js rendering
1613
1099
 
1614
- ```ts
1615
- thicken(thickness: number): Shape
1616
- ```
1100
+ #### `slice(offset?: number): any` — Slice the runtime solid by a plane normal to local Z at the given offset.
1617
1101
 
1618
- #### `getMesh()` — Extract triangle mesh for Three.js rendering
1619
-
1620
- ```ts
1621
- getMesh(): ShapeRuntimeMesh
1622
- ```
1623
-
1624
- #### `slice()` — Slice the runtime solid by a plane normal to local Z at the given offset.
1625
-
1626
- ```ts
1627
- slice(offset?: number): any
1628
- ```
1629
-
1630
- #### `project()` — Orthographically project the runtime solid onto the local XY plane.
1631
-
1632
- ```ts
1633
- project(): any
1634
- ```
1102
+ #### `project(): any` — Orthographically project the runtime solid onto the local XY plane.
1635
1103
 
1636
1104
  **Compatibility Aliases**
1637
1105
 
@@ -1640,101 +1108,45 @@ project(): any
1640
1108
 
1641
1109
  ### `Transform`
1642
1110
 
1643
- #### `identity()` — Return the identity transform.
1111
+ #### `static identity(): Transform` — Return the identity transform.
1644
1112
 
1645
- ```ts
1646
- static identity(): Transform
1647
- ```
1113
+ #### `static from(input: TransformInput): Transform` — Wrap an existing `Transform` or raw 4x4 matrix as a `Transform`.
1648
1114
 
1649
- #### `from()` — Wrap an existing `Transform` or raw 4x4 matrix as a `Transform`.
1650
-
1651
- ```ts
1652
- static from(input: TransformInput): Transform
1653
- ```
1115
+ #### `static compose(...steps: TransformInput[]): Transform` — Compose transforms in chain order: `Transform.compose(a, b, c)` applies `a`, then `b`, then `c` — the same left-to-right order as `Transform.from(a).mul(b).mul(c)`.
1654
1116
 
1655
- #### `translation()` Create a translation transform.
1117
+ Prefer this over manual `.mul()` chains when composing 3+ transforms (e.g. kinematics: `local -> childBase -> jointMotion -> jointFrame -> parentWorld`); the variadic form makes the application order explicit and prevents order mistakes.
1656
1118
 
1657
1119
  ```ts
1658
- static translation(x: number, y: number, z: number): Transform
1120
+ const world = Transform.compose(childBase, jointMotion, jointFrame, parentWorld);
1659
1121
  ```
1660
1122
 
1661
- #### `scale()` — Create a uniform or per-axis scale transform.
1123
+ #### `static translation(x: number, y: number, z: number): Transform` — Create a translation transform.
1662
1124
 
1663
- ```ts
1664
- static scale(v: number | Vec3): Transform
1665
- ```
1125
+ #### `static scale(v: number | Vec3): Transform` — Create a uniform or per-axis scale transform.
1666
1126
 
1667
- #### `rotationAxis()` — Create a rotation around an arbitrary axis, optionally about a pivot.
1127
+ #### `static rotationAxis(axis: Vec3, angleDeg: number, pivot?: Vec3): Transform` — Create a rotation around an arbitrary axis, optionally about a pivot.
1668
1128
 
1669
- ```ts
1670
- static rotationAxis(axis: Vec3, angleDeg: number, pivot?: Vec3): Transform
1671
- ```
1129
+ #### `static rotateAroundTo(axis: Vec3, pivot: Vec3, movingPoint: Vec3, targetPoint: Vec3, options?: RotateAroundToOptions): Transform` — Solve the rotation needed to move one point onto a target line or plane.
1672
1130
 
1673
- #### `rotateAroundTo()` — Solve the rotation needed to move one point onto a target line or plane.
1131
+ #### `mul(other: TransformInput): Transform` — Compose transforms in chain order: `a.mul(b)` applies `a`, then `b`.
1674
1132
 
1675
- ```ts
1676
- static rotateAroundTo(axis: Vec3, pivot: Vec3, movingPoint: Vec3, targetPoint: Vec3, options?: RotateAroundToOptions): Transform
1677
- ```
1133
+ #### `translate(x: number, y: number, z: number): Transform` — Translate after the current transform.
1678
1134
 
1679
- #### `mul()` Compose transforms in chain order: `a.mul(b)` applies `a`, then `b`.
1135
+ #### `rotateAxis(axis: Vec3, angleDeg: number, pivot?: Vec3): Transform` Rotate after the current transform.
1680
1136
 
1681
- ```ts
1682
- mul(other: TransformInput): Transform
1683
- ```
1137
+ #### `rotateX(angleDeg: number, pivot?: Vec3): Transform` — Rotate about the X axis after the current transform (parity with `Shape.rotateX`).
1684
1138
 
1685
- #### `translate()` — Translate after the current transform.
1139
+ #### `rotateY(angleDeg: number, pivot?: Vec3): Transform` — Rotate about the Y axis after the current transform (parity with `Shape.rotateY`).
1686
1140
 
1687
- ```ts
1688
- translate(x: number, y: number, z: number): Transform
1689
- ```
1141
+ #### `rotateZ(angleDeg: number, pivot?: Vec3): Transform` — Rotate about the Z axis after the current transform (parity with `Shape.rotateZ`).
1690
1142
 
1691
- #### `rotateAxis()` — Rotate after the current transform.
1143
+ #### `inverse(): Transform` — Return the inverse transform.
1692
1144
 
1693
- ```ts
1694
- rotateAxis(axis: Vec3, angleDeg: number, pivot?: Vec3): Transform
1695
- ```
1145
+ #### `point(p: Vec3): Vec3` — Transform a point using homogeneous coordinates.
1696
1146
 
1697
- #### `rotateX()` — Rotate about the X axis after the current transform (parity with `Shape.rotateX`).
1147
+ #### `vector(v: Vec3): Vec3` — Transform a direction vector without translation.
1698
1148
 
1699
- ```ts
1700
- rotateX(angleDeg: number, pivot?: Vec3): Transform
1701
- ```
1702
-
1703
- #### `rotateY()` — Rotate about the Y axis after the current transform (parity with `Shape.rotateY`).
1704
-
1705
- ```ts
1706
- rotateY(angleDeg: number, pivot?: Vec3): Transform
1707
- ```
1708
-
1709
- #### `rotateZ()` — Rotate about the Z axis after the current transform (parity with `Shape.rotateZ`).
1710
-
1711
- ```ts
1712
- rotateZ(angleDeg: number, pivot?: Vec3): Transform
1713
- ```
1714
-
1715
- #### `inverse()` — Return the inverse transform.
1716
-
1717
- ```ts
1718
- inverse(): Transform
1719
- ```
1720
-
1721
- #### [`point()`](/docs/sketch#point) — Transform a point using homogeneous coordinates.
1722
-
1723
- ```ts
1724
- point(p: Vec3): Vec3
1725
- ```
1726
-
1727
- #### `vector()` — Transform a direction vector without translation.
1728
-
1729
- ```ts
1730
- vector(v: Vec3): Vec3
1731
- ```
1732
-
1733
- #### `toArray()` — Return the transform as a raw 4x4 matrix array.
1734
-
1735
- ```ts
1736
- toArray(): Mat4
1737
- ```
1149
+ #### `toArray(): Mat4` — Return the transform as a raw 4x4 matrix array.
1738
1150
 
1739
1151
  ### `ShapeGroup`
1740
1152
 
@@ -1743,117 +1155,49 @@ toArray(): Mat4
1743
1155
  | Property | Type | Description |
1744
1156
  |----------|------|-------------|
1745
1157
  | `children` | `GroupChild[]` | — |
1746
- | `childNames` | `Array<string | undefined>` | — |
1158
+ | `childNames` | `Array<string \| undefined>` | — |
1747
1159
 
1748
1160
  **Children**
1749
1161
 
1750
- #### `child()` — Return the named child by name. Throws if not found. Useful when importing a multipart group and working on components individually.
1751
-
1752
- ```ts
1753
- child(name: string): GroupChild
1754
- ```
1755
-
1756
- #### `childName()` — Return the optional name of the child at `index`.
1162
+ #### `child(name: string): GroupChild` — Return the named child by name. Throws if not found. Useful when importing a multipart group and working on components individually.
1757
1163
 
1758
- ```ts
1759
- childName(index: number): string | undefined
1760
- ```
1164
+ #### `childName(index: number): string | undefined` — Return the optional name of the child at `index`.
1761
1165
 
1762
1166
  **Transforms**
1763
1167
 
1764
- #### `translate()` — Move the entire group by (x, y, z). All children move together as a unit.
1765
-
1766
- ```ts
1767
- translate(x: number, y: number, z: number): ShapeGroup
1768
- ```
1769
-
1770
- #### `moveTo()` — Move the group so its bounding-box min corner lands at the given coordinate.
1771
-
1772
- ```ts
1773
- moveTo(x: number, y: number, z: number): ShapeGroup
1774
- ```
1775
-
1776
- #### `moveToLocal()` — Move the group relative to another part's bounding-box min corner.
1777
-
1778
- ```ts
1779
- moveToLocal(target: Shape | ShapeGroup, x: number, y: number, z: number): ShapeGroup
1780
- ```
1781
-
1782
- #### `rotate()` — Rotate the group around an arbitrary axis through the origin.
1168
+ #### `translate(x: number, y: number, z: number): ShapeGroup` — Move the entire group by (x, y, z). All children move together as a unit.
1783
1169
 
1784
- ```ts
1785
- rotate(axis: [ number, number, number ], angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
1786
- ```
1170
+ #### `moveTo(x: number, y: number, z: number): ShapeGroup` — Move the group so its bounding-box min corner lands at the given coordinate.
1787
1171
 
1788
- #### `rotateX()` — Rotate the group around the X axis.
1172
+ #### `moveToLocal(target: Shape | ShapeGroup, x: number, y: number, z: number): ShapeGroup` — Move the group relative to another part's bounding-box min corner.
1789
1173
 
1790
- ```ts
1791
- rotateX(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
1792
- ```
1174
+ #### `rotate(axis: Vec3, angleDeg: number, options?: { pivot?: Vec3; }): ShapeGroup` — Rotate the group around an arbitrary axis through the origin. Unlike `scale()`/`mirror()` (bounding-box center) and `Sketch.rotate()`, this pivots at the world origin — pass `options.pivot` to rotate in place.
1793
1175
 
1794
- #### `rotateY()` — Rotate the group around the Y axis.
1176
+ #### `rotateX(angleDeg: number, options?: { pivot?: Vec3; }): ShapeGroup` — Rotate the group around the X axis.
1795
1177
 
1796
- ```ts
1797
- rotateY(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
1798
- ```
1178
+ #### `rotateY(angleDeg: number, options?: { pivot?: Vec3; }): ShapeGroup` — Rotate the group around the Y axis.
1799
1179
 
1800
- #### `rotateZ()` — Rotate the group around the Z axis.
1180
+ #### `rotateZ(angleDeg: number, options?: { pivot?: Vec3; }): ShapeGroup` — Rotate the group around the Z axis.
1801
1181
 
1802
- ```ts
1803
- rotateZ(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
1804
- ```
1182
+ #### `rotateAroundAxis(axis: Vec3, angleDeg: number, pivot?: Vec3): ShapeGroup` — Rotate around an arbitrary axis, optionally through a pivot point.
1805
1183
 
1806
- #### `rotateAroundAxis()` — Rotate around an arbitrary axis, optionally through a pivot point.
1184
+ #### `rotateAroundTo(axis: Vec3, pivot: Vec3, movingPoint: Anchor3D | Vec3, targetPoint: Anchor3D | Vec3, options?: RotateAroundToOptions): ShapeGroup` — 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.
1807
1185
 
1808
- ```ts
1809
- rotateAroundAxis(axis: [ number, number, number ], angleDeg: number, pivot?: [ number, number, number ]): ShapeGroup
1810
- ```
1186
+ #### `pointAlong(direction: Vec3): ShapeGroup` — Reorient the group so its local Z axis points along `direction`.
1811
1187
 
1812
- #### `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.
1188
+ #### `transform(m: Mat4 | Transform): ShapeGroup` Apply a 4x4 transform matrix or `Transform` to all 3D children.
1813
1189
 
1814
- ```ts
1815
- rotateAroundTo(axis: [ number, number, number ], pivot: [ number, number, number ], movingPoint: Anchor3D | [ number, number, number ], targetPoint: Anchor3D | [ number, number, number ], options?: RotateAroundToOptions): ShapeGroup
1816
- ```
1190
+ #### `scale(v: number | Vec3): ShapeGroup` — Scale uniformly or per-axis from the group's bounding-box center.
1817
1191
 
1818
- #### `pointAlong()` — Reorient the group so its local Z axis points along `direction`.
1192
+ #### `scaleAround(pivot: Vec3, v: number | Vec3): ShapeGroup` — Scale uniformly or per-axis from an explicit pivot point.
1819
1193
 
1820
- ```ts
1821
- pointAlong(direction: [ number, number, number ]): ShapeGroup
1822
- ```
1194
+ #### `mirror(normal: Vec3): ShapeGroup` — Mirror across a plane through the group's bounding-box center.
1823
1195
 
1824
- #### `transform()` — Apply a 4x4 transform matrix or `Transform` to all 3D children.
1825
-
1826
- ```ts
1827
- transform(m: Mat4 | Transform): ShapeGroup
1828
- ```
1829
-
1830
- #### `scale()` — Scale uniformly or per-axis from the group's bounding-box center.
1831
-
1832
- ```ts
1833
- scale(v: number | [ number, number, number ]): ShapeGroup
1834
- ```
1835
-
1836
- #### `scaleAround()` — Scale uniformly or per-axis from an explicit pivot point.
1837
-
1838
- ```ts
1839
- scaleAround(pivot: [ number, number, number ], v: number | [ number, number, number ]): ShapeGroup
1840
- ```
1841
-
1842
- #### `mirror()` — Mirror across a plane through the group's bounding-box center.
1843
-
1844
- ```ts
1845
- mirror(normal: [ number, number, number ]): ShapeGroup
1846
- ```
1847
-
1848
- #### `mirrorThrough()` — Mirror across a plane through an explicit point.
1849
-
1850
- ```ts
1851
- mirrorThrough(point: [ number, number, number ], normal: [ number, number, number ]): ShapeGroup
1852
- ```
1196
+ #### `mirrorThrough(point: Vec3, normal: Vec3): ShapeGroup` — Mirror across a plane through an explicit point.
1853
1197
 
1854
1198
  **Placement**
1855
1199
 
1856
- #### `placeReference()` — Translate the group so the given anchor or reference lands on the target coordinate.
1200
+ #### `placeReference(ref: PlacementAnchorLike, target: Vec3, offset?: Vec3): ShapeGroup` — Translate the group so the given anchor or reference lands on the target coordinate.
1857
1201
 
1858
1202
  Accepts any built-in anchor name (`'bottom'`, `'center'`, `'top-front-left'`, etc.) or a custom placement reference attached via `withReferences()`.
1859
1203
 
@@ -1866,57 +1210,27 @@ const placed = require('./bracket-assembly.forge.js').group
1866
1210
  .placeReference('mountCenter', [0, 0, 50]);
1867
1211
  ```
1868
1212
 
1869
- ```ts
1870
- placeReference(ref: PlacementAnchorLike, target: [ number, number, number ], offset?: [ number, number, number ]): ShapeGroup
1871
- ```
1872
-
1873
- #### `attachTo()` — Attach this group to a face or anchor on another part.
1213
+ #### `attachTo(target: Shape | ShapeGroup, targetAnchor: Anchor3D | string, selfAnchor?: Anchor3D, offset?: Vec3): ShapeGroup` — Attach this group to a face or anchor on another part.
1874
1214
 
1875
1215
  `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.
1876
1216
 
1877
- ```ts
1878
- attachTo(target: Shape | ShapeGroup, targetAnchor: Anchor3D | string, selfAnchor?: Anchor3D, offset?: [ number, number, number ]): ShapeGroup
1879
- ```
1880
-
1881
- #### `onFace()` — Place this group on a face of a parent shape. See Shape.onFace() for full documentation.
1882
-
1883
- ```ts
1884
- onFace(parent: Shape | ShapeGroup, face: "front" | "back" | "left" | "right" | "top" | "bottom", opts?: { u?: number; v?: number; protrude?: number; }): ShapeGroup
1885
- ```
1217
+ #### `onFace(parent: Shape | ShapeGroup, face: "front" | "back" | "left" | "right" | "top" | "bottom", opts?: { u?: number; v?: number; protrude?: number; }): ShapeGroup` — Place this group on a face of a parent shape. See Shape.onFace() for full documentation.
1886
1218
 
1887
1219
  **Connectors**
1888
1220
 
1889
- #### `withConnectors()` — Attach named connectors — attachment points that survive transforms. Connectors can be bare (position + orientation) or typed (with connectorType/gender for compatibility matching).
1221
+ #### `withConnectors(connectors: Record<string, ConnectorInput>): ShapeGroup` — Attach named connectors — attachment points that survive transforms. Connectors can be bare (position + orientation) or typed (with connectorType/gender for compatibility matching).
1890
1222
 
1891
- ```ts
1892
- withConnectors(connectors: Record<string, ConnectorInput>): ShapeGroup
1893
- ```
1894
-
1895
- #### `connectorNames()` — List all connector names, including "ChildName.connectorName" from named children.
1896
-
1897
- ```ts
1898
- connectorNames(): string[]
1899
- ```
1900
-
1901
- #### `connectorsByType()` — Get all connectors of a given type, including from named children.
1902
-
1903
- ```ts
1904
- connectorsByType(type: string): Array<{ name: string; port: ConnectorDef; }>
1905
- ```
1223
+ #### `connectorNames(): string[]` — List all connector names, including "ChildName.connectorName" from named children.
1906
1224
 
1907
- #### `connectorDistance()`Distance between two connector origins on this group (supports dotted child paths).
1225
+ #### `connectorsByType(type: string): Array<{ name: string; port: ConnectorDef; }>` Get all connectors of a given type, including from named children.
1908
1226
 
1909
- ```ts
1910
- connectorDistance(nameA: string, nameB: string): number
1911
- ```
1227
+ #### `connectorDistance(nameA: string, nameB: string): number` — Distance between two connector origins on this group (supports dotted child paths).
1912
1228
 
1913
- #### `connectorMeasurements()` — Get measurements metadata from a connector (supports dotted child paths).
1229
+ #### `connectorMeasurements(name: string): Record<string, number | string>` — Get measurements metadata from a connector (supports dotted child paths).
1914
1230
 
1915
- ```ts
1916
- connectorMeasurements(name: string): Record<string, number | string>
1917
- ```
1231
+ #### `matchTo(targetOrPairs: Shape | ShapeGroup | Array<[ Shape | ShapeGroup, string, string ]>, selfConnOrDict?: string | Record<string, string>, targetConnOrOptions?: string | MatchToOptions, maybeOptions?: MatchToOptions): ShapeGroup` — Position this group by matching connectors to a target. Connector names support dotted paths into named children: "ChildName.connectorName".
1918
1232
 
1919
- #### `matchTo()` — Position this group by matching connectors to a target. Connector names support dotted paths into named children: "ChildName.connectorName".
1233
+ Alignment: with a single connector pair, the group translates and rotates so the connector origins coincide and the axes oppose (plug-in model); `up` pins the roll. With multiple pairs, the connector origins define the rigid transform still author meaningful `axis`/`up` values so the same connectors remain useful for `connect()`, audits, and future matching.
1920
1234
 
1921
1235
  Overloads:
1922
1236
 
@@ -1924,13 +1238,9 @@ Overloads:
1924
1238
  - Dictionary (same target): `matchTo(target, { selfConn: targetConn, ... }, options?)`
1925
1239
  - Multi-target: `matchTo([ [target1, selfConn1, targetConn1], ... ], options?)`
1926
1240
 
1927
- ```ts
1928
- matchTo(targetOrPairs: Shape | ShapeGroup | Array<[ Shape | ShapeGroup, string, string ]>, selfConnOrDict?: string | Record<string, string>, targetConnOrOptions?: string | MatchToOptions, maybeOptions?: MatchToOptions): ShapeGroup
1929
- ```
1930
-
1931
1241
  **References**
1932
1242
 
1933
- #### `withReferences()` — Attach named placement references to this group. References survive normal transforms (translate/rotate/scale/mirror/transform).
1243
+ #### `withReferences(refs: PlacementReferenceInput): ShapeGroup` — Attach named placement references to this group. References survive normal transforms (translate/rotate/scale/mirror/transform).
1934
1244
 
1935
1245
  ```javascript
1936
1246
  const bracket = group(
@@ -1941,41 +1251,17 @@ const bracket = group(
1941
1251
  });
1942
1252
  ```
1943
1253
 
1944
- ```ts
1945
- withReferences(refs: PlacementReferenceInput): ShapeGroup
1946
- ```
1947
-
1948
- #### `referenceNames()` — List named placement references carried by this group.
1949
-
1950
- ```ts
1951
- referenceNames(kind?: PlacementReferenceKind): string[]
1952
- ```
1953
-
1954
- #### `referencePoint()` — Resolve a named placement reference or built-in Anchor3D to a 3D point. Named refs take priority over built-in anchors.
1254
+ #### `referenceNames(kind?: PlacementReferenceKind): string[]` — List named placement references carried by this group.
1955
1255
 
1956
- ```ts
1957
- referencePoint(ref: PlacementAnchorLike): [ number, number, number ]
1958
- ```
1256
+ #### `referencePoint(ref: PlacementAnchorLike): Vec3` — Resolve a named placement reference or built-in Anchor3D to a 3D point. Named refs take priority over built-in anchors.
1959
1257
 
1960
1258
  **Other**
1961
1259
 
1962
- #### `clone()` — Return a deep-cloned ShapeGroup tree (refs copied).
1260
+ #### `clone(): ShapeGroup` — Return a deep-cloned ShapeGroup tree (refs copied).
1963
1261
 
1964
- ```ts
1965
- clone(): ShapeGroup
1966
- ```
1262
+ #### `boundingBox(): { min: Vec3; max: Vec3; }` — Return the combined 3D bounding box of all children.
1967
1263
 
1968
- #### `boundingBox()` — Return the combined 3D bounding box of all children.
1969
-
1970
- ```ts
1971
- boundingBox(): { min: [ number, number, number ]; max: [ number, number, number ]; }
1972
- ```
1973
-
1974
- #### `color()` — Return a copy of the group with the given display color applied to each child.
1975
-
1976
- ```ts
1977
- color(hex: string): ShapeGroup
1978
- ```
1264
+ #### `color(hex: string): ShapeGroup` — Return a copy of the group with the given display color applied to each child.
1979
1265
 
1980
1266
  **Compatibility Aliases**
1981
1267
 
@@ -1993,67 +1279,27 @@ color(hex: string): ShapeGroup
1993
1279
 
1994
1280
  ### `Pattern2D`
1995
1281
 
1996
- #### `add()` — Add this pattern to one or more patterns or constant height offsets.
1997
-
1998
- ```ts
1999
- add(...patterns: Pattern2DInput[]): Pattern2D
2000
- ```
2001
-
2002
- #### `subtract()` — Subtract another pattern or constant height offset from this pattern.
2003
-
2004
- ```ts
2005
- subtract(pattern: Pattern2DInput): Pattern2D
2006
- ```
2007
-
2008
- #### `multiply()` — Multiply this pattern by one or more patterns or numeric scale factors.
1282
+ #### `add(...patterns: Pattern2DInput[]): Pattern2D` — Add this pattern to one or more patterns or constant height offsets.
2009
1283
 
2010
- ```ts
2011
- multiply(...patterns: Pattern2DInput[]): Pattern2D
2012
- ```
2013
-
2014
- #### `min()` — Keep the lower height between this pattern and one or more other patterns.
2015
-
2016
- ```ts
2017
- min(...patterns: Pattern2DInput[]): Pattern2D
2018
- ```
2019
-
2020
- #### `max()` — Keep the higher height between this pattern and one or more other patterns.
1284
+ #### `subtract(pattern: Pattern2DInput): Pattern2D` — Subtract another pattern or constant height offset from this pattern.
2021
1285
 
2022
- ```ts
2023
- max(...patterns: Pattern2DInput[]): Pattern2D
2024
- ```
1286
+ #### `multiply(...patterns: Pattern2DInput[]): Pattern2D` — Multiply this pattern by one or more patterns or numeric scale factors.
2025
1287
 
2026
- #### `clamp()` — Limit pattern height to the inclusive `[min, max]` range in millimeters.
1288
+ #### `min(...patterns: Pattern2DInput[]): Pattern2D` — Keep the lower height between this pattern and one or more other patterns.
2027
1289
 
2028
- ```ts
2029
- clamp(min: number, max: number): Pattern2D
2030
- ```
1290
+ #### `max(...patterns: Pattern2DInput[]): Pattern2D` — Keep the higher height between this pattern and one or more other patterns.
2031
1291
 
2032
- #### `abs()` — Convert negative heights to positive heights.
2033
-
2034
- ```ts
2035
- abs(): Pattern2D
2036
- ```
1292
+ #### `clamp(min: number, max: number): Pattern2D` — Limit pattern height to the inclusive `[min, max]` range in millimeters.
2037
1293
 
2038
- #### `negate()` — Flip the pattern height sign.
1294
+ #### `abs(): Pattern2D` — Convert negative heights to positive heights.
2039
1295
 
2040
- ```ts
2041
- negate(): Pattern2D
2042
- ```
1296
+ #### `negate(): Pattern2D` — Flip the pattern height sign.
2043
1297
 
2044
1298
  ### `Pattern2DBuilder`
2045
1299
 
2046
- #### `constant()` — Create a constant-height pattern in millimeters.
1300
+ #### `constant(value?: number): Pattern2D` — Create a constant-height pattern in millimeters.
2047
1301
 
2048
- ```ts
2049
- constant(value?: number): Pattern2D
2050
- ```
2051
-
2052
- #### `sineWave()` — Create a sinusoidal wave pattern in UV space.
2053
-
2054
- ```ts
2055
- sineWave(options: Pattern2DSineWaveOptions): Pattern2D
2056
- ```
1302
+ #### `sineWave(options: Pattern2DSineWaveOptions): Pattern2D` — Create a sinusoidal wave pattern in UV space.
2057
1303
 
2058
1304
  **`Pattern2DSineWaveOptions`**
2059
1305
 
@@ -2065,11 +1311,7 @@ sineWave(options: Pattern2DSineWaveOptions): Pattern2D
2065
1311
  | `phase?` | `number` | Phase offset in radians. Default: 0. |
2066
1312
  | `bias?` | `number` | Constant height offset in millimeters. Default: 0. |
2067
1313
 
2068
- #### `stripes()` — Create recessed stripe bands in UV space.
2069
-
2070
- ```ts
2071
- stripes(options: Pattern2DStripesOptions): Pattern2D
2072
- ```
1314
+ #### `stripes(options: Pattern2DStripesOptions): Pattern2D` — Create recessed stripe bands in UV space.
2073
1315
 
2074
1316
  **`Pattern2DStripesOptions`**
2075
1317
 
@@ -2080,11 +1322,7 @@ stripes(options: Pattern2DStripesOptions): Pattern2D
2080
1322
  | `width` | `number` | Stripe width in surface millimeters. |
2081
1323
  | `depth?` | `number` | Stripe groove depth in millimeters. Default: 1. |
2082
1324
 
2083
- #### `overUnderWeave()` — Create an over-under woven relief pattern in UV space.
2084
-
2085
- ```ts
2086
- overUnderWeave(options: Pattern2DOverUnderWeaveOptions): Pattern2D
2087
- ```
1325
+ #### `overUnderWeave(options: Pattern2DOverUnderWeaveOptions): Pattern2D` — Create an over-under woven relief pattern in UV space.
2088
1326
 
2089
1327
  **`Pattern2DOverUnderWeaveOptions`**
2090
1328
 
@@ -2095,120 +1333,6 @@ overUnderWeave(options: Pattern2DOverUnderWeaveOptions): Pattern2D
2095
1333
  | `depth?` | `number` | Thread groove depth in millimeters. Default: 0.8. |
2096
1334
  | `underScale?` | `number` | Relative height of the under-crossing thread. Default: 0.15. |
2097
1335
 
2098
- ### `HermiteCurve3D`
2099
-
2100
- **Properties:**
2101
-
2102
- | Property | Type | Description |
2103
- |----------|------|-------------|
2104
- | `p0` | `Vec3` | Start position |
2105
- | `p1` | `Vec3` | End position |
2106
- | `t0` | `Vec3` | Scaled tangent at start (direction * weight * chordLength) |
2107
- | `t1` | `Vec3` | Scaled tangent at end (direction * weight * chordLength) |
2108
- | `chordLength` | `number` | Chord length (straight-line distance between endpoints) |
2109
-
2110
- **Methods:**
2111
-
2112
- #### `pointAt()` — Evaluate position at parameter t ∈ [0, 1]
2113
-
2114
- ```ts
2115
- pointAt(t: number): Vec3
2116
- ```
2117
-
2118
- #### `tangentAt()` — Evaluate tangent (first derivative) at parameter t ∈ [0, 1]
2119
-
2120
- ```ts
2121
- tangentAt(t: number): Vec3
2122
- ```
2123
-
2124
- #### `curvatureAt()` — Evaluate curvature vector (second derivative) at parameter t ∈ [0, 1]
2125
-
2126
- ```ts
2127
- curvatureAt(t: number): Vec3
2128
- ```
2129
-
2130
- #### `sample()` — Sample the curve as a polyline of evenly-spaced parameter values.
2131
-
2132
- ```ts
2133
- sample(count?: number): Vec3[]
2134
- ```
2135
-
2136
- #### `length()` — Approximate arc length by sampling.
2137
-
2138
- ```ts
2139
- length(samples?: number): number
2140
- ```
2141
-
2142
- #### `sampleAdaptive()` — Sample with adaptive density — more points where curvature is higher. Returns at least `minCount` points, up to `maxCount`.
2143
-
2144
- ```ts
2145
- sampleAdaptive(minCount?: number, maxCount?: number): Vec3[]
2146
- ```
2147
-
2148
- #### `toPolyline()` — Convert to a format compatible with sweep() path input.
2149
-
2150
- ```ts
2151
- toPolyline(samples?: number): Vec3[]
2152
- ```
2153
-
2154
- ### `QuinticHermiteCurve3D`
2155
-
2156
- **Properties:**
2157
-
2158
- | Property | Type | Description |
2159
- |----------|------|-------------|
2160
- | `p0` | `Vec3` | Start position |
2161
- | `p1` | `Vec3` | End position |
2162
- | `t0` | `Vec3` | Scaled tangent at start (direction * weight * chordLength) |
2163
- | `t1` | `Vec3` | Scaled tangent at end (direction * weight * chordLength) |
2164
- | `c0` | `Vec3` | Scaled second derivative at start (curvature * weight² * chordLength²) |
2165
- | `c1` | `Vec3` | Scaled second derivative at end (curvature * weight² * chordLength²) |
2166
- | `chordLength` | `number` | Chord length (straight-line distance between endpoints) |
2167
-
2168
- **Methods:**
2169
-
2170
- #### `pointAt()` — Evaluate position at parameter t ∈ [0, 1]
2171
-
2172
- ```ts
2173
- pointAt(t: number): Vec3
2174
- ```
2175
-
2176
- #### `tangentAt()` — Evaluate tangent (first derivative, normalized) at parameter t ∈ [0, 1]
2177
-
2178
- ```ts
2179
- tangentAt(t: number): Vec3
2180
- ```
2181
-
2182
- #### `curvatureAt()` — Evaluate curvature vector (second derivative) at parameter t ∈ [0, 1]
2183
-
2184
- ```ts
2185
- curvatureAt(t: number): Vec3
2186
- ```
2187
-
2188
- #### `sample()` — Sample the curve as a polyline of evenly-spaced parameter values.
2189
-
2190
- ```ts
2191
- sample(count?: number): Vec3[]
2192
- ```
2193
-
2194
- #### `length()` — Approximate arc length by sampling.
2195
-
2196
- ```ts
2197
- length(samples?: number): number
2198
- ```
2199
-
2200
- #### `sampleAdaptive()` — Sample with adaptive density — more points where curvature is higher. Returns at least `minCount` points, up to `maxCount`.
2201
-
2202
- ```ts
2203
- sampleAdaptive(minCount?: number, maxCount?: number): Vec3[]
2204
- ```
2205
-
2206
- #### `toPolyline()` — Convert to a format compatible with sweep() path input.
2207
-
2208
- ```ts
2209
- toPolyline(samples?: number): Vec3[]
2210
- ```
2211
-
2212
1336
  ### `ShapeRef`
2213
1337
 
2214
1338
  A first-class reference path over a shape's semantic faces and face relationships.
@@ -2223,107 +1347,39 @@ Created with `shape.ref("lid/back")`, then refined through methods such as `.poi
2223
1347
 
2224
1348
  **Methods:**
2225
1349
 
2226
- #### `resolve()` — Resolve this reference into its current faces, edges, or points.
2227
-
2228
- ```ts
2229
- resolve(): ShapeReferenceResolution
2230
- ```
2231
-
2232
- #### `kind()` — The resolved reference kind, such as `face`, `edge-set`, or [`point`](/docs/sketch#point).
2233
-
2234
- ```ts
2235
- get kind(): ShapeReferenceKind
2236
- ```
2237
-
2238
- #### `cardinality()` — Whether the reference currently resolves to zero, one, or many matches.
2239
-
2240
- ```ts
2241
- get cardinality(): ShapeReferenceCardinality
2242
- ```
2243
-
2244
- #### `status()` — Return the reference lifecycle status for the current shape state.
2245
-
2246
- ```ts
2247
- status(): ShapeReferenceStatus
2248
- ```
2249
-
2250
- #### `explain()` — Return a human-readable explanation of how this reference resolved.
2251
-
2252
- ```ts
2253
- explain(): string
2254
- ```
2255
-
2256
- #### `as()` — Name this derived reference so the same shape can resolve it by `shape.ref(name)`.
2257
-
2258
- ```ts
2259
- as(name: string): ShapeRef
2260
- ```
2261
-
2262
- #### `maybe()` — Return an optional reference that resolves to zero matches instead of throwing when missing.
2263
-
2264
- ```ts
2265
- maybe(): ShapeRef
2266
- ```
2267
-
2268
- #### `all()` — Mark that a multi-match reference is intentionally being used as a set.
2269
-
2270
- ```ts
2271
- all(): ShapeRef
2272
- ```
2273
-
2274
- #### `one()` — Require this reference to resolve to exactly one match.
2275
-
2276
- ```ts
2277
- one(): ShapeRef
2278
- ```
1350
+ #### `resolve(): ShapeReferenceResolution` — Resolve this reference into its current faces, edges, or points.
2279
1351
 
2280
- #### `faces()` — Resolve this reference as one or more faces.
1352
+ #### `get kind(): ShapeReferenceKind` — The resolved reference kind, such as `face`, `edge-set`, or `point`.
2281
1353
 
2282
- ```ts
2283
- faces(): FaceRef[]
2284
- ```
1354
+ #### `get cardinality(): ShapeReferenceCardinality` — Whether the reference currently resolves to zero, one, or many matches.
2285
1355
 
2286
- #### `face()` — Resolve this reference as exactly one face.
1356
+ #### `status(): ShapeReferenceStatus` — Return the reference lifecycle status for the current shape state.
2287
1357
 
2288
- ```ts
2289
- face(): FaceRef
2290
- ```
1358
+ #### `explain(): string` — Return a human-readable explanation of how this reference resolved.
2291
1359
 
2292
- #### `edges()` — Resolve this reference as one or more edges. Face references return boundary edges.
1360
+ #### `as(name: string): ShapeRef` — Name this derived reference so the same shape can resolve it by `shape.ref(name)`.
2293
1361
 
2294
- ```ts
2295
- edges(): EdgeSegment[]
2296
- ```
1362
+ #### `maybe(): ShapeRef` — Return an optional reference that resolves to zero matches instead of throwing when missing.
2297
1363
 
2298
- #### `edge()` — Resolve this reference as exactly one edge.
1364
+ #### `all(): ShapeRef` — Mark that a multi-match reference is intentionally being used as a set.
2299
1365
 
2300
- ```ts
2301
- edge(): EdgeSegment
2302
- ```
1366
+ #### `one(): ShapeRef` — Require this reference to resolve to exactly one match.
2303
1367
 
2304
- #### `points()` — Resolve this reference as one or more points. Faces use centers and edges use midpoints.
1368
+ #### `faces(): FaceRef[]` — Resolve this reference as one or more faces.
2305
1369
 
2306
- ```ts
2307
- points(): Vec3[]
2308
- ```
1370
+ #### `face(): FaceRef` — Resolve this reference as exactly one face.
2309
1371
 
2310
- #### [`point()`](/docs/sketch#point) — Resolve this reference as exactly one point.
1372
+ #### `edges(): EdgeSegment[]` — Resolve this reference as one or more edges. Face references return boundary edges.
2311
1373
 
2312
- ```ts
2313
- point(): Vec3
2314
- ```
1374
+ #### `edge(): EdgeSegment` — Resolve this reference as exactly one edge.
2315
1375
 
2316
- #### `toJSON()` — Return the structured JSON-friendly reference resolution.
1376
+ #### `points(): Vec3[]` — Resolve this reference as one or more points. Faces use centers and edges use midpoints.
2317
1377
 
2318
- ```ts
2319
- toJSON(): ShapeReferenceResolution
2320
- ```
1378
+ #### `point(): Vec3` — Resolve this reference as exactly one point.
2321
1379
 
2322
- #### `toString()` — Return a compact display form for this reference path.
1380
+ #### `toJSON(): ShapeReferenceResolution` — Return the structured JSON-friendly reference resolution.
2323
1381
 
2324
- ```ts
2325
- toString(): string
2326
- ```
1382
+ #### `toString(): string` — Return a compact display form for this reference path.
2327
1383
 
2328
1384
  ---
2329
1385
 
@@ -2333,46 +1389,7 @@ toString(): string
2333
1389
 
2334
1390
  ### `verify`
2335
1391
 
2336
- - `that(label: string, check: () => boolean, message?: string): void` Custom predicate check.
2337
- - `equal(label: string, actual: number, expected: number, tolerance?: number, message?: string): void` — Check that two numbers are approximately equal (within tolerance).
2338
- - `notEqual(label: string, actual: number, unexpected: number, tolerance?: number, message?: string): void` — Check that two numbers are NOT equal (differ by more than tolerance).
2339
- - `greaterThan(label: string, actual: number, min: number, message?: string): void` — Check that actual > min.
2340
- - `lessThan(label: string, actual: number, max: number, message?: string): void` — Check that actual < max.
2341
- - `inRange(label: string, actual: number, min: number, max: number, message?: string): void` — Check that min <= actual <= max.
2342
- - `centersCoincide(label: string, a: ShapeLike, b: ShapeLike, tolerance?: number): void` — Check that the bounding-box centers of two shapes coincide within tolerance (mm).
2343
- - `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); ```
2344
- - `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); ```
2345
- - `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"); ```
2346
- - `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.
2347
- - `minClearance(label: string, a: ShapeLike, b: ShapeLike, minGap: number, searchLength?: number): void` — Check that a minimum clearance gap exists between two shapes.
2348
- - `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); ```
2349
- - `parallel(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void` — Check that two face normals are parallel (within toleranceDeg degrees).
2350
- - `perpendicular(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void` — Check that two face normals are perpendicular (within toleranceDeg degrees).
2351
- - `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.
2352
- - `faceAt(label: string, face: FaceRefLike, expectedPos: [ number, number, number ], toleranceMm?: number): void` — Check that a face center lies at a specific position (within toleranceMm).
2353
- - `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.
2354
- - `isEmpty(label: string, shape: ShapeLike, message?: string): void` — Check that a shape is empty.
2355
- - `notEmpty(label: string, shape: ShapeLike, message?: string): void` — Check that a shape is NOT empty.
2356
- - `volumeApprox(label: string, shape: ShapeLike, expected: number, tolerance?: number): void` — Check that a shape's volume is approximately equal to expected (mm³).
2357
- - `areaApprox(label: string, shape: ShapeLike, expected: number, tolerance?: number): void` — Check that a shape's surface area is approximately equal to expected (mm²).
2358
- - `boundingBoxSize(label: string, shape: ShapeLike, expectedSize: [ number, number, number ], tolerance?: number): void` — Check that a shape's bounding box has approximately the given size.
2359
- - `edgeContinuity(label: string, shape: ShapeLike, options?: EdgeContinuityThresholds): void` — Check that every sampled seam on a shape meets a requested continuity threshold.
2360
- - `noTinyEdges(label: string, shape: ShapeLike, threshold?: number): void` — Check that a shape has no tiny edges below the requested threshold.
2361
- - `noSliverFaces(label: string, shape: ShapeLike, threshold?: number): void` — Check that a shape has no sliver faces below the requested score threshold.
2362
- - `noSelfIntersection(label: string, shape: ShapeLike): void` — Best-effort exact-shape validity guard for self-intersections or broken B-Rep topology.
2363
-
2364
- ### `Constraint`
2365
-
2366
- - `makeParallel(builder: ConstrainedSketchBuilder, a: LineArg, b: LineArg): ConstrainedSketchBuilder` — Constrain two lines to be parallel.
2367
- - `enforceAngle(builder: ConstrainedSketchBuilder, a: LineArg, b: LineArg, angleDeg: number): ConstrainedSketchBuilder` — Constrain the signed angle from line `a` to line `b`.
2368
- - `horizontal(builder: ConstrainedSketchBuilder, line: LineArg): ConstrainedSketchBuilder` — Constrain a line to be horizontal.
2369
- - `vertical(builder: ConstrainedSketchBuilder, line: LineArg): ConstrainedSketchBuilder` — Constrain a line to be vertical.
2370
- - `equalLength(builder: ConstrainedSketchBuilder, a: LineArg, b: LineArg): ConstrainedSketchBuilder` — Constrain two lines to have equal length.
2371
- - `distance(builder: ConstrainedSketchBuilder, a: PointArg, b: PointArg, value: number): ConstrainedSketchBuilder` — Constrain the distance between two points.
2372
- - `fix(builder: ConstrainedSketchBuilder, pt: PointArg, x: number, y: number): ConstrainedSketchBuilder` — Fix a point at a specific coordinate.
2373
- - `coincident(builder: ConstrainedSketchBuilder, a: PointArg, b: PointArg): ConstrainedSketchBuilder` — Constrain two points to occupy the same location.
2374
- - `perpendicular(builder: ConstrainedSketchBuilder, a: LineArg, b: LineArg): ConstrainedSketchBuilder` — Constrain two lines to be perpendicular.
2375
- - `length(builder: ConstrainedSketchBuilder, line: LineArg, value: number): ConstrainedSketchBuilder` — Constrain the length of a line.
1392
+ Members (full entries under [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`.
2376
1393
 
2377
1394
  ### `Points`
2378
1395
 
@@ -2381,7 +1398,7 @@ toString(): string
2381
1398
  - `lerp(a: Vec3, b: Vec3, t: number): Vec3` — Linearly interpolate between two 3D points. t=0 returns a, t=1 returns b.
2382
1399
  - `direction(a: Vec3, b: Vec3): Vec3` — Unit direction vector from a to b. Throws if a and b are the same point.
2383
1400
  - `offset(point: Vec3, dir: Vec3, amount: number): Vec3` — Move a point along a direction vector by a given amount.
2384
- - `polar(length: number, angleDeg: number, from?: [ number, number ]): [ number, number ]` — Compute a 2D point at distance and angle (degrees) from an optional origin.
1401
+ - `polar(length: number, angleDeg: number, from?: Vec2): Vec2` — Compute a 2D point at distance and angle (degrees) from an optional origin.
2385
1402
 
2386
1403
  ### `connector`
2387
1404
 
@@ -2389,6 +1406,22 @@ Connector factory. Create attachment points: `connector({...})`, `connector.male
2389
1406
 
2390
1407
  ### `Import`
2391
1408
 
2392
- Namespaced file import helpers for formats that should not add new lowercase globals.
1409
+ Namespaced file-format import helpers — the single vocabulary for bringing external geometry files into a model.
2393
1410
 
2394
1411
  - `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.
1412
+ - `svgSketch(fileName: string, options?: SvgImportOptions): Sketch` — Parse an SVG file and return it as a Sketch with options for region filtering, scaling, and simplification.
1413
+ - `mesh(fileName: string, options?: MeshImportOptions): Shape | ShapeGroup` — Import an external mesh file (STL, OBJ, 3MF).
1414
+
1415
+ By default, 3MF build items are flattened into one Shape for compatibility. Use `separateObjects: true` to import 3MF build items/resource objects as a named ShapeGroup whose children are targetable by `forgecad ls`. Use `object` to import one item by the stable ref/name reported by `forgecad run`.
1416
+
1417
+ For 3MF sources, `forgecad run` prints a source-structure table with one line per build item: `[3mf:build:NNN:object:N] name type=... verts=... tris=... bbox=[min] → [max]`. Build items are numbered from `001`; files with no build items list resource objects as `3mf:object:N` instead. Per-item bboxes reveal multi-part structure — account for every substantial item before flattening. Pass any listed stable ref or name as `object` to import that item alone.
1418
+
1419
+ Use `sourceFrame: { up: "+Y" }` when the file was authored in a non-Z-up coordinate system. ForgeCAD remains Z-up; the import is rotated so the named source axis becomes ForgeCAD +Z. Supported values: `"+X"`, `"-X"`, `"+Y"`, `"-Y"`, `"+Z"`, `"-Z"`.
1420
+
1421
+ ```js
1422
+ const all = Import.mesh("./assembly.3mf", { separateObjects: true });
1423
+ const pin = all.child("Pin #001");
1424
+ const plate = Import.mesh("./assembly.3mf", { object: "3mf:build:001:object:7" });
1425
+ const yUpPart = Import.mesh("./part.obj", { sourceFrame: { up: "+Y" } });
1426
+ ```
1427
+ - `step(fileName: string, options?: StepImportOptions): Shape` — 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. Use `sourceFrame: { up: "+Y" }` to rotate Y-up source files into ForgeCAD's Z-up world.