New Project

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 part has either instance= or an inline stock, never both.
  • Placement is M = S . T(pos) . R(rot) . T(-anchor) (D5, ADR-0009): put the anchor point at pos, having rotated about it. Anchors, on every part for free: top corners top_sw top_se top_ne top_nw; top edge midpoints top_n top_s top_e top_w; bottom corners and midpoints bottom_*; and top_center bottom_center center. sw is the part-local (0,0) corner. Default top_sw.
  • The anchor is named in the part's own frame, before rotation. top_se is always that part's (x_max, 0, 0) corner whatever rot says. 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 keep top_sw and turn by 0/90/180/270 so the same faces point inward. Never mix the two (by top_se; rot z=90.0 swings the leg out of the assembly). examples/dining_table.kdl uses the rotate form inside a repeat.
  • mirror takes exactly one of x= y= z=: a plane axis = value in the enclosing frame, applied last. A true left/right pair is one component written once, the right hand the same by/pos/rot plus mirror. Marks and their normal= 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> to 0..n-1, a float (so i / 2.0 works). Also legal in a component body (a schedule of features) and a joinery body (a pin schedule), lowered by what the body means. Nested repeats multiply (see the limits in project-and-params).
  • instance= may name a component or an assembly. An instance of an assembly may override that assembly's own params with child nodes (width 800.0); a name that is not a param of the instanced assembly is ww::eval::unknown_override.
  • finish= on any part overrides its material's finish for the cut sheet; not valid on an assembly instance.
  • Reference parts (ADR-0021): role=reference plus label="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 repeat body part or mark whose id does not interpolate the loop variable collides; kdl-check refuses it. A repeat loop 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 a kdl-check error.
  • mirror takes exactly one axis, and finish= 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.0 is 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 (join lives 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'"

Settings

Region

A region picks sensible defaults; change any of these separately.

Units

In inches, type lengths the way you say them: 34 1/2, 3/4", 1'-2" or 12.7mm. A length that can't be read is flagged, never rounded to something else.

Paper
Date order
Clock