Skip to content

Features

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

Features are cut into a part’s blank. They do not change the blank’s size: a dado does not shorten a board and a tenon does not lengthen one. The blank is what you buy; the features are what you do to it.

parts:
- id: head_post
blank: { length: 900, width: 75, thickness: 75 }
features:
- kind: groove
id: panel_slot
on: right
along: length
width: 18
depth: 10
center: true
stopped: 40
- kind: chamfer
edges: [end_*]
size: 3

Every feature takes an optional id. Give it one if a joint needs to mate to it — that is what turns the feature into an anchor, and it is how a panel and the groove it sits in stay in step.

Most features also take an on: face. Faces, edges and the (u, v) frames they are measured in are all on the Anchors page.

Dados, grooves, mortises and notches sit somewhere along an axis, and there are four ways to say where. Give exactly one.

Form Means
from: 120 120 mm from the start end to the feature’s near edge
to: 300 300 mm from the start end to the feature’s far edge
at: 210 210 mm from the start end to the feature’s centre
center: true Centred on the axis

Giving two, or none, is a RES- finding rather than a guess. The resolver will not pick for you, because picking wrong puts a trench in the wrong place and nothing downstream would notice.

from and to may be expressions, so a feature can follow a parameter:

- kind: dado
on: right
from: deck_height - shelf_stock.thickness
width: 18
depth: 9

A square-bottomed trench across the grain, running the full width of the face unless stopped.

- kind: dado
id: shelf_seat
on: right
along: width # optional — defaults to the short axis of `on`
at: 400
width: 18 # the width of the trench, i.e. the thickness of what sits in it
depth: 9
stopped: 12 # optional: stop 12 mm short of the far edge

width is the trench’s width, not the part’s. Size it to the thickness of the part that seats in it — or better, reference that part directly:

width: shelf.blank.thickness

The same cut, with the grain. It is a separate kind because the validation differs: a groove along the grain and a dado across it fail in different ways, and a groove is what captures a panel that will move seasonally.

Same keys as dado. stopped: leaves a blind end, which is what keeps a groove from showing on the front edge of a post.

An L-shaped cut along one edge of a face.

- kind: rabbet
id: back_seat
on: right # one of the six faces
edge: end # which edge of that face it runs along
width: 12
depth: 9
from: 0 # optional — run only part of the edge
to: 400

Note edge: here names an edge of the face, not one of the twelve blank edges.

A blind or through rectangular slot.

- kind: mortise
id: tenon_seat
on: right
along: length # the slot's long axis
at: 300
length: 60 # along `along`
width: 12 # across it
depth: 30 # omit and set `through: true` for a through mortise
ends: round # `round` (routed) or `square` (chiselled)

ends matters to whoever cuts it: a router leaves round ends and a chisel leaves square ones, and a tenon has to match whichever you chose.

A rectangle removed from a corner or an edge — the classic leg-and-apron cut-out.

- kind: notch
on: top
edge: start
from: 0
length: 40
depth: 20
- kind: hole
on: top
diameter: 8
depth: 12 # or: through: true
at: { u: 100, v: center }
counterbore: { diameter: 14, depth: 7 }
countersink: { diameter: 8.4, angle: 90 }

at is a position in the face’s own (u, v) frame, measured from its minimum corner. Either value may be center.

For more than one hole, use pattern: instead of at: — see below.

Most holes should not be authored. Every clearance hole, pilot hole, counterbore and countersink that belongs to a fastener comes from the fixture’s machining recipe when you declare a joint. Hand-placing a screw hole means the validator cannot check its edge distance or thread engagement, because as far as it knows there is no screw there. Author a hole for a cable exit, a bolt you are supplying yourself, or a shelf-pin array — not for a screw.

A flat-bottomed rectangular recess that reaches no edge.

- kind: pocket
on: bottom
length: 120
width: 40
depth: 6
at: { u: center, v: center }

A repeated position. This is the shape behind every run of screw holes and every shelf-pin line.

pattern:
along: length # required: which axis the run follows
count: 10
spacing: auto # centre-to-centre, or `auto` to divide the span evenly
margin: 80 # keep this clear at each end
Key
along Required. length, width or thickness.
count How many. An expression, so it can be computed.
spacing Centre-to-centre, or auto to divide the available span evenly.
margin Clear space to leave at each end of the span.
from / to Bound the run explicitly instead of using the whole axis.
center Centre the run on the axis.
offset Position on the face’s other axis. Defaults to centred.
at Shorthand instead of count and spacing: any of start, end, centre, ends.
# Ten screws down a cleat, evenly spread, 80 mm clear at each end.
pattern: { along: length, count: 10, spacing: auto, margin: 80 }
# Two bolts, 100 mm apart, positioned at a computed height.
pattern:
along: length
count: 2
spacing: bolt_pitch
from: rail_centre_height - bolt_pitch / 2
to: rail_centre_height + bolt_pitch / 2
# Shelf pins on the 32 mm system.
pattern: { along: length, count: 12, spacing: 32, from: 200 }

The same pattern shape is used by joints[].fasteners.pattern, which is where you will write it most of the time.

Both take edge selectors, and wildcards work:

- kind: chamfer
edges: [top_end, bottom_end, left_end, right_end]
size: 3
- kind: chamfer
edges: [end_*] # the same four, shorter
size: 3
- kind: roundover
edges: [top_left, top_right]
radius: 3

Edge names and the wildcard rules are on the Anchors page. A chamfer or roundover has no frame, so it cannot be mated to.

A 2–3 mm break on every edge a hand will touch is worth authoring. It is also the cheapest thing in the file to change later.

A linear taper of one dimension along another axis — table and chair legs.

- kind: taper
along: length # the axis the taper runs down
reduce: width # the dimension that shrinks
to: 30 # its size at the far end of `along`
from: 150 # where the taper begins, from the `start` end
sides: both # `both` (default), `near` or `far`

sides: far tapers only the outward face, which is the usual choice for a leg that must stay square where it meets an apron.

Kind Positioned by Mateable
dado on + along-axis form yes
groove on + along-axis form yes
rabbet on + edge + from/to yes
mortise on + along-axis form yes
hole on + at or pattern yes
pocket on + at yes
notch on + edge + along-axis form no
chamfer edges no
roundover edges no
taper along + reduce no