forgecad 0.7.0 → 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 (158) hide show
  1. package/README.md +1 -1
  2. package/dist/assets/{AdminPage-DAu1C1ST.js → AdminPage-D4bocK4E.js} +1 -1
  3. package/dist/assets/{DocsPage-Gc_BCdqC.js → DocsPage-D3A_g8V3.js} +85 -45
  4. package/dist/assets/{EditorApp-DG1-oUSV.css → EditorApp-BWYUSpUN.css} +133 -51
  5. package/dist/assets/EditorApp-Cihhqcsq.js +11692 -0
  6. package/dist/assets/{EmbedViewer-CEO8XbV8.js → EmbedViewer-kWjKaC_t.js} +1 -1
  7. package/dist/assets/LandingPageProofDriven-Bg2IUc3l.css +856 -0
  8. package/dist/assets/LandingPageProofDriven-DXkKlyhI.js +601 -0
  9. package/dist/assets/{PricingPage-BSrxu6d7.js → PricingPage-BsU5vzEx.js} +1 -1
  10. package/dist/assets/{SettingsPage-FUCSIRq6.js → SettingsPage-PqvpAKIs.js} +1 -1
  11. package/dist/assets/{evalWorker-KoR0SNKq.js → evalWorker-C-hzNUMy.js} +2218 -286
  12. package/dist/assets/{index-wTEK39at.js → index-Pz321YAt.js} +7416 -1481
  13. package/dist/assets/{index-CyVd1D4D.css → index-ay13WNfa.css} +501 -2
  14. package/dist/assets/{manifold-B1sGWdYk.js → manifold-BcbjWLIo.js} +3 -3
  15. package/dist/assets/{manifold-D7o0N50J.js → manifold-DBckbFgx.js} +1 -1
  16. package/dist/assets/{manifold-G5sBaXzi.js → manifold-O2AAGXyj.js} +1 -1
  17. package/dist/assets/{reportWorker-DYcRHhv9.js → reportWorker-Dxr-5A7w.js} +2003 -259
  18. package/dist/docs/index.html +2 -2
  19. package/dist/docs-raw/CLI.md +488 -0
  20. package/dist/docs-raw/generated/assembly.md +19 -11
  21. package/dist/docs-raw/generated/concepts.md +1023 -360
  22. package/dist/docs-raw/generated/core.md +1165 -264
  23. package/dist/docs-raw/generated/curves.md +168 -1
  24. package/dist/docs-raw/generated/lib.md +10 -5
  25. package/dist/docs-raw/generated/output.md +1 -1
  26. package/dist/docs-raw/generated/sdf.md +208 -0
  27. package/dist/docs-raw/generated/sketch.md +1281 -329
  28. package/dist/docs-raw/generated/viewport.md +29 -2
  29. package/dist/index.html +2 -2
  30. package/dist/landing/proof-ams-adapter.png +0 -0
  31. package/dist/landing/proof-bolt-and-nut.png +0 -0
  32. package/dist/landing/proof-fillet-enclosure.png +0 -0
  33. package/dist/landing/proof-glasses.png +0 -0
  34. package/dist/landing/proof-gyroid.png +0 -0
  35. package/dist/sitemap.xml +6 -6
  36. package/dist-cli/forgecad.js +3148 -555
  37. package/dist-cli/forgecad.js.map +1 -1
  38. package/dist-cli/{solver-FV7TJZGI.js → solver-46FFSK2U.js} +1 -3
  39. package/dist-cli/{solver-FV7TJZGI.js.map → solver-46FFSK2U.js.map} +1 -1
  40. package/dist-skill/CONTEXT.md +3700 -1153
  41. package/dist-skill/SKILL-dev.md +15 -17
  42. package/dist-skill/SKILL.md +14 -9
  43. package/dist-skill/docs/API/core/concepts.md +28 -1
  44. package/dist-skill/docs/CLI.md +488 -0
  45. package/dist-skill/docs/generated/assembly.md +19 -11
  46. package/dist-skill/docs/generated/core.md +1165 -264
  47. package/dist-skill/docs/generated/curves.md +168 -1
  48. package/dist-skill/docs/generated/lib.md +10 -5
  49. package/dist-skill/docs/generated/output.md +1 -1
  50. package/dist-skill/docs/generated/sdf.md +208 -0
  51. package/dist-skill/docs/generated/sketch.md +1281 -329
  52. package/dist-skill/docs/generated/viewport.md +29 -2
  53. package/dist-skill/docs/guides/joint-design.md +78 -0
  54. package/dist-skill/docs-dev/API/core/concepts.md +28 -1
  55. package/dist-skill/docs-dev/CLI.md +488 -0
  56. package/dist-skill/docs-dev/coding.md +1 -1
  57. package/dist-skill/docs-dev/component-model.md +164 -0
  58. package/dist-skill/docs-dev/generated/assembly.md +19 -11
  59. package/dist-skill/docs-dev/generated/core.md +1165 -264
  60. package/dist-skill/docs-dev/generated/curves.md +168 -1
  61. package/dist-skill/docs-dev/generated/lib.md +10 -5
  62. package/dist-skill/docs-dev/generated/output.md +1 -1
  63. package/dist-skill/docs-dev/generated/sdf.md +208 -0
  64. package/dist-skill/docs-dev/generated/sketch.md +1281 -329
  65. package/dist-skill/docs-dev/generated/viewport.md +29 -2
  66. package/dist-skill/docs-dev/guides/joint-design.md +78 -0
  67. package/examples/api/attachTo-basics.forge.js +3 -3
  68. package/examples/api/bill-of-materials.forge.js +9 -9
  69. package/examples/api/bolt-pattern.forge.js +5 -5
  70. package/examples/api/boolean-operations.forge.js +2 -2
  71. package/examples/api/bounding-box-visualizer.forge.js +1 -1
  72. package/examples/api/clone-duplicate.forge.js +1 -1
  73. package/examples/api/connector-assembly.forge.js +4 -2
  74. package/examples/api/connector-basics.forge.js +5 -5
  75. package/examples/api/constrained-sketch-mechanical.forge.js +4 -4
  76. package/examples/api/elbow-test.forge.js +3 -3
  77. package/examples/api/extrude-options.forge.js +4 -4
  78. package/examples/api/fillet-showcase.forge.js +1 -1
  79. package/examples/api/gears-tier1.forge.js +5 -5
  80. package/examples/api/group-test.forge.js +2 -2
  81. package/examples/api/mesh-import-slats.forge.js +3 -3
  82. package/examples/api/patterns.forge.js +3 -3
  83. package/examples/api/pointAlong-orientation.forge.js +2 -2
  84. package/examples/api/profile-2020-b-slot6.forge.js +4 -4
  85. package/examples/api/sketch-rounding-strategies.forge.js +1 -1
  86. package/examples/api/smooth-curve-connections.forge.js +1 -1
  87. package/examples/api/transition-curves.forge.js +3 -3
  88. package/examples/constraints/01-fully-constrained-rect.forge.js +2 -2
  89. package/examples/constraints/02-underconstrained-triangle.forge.js +1 -1
  90. package/examples/constraints/03-redundant-constraints.forge.js +2 -2
  91. package/examples/constraints/05-parallel-with-linedistance.forge.js +2 -2
  92. package/examples/constraints/06-complex-spectrogram.forge.js +1 -1
  93. package/examples/constraints/07-perpendicular-chain.forge.js +4 -4
  94. package/examples/constraints/08-symmetric-bracket.forge.js +4 -4
  95. package/examples/constraints/09-stress-spiral.forge.js +1 -1
  96. package/examples/constraints/10-stress-honeycomb.forge.js +1 -1
  97. package/examples/constraints/11-surface-grid.forge.js +2 -2
  98. package/examples/constraints/12-surface-nested.forge.js +4 -4
  99. package/examples/constraints/13-surface-complex.forge.js +1 -1
  100. package/examples/exact-arc-housing.forge.js +12 -0
  101. package/examples/furniture/adjustable-table.forge.js +13 -13
  102. package/examples/furniture/bathroom.forge.js +15 -15
  103. package/examples/furniture/chair.forge.js +12 -12
  104. package/examples/furniture/picture-frame.forge.js +6 -6
  105. package/examples/furniture/shoe-rack-doors.forge.js +8 -8
  106. package/examples/furniture/shoe-rack.forge.js +7 -7
  107. package/examples/furniture/table-lamp.forge.js +8 -8
  108. package/examples/gcode/lissajous-vase.forge.js +4 -4
  109. package/examples/gcode/math-surface.forge.js +3 -3
  110. package/examples/gcode/parametric-vase.forge.js +4 -4
  111. package/examples/gcode/spiral-tower.forge.js +4 -4
  112. package/examples/generative/crystal-growth.forge.js +7 -7
  113. package/examples/generative/frost-spires.forge.js +6 -6
  114. package/examples/generative/golden-spiral-tower.forge.js +8 -8
  115. package/examples/generative/molten-forge.forge.js +6 -6
  116. package/examples/generative/neon-coral.forge.js +7 -7
  117. package/examples/mechanical/3d-printer.forge.js +9 -9
  118. package/examples/mechanical/5-finger-robot-hand.forge.js +4 -4
  119. package/examples/mechanical/airplane-propeller.forge.js +7 -7
  120. package/examples/mechanical/bolt-and-nut.forge.js +10 -10
  121. package/examples/mechanical/door-with-hinges.forge.js +7 -7
  122. package/examples/mechanical/fillet-enclosure.forge.js +14 -10
  123. package/examples/mechanical/headphone-hanger-v2.forge.js +9 -9
  124. package/examples/mechanical/robot_hand.forge.js +10 -10
  125. package/examples/mechanical/robot_hand_2.forge.js +17 -17
  126. package/examples/nurbs-surface.forge.js +8 -0
  127. package/examples/nurbs-tube.forge.js +7 -0
  128. package/examples/products/bottle.forge.js +7 -7
  129. package/examples/products/chess-set.forge.js +6 -6
  130. package/examples/products/classical-piano.forge.js +9 -9
  131. package/examples/products/clock.forge.js +21 -21
  132. package/examples/products/cup.forge.js +5 -5
  133. package/examples/products/iphone.forge.js +12 -12
  134. package/examples/products/laptop.forge.js +9 -9
  135. package/examples/products/laser-cut-box.forge.js +6 -6
  136. package/examples/products/laser-cut-tray.forge.js +6 -6
  137. package/examples/products/liquid-soap-dispenser.forge.js +5 -5
  138. package/examples/products/origami-fish.forge.js +6 -6
  139. package/examples/products/spiderman-cake.forge.js +2 -2
  140. package/examples/shelf/container.forge.js +5 -5
  141. package/examples/shelf/shelf-unit.forge.js +6 -6
  142. package/examples/toolbox/bolted-joint.forge.js +5 -5
  143. package/package.json +3 -1
  144. package/dist/assets/EditorApp-D9bJvtf7.js +0 -11338
  145. package/dist/assets/LandingPage-CdCuEOdC.js +0 -451
  146. package/dist-cli/chunk-PZ5AY32C.js +0 -10
  147. package/dist-cli/chunk-PZ5AY32C.js.map +0 -1
  148. package/dist-skill/docs/CLI/export.md +0 -91
  149. package/dist-skill/docs/CLI/projects.md +0 -107
  150. package/dist-skill/docs/CLI/studio_publishing.md +0 -52
  151. package/dist-skill/docs/CLI/validation.md +0 -66
  152. package/dist-skill/docs-dev/API/core/sdf-advanced.md +0 -92
  153. package/dist-skill/docs-dev/API/core/sdf-primitives.md +0 -58
  154. package/dist-skill/docs-dev/API/core/sdf-workflow.md +0 -42
  155. package/dist-skill/docs-dev/CLI/export.md +0 -91
  156. package/dist-skill/docs-dev/CLI/projects.md +0 -107
  157. package/dist-skill/docs-dev/CLI/studio_publishing.md +0 -52
  158. package/dist-skill/docs-dev/CLI/validation.md +0 -66
