forgecad 0.6.3 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/README.md +3 -12
  2. package/dist/assets/{AdminPage-CeqCUUgu.js → AdminPage-D4bocK4E.js} +250 -151
  3. package/dist/assets/{BlogPage-P_AJP0v9.js → BlogPage-CJEXL_zJ.js} +94 -70
  4. package/dist/assets/{DocsPage-CKRV2iq2.js → DocsPage-D3A_g8V3.js} +329 -163
  5. package/dist/assets/{EditorApp-CnC2k4cW.css → EditorApp-BWYUSpUN.css} +590 -136
  6. package/dist/assets/EditorApp-Cihhqcsq.js +11692 -0
  7. package/dist/assets/{EmbedViewer-DBlzmQ5i.js → EmbedViewer-kWjKaC_t.js} +2 -4
  8. package/dist/assets/LandingPageProofDriven-Bg2IUc3l.css +856 -0
  9. package/dist/assets/LandingPageProofDriven-DXkKlyhI.js +601 -0
  10. package/dist/assets/PricingPage-BsU5vzEx.js +232 -0
  11. package/dist/assets/{SettingsPage-BqCh9JcC.js → SettingsPage-PqvpAKIs.js} +129 -5
  12. package/dist/assets/{evalWorker-Ql-aKwLA.js → evalWorker-C-hzNUMy.js} +8949 -3161
  13. package/dist/assets/{Viewport-CoB46f5R.js → index-Pz321YAt.js} +38382 -7501
  14. package/dist/assets/{index-2hfs_ub0.css → index-ay13WNfa.css} +726 -53
  15. package/dist/assets/{javascript-DCxGoE5Y.js → javascript-DAl8Gmyo.js} +1 -1
  16. package/dist/assets/{manifold-CqNMHHKO.js → manifold-BcbjWLIo.js} +4 -3
  17. package/dist/assets/{manifold-Cce9wRFz.js → manifold-DBckbFgx.js} +1 -1
  18. package/dist/assets/{manifold-D6BeHIOo.js → manifold-O2AAGXyj.js} +1 -1
  19. package/dist/assets/{reportWorker-sFEFonXf.js → reportWorker-Dxr-5A7w.js} +8760 -3559
  20. package/dist/assets/{vendor-react-Dt7-aaJH.js → vendor-react-CG3i_wp0.js} +65 -8
  21. package/dist/docs/index.html +2 -2
  22. package/dist/docs-raw/CLI.md +341 -718
  23. package/dist/docs-raw/generated/assembly.md +699 -112
  24. package/dist/docs-raw/generated/concepts.md +1834 -1346
  25. package/dist/docs-raw/generated/core.md +1012 -1059
  26. package/dist/docs-raw/generated/curves.md +759 -116
  27. package/dist/docs-raw/generated/lib.md +43 -748
  28. package/dist/docs-raw/generated/output.md +139 -245
  29. package/dist/docs-raw/generated/sdf.md +208 -0
  30. package/dist/docs-raw/generated/sheet-metal.md +473 -21
  31. package/dist/docs-raw/generated/sketch.md +1518 -362
  32. package/dist/docs-raw/generated/viewport.md +368 -299
  33. package/dist/docs-raw/generated/wood.md +104 -0
  34. package/dist/index.html +2 -2
  35. package/dist/landing/proof-ams-adapter.png +0 -0
  36. package/dist/landing/proof-bolt-and-nut.png +0 -0
  37. package/dist/landing/proof-fillet-enclosure.png +0 -0
  38. package/dist/landing/proof-glasses.png +0 -0
  39. package/dist/landing/proof-gyroid.png +0 -0
  40. package/dist/sitemap.xml +6 -6
  41. package/dist-cli/forgecad.js +12321 -5700
  42. package/dist-cli/forgecad.js.map +1 -0
  43. package/dist-cli/solver-46FFSK2U.js +363 -0
  44. package/dist-cli/solver-46FFSK2U.js.map +1 -0
  45. package/dist-skill/CONTEXT.md +4890 -6302
  46. package/dist-skill/SKILL-dev.md +22 -66
  47. package/dist-skill/SKILL.md +20 -59
  48. package/dist-skill/docs/API/core/concepts.md +37 -92
  49. package/dist-skill/docs/CLI.md +341 -718
  50. package/dist-skill/docs/generated/assembly.md +699 -112
  51. package/dist-skill/docs/generated/core.md +1012 -1059
  52. package/dist-skill/docs/generated/curves.md +759 -116
  53. package/dist-skill/docs/generated/lib.md +43 -748
  54. package/dist-skill/docs/generated/output.md +139 -245
  55. package/dist-skill/docs/generated/sdf.md +208 -0
  56. package/dist-skill/docs/generated/sheet-metal.md +473 -21
  57. package/dist-skill/docs/generated/sketch.md +1518 -362
  58. package/dist-skill/docs/generated/viewport.md +368 -299
  59. package/dist-skill/docs/generated/wood.md +104 -0
  60. package/dist-skill/docs/guides/coordinate-system.md +11 -17
  61. package/dist-skill/docs/guides/geometry-conventions.md +13 -70
  62. package/dist-skill/docs/guides/joint-design.md +78 -0
  63. package/dist-skill/docs/guides/modeling-recipes.md +22 -195
  64. package/dist-skill/docs/guides/positioning.md +88 -147
  65. package/dist-skill/docs-dev/API/core/concepts.md +78 -0
  66. package/dist-skill/docs-dev/CLI.md +488 -0
  67. package/dist-skill/{docs → docs-dev}/blueprint-first.md +5 -0
  68. package/dist-skill/{docs → docs-dev}/coding-best-practices.md +6 -8
  69. package/dist-skill/{docs → docs-dev}/coding.md +2 -4
  70. package/dist-skill/docs-dev/component-model.md +164 -0
  71. package/dist-skill/docs-dev/generated/assembly.md +779 -0
  72. package/dist-skill/docs-dev/generated/core.md +1676 -0
  73. package/dist-skill/docs-dev/generated/curves.md +855 -0
  74. package/dist-skill/docs-dev/generated/lib.md +55 -0
  75. package/dist-skill/docs-dev/generated/output.md +234 -0
  76. package/dist-skill/docs-dev/generated/sdf.md +208 -0
  77. package/dist-skill/docs-dev/generated/sheet-metal.md +506 -0
  78. package/dist-skill/docs-dev/generated/sketch.md +1753 -0
  79. package/dist-skill/docs-dev/generated/viewport.md +513 -0
  80. package/dist-skill/docs-dev/generated/wood.md +104 -0
  81. package/dist-skill/docs-dev/guides/coordinate-system.md +46 -0
  82. package/dist-skill/docs-dev/guides/geometry-conventions.md +52 -0
  83. package/dist-skill/docs-dev/guides/joint-design.md +78 -0
  84. package/dist-skill/docs-dev/guides/modeling-recipes.md +77 -0
  85. package/dist-skill/docs-dev/guides/positioning.md +151 -0
  86. package/dist-skill/{docs → docs-dev}/guides/skill-maintenance.md +21 -10
  87. package/dist-skill/{docs → docs-dev}/internals/compiler.md +5 -6
  88. package/dist-skill/{docs → docs-dev}/internals/constraint-solver-quality.md +0 -1
  89. package/dist-skill/{docs → docs-dev}/internals/constraint-solver.md +0 -1
  90. package/dist-skill/{docs → docs-dev}/internals/sketch-2d-pipeline.md +2 -3
  91. package/examples/api/attachTo-basics.forge.js +8 -8
  92. package/examples/api/bill-of-materials.forge.js +9 -9
  93. package/examples/api/bolt-pattern.forge.js +5 -5
  94. package/examples/api/boolean-operations.forge.js +5 -5
  95. package/examples/api/bounding-box-visualizer.forge.js +3 -3
  96. package/examples/api/clone-duplicate.forge.js +2 -2
  97. package/examples/api/colors-union-vs-array.forge.js +6 -6
  98. package/examples/api/connector-assembly.forge.js +8 -6
  99. package/examples/api/connector-basics.forge.js +7 -7
  100. package/examples/api/constrained-sketch-mechanical.forge.js +4 -4
  101. package/examples/api/elbow-test.forge.js +3 -3
  102. package/examples/api/extrude-options.forge.js +8 -14
  103. package/examples/api/feature-created-faces.forge.js +6 -10
  104. package/examples/api/fillet-showcase.forge.js +2 -2
  105. package/examples/api/folded-service-panel-cover.forge.js +2 -2
  106. package/examples/api/gears-tier1.forge.js +5 -5
  107. package/examples/api/group-test.forge.js +3 -3
  108. package/examples/api/group-vs-union.forge.js +1 -1
  109. package/examples/api/highlight-debug.forge.js +4 -0
  110. package/examples/api/js-module-pillars.js +1 -1
  111. package/examples/api/js-module-scene.js +2 -2
  112. package/examples/api/mesh-import-slats.forge.js +4 -4
  113. package/examples/api/patterns.forge.js +3 -3
  114. package/examples/api/pointAlong-orientation.forge.js +3 -3
  115. package/examples/api/profile-2020-b-slot6.forge.js +4 -5
  116. package/examples/api/route-perimeter-flange.forge.js +1 -1
  117. package/examples/api/sdf-rover-demo.forge.js +10 -10
  118. package/examples/api/sketch-on-face-demo.forge.js +2 -2
  119. package/examples/api/sketch-regions.forge.js +4 -4
  120. package/examples/api/sketch-rounding-strategies.forge.js +1 -1
  121. package/examples/api/smooth-curve-connections.forge.js +1 -1
  122. package/examples/api/transition-curves.forge.js +4 -4
  123. package/examples/api/variable-sweep-pure-sdf-test.forge.js +162 -0
  124. package/examples/api/variable-sweep-test.forge.js +2 -2
  125. package/examples/api/wood-joinery.forge.js +60 -0
  126. package/examples/compiler-corpus/enclosure-shell-cuts.forge.js +3 -3
  127. package/examples/compiler-corpus/fastener-plate-variants.forge.js +2 -2
  128. package/examples/constraints/01-fully-constrained-rect.forge.js +2 -2
  129. package/examples/constraints/02-underconstrained-triangle.forge.js +1 -1
  130. package/examples/constraints/03-redundant-constraints.forge.js +2 -2
  131. package/examples/constraints/05-parallel-with-linedistance.forge.js +2 -2
  132. package/examples/constraints/06-complex-spectrogram.forge.js +1 -1
  133. package/examples/constraints/07-perpendicular-chain.forge.js +4 -4
  134. package/examples/constraints/08-symmetric-bracket.forge.js +4 -4
  135. package/examples/constraints/09-stress-spiral.forge.js +1 -1
  136. package/examples/constraints/10-stress-honeycomb.forge.js +1 -1
  137. package/examples/constraints/11-surface-grid.forge.js +2 -2
  138. package/examples/constraints/12-surface-nested.forge.js +4 -4
  139. package/examples/constraints/13-surface-complex.forge.js +1 -1
  140. package/examples/exact-arc-housing.forge.js +12 -0
  141. package/examples/experiments/drone-arm.forge.js +53 -0
  142. package/examples/furniture/adjustable-table.forge.js +15 -15
  143. package/examples/furniture/bathroom.forge.js +26 -26
  144. package/examples/furniture/chair.forge.js +13 -13
  145. package/examples/furniture/picture-frame.forge.js +6 -6
  146. package/examples/furniture/shoe-rack-doors.forge.js +8 -8
  147. package/examples/furniture/shoe-rack.forge.js +7 -7
  148. package/examples/furniture/table-lamp.forge.js +8 -8
  149. package/examples/gcode/lissajous-vase.forge.js +4 -4
  150. package/examples/gcode/math-surface.forge.js +3 -3
  151. package/examples/gcode/parametric-vase.forge.js +4 -4
  152. package/examples/gcode/spiral-tower.forge.js +4 -4
  153. package/examples/generative/crystal-growth.forge.js +9 -9
  154. package/examples/generative/frost-spires.forge.js +9 -9
  155. package/examples/generative/golden-spiral-tower.forge.js +11 -11
  156. package/examples/generative/molten-forge.forge.js +6 -6
  157. package/examples/generative/neon-coral.forge.js +7 -7
  158. package/examples/mechanical/3d-printer.forge.js +37 -37
  159. package/examples/mechanical/5-finger-robot-hand.forge.js +19 -19
  160. package/examples/mechanical/airplane-propeller.forge.js +9 -9
  161. package/examples/mechanical/bolt-and-nut.forge.js +10 -10
  162. package/examples/mechanical/door-with-hinges.forge.js +7 -7
  163. package/examples/mechanical/fillet-enclosure.forge.js +15 -11
  164. package/examples/mechanical/headphone-hanger-v2.forge.js +11 -11
  165. package/examples/mechanical/robot_hand.forge.js +24 -24
  166. package/examples/mechanical/robot_hand_2.forge.js +26 -26
  167. package/examples/nurbs-surface.forge.js +8 -0
  168. package/examples/nurbs-tube.forge.js +7 -0
  169. package/examples/products/bottle.forge.js +8 -8
  170. package/examples/products/chess-set.forge.js +25 -25
  171. package/examples/products/classical-piano.forge.js +20 -20
  172. package/examples/products/clock.forge.js +33 -33
  173. package/examples/products/cup.forge.js +5 -5
  174. package/examples/products/iphone.forge.js +20 -20
  175. package/examples/products/laptop.forge.js +24 -24
  176. package/examples/products/laser-cut-box.forge.js +6 -6
  177. package/examples/products/laser-cut-tray.forge.js +6 -6
  178. package/examples/products/liquid-soap-dispenser.forge.js +23 -23
  179. package/examples/products/origami-fish.forge.js +14 -12
  180. package/examples/products/spiderman-cake.forge.js +6 -6
  181. package/examples/shelf/container.forge.js +5 -5
  182. package/examples/shelf/shelf-unit.forge.js +6 -6
  183. package/examples/toolbox/bolted-joint.forge.js +7 -7
  184. package/package.json +9 -4
  185. package/dist/assets/EditorApp-B-vQvgam.js +0 -9888
  186. package/dist/assets/LandingPage-C5n9hDXI.js +0 -322
  187. package/dist/assets/PublishedModelPage-Dt7PCVBj.js +0 -146
  188. package/dist/assets/__vite-browser-external-CURh0WXD.js +0 -8
  189. package/dist/assets/deserializeRunResult-BLAFoiE0.js +0 -19365
  190. package/dist/assets/index-1CYp3zUp.js +0 -1455
  191. package/dist-skill/docs/API/API.md +0 -1666
  192. package/dist-skill/docs/API/README.md +0 -37
  193. package/dist-skill/docs/API/assembly/assembly.md +0 -617
  194. package/dist-skill/docs/API/core/edge-queries.md +0 -130
  195. package/dist-skill/docs/API/core/parameters.md +0 -122
  196. package/dist-skill/docs/API/core/reserved-terms.md +0 -137
  197. package/dist-skill/docs/API/core/sdf.md +0 -326
  198. package/dist-skill/docs/API/core/skill-cli.md +0 -194
  199. package/dist-skill/docs/API/core/skill-guide.md +0 -205
  200. package/dist-skill/docs/API/core/specs.md +0 -186
  201. package/dist-skill/docs/API/core/topology.md +0 -372
  202. package/dist-skill/docs/API/entities.md +0 -268
  203. package/dist-skill/docs/API/output/bom.md +0 -58
  204. package/dist-skill/docs/API/output/brep-export.md +0 -87
  205. package/dist-skill/docs/API/output/dimensions.md +0 -67
  206. package/dist-skill/docs/API/output/export.md +0 -110
  207. package/dist-skill/docs/API/output/gcode.md +0 -195
  208. package/dist-skill/docs/API/runtime/viewport.md +0 -420
  209. package/dist-skill/docs/API/sheet-metal/sheet-metal.md +0 -185
  210. package/dist-skill/docs/API/sketch/anchor.md +0 -37
  211. package/dist-skill/docs/API/sketch/booleans.md +0 -91
  212. package/dist-skill/docs/API/sketch/core.md +0 -73
  213. package/dist-skill/docs/API/sketch/extrude.md +0 -62
  214. package/dist-skill/docs/API/sketch/on-face.md +0 -104
  215. package/dist-skill/docs/API/sketch/operations.md +0 -78
  216. package/dist-skill/docs/API/sketch/path.md +0 -75
  217. package/dist-skill/docs/API/sketch/primitives.md +0 -146
  218. package/dist-skill/docs/API/sketch/regions.md +0 -80
  219. package/dist-skill/docs/API/sketch/text.md +0 -108
  220. package/dist-skill/docs/API/sketch/transforms.md +0 -65
  221. package/dist-skill/docs/API/toolbox/fasteners.md +0 -129
  222. package/dist-skill/docs/INDEX.md +0 -94
  223. package/dist-skill/docs/RELEASING.md +0 -55
  224. package/dist-skill/docs/cli-monetization.md +0 -111
  225. package/dist-skill/docs/deployment.md +0 -281
  226. package/dist-skill/docs/generated/concepts.md +0 -2112
  227. package/dist-skill/docs/internals/shape-from-slices.md +0 -152
  228. package/dist-skill/docs/platform/admin.md +0 -45
  229. package/dist-skill/docs/platform/architecture.md +0 -79
  230. package/dist-skill/docs/platform/auth.md +0 -110
  231. package/dist-skill/docs/platform/email.md +0 -67
  232. package/dist-skill/docs/platform/projects.md +0 -111
  233. package/dist-skill/docs/platform/sharing.md +0 -90
  234. package/dist-skill/docs/runbook.md +0 -345
