forgecad 0.6.3 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -12
- package/dist/assets/{AdminPage-CeqCUUgu.js → AdminPage-D4bocK4E.js} +250 -151
- package/dist/assets/{BlogPage-P_AJP0v9.js → BlogPage-CJEXL_zJ.js} +94 -70
- package/dist/assets/{DocsPage-CKRV2iq2.js → DocsPage-D3A_g8V3.js} +329 -163
- package/dist/assets/{EditorApp-CnC2k4cW.css → EditorApp-BWYUSpUN.css} +590 -136
- package/dist/assets/EditorApp-Cihhqcsq.js +11692 -0
- package/dist/assets/{EmbedViewer-DBlzmQ5i.js → EmbedViewer-kWjKaC_t.js} +2 -4
- package/dist/assets/LandingPageProofDriven-Bg2IUc3l.css +856 -0
- package/dist/assets/LandingPageProofDriven-DXkKlyhI.js +601 -0
- package/dist/assets/PricingPage-BsU5vzEx.js +232 -0
- package/dist/assets/{SettingsPage-BqCh9JcC.js → SettingsPage-PqvpAKIs.js} +129 -5
- package/dist/assets/{evalWorker-Ql-aKwLA.js → evalWorker-C-hzNUMy.js} +8949 -3161
- package/dist/assets/{Viewport-CoB46f5R.js → index-Pz321YAt.js} +38382 -7501
- package/dist/assets/{index-2hfs_ub0.css → index-ay13WNfa.css} +726 -53
- package/dist/assets/{javascript-DCxGoE5Y.js → javascript-DAl8Gmyo.js} +1 -1
- package/dist/assets/{manifold-CqNMHHKO.js → manifold-BcbjWLIo.js} +4 -3
- package/dist/assets/{manifold-Cce9wRFz.js → manifold-DBckbFgx.js} +1 -1
- package/dist/assets/{manifold-D6BeHIOo.js → manifold-O2AAGXyj.js} +1 -1
- package/dist/assets/{reportWorker-sFEFonXf.js → reportWorker-Dxr-5A7w.js} +8760 -3559
- package/dist/assets/{vendor-react-Dt7-aaJH.js → vendor-react-CG3i_wp0.js} +65 -8
- package/dist/docs/index.html +2 -2
- package/dist/docs-raw/CLI.md +341 -718
- package/dist/docs-raw/generated/assembly.md +699 -112
- package/dist/docs-raw/generated/concepts.md +1834 -1346
- package/dist/docs-raw/generated/core.md +1012 -1059
- package/dist/docs-raw/generated/curves.md +759 -116
- package/dist/docs-raw/generated/lib.md +43 -748
- package/dist/docs-raw/generated/output.md +139 -245
- package/dist/docs-raw/generated/sdf.md +208 -0
- package/dist/docs-raw/generated/sheet-metal.md +473 -21
- package/dist/docs-raw/generated/sketch.md +1518 -362
- package/dist/docs-raw/generated/viewport.md +368 -299
- package/dist/docs-raw/generated/wood.md +104 -0
- package/dist/index.html +2 -2
- package/dist/landing/proof-ams-adapter.png +0 -0
- package/dist/landing/proof-bolt-and-nut.png +0 -0
- package/dist/landing/proof-fillet-enclosure.png +0 -0
- package/dist/landing/proof-glasses.png +0 -0
- package/dist/landing/proof-gyroid.png +0 -0
- package/dist/sitemap.xml +6 -6
- package/dist-cli/forgecad.js +12321 -5700
- package/dist-cli/forgecad.js.map +1 -0
- package/dist-cli/solver-46FFSK2U.js +363 -0
- package/dist-cli/solver-46FFSK2U.js.map +1 -0
- package/dist-skill/CONTEXT.md +4890 -6302
- package/dist-skill/SKILL-dev.md +22 -66
- package/dist-skill/SKILL.md +20 -59
- package/dist-skill/docs/API/core/concepts.md +37 -92
- package/dist-skill/docs/CLI.md +341 -718
- package/dist-skill/docs/generated/assembly.md +699 -112
- package/dist-skill/docs/generated/core.md +1012 -1059
- package/dist-skill/docs/generated/curves.md +759 -116
- package/dist-skill/docs/generated/lib.md +43 -748
- package/dist-skill/docs/generated/output.md +139 -245
- package/dist-skill/docs/generated/sdf.md +208 -0
- package/dist-skill/docs/generated/sheet-metal.md +473 -21
- package/dist-skill/docs/generated/sketch.md +1518 -362
- package/dist-skill/docs/generated/viewport.md +368 -299
- package/dist-skill/docs/generated/wood.md +104 -0
- package/dist-skill/docs/guides/coordinate-system.md +11 -17
- package/dist-skill/docs/guides/geometry-conventions.md +13 -70
- package/dist-skill/docs/guides/joint-design.md +78 -0
- package/dist-skill/docs/guides/modeling-recipes.md +22 -195
- package/dist-skill/docs/guides/positioning.md +88 -147
- package/dist-skill/docs-dev/API/core/concepts.md +78 -0
- package/dist-skill/docs-dev/CLI.md +488 -0
- package/dist-skill/{docs → docs-dev}/blueprint-first.md +5 -0
- package/dist-skill/{docs → docs-dev}/coding-best-practices.md +6 -8
- package/dist-skill/{docs → docs-dev}/coding.md +2 -4
- package/dist-skill/docs-dev/component-model.md +164 -0
- package/dist-skill/docs-dev/generated/assembly.md +779 -0
- package/dist-skill/docs-dev/generated/core.md +1676 -0
- package/dist-skill/docs-dev/generated/curves.md +855 -0
- package/dist-skill/docs-dev/generated/lib.md +55 -0
- package/dist-skill/docs-dev/generated/output.md +234 -0
- package/dist-skill/docs-dev/generated/sdf.md +208 -0
- package/dist-skill/docs-dev/generated/sheet-metal.md +506 -0
- package/dist-skill/docs-dev/generated/sketch.md +1753 -0
- package/dist-skill/docs-dev/generated/viewport.md +513 -0
- package/dist-skill/docs-dev/generated/wood.md +104 -0
- package/dist-skill/docs-dev/guides/coordinate-system.md +46 -0
- package/dist-skill/docs-dev/guides/geometry-conventions.md +52 -0
- package/dist-skill/docs-dev/guides/joint-design.md +78 -0
- package/dist-skill/docs-dev/guides/modeling-recipes.md +77 -0
- package/dist-skill/docs-dev/guides/positioning.md +151 -0
- package/dist-skill/{docs → docs-dev}/guides/skill-maintenance.md +21 -10
- package/dist-skill/{docs → docs-dev}/internals/compiler.md +5 -6
- package/dist-skill/{docs → docs-dev}/internals/constraint-solver-quality.md +0 -1
- package/dist-skill/{docs → docs-dev}/internals/constraint-solver.md +0 -1
- package/dist-skill/{docs → docs-dev}/internals/sketch-2d-pipeline.md +2 -3
- package/examples/api/attachTo-basics.forge.js +8 -8
- package/examples/api/bill-of-materials.forge.js +9 -9
- package/examples/api/bolt-pattern.forge.js +5 -5
- package/examples/api/boolean-operations.forge.js +5 -5
- package/examples/api/bounding-box-visualizer.forge.js +3 -3
- package/examples/api/clone-duplicate.forge.js +2 -2
- package/examples/api/colors-union-vs-array.forge.js +6 -6
- package/examples/api/connector-assembly.forge.js +8 -6
- package/examples/api/connector-basics.forge.js +7 -7
- package/examples/api/constrained-sketch-mechanical.forge.js +4 -4
- package/examples/api/elbow-test.forge.js +3 -3
- package/examples/api/extrude-options.forge.js +8 -14
- package/examples/api/feature-created-faces.forge.js +6 -10
- package/examples/api/fillet-showcase.forge.js +2 -2
- package/examples/api/folded-service-panel-cover.forge.js +2 -2
- package/examples/api/gears-tier1.forge.js +5 -5
- package/examples/api/group-test.forge.js +3 -3
- package/examples/api/group-vs-union.forge.js +1 -1
- package/examples/api/highlight-debug.forge.js +4 -0
- package/examples/api/js-module-pillars.js +1 -1
- package/examples/api/js-module-scene.js +2 -2
- package/examples/api/mesh-import-slats.forge.js +4 -4
- package/examples/api/patterns.forge.js +3 -3
- package/examples/api/pointAlong-orientation.forge.js +3 -3
- package/examples/api/profile-2020-b-slot6.forge.js +4 -5
- package/examples/api/route-perimeter-flange.forge.js +1 -1
- package/examples/api/sdf-rover-demo.forge.js +10 -10
- package/examples/api/sketch-on-face-demo.forge.js +2 -2
- package/examples/api/sketch-regions.forge.js +4 -4
- package/examples/api/sketch-rounding-strategies.forge.js +1 -1
- package/examples/api/smooth-curve-connections.forge.js +1 -1
- package/examples/api/transition-curves.forge.js +4 -4
- package/examples/api/variable-sweep-pure-sdf-test.forge.js +162 -0
- package/examples/api/variable-sweep-test.forge.js +2 -2
- package/examples/api/wood-joinery.forge.js +60 -0
- package/examples/compiler-corpus/enclosure-shell-cuts.forge.js +3 -3
- package/examples/compiler-corpus/fastener-plate-variants.forge.js +2 -2
- package/examples/constraints/01-fully-constrained-rect.forge.js +2 -2
- package/examples/constraints/02-underconstrained-triangle.forge.js +1 -1
- package/examples/constraints/03-redundant-constraints.forge.js +2 -2
- package/examples/constraints/05-parallel-with-linedistance.forge.js +2 -2
- package/examples/constraints/06-complex-spectrogram.forge.js +1 -1
- package/examples/constraints/07-perpendicular-chain.forge.js +4 -4
- package/examples/constraints/08-symmetric-bracket.forge.js +4 -4
- package/examples/constraints/09-stress-spiral.forge.js +1 -1
- package/examples/constraints/10-stress-honeycomb.forge.js +1 -1
- package/examples/constraints/11-surface-grid.forge.js +2 -2
- package/examples/constraints/12-surface-nested.forge.js +4 -4
- package/examples/constraints/13-surface-complex.forge.js +1 -1
- package/examples/exact-arc-housing.forge.js +12 -0
- package/examples/experiments/drone-arm.forge.js +53 -0
- package/examples/furniture/adjustable-table.forge.js +15 -15
- package/examples/furniture/bathroom.forge.js +26 -26
- package/examples/furniture/chair.forge.js +13 -13
- package/examples/furniture/picture-frame.forge.js +6 -6
- package/examples/furniture/shoe-rack-doors.forge.js +8 -8
- package/examples/furniture/shoe-rack.forge.js +7 -7
- package/examples/furniture/table-lamp.forge.js +8 -8
- package/examples/gcode/lissajous-vase.forge.js +4 -4
- package/examples/gcode/math-surface.forge.js +3 -3
- package/examples/gcode/parametric-vase.forge.js +4 -4
- package/examples/gcode/spiral-tower.forge.js +4 -4
- package/examples/generative/crystal-growth.forge.js +9 -9
- package/examples/generative/frost-spires.forge.js +9 -9
- package/examples/generative/golden-spiral-tower.forge.js +11 -11
- package/examples/generative/molten-forge.forge.js +6 -6
- package/examples/generative/neon-coral.forge.js +7 -7
- package/examples/mechanical/3d-printer.forge.js +37 -37
- package/examples/mechanical/5-finger-robot-hand.forge.js +19 -19
- package/examples/mechanical/airplane-propeller.forge.js +9 -9
- package/examples/mechanical/bolt-and-nut.forge.js +10 -10
- package/examples/mechanical/door-with-hinges.forge.js +7 -7
- package/examples/mechanical/fillet-enclosure.forge.js +15 -11
- package/examples/mechanical/headphone-hanger-v2.forge.js +11 -11
- package/examples/mechanical/robot_hand.forge.js +24 -24
- package/examples/mechanical/robot_hand_2.forge.js +26 -26
- package/examples/nurbs-surface.forge.js +8 -0
- package/examples/nurbs-tube.forge.js +7 -0
- package/examples/products/bottle.forge.js +8 -8
- package/examples/products/chess-set.forge.js +25 -25
- package/examples/products/classical-piano.forge.js +20 -20
- package/examples/products/clock.forge.js +33 -33
- package/examples/products/cup.forge.js +5 -5
- package/examples/products/iphone.forge.js +20 -20
- package/examples/products/laptop.forge.js +24 -24
- package/examples/products/laser-cut-box.forge.js +6 -6
- package/examples/products/laser-cut-tray.forge.js +6 -6
- package/examples/products/liquid-soap-dispenser.forge.js +23 -23
- package/examples/products/origami-fish.forge.js +14 -12
- package/examples/products/spiderman-cake.forge.js +6 -6
- package/examples/shelf/container.forge.js +5 -5
- package/examples/shelf/shelf-unit.forge.js +6 -6
- package/examples/toolbox/bolted-joint.forge.js +7 -7
- package/package.json +9 -4
- package/dist/assets/EditorApp-B-vQvgam.js +0 -9888
- package/dist/assets/LandingPage-C5n9hDXI.js +0 -322
- package/dist/assets/PublishedModelPage-Dt7PCVBj.js +0 -146
- package/dist/assets/__vite-browser-external-CURh0WXD.js +0 -8
- package/dist/assets/deserializeRunResult-BLAFoiE0.js +0 -19365
- package/dist/assets/index-1CYp3zUp.js +0 -1455
- package/dist-skill/docs/API/API.md +0 -1666
- package/dist-skill/docs/API/README.md +0 -37
- package/dist-skill/docs/API/assembly/assembly.md +0 -617
- package/dist-skill/docs/API/core/edge-queries.md +0 -130
- package/dist-skill/docs/API/core/parameters.md +0 -122
- package/dist-skill/docs/API/core/reserved-terms.md +0 -137
- package/dist-skill/docs/API/core/sdf.md +0 -326
- package/dist-skill/docs/API/core/skill-cli.md +0 -194
- package/dist-skill/docs/API/core/skill-guide.md +0 -205
- package/dist-skill/docs/API/core/specs.md +0 -186
- package/dist-skill/docs/API/core/topology.md +0 -372
- package/dist-skill/docs/API/entities.md +0 -268
- package/dist-skill/docs/API/output/bom.md +0 -58
- package/dist-skill/docs/API/output/brep-export.md +0 -87
- package/dist-skill/docs/API/output/dimensions.md +0 -67
- package/dist-skill/docs/API/output/export.md +0 -110
- package/dist-skill/docs/API/output/gcode.md +0 -195
- package/dist-skill/docs/API/runtime/viewport.md +0 -420
- package/dist-skill/docs/API/sheet-metal/sheet-metal.md +0 -185
- package/dist-skill/docs/API/sketch/anchor.md +0 -37
- package/dist-skill/docs/API/sketch/booleans.md +0 -91
- package/dist-skill/docs/API/sketch/core.md +0 -73
- package/dist-skill/docs/API/sketch/extrude.md +0 -62
- package/dist-skill/docs/API/sketch/on-face.md +0 -104
- package/dist-skill/docs/API/sketch/operations.md +0 -78
- package/dist-skill/docs/API/sketch/path.md +0 -75
- package/dist-skill/docs/API/sketch/primitives.md +0 -146
- package/dist-skill/docs/API/sketch/regions.md +0 -80
- package/dist-skill/docs/API/sketch/text.md +0 -108
- package/dist-skill/docs/API/sketch/transforms.md +0 -65
- package/dist-skill/docs/API/toolbox/fasteners.md +0 -129
- package/dist-skill/docs/INDEX.md +0 -94
- package/dist-skill/docs/RELEASING.md +0 -55
- package/dist-skill/docs/cli-monetization.md +0 -111
- package/dist-skill/docs/deployment.md +0 -281
- package/dist-skill/docs/generated/concepts.md +0 -2112
- package/dist-skill/docs/internals/shape-from-slices.md +0 -152
- package/dist-skill/docs/platform/admin.md +0 -45
- package/dist-skill/docs/platform/architecture.md +0 -79
- package/dist-skill/docs/platform/auth.md +0 -110
- package/dist-skill/docs/platform/email.md +0 -67
- package/dist-skill/docs/platform/projects.md +0 -111
- package/dist-skill/docs/platform/sharing.md +0 -90
- package/dist-skill/docs/runbook.md +0 -345
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# ForgeCAD API Documentation
|
|
2
|
-
|
|
3
|
-
This directory contains **hand-written API guides** for model authors. For auto-generated API docs extracted from source, see the [`generated/`](../generated/) directory (one file per module).
|
|
4
|
-
|
|
5
|
-
## Directory Layout
|
|
6
|
-
|
|
7
|
-
| Directory | Audience | What's in it |
|
|
8
|
-
|-----------|----------|-------------|
|
|
9
|
-
| `core/` | Model authors | Core reference: primitives, transforms, booleans, imports, parameters, topology, specs |
|
|
10
|
-
| `sketch/` | Model authors (sketch-heavy tasks) | 2D sketch construction, transforms, booleans, on-face, extrude |
|
|
11
|
-
| `assembly/` | Model authors (mechanisms) | Assembly graph, joints, couplings, kinematics |
|
|
12
|
-
| `sheet-metal/` | Model authors (folded parts) | Sheet metal, flanges, flat patterns |
|
|
13
|
-
| `runtime/` | Model authors (viewport) | Cut planes, exploded views, joint controls |
|
|
14
|
-
| `output/` | Model authors (export/reports) | BOM, dimensions, G-code toolpaths, mesh export, STEP/BREP export |
|
|
15
|
-
| `toolbox/` | Model authors (library) | Fasteners, hardware helpers |
|
|
16
|
-
|
|
17
|
-
## Read Plan
|
|
18
|
-
|
|
19
|
-
> Load only what the current task needs. Start from the top, add docs as needed.
|
|
20
|
-
|
|
21
|
-
1. **Always start with** `core/reference.md` — the core script contract
|
|
22
|
-
2. **For geometry orientation**: `../guides/coordinate-system.md`, `../guides/geometry-conventions.md`, `../guides/positioning.md`
|
|
23
|
-
3. **For sketch-heavy work**: `sketch/*.md` (9 files covering 2D APIs)
|
|
24
|
-
4. **For topology/constraints**: `core/topology.md`
|
|
25
|
-
5. **For assemblies**: `assembly/assembly.md`
|
|
26
|
-
6. **For sheet metal**: `sheet-metal/sheet-metal.md`
|
|
27
|
-
7. **For viewport controls**: `runtime/viewport.md`
|
|
28
|
-
8. **For recipes/debugging**: `../guides/modeling-recipes.md`
|
|
29
|
-
9. **For CLI/export**: `../cli.md`, `output/*.md`
|
|
30
|
-
|
|
31
|
-
## Adjacent Documentation
|
|
32
|
-
|
|
33
|
-
- **[`../generated/api-reference.md`](../generated/api-reference.md)** — Auto-generated complete API index (run `npm run gen:docs` to refresh)
|
|
34
|
-
- **[`../guides/`](../guides/)** — Conceptual orientation: coordinate system, conventions, recipes
|
|
35
|
-
- **[`../internals/`](../internals/)** — Engine internals for ForgeCAD contributors
|
|
36
|
-
- **[`../project/`](../project/)** — Development workflow, coding standards, deployment
|
|
37
|
-
- **[`../cli.md`](../cli.md)** — CLI reference
|
|
@@ -1,617 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
skill-group: assembly
|
|
3
|
-
skill-order: 1
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Assembly + Mechanism API
|
|
7
|
-
|
|
8
|
-
Use this API when your model is a mechanism, not a single booleaned solid.
|
|
9
|
-
|
|
10
|
-
## Mental model
|
|
11
|
-
- `Part` = manufacturable object (shape + metadata)
|
|
12
|
-
- `Joint` = relationship between parent and child part
|
|
13
|
-
- `State` = current joint values
|
|
14
|
-
- `Solve` = compute world transforms for all parts
|
|
15
|
-
- `Validate` = collisions / clearances / sweep checks
|
|
16
|
-
|
|
17
|
-
## Quick start
|
|
18
|
-
|
|
19
|
-
```javascript
|
|
20
|
-
const mech = assembly("Arm")
|
|
21
|
-
.addPart("base", box(80, 80, 20, true), {
|
|
22
|
-
metadata: { material: "PETG", process: "FDM", qty: 1 },
|
|
23
|
-
})
|
|
24
|
-
.addPart("link", box(140, 24, 24).translate(0, -12, -12))
|
|
25
|
-
.addJoint("shoulder", "revolute", "base", "link", {
|
|
26
|
-
axis: [0, 1, 0],
|
|
27
|
-
min: -30,
|
|
28
|
-
max: 120,
|
|
29
|
-
default: 25,
|
|
30
|
-
frame: Transform.identity().translate(0, 0, 20),
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
return mech; // auto-solved at defaults, renders all parts
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
Returning `mech` (unsolved Assembly) auto-solves at default joint values and renders all parts. You can also return a `SolvedAssembly` for a specific pose:
|
|
37
|
-
|
|
38
|
-
```javascript
|
|
39
|
-
return mech.solve({ shoulder: 60 });
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
## Return types and imports — how they fit together
|
|
43
|
-
|
|
44
|
-
| Return value | Standalone | `require()` result type |
|
|
45
|
-
|---|---|---|
|
|
46
|
-
| `Shape` | yes | `Shape` |
|
|
47
|
-
| `Sketch` | yes | `Sketch` |
|
|
48
|
-
| `ShapeGroup` | yes | `ShapeGroup` |
|
|
49
|
-
| `Assembly` (unsolved) | **yes** | `ImportedAssembly` |
|
|
50
|
-
| `SolvedAssembly` | **yes** | `SolvedAssembly` |
|
|
51
|
-
|
|
52
|
-
Use `require("./file.forge.js")` to import any `.forge.js` file. Pass param overrides as the second argument: `require("./file.forge.js", { Speed: 2 })`.
|
|
53
|
-
|
|
54
|
-
**`Assembly` is the dual-use type**: a file that returns an unsolved `Assembly` works both as a standalone renderable script *and* as an import target for `require()` (which returns an `ImportedAssembly`).
|
|
55
|
-
|
|
56
|
-
Pattern for dual-use assembly files:
|
|
57
|
-
|
|
58
|
-
```javascript
|
|
59
|
-
// handle.forge.js — works standalone AND importable via require()
|
|
60
|
-
const mech = assembly("Handle")
|
|
61
|
-
.addPart("Base", baseBracket)
|
|
62
|
-
.addPart("Arm", arm)
|
|
63
|
-
.addRevolute("Fold", "Base", "Arm", { axis: [0, 1, 0], min: 0, max: 90 });
|
|
64
|
-
|
|
65
|
-
// Animation setup — runs when standalone, ignored on import
|
|
66
|
-
mech.toJointsView({
|
|
67
|
-
animations: [{ name: "Fold", duration: 2, loop: true,
|
|
68
|
-
keyframes: [{ values: { Fold: 0 } }, { values: { Fold: 90 } }, { values: { Fold: 0 } }],
|
|
69
|
-
}],
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
return mech; // works standalone (auto-solved + animated) AND with require()
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
```javascript
|
|
76
|
-
// case.forge.js — imports the handle as a positioned assembly
|
|
77
|
-
const handle = require("./handle.forge.js");
|
|
78
|
-
|
|
79
|
-
// Convenience transforms: solve at defaults, return ShapeGroup
|
|
80
|
-
const handleGroup = handle.rotate(0, 0, -90).translate(0, -20, 50);
|
|
81
|
-
|
|
82
|
-
return [
|
|
83
|
-
{ name: "Case", shape: caseBody },
|
|
84
|
-
{ name: "Handle", shape: handleGroup },
|
|
85
|
-
];
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
## Connector-based assembly
|
|
89
|
-
|
|
90
|
-
**Connectors** are typed, gendered ports. They carry semantic metadata (type like `"dovetail"`, gender `male`/`female`/`neutral`, and optional measurements) that enables automatic positioning, type/gender validation, and connector-aware explode views.
|
|
91
|
-
|
|
92
|
-
### Defining connectors
|
|
93
|
-
|
|
94
|
-
```javascript
|
|
95
|
-
const shelf = box(200, 120, 10, true).withConnectors({
|
|
96
|
-
left_tab: connector.male("dovetail", { origin: [-100, 0, 0], axis: [-1, 0, 0] }),
|
|
97
|
-
right_tab: connector.male("dovetail", { origin: [100, 0, 0], axis: [1, 0, 0] }),
|
|
98
|
-
});
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
Connector factories: `connector.male(type, portInput)`, `connector.female(type, portInput)`, `connector.neutral(type, portInput)`.
|
|
102
|
-
|
|
103
|
-
Connectors are stored as ports with extra metadata — they work everywhere ports work (`connect()`, `assembly.match()`, transforms, imports).
|
|
104
|
-
|
|
105
|
-
### `matchTo()` — Static connector matching (no assembly needed)
|
|
106
|
-
|
|
107
|
-
Position a part by snapping its connector to a target's connector. Full 6-DOF alignment: origins coincide, axes anti-parallel.
|
|
108
|
-
|
|
109
|
-
```javascript
|
|
110
|
-
// Single pair
|
|
111
|
-
const placed = shelf.matchTo(panel, "left_tab", "shelf_slot_0");
|
|
112
|
-
|
|
113
|
-
// Dictionary (multiple pairs, same target)
|
|
114
|
-
const placed = shelf.matchTo(panel, { left_tab: "shelf_slot_0", right_tab: "shelf_slot_1" });
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
Works on `Shape` and `ShapeGroup`. Matched parts auto-get explode hints — the viewport explode slider separates them along connector axes.
|
|
118
|
-
|
|
119
|
-
### `assembly.match()` — Auto-create joints from connectors
|
|
120
|
-
|
|
121
|
-
```javascript
|
|
122
|
-
const mech = assembly("Door")
|
|
123
|
-
.addPart("Frame", frame)
|
|
124
|
-
.addPart("Door", door)
|
|
125
|
-
.match("Door", "Frame", { hinge_top: "hinge_top" });
|
|
126
|
-
// Revolute connectors → auto-creates revolute joint. No manual addRevolute needed.
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### Connector queries
|
|
130
|
-
|
|
131
|
-
```javascript
|
|
132
|
-
shape.connectorNames() // all connector names
|
|
133
|
-
shape.connectorsByType("dovetail") // filter by type
|
|
134
|
-
shape.connectorDistance("slot_1", "slot_2") // 3D distance between origins
|
|
135
|
-
shape.connectorMeasurements("mount") // { bolt_circle: 31, ... }
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### Auto-bubbling through groups
|
|
139
|
-
|
|
140
|
-
Named group children automatically expose their connectors via dotted paths:
|
|
141
|
-
|
|
142
|
-
```javascript
|
|
143
|
-
const cabinet = group(
|
|
144
|
-
{ name: "Left", shape: leftPanel },
|
|
145
|
-
{ name: "Right", shape: rightPanel },
|
|
146
|
-
);
|
|
147
|
-
cabinet.connectorNames() // → ["Left.shelf_0", "Left.shelf_1", "Right.shelf_0", ...]
|
|
148
|
-
shelf.matchTo(cabinet, "left_tab", "Left.shelf_0");
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
## Port-based assembly
|
|
152
|
-
|
|
153
|
-
Declare **ports** on parts and let `connect()` align them automatically — no manual `frame` or `axis` math.
|
|
154
|
-
|
|
155
|
-
### When to use `match()` vs `connect()` vs `addRevolute()`
|
|
156
|
-
|
|
157
|
-
| Pattern | Use | Example |
|
|
158
|
-
|---------|-----|---------|
|
|
159
|
-
| **Typed connectors with validation** | `match()` | Connector type + gender checking, auto joint creation |
|
|
160
|
-
| **Ports physically meet** | `connect()` | Flange-to-flange, bolt-to-hole, motor shaft into gear bore |
|
|
161
|
-
| **Shared axis, offset position** | `addRevolute()` | Hinge with arm between two ear brackets, scissor linkage |
|
|
162
|
-
|
|
163
|
-
`connect()` aligns port origins — child port lands exactly on parent port. Use it when the attachment points should coincide. For mechanisms where parts share a rotation axis but are deliberately spaced apart along it (e.g. a folding hinge arm that sits between bracket ears), use `addRevolute()` with pre-positioned parts and let the transform cancellation handle placement.
|
|
164
|
-
|
|
165
|
-
### Declaring ports
|
|
166
|
-
|
|
167
|
-
A port is an oriented attachment frame: origin + axis + up vector, in part-local coordinates.
|
|
168
|
-
|
|
169
|
-
```javascript
|
|
170
|
-
const base = box(80, 80, 20, true).withPorts({
|
|
171
|
-
top: port.revolute({
|
|
172
|
-
origin: [0, 0, 10],
|
|
173
|
-
axis: [0, 0, 1], // joint rotation axis
|
|
174
|
-
up: [0, 1, 0], // zero-angle reference (optional, auto-computed if omitted)
|
|
175
|
-
}),
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
const arm = box(120, 20, 20).translate(60, 0, 0).withPorts({
|
|
179
|
-
shoulder: port.revolute({ origin: [0, 0, 0], axis: [0, 0, 1] }),
|
|
180
|
-
elbow: port.revolute({ origin: [120, 0, 0], axis: [0, 0, 1] }),
|
|
181
|
-
});
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
Port factories: `port.revolute(...)`, `port.prismatic(...)`, `port.fixed(...)`, or generic `port(...)`.
|
|
185
|
-
|
|
186
|
-
**Alternative: define by physical extent** — use `start`/`end` instead of `origin`/`axis`:
|
|
187
|
-
|
|
188
|
-
```javascript
|
|
189
|
-
// Origin and axis auto-computed from start→end
|
|
190
|
-
const arm = arm3D.withPorts({
|
|
191
|
-
bore: port.revolute({
|
|
192
|
-
start: [0, -armWidth/2, 0], // bore entrance
|
|
193
|
-
end: [0, armWidth/2, 0], // bore exit
|
|
194
|
-
}),
|
|
195
|
-
});
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
This also stores the extent length for alignment (see below).
|
|
199
|
-
|
|
200
|
-
Ports survive transforms — if you `translate()` or `rotate()` a shape, its ports move with it.
|
|
201
|
-
|
|
202
|
-
### Connecting parts
|
|
203
|
-
|
|
204
|
-
```javascript
|
|
205
|
-
const mech = assembly("Arm")
|
|
206
|
-
.addPart("Base", base)
|
|
207
|
-
.addPart("Link", arm)
|
|
208
|
-
.connect("Base.top", "Link.shoulder", {
|
|
209
|
-
as: "J1", // joint name (auto-generated if omitted)
|
|
210
|
-
min: -90, max: 90, // optional limits
|
|
211
|
-
default: 0, // optional default angle
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
const solved = mech.solve({ J1: 45 });
|
|
215
|
-
return solved.toScene();
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
`connect()` computes the joint `frame` and `axis` from port alignment, then delegates to `addJoint()` internally. The kinematic solver is unchanged.
|
|
219
|
-
|
|
220
|
-
**ConnectOptions:** `as`, `type` (override port kind), `min`, `max`, `default`, `unit`, `flip` (oppose axes for mirrored parts), `align` / `parentAlign` / `childAlign` (start/middle/end), `effort`, `velocity`, `damping`, `friction`.
|
|
221
|
-
|
|
222
|
-
**Alignment along port extent:** When ports are defined with `start`/`end`, you can control which points align:
|
|
223
|
-
|
|
224
|
-
```javascript
|
|
225
|
-
.connect("Pin.shaft", "Arm.bore", { align: "middle" }) // default — midpoints meet
|
|
226
|
-
.connect("Pin.shaft", "Arm.bore", { align: "start" }) // start points meet
|
|
227
|
-
.connect("Pin.shaft", "Arm.bore", { align: "end" }) // end points meet
|
|
228
|
-
.connect("Pin.shaft", "Arm.bore", {
|
|
229
|
-
parentAlign: "start", childAlign: "middle", // independent control
|
|
230
|
-
})
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
**Mirrored parts:** When a part is created with `.mirror()`, its port axis flips direction. Use `flip: true` on the connect call so the axes oppose rather than align:
|
|
234
|
-
|
|
235
|
-
```javascript
|
|
236
|
-
.connect("Right Base.hinge", "Right Arm.pivot", {
|
|
237
|
-
as: "Fold Right", flip: true,
|
|
238
|
-
})
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
### Derived jointsView
|
|
242
|
-
|
|
243
|
-
Instead of manually restating the kinematic chain for `jointsView()`, derive it from the assembly graph:
|
|
244
|
-
|
|
245
|
-
```javascript
|
|
246
|
-
mech.toJointsView({
|
|
247
|
-
defaults: { J1: 30 },
|
|
248
|
-
animations: [
|
|
249
|
-
{
|
|
250
|
-
name: "Swing",
|
|
251
|
-
duration: 2,
|
|
252
|
-
loop: true,
|
|
253
|
-
keyframes: [
|
|
254
|
-
{ values: { J1: -45 } },
|
|
255
|
-
{ values: { J1: 45 } },
|
|
256
|
-
{ values: { J1: -45 } },
|
|
257
|
-
],
|
|
258
|
-
},
|
|
259
|
-
],
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
// IMPORTANT: solve at REST when using toJointsView — the viewport handles posing.
|
|
263
|
-
return mech.solve().toScene();
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
`toJointsView()` solves the assembly at rest, computes world-space pivots and axes, and calls the existing `jointsView()` function. No manual pivot coordinates needed.
|
|
267
|
-
|
|
268
|
-
**Fixed attachments are handled automatically.** Parts connected with `addFixed()` are emitted as zero-range revolute joints in the viewport chain, so they follow their parent during animation (e.g. a stick bolted to an arm will rotate with it).
|
|
269
|
-
|
|
270
|
-
**Do not solve at a non-zero angle when using `toJointsView()`.** The viewport applies `toJointsView` transforms on top of the scene — solving at the same angle would double-rotate parts. Use `defaults` to set the initial pose instead:
|
|
271
|
-
|
|
272
|
-
```javascript
|
|
273
|
-
// BAD — double rotation
|
|
274
|
-
mech.toJointsView({ defaults: { J1: 45 } });
|
|
275
|
-
return mech.solve({ J1: 45 }).toScene();
|
|
276
|
-
|
|
277
|
-
// GOOD — solve at rest, viewport poses via defaults
|
|
278
|
-
mech.toJointsView({ defaults: { J1: 45 } });
|
|
279
|
-
return mech.solve().toScene();
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
### Port API on shapes
|
|
283
|
-
|
|
284
|
-
- `shape.withPorts({ name: port.revolute({...}) })` — attach ports (returns new shape)
|
|
285
|
-
- `shape.portNames()` — list port names
|
|
286
|
-
- Works on `Shape`, `TrackedShape`, and `ShapeGroup`
|
|
287
|
-
|
|
288
|
-
### Port API on assemblies
|
|
289
|
-
|
|
290
|
-
- `assembly.withPorts("PartName", { name: port.revolute({...}) })` — add ports to an existing part
|
|
291
|
-
- `assembly.getPorts("PartName")` — get ports on a part
|
|
292
|
-
- `assembly.getPort("Part.port")` — resolve a "Part.port" reference
|
|
293
|
-
- Ports from imported parts are captured automatically in `addPart()`
|
|
294
|
-
- Ports are forwarded through `mergeInto()` with the prefix
|
|
295
|
-
|
|
296
|
-
## Ergonomic helpers
|
|
297
|
-
- `addFrame(name, { transform? })` adds a virtual reference frame (no geometry)
|
|
298
|
-
- `addRevolute(name, parent, child, opts)` shorthand for `addJoint(..., "revolute", ...)`
|
|
299
|
-
- `addPrismatic(name, parent, child, opts)` shorthand for `addJoint(..., "prismatic", ...)`
|
|
300
|
-
- `addFixed(name, parent, child, opts)` shorthand for `addJoint(..., "fixed", ...)`
|
|
301
|
-
- `addJointCoupling(jointName, { terms, offset? })` links joints with linear relationships
|
|
302
|
-
- `addGearCoupling(drivenJoint, driverJoint, opts)` links revolute joints using gear ratios
|
|
303
|
-
|
|
304
|
-
## Joint couplings
|
|
305
|
-
|
|
306
|
-
Use couplings when one joint should be derived from other joints.
|
|
307
|
-
|
|
308
|
-
Formula:
|
|
309
|
-
- `driven = offset + Σ(ratio_i * source_i)`
|
|
310
|
-
|
|
311
|
-
Example:
|
|
312
|
-
|
|
313
|
-
```javascript
|
|
314
|
-
const mech = assembly("Differential")
|
|
315
|
-
.addFrame("Base")
|
|
316
|
-
.addFrame("Turret")
|
|
317
|
-
.addFrame("Wheel")
|
|
318
|
-
.addFrame("TopInput")
|
|
319
|
-
.addRevolute("Steering", "Base", "Turret", { axis: [0, 0, 1] })
|
|
320
|
-
.addRevolute("WheelDrive", "Turret", "Wheel", { axis: [1, 0, 0] })
|
|
321
|
-
.addRevolute("TopGear", "Base", "TopInput", { axis: [0, 0, 1] })
|
|
322
|
-
.addJointCoupling("TopGear", {
|
|
323
|
-
terms: [
|
|
324
|
-
{ joint: "Steering", ratio: 1 },
|
|
325
|
-
{ joint: "WheelDrive", ratio: 20 / 14 },
|
|
326
|
-
],
|
|
327
|
-
});
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
Notes:
|
|
331
|
-
- Coupled joints ignore direct values in `solve(state)` and emit a warning.
|
|
332
|
-
- Coupling cycles are rejected.
|
|
333
|
-
- `sweepJoint(...)` cannot sweep a coupled target; sweep one of its source joints instead.
|
|
334
|
-
|
|
335
|
-
## Gear couplings
|
|
336
|
-
|
|
337
|
-
Use this helper to connect two **revolute** joints as a gear mesh without manually writing `addJointCoupling(...)`.
|
|
338
|
-
|
|
339
|
-
```javascript
|
|
340
|
-
const pair = lib.gearPair({
|
|
341
|
-
pinion: { module: 1.25, teeth: 14, faceWidth: 8 },
|
|
342
|
-
gear: { module: 1.25, teeth: 42, faceWidth: 8 },
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
const mech = assembly("Spur Stage")
|
|
346
|
-
.addFrame("Base")
|
|
347
|
-
.addFrame("PinionPart")
|
|
348
|
-
.addFrame("GearPart")
|
|
349
|
-
.addRevolute("Pinion", "Base", "PinionPart", { axis: [0, 0, 1] })
|
|
350
|
-
.addRevolute("Driven", "Base", "GearPart", { axis: [0, 0, 1] })
|
|
351
|
-
.addGearCoupling("Driven", "Pinion", { pair }); // uses pair.jointRatio
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
`addGearCoupling(...)` ratio sources (choose exactly one):
|
|
355
|
-
- `ratio` (explicit multiplier)
|
|
356
|
-
- `pair` (`lib.gearPair(...)`, `lib.bevelGearPair(...)`, or `lib.faceGearPair(...)` result using `pair.jointRatio`)
|
|
357
|
-
- `driverTeeth` + `drivenTeeth` (auto ratio; `internal` mesh is positive, `external`/`bevel`/`face` are negative)
|
|
358
|
-
|
|
359
|
-
For bevel stages, pairing helpers also return placement aids:
|
|
360
|
-
- `pinionAxis`, `gearAxis`
|
|
361
|
-
- `pinionCenter`, `gearCenter`
|
|
362
|
-
|
|
363
|
-
For face stages, use `centerDistance` and `meshPlaneZ` from `lib.faceGearPair(...)`; with `place: true`, the face gear stays on the Z axis and the vertical spur is placed at `[centerDistance, 0, meshPlaneZ]`.
|
|
364
|
-
|
|
365
|
-
## Joint frames
|
|
366
|
-
|
|
367
|
-
`frame` is a transform from the **parent part frame** to the **joint frame at zero state**.
|
|
368
|
-
|
|
369
|
-
For a child part:
|
|
370
|
-
|
|
371
|
-
Matrix form:
|
|
372
|
-
- `childWorld = parentWorld * frame * motion(value) * childBase`
|
|
373
|
-
|
|
374
|
-
Forge chain form:
|
|
375
|
-
- `childWorld = composeChain(childBase, motion(value), frame, parentWorld)`
|
|
376
|
-
|
|
377
|
-
This keeps kinematic chains declarative and avoids repeated manual pivot math.
|
|
378
|
-
|
|
379
|
-
## SolvedAssembly
|
|
380
|
-
|
|
381
|
-
`mech.solve(state?)` returns a `SolvedAssembly` with these methods:
|
|
382
|
-
|
|
383
|
-
| Method | Returns | Use for |
|
|
384
|
-
|--------|---------|---------|
|
|
385
|
-
| `toGroup()` | `ShapeGroup` | Primary way to get positioned parts as a group — for `show()`, embedding, transforms |
|
|
386
|
-
| `getPart(name)` | `AssemblyPart` | Extract a single part at its solved position |
|
|
387
|
-
| `getTransform(name)` | `Transform` | Raw world transform for a part |
|
|
388
|
-
| `bom()` / `bomCsv()` | `BomRow[]` / `string` | Bill of materials |
|
|
389
|
-
| `collisionReport()` | `CollisionFinding[]` | Interference detection |
|
|
390
|
-
| `minClearance(a, b)` | `number` | Minimum gap between two parts |
|
|
391
|
-
| `toSceneObjects()` | `Array<{name, shape?, group?}>` | Advanced: raw scene-graph array for custom rendering |
|
|
392
|
-
|
|
393
|
-
**`toGroup()`** is the preferred way to convert a solved assembly to a positionable group:
|
|
394
|
-
|
|
395
|
-
```javascript
|
|
396
|
-
const solved = mech.solve({ shoulder: 45 });
|
|
397
|
-
show(solved.toGroup()); // in notebooks
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
## Validation helpers
|
|
401
|
-
- `solved.collisionReport()` returns overlapping part pairs and volume
|
|
402
|
-
- `solved.minClearance("PartA", "PartB", 10)` computes minimum gap
|
|
403
|
-
- `assembly.sweepJoint("elbow", -20, 140, 24)` samples motion and reports collisions
|
|
404
|
-
|
|
405
|
-
Notebook-friendly pattern:
|
|
406
|
-
|
|
407
|
-
```javascript
|
|
408
|
-
const solved = mech.solve({ shoulder: 35, elbow: 60 });
|
|
409
|
-
console.log("Collisions", solved.collisionReport());
|
|
410
|
-
|
|
411
|
-
const sweep = mech.sweepJoint("elbow", -10, 135, 12, { shoulder: 35 });
|
|
412
|
-
console.log("Sweep collisions", sweep.filter((step) => step.collisions.length > 0).length);
|
|
413
|
-
|
|
414
|
-
show(solved);
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
That keeps mechanism setup in earlier cells and collision/sweep investigation in the current preview cell.
|
|
418
|
-
|
|
419
|
-
## ImportedAssembly
|
|
420
|
-
|
|
421
|
-
`require("./assembly.forge.js")` returns an `ImportedAssembly` when the file returns an `Assembly`. It has these capabilities:
|
|
422
|
-
|
|
423
|
-
### Kinematic access
|
|
424
|
-
|
|
425
|
-
```javascript
|
|
426
|
-
const arm = require("./arm.forge.js");
|
|
427
|
-
|
|
428
|
-
// Full kinematic access
|
|
429
|
-
const solved = arm.solve({ shoulder: 45 });
|
|
430
|
-
console.log(solved.bom());
|
|
431
|
-
arm.assembly.sweepJoint("shoulder", -30, 120, 24);
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
### Extracting parts
|
|
435
|
-
|
|
436
|
-
```javascript
|
|
437
|
-
const base = arm.part("Base"); // at default state
|
|
438
|
-
const link = arm.part("Link", { shoulder: 60 }); // at specific state
|
|
439
|
-
```
|
|
440
|
-
|
|
441
|
-
### Converting to group
|
|
442
|
-
|
|
443
|
-
```javascript
|
|
444
|
-
const g = arm.toGroup({ shoulder: 45 }); // ShapeGroup with named children
|
|
445
|
-
const baseChild = g.child("Base");
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
### Convenience transforms
|
|
449
|
-
|
|
450
|
-
`ImportedAssembly` has `.rotate()`, `.translate()`, `.scale()`, `.mirror()`, `.color()`, and `.child()` that auto-solve at defaults and return a `ShapeGroup`:
|
|
451
|
-
|
|
452
|
-
```javascript
|
|
453
|
-
const handle = require("./handle.forge.js");
|
|
454
|
-
const positioned = handle.rotate(0, 0, -90).translate(0, -20, 50);
|
|
455
|
-
// positioned is a ShapeGroup — use directly in named arrays or group()
|
|
456
|
-
```
|
|
457
|
-
|
|
458
|
-
### Placement references
|
|
459
|
-
|
|
460
|
-
```javascript
|
|
461
|
-
const arm = require("./arm.forge.js");
|
|
462
|
-
const placed = arm.placeReference("mountHole", [100, 0, 50]);
|
|
463
|
-
```
|
|
464
|
-
|
|
465
|
-
## Merging sub-assemblies into a parent
|
|
466
|
-
|
|
467
|
-
`importedAssembly.mergeInto(parent, options)` flattens a sub-assembly's parts and joints into a parent `Assembly`, then wires a mount joint connecting a parent part to the sub-assembly root. After the merge, the parent graph can drive sub-assembly joints directly.
|
|
468
|
-
|
|
469
|
-
```javascript
|
|
470
|
-
// scene.forge.js
|
|
471
|
-
const chassis = box(200, 80, 20, true);
|
|
472
|
-
|
|
473
|
-
const robot = assembly("Robot")
|
|
474
|
-
.addPart("Chassis", chassis);
|
|
475
|
-
|
|
476
|
-
// Merge left arm — all parts/joints prefixed "Left Arm."
|
|
477
|
-
require("./arm.forge.js")
|
|
478
|
-
.mergeInto(robot, {
|
|
479
|
-
prefix: "Left Arm",
|
|
480
|
-
mountParent: "Chassis",
|
|
481
|
-
mountJoint: "leftMount",
|
|
482
|
-
mountOptions: { frame: Transform.identity().translate(-70, 0, 10) },
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
// Merge right arm — same source file, different prefix and position
|
|
486
|
-
require("./arm.forge.js")
|
|
487
|
-
.mergeInto(robot, {
|
|
488
|
-
prefix: "Right Arm",
|
|
489
|
-
mountParent: "Chassis",
|
|
490
|
-
mountJoint: "rightMount",
|
|
491
|
-
mountOptions: { frame: Transform.identity().translate(70, 0, 10) },
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
// Drive sub-assembly joints from the parent using prefixed names
|
|
495
|
-
return robot.solve({
|
|
496
|
-
"Left Arm.shoulder": 45,
|
|
497
|
-
"Right Arm.shoulder": -20,
|
|
498
|
-
});
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
**`mergeInto(parent, options)` options:**
|
|
502
|
-
|
|
503
|
-
| Option | Type | Required | Description |
|
|
504
|
-
|---|---|---|---|
|
|
505
|
-
| `prefix` | `string` | recommended | Prefix for all part and joint names. `"Left Arm"` turns `"Base"` into `"Left Arm.Base"`. |
|
|
506
|
-
| `mountParent` | `string` | yes | Part name in `parent` to attach the sub-assembly root to. |
|
|
507
|
-
| `mountJoint` | `string` | yes | Name for the new mount joint in the parent graph. |
|
|
508
|
-
| `mountType` | `JointType` | no | Joint type for the mount (default: `'fixed'`). |
|
|
509
|
-
| `mountOptions` | `JointOptions` | no | Frame, axis, limits, etc. for the mount joint. |
|
|
510
|
-
|
|
511
|
-
**Notes:**
|
|
512
|
-
- The sub-assembly must have exactly one root part. If it has multiple roots, connect them with `addFixed()` first.
|
|
513
|
-
- Joint couplings inside the sub-assembly are preserved and rewritten with the prefix.
|
|
514
|
-
- After merging, use `parent.sweepJoint("Left Arm.shoulder", ...)` for collision sweeps across the full hierarchy.
|
|
515
|
-
- Returns `parent` for chaining.
|
|
516
|
-
|
|
517
|
-
## Common pitfalls
|
|
518
|
-
- **Animating assemblies with `jointsView`**: If you use [`jointsView()`](../runtime/viewport.md) to animate an assembly, solve the assembly at rest pose (all animated joints = 0) and let `jointsView` control posing via `default` values and animation keyframes. Solving at non-zero angles and then animating will double-rotate parts. See the [viewport docs](../runtime/viewport.md#using-jointsview-with-assemblies) for the full pattern.
|
|
519
|
-
- If parts vanish in the viewport, check whether a cut plane is active before debugging kinematics. The viewer-side APIs live in [../runtime/viewport.md](../runtime/viewport.md).
|
|
520
|
-
- If a returned object is empty, Forge logs a warning in script output.
|
|
521
|
-
|
|
522
|
-
## Metadata
|
|
523
|
-
- `addPart(..., { metadata })` attaches per-part metadata to an assembly part.
|
|
524
|
-
- BOM/report helpers such as `solved.bom()` and `solved.bomCsv()` live in [../output/bom.md](../output/bom.md).
|
|
525
|
-
|
|
526
|
-
## Naming grouped assembly children
|
|
527
|
-
|
|
528
|
-
When an assembly part is a `ShapeGroup`, Forge flattens the group into separate viewport objects. To avoid opaque labels like `Base Assembly.1`, name the group children explicitly:
|
|
529
|
-
|
|
530
|
-
```javascript
|
|
531
|
-
const housing = group(
|
|
532
|
-
{ name: "Body", shape: body },
|
|
533
|
-
{ name: "Lid", shape: lid },
|
|
534
|
-
);
|
|
535
|
-
|
|
536
|
-
const mech = assembly("Case")
|
|
537
|
-
.addPart("Base Assembly", housing);
|
|
538
|
-
```
|
|
539
|
-
|
|
540
|
-
That produces labels such as `Base Assembly.Body` and `Base Assembly.Lid`.
|
|
541
|
-
|
|
542
|
-
## Robot export
|
|
543
|
-
|
|
544
|
-
Use `robotExport({...})` when an assembly should become a simulator package instead of only a viewport scene.
|
|
545
|
-
|
|
546
|
-
```javascript
|
|
547
|
-
const rover = assembly("Scout")
|
|
548
|
-
.addPart("Chassis", box(300, 220, 50, true))
|
|
549
|
-
.addPart("Left Wheel", cylinder(30, 60, undefined, 48, true).pointAlong([0, 1, 0]))
|
|
550
|
-
.addPart("Right Wheel", cylinder(30, 60, undefined, 48, true).pointAlong([0, 1, 0]))
|
|
551
|
-
.addRevolute("leftWheel", "Chassis", "Left Wheel", {
|
|
552
|
-
axis: [0, 1, 0],
|
|
553
|
-
frame: Transform.identity().translate(90, 140, 60),
|
|
554
|
-
effort: 20,
|
|
555
|
-
velocity: 1080,
|
|
556
|
-
})
|
|
557
|
-
.addRevolute("rightWheel", "Chassis", "Right Wheel", {
|
|
558
|
-
axis: [0, 1, 0],
|
|
559
|
-
frame: Transform.identity().translate(90, -140, 60),
|
|
560
|
-
effort: 20,
|
|
561
|
-
velocity: 1080,
|
|
562
|
-
});
|
|
563
|
-
|
|
564
|
-
robotExport({
|
|
565
|
-
assembly: rover,
|
|
566
|
-
modelName: "Scout",
|
|
567
|
-
links: {
|
|
568
|
-
Chassis: { massKg: 10 },
|
|
569
|
-
"Left Wheel": { massKg: 0.8 },
|
|
570
|
-
"Right Wheel": { massKg: 0.8 },
|
|
571
|
-
},
|
|
572
|
-
plugins: {
|
|
573
|
-
diffDrive: {
|
|
574
|
-
leftJoints: ["leftWheel"],
|
|
575
|
-
rightJoints: ["rightWheel"],
|
|
576
|
-
wheelSeparationMm: 280,
|
|
577
|
-
wheelRadiusMm: 60,
|
|
578
|
-
},
|
|
579
|
-
},
|
|
580
|
-
world: {
|
|
581
|
-
generateDemoWorld: true,
|
|
582
|
-
},
|
|
583
|
-
});
|
|
584
|
-
```
|
|
585
|
-
|
|
586
|
-
### Export formats
|
|
587
|
-
|
|
588
|
-
```bash
|
|
589
|
-
# SDF package (Gazebo/Ignition) — generates model.sdf + world + STL meshes
|
|
590
|
-
forgecad export sdf model.forge.js
|
|
591
|
-
|
|
592
|
-
# URDF package (ROS/PyBullet/MuJoCo) — generates .urdf + STL meshes
|
|
593
|
-
forgecad export urdf model.forge.js
|
|
594
|
-
```
|
|
595
|
-
|
|
596
|
-
Both exporters produce:
|
|
597
|
-
- **Mesh-based inertia tensors** — computed from actual triangle geometry via divergence theorem (not bounding-box approximation). Includes full 6-component tensor (Ixx, Iyy, Izz, Ixy, Ixz, Iyz) and center of mass.
|
|
598
|
-
- **Separate collision meshes** — controlled per-link via `collision` option.
|
|
599
|
-
- **Joint mimic elements** — joint couplings (`addJointCoupling`, `addGearCoupling`) are exported as `<mimic>` elements in both SDF and URDF.
|
|
600
|
-
|
|
601
|
-
### Collision mesh modes
|
|
602
|
-
|
|
603
|
-
Set per-link in `robotExport({ links: { "PartName": { collision: mode } } })`:
|
|
604
|
-
|
|
605
|
-
| Mode | Description | Default |
|
|
606
|
-
|------|-------------|---------|
|
|
607
|
-
| `'convex'` | Convex hull of visual geometry (separate `_collision.stl`). Typically 50-80% smaller. | **Yes** |
|
|
608
|
-
| `'box'` | Axis-aligned bounding box primitive. Fastest physics but least accurate. | |
|
|
609
|
-
| `'visual'` | Same mesh as visual. Exact but slow for simulation. | |
|
|
610
|
-
| `'none'` | No collision geometry. Link passes through other objects. | |
|
|
611
|
-
|
|
612
|
-
### Notes
|
|
613
|
-
|
|
614
|
-
- Revolute joint `velocity` values are expressed in degrees/second in Forge; the exporters convert them to radians/second.
|
|
615
|
-
- Prismatic distances are authored in millimeters and exported in meters.
|
|
616
|
-
- `massKg` is preferred for demo robots; `densityKgM3` is a decent fallback when mass is unknown.
|
|
617
|
-
- Joint couplings with multiple terms use the primary term (largest ratio) for `<mimic>` since SDF/URDF only support single-leader mimic. A warning is emitted for dropped terms.
|