# Hardware Mounting

*Placement, profile, recess cuts, and display-proxy geometry for catalogue hardware*

**Names:** `hardware` `hardware_mount` `hardware_use` `distribute`

## When to use

Use it when a purchased item has a position on the wood, or asks something of it: a knob or
pull that is screwed through a door front, a hinge whose leaf is let into an edge, a bail
handle you want to see in the viewer. If the item only needs counting (a box of screws, a
slide, a hank of rush) use `hardware_use` and stop; nothing here is needed.

Keywords: knob, pull, handle, hinge, leaf, recess, mortise, bore, screw hole, drawer pull,
bail, proxy, display, `hardware_mount`, one-sided join.

## Syntax

A catalogue item in `materials`. Only the numbers the cut or the picture needs:

```kdl grammar
hardware <Name> supplier="…" part_no="…" price=<n> \
    screw_diameter=<n> screw_length=<n> \          // a `hole` takes its size from these
    leaf_length=<n> leaf_width=<n> leaf_thickness=<n> \   // a recess takes its footprint from these
    shape=round|turned|bar|bail|custom|leaf|span color="#rrggbb" \  // optional display proxy
    diameter=<n> projection=<n> length=<n> width=<n>
```

A mount, and the `join` that places it. `female=` may be left off for a `hardware_mount`
joint, and only for that kind:

```kdl grammar
joinery hardware_mount name=<Joint> {
    hardware <Name> [qty=<n>]          // required; qty is per join, for the schedule
    hole [diameter=<n>] [depth=<n>] [dx=<n> dy=<n> dz=<n>] [normal=pos_x…neg_z]
    mortise_depth <n> [mortise_length <n>] [mortise_width <n>]
    axis x|y|z                         // part-local axis the leaf's long side runs along
    distribute [from=<mm>] [to=<mm>] [count=<n>] [setback=<mm>]   // N items along the axis (ADR-0078)
}
join joint=<Joint> male=Part@Mark [female=Part@Mark]
```

| Row | Does | Defaults |
| --- | --- | --- |
| `hole` | bores the male part's face at the mark | `diameter` from `screw_diameter`, `depth` from `screw_length`; `depth=0.0` is through |
| `mortise_depth` | recesses a leaf on every end the join names | footprint from `leaf_length` by `leaf_width`; the long side runs along `axis` (the hinge axis) when the joinery names one, else along the face's longer in-face axis |
| neither | counts the item and cuts nothing | the behaviour of every design written before ADR-0030 |

