Skip to main content

Write a typical floor once

When the same plan repeats up a building, write the levels, the spaces and the boundaries once. Exceptional floors go on top as a difference.

File paths in the output below are absolute when you actually run these commands. They are shortened to bare filenames here for readability.

Three things can be written once

WhatThe write-once formWhat it expands to
Levelslevel L2..L6 4200 pitch:4200 …five declarations, L2 through L6
Spaces and boundariesa path whose head is /L2..L6/…the space or boundary on each level
Vertical boundariesstack core L1..L6 type:stairone per consecutive pair of levels

The three are independent. You can span the paths without spanning the levels, or the reverse.

1. Declare the levels as a range

A name of the form <prefix><number>..<prefix><number> expands as an arithmetic series. Each level's z is z + pitch × k.

level L1 0 h:2800 slab:1400
level L2..L6 4200 pitch:4200 h:2800 slab:1400
level R 25200 slab:600

pitch: is required on a range and forbidden on a single declaration.

✖ nopitch.muro:line 9: A level range requires pitch: (the storey height in mm): L2..L6
✖ single.muro:line 8: pitch is available only on a level range declaration (L?..L?)
✖ desc.muro:line 9: Cannot read the level range: L6..L2

h: and slab: land on every expanded level with the same value. The full rules for ranges are in level.

2. Span the head of the path

A path on a space, a zone or a boundary whose first segment reads L2..L6 expands over the declared levels between the two z values, in ascending z order. The prefixes need not match — z decides the order, not the spelling of the name.

space /L2..L6/office room  X1..X2 Y1..Y2 name:Tenancy use:exclusive daylight:1
space /L2..L6/core   stair X2..X3 Y1..Y2 name:Stair use:common stair:N form:return

boundary /L2..L6/office /L2..L6/core t:200 spec:RC
  door w:900 name:Stair-door

Span both ends of a boundary and you get one same-storey boundary per level. Spanning only one end is allowed too.

Something that exists on only part of the stack gets only that range. The bundled examples/tower/typical.muro writes the dwellings as /L3..L10/, the balconies as /L4..L10/ and the core as /L3..L11/ — because on the third floor the south face is a roof terrace over the podium instead of a balcony.

3. Run the vertical circulation through with stack

stack core L1..L6 type:stair

Connect storeys covers this in full.

4. Put exceptional floors in a difference layer

Leave the typical layer alone. Write the exception in its own file and import it afterwards. Later layers are stronger, and over replaces a value that already exists.

# L6.muro — the top floor becomes the staff canteen
over /L6/office h:3200 use:common name:Staff-canteen
over level L6 h:3200 slab:1000

over is not a definition. It errors if its target has not already been composed, so the exception layer must be imported after the typical layer. Layer strength and the rules for over are in Split a building into layers.

Check it

# main.muro
koyu 1.0
name Office with a typical floor
unit mm

grid X 0 8400 16800
grid Y 0 8400

level L1 0 h:2800 slab:1400
level L2..L6 4200 pitch:4200 h:2800 slab:1400
level R 25200 slab:600

import ./L1.muro
import ./typical.muro
import ./L6.muro

stack core L1..L6 type:stair
# typical.muro — twelve lines that describe five storeys
space /L2..L6/office room  X1..X2 Y1..Y2 name:Tenancy use:exclusive daylight:1
space /L2..L6/core   stair X2..X3 Y1..Y2 name:Stair use:common stair:N form:return

boundary /L2..L6/office /L2..L6/core t:200 spec:RC
  door w:900 name:Stair-door
boundary /L2..L6/office /out edge:S t:200 spec:CW
  window w:8000 h:2600 name:CW
boundary /L2..L6/office /out edge:W t:200 spec:CW
boundary /L2..L6/office /out edge:N t:200 spec:CW
boundary /L2..L6/core /out edge:E t:200 spec:RC
boundary /L2..L6/core /out edge:N t:200 spec:RC
boundary /L2..L6/core /out edge:S t:200 spec:RC
$ npx tsx src/cli.ts check main.muro
✔ Consistent — 13 spaces / 47 boundaries
  Structural consistency only — architectural validity is what koyu validate says, separately

Thirteen spaces and forty-seven boundaries out of fewer than forty lines. koyu levels shows the expansion — and the exception layer's replacement of L6's ceiling and slab shows up in the same output.

$ npx tsx src/cli.ts levels main.muro
R	z:25200	slab:600
L6	z:21000	h:3200	slab:1000
  ↑ storey height 4200 = ceiling 3200 + slab 600 + 400 left over
L5	z:16800	h:2800	slab:1400
  ↑ storey height 4200 = ceiling 2800 + slab 1000 + 400 left over
L4	z:12600	h:2800	slab:1400
  ↑ storey height 4200 = ceiling 2800 + slab 1400
L3	z:8400	h:2800	slab:1400
  ↑ storey height 4200 = ceiling 2800 + slab 1400
L2	z:4200	h:2800	slab:1400
  ↑ storey height 4200 = ceiling 2800 + slab 1400
L1	z:0	h:2800	slab:1400
  ↑ storey height 4200 = ceiling 2800 + slab 1400
Per-space ceiling height: /L6/office h:3200

koyu stats counts L6 under its own name and use.

L6
  /L6/office	Staff-canteen	room	70.56 m2
  /L6/core	Stair	stair	70.56 m2
  Subtotal 141.12 m2
Total 846.72 m2 (indoor floor area)
  hall: 70.56 m2
  stair: 423.36 m2
  room: 352.80 m2
By use: common 564.48 m2 (66.7%) / exclusive 282.24 m2 (33.3%)

Which layer supplied which value is koyu layers --attrs's answer.

$ npx tsx src/cli.ts layers main.muro --attrs
Layers (weakest first — later layers are stronger):
  0	main.muro
  1	L1.muro
  2	typical.muro
  3	L6.muro

Attribute provenance:
  level:L6:h	← 3 L6.muro
  level:L6:slab	← 3 L6.muro
  space:/L6/office:h	← 3 L6.muro
  space:/L6/office:name	← 3 L6.muro
  space:/L6/office:use	← 3 L6.muro

Circulation expands too. From the top floor to the street, descending five storeys costs no extra doors.

$ npx tsx src/cli.ts doors main.muro /L6/office /out
3 doors — /L6/office → /L6/core → /L5/core → /L4/core → /L3/core → /L2/core → /L1/core → /L1/lobby → /out

Where it goes wrong

  • Forgetting pitch:. A range cannot expand without a storey height — and writing it on a single declaration is an error the other way round.
  • Numbering downwards. L6..L2 cannot be read.
  • Importing the exception layer before the typical layer. over has no target yet, so it errors.
  • A level in the span that should not carry the space. /L2..L6/… creates the space on every level in range. Split the range when only some storeys should have it.

Next