forgecad 0.9.16 → 0.10.1

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.
Files changed (162) hide show
  1. package/dist/assets/{AdminPage-CXvls4-J.js → AdminPage-DcCnj0qo.js} +1 -1
  2. package/dist/assets/{BenchmarkPage-B27zk8xL.js → BenchmarkPage-BVEpJSVk.js} +1 -1
  3. package/dist/assets/{BlogPage-CMAVvgQL.js → BlogPage-DHaGP50_.js} +1 -1
  4. package/dist/assets/{DocsPage-knf4I4h7.js → DocsPage-CDoxHkz8.js} +40 -859
  5. package/dist/assets/EditorApp-BJ0Dloyh.js +16446 -0
  6. package/dist/assets/{EmbedViewer-D7ZGlFjx.js → EmbedViewer-CRKZbY0y.js} +2 -2
  7. package/dist/assets/{LandingPageProofDriven-CnevhTE8.js → LandingPageProofDriven-BxHkYRE7.js} +1 -1
  8. package/dist/assets/{LegalPage-BPTUmqeg.js → LegalPage-B-u6FrVv.js} +1 -1
  9. package/dist/assets/{PricingPage-B0D4goG_.js → PricingPage-CzpZ6-Ce.js} +1 -1
  10. package/dist/assets/{SettingsPage-CFF-UgjI.js → SettingsPage-CIZSSAd0.js} +1 -1
  11. package/dist/assets/{app-CE3sYcV7.css → app-CjsbDlb7.css} +143 -0
  12. package/dist/assets/{app-T0pDcSX4.js → app-DaTMg3nH.js} +1310 -290
  13. package/dist/assets/cli/{render-C5pcIISc.js → render-DPf4AYJK.js} +55 -60
  14. package/dist/assets/{constructionHistoryWorker-Ba2Hm58b.js → constructionHistoryWorker-AwMMWSxg.js} +1103 -349
  15. package/dist/assets/{evalWorker-vkx310U2.js → evalWorker-CjZZWRWW.js} +5209 -2643
  16. package/dist/assets/{inspectWorker-BuTJDVX6.js → inspectWorker-CZsCFtQT.js} +1163 -409
  17. package/dist/assets/{jointPose-B_Cgedn9.js → jointPose-DzQOViQH.js} +1 -1
  18. package/dist/assets/{manifold-BWgsjmAM.js → manifold-BYlzU521.js} +1 -1
  19. package/dist/assets/{manifold-D6IFSkhH.js → manifold-DgXo0T5P.js} +2 -2
  20. package/dist/assets/{manifold-rZexZI0G.js → manifold-K1SkarlQ.js} +1 -1
  21. package/dist/assets/{reportWorker-0AGij1Ru.js → reportWorker-B9nWwSrB.js} +8501 -3393
  22. package/dist/assets/{scalar-sampling-budget-J5cuzxT1.js → scalar-sampling-budget-prBw_s8t.js} +6067 -3479
  23. package/dist/assets/{scanProxyWorker-Vl4Wxa1y.js → scanProxyWorker-2GtDLk-R.js} +1 -1
  24. package/dist/assets/{javascript-1kQXfVaz.js → typescript-DBQ6RN5l.js} +874 -22
  25. package/dist/cli/render.html +1 -1
  26. package/dist/docs/index.html +3 -3
  27. package/dist/docs-raw/AI/usage.md +1 -1
  28. package/dist/docs-raw/CLI.md +77 -240
  29. package/dist/docs-raw/README.md +6 -0
  30. package/dist/docs-raw/component-model.md +17 -150
  31. package/dist/docs-raw/generated/assembly.md +188 -582
  32. package/dist/docs-raw/generated/concepts.md +259 -3501
  33. package/dist/docs-raw/generated/core.md +283 -1250
  34. package/dist/docs-raw/generated/curves.md +387 -1608
  35. package/dist/docs-raw/generated/legacy.md +162 -0
  36. package/dist/docs-raw/generated/lib.md +227 -85
  37. package/dist/docs-raw/generated/output.md +35 -99
  38. package/dist/docs-raw/generated/runtime-names.md +23 -23
  39. package/dist/docs-raw/generated/sdf.md +68 -284
  40. package/dist/docs-raw/generated/sheet-metal.md +68 -335
  41. package/dist/docs-raw/generated/sketch.md +240 -1161
  42. package/dist/docs-raw/generated/viewport.md +75 -316
  43. package/dist/docs-raw/generated/wood.md +21 -49
  44. package/dist/docs-raw/guides/coordinate-system.md +4 -42
  45. package/dist/docs-raw/guides/inspection-bundles.md +44 -442
  46. package/dist/docs-raw/guides/joint-design.md +18 -79
  47. package/dist/docs-raw/guides/positioning.md +21 -143
  48. package/dist/docs-raw/guides/scene-presentation.md +89 -0
  49. package/dist/docs-raw/guides/simready-quickstart.md +171 -0
  50. package/dist/docs-raw/simulation-workflow.md +273 -0
  51. package/dist/docs-raw/skills/forgecad-3d-reconstruction.md +25 -111
  52. package/dist/docs-raw/skills/forgecad-blockout-model.md +20 -117
  53. package/dist/docs-raw/skills/forgecad-component-model.md +23 -107
  54. package/dist/docs-raw/skills/forgecad-high-level-spec.md +47 -155
  55. package/dist/docs-raw/skills/forgecad-image-replicator.md +26 -143
  56. package/dist/docs-raw/skills/forgecad-lld.md +19 -113
  57. package/dist/docs-raw/skills/forgecad-make-a-model.md +112 -532
  58. package/dist/docs-raw/skills/forgecad-model-grader.md +38 -108
  59. package/dist/docs-raw/skills/forgecad-prepare-prompt.md +24 -211
  60. package/dist/docs-raw/skills/forgecad-project.md +13 -131
  61. package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +42 -134
  62. package/dist/docs-raw/skills/forgecad-render-inspect.md +27 -174
  63. package/dist/docs-raw/skills/forgecad-visual-spec.md +32 -112
  64. package/dist/docs-raw/skills/forgecad.md +19 -18
  65. package/dist/docs-raw/skills/index.md +2 -0
  66. package/dist/docs-raw/welcome.md +2 -2
  67. package/dist/index.html +2 -2
  68. package/dist/llms.txt +1 -2
  69. package/dist/sitemap.xml +25 -13
  70. package/dist-cli/{check-compiler-SYQ2PWOB.js → check-compiler-II7NLPAB.js} +1 -1
  71. package/dist-cli/{check-query-propagation-HIAGV62W.js → check-query-propagation-7462TR3R.js} +1 -1
  72. package/dist-cli/{chunk-SPZE3DUY.js → chunk-UWTJCGXF.js} +5848 -2915
  73. package/dist-cli/forgecad.js +3496 -703
  74. package/dist-skill/CONTEXT.md +1797 -7963
  75. package/dist-skill/SKILL.md +15 -15
  76. package/dist-skill/docs/API/core/concepts.md +27 -157
  77. package/dist-skill/docs/CLI.md +77 -240
  78. package/dist-skill/docs/generated/assembly.md +182 -532
  79. package/dist-skill/docs/generated/core.md +283 -1250
  80. package/dist-skill/docs/generated/curves.md +387 -1609
  81. package/dist-skill/docs/generated/lib.md +227 -85
  82. package/dist-skill/docs/generated/output.md +35 -99
  83. package/dist-skill/docs/generated/runtime-names.md +16 -21
  84. package/dist-skill/docs/generated/sdf.md +68 -284
  85. package/dist-skill/docs/generated/sheet-metal.md +68 -335
  86. package/dist-skill/docs/generated/sketch.md +240 -1160
  87. package/dist-skill/docs/generated/viewport.md +75 -223
  88. package/dist-skill/docs/generated/wood.md +21 -49
  89. package/dist-skill/docs/guides/coordinate-system.md +4 -42
  90. package/dist-skill/docs/guides/inspection-bundles.md +44 -442
  91. package/dist-skill/docs/guides/joint-design.md +18 -79
  92. package/dist-skill/docs/guides/positioning.md +21 -143
  93. package/dist-skill/docs/guides/scene-presentation.md +89 -0
  94. package/dist-skill/docs/guides/surface-members.md +26 -0
  95. package/dist-skill/library/forgecad-3d-reconstruction/SKILL.md +23 -111
  96. package/dist-skill/library/forgecad-blockout-model/SKILL.md +18 -117
  97. package/dist-skill/library/forgecad-component-model/SKILL.md +21 -107
  98. package/dist-skill/library/forgecad-high-level-spec/SKILL.md +45 -155
  99. package/dist-skill/library/forgecad-image-replicator/SKILL.md +24 -143
  100. package/dist-skill/library/forgecad-lld/SKILL.md +17 -113
  101. package/dist-skill/library/forgecad-make-a-model/SKILL.md +110 -532
  102. package/dist-skill/library/forgecad-model-grader/SKILL.md +36 -108
  103. package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +35 -224
  104. package/dist-skill/library/forgecad-prepare-prompt/references/default-profiles.md +43 -271
  105. package/dist-skill/library/forgecad-prepare-prompt/references/master-prompt.md +30 -99
  106. package/dist-skill/library/forgecad-project/SKILL.md +13 -133
  107. package/dist-skill/library/forgecad-reconstruction-benchmark/SKILL.md +29 -123
  108. package/dist-skill/library/forgecad-render-inspect/SKILL.md +25 -174
  109. package/dist-skill/library/forgecad-visual-spec/SKILL.md +30 -111
  110. package/dist-skill/website/skills/forgecad-3d-reconstruction.md +58 -0
  111. package/dist-skill/website/skills/forgecad-blockout-model.md +49 -0
  112. package/dist-skill/website/skills/forgecad-component-model.md +53 -0
  113. package/dist-skill/website/skills/forgecad-high-level-spec.md +101 -0
  114. package/dist-skill/website/skills/forgecad-image-replicator.md +63 -0
  115. package/dist-skill/website/skills/forgecad-lld.md +41 -0
  116. package/dist-skill/website/skills/forgecad-make-a-model.md +186 -0
  117. package/dist-skill/website/skills/forgecad-model-grader.md +82 -0
  118. package/dist-skill/website/skills/forgecad-prepare-prompt.md +63 -0
  119. package/dist-skill/website/skills/forgecad-project.md +26 -0
  120. package/dist-skill/website/skills/forgecad-reconstruction-benchmark.md +60 -0
  121. package/dist-skill/website/skills/forgecad-render-inspect.md +80 -0
  122. package/dist-skill/website/skills/forgecad-visual-spec.md +71 -0
  123. package/dist-skill/website/skills/forgecad.md +122 -0
  124. package/dist-skill/website/skills/index.md +26 -0
  125. package/examples/api/comparison-imported-sphere-candidate.forge.js +1 -1
  126. package/examples/api/conformal-product-ribbon.forge.js +1 -1
  127. package/examples/api/exact-sheet-shell-assembly.forge.js +1 -1
  128. package/examples/api/extrude-options.forge.js +4 -2
  129. package/examples/api/field-loft-drive-tip.forge.js +40 -0
  130. package/examples/api/guided-loft-olive-oil-bottle.forge.js +1 -1
  131. package/examples/api/highlight-debug.forge.js +10 -10
  132. package/examples/api/mesh-import-slats.forge.js +1 -1
  133. package/examples/api/real-product-curves.forge.js +1 -1
  134. package/examples/api/sculpt-box-circle-booleans.forge.js +1 -1
  135. package/examples/api/sdf-shapes.forge.js +2 -5
  136. package/examples/api/sketch-rounding-strategies.forge.js +6 -6
  137. package/examples/api/surface-member-bottle-cage.forge.js +3 -3
  138. package/examples/api/surface-member-conformal-product-ribbon.forge.js +3 -3
  139. package/examples/api/surface-member-razor-inlay.forge.js +1 -1
  140. package/examples/api/variable-sweep-test.forge.js +3 -3
  141. package/examples/mechanical/airplane-propeller.forge.js +74 -39
  142. package/examples/nurbs-surface.forge.js +1 -1
  143. package/examples/products/iphone.forge.js +1 -1
  144. package/examples/robotics/README.md +46 -0
  145. package/examples/robotics/scout-cam-rover-simready/README.md +119 -0
  146. package/examples/robotics/scout-cam-rover-simready/lib/dims.js +140 -0
  147. package/examples/robotics/scout-cam-rover-simready/main.forge.js +343 -0
  148. package/examples/robotics/scout-cam-rover-simready/parts/body.forge.js +304 -0
  149. package/examples/robotics/scout-cam-rover-simready/parts/chassis.forge.js +320 -0
  150. package/examples/robotics/scout-cam-rover-simready/parts/hardware.forge.js +21 -0
  151. package/examples/robotics/scout-cam-rover-simready/parts/turret.forge.js +70 -0
  152. package/examples/robotics/scout-cam-rover-simready/parts/wheel.forge.js +116 -0
  153. package/examples/robotics/simready-asset-crate.forge.js +79 -0
  154. package/examples/robotics/simready-diff-drive-rover.forge.js +141 -0
  155. package/examples/robotics/simready-parallel-gripper.forge.js +102 -0
  156. package/package.json +1 -1
  157. package/dist/assets/EditorApp-BHMQlJ-D.js +0 -14686
  158. package/dist/docs-raw/guides/geometry-conventions.md +0 -52
  159. package/dist/docs-raw/guides/modeling-recipes.md +0 -78
  160. package/dist-skill/docs/guides/geometry-conventions.md +0 -52
  161. package/dist-skill/docs/guides/modeling-recipes.md +0 -78
  162. package/dist-skill/library/forgecad-visual-spec/references/prompt-template.md +0 -79
