---
skill-group: assembly
skill-order: 100
---

# Assembly API

Assembly-owned links, constraints, connectors, solved poses, and robot export.

## Contents

- [Assembly & Joints](#assembly-joints) — `bomToCsv`, `assembly`
- [Assembly](#assembly) — Kinematics, Structure, Connectors, References, Solving
- [ImportedAssembly](#importedassembly)
- [SolvedAssembly](#solvedassembly)
- [MateBuilder](#matebuilder)

## Functions

### Assembly & Joints

#### `bomToCsv()` — Convert an array of BOM rows into a CSV string.

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.

```ts
bomToCsv(rows: BomRow[]): string
```

**`BomRow`**: `part: string`, `qty: number`, `material?: string`, `process?: string`, `tolerance?: string`, `notes?: string`, `metadata?: PartMetadata`

**`PartMetadata`**

| Option | Type | Description |
|--------|------|-------------|
| `tags?` | `string \| readonly string[]` | Viewport organization tags applied to scene objects produced from this part. |
| `material?`, `process?`, `tolerance?`, `qty?`, `notes?`, `densityKgM3?`, `massKg?` | | — |

#### `assembly()` — Create an assembly container with named parts, connectors, and kinematic links.

**Use this from iteration 1 for any model with moving parts.** Do not build one static pose and retrofit motion later.

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.

`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.

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.

If no link in a connected kinematic component is fixed, ForgeCAD chooses a deterministic gauge link for solving and reports a floating-component warning.

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.

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.

**Point-link example**

This snippet mates a marker to the solved `tip` point. It does not orient a bar along `ground -> tip`.

```ts
const marker = box(8, 8, 4).withConnectors({
  center: connector({ origin: [0, 0, 0], axis: [0, 0, 1] }),
});

const mech = assembly("Linkage")
  .link("ground", { at: [0, 0, 0], fixed: true })
  .link("worldX", { at: [10, 0, 0], fixed: true })
  .link("tip", { at: [40, 0, 0] })
  .edgeBetweenLinks("ground", "tip", { name: "bar" })
  .addAngleBetweenLinks("worldX", "ground", "tip", {
    name: "theta",
    control: { min: 0, max: 120, default: 30 },
  })
  .addPart("Tip marker", marker, { mate: { connector: "center", toLink: "tip" } });

return mech;
```

```ts
assembly(name?: string): Assembly
```

---

## Classes

### `Assembly`

Container for a kinematic mechanism made up of links, relationships, and parts.

Assembly has two related but different motion tools:

- **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.
- **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.

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.

**Point-link quick start**

This attaches a marker to a solved point. It is intentionally not a bone or oriented part example.

```ts
const marker = box(8, 8, 4).withConnectors({
  center: connector({ origin: [0, 0, 0], axis: [0, 0, 1] }),
});

const mech = assembly("Linkage")
  .link("ground", { at: [0, 0, 0], fixed: true })
  .link("worldX", { at: [10, 0, 0], fixed: true })
  .link("tip", { at: [40, 0, 0] })
  .edgeBetweenLinks("ground", "tip", { name: "bar" })
  .addAngleBetweenLinks("worldX", "ground", "tip", {
    name: "theta",
    control: { min: 0, max: 120, default: 30 },
  })
  .addPart("Tip marker", marker, {
    mate: { connector: "center", toLink: "tip" },
  });

return mech;
```

Returning an unsolved `Assembly` keeps the graph available to the runtime. Return a `SolvedAssembly` directly for a specific control state:

```ts
return mech.solve({ theta: 60 });
```

**Frame-aware serial joint**

```ts
const hip = cylinder(12, 10).withConnectors({
  socket: connector("hinge", {
    origin: [0, 0, 6],
    axis: [0, 0, 1],
    up: [1, 0, 0],
    kind: "revolute",
  }),
});

const upperLeg = box(60, 10, 8).translate(30, 0, -4).withConnectors({
  hip: connector("hinge", {
    origin: [0, 0, 0],
    axis: [0, 0, -1],
    up: [1, 0, 0],
    kind: "revolute",
  }),
});

return assembly("Leg")
  .addPart("Hip", hip)
  .addPart("Upper Leg", upperLeg)
  .connect("Hip.socket", "Upper Leg.hip", { as: "hip", min: -35, max: 55 })
  .solve({ hip: 20 });
```

**Return types**

| Return value | Standalone | `require()` result type |
|---|---|---|
| `Assembly` (unsolved) | yes | `ImportedAssembly` |
| `SolvedAssembly` | yes | `SolvedAssembly` |

**Properties:**

| Property | Type | Description |
|----------|------|-------------|
| `name` | `string` | — |

**Kinematics**

#### `link()` — Add a named kinematic link to the assembly graph.

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.

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.

```ts
link(name: string, options?: AssemblyLinkOptions): Assembly
```

**`AssemblyLinkOptions`**
- `at?: [ number, number, number ]` — Initial world-space position of this link before kinematic constraints solve it.
- `fixed?: boolean` — Keep the link locked at its authored `at` position during solves.
- `metadata?: Record<string, unknown>` — User metadata carried through the kinematic graph for inspection and tooling.

#### `edgeBetweenLinks()` — Add a relationship edge between two kinematic links.

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.

```ts
edgeBetweenLinks(a: string, b: string, options?: AssemblyEdgeBetweenLinksOptions): Assembly
```

**`AssemblyEdgeBetweenLinksOptions`**: `name?: string`, `length?: number | "lockCurrent" | "free"`, `min?: number`, `max?: number`, `visualOnly?: boolean`, `control?: AssemblyKinematicControlOptions`, `metadata?: Record<string, unknown>`

`AssemblyKinematicControlOptions`: `{ min?: number, max?: number, default?: number, unit?: string }`

#### `addAngleBetweenLinks()` — Add an angle relationship among three kinematic links.

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.

```ts
addAngleBetweenLinks(a: string, b: string, c: string, options?: AssemblyAngleBetweenLinksOptions): Assembly
```

**`AssemblyAngleBetweenLinksOptions`**: `name?: string`, `value?: number`, `min?: number`, `max?: number`, `control?: boolean | AssemblyKinematicControlOptions`, `limit?: AssemblyKinematicLimitOptions`, `metadata?: Record<string, unknown>`

`AssemblyKinematicLimitOptions`: `{ min?: number, max?: number }`

#### `describeKinematics()` — Return the assembly-native kinematic graph definition.

```ts
describeKinematics(): AssemblyKinematicGraphDef
```

**Structure**

#### `addPart()` — Add a named part to the assembly.

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.

`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.

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"`):

```ts
const housing = group(
  { name: "Body", shape: body },
  { name: "Lid", shape: lid },
);
assembly.addPart("Base Assembly", housing);
```

```ts
addPart(name: string, part: AssemblyPart, options?: PartOptions): Assembly
```

`PartOptions`: `{ transform?: TransformInput, metadata?: PartMetadata, mate?: AssemblyPartMateInput | AssemblyPartMateInput[] }`

**`AssemblyPartMateInput`**
- `connector: string` — Name of a connector declared on the part (via `withConnectors()`).
- `toLink: string` — Name of the link this connector's origin is pinned to.
- `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).

#### `addFrame()` — Add a virtual reference frame (no geometry) to the assembly graph.

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.

```ts
addFrame(name: string, options?: PartOptions): Assembly
```

**Connectors**

#### `usedConnectorRefs()` — Connector refs (e.g. "PartName.connectorName") consumed by connect/match calls.

```ts
get usedConnectorRefs(): ReadonlySet<string>
```

#### `withConnectors()` — Attach named connectors to a specific part or the assembly as a whole.

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.

Use the single-argument overload to attach assembly-level connectors — these are exposed when this assembly is imported as a sub-assembly.

```ts
withConnectors(partName: string, connectors: Record<string, ConnectorInput>): Assembly
```

**`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`

`ConnectorInput`: `{ connectorType?: string, gender?: ConnectorGender, measurements?: Record<string, number | string> }`

#### `getConnectors()` — Get connectors declared on a part in part-local space.

```ts
getConnectors(partName: string): ConnectorMap
```

#### `getConnector()` — Parse a "PartName.connectorName" reference and return the resolved connector. Throws descriptive errors if the part or connector doesn't exist.

```ts
getConnector(ref: string): { partName: string; connectorName: string; connector: ConnectorDef; }
```

#### `connect()` — Connect two parts by aligning their declared connectors, automatically computing frame and axis.

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.

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.

**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()`.

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.

**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.

The joint type is inferred from the connector's `kind` field if not specified in `options`.

When connectors are defined with `start`/`end`, you can control which point on each connector meets via `align` / `parentAlign` / `childAlign` (`'start'`, `'middle'`, `'end'`).

Use `connect()` when connector origins must physically coincide (flange-to-flange, bolt-into-bore).

```ts
// Hinge: both axes point outward along the hinge line
const frame = box(100, 10, 80).withConnectors({
  hinge: connector("hinge", {
    origin: [0, 0, 40],
    axis: [0, 0, 1],
    up: [1, 0, 0],
  }),
});
const door = box(60, 4, 80).withConnectors({
  hinge: connector("hinge", {
    origin: [0, 0, 40],
    axis: [0, 0, -1],
    up: [1, 0, 0],
  }),
});
assembly("Door")
  .addPart("Frame", frame)
  .addPart("Door", door)
  .connect("Frame.hinge", "Door.hinge", { as: "swing", min: 0, max: 110 });
```

```ts
connect(parentConnectorRef: string, childConnectorRef: string, options?: ConnectOptions): Assembly
```

**`ConnectOptions`**

| Option | Type | Description |
|--------|------|-------------|
| `flip?` | `boolean` | This parameter is ignored. If your connectors produce wrong orientation, fix the connector axis directions instead of using flip. |
| `parentAlign?` | `PortAlign` | Which point on the parent connector to align: 'start', 'middle' (default), or 'end'. |
| `childAlign?` | `PortAlign` | Which point on the child connector to align: 'start', 'middle' (default), or 'end'. |
| `align?` | `PortAlign` | Shorthand: set both parentAlign and childAlign at once. |
| `as?`, `type?`, `min?`, `max?`, `default?`, `unit?`, `effort?`, `velocity?`, `damping?`, `friction?` | | — |

#### `match()` — Auto-create a joint by matching typed connectors between two parts.

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.

The `pairs` map is `{ childConnector: parentConnector }`. The first pair drives joint creation; additional pairs are validated but do not create additional joints (they constrain the same rigid connection).

Define connectors on shapes with `shape.withConnectors(...)`:

```ts
const door = doorShape.withConnectors({
  hinge_top: connector.male("hinge", { origin: [0, 0, 90], axis: [0, 0, 1] }),
  hinge_bottom: connector.male("hinge", { origin: [0, 0, 10], axis: [0, 0, 1] }),
});
```

Then match in the assembly:

```ts
const mech = assembly("Door")
  .addPart("Frame", frame)
  .addPart("Door", door)
  .match("Door", "Frame", { hinge_top: "hinge_top", hinge_bottom: "hinge_bottom" });
// Matching connectors computes the placement relationship automatically.
```

```ts
match(childPartName: string, parentPartName: string, pairs: Record<string, string>, options?: MatchToOptions & { as?: string; }): Assembly
```

`MatchToOptions`: `{ force?: boolean, angle?: number, distance?: number }`

**References**

#### `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.

```ts
withReferences(refs: Pick<PlacementReferenceInput, "points">): Assembly
```

**`PlacementReferenceInput`**: `points?: Record<string, [ number, number, number ]>`, `edges?: Record<string, PlacementEdgeRef>`, `surfaces?: Record<string, PlacementSurfaceRef>`, `objects?: Record<string, PlacementObjectInput>`

`PlacementEdgeRef`: `{ start: Vec3, end: Vec3 }`

`PlacementSurfaceRef`: `{ center: Vec3, normal: Vec3 }`

**Solving**

#### `solve()` — Solve the assembly at the given control state and return positioned parts.

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.

Connector mates declared on `addPart(..., { mate })` attach geometry to solved links while preserving part and connector identity:

- one mate **positions** the connector origin on its link;
- 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;
- a third mate **pins the roll** about the bone axis (full frame), e.g. a bore or clevis that must face a specific way.

Connector-frame joints created by `connect()` / `match()` are also evaluated; their values are read from `state` by joint name and clamped to joint limits.

```ts
return mech.solve({ theta: 45 });
```

```ts
solve(state?: JointState): SolvedAssembly
```

**Other**

#### `mate()` — Register mate constraints between parts. Constraints are solved during `solve()` to derive part positions and explode hints. Part references use "partName:featureName" format.

```ts
mate(fn: (m: MateBuilder) => void): Assembly
```

#### `describe()` — Return the serializable assembly definition used by solve/inspect pipelines.

```ts
describe(): AssemblyDefinition
```

**Compatibility Aliases**

- `usedPortRefs` -> `usedConnectorRefs`
- `withPorts()` -> `withConnectors()`
- `getPorts()` -> `getConnectors()`
- `getPort()` -> `getConnector()`

### `ImportedAssembly`

A wrapper around an imported `Assembly` that provides kinematic access and convenient transform helpers.

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.

**Kinematic access**

```ts
const arm = require("./arm.forge.js");

const solved = arm.solve({ shoulder: 45 });   // full kinematic solve
const link   = arm.part("Link", { shoulder: 60 }); // single part at state
const group  = arm.toGroup({ shoulder: 45 });  // only when ShapeGroup behavior is needed
```

**Convenience transforms** (auto-solve at defaults, return [`ShapeGroup`](/docs/core#shapegroup)):

```ts
const positioned = arm.rotateZ(-90).translate(0, -20, 50);
```

**Merging into a parent**

```ts
require("./arm.forge.js").mergeInto(robot, {
  prefix: "Left Arm",
  mountParent: "Chassis",
  mountJoint: "leftMount",
  mountOptions: { frame: Transform.identity().translate(-70, 0, 10) },
});
```

#### `assembly()` — The underlying Assembly, for advanced composition and inspection.

```ts
get assembly(): Assembly
```

#### `solve()` — Solve the assembly at the given joint state (defaults to each joint's default value).

```ts
solve(state?: JointState): SolvedAssembly
```

#### `part()` — Return a specific named part positioned at the given joint state, with any stored placement offset applied.

```ts
part(name: string, state?: JointState): AssemblyPart
```

#### `getPart()` — Return a specific named part positioned at the default solved pose.

This mirrors `SolvedAssembly.getPart()` for imported assemblies. Use `solve(state).getPart(name)` when inspecting a non-default joint state.

```ts
getPart(partName: string): AssemblyPart
```

#### `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.

```ts
toGroup(state?: JointState): ShapeGroup
```

#### `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.

```ts
withReferences(refs: Pick<PlacementReferenceInput, "points">): ImportedAssembly
```

#### `referenceNames()` — List all attached placement reference names.

```ts
referenceNames(kind?: PlacementReferenceKind): string[]
```

#### `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.

```ts
placeReference(ref: string, target: [ number, number, number ], offset?: [ number, number, number ]): ImportedAssembly
```

#### `translate()` — Solve at defaults and return a translated ShapeGroup.

```ts
translate(x: number, y: number, z: number): ShapeGroup
```

#### `rotate()` — Solve at defaults and return a rotated ShapeGroup.

```ts
rotate(axis: [ number, number, number ], angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
```

#### `rotateX()` — Solve at defaults and return a ShapeGroup rotated around X.

```ts
rotateX(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
```

#### `rotateY()` — Solve at defaults and return a ShapeGroup rotated around Y.

```ts
rotateY(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
```

#### `rotateZ()` — Solve at defaults and return a ShapeGroup rotated around Z.

```ts
rotateZ(angleDeg: number, options?: { pivot?: [ number, number, number ]; }): ShapeGroup
```

#### `scale()` — Solve at defaults and return a scaled ShapeGroup.

```ts
scale(v: number | [ number, number, number ]): ShapeGroup
```

#### `mirror()` — Solve at defaults and return a mirrored ShapeGroup.

```ts
mirror(normal: [ number, number, number ]): ShapeGroup
```

#### `color()` — Solve at defaults and return a colored ShapeGroup.

```ts
color(hex: string): ShapeGroup
```

#### `child()` — Solve at defaults, get a named child part from the resulting group.

```ts
child(name: string): Shape | Sketch | ShapeGroup
```

#### `collisionReport()` — Detect overlapping part pairs at the default solved pose.

This mirrors `SolvedAssembly.collisionReport()` for imported assemblies. Use `solve(state).collisionReport(options)` when inspecting a non-default joint state.

```ts
collisionReport(options?: CollisionOptions): CollisionFinding[]
```

`CollisionOptions`: `{ parts?: string[], ignorePairs?: Array<[ string, string ]>, minOverlapVolume?: number }`

#### `minClearance()` — Compute the minimum gap between two parts at the default solved pose.

This mirrors `SolvedAssembly.minClearance()` for imported assemblies. Use `solve(state).minClearance(partA, partB, searchLength)` when inspecting a non-default joint state.

```ts
minClearance(partA: string, partB: string, searchLength?: number): number
```

#### `mergeInto()` — Flatten this sub-assembly's parts and relationships into `parent` and wire a mount relationship.

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:

```ts
parent.solve({ "Left Arm.theta": 45, "Right Arm.theta": -20 })
```

Connectors from sub-assembly parts are forwarded with the prefix.

The sub-assembly must have exactly one root part before it can be merged.

```ts
const robot = assembly("Robot").addPart("Chassis", chassis);

require("./arm.forge.js").mergeInto(robot, {
  prefix: "Left Arm",
  mountParent: "Chassis",
  mountJoint: "leftMount",
  mountOptions: { frame: Transform.identity().translate(-70, 0, 10) },
});
```

```ts
mergeInto(parent: Assembly, options: MergeIntoOptions): Assembly
```

**`MergeIntoOptions`**

| Option | Type | Description |
|--------|------|-------------|
| `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. |
| `mountParent` | `string` | Part name in the parent assembly to attach the sub-assembly root to. |
| `mountJoint` | `string` | Name for the new mount joint in the parent graph. |
| `mountType?` | `JointType` | Joint type for the mount connection (default: 'fixed'). |
| `mountOptions?` | `JointOptions` | Frame, axis, limits, and other options for the mount joint. |

**`JointOptions`**

| Option | Type | Description |
|--------|------|-------------|
| `connectorRefs?` | `JointConnectorRefs` | Connector refs that define this joint contract. Usually set by `connect()` / `match()`. |
| `frame?`, `origin?`, `axis?`, `min?`, `max?`, `default?`, `unit?`, `effort?`, `velocity?`, `damping?`, `friction?` | | — |

`JointConnectorRefs`: `{ parent: string, child: string, parentAlign?: PortAlign, childAlign?: PortAlign }`

### `SolvedAssembly`

The result of solving an assembly at a specific joint state.

`SolvedAssembly` holds world-space transforms for every part at a given pose. Top-level scripts can return a `SolvedAssembly` directly for display. Use `toGroup()` when you specifically need a [`ShapeGroup`](/docs/core#shapegroup) for composition, group-style transforms, or named-child lookup. Do not call `toGroup()` just to make a solved assembly render. Use `getPart()` / `getTransform()` to inspect individual parts programmatically.

**Validation**

Call `collisionReport()` to detect overlapping parts at this solved pose.

```ts
const solved = mech.solve({ shoulder: 45, elbow: -20 });
console.log("Collisions", solved.collisionReport());
return solved;
```

**Properties:**

| Property | Type | Description |
|----------|------|-------------|
| `name` | `string` | — |

**Methods:**

#### `warnings()` — Return any warnings generated during solve (clamped joints, unconverged mates, etc.).

```ts
warnings(): string[]
```

#### `getJointState()` — Return a snapshot of resolved joint values (after clamping and coupling).

```ts
getJointState(): JointState
```

#### `mateExplodeHints()` — Explode direction hints derived from mate constraints, or null if no mates.

```ts
get mateExplodeHints(): Record<string, { direction: Vec3; }> | null
```

#### `mateDof()` — Remaining degrees of freedom after mate constraints, or null if no mates.

```ts
get mateDof(): number | null
```

#### `mateConverged()` — Whether the mate constraint solver converged, or null if no mates.

```ts
get mateConverged(): boolean | null
```

#### `kinematics()` — Solved assembly-native kinematic graph, or null when no links were declared.

```ts
get kinematics(): SolvedAssemblyKinematics | null
```

#### `getLinkPosition()` — Return the solved world position of a kinematic link.

```ts
getLinkPosition(linkName: string): Vec3
```

#### `getTransform()` — Return the world-space [`Transform`](/docs/core#transform) for the named part at the solved pose.

```ts
getTransform(partName: string): Transform
```

#### `getPart()` — Return the named part already positioned at its solved world transform.

```ts
getPart(partName: string): AssemblyPart
```

#### `toGroup()` — Convert all solved parts into a [`ShapeGroup`](/docs/core#shapegroup) with named children.

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.

```ts
const armGroup = mech.solve({ shoulder: 60 }).toGroup(); // only because we need rotateZ()
return armGroup.rotateZ(90);
```

```ts
toGroup(): ShapeGroup
```

#### `toSceneObjects()` — Return an array of named scene objects for the viewport renderer.

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.

```ts
toSceneObjects(): Array<{ name: string; shape?: Shape; group?: Array<{ name: string; shape: Shape; tags?: string[]; }>; metadata?: PartMetadata; }>
```

#### `toScene()` — Backward-compatible alias for `toSceneObjects()`.

```ts
toScene(): Array<{ name: string; shape?: Shape; group?: Array<{ name: string; shape: Shape; tags?: string[]; }>; metadata?: PartMetadata; }>
```

#### [`bom()`](/docs/output#bom) — Generate a bill of materials for all parts in the solved assembly.

```ts
bom(): BomRow[]
```

#### `bomCsv()` — Generate a bill of materials as a CSV string.

```ts
bomCsv(): string
```

#### `collisionReport()` — Detect overlapping (colliding) part pairs in this solved pose.

Computes boolean intersections between all part pairs and returns findings where the overlap volume exceeds `minOverlapVolume` (default 0.1 mm³).

```ts
const solved = mech.solve({ shoulder: 35, elbow: 60 });
console.log("Collisions", solved.collisionReport());
```

```ts
collisionReport(options?: CollisionOptions): CollisionFinding[]
```

#### `minClearance()` — Compute the minimum gap (clearance) between two parts in this solved pose.

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.

```ts
minClearance(partA: string, partB: string, searchLength?: number): number
```

### `MateBuilder`

**Properties:**

| Property | Type | Description |
|----------|------|-------------|
| `constraints` | `Constraint3D[]` | — |

**Methods:**

#### `flush()` — Constrain two faces so they stay flush.

```ts
flush(faceA: string, faceB: string): string
```

#### `align()` — Constrain two faces so their normals align.

```ts
align(faceA: string, faceB: string): string
```

#### `parallel()` — Constrain two faces so they remain parallel.

```ts
parallel(faceA: string, faceB: string): string
```

#### `faceDistance()` — Constrain the distance between two faces.

```ts
faceDistance(faceA: string, faceB: string, distance: number): string
```

#### `concentric()` — Constrain two axes to share the same center line.

```ts
concentric(axisA: string, axisB: string): string
```

#### `axisParallel()` — Constrain two axes to remain parallel.

```ts
axisParallel(axisA: string, axisB: string): string
```

#### `pointCoincident()` — Constrain two points to coincide.

```ts
pointCoincident(pointA: string, pointB: string): string
```

#### `pointOnFace()` — Constrain a point to lie on a face.

```ts
pointOnFace(point: string, face: string): string
```

#### `pointOnAxis()` — Constrain a point to lie on an axis.

```ts
pointOnAxis(point: string, axis: string): string
```

#### `angle()` — Constrain the angle between two faces.

```ts
angle(faceA: string, faceB: string, degrees: number): string
```

#### `totalEquations()` — Total constraint equations.

```ts
get totalEquations(): number
```
