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
@@ -2,53 +2,53 @@
2
2
  // Tests ForgeCAD's sketch→extrude + smoothing workflow
3
3
 
4
4
  // === Dimensions (roughly iPhone 15 Pro proportions) ===
5
- const w = param("Width", 71.6, { min: 60, max: 80, unit: "mm" });
6
- const h = param("Height", 146.6, { min: 120, max: 170, unit: "mm" });
7
- const d = param("Depth", 8.25, { min: 6, max: 12, unit: "mm" });
8
- const cornerR = param("Corner Radius", 10, { min: 2, max: 20, unit: "mm" });
9
- const edgeR = param("Edge Radius", 1.5, { min: 0, max: 3, step: 0.1, unit: "mm" });
5
+ const w = Param.number("Width", 71.6, { min: 60, max: 80, unit: "mm" });
6
+ const h = Param.number("Height", 146.6, { min: 120, max: 170, unit: "mm" });
7
+ const d = Param.number("Depth", 8.25, { min: 6, max: 12, unit: "mm" });
8
+ const cornerR = Param.number("Corner Radius", 10, { min: 2, max: 20, unit: "mm" });
9
+ const edgeR = Param.number("Edge Radius", 1.5, { min: 0, max: 3, step: 0.1, unit: "mm" });
10
10
 
11
11
  // === Body ===
12
12
  // Rounded-rect profile extruded to phone depth, then subdivision-smoothed
13
13
  // to round the 90° edges where top/bottom meet sides.
14
- const bodyProfile = roundedRect(w, h, cornerR, true);
15
- let body = bodyProfile.extrude(d, { center: true });
14
+ const bodyProfile = roundedRect(w, h, cornerR);
15
+ let body = bodyProfile.extrude(d).translate(0, 0, -d / 2);
16
16
 
17
17
  // === Screen cutout (top +Z face = phone screen side) ===
18
- const screenInset = param("Screen Inset", 2, { min: 1, max: 5, unit: "mm" });
18
+ const screenInset = Param.number("Screen Inset", 2, { min: 1, max: 5, unit: "mm" });
19
19
  const screenDepth = 0.4;
20
20
  const screenW = w - screenInset * 2;
21
21
  const screenH = h - screenInset * 2;
22
22
  const screenR = Math.max(cornerR - screenInset, 1);
23
23
 
24
- const screenCut = roundedRect(screenW, screenH, screenR, true)
24
+ const screenCut = roundedRect(screenW, screenH, screenR)
25
25
  .extrude(screenDepth + 1)
26
26
  .translate(0, 0, d / 2 - screenDepth);
27
27
 
28
28
  body = body.subtract(screenCut);
29
29
 
30
30
  // Dark screen surface filling the cutout
31
- const screenFill = roundedRect(screenW, screenH, screenR, true)
31
+ const screenFill = roundedRect(screenW, screenH, screenR)
32
32
  .color('#111111')
33
33
  .extrude(0.15)
34
34
  .translate(0, 0, d / 2 - screenDepth);
35
35
 
36
36
  // === Camera island (bottom -Z face = phone back, upper-left area) ===
37
- const camSize = param("Camera Island", 36, { min: 25, max: 45, unit: "mm" });
37
+ const camSize = Param.number("Camera Island", 36, { min: 25, max: 45, unit: "mm" });
38
38
  const camR = 8;
39
- const camBump = param("Camera Bump", 1.5, { min: 0.5, max: 3, unit: "mm" });
39
+ const camBump = Param.number("Camera Bump", 1.5, { min: 0.5, max: 3, unit: "mm" });
40
40
  const camEdgeInset = 4;
41
41
  const camX = -w / 2 + camSize / 2 + camEdgeInset;
42
42
  const camY = h / 2 - camSize / 2 - camEdgeInset;
43
43
 
44
- let camIsland = roundedRect(camSize, camSize, camR, true)
44
+ let camIsland = roundedRect(camSize, camSize, camR)
45
45
  .translate(camX, camY)
46
46
  .extrude(camBump)
47
47
  .translate(0, 0, -d / 2 - camBump);
48
48
 
49
49
  // Camera lenses — 3 in L-pattern, positioned via entity geometry
50
- const lensR = param("Lens Radius", 6, { min: 3, max: 10, unit: "mm" });
51
- const lensGap = param("Lens Gap", 3, { min: 1, max: 8, unit: "mm" });
50
+ const lensR = Param.number("Lens Radius", 6, { min: 3, max: 10, unit: "mm" });
51
+ const lensGap = Param.number("Lens Gap", 3, { min: 1, max: 8, unit: "mm" });
52
52
 
53
53
  // Use Rectangle2D entity to derive lens positions from island bounds
54
54
  const camRect = Rectangle2D.fromCenterAndDimensions(point(camX, camY), camSize, camSize);
