forgecad 0.9.16 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/dist/assets/{AdminPage-CXvls4-J.js → AdminPage-DwYHz72L.js} +1 -1
  2. package/dist/assets/{BenchmarkPage-B27zk8xL.js → BenchmarkPage-a9_f-1US.js} +1 -1
  3. package/dist/assets/{BlogPage-CMAVvgQL.js → BlogPage-DodHpvmf.js} +1 -1
  4. package/dist/assets/{DocsPage-knf4I4h7.js → DocsPage-B5LePEuj.js} +8 -858
  5. package/dist/assets/EditorApp-QXsAISLR.js +16307 -0
  6. package/dist/assets/{EmbedViewer-D7ZGlFjx.js → EmbedViewer-DdEHGUMU.js} +2 -2
  7. package/dist/assets/{LandingPageProofDriven-CnevhTE8.js → LandingPageProofDriven-yhhOodbf.js} +1 -1
  8. package/dist/assets/{LegalPage-BPTUmqeg.js → LegalPage-5RbKRGYK.js} +1 -1
  9. package/dist/assets/{PricingPage-B0D4goG_.js → PricingPage-E3Rma7aV.js} +1 -1
  10. package/dist/assets/{SettingsPage-CFF-UgjI.js → SettingsPage-BJZcM97j.js} +1 -1
  11. package/dist/assets/{app-T0pDcSX4.js → app-DSYrDg0V.js} +733 -205
  12. package/dist/assets/cli/{render-C5pcIISc.js → render-ZMHR9HkV.js} +19 -46
  13. package/dist/assets/{constructionHistoryWorker-Ba2Hm58b.js → constructionHistoryWorker-AwMMWSxg.js} +1103 -349
  14. package/dist/assets/{evalWorker-vkx310U2.js → evalWorker-DbNs7Dkp.js} +3798 -1622
  15. package/dist/assets/{inspectWorker-BuTJDVX6.js → inspectWorker-CZsCFtQT.js} +1163 -409
  16. package/dist/assets/{jointPose-B_Cgedn9.js → jointPose-DO6mnXn_.js} +1 -1
  17. package/dist/assets/{manifold-BWgsjmAM.js → manifold-BGlQBBH9.js} +1 -1
  18. package/dist/assets/{manifold-rZexZI0G.js → manifold-BU-tJwQh.js} +1 -1
  19. package/dist/assets/{manifold-D6IFSkhH.js → manifold-fy2MV7K1.js} +2 -2
  20. package/dist/assets/{reportWorker-0AGij1Ru.js → reportWorker-DO6hcQbh.js} +7155 -2437
  21. package/dist/assets/{scalar-sampling-budget-J5cuzxT1.js → scalar-sampling-budget-o90NSNmF.js} +3940 -1742
  22. package/dist/assets/{scanProxyWorker-Vl4Wxa1y.js → scanProxyWorker-2GtDLk-R.js} +1 -1
  23. package/dist/assets/{javascript-1kQXfVaz.js → typescript-DBQ6RN5l.js} +874 -22
  24. package/dist/cli/render.html +1 -1
  25. package/dist/docs/index.html +3 -3
  26. package/dist/docs-raw/AI/usage.md +1 -1
  27. package/dist/docs-raw/CLI.md +63 -241
  28. package/dist/docs-raw/README.md +6 -0
  29. package/dist/docs-raw/component-model.md +17 -150
  30. package/dist/docs-raw/generated/assembly.md +139 -598
  31. package/dist/docs-raw/generated/concepts.md +245 -3501
  32. package/dist/docs-raw/generated/core.md +277 -1251
  33. package/dist/docs-raw/generated/curves.md +387 -1608
  34. package/dist/docs-raw/generated/legacy.md +162 -0
  35. package/dist/docs-raw/generated/lib.md +227 -85
  36. package/dist/docs-raw/generated/output.md +38 -73
  37. package/dist/docs-raw/generated/runtime-names.md +23 -23
  38. package/dist/docs-raw/generated/sdf.md +68 -284
  39. package/dist/docs-raw/generated/sheet-metal.md +68 -335
  40. package/dist/docs-raw/generated/sketch.md +240 -1161
  41. package/dist/docs-raw/generated/viewport.md +75 -316
  42. package/dist/docs-raw/generated/wood.md +21 -49
  43. package/dist/docs-raw/guides/coordinate-system.md +4 -42
  44. package/dist/docs-raw/guides/inspection-bundles.md +44 -442
  45. package/dist/docs-raw/guides/joint-design.md +18 -79
  46. package/dist/docs-raw/guides/positioning.md +21 -143
  47. package/dist/docs-raw/guides/scene-presentation.md +89 -0
  48. package/dist/docs-raw/skills/forgecad-3d-reconstruction.md +25 -111
  49. package/dist/docs-raw/skills/forgecad-blockout-model.md +20 -117
  50. package/dist/docs-raw/skills/forgecad-component-model.md +23 -107
  51. package/dist/docs-raw/skills/forgecad-high-level-spec.md +47 -155
  52. package/dist/docs-raw/skills/forgecad-image-replicator.md +26 -143
  53. package/dist/docs-raw/skills/forgecad-lld.md +19 -113
  54. package/dist/docs-raw/skills/forgecad-make-a-model.md +112 -532
  55. package/dist/docs-raw/skills/forgecad-model-grader.md +38 -108
  56. package/dist/docs-raw/skills/forgecad-prepare-prompt.md +24 -211
  57. package/dist/docs-raw/skills/forgecad-project.md +13 -131
  58. package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +42 -134
  59. package/dist/docs-raw/skills/forgecad-render-inspect.md +27 -174
  60. package/dist/docs-raw/skills/forgecad-visual-spec.md +32 -112
  61. package/dist/docs-raw/skills/forgecad.md +19 -18
  62. package/dist/docs-raw/skills/index.md +2 -0
  63. package/dist/docs-raw/welcome.md +2 -2
  64. package/dist/index.html +1 -1
  65. package/dist/llms.txt +1 -2
  66. package/dist/sitemap.xml +13 -13
  67. package/dist-cli/{check-compiler-SYQ2PWOB.js → check-compiler-JTVBITCR.js} +1 -1
  68. package/dist-cli/{check-query-propagation-HIAGV62W.js → check-query-propagation-3FFLSMVN.js} +1 -1
  69. package/dist-cli/{chunk-SPZE3DUY.js → chunk-OAN5T4XD.js} +4412 -2212
  70. package/dist-cli/forgecad.js +507 -179
  71. package/dist-skill/CONTEXT.md +2172 -8377
  72. package/dist-skill/SKILL.md +15 -15
  73. package/dist-skill/docs/API/core/concepts.md +27 -157
  74. package/dist-skill/docs/CLI.md +63 -241
  75. package/dist-skill/docs/generated/assembly.md +138 -549
  76. package/dist-skill/docs/generated/core.md +277 -1251
  77. package/dist-skill/docs/generated/curves.md +387 -1609
  78. package/dist-skill/docs/generated/lib.md +227 -85
  79. package/dist-skill/docs/generated/output.md +38 -73
  80. package/dist-skill/docs/generated/runtime-names.md +16 -21
  81. package/dist-skill/docs/generated/sdf.md +68 -284
  82. package/dist-skill/docs/generated/sheet-metal.md +68 -335
  83. package/dist-skill/docs/generated/sketch.md +240 -1160
  84. package/dist-skill/docs/generated/viewport.md +75 -223
  85. package/dist-skill/docs/generated/wood.md +21 -49
  86. package/dist-skill/docs/guides/coordinate-system.md +4 -42
  87. package/dist-skill/docs/guides/inspection-bundles.md +44 -442
  88. package/dist-skill/docs/guides/joint-design.md +18 -79
  89. package/dist-skill/docs/guides/positioning.md +21 -143
  90. package/dist-skill/docs/guides/scene-presentation.md +89 -0
  91. package/dist-skill/docs/guides/surface-members.md +26 -0
  92. package/dist-skill/library/forgecad-3d-reconstruction/SKILL.md +23 -111
  93. package/dist-skill/library/forgecad-blockout-model/SKILL.md +18 -117
  94. package/dist-skill/library/forgecad-component-model/SKILL.md +21 -107
  95. package/dist-skill/library/forgecad-high-level-spec/SKILL.md +45 -155
  96. package/dist-skill/library/forgecad-image-replicator/SKILL.md +24 -143
  97. package/dist-skill/library/forgecad-lld/SKILL.md +17 -113
  98. package/dist-skill/library/forgecad-make-a-model/SKILL.md +110 -532
  99. package/dist-skill/library/forgecad-model-grader/SKILL.md +36 -108
  100. package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +35 -224
  101. package/dist-skill/library/forgecad-prepare-prompt/references/default-profiles.md +43 -271
  102. package/dist-skill/library/forgecad-prepare-prompt/references/master-prompt.md +30 -99
  103. package/dist-skill/library/forgecad-project/SKILL.md +13 -133
  104. package/dist-skill/library/forgecad-reconstruction-benchmark/SKILL.md +29 -123
  105. package/dist-skill/library/forgecad-render-inspect/SKILL.md +25 -174
  106. package/dist-skill/library/forgecad-visual-spec/SKILL.md +30 -111
  107. package/dist-skill/website/skills/forgecad-3d-reconstruction.md +58 -0
  108. package/dist-skill/website/skills/forgecad-blockout-model.md +49 -0
  109. package/dist-skill/website/skills/forgecad-component-model.md +53 -0
  110. package/dist-skill/website/skills/forgecad-high-level-spec.md +101 -0
  111. package/dist-skill/website/skills/forgecad-image-replicator.md +63 -0
  112. package/dist-skill/website/skills/forgecad-lld.md +41 -0
  113. package/dist-skill/website/skills/forgecad-make-a-model.md +186 -0
  114. package/dist-skill/website/skills/forgecad-model-grader.md +82 -0
  115. package/dist-skill/website/skills/forgecad-prepare-prompt.md +63 -0
  116. package/dist-skill/website/skills/forgecad-project.md +26 -0
  117. package/dist-skill/website/skills/forgecad-reconstruction-benchmark.md +60 -0
  118. package/dist-skill/website/skills/forgecad-render-inspect.md +80 -0
  119. package/dist-skill/website/skills/forgecad-visual-spec.md +71 -0
  120. package/dist-skill/website/skills/forgecad.md +122 -0
  121. package/dist-skill/website/skills/index.md +26 -0
  122. package/examples/api/comparison-imported-sphere-candidate.forge.js +1 -1
  123. package/examples/api/conformal-product-ribbon.forge.js +1 -1
  124. package/examples/api/exact-sheet-shell-assembly.forge.js +1 -1
  125. package/examples/api/extrude-options.forge.js +4 -2
  126. package/examples/api/field-loft-drive-tip.forge.js +40 -0
  127. package/examples/api/guided-loft-olive-oil-bottle.forge.js +1 -1
  128. package/examples/api/highlight-debug.forge.js +10 -10
  129. package/examples/api/mesh-import-slats.forge.js +1 -1
  130. package/examples/api/real-product-curves.forge.js +1 -1
  131. package/examples/api/sculpt-box-circle-booleans.forge.js +1 -1
  132. package/examples/api/sdf-shapes.forge.js +2 -5
  133. package/examples/api/sketch-rounding-strategies.forge.js +6 -6
  134. package/examples/api/surface-member-bottle-cage.forge.js +3 -3
  135. package/examples/api/surface-member-conformal-product-ribbon.forge.js +3 -3
  136. package/examples/api/surface-member-razor-inlay.forge.js +1 -1
  137. package/examples/api/variable-sweep-test.forge.js +3 -3
  138. package/examples/mechanical/airplane-propeller.forge.js +74 -39
  139. package/examples/nurbs-surface.forge.js +1 -1
  140. package/examples/products/iphone.forge.js +1 -1
  141. package/package.json +1 -1
  142. package/dist/assets/EditorApp-BHMQlJ-D.js +0 -14686
  143. package/dist/docs-raw/guides/geometry-conventions.md +0 -52
  144. package/dist/docs-raw/guides/modeling-recipes.md +0 -78
  145. package/dist-skill/docs/guides/geometry-conventions.md +0 -52
  146. package/dist-skill/docs/guides/modeling-recipes.md +0 -78
  147. 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,6 +107,7 @@ 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
 
