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.
- package/README.md +3 -12
- package/dist/assets/{AdminPage-CeqCUUgu.js → AdminPage-D4bocK4E.js} +250 -151
- package/dist/assets/{BlogPage-P_AJP0v9.js → BlogPage-CJEXL_zJ.js} +94 -70
- package/dist/assets/{DocsPage-CKRV2iq2.js → DocsPage-D3A_g8V3.js} +329 -163
- package/dist/assets/{EditorApp-CnC2k4cW.css → EditorApp-BWYUSpUN.css} +590 -136
- package/dist/assets/EditorApp-Cihhqcsq.js +11692 -0
- package/dist/assets/{EmbedViewer-DBlzmQ5i.js → EmbedViewer-kWjKaC_t.js} +2 -4
- package/dist/assets/LandingPageProofDriven-Bg2IUc3l.css +856 -0
- package/dist/assets/LandingPageProofDriven-DXkKlyhI.js +601 -0
- package/dist/assets/PricingPage-BsU5vzEx.js +232 -0
- package/dist/assets/{SettingsPage-BqCh9JcC.js → SettingsPage-PqvpAKIs.js} +129 -5
- package/dist/assets/{evalWorker-Ql-aKwLA.js → evalWorker-C-hzNUMy.js} +8949 -3161
- package/dist/assets/{Viewport-CoB46f5R.js → index-Pz321YAt.js} +38382 -7501
- package/dist/assets/{index-2hfs_ub0.css → index-ay13WNfa.css} +726 -53
- package/dist/assets/{javascript-DCxGoE5Y.js → javascript-DAl8Gmyo.js} +1 -1
- package/dist/assets/{manifold-CqNMHHKO.js → manifold-BcbjWLIo.js} +4 -3
- package/dist/assets/{manifold-Cce9wRFz.js → manifold-DBckbFgx.js} +1 -1
- package/dist/assets/{manifold-D6BeHIOo.js → manifold-O2AAGXyj.js} +1 -1
- package/dist/assets/{reportWorker-sFEFonXf.js → reportWorker-Dxr-5A7w.js} +8760 -3559
- package/dist/assets/{vendor-react-Dt7-aaJH.js → vendor-react-CG3i_wp0.js} +65 -8
- package/dist/docs/index.html +2 -2
- package/dist/docs-raw/CLI.md +341 -718
- package/dist/docs-raw/generated/assembly.md +699 -112
- package/dist/docs-raw/generated/concepts.md +1834 -1346
- package/dist/docs-raw/generated/core.md +1012 -1059
- package/dist/docs-raw/generated/curves.md +759 -116
- package/dist/docs-raw/generated/lib.md +43 -748
- package/dist/docs-raw/generated/output.md +139 -245
- package/dist/docs-raw/generated/sdf.md +208 -0
- package/dist/docs-raw/generated/sheet-metal.md +473 -21
- package/dist/docs-raw/generated/sketch.md +1518 -362
- package/dist/docs-raw/generated/viewport.md +368 -299
- package/dist/docs-raw/generated/wood.md +104 -0
- package/dist/index.html +2 -2
- package/dist/landing/proof-ams-adapter.png +0 -0
- package/dist/landing/proof-bolt-and-nut.png +0 -0
- package/dist/landing/proof-fillet-enclosure.png +0 -0
- package/dist/landing/proof-glasses.png +0 -0
- package/dist/landing/proof-gyroid.png +0 -0
- package/dist/sitemap.xml +6 -6
- package/dist-cli/forgecad.js +12321 -5700
- package/dist-cli/forgecad.js.map +1 -0
- package/dist-cli/solver-46FFSK2U.js +363 -0
- package/dist-cli/solver-46FFSK2U.js.map +1 -0
- package/dist-skill/CONTEXT.md +4890 -6302
- package/dist-skill/SKILL-dev.md +22 -66
- package/dist-skill/SKILL.md +20 -59
- package/dist-skill/docs/API/core/concepts.md +37 -92
- package/dist-skill/docs/CLI.md +341 -718
- package/dist-skill/docs/generated/assembly.md +699 -112
- package/dist-skill/docs/generated/core.md +1012 -1059
- package/dist-skill/docs/generated/curves.md +759 -116
- package/dist-skill/docs/generated/lib.md +43 -748
- package/dist-skill/docs/generated/output.md +139 -245
- package/dist-skill/docs/generated/sdf.md +208 -0
- package/dist-skill/docs/generated/sheet-metal.md +473 -21
- package/dist-skill/docs/generated/sketch.md +1518 -362
- package/dist-skill/docs/generated/viewport.md +368 -299
- package/dist-skill/docs/generated/wood.md +104 -0
- package/dist-skill/docs/guides/coordinate-system.md +11 -17
- package/dist-skill/docs/guides/geometry-conventions.md +13 -70
- package/dist-skill/docs/guides/joint-design.md +78 -0
- package/dist-skill/docs/guides/modeling-recipes.md +22 -195
- package/dist-skill/docs/guides/positioning.md +88 -147
- package/dist-skill/docs-dev/API/core/concepts.md +78 -0
- package/dist-skill/docs-dev/CLI.md +488 -0
- package/dist-skill/{docs → docs-dev}/blueprint-first.md +5 -0
- package/dist-skill/{docs → docs-dev}/coding-best-practices.md +6 -8
- package/dist-skill/{docs → docs-dev}/coding.md +2 -4
- package/dist-skill/docs-dev/component-model.md +164 -0
- package/dist-skill/docs-dev/generated/assembly.md +779 -0
- package/dist-skill/docs-dev/generated/core.md +1676 -0
- package/dist-skill/docs-dev/generated/curves.md +855 -0
- package/dist-skill/docs-dev/generated/lib.md +55 -0
- package/dist-skill/docs-dev/generated/output.md +234 -0
- package/dist-skill/docs-dev/generated/sdf.md +208 -0
- package/dist-skill/docs-dev/generated/sheet-metal.md +506 -0
- package/dist-skill/docs-dev/generated/sketch.md +1753 -0
- package/dist-skill/docs-dev/generated/viewport.md +513 -0
- package/dist-skill/docs-dev/generated/wood.md +104 -0
- package/dist-skill/docs-dev/guides/coordinate-system.md +46 -0
- package/dist-skill/docs-dev/guides/geometry-conventions.md +52 -0
- package/dist-skill/docs-dev/guides/joint-design.md +78 -0
- package/dist-skill/docs-dev/guides/modeling-recipes.md +77 -0
- package/dist-skill/docs-dev/guides/positioning.md +151 -0
- package/dist-skill/{docs → docs-dev}/guides/skill-maintenance.md +21 -10
- package/dist-skill/{docs → docs-dev}/internals/compiler.md +5 -6
- package/dist-skill/{docs → docs-dev}/internals/constraint-solver-quality.md +0 -1
- package/dist-skill/{docs → docs-dev}/internals/constraint-solver.md +0 -1
- package/dist-skill/{docs → docs-dev}/internals/sketch-2d-pipeline.md +2 -3
- package/examples/api/attachTo-basics.forge.js +8 -8
- package/examples/api/bill-of-materials.forge.js +9 -9
- package/examples/api/bolt-pattern.forge.js +5 -5
- package/examples/api/boolean-operations.forge.js +5 -5
- package/examples/api/bounding-box-visualizer.forge.js +3 -3
- package/examples/api/clone-duplicate.forge.js +2 -2
- package/examples/api/colors-union-vs-array.forge.js +6 -6
- package/examples/api/connector-assembly.forge.js +8 -6
- package/examples/api/connector-basics.forge.js +7 -7
- package/examples/api/constrained-sketch-mechanical.forge.js +4 -4
- package/examples/api/elbow-test.forge.js +3 -3
- package/examples/api/extrude-options.forge.js +8 -14
- package/examples/api/feature-created-faces.forge.js +6 -10
- package/examples/api/fillet-showcase.forge.js +2 -2
- package/examples/api/folded-service-panel-cover.forge.js +2 -2
- package/examples/api/gears-tier1.forge.js +5 -5
- package/examples/api/group-test.forge.js +3 -3
- package/examples/api/group-vs-union.forge.js +1 -1
- package/examples/api/highlight-debug.forge.js +4 -0
- package/examples/api/js-module-pillars.js +1 -1
- package/examples/api/js-module-scene.js +2 -2
- package/examples/api/mesh-import-slats.forge.js +4 -4
- package/examples/api/patterns.forge.js +3 -3
- package/examples/api/pointAlong-orientation.forge.js +3 -3
- package/examples/api/profile-2020-b-slot6.forge.js +4 -5
- package/examples/api/route-perimeter-flange.forge.js +1 -1
- package/examples/api/sdf-rover-demo.forge.js +10 -10
- package/examples/api/sketch-on-face-demo.forge.js +2 -2
- package/examples/api/sketch-regions.forge.js +4 -4
- package/examples/api/sketch-rounding-strategies.forge.js +1 -1
- package/examples/api/smooth-curve-connections.forge.js +1 -1
- package/examples/api/transition-curves.forge.js +4 -4
- package/examples/api/variable-sweep-pure-sdf-test.forge.js +162 -0
- package/examples/api/variable-sweep-test.forge.js +2 -2
- package/examples/api/wood-joinery.forge.js +60 -0
- package/examples/compiler-corpus/enclosure-shell-cuts.forge.js +3 -3
- package/examples/compiler-corpus/fastener-plate-variants.forge.js +2 -2
- package/examples/constraints/01-fully-constrained-rect.forge.js +2 -2
- package/examples/constraints/02-underconstrained-triangle.forge.js +1 -1
- package/examples/constraints/03-redundant-constraints.forge.js +2 -2
- package/examples/constraints/05-parallel-with-linedistance.forge.js +2 -2
- package/examples/constraints/06-complex-spectrogram.forge.js +1 -1
- package/examples/constraints/07-perpendicular-chain.forge.js +4 -4
- package/examples/constraints/08-symmetric-bracket.forge.js +4 -4
- package/examples/constraints/09-stress-spiral.forge.js +1 -1
- package/examples/constraints/10-stress-honeycomb.forge.js +1 -1
- package/examples/constraints/11-surface-grid.forge.js +2 -2
- package/examples/constraints/12-surface-nested.forge.js +4 -4
- package/examples/constraints/13-surface-complex.forge.js +1 -1
- package/examples/exact-arc-housing.forge.js +12 -0
- package/examples/experiments/drone-arm.forge.js +53 -0
- package/examples/furniture/adjustable-table.forge.js +15 -15
- package/examples/furniture/bathroom.forge.js +26 -26
- package/examples/furniture/chair.forge.js +13 -13
- package/examples/furniture/picture-frame.forge.js +6 -6
- package/examples/furniture/shoe-rack-doors.forge.js +8 -8
- package/examples/furniture/shoe-rack.forge.js +7 -7
- package/examples/furniture/table-lamp.forge.js +8 -8
- package/examples/gcode/lissajous-vase.forge.js +4 -4
- package/examples/gcode/math-surface.forge.js +3 -3
- package/examples/gcode/parametric-vase.forge.js +4 -4
- package/examples/gcode/spiral-tower.forge.js +4 -4
- package/examples/generative/crystal-growth.forge.js +9 -9
- package/examples/generative/frost-spires.forge.js +9 -9
- package/examples/generative/golden-spiral-tower.forge.js +11 -11
- package/examples/generative/molten-forge.forge.js +6 -6
- package/examples/generative/neon-coral.forge.js +7 -7
- package/examples/mechanical/3d-printer.forge.js +37 -37
- package/examples/mechanical/5-finger-robot-hand.forge.js +19 -19
- package/examples/mechanical/airplane-propeller.forge.js +9 -9
- package/examples/mechanical/bolt-and-nut.forge.js +10 -10
- package/examples/mechanical/door-with-hinges.forge.js +7 -7
- package/examples/mechanical/fillet-enclosure.forge.js +15 -11
- package/examples/mechanical/headphone-hanger-v2.forge.js +11 -11
- package/examples/mechanical/robot_hand.forge.js +24 -24
- package/examples/mechanical/robot_hand_2.forge.js +26 -26
- package/examples/nurbs-surface.forge.js +8 -0
- package/examples/nurbs-tube.forge.js +7 -0
- package/examples/products/bottle.forge.js +8 -8
- package/examples/products/chess-set.forge.js +25 -25
- package/examples/products/classical-piano.forge.js +20 -20
- package/examples/products/clock.forge.js +33 -33
- package/examples/products/cup.forge.js +5 -5
- package/examples/products/iphone.forge.js +20 -20
- package/examples/products/laptop.forge.js +24 -24
- package/examples/products/laser-cut-box.forge.js +6 -6
- package/examples/products/laser-cut-tray.forge.js +6 -6
- package/examples/products/liquid-soap-dispenser.forge.js +23 -23
- package/examples/products/origami-fish.forge.js +14 -12
- package/examples/products/spiderman-cake.forge.js +6 -6
- package/examples/shelf/container.forge.js +5 -5
- package/examples/shelf/shelf-unit.forge.js +6 -6
- package/examples/toolbox/bolted-joint.forge.js +7 -7
- package/package.json +9 -4
- package/dist/assets/EditorApp-B-vQvgam.js +0 -9888
- package/dist/assets/LandingPage-C5n9hDXI.js +0 -322
- package/dist/assets/PublishedModelPage-Dt7PCVBj.js +0 -146
- package/dist/assets/__vite-browser-external-CURh0WXD.js +0 -8
- package/dist/assets/deserializeRunResult-BLAFoiE0.js +0 -19365
- package/dist/assets/index-1CYp3zUp.js +0 -1455
- package/dist-skill/docs/API/API.md +0 -1666
- package/dist-skill/docs/API/README.md +0 -37
- package/dist-skill/docs/API/assembly/assembly.md +0 -617
- package/dist-skill/docs/API/core/edge-queries.md +0 -130
- package/dist-skill/docs/API/core/parameters.md +0 -122
- package/dist-skill/docs/API/core/reserved-terms.md +0 -137
- package/dist-skill/docs/API/core/sdf.md +0 -326
- package/dist-skill/docs/API/core/skill-cli.md +0 -194
- package/dist-skill/docs/API/core/skill-guide.md +0 -205
- package/dist-skill/docs/API/core/specs.md +0 -186
- package/dist-skill/docs/API/core/topology.md +0 -372
- package/dist-skill/docs/API/entities.md +0 -268
- package/dist-skill/docs/API/output/bom.md +0 -58
- package/dist-skill/docs/API/output/brep-export.md +0 -87
- package/dist-skill/docs/API/output/dimensions.md +0 -67
- package/dist-skill/docs/API/output/export.md +0 -110
- package/dist-skill/docs/API/output/gcode.md +0 -195
- package/dist-skill/docs/API/runtime/viewport.md +0 -420
- package/dist-skill/docs/API/sheet-metal/sheet-metal.md +0 -185
- package/dist-skill/docs/API/sketch/anchor.md +0 -37
- package/dist-skill/docs/API/sketch/booleans.md +0 -91
- package/dist-skill/docs/API/sketch/core.md +0 -73
- package/dist-skill/docs/API/sketch/extrude.md +0 -62
- package/dist-skill/docs/API/sketch/on-face.md +0 -104
- package/dist-skill/docs/API/sketch/operations.md +0 -78
- package/dist-skill/docs/API/sketch/path.md +0 -75
- package/dist-skill/docs/API/sketch/primitives.md +0 -146
- package/dist-skill/docs/API/sketch/regions.md +0 -80
- package/dist-skill/docs/API/sketch/text.md +0 -108
- package/dist-skill/docs/API/sketch/transforms.md +0 -65
- package/dist-skill/docs/API/toolbox/fasteners.md +0 -129
- package/dist-skill/docs/INDEX.md +0 -94
- package/dist-skill/docs/RELEASING.md +0 -55
- package/dist-skill/docs/cli-monetization.md +0 -111
- package/dist-skill/docs/deployment.md +0 -281
- package/dist-skill/docs/generated/concepts.md +0 -2112
- package/dist-skill/docs/internals/shape-from-slices.md +0 -152
- package/dist-skill/docs/platform/admin.md +0 -45
- package/dist-skill/docs/platform/architecture.md +0 -79
- package/dist-skill/docs/platform/auth.md +0 -110
- package/dist-skill/docs/platform/email.md +0 -67
- package/dist-skill/docs/platform/projects.md +0 -111
- package/dist-skill/docs/platform/sharing.md +0 -90
- 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 =
|
|
6
|
-
const h =
|
|
7
|
-
const d =
|
|
8
|
-
const cornerR =
|
|
9
|
-
const edgeR =
|
|
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
|
|
15
|
-
let body = bodyProfile.extrude(d,
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
37
|
+
const camSize = Param.number("Camera Island", 36, { min: 25, max: 45, unit: "mm" });
|
|
38
38
|
const camR = 8;
|
|
39
|
-
const camBump =
|
|
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
|
|
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 =
|
|
51
|
-
const lensGap =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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.
|
|
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 =
|
|
5
|
-
const d =
|
|
6
|
-
const baseH =
|
|
7
|
-
const lidH =
|
|
8
|
-
const cornerR =
|
|
9
|
-
const screenInset =
|
|
10
|
-
const kbInsetX =
|
|
11
|
-
const kbInsetY =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
58
|
-
.
|
|
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
|
|
61
|
-
.
|
|
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
|
-
.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
.
|
|
115
|
+
.rotateX(180)
|
|
116
116
|
.translate(0, hingeY, hingeZ);
|
|
117
117
|
|
|
118
118
|
const screenAtHinge = screenPanel.translate(0, -hingeY, 0)
|
|
119
|
-
.
|
|
119
|
+
.rotateX(180)
|
|
120
120
|
.translate(0, hingeY, hingeZ);
|
|
121
121
|
|
|
122
|
-
const lidAngle =
|
|
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 =
|
|
24
|
-
const w =
|
|
25
|
-
const d =
|
|
26
|
-
const h =
|
|
27
|
-
const fold =
|
|
28
|
-
const kerf =
|
|
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 =
|
|
19
|
-
const w =
|
|
20
|
-
const d =
|
|
21
|
-
const h =
|
|
22
|
-
const fold =
|
|
23
|
-
const kerf =
|
|
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 =
|
|
5
|
-
const bodyR =
|
|
6
|
-
const wall =
|
|
7
|
-
const pumpStroke =
|
|
8
|
-
const progressPct =
|
|
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
|
|
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
|
|
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
|
|
51
|
-
const neckHole = cylinder(neckH + 6, chamberR - 2.5, chamberR - 2.5
|
|
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
|
|
56
|
+
const chamberOuter = cylinder(chamberH, chamberR, chamberR)
|
|
57
57
|
.translate(0, 0, chamberBottomZ + chamberH * 0.5);
|
|
58
|
-
const chamberInner = cylinder(chamberH - 4, chamberInnerR, chamberInnerR
|
|
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
|
|
64
|
-
const nozzleOuter = cylinder(nozzleL, nozzleR, nozzleR * 0.92
|
|
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
|
|
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
|
|
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
|
|
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
|
|
92
|
-
const ringInner = cylinder(2.4, chamberR - 5.3, chamberR - 5.3
|
|
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
|
|
102
|
-
const tubeInner = cylinder(tubeLen + 2, 1.2, 1.2
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
5
|
-
const paper =
|
|
6
|
-
const fold =
|
|
7
|
-
const tailSpread =
|
|
8
|
-
const tailDrop =
|
|
9
|
-
const showStand =
|
|
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.
|
|
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
|
|
24
|
+
.extrude(paper)
|
|
25
|
+
.translate(0, 0, -paper / 2)
|
|
25
26
|
.color(color)
|
|
26
|
-
.
|
|
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
|
|
34
|
+
.extrude(paper * 0.9)
|
|
35
|
+
.translate(0, 0, -paper * 0.45)
|
|
34
36
|
.color(color)
|
|
35
|
-
.
|
|
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
|
|
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 =
|
|
5
|
-
const baseRadius =
|
|
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
|
|
49
|
-
.
|
|
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
|
|
53
|
-
.
|
|
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 =
|
|
5
|
-
const d =
|
|
6
|
-
const h =
|
|
7
|
-
const wall =
|
|
8
|
-
const lipH =
|
|
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 =
|
|
5
|
-
const shelfD =
|
|
6
|
-
const totalH =
|
|
7
|
-
const boardT =
|
|
8
|
-
const rows =
|
|
9
|
-
const cols =
|
|
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 =
|
|
11
|
-
const plateD =
|
|
12
|
-
const topThick =
|
|
13
|
-
const botThick =
|
|
14
|
-
const exploded =
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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 &&
|
|
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
|
},
|