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
@@ -13,352 +13,515 @@ skill-order: 100
13
13
 
14
14
  ### 2D Sketch Primitives
15
15
 
16
- Create 2D profiles for extrusion and other operations.
16
+ #### `path()` — Create a new `PathBuilder` for tracing a 2D outline point by point.
17
17
 
18
- #### `path()`
18
+ **Details**
19
+
20
+ `PathBuilder` is a fluent API for constructing 2D profiles using a mix of line segments, arcs, bezier curves, and splines. Always start with `.moveTo(x, y)` to set the starting point. Call `.close()` to get a filled `Sketch`, or `.stroke(width)` to thicken an open polyline into a solid profile.
21
+
22
+ Edge labels can be assigned with `.label('name')` after any segment — they propagate through extrusion, revolve, loft, and sweep into named faces on the resulting `Shape`.
23
+
24
+ **Example**
19
25
 
20
26
  ```ts
21
- path(): PathBuilder
27
+ // Closed triangle
28
+ const triangle = path().moveTo(0, 0).lineH(50).lineV(30).close();
29
+
30
+ // L-shaped bracket as a stroke
31
+ const bracket = path().moveTo(0, 0).lineH(50).lineV(-70).lineAngled(20, 235).stroke(4);
32
+
33
+ // Labeled edges for downstream face references
34
+ const slot = path()
35
+ .moveTo(0, 0)
36
+ .lineTo(30, 0).label('bottom')
37
+ .lineTo(30, 10)
38
+ .lineTo(0, 10).label('top')
39
+ .close();
22
40
  ```
23
41
 
24
- Create a path builder for constructing 2D outlines.
42
+ `path(): PathBuilder`
43
+
44
+ #### `stroke()` — Create a stroked polyline sketch from an array of 2D points.
45
+
46
+ `stroke(points: [ number, number ][], width: number, join?: "Round" | "Square"): Sketch`
47
+
48
+ #### `rect()` — Create a 2D rectangle centered at the origin.
25
49
 
26
- #### `stroke()`
50
+ **Example**
27
51
 
28
52
  ```ts
29
- stroke(points: [ number, number ][], width: number, join?: "Round" | "Square"): Sketch
53
+ rect(40, 20).extrude(5);
30
54
  ```
31
55
 
32
- Create a stroked polyline sketch from an array of 2D points.
56
+ `rect(width: number, height: number): Sketch`
33
57
 
34
- #### `rect()`
58
+ #### `circle2d()` — Create a 2D circle centered at the origin.
59
+
60
+ **Details**
61
+
62
+ Omit `segments` for a smooth (auto-tessellated) circle. Pass an integer to get a regular polygon approximation — e.g. `6` for a hexagon, `8` for an octagon.
63
+
64
+ **Example**
35
65
 
36
66
  ```ts
37
- rect(width: number, height: number, center?: boolean): Sketch
67
+ circle2d(25).extrude(10); // smooth cylinder
68
+ circle2d(25, 6).extrude(10); // hexagonal prism
38
69
  ```
39
70
 
40
- Create a 2D rectangle. When center is true, the origin is at the rectangle center; otherwise at the bottom-left corner.
71
+ `circle2d(radius: number, segments?: number): Sketch`
72
+
73
+ #### `roundedRect()` — Create a 2D rectangle with rounded corners, centered at the origin.
41
74
 
42
- #### `circle2d()`
75
+ **Details**
76
+
77
+ The corner radius is automatically clamped to `min(width/2, height/2)` so it can never exceed the shape dimensions.
78
+
79
+ **Example**
43
80
 
44
81
  ```ts
45
- circle2d(radius: number, segments?: number): Sketch
82
+ roundedRect(60, 30, 5).extrude(3);
46
83
  ```
47
84
 
48
- Create a 2D circle centered at the origin. Use segments for lower-poly approximations.
85
+ `roundedRect(width: number, height: number, radius: number): Sketch`
86
+
87
+ #### `polygon()` — Create a 2D polygon from an array of `[x, y]` points or `Point2D` objects.
49
88
 
50
- #### `roundedRect()`
89
+ **Details**
90
+
91
+ Winding order is normalized automatically — clockwise (CW) input is silently reversed to CCW before being passed to the geometry kernel.
92
+
93
+ **Example**
51
94
 
52
95
  ```ts
53
- roundedRect(width: number, height: number, radius: number, center?: boolean): Sketch
96
+ polygon([[0, 0], [50, 0], [25, 40]]).extrude(5); // triangle
54
97
  ```
55
98
 
56
- Create a 2D rectangle with rounded corners. The radius is clamped to fit within the dimensions.
99
+ `polygon(points: ([ number, number ] | Point2D)[]): Sketch`
100
+
101
+ #### `ngon()` — Create a regular polygon inscribed in a circle of the given radius.
102
+
103
+ **Details**
57
104
 
58
- #### `polygon()`
105
+ `radius` is the center-to-vertex (circumradius) distance. Use `sides` of `3` for a triangle, `6` for a hexagon, etc. The first vertex is at the top (−90° from +X).
106
+
107
+ **Example**
59
108
 
60
109
  ```ts
61
- polygon(points: ([ number, number ] | Point2D)[]): Sketch
110
+ ngon(6, 20).extrude(10); // hexagonal prism, circumradius 20
62
111
  ```
63
112
 
64
- Create a 2D polygon from an array of [x, y] points or Point2D objects. Winding is normalized to CCW.
113
+ `ngon(sides: number, radius: number): Sketch`
114
+
115
+ #### `ellipse()` — Create a 2D ellipse centered at the origin.
65
116
 
66
- #### `ngon()`
117
+ **Example**
67
118
 
68
119
  ```ts
69
- ngon(sides: number, radius: number): Sketch
120
+ ellipse(30, 15).extrude(5);
121
+ ellipse(30, 15, 32).extrude(5); // lower-resolution approximation
70
122
  ```
71
123
 
72
- Create a regular polygon (equilateral triangle, hexagon, etc.) inscribed in a circle of the given radius.
124
+ `ellipse(rx: number, ry: number, segments?: number): Sketch`
125
+
126
+ #### `slot()` — Create a slot (oblong / stadium shape) — a rectangle with semicircular ends, centered at the origin.
73
127
 
74
- #### `ellipse()`
128
+ **Example**
75
129
 
76
130
  ```ts
77
- ellipse(rx: number, ry: number, segments?: number): Sketch
131
+ slot(40, 10).extrude(3); // 40mm long, 10mm wide slot
78
132
  ```
79
133
 
80
- Create a 2D ellipse centered at the origin with the given X and Y radii.
134
+ `slot(length: number, width: number): Sketch`
81
135
 
82
- #### `slot()`
136
+ #### `arcSlot()` — Create an arc-shaped slot (banana / annular sector) centered at the origin.
137
+
138
+ **Details**
139
+
140
+ The slot is symmetric about the +X axis. The two ends are closed with semicircular caps. `pitchRadius` is the distance from the origin to the centerline of the slot, and `thickness` is the radial width of the slot.
141
+
142
+ **Example**
83
143
 
84
144
  ```ts
85
- slot(length: number, width: number): Sketch
145
+ arcSlot(135, 74, 40).extrude(5); // pitch R135, 74° sweep, 40mm wide
86
146
  ```
87
147
 
88
- Create a slot (stadium/discorectangle) a rectangle with semicircular ends, centered at origin.
148
+ `arcSlot(pitchRadius: number, sweepDeg: number, thickness: number): Sketch`
149
+
150
+ #### `star()` — Create a star shape with alternating outer and inner radii.
89
151
 
90
- #### `star()`
152
+ **Example**
91
153
 
92
154
  ```ts
93
- star(points: number, outerR: number, innerR: number): Sketch
155
+ star(5, 30, 12).extrude(4); // five-pointed star
94
156
  ```
95
157
 
96
- Create a star shape with alternating outer and inner radii.
158
+ `star(points: number, outerR: number, innerR: number): Sketch`
97
159
 
98
160
  ### 2D Sketch Booleans
99
161
 
100
- Combine 2D sketches.
162
+ #### `union2d()` — Combine 2D sketches into a single profile using an additive boolean union.
101
163
 
102
- #### `union2d()`
164
+ **Details**
165
+
166
+ Accepts individual sketches or arrays: `union2d(a, b, c)` or `union2d([a, b, c])`. Uses Manifold's batch operation — faster than chaining `.add()` one by one when combining many sketches.
167
+
168
+ **Example**
103
169
 
104
170
  ```ts
105
- union2d(...inputs: SketchOperandInput[]): Sketch
171
+ const cross = union2d(rect(60, 10), rect(10, 60));
106
172
  ```
107
173
 
108
- Combine 2D sketches into a single profile (additive boolean). Accepts individual sketches or arrays.
174
+ `union2d(...inputs: SketchOperandInput[]): Sketch`
109
175
 
110
- #### `difference2d()`
176
+ #### `difference2d()` — Subtract one or more 2D sketches from a base sketch.
111
177
 
112
- ```ts
113
- difference2d(...inputs: SketchOperandInput[]): Sketch
114
- ```
178
+ **Details**
115
179
 
116
- Subtract 2D sketches from a base sketch. The first sketch is the base; all others are subtracted.
180
+ The first sketch is the base; all subsequent sketches are subtracted from it. Accepts individual sketches or arrays: `difference2d(base, c1, c2)` or `difference2d([base, c1, c2])`. Uses Manifold's batch operation — faster than chaining `.subtract()` one by one.
117
181
 
118
- #### `intersection2d()`
182
+ **Example**
119
183
 
120
184
  ```ts
121
- intersection2d(...inputs: SketchOperandInput[]): Sketch
185
+ const donut = difference2d(circle2d(50), circle2d(30));
122
186
  ```
123
187
 
124
- Keep only the overlapping area of the input sketches (intersection boolean).
188
+ `difference2d(...inputs: SketchOperandInput[]): Sketch`
125
189
 
126
- ### 2D Text
190
+ #### `intersection2d()` — Keep only the area where all input sketches overlap (intersection boolean).
127
191
 
128
- Create text geometry from strings using the built-in geometric font.
192
+ **Details**
129
193
 
130
- #### `text2d()`
194
+ Accepts individual sketches or arrays: `intersection2d(a, b)` or `intersection2d([a, b, c])`. Uses Manifold's batch operation — faster than chaining `.intersect()` one by one.
195
+
196
+ **Example**
131
197
 
