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,162 @@
1
+ // Pure-SDF reference for the variableSweep taper test.
2
+ // If this renders with clean sidewalls while variable-sweep-test.forge.js does not,
3
+ // the artifact is in variableSweep()'s spine field construction rather than the
4
+ // shared SDF meshing pipeline.
5
+
6
+ const controlPoints = [
7
+ [0, 0, 0],
8
+ [20, 0, 10],
9
+ [40, 10, 20],
10
+ [60, 10, 30],
11
+ ];
12
+
13
+ const spine = spline3d(controlPoints, { tension: 0.4 });
14
+ const pathPoints = spine.sample(160);
15
+
16
+ const cumulativeLengths = [0];
17
+ for (let index = 1; index < pathPoints.length; index += 1) {
18
+ const [ax, ay, az] = pathPoints[index - 1];
19
+ const [bx, by, bz] = pathPoints[index];
20
+ cumulativeLengths.push(
21
+ cumulativeLengths[cumulativeLengths.length - 1] + Math.hypot(bx - ax, by - ay, bz - az),
22
+ );
23
+ }
24
+ const totalLength = cumulativeLengths[cumulativeLengths.length - 1];
25
+
26
+ const smallRadius = 3;
27
+ const largeRadius = 8;
28
+ const maxRadius = largeRadius;
29
+ const pad = maxRadius + 4;
30
+
31
+ let minX = Infinity;
32
+ let minY = Infinity;
33
+ let minZ = Infinity;
34
+ let maxX = -Infinity;
35
+ let maxY = -Infinity;
36
+ let maxZ = -Infinity;
37
+ for (const [x, y, z] of pathPoints) {
38
+ minX = Math.min(minX, x);
39
+ minY = Math.min(minY, y);
40
+ minZ = Math.min(minZ, z);
41
+ maxX = Math.max(maxX, x);
42
+ maxY = Math.max(maxY, y);
43
+ maxZ = Math.max(maxZ, z);
44
+ }
45
+
46
+ const embeddedPath = JSON.stringify(pathPoints);
47
+ const embeddedArc = JSON.stringify(cumulativeLengths);
48
+
49
+ const pureSweepSdf = new Function(`
50
+ return (x, y, z) => (() => {
51
+ const pts = ${embeddedPath};
52
+ const arc = ${embeddedArc};
53
+ const totalLength = ${totalLength};
54
+ const smallRadius = ${smallRadius};
55
+ const largeRadius = ${largeRadius};
56
+
57
+ function clamp(v, lo, hi) {
58
+ return Math.max(lo, Math.min(hi, v));
59
+ }
60
+
61
+ function catmullScalar(p0, p1, p2, p3, t0, t1, t2, t3, t) {
62
+ const dt = Math.max(t2 - t1, 1e-9);
63
+ const local = clamp((t - t1) / dt, 0, 1);
64
+ const tt = local * local;
65
+ const ttt = tt * local;
66
+ const m1 = (p2 - p0) / Math.max(t2 - t0, 1e-9);
67
+ const m2 = (p3 - p1) / Math.max(t3 - t1, 1e-9);
68
+ const h00 = 2 * ttt - 3 * tt + 1;
69
+ const h10 = ttt - 2 * tt + local;
70
+ const h01 = -2 * ttt + 3 * tt;
71
+ const h11 = ttt - tt;
72
+ return h00 * p1 + h10 * dt * m1 + h01 * p2 + h11 * dt * m2;
73
+ }
74
+
75
+ function radiusAt(t) {
76
+ const tc = clamp(t, 0, 1);
77
+ if (tc <= 0) return smallRadius;
78
+ if (tc >= 1) return smallRadius;
79
+ if (tc <= 0.5) return catmullScalar(smallRadius, smallRadius, largeRadius, smallRadius, -0.5, 0, 0.5, 1, tc);
80
+ return catmullScalar(smallRadius, largeRadius, smallRadius, smallRadius, 0, 0.5, 1, 1.5, tc);
81
+ }
82
+
83
+ let bestDist2 = Infinity;
84
+ let bestIndex = 0;
85
+ let bestSegmentT = 0;
86
+ let bestPoint = pts[0];
87
+
88
+ for (let index = 0; index < pts.length - 1; index += 1) {
89
+ const a = pts[index];
90
+ const b = pts[index + 1];
91
+ const dx = b[0] - a[0];
92
+ const dy = b[1] - a[1];
93
+ const dz = b[2] - a[2];
94
+ const len2 = dx * dx + dy * dy + dz * dz;
95
+ if (len2 < 1e-12) continue;
96
+
97
+ const px = x - a[0];
98
+ const py = y - a[1];
99
+ const pz = z - a[2];
100
+ const segT = clamp((px * dx + py * dy + pz * dz) / len2, 0, 1);
101
+ const qx = a[0] + dx * segT;
102
+ const qy = a[1] + dy * segT;
103
+ const qz = a[2] + dz * segT;
104
+ const rx = x - qx;
105
+ const ry = y - qy;
106
+ const rz = z - qz;
107
+ const dist2 = rx * rx + ry * ry + rz * rz;
108
+
109
+ if (dist2 < bestDist2) {
110
+ bestDist2 = dist2;
111
+ bestIndex = index;
112
+ bestSegmentT = segT;
113
+ bestPoint = [qx, qy, qz];
114
+ }
115
+ }
116
+
117
+ const radiusT = (arc[bestIndex] + (arc[bestIndex + 1] - arc[bestIndex]) * bestSegmentT) / Math.max(totalLength, 1e-9);
118
+ let sdfValue = Math.sqrt(bestDist2) - radiusAt(radiusT);
119
+
120
+ if (bestIndex === 0 && bestSegmentT <= 1e-6) {
121
+ const a = pts[0];
122
+ const b = pts[1];
123
+ const dx = b[0] - a[0];
124
+ const dy = b[1] - a[1];
125
+ const dz = b[2] - a[2];
126
+ const len = Math.sqrt(dx * dx + dy * dy + dz * dz);
127
+ if (len > 1e-9) {
128
+ const tx = dx / len;
129
+ const ty = dy / len;
130
+ const tz = dz / len;
131
+ const startCap = -((x - a[0]) * tx + (y - a[1]) * ty + (z - a[2]) * tz);
132
+ sdfValue = Math.max(sdfValue, startCap);
133
+ }
134
+ } else if (bestIndex === pts.length - 2 && bestSegmentT >= 1 - 1e-6) {
135
+ const a = pts[pts.length - 2];
136
+ const b = pts[pts.length - 1];
137
+ const dx = b[0] - a[0];
138
+ const dy = b[1] - a[1];
139
+ const dz = b[2] - a[2];
140
+ const len = Math.sqrt(dx * dx + dy * dy + dz * dz);
141
+ if (len > 1e-9) {
142
+ const tx = dx / len;
143
+ const ty = dy / len;
144
+ const tz = dz / len;
145
+ const endCap = (x - b[0]) * tx + (y - b[1]) * ty + (z - b[2]) * tz;
146
+ sdfValue = Math.max(sdfValue, endCap);
147
+ }
148
+ }
149
+
150
+ return sdfValue;
151
+ })();
152
+ `)();
153
+
154
+ const tapered = sdf.fromFunction(
155
+ pureSweepSdf,
156
+ {
157
+ min: [minX - pad, minY - pad, minZ - pad],
158
+ max: [maxX + pad, maxY + pad, maxZ + pad],
159
+ },
160
+ ).toShape({ edgeLength: 0.8 });
161
+
162
+ return tapered.color('#8899aa');
@@ -8,8 +8,8 @@ const spine = spline3d([
8
8
  [60, 10, 30],
9
9
  ], { tension: 0.4 });
