forgecad 0.6.3 → 0.8.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 (234) hide show
  1. package/README.md +3 -12
  2. package/dist/assets/{AdminPage-CeqCUUgu.js → AdminPage-D4bocK4E.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-D3A_g8V3.js} +329 -163
  5. package/dist/assets/{EditorApp-CnC2k4cW.css → EditorApp-BWYUSpUN.css} +590 -136
  6. package/dist/assets/EditorApp-Cihhqcsq.js +11692 -0
  7. package/dist/assets/{EmbedViewer-DBlzmQ5i.js → EmbedViewer-kWjKaC_t.js} +2 -4
  8. package/dist/assets/LandingPageProofDriven-Bg2IUc3l.css +856 -0
  9. package/dist/assets/LandingPageProofDriven-DXkKlyhI.js +601 -0
  10. package/dist/assets/PricingPage-BsU5vzEx.js +232 -0
  11. package/dist/assets/{SettingsPage-BqCh9JcC.js → SettingsPage-PqvpAKIs.js} +129 -5
  12. package/dist/assets/{evalWorker-Ql-aKwLA.js → evalWorker-C-hzNUMy.js} +8949 -3161
  13. package/dist/assets/{Viewport-CoB46f5R.js → index-Pz321YAt.js} +38382 -7501
  14. package/dist/assets/{index-2hfs_ub0.css → index-ay13WNfa.css} +726 -53
  15. package/dist/assets/{javascript-DCxGoE5Y.js → javascript-DAl8Gmyo.js} +1 -1
  16. package/dist/assets/{manifold-CqNMHHKO.js → manifold-BcbjWLIo.js} +4 -3
  17. package/dist/assets/{manifold-Cce9wRFz.js → manifold-DBckbFgx.js} +1 -1
  18. package/dist/assets/{manifold-D6BeHIOo.js → manifold-O2AAGXyj.js} +1 -1
  19. package/dist/assets/{reportWorker-sFEFonXf.js → reportWorker-Dxr-5A7w.js} +8760 -3559
  20. package/dist/assets/{vendor-react-Dt7-aaJH.js → vendor-react-CG3i_wp0.js} +65 -8
  21. package/dist/docs/index.html +2 -2
  22. package/dist/docs-raw/CLI.md +341 -718
  23. package/dist/docs-raw/generated/assembly.md +699 -112
  24. package/dist/docs-raw/generated/concepts.md +1834 -1346
  25. package/dist/docs-raw/generated/core.md +1012 -1059
  26. package/dist/docs-raw/generated/curves.md +759 -116
  27. package/dist/docs-raw/generated/lib.md +43 -748
  28. package/dist/docs-raw/generated/output.md +139 -245
  29. package/dist/docs-raw/generated/sdf.md +208 -0
  30. package/dist/docs-raw/generated/sheet-metal.md +473 -21
  31. package/dist/docs-raw/generated/sketch.md +1518 -362
  32. package/dist/docs-raw/generated/viewport.md +368 -299
  33. package/dist/docs-raw/generated/wood.md +104 -0
  34. package/dist/index.html +2 -2
  35. package/dist/landing/proof-ams-adapter.png +0 -0
  36. package/dist/landing/proof-bolt-and-nut.png +0 -0
  37. package/dist/landing/proof-fillet-enclosure.png +0 -0
  38. package/dist/landing/proof-glasses.png +0 -0
  39. package/dist/landing/proof-gyroid.png +0 -0
  40. package/dist/sitemap.xml +6 -6
  41. package/dist-cli/forgecad.js +12321 -5700
  42. package/dist-cli/forgecad.js.map +1 -0
  43. package/dist-cli/solver-46FFSK2U.js +363 -0
  44. package/dist-cli/solver-46FFSK2U.js.map +1 -0
  45. package/dist-skill/CONTEXT.md +4890 -6302
  46. package/dist-skill/SKILL-dev.md +22 -66
  47. package/dist-skill/SKILL.md +20 -59
  48. package/dist-skill/docs/API/core/concepts.md +37 -92
  49. package/dist-skill/docs/CLI.md +341 -718
  50. package/dist-skill/docs/generated/assembly.md +699 -112
  51. package/dist-skill/docs/generated/core.md +1012 -1059
  52. package/dist-skill/docs/generated/curves.md +759 -116
  53. package/dist-skill/docs/generated/lib.md +43 -748
  54. package/dist-skill/docs/generated/output.md +139 -245
  55. package/dist-skill/docs/generated/sdf.md +208 -0
  56. package/dist-skill/docs/generated/sheet-metal.md +473 -21
  57. package/dist-skill/docs/generated/sketch.md +1518 -362
  58. package/dist-skill/docs/generated/viewport.md +368 -299
  59. package/dist-skill/docs/generated/wood.md +104 -0
  60. package/dist-skill/docs/guides/coordinate-system.md +11 -17
  61. package/dist-skill/docs/guides/geometry-conventions.md +13 -70
  62. package/dist-skill/docs/guides/joint-design.md +78 -0
  63. package/dist-skill/docs/guides/modeling-recipes.md +22 -195
  64. package/dist-skill/docs/guides/positioning.md +88 -147
  65. package/dist-skill/docs-dev/API/core/concepts.md +78 -0
  66. package/dist-skill/docs-dev/CLI.md +488 -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 +2 -4
  70. package/dist-skill/docs-dev/component-model.md +164 -0
  71. package/dist-skill/docs-dev/generated/assembly.md +779 -0
  72. package/dist-skill/docs-dev/generated/core.md +1676 -0
  73. package/dist-skill/docs-dev/generated/curves.md +855 -0
  74. package/dist-skill/docs-dev/generated/lib.md +55 -0
  75. package/dist-skill/docs-dev/generated/output.md +234 -0
  76. package/dist-skill/docs-dev/generated/sdf.md +208 -0
  77. package/dist-skill/docs-dev/generated/sheet-metal.md +506 -0
  78. package/dist-skill/docs-dev/generated/sketch.md +1753 -0
  79. package/dist-skill/docs-dev/generated/viewport.md +513 -0
  80. package/dist-skill/docs-dev/generated/wood.md +104 -0
  81. package/dist-skill/docs-dev/guides/coordinate-system.md +46 -0
  82. package/dist-skill/docs-dev/guides/geometry-conventions.md +52 -0
  83. package/dist-skill/docs-dev/guides/joint-design.md +78 -0
  84. package/dist-skill/docs-dev/guides/modeling-recipes.md +77 -0
  85. package/dist-skill/docs-dev/guides/positioning.md +151 -0
  86. package/dist-skill/{docs → docs-dev}/guides/skill-maintenance.md +21 -10
  87. package/dist-skill/{docs → docs-dev}/internals/compiler.md +5 -6
  88. package/dist-skill/{docs → docs-dev}/internals/constraint-solver-quality.md +0 -1
  89. package/dist-skill/{docs → docs-dev}/internals/constraint-solver.md +0 -1
  90. package/dist-skill/{docs → docs-dev}/internals/sketch-2d-pipeline.md +2 -3
  91. package/examples/api/attachTo-basics.forge.js +8 -8
  92. package/examples/api/bill-of-materials.forge.js +9 -9
  93. package/examples/api/bolt-pattern.forge.js +5 -5
  94. package/examples/api/boolean-operations.forge.js +5 -5
  95. package/examples/api/bounding-box-visualizer.forge.js +3 -3
  96. package/examples/api/clone-duplicate.forge.js +2 -2
  97. package/examples/api/colors-union-vs-array.forge.js +6 -6
  98. package/examples/api/connector-assembly.forge.js +8 -6
  99. package/examples/api/connector-basics.forge.js +7 -7
  100. package/examples/api/constrained-sketch-mechanical.forge.js +4 -4
  101. package/examples/api/elbow-test.forge.js +3 -3
  102. package/examples/api/extrude-options.forge.js +8 -14
  103. package/examples/api/feature-created-faces.forge.js +6 -10
  104. package/examples/api/fillet-showcase.forge.js +2 -2
  105. package/examples/api/folded-service-panel-cover.forge.js +2 -2
  106. package/examples/api/gears-tier1.forge.js +5 -5
  107. package/examples/api/group-test.forge.js +3 -3
  108. package/examples/api/group-vs-union.forge.js +1 -1
  109. package/examples/api/highlight-debug.forge.js +4 -0
  110. package/examples/api/js-module-pillars.js +1 -1
  111. package/examples/api/js-module-scene.js +2 -2
  112. package/examples/api/mesh-import-slats.forge.js +4 -4
  113. package/examples/api/patterns.forge.js +3 -3
  114. package/examples/api/pointAlong-orientation.forge.js +3 -3
  115. package/examples/api/profile-2020-b-slot6.forge.js +4 -5
  116. package/examples/api/route-perimeter-flange.forge.js +1 -1
  117. package/examples/api/sdf-rover-demo.forge.js +10 -10
  118. package/examples/api/sketch-on-face-demo.forge.js +2 -2
  119. package/examples/api/sketch-regions.forge.js +4 -4
  120. package/examples/api/sketch-rounding-strategies.forge.js +1 -1
  121. package/examples/api/smooth-curve-connections.forge.js +1 -1
  122. package/examples/api/transition-curves.forge.js +4 -4
  123. package/examples/api/variable-sweep-pure-sdf-test.forge.js +162 -0
  124. package/examples/api/variable-sweep-test.forge.js +2 -2
  125. package/examples/api/wood-joinery.forge.js +60 -0
  126. package/examples/compiler-corpus/enclosure-shell-cuts.forge.js +3 -3
  127. package/examples/compiler-corpus/fastener-plate-variants.forge.js +2 -2
  128. package/examples/constraints/01-fully-constrained-rect.forge.js +2 -2
  129. package/examples/constraints/02-underconstrained-triangle.forge.js +1 -1
  130. package/examples/constraints/03-redundant-constraints.forge.js +2 -2
  131. package/examples/constraints/05-parallel-with-linedistance.forge.js +2 -2
  132. package/examples/constraints/06-complex-spectrogram.forge.js +1 -1
  133. package/examples/constraints/07-perpendicular-chain.forge.js +4 -4
  134. package/examples/constraints/08-symmetric-bracket.forge.js +4 -4
  135. package/examples/constraints/09-stress-spiral.forge.js +1 -1
  136. package/examples/constraints/10-stress-honeycomb.forge.js +1 -1
  137. package/examples/constraints/11-surface-grid.forge.js +2 -2
  138. package/examples/constraints/12-surface-nested.forge.js +4 -4
  139. package/examples/constraints/13-surface-complex.forge.js +1 -1
  140. package/examples/exact-arc-housing.forge.js +12 -0
  141. package/examples/experiments/drone-arm.forge.js +53 -0
  142. package/examples/furniture/adjustable-table.forge.js +15 -15
  143. package/examples/furniture/bathroom.forge.js +26 -26
  144. package/examples/furniture/chair.forge.js +13 -13
  145. package/examples/furniture/picture-frame.forge.js +6 -6
  146. package/examples/furniture/shoe-rack-doors.forge.js +8 -8
  147. package/examples/furniture/shoe-rack.forge.js +7 -7
  148. package/examples/furniture/table-lamp.forge.js +8 -8
  149. package/examples/gcode/lissajous-vase.forge.js +4 -4
  150. package/examples/gcode/math-surface.forge.js +3 -3
  151. package/examples/gcode/parametric-vase.forge.js +4 -4
  152. package/examples/gcode/spiral-tower.forge.js +4 -4
  153. package/examples/generative/crystal-growth.forge.js +9 -9
  154. package/examples/generative/frost-spires.forge.js +9 -9
  155. package/examples/generative/golden-spiral-tower.forge.js +11 -11
  156. package/examples/generative/molten-forge.forge.js +6 -6
  157. package/examples/generative/neon-coral.forge.js +7 -7
  158. package/examples/mechanical/3d-printer.forge.js +37 -37
  159. package/examples/mechanical/5-finger-robot-hand.forge.js +19 -19
  160. package/examples/mechanical/airplane-propeller.forge.js +9 -9
  161. package/examples/mechanical/bolt-and-nut.forge.js +10 -10
  162. package/examples/mechanical/door-with-hinges.forge.js +7 -7
  163. package/examples/mechanical/fillet-enclosure.forge.js +15 -11
  164. package/examples/mechanical/headphone-hanger-v2.forge.js +11 -11
  165. package/examples/mechanical/robot_hand.forge.js +24 -24
  166. package/examples/mechanical/robot_hand_2.forge.js +26 -26
  167. package/examples/nurbs-surface.forge.js +8 -0
  168. package/examples/nurbs-tube.forge.js +7 -0
  169. package/examples/products/bottle.forge.js +8 -8
  170. package/examples/products/chess-set.forge.js +25 -25
  171. package/examples/products/classical-piano.forge.js +20 -20
  172. package/examples/products/clock.forge.js +33 -33
  173. package/examples/products/cup.forge.js +5 -5
  174. package/examples/products/iphone.forge.js +20 -20
  175. package/examples/products/laptop.forge.js +24 -24
  176. package/examples/products/laser-cut-box.forge.js +6 -6
  177. package/examples/products/laser-cut-tray.forge.js +6 -6
  178. package/examples/products/liquid-soap-dispenser.forge.js +23 -23
  179. package/examples/products/origami-fish.forge.js +14 -12
  180. package/examples/products/spiderman-cake.forge.js +6 -6
  181. package/examples/shelf/container.forge.js +5 -5
  182. package/examples/shelf/shelf-unit.forge.js +6 -6
  183. package/examples/toolbox/bolted-joint.forge.js +7 -7
  184. package/package.json +9 -4
  185. package/dist/assets/EditorApp-B-vQvgam.js +0 -9888
  186. package/dist/assets/LandingPage-C5n9hDXI.js +0 -322
  187. package/dist/assets/PublishedModelPage-Dt7PCVBj.js +0 -146
  188. package/dist/assets/__vite-browser-external-CURh0WXD.js +0 -8
  189. package/dist/assets/deserializeRunResult-BLAFoiE0.js +0 -19365
  190. package/dist/assets/index-1CYp3zUp.js +0 -1455
  191. package/dist-skill/docs/API/API.md +0 -1666
  192. package/dist-skill/docs/API/README.md +0 -37
  193. package/dist-skill/docs/API/assembly/assembly.md +0 -617
  194. package/dist-skill/docs/API/core/edge-queries.md +0 -130
  195. package/dist-skill/docs/API/core/parameters.md +0 -122
  196. package/dist-skill/docs/API/core/reserved-terms.md +0 -137
  197. package/dist-skill/docs/API/core/sdf.md +0 -326
  198. package/dist-skill/docs/API/core/skill-cli.md +0 -194
  199. package/dist-skill/docs/API/core/skill-guide.md +0 -205
  200. package/dist-skill/docs/API/core/specs.md +0 -186
  201. package/dist-skill/docs/API/core/topology.md +0 -372
  202. package/dist-skill/docs/API/entities.md +0 -268
  203. package/dist-skill/docs/API/output/bom.md +0 -58
  204. package/dist-skill/docs/API/output/brep-export.md +0 -87
  205. package/dist-skill/docs/API/output/dimensions.md +0 -67
  206. package/dist-skill/docs/API/output/export.md +0 -110
  207. package/dist-skill/docs/API/output/gcode.md +0 -195
  208. package/dist-skill/docs/API/runtime/viewport.md +0 -420
  209. package/dist-skill/docs/API/sheet-metal/sheet-metal.md +0 -185
  210. package/dist-skill/docs/API/sketch/anchor.md +0 -37
  211. package/dist-skill/docs/API/sketch/booleans.md +0 -91
  212. package/dist-skill/docs/API/sketch/core.md +0 -73
  213. package/dist-skill/docs/API/sketch/extrude.md +0 -62
  214. package/dist-skill/docs/API/sketch/on-face.md +0 -104
  215. package/dist-skill/docs/API/sketch/operations.md +0 -78
  216. package/dist-skill/docs/API/sketch/path.md +0 -75
  217. package/dist-skill/docs/API/sketch/primitives.md +0 -146
  218. package/dist-skill/docs/API/sketch/regions.md +0 -80
  219. package/dist-skill/docs/API/sketch/text.md +0 -108
  220. package/dist-skill/docs/API/sketch/transforms.md +0 -65
  221. package/dist-skill/docs/API/toolbox/fasteners.md +0 -129
  222. package/dist-skill/docs/INDEX.md +0 -94
  223. package/dist-skill/docs/RELEASING.md +0 -55
  224. package/dist-skill/docs/cli-monetization.md +0 -111
  225. package/dist-skill/docs/deployment.md +0 -281
  226. package/dist-skill/docs/generated/concepts.md +0 -2112
  227. package/dist-skill/docs/internals/shape-from-slices.md +0 -152
  228. package/dist-skill/docs/platform/admin.md +0 -45
  229. package/dist-skill/docs/platform/architecture.md +0 -79
  230. package/dist-skill/docs/platform/auth.md +0 -110
  231. package/dist-skill/docs/platform/email.md +0 -67
  232. package/dist-skill/docs/platform/projects.md +0 -111
  233. package/dist-skill/docs/platform/sharing.md +0 -90
  234. package/dist-skill/docs/runbook.md +0 -345
