Skip to content

Parts

This page as Markdown/docs/schema/parts.mdView .md

A part is a blank — a rectangular board — plus the features cut into it. Nothing about where it sits in the piece appears here; that is what joints are for.

parts:
- id: side_rail
label: Side rail
instances: [left, right]
material: rail_stock
grain: length
blank:
length: deck_length
width: rail_stock.width
thickness: rail_stock.thickness
features:
- kind: roundover
edges: [top_left, top_right]
radius: 3
Key
id required Referenced by joints, expressions and groups.
material required A materials id.
blank required length, width, thickness — all expressions.
label What the cut list and the drawing call it.
instances Named copies — see below.
grain length (default), width or thickness.
features What is cut into it — see Features.
note Free text, reaches the cut list and the drawing.

The blank is the board, not the finished part

Section titled “The blank is the board, not the finished part”

Give the size before the joinery. A tenon does not lengthen the blank and a dado does not shorten it; the resolver subtracts the features itself. Blank dimensions are what the cut list asks the timber yard for, with the material’s rough allowance added on top.

Size them from parameters rather than typing measurements:

blank:
length: deck_length # a derived parameter
width: rail_stock.width # the full width of the stock
thickness: rail_stock.thickness
blank:
length: side_rail.blank.length - 150 # another part's blank

The second form is how parts stay in step. The cleat is always 150 mm shorter than the rail, whatever the rail becomes.

length is the long axis, and it decides everything else

Section titled “length is the long axis, and it decides everything else”

The three names are not interchangeable labels — they are the part’s own coordinate axes, and every face, edge and feature is named against them:

+X = length start ←→ end
+Y = width left ←→ right
+Z = thickness bottom ←→ top

A board 800 × 195 × 25 is length: 800, width: 195, thickness: 25. Writing it as length: 25 is legal and will produce a part whose top face is 800 × 195 — which is not what any of your joints expect.

The full coordinate model is Anchors.

Which blank axis the long grain runs along. Defaults to length.

grain: length # the usual case
grain: width # a panel whose grain runs across its length

It drives the wood-movement checks, the end-grain fastener checks and the nesting on the cut list. A panel captured in a groove on all four sides will move across its grain and split the frame if it was not allowed room, and the validator can only tell you which way it will move if you have said which way the grain runs.

- id: headboard_panel
material: panel_stock
grain: width
blank: { length: 1600, width: 560, thickness: 18 }

That panel is wider than it is tall in the piece, and its grain runs vertically — the width axis. Say so.

Named copies of one part, cut from one cut-list row.

- id: head_post
instances: [left, right]

You then have head_post@left and head_post@right, and anchors address them with the @:

mate:
a: side_rail@left:face.start
b: head_post@left:face.top

left and right are special. A part with exactly those two instances is mirrored about the design’s YZ plane. Any other set of instance names is copied unmirrored, and each copy is positioned by its own joints.

Mirrored instances are the sharpest edge in the model

Section titled “Mirrored instances are the sharpest edge in the model”

Mirroring flips the part, and it takes the part’s faces and features with it. The consequences are worth reading twice:

  • The inner face of the left post is its right face; the inner face of the right post is its left face. Any joint that talks about “the inside” has to name a different face for each side, which is why rail-to-post joints are usually written out twice rather than with for_each.
  • A feature arrives upside down on the mirrored copy. A groove on the right face of the left post is on the right face of the right post too — rotated 180°. A panel mating into both will go in backwards on one side unless you correct it with align.roll: 180.

Both of these are visible in the worked example: the headboard panel seats into the left post’s groove normally and into the right post’s with roll: 180.

If a part has three or more copies that are genuinely identical and evenly spaced — slats, shelves, pickets — do not use instances. Use an array.

- id: centre_leg
note: Trim to fit on site — floors are rarely flat.

Free text that travels to the cut list and the drawing. Use it for the thing a person needs to know that no number captures.