# Parts & Assemblies

*Placement, mirroring, repeats, and reference parts*

**Names:** `assembly` `part` `instance` `by` `pos` `rot` `mirror` `repeat` `count` `id` `label` `role` `finish`

## When to use

Placing things in space. A `component` is the template (see `components-and-stock`); a
`part` in an `assembly` is one placed copy of it. Also here: `repeat` for rows of parts,
`mirror` for left/right pairs, nested assemblies for pieces that come apart, and
`role=reference` parts for context (a fridge, a wall) that must be seen and clash-checked but
never built.

## Syntax

```text
assembly <Name> [export=<Name>] { [params {...}] <part|join|repeat|assembly|hardware_use>... }

part <Id> instance=<Component|Assembly|@slot> [<param> <value>...] [finish=<expr>] [label="..."] {
    by <anchor>                     // default top_sw
    pos x=<expr> y=<expr> z=<expr>  // omitted axes are 0.0
    rot x=<deg> y=<deg> z=<deg>     // degrees, about the anchor
    mirror x=<expr> | y=<expr> | z=<expr>   // exactly one axis
}
part <Id> material=<M> role=<r> { stock <M> x y z [grain]; by ...; pos ... }   // inline one-off

repeat <var> count=<n> { ... }       // <var> is a float 0..n-1; id="Name_{var}" required
```

- A `part` has **either** `instance=` or an inline `stock`, never both.
- **Placement** is `M = S . T(pos) . R(rot) . T(-anchor)` (D5, ADR-0009): put the anchor point
  at `pos`, having rotated about it. Anchors, on every part for free: top corners `top_sw
  top_se top_ne top_nw`; top edge midpoints `top_n top_s top_e top_w`; bottom corners and
  midpoints `bottom_*`; and `top_center bottom_center center`. `sw` is the part-local `(0,0)`
  corner. Default `top_sw`.
- **The anchor is named in the part's own frame, before rotation.** `top_se` is always that
  part's `(x_max, 0, 0)` corner whatever `rot` says. Two correct ways to place four legs: do
  not rotate and anchor each leg by the corner that lands there (`top_sw`/`top_se`/`top_ne`/
  `top_nw`), or keep `top_sw` and turn by 0/90/180/270 so the same faces point inward.
  **Never mix the two** (`by top_se; rot z=90.0` swings the leg out of the assembly).
  `examples/dining_table.kdl` uses the rotate form inside a `repeat`.
- **`mirror`** takes exactly one of `x= y= z=`: a plane `axis = value` in the enclosing frame,
  applied last. A true left/right pair is one component written once, the right hand the same
  `by/pos/rot` plus `mirror`. Marks and their `normal=` reflect with the part; the cut list
  gives the pair two rows (left hand / right hand). Grain and asymmetric joinery are reflected
  geometrically, not re-cut. Legal on an assembly instance too.
- **`repeat <var> count=<n>`** binds `<var>` to `0..n-1`, a float (so `i / 2.0` works).
  Also legal in a `component` body (a schedule of features) and a `joinery` body (a `pin`
  schedule), lowered by what the body means. Nested repeats multiply (see the limits in
  `project-and-params`).
- **`instance=`** may name a component **or** an assembly. An instance of an assembly may
  override that assembly's own `params` with child nodes (`width 800.0`); a name that is not a
  param of the instanced assembly is `ww::eval::unknown_override`.
- **`finish=`** on any `part` overrides its material's finish for the cut sheet; not valid on
  an assembly instance.
- **Reference parts (ADR-0021):** `role=reference` plus `label="Refrigerator"`. It is in the
  scene and **in the clash check**, and in **no list**: not the cut list, stock list, nest,
  labels, DXF, edge schedule, cost, hardware schedule or waste. `label=` on any part is the
  display name in the viewer; the part id is unchanged.

## Example

```kdl
project name="Four Legs And A Fridge" units=mm schema_version=1 {
    params {
        width 800.0
        depth 500.0
        leg_dim 40.0
        top_th 20.0
    }
    materials {
        material Ash type=hardwood
        material Appliance type=sheet nominal_th=(in)1 finished_th=(in)1 color="#B0B4B8"
    }
    component Leg role=leg {
        stock Ash leg_dim leg_dim 700.0
    }
    assembly Table {
        // do not rotate: anchor each leg by the corner that lands there
        part Leg_FL instance=Leg { by top_sw; pos x=0.0 y=0.0 z=0.0 }
        part Leg_FR instance=Leg { by top_se; pos x=width y=0.0 z=0.0 }
        part Leg_BR instance=Leg { by top_ne; pos x=width y=depth z=0.0 }
        part Leg_BL instance=Leg { by top_nw; pos x=0.0 y=depth z=0.0 }
        part Top material=Ash role=top {
            stock Ash width depth top_th
            by top_sw
            pos x=0.0 y=0.0 z=0.0
        }
        // a mirror pair from one component, plane at the middle of the table
        part Leg_Mirrored instance=Leg { by top_sw; pos x=0.0 y=0.0 z=-100.0; mirror x="width / 2.0" }
        // a row, with an interpolated id
        repeat i count=3 {
            part Peg instance=Leg id="Peg_{i}" { by top_sw; pos x="100.0 + i * 50.0" y=100.0 z=-300.0 }
        }
        part Fridge role=reference label="Refrigerator" {
            stock Appliance (in)36 (in)30 (in)68
            by top_sw
            pos x=(in)42 y=0.0 z=0.0
        }
    }
}
```