132
198
  ```ts
133
- text2d(content: string, options?: TextOptions): Sketch
199
+ const lens = intersection2d(circle2d(30).translate(-10, 0), circle2d(30).translate(10, 0));
134
200
  ```
135
201
 
136
- Build a 2-D filled Sketch from a text string. The Sketch origin is at the left end of the text baseline by default (see `align` and `baseline` options to adjust placement). Text is rendered using the bundled Inter font by default, or any TTF/OTF/WOFF font you provide. // Extruded nameplate text2d('FORGE CAD', { size: 8 }).extrude(1.2) // Centered label on the XY plane text2d('V 2.0', { size: 6, align: 'center', baseline: 'center' })
202
+ `intersection2d(...inputs: SketchOperandInput[]): Sketch`
137
203
 
138
- <details><summary><code>TextOptions</code></summary>
204
+ ### 2D Sketch Features
139
205
 
140
- ```ts
141
- interface TextOptions {
142
- /** Cap height of the text in model units. All other dimensions (stroke weight, spacing) scale proportionally. */
143
- size?: number;
144
- /** Extra space between characters in model units. Negative values tighten the tracking. */
145
- letterSpacing?: number;
146
- /** Horizontal alignment relative to x = 0. - `'left'` left edge at x = 0 (default) - `'center'` centred on x = 0 - `'right'` — right edge at x = 0 */
147
- align?: "left" | "center" | "right";
148
- /** Vertical alignment relative to y = 0. - `'baseline'` — y = 0 is the text baseline (bottom of capital letters) - `'center'` — y = 0 is the vertical midpoint of the cap height - `'top'` — y = 0 is the top of capital letters */
149
- baseline?: "baseline" | "center" | "top";
150
- /** Font to use for text rendering. - `'sans-serif'` or `'inter'` — bundled Inter font (works everywhere, including browser) - **file path** — path to a TTF, OTF, or WOFF font file (CLI/Node only) - **Font object** — a previously loaded opentype.js Font (from `loadFont()`) - **omitted** — uses the bundled Inter font (same as `'sans-serif'`) text2d('Hello World', { size: 10 }) // default Inter text2d('Custom Font', { size: 10, font: '/path/to/font.ttf' }) */
151
- font?: string | opentype$1.Font;
152
- /** Bezier flattening tolerance in model units. Smaller = more polygon segments = smoother curves. */
153
- flattenTolerance?: number;
154
- }
155
- ```
206
+ #### `fillet2d()` — Round a named vertical edge of a shape with a circular fillet.
207
+
208
+ **Details**
209
+
210
+ Compiler-owned fillet for tracked vertical edges. Requires a compile-plan-covered target (shapes from `box()`, `rectangle().extrude()`, or rigid transforms of those).
211
+
212
+ **Supported edges:** - Tracked vertical edges from `box()` or `rectangle().extrude()` - Rigid transforms between tracked source and target - Untouched sibling tracked vertical edges after earlier `fillet2d`/`chamfer2d`
156
213
 
157
- </details>
214
+ **Not supported:** edges after shell, hole, cut, trim, difference, intersection, generic sketch extrudes, or tapered extrudes. Use `fillet()` with an `EdgeQuery` for those cases.
158
215
 
159
- #### `textWidth()`
216
+ Canonical quadrants: `vert-bl → [1,-1]`, `vert-br → [-1,-1]`, `vert-tr → [-1,1]`, `vert-tl → [1,1]`
217
+
218
+ **Example**
160
219
 
161
220
  ```ts
162
- textWidth(content: string, options?: Pick<TextOptions, "size" | "letterSpacing" | "font">): number
221
+ const b = rectangle(0, 0, 50, 50).extrude(20);
222
+ const filleted = fillet2d(b.toShape(), b.edge('vert-br'), 5, [-1, -1]);
163
223
  ```
164
224
 
165
- Returns the rendered width of a string in model units (same options as text2d).
225
+ `fillet2d(shape: Shape, edge: EdgeRef, radius: number, quadrant?: [ number, number ], segments?: number): Shape`
166
226
 
167
- ### Constrained Sketches
227
+ **`EdgeRef`**
228
+ - `query?: EdgeQueryRef` — Compiler-owned edge query when available.
229
+ - Also: `name: EdgeName`
230
+
231
+ #### `chamfer2d()` — Bevel a named vertical edge of a shape with a 45° chamfer.
232
+
233
+ **Details**
168
234
 
169
- Build parametric 2D geometry with geometric constraints and a solver.
235
+ Compiler-owned chamfer for tracked vertical edges. Requires a compile-plan-covered target. Supported subset and quadrant semantics are the same as `fillet2d()` — see that function for details.
170
236
 
171
- #### `constrainedSketch()`
237
+ **Example**
172
238
 
173
239
  ```ts
174
- constrainedSketch(options?: ConstrainedSketchOptions): ConstrainedSketchBuilder
240
+ const b = rectangle(0, 0, 50, 50).extrude(20);
241
+ const chamfered = chamfer2d(b.toShape(), b.edge('vert-br'), 3, [-1, -1]);
175
242
  ```
176
243
 
177
- Build a parametric 2D sketch with geometric constraints solved by the built-in constraint solver.
244
+ `chamfer2d(shape: Shape, edge: EdgeRef, size: number, quadrant?: [ number, number ]): Shape`
178
245
 
179
- <details><summary><code>ConstrainedSketchOptions</code></summary>
246
+ ### 2D Text
180
247
 
181
- ```ts
182
- interface ConstrainedSketchOptions {
183
- /** When true, adding a constraint that cannot be satisfied throws instead of silently discarding it. */
184
- strict?: boolean;
185
- }
186
- ```
248
+ #### `loadFont()` — Pre-load and cache a font for use with `text2d()`.
187
249
 
188
- </details>
250
+ **Details**
189
251
 
190
- #### `addRect()`
252
+ Fonts are cached by their source string (or `cacheKey` for `ArrayBuffer` sources), so repeated calls with the same path are free. Pre-loading is useful when you call `text2d()` many times with the same font — it avoids repeated disk reads.
253
+
254
+ Built-in font names that work everywhere (browser + CLI): - `'sans-serif'` or `'inter'` — bundled Inter Regular
255
+
256
+ **Example**
191
257
 
192
258
  ```ts
193
- addRect(sk: ConstrainedSketchBuilder, options?: RectOptions): ConstrainedRect
259
+ const font = loadFont('/path/to/Arial Bold.ttf');
260
+ text2d('Title', { size: 12, font }).extrude(1.5);
261
+ text2d('Subtitle', { size: 8, font }).extrude(1);
194
262
  ```
195
263
 
196
- Add an axis-aligned rectangle concept to the builder. Creates 4 vertices (CCW: bl→br→tr→tl), 4 sides, applies 4 structural constraints (`horizontal`/`vertical` on each side), enforces CCW winding, registers a loop and a shape, and returns a `ConstrainedRect` handle. ```ts const sk = constrainedSketch(); const rect = addRect(sk, { x: 0, y: 0, width: 100, height: 50 }); sk.fix(rect.bottomLeft, 0, 0); sk.length(rect.bottom, 120); ```
264
+ `loadFont(source: string | ArrayBuffer, cacheKey?: string): opentype$1.Font`
197
265
 
198
- <details><summary><code>RectOptions</code></summary>
266
+ #### `text2d()` — Build a filled 2D Sketch from a text string.
199
267
 
200
- ```ts
201
- interface RectOptions {
202
- /** Bottom-left x coordinate. Default: 0. */
203
- x?: number;
204
- /** Bottom-left y coordinate. Default: 0. */
205
- y?: number;
206
- /** Width (along x). Default: 10. */
207
- width?: number;
208
- /** Height (along y). Default: 10. */
209
- height?: number;
210
- /** Prevent 180° rotation (ensures bottom edge points rightward). Default: false. */
211
- blockRotation?: boolean;
212
- }
213
- ```
268
+ **Details**
214
269
 
215
- </details>
270
+ The Sketch origin is at the left end of the text baseline by default. Use `align` and `baseline` options to adjust placement. Text is rendered using the bundled Inter font by default, or any TTF/OTF/WOFF font you provide.
216
271
 
217
- <details><summary><code>ConstrainedRect</code></summary>
272
+ Alignment reference table:
273
+
274
+ | `align` | `baseline` | Origin | |------------|--------------|-------------------------------------| | `'left'` | `'baseline'` | Bottom-left of first char (default) | | `'center'` | `'center'` | Dead center of text block | | `'right'` | `'top'` | Top-right corner |
275
+
276
+ **Example**
218
277
 
219
278
  ```ts
220
- interface ConstrainedRect {
221
- bottomLeft: PointId;
222
- bottomRight: PointId;
223
- topRight: PointId;
224
- topLeft: PointId;
225
- /** bottom-left → bottom-right */
226
- bottom: LineId;
227
- /** bottom-right top-right */
228
- right: LineId;
229
- /** top-right → top-left */
230
- top: LineId;
231
- /** top-left bottom-left */
232
- left: LineId;
233
- /** Center point constrained to the geometric center via `midpoint` on the diagonal. Can be used in further constraints: `sk.fix(rect.center, 0, 0)`, `sk.coincident(rect.center, other)`. */
234
- center: PointId;
235
- /** ShapeId for `shapeWidth`, `shapeHeight`, `shapeArea`, `shapeCentroidX/Y`. */
236
- shape: ShapeId;
237
- }
279
+ // Extruded nameplate
280
+ text2d('FORGE CAD', { size: 8 }).extrude(1.2);
281
+
282
+ // Centered label on the XY plane
283
+ text2d('V 2.0', { size: 6, align: 'center', baseline: 'center' });
284
+
285
+ // Engraved text cut into the top face of a box
286
+ const label = text2d('REV A', { size: 5, align: 'center', baseline: 'center' });
287
+ plate.subtract(label.onFace(plate, 'top', { protrude: -0.5 }).extrude(1));
288
+
289
+ // Custom TTF font
290
+ text2d('Hello', { size: 10, font: '/path/to/Arial.ttf' }).extrude(1);
291
+
292
+ // Pre-loaded font for reuse
293
+ const font = loadFont('/path/to/Arial Bold.ttf');
294
+ text2d('Title', { size: 12, font }).extrude(1.5);
238
295
  ```
239
296
 
