Joinery Contract#
The two-body contract between joinery and join: marks, clearance, and the kinds table
Names: joinery join male female joint clearance fit butt name grain_check
When to use#
Every time two parts are joined. A joint is declared by the author, never inferred (D8):
you name the joint type once with a joinery block, then apply it with join between two
marks. The compiler cuts the male feature on one part and the female cavity on the other at
the same time. Which kind to write is the next question: use the kinds table below to find the
family file.
Syntax#
joinery <kind> name=<Name> {
<param> <value>... // kind-specific; see the family file
clearance <expr> // female side only, grown by clearance / 2 per face
fit slip|snug|press // instead of clearance=: resolve the female material's own
grain_check off reason="..." // opt out of the grain-contact error/rating; reason required
}
join joint=<Name> male=<Part>@<Mark> female=<Part>@<Mark>
male=/female=arePart@Mark, written unquoted (ADR-0002). The male part receives the protruding feature (tenon, tongue, pin); the female receives the cavity.- A reference whose part id contains a
repeatinterpolation must be quoted because{is structural in KDL:male="TopBoard_{j}@EdgeGlue_N". A literal id stays bare. joinlives in anassembly. Thejoineryblock is project-level and reusable by name.repeatinside ajoinerybody writes a schedule (graduated dovetailpinrows).grain_check off reason="..."opts a joint out of the grain-contact error and the banded strength rating (never the movement warning, a separate concern) — seedocs/KDL_AUTHORING.md§8's "Grain contact and the joint-strength rating" anddocs/adr/0082-grain-contact-and-joint-strength-rating.md.reason=is required.
Kinds and the file that documents each#
| Kind | Key params | File |
|---|---|---|
butt | none (needs no marks) | here |
dowel pocket_hole biscuit domino edge_glue | fastener rows | joinery-fasteners |
mortise_and_tenon round_tenon | tenon family (pin, wedge, key, through, proud) | joinery-tenons |
half_lap cross_lap bridle box_joint dovetail_through dovetail_half_blind sliding_dovetail | laps, bridles, box and dovetail joints | joinery-laps-and-boxes |
miter stile_and_rail | mitres and frame-and-panel | joinery-frames |
tongue_and_groove | declared only; a newer file in INDEX.md may build it | see INDEX.md |
hardware_mount | hardware <Name>; cuts nothing | materials-and-hardware |
A kind that is declared but not yet cut is resolved, drawn and reported as "not yet
synthesised" by ww joins, ww check and the viewer.
The layout rule every cut joint shares (M5, ADR-0013)#
A joint is laid out from its male mark, on the stock face the mark sits on and faces.
- The two marks'
normals must face each other (opposite in world space) on faces that are in contact, and the male mark must lie where the faces overlap. Where the female mark sits along its face is not used. - A standard anchor (
top_sw) has no facing, so a joint that cuts anything needsmarks on both parts. Onlybuttneeds none. clearancegoes on the female side only: its hole, slot or mortise is the male one grown byclearance / 2on every face.- Clearance resolution (issue #55).
clearance=always wins when given. With neitherclearance=norfit=, orfit=alone: the female material'stolerance_slip=/tolerance_snug=/tolerance_press=(materials-and-hardware), for the named class orsnugby default; else the project'sglue_clearanceparam; else0.2.clearance=andfit=together is an error. Seedocs/KDL_AUTHORING.md's "Clearance resolution" for the full chain and the draft per-material starting values. - Refused (nothing cut;
ww checkfails): marks that do not face each other, faces not in contact (an inset door's reveal), faces that do not overlap or are too short for the fastener, a fastener wider than the shared overlap or one that does not fit its stock, fasteners closer than their own size, a hole deeper than the board, holes from opposite faces that meet, and a parameter missing or out of range. - Warned: less wood left behind a blind cut than
max(3 mm, 15 %)of the board, and a fractionalcount. - A mark on no real stock face fails at
jointime. Oblique shoulders andtilt=/heading=marks are handled by the tenon family; seecomponents-and-stock.
Example#
project name="Joined Boards" units=mm schema_version=1 {
params { glue_clearance 0.2 }
materials { material Oak type=hardwood }
component Leg role=leg {
stock Oak 45.0 45.0 700.0
mark Face x=45.0 y=22.5 z=-100.0 normal=pos_x
}
component Rail role=rail {
stock Oak 400.0 20.0 80.0
mark End x=0.0 y=10.0 z=-40.0 normal=neg_x
}
joinery mortise_and_tenon name=RailJoint {
tenon_thickness 8.0
tenon_length 25.0
clearance glue_clearance
}
assembly Frame {
part Leg_A instance=Leg { by top_sw; pos x=0.0 y=0.0 z=0.0 }
part Rail_A instance=Rail { by top_sw; pos x=45.0 y=12.5 z=-60.0 }
join joint=RailJoint male=Rail_A@End female=Leg_A@Face
}
}
Pitfalls and diagnostics#
- Write
male=Rail_A@End, neverRail_A:Endor a quoted string (unless interpolated). joinis a mutual contract, not an action on one body; do not order joins or features expecting a sequence.connectis the removed old spelling ofjoin.ww joinslists each join with its gap, whether the marks meet and the normals' dot product (-1.0 is facing squarely);ww checkadds the clash and joinery verdicts.- A parameter on a
joineryblock may be a literal or a CEL expression over the project'sparams; enums (slope "1:8",face "'neg_y'") have per-kind quoting; see the family file. - Choose the joint a woodworker would actually use. The validator rejects what cannot be made; it cannot tell a poor choice (end-grain pocket screws into hardwood, a mortise into 6 mm ply) from a good one.
- Marks that touch but are not
joined are not a joint; ajoinis what cuts.
See also#
docs/KDL_AUTHORING.md§8; ADR-0013 (joint framework), ADR-0014 (joint authoring edits), ADR-0002 (Part@Mark).components-and-stock.md(marks),parts-and-assemblies.md(wherejoinsits).joinery-fasteners.md,joinery-tenons.md,joinery-laps-and-boxes.md,joinery-frames.md,materials-and-hardware.md.
Names#
Every name this page declares, how the example library uses it, and the shortest real use.
joinery#
project-level node; node in 109 of 116 examples (440 uses).
Shortest real use, from neo_shaker_cherry_sideboard.kdl:
joinery butt name=BackJoint { }
join#
node in 109 of 116 examples (1962 uses).
Shortest real use, from desk.kdl:
join joint=PullMount male=Face@PullPoint
male#
word-valued property; property in 109 of 116 examples (1962 uses).
Shortest real use, from desk.kdl:
join joint=PullMount male=Face@PullPoint
female#
word-valued property; property in 108 of 116 examples (1918 uses).
Shortest real use, from wedged_post_and_rung_stool.kdl:
join joint=LegJ male=Leg_FL@Top female=Seat@LegFL
joint#
word-valued property; property in 109 of 116 examples (1962 uses).
Shortest real use, from desk.kdl:
join joint=PullMount male=Face@PullPoint
clearance#
node in 102 of 116 examples (333 uses).
Shortest real use, from appalachian_ladder_back_side_chair.kdl:
clearance 0.4
fit#
word-valued property; not used by any example yet.
butt#
joinery kind; value in 20 of 116 examples (21 uses).
Shortest real use, from neo_shaker_cherry_sideboard.kdl:
joinery butt name=BackJoint { }
name#
word-valued property; property in 116 of 116 examples (556 uses).
Shortest real use, from neo_shaker_cherry_sideboard.kdl:
joinery butt name=BackJoint { }
grain_check#
word-valued property; not used by any example yet.