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,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "code",
|
|
5
|
+
"execution_count": null,
|
|
6
|
+
"id": "mechanism-cell",
|
|
7
|
+
"metadata": {},
|
|
8
|
+
"outputs": [],
|
|
9
|
+
"source": [
|
|
10
|
+
"const upperLen = 90;\n",
|
|
11
|
+
"const foreLen = 75;\n",
|
|
12
|
+
"\n",
|
|
13
|
+
"const mech = assembly(\"Notebook Debug Arm\")\n",
|
|
14
|
+
" .addPart(\"Base\", box(44, 44, 18, true).color(\"#6e7f96\"))\n",
|
|
15
|
+
" .addPart(\n",
|
|
16
|
+
" \"Upper Arm\",\n",
|
|
17
|
+
" box(upperLen, 18, 18).translate(0, -9, -9).color(\"#d0a24c\"),\n",
|
|
18
|
+
" )\n",
|
|
19
|
+
" .addPart(\n",
|
|
20
|
+
" \"Forearm\",\n",
|
|
21
|
+
" box(foreLen, 14, 14).translate(0, -7, -7).color(\"#b86656\"),\n",
|
|
22
|
+
" )\n",
|
|
23
|
+
" .addJoint(\"shoulder\", \"revolute\", \"Base\", \"Upper Arm\", {\n",
|
|
24
|
+
" axis: [0, 1, 0],\n",
|
|
25
|
+
" min: -45,\n",
|
|
26
|
+
" max: 110,\n",
|
|
27
|
+
" default: 25,\n",
|
|
28
|
+
" frame: Transform.identity().translate(0, 0, 18),\n",
|
|
29
|
+
" })\n",
|
|
30
|
+
" .addJoint(\"elbow\", \"revolute\", \"Upper Arm\", \"Forearm\", {\n",
|
|
31
|
+
" axis: [0, -1, 0],\n",
|
|
32
|
+
" min: -10,\n",
|
|
33
|
+
" max: 135,\n",
|
|
34
|
+
" default: 35,\n",
|
|
35
|
+
" frame: Transform.identity().translate(upperLen, 0, 0),\n",
|
|
36
|
+
" });\n"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"cell_type": "code",
|
|
41
|
+
"execution_count": null,
|
|
42
|
+
"id": "collision-cell",
|
|
43
|
+
"metadata": {},
|
|
44
|
+
"outputs": [],
|
|
45
|
+
"source": [
|
|
46
|
+
"const solved = mech.solve({ shoulder: 38, elbow: 72 });\n",
|
|
47
|
+
"const collisions = solved.collisionReport({\n",
|
|
48
|
+
" minOverlapVolume: 0.5,\n",
|
|
49
|
+
" ignorePairs: [[\"Upper Arm\", \"Forearm\"]],\n",
|
|
50
|
+
"});\n",
|
|
51
|
+
"\n",
|
|
52
|
+
"console.log(\"Collision report\", collisions);\n",
|
|
53
|
+
"show(solved.toScene());\n"
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"cell_type": "code",
|
|
58
|
+
"execution_count": null,
|
|
59
|
+
"id": "preview-cell",
|
|
60
|
+
"metadata": {},
|
|
61
|
+
"outputs": [],
|
|
62
|
+
"source": [
|
|
63
|
+
"const sweepFrames = mech.sweepJoint(\"elbow\", -10, 135, 14, { shoulder: 38 });\n",
|
|
64
|
+
"const collidingSteps = sweepFrames.filter((step) => step.collisions.length > 0).length;\n",
|
|
65
|
+
"\n",
|
|
66
|
+
"console.log(\"Sweep collisions\", collidingSteps, \"of\", sweepFrames.length);\n",
|
|
67
|
+
"show(mech.solve({ shoulder: 38, elbow: 90 }).toScene());\n"
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
],
|
|
71
|
+
"metadata": {
|
|
72
|
+
"forgecad": {
|
|
73
|
+
"format": "forgecad-notebook/v1",
|
|
74
|
+
"previewCellId": "preview-cell",
|
|
75
|
+
"title": "Notebook Assembly Debug"
|
|
76
|
+
},
|
|
77
|
+
"kernelspec": {
|
|
78
|
+
"display_name": "ForgeCAD",
|
|
79
|
+
"language": "javascript",
|
|
80
|
+
"name": "forgecad"
|
|
81
|
+
},
|
|
82
|
+
"language_info": {
|
|
83
|
+
"name": "javascript",
|
|
84
|
+
"file_extension": ".js",
|
|
85
|
+
"mimetype": "text/javascript"
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
"nbformat": 4,
|
|
89
|
+
"nbformat_minor": 5
|
|
90
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "code",
|
|
5
|
+
"execution_count": null,
|
|
6
|
+
"id": "setup-cell",
|
|
7
|
+
"metadata": {},
|
|
8
|
+
"outputs": [],
|
|
9
|
+
"source": [
|
|
10
|
+
"const width = param(\"Width\", 120, { min: 80, max: 180, unit: \"mm\" });\n",
|
|
11
|
+
"const depth = param(\"Depth\", 80, { min: 50, max: 140, unit: \"mm\" });\n",
|
|
12
|
+
"const height = param(\"Height\", 14, { min: 8, max: 30, unit: \"mm\" });\n",
|
|
13
|
+
"\n",
|
|
14
|
+
"const base = roundedRect(width, depth, 10)\n",
|
|
15
|
+
" .extrude(height)\n",
|
|
16
|
+
" .color(\"#5f87c6\");\n",
|
|
17
|
+
"\n",
|
|
18
|
+
"show(base);\n"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"cell_type": "code",
|
|
23
|
+
"execution_count": null,
|
|
24
|
+
"id": "inspect-pocket-cell",
|
|
25
|
+
"metadata": {},
|
|
26
|
+
"outputs": [],
|
|
27
|
+
"source": [
|
|
28
|
+
"const pocket = roundedRect(width - 24, depth - 24, 8)\n",
|
|
29
|
+
" .extrude(height - 3)\n",
|
|
30
|
+
" .translate(12, 12, 3)\n",
|
|
31
|
+
" .color(\"#d67a61\");\n",
|
|
32
|
+
"\n",
|
|
33
|
+
"show([\n",
|
|
34
|
+
" { name: \"Base\", shape: base },\n",
|
|
35
|
+
" { name: \"Pocket\", shape: pocket },\n",
|
|
36
|
+
"]);\n"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"cell_type": "code",
|
|
41
|
+
"execution_count": null,
|
|
42
|
+
"id": "preview-cell",
|
|
43
|
+
"metadata": {},
|
|
44
|
+
"outputs": [],
|
|
45
|
+
"source": [
|
|
46
|
+
"const part = base.subtract(pocket);\n",
|
|
47
|
+
"\n",
|
|
48
|
+
"dim([0, 0, 0], [width, 0, 0], { label: \"Width\" });\n",
|
|
49
|
+
"dim([0, 0, 0], [0, depth, 0], { label: \"Depth\", offset: 14 });\n",
|
|
50
|
+
"cutPlane(\"Center Section\", [1, 0, 0], width / 2);\n",
|
|
51
|
+
"\n",
|
|
52
|
+
"return part;\n"
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"metadata": {
|
|
57
|
+
"forgecad": {
|
|
58
|
+
"format": "forgecad-notebook/v1",
|
|
59
|
+
"previewCellId": "preview-cell",
|
|
60
|
+
"title": "Notebook Iteration"
|
|
61
|
+
},
|
|
62
|
+
"kernelspec": {
|
|
63
|
+
"display_name": "ForgeCAD",
|
|
64
|
+
"language": "javascript",
|
|
65
|
+
"name": "forgecad"
|
|
66
|
+
},
|
|
67
|
+
"language_info": {
|
|
68
|
+
"name": "javascript",
|
|
69
|
+
"file_extension": ".js",
|
|
70
|
+
"mimetype": "text/javascript"
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"nbformat": 4,
|
|
74
|
+
"nbformat_minor": 5
|
|
75
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Patterns — linearPattern and circularPattern for repeating shapes.
|
|
2
|
+
|
|
3
|
+
const count = param("Count", 6, { min: 2, max: 12, integer: true });
|
|
4
|
+
const spacing = param("Spacing", 20, { min: 10, max: 40, unit: "mm" });
|
|
5
|
+
const radius = param("Ring Radius", 40, { min: 20, max: 80, unit: "mm" });
|
|
6
|
+
|
|
7
|
+
// --- linearPattern: repeat along a direction ---
|
|
8
|
+
const peg = cylinder(15, 4).color('#4488cc');
|
|
9
|
+
const row = linearPattern(peg, count, spacing, 0);
|
|
10
|
+
|
|
11
|
+
// --- circularPattern: repeat around Z axis ---
|
|
12
|
+
const hole = cylinder(8, 3).translate(radius, 0, 0).color('#cc4444');
|
|
13
|
+
const ring = circularPattern(hole, count);
|
|
14
|
+
|
|
15
|
+
// --- mirrorCopy: mirror + union with original ---
|
|
16
|
+
const halfBracket = box(40, 10, 20).color('#44cc88');
|
|
17
|
+
const fullBracket = mirrorCopy(halfBracket, [1, 0, 0]).translate(0, 0, 40);
|
|
18
|
+
|
|
19
|
+
// Show a base plate with the circular holes subtracted
|
|
20
|
+
const plate = cylinder(10, radius + 15).color('#888888').translate(0, 80, 0);
|
|
21
|
+
const holeRing = circularPattern(
|
|
22
|
+
cylinder(12, 3).translate(radius, 0, -1),
|
|
23
|
+
count
|
|
24
|
+
).translate(0, 80, 0);
|
|
25
|
+
const drilled = plate.subtract(holeRing);
|
|
26
|
+
|
|
27
|
+
return [
|
|
28
|
+
{ name: "Linear Pattern", shape: row },
|
|
29
|
+
{ name: "Circular Pattern", shape: ring },
|
|
30
|
+
{ name: "Mirror Copy", shape: fullBracket },
|
|
31
|
+
{ name: "Drilled Plate", shape: drilled },
|
|
32
|
+
];
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// pointAlong() — orient a cylinder's axis without thinking about Euler angles.
|
|
2
|
+
//
|
|
3
|
+
// Cylinders default to Z-up. To lay one along X or Y:
|
|
4
|
+
// ❌ cylinder(80, 5).rotate(90, 0, 0) — which axis? confusing
|
|
5
|
+
// ✅ cylinder(80, 5).pointAlong([0, 1, 0]) — "point along Y"
|
|
6
|
+
//
|
|
7
|
+
// After pointAlong, the cylinder starts at origin and extends in that direction.
|
|
8
|
+
// Always call pointAlong BEFORE translate/attachTo.
|
|
9
|
+
|
|
10
|
+
const len = param("Length", 80, { min: 30, max: 150, unit: "mm" });
|
|
11
|
+
const r = param("Radius", 5, { min: 2, max: 15, unit: "mm" });
|
|
12
|
+
const spacing = 40;
|
|
13
|
+
|
|
14
|
+
// Default: along +Z (up)
|
|
15
|
+
const zCyl = cylinder(len, r).color('#4444cc')
|
|
16
|
+
.translate(0, 0, 0);
|
|
17
|
+
const zTip = sphere(r * 1.5).color('#6666ff')
|
|
18
|
+
.translate(0, 0, len);
|
|
19
|
+
|
|
20
|
+
// Along +X (right)
|
|
21
|
+
const xCyl = cylinder(len, r).color('#cc4444')
|
|
22
|
+
.pointAlong([1, 0, 0])
|
|
23
|
+
.translate(0, spacing, 0);
|
|
24
|
+
const xTip = sphere(r * 1.5).color('#ff6666')
|
|
25
|
+
.translate(len, spacing, 0);
|
|
26
|
+
|
|
27
|
+
// Along +Y (forward)
|
|
28
|
+
const yCyl = cylinder(len, r).color('#44cc44')
|
|
29
|
+
.pointAlong([0, 1, 0])
|
|
30
|
+
.translate(0, 0, 0)
|
|
31
|
+
.translate(spacing, 0, 0);
|
|
32
|
+
const yTip = sphere(r * 1.5).color('#66ff66')
|
|
33
|
+
.translate(spacing, len, 0);
|
|
34
|
+
|
|
35
|
+
// Along diagonal [1, 1, 1]
|
|
36
|
+
const dCyl = cylinder(len, r).color('#cccc44')
|
|
37
|
+
.pointAlong([1, 1, 1])
|
|
38
|
+
.translate(spacing, spacing, 0);
|
|
39
|
+
const dLen = len / Math.sqrt(3); // projected length per axis
|
|
40
|
+
const dTip = sphere(r * 1.5).color('#ffff66')
|
|
41
|
+
.translate(spacing + dLen, spacing + dLen, dLen);
|
|
42
|
+
|
|
43
|
+
return [
|
|
44
|
+
{ name: "Z-axis (default)", shape: zCyl },
|
|
45
|
+
{ name: "Z tip", shape: zTip },
|
|
46
|
+
{ name: "X-axis (pointAlong [1,0,0])", shape: xCyl },
|
|
47
|
+
{ name: "X tip", shape: xTip },
|
|
48
|
+
{ name: "Y-axis (pointAlong [0,1,0])", shape: yCyl },
|
|
49
|
+
{ name: "Y tip", shape: yTip },
|
|
50
|
+
{ name: "Diagonal (pointAlong [1,1,1])", shape: dCyl },
|
|
51
|
+
{ name: "Diagonal tip", shape: dTip },
|
|
52
|
+
];
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// 20x20 B-type slot 6 profile extrusion.
|
|
2
|
+
// Demonstrates:
|
|
3
|
+
// - algorithmic 2D profile generation (`lib.tSlotProfile`)
|
|
4
|
+
// - direct 3D helper (`lib.profile2020BSlot6`)
|
|
5
|
+
// - parameterized technical dimensions
|
|
6
|
+
|
|
7
|
+
const length = param("Length", 220, { min: 40, max: 800, unit: "mm" });
|
|
8
|
+
const slotDepth = param("Slot Depth", 5.5, { min: 4.6, max: 6.6, step: 0.1, unit: "mm" });
|
|
9
|
+
const slotInner = param("Slot Inner Width", 8.2, { min: 7, max: 10.5, step: 0.1, unit: "mm" });
|
|
10
|
+
const centerBore = param("Center Bore", 5.5, { min: 0, max: 6.5, step: 0.1, unit: "mm" });
|
|
11
|
+
|
|
12
|
+
const profile2d = lib.profile2020BSlot6Profile({
|
|
13
|
+
slotInnerWidth: slotInner,
|
|
14
|
+
slotDepth,
|
|
15
|
+
centerBoreDia: centerBore,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const extrusion = lib.profile2020BSlot6(length, {
|
|
19
|
+
center: true,
|
|
20
|
+
slotDepth,
|
|
21
|
+
slotInnerWidth: slotInner,
|
|
22
|
+
centerBoreDia: centerBore,
|
|
23
|
+
}).color('#98a7b8');
|
|
24
|
+
|
|
25
|
+
// Visual dimensions
|
|
26
|
+
dim([-10, -10, 0], [10, -10, 0], { label: "20 mm", offset: -8, color: "#ffaa44" });
|
|
27
|
+
dim([10, -10, 0], [10, 10, 0], { label: "20 mm", offset: 10, color: "#ffaa44" });
|
|
28
|
+
dim([0, 0, -length / 2], [0, 0, length / 2], { label: "Length", offset: 16, color: "#66ccff" });
|
|
29
|
+
if (centerBore > 0) {
|
|
30
|
+
dim([-centerBore / 2, 0, 0], [centerBore / 2, 0, 0], { label: "Center bore", offset: -14, color: "#88dd88" });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return [
|
|
34
|
+
{ name: "2D Profile", sketch: profile2d.translate(-34, 0), color: "#f3c98b" },
|
|
35
|
+
{ name: "3D Extrusion", shape: extrusion.translate(36, 0, 0), color: "#98a7b8" },
|
|
36
|
+
];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// rotateAroundTo() — solve the angle around an axis from geometry instead of manual trig.
|
|
2
|
+
|
|
3
|
+
function makeArm(color) {
|
|
4
|
+
return box(80, 8, 8, true)
|
|
5
|
+
.translate(40, 0, 0)
|
|
6
|
+
.color(color)
|
|
7
|
+
.withReferences({
|
|
8
|
+
points: {
|
|
9
|
+
tip: [80, 0, 0],
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function makeScene(offsetX, targetPoint, options, color) {
|
|
15
|
+
const pivot = [offsetX, 0, 0];
|
|
16
|
+
const arm = makeArm('#7c8794').translate(offsetX, 0, 0);
|
|
17
|
+
const solved = arm.rotateAroundTo([0, 0, 1], pivot, 'tip', targetPoint, options).color(color);
|
|
18
|
+
|
|
19
|
+
return [
|
|
20
|
+
{ name: `${color} Axis`, shape: cylinder(90, 1.4).translate(offsetX, 0, 45).color('#999999') },
|
|
21
|
+
{ name: `${color} Pivot`, shape: cylinder(6, 8).translate(offsetX, 0, -3).color('#555555') },
|
|
22
|
+
{ name: `${color} Start`, shape: arm.translate(0, 0, -7) },
|
|
23
|
+
{ name: `${color} Solved`, shape: solved.translate(0, 0, 7) },
|
|
24
|
+
{ name: `${color} Target`, shape: sphere(3).translate(targetPoint[0], targetPoint[1], targetPoint[2]).color('#cc4444') },
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return [
|
|
29
|
+
...makeScene(-70, [-38, 32, 24], { mode: 'plane' }, '#4a90e2'),
|
|
30
|
+
...makeScene(70, [102, 32, 0], { mode: 'line' }, '#4cc48a'),
|
|
31
|
+
];
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Runtime joints demo
|
|
2
|
+
// Move the "Joints" sliders in the View Panel for smooth articulation,
|
|
3
|
+
// or use the Animation controls (play/pause + scrub), all without recompute.
|
|
4
|
+
// Demonstrates linked joints via couplings (Ankle is driven by Hip + Knee).
|
|
5
|
+
|
|
6
|
+
const body = box(150, 70, 36, true).translate(0, 0, 40).color('#6e7b88');
|
|
7
|
+
|
|
8
|
+
const upperLen = 84;
|
|
9
|
+
const lowerLen = 86;
|
|
10
|
+
const footLen = 48;
|
|
11
|
+
|
|
12
|
+
const upper = box(upperLen, 18, 18).translate(0, -9, -9).color('#7da2d6');
|
|
13
|
+
const lower = box(lowerLen, 16, 16).translate(0, -8, -8).color('#8db3e4');
|
|
14
|
+
const foot = box(footLen, 24, 10, true).translate(footLen * 0.5 - 8, 0, -10).color('#9dbfe8');
|
|
15
|
+
|
|
16
|
+
const leg = assembly('Leg Runtime Demo')
|
|
17
|
+
.addPart('Body', body)
|
|
18
|
+
.addPart('Upper Leg', upper)
|
|
19
|
+
.addPart('Lower Leg', lower)
|
|
20
|
+
.addPart('Foot', foot)
|
|
21
|
+
.addRevolute('hip', 'Body', 'Upper Leg', {
|
|
22
|
+
axis: [0, -1, 0],
|
|
23
|
+
frame: Transform.identity().translate(34, 24, 40),
|
|
24
|
+
})
|
|
25
|
+
.addRevolute('knee', 'Upper Leg', 'Lower Leg', {
|
|
26
|
+
axis: [0, -1, 0],
|
|
27
|
+
frame: Transform.identity().translate(upperLen, 0, 0),
|
|
28
|
+
})
|
|
29
|
+
.addRevolute('ankle', 'Lower Leg', 'Foot', {
|
|
30
|
+
axis: [0, -1, 0],
|
|
31
|
+
frame: Transform.identity().translate(lowerLen, 0, 0),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const solved = leg.solve({
|
|
35
|
+
hip: 0,
|
|
36
|
+
knee: 0,
|
|
37
|
+
ankle: 0,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
viewConfig({
|
|
41
|
+
jointOverlay: {
|
|
42
|
+
axisColor: '#13dfff',
|
|
43
|
+
arcColor: '#ff7a1a',
|
|
44
|
+
zeroColor: '#ffe26a',
|
|
45
|
+
axisArrowLengthScale: 0.16,
|
|
46
|
+
axisArrowRadiusScale: 0.052,
|
|
47
|
+
arcArrowLengthScale: 0.12,
|
|
48
|
+
arcArrowRadiusScale: 0.038,
|
|
49
|
+
arcLineRadiusScale: 0.02,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
jointsView({
|
|
54
|
+
joints: [
|
|
55
|
+
{
|
|
56
|
+
name: 'Hip',
|
|
57
|
+
child: 'Upper Leg',
|
|
58
|
+
parent: 'Body',
|
|
59
|
+
type: 'revolute',
|
|
60
|
+
axis: [0, -1, 0],
|
|
61
|
+
pivot: [34, 24, 40],
|
|
62
|
+
min: -50,
|
|
63
|
+
max: 80,
|
|
64
|
+
default: 10,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'Knee',
|
|
68
|
+
child: 'Lower Leg',
|
|
69
|
+
parent: 'Upper Leg',
|
|
70
|
+
type: 'revolute',
|
|
71
|
+
axis: [0, -1, 0],
|
|
72
|
+
pivot: [34 + upperLen, 24, 40],
|
|
73
|
+
min: -5,
|
|
74
|
+
max: 125,
|
|
75
|
+
default: 40,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'Ankle',
|
|
79
|
+
child: 'Foot',
|
|
80
|
+
parent: 'Lower Leg',
|
|
81
|
+
type: 'revolute',
|
|
82
|
+
axis: [0, -1, 0],
|
|
83
|
+
pivot: [34 + upperLen + lowerLen, 24, 40],
|
|
84
|
+
min: -40,
|
|
85
|
+
max: 55,
|
|
86
|
+
default: -10,
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
couplings: [
|
|
90
|
+
{
|
|
91
|
+
joint: 'Ankle',
|
|
92
|
+
terms: [
|
|
93
|
+
{ joint: 'Knee', ratio: -0.35 },
|
|
94
|
+
{ joint: 'Hip', ratio: 0.18 },
|
|
95
|
+
],
|
|
96
|
+
offset: 6,
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
animations: [
|
|
100
|
+
{
|
|
101
|
+
name: 'Step',
|
|
102
|
+
duration: 1.8,
|
|
103
|
+
loop: true,
|
|
104
|
+
keyframes: [
|
|
105
|
+
{ at: 0.0, values: { Hip: 18, Knee: 42 } },
|
|
106
|
+
{ at: 0.25, values: { Hip: -20, Knee: 22 } },
|
|
107
|
+
{ at: 0.5, values: { Hip: 8, Knee: 86 } },
|
|
108
|
+
{ at: 0.75, values: { Hip: 24, Knee: 34 } },
|
|
109
|
+
{ at: 1.0, values: { Hip: 18, Knee: 42 } },
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
defaultAnimation: 'Step',
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return solved.toScene();
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// SDF export demo: four-wheel differential-drive rover with a demo world.
|
|
2
|
+
// Run:
|
|
3
|
+
// forgecad export sdf examples/api/sdf-rover-demo.forge.js
|
|
4
|
+
//
|
|
5
|
+
// Then launch Gazebo against the generated package:
|
|
6
|
+
// export GZ_SIM_RESOURCE_PATH="examples/api/sdf-rover-demo.forge.sdfpkg/models${GZ_SIM_RESOURCE_PATH:+:$GZ_SIM_RESOURCE_PATH}"
|
|
7
|
+
// gz sim -s -r examples/api/sdf-rover-demo.forge.sdfpkg/worlds/forge_scout_trial.sdf
|
|
8
|
+
// gz sim -g examples/api/sdf-rover-demo.forge.sdfpkg/worlds/forge_scout_trial.sdf
|
|
9
|
+
//
|
|
10
|
+
// Click the 3D view and use W/X/A/D or Q/E/Z/C to drive. S or Space stops the rover.
|
|
11
|
+
|
|
12
|
+
const chassisLength = 430;
|
|
13
|
+
const chassisWidth = 260;
|
|
14
|
+
const chassisHeight = 58;
|
|
15
|
+
const roofLength = 210;
|
|
16
|
+
const roofWidth = 150;
|
|
17
|
+
const roofHeight = 46;
|
|
18
|
+
const bumperLength = 150;
|
|
19
|
+
const bumperWidth = 300;
|
|
20
|
+
const bumperDepth = 24;
|
|
21
|
+
|
|
22
|
+
const wheelRadius = 72;
|
|
23
|
+
const wheelWidth = 34;
|
|
24
|
+
const wheelTrack = 320;
|
|
25
|
+
const wheelbase = 250;
|
|
26
|
+
const groundClearance = 26;
|
|
27
|
+
const bodyZ = wheelRadius + groundClearance + chassisHeight * 0.5;
|
|
28
|
+
|
|
29
|
+
const baseDeck = box(chassisLength, chassisWidth, chassisHeight, true)
|
|
30
|
+
.translate(0, 0, bodyZ);
|
|
31
|
+
|
|
32
|
+
const roofPod = box(roofLength, roofWidth, roofHeight, true)
|
|
33
|
+
.translate(20, 0, bodyZ + 40);
|
|
34
|
+
|
|
35
|
+
const bumper = hull3d(
|
|
36
|
+
box(54, bumperWidth, bumperDepth, true).translate(chassisLength * 0.5 - 18, 0, wheelRadius + 6),
|
|
37
|
+
box(bumperLength, bumperWidth - 42, bumperDepth * 0.7, true).translate(chassisLength * 0.5 + 46, 0, wheelRadius - 10),
|
|
38
|
+
).color('#c8742b');
|
|
39
|
+
|
|
40
|
+
const sensorMast = union(
|
|
41
|
+
cylinder(92, 10, undefined, 40, true).translate(58, 0, bodyZ + 78),
|
|
42
|
+
box(78, 34, 26, true).translate(88, 0, bodyZ + 126),
|
|
43
|
+
).color('#d7dee8');
|
|
44
|
+
|
|
45
|
+
const chassis = union(baseDeck, roofPod)
|
|
46
|
+
.color('#60707d');
|
|
47
|
+
|
|
48
|
+
const wheelTire = difference(
|
|
49
|
+
cylinder(wheelWidth, wheelRadius, undefined, 64, true).pointAlong([0, 1, 0]),
|
|
50
|
+
cylinder(wheelWidth + 2, wheelRadius * 0.56, undefined, 48, true).pointAlong([0, 1, 0]),
|
|
51
|
+
).color('#1d2329');
|
|
52
|
+
|
|
53
|
+
const wheelRim = union(
|
|
54
|
+
cylinder(wheelWidth * 0.86, wheelRadius * 0.52, undefined, 40, true).pointAlong([0, 1, 0]),
|
|
55
|
+
cylinder(wheelWidth * 1.02, wheelRadius * 0.16, undefined, 28, true).pointAlong([0, 1, 0]),
|
|
56
|
+
).color('#b8c5d3');
|
|
57
|
+
|
|
58
|
+
const wheel = group(
|
|
59
|
+
{ name: "Tire", shape: wheelTire },
|
|
60
|
+
{ name: "Rim", shape: wheelRim }
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const rover = assembly('Forge Scout Rover')
|
|
64
|
+
.addPart('Chassis', group(
|
|
65
|
+
{ name: "Deck", shape: chassis },
|
|
66
|
+
{ name: "Bumper", shape: bumper },
|
|
67
|
+
{ name: "Sensor Mast", shape: sensorMast }
|
|
68
|
+
), {
|
|
69
|
+
metadata: {
|
|
70
|
+
material: 'PETG-CF',
|
|
71
|
+
process: 'FDM',
|
|
72
|
+
massKg: 13.5,
|
|
73
|
+
notes: 'Battery bay lives under the roof pod.',
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
.addPart('Front Left Wheel', wheel, {
|
|
77
|
+
metadata: { material: 'TPU + PLA hub', massKg: 0.95 },
|
|
78
|
+
})
|
|
79
|
+
.addPart('Front Right Wheel', wheel, {
|
|
80
|
+
metadata: { material: 'TPU + PLA hub', massKg: 0.95 },
|
|
81
|
+
})
|
|
82
|
+
.addPart('Rear Left Wheel', wheel, {
|
|
83
|
+
metadata: { material: 'TPU + PLA hub', massKg: 0.95 },
|
|
84
|
+
})
|
|
85
|
+
.addPart('Rear Right Wheel', wheel, {
|
|
86
|
+
metadata: { material: 'TPU + PLA hub', massKg: 0.95 },
|
|
87
|
+
})
|
|
88
|
+
.addRevolute('frontLeftWheel', 'Chassis', 'Front Left Wheel', {
|
|
89
|
+
axis: [0, 1, 0],
|
|
90
|
+
frame: Transform.identity().translate(wheelbase * 0.5, wheelTrack * 0.5, wheelRadius),
|
|
91
|
+
effort: 22,
|
|
92
|
+
velocity: 1320,
|
|
93
|
+
damping: 0.12,
|
|
94
|
+
friction: 0.03,
|
|
95
|
+
})
|
|
96
|
+
.addRevolute('frontRightWheel', 'Chassis', 'Front Right Wheel', {
|
|
97
|
+
axis: [0, 1, 0],
|
|
98
|
+
frame: Transform.identity().translate(wheelbase * 0.5, -wheelTrack * 0.5, wheelRadius),
|
|
99
|
+
effort: 22,
|
|
100
|
+
velocity: 1320,
|
|
101
|
+
damping: 0.12,
|
|
102
|
+
friction: 0.03,
|
|
103
|
+
})
|
|
104
|
+
.addRevolute('rearLeftWheel', 'Chassis', 'Rear Left Wheel', {
|
|
105
|
+
axis: [0, 1, 0],
|
|
106
|
+
frame: Transform.identity().translate(-wheelbase * 0.5, wheelTrack * 0.5, wheelRadius),
|
|
107
|
+
effort: 22,
|
|
108
|
+
velocity: 1320,
|
|
109
|
+
damping: 0.12,
|
|
110
|
+
friction: 0.03,
|
|
111
|
+
})
|
|
112
|
+
.addRevolute('rearRightWheel', 'Chassis', 'Rear Right Wheel', {
|
|
113
|
+
axis: [0, 1, 0],
|
|
114
|
+
frame: Transform.identity().translate(-wheelbase * 0.5, -wheelTrack * 0.5, wheelRadius),
|
|
115
|
+
effort: 22,
|
|
116
|
+
velocity: 1320,
|
|
117
|
+
damping: 0.12,
|
|
118
|
+
friction: 0.03,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
robotExport({
|
|
122
|
+
assembly: rover,
|
|
123
|
+
modelName: 'Forge Scout Rover',
|
|
124
|
+
links: {
|
|
125
|
+
Chassis: { massKg: 13.5 },
|
|
126
|
+
'Front Left Wheel': { massKg: 0.95 },
|
|
127
|
+
'Front Right Wheel': { massKg: 0.95 },
|
|
128
|
+
'Rear Left Wheel': { massKg: 0.95 },
|
|
129
|
+
'Rear Right Wheel': { massKg: 0.95 },
|
|
130
|
+
},
|
|
131
|
+
plugins: {
|
|
132
|
+
diffDrive: {
|
|
133
|
+
leftJoints: ['frontLeftWheel', 'rearLeftWheel'],
|
|
134
|
+
rightJoints: ['frontRightWheel', 'rearRightWheel'],
|
|
135
|
+
wheelSeparationMm: wheelTrack,
|
|
136
|
+
wheelRadiusMm: wheelRadius,
|
|
137
|
+
maxLinearVelocity: 1.8,
|
|
138
|
+
maxAngularVelocity: 2.8,
|
|
139
|
+
linearAcceleration: 1.6,
|
|
140
|
+
angularAcceleration: 3.2,
|
|
141
|
+
},
|
|
142
|
+
jointStatePublisher: {
|
|
143
|
+
enabled: true,
|
|
144
|
+
updateRate: 30,
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
world: {
|
|
148
|
+
generateDemoWorld: true,
|
|
149
|
+
name: 'Forge Scout Trial',
|
|
150
|
+
spawnPose: [-1800, 0, 120, 0, 0, 0],
|
|
151
|
+
keyboardTeleop: {
|
|
152
|
+
enabled: true,
|
|
153
|
+
linearStep: 0.9,
|
|
154
|
+
angularStep: 1.35,
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
return rover.solve().toScene();
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Section Plane Visualization — renderer-side guides for active cut planes.
|
|
2
|
+
//
|
|
3
|
+
// How to use:
|
|
4
|
+
// 1) Toggle planes in View Panel -> Cut Planes
|
|
5
|
+
// 2) Adjust View Panel -> Section Visuals (fill, border, normal axis)
|
|
6
|
+
// 3) "Probe" is excluded from both cuts, so it stays intact for alignment checks.
|
|
7
|
+
//
|
|
8
|
+
// No helper solids are needed in your model. Guides are viewport-only overlays.
|
|
9
|
+
|
|
10
|
+
const width = param("Width", 120, { min: 80, max: 180, unit: "mm" });
|
|
11
|
+
const depth = param("Depth", 80, { min: 50, max: 140, unit: "mm" });
|
|
12
|
+
const height = param("Height", 70, { min: 40, max: 120, unit: "mm" });
|
|
13
|
+
const wall = param("Wall", 8, { min: 3, max: 16, unit: "mm" });
|
|
14
|
+
|
|
15
|
+
const cutX = param("Cut X", 0, { min: -80, max: 80, unit: "mm" });
|
|
16
|
+
const cutZ = param("Cut Z", 10, { min: -30, max: 80, unit: "mm" });
|
|
17
|
+
|
|
18
|
+
cutPlane("Internal X", [1, 0, 0], cutX, { exclude: "Probe" });
|
|
19
|
+
cutPlane("Internal Z", [0, 0, 1], cutZ, { exclude: "Probe" });
|
|
20
|
+
|
|
21
|
+
const shell = box(width, depth, height, true);
|
|
22
|
+
const cavity = box(width - wall * 2, depth - wall * 2, height - wall * 1.6, true).translate(0, 0, wall * 0.2);
|
|
23
|
+
const passX = cylinder(width + 8, Math.min(depth, height) * 0.12, undefined, 48, true).rotate(0, 90, 0);
|
|
24
|
+
const passY = cylinder(depth + 8, Math.min(width, height) * 0.09, undefined, 48, true).rotate(90, 0, 0).translate(0, 0, 12);
|
|
25
|
+
const probe = cylinder(height + 20, 2.5, undefined, 36, true)
|
|
26
|
+
.translate(width * 0.22, depth * 0.18, 0)
|
|
27
|
+
.color("#f3a847");
|
|
28
|
+
|
|
29
|
+
const housing = shell
|
|
30
|
+
.subtract(cavity)
|
|
31
|
+
.subtract(passX)
|
|
32
|
+
.subtract(passY)
|
|
33
|
+
.color("#8aa7c8");
|
|
34
|
+
|
|
35
|
+
return [
|
|
36
|
+
{ name: "Housing", shape: housing },
|
|
37
|
+
{ name: "Probe", shape: probe },
|
|
38
|
+
];
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// 2D sketch basics — primitives, booleans, offset, then extrude to 3D.
|
|
2
|
+
|
|
3
|
+
const wall = param("Wall", 3, { min: 1, max: 8, unit: "mm" });
|
|
4
|
+
const height = param("Height", 30, { min: 10, max: 80, unit: "mm" });
|
|
5
|
+
|
|
6
|
+
// --- Sketch primitives ---
|
|
7
|
+
const r = rect(40, 30);
|
|
8
|
+
const c = circle2d(15).translate(20, 15);
|
|
9
|
+
const hex = ngon(6, 12).translate(70, 15);
|
|
10
|
+
const rounded = roundedRect(40, 30, 5).translate(100, 0);
|
|
11
|
+
const oblong = slot(40, 15).translate(0, -30);
|
|
12
|
+
|
|
13
|
+
// --- 2D booleans ---
|
|
14
|
+
// Subtract circle from rectangle → plate with hole
|
|
15
|
+
const plateSketch = rect(50, 40).subtract(circle2d(10).translate(25, 20));
|
|
16
|
+
|
|
17
|
+
// --- Offset: inflate/deflate contours ---
|
|
18
|
+
const outer = ngon(6, 20);
|
|
19
|
+
const inner = outer.offset(-wall);
|
|
20
|
+
const shellSketch = outer.subtract(inner); // hollow hexagon
|
|
21
|
+
|
|
22
|
+
// --- Extrude to 3D ---
|
|
23
|
+
const plate3d = plateSketch.extrude(height).translate(0, 60, 0).color('#4488cc');
|
|
24
|
+
const shell3d = shellSketch.extrude(height).translate(70, 60, 0).color('#cc8844');
|
|
25
|
+
|
|
26
|
+
// --- Path builder ---
|
|
27
|
+
const bracket = path()
|
|
28
|
+
.moveTo(0, 0)
|
|
29
|
+
.lineH(30)
|
|
30
|
+
.lineV(40)
|
|
31
|
+
.lineH(-10)
|
|
32
|
+
.lineV(-30)
|
|
33
|
+
.lineH(-20)
|
|
34
|
+
.close()
|
|
35
|
+
.extrude(5)
|
|
36
|
+
.translate(130, 60, 0)
|
|
37
|
+
.color('#44cc88');
|
|
38
|
+
|
|
39
|
+
return [
|
|
40
|
+
{ name: "Rect", sketch: r },
|
|
41
|
+
{ name: "Circle", sketch: c },
|
|
42
|
+
{ name: "Hexagon", sketch: hex },
|
|
43
|
+
{ name: "Rounded Rect", sketch: rounded },
|
|
44
|
+
{ name: "Slot", sketch: oblong },
|
|
45
|
+
{ name: "Plate (extruded)", shape: plate3d },
|
|
46
|
+
{ name: "Shell (offset + extrude)", shape: shell3d },
|
|
47
|
+
{ name: "Bracket (path + extrude)", shape: bracket },
|
|
48
|
+
];
|