@@ -5,368 +5,547 @@ skill-order: 100
5
5
 
6
6
  # Sketch API
7
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
8
  2D geometry creation, transforms, booleans, constrained sketches, and extrusion.
11
9
 
10
+ ## Contents
11
+
12
+ - [2D Sketch Primitives](#2d-sketch-primitives) — `path`, `stroke`, `rect`, `circle2d`, `roundedRect`, `polygon`, `ngon`, `ellipse`, `slot`, `arcSlot`, `star`
13
+ - [2D Sketch Booleans](#2d-sketch-booleans) — `union2d`, `difference2d`, `intersection2d`
14
+ - [2D Sketch Features](#2d-sketch-features) — `fillet2d`, `chamfer2d`
15
+ - [2D Text](#2d-text) — `loadFont`, `text2d`, `textWidth`
16
+ - [Constrained Sketches](#constrained-sketches) — `constrainedSketch`, `addRect`, `addPolygon`, `addRegularPolygon`
17
+ - [2D Geometry Helpers](#2d-geometry-helpers) — `point`, `line`, `circle`, `degrees`, `radians`
18
+ - [Sketch](#sketch) — Transforms, Booleans, Features, Promotion, Placement, Labels, Measurement
19
+ - [ConstrainedSketchBuilder](#constrainedsketchbuilder) — Drawing, Entities, Geometric Constraints, Dimensional Constraints, Coincidence & Equality, Tangent Transitions, Shape Constraints, Positioning, Solving
20
+ - [ConstraintSketch](#constraintsketch)
21
+ - [SketchGroupBuilder](#sketchgroupbuilder)
22
+ - [Point2D](#point2d)
23
+ - [Line2D](#line2d)
24
+ - [Circle2D](#circle2d)
25
+ - [Rectangle2D](#rectangle2d)
26
+
12
27
  ## Functions
13
28
 
14
29
  ### 2D Sketch Primitives
15
30
 
16
- Create 2D profiles for extrusion and other operations.
31
+ #### `path()` — Create a new [`PathBuilder`](/docs/curves#pathbuilder) for tracing a 2D outline point by point.
17
32
 
18
- #### `path()`
33
+ [`PathBuilder`](/docs/curves#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.
34
+
35
+ 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`](/docs/core#shape).
19
36
 
20
37
  ```ts
21
- path(): PathBuilder
38
+ // Closed triangle
39
+ const triangle = path().moveTo(0, 0).lineH(50).lineV(30).close();
40
+
41
+ // L-shaped bracket as a stroke
42
+ const bracket = path().moveTo(0, 0).lineH(50).lineV(-70).lineAngled(20, 235).stroke(4);
43
+
44
+ // Labeled edges for downstream face references
45
+ const slot = path()
46
+ .moveTo(0, 0)
47
+ .lineTo(30, 0).label('bottom')
48
+ .lineTo(30, 10)
49
+ .lineTo(0, 10).label('top')
50
+ .close();
22
51
  ```
23
52
 
24
- Create a path builder for constructing 2D outlines.
53
+ ```ts
54
+ path(): PathBuilder
55
+ ```
25
56
 
26
- #### `stroke()`
57
+ #### `stroke()` — Create a stroked polyline sketch from an array of 2D points.
27
58
 
28
59
  ```ts
29
60
  stroke(points: [ number, number ][], width: number, join?: "Round" | "Square"): Sketch
30
61
  ```
31
62
 
32
- Create a stroked polyline sketch from an array of 2D points.
63
+ #### `rect()` Create a 2D rectangle centered at the origin.
33
64
 
34
- #### `rect()`
65
+ ```ts
66
+ rect(40, 20).extrude(5);
67
+ ```
35
68
 
36
69
  ```ts
37
- rect(width: number, height: number, center?: boolean): Sketch
70
+ rect(width: number, height: number): Sketch
38
71
  ```
39
72
 
40
- Create a 2D rectangle. When center is true, the origin is at the rectangle center; otherwise at the bottom-left corner.
73
+ #### `circle2d()` — Create a 2D circle centered at the origin.
74
+
75
+ 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.
41
76
 
42
- #### `circle2d()`
77
+ ```ts
78
+ circle2d(25).extrude(10); // smooth cylinder
79
+ circle2d(25, 6).extrude(10); // hexagonal prism
80
+ ```
43
81
 
44
82
  ```ts
45
83
  circle2d(radius: number, segments?: number): Sketch
46
84
  ```
47
85
 
48
- Create a 2D circle centered at the origin. Use segments for lower-poly approximations.
86
+ #### `roundedRect()` — Create a 2D rectangle with rounded corners, centered at the origin.
49
87
 
50
- #### `roundedRect()`
88
+ The corner radius is automatically clamped to `min(width/2, height/2)` so it can never exceed the shape dimensions.
51
89
 
52
90
  ```ts
53
- roundedRect(width: number, height: number, radius: number, center?: boolean): Sketch
91
+ roundedRect(60, 30, 5).extrude(3);
54
92
  ```
55
93
 
56
- Create a 2D rectangle with rounded corners. The radius is clamped to fit within the dimensions.
94
+ ```ts
95
+ roundedRect(width: number, height: number, radius: number): Sketch
96
+ ```
97
+
98
+ #### `polygon()` — Create a 2D polygon from an array of `[x, y]` points or `Point2D` objects.
99
+
100
+ Winding order is normalized automatically — clockwise (CW) input is silently reversed to CCW before being passed to the geometry kernel.
57
101
 
58
- #### `polygon()`
102
+ ```ts
103
+ polygon([[0, 0], [50, 0], [25, 40]]).extrude(5); // triangle
104
+ ```
59
105
 
60
106
  ```ts
61
107
  polygon(points: ([ number, number ] | Point2D)[]): Sketch
62
108
  ```
63
109
 
64
- Create a 2D polygon from an array of [x, y] points or Point2D objects. Winding is normalized to CCW.
110
+ #### `ngon()` — Create a regular polygon inscribed in a circle of the given radius.
65
111
 
66
- #### `ngon()`
112
+ `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).
113
+
114
+ ```ts
115
+ ngon(6, 20).extrude(10); // hexagonal prism, circumradius 20
116
+ ```
67
117
 
68
118
  ```ts
69
119
  ngon(sides: number, radius: number): Sketch
70
120
  ```
71
121
 
72
- Create a regular polygon (equilateral triangle, hexagon, etc.) inscribed in a circle of the given radius.
122
+ #### `ellipse()` Create a 2D ellipse centered at the origin.
73
123
 
74
- #### `ellipse()`
124
+ ```ts
125
+ ellipse(30, 15).extrude(5);
126
+ ellipse(30, 15, 32).extrude(5); // lower-resolution approximation
127
+ ```
75
128
 
76
129
  ```ts
77
130
  ellipse(rx: number, ry: number, segments?: number): Sketch
78
131
  ```
79
132
 
80
- Create a 2D ellipse centered at the origin with the given X and Y radii.
133
+ #### `slot()` — Create a slot (oblong / stadium shape) a rectangle with semicircular ends, centered at the origin.
81
134
 
82
- #### `slot()`
135
+ ```ts
136
+ slot(40, 10).extrude(3); // 40mm long, 10mm wide slot
137
+ ```
83
138
 
84
139
  ```ts
85
140
  slot(length: number, width: number): Sketch
86
141
  ```
87
142
 
88
- Create a slot (stadium/discorectangle) — a rectangle with semicircular ends, centered at origin.
143
+ #### `arcSlot()`Create an arc-shaped slot (banana / annular sector) centered at the origin.
89
144
 
90
- #### `star()`
145
+ 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.
91
146
 
92
147
  ```ts
93
- star(points: number, outerR: number, innerR: number): Sketch
148
+ arcSlot(135, 74, 40).extrude(5); // pitch R135, 74° sweep, 40mm wide
149
+ ```
150
+
151
+ ```ts
152
+ arcSlot(pitchRadius: number, sweepDeg: number, thickness: number): Sketch
153
+ ```
154
+
155
+ #### `star()` — Create a star shape with alternating outer and inner radii.
156
+
157
+ ```ts
158
+ star(5, 30, 12).extrude(4); // five-pointed star
94
159
  ```
95
160
 
96
- Create a star shape with alternating outer and inner radii.
161
+ ```ts
162
+ star(points: number, outerR: number, innerR: number): Sketch
163
+ ```
97
164
 
98
165
  ### 2D Sketch Booleans
99
166
 
100
- Combine 2D sketches.
167
+ #### `union2d()` — Combine 2D sketches into a single profile using an additive boolean union.
101
168
 
102
- #### `union2d()`
169
+ 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.
170
+
171
+ ```ts
172
+ const cross = union2d(rect(60, 10), rect(10, 60));
173
+ ```
103
174
 
104
175
  ```ts
105
176
  union2d(...inputs: SketchOperandInput[]): Sketch
106
177
  ```
107
178
 
108
- Combine 2D sketches into a single profile (additive boolean). Accepts individual sketches or arrays.
179
+ #### `difference2d()` Subtract one or more 2D sketches from a base sketch.
180
+
181
+ 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.
109
182
 
110
- #### `difference2d()`
183
+ ```ts
184
+ const donut = difference2d(circle2d(50), circle2d(30));
185
+ ```
111
186
 
112
187
  ```ts
113
188
  difference2d(...inputs: SketchOperandInput[]): Sketch
114
189
  ```
115
190
 
116
- Subtract 2D sketches from a base sketch. The first sketch is the base; all others are subtracted.
191
+ #### `intersection2d()` Keep only the area where all input sketches overlap (intersection boolean).
117
192
 
118
- #### `intersection2d()`
193
+ 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.
194
+
195
+ ```ts
196
+ const lens = intersection2d(circle2d(30).translate(-10, 0), circle2d(30).translate(10, 0));
197
+ ```
119
198
 
120
199
  ```ts
121
200
  intersection2d(...inputs: SketchOperandInput[]): Sketch
122
201
  ```
123
202
 
124
- Keep only the overlapping area of the input sketches (intersection boolean).
203
+ ### 2D Sketch Features
125
204
 
126
- ### 2D Text
205
+ #### `fillet2d()` — Round a named vertical edge of a shape with a circular fillet.
127
206
 
128
- Create text geometry from strings using the built-in geometric font.
207
+ Compiler-owned fillet for tracked vertical edges. Requires a compile-plan-covered target (shapes from `box()`, `rectangle().extrude()`, or rigid transforms of those).
129
208
 
130
- #### `text2d()`
209
+ **Supported edges:**
131
210
 
132
- ```ts
133
- text2d(content: string, options?: TextOptions): Sketch
134
- ```
211
+ - Tracked vertical edges from `box()` or `rectangle().extrude()`
212
+ - Rigid transforms between tracked source and target
213
+ - Untouched sibling tracked vertical edges after earlier `fillet2d`/`chamfer2d`
135
214
 
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' })
215
+ **Not supported:** edges after shell, hole, cut, trim, difference, intersection, generic sketch extrudes, or tapered extrudes. Use [`fillet()`](/docs/core#fillet) with an `EdgeQuery` for those cases.
137
216
 
138
- <details><summary><code>TextOptions</code></summary>
217
+ Canonical quadrants: `vert-bl → [1,-1]`, `vert-br → [-1,-1]`, `vert-tr → [-1,1]`, `vert-tl → [1,1]`
139
218
 
140
219
  ```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
- }
220
+ const b = rectangle(0, 0, 50, 50).extrude(20);
221
+ const filleted = fillet2d(b.toShape(), b.edge('vert-br'), 5, [-1, -1]);
155
222
  ```
156
223
 
157
- </details>
158
-
159
- #### `textWidth()`
160
-
161
224
  ```ts
162
- textWidth(content: string, options?: Pick<TextOptions, "size" | "letterSpacing" | "font">): number
225
+ fillet2d(shape: Shape, edge: EdgeRef, radius: number, quadrant?: [ number, number ], segments?: number): Shape
163
226
  ```
164
227
 
165
- Returns the rendered width of a string in model units (same options as text2d).
228
+ **`EdgeRef`**
229
+ - `query?: EdgeQueryRef` — Compiler-owned edge query when available.
230
+ - Also: `name: EdgeName`
166
231
 
167
- ### Constrained Sketches
232
+ #### `chamfer2d()` — Bevel a named vertical edge of a shape with a 45° chamfer.
168
233
 
169
- Build parametric 2D geometry with geometric constraints and a solver.
234
+ 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
235
 
171
- #### `constrainedSketch()`
236
+ ```ts
237
+ const b = rectangle(0, 0, 50, 50).extrude(20);
238
+ const chamfered = chamfer2d(b.toShape(), b.edge('vert-br'), 3, [-1, -1]);
239
+ ```
172
240
 
173
241
  ```ts
174
- constrainedSketch(options?: ConstrainedSketchOptions): ConstrainedSketchBuilder
242
+ chamfer2d(shape: Shape, edge: EdgeRef, size: number, quadrant?: [ number, number ]): Shape
175
243
  ```
176
244
 
177
- Build a parametric 2D sketch with geometric constraints solved by the built-in constraint solver.
245
+ ### 2D Text
178
246
 
179
- <details><summary><code>ConstrainedSketchOptions</code></summary>
247
+ #### `loadFont()` — Pre-load and cache a font for use with `text2d()`.
180
248
 
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
- ```
249
+ 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.
187
250
 
188
- </details>
251
+ Built-in font names that work everywhere (browser + CLI):
189
252
 
190
- #### `addRect()`
253
+ - `'sans-serif'` or `'inter'` — bundled Inter Regular
191
254
 
192
255
  ```ts
193
- addRect(sk: ConstrainedSketchBuilder, options?: RectOptions): ConstrainedRect
256
+ const font = loadFont('/path/to/Arial Bold.ttf');
257
+ text2d('Title', { size: 12, font }).extrude(1.5);
258
+ text2d('Subtitle', { size: 8, font }).extrude(1);
194
259
  ```
195
260
 
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); ```
261
+ ```ts
262
+ loadFont(source: string | ArrayBuffer, cacheKey?: string): opentype.Font
263
+ ```
264
+
265
+ #### `text2d()` — Build a filled 2D Sketch from a text string.
266
+
267
+ 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.
197
268
 
198
- <details><summary><code>RectOptions</code></summary>
269
+ Alignment reference table:
270
+
271
+ | `align` | `baseline` | Origin | |------------|--------------|-------------------------------------| | `'left'` | `'baseline'` | Bottom-left of first char (default) | | `'center'` | `'center'` | Dead center of text block | | `'right'` | `'top'` | Top-right corner |
199
272
 
200
273
  ```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
- ```
274
+ // Extruded nameplate
275
+ text2d('FORGE CAD', { size: 8 }).extrude(1.2);
276
+
277
+ // Centered label on the XY plane
278
+ text2d('V 2.0', { size: 6, align: 'center', baseline: 'center' });
279
+
280
+ // Engraved text cut into the top face of a box
281
+ const label = text2d('REV A', { size: 5, align: 'center', baseline: 'center' });
282
+ plate.subtract(label.onFace(plate, 'top', { protrude: -0.5 }).extrude(1));
214
283
 
215
- </details>
284
+ // Custom TTF font
285
+ text2d('Hello', { size: 10, font: '/path/to/Arial.ttf' }).extrude(1);
216
286
 
217
- <details><summary><code>ConstrainedRect</code></summary>
287
+ // Pre-loaded font for reuse
288
+ const font = loadFont('/path/to/Arial Bold.ttf');
289
+ text2d('Title', { size: 12, font }).extrude(1.5);
290
+ ```
218
291
 
219
292
  ```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
- }
293
+ text2d(content: string, options?: TextOptions): Sketch
238
294
  ```
239
295
 
240
- </details>
296
+ **`TextOptions`**
297
+
298
+ | Option | Type | Description |
299
+ |--------|------|-------------|
300
+ | `size?` | `number` | Cap height of the text in model units. All other dimensions (stroke weight, spacing) scale proportionally. |
301
+ | `letterSpacing?` | `number` | Extra space between characters in model units. Negative values tighten the tracking. |
302
+ | `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 |
303
+ | `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 |
304
+ | `font?` | `string | opentype.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' }) |
305
+ | `flattenTolerance?` | `number` | Bezier flattening tolerance in model units. Smaller = more polygon segments = smoother curves. |
306
+
307
+ #### `textWidth()` — Measure the rendered advance width of a string without creating any geometry.
241
308
 
242
- #### `addPolygon()`
309
+ 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.
243
310
 
244
311
  ```ts
245
- addPolygon(sk: ConstrainedSketchBuilder, options: PolygonOptions): ConstrainedPolygon
312
+ const w = textWidth('SERIAL: 001', { size: 6 });
313
+ const plate = box(w + 10, 12, 2);
314
+ ```
315
+
316
+ ```ts
317
+ textWidth(content: string, options?: Pick<TextOptions, "size" | "letterSpacing" | "font">): number
246
318
  ```
247
319
 
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); ```
320
+ ### Constrained Sketches
321
+
322
+ #### `constrainedSketch()` — Create a parametric 2D sketch driven by geometric constraints and a nonlinear solver.
323
+
324
+ **Workflow**
249
325
 
250
- <details><summary><code>PolygonOptions</code></summary>
326
+ 1. Create a builder with `constrainedSketch()`.
327
+ 2. Add geometry — points, lines, circles, arcs — using the builder methods.
328
+ 3. Add constraints (`horizontal`, `length`, `fix`, etc.) to drive the geometry.
329
+ 4. Call `.solve()` to run the solver and get a `ConstraintSketch` (which extends `Sketch`).
251
330
 
252
331
  ```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
- }
332
+ const sk = constrainedSketch();
333
+ const p1 = sk.point(0, 0);
334
+ const p2 = sk.point(50, 0);
335
+ const l1 = sk.line(p1, p2);
336
+ sk.fix(p1, 0, 0);
337
+ sk.horizontal(l1);
338
+ sk.length(l1, 50);
339
+ return sk.solve().extrude(10);
259
340
  ```
260
341
 
261
- </details>
342
+ **Solver status**
262
343
 
263
- <details><summary><code>ConstrainedPolygon</code></summary>
344
+ ```ts
345
+ const result = sk.solve();
346
+ result.constraintMeta.status; // 'fully' | 'under' | 'over' | 'over-redundant'
347
+ result.constraintMeta.dof; // 0 = fully constrained
348
+ result.constraintMeta.maxError; // residual — should be < 1e-6
349
+ result.inspect(); // human-readable summary
350
+ result.withUpdatedConstraint('cst-5', 120); // update a dimension without rebuilding
351
+ ```
264
352
 
265
353
  ```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
- }
354
+ constrainedSketch(options?: ConstrainedSketchOptions): ConstrainedSketchBuilder
274
355
  ```
275
356
 
276
- </details>
357
+ **`ConstrainedSketchOptions`**
358
+ - `strict?: boolean` — When true, adding a constraint that cannot be satisfied throws instead of silently discarding it.
359
+
360
+ #### `addRect()` — Add an axis-aligned rectangle concept to the builder.
277
361
 
278
- #### `addRegularPolygon()`
362
+ 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).
363
+
364
+ Use `sk.rect()` as the shorthand builder method.
279
365
 
280
366
  ```ts
281
- addRegularPolygon(sk: ConstrainedSketchBuilder, options: RegularPolygonOptions): ConstrainedRegularPolygon
367
+ const sk = constrainedSketch();
368
+ const r = sk.rect({ x: 0, y: 0, width: 100, height: 50 });
369
+ sk.fix(r.bottomLeft, 0, 0);
370
+ sk.length(r.bottom, 120); // override initial width
371
+ return sk.solve().extrude(10);
372
+ ```
373
+
374
+ ```ts
375
+ addRect(sk: ConstrainedSketchBuilder, options?: RectOptions): ConstrainedRect
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
+ **`RectOptions`**
379
+
380
+ | Option | Type | Description |
381
+ |--------|------|-------------|
382
+ | `x?` | `number` | Bottom-left x coordinate. Default: 0. |
383
+ | `y?` | `number` | Bottom-left y coordinate. Default: 0. |
384
+ | `width?` | `number` | Width (along x). Default: 10. |
385
+ | `height?` | `number` | Height (along y). Default: 10. |
386
+ | `blockRotation?` | `boolean` | Prevent 180° rotation (ensures bottom edge points rightward). Default: false. |
387
+
388
+ **`ConstrainedRect`**
389
+
390
+ | Option | Type | Description |
391
+ |--------|------|-------------|
392
+ | `bottom` | `LineId` | bottom-left → bottom-right |
393
+ | `right` | `LineId` | bottom-right → top-right |
394
+ | `top` | `LineId` | top-right → top-left |
395
+ | `left` | `LineId` | top-left → bottom-left |
396
+ | `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)`. |
397
+ | `shape` | `ShapeId` | ShapeId for `shapeWidth`, `shapeHeight`, `shapeArea`, `shapeCentroidX/Y`. |
398
+ | `bottomLeft`, `bottomRight`, `topRight`, `topLeft` | | — |
399
+
400
+ #### `addPolygon()` — Add a general polygon concept to the builder.
401
+
402
+ 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.
403
+
404
+ Use `sk.addPolygon()` as the shorthand builder method.
285
405
 
286
- <details><summary><code>RegularPolygonOptions</code></summary>
406
+ ```ts
407
+ const sk = constrainedSketch();
408
+ const tri = sk.addPolygon({ points: [[0,0],[100,0],[50,80]] });
409
+ sk.fix(tri.vertex(0), 0, 0);
410
+ sk.length(tri.side(0), 100);
411
+ return sk.solve().extrude(5);
412
+ ```
287
413
 
288
414
  ```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
- }
415
+ addPolygon(sk: ConstrainedSketchBuilder, options: PolygonOptions): ConstrainedPolygon
303
416
  ```
304
417
 
305
- </details>
418
+ **`PolygonOptions`**
419
+ - `addLoop?: boolean` — Whether to register a closed loop for sketch generation. Default: true.
420
+ - `blockRotation?: boolean` — Prevent 180° rotation (ensures first edge maintains its initial direction). Default: false.
421
+
422
+ **`ConstrainedPolygon`**
423
+ - `vertices: PointId[]` — CCW-ordered PointIds.
424
+ - `sides: LineId[]` — CCW-ordered LineIds. `sides[i]` runs from `vertices[i]` → `vertices[(i+1) % n]`.
425
+ - `shape: ShapeId` — ShapeId for `shapeWidth`, `shapeHeight`, `shapeArea`, `shapeCentroidX/Y`.
306
426
 
427
+ #### `addRegularPolygon()` — Add a regular n-gon concept to the builder.
307
428
 
308
- <details><summary><code>ConstrainedRegularPolygon</code> extends ConstrainedPolygon</summary>
429
+ 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.
430
+
431
+ Use `sk.regularPolygon()` as the shorthand builder method.
432
+
433
+ ```ts
434
+ const sk = constrainedSketch();
435
+ const hex = sk.regularPolygon({ sides: 6, radius: 25 });
436
+ sk.fix(hex.center, 0, 0);
437
+ sk.length(hex.side(0), 30); // all sides change (equal constraint)
438
+ return sk.solve().extrude(5);
439
+ ```
309
440
 
310
441
  ```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
- }
442
+ addRegularPolygon(sk: ConstrainedSketchBuilder, options: RegularPolygonOptions): ConstrainedRegularPolygon
315
443
  ```
316
444
 
317
- </details>
445
+ **`RegularPolygonOptions`**
446
+
447
+ | Option | Type | Description |
448
+ |--------|------|-------------|
449
+ | `sides` | `number` | Number of sides (minimum 3). |
450
+ | `radius?` | `number` | Circumradius — distance from center to vertex. Default: 10. |
451
+ | `cx?` | `number` | Center x coordinate. Default: 0. |
452
+ | `cy?` | `number` | Center y coordinate. Default: 0. |
453
+ | `startAngle?` | `number` | Angle (in degrees) of vertex[0] measured from the +X axis (CCW positive). Default: 0 (rightmost vertex). |
454
+ | `blockRotation?` | `boolean` | Prevent 180° rotation (ensures first edge maintains its initial direction). Default: false. |
455
+
456
+
457
+ **`ConstrainedRegularPolygon`** extends ConstrainedPolygon
458
+ - `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
459
 
319
460
  ### 2D Geometry Helpers
320
461
 
321
- Analytic 2D geometry classes for measurement and construction.
462
+ #### `point()` — Create an analytic 2D point for measurement and construction geometry.
322
463
 
323
- #### `point()`
464
+ ```ts
465
+ const p = point(10, 20);
466
+ p.distanceTo(point(30, 40)); // Euclidean distance
467
+ p.midpointTo(point(30, 40)); // midpoint
468
+ p.translate(5, 5); // new shifted point
469
+ p.toTuple(); // [10, 20]
470
+ ```
324
471
 
325
472
  ```ts
326
473
  point(x: number, y: number): Point2D
327
474
  ```
328
475
 
329
- Create an analytic 2D point for measurement and construction geometry.
476
+ #### `line()` — Create an analytic 2D line segment between two points.
330
477
 
331
- #### `line()`
478
+ ```ts
479
+ const l = line(0, 0, 50, 0);
480
+ l.length; l.midpoint; l.angle; l.direction;
481
+ l.parallel(10); // parallel line offset 10 (positive = left)
482
+ l.intersect(l2); // Point2D — treats lines as infinite
483
+ l.intersectSegment(l2); // Point2D or null — segments only
484
+
485
+ Line2D.fromPointAndAngle(point(0, 0), 45, 100);
486
+ Line2D.fromPointAndDirection(point(0, 0), [1, 1], 50);
487
+ ```
332
488
 
333
489
  ```ts
334
490
  line(x1: number, y1: number, x2: number, y2: number): Line2D
335
491
  ```
336
492
 
337
- Create an analytic 2D line segment between two points. Provides length, midpoint, angle, intersection, and parallel helpers.
493
+ #### `circle()` — Create an analytic 2D circle for measurement, construction, and extrusion.
338
494
 
339
- #### `circle()`
495
+ ```ts
496
+ const c = circle(0, 0, 25);
497
+ c.diameter; c.circumference; c.area;
498
+ c.pointAtAngle(90); // Point2D at top (90° CCW from +X)
499
+
500
+ // Extrude to cylinder with named faces
501
+ const cyl = c.extrude(30);
502
+ cyl.face('top'); // FaceRef (planar)
503
+ cyl.face('side'); // FaceRef (curved)
504
+
505
+ Circle2D.fromDiameter(point(0, 0), 50);
506
+ ```
340
507
 
341
508
  ```ts
342
509
  circle(cx: number, cy: number, radius: number): Circle2D
343
510
  ```
344
511
 
345
- Create an analytic 2D circle for measurement, construction, and extrusion. Provides diameter, circumference, area, and toSketch().
512
+ #### `degrees()` Identity function that returns degrees unchanged.
346
513
 
347
- #### `degrees()`
514
+ Use for clarity when the unit of an angle value would otherwise be ambiguous — e.g. `param("Angle", degrees(45))`.
348
515
 
349
516
  ```ts
350
517
  degrees(deg: number): number
351
518
  ```
352
519
 
353
- Convert degrees to degrees (identityfor readability in scripts)
520
+ #### `radians()`Convert radians to degrees.
354
521
 
355
- #### `radians()`
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.
356
523
 
357
524
  ```ts
358
525
  radians(rad: number): number
359
526
  ```
360
527
 
361
- Convert radians to degrees
362
-
363
528
  ---
364
529
 
365
530
  ## Classes
366
531
 
367
532
  ### `Sketch`
368
533
 
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.
534
+ Immutable 2D profile for extrusion, revolve, and other operations.
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:
539
+
540
+ - **Transforms** — `translate`, `rotate`, `rotateAround`, `scale`, `mirror`
541
+ - **Booleans** — `add` (union), `subtract` (difference), `intersect`
542
+ - **Operations** — `offset`, `simplify`
543
+ - **Queries** — `area`, `bounds`, `isEmpty`, `numVert`
544
+ - **3D operations** — `extrude`, `revolve`, `onFace`
545
+ - **Regions** — `regions`, `region`
546
+ - **Placement** — `attachTo`
547
+
548
+ Named anchor positions used by `attachTo()`: `'center'` | `'top-left'` | `'top-right'` | `'bottom-left'` | `'bottom-right'` | `'top'` | `'bottom'` | `'left'` | `'right'`
370
549
 
371
550
  **Properties:**
372
551
 
@@ -374,224 +553,1201 @@ Convert radians to degrees
374
553
  |----------|------|-------------|
375
554
  | `cross` | `ProfileBackend` | — |
376
555
 
377
- **Methods:**
556
+ **Transforms**
378
557
 
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[]
558
+ #### `translate()` — Move the sketch by the given X and Y offset.
407
559
 
408
- ### `ConstrainedSketchBuilder`
560
+ ```ts
561
+ translate(x: number, y?: number): Sketch
562
+ ```
409
563
 
410
- **Methods:**
564
+ #### `rotate()` — Rotate the sketch around its bounding-box center.
565
+
566
+ ```ts
567
+ rotate(degrees: number): Sketch
568
+ ```
411
569
 
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.
570
+ #### `rotateAround()` — Rotate the sketch around a specific pivot point.
492
571
 
493
- ### `ConstraintSketch`
572
+ ```ts
573
+ rect(20, 20).rotateAround(45, [0, 0]);
574
+ ```
494
575
 
495
- **Properties:**
576
+ ```ts
577
+ rotateAround(degrees: number, pivot: [ number, number ]): Sketch
578
+ ```
496
579
 
497
- | Property | Type | Description |
498
- |----------|------|-------------|
499
- | `constraintMeta` | `SketchConstraintMeta` | — |
500
- | `definition` | `ConstraintDefinition` | — |
580
+ #### `scale()` Scale the sketch relative to its bounding-box center.
501
581
 
502
- **Methods:**
582
+ Pass a single number for uniform scaling, or `[sx, sy]` for per-axis scaling.
503
583
 
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.
584
+ ```ts
585
+ scale(v: number | [ number, number ]): Sketch
586
+ ```
508
587
 
509
- ### `SketchGroupBuilder`
588
+ #### `scaleAround()` — Scale the sketch relative to an arbitrary pivot point.
510
589
 
511
- **Methods:**
590
+ ```ts
591
+ scaleAround(pivot: [ number, number ], v: number | [ number, number ]): Sketch
592
+ ```
512
593
 
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.
594
+ #### `mirror()` — Mirror the sketch across a line through its bounding-box center.
518
595
 
519
- ### `Point2D`
596
+ `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.
520
597
 
521
- **Properties:**
598
+ ```ts
599
+ mirror(normal: [ number, number ]): Sketch
600
+ ```
522
601
 
523
- | Property | Type | Description |
524
- |----------|------|-------------|
525
- | `x` | `number` | — |
526
- | `y` | `number` | — |
602
+ #### `mirrorThrough()` Mirror the sketch across a line defined by a point and a normal direction.
527
603
 
528
- **Methods:**
604
+ ```ts
605
+ mirrorThrough(point: [ number, number ], normal: [ number, number ]): Sketch
606
+ ```
529
607
 
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 ]
608
+ **Booleans**
534
609
 
535
- ### `Line2D`
610
+ #### `add()` — Add (union) one or more sketches to this sketch.
536
611
 
537
- **Properties:**
612
+ 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.
538
613
 
539
- | Property | Type | Description |
540
- |----------|------|-------------|
541
- | `start` | `Point2D` | — |
542
- | `end` | `Point2D` | — |
614
+ ```ts
615
+ circle2d(20).add(rect(10, 30)).extrude(5);
616
+ ```
543
617
 
544
- **Methods:**
618
+ ```ts
619
+ add(...others: SketchOperandInput[]): Sketch
620
+ ```
545
621
 
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
622
+ #### `subtract()` — Subtract one or more sketches from this sketch.
556
623
 
557
- ### `Circle2D`
624
+ 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()`.
558
625
 
559
- **Properties:**
626
+ ```ts
627
+ rect(40, 40).subtract(circle2d(10)).extrude(5);
628
+ ```
560
629
 
561
- | Property | Type | Description |
562
- |----------|------|-------------|
563
- | `center` | `Point2D` | — |
564
- | `radius` | `number` | — |
630
+ ```ts
631
+ subtract(...others: SketchOperandInput[]): Sketch
632
+ ```
565
633
 
566
- **Methods:**
634
+ #### `intersect()` — Intersect this sketch with one or more others (keep overlapping area only).
567
635
 
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
636
+ Accepts individual sketches or arrays: `sketch.intersect(a, b)` or `sketch.intersect([a, b])`. For intersecting many sketches, prefer the free function `intersection2d()`.
577
637
 
578
- ### `Rectangle2D`
638
+ ```ts
639
+ intersect(...others: SketchOperandInput[]): Sketch
640
+ ```
579
641
 
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).
642
+ **Features**
581
643
 
582
- **Methods:**
644
+ #### `offset()` — Inflate (positive delta) or deflate (negative delta) the sketch contour.
645
+
646
+ Use `offset(-r).offset(+r)` to round every convex corner of a closed sketch.
647
+
648
+ - `'Round'` — smooth arc at each corner (default)
649
+ - `'Square'` — flat mitered extension
650
+ - `'Miter'` — sharp pointed extension
651
+
652
+ ```ts
653
+ rect(40, 20).offset(3); // expand by 3
654
+ rect(40, 20).offset(-2).offset(2); // round all convex corners
655
+ ```
656
+
657
+ ```ts
658
+ offset(delta: number, join?: "Square" | "Round" | "Miter"): Sketch
659
+ ```
660
+
661
+ #### `regions()` — Decompose this sketch into its distinct filled regions, sorted largest-first by area.
583
662
 
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
663
+ 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.
664
+
665
+ ```ts
666
+ const pair = union2d(rect(40, 40), rect(40, 40).translate(60, 0));
667
+ const [left, right] = pair.regions(); // largest first
668
+ left.extrude(5);
669
+ ```
670
+
671
+ ```ts
672
+ regions(): Sketch[]
673
+ ```
674
+
675
+ #### `region()` — Select the single filled region that contains the given 2D seed point.
676
+
677
+ 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()`.
678
+
679
+ ```ts
680
+ const donut = circle2d(50).subtract(circle2d(30));
681
+ donut.region([40, 0]).extrude(10); // seed at radius 40, inside the ring
682
+ ```
683
+
684
+ ```ts
685
+ region(seed: [ number, number ]): Sketch
686
+ ```
687
+
688
+ **Promotion**
689
+
690
+ #### `extrude()` — Extrude this 2D sketch along Z to create a 3D solid. Supports twist and scale tapering.
691
+
692
+ ```ts
693
+ extrude(height: number, opts?: { twist?: number; divisions?: number; scaleTop?: number | [ number, number ]; }): Shape
694
+ ```
695
+
696
+ #### `revolve()` — Revolve this 2D sketch around the Y axis to create a 3D solid of revolution.
697
+
698
+ ```ts
699
+ revolve(degrees?: number, segments?: number): Shape
700
+ ```
701
+
702
+ **Placement**
703
+
704
+ #### `attachTo()` — Position this sketch relative to another using named anchor points.
705
+
706
+ Computes the translation needed to align `selfAnchor` on this sketch with `targetAnchor` on the target sketch, then applies an optional pixel-exact offset.
707
+
708
+ Anchor positions: `'center'` | `'top-left'` | `'top-right'` | `'bottom-left'` | `'bottom-right'` | `'top'` | `'bottom'` | `'left'` | `'right'`
709
+
710
+ ```ts
711
+ const arm = rect(4, 70).attachTo(plate, 'bottom-left', 'top-left');
712
+ const shifted = rect(4, 70).attachTo(plate, 'bottom-left', 'top-left', [5, 0]);
713
+ ```
714
+
715
+ ```ts
716
+ attachTo(target: Sketch, targetAnchor: Anchor, selfAnchor?: Anchor, offset?: [ number, number ]): Sketch
717
+ ```
718
+
719
+ #### `onFace()` — Place this sketch on a face or planar target in 3D space.
720
+
721
+ Use this when a 2D profile should be oriented onto a 3D face before extrusion or other downstream operations.
722
+
723
+ ```ts
724
+ 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
725
+ ```
726
+
727
+ **Labels**
728
+
729
+ #### `labelEdge()` — Label the single boundary edge (for circles, single-loop profiles).
730
+
731
+ ```ts
732
+ labelEdge(name: string): Sketch
733
+ ```
734
+
735
+ #### `labelEdges()` — Label edges in winding order, or by named map for rect.
736
+
737
+ Positional: `labelEdges('bottom', 'right', 'top', 'left')` — one per edge, `null` to skip. Named (rect only): `labelEdges({ bottom: 'floor', top: 'ceiling' })`.
738
+
739
+ ```ts
740
+ labelEdges(...args: (string | null)[] | [ Record<string, string> ]): Sketch
741
+ ```
742
+
743
+ #### `edgeLabels()` — List current edge label names.
744
+
745
+ ```ts
746
+ edgeLabels(): string[]
747
+ ```
748
+
749
+ #### `prefixLabels()` — Prefix all edge labels. Returns this sketch (mutates labels in place).
750
+
751
+ ```ts
752
+ prefixLabels(prefix: string): Sketch
753
+ ```
754
+
755
+ #### `renameLabel()` — Rename a single edge label.
756
+
757
+ ```ts
758
+ renameLabel(from: string, to: string): Sketch
759
+ ```
760
+
761
+ #### `dropLabels()` — Remove specific labels.
762
+
763
+ ```ts
764
+ dropLabels(...names: string[]): Sketch
765
+ ```
766
+
767
+ #### `dropAllLabels()` — Remove all labels.
768
+
769
+ ```ts
770
+ dropAllLabels(): Sketch
771
+ ```
772
+
773
+ **Measurement**
774
+
775
+ #### `area()` — Return the total filled area of the sketch.
776
+
777
+ ```ts
778
+ area(): number
779
+ ```
780
+
781
+ #### `bounds()` — Return the axis-aligned bounding box of the sketch.
782
+
783
+ ```ts
784
+ bounds(): ProfileBounds
785
+ ```
786
+
787
+ #### `isEmpty()` — Return `true` if the sketch contains no filled area.
788
+
789
+ ```ts
790
+ isEmpty(): boolean
791
+ ```
792
+
793
+ #### `numVert()` — Return the number of vertices in the polygon representation of the sketch contours.
794
+
795
+ ```ts
796
+ numVert(): number
797
+ ```
798
+
799
+ #### `toPolygons()` — Return the sketch as a list of polygons matching its contour topology.
800
+
801
+ Useful when you need raw polygon data for inspection or custom export.
802
+
803
+ ```ts
804
+ toPolygons(): number[][][]
805
+ ```
806
+
807
+ **Other**
808
+
809
+ #### `color()` — Set the display color of this sketch.
810
+
811
+ Color is preserved through all transforms and boolean operations. Pass `undefined` to clear the color.
812
+
813
+ ```ts
814
+ circle2d(20).color('#ff0000').extrude(5);
815
+ ```
816
+
817
+ ```ts
818
+ color(value: string | undefined): Sketch
819
+ ```
820
+
821
+ #### `clone()` — Create an explicit copy of this sketch for branching variants.
822
+
823
+ 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.
824
+
825
+ ```ts
826
+ clone(): Sketch
827
+ ```
828
+
829
+ ### `ConstrainedSketchBuilder`
830
+
831
+ **Drawing**
832
+
833
+ #### `moveTo()` — Move the cursor to `(x, y)` and start a new profile loop.
834
+
835
+ ```ts
836
+ moveTo(x: number, y: number): this
837
+ ```
838
+
839
+ #### `lineTo()` — Draw a line from the current cursor to `(x, y)`.
840
+
841
+ ```ts
842
+ lineTo(x: number, y: number): this
843
+ ```
844
+
845
+ #### `lineH()` — Draw a horizontal line of length `dx` from the current cursor.
846
+
847
+ ```ts
848
+ lineH(dx: number): this
849
+ ```
850
+
851
+ #### `lineV()` — Draw a vertical line of length `dy` from the current cursor.
852
+
853
+ ```ts
854
+ lineV(dy: number): this
855
+ ```
856
+
857
+ #### `lineAngled()` — Draw a line of the given `length` at `degrees` from +X.
858
+
859
+ ```ts
860
+ lineAngled(length: number, degrees: number): this
861
+ ```
862
+
863
+ #### `arcTo()` — Draw a circular arc from the current cursor to `(x, y)` with the given radius.
864
+
865
+ ```ts
866
+ arcTo(x: number, y: number, radius: number, clockwise?: boolean): this
867
+ ```
868
+
869
+ #### `arcByCenter()` — Create an arc from an explicit center point and endpoint IDs.
870
+
871
+ ```ts
872
+ arcByCenter(centerId: PointId, startId: PointId, endId: PointId, clockwise?: boolean, name?: string, fixedRadius?: boolean): ArcId
873
+ ```
874
+
875
+ #### `bezier()` — Create a cubic Bezier curve from four control points.
876
+
877
+ ```ts
878
+ bezier(p0: any, p1: any, p2: any, p3: any, name?: string): BezierId
879
+ ```
880
+
881
+ #### `bezierTo()` — Draw a cubic Bezier from the current cursor to `(x3, y3)`.
882
+
883
+ ```ts
884
+ bezierTo(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number): this
885
+ ```
886
+
887
+ #### `blendTo()` — Draw a smooth Bezier tangent to the previous arc.
888
+
889
+ ```ts
890
+ blendTo(x: number, y: number, weight?: number): this
891
+ ```
892
+
893
+ #### `label()` — Label the current path segment.
894
+
895
+ ```ts
896
+ label(name: string): this
897
+ ```
898
+
899
+ #### `close()` — Close the current path and register the loop.
900
+
901
+ ```ts
902
+ close(): this
903
+ ```
904
+
905
+ #### `addLoopCircle()` — Add a circle loop to the path.
906
+
907
+ ```ts
908
+ addLoopCircle(center: PointId, radius: number, segments?: number): this
909
+ ```
910
+
911
+ #### `addLoop()` — Add a closed polygon loop from point IDs.
912
+
913
+ ```ts
914
+ addLoop(points: any[]): this
915
+ ```
916
+
917
+ #### `addProfileLoop()` — Add a profile loop from prebuilt line/arc/bezier segments.
918
+
919
+ ```ts
920
+ addProfileLoop(segments: Array<{ kind: "line"; line: any; } | { kind: "arc"; arc: any; } | { kind: "bezier"; bezier: any; }>): this
921
+ ```
922
+
923
+ **Entities**
924
+
925
+ #### `point()` — Add a free point to the sketch at `(x, y)`.
926
+
927
+ 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`.
928
+
929
+ ```ts
930
+ point(x?: number, y?: number, fixed?: boolean): PointId
931
+ ```
932
+
933
+ #### `pointAt()` — Return the `PointId` of the point created at the given insertion index.
934
+
935
+ ```ts
936
+ pointAt(index: number): PointId
937
+ ```
938
+
939
+ #### `line()` — Connect two existing points with a line segment.
940
+
941
+ 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).
942
+
943
+ ```ts
944
+ const axis = sk.line(sk.point(0, -50), sk.point(0, 50), true);
945
+ sk.symmetric(p1, p2, axis);
946
+ ```
947
+
948
+ ```ts
949
+ line(a: PointId, b: PointId, construction?: boolean, name?: string): LineId
950
+ ```
951
+
952
+ #### `lineAt()` — Return the `LineId` of the line created at the given insertion index.
953
+
954
+ ```ts
955
+ lineAt(index: number): LineId
956
+ ```
957
+
958
+ #### `circle()` — Add a circle to the sketch with the given center point and initial radius.
959
+
960
+ 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.
961
+
962
+ ```ts
963
+ circle(center: PointId, radius: number, construction?: boolean, segments?: number, name?: string): CircleId
964
+ ```
965
+
966
+ #### `circleAt()` — Return the `CircleId` of the circle created at the given insertion index.
967
+
968
+ ```ts
969
+ circleAt(index: number): CircleId
970
+ ```
971
+
972
+ #### `shape()` — Register a named shape (closed polygon) from an ordered list of line IDs.
973
+
974
+ 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()`.
975
+
976
+ ```ts
977
+ shape(lines: LineId[]): ShapeId
978
+ ```
979
+
980
+ #### [`group()`](/docs/core#group) — Create a rigid-body group with a local coordinate frame.
981
+
982
+ 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`.
983
+
984
+ Group points are addressable by their `PointId` in all sketch constraints (e.g. `sk.coincident`, `sk.distance`) just like any other points.
985
+
986
+ ```ts
987
+ const g = sk.group({ x: 50, y: 30 });
988
+ const p0 = g.point(0, 0); // local origin → world (50, 30)
989
+ const p1 = g.point(100, 0); // local (100,0) → world (150, 30)
990
+ const l = g.line(p0, p1);
991
+ g.fixRotation();
992
+ const handle = g.done();
993
+ // p0, p1 work in constraints like any other PointId:
994
+ sk.coincident(p0, someExternalPoint);
995
+ ```
996
+
997
+ ```ts
998
+ group(opts?: { x?: number; y?: number; theta?: number; id?: string; }): SketchGroupBuilder
999
+ ```
1000
+
1001
+ #### `rect()` — Add an axis-aligned rectangle concept. Returns a `ConstrainedRect` handle with named vertices, sides, and center.
1002
+
1003
+ ```ts
1004
+ rect(options?: RectOptions): ConstrainedRect
1005
+ ```
1006
+
1007
+ #### `addPolygon()` — Add a general polygon concept (CCW winding enforced). Returns a `ConstrainedPolygon` handle.
1008
+
1009
+ ```ts
1010
+ addPolygon(options: PolygonOptions): ConstrainedPolygon
1011
+ ```
1012
+
1013
+ #### `regularPolygon()` — Add a regular n-gon concept (equal sides, CCW winding). Returns a `ConstrainedRegularPolygon` handle with a center point.
1014
+
1015
+ ```ts
1016
+ regularPolygon(options: RegularPolygonOptions): ConstrainedRegularPolygon
1017
+ ```
1018
+
1019
+ #### `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.
1020
+
1021
+ ```ts
1022
+ groupRect(options: GroupRectOptions): ConstrainedGroupRect
1023
+ ```
1024
+
1025
+ **Geometric Constraints**
1026
+
1027
+ #### `horizontal()` — Constrain a line to be horizontal (parallel to the X axis).
1028
+
1029
+ ```ts
1030
+ horizontal(line: any): this
1031
+ ```
1032
+
1033
+ #### `vertical()` — Constrain a line to be vertical (parallel to the Y axis).
1034
+
1035
+ ```ts
1036
+ vertical(line: any): this
1037
+ ```
1038
+
1039
+ #### `parallel()` — Constrain two lines to be parallel.
1040
+
1041
+ ```ts
1042
+ parallel(a: any, b: any): this
1043
+ ```
1044
+
1045
+ #### `sameDirection()` — Constrain two lines to point in the same direction.
1046
+
1047
+ ```ts
1048
+ sameDirection(a: any, b: any): this
1049
+ ```
1050
+
1051
+ #### `oppositeDirection()` — Constrain two lines to point in opposite directions.
1052
+
1053
+ ```ts
1054
+ oppositeDirection(a: any, b: any): this
1055
+ ```
1056
+
1057
+ #### `perpendicular()` — Constrain two lines to be perpendicular.
1058
+
1059
+ ```ts
1060
+ perpendicular(a: any, b: any): this
1061
+ ```
1062
+
1063
+ #### `tangent()` — Constrain a line/circle or circle/circle tangency relationship.
1064
+
1065
+ ```ts
1066
+ tangent(a: any, b: any): this
1067
+ ```
1068
+
1069
+ #### `collinear()` — Constrain a point to lie on the infinite extension of a line.
1070
+
1071
+ ```ts
1072
+ collinear(point: any, line: any): this
1073
+ ```
1074
+
1075
+ #### `symmetric()` — Constrain two points to be symmetric about an axis line.
1076
+
1077
+ ```ts
1078
+ symmetric(a: any, b: any, axis: any): this
1079
+ ```
1080
+
1081
+ #### `blockRotation()` — Prevent 180° rotation of a polygon by anchoring its first edge.
1082
+
1083
+ ```ts
1084
+ blockRotation(points: any[], axis?: "x" | "y"): this
1085
+ ```
1086
+
1087
+ **Dimensional Constraints**
1088
+
1089
+ #### `distance()` — Constrain the Euclidean distance between two points.
1090
+
1091
+ ```ts
1092
+ distance(a: any, b: any, value: number): this
1093
+ ```
1094
+
1095
+ #### `length()` — Constrain the length of a line segment.
1096
+
1097
+ ```ts
1098
+ length(line: any, value: number): this
1099
+ ```
1100
+
1101
+ #### `angle()` — Constrain the signed angle from line `a` to line `b`.
1102
+
1103
+ ```ts
1104
+ angle(a: any, b: any, value: number): this
1105
+ ```
1106
+
1107
+ #### `radius()` — Constrain the radius of a circle.
1108
+
1109
+ ```ts
1110
+ radius(circle: any, value: number): this
1111
+ ```
1112
+
1113
+ #### `diameter()` — Constrain the diameter of a circle.
1114
+
1115
+ ```ts
1116
+ diameter(circle: any, value: number): this
1117
+ ```
1118
+
1119
+ #### `hDistance()` — Constrain the horizontal distance between two points.
1120
+
1121
+ ```ts
1122
+ hDistance(a: any, b: any, value: number): this
1123
+ ```
1124
+
1125
+ #### `vDistance()` — Constrain the vertical distance between two points.
1126
+
1127
+ ```ts
1128
+ vDistance(a: any, b: any, value: number): this
1129
+ ```
1130
+
1131
+ #### `pointLineDistance()` — Constrain the signed perpendicular distance from a point to a line.
1132
+
1133
+ ```ts
1134
+ pointLineDistance(point: any, line: any, value: number): this
1135
+ ```
1136
+
1137
+ #### `lineDistance()` — Constrain the perpendicular offset distance between two lines.
1138
+
1139
+ ```ts
1140
+ lineDistance(a: any, b: any, value: number): this
1141
+ ```
1142
+
1143
+ #### `absoluteAngle()` — Constrain the absolute angle of a line measured from +X.
1144
+
1145
+ ```ts
1146
+ absoluteAngle(line: any, value: number): this
1147
+ ```
1148
+
1149
+ #### `arcLength()` — Constrain the arc length of an arc.
1150
+
1151
+ ```ts
1152
+ arcLength(arc: any, value: number): this
1153
+ ```
1154
+
1155
+ #### `equalRadius()` — Constrain two circles to have equal radii.
1156
+
1157
+ ```ts
1158
+ equalRadius(a: any, b: any): this
1159
+ ```
1160
+
1161
+ #### `angleBetween()` — Constrain the unsigned angle between two lines.
1162
+
1163
+ ```ts
1164
+ angleBetween(a: any, b: any, value: number): this
1165
+ ```
1166
+
1167
+ **Coincidence & Equality**
1168
+
1169
+ #### `equal()` — Constrain two lines to have equal length.
1170
+
1171
+ ```ts
1172
+ equal(a: any, b: any): this
1173
+ ```
1174
+
1175
+ #### `coincident()` — Constrain two points to coincide.
1176
+
1177
+ ```ts
1178
+ coincident(a: any, b: any): this
1179
+ ```
1180
+
1181
+ #### `concentric()` — Constrain two circles to share a center.
1182
+
1183
+ ```ts
1184
+ concentric(a: any, b: any): this
1185
+ ```
1186
+
1187
+ #### `fix()` — Pin a point at a specific world location.
1188
+
1189
+ ```ts
1190
+ fix(point: any, x?: number, y?: number): this
1191
+ ```
1192
+
1193
+ #### `midpoint()` — Constrain a point to lie at the midpoint of a line.
1194
+
1195
+ ```ts
1196
+ midpoint(point: any, line: any): this
1197
+ ```
1198
+
1199
+ #### `pointOnCircle()` — Constrain a point to lie on the perimeter of a circle.
1200
+
1201
+ ```ts
1202
+ pointOnCircle(point: any, circle: any): this
1203
+ ```
1204
+
1205
+ #### `pointOnLine()` — Constrain a point to lie on the bounded segment of a line.
1206
+
1207
+ ```ts
1208
+ pointOnLine(point: any, line: any): this
1209
+ ```
1210
+
1211
+ #### `ccw()` — Constrain all given points to be in counter-clockwise order.
1212
+
1213
+ ```ts
1214
+ ccw(...points: any[]): this
1215
+ ```
1216
+
1217
+ **Tangent Transitions**
1218
+
1219
+ #### `lineTangentArc()` — Constrain a line to be tangent to an arc at its start or end point.
1220
+
1221
+ ```ts
1222
+ lineTangentArc(line: any, arc: any, atStart: boolean): this
1223
+ ```
1224
+
1225
+ #### `arcTangentArc()` — Constrain two arcs to be tangent at their shared junction point.
1226
+
1227
+ ```ts
1228
+ arcTangentArc(arcA: any, arcB: any, aAtStart?: boolean, bAtStart?: boolean): this
1229
+ ```
1230
+
1231
+ #### `bezierTangentArc()` — Constrain a Bezier to be tangent to an arc at one endpoint.
1232
+
1233
+ ```ts
1234
+ bezierTangentArc(bezier: any, arc: any, atBezierStart: boolean, atArcStart: boolean): this
1235
+ ```
1236
+
1237
+ #### `smoothBlend()` — Create a Bezier blend between two arcs.
1238
+
1239
+ ```ts
1240
+ smoothBlend(arc1: any, arc2: any, options?: { weight?: number; arc1End?: "start" | "end"; arc2End?: "start" | "end"; }): BezierId
1241
+ ```
1242
+
1243
+ **Shape Constraints**
1244
+
1245
+ #### `shapeWidth()` — Constrain a shape's width.
1246
+
1247
+ ```ts
1248
+ shapeWidth(shape: any, value: number): this
1249
+ ```
1250
+
1251
+ #### `shapeHeight()` — Constrain a shape's height.
1252
+
1253
+ ```ts
1254
+ shapeHeight(shape: any, value: number): this
1255
+ ```
1256
+
1257
+ #### `shapeCentroidX()` — Constrain a shape's centroid X position.
1258
+
1259
+ ```ts
1260
+ shapeCentroidX(shape: any, value: number): this
1261
+ ```
1262
+
1263
+ #### `shapeCentroidY()` — Constrain a shape's centroid Y position.
1264
+
1265
+ ```ts
1266
+ shapeCentroidY(shape: any, value: number): this
1267
+ ```
1268
+
1269
+ #### `shapeArea()` — Constrain a shape's area.
1270
+
1271
+ ```ts
1272
+ shapeArea(shape: any, value: number): this
1273
+ ```
1274
+
1275
+ #### `shapeEqualCentroid()` — Constrain two shapes to have the same centroid.
1276
+
1277
+ ```ts
1278
+ shapeEqualCentroid(a: any, b: any): this
1279
+ ```
1280
+
1281
+ **Positioning**
1282
+
1283
+ #### `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.
1284
+
1285
+ ```ts
1286
+ offsetX(a: any, b: any, value: number): this
1287
+ ```
1288
+
1289
+ #### `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.
1290
+
1291
+ ```ts
1292
+ offsetY(a: any, b: any, value: number): this
1293
+ ```
1294
+
1295
+ #### `importPoint()` — Import a `Point2D` object into the sketch.
1296
+
1297
+ ```ts
1298
+ importPoint(pt: { x: number; y: number; }, fixed?: boolean): PointId
1299
+ ```
1300
+
1301
+ #### `importLine()` — Import a `Line2D` object into the sketch.
1302
+
1303
+ ```ts
1304
+ importLine(l: { start: { x: number; y: number; }; end: { x: number; y: number; }; }, fixed?: boolean): LineId
1305
+ ```
1306
+
1307
+ #### `importRectangle()` — Import a `Rectangle2D` as four points and four lines.
1308
+
1309
+ ```ts
1310
+ importRectangle(r: { vertices: [ { x: number; y: number; }, { x: number; y: number; }, { x: number; y: number; }, { x: number; y: number; } ]; }, fixed?: boolean): { ... }
1311
+ ```
1312
+
1313
+ #### `referencePoint()` — Add a fixed reference point at `(x, y)`.
1314
+
1315
+ ```ts
1316
+ referencePoint(x: number, y: number): PointId
1317
+ ```
1318
+
1319
+ #### `referenceLine()` — Add a fixed reference line from `(x1, y1)` to `(x2, y2)`.
1320
+
1321
+ ```ts
1322
+ referenceLine(x1: number, y1: number, x2: number, y2: number): LineId
1323
+ ```
1324
+
1325
+ #### `referenceFrom()` — Import a single named entity from a solved sketch as fixed reference geometry.
1326
+
1327
+ ```ts
1328
+ referenceFrom(source: ConstraintSketch, entityId: string): PointId | LineId | null
1329
+ ```
1330
+
1331
+ #### `referenceAllFrom()` — Import all non-construction entities from a solved sketch as fixed references.
1332
+
1333
+ ```ts
1334
+ referenceAllFrom(source: ConstraintSketch): { points: Map<string, PointId>; lines: Map<string, LineId>; }
1335
+ ```
1336
+
1337
+ **Solving**
1338
+
1339
+ #### `constrain()` — Add a raw constraint object to the builder.
1340
+
1341
+ ```ts
1342
+ constrain(constraint: Omit<SketchConstraint, "id">): this
1343
+ ```
1344
+
1345
+ #### `solve()` — Run the constraint solver and return a solved sketch.
1346
+
1347
+ 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:
1348
+
1349
+ ```ts
1350
+ const result = sk.solve();
1351
+ result.constraintMeta.status; // 'fully' | 'under' | 'over' | 'over-redundant'
1352
+ result.constraintMeta.dof; // 0 = fully constrained
1353
+ result.constraintMeta.maxError; // residual — should be < 1e-6
1354
+ result.inspect(); // human-readable summary
1355
+ result.withUpdatedConstraint('cst-5', 120); // update a dimension without rebuilding
1356
+ ```
1357
+
1358
+ **Troubleshooting**
1359
+
1360
+ - **Under-constrained (dof > 0)** — add `fix()`, `length()`, or other dimensional constraints.
1361
+ - **Over-constrained** — conflicting constraints are auto-rejected. Check `result.constraintMeta.constraints` and `result.inspect()`.
1362
+ - **maxError > 1e-6** — solver did not converge; check for contradictory constraints.
1363
+
1364
+ ```ts
1365
+ solve(options?: SolveOptions): ConstraintSketch | Sketch
1366
+ ```
1367
+
1368
+ #### `solveConstraintsOnly()` — Run the solver without building a full `ConstraintSketch`.
1369
+
1370
+ Lighter than `solve()` — skips profile and DOF analysis. Useful for lightweight constraint validation or progress monitoring mid-construction.
1371
+
1372
+ ```ts
1373
+ solveConstraintsOnly(options?: SolveOptions): { maxError: number; rejectedCount: number; definition: ConstraintDefinition; }
1374
+ ```
1375
+
1376
+ #### `route()` — Start a directional route from coordinates.
1377
+
1378
+ Returns a [`RouteBuilder`](/docs/viewport#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.
1379
+
1380
+ ```js
1381
+ ```ts
1382
+
1383
+ 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);
1384
+
1385
+ ```
1386
+ ```
1387
+
1388
+ ```ts
1389
+ route(x: number, y: number): RouteBuilder
1390
+ ```
1391
+
1392
+ ### `ConstraintSketch`
1393
+
1394
+ **Properties:**
1395
+
1396
+ | Property | Type | Description |
1397
+ |----------|------|-------------|
1398
+ | `constraintMeta` | `SketchConstraintMeta` | — |
1399
+ | `definition` | `ConstraintDefinition` | — |
1400
+
1401
+ **Methods:**
1402
+
1403
+ #### `detectArrangement()` — Enumerate all bounded regions formed by the line arrangement of this sketch. Construction lines are excluded. Regions are returned largest-first by area.
1404
+
1405
+ ```ts
1406
+ detectArrangement(): Sketch[]
1407
+ ```
1408
+
1409
+ #### `detectArrangementRegion()` — Select the single arrangement region that contains the given seed point. Throws if no region contains the seed.
1410
+
1411
+ ```ts
1412
+ detectArrangementRegion(seed: [ number, number ]): Sketch
1413
+ ```
1414
+
1415
+ #### `withUpdatedConstraint()` — Re-solve the sketch after changing the value of one existing constraint.
1416
+
1417
+ 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.
1418
+
1419
+ ```ts
1420
+ withUpdatedConstraint(constraintId: string, value: number): ConstraintSketch
1421
+ ```
1422
+
1423
+ #### `inspect()` — Return a human-readable diagnostic string of the solved state.
1424
+
1425
+ ```ts
1426
+ inspect(): string
1427
+ ```
1428
+
1429
+ ### `SketchGroupBuilder`
1430
+
1431
+ #### `point()` — Add a point in local coordinates. Returns its globally-addressable PointId.
1432
+
1433
+ ```ts
1434
+ point(lx: number, ly: number): PointId
1435
+ ```
1436
+
1437
+ #### `line()` — Connect two group points with a line. Both must be PointIds from this group.
1438
+
1439
+ ```ts
1440
+ line(a: PointId, b: PointId, name?: string): LineId
1441
+ ```
1442
+
1443
+ #### `fixRotation()` — Freeze rotation (theta). Group can still translate - 2 DOF remain.
1444
+
1445
+ ```ts
1446
+ fixRotation(): this
1447
+ ```
1448
+
1449
+ #### `fix()` — Freeze all 3 DOF - group is completely fixed.
1450
+
1451
+ ```ts
1452
+ fix(): this
1453
+ ```
1454
+
1455
+ #### `done()` — Finalize and register the group with the builder.
1456
+
1457
+ ```ts
1458
+ done(): SketchGroupHandle
1459
+ ```
1460
+
1461
+ ### `Point2D`
1462
+
1463
+ An immutable 2D point with measurement and construction helpers.
1464
+
1465
+ Used as construction geometry in sketches, constraints, and analytic measurements. All methods return new instances — `Point2D` is immutable.
1466
+
1467
+ **Properties:**
1468
+
1469
+ | Property | Type | Description |
1470
+ |----------|------|-------------|
1471
+ | `x` | `number` | — |
1472
+ | `y` | `number` | — |
1473
+
1474
+ **Methods:**
1475
+
1476
+ #### `distanceTo()` — Measure straight-line distance to another point.
1477
+
1478
+ ```ts
1479
+ distanceTo(other: Point2D): number
1480
+ ```
1481
+
1482
+ #### `midpointTo()` — Compute the midpoint between this point and another point.
1483
+
1484
+ ```ts
1485
+ midpointTo(other: Point2D): Point2D
1486
+ ```
1487
+
1488
+ #### `translate()` — Return a point shifted by the given delta.
1489
+
1490
+ ```ts
1491
+ translate(dx: number, dy: number): Point2D
1492
+ ```
1493
+
1494
+ #### `toTuple()` — Convert this point to a plain `[x, y]` tuple.
1495
+
1496
+ ```ts
1497
+ toTuple(): [ number, number ]
1498
+ ```
1499
+
1500
+ ### `Line2D`
1501
+
1502
+ An immutable 2D line segment with length, angle, intersection, and parallel helpers.
1503
+
1504
+ Provides both segment-only (`intersectSegment`) and infinite-line (`intersect`) intersection queries. All methods return new instances.
1505
+
1506
+ **Properties:**
1507
+
1508
+ | Property | Type | Description |
1509
+ |----------|------|-------------|
1510
+ | `start` | `Point2D` | — |
1511
+ | `end` | `Point2D` | — |
1512
+
1513
+ **Methods:**
1514
+
1515
+ #### `length()` — Length of the line segment.
1516
+
1517
+ ```ts
1518
+ get length(): number
1519
+ ```
1520
+
1521
+ #### `midpoint()` — Midpoint of the line segment.
1522
+
1523
+ ```ts
1524
+ get midpoint(): Point2D
1525
+ ```
1526
+
1527
+ #### `angle()` — Direction angle in degrees, measured CCW from +X.
1528
+
1529
+ ```ts
1530
+ get angle(): number
1531
+ ```
1532
+
1533
+ #### `direction()` — Unit direction vector from start to end.
1534
+
1535
+ ```ts
1536
+ get direction(): [ number, number ]
1537
+ ```
1538
+
1539
+ #### `parallel()` — Create a parallel line offset by the given distance.
1540
+
1541
+ Positive distance shifts to the left of the line direction.
1542
+
1543
+ ```ts
1544
+ parallel(distance: number): Line2D
1545
+ ```
1546
+
1547
+ #### `intersect()` — Intersect this line with another infinite line.
1548
+
1549
+ ```ts
1550
+ intersect(other: Line2D): Point2D | null
1551
+ ```
1552
+
1553
+ #### `intersectSegment()` — Intersect this line with another as bounded segments.
1554
+
1555
+ ```ts
1556
+ intersectSegment(other: Line2D): Point2D | null
1557
+ ```
1558
+
1559
+ #### `fromCoordinates()` — Create a line from raw coordinates.
1560
+
1561
+ ```ts
1562
+ static fromCoordinates(x1: number, y1: number, x2: number, y2: number): Line2D
1563
+ ```
1564
+
1565
+ #### `fromPointAndAngle()` — Create a line from a start point, angle, and length.
1566
+
1567
+ ```ts
1568
+ static fromPointAndAngle(origin: Point2D, angleDeg: number, length: number): Line2D
1569
+ ```
1570
+
1571
+ #### `fromPointAndDirection()` — Create a line from a start point, direction vector, and length.
1572
+
1573
+ ```ts
1574
+ static fromPointAndDirection(origin: Point2D, dir: [ number, number ], length: number): Line2D
1575
+ ```
1576
+
1577
+ ### `Circle2D`
1578
+
1579
+ An immutable 2D circle with area, circumference, and extrusion support.
1580
+
1581
+ Extruding a `Circle2D` produces a cylinder with named `top`, `bottom`, and `side` faces accessible via the topology API.
1582
+
1583
+ **Properties:**
1584
+
1585
+ | Property | Type | Description |
1586
+ |----------|------|-------------|
1587
+ | `center` | `Point2D` | — |
1588
+ | `radius` | `number` | — |
1589
+
1590
+ **Methods:**
1591
+
1592
+ #### `diameter()` — Diameter of the circle.
1593
+
1594
+ ```ts
1595
+ get diameter(): number
1596
+ ```
1597
+
1598
+ #### `circumference()` — Circumference of the circle.
1599
+
1600
+ ```ts
1601
+ get circumference(): number
1602
+ ```
1603
+
1604
+ #### `area()` — Area of the circle.
1605
+
1606
+ ```ts
1607
+ get area(): number
1608
+ ```
1609
+
1610
+ #### `pointAtAngle()` — Return a point on the circle at the given angle.
1611
+
1612
+ ```ts
1613
+ pointAtAngle(angleDeg: number): Point2D
1614
+ ```
1615
+
1616
+ #### `translate()` — Return a translated circle.
1617
+
1618
+ ```ts
1619
+ translate(dx: number, dy: number): Circle2D
1620
+ ```
1621
+
1622
+ #### `toSketch()` — Convert this circle to a sketch profile.
1623
+
1624
+ ```ts
1625
+ toSketch(segments?: number): Sketch
1626
+ ```
1627
+
1628
+ #### `extrude()` — Extrude the circle into a solid cylinder.
1629
+
1630
+ ```ts
1631
+ extrude(height: number, segments?: number): Shape
1632
+ ```
1633
+
1634
+ #### `fromCenterAndRadius()` — Create a circle from its center and radius.
1635
+
1636
+ ```ts
1637
+ static fromCenterAndRadius(center: Point2D, radius: number): Circle2D
1638
+ ```
1639
+
1640
+ #### `fromDiameter()` — Create a circle from its center and diameter.
1641
+
1642
+ ```ts
1643
+ static fromDiameter(center: Point2D, diameter: number): Circle2D
1644
+ ```
1645
+
1646
+ ### `Rectangle2D`
1647
+
1648
+ A rectangle with named sides, vertices, and extrusion support.
1649
+
1650
+ Sides are named based on the rectangle's local orientation at construction time. Vertices go: bottom-left, bottom-right, top-right, top-left (CCW).
1651
+
1652
+ Extruding a `Rectangle2D` produces a [`Shape`](/docs/core#shape) with named faces: `top`, `bottom`, `side-left`, `side-right`, `side-top`, `side-bottom`. These are accessible via the topology API (`.face()`, `.edge()`).
1653
+
1654
+ ```ts
1655
+ const r = rectangle(0, 0, 100, 60);
1656
+ r.side('top'); r.side('left'); // Line2D
1657
+ r.vertex('top-left'); // Point2D
1658
+ r.width; r.height; r.center;
1659
+ const [d1, d2] = r.diagonals(); // [bl-tr, br-tl]
1660
+
1661
+ r.toSketch(); // Sketch (for 2D operations)
1662
+ r.extrude(20); // Shape with named faces
1663
+
1664
+ Rectangle2D.fromCenterAndDimensions(point(50, 30), 100, 60);
1665
+ Rectangle2D.from2Corners(point(0, 0), point(100, 60));
1666
+ Rectangle2D.from3Points(p1, p2, p3); // free-angle rectangle
1667
+ ```
1668
+
1669
+ #### `width()` — Width of the rectangle.
1670
+
1671
+ ```ts
1672
+ get width(): number
1673
+ ```
1674
+
1675
+ #### `height()` — Height of the rectangle.
1676
+
1677
+ ```ts
1678
+ get height(): number
1679
+ ```
1680
+
1681
+ #### `center()` — Geometric center of the rectangle.
1682
+
1683
+ ```ts
1684
+ get center(): Point2D
1685
+ ```
1686
+
1687
+ #### `side()` — Return a named side of the rectangle.
1688
+
1689
+ ```ts
1690
+ side(name: RectSide): Line2D
1691
+ ```
1692
+
1693
+ #### `sideAt()` — Return a side by index.
1694
+
1695
+ ```ts
1696
+ sideAt(index: number): Line2D
1697
+ ```
1698
+
1699
+ #### `vertex()` — Return a named vertex of the rectangle.
1700
+
1701
+ ```ts
1702
+ vertex(name: RectVertex): Point2D
1703
+ ```
1704
+
1705
+ #### `diagonals()` — Return the two diagonals of the rectangle.
1706
+
1707
+ ```ts
1708
+ diagonals(): [ Line2D, Line2D ]
1709
+ ```
1710
+
1711
+ #### `toSketch()` — Convert the rectangle to a sketch profile.
1712
+
1713
+ ```ts
1714
+ toSketch(): Sketch
1715
+ ```
1716
+
1717
+ #### `translate()` — Return a translated rectangle.
1718
+
1719
+ ```ts
1720
+ translate(dx: number, dy: number): Rectangle2D
1721
+ ```
1722
+
1723
+ #### `fromDimensions()` — Create an axis-aligned rectangle from origin corner plus width and height.
1724
+
1725
+ ```ts
1726
+ static fromDimensions(x: number, y: number, width: number, height: number): Rectangle2D
1727
+ ```
1728
+
1729
+ #### `fromCenterAndDimensions()` — Create a rectangle centered on a point.
1730
+
1731
+ ```ts
1732
+ static fromCenterAndDimensions(center: Point2D, width: number, height: number): Rectangle2D
1733
+ ```
1734
+
1735
+ #### `from2Corners()` — Create an axis-aligned rectangle from two opposite corners.
1736
+
1737
+ ```ts
1738
+ static from2Corners(p1: Point2D, p2: Point2D): Rectangle2D
1739
+ ```
1740
+
1741
+ #### `from3Points()` — Create a free-angle rectangle from three points.
1742
+
1743
+ `p1` and `p2` define one edge, and `p3` chooses the perpendicular side.
1744
+
1745
+ ```ts
1746
+ static from3Points(p1: Point2D, p2: Point2D, p3: Point2D): Rectangle2D
1747
+ ```
1748
+
1749
+ #### `extrude()` — Extrude the rectangle into a solid prism with named topology.
1750
+
1751
+ ```ts
1752
+ extrude(height: number, up?: boolean): Shape
1753
+ ```