forgecad 0.6.3 → 0.7.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 (193) hide show
  1. package/README.md +2 -11
  2. package/dist/assets/{AdminPage-CeqCUUgu.js → AdminPage-DAu1C1ST.js} +250 -151
  3. package/dist/assets/{BlogPage-P_AJP0v9.js → BlogPage-CJEXL_zJ.js} +94 -70
  4. package/dist/assets/{DocsPage-CKRV2iq2.js → DocsPage-Gc_BCdqC.js} +269 -143
  5. package/dist/assets/EditorApp-D9bJvtf7.js +11338 -0
  6. package/dist/assets/{EditorApp-CnC2k4cW.css → EditorApp-DG1-oUSV.css} +459 -87
  7. package/dist/assets/{EmbedViewer-DBlzmQ5i.js → EmbedViewer-CEO8XbV8.js} +2 -4
  8. package/dist/assets/LandingPage-CdCuEOdC.js +451 -0
  9. package/dist/assets/PricingPage-BSrxu6d7.js +232 -0
  10. package/dist/assets/{SettingsPage-BqCh9JcC.js → SettingsPage-FUCSIRq6.js} +129 -5
  11. package/dist/assets/{evalWorker-Ql-aKwLA.js → evalWorker-KoR0SNKq.js} +6770 -2914
  12. package/dist/assets/{index-2hfs_ub0.css → index-CyVd1D4D.css} +227 -53
  13. package/dist/assets/{Viewport-CoB46f5R.js → index-wTEK39at.js} +31385 -6439
  14. package/dist/assets/{javascript-DCxGoE5Y.js → javascript-DAl8Gmyo.js} +1 -1
  15. package/dist/assets/{manifold-CqNMHHKO.js → manifold-B1sGWdYk.js} +4 -3
  16. package/dist/assets/{manifold-Cce9wRFz.js → manifold-D7o0N50J.js} +1 -1
  17. package/dist/assets/{manifold-D6BeHIOo.js → manifold-G5sBaXzi.js} +1 -1
  18. package/dist/assets/{reportWorker-sFEFonXf.js → reportWorker-DYcRHhv9.js} +6798 -3341
  19. package/dist/assets/{vendor-react-Dt7-aaJH.js → vendor-react-CG3i_wp0.js} +65 -8
  20. package/dist/docs-raw/generated/assembly.md +691 -112
  21. package/dist/docs-raw/generated/concepts.md +1225 -1400
  22. package/dist/docs-raw/generated/core.md +464 -1412
  23. package/dist/docs-raw/generated/curves.md +593 -117
  24. package/dist/docs-raw/generated/lib.md +38 -748
  25. package/dist/docs-raw/generated/output.md +139 -245
  26. package/dist/docs-raw/generated/sheet-metal.md +473 -21
  27. package/dist/docs-raw/generated/sketch.md +553 -349
  28. package/dist/docs-raw/generated/viewport.md +345 -303
  29. package/dist/docs-raw/generated/wood.md +104 -0
  30. package/dist/index.html +2 -2
  31. package/dist/sitemap.xml +6 -6
  32. package/dist-cli/chunk-PZ5AY32C.js +10 -0
  33. package/dist-cli/chunk-PZ5AY32C.js.map +1 -0
  34. package/dist-cli/forgecad.js +9435 -5407
  35. package/dist-cli/forgecad.js.map +1 -0
  36. package/dist-cli/solver-FV7TJZGI.js +365 -0
  37. package/dist-cli/solver-FV7TJZGI.js.map +1 -0
  38. package/dist-skill/CONTEXT.md +3186 -7145
  39. package/dist-skill/SKILL-dev.md +21 -63
  40. package/dist-skill/SKILL.md +12 -56
  41. package/dist-skill/docs/API/core/concepts.md +16 -98
  42. package/dist-skill/docs/CLI/export.md +91 -0
  43. package/dist-skill/docs/CLI/projects.md +107 -0
  44. package/dist-skill/docs/CLI/studio_publishing.md +52 -0
  45. package/dist-skill/docs/CLI/validation.md +66 -0
  46. package/dist-skill/docs/generated/assembly.md +691 -112
  47. package/dist-skill/docs/generated/core.md +464 -1412
  48. package/dist-skill/docs/generated/curves.md +593 -117
  49. package/dist-skill/docs/generated/lib.md +38 -748
  50. package/dist-skill/docs/generated/output.md +139 -245
  51. package/dist-skill/docs/generated/sheet-metal.md +473 -21
  52. package/dist-skill/docs/generated/sketch.md +553 -349
  53. package/dist-skill/docs/generated/viewport.md +345 -303
  54. package/dist-skill/docs/generated/wood.md +104 -0
  55. package/dist-skill/docs/guides/coordinate-system.md +11 -17
  56. package/dist-skill/docs/guides/geometry-conventions.md +13 -70
  57. package/dist-skill/docs/guides/modeling-recipes.md +22 -195
  58. package/dist-skill/docs/guides/positioning.md +88 -147
  59. package/dist-skill/docs-dev/API/core/concepts.md +51 -0
  60. package/dist-skill/docs-dev/API/core/sdf-advanced.md +92 -0
  61. package/dist-skill/docs-dev/API/core/sdf-primitives.md +58 -0
  62. package/dist-skill/docs-dev/API/core/sdf-workflow.md +42 -0
  63. package/dist-skill/docs-dev/CLI/export.md +91 -0
  64. package/dist-skill/docs-dev/CLI/projects.md +107 -0
  65. package/dist-skill/docs-dev/CLI/studio_publishing.md +52 -0
  66. package/dist-skill/docs-dev/CLI/validation.md +66 -0
  67. package/dist-skill/{docs → docs-dev}/blueprint-first.md +5 -0
  68. package/dist-skill/{docs → docs-dev}/coding-best-practices.md +6 -8
  69. package/dist-skill/{docs → docs-dev}/coding.md +1 -3
  70. package/dist-skill/docs-dev/generated/assembly.md +771 -0
  71. package/dist-skill/docs-dev/generated/core.md +775 -0
  72. package/dist-skill/docs-dev/generated/curves.md +688 -0
  73. package/dist-skill/docs-dev/generated/lib.md +50 -0
  74. package/dist-skill/docs-dev/generated/output.md +234 -0
  75. package/dist-skill/docs-dev/generated/sheet-metal.md +506 -0
  76. package/dist-skill/docs-dev/generated/sketch.md +801 -0
  77. package/dist-skill/docs-dev/generated/viewport.md +486 -0
  78. package/dist-skill/docs-dev/generated/wood.md +104 -0
  79. package/dist-skill/docs-dev/guides/coordinate-system.md +46 -0
  80. package/dist-skill/docs-dev/guides/geometry-conventions.md +52 -0
  81. package/dist-skill/docs-dev/guides/modeling-recipes.md +77 -0
  82. package/dist-skill/docs-dev/guides/positioning.md +151 -0
  83. package/dist-skill/{docs → docs-dev}/guides/skill-maintenance.md +21 -10
  84. package/dist-skill/{docs → docs-dev}/internals/compiler.md +5 -6
  85. package/dist-skill/{docs → docs-dev}/internals/constraint-solver-quality.md +0 -1
  86. package/dist-skill/{docs → docs-dev}/internals/constraint-solver.md +0 -1
  87. package/dist-skill/{docs → docs-dev}/internals/sketch-2d-pipeline.md +2 -3
  88. package/examples/api/attachTo-basics.forge.js +5 -5
  89. package/examples/api/boolean-operations.forge.js +3 -3
  90. package/examples/api/bounding-box-visualizer.forge.js +2 -2
  91. package/examples/api/clone-duplicate.forge.js +1 -1
  92. package/examples/api/colors-union-vs-array.forge.js +6 -6
  93. package/examples/api/connector-assembly.forge.js +4 -4
  94. package/examples/api/connector-basics.forge.js +2 -2
  95. package/examples/api/extrude-options.forge.js +4 -10
  96. package/examples/api/feature-created-faces.forge.js +6 -10
  97. package/examples/api/fillet-showcase.forge.js +1 -1
  98. package/examples/api/folded-service-panel-cover.forge.js +2 -2
  99. package/examples/api/group-test.forge.js +1 -1
  100. package/examples/api/group-vs-union.forge.js +1 -1
  101. package/examples/api/highlight-debug.forge.js +4 -0
  102. package/examples/api/js-module-pillars.js +1 -1
  103. package/examples/api/js-module-scene.js +2 -2
  104. package/examples/api/mesh-import-slats.forge.js +1 -1
  105. package/examples/api/pointAlong-orientation.forge.js +1 -1
  106. package/examples/api/profile-2020-b-slot6.forge.js +0 -1
  107. package/examples/api/route-perimeter-flange.forge.js +1 -1
  108. package/examples/api/sdf-rover-demo.forge.js +10 -10
  109. package/examples/api/sketch-on-face-demo.forge.js +2 -2
  110. package/examples/api/sketch-regions.forge.js +4 -4
  111. package/examples/api/transition-curves.forge.js +1 -1
  112. package/examples/api/variable-sweep-pure-sdf-test.forge.js +162 -0
  113. package/examples/api/variable-sweep-test.forge.js +2 -2
  114. package/examples/api/wood-joinery.forge.js +60 -0
  115. package/examples/compiler-corpus/enclosure-shell-cuts.forge.js +3 -3
  116. package/examples/compiler-corpus/fastener-plate-variants.forge.js +2 -2
  117. package/examples/experiments/drone-arm.forge.js +53 -0
  118. package/examples/furniture/adjustable-table.forge.js +2 -2
  119. package/examples/furniture/bathroom.forge.js +11 -11
  120. package/examples/furniture/chair.forge.js +1 -1
  121. package/examples/generative/crystal-growth.forge.js +2 -2
  122. package/examples/generative/frost-spires.forge.js +3 -3
  123. package/examples/generative/golden-spiral-tower.forge.js +3 -3
  124. package/examples/mechanical/3d-printer.forge.js +28 -28
  125. package/examples/mechanical/5-finger-robot-hand.forge.js +15 -15
  126. package/examples/mechanical/airplane-propeller.forge.js +2 -2
  127. package/examples/mechanical/fillet-enclosure.forge.js +1 -1
  128. package/examples/mechanical/headphone-hanger-v2.forge.js +2 -2
  129. package/examples/mechanical/robot_hand.forge.js +15 -15
  130. package/examples/mechanical/robot_hand_2.forge.js +9 -9
  131. package/examples/products/bottle.forge.js +1 -1
  132. package/examples/products/chess-set.forge.js +19 -19
  133. package/examples/products/classical-piano.forge.js +11 -11
  134. package/examples/products/clock.forge.js +12 -12
  135. package/examples/products/iphone.forge.js +8 -8
  136. package/examples/products/laptop.forge.js +15 -15
  137. package/examples/products/liquid-soap-dispenser.forge.js +18 -18
  138. package/examples/products/origami-fish.forge.js +8 -6
  139. package/examples/products/spiderman-cake.forge.js +4 -4
  140. package/examples/toolbox/bolted-joint.forge.js +2 -2
  141. package/package.json +7 -4
  142. package/dist/assets/EditorApp-B-vQvgam.js +0 -9888
  143. package/dist/assets/LandingPage-C5n9hDXI.js +0 -322
  144. package/dist/assets/PublishedModelPage-Dt7PCVBj.js +0 -146
  145. package/dist/assets/__vite-browser-external-CURh0WXD.js +0 -8
  146. package/dist/assets/deserializeRunResult-BLAFoiE0.js +0 -19365
  147. package/dist/assets/index-1CYp3zUp.js +0 -1455
  148. package/dist/docs-raw/CLI.md +0 -865
  149. package/dist-skill/docs/API/API.md +0 -1666
  150. package/dist-skill/docs/API/README.md +0 -37
  151. package/dist-skill/docs/API/assembly/assembly.md +0 -617
  152. package/dist-skill/docs/API/core/edge-queries.md +0 -130
  153. package/dist-skill/docs/API/core/parameters.md +0 -122
  154. package/dist-skill/docs/API/core/reserved-terms.md +0 -137
  155. package/dist-skill/docs/API/core/sdf.md +0 -326
  156. package/dist-skill/docs/API/core/skill-cli.md +0 -194
  157. package/dist-skill/docs/API/core/skill-guide.md +0 -205
  158. package/dist-skill/docs/API/core/specs.md +0 -186
  159. package/dist-skill/docs/API/core/topology.md +0 -372
  160. package/dist-skill/docs/API/entities.md +0 -268
  161. package/dist-skill/docs/API/output/bom.md +0 -58
  162. package/dist-skill/docs/API/output/brep-export.md +0 -87
  163. package/dist-skill/docs/API/output/dimensions.md +0 -67
  164. package/dist-skill/docs/API/output/export.md +0 -110
  165. package/dist-skill/docs/API/output/gcode.md +0 -195
  166. package/dist-skill/docs/API/runtime/viewport.md +0 -420
  167. package/dist-skill/docs/API/sheet-metal/sheet-metal.md +0 -185
  168. package/dist-skill/docs/API/sketch/anchor.md +0 -37
  169. package/dist-skill/docs/API/sketch/booleans.md +0 -91
  170. package/dist-skill/docs/API/sketch/core.md +0 -73
  171. package/dist-skill/docs/API/sketch/extrude.md +0 -62
  172. package/dist-skill/docs/API/sketch/on-face.md +0 -104
  173. package/dist-skill/docs/API/sketch/operations.md +0 -78
  174. package/dist-skill/docs/API/sketch/path.md +0 -75
  175. package/dist-skill/docs/API/sketch/primitives.md +0 -146
  176. package/dist-skill/docs/API/sketch/regions.md +0 -80
  177. package/dist-skill/docs/API/sketch/text.md +0 -108
  178. package/dist-skill/docs/API/sketch/transforms.md +0 -65
  179. package/dist-skill/docs/API/toolbox/fasteners.md +0 -129
  180. package/dist-skill/docs/CLI.md +0 -865
  181. package/dist-skill/docs/INDEX.md +0 -94
  182. package/dist-skill/docs/RELEASING.md +0 -55
  183. package/dist-skill/docs/cli-monetization.md +0 -111
  184. package/dist-skill/docs/deployment.md +0 -281
  185. package/dist-skill/docs/generated/concepts.md +0 -2112
  186. package/dist-skill/docs/internals/shape-from-slices.md +0 -152
  187. package/dist-skill/docs/platform/admin.md +0 -45
  188. package/dist-skill/docs/platform/architecture.md +0 -79
  189. package/dist-skill/docs/platform/auth.md +0 -110
  190. package/dist-skill/docs/platform/email.md +0 -67
  191. package/dist-skill/docs/platform/projects.md +0 -111
  192. package/dist-skill/docs/platform/sharing.md +0 -90
  193. package/dist-skill/docs/runbook.md +0 -345