@@ -148,19 +121,19 @@ fillet(myShape, 1.5, { atZ: 20, convex: true })
148
121
  // Fillet vertical edges selected beforehand
149
122
  const edges = selectEdges(myShape, { parallel: [0, 0, 1] })
150
123
  fillet(myShape, 3, edges)
151
- ```
152
124
 
153
- ```ts
154
- fillet(shape: Shape, radius: number, edges?: EdgeSelector, segments?: number): Shape
125
+ // Exact compiler-owned fillet on a tracked box edge
126
+ const base = box(50, 50, 20)
127
+ fillet(base, 5, base.edge('vert-br'))
155
128
  ```
156
129
 
157
- #### `chamfer()` — Apply experimental chamfers (beveled edges) to one or more edges of a shape.
130
+ #### `chamfer(shape: Shape, size: number, edges?: EdgeSelector): Shape` — Apply experimental chamfers (beveled edges) to one or more edges of a shape.
158
131
 
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.
132
+ **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
133
 
161
134
  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
135
 
163
- The `edges` parameter accepts the same options as `fillet()`: inline `EdgeQuery`, pre-selected `EdgeSegment`/`EdgeSegment[]`, or `undefined` (all sharp edges).
136
+ 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
137
 
165
138
  ```ts
166
139
  // Chamfer all edges
@@ -168,13 +141,13 @@ chamfer(myShape, 1)
168
141
 
169
142
  // Chamfer only vertical edges
170
143
  chamfer(myShape, 2, { parallel: [0, 0, 1] })
171
- ```
172
144
 
173
- ```ts
174
- chamfer(shape: Shape, size: number, edges?: EdgeSelector): Shape
145
+ // Exact compiler-owned chamfer on a tracked box edge
146
+ const base = box(50, 50, 20)
147
+ chamfer(base, 3, base.edge('vert-br'))
175
148
  ```
176
149
 
177
- #### `draft()` — Apply a draft angle (taper) to vertical faces for mold extraction.
150
+ #### `draft(shape: Shape, angleDeg: number, pullDirection?: Vec3, neutralPlaneOffset?: number): Shape` — Apply a draft angle (taper) to vertical faces for mold extraction.
178
151
 
179
152
  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
153
 
@@ -188,11 +161,7 @@ draft(myBox, 3)
188
161
  draft(myShape, 2, [0, 0, 1], 10)
189
162
  ```
190
163
 
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.
164
+ #### `offsetSolid(shape: Shape, thickness: number): Shape` — Uniformly offset all surfaces of a solid inward or outward.
196
165
 
197
166
  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
167
 
@@ -206,13 +175,9 @@ offsetSolid(myBox, 1)
206
175
  offsetSolid(myShape, -0.5)
207
176
  ```
208
177
 
209
- ```ts
210
- offsetSolid(shape: Shape, thickness: number): Shape
211
- ```
212
-
213
178
  ### Patterns & Layout
214
179
 
215
- #### `circularLayout()` — Compute evenly-spaced positions around a circle.
180
+ #### `circularLayout(count: number, radius: number, options?: CircularLayoutOptions): LayoutPoint[]` — Compute evenly-spaced positions around a circle.
216
181
 
217
182
  Eliminates the most common trig pattern in CAD scripts:
218
183
 
@@ -229,10 +194,6 @@ for (const {x, y} of circularLayout(12, r)) {
229
194
  }
230
195
  ```
231
196
 
232
- ```ts
233
- circularLayout(count: number, radius: number, options?: CircularLayoutOptions): LayoutPoint[]
234
- ```
235
-
236
197
  **`CircularLayoutOptions`**
237
198
  - `startDeg?: number` — Angle of the first element in degrees (default: 0 = +X axis).
238
199
  - `centerX?: number` — Center X coordinate (default: 0).
@@ -240,7 +201,7 @@ circularLayout(count: number, radius: number, options?: CircularLayoutOptions):
240
201
 
241
202
  `LayoutPoint`: `{ x: number, y: number }`
242
203
 
243
- #### `polygonVertices()` — Compute the vertex positions of a regular polygon.
204
+ #### `polygonVertices(sides: number, radius: number, options?: PolygonVerticesOptions): LayoutPoint[]` — Compute the vertex positions of a regular polygon.
244
205
 