`distribute` (ADR-0078) spreads N items along the mark's long axis instead of one at the mark:
`from`/`to` are the zone the items must lie within, signed mm from the mark's point along
the male face's axis (default: the whole run of the male face, so a bare `distribute` hangs
a door along its full edge). The first and last item's near end sits `setback` inside the
zone's ends (the catalogue item's `setback=`, or the row's); N is `ceil(D / max_pitch) + 1`
for `D` the centre-to-centre run (the item's `max_pitch=`), or the row's `count=`; the items
are evenly spaced. One join places every item on every end it names, each recess, `hole`
and proxy included, and the schedule counts N. A zone too short for one item and its
setbacks is `hardware-distribute`; so is a missing `setback` or `max_pitch` (never invented).
A zone that keeps hinges clear of a till divider is just a `from`/`to` pair.

Proxy shapes and the numbers each needs: `round`/`turned` need `diameter`, `projection`;
`bar`/`bail` need `length`, `diameter`, `projection`; `custom` needs `length`, `width`,
`projection`; `leaf` needs `leaf_length`, `leaf_width`, `leaf_thickness`; `span` needs only
`diameter` (RFC-0145): a bar drawn from the male mark to the female mark, so a `join` with
`shape=span` must name both ends (`hardware-span` otherwise), and its marks are apart by
design (the join gap check skips it). A proxy is a
display solid only: it is in no cut list, stock list, drawing or clash check, so it never
clashes with the wood it is mounted on. An item with no `shape=` draws nothing.

## Example

```kdl
project name="Cabinet door" units=mm schema_version=1 {
    materials {
        material Oak type=hardwood
        hardware Knob shape=round diameter=30.0 projection=25.0 \
            screw_diameter=4.0 screw_length=12.0 color="#b08d57" \
            supplier="Horton Brasses" part_no="K-30" price=4.0
        hardware Hinge shape=leaf leaf_length=60.0 leaf_width=18.0 leaf_thickness=2.0 price=3.5
    }
    component Door role=door {
        stock Oak 400.0 300.0 20.0
        mark PullPoint x=200.0 y=150.0 z=0.0 normal=pos_z
        mark HingeEdge x=0.0 y=150.0 z=-10.0 normal=neg_x
    }
    component Post role=post {
        stock Oak 40.0 300.0 20.0
        mark HingePost x=40.0 y=150.0 z=-10.0 normal=pos_x
    }
    joinery hardware_mount name=KnobMount { hardware Knob; hole }
    joinery hardware_mount name=HingeMount { hardware Hinge qty=2; mortise_depth 2.0 }
    assembly Root {
        part D instance=Door
        part P instance=Post { pos x=-40.0 }
        join joint=KnobMount male=D@PullPoint
        join joint=HingeMount male=D@HingeEdge female=P@HingePost
    }
}
```

The knob is bored ⌀4 by 12 into the door's face and drawn as a ⌀30 by 25 cylinder standing
on it. The hinge is let 2 mm into the door's edge and the post's edge, and a leaf plate is
drawn on each. The hardware schedule says one knob and two hinges.

## Pitfalls and diagnostics

- **A recess is a fit you size.** There is no clearance allowance. `recess-fit` (error) means
  the leaf is larger than the face it is let into; shrink `mortise_length`/`mortise_width` or
  choose a bigger edge.
- **`remaining-wall`** (error): a blind bore or recess as deep as the part is thick. Write
  `depth=0.0` for a through bore.
- **`hardware-size`** (error): a `hole` has no diameter or depth and the catalogue item has no
  `screw_diameter`/`screw_length`; or a `mortise_depth` has no leaf size.
- **`hardware-face`** (error): the mark is a standard anchor, which has a point but no facing.
  Use a declared `mark … normal=`, or give the `hole` a `normal=`.
- **`hardware-fit`** (error): a bore overruns the face. Move the mark or shrink the bore.
- **`hardware-shape`** (warning): `shape=` is not one of the words above, or its numbers are
  missing or not positive. No proxy is drawn; the mount itself is unaffected.
- **A one-sided `join` on any other joint is an error** (`ww::expand::one_sided_join`).
- A proxy is an envelope, not a model of the product: a knob is a cylinder, a bail a box.
  Do not read it as a clearance check. A `join` that carries a `travel` is swept in motion, but
  only the wood is (ADR-0075, [`travel`](travel.md)).
- The proxy count is one per mount mark. `qty=` is the schedule's count only.

## Reveals, orientation and `ww joins`

- **Which way the leaf lies.** Without `axis` the recess (and the drawn leaf) follows the
  face's longer in-face axis, which is right for a door taller than it is wide. For a wide
  lid, or any part hung on its long edge, write `axis x|y|z`: the part-local axis the hinge
  runs along. It must be one of the face's two in-face axes; the face normal, or an axis the
  face does not carry, is a `recess-axis` error naming the axis and the face.
- **A reveal is not a gap.** An inset door stands off the carcass by its reveal and the hinge
  spans it. `ww joins` reports a leaf-hinge `hardware_mount` join (the item gives `leaf_length`
  and `leaf_width`) whose parts stand apart by no more than the hinge reaches (its catalogue
  `knuckle_dia`, else `leaf_width`) as `bridged`, counted apart from `gap`s; a bolt, a pull or
  a hinge pulled further apart than its reach is still a `gap`.
- **Veneer is not a gap.** A veneer leaf bonded to a marked face stands proud of the host by
  its own thickness; `ww joins` takes that thickness off the measured distance, so veneering
  a face a part is joined to neither opens a gap nor changes the reveal.

## See also

- `docs/adr/0030-hardware-placement-profile-and-recess.md`, `docs/adr/0017-hardware-and-materials.md`, `docs/adr/0075-travel-primitive.md`
- [`travel`](travel.md): sweeping a part that moves
- `docs/rfc/0128-hardware-placement-and-profile.md`, `docs/rfc/0071-hardware-hinges.md`
- `docs/KDL_AUTHORING.md` §5 (`hardware`) and the joinery table
- `examples/blanket_chest.kdl`, `examples/six_board_chest.kdl` (hinged lids), `examples/welsh_dresser.kdl`
  (counted-only hinges)
- `docs/styles/models/hardware-mount.kdl`

## Names

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

### `hardware`

word-valued property; node in 36 of 116 examples (112 uses); value in 1 of 116 examples (1 uses).

Shortest real use, from `kitchen_collections.kdl`:

```kdl
hardware @pull
```

### `hardware_mount`

joinery kind; value in 35 of 116 examples (49 uses).

Shortest real use, from `step_ladder.kdl`:

```kdl
joinery hardware_mount name=TieRod { hardware TieRod_Steel }
```

### `hardware_use`

node in 5 of 116 examples (15 uses).

Shortest real use, from `pennsylvania_schrank.kdl`:

```kdl
hardware_use DoorLock qty=1
```

### `distribute`

node in 2 of 116 examples (2 uses).

Shortest real use, from `welsh_dresser.kdl`:

```kdl
distribute
```

## Related terms

- [Gilt-bronze mount](/almanac/techniques#gilt-bronze-mount)
- [Hardware mount](/almanac/techniques#hardware-mount)
