forgecad 0.6.3 → 0.7.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 +2 -11
- package/dist/assets/{AdminPage-CeqCUUgu.js → AdminPage-DAu1C1ST.js} +250 -151
- package/dist/assets/{BlogPage-P_AJP0v9.js → BlogPage-CJEXL_zJ.js} +94 -70
- package/dist/assets/{DocsPage-CKRV2iq2.js → DocsPage-Gc_BCdqC.js} +269 -143
- package/dist/assets/EditorApp-D9bJvtf7.js +11338 -0
- package/dist/assets/{EditorApp-CnC2k4cW.css → EditorApp-DG1-oUSV.css} +459 -87
- package/dist/assets/{EmbedViewer-DBlzmQ5i.js → EmbedViewer-CEO8XbV8.js} +2 -4
- package/dist/assets/LandingPage-CdCuEOdC.js +451 -0
- package/dist/assets/PricingPage-BSrxu6d7.js +232 -0
- package/dist/assets/{SettingsPage-BqCh9JcC.js → SettingsPage-FUCSIRq6.js} +129 -5
- package/dist/assets/{evalWorker-Ql-aKwLA.js → evalWorker-KoR0SNKq.js} +6770 -2914
- package/dist/assets/{index-2hfs_ub0.css → index-CyVd1D4D.css} +227 -53
- package/dist/assets/{Viewport-CoB46f5R.js → index-wTEK39at.js} +31385 -6439
- package/dist/assets/{javascript-DCxGoE5Y.js → javascript-DAl8Gmyo.js} +1 -1
- package/dist/assets/{manifold-CqNMHHKO.js → manifold-B1sGWdYk.js} +4 -3
- package/dist/assets/{manifold-Cce9wRFz.js → manifold-D7o0N50J.js} +1 -1
- package/dist/assets/{manifold-D6BeHIOo.js → manifold-G5sBaXzi.js} +1 -1
- package/dist/assets/{reportWorker-sFEFonXf.js → reportWorker-DYcRHhv9.js} +6798 -3341
- package/dist/assets/{vendor-react-Dt7-aaJH.js → vendor-react-CG3i_wp0.js} +65 -8
- package/dist/docs-raw/generated/assembly.md +691 -112
- package/dist/docs-raw/generated/concepts.md +1225 -1400
- package/dist/docs-raw/generated/core.md +464 -1412
- package/dist/docs-raw/generated/curves.md +593 -117
- package/dist/docs-raw/generated/lib.md +38 -748
- package/dist/docs-raw/generated/output.md +139 -245
- package/dist/docs-raw/generated/sheet-metal.md +473 -21
- package/dist/docs-raw/generated/sketch.md +553 -349
- package/dist/docs-raw/generated/viewport.md +345 -303
- package/dist/docs-raw/generated/wood.md +104 -0
- package/dist/index.html +2 -2
- package/dist/sitemap.xml +6 -6
- package/dist-cli/chunk-PZ5AY32C.js +10 -0
- package/dist-cli/chunk-PZ5AY32C.js.map +1 -0
- package/dist-cli/forgecad.js +9435 -5407
- package/dist-cli/forgecad.js.map +1 -0
- package/dist-cli/solver-FV7TJZGI.js +365 -0
- package/dist-cli/solver-FV7TJZGI.js.map +1 -0
- package/dist-skill/CONTEXT.md +3186 -7145
- package/dist-skill/SKILL-dev.md +21 -63
- package/dist-skill/SKILL.md +12 -56
- package/dist-skill/docs/API/core/concepts.md +16 -98
- package/dist-skill/docs/CLI/export.md +91 -0
- package/dist-skill/docs/CLI/projects.md +107 -0
- package/dist-skill/docs/CLI/studio_publishing.md +52 -0
- package/dist-skill/docs/CLI/validation.md +66 -0
- package/dist-skill/docs/generated/assembly.md +691 -112
- package/dist-skill/docs/generated/core.md +464 -1412
- package/dist-skill/docs/generated/curves.md +593 -117
- package/dist-skill/docs/generated/lib.md +38 -748
- package/dist-skill/docs/generated/output.md +139 -245
- package/dist-skill/docs/generated/sheet-metal.md +473 -21
- package/dist-skill/docs/generated/sketch.md +553 -349
- package/dist-skill/docs/generated/viewport.md +345 -303
- 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/modeling-recipes.md +22 -195
- package/dist-skill/docs/guides/positioning.md +88 -147
- package/dist-skill/docs-dev/API/core/concepts.md +51 -0
- package/dist-skill/docs-dev/API/core/sdf-advanced.md +92 -0
- package/dist-skill/docs-dev/API/core/sdf-primitives.md +58 -0
- package/dist-skill/docs-dev/API/core/sdf-workflow.md +42 -0
- package/dist-skill/docs-dev/CLI/export.md +91 -0
- package/dist-skill/docs-dev/CLI/projects.md +107 -0
- package/dist-skill/docs-dev/CLI/studio_publishing.md +52 -0
- package/dist-skill/docs-dev/CLI/validation.md +66 -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 +1 -3
- package/dist-skill/docs-dev/generated/assembly.md +771 -0
- package/dist-skill/docs-dev/generated/core.md +775 -0
- package/dist-skill/docs-dev/generated/curves.md +688 -0
- package/dist-skill/docs-dev/generated/lib.md +50 -0
- package/dist-skill/docs-dev/generated/output.md +234 -0
- package/dist-skill/docs-dev/generated/sheet-metal.md +506 -0
- package/dist-skill/docs-dev/generated/sketch.md +801 -0
- package/dist-skill/docs-dev/generated/viewport.md +486 -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/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 +5 -5
- package/examples/api/boolean-operations.forge.js +3 -3
- package/examples/api/bounding-box-visualizer.forge.js +2 -2
- package/examples/api/clone-duplicate.forge.js +1 -1
- package/examples/api/colors-union-vs-array.forge.js +6 -6
- package/examples/api/connector-assembly.forge.js +4 -4
- package/examples/api/connector-basics.forge.js +2 -2
- package/examples/api/extrude-options.forge.js +4 -10
- package/examples/api/feature-created-faces.forge.js +6 -10
- package/examples/api/fillet-showcase.forge.js +1 -1
- package/examples/api/folded-service-panel-cover.forge.js +2 -2
- package/examples/api/group-test.forge.js +1 -1
- 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 +1 -1
- package/examples/api/pointAlong-orientation.forge.js +1 -1
- package/examples/api/profile-2020-b-slot6.forge.js +0 -1
- 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/transition-curves.forge.js +1 -1
- 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/experiments/drone-arm.forge.js +53 -0
- package/examples/furniture/adjustable-table.forge.js +2 -2
- package/examples/furniture/bathroom.forge.js +11 -11
- package/examples/furniture/chair.forge.js +1 -1
- package/examples/generative/crystal-growth.forge.js +2 -2
- package/examples/generative/frost-spires.forge.js +3 -3
- package/examples/generative/golden-spiral-tower.forge.js +3 -3
- package/examples/mechanical/3d-printer.forge.js +28 -28
- package/examples/mechanical/5-finger-robot-hand.forge.js +15 -15
- package/examples/mechanical/airplane-propeller.forge.js +2 -2
- package/examples/mechanical/fillet-enclosure.forge.js +1 -1
- package/examples/mechanical/headphone-hanger-v2.forge.js +2 -2
- package/examples/mechanical/robot_hand.forge.js +15 -15
- package/examples/mechanical/robot_hand_2.forge.js +9 -9
- package/examples/products/bottle.forge.js +1 -1
- package/examples/products/chess-set.forge.js +19 -19
- package/examples/products/classical-piano.forge.js +11 -11
- package/examples/products/clock.forge.js +12 -12
- package/examples/products/iphone.forge.js +8 -8
- package/examples/products/laptop.forge.js +15 -15
- package/examples/products/liquid-soap-dispenser.forge.js +18 -18
- package/examples/products/origami-fish.forge.js +8 -6
- package/examples/products/spiderman-cake.forge.js +4 -4
- package/examples/toolbox/bolted-joint.forge.js +2 -2
- package/package.json +7 -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/docs-raw/CLI.md +0 -865
- 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/CLI.md +0 -865
- 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
|
@@ -47,16 +47,16 @@ const innerRadius = radius - rimWidth;
|
|
|
47
47
|
const handBaseZ = depth / 2 - 2;
|
|
48
48
|
|
|
49
49
|
// === Clock Rim ===
|
|
50
|
-
const outer = cylinder(depth, radius
|
|
51
|
-
const inner = cylinder(depth + 2, innerRadius
|
|
50
|
+
const outer = cylinder(depth, radius);
|
|
51
|
+
const inner = cylinder(depth + 2, innerRadius);
|
|
52
52
|
const rimBody = outer.subtract(inner);
|
|
53
53
|
|
|
54
54
|
// Add subtle bevel to rim edge
|
|
55
|
-
const bevel = cylinder(depth - 5, radius - 1, radius
|
|
55
|
+
const bevel = cylinder(depth - 5, radius - 1, radius);
|
|
56
56
|
const rim = union(rimBody, bevel).color(rimHex);
|
|
57
57
|
|
|
58
58
|
// === Clock Face ===
|
|
59
|
-
const face = cylinder(2, innerRadius - 1
|
|
59
|
+
const face = cylinder(2, innerRadius - 1)
|
|
60
60
|
.translate(0, 0, depth / 2 - 3)
|
|
61
61
|
.color(faceHex);
|
|
62
62
|
|
|
@@ -76,7 +76,7 @@ for (let i = 0; i < 12; i++) {
|
|
|
76
76
|
const x = Math.cos(rad) * r;
|
|
77
77
|
const y = Math.sin(rad) * r;
|
|
78
78
|
|
|
79
|
-
const tick = roundedRect(width, length, width / 2
|
|
79
|
+
const tick = roundedRect(width, length, width / 2)
|
|
80
80
|
.rotateAround([0, 0, 0], angle)
|
|
81
81
|
.extrude(1)
|
|
82
82
|
.translate(x, y, depth / 2 - 2);
|
|
@@ -100,10 +100,10 @@ const secondAngle = second * 6;
|
|
|
100
100
|
|
|
101
101
|
// Create Apple-style hand with rounded ends
|
|
102
102
|
function createHand(length, width, angle, zOffset, thickness) {
|
|
103
|
-
return roundedRect(width, length, width / 2
|
|
103
|
+
return roundedRect(width, length, width / 2)
|
|
104
104
|
.translate(0, length / 2)
|
|
105
105
|
.extrude(thickness)
|
|
106
|
-
.
|
|
106
|
+
.rotateZ(angle - 90)
|
|
107
107
|
.translate(0, 0, zOffset);
|
|
108
108
|
}
|
|
109
109
|
|
|
@@ -122,10 +122,10 @@ if (showSecondHand > 0) {
|
|
|
122
122
|
const counterWidth = 4;
|
|
123
123
|
|
|
124
124
|
const sweep = createHand(secondHandLength, secondHandWidth, secondAngle, handBaseZ + 10, 2);
|
|
125
|
-
const counter = roundedRect(counterWidth, counterLength, counterWidth / 2
|
|
125
|
+
const counter = roundedRect(counterWidth, counterLength, counterWidth / 2)
|
|
126
126
|
.translate(0, -counterLength / 2)
|
|
127
127
|
.extrude(2)
|
|
128
|
-
.
|
|
128
|
+
.rotateZ(secondAngle - 90)
|
|
129
129
|
.translate(0, 0, handBaseZ + 10);
|
|
130
130
|
|
|
131
131
|
secondHand = union(sweep, counter).color(secondHandHex);
|
|
@@ -133,11 +133,11 @@ if (showSecondHand > 0) {
|
|
|
133
133
|
|
|
134
134
|
// === Center Cap ===
|
|
135
135
|
const centerRadius = 6;
|
|
136
|
-
const centerRing = cylinder(3, centerRadius + 2
|
|
136
|
+
const centerRing = cylinder(3, centerRadius + 2)
|
|
137
137
|
.translate(0, 0, handBaseZ + 13)
|
|
138
138
|
.color(rimHex);
|
|
139
139
|
|
|
140
|
-
const centerDot = cylinder(4, centerRadius
|
|
140
|
+
const centerDot = cylinder(4, centerRadius)
|
|
141
141
|
.translate(0, 0, handBaseZ + 13)
|
|
142
142
|
.color(showSecondHand > 0 ? secondHandHex : "#444444");
|
|
143
143
|
|
|
@@ -145,7 +145,7 @@ const centerDot = cylinder(4, centerRadius, undefined, undefined, true)
|
|
|
145
145
|
const mountWidth = 25;
|
|
146
146
|
const mountHeight = 12;
|
|
147
147
|
const mountDepth = 6;
|
|
148
|
-
const mount = roundedRect(mountWidth, mountHeight, 3
|
|
148
|
+
const mount = roundedRect(mountWidth, mountHeight, 3)
|
|
149
149
|
.extrude(mountDepth)
|
|
150
150
|
.translate(0, -radius + 8, -depth / 2 - mountDepth / 2)
|
|
151
151
|
.color(rimHex);
|
|
@@ -11,8 +11,8 @@ const edgeR = param("Edge Radius", 1.5, { min: 0, max: 3, step: 0.1, unit: "mm"
|
|
|
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
18
|
const screenInset = param("Screen Inset", 2, { min: 1, max: 5, unit: "mm" });
|
|
@@ -21,14 +21,14 @@ 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);
|
|
@@ -41,7 +41,7 @@ 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);
|
|
@@ -75,7 +75,7 @@ const portW = param("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);
|
|
@@ -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');
|
|
@@ -11,14 +11,14 @@ const kbInsetX = param("KB Inset X", 25, { min: 10, max: 50, unit: "mm" });
|
|
|
11
11
|
const kbInsetY = param("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,11 +112,11 @@ 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
122
|
const lidAngle = param("Lid Angle", 110, { min: 0, max: 135, unit: "°" });
|
|
@@ -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
|
|
|
@@ -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));
|
|
@@ -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 });
|
|
@@ -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.7.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",
|
|
@@ -95,6 +97,7 @@
|
|
|
95
97
|
"puppeteer-core": "^24.37.2",
|
|
96
98
|
"react-router-dom": "^7.13.2",
|
|
97
99
|
"resend": "^6.9.4",
|
|
100
|
+
"stripe": "^21.0.1",
|
|
98
101
|
"typescript": "^5.8.3",
|
|
99
102
|
"zod": "^4.3.6"
|
|
100
103
|
},
|