@@ -5,23 +5,34 @@ 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
- #### `path()` — Create a new `PathBuilder` for tracing a 2D outline point by point.
17
-
18
- **Details**
19
-
20
- `PathBuilder` is a fluent API for constructing 2D profiles using a mix of line segments, arcs, bezier curves, and splines. Always start with `.moveTo(x, y)` to set the starting point. Call `.close()` to get a filled `Sketch`, or `.stroke(width)` to thicken an open polyline into a solid profile.
31
+ #### `path()` — Create a new [`PathBuilder`](/docs/curves#pathbuilder) for tracing a 2D outline point by point.
21
32
 
22
- Edge labels can be assigned with `.label('name')` after any segment they propagate through extrusion, revolve, loft, and sweep into named faces on the resulting `Shape`.
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.
23
34
 
24
- **Example**
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).
25
36
 
26
37
  ```ts
27
38
  // Closed triangle
@@ -39,190 +50,180 @@ const slot = path()
39
50
  .close();
40
51
  ```
41
52
 
42
- `path(): PathBuilder`
53
+ ```ts
54
+ path(): PathBuilder
55
+ ```
43
56
 
44
57
  #### `stroke()` — Create a stroked polyline sketch from an array of 2D points.
45
58
 
46
- `stroke(points: [ number, number ][], width: number, join?: "Round" | "Square"): Sketch`
59
+ ```ts
60
+ stroke(points: [ number, number ][], width: number, join?: "Round" | "Square"): Sketch
61
+ ```
47
62
 
48
63
  #### `rect()` — Create a 2D rectangle centered at the origin.
49
64
 
50
- **Example**
51
-
52
65
  ```ts
53
66
  rect(40, 20).extrude(5);
54
67
  ```
55
68
 
56
- `rect(width: number, height: number): Sketch`
69
+ ```ts
70
+ rect(width: number, height: number): Sketch
71
+ ```
57
72
 
58
73
  #### `circle2d()` — Create a 2D circle centered at the origin.
59
74
 
60
- **Details**
61
-
62
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.
63
76
 
64
- **Example**
65
-
66
77
  ```ts
67
78
  circle2d(25).extrude(10); // smooth cylinder
68
79
  circle2d(25, 6).extrude(10); // hexagonal prism
69
80
  ```
70
81
 
71
- `circle2d(radius: number, segments?: number): Sketch`
82
+ ```ts
83
+ circle2d(radius: number, segments?: number): Sketch
84
+ ```
72
85
 
73
86
  #### `roundedRect()` — Create a 2D rectangle with rounded corners, centered at the origin.
74
87
 
75
- **Details**
76
-
77
88
  The corner radius is automatically clamped to `min(width/2, height/2)` so it can never exceed the shape dimensions.
78
89
 
79
- **Example**
80
-
81
90
  ```ts
82
91
  roundedRect(60, 30, 5).extrude(3);
83
92
  ```
84
93
 
85
- `roundedRect(width: number, height: number, radius: number): Sketch`
94
+ ```ts
95
+ roundedRect(width: number, height: number, radius: number): Sketch
96
+ ```
86
97
 
87
98
  #### `polygon()` — Create a 2D polygon from an array of `[x, y]` points or `Point2D` objects.
88
99
 
89
- **Details**
90
-
91
100
  Winding order is normalized automatically — clockwise (CW) input is silently reversed to CCW before being passed to the geometry kernel.
92
101
 
93
- **Example**
94
-
95
102
  ```ts
96
103
  polygon([[0, 0], [50, 0], [25, 40]]).extrude(5); // triangle
97
104
  ```
98
105
 
99
- `polygon(points: ([ number, number ] | Point2D)[]): Sketch`
106
+ ```ts
107
+ polygon(points: ([ number, number ] | Point2D)[]): Sketch
108
+ ```
100
109
 
101
110
  #### `ngon()` — Create a regular polygon inscribed in a circle of the given radius.
102
111
 
103
- **Details**
104
-
105
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).
106
113
 
107
- **Example**
108
-
109
114
  ```ts
110
115
  ngon(6, 20).extrude(10); // hexagonal prism, circumradius 20
111
116
  ```
112
117
 
113
- `ngon(sides: number, radius: number): Sketch`
118
+ ```ts
119
+ ngon(sides: number, radius: number): Sketch
120
+ ```
114
121
 
115
122
  #### `ellipse()` — Create a 2D ellipse centered at the origin.
116
123
 
117
- **Example**
118
-
119
124
  ```ts
120
125
  ellipse(30, 15).extrude(5);
121
126
  ellipse(30, 15, 32).extrude(5); // lower-resolution approximation
122
127
  ```
123
128
 
124
- `ellipse(rx: number, ry: number, segments?: number): Sketch`
129
+ ```ts
130
+ ellipse(rx: number, ry: number, segments?: number): Sketch
131
+ ```
125
132
 
