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
@@ -0,0 +1,52 @@
1
+ ---
2
+ skill-group: geometry
3
+ skill-order: 2
4
+ ---
5
+
6
+ # Geometry Conventions
7
+
8
+ ForgeCAD wraps Manifold (mesh kernel) and Three.js (Y-up renderer). This doc captures convention mismatches and how ForgeCAD resolves them.
9
+
10
+ ## Winding Order
11
+
12
+ CCW = positive area, CW = empty in Manifold's `CrossSection`. ForgeCAD auto-fixes at all entry points:
13
+ - `polygon(points)` — computes signed area (shoelace), reverses if CW
14
+ - `path().close()` — same fix
15
+
16
+ **Rule for new code:** Any function accepting user point arrays that creates a `CrossSection` MUST auto-fix winding.
17
+
18
+ ## Coordinate System (Z-up vs Y-up)
19
+
20
+ Three.js is Y-up; ForgeCAD is Z-up. Fix applied at camera level (`camera.up = (0,0,1)`) — geometry coordinates are native Z-up. Never swap Y/Z in geometry.
21
+
22
+ ## Revolution Axis
23
+
24
+ `CrossSection.revolve()` revolves around Y. Profile X = radial distance, Profile Y = height (becomes Z after revolution). Profile must be at X > 0.
25
+
26
+ ## Boolean Winding (3D)
27
+
28
+ Manifold requires consistent outward face normals. ForgeCAD only creates meshes through Manifold's own constructors, which guarantee correct normals.
29
+
30
+ ## Transform Order
31
+
32
+ Transforms apply left-to-right. `rotate()`, `scale()`, `mirror()` operate around bounding-box center. Use `rotateAround([0,0,0], ...)` to orbit around world origin.
33
+
34
+ For explicit transform objects: `A.mul(B)` = apply A then B; `composeChain(A, B, C)` = A→B→C.
35
+
36
+ ## Assembly Frame Composition
37
+
38
+ ```ts
39
+ childWorld = composeChain(childBase, jointMotion, jointFrame, parentWorld)
40
+ ```
41
+
42
+ Prefer `composeChain(...)` over manual `.mul(...).mul(...)` in kinematics code to avoid order mistakes.
43
+
44
+ ## Summary
45
+
46
+ | Convention | User sees | Kernel needs | Where we fix it |
47
+ |---|---|---|---|
48
+ | Winding | Any point order | CCW | `polygon()`, `path().close()` |
49
+ | Up axis | Z-up | Y-up (Three.js) | `camera.up`, gizmo labels |
50
+ | Revolution | "revolve this profile" | Profile in X-Y, X>0 | Documented only |
51
+ | Face normals | Doesn't think about it | Outward-pointing | Manifold constructors |
52
+ | Transform order | Left-to-right chain | Post-multiply | Native match |
@@ -0,0 +1,78 @@
1
+ ---
2
+ skill-group: recipes
3
+ skill-order: 5
4
+ ---
5
+
6
+ # Joint Design Recipes
7
+
8
+ How to build mechanical joints — clevis-tongue hinges, ball-and-socket, dovetails — that actually rotate without binding and stop where they should.
9
+
10
+ ## The Cavity Rule
11
+
12
+ Every mechanical joint has a **cavity** in one part and a **tenon** in the other. The cavity must be a real empty volume — not a gap implied by the absence of two separate solids.
13
+
14
+ If two adjacent parts in an assembly show a collision volume larger than the expected clearance volume in `forgecad run`, one part is missing its cavity. Both parts have solid material at the same joint position. This will look fine at rest pose but will block rotation and produce confusing joint behavior.
15
+
16
+ ```ts
17
+ // BAD — body has a stadium cap at both ends; the "slot" between two clevis tines
18
+ // is just empty space next to a solid body cap. The next phalanx's tongue knuckle
19
+ // has nowhere to go (it intersects the previous body's cap).
20
+ const body = stadiumBar(L); // cap at X=0 AND X=L
21
+ const tine1 = box(...).translate(L, Y_OFF, 0);
22
+ const tine2 = box(...).translate(L, -Y_OFF, 0);
23
+ let phalanx = union(body, tine1, tine2);
24
+
25
+ // GOOD — body ends FLAT before the joint. Tines extend forward to the pivot.
26
+ // The X = L-KNUCK_R..L+KNUCK_R volume between the tines is genuinely empty.
27
+ const body = box(L - KNUCK_R, TONG_T, H, true).translate((L - KNUCK_R) / 2, 0, 0);
28
+ const tongueKnuckle = knuckleDisc(0, 0, TONG_T); // proximal cap only
29
+ let phalanx = union(tongueKnuckle, body, tine1, tine2, ...tineCaps);
30
+ ```
31
+
32
+ After applying the cavity rule, `forgecad run` collision volume between adjacent parts in a clevis-tongue chain should drop to **zero** (or a few mm³ of clearance overlap). If it doesn't, there's still solid material where there should be a cavity.
33
+
34
+ ## Connecting Cantilevers
35
+
36
+ A clevis tine arm at Y=±Y_OFF is geometrically separate from a body at Y=±TONG_T/2. With Y_OFF > TONG_T/2 + clearance, there is a **physical gap** between them. The tines float — they would snap off as soon as load is applied.
37
+
38
+ Always add a **yoke**: a short slab spanning the full clevis width, sitting between the body's flat distal end and the tines' attachment point. The yoke fills the Y gap so material is continuous from the body through to each tine.
39
+
40
+ ```ts
41
+ const yokeLen = 3; // a few mm of structural overlap
42
+ const yokeStart = L - KNUCK_R - yokeLen;
43
+ const totalY = (Y_OFF + TINE_T / 2) * 2; // full clevis width
44
+ const yoke = box(yokeLen, totalY, H, true)
45
+ .translate(yokeStart + yokeLen / 2, 0, 0);
46
+ phalanx = union(phalanx, yoke);
47
+ ```
48
+
49
+ ## Hard Stops vs Slider Limits
50
+
51
+ `addRevolute({ min: 0, max: 90 })` sets **slider limits** — the viewport won't let the user drag past them, but the geometry permits any rotation. There is no physical stop.
52
+
53
+ For a **geometric** hard stop (parts can't backbend past extension, or can't curl past full closure), add a small protrusion on one part that interferes with the other at the limit angle:
54
+
55
+ - **Extension stop at 0°** (typical for fingers, knees, elbows): add a small "lip" on the dorsal side of the proximal end of the child phalanx, sized so it just touches the parent's distal dorsal corner at 0°. Negative rotation (backbending) is then blocked by part-on-part contact.
56
+ - **Flexion stop at θmax**: add a similar lip on the palmar side, or rely on the body-to-body collision when bodies meet.
57
+
58
+ Verify with `forgecad run` at the limit poses — the contact pair should show ~0 mm³ collision (just touching), and rotation past the limit should report a non-zero collision volume.
59
+
60
+ ## Knuckle Sizing
61
+
62
+ For a clevis-tongue joint with body height H, the tongue knuckle radius and clevis tine knuckle radius must satisfy:
63
+
64
+ ```
65
+ KNUCK_R >= H / 2
66
+ ```
67
+
68
+ If the knuckle radius is smaller than the body's half-height, the body's corners protrude beyond the knuckle envelope. When the joint rotates, those corners sweep through space outside the cylindrical envelope and collide with the adjacent part.
69
+
70
+ Setting `KNUCK_R = H / 2` exactly makes the body cross-section a stadium that perfectly fits the knuckle envelope.
71
+
72
+ ## Verification Workflow
73
+
74
+ 1. Build the joint at rest pose. Run `forgecad run`. Check collision volumes.
75
+ 2. If adjacent parts in the joint show > clearance-volume of overlap → missing cavity (apply the cavity rule).
76
+ 3. Render with `--focus PartName` to inspect each part in isolation. The clevis end should clearly show a gap between the tines (the cavity).
77
+ 4. Render at curl angles (set joint debug params) at 30°, 60°, 90°. No new collisions should appear from rotation.
78
+ 5. Render at -10° (backbend test). Either no rotation possible (geometric stop in place) or rotation occurs and you need to add a stop.
@@ -0,0 +1,77 @@
1
+ ---
2
+ skill-group: recipes
3
+ skill-order: 1
4
+ ---
5
+
6
+ # Modeling Recipes
7
+
8
+ ## Iteration Bias
9
+
10
+ - Default to a buildable first pass instead of a long proposal.
11
+ - Replace a broken model wholesale when that is faster than incremental patching.
12
+ - Validate early with `forgecad run <file>`.
13
+
14
+ ## Common Patterns
15
+
16
+ ### Hollow Shell
17
+ ```javascript
18
+ const outerBox = box(outer, outer, outer, true);
19
+ const innerBox = box(outer - 2 * wall, outer - 2 * wall, outer - 2 * wall, true);
20
+ return outerBox.subtract(innerBox);
21
+ ```
22
+
23
+ ### Sketch-Based Twist
24
+ ```javascript
25
+ const outer = ngon(sides, radius);
26
+ const inner = ngon(sides, radius - wall);
27
+ return outer.subtract(inner).extrude(height, { twist: 45, divisions: 32 });
28
+ ```
29
+
30
+ ### Rounded Profiles
31
+ ```javascript
32
+ // All convex corners — offset trick
33
+ const base = rect(50, 30).offset(-3, 'Round').offset(3, 'Round');
34
+
35
+ // Selected corners only
36
+ const roof = filletCorners(roofPoints, [
37
+ { index: 3, radius: 19 },
38
+ { index: 4, radius: 19 },
39
+ { index: 5, radius: 19 },
40
+ ]);
41
+ ```
42
+
43
+ ### Choosing the right sketch-rounding tool
44
+
45
+ - `offset(-r).offset(+r)` — round every convex corner of a closed outline
46
+ - `stroke(points, width, 'Round')` — centerline-based geometry (ribs, traces)
47
+ - `filletCorners(points, ...)` — selective true-corner fillets on mixed profiles
48
+
49
+ ## Best Practices
50
+
51
+ - All dimensions in millimeters; angles in degrees.
52
+ - Primitives are centered on XY, base at Z=0. Use `placeReference('center', [0,0,0])` to center on all axes.
53
+ - Prefer named intermediate values over deeply nested one-liners.
54
+ - `union2d`, `difference2d`, `intersection2d` batch faster than chained `.add()` / `.subtract()`.
55
+
56
+ ## Debugging
57
+
58
+ ```javascript
59
+ console.log("Volume:", shape.volume());
60
+ ```
61
+
62
+ For sketch-heavy work, compare the raw profile and rounded profile side-by-side before extruding:
63
+
64
+ ```javascript
65
+ return [
66
+ { name: "Raw", sketch: polygon(roofPoints) },
67
+ { name: "Rounded", sketch: filletCorners(roofPoints, [...]).translate(120, 0) },
68
+ ];
69
+ ```
70
+
71
+ ## Common Errors
72
+
73
+ - `"Kernel not initialized"` — internal/runtime issue, reload the app
74
+ - zero dimensions or self-intersecting sketches → invalid geometry
75
+ - wrong variable name → `"Cannot read property of undefined"`
76
+
77
+ For larger runnable examples, read `examples/api/`.
@@ -0,0 +1,151 @@
1
+ ---
2
+ skill-group: geometry
3
+ skill-order: 3
4
+ ---
5
+
6
+ # Positioning Strategy
7
+
8
+ ## Primitive origin convention
9
+
10
+ All 3D primitives are **centered on XY, base at Z=0**:
11
+
12
+ | Primitive | X range | Y range | Z range |
13
+ |-----------|---------|---------|---------|
14
+ | `box(60, 40, 20)` | [-30, 30] | [-20, 20] | [0, 20] |
15
+ | `cylinder(50, 10)` | [-10, 10] | [-10, 10] | [0, 50] |
16
+ | `sphere(15)` | [-15, 15] | [-15, 15] | [-15, 15] |
17
+ | `torus(20, 5)` | [-25, 25] | [-25, 25] | [-5, 5] |
18
+
19
+ Sphere and torus are fully centered (symmetric in Z). Box and cylinder sit on the XY ground plane — **Z goes up from zero, never negative**.
20
+
21
+ This means `box(w, d, h).translate(0, 0, -h/2)` is wrong for "center on Z" — the box is already at `[0, h]`, not `[-h/2, h/2]`.
22
+
23
+ ---
24
+
25
+ Most positioning bugs come from manual coordinate arithmetic. Use these methods in priority order.
26
+
27
+ ## 1. `group()` — local coordinates for multi-part assemblies
28
+
29
+ The most common positioning bug: manually adding a parent's global offset to every sub-part. One wrong sign or forgotten variable and parts float into space. **Use `group()` to build parts in local coordinates (at the origin), then position the group once.**
30
+
31
+ ```javascript
32
+ // BAD — every sub-part repeats the parent's global position
33
+ const unitY = -18, unitZ = 70;
34
+ const body = lib.roundedBox(100, 20, 32, 4).translate(0, unitY, unitZ);
35
+ const panel = box(98, 2, 18).translate(0, unitY - 12, unitZ + 4);
36
+ const louver = box(88, 2, 6).translate(0, unitY - 14, unitZ - 11);
37
+ const led = sphere(1.2).translate(35, unitY - 12, unitZ + 9);
38
+
39
+ // GOOD — build at local origin, group, translate once
40
+ const body = lib.roundedBox(100, 20, 32, 4);
41
+ const panel = box(98, 2, 18).translate(0, -12, 4); // relative to local origin
42
+ const louver = box(88, 2, 6).translate(0, -14, -11); // relative to local origin
43
+ const led = sphere(1.2).translate(35, -12, 9); // relative to local origin
44
+ const indoorUnit = group(
45
+ { name: 'Body', shape: body },
46
+ { name: 'Panel', shape: panel },
47
+ { name: 'Louver', shape: louver },
48
+ { name: 'LED', shape: led },
49
+ ).translate(0, -18, 70); // ONE translate for the whole assembly
50
+ ```
51
+
52
+ **Groups nest.** Build sub-assemblies as groups, then group those into larger assemblies — each level has its own local origin.
53
+
54
+ ```javascript
55
+ const fan = group(hub, ...blades).translate(0, 25, 0); // fan assembly
56
+ const outdoorUnit = group(
57
+ { name: 'Body', shape: casing },
58
+ { name: 'Fan', shape: fan }, // already a group
59
+ { name: 'Grille', shape: grille },
60
+ ).translate(0, 23, -42); // position the whole outdoor unit
61
+ ```
62
+
63
+ **When to use something else:** `group()` preserves individual shapes — you can't boolean (subtract/intersect) a group. If a sub-part needs a boolean with the parent body, do that boolean first in local coordinates, then group the result.
64
+
65
+ ## 2. Connectors + `matchTo()` — default for mating interfaces
66
+
67
+ Define connectors on parts; `matchTo()` provides automatic 6-DOF alignment. The child translates and rotates so its connector aligns with the target's — origins coincide, axes oppose (plug-in model).
68
+
69
+ ```javascript
70
+ const shelf = box(200, 120, 10, true).withConnectors({
71
+ left_tab: connector.male("dovetail", { origin: [-100, 0, 0], axis: [-1, 0, 0] }),
72
+ });
73
+ const panel = box(12, 120, 200, true).withConnectors({
74
+ shelf_0: connector.female("dovetail", { origin: [6, 0, -50], axis: [1, 0, 0] }),
75
+ });
76
+ const placed = shelf.matchTo(panel, "left_tab", "shelf_0");
77
+ // Dictionary form for multiple pairs on same target:
78
+ const placed2 = shelf.matchTo(panel, { left_tab: "shelf_0" });
79
+ // Named group children bubble connectors via dotted paths:
80
+ const cabinet = group({ name: "Left", shape: panel });
81
+ shelf.matchTo(cabinet, "left_tab", "Left.shelf_0");
82
+ ```
83
+
84
+ **Why connectors first:** stable (don't shift on fillet/chamfer/boolean), semantic (carry type/gender), oriented (full frame), queryable (`shape.connectorDistance('a','b')`), explode-aware.
85
+
86
+ ## 3. `pointAlong()` — orient cylinders before positioning
87
+
88
+ ```javascript
89
+ // BAD
90
+ const pipe = cylinder(100, 5).rotateX(90).translate(x, y, z);
91
+ // GOOD — reads as "pipe pointing along Y"
92
+ const pipe = cylinder(100, 5).pointAlong([0, 1, 0]).translate(x, y, z);
93
+ ```
94
+
95
+ **Always call `pointAlong()` BEFORE `matchTo()` or `translate()`** — it reorients around the origin.
96
+
97
+ ## 4. `attachTo()` — quick bounding-box positioning
98
+
99
+ ```javascript
100
+ const column = cylinder(50, 8).attachTo(base, 'top', 'bottom');
101
+ ```
102
+
103
+ `child.attachTo(parent, parentAnchor, selfAnchor, offset)`. Anchor points shift on fillet/chamfer/boolean — fragile for assembly interfaces, fine for quick prototyping.
104
+
105
+ ## 5. `rotateAroundTo()` — aim a point around a hinge/axis
106
+
107
+ ```javascript
108
+ const aimed = arm.rotateAroundTo([0, 0, 1], [0, 0, 0], "tip", [30, 30, 20]);
109
+ // Exact line solve:
110
+ const lineHit = arm.rotateAroundTo([0, 0, 1], [0, 0, 0], "tip", [30, 30, 0], { mode: 'line' });
111
+ ```
112
+
113
+ ## 6. `moveToLocal()` — offset from another shape's min corner
114
+
115
+ ```javascript
116
+ const part = box(20, 20, 30).moveToLocal(base, 10, 10, 10);
117
+ ```
118
+
119
+ ## 7. `translate()` — for simple offsets or bridging computed locations
120
+
121
+ ```javascript
122
+ const pipeLen = bb2.min[1] - bb1.max[1];
123
+ const pipe = cylinder(pipeLen, 5).pointAlong([0, 1, 0]).translate(40, (bb1.max[1] + bb2.min[1]) / 2, bb1.min[2] + 15);
124
+ ```
125
+
126
+ ## 8. `placeReference()` — align any anchor to a world coordinate
127
+
128
+ Place a shape so a named anchor point lands exactly where you want it. Accepts all built-in anchors (`'bottom'`, `'center'`, `'top-front-left'`, etc.) plus custom references from `withReferences()`.
129
+
130
+ ```javascript
131
+ // Ground a shape — bottom face center at Z = 0
132
+ const grounded = shape.placeReference('bottom', [0, 0, 0])
133
+
134
+ // Center at the world origin
135
+ const centered = shape.placeReference('center', [0, 0, 0])
136
+
137
+ // Align left edge to X = 10
138
+ const aligned = shape.placeReference('left', [10, 0, 0])
139
+ ```
140
+
141
+ Also works with custom placement references for cross-file parts:
142
+
143
+ ```javascript
144
+ // widget.forge.js — define once
145
+ return union(base, post).withReferences({ points: { mount: [0, -16, -4] } });
146
+
147
+ // importer — consume
148
+ const widget = require("./widget.forge.js").placeReference("mount", [120, 40, 0]);
149
+ ```
150
+
151
+ For cross-file parts needing proper alignment, prefer connectors over placement references.
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  skill-group: dev-skill
3
3
  skill-order: 1
4
- skill-tiers: [dev]
5
4
  ---
6
5
 
7
6
  # Skill System Maintenance
@@ -47,14 +46,12 @@ The build script discovers docs by scanning `docs/permanent/` for files with `sk
47
46
  |-------|----------|---------|-------------|
48
47
  | `skill-group` | Yes | — | Group name (must match `GROUP_REGISTRY` in build script) |
49
48
  | `skill-order` | No | 50 | Sort order within the group |
50
- | `skill-tiers` | No | `[standard, one-file]` | Which outputs include this file |
51
49
 
52
- ### Tier values
50
+ ### Inclusion rules
53
51
 
54
- - `[standard, one-file]` (default) — included in installed skill and CONTEXT.md
55
- - `[standard]`installed skill only (too verbose for one-file paste)
56
- - `[one-file]`CONTEXT.md only (slim excerpt replacing a standard doc)
57
- - `[dev]` — dev skill only (SKILL-dev.md)
52
+ - **`skill-group: <name>`** — included in SKILL.md (index) and CONTEXT.md (inlined).
53
+ - **`skill-group: dev-<name>`** dev-only, appears in SKILL-dev.md only.
54
+ - **No `skill-group`**not part of the skill at all.
58
55
 
59
56
  ### Adding a new group
60
57
 
@@ -62,12 +59,11 @@ If you need a new group, add it to `GROUP_REGISTRY` in `scripts/build-forgecad-s
62
59
 
63
60
  ## Adding Dev-Only Docs
64
61
 
65
- Use `skill-tiers: [dev]` in the frontmatter. These appear in `SKILL-dev.md` but not in `SKILL.md` or `CONTEXT.md`. Use this for:
62
+ Use a `skill-group` with a `dev-` prefix (e.g. `dev-compiler`, `dev-solver`, `dev-conventions`). These appear in `SKILL-dev.md` but not in `SKILL.md` or `CONTEXT.md`. Use this for:
66
63
 
67
64
  - Compiler/solver internals
68
65
  - Coding conventions and PR guidelines
69
- - Deployment and release processes
70
- - Full CLI reference (vs the slim `skill-cli.md` excerpt)
66
+ - Full CLI reference (vs the slim user-facing docs)
71
67
 
72
68
  ## Deciding Standard vs Dev
73
69
 
@@ -78,6 +74,21 @@ Use `skill-tiers: [dev]` in the frontmatter. These appear in `SKILL-dev.md` but
78
74
  | Does it cover team process (coding standards, releases, CI)? | Dev only |
79
75
  | Is it a CLI command only developers run (`check suite`, `sdf`, debug flags)? | Dev only |
80
76
 
77
+ ## Orphan files
78
+
79
+ `skill install` uses `cpSync` — it copies `dist-skill/docs/` on top of the installed skill directory but **does not delete** files that no longer exist in the source. If a doc is removed or loses its `skill-group` frontmatter, its copy persists in `~/.agents/skills/forgecad/docs/` as an orphan.
80
+
81
+ To check for orphans:
82
+ ```bash
83
+ for f in $(find ~/.agents/skills/forgecad/docs -name '*.md'); do
84
+ echo "$f" | grep -q '/generated/' && continue
85
+ base=$(echo "$f" | sed "s|$HOME/.agents/skills/forgecad/docs/|docs/permanent/|")
86
+ [ -f "$base" ] || echo "ORPHAN: $f"
87
+ done
88
+ ```
89
+
90
+ Delete any orphans found. They're stale copies that can mislead agents with outdated information.
91
+
81
92
  ## Verifying
82
93
 
83
94
  After changes, check that the build succeeds and the doc counts look right:
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  skill-group: dev-compiler
3
3
  skill-order: 1
4
- skill-tiers: [dev]
5
4
  ---
6
5
 
7
6
  # Forge Compiler Architecture
@@ -173,10 +172,10 @@ Current progress:
173
172
  - trim/split-by-plane, shell, hole, cut, boolean, and edge-finish rewrites now also declare descendant contracts on top of that propagation kernel
174
173
  - the shared descendant-resolution layer turns those lineage inputs into defended singles, face regions/sets, edge chains, or explicit unsupported results instead of making each caller reverse-engineer rewrite heuristics
175
174
  - trim/split-by-plane now expose their plane-cap face plus descendant-region contracts for preserved source surfaces, while hole/cut host-face splits and supported boolean difference/intersection descendants stay reviewable as defended regions instead of disappearing behind one generic ambiguity bucket
176
- - boolean rewrites now consume that same contract: supported unions still preserve operand canonical-face/query lineage when the source owners stay distinct, and coplanar/multi-member descendants can now surface as defended face sets instead of only masked name conflicts
175
+ - boolean rewrites now consume that same contract: supported unions still preserve operand label/query lineage when the source owners stay distinct, and coplanar/multi-member descendants can now surface as defended face sets instead of only masked name conflicts
177
176
  - shell, hole, and cut now layer defended created-face slots plus defended descendant regions on top of that propagation kernel instead of leaving all post-rewrite face meaning as placeholders
178
177
  - compile-covered `Shape.face(name)` resolution now reads from the compile graph plus the descendant layer, so supported shell inner walls, blind-hole floors, cut-created walls, split host faces, and defended boolean face sets can produce real `FaceRef` values after topology rewrites
179
- - non-canonical `onFace(shape, 'inner-side-right', ...)` placement now routes through that same compiler-owned face resolver, while direct `FaceRef` placements preserve created-face/propagated-face provenance instead of collapsing everything back to anonymous refs
178
+ - label-based `onFace(shape, 'inner-side-right', ...)` placement now routes through the compiler-owned face resolver, while direct `FaceRef` placements preserve created-face/propagated-face provenance instead of collapsing everything back to anonymous refs
180
179
  - `src/forge/kernel.ts` and the compiler inspection surface now expose collected topology-rewrite propagation plus descendant contracts directly, and the placement/compiler invariants assert that those contracts stay inspectable and deterministic through later transforms
181
180
  - `forgecad check query-propagation` now snapshots both the propagation surface and the descendant contracts directly, so defended plane-cap faces, split-face regions, face sets, merged-edge chains, and explicit unsupported rewrite boundaries stay reviewable without wading through the full compiler-scene baseline
182
181
 
@@ -217,10 +216,10 @@ Current progress:
217
216
  - the regression suite now also includes a file-backed ordinary-parts corpus under `examples/compiler-corpus/`, so shell, richer hole/cut workflows, projection replay, trim-created faces, repeated descendants, and finishing flows are exercised together instead of only as isolated unit slices
218
217
  - the MLP closeout review surface now lives in that corpus plus `forgecad check compiler`, `forgecad check query-propagation`, and `forgecad check brep`, so the defended subset is reviewable from the repo instead of from tribal knowledge
219
218
  - mirrored downstream features and helper-driven linear/circular repetition now preserve repeated-result ownership on top of the shared face-query backbone
220
- - supported boolean unions now also preserve owner-scoped canonical face queries from repeated descendants, and compiler regressions cover both explicit duplicate-owner merge ambiguity and later boolean chains that inherit those propagated queries
219
+ - supported boolean unions now also preserve owner-scoped label queries from repeated descendants, and compiler regressions cover both explicit duplicate-owner merge ambiguity and later boolean chains that inherit those propagated queries
221
220
  - exact export regression coverage now includes a repeated-feature part where a mirrored descendant drives a downstream workplane feature inside a boolean chain
222
221
  - `projectToPlane()` sketches now keep an explicit projection node in the compiler graph instead of collapsing immediately to anonymous runtime geometry
223
- - compile-covered `Sketch.onFace(shape, name)` resolution now prefers the defended face-query table on `Shape` targets, so supported boolean-preserved names and explicit repeated-descendant `FaceRef`s stay visible to later features instead of falling back to anonymous canonical heuristics
222
+ - compile-covered `Sketch.onFace(shape, name)` resolution now prefers the defended face-query table on `Shape` targets, so supported boolean-preserved names and explicit repeated-descendant `FaceRef`s stay visible to later features instead of falling back to anonymous heuristics
224
223
  - the supported exact subset can now replay projection-driven follow-on features when the source reduces to one defended planar projection basis: placed straight extrusions, compatible shell/hole/cut descendants, and boolean unions of compatible projected operands all stay aligned on matching parallel target planes
225
224
 
226
225
  Current limits:
@@ -230,7 +229,7 @@ Current limits:
230
229
  - `filletEdge()` / `chamferEdge()` v1 cover tracked vertical edges on compile-covered `box()` and `rectangle(...).extrude(...)` bodies plus preserved propagated sibling edges through supported edge-finish and boolean-union chains; the selected rewritten edge now resolves as an explicit descendant edge-chain for inspection/debug, but not yet as a new single finishable edge target
231
230
  - two-sided extents, tapered cutouts, and thread metadata are now supported; combined counterbore+countersink heads, modeled helical threads, and broader durable identity beyond today's defended shell/hole/cut created-face subset are still missing
232
231
  - `upToFace` currently requires a planar termination face parallel to the feature direction, but defended split termination faces can now stay queryable as descendant regions where Forge still owns the source plane
233
- - boolean/pattern propagation currently defends owner-scoped canonical face lineage through supported unions, and the descendant layer can now expose some post-merge/post-difference results as face sets/regions, but broader durable per-face ownership after arbitrary merged topology changes is still incomplete
232
+ - boolean/pattern propagation currently defends owner-scoped face label lineage through supported unions, and the descendant layer can now expose some post-merge/post-difference results as face sets/regions, but broader durable per-face ownership after arbitrary merged topology changes is still incomplete
234
233
  - repeated-feature ownership currently tracks repeated bodies and mirrored descendants, not durable per-face identity after merged pattern topology changes
235
234
  - shell, hole/cut, tracked-edge finishing, and repeated-feature workflows now preserve parent-body ownership lineage, but stable downstream face/edge ownership after topology-changing edits is still not solved, which is why richer boolean-difference/intersection targets and broader fillet/chamfer workflows remain harder next layers
236
235
  - projection replay still rejects boolean difference/intersection sources, trim/fillet/chamfer silhouette changes, and non-parallel projection bases with explicit compiler diagnostics instead of silently pretending those paths are exact-safe
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  skill-group: dev-solver
3
3
  skill-order: 2
4
- skill-tiers: [dev]
5
4
  ---
6
5
 
7
6
  # Constraint Solver Quality — What's Tunable vs What's Architecture
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  skill-group: dev-solver
3
3
  skill-order: 1
4
- skill-tiers: [dev]
5
4
  ---
6
5
 
7
6
  # Constraint Solver Internals
@@ -1,7 +1,6 @@
1
1
  ---
2
- skill-group: dev-sketch-pipeline
3
- skill-order: 1
4
- skill-tiers: [dev]
2
+ skill-group: dev-compiler
3
+ skill-order: 2
5
4
  ---
6
5
 
7
6
  # 2D Sketch Pipeline: Manifold ↔ ProfileCompilePlan ↔ OCCT
@@ -8,31 +8,31 @@
8
8
  // 2 words = edge midpoint: 'top-front', 'back-left', etc.
9
9
  // 3 words = corner: 'top-front-left', 'bottom-back-right', etc.
10
10
 
11
- const baseW = param("Base Width", 100, { min: 50, max: 200, unit: "mm" });
12
- const baseD = param("Base Depth", 80, { min: 40, max: 150, unit: "mm" });
13
- const baseH = param("Base Height", 10, { min: 5, max: 30, unit: "mm" });
11
+ const baseW = Param.number("Base Width", 100, { min: 50, max: 200, unit: "mm" });
12
+ const baseD = Param.number("Base Depth", 80, { min: 40, max: 150, unit: "mm" });
13
+ const baseH = Param.number("Base Height", 10, { min: 5, max: 30, unit: "mm" });
14
14
 
15
- const base = box(baseW, baseD, baseH, true).color('#888888');
15
+ const base = box(baseW, baseD, baseH).color('#888888');
16
16
 
17
17
  // Stack on top: column's bottom face meets base's top face
18
18
  const column = cylinder(40, 8).color('#4488cc')
19
19
  .attachTo(base, 'top', 'bottom');
20
20
 
21
21
  // Protrude from front: button's back face meets base's front face
22
- const button = box(20, 6, 10, true).color('#cc4444')
22
+ const button = box(20, 6, 10).color('#cc4444')
23
23
  .attachTo(base, 'front', 'back');
24
24
 
25
25
  // Hang below: bracket's top face meets base's bottom face
26
- const bracket = box(30, 30, 5, true).color('#44cc44')
26
+ const bracket = box(30, 30, 5).color('#44cc44')
27
27
  .attachTo(base, 'bottom', 'top');
28
28
 
29
29
  // Attach to side with offset: panel's left face meets base's right face,
30
30
  // then shift 0mm on X, 0mm on Y, 10mm up on Z
31
- const sidePanel = box(4, 40, 25, true).color('#cc8844')
31
+ const sidePanel = box(4, 40, 25).color('#cc8844')
32
32
  .attachTo(base, 'right', 'left', [0, 0, 10]);
33
33
 
34
34
  // Corner alignment: small cube at top-front-right corner of base
35
- const corner = box(8, 8, 8, true).color('#8844cc')
35
+ const corner = box(8, 8, 8).color('#8844cc')
36
36
  .attachTo(base, 'top-front-right', 'bottom-back-left');
37
37
 
38
38
  return [
@@ -1,14 +1,14 @@
1
1
  // API demo: script-declared bill of materials that gets auto-summed in report export
2
2
 
3
- const frameWidth = param('Frame Width', 900, { min: 300, max: 1800, unit: 'mm' });
4
- const frameDepth = param('Frame Depth', 500, { min: 200, max: 1200, unit: 'mm' });
5
- const legHeight = param('Leg Height', 720, { min: 300, max: 1200, unit: 'mm' });
6
- const tubeW = param('Tube Width', 30, { min: 15, max: 80, unit: 'mm' });
7
- const tubeH = param('Tube Height', 20, { min: 10, max: 80, unit: 'mm' });
8
-
9
- const frontBolts = param('Front Bolts', 8, { min: 0, max: 64, integer: true });
10
- const rearBolts = param('Rear Bolts', 8, { min: 0, max: 64, integer: true });
11
- const boltLength = param('Bolt Length', 16, { min: 6, max: 60, unit: 'mm' });
3
+ const frameWidth = Param.number('Frame Width', 900, { min: 300, max: 1800, unit: 'mm' });
4
+ const frameDepth = Param.number('Frame Depth', 500, { min: 200, max: 1200, unit: 'mm' });
5
+ const legHeight = Param.number('Leg Height', 720, { min: 300, max: 1200, unit: 'mm' });
6
+ const tubeW = Param.number('Tube Width', 30, { min: 15, max: 80, unit: 'mm' });
7
+ const tubeH = Param.number('Tube Height', 20, { min: 10, max: 80, unit: 'mm' });
8
+
9
+ const frontBolts = Param.number('Front Bolts', 8, { min: 0, max: 64, integer: true });
10
+ const rearBolts = Param.number('Rear Bolts', 8, { min: 0, max: 64, integer: true });
11
+ const boltLength = Param.number('Bolt Length', 16, { min: 6, max: 60, unit: 'mm' });
12
12
 
13
13
  const wall = 2;
14
14
  const longTubeMm = frameWidth * 2;
@@ -1,10 +1,10 @@
1
1
  // Bolt Pattern — circularPattern + linearPattern demo
2
2
 
3
- const baseR = param("Base Radius", 40, { min: 20, max: 80, unit: "mm" });
4
- const baseH = param("Base Height", 10, { min: 5, max: 20, unit: "mm" });
5
- const boltR = param("Bolt Radius", 3, { min: 1, max: 6, unit: "mm" });
6
- const boltCount = param("Bolt Count", 6, { min: 3, max: 12 });
7
- const boltCircleR = param("Bolt Circle", 30, { min: 15, max: 70, unit: "mm" });
3
+ const baseR = Param.number("Base Radius", 40, { min: 20, max: 80, unit: "mm" });
4
+ const baseH = Param.number("Base Height", 10, { min: 5, max: 20, unit: "mm" });
5
+ const boltR = Param.number("Bolt Radius", 3, { min: 1, max: 6, unit: "mm" });
6
+ const boltCount = Param.number("Bolt Count", 6, { min: 3, max: 12 });
7
+ const boltCircleR = Param.number("Bolt Circle", 30, { min: 15, max: 70, unit: "mm" });
8
8
 
9
9
  // Base plate
10
10
  const base = circle2d(baseR).extrude(baseH);
@@ -9,13 +9,13 @@
9
9
  // and the helper functions also accept arrays:
10
10
  // difference([a, b, c]), union([a, b, c]), intersection([a, b, c])
11
11
 
12
- const size = param("Size", 30, { min: 15, max: 50, unit: "mm" });
13
- const overlap = param("Overlap", 15, { min: 0, max: 30, unit: "mm" });
12
+ const size = Param.number("Size", 30, { min: 15, max: 50, unit: "mm" });
13
+ const overlap = Param.number("Overlap", 15, { min: 0, max: 30, unit: "mm" });
14
14
  const spacing = 80;
15
15
 
16
16
  // Two overlapping shapes for each demo
17
17
  function makePair(offsetX) {
18
- const a = box(size, size, size, true).translate(offsetX, 0, 0).color('#4488cc');
18
+ const a = box(size, size, size).translate(offsetX, 0, 0).color('#4488cc');
19
19
  const b = sphere(size * 0.6).translate(offsetX + size - overlap, 0, 0).color('#cc4444');
20
20
  return [a, b];
21
21
  }
@@ -26,7 +26,7 @@ const unioned = union(u1, u2).color('#8866cc');
26
26
 
27
27
  // 2. Difference — box minus sphere and cross-bore
28
28
  const [d1, d2] = makePair(spacing);
29
- const d3 = cylinder(size * 1.2, size * 0.14, undefined, undefined, true)
29
+ const d3 = cylinder(size * 1.2, size * 0.14)
30
30
  .pointAlong([0, 1, 0])
31
31
  .translate(spacing, 0, 0);
32
32
  const diffed = d1.subtract(d2, d3);
@@ -36,7 +36,7 @@ const [i1, i2] = makePair(2 * spacing);
36
36
  const intersected = intersection(i1, i2).color('#cc8844');
37
37
 
38
38
  // Show the original shapes (translucent-ish via separate objects) for reference
39
- const refA = box(size, size, size, true).translate(3 * spacing, 0, 0).color('#4488cc');
39
+ const refA = box(size, size, size).translate(3 * spacing, 0, 0).color('#4488cc');
40
40
  const refB = sphere(size * 0.6).translate(3 * spacing + size - overlap, 0, 0).color('#cc4444');
41
41
 
42
42
  return [
@@ -36,8 +36,8 @@ function vizBBox(shape) {
36
36
  // --- Demo shapes ---
37
37
 
38
38
  // A rotated box — bbox is larger than the shape itself
39
- const angle = param("Rotation", 30, { min: 0, max: 90, unit: "°" });
40
- const rotBox = box(40, 30, 20, true).rotate(0, 0, angle).color('#4488cc');
39
+ const angle = Param.number("Rotation", 30, { min: 0, max: 90, unit: "°" });
40
+ const rotBox = box(40, 30, 20).rotateZ(angle).color('#4488cc');
41
41
  const rotBBox = vizBBox(rotBox).color('#cc4444');
42
42
 
43
43
  // A sphere — bbox is a perfect cube around it
@@ -45,7 +45,7 @@ const sph = sphere(20).translate(80, 0, 0).color('#44cc44');
45
45
  const sphBBox = vizBBox(sph).color('#cc4444');
46
46
 
47
47
  // A tilted cylinder — bbox shows the extent
48
- const tiltCyl = cylinder(50, 10).rotate(30, 0, 0).translate(0, 80, 0).color('#cc88ff');
48
+ const tiltCyl = cylinder(50, 10).rotateX(30).translate(0, 80, 0).color('#cc88ff');
49
49
  const cylBBox = vizBBox(tiltCyl).color('#cc4444');
50
50
 
51
51
  return [
@@ -1,9 +1,9 @@
1
1
  // clone() / duplicate() — explicit copy helpers for Shape, TrackedShape, Sketch, and ShapeGroup.
2
2
 
3
- const spacing = param("Spacing", 90, { min: 40, max: 180, unit: "mm" });
3
+ const spacing = Param.number("Spacing", 90, { min: 40, max: 180, unit: "mm" });
4
4
 
5
5
  // --- Shape clone ---
6
- const block = box(36, 20, 12, true).color("#4a90e2");
6
+ const block = box(36, 20, 12).color("#4a90e2");
7
7
  const blockL = block.clone().translate(-spacing / 2, 0, 0);
8
8
  const blockR = block.duplicate().translate(spacing / 2, 0, 0);
9
9