240
- </details>
297
+ `text2d(content: string, options?: TextOptions): Sketch`
298
+
299
+ **`TextOptions`**
300
+
301
+ | Option | Type | Description |
302
+ |--------|------|-------------|
303
+ | `size?` | `number` | Cap height of the text in model units. All other dimensions (stroke weight, spacing) scale proportionally. |
304
+ | `letterSpacing?` | `number` | Extra space between characters in model units. Negative values tighten the tracking. |
305
+ | `align?` | `"left" | "center" | "right"` | Horizontal alignment relative to x = 0. - `'left'` — left edge at x = 0 (default) - `'center'` — centred on x = 0 - `'right'` — right edge at x = 0 |
306
+ | `baseline?` | `"baseline" | "center" | "top"` | Vertical alignment relative to y = 0. - `'baseline'` — y = 0 is the text baseline (bottom of capital letters) - `'center'` — y = 0 is the vertical midpoint of the cap height - `'top'` — y = 0 is the top of capital letters |
307
+ | `font?` | `string | opentype$1.Font` | Font to use for text rendering. - `'sans-serif'` or `'inter'` — bundled Inter font (works everywhere, including browser) - **file path** — path to a TTF, OTF, or WOFF font file (CLI/Node only) - **Font object** — a previously loaded opentype.js Font (from `loadFont()`) - **omitted** — uses the bundled Inter font (same as `'sans-serif'`) text2d('Hello World', { size: 10 }) // default Inter text2d('Custom Font', { size: 10, font: '/path/to/font.ttf' }) |
308
+ | `flattenTolerance?` | `number` | Bezier flattening tolerance in model units. Smaller = more polygon segments = smoother curves. |
309
+
310
+ #### `textWidth()` — Measure the rendered advance width of a string without creating any geometry.
311
+
312
+ **Details**
241
313
 
242
- #### `addPolygon()`
314
+ Uses the same font metrics as `text2d()`. Useful for computing layout dimensions before building the actual sketch — e.g. sizing a plate to fit a label.
315
+
316
+ **Example**
243
317
 
244
318
  ```ts
245
- addPolygon(sk: ConstrainedSketchBuilder, options: PolygonOptions): ConstrainedPolygon
319
+ const w = textWidth('SERIAL: 001', { size: 6 });
320
+ const plate = box(w + 10, 12, 2);
246
321
  ```
247
322
 
248
- Add a general polygon concept to the builder. Creates n vertices and n sides (CCW: `sides[i]` from `vertices[i]` → `vertices[(i+1) % n]`). Applies a `ccw` constraint to enforce winding. The user is responsible for all dimensional constraints. ```ts const sk = constrainedSketch(); const tri = addPolygon(sk, { points: [[0,0],[100,0],[50,80]] }); sk.fix(tri.vertex(0), 0, 0); sk.length(tri.side(0), 100); ```
323
+ `textWidth(content: string, options?: Pick<TextOptions, "size" | "letterSpacing" | "font">): number`
324
+
325
+ ### Constrained Sketches
326
+
327
+ #### `constrainedSketch()` — Create a parametric 2D sketch driven by geometric constraints and a nonlinear solver.
249
328
 
250
- <details><summary><code>PolygonOptions</code></summary>
329
+ **Workflow**
330
+
331
+ 1. Create a builder with `constrainedSketch()`. 2. Add geometry — points, lines, circles, arcs — using the builder methods. 3. Add constraints (`horizontal`, `length`, `fix`, etc.) to drive the geometry. 4. Call `.solve()` to run the solver and get a `ConstraintSketch` (which extends `Sketch`).
332
+
333
+ **Example**
251
334
 
252
335
  ```ts
253
- interface PolygonOptions {
254
- /** Whether to register a closed loop for sketch generation. Default: true. */
255
- addLoop?: boolean;
256
- /** Prevent 180° rotation (ensures first edge maintains its initial direction). Default: false. */
257
- blockRotation?: boolean;
258
- }
336
+ const sk = constrainedSketch();
337
+ const p1 = sk.point(0, 0);
338
+ const p2 = sk.point(50, 0);
339
+ const l1 = sk.line(p1, p2);
340
+ sk.fix(p1, 0, 0);
341
+ sk.horizontal(l1);
342
+ sk.length(l1, 50);
343
+ return sk.solve().extrude(10);
259
344
  ```
260
345
 
261
- </details>
262
-
263
- <details><summary><code>ConstrainedPolygon</code></summary>
346
+ **Solver status**
264
347
 
265
348
  ```ts
266
- interface ConstrainedPolygon {
267
- /** CCW-ordered PointIds. */
268
- vertices: PointId[];
269
- /** CCW-ordered LineIds. `sides[i]` runs from `vertices[i]` `vertices[(i+1) % n]`. */
270
- sides: LineId[];
271
- /** ShapeId for `shapeWidth`, `shapeHeight`, `shapeArea`, `shapeCentroidX/Y`. */
272
- shape: ShapeId;
273
- }
349
+ const result = sk.solve();
350
+ result.constraintMeta.status; // 'fully' | 'under' | 'over' | 'over-redundant'
351
+ result.constraintMeta.dof; // 0 = fully constrained
352
+ result.constraintMeta.maxError; // residual should be < 1e-6
353
+ result.inspect(); // human-readable summary
354
+ result.withUpdatedConstraint('cst-5', 120); // update a dimension without rebuilding
274
355
  ```
275
356
 
276
- </details>
357
+ `constrainedSketch(options?: ConstrainedSketchOptions): ConstrainedSketchBuilder`
358
+
359
+ **`ConstrainedSketchOptions`**
360
+ - `strict?: boolean` — When true, adding a constraint that cannot be satisfied throws instead of silently discarding it.
277
361
 
278
- #### `addRegularPolygon()`
362
+ #### `addRect()` — Add an axis-aligned rectangle concept to the builder.
363
+
364
+ Creates 4 vertices (CCW: bl→br→tr→tl), 4 sides, 4 structural constraints (`horizontal`/`vertical` on each side), CCW winding, a center point, a loop, and a shape. Returns a `ConstrainedRect` handle with 4 DOF (x, y, width, height).
365
+
366
+ Use `sk.rect()` as the shorthand builder method.
367
+
368
+ **Example**
279
369
 
280
370
  ```ts
281
- addRegularPolygon(sk: ConstrainedSketchBuilder, options: RegularPolygonOptions): ConstrainedRegularPolygon
371
+ const sk = constrainedSketch();
372
+ const r = sk.rect({ x: 0, y: 0, width: 100, height: 50 });
373
+ sk.fix(r.bottomLeft, 0, 0);
374
+ sk.length(r.bottom, 120); // override initial width
375
+ return sk.solve().extrude(10);
282
376
  ```
283
377
 
284
- Add a regular n-gon concept to the builder. Vertices are placed at `(cx + r·cos(startAngle + i·2π/n), cy + r·sin(...))`. Equal-side constraints enforce regularity. The center point is constrained to the centroid via midpoint constraints on the first diagonal. ```ts const sk = constrainedSketch(); const hex = addRegularPolygon(sk, { sides: 6, radius: 25, cx: 0, cy: 0 }); sk.fix(hex.center, 0, 0); sk.length(hex.side(0), 30); // changes all sides (equal constraint) ```
378
+ `addRect(sk: ConstrainedSketchBuilder, options?: RectOptions): ConstrainedRect`
379
+
380
+ **`RectOptions`**
381
+
382
+ | Option | Type | Description |
383
+ |--------|------|-------------|
384
+ | `x?` | `number` | Bottom-left x coordinate. Default: 0. |
385
+ | `y?` | `number` | Bottom-left y coordinate. Default: 0. |
386
+ | `width?` | `number` | Width (along x). Default: 10. |
387
+ | `height?` | `number` | Height (along y). Default: 10. |
388
+ | `blockRotation?` | `boolean` | Prevent 180° rotation (ensures bottom edge points rightward). Default: false. |
389
+
390
+ **`ConstrainedRect`**
391
+
392
+ | Option | Type | Description |
393
+ |--------|------|-------------|
394
+ | `bottom` | `LineId` | bottom-left → bottom-right |
395
+ | `right` | `LineId` | bottom-right → top-right |
396
+ | `top` | `LineId` | top-right → top-left |
397
+ | `left` | `LineId` | top-left → bottom-left |
398
+ | `center` | `PointId` | Center point constrained to the geometric center via `midpoint` on the diagonal. Can be used in further constraints: `sk.fix(rect.center, 0, 0)`, `sk.coincident(rect.center, other)`. |
399
+ | `shape` | `ShapeId` | ShapeId for `shapeWidth`, `shapeHeight`, `shapeArea`, `shapeCentroidX/Y`. |
400
+ | `bottomLeft`, `bottomRight`, `topRight`, `topLeft` | | — |
285
401
 
286
- <details><summary><code>RegularPolygonOptions</code></summary>
402
+ #### `addPolygon()` — Add a general polygon concept to the builder.
403
+
404
+ Creates n vertices and n sides (CCW: `sides[i]` from `vertices[i]` → `vertices[(i+1) % n]`). Applies a `ccw` constraint to enforce winding. All dimensional constraints (lengths, angles, position) are left to the caller.
405
+
406
+ Use `sk.addPolygon()` as the shorthand builder method.
407
+
408
+ **Example**
287
409
 
288
410
  ```ts
289
- interface RegularPolygonOptions {
290
- /** Number of sides (minimum 3). */
291
- sides: number;
292
- /** Circumradius — distance from center to vertex. Default: 10. */
293
- radius?: number;
294
- /** Center x coordinate. Default: 0. */
295
- cx?: number;
296
- /** Center y coordinate. Default: 0. */
297
- cy?: number;
298
- /** Angle (in degrees) of vertex[0] measured from the +X axis (CCW positive). Default: 0 (rightmost vertex). */
299
- startAngle?: number;
300
- /** Prevent 180° rotation (ensures first edge maintains its initial direction). Default: false. */
301
- blockRotation?: boolean;
302
- }
411
+ const sk = constrainedSketch();
412
+ const tri = sk.addPolygon({ points: [[0,0],[100,0],[50,80]] });
413
+ sk.fix(tri.vertex(0), 0, 0);
414
+ sk.length(tri.side(0), 100);
415
+ return sk.solve().extrude(5);
303
416
  ```
304
417
 
