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,48 @@
|
|
|
1
|
+
// Boolean operations — union, difference, intersection.
|
|
2
|
+
//
|
|
3
|
+
// union(a, b) → combined volume
|
|
4
|
+
// difference(a, b) → a minus b (subtract b from a)
|
|
5
|
+
// intersection(a, b) → only the overlapping volume
|
|
6
|
+
//
|
|
7
|
+
// Method syntax also accepts multiple cutters/operands:
|
|
8
|
+
// a.add(b, c), a.subtract(b, c), a.intersect(b, c)
|
|
9
|
+
// and the helper functions also accept arrays:
|
|
10
|
+
// difference([a, b, c]), union([a, b, c]), intersection([a, b, c])
|
|
11
|
+
|
|
12
|
+
const size = param("Size", 30, { min: 15, max: 50, unit: "mm" });
|
|
13
|
+
const overlap = param("Overlap", 15, { min: 0, max: 30, unit: "mm" });
|
|
14
|
+
const spacing = 80;
|
|
15
|
+
|
|
16
|
+
// Two overlapping shapes for each demo
|
|
17
|
+
function makePair(offsetX) {
|
|
18
|
+
const a = box(size, size, size, true).translate(offsetX, 0, 0).color('#4488cc');
|
|
19
|
+
const b = sphere(size * 0.6).translate(offsetX + size - overlap, 0, 0).color('#cc4444');
|
|
20
|
+
return [a, b];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 1. Union — combined
|
|
24
|
+
const [u1, u2] = makePair(0);
|
|
25
|
+
const unioned = union(u1, u2).color('#8866cc');
|
|
26
|
+
|
|
27
|
+
// 2. Difference — box minus sphere and cross-bore
|
|
28
|
+
const [d1, d2] = makePair(spacing);
|
|
29
|
+
const d3 = cylinder(size * 1.2, size * 0.14, undefined, undefined, true)
|
|
30
|
+
.pointAlong([0, 1, 0])
|
|
31
|
+
.translate(spacing, 0, 0);
|
|
32
|
+
const diffed = d1.subtract(d2, d3);
|
|
33
|
+
|
|
34
|
+
// 3. Intersection — only overlap
|
|
35
|
+
const [i1, i2] = makePair(2 * spacing);
|
|
36
|
+
const intersected = intersection(i1, i2).color('#cc8844');
|
|
37
|
+
|
|
38
|
+
// Show the original shapes (translucent-ish via separate objects) for reference
|
|
39
|
+
const refA = box(size, size, size, true).translate(3 * spacing, 0, 0).color('#4488cc');
|
|
40
|
+
const refB = sphere(size * 0.6).translate(3 * spacing + size - overlap, 0, 0).color('#cc4444');
|
|
41
|
+
|
|
42
|
+
return [
|
|
43
|
+
{ name: "Union", shape: unioned },
|
|
44
|
+
{ name: "Difference (box - sphere - bore)", shape: diffed },
|
|
45
|
+
{ name: "Intersection", shape: intersected },
|
|
46
|
+
{ name: "Original Box", shape: refA },
|
|
47
|
+
{ name: "Original Sphere", shape: refB },
|
|
48
|
+
];
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Visualize bounding boxes — useful for debugging positioning.
|
|
2
|
+
//
|
|
3
|
+
// boundingBox() returns { min: [x,y,z], max: [x,y,z] }.
|
|
4
|
+
// This example draws thin cylinders along the 12 edges of the bbox.
|
|
5
|
+
|
|
6
|
+
const edgeR = 0.5; // wireframe edge radius
|
|
7
|
+
|
|
8
|
+
function vizBBox(shape) {
|
|
9
|
+
const bb = shape.boundingBox();
|
|
10
|
+
const [x0, y0, z0] = bb.min;
|
|
11
|
+
const [x1, y1, z1] = bb.max;
|
|
12
|
+
const dx = x1 - x0, dy = y1 - y0, dz = z1 - z0;
|
|
13
|
+
|
|
14
|
+
const edges = [];
|
|
15
|
+
// 4 edges along X (at each combination of Y,Z corners)
|
|
16
|
+
for (const y of [y0, y1]) {
|
|
17
|
+
for (const z of [z0, z1]) {
|
|
18
|
+
edges.push(cylinder(dx, edgeR).pointAlong([1, 0, 0]).translate(x0, y, z));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// 4 edges along Y
|
|
22
|
+
for (const x of [x0, x1]) {
|
|
23
|
+
for (const z of [z0, z1]) {
|
|
24
|
+
edges.push(cylinder(dy, edgeR).pointAlong([0, 1, 0]).translate(x, y0, z));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// 4 edges along Z
|
|
28
|
+
for (const x of [x0, x1]) {
|
|
29
|
+
for (const y of [y0, y1]) {
|
|
30
|
+
edges.push(cylinder(dz, edgeR).translate(x, y, z0));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return union(...edges);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// --- Demo shapes ---
|
|
37
|
+
|
|
38
|
+
// A rotated box — bbox is larger than the shape itself
|
|
39
|
+
const angle = param("Rotation", 30, { min: 0, max: 90, unit: "°" });
|
|
40
|
+
const rotBox = box(40, 30, 20, true).rotate(0, 0, angle).color('#4488cc');
|
|
41
|
+
const rotBBox = vizBBox(rotBox).color('#cc4444');
|
|
42
|
+
|
|
43
|
+
// A sphere — bbox is a perfect cube around it
|
|
44
|
+
const sph = sphere(20).translate(80, 0, 0).color('#44cc44');
|
|
45
|
+
const sphBBox = vizBBox(sph).color('#cc4444');
|
|
46
|
+
|
|
47
|
+
// A tilted cylinder — bbox shows the extent
|
|
48
|
+
const tiltCyl = cylinder(50, 10).rotate(30, 0, 0).translate(0, 80, 0).color('#cc88ff');
|
|
49
|
+
const cylBBox = vizBBox(tiltCyl).color('#cc4444');
|
|
50
|
+
|
|
51
|
+
return [
|
|
52
|
+
{ name: "Rotated Box", shape: rotBox },
|
|
53
|
+
{ name: "Box BBox", shape: rotBBox },
|
|
54
|
+
{ name: "Sphere", shape: sph },
|
|
55
|
+
{ name: "Sphere BBox", shape: sphBBox },
|
|
56
|
+
{ name: "Tilted Cylinder", shape: tiltCyl },
|
|
57
|
+
{ name: "Cylinder BBox", shape: cylBBox },
|
|
58
|
+
];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Exact-exportable subset demo for STEP/BREP.
|
|
2
|
+
// Run: forgecad export step examples/api/brep-exportable.forge.js
|
|
3
|
+
|
|
4
|
+
const plate = rect(120, 80, true).extrude(10).color('#748b99');
|
|
5
|
+
const boss = cylinder(24, 18).translate(0, 0, 10).color('#b7c4cc');
|
|
6
|
+
|
|
7
|
+
const leftHole = cylinder(18, 5).translate(-34, 0, -4);
|
|
8
|
+
const rightHole = cylinder(18, 5).translate(34, 0, -4);
|
|
9
|
+
const centerBore = cylinder(34, 8).translate(0, 0, 6);
|
|
10
|
+
|
|
11
|
+
const exactPart = union(plate.toShape(), boss)
|
|
12
|
+
.subtract(leftHole)
|
|
13
|
+
.subtract(rightHole)
|
|
14
|
+
.subtract(centerBore)
|
|
15
|
+
.color('#9db1bd');
|
|
16
|
+
|
|
17
|
+
return [
|
|
18
|
+
{ name: 'Exact Export Demo', shape: exactPart },
|
|
19
|
+
];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// center=true vs center=false — the #1 source of positioning confusion.
|
|
2
|
+
//
|
|
3
|
+
// box(w, d, h) → corner at origin, extends into +X, +Y, +Z
|
|
4
|
+
// box(w, d, h, true) → centered at origin
|
|
5
|
+
//
|
|
6
|
+
// Same applies to cylinder(h, r) vs cylinder(h, r, r, undefined, true).
|
|
7
|
+
|
|
8
|
+
const w = 40, d = 30, h = 20;
|
|
9
|
+
|
|
10
|
+
// --- Side-by-side comparison ---
|
|
11
|
+
|
|
12
|
+
// Left: center=false (default). Red sphere marks the origin [0,0,0].
|
|
13
|
+
const cornerBox = box(w, d, h).color('#4488cc').translate(-60, 0, 0);
|
|
14
|
+
const cornerOrigin = sphere(2).color('#cc0000').translate(-60, 0, 0);
|
|
15
|
+
|
|
16
|
+
// Right: center=true. Red sphere marks the origin [0,0,0].
|
|
17
|
+
const centeredBox = box(w, d, h, true).color('#44cc88').translate(60, 0, 0);
|
|
18
|
+
const centeredOrigin = sphere(2).color('#cc0000').translate(60, 0, 0);
|
|
19
|
+
|
|
20
|
+
// --- Practical impact: placing a cylinder on top of a base ---
|
|
21
|
+
|
|
22
|
+
// With center=false: cylinder must go to (w/2, d/2, h)
|
|
23
|
+
const base1 = box(w, d, h).color('#888888').translate(-60, 60, 0);
|
|
24
|
+
const cyl1 = cylinder(15, 6).color('#cc8844').translate(-60 + w/2, 60 + d/2, h);
|
|
25
|
+
|
|
26
|
+
// With center=true + attachTo: no math needed
|
|
27
|
+
const base2 = box(w, d, h, true).color('#888888').translate(60, 60 + d/2, h/2);
|
|
28
|
+
const cyl2 = cylinder(15, 6).color('#cc8844')
|
|
29
|
+
.attachTo(base2, 'top', 'bottom');
|
|
30
|
+
|
|
31
|
+
return [
|
|
32
|
+
{ name: "Corner Box (center=false)", shape: cornerBox },
|
|
33
|
+
{ name: "Corner Origin ●", shape: cornerOrigin },
|
|
34
|
+
{ name: "Centered Box (center=true)", shape: centeredBox },
|
|
35
|
+
{ name: "Centered Origin ●", shape: centeredOrigin },
|
|
36
|
+
{ name: "Base (corner)", shape: base1 },
|
|
37
|
+
{ name: "Cylinder (manual math)", shape: cyl1 },
|
|
38
|
+
{ name: "Base (centered)", shape: base2 },
|
|
39
|
+
{ name: "Cylinder (attachTo)", shape: cyl2 },
|
|
40
|
+
];
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// clone() / duplicate() — explicit copy helpers for Shape, TrackedShape, Sketch, and ShapeGroup.
|
|
2
|
+
|
|
3
|
+
const spacing = param("Spacing", 90, { min: 40, max: 180, unit: "mm" });
|
|
4
|
+
|
|
5
|
+
// --- Shape clone ---
|
|
6
|
+
const block = box(36, 20, 12, true).color("#4a90e2");
|
|
7
|
+
const blockL = block.clone().translate(-spacing / 2, 0, 0);
|
|
8
|
+
const blockR = block.duplicate().translate(spacing / 2, 0, 0);
|
|
9
|
+
|
|
10
|
+
// --- TrackedShape clone (topology preserved) ---
|
|
11
|
+
const post = cylinder(36, 6).color("#49b675");
|
|
12
|
+
const postCopy = post.clone().translate(0, 45, 0);
|
|
13
|
+
|
|
14
|
+
// --- Sketch clone ---
|
|
15
|
+
const slotProfile = slot(30, 10).color("#e98b39");
|
|
16
|
+
const slotL = slotProfile.clone().translate(-spacing / 2, -35);
|
|
17
|
+
const slotR = slotProfile.duplicate().translate(spacing / 2, -35);
|
|
18
|
+
|
|
19
|
+
// --- ShapeGroup clone (with named children) ---
|
|
20
|
+
const moduleGroup = group(
|
|
21
|
+
{ name: "Block", shape: block },
|
|
22
|
+
{ name: "Post", shape: post.attachTo(block, "top", "bottom") }
|
|
23
|
+
);
|
|
24
|
+
const moduleL = moduleGroup.clone().translate(-spacing / 2, 95, 0);
|
|
25
|
+
const moduleR = moduleGroup.duplicate().translate(spacing / 2, 95, 0).color("#c85a54");
|
|
26
|
+
|
|
27
|
+
return [
|
|
28
|
+
{ name: "Shape clone/duplicate", group: [
|
|
29
|
+
{ name: "Block L", shape: blockL },
|
|
30
|
+
{ name: "Block R", shape: blockR },
|
|
31
|
+
] },
|
|
32
|
+
{ name: "TrackedShape clone", shape: postCopy },
|
|
33
|
+
{ name: "Sketch clone/duplicate", group: [
|
|
34
|
+
{ name: "Slot L", sketch: slotL },
|
|
35
|
+
{ name: "Slot R", sketch: slotR },
|
|
36
|
+
] },
|
|
37
|
+
{ name: "ShapeGroup clone/duplicate", group: [
|
|
38
|
+
{ name: "Module L", shape: moduleL },
|
|
39
|
+
{ name: "Module R", shape: moduleR },
|
|
40
|
+
] },
|
|
41
|
+
];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Colors: union() vs returning separate objects.
|
|
2
|
+
//
|
|
3
|
+
// ❌ union() merges into one mesh → only the first shape's color survives.
|
|
4
|
+
// ✅ Returning an array of {name, shape} → each keeps its own color.
|
|
5
|
+
|
|
6
|
+
const size = 25;
|
|
7
|
+
const gap = 5;
|
|
8
|
+
|
|
9
|
+
// --- Three colored boxes ---
|
|
10
|
+
const red = box(size, size, size, true).color('#cc4444');
|
|
11
|
+
const green = box(size, size, size, true).color('#44cc44').translate(size + gap, 0, 0);
|
|
12
|
+
const blue = box(size, size, size, true).color('#4444cc').translate(2 * (size + gap), 0, 0);
|
|
13
|
+
|
|
14
|
+
// BAD: union kills individual colors — result is all red (first shape's color)
|
|
15
|
+
const merged = union(red, green, blue).translate(-80, 0, 0);
|
|
16
|
+
|
|
17
|
+
// GOOD: separate objects keep their colors
|
|
18
|
+
const redSep = box(size, size, size, true).color('#cc4444').translate(80, 0, 0);
|
|
19
|
+
const greenSep = box(size, size, size, true).color('#44cc44').translate(80 + size + gap, 0, 0);
|
|
20
|
+
const blueSep = box(size, size, size, true).color('#4444cc').translate(80 + 2 * (size + gap), 0, 0);
|
|
21
|
+
|
|
22
|
+
return [
|
|
23
|
+
{ name: "❌ Union (all one color)", shape: merged },
|
|
24
|
+
{ name: "✅ Red (separate)", shape: redSep },
|
|
25
|
+
{ name: "✅ Green (separate)", shape: greenSep },
|
|
26
|
+
{ name: "✅ Blue (separate)", shape: blueSep },
|
|
27
|
+
];
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Coordinate system — ForgeCAD uses Z-up, right-handed.
|
|
2
|
+
// X = right (+X), left (-X)
|
|
3
|
+
// Y = forward (+Y), back (-Y)
|
|
4
|
+
// Z = up (+Z), down (-Z)
|
|
5
|
+
//
|
|
6
|
+
// "front" = -Y face (camera default looks from -Y toward +Y)
|
|
7
|
+
// "back" = +Y face
|
|
8
|
+
|
|
9
|
+
const axisLen = 80;
|
|
10
|
+
const shaftR = 2;
|
|
11
|
+
const tipH = 10;
|
|
12
|
+
const tipR = 5;
|
|
13
|
+
|
|
14
|
+
// X axis — red, pointing right
|
|
15
|
+
const xShaft = cylinder(axisLen, shaftR).pointAlong([1, 0, 0]).color('#cc4444');
|
|
16
|
+
const xTip = cylinder(tipH, tipR, 0).pointAlong([1, 0, 0]).translate(axisLen, 0, 0).color('#cc4444');
|
|
17
|
+
const xMark = sphere(4).translate(axisLen + tipH + 5, 0, 0).color('#cc4444');
|
|
18
|
+
|
|
19
|
+
// Y axis — green, pointing forward
|
|
20
|
+
const yShaft = cylinder(axisLen, shaftR).pointAlong([0, 1, 0]).color('#44cc44');
|
|
21
|
+
const yTip = cylinder(tipH, tipR, 0).pointAlong([0, 1, 0]).translate(0, axisLen, 0).color('#44cc44');
|
|
22
|
+
const yMark = box(7, 7, 7, true).translate(0, axisLen + tipH + 5, 0).color('#44cc44');
|
|
23
|
+
|
|
24
|
+
// Z axis — blue, pointing up
|
|
25
|
+
const zShaft = cylinder(axisLen, shaftR).color('#4444cc');
|
|
26
|
+
const zTip = cylinder(tipH, tipR, 0).translate(0, 0, axisLen).color('#4444cc');
|
|
27
|
+
const zMark = ngon(6, 4).extrude(4, { center: true }).translate(0, 0, axisLen + tipH + 5).color('#4444cc');
|
|
28
|
+
|
|
29
|
+
// Origin
|
|
30
|
+
const origin = sphere(3).color('#ffffff');
|
|
31
|
+
|
|
32
|
+
// Reference box to show face names
|
|
33
|
+
const ref = box(30, 20, 15, true).translate(40, 40, 0).color('#888888');
|
|
34
|
+
// "front" face is at -Y, "right" face is at +X, "top" face is at +Z
|
|
35
|
+
const frontDot = sphere(3).color('#ffaa00')
|
|
36
|
+
.attachTo(ref, 'front', 'center', [0, -5, 0]);
|
|
37
|
+
const topDot = sphere(3).color('#ffaa00')
|
|
38
|
+
.attachTo(ref, 'top', 'center', [0, 0, 5]);
|
|
39
|
+
|
|
40
|
+
return [
|
|
41
|
+
{ name: "X shaft (right)", shape: xShaft },
|
|
42
|
+
{ name: "X tip", shape: xTip },
|
|
43
|
+
{ name: "X mark ●", shape: xMark },
|
|
44
|
+
{ name: "Y shaft (forward)", shape: yShaft },
|
|
45
|
+
{ name: "Y tip", shape: yTip },
|
|
46
|
+
{ name: "Y mark ■", shape: yMark },
|
|
47
|
+
{ name: "Z shaft (up)", shape: zShaft },
|
|
48
|
+
{ name: "Z tip", shape: zTip },
|
|
49
|
+
{ name: "Z mark ⬡", shape: zMark },
|
|
50
|
+
{ name: "Origin", shape: origin },
|
|
51
|
+
{ name: "Reference Box", shape: ref },
|
|
52
|
+
{ name: "Front dot (−Y)", shape: frontDot },
|
|
53
|
+
{ name: "Top dot (+Z)", shape: topDot },
|
|
54
|
+
];
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Curves + Surfacing basics
|
|
2
|
+
// Demonstrates reusable APIs for everyday products:
|
|
3
|
+
// - spline2d() for smooth section sketches
|
|
4
|
+
// - loft() for section-driven solids
|
|
5
|
+
// - spline3d() + sweep() for curved tubes/handles/details
|
|
6
|
+
|
|
7
|
+
const height = param("Bottle Height", 170, { min: 110, max: 260, unit: "mm" });
|
|
8
|
+
const bodyW = param("Body Width", 72, { min: 45, max: 110, unit: "mm" });
|
|
9
|
+
const bodyD = param("Body Depth", 48, { min: 30, max: 90, unit: "mm" });
|
|
10
|
+
const neckW = param("Neck Width", 28, { min: 18, max: 45, unit: "mm" });
|
|
11
|
+
const neckD = param("Neck Depth", 24, { min: 14, max: 40, unit: "mm" });
|
|
12
|
+
const corner = param("Corner Round", 8, { min: 2, max: 20, unit: "mm" });
|
|
13
|
+
|
|
14
|
+
const sectionAt = (w, d, pinch = 0) => spline2d([
|
|
15
|
+
[w * 0.5, 0],
|
|
16
|
+
[w * 0.42, d * 0.45],
|
|
17
|
+
[w * 0.2, d * 0.5 + pinch],
|
|
18
|
+
[0, d * 0.52 + pinch],
|
|
19
|
+
[-w * 0.2, d * 0.5 + pinch],
|
|
20
|
+
[-w * 0.42, d * 0.45],
|
|
21
|
+
[-w * 0.5, 0],
|
|
22
|
+
[-w * 0.42, -d * 0.45],
|
|
23
|
+
[-w * 0.2, -d * 0.5 + pinch],
|
|
24
|
+
[0, -d * 0.52 + pinch],
|
|
25
|
+
[w * 0.2, -d * 0.5 + pinch],
|
|
26
|
+
[w * 0.42, -d * 0.45],
|
|
27
|
+
], {
|
|
28
|
+
closed: true,
|
|
29
|
+
samplesPerSegment: 10,
|
|
30
|
+
tension: 0.42,
|
|
31
|
+
}).offset(corner * 0.08, 'Round');
|
|
32
|
+
|
|
33
|
+
const z0 = 0;
|
|
34
|
+
const z1 = height * 0.25;
|
|
35
|
+
const z2 = height * 0.62;
|
|
36
|
+
const z3 = height * 0.9;
|
|
37
|
+
const z4 = height;
|
|
38
|
+
|
|
39
|
+
const body = loft(
|
|
40
|
+
[
|
|
41
|
+
sectionAt(bodyW * 0.86, bodyD * 0.84, -2),
|
|
42
|
+
sectionAt(bodyW, bodyD, 0),
|
|
43
|
+
sectionAt(bodyW * 0.92, bodyD * 0.94, 1),
|
|
44
|
+
sectionAt(neckW * 1.25, neckD * 1.2, 0.5),
|
|
45
|
+
sectionAt(neckW, neckD, 0),
|
|
46
|
+
],
|
|
47
|
+
[z0, z1, z2, z3, z4],
|
|
48
|
+
{ edgeLength: 1.1 },
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// Hollow interior by lofting smaller inner sections.
|
|
52
|
+
const wall = 2.4;
|
|
53
|
+
const inner = loft(
|
|
54
|
+
[
|
|
55
|
+
sectionAt(bodyW * 0.78, bodyD * 0.76, -2.2),
|
|
56
|
+
sectionAt(bodyW - wall * 2, bodyD - wall * 2, -0.6),
|
|
57
|
+
sectionAt(bodyW * 0.86 - wall * 2, bodyD * 0.88 - wall * 2, 0.2),
|
|
58
|
+
sectionAt(neckW * 1.06 - wall, neckD * 1.06 - wall, 0),
|
|
59
|
+
sectionAt(neckW - wall, neckD - wall, 0),
|
|
60
|
+
],
|
|
61
|
+
[z0 + 3, z1, z2, z3, z4 + 2],
|
|
62
|
+
{ edgeLength: 1.1 },
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
let bottle = body.subtract(inner);
|
|
66
|
+
// Mild smoothing to reduce voxel-like artifacts on curved sections.
|
|
67
|
+
bottle = bottle.smoothOut(70, 0.25).refine(2);
|
|
68
|
+
|
|
69
|
+
// Curved spout/tube detail using sweep.
|
|
70
|
+
const spoutPath = spline3d(
|
|
71
|
+
[
|
|
72
|
+
[0, 0, z4 - 8],
|
|
73
|
+
[12, 0, z4 + 8],
|
|
74
|
+
[26, 0, z4 + 24],
|
|
75
|
+
[36, 0, z4 + 16],
|
|
76
|
+
],
|
|
77
|
+
{ tension: 0.45 },
|
|
78
|
+
);
|
|
79
|
+
const spout = sweep(circle2d(2.8, 20), spoutPath, {
|
|
80
|
+
samples: 36,
|
|
81
|
+
edgeLength: 0.65,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const topCap = circle2d(Math.max(neckW, neckD) * 0.34, 40).extrude(9)
|
|
85
|
+
.translate(0, 0, z4 - 1.5);
|
|
86
|
+
|
|
87
|
+
return [
|
|
88
|
+
{ name: "Bottle Body", shape: bottle.color('#d8e5ec') },
|
|
89
|
+
{ name: "Spout", shape: spout.color('#c0ccd4') },
|
|
90
|
+
{ name: "Cap", shape: topCap.color('#4f5f70') },
|
|
91
|
+
];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Dimensioned L-bracket — shows how to add dimension annotations
|
|
2
|
+
|
|
3
|
+
const w = param("Width", 80, { min: 40, max: 150, unit: "mm" });
|
|
4
|
+
const h = param("Height", 60, { min: 30, max: 100, unit: "mm" });
|
|
5
|
+
const d = param("Depth", 40, { min: 20, max: 80, unit: "mm" });
|
|
6
|
+
const t = param("Thickness", 5, { min: 2, max: 15, unit: "mm" });
|
|
7
|
+
|
|
8
|
+
// Build the L-bracket
|
|
9
|
+
const base = box(w, d, t);
|
|
10
|
+
const wall = box(t, d, h).translate(0, 0, t);
|
|
11
|
+
const bracket = union(base, wall);
|
|
12
|
+
|
|
13
|
+
// Add dimensions — purely visual annotations
|
|
14
|
+
dim([0, 0, 0], [w, 0, 0], { label: "Width" });
|
|
15
|
+
dim([0, 0, 0], [0, d, 0], { label: "Depth", offset: 12 });
|
|
16
|
+
dim([0, 0, 0], [0, 0, h + t], { label: "Height", offset: 15 });
|
|
17
|
+
dim([0, 0, 0], [t, 0, 0], { label: "Wall", offset: -8, color: "#ffaa44" });
|
|
18
|
+
|
|
19
|
+
return bracket;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Test lib.elbow() — pipe bend primitive
|
|
2
|
+
const pipeR = param("Pipe Radius", 5, { min: 2, max: 15, unit: "mm" });
|
|
3
|
+
const bendR = param("Bend Radius", 25, { min: 10, max: 60, unit: "mm" });
|
|
4
|
+
const angle = param("Angle", 90, { min: 15, max: 180, unit: "°" });
|
|
5
|
+
|
|
6
|
+
// Basic elbow at default orientation
|
|
7
|
+
const basic = lib.elbow(pipeR, bendR, angle).color('#B87333');
|
|
8
|
+
|
|
9
|
+
// Elbow with from/to directions
|
|
10
|
+
const oriented = lib.elbow(pipeR, bendR, {
|
|
11
|
+
from: [0, 0, 1],
|
|
12
|
+
to: [1, 0, 0],
|
|
13
|
+
}).translate(80, 0, 0).color('#4488cc');
|
|
14
|
+
|
|
15
|
+
// Hollow elbow
|
|
16
|
+
const hollow = lib.elbow(pipeR, bendR, angle, { wall: 1.5 })
|
|
17
|
+
.translate(0, 80, 0).color('#888888');
|
|
18
|
+
|
|
19
|
+
return [
|
|
20
|
+
{ name: "Basic 90° Elbow", shape: basic },
|
|
21
|
+
{ name: "Oriented Elbow (Z→X)", shape: oriented },
|
|
22
|
+
{ name: "Hollow Elbow", shape: hollow },
|
|
23
|
+
];
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Standard-library exploded view: staged offsets + per-part direction overrides.
|
|
2
|
+
|
|
3
|
+
const explodeAmt = param("Explode", 0, { min: 0, max: 36, unit: "mm" });
|
|
4
|
+
|
|
5
|
+
const base = box(120, 80, 10, true).color('#5f6d7a');
|
|
6
|
+
const pedestal = box(70, 40, 20, true).translate(0, 0, 15).color('#6f7f8f');
|
|
7
|
+
|
|
8
|
+
const motorBody = cylinder(55, 16, 16, 40, true)
|
|
9
|
+
.pointAlong([1, 0, 0])
|
|
10
|
+
.translate(0, 0, 32)
|
|
11
|
+
.color('#8f9eab');
|
|
12
|
+
const shaft = cylinder(80, 4, 4, 24, true)
|
|
13
|
+
.pointAlong([1, 0, 0])
|
|
14
|
+
.translate(0, 0, 32)
|
|
15
|
+
.color('#d1d7de');
|
|
16
|
+
const rotorCap = cylinder(8, 18, 18, 36, true)
|
|
17
|
+
.pointAlong([1, 0, 0])
|
|
18
|
+
.translate(31, 0, 32)
|
|
19
|
+
.color('#9eacb9');
|
|
20
|
+
|
|
21
|
+
const boltTemplate = lib.bolt(6, 26).rotate(180, 0, 0).color('#d8dde3');
|
|
22
|
+
const bolts = [
|
|
23
|
+
boltTemplate.translate(-45, -25, 10),
|
|
24
|
+
boltTemplate.translate(45, -25, 10),
|
|
25
|
+
boltTemplate.translate(-45, 25, 10),
|
|
26
|
+
boltTemplate.translate(45, 25, 10),
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const explodedParts = [
|
|
30
|
+
{ name: "Base", shape: base },
|
|
31
|
+
{ name: "Pedestal", shape: pedestal, explode: { stage: 0.35, direction: [0, 0, 1] } },
|
|
32
|
+
{
|
|
33
|
+
name: "Drive",
|
|
34
|
+
group: [
|
|
35
|
+
{ name: "Motor Body", shape: motorBody },
|
|
36
|
+
{ name: "Rotor Cap", shape: rotorCap, explode: { stage: 1.1, direction: [1, 0, 0] } },
|
|
37
|
+
{ name: "Shaft", shape: shaft },
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "Fasteners",
|
|
42
|
+
group: bolts.map((b, i) => ({
|
|
43
|
+
name: `Bolt ${i + 1}`,
|
|
44
|
+
shape: b,
|
|
45
|
+
explode: { stage: 0.9, direction: 'z' },
|
|
46
|
+
})),
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
cutPlane("Center Section", [0, 1, 0], 0);
|
|
51
|
+
|
|
52
|
+
return lib.explode(explodedParts, {
|
|
53
|
+
amount: explodeAmt,
|
|
54
|
+
stages: [0.35, 0.7, 1.0],
|
|
55
|
+
mode: 'radial',
|
|
56
|
+
byName: {
|
|
57
|
+
"Shaft": { direction: [1, 0, 0], stage: 1.4 },
|
|
58
|
+
"Fasteners": { axisLock: 'z', stage: 0.45 },
|
|
59
|
+
},
|
|
60
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Extrude options — twist, taper, center.
|
|
2
|
+
//
|
|
3
|
+
// .extrude(height) is the basic form.
|
|
4
|
+
// Options: { twist, divisions, scaleTop, center }
|
|
5
|
+
|
|
6
|
+
const r = param("Radius", 20, { min: 10, max: 40, unit: "mm" });
|
|
7
|
+
const h = param("Height", 60, { min: 20, max: 120, unit: "mm" });
|
|
8
|
+
const twist = param("Twist", 90, { min: 0, max: 360, unit: "°" });
|
|
9
|
+
const taper = param("Taper", 0.5, { min: 0.1, max: 1.0 });
|
|
10
|
+
const spacing = 60;
|
|
11
|
+
|
|
12
|
+
// 1. Plain extrude
|
|
13
|
+
const plain = ngon(6, r).extrude(h)
|
|
14
|
+
.color('#4488cc');
|
|
15
|
+
|
|
16
|
+
// 2. Twisted extrude — needs divisions for smooth twist
|
|
17
|
+
const twisted = ngon(6, r).extrude(h, { twist: twist, divisions: 32 })
|
|
18
|
+
.translate(spacing, 0, 0)
|
|
19
|
+
.color('#cc8844');
|
|
20
|
+
|
|
21
|
+
// 3. Tapered extrude — scaleTop shrinks the top face
|
|
22
|
+
const tapered = circle2d(r).extrude(h, { scaleTop: taper })
|
|
23
|
+
.translate(2 * spacing, 0, 0)
|
|
24
|
+
.color('#44cc88');
|
|
25
|
+
|
|
26
|
+
// 4. Centered extrude — shape is centered along Z instead of starting at Z=0
|
|
27
|
+
const centered = rect(r * 1.5, r, true).extrude(h, { center: true })
|
|
28
|
+
.translate(3 * spacing, 0, 0)
|
|
29
|
+
.color('#cc44cc');
|
|
30
|
+
|
|
31
|
+
// 5. Combined: twist + taper
|
|
32
|
+
const combo = star(5, r, r * 0.5).extrude(h, {
|
|
33
|
+
twist: twist,
|
|
34
|
+
scaleTop: taper,
|
|
35
|
+
divisions: 32,
|
|
36
|
+
}).translate(4 * spacing, 0, 0).color('#cccc44');
|
|
37
|
+
|
|
38
|
+
return [
|
|
39
|
+
{ name: "Plain", shape: plain },
|
|
40
|
+
{ name: "Twisted", shape: twisted },
|
|
41
|
+
{ name: "Tapered", shape: tapered },
|
|
42
|
+
{ name: "Centered (Z)", shape: centered },
|
|
43
|
+
{ name: "Twist + Taper", shape: combo },
|
|
44
|
+
];
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Face gear demo: teeth on one face + perpendicular vertical spur gear.
|
|
2
|
+
|
|
3
|
+
const moduleSize = param("Module", 1.25, { min: 0.6, max: 3.0, step: 0.05 });
|
|
4
|
+
const faceTeeth = param("Face Teeth", 36, { min: 18, max: 84, integer: true });
|
|
5
|
+
const verticalTeeth = param("Vertical Teeth", 12, { min: 8, max: 30, integer: true });
|
|
6
|
+
const faceWidth = param("Face Width", 8, { min: 4, max: 18, unit: "mm" });
|
|
7
|
+
const toothHeight = param("Face Tooth Height", 1.25, { min: 0.4, max: 3.5, step: 0.05, unit: "mm" });
|
|
8
|
+
const backlash = param("Backlash", 0.04, { min: 0, max: 0.2, step: 0.01, unit: "mm" });
|
|
9
|
+
const topFace = param("Top Face Teeth (1/0)", 1, { min: 0, max: 1, integer: true });
|
|
10
|
+
|
|
11
|
+
const pair = lib.faceGearPair({
|
|
12
|
+
face: {
|
|
13
|
+
module: moduleSize,
|
|
14
|
+
teeth: faceTeeth,
|
|
15
|
+
pressureAngleDeg: 20,
|
|
16
|
+
faceWidth,
|
|
17
|
+
toothHeight,
|
|
18
|
+
side: topFace === 0 ? "bottom" : "top",
|
|
19
|
+
boreDiameter: moduleSize * 5,
|
|
20
|
+
},
|
|
21
|
+
vertical: {
|
|
22
|
+
module: moduleSize,
|
|
23
|
+
teeth: verticalTeeth,
|
|
24
|
+
pressureAngleDeg: 20,
|
|
25
|
+
faceWidth,
|
|
26
|
+
boreDiameter: moduleSize * 3,
|
|
27
|
+
},
|
|
28
|
+
backlash,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
for (const d of pair.diagnostics) {
|
|
32
|
+
const tag = `[${d.level}] ${d.code}`;
|
|
33
|
+
if (d.level === "error") console.error(tag, d.message);
|
|
34
|
+
else if (d.level === "warn") console.warn(tag, d.message);
|
|
35
|
+
else console.info(tag, d.message);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const faceColor = pair.status === "error" ? "#bf4b4b" : "#8aa8c2";
|
|
39
|
+
const verticalColor = pair.status === "error" ? "#c58b5d" : "#d5a15f";
|
|
40
|
+
|
|
41
|
+
return [
|
|
42
|
+
{ name: "Face Gear", shape: pair.face.color(faceColor) },
|
|
43
|
+
{ name: "Vertical Gear", shape: pair.vertical.color(verticalColor) },
|
|
44
|
+
];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Face Transformation History Demo
|
|
3
|
+
*
|
|
4
|
+
* Shows how to trace the transformation chain for each surface.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const base = box(100, 60, 20, true)
|
|
8
|
+
.translate(0, 0, 10)
|
|
9
|
+
.rotate(0, 0, 15);
|
|
10
|
+
|
|
11
|
+
const withHole = base
|
|
12
|
+
.hole('top', { diameter: 12, depth: 8, u: 20, v: 10 });
|
|
13
|
+
|
|
14
|
+
// Get the transformation history for the top face
|
|
15
|
+
const topHistory = withHole.faceHistory('top');
|
|
16
|
+
|
|
17
|
+
// Get history for the hole floor
|
|
18
|
+
const floorHistory = withHole.faceHistory('floor');
|
|
19
|
+
|
|
20
|
+
// Display the histories as dimensions for visibility
|
|
21
|
+
dim([0, 0, 0], [10, 0, 0], {
|
|
22
|
+
label: `Top: ${topHistory.origin.operation} → ${topHistory.transformations.length} transforms`,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
dim([0, 0, 0], [0, 10, 0], {
|
|
26
|
+
label: `Floor: ${floorHistory.origin.operation} → ${floorHistory.transformations.length} transforms`,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Also log to console for CLI output
|
|
30
|
+
console.log('=== Face Transformation History ===');
|
|
31
|
+
console.log('\nTop Face:');
|
|
32
|
+
console.log(' Origin:', topHistory.origin.operation);
|
|
33
|
+
console.log(' Transformations:');
|
|
34
|
+
topHistory.transformations.forEach((step, i) => {
|
|
35
|
+
console.log(` ${i + 1}. ${step.description}`);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
console.log('\nHole Floor Face:');
|
|
39
|
+
console.log(' Origin:', floorHistory.origin.operation);
|
|
40
|
+
console.log(' Transformations:');
|
|
41
|
+
floorHistory.transformations.forEach((step, i) => {
|
|
42
|
+
console.log(` ${i + 1}. ${step.description}`);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
return withHole;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const shellBase = roundedRect(90, 56, 6, true).extrude(28);
|
|
2
|
+
const cup = shellBase.shell(2.5, { openFaces: ['top'] }).color('#6f7b86');
|
|
3
|
+
const innerWallPad = roundedRect(16, 9, 1.8, true)
|
|
4
|
+
.onFace(cup, 'inner-side-right', { u: 0, v: -3, protrude: 0.05, selfAnchor: 'center' })
|
|
5
|
+
.extrude(1.4)
|
|
6
|
+
.toShape()
|
|
7
|
+
.color('#f2b16a');
|
|
8
|
+
|
|
9
|
+
const holeBase = roundedRect(72, 44, 5, true).extrude(20);
|
|
10
|
+
const drilled = holeBase.hole('top', { diameter: 8, u: 16, v: -8, depth: 10 }).color('#7a8792');
|
|
11
|
+
const floorBoss = circle2d(3)
|
|
12
|
+
.onFace(drilled, 'floor', { u: 0, v: 0, protrude: 0.05, selfAnchor: 'center' })
|
|
13
|
+
.extrude(1.2)
|
|
14
|
+
.toShape()
|
|
15
|
+
.color('#d46452');
|
|
16
|
+
|
|
17
|
+
const counterboreBase = roundedRect(72, 44, 5, true).extrude(20);
|
|
18
|
+
const counterboreExitFace = counterboreBase.face('bottom');
|
|
19
|
+
const counterbored = counterboreBase.hole('top', {
|
|
20
|
+
diameter: 6,
|
|
21
|
+
u: -14,
|
|
22
|
+
v: 10,
|
|
23
|
+
upToFace: counterboreExitFace,
|
|
24
|
+
counterbore: { diameter: 10, depth: 4 },
|
|
25
|
+
}).color('#748498');
|
|
26
|
+
const shoulderPad = rect(4, 3)
|
|
27
|
+
.onFace(counterbored, 'counterbore-floor', { u: 0, v: 0, protrude: 0.05, selfAnchor: 'center' })
|
|
28
|
+
.extrude(0.9)
|
|
29
|
+
.toShape()
|
|
30
|
+
.color('#f0c36c');
|
|
31
|
+
|
|
32
|
+
const cutBase = roundedRect(78, 46, 5, true).extrude(22);
|
|
33
|
+
const pocket = roundedRect(20, 12, 2, true)
|
|
34
|
+
.onFace(cutBase, 'front', { u: 0, v: 4, selfAnchor: 'center' });
|
|
35
|
+
const cut = cutBase.cutout(pocket, { depth: 8 }).color('#64707d');
|
|
36
|
+
const wallTab = rect(5, 4)
|
|
37
|
+
.onFace(cut, 'wall-right', { u: 0, v: 0, protrude: 0.05, selfAnchor: 'center' })
|
|
38
|
+
.extrude(1)
|
|
39
|
+
.toShape()
|
|
40
|
+
.color('#5ba6d6');
|
|
41
|
+
|
|
42
|
+
return [
|
|
43
|
+
{ name: 'Shell Inner Wall Pad', shape: union(cup, innerWallPad).translate(-165, 0, 0) },
|
|
44
|
+
{ name: 'Blind Hole Floor Boss', shape: union(drilled, floorBoss).translate(-55, 0, 0) },
|
|
45
|
+
{ name: 'Counterbore Shoulder Pad', shape: union(counterbored, shoulderPad).translate(55, 0, 0) },
|
|
46
|
+
{ name: 'Cut Wall Tab', shape: union(cut, wallTab).translate(165, 0, 0) },
|
|
47
|
+
];
|