## Pitfalls and diagnostics

- A `repeat` body part or mark whose id does not interpolate the loop variable collides;
  `kdl-check` refuses it. A `repeat` loop variable is a float, never a CEL int.
- Mixing "anchor by one corner" with "rotate about it" moves the part out of the assembly (the
  guide printed that mistake until 2026-09-24).
- `instance=` naming neither a component nor an assembly is a `kdl-check` error.
- `mirror` takes exactly one axis, and `finish=` is not valid on an assembly instance (an
  assembly is not one material) (KDL_AUTHORING §9).
- An assembly's params see its own file's project scope only, never the enclosing assembly's.
- Z is negative going **down**: `z=-100.0` is 100 mm below the assembly origin.
- Reference parts still clash: a cabinet overlapping the fridge is reported.

## See also

- `docs/KDL_AUTHORING.md` §2 (anchors), §9 (`assembly`, `part`, `repeat`), §15 (reference).
- `docs/rfc/0012-mirrored-placement.md` (`mirror`); `docs/adr/0009-rotation-order.md` (anchors).
- `components-and-stock.md`, `joinery-contract.md` (`join` lives in an assembly),
  `reuse-and-collections.md` (importing an assembly).

## Names

Every name this page declares, how the example library uses it, and the shortest real use.

### `assembly`

project-level node; node in 116 of 116 examples (159 uses); value in 2 of 116 examples (7 uses).

Shortest real use, from `drawer_dividers.kdl`:

```kdl
assembly Pair {
    part Left  instance=Uniform { by top_sw; pos x=0.0 y=0.0 z=0.0 }
    part Right instance=Compact { by top_sw; pos x="400.0 + organizer_gap" y=0.0 z=0.0 }
}
```

### `part`

node in 116 of 116 examples (1574 uses).

Shortest real use, from `queen_anne_tea_table.kdl`:

```kdl
part Top instance=Top { by top_sw; pos x=0.0 y=0.0 z=0.0 }
```

### `instance`

word-valued property; property in 112 of 116 examples (1483 uses).

Shortest real use, from `queen_anne_tea_table.kdl`:

```kdl
part Top instance=Top { by top_sw; pos x=0.0 y=0.0 z=0.0 }
```

### `by`

feature; node in 113 of 116 examples (1543 uses).

Shortest real use, from `pop_laminate_totem_divider.kdl`:

```kdl
by top_w
```

### `pos`

feature; node in 116 of 116 examples (1574 uses).

Shortest real use, from `danish_teak_sideboard.kdl`:

```kdl
pos x=ox y=oy z=oz
```

### `rot`

feature; node in 82 of 116 examples (439 uses).

Shortest real use, from `adirondack_pole_side_table.kdl`:

```kdl
rot x=90.0
```

### `mirror`

node in 30 of 116 examples (64 uses); value in 1 of 116 examples (2 uses).

Shortest real use, from `adirondack_plank_chair.kdl`:

```kdl
mirror x=xc
```

### `repeat`

node in 65 of 116 examples (160 uses).

Shortest real use, from `inlaid_tray.kdl`:

```kdl
repeat i count=2 {
    stringing face=pos_z material=Holly offset="6.0 + i * string_gap"
}
```

### `count`

property; node in 32 of 116 examples (54 uses); property in 70 of 116 examples (192 uses).

Shortest real use, from `tall_white_cutout_cabinet.kdl`:

```kdl
count 3
```

### `id`

word-valued property; property in 43 of 116 examples (92 uses).

Shortest real use, from `art_deco_skyscraper_bookcase.kdl`:

```kdl
dado id="Housing_{i}" face=pos_x width=shelf_th depth=dado_depth offset="toe + (i * p)" stopped=#true start=dado_stop
```

### `label`

word-valued property; property in 116 of 116 examples (2144 uses).

Shortest real use, from `drawer_stack.kdl`:

```kdl
width  400.0 min=250.0 max=900.0 step=10.0 label="Drawer width"
```

### `role`

word-valued property; property in 114 of 116 examples (900 uses).

Shortest real use, from `glazed_vitrine_whiplash_crest.kdl`:

```kdl
component Pane role=glass {
    stock Glass dw 6.0 dh
}
```

### `finish`

property; property in 78 of 116 examples (147 uses).

Shortest real use, from `farmhouse_harvest_table.kdl`:

```kdl
material Pine type=softwood finish="'none'"
```

## Related terms

- [Mirrored (handed) placement](/almanac/techniques#mirrored-placement)
- [Peg schedule](/almanac/techniques#peg-schedule)
