Parts & Assemblies#
Placement, mirroring, repeats, and reference parts
Names: assembly part instance by pos rot mirror repeat count id label role finish
When to use#
Placing things in space. A component is the template (see components-and-stock); a
part in an assembly is one placed copy of it. Also here: repeat for rows of parts,
mirror for left/right pairs, nested assemblies for pieces that come apart, and
role=reference parts for context (a fridge, a wall) that must be seen and clash-checked but
never built.
Syntax#
assembly <Name> [export=<Name>] { [params {...}] <part|join|repeat|assembly|hardware_use>... }
part <Id> instance=<Component|Assembly|@slot> [<param> <value>...] [finish=<expr>] [label="..."] {
by <anchor> // default top_sw
pos x=<expr> y=<expr> z=<expr> // omitted axes are 0.0
rot x=<deg> y=<deg> z=<deg> // degrees, about the anchor
mirror x=<expr> | y=<expr> | z=<expr> // exactly one axis
}
part <Id> material=<M> role=<r> { stock <M> x y z [grain]; by ...; pos ... } // inline one-off
repeat <var> count=<n> { ... } // <var> is a float 0..n-1; id="Name_{var}" required
- A
parthas eitherinstance=or an inlinestock, never both. - Placement is
M = S . T(pos) . R(rot) . T(-anchor)(D5, ADR-0009): put the anchor point atpos, having rotated about it. Anchors, on every part for free: top cornerstop_sw top_se top_ne top_nw; top edge midpointstop_n top_s top_e top_w; bottom corners and midpointsbottom_*; andtop_center bottom_center center.swis the part-local(0,0)corner. Defaulttop_sw. - The anchor is named in the part's own frame, before rotation.
top_seis always that part's(x_max, 0, 0)corner whateverrotsays. Two correct ways to place four legs: do not rotate and anchor each leg by the corner that lands there (top_sw/top_se/top_ne/top_nw), or keeptop_swand turn by 0/90/180/270 so the same faces point inward. Never mix the two (by top_se; rot z=90.0swings the leg out of the assembly).examples/dining_table.kdluses the rotate form inside arepeat. mirrortakes exactly one ofx= y= z=: a planeaxis = valuein the enclosing frame, applied last. A true left/right pair is one component written once, the right hand the sameby/pos/rotplusmirror. Marks and theirnormal=reflect with the part; the cut list gives the pair two rows (left hand / right hand). Grain and asymmetric joinery are reflected geometrically, not re-cut. Legal on an assembly instance too.repeat <var> count=<n>binds<var>to0..n-1, a float (soi / 2.0works). Also legal in acomponentbody (a schedule of features) and ajoinerybody (apinschedule), lowered by what the body means. Nested repeats multiply (see the limits inproject-and-params).instance=may name a component or an assembly. An instance of an assembly may override that assembly's ownparamswith child nodes (width 800.0); a name that is not a param of the instanced assembly isww::eval::unknown_override.finish=on anypartoverrides its material's finish for the cut sheet; not valid on an assembly instance.- Reference parts (ADR-0021):
role=referencepluslabel="Refrigerator". It is in the scene and in the clash check, and in no list: not the cut list, stock list, nest, labels, DXF, edge schedule, cost, hardware schedule or waste.label=on any part is the display name in the viewer; the part id is unchanged.
Example#
project name="Four Legs And A Fridge" units=mm schema_version=1 {
params {
width 800.0
depth 500.0
leg_dim 40.0
top_th 20.0
}
materials {
material Ash type=hardwood
material Appliance type=sheet nominal_th=(in)1 finished_th=(in)1 color="#B0B4B8"
}
component Leg role=leg {
stock Ash leg_dim leg_dim 700.0
}
assembly Table {
// do not rotate: anchor each leg by the corner that lands there
part Leg_FL instance=Leg { by top_sw; pos x=0.0 y=0.0 z=0.0 }
part Leg_FR instance=Leg { by top_se; pos x=width y=0.0 z=0.0 }
part Leg_BR instance=Leg { by top_ne; pos x=width y=depth z=0.0 }
part Leg_BL instance=Leg { by top_nw; pos x=0.0 y=depth z=0.0 }
part Top material=Ash role=top {
stock Ash width depth top_th
by top_sw
pos x=0.0 y=0.0 z=0.0
}
// a mirror pair from one component, plane at the middle of the table
part Leg_Mirrored instance=Leg { by top_sw; pos x=0.0 y=0.0 z=-100.0; mirror x="width / 2.0" }
// a row, with an interpolated id
repeat i count=3 {
part Peg instance=Leg id="Peg_{i}" { by top_sw; pos x="100.0 + i * 50.0" y=100.0 z=-300.0 }
}
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
}
}
}
Pitfalls and diagnostics#
- A
repeatbody part or mark whose id does not interpolate the loop variable collides;kdl-checkrefuses it. Arepeatloop variable is a float, never a CEL int. - Mixing "anchor by one corner" with "rotate about it" moves the part out of the assembly (the guide printed that mistake until 2026-09-24).
instance=naming neither a component nor an assembly is akdl-checkerror.mirrortakes exactly one axis, andfinish=is not valid on an assembly instance (an assembly is not one material) (KDL_AUTHORING §9).- An assembly's params see its own file's project scope only, never the enclosing assembly's.
- Z is negative going down:
z=-100.0is 100 mm below the assembly origin. - Reference parts still clash: a cabinet overlapping the fridge is reported.
See also#
docs/KDL_AUTHORING.md§2 (anchors), §9 (assembly,part,repeat), §15 (reference).docs/rfc/0012-mirrored-placement.md(mirror);docs/adr/0009-rotation-order.md(anchors).components-and-stock.md,joinery-contract.md(joinlives in an assembly),reuse-and-collections.md(importing an assembly).
Names#
Every name this page declares, how the example library uses it, and the shortest real use.
assembly#
project-level node; node in 116 of 116 examples (159 uses); value in 2 of 116 examples (7 uses).
Shortest real use, from drawer_dividers.kdl:
assembly Pair {
part Left instance=Uniform { by top_sw; pos x=0.0 y=0.0 z=0.0 }
part Right instance=Compact { by top_sw; pos x="400.0 + organizer_gap" y=0.0 z=0.0 }
}
part#
node in 116 of 116 examples (1574 uses).
Shortest real use, from queen_anne_tea_table.kdl:
part Top instance=Top { by top_sw; pos x=0.0 y=0.0 z=0.0 }
instance#
word-valued property; property in 112 of 116 examples (1483 uses).
Shortest real use, from queen_anne_tea_table.kdl:
part Top instance=Top { by top_sw; pos x=0.0 y=0.0 z=0.0 }
by#
feature; node in 113 of 116 examples (1543 uses).
Shortest real use, from pop_laminate_totem_divider.kdl:
by top_w
pos#
feature; node in 116 of 116 examples (1574 uses).
Shortest real use, from danish_teak_sideboard.kdl:
pos x=ox y=oy z=oz
rot#
feature; node in 82 of 116 examples (439 uses).
Shortest real use, from adirondack_pole_side_table.kdl:
rot x=90.0
mirror#
node in 30 of 116 examples (64 uses); value in 1 of 116 examples (2 uses).
Shortest real use, from adirondack_plank_chair.kdl:
mirror x=xc
repeat#
node in 65 of 116 examples (160 uses).
Shortest real use, from inlaid_tray.kdl:
repeat i count=2 {
stringing face=pos_z material=Holly offset="6.0 + i * string_gap"
}
count#
property; node in 32 of 116 examples (54 uses); property in 70 of 116 examples (192 uses).
Shortest real use, from tall_white_cutout_cabinet.kdl:
count 3
id#
word-valued property; property in 43 of 116 examples (92 uses).
Shortest real use, from art_deco_skyscraper_bookcase.kdl:
dado id="Housing_{i}" face=pos_x width=shelf_th depth=dado_depth offset="toe + (i * p)" stopped=#true start=dado_stop
label#
word-valued property; property in 116 of 116 examples (2144 uses).
Shortest real use, from drawer_stack.kdl:
width 400.0 min=250.0 max=900.0 step=10.0 label="Drawer width"
role#
word-valued property; property in 114 of 116 examples (900 uses).
Shortest real use, from glazed_vitrine_whiplash_crest.kdl:
component Pane role=glass {
stock Glass dw 6.0 dh
}
finish#
property; property in 78 of 116 examples (147 uses).
Shortest real use, from farmhouse_harvest_table.kdl:
material Pine type=softwood finish="'none'"