forgecad 0.9.14 → 0.9.16
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 +6 -4
- package/README.md +8 -4
- package/dist/assets/{AdminPage-eWGs2K6H.js → AdminPage-CXvls4-J.js} +2 -2
- package/dist/assets/{BenchmarkPage-CTrLKfpo.js → BenchmarkPage-B27zk8xL.js} +4 -15
- package/dist/assets/{BlogPage-5nPesyds.js → BlogPage-CMAVvgQL.js} +2 -2
- package/dist/assets/{DocsPage-C4Y3nbYc.js → DocsPage-knf4I4h7.js} +9 -3
- package/dist/assets/EditorApp-BHMQlJ-D.js +14686 -0
- package/dist/assets/{EditorApp-BAnckbsk.css → EditorApp-BpjZgzk0.css} +846 -0
- package/dist/assets/{EmbedViewer-C8fB4n5U.js → EmbedViewer-D7ZGlFjx.js} +3 -3
- package/dist/assets/{LandingPageProofDriven-jSz0LaMM.js → LandingPageProofDriven-CnevhTE8.js} +36 -38
- package/dist/assets/LegalPage-BPTUmqeg.js +39 -0
- package/dist/assets/LegalPage-BRlScr9A.css +91 -0
- package/dist/assets/{PricingPage-B83B90zh.js → PricingPage-B0D4goG_.js} +19 -19
- package/dist/assets/{PricingPage-BMedqFef.css → PricingPage-BPF6HKyO.css} +25 -0
- package/dist/assets/{SettingsPage-DY889pcu.js → SettingsPage-CFF-UgjI.js} +2 -2
- package/dist/assets/app-CE3sYcV7.css +3890 -0
- package/dist/assets/{app-bEww1ic4.js → app-T0pDcSX4.js} +3382 -1069
- package/dist/assets/cli/{render-Cho2uKG_.js → render-C5pcIISc.js} +477 -29
- package/dist/assets/{constructionHistoryWorker-HYwzJY4m.js → constructionHistoryWorker-Ba2Hm58b.js} +928 -243
- package/dist/assets/{evalWorker-CjQwJSE-.js → evalWorker-vkx310U2.js} +8883 -6040
- package/dist/assets/{forgecad_geometry-CH2nvuLA.js → forgecad_geometry-Dgceylq9.js} +43 -1
- package/dist/assets/forgecad_geometry_bg-dD4RNQF1.wasm +0 -0
- package/dist/assets/{inspectWorker-DeRnMVv1.js → inspectWorker-BuTJDVX6.js} +1179 -273
- package/dist/assets/{javascript-70-4uGcz.js → javascript-1kQXfVaz.js} +1 -1
- package/dist/assets/{targets-D6PWsv6X.js → jointPose-B_Cgedn9.js} +71 -3
- package/dist/assets/landing-proof-driven-DiGqdtWa.js +18 -0
- package/dist/assets/{landing-proof-driven-oFYW6mjz.css → landing-proof-driven-ORyigZ6p.css} +13 -7
- package/dist/assets/legalContent-ZfFGMmi4.js +251 -0
- package/dist/assets/{manifold-rmfAcdwF.js → manifold-BWgsjmAM.js} +1 -1
- package/dist/assets/{manifold-uRzgk5O8.js → manifold-D6IFSkhH.js} +2 -2
- package/dist/assets/{manifold-CG9Fokx-.js → manifold-rZexZI0G.js} +1 -1
- package/dist/assets/{reportWorker-4cW_ZpoS.js → reportWorker-0AGij1Ru.js} +8659 -12771
- package/dist/assets/{scalar-sampling-budget-CfDiFvh7.js → scalar-sampling-budget-J5cuzxT1.js} +8050 -6203
- package/dist/assets/{scanProxyWorker-Bs2TDgLw.js → scanProxyWorker-Vl4Wxa1y.js} +50 -6
- package/dist/assets/{solver-DuJAO8S6.js → solver-BZ9LPTHs.js} +1 -1
- package/dist/assets/solver_bg-DAHZJ_rw.wasm +0 -0
- package/dist/assets/{vendor-react-Da3A2QmU.js → vendor-react-6j1Kke-Y.js} +6 -5
- package/dist/cli/render.html +1 -1
- package/dist/docs/index.html +2 -2
- package/dist/docs-raw/AI/ai-native-cad.md +50 -0
- package/dist/docs-raw/AI/usage.md +5 -12
- package/dist/docs-raw/CLI.md +34 -10
- package/dist/docs-raw/component-model.md +27 -11
- package/dist/docs-raw/generated/assembly.md +374 -187
- package/dist/docs-raw/generated/concepts.md +245 -237
- package/dist/docs-raw/generated/core.md +283 -6
- package/dist/docs-raw/generated/curves.md +274 -361
- package/dist/docs-raw/generated/lib.md +9 -19
- package/dist/docs-raw/generated/output.md +29 -4
- package/dist/docs-raw/generated/runtime-names.md +49 -0
- package/dist/docs-raw/generated/sdf.md +31 -0
- package/dist/docs-raw/generated/sheet-metal.md +9 -0
- package/dist/docs-raw/generated/sketch.md +44 -1
- package/dist/docs-raw/generated/viewport.md +11 -3
- package/dist/docs-raw/guides/coordinate-system.md +20 -16
- package/dist/docs-raw/guides/geometry-conventions.md +2 -2
- package/dist/docs-raw/guides/inspection-bundles.md +2 -1
- package/dist/docs-raw/guides/joint-design.md +24 -0
- package/dist/docs-raw/guides/positioning.md +13 -3
- package/dist/docs-raw/legal/privacy.md +63 -0
- package/dist/docs-raw/legal/software-license.md +55 -0
- package/dist/docs-raw/legal/terms.md +87 -0
- package/dist/docs-raw/skills/forgecad-3d-reconstruction.md +1 -1
- package/dist/docs-raw/skills/forgecad-blockout-model.md +1 -1
- package/dist/docs-raw/skills/forgecad-component-model.md +11 -2
- package/dist/docs-raw/skills/forgecad-high-level-spec.md +1 -1
- package/dist/docs-raw/skills/forgecad-image-replicator.md +8 -8
- package/dist/docs-raw/skills/forgecad-lld.md +1 -1
- package/dist/docs-raw/skills/forgecad-make-a-model.md +40 -39
- package/dist/docs-raw/skills/forgecad-model-grader.md +2 -2
- package/dist/docs-raw/skills/forgecad-prepare-prompt.md +2 -2
- package/dist/docs-raw/skills/forgecad-project.md +3 -1
- package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +1 -1
- package/dist/docs-raw/skills/forgecad-render-inspect.md +4 -2
- package/dist/docs-raw/skills/forgecad-visual-spec.md +1 -1
- package/dist/docs-raw/skills/forgecad.md +4 -3
- package/dist/docs-raw/welcome.md +2 -0
- package/dist/index.html +40 -12
- package/dist/llms.txt +8 -0
- package/dist/site.webmanifest +1 -1
- package/dist/sitemap.xml +49 -13
- package/dist-cli/{check-compiler-U5SOPN7X.js → check-compiler-SYQ2PWOB.js} +1 -2
- package/dist-cli/{check-query-propagation-XOKNSSYU.js → check-query-propagation-HIAGV62W.js} +1 -2
- package/dist-cli/{chunk-EXWGNL6K.js → chunk-SPZE3DUY.js} +20659 -17930
- package/dist-cli/forgecad.js +3568 -1250
- package/dist-cli/{forgecad_geometry-GYVNKPIE.js → forgecad_geometry-QOQIIP53.js} +42 -1
- package/dist-cli/forgecad_geometry_bg.wasm +0 -0
- package/dist-cli/{solver-46FFSK2U.js → solver-OK4HECRH.js} +0 -1
- package/dist-cli/solver_bg.wasm +0 -0
- package/dist-skill/CONTEXT.md +1192 -725
- package/dist-skill/SKILL.md +3 -2
- package/dist-skill/docs/API/core/concepts.md +64 -1
- package/dist-skill/docs/CLI.md +34 -10
- package/dist-skill/docs/generated/assembly.md +339 -213
- package/dist-skill/docs/generated/core.md +283 -6
- package/dist-skill/docs/generated/curves.md +272 -362
- package/dist-skill/docs/generated/lib.md +9 -19
- package/dist-skill/docs/generated/output.md +29 -4
- package/dist-skill/docs/generated/runtime-names.md +40 -0
- package/dist-skill/docs/generated/sdf.md +31 -0
- package/dist-skill/docs/generated/sheet-metal.md +9 -0
- package/dist-skill/docs/generated/sketch.md +44 -2
- package/dist-skill/docs/generated/viewport.md +2 -87
- package/dist-skill/docs/guides/coordinate-system.md +20 -16
- package/dist-skill/docs/guides/geometry-conventions.md +2 -2
- package/dist-skill/docs/guides/inspection-bundles.md +2 -1
- package/dist-skill/docs/guides/joint-design.md +24 -0
- package/dist-skill/docs/guides/positioning.md +13 -3
- package/dist-skill/library/forgecad-component-model/SKILL.md +10 -1
- package/dist-skill/library/forgecad-image-replicator/SKILL.md +6 -6
- package/dist-skill/library/forgecad-image-replicator/scripts/compare_images.py +166 -0
- package/dist-skill/library/forgecad-make-a-model/SKILL.md +39 -38
- package/dist-skill/library/forgecad-model-grader/SKILL.md +1 -1
- package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +1 -1
- package/dist-skill/library/forgecad-project/SKILL.md +2 -0
- package/dist-skill/library/forgecad-render-inspect/SKILL.md +3 -1
- package/examples/api/assembly-kinematics-foundation.forge.js +65 -0
- package/examples/api/assembly-kinematics-four-bar.forge.js +115 -0
- package/examples/api/assembly-kinematics-limb.forge.js +116 -0
- package/examples/api/connector-frame-rig-chain.forge.js +102 -0
- package/examples/api/exact-sheet-shell-assembly.forge.js +0 -2
- package/examples/api/exact-surface-studio.forge.js +6 -8
- package/examples/api/helix-basics.forge.js +8 -8
- package/examples/api/lean-foundations/README.md +12 -0
- package/examples/api/lean-foundations/curve-blend-exact.forge.js +22 -0
- package/examples/api/lean-foundations/curve-fit-interpolation.forge.js +18 -0
- package/examples/api/lean-foundations/curve-helix-canonicalization.forge.js +27 -0
- package/examples/api/lean-foundations/curve-route-canonicalization.forge.js +16 -0
- package/examples/api/lean-foundations/curve-trim-reverse.forge.js +24 -0
- package/examples/api/lean-foundations/exact-curve-arc.forge.js +36 -0
- package/examples/api/mixed-edge-finishes-proof.forge.js +8 -11
- package/examples/api/route3d-elbow.forge.js +71 -0
- package/examples/api/transition-curves.forge.js +44 -15
- package/examples/api/variable-sweep-test.forge.js +3 -1
- package/examples/api/y-blend-corner-showcase.forge.js +0 -2
- package/examples/generative/coral-vase.forge.js +1 -1
- package/examples/nurbs-tube.forge.js +1 -1
- package/package.json +17 -13
- package/dist/assets/EditorApp-lXv53A1m.js +0 -13610
- package/dist/assets/app-CsHnaBWt.css +0 -1789
- package/dist/assets/forgecad_geometry_bg-C5_E9Oa9.wasm +0 -0
- package/dist/assets/solver_bg-CWvv4lnN.wasm +0 -0
- package/dist/docs-raw/API/README.md +0 -16
- package/dist/docs-raw/API/core/concepts.md +0 -118
- package/dist/docs-raw/INDEX.md +0 -138
- package/dist/docs-raw/RELEASING.md +0 -87
- package/dist/docs-raw/agent-native-api.md +0 -27
- package/dist/docs-raw/beta-deployment.md +0 -304
- package/dist/docs-raw/beta-operations.md +0 -325
- package/dist/docs-raw/blueprint-first.md +0 -145
- package/dist/docs-raw/cli-monetization.md +0 -112
- package/dist/docs-raw/coding-best-practices.md +0 -120
- package/dist/docs-raw/coding.md +0 -340
- package/dist/docs-raw/deployment.md +0 -374
- package/dist/docs-raw/guides/skill-maintenance.md +0 -161
- package/dist/docs-raw/guides/surface-members.md +0 -82
- package/dist/docs-raw/harbor-cli.md +0 -854
- package/dist/docs-raw/internals/backend-vocabulary.md +0 -35
- package/dist/docs-raw/internals/compiler.md +0 -307
- package/dist/docs-raw/internals/constraint-solver-quality.md +0 -161
- package/dist/docs-raw/internals/constraint-solver.md +0 -176
- package/dist/docs-raw/internals/shape-from-slices.md +0 -152
- package/dist/docs-raw/internals/sketch-2d-pipeline.md +0 -108
- package/dist/docs-raw/platform/admin.md +0 -45
- package/dist/docs-raw/platform/architecture.md +0 -82
- package/dist/docs-raw/platform/auth.md +0 -139
- package/dist/docs-raw/platform/email.md +0 -67
- package/dist/docs-raw/platform/google-oauth-setup.md +0 -88
- package/dist/docs-raw/platform/observability.md +0 -197
- package/dist/docs-raw/platform/projects.md +0 -111
- package/dist/docs-raw/platform/sharing.md +0 -90
- package/dist/docs-raw/product/README.md +0 -39
- package/dist/docs-raw/product/api-as-product-language.md +0 -13
- package/dist/docs-raw/product/business-model.md +0 -15
- package/dist/docs-raw/product/competitive-positioning.md +0 -17
- package/dist/docs-raw/product/creative-manufacturing.md +0 -15
- package/dist/docs-raw/product/founder-story.md +0 -11
- package/dist/docs-raw/product/manufacturing-workflows.md +0 -15
- package/dist/docs-raw/product/onboarding-first-experience.md +0 -256
- package/dist/docs-raw/product/product-loop.md +0 -17
- package/dist/docs-raw/product/strategic-decisions.md +0 -22
- package/dist/docs-raw/product/user-outreach-email-templates.md +0 -161
- package/dist/docs-raw/product/user-segments.md +0 -15
- package/dist/docs-raw/product/vision.md +0 -26
- package/dist/docs-raw/rl-environments.md +0 -350
- package/dist/docs-raw/runbook.md +0 -611
- package/dist-cli/check-compiler-U5SOPN7X.js.map +0 -1
- package/dist-cli/check-query-propagation-XOKNSSYU.js.map +0 -1
- package/dist-cli/chunk-EXWGNL6K.js.map +0 -1
- package/dist-cli/forgecad.js.map +0 -1
- package/dist-cli/forgecad_geometry-GYVNKPIE.js.map +0 -1
- package/dist-cli/solver-46FFSK2U.js.map +0 -1
- package/dist-skill/SKILL-dev.md +0 -145
- package/dist-skill/docs-dev/API/core/concepts.md +0 -118
- package/dist-skill/docs-dev/CLI.md +0 -677
- package/dist-skill/docs-dev/agent-native-api.md +0 -27
- package/dist-skill/docs-dev/blueprint-first.md +0 -145
- package/dist-skill/docs-dev/coding-best-practices.md +0 -120
- package/dist-skill/docs-dev/coding.md +0 -340
- package/dist-skill/docs-dev/component-model.md +0 -164
- package/dist-skill/docs-dev/generated/assembly.md +0 -794
- package/dist-skill/docs-dev/generated/core.md +0 -2117
- package/dist-skill/docs-dev/generated/curves.md +0 -2583
- package/dist-skill/docs-dev/generated/lib.md +0 -169
- package/dist-skill/docs-dev/generated/output.md +0 -247
- package/dist-skill/docs-dev/generated/sdf.md +0 -446
- package/dist-skill/docs-dev/generated/sheet-metal.md +0 -504
- package/dist-skill/docs-dev/generated/sketch.md +0 -1811
- package/dist-skill/docs-dev/generated/viewport.md +0 -585
- package/dist-skill/docs-dev/generated/wood.md +0 -108
- package/dist-skill/docs-dev/guides/coordinate-system.md +0 -46
- package/dist-skill/docs-dev/guides/geometry-conventions.md +0 -52
- package/dist-skill/docs-dev/guides/inspection-bundles.md +0 -485
- package/dist-skill/docs-dev/guides/joint-design.md +0 -78
- package/dist-skill/docs-dev/guides/modeling-recipes.md +0 -78
- package/dist-skill/docs-dev/guides/positioning.md +0 -161
- package/dist-skill/docs-dev/guides/skill-maintenance.md +0 -161
- package/dist-skill/docs-dev/internals/backend-vocabulary.md +0 -35
- package/dist-skill/docs-dev/internals/compiler.md +0 -307
- package/dist-skill/docs-dev/internals/constraint-solver-quality.md +0 -161
- package/dist-skill/docs-dev/internals/constraint-solver.md +0 -176
- package/dist-skill/docs-dev/internals/sketch-2d-pipeline.md +0 -108
- package/dist-skill/library/forgecad-image-replicator/scripts/compare_images.mjs +0 -289
- package/examples/api/bolted-service-cover.forge.js +0 -17
- package/examples/api/cable-gland-anchor.forge.js +0 -14
- package/examples/api/captured-cartridge-guide.forge.js +0 -14
- package/examples/api/captured-linear-slide.forge.js +0 -13
- package/examples/api/clevis-pin-joint.forge.js +0 -13
- package/examples/api/datum-enclosure.forge.js +0 -16
- package/examples/api/hose-barb-port.forge.js +0 -14
- package/examples/api/knuckled-hinge-assembly.forge.js +0 -15
- package/examples/api/living-hinge-cover.forge.js +0 -14
- package/examples/api/pcb-terminal-block.forge.js +0 -22
- package/examples/api/pinned-lever-pivot-stack.forge.js +0 -14
- package/examples/api/retained-shaft-knob-stack.forge.js +0 -15
- package/examples/api/routed-tube-clip.forge.js +0 -15
- package/examples/api/seated-bearing-stack.forge.js +0 -30
- package/examples/api/snap-latch-cover.forge.js +0 -14
- package/examples/api/thumb-screw-clamp.forge.js +0 -15
|
@@ -5,12 +5,12 @@ skill-order: 100
|
|
|
5
5
|
|
|
6
6
|
# Assembly API
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Assembly-owned links, constraints, connectors, solved poses, and robot export.
|
|
9
9
|
|
|
10
10
|
## Contents
|
|
11
11
|
|
|
12
|
-
- [Assembly & Joints](#assembly-joints) — `bomToCsv`, `assembly
|
|
13
|
-
- [Assembly](#assembly) — Structure, Connectors, References,
|
|
12
|
+
- [Assembly & Joints](#assembly-joints) — `bomToCsv`, `assembly`
|
|
13
|
+
- [Assembly](#assembly) — Kinematics, Structure, Connectors, References, Solving
|
|
14
14
|
- [ImportedAssembly](#importedassembly)
|
|
15
15
|
- [SolvedAssembly](#solvedassembly)
|
|
16
16
|
- [MateBuilder](#matebuilder)
|
|
@@ -36,96 +36,121 @@ bomToCsv(rows: BomRow[]): string
|
|
|
36
36
|
| `tags?` | `string \| readonly string[]` | Viewport organization tags applied to scene objects produced from this part. |
|
|
37
37
|
| `material?`, `process?`, `tolerance?`, `qty?`, `notes?`, `densityKgM3?`, `massKg?` | | — |
|
|
38
38
|
|
|
39
|
-
#### `assembly()` — Create an assembly container with named parts and
|
|
39
|
+
#### `assembly()` — Create an assembly container with named parts, connectors, and kinematic links.
|
|
40
40
|
|
|
41
|
-
**Use this from iteration 1 for any model with moving parts.**
|
|
41
|
+
**Use this from iteration 1 for any model with moving parts.** Do not build one static pose and retrofit motion later.
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
Links are named kinematic markers in the assembly. `edgeBetweenLinks()` records structural distances or visual relationships. `addAngleBetweenLinks()` records measured, limited, or controlled angles. These APIs solve point positions, not rigid-body frames.
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
`addPart(..., { mate })` attaches a part connector origin to a solved link point by translation only. It is right for markers and point-following geometry. Use `connect()` / `match()` for physical articulated parts that need full connector frame alignment and deterministic rest orientation.
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
Return an `Assembly` directly to expose its connector joints and driven link controls in the editor. Moving those controls re-runs the assembly solve with the new state, so closed-loop link/edge mechanisms move through the real kinematic solver instead of a viewport-only forward-kinematics approximation.
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
If no link in a connected kinematic component is fixed, ForgeCAD chooses a deterministic gauge link for solving and reports a floating-component warning.
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
const mech = assembly("Arm")
|
|
53
|
-
.addPart("base", box(80, 80, 20).translate(0, 0, -10), {
|
|
54
|
-
metadata: { material: "PETG", process: "FDM", qty: 1 },
|
|
55
|
-
})
|
|
56
|
-
.addPart("link", box(140, 24, 24).translate(0, -12, -12))
|
|
57
|
-
.addRevolute("shoulder", "base", "link", {
|
|
58
|
-
axis: [0, 1, 0],
|
|
59
|
-
min: -30, max: 120, default: 25,
|
|
60
|
-
frame: Transform.identity().translate(0, 0, 20),
|
|
61
|
-
});
|
|
51
|
+
The legacy joint-chain APIs still exist for compatibility and exporter plumbing. New work should choose between point-link kinematics and connector-frame joints based on whether the part needs orientation.
|
|
62
52
|
|
|
63
|
-
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
```ts
|
|
67
|
-
assembly(name?: string): Assembly
|
|
68
|
-
```
|
|
53
|
+
For multi-file assemblies, a file that returns an `Assembly` is importable via [`require()`](/docs/core#require) and yields an `ImportedAssembly`. Use `mergeInto()` to flatten a sub-assembly into a parent assembly.
|
|
69
54
|
|
|
70
|
-
|
|
55
|
+
**Point-link example**
|
|
71
56
|
|
|
72
|
-
This
|
|
57
|
+
This snippet mates a marker to the solved `tip` point. It does not orient a bar along `ground -> tip`.
|
|
73
58
|
|
|
74
59
|
```ts
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
min: -30, max: 120, default: 25,
|
|
60
|
+
const marker = box(8, 8, 4).withConnectors({
|
|
61
|
+
center: connector({ origin: [0, 0, 0], axis: [0, 0, 1] }),
|
|
78
62
|
});
|
|
79
|
-
|
|
63
|
+
|
|
64
|
+
const mech = assembly("Linkage")
|
|
65
|
+
.link("ground", { at: [0, 0, 0], fixed: true })
|
|
66
|
+
.link("worldX", { at: [10, 0, 0], fixed: true })
|
|
67
|
+
.link("tip", { at: [40, 0, 0] })
|
|
68
|
+
.edgeBetweenLinks("ground", "tip", { name: "bar" })
|
|
69
|
+
.addAngleBetweenLinks("worldX", "ground", "tip", {
|
|
70
|
+
name: "theta",
|
|
71
|
+
control: { min: 0, max: 120, default: 30 },
|
|
72
|
+
})
|
|
73
|
+
.addPart("Tip marker", marker, { mate: { connector: "center", toLink: "tip" } });
|
|
74
|
+
|
|
75
|
+
return mech;
|
|
80
76
|
```
|
|
81
77
|
|
|
82
78
|
```ts
|
|
83
|
-
|
|
79
|
+
assembly(name?: string): Assembly
|
|
84
80
|
```
|
|
85
81
|
|
|
86
|
-
`RevoluteJointOpts`: `{ axis?: [ number, number, number ], min?: number, max?: number, default?: number, unit?: string, reverse?: boolean }`
|
|
87
|
-
|
|
88
82
|
---
|
|
89
83
|
|
|
90
84
|
## Classes
|
|
91
85
|
|
|
92
86
|
### `Assembly`
|
|
93
87
|
|
|
94
|
-
Container for a kinematic mechanism made up of
|
|
95
|
-
|
|
96
|
-
An assembly is a directed graph where **parts** are nodes and **joints** are directed edges from parent to child. The graph must be a forest (one or more trees with no cycles). Root parts (no incoming joint) are fixed to world space.
|
|
88
|
+
Container for a kinematic mechanism made up of links, relationships, and parts.
|
|
97
89
|
|
|
98
|
-
|
|
90
|
+
Assembly has two related but different motion tools:
|
|
99
91
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
```
|
|
92
|
+
- **Link graph kinematics** (`link()`, `edgeBetweenLinks()`, `addAngleBetweenLinks()`) solve named point positions. A link is a point, not a rigid-body frame or bone. Connector-to-link mates then place geometry on the solved graph: one mate translates a connector origin onto a link, two mates orient a part so it spans between two solved links, and a third mate pins roll about that span.
|
|
93
|
+
- **Connector-frame joints** (`connect()` / `match()`) align full connector frames and derive joint frame + axis from `origin`, `axis`, and `up`. Use this for serial articulated geometry such as hips, knees, hinges, drums, sliders, and wheels where the physical part orientation matters.
|
|
103
94
|
|
|
104
|
-
|
|
95
|
+
Use link graphs when the hard part is solving positions, especially closed loops. Use connector-frame joints when the hard part is a serial tree of explicit rigid-body joint frames such as hinges, sliders, drums, and wheels.
|
|
105
96
|
|
|
106
|
-
- **
|
|
107
|
-
- **prismatic** — translates the child along an axis by `value` mm
|
|
108
|
-
- **fixed** — no motion; rigidly attaches the child at `frame`
|
|
97
|
+
**Point-link quick start**
|
|
109
98
|
|
|
110
|
-
|
|
99
|
+
This attaches a marker to a solved point. It is intentionally not a bone or oriented part example.
|
|
111
100
|
|
|
112
101
|
```ts
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
102
|
+
const marker = box(8, 8, 4).withConnectors({
|
|
103
|
+
center: connector({ origin: [0, 0, 0], axis: [0, 0, 1] }),
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const mech = assembly("Linkage")
|
|
107
|
+
.link("ground", { at: [0, 0, 0], fixed: true })
|
|
108
|
+
.link("worldX", { at: [10, 0, 0], fixed: true })
|
|
109
|
+
.link("tip", { at: [40, 0, 0] })
|
|
110
|
+
.edgeBetweenLinks("ground", "tip", { name: "bar" })
|
|
111
|
+
.addAngleBetweenLinks("worldX", "ground", "tip", {
|
|
112
|
+
name: "theta",
|
|
113
|
+
control: { min: 0, max: 120, default: 30 },
|
|
114
|
+
})
|
|
115
|
+
.addPart("Tip marker", marker, {
|
|
116
|
+
mate: { connector: "center", toLink: "tip" },
|
|
120
117
|
});
|
|
121
118
|
|
|
122
|
-
return mech;
|
|
119
|
+
return mech;
|
|
123
120
|
```
|
|
124
121
|
|
|
125
|
-
Returning an unsolved `Assembly`
|
|
122
|
+
Returning an unsolved `Assembly` keeps the graph available to the runtime. Return a `SolvedAssembly` directly for a specific control state:
|
|
126
123
|
|
|
127
124
|
```ts
|
|
128
|
-
return mech.solve({
|
|
125
|
+
return mech.solve({ theta: 60 });
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Frame-aware serial joint**
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
const hip = cylinder(12, 10).withConnectors({
|
|
132
|
+
socket: connector("hinge", {
|
|
133
|
+
origin: [0, 0, 6],
|
|
134
|
+
axis: [0, 0, 1],
|
|
135
|
+
up: [1, 0, 0],
|
|
136
|
+
kind: "revolute",
|
|
137
|
+
}),
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const upperLeg = box(60, 10, 8).translate(30, 0, -4).withConnectors({
|
|
141
|
+
hip: connector("hinge", {
|
|
142
|
+
origin: [0, 0, 0],
|
|
143
|
+
axis: [0, 0, -1],
|
|
144
|
+
up: [1, 0, 0],
|
|
145
|
+
kind: "revolute",
|
|
146
|
+
}),
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
return assembly("Leg")
|
|
150
|
+
.addPart("Hip", hip)
|
|
151
|
+
.addPart("Upper Leg", upperLeg)
|
|
152
|
+
.connect("Hip.socket", "Upper Leg.hip", { as: "hip", min: -35, max: 55 })
|
|
153
|
+
.solve({ hip: 20 });
|
|
129
154
|
```
|
|
130
155
|
|
|
131
156
|
**Return types**
|
|
@@ -141,6 +166,130 @@ return mech.solve({ shoulder: 60 });
|
|
|
141
166
|
|----------|------|-------------|
|
|
142
167
|
| `name` | `string` | — |
|
|
143
168
|
|
|
169
|
+
**Kinematics**
|
|
170
|
+
|
|
171
|
+
#### `link()` — Add a named kinematic link to the assembly graph.
|
|
172
|
+
|
|
173
|
+
Links are assembly-native solved points. They can exist before any geometry is attached, can be displayed by the viewport, and are solved by link/edge/angle constraints.
|
|
174
|
+
|
|
175
|
+
A link is not a rigid-body frame. It has a world position but no orientation basis. Use `connect()` when a physical part must inherit a connector frame and rotate about a real hinge/slider axis.
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
link(name: string, options?: AssemblyLinkOptions): Assembly
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**`AssemblyLinkOptions`**
|
|
182
|
+
- `at?: [ number, number, number ]` — Initial world-space position of this link before kinematic constraints solve it.
|
|
183
|
+
- `fixed?: boolean` — Keep the link locked at its authored `at` position during solves.
|
|
184
|
+
- `metadata?: Record<string, unknown>` — User metadata carried through the kinematic graph for inspection and tooling.
|
|
185
|
+
|
|
186
|
+
#### `edgeBetweenLinks()` — Add a relationship edge between two kinematic links.
|
|
187
|
+
|
|
188
|
+
By default the edge captures the authored distance between links as a structural length. Pass `{ length: 'free' }` or `{ visualOnly: true }` for a non-structural overlay edge.
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
edgeBetweenLinks(a: string, b: string, options?: AssemblyEdgeBetweenLinksOptions): Assembly
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**`AssemblyEdgeBetweenLinksOptions`**: `name?: string`, `length?: number | "lockCurrent" | "free"`, `min?: number`, `max?: number`, `visualOnly?: boolean`, `control?: AssemblyKinematicControlOptions`, `metadata?: Record<string, unknown>`
|
|
195
|
+
|
|
196
|
+
`AssemblyKinematicControlOptions`: `{ min?: number, max?: number, default?: number, unit?: string }`
|
|
197
|
+
|
|
198
|
+
#### `addAngleBetweenLinks()` — Add an angle relationship among three kinematic links.
|
|
199
|
+
|
|
200
|
+
The middle link is the vertex. When `control` is set, `solve(state)` reads the control value from `state[name]` and solves dependent links from that driven angle.
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
addAngleBetweenLinks(a: string, b: string, c: string, options?: AssemblyAngleBetweenLinksOptions): Assembly
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**`AssemblyAngleBetweenLinksOptions`**: `name?: string`, `value?: number`, `min?: number`, `max?: number`, `control?: boolean | AssemblyKinematicControlOptions`, `limit?: AssemblyKinematicLimitOptions`, `metadata?: Record<string, unknown>`
|
|
207
|
+
|
|
208
|
+
`AssemblyKinematicLimitOptions`: `{ min?: number, max?: number }`
|
|
209
|
+
|
|
210
|
+
#### `addAngleBetweenLinkSegmentAndWorldDirection()` — Add an absolute angle relationship from a world direction to a link segment.
|
|
211
|
+
|
|
212
|
+
The first link is the vertex/pivot and the second link is the moving point. A value of `0` places `fromLink -> toLink` along `direction` in the mechanism plane; positive angles rotate counter-clockwise in that plane.
|
|
213
|
+
|
|
214
|
+
Use `Points.polar(1, angleDeg)` when the reference direction is planar and angle-based instead of axis-aligned.
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
addAngleBetweenLinkSegmentAndWorldDirection(fromLink: string, toLink: string, direction: Vec3, options?: AssemblyAngleBetweenLinksOptions): Assembly
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
#### `describeKinematics()` — Return the assembly-native kinematic graph definition.
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
describeKinematics(): AssemblyKinematicGraphDef
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Structure**
|
|
227
|
+
|
|
228
|
+
#### `addPart()` — Add a named part to the assembly.
|
|
229
|
+
|
|
230
|
+
Connectors declared on the part (via `withConnectors()`) are captured automatically. Parts are positioned at world origin by default unless a `transform` is provided in `options`. For root parts (no incoming joint), `transform` is their final world position.
|
|
231
|
+
|
|
232
|
+
`options.mate` is for point-link attachments. During `solve()`, ForgeCAD translates the part so the named connector origin lands on the solved link position. The part keeps its existing orientation; connector `axis` and `up` are not used for link mating. Use this for markers, sensors, labels, and other geometry that should ride on a solved point. Use `connect()` for oriented physical parts such as limbs, levers, hinges, and wheels.
|
|
233
|
+
|
|
234
|
+
When a part is a [`ShapeGroup`](/docs/core#shapegroup), name the group children explicitly to get readable viewport labels (e.g. `"Base Assembly.Body"` instead of `"Base Assembly.1"`):
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
const housing = group(
|
|
238
|
+
{ name: "Body", shape: body },
|
|
239
|
+
{ name: "Lid", shape: lid },
|
|
240
|
+
);
|
|
241
|
+
assembly.addPart("Base Assembly", housing);
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
```ts
|
|
245
|
+
addPart(name: string, part: AssemblyPart, options?: PartOptions): Assembly
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**`PartOptions`**: `transform?: TransformInput`, `metadata?: PartMetadata`, `mate?: AssemblyPartMateInput | AssemblyPartMateInput[]`, `bindToFrame?: string`
|
|
249
|
+
|
|
250
|
+
**`AssemblyPartMateInput`**
|
|
251
|
+
- `connector: string` — Name of a connector declared on the part (via `withConnectors()`).
|
|
252
|
+
- `toLink: string` — Name of the link this connector's origin is pinned to.
|
|
253
|
+
- `aimLink?: string` — Optional second link to orient toward. When set, the part is rotated so the connector's **axis** aims from `toLink` toward `aimLink`, posing an oriented bone instead of only translating it. For full pose without relying on a connector axis, declare a second mate (two connectors → two links).
|
|
254
|
+
|
|
255
|
+
#### `frame()` — Add a named rig frame to the assembly.
|
|
256
|
+
|
|
257
|
+
A frame is a solved pose: `origin` plus orientation. `axis` is the frame's primary direction and `up` fixes roll around that axis. Use frames for robot links, joint axes, and parts that must carry orientation. Use `link()` for solved points in distance/angle graphs.
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
frame(name: string, options: AssemblyFrameOptions): Assembly
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**`AssemblyFrameOptions`**: `origin: [ number, number, number ]`, `axis: [ number, number, number ]`, `up: [ number, number, number ]`, `fixed?: boolean`, `metadata?: Record<string, unknown>`
|
|
264
|
+
|
|
265
|
+
#### `fixedJoint()` — Rigidly attach a child rig frame to a parent rig frame.
|
|
266
|
+
|
|
267
|
+
Fixed joints carry frame hierarchy but do not expose a Motion control.
|
|
268
|
+
|
|
269
|
+
```ts
|
|
270
|
+
fixedJoint(name: string, options: AssemblyFixedFrameJointOptions): Assembly
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
`AssemblyFixedFrameJointOptions`: `{ parent: string, child: string, metadata?: Record<string, unknown> }`
|
|
274
|
+
|
|
275
|
+
#### `revoluteJoint()` — Add a revolute rig-frame joint.
|
|
276
|
+
|
|
277
|
+
The child frame rotates around the parent frame's `axis` direction. Moving frame joints appear in Motion by default; pass `control: false` to keep the joint solved at its default value without showing a Motion control.
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
revoluteJoint(name: string, options: AssemblyMovingFrameJointOptions): Assembly
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**`AssemblyMovingFrameJointOptions`**: `parent: string`, `child: string`, `min?: number`, `max?: number`, `default?: number`, `unit?: string`, `control?: boolean`, `metadata?: Record<string, unknown>`
|
|
284
|
+
|
|
285
|
+
#### `prismaticJoint()` — Add a prismatic rig-frame joint.
|
|
286
|
+
|
|
287
|
+
The child frame translates along the parent frame's `axis` direction. Moving frame joints appear in Motion by default; pass `control: false` to keep the joint solved at its default value without showing a Motion control.
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
prismaticJoint(name: string, options: AssemblyMovingFrameJointOptions): Assembly
|
|
291
|
+
```
|
|
292
|
+
|
|
144
293
|
**Connectors**
|
|
145
294
|
|
|
146
295
|
#### `usedConnectorRefs()` — Connector refs (e.g. "PartName.connectorName") consumed by connect/match calls.
|
|
@@ -159,6 +308,10 @@ Use the single-argument overload to attach assembly-level connectors — these a
|
|
|
159
308
|
withConnectors(partName: string, connectors: Record<string, ConnectorInput>): Assembly
|
|
160
309
|
```
|
|
161
310
|
|
|
311
|
+
**`PortInput`**: `origin?: [ number, number, number ]`, `axis?: [ number, number, number ]`, `start?: [ number, number, number ]`, `end?: [ number, number, number ]`, `up?: [ number, number, number ]`, `kind?: JointType`, `min?: number`, `max?: number`
|
|
312
|
+
|
|
313
|
+
`ConnectorInput`: `{ connectorType?: string, gender?: ConnectorGender, measurements?: Record<string, number | string> }`
|
|
314
|
+
|
|
162
315
|
#### `getConnectors()` — Get connectors declared on a part in part-local space.
|
|
163
316
|
|
|
164
317
|
```ts
|
|
@@ -175,23 +328,35 @@ getConnector(ref: string): { partName: string; connectorName: string; connector:
|
|
|
175
328
|
|
|
176
329
|
Connector references use `"PartName.connectorName"` format. The system aligns connector origins (child connector lands exactly on parent connector) and derives the joint frame and axis from the connector geometry — no manual `frame` or `axis` math needed.
|
|
177
330
|
|
|
331
|
+
Use `connect()` for frame-aware articulated geometry. A connector's `origin` is the pivot/contact point, `axis` is the hinge line or slide direction, and `up` is the secondary orientation vector that locks the part's zero-angle twist. If `up` is omitted, ForgeCAD chooses a deterministic perpendicular vector, but authored mechanisms should provide `up` whenever rest orientation matters.
|
|
332
|
+
|
|
178
333
|
**Face-to-face convention:** Connectors always meet face-to-face, like a USB plug meeting a socket. Each connector's axis points "outward" from its part. When two connectors mate, the system brings them together so their axes oppose (anti-parallel). This is the same convention used by `matchTo()`.
|
|
179
334
|
|
|
180
335
|
For a revolute joint (hinge), both connectors' axes should point outward from their respective parts along the hinge line. For a prismatic joint (slider), both axes should point along the slide direction from their part's perspective.
|
|
181
336
|
|
|
337
|
+
**Mirrored revolute axes:** Revolute values follow the right-hand rule around the joint axis. If a bilateral mechanism mirrors a hinge axis, for example `[1, 0, 0]` on the right side and `[-1, 0, 0]` on the left side, the same physical `+theta` value rotates the two sides in opposite fore/aft senses. The mirrored pose uses the negated revolute value; physical limits mirror as `[min, max] -> [-max, -min]`. Prismatic joints do not have this handedness flip. For bilateral mechanisms, prefer side-neutral link controls or an explicit state mapping when you want equal semantic pose values on both sides.
|
|
338
|
+
|
|
182
339
|
The joint type is inferred from the connector's `kind` field if not specified in `options`.
|
|
183
340
|
|
|
184
341
|
When connectors are defined with `start`/`end`, you can control which point on each connector meets via `align` / `parentAlign` / `childAlign` (`'start'`, `'middle'`, `'end'`).
|
|
185
342
|
|
|
186
|
-
Use `connect()` when connector origins must physically coincide (flange-to-flange, bolt-into-bore).
|
|
343
|
+
Use `connect()` when connector origins must physically coincide (flange-to-flange, bolt-into-bore).
|
|
187
344
|
|
|
188
345
|
```ts
|
|
189
346
|
// Hinge: both axes point outward along the hinge line
|
|
190
347
|
const frame = box(100, 10, 80).withConnectors({
|
|
191
|
-
hinge: connector("hinge", {
|
|
348
|
+
hinge: connector("hinge", {
|
|
349
|
+
origin: [0, 0, 40],
|
|
350
|
+
axis: [0, 0, 1],
|
|
351
|
+
up: [1, 0, 0],
|
|
352
|
+
}),
|
|
192
353
|
});
|
|
193
354
|
const door = box(60, 4, 80).withConnectors({
|
|
194
|
-
hinge: connector("hinge", {
|
|
355
|
+
hinge: connector("hinge", {
|
|
356
|
+
origin: [0, 0, 40],
|
|
357
|
+
axis: [0, 0, -1],
|
|
358
|
+
up: [1, 0, 0],
|
|
359
|
+
}),
|
|
195
360
|
});
|
|
196
361
|
assembly("Door")
|
|
197
362
|
.addPart("Frame", frame)
|
|
@@ -203,6 +368,16 @@ assembly("Door")
|
|
|
203
368
|
connect(parentConnectorRef: string, childConnectorRef: string, options?: ConnectOptions): Assembly
|
|
204
369
|
```
|
|
205
370
|
|
|
371
|
+
**`ConnectOptions`**
|
|
372
|
+
|
|
373
|
+
| Option | Type | Description |
|
|
374
|
+
|--------|------|-------------|
|
|
375
|
+
| `flip?` | `boolean` | This parameter is ignored. If your connectors produce wrong orientation, fix the connector axis directions instead of using flip. |
|
|
376
|
+
| `parentAlign?` | `PortAlign` | Which point on the parent connector to align: 'start', 'middle' (default), or 'end'. |
|
|
377
|
+
| `childAlign?` | `PortAlign` | Which point on the child connector to align: 'start', 'middle' (default), or 'end'. |
|
|
378
|
+
| `align?` | `PortAlign` | Shorthand: set both parentAlign and childAlign at once. |
|
|
379
|
+
| `as?`, `type?`, `min?`, `max?`, `default?`, `unit?`, `effort?`, `velocity?`, `damping?`, `friction?` | | — |
|
|
380
|
+
|
|
206
381
|
#### `match()` — Auto-create a joint by matching typed connectors between two parts.
|
|
207
382
|
|
|
208
383
|
Connectors can carry a `connectorType` string and a `gender` (`'male'`, `'female'`, or `'neutral'`). `match()` validates type and gender compatibility (use `{ force: true }` to skip validation) and creates the joint automatically from the connector's `kind` metadata.
|
|
@@ -225,13 +400,15 @@ const mech = assembly("Door")
|
|
|
225
400
|
.addPart("Frame", frame)
|
|
226
401
|
.addPart("Door", door)
|
|
227
402
|
.match("Door", "Frame", { hinge_top: "hinge_top", hinge_bottom: "hinge_bottom" });
|
|
228
|
-
//
|
|
403
|
+
// Matching connectors computes the placement relationship automatically.
|
|
229
404
|
```
|
|
230
405
|
|
|
231
406
|
```ts
|
|
232
407
|
match(childPartName: string, parentPartName: string, pairs: Record<string, string>, options?: MatchToOptions & { as?: string; }): Assembly
|
|
233
408
|
```
|
|
234
409
|
|
|
410
|
+
`MatchToOptions`: `{ force?: boolean, angle?: number, distance?: number }`
|
|
411
|
+
|
|
235
412
|
**References**
|
|
236
413
|
|
|
237
414
|
#### `withReferences()` — Attach named placement reference points to this assembly. These are surfaced automatically on the ImportedAssembly when this file is imported via require(), so consumers can use placeReference() without re-declaring them. Returns a new Assembly — does not mutate.
|
|
@@ -240,186 +417,127 @@ match(childPartName: string, parentPartName: string, pairs: Record<string, strin
|
|
|
240
417
|
withReferences(refs: Pick<PlacementReferenceInput, "points">): Assembly
|
|
241
418
|
```
|
|
242
419
|
|
|
243
|
-
|
|
420
|
+
**`PlacementReferenceInput`**: `points?: Record<string, [ number, number, number ]>`, `edges?: Record<string, PlacementEdgeRef>`, `surfaces?: Record<string, PlacementSurfaceRef>`, `objects?: Record<string, PlacementObjectInput>`
|
|
421
|
+
|
|
422
|
+
`PlacementEdgeRef`: `{ start: Vec3, end: Vec3 }`
|
|
244
423
|
|
|
245
|
-
|
|
424
|
+
`PlacementSurfaceRef`: `{ center: Vec3, normal: Vec3 }`
|
|
425
|
+
|
|
426
|
+
**Solving**
|
|
246
427
|
|
|
247
|
-
|
|
428
|
+
#### `solve()` — Solve the assembly at the given control state and return positioned parts.
|
|
248
429
|
|
|
249
|
-
|
|
430
|
+
Solves assembly-native kinematic links first. Controlled `addAngleBetweenLinks()` relationships read values from `state` by name, clamp to their declared limits, and expose the solved graph on `SolvedAssembly.kinematics`. Angles solve in the plane of their three authored link positions, so a limb that swings out of the `z = 0` plane poses correctly; structural edges hold their bone lengths so a fully angle-driven serial chain follows forward kinematics.
|
|
250
431
|
|
|
251
|
-
|
|
432
|
+
Connector mates declared on `addPart(..., { mate })` attach geometry to solved links while preserving part and connector identity:
|
|
252
433
|
|
|
253
|
-
|
|
434
|
+
- one mate **positions** the connector origin on its link;
|
|
435
|
+
- a mate with `aimLink` (or a second mate to another link) also **orients** the part, rotating an oriented bone to span its links rather than only translating it;
|
|
436
|
+
- a third mate **pins the roll** about the bone axis (full frame), e.g. a bore or clevis that must face a specific way.
|
|
254
437
|
|
|
255
|
-
|
|
438
|
+
Connector-frame joints created by `connect()` / `match()` are also evaluated; their values are read from `state` by joint name and clamped to joint limits.
|
|
256
439
|
|
|
257
440
|
```ts
|
|
258
|
-
return mech.solve({
|
|
441
|
+
return mech.solve({ theta: 45 });
|
|
259
442
|
```
|
|
260
443
|
|
|
261
444
|
```ts
|
|
262
445
|
solve(state?: JointState): SolvedAssembly
|
|
263
446
|
```
|
|
264
447
|
|
|
265
|
-
|
|
448
|
+
<!-- forgecad-skill:exclude-start symbol="Assembly.Compatibility methods" reason="Compatibility-only renamed API. Use `addAngleBetweenLinkSegmentAndWorldDirection()` instead." -->
|
|
449
|
+
**Compatibility**
|
|
450
|
+
<!-- forgecad-skill:exclude-end -->
|
|
266
451
|
|
|
267
|
-
|
|
452
|
+
<!-- forgecad-skill:exclude-start symbol="Assembly.addAngleOfLinkSegmentFromXAxis" reason="Compatibility-only renamed API. Use `addAngleBetweenLinkSegmentAndWorldDirection()` instead." -->
|
|
453
|
+
#### `addAngleOfLinkSegmentFromXAxis()`
|
|
268
454
|
|
|
269
|
-
|
|
270
|
-
mate(fn: (m: MateBuilder) => void): Assembly
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
#### `addFrame()` — Add a virtual reference frame (no geometry) to the assembly graph.
|
|
274
|
-
|
|
275
|
-
Useful when you need a named pivot point or coordinate frame that has no visual geometry. Acts like a zero-volume part and can be connected to other parts via joints.
|
|
455
|
+
> **Not included in ForgeCAD AI skill context yet.** This API remains visible in human docs, but is intentionally omitted from shipped agent skills until it is ready for agent-first use. Compatibility-only renamed API. Use `addAngleBetweenLinkSegmentAndWorldDirection()` instead.
|
|
276
456
|
|
|
277
457
|
```ts
|
|
278
|
-
|
|
458
|
+
addAngleOfLinkSegmentFromXAxis(fromLink: string, toLink: string, options?: AssemblyAngleBetweenLinksOptions): Assembly
|
|
279
459
|
```
|
|
460
|
+
<!-- forgecad-skill:exclude-end -->
|
|
280
461
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
Connectors declared on the part (via `withConnectors()`) are captured automatically. Parts are positioned at world origin by default unless a `transform` is provided in `options`. For root parts (no incoming joint), `transform` is their final world position.
|
|
462
|
+
<!-- forgecad-skill:exclude-start symbol="Assembly.addAngleOfLinkSegmentFromYAxis" reason="Compatibility-only renamed API. Use `addAngleBetweenLinkSegmentAndWorldDirection()` instead." -->
|
|
463
|
+
#### `addAngleOfLinkSegmentFromYAxis()`
|
|
284
464
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
```ts
|
|
288
|
-
const housing = group(
|
|
289
|
-
{ name: "Body", shape: body },
|
|
290
|
-
{ name: "Lid", shape: lid },
|
|
291
|
-
);
|
|
292
|
-
assembly.addPart("Base Assembly", housing);
|
|
293
|
-
```
|
|
465
|
+
> **Not included in ForgeCAD AI skill context yet.** This API remains visible in human docs, but is intentionally omitted from shipped agent skills until it is ready for agent-first use. Compatibility-only renamed API. Use `addAngleBetweenLinkSegmentAndWorldDirection()` instead.
|
|
294
466
|
|
|
295
467
|
```ts
|
|
296
|
-
|
|
468
|
+
addAngleOfLinkSegmentFromYAxis(fromLink: string, toLink: string, options?: AssemblyAngleBetweenLinksOptions): Assembly
|
|
297
469
|
```
|
|
470
|
+
<!-- forgecad-skill:exclude-end -->
|
|
298
471
|
|
|
299
|
-
|
|
472
|
+
<!-- forgecad-skill:exclude-start symbol="Assembly.toJointsView" reason="Compatibility-only viewport FK adapter. Prefer returning `Assembly` directly so controls move through the solver-backed link/edge kinematics model." -->
|
|
473
|
+
#### `toJointsView()` — Deprecated adapter that derives viewport-only FK controls from the assembly graph.
|
|
300
474
|
|
|
301
|
-
|
|
475
|
+
> **Not included in ForgeCAD AI skill context yet.** This API remains visible in human docs, but is intentionally omitted from shipped agent skills until it is ready for agent-first use. Compatibility-only viewport FK adapter. Prefer returning `Assembly` directly so controls move through the solver-backed link/edge kinematics model.
|
|
302
476
|
|
|
303
|
-
|
|
304
|
-
childWorld = parentWorld × frame × motion(value) × childBase
|
|
305
|
-
```
|
|
477
|
+
Prefer returning the `Assembly` itself. The runtime now exposes returned assembly controls automatically and re-evaluates the assembly with `solve(state)` when a control changes. That path is the source of truth for link/edge kinematics, handles closed-loop mechanisms through the real solver, and avoids stacking a viewport transform on top of already-solved geometry.
|
|
306
478
|
|
|
307
|
-
|
|
479
|
+
`toJointsView()` remains for legacy scripts that intentionally want the old viewport-only FK behavior.
|
|
308
480
|
|
|
309
|
-
|
|
310
|
-
addJoint(name: string, type: JointType, parent: string, child: string, options?: JointOptions): Assembly
|
|
311
|
-
```
|
|
481
|
+
**Legacy mirrored revolute tracks:** Viewport defaults and animation keyframes use physical joint values. If two mirrored revolute joints have opposite axes, equal keyframe values are not a mirrored pose; negate the mirrored side's values, or prefer returned-assembly controls so the solver owns the side-neutral state.
|
|
312
482
|
|
|
313
|
-
|
|
483
|
+
**Preferred**
|
|
314
484
|
|
|
315
485
|
```ts
|
|
316
|
-
|
|
486
|
+
return mech; // editor controls call mech.solve(state)
|
|
317
487
|
```
|
|
318
488
|
|
|
319
|
-
#### `addPrismatic()` — Shorthand for `addJoint(name, 'prismatic', parent, child, options)`.
|
|
320
|
-
|
|
321
489
|
```ts
|
|
322
|
-
|
|
490
|
+
toJointsView(options?: ToJointsViewOptions): void
|
|
323
491
|
```
|
|
324
492
|
|
|
325
|
-
|
|
493
|
+
**`ToJointsViewOptions`**: `defaults?: Record<string, number>`, `overrides?: Record<string, Partial<JointViewInput>>`, `animations?: JointViewAnimationInput[]`, `couplings?: JointViewCouplingInput[]`, `defaultAnimation?: string`, `enabled?: boolean`
|
|
326
494
|
|
|
327
|
-
|
|
495
|
+
**`JointViewInput`**: `name: string`, `child: string`, `parent?: string`, `type?: JointViewType`, `axis?: JointViewAxis`, `pivot?: [ number, number, number ]`, `min?: number`, `max?: number`, `default?: number`, `unit?: string`, `hidden?: boolean`
|
|
328
496
|
|
|
329
|
-
|
|
330
|
-
addFixed(name: string, parent: string, child: string, options?: JointOptions): Assembly
|
|
331
|
-
```
|
|
497
|
+
`JointViewAnimationInput`: `{ name: string, duration?: number, loop?: boolean, continuous?: boolean, keyframes: JointViewAnimationKeyframeInput[] }`
|
|
332
498
|
|
|
333
|
-
|
|
499
|
+
**`JointViewAnimationKeyframeInput`**
|
|
500
|
+
- `at?: number` — Timeline position [0, 1]. If omitted from ALL keyframes, positions are auto-computed from tick weights.
|
|
501
|
+
- `ticks?: number` — Relative weight of the segment from this keyframe to the next (default 1). Only used in tick-based mode (when `at` is omitted). Last keyframe's ticks value is ignored.
|
|
502
|
+
- Also: `values: Record<string, number>`
|
|
334
503
|
|
|
335
|
-
|
|
504
|
+
`JointViewCouplingInput`: `{ joint: string, terms: JointViewCouplingTermInput[], offset?: number }`
|
|
336
505
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
```
|
|
506
|
+
`JointViewCouplingTermInput`: `{ joint: string, ratio?: number }`
|
|
507
|
+
<!-- forgecad-skill:exclude-end -->
|
|
340
508
|
|
|
341
|
-
|
|
509
|
+
**Other**
|
|
342
510
|
|
|
343
|
-
|
|
344
|
-
assembly
|
|
345
|
-
.addRevolute("Steering", "Base", "Turret", { axis: [0, 0, 1] })
|
|
346
|
-
.addRevolute("WheelDrive", "Turret", "Wheel", { axis: [1, 0, 0] })
|
|
347
|
-
.addRevolute("TopGear", "Base", "TopInput", { axis: [0, 0, 1] })
|
|
348
|
-
.addJointCoupling("TopGear", {
|
|
349
|
-
terms: [
|
|
350
|
-
{ joint: "Steering", ratio: 1 },
|
|
351
|
-
{ joint: "WheelDrive", ratio: 20 / 14 },
|
|
352
|
-
],
|
|
353
|
-
});
|
|
354
|
-
```
|
|
511
|
+
#### `mate()` — Register mate constraints between parts. Constraints are solved during `solve()` to derive part positions and explode hints. Part references use "partName:featureName" format.
|
|
355
512
|
|
|
356
513
|
```ts
|
|
357
|
-
|
|
514
|
+
mate(fn: (m: MateBuilder) => void): Assembly
|
|
358
515
|
```
|
|
359
516
|
|
|
360
|
-
#### `
|
|
361
|
-
|
|
362
|
-
Choose exactly one ratio source:
|
|
517
|
+
#### `edgeBetweenFrames()` — Add a visual skeleton edge between two rig frame origins.
|
|
363
518
|
|
|
364
|
-
|
|
365
|
-
- `pair` — a `GearRatioLike` from `lib.gearPair`, `lib.bevelGearPair`, etc. (uses `pair.jointRatio`)
|
|
366
|
-
- `driverTeeth` + `drivenTeeth` — auto-computes ratio; use `mesh` to control sign (`'external'` = negative/opposite rotation, `'internal'` = positive, `'bevel'`/`'face'` = negative)
|
|
367
|
-
|
|
368
|
-
When `pair` carries a `phaseDeg`, it is auto-applied as the coupling `offset` to align teeth correctly. Override with `offset: 0` if gear shapes already have the phase baked in.
|
|
369
|
-
|
|
370
|
-
```ts
|
|
371
|
-
const pair = lib.gearPair({ pinion: { module: 1.25, teeth: 14 }, gear: { module: 1.25, teeth: 42 } });
|
|
372
|
-
assembly
|
|
373
|
-
.addRevolute("Pinion", "Base", "PinionPart", { axis: [0, 0, 1] })
|
|
374
|
-
.addRevolute("Driven", "Base", "GearPart", { axis: [0, 0, 1] })
|
|
375
|
-
.addGearCoupling("Driven", "Pinion", { pair });
|
|
376
|
-
```
|
|
519
|
+
Frame edges follow the solved frame poses produced by `fixedJoint()`, `revoluteJoint()`, and `prismaticJoint()`. They do not add constraints, degrees of freedom, parts, or geometry; use them to make a frame-only rig readable in the Motion/rig inspection overlay.
|
|
377
520
|
|
|
378
521
|
```ts
|
|
379
|
-
|
|
522
|
+
edgeBetweenFrames(a: string, b: string, options?: AssemblyFrameEdgeOptions): Assembly
|
|
380
523
|
```
|
|
381
524
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
Divides `[from, to]` into `steps` intervals (producing `steps + 1` frames). At each sample, the assembly is solved with the sweeping joint at that value and `baseState` for all others. Returns one `JointSweepFrame` per sample with the joint value, collision findings, and any solve warnings.
|
|
525
|
+
`AssemblyFrameEdgeOptions`: `{ name?: string, metadata?: Record<string, unknown> }`
|
|
385
526
|
|
|
386
|
-
|
|
527
|
+
#### `linkToward()` — Create a derived link at a fixed distance from `fromLink` toward `towardLink`.
|
|
387
528
|
|
|
388
|
-
|
|
389
|
-
const sweep = mech.sweepJoint("elbow", -10, 135, 12, { shoulder: 35 });
|
|
390
|
-
const hits = sweep.filter(frame => frame.collisions.length > 0);
|
|
391
|
-
console.log(`Collisions at ${hits.length} of ${sweep.length} poses`);
|
|
392
|
-
```
|
|
529
|
+
Derived links are trace/reference points. They are recomputed after the primary link solve and cannot participate in structural edges or angle constraints.
|
|
393
530
|
|
|
394
531
|
```ts
|
|
395
|
-
|
|
532
|
+
linkToward(name: string, fromLink: string, towardLink: string, distance: number): Assembly
|
|
396
533
|
```
|
|
397
534
|
|
|
398
|
-
#### `
|
|
399
|
-
|
|
400
|
-
Solves the assembly at rest (all joints = default), then converts each joint into a `JointViewInput` with world-space pivot and axis. Fixed joints become hidden zero-range revolute entries so attached parts follow their parent during animation. Joint couplings are forwarded to the viewport automatically.
|
|
401
|
-
|
|
402
|
-
This method is optional. Call it only when you want viewport joint sliders, coupled controls, or playback animations. If you only want geometry, return the `Assembly` or `SolvedAssembly` directly and skip `toJointsView()`.
|
|
403
|
-
|
|
404
|
-
**Critical pitfall:** Always call `toJointsView()` before solving for display. Then solve at the **rest pose** (no state overrides) and return that solved assembly result directly. Do not flatten it with `.toGroup()` if you want the viewport joint animation to keep working.
|
|
405
|
-
|
|
406
|
-
Do not solve at a non-zero angle when using `toJointsView()` — the viewport will apply the same rotation again, double-rotating the part.
|
|
407
|
-
|
|
408
|
-
```ts
|
|
409
|
-
mech.toJointsView({
|
|
410
|
-
defaults: { J1: 30 },
|
|
411
|
-
animations: [{
|
|
412
|
-
name: "Swing", duration: 2, loop: true,
|
|
413
|
-
keyframes: [{ values: { J1: -45 } }, { values: { J1: 45 } }, { values: { J1: -45 } }],
|
|
414
|
-
}],
|
|
415
|
-
});
|
|
535
|
+
#### `linkAwayFrom()` — Create a derived link at a fixed distance from `fromLink` away from `awayFromLink`.
|
|
416
536
|
|
|
417
|
-
|
|
418
|
-
return mech.solve();
|
|
419
|
-
```
|
|
537
|
+
Use this for coupler trace/extension points such as the Chebyshev lambda linkage's point beyond the rocker joint.
|
|
420
538
|
|
|
421
539
|
```ts
|
|
422
|
-
|
|
540
|
+
linkAwayFrom(name: string, fromLink: string, awayFromLink: string, distance: number): Assembly
|
|
423
541
|
```
|
|
424
542
|
|
|
425
543
|
#### `describe()` — Return the serializable assembly definition used by solve/inspect pipelines.
|
|
@@ -439,7 +557,7 @@ describe(): AssemblyDefinition
|
|
|
439
557
|
|
|
440
558
|
A wrapper around an imported `Assembly` that provides kinematic access and convenient transform helpers.
|
|
441
559
|
|
|
442
|
-
When a `.forge.js` file returns an unsolved `Assembly`, [`require()`](/docs/core#require) wraps it in an `ImportedAssembly`. This preserves the kinematic structure — you can call `solve()
|
|
560
|
+
When a `.forge.js` file returns an unsolved `Assembly`, [`require()`](/docs/core#require) wraps it in an `ImportedAssembly`. This preserves the kinematic structure — you can call `solve()` and `mergeInto()` — while also allowing convenience transforms that auto-solve at default values.
|
|
443
561
|
|
|
444
562
|
**Kinematic access**
|
|
445
563
|
|
|
@@ -468,7 +586,7 @@ require("./arm.forge.js").mergeInto(robot, {
|
|
|
468
586
|
});
|
|
469
587
|
```
|
|
470
588
|
|
|
471
|
-
#### `assembly()` — The underlying Assembly
|
|
589
|
+
#### `assembly()` — The underlying Assembly, for advanced composition and inspection.
|
|
472
590
|
|
|
473
591
|
```ts
|
|
474
592
|
get assembly(): Assembly
|
|
@@ -486,6 +604,14 @@ solve(state?: JointState): SolvedAssembly
|
|
|
486
604
|
part(name: string, state?: JointState): AssemblyPart
|
|
487
605
|
```
|
|
488
606
|
|
|
607
|
+
#### `getPart()` — Return a specific named part positioned at the default solved pose.
|
|
608
|
+
|
|
609
|
+
This mirrors `SolvedAssembly.getPart()` for imported assemblies. Use `solve(state).getPart(name)` when inspecting a non-default joint state.
|
|
610
|
+
|
|
611
|
+
```ts
|
|
612
|
+
getPart(partName: string): AssemblyPart
|
|
613
|
+
```
|
|
614
|
+
|
|
489
615
|
#### `toGroup()` — Convert all assembly parts to a ShapeGroup with named children. Use this for composition, transforms, or child lookup — not as a required render step for assemblies. Child names match the part names used in the assembly. Any stored placement offset and placement references are forwarded to the group.
|
|
490
616
|
|
|
491
617
|
```ts
|
|
@@ -564,17 +690,35 @@ color(hex: string): ShapeGroup
|
|
|
564
690
|
child(name: string): Shape | Sketch | ShapeGroup
|
|
565
691
|
```
|
|
566
692
|
|
|
567
|
-
#### `
|
|
693
|
+
#### `collisionReport()` — Detect overlapping part pairs at the default solved pose.
|
|
694
|
+
|
|
695
|
+
This mirrors `SolvedAssembly.collisionReport()` for imported assemblies. Use `solve(state).collisionReport(options)` when inspecting a non-default joint state.
|
|
696
|
+
|
|
697
|
+
```ts
|
|
698
|
+
collisionReport(options?: CollisionOptions): CollisionFinding[]
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
`CollisionOptions`: `{ parts?: string[], ignorePairs?: Array<[ string, string ]>, minOverlapVolume?: number }`
|
|
702
|
+
|
|
703
|
+
#### `minClearance()` — Compute the minimum gap between two parts at the default solved pose.
|
|
704
|
+
|
|
705
|
+
This mirrors `SolvedAssembly.minClearance()` for imported assemblies. Use `solve(state).minClearance(partA, partB, searchLength)` when inspecting a non-default joint state.
|
|
706
|
+
|
|
707
|
+
```ts
|
|
708
|
+
minClearance(partA: string, partB: string, searchLength?: number): number
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
#### `mergeInto()` — Flatten this sub-assembly's parts and relationships into `parent` and wire a mount relationship.
|
|
568
712
|
|
|
569
|
-
All part and joint names from the sub-assembly are prefixed with `"${options.prefix}."` to avoid collisions. After the merge,
|
|
713
|
+
All part, link, and legacy joint names from the sub-assembly are prefixed with `"${options.prefix}."` to avoid collisions. After the merge, controls are driven from the parent using the prefixed names:
|
|
570
714
|
|
|
571
715
|
```ts
|
|
572
|
-
parent.solve({ "Left Arm.
|
|
716
|
+
parent.solve({ "Left Arm.theta": 45, "Right Arm.theta": -20 })
|
|
573
717
|
```
|
|
574
718
|
|
|
575
|
-
|
|
719
|
+
Connectors from sub-assembly parts are forwarded with the prefix.
|
|
576
720
|
|
|
577
|
-
The sub-assembly must have exactly one root part
|
|
721
|
+
The sub-assembly must have exactly one root part before it can be merged.
|
|
578
722
|
|
|
579
723
|
```ts
|
|
580
724
|
const robot = assembly("Robot").addPart("Chassis", chassis);
|
|
@@ -591,6 +735,25 @@ require("./arm.forge.js").mergeInto(robot, {
|
|
|
591
735
|
mergeInto(parent: Assembly, options: MergeIntoOptions): Assembly
|
|
592
736
|
```
|
|
593
737
|
|
|
738
|
+
**`MergeIntoOptions`**
|
|
739
|
+
|
|
740
|
+
| Option | Type | Description |
|
|
741
|
+
|--------|------|-------------|
|
|
742
|
+
| `prefix?` | `string` | Prefix applied to every part name and joint name from the sub-assembly. E.g. prefix "Left Arm" turns part "Base" into "Left Arm.Base". Strongly recommended to avoid name collisions when merging multiple instances. |
|
|
743
|
+
| `mountParent` | `string` | Part name in the parent assembly to attach the sub-assembly root to. |
|
|
744
|
+
| `mountJoint` | `string` | Name for the new mount joint in the parent graph. |
|
|
745
|
+
| `mountType?` | `JointType` | Joint type for the mount connection (default: 'fixed'). |
|
|
746
|
+
| `mountOptions?` | `JointOptions` | Frame, axis, limits, and other options for the mount joint. |
|
|
747
|
+
|
|
748
|
+
**`JointOptions`**
|
|
749
|
+
|
|
750
|
+
| Option | Type | Description |
|
|
751
|
+
|--------|------|-------------|
|
|
752
|
+
| `connectorRefs?` | `JointConnectorRefs` | Connector refs that define this joint contract. Usually set by `connect()` / `match()`. |
|
|
753
|
+
| `frame?`, `origin?`, `axis?`, `min?`, `max?`, `default?`, `unit?`, `effort?`, `velocity?`, `damping?`, `friction?` | | — |
|
|
754
|
+
|
|
755
|
+
`JointConnectorRefs`: `{ parent: string, child: string, parentAlign?: PortAlign, childAlign?: PortAlign }`
|
|
756
|
+
|
|
594
757
|
### `SolvedAssembly`
|
|
595
758
|
|
|
596
759
|
The result of solving an assembly at a specific joint state.
|
|
@@ -599,7 +762,7 @@ The result of solving an assembly at a specific joint state.
|
|
|
599
762
|
|
|
600
763
|
**Validation**
|
|
601
764
|
|
|
602
|
-
Call `collisionReport()` to detect overlapping parts
|
|
765
|
+
Call `collisionReport()` to detect overlapping parts at this solved pose.
|
|
603
766
|
|
|
604
767
|
```ts
|
|
605
768
|
const solved = mech.solve({ shoulder: 45, elbow: -20 });
|
|
@@ -645,6 +808,30 @@ get mateDof(): number | null
|
|
|
645
808
|
get mateConverged(): boolean | null
|
|
646
809
|
```
|
|
647
810
|
|
|
811
|
+
#### `kinematics()` — Solved assembly-native kinematic or frame-edge overlay data, or null when no rig overlay data was declared.
|
|
812
|
+
|
|
813
|
+
```ts
|
|
814
|
+
get kinematics(): SolvedAssemblyKinematics | null
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
#### `getLinkPosition()` — Return the solved world position of a kinematic link.
|
|
818
|
+
|
|
819
|
+
```ts
|
|
820
|
+
getLinkPosition(linkName: string): Vec3
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
#### `getFrame()` — Return the solved world transform for a named rig frame.
|
|
824
|
+
|
|
825
|
+
```ts
|
|
826
|
+
getFrame(frameName: string): Transform
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
#### `frames()` — Return solved rig frames, including origin, axis, up, and transform.
|
|
830
|
+
|
|
831
|
+
```ts
|
|
832
|
+
get frames(): SolvedAssemblyFrameDef[]
|
|
833
|
+
```
|
|
834
|
+
|
|
648
835
|
#### `getTransform()` — Return the world-space [`Transform`](/docs/core#transform) for the named part at the solved pose.
|
|
649
836
|
|
|
650
837
|
```ts
|