@@ -71,18 +71,18 @@ camIsland = camIsland.subtract(makeLens(lens1), makeLens(lens2), makeLens(lens3)
71
71
 
72
72
  // === Charging port (front -Y face = phone bottom edge) ===
73
73
  // pointAlong([0,1,0]) orients the extrusion along +Y so it cuts INTO the body
74
- const portW = param("Port Width", 9, { min: 6, max: 14, unit: "mm" });
74
+ const portW = Param.number("Port Width", 9, { min: 6, max: 14, unit: "mm" });
75
75
  const portH = 3;
76
76
  const portDepth = 4;
77
77
 
78
- const portCut = roundedRect(portW, portH, portH / 2, true)
78
+ const portCut = roundedRect(portW, portH, portH / 2)
79
79
  .extrude(portDepth)
80
80
  .pointAlong([0, 1, 0])
81
81
  .translate(0, -h / 2, 0);
82
82
 
83
83
  // === Speaker & mic grille (front -Y face) ===
84
84
  const holeR = 0.5;
85
- const holeCount = param("Speaker Holes", 6, { min: 3, max: 10, integer: true });
85
+ const holeCount = Param.number("Speaker Holes", 6, { min: 3, max: 10, integer: true });
86
86
  const holeSpacing = 2.2;
87
87
  const grillX0 = portW / 2 + 5;
88
88
 
@@ -111,11 +111,11 @@ const btnInset = 0.4;
111
111
  const btnThick = 1.6;
112
112
 
113
113
  const makeBtn = (bw, by, side) => {
114
- const btn = roundedRect(bw, btnThick, 0.5, true).extrude(btnInset);
114
+ const btn = roundedRect(bw, btnThick, 0.5).extrude(btnInset);
115
115
  const x = side === 'left'
116
116
  ? -w / 2 - edgeR - btnInset
117
117
  : w / 2 + edgeR + btnInset;
118
- return btn.rotateAround([0, 0, 0], 90, 0, 90).translate(x, by, 0);
118
+ return btn.rotateX(90).rotateZ(90).translate(x, by, 0);
119
119
  };
120
120
 
121
121
  const volUp = makeBtn(14, h / 2 - 35, 'left');
@@ -1,24 +1,24 @@
1
1
  // Laptop — parametric with opening hinge
2
2
  // Demonstrates: joint(), multi-object, roundedRect, boolean ops
3
3
 
4
- const w = param("Width", 320, { min: 250, max: 400, unit: "mm" });
5
- const d = param("Depth", 220, { min: 170, max: 300, unit: "mm" });
6
- const baseH = param("Base Height", 15, { min: 8, max: 25, unit: "mm" });
7
- const lidH = param("Lid Height", 6, { min: 3, max: 12, unit: "mm" });
8
- const cornerR = param("Corner Radius", 10, { min: 3, max: 25, unit: "mm" });
9
- const screenInset = param("Screen Bezel", 8, { min: 3, max: 20, unit: "mm" });
10
- const kbInsetX = param("KB Inset X", 25, { min: 10, max: 50, unit: "mm" });
11
- const kbInsetY = param("KB Inset Y", 15, { min: 8, max: 40, unit: "mm" });
4
+ const w = Param.number("Width", 320, { min: 250, max: 400, unit: "mm" });
5
+ const d = Param.number("Depth", 220, { min: 170, max: 300, unit: "mm" });
6
+ const baseH = Param.number("Base Height", 15, { min: 8, max: 25, unit: "mm" });
7
+ const lidH = Param.number("Lid Height", 6, { min: 3, max: 12, unit: "mm" });
8
+ const cornerR = Param.number("Corner Radius", 10, { min: 3, max: 25, unit: "mm" });
9
+ const screenInset = Param.number("Screen Bezel", 8, { min: 3, max: 20, unit: "mm" });
10
+ const kbInsetX = Param.number("KB Inset X", 25, { min: 10, max: 50, unit: "mm" });
11
+ const kbInsetY = Param.number("KB Inset Y", 15, { min: 8, max: 40, unit: "mm" });
12
12
 
13
13
  // ─── Base ───
14
- const baseProfile = roundedRect(w, d, cornerR, true);
14
+ const baseProfile = roundedRect(w, d, cornerR);
15
15
  let base = baseProfile.extrude(baseH);
16
16
 
17
17
  // Keyboard recess
18
18
  const kbW = w - kbInsetX * 2;
19
19
  const kbD = d * 0.55;
20
20
  const kbDepth = 1.5;
21
- const kbRecess = roundedRect(kbW, kbD, 4, true)
21
+ const kbRecess = roundedRect(kbW, kbD, 4)
22
22
  .extrude(kbDepth + 1)
23
23
  .translate(0, d * 0.1, baseH - kbDepth);
24
24
  base = base.subtract(kbRecess);
@@ -26,7 +26,7 @@ base = base.subtract(kbRecess);
26
26
  // Trackpad recess
27
27
  const tpW = w * 0.3;
28
28
  const tpD = d * 0.22;
29
- const tpRecess = roundedRect(tpW, tpD, 3, true)
29
+ const tpRecess = roundedRect(tpW, tpD, 3)
30
30
  .extrude(kbDepth + 1)
31
31
  .translate(0, -d * 0.28, baseH - kbDepth);
32
32
  base = base.subtract(tpRecess);
@@ -45,7 +45,7 @@ const keys = [];
45
45
  for (let r = 0; r < keyRows; r++) {
46
46
  for (let c = 0; c < keyCols; c++) {
47
47
  keys.push(
48
- roundedRect(keyW, keyH, 1, true)
48
+ roundedRect(keyW, keyH, 1)
49
49
  .extrude(1)
50
50
  .translate(keyStartX + c * keyStep, keyStartY + r * keyStep, baseH - kbDepth - 0.5)
51
51
  );
@@ -54,17 +54,17 @@ for (let r = 0; r < keyRows; r++) {
54
54
  const keyboard = union(...keys);
55
55
 
56
56
  // Ports — left side
57
- const usbC1 = roundedRect(9, 3.5, 1.5, true).extrude(8)
58
- .rotateAround([0, 0, 0], 0, 0, 90)
57
+ const usbC1 = roundedRect(9, 3.5, 1.5).extrude(8)
58
+ .rotateZ(90)
59
59
  .translate(-w / 2, -d * 0.1, baseH * 0.4);
60
- const usbC2 = roundedRect(9, 3.5, 1.5, true).extrude(8)
61
- .rotateAround([0, 0, 0], 0, 0, 90)
60
+ const usbC2 = roundedRect(9, 3.5, 1.5).extrude(8)
61
+ .rotateZ(90)
62
62
  .translate(-w / 2, d * 0.05, baseH * 0.4);
63
63
  base = base.subtract(usbC1).subtract(usbC2);
64
64
 
65
65
  // Headphone jack — right side
66
66
  const hpJack = cylinder(8, 2)
67
- .rotateAround([0, 0, 0], 0, 90, 0)
67
+ .rotateY(90)
68
68
  .translate(w / 2, -d * 0.1, baseH * 0.5);
69
69
  base = base.subtract(hpJack);
70
70
 
@@ -75,7 +75,7 @@ const ventW = w * 0.4;
75
75
  const ventGap = 3;
76
76
  for (let i = 0; i < ventCount; i++) {
77
77
  ventSlots.push(
78
- roundedRect(ventW, 1.5, 0.5, true)
78
+ roundedRect(ventW, 1.5, 0.5)
79
79
  .extrude(2)
80
80
  .translate(0, -d * 0.1 + i * ventGap, -1)
81
81
  );
@@ -83,14 +83,14 @@ for (let i = 0; i < ventCount; i++) {
83
83
  base = difference(base, ...ventSlots);
84
84
 
85
85
  // ─── Lid ───
86
- const lidProfile = roundedRect(w, d, cornerR, true);
86
+ const lidProfile = roundedRect(w, d, cornerR);
87
87
  let lid = lidProfile.extrude(lidH);
88
88
 
89
89
  // Screen cutout (inset from inner face)
90
90
  const screenW = w - screenInset * 2;
91
91
  const screenD = d - screenInset * 2;
92
92
  const screenCutDepth = 1;
93
- const screenCut = roundedRect(screenW, screenD, cornerR - screenInset / 2, true)
93
+ const screenCut = roundedRect(screenW, screenD, cornerR - screenInset / 2)
94
94
  .extrude(screenCutDepth + 1)
95
95
  .translate(0, 0, -0.5);
96
96
  lid = lid.subtract(screenCut);
@@ -101,7 +101,7 @@ const cameraDot = cylinder(lidH + 2, 2)
101
101
  lid = lid.subtract(cameraDot);
102
102
 
103
103
  // Screen panel (thin slab filling the cutout)
104
- const screenPanel = roundedRect(screenW - 1, screenD - 1, cornerR - screenInset / 2 - 1, true)
104
+ const screenPanel = roundedRect(screenW - 1, screenD - 1, cornerR - screenInset / 2 - 1)
105
105
  .extrude(screenCutDepth)
106
106
  .translate(0, 0, 0);
107
107
 
@@ -112,14 +112,14 @@ const hingeZ = baseH;
112
112
 
113
113
  // Position lid at hinge point, then use joint to rotate
114
114
  const lidAtHinge = lid.translate(0, -hingeY, 0)
115
- .rotateAround([0, 0, 0], 180, 0, 0)
115
+ .rotateX(180)
116
116
  .translate(0, hingeY, hingeZ);
117
117
 
118
118
  const screenAtHinge = screenPanel.translate(0, -hingeY, 0)
119
- .rotateAround([0, 0, 0], 180, 0, 0)
119
+ .rotateX(180)
120
120
  .translate(0, hingeY, hingeZ);
121
121
 
122
- const lidAngle = param("Lid Angle", 110, { min: 0, max: 135, unit: "°" });
122
+ const lidAngle = Param.number("Lid Angle", 110, { min: 0, max: 135, unit: "°" });
123
123
 
124
124
  const openLid = lidAtHinge.rotateAroundAxis([1, 0, 0], lidAngle, [0, hingeY, hingeZ]);
125
125
  const openScreen = screenAtHinge.rotateAroundAxis([1, 0, 0], lidAngle, [0, hingeY, hingeZ]);
@@ -20,12 +20,12 @@
20
20
  // joints re-adapt because they reference the panel edge lengths.
21
21
 
22
22
  // ── Parameters ─────────────────────────────────────────────────────
23
- const t = param('Thickness', 3, { min: 2, max: 6, step: 0.5 });
24
- const w = param('Width', 100, { min: 40, max: 300 });
25
- const d = param('Depth', 80, { min: 40, max: 200 });
26
- const h = param('Height', 50, { min: 20, max: 150 });
27
- const fold = param('Fold', 1, { min: 0, max: 1, step: 0.01 });
28
- const kerf = param('Kerf', 0.2, { min: 0, max: 0.5, step: 0.05 });
23
+ const t = Param.number('Thickness', 3, { min: 2, max: 6, step: 0.5 });
24
+ const w = Param.number('Width', 100, { min: 40, max: 300 });
25
+ const d = Param.number('Depth', 80, { min: 40, max: 200 });
26
+ const h = Param.number('Height', 50, { min: 20, max: 150 });
27
+ const fold = Param.number('Fold', 1, { min: 0, max: 1, step: 0.01 });
28
+ const kerf = Param.number('Kerf', 0.2, { min: 0, max: 0.5, step: 0.05 });
29
29
 
30
30
  // ── Create flat panels ─────────────────────────────────────────────
31
31
  // Each panel is a named rectangle with four edges: bottom, right, top, left.
@@ -15,12 +15,12 @@
15
15
  // • Step-by-step assembly instructions
16
16
 
17
17
  // ── Parameters ─────────────────────────────────────────────────────────
18
- const t = param('Thickness', 3, { min: 2, max: 6, step: 0.5 });
19
- const w = param('Width', 200, { min: 80, max: 400 });
20
- const d = param('Depth', 120, { min: 60, max: 250 });
21
- const h = param('Height', 50, { min: 20, max: 120 });
22
- const fold = param('Fold', 1, { min: 0, max: 1, step: 0.01 });
23
- const kerf = param('Kerf', 0.2, { min: 0, max: 0.5, step: 0.05 });
18
+ const t = Param.number('Thickness', 3, { min: 2, max: 6, step: 0.5 });
19
+ const w = Param.number('Width', 200, { min: 80, max: 400 });
20
+ const d = Param.number('Depth', 120, { min: 60, max: 250 });
21
+ const h = Param.number('Height', 50, { min: 20, max: 120 });
22
+ const fold = Param.number('Fold', 1, { min: 0, max: 1, step: 0.01 });
23
+ const kerf = Param.number('Kerf', 0.2, { min: 0, max: 0.5, step: 0.05 });
24
24
 
25
25
  // ── Flat panels ─────────────────────────────────────────────────────────
26
26
  // Named edges: bottom, right, top, left — CCW winding, origin at corner.
@@ -1,11 +1,11 @@
1
1
  // Liquid Soap Dispenser — staged dispense animation without fluid simulation.
2
2
  // Move "Dispense Progress" from 0 → 100 to preview one pump cycle.
3
3
 
4
- const bodyH = param("Bottle Height", 130, { min: 90, max: 180, unit: "mm" });
5
- const bodyR = param("Bottle Radius", 34, { min: 24, max: 45, unit: "mm" });
6
- const wall = param("Wall Thickness", 2.8, { min: 1.5, max: 5, unit: "mm" });
7
- const pumpStroke = param("Pump Stroke", 10, { min: 4, max: 18, unit: "mm" });
8
- const progressPct = param("Dispense Progress", 0, { min: 0, max: 100, unit: "%" });
4
+ const bodyH = Param.number("Bottle Height", 130, { min: 90, max: 180, unit: "mm" });
5
+ const bodyR = Param.number("Bottle Radius", 34, { min: 24, max: 45, unit: "mm" });
6
+ const wall = Param.number("Wall Thickness", 2.8, { min: 1.5, max: 5, unit: "mm" });
7
+ const pumpStroke = Param.number("Pump Stroke", 10, { min: 4, max: 18, unit: "mm" });
8
+ const progressPct = Param.number("Dispense Progress", 0, { min: 0, max: 100, unit: "%" });
9
9
 
10
10
  const progress = progressPct / 100;
11
11
  const clamp01 = (v) => Math.max(0, Math.min(1, v));
@@ -41,30 +41,30 @@ const nozzleTipX = nozzleRootX + nozzleL;
41
41
  const nozzleZ = headZ + 1;
42
42
 
43
43
  // Bottle shell + neck.
44
- const bottleOuter = cylinder(bodyH, bodyR, bodyR, undefined, true)
44
+ const bottleOuter = cylinder(bodyH, bodyR, bodyR)
45
45
  .translate(0, 0, bodyH * 0.5);
46
- const bottleInner = cylinder(bodyH - wall * 1.4, bodyR - wall, bodyR - wall, undefined, true)
46
+ const bottleInner = cylinder(bodyH - wall * 1.4, bodyR - wall, bodyR - wall)
47
47
  .translate(0, 0, wall + (bodyH - wall * 1.4) * 0.5);
48
48
  const bottleShell = bottleOuter.subtract(bottleInner);
49
49
 
50
- const neckOuter = cylinder(neckH, neckR, neckR, undefined, true).translate(0, 0, bodyH + neckH * 0.5 - 2);
51
- const neckHole = cylinder(neckH + 6, chamberR - 2.5, chamberR - 2.5, undefined, true)
50
+ const neckOuter = cylinder(neckH, neckR, neckR).translate(0, 0, bodyH + neckH * 0.5 - 2);
51
+ const neckHole = cylinder(neckH + 6, chamberR - 2.5, chamberR - 2.5)
52
52
  .translate(0, 0, bodyH + neckH * 0.5 - 2);
53
53
  const bottle = union(bottleShell, neckOuter).subtract(neckHole);
54
54
 
55
55
  // Pump chamber.
56
- const chamberOuter = cylinder(chamberH, chamberR, chamberR, undefined, true)
56
+ const chamberOuter = cylinder(chamberH, chamberR, chamberR)
57
57
  .translate(0, 0, chamberBottomZ + chamberH * 0.5);
58
- const chamberInner = cylinder(chamberH - 4, chamberInnerR, chamberInnerR, undefined, true)
58
+ const chamberInner = cylinder(chamberH - 4, chamberInnerR, chamberInnerR)
59
59
  .translate(0, 0, chamberBottomZ + chamberH * 0.5 + 1.2);
60
60
  const chamberShell = chamberOuter.subtract(chamberInner);
61
61
 
62
62
  // Pump head + nozzle.
63
- const head = box(headW, headD, headH, true).translate(0, 0, headZ);
64
- const nozzleOuter = cylinder(nozzleL, nozzleR, nozzleR * 0.92, undefined, true)
63
+ const head = box(headW, headD, headH).translate(0, 0, headZ);
64
+ const nozzleOuter = cylinder(nozzleL, nozzleR, nozzleR * 0.92)
65
65
  .pointAlong([1, 0, 0])
66
66
  .translate(nozzleCenterX, 0, nozzleZ);
67
- const nozzleInner = cylinder(nozzleL + 2, 1.5, 1.35, undefined, true)
67
+ const nozzleInner = cylinder(nozzleL + 2, 1.5, 1.35)
68
68
  .pointAlong([1, 0, 0])
69
69
  .translate(nozzleCenterX, 0, nozzleZ);
70
70
  const nozzle = nozzleOuter.subtract(nozzleInner);
@@ -72,12 +72,12 @@ const nozzle = nozzleOuter.subtract(nozzleInner);
72
72
  // Stem + piston (moves with the head).
73
73
  const pistonH = 7;
74
74
  const pistonZ = chamberTopZ - 8 - press * pumpStroke * 0.9;
75
- const piston = cylinder(pistonH, chamberR - 3.1, chamberR - 3.1, undefined, true).translate(0, 0, pistonZ);
75
+ const piston = cylinder(pistonH, chamberR - 3.1, chamberR - 3.1).translate(0, 0, pistonZ);
76
76
 
77
77
  const stemTopZ = headZ - headH * 0.5;
78
78
  const stemBottomZ = pistonZ + pistonH * 0.5;
79
79
  const stemLen = Math.max(8, stemTopZ - stemBottomZ);
80
- const stem = cylinder(stemLen, 2.2, 2.2, undefined, true).translate(0, 0, (stemTopZ + stemBottomZ) * 0.5);
80
+ const stem = cylinder(stemLen, 2.2, 2.2).translate(0, 0, (stemTopZ + stemBottomZ) * 0.5);
81
81
 
82
82
  // Return spring shown as compressed ring stack.
83
83
  const springBottomZ = chamberBottomZ + 8;
@@ -88,8 +88,8 @@ const springRings = [];
88
88
  for (let i = 0; i < springTurns; i++) {
89
89
  const t = i / Math.max(1, springTurns - 1);
90
90
  const z = springBottomZ + t * springSpan;
91
- const ringOuter = cylinder(1.2, chamberR - 4.2, chamberR - 4.2, undefined, true).translate(0, 0, z);
92
- const ringInner = cylinder(2.4, chamberR - 5.3, chamberR - 5.3, undefined, true).translate(0, 0, z);
91
+ const ringOuter = cylinder(1.2, chamberR - 4.2, chamberR - 4.2).translate(0, 0, z);
92
+ const ringInner = cylinder(2.4, chamberR - 5.3, chamberR - 5.3).translate(0, 0, z);
93
93
  springRings.push(ringOuter.subtract(ringInner));
94
94
  }
95
95
  const spring = union(...springRings);
@@ -98,8 +98,8 @@ const spring = union(...springRings);
98
98
  const tubeBottomZ = 8;
99
99
  const tubeTopZ = chamberBottomZ + 6;
100
100
  const tubeLen = tubeTopZ - tubeBottomZ;
101
- const tubeOuter = cylinder(tubeLen, 2.5, 2.5, undefined, true).translate(0, 0, (tubeBottomZ + tubeTopZ) * 0.5);
102
- const tubeInner = cylinder(tubeLen + 2, 1.2, 1.2, undefined, true).translate(0, 0, (tubeBottomZ + tubeTopZ) * 0.5);
101
+ const tubeOuter = cylinder(tubeLen, 2.5, 2.5).translate(0, 0, (tubeBottomZ + tubeTopZ) * 0.5);
102
+ const tubeInner = cylinder(tubeLen + 2, 1.2, 1.2).translate(0, 0, (tubeBottomZ + tubeTopZ) * 0.5);
103
103
  const dipTube = tubeOuter.subtract(tubeInner);
104
104
 
105
105
  // Check valves: inlet opens during release, outlet opens during press.
@@ -120,18 +120,18 @@ const reservoir = cylinder(reservoirLevel, bodyR - wall - 1, bodyR - wall - 1)
120
120
  .translate(0, 0, 4 + reservoirLevel * 0.5);
121
121
 
122
122
  const tubeSlugH = 6 + drawPhase * (tubeLen - 10);
123
- const tubeLiquid = cylinder(tubeSlugH, 1.1, 1.1, undefined, true)
123
+ const tubeLiquid = cylinder(tubeSlugH, 1.1, 1.1)
124
124
  .translate(0, 0, tubeBottomZ + tubeSlugH * 0.5);
125
125
 
126
126
  const chamberLiquidH = Math.max(2, 9 + drawPhase * 17 - pushPhase * 16);
127
- const chamberLiquid = cylinder(chamberLiquidH, chamberR - 4.8, chamberR - 4.8, undefined, true)
127
+ const chamberLiquid = cylinder(chamberLiquidH, chamberR - 4.8, chamberR - 4.8)
128
128
  .translate(0, 0, chamberBottomZ + 4 + chamberLiquidH * 0.5);
129
129
 
130
130
  const flowIn = clamp01((progress - 0.45) / 0.2);
131
131
  const flowOut = 1 - clamp01((progress - 0.8) / 0.15);
132
132
  const nozzleFlow = clamp01(flowIn * flowOut);
133
133
  const nozzleLiquidLen = Math.max(1, 1 + nozzleFlow * (nozzleL - 3));
134
- const nozzleLiquid = cylinder(nozzleLiquidLen, 1.05, 0.95, undefined, true)
134
+ const nozzleLiquid = cylinder(nozzleLiquidLen, 1.05, 0.95)
135
135
  .pointAlong([1, 0, 0])
136
136
  .translate(nozzleRootX + nozzleLiquidLen * 0.5, 0, nozzleZ);
137
137
 
@@ -1,12 +1,12 @@
1
1
  // Origami Fish
2
2
  // Stylized folded-paper fish inspired by a side-view crane-fish form.
3
3
 
4
- const scale = param("Scale", 1.25, { min: 0.7, max: 1.9, step: 0.05 });
5
- const paper = param("Paper Thickness", 0.9, { min: 0.4, max: 2, step: 0.05, unit: "mm" });
6
- const fold = param("Fold", 30, { min: 0, max: 65, unit: "°" });
7
- const tailSpread = param("Tail Spread", 34, { min: 10, max: 70, unit: "°" });
8
- const tailDrop = param("Tail Drop", 26, { min: 0, max: 55, unit: "°" });
9
- const showStand = param("Show Stand", 1, { min: 0, max: 1, integer: true });
4
+ const scale = Param.number("Scale", 1.25, { min: 0.7, max: 1.9, step: 0.05 });
5
+ const paper = Param.number("Paper Thickness", 0.9, { min: 0.4, max: 2, step: 0.05, unit: "mm" });
6
+ const fold = Param.number("Fold", 30, { min: 0, max: 65, unit: "°" });
7
+ const tailSpread = Param.number("Tail Spread", 34, { min: 10, max: 70, unit: "°" });
8
+ const tailDrop = Param.number("Tail Drop", 26, { min: 0, max: 55, unit: "°" });
9
+ const showStand = Param.number("Show Stand", 1, { min: 0, max: 1, integer: true });
10
10
 
11
11
  const redMain = "#c51f3a";
12
12
  const redLight = "#d8324f";
@@ -16,23 +16,25 @@ const S = (v) => v * scale;
16
16
  const toPts = (pts) => pts.map(([x, y]) => [S(x), S(y)]);
17
17
 
18
18
  function pose(shape) {
19
- return shape.rotateAround([0, 0, 0], 14, -8, 20).translate(S(6), S(0), S(36));
19
+ return shape.rotateX(14).rotateY(-8).rotateZ(20).translate(S(6), S(0), S(36));
20
20
  }
21
21
 
22
22
  function panel(name, pts, color, rotate = [0, 0, 0], translate = [0, 0, 0]) {
23
23
  const shape = polygon(toPts(pts))
24
- .extrude(paper, { center: true })
24
+ .extrude(paper)
25
+ .translate(0, 0, -paper / 2)
25
26
  .color(color)
26
- .rotateAround([0, 0, 0], rotate[0], rotate[1], rotate[2])
27
+ .rotateX(rotate[0]).rotateY(rotate[1]).rotateZ(rotate[2])
27
28
  .translate(S(translate[0]), S(translate[1]), S(translate[2]));
28
29
  return { name, shape: pose(shape) };
29
30
  }
30
31
 
31
32
  function ridge(name, pts, width, color, rotate = [0, 0, 0], translate = [0, 0, 0]) {
32
33
  const shape = stroke(toPts(pts), S(width), "Round")
33
- .extrude(paper * 0.9, { center: true })
34
+ .extrude(paper * 0.9)
35
+ .translate(0, 0, -paper * 0.45)
34
36
  .color(color)
35
- .rotateAround([0, 0, 0], rotate[0], rotate[1], rotate[2])
37
+ .rotateX(rotate[0]).rotateY(rotate[1]).rotateZ(rotate[2])
36
38
  .translate(S(translate[0]), S(translate[1]), S(translate[2]));
37
39
  return { name, shape: pose(shape) };
38
40
  }
@@ -130,7 +132,7 @@ if (showStand === 1) {
130
132
  .color("#6f6f77")
131
133
  .translate(S(0), S(-4), S(-52));
132
134
 
133
- const clip = cylinder(S(7), S(2.6), S(1.2), 28, true)
135
+ const clip = cylinder(S(7), S(2.6), S(1.2), 28)
134
136
  .pointAlong([0, 1, 0])
135
137
  .color("#56565d")
136
138
  .translate(S(1), S(-4), S(34));
@@ -1,8 +1,8 @@
1
1
  // Spider-Man Birthday Cake
2
2
  // A multi-tiered cake with Spider-Man colors and decorations.
3
3
 
4
- const tierHeight = param("Tier Height", 30, { min: 20, max: 50, unit: "mm" });
5
- const baseRadius = param("Base Radius", 60, { min: 40, max: 100, unit: "mm" });
4
+ const tierHeight = Param.number("Tier Height", 30, { min: 20, max: 50, unit: "mm" });
5
+ const baseRadius = Param.number("Base Radius", 60, { min: 40, max: 100, unit: "mm" });
6
6
 
7
7
  // Colors
8
8
  const spideyRed = '#e60000';
@@ -45,12 +45,12 @@ const eyeW = topRadius * 0.4;
45
45
  const eyeH = tierHeight * 0.6;
46
46
  const eyeT = 2;
47
47
 
48
- const leftEye = box(eyeW, eyeT, eyeH, true).color(spideyWhite)
49
- .rotateAround([0, 0, 0], 0, 0, 20)
48
+ const leftEye = box(eyeW, eyeT, eyeH).color(spideyWhite)
49
+ .rotateZ(20)
50
50
  .onFace(topTier, 'front', { u: -topRadius * 0.35, v: 0, protrude: 1 });
51
51
 
52
- const rightEye = box(eyeW, eyeT, eyeH, true).color(spideyWhite)
53
- .rotateAround([0, 0, 0], 0, 0, -20)
52
+ const rightEye = box(eyeW, eyeT, eyeH).color(spideyWhite)
53
+ .rotateZ(-20)
54
54
  .onFace(topTier, 'front', { u: topRadius * 0.35, v: 0, protrude: 1 });
55
55
 
56
56
  cake.push({ name: "Top Tier", shape: topTier });
@@ -1,11 +1,11 @@
1
1
  // Storage Container — parametric open-top bin
2
2
  // Used by shelf-unit.forge.js via require()
3
3
 
4
- const w = param("Container Width", 120, { min: 60, max: 200, unit: "mm" });
5
- const d = param("Container Depth", 180, { min: 100, max: 300, unit: "mm" });
6
- const h = param("Container Height", 100, { min: 50, max: 200, unit: "mm" });
7
- const wall = param("Wall", 3, { min: 1.5, max: 6, unit: "mm" });
8
- const lipH = param("Lip Height", 5, { min: 2, max: 10, unit: "mm" });
4
+ const w = Param.number("Container Width", 120, { min: 60, max: 200, unit: "mm" });
5
+ const d = Param.number("Container Depth", 180, { min: 100, max: 300, unit: "mm" });
6
+ const h = Param.number("Container Height", 100, { min: 50, max: 200, unit: "mm" });
7
+ const wall = Param.number("Wall", 3, { min: 1.5, max: 6, unit: "mm" });
8
+ const lipH = Param.number("Lip Height", 5, { min: 2, max: 10, unit: "mm" });
9
9
 
10
10
  // Outer shell
11
11
  const outer = box(w, d, h);
@@ -1,12 +1,12 @@
1
1
  // Tool Shelf — multi-shelf unit with identical containers
2
2
  // Demonstrates require() for reusing the same container across shelves
3
3
 
4
- const shelfW = param("Shelf Width", 800, { min: 400, max: 1200, unit: "mm" });
5
- const shelfD = param("Shelf Depth", 200, { min: 150, max: 350, unit: "mm" });
6
- const totalH = param("Total Height", 1200, { min: 600, max: 1800, unit: "mm" });
7
- const boardT = param("Board Thickness", 18, { min: 12, max: 25, unit: "mm" });
8
- const rows = param("Shelf Rows", 4, { min: 2, max: 6, integer: true });
9
- const cols = param("Containers/Row", 3, { min: 1, max: 6, integer: true });
4
+ const shelfW = Param.number("Shelf Width", 800, { min: 400, max: 1200, unit: "mm" });
5
+ const shelfD = Param.number("Shelf Depth", 200, { min: 150, max: 350, unit: "mm" });
6
+ const totalH = Param.number("Total Height", 1200, { min: 600, max: 1800, unit: "mm" });
7
+ const boardT = Param.number("Board Thickness", 18, { min: 12, max: 25, unit: "mm" });
8
+ const rows = Param.number("Shelf Rows", 4, { min: 2, max: 6, integer: true });
9
+ const cols = Param.number("Containers/Row", 3, { min: 1, max: 6, integer: true });
10
10
 
11
11
  // Container dimensions (must match container.forge.js defaults or be close)
12
12
  const containerW = 120;
@@ -7,11 +7,11 @@
7
7
  // - BOM listing fastener quantities
8
8
  // - Optional exploded view
9
9
 
10
- const plateW = param("Plate Width", 60, { min: 40, max: 120, unit: "mm" });
11
- const plateD = param("Plate Depth", 40, { min: 30, max: 80, unit: "mm" });
12
- const topThick = param("Top Thick", 8, { min: 4, max: 20, unit: "mm" });
13
- const botThick = param("Bot Thick", 8, { min: 4, max: 20, unit: "mm" });
14
- const exploded = param("Explode", 0, { min: 0, max: 1, step: 1 });
10
+ const plateW = Param.number("Plate Width", 60, { min: 40, max: 120, unit: "mm" });
11
+ const plateD = Param.number("Plate Depth", 40, { min: 30, max: 80, unit: "mm" });
12
+ const topThick = Param.number("Top Thick", 8, { min: 4, max: 20, unit: "mm" });
13
+ const botThick = Param.number("Bot Thick", 8, { min: 4, max: 20, unit: "mm" });
14
+ const exploded = Param.number("Explode", 0, { min: 0, max: 1, step: 1 });
15
15
 
16
16
  // Bolt-circle inset from plate edge
17
17
  const inset = 10;
@@ -32,7 +32,7 @@ const hw = lib.fastenerSet("M5", grip + 4);
32
32
  const { dims } = hw;
33
33
 
34
34
  // --- Top plate: clearance holes (normal fit) ---
35
- let topPlate = box(plateW, plateD, topThick, true);
35
+ let topPlate = box(plateW, plateD, topThick);
36
36
  for (const [x, y] of boltPositions) {
37
37
  topPlate = topPlate.subtract(
38
38
  hw.clearanceHole.translate(x, y, 0),
@@ -47,7 +47,7 @@ const botCenter = -(topThick / 2 + botThick / 2);
47
47
  const botFace = botCenter - botThick / 2; // bottom face of bottom plate
48
48
 
49
49
  // --- Bottom plate: tapped holes ---
50
- let botPlate = box(plateW, plateD, botThick, true).translate(0, 0, botCenter);
50
+ let botPlate = box(plateW, plateD, botThick).translate(0, 0, botCenter);
51
51
  for (const [x, y] of boltPositions) {
52
52
  botPlate = botPlate.subtract(
53
53
  hw.tappedHole.translate(x, y, botCenter),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forgecad",
3
- "version": "0.6.3",
3
+ "version": "0.8.0",
4
4
  "description": "Code-first parametric CAD for JavaScript/TypeScript, in the browser and CLI.",
5
5
  "license": "BUSL-1.1",
6
6
  "type": "module",
@@ -8,7 +8,7 @@
8
8
  "forgecad": "./dist-cli/forgecad.js"
9
9
  },
10
10
  "files": [
11
- "dist-cli/forgecad.js",
11
+ "dist-cli",
12
12
  "dist",
13
13
  "dist-skill",
14
14
  "examples",
@@ -38,14 +38,16 @@
38
38
  "build:hosted": "npm run build:solver && npm run build:skill:forgecad && tsc && npm run build:sitemap && FORGE_HOSTED=true vite build",
39
39
  "dev:cli": "tsup cli/forgecad.ts --format esm --platform node --target node20 --out-dir dist-cli --sourcemap --external typescript",
40
40
  "build:cli": "tsup cli/forgecad.ts --format esm --platform node --target node20 --out-dir dist-cli --clean --sourcemap --external typescript",
41
+ "build:backend": "tsup apps/backend/src/server.ts --format esm --platform node --target node20 --out-dir dist-backend --clean --sourcemap",
41
42
  "build:binary": "node scripts/build-binary.mjs",
42
43
  "build:binary:all": "node scripts/build-binary.mjs --all",
43
44
  "lint": "npx biome check .",
44
45
  "lint:fix": "npx biome check . --write",
45
46
  "format": "npx biome format . --write",
46
- "test": "npm run test:prepare && npm run test:unit && node dist-cli/forgecad.js check suite",
47
+ "test": "npm run test:prepare && node dist-cli/forgecad.js check suite --profile local",
48
+ "test:full": "npm run test:prepare && node dist-cli/forgecad.js check suite",
47
49
  "test:unit": "vitest run",
48
- "test:prepare": "tsc --noEmit && npm run build:cli",
50
+ "test:prepare": "npm run build:solver:if-missing && tsc --noEmit && npm run build:cli",
49
51
  "test:compiler": "npm run test:prepare && node dist-cli/forgecad.js check compiler",
50
52
  "test:compiler:update": "npm run test:prepare && node dist-cli/forgecad.js check compiler --update",
51
53
  "test:query-propagation": "npm run test:prepare && node dist-cli/forgecad.js check query-propagation",
@@ -66,6 +68,7 @@
66
68
  "meta:path": "bash scripts/local-meta.sh path",
67
69
  "gen:types": "node scripts/gen-forge-types.mjs",
68
70
  "gen:docs": "node scripts/gen-api-docs.mjs",
71
+ "gen:cli-docs": "node scripts/gen-cli-docs.mjs",
69
72
  "refresh": "node scripts/refresh.mjs"
70
73
  },
71
74
  "dependencies": {
@@ -85,6 +88,7 @@
85
88
  "lz-string": "^1.5.0",
86
89
  "manifold-3d": "^3.4.0",
87
90
  "meshoptimizer": "^1.0.1",
91
+ "mp4-muxer": "^5.2.1",
88
92
  "nanoid": "^5.1.7",
89
93
  "opencascade.js": "2.0.0-beta.b5ff984",
90
94
  "opentype.js": "^1.3.4",
@@ -95,6 +99,7 @@
95
99
  "puppeteer-core": "^24.37.2",
96
100
  "react-router-dom": "^7.13.2",
97
101
  "resend": "^6.9.4",
102
+ "stripe": "^21.0.1",
98
103
  "typescript": "^5.8.3",
99
104
  "zod": "^4.3.6"
100
105
  },