305
- </details>
418
+ `addPolygon(sk: ConstrainedSketchBuilder, options: PolygonOptions): ConstrainedPolygon`
419
+
420
+ **`PolygonOptions`**
421
+ - `addLoop?: boolean` — Whether to register a closed loop for sketch generation. Default: true.
422
+ - `blockRotation?: boolean` — Prevent 180° rotation (ensures first edge maintains its initial direction). Default: false.
423
+
424
+ **`ConstrainedPolygon`**
425
+ - `vertices: PointId[]` — CCW-ordered PointIds.
426
+ - `sides: LineId[]` — CCW-ordered LineIds. `sides[i]` runs from `vertices[i]` → `vertices[(i+1) % n]`.
427
+ - `shape: ShapeId` — ShapeId for `shapeWidth`, `shapeHeight`, `shapeArea`, `shapeCentroidX/Y`.
428
+
429
+ #### `addRegularPolygon()` — Add a regular n-gon concept to the builder.
430
+
431
+ Vertices are placed at `(cx + r·cos(startAngle + i·2π/n), cy + r·sin(...))`. Equal-radius and equal-side constraints enforce regularity (4 DOF: center x/y, radius, rotation). The center point is tracked by the solver and exposed via the returned handle.
306
432
 
433
+ Use `sk.regularPolygon()` as the shorthand builder method.
307
434
 
308
- <details><summary><code>ConstrainedRegularPolygon</code> extends ConstrainedPolygon</summary>
435
+ **Example**
309
436
 
310
437
  ```ts
311
- interface ConstrainedRegularPolygon extends ConstrainedPolygon {
312
- /** Center point. Use `sk.fix(poly.center, x, y)` to pin location, or `sk.coincident(poly.center, other)` to align with other geometry. */
313
- center: PointId;
314
- }
438
+ const sk = constrainedSketch();
439
+ const hex = sk.regularPolygon({ sides: 6, radius: 25 });
440
+ sk.fix(hex.center, 0, 0);
441
+ sk.length(hex.side(0), 30); // all sides change (equal constraint)
442
+ return sk.solve().extrude(5);
315
443
  ```
316
444
 
317
- </details>
445
+ `addRegularPolygon(sk: ConstrainedSketchBuilder, options: RegularPolygonOptions): ConstrainedRegularPolygon`
446
+
447
+ **`RegularPolygonOptions`**
448
+
449
+ | Option | Type | Description |
450
+ |--------|------|-------------|
451
+ | `sides` | `number` | Number of sides (minimum 3). |
452
+ | `radius?` | `number` | Circumradius — distance from center to vertex. Default: 10. |
453
+ | `cx?` | `number` | Center x coordinate. Default: 0. |
454
+ | `cy?` | `number` | Center y coordinate. Default: 0. |
455
+ | `startAngle?` | `number` | Angle (in degrees) of vertex[0] measured from the +X axis (CCW positive). Default: 0 (rightmost vertex). |
456
+ | `blockRotation?` | `boolean` | Prevent 180° rotation (ensures first edge maintains its initial direction). Default: false. |
457
+
458
+
459
+ **`ConstrainedRegularPolygon`** extends ConstrainedPolygon
460
+ - `center: PointId` — Center point. Use `sk.fix(poly.center, x, y)` to pin location, or `sk.coincident(poly.center, other)` to align with other geometry.
318
461
 
319
462
  ### 2D Geometry Helpers
320
463
 
321
- Analytic 2D geometry classes for measurement and construction.
464
+ #### `point()` — Create an analytic 2D point for measurement and construction geometry.
322
465
 
323
- #### `point()`
466
+ **Example**
324
467
 
325
468
  ```ts
326
- point(x: number, y: number): Point2D
469
+ const p = point(10, 20);
470
+ p.distanceTo(point(30, 40)); // Euclidean distance
471
+ p.midpointTo(point(30, 40)); // midpoint
472
+ p.translate(5, 5); // new shifted point
473
+ p.toTuple(); // [10, 20]
327
474
  ```
328
475
 
329
- Create an analytic 2D point for measurement and construction geometry.
476
+ `point(x: number, y: number): Point2D`
330
477
 
331
- #### `line()`
478
+ #### `line()` — Create an analytic 2D line segment between two points.
479
+
480
+ **Example**
332
481
 
333
482
  ```ts
334
- line(x1: number, y1: number, x2: number, y2: number): Line2D
483
+ const l = line(0, 0, 50, 0);
484
+ l.length; l.midpoint; l.angle; l.direction;
485
+ l.parallel(10); // parallel line offset 10 (positive = left)
486
+ l.intersect(l2); // Point2D — treats lines as infinite
487
+ l.intersectSegment(l2); // Point2D or null — segments only
488
+
489
+ Line2D.fromPointAndAngle(point(0, 0), 45, 100);
490
+ Line2D.fromPointAndDirection(point(0, 0), [1, 1], 50);
335
491
  ```
336
492
 
337
- Create an analytic 2D line segment between two points. Provides length, midpoint, angle, intersection, and parallel helpers.
493
+ `line(x1: number, y1: number, x2: number, y2: number): Line2D`
494
+
495
+ #### `circle()` — Create an analytic 2D circle for measurement, construction, and extrusion.
338
496
 
339
- #### `circle()`
497
+ **Example**
340
498
 
341
499
  ```ts
342
- circle(cx: number, cy: number, radius: number): Circle2D
500
+ const c = circle(0, 0, 25);
501
+ c.diameter; c.circumference; c.area;
502
+ c.pointAtAngle(90); // Point2D at top (90° CCW from +X)
503
+
504
+ // Extrude to cylinder with named faces
505
+ const cyl = c.extrude(30);
506
+ cyl.face('top'); // FaceRef (planar)
507
+ cyl.face('side'); // FaceRef (curved)
508
+
509
+ Circle2D.fromDiameter(point(0, 0), 50);
343
510
  ```
344
511
 
345
- Create an analytic 2D circle for measurement, construction, and extrusion. Provides diameter, circumference, area, and toSketch().
512
+ `circle(cx: number, cy: number, radius: number): Circle2D`
346
513
 
347
- #### `degrees()`
514
+ #### `degrees()` — Identity function that returns degrees unchanged.
348
515
 
349
- ```ts
350
- degrees(deg: number): number
351
- ```
516
+ Use for clarity when the unit of an angle value would otherwise be ambiguous — e.g. `param("Angle", degrees(45))`.
352
517
 
353
- Convert degrees to degrees (identity for readability in scripts)
518
+ `degrees(deg: number): number`
354
519
 
355
- #### `radians()`
520
+ #### `radians()` — Convert radians to degrees.
356
521
 
357
- ```ts
358
- radians(rad: number): number
359
- ```
522
+ ForgeCAD's public API uses degrees throughout. Use this when you have a radian value (e.g. from `Math.atan2`) that you want to express in degrees.
360
523
 
361
- Convert radians to degrees
524
+ `radians(rad: number): number`
362
525
 
363
526
  ---
364
527
 
@@ -366,7 +529,15 @@ Convert radians to degrees
366
529
 
367
530
  ### `Sketch`
368
531
 
369
- 2D profile for extrusion, revolve, and other operations. Supports transforms (translate, rotate, scale, mirror), booleans (add, subtract, intersect), offset, simplify, extrude, revolve, and queries (area, bounds, isEmpty, numVert). All operations are immutable and return new sketches.
532
+ Immutable 2D profile for extrusion, revolve, and other operations.
533
+
534
+ **Details**
535
+
536
+ `Sketch` wraps Manifold's `CrossSection` with a chainable 2D API. Every method returns a new `Sketch` — the original is never mutated. Colors, edge labels, and placement data are preserved through all transforms and boolean operations.
537
+
538
+ Supported operations: - **Transforms** — `translate`, `rotate`, `rotateAround`, `scale`, `mirror` - **Booleans** — `add` (union), `subtract` (difference), `intersect` - **Operations** — `offset`, `simplify` - **Queries** — `area`, `bounds`, `isEmpty`, `numVert` - **3D operations** — `extrude`, `revolve`, `onFace` - **Regions** — `regions`, `region` - **Placement** — `attachTo`
539
+
540
+ Named anchor positions used by `attachTo()`: `'center'` | `'top-left'` | `'top-right'` | `'bottom-left'` | `'bottom-right'` | `'top'` | `'bottom'` | `'left'` | `'right'`
370
541
 
371
542
  **Properties:**
372
543
 
@@ -376,119 +547,121 @@ Convert radians to degrees
376
547
 
377
548
  **Methods:**
378
549
 