10
10
 
11
- const smallCircle = circle2d(3, 24);
12
- const largeCircle = circle2d(8, 24);
11
+ const smallCircle = circle2d(3);
12
+ const largeCircle = circle2d(8);
13
13
 
14
14
  const tapered = variableSweep(spine, [
15
15
  { t: 0.0, profile: smallCircle },
@@ -0,0 +1,60 @@
1
+ // Wood Joinery — dado, rabbet, mortise & tenon
2
+ // Demonstrates joint cuts and assembled positioning.
3
+
4
+ const t = 18; // stock thickness
5
+
6
+ // ─── BOOKCASE CORNER ───────────────────────────────────────────
7
+ // Side panel lies flat: width=250 (X, depth), height=400 (Y, vertical), thickness=18 (Z, face)
8
+ const side = Wood.board(250, 400, t, { species: 'birch' });
9
+ const shelf = Wood.board(250, 300, t, { species: 'birch' });
10
+
11
+ // Dado: channel across the face of side for shelf to sit in
12
+ Wood.dado(side, shelf, { fromBottom: 150 });
13
+
14
+ // Rabbet: step on the back face for a back panel
15
+ Wood.rabbet(side, { edge: 'back', width: 6, depth: 9 });
16
+
17
+ // Assemble the bookcase corner:
18
+ // Side panel stands upright — rotate so Y (400mm) becomes vertical (Z)
19
+ const sideAssembled = side.shape.rotateX(90).color('#c4956a');
20
+
21
+ // Shelf sits in the dado channel
22
+ // After side.rotateX(90): side is in XZ plane, dado channel is at Z = -200 + 150 + 9 = -41
23
+ // Shelf needs its 18mm thickness edge in the dado
24
+ const shelfAssembled = shelf.shape
25
+ .rotateX(90) // stand upright
26
+ .rotateZ(90) // perpendicular to side
27
+ .translate(0, t, -200 + 150 + t / 2)
28
+ .color('#b8864e');
29
+
30
+ // ─── TABLE JOINT: MORTISE & TENON ──────────────────────────────
31
+ // Leg: width=45 (X), height=400 (Y, vertical), thickness=45 (Z, face for mortise)
32
+ // The mortise cuts into Z (45mm face), positioned along Y (400mm height)
33
+ const leg = Wood.board(45, 400, 45, { species: 'oak' });
34
+ const apron = Wood.board(300, 80, 20, { species: 'oak' });
35
+
36
+ Wood.mortiseAndTenon(leg, apron, {
37
+ style: 'blind',
38
+ position: { fromTop: 25 },
39
+ cornerRadius: 4,
40
+ });
41
+
42
+ // Tenon defaults: thickness = 20/3 ≈ 6.7mm, width = min(48, 320) = 48mm, length = 45*2/3 = 30mm
43
+
44
+ // Assemble: leg stands on its Y-axis (already vertical), apron connects to face
45
+ const legAssembled = leg.shape
46
+ .translate(500, 0, 0)
47
+ .color('#a07040');
48
+
49
+ // Apron: tenon protrudes from +X end. Rotate so it points into the leg's Z-face.
50
+ const apronAssembled = apron.shape
51
+ .rotateY(90) // tenon now points +Z (into leg face)
52
+ .translate(500, 200 - 25 - 24, 45) // align with mortise position on leg
53
+ .color('#8b6030');
54
+
55
+ return [
56
+ { name: 'Side (dado + rabbet)', shape: sideAssembled },
57
+ { name: 'Shelf', shape: shelfAssembled },
58
+ { name: 'Leg (mortise)', shape: legAssembled },
59
+ { name: 'Apron (tenon)', shape: apronAssembled },
60
+ ];
@@ -1,9 +1,9 @@
1
1
  // Guard part: shell + face-driven cuts + mirrored feet in one ordinary enclosure workflow.
2
2
 
3
- const base = roundedRect(120, 80, 10, true).extrude(36);
3
+ const base = roundedRect(120, 80, 10).extrude(36);
4
4
  const shell = base.shell(3, { openFaces: ['top'] });
5
5
 
6
- const displayCut = roundedRect(34, 18, 3, true)
6
+ const displayCut = roundedRect(34, 18, 3)
7
7
  .onFace(base, 'front', { u: 0, v: 8, protrude: 0.25, selfAnchor: 'center' })
8
8
  .extrude(10);
9
9
 
@@ -11,7 +11,7 @@ const cableCut = circle2d(7)
11
11
  .onFace(base, 'right', { u: -12, v: -8, protrude: 0.25, selfAnchor: 'center' })
12
12
  .extrude(10);
13
13
 
14
- const foot = roundedRect(18, 18, 4, true)
14
+ const foot = roundedRect(18, 18, 4)
15
15
  .onFace(base, 'bottom', { u: 36, v: 20, protrude: 0, selfAnchor: 'center' })
16
16
  .extrude(6);
17
17
 
@@ -1,11 +1,11 @@
1
1
  // Guard part: counterbore/countersink hole variants plus an up-to-face service pocket in one ordinary plate workflow.
2
2
 
3
- const plate = roundedRect(118, 76, 8, true).extrude(18);
3
+ const plate = roundedRect(118, 76, 8).extrude(18);
4
4
  const entryFace = plate.face('top');
5
5
  const exitFace = plate.face('bottom');
6
6
  const pocketExitFace = plate.face('side-top');
7
7
 
8
- const servicePocket = roundedRect(34, 18, 4, true)
8
+ const servicePocket = roundedRect(34, 18, 4)
9
9
  .onFace(plate, 'front', { u: 0, v: -2, selfAnchor: 'center' });
10
10
 
11
11
  const fastenerPlate = plate
@@ -21,7 +21,7 @@ sk.horizontal(bottom);
21
21
  sk.vertical(right);
22
22
  sk.parallel(bottom, top);
23
23
  sk.parallel(right, left);
24
- sk.length(bottom, param("width", 40, { unit: "mm" }));
25
- sk.length(right, param("height", 30, { unit: "mm" }));
24
+ sk.length(bottom, Param.number("width", 40, { unit: "mm" }));
25
+ sk.length(right, Param.number("height", 30, { unit: "mm" }));
26
26
 
27
27
  return sk.solve();
@@ -17,6 +17,6 @@ sk.addLoop([p1, p2, p3]);
17
17
  // Only constrain shape, not position
18
18
  sk.equal(l1, l2);
19
19
  sk.equal(l1, l3);
20
- sk.length(l1, param("side", 20, { unit: "mm" }));
20
+ sk.length(l1, Param.number("side", 20, { unit: "mm" }));
21
21
 
22
22
  return sk.solve();
@@ -24,7 +24,7 @@ sk.absoluteAngle(lBottom, 0); // line must be at 0 degrees (= horizontal)
24
24
  sk.vertical(lRight);
25
25
  sk.vertical(lLeft);
26
26
  sk.horizontal(lTop);
27
- sk.length(lBottom, param("width", 10, { unit: "mm" }));
28
- sk.length(lRight, param("height", 5, { unit: "mm" }));
27
+ sk.length(lBottom, Param.number("width", 10, { unit: "mm" }));
28
+ sk.length(lRight, Param.number("height", 5, { unit: "mm" }));
29
29
 
30
30
  return sk.solve();
@@ -20,10 +20,10 @@ sk.addLoop([p1, p2, p4, p3]);
20
20
  sk.fix(p1);
21
21
  sk.horizontal(bottom);
22
22
  sk.vertical(left);
23
- sk.length(bottom, param("width", 20, { unit: "mm" }));
23
+ sk.length(bottom, Param.number("width", 20, { unit: "mm" }));
24
24
 
25
25
  // lineDistance already implies parallel:
26
- sk.lineDistance(bottom, top, param("height", 10, { unit: "mm" }));
26
+ sk.lineDistance(bottom, top, Param.number("height", 10, { unit: "mm" }));
27
27
  sk.parallel(bottom, top); // redundant — lineDistance already forces parallel
28
28
 
29
29
  sk.parallel(left, right);
@@ -1,4 +1,4 @@
1
- const width = param('width', 39);
1
+ const width = Param.number('width', 39);
2
2
 
3
3
  /** Create an open polyline of `count` segments from startPoint to endPoint. */
4
4
  function polyline(sk, startPoint, endPoint, count) {
@@ -20,9 +20,9 @@ sk.absoluteAngle(l1, 0);
20
20
  sk.perpendicular(l1, l2);
21
21
  sk.perpendicular(l2, l3);
22
22
  sk.perpendicular(l3, l4);
23
- sk.length(l1, param("seg1", 20, { unit: "mm" }));
24
- sk.length(l2, param("seg2", 15, { unit: "mm" }));
25
- sk.length(l3, param("seg3", 15, { unit: "mm" }));
26
- sk.length(l4, param("seg4", 15, { unit: "mm" }));
23
+ sk.length(l1, Param.number("seg1", 20, { unit: "mm" }));
24
+ sk.length(l2, Param.number("seg2", 15, { unit: "mm" }));
25
+ sk.length(l3, Param.number("seg3", 15, { unit: "mm" }));
26
+ sk.length(l4, Param.number("seg4", 15, { unit: "mm" }));
27
27
 
28
28
  return sk.solve();
@@ -47,9 +47,9 @@ sk.vertical(lLeft);
47
47
  sk.horizontal(lLeftTop);
48
48
  sk.vertical(lLeftUp);
49
49
 
50
- sk.length(lBottom, param("base_width", 50, { unit: "mm" }));
51
- sk.length(lLeft, param("leg_height", 20, { unit: "mm" }));
52
- sk.length(lLeftTop, param("step_width", 15, { unit: "mm" }));
53
- sk.length(lLeftUp, param("top_height", 20, { unit: "mm" }));
50
+ sk.length(lBottom, Param.number("base_width", 50, { unit: "mm" }));
51
+ sk.length(lLeft, Param.number("leg_height", 20, { unit: "mm" }));
52
+ sk.length(lLeftTop, Param.number("step_width", 15, { unit: "mm" }));
53
+ sk.length(lLeftUp, Param.number("top_height", 20, { unit: "mm" }));
54
54
 
55
55
  return sk.solve();
@@ -6,7 +6,7 @@
6
6
  const sk = constrainedSketch();
7
7
 
8
8
  const N = 50; // number of segments
9
- const SEG_LEN = param("seg_len", 5, { unit: "mm" });
9
+ const SEG_LEN = Param.number("seg_len", 5, { unit: "mm" });
10
10
 
11
11
  // Build a spiral: each segment turns 90° left from the previous,
12
12
  // with increasing runs (1,1,2,2,3,3,4,4,...) to form a square spiral.
@@ -8,7 +8,7 @@ const sk = constrainedSketch();
8
8
 
9
9
  const COLS = 3;
10
10
  const ROWS = 2;
11
- const SIDE = param("cell_size", 8, { unit: "mm" });
11
+ const SIDE = Param.number("cell_size", 8, { unit: "mm" });
12
12
 
13
13
  // Flat-top hex: vertex 0 at 0° (right), CCW
14
14
  const vAngles = [0, 60, 120, 180, 240, 300].map(d => d * Math.PI / 180);
@@ -6,8 +6,8 @@
6
6
 
7
7
  const sk = constrainedSketch();
8
8
 
9
- const W = param("width", 150, { unit: "mm" });
10
- const H = param("height", 100, { unit: "mm" });
9
+ const W = Param.number("width", 150, { unit: "mm" });
10
+ const H = Param.number("height", 100, { unit: "mm" });
11
11
 
12
12
  // Outer box corners
13
13
  const p00 = sk.point(0, 0);
@@ -6,10 +6,10 @@
6
6
 
7
7
  const sk = constrainedSketch();
8
8
 
9
- const OW = param("outer_w", 120, { unit: "mm" });
10
- const OH = param("outer_h", 100, { unit: "mm" });
11
- const IW = param("inner_w", 60, { unit: "mm" });
12
- const IH = param("inner_h", 50, { unit: "mm" });
9
+ const OW = Param.number("outer_w", 120, { unit: "mm" });
10
+ const OH = Param.number("outer_h", 100, { unit: "mm" });
11
+ const IW = Param.number("inner_w", 60, { unit: "mm" });
12
+ const IH = Param.number("inner_h", 50, { unit: "mm" });
13
13
 
14
14
  // Outer rectangle
15
15
  const o1 = sk.point(0, 0);
@@ -5,7 +5,7 @@
5
5
 
6
6
  const sk = constrainedSketch();
7
7
 
8
- const SIZE = param("size", 60, { unit: "mm" });
8
+ const SIZE = Param.number("size", 60, { unit: "mm" });
9
9
 
10
10
  // L-shape vertices (going CCW)
11
11
  // (0,60)────(60,60)
@@ -0,0 +1,12 @@
1
+ // Bearing housing with exact circular arc (not polyline-approximated)
2
+ const profile = path()
3
+ .moveTo(0, 0)
4
+ .lineTo(50, 0)
5
+ .lineTo(50, 10)
6
+ .exactArcTo(40, 20, { radius: 10 })
7
+ .lineTo(0, 20)
8
+ .close();
9
+
10
+ const housing = profile.extrude(30);
11
+ const bore = cylinder(30, 8).translate(25, 10, 0);
12
+ return difference(housing, bore);
@@ -0,0 +1,53 @@
1
+ // Smooth drone arm: flat rectangular mount → organic taper → circular motor mount
2
+ // Tests: cross-section morphing, S-curve sweep, shell, G2 transitions
3
+
4
+ // --- Spine: gentle S-curve from origin to motor mount ---
5
+ const armLength = 120;
6
+ const spine = spline3d([
7
+ [0, 0, 0],
8
+ [armLength * 0.3, 0, 8],
9
+ [armLength * 0.7, 0, -5],
10
+ [armLength, 0, 3],
11
+ ], { tension: 0.3 });
12
+
13
+ // --- Cross-sections ---
14
+
15
+ // Mount end: flat rectangle with rounded corners
16
+ const mountProfile = roundedRect(20, 6, 1.5);
17
+
18
+ // Mid-section: transitional rounded rectangle
19
+ const midProfile = roundedRect(14, 10, 4);
20
+
21
+ // Motor end: circle
22
+ const motorProfile = circle2d(8);
23
+
24
+ // --- Variable sweep: morph between profiles along spine ---
25
+ const solidArm = variableSweep(spine, [
26
+ { t: 0, profile: mountProfile },
27
+ { t: 0.4, profile: midProfile },
28
+ { t: 1, profile: motorProfile },
29
+ ], { edgeLength: 1.5 });
30
+
31
+ // --- Hollow it out ---
32
+ const arm = solidArm.shell(1.5);
33
+
34
+ // --- Mounting holes at the flat end ---
35
+ const mountHole = cylinder(10, 2).translate(0, 0, -5);
36
+ const mountHoles = union(
37
+ mountHole.translate(-6, 0, 0),
38
+ mountHole.translate(6, 0, 0),
39
+ );
40
+
41
+ // --- Motor mount ring at the far end ---
42
+ // Place at the end of the spine
43
+ const motorRing = difference(
44
+ cylinder(4, 10),
45
+ cylinder(4, 7),
46
+ ).translate(armLength, 0, 3);
47
+
48
+ const result = difference(
49
+ union(arm, motorRing),
50
+ mountHoles,
51
+ );
52
+
53
+ return result;
@@ -5,19 +5,19 @@
5
5
  // BUILD NOTES (at bottom of file):
6
6
  // Materials, dimensions, and assembly instructions for actually building this.
7
7
 
8
- const topW = param("Top Width", 120, { min: 80, max: 200, unit: "cm" });
9
- const topD = param("Top Depth", 60, { min: 40, max: 100, unit: "cm" });
10
- const topThick = param("Top Thickness", 3, { min: 2, max: 5, unit: "cm" });
11
- const minH = param("Min Height", 72, { min: 60, max: 80, unit: "cm" });
12
- const maxH = param("Max Height", 120, { min: 100, max: 140, unit: "cm" });
13
- const heightPct = param("Height %", 30, { min: 0, max: 100, unit: "%" });
14
- const outerLeg = param("Outer Leg", 6, { min: 4, max: 10, unit: "cm" });
15
- const innerLeg = param("Inner Leg", 4, { min: 3, max: 8, unit: "cm" });
16
- const legWall = param("Leg Wall", 0.3, { min: 0.2, max: 0.5, unit: "cm" });
17
- const inset = param("Leg Inset", 5, { min: 2, max: 15, unit: "cm" });
18
- const braceW = param("Brace Width", 3, { min: 2, max: 5, unit: "cm" });
19
- const braceH = param("Brace Height", 1.5, { min: 1, max: 3, unit: "cm" });
20
- const crankR = param("Crank Radius", 8, { min: 5, max: 15, unit: "cm" });
8
+ const topW = Param.number("Top Width", 120, { min: 80, max: 200, unit: "cm" });
9
+ const topD = Param.number("Top Depth", 60, { min: 40, max: 100, unit: "cm" });
10
+ const topThick = Param.number("Top Thickness", 3, { min: 2, max: 5, unit: "cm" });
11
+ const minH = Param.number("Min Height", 72, { min: 60, max: 80, unit: "cm" });
12
+ const maxH = Param.number("Max Height", 120, { min: 100, max: 140, unit: "cm" });
13
+ const heightPct = Param.number("Height %", 30, { min: 0, max: 100, unit: "%" });
14
+ const outerLeg = Param.number("Outer Leg", 6, { min: 4, max: 10, unit: "cm" });
15
+ const innerLeg = Param.number("Inner Leg", 4, { min: 3, max: 8, unit: "cm" });
16
+ const legWall = Param.number("Leg Wall", 0.3, { min: 0.2, max: 0.5, unit: "cm" });
17
+ const inset = Param.number("Leg Inset", 5, { min: 2, max: 15, unit: "cm" });
18
+ const braceW = Param.number("Brace Width", 3, { min: 2, max: 5, unit: "cm" });
19
+ const braceH = Param.number("Brace Height", 1.5, { min: 1, max: 3, unit: "cm" });
20
+ const crankR = Param.number("Crank Radius", 8, { min: 5, max: 15, unit: "cm" });
21
21
 
22
22
  // Current height based on slider
23
23
  const currentH = minH + (maxH - minH) * heightPct / 100;
@@ -75,7 +75,7 @@ for (const [lx, ly] of legPositions) {
75
75
  // Through-hole in Y direction
76
76
  pinHoles.push(
77
77
  cylinder(outerLeg + 2, pinHoleR)
78
- .rotateAround([0, 0, 0], 90, 0, 0)
78
+ .rotateX(90)
79
79
  .translate(legCenterX, ly - 1, pz)
80
80
  );
81
81
  }
@@ -105,7 +105,7 @@ const shaftY = inset + outerLeg / 2;
105
105
  const shaftZ = legH - outerLegH + 25;
106
106
  const shaftLen = topW - 2 * inset;
107
107
  const shaft = cylinder(shaftLen, 0.8)
108
- .rotateAround([0, 0, 0], 0, 90, 0)
108
+ .rotateY(90)
109
109
  .translate(inset, shaftY, shaftZ);
110
110
 
111
111
  // Crank handle (on the right side)