forgecad 0.1.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/LICENSE +97 -0
- package/README.md +354 -0
- package/dist/assets/evalWorker-BYHXxh15.js +461 -0
- package/dist/assets/index--CYbOPKS.js +5797 -0
- package/dist/assets/manifold-65fIQlgQ.js +20 -0
- package/dist/assets/manifold-B85M7kop.js +20 -0
- package/dist/assets/manifold-B8h_vZ5O.js +16 -0
- package/dist/assets/manifold-D9yvTBHx.wasm +0 -0
- package/dist/assets/manifold-d1UpyLJ8.js +20 -0
- package/dist/assets/reportWorker-B1Zdrz9l.js +494 -0
- package/dist/index.html +16 -0
- package/dist-cli/forgecad.js +44464 -0
- package/dist-skill/SKILL.md +4635 -0
- package/examples/3d-printer.forge.js +328 -0
- package/examples/5-figen-robot-hand.forge.js +283 -0
- package/examples/ac-unit-glm47.forge.js +108 -0
- package/examples/ac-unit-glm5.forge.js +174 -0
- package/examples/ac-unit-kimi25.forge.js +236 -0
- package/examples/ac-unit-minimax.forge.js +123 -0
- package/examples/ac-unit.forge.js +126 -0
- package/examples/adjustable-table.forge.js +191 -0
- package/examples/api/assembly-gear-coupling.forge.js +32 -0
- package/examples/api/assembly-mechanism.forge.js +111 -0
- package/examples/api/attachTo-basics.forge.js +45 -0
- package/examples/api/benchy-style-hull.forge.js +89 -0
- package/examples/api/bill-of-materials.forge.js +46 -0
- package/examples/api/boolean-operations.forge.js +48 -0
- package/examples/api/bounding-box-visualizer.forge.js +58 -0
- package/examples/api/brep-exportable.forge.js +19 -0
- package/examples/api/center-true-vs-false.forge.js +40 -0
- package/examples/api/clone-duplicate.forge.js +41 -0
- package/examples/api/colors-union-vs-array.forge.js +27 -0
- package/examples/api/coordinate-system.forge.js +54 -0
- package/examples/api/curves-surfacing-basics.forge.js +91 -0
- package/examples/api/dimensioned-bracket.forge.js +19 -0
- package/examples/api/elbow-test.forge.js +23 -0
- package/examples/api/exploded-view.forge.js +60 -0
- package/examples/api/extrude-options.forge.js +44 -0
- package/examples/api/face-gears.forge.js +44 -0
- package/examples/api/face-transformation-history.forge.js +45 -0
- package/examples/api/feature-created-faces.forge.js +47 -0
- package/examples/api/folded-service-panel-cover.forge.js +3 -0
- package/examples/api/folded-service-panel-cover.js +117 -0
- package/examples/api/gears-bevel-face-joints.forge.js +157 -0
- package/examples/api/gears-tier1.forge.js +57 -0
- package/examples/api/geometry-info.forge.js +49 -0
- package/examples/api/group-test.forge.js +34 -0
- package/examples/api/group-vs-union.forge.js +25 -0
- package/examples/api/import-args-unit.forge.js +5 -0
- package/examples/api/import-args.forge.js +16 -0
- package/examples/api/import-dimensions-follow.forge.js +18 -0
- package/examples/api/import-placement-references.forge.js +18 -0
- package/examples/api/import-placement-widget-source.forge.js +30 -0
- package/examples/api/import-relative-paths.forge.js +18 -0
- package/examples/api/import-svg-sketch-shape.svg +15 -0
- package/examples/api/import-svg-sketch.forge.js +28 -0
- package/examples/api/js-module-imports.forge.js +9 -0
- package/examples/api/js-module-pillars.js +25 -0
- package/examples/api/js-module-scene.js +9 -0
- package/examples/api/notebook-assembly-debug.forge-notebook.json +90 -0
- package/examples/api/notebook-iteration.forge-notebook.json +75 -0
- package/examples/api/patterns.forge.js +32 -0
- package/examples/api/pointAlong-orientation.forge.js +52 -0
- package/examples/api/profile-2020-b-slot6.forge.js +36 -0
- package/examples/api/rotate-around-to.forge.js +31 -0
- package/examples/api/runtime-joints-view.forge.js +116 -0
- package/examples/api/sdf-rover-demo.forge.js +159 -0
- package/examples/api/section-plane-visualization.forge.js +38 -0
- package/examples/api/sketch-basics.forge.js +48 -0
- package/examples/api/sketch-on-face.forge.js +56 -0
- package/examples/api/sketch-rounding-strategies.forge.js +56 -0
- package/examples/api/spatial-recipes.forge.js +129 -0
- package/examples/bathroom.forge.js +197 -0
- package/examples/bolt-and-nut.forge.js +39 -0
- package/examples/bolt-pattern.forge.js +18 -0
- package/examples/bottle.forge.js +101 -0
- package/examples/chair.forge.js +62 -0
- package/examples/chess-set.forge.js +232 -0
- package/examples/classical-piano.forge.js +203 -0
- package/examples/clock.forge.js +169 -0
- package/examples/compiler-corpus/README.md +88 -0
- package/examples/compiler-corpus/edge-finished-mount.forge.js +18 -0
- package/examples/compiler-corpus/enclosure-shell-cuts.forge.js +24 -0
- package/examples/compiler-corpus/fastener-plate-variants.forge.js +42 -0
- package/examples/compiler-corpus/folded-service-panel-cover.forge.js +5 -0
- package/examples/compiler-corpus/motor-mount-plate.forge.js +32 -0
- package/examples/compiler-corpus/projection-relay-cover.forge.js +16 -0
- package/examples/compiler-corpus/sensor-bracket.forge.js +35 -0
- package/examples/compiler-corpus/service-panel-cover.forge.js +53 -0
- package/examples/compiler-corpus/trimmed-access-cover.forge.js +26 -0
- package/examples/cup.forge.js +25 -0
- package/examples/cut-plane-demo.forge.js +28 -0
- package/examples/door-with-hinges.forge.js +54 -0
- package/examples/frame.sketch.js +4 -0
- package/examples/headphone-hanger-profile.sketch.js +18 -0
- package/examples/headphone-hanger-v2.forge.js +88 -0
- package/examples/headphone-hanger.forge.js +5 -0
- package/examples/iphone-stand.forge.js +72 -0
- package/examples/iphone.forge.js +114 -0
- package/examples/ironman-helmet.js +79 -0
- package/examples/kitchen.forge.js +231 -0
- package/examples/lamp-shade.sketch.js +17 -0
- package/examples/laptop.forge.js +144 -0
- package/examples/liquid-soap-dispenser.forge.js +159 -0
- package/examples/modern-tv.forge.js +86 -0
- package/examples/picture-frame.forge.js +34 -0
- package/examples/robot_hand.forge.js +393 -0
- package/examples/robot_hand_2.forge.js +622 -0
- package/examples/sandbox.forge.js +3 -0
- package/examples/shelf/container.forge.js +30 -0
- package/examples/shelf/shelf-unit.forge.js +62 -0
- package/examples/shoe-rack-doors.forge.js +107 -0
- package/examples/shoe-rack.forge.js +65 -0
- package/examples/spiderman-cake.forge.js +92 -0
- package/examples/table-lamp.forge.js +33 -0
- package/examples/table.forge.js +44 -0
- package/examples/test-colors.forge.js +19 -0
- package/examples/tv-stand.forge.js +21 -0
- package/package.json +69 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
// CoreXY 3D Printer — Bambu-style with proper clearances
|
|
2
|
+
// Z-up, Y = depth (front/back), X = width (left/right)
|
|
3
|
+
//
|
|
4
|
+
// Printing model: bed starts near nozzle, lowers as layers build up.
|
|
5
|
+
// Bed Z param represents current print progress (high = start, low = done).
|
|
6
|
+
|
|
7
|
+
// ─── Parameters ───
|
|
8
|
+
const bedW = param("Bed Width", 220, { min: 180, max: 300, unit: "mm" });
|
|
9
|
+
const bedD = param("Bed Depth", 220, { min: 180, max: 300, unit: "mm" });
|
|
10
|
+
const bedThick = param("Bed Thickness", 4, { min: 3, max: 8, unit: "mm" });
|
|
11
|
+
const frameMargin = param("Frame Margin", 40, { min: 20, max: 80, unit: "mm" });
|
|
12
|
+
const beam = param("Beam Size", 20, { min: 15, max: 30, unit: "mm" });
|
|
13
|
+
const frameH = param("Frame Height", 400, { min: 320, max: 520, unit: "mm" });
|
|
14
|
+
|
|
15
|
+
// ─── Derived frame ───
|
|
16
|
+
const frameW = bedW + 2 * frameMargin;
|
|
17
|
+
const frameD = bedD + 2 * frameMargin;
|
|
18
|
+
const innerW = frameW - 2 * beam;
|
|
19
|
+
const innerD = frameD - 2 * beam;
|
|
20
|
+
const railR = 4;
|
|
21
|
+
|
|
22
|
+
// Gantry sits near the top of the frame
|
|
23
|
+
const gantryZ = frameH - 80;
|
|
24
|
+
const nozzleToRail = 55;
|
|
25
|
+
const nozzleZ = gantryZ - nozzleToRail;
|
|
26
|
+
|
|
27
|
+
// Bed starts near nozzle tip and lowers during printing.
|
|
28
|
+
const bedTopMax = nozzleZ - 1;
|
|
29
|
+
const bedTopMin = beam + bedThick + 80;
|
|
30
|
+
const bedZ = param("Bed Z (print progress)", bedTopMax, {
|
|
31
|
+
min: bedTopMin, max: bedTopMax, unit: "mm",
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Gantry XY travel
|
|
35
|
+
const gantryTravel = innerD / 2 - 30;
|
|
36
|
+
const gantryY = param("Gantry Y", 0, { min: -gantryTravel, max: gantryTravel, unit: "mm" });
|
|
37
|
+
const headTravel = innerW / 2 - 50;
|
|
38
|
+
const headX = param("Nozzle X", 0, { min: -headTravel, max: headTravel, unit: "mm" });
|
|
39
|
+
|
|
40
|
+
// ─── Colors ───
|
|
41
|
+
const C = {
|
|
42
|
+
frame: "#555555", bed: "#c45c1a", glass: "#6bb6ff",
|
|
43
|
+
carriage: "#888888", rail: "#c0c0c0", lead: "#999999",
|
|
44
|
+
belt: "#222222", motor: "#444444", electronics: "#666666",
|
|
45
|
+
screen: "#00ccff", nozzle: "#b87333", hotend: "#999999",
|
|
46
|
+
fan: "#333333", spool: "#dddddd", filament: "#cc4444",
|
|
47
|
+
buildVol: "#33ccff",
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const parts = [];
|
|
51
|
+
const add = (name, shape, color) =>
|
|
52
|
+
parts.push({ name, shape: color ? shape.color(color) : shape });
|
|
53
|
+
|
|
54
|
+
// ─── Frame ───
|
|
55
|
+
const fp = [];
|
|
56
|
+
const bz = beam / 2;
|
|
57
|
+
const tz = frameH - beam / 2;
|
|
58
|
+
const pH = frameH - 2 * beam;
|
|
59
|
+
const pZ = beam + pH / 2;
|
|
60
|
+
const hw = frameW / 2 - beam / 2;
|
|
61
|
+
const hd = frameD / 2 - beam / 2;
|
|
62
|
+
|
|
63
|
+
for (const z of [bz, tz]) {
|
|
64
|
+
for (const sy of [-1, 1])
|
|
65
|
+
fp.push(box(frameW, beam, beam, true).translate(0, sy * hd, z));
|
|
66
|
+
for (const sx of [-1, 1])
|
|
67
|
+
fp.push(box(beam, frameD - 2 * beam, beam, true).translate(sx * hw, 0, z));
|
|
68
|
+
}
|
|
69
|
+
for (const sx of [-1, 1])
|
|
70
|
+
for (const sy of [-1, 1])
|
|
71
|
+
fp.push(box(beam, beam, pH, true).translate(sx * hw, sy * hd, pZ));
|
|
72
|
+
|
|
73
|
+
add("Frame", union(...fp), C.frame);
|
|
74
|
+
|
|
75
|
+
// ─── Z-Axis: rails + lead screws at rear corners ───
|
|
76
|
+
// Z rails only extend from bottom beam to just below gantry zone
|
|
77
|
+
const zRailInsetX = 25;
|
|
78
|
+
const zRailX = innerW / 2 - zRailInsetX;
|
|
79
|
+
const zRailY = innerD / 2 - 5; // flush with rear inner wall
|
|
80
|
+
const zTopClearance = gantryZ - 40; // stop well below gantry
|
|
81
|
+
const zLen = zTopClearance - beam - 10;
|
|
82
|
+
const zBase = beam + 10;
|
|
83
|
+
|
|
84
|
+
add("Z Rails", union(
|
|
85
|
+
cylinder(zLen, railR).translate(-zRailX, zRailY, zBase),
|
|
86
|
+
cylinder(zLen, railR).translate(zRailX, zRailY, zBase),
|
|
87
|
+
), C.rail);
|
|
88
|
+
|
|
89
|
+
const leadY = zRailY - 10; // lead screws just forward of rails, but behind bed edge
|
|
90
|
+
add("Lead Screws", union(
|
|
91
|
+
cylinder(zLen - 10, 4).translate(-zRailX, leadY, zBase),
|
|
92
|
+
cylinder(zLen - 10, 4).translate(zRailX, leadY, zBase),
|
|
93
|
+
), C.lead);
|
|
94
|
+
|
|
95
|
+
add("Z Motors", union(
|
|
96
|
+
box(42, 42, 40, true).translate(-zRailX, leadY, beam + 20),
|
|
97
|
+
box(42, 42, 40, true).translate(zRailX, leadY, beam + 20),
|
|
98
|
+
), C.motor);
|
|
99
|
+
|
|
100
|
+
// ─── Bed Assembly (moves in Z) ───
|
|
101
|
+
const glassThick = 2;
|
|
102
|
+
const glassTopZ = bedZ;
|
|
103
|
+
const bedPlateTopZ = glassTopZ - glassThick;
|
|
104
|
+
const bedPlateCenterZ = bedPlateTopZ - bedThick / 2;
|
|
105
|
+
|
|
106
|
+
const bedPlate = box(bedW, bedD, bedThick, true).translate(0, 0, bedPlateCenterZ);
|
|
107
|
+
const glass = box(bedW - 6, bedD - 6, glassThick, true)
|
|
108
|
+
.translate(0, 0, bedPlateTopZ + glassThick / 2);
|
|
109
|
+
|
|
110
|
+
const carriageThick = 8;
|
|
111
|
+
const springH = 12;
|
|
112
|
+
const carriageZ = bedPlateCenterZ - bedThick / 2 - springH - carriageThick / 2;
|
|
113
|
+
|
|
114
|
+
// Carriage narrower in Y so it doesn't extend past the Z rail Y position
|
|
115
|
+
const bedCarriage = box(bedW + 20, bedD - 40, carriageThick, true)
|
|
116
|
+
.translate(0, 0, carriageZ);
|
|
117
|
+
|
|
118
|
+
add("Z Bearings", union(
|
|
119
|
+
box(24, 24, 16, true).translate(-zRailX, zRailY, carriageZ),
|
|
120
|
+
box(24, 24, 16, true).translate(zRailX, zRailY, carriageZ),
|
|
121
|
+
), C.carriage);
|
|
122
|
+
|
|
123
|
+
add("Lead Nuts", union(
|
|
124
|
+
cylinder(8, 6).translate(-zRailX, leadY, carriageZ),
|
|
125
|
+
cylinder(8, 6).translate(zRailX, leadY, carriageZ),
|
|
126
|
+
), C.lead);
|
|
127
|
+
|
|
128
|
+
const spX = bedW / 2 - 25;
|
|
129
|
+
const spY = bedD / 2 - 25;
|
|
130
|
+
const springs = [];
|
|
131
|
+
for (const sx of [-1, 1])
|
|
132
|
+
for (const sy of [-1, 1])
|
|
133
|
+
springs.push(cylinder(springH, 3).translate(
|
|
134
|
+
sx * spX, sy * spY, bedPlateCenterZ - bedThick / 2 - springH));
|
|
135
|
+
|
|
136
|
+
add("Bed Plate", bedPlate, C.bed);
|
|
137
|
+
add("Glass Surface", glass, C.glass);
|
|
138
|
+
add("Bed Carriage", bedCarriage, C.carriage);
|
|
139
|
+
add("Leveling Springs", union(...springs), C.lead);
|
|
140
|
+
|
|
141
|
+
// ─── XY Gantry ───
|
|
142
|
+
const yRailLen = innerD - 30;
|
|
143
|
+
const yRailX = innerW / 2 - 15;
|
|
144
|
+
|
|
145
|
+
add("Y Rails", union(
|
|
146
|
+
cylinder(yRailLen, railR).pointAlong([0, 1, 0]).translate(-yRailX, -yRailLen / 2, gantryZ),
|
|
147
|
+
cylinder(yRailLen, railR).pointAlong([0, 1, 0]).translate(yRailX, -yRailLen / 2, gantryZ),
|
|
148
|
+
), C.rail);
|
|
149
|
+
|
|
150
|
+
add("Y Carriages", union(
|
|
151
|
+
box(28, 32, 18, true).translate(-yRailX, gantryY, gantryZ),
|
|
152
|
+
box(28, 32, 18, true).translate(yRailX, gantryY, gantryZ),
|
|
153
|
+
), C.carriage);
|
|
154
|
+
|
|
155
|
+
const xRailLen = yRailX * 2 - 30;
|
|
156
|
+
add("X Rail", cylinder(xRailLen, railR).pointAlong([1, 0, 0])
|
|
157
|
+
.translate(-xRailLen / 2, gantryY, gantryZ), C.rail);
|
|
158
|
+
|
|
159
|
+
// X beam at gantry level — supports the X rail from behind
|
|
160
|
+
add("X Beam", box(xRailLen, beam, beam, true)
|
|
161
|
+
.translate(0, gantryY, gantryZ), C.frame);
|
|
162
|
+
|
|
163
|
+
// ─── CoreXY Belts ───
|
|
164
|
+
const beltZ = gantryZ + 22;
|
|
165
|
+
add("XY Belts", union(
|
|
166
|
+
box(xRailLen, 2, 3, true).translate(0, gantryY + 8, beltZ),
|
|
167
|
+
box(xRailLen, 2, 3, true).translate(0, gantryY - 8, beltZ),
|
|
168
|
+
box(2, yRailLen, 3, true).translate(-yRailX, 0, beltZ),
|
|
169
|
+
box(2, yRailLen, 3, true).translate(yRailX, 0, beltZ),
|
|
170
|
+
), C.belt);
|
|
171
|
+
|
|
172
|
+
// XY motors inside frame at top-rear
|
|
173
|
+
const motorSize = 42;
|
|
174
|
+
const motorY = innerD / 2 - motorSize / 2 - 5;
|
|
175
|
+
add("XY Motors", union(
|
|
176
|
+
box(motorSize, motorSize, motorSize, true).translate(-yRailX, motorY, gantryZ + 30),
|
|
177
|
+
box(motorSize, motorSize, motorSize, true).translate(yRailX, motorY, gantryZ + 30),
|
|
178
|
+
), C.motor);
|
|
179
|
+
|
|
180
|
+
add("XY Idlers", union(
|
|
181
|
+
cylinder(8, 6).pointAlong([0, 1, 0]).translate(-yRailX, -yRailLen / 2, beltZ),
|
|
182
|
+
cylinder(8, 6).pointAlong([0, 1, 0]).translate(yRailX, -yRailLen / 2, beltZ),
|
|
183
|
+
cylinder(8, 6).pointAlong([0, 1, 0]).translate(-yRailX, yRailLen / 2, beltZ),
|
|
184
|
+
cylinder(8, 6).pointAlong([0, 1, 0]).translate(yRailX, yRailLen / 2, beltZ),
|
|
185
|
+
), C.lead);
|
|
186
|
+
|
|
187
|
+
// ─── Print Head / Extruder ───
|
|
188
|
+
const hx = headX;
|
|
189
|
+
const hy = gantryY;
|
|
190
|
+
|
|
191
|
+
add("Nozzle", cylinder(6, 0.4, 2).translate(hx, hy, nozzleZ), C.nozzle);
|
|
192
|
+
add("Heater Block", box(12, 12, 8, true).translate(hx, hy, nozzleZ + 10), C.hotend);
|
|
193
|
+
add("Heatbreak", cylinder(6, 1.5).translate(hx, hy, nozzleZ + 14), C.lead);
|
|
194
|
+
add("Heatsink", cylinder(12, 6).translate(hx, hy, nozzleZ + 22), C.hotend);
|
|
195
|
+
|
|
196
|
+
const headCarriage = box(46, 28, 36, true).translate(hx, hy, gantryZ - 18);
|
|
197
|
+
add("Extruder Carriage", headCarriage, C.carriage);
|
|
198
|
+
|
|
199
|
+
add("Part Fan", box(18, 8, 16, true)
|
|
200
|
+
.onFace(headCarriage, 'front', { v: -5, protrude: 5 }), C.fan);
|
|
201
|
+
|
|
202
|
+
// Extruder motor — compact, sits just above gantry plane
|
|
203
|
+
const extruderMotorZ = gantryZ + 5;
|
|
204
|
+
add("Extruder Motor", box(30, 30, 20, true)
|
|
205
|
+
.translate(hx, hy, extruderMotorZ), C.motor);
|
|
206
|
+
|
|
207
|
+
// ─── Spool Holder (behind frame, on top) ───
|
|
208
|
+
const spoolW = 60;
|
|
209
|
+
const spoolR = 35;
|
|
210
|
+
const spoolY = frameD / 2 + spoolR + 15;
|
|
211
|
+
const spoolZ = frameH;
|
|
212
|
+
|
|
213
|
+
const spoolRod = cylinder(spoolW + 20, 3).pointAlong([1, 0, 0])
|
|
214
|
+
.translate(-(spoolW + 20) / 2, spoolY, spoolZ);
|
|
215
|
+
add("Spool Rod", spoolRod, C.lead);
|
|
216
|
+
|
|
217
|
+
const spoolShell = cylinder(spoolW, spoolR)
|
|
218
|
+
.subtract(cylinder(spoolW + 2, spoolR - 4).translate(0, 0, -1))
|
|
219
|
+
.pointAlong([1, 0, 0])
|
|
220
|
+
.translate(-spoolW / 2, spoolY, spoolZ);
|
|
221
|
+
add("Spool Shell", spoolShell, C.spool);
|
|
222
|
+
add("Filament Roll", cylinder(spoolW - 4, spoolR - 6).pointAlong([1, 0, 0])
|
|
223
|
+
.translate(-spoolW / 2 + 2, spoolY, spoolZ), C.filament);
|
|
224
|
+
add("Spool Hub", cylinder(spoolW, 8).pointAlong([1, 0, 0])
|
|
225
|
+
.translate(-spoolW / 2, spoolY, spoolZ), C.lead);
|
|
226
|
+
|
|
227
|
+
add("Spool Supports", union(
|
|
228
|
+
box(10, 20, 50, true).translate(-spoolW / 2 - 15, frameD / 2 - beam / 2, frameH - 25),
|
|
229
|
+
box(10, 20, 50, true).translate(spoolW / 2 + 15, frameD / 2 - beam / 2, frameH - 25),
|
|
230
|
+
), C.frame);
|
|
231
|
+
|
|
232
|
+
// ─── Filament Path ───
|
|
233
|
+
// Simple path: spool → over rear frame beam → down into extruder
|
|
234
|
+
const filR = 1.5;
|
|
235
|
+
const filBendR = 30;
|
|
236
|
+
|
|
237
|
+
// Guide tube: spool to top of frame (fixed portion)
|
|
238
|
+
const filFixedPath = lib.pipeRoute(
|
|
239
|
+
[
|
|
240
|
+
[0, spoolY, spoolZ], // spool center
|
|
241
|
+
[0, frameD / 2 - beam, frameH + 20], // up and forward over rear beam
|
|
242
|
+
[0, 0, frameH + 20], // across top to center
|
|
243
|
+
[hx, hy, extruderMotorZ + 12], // straight to extruder motor top
|
|
244
|
+
],
|
|
245
|
+
filR,
|
|
246
|
+
{ bendRadius: 25, wall: 0.4, segments: 16 }
|
|
247
|
+
);
|
|
248
|
+
add("Filament Guide Tube", filFixedPath, C.bowden);
|
|
249
|
+
|
|
250
|
+
// ─── Electronics ───
|
|
251
|
+
add("PSU", box(100, 60, 40, true)
|
|
252
|
+
.translate(frameW / 2 - 55, -frameD / 2 + 35, 25), C.electronics);
|
|
253
|
+
|
|
254
|
+
add("Control Board", box(70, 40, 25, true)
|
|
255
|
+
.translate(-frameW / 2 + 40, -frameD / 2 + 25, 18), C.electronics);
|
|
256
|
+
|
|
257
|
+
add("Display", box(50, 3, 25, true)
|
|
258
|
+
.translate(-frameW / 2 + 40, -frameD / 2 + 1, frameH / 3), C.screen);
|
|
259
|
+
|
|
260
|
+
// ─── Build Volume wireframe ───
|
|
261
|
+
const buildH = Math.max(20, nozzleZ - bedTopMin - 5);
|
|
262
|
+
const bvr = 0.6;
|
|
263
|
+
const bvW = bedW - 10, bvD = bedD - 10;
|
|
264
|
+
const bvEdges = [];
|
|
265
|
+
const x0 = -bvW / 2, x1 = bvW / 2;
|
|
266
|
+
const y0 = -bvD / 2, y1 = bvD / 2;
|
|
267
|
+
const z0b = bedTopMin + 5, z1b = z0b + buildH;
|
|
268
|
+
for (const y of [y0, y1]) for (const z of [z0b, z1b])
|
|
269
|
+
bvEdges.push(cylinder(bvW, bvr).pointAlong([1, 0, 0]).translate(x0, y, z));
|
|
270
|
+
for (const x of [x0, x1]) for (const z of [z0b, z1b])
|
|
271
|
+
bvEdges.push(cylinder(bvD, bvr).pointAlong([0, 1, 0]).translate(x, y0, z));
|
|
272
|
+
for (const x of [x0, x1]) for (const y of [y0, y1])
|
|
273
|
+
bvEdges.push(cylinder(buildH, bvr).translate(x, y, z0b));
|
|
274
|
+
|
|
275
|
+
add("Build Volume", union(...bvEdges), C.buildVol);
|
|
276
|
+
|
|
277
|
+
// ─── Cut Planes ───
|
|
278
|
+
cutPlane("Front Section", [0, -1, 0], 0);
|
|
279
|
+
cutPlane("Side Section", [1, 0, 0], 0);
|
|
280
|
+
cutPlane("Top Section", [0, 0, 1], gantryZ);
|
|
281
|
+
|
|
282
|
+
// Assembly groups — intentional overlaps within groups are not flagged
|
|
283
|
+
return [
|
|
284
|
+
{ name: "Structure", group: [
|
|
285
|
+
parts.find(p => p.name === "Frame"),
|
|
286
|
+
parts.find(p => p.name === "Spool Supports"),
|
|
287
|
+
parts.find(p => p.name === "PSU"),
|
|
288
|
+
parts.find(p => p.name === "Control Board"),
|
|
289
|
+
parts.find(p => p.name === "Display"),
|
|
290
|
+
]},
|
|
291
|
+
{ name: "Z Axis", group: [
|
|
292
|
+
parts.find(p => p.name === "Z Rails"),
|
|
293
|
+
parts.find(p => p.name === "Lead Screws"),
|
|
294
|
+
parts.find(p => p.name === "Z Motors"),
|
|
295
|
+
parts.find(p => p.name === "Z Bearings"),
|
|
296
|
+
parts.find(p => p.name === "Lead Nuts"),
|
|
297
|
+
]},
|
|
298
|
+
{ name: "Bed Assembly", group: [
|
|
299
|
+
parts.find(p => p.name === "Bed Plate"),
|
|
300
|
+
parts.find(p => p.name === "Glass Surface"),
|
|
301
|
+
parts.find(p => p.name === "Bed Carriage"),
|
|
302
|
+
parts.find(p => p.name === "Leveling Springs"),
|
|
303
|
+
]},
|
|
304
|
+
{ name: "XY Gantry + Head", group: [
|
|
305
|
+
parts.find(p => p.name === "Y Rails"),
|
|
306
|
+
parts.find(p => p.name === "Y Carriages"),
|
|
307
|
+
parts.find(p => p.name === "X Rail"),
|
|
308
|
+
parts.find(p => p.name === "X Beam"),
|
|
309
|
+
parts.find(p => p.name === "XY Belts"),
|
|
310
|
+
parts.find(p => p.name === "XY Motors"),
|
|
311
|
+
parts.find(p => p.name === "XY Idlers"),
|
|
312
|
+
parts.find(p => p.name === "Nozzle"),
|
|
313
|
+
parts.find(p => p.name === "Heater Block"),
|
|
314
|
+
parts.find(p => p.name === "Heatbreak"),
|
|
315
|
+
parts.find(p => p.name === "Heatsink"),
|
|
316
|
+
parts.find(p => p.name === "Extruder Carriage"),
|
|
317
|
+
parts.find(p => p.name === "Part Fan"),
|
|
318
|
+
parts.find(p => p.name === "Extruder Motor"),
|
|
319
|
+
]},
|
|
320
|
+
{ name: "Spool + Filament", group: [
|
|
321
|
+
parts.find(p => p.name === "Spool Rod"),
|
|
322
|
+
parts.find(p => p.name === "Spool Shell"),
|
|
323
|
+
parts.find(p => p.name === "Filament Roll"),
|
|
324
|
+
parts.find(p => p.name === "Spool Hub"),
|
|
325
|
+
parts.find(p => p.name === "Filament Guide Tube"),
|
|
326
|
+
]},
|
|
327
|
+
parts.find(p => p.name === "Build Volume"),
|
|
328
|
+
];
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
// Robot Hand — functional, buildable concept
|
|
2
|
+
// Z-up, Y depth (front = -Y)
|
|
3
|
+
|
|
4
|
+
const scale = param("Scale", 1.0, { min: 0.7, max: 1.3, step: 0.05 });
|
|
5
|
+
const curl = param("Finger Curl", 40, { min: 0, max: 70, unit: "°" });
|
|
6
|
+
const thumbCurl = param("Thumb Curl", 35, { min: 0, max: 70, unit: "°" });
|
|
7
|
+
const spread = param("Finger Spread", 8, { min: 0, max: 20, unit: "°" });
|
|
8
|
+
|
|
9
|
+
// --- Dimensions ---
|
|
10
|
+
const palmW = 90 * scale;
|
|
11
|
+
const palmD = 70 * scale;
|
|
12
|
+
const palmH = 18 * scale;
|
|
13
|
+
const fingerT = 12 * scale;
|
|
14
|
+
const gap = 2 * scale;
|
|
15
|
+
|
|
16
|
+
const baseY = -palmD / 2 - gap;
|
|
17
|
+
const baseZ = palmH / 2 + fingerT / 2 + 2 * scale;
|
|
18
|
+
|
|
19
|
+
// --- Colors ---
|
|
20
|
+
const colors = {
|
|
21
|
+
palm: '#d8cbb2',
|
|
22
|
+
palmPad: '#2a2a2a',
|
|
23
|
+
knuckle: '#aaaaaa',
|
|
24
|
+
wrist: '#666666',
|
|
25
|
+
motor: '#444444',
|
|
26
|
+
spool: '#cc9933',
|
|
27
|
+
segment: '#e6e6e6',
|
|
28
|
+
pad: '#222222',
|
|
29
|
+
pin: '#9aa1a8',
|
|
30
|
+
tendon: '#c9a227',
|
|
31
|
+
cable: '#999999',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// --- Helper functions ---
|
|
35
|
+
function rotX(p, pivot, deg) {
|
|
36
|
+
const rad = deg * Math.PI / 180;
|
|
37
|
+
const cos = Math.cos(rad);
|
|
38
|
+
const sin = Math.sin(rad);
|
|
39
|
+
const x = p[0] - pivot[0];
|
|
40
|
+
const y = p[1] - pivot[1];
|
|
41
|
+
const z = p[2] - pivot[2];
|
|
42
|
+
const y2 = y * cos - z * sin;
|
|
43
|
+
const z2 = y * sin + z * cos;
|
|
44
|
+
return [pivot[0] + x, pivot[1] + y2, pivot[2] + z2];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function centerOf(shape) {
|
|
48
|
+
const bb = shape.boundingBox();
|
|
49
|
+
return [
|
|
50
|
+
(bb.min[0] + bb.max[0]) / 2,
|
|
51
|
+
(bb.min[1] + bb.max[1]) / 2,
|
|
52
|
+
(bb.min[2] + bb.max[2]) / 2,
|
|
53
|
+
];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// --- Finger builder ---
|
|
57
|
+
function makeFinger(opts) {
|
|
58
|
+
const {
|
|
59
|
+
name,
|
|
60
|
+
base,
|
|
61
|
+
lengths,
|
|
62
|
+
widths,
|
|
63
|
+
thickness,
|
|
64
|
+
angles,
|
|
65
|
+
yaw = 0,
|
|
66
|
+
colors,
|
|
67
|
+
} = opts;
|
|
68
|
+
|
|
69
|
+
const [L1, L2, L3] = lengths;
|
|
70
|
+
const [W1, W2, W3] = widths;
|
|
71
|
+
const t = thickness;
|
|
72
|
+
const [a1, a2, a3] = angles;
|
|
73
|
+
|
|
74
|
+
const basePivot = base;
|
|
75
|
+
const midPivot0 = [basePivot[0], basePivot[1] - L1, basePivot[2]];
|
|
76
|
+
const tipPivot0 = [basePivot[0], basePivot[1] - L1 - L2, basePivot[2]];
|
|
77
|
+
|
|
78
|
+
// Straight segments in world space
|
|
79
|
+
let s1 = box(W1, L1, t, true)
|
|
80
|
+
.translate(basePivot[0], basePivot[1] - L1 / 2, basePivot[2])
|
|
81
|
+
.color(colors.segment);
|
|
82
|
+
let s2 = box(W2, L2, t, true)
|
|
83
|
+
.translate(basePivot[0], basePivot[1] - L1 - L2 / 2, basePivot[2])
|
|
84
|
+
.color(colors.segment);
|
|
85
|
+
let s3 = box(W3, L3, t, true)
|
|
86
|
+
.translate(basePivot[0], basePivot[1] - L1 - L2 - L3 / 2, basePivot[2])
|
|
87
|
+
.color(colors.segment);
|
|
88
|
+
|
|
89
|
+
// Pads (rubber contact)
|
|
90
|
+
const padT = t * 0.18;
|
|
91
|
+
let pad1 = box(W1 * 0.7, L1 * 0.45, padT, true)
|
|
92
|
+
.color(colors.pad)
|
|
93
|
+
.onFace(s1, 'bottom', { v: -L1 * 0.15, protrude: padT / 2 });
|
|
94
|
+
let pad2 = box(W2 * 0.65, L2 * 0.45, padT, true)
|
|
95
|
+
.color(colors.pad)
|
|
96
|
+
.onFace(s2, 'bottom', { v: -L2 * 0.15, protrude: padT / 2 });
|
|
97
|
+
let pad3 = box(W3 * 0.8, L3 * 0.6, padT, true)
|
|
98
|
+
.color(colors.pad)
|
|
99
|
+
.onFace(s3, 'bottom', { v: -L3 * 0.1, protrude: padT / 2 });
|
|
100
|
+
|
|
101
|
+
// Tendon guides (raised top rails)
|
|
102
|
+
const rodT = t * 0.12;
|
|
103
|
+
let rod1 = box(W1 * 0.2, L1 * 0.9, rodT, true)
|
|
104
|
+
.color(colors.tendon)
|
|
105
|
+
.onFace(s1, 'top', { v: -L1 * 0.05, protrude: rodT / 2 });
|
|
106
|
+
let rod2 = box(W2 * 0.2, L2 * 0.9, rodT, true)
|
|
107
|
+
.color(colors.tendon)
|
|
108
|
+
.onFace(s2, 'top', { v: -L2 * 0.05, protrude: rodT / 2 });
|
|
109
|
+
let rod3 = box(W3 * 0.2, L3 * 0.85, rodT, true)
|
|
110
|
+
.color(colors.tendon)
|
|
111
|
+
.onFace(s3, 'top', { v: -L3 * 0.05, protrude: rodT / 2 });
|
|
112
|
+
|
|
113
|
+
// Pivots after rotation
|
|
114
|
+
const midPivot1 = rotX(midPivot0, basePivot, a1);
|
|
115
|
+
const tipPivot1 = rotX(tipPivot0, basePivot, a1);
|
|
116
|
+
const tipPivot2 = rotX(tipPivot1, midPivot1, a2);
|
|
117
|
+
|
|
118
|
+
const seg1 = (shape) => shape.rotateAround([1, 0, 0], a1, basePivot);
|
|
119
|
+
const seg2 = (shape) => shape
|
|
120
|
+
.rotateAround([1, 0, 0], a1, basePivot)
|
|
121
|
+
.rotateAround([1, 0, 0], a2, midPivot1);
|
|
122
|
+
const seg3 = (shape) => shape
|
|
123
|
+
.rotateAround([1, 0, 0], a1, basePivot)
|
|
124
|
+
.rotateAround([1, 0, 0], a2, midPivot1)
|
|
125
|
+
.rotateAround([1, 0, 0], a3, tipPivot2);
|
|
126
|
+
|
|
127
|
+
s1 = seg1(s1); s2 = seg2(s2); s3 = seg3(s3);
|
|
128
|
+
pad1 = seg1(pad1); pad2 = seg2(pad2); pad3 = seg3(pad3);
|
|
129
|
+
rod1 = seg1(rod1); rod2 = seg2(rod2); rod3 = seg3(rod3);
|
|
130
|
+
|
|
131
|
+
// Joint pins
|
|
132
|
+
const pinLen = Math.max(W1, W2, W3) + 8 * scale;
|
|
133
|
+
const pinR = t * 0.15;
|
|
134
|
+
const pinBase = cylinder(pinLen, pinR).pointAlong([1, 0, 0]).color(colors.pin);
|
|
135
|
+
let pin1 = pinBase.translate(basePivot[0] - pinLen / 2, basePivot[1], basePivot[2]);
|
|
136
|
+
let pin2 = pinBase.translate(midPivot1[0] - pinLen / 2, midPivot1[1], midPivot1[2]);
|
|
137
|
+
let pin3 = pinBase.translate(tipPivot2[0] - pinLen / 2, tipPivot2[1], tipPivot2[2]);
|
|
138
|
+
|
|
139
|
+
// Yoke block at base
|
|
140
|
+
let yoke = box(W1 * 1.1, 6 * scale, t * 0.8, true)
|
|
141
|
+
.translate(basePivot[0], basePivot[1] + 3 * scale, basePivot[2])
|
|
142
|
+
.color(colors.knuckle);
|
|
143
|
+
yoke = seg1(yoke);
|
|
144
|
+
|
|
145
|
+
// Apply yaw (splay)
|
|
146
|
+
if (yaw !== 0) {
|
|
147
|
+
const yawRot = (shape) => shape.rotateAround([0, 0, 1], yaw, basePivot);
|
|
148
|
+
s1 = yawRot(s1); s2 = yawRot(s2); s3 = yawRot(s3);
|
|
149
|
+
pad1 = yawRot(pad1); pad2 = yawRot(pad2); pad3 = yawRot(pad3);
|
|
150
|
+
rod1 = yawRot(rod1); rod2 = yawRot(rod2); rod3 = yawRot(rod3);
|
|
151
|
+
pin1 = yawRot(pin1); pin2 = yawRot(pin2); pin3 = yawRot(pin3);
|
|
152
|
+
yoke = yawRot(yoke);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
name,
|
|
157
|
+
group: [
|
|
158
|
+
{ name: `${name} Prox`, shape: s1 },
|
|
159
|
+
{ name: `${name} Mid`, shape: s2 },
|
|
160
|
+
{ name: `${name} Tip`, shape: s3 },
|
|
161
|
+
{ name: `${name} Pad 1`, shape: pad1 },
|
|
162
|
+
{ name: `${name} Pad 2`, shape: pad2 },
|
|
163
|
+
{ name: `${name} Pad 3`, shape: pad3 },
|
|
164
|
+
{ name: `${name} Tendon 1`, shape: rod1 },
|
|
165
|
+
{ name: `${name} Tendon 2`, shape: rod2 },
|
|
166
|
+
{ name: `${name} Tendon 3`, shape: rod3 },
|
|
167
|
+
{ name: `${name} Pin Base`, shape: pin1 },
|
|
168
|
+
{ name: `${name} Pin Mid`, shape: pin2 },
|
|
169
|
+
{ name: `${name} Pin Tip`, shape: pin3 },
|
|
170
|
+
{ name: `${name} Yoke`, shape: yoke },
|
|
171
|
+
],
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// --- Palm assembly ---
|
|
176
|
+
const palm = box(palmW, palmD, palmH, true).color(colors.palm);
|
|
177
|
+
const palmPad = box(palmW * 0.6, 3 * scale, palmH * 0.5, true)
|
|
178
|
+
.color(colors.palmPad)
|
|
179
|
+
.onFace(palm, 'front', { v: -palmH * 0.05, protrude: 1 * scale });
|
|
180
|
+
|
|
181
|
+
const knuckleBar = box(palmW * 0.9, 10 * scale, 6 * scale, true)
|
|
182
|
+
.color(colors.knuckle)
|
|
183
|
+
.onFace(palm, 'top', { v: -palmD / 2 + 8 * scale, protrude: 3 * scale });
|
|
184
|
+
|
|
185
|
+
const wristLen = 60 * scale;
|
|
186
|
+
const wristR = 14 * scale;
|
|
187
|
+
const wrist = cylinder(wristLen, wristR)
|
|
188
|
+
.pointAlong([0, 1, 0])
|
|
189
|
+
.color(colors.wrist)
|
|
190
|
+
.attachTo(palm, 'back', 'front', [0, 0, -palmH * 0.2]);
|
|
191
|
+
|
|
192
|
+
const motorW = 40 * scale;
|
|
193
|
+
const motorD = 28 * scale;
|
|
194
|
+
const motorH = 20 * scale;
|
|
195
|
+
const motor = box(motorW, motorD, motorH, true)
|
|
196
|
+
.color(colors.motor)
|
|
197
|
+
.attachTo(palm, 'bottom', 'top', [0, palmD / 2 - motorD / 2 - 4 * scale, -2 * scale]);
|
|
198
|
+
|
|
199
|
+
const spoolLen = palmW * 0.7;
|
|
200
|
+
const spoolR = 6 * scale;
|
|
201
|
+
const spool = cylinder(spoolLen, spoolR)
|
|
202
|
+
.pointAlong([1, 0, 0])
|
|
203
|
+
.color(colors.spool)
|
|
204
|
+
.attachTo(motor, 'top', 'bottom', [0, -motorD * 0.25, spoolR * 0.2]);
|
|
205
|
+
|
|
206
|
+
// --- Fingers ---
|
|
207
|
+
const fingerDefs = [
|
|
208
|
+
{ name: 'Index', x: -palmW * 0.28, lengths: [32, 24, 18], widths: [16, 14, 13], yaw: -spread * 0.4 },
|
|
209
|
+
{ name: 'Middle', x: -palmW * 0.08, lengths: [35, 26, 20], widths: [17, 15, 14], yaw: -spread * 0.15 },
|
|
210
|
+
{ name: 'Ring', x: palmW * 0.12, lengths: [33, 24, 19], widths: [16, 14, 13], yaw: spread * 0.2 },
|
|
211
|
+
{ name: 'Pinky', x: palmW * 0.30, lengths: [28, 20, 16], widths: [14, 12, 11], yaw: spread * 0.45 },
|
|
212
|
+
];
|
|
213
|
+
|
|
214
|
+
const fingerGroups = fingerDefs.map(def => makeFinger({
|
|
215
|
+
name: def.name,
|
|
216
|
+
base: [def.x, baseY, baseZ],
|
|
217
|
+
lengths: def.lengths.map(v => v * scale),
|
|
218
|
+
widths: def.widths.map(v => v * scale),
|
|
219
|
+
thickness: fingerT,
|
|
220
|
+
angles: [curl * 0.6, curl * 0.9, curl * 1.1],
|
|
221
|
+
yaw: def.yaw,
|
|
222
|
+
colors,
|
|
223
|
+
}));
|
|
224
|
+
|
|
225
|
+
// Thumb (angled, shorter)
|
|
226
|
+
const thumb = makeFinger({
|
|
227
|
+
name: 'Thumb',
|
|
228
|
+
base: [-palmW * 0.45, -palmD * 0.1, baseZ - fingerT * 0.2],
|
|
229
|
+
lengths: [26, 18, 14].map(v => v * scale),
|
|
230
|
+
widths: [18, 16, 14].map(v => v * scale),
|
|
231
|
+
thickness: fingerT * 0.9,
|
|
232
|
+
angles: [thumbCurl * 0.6, thumbCurl * 0.8, thumbCurl * 0.9],
|
|
233
|
+
yaw: -50,
|
|
234
|
+
colors,
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// --- Tendon cables from spool to finger bases ---
|
|
238
|
+
const spoolCenter = centerOf(spool);
|
|
239
|
+
const spoolBB = spool.boundingBox();
|
|
240
|
+
const cableR = 1.2 * scale;
|
|
241
|
+
const cableTopZ = palmH / 2 + fingerT + 20 * scale;
|
|
242
|
+
|
|
243
|
+
const fingerBases = [
|
|
244
|
+
...fingerDefs.map(def => [def.x, baseY, baseZ]),
|
|
245
|
+
[-palmW * 0.45, -palmD * 0.1, baseZ - fingerT * 0.2],
|
|
246
|
+
];
|
|
247
|
+
|
|
248
|
+
const cableCount = fingerBases.length;
|
|
249
|
+
const cables = fingerBases.map((end, i) => {
|
|
250
|
+
const t = (i + 1) / (cableCount + 1);
|
|
251
|
+
const startX = spoolBB.min[0] + t * (spoolBB.max[0] - spoolBB.min[0]);
|
|
252
|
+
const start = [startX, spoolCenter[1], spoolCenter[2] + spoolR * 0.8];
|
|
253
|
+
const midBack = [startX, palmD / 2 + 6 * scale, cableTopZ];
|
|
254
|
+
const midPalm = [end[0], 0, cableTopZ];
|
|
255
|
+
const endPt = [end[0], end[1] + 6 * scale, end[2] + fingerT * 0.6];
|
|
256
|
+
|
|
257
|
+
return lib.pipeRoute([start, midBack, midPalm, endPt], cableR, { bendRadius: 10 * scale })
|
|
258
|
+
.color(colors.cable);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// --- Grasp target object ---
|
|
262
|
+
const graspObject = hull3d(
|
|
263
|
+
sphere(18 * scale).translate(0, baseY - 45 * scale, baseZ + 6 * scale),
|
|
264
|
+
box(32 * scale, 20 * scale, 26 * scale, true).translate(12 * scale, baseY - 65 * scale, baseZ + 2 * scale)
|
|
265
|
+
).color('#88ccee');
|
|
266
|
+
|
|
267
|
+
// --- Return scene ---
|
|
268
|
+
return [
|
|
269
|
+
{
|
|
270
|
+
name: 'Palm Assembly', group: [
|
|
271
|
+
{ name: 'Palm', shape: palm },
|
|
272
|
+
{ name: 'Palm Grip', shape: palmPad },
|
|
273
|
+
{ name: 'Knuckle Bar', shape: knuckleBar },
|
|
274
|
+
{ name: 'Wrist', shape: wrist },
|
|
275
|
+
{ name: 'Motor', shape: motor },
|
|
276
|
+
{ name: 'Spool', shape: spool },
|
|
277
|
+
...cables.map((c, i) => ({ name: `Cable ${i + 1}`, shape: c })),
|
|
278
|
+
]
|
|
279
|
+
},
|
|
280
|
+
...fingerGroups,
|
|
281
|
+
thumb,
|
|
282
|
+
// { name: 'Grasp Object', shape: graspObject },
|
|
283
|
+
];
|