379
- - `color()` — Set the color of this sketch (hex string, e.g. "#ff0000")
380
- - `clone()` — Return a new Sketch wrapper for explicit duplication in scripts.
381
- - `duplicate()` — Alias for clone()
382
- - `area()` — Area in mm squared.
383
- - `bounds()` — Bounding box as { min: [x,y], max: [x,y] }.
384
- - `isEmpty()` — True if the sketch contains no area.
385
- - `numVert()` — Vertex count of the polygon representation.
386
- - `toPolygons()` — toPolygons(): number[][][]
387
- - `translate()` — translate(_x: number, _y?: number): Sketch
388
- - `rotate()` — rotate(_degrees: number): Sketch
389
- - `rotateAround()` — rotateAround(_degrees: number, _pivot: [ number, number ]): Sketch
390
- - `scale()` — scale(_v: number | [ number, number ]): Sketch
391
- - `mirror()` mirror(_ax: [ number, number ]): Sketch
392
- - `scaleAround()` — scaleAround(_pivot: [ number, number ], _v: number | [ number, number ]): Sketch
393
- - `mirrorThrough()` — mirrorThrough(_point: [ number, number ], _ax: [ number, number ]): Sketch
394
- - `add()` — add(..._others: SketchOperandInput[]): Sketch
395
- - `subtract()` — subtract(..._others: SketchOperandInput[]): Sketch
396
- - `intersect()` — intersect(..._others: SketchOperandInput[]): Sketch
397
- - `union()` — Alias for add() matches the free-function union() naming.
398
- - `difference()` — Alias for subtract() — matches the free-function difference() naming.
399
- - `intersection()` — Alias for intersect() matches the free-function intersection() naming.
400
- - `offset()` — offset(_delta: number, _join?: "Square" | "Round" | "Miter"): Sketch
401
- - `regions()` — Decompose this sketch into its distinct filled regions. See `sketchRegions()`. Regions are returned largest-first by area.
402
- - `region()` — Select the single filled region that contains the given 2D seed point. Throws if the seed is outside all regions. See `sketchRegion()`.
403
- - `extrude()` — Extrude this 2D sketch along Z to create a 3D solid. Supports twist, scale tapering, and centering.
404
- - `revolve()` — Revolve this 2D sketch around the Y axis to create a 3D solid of revolution.
405
- - `attachTo()` — attachTo(_target: Sketch, _targetAnchor: Anchor, _selfAnchor?: Anchor, _offset?:
406
- - `onFace()` — onFace(_parentOrFace: Shape | { toShape(): Shape; } | { _bbox(): { min: number[]
550
+ - `color(value: string | undefined): Sketch` — Set the display color of this sketch. **Details** Color is preserved through all transforms and boolean operations. Pass `undefined` to clear the color. **Example** ```ts circle2d(20).color('#ff0000').extrude(5); ```
551
+ - `clone(): Sketch` — Create an explicit copy of this sketch for branching variants. **Details** Because all Sketch operations are immutable, `clone()` is rarely needed. Use it when you want to assign the same sketch to multiple names and continue modifying each independently without confusion.
552
+ - `area(): number` — Return the total filled area of the sketch.
553
+ - `bounds(): ProfileBounds` — Return the axis-aligned bounding box of the sketch.
554
+ - `isEmpty(): boolean` — Return `true` if the sketch contains no filled area.
555
+ - `numVert(): number` — Return the number of vertices in the polygon representation of the sketch contours.
556
+ - `toPolygons(): number[][][]` — Return the sketch as a list of polygons matching its contour topology. Useful when you need raw polygon data for inspection or custom export.
557
+ - `translate(x: number, y?: number): Sketch` — Move the sketch by the given X and Y offset.
558
+ - `rotate(degrees: number): Sketch` — Rotate the sketch around its bounding-box center.
559
+ - `rotateAround(degrees: number, pivot: [ number, number ]): Sketch` — Rotate the sketch around a specific pivot point. **Example** ```ts rect(20, 20).rotateAround(45, [0, 0]); ```
560
+ - `scale(v: number | [ number, number ]): Sketch` — Scale the sketch relative to its bounding-box center. **Details** Pass a single number for uniform scaling, or `[sx, sy]` for per-axis scaling.
561
+ - `mirror(normal: [ number, number ]): Sketch` — Mirror the sketch across a line through its bounding-box center. **Details** `normal` is the normal vector of the mirror line (not the line direction). For example, `[1, 0]` mirrors across a vertical line (Y axis direction), and `[0, 1]` mirrors across a horizontal line.
562
+ - `scaleAround(pivot: [ number, number ], v: number | [ number, number ]): Sketch` — Scale the sketch relative to an arbitrary pivot point.
563
+ - `mirrorThrough(point: [ number, number ], normal: [ number, number ]): Sketch` — Mirror the sketch across a line defined by a point and a normal direction.
564
+ - `add(...others: SketchOperandInput[]): Sketch` — Add (union) one or more sketches to this sketch. **Details** Accepts individual sketches or arrays: `sketch.add(a, b)` or `sketch.add([a, b])`. For combining many sketches at once, prefer the free function `union2d()` which uses Manifold's batch operation and is faster than chaining. **Example** ```ts circle2d(20).add(rect(10, 30)).extrude(5); ```
565
+ - `subtract(...others: SketchOperandInput[]): Sketch` — Subtract one or more sketches from this sketch. **Details** Accepts individual sketches or arrays: `sketch.subtract(a, b)` or `sketch.subtract([a, b])`. For subtracting many cutters at once, prefer the free function `difference2d()`. **Example** ```ts rect(40, 40).subtract(circle2d(10)).extrude(5); ```
566
+ - `intersect(...others: SketchOperandInput[]): Sketch` — Intersect this sketch with one or more others (keep overlapping area only). **Details** Accepts individual sketches or arrays: `sketch.intersect(a, b)` or `sketch.intersect([a, b])`. For intersecting many sketches, prefer the free function `intersection2d()`.
567
+ - `offset(delta: number, join?: "Square" | "Round" | "Miter"): Sketch` — Inflate (positive delta) or deflate (negative delta) the sketch contour. **Details** Use `offset(-r).offset(+r)` to round every convex corner of a closed sketch. - `'Round'` — smooth arc at each corner (default) - `'Square'` — flat mitered extension - `'Miter'` — sharp pointed extension **Example** ```ts rect(40, 20).offset(3); // expand by 3 rect(40, 20).offset(-2).offset(2); // round all convex corners ```
568
+ - `regions(): Sketch[]` — Decompose this sketch into its distinct filled regions, sorted largest-first by area. **Details** A single sketch can contain several disconnected filled areas (e.g., two separate rectangles, or a ring shape with a hole). This method enumerates all top-level connected regions as independent `Sketch` objects, each with its own outer boundary and associated holes. **Example** ```ts const pair = union2d(rect(40, 40), rect(40, 40).translate(60, 0)); const [left, right] = pair.regions(); // largest first left.extrude(5); ```
569
+ - `region(seed: [ number, number ]): Sketch` — Select the single filled region that contains the given 2D seed point. **Details** The seed must lie strictly inside the filled area — not on a boundary edge and not inside a hole. Throws a descriptive error if the seed is outside all regions. If unsure where regions are, use `.regions()` first each result has `.bounds()`. **Example** ```ts const donut = circle2d(50).subtract(circle2d(30)); donut.region([40, 0]).extrude(10); // seed at radius 40, inside the ring ```
570
+ - `extrude(height: number, opts?: { twist?: number; divisions?: number; scaleTop?: number | [ number, number ]; }): Shape` — Extrude this 2D sketch along Z to create a 3D solid. Supports twist and scale tapering.
571
+ - `revolve(degrees?: number, segments?: number): Shape` — Revolve this 2D sketch around the Y axis to create a 3D solid of revolution.
572
+ - `attachTo(target: Sketch, targetAnchor: Anchor, selfAnchor?: Anchor, offset?: [ number, number ]): Sketch` — Position this sketch relative to another using named anchor points. **Details** Computes the translation needed to align `selfAnchor` on this sketch with `targetAnchor` on the target sketch, then applies an optional pixel-exact offset. Anchor positions: `'center'` | `'top-left'` | `'top-right'` | `'bottom-left'` | `'bottom-right'` | `'top'` | `'bottom'` | `'left'` | `'right'` **Example** ```ts const arm = rect(4, 70).attachTo(plate, 'bottom-left', 'top-left'); const shifted = rect(4, 70).attachTo(plate, 'bottom-left', 'top-left', [5, 0]); ```
573
+ - `onFace(parentOrFace: Shape | { toShape(): Shape; } | { _bbox(): { min: number[]; max: number[]; }; } | FaceRef, faceOrOpts?: "front" | "back" | "left" | "right" | "top" | "bottom" | string | FaceRef | { u?: number; v?: number; protrude?: number; selfAnchor?: Anchor; }, opts?: { u?: number; v?: number; protrude?: number; selfAnchor?: Anchor; }): Sketch` — Place this sketch on a face or planar target in 3D space. Use this when a 2D profile should be oriented onto a 3D face before extrusion or other downstream operations.
574
+ - `labelEdge(name: string): Sketch` — Label the single boundary edge (for circles, single-loop profiles).
575
+ - `labelEdges(...args: (string | null)[] | [ Record<string, string> ]): Sketch` — Label edges in winding order, or by named map for rect. Positional: `labelEdges('bottom', 'right', 'top', 'left')` — one per edge, `null` to skip. Named (rect only): `labelEdges({ bottom: 'floor', top: 'ceiling' })`.
576
+ - `edgeLabels(): string[]` — List current edge label names.
577
+ - `prefixLabels(prefix: string): Sketch` — Prefix all edge labels. Returns this sketch (mutates labels in place).
578
+ - `renameLabel(from: string, to: string): Sketch` — Rename a single edge label.
579
+ - `dropLabels(...names: string[]): Sketch` — Remove specific labels.
580
+ - `dropAllLabels(): Sketch` — Remove all labels.
407
581
 
408
582
  ### `ConstrainedSketchBuilder`
409
583
 
410
- **Methods:**
411
-
412
- - `moveTo()` — moveTo(x: number, y: number): this
413
- - `lineTo()` — lineTo(x: number, y: number): this
414
- - `lineH()` lineH(dx: number): this
415
- - `lineV()` — lineV(dy: number): this
416
- - `lineAngled()` lineAngled(length: number, degrees: number): this
417
- - `arcTo()` arcTo(x: number, y: number, radius: number, clockwise?: boolean): this
418
- - `arcByCenter()` arcByCenter(centerId: PointId, startId: PointId, endId: PointId, clockwise?: boo
419
- - `bezier()` — bezier(p0: any, p1: any, p2: any, p3: any, name?: string): BezierId
420
- - `bezierTo()` — bezierTo(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number)
421
- - `blendTo()` — blendTo(x: number, y: number, weight?: number): this
422
- - `close()` — close(): this
423
- - `addLoopCircle()` — addLoopCircle(center: PointId, radius: number, segments?: number): this
424
- - `addLoop()` addLoop(points: any[]): this
425
- - `addProfileLoop()` — addProfileLoop(segments: Array<{ kind: "line"; line: any; } | { kind: "arc"; arc
426
- - `horizontal()` — horizontal(line: any): this
427
- - `vertical()` vertical(line: any): this
428
- - `parallel()` — parallel(a: any, b: any): this
429
- - `sameDirection()` — sameDirection(a: any, b: any): this
430
- - `oppositeDirection()` — oppositeDirection(a: any, b: any): this
431
- - `blockRotation()` — blockRotation(points: any[], axis?: "x" | "y"): this
432
- - `perpendicular()` — perpendicular(a: any, b: any): this
433
- - `tangent()` — tangent(a: any, b: any): this
434
- - `equal()` — equal(a: any, b: any): this
435
- - `coincident()` — coincident(a: any, b: any): this
436
- - `concentric()` — concentric(a: any, b: any): this
437
- - `collinear()` collinear(point: any, line: any): this
438
- - `symmetric()` — symmetric(a: any, b: any, axis: any): this
439
- - `fix()` — fix(point: any, x?: number, y?: number): this
440
- - `midpoint()` — midpoint(point: any, line: any): this
441
- - `pointOnCircle()` — pointOnCircle(point: any, circle: any): this
442
- - `pointOnLine()` pointOnLine(point: any, line: any): this
443
- - `distance()` — distance(a: any, b: any, value: number): this
444
- - `length()` length(line: any, value: number): this
445
- - `angle()` — angle(a: any, b: any, value: number): this
446
- - `radius()` — radius(circle: any, value: number): this
447
- - `diameter()` diameter(circle: any, value: number): this
448
- - `hDistance()` — hDistance(a: any, b: any, value: number): this
449
- - `vDistance()` — vDistance(a: any, b: any, value: number): this
450
- - `pointLineDistance()` — pointLineDistance(point: any, line: any, value: number): this
451
- - `lineDistance()` — lineDistance(a: any, b: any, value: number): this
452
- - `absoluteAngle()` — absoluteAngle(line: any, value: number): this
453
- - `equalRadius()` — equalRadius(a: any, b: any): this
454
- - `arcLength()` arcLength(arc: any, value: number): this
455
- - `lineTangentArc()` — lineTangentArc(line: any, arc: any, atStart: boolean): this
456
- - `arcTangentArc()` — arcTangentArc(arcA: any, arcB: any, aAtStart?: boolean, bAtStart?: boolean): thi
457
- - `bezierTangentArc()` — bezierTangentArc(bezier: any, arc: any, atBezierStart: boolean, atArcStart: bool
458
- - `smoothBlend()` — smoothBlend(arc1: any, arc2: any, options?: { weight?: number; arc1End?: "start"
459
- - `shapeWidth()` — shapeWidth(shape: any, value: number): this
460
- - `shapeHeight()` — shapeHeight(shape: any, value: number): this
461
- - `shapeCentroidX()` — shapeCentroidX(shape: any, value: number): this
462
- - `shapeCentroidY()` — shapeCentroidY(shape: any, value: number): this
463
- - `shapeArea()` — shapeArea(shape: any, value: number): this
464
- - `shapeEqualCentroid()` — shapeEqualCentroid(a: any, b: any): this
465
- - `angleBetween()` — angleBetween(a: any, b: any, value: number): this
466
- - `ccw()` — ccw(...points: any[]): this
467
- - `offsetX()` — Constrain the horizontal (X-axis) offset between two lines. Uses the start-point of each line to measure horizontal distance. `value` is the signed distance: b.startPt.x − a.startPt.x = value.
468
- - `offsetY()` Constrain the vertical (Y-axis) offset between two lines. Uses the start-point of each line to measure vertical distance. `value` is the signed distance: b.startPt.y − a.startPt.y = value.
469
- - `importPoint()` importPoint(pt: { x: number; y: number; }, fixed?: boolean): PointId
470
- - `importLine()` importLine(l: { start: { x: number; y: number; }; end: { x: number; y: number; }
471
- - `importRectangle()` importRectangle(r: { vertices: [ { x: number; y: number; }, { x: number; y: numb
472
- - `referencePoint()` referencePoint(x: number, y: number): PointId
473
- - `referenceLine()` — referenceLine(x1: number, y1: number, x2: number, y2: number): LineId
474
- - `referenceFrom()` — referenceFrom(source: ConstraintSketch, entityId: string): PointId | LineId | nu
475
- - `referenceAllFrom()` — referenceAllFrom(source: ConstraintSketch): { points: Map<string, PointId>; line
476
- - `point()` — point(x?: number, y?: number, fixed?: boolean): PointId
477
- - `pointAt()` — pointAt(index: number): PointId
478
- - `line()` — line(a: PointId, b: PointId, construction?: boolean, name?: string): LineId
479
- - `lineAt()` — lineAt(index: number): LineId
480
- - `circle()` — circle(center: PointId, radius: number, construction?: boolean, segments?: numbe
481
- - `circleAt()` — circleAt(index: number): CircleId
482
- - `shape()` — Register a named shape (closed polygon) from an ordered list of line IDs. Returns the ShapeId for use in shape constraints (shapeWidth, shapeCentroidX, etc.).
483
- - `group()` — Create a rigid-body group with a local coordinate frame. Points/lines added to the group move together as a unit — the solver sees 3 DOF (x, y, θ) instead of 2N per point. ```ts const g = sk.group({ x: 50, y: 30 }); const p0 = g.point(0, 0); // local origin → world (50, 30) const p1 = g.point(100, 0); // local (100,0) → world (150, 30) const l = g.line(p0, p1); g.fixRotation(); // p0, p1 work in constraints like any other PointId: sk.coincident(p0, someExternalPoint); ```
484
- - `constrain()` — constrain(constraint: Omit<SketchConstraint, "id">): this
485
- - `solve()` — solve(options?: SolveOptions): ConstraintSketch | Sketch
486
- - `solveConstraintsOnly()` — Run the solver without building a full `ConstraintSketch`. Useful for lightweight constraint validation or progress monitoring. Returns the final maxError, the number of rejected constraints, and the solved `ConstraintDefinition` with updated point positions.
487
- - `route()` — Route a profile through a sequence of geometric elements (legacy API). The solver computes all tangent points and intersections automatically. Steps can include: - `{ point: [x, y] }` route through a point - `{ axis: 'x'|'y', offset: n }` — follow a construction line - `{ line: {...}, until: n }` follow a line clipped to a coordinate - `{ tangent: { center, radius } }` — tangent arc onto a construction circle - `{ fillet: radius }` — fillet between adjacent elements - `{ tangentArc: radius }` — free tangent arc (solver finds center) Returns `this` for chaining. Call `.solve()` after to get the Sketch.
488
- - `rect()` — Add an axis-aligned rectangle concept. Returns a `ConstrainedRect` handle with named vertices, sides, and center.
489
- - `addPolygon()` — Add a general polygon concept (CCW winding enforced). Returns a `ConstrainedPolygon` handle.
490
- - `regularPolygon()` — Add a regular n-gon concept (equal sides, CCW winding). Returns a `ConstrainedRegularPolygon` handle with a center point.
491
- - `groupRect()` — Add a rigid rectangle as a group concept. Returns a `ConstrainedGroupRect` handle with named vertices and sides. The rectangle is fixed in shape — only position (and optionally rotation) varies.
584
+ - `moveTo(x: number, y: number): this` — Move the cursor to `(x, y)` and start a new profile loop.
585
+ - `lineTo(x: number, y: number): this` — Draw a line from the current cursor to `(x, y)`.
586
+ - `lineH(dx: number): this` — Draw a horizontal line of length `dx` from the current cursor.
587
+ - `lineV(dy: number): this` — Draw a vertical line of length `dy` from the current cursor.
588
+ - `lineAngled(length: number, degrees: number): this` — Draw a line of the given `length` at `degrees` from +X.
589
+ - `arcTo(x: number, y: number, radius: number, clockwise?: boolean): this` — Draw a circular arc from the current cursor to `(x, y)` with the given radius.
590
+ - `arcByCenter(centerId: PointId, startId: PointId, endId: PointId, clockwise?: boolean, name?: string, fixedRadius?: boolean): ArcId` — Create an arc from an explicit center point and endpoint IDs.
591
+ - `bezier(p0: any, p1: any, p2: any, p3: any, name?: string): BezierId` — Create a cubic Bezier curve from four control points.
592
+ - `bezierTo(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number): this` — Draw a cubic Bezier from the current cursor to `(x3, y3)`.
593
+ - `blendTo(x: number, y: number, weight?: number): this` Draw a smooth Bezier tangent to the previous arc.
594
+ - `label(name: string): this` Label the current path segment.
595
+ - `close(): this` — Close the current path and register the loop.
596
+ - `addLoopCircle(center: PointId, radius: number, segments?: number): this` — Add a circle loop to the path.
597
+ - `addLoop(points: any[]): this` — Add a closed polygon loop from point IDs.
598
+ - `addProfileLoop(segments: Array<{ kind: "line"; line: any; } | { kind: "arc"; arc: any; } | { kind: "bezier"; bezier: any; }>): this` — Add a profile loop from prebuilt line/arc/bezier segments.
599
+ - `horizontal(line: any): this` — Constrain a line to be horizontal (parallel to the X axis).
600
+ - `vertical(line: any): this` — Constrain a line to be vertical (parallel to the Y axis).
601
+ - `parallel(a: any, b: any): this` — Constrain two lines to be parallel.
602
+ - `sameDirection(a: any, b: any): this` — Constrain two lines to point in the same direction.
603
+ - `oppositeDirection(a: any, b: any): this` — Constrain two lines to point in opposite directions.
604
+ - `blockRotation(points: any[], axis?: "x" | "y"): this` — Prevent 180° rotation of a polygon by anchoring its first edge.
605
+ - `perpendicular(a: any, b: any): this` — Constrain two lines to be perpendicular.
606
+ - `tangent(a: any, b: any): this` — Constrain a line/circle or circle/circle tangency relationship.
607
+ - `equal(a: any, b: any): this` — Constrain two lines to have equal length.
608
+ - `coincident(a: any, b: any): this` — Constrain two points to coincide.
609
+ - `concentric(a: any, b: any): this` — Constrain two circles to share a center.
610
+ - `collinear(point: any, line: any): this` — Constrain a point to lie on the infinite extension of a line.
611
+ - `symmetric(a: any, b: any, axis: any): this` — Constrain two points to be symmetric about an axis line.
612
+ - `fix(point: any, x?: number, y?: number): this` — Pin a point at a specific world location.
613
+ - `midpoint(point: any, line: any): this` — Constrain a point to lie at the midpoint of a line.
614
+ - `pointOnCircle(point: any, circle: any): this` — Constrain a point to lie on the perimeter of a circle.
615
+ - `pointOnLine(point: any, line: any): this` — Constrain a point to lie on the bounded segment of a line.
616
+ - `distance(a: any, b: any, value: number): this` — Constrain the Euclidean distance between two points.
617
+ - `length(line: any, value: number): this` — Constrain the length of a line segment.
618
+ - `angle(a: any, b: any, value: number): this` — Constrain the signed angle from line `a` to line `b`.
619
+ - `radius(circle: any, value: number): this` — Constrain the radius of a circle.
620
+ - `diameter(circle: any, value: number): this` — Constrain the diameter of a circle.
621
+ - `hDistance(a: any, b: any, value: number): this` — Constrain the horizontal distance between two points.
622
+ - `vDistance(a: any, b: any, value: number): this` — Constrain the vertical distance between two points.
623
+ - `pointLineDistance(point: any, line: any, value: number): this` — Constrain the signed perpendicular distance from a point to a line.
624
+ - `lineDistance(a: any, b: any, value: number): this` — Constrain the perpendicular offset distance between two lines.
625
+ - `absoluteAngle(line: any, value: number): this` — Constrain the absolute angle of a line measured from +X.
626
+ - `equalRadius(a: any, b: any): this` — Constrain two circles to have equal radii.
627
+ - `arcLength(arc: any, value: number): this` — Constrain the arc length of an arc.
628
+ - `lineTangentArc(line: any, arc: any, atStart: boolean): this` — Constrain a line to be tangent to an arc at its start or end point.
629
+ - `arcTangentArc(arcA: any, arcB: any, aAtStart?: boolean, bAtStart?: boolean): this` — Constrain two arcs to be tangent at their shared junction point.
630
+ - `bezierTangentArc(bezier: any, arc: any, atBezierStart: boolean, atArcStart: boolean): this` — Constrain a Bezier to be tangent to an arc at one endpoint.
631
+ - `smoothBlend(arc1: any, arc2: any, options?: { weight?: number; arc1End?: "start" | "end"; arc2End?: "start" | "end"; }): BezierId` — Create a Bezier blend between two arcs.
632
+ - `shapeWidth(shape: any, value: number): this` Constrain a shape's width.
633
+ - `shapeHeight(shape: any, value: number): this` — Constrain a shape's height.
634
+ - `shapeCentroidX(shape: any, value: number): this` — Constrain a shape's centroid X position.
635
+ - `shapeCentroidY(shape: any, value: number): this` — Constrain a shape's centroid Y position.
636
+ - `shapeArea(shape: any, value: number): this` — Constrain a shape's area.
637
+ - `shapeEqualCentroid(a: any, b: any): this` — Constrain two shapes to have the same centroid.
638
+ - `angleBetween(a: any, b: any, value: number): this` — Constrain the unsigned angle between two lines.
639
+ - `ccw(...points: any[]): this` — Constrain all given points to be in counter-clockwise order.
640
+ - `offsetX(a: any, b: any, value: number): this` — Constrain the horizontal (X-axis) offset between two lines. Uses the start-point of each line to measure horizontal distance. `value` is the signed distance: b.startPt.x − a.startPt.x = value.
641
+ - `offsetY(a: any, b: any, value: number): this` — Constrain the vertical (Y-axis) offset between two lines. Uses the start-point of each line to measure vertical distance. `value` is the signed distance: b.startPt.y − a.startPt.y = value.
642
+ - `importPoint(pt: { x: number; y: number; }, fixed?: boolean): PointId` Import a `Point2D` object into the sketch.
643
+ - `importLine(l: { start: { x: number; y: number; }; end: { x: number; y: number; }; }, fixed?: boolean): LineId` — Import a `Line2D` object into the sketch.
644
+ - `importRectangle(r: { vertices: [ { x: number; y: number; }, { x: number; y: number; }, { x: number; y: number; }, { x: number; y: number; } ]; }, fixed?: boolean): { ... }` — Import a `Rectangle2D` as four points and four lines.
645
+ - `referencePoint(x: number, y: number): PointId` Add a fixed reference point at `(x, y)`.
646
+ - `referenceLine(x1: number, y1: number, x2: number, y2: number): LineId` — Add a fixed reference line from `(x1, y1)` to `(x2, y2)`.
647
+ - `referenceFrom(source: ConstraintSketch, entityId: string): PointId | LineId | null` — Import a single named entity from a solved sketch as fixed reference geometry.
648
+ - `referenceAllFrom(source: ConstraintSketch): { points: Map<string, PointId>; lines: Map<string, LineId>; }` — Import all non-construction entities from a solved sketch as fixed references.
649
+ - `point(x?: number, y?: number, fixed?: boolean): PointId` — Add a free point to the sketch at `(x, y)`. If `x` or `y` are omitted, the point is placed at the bounding-box center of existing geometry so it starts near other entities rather than at the origin. Throws if either coordinate is `NaN` or `Infinity`.
650
+ - `pointAt(index: number): PointId` — Return the `PointId` of the point created at the given insertion index.
651
+ - `line(a: PointId, b: PointId, construction?: boolean, name?: string): LineId` — Connect two existing points with a line segment. Pass `construction = true` for a helper line that participates in constraints but is excluded from the solved sketch output (not part of any profile loop). **Example** (construction axis for symmetry) ```ts const axis = sk.line(sk.point(0, -50), sk.point(0, 50), true); sk.symmetric(p1, p2, axis); ```
652
+ - `lineAt(index: number): LineId` — Return the `LineId` of the line created at the given insertion index.
653
+ - `circle(center: PointId, radius: number, construction?: boolean, segments?: number, name?: string): CircleId` — Add a circle to the sketch with the given center point and initial radius. The radius is a starting value — if you add a `radius()` or `diameter()` constraint, the solver will adjust it. Non-construction circles automatically register a loop.
654
+ - `circleAt(index: number): CircleId` — Return the `CircleId` of the circle created at the given insertion index.
655
+ - `shape(lines: LineId[]): ShapeId` — Register a named shape (closed polygon) from an ordered list of line IDs. The `ShapeId` can be passed to `shapeWidth()`, `shapeHeight()`, `shapeArea()`, `shapeCentroidX()`, `shapeCentroidY()`, and `shapeEqualCentroid()` constraints. Shape registration is done automatically by concept factories like `rect()` and `addPolygon()`.
656
+ - `group(opts?: { x?: number; y?: number; theta?: number; id?: string; }): SketchGroupBuilder` — Create a rigid-body group with a local coordinate frame. Points and lines added to the group move together as a unit — the solver sees 3 DOF (x, y, θ) instead of 2N per point. After configuring the group, call `.done()` to register it and receive a `SketchGroupHandle`. Group points are addressable by their `PointId` in all sketch constraints (e.g. `sk.coincident`, `sk.distance`) just like any other points. **Example** ```ts const g = sk.group({ x: 50, y: 30 }); const p0 = g.point(0, 0); // local origin → world (50, 30) const p1 = g.point(100, 0); // local (100,0) → world (150, 30) const l = g.line(p0, p1); g.fixRotation(); const handle = g.done(); // p0, p1 work in constraints like any other PointId: sk.coincident(p0, someExternalPoint); ```
657
+ - `constrain(constraint: Omit<SketchConstraint, "id">): this` — Add a raw constraint object to the builder.
658
+ - `solve(options?: SolveOptions): ConstraintSketch | Sketch` — Run the constraint solver and return a solved sketch. The returned `ConstraintSketch` extends `Sketch` and can be used directly in all 3D operations (`extrude`, `revolve`, etc.). It also exposes `constraintMeta` with the solver status: ```ts const result = sk.solve(); result.constraintMeta.status; // 'fully' | 'under' | 'over' | 'over-redundant' result.constraintMeta.dof; // 0 = fully constrained result.constraintMeta.maxError; // residual — should be < 1e-6 result.inspect(); // human-readable summary result.withUpdatedConstraint('cst-5', 120); // update a dimension without rebuilding ``` **Troubleshooting** - **Under-constrained (dof > 0)** — add `fix()`, `length()`, or other dimensional constraints. - **Over-constrained** — conflicting constraints are auto-rejected. Check `result.constraintMeta.constraints` and `result.inspect()`. - **maxError > 1e-6** — solver did not converge; check for contradictory constraints.
659
+ - `solveConstraintsOnly(options?: SolveOptions): { maxError: number; rejectedCount: number; definition: ConstraintDefinition; }` — Run the solver without building a full `ConstraintSketch`. Lighter than `solve()` skips profile and DOF analysis. Useful for lightweight constraint validation or progress monitoring mid-construction.
660
+ - `route(x: number, y: number): RouteBuilder` — Start a directional route from coordinates. Returns a `RouteBuilder` - describe the path with up/down/left/right/arcLeft/arcRight. Each method returns the entity ID (`LineId` or `ArcId`) for use in `sk.*` constraints. ```ts const r = sk.route(0, 0); const stem = r.up(18); r.arcLeft(8.9); const neck = r.down(); r.done(); sk.offsetX(stem, neck, 10.8); ```
661
+ - `rect(options?: RectOptions): ConstrainedRect` — Add an axis-aligned rectangle concept. Returns a `ConstrainedRect` handle with named vertices, sides, and center.
662
+ - `addPolygon(options: PolygonOptions): ConstrainedPolygon` — Add a general polygon concept (CCW winding enforced). Returns a `ConstrainedPolygon` handle.
663
+ - `regularPolygon(options: RegularPolygonOptions): ConstrainedRegularPolygon` — Add a regular n-gon concept (equal sides, CCW winding). Returns a `ConstrainedRegularPolygon` handle with a center point.
664
+ - `groupRect(options: GroupRectOptions): ConstrainedGroupRect` — Add a rigid rectangle as a group concept. Returns a `ConstrainedGroupRect` handle with named vertices and sides. The rectangle is fixed in shape — only position (and optionally rotation) varies.
492
665
 
493
666
  ### `ConstraintSketch`
494
667
 
@@ -501,23 +674,25 @@ Convert radians to degrees
501
674
 
502
675
  **Methods:**
503
676
 
504
- - `detectArrangement()` — Enumerate all bounded regions formed by the line arrangement of this sketch. Construction lines are excluded. Regions are returned largest-first by area.
505
- - `detectArrangementRegion()` — Select the single arrangement region that contains the given seed point. Throws if no region contains the seed.
506
- - `withUpdatedConstraint()` — withUpdatedConstraint(constraintId: string, value: number): ConstraintSketch
507
- - `inspect()` — Return a human-readable diagnostic string of the solved state.
677
+ - `detectArrangement(): Sketch[]` — Enumerate all bounded regions formed by the line arrangement of this sketch. Construction lines are excluded. Regions are returned largest-first by area.
678
+ - `detectArrangementRegion(seed: [ number, number ]): Sketch` — Select the single arrangement region that contains the given seed point. Throws if no region contains the seed.
679
+ - `withUpdatedConstraint(constraintId: string, value: number): ConstraintSketch` — Re-solve the sketch after changing the value of one existing constraint. Use this for interactive dimension edits without rebuilding the whole sketch graph. It attempts a warm-started solve first, then falls back to a full solve if needed.
680
+ - `inspect(): string` — Return a human-readable diagnostic string of the solved state.
508
681
 
509
682
  ### `SketchGroupBuilder`
510
683
 
511
- **Methods:**
512
-
513
- - `point()` — Add a point in local coordinates. Returns its globally-addressable PointId.
514
- - `line()` — Connect two group points with a line. Both must be PointIds from this group.
515
- - `fixRotation()` — Freeze rotation (θ). Group can still translate — 2 DOF remain.
516
- - `fix()` — Freeze all 3 DOF — group is completely fixed.
517
- - `done()` — Finalize and register the group with the builder. Returns a handle for referencing group points/lines in constraints.
684
+ - `point(lx: number, ly: number): PointId` — Add a point in local coordinates. Returns its globally-addressable PointId.
685
+ - `line(a: PointId, b: PointId, name?: string): LineId` — Connect two group points with a line. Both must be PointIds from this group.
686
+ - `fixRotation(): this` — Freeze rotation (theta). Group can still translate - 2 DOF remain.
687
+ - `fix(): this` — Freeze all 3 DOF - group is completely fixed.
688
+ - `done(): SketchGroupHandle` — Finalize and register the group with the builder.
518
689
 
519
690
  ### `Point2D`
520
691
 
692
+ An immutable 2D point with measurement and construction helpers.
693
+
694
+ Used as construction geometry in sketches, constraints, and analytic measurements. All methods return new instances — `Point2D` is immutable.
695
+
521
696
  **Properties:**
522
697
 
523
698
  | Property | Type | Description |
@@ -527,13 +702,17 @@ Convert radians to degrees
527
702
 
528
703
  **Methods:**
529
704
 
530
- - `distanceTo()` — distanceTo(other: Point2D): number
531
- - `midpointTo()` — midpointTo(other: Point2D): Point2D
532
- - `translate()` — translate(dx: number, dy: number): Point2D
533
- - `toTuple()` — toTuple(): [ number, number ]
705
+ - `distanceTo(other: Point2D): number` — Measure straight-line distance to another point.
706
+ - `midpointTo(other: Point2D): Point2D` — Compute the midpoint between this point and another point.
707
+ - `translate(dx: number, dy: number): Point2D` — Return a point shifted by the given delta.
708
+ - `toTuple(): [ number, number ]` — Convert this point to a plain `[x, y]` tuple.
534
709
 
535
710
  ### `Line2D`
536
711
 
712
+ An immutable 2D line segment with length, angle, intersection, and parallel helpers.
713
+
714
+ Provides both segment-only (`intersectSegment`) and infinite-line (`intersect`) intersection queries. All methods return new instances.
715
+
537
716
  **Properties:**
538
717
 
539
718
  | Property | Type | Description |
@@ -543,19 +722,23 @@ Convert radians to degrees
543
722
 
544
723
  **Methods:**
545
724
 
546
- - `get length()` — get length(): number
547
- - `get midpoint()` — get midpoint(): Point2D
548
- - `get angle()` — get angle(): number
549
- - `get direction()` — get direction(): [ number, number ]
550
- - `parallel()` — Create a line parallel to this one, offset by distance. positive = left of direction
551
- - `intersect()` Intersection point of two lines (treating them as infinite lines). Returns null if lines are parallel.
552
- - `intersectSegment()` — Intersection point within both line segments only. Returns null if segments don't cross.
553
- - `static fromCoordinates()` — static fromCoordinates(x1: number, y1: number, x2: number, y2: number): Line2D
554
- - `static fromPointAndAngle()` — static fromPointAndAngle(origin: Point2D, angleDeg: number, length: number): Lin
555
- - `static fromPointAndDirection()` — static fromPointAndDirection(origin: Point2D, dir: [ number, number ], length: n
725
+ - `get length(): number` — Length of the line segment.
726
+ - `get midpoint(): Point2D` — Midpoint of the line segment.
727
+ - `get angle(): number` — Direction angle in degrees, measured CCW from +X.
728
+ - `get direction(): [ number, number ]` — Unit direction vector from start to end.
729
+ - `parallel(distance: number): Line2D` — Create a parallel line offset by the given distance. Positive distance shifts to the left of the line direction.
730
+ - `intersect(other: Line2D): Point2D | null` Intersect this line with another infinite line.
731
+ - `intersectSegment(other: Line2D): Point2D | null` — Intersect this line with another as bounded segments.
732
+ - `static fromCoordinates(x1: number, y1: number, x2: number, y2: number): Line2D` — Create a line from raw coordinates.
733
+ - `static fromPointAndAngle(origin: Point2D, angleDeg: number, length: number): Line2D` — Create a line from a start point, angle, and length.
734
+ - `static fromPointAndDirection(origin: Point2D, dir: [ number, number ], length: number): Line2D` — Create a line from a start point, direction vector, and length.
556
735
 
557
736
  ### `Circle2D`
558
737
 
738
+ An immutable 2D circle with area, circumference, and extrusion support.
739
+
740
+ Extruding a `Circle2D` produces a cylinder with named `top`, `bottom`, and `side` faces accessible via the topology API.
741
+
559
742
  **Properties:**
560
743
 
561
744
  | Property | Type | Description |
@@ -565,33 +748,54 @@ Convert radians to degrees
565
748
 
566
749
  **Methods:**
567
750
 
568
- - `get diameter()` — get diameter(): number
569
- - `get circumference()` — get circumference(): number
570
- - `get area()` — get area(): number
571
- - `pointAtAngle()` — Point on the circle at given angle (degrees, 0=right, CCW)
572
- - `translate()` — translate(dx: number, dy: number): Circle2D
573
- - `toSketch()` — toSketch(segments?: number): Sketch
574
- - `extrude()` — Extrude to Shape with top/bottom/side faces via topology WeakMap
575
- - `static fromCenterAndRadius()` — static fromCenterAndRadius(center: Point2D, radius: number): Circle2D
576
- - `static fromDiameter()` — static fromDiameter(center: Point2D, diameter: number): Circle2D
751
+ - `get diameter(): number` — Diameter of the circle.
752
+ - `get circumference(): number` — Circumference of the circle.
753
+ - `get area(): number` — Area of the circle.
754
+ - `pointAtAngle(angleDeg: number): Point2D` — Return a point on the circle at the given angle.
755
+ - `translate(dx: number, dy: number): Circle2D` — Return a translated circle.
756
+ - `toSketch(segments?: number): Sketch` — Convert this circle to a sketch profile.
757
+ - `extrude(height: number, segments?: number): Shape` — Extrude the circle into a solid cylinder.
758
+ - `static fromCenterAndRadius(center: Point2D, radius: number): Circle2D` — Create a circle from its center and radius.
759
+ - `static fromDiameter(center: Point2D, diameter: number): Circle2D` — Create a circle from its center and diameter.
577
760
 
578
761
  ### `Rectangle2D`
579
762
 
580
- A rectangle with named sides and vertices. Sides are named based on the rectangle's local orientation at construction time. Vertices go: bottom-left, bottom-right, top-right, top-left (CCW from bottom-left).
763
+ A rectangle with named sides, vertices, and extrusion support.
581
764
 
582
- **Methods:**
765
+ **Details**
766
+
767
+ Sides are named based on the rectangle's local orientation at construction time. Vertices go: bottom-left, bottom-right, top-right, top-left (CCW).
768
+
769
+ Extruding a `Rectangle2D` produces a `Shape` with named faces: `top`, `bottom`, `side-left`, `side-right`, `side-top`, `side-bottom`. These are accessible via the topology API (`.face()`, `.edge()`).
770
+
771
+ **Example**
772
+
773
+ ```ts
774
+ const r = rectangle(0, 0, 100, 60);
775
+ r.side('top'); r.side('left'); // Line2D
776
+ r.vertex('top-left'); // Point2D
777
+ r.width; r.height; r.center;
778
+ const [d1, d2] = r.diagonals(); // [bl-tr, br-tl]
779
+
780
+ r.toSketch(); // Sketch (for 2D operations)
781
+ r.extrude(20); // Shape with named faces
782
+
783
+ Rectangle2D.fromCenterAndDimensions(point(50, 30), 100, 60);
784
+ Rectangle2D.from2Corners(point(0, 0), point(100, 60));
785
+ Rectangle2D.from3Points(p1, p2, p3); // free-angle rectangle
786
+ ```
583
787
 
584
- - `get width()` — get width(): number
585
- - `get height()` — get height(): number
586
- - `get center()` — get center(): Point2D
587
- - `side()` — side(name: RectSide): Line2D
588
- - `sideAt()` — Get side by index (0=bottom, 1=right, 2=top, 3=left)
589
- - `vertex()` — vertex(name: RectVertex): Point2D
590
- - `diagonals()` — Get the two diagonals of this rectangle
591
- - `toSketch()` — toSketch(): Sketch
592
- - `translate()` — translate(dx: number, dy: number): Rectangle2D
593
- - `static fromDimensions()` — Create from origin corner + width/height (axis-aligned)
594
- - `static fromCenterAndDimensions()` — Create centered at a point
595
- - `static from2Corners()` — Create from two opposite corners (axis-aligned)
596
- - `static from3Points()` — Create from three points (free angle). p1-p2 defines one side, p3 gives the height direction.
597
- - `extrude()` — Extrude this rectangle into a 3D Shape with named faces and edges via topology WeakMap
788
+ - `get width(): number` — Width of the rectangle.
789
+ - `get height(): number` — Height of the rectangle.
790
+ - `get center(): Point2D` — Geometric center of the rectangle.
791
+ - `side(name: RectSide): Line2D` — Return a named side of the rectangle.
792
+ - `sideAt(index: number): Line2D` — Return a side by index.
793
+ - `vertex(name: RectVertex): Point2D` — Return a named vertex of the rectangle.
794
+ - `diagonals(): [ Line2D, Line2D ]` — Return the two diagonals of the rectangle.
795
+ - `toSketch(): Sketch` — Convert the rectangle to a sketch profile.
796
+ - `translate(dx: number, dy: number): Rectangle2D` — Return a translated rectangle.
797
+ - `static fromDimensions(x: number, y: number, width: number, height: number): Rectangle2D` — Create an axis-aligned rectangle from origin corner plus width and height.
798
+ - `static fromCenterAndDimensions(center: Point2D, width: number, height: number): Rectangle2D` — Create a rectangle centered on a point.
799
+ - `static from2Corners(p1: Point2D, p2: Point2D): Rectangle2D` — Create an axis-aligned rectangle from two opposite corners.
800
+ - `static from3Points(p1: Point2D, p2: Point2D, p3: Point2D): Rectangle2D` — Create a free-angle rectangle from three points. `p1` and `p2` define one edge, and `p3` chooses the perpendicular side.
801
+ - `extrude(height: number, up?: boolean): Shape` — Extrude the rectangle into a solid prism with named topology.