126
133
  #### `slot()` — Create a slot (oblong / stadium shape) — a rectangle with semicircular ends, centered at the origin.
127
134
 
128
- **Example**
129
-
130
135
  ```ts
131
136
  slot(40, 10).extrude(3); // 40mm long, 10mm wide slot
132
137
  ```
133
138
 
134
- `slot(length: number, width: number): Sketch`
139
+ ```ts
140
+ slot(length: number, width: number): Sketch
141
+ ```
135
142
 
136
143
  #### `arcSlot()` — Create an arc-shaped slot (banana / annular sector) centered at the origin.
137
144
 
138
- **Details**
139
-
140
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.
141
146
 
142
- **Example**
143
-
144
147
  ```ts
145
148
  arcSlot(135, 74, 40).extrude(5); // pitch R135, 74° sweep, 40mm wide
146
149
  ```
147
150
 
148
- `arcSlot(pitchRadius: number, sweepDeg: number, thickness: number): Sketch`
151
+ ```ts
152
+ arcSlot(pitchRadius: number, sweepDeg: number, thickness: number): Sketch
153
+ ```
149
154
 
150
155
  #### `star()` — Create a star shape with alternating outer and inner radii.
151
156
 
152
- **Example**
153
-
154
157
  ```ts
155
158
  star(5, 30, 12).extrude(4); // five-pointed star
156
159
  ```
157
160
 
158
- `star(points: number, outerR: number, innerR: number): Sketch`
161
+ ```ts
162
+ star(points: number, outerR: number, innerR: number): Sketch
163
+ ```
159
164
 
160
165
  ### 2D Sketch Booleans
161
166
 
162
167
  #### `union2d()` — Combine 2D sketches into a single profile using an additive boolean union.
163
168
 
164
- **Details**
165
-
166
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.
167
170
 
168
- **Example**
169
-
170
171
  ```ts
171
172
  const cross = union2d(rect(60, 10), rect(10, 60));
172
173
  ```
173
174
 
174
- `union2d(...inputs: SketchOperandInput[]): Sketch`
175
+ ```ts
176
+ union2d(...inputs: SketchOperandInput[]): Sketch
177
+ ```
175
178
 
176
179
  #### `difference2d()` — Subtract one or more 2D sketches from a base sketch.
177
180
 
178
- **Details**
179
-
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.
181
182
 
182
- **Example**
183
-
184
183
  ```ts
185
184
  const donut = difference2d(circle2d(50), circle2d(30));
186
185
  ```
187
186
 
188
- `difference2d(...inputs: SketchOperandInput[]): Sketch`
187
+ ```ts
188
+ difference2d(...inputs: SketchOperandInput[]): Sketch
189
+ ```
189
190
 
190
191
  #### `intersection2d()` — Keep only the area where all input sketches overlap (intersection boolean).
191
192
 
192
- **Details**
193
-
194
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.
195
194
 
196
- **Example**
197
-
198
195
  ```ts
199
196
  const lens = intersection2d(circle2d(30).translate(-10, 0), circle2d(30).translate(10, 0));
200
197
  ```
201
198
 
202
- `intersection2d(...inputs: SketchOperandInput[]): Sketch`
199
+ ```ts
200
+ intersection2d(...inputs: SketchOperandInput[]): Sketch
201
+ ```
203
202
 
204
203
  ### 2D Sketch Features
205
204
 
206
205
  #### `fillet2d()` — Round a named vertical edge of a shape with a circular fillet.
207
206
 
208
- **Details**
209
-
210
207
  Compiler-owned fillet for tracked vertical edges. Requires a compile-plan-covered target (shapes from `box()`, `rectangle().extrude()`, or rigid transforms of those).
211
208
 
212
- **Supported edges:** - Tracked vertical edges from `box()` or `rectangle().extrude()` - Rigid transforms between tracked source and target - Untouched sibling tracked vertical edges after earlier `fillet2d`/`chamfer2d`
209
+ **Supported edges:**
213
210
 
214
- **Not supported:** edges after shell, hole, cut, trim, difference, intersection, generic sketch extrudes, or tapered extrudes. Use `fillet()` with an `EdgeQuery` for those cases.
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`
215
214
 
216
- Canonical quadrants: `vert-bl [1,-1]`, `vert-br [-1,-1]`, `vert-tr [-1,1]`, `vert-tl [1,1]`
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.
217
216
 
218
- **Example**
217
+ Canonical quadrants: `vert-bl → [1,-1]`, `vert-br → [-1,-1]`, `vert-tr → [-1,1]`, `vert-tl → [1,1]`
219
218
 
220
219
  ```ts
221
220
  const b = rectangle(0, 0, 50, 50).extrude(20);
222
221
  const filleted = fillet2d(b.toShape(), b.edge('vert-br'), 5, [-1, -1]);
223
222
  ```
224
223
 
225
- `fillet2d(shape: Shape, edge: EdgeRef, radius: number, quadrant?: [ number, number ], segments?: number): Shape`
224
+ ```ts
225
+ fillet2d(shape: Shape, edge: EdgeRef, radius: number, quadrant?: [ number, number ], segments?: number): Shape
226
+ ```
226
227
 
227
228
  **`EdgeRef`**
228
229
  - `query?: EdgeQueryRef` — Compiler-owned edge query when available.
@@ -230,30 +231,26 @@ const filleted = fillet2d(b.toShape(), b.edge('vert-br'), 5, [-1, -1]);
230
231
 
231
232
  #### `chamfer2d()` — Bevel a named vertical edge of a shape with a 45° chamfer.
232
233
 
233
- **Details**
234
-
235
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.
236
235
 
237
- **Example**
238
-
239
236
  ```ts
240
237
  const b = rectangle(0, 0, 50, 50).extrude(20);
241
238
  const chamfered = chamfer2d(b.toShape(), b.edge('vert-br'), 3, [-1, -1]);
242
239
  ```
243
240
 
244
- `chamfer2d(shape: Shape, edge: EdgeRef, size: number, quadrant?: [ number, number ]): Shape`
241
+ ```ts
242
+ chamfer2d(shape: Shape, edge: EdgeRef, size: number, quadrant?: [ number, number ]): Shape
243
+ ```
245
244
 
246
245
  ### 2D Text
247
246
 
248
247
  #### `loadFont()` — Pre-load and cache a font for use with `text2d()`.
249
248
 
250
- **Details**
251
-
252
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.
253
250
 
254
- Built-in font names that work everywhere (browser + CLI): - `'sans-serif'` or `'inter'` — bundled Inter Regular
251
+ Built-in font names that work everywhere (browser + CLI):
255
252
 
256
- **Example**
253
+ - `'sans-serif'` or `'inter'` — bundled Inter Regular
257
254
 
258
255
  ```ts
259
256
  const font = loadFont('/path/to/Arial Bold.ttf');
@@ -261,20 +258,18 @@ text2d('Title', { size: 12, font }).extrude(1.5);
261
258
  text2d('Subtitle', { size: 8, font }).extrude(1);
262
259
  ```
263
260
 
264
- `loadFont(source: string | ArrayBuffer, cacheKey?: string): opentype$1.Font`
261
+ ```ts
262
+ loadFont(source: string | ArrayBuffer, cacheKey?: string): opentype.Font
263
+ ```
265
264
 
266
265
  #### `text2d()` — Build a filled 2D Sketch from a text string.
267
266
 
268
- **Details**
269
-
270
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.
271
268
 
272
269
  Alignment reference table:
273
270
 
274
271
  | `align` | `baseline` | Origin | |------------|--------------|-------------------------------------| | `'left'` | `'baseline'` | Bottom-left of first char (default) | | `'center'` | `'center'` | Dead center of text block | | `'right'` | `'top'` | Top-right corner |
275
272
 
276
- **Example**
277
-
278
273
  ```ts
279
274
  // Extruded nameplate
280
275
  text2d('FORGE CAD', { size: 8 }).extrude(1.2);
@@ -294,7 +289,9 @@ const font = loadFont('/path/to/Arial Bold.ttf');
294
289
  text2d('Title', { size: 12, font }).extrude(1.5);
295
290
  ```
296
291
 
297
- `text2d(content: string, options?: TextOptions): Sketch`
292
+ ```ts
293
+ text2d(content: string, options?: TextOptions): Sketch
294
+ ```
298
295
 
299
296
  **`TextOptions`**
300
297
 
@@ -304,23 +301,21 @@ text2d('Title', { size: 12, font }).extrude(1.5);
304
301
  | `letterSpacing?` | `number` | Extra space between characters in model units. Negative values tighten the tracking. |
305
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 |
306
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 |
307
- | `font?` | `string | opentype$1.Font` | Font to use for text rendering. - `'sans-serif'` or `'inter'` — bundled Inter font (works everywhere, including browser) - **file path** — path to a TTF, OTF, or WOFF font file (CLI/Node only) - **Font object** — a previously loaded opentype.js Font (from `loadFont()`) - **omitted** — uses the bundled Inter font (same as `'sans-serif'`) text2d('Hello World', { size: 10 }) // default Inter text2d('Custom Font', { size: 10, font: '/path/to/font.ttf' }) |
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' }) |
308
305
  | `flattenTolerance?` | `number` | Bezier flattening tolerance in model units. Smaller = more polygon segments = smoother curves. |
309
306
 
310
307
  #### `textWidth()` — Measure the rendered advance width of a string without creating any geometry.
311
308
 
312
- **Details**
313
-
314
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.
315
310
 
316
- **Example**
317
-
318
311
  ```ts
319
312
  const w = textWidth('SERIAL: 001', { size: 6 });
320
313
  const plate = box(w + 10, 12, 2);
321
314
  ```
322
315
 
323
- `textWidth(content: string, options?: Pick<TextOptions, "size" | "letterSpacing" | "font">): number`
316
+ ```ts
317
+ textWidth(content: string, options?: Pick<TextOptions, "size" | "letterSpacing" | "font">): number
318
+ ```
324
319
 
325
320
  ### Constrained Sketches
326
321
 
@@ -328,9 +323,10 @@ const plate = box(w + 10, 12, 2);
328
323
 
329
324
  **Workflow**
330
325
 
331
- 1. Create a builder with `constrainedSketch()`. 2. Add geometry — points, lines, circles, arcs — using the builder methods. 3. Add constraints (`horizontal`, `length`, `fix`, etc.) to drive the geometry. 4. Call `.solve()` to run the solver and get a `ConstraintSketch` (which extends `Sketch`).
332
-
333
- **Example**
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`).
334
330
 
335
331
  ```ts
336
332
  const sk = constrainedSketch();
@@ -354,7 +350,9 @@ result.inspect(); // human-readable summary
354
350
  result.withUpdatedConstraint('cst-5', 120); // update a dimension without rebuilding
355
351
  ```
356
352
 
357
- `constrainedSketch(options?: ConstrainedSketchOptions): ConstrainedSketchBuilder`
353
+ ```ts
354
+ constrainedSketch(options?: ConstrainedSketchOptions): ConstrainedSketchBuilder
355
+ ```
358
356
 
359
357
  **`ConstrainedSketchOptions`**
360
358
  - `strict?: boolean` — When true, adding a constraint that cannot be satisfied throws instead of silently discarding it.