@@ -5,98 +5,90 @@ skill-order: 100
5
5
 
6
6
  # Assembly API
7
7
 
8
- Assembly-owned links, constraints, connectors, solved poses, and robot export.
8
+ Assembly-owned links, constraints, connectors, solved poses, and source-level simulation metadata.
9
9
 
10
10
  ## Contents
11
11
 
12
- - [Assembly & Joints](#assembly-joints) — `bomToCsv`, `assembly`
12
+ - [Assembly & Joints](#assembly-joints)
13
13
  - [Assembly](#assembly) — Kinematics, Structure, Connectors, References, Solving
14
14
  - [ImportedAssembly](#importedassembly)
15
15
  - [SolvedAssembly](#solvedassembly)
16
- - [MateBuilder](#matebuilder)
17
16
 
18
17
  ## Functions
19
18
 
20
19
  ### Assembly & Joints
21
20
 
22
- #### `bomToCsv()` — Convert an array of BOM rows into a CSV string.
21
+ #### `Sim.material(name: string, options?: SimMaterialOptions): SimMaterialDef` — Create a named physical material with density and contact coefficients for simulation export and checks.
23
22
 
24
- Produces a CSV with columns: `part`, `qty`, `material`, `process`, `tolerance`, `notes`. String values are quoted and internal double-quotes are escaped. Prefer calling `solvedAssembly.bomCsv()` directly — this function is exposed for custom BOM processing.
23
+ `SimMaterialOptions`: `{ densityKgM3?: number, staticFriction?: number, dynamicFriction?: number, restitution?: number }`
25
24
 
26
- ```ts
27
- bomToCsv(rows: BomRow[]): string
28
- ```
25
+ `SimMaterialDef`: `{ kind: "material", name: string }`
29
26
 
30
- **`BomRow`**: `part: string`, `qty: number`, `material?: string`, `process?: string`, `tolerance?: string`, `notes?: string`, `metadata?: PartMetadata`
27
+ #### `Sim.body(options: SimBodyOptions): SimBodyDef` Describe one assembly part as a physical body with mass/density, material, collider intent, and optional contact surfaces.
31
28
 
32
- **`PartMetadata`**
29
+ **`SimBodyOptions`**: `massKg?: number`, `densityKgM3?: number`, `material?: SimMaterialDef`, `collider?: SimColliderDef`, `contacts?: Record<string, SimContactDef>`
33
30
 
34
- | Option | Type | Description |
35
- |--------|------|-------------|
36
- | `tags?` | `string \| readonly string[]` | Viewport organization tags applied to scene objects produced from this part. |
37
- | `material?`, `process?`, `tolerance?`, `qty?`, `notes?`, `densityKgM3?`, `massKg?` | | — |
31
+ `SimColliderDef`: `{ kind: "collider", mode: SimColliderMode, reason?: string }`
38
32
 
39
- #### `assembly()` Create an assembly container with named parts, connectors, and kinematic links.
33
+ `SimContactDef`: `{ kind: "wheelSurface" | "gripperSurface", connectorName: string }`
40
34
 
41
- **Use this from iteration 1 for any model with moving parts.** Do not build one static pose and retrofit motion later.
35
+ `SimBodyDef`: `{ kind: "body" }`
42
36
 
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.
37
+ #### `Sim.collider` Collision-geometry intent constructors for physical parts.
44
38
 
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.
39
+ - `Sim.collider.convexHull(): SimColliderDef` Use a generated collision mesh for the part. This is the default fast rigid-body collider for irregular parts.
40
+ - `Sim.collider.boundingBox(): SimColliderDef` — Use the part bounding box as the collision geometry. This is fastest and works well for chassis and simple blocks.
41
+ - `Sim.collider.visualMesh(): SimColliderDef` — Use the visual mesh as collision geometry. This is exact but usually slower in physics engines.
42
+ - `Sim.collider.none(reason: string): SimColliderDef` — Disable collision for a part with an explicit reason, such as a sensor-only or decorative object.
46
43
 
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.
44
+ #### `Sim.drive` Joint-drive intent constructors for passive or powered assembly joints.
48
45
 
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.
46
+ - `Sim.drive.passive(options?: SimPassiveDriveOptions): SimDriveDef` Mark a joint as passive while preserving damping and friction metadata for simulation export.
47
+ - `Sim.drive.velocity(options: SimVelocityDriveOptions): SimDriveDef` — Mark a revolute joint as velocity-driven with torque and speed limits. Speed is authored in rpm and exported as deg/s or rad/s as needed.
50
48
 
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.
49
+ `SimPassiveDriveOptions`: `{ damping?: number, friction?: number }`
52
50
 
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.
51
+ `SimVelocityDriveOptions`: `{ maxTorqueNm: number, maxSpeedRpm: number }`
54
52
 
55
- **Point-link example**
53
+ #### `Sim.contact` — Contact-surface metadata over existing part connectors.
56
54
 
57
- This snippet mates a marker to the solved `tip` point. It does not orient a bar along `ground -> tip`.
55
+ - `Sim.contact.wheelSurface(connectorName: string): SimContactDef` — Mark a connector as the wheel tread contact surface for offline checks and downstream simulation metadata.
56
+ - `Sim.contact.gripperSurface(connectorName: string): SimContactDef` — Mark a connector as a gripper pad/contact surface for offline checks and downstream grasp-readiness metadata.
58
57
 
59
- ```ts
60
- const marker = box(8, 8, 4).withConnectors({
61
- center: connector({ origin: [0, 0, 0], axis: [0, 0, 1] }),
62
- });
58
+ #### `Sim.profile` — Named validation/export profile constructors.
63
59
 
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" } });
60
+ - `Sim.profile.robotBodyRunnable(): SimProfileDef` — SimReady-style profile for a robot body that should be runnable in a physics simulator.
61
+ - `Sim.profile.robotBodyIsaac(): SimProfileDef` SimReady-style profile for robot bodies targeting Isaac Sim readiness.
62
+ - `Sim.profile.roboticsAssetPhysx(): SimProfileDef` SimReady-style profile for robotics assets with PhysX-ready rigid bodies and colliders.
74
63
 
75
- return mech;
76
- ```
64
+ `SimProfileDef`: `{ kind: "profile", name: SimProfileName }`
77
65
 
78
- ```ts
79
- assembly(name?: string): Assembly
80
- ```
66
+ #### `Sim.controller` — Standard controller metadata constructors for simulator package generation.
81
67
 
82
- ---
68
+ - `Sim.controller.diffDrive(options: SimDiffDriveControllerOptions): SimDiffDriveControllerDef` — Describe a differential-drive controller from left/right wheel joints and wheel dimensions.
83
69
 
84
- ## Classes
70
+ **`SimDiffDriveControllerOptions`**: `leftJoints: string[]`, `rightJoints: string[]`, `wheelSeparationMm: number`, `wheelRadiusMm: number`, `topic?: string`, `odomTopic?: string`, `tfTopic?: string`, `frameId?: string`, `odomFrameId?: string`, `maxLinearVelocity?: number`, `maxAngularVelocity?: number`, `linearAcceleration?: number`, `angularAcceleration?: number`
85
71
 
86
- ### `Assembly`
72
+ `SimDiffDriveControllerDef`: `{ kind: "diffDrive" }`
73
+
74
+ #### `assembly(name?: string): Assembly` — Create an assembly container with named parts, connectors, and kinematic links.
87
75
 
88
- Container for a kinematic mechanism made up of links, relationships, and parts.
76
+ **Use this from iteration 1 for any model with moving parts.** Do not build one static pose and retrofit motion later.
77
+
78
+ Two motion tools:
89
79
 
90
- Assembly has two related but different motion tools:
80
+ - **Link-graph kinematics** (`link()`, `edgeBetweenLinks()`, `addAngleBetweenLinks()`) solve named point positions — a link is a point, not a rigid-body frame. Use when the hard part is solving positions, especially closed loops.
81
+ - **Connector-frame joints** (`connect()` / `match()`) align full connector frames (`origin`, `axis`, `up`) and derive joint frame + axis. Use for serial articulated parts whose orientation matters: hips, hinges, drums, sliders, wheels.
91
82
 
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.
83
+ `addPart(..., { mate })` places geometry on the solved link graph by **translation only**: one mate pins a connector origin to a link, two mates orient a part to span two solved links, a third pins roll. Right for markers and point-following geometry; use `connect()`/`match()` when the part needs a deterministic rest orientation.
94
84
 
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.
85
+ Return the `Assembly` itself to expose its joints and driven link controls in the editor; moving a control re-runs `solve(state)`, so closed loops move through the real solver instead of a viewport-only FK approximation.
96
86
 
97
- **Point-link quick start**
87
+ If no link in a connected kinematic component is fixed, ForgeCAD chooses a deterministic gauge link for solving and reports a floating-component warning.
98
88
 
99
- This attaches a marker to a solved point. It is intentionally not a bone or oriented part example.
89
+ A file that returns an `Assembly` is importable via [`require()`](/docs/core#require) and yields an `ImportedAssembly`; use `mergeInto()` to flatten it into a parent assembly.
90
+
91
+ **Point-link example** (mates a marker to the solved `tip` point; does not orient a bar along `ground -> tip`):
100
92
 
101
93
  ```ts
102
94
  const marker = box(8, 8, 4).withConnectors({
@@ -112,46 +104,20 @@ const mech = assembly("Linkage")
112
104
  name: "theta",
113
105
  control: { min: 0, max: 120, default: 30 },
114
106
  })
115
- .addPart("Tip marker", marker, {
116
- mate: { connector: "center", toLink: "tip" },
117
- });
107
+ .addPart("Tip marker", marker, { mate: { connector: "center", toLink: "tip" } });
118
108
 
119
109
  return mech;
120
110
  ```
121
111
 
122
- Returning an unsolved `Assembly` keeps the graph available to the runtime. Return a `SolvedAssembly` directly for a specific control state:
123
-
124
- ```ts
125
- return mech.solve({ theta: 60 });
126
- ```
112
+ ---
127
113
 
128
- **Frame-aware serial joint**
114
+ ## Classes
129
115
 
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
- });
116
+ ### `Assembly`
139
117
 
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
- });
118
+ Container for a kinematic mechanism made up of links, relationships, and parts. See `assembly` for the link-graph vs connector-frame decision rules.
148
119
 
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 });
154
- ```
120
+ Returning an unsolved `Assembly` keeps the graph available to the runtime; return `mech.solve({ theta: 60 })` for a fixed pose instead.
155
121
 
156
122
  **Return types**
157
123
 
@@ -168,64 +134,61 @@ return assembly("Leg")
168
134
 
169
135
  **Kinematics**
170
136
 
171
- #### `link()` — Add a named kinematic link to the assembly graph.
137
+ #### `link(name: string, options?: AssemblyLinkOptions): Assembly` — Add a named kinematic link to the assembly graph.
172
138
 
173
139
  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
140
 
175
141
  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
142
 
177
- ```ts
178
- link(name: string, options?: AssemblyLinkOptions): Assembly
179
- ```
180
-
181
143
  **`AssemblyLinkOptions`**
182
- - `at?: [ number, number, number ]` — Initial world-space position of this link before kinematic constraints solve it.
144
+ - `at?: Vec3` — Initial world-space position of this link before kinematic constraints solve it.
183
145
  - `fixed?: boolean` — Keep the link locked at its authored `at` position during solves.
184
146
  - `metadata?: Record<string, unknown>` — User metadata carried through the kinematic graph for inspection and tooling.
185
147
 
186
- #### `edgeBetweenLinks()` — Add a relationship edge between two kinematic links.
148
+ #### `linkAlong(name: string, fromLink: string, towardLink: string, distance: number): Assembly` — Create a derived link on the line through `fromLink` and `towardLink`, at a **signed** distance from `fromLink`.
187
149
 
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.
150
+ **Sign convention** (read this first):
151
+
152
+ - `distance > 0` — the point moves from `fromLink` **toward** `towardLink`.
153
+ - `distance < 0` — the point moves from `fromLink` **away from** `towardLink` (the coupler-extension case, e.g. the Chebyshev lambda linkage's trace point beyond the rocker joint).
154
+ - `distance` greater than the solved edge length places the point **beyond** `towardLink`, still on the same line.
155
+
156
+ Derived links are trace/reference points. They are recomputed after the primary link solve and cannot participate in structural edges or angle constraints. Because the distance is one signed parameter, a `param()`-driven value can sweep continuously from extension (negative) through `fromLink` (zero) to beyond `towardLink` (large positive).
189
157
 
190
158
  ```ts
191
- edgeBetweenLinks(a: string, b: string, options?: AssemblyEdgeBetweenLinksOptions): Assembly
159
+ // Chebyshev lambda linkage: trace point C3 extends beyond C2, away from C1.
160
+ mech.linkAlong('C3', 'C2', 'C1', -2.5 * a);
161
+ // Midpoint-style reference 30 mm from A toward B:
162
+ mech.linkAlong('probe', 'A', 'B', 30);
192
163
  ```
193
164
 
165
+ #### `edgeBetweenLinks(a: string, b: string, options?: AssemblyEdgeBetweenLinksOptions): Assembly` — Add a relationship edge between two kinematic links.
166
+
167
+ 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.
168
+
194
169
  **`AssemblyEdgeBetweenLinksOptions`**: `name?: string`, `length?: number | "lockCurrent" | "free"`, `min?: number`, `max?: number`, `visualOnly?: boolean`, `control?: AssemblyKinematicControlOptions`, `metadata?: Record<string, unknown>`
195
170
 
196
171
  `AssemblyKinematicControlOptions`: `{ min?: number, max?: number, default?: number, unit?: string }`
197
172
 
198
- #### `addAngleBetweenLinks()` — Add an angle relationship among three kinematic links.
173
+ #### `addAngleBetweenLinks(a: string, b: string, c: string, options?: AssemblyAngleBetweenLinksOptions): Assembly` — Add an angle relationship among three kinematic links.
199
174
 
200
175
  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
176
 
202
- ```ts
203
- addAngleBetweenLinks(a: string, b: string, c: string, options?: AssemblyAngleBetweenLinksOptions): Assembly
204
- ```
205
-
206
177
  **`AssemblyAngleBetweenLinksOptions`**: `name?: string`, `value?: number`, `min?: number`, `max?: number`, `control?: boolean | AssemblyKinematicControlOptions`, `limit?: AssemblyKinematicLimitOptions`, `metadata?: Record<string, unknown>`
207
178
 
208
179
  `AssemblyKinematicLimitOptions`: `{ min?: number, max?: number }`
209
180
 
210
- #### `addAngleBetweenLinkSegmentAndWorldDirection()` — Add an absolute angle relationship from a world direction to a link segment.
181
+ #### `addAngleBetweenLinkSegmentAndWorldDirection(fromLink: string, toLink: string, direction: Vec3, options?: AssemblyAngleBetweenLinksOptions): Assembly` — Add an absolute angle relationship from a world direction to a link segment.
211
182
 
212
183
  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
184
 
214
185
  Use `Points.polar(1, angleDeg)` when the reference direction is planar and angle-based instead of axis-aligned.
215
186
 
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
- ```
187
+ #### `describeKinematics(): AssemblyKinematicGraphDef` — Return the assembly-native kinematic graph definition.
225
188
 
226
189
  **Structure**
227
190
 
228
- #### `addPart()` — Add a named part to the assembly.
191
+ #### `addPart(name: string, part: AssemblyPart, options?: PartOptions): Assembly` — Add a named part to the assembly.
229
192
 
230
193
  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
194
 
@@ -241,144 +204,104 @@ const housing = group(
241
204
  assembly.addPart("Base Assembly", housing);
242
205
  ```
243
206
 
244
- ```ts
245
- addPart(name: string, part: AssemblyPart, options?: PartOptions): Assembly
246
- ```
207
+ **`PartOptions`**: `transform?: TransformInput`, `metadata?: PartMetadata`, `sim?: SimBodyDef`, `mate?: AssemblyPartMateInput | AssemblyPartMateInput[]`, `bindToFrame?: string`
208
+
209
+ **`PartMetadata`**
210
+
211
+ | Option | Type | Description |
212
+ |--------|------|-------------|
213
+ | `tags?` | `string \| readonly string[]` | Viewport organization tags applied to scene objects produced from this part. |
247
214
 
248
- **`PartOptions`**: `transform?: TransformInput`, `metadata?: PartMetadata`, `mate?: AssemblyPartMateInput | AssemblyPartMateInput[]`, `bindToFrame?: string`
215
+ Also: `material?: string`, `process?: string`, `tolerance?: string`, `qty?: number`, `notes?: string`, `densityKgM3?: number`, `massKg?: number`.
249
216
 
250
217
  **`AssemblyPartMateInput`**
251
218
  - `connector: string` — Name of a connector declared on the part (via `withConnectors()`).
252
219
  - `toLink: string` — Name of the link this connector's origin is pinned to.
253
220
  - `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
221
 
255
- #### `frame()` — Add a named rig frame to the assembly.
222
+ #### `frame(name: string, options: AssemblyFrameOptions): Assembly` — Add a named rig frame to the assembly.
256
223
 
257
224
  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
225
 
259
- ```ts
260
- frame(name: string, options: AssemblyFrameOptions): Assembly
261
- ```
226
+ `AssemblyFrameOptions`: `{ origin: Vec3, axis: Vec3, up: Vec3, fixed?: boolean, metadata?: Record<string, unknown> }`
262
227
 
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.
228
+ #### `fixedJoint(name: string, options: AssemblyFixedFrameJointOptions): Assembly` Rigidly attach a child rig frame to a parent rig frame.
266
229
 
267
230
  Fixed joints carry frame hierarchy but do not expose a Motion control.
268
231
 
269
- ```ts
270
- fixedJoint(name: string, options: AssemblyFixedFrameJointOptions): Assembly
271
- ```
272
-
273
232
  `AssemblyFixedFrameJointOptions`: `{ parent: string, child: string, metadata?: Record<string, unknown> }`
274
233
 
275
- #### `revoluteJoint()` — Add a revolute rig-frame joint.
234
+ #### `revoluteJoint(name: string, options: AssemblyMovingFrameJointOptions): Assembly` — Add a revolute rig-frame joint.
276
235
 
277
236
  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
237
 
279
- ```ts
280
- revoluteJoint(name: string, options: AssemblyMovingFrameJointOptions): Assembly
281
- ```
282
-
283
238
  **`AssemblyMovingFrameJointOptions`**: `parent: string`, `child: string`, `min?: number`, `max?: number`, `default?: number`, `unit?: string`, `control?: boolean`, `metadata?: Record<string, unknown>`
284
239
 
285
- #### `prismaticJoint()` — Add a prismatic rig-frame joint.
240
+ #### `prismaticJoint(name: string, options: AssemblyMovingFrameJointOptions): Assembly` — Add a prismatic rig-frame joint.
286
241
 
287
242
  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
243
 
289
- ```ts
290
- prismaticJoint(name: string, options: AssemblyMovingFrameJointOptions): Assembly
291
- ```
292
-
293
244
  **Connectors**
294
245
 
295
- #### `usedConnectorRefs()` — Connector refs (e.g. "PartName.connectorName") consumed by connect/match calls.
296
-
297
- ```ts
298
- get usedConnectorRefs(): ReadonlySet<string>
299
- ```
246
+ #### `get usedConnectorRefs(): ReadonlySet<string>` — Connector refs (e.g. "PartName.connectorName") consumed by connect/match calls.
300
247
 
301
- #### `withConnectors()` — Attach named connectors to a specific part or the assembly as a whole.
248
+ #### `withConnectors(partName: string, connectors: Record<string, ConnectorInput>): Assembly` — Attach named connectors to a specific part or the assembly as a whole.
302
249
 
303
250
  Connectors declared this way are in the part's local coordinate system. They are captured automatically if the incoming [`Shape`](/docs/core#shape) already has connectors via `shape.withConnectors(...)`, but you can also add or override connectors after the fact with this method.
304
251
 
305
252
  Use the single-argument overload to attach assembly-level connectors — these are exposed when this assembly is imported as a sub-assembly.
306
253
 
307
- ```ts
308
- withConnectors(partName: string, connectors: Record<string, ConnectorInput>): Assembly
309
- ```
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> }`
254
+ `ConnectorInput` — defined in [core](/docs/core).
314
255
 
315
- #### `getConnectors()` — Get connectors declared on a part in part-local space.
256
+ #### `getConnectors(partName: string): ConnectorMap` — Get connectors declared on a part in part-local space.
316
257
 
317
- ```ts
318
- getConnectors(partName: string): ConnectorMap
319
- ```
320
-
321
- #### `getConnector()` — Parse a "PartName.connectorName" reference and return the resolved connector. Throws descriptive errors if the part or connector doesn't exist.
322
-
323
- ```ts
324
- getConnector(ref: string): { partName: string; connectorName: string; connector: ConnectorDef; }
325
- ```
326
-
327
- #### `connect()` — Connect two parts by aligning their declared connectors, automatically computing frame and axis.
328
-
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.
258
+ #### `getConnector(ref: string): { partName: string; connectorName: string; connector: ConnectorDef; }` — Parse a "PartName.connectorName" reference and return the resolved connector. Throws descriptive errors if the part or connector doesn't exist.
330
259
 
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.
260
+ #### `connect(parentConnectorRef: string, childConnectorRef: string, options?: ConnectOptions): Assembly` Connect two parts by aligning their declared connectors, automatically computing frame and axis.
332
261
 
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()`.
262
+ Connector refs use `"PartName.connectorName"`. The child connector origin lands exactly on the parent connector origin; joint frame and axis are derived from the connector geometry no manual `frame`/`axis` math.
334
263
 
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.
264
+ Frame semantics: `origin` is the pivot/contact point, `axis` the hinge or slide direction, `up` locks the part's zero-state twist. Omitted `up` gets a deterministic perpendicular provide `up` whenever rest orientation matters. (`addPart(..., { mate })` translates only; see `addPart`.)
336
265
 
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.
266
+ **Face-to-face:** each connector's axis points outward from its part; mating makes the axes anti-parallel, like a plug meeting a socket (same convention as `matchTo()`).
338
267
 
339
- The joint type is inferred from the connector's `kind` field if not specified in `options`.
268
+ **Revolute sign:** a positive joint value follows the right-hand rule about the **child** connector's placed axis. Because face-to-face mating makes the axes anti-parallel, that is the *left*-hand rule about the parent connector's outward axis — if `+30` swings the opposite way you expected, you predicted from the parent's axis. `forgecad debug assembly` prints each joint's resolved world axis.
340
269
 
341
- When connectors are defined with `start`/`end`, you can control which point on each connector meets via `align` / `parentAlign` / `childAlign` (`'start'`, `'middle'`, `'end'`).
270
+ **Mirrored revolute axes:** because of the right-hand rule, a mirrored hinge axis (`[1, 0, 0]` vs `[-1, 0, 0]`) rotates oppositely for the same `+theta`: negate the mirrored side's value and mirror limits as `[min, max] -> [-max, -min]`. Prismatic joints have no handedness flip. Use an explicit per-side sign mapping (or side-neutral link controls) for bilateral mechanisms.
342
271
 
343
- Use `connect()` when connector origins must physically coincide (flange-to-flange, bolt-into-bore).
272
+ Joint type defaults to the connector's `kind`. For `start`/`end` connectors, `align` / `parentAlign` / `childAlign` (`'start' | 'middle' | 'end'`) choose which point meets.
344
273
 
345
274
  ```ts
346
- // Hinge: both axes point outward along the hinge line
347
275
  const frame = box(100, 10, 80).withConnectors({
348
- hinge: connector("hinge", {
349
- origin: [0, 0, 40],
350
- axis: [0, 0, 1],
351
- up: [1, 0, 0],
352
- }),
276
+ hinge: connector("hinge", { origin: [0, 0, 40], axis: [0, 0, 1], up: [1, 0, 0] }),
353
277
  });
354
278
  const door = box(60, 4, 80).withConnectors({
355
- hinge: connector("hinge", {
356
- origin: [0, 0, 40],
357
- axis: [0, 0, -1],
358
- up: [1, 0, 0],
359
- }),
279
+ hinge: connector("hinge", { origin: [0, 0, 40], axis: [0, 0, -1], up: [1, 0, 0] }),
360
280
  });
361
- assembly("Door")
362
- .addPart("Frame", frame)
363
- .addPart("Door", door)
281
+ assembly("Door").addPart("Frame", frame).addPart("Door", door)
364
282
  .connect("Frame.hinge", "Door.hinge", { as: "swing", min: 0, max: 110 });
365
283
  ```
366
284
 
367
- ```ts
368
- connect(parentConnectorRef: string, childConnectorRef: string, options?: ConnectOptions): Assembly
369
- ```
370
-
371
285
  **`ConnectOptions`**
372
286
 
373
287
  | Option | Type | Description |
374
288
  |--------|------|-------------|
289
+ | `min?` | `number` | Lower joint-slider limit; solve clamps to it with a warning. Not a physical stop — enforce real travel limits with stop geometry. |
290
+ | `max?` | `number` | Upper joint-slider limit; same semantics as `min`. |
375
291
  | `flip?` | `boolean` | This parameter is ignored. If your connectors produce wrong orientation, fix the connector axis directions instead of using flip. |
376
292
  | `parentAlign?` | `PortAlign` | Which point on the parent connector to align: 'start', 'middle' (default), or 'end'. |
377
293
  | `childAlign?` | `PortAlign` | Which point on the child connector to align: 'start', 'middle' (default), or 'end'. |
378
294
  | `align?` | `PortAlign` | Shorthand: set both parentAlign and childAlign at once. |
379
- | `as?`, `type?`, `min?`, `max?`, `default?`, `unit?`, `effort?`, `velocity?`, `damping?`, `friction?` | | |
295
+ | `follows?` | `JointFollowOptions` | Slave this joint to another joint: `value = ratio × source + offset` (e.g. a mirrored jaw with `ratio: -1`). |
296
+
297
+ Also: `as?: string`, `type?: JointType`, `default?: number`, `unit?: string`, `effort?: number`, `velocity?: number`, `damping?: number`, `friction?: number`, `drive?: SimDriveDef`.
298
+
299
+ **`JointFollowOptions`**
300
+ - `joint: string` — Name of the source joint that drives this one.
301
+ - `ratio?: number` — Multiplier applied to the source joint value (default 1).
302
+ - `offset?: number` — Constant added after the ratio (default 0).
380
303
 
381
- #### `match()` — Auto-create a joint by matching typed connectors between two parts.
304
+ #### `match(childPartName: string, parentPartName: string, pairs: Record<string, string>, options?: MatchToOptions & { as?: string; }): Assembly` — Auto-create a joint by matching typed connectors between two parts.
382
305
 
383
306
  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.
384
307
 
@@ -403,29 +326,17 @@ const mech = assembly("Door")
403
326
  // Matching connectors computes the placement relationship automatically.
404
327
  ```
405
328
 
406
- ```ts
407
- match(childPartName: string, parentPartName: string, pairs: Record<string, string>, options?: MatchToOptions & { as?: string; }): Assembly
408
- ```
409
-
410
- `MatchToOptions`: `{ force?: boolean, angle?: number, distance?: number }`
329
+ `MatchToOptions` — defined in [core](/docs/core).
411
330
 
412
331
  **References**
413
332
 
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.
415
-
416
- ```ts
417
- withReferences(refs: Pick<PlacementReferenceInput, "points">): Assembly
418
- ```
419
-
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 }`
333
+ #### `withReferences(refs: Pick<PlacementReferenceInput, "points">): Assembly` — 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.
423
334
 
424
- `PlacementSurfaceRef`: `{ center: Vec3, normal: Vec3 }`
335
+ `PlacementReferenceInput` defined in [core](/docs/core).
425
336
 
426
337
  **Solving**
427
338
 
428
- #### `solve()` — Solve the assembly at the given control state and return positioned parts.
339
+ #### `solve(state?: JointState): SolvedAssembly` — Solve the assembly at the given control state and return positioned parts.
429
340
 
430
341
  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.
431
342
 
@@ -441,62 +352,66 @@ Connector-frame joints created by `connect()` / `match()` are also evaluated; th
441
352
  return mech.solve({ theta: 45 });
442
353
  ```
443
354
 
444
- ```ts
445
- solve(state?: JointState): SolvedAssembly
446
- ```
447
-
448
355
  **Other**
449
356
 
450
- #### `mate()` — Register mate constraints between parts. Constraints are solved during `solve()` to derive part positions and explode hints. Part references use "partName:featureName" format.
357
+ #### `withSimulation(options: SimAssemblySimulationOptions): Assembly` — Attach the root simulation contract for this assembly.
451
358
 
452
- ```ts
453
- mate(fn: (m: MateBuilder) => void): Assembly
454
- ```
359
+ Use this after adding physical parts and joints. Robot-body profiles require `rootPart`; asset profiles can describe one-part or multi-part physical assets. URDF/SDF exporters and `forgecad check simready` read this contract directly, so model files no longer need a separate `robotExport(...)` side effect.
455
360
 
456
- #### `edgeBetweenFrames()` Add a visual skeleton edge between two rig frame origins.
361
+ `SimAssemblySimulationOptions`: `{ profile: SimProfileDef, rootPart?: string, controllers?: SimControllerDef[] }`
457
362
 
458
- 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.
363
+ #### `edgeBetweenFrames(a: string, b: string, options?: AssemblyFrameEdgeOptions): Assembly` Add a visual skeleton edge between two rig frame origins.
459
364
 
460
- ```ts
461
- edgeBetweenFrames(a: string, b: string, options?: AssemblyFrameEdgeOptions): Assembly
462
- ```
365
+ 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.
463
366
 
464
367
  `AssemblyFrameEdgeOptions`: `{ name?: string, metadata?: Record<string, unknown> }`
465
368
 
466
- #### `linkToward()` — Create a derived link at a fixed distance from `fromLink` toward `towardLink`.
369
+ #### `addAnimation(name: string, options: AssemblyAnimationOptions): Assembly` — Register a named keyframe animation for this assembly's Motion view.
467
370
 
468
- Derived links are trace/reference points. They are recomputed after the primary link solve and cannot participate in structural edges or angle constraints.
371
+ Works with the returned-assembly controls path: return the unsolved `Assembly` and the animation appears in the Motion tab alongside the solver-backed joint controls. Keyframes hold control values by joint name; joints declared with `follows` are derived automatically and must not appear in keyframes.
469
372
 
470
373
  ```ts
471
- linkToward(name: string, fromLink: string, towardLink: string, distance: number): Assembly
374
+ robot.addAnimation("Pick and place", {
375
+ duration: 12,
376
+ loop: true,
377
+ keyframes: [
378
+ { values: { J1: 0, J2: -90 } },
379
+ { values: { J1: 45, J2: -30 } },
380
+ { values: { J1: 0, J2: -90 } },
381
+ ],
382
+ });
383
+ return robot;
472
384
  ```
473
385
 
474
- #### `linkAwayFrom()` — Create a derived link at a fixed distance from `fromLink` away from `awayFromLink`.
386
+ **`AssemblyAnimationOptions`**
475
387
 
476
- Use this for coupler trace/extension points such as the Chebyshev lambda linkage's point beyond the rocker joint.
388
+ | Option | Type | Description |
389
+ |--------|------|-------------|
390
+ | `duration?` | `number` | Animation length in seconds (default chosen by the viewer). |
391
+ | `loop?` | `boolean` | Loop the animation (default false). |
392
+ | `continuous?` | `boolean` | Interpolate continuously through keyframes instead of pausing on each. |
393
+ | `keyframes` | `JointViewAnimationInput["keyframes"]` | Keyframes of control values by joint/control name. `at` (0..1) or `ticks` control timing. |
394
+ | `default?` | `boolean` | Make this the animation that plays when the model loads. |
477
395
 
478
- ```ts
479
- linkAwayFrom(name: string, fromLink: string, awayFromLink: string, distance: number): Assembly
480
- ```
396
+ `JointViewAnimationInput`: `{ name: string, duration?: number, loop?: boolean, continuous?: boolean, keyframes: JointViewAnimationKeyframeInput[] }`
481
397
 
482
- #### `describe()` — Return the serializable assembly definition used by solve/inspect pipelines.
398
+ **`JointViewAnimationKeyframeInput`**
399
+ - `at?: number` — Timeline position [0, 1]. If omitted from ALL keyframes, positions are auto-computed from tick weights.
400
+ - `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.
401
+ - Also: `values: Record<string, number>`.
483
402
 
484
- ```ts
485
- describe(): AssemblyDefinition
486
- ```
403
+ #### `describe(): AssemblyDefinition` — Return the serializable assembly definition used by solve/inspect pipelines.
487
404
 
488
405
  **Compatibility Aliases**
489
406
 
490
- - `usedPortRefs` -> `usedConnectorRefs`
491
407
  - `withPorts()` -> `withConnectors()`
492
408
  - `getPorts()` -> `getConnectors()`
493
- - `getPort()` -> `getConnector()`
494
409
 
495
410
  ### `ImportedAssembly`
496
411
 
497
412
  A wrapper around an imported `Assembly` that provides kinematic access and convenient transform helpers.
498
413
 
499
- 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.
414
+ 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()` — and converts to a static [`ShapeGroup`](/docs/core#shapegroup) via the explicit `toGroup(state?)` boundary when group-style transforms are needed.
500
415
 
501
416
  **Kinematic access**
502
417
 
@@ -504,14 +419,14 @@ When a `.forge.js` file returns an unsolved `Assembly`, [`require()`](/docs/core
504
419
  const arm = require("./arm.forge.js");
505
420
 
506
421
  const solved = arm.solve({ shoulder: 45 }); // full kinematic solve
507
- const link = arm.part("Link", { shoulder: 60 }); // single part at state
422
+ const link = arm.getPart("Link", { shoulder: 60 }); // single part at state
508
423
  const group = arm.toGroup({ shoulder: 45 }); // only when ShapeGroup behavior is needed
509
424
  ```
510
425
 
511
- **Convenience transforms** (auto-solve at defaults, return [`ShapeGroup`](/docs/core#shapegroup)):
426
+ **Static positioning** convert explicitly, then transform the group (`toGroup()` solves at default joint values and discards kinematics):
512
427
 
513
428
  ```ts
514
- const positioned = arm.rotateZ(-90).translate(0, -20, 50);
429
+ const positioned = arm.toGroup().rotateZ(-90).translate(0, -20, 50);
515
430
  ```
516
431
 
517
432
  **Merging into a parent**
@@ -525,154 +440,43 @@ require("./arm.forge.js").mergeInto(robot, {
525
440
  });
526
441
  ```
527
442
 
528
- #### `assembly()` — The underlying Assembly, for advanced composition and inspection.
529
-
530
- ```ts
531
- get assembly(): Assembly
532
- ```
533
-
534
- #### `solve()` — Solve the assembly at the given joint state (defaults to each joint's default value).
535
-
536
- ```ts
537
- solve(state?: JointState): SolvedAssembly
538
- ```
539
-
540
- #### `part()` — Return a specific named part positioned at the given joint state, with any stored placement offset applied.
541
-
542
- ```ts
543
- part(name: string, state?: JointState): AssemblyPart
544
- ```
545
-
546
- #### `getPart()` — Return a specific named part positioned at the default solved pose.
547
-
548
- This mirrors `SolvedAssembly.getPart()` for imported assemblies. Use `solve(state).getPart(name)` when inspecting a non-default joint state.
549
-
550
- ```ts
551
- getPart(partName: string): AssemblyPart
552
- ```
553
-
554
- #### `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.
555
-
556
- ```ts
557
- toGroup(state?: JointState): ShapeGroup
558
- ```
559
-
560
- #### `withReferences()` — Attach named placement reference points to this assembly. Points are simple 3D coordinates (relative to the assembly's own origin). Returns a new ImportedAssembly — does not mutate.
561
-
562
- ```ts
563
- withReferences(refs: Pick<PlacementReferenceInput, "points">): ImportedAssembly
564
- ```
565
-
566
- #### `referenceNames()` — List all attached placement reference names.
567
-
568
- ```ts
569
- referenceNames(kind?: PlacementReferenceKind): string[]
570
- ```
571
-
572
- #### `placeReference()` — Translate the assembly so the named reference point lands on `target`. Returns a new ImportedAssembly — does not mutate. All point refs are translated by the same delta.
573
-
574
- ```ts
575
- placeReference(ref: string, target: [ number, number, number ], offset?: [ number, number, number ]): ImportedAssembly
576
- ```
577
-
578
- #### `translate()` — Solve at defaults and return a translated ShapeGroup.
579
-
580
- ```ts
581
- translate(x: number, y: number, z: number): ShapeGroup
582
- ```
583
-
584
- #### `rotate()` — Solve at defaults and return a rotated ShapeGroup.
585
-
586
- ```ts
587
- rotate(axis: [ number, number, number ], angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
588
- ```
589
-
590
- #### `rotateX()` — Solve at defaults and return a ShapeGroup rotated around X.
591
-
592
- ```ts
593
- rotateX(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
594
- ```
595
-
596
- #### `rotateY()` — Solve at defaults and return a ShapeGroup rotated around Y.
597
-
598
- ```ts
599
- rotateY(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
600
- ```
601
-
602
- #### `rotateZ()` — Solve at defaults and return a ShapeGroup rotated around Z.
603
-
604
- ```ts
605
- rotateZ(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
606
- ```
443
+ #### `get assembly(): Assembly` — The underlying Assembly, for advanced composition and inspection.
607
444
 
608
- #### `scale()` — Solve at defaults and return a scaled ShapeGroup.
445
+ #### `solve(state?: JointState): SolvedAssembly` — Solve the assembly at the given joint state (defaults to each joint's default value).
609
446
 
610
- ```ts
611
- scale(v: number | [ number, number, number ]): ShapeGroup
612
- ```
447
+ #### `getPart(partName: string, state?: JointState): AssemblyPart` — Return a specific named part positioned at the solved pose, with any stored placement offset applied.
613
448
 
614
- #### `mirror()` Solve at defaults and return a mirrored ShapeGroup.
449
+ This mirrors `SolvedAssembly.getPart()` for imported assemblies, with one addition: any offset stored by `placeReference()` is applied, so the part lands where the imported assembly was placed. (`solve(state).getPart(name)` returns the part in the assembly's own coordinates, without that offset.)
615
450
 
616
- ```ts
617
- mirror(normal: [ number, number, number ]): ShapeGroup
618
- ```
451
+ #### `toGroup(state?: JointState): ShapeGroup` — 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.
619
452
 
620
- #### `color()` — Solve at defaults and return a colored ShapeGroup.
453
+ #### `withReferences(refs: Pick<PlacementReferenceInput, "points">): ImportedAssembly` — Attach named placement reference points to this assembly. Points are simple 3D coordinates (relative to the assembly's own origin). Returns a new ImportedAssembly — does not mutate.
621
454
 
622
- ```ts
623
- color(hex: string): ShapeGroup
624
- ```
455
+ #### `referenceNames(kind?: PlacementReferenceKind): string[]` — List all attached placement reference names.
625
456
 
626
- #### `child()` — Solve at defaults, get a named child part from the resulting group.
457
+ #### `placeReference(ref: string, target: Vec3, offset?: Vec3): ImportedAssembly` — Translate the assembly so the named reference point lands on `target`. Returns a new ImportedAssembly — does not mutate. All point refs are translated by the same delta.
627
458
 
628
- ```ts
629
- child(name: string): Shape | Sketch | ShapeGroup
630
- ```
459
+ #### `child(name: string): Shape | Sketch | ShapeGroup` — Solve at defaults, get a named child part from the resulting group.
631
460
 
632
- #### `collisionReport()` — Detect overlapping part pairs at the default solved pose.
461
+ #### `collisionReport(options?: CollisionOptions): CollisionFinding[]` — Detect overlapping part pairs at the default solved pose.
633
462
 
634
463
  This mirrors `SolvedAssembly.collisionReport()` for imported assemblies. Use `solve(state).collisionReport(options)` when inspecting a non-default joint state.
635
464
 
636
- ```ts
637
- collisionReport(options?: CollisionOptions): CollisionFinding[]
638
- ```
639
-
640
465
  `CollisionOptions`: `{ parts?: string[], ignorePairs?: Array<[ string, string ]>, minOverlapVolume?: number }`
641
466
 
642
- #### `minClearance()` — Compute the minimum gap between two parts at the default solved pose.
467
+ #### `minClearance(partA: string, partB: string, searchLength?: number): number` — Compute the minimum gap between two parts at the default solved pose.
643
468
 
644
469
  This mirrors `SolvedAssembly.minClearance()` for imported assemblies. Use `solve(state).minClearance(partA, partB, searchLength)` when inspecting a non-default joint state.
645
470
 
646
- ```ts
647
- minClearance(partA: string, partB: string, searchLength?: number): number
648
- ```
649
-
650
- #### `mergeInto()` — Flatten this sub-assembly's parts and relationships into `parent` and wire a mount relationship.
471
+ #### `mergeInto(parent: Assembly, options: MergeIntoOptions): Assembly` — Flatten this sub-assembly's parts and relationships into `parent` and wire a mount relationship.
651
472
 
652
- 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:
473
+ All part, link, and legacy joint names from the sub-assembly are prefixed with `"${options.prefix}."` to avoid collisions; connectors are forwarded with the same prefix. After the merge, drive controls from the parent using the prefixed names:
653
474
 
654
475
  ```ts
655
476
  parent.solve({ "Left Arm.theta": 45, "Right Arm.theta": -20 })
656
477
  ```
657
478
 
658
- Connectors from sub-assembly parts are forwarded with the prefix.
659
-
660
- The sub-assembly must have exactly one root part before it can be merged.
661
-
662
- ```ts
663
- const robot = assembly("Robot").addPart("Chassis", chassis);
664
-
665
- require("./arm.forge.js").mergeInto(robot, {
666
- prefix: "Left Arm",
667
- mountParent: "Chassis",
668
- mountJoint: "leftMount",
669
- mountOptions: { frame: Transform.identity().translate(-70, 0, 10) },
670
- });
671
- ```
672
-
673
- ```ts
674
- mergeInto(parent: Assembly, options: MergeIntoOptions): Assembly
675
- ```
479
+ The sub-assembly must have exactly one root part before it can be merged (collapse multiple roots with `addFixed()` first). See the `ImportedAssembly` class docs for a full merge example.
676
480
 
677
481
  **`MergeIntoOptions`**
678
482
 
@@ -689,7 +493,9 @@ mergeInto(parent: Assembly, options: MergeIntoOptions): Assembly
689
493
  | Option | Type | Description |
690
494
  |--------|------|-------------|
691
495
  | `connectorRefs?` | `JointConnectorRefs` | Connector refs that define this joint contract. Usually set by `connect()` / `match()`. |
692
- | `frame?`, `origin?`, `axis?`, `min?`, `max?`, `default?`, `unit?`, `effort?`, `velocity?`, `damping?`, `friction?` | | |
496
+ | `follows?` | `JointFollowOptions` | Slave this joint to another joint: `value = ratio × source + offset`. Use for mechanisms with one physical DOF expressed through several joints — a mirrored gripper jaw (`ratio: -1`), a gear pair, a drive crank turning with its servo. A followed joint stops being an independent control: the Motion view drives it from its source, `solve()` derives its value (a direct state override is ignored with a warning), and limits still clamp the derived value. |
497
+
498
+ Also: `frame?: TransformInput`, `origin?: Vec3`, `axis?: Vec3`, `min?: number`, `max?: number`, `default?: number`, `unit?: string`, `effort?: number`, `velocity?: number`, `damping?: number`, `friction?: number`, `drive?: SimDriveDef`.
693
499
 
694
500
  `JointConnectorRefs`: `{ parent: string, child: string, parentAlign?: PortAlign, childAlign?: PortAlign }`
695
501
 
@@ -717,73 +523,23 @@ return solved;
717
523
 
718
524
  **Methods:**
719
525
 
720
- #### `warnings()` — Return any warnings generated during solve (clamped joints, unconverged mates, etc.).
721
-
722
- ```ts
723
- warnings(): string[]
724
- ```
725
-
726
- #### `getJointState()` — Return a snapshot of resolved joint values (after clamping and coupling).
727
-
728
- ```ts
729
- getJointState(): JointState
730
- ```
731
-
732
- #### `mateExplodeHints()` — Explode direction hints derived from mate constraints, or null if no mates.
733
-
734
- ```ts
735
- get mateExplodeHints(): Record<string, { direction: Vec3; }> | null
736
- ```
737
-
738
- #### `mateDof()` — Remaining degrees of freedom after mate constraints, or null if no mates.
739
-
740
- ```ts
741
- get mateDof(): number | null
742
- ```
743
-
744
- #### `mateConverged()` — Whether the mate constraint solver converged, or null if no mates.
745
-
746
- ```ts
747
- get mateConverged(): boolean | null
748
- ```
749
-
750
- #### `kinematics()` — Solved assembly-native kinematic or frame-edge overlay data, or null when no rig overlay data was declared.
526
+ #### `warnings(): string[]` — Return any warnings generated during solve (clamped joints, unconverged mates, etc.).
751
527
 
752
- ```ts
753
- get kinematics(): SolvedAssemblyKinematics | null
754
- ```
755
-
756
- #### `getLinkPosition()` — Return the solved world position of a kinematic link.
757
-
758
- ```ts
759
- getLinkPosition(linkName: string): Vec3
760
- ```
761
-
762
- #### `getFrame()` — Return the solved world transform for a named rig frame.
763
-
764
- ```ts
765
- getFrame(frameName: string): Transform
766
- ```
528
+ #### `getJointState(): JointState` — Return a snapshot of resolved joint values (after clamping and coupling).
767
529
 
768
- #### `frames()` — Return solved rig frames, including origin, axis, up, and transform.
530
+ #### `get kinematics(): SolvedAssemblyKinematics | null` — Solved assembly-native kinematic or frame-edge overlay data, or null when no rig overlay data was declared.
769
531
 
770
- ```ts
771
- get frames(): SolvedAssemblyFrameDef[]
772
- ```
532
+ #### `getLinkPosition(linkName: string): Vec3` — Return the solved world position of a kinematic link.
773
533
 
774
- #### `getTransform()` — Return the world-space [`Transform`](/docs/core#transform) for the named part at the solved pose.
534
+ #### `getFrame(frameName: string): Transform` — Return the solved world transform for a named rig frame.
775
535
 
776
- ```ts
777
- getTransform(partName: string): Transform
778
- ```
536
+ #### `get frames(): SolvedAssemblyFrameDef[]` — Return solved rig frames, including origin, axis, up, and transform.
779
537
 
780
- #### `getPart()` — Return the named part already positioned at its solved world transform.
538
+ #### `getTransform(partName: string): Transform` — Return the world-space [`Transform`](/docs/core#transform) for the named part at the solved pose.
781
539
 
782
- ```ts
783
- getPart(partName: string): AssemblyPart
784
- ```
540
+ #### `getPart(partName: string): AssemblyPart` — Return the named part already positioned at its solved world transform.
785
541
 
786
- #### `toGroup()` — Convert all solved parts into a [`ShapeGroup`](/docs/core#shapegroup) with named children.
542
+ #### `toGroup(): ShapeGroup` — Convert all solved parts into a [`ShapeGroup`](/docs/core#shapegroup) with named children.
787
543
 
788
544
  Each part becomes a named child in the group, already positioned at its solved world transform. Use this only when you specifically need a [`ShapeGroup`](/docs/core#shapegroup) for composition, [`ShapeGroup`](/docs/core#shapegroup) transforms, or named-child access. Top-level scripts can return the `SolvedAssembly` directly; do not call `toGroup()` just to make a solved assembly render.
789
545
 
@@ -792,37 +548,15 @@ const armGroup = mech.solve({ shoulder: 60 }).toGroup(); // only because we need
792
548
  return armGroup.rotateZ(90);
793
549
  ```
794
550
 
795
- ```ts
796
- toGroup(): ShapeGroup
797
- ```
798
-
799
- #### `toSceneObjects()` — Return an array of named scene objects for the viewport renderer.
551
+ #### `toSceneObjects(): Array<{ ... }>` — Return an array of named scene objects for the viewport renderer.
800
552
 
801
553
  Each part becomes `{ name, shape }` or `{ name, group: [...] }` if the part is a [`ShapeGroup`](/docs/core#shapegroup). Top-level scripts should normally return the `SolvedAssembly` directly. Use `toGroup()` when you need [`ShapeGroup`](/docs/core#shapegroup) behavior; use this method only for advanced scene-graph control where you need access to the flat per-part array with metadata.
802
554
 
803
- ```ts
804
- toSceneObjects(): Array<{ name: string; shape?: Shape; group?: Array<{ name: string; shape: Shape; tags?: string[]; }>; metadata?: PartMetadata; }>
805
- ```
806
-
807
- #### `toScene()` — Backward-compatible alias for `toSceneObjects()`.
808
-
809
- ```ts
810
- toScene(): Array<{ name: string; shape?: Shape; group?: Array<{ name: string; shape: Shape; tags?: string[]; }>; metadata?: PartMetadata; }>
811
- ```
812
-
813
- #### [`bom()`](/docs/output#bom) — Generate a bill of materials for all parts in the solved assembly.
814
-
815
- ```ts
816
- bom(): BomRow[]
817
- ```
818
-
819
- #### `bomCsv()` — Generate a bill of materials as a CSV string.
555
+ #### `bom(): BomRow[]` — Generate a bill of materials for all parts in the solved assembly.
820
556
 
821
- ```ts
822
- bomCsv(): string
823
- ```
557
+ #### `bomCsv(): string` — Generate a bill of materials as a CSV string.
824
558
 
825
- #### `collisionReport()` — Detect overlapping (colliding) part pairs in this solved pose.
559
+ #### `collisionReport(options?: CollisionOptions): CollisionFinding[]` — Detect overlapping (colliding) part pairs in this solved pose.
826
560
 
827
561
  Computes boolean intersections between all part pairs and returns findings where the overlap volume exceeds `minOverlapVolume` (default 0.1 mm³).
828
562
 
@@ -831,90 +565,6 @@ const solved = mech.solve({ shoulder: 35, elbow: 60 });
831
565
  console.log("Collisions", solved.collisionReport());
832
566
  ```
833
567
 
834
- ```ts
835
- collisionReport(options?: CollisionOptions): CollisionFinding[]
836
- ```
837
-
838
- #### `minClearance()` — Compute the minimum gap (clearance) between two parts in this solved pose.
568
+ #### `minClearance(partA: string, partB: string, searchLength?: number): number` — Compute the minimum gap (clearance) between two parts in this solved pose.
839
569
 
840
570
  Returns `0` if the parts are touching or overlapping. Requires the Manifold backend. `searchLength` bounds the search radius in mm — increase it for widely separated parts.
841
-
842
- ```ts
843
- minClearance(partA: string, partB: string, searchLength?: number): number
844
- ```
845
-
846
- ### `MateBuilder`
847
-
848
- **Properties:**
849
-
850
- | Property | Type | Description |
851
- |----------|------|-------------|
852
- | `constraints` | `Constraint3D[]` | — |
853
-
854
- **Methods:**
855
-
856
- #### `flush()` — Constrain two faces so they stay flush.
857
-
858
- ```ts
859
- flush(faceA: string, faceB: string): string
860
- ```
861
-
862
- #### `align()` — Constrain two faces so their normals align.
863
-
864
- ```ts
865
- align(faceA: string, faceB: string): string
866
- ```
867
-
868
- #### `parallel()` — Constrain two faces so they remain parallel.
869
-
870
- ```ts
871
- parallel(faceA: string, faceB: string): string
872
- ```
873
-
874
- #### `faceDistance()` — Constrain the distance between two faces.
875
-
876
- ```ts
877
- faceDistance(faceA: string, faceB: string, distance: number): string
878
- ```
879
-
880
- #### `concentric()` — Constrain two axes to share the same center line.
881
-
882
- ```ts
883
- concentric(axisA: string, axisB: string): string
884
- ```
885
-
886
- #### `axisParallel()` — Constrain two axes to remain parallel.
887
-
888
- ```ts
889
- axisParallel(axisA: string, axisB: string): string
890
- ```
891
-
892
- #### `pointCoincident()` — Constrain two points to coincide.
893
-
894
- ```ts
895
- pointCoincident(pointA: string, pointB: string): string
896
- ```
897
-
898
- #### `pointOnFace()` — Constrain a point to lie on a face.
899
-
900
- ```ts
901
- pointOnFace(point: string, face: string): string
902
- ```
903
-
904
- #### `pointOnAxis()` — Constrain a point to lie on an axis.
905
-
906
- ```ts
907
- pointOnAxis(point: string, axis: string): string
908
- ```
909
-
910
- #### `angle()` — Constrain the angle between two faces.
911
-
912
- ```ts
913
- angle(faceA: string, faceB: string, degrees: number): string
914
- ```
915
-
916
- #### `totalEquations()` — Total constraint equations.
917
-
918
- ```ts
919
- get totalEquations(): number
920
- ```