Edge & Face Cuts#
Dado, groove, rabbet, chamfer, roundover, moulding, taper, hole, and trim
Names: dado groove rabbet chamfer roundover moulding taper hole trim edges faces face offset stopped start run_length stop size width depth target_dim start_z end_z target reference at length diameter ovolo cove ogee thumbnail half_round cockbead ramp lambs_tongue top_perimeter bottom_perimeter front_perimeter
When to use#
Material removal on one part, written inside its component (or an inline part) in the
part's own frame. Every feature on a part lowers to a single cut: the compiler unions every
cutter and cuts once, so write order does not matter. Use this file for housings, panel
grooves, edge profiles, tapered legs, holes and slots, and levelling a raked leg to the floor.
For an outline cut through the thickness use cut-paths; for joints between two parts use
joinery-contract. A veneer face is veneer-and-laminate.
Syntax#
Enum lists (edges, faces) are child nodes taking one or more arguments; KDL v2 has no
array literals.
| Node | Required | Notes |
|---|---|---|
dado | width depth offset | Runs across the grain. |
groove | width depth offset | Runs with the grain. A face the grain runs into (end grain) skips the cut at that face. |
rabbet | width depth + { edges ... } or face= edge= | face=/edge= reaches any edge, vertical ones included. Stoppable, square-ended only. bevel-and-rabbet. |
bevel | angle + { edges ... } or face= edge= | Built (RFC-0143). bevel-and-rabbet. |
chamfer | size + { edges ... } | Stoppable (below). |
roundover | size + { edges ... } | Stoppable; stop=lambs_tongue refused. |
moulding | section= size= + { edges ... } | Stoppable (stopped= start= run_length= stop=square|ramp). section= one of ovolo cove ogee thumbnail half_round cockbead; any other word is refused. |
taper | start_z end_z target_dim + { faces ... } | Full section at start_z, target_dim at end_z, on the named faces. |
hole | diameter depth | depth=0.0 is through. x= y= z= and normal= (the face it is drilled into) place it. length= + axis=(x|y) make a slot. |
trim | reference=world|assembly normal= at= + { faces ... } | The one feature resolved in world or assembly space, after placement. |
- Edge selectors (
edgeschildren):top_perimeter,bottom_perimeter,front_perimeter,all; named edgestop_n top_s top_e top_wandbottom_n bottom_s bottom_e bottom_w; the four vertical arrisesupright_ne upright_nw upright_se upright_sw; the four edges of the front facefront_top front_bottom front_e front_w. - Face words (
face=,faces):pos_x neg_x pos_y neg_y pos_z neg_z, part-local. - Placing a
dado/groove(ADR-0011).face=is the face it is cut into (default: the broad face; required on a square section).offset=is the distance of the cut's low edge from the low end of its axis (the bottom face for z): always positive, "up from the bottom" or "in from the front or left".stopped=#truewithstart=(default 10 mm in) andrun_length=(default: out through the far end) stops it. Which way it runs is not a property: it comes from the grain. The removed namesoffset_bottomandoffset_xdo not exist; the build reports them by name. target=<Name>on adado/groove(a bare word, e.g.target=panel) names the part the cut captures. It is documentation for the reader and the joint rules (astile_and_railframe's grooves,joinery-frames), not geometry: it moves nothing.id=/label=on adado/grooveare accepted syntax (both parse as strings) but nothing in the compiler reads them back: no diagnostic, no cross-reference, no cut-list or drawing output resolves against them (issue #549). They are for the author's own bookkeeping or future-proofing only — a name you can grep your own source for — not yet a real reference mechanism another node can resolve against.- Stopped
chamfer/roundover(RFC-0025):stopped=#true start= run_length= stop=square|ramp(squaredefault).chamferalone also takesstop=lambs_tongue. A chamfer or roundover on a vertical arris is refused if the run overlaps ataperon either of its faces. moulding size=is the profile's overall projection, measured aschamfer/roundover'ssize. Every section is a stepped polygon (16 chords per quarter circle).half_roundandcockbeadare applied mouldings: author them as their own narrowcomponent(a strip whose stock is the bead's section, profile cut on the faces that show) andjoinit on; a cut on the host can never leave material standing proud.- Most profiles do not follow a
cut_pathoutline:rabbet/bevel/mouldingand any edge selector naming individual compass edges ease the blank's straight edges only. On a part with a throughcut_paththat changes the outline, a perimeter selector (front_perimeter,all) leaves the sawn edges plain and reports "shaped only along the blank's own edges" in the skips. A named edge (top_n) the saw has taken away entirely is skipped as "sawn away" (a profile there would cut air); a named edge the saw only shortened is still shaped along what is left. A true profile along a curved run is not built (RFC-0126 addendum). chamfer's androundover's owntop_perimeter/bottom_perimeterare the exception (RFC-0021, issue #58 Part A): on a part whose outline came from a through,keep="inside"cut_path, these two sweep the real outline instead — arcs and spline-flattened runs included — rather than the box's four compass edges, which such a part does not have at all (three_legged_stool.kdl's rounded-triangleSeat).stopped=is refused there (no single straight edge's own length to measure a run against).- How big a profile may be: a
chamfer,roundover,mouldingorrabbetis cut into the corner of two faces and must stop strictly short of the opposite face of each, otherwise it is skipped ("would remove the whole section", naming the short dimension). Sosizeis not "the strip's thickness": a half-round strip needs thickness greater thansize(and width greater thansize);size = width / 2with both long edges moulded is the true semicircle. A strip 6 thick takessizeup to just under 6, not 6. - Stop a rabbet (or any stopped cut) at a height: there is no "stop at extent" keyword.
Measure on the edge's run axis from its low end:
stopped=#true start=0.0 run_length=<height>stops it<height>up; for a run measured down from the high end,start=<length - height>and leaverun_lengthout. Omittingstart=begins 10mm in. holewithlength=/axis=(RFC-0030) is a slot:lengthis the straight run between the two semicircular caps andaxis(xory) the part-local axis it runs along. Omitlength(or0.0) for a round hole;axis=does nothing then, so arepeatcan pass both and ternary onlylength.id=/label=on aholeare accepted syntax (both parse as strings) but nothing in the compiler reads them back: no diagnostic, no cross-reference, no cut-list or drawing output resolves against them (issue #551, same gap asdado/groove, issue #549). They are for the author's own bookkeeping only — a name you can grep your own source for — not yet a real reference mechanism another node can resolve against.trim(RFC-0024;reference=assembly, #458):normal=is a direction inreference='s own frame naming the side of the plane that is kept;at=is where the plane sits along that axis (a CEL expression, usually derived,"0.0 - leg_drop").facesnames the part's own face the cut is written from. Pick a side face nearly perpendicular to the plane, not the end the trim conceptually shortens: a face leaning past 60 degrees (MAX_LEAN) is refused.reference=isworld(the document's own origin) orassembly(the nearest enclosinginstance=-placed assembly's own frame; identity, same asworld, when the part has no such ancestor).
Example#
// housing across the grain, a panel groove in an edge, a stopped groove
dado face=pos_y width=19.0 depth=8.0 offset=40.0
groove face=pos_y width=6.0 depth=10.0 offset="(100.0 - 6.0) / 2.0"
groove face=pos_y width=10.0 depth=6.0 offset=40.0 stopped=#true start=50.0 run_length=400.0
// edge treatments
chamfer size=3.0 {
edges top_perimeter
}
roundover size=4.0 stopped=#true start=60.0 run_length=400.0 stop=ramp {
edges upright_se upright_sw
}
moulding section=ogee size=4.0 {
edges bottom_perimeter
}
rabbet width=10.0 depth=6.0 {
edges front_top
}
taper start_z=-200.0 end_z=-600.0 target_dim=60.0 {
faces pos_x pos_y
}
// a row of holes where only the outer two are slots
repeat i count=4 {
hole id="Peg_{i}" diameter=10.0 depth=0.0 x="20.0 + (i * 20.0)" y=40.0 z=0.0 length="(i == 0.0 || i == 3.0) ? 16.0 : 0.0" axis=x
}
Pitfalls and diagnostics#
- Not built features are skipped, never silently ignored.
lamination,pocket,splineand the legacy component-levelmortise,tenon,dovetail,box_joint(etc.) appear as askippedentry in the scene JSON and asww check's warning "laminationnot built: ...". Nothing is cut. bevel,rabbet face= edge=and stoppedmouldingare built; seebevel-and-rabbet.trim reference=assemblyis built (#458); only areference=naming neitherworldnorassemblyis refused.stop=lambs_tongueon aroundoveris refused, named. Asection=outside the six words is refused with the reason.- A feature that never reaches the part (a
trimwhose face never crosses the plane) is a skip in the build report, not a silent no-op. - Padding stock only to give
trimsomething to cut is real waste (ww::lower::wasteful_stock). Size the stock so the shallowest corner lands on the plane. - A
grooveordadocut into a face the grain runs into (end grain) is skipped. offsetis always positive and measured from the low end; do not use negative z here.- Features in a
repeatbody needid="Name_{i}"wherever an id or mark would collide.
See also#
docs/KDL_AUTHORING.md§7; ADR-0011 (dado/groove placement); RFC-0024 (trim), RFC-0025 (stopped profiles), RFC-0030 (slots), RFC-0126 (mouldings).examples/three_legged_stool.kdl(trim),examples/desk.kdl(dados),examples/dining_table.kdl.cut-paths.md,veneer-and-laminate.md,joinery-contract.md,components-and-stock.md.
Names#
Every name this page declares, how the example library uses it, and the shortest real use.
dado#
feature; node in 23 of 116 examples (63 uses).
Shortest real use, from tall_white_cutout_cabinet.kdl:
dado face=pos_x width=shelf_th depth=dd offset=plinth
groove#
feature; node in 40 of 116 examples (145 uses).
Shortest real use, from kumiko_andon_lantern.kdl:
groove face=pos_z width=gw depth=gd offset="(post_s - gw) / 2.0"
rabbet#
feature; node in 24 of 116 examples (56 uses).
Shortest real use, from tall_white_cutout_cabinet.kdl:
rabbet face=pos_x edge=pos_y width=back_th depth=rd
chamfer#
feature; node in 30 of 116 examples (56 uses).
Shortest real use, from adirondack_plank_chair.kdl:
chamfer size=3.0 { edges top_perimeter }
roundover#
feature; node in 42 of 116 examples (70 uses).
Shortest real use, from adirondack_plank_chair.kdl:
roundover size=3.0 { edges top_perimeter }
moulding#
feature; node in 18 of 116 examples (36 uses); value in 2 of 116 examples (4 uses).
Shortest real use, from farmhouse_harvest_table.kdl:
moulding section=thumbnail size=6.0 {
edges top_s
}
taper#
feature; node in 12 of 116 examples (27 uses); property in 4 of 116 examples (4 uses).
Shortest real use, from deco_side_chair.kdl:
taper start_z=0.0 end_z=seat_z target_dim=leg_dim {
faces pos_y
}
hole#
feature; node in 23 of 116 examples (29 uses).
Shortest real use, from pennsylvania_schrank.kdl:
hole
trim#
feature; node in 5 of 116 examples (7 uses); value in 1 of 116 examples (1 uses).
Shortest real use, from mid_century_credenza.kdl:
trim reference=world normal=pos_z at=floor_z {
faces pos_x
}
edges#
word-valued property; node in 71 of 116 examples (189 uses).
Shortest real use, from dining_chair.kdl:
edges top_n
faces#
word-valued property; node in 15 of 116 examples (34 uses).
Shortest real use, from deco_side_chair.kdl:
faces neg_y
face#
property; node in 1 of 116 examples (1 uses); property in 77 of 116 examples (483 uses).
Shortest real use, from blanket_chest.kdl:
face "'neg_y'"
offset#
property; property in 57 of 116 examples (240 uses).
Shortest real use, from deco_console_table.kdl:
stringing face=neg_y material=Ivory offset=8.0
stopped#
property; node in 7 of 116 examples (7 uses); property in 40 of 116 examples (135 uses).
Shortest real use, from biedermeier_chest_of_drawers.kdl:
stopped #true
start#
property; property in 40 of 116 examples (136 uses).
Shortest real use, from blanket_chest.kdl:
avoid start="carcass_height - till_lid_th - till_height" width=till_stock_th
run_length#
property; property in 36 of 116 examples (123 uses).
Shortest real use, from tansu_kiri_chest.kdl:
groove face=neg_z width=door_groove_w depth=8.0 offset=track_y stopped=#true start=0.0 run_length=left_w
stop#
word-valued property; property in 5 of 116 examples (15 uses).
Shortest real use, from ebonised_incised_cabinet.kdl:
chamfer size=chamfer_sz stopped=#true start=14.0 run_length="post_h - 28.0" stop=lambs_tongue {
edges upright_sw
}
size#
property; node in 1 of 116 examples (3 uses); property in 73 of 116 examples (198 uses).
Shortest real use, from bookcase.kdl:
size domino_size
width#
property; node in 46 of 116 examples (66 uses); property in 56 of 116 examples (292 uses); value in 22 of 116 examples (38 uses).
Shortest real use, from appalachian_post_and_rung_bench.kdl:
width 20.0
depth#
property; node in 59 of 116 examples (122 uses); property in 95 of 116 examples (571 uses); value in 33 of 116 examples (74 uses).
Shortest real use, from glazed_vitrine_whiplash_crest.kdl:
depth 8.0
target_dim#
property; property in 12 of 116 examples (27 uses).
Shortest real use, from deco_side_chair.kdl:
taper start_z=0.0 end_z=seat_z target_dim=leg_dim {
faces pos_y
}
start_z#
property; property in 12 of 116 examples (27 uses).
Shortest real use, from deco_side_chair.kdl:
taper start_z=0.0 end_z=seat_z target_dim=leg_dim {
faces pos_y
}
end_z#
property; property in 12 of 116 examples (27 uses).
Shortest real use, from deco_side_chair.kdl:
taper start_z=0.0 end_z=seat_z target_dim=leg_dim {
faces pos_y
}
target#
word-valued property; property in 33 of 116 examples (95 uses).
Shortest real use, from ebonised_incised_cabinet.kdl:
groove target=panel face=neg_z width=panel_th depth=groove_d offset=panel_off
reference#
word-valued property; property in 5 of 116 examples (7 uses); value in 3 of 116 examples (3 uses).
Shortest real use, from kitchen_collections.kdl:
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
}
at#
property; node in 47 of 116 examples (1024 uses); property in 10 of 116 examples (18 uses).
Shortest real use, from windsor_round_stool.kdl:
at z=-4.0 { circle r=rf }
length#
property; node in 59 of 116 examples (154 uses); property in 50 of 116 examples (234 uses); value in 46 of 116 examples (203 uses).
Shortest real use, from appalachian_ladder_back_side_chair.kdl:
length 26.0
diameter#
property; node in 50 of 116 examples (94 uses); property in 65 of 116 examples (129 uses).
Shortest real use, from appalachian_post_and_rung_bench.kdl:
diameter 9.0
ovolo#
value in 2 of 116 examples (5 uses).
Shortest real use, from jacobean_joined_chest.kdl:
moulding section=ovolo size=rail_ovolo {
edges front_top
}
cove#
value in 3 of 116 examples (3 uses).
Shortest real use, from pierced_splat_side_chair.kdl:
moulding section=cove size=5.0 {
edges upright_sw upright_nw upright_se
}
ogee#
value in 9 of 116 examples (14 uses).
Shortest real use, from blanket_chest.kdl:
moulding section=ogee size=4.0 {
edges top_perimeter
}
thumbnail#
value in 6 of 116 examples (9 uses).
Shortest real use, from farmhouse_harvest_table.kdl:
moulding section=thumbnail size=6.0 {
edges top_s
}
half_round#
value in 2 of 116 examples (4 uses).
Shortest real use, from william_and_mary_chest_of_drawers.kdl:
moulding section=half_round size=bead_size {
edges front_e front_w
}
cockbead#
value in 1 of 116 examples (1 uses).
Shortest real use, from federal_chest_of_drawers.kdl:
moulding section=cockbead size=3.0 {
edges front_perimeter
}
ramp#
not used by any example yet.
lambs_tongue#
value in 5 of 116 examples (15 uses).
Shortest real use, from ebonised_incised_cabinet.kdl:
chamfer size=chamfer_sz stopped=#true start=14.0 run_length="post_h - 28.0" stop=lambs_tongue {
edges upright_sw
}
top_perimeter#
value in 49 of 116 examples (70 uses).
Shortest real use, from adirondack_plank_chair.kdl:
edges top_perimeter
bottom_perimeter#
value in 19 of 116 examples (21 uses).
Shortest real use, from flat_pack_scale_test_table.kdl:
edges bottom_perimeter
front_perimeter#
value in 11 of 116 examples (11 uses).
Shortest real use, from bedside_cabinet.kdl:
edges front_perimeter