# Assembly geometry

Taking a machine apart in the reverse of the order it was put together: each part along the axis it was fitted on, in stages, and seated again exactly — plus the room the teardown needs.

> For the complete index, see [llms.txt](https://robocn.dev/llms.txt). A Markdown version of any page is available by appending `.md` to its URL or by sending an `Accept: text/markdown` header.

## Install

```bash
bunx --bun shadcn@latest add https://robocn.dev/r/assembly-geometry.json
```

Registry item: `assembly-geometry` · [`https://robocn.dev/r/assembly-geometry.json`](https://robocn.dev/r/assembly-geometry.json)

## Notes

- No collision model and no fastener model. Parts pass through each other's paths the way they do in every exploded drawing, and `progress` runs backwards as happily as forwards.
- `lantern-geometry` re-exports all of these, so anything installed before they moved keeps compiling.

## Usage

```tsx
import { assemblyEnvelope, explodeAssembly } from "@/lib/robocn/assembly"

// Fitted bottom up, so it comes apart top down. Rank 0 leaves first.
const parts = [
  { id: "skid", axis: { x: 0, y: 1, z: 0 }, travel: 0, order: 0 },
  { id: "post", axis: { x: 0, y: 1, z: 0 }, travel: 30, order: 1 },
  // A handed pair is one stage, and leaves along opposite axes.
  { id: "pitman-port", axis: { x: -1, y: 0, z: 0 }, travel: 34, order: 2 },
  { id: "pitman-starboard", axis: { x: 1, y: 0, z: 0 }, travel: 34, order: 2 },
]

const apart = explodeAssembly(parts, 0.4)
apart[1].offset     // a world offset — project it and it is a screen offset
apart[1].rank       // 0 is the first part off

// The box a frame has to fit, so it zooms out with the teardown and never with the pose.
assemblyEnvelope(parts, 0.4, { min: { x: -30, y: 0, z: -236 }, max: { x: 30, y: 176, z: 4 } })
```

## API

| name | type | default | description |
| --- | --- | --- | --- |
| `explodeAssembly` | `(parts: AssemblyPart[], progress: number, options?: { overlap?: number }) => ExplodedPart[]` | — | Takes an assembly apart in the reverse of the order it was fitted. At progress 0 every offset is exactly the zero vector; at 1 every part is exactly its own `travel` from where it sat. `overlap` runs from a strictly sequential teardown at 0 to every part moving at once at 1. |
| `AssemblyPart` | `{ id, axis: Vec3, travel: number, order: number }` | — | One part: the axis it was fitted along (need not be a unit vector), how far it has to go to be clear, and when it was fitted. Parts sharing an `order` are one stage and leave together — a course of ribs, a handed pair of pitmans. |
| `ExplodedPart` | `AssemblyPart & { direction, rank, fraction, distance, offset }` | — | `rank` 0 is the first part off. `offset` is in world units, and projection is linear, so `camera.project(offset.x, offset.y, offset.z)` is the translation to hang on the part. |
| `explodeFraction` | `(rank, count, progress, overlap?) => number` | — | The schedule on its own: how far through its own travel the part at `rank` is, out of `count` stages. |
| `assemblyEnvelope` | `(parts, progress, seated: { min: Vec3, max: Vec3 }) => { min: Vec3, max: Vec3 }` | — | The world box the assembly needs at `progress`: the seated box grown by the furthest travel along each axis. Fed to `fitFrame`, the drawing zooms out as the machine comes apart and at no other time — it takes a progress, not a pose, so the framing cannot breathe. |

## Source

- `src/lib/robocn/assembly.ts`
