# Project & Params

*The project root, parameters, and CEL expressions*

**Names:** `project` `units` `meta` `params` `preset` `label` `category` `unit_display` `solves` `inverse`

## When to use

Every file starts here: the `project` root, the `params` that make the design parametric, and
the expression language that every quoted string value is written in. Read this before any
other family, because the quoting rule below decides how every other node is spelled.

## Syntax

```text
project name="..." units=mm [schema_version=1] [front=<pos_x|neg_x|pos_y|neg_y|pos_z|neg_z>]
        [collection=<Name>] { ... }
params { <name> <default> [min= max= step= label= category= unit_display=] ... }
```

- `project` is the one root node. `units=mm` is the only accepted value (`units=in` changes
  only the viewer's readout). `front=` says which world direction is the piece's front
  (default `neg_y`). `collection=` selects a collection (see `reuse-and-collections`).
- `params` may appear at three scopes, and the inner shadows the outer: `project`,
  `component` (defaults, overridable per instance) and `assembly` (local working values).
- A parameter is **one line**: `name <default> min= max= step= label= category= unit_display=`.
  Give every user-facing dimension `min`/`max`/`step`/`label`/`category`; a derived value gets
  none (it is not a slider). `unit_display="in"` makes the viewer show fractional inches.
  To *write* a default in fractional inches use `(in)"1-3/4"` (ADR-0074), e.g. `w (in)"34 1/2" min=(in)12 max=(in)96`.
- `meta` is a block of free-form nodes inside `project`, parsed and kept. It carries no
  geometry and nothing reads it at compile time.

**The quoting rule (ADR-0004), which governs the whole language:**

| You write | It means |
| --- | --- |
| `19.0`, `glue_clearance 0.2` | a literal number |
| `"width / 2.0"` (a quoted string in a value position) | a **CEL expression** |
| `normal=pos_x`, `edges top_perimeter`, `part Face` | a **name or enum word**, never quoted |
| `"'danish oil'"` | a CEL expression that evaluates to a *string literal* |

All dimensions are floats (`19.0`, never `19`). Z is **negative going down** from a part's top
face. Unit literals: `(in)34.5` is 876.3 mm at parse time, `(mm)N` is the identity, anything
else (`(ft)3`) is `ww::dsl::unknown_unit`; in a CEL string use `inch(0.75)`.

**CEL (degrees, not radians).** Functions: `sin cos tan asin acos atan atan2`, `floor ceil
round abs`, `min max`, `sqrt pow`, `inch`, and the loudspeaker set `ac_sealed_fc ac_sealed_qtc
ac_port_length ac_helmholtz_fb` (litres, Hz, mm; [`speaker-analysis`](speaker-analysis.md)). Operators: `+ - * /`, unary `-`, comparisons,
`&& || !`, `cond ? a : b`; `+` joins text. Nothing else of CEL: `map`, `filter`, `all`,
`exists`, `has`, `size`, `matches`, the type-conversion functions, method calls, `in` and
list literals are refused with `ww::eval::not_in_language`.

Limits (the diagnostic names the one you hit): expression source 4 KiB, 128
operators/brackets per expression, text 4 KiB, integer arithmetic +-2^53; and the whole-file
limits: source 256 KiB, nesting depth 64, 2,000 parts, 1,000 features per part, one `repeat`
4,096 iterations.

## Example

```kdl
project name="Test Shelf" units=mm schema_version=1 {
    params {
        width  600.0 min=300.0 max=1200.0 step=10.0 label="Width"  category="Envelope"
        depth  250.0 min=150.0 max=400.0  step=10.0 label="Depth"  category="Envelope"
        thick   19.0 min=12.0  max=32.0   step=1.0  label="Thickness" category="Stock"
        half_w "width / 2.0"                       // derived: quoted, no slider
        angle_rise "atan2(100.0, width) + sin(30.0)"   // degrees
        gap (in)0.125                              // unit literal
        finish_name "'danish oil'"                 // a string literal, via CEL
    }
    materials { material WhiteOak type=hardwood }
    assembly Root {
        part Board material=WhiteOak role=shelf {
            stock WhiteOak width depth thick
            by top_sw
            pos x=0.0 y=0.0 z=0.0
        }
    }
}
```

**Linked parameters (ADR-0035) — a slider for a derived value.** A customer asks "how tall?",
the model is driven by "how many shelves". A *derived* project param may name the literal
param it **solves** and an **inverse** that turns a requested value into that param's input:

```text
<derived> "<forward expr>" [min= max= step= label= category=] solves=<literal_param> inverse="<expr using v>"
          [inverse_for_<other_param>="<expr using v>" ...]
```

- `inverse` is ordinary CEL, evaluated with the current params in scope plus the reserved name
  `v` (the requested value). The tie rule is yours to write: `floor(x + 0.5)` rounds ties up
  (the builtin `round` is ties-to-even). The result is clamped, then snapped to the target's
  own `min`/`max`/`step`, so `--set shelf_h=900` on a 5-shelf-max case lands on a real count.
- The forward expression must depend on the target, the target must be a **literal** param
  (never derived), and the inverse must not read the derived param it sets. Cycles are errors.
- Several inverses: `inverse_for_<param>=`; the first declared is the default, and
  `ww build f.kdl --set total_h=900 --for total_h=other_param` picks another.
- A request lands as the *target's* value; the compile reports `requested -> resolved`, and
  identity (share link, cache key) is the canonical target value, never the derived one.
  Setting the derived param and its target in one request is refused.

```kdl
project name="Shelving" units=mm schema_version=1 {
    params {
        shelf_count 3.0 min=2.0 max=6.0 step=1.0 label="Shelves" category="Envelope"
        gap         300.0 min=250.0 max=350.0 step=10.0 label="Shelf Gap" category="Envelope"
        thick        19.0 min=12.0 max=32.0 step=1.0 label="Thickness" category="Stock"
        total_h "(shelf_count * gap) + thick" min=500.0 max=2200.0 step=10.0 \
            label="Height" category="Envelope" \
            solves=shelf_count inverse="floor((v - thick) / gap + 0.5)"
    }
    materials { material WhiteOak type=hardwood }
    assembly Root {
        part Board material=WhiteOak role=shelf {
            stock WhiteOak 600.0 250.0 thick
            by top_sw
            pos x=0.0 y=0.0 z=0.0
        }
    }
}
```

**Named parameter bundles — `preset` (#642, ADR-0084).** A `preset "Name" { … }` node,
sibling to `params`, bundles a sparse set of literal-parameter overrides under a display
name; the viewer shows each one as a clickable card:

```text
preset "<Display Name>" { <param> <value> ... }
```

```text
params {
    case_w  800.0 min=420.0 max=1200.0 step=10.0 label="Width"  category="Envelope"
    height 1700.0 min=500.0 max=1900.0 step=10.0 label="Height" category="Envelope"
}
preset "Tallboy" { case_w 800.0 height 1700.0 }
preset "Bedside Chest" { case_w 500.0 height 620.0 }
```

- The node's own argument is the display name, same convention as `component`/`assembly`.
  The body holds bare `name value` lines only — no `min=`/`max=`/`step=`/`label=`, since an
  override is a value, not a second slider declaration.
- Any subset of the project's declared parameters; a parameter the preset does not mention
  is left alone. A target must be a literal parameter, never derived (`ww::preset::derived`,
  same rule as `--set`).
- Checked at compile time against the target's own `min=`/`max=`/`step=`, reusing the same
  validation `--set` already goes through: `ww::preset::unknown`/`::derived`/`::type`/
  `::range`/`::step`, each naming which preset. A compile error, not a warning. Two presets
  may not share a name (`ww::preset::duplicate`). Declaration order is display order.

## Pitfalls and diagnostics

- A bare integer dimension (`19`) and `units=` anything but `mm` are rejected by `kdl-check`.
- A typo in an identifier (`"heigth - top_th"`) is a hard error, not a silent dead reference:
  `kdl-check` resolves every identifier in every expression and ignores only the contents of
  CEL string literals.
- A literal word where an expression is expected (`finish="danish"`) reads as an unknown
  identifier. Write `"'danish'"`.
- A bare argument outside `params` is a **name**: `edges top_perimeter` is never an
  expression. A named property whose key is a label/enum slot (`label`, `category`, `id`,
  `name`, `instance`, `role`, `material`, `type`, `slope`, `profile`, `section`, `normal`,
  `axis`, `joint`, `male`, `female`, `stop`, `keep`, `match`, ...) is also never an expression.
- A parameter with children (`width 1.0 { ... }`) or more than one default is a parse error;
  an unknown key on a parameter lists the allowed ones (`min` `max` `step` `label` `category`
  `unit_display`).
- Hitting a limit: split a deeply nested `repeat` (`repeat 64` inside `repeat 64` is 4,096
  and is refused before the inner loop unrolls) or hoist an expression into a named param.

## See also

- `docs/KDL_AUTHORING.md` §1-4 (units, Z, params, `preset`), §13 (limits), §16 (unit literals).
- `docs/adr/0004` (string = expression), `docs/adr/0005` (CEL), `docs/adr/0021`,
  `docs/adr/0084` (`preset` blocks).
- `quickstart.md` for a first compiling file; `components-and-stock.md` for what goes in a
  project next.

## Names

Every name this page declares, how the example library uses it, and the shortest real use.

### `project`

node in 116 of 116 examples (116 uses).

### `units`

word-valued property; property in 116 of 116 examples (116 uses).

### `meta`

project-level node; not used by any example yet.

### `params`

project-level node, feature; node in 116 of 116 examples (386 uses).

Shortest real use, from `high_back_ladder_chair.kdl`:

```kdl
params { h 30.0 }
```

### `preset`

project-level node; node in 4 of 116 examples (10 uses).

Shortest real use, from `shaker_trestle_dining_table.kdl`:

```kdl
preset "Dining Table" {
    top_len 1800.0
    top_w   840.0
}
```

### `label`

word-valued property; property in 116 of 116 examples (2144 uses).

Shortest real use, from `drawer_stack.kdl`:

```kdl
width  400.0 min=250.0 max=900.0 step=10.0 label="Drawer width"
```

### `category`

word-valued property; property in 115 of 116 examples (2105 uses).

Shortest real use, from `scandinavian_birch_shelving_unit.kdl`:

```kdl
bays    3.0 min=3.0 max=5.0 step=1.0 label="Bays" category="Envelope"
```

### `unit_display`

word-valued property; not used by any example yet.

### `solves`

word-valued property; property in 1 of 116 examples (1 uses).

Shortest real use, from `filing_cabinet_vertical.kdl`:

```kdl
overall_h "leg_len + top_th" min=650.0 max=2000.0 step=5.0 \
    label="Overall Height" category="Envelope" \
    solves=drawer_count \
    inverse="floor((v - top_th - bank_floor_h - rail_h) / opening_pitch + 0.5)"
```

### `inverse`

property; property in 1 of 116 examples (1 uses).

Shortest real use, from `filing_cabinet_vertical.kdl`:

```kdl
overall_h "leg_len + top_th" min=650.0 max=2000.0 step=5.0 \
    label="Overall Height" category="Envelope" \
    solves=drawer_count \
    inverse="floor((v - top_th - bank_floor_h - rail_h) / opening_pitch + 0.5)"
```