@@ -0,0 +1,775 @@
1
+ ---
2
+ skill-group: core
3
+ skill-order: 100
4
+ ---
5
+
6
+ # Core API
7
+
8
+ > **Auto-generated** from `src/forge/forge-public-api.ts`. Do not edit by hand — run `npm run gen:docs` to regenerate.
9
+
10
+ 3D primitives, boolean operations, transforms, patterns, imports, and parameters.
11
+
12
+ ## Functions
13
+
14
+ ### 3D Primitives
15
+
16
+ #### `box()` — Create a rectangular box. Centered on XY, base at Z=0.
17
+
18
+ For named faces, build from a labeled sketch: `rect(x, y).labelEdges('s', 'e', 'n', 'w').extrude(z, { labels: { start: 'bottom', end: 'top' } })`.
19
+
20
+ `box$1(x: number, y: number, z: number): Shape`
21
+
22
+ #### `cylinder()` — Create a cylinder or cone with named faces and edges. Centered on XY, base at Z=0.
23
+
24
+ When radiusTop differs from radius, creates a tapered cone. Use the segments parameter to create regular prisms (e.g. 6 for a hexagonal prism). Returns a Shape with faces: top, bottom, side; and edges: top-rim, bottom-rim.
25
+
26
+ `cylinder$1(height: number, radius: number, radiusTop?: number, segments?: number): Shape`
27
+
28
+ #### `sphere()` — Create a sphere centered at the origin. Use segments for lower-poly approximations.
29
+
30
+ `sphere$1(radius: number, segments?: number): Shape`
31
+
32
+ #### `torus()` — Create a torus (donut shape) lying in the XY plane. Centered on all axes (origin is the ring center).
33
+
34
+ `torus$1(majorRadius: number, minorRadius: number, segments?: number): Shape`
35
+
36
+ ### Boolean Operations
37
+
38
+ #### `union()` — Combine shapes into a single solid (additive boolean).
39
+
40
+ Accepts individual shapes, or an array of shapes. The first operand's color is preserved in the result.
41
+
42
+ `union(...inputs: ShapeOperandInput[]): Shape`
43
+
44
+ #### `difference()` — Subtract shapes from a base shape (subtractive boolean).
45
+
46
+ The first shape is the base; all subsequent shapes are subtracted from it. Accepts individual shapes, or an array of shapes.
47
+
48
+ `difference(...inputs: ShapeOperandInput[]): Shape`
49
+
50
+ #### `intersection()` — Keep only the overlapping volume of the input shapes (intersection boolean).
51
+
52
+ Requires at least two shapes. Accepts individual shapes, or an array.
53
+
54
+ `intersection(...inputs: ShapeOperandInput[]): Shape`
55
+
56
+ ### Edge Features
57
+
58
+ #### `fillet()` — Apply fillets (rounded edges) to one or more edges of a shape.
59
+
60
+ **Details**
61
+
62
+ Works on both straight and curved edges. Supports OCCT and Manifold backends. When using OCCT, all edges are filleted in a single kernel operation for best quality. When using Manifold, edges are filleted sequentially.
63
+
64
+ The `edges` parameter is flexible: - Omit to fillet **all** sharp edges - Pass an `EdgeQuery` for an inline filter (most common) - Pass an `EdgeSegment` or `EdgeSegment[]` from `selectEdges()` for pre-selected edges
65
+
66
+ Throws if no edges match the selection, or if `radius` is not a positive finite number.
67
+
68
+ **Example**
69
+
70
+ ```ts
71
+ // Fillet all edges
72
+ fillet(myShape, 2)
73
+
74
+ // Fillet only top convex edges
75
+ fillet(myShape, 1.5, { atZ: 20, convex: true })
76
+
77
+ // Fillet vertical edges selected beforehand
78
+ const edges = selectEdges(myShape, { parallel: [0, 0, 1] })
79
+ fillet(myShape, 3, edges)
80
+ ```
81
+
82
+ `fillet(shape: Shape, radius: number, edges?: EdgeSelector, segments?: number): Shape`
83
+
84
+ #### `chamfer()` — Apply chamfers (beveled edges) to one or more edges of a shape.
85
+
86
+ **Details**
87
+
88
+ Produces a 45° bevel at the specified `size` (distance from edge). Works on both straight and curved edges. Supports OCCT and Manifold backends.
89
+
90
+ The `edges` parameter accepts the same options as `fillet()`: inline `EdgeQuery`, pre-selected `EdgeSegment`/`EdgeSegment[]`, or `undefined` (all sharp edges).
91
+
92
+ **Example**
93
+
94
+ ```ts
95
+ // Chamfer all edges
96
+ chamfer(myShape, 1)
97
+
98
+ // Chamfer only vertical edges
99
+ chamfer(myShape, 2, { parallel: [0, 0, 1] })
100
+ ```
101
+
102
+ `chamfer(shape: Shape, size: number, edges?: EdgeSelector): Shape`
103
+
104
+ #### `draft()` — Apply a draft angle (taper) to vertical faces for mold extraction.
105
+
106
+ **Details**
107
+
108
+ 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°.
109
+
110
+ Requires the OCCT backend. Throws on Manifold.
111
+
112
+ **Example**
113
+
114
+ ```ts
115
+ // Add 3° draft to a box for injection molding
116
+ draft(myBox, 3)
117
+
118
+ // Draft with custom pull direction and neutral plane
119
+ draft(myShape, 2, [0, 0, 1], 10)
120
+ ```
121
+
122
+ `draft(shape: Shape, angleDeg: number, pullDirection?: [ number, number, number ], neutralPlaneOffset?: number): Shape`
123
+
124
+ #### `offsetSolid()` — Uniformly offset all surfaces of a solid inward or outward.
125
+
126
+ **Details**
127
+
128
+ 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.
129
+
130
+ Requires the OCCT backend. Throws on Manifold.
131
+
132
+ **Example**
133
+
134
+ ```ts
135
+ // Grow a box outward by 1mm on all sides
136
+ offsetSolid(myBox, 1)
137
+
138
+ // Shrink a shape inward by 0.5mm
139
+ offsetSolid(myShape, -0.5)
140
+ ```
141
+
142
+ `offsetSolid(shape: Shape, thickness: number): Shape`
143
+
144
+ ### Patterns & Layout
145
+
146
+ #### `selectEdges()` — Select all edges from a shape that match the given query.
147
+
148
+ **Details**
149
+
150
+ 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.
151
+
152
+ Works on any shape — primitives, booleans, shells, and imported meshes. Use this when tracked topology is unavailable (e.g. after a difference or on imported geometry). For simpler cases, pass an `EdgeQuery` directly to `fillet()` or `chamfer()` instead of calling `selectEdges` separately.
153
+
154
+ **Example**
155
+
156
+ ```ts
157
+ // Fillet all top edges of a box
158
+ const topEdges = selectEdges(part, { atZ: 20, perpendicular: [0, 0, 1] });
159
+ let result = part;
160
+ for (const edge of coalesceEdges(topEdges)) {
161
+ result = fillet(result, 2, edge);
162
+ }
163
+ ```
164
+
165
+ `selectEdges(shape: Shape, query?: EdgeQuery): EdgeSegment[]`
166
+
167
+ **`EdgeQuery`**
168
+
169
+ | Option | Type | Description |
170
+ |--------|------|-------------|
171
+ | `near?` | `Vec3` | Sort by proximity to this point (closest first). When used with `selectEdge`, picks the closest match. |
172
+ | `parallel?` | `Vec3` | Filter: edge direction approximately parallel to this vector. |
173
+ | `perpendicular?` | `Vec3` | Filter: edge direction approximately perpendicular to this vector. |
174
+ | `convex?` | `boolean` | Filter: only convex (outside corner) edges. |
175
+ | `concave?` | `boolean` | Filter: only concave (inside corner) edges. |
176
+ | `minAngle?` | `number` | Filter: minimum dihedral angle in degrees. |
177
+ | `maxAngle?` | `number` | Filter: maximum dihedral angle in degrees. |
178
+ | `minLength?` | `number` | Filter: minimum edge length. |
179
+ | `maxLength?` | `number` | Filter: maximum edge length. |
180
+ | `within?` | `BoundingRegion` | Filter: edge midpoint must be within this bounding region. |
181
+ | `atZ?` | `number` | Shorthand: edge midpoint Z ≈ this value (within `tolerance`). Equivalent to `within: { zMin: atZ - tol, zMax: atZ + tol }`. |
182
+ | `tolerance?` | `number` | Position tolerance for approximate matches (default: `1.0`). Used by `atZ` and `near`. |
183
+ | `angleTolerance?` | `number` | Angular tolerance in degrees for `parallel`/`perpendicular` filters (default: `10`). |
184
+
185
+ `BoundingRegion`: `{ xMin?: number, xMax?: number, yMin?: number, yMax?: number, zMin?: number, zMax?: number }`
186
+
187
+ **`EdgeSegment`**
188
+
189
+ | Option | Type | Description |
190
+ |--------|------|-------------|
191
+ | `index` | `number` | Stable index within the extraction (deterministic for a given mesh). |
192
+ | `direction` | `Vec3` | Normalized direction from start → end. |
193
+ | `dihedralAngle` | `number` | Dihedral angle in degrees (0 = coplanar, 180 = knife edge). |
194
+ | `convex` | `boolean` | true = outside corner (convex), false = inside corner (concave). |
195
+ | `normalA` | `Vec3` | Normal of first adjacent face. |
196
+ | `normalB` | `Vec3` | Normal of second adjacent face (same as normalA for boundary edges). |
197
+ | `boundary` | `boolean` | true if this is a boundary (unmatched) edge — unusual for closed solids. |
198
+ | `start`, `end`, `midpoint`, `length` | | — |
199
+
200
+ #### `selectEdge()` — Select the single best-matching edge from a shape.
201
+
202
+ **Details**
203
+
204
+ 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.
205
+
206
+ **Example**
207
+
208
+ ```ts
209
+ // Chamfer one specific edge near a known point
210
+ const bottomEdge = selectEdge(part, { near: [25, 0, 0], atZ: 0 });
211
+ result = chamfer(result, 1.5, bottomEdge);
212
+ ```
213
+
214
+ `selectEdge(shape: Shape, query?: EdgeQuery): EdgeSegment`
215
+
216
+ #### `coalesceEdges()` — Merge collinear edge segments into longer logical edges.
217
+
218
+ **Details**
219
+
220
+ 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.
221
+
222
+ The `tolerance` controls the maximum perpendicular distance from collinearity before two segments are considered non-collinear. Default: `0.01`.
223
+
224
+ **Example**
225
+
226
+ ```ts
227
+ const topEdges = selectEdges(part, { atZ: 20 });
228
+ for (const edge of coalesceEdges(topEdges)) {
229
+ result = fillet(result, 2, edge);
230
+ }
231
+ ```
232
+
233
+ `coalesceEdges(segments: EdgeSegment[], tolerance?: number): EdgeSegment[]`
234
+
235
+ #### `filletCorners()` — Create a polygon from points with specific corners rounded to arc fillets.
236
+
237
+ **Details**
238
+
239
+ Each corner spec identifies a vertex by its index in the `points` array and the desired fillet `radius`. Both convex and concave corners are supported.
240
+
241
+ Constraints: - Collinear corners cannot be filleted (throws an error) - Two neighboring fillets whose tangent lengths overlap the same edge will throw - Radius must be positive and small enough to fit within the adjacent edge lengths
242
+
243
+ Use `offset(-r).offset(+r)` instead if you want to round **all** convex corners uniformly. Use `filletCorners` when you need selective or mixed sharp/rounded profiles.
244
+
245
+ **Example**
246
+
247
+ ```ts
248
+ const roof = filletCorners(roofPoints, [
249
+ { index: 3, radius: 19 },
250
+ { index: 4, radius: 19 },
251
+ { index: 5, radius: 19 },
252
+ ]);
253
+ ```
254
+
255
+ `filletCorners(points: PointInput[], corners: FilletCornerSpec[]): Sketch`
256
+
257
+ `FilletCornerSpec`: `{ index: number, radius: number, segments?: number }`
258
+
259
+ #### `circularLayout()` — Compute evenly-spaced positions around a circle.
260
+
261
+ Eliminates the most common trig pattern in CAD scripts:
262
+
263
+ ```js
264
+ // Before — manual trig
265
+ for (let i = 0; i < 12; i++) {
266
+ const angle = i * 30 * Math.PI / 180;
267
+ markers.push(marker.translate(r * Math.cos(angle), r * Math.sin(angle), 0));
268
+ }
269
+
270
+ // After — declarative
271
+ for (const {x, y} of circularLayout(12, r)) {
272
+ markers.push(marker.translate(x, y, 0));
273
+ }
274
+ ```
275
+
276
+ `circularLayout(count: number, radius: number, options?: CircularLayoutOptions): LayoutPoint[]`
277
+
278
+ **`CircularLayoutOptions`**
279
+ - `startDeg?: number` — Angle of the first element in degrees (default: 0 = +X axis).
280
+ - `centerX?: number` — Center X coordinate (default: 0).
281
+ - `centerY?: number` — Center Y coordinate (default: 0).
282
+
283
+ `LayoutPoint`: `{ x: number, y: number }`
284
+
285
+ #### `polygonVertices()` — Compute the vertex positions of a regular polygon.
286
+
287
+ Default orientation places the first vertex at the top (90 degrees), matching the convention used by `ngon()`.
288
+
289
+ Eliminates manual Math.sqrt(3) for triangles, pentagon vertex math, etc:
290
+
291
+ ```js
292
+ // Before — manual equilateral triangle
293
+ const v1 = [center.x - r/2, center.y + r * Math.sqrt(3)/2];
294
+ const v2 = [center.x - r/2, center.y - r * Math.sqrt(3)/2];
295
+ const v3 = [center.x + r, center.y];
296
+
297
+ // After — declarative
298
+ const [v1, v2, v3] = polygonVertices(3, r);
299
+ ```
300
+
301
+ `polygonVertices(sides: number, radius: number, options?: PolygonVerticesOptions): LayoutPoint[]`
302
+
303
+ **`PolygonVerticesOptions`**
304
+ - `startDeg?: number` — Angle of the first vertex in degrees (default: 90 = top).
305
+ - `centerX?: number` — Center X coordinate (default: 0).
306
+ - `centerY?: number` — Center Y coordinate (default: 0).
307
+
308
+ #### `linearPattern()` — Repeat a shape in a linear pattern along a direction vector and union the copies.
309
+
310
+ **Details**
311
+
312
+ 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.
313
+
314
+ **Example**
315
+
316
+ ```ts
317
+ // 5 cylinders, 20mm apart along X
318
+ linearPattern(cylinder(10, 3), 5, 20, 0)
319
+ ```
320
+
321
+ `linearPattern(shape: Shape, count: number, dx: number, dy: number, dz?: number): Shape`
322
+
323
+ #### `circularPattern()` — Repeat a shape in a circular pattern around an axis and union the copies.
324
+
325
+ **Details**
326
+
327
+ 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.
328
+
329
+ Two calling conventions: - **Simple** (Z axis): `circularPattern(shape, 6)` or `circularPattern(shape, 6, centerX, centerY)` - **Advanced** (arbitrary axis): `circularPattern(shape, 6, { axis, origin })`
330
+
331
+ **Example**
332
+
333
+ ```ts
334
+ // 8 holes evenly spaced around origin
335
+ circularPattern(cylinder(12, 4).translate(30, 0, -1), 8)
336
+
337
+ // Circular pattern around X axis
338
+ circularPattern(myFeature, 4, { axis: [1, 0, 0], origin: [0, 0, 50] })
339
+ ```
340
+
341
+ `circularPattern(shape: Shape, count: number, centerXOrOpts?: number | CircularPatternOptions, centerY?: number): Shape`
342
+
343
+ **`CircularPatternOptions`**
344
+ - `centerX?: number` — Center X of the rotation (default: 0). Used when axis is Z (legacy mode).
345
+ - `centerY?: number` — Center Y of the rotation (default: 0). Used when axis is Z (legacy mode).
346
+
347
+ #### `linearPattern2d()` — Repeat a 2D sketch in a linear pattern and union the copies.
348
+
349
+ `linearPattern2d(sketch: Sketch, count: number, dx: number, dy?: number): Sketch`
350
+
351
+ #### `circularPattern2d()` — Repeat a 2D sketch in a circular pattern around a center point and union the copies.
352
+
353
+ `circularPattern2d(sketch: Sketch, count: number, centerXOrOpts?: number | { centerX?: number; centerY?: number; startDeg?: number; }, centerY?: number): Sketch`
354
+
355
+ #### `mirrorCopy()` — Mirror a shape across a plane and union the mirror with the original.
356
+
357
+ **Details**
358
+
359
+ 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.
360
+
361
+ **Example**
362
+
363
+ ```ts
364
+ // Mirror across the YZ plane (X=0)
365
+ mirrorCopy(box(50, 30, 10), [1, 0, 0])
366
+ ```
367
+
368
+ `mirrorCopy(shape: Shape, normal: [ number, number, number ]): Shape`
369
+
370
+ ### Imports & Composition
371
+
372
+ #### `require()` — Import a module with optional ForgeCAD parameter overrides. Returns the module's exports.
373
+
374
+ `require$1(path: string, paramOverrides?: Record<string, number>): any`
375
+
376
+ #### `importSvgSketch()` — Parse an SVG file and return it as a Sketch with options for region filtering, scaling, and simplification.
377
+
378
+ `importSvgSketch(fileName: string, options?: SvgImportOptions): Sketch`
379
+
380
+ **`SvgImportOptions`**
381
+
382
+ | Option | Type | Description |
383
+ |--------|------|-------------|
384
+ | `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 |
385
+ | `regionSelection?` | `"all" | "largest"` | Keep all disconnected regions, or only the largest. |
386
+ | `maxRegions?` | `number` | Keep at most this many regions (largest-first). |
387
+ | `minRegionArea?` | `number` | Drop regions below this absolute area threshold. |
388
+ | `minRegionAreaRatio?` | `number` | Drop regions below this ratio of largest-region area. |
389
+ | `flattenTolerance?` | `number` | Curve flattening tolerance in SVG user units. Smaller = more segments, higher fidelity. |
390
+ | `arcSegments?` | `number` | Minimum segment count for arc discretization. |
391
+ | `scale?` | `number` | Global scale applied after SVG parsing. |
392
+ | `maxWidth?` | `number` | Maximum imported sketch width. If exceeded, geometry is uniformly downscaled to fit. |
393
+ | `maxHeight?` | `number` | Maximum imported sketch height. If exceeded, geometry is uniformly downscaled to fit. |
394
+ | `centerOnOrigin?` | `boolean` | Recenter imported geometry so its 2D bounds center is at CAD origin. |
395
+ | `simplify?` | `number` | Simplification tolerance for final sketch cleanup. |
396
+ | `invertY?` | `boolean` | Flip SVG Y-down coordinates to CAD Y-up. Enabled by default. |
397
+
398
+ #### `importMesh()` — Import an external mesh file (STL, OBJ, 3MF) as a Shape.
399
+
400
+ `importMesh(fileName: string, options?: { scale?: number; center?: boolean; }): Shape`
401
+
402
+ ### Parameters
403
+
404
+ #### `param()` — Declare a numeric parameter that renders as a slider in the UI.
405
+
406
+ **Details**
407
+
408
+ Each `param()` 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.
409
+
410
+ Default range rules when options are omitted: - `min` defaults to `0` - `max` defaults to `defaultValue * 4` - `step` is auto-calculated: `1` for integer params, `0.1` for ranges ≤ 100, `1` for larger ranges
411
+
412
+ The `unit` option is cosmetic only — no conversion is performed. Use `integer: true` for counts, sides, quantities (rounds to whole numbers; step defaults to `1`).
413
+
414
+ **Example**
415
+
416
+ ```ts
417
+ const width = param("Width", 50);
418
+ const angle = param("Angle", 45, { min: 0, max: 180, unit: "°" });
419
+ const sides = param("Sides", 6, { min: 3, max: 12, integer: true });
420
+ ```
421
+
422
+ **Parameter overrides** — key must match `name` exactly:
423
+
424
+ ```ts
425
+ // Via require()
426
+ const bracket = require("./bracket.forge.js", { Width: 80 });
427
+
428
+ // Via CLI
429
+ // forgecad run model.forge.js --param "Wall Thickness=3"
430
+ ```
431
+
432
+ `param(name: string, defaultValue: number, opts?: { min?: number; max?: number; step?: number; unit?: string; integer?: boolean; reverse?: boolean; }): number`
433
+
434
+ #### `boolParam()` — Declare a boolean parameter that renders as a checkbox in the UI.
435
+
436
+ **Details**
437
+
438
+ 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.
439
+
440
+ **Example**
441
+
442
+ ```ts
443
+ const showHoles = boolParam("Show Holes", true);
444
+ if (showHoles) return difference(plate, cylinder(10, 5).translate(50, 30, 0));
445
+ return plate;
446
+ ```
447
+
448
+ Override via import:
449
+
450
+ ```ts
451
+ const pan = require("./pan.forge.js", { "Show Lid": 0 });
452
+ ```
453
+
454
+ `boolParam(name: string, defaultValue: boolean): boolean`
455
+
456
+ #### `choiceParam()` — Declare a choice parameter that renders as a dropdown in the UI.
457
+
458
+ **Details**
459
+
460
+ `defaultValue` must exactly match one entry in `choices`. Returns the selected string label. Prefer `choiceParam` over `param` when a slider would hide intent — named choices like `"wok"` are self-describing.
461
+
462
+ Overrides may be passed as the choice label string (preferred) or as a numeric index. The `name` string is the override key.
463
+
464
+ **Example**
465
+
466
+ ```ts
467
+ const panStyle = choiceParam("Pan Style", "frying-pan", ["frying-pan", "saute-pan", "wok"]);
468
+ if (panStyle === "wok") return buildWok();
469
+ ```
470
+
471
+ Override via import:
472
+
473
+ ```ts
474
+ const pan = require("./pan.forge.js", { "Pan Style": "wok" });
475
+ ```
476
+
477
+ Override via CLI:
478
+
479
+ ```bash
480
+ forgecad run model.forge.js --param "Pan Style=wok"
481
+ ```
482
+
483
+ `choiceParam(name: string, defaultValue: string, choices: string[]): string`
484
+
485
+ #### `listParam()` — Declare a list parameter — an array of struct items with per-field UI controls.
486
+
487
+ **Details**
488
+
489
+ 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.
490
+
491
+ Field types: - Boolean fields (`boolean: true` in field defs) return as `boolean` - Choice fields (`choices: [...]` in field defs) return as `string` - All other fields return as `number`
492
+
493
+ `listParam<T extends Record<string, number | boolean | string>>(name: string, defaultItems: T[], opts: { ... }): T[]`
494
+
495
+ `ListParamFieldDef`: `{ min?: number, max?: number, step?: number, unit?: string, integer?: boolean, boolean?: boolean, choices?: string[] }`
496
+
497
+ ### Grouping & Local Coordinates
498
+
499
+ #### `group()` — Group multiple shapes/sketches for joint transforms without merging into a single mesh.
500
+
501
+ Unlike union(), 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.).
502
+
503
+ **Local coordinate pattern:** Build child parts at the origin (local coordinates), then group and translate once to place the whole assembly. This eliminates the error-prone pattern of manually adding parent offsets to every sub-part.
504
+
505
+ // BAD — every sub-part repeats the parent's global offset const unitX = 0, unitY = -18, unitZ = 70; const body = roundedBox(100, 20, 32, 4).translate(unitX, unitY, unitZ); const panel = box(98, 2, 18).translate(unitX, unitY - 12, unitZ + 4); const louver = box(88, 2, 6).translate(unitX, unitY - 14, unitZ - 11);
506
+
507
+ // GOOD — build at origin, group, translate once const body = roundedBox(100, 20, 32, 4); const panel = box(98, 2, 18).translate(0, -12, 4); const louver = box(88, 2, 6).translate(0, -14, -11); const indoorUnit = group( { name: 'Body', shape: body }, { name: 'Panel', shape: panel }, { name: 'Louver', shape: louver }, ).translate(0, -18, 70);
508
+
509
+ `group(...items: GroupInput[]): ShapeGroup`
510
+
511
+ ### Section & Projection
512
+
513
+ #### `intersectWithPlane()` — Cross-section: slice a 3D shape with a plane and return the intersection as a 2D Sketch.
514
+
515
+ `intersectWithPlane(shape: Shape, plane: PlaneSpec): Sketch`
516
+
517
+ #### `faceProfile()` — Extract the boundary profile of a named face as a 2D sketch.
518
+
519
+ 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.
520
+
521
+ `faceProfile(shape: Shape, face: FaceSelector): Sketch`
522
+
523
+ #### `projectToPlane()` — Orthographically project a 3D shape onto a plane and return the silhouette as a 2D Sketch.
524
+
525
+ `projectToPlane(shape: Shape, plane: PlaneSpec): Sketch`
526
+
527
+ ### Transforms
528
+
529
+ #### `composeChain()` — Compose transforms in chain order. Equivalent to Transform.identity().mul(a).mul(b).mul(c)...
530
+
531
+ `composeChain(...steps: TransformInput[]): Transform`
532
+
533
+ ### Verification
534
+
535
+ #### `spec()` — Create a named, reusable bundle of verification checks.
536
+
537
+ **Details**
538
+
539
+ 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.
540
+
541
+ Specs can be defined in separate `.forge.js` files and imported via `require()` to share them across models.
542
+
543
+ `spec.check()` returns a `SpecResult` — you can inspect it programmatically or ignore the return value and let the Checks panel show results.
544
+
545
+ **Example**
546
+
547
+ ```ts
548
+ const printable = spec("Fits printer bed", (shape) => {
549
+ verify.notEmpty("Has geometry", shape);
550
+ const bb = shape.boundingBox();
551
+ verify.lessThan("Width < 220mm", bb.max[0] - bb.min[0], 220);
552
+ verify.lessThan("Depth < 220mm", bb.max[1] - bb.min[1], 220);
553
+ verify.lessThan("Height < 250mm", bb.max[2] - bb.min[2], 250);
554
+ });
555
+
556
+ // Reuse on multiple shapes
557
+ printable.check(bracket);
558
+ printable.check(standoff);
559
+
560
+ // Check relationships between parts
561
+ const fitSpec = spec("Assembly fit", (partA, partB) => {
562
+ verify.notColliding("No interference", partA, partB, 10);
563
+ });
564
+ fitSpec.check(bracket, standoff);
565
+ ```
566
+
567
+ **Spec-first workflow:** Write specs before building geometry. Checks go from red to green as you build — effectively TDD for CAD.
568
+
569
+ `spec(name: string, checkFn: (...args: any[]) => void): Spec`
570
+
571
+ **`Spec`**
572
+ - `name: string` — The display name of this spec
573
+
574
+ ---
575
+
576
+ ## Classes
577
+
578
+ ### `Shape`
579
+
580
+ Core 3D solid shape. All operations are immutable and return new shapes.
581
+
582
+ Supports transforms (translate, rotate, scale, mirror, transform, rotateAround, pointAlong), booleans (add, subtract, intersect), cutting (split, splitByPlane, trimByPlane), shelling, anchor positioning (attachTo, onFace), placement references, and queries (volume, surfaceArea, boundingBox, isEmpty, numTri, geometryInfo).
583
+
584
+ **Properties:**
585
+
586
+ | Property | Type | Description |
587
+ |----------|------|-------------|
588
+ | `materialProps` | `ShapeMaterialProps | undefined` | — |
589
+
590
+ **Methods:**
591
+
592
+ - `color(value: string | undefined): Shape` — Set the color of this shape (hex string, e.g. "#ff0000"). Returns a new Shape with the color applied.
593
+ - `material(props: ShapeMaterialProps): Shape` — Set PBR material properties for this shape's visual appearance. **Details** 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. Use `.color()` to set the base diffuse color; `.material()` controls how that color behaves under light (metalness, roughness, clearcoat) and can add emissive glow independent of lighting. Emissive glow pairs naturally with the `postProcessing.bloom` effect in `scene()`. **Example** ```js box(50, 50, 50).material({ metalness: 0.9, roughness: 0.1 }); // polished metal sphere(30).material({ emissive: '#ff6b35', emissiveIntensity: 2 }); // glowing cylinder(40, 20).material({ opacity: 0.4, clearcoat: 1.0, clearcoatRoughness: 0.02 }); // ice // Chainable with other shape methods box(100, 100, 10).color('#gold').material({ metalness: 0.95, roughness: 0.05 }).translate(0, 0, 50); ```
594
+ - `clone(): Shape` — Return a new Shape wrapper for explicit duplication in scripts.
595
+ - `geometryInfo(): GeometryInfo` — Inspect which backend/representation produced this solid.
596
+ - `withReferences(refs: PlacementReferenceInput): Shape` — Attach named placement references that survive normal transforms and imports.
597
+ - `referenceNames(kind?: PlacementReferenceKind): string[]` — List named placement references carried by this shape.
598
+ - `withPorts(ports: Record<string, PortInput>): Shape` — Deprecated alias for `withConnectors()`.
599
+ - `portNames(): string[]` — Deprecated alias for `connectorNames()`.
600
+ - `referencePoint(ref: PlacementAnchorLike): [ number, number, number ]` — Resolve a named placement reference or built-in anchor to a 3D point.
601
+ - `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. **Details** `.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. 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. 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. **Example** ```ts // Edge labels become side face names after extrude const profile = path() .moveTo(0, 0) .lineTo(100, 0).label('floor') .lineTo(100, 50).label('wall') .lineTo(0, 50).label('ceiling') .closeLabel('left-wall'); const room = profile.extrude(30, { labels: { start: 'base', end: 'top' } }); room.face('floor'); // side face from the labeled edge room.face('base'); // base cap (user-specified) // .labelEdges() shorthand for sequential edge labeling const plate = rect(100, 50).labelEdges('south', 'east', 'north', 'west'); const solid = plate.extrude(20, { labels: { start: 'bottom', end: 'top' } }); solid.face('south'); // side face // Prefix before combining to avoid collisions const left = wing.prefixLabels('l/'); const right = wing.mirror([1, 0, 0]).prefixLabels('r/'); const full = union(left, right); full.face('l/upper'); // left wing upper surface ```
602
+ - `faces(query?: FaceQuery): FaceRef[]` — Return all faces matching a query, or all mesh-detected faces when no query is given.
603
+ - `faceNames(): string[]` — List defended semantic face names currently available on this shape, including user labels from `labelFaces()`.
604
+ - `prefixLabels(prefix: string): Shape` — Prefix all user-authored face labels (both sketch-edge labels and labelFaces labels). Returns a new shape with modified labels.
605
+ - `renameLabel(from: string, to: string): Shape` — Rename a single face label. Returns a new shape.
606
+ - `dropLabels(...names: string[]): Shape` — Remove specific face labels. Returns a new shape.
607
+ - `dropAllLabels(): Shape` — Remove all face labels. Returns a new shape.
608
+ - `edge(name: string): EdgeRef` — Get a named topology edge. Only available on shapes with tracked topology (from box/cylinder/extrude).
609
+ - `edgeNames(): string[]` — List named topology edge names. Returns empty array if shape has no tracked topology.
610
+ - `labelFaces(mapping: Record<string, string>): Shape` — Assign user-chosen labels to faces identified by their canonical position keys. **Details** Primitives (`box`, `cylinder`) and extrusions have internal canonical face positions (`top`, `bottom`, `side`, `side-left`, etc.) but these are **not** labels — they are just position selectors. Use `labelFaces()` to give faces meaningful, project-specific names that survive through transforms, booleans, fillets, and chamfers. The mapping keys are canonical position selectors; the values are your labels. Labels are the recommended way to identify faces for topological edge queries (`edgesOf`, `edgesBetween`). **Example** ```js // Full workflow: label → query edges → fillet let plate = box(100, 60, 5).labelFaces({ top: 'work-surface', bottom: 'mount-face' }) plate = fillet(plate, 2, plate.edgesOf('work-surface')) // Cylinder: fillet the rim where cap meets barrel let tube = cylinder(30, 10).labelFaces({ top: 'cap', side: 'barrel' }) tube = fillet(tube, 1, tube.edgesBetween('cap', 'barrel')) // Prefix before combining shapes to avoid label collisions const left = plate.prefixLabels('l/') const right = plate.mirror([1, 0, 0]).prefixLabels('r/') const full = union(left, right) full.edgesOf('l/work-surface') // still works ```
611
+ - `edgesOf(faceLabel: string, options?: EdgesOfOptions): EdgeSegment[]` — Return all boundary edges of a named face. **Details** 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()`. This is a topological query — no coordinates, no tolerances, no minimum-length hacks. It works because an edge is the boundary between two faces. **Example** ```js // Fillet all top edges of a mounting plate let plate = box(120, 80, 6).labelFaces({ top: 'work-surface' }) plate = fillet(plate, 3, plate.edgesOf('work-surface')) // Shelled enclosure — fillet the outer lip let body = box(80, 50, 35).labelFaces({ top: 'opening' }) body = body.shell(2, { openFaces: ['top'] }) body = fillet(body, 1.5, body.edgesOf('opening')) // Filter: only concave edges (after a boolean subtraction) body.edgesOf('top', { concave: true }) ```
612
+ - `edgesBetween(faceA: string, faceB: string | string[]): EdgeSegment[]` — Return edges shared between two named faces. **Details** 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." The second argument can be a single face name or an array (edges between A and any of B1, B2, ...). **Example** ```js // Fillet the edge where lid meets one wall let body = box(100, 60, 30).labelFaces({ top: 'lid', 'side-left': 'wall' }) body = fillet(body, 2, body.edgesBetween('lid', 'wall')) // Fillet a cylinder rim — where the flat cap meets the curved barrel let tube = cylinder(30, 10).labelFaces({ top: 'cap', side: 'barrel' }) tube = fillet(tube, 1, tube.edgesBetween('cap', 'barrel')) // Multiple target faces at once body.edgesBetween('lid', ['left-wall', 'right-wall', 'front-wall', 'back-wall']) ```
613
+ - `faceHistory(name: string): FaceTransformationHistory` — Get the transformation history for a specific face.
614
+ - `placeReference(ref: PlacementAnchorLike, target: [ number, number, number ], offset?: [ number, number, number ]): Shape` — Translate the shape so the given anchor or reference lands on the target coordinate. Accepts any built-in anchor name (`'bottom'`, `'center'`, `'top-front-left'`, etc.) or a custom placement reference attached via `withReferences()`. ```javascript // Ground a shape — put its bottom face center at Z = 0 shape.placeReference('bottom', [0, 0, 0]) // Center at the world origin shape.placeReference('center', [0, 0, 0]) // Align left edge to X = 10 shape.placeReference('left', [10, 0, 0]) ```
615
+ - `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. Example: `shape.translatePolar(50, 30)` moves 50mm at 30 degrees from +X.
616
+ - `translate(x: number, y: number, z: number): Shape` — Move the shape relative to its current position. All transforms are immutable and return new shapes.
617
+ - `moveTo(x: number, y: number, z: number): Shape` — Position the shape so its bounding box min corner is at the given global coordinate.
618
+ - `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).
619
+ - `rotate(axis: [ number, number, number ], angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape` — Rotate around an arbitrary axis through the origin.
620
+ - `rotateX(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape` — Rotate around the X axis by the given angle in degrees.
621
+ - `rotateY(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape` — Rotate around the Y axis by the given angle in degrees.
622
+ - `rotateZ(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): Shape` — Rotate around the Z axis by the given angle in degrees.
623
+ - `transform(m: Mat4 | Transform): Shape` — Apply a 4x4 affine transform matrix (column-major) or a Transform object.
624
+ - `scale(v: number | [ number, number, number ]): Shape` — Scale the shape uniformly or per-axis from the shape's bounding box center. Accepts a single number or [x, y, z] array.
625
+ - `scaleAround(pivot: [ number, number, number ], v: number | [ number, number, number ]): Shape` — Scale the shape uniformly or per-axis from an explicit pivot point.
626
+ - `mirror(normal: [ number, number, number ]): Shape` — Mirror across a plane through the shape's bounding box center, defined by its normal vector.
627
+ - `mirrorThrough(point: [ number, number, number ], normal: [ number, number, number ]): Shape` — Mirror across a plane through an explicit point, defined by its normal vector.
628
+ - `pointAlong(direction: [ number, number, number ]): 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. Example: cylinder(40, 5).pointAlong([1, 0, 0]) — lays cylinder along X, starting at origin
629
+ - `rotateAroundTo(axis: [ number, number, number ], pivot: [ number, number, number ], 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.
630
+ - `add(...others: ShapeOperandInput[]): Shape` — Union this shape with others (additive boolean). Method form of union().
631
+ - `subtract(...others: ShapeOperandInput[]): Shape` — Subtract other shapes from this one. Method form of difference().
632
+ - `intersect(...others: ShapeOperandInput[]): Shape` — Keep only the overlap with other shapes. Method form of intersection().
633
+ - `split(cutter: Shape | { toShape(): Shape; }): [ Shape, Shape ]` — Split into [inside, outside] by another shape.
634
+ - `splitByPlane(normal: [ number, number, number ], originOffset?: number): [ Shape, Shape ]` — Split by infinite plane. Returns [positive-side, negative-side].
635
+ - `trimByPlane(normal: [ number, number, number ], originOffset?: number): Shape` — Keep the positive side of the plane and discard the opposite side.
636
+ - `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).
637
+ - `boundingBox(): ShapeRuntimeBounds` — Get the axis-aligned bounding box as { min: [x,y,z], max: [x,y,z] }.
638
+ - `volume(): number` — Volume in mm cubed.
639
+ - `surfaceArea(): number` — Surface area in mm squared.
640
+ - `isEmpty(): boolean` — True if the shape contains no geometry.
641
+ - `numBodies(): number` — Number of disconnected solid bodies in this shape.
642
+ - `numTri(): number` — Triangle count of the mesh representation.
643
+ - `getMesh(): ShapeRuntimeMesh` — Extract triangle mesh for Three.js rendering
644
+ - `slice(offset?: number): any` — Slice the runtime solid by a plane normal to local Z at the given offset.
645
+ - `project(): any` — Orthographically project the runtime solid onto the local XY plane.
646
+ - `attachTo(target: ShapeAnchorTarget, targetAnchor: PlacementAnchorLike, selfAnchor?: PlacementAnchorLike, offset?: [ number, number, number ]): Shape` — Position this shape relative to another using named 3D anchor points. 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.
647
+ - `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. Think of it like sticking a label on a box surface: - `face` picks which surface ('front', 'back', 'top', etc.) - `u, v` position within that face's 2D plane (from center) - front/back: u = left/right (X), v = up/down (Z) - left/right: u = forward/back (Y), v = up/down (Z) - top/bottom: u = left/right (X), v = forward/back (Y) - `protrude` = how far the child sticks out (positive = outward from face)
648
+ - `seatInto(target: Shape, surface: string, options?: SeatIntoOptions): Shape` — Slide this shape along an axis until a labeled face is embedded in the target body. Position the shape roughly first (translate/rotate), then call seatInto to auto-adjust the penetration depth. No manual coordinate math needed. ```js // Wing root embeds into fuselage — adapts to any fuselage shape wing.translate(0, wingY, 0).seatInto(fuselage, 'root'); // Sensor pod sits flush on fuselage surface pod.translate(0, station, radius + 20).seatInto(fuselage, 'base', { depth: 'flush' }); // Antenna with 3mm gasket standoff mast.translate(0, station, radius + 50).seatInto(fuselage, 'mount', { depth: 'flush', gap: 3 }); ```
649
+ - `seatOver(target: Shape, targetSurface: string, options?: SeatIntoOptions): Shape` — Slide this shape until a target's labeled face is fully covered (inside this shape). The inverse of `seatInto`: instead of embedding *your* face into the target, you move until the *target's* face is embedded inside you. ```js // Nacelle moves up until pylon's bottom face is inside the nacelle nacelle.translate(rough).seatOver(pylon, 'bottom'); // Cap slides down over a post until post's top face is covered cap.translate(rough).seatOver(post, 'top'); ```
650
+ - `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).
651
+ - `connectorNames(): string[]` — List all connector names on this shape.
652
+ - `connectorsByType(type: string): Array<{ name: string; port: PortDef; }>` — Get all connectors of a given type.
653
+ - `connectorDistance(nameA: string, nameB: string): number` — Distance between two connector origins on this shape.
654
+ - `connectorMeasurements(name: string): Record<string, number | string>` — Get measurements metadata from a connector.
655
+ - `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. Overloads: - Single pair: `matchTo(target, selfConn, targetConn, options?)` - Dictionary (same target): `matchTo(target, { selfConn: targetConn, ... }, options?)` - Multi-target: `matchTo([ [target1, selfConn1, targetConn1], ... ], options?)`
656
+ - `pocket(face: FaceSelector, depth: number, opts?: PocketOptions): Shape` — Cut a pocket (cavity) into this solid through the named face. box(100, 100, 20).pocket('top', 8) box(100, 100, 20).pocket('top', 8, { inset: 5 }) box(100, 100, 20).pocket('top', 8, { scale: 0.8 })
657
+ - `boss(face: FaceSelector, height: number, opts?: BossOptions): Shape` — Add a boss (protrusion) from the named face. box(100, 100, 20).boss('top', 5) box(100, 100, 20).boss('top', 10, { scale: 0.6 })
658
+ - `hole(faceOrRef: SketchFaceTarget | FaceRef, opts: ShapeHoleOptions): Shape` — Drill a hole into this solid at a face. box(50, 50, 20).hole('top', { diameter: 8, depth: 10 }) box(50, 50, 20).hole('top', { diameter: 6, counterbore: { diameter: 12, depth: 3 } })
659
+ - `cutout(sketch: Sketch, opts?: ShapeCutoutOptions): Shape` — Cut a profile-shaped pocket through a face using a placed sketch. The sketch must be placed on a face with `Sketch.onFace(...)`. The cut follows the sketch's 2D profile. const profile = circle2d(10).onFace(body, 'top'); body.cutout(profile, { depth: 5 })
660
+
661
+ ### `Transform`
662
+
663
+ - `static identity(): Transform` — Return the identity transform.
664
+ - `static from(input: TransformInput): Transform` — Wrap an existing `Transform` or raw 4x4 matrix as a `Transform`.
665
+ - `static translation(x: number, y: number, z: number): Transform` — Create a translation transform.
666
+ - `static scale(v: number | Vec3): Transform` — Create a uniform or per-axis scale transform.
667
+ - `static rotationAxis(axis: Vec3, angleDeg: number, pivot?: Vec3): Transform` — Create a rotation around an arbitrary axis, optionally about a pivot.
668
+ - `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.
669
+ - `mul(other: TransformInput): Transform` — Compose transforms in chain order: `a.mul(b)` applies `a`, then `b`.
670
+ - `translate(x: number, y: number, z: number): Transform` — Translate after the current transform.
671
+ - `rotateAxis(axis: Vec3, angleDeg: number, pivot?: Vec3): Transform` — Rotate after the current transform.
672
+ - `inverse(): Transform` — Return the inverse transform.
673
+ - `point(p: Vec3): Vec3` — Transform a point using homogeneous coordinates.
674
+ - `vector(v: Vec3): Vec3` — Transform a direction vector without translation.
675
+ - `toArray(): Mat4` — Return the transform as a raw 4x4 matrix array.
676
+
677
+ ### `ShapeGroup`
678
+
679
+ **Properties:**
680
+
681
+ | Property | Type | Description |
682
+ |----------|------|-------------|
683
+ | `children` | `GroupChild[]` | — |
684
+ | `childNames` | `Array<string | undefined>` | — |
685
+
686
+ **Methods:**
687
+
688
+ - `childName(index: number): string | undefined` — Return the optional name of the child at `index`.
689
+ - `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.
690
+ - `clone(): ShapeGroup` — Return a deep-cloned ShapeGroup tree (refs copied).
691
+ - `translate(x: number, y: number, z: number): ShapeGroup` — Move the entire group by (x, y, z). All children move together as a unit.
692
+ - `boundingBox(): { min: [ number, number, number ]; max: [ number, number, number ]; }` — Return the combined 3D bounding box of all children.
693
+ - `moveTo(x: number, y: number, z: number): ShapeGroup` — Move the group so its bounding-box min corner lands at the given coordinate.
694
+ - `moveToLocal(target: Shape | ShapeGroup, x: number, y: number, z: number): ShapeGroup` — Move the group relative to another part's bounding-box min corner.
695
+ - `attachTo(target: Shape | ShapeGroup, targetAnchor: Anchor3D | string, selfAnchor?: Anchor3D, offset?: [ number, number, number ]): ShapeGroup` — Attach this group to a face or anchor on another part. `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.
696
+ - `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.
697
+ - `rotate(axis: [ number, number, number ], angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup` — Rotate the group around an arbitrary axis through the origin.
698
+ - `rotateX(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup` — Rotate the group around the X axis.
699
+ - `rotateY(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup` — Rotate the group around the Y axis.
700
+ - `rotateZ(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup` — Rotate the group around the Z axis.
701
+ - `rotateAroundAxis(axis: [ number, number, number ], angleDeg: number, pivot?: [ number, number, number ]): ShapeGroup` — Rotate around an arbitrary axis, optionally through a pivot point.
702
+ - `rotateAroundTo(axis: [ number, number, number ], pivot: [ number, number, number ], movingPoint: Anchor3D | [ number, number, number ], targetPoint: Anchor3D | [ number, number, number ], 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.
703
+ - `pointAlong(direction: [ number, number, number ]): ShapeGroup` — Reorient the group so its local Z axis points along `direction`.
704
+ - `transform(m: Mat4 | Transform): ShapeGroup` — Apply a 4x4 transform matrix or `Transform` to all 3D children.
705
+ - `scale(v: number | [ number, number, number ]): ShapeGroup` — Scale uniformly or per-axis from the group's bounding-box center.
706
+ - `scaleAround(pivot: [ number, number, number ], v: number | [ number, number, number ]): ShapeGroup` — Scale uniformly or per-axis from an explicit pivot point.
707
+ - `mirror(normal: [ number, number, number ]): ShapeGroup` — Mirror across a plane through the group's bounding-box center.
708
+ - `mirrorThrough(point: [ number, number, number ], normal: [ number, number, number ]): ShapeGroup` — Mirror across a plane through an explicit point.
709
+ - `color(hex: string): ShapeGroup` — Return a copy of the group with the given display color applied to each child.
710
+ - `withReferences(refs: PlacementReferenceInput): ShapeGroup` — Attach named placement references to this group. References survive normal transforms (translate/rotate/scale/mirror/transform). ```javascript const bracket = group( { name: 'Left', shape: leftShape }, { name: 'Right', shape: rightShape }, ).withReferences({ points: { mountCenter: [0, 0, 0] }, }); ```
711
+ - `referenceNames(kind?: PlacementReferenceKind): string[]` — List named placement references carried by this group.
712
+ - `withPorts(ports: Record<string, PortInput>): ShapeGroup` — Backward-compatible alias for `withConnectors()`.
713
+ - `portNames(): string[]` — Backward-compatible alias for `connectorNames()`.
714
+ - `referencePoint(ref: PlacementAnchorLike): [ number, number, number ]` — Resolve a named placement reference or built-in Anchor3D to a 3D point. Named refs take priority over built-in anchors.
715
+ - `placeReference(ref: PlacementAnchorLike, target: [ number, number, number ], offset?: [ number, number, number ]): ShapeGroup` — Translate the group so the given anchor or reference lands on the target coordinate. Accepts any built-in anchor name (`'bottom'`, `'center'`, `'top-front-left'`, etc.) or a custom placement reference attached via `withReferences()`. ```javascript // Ground a group — put its bottom at Z = 0 assembly.placeReference('bottom', [0, 0, 0]) // Use a custom reference from a multi-file part const placed = require('./bracket-assembly.forge.js').group .placeReference('mountCenter', [0, 0, 50]); ```
716
+ - `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).
717
+ - `connectorNames(): string[]` — List all connector names, including "ChildName.connectorName" from named children.
718
+ - `connectorsByType(type: string): Array<{ name: string; port: PortDef; }>` — Get all connectors of a given type, including from named children.
719
+ - `connectorDistance(nameA: string, nameB: string): number` — Distance between two connector origins on this group (supports dotted child paths).
720
+ - `connectorMeasurements(name: string): Record<string, number | string>` — Get measurements metadata from a connector (supports dotted child paths).
721
+ - `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". Overloads: - Single pair: `matchTo(target, selfConn, targetConn, options?)` - Dictionary (same target): `matchTo(target, { selfConn: targetConn, ... }, options?)` - Multi-target: `matchTo([ [target1, selfConn1, targetConn1], ... ], options?)`
722
+
723
+ ---
724
+
725
+ ## Constants
726
+
727
+ ### `ANCHOR3D_NAMES`
728
+
729
+ ### `verify`
730
+
731
+ - `that(label: string, check: () => boolean, message?: string): void` — Custom predicate check.
732
+ - `equal(label: string, actual: number, expected: number, tolerance?: number, message?: string): void` — Check that two numbers are approximately equal (within tolerance).
733
+ - `notEqual(label: string, actual: number, unexpected: number, tolerance?: number, message?: string): void` — Check that two numbers are NOT equal (differ by more than tolerance).
734
+ - `greaterThan(label: string, actual: number, min: number, message?: string): void` — Check that actual > min.
735
+ - `lessThan(label: string, actual: number, max: number, message?: string): void` — Check that actual < max.
736
+ - `inRange(label: string, actual: number, min: number, max: number, message?: string): void` — Check that min <= actual <= max.
737
+ - `centersCoincide(label: string, a: ShapeLike$1, b: ShapeLike$1, tolerance?: number): void` — Check that the bounding-box centers of two shapes coincide within tolerance (mm).
738
+ - `notColliding(label: string, a: ShapeLike$1, b: ShapeLike$1, searchLength?: number): void` — Check that two shapes do not collide (minGap > 0).
739
+ - `minClearance(label: string, a: ShapeLike$1, b: ShapeLike$1, minGap: number, searchLength?: number): void` — Check that a minimum clearance gap exists between two shapes.
740
+ - `parallel(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void` — Check that two face normals are parallel (within toleranceDeg degrees).
741
+ - `perpendicular(label: string, faceA: FaceRefLike, faceB: FaceRefLike, toleranceDeg?: number): void` — Check that two face normals are perpendicular (within toleranceDeg degrees).
742
+ - `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.
743
+ - `faceAt(label: string, face: FaceRefLike, expectedPos: [ number` — Check that a face center lies at a specific position (within toleranceMm).
744
+ - `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.
745
+ - `isEmpty(label: string, shape: ShapeLike$1, message?: string): void` — Check that a shape is empty.
746
+ - `notEmpty(label: string, shape: ShapeLike$1, message?: string): void` — Check that a shape is NOT empty.
747
+ - `volumeApprox(label: string, shape: ShapeLike$1, expected: number, tolerance?: number): void` — Check that a shape's volume is approximately equal to expected (mm³).
748
+ - `areaApprox(label: string, shape: ShapeLike$1, expected: number, tolerance?: number): void` — Check that a shape's surface area is approximately equal to expected (mm²).
749
+ - `boundingBoxSize(label: string, shape: ShapeLike$1, expectedSize: [ number` — Check that a shape's bounding box has approximately the given size.
750
+
751
+ ### `Constraint`
752
+
753
+ - `makeParallel(builder: ConstrainedSketchBuilder, a: LineArg, b: LineArg): ConstrainedSketchBuilder` — Constrain two lines to be parallel.
754
+ - `enforceAngle(builder: ConstrainedSketchBuilder, a: LineArg, b: LineArg, angleDeg: number): ConstrainedSketchBuilder` — Constrain the signed angle from line `a` to line `b`.
755
+ - `horizontal(builder: ConstrainedSketchBuilder, line: LineArg): ConstrainedSketchBuilder` — Constrain a line to be horizontal.
756
+ - `vertical(builder: ConstrainedSketchBuilder, line: LineArg): ConstrainedSketchBuilder` — Constrain a line to be vertical.
757
+ - `equalLength(builder: ConstrainedSketchBuilder, a: LineArg, b: LineArg): ConstrainedSketchBuilder` — Constrain two lines to have equal length.
758
+ - `distance(builder: ConstrainedSketchBuilder, a: PointArg, b: PointArg, value: number): ConstrainedSketchBuilder` — Constrain the distance between two points.
759
+ - `fix(builder: ConstrainedSketchBuilder, pt: PointArg, x: number, y: number): ConstrainedSketchBuilder` — Fix a point at a specific coordinate.
760
+ - `coincident(builder: ConstrainedSketchBuilder, a: PointArg, b: PointArg): ConstrainedSketchBuilder` — Constrain two points to occupy the same location.
761
+ - `perpendicular(builder: ConstrainedSketchBuilder, a: LineArg, b: LineArg): ConstrainedSketchBuilder` — Constrain two lines to be perpendicular.
762
+ - `length(builder: ConstrainedSketchBuilder, line: LineArg, value: number): ConstrainedSketchBuilder` — Constrain the length of a line.
763
+
764
+ ### `Points`
765
+
766
+ - `distance(a: Vec3, b: Vec3): number` — Euclidean distance between two 3D points.
767
+ - `midpoint(a: Vec3, b: Vec3): Vec3` — Center point between two 3D points.
768
+ - `lerp(a: Vec3, b: Vec3, t: number): Vec3` — Linearly interpolate between two 3D points. t=0 returns a, t=1 returns b.
769
+ - `direction(a: Vec3, b: Vec3): Vec3` — Unit direction vector from a to b. Throws if a and b are the same point.
770
+ - `offset(point: Vec3, dir: Vec3, amount: number): Vec3` — Move a point along a direction vector by a given amount.
771
+ - `polar(length: number, angleDeg: number, from?: [ number, number ]): [ number, number ]` — Compute a 2D point at distance and angle (degrees) from an optional origin.
772
+
773
+ ### `connector`
774
+
775
+ Connector factory. Create attachment points: `connector({...})`, `connector.male(type, {...})`, etc.