@@ -365,8 +363,6 @@ Creates 4 vertices (CCW: bl→br→tr→tl), 4 sides, 4 structural constraints (
365
363
 
366
364
  Use `sk.rect()` as the shorthand builder method.
367
365
 
368
- **Example**
369
-
370
366
  ```ts
371
367
  const sk = constrainedSketch();
372
368
  const r = sk.rect({ x: 0, y: 0, width: 100, height: 50 });
@@ -375,7 +371,9 @@ sk.length(r.bottom, 120); // override initial width
375
371
  return sk.solve().extrude(10);
376
372
  ```
377
373
 
378
- `addRect(sk: ConstrainedSketchBuilder, options?: RectOptions): ConstrainedRect`
374
+ ```ts
375
+ addRect(sk: ConstrainedSketchBuilder, options?: RectOptions): ConstrainedRect
376
+ ```
379
377
 
380
378
  **`RectOptions`**
381
379
 
@@ -405,8 +403,6 @@ Creates n vertices and n sides (CCW: `sides[i]` from `vertices[i]` → `vertices
405
403
 
406
404
  Use `sk.addPolygon()` as the shorthand builder method.
407
405
 
408
- **Example**
409
-
410
406
  ```ts
411
407
  const sk = constrainedSketch();
412
408
  const tri = sk.addPolygon({ points: [[0,0],[100,0],[50,80]] });
@@ -415,7 +411,9 @@ sk.length(tri.side(0), 100);
415
411
  return sk.solve().extrude(5);
416
412
  ```
417
413
 
418
- `addPolygon(sk: ConstrainedSketchBuilder, options: PolygonOptions): ConstrainedPolygon`
414
+ ```ts
415
+ addPolygon(sk: ConstrainedSketchBuilder, options: PolygonOptions): ConstrainedPolygon
416
+ ```
419
417
 
420
418
  **`PolygonOptions`**
421
419
  - `addLoop?: boolean` — Whether to register a closed loop for sketch generation. Default: true.
@@ -432,8 +430,6 @@ Vertices are placed at `(cx + r·cos(startAngle + i·2π/n), cy + r·sin(...))`.
432
430
 
433
431
  Use `sk.regularPolygon()` as the shorthand builder method.
434
432
 
435
- **Example**
436
-
437
433
  ```ts
438
434
  const sk = constrainedSketch();
439
435
  const hex = sk.regularPolygon({ sides: 6, radius: 25 });
@@ -442,7 +438,9 @@ sk.length(hex.side(0), 30); // all sides change (equal constraint)
442
438
  return sk.solve().extrude(5);
443
439
  ```
444
440
 
445
- `addRegularPolygon(sk: ConstrainedSketchBuilder, options: RegularPolygonOptions): ConstrainedRegularPolygon`
441
+ ```ts
442
+ addRegularPolygon(sk: ConstrainedSketchBuilder, options: RegularPolygonOptions): ConstrainedRegularPolygon
443
+ ```
446
444
 
447
445
  **`RegularPolygonOptions`**
448
446
 
@@ -463,8 +461,6 @@ return sk.solve().extrude(5);
463
461
 
464
462
  #### `point()` — Create an analytic 2D point for measurement and construction geometry.
465
463
 
466
- **Example**
467
-
468
464
  ```ts
469
465
  const p = point(10, 20);
470
466
  p.distanceTo(point(30, 40)); // Euclidean distance
@@ -473,12 +469,12 @@ p.translate(5, 5); // new shifted point
473
469
  p.toTuple(); // [10, 20]
474
470
  ```
475
471
 
476
- `point(x: number, y: number): Point2D`
472
+ ```ts
473
+ point(x: number, y: number): Point2D
474
+ ```
477
475
 
478
476
  #### `line()` — Create an analytic 2D line segment between two points.
479
477
 
480
- **Example**
481
-
482
478
  ```ts
483
479
  const l = line(0, 0, 50, 0);
484
480
  l.length; l.midpoint; l.angle; l.direction;
@@ -490,12 +486,12 @@ Line2D.fromPointAndAngle(point(0, 0), 45, 100);
490
486
  Line2D.fromPointAndDirection(point(0, 0), [1, 1], 50);
491
487
  ```
492
488
 
493
- `line(x1: number, y1: number, x2: number, y2: number): Line2D`
489
+ ```ts
490
+ line(x1: number, y1: number, x2: number, y2: number): Line2D
491
+ ```
494
492
 
495
493
  #### `circle()` — Create an analytic 2D circle for measurement, construction, and extrusion.
496
494
 
497
- **Example**
498
-
499
495
  ```ts
500
496
  const c = circle(0, 0, 25);
501
497
  c.diameter; c.circumference; c.area;
@@ -509,19 +505,25 @@ cyl.face('side'); // FaceRef (curved)
509
505
  Circle2D.fromDiameter(point(0, 0), 50);
510
506
  ```
511
507
 
512
- `circle(cx: number, cy: number, radius: number): Circle2D`
508
+ ```ts
509
+ circle(cx: number, cy: number, radius: number): Circle2D
510
+ ```
513
511
 
514
512
  #### `degrees()` — Identity function that returns degrees unchanged.
515
513
 
516
514
  Use for clarity when the unit of an angle value would otherwise be ambiguous — e.g. `param("Angle", degrees(45))`.
517
515
 
518
- `degrees(deg: number): number`
516
+ ```ts
517
+ degrees(deg: number): number
518
+ ```
519
519
 
520
520
  #### `radians()` — Convert radians to degrees.
521
521
 
522
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.
523
523
 
524
- `radians(rad: number): number`
524
+ ```ts
525
+ radians(rad: number): number
526
+ ```
525
527
 
526
528
  ---
527
529
 
@@ -531,11 +533,17 @@ ForgeCAD's public API uses degrees throughout. Use this when you have a radian v
531
533
 
532
534
  Immutable 2D profile for extrusion, revolve, and other operations.
533
535
 
534
- **Details**
535
-
536
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
537
 
538
- Supported operations: - **Transforms** — `translate`, `rotate`, `rotateAround`, `scale`, `mirror` - **Booleans** — `add` (union), `subtract` (difference), `intersect` - **Operations** — `offset`, `simplify` - **Queries** — `area`, `bounds`, `isEmpty`, `numVert` - **3D operations** — `extrude`, `revolve`, `onFace` - **Regions** — `regions`, `region` - **Placement** — `attachTo`
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`
539
547
 
540
548
  Named anchor positions used by `attachTo()`: `'center'` | `'top-left'` | `'top-right'` | `'bottom-left'` | `'bottom-right'` | `'top'` | `'bottom'` | `'left'` | `'right'`
541
549
 
@@ -545,257 +553,1201 @@ Named anchor positions used by `attachTo()`: `'center'` | `'top-left'` | `'top-r
545
553
  |----------|------|-------------|
546
554
  | `cross` | `ProfileBackend` | — |
547
555
 
548
- **Methods:**
556
+ **Transforms**
549
557
 
550
- - `color(value: string | undefined): Sketch` — Set the display color of this sketch. **Details** Color is preserved through all transforms and boolean operations. Pass `undefined` to clear the color. **Example** ```ts circle2d(20).color('#ff0000').extrude(5); ```
551
- - `clone(): Sketch` — Create an explicit copy of this sketch for branching variants. **Details** Because all Sketch operations are immutable, `clone()` is rarely needed. Use it when you want to assign the same sketch to multiple names and continue modifying each independently without confusion.
552
- - `area(): number` — Return the total filled area of the sketch.
553
- - `bounds(): ProfileBounds` — Return the axis-aligned bounding box of the sketch.
554
- - `isEmpty(): boolean` — Return `true` if the sketch contains no filled area.
555
- - `numVert(): number` — Return the number of vertices in the polygon representation of the sketch contours.
556
- - `toPolygons(): number[][][]` — Return the sketch as a list of polygons matching its contour topology. Useful when you need raw polygon data for inspection or custom export.
557
- - `translate(x: number, y?: number): Sketch` — Move the sketch by the given X and Y offset.
558
- - `rotate(degrees: number): Sketch` — Rotate the sketch around its bounding-box center.
559
- - `rotateAround(degrees: number, pivot: [ number, number ]): Sketch` — Rotate the sketch around a specific pivot point. **Example** ```ts rect(20, 20).rotateAround(45, [0, 0]); ```
560
- - `scale(v: number | [ number, number ]): Sketch` — Scale the sketch relative to its bounding-box center. **Details** Pass a single number for uniform scaling, or `[sx, sy]` for per-axis scaling.
561
- - `mirror(normal: [ number, number ]): Sketch` — Mirror the sketch across a line through its bounding-box center. **Details** `normal` is the normal vector of the mirror line (not the line direction). For example, `[1, 0]` mirrors across a vertical line (Y axis direction), and `[0, 1]` mirrors across a horizontal line.
562
- - `scaleAround(pivot: [ number, number ], v: number | [ number, number ]): Sketch` — Scale the sketch relative to an arbitrary pivot point.
563
- - `mirrorThrough(point: [ number, number ], normal: [ number, number ]): Sketch` — Mirror the sketch across a line defined by a point and a normal direction.
564
- - `add(...others: SketchOperandInput[]): Sketch` — Add (union) one or more sketches to this sketch. **Details** Accepts individual sketches or arrays: `sketch.add(a, b)` or `sketch.add([a, b])`. For combining many sketches at once, prefer the free function `union2d()` which uses Manifold's batch operation and is faster than chaining. **Example** ```ts circle2d(20).add(rect(10, 30)).extrude(5); ```
565
- - `subtract(...others: SketchOperandInput[]): Sketch` — Subtract one or more sketches from this sketch. **Details** Accepts individual sketches or arrays: `sketch.subtract(a, b)` or `sketch.subtract([a, b])`. For subtracting many cutters at once, prefer the free function `difference2d()`. **Example** ```ts rect(40, 40).subtract(circle2d(10)).extrude(5); ```
566
- - `intersect(...others: SketchOperandInput[]): Sketch` — Intersect this sketch with one or more others (keep overlapping area only). **Details** Accepts individual sketches or arrays: `sketch.intersect(a, b)` or `sketch.intersect([a, b])`. For intersecting many sketches, prefer the free function `intersection2d()`.
567
- - `offset(delta: number, join?: "Square" | "Round" | "Miter"): Sketch` — Inflate (positive delta) or deflate (negative delta) the sketch contour. **Details** Use `offset(-r).offset(+r)` to round every convex corner of a closed sketch. - `'Round'` — smooth arc at each corner (default) - `'Square'` — flat mitered extension - `'Miter'` — sharp pointed extension **Example** ```ts rect(40, 20).offset(3); // expand by 3 rect(40, 20).offset(-2).offset(2); // round all convex corners ```
568
- - `regions(): Sketch[]` — Decompose this sketch into its distinct filled regions, sorted largest-first by area. **Details** A single sketch can contain several disconnected filled areas (e.g., two separate rectangles, or a ring shape with a hole). This method enumerates all top-level connected regions as independent `Sketch` objects, each with its own outer boundary and associated holes. **Example** ```ts const pair = union2d(rect(40, 40), rect(40, 40).translate(60, 0)); const [left, right] = pair.regions(); // largest first left.extrude(5); ```
569
- - `region(seed: [ number, number ]): Sketch` — Select the single filled region that contains the given 2D seed point. **Details** The seed must lie strictly inside the filled area — not on a boundary edge and not inside a hole. Throws a descriptive error if the seed is outside all regions. If unsure where regions are, use `.regions()` first — each result has `.bounds()`. **Example** ```ts const donut = circle2d(50).subtract(circle2d(30)); donut.region([40, 0]).extrude(10); // seed at radius 40, inside the ring ```
570
- - `extrude(height: number, opts?: { twist?: number; divisions?: number; scaleTop?: number | [ number, number ]; }): Shape` — Extrude this 2D sketch along Z to create a 3D solid. Supports twist and scale tapering.
571
- - `revolve(degrees?: number, segments?: number): Shape` — Revolve this 2D sketch around the Y axis to create a 3D solid of revolution.
572
- - `attachTo(target: Sketch, targetAnchor: Anchor, selfAnchor?: Anchor, offset?: [ number, number ]): Sketch` — Position this sketch relative to another using named anchor points. **Details** Computes the translation needed to align `selfAnchor` on this sketch with `targetAnchor` on the target sketch, then applies an optional pixel-exact offset. Anchor positions: `'center'` | `'top-left'` | `'top-right'` | `'bottom-left'` | `'bottom-right'` | `'top'` | `'bottom'` | `'left'` | `'right'` **Example** ```ts const arm = rect(4, 70).attachTo(plate, 'bottom-left', 'top-left'); const shifted = rect(4, 70).attachTo(plate, 'bottom-left', 'top-left', [5, 0]); ```
573
- - `onFace(parentOrFace: Shape | { toShape(): Shape; } | { _bbox(): { min: number[]; max: number[]; }; } | FaceRef, faceOrOpts?: "front" | "back" | "left" | "right" | "top" | "bottom" | string | FaceRef | { u?: number; v?: number; protrude?: number; selfAnchor?: Anchor; }, opts?: { u?: number; v?: number; protrude?: number; selfAnchor?: Anchor; }): Sketch` — Place this sketch on a face or planar target in 3D space. Use this when a 2D profile should be oriented onto a 3D face before extrusion or other downstream operations.
574
- - `labelEdge(name: string): Sketch` — Label the single boundary edge (for circles, single-loop profiles).
575
- - `labelEdges(...args: (string | null)[] | [ Record<string, string> ]): Sketch` — Label edges in winding order, or by named map for rect. Positional: `labelEdges('bottom', 'right', 'top', 'left')` — one per edge, `null` to skip. Named (rect only): `labelEdges({ bottom: 'floor', top: 'ceiling' })`.
576
- - `edgeLabels(): string[]` — List current edge label names.
577
- - `prefixLabels(prefix: string): Sketch` — Prefix all edge labels. Returns this sketch (mutates labels in place).
578
- - `renameLabel(from: string, to: string): Sketch` — Rename a single edge label.
579
- - `dropLabels(...names: string[]): Sketch` — Remove specific labels.
580
- - `dropAllLabels(): Sketch` — Remove all labels.
558
+ #### `translate()` — Move the sketch by the given X and Y offset.
581
559
 
582
- ### `ConstrainedSketchBuilder`
560
+ ```ts
561
+ translate(x: number, y?: number): Sketch
562
+ ```
583
563
 
584
- - `moveTo(x: number, y: number): this` — Move the cursor to `(x, y)` and start a new profile loop.
585
- - `lineTo(x: number, y: number): this` — Draw a line from the current cursor to `(x, y)`.
586
- - `lineH(dx: number): this` — Draw a horizontal line of length `dx` from the current cursor.
587
- - `lineV(dy: number): this` — Draw a vertical line of length `dy` from the current cursor.
588
- - `lineAngled(length: number, degrees: number): this` — Draw a line of the given `length` at `degrees` from +X.
589
- - `arcTo(x: number, y: number, radius: number, clockwise?: boolean): this` — Draw a circular arc from the current cursor to `(x, y)` with the given radius.
590
- - `arcByCenter(centerId: PointId, startId: PointId, endId: PointId, clockwise?: boolean, name?: string, fixedRadius?: boolean): ArcId` — Create an arc from an explicit center point and endpoint IDs.
591
- - `bezier(p0: any, p1: any, p2: any, p3: any, name?: string): BezierId` — Create a cubic Bezier curve from four control points.
592
- - `bezierTo(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number): this` — Draw a cubic Bezier from the current cursor to `(x3, y3)`.
593
- - `blendTo(x: number, y: number, weight?: number): this` — Draw a smooth Bezier tangent to the previous arc.
594
- - `label(name: string): this` — Label the current path segment.
595
- - `close(): this` — Close the current path and register the loop.
596
- - `addLoopCircle(center: PointId, radius: number, segments?: number): this` — Add a circle loop to the path.
597
- - `addLoop(points: any[]): this` — Add a closed polygon loop from point IDs.
598
- - `addProfileLoop(segments: Array<{ kind: "line"; line: any; } | { kind: "arc"; arc: any; } | { kind: "bezier"; bezier: any; }>): this` — Add a profile loop from prebuilt line/arc/bezier segments.
599
- - `horizontal(line: any): this` — Constrain a line to be horizontal (parallel to the X axis).
600
- - `vertical(line: any): this` — Constrain a line to be vertical (parallel to the Y axis).
601
- - `parallel(a: any, b: any): this` — Constrain two lines to be parallel.
602
- - `sameDirection(a: any, b: any): this` — Constrain two lines to point in the same direction.
603
- - `oppositeDirection(a: any, b: any): this` — Constrain two lines to point in opposite directions.
604
- - `blockRotation(points: any[], axis?: "x" | "y"): this` — Prevent 180° rotation of a polygon by anchoring its first edge.
605
- - `perpendicular(a: any, b: any): this` — Constrain two lines to be perpendicular.
606
- - `tangent(a: any, b: any): this` — Constrain a line/circle or circle/circle tangency relationship.
607
- - `equal(a: any, b: any): this` — Constrain two lines to have equal length.
608
- - `coincident(a: any, b: any): this` — Constrain two points to coincide.
609
- - `concentric(a: any, b: any): this` — Constrain two circles to share a center.
610
- - `collinear(point: any, line: any): this` — Constrain a point to lie on the infinite extension of a line.
611
- - `symmetric(a: any, b: any, axis: any): this` — Constrain two points to be symmetric about an axis line.
612
- - `fix(point: any, x?: number, y?: number): this` — Pin a point at a specific world location.
613
- - `midpoint(point: any, line: any): this` — Constrain a point to lie at the midpoint of a line.
614
- - `pointOnCircle(point: any, circle: any): this` — Constrain a point to lie on the perimeter of a circle.
615
- - `pointOnLine(point: any, line: any): this` — Constrain a point to lie on the bounded segment of a line.
616
- - `distance(a: any, b: any, value: number): this` — Constrain the Euclidean distance between two points.
617
- - `length(line: any, value: number): this` — Constrain the length of a line segment.
618
- - `angle(a: any, b: any, value: number): this` — Constrain the signed angle from line `a` to line `b`.
619
- - `radius(circle: any, value: number): this` — Constrain the radius of a circle.
620
- - `diameter(circle: any, value: number): this` — Constrain the diameter of a circle.
621
- - `hDistance(a: any, b: any, value: number): this` — Constrain the horizontal distance between two points.
622
- - `vDistance(a: any, b: any, value: number): this` — Constrain the vertical distance between two points.
623
- - `pointLineDistance(point: any, line: any, value: number): this` — Constrain the signed perpendicular distance from a point to a line.
624
- - `lineDistance(a: any, b: any, value: number): this` — Constrain the perpendicular offset distance between two lines.
625
- - `absoluteAngle(line: any, value: number): this` — Constrain the absolute angle of a line measured from +X.
626
- - `equalRadius(a: any, b: any): this` — Constrain two circles to have equal radii.
627
- - `arcLength(arc: any, value: number): this` — Constrain the arc length of an arc.
628
- - `lineTangentArc(line: any, arc: any, atStart: boolean): this` — Constrain a line to be tangent to an arc at its start or end point.
629
- - `arcTangentArc(arcA: any, arcB: any, aAtStart?: boolean, bAtStart?: boolean): this` — Constrain two arcs to be tangent at their shared junction point.
630
- - `bezierTangentArc(bezier: any, arc: any, atBezierStart: boolean, atArcStart: boolean): this` — Constrain a Bezier to be tangent to an arc at one endpoint.
631
- - `smoothBlend(arc1: any, arc2: any, options?: { weight?: number; arc1End?: "start" | "end"; arc2End?: "start" | "end"; }): BezierId` — Create a Bezier blend between two arcs.
632
- - `shapeWidth(shape: any, value: number): this` — Constrain a shape's width.
633
- - `shapeHeight(shape: any, value: number): this` — Constrain a shape's height.
634
- - `shapeCentroidX(shape: any, value: number): this` — Constrain a shape's centroid X position.
635
- - `shapeCentroidY(shape: any, value: number): this` — Constrain a shape's centroid Y position.
636
- - `shapeArea(shape: any, value: number): this` — Constrain a shape's area.
637
- - `shapeEqualCentroid(a: any, b: any): this` — Constrain two shapes to have the same centroid.
638
- - `angleBetween(a: any, b: any, value: number): this` — Constrain the unsigned angle between two lines.
639
- - `ccw(...points: any[]): this` — Constrain all given points to be in counter-clockwise order.
640
- - `offsetX(a: any, b: any, value: number): this` — Constrain the horizontal (X-axis) offset between two lines. Uses the start-point of each line to measure horizontal distance. `value` is the signed distance: b.startPt.x − a.startPt.x = value.
641
- - `offsetY(a: any, b: any, value: number): this` — Constrain the vertical (Y-axis) offset between two lines. Uses the start-point of each line to measure vertical distance. `value` is the signed distance: b.startPt.y − a.startPt.y = value.
642
- - `importPoint(pt: { x: number; y: number; }, fixed?: boolean): PointId` — Import a `Point2D` object into the sketch.
643
- - `importLine(l: { start: { x: number; y: number; }; end: { x: number; y: number; }; }, fixed?: boolean): LineId` — Import a `Line2D` object into the sketch.
644
- - `importRectangle(r: { vertices: [ { x: number; y: number; }, { x: number; y: number; }, { x: number; y: number; }, { x: number; y: number; } ]; }, fixed?: boolean): { ... }` — Import a `Rectangle2D` as four points and four lines.
645
- - `referencePoint(x: number, y: number): PointId` — Add a fixed reference point at `(x, y)`.
646
- - `referenceLine(x1: number, y1: number, x2: number, y2: number): LineId` — Add a fixed reference line from `(x1, y1)` to `(x2, y2)`.
647
- - `referenceFrom(source: ConstraintSketch, entityId: string): PointId | LineId | null` — Import a single named entity from a solved sketch as fixed reference geometry.
648
- - `referenceAllFrom(source: ConstraintSketch): { points: Map<string, PointId>; lines: Map<string, LineId>; }` — Import all non-construction entities from a solved sketch as fixed references.
649
- - `point(x?: number, y?: number, fixed?: boolean): PointId` — Add a free point to the sketch at `(x, y)`. If `x` or `y` are omitted, the point is placed at the bounding-box center of existing geometry so it starts near other entities rather than at the origin. Throws if either coordinate is `NaN` or `Infinity`.
650
- - `pointAt(index: number): PointId` — Return the `PointId` of the point created at the given insertion index.
651
- - `line(a: PointId, b: PointId, construction?: boolean, name?: string): LineId` — Connect two existing points with a line segment. Pass `construction = true` for a helper line that participates in constraints but is excluded from the solved sketch output (not part of any profile loop). **Example** (construction axis for symmetry) ```ts const axis = sk.line(sk.point(0, -50), sk.point(0, 50), true); sk.symmetric(p1, p2, axis); ```
652
- - `lineAt(index: number): LineId` — Return the `LineId` of the line created at the given insertion index.
653
- - `circle(center: PointId, radius: number, construction?: boolean, segments?: number, name?: string): CircleId` — Add a circle to the sketch with the given center point and initial radius. The radius is a starting value — if you add a `radius()` or `diameter()` constraint, the solver will adjust it. Non-construction circles automatically register a loop.
654
- - `circleAt(index: number): CircleId` — Return the `CircleId` of the circle created at the given insertion index.
655
- - `shape(lines: LineId[]): ShapeId` — Register a named shape (closed polygon) from an ordered list of line IDs. The `ShapeId` can be passed to `shapeWidth()`, `shapeHeight()`, `shapeArea()`, `shapeCentroidX()`, `shapeCentroidY()`, and `shapeEqualCentroid()` constraints. Shape registration is done automatically by concept factories like `rect()` and `addPolygon()`.
656
- - `group(opts?: { x?: number; y?: number; theta?: number; id?: string; }): SketchGroupBuilder` — Create a rigid-body group with a local coordinate frame. Points and lines added to the group move together as a unit — the solver sees 3 DOF (x, y, θ) instead of 2N per point. After configuring the group, call `.done()` to register it and receive a `SketchGroupHandle`. Group points are addressable by their `PointId` in all sketch constraints (e.g. `sk.coincident`, `sk.distance`) just like any other points. **Example** ```ts const g = sk.group({ x: 50, y: 30 }); const p0 = g.point(0, 0); // local origin → world (50, 30) const p1 = g.point(100, 0); // local (100,0) → world (150, 30) const l = g.line(p0, p1); g.fixRotation(); const handle = g.done(); // p0, p1 work in constraints like any other PointId: sk.coincident(p0, someExternalPoint); ```
657
- - `constrain(constraint: Omit<SketchConstraint, "id">): this` — Add a raw constraint object to the builder.
658
- - `solve(options?: SolveOptions): ConstraintSketch | Sketch` — Run the constraint solver and return a solved sketch. The returned `ConstraintSketch` extends `Sketch` and can be used directly in all 3D operations (`extrude`, `revolve`, etc.). It also exposes `constraintMeta` with the solver status: ```ts const result = sk.solve(); result.constraintMeta.status; // 'fully' | 'under' | 'over' | 'over-redundant' result.constraintMeta.dof; // 0 = fully constrained result.constraintMeta.maxError; // residual — should be < 1e-6 result.inspect(); // human-readable summary result.withUpdatedConstraint('cst-5', 120); // update a dimension without rebuilding ``` **Troubleshooting** - **Under-constrained (dof > 0)** — add `fix()`, `length()`, or other dimensional constraints. - **Over-constrained** — conflicting constraints are auto-rejected. Check `result.constraintMeta.constraints` and `result.inspect()`. - **maxError > 1e-6** — solver did not converge; check for contradictory constraints.
659
- - `solveConstraintsOnly(options?: SolveOptions): { maxError: number; rejectedCount: number; definition: ConstraintDefinition; }` — Run the solver without building a full `ConstraintSketch`. Lighter than `solve()` — skips profile and DOF analysis. Useful for lightweight constraint validation or progress monitoring mid-construction.
660
- - `route(x: number, y: number): RouteBuilder` — Start a directional route from coordinates. Returns a `RouteBuilder` - describe the path with up/down/left/right/arcLeft/arcRight. Each method returns the entity ID (`LineId` or `ArcId`) for use in `sk.*` constraints. ```ts const r = sk.route(0, 0); const stem = r.up(18); r.arcLeft(8.9); const neck = r.down(); r.done(); sk.offsetX(stem, neck, 10.8); ```
661
- - `rect(options?: RectOptions): ConstrainedRect` — Add an axis-aligned rectangle concept. Returns a `ConstrainedRect` handle with named vertices, sides, and center.
662
- - `addPolygon(options: PolygonOptions): ConstrainedPolygon` — Add a general polygon concept (CCW winding enforced). Returns a `ConstrainedPolygon` handle.
663
- - `regularPolygon(options: RegularPolygonOptions): ConstrainedRegularPolygon` — Add a regular n-gon concept (equal sides, CCW winding). Returns a `ConstrainedRegularPolygon` handle with a center point.
664
- - `groupRect(options: GroupRectOptions): ConstrainedGroupRect` — Add a rigid rectangle as a group concept. Returns a `ConstrainedGroupRect` handle with named vertices and sides. The rectangle is fixed in shape — only position (and optionally rotation) varies.
564
+ #### `rotate()` — Rotate the sketch around its bounding-box center.
665
565
 
666
- ### `ConstraintSketch`
566
+ ```ts
567
+ rotate(degrees: number): Sketch
568
+ ```
667
569
 
668
- **Properties:**
570
+ #### `rotateAround()` — Rotate the sketch around a specific pivot point.
669
571
 
670
- | Property | Type | Description |
671
- |----------|------|-------------|
672
- | `constraintMeta` | `SketchConstraintMeta` | — |
673
- | `definition` | `ConstraintDefinition` | — |
572
+ ```ts
573
+ rect(20, 20).rotateAround(45, [0, 0]);
574
+ ```
674
575
 
675
- **Methods:**
576
+ ```ts
577
+ rotateAround(degrees: number, pivot: [ number, number ]): Sketch
578
+ ```
676
579
 
677
- - `detectArrangement(): Sketch[]` — Enumerate all bounded regions formed by the line arrangement of this sketch. Construction lines are excluded. Regions are returned largest-first by area.
678
- - `detectArrangementRegion(seed: [ number, number ]): Sketch` — Select the single arrangement region that contains the given seed point. Throws if no region contains the seed.
679
- - `withUpdatedConstraint(constraintId: string, value: number): ConstraintSketch` — Re-solve the sketch after changing the value of one existing constraint. Use this for interactive dimension edits without rebuilding the whole sketch graph. It attempts a warm-started solve first, then falls back to a full solve if needed.
680
- - `inspect(): string` — Return a human-readable diagnostic string of the solved state.
580
+ #### `scale()` — Scale the sketch relative to its bounding-box center.
681
581
 
682
- ### `SketchGroupBuilder`
582
+ Pass a single number for uniform scaling, or `[sx, sy]` for per-axis scaling.
683
583
 
684
- - `point(lx: number, ly: number): PointId` — Add a point in local coordinates. Returns its globally-addressable PointId.
685
- - `line(a: PointId, b: PointId, name?: string): LineId` — Connect two group points with a line. Both must be PointIds from this group.
686
- - `fixRotation(): this` — Freeze rotation (theta). Group can still translate - 2 DOF remain.
687
- - `fix(): this` — Freeze all 3 DOF - group is completely fixed.
688
- - `done(): SketchGroupHandle` — Finalize and register the group with the builder.
584
+ ```ts
585
+ scale(v: number | [ number, number ]): Sketch
586
+ ```
689
587
 
690
- ### `Point2D`
588
+ #### `scaleAround()` — Scale the sketch relative to an arbitrary pivot point.
691
589
 
692
- An immutable 2D point with measurement and construction helpers.
590
+ ```ts
591
+ scaleAround(pivot: [ number, number ], v: number | [ number, number ]): Sketch
592
+ ```
693
593
 
694
- Used as construction geometry in sketches, constraints, and analytic measurements. All methods return new instances — `Point2D` is immutable.
594
+ #### `mirror()` Mirror the sketch across a line through its bounding-box center.
695
595
 
696
- **Properties:**
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.
697
597
 
698
- | Property | Type | Description |
699
- |----------|------|-------------|
700
- | `x` | `number` | — |
701
- | `y` | `number` | — |
598
+ ```ts
599
+ mirror(normal: [ number, number ]): Sketch
600
+ ```
702
601
 
703
- **Methods:**
602
+ #### `mirrorThrough()` — Mirror the sketch across a line defined by a point and a normal direction.
704
603
 
705
- - `distanceTo(other: Point2D): number` — Measure straight-line distance to another point.
706
- - `midpointTo(other: Point2D): Point2D` Compute the midpoint between this point and another point.
707
- - `translate(dx: number, dy: number): Point2D` — Return a point shifted by the given delta.
708
- - `toTuple(): [ number, number ]` — Convert this point to a plain `[x, y]` tuple.
604
+ ```ts
605
+ mirrorThrough(point: [ number, number ], normal: [ number, number ]): Sketch
606
+ ```
709
607
 
710
- ### `Line2D`
608
+ **Booleans**
711
609
 
712
- An immutable 2D line segment with length, angle, intersection, and parallel helpers.
610
+ #### `add()` Add (union) one or more sketches to this sketch.
713
611
 
714
- Provides both segment-only (`intersectSegment`) and infinite-line (`intersect`) intersection queries. All methods return new instances.
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.
715
613
 
716
- **Properties:**
614
+ ```ts
615
+ circle2d(20).add(rect(10, 30)).extrude(5);
616
+ ```
717
617
 
718
- | Property | Type | Description |
719
- |----------|------|-------------|
720
- | `start` | `Point2D` | — |
721
- | `end` | `Point2D` | — |
618
+ ```ts
619
+ add(...others: SketchOperandInput[]): Sketch
620
+ ```
722
621
 
723
- **Methods:**
622
+ #### `subtract()` — Subtract one or more sketches from this sketch.
724
623
 
725
- - `get length(): number` Length of the line segment.
726
- - `get midpoint(): Point2D` — Midpoint of the line segment.
727
- - `get angle(): number` — Direction angle in degrees, measured CCW from +X.
728
- - `get direction(): [ number, number ]` — Unit direction vector from start to end.
729
- - `parallel(distance: number): Line2D` — Create a parallel line offset by the given distance. Positive distance shifts to the left of the line direction.
730
- - `intersect(other: Line2D): Point2D | null` — Intersect this line with another infinite line.
731
- - `intersectSegment(other: Line2D): Point2D | null` — Intersect this line with another as bounded segments.
732
- - `static fromCoordinates(x1: number, y1: number, x2: number, y2: number): Line2D` — Create a line from raw coordinates.
733
- - `static fromPointAndAngle(origin: Point2D, angleDeg: number, length: number): Line2D` — Create a line from a start point, angle, and length.
734
- - `static fromPointAndDirection(origin: Point2D, dir: [ number, number ], length: number): Line2D` — Create a line from a start point, direction vector, and length.
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()`.
735
625
 
736
- ### `Circle2D`
626
+ ```ts
627
+ rect(40, 40).subtract(circle2d(10)).extrude(5);
628
+ ```
737
629
 
738
- An immutable 2D circle with area, circumference, and extrusion support.
630
+ ```ts
631
+ subtract(...others: SketchOperandInput[]): Sketch
632
+ ```
739
633
 
740
- Extruding a `Circle2D` produces a cylinder with named `top`, `bottom`, and `side` faces accessible via the topology API.
634
+ #### `intersect()` Intersect this sketch with one or more others (keep overlapping area only).
741
635
 
742
- **Properties:**
636
+ Accepts individual sketches or arrays: `sketch.intersect(a, b)` or `sketch.intersect([a, b])`. For intersecting many sketches, prefer the free function `intersection2d()`.
743
637
 
744
- | Property | Type | Description |
745
- |----------|------|-------------|
746
- | `center` | `Point2D` | — |
747
- | `radius` | `number` | — |
638
+ ```ts
639
+ intersect(...others: SketchOperandInput[]): Sketch
640
+ ```
748
641
 
749
- **Methods:**
642
+ **Features**
750
643
 
751
- - `get diameter(): number` — Diameter of the circle.
752
- - `get circumference(): number` — Circumference of the circle.
753
- - `get area(): number` — Area of the circle.
754
- - `pointAtAngle(angleDeg: number): Point2D` — Return a point on the circle at the given angle.
755
- - `translate(dx: number, dy: number): Circle2D` — Return a translated circle.
756
- - `toSketch(segments?: number): Sketch` — Convert this circle to a sketch profile.
757
- - `extrude(height: number, segments?: number): Shape` — Extrude the circle into a solid cylinder.
758
- - `static fromCenterAndRadius(center: Point2D, radius: number): Circle2D` — Create a circle from its center and radius.
759
- - `static fromDiameter(center: Point2D, diameter: number): Circle2D` — Create a circle from its center and diameter.
644
+ #### `offset()` — Inflate (positive delta) or deflate (negative delta) the sketch contour.
760
645
 
761
- ### `Rectangle2D`
646
+ Use `offset(-r).offset(+r)` to round every convex corner of a closed sketch.
762
647
 
763
- A rectangle with named sides, vertices, and extrusion support.
648
+ - `'Round'` smooth arc at each corner (default)
649
+ - `'Square'` — flat mitered extension
650
+ - `'Miter'` — sharp pointed extension
764
651
 
765
- **Details**
652
+ ```ts
653
+ rect(40, 20).offset(3); // expand by 3
654
+ rect(40, 20).offset(-2).offset(2); // round all convex corners
655
+ ```
766
656
 
767
- Sides are named based on the rectangle's local orientation at construction time. Vertices go: bottom-left, bottom-right, top-right, top-left (CCW).
657
+ ```ts
658
+ offset(delta: number, join?: "Square" | "Round" | "Miter"): Sketch
659
+ ```
768
660
 
769
- Extruding a `Rectangle2D` produces a `Shape` with named faces: `top`, `bottom`, `side-left`, `side-right`, `side-top`, `side-bottom`. These are accessible via the topology API (`.face()`, `.edge()`).
661
+ #### `regions()` Decompose this sketch into its distinct filled regions, sorted largest-first by area.
770
662
 
771
- **Example**
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.
772
664
 
773
665
  ```ts
774
- const r = rectangle(0, 0, 100, 60);
775
- r.side('top'); r.side('left'); // Line2D
776
- r.vertex('top-left'); // Point2D
777
- r.width; r.height; r.center;
778
- const [d1, d2] = r.diagonals(); // [bl-tr, br-tl]
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
+ ```
779
670
 
780
- r.toSketch(); // Sketch (for 2D operations)
781
- r.extrude(20); // Shape with named faces
671
+ ```ts
672
+ regions(): Sketch[]
673
+ ```
782
674
 
783
- Rectangle2D.fromCenterAndDimensions(point(50, 30), 100, 60);
784
- Rectangle2D.from2Corners(point(0, 0), point(100, 60));
785
- Rectangle2D.from3Points(p1, p2, p3); // free-angle rectangle
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
786
791
  ```
787
792
 
788
- - `get width(): number` — Width of the rectangle.
789
- - `get height(): number` — Height of the rectangle.
790
- - `get center(): Point2D` — Geometric center of the rectangle.
791
- - `side(name: RectSide): Line2D` — Return a named side of the rectangle.
792
- - `sideAt(index: number): Line2D` — Return a side by index.
793
- - `vertex(name: RectVertex): Point2D` — Return a named vertex of the rectangle.
794
- - `diagonals(): [ Line2D, Line2D ]` — Return the two diagonals of the rectangle.
795
- - `toSketch(): Sketch` — Convert the rectangle to a sketch profile.
796
- - `translate(dx: number, dy: number): Rectangle2D` Return a translated rectangle.
797
- - `static fromDimensions(x: number, y: number, width: number, height: number): Rectangle2D` — Create an axis-aligned rectangle from origin corner plus width and height.
798
- - `static fromCenterAndDimensions(center: Point2D, width: number, height: number): Rectangle2D` — Create a rectangle centered on a point.
799
- - `static from2Corners(p1: Point2D, p2: Point2D): Rectangle2D` — Create an axis-aligned rectangle from two opposite corners.
800
- - `static from3Points(p1: Point2D, p2: Point2D, p3: Point2D): Rectangle2D` — Create a free-angle rectangle from three points. `p1` and `p2` define one edge, and `p3` chooses the perpendicular side.
801
- - `extrude(height: number, up?: boolean): Shape` — Extrude the rectangle into a solid prism with named topology.
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
+ ```