245
206
  Default orientation places the first vertex at the top (90 degrees), matching the convention used by [`ngon()`](/docs/sketch#ngon).
246
207
 
@@ -256,16 +217,12 @@ const v3 = [center.x + r, center.y];
256
217
  const [v1, v2, v3] = polygonVertices(3, r);
257
218
  ```
258
219
 
259
- ```ts
260
- polygonVertices(sides: number, radius: number, options?: PolygonVerticesOptions): LayoutPoint[]
261
- ```
262
-
263
220
  **`PolygonVerticesOptions`**
264
221
  - `startDeg?: number` — Angle of the first vertex in degrees (default: 90 = top).
265
222
  - `centerX?: number` — Center X coordinate (default: 0).
266
223
  - `centerY?: number` — Center Y coordinate (default: 0).
267
224
 
268
- #### `linearPattern()` — Repeat a shape in a linear pattern along a direction vector and union the copies.
225
+ #### `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
226
 
270
227
  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
228
 
@@ -274,11 +231,7 @@ Creates `count` copies of `shape`, each offset by `(dx*i, dy*i, dz*i)` from the
274
231
  linearPattern(cylinder(10, 3), 5, 20, 0)
275
232
  ```
276
233
 
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.
234
+ #### `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
235
 
283
236
  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
237
 
@@ -295,32 +248,20 @@ circularPattern(cylinder(12, 4).translate(30, 0, -1), 8)
295
248
  circularPattern(myFeature, 4, { axis: [1, 0, 0], origin: [0, 0, 50] })
296
249
  ```
297
250
 
298
- ```ts
299
- circularPattern(shape: Shape, count: number, centerXOrOpts?: number | CircularPatternOptions, centerY?: number): Shape
300
- ```
301
-
302
251
  **`CircularPatternOptions`**
303
252
 
304
253
  | Option | Type | Description |
305
254
  |--------|------|-------------|
306
255
  | `centerX?` | `number` | Center X of the rotation (default: 0). Used when the rotation axis is Z. |
307
256
  | `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.
312
-
313
- ```ts
314
- linearPattern2d(sketch: Sketch, count: number, dx: number, dy?: number): Sketch
315
- ```
257
+ | `axis?` | `Vec3` | Rotation axis direction (default: [0, 0, 1] = Z axis). |
258
+ | `origin?` | `Vec3` | Pivot point for the rotation (default: [0, 0, 0]). Overrides centerX/centerY when set. |
316
259
 
317
- #### `circularPattern2d()` — Repeat a 2D sketch in a circular pattern around a center point and union the copies.
260
+ #### `linearPattern2d(sketch: Sketch, count: number, dx: number, dy?: number): Sketch` — Repeat a 2D sketch in a linear pattern and union the copies.
318
261
 
319
- ```ts
320
- circularPattern2d(sketch: Sketch, count: number, centerXOrOpts?: number | { centerX?: number; centerY?: number; startDeg?: number; }, centerY?: number): Sketch
321
- ```
262
+ #### `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
263
 
323
- #### `mirrorCopy()` — Mirror a shape across a plane and union the mirror with the original.
264
+ #### `mirrorCopy(shape: Shape, normal: Vec3): Shape` — Mirror a shape across a plane and union the mirror with the original.
324
265
 
325
266
  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
267
 
@@ -329,11 +270,7 @@ The mirror plane passes through the origin and is defined by its normal vector.
329
270
  mirrorCopy(box(50, 30, 10), [1, 0, 0])
330
271
  ```
331
272
 
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.
273
+ #### `selectEdges(shape: Shape, query?: EdgeQuery): EdgeSegment[]` — Select all edges from a shape that match the given query.
337
274
 
338
275
  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
276
 
@@ -348,10 +285,6 @@ for (const edge of coalesceEdges(topEdges)) {
348
285
  }
349
286
  ```
350
287
 
351
- ```ts
352
- selectEdges(shape: Shape, query?: EdgeQuery): EdgeSegment[]
353
- ```
354
-
355
288
  **`EdgeQuery`**
356
289
 
357
290
  | Option | Type | Description |
@@ -383,9 +316,10 @@ selectEdges(shape: Shape, query?: EdgeQuery): EdgeSegment[]
383
316
  | `normalA` | `Vec3` | Normal of first adjacent face. |
384
317
  | `normalB` | `Vec3` | Normal of second adjacent face (same as normalA for boundary edges). |
385
318
  | `boundary` | `boolean` | true if this is a boundary (unmatched) edge — unusual for closed solids. |
386
- | `start`, `end`, `midpoint`, `length` | | — |
387
319
 
388
- #### `selectEdge()` Select the single best-matching edge from a shape.
320
+ Also: `start: Vec3`, `end: Vec3`, `midpoint: Vec3`, `length: number`.
321
+
322
+ #### `selectEdge(shape: Shape, query?: EdgeQuery): EdgeSegment` — Select the single best-matching edge from a shape.
389
323
 
390
324
  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
325
 
@@ -395,11 +329,7 @@ const bottomEdge = selectEdge(part, { near: [25, 0, 0], atZ: 0 });
395
329
  result = chamfer(result, 1.5, bottomEdge);
396
330
  ```
397
331
 
398
- ```ts
399
- selectEdge(shape: Shape, query?: EdgeQuery): EdgeSegment
400
- ```
401
-
402
- #### `coalesceEdges()` — Merge collinear edge segments into longer logical edges.
332
+ #### `coalesceEdges(segments: EdgeSegment[], tolerance?: number): EdgeSegment[]` — Merge collinear edge segments into longer logical edges.
403
333
 
404
334
  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
335
 
@@ -412,16 +342,14 @@ for (const edge of coalesceEdges(topEdges)) {
412
342
  }
413
343
  ```
414
344
 
415
- ```ts
416
- coalesceEdges(segments: EdgeSegment[], tolerance?: number): EdgeSegment[]
417
- ```
418
-
419
345
  ### Imports & Composition
420
346
 
421
- #### `require()` — Import a module with optional ForgeCAD parameter overrides. Returns the module's exports.
347
+ #### `require(path: string, paramOverrides?: Record<string, number | string>): any` — Import a module with optional ForgeCAD parameter overrides. Returns the module's exports.
422
348
 
423
349
  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
350
 
351
+ **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.
352
+
425
353
  **Assembly return contract**
426
354
 
427
355
  | `.forge.js` return value | `require()` result |
@@ -480,57 +408,20 @@ const wheel = profiles.make.wheelProfile().extrude(8);
480
408
 
481
409
  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
410
 
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`.
411
+ **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
412
 
515
413
  ```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
414
+ // part.forge.js
415
+ function bracket() { ... }
416
+ if (require.main === module) {
417
+ return { preview: [{ name: 'Bracket', shape: bracket() }] }; // direct run: render it
418
+ }
419
+ return { make: { bracket } }; // imported: builders only
529
420
  ```
530
421
 
531
422
  ### Parameters
532
423
 
533
- #### `Param.number()` — Declare a numeric parameter that renders as a slider in the UI.
424
+ #### `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
425
 
535
426
  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
427
 
@@ -560,11 +451,7 @@ const bracket = require("./bracket.forge.js", { Width: 80 });
560
451
 
561
452
  Also available as the shorthand alias `param()`.
562
453
 
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.
454
+ #### `Param.string(name: string, defaultValue: string, opts?: { maxLength?: number; }): string` — Declare a string parameter that renders as a text input in the UI.
568
455
 
569
456
  String parameters let users type free-form text — labels, names, inscriptions, file paths, etc. The `name` string is the override key.
570
457
 
@@ -581,11 +468,7 @@ const tag = require("./tag.forge.js", { Label: "Custom Text" });
581
468
 
582
469
  Only available as `Param.string()` — no standalone alias.
583
470
 
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.
471
+ #### `Param.bool(name: string, defaultValue: boolean): boolean` — Declare a boolean parameter that renders as a checkbox in the UI.
589
472
 
590
473
  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
474
 
@@ -601,13 +484,7 @@ Override via import:
601
484
  const pan = require("./pan.forge.js", { "Show Lid": 0 });
602
485
  ```
603
486
 
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.
487
+ #### `Param.choice(name: string, defaultValue: string, choices: string[]): string` — Declare a choice parameter that renders as a dropdown in the UI.
611
488
 
612
489
  `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
490
 
@@ -630,13 +507,7 @@ Override via CLI:
630
507
  forgecad run model.forge.js --param "Pan Style=wok"
631
508
  ```
632
509
 
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.
510
+ #### `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
511
 
641
512
  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
513
 
@@ -646,15 +517,11 @@ Field types:
646
517
  - Choice fields (`choices: [...]` in field defs) return as `string`
647
518
  - All other fields return as `number`
648
519
 
649
- ```ts
650
- Param.list<T extends Record<string, number | boolean | string>>(name: string, defaultItems: T[], opts: { ... }): T[]
651
- ```
652
-
653
520
  `ListParamFieldDef`: `{ min?: number, max?: number, step?: number, unit?: string, integer?: boolean, boolean?: boolean, choices?: string[] }`
654
521
 
655
522
  ### Grouping & Local Coordinates
656
523
 
657
- #### `group()` — Group multiple shapes/sketches for joint transforms without merging into a single mesh.
524
+ #### `group(...items: GroupInput[]): ShapeGroup` — Group multiple shapes/sketches for joint transforms without merging into a single mesh.
658
525
 
659
526
  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
527
 
@@ -673,87 +540,35 @@ const indoorUnit = group(
673
540
  ).translate(0, -18, 70);
674
541
  ```
675
542
 
676
- ```ts
677
- group(...items: GroupInput[]): ShapeGroup
678
- ```
679
-
680
543
  ### Section & Projection
681
544
 
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
- ```
545
+ #### `intersectWithPlane(shape: Shape, plane: PlaneSpec): Sketch` — Cross-section: slice a 3D shape with a plane and return the intersection as a 2D Sketch.
687
546
 
688
- #### `faceProfile()` — Extract the boundary profile of a named face as a 2D sketch.
547
+ #### `faceProfile(shape: Shape, face: FaceSelector): Sketch` — Extract the boundary profile of a named face as a 2D sketch.
689
548
 
690
549
  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
550
 
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
- ```
551
+ #### `projectToPlane(shape: Shape, plane: PlaneSpec): Sketch` — Orthographically project a 3D shape onto a plane and return the silhouette as a 2D Sketch.
709
552
 
710
553
  ### Verification
711
554
 
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
- ```
555
+ #### `verify.that(label: string, check: () => boolean, message?: string): void` — Custom predicate check.
735
556
 
736
- #### `verify.lessThan()` — Check that actual < max.
557
+ #### `verify.equal(label: string, actual: number, expected: number, tolerance?: number, message?: string): void` — Check that two numbers are approximately equal (within tolerance).
737
558
 
738
- ```ts
739
- verify.lessThan(label: string, actual: number, max: number, message?: string): void
740
- ```
559
+ #### `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
560
 
742
- #### `verify.inRange()` — Check that min <= actual <= max.
561
+ #### `verify.greaterThan(label: string, actual: number, min: number, message?: string): void` — Check that actual > min.
743
562
 
744
- ```ts
745
- verify.inRange(label: string, actual: number, min: number, max: number, message?: string): void
746
- ```
563
+ #### `verify.lessThan(label: string, actual: number, max: number, message?: string): void` — Check that actual < max.
747
564
 
748
- #### `verify.centersCoincide()` — Check that the bounding-box centers of two shapes coincide within tolerance (mm).
565
+ #### `verify.inRange(label: string, actual: number, min: number, max: number, message?: string): void` — Check that min <= actual <= max.
749
566
 
750
- ```ts
751
- verify.centersCoincide(label: string, a: ShapeLike, b: ShapeLike, tolerance?: number): void
752
- ```
567
+ #### `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
568
 
754
569
  `ShapeLike`: `{ min: number[], max: number[] }`
755
570
 
756
- #### `verify.connectorDistance()` — Check the distance between two named connectors on a shape or group.
571
+ #### `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
572
 
758
573
  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
574
 
@@ -761,11 +576,7 @@ Use this when connectors + `matchTo()` define a static assembly interface. It pr
761
576
  verify.connectorDistance("leg is seated", bench, "Rail.leg_0", "Leg0.head", 0, 0.01);
762
577
  ```
763
578
 
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.
579
+ #### `verify.physicalComponentCount(label: string, expected: number): void` — Declare the expected physical connectivity component count for the returned visible model.
769
580
 
770
581
  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
582
 
@@ -775,11 +586,7 @@ This catches the common generated-CAD failure where a script returns a visually
775
586
  verify.physicalComponentCount("vise is one connected installed assembly", 1);
776
587
  ```
777
588
 
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.
589
+ #### `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
590
 
784
591
  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
592
 
@@ -789,25 +596,13 @@ Use this only for overlaps that a mechanical reviewer would accept as actual mat
789
596
  verify.intentionalOverlap("rubber grip is overmolded on handle", rubberGrip, handleCore, "overmolded insert");
790
597
  ```
791
598
 
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.
599
+ #### `verify.notColliding(label: string, a: ShapeLike, b: ShapeLike, searchLength?: number): void` — Check that two shapes do not share positive volume.
797
600
 
798
601
  Face-to-face contact is allowed; use `verify.minClearance()` when an actual running gap is required.
799
602
 
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.
805
-
806
- ```ts
807
- verify.minClearance(label: string, a: ShapeLike, b: ShapeLike, minGap: number, searchLength?: number): void
808
- ```
603
+ #### `verify.minClearance(label: string, a: ShapeLike, b: ShapeLike, minGap: number, searchLength?: number): void` — Check that a minimum clearance gap exists between two shapes.
809
604
 
810
- #### `verify.clearanceBetween()` — Check that the clearance gap between two shapes is inside an allowed range.
605
+ #### `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
606
 
812
607
  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
608
 
@@ -820,99 +615,39 @@ verify.clearanceBetween("cover is seated on gasket", cover, gasket, -0.01, 0.05)
820
615
  verify.clearanceBetween("carriage runs inside rail", carriage, rail, 0.2, 0.5);
821
616
  ```
822
617
 
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 ] }`
618
+ #### `verify.parallel(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void` — Check that two face normals are parallel (within toleranceDeg degrees).
834
619
 
835
- #### `verify.perpendicular()` Check that two face normals are perpendicular (within toleranceDeg degrees).
620
+ `FaceRefLike`: `{ normal: Vec3, center: Vec3 }`
836
621
 
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.
860
-
861
- ```ts
862
- verify.isEmpty(label: string, shape: ShapeLike, message?: string): void
863
- ```
622
+ #### `verify.perpendicular(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void` — Check that two face normals are perpendicular (within toleranceDeg degrees).
864
623
 
865
- #### `verify.notEmpty()` — Check that a shape is NOT empty.
866
-
867
- ```ts
868
- verify.notEmpty(label: string, shape: ShapeLike, message?: string): void
869
- ```
624
+ #### `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
625
 
871
- #### `verify.volumeApprox()` — Check that a shape's volume is approximately equal to expected (mm³).
626
+ #### `verify.faceAt(label: string, face: FaceRefLike, expectedPos: Vec3, toleranceMm?: number): void` — Check that a face center lies at a specific position (within toleranceMm).
872
627
 
873
- ```ts
874
- verify.volumeApprox(label: string, shape: ShapeLike, expected: number, tolerance?: number): void
875
- ```
628
+ #### `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
629
 
877
- #### `verify.areaApprox()` — Check that a shape's surface area is approximately equal to expected (mm²).
630
+ #### `verify.isEmpty(label: string, shape: ShapeLike, message?: string): void` — Check that a shape is empty.
878
631
 
879
- ```ts
880
- verify.areaApprox(label: string, shape: ShapeLike, expected: number, tolerance?: number): void
881
- ```
632
+ #### `verify.notEmpty(label: string, shape: ShapeLike, message?: string): void` — Check that a shape is NOT empty.
882
633
 
883
- #### `verify.boundingBoxSize()` — Check that a shape's bounding box has approximately the given size.
634
+ #### `verify.volumeApprox(label: string, shape: ShapeLike, expected: number, tolerance?: number): void` — Check that a shape's volume is approximately equal to expected (mm³).
884
635
 
885
- ```ts
886
- verify.boundingBoxSize(label: string, shape: ShapeLike, expectedSize: [ number, number, number ], tolerance?: number): void
887
- ```
636
+ #### `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
637
 
889
- #### `verify.edgeContinuity()` — Check that every sampled seam on a shape meets a requested continuity threshold.
638
+ #### `verify.boundingBoxSize(label: string, shape: ShapeLike, expectedSize: Vec3, tolerance?: number): void` — Check that a shape's bounding box has approximately the given size.
890
639
 
891
- ```ts
892
- verify.edgeContinuity(label: string, shape: ShapeLike, options?: EdgeContinuityThresholds): void
893
- ```
640
+ #### `verify.edgeContinuity(label: string, shape: ShapeLike, options?: EdgeContinuityThresholds): void` — Check that every sampled seam on a shape meets a requested continuity threshold.
894
641
 
895
642
  **`EdgeContinuityThresholds`**: `continuity?: SurfaceContinuity`, `samples?: number`, `positionTolerance?: number`, `tangentToleranceDeg?: number`, `curvatureTolerance?: number`
896
643
 
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
- ```
644
+ #### `verify.noTinyEdges(label: string, shape: ShapeLike, threshold?: number): void` — Check that a shape has no tiny edges below the requested threshold.
908
645
 
909
- #### `verify.noSelfIntersection()` — Best-effort exact-shape validity guard for self-intersections or broken B-Rep topology.
646
+ #### `verify.noSliverFaces(label: string, shape: ShapeLike, threshold?: number): void` — Check that a shape has no sliver faces below the requested score threshold.
910
647
 
911
- ```ts
912
- verify.noSelfIntersection(label: string, shape: ShapeLike): void
913
- ```
648
+ #### `verify.noSelfIntersection(label: string, shape: ShapeLike): void` — Best-effort exact-shape validity guard for self-intersections or broken B-Rep topology.
914
649
 
915
- #### `spec()` — Create a named, reusable bundle of verification checks.
650
+ #### `spec(name: string, checkFn: (...args: any[]) => void): Spec` — Create a named, reusable bundle of verification checks.
916
651
 
917
652
  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
653
 
@@ -942,10 +677,6 @@ fitSpec.check(bracket, standoff);
942
677
 
943
678
  **Spec-first workflow:** Write specs before building geometry. Checks go from red to green as you build — effectively TDD for CAD.
944
679
 
945
- ```ts
946
- spec(name: string, checkFn: (...args: any[]) => void): Spec
947
- ```
948
-
949
680
  **`Spec`**
950
681
  - `name: string` — The display name of this spec
951
682
 
@@ -963,17 +694,13 @@ Supports transforms (translate, rotate, scale, mirror, transform, rotateAround,
963
694
 
964
695
  | Property | Type | Description |
965
696
  |----------|------|-------------|
966
- | `materialProps` | `ShapeMaterialProps | undefined` | — |
697
+ | `materialProps` | `ShapeMaterialProps \| undefined` | — |
967
698
 
968
699
  **Appearance**
969
700
 
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
- ```
701
+ #### `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
702
 
976
- #### `material()` — Set PBR material properties for this shape's visual appearance.
703
+ #### `material(props: ShapeMaterialProps): Shape` — Set PBR material properties for this shape's visual appearance.
977
704
 
978
705
  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
706
 
@@ -988,10 +715,6 @@ cylinder(40, 20).material({ opacity: 0.4, clearcoat: 1.0, clearcoatRoughness: 0.
988
715
  box(100, 100, 10).color('#gold').material({ metalness: 0.95, roughness: 0.05 }).translate(0, 0, 50);
989
716
  ```
990
717
 
991
- ```ts
992
- material(props: ShapeMaterialProps): Shape
993
- ```
994
-
995
718
  **`ShapeMaterialProps`**
996
719
 
997
720
  | Option | Type | Description |
@@ -1013,12 +736,14 @@ material(props: ShapeMaterialProps): Shape
1013
736
 
1014
737
  **Face Topology**
1015
738
 
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.
739
+ #### `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
740
 
1018
741
  `.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
742
 
1020
743
  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
744
 
745
+ 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.
746
+
1022
747
  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
748
 
1024
749
  ```ts
@@ -1045,69 +770,29 @@ const full = union(left, right);
1045
770
  full.face('l/upper'); // left wing upper surface
1046
771
  ```
1047
772
 
1048
- ```ts
1049
- face(selector: FaceSelector): FaceRef
1050
- ```
1051
-
1052
- #### `faces()` — Return faces matching a query, or label semantic faces when passed a mapping.
773
+ #### `faces(): FaceRef[]` — Return faces matching a query, or label semantic faces when passed a mapping.
1053
774
 
1054
775
  Mapping form returns a new shape: `shape.faces({ lid: 'top', walls: ['front', 'back', 'left', 'right'] })`.
1055
776
 
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.
1073
-
1074
- ```ts
1075
- renameLabel(from: string, to: string): Shape
1076
- ```
1077
-
1078
- #### `dropLabels()` — Remove specific face labels. Returns a new shape.
777
+ #### `faceNames(): string[]` — List defined semantic face names currently available on this shape.
1079
778
 
1080
- ```ts
1081
- dropLabels(...names: string[]): Shape
1082
- ```
779
+ #### `prefixLabels(prefix: string): Shape` — Prefix all user-authored face labels, including semantic labels from `faces(mapping)`. Returns a new shape with modified labels.
1083
780
 
1084
- #### `dropAllLabels()` — Remove all face labels. Returns a new shape.
781
+ #### `renameLabel(from: string, to: string): Shape` — Rename a single face label. Returns a new shape.
1085
782
 
1086
- ```ts
1087
- dropAllLabels(): Shape
1088
- ```
783
+ #### `dropLabels(...names: string[]): Shape` — Remove specific face labels. Returns a new shape.
1089
784
 
1090
- #### `faceHistory()` — Get the transformation history for a specific face.
785
+ #### `dropAllLabels(): Shape` — Remove all face labels. Returns a new shape.
1091
786
 
1092
- ```ts
1093
- faceHistory(name: string): FaceTransformationHistory
1094
- ```
787
+ #### `faceHistory(name: string): FaceTransformationHistory` — Get the transformation history for a specific face.
1095
788
 
1096
789
  **Edge Topology**
1097
790
 
1098
- #### `edge()` — Get a named topology edge. Only available on shapes with tracked topology (from box/cylinder/extrude).
791
+ #### `edge(name: string): EdgeRef` — Get a named topology edge. Only available on shapes with tracked topology (from box/cylinder/extrude).
1099
792
 
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.
1105
-
1106
- ```ts
1107
- edgeNames(): string[]
1108
- ```
793
+ #### `edgeNames(): string[]` — List named topology edge names. Returns empty array if shape has no tracked topology.
1109
794
 
1110
- #### `edgesOf()` — Return all boundary edges of a named face.
795
+ #### `edgesOf(faceLabel: string, options?: EdgesOfOptions): EdgeSegment[]` — Return all boundary edges of a named face.
1111
796
 
1112
797
  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
798
 
@@ -1127,10 +812,6 @@ body = fillet(body, 1.5, body.edgesOf('opening'))
1127
812
  body.edgesOf('top', { concave: true })
1128
813
  ```
1129
814
 
1130
- ```ts
1131
- edgesOf(faceLabel: string, options?: EdgesOfOptions): EdgeSegment[]
1132
- ```
1133
-
1134
815
  **`EdgesOfOptions`**
1135
816
 
1136
817
  | Option | Type | Description |
@@ -1140,7 +821,7 @@ edgesOf(faceLabel: string, options?: EdgesOfOptions): EdgeSegment[]
1140
821
  | `concave?` | `boolean` | Additional geometric filter: only concave edges. |
1141
822
  | `minLength?` | `number` | Minimum edge length filter. |
1142
823
 
1143
- #### `edgesBetween()` — Return edges shared between two named faces.
824
+ #### `edgesBetween(faceA: string, faceB: string | string[]): EdgeSegment[]` — Return edges shared between two named faces.
1144
825
 
1145
826
  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
827
 
@@ -1159,155 +840,63 @@ tube = fillet(tube, 1, tube.edgesBetween('cap', 'barrel'))
1159
840
  body.edgesBetween('lid', ['left-wall', 'right-wall', 'front-wall', 'back-wall'])
1160
841
  ```
1161
842
 
1162
- ```ts
1163
- edgesBetween(faceA: string, faceB: string | string[]): EdgeSegment[]
1164
- ```
1165
-
1166
843
  **Transforms**
1167
844
 
1168
- #### `translate()` — Move the shape relative to its current position. All transforms are immutable and return new shapes.
845
+ #### `translate(x: number, y: number, z: number): Shape` — Move the shape relative to its current position. All transforms are immutable and return new shapes.
1169
846
 
1170
- ```ts
1171
- translate(x: number, y: number, z: number): Shape
1172
- ```
1173
-
1174
- #### `translatePolar()` — Translate using polar coordinates (radius + angle in degrees). Eliminates manual `r * Math.cos(angle * PI/180)` calculations.
847
+ #### `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
848
 
1176
849
  Example: `shape.translatePolar(50, 30)` moves 50mm at 30 degrees from +X.
1177
850
 
1178
- ```ts
1179
- translatePolar(radius: number, angleDeg: number, z?: number): Shape
1180
- ```
851
+ #### `moveTo(x: number, y: number, z: number): Shape` — Position the shape so its bounding box min corner is at the given global coordinate.
1181
852
 
1182
- #### `moveTo()` — Position the shape so its bounding box min corner is at the given global coordinate.
853
+ #### `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
854
 
1184
- ```ts
1185
- moveTo(x: number, y: number, z: number): Shape
1186
- ```
855
+ #### `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
856
 
1188
- #### `moveToLocal()` — Position the shape relative to another shape's local coordinate system (bounding box min corner).
857
+ #### `rotateX(angleDeg: number, options?: { pivot?: Vec3; }): Shape` — Rotate around the X axis by the given angle in degrees.
1189
858
 
1190
- ```ts
1191
- moveToLocal(target: Shape | { toShape(): Shape; }, x: number, y: number, z: number): Shape
1192
- ```
859
+ #### `rotateY(angleDeg: number, options?: { pivot?: Vec3; }): Shape` — Rotate around the Y axis by the given angle in degrees.
1193
860
 
1194
- #### `rotate()` — Rotate around an arbitrary axis through the origin.
861
+ #### `rotateZ(angleDeg: number, options?: { pivot?: Vec3; }): Shape` — Rotate around the Z axis by the given angle in degrees.
1195
862
 
1196
- ```ts
1197
- rotate(axis: [ number, number, number ], angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape
1198
- ```
863
+ #### `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.
1199
864
 
1200
- #### `rotateX()` Rotate around the X axis by the given angle in degrees.
865
+ `RotateAroundToOptions`: `{ mode?: RotateAroundToMode }`
1201
866
 
1202
- ```ts
1203
- rotateX(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape
1204
- ```
867
+ #### `transform(m: Mat4 | Transform): Shape` — Apply a 4x4 affine transform matrix (column-major) or a Transform object.
1205
868
 
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
- ```
869
+ #### `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.
1211
870
 
1212
- #### `rotateZ()` — Rotate around the Z axis by the given angle in degrees.
871
+ #### `scaleAround(pivot: Vec3, v: number | Vec3): Shape` — Scale the shape uniformly or per-axis from an explicit pivot point.
1213
872
 
1214
- ```ts
1215
- rotateZ(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape
1216
- ```
873
+ #### `mirror(normal: Vec3): Shape` — Mirror across a plane through the shape's bounding box center, defined by its normal vector.
1217
874
 
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.
875
+ #### `mirrorThrough(point: Vec3, normal: Vec3): Shape` — Mirror across a plane through an explicit point, defined by its normal vector.
1219
876
 
1220
- ```ts
1221
- rotateAroundTo(axis: [ number, number, number ], pivot: [ number, number, number ], movingPoint: RotationPointLike, targetPoint: RotationPointLike, options?: RotateAroundToOptions): Shape
1222
- ```
1223
-
1224
- `RotateAroundToOptions`: `{ mode?: RotateAroundToMode }`
1225
-
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
- ```
1237
-
1238
- #### `scaleAround()` — Scale the shape uniformly or per-axis from an explicit pivot point.
1239
-
1240
- ```ts
1241
- scaleAround(pivot: [ number, number, number ], v: number | [ number, number, number ]): Shape
1242
- ```
1243
-
1244
- #### `mirror()` — Mirror across a plane through the shape's bounding box center, defined by its normal vector.
1245
-
1246
- ```ts
1247
- mirror(normal: [ number, number, number ]): Shape
1248
- ```
1249
-
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.
877
+ #### `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
878
 
1258
879
  Example: cylinder(40, 5).pointAlong([1, 0, 0]) — lays cylinder along X, starting at origin
1259
880
 
1260
- ```ts
1261
- pointAlong(direction: [ number, number, number ]): Shape
1262
- ```
1263
-
1264
881
  **Booleans & Cutting**
1265
882
 
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().
883
+ #### `add(...others: ShapeOperandInput[]): Shape` — Union this shape with others (additive boolean). Method form of union().
1279
884
 
1280
- ```ts
1281
- intersect(...others: ShapeOperandInput[]): Shape
1282
- ```
885
+ #### `subtract(...others: ShapeOperandInput[]): Shape` — Subtract other shapes from this one. Method form of difference().
1283
886
 
1284
- #### `split()` — Split into [inside, outside] by another shape.
887
+ #### `intersect(...others: ShapeOperandInput[]): Shape` — Keep only the overlap with other shapes. Method form of intersection().
1285
888
 
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
- ```
889
+ #### `split(cutter: Shape | { toShape(): Shape; }): [ Shape, Shape ]` — Split into [inside, outside] by another shape.
1295
890
 
1296
- #### `trimByPlane()` Keep the positive side of the plane and discard the opposite side.
891
+ #### `splitByPlane(normal: Vec3, originOffset?: number): [ Shape, Shape ]` Split by infinite plane. Returns [positive-side, negative-side].
1297
892
 
1298
- ```ts
1299
- trimByPlane(normal: [ number, number, number ], originOffset?: number): Shape
1300
- ```
893
+ #### `trimByPlane(normal: Vec3, originOffset?: number): Shape` — Keep the positive side of the plane and discard the opposite side.
1301
894
 
1302
895
  **Features**
1303
896
 
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).
897
+ #### `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
898
 
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.
899
+ #### `pocket(face: FaceSelector, depth: number, opts?: PocketOptions): Shape` — Cut a pocket (cavity) into this solid through the named face.
1311
900
 
1312
901
  ```js
1313
902
  box(100, 100, 20).pocket('top', 8)
@@ -1315,50 +904,39 @@ box(100, 100, 20).pocket('top', 8, { inset: 5 })
1315
904
  box(100, 100, 20).pocket('top', 8, { scale: 0.8 })
1316
905
  ```
1317
906
 
1318
- ```ts
1319
- pocket(face: FaceSelector, depth: number, opts?: PocketOptions): Shape
1320
- ```
1321
-
1322
907
  **`PocketOptions`**
1323
908
  - `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
909
  - `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
910
  - `join?: "Square" | "Round" | "Miter"` — Corner join style when using `inset`. Default: 'Round'.
1326
911
 
1327
- #### `boss()` — Add a boss (protrusion) from the named face.
912
+ #### `boss(face: FaceSelector, height: number, opts?: BossOptions): Shape` — Add a boss (protrusion) from the named face.
1328
913
 
1329
914
  ```js
1330
915
  box(100, 100, 20).boss('top', 5)
1331
916
  box(100, 100, 20).boss('top', 10, { scale: 0.6 })
1332
917
  ```
1333
918
 
1334
- ```ts
1335
- boss(face: FaceSelector, height: number, opts?: BossOptions): Shape
1336
- ```
1337
-
1338
- #### `hole()` — Drill a hole into this solid at a face.
919
+ #### `hole(faceOrRef: SketchFaceTarget | FaceRef, opts: ShapeHoleOptions): Shape` — Drill a hole into this solid at a face.
1339
920
 
1340
921
  ```js
1341
922
  box(50, 50, 20).hole('top', { diameter: 8, depth: 10 })
1342
923
  box(50, 50, 20).hole('top', { diameter: 6, counterbore: { diameter: 12, depth: 3 } })
1343
924
  ```
1344
925
 
1345
- ```ts
1346
- hole(faceOrRef: SketchFaceTarget | FaceRef, opts: ShapeHoleOptions): Shape
1347
- ```
1348
-
1349
926
  **`FaceRef`**
1350
927
 
1351
928
  | Option | Type | Description |
1352
929
  |--------|------|-------------|
1353
- | `normal` | `[ number, number, number ]` | Normal direction of the face |
1354
- | `center` | `[ number, number, number ]` | Center point of the face |
930
+ | `normal` | `Vec3` | Normal direction of the face |
931
+ | `center` | `Vec3` | Center point of the face |
1355
932
  | `query?` | `FaceQueryRef` | Compiler-owned face query when available. |
1356
933
  | `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 |
934
+ | `uAxis?` | `Vec3` | Face-local horizontal axis for planar faces |
935
+ | `vAxis?` | `Vec3` | Face-local vertical axis for planar faces |
1359
936
  | `surface?` | `FaceSurface` | Analytic surface family when the backend can identify one. |
1360
937
  | `descendant?` | `FaceDescendantMetadata` | Shared descendant-resolution metadata when this face is a semantic region/set. |
1361
- | `name` | | — |
938
+
939
+ Also: `name: FaceName`.
1362
940
 
1363
941
  **`FaceDescendantMetadata`**: `kind: "single" | "face-set"`, `semantic: FaceDescendantSemantic`, `memberCount: number`, `memberNames: string[]`, `coplanar: boolean`
1364
942
 
@@ -1370,7 +948,7 @@ hole(faceOrRef: SketchFaceTarget | FaceRef, opts: ShapeHoleOptions): Shape
1370
948
 
1371
949
  **`ShapeHoleThreadOptions`**: `designation?: string`, `pitch?: number`, `class?: string`, `handedness?: "right" | "left"`, `depth?: number`, `modeled?: boolean`
1372
950
 
1373
- #### `cutout()` — Cut a profile-shaped pocket through a face using a placed sketch.
951
+ #### `cutout(sketch: Sketch, opts?: ShapeCutoutOptions): Shape` — Cut a profile-shaped pocket through a face using a placed sketch.
1374
952
 
1375
953
  The sketch must be placed on a face with `Sketch.onFace(...)`. The cut follows the sketch's 2D profile.
1376
954
 
@@ -1379,15 +957,11 @@ const profile = circle2d(10).onFace(body, 'top');
1379
957
  body.cutout(profile, { depth: 5 })
1380
958
  ```
1381
959
 
1382
- ```ts
1383
- cutout(sketch: Sketch, opts?: ShapeCutoutOptions): Shape
1384
- ```
1385
-
1386
- **`ShapeCutoutOptions`**: `depth?: number`, `upToFace?: SketchFaceTarget | FaceRef`, `extent?: ShapeFeatureExtentOptions`, `taperScale?: number | [ number, number ]`
960
+ **`ShapeCutoutOptions`**: `depth?: number`, `upToFace?: SketchFaceTarget | FaceRef`, `extent?: ShapeFeatureExtentOptions`, `taperScale?: number | Vec2`
1387
961
 
1388
962
  **Placement**
1389
963
 
1390
- #### `placeReference()` — Translate the shape so the given anchor or reference lands on the target coordinate.
964
+ #### `placeReference(ref: PlacementAnchorLike, target: Vec3, offset?: Vec3): Shape` — Translate the shape so the given anchor or reference lands on the target coordinate.
1391
965
 
1392
966
  Accepts any built-in anchor name (`'bottom'`, `'center'`, `'top-front-left'`, etc.) or a custom placement reference attached via `withReferences()`.
1393
967
 
@@ -1402,19 +976,11 @@ shape.placeReference('center', [0, 0, 0])
1402
976
  shape.placeReference('left', [10, 0, 0])
1403
977
  ```
1404
978
 
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.
979
+ #### `attachTo(target: ShapeAnchorTarget, targetAnchor: PlacementAnchorLike, selfAnchor?: PlacementAnchorLike, offset?: Vec3): Shape` — Position this shape relative to another using named 3D anchor points.
1410
980
 
1411
981
  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
982
 
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.
983
+ #### `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
984
 
1419
985
  Think of it like sticking a label on a box surface:
1420
986
 
@@ -1425,11 +991,7 @@ Think of it like sticking a label on a box surface:
1425
991
  - top/bottom: u = left/right (X), v = forward/back (Y)
1426
992
  - `protrude` = how far the child sticks out (positive = outward from face)
1427
993
 
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.
994
+ #### `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
995
 
1434
996
  Position the shape roughly first (translate/rotate), then call seatInto to auto-adjust the penetration depth. No manual coordinate math needed.
1435
997
 
@@ -1444,16 +1006,12 @@ pod.translate(0, station, radius + 20).seatInto(fuselage, 'base', { depth: 'flus
1444
1006
  mast.translate(0, station, radius + 50).seatInto(fuselage, 'mount', { depth: 'flush', gap: 3 });
1445
1007
  ```
1446
1008
 
1447
- ```ts
1448
- seatInto(target: Shape, surface: string, options?: SeatIntoOptions): Shape
1449
- ```
1450
-
1451
1009
  **`SeatIntoOptions`**
1452
- - `along?: [ number, number, number ]` — Movement axis. Default: inverted face normal (points into target).
1010
+ - `along?: Vec3` — Movement axis. Default: inverted face normal (points into target).
1453
1011
  - `depth?: "full" | "flush" | number` — How deep to embed. 'full' = entire face inside. 'flush' = nearest point touches. number = mm past flush. Default: 'full'.
1454
1012
  - `gap?: number` — Standoff gap in mm. Positive = gap between face and target. Negative = extra penetration. Default: 0.
1455
1013
 
1456
- #### `seatOver()` — Slide this shape until a target's labeled face is fully covered (inside this shape).
1014
+ #### `seatOver(target: Shape, targetSurface: string, options?: SeatIntoOptions): Shape` — Slide this shape until a target's labeled face is fully covered (inside this shape).
1457
1015
 
1458
1016
  The inverse of `seatInto`: instead of embedding *your* face into the target, you move until the *target's* face is embedded inside you.
1459
1017
 
@@ -1465,47 +1023,25 @@ nacelle.translate(rough).seatOver(pylon, 'bottom');
1465
1023
  cap.translate(rough).seatOver(post, 'top');
1466
1024
  ```
1467
1025
 
1468
- ```ts
1469
- seatOver(target: Shape, targetSurface: string, options?: SeatIntoOptions): Shape
1470
- ```
1471
-
1472
1026
  **Connectors**
1473
1027
 
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).
1475
-
1476
- ```ts
1477
- withConnectors(connectors: Record<string, ConnectorInput>): Shape
1478
- ```
1028
+ #### `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).
1479
1029
 
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`
1030
+ `PortInput`: `{ origin?: Vec3, axis?: Vec3, start?: Vec3, end?: Vec3, up?: Vec3, kind?: JointType, min?: number, max?: number }`
1481
1031
 
1482
1032
  `ConnectorInput`: `{ connectorType?: string, gender?: ConnectorGender, measurements?: Record<string, number | string> }`
1483
1033
 
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
- ```
1034
+ #### `connectorNames(): string[]` — List all connector names on this shape.
1495
1035
 
1496
- #### `connectorDistance()`Distance between two connector origins on this shape.
1036
+ #### `connectorsByType(type: string): Array<{ name: string; port: ConnectorDef; }>` Get all connectors of a given type.
1497
1037
 
1498
- ```ts
1499
- connectorDistance(nameA: string, nameB: string): number
1500
- ```
1038
+ #### `connectorDistance(nameA: string, nameB: string): number` — Distance between two connector origins on this shape.
1501
1039
 
1502
- #### `connectorMeasurements()` — Get measurements metadata from a connector.
1040
+ #### `connectorMeasurements(name: string): Record<string, number | string>` — Get measurements metadata from a connector.
1503
1041
 
1504
- ```ts
1505
- connectorMeasurements(name: string): Record<string, number | string>
1506
- ```
1042
+ #### `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
1043
 
1508
- #### `matchTo()` — Position this shape by matching connectors to a target.
1044
+ 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
1045
 
1510
1046
  Overloads:
1511
1047
 
@@ -1513,125 +1049,53 @@ Overloads:
1513
1049
  - Dictionary (same target): `matchTo(target, { selfConn: targetConn, ... }, options?)`
1514
1050
  - Multi-target: `matchTo([ [target1, selfConn1, targetConn1], ... ], options?)`
1515
1051
 
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
1052
  `MatchToOptions`: `{ force?: boolean, angle?: number, distance?: number }`
1521
1053
 
1522
1054
  **References**
1523
1055
 
1524
- #### `withReferences()` — Attach named placement references that survive normal transforms and imports.
1056
+ #### `withReferences(refs: PlacementReferenceInput): Shape` — Attach named placement references that survive normal transforms and imports.
1525
1057
 
1526
- ```ts
1527
- withReferences(refs: PlacementReferenceInput): Shape
1528
- ```
1529
-
1530
- **`PlacementReferenceInput`**: `points?: Record<string, [ number, number, number ]>`, `edges?: Record<string, PlacementEdgeRef>`, `surfaces?: Record<string, PlacementSurfaceRef>`, `objects?: Record<string, PlacementObjectInput>`
1058
+ **`PlacementReferenceInput`**: `points?: Record<string, Vec3>`, `edges?: Record<string, PlacementEdgeRef>`, `surfaces?: Record<string, PlacementSurfaceRef>`, `objects?: Record<string, PlacementObjectInput>`
1531
1059
 
1532
1060
  `PlacementEdgeRef`: `{ start: Vec3, end: Vec3 }`
1533
1061
 
1534
1062
  `PlacementSurfaceRef`: `{ center: Vec3, normal: Vec3 }`
1535
1063
 
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.
1064
+ #### `referenceNames(kind?: PlacementReferenceKind): string[]` — List named placement references carried by this shape.
1543
1065
 
1544
- ```ts
1545
- referencePoint(ref: PlacementAnchorLike): [ number, number, number ]
1546
- ```
1066
+ #### `referencePoint(ref: PlacementAnchorLike): Vec3` — Resolve a named placement reference or built-in anchor to a 3D point.
1547
1067
 
1548
1068
  **Measurement**
1549
1069
 
1550
- #### `boundingBox()` — Get the axis-aligned bounding box as { min: [x,y,z], max: [x,y,z] }.
1551
-
1552
- ```ts
1553
- boundingBox(): ShapeRuntimeBounds
1554
- ```
1555
-
1556
- #### `volume()` — Volume in mm cubed.
1557
-
1558
- ```ts
1559
- volume(): number
1560
- ```
1070
+ #### `boundingBox(): ShapeRuntimeBounds` — Get the axis-aligned bounding box as { min: [x,y,z], max: [x,y,z] }.
1561
1071
 
1562
- #### `surfaceArea()` — Surface area in mm squared.
1072
+ #### `volume(): number` — Volume in mm cubed.
1563
1073
 
1564
- ```ts
1565
- surfaceArea(): number
1566
- ```
1074
+ #### `surfaceArea(): number` — Surface area in mm squared.
1567
1075
 
1568
- #### `isEmpty()` — True if the shape contains no geometry.
1076
+ #### `isEmpty(): boolean` — True if the shape contains no geometry.
1569
1077
 
1570
- ```ts
1571
- isEmpty(): boolean
1572
- ```
1078
+ #### `numBodies(): number` — Number of disconnected solid bodies in this shape.
1573
1079
 
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
- ```
1080
+ #### `numTri(): number` — Triangle count of the mesh representation.
1585
1081
 
1586
1082
  **Other**
1587
1083
 
1588
- #### `clone()` — Return a new Shape wrapper for explicit duplication in scripts.
1084
+ #### `clone(): Shape` — Return a new Shape wrapper for explicit duplication in scripts.
1589
1085
 
1590
- ```ts
1591
- clone(): Shape
1592
- ```
1593
-
1594
- #### `geometryInfo()` — Inspect which backend/representation produced this solid.
1595
-
1596
- ```ts
1597
- geometryInfo(): GeometryInfo
1598
- ```
1599
-
1600
- #### `as()` — Name this shape as a reference namespace for diagnostics and future published refs.
1086
+ #### `geometryInfo(): GeometryInfo` — Inspect which backend/representation produced this solid.
1601
1087
 
1602
- ```ts
1603
- as(name: string): Shape
1604
- ```
1088
+ #### `as(name: string): Shape` — Name this shape as a reference namespace for diagnostics and future published refs.
1605
1089
 
1606
- #### `ref()` — Resolve a semantic reference path like `lid`, `lid/back`, or a midpoint selector on `lid/back`.
1090
+ #### `ref(path: string): ShapeRef` — Resolve a semantic reference path like `lid`, `lid/back`, or a midpoint selector on `lid/back`.
1607
1091
 
1608
- ```ts
1609
- ref(path: string): ShapeRef
1610
- ```
1092
+ #### `thicken(thickness: number): Shape` — Offset-thicken an exact open surface or shell into a solid.
1611
1093
 
1612
- #### `thicken()` — Offset-thicken an exact open surface or shell into a solid.
1094
+ #### `getMesh(): ShapeRuntimeMesh` — Extract triangle mesh for Three.js rendering
1613
1095
 
1614
- ```ts
1615
- thicken(thickness: number): Shape
1616
- ```
1096
+ #### `slice(offset?: number): any` — Slice the runtime solid by a plane normal to local Z at the given offset.
1617
1097
 
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
- ```
1098
+ #### `project(): any` — Orthographically project the runtime solid onto the local XY plane.
1635
1099
 
1636
1100
  **Compatibility Aliases**
1637
1101
 
@@ -1640,101 +1104,45 @@ project(): any
1640
1104
 
1641
1105
  ### `Transform`
1642
1106
 
1643
- #### `identity()` — Return the identity transform.
1107
+ #### `static identity(): Transform` — Return the identity transform.
1644
1108
 
1645
- ```ts
1646
- static identity(): Transform
1647
- ```
1648
-
1649
- #### `from()` — Wrap an existing `Transform` or raw 4x4 matrix as a `Transform`.
1109
+ #### `static from(input: TransformInput): Transform` — Wrap an existing `Transform` or raw 4x4 matrix as a `Transform`.
1650
1110
 
1651
- ```ts
1652
- static from(input: TransformInput): Transform
1653
- ```
1111
+ #### `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
1112
 
1655
- #### `translation()` Create a translation transform.
1113
+ 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
1114
 
1657
1115
  ```ts
1658
- static translation(x: number, y: number, z: number): Transform
1116
+ const world = Transform.compose(childBase, jointMotion, jointFrame, parentWorld);
1659
1117
  ```
1660
1118
 
1661
- #### `scale()` — Create a uniform or per-axis scale transform.
1119
+ #### `static translation(x: number, y: number, z: number): Transform` — Create a translation transform.
1662
1120
 
1663
- ```ts
1664
- static scale(v: number | Vec3): Transform
1665
- ```
1121
+ #### `static scale(v: number | Vec3): Transform` — Create a uniform or per-axis scale transform.
1666
1122
 
1667
- #### `rotationAxis()` — Create a rotation around an arbitrary axis, optionally about a pivot.
1123
+ #### `static rotationAxis(axis: Vec3, angleDeg: number, pivot?: Vec3): Transform` — Create a rotation around an arbitrary axis, optionally about a pivot.
1668
1124
 
1669
- ```ts
1670
- static rotationAxis(axis: Vec3, angleDeg: number, pivot?: Vec3): Transform
1671
- ```
1125
+ #### `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
1126
 
1673
- #### `rotateAroundTo()` — Solve the rotation needed to move one point onto a target line or plane.
1127
+ #### `mul(other: TransformInput): Transform` — Compose transforms in chain order: `a.mul(b)` applies `a`, then `b`.
1674
1128
 
1675
- ```ts
1676
- static rotateAroundTo(axis: Vec3, pivot: Vec3, movingPoint: Vec3, targetPoint: Vec3, options?: RotateAroundToOptions): Transform
1677
- ```
1129
+ #### `translate(x: number, y: number, z: number): Transform` — Translate after the current transform.
1678
1130
 
1679
- #### `mul()` Compose transforms in chain order: `a.mul(b)` applies `a`, then `b`.
1131
+ #### `rotateAxis(axis: Vec3, angleDeg: number, pivot?: Vec3): Transform` Rotate after the current transform.
1680
1132
 
1681
- ```ts
1682
- mul(other: TransformInput): Transform
1683
- ```
1133
+ #### `rotateX(angleDeg: number, pivot?: Vec3): Transform` — Rotate about the X axis after the current transform (parity with `Shape.rotateX`).
1684
1134
 
1685
- #### `translate()` — Translate after the current transform.
1135
+ #### `rotateY(angleDeg: number, pivot?: Vec3): Transform` — Rotate about the Y axis after the current transform (parity with `Shape.rotateY`).
1686
1136
 
1687
- ```ts
1688
- translate(x: number, y: number, z: number): Transform
1689
- ```
1137
+ #### `rotateZ(angleDeg: number, pivot?: Vec3): Transform` — Rotate about the Z axis after the current transform (parity with `Shape.rotateZ`).
1690
1138
 
1691
- #### `rotateAxis()` — Rotate after the current transform.
1139
+ #### `inverse(): Transform` — Return the inverse transform.
1692
1140
 
1693
- ```ts
1694
- rotateAxis(axis: Vec3, angleDeg: number, pivot?: Vec3): Transform
1695
- ```
1141
+ #### `point(p: Vec3): Vec3` — Transform a point using homogeneous coordinates.
1696
1142
 
1697
- #### `rotateX()` — Rotate about the X axis after the current transform (parity with `Shape.rotateX`).
1143
+ #### `vector(v: Vec3): Vec3` — Transform a direction vector without translation.
1698
1144
 
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
- ```
1145
+ #### `toArray(): Mat4` — Return the transform as a raw 4x4 matrix array.
1738
1146
 
1739
1147
  ### `ShapeGroup`
1740
1148
 
@@ -1743,117 +1151,49 @@ toArray(): Mat4
1743
1151
  | Property | Type | Description |
1744
1152
  |----------|------|-------------|
1745
1153
  | `children` | `GroupChild[]` | — |
1746
- | `childNames` | `Array<string | undefined>` | — |
1154
+ | `childNames` | `Array<string \| undefined>` | — |
1747
1155
 
1748
1156
  **Children**
1749
1157
 
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`.
1158
+ #### `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
1159
 
1758
- ```ts
1759
- childName(index: number): string | undefined
1760
- ```
1160
+ #### `childName(index: number): string | undefined` — Return the optional name of the child at `index`.
1761
1161
 
1762
1162
  **Transforms**
1763
1163
 
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.
1783
-
1784
- ```ts
1785
- rotate(axis: [ number, number, number ], angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
1786
- ```
1787
-
1788
- #### `rotateX()` — Rotate the group around the X axis.
1789
-
1790
- ```ts
1791
- rotateX(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
1792
- ```
1793
-
1794
- #### `rotateY()` — Rotate the group around the Y axis.
1795
-
1796
- ```ts
1797
- rotateY(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
1798
- ```
1799
-
1800
- #### `rotateZ()` — Rotate the group around the Z axis.
1801
-
1802
- ```ts
1803
- rotateZ(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
1804
- ```
1164
+ #### `translate(x: number, y: number, z: number): ShapeGroup` — Move the entire group by (x, y, z). All children move together as a unit.
1805
1165
 
1806
- #### `rotateAroundAxis()` — Rotate around an arbitrary axis, optionally through a pivot point.
1166
+ #### `moveTo(x: number, y: number, z: number): ShapeGroup` — Move the group so its bounding-box min corner lands at the given coordinate.
1807
1167
 
1808
- ```ts
1809
- rotateAroundAxis(axis: [ number, number, number ], angleDeg: number, pivot?: [ number, number, number ]): ShapeGroup
1810
- ```
1168
+ #### `moveToLocal(target: Shape | ShapeGroup, x: number, y: number, z: number): ShapeGroup` — Move the group relative to another part's bounding-box min corner.
1811
1169
 
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.
1170
+ #### `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.
1813
1171
 
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
- ```
1172
+ #### `rotateX(angleDeg: number, options?: { pivot?: Vec3; }): ShapeGroup` — Rotate the group around the X axis.
1817
1173
 
1818
- #### `pointAlong()` — Reorient the group so its local Z axis points along `direction`.
1174
+ #### `rotateY(angleDeg: number, options?: { pivot?: Vec3; }): ShapeGroup` — Rotate the group around the Y axis.
1819
1175
 
1820
- ```ts
1821
- pointAlong(direction: [ number, number, number ]): ShapeGroup
1822
- ```
1176
+ #### `rotateZ(angleDeg: number, options?: { pivot?: Vec3; }): ShapeGroup` — Rotate the group around the Z axis.
1823
1177
 
1824
- #### `transform()` — Apply a 4x4 transform matrix or `Transform` to all 3D children.
1178
+ #### `rotateAroundAxis(axis: Vec3, angleDeg: number, pivot?: Vec3): ShapeGroup` — Rotate around an arbitrary axis, optionally through a pivot point.
1825
1179
 
1826
- ```ts
1827
- transform(m: Mat4 | Transform): ShapeGroup
1828
- ```
1180
+ #### `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.
1829
1181
 
1830
- #### `scale()` — Scale uniformly or per-axis from the group's bounding-box center.
1182
+ #### `pointAlong(direction: Vec3): ShapeGroup` — Reorient the group so its local Z axis points along `direction`.
1831
1183
 
1832
- ```ts
1833
- scale(v: number | [ number, number, number ]): ShapeGroup
1834
- ```
1184
+ #### `transform(m: Mat4 | Transform): ShapeGroup` — Apply a 4x4 transform matrix or `Transform` to all 3D children.
1835
1185
 
1836
- #### `scaleAround()` — Scale uniformly or per-axis from an explicit pivot point.
1186
+ #### `scale(v: number | Vec3): ShapeGroup` — Scale uniformly or per-axis from the group's bounding-box center.
1837
1187
 
1838
- ```ts
1839
- scaleAround(pivot: [ number, number, number ], v: number | [ number, number, number ]): ShapeGroup
1840
- ```
1188
+ #### `scaleAround(pivot: Vec3, v: number | Vec3): ShapeGroup` — Scale uniformly or per-axis from an explicit pivot point.
1841
1189
 
1842
- #### `mirror()` — Mirror across a plane through the group's bounding-box center.
1190
+ #### `mirror(normal: Vec3): ShapeGroup` — Mirror across a plane through the group's bounding-box center.
1843
1191
 
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
- ```
1192
+ #### `mirrorThrough(point: Vec3, normal: Vec3): ShapeGroup` — Mirror across a plane through an explicit point.
1853
1193
 
1854
1194
  **Placement**
1855
1195
 
1856
- #### `placeReference()` — Translate the group so the given anchor or reference lands on the target coordinate.
1196
+ #### `placeReference(ref: PlacementAnchorLike, target: Vec3, offset?: Vec3): ShapeGroup` — Translate the group so the given anchor or reference lands on the target coordinate.
1857
1197
 
1858
1198
  Accepts any built-in anchor name (`'bottom'`, `'center'`, `'top-front-left'`, etc.) or a custom placement reference attached via `withReferences()`.
1859
1199
 
@@ -1866,57 +1206,27 @@ const placed = require('./bracket-assembly.forge.js').group
1866
1206
  .placeReference('mountCenter', [0, 0, 50]);
1867
1207
  ```
1868
1208
 
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.
1209
+ #### `attachTo(target: Shape | ShapeGroup, targetAnchor: Anchor3D | string, selfAnchor?: Anchor3D, offset?: Vec3): ShapeGroup` — Attach this group to a face or anchor on another part.
1874
1210
 
1875
1211
  `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
1212
 
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
- ```
1213
+ #### `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
1214
 
1887
1215
  **Connectors**
1888
1216
 
1889
- #### `withConnectors()` — Attach named connectors — attachment points that survive transforms. Connectors can be bare (position + orientation) or typed (with connectorType/gender for compatibility matching).
1217
+ #### `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
1218
 
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.
1219
+ #### `connectorNames(): string[]` — List all connector names, including "ChildName.connectorName" from named children.
1902
1220
 
1903
- ```ts
1904
- connectorsByType(type: string): Array<{ name: string; port: ConnectorDef; }>
1905
- ```
1221
+ #### `connectorsByType(type: string): Array<{ name: string; port: ConnectorDef; }>` — Get all connectors of a given type, including from named children.
1906
1222
 
1907
- #### `connectorDistance()` — Distance between two connector origins on this group (supports dotted child paths).
1223
+ #### `connectorDistance(nameA: string, nameB: string): number` — Distance between two connector origins on this group (supports dotted child paths).
1908
1224
 
1909
- ```ts
1910
- connectorDistance(nameA: string, nameB: string): number
1911
- ```
1225
+ #### `connectorMeasurements(name: string): Record<string, number | string>` — Get measurements metadata from a connector (supports dotted child paths).
1912
1226
 
1913
- #### `connectorMeasurements()` — Get measurements metadata from a connector (supports dotted child paths).
1914
-
1915
- ```ts
1916
- connectorMeasurements(name: string): Record<string, number | string>
1917
- ```
1227
+ #### `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
1228
 
1919
- #### `matchTo()` — Position this group by matching connectors to a target. Connector names support dotted paths into named children: "ChildName.connectorName".
1229
+ 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
1230
 
1921
1231
  Overloads:
1922
1232
 
@@ -1924,13 +1234,9 @@ Overloads:
1924
1234
  - Dictionary (same target): `matchTo(target, { selfConn: targetConn, ... }, options?)`
1925
1235
  - Multi-target: `matchTo([ [target1, selfConn1, targetConn1], ... ], options?)`
1926
1236
 
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
1237
  **References**
1932
1238
 
1933
- #### `withReferences()` — Attach named placement references to this group. References survive normal transforms (translate/rotate/scale/mirror/transform).
1239
+ #### `withReferences(refs: PlacementReferenceInput): ShapeGroup` — Attach named placement references to this group. References survive normal transforms (translate/rotate/scale/mirror/transform).
1934
1240
 
1935
1241
  ```javascript
1936
1242
  const bracket = group(
@@ -1941,41 +1247,17 @@ const bracket = group(
1941
1247
  });
1942
1248
  ```
1943
1249
 
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.
1250
+ #### `referenceNames(kind?: PlacementReferenceKind): string[]` — List named placement references carried by this group.
1955
1251
 
1956
- ```ts
1957
- referencePoint(ref: PlacementAnchorLike): [ number, number, number ]
1958
- ```
1252
+ #### `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
1253
 
1960
1254
  **Other**
1961
1255
 
1962
- #### `clone()` — Return a deep-cloned ShapeGroup tree (refs copied).
1256
+ #### `clone(): ShapeGroup` — Return a deep-cloned ShapeGroup tree (refs copied).
1963
1257
 
1964
- ```ts
1965
- clone(): ShapeGroup
1966
- ```
1258
+ #### `boundingBox(): { min: Vec3; max: Vec3; }` — Return the combined 3D bounding box of all children.
1967
1259
 
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
- ```
1260
+ #### `color(hex: string): ShapeGroup` — Return a copy of the group with the given display color applied to each child.
1979
1261
 
1980
1262
  **Compatibility Aliases**
1981
1263
 
@@ -1993,67 +1275,27 @@ color(hex: string): ShapeGroup
1993
1275
 
1994
1276
  ### `Pattern2D`
1995
1277
 
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.
2009
-
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.
1278
+ #### `add(...patterns: Pattern2DInput[]): Pattern2D` — Add this pattern to one or more patterns or constant height offsets.
2021
1279
 
2022
- ```ts
2023
- max(...patterns: Pattern2DInput[]): Pattern2D
2024
- ```
1280
+ #### `subtract(pattern: Pattern2DInput): Pattern2D` — Subtract another pattern or constant height offset from this pattern.
2025
1281
 
2026
- #### `clamp()` — Limit pattern height to the inclusive `[min, max]` range in millimeters.
1282
+ #### `multiply(...patterns: Pattern2DInput[]): Pattern2D` — Multiply this pattern by one or more patterns or numeric scale factors.
2027
1283
 
2028
- ```ts
2029
- clamp(min: number, max: number): Pattern2D
2030
- ```
1284
+ #### `min(...patterns: Pattern2DInput[]): Pattern2D` — Keep the lower height between this pattern and one or more other patterns.
2031
1285
 
2032
- #### `abs()` — Convert negative heights to positive heights.
1286
+ #### `max(...patterns: Pattern2DInput[]): Pattern2D` — Keep the higher height between this pattern and one or more other patterns.
2033
1287
 
2034
- ```ts
2035
- abs(): Pattern2D
2036
- ```
1288
+ #### `clamp(min: number, max: number): Pattern2D` — Limit pattern height to the inclusive `[min, max]` range in millimeters.
2037
1289
 
2038
- #### `negate()` — Flip the pattern height sign.
1290
+ #### `abs(): Pattern2D` — Convert negative heights to positive heights.
2039
1291
 
2040
- ```ts
2041
- negate(): Pattern2D
2042
- ```
1292
+ #### `negate(): Pattern2D` — Flip the pattern height sign.
2043
1293
 
2044
1294
  ### `Pattern2DBuilder`
2045
1295
 
2046
- #### `constant()` — Create a constant-height pattern in millimeters.
2047
-
2048
- ```ts
2049
- constant(value?: number): Pattern2D
2050
- ```
2051
-
2052
- #### `sineWave()` — Create a sinusoidal wave pattern in UV space.
1296
+ #### `constant(value?: number): Pattern2D` — Create a constant-height pattern in millimeters.
2053
1297
 
2054
- ```ts
2055
- sineWave(options: Pattern2DSineWaveOptions): Pattern2D
2056
- ```
1298
+ #### `sineWave(options: Pattern2DSineWaveOptions): Pattern2D` — Create a sinusoidal wave pattern in UV space.
2057
1299
 
2058
1300
  **`Pattern2DSineWaveOptions`**
2059
1301
 
@@ -2065,11 +1307,7 @@ sineWave(options: Pattern2DSineWaveOptions): Pattern2D
2065
1307
  | `phase?` | `number` | Phase offset in radians. Default: 0. |
2066
1308
  | `bias?` | `number` | Constant height offset in millimeters. Default: 0. |
2067
1309
 
2068
- #### `stripes()` — Create recessed stripe bands in UV space.
2069
-
2070
- ```ts
2071
- stripes(options: Pattern2DStripesOptions): Pattern2D
2072
- ```
1310
+ #### `stripes(options: Pattern2DStripesOptions): Pattern2D` — Create recessed stripe bands in UV space.
2073
1311
 
2074
1312
  **`Pattern2DStripesOptions`**
2075
1313
 
@@ -2080,11 +1318,7 @@ stripes(options: Pattern2DStripesOptions): Pattern2D
2080
1318
  | `width` | `number` | Stripe width in surface millimeters. |
2081
1319
  | `depth?` | `number` | Stripe groove depth in millimeters. Default: 1. |
2082
1320
 
2083
- #### `overUnderWeave()` — Create an over-under woven relief pattern in UV space.
2084
-
2085
- ```ts
2086
- overUnderWeave(options: Pattern2DOverUnderWeaveOptions): Pattern2D
2087
- ```
1321
+ #### `overUnderWeave(options: Pattern2DOverUnderWeaveOptions): Pattern2D` — Create an over-under woven relief pattern in UV space.
2088
1322
 
2089
1323
  **`Pattern2DOverUnderWeaveOptions`**
2090
1324
 
@@ -2095,120 +1329,6 @@ overUnderWeave(options: Pattern2DOverUnderWeaveOptions): Pattern2D
2095
1329
  | `depth?` | `number` | Thread groove depth in millimeters. Default: 0.8. |
2096
1330
  | `underScale?` | `number` | Relative height of the under-crossing thread. Default: 0.15. |
2097
1331
 
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
1332
  ### `ShapeRef`
2213
1333
 
2214
1334
  A first-class reference path over a shape's semantic faces and face relationships.
@@ -2223,107 +1343,39 @@ Created with `shape.ref("lid/back")`, then refined through methods such as `.poi
2223
1343
 
2224
1344
  **Methods:**
2225
1345
 
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
- ```
1346
+ #### `resolve(): ShapeReferenceResolution` — Resolve this reference into its current faces, edges, or points.
2279
1347
 
2280
- #### `faces()` — Resolve this reference as one or more faces.
1348
+ #### `get kind(): ShapeReferenceKind` — The resolved reference kind, such as `face`, `edge-set`, or `point`.
2281
1349
 
2282
- ```ts
2283
- faces(): FaceRef[]
2284
- ```
1350
+ #### `get cardinality(): ShapeReferenceCardinality` — Whether the reference currently resolves to zero, one, or many matches.
2285
1351
 
2286
- #### `face()` — Resolve this reference as exactly one face.
1352
+ #### `status(): ShapeReferenceStatus` — Return the reference lifecycle status for the current shape state.
2287
1353
 
2288
- ```ts
2289
- face(): FaceRef
2290
- ```
1354
+ #### `explain(): string` — Return a human-readable explanation of how this reference resolved.
2291
1355
 
2292
- #### `edges()` — Resolve this reference as one or more edges. Face references return boundary edges.
1356
+ #### `as(name: string): ShapeRef` — Name this derived reference so the same shape can resolve it by `shape.ref(name)`.
2293
1357
 
2294
- ```ts
2295
- edges(): EdgeSegment[]
2296
- ```
1358
+ #### `maybe(): ShapeRef` — Return an optional reference that resolves to zero matches instead of throwing when missing.
2297
1359
 
2298
- #### `edge()` — Resolve this reference as exactly one edge.
1360
+ #### `all(): ShapeRef` — Mark that a multi-match reference is intentionally being used as a set.
2299
1361
 
2300
- ```ts
2301
- edge(): EdgeSegment
2302
- ```
1362
+ #### `one(): ShapeRef` — Require this reference to resolve to exactly one match.
2303
1363
 
2304
- #### `points()` — Resolve this reference as one or more points. Faces use centers and edges use midpoints.
1364
+ #### `faces(): FaceRef[]` — Resolve this reference as one or more faces.
2305
1365
 
2306
- ```ts
2307
- points(): Vec3[]
2308
- ```
1366
+ #### `face(): FaceRef` — Resolve this reference as exactly one face.
2309
1367
 
2310
- #### [`point()`](/docs/sketch#point) — Resolve this reference as exactly one point.
1368
+ #### `edges(): EdgeSegment[]` — Resolve this reference as one or more edges. Face references return boundary edges.
2311
1369
 
2312
- ```ts
2313
- point(): Vec3
2314
- ```
1370
+ #### `edge(): EdgeSegment` — Resolve this reference as exactly one edge.
2315
1371
 
2316
- #### `toJSON()` — Return the structured JSON-friendly reference resolution.
1372
+ #### `points(): Vec3[]` — Resolve this reference as one or more points. Faces use centers and edges use midpoints.
2317
1373
 
2318
- ```ts
2319
- toJSON(): ShapeReferenceResolution
2320
- ```
1374
+ #### `point(): Vec3` — Resolve this reference as exactly one point.
2321
1375
 
2322
- #### `toString()` — Return a compact display form for this reference path.
1376
+ #### `toJSON(): ShapeReferenceResolution` — Return the structured JSON-friendly reference resolution.
2323
1377
 
2324
- ```ts
2325
- toString(): string
2326
- ```
1378
+ #### `toString(): string` — Return a compact display form for this reference path.
2327
1379
 
2328
1380
  ---
2329
1381
 
@@ -2333,46 +1385,7 @@ toString(): string
2333
1385
 
2334
1386
  ### `verify`
2335
1387
 
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.
1388
+ 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
1389
 
2377
1390
  ### `Points`
2378
1391
 
@@ -2381,7 +1394,7 @@ toString(): string
2381
1394
  - `lerp(a: Vec3, b: Vec3, t: number): Vec3` — Linearly interpolate between two 3D points. t=0 returns a, t=1 returns b.
2382
1395
  - `direction(a: Vec3, b: Vec3): Vec3` — Unit direction vector from a to b. Throws if a and b are the same point.
2383
1396
  - `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.
1397
+ - `polar(length: number, angleDeg: number, from?: Vec2): Vec2` — Compute a 2D point at distance and angle (degrees) from an optional origin.
2385
1398
 
2386
1399
  ### `connector`
2387
1400
 
@@ -2389,6 +1402,19 @@ Connector factory. Create attachment points: `connector({...})`, `connector.male
2389
1402
 
2390
1403
  ### `Import`
2391
1404
 
2392
- Namespaced file import helpers for formats that should not add new lowercase globals.
1405
+ Namespaced file-format import helpers — the single vocabulary for bringing external geometry files into a model.
2393
1406
 
2394
1407
  - `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.
1408
+ - `svgSketch(fileName: string, options?: SvgImportOptions): Sketch` — Parse an SVG file and return it as a Sketch with options for region filtering, scaling, and simplification.
1409
+ - `mesh(fileName: string, options?: { scale?: number; center?: boolean; object?: string; separateObjects?: boolean; }): Shape | ShapeGroup` — Import an external mesh file (STL, OBJ, 3MF).
1410
+
1411
+ 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`.
1412
+
1413
+ 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.
1414
+
1415
+ ```js
1416
+ const all = Import.mesh("./assembly.3mf", { separateObjects: true });
1417
+ const pin = all.child("Pin #001");
1418
+ const plate = Import.mesh("./assembly.3mf", { object: "3mf:build:001:object:7" });
1419
+ ```
1420
+ - `step(fileName: string): 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.