@@ -5,39 +5,260 @@ skill-order: 100
5
5
 
6
6
  # Sheet Metal
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
  Folded sheet metal parts with flanges, bends, and flat pattern unfolding.
11
9
 
10
+ ## Contents
11
+
12
+ - [Sheet Metal](#sheet-metal) — `sheetMetal`
13
+ - [Laser Cutting](#laser-cutting) — `kerfCompensateOutline`, `kerfCompensateTabs`, `kerfCompensateSlots`, `kerfCompensatePart`, `lookupKerf`, `flatPanel`, `flatPart`, `fingerJoint`, `tabSlot`, `assemblyPreview`, `assemblyInstructions`, `formatInstructions`, `laserKit`
14
+ - [SheetMetalPart](#sheetmetalpart)
15
+ - [FlatPart](#flatpart)
16
+ - [LaserKit](#laserkit)
17
+ - [SHEET_METAL_EDGES](#sheet-metal-edges)
18
+ - [COMMON_KERFS](#common-kerfs)
19
+
12
20
  ## Functions
13
21
 
14
22
  ### Sheet Metal
15
23
 
16
- Create folded sheet metal parts with flanges and flat patterns.
24
+ #### `sheetMetal()` — Create a parametric sheet metal part with flanges, bend allowances, and flat-pattern unfolding.
25
+
26
+ `sheetMetal()` keeps one semantic model and derives both a folded 3D solid and an accurate flat pattern from it. The K-factor bend allowance is applied during unfolding. This is a strict v1 subset — it does not infer sheet metal from arbitrary solids.
17
27
 
18
- #### `sheetMetal()`
28
+ **Recommended authoring order:**
29
+
30
+ 1. Define the base panel + thickness + bend parameters.
31
+ 2. Chain `.flange()` calls for each edge. Validate with `.folded()` and `.flatPattern()` before adding cutouts.
32
+ 3. Add panel cutouts, then flange cutouts one region at a time.
33
+ 4. Validate after each new cutout region.
34
+
35
+ **v1 limitations:** one base panel, up to four 90° edge flanges, constant thickness, explicit K-factor, rectangular corner reliefs, planar cutouts only. No hems, jogs, lofted bends, non-90° flanges, or bend-region cutouts.
36
+
37
+ ```ts
38
+ const cover = sheetMetal({
39
+ panel: { width: 180, height: 110 },
40
+ thickness: 1.5,
41
+ bendRadius: 2,
42
+ bendAllowance: { kFactor: 0.42 },
43
+ cornerRelief: { size: 4 },
44
+ })
45
+ .flange('top', { length: 18 })
46
+ .flange('right', { length: 18 })
47
+ .flange('bottom', { length: 18 })
48
+ .flange('left', { length: 18 })
49
+ .cutout('panel', rect(72, 36, true), { selfAnchor: 'center' })
50
+ .cutout('flange-right', roundedRect(26, 10, 5, true), { selfAnchor: 'center' });
51
+
52
+ const folded = cover.folded();
53
+ const flat = cover.flatPattern();
54
+ ```
19
55
 
20
56
  ```ts
21
57
  sheetMetal(options: SheetMetalOptions): SheetMetalPart
22
58
  ```
23
59
 
24
- Create a sheet-metal part with flanges, bend allowances, and flat pattern unfolding. Define the base panel, thickness, bend radius, and K-factor, then chain .flange() and .cutout() calls. Materialize with .folded() or .flatPattern().
60
+ **`SheetMetalOptions`**
61
+
62
+ | Option | Type | Description |
63
+ |--------|------|-------------|
64
+ | `width` | `number` | Width of the panel along the X axis. |
65
+ | `height` | `number` | Height of the panel along the Y axis. |
66
+ | `thickness` | `number` | Sheet thickness in mm. Applied uniformly across the panel and all flanges. |
67
+ | `bendRadius` | `number` | Inside bend radius in mm. Must be ≥ 0. Typically 0.5–2× the sheet thickness. |
68
+ | `kFactor` | `number` | K-factor (neutral-axis offset, 0–1). |
69
+ | `kind?` | `"rect"` | Relief shape — only `'rect'` is supported in v1. |
70
+ | `size` | `number` | Side length of the square relief cut in mm. |
71
+
72
+ ### Laser Cutting
73
+
74
+ #### `kerfCompensateOutline()` — Apply kerf compensation to a complete part outline (outer boundary + holes).
75
+
76
+ Offsets inward by half-kerf: the outer boundary shrinks and inner holes grow. This is correct because the laser beam removes material on both sides of the cut line.
77
+
78
+ ```ts
79
+ kerfCompensateOutline(sketch: Sketch, kerf: number): Sketch
80
+ ```
81
+
82
+ #### `kerfCompensateTabs()` — Apply kerf compensation to joint protrusions (tabs, fingers).
83
+
84
+ These grow by half-kerf so they are slightly oversized and fit tightly in their mating slots after the laser removes material.
85
+
86
+ ```ts
87
+ kerfCompensateTabs(sketch: Sketch, kerf: number): Sketch
88
+ ```
89
+
90
+ #### `kerfCompensateSlots()` — Apply kerf compensation to joint cutouts (slots, holes that receive tabs).
91
+
92
+ These grow by half-kerf so tabs can fit into them after the laser removes material from both sides of the slot walls.
93
+
94
+ ```ts
95
+ kerfCompensateSlots(sketch: Sketch, kerf: number): Sketch
96
+ ```
97
+
98
+ #### `kerfCompensatePart()` — Build a kerf-compensated part profile.
99
+
100
+ 1. Start with the base profile.
101
+ 2. Kerf-compensate each tab addition (grow by kerf/2), then union with base.
102
+ 3. Kerf-compensate each slot subtraction (grow by kerf/2), then subtract from base.
103
+ 4. Kerf-compensate the resulting outline (shrink by kerf/2).
104
+
105
+ Order matters: joints modify geometry BEFORE outline compensation so the final inward offset applies uniformly to the assembled profile.
106
+
107
+ ```ts
108
+ kerfCompensatePart(baseProfile: Sketch, joints: PartJoints, kerf: number): Sketch
109
+ ```
110
+
111
+ **`PartJoints`**
112
+ - `additions?: Sketch[]` — Geometry to ADD to the base profile (tabs, fingers protruding from edges).
113
+ - `subtractions?: Sketch[]` — Geometry to SUBTRACT from the base profile (slots, holes for mating tabs).
114
+
115
+ #### `lookupKerf()` — Look up kerf for a material + thickness + laser combo.
116
+
117
+ If `laserType` is omitted, returns the first matching material + thickness entry. Returns `undefined` when no match is found.
118
+
119
+ ```ts
120
+ lookupKerf(material: string, thickness: number, laserType?: string): number | undefined
121
+ ```
122
+
123
+ #### `flatPanel()` — Create a rectangular flat panel with 4 named edges.
124
+
125
+ Profile origin at bottom-left corner. Edges: bottom (y=0), right (x=width), top (y=height), left (x=0). Edge traversal follows CCW winding order.
126
+
127
+ ```ts
128
+ flatPanel(name: string, width: number, height: number, thickness: number, options?: FlatPartOptions): FlatPart
129
+ ```
130
+
131
+ `FlatPartOptions`: `{ material?: string, qty?: number, color?: string }`
132
+
133
+ #### `flatPart()` — Create a flat part from an arbitrary profile with user-named edges.
134
+
135
+ Edge normals are computed automatically (perpendicular to direction, rotated 90deg CW).
136
+
137
+ ```ts
138
+ flatPart(name: string, profile: Sketch, thickness: number, edges?: Record<string, { start: [ number, number ]; end: [ number, number ]; }>, options?: FlatPartOptions): FlatPart
139
+ ```
140
+
141
+ #### `fingerJoint()` — Connect two parts with finger joints along specified edges.
142
+
143
+ Adds finger geometry to partA's edge, cuts matching slots from partB's edge. The joint profiles are positioned along each edge using rotation + translation.
144
+
145
+ ```ts
146
+ fingerJoint(partA: FlatPart, edgeNameA: string, partB: FlatPart, edgeNameB: string, options?: FingerJointOptions & { foldAngle?: number; }): void
147
+ ```
148
+
149
+ **`FingerJointOptions`**
150
+
151
+ | Option | Type | Description |
152
+ |--------|------|-------------|
153
+ | `fingers?` | `number` | Explicit finger count (must be odd, >= 3). Default: auto from length/thickness. |
154
+ | `fingerWidth?` | `number` | Explicit finger width. Default: auto. |
155
+ | `clearance?` | `number` | Extra clearance per side (mm). Default: 0. |
156
+ | `kerf?` | `number` | Laser kerf (mm). Default: 0. |
157
+ | `endStyle?` | `"full" | "half"` | Whether edge starts with full finger or half. Default: 'full'. |
158
+
159
+ #### `tabSlot()` — Connect two parts with tab-and-slot joints along specified edges.
160
+
161
+ Adds tab geometry to partA's edge, cuts matching slots from partB's edge.
162
+
163
+ ```ts
164
+ tabSlot(partA: FlatPart, edgeNameA: string, partB: FlatPart, edgeNameB: string, options?: TabSlotOptions & { foldAngle?: number; }): void
165
+ ```
166
+
167
+ **`TabSlotOptions`**
168
+
169
+ | Option | Type | Description |
170
+ |--------|------|-------------|
171
+ | `tabCount?` | `number` | Number of tabs. Default: auto (length / (4 * thickness)). |
172
+ | `tabWidth?` | `number` | Tab width. Default: 2 * thickness. |
173
+ | `clearance?` | `number` | Extra clearance per side (mm). Default: 0. |
174
+ | `kerf?` | `number` | Laser kerf (mm). Default: 0. |
175
+ | `inset?` | `number` | Distance from panel edges to first/last tab center. Default: thickness. |
176
+
177
+ #### `assemblyPreview()` — Generate a 3D assembly preview from flat parts and their joint records.
178
+
179
+ The preview can fold joints partially or fully and optionally apply exploded spacing so part relationships are easier to inspect visually.
180
+
181
+ ```ts
182
+ assemblyPreview(parts: FlatPart[], joints: JointRecord[], options?: AssemblyPreviewOptions): AssemblyPreviewResult
183
+ ```
184
+
185
+ **`JointRecord`**
186
+ - `foldAngle: number` — Fold angle in degrees. Default: 90.
187
+ - Also: `type: "finger" | "tabSlot" | "snapFit", partA: string, partB: string, edgeA: string, edgeB: string`
188
+
189
+ **`AssemblyPreviewOptions`**
190
+ - `kerf?: number` — Kerf compensation passed to each part's solid(). Default: 0
191
+ - `fold?: number` — Fold amount: 0 = flat layout, 1 = fully assembled. Default: 1
192
+ - `explode?: number` — Explode distance: 0 = assembled, >0 = parts spread outward. Default: 0
193
+
194
+ **`AssemblyPreviewResult`**
195
+ - `shapes: ShapeGroup` — All part shapes grouped for display.
196
+ - `partShapes: Map<string, Shape>` — Individual transformed shapes keyed by part name.
197
+
198
+ #### `assemblyInstructions()` — Generate step-by-step assembly instructions from flat parts and joints.
199
+
200
+ Algorithm:
201
+
202
+ 1. Build adjacency graph from joints
203
+ 2. Pick root part (most connections, or user-specified)
204
+ 3. BFS from root, creating one step per part addition
205
+ 4. Each step describes: which part to add, where it connects, how to orient it
206
+
207
+ Heuristics for step ordering:
208
+
209
+ - Start with the part that has the most connections (the base)
210
+ - Add parts that connect to already-assembled parts first (BFS order)
211
+ - Among candidates at the same BFS depth, prefer parts with more connections to already-assembled parts (structurally stable)
212
+
213
+ ```ts
214
+ assemblyInstructions(parts: FlatPart[], joints: JointRecord[], options?: AssemblyInstructionsOptions): AssemblyInstructionsResult
215
+ ```
216
+
217
+ **`AssemblyInstructionsOptions`**
218
+ - `rootPart?: string` — Part to start from. Default: part with most joint connections.
219
+
220
+ **`AssemblyInstructionsResult`**
221
+ - `totalParts: number` — Total number of parts in the assembly.
222
+ - `orphanParts: string[]` — Parts not connected to the joint graph (orphans).
223
+ - Also: `steps: AssemblyStep[]`
224
+
225
+ **`AssemblyStep`**
226
+
227
+ | Option | Type | Description |
228
+ |--------|------|-------------|
229
+ | `stepNumber` | `number` | 1-based step number. |
230
+ | `description` | `string` | Human-readable instruction. |
231
+ | `partName` | `string` | The part being added in this step. |
232
+ | `partNumber` | `number` | Part number (for cross-ref with cut sheets). |
233
+ | `connectsTo` | `string` | Which existing part it connects to. |
234
+ | `jointType` | `"finger" | "tabSlot" | "snapFit"` | Joint type used. |
235
+ | `newPartEdge` | `string` | The edge on the new part. |
236
+ | `existingPartEdge` | `string` | The edge on the existing part. |
237
+ | `foldAngle` | `number` | Fold angle in degrees. |
238
+ | `assembledParts` | `string[]` | Part names in the assembly so far (after this step). |
239
+
240
+ #### `formatInstructions()` — Format assembly instructions as a human-readable text document.
241
+
242
+ Includes a "Step 0" preamble identifying the base part, followed by numbered steps, and a note about any orphan parts.
243
+
244
+ ```ts
245
+ formatInstructions(result: AssemblyInstructionsResult): string
246
+ ```
25
247
 
26
- <details><summary><code>SheetMetalOptions</code></summary>
248
+ #### `laserKit()` — Top-level factory for creating a LaserKit container.
27
249
 
28
250
  ```ts
29
- interface SheetMetalOptions {
30
- width: number;
31
- height: number;
32
- thickness: number;
33
- bendRadius: number;
34
- kFactor: number;
35
- kind?: "rect";
36
- size: number;
37
- }
251
+ laserKit(options?: LaserKitOptions): LaserKit
38
252
  ```
39
253
 
40
- </details>
254
+ **`LaserKitOptions`**
255
+
256
+ | Option | Type | Description |
257
+ |--------|------|-------------|
258
+ | `material?` | `string` | Default material label for parts that don't specify one. |
259
+ | `sheetWidth?` | `number` | Stock sheet width in mm (default 600). |
260
+ | `sheetHeight?` | `number` | Stock sheet height in mm (default 400). |
261
+ | `kerf?` | `number` | Laser kerf in mm (default 0.2). |
41
262
 
42
263
  ---
43
264
 
@@ -45,10 +266,241 @@ interface SheetMetalOptions {
45
266
 
46
267
  ### `SheetMetalPart`
47
268
 
269
+ An immutable sheet metal part that accumulates flanges and cutouts.
270
+
271
+ Each mutating method returns a **new** `SheetMetalPart`; the original is unchanged. The part does not produce geometry until you call `.folded()` or `.flatPattern()`.
272
+
273
+ #### `flange()` — Add a 90° flange along one edge of the base panel.
274
+
275
+ Each of the four edges (`'top'`, `'right'`, `'bottom'`, `'left'`) may carry at most one flange. Calling `.flange()` twice for the same edge throws.
276
+
277
+ Corner reliefs are automatically inserted at the intersections of adjacent flanges. Build flanges before cutouts — validate with `.folded()` and `.flatPattern()` after each addition.
278
+
279
+ ```ts
280
+ const part = sheetMetal({ panel: { width: 100, height: 60 }, thickness: 1.5, bendRadius: 2, bendAllowance: { kFactor: 0.42 } })
281
+ .flange('top', { length: 15 })
282
+ .flange('bottom', { length: 15 });
283
+ ```
284
+
285
+ ```ts
286
+ flange(edge: SheetMetalEdge, options: SheetMetalFlangeOptions): SheetMetalPart
287
+ ```
288
+
289
+ #### `cutout()` — Subtract a 2D sketch cutout from a planar region of the sheet metal part.
290
+
291
+ `region` must be `'panel'` or one of `'flange-top'`, `'flange-right'`, `'flange-bottom'`, `'flange-left'` (only available once the corresponding flange has been added). Cutouts inside bend regions are **not** supported in v1.
292
+
293
+ `sketch` must be an **unplaced** compile-covered 2D profile (e.g. the result of [`circle2d()`](/docs/sketch#circle2d), [`rect()`](/docs/sketch#rect), [`roundedRect()`](/docs/sketch#roundedrect)). Passing an already-placed sketch (one that has had `.onFace(...)` called on it) will throw.
294
+
295
+ **Authoring order:** Add all flanges before adding cutouts. Add panel cutouts before flange cutouts. Add one region at a time and validate with `.folded()` / `.flatPattern()` after each step.
296
+
297
+ ```ts
298
+ const part = sheetMetal({ panel: { width: 180, height: 110 }, thickness: 1.5, bendRadius: 2, bendAllowance: { kFactor: 0.42 } })
299
+ .flange('top', { length: 18 })
300
+ .cutout('panel', rect(72, 36, true), { selfAnchor: 'center' })
301
+ .cutout('flange-top', roundedRect(26, 10, 5, true), { selfAnchor: 'center' });
302
+ ```
303
+
304
+ ```ts
305
+ cutout(region: SheetMetalPlanarRegionName, sketch: Sketch, options?: SheetMetalCutoutOptions): SheetMetalPart
306
+ ```
307
+
308
+ #### `regionNames()` — Return all semantic region names currently available on this part.
309
+
310
+ The returned list always includes `'panel'`. For every flange that has been added, the list also includes the corresponding `'flange-<edge>'` and `'bend-<edge>'` entries.
311
+
312
+ Use this to discover valid targets for `.cutout()` or for querying faces by region after materializing with `.folded()`.
313
+
314
+ Defended region names: `panel` | `flange-top` | `flange-right` | `flange-bottom` | `flange-left` | `bend-top` | `bend-right` | `bend-bottom` | `bend-left`
315
+
316
+ ```ts
317
+ regionNames(): SheetMetalRegionName[]
318
+ ```
319
+
320
+ #### `folded()` — Materialize the 3D folded solid.
321
+
322
+ Applies all flanges (bent up at their configured angles) and all registered cutouts, then returns the resulting [`Shape`](/docs/core#shape). The shape is compiler-owned and exact-exportable (STEP, IGES, etc.).
323
+
324
+ Prefer calling `.folded()` to validate each build step before proceeding to the final model.
325
+
326
+ ```ts
327
+ folded(): Shape
328
+ ```
329
+
330
+ #### `flatPattern()` — Materialize the flat-pattern (unfolded blank) for fabrication.
331
+
332
+ Unfolds all flanges using the K-factor bend allowance and lays the result flat in the XY plane. Cutouts are projected into the flat geometry. The returned shape is exact-exportable and ready for laser / waterjet / CNC nesting workflows.
333
+
334
+ The developed length of each bend zone is: `BA = (bendRadius + kFactor × thickness) × angleDeg × π / 180`
335
+
336
+ ```ts
337
+ flatPattern(): Shape
338
+ ```
339
+
340
+ ### `FlatPart`
341
+
342
+ **Properties:**
343
+
344
+ | Property | Type | Description |
345
+ |----------|------|-------------|
346
+ | `name` | `string` | — |
347
+ | `thickness` | `number` | — |
348
+ | `options` | `FlatPartOptions` | — |
349
+
48
350
  **Methods:**
49
351
 
50
- - `flange()` — flange(edge: SheetMetalEdge, options: SheetMetalFlangeOptions): SheetMetalPart
51
- - `cutout()` — cutout(region: SheetMetalPlanarRegionName, sketch: Sketch, options?: SheetMetalC
52
- - `regionNames()` — regionNames(): SheetMetalRegionName[]
53
- - `folded()` folded(): Shape
54
- - `flatPattern()` — flatPattern(): Shape
352
+ #### `edges()` — All edges as a read-only map.
353
+
354
+ ```ts
355
+ get edges(): ReadonlyMap<string, EdgeInfo>
356
+ ```
357
+
358
+ #### `edge()` — Look up a named edge. Throws if the edge does not exist.
359
+
360
+ ```ts
361
+ edge(name: string): EdgeInfo
362
+ ```
363
+
364
+ #### `edgeNames()` — All edge names on this part.
365
+
366
+ ```ts
367
+ edgeNames(): string[]
368
+ ```
369
+
370
+ #### `partNumber()` — BOM part number assigned to this flat part.
371
+
372
+ ```ts
373
+ get partNumber(): number
374
+ ```
375
+
376
+ #### `joints()` — Joint records that attach this part to other parts in the kit.
377
+
378
+ ```ts
379
+ get joints(): readonly JointRecord[]
380
+ ```
381
+
382
+ #### `quantity()` — Requested quantity of this part in the kit. Defaults to `1`.
383
+
384
+ ```ts
385
+ get quantity(): number
386
+ ```
387
+
388
+ #### `addGeometry()` — Add geometry (e.g. protruding tabs) to the part profile.
389
+
390
+ ```ts
391
+ addGeometry(sketch: Sketch): void
392
+ ```
393
+
394
+ #### `subtractGeometry()` — Subtract geometry (e.g. slot cuts) from the part profile.
395
+
396
+ ```ts
397
+ subtractGeometry(sketch: Sketch): void
398
+ ```
399
+
400
+ #### `addJoint()` — Record a joint connection for assembly preview.
401
+
402
+ ```ts
403
+ addJoint(record: JointRecord): void
404
+ ```
405
+
406
+ #### `profile()` — Final 2D profile with joints and optional kerf compensation.
407
+
408
+ ```ts
409
+ profile(kerf?: number): Sketch
410
+ ```
411
+
412
+ #### `solid()` — 3D solid — extrude the profile by material thickness.
413
+
414
+ ```ts
415
+ solid(kerf?: number): Shape
416
+ ```
417
+
418
+ ### `LaserKit`
419
+
420
+ #### `kerf()` — Laser kerf in mm.
421
+
422
+ ```ts
423
+ get kerf(): number
424
+ ```
425
+
426
+ #### `parts()` — All registered parts (flat, in insertion order).
427
+
428
+ ```ts
429
+ get parts(): readonly FlatPart[]
430
+ ```
431
+
432
+ #### `material()` — Default material label.
433
+
434
+ ```ts
435
+ get material(): string
436
+ ```
437
+
438
+ #### `sheetWidth()` — Stock sheet width in mm.
439
+
440
+ ```ts
441
+ get sheetWidth(): number
442
+ ```
443
+
444
+ #### `sheetHeight()` — Stock sheet height in mm.
445
+
446
+ ```ts
447
+ get sheetHeight(): number
448
+ ```
449
+
450
+ #### `addPart()` — Register a flat part with this kit. Assigns a sequential part number and records the quantity.
451
+
452
+ ```ts
453
+ addPart(part: FlatPart, overrides?: { qty?: number; }): this
454
+ ```
455
+
456
+ #### `cutSheets()` — Generate nested cut sheets using guillotine bin-packing.
457
+
458
+ ```ts
459
+ cutSheets(): CuttingLayoutResult
460
+ ```
461
+
462
+ #### [`bom()`](/docs/output#bom) — Bill of materials listing every part with dimensions.
463
+
464
+ ```ts
465
+ bom(): LaserKitBomEntry[]
466
+ ```
467
+
468
+ #### `partSvgs()` — Individual SVG string for each part profile, keyed by part name.
469
+
470
+ ```ts
471
+ partSvgs(): Map<string, string>
472
+ ```
473
+
474
+ #### `inventorySvg()` — Combined inventory SVG showing all parts in a labeled grid.
475
+
476
+ ```ts
477
+ inventorySvg(): string
478
+ ```
479
+
480
+ #### `assemblyPreview()` — 3D fold-up preview of the assembled kit.
481
+
482
+ ```ts
483
+ assemblyPreview(options?: Omit<AssemblyPreviewOptions, "kerf">): AssemblyPreviewResult
484
+ ```
485
+
486
+ #### `assemblyInstructions()` — Step-by-step assembly instructions.
487
+
488
+ ```ts
489
+ assemblyInstructions(options?: AssemblyInstructionsOptions): AssemblyInstructionsResult
490
+ ```
491
+
492
+ #### `formatInstructions()` — Human-readable assembly instructions text.
493
+
494
+ ```ts
495
+ formatInstructions(options?: AssemblyInstructionsOptions): string
496
+ ```
497
+
498
+ ---
499
+
500
+ ## Constants
501
+
502
+ ### `SHEET_METAL_EDGES`
503
+
504
+ ### `COMMON_KERFS`
505
+
506
+ Common kerf values. Users should always test-cut to verify for their specific setup.