Wadi data model (.wadi / house_config.json)
Generated from editor/src/schema/houseConfig.ts — do not edit by hand.
Regenerate: node scripts/gen-schema-doc.mjs <path/to/houseConfig.ts> reference/data-model.md
Some primitives (beam, floor_slab, pillar, plinth, ground) are generated from their
fields (schema/fields/*) into generated/objects.generated.ts — run npm run gen-primitives
in editor/ first if you changed those, so the generated schemas (which this doc reads) are current.
The Zod schema is the single source of truth; this file mirrors it (structure + the
semantics carried in its comments) so it can't drift.
A .wadi file is one JSON object matching HouseConfig. Geometry is in project
units (a unitless grid; by default units.per_unit = 10 means 10 units = 1 ft).
Plan coordinates are Inkscape-style: origin top-left, X → right, Y → down.
See coordinate-system.md for the coordinate/units detail and parametric-conventions.md
for variables/points/formulas.
Fields shared by (almost) every object
These appear on most object types; documented once here, marked (cross-cutting) below.
| field |
type |
req |
notes |
type |
literal room |
yes |
|
formulas |
map: field name → "= formula" string |
|
|
enabled |
boolean or number (false/0 = hidden) |
|
|
layer |
string |
|
|
name |
string |
yes |
|
material |
string |
|
|
z_offset |
number |
|
Vertical position of the room (its floor + walls), as a lift above the FLOOR BASE (slabZ = plinth top for floor 0, else the floor below's top; project units, 10 = 1 ft). This is the UNIFIED z_offset convention: every object is placed at slabZ + z_offset. When OMITTED, on-slab objects (room, wall, staircase, kitchen_platform) default z_offset to the floor's resolved slab thickness (floor.slab_thickness → house.defaults.slab_thickness → code default), so by default they sit on top of the slab, exactly as before. Set it explicitly for split-level floors — e.g. a room raised onto a thicker slab uses the same value the raised slab's top sits at. |
type — the discriminated-union tag; selects the object shape (values below).
formulas — per-field "= expression" overrides; the resolver evaluates each into the
matching numeric field. See parametric-conventions.md.
z_offset — vertical lift above the floor base (slab top). On-slab objects (room, wall,
staircase, kitchen_platform) default it to the floor's slab thickness; slab/beam/pillar/
roof default to 0.
Top level — HouseConfig
| field |
type |
req |
notes |
coord_convention |
enum: outer center |
|
How a rectangular object's x/y/width/length relate to its walls (plans/grid-convention.md). "center" (new/canonical): coordinates are wall CENTRELINES — adjacent rooms ABUT on a shared line (no overlap), walls are centred on the boundary, and expandRoomWalls grows each footprint by wall_thickness/2 to the outer face. "outer" / absent (legacy): coordinates are the OUTER wall face and adjacent rooms must overlap by wall_thickness. |
plinth |
any (freeform) |
|
Legacy top-level plinth (pre-"Plinth floor"). Tolerated but IGNORED so an un-migrated file still loads (it just renders without a plinth/ground) instead of failing .strict() validation. New configs put the plinth on the Plinth floor as a plinth object. |
defaults |
houseDefaults |
|
|
units |
units |
|
|
layers |
array of LayerDef |
|
Configurable 3D visibility layers (optional; defaults applied when absent). Objects opt in via their own layer field. |
variables |
map: string → number, or "= formula" string |
|
Parametric layer (plans/object-relationships-plan.md). Named scalar variables (number or "= formula", may reference other variables) and named 2D points; object formulas maps reference these. Optional — absent = a plain non-parametric house, resolved as a no-op. |
points |
map: string → inline object |
|
|
components |
map: string → ComponentDef |
|
Reusable-component library (in-file). Map of id → ComponentDef. A component object instantiates one by ref. Stored once; referenced by many instances; edit here to update every instance. |
grids |
map: string → gridDef |
|
First-class parametric grids (plans/grid-convention.md). Map of id → GridDef (named X/Y wall centrelines). Rooms/slabs bind via grid+cell, pillars via grid+node; the resolver derives their geometry from the centrelines + wall thickness. Optional; reusable across templates. |
configurator |
configurator |
|
Configurator metadata (Gharkul owner UI). Optional; see plans/configurator-plan.md. |
thumbnails |
array of string |
|
Preview snapshots (data: URLs) captured by the architect editor and saved WITH the template so the owner gallery can show real previews — multiple angles + the floor plan. thumbnails[0] is the gallery cover. Optional; excluded from share links (a preview isn't model data — see io/shareLink.ts). thumbnail (singular) is the legacy one-image form, still read as a fallback so old template files keep working. |
thumbnail |
string |
|
|
template |
inline object |
|
Catalog metadata that makes a .wadi SELF-DESCRIBING: the editorial fields a gallery card needs that can't be derived from geometry (title, blurb, style/roof tags, min plot). With this block + thumbnails[], a folder of .wadi files IS the catalog — the app lists the folder and indexes each file, with no separate index.json to maintain (see io/templateSource.ts). Non-strict so newer editorial fields don't break an older build. |
floors |
array of floor |
yes |
|
_walls_expanded |
boolean |
|
|
floor
| field |
type |
req |
notes |
floor_number |
integer ≥ 0 |
yes |
|
name |
string |
yes |
|
height |
number > 0 |
|
Per-floor overrides for the default heights in GlobalConfig. In project units (10 units = 1 ft). All three are INDEPENDENT — no relationship enforced between them: height — floor-to-floor rise (drives roof wallTop-Z stack) wall_height — standing wall height (floor top → ceiling) slab_thickness — RCC deck between this floor and the one above All fall back to GlobalConfig defaults when omitted. |
wall_height |
number > 0 |
|
|
slab_thickness |
number ≥ 0 |
|
|
formulas |
map: field name → "= formula" string |
|
|
enabled |
boolean or number (false/0 = hidden) |
|
|
objects |
array of Object types |
yes |
|
Object types (floors[].objects[])
Every entry in a floor's objects array is one of these, tagged by type:
plinth
The plinth is now a normal object placed on the "Plinth" floor (the first floor, number 0), not a top-level config key. Its footprint + height match the old top-level plinth; the plinth floor's height drives the rise to the floor above (replacing the old hardcoded plinth_height seed).
| field |
type |
req |
notes |
type |
literal plinth |
yes |
|
formulas |
map: field name → "= formula" string |
|
(shared — see top) |
enabled |
boolean or number (false/0 = hidden) |
|
(shared — see top) |
layer |
string |
|
(shared — see top) |
name |
string |
|
Label |
material |
string |
|
Material key |
x |
number |
yes |
Top-left X (project units) |
y |
number |
yes |
Top-left Y (project units) |
width |
number > 0 |
yes |
X extent (project units) |
length |
number > 0 |
yes |
Y extent (project units) |
height |
number > 0 |
yes |
Plinth height (project units) |
z_offset |
number |
|
Lift above ground (project units) |
ground
The ground plane, also on the Plinth floor. Extent defaults to the site plot when authored by the migration. height is an optional thickness (0 = a flat plane); slope fields are a later phase.
| field |
type |
req |
notes |
type |
literal ground |
yes |
|
formulas |
map: field name → "= formula" string |
|
(shared — see top) |
enabled |
boolean or number (false/0 = hidden) |
|
(shared — see top) |
layer |
string |
|
(shared — see top) |
name |
string |
|
Label |
material |
string |
|
Material key |
x |
number |
yes |
Top-left X (project units) |
y |
number |
yes |
Top-left Y (project units) |
width |
number > 0 |
yes |
X extent (project units) |
length |
number > 0 |
yes |
Y extent (project units) |
height |
number ≥ 0 |
|
Thickness (0 = flat) (project units) |
z_offset |
number |
|
Lift above origin (project units) |
component
An INSTANCE of a reusable component from the in-file components library. It references a component by id (ref), overrides the component's input variables via params, and places it at (x, y) with a z_offset lift on its parent floor. At render time expandRoomWalls flattens it into concrete objects (resolve component with param+origin overrides → recurse → offset), so no renderer needs to know about component.
| field |
type |
req |
notes |
type |
literal component |
yes |
|
formulas |
map: field name → "= formula" string |
|
(shared — see top) |
enabled |
boolean or number (false/0 = hidden) |
|
(shared — see top) |
layer |
string |
|
(shared — see top) |
name |
string |
|
|
ref |
string |
yes |
|
params |
map: string → union — see notes |
|
Overrides for the component's declared input variables. A string starting with "=" is a formula evaluated in the HOST scope (so it can reference the host's variables/points); a number is used directly. |
x |
number |
yes |
|
y |
number |
yes |
|
rotation |
number |
|
Standard placement: yaw° about the instance origin (clockwise, same sense as item rotation: 0=south, 90=east). Right angles (0/90/180/270) are exact for any component; a non-right angle is allowed only for furniture-only ones. |
z_offset |
number |
|
|
item
A free-standing GLB furniture / decor instance placed directly on a floor (for pieces that aren't inside an enclosed room — outdoor/site/verandah decor, a loft item, etc.). x/y are the item's plan CENTRE. It MAY instead anchor to a named room via anchor_to + anchor + gap, in which case x/y are DERIVED at expand time (same anchor model as room-nested items). rotation is yaw°; scale is a uniform resize; z_offset lifts it above the floor base (default = slab thickness). (itemAsset, itemAnchor, gapField are defined above room.)
| field |
type |
req |
notes |
type |
literal item |
yes |
|
formulas |
map: field name → "= formula" string |
|
(shared — see top) |
enabled |
boolean or number (false/0 = hidden) |
|
(shared — see top) |
layer |
string |
|
(shared — see top) |
name |
string |
|
|
asset |
ItemAsset |
yes |
|
x |
number |
yes |
|
y |
number |
yes |
|
rotation |
number |
|
|
scale |
number > 0 |
|
|
z_offset |
number |
|
|
anchor_to |
string |
|
Optional room-relative anchoring (for a free item that should follow a room). |
anchor |
ItemAnchor |
|
|
gap_x |
number |
|
|
gap_y |
number |
|
|
model
A GLB placed at real scale and manipulated by a rig of named-node ops. Distinct from item (furniture, catalog + anchoring): model is a rigged structural asset. asset.dimensions is the real metre size, used for the 2D footprint and the scale.
| field |
type |
req |
notes |
type |
literal model |
yes |
|
formulas |
map: field name → "= formula" string |
|
(shared — see top) |
enabled |
boolean or number (false/0 = hidden) |
|
(shared — see top) |
layer |
string |
|
(shared — see top) |
name |
string |
|
|
asset |
ItemAsset |
yes |
|
x |
number |
yes |
|
y |
number |
yes |
|
rotation |
number |
|
|
scale |
number > 0 |
|
|
z_offset |
number |
|
|
rig |
array of rigOp |
|
|
floor_slab
| field |
type |
req |
notes |
type |
literal floor_slab |
yes |
|
formulas |
map: field name → "= formula" string |
|
(shared — see top) |
enabled |
boolean or number (false/0 = hidden) |
|
(shared — see top) |
layer |
string |
|
(shared — see top) |
name |
string |
|
Label |
x |
number |
yes |
Top-left X (project units) |
y |
number |
yes |
Top-left Y (project units) |
width |
number > 0 |
yes |
X extent (project units) |
length |
number > 0 |
yes |
Y extent (project units) |
thickness |
number ≥ 0 |
|
Slab thickness (defaults to floor's) (project units) |
z_offset |
number |
|
Lift above floor base (project units) |
pillar
| field |
type |
req |
notes |
type |
literal pillar |
yes |
|
formulas |
map: field name → "= formula" string |
|
(shared — see top) |
enabled |
boolean or number (false/0 = hidden) |
|
(shared — see top) |
layer |
string |
|
(shared — see top) |
name |
string |
yes |
Label |
x |
number |
yes |
Top-left corner X (project units) |
y |
number |
yes |
Top-left corner Y (project units) |
width |
number > 0 |
|
X extent (project units) |
length |
number > 0 |
|
Y extent (project units) |
height |
number > 0 |
yes |
Column height (project units) |
z_offset |
number |
|
Lift above floor base (project units) |
beam
| field |
type |
req |
notes |
type |
literal beam |
yes |
|
formulas |
map: field name → "= formula" string |
|
(shared — see top) |
enabled |
boolean or number (false/0 = hidden) |
|
(shared — see top) |
layer |
string |
|
(shared — see top) |
name |
string |
|
Label |
x |
number |
yes |
Top-left X (project units) |
y |
number |
yes |
Top-left Y (project units) |
width |
number > 0 |
yes |
X extent (project units) |
length |
number > 0 |
yes |
Y extent (project units) |
height |
number > 0 |
|
Vertical thickness (project units) |
z_offset |
number |
|
Lift above floor base (project units) |
room
| field |
type |
req |
notes |
type |
literal room |
yes |
|
formulas |
map: field name → "= formula" string |
|
(shared — see top) |
enabled |
boolean or number (false/0 = hidden) |
|
(shared — see top) |
layer |
string |
|
(shared — see top) |
name |
string |
yes |
|
x |
number |
yes |
|
y |
number |
yes |
|
width |
number > 0 |
yes |
|
length |
number > 0 |
yes |
|
height |
number ≥ 0 |
|
0 accepted — semantically the same as absent ("use floor default"). Old configs that accidentally saved height: 0 keep loading; the form treats 0 as "no override" and doesn't write it back. |
material |
string |
|
|
z_offset |
number |
|
Vertical position of the room (its floor + walls), as a lift above the FLOOR BASE (slabZ = plinth top for floor 0, else the floor below's top; project units, 10 = 1 ft). This is the UNIFIED z_offset convention: every object is placed at slabZ + z_offset. When OMITTED, on-slab objects (room, wall, staircase, kitchen_platform) default z_offset to the floor's resolved slab thickness (floor.slab_thickness → house.defaults.slab_thickness → code default), so by default they sit on top of the slab, exactly as before. Set it explicitly for split-level floors — e.g. a room raised onto a thicker slab uses the same value the raised slab's top sits at. |
walls |
union — see notes |
|
|
wall_heights |
map: string → wall_heights entry |
|
|
items |
array of RoomItem |
|
Furniture nested in this room. Each piece is anchored to the room's inner footprint (see roomItem), so it reflows when the room resizes. Expanded into top-level item objects at render time. |
wall
| field |
type |
req |
notes |
type |
literal wall |
yes |
|
formulas |
map: field name → "= formula" string |
|
(shared — see top) |
enabled |
boolean or number (false/0 = hidden) |
|
(shared — see top) |
layer |
string |
|
(shared — see top) |
name |
string |
yes |
|
start_x |
number |
yes |
|
start_y |
number |
yes |
|
end_x |
number |
yes |
|
end_y |
number |
yes |
|
height |
number > 0 |
|
|
height_end |
number |
|
|
material |
string |
|
|
facing |
enum: north south east west |
|
|
z_offset |
number |
|
Lift above the FLOOR BASE (slabZ), project units. Omitted → defaults to the floor's resolved slab thickness (sits on the slab, as before). Set it for a split-level wall. Same convention as room. |
openings |
array of Opening |
|
|
staircase
| field |
type |
req |
notes |
type |
literal staircase |
yes |
|
formulas |
map: field name → "= formula" string |
|
(shared — see top) |
enabled |
boolean or number (false/0 = hidden) |
|
(shared — see top) |
layer |
string |
|
(shared — see top) |
name |
string |
|
|
climb |
enum: up down |
|
climb picks which end (start_x, start_y) is and which way the flight runs in z as it extends into direction: • "up" (recommended): BOTTOM-anchored. Put the stair on the LOWER floor it rises FROM; (start_x, start_y) is the bottom step's near corner on that floor and the flight ASCENDS into direction. rise_height defaults to THIS floor's height (climb to the next level). The intuitive way. • "down" (DEFAULT, kept for older configs): TOP-anchored. Put the stair on the upper DESTINATION floor; (start_x, start_y) is the top connection and the flight DESCENDS into direction. rise_height defaults to the floor immediately BELOW this one. Either way the body + landings fill the box [start, start + max_run] along direction, and z_offset is the ANCHORED end's height above the floor base (omitted → this floor's slab thickness, flush with the walking surface). |
start_x |
number |
yes |
|
start_y |
number |
yes |
|
rise_height |
number > 0 |
|
Total height the stair covers, top → floor below. The step COUNT is derived: num_steps = round(rise_height / step_rise). Omitted → defaults to the height of the floor immediately below this one. Formula-capable (e.g. "= floor_height"). Replaces the old explicit num_steps. |
step_rise |
number > 0 |
yes |
|
step_tread |
number > 0 |
yes |
|
step_width |
number > 0 |
yes |
|
direction |
enum: north south east west |
yes |
The direction the stair EXTENDS from its top — the whole assembly fills the allocated box from (start_x,start_y) going this way for up to max_run. |
max_run |
number > 0 |
|
ALLOCATED run: the length of space reserved for the stair along direction. The WHOLE assembly (flights + turn landings) is kept within [start, start+max_run]; when the run won't fit as one flight it auto-splits into switchback flights (more flights when tight), expanded in expandRoomWalls into plain staircases + floor_slab landings so every renderer is unchanged. Omit → one flight, no length limit. |
landing_depth |
number > 0 |
|
Turn-landing depth (along the run). Omitted → equals step_width. |
landing_thickness |
number ≥ 0 |
|
Turn-landing slab thickness. Omitted → equals step_rise. |
turn |
enum: clockwise anticlockwise |
|
Switchback handedness, reckoned DESCENDING from the top. Omitted → "clockwise". Only affects split stairs. |
flight_gap |
number > 0 |
|
Lateral gap between the two switchback flights (a stairwell void for a spine wall). Omitted/0 → flights are adjacent. The turn landings widen to bridge the gap. Only affects split stairs. |
z_offset |
number |
|
Height of the stair's TOP above the floor base (slabZ; project units, 10 = 1 ft). Omitted → this floor's slab thickness, so the top is flush with the walking surface and the flights descend to the floor below. Raise it for an internal step whose top sits above the floor. |
material |
string |
|
|
spiral_staircase
A helical staircase: steps treads winding turns revolutions around a central pole, from the floor to total_height, within radius. Placed by its CENTRE (x, y). Optional fields fall back to sensible defaults at render time.
| field |
type |
req |
notes |
type |
literal spiral_staircase |
yes |
|
formulas |
map: field name → "= formula" string |
|
(shared — see top) |
enabled |
boolean or number (false/0 = hidden) |
|
(shared — see top) |
layer |
string |
|
(shared — see top) |
name |
string |
|
Label |
x |
number |
yes |
Centre X (project units) |
y |
number |
yes |
Centre Y (project units) |
radius |
number > 0 |
yes |
Outer radius (project units) |
total_height |
number > 0 |
yes |
Total rise (floor to top step) (project units) |
turns |
number > 0 |
|
Revolutions (default 1) |
steps |
integer |
|
Number of treads (default ~12 per turn) |
tread_thickness |
number > 0 |
|
Tread slab thickness (project units) |
pole_radius |
number > 0 |
|
Central pole radius (project units) |
z_offset |
number |
|
Lift above floor base (project units) |
door
Flat door/window remain valid as a legacy schema — new configs nest them inside room.walls[side].openings or wall.openings.
| field |
type |
req |
notes |
type |
literal door |
yes |
|
formulas |
map: field name → "= formula" string |
|
(shared — see top) |
enabled |
boolean or number (false/0 = hidden) |
|
(shared — see top) |
layer |
string |
|
(shared — see top) |
name |
string |
yes |
|
x |
number |
yes |
|
y |
number |
yes |
|
width |
number > 0 |
yes |
|
height |
number > 0 |
yes |
|
direction |
enum: north south east west |
yes |
|
room |
string |
|
|
wall |
string |
|
|
open |
boolean |
|
|
window
| field |
type |
req |
notes |
type |
literal window |
yes |
|
formulas |
map: field name → "= formula" string |
|
(shared — see top) |
enabled |
boolean or number (false/0 = hidden) |
|
(shared — see top) |
layer |
string |
|
(shared — see top) |
name |
string |
yes |
|
x |
number |
yes |
|
y |
number |
yes |
|
width |
number > 0 |
yes |
|
height |
number > 0 |
yes |
|
sill_height |
number ≥ 0 |
|
|
direction |
enum: north south east west |
yes |
|
room |
string |
|
|
wall |
string |
|
|
open |
boolean |
|
|
Kitchen platform — a polyline countertop / cooking slab that runs along the base of walls. Path is the wall-side edge; the platform extends depth units perpendicular to each segment on the given side. Renders as one box per path segment; corners meet at the shared point (no fancy mitering in v1).
| field |
type |
req |
notes |
type |
literal kitchen_platform |
yes |
|
formulas |
map: field name → "= formula" string |
|
(shared — see top) |
enabled |
boolean or number (false/0 = hidden) |
|
(shared — see top) |
layer |
string |
|
(shared — see top) |
name |
string |
|
|
path |
array of tuple [n,n] |
yes |
|
side |
enum: left right |
yes |
|
depth |
number > 0 |
yes |
|
height |
number > 0 |
yes |
|
z_offset |
number |
|
Lift above the FLOOR BASE (slabZ), project units. Omitted → defaults to the floor's resolved slab thickness (sits on the slab top, as before). Same convention as room. |
base_z |
number |
|
|
material |
string |
|
|
roof
v2 roof — unified segment-based type that replaces hip/gable/flat/shed. Schema is permissive; the v2 pipeline (svg2d/roof/v2/) validates segments + slope + endpoint style at derivation time.
Freeform: extra fields are allowed (.catchall) and validated at derivation time. See roof-v2-guide.md.
| field |
type |
req |
notes |
type |
literal roof |
yes |
|
formulas |
map: field name → "= formula" string |
|
(shared — see top) |
enabled |
boolean or number (false/0 = hidden) |
|
(shared — see top) |
Shared & nested schemas
site
Objects don't bind to the grid with a special field — a grid line's position is published as a formula symbol (<gridId>.x<name> / .y<name>, see param/resolve.ts), so a room places itself with ordinary formulas, e.g. { x: "= main.x1", width: "= main.x5 - main.x1" }. With coord_convention:"center" those are wall centrelines and expandRoomWalls handles the wall extent.
| field |
type |
req |
notes |
reference_x |
number |
yes |
|
reference_y |
number |
yes |
|
plot_length |
number > 0 |
yes |
|
plot_width |
number > 0 |
yes |
|
formulas |
map: field name → "= formula" string |
|
|
enabled |
boolean or number (false/0 = hidden) |
|
|
houseDefaults
House-level overrides for the built-in GlobalConfig defaults. Every floor without its own value falls back to these; if these are absent too, the code defaults in DEFAULT_GLOBAL_CONFIG apply.
| field |
type |
req |
notes |
floor_height |
number > 0 |
|
|
wall_height |
number > 0 |
|
|
slab_thickness |
number ≥ 0 |
|
|
wall_thickness |
number > 0 |
|
House-wide wall thickness (project units). Per-object wall_thickness/thickness overrides still win. Falls back to the code default (DEFAULT_GLOBAL_CONFIG.wall_thickness = 8) when omitted. |
formulas |
map: field name → "= formula" string |
|
|
enabled |
boolean or number (false/0 = hidden) |
|
|
units
How dimensions are LABELLED on the drawings. Display-only — geometry always stays in project units; this just controls the text on the dimension lines. Omitted = the built-in default (feet & inches, 10 project units = 1 ft).
| field |
type |
req |
notes |
system |
enum: feet_inches feet meters centimeters millimeters |
|
feet_inches → 12' 6" ; the rest → decimal with a unit suffix. |
per_unit |
number > 0 |
|
Project units that equal ONE display unit (10 → 10 units = 1 ft; 100 → 100 units = 1 m). Default 10. |
precision |
integer ≥ 0 |
|
Decimal places for the non-feet_inches systems. |
Opening
The plinth is now a normal object placed on the "Plinth" floor (the first floor, number 0), not a top-level config key. Its footprint + height match the old top-level plinth; the plinth floor's height drives the rise to the floor above (replacing the old hardcoded plinth_height seed). plinth + ground are GENERATED from fields (schema/fields/{plinth,ground}.ts), imported above as plinthObject / groundObject. (P2b)
| field |
type |
req |
notes |
kind |
enum: door window |
yes |
|
name |
string |
|
|
formulas |
map: field name → "= formula" string |
|
Numeric fields hold the RESOLVED value; a = formula for any of them lives in formulas (e.g. formulas.offset), evaluated by resolveParametric against the house variables/points — same pattern as every other object. |
offset |
number |
yes |
Any number: center anchor takes a signed shift; the resolved placement is fit-checked against the wall in expand.ts, so a bad value errors there. |
anchor |
enum: start center end |
|
Which end of the wall offset is measured from, so the opening holds its place when the wall/room scales — no formula needed. start (default, legacy): offset from the wall start to the near edge. end: offset from the wall end to the far edge (0 = flush to the end). center: signed shift of the opening centre from the wall midpoint (0 = centred; may be negative). Resolved to a start-based offset in expand.ts (openingAnchor.ts). |
width |
number > 0 |
yes |
|
height |
number > 0 |
yes |
|
sill_height |
number |
|
|
direction |
enum: north south east west |
|
|
facing |
enum: north south east west |
|
|
open |
boolean |
|
When true, the opening is left BARE (just a hole) — no glazing/frame for a window, no leaf for a door — e.g. an open doorway or unglazed vent. |
RoomWallSide
| field |
type |
req |
notes |
height |
number |
|
|
height_end |
number |
|
|
openings |
array of Opening |
|
|
RoomItem
A furniture piece nested INSIDE a room (room.items[]). It has NO x/y — its plan position is DERIVED at expand time from the parent room's footprint + anchor + per-axis gap (+ its own rotation). Flattened into a top-level item for every renderer. gap_x/gap_y are the inset (project units) kept from the anchor into the room (edge/corner anchor → clears the wall; centre anchor → signed offset, +x east / +y south). gap_x/gap_y/rotation/scale/z_offset are all plain numeric fields so each can be driven by a = formula (via the formulas map).
| field |
type |
req |
notes |
name |
string |
|
|
formulas |
map: field name → "= formula" string |
|
|
enabled |
boolean or number (false/0 = hidden) |
|
|
layer |
string |
|
|
asset |
ItemAsset |
yes |
|
anchor |
ItemAnchor |
|
|
gap_x |
number |
|
|
gap_y |
number |
|
|
rotation |
number |
|
|
scale |
number > 0 |
|
|
z_offset |
number |
|
|
ItemAsset
Furniture (GLB item) — shared schema pieces Defined BEFORE room so a room can nest its own items[]. Asset distances are METRES (the GLB's native unit); the 3D/2D layers scale them into project units. See registry/nodes/item + three/units. The asset backing a furniture item — stored INLINE so a .wadi is self-contained (share links / web load the GLB from src). A catalog is just a picker convenience.
| field |
type |
req |
notes |
id |
string |
yes |
|
name |
string |
|
|
src |
string |
yes |
|
dimensions |
tuple [n>0,n>0,n>0] |
yes |
|
thumbnail |
string |
|
|
floorPlanUrl |
string |
|
|
category |
string |
|
|
tags |
array of string |
|
|
offset |
tuple [n,n,n] |
|
|
corrRotation |
tuple [n,n,n] |
|
|
corrScale |
tuple [n>0,n>0,n>0] |
|
|
ComponentDef
| field |
type |
req |
notes |
name |
string |
|
|
goal |
string |
|
A short natural-language description of what this component accomplishes (the discovery key for goal-based module lookup, e.g. "climb to the next floor"). Purely metadata — renderers ignore it. |
params |
array of ComponentParam |
|
|
variables |
map: string → number, or "= formula" string |
|
|
points |
map: string → inline object |
|
|
objects |
array of Object types |
yes |
|
expose |
inline object |
|
Promote this component to a typed primitive at load time (plans/declarative-plugins.md P0). When present, the component registers a NodeDefinition of type expose.type whose fields come from params; it can then be used like any core object type. type is namespaced (pack.thing). |
ComponentParam
A reusable component DEFINITION in the in-file components library. It is a mini-house: its own variables/points and a flat objects body authored in LOCAL coords (origin 0,0). params names which variables are the public inputs (label/default for the instance form). A component instance overrides those variables and places the body at its (x,y,z_offset). Stored once; referenced by many instances.
| field |
type |
req |
notes |
name |
string |
yes |
|
label |
string |
|
|
description |
string |
|
|
default |
number |
|
|
kind |
string |
|
Field-projection annotations, used only when the component is exposed as a typed primitive (plans/declarative-plugins.md). kind is a FieldKind preset (coord/extent/nonneg/int/text/flag/enum); when absent the kind is inferred from the default's type. unit is a doc-only unit hint. |
unit |
string |
|
|
LayerDef
A visibility layer for the 3D view. Each object may reference a layer by id (via its layer field); the layers menu toggles whole layers on/off. Display-only — never affects geometry. Optional: when absent, a built-in default layer set is used, and objects fall back to an automatic per-type/floor mapping.
| field |
type |
req |
notes |
id |
string (non-empty) |
yes |
|
label |
string |
yes |
|
color |
string |
|
|
group |
string |
|
Friendly group for the owner "Show/hide layers" menu (e.g. "Roof", "Walls"). Layers sharing a group toggle together. Optional. |
configurator
| field |
type |
req |
notes |
title |
string |
|
|
description |
string |
|
|
groups |
array of inline object |
|
|
inputs |
array of ConfiguratorInput |
yes |
|
Configurator (Gharkul owner UI) Optional, author-supplied metadata: which variables/points a template exposes to end users, and how to present them. IGNORED by the resolver and every geometry consumer — read only by the owner-facing Configurator UI. target is a variable name (e.g. "floorH") or a point coordinate ("House.W" → points.House.x; W/L/X/Y/x/y are resolver synonyms). min/max/ step are in RAW project units; unit only affects display.
| field |
type |
req |
notes |
target |
string (non-empty) |
yes |
|
label |
string (non-empty) |
yes |
|
description |
string |
|
|
control |
enum: slider number select toggle |
|
|
unit |
enum: ft in m units percent count none |
|
|
min |
number |
|
|
max |
number |
|
|
step |
number > 0 |
|
|
options |
array of inline object |
|
|
group |
string |
|
|
ItemAnchor
9-point anchor on a room's INNER footprint. First token = vertical (top = north … bottom = south), second = horizontal (left = west … right = east); "center" alone = both. The item aligns its matching edge/corner to this spot, held gap off it, into the room — so it reflows when the room is resized.
Enum: top-left top-center top-right center-left center center-right bottom-left bottom-center bottom-right