OpenEik.org

Draft OpenEik 0.3.0: draft, not final. From commit 197c2676 of 31 October 2026. Errata: the errata of 0.3. Raw text: /spec/0.3/compiler.md. Earlier: OpenEik 0.1, OpenEik 0.2, kept as they were.

On this page

OpenEik 0.3 specification

Part IV. Compiler

A compiler derives a model's geometry and quantities from its records, as a compiler derives a program from its source. The records are the only source; what the compiler produces is build output.

1. What is derived, and never stored

  1. These are derived: bodies and meshes; the layers' bodies; joins between walls and between members found from geometry; openings cut in their hosts; room boundaries, areas and volumes; quantities; members derived from a framing rule or a truss layout; stair runs, landings and treads; railings' balusters; MEP fittings; the elements of placed copies; drawings.
  2. Implementations MUST NOT store derived results in records or in record files. A cache of derived results MAY live outside the model (for example in a hidden folder) and MUST be keyed by the records it was derived from.
  3. Derived results MUST be a function of the model alone: the same records give the same results, whatever order the elements are in.
  4. A compiler derives no body for an element of a category it does not know (Part I, 10, rule 5). Where such an element carries declared geometry of its own in the world frame, the compiler MAY place it as section 11.1 says; a type's declaration it cannot place, since a type's frame depends on its category. It says, for each such element, which it did.

Reference: solids in src/core/geometry/geometry.ts builds every body from the model, and nothing it builds is written by eikFolder; the reference tests build a model whole and part by part and require the same bodies (buildSolids, "each build is made both ways").

1.1 The derived layer

For results that are hard to derive (a wall top attached to a pitched roof, joined members), a writer MAY add an optional derived layer beside the records, in the hidden folder .eik/derived/.

  1. A derived file MUST name every record it was derived from, by id and by hash (Part III, section 6), and the implementation and rule that produced it.
  2. A reader MAY use a derived result only after checking that each named record's current hash equals the one recorded. If any differs or is missing, the reader MUST discard the result and derive it again or do without.
  3. A derived file is never a record. A reader MUST NOT read model data from it, a validator MUST NOT require it, and it is not part of a version's tree or of a merge.
  4. A model MUST be complete without it: deleting .eik/derived/ loses nothing.

The file layout inside .eik/derived/ and the form of each result are not fixed in 0.1; 0.2 fixes them (decided 11 October 2026), in section 1.1.2. A shape that meets the rules is the one 1.1.2 gives: .eik/derived/elements/<id>.json holding {"sources": {"<key>": "<hash>", …}, "producer": "<implementation> <version>", "rule": "<rule name>", "result": {"bodies": […]}}.

1.1.1 A result's bodies

Part of 0.2. OpenEik 0.1 had it as an informative section, the proposal derived.

By the steward's decision of 26 October 2026, the reference implementation and a host print IFC with one writer, the bridge of @openeik/tools (Part VIII, 1), and an application hands it the bodies this specification does not derive yet, until it derives them all. So that what it hands is the derived layer's own form and not a bridge's, this section gives the first part of the form of a result: a result's bodies, in schemas/0.2/derived/bodies.schema.json.

A result has the form section 1.1.2 gives, {sources, producer, rule, result: {bodies, …}}: its sources by key and hash (1.1, rule 1), so that a reader uses it only while they hold (1.1, rule 2); its producer as people read it (Acme Modeller 2.31); its rule, by the producer's name for it; and its record's bodies, in the producer's order. A result is of one record: an element's bodies, or a type's representation, which its elements place. Handed from one tool to another, results are keyed as sources are: elements/<id>, types/<id>. Coordinates are in millimetres, z up (Part I, 3): an element's in the model's coordinates, a type's in its own frame.

KindMembersA body
extrusionprofile, holes, z0, heighta plan polygon, counter-clockwise seen from above, less its holes, each clockwise, from z0 up by height: a prism with a level underside and top (section 8, N1)
facesvertices, faces (each {outer, holes}, indices from 0), normalsplane faces, each counter-clockwise seen from outside, its holes the other way, and a normal per vertex where the body is smooth (N2, N6)
rodfrom, to, diametera round bar (N5)
cylindercentre, radius, z0, heightan upright cylinder, such as a round pier (N5)
sweptsection, from, to, rolla section of Part II, 5.2, or a polygon in its own plane, carried straight from one point to the other in a member's frame, the middle of its bounding box on the line, turned by its roll as a beam's is (sections 6.1 and 6.2, rule 5; N4)
mappedtype, origin, xAxis, zAxisthe representation of a type (its element's own where it names none), placed at origin, its frame's x along xAxis and its z along zAxis

holes, normals, roll, type, xAxis and zAxis may be left out. Each body may carry tags: its material, by the name of one of the model's materials or a name of its own; the layer of its element's type it is, by the layer's id (Part II, 5.1), which gives its material and colour where it names none; the part of its element it is, on the body of any element, by a name of the producer's: a door's or a window's frame, sash, glass, leaf, panel, casing or handle, a railing's post, rail or baluster, a truss's topChord or strut, and the like; the piece of its element it is, a part written apart (a stair's flight:0, winders, landing:0, stringer:0:left, beam, newel, column, bearing:0:left); the framing member it is, {id, class, predefined, objectType, name, section}, its id unique in the model and all else optional; its color; and whether it is glass.

An element's parts are the distinct names its bodies give in part, and a type's those its representation's bodies give; each part is the bodies that name it, with their material. Where they are two or more, a bridge writes each as a material of its own and the shape of its bodies (Part VIII, 1, its note on given bodies). One part, however many bodies name it, adds nothing: the element keeps the material it has without parts, its type's, or a plain material (open question 91).

1.1.2 The derived layer's layout and a result's form

Part of 0.2. OpenEik 0.1 had it as the proposal derived: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), the layout and the form are open (1.1), and what this section says meets their rules; no file of 0.1 is held to it.

By the steward's decision of 30 October 2026 (open questions, 98), which completes the decision of 11 October 2026 (open questions, 11):

  1. The layout. A result is of one record, an element or a type. An element's result lies at .eik/derived/elements/<id>.json and a type's at .eik/derived/types/<id>.json, its id written as a path segment (Part III, 2), so that a result's path says its key, the key its sources and a bridge (1.1.1) name it by: elements/<id> or types/<id>, the path less .eik/derived/ and .json, its segment read back as an id. A file elsewhere in .eik/derived/ is no result, and a reader ignores it.

  2. A result's form. A result is one JSON object {sources, producer, rule, result}: its sources (rule 3); its producer, the implementation that derived it and its version, as people read it; its rule, the rule or the set of rules it was derived by, by the producer's name for it; and its result, {bodies, quantities?, line?}. bodies are the record's bodies in the kinds of section 1.1.1 alone, with its tags, in the producer's order, which a reader keeps: an element's in the model's coordinates, a type's in its own frame. quantities, an element's, are its quantities by their names, each {value, unit, basis, rule}, its unit m3, m2, m or count, with layers, a quantity by each of its type's layer ids, as section 9 reports them. line, a beam's or a column's, is its lines of section 6.5: its reference line and its axis, each from its start to its end or null, a beam's setOut line, its centroidOffset or null, its section, {shape, width, depth, endDepth?, area}, its area null where it is not derived, and its length. A result holds nothing else. schemas/0.2/derived/bodies.schema.json states the form.

  3. Its sources. sources names, by its key, each record and each set of records whose change may change the result, with its hash (Part III, 6), or null where the model has none of that key:

    • <list>/<id>, a record of a list (elements/<id>, types/<id>, materials/<id> and the others), levels, the levels record, and project, the project record;
    • referrers/<id>, the elements that name the element by hostId or by a join's to (Part II, 7): the hash of the list of their ids, in code-unit order, so that a door added to a wall, or a mitre written in another member's record, makes the result stale;
    • categories/<category>, every element of a category: the hash of the list of their records, in the order of their ids, where any element of it may change the result wherever it is, as a shaft opens a slab (4.2, rule 8) and a roof of a group buries a gutter (4.7).

    A result names at least its own record.

  4. When it is current. A reader uses a result only while each of its keys has, in the model, the hash it names, null among them: a record named as missing that is there now makes the result stale, as one that changed or went does (1.1, rule 2).

  5. Several producers. A folder holds one result per record. A writer that derives a record's result writes it in place of the one there, whoever wrote that. A reader that wants its own rules' result compares the result's producer and rule with its own, and derives it again where they differ.

  6. Never in a version. Rule 3 of section 1.1 stands: a result is not part of a version's tree, of a merge, or of what a version carries from a host to a tool (Part VI), and no reader takes one from a version. A host or a bridge that prints a version prints what this Part derives from its records; where an application hands it results (1.1.1), they are results of the application's own folder, used only while current against the version's records (rule 4). Every body a print needs gets a rule of this Part in its time, so that each host prints from a version alone what the application printed.

2. Rule classes

ClassWhatStatus in 0.2
AClosed-form rules: layered extrusions, a slab's sloped plane, outline edges trimmed to a wall's face, roof planes, attached tops, member sweeps, a framing family's parts, rectangular openings and a door's and a window's frame, leaves and glass, shafts' openings, slab edge elements and the thickened edges and fills of their hosts, gutters, the line and the given posts of a railing on an outline, a wall's top or a slab edge element, a generic element's declared bodies, a pipe's or a duct's run swept round its own bends; and, stated from the reference implementation's engines, straight, L and U stairs, a framed wall layer's members, a roof's truss layout and its hip ends, a floor's joists, a room's boundary, ceiling and quantities, and a component's frame and its bodies (sections 7.3 to 7.6)Normative where this part states them
BRules that infer from geometry with tolerances: wall corner and T joins, member join extensions, room boundaries (but for those of section 7.5), MEP fittings between runs (branches, reductions, joints)Reference behaviour (sections 7.1 and 7.2)
CProduct and practice engines: timber framing and trusses (but for those of section 7.4), stairs (but for those of section 7.3), precast, steel joints, regional area rulesReference behaviour (section 7.2)

A section that is part of 0.2 says what it is in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3): there its rules are reference behaviour, as this table had them in 0.1.

A compiler of the core class MUST implement the class-A rules of sections 3 to 6 (4.6 and 4.7 among them), 7.3 to 7.6, 11.2 and 12, and treat declared geometry and generic elements as sections 11.1 and 11.3 say. Where a class-B or class-C rule changes a body, a compiler MAY differ from the reference implementation; it SHOULD say where it does.

3. Conventions

The level semantics, the directed baseline and the layer order of Part I, sections 4 and 5, are part of every rule below. Throughout:

  • elev(l) is the elevation of level l;
  • for a wall or beam with profile points P0, P1: L = |P1 − P0| is the plan length, u = (P1 − P0) / L the plan direction, and n = (−u_y, u_x) its left normal;
  • T is a type's total layer thickness, the sum of its layers' thicknesses.

4. Layered elements

4.1 Walls (N1)

Given a wall with base z0 = elev(levelId) + offset, height H (section 5.3) and a type with layers t1 … tk listed exterior first, the compiler MUST produce, before openings, joins, outline and attachment are applied, one body per layer of non-zero thickness:

  • Layer i is the prism over [z0, z0 + H] of the quadrilateral spanned along P0 → P1 between the offsets d_i and d_(i+1) along n, where d_1 = T / 2 and d_(i+1) = d_i − t_i.
  • A wall whose type has no layers is one body of thickness width, centred on the profile.

So the first layer lies on the left of the directed baseline, and the layers together fill [−T/2, +T/2] across it. The wall's width does not set its thickness when its type has layers.

The bodies are then: cut by the wall's hosted doors and windows (4.3); shaped at their ends by joins (7.1); limited to the wall's wallOutline when it has one (an elevation outline in wall coordinates: x along from P0, y up from the base); and limited by the wall's top (5.3).

Reference: strip and basicSolids (wallWidth = thickness ?? e.width), layeredBodies in src/core/geometry/geometry.ts; layerBands in src/core/elements/wallJoins.ts.

4.2 Slabs, foundations, ceilings and flat roofs

Given an element of category slab, foundation, ceiling, or roof without a roof payload, with top z1 = elev(levelId) + offset and outline profile, trimmed where its edges are trimmed to a wall's face (4.5):

  1. Its thickness is T when its type has layers, else its height.
  2. With layers t1 … tk listed top first, layer i is the prism of the outline over [z1 − Σ_(j≤i) t_j, z1 − Σ_(j<i) t_j]. A layer with an inset uses the outline offset inwards by the inset.
  3. Without layers, the body is the prism of the outline over [z1 − height, z1].
  4. Each outline in voids is cut through every layer.
  5. A foundation whose type has a round footing is a cylinder of the footing's diameter about the profile's single point, over [z1 − T, z1] (or [z1 − height, z1]).
  6. Thickened edges. A slab or a foundation is thickened under its edges that carry a slab edge element whose type's edgeProfile has a beam, and its layers under its structure give way there, as section 4.6, rules 9 and 11 say.
  7. Chamfers' fills. A slab or a foundation fills the corner a slab edge element's chamfer cuts off, under its structure's top, as section 4.6, rule 10 says.
  8. Shafts. A shaft (Part II, 6.2), its outline profile between its base elev(levelId) + offset and its top (5.1), takes the prism of its outline from its base to its top out of every body of every slab, ceiling and roof it overlaps in height by more than 0.5 mm, whatever their levels; and a shaft whose type's shaft.purpose is stair out of every body of a foundation too. A body is one layer's (rules 2 and 3; a pitched roof's, 4.4, rule 4), or one band of a layer or of a thickened edge (4.6, rule 9), as built before any shaft cuts it, with its own lowest and highest points. Where the shaft's base lies under the body's lowest point or within 0.5 mm of it, the prism reaches through the body's underside; where its top lies over the body's highest point or within 0.5 mm of it, through its top; so no sliver is left. A body that lies wholly over the shaft's top or under its base within the shaft's outline, as a pitched roof can whose lowest points lie elsewhere, loses nothing to it. Walls, beams, slab edge elements, a chamfer's fill and, for any other shaft, foundations stay whole. A shaft that ends inside a body leaves what lies under its base or over its top within its outline: a recess. It takes an opening out of a host where it takes all of the host within its outline, and a recess where it takes some of it; a host it takes nothing of keeps neither; a bridge writes each (Part VIII, 1.3).

A shaft from 400 under level 0 to level 1 (3000) passes a slab 350 thick whose top is level 0: it takes its outline out of each of the slab's layers whole, an opening. One from 100 under the slab's top to 400 over it takes the top 100 mm of the slab out within its outline and leaves the rest under it: a recess. One whose base is the slab's top, or whose top lies 0.5 mm or less over the slab's underside, takes nothing.

Reference: basicSolids (horizontal branch and round footings), layeredBodies, subtractHorizontalVoids; thickenedBodies (src/core/elements/edgeBeams.ts) and withChamferFills (src/core/elements/edgeChamfer.ts) for rules 6 and 7; shaftRange, shaftCutters, shaftHosts and stairOpeningHosts in src/core/elements/shafts.ts, subtractShafts in src/core/geometry/voidCuts.ts and shaftMeetings in src/core/elements/shaftOpenings.ts for rule 8.

4.2.1 A slab on a sloped plane

Part of 0.2. OpenEik 0.1 had it as the proposal slopedSlab: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), a slab's plane is a member a schema of 0.1 does not know, and the slab is built level, by section 4.2. The section adds that one member and no file (Part II, 6.3.1).

By the steward's decision of 30 October 2026 (open questions, 111). Given a slab that carries a plane (Part II, 6.3.1), its top at a point p in plan, z(p), as Part II, 6.3.1, rule 2 gives it, a plane of gradient (g_x, g_y), its slope σ, z1 its elev(levelId) + offset, and k = √(1 + g_x² + g_y²) = 1/cos σ:

  1. Its layers are laid square to the plane over its plan outline and cut plumb at its edges. With layers t1 … tn listed top first, layer i lies over the outline, trimmed where its edges are trimmed to a wall's face (4.5), between z(p) − k·Σ_(j<i) t_j and z(p) − k·Σ_(j≤i) t_j: a thickness measured square to the plane lies k times as deep vertically, as a pitched roof's layer does (4.4, rule 4). A layer with an inset lies over the outline offset inwards by the inset, as in 4.2, rule 2. Without layers, the body lies between z(p) and z(p) − k·height.
  2. Its openings. Each outline in voids is cut plumb through every layer, as through a level slab's (4.2, rule 4) and a pitched roof's. A shaft takes its outline out of each layer's body as 4.2, rule 8 says, by that body's lowest and highest points.
  3. Its edges trimmed to a wall's face (4.5). Its plane comes from its outline as recorded, as a pitched roof's planes do (4.5, rule 4): a plane given by an edgeId starts at the recorded edge, and one given by a fall at the recorded first point. Its outline is trimmed by 4.5, rule 2, and its layers fill what is left.
  4. Its quantities (informative, as section 9) are a pitched roof's: its volume is its layers' bodies'; its area is its outline in plan, trimmed, its voids and the shafts through its whole thickness deducted, as a level slab's; and its slopedArea is its top face over that area, measured on the slope: the area in plan times k.
  5. Where other rules read its top. A rule that reads a slab's top at a point reads the plane there: a column's base attached to it (5.2), at the column's centre, the plane, or the plane lowered by the slab's thickness times k for its underside; a wall's top attached to it (5.3), the plane lowered by the face's depth times k, as 5.3, rule 3 lowers a pitched roof's plane; a railing on its outline (11.2, rule 4), each point of the railing's line on the plane there, and each given post rising from the plane at its point; and a room's ceiling under it (7.5, rule 5): its underside, the plane lowered by its thickness times k, there everywhere, where its outline as recorded holds the room's point and none of its voids does, and where it lies more than 100 mm over the floor at the room's point. A rule that reads a slab's top as one height reads z1: which floors a level stands on (7.5, rule 1).
  6. What is not built on it. A slab whose plane slopes MUST NOT host a slab edge element in 0.2: a validator reports one hosted by it (slab-plane-edge), and a compiler derives that element no body, and the slab no thickened edge and no chamfer's fill (4.6, rules 9 and 10), and says so. A slab whose plane slopes has no joists laid out (7.4.15), and is no floor a framed wall bears on (7.4.4, rule 4): a compiler says so.
  7. A level slab, without a plane or with one of slope 0, is built by section 4.2 alone, as in 0.1, and every rule reads it as in 0.1. A plane a compiler cannot read, as a validator reports it (Part II, 6.3.1, rule 6) or its schema does, gives the slab no body, and the compiler says why (slab-plane).

These are class-A rules, held to the tolerances of section 10.

The ramp of Part II, 6.3.1, of a type whose layers are 50 mm of screed over 200 mm of concrete: k = √(1 + (1/20)²) = 1.0012492, so its screed lies from z = x/20 down to x/20 − 50.06, and its concrete on down to x/20 − 250.31. Its volume is 6 m × 2 m × 0.25 m × 1.0012492 = 3.003747 m³, its area 12 m² and its slopedArea 12.014991 m².

4.3 Openings in walls

A door or window hosted by a wall removes, from every layer of its host, the box from along to along + width along the wall and from z0 + offset to z0 + offset + height up, where z0 is the host's base. An opening whose type has an outline removes that outline's prism instead, the outline placed at the opening: a point [x, z] of it, in mm from the opening's start and its bottom, lies along + x along the wall and z0 + offset + z up, or along + width − x along it where the opening's handFlipped is true, which mirrors its shape along its wall as it mirrors its hinges (Part II, 6.5). The opening's own body (frame, leaf, glazing) is reference behaviour in 0.1; how its leaves and lights open is Part II, 6.5's.

Reference: basicSolids (wall branch: spans between openings).

4.3.1 A door's and a window's own body

Part of 0.2. OpenEik 0.1 had it as the proposal frames: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), a door's or a window's own body is reference behaviour, as section 4.3 says, and these members are members a schema of 0.1 does not know, kept (Part I, 10): a door family's leafMaterial (rule 14), and a family's profiles, an openingFamily's frameProfile, mullionProfile and sashProfile and a windowFamily's frameProfile and mullionProfile (rule 15). The section adds no file, and those members: the sizes are those of a type's openingFamily and windowFamily (Part II, 5).

By the steward's decision of 26 October 2026 (open questions, 71), a door or a window hosted by a wall, whose type has an openingFamily or a windowFamily, has a body of its own: its frame, its leaves or sashes in their grid, and its glass, each a box or a prism, or, where its family gives a profile, a member's profile carried along it (rule 15), drawn closed. A door or a window whose type has neither family keeps its body reference behaviour. A declared envelope stands in place of these bodies as of any other (11.1, rule 2).

Every size is in the opening's own frame: x along its wall from the opening's start, along, towards the wall's second profile point, along u (section 3); y across the wall along its left normal n, from the wall's axis, so that the wall's exterior face is at y = T/2 and its interior face at y = −T/2 (4.1); and z up from the opening's bottom, the host's base plus the opening's offset (4.3). W and H are the element's width and height, T its wall's thickness. A box x0…x1, y0…y1, z0…z1 is a prism of its plan rectangle from z0 to z1; an outline (x, z) in the wall's plane carried across from y0 to y1 is a prism across the wall. Every body names its part (1.1.1): frame, sash, leaf or glass.

For an openingFamily f, its sizes frameWidth (fw), frameDepth, sashWidth (sw), sashDepth, mullionWidth (mw), glassThickness, leafThickness, wallOffset and gapInsulation (g):

  1. What is drawn. Each part below is drawn where each of its extents is more than 0.01 mm, and an outline where its area is more than 1 mm². Where the family's sizes leave no room for its frame, a light's sash or glass, or a leaf's glass, or its glass is thicker than what holds it (a frame's or a sash's depth, a glazed leaf's thickness), the compiler derives no body of its own for the opening and says why (frames-fit). A validator SHOULD warn of such a door or window, which it can tell from the records alone.
  2. Across the wall. The frame runs from y = yExt − d to y = yExt, where yExt = T/2 − wallOffset and d = frameDepth; where frameFollowsWall is true, d = T and wallOffset is 0, so that it spans the wall. It is measured from the exterior face whatever the element's flipped says.
  3. The frame. It stands g inside the opening at its sides and its head, and, for a window, at its bottom: fx0 = g, fx1 = W − g, fz0 = g for a window and 0 for a door, fz1 = H − g. Its members are fw wide and as deep as the frame: two jambs, fx0…fx0+fw and fx1−fw…fx1, from fz0 to fz1; a head between them, from fz1−fw to fz1; and, for a window, a sill between them, from fz0 to fz0+fw. A door and a garage door have no sill. The clear opening is ix0 = fx0+fw to ix1 = fx1−fw, and iz0 = fz0+fw for a window, fz0 for a door, to iz1 = fz1−fw.
  4. A window's lights. Its columns divide ix0…ix1 and its rows iz0…iz1: lightsX columns, and lightsY rows, each (span − (n − 1)·mw)/n, mw apart; or, where mullions.x (mullions.z) gives mullions, the fields between them, each mullion centred at fx0 + at (fz0 + at) and as wide as its width, else mw, in the order of at. Between two columns a mullion runs the clear opening's height, iz0…iz1; between two rows a transom runs across its column. Mullions and transoms are frame members, as deep as the frame.
  5. A fixed light, where operable is false: its glass fills it, glassThickness thick, centred in the frame's depth.
  6. An opening light, where operable is true: a sash fills it, from y = yExt − 12 − sashDepth to y = yExt − 12, 12 mm behind the frame's exterior face; its stiles run the light's height and its rails between them, each sw wide. Its glass fills the sash's inside, glassThickness thick, centred in the sash's depth.
  7. A door's leaves. A door of the kind door has lightsX leaves, and one of the kind garageSideHinged its panels, side by side, each (ix1 − ix0)/n wide, from fz0 to iz1, leafThickness thick and centred in the frame's depth.
  8. A glazed leaf. Where a door's glazing is full or upper, a leaf x0…x1 from z0 to z1 has stiles sw wide its whole height and a top rail sw deep; its glass runs from gz0 to z1 − sw, where gz0 = z0 + 2·sw for full and z0 + (z1 − z0)/2 for upper, between the stiles, in lightsY rows mw/2 apart, centred in the leaf's thickness, glassThickness thick; the rest of the leaf, below gz0 and between the rows, is leaf.
  9. A sliding door (operation: slide, Part II, 6.5, rule 4), closed. A single leaf covers the whole opening, x 0…W and z 0…H, on the face of its wall on the side it opens to (Part II, 6.5, rule 2), its face 10 mm from the wall's: from T/2 + 10 to T/2 + 10 + leafThickness, or the mirror of that on the interior. Of several leaves, laid out as rule 7 lays them, counted from the hinge side (Part II, 6.5, rule 1), the first, the third and so on stay and the others slide; with m = yExt − d/2 the frame's middle, the tracks run from m + 3 to m + 3 + leafThickness and from m − 3 − leafThickness to m − 3, 6 mm apart, and a leaf that slides runs on the track on the side the door opens to, a leaf that stays on the other. A pocket door (operation: pocket) is laid out as rule 7 lays it.
  10. A garage door of the kind garageSectional, garageRoller or garageFolding has the frame of rule 3, and one leaf closing its clear opening, ix0…ix1 from fz0 to iz1, leafThickness thick and centred in the frame's depth.
  11. A shaped opening, whose type has an outline (4.3): its frame follows the outline as its wall is cut by it, mirrored where its handFlipped is true. The outline counter-clockwise, its edges are moved inwards by g (the gap's outline), and those again by fw (the frame's inner outline), each corner where its two edges' lines meet; a door's edges that lie on its bottom, within 0.5 mm of z = 0, are not moved and take no member. Each other edge's member is the quad between the edge on the gap's outline and the same edge on the inner one, mitred at the corners. Where the gap's outline does not exist, the outline is the gap's; where the inner one does not, the whole gap's outline is frame, and nothing else is drawn. The lights of rules 4 to 6 lie in the inner outline's bounding box, measured from the gap's outline's (fx0, fz0 its lowest x and z), each clipped to the inner outline, as are the mullions and transoms; a sash is the ring between its light and the light moved inwards by sw, its glass that inner outline. A door's leaves divide the inner outline's bounding box as rule 7 does, each clipped to the inner outline; a glazed leaf's glass is its outline moved inwards by sw, clipped from gz0 (rule 8, z0 the box's bottom) to the box's top, its stiles and rails the ring between, and the rest below the glass leaf. A garage door's leaf is its inner outline.
  12. What stays the product's (class C, reference behaviour): clearances round a leaf, a threshold, a handle or a pull, glazing bars, casings, flashings, an inner sill, the insulation in the gap round the frame, an aluminium-clad frame's two materials, a garage door's panels, curtain and tracks, and a pose that opens a leaf (open, Part II, 6.5). A compiler MAY draw them; where it does, it SHOULD say so.

For a windowFamily (a window's legacy family; its family is a label only):

  1. Its frame is frame wide and depth deep, centred on the wall's axis: a head and a sill the whole width, x 0…W, and the jambs between them. Its panes are panes across and transoms + 1 up, each (W − 2·frame − (panes − 1)·mullion)/panes wide and (H − 2·frame − transoms·mullion)/(transoms + 1) high, with mullions and transoms mullion wide between them, frame members as deep as the frame; and each pane's glass fills it, glassThickness thick, centred on the axis.

For an openingFamily's materials:

  1. A door's leaf of its own material. By the steward's decision of 28 October 2026 (open questions, 89), a family of the kind door MAY give leafMaterial, the material its leaves are of where it is not its frame's: wood, aluminium or steel, as a wood leaf in a steel frame. A leaf (rules 7 to 9) is of leafMaterial, and its frame, its sashes and its mullions of the family's material; without leafMaterial, the leaf is of material too. The material changes no body. A family of another kind has no leaf of its own, its sashes or its panels being of material: it SHOULD NOT give leafMaterial, and a validator SHOULD warn of one (leaf-material).

For a family's profiles:

  1. The profile of a frame, a mullion and a sash. By the steward's decision of 30 October 2026 (open questions, 97), a family MAY give its members' section as a profile: an openingFamily its frameProfile, mullionProfile and sashProfile, and a windowFamily its frameProfile and mullionProfile. A profile is an outline as a custom section's is (Part II, 5.2, rules 8 and 9), {points, voids?}, in mm in the section plane of the members it shapes, its x across a member in the wall's plane and its y across the wall. Its bounding box, its arcs included, MUST be the width by the depth of the members it shapes, each within 0.1 mm: a frame profile's frameWidth by frameDepth (a windowFamily's frame by depth), a mullion profile's mullionWidth by frameDepth (mullion by depth), and a sash profile's sashWidth by sashDepth. A family whose frame follows its wall (rule 2) MUST NOT give frameProfile: its frame is as deep as each wall it stands in. A validator reports a profile that breaks rule 9 of Part II, 5.2 or one of these as an error (frames-profile), and a compiler derives no body of its own for a door or a window of such a family, and says why. Where a profile is given, each member it shapes is the profile carried straight along the member, its bounding box lying on the member's section: its lesser y at the interior face of the frame (or of the sash), its greater y at the exterior face, and its x as each member below says. The members' region in the wall's plane, and every other part, stay as rules 2 to 13 lay them out:

    • The frame. Its members are the ring round the outline the frame stands on, as rule 11 makes it: the rectangle fx0…fx1 by fz0…fz1 of rule 3, the gap's outline of rule 11, or a windowFamily's opening, 0…W by 0…H, of rule 13, each edge's member between the edge and the same edge of the frame's inner outline, moved inwards by its width. The profile's lesser x lies on the edge and its greater x towards the clear opening. Each member is mitred at each of its corners on the line from the corner of the outline it stands on to the same corner of the inner outline; a door's edge on its bottom has no member (rule 11), so that its jambs end square on it.
    • A mullion and a transom (rules 4 and 13) carry the mullion profile from end to end of their box, their ends square, its lesser x at a mullion's side towards the opening's start and at a transom's underside. A mullion or a transom whose own width is not mullionWidth (rule 4) keeps its box, and so does every mullion and transom of a shaped opening, which rule 11 clips to the frame's inner outline.
    • A sash (rule 6) is the ring round its light, as the frame is round the opening: each member between the light's edge and the same edge moved inwards by sashWidth, the profile's lesser x on the light's edge and its greater x towards the glass, mitred at the light's corners. A sash of a shaped opening keeps rule 11's ring.

    A profile shapes no leaf, and no stile or rail of a glazed leaf (rule 8); the glass stands where rules 5, 6 and 13 put it.

These are class-A rules, held to the tolerances of section 10: each part's bodies within 0.1 mm of the rule's, and its volume within a relative 10⁻⁶. What rule 12 leaves to the product is not compared. A compiler MAY draw a profile's arcs as straight pieces, as a custom section's (Part II, 5.2, rule 10).

Reference: openingSolids, shapedOpeningSolids and familyInWall in src/core/elements/openings.ts; insetPolygon, ringQuads and clipBox in src/core/elements/openingOutline.ts; lightSpans in src/core/elements/mullions.ts; slidingLeaves in src/core/elements/slidingDoors.ts; windowSolids in src/core/geometry/familyGeometry.ts. In @openeik/tools: frameParts, and for rule 15 familyProfiles, profiledRing and profiledBar, in packages/openeik/src/compile-frames.js, placed by compileOpening in compile.js.

4.4 Pitched roofs

Given a roof with a roof payload, z0 = elev(levelId) + offset, and its outline's edges e_1 … e_m (edge i from profile point i to the next):

  1. Each edge with definesSlope: true and slope σ_i (degrees, 0–75) defines the plane z_i(p) = z0 + tan(σ_i) · (n_i · (p − a_i)), where a_i is the edge's start and n_i its unit normal pointing into the outline. So the top face meets z0 along every slope-defining edge line.
  2. The roof's top surface is the lowest of these planes at each point: z(p) = min_i z_i(p). With no slope-defining edge it is the plane z = z0.
  3. The surface extends past each edge by that edge's overhang (mm, in plan).
  4. Layers are measured at right angles to the face they lie under: a layer of thickness t under a face of gradient (g_x, g_y) lies t · √(1 + g_x² + g_y²) lower, vertically.
  5. Where edges of the roof are trimmed to a wall's face, its extent is trimmed as 4.5 says, and its planes stay those of its recorded edges.
  6. Shafts cut its layers' bodies as 4.2, rule 8 says, and a gutter hangs on its edges as 4.7 says.

Reference: roofFaces in src/core/elements/roofs.ts (one plane per slope-defining edge, faces clipped to the lower envelope, factor = hypot(1, gx, gy)); roofSolids (each layer's vertical depth is its thickness times the factor).

4.5 Outline edges trimmed to a wall's face

Given an element of category slab, foundation, ceiling or roof with edgeAttachments (Part II, 6.7), and, for each attachment, its wall, with P0, u and n as in section 3 and thickness T: its type's total layer thickness, or its width where its type has no layers (4.1):

  1. The face is a line in plan: the exterior face is n · (p − P0) = T/2, the interior face n · (p − P0) = −T/2. A point's distance from the face, positive away from the wall, is δ(p) = n · (p − P0) − T/2 for the exterior face and δ(p) = −n · (p − P0) − T/2 for the interior face.
  2. The trimmed outline is the part of the outline where δ(p) ≥ 0 for every attachment. Each face cuts along its whole line, not only along the wall, and keeps the side away from the wall; the order of the attachments does not matter.
  3. A slab's, foundation's, ceiling's or flat roof's bodies (4.2) are built on the trimmed outline: its layers, each layer's inset measured from the trimmed outline, and its voids cut through them.
  4. A pitched roof's planes (4.4, rules 1 and 2) and overhangs (rule 3) come from its outline as recorded: a trimmed edge's plane still starts at the recorded edge, so its slope does not move. The roof's extent, its outline extended by the overhangs, is trimmed as rule 2 says, and its layers (rule 4) fill what is left.
  5. Only the element's own bodies and their quantities are trimmed. Other rules read the outline as it is recorded: the footprint that a column's base or a wall's top attaches to (5.2, 5.3), the edges a railing runs along (11.2), the edge a slab edge element runs along and the edges its host is thickened under (4.6), the eave line a gutter hangs on (4.7), and the edge ids. A thickened edge's bands are the host's own bodies, and are trimmed.

So the trim follows the wall: moved, turned, or made thicker or thinner, the wall moves the line its face lies on, and the element's bodies stop there, with neither record changed (Part II, 6.7 shows an example).

Reference: wallFaceLine, edgeTrimDistance, clipPolygon and trimmedFootprint in src/core/elements/wallTop.ts; basicSolids (its horizontal branch builds on trimmedFootprint) and layeredBodies in src/core/geometry/geometry.ts; roofSolids (footprintTrims) in src/core/elements/roofs.ts; the area in quantities and floorPerimeterM (src/core/elements/elementMeasures.ts), from trimmedFootprint.

4.6 Slab edge elements

By the steward's decision of 26 October 2026 (open questions, 74), as the reference implementation builds them. Given a slab edge element on the edge hostEdgeId of its host's outline (Part II, 6.1, rule 6), that edge running from point a of the outline as recorded to the next, b, with L = |b − a|, u = (b − a)/L and n_in its unit normal into the outline ((−u_y, u_x) where the outline runs counter-clockwise, (u_y, −u_x) where it runs clockwise):

  1. Its section is its type's edgeProfile (Part II, 5.6). Without one it is a rectangle of the element's width by its height, anchored at structureBottom. A type that is a framing family (Part II, 5.4) draws its own section, its family's: these rules are not its rules, but for where its section lies and how its ends are cut, which section 6.6, rule 11, of 0.2, takes from them.
  2. The section's rectangles, in x, across the edge into the host from the edge's line, and z, up from the anchor: a rectangle from x = −width/2 to width/2; an L, its upstand from x = −web to 0, and its bottom leg from 0 to width − web and from z = −depth to −depth + flange; a U, an L whose leg ends at width − 2·web, and a second upstand from there to width − web. A rectangle and an upstand run from z = −depth to 0.
  3. Its facing, where the section has one, is a rectangle outside it: from x = x_out − facing.thickness to x_out, where x_out is the least x of the section's rectangles, and from z = −facing.depth, else −depth, to 0.
  4. Its anchor. The section's z = 0 lies at z_a + verticalOffset (0 without it). z_a is the host's top, elev(host.levelId) + host.offset, for slabTop; for structureBottom it is the underside of the host's structure, the host's top less the thicknesses of its type's layers, top first, down to and including its first layer whose function is structure, or less the host's height where its type has no such layer.
  5. Where it lies. The section's x lies at the reach r(x) = horizontalOffset + x in from the edge's line (horizontalOffset 0 without it), or horizontalOffset − x where the element is flipped. At the edge's start that is the point a + r n_in + k_a r u, and at its end b + r n_in + k_b r u, where k_a and k_b are 0 at a square end and the mitre's slide (rule 6) at a mitred one. Each rectangle of rule 2, and the facing, is the prism, in plan, of the four points of its two x at the two ends, between the heights of its two z: one body each, along the whole edge.
  6. Its ends. An end is mitred where miterEnds is true, square where it is false, and, without it, mitred where the edge round that corner carries another slab edge element that mitres by itself (Part II, 6.3) and the element's own type is no framing family. The corner at the edge's start is turned by the edge before it, at its end by the edge after it. With n_c that edge's unit normal into the outline, a mitred end's slide is k = (1 − n_in · n_c) / (u · n_c): every point of the end lies on the bisector of the host's corner, whatever the element's offsets. A mitred end stays square where |u · n_c| < 10⁻⁶ (the two edges run straight on) and where |k| > 4 (the corner is sharper than 2·arctan(1/4), about 28.07°, and the cut would run on more than four times the section's reach). Both ends stay square where, between them, the element would be 1 mm long or less at the least x of its section and facing or at its greatest: where L + (k_b − k_a) r(x) ≤ 1 for either, k being 0 at an end that is square by the rules before.
  7. A chamfer cuts the upstand's top inner corner (an L's, or a U's first upstand's): that upstand is instead the polygon (−web, −depth), (0, −depth), (0, −chamfer.height), (−chamfer.width, 0) and, where chamfer.width < web, (−web, 0), whose points lie at its two ends as rule 5 places them: a convex body between the two end polygons.
  8. These are the element's bodies, and its volume theirs. Its notches are rules 12 to 14's, of 0.2.

What it does to its host, rules of the host's own body (4.2, rules 6 and 7):

  1. Thickened edges. A slab or a foundation is thickened under each edge of its outline that carries a slab edge element whose type's edgeProfile has a beam; two on one edge thicken it by the wider and the deeper. Its structure is its type's first layer whose function is structure, or, without layers, its one body; a slab whose type has layers and none of them structure is not thickened. Let z1 be its top, s its structure's underside, t its structure layer's inset (0 without one), and each edge of its outline as recorded moved in by a distance: its line moved into the outline by it, each end where the moved lines of it and its neighbour meet, or, where the two run in line and are moved by different distances, square at their common point.
    • Each beam's depth gives a level z1 − depth; those more than 0.5 mm under s, from the highest, close bands, the first from s down to its level and each other from the level before it down to its own.
    • In a band down to a level λ, a beam reaches through where z1 − depth ≤ λ + 0.5 and its width is more than t. With its edge moved in by its width and every other edge by t, the four-sided strip between its edge moved in by t and moved in by its width is a body of the structure layer, from the band's top down to λ.
    • A layer under the structure, one whose top lies 0.5 mm or less over s, is cut at the levels within it, and each part takes the outline that its edges moved in leave: the edges of the beams that reach through the part's underside by their widths, where these are more than the layer's own inset, and the others by its inset.
    • The strips and the layers' outlines are trimmed where the host's edges are trimmed to a wall's face (4.5, rule 3), and the host's voids cut through the layers (4.2, rule 4).
  2. A chamfer's fill. The host fills the corner a chamfer cuts off (rule 7) with its structure, where the corner lies under its structure's top (its top less its layers over its first structure layer; its top without one): the triangle (0, −chamfer.height), (0, 0), (−chamfer.width, 0) of the element's section, cut level at that height where it lies within it, placed as rule 7 places the upstand. It is a body of the host's structure layer, else of its first layer.
  3. A fit. At each depth a beam reaches, the edges moved in by the widths of the beams reaching that depth, and the others by t, MUST close a simple outline that turns as the host's does, of 1 mm² or more, and the host's voids MUST lie within it. A validator reports a host whose thickened edges do not fit; a compiler builds it without them, and says so.

An L 600 wide and 500 deep, its web and its flange 100, at slabTop, on the first edge of a slab from [0, 0] to [6000, 0], whose outline runs counter-clockwise: its upstand stands from y −100 to 0 and its leg from y 0 to 500, from 500 under the slab's top up. Where another such element runs up the next edge from [6000, 0], both mitre: k = −1 at the corner, so the upstand's outer face runs on to [6100, −100] and the leg's inner face stops at x 5500.

Its notches, rules 12 to 14:

Part of 0.2. OpenEik 0.1 had them as the proposal adopted (Part II, 14): in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), a slab edge element's notches is a member a schema does not know (Part I, 10), and no notch is cut.

  1. Notches. By the steward's decision of 30 October 2026 (open questions, 115), a slab edge element MAY carry notches, at most 20 stretches {from, to, depth} where its outside upstand is cut down: from and to in mm along its host's edge from the edge's first point a, and depth in mm down from the section's top, z = 0. Only an L or a U has an outside upstand, its section's first rectangle (rule 2): an element whose section is a rectangle, or whose type is a framing family, MUST NOT carry notches. A notch MUST run from from to at least from + 10, MUST end on its edge (to ≤ L), MUST keep above the flange (depth ≤ depth − flange, the section's), and, where the section has a chamfer (rule 7), MUST be at least as deep as the chamfer is high, so that it takes the chamfer whole; and two notches MUST NOT overlap. A validator reports a breach (notch-invalid). A compiler builds such an element without its notches, and says so. Only a slab edge element carries notches (Part II, 6.3).
  2. Its bodies. The edge is cut into stretches at the notches' ends: from its start to its first notch, each notch, each stretch between two notches, and from its last notch to its end, a stretch shorter than 10⁻⁶ mm being none. Each stretch lies as rule 5 places the element, between its two ends: an end that is the element's own, at 0 or at L along the edge, is that end, square or mitred by rule 6; an end within the edge is cut square, at s along it, its points a + s·u + r(x)·n_in. The outside upstand is a body for each stretch, from z = −depth up to the section's top less the stretch's depth (a whole stretch's being 0), none where that leaves nothing; its chamfer (rule 7) is cut on a whole stretch alone. The facing (rule 3), where the section has one, is a body for each stretch whose depth is less than the facing's, from its foot up to the section's top less that depth. Every other rectangle of the section is one body along the whole edge, as rule 5 says.
  3. The host's fill. A chamfer's fill (rule 10) is cast along the whole stretches alone: a notch takes the wedge with it.

The L of the example above, alone on its edge, with a notch {from: 1000, to: 3500, depth: 150}, stands its upstand as three bodies: from [0, 0] to [1000, 0] along the edge and from [3500, 0] to [6000, 0] its whole depth, and between them from 500 under the slab's top up to 150 under it. Its leg is one body, as before.

Reference: profileRects in src/core/elements/edges.ts; edgePlacement and mitresAlone in src/core/elements/edgePlacement.ts; chamferProblem, upstandSection, fillSection, sweptSection, chamferFills and withChamferFills in src/core/elements/edgeChamfer.ts; movedEdges, edgeBeamProblem and thickenedBodies in src/core/elements/edgeBeams.ts; basicSolids (its slab edge branch) in src/core/geometry/geometry.ts; for rules 12 to 14, notchProblem and notchedEdgeBodies in src/core/elements/edgeNotches.ts, and edgeSpans and spanPlacement in edgePlacement.ts (Stomra work 487).

4.7 Gutters

By the steward's decision of 26 October 2026 (open questions, 75), as the reference implementation builds them, but for rule 6, which it finds at 25 points. Given a gutter on the edge hostEdgeId of its roof's outline (Part II, 6.1, rule 13), its type's gutterProfile (Part II, 5.6), and its roof's top surface (4.4, rules 1 and 2), the level plane z = elev(levelId) + offset for a roof without a roof:

  1. Its eave line. Each edge of the roof's outline, its line moved out of the outline by its overhang (4.4, rule 3; 0 for a roof without a roof), meets the moved line of the edge before it at a corner, or, where the two run in line, at the start of its own moved line. The gutter's eave line runs from the corner at its edge's start to the corner at its end, whatever trims the roof (4.5, rule 5).
  2. Its height. At each end of each piece of the gutter (rule 6), its section's z = 0 lies on the roof's top surface at that point of the eave line; between the two ends, the piece runs straight.
  3. Its section, in x out from the eave line, square to it in plan, and z up: a halfRound is the half annulus about (r, 0), r = diameter/2, between the radii r and r + thickness, under z = 0: its back lip on the eave line and its rim level with the roof's top. A box is its bottom, from x = 0 to width and from z = −depth − thickness to −depth, and its two walls, from x = 0 to thickness and from width − thickness to width, from z = −depth to 0. The half-round's arc is the rule; the straight pieces that stand for it are a compiler's, as a pipe's circle is (12.3, rule 5).
  4. Its offsets. Its type's gutterProfile.horizontalOffset and its own horizontalOffset add, and move its section out from the eave line; where it is flipped, they move it the other way, in towards the roof. Its type's gutterProfile.verticalOffset and its own verticalOffset add, and raise it.
  5. Its body is its section carried straight from one end of each piece to the other: its ends are square to the eave line in plan.
  6. Where it is buried. Where its roof's roof.joinGroup names a group, the gutter is left out wherever its eave line lies, in plan, strictly inside the outline of another roof of the group extended by its overhangs (rule 1), and that roof's top surface there lies more than 20 mm over its own roof's. It is found exactly along the line, from the outlines and the planes. A piece of 10 mm or less in plan that is left is left out too, and a gutter buried along its whole line has no body.
  7. Its quantities (informative, as section 9): its length is its pieces' length in plan, and its volume its section's area by that length, a half-round's (π/2)((r + thickness)² − r²).

A downpipe's path from its gutter stays reference behaviour (7.2).

A half-round of 150 and 3 on the first edge of a roof from [0, 0] to [8000, 0] of level 1 (3000), that edge defining a slope of 30° with an overhang of 400, its two neighbours an overhang of 300: its eave line runs at y −400 from x −300 to 8300, at the roof's top there, 3000 − 400 tan 30° = 2769.06; its body reaches from y −397 to −553 and from z 2691.06 to 2769.06, and its volume is (π/2)(78² − 75²) mm² by 8.6 m.

Reference: gutterPath, halfRoundCells, boxCells, gutterProfileCells, unburiedSegments and gutterSolids in src/core/elements/gutters.ts; roofDomain and roofFaces in src/core/elements/roofs.ts.

5. Tops, bases and heights

5.1 Heights from the top constraint

For a wall, shaft or stair with top.constraint = {levelId, offset}, the height is elev(levelId) + offset − base, which MUST be positive. With "unconnected" or no top, it is the element's height.

Reference: constrainedWall in src/core/elements/attachments.ts; shaftRange, stairRange.

5.2 Attached column bases

For a column with base.attach = {hostId, face, offset}:

  1. The host MUST be a foundation or a slab whose footprint contains the column's centre (on the boundary counts).
  2. The host's face is its top (face: "top") or its underside, T_host below its top. For a flat host the face is the plane elev(host.levelId) + host.offset − depth.
  3. The derived base is the lowest face plane at the column's centre plus offset (a negative offset embeds the column below the face).
  4. The column's top stays where its record puts it, elev(levelId) + offset + height; the derived length is top minus derived base, which MUST be positive.

Reference: attachedBaseZ and resolvedColumn in src/core/elements/columnBase.ts.

5.3 Attached wall tops

For a wall with top.attach = {hostId, face, offset, mode}:

  1. The host MUST be a roof, ceiling, slab or foundation, and its footprint MUST cover the wall's whole centreline in plan, to within 5 mm (section 5.3.1).
  2. The face's depth below the host's top is: 0 for "top"; the host's thickness for "underside"; for {layerId}, the sum of the host's layer thicknesses from the top down to and including that layer.
  3. The face is, for a flat host, the plane at the host's top minus the depth plus offset; for a pitched roof, each of the roof's planes (4.4) lowered by depth · √(1 + g_x² + g_y²) and raised by offset.
  4. mode: "raking" (the default): along the centreline the wall's top is the lowest face plane at each point. It is piecewise linear, with breaks where two planes cross over the centreline.
  5. mode: "level": the top is flat, at the lowest point of the face over the wall's core band.
  6. The face MUST rise above the wall's base somewhere along the centreline, and MUST NOT fall below the base by more than the host's own depth.
  7. The wall's height and wallOutline stay as recorded; they are the unconnected values, used again when the wall is detached.

Reference: attachedWall, derivedTop and hostFacePlanes in src/core/elements/wallTop.ts and src/core/elements/attachments.ts; hostCoversBaseline.

5.3.1 A wall's top at its host's edge

Part of 0.3. OpenEik 0.2 had it as the proposal wallTopTolerance (open questions, 123): a model of 0.2, read by 0.2's rules (Part I, 9.6, rule 3), is read by it only where a caller asks for it (decision D12).

A wall whose top is attached to a host is held to rule 1 of section 5.3 to within 5 mm in plan: a wall drawn to a roof's or a slab's edge stays attached when the edge or the wall's end moves by a few millimetres, as rounding moves them. In a model of 0.2 that does not ask for the proposal, rule 1 holds as written.

  1. Covered to within 5 mm. The host's footprint, as rule 1 of section 5.3 reads it (a pitched roof's with its overhangs, a round footing's circle), MUST cover the wall's whole centreline in plan to within 5 mm: no point of the centreline lies more than 5 mm outside it, measured in plan. A validator reports a centreline that runs further past it as rule 1 does (top-host), and a compiler gives such a wall its unconnected top.
  2. The face past the edge. Where the centreline runs past the footprint's edge, the face of rule 3 is carried on past it: a flat host's face is the same plane there, and a pitched roof's each of its planes, lowered and raised as rule 3 says. The top of rules 4 and 5 is read from that face along the whole centreline, and rule 6 holds along all of it.
  3. The rest stands. Rules 2 to 7 of section 5.3 are unchanged.

Reference: ROOF_EDGE_SLACK, baselinePastRoof and hostCoversBaseline in src/core/elements/wallTop.ts. In @openeik/tools: WALL_TOP_TOLERANCE and wallTopRules in packages/openeik/src/rules.js, and wallTop in packages/openeik/src/compile.js, for a model of 0.3, and for one of 0.2 asked for with proposals: ["wallTopTolerance"].

6. Members: the structural subset

This section fixes what an analysis tool needs to read beams and columns.

6.1 Columns

Given a column with centre c (its profile's first point), base z0 = elev(levelId) + offset (or the attached base, 5.2) and length H = height (or the derived length):

  1. Without a section, the body is the square prism of side width centred on c, over [z0, z0 + H].
  2. With a section, the section's width runs along x and its depth along y, centred on c, swept over [z0, z0 + H].
  3. Either is then turned by turn degrees counter-clockwise in plan about c.
  4. The column's reference line runs from (c, z0) to (c, z0 + H) (6.5). Its start is its base.

6.2 Beams

Given a beam from P0 to P1, z0 = elev(levelId) + offset and r = endRise (0 when absent):

  1. The beam's set-out line runs from (P0, z0) to (P1, z0 + r). It is the centreline of the member's underside, where the record places it. Its length is Ls = √(L² + r²); let sin α = r / Ls, cos α = L / Ls.
  2. Without a section, the body is the prism over [z0, z0 + height] of the rectangle of width width centred on the plan axis. A beam with neither a section nor a framing family MUST NOT have a non-zero endRise.
  3. With a section of width w and depth d, the section stands at right angles to the set-out line: the point at distance s along the line (0 ≤ s ≤ Ls), x across it (−w/2 ≤ x ≤ w/2) and v up from the underside (0 ≤ v ≤ d) is at plan position P0 + u (L s / Ls) + n x − u v sin α and elevation z0 + r s / Ls + v cos α. The element's own width and height do not size it.
  4. With a taper (rectangular sections only), the depth varies linearly from d at the start to taper.endDepth at the end; the underside follows the set-out line. A rectangle's radius does not round a tapered beam: its body is the wedge with square corners.
  5. With a roll, the body is turned roll degrees about the reference line (6.5): the line through the middle of the section, parallel to the set-out line. It turns right-handed about the line's direction from the start to the end, so that a positive roll turns the section's x (across, along n) towards its v (up), as the reference implementation and @openeik/tools turn it.
  6. A beam whose type has a framing family takes its body from the family (Part II, 5.4), with Length = Ls. Section 6.6, of 0.2, builds that body.

6.3 What sizes a member

Section widthSection depthLength
Beam with a sectionsection.widthsection.depthfrom the profile and endRise
Beam without a sectionwidthheightfrom the profile
Column with a sectionsection.width (along x)section.depth (along y)height
Column without a sectionwidthwidthheight

Reference: basicSolids (column and beam without a section), memberSolids, squareMemberSolids, beamMiddleLine and turnSolids in src/core/geometry/familyGeometry.ts; validateElement ("Sloped members require a beam section").

6.4 Member joins

A member's joins list says how each of its ends meets another member or plate: {id, to, at, kind, gap?, extend?}.

  1. at is "start" or "end" of this member; a column's start is its base.
  2. kind: "butt": this member's end stops at the face of to, held back by gap (mm). With extend: false the end is only cut, never lengthened to the face.
  3. kind: "mitre": both members are cut on the plane that bisects their reference lines, so they meet on one plane; one record does both sides. to MUST be a beam or column, and extend: false MUST NOT be used.
  4. A join does not change either member's record: the set-out line and the stored ends stay as recorded. The cut and the lengthening are derived.
  5. Join ids MUST be unique on a member; a member MUST NOT be joined to itself; to MUST exist.
  6. Joins and trims are computed on the members' reference lines (6.5), not on their axes.

Reference: memberJoinSchema and validateMemberJoins in src/core/elements/memberJoins.ts.

6.5 Reference lines and axes

A member has two lines:

  • Its reference line runs through the middle of its section: the centre of the section's bounding box, width by depth. The record places the member by it: a column stands on its profile point, and a beam's section is centred across its plan axis and stands on its set-out line (6.2). A roll turns the body about it (6.2), and joins and trims are computed on it (6.4). The rules of 6.1 and 6.2 already place every body this way; no existing body moves.
  • Its axis, the structural axis, is the centroidal line of its body as built, after the roll: the reference line moved by the section's centroid offset. Analysis tools and analytical exports MUST use the axis. They MAY publish the section's shear centre with it.

For a section that is symmetric about both its own axes the two lines coincide.

  1. A column: the reference line runs from (c, z0) to (c, z0 + H), where c is the centre (the profile's first point), z0 the base (or the attached base, 5.2) and H the length.
  2. A beam with a section of depth d: the reference line runs through the middle of the section, d/2 above the set-out line measured square to the member, along the member's true length Ls = √(L² + r²). It runs from (P0 − u (d/2) sin α, z0 + (d/2) cos α) to (P1 − u (d/2) sin α, z0 + r + (d/2) cos α), with α and r as in 6.2. Across the member it lies on the plan axis.
  3. A tapered beam: the reference line runs through the middle of the section at each end, using the section's depth at the start and taper.endDepth at the end.
  4. A beam without a section: the reference line runs height / 2 above the underside, from (P0, z0 + height/2) to (P1, z0 + height/2) (such a beam has no endRise).
  5. The axis. A section's centroid offset is the centroid of the section's area less the middle of its bounding box, in the section's own coordinates: (e_x, e_v) for a beam, with x across the member (along n) and v up from the underside, as in 6.2; (e_x, e_y) for a column, along its section's x and y before its turn. The offset turns with the body, by a beam's roll about its reference line or by a column's turn in plan, giving (e_x', e_v') or (e_x', e_y'). The axis is the reference line moved by the turned offset: for a beam, each point moves by n e_x' − u e_v' sin α in plan and by e_v' cos α up; for a column, by (e_x', e_y') in plan.
  6. A rectangle (with a radius or without), I, RHS, CHS or round section, a tapered beam and a beam without a section have a centroid offset of zero: their axis is their reference line, and a roll does not move it. A T or 2L section has a centroid offset of zero across and not zero in v (in y for a column), set by its sizes, its radii and, for a 2L, its gap (Part II, 5.2). An L or U section, and a sheet section in general, have a centroid offset that is not zero, set by the section's legs or flanges, its corner and its radii. A custom section's centroid offset is its region's centroid less the middle of its outline's bounding box (Part II, 5.2, rule 10), zero only where they meet.

A beam built from a framing family (6.2, rule 6) takes its body from the family, a class-C rule (section 2), and 0.1 defines no reference line and no axis for it: rules 2 to 6 are not its rules. The steward deferred its reference line and axis to 0.2 on 15 October 2026, and decided on 21 October 2026 what 0.2 says: its reference line is the middle of its height over its set-out line, and it has no single axis, an analysis taking each of its member parts as a member with its own axis (open questions, 10). A reader that cannot place those parts, because it does not evaluate the family's expressions (Part II, 5.4), leaves such a beam out of an analysis and says so, rather than standing a member of its own in its place (decided on 22 October 2026 for 0.2). Section 6.6, of 0.2, places and builds the parts as class-A rules, and section 6.5.1 gives the beam its reference line.

Reference: beamMiddleLine in src/core/geometry/familyGeometry.ts computes rule 2; memberStructuralAxis in src/core/geometry/memberStructuralAxis.ts, with sectionProperties.ts, computes rule 5.

6.5.1 A framing family's beam

Part of 0.2. OpenEik 0.1 had it as the proposal framing, with section 6.6: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), a beam built from a framing family has neither line, as section 6.5 says. The section adds no file and no member. It was written as a proposal of its own, framingLines, and folded into the proposal framing; the steward confirmed the fold on 30 October 2026 ("allt ja enligt din rekommendation"; open questions, 10).

By the steward's decisions of 21 and 22 October 2026 (open questions, 10), the second asked by an analysis application. A beam whose type has a framing family (6.2, rule 6) takes its body from the family, whose member parts the family's expressions place (Part II, 5.4).

  1. Its reference line is the middle of its height over its set-out line (6.2, rule 1): from (P0, z0 + height/2) to (P1, z0 + r + height/2), where z0 and r are section 6.2's and height is the element's, so that it rises by the beam's endRise as its set-out line does. Its joins and trims are computed on it (6.4, rule 6), and the beam's record places it, as any beam's does.
  2. It has no single axis. A family's parts, a truss girder's chords and diagonals or an I-joist's flanges and web, have no one centroid. An analysis takes each of the family's member parts as a member of its own, with its own axis through the middle of its section, where the family's expressions place it (Part II, 5.4; section 6.6, rule 4).
  3. A reader that cannot place the parts, because it does not evaluate the family's expressions, MUST leave such a beam out of an analysis, and SHOULD say so. It MUST NOT stand a member of its own on the reference line in its place: that member would carry loads with a section the beam does not have.

A purlin of a framing family, 220 mm high, from [0, 0] to [6000, 0] on a level at 3000 with an endRise of 600, has the set-out line from (0, 0, 3000) to (6000, 0, 3600) and the reference line from (0, 0, 3110) to (6000, 0, 3710).

6.6 A framing family's parts

Part of 0.2. OpenEik 0.1 had it as the proposal framing: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), the body of a beam or a slab edge element whose type is a framing family is reference behaviour, as section 6.2, rule 6, section 4.6, rule 1, and section 7.2 say. The section adds no file and no member: the family is its type's framing, and its parameters the element's framingParameters (Part II, 5.4).

By the steward's decision of 26 October 2026, amended on 29 October 2026 (open questions, 83), a beam or a slab edge element whose type is a framing family has the bodies of its family's parts: the family's expressions, which Part II, 5.4 specifies, are evaluated with the element's parameters, and each part is built from what they give. Nothing stands in for a family that cannot be.

f is the type's framing. A point of the family's plane is (x, z), in mm, and cross(a, b, p) = (b_x − a_x)(p_z − a_z) − (b_z − a_z)(p_x − a_x), which is positive where p lies to the left of the line from a to b. To clip a polygon to one side of a line is to walk its edges in order, keeping each point on that side, within 10⁻⁹, and the point where an edge crosses the line.

  1. No stand-in. A family that cannot be evaluated by rule 2, or whose parts cannot be built by rules 3 to 9 and 11, is a fault of the model. The compiler derives no body for the element, and says why; nothing stands in for its bodies. A validator reports it as an error, which it can tell from the records alone: framing-expression where an expression is invalid or a parameter breaks Part II, 5.4's rules, and framing-part where its Length is out of its range, two of its parts share an id, or a part cannot be built.
  2. The scope. Length is a beam's set-out line's length, Ls (section 6.2, rule 1), or a slab edge element's edge's length in plan (section 4.6), and lies from 1 to 100 000 mm. Each parameter of f takes the element's framingParameters[name] where its scope is instance and the element gives it, and its value otherwise; f keeps Part II, 5.4's rules on parameters, and its parts' ids are unique. Every expression of these rules is evaluated in this scope as Part II, 5.4 states, and is valid.
  3. Which parts exist. A part whose when is given exists while its value is greater than 0. A part that does not exist has no body, and cuts no other part (rule 6).
  4. A part's outline. A member part runs from its point a to its point b, at least 1 mm apart, and its depth d lies from 1 to 10 000 mm. With t = (b − a)/|b − a| its axis and m = (−t_z, t_x), its outline is the rectangle a − (d/2)m, b − (d/2)m, b + (d/2)m, a + (d/2)m, so that its line from a to b runs through the middle of its depth, and its centre is the middle of a and b. A profile part's outline is its outline's points, and its centre the centroid of the area they bound. Either outline is simple: of three points or more, each two consecutive points at least 0.1 mm apart, no two of its edges crossing or touching but neighbours at their common point, and of 5 mm² or more. A part's width lies from 1 to 10 000 mm on a beam, and from 1 to 100 000 mm on a slab edge element.
  5. Its straight cuts. Each of a part's cuts, the line through its two points, which lie at least 10⁻⁶ mm apart, and for a top chord each of its tail's line cuts (rule 7), keeps the side of the line that holds the part's centre, which does not lie on the line: the outline is clipped to that side, by each line in turn. The result, cleaned (rule 8), is the part's cut outline. A part cuts others by its cut outline, never by what its own joints leave of it (rule 6), so that the order of the parts does not matter.
  6. Its joints. Each part a part's cutBy names is another part of f. Where that part exists and its cut outline has three points or more, that outline is convex, its turns all one way within 10⁻⁶, and is taken out of the part: for each of its edges in turn, what lies outside the edge's line is set apart as a piece, where it has three points or more and more than 10⁻⁶ mm², and what lies inside is carried on to the next edge; what lies inside every edge is gone. The parts cutBy names are taken out in its order, each from every piece the ones before it left.
  7. A tail. A family's tail cuts each member part whose role is topChord and whose axis rises, t_z more than 10⁻⁹ (its left tail), or falls, t_z less than −10⁻⁹ (its right tail); a level top chord has none. f then has the parameters Pitch, HeelHeight, OverhangLeft and OverhangRight. With o that side's overhang, OverhangLeft or OverhangRight, and TipTop = HeelHeight − o·tan(Pitch), a tail's expressions are evaluated in the scope of rule 2 with Overhang = o and TipTop added (Part II, 5.4). A point (u, v) whose origin is tip lies at (−o + u, TipTop + v), and one whose origin is bearing at (u, v); on the right tail its x is mirrored to Length − x. A line cut's origin is tip where it gives none, and a notch's bearing. A line cut is one more straight cut of the part (rule 5). A notch, its outline convex and of 1 mm² or more, is taken out of the part's pieces after its joints, as rule 6 takes a part out.
  8. Its pieces. A polygon is cleaned by dropping each point within 0.5 mm of the next, and then each point b whose neighbours a and c leave |cross(a, b, c)| no more than 10⁻³·|c − a|. Each of a part's pieces is cleaned, and one of fewer than three points, or under 200 mm², is dropped. A part that exists keeps at least one piece, and each of its pieces is simple (rule 4).
  9. Nail plates. A plate part that exists has a thickness from 0.5 to 20 mm and an outline that, cleaned, has three points or more and is convex; each part its on names is a member part of f. On each piece of each such member, in turn, the piece clipped to the inside of each edge of the plate's outline, cleaned, where it keeps three points and 100 mm² or more, is a piece of the plate on each face of the member: across the plane from w/2 to w/2 + thickness on either side, w the member's width. Its pieces are simple (rule 4).
  10. On a beam. The plane is Part II, 5.4's, over the beam's set-out line: with P0, u, z0 and α as section 6.2 has them, and n = (−u_y, u_x), the point (x, z) of the plane at y across it lies at plan position P0 + u (x cos α − z sin α) + n y, and at elevation z0 + z cos α + x sin α. A part's piece lies from y = −w/2 to w/2, w its width; a plate's as rule 9 says.
  11. On a slab edge element. The plane is the element's section along its host's edge: x across the edge into the host, where section 4.6, rule 5 places a section's x (by its horizontalOffset, its flipped and its mitred ends), and z up from its anchor, z_a + verticalOffset, where z_a is as section 4.6, rule 4 gives it for f's edgeAnchor, and slabTop where f gives none; its type's edgeProfile is not used. Its ends are mitred only where its miterEnds is true (section 4.6, rule 6), and stay square where that rule keeps them square, the least and the greatest x of its section being those of its parts' pieces. A part's piece runs along the edge for r = min(w, L), w its width and L the edge's length, centred on the edge's middle: each of its points lies at the stations L/2 − r/2 and L/2 + r/2 along the line between its places at the edge's two ends. A plate has no faces to lie on there: a slab edge element's family has no plate part that exists.
  12. Bodies and quantities. Each piece is a body, closed: its polygon at each end of its extent across the plane, or along the edge, joined by its sides. Each body names its part by the part's id. The element's volume is the sum of its bodies' volumes: on a beam, each polygon's area by its width; on a slab edge element, each body's own, its polygon's area by its run r where its ends are square, and less or more where a mitre slants them.
  13. Members. Where a member part or a plate has a piece, every part that has one is a member of its element, written apart from it (Part VIII, 1.6): keyed <element id>:<part id>, which is unique in the model, or the model cannot be printed (Part VIII, 1.6); named by its element's name (its id where it has none), its role in words (Part VIII, 1.6) and its id, as T1 · top chord topL, a profile part by its id alone, as T1 · seat, and a plate as T1 · nail plate p1; and with its section, its width by its depth, each to a tenth of a millimetre, as 45 × 220, where a member part's depth is its own and another part's its pieces' height in the plane. Otherwise its parts, all profile parts, are the element's own body.
  14. What a family may say. Rules 2 to 13 build every member Part II, 5.4 gives a family: its parameters and their scopes; its member, profile and plate parts, with when, cuts, cutBy and on; its tail, with both kinds of cut and both origins; and its edgeAnchor. Its trussStyle names what kind of family it is, and its previewLength is an editor's: neither changes a body. What the language lets a family say that these rules cannot build is a finding of rule 1, never a stand-in: an expression that is invalid or out of its range, a member shorter than 1 mm, an outline that is not simple, a cut through a part's centre, a joint by a part that is not convex or is not there, a tail without the parameters it needs, a part cut away, a plate off its members, and a plate on a slab edge element.
  15. What stays reference behaviour (class C). The framing an engine lays out from a wall, a floor or a roof, which the model does not hold as a family's parts (studs, plates, noggings, headers, joists, and the trusses along a roof), is not this section's: by the steward's decision of 29 October 2026 (open questions, 83) section 7.4 lays it out, its trusses built as these rules build a family's parts. A family's beam's joins (section 6.4) do not cut or lengthen its parts, nor do its connection, its own holes or the holes of a bolt group through it: the compiler builds its parts without them, and says so (not-derived). Its roll and its taper turn and taper a section (section 6.2, rules 4 and 5), which it does not have, and are not applied: the compiler says so (roll-family, taper-shape). Its reference line, and its parts' axes, which open question 10 decided for 0.2, are not these rules'.

These are class-A rules, held to the tolerances of section 10: each part's bodies within 0.1 mm of the rules', and its volume within a relative 10⁻⁶.

An example: a beam from [0, 0] to [4000, 0], its family two member parts of width 45, bottom, a bottomChord from [0, 100] to [Length, 100] of depth 200, and post, a heelPost from [Length/2, 0] to [Length/2, 1200] of depth 95, whose cutBy names bottom. bottom is the rectangle from x 0 to 4000 and z 0 to 200, 45 × 200 × 4000 mm. post is the rectangle from x 1952.5 to 2047.5 and z 0 to 1200, less bottom: its first edge, along bottom's underside, leaves nothing outside, its second, bottom's far end, nothing either, and its third, bottom's top, sets apart the piece from z 200 to 1200, which is kept; what lies inside bottom is gone. So the beam's volume is 45 × (200 × 4000 + 95 × 1000) mm³, 0.040275 m³, and its two parts are its members, an IfcMember CHORD and an IfcMember POST (Part VIII, 1.6).

Reference: framingSchema, framingExpression and framingProfiles in src/core/elements/framing.ts; framingSolids in src/core/geometry/framingGeometry.ts; edgeFramingSolids in src/core/geometry/edgeFraming.ts; edgePlacement in src/core/elements/edgePlacement.ts; framingIfcClass in src/core/elements/framingIfc.ts. In @openeik/tools: familyParts and familyEvaluate in packages/openeik/src/compile-framing.js, placed by compileBeam and compileFramingEdge in compile.js.

6.7 A member between two points

Part of 0.2. OpenEik 0.1 had it as the proposal member: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), a member is of a category 0.1 does not have, and a compiler derives it no body (section 1, rule 4). The section adds no file: the member is its element's record (Part II, 6.2.1).

By the steward's decision of 30 October 2026 (open questions, 110), one rule for linear members in any direction, sharing this section's reference line, axis, roll, taper and joins. Given a member (Part II, 6.2.1) with profile points P0 and P1, its start's height z0 = elev(levelId) + offset and its end's z1 = elev(end.levelId) + end.offset:

  1. Its line. A = (P0, z0) and B = (P1, z1) are the ends of its reference line (6.5), the line through the middle of its section, at least 10 mm apart. Its length is Ls = |B − A|, and t = (B − A) / Ls its direction. No underside sets it out: its reference line is the line its record gives.
  2. Its frame, the section's x and v before a roll. Where the member is not plumb, x = (−u_y, u_x, 0), the left normal of its plan direction u = (P1 − P0) / |P1 − P0|, as a beam's n (section 3); where it is plumb (Part II, 6.2.1, rule 4), x = (1, 0, 0), the model's x axis. In both, v = t × x. So x, v and t turn as the model's x, y and z do: for a member that is not plumb, v is square to the line in its vertical plane and points up, as a beam's is (6.2, rule 3); for a plumb member that runs up, x and v are the model's x and y, as a column's section's width and depth before its turn (6.1, rule 2), and for one that runs down, v is the model's −y.
  3. Its roll. With a roll ρ, in degrees, the section turns about the line, right-handed about t, as a beam's does (6.2, rule 5): x' = x cos ρ + v sin ρ and v' = −x sin ρ + v cos ρ. Without one, x' = x and v' = v.
  4. Its body. The point (x, y) of the section, x across and y up from the middle of its bounding box, lies at A + s t + x x' + y v' for s from 0 to Ls: the section stands square to the line, the middle of its bounding box on it (6.5), its width along x' and its depth along v'. A custom section's point [px, py] is (px − mx, py − my) there, (mx, my) the middle of its outline's bounding box (Part II, 5.2, rule 10). Without a section, the section is a rectangle width across by height deep (Part II, 6.2.1, rule 5).
  5. A taper, on a rectangular section only, as a beam's (6.2, rule 4): the depth runs linearly from the section's depth d at A to taper.endDepth e at B, centred on the line: at s along it, y runs from −d(s)/2 to d(s)/2, where d(s) = d + (e − d) s / Ls. Its reference line runs through the middle of each section, as 6.5, rule 3 says. A taper on another section is left out, and a compiler says so (taper-shape).
  6. Its axis is its reference line moved by its section's centroid offset, turned with the body (6.5, rule 5): each point moves by e_x x' + e_v v', where (e_x, e_v) is the offset in the section's own x and v. Section 6.5, rule 6 says which sections have none; a tapered member, as a tapered beam, has none.
  7. Its joins are a beam's (6.4): at names its start, A, or its end, B; they are found on reference lines; and a mitre joins two members of the categories beam, column and member, any two of them.
  8. What it measures (informative, as section 9): its length, Ls, as recorded, whatever its joins do (6.4, rule 4); its volume, its section's area along Ls, a tapered one's mean area, its joined ends on their cuts.
  9. A column's and a beam's line as a member's. A column is the member from its base to its top, (c, z0) to (c, z0 + H) (6.5, rule 1), of its section, its turn its roll: plumb and running up, its frame is the column's before its turn (rule 2), and a roll turns it as turn does, counter-clockwise in plan. A beam with a section and no taper is the member along its reference line (6.5, rule 2), of its section and its roll: that line runs parallel to its set-out line, the same distance over it at both ends, and the member's frame is the beam's. Each has the same body and the same axis as that member. So an analysis reads beams, columns and members alike, each by its line, its frame and its section. A tapered beam's sections stand square to its set-out line (6.2, rule 4), not to its reference line as a member's do (rule 5), and a beam without a section is level (6.2, rule 2).

These are class-A rules, held to the tolerances of section 10.

The brace of Part II, 6.2.1, a square hollow section 100 × 100 × 5 from (0, 0, 0) to (6000, 0, 3450), runs Ls = √(6000² + 3450²) = 6921.16 mm, t = (0.86691, 0, 0.49847), with x = (0, 1, 0) and v = (−0.49847, 0, 0.86691); its section, 1900 mm² with square corners, gives it 0.013150 m³. The plumb hanger from (2000, 1000, 3600) down to (2000, 1000, 2700) has t = (0, 0, −1), x = (1, 0, 0) and v = (0, −1, 0).

7. Reference behaviour, and the class-A rules stated from it

Sections 7.1 and 7.2 are informative: what the reference implementation does where this specification fixes no rule, the class-B and class-C rules (section 2). Sections 7.3 to 7.6, part of 0.2, state class-A rules from its engines, which a compiler of the core class implements (section 2).

7.1 Wall joins (informative)

The reference implementation joins walls from their geometry; nothing is stored but the switch joinEnds: [start, end], where false keeps that end free.

  • Corners (L). Two wall ends pair when the walls' bases are at the same height (rounded to the millimetre), the ends lie within twice the thickest wall's thickness of each other, and the two axes cross near both ends: within the thicker wall's thickness, and within a quarter of each wall's length. Ends at the same point always pair. When more than one pair is possible, a partner of the same height, outline or attachment host is chosen; when that still leaves more than one, the end stays free. A corner is mitred: each wall ends on the line through its inner and outer corner points.
  • Tees (T). An end that has no corner partner and meets another wall's side part-way along, within half that wall's thickness plus 50 mm of its axis and at more than 10° to it, butts it. Its layers run through the host's layers on that side up to the host's core face.

In the example, the partition's ends meet the south and north walls as tees at their core faces, y = 135 and y = 6865 (the exterior walls' 15 mm linings give way), and the four outer walls meet in mitred corners at the outer corner points.

Reference: joinPartner, wallEndPair and teeHost in src/core/elements/wallJoins.ts.

7.2 Others (informative)

Room boundaries (but for those of section 7.5, of 0.2), stair runs and landings (but for those of section 7.3, of 0.2), railings (but for the line of a railing on an outline, a wall's top or a slab edge element, and its given posts, section 11.2), a door's and a window's own body (but for how it opens, Part II, 6.5, and its frame, leaves and glass, section 4.3.1, of 0.2), components (but for a component's frame and the bodies of section 7.6, of 0.2), a slab edge element's notches (but for section 4.6, rules 12 to 14, of 0.2) and a framing family's section (but for section 6.6, of 0.2), timber framing members and trusses (but for a wall's framed layers', a roof's truss layout and its hip sets, and a floor's joists, section 7.4, of 0.2), MEP fittings between runs (a run's own bends are part of its body, section 12) and the paths of downpipes worked out from their gutters, rebar shapes, precast pieces, steel joints, a column's or a beam's own holes and the holes a bolt group drills in the members its through names (Part II, 6.2), and regional area rules are reference behaviour. Their results enter the model only when a command materialises them as records.

7.3 Straight, L and U stairs

Part of 0.2. OpenEik 0.1 had it as the proposal stairs: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), stair runs and landings are reference behaviour, as section 7.2 says. The section adds no file and no member: the members are those of a stair's stair and of its type's stair (Part II, 5 and 6.1).

By the steward's decision of 26 October 2026 (open questions, 76), every stair's risers, their height, its going and its location line, the layout of a straight stair and of an L or a U stair with a landing, and the bodies of a cast concrete stair's flights and landings, are these rules.

A stair's frame: x up its first run, from its profile's first point, the foot of its first riser, towards its second (Part II, 6.1, rule 7), along u; y to the left looking up the stair, along n (section 3); z up. Its base is its level's elevation plus its offset, and its top its top constraint's level's elevation plus that constraint's offset, else its base plus its height (5.1). W is its width, s its stair, and f its type's stair.

  1. Its numbers. Its rise is its top less its base, and MUST be more than 0. It has s.risers risers, else the fewest f.maxRiser allows, ⌈rise / f.maxRiser − 10⁻⁹⌉, and at least 2; each riser R = rise / risers high; its going g is s.going, else f.going. Where it has no risers or no going, its top is not above its base, its first run has as many risers as it has or more (rule 5), or an L's landing is shallower than its run is wide (rule 5), the compiler gives it no layout and no body, and says why (stairs-layout). A validator SHOULD warn of such a stair, which it can tell from the records alone.
  2. Its location line is the line its profile gives. Its width lies across it from y0 to y0 + W, where y0 is −W where s.locationLine is left (the line is its left edge, looking up), 0 where it is right (its right edge), and −W/2 otherwise.
  3. A flight of n treads, its first riser's foot at o, running along a way d, from a to b across it along d's left, on a height z: its tread j, from 1 to n, is the plan rectangle from (j − 1)·g to j·g along d and from a to b across it, its top at z + j·R; its nosing line is its front edge at that height, from its left end to its right, looking up the run. Treads are numbered from 1 up the stair, across its flights. A flight has one riser more than it has treads: its last rises to the landing or the floor it reaches.
  4. A straight stair (s.shape straight) is one flight: from its frame's origin along u, from y0 to y0 + W, on its base, with risers − 1 treads; its last riser rises to its top, the floor above.
  5. An L or a U stair with a landing (L or U, without s.winders). Its first run has r1 risers: s.firstRunRisers, else half its risers rounded up, at least 1 and at most risers − 1. Its first flight is rule 4's with r1 − 1 treads, so that it ends at L1 = (r1 − 1)·g; its landing's top is at its base plus r1·R, and the landing runs from x = L1 to L1 + D, where D is s.landingDepth, else W.
    • An L's landing lies from y0 to y0 + W, and D MUST NOT be less than W. Its second flight turns a quarter to the side s.turn gives, left where it gives none: along n, its first riser's foot at (L1 + D − W, y0 + W), or, to the right, along −n, its first riser's foot at (L1 + D, y0); either way from −W to 0 across its way's left. It leaves the landing's far end, beside its side.
    • A U's second flight comes back beside the first, s.gap from it, 100 mm where it gives none: with yb = y0 + W + gap to the left and y0 − W − gap to the right, it runs along −u from (L1, yb + W), from 0 to W across its way's left, its first riser on the landing's front edge. Its landing lies from min(y0, yb) to max(y0 + W, yb + W).
    • The second flight stands on the landing's top and has risers − r1 − 1 treads; its last riser rises to the stair's top.
  6. A cast concrete stair (f.construction concreteCast) has these bodies, closed.
    • Each flight with treads, the whole of its width: in its run's vertical plane, x up the run from its first riser and z up, its top is its steps, each tread's top over its going; its ends are square, at its first riser and at its last tread's back; and its underside is the soffit, the line f.waist below the line through its risers' roots, z = z0 + (R/g)·x where z0 is the height it stands on, measured square to that line, so at z0 − f.waist/cos(atan(R/g)) + (R/g)·x, but never below its floor: the stair's base for a flight on it, and the landing's underside, its top less f.landingThickness, for a flight on a landing. The body is that outline carried across the flight's width. Each body names its part flight and its piece flight:<k>, k from 0.
    • Each landing: its outline, from its top less f.landingThickness up to its top; its part landing, its piece landing:<k>.
    • A flight and its landing meet on the plane of the flight's last riser, and do not overlap.
  7. What stays reference behaviour (class C): the parts of every other construction (stringers, treads and risers of timber, precast pieces, cross-laminated timber), the nosing a type's nosing gives a tread, winders and balanced winders, spiral and helical stairs, and a stair that repeats another (s.repeatOf). The compiler gives them no layout and no body; a bridge says that it approximates them (Part VIII, 1).

These are class-A rules, held to the tolerances of section 10. With them, a railing on a straight, L or U stair can follow its flights by rule (open questions, 18); until that rule is written, a railing's line on a stair stays reference behaviour (11.2).

Reference: stairNumbers, stairLayout and stairSolids in src/core/elements/stairs.ts; stairPredefined in src/core/io/ifcExport.ts. In @openeik/tools: stairLayout and flightSection in packages/openeik/src/compile-stairs.js, built by compileStair in compile.js.

7.4 The members of a framed wall layer

Part of 0.2. OpenEik 0.1 had it as the proposal framingLayout, which decision D5 of the plan for 0.2 put into the first version it was finished for: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), timber framing members and trusses are reference behaviour, as section 7.2 says. The section adds two members, a layer rule's battens (section 7.4.8, rule 1) and a roof opening's header (section 7.4.13, rule 10), and no file: the other members it reads are those of a wall type's layer framing, a slab type's layer joists, a wall's framing members, a roof's trussLayout and a truss family of 0.1 (Part II, 5.1, 5.4 and 6.3), whose meaning these rules give.

By the steward's decision of 29 October 2026 (open questions, 83, part 2), the reference implementation's timber framing engine becomes these rules, as far as it lays out a wall's layers: its plates, studs, end and corner studs, the backing of a wall that ends against it, each door's and window's jacks, kings, headers, sill and cripples, its noggings and battens, and the heel members over a level top plate. A wall's corners and tees, which section 7.1 leaves to reference behaviour, are stated in section 7.4.2 for what the layout reads of them, and a wall's cut roof, which the note on section 5.3 leaves to reference behaviour, in section 7.4.3. Its other engines become these rules too, by the same decision: a roof's truss layout (section 7.4.13), the walls it bears on (section 7.4.14), a floor's joists (section 7.4.15) and a truss layout's hip ends (section 7.4.16). What the rules cannot state yet is said, with a code, and laid out not at all (section 7.4.12).

In these rules lengths are in mm, and two lengths within 10⁻⁶ mm of each other are equal.

7.4.1 A framed layer, its rule and its elevation

  1. A framed layer is a layer of a wall's type that carries framing. A compiler lays out each framed layer of each wall but those of section 7.4.12, each on its own, as if it were its type's only framed layer, into members. It changes no body: the layer's own body stays its layer's (section 4.1), and the members are results of their own.
  2. Its rule, with its defaults. A rule is read with these values for what it leaves out: material C24, direction vertical, start left, restartAtOpenings false, restartFrom king, restartMinWidth 0, bottomPlates and topPlates none, plateOverhangAtEnds 0, top.mode auto, rakingTopPlate true, roughOpeningGap 0, bottomPlateAtOpenings with doors cut, windows keep and clearance 0; for each tier headerPlies, kingStuds and jackStuds 1, cripplesAbove and cripplesBelow true and studsAtKings false; no noggings where it gives no noggings, and there rows 0 and staggered false; junctions.L butt, junctions.T backer, junctions.end single and junctions.endFlat false; moveBlockedStuds false, minGap and middleBay 0, studsAtPosts false, and battens service. A plate is a rule's bottomPlates or topPlates entry, the first two of each. A rule without a member's width, a spacing or a structural tier is not read: its layer is not laid out (framing-layout-rule).
  3. What the rule may hold. A rule ought to have its member, its plates and its sills no deeper than its layer, a spacing no less than its stud's width, its tiers in order of increasing maxWidth, each tier's header plies together no deeper than its layer, and a relyOn that names another framed layer of its type; and to be on a wall type's layer of some thickness. A validator warns of one that is not (framing-layout-rule). A compiler lays such a rule out as these rules say, each section's depth taken as at most its layer's thickness (rule 5); a layer of no thickness it does not lay out.
  4. The elevation. A layer is laid out in its wall's elevation: x along the wall's baseline from its first point, along u (section 3), and z up from its base, its level's elevation plus its offset; and y across the wall from its exterior face, the first layer's outer face, so that a layer lies from y0, the sum of the thicknesses of the layers before it, to y1 = y0 + its thickness. The point at x and y lies in plan at P0 + u x + n (T/2 − y), T the wall's thickness (section 4.1).
  5. Sizes. w is the rule's member's width, a stud's width along the wall. A section's depth across the wall, d, is its depth, else its layer's thickness, and at most its layer's thickness; a section is centred in its layer across the wall unless a rule below places it.
  6. The order of its members. A layer's members are laid in this order, which is the order of its result: its bottom plates and its top plates (7.4.5); its end studs, its corner and end flat studs, the studs that back a T, and the grid's studs (7.4.6); each opening's jacks, kings, headers, sill and cripples (7.4.7); a T's ladder and the noggings, or a batten layer's battens (7.4.8); then cut where a ridge beam bears in it, and its post (7.4.14, rule 6); then cut where a wall runs through (7.4.8, rule 4); then its heel members (7.4.4, rule 6).
  7. Chains. The bottom and the top of a layer, and its plates' faces, are chains: a function of x, linear between its points, given by points of increasing x; where two points have one x the chain steps there, and has a value left of the step and one right of it. A member at x, w wide, stands from x − w/2 to x + w/2 between two chains, as the polygon between them over that span; it has no body where the polygon has no area, or its top lies under its bottom anywhere by more than 10⁻³ mm.

7.4.2 Corners and tees

These rules say which wall ends meet, at a corner or on a wall's side, and the line each joined end's layers stop on (section 7.1, stated). They change no wall's body: a compiler of the core class ends a wall square, as in 0.1 (open questions, 104).

  1. A storey. Two walls are of one storey where their bases lie within 0.01 mm of each other. A wall's end is kept free, and meets nothing, where its joinEnds says false for it. A wall's thickness T is its type's layers' together, or its width where its type has none.
  2. Ends that pair. An end of a wall pairs with an end of another where the two points are within 0.01 mm of each other; or where the two walls are not parallel (|u × u′| ≥ 10⁻⁶) and their axes cross within the thicker wall's thickness, and within a quarter of each wall's length, of each end, measured along each axis.
  3. A corner on its storey. An end's storey partners are the ends of the walls of its storey within 2·max(T_max, T) + 0.01 of it, T_max the thickest wall of the model, that pair with it and are not kept free. Where it has more than one, only those of a wall as high as it within 0.01 mm, or of a wall that has a wallOutline where it has one, or attached to the same host (top.attach.hostId), count. Where it has storey partners at all, it has a corner only where one counts, and only one, and the corner fits (rule 6): over the end's height, up to its own top or its base plus its partner's height, whichever is lower.
  4. A corner across bases. An end that has no storey partner and meets no wall's side (rule 7) has corners with the ends of walls of other bases within 1 mm of its point that pair with it, are not kept free, have no storey partner and meet no side either, and share more than 1 mm of its height, each over the heights the two share. Between each two of the heights at which a shared height begins or ends, the end has a corner with the one such end there, where there is only one, and that end has only this end there too, and the corner fits; a partner's corners one above the other are one corner.
  5. Its partner is the one of an end's corners over most of its height, a later one only where it is over more by more than 0.01 mm.
  6. A corner fits where the two walls do not run the same way from it: the directions from the two ends into their walls' bodies differ, their dot product at most 0.999999. Parallel walls (|u × u′| ≤ 10⁻⁶) fit. Otherwise the mitre reaches along the wall t = |(s n′ − n) × u′| / |u × u′| · T / 2, s −1 where both ends are first points or both last, else +1; the corner fits where t is at most half either wall's length and 4 T. Where it reaches further, it fits only where, for each of the two walls, each face is cut back by the mitre line (rule 8) by at most 4 times its thickness, and keeps more than 1 mm of its length beside what the lines its other end may stop on cut back from it (each at least 0): its corner's mitre and its partner's outer face, or its T's core face.
  7. A T. An end of a wall at least 1 mm long meets the side of a wall of its storey at least 1 mm long where the two run at more than 10° to each other (|u · u′| ≤ cos 10°), the end lies within T′/2 + 50 of that wall's axis, measured square to it, and part-way along it, at least T/2 + 1 from either of its ends, T′ that wall's thickness and T this one's; and where the wall runs away from it: the direction from the end into its own body has a part along that wall's left normal of at least 10⁻⁶, of the sign of the end's offset from its axis where that offset is more than 1 mm. Where exactly one wall's side is met, the end meets it as a T, where it has no corner partner: on that wall's left (+1) or right (−1), as its body runs, and at its core face on that side, the outer face of its first core layer on the left and the inner face of its last on the right, or its own faces where no layer is core.
  8. The line a joined end stops on. At a corner, the mitre: the line through the point where the two walls' inner faces meet (the faces towards each other's bodies) and the point where their outer faces meet, or the partner's axis where the two points are one; where the two walls' faces are parallel and never meet, the line square to the wall through its end's point; an end none of whose corners has a mitre has no line, and its frame ends free. At a T, the host's core face on its side. An end's x at an offset c along its left normal is where the wall's line at c meets that line, measured from the wall's first point.

7.4.3 A framed layer's envelope

  1. The outline. A framed layer is laid out within its envelope, an outline in its elevation. Its source is the wall's wallOutline, else the rectangle from x = 0 to its length L and from its base up to its height (section 5.1).
  2. The faces that cut it. A wall attached to a host (section 5.3) is cut by the host's face, by rules 2 and 3 of 5.3 and its offset, every plane of it, or, where its mode is level, the one level plane at the lowest point of that face over the wall's core band at its two ends (from its first core layer's outer face to its last core layer's inner face, or its whole thickness where no layer is core). A wall with a cut roof is cut by that roof's face too: its top.cutRoofId, else, where its type's wallAttach.cutToAttachedRoof is true, the roof it is attached to; the face its top.cutRoofFace names (its underside where it names none), by rules 2 and 3 of 5.3, with no offset. A cut roof MUST be a roof. Every framed layer is cut by the cut roof's face; a layer extended past the attached face is not cut by the attached face. The layers before a wall's type's first core layer are extended where the wall is attached and its top.extendExteriorLayers is true, or where it gives none, the wall has a cut roof, and its type's wallAttach.extendExteriorLayers is not false. Legacy members are read as Part II, 6.4 reads them. A wall whose top fails a rule of section 5.1 or 5.3, attached to a barrel vault or cut under one, or whose cut roof is no roof, is not laid out (framing-layout-top).
  3. The faces as lines. Each plane of the faces that cut a layer is taken, for its envelope, as the line along the wall of its height above the base over that face of the layer, y0 or y1, over which it is higher at the middle of the wall's length (y0 where equal), from x = 0 to x = L. The top of a layer cut by an attached face is the lower envelope of these lines; of any other layer, the lower envelope of its source's top and these lines. Its bottom is its source's bottom.
  4. Its ends. At each end of the wall, the layer ends: at a T (section 7.4.2, rule 7), at the x of the far one of its two faces' ends on the line the end stops on (the less at the wall's first point, the greater at its last); at a corner, at its corner's x (rule 5). Where an end lies outside 0 to L, the layer's top and bottom run on, level, to it; where it lies inside, the outline is cut there.
  5. How a layer ends at a corner. With its partner (7.4.2, rule 5), where the two walls are square to each other within 5° (|u · u′| ≤ sin 5°): the partner's matching layer is its layer of the same place in its type, counted from the other side where both ends are first points or both last; the layer's middle line meets that layer's two faces at two x, the outer the one further from the wall's body. The core runs through on the longer wall (by more than 1 mm; of two within 1 mm, the one whose id is first by code units), unless a framingCorners names it: the wall's own through or butt at that end where it differs from its partner's at its end, else its partner's, butt there making this core run through. A core layer runs through where its wall's core does, any other layer where its core does not. A layer that runs through ends at the outer x (through), any other at the inner x (butt); the inner x is its partner's inner face. Where the walls are not square, or the partner's type has no matching layer, the layer is mitred: its end is where its middle line meets the line the end stops on, and its faces' ends where its two faces do (miter). At a T, its end is where its middle line meets the T's core face, and its faces' ends where its two faces do (tee).
  6. Standing. The outline is cut to the x where its top lies above its bottom: an attached gable drawn to its eave's corner stands where its roof comes down to its base.
  7. Its lowest top is the least of: its top at its outline's two ends, its top's points between, and, where faces cut it, each face's least plane over each of its two faces, y0 and y1, at the x of each end's face (its corner's or its T's face end, held within the outline's x; 0 and L where the end is free). Its drop across is, where faces cut it, the difference between the least of the faces over y0 and over y1 at the middle of its outline's x.
  8. The walls that end against it. The walls whose ends meet the layer's wall as a T (7.4.2, rule 7) are taken in order of their first x along it, then of their ids. Where the layer lies between the T's core face and the face of its wall on the ending wall's side, the ending wall runs through it (cut): over the x where the ending wall's two faces cross the layer's two faces, from the ending wall's base up by its height. Where the layer is the core it runs to, or its type has no core layer, it backs it (back): over the x where the ending wall's two faces meet the core face, and with the x where the ending wall's framed layer (its first that carries framing, else its first core layer) meets that face. A T of any other layer is not the layer's.

7.4.4 Its top

  1. Its mode is its wall's framingTop.mode, else its rule's top.mode. auto is raking where its top has a segment steeper than 1 in 10 along the wall; else underFloor where a floor bears on it (rule 4); else levelPlate.
  2. A level plate: its top plates' top is its wall's framingTop.wallLiveHeight, else its rule's top.height, else its lowest top (7.4.3, rule 7, or its top's points, the less). A top plate above its lowest top is not laid out (framing-layout-top). The plate's top is its bearing plane, and over it the heel zone is its envelope between the bearing plane and its top, where that holds more than 100 mm².
  3. Under a floor: its top plates' top is the underside of the bearing floor's structure (rule 4). Without a bearing floor, or with one whose structure's underside lies above its lowest top, it is not laid out (framing-layout-top). Its bearing plane is the floor's top, held at its lowest top; its heel zone is its envelope between the bearing plane, held under its top, and its top. A wall-live height is not read.
  4. The bearing floor is the slab its wall's framingTop.floorId names, which MUST be a slab (else framing-layout-floor); else the floor found. A slab's top is its level's elevation plus its offset, less the wall's base; the underside of its structure lies below its top by its layers down to and including its last core layer, or by all its thickness where no layer is core. A floor is found among the slabs whose structure's underside lies at least 1000 mm over the wall's base and not above the layer's lowest top. Along the layer's x, from its outline's least x + 50 every 100 mm to its greatest, a slab covers a sample where its outline (its stored profile) holds the plan point at that x and one of the y y0 − 50, y0, the layer's middle, y1, y1 + 50, and, where no core layer follows the framed layer in its type, the wall's room face plus 50 (T + 50); its coverage is 100 for each sample it covers, at most the layer's length. The candidates are the slabs that cover at least the lesser of 1000 and the layer's length, the highest underside first, then the greatest coverage. Below, "enough" is the lesser of 1000 and the layer's length, in mm of coverage. A candidate reaches the frame at a sample where its outline holds one of y0 + i, y1 − i and the layer's middle, i the lesser of 10 and a quarter of the layer's thickness; at a sample where it does not, it meets a face at y0 − 50 or at y1 + 50, and meets the facade at y0 − 50 or at −50. Where the layer's top is found by itself (its mode auto), and among the candidates that reach the frame over less than enough, one meets the face y0 − 50 and one the face y1 + 50, each over enough, only the candidates that reach the frame over enough count: a party wall that floors meet from both sides bears neither. Else, where its mode is auto, a candidate covers enough only with the room face (at the first five y it covers less), and a slab of those whose underside lies as these rules ask meets the facade over enough, only the candidates that cover enough at the first five y count: a floor drawn to the room face of a wall that another floor meets at its facade stands beside it. The bearing floor is the first that counts.
  5. Raking: its top plates follow its top.
  6. Heel members. Over a level plate or under a floor, where it has a heel zone and its lowest top (7.4.3, rule 7) lies at least its first top plate's width (or its member's) plus w above its bearing plane: on each segment of its top a heel plate, the section of its first top plate (or of its member), hanging from its top lowered by its drop across, h √(1 + s²) deep, h that plate's width and s the segment's slope; and a heel post at each x of its studs', end studs', kings' and cripples' lines (their first points), in the order they were laid, then of the places of its stud grid (7.4.6, rule 1), in their order, each but where one taken already lies within w of it, from the bearing plane up to the heel plates' underside, where that is at least w. A heel plate's key is its segment's number, from 0; a heel post's the key of its grid place, or the x of the line it stands on, to 10⁻⁶ mm (1222.5). Where its lowest top is lower, it has none, which is said. Where trusses bear on the wall, they hold their width of the heel zone, or rest on the heel plate at their seat (7.4.14, rules 3 and 5).

7.4.5 Plates and openings

  1. Its openings are the doors and windows its wall hosts. An opening whose bottom (offset) lies at or above the frame's top (rule 7) less 1 mm, the least of it at the opening's two sides and its middle, held within the frame, is not framed: it lies in the roof's zone. Each other opening's rough opening runs from along − g to along + width + g and from offset − g (0 where its offset is 0) to offset + height + g, g the rule's roughOpeningGap. A pocket door (a door whose type's openingFamily is of the kind door with the operation pocket, Part II, 6.5) reaches over its pocket: its rough opening runs on by its width + 150 towards its wall's first point where its family's hinge is not right and its handFlipped is not true, or both are so, else towards its last point. An opening with an offset above 0 may have a sill and cripples under it (7.4.7, rules 4 and 5); one at 0 has none.
  2. Its tier is of the rule's openings.nonStructural where the wall's structural is false and the rule gives them, else of its structural: the first whose maxWidth is at least its rough width; else the last, which is said.
  3. Its zone is its rough opening widened on each side by its tier's jacks and kings, (jackStuds + kingStuds) w. A place x lies in a zone where a stud there overlaps it, x + w/2 above its first x and x − w/2 below its last, the first such zone; and in a hole where a stud there lies within its rough opening.
  4. Element joints. Each of its wall's framingJoints that lies more than 2w inside the outline's x from both ends is a joint, where prefabricated elements meet, each framed on its own; one that does not is not used, which is said.
  5. Plates. Its bottom plates are laid up from its bottom, the first (sole) on it and the second (sole2) on the first; its top plates down from its top (raking), from the level plate's top, or from the floor's structure's underside, the first (top) under it and the second (top2) under the first. On each segment of the chain they follow, of slope s, a plate h wide (its width) is h √(1 + s²) deep, and lies the depth of the plates before it, each √(1 + s²) times its width, from the chain. A plate runs over its segment, on past the outline's ends by plateOverhangAtEnds, and is cut at each joint; a top plate is not laid on a sloped segment where rakingTopPlate is false, and is cut to above the frame's foot. A plate whose side is exterior lies against the layer's face y0, one whose side is interior against y1, across its depth; any other is centred. A plate's key is its segment's number along its chain, from 0 (a step is no segment, a segment that takes no plate is one), and, for a piece after a gap or a joint, @ and the gap's opening's id or j<i>, i the joint's number from 0: 0, 1@D1, 0@j0.
  6. Bottom plates cut at openings. Where the bottom plates have a width, an opening's own bottomPlate (cut or keep), else its rule's bottomPlateAtOpenings for its category (doors, windows), says whether they are cut under it. They are cut only where the opening's offset lies under the bottom plates' top (else, where its own bottomPlate asks for a cut, that is said); from its along − g − c to its along + width + g + c, c the clearance, held within the outline's x, where that is at least 1 mm; an opening cut above its wall's base leaves the strip under it open, which is said. Cuts that touch or overlap, within 1 mm, are one gap, named for the opening whose cut reaches furthest along it. A bottom plate's pieces between the gaps, each at least 1 mm, are its plates.
  7. The frame's foot is the bottom raised by the bottom plates' widths together; its top is, raking, the top lowered on each segment by the top plates' widths together times √(1 + s²) (by nothing on a sloped segment where rakingTopPlate is false), else the level plate's or the floor's underside lowered by the top plates' widths. Over a level plate or under a floor, a frame whose top lies less than w above its foot's highest point is not laid out (framing-layout-top).

7.4.6 The studs

  1. The stud grid is a list of places, each a stud's middle, with a key. A place is set only within the frame, from its least x + w/2 to its greatest − w/2, and set again where its key is set. Between each two of the frame's ends and joints, the grid is walked: from a point moved by the offset, a spacing at a time, each step a place, while the step does not pass the walk's limit; each place keyed <e><walk><k>, e e<s>- for the s-th element after a joint and nothing for the first, walk the walk's prefix below, and k counting the walk's places from 1. Its start is the wall's framingStart, else its rule's start, and the wall's framingOffset (0 where it gives none) moves the point a walk starts from, in its direction:

    • left: from the first end, towards the second;
    • right: from the second end, towards the first, prefix r;
    • centre: a place at the middle (c0), and from it towards both ends, prefixes c and cl;
    • bothEnds: from the second end to the middle (r), then from the first towards the middle, or, with a middleBay, towards the least of the middle and the first place from the second end less the middle bay, so that no short bay is left between them.

    Where restartAtOpenings is true, the start is left or right, and a place falls in an opening's zone whose rough width is at least restartMinWidth, the grid sets its places a spacing apart from there across the zone, and walks on from the far king's middle (restartFrom king) or the opening's far side (opening).

  2. Posts. A post stands in a framed layer where a column whose type's section is more than w both ways, which is no materialised member (Part II, 10), has its point in the layer's band (y0 to y1) and within its wall's length (x from 0 to L), and spans some of its wall's height (its top not under the wall's base, its base not over the wall's base plus its height); it stands along the wall over its section's side that lies along it (|u_x| width + |u_y| depth). With studsAtPosts, the grid sets a place against each side of each post (p-<post>-a, p-<post>-b).

  3. Studs set by hand. Each of its wall's framingStuds, in order, is a place (m-<i>): a grid place less than w − 10⁻⁶ from it is dropped, or, where it lies within 2 mm of touching, the stud by hand stands against it, moved w from it on its side.

  4. End studs (endStud) stand full height at each end of the frame and on each side of each joint, flush: end0 at the first end, joint<i>a and joint<i>b at the i-th joint, end1 at the second. Where junctions.end is double, each has a second inwards beside it, keyed with a b after its key.

  5. A corner's flat stud. Where junctions.L is wrap and the layer runs through at a corner (7.4.3, rule 5), a stud laid flat (cornerStud, corner0 at the first end, corner1 at the second) stands full height against its inner face (y1 − w to y1), from the end stud's inner side to w past the partner's inner face, stopping at the first opening zone that begins (or ends) between the partner's inner face and it; with a cornerStudWidth, at most that wide, from where it stops; none where it is narrower than w or reaches outside the frame. Its section is its width along the wall by w. Where junctions.endFlat is true, at each end with no corner and no T, a stud laid flat at the inner face, as wide as the layer's thickness, beside the end stud (end<i>f).

  6. The places taken are, as they are laid, the end studs, the corner and end flat studs, the posts, the studs that back a T, the grid's studs, and each opening's jacks and kings; a place is occupied where a stud there would overlap one taken, by more than 10⁻⁶.

  7. A wall that ends against this one. Each T the layer backs (7.4.3, rule 8), in their order, is backed as the ending wall's framingTees says at its end, else as junctions.T says, else backer: backer, a stud (endStud) on each of the ending wall's face lines, tee-<wall>-a and -b, or one at its middle, tee-<wall>, where it is less than 2w + 10⁻⁶ wide; ladder, noggings between the two verticals that flank it (7.4.8, rule 2); flat, a stud against one face of the ending wall's framed layer, or the vertical already standing in it, and a stud laid flat at the inner face across the wall's end from it, teeFlatWidth wide, else the stock width (45, 70, …, 245, every 25 mm) nearest the ending wall's thickness, the first of equals (tee-<wall>-s, tee-<wall>-f); none, nothing. A backing stud is set only within the frame, on a place not occupied and in no zone; a flat stud only where it overlaps no zone and no vertical but the one it stands against.

  8. The grid's studs, in order along the wall: a stud set by hand stands against what it overlaps within 2 mm of touching, and is left out where it overlaps more. A stud on an occupied place is left out; with moveBlockedStuds, it moves beside what blocks it, on its side, where that place is free, in no zone and within the frame. A stud in an opening's zone is a cripple where it lies in the hole (7.4.7, rule 5), else it is left out; with moveBlockedStuds, it moves out beside the zone as a stud, where it lies in the outer half of the jamb and that place is free, or in as a cripple, beside the hole's side, where it lies in the inner half. A stud where the frame has no height is left out. With a minGap, a stud on its own place (not moved, not by hand, not against a post) whose clear gap to a vertical (but one set by hand that it touches) or to a zone is less than the minimum gap is left out. Each other is a stud, its key its place's, full height.

  9. The verticals are the end, corner and flat studs, the posts, the backing studs, the studs, the jacks and the kings (7.4.7), each with its half width.

7.4.7 Each opening's framing

For each opening, in order of its rough opening's first x, with its tier (7.4.5):

  1. Jacks (jack): jackStuds on each side, against the rough opening and each against the last, from the frame's foot up to the rough opening's top, or to the frame's top where the rough opening reaches it (within 1 mm, rule 3); <opening>:l<j> on the first side and <opening>:r<j> on the second, j from 0.
  2. Kings (king): kingStuds on each side, beyond the jacks, full height, each within the frame but where its place is occupied; <opening>:l<k>, <opening>:r<k>. Jacks are laid within the frame too, whatever stands there.
  3. Headers (header): over the rough opening and the jacks, held within the frame, from the rough opening's top up by the tier's header's width, cut at the frame's top, where that is lower (which is said); its plies side by side across the layer, centred together, each its header's depth; <opening>:<ply>, ply from 0. A tier without a header has one ply of the stud's section. An opening whose rough opening reaches the frame's top within 1 mm has no header and no cripples above it, which is said.
  4. A sill (sill): under a rough opening that has an offset above 0 and whose bottom plates are not cut, where its tier has a sill and the rough opening's bottom lies at least the sill's width above the frame's foot at its sides, from there down by the sill's width, over the rough opening; <opening>.
  5. Cripples (cripple), at the grid's places that fall in its hole (7.4.6, rule 8), in order: above, from the header's top to the frame's top, where cripplesAbove is true, there is a header, and that is more than 1 mm (<opening>:a:<key>); under, from the frame's foot to the sill's underside (or the rough opening's bottom), where cripplesBelow is true, the opening has an offset above 0, its bottom plates are not cut, and that is more than 1 mm (<opening>:b:<key>). With studsAtKings, a place against each side of the hole (kl, kr) where its nearest cripple lies more than half a spacing from that side, after those of the grid. With a minGap, a cripple of the grid less than the minimum gap clear of one against a king or a post is left out.
  6. Where two openings' zones overlap, their members overlap, which is said.

7.4.8 Noggings and battens

  1. Battens. A rule whose direction is horizontal lays battens, of its battens: service (the default, serviceBatten), nailing (nailingBatten) or counter (ventBatten). Where the layer is more than 3w long, a batten stands at each end, w wide, from the layer's bottom to its top at its middle, held within the bottom and the top at its sides, or, where a floor bears on the layer (7.4.4, rule 4), up to the floor's structure's underside and again from the floor's top, each starting over each hole that overlaps the batten and starts at or under its foot, at the hole's top, a hole being a door's or a window's box (Part IV, 4.3) or a truss's bottom chord (7.4.14, rule 4); one less than w high is not laid (end0, end1, .0 and .1 after them where there are two). Rows of battens w high are laid from the layer's lowest point a spacing apart while they fit under its highest, each but where it crosses a floor, cut at the end battens' inner sides, or at the layer's ends without them, and at the sides of each hole it crosses; of the pieces between the cuts, each is laid where its middle lies in none of those holes, up to the layer's top at its ends and middle, where that leaves at least 1 mm (<row>:<piece>, from 0). A batten layer has no plates, studs or openings' framing.
  2. A T's ladder. For a T that a layer backs with ladder, in the bay of the two verticals that flank its middle, where the bay is at least 1 mm and its middle lies in no zone, noggings w high, of the stud's section, from 600 above the frame's foot every 600 while they fit under its top (tee-<wall>:<r>, from 0). That bay takes no other noggings.
  3. Noggings (nogging), where the rule's noggings gives a spacing or rows: in each bay between two neighbouring verticals at least w wide, but a ladder's and one whose middle lies in a zone, between the frame's foot and its top at the bay's sides: every spacing from its foot while they fit, or rows rows evenly, each centred at its foot plus the bay's height times r / (rows + 1); in every second bay a nogging's height higher where staggered is true, where it still fits; <bay>:<row>, bays and rows counted from 0.
  4. Where a wall runs through. Once its noggings or battens are laid, each member of a layer that reaches into the rectangle a wall runs through it over (7.4.3, rule 8) is cut to its parts outside it, each at least 100 mm², keyed <key>.<k>, k from 0, where there is more than one; its line is cut with it. A member wholly inside is left out.

7.4.9 Each member: its name, its number and its class

  1. A member's id is <wall>:<layer>:<role>:<key>, unique in the model; its name is its role's name and its key, stud 3, header D1:0; its section its width in the elevation (a stud's along the wall, a plate's height) by its depth across the wall, each to 0.1 mm, 45 × 170; its line the line along its middle, in the elevation and, at the middle of its y, in the model; its length that line's; its material its rule's material.
  2. Each role's name, and the IFC4 class and predefined type it is written as, as the reference implementation writes a wall's members (a heel post a STUD and a header a LINTEL here, where section 6.6 writes a truss's a POST and a USERDEFINED HEADER):
RoleNameIFC classPredefined type
sole, sole2sole plate, second sole plateIfcMemberPLATE
top, top2top plate, double top plateIfcMemberPLATE
sillrough sillIfcMemberPLATE
stud, endStud, cornerStudstud, end stud, flat corner studIfcMemberSTUD
king, jack, crippleking stud, jack stud, cripple studIfcMemberSTUD
headerheaderIfcBeamLINTEL
noggingnoggingIfcMemberUSERDEFINED, NOGGING
serviceBatten, nailingBatten, ventBattenservice batten, nailing batten, counter battenIfcMemberUSERDEFINED, BATTEN
heelPlate, heelPostheel plate, heel postIfcMemberPLATE, STUD
postpost under a beamIfcMemberPOST

7.4.10 Each member's body

  1. A member's body is its outline in the elevation carried across the wall from its y to its other y, in the model (7.4.1, rule 4).
  2. It is cut by the faces its layer is cut by (7.4.3, rule 2), every plane of each a half-space under which the body is kept: the attached face, unless the layer is extended, and the cut roof's. Where a layer ends mitred, or at a T whose face ends lie more than 0.5 mm apart along the wall, it is cut by the vertical plane through those face ends, at y0 and at y1, the side away from the end kept.
  3. Its volume is its body's.

These are class-A rules, held to the tolerances of section 10: each member's body within 0.1 mm of the rule's, and its volume within a relative 10⁻⁶; members, their ids, names, sections and classes exactly.

7.4.11 In IFC

A bridge writes each member as its class (7.4.9, rule 2), aggregated under its wall by an IfcRelAggregates, its GlobalId from framing:<id>, its name its name and its tag its id, its object type its user type, or its predefined type and its section (PLATE 45 × 170), and of an IFC type of its class, predefined type and section (type:<class>:framing:<predefined type>:<user type>:<section>), as Part VIII, 1 keys a framing member given to it; its material Timber and its rule's material, Timber C24.

7.4.12 What is not laid out yet

  1. A face no set frames. A truss layout frames its roof's eaves (section 7.4.13) and its hip ends (section 7.4.16). A sloped face that is neither, such as a valley's or a second hip end at one end of the ridge, is framed by no truss, which is said (framing-layout-note): the reference implementation lays no set out under it either. A layout of one fall, or one whose hips.frame is false, frames no hip end, and says its hip ends.
  2. Materialised members (framing-layout-materialised). A wall whose members are materialised as elements, which name it in their frameHost.wallId (Part II, 10), is not laid out: its members are those elements. Nor are the members of a roof whose trusses are materialised, which name it in their frameHost.roofId: the layout is still laid out, and the walls and floors that read it (7.4.14, 7.4.15) read it as it is.
  3. What a later rule may read. A rule's relyOn, which names the layer whose studs its battens follow, is not read.

Reference: deriveWallFraming, resolveFrameTop, bottomPlateCutsFor, splitByGaps, framingRuleSchema and validateFramingRule in src/core/elements/wallFraming.ts; frameEnvelope, frameCorners, frameTees, wallFramingFor and framingSolids in src/core/elements/wallFramingGeometry.ts; bearingFloor in src/core/elements/bearingFloor.ts; joinPartner, teeHost, teeBranches and wallJoinCoordinate in src/core/elements/wallJoins.ts; wallTopClips and extendsExteriorLayers in src/core/elements/wallTop.ts; pocketSide in src/core/elements/openings.ts; framingIfcClass in src/core/elements/framingIfc.ts; derivedFraming in src/core/io/ifcFraming.ts. In @openeik/tools: framingLayouts and layoutLayer in packages/openeik/src/compile-framing-layout.js, wallJoins in packages/openeik/src/compile-framing-joins.js, called by compileModel in compile.js.

7.4.13 A roof's truss layout

These rules lay out the trusses of a roof's trussLayout (Part II, 6.3): each truss its family's parts, built as section 6.6 builds a beam's, on a line across the roof's span between the walls or beams it bears on; and a roof opening's trimmers, headers and dormer rafters, a ridge beam and its posts, and an anchor pair where each truss rests. Its hip ends are framed by section 7.4.16. In these rules lengths are in mm, and points in plan.

  1. A truss layout, its rule and its family. A roof with a roof payload, no barrel vault, and a trussLayout is laid out. Its rule is read with these values for what it leaves out: layoutRule fixedDistance, spacing 1200, count 2, justification beginning, bearingReference coreFace, ends insideGables, endOffset 0, openingFraming with trimmerPlies and headerPlies 2, hips as section 7.4.16, rule 1 reads it, and a ridgeBeam's topOffset 0 and no posts. Its family is the framing of the type its familyTypeId names, which MUST be a beam type, and is a truss family: one that declares the parameters Pitch, HeelHeight, OverhangLeft and OverhangRight of the scope instance. The family's parameters' values (value) give w, its Width (45 where it has none), and its ChordDepth (170), BottomDepth (0), RidgeGap (0), BottomPitch (0) and Notch (0); a family with a FurringMin is a furring family. A layout that a rule of this section cannot lay out is a finding (framing-layout-roof): the roof has no layout, and the walls it would bear on are laid out without its trusses. A validator warns of a layout whose family type is no beam type with a truss family, which it can tell from the records alone (framing-layout-roof).

  2. Its faces, its eaves and its frame. The roof's faces are those of section 4.4: a plane for each slope-defining edge, of two within 10⁻⁷ the first; each face the roof's domain, its outline pushed out by each edge's overhang (4.4, rule 3), where its plane is the lowest; a face of 0.01 mm² or less is none. An edge that roof.edges gives no rule defines no slope and has no overhang. A face is sloped where its gradient is more than 10⁻⁹; a roof with no sloped face is not laid out. Two sloped faces face each other where their edges run at least 179° apart (their directions' product less than −cos 1°); of such pairs, the eaves are the pair whose edges are together the longest, the first in the order of the outline where two are as long, and the left eave is the face of the pair's edge that comes first. Without such a pair the layout falls one way, its left eave the sloped face of the longest edge, the first of equals. The layout's frame: across, the left eave's edge's normal into the outline; along, square to it, the one of the two that points along +x, or along +y where across is square to x within 10⁻⁹; its origin the outline's first point; τ(p) = (p − origin)·across and r(p) = (p − origin)·along. Of the other sloped faces, those whose edges run across the span, within 5° of across (|u × across| ≤ sin 5°), are hip ends, framed by section 7.4.16, and the others are not framed, which is said (7.4.12, rule 1).

  3. Its bearings. Where bearingWallIds gives them, each side is the walls or beams it names in order, one or a list, each with two points or more and running along the eaves, its first two points' direction within 5° of along; the middles of the two sides' first bearings lie at least 500 apart in τ, and the side of the lesser τ is the left. Else each side is found: of the walls at least 1 mm long that run along the eaves and come up to the roof (attached to it, top.attach.hostId, or whose top, at its base plus its height, the highest of its top by sections 5.1 and 5.3, lies at most 1500 under the least of the roof's faces lowered by its thickness at the wall's middle), whose middle's τ lies less than 500 outside the eaves' τ, and whose run in r overlaps the domain's by at least the lesser of 1000 and 0.3 of the domain's run: those whose middle lies on the side's half of the span (under or over the middle of the eaves' τ), and of them those that overlap at least half the domain's run where there are any; the nearest to the side's eave, by |τ − τ_eave| / 50 rounded to a whole number, then the higher base, then the greater overlap, then the first by id in code units. The eaves' τ: the left eave's edge's first point's, and the right's, or the domain's greatest where the layout falls one way. A side with no bearing is a finding.

  4. A bearing's plane and its faces. A wall bears on the one of its framed layers that has a bearing plane (7.4.4, rules 2 and 3) when it is laid out with no truss on it: a core layer, else any, the thickest, the first of equals. Its bearing elevation is its base plus that plane's height, its faces that layer's two faces (y0 and y1), and its extent along the wall that plane's (its envelope's least and greatest x). A beam bears on its top, its base plus its height, across its whole width, and along its whole length. A wall with no such layer bears on its lowest top (sections 5.1 and 5.3), across its core band (from its first core layer's outer face to its last core layer's inner face, else its whole thickness), along its whole length, which is said. With bearingReference finishFace a wall's faces are its outer faces, 0 and its thickness T. A bearing's reference face is the one of its two faces, taken at the middle of its extent, nearer the outside: on the left the lesser τ, on the right the greater; and its run the least and greatest r of its extent's two ends at the middle of its faces. A side of several bearings bears at the highest of their elevations, its reference face the first's, and its run from the least of theirs to the greatest; a truss at a place bears on the first of them whose run holds the place within 1 mm, else on the one whose run's nearer end is the nearest. The layout's bearing plane E is the higher of its two sides'; but a layout that falls one way and whose family has no member part of the role bottomChord bears at its left side's. Sides more than 1 mm apart in elevation, and a bearing more than 5 mm out of line with its side's first, are said.

  5. Its range and its trusses' places. The bearings' run is where both sides' runs overlap, at least 1 mm (else a finding); the roof's run the least and greatest r of its domain. The range is the bearings' run held within the roof's run, closed at its gables; where that leaves less room than w + 2·endOffset (within 10⁻⁶), the bearings' run closed at its gables, which is said. The gables of a run are the walls that are no bearings, at least 1 mm long, running across the span within 5°, whose τ overlaps the span between the two reference faces by at least half of it, whose first point's r lies within half their thickness plus 150 of the run's start (a gable at the start) or else of its end (at the end), and that stand on the trusses' storey: base at most E + 500 and top, their base plus their height, at least E − 1500 (a wall whose top cannot be resolved stands). With ends insideGables the range starts no sooner than a start gable's inner face, r + T/2, and ends no later than an end gable's, r − T/2. The first place is the range's start plus endOffset + w/2, the last its end less endOffset + w/2; a last more than 10⁻⁶ before the first is a finding. With layoutRule positions, each of positions, sorted by at, is a truss at the range's start plus its at, of the mark its mark gives; a layout that lists none is a finding, and one that stands more than 1 mm outside the range less w/2 is said. Otherwise, with t = last − first and c the spacing: no truss where t is less than −10⁻⁶; one at the middle where t is less than w; else

    • maximumSpacing: n = max(1, ⌈t / c − 10⁻⁹⌉), and n + 1 places evenly from the first to the last;
    • fixedNumber: count places evenly from the first to the last, which is a finding where they are less than w apart;
    • fixedDistance, with n = ⌊t / c + 10⁻⁹⌋: from the beginning, the places first + k·c for k from 0 to n, the last of them moved to the last place where it lies within w of it, else the last place added after it (and added where n is 0); from the end, the same mirrored, last − k·c in order, the first of them moved to the first place where it lies within w of it, else the first place added before it; from the center, with r = t − n·c, the places first + r/2 + k·c, and the first and the last places added at the ends where r/2 is more than w, else the first and the last of them moved there (where n is 0, the first and the last places).

    The trusses so placed are numbered T1, T2 … in order, but where a position gives a mark; each is keyed by its index from 0. At a hip end, the range, its places and its girder are as section 7.4.16, rule 3 says.

  6. Each truss's line. A truss at a place lies on the line from the point origin + along·place across the roof, along across. Its left and right ends, at τ_L and τ_R, are where that line meets its two bearings' reference faces, each the line through the face along its bearing's direction; its span is τ_R − τ_L, at least 1000 (else a finding), and its points a and b, at τ_L and τ_R, run from its left bearing to its right one, each to 0.001.

  7. Its heel and its parameters. The chords' top is the face of the roof's chord layer: the layer of its type that chordLayer names, which MUST be one, else the first layer as thick as ChordDepth within 0.5 where the family is no furring family, else none; chordLayer underside names none. Its face is the underside of the layer over it, or the roof's top for its first layer, and without a chord layer the roof's underside; each of the roof's faces lowered by that depth, square to it (4.4, rule 4), the least of them. A truss's heel is heelHeight, else that face's height at its a less E; a heel under 0 is a finding, and so, in a furring family, is a heel less BottomDepth that is more than 0.5 under its FurringMin. It gives its family the parameters Pitch, the left eave's edge's slope; HeelHeight, its heel; and OverhangLeft and OverhangRight, overhang, else τ_L less the least τ and the greatest τ less τ_R where its line lies in the roof's domain, each at least 0; each to 0.001. Pitches more than 0.05° apart are said; with a heel given, a top that stands more than 1 mm into or under the face, and a face that lies more than 1 mm off the heel at the right bearing, are said.

  8. Its parts. A truss's parts are its family's, built by section 6.6, rules 2 to 9, with the parameters of rule 7 as the element's, with two changes for roof openings (rule 10):

    • a one-sided truss keeps only the top chords of its side: a member part of the role topChord is the left's where its axis rises (t_z more than 10⁻⁹) and the right's where it falls;
    • a band taken out of a side's top chords, from and to horizontal distances from that side's bearing line, is taken out of each of their pieces after its notches: with A(d) the point d along and HeelHeight + tan(Pitch)·d up (on the right, Length − d along) and the direction up the slope (cos Pitch, sin Pitch), mirrored on the right, what lies down the slope of the line square to it through A(from) is kept, where the band gives from, and what lies up the slope of the line through A(to), where it gives to, each a piece where it has three points or more and more than 10⁻⁶ mm². A top chord that its bands take all of has no piece, which is no fault.

    A family that cannot be built is a finding. Length is the span τ_R − τ_L for its seats (rule 9), the span to 0.001 for its members' sections and lengths (rule 13), and the length of its line from a to b for its bodies (rule 14).

  9. Its seats. On each side, where it bears on a framed wall or a beam, its seat is the least height over the bearing plane of its pieces, its plates' too, where each is clipped to between that bearing's two faces (each τ less τ_L, x0 and x1, in the plane), counting a clipped piece of 1 mm² or more, plus E less its side's elevation (rule 4), to 0.001. It has none where no piece spans the faces, or the bearing is no framed wall nor beam.

  10. Its roof openings. A roof opening (roof.windows) lies on the face of its edge edgeId: on an eave face, else it is not framed, which is said. Its outline is the rectangle width across the slope and length along it, its plan length length/√(1 + tan² σ), σ its edge's slope, about its center, along its edge's direction and normal; its run, from a0 to a1, and its τ are its corners' least and greatest. Openings are framed in a layout whose family's trussStyle is rafters; in any other, they are said. For each opening, in order:

    • its trimmers, trimmerPlies k from 0 on each side, a truss of its side only (rule 8) at a0 − w/2 − k·w (keyed <opening>:a<k>) and at a1 + w/2 + k·w (<opening>:b<k>), in that order, after the layout's trusses, numbered V1, V2 … across the openings, with their own indices on from the layout's;
    • the trusses it cuts: each of the layout's trusses (not a trimmer) whose width overlaps, by more than 10⁻⁶, a trimmers' band, a0 − trimmerPlies·w to a0 or a1 to a1 + trimmerPlies·w, loses its top chords on the opening's side whole (a band with neither end); one that overlaps a0 to a1 loses a band from d_eave − h to d_ridge + h, its from only where d_eave is more than 0, each to 0.001, where d_eave and d_ridge are the opening's τ on its eave side and on its ridge side measured inward from that side's bearing line at the truss (τ − τ_L on the left, τ_R − τ on the right), and h is its header's plies by their width by cos Pitch, 0 where it has none;
    • its header: headerPlies plies of w by ChordDepth, else one ply of the section its opening's header gives, {width, depth}, or none where it gives none. A header runs from a0 to a1 along the ridge, on the chords' top face, its plies' width up the slope and its depth square to it: across the ridge side from d_ridge to d_ridge + h, d_ridge measured at a0, but where its far top edge, 1 mm short, lies outside the opening's face at a0 or a1, which is said; and across the eave side from d_eave − h to d_eave, where the opening does not reach its bearing line (d_eave at a0 more than 0). Its corners: at each end, at d along from the bearing line and k into the chord, the point τ_L + d + k sin Pitch (τ_R − d − k sin Pitch on the right) on its line, at E + heel + tan(Pitch)·d − k cos Pitch, each to 0.001; keyed <roof>:opening:<opening>:header:ridge and …:header:eave;
    • its dormer rafters: under the roof whose face over the opening's middle (the middle of its first and third corners) is sloped, of a pitch at least 1° from Pitch, its underside the highest there (the first by id of equals), where its gradient lies within 5° of across (else said): for each truss it cuts, in order, a rafter at the truss's place (to 0.001), from its ridge-side d_ridge to where its line leaves that roof's domain on the opening's side, where that is 100 or more, dormerRafterDepth deep (else ChordDepth) and w wide, its top on that roof's underside and its plumb depth that depth over the cosine of its pitch, its length along its slope; keyed <roof>:opening:<opening>:dormerRafter:<truss key>.
  11. A ridge beam. A layout whose family's trussStyle is rafters lays its ridgeBeam; another says it does not. Its width is its width, else RidgeGap, and one less than 45 is said, and none laid; a width more than 1 mm from RidgeGap is said. At a place along the ridge, its top is E + heel + tan(Pitch)·((τ_R − τ_L)/2 − width/2) less topOffset, its underside its depth lower, and its middle at (τ_L + τ_R)/2. It runs from the range's start to its end, but where a gable stands there (rule 5): from that gable's framed layer's outer face, its core layer that carries framing, else its first that does, where that face is the outer one (else from between its faces, said), in which it then bears; or from the gable's line where it has no framed layer (said). Its corners at each end are its top and underside at its middle ± width/2. Each of posts, a distance from the range's start, is a post width by width at its middle, from the base of the left side's first bearing up to the beam's underside there, where it lies within the beam's run (else said); keyed <roof>:ridge:post:<i>. Where it bears in a gable, its bearing there is its middle's x along the gable at the gable's first point's place, its width, its underside and top over the gable's base, and the y of the framed layer's outer face (7.4.14, rule 6). The beam is keyed <roof>:ridge.

  12. Its connections. Each truss, on each side where it rests, has an anchor pair on that side's bearing at its point a or b, at E, of two brackets, keyed <truss id>:anchor:<side>. A truss rests on a side unless it is one-sided to the other side, or a band that gives no from (one that takes its tail) is taken out of that side; a hip truss and a jack as section 7.4.16, rule 9 says, which gives them hangers too.

  13. Each member: its name, its section and its class. A truss is keyed <roof>:truss:<key> and named Truss and its mark, as Truss T3; a hip set's trusses and a girder's plies as section 7.4.16, rule 10 names them. Each of its parts that has a piece is a member, keyed <truss id>:<part id>, named by the truss's name, its role in words and the part's id, as Truss T1 · top chord topL (a profile part by its id alone, a plate as a nail plate), of the IFC class and predefined type section 6.6 and Part VIII, 1.6 give a family's part by its role, its material its family type's timber, and a nail plate's steel. Its section is its width by the greatest depth of its pieces: a part's pieces, ordered by their least extent along its axis, are grouped where each starts within 1 mm of the reach of those before it, and each group's depth is its extent square to the axis; a plate's is its pieces' height in the plane. Its length is the sum of its groups' extents along the axis. The headers, dormer rafters, ridge beam and posts are members too, named header, rafter, ridge beam and ridge post and their key after its first :, as header opening:W1:header:ridge and ridge post ridge:post:0, of the classes of section 6.6's table: a header an IfcMember USERDEFINED HEADER, a dormer rafter an IfcMember RAFTER, a ridge beam an IfcBeam BEAM RIDGEBEAM and a post an IfcColumn COLUMN; their sections a ply's width by its depth, the beam's width by its depth and a post's width by width. Sections are each to 0.1, as 45 × 220.

  14. Each member's body. A truss's pieces are placed as a beam's (section 6.6, rule 10) on its line from a to b, level, its set-out line at E: each piece is its polygon carried across its width, a plate's on its member's faces (6.6, rule 9). A header, a dormer rafter, the ridge beam and a post is the closed body of its eight corners, its start's four and then its far end's in the same order: its six faces the end faces and the four sides between them, its volume by the divergence theorem over each face's triangles from its first corner.

  15. In IFC. A bridge writes each truss as an IfcElementAssembly of the predefined type TRUSS, its object type Truss, under its roof by an IfcRelAggregates, its GlobalId from truss:<truss id>, its name its name and its tag its key, with no body of its own, and its members under it, each as section 7.4.11 writes a wall's, keyed framing:<member id> and typed by class, predefined type and section, its material Timber and its family type's material, a nail plate's steel. It writes the headers, dormer rafters, ridge beam and posts under the roof the same way. Each anchor pair is an IfcMechanicalFastener in its roof's storey, its GlobalId from fastener:<key>, its name Truss clip pair, its object type TRUSSCLIP and its predefined type USERDEFINED, with no body: the rules give its place, not its shape. An IfcRelConnectsWithRealizingElements of the type TRUSS_TO_PLATE, keyed connects:<key>, connects its truss's assembly to its wall or beam, where both are written.

These are class-A rules, held to the tolerances of section 10: each member's body within 0.1 mm of the rules', and its volume within a relative 10⁻⁶; trusses, members, their ids, names, sections and classes exactly.

  • Names. The reference names a truss, a ridge beam, a ridge post and an anchor pair with its trade's Swedish words (Takstol T3, nockbalk, nockstolpe, Takstolsvinkel, par (truss clip pair)), while it names every member's role in English. By the steward's decision of 30 October 2026 these rules name them in English, Truss T3, ridge beam, ridge post and Truss clip pair, and the reference changes (open questions, section 2). Until it does, @openeik/tools' tests compare each name with the reference's, its Swedish words put into these English words one for one (packages/openeik/test/support/reference-names.mjs), so that the comparison stays exact. It names a profile part of a truss by its id twice (Takstol T1 · seat seat); rule 13 names it by its id once, as section 6.6 does.
  • An anchor pair's body. The reference gives each a box 60 × 60 × 40 mm at its point, which stands for no part's shape; rule 15 writes it with none.
  • A nail plate's material. The reference writes a truss's nail plates of its family type's timber; rule 15 writes them of steel, as Part VIII, 1.6 does a family's.
  • Ties. Where two walls are found alike for a side, or two gables or two dormer roofs stand alike, the reference takes the first in the order of its elements, which section 9 of Part III gives no meaning; these rules take the first by id.

Reference: deriveTrussLayout, trussPositions, trussChordLayer, wallBearing, trussElement, trussSolids and bearsOn in src/core/elements/trussLayout.ts; trussLayoutSchema in src/core/elements/trussLayoutRule.ts; layoutRange in src/core/elements/trussLayoutRange.ts; bearingFrame in src/core/elements/bearingFrame.ts; lineInside and seatOver in src/core/elements/trussHips.ts; framingProfiles (its interruptions and side) in src/core/elements/framing.ts; roofFaces and roofWindowPolygon in src/core/elements/roofs.ts; openingHeaderSection in src/core/elements/roofOpeningHeader.ts; trussMembers in src/core/elements/trussReport.ts; boxSolid in src/core/geometry/memberBox.ts; derivedFraming in src/core/io/ifcFraming.ts. In @openeik/tools: trussLayoutOf and trussMembers in packages/openeik/src/compile-framing-roofs.js, with familyParts in packages/openeik/src/compile-framing.js, called by framingLayouts in packages/openeik/src/compile-framing-layout.js.

7.4.14 The walls a truss layout bears on

A framed wall that a truss layout bears on is laid out by sections 7.4.1 to 7.4.11, with these rules besides.

  1. The trusses on a wall. Each truss of each roof's layout (7.4.13), the roofs in the order of their ids and each roof's trusses in their order, whose bearing on a side is the wall, the first side that is, and that rests on that side (7.4.13, rule 12, and 7.4.16, rule 9), stands on the wall as a truss line: at x = (its a, or its b on the right, less the wall's first point)·u along it, as wide as its family's Width, its bottom its family's BottomDepth over the bearing plane, and with its seat on that side (7.4.13, rule 9). Each ridge beam that bears in the wall (7.4.13, rule 11) bears on it too. A layout that is a finding bears on none.
  2. Studs under trusses. Where the rule's alignToTrusses is true, after the studs set by hand (7.4.6, rule 3), each truss line within the frame (from its least x + w/2 to its greatest − w/2, within 10⁻⁶) takes the grid's places within w + 50 of it out of the grid, and sets a place at its x, keyed t-<truss id>.
  3. The heel zone the trusses hold. Over a level plate or under a floor (7.4.4, rule 6), a truss line that has no seat over the bearing plane (rule 5) holds the heel zone over its width: each heel plate is cut at it, into its pieces more than w long, keyed <segment>.<k>, k from 0, wherever the layer has a truss line; and no heel post stands where it would overlap one.
  4. Bottom chords across battens. In a batten layer on which a floor bears (7.4.8, rule 1), each truss line with a bottom crosses the layer: a hole over its width about its x, from the floor's top up by its bottom, which cuts the rows and which the end battens start over.
  5. A seat. The truss lines whose seats are more than 1 mm over the bearing plane rest on the heel plate. Where the least of their seats is at least the heel plate's width plus w, the heel plate lies level, its top at the bearing plane plus that seat and with no drop across, continuous under them, whatever the heel zone's height; its heel members are cut by the layer's mitres alone (7.4.10, rule 2). Seats more than 1 mm apart are said. Where the least seat is less, it is said, and the trusses are taken to stand on the plane (rule 3).
  6. A ridge beam in a gable. For each ridge beam bearing in the wall, x0 to x1 its width about its x, held within the frame (none where that is under 10⁻⁶), in each framed layer whose inner face lies past the beam's end, y1 more than its y plus 10⁻⁶: where the layer holds its end (its outer face y0 at or before it), its post stands from the layer's bottom at x0 and x1, the higher, raised to the top of each sole plate, second sole plate and header over x0 to x1 whose top lies at or under the beam's underside, up to the beam's underside: x1 − x0 wide and as deep as the layer, across all of it, keyed beam-<beam id>, where that is at least w high (else said); and its members lose what lies in the rectangle from x0 to x1 and from the post's foot up to the beam's top, as section 7.4.8, rule 4 cuts them. Where the layer does not hold its end, its members lose what lies from x0 to x1 and from the beam's underside up to its top.
  7. A rafter's notch. A wall attached level to a layer of a roof (top.attach.face names a layer) that has a truss layout reads the face raised by its family's Notch, at least 0, as well as by its offset: for its envelope (7.4.3, rule 2), its members' bodies (7.4.10) and its top as rule 3 of 7.4.13 reads it.

These are class-A rules, held to the tolerances of section 10.

Reference: deriveWallFraming (its trusses, beams, applyBeamBearings and deriveHeelMembers) in src/core/elements/wallFraming.ts; trussLinesOnWall and beamBearingsOnWall in src/core/elements/trussLayout.ts; rafterNotch and wallTopClips in src/core/elements/wallTop.ts. In @openeik/tools: layoutLayer in packages/openeik/src/compile-framing-layout.js, given its trusses by trussLinesOnWall and beamBearingsOnWall in packages/openeik/src/compile-framing-roofs.js.

7.4.15 A floor's joists

These rules lay out a floor's joists from the joist rule of its type's layer (Part II, 5.1, a layer's joists): joists across the span between its bearing walls, trimmers beside its holes, headers across the edges the joists end at, rim joists on its bearing walls, and blocking in its end bays, each a rectangle in plan carried through the joist layer. In these rules lengths are in mm.

  1. A joist layer and its rule. A slab whose type is a slab type with a layer that carries joists is laid out, by its first such layer. Its rule is read with these values for what it leaves out: material C24, spacing 600, direction auto, rim 45 wide (null: none), trimmerPlies and headerPlies 2, and blocking with endBays true and spacing 600. A rule without a member's width is not read (framing-layout-joists). A joist is w, the member's width, wide, and d, its depth else the layer's thickness and at most that, deep; the layer's top lies under the slab's top, its level's elevation plus its offset, by the layers before it, and its underside d under that: z0 and z1, each to 0.001. A validator warns of a rule on a layer of a type that is not a slab type, or whose member is deeper than its layer (framing-layout-joists).
  2. The posts through it. A column whose base and top (its level's elevation plus its offset, and that plus its height) reach more than 1 mm into the layer from below and above (top more than z0 + 1, base less than z1 − 1) is a post: a square of its width about its first point. So is a ridge post of a truss layout (7.4.13, rule 11) that does: the box of its corners in plan. A post is kept where its box overlaps the box of the floor's outline (its profile). Columns are taken in the order of their ids, then the ridge posts, the roofs in the order of their ids.
  3. Its bearing walls. The framed walls, in the order of their ids, a point of whose baseline lies within 1000 of the outline's box, each with a framed layer that, laid out with no truss on it, ends under this floor (its bearing floor, 7.4.4, rule 4): the first such layer. A bearing wall gives its layer's faces, y0 and y1, and its own faces, 0 and T, as lines in plan.
  4. The joists' direction. With direction auto, the normal (−u_y, u_x) of a bearing wall across which the outline is least wide (the greatest less the least of its points' products with it), of equals the one that points the most along +x, then along +y. Without a bearing wall, x where the outline's box is no wider in x than in y, else y, which is said. With a number θ, (cos θ, sin θ). The joists run along d, the direction; across them runs û = (−d_y, d_x), and a point p of the plan lies at u = p·û, v = p·d.
  5. Its holes. Each of the slab's voids, and each shaft whose base lies under z1 − 1 and whose top over z0 + 1 (4.2, rule 8), is a hole: its box in (u, v), held within the outline's box, where it is at least 1 mm each way. Holes whose boxes touch or overlap, within 1 mm, are one, until none do. A hole whose corners, 1 mm in, do not all lie in the outline is not framed, which is said. Holes are ordered by their least u, then v, and keyed h<u0>_<v0>, each rounded to the millimetre. A hole wider in u than the spacing less w is framed; another that a joist overlaps is said.
  6. Its edges. The outline, and each framed hole's box, its corners wound as the outline's, are rings. Each edge of a ring at least 1 mm long runs along the joists (less than 1 mm in u), at u its middle's, over its v; or across them (less than 1 mm in v), at v its middle's, over its u; or neither, and is not framed, which is said. An edge's inward side is the one the floor lies on, as the ring is wound; an outline's edge is a hull edge where no point of the outline lies more than 1/ℓ outside its line, ℓ its length. An edge is numbered by its place in the outline, from 0, or keyed <hole key>.<i> in a hole's ring.
  7. Where a joist ends. An edge across the joists lies on a bearing wall where, of the bearing walls in order, the first has its layer's two faces and its own two faces so that, with v_in the layer's face nearer the floor's inside and v_out the other, and v_w the wall's own face nearer the inside: the edge lies no more than 60 inward past v_w, and no more than |v_out − v_in| + 60 outward of v_in; and the wall's baseline, widened by 300 at each end, overlaps the edge by at least 1 mm in u. A joist's end at an across edge (its v within 1 mm of it, the edge facing it, and its u within the edge's u, within 10⁻⁶) on a bearing wall is v_out less the rim's width, towards the joist; at another across edge it ends short of it by headerPlies·w; elsewhere at the floor's edge.
  8. Its members. Its joists' places are: on each edge along the joists, a joist w/2 inward of a hull edge (keyed e<edge>.0), else trimmerPlies trimmers (trimmer), (k + ½)·w inward of it, keyed e<edge>.<k>; and the grid, from the least u of the outline plus w/2 + spacing, every spacing, while it lies more than 10⁻⁶ under its greatest u less w/2, each keyed by its count from 1, but where it lies within w of a place on an edge; in order of u, each within 1 mm of the one before it dropped. Each place is a joist over each interval of the floor along its line (where the line lies inside the outline and outside the framed holes), from where it ends at the interval's one end to where it ends at the other (rule 7), where that is at least w; keyed <key>.<k> where its line has more than one interval. On each across edge on a bearing wall, where the rule has a rim, a rim joist of the rim's width on the outer face's inner side, along the edge, keyed by the edge; on each other across edge, headerPlies headers w wide, the first against the edge, inward, keyed <edge>.<k>. Where blocking.endBays is true and the floor has two joists' places or more, blocking in its first bay and its last (the first two places and the last two; one bay where there are two): for each interval of one joist and each of the other, from where they overlap plus the blocking's spacing, every spacing, while less than where they end less w, a row w wide about it, from one joist's side to the other's, keyed <start or end>.<the first interval's start, to 0.001>.<k>. A member is a rectangle in (u, v) at least 1 mm each way.
  9. Posts cut its members. Each post, in order, as its box in (u, v), cuts each member it overlaps by more than 10⁻⁶ into its parts before it and after it along the member's length (v where the member is at least as long in v as it is wide in u, else u), each kept where it is at least 1 mm each way; a member cut in more than one part is keyed <key>.<k>, k from 0.
  10. Each member: its name, its section and its class. A member's id is <floor>:<layer>:<role>:<key>; its name its role's name and its key, joist 3, rim joist 1; its section its width across its length by d, each to 0.001 and then to 0.1, 45 × 220; its length along it; its material its rule's material. Each role's name and its IFC4 class, predefined type and user type:
RoleNameIFC classPredefined type
joistjoistIfcBeamJOIST
trimmertrimmer joistIfcBeamJOIST
rimJoistrim joistIfcBeamUSERDEFINED, RIMBOARD
headerheaderIfcBeamUSERDEFINED, HEADER
blockingblockingIfcMemberUSERDEFINED, BLOCKING
  1. Its body, and in IFC. A member's body is the closed body of its eight corners (7.4.13, rule 14): its start's four, at its least along its length, and then its far end's, each at its two sides across, at z1 and then at z0, each plan point to 0.001. A bridge writes each member under its floor by an IfcRelAggregates, as section 7.4.11 writes a wall's.

These are class-A rules, held to the tolerances of section 10.

Reference: joistLayer, postsThroughFloor, floorFramingFor and floorFramingSolids in src/core/elements/floorFraming.ts; floorFramingRuleSchema in src/core/elements/floorFramingRule.ts; floorHoles in src/core/elements/floorOpenings.ts; framingIfcClass in src/core/elements/framingIfc.ts. In @openeik/tools: floorLayoutOf in packages/openeik/src/compile-framing-floors.js, called by framingLayouts in packages/openeik/src/compile-framing-layout.js.

7.4.16 A truss layout's hip ends

These rules frame each hip end of a truss layout (7.4.13, rule 2) as the reference implementation lays out a hipped roof's trusses: a girder of plies where the ridge ends, a hip truss of plies up each hip line to it, and jacks square to the end and to the sides up to them; or, where hips at both ends leave a short ridge, one girder along the ridge, which half trusses from both eaves hang on. Hip trusses and jacks are trusses of the layout's jack family, a girder along the ridge of its flat-top family: families written in the language of Part II, 5.4, which the rules make of the layout's own family where the model holds none (rule 2). In these rules lengths are in mm and points in plan. τ, r, across, along, the origin, the bearing plane E and w are those of 7.4.13 (rules 1, 2 and 4); P(t, r) is the point origin + along·r + across·t; τ_L(r) and τ_R(r) are where the line across the roof at the place r meets the two sides' reference faces (7.4.13, rule 6); and z(p) is the height over p of the face the chords' top lies on (7.4.13, rule 7).

  1. Its hip ends and its rule. A layout of two sides frames its hip ends; one whose hips.frame is false frames none, and says them (7.4.13, rule 2). Its hips is read with these values for what it leaves out: system auto, girderPlies 2, hipPlies 2, minJack 400, and jackSpacing the layout's spacing. Of a hip end's face, g its gradient (the plan direction it rises in, |g| the tangent of its pitch): its pitch is atan |g| in degrees, to 0.001; it lies at the start of the ridge where g·along is more than 0, else at its end; its apex a_r is the greatest r of its polygon's points at the start and the least at the end, and a_τ the mean τ of its points whose r lies within 1 of a_r: where its hip lines meet the ridge. Where two hip ends or more lie at one end of the ridge, the one of the longest edge is framed, of equals the first in the order of the faces, which is said.

  2. Its families. The jack family is the framing of the model's type whose id is the layout's family type's id followed by -jack, where the model has one with a framing, and the flat-top family that of the type whose id is followed by -flat: the layout uses such a type, and a validator does not warn of it as of a library record the model does not use (Part III, 13, rule 2). A family the model holds ought to be a truss family (7.4.13, rule 1), and a flat-top family to declare FlatLength of the scope instance as well; a validator warns of one that is not, which it can tell from the records alone (framing-layout-roof), since a compiler leaves out each hip truss and jack, or each girder along the ridge, it cannot build (rules 7 and 8). Where the model holds none, each is made of the layout's family f, as follows. A rafter family is one whose trussStyle is rafters or collar.

    • Its parameters are f's but Pitch, HeelHeight, OverhangLeft and OverhangRight, in their order; then, where f has none of the name, Width (45, from 20 to 200) and ChordDepth (170, from 45 to 600), and, but in the jack family of a rafter family, BottomDepth (170, from 45 to 500) and WebDepth (95, from 45 to 300), each of the scope type; then Pitch (20, from 1 to 75), HeelHeight (200, from 0 to 5000), OverhangLeft (500, from 0 to 3000), OverhangRight (0 in the jack family, 500 in the flat-top family, from 0 to 3000) and, in the flat-top family, FlatLength (0, from 0 to 50 000), each of the scope instance; each with its value and then its min and max.
    • It has f's tail. The jack family's trussStyle is rafters where f is a rafter family, else mono; the flat-top family has none.
    • Its parts are the member parts of the tables below, each Width wide, in their order. A point is its x and its z; a plumb cut at x is the cut from (x, 0) to (x, 1); and in the tables B stands for the expression BottomDepth/2, C(x) for HeelHeight-ChordDepth/(2*cos(Pitch))+tan(Pitch)*(x), D(x) for HeelHeight-ChordDepth/(2*cos(Pitch))+tan(Pitch)*(Length-(x)), K(x) for HeelHeight-ChordDepth/cos(Pitch)+tan(Pitch)*(x)-BottomDepth, X1 for (Length-FlatLength)/2, X2 for (Length+FlatLength)/2, F for HeelHeight+tan(Pitch)*(Length-FlatLength)/2-ChordDepth/2 and W for FlatLength-WebDepth-20, each written out with the text of x, X1 or X2 in its place: C(Length/2) is HeelHeight-ChordDepth/(2*cos(Pitch))+tan(Pitch)*(Length/2), X1/2 is (Length-FlatLength)/2/2, and K(0)-45 is K(0) followed by -45.

    The jack family of a rafter family has the part top alone, with no cutBy; any other jack family has these nine:

PartRoleabdepthwhenPlumb cuts atcutBy
bottombottomChord0, BLength, BBottomDepth
toptopChord-OverhangLeft-ChordDepth, C(-OverhangLeft-ChordDepth)Length+OverhangRight+ChordDepth, C(Length+OverhangRight+ChordDepth)ChordDepth-OverhangLeft, Length+OverhangRightbottom
heelLheelPostWebDepth/2, BWebDepth/2, C(WebDepth/2)WebDepthK(0)-45bottom, top
postwebLength-WebDepth/2, BLength-WebDepth/2, C(Length-WebDepth/2)WebDepthK(Length-WebDepth)-45bottom, top
diagwebLength/2, C(Length/2)Length-WebDepth, BWebDepthmin(Length-1200,3000-Length)bottom, top, post
post1webLength/3, BLength/3, C(Length/3)WebDepthLength-3000bottom, top
post2web2*Length/3, B2*Length/3, C(2*Length/3)WebDepthLength-3000bottom, top
diag1webLength/3, C(Length/3)2*Length/3, BWebDepthLength-3000bottom, top, post1, post2
diag2web2*Length/3, C(2*Length/3)Length-WebDepth, BWebDepthLength-3000bottom, top, post2, post

The flat-top family has these fourteen:

PartRoleabdepthwhenPlumb cuts atcutBy
bottombottomChord0, BLength, BBottomDepth
topLtopChord-OverhangLeft-ChordDepth, C(-OverhangLeft-ChordDepth)X1+ChordDepth, C(X1+ChordDepth)ChordDepth-OverhangLeft, X1bottom
topRtopChordX2-ChordDepth, D(X2-ChordDepth)Length+OverhangRight+ChordDepth, D(Length+OverhangRight+ChordDepth)ChordDepthX2, Length+OverhangRightbottom
topFlattopChordX1, FX2, FChordDepthW
heelLheelPostWebDepth/2, BWebDepth/2, C(WebDepth/2)WebDepthK(0)-45bottom, topL
heelRheelPostLength-WebDepth/2, BLength-WebDepth/2, D(Length-WebDepth/2)WebDepthK(0)-45bottom, topR
postLwebX1, BX1, FWebDepthmin(W,K(X1)-45)bottom, topFlat, topL
postRwebX2, BX2, FWebDepthmin(W,K(X1)-45)bottom, topFlat, topR
kingwebLength/2, BLength/2, HeelHeight+tan(Pitch)*X1WebDepthmin(WebDepth+20-FlatLength,K(X1)-45)bottom, topL, topR
diagLwebX1/2, C(X1/2)X1-WebDepth, BWebDepthX1-1500bottom, topL, postL, king, vertL
diagRwebLength-X1/2, D(Length-X1/2)X2+WebDepth, BWebDepthX1-1500bottom, topR, postR, king, vertR
vertLwebX1/2, BX1/2, C(X1/2)WebDepthX1-3000bottom, topL
vertRwebLength-X1/2, BLength-X1/2, D(Length-X1/2)WebDepthX1-3000bottom, topR
diagFlatwebX1+WebDepth, BX2-WebDepth, FWebDepthFlatLength-800bottom, topFlat, postL, postR
  1. The ridge's range and its girders. At a hip end, rule 5 of 7.4.13 is changed so. The range starts at the start's hip end's apex a_r, and ends at the end's. Where the layout has hip ends at both ends, its family's trussStyle is neither rafters nor collar, and its hips.system is ridgeGirder, or auto and the range is shorter than 2·spacing, it has a girder along the ridge (rule 8), and no truss is placed by 7.4.13, rule 5. Else, where it has hip ends at both ends and the range is shorter than 2·girderPlies·w + 1, the two share one girder: the range is narrowed to girderPlies·w about its middle, and its one truss stands at its first place. The first place is the range's start plus w/2 where a hip end lies at the start, else plus endOffset + w/2, and the last its end less w/2, or less endOffset + w/2; with no girder along the ridge, a last place more than 10⁻⁶ before the first is a finding. Where a hip end lies at the start and its girder is not shared, each place but the first that lies less than w − 10⁻⁶ past the first place plus (girderPlies − 1)·w is dropped; where one lies at the end and its girder is not shared, each place but the last that lies less than w − 10⁻⁶ before the last place less (girderPlies − 1)·w. The truss at the first place, where a hip end lies at the start, and the one at the last, where one lies at the end and the girder is not shared, is a girder: girderPlies trusses of the layout's family by 7.4.13's rules, its plies, the k-th, from 0, at its place plus k·w where it is the first truss, else less k·w; all of its mark, keyed by its index and, after the first ply, :ply<k> (0, 0:ply1).

  2. A hip set's end bearing. Each hip end gets a hip set, laid after the layout's other trusses: with no girder along the ridge, the start's and then the end's; with one, as rule 8 says. With s 1 at the start and −1 at the end, its girder face G is the range's start or end (rule 3), or, under a girder along the ridge, its apex a_r; and its heel h is the heel of 7.4.13, rule 7 at the first place (at the start) or the last (at the end): the layout's heelHeight, else z at the left point of the line across the roof there, less E; under a girder along the ridge, at the middle of the two apexes. Its bearing line is a place R along the ridge: with Z(r) = z(P(a_τ, r)), r1 = G − 100s, r2 = G − 1100s and m = (Z(r1) − Z(r2))/1000, R = r1 − s(Z(r1) − (E + h))/m where m is more than 10⁻⁶, else G − 1000s. Then its end wall: of the walls and the beams of two points or more, none with a frameHost and none a bearing of the layout, each running across the span within 5° (its first two points' direction, at least 1 mm apart) and overlapping the span between the two sides' reference faces (7.4.13, rule 5) in τ by at least half of it, whose first point's r lies within half its thickness (a beam's width) plus 1000 of R; each one's bearing found as rule 4 of 7.4.13 finds a side's, with r and τ exchanged (its reference face, by its bearingReference, the one of the lesser r at the start and of the greater at the end; its run in τ), where that bearing lies within 300 of E and its reference face, at its first point, within 300 of R in r; the one whose first point's r lies nearest R, the first by id of equals. Where it has one, R is that face's r. A set whose R is not finite, or that leaves less than 500 from R to G (s(G − R) < 500), frames nothing, which is said.

  3. Its hip trusses. The hip face's edge's two points in the roof's outline (its stored profile) are its left corner p_L, of the lesser τ (the edge's first of equals), and its right p_R; its apex point A = P(a_τ, a_r). For each side, its hip line runs from its corner p towards A: d = (A − p)/|A − p|, d_τ = d·across and d_r = d·along; it crosses the bearing line at τ = t_0 = τ(p) + (R − r(p))·d_τ/d_r, or τ(p) where |d_r| is 10⁻⁹ or less; and it starts at S, the point of it nearest the corner of the bearing lines, P(τ_L(R), R) on the left and P(τ_R(R), R) on the right. With ν the greater over the two sides of (w/2)|d_r|/|d_τ| (0 where |d_τ| is 10⁻⁹ or less), the centre jack is s(G − R) − ν long, and stands where the set has no girder along the ridge and it is at least minJack. The half width κ kept clear of the ridge line is girderPlies·w/2 under a girder along the ridge, else w/2 where the centre jack stands, else 0. Then, the left side's first: with n = (−d_y, d_x) and b = hipPlies·w/2, its drop is b·max(|g_s·n|, |g·n|), g_s the gradient of the side's eave face and g the hip face's; its pitch atan((z(A) − z(S))/|A − S|) in degrees; and its heel z(S) − E less the drop. Each ply, k from 0 to hipPlies − 1, runs from a = S + n·(k − (hipPlies − 1)/2)·w along d for its reach, the least, over its corners a ± n·w/2, of (G − r(corner))/d_r, where the set has no girder along the ridge and |d_r| is more than 10⁻⁹, and of (λ − τ(corner))/d_τ, where κ is more than 0 and |d_τ| more than 10⁻⁹, λ = a_τ − κ on the left and a_τ + κ on the right. A ply whose reach is not more than 300 is not laid. Each other is a hip truss (rule 7), keyed <end>:hip:<side>:<k> (start:hip:left:0), of the mark H<2j+1> on the left and H<2j+2> on the right, j the number of hip sets framed before this one; it bears on the side's bearing that stands at R (the first whose run holds R within 1 mm, 7.4.13, rule 4), else on the end wall, else on nothing.

  4. Its jacks. A jack runs from a point a along a direction d for a length L: its heel z(a) − E, and its pitch atan((z(a + dL/2) − z(a))/(L/2)) in degrees. One shorter than minJack is not laid. Each other takes the mark J<i>, i counting the layout's jacks from 1 in the order they are taken, and is a truss of rule 7. With J the jackSpacing, a set's jacks are, in this order:

    • its centre jack (rule 5), from P(a_τ, R) along s·along, keyed <end>:jack:c;
    • its end jacks, the left side's and then the right's, of each side where t_0, d_τ, d_r, τ_L(R) and τ_R(R) are finite and |d_τ| is at least 10⁻⁹: for k from 1 to 500, at t = a_τ − kJ on the left and a_τ + kJ on the right, each but where |t − a_τ| − w/2 is less than κ, and none more from where its near side, t_n = t − w/2 on the left and t + w/2 on the right, reaches the hip line or the side's corner (t_n at most max(t_0, τ_L(R)) on the left, at least min(t_0, τ_R(R)) on the right): a jack from P(t, R) along s·along, s(r_e − R) long, where r_e = R + (t_n − t_0)·d_r/d_τ − s·b/|d_τ|; keyed <end>:jack:el<k> on the left and er<k> on the right;
    • its side jacks, at places r along the ridge: without a girder along the ridge, G − s(k + 1)J for k from 0 to 499, in that order, those where s(r − sw/2 − R) is more than 0; under one, its grid's places (rule 8), from the least, where s(r − sw/2 − R) is more than 0 and s(a_r − (r + sw/2)) is at least 0. The left side's and then the right's, of each side where t_0, d_τ and d_r are finite and |d_r| is at least 10⁻⁹, at the k-th place, from 1: with r_n = r − sw/2 and t_h = t_0 + (r_n − R)·d_τ/d_r, a jack from P(τ_L(r), r) along across, t_h − b/|d_r| − τ_L(r) long, on the left, and from P(τ_R(r), r) along −across, τ_R(r) − t_h − b/|d_r| long, on the right; keyed <end>:jack:sl<k> and sr<k>.

    The centre jack and an end jack bear on the end wall where their τ lies within its run, within 1 mm; a side jack on the side's bearing that stands at its place. Jack or hip truss ends with no bearing are said.

  5. A hip truss or a jack as a truss. Its parameters are its Pitch and HeelHeight, its OverhangLeft, how far its line runs on behind a inside the roof's domain (−t for the least t at which a + d·t lies in it, where that is finite and less than 0, else 0), and OverhangRight 0; each to 0.001. Its parts are the jack family's, built by section 6.6, rules 2 to 9, with its length L as Length; where they cannot be, it is left out, which is said, and its mark is given to no other. Its id is <roof>:truss:<key>; its place, the r of the middle of a and b = a + dL, its points a and b and its span L are each to 0.001, at E. It bears on its left side only, on its bearing. Its seat there, where its bearing has the two faces of a frame (7.4.13, rule 4), is the least height of its pieces between the two distances along d from a at which its line crosses the faces' lines (7.4.13, rule 9), to 0.001, as its family's plane has it, with no change for the bearing's own elevation; it has none where d·k lies within 10⁻⁹ of 0 for a face, k the face's normal.

  6. A girder along the ridge. Its two hip sets are laid out first, the start's and then the end's (rules 4 to 7), each with its side jacks at its grid's places. Its grid is the middle of the two apexes, M, and M ± kJ for k from 1 to 500, while either lies within 20 000 of the range, from least to greatest. With R_s and R_e the two sets' bearing lines, its line lies at τ_g, the mean of the two hip ends' a_τ, from r_0 = M − ℓ/2 to r_1 = M + ℓ/2, where ℓ = 2ρ + φ, φ = max(0, a_r(end) − a_r(start)) its flat and ρ = (a_r(start) − R_s + R_e − a_r(end))/2 its rise. Its pitch is the mean of the two hip ends' pitches; where they differ by more than 0.5°, that is said. Its drop δ is girderPlies·w/2 times the greatest |g·across| of the two eaves' gradients g, and its flat top φ + 2δ/tan(pitch) (φ where the tangent is 10⁻⁹ or less). It gives the flat-top family Pitch, its pitch; HeelHeight, z(P(τ_g, r_0)) − E; OverhangLeft, how far its line runs on before r_0 inside the domain, and OverhangRight, after r_1 (rule 7); and FlatLength, the lesser of its flat top and ℓ; each to 0.001. Where the family cannot be built with ℓ as Length, the girder is said, and neither it nor its half trusses are laid. Else its girderPlies plies, the k-th from 0 on the line at τ_g + (k − (girderPlies − 1)/2)·w from r_0 to r_1, each keyed ridge:<k>, of the mark of the layout's first listed position, else T1, at the place M, of the span ℓ, bearing on the two sets' end walls, the start's on the left, with no seat. Then its half trusses: at each place r of its grid that lies between the two apexes, more than 10 from each (r − w/2 > a_r(start) + 10 and r + w/2 < a_r(end) − 10), the k-th from 1, a jack (rule 6) on the left and one on the right, from the side's reference face to the girder's side face: from P(τ_L(r), r) along across, τ_g − girderPlies·w/2 − τ_L(r) long, keyed ridge:jack:l<k>, and from P(τ_R(r), r) along −across, τ_R(r) − τ_g − girderPlies·w/2 long, keyed ridge:jack:r<k>, each bearing on its side's bearing that stands at r.

  7. Where they rest and hang. A hip truss and a jack rest on their first side only, where they have a bearing (7.4.13, rule 12): an anchor pair at a. Each also hangs at its far end: a hanger of one bracket at b, at E, keyed <truss id>:hanger and named Hanger, hip truss to girder for a hip truss, Hanger, jack to girder for a centre jack and a half truss, and Hanger, jack for any other jack. The layout's connections are its anchor pairs, in the order of its trusses, and then its hangers, in the same order.

  8. Names, members and IFC. A hip truss is named Hip truss and its mark, and (ply <k + 1>) after it for a ply after the first; a jack Jack truss and its mark; a girder's ply Truss and its mark with (girder), (girder, ply <k + 1>) after the first, and a girder along the ridge's with along the ridge after girder: Truss T1 (girder, along the ridge, ply 2) (7.4.13, rule 13). Each one's members are its family's parts, by 7.4.13, rules 13 and 14, its jack or flat-top family's for a hip truss, a jack and a girder along the ridge; their material is the layout's family type's. In IFC each is a truss of 7.4.13, rule 15. A hanger is an IfcMechanicalFastener in its roof's storey, its GlobalId from fastener:<key>, its name its name, its object type HANGER and its predefined type USERDEFINED, with no body; it connects no element, since what it hangs on, a girder's or a hip truss's plies, is no one element.

These are class-A rules, held to the tolerances of section 10: each member's body within 0.1 mm of the rules', and its volume within a relative 10⁻⁶; trusses, members, connections, their ids, names, sections and classes exactly.

Reference: hipEndsOf, hipSet, ridgeGirder, lineInside and seatOver in src/core/elements/trussHips.ts; jackFamily, flatTopFamily, jackType and flatTopType in src/core/elements/trussFamilies.ts; deriveTrussLayout (its hip ends, its range at a hip, its girders and its endWall), bearsOn and trussName in src/core/elements/trussLayout.ts; derivedFraming in src/core/io/ifcFraming.ts; exportIfc (its fasteners) in src/core/io/ifcExport.ts. In @openeik/tools: hipEndsOf, hipSet, ridgeGirder, jackFamily, flatTopFamily, hipFamilies and trussName in packages/openeik/src/compile-framing-hips.js, called by trussLayoutOf in packages/openeik/src/compile-framing-roofs.js.

7.5 A room's boundary, ceiling and quantities

Part of 0.2. OpenEik 0.1 had it as the proposal rooms: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), room boundaries are reference behaviour, as section 7.2 says. The section adds no file and no member: the members are those of a room's room, a wall's and a column's roomBounding, a room separator's profile and the project's rooms (Part II, 2, 6.1 and 6.3).

By the steward's decision of 26 October 2026, and of 30 October 2026 for a roof that comes down through the floor (open questions, 72), the boundary of a room placed by a point, found from the walls, columns, room separators and roofs round it, the ceiling of every room and its quantities are these rules, as the reference implementation finds them. A room drawn by hand (Part II, 6.1, rule 4) keeps its outline, and takes its ceiling and its quantities from them.

A room's level is its levelId, and its floor that level's elevation plus its offset. Its point is its profile's one point; for a room drawn by hand, its outline's centroid where that lies in the outline, else the middle of the first of its diagonals, from a point to the next but one, that does, else its first point.

  1. What bounds rooms. On a room's level, each wall and each column whose status is not demolished, and whose roomBounding is true, or which gives none and is no wall whose offset is 1900 mm or more, bounds rooms with its plan:

    • a wall's: the convex hull, in plan, of its layers' bodies (section 4.1) as its joins end them, its points at each end within 1 mm of the furthest along it moved 15 mm further out along it, so that a joint up to 30 mm wide between two walls in line closes. Each layer's band is taken from the wall's base to its top (section 5.1), between the heights at which the line its ends stop on changes. Each point at an end moves along the wall to the line section 7.4.2 joins that end on at its height: at a corner, its mitre (7.4.2, rule 8) over the heights its corner covers (rules 3 and 4), and its partner's outer face above and below them; at a T, the host's core face on its side (rule 7). It moves only where that takes it no further than half the wall's length, or all of it where the corner fits only with room for its mitre (rule 6), and no further than four times the wall's thickness and 1 mm; the band is then cut on the line, keeping the side of the wall's middle, or of its other end where the corner fits only so. An end with no corner and no T, and an end section 7.4.2 keeps free (joinEnds), stays square. A wall with a wallOutline runs from its outline's least x to its greatest, an end that reaches the wall's end stopping on its main join's line (7.4.2, rule 5), and every end kept within it;
    • a column's: the convex hull of its body in plan (section 6.1), its section's outline, or a square of its width, centred on its point and turned by its turn. An arc of the outline is the rule; a compiler MAY draw it in chords that come within 0.01 mm of it.

    Each room separator on the level bounds rooms with a line from each of its points to the next, where they are at least 1 mm apart, lengthened by 1 mm at each end.

    A roof that comes down through the floor, a loft's, bounds rooms too. A roof with a roof, but for a barrel vault, has a face for each of its planes that is the lowest of them over some of its extent (its outline, each edge moved out by its overhang, each corner where the moved edges' lines meet; section 4.4, rules 2 and 3): the part of its extent where that plane is the lowest, of more than 0.01 mm². Its underside over a face is that plane lowered by the roof's thickness measured square to it (4.4, rule 4; a thickness is its type's layers' total, else its height). Where that underside lies under the level's elevation plus 10 mm at a corner of a face and over it at another, of any of its faces, the part of each face where it lies at that height or under, of more than 1 mm², is a plan. And then the edges of the floors the level stands on bound rooms as lines, each at least 1 mm long and lengthened by 1 mm at each end: the edges of the union of the outlines of the slabs and foundations of the level whose top lies less than 1000 mm under its elevation and less than 300 mm over it, and of those of other levels whose top lies within 50 mm of it, and the edges of their voids; a demolished one, and a round footing, is no floor.

  2. The faces. The plans and the lines cut the plane into faces: the parts of the plane outside every plan and off every line, each in one piece. A face that is bounded, and whose outer ring encloses more than 100 mm², is a room's face. Its boundary is its outer ring and its holes, the outer rings of the groups of plans and lines that touch one another, lie inside it and touch none of those round it. A line that ends inside a face is part of its boundary, which runs along it on each side; a line that touches no plan and no other line divides nothing and is part of no boundary. The boundary's points are where the plans' edges and the lines meet, found to the model resolution of 0.01 mm (section 10).

  3. A room placed by a point has the boundary of the face that holds its point inside it, off every ring of the face's boundary. Where none does, as where the point lies on a ring or in a plan, it has the boundary of the first face that so holds the point moved 1 mm along +x, along −x, along +y or along −y, in that order. Where no face holds it, it has no boundary, and a validator SHOULD warn of it (rooms-enclosed).

  4. One room to a face. Where the points of two rooms or more lie in one face, the room whose id comes first in code-unit order (Part III, 9) keeps it, and each of the others has no boundary; a validator SHOULD warn of each of them (rooms-shared). A room drawn by hand has its outline as its boundary, without holes, whatever faces it lies in.

  5. The ceiling is a surface over the room's boundary, from which its height, its walls' areas and its volume are measured:

    • where its room.height is given, the level plane that far over its floor;
    • else, at each point, the lowest of the planes over the room that are there: the underside of each slab and each ceiling whose outline, as recorded, holds the room's point, and none of whose voids does, level at its top less its thickness (section 4.2, rule 1), where that is more than 100 mm over the floor, there everywhere; and, of each roof with a roof whose extent (rule 1) holds the room's point, or leaves three points or more of the room's boundary's outer ring cut on the inner side of each of the extent's edges' lines in turn (the part of the room under it, where the extent is convex), the underside, there over its extent alone: of a pitched roof, its face's planes lowered as rule 1 lowers them, and of a roof none of whose edges defines a slope, its top less its thickness. A roof's underside is taken where it is more than 100 mm over the floor at the room's point, if the extent holds the point, or at a corner or at the centroid of that cut part of the room;
    • where neither is, the level plane of the lowest level 1800 mm or more over the floor, else the room's element's height over its floor (the room height, Part II, 6.2);
    • where only roofs' undersides are, that same plane is the ceiling over the part of the room that no roof's extent holds.

    A slab, a ceiling or a roof whose status is demolished is none. A thickness is its type's layers' total, else the element's height (4.2, rule 1). A barrel vault (roof.vault), whose faces are reference behaviour, is no ceiling by rule: a room whose point its extent holds, or whose boundary it cuts as above, has no ceiling by these rules, and a compiler says so.

  6. The quantities, under the names of IFC4's Qto_SpaceBaseQuantities. Each stretch of a room's boundary lies against the plan that holds the point 0.05 mm out of the stretch's middle, square to it and away from the room, else the point 1 mm out, else 3 mm out; of the plans that hold the point, a wall's before a column's before a roof's, and of two walls' the one whose id comes first in code-unit order. A stretch along a line lies against that line, a room separator's before a floor's edge. Any other stretch lies against nothing. The room's walls are its stretches that lie against walls and columns.

    • NetFloorArea: the area of its boundary's outer ring less its holes'.
    • GrossPerimeter: the length of its boundary, its outer ring and its holes.
    • NetPerimeter: the length of its walls, less, unless the project's rooms.doorsFromSkirting is false, the width along them of each door in them.
    • GrossWallArea: the area of its walls up to its ceiling: along each stretch, the ceiling's height over the floor, where the ceiling is above it.
    • NetWallArea: GrossWallArea less, for each door and each window in its walls whose area is at least the project's rooms.openingMinArea (m²; 0 where it gives none), that area times its width along them over its width, at most its whole area; and never less than 0. A door or a window is in the room's walls where its host is a wall one of the room's walls lies against, its opening (section 4.3, from its host's base plus its offset, height high) begins under the ceiling's highest point over the room and ends over its floor, and its span along its host, from along to along + width, overlaps by 1 mm or more the stretches against its host, each measured along its host's baseline: that overlap is its width along them. Its area is its type's outline's, else its width times its height.
    • NetCeilingArea: the area of the ceiling's surface over the room's boundary, a sloped plane's measured on its slope.
    • NetVolume: the volume from the room's floor up to its ceiling over its boundary, where the ceiling is above the floor.
    • Height: NetVolume over NetFloorArea, the room's mean height.

    National area rules stay the application's (Part II, 8).

  7. The volume. A room's body, for a bridge or a view, is its volume: over its boundary, from its floor up to its ceiling where the ceiling is above it, as a prism for each part of the boundary that lies under one set of the ceiling's planes. It is no body of the model's (Part II, 6.2: a room has none): it cuts nothing, and a clash check does not read it.

These are class-A rules, held to the tolerances of section 10.

These rules leave the reference implementation to change where this specification decided otherwise, and where the reference contradicts itself: a room separator, and a floor's edge, as a line; two rooms in one face given to the one whose id comes first, where it gives the face to the one first in its list of elements; the element's height as the last ceiling, where it takes 2500 mm (open question 72); a room's ceiling area and volume under a roof whose extent is not convex measured over the extent as it is, where it cuts the room by the extent's edges as if it were convex, as it does not when it measures the room's walls; a door's or a window's opening from its host's base, as section 4.3 places it, where it measures from its host's level; a room's floor at its level plus its offset, as Part I, 4 places an element, where it takes its level alone; and a barrel vault no ceiling and no bound of a room, where it takes the facets of its own vault. None of these made a difference on the rooms compared.

Reference: roomsOf, roomBodies, roofBodies, levelFloors, pastEnds, traceUnion and enclosedRegions in src/core/elements/rooms.ts; joinedWallSolids in src/core/elements/wallJoinSolids.ts and profileSolids in src/core/geometry/profileGeometry.ts, on the joins of src/core/elements/wallJoins.ts; ceilingPlanes, roomEdges, roomQuantities and roomVolume in src/core/elements/roomQuantities.ts; spaceTriangles in src/core/io/ifcSpaces.ts. In @openeik/tools: roomContext, wallPlan, roofShape, levelBounds, planFaces, faceAt, roomsOf, ceilingOf and measureRoom in packages/openeik/src/compile-rooms.js, on wallJoins in compile-framing-joins.js, built by compileRooms in compile.js.

7.6 A component's frame and its bodies

Part of 0.2. OpenEik 0.1 had it as the proposal components (Part II, 5): in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), what componentInverted, axes, upright, plate, bolts, weld and insertAt do to a component's body is reference behaviour, as Part II, 6.3 says. The section adds no file and no member.

By the steward's decisions of 26 October 2026 (open questions, 73), which completes his decision of 17 October 2026, and of 30 October 2026 (open questions, 99), a component's frame, and its bodies that are boxes and prisms, are class-A rules, as the reference implementation places and builds them but where question 99 decided otherwise.

  1. Its frame and its bodies. A component (Part II, 6.2) stands in a frame: rules 2 to 4 give it, or, for a cast-in item placed by insertAt, rule 5 gives each of its items one. A point (u, v, w) of a frame is its origin moved u along its x, v along its y and w along its z. Its bodies are rule 6's and rule 7's, by its type's family (Part II, 5): a timber section's (timberSection), a plate's (plate), a weld's (weld, with the element's weld), a bolt group's (bolt, with the element's bolts and its type's component.bolt), and a cast-in item's block or bar (its type's insert.shape, block or bar). Rule 8 says what stays reference behaviour. A component that has no frame, as rules 2 to 5 say, has no body, and a validator SHOULD warn of it (component-frame).

  2. Its frame. Its origin is its insertion point, its profile's first point, at its level's elevation plus its offset. Where it gives axes, they are its frame, whatever its family: its x is axes.x made a unit vector, its y is axes.y made square to x and a unit vector, and its z is x × y, or y × x where it is flipped (Part II, 6.3); axes whose x is zero, or whose y runs along x, give no frame. Without axes, its x runs in plan from its profile's first point towards its second, its y is the left normal of x in plan, or its right normal where it is flipped, and its z is up; a profile whose two points are one gives no frame (Part II, 6.1, rule 8).

  3. A plate on a column. A plate whose hostId names a column stands on it: its origin is the column's centre, the column's profile's first point, at the column's top (5.2, rule 4) plus the plate's own offset. Its axes are rule 2's, from its own profile or its axes. A column without a level, a centre or a height gives it no frame.

  4. Hung down and stood up. A component without axes whose componentInverted is true hangs its box (rule 6) or its plate (rule 7) down from its origin, from w = −height to w = 0, where it otherwise stands from w = 0 to w = height. A plate without axes whose upright is true stands on its edge: a point (u, v) of its outline lies u along its frame's x and v up, and its thickness runs along its frame's y, centred on its origin, from −height/2 to height/2, whatever its componentInverted says. Neither member changes a weld, a bolt group, a cast-in item or a component with axes, and upright changes a component of no other family.

  5. A cast-in item's frame. A cast-in item, a component whose type has an insert, placed by insertAt stands in the wall its hostId names, whose profile is two points, P0 and P1: u = (P1 − P0)/L along the wall, L = |P1 − P0|, n the left normal of u, T the wall's thickness (4.1), H its height, and its base at its level's elevation plus its offset.

    • Its point lies along from P0 along u, up from the wall's base and across in from its exterior face: P0 + u·along + n·(T/2 − across), at the base plus up. along is 0 where it gives none, L on the face end; up 0, H on the face top; and across 0 on the face exterior, T on interior and T/2 on the others.
    • Its ways. The way into the wall from its face, d, is −n from exterior, n from interior, down from top, up from bottom, u from start and −u from end. Its length's way, x, is u on exterior, interior, top and bottom, and up on start and end; where its turn is 90, x is up on exterior and interior, and n on the others. Its third way is y = d × x.
    • Its body runs in along a = d·cos(tilt) + x·sin(tilt), tilt in degrees, 0 where it gives none: a point (u′, v′) at a depth δ lies at its point + u′·x + v′·y + δ·a, so that a body that leans keeps its end in its face.
    • Its items. It has count items, 1 where it gives none, the k-th, from 0, k·spacing further along the wall, or up it where its direction is up, spacing 0 where it gives none.

    Its profile, levelId and offset are its writer's copy, which a reader ignores where insertAt is given (open questions, 73). A cast-in item whose hostId names no wall whose profile is two distinct points has no frame, and one that insertAt does not place has no body.

  6. A timber section is a box in its frame: u from −width/2 to width/2, v from −depth/2 to depth/2, and w from 0 to height (rule 4), its width and height the element's and its depth its type's component.depth.

  7. A plate, a weld, a bolt group, and a cast-in item's block and bar.

    • A plate is its outline less its holes, height thick. Its outline is its own plate.outline, points (u, v) in its frame, else its type's width by its component.depth, centred on its origin; its holes are its type's component.holes and its own plate.holes, each a circle of its diameter centred at (x, y), or, where its length is more than its diameter, a slot that long overall along angle degrees from u, its ends half circles (Part II, 5); its thickness runs from w = 0 to w = height (rule 4).
    • A weld is a fillet: the triangle (v, w) = (0, 0), (leg, 0), (0, leg), leg = weld.throat·√2, carried along u from 0 to its width (Part II, 6.3).
    • A bolt group is each bolt's shank within its grip: at each of its positions (u, v), a cylinder of its bolt's diameter along w, from w = 0, the face its head, or an anchor's nut, bears on, to w = grip (open questions, 99).
    • A cast-in item's block is, at each item, the box from u′ = −width/2 to width/2 and v′ = −height/2 to height/2, its type's, run in from δ = 0 to δ = insert.depth (rule 5); its bar, the round bar from δ = 0 to insert.depth along a, its insert.diameter round, else its type's width.

    A compiler MAY draw a hole's circle and a slot's ends in straight pieces, as it draws a pipe's circle (12.3, rule 5): the circle is the rule, and a component's volume is its bodies', its holes' circles exact.

  8. What stays reference behaviour (class C): a bolt's head, washers and nut, an anchor's plate and the nut at its foot, and a shank past its grip, sized from a table of ISO bolts no record holds; the holes a bolt group drills in what its through names, a plate among them (7.2); an angle bracket's and a joist hanger's legs and nail holes; a cast-in item's lattice tie, its conduit and its bends, the cut its recess former makes in its wall, and the bar and the edge detail its product asks for at each item; a void form's and an edge detail's bodies; and the shapes of the families toilet and basin, deprecated in 0.2 (Part II, 5). A compiler says which of them a component has, and a supplier who needs one of them exact declares it (Part II, 12.1).

These are class-A rules, held to the tolerances of section 10. A component's IFC class is its type's (Part VIII, 1, its note on components): a plate an IfcPlate, a bolt group an IfcMechanicalFastener, a weld an IfcFastener WELD, and any other an IfcBuildingElementProxy.

Reference: partFrame in src/core/elements/partFrames.ts; componentSolids, plateFrame and plateOutline in src/core/elements/components.ts; holeLoop in src/core/geometry/holedCells.ts; placedPlate and plateBaseZ in src/core/elements/plates.ts; weldSolids in src/core/elements/welds.ts; boltFrame and boltGroupSolids in src/core/elements/bolts.ts; insertPoint in src/core/elements/inserts.ts; insertPlacements and insertSolids in src/core/geometry/insertSolids.ts. In @openeik/tools: componentFrame, castInItems and componentBodies in packages/openeik/src/compile-components.js, called by compileComponentBody in compile.js.

8. Body kinds (informative)

The reference compiler produces six kinds of body. The plan proposes them as the normative kinds N1–N6 of a later version.

KindReference representation
N1. A vertical prism of a plan polygonprofile, bottom, top
N2. A convex polyhedron, or a set of convex cellsvertices, faces, cells
N3. A layer between two planes (pitched roofs)prisms clipped by half-spaces
N4. A member swept along a line, square to itvertices, faces, with an axis
N5. An exact cylinder or a rodround (centre, radius); rod (two points, diameter)
N6. An opaque mesh with a stand-in boxshape (indexed mesh), with a proxy body for sections and quantities

A pipe's or a duct's body (section 12) is a closed mesh of vertices and faces; the reference implementation also cuts it into convex cells between its rings (N2), so that a section shows a bend as a bend.

Reference: the Solid type in src/core/geometry/geometry.ts.

9. Quantities (informative)

The reference implementation reports, per element, a volume in m³ with its basis ("per-element solids; openings deducted; matching endpoint wall mitres included; other inter-element overlaps not deducted"), the volume of each layer, a wall's gross, opening and net area per side on its centreline, and a roof's sloped area, perimeter, eaves, verges and ridge. 0.1 does not standardise quantities; the plan proposes typed values {value, unit, basis, rule, version}.

For examples/one-storey-house the reference compiler gives:

ElementQuantityValueBy hand
slab-groundvolume33.8355 m³10.3 m × 7.3 m × 0.45 m
slab-groundlayer concrete7.519 m³75.19 m² × 0.1 m
wall-southgross area, one side27.0359 m²10 m × 2.70359 m: the roof's underside over the centreline lies at 2700 + 350 − 300/cos 30° = 2703.59 mm
wall-southopenings3.9 m²1.0 × 2.1 + 1.5 × 1.2
wall-west (gable)gross area, one side25.9977 m²7 m × 2.70359 m + ½ × 7 m × 3.5 m × tan 30°
roof-mainsloped area95.4706 m²(10 + 2 × 0.3) m × (7 + 2 × 0.4) m / cos 30°
roof-mainridge10.6 m10 m plus two 0.3 m gable overhangs

These values are informative; they are what a conformance vector for this folder would check.

10. Tolerances and conformance (proposed)

The plan proposes that a compiler conforms when, on published vectors, each element's bodies lie within a Hausdorff distance of 0.1 mm of the reference, volumes and areas within a relative error of 10⁻⁶, and counts are exact, with a model resolution of 0.01 mm. This specification publishes no vectors yet; until it does, the class-A rules above are the requirement and the table in section 9 is an example. Pipes and ducts, whose faceting is the reference implementation's and not the rule's, have a tolerance of their own (12.5).

11. A supplier's parts

This section says what a supplier's parts (Part II, 12) ask of a compiler: declared geometry (Part II, 12.1) and railings for product systems (Part II, 12.2).

11.1 Declared geometry

A type or an element may carry a supplier's declared geometry at a declared level of detail, with the aspects it is authoritative for (Part II, 12.1). A compiler treats it so:

  1. It derives nothing beyond what a record declares itself authoritative for. From an envelope it MAY derive the space the part takes (its bounds, its volume as space taken, sections and plans through it), and nothing about what is inside: not its materials, its parts, a count of parts or a cut list.
  2. For a declared aspect, the declaration wins over the compiler's own rule: a declared envelope replaces the body derived from the type's payload where a body stands for the space taken (clashes, sections, exports); declared positions replace a reference layout (the posts of a railing).
  3. For an aspect that is not declared, the compiler derives as it does for any record of the category, and says which results come from the declaration and which from its own rules.
  4. It MUST NOT derive fabrication data from declared geometry. When a declaration is authoritative for fabrication, a compiler or a bridge passes that data on as the supplier gave it, and does not work it out again.
  5. Nothing it derives is stored (section 1): the declaration is a record's own content, and the results a compiler works out from it are derived like any others.
  6. A bridge that exports a declared envelope as a product's body SHOULD say so in its fidelity report (Part VIII, 1), with the declaration's level.

Reference: declaredSolids in src/core/geometry/declaredBodies.ts (rules 2 and 3: a declared envelope, or declared parts, drawn in place of the derived body for 3D, sections, quantities and IFC).

11.2 Railing lines and posts

The line of a railing on the outline of a slab or a shaft, and its given posts, are class-A rules. The line of a railing on a stair stays reference behaviour (class C): it follows the stair's derived flights.

  1. The run. The edges railing.edges names are taken in their order round the host's outline, as one run (Part II, 12.2). The run's points are the outline's points from the start of its first edge to the end of its last; when it names every edge, the line is closed.
  2. The walking side is the inside of a slab's outline, and the outside of a shaft's (the floor round an opening).
  3. The offset. Each edge's line is moved towards the walking side by its inset: railing.insets[<edge id>], else railing.inset, else 50 mm (the reference default). A negative inset moves it off the edge, as side mounting needs. When every edge of the run has the same inset, each corner moves along its bisector by the inset divided by the cosine of half the turn at the corner, but never by more than 4 times the inset. When the edges' insets are not all the same, every corner of the run is where the moved lines of its two edges cross, or halfway between their ends where those lines are parallel or cross farther than four times the larger inset from the corner (Part II, 12.2).
  4. The base. The line stands on the host's top: elev(levelId) + offset of a slab, or the top of a shaft (section 5.1).
  5. Given posts. With railing.posts.at, a post stands at each listed distance s along the line from its first point, measured in plan, and nowhere else. A post is postWidth along the line by postDepth across it (post by post without them), centred on the line and square to its segment (at a corner, square to the corner's bisector). It rises from the base, or from belowBase below it when mount is side, to the underside of the top rail, which carries the railing to its height above the base.
  6. Without given posts the layout is reference behaviour. The reference implementation places a post at both ends, at every corner, and evenly between them, no farther apart than postSpacing; with postEndDistance it stands the first and last posts that far from the ends, and it leaves out the end posts posts.omit names.
  7. Rails, infill and handrails stay reference behaviour.

By the steward's decisions of 21 and 24 October 2026 (Part II, 12.2; open questions, 18), in 0.1 since 24 October 2026, the lines of railings on a wall's top and along a slab edge element are derived so; both are class-A rules, a slab edge element's since its section became one on 26 October 2026 (section 4.6):

  1. On a wall's top. Let a, b be the wall's baseline, L its length, u = (b − a)/L, n its left normal and T its thickness (4.1), and d the railing's inset, else T/2. A point x along the wall, from 0 to L, is at a + u·x + n·(T/2 − d) in plan. Its height is the wall's top there: where the wall has an elevation outline (an attached top, a profile of its own; section 5.3), the upper edge of that outline, with a point at each of its corners and two at one x where the top steps; else the wall's height over its base. The line runs from one end of the baseline to the other.
  2. Along a slab edge element. With the element's section across its edge, its rectangles and its facing, as section 4.6 places them (its chamfer not counted), its outer face is the least reach of the section from the edge's line, r_out, and its top face the part of the section within 0.5 mm of its highest point, from reach r0 to r1. The line runs at the reach r_out + d from the edge's line, d the railing's inset, else (r0 + r1)/2 − r_out, along the element between its own ends, square, or on a corner's bisector where the element is mitred (4.6, rule 6), at the height of the element's top, and never below its slab's top.
  3. Walking side (rule 2): a wall's interior, the right of its baseline; the slab's side of a slab edge element.
  4. Posts stand by rules 5 and 6; on a wall's top, its corners include every point where the top steps or its slope changes.

Reference: railingLine and offsetLine in src/core/elements/railings.ts; offsetEdges in src/core/elements/railingEdges.ts (different insets); railingPosts in src/core/elements/railingPostLayout.ts.

11.3 Generic elements

A generic element (Part II, 6.6) has no shape but its declared geometry.

  1. A compiler derives nothing for it beyond placing its declared bodies: its own declaration's, in the world frame, and its type's, in the frame of a category placed at a point (Part II, 12.1), at its insertion point, turned to its orientation, on its base. Where both declare an aspect, the element's wins (Part II, 12.1).
  2. Its quantities are those of its declared bodies: a volume from its closed bodies, as space taken (11.1, rule 1), with the basis declared.
  3. It cuts nothing, and no rule joins it to another element.

12. Pipes and ducts

A pipe or a duct is a run (Part II, 6.2): its points in plan, a height for each, a size and a system. Its body is its outer section swept along the run with each corner rounded into a bend. The body and its quantities are class-A rules; the fittings found between runs (a branch where one run's end lies on another, a reduction or a joint where two runs meet end to end) stay reference behaviour (section 7.2).

12.1 The run

Given a pipe or a duct with profile points p_0 … p_(m−1), its mep payload and its type:

  1. The points. Point i of the run is P_i = (p_i, elev(levelId) + mep.z[i]): the heights are measured from the level, and the element's offset is not added. A run MUST carry mep with one height per profile point; one that does not has no body. Two points on one spot in plan, at two heights, make a vertical piece: the run climbs or drops there.
  2. A run MUST have two points or more, not all at one place; else it has no body.
  3. The outer size. Let ins be the run's insulation thickness (Part II, 6.9): its own mep.insulation.thickness, else its type's pipe.insulation or duct.insulation, 0 when it has none and 0 where its own mep.insulation gives none; where a pipe or its type gives a deprecated bti, its thickness (Part II, 6.9, rule 4). The run's outer width is W = mep.size + 2 ins. A duct whose type's duct.shape is rectangular has a rectangular section W wide and H = h + 2 ins high, where h is mep.height, or mep.size without it. Every other run, a pipe or a round duct, is round, W across; its mep.height is not used. W and H MUST be positive.
  4. Two points in a row less than 1 mm apart, and a turn of more than 179° at a point, are not runs the reference implementation writes (its validator refuses them). A compiler builds them by the rules below and SHOULD say so.

12.2 Bends

The rounded path replaces each corner of the run by a circular arc. For each inner point P_i, let d0 and d1 be the unit directions of the pieces P_(i−1) P_i and P_i P_(i+1), and θ = arccos(d0 · d1) the turn there.

  1. Where θ < 10⁻⁴ (the run goes straight on) or θ > π − 10⁻³ (it turns back), P_i stays a corner.
  2. Otherwise the corner becomes an arc of radius r = W, the outer width, tangent to both pieces. The arc starts at A = P_i − t d0 and ends at P_i + t d1, where t = r tan(θ/2).
  3. Where t is more than half the shorter of the two pieces, |P_i − P_(i−1)| and |P_(i+1) − P_i|, it is that half, and the radius shrinks to r = t / tan(θ/2). So two arcs never overlap on one piece. Each corner is measured on the run's own points, not on its neighbours' arcs.
  4. The arc is sampled at k + 1 points, k = max(2, ⌈θ / (π/12)⌉): with n = unit(d1 − d0 (d0 · d1)) and the centre c = A + r n, point j is c − r cos φ_j n + r sin φ_j d0, where φ_j = θ j / k, for j = 0 … k.
  5. The rounded path is P_0; then, for each inner point in order, its arc's k + 1 points, or the point itself; then P_(m−1). A point within 10⁻⁶ mm of the one before it is dropped (where two arcs each take half of one piece, the second starts where the first ends).

A rectangular duct's bend has the radius of its width W whether it turns in plan or climbs.

12.3 The body

The section is carried along the rounded path, one ring at each of its points q_0 … q_(N−1), and the rings are joined into one closed mesh.

  1. The tangent at q_0 is the direction of q_0 q_1, at q_(N−1) that of q_(N−2) q_(N−1); at an inner point it is unit(unit(q_j − q_(j−1)) + unit(q_(j+1) − q_j)), or the direction of q_j q_(j+1) where that sum vanishes. Each ring lies square to its tangent, with a side vector s square to the tangent and u = unit(t × s).
  2. A round run. Its ring is a circle of radius W/2 about q_j, in the plane of s and u. Its side vector is carried along the path without twisting: at q_0, s = unit(t × Z), or the x axis where the tangent is vertical; at each later point, the last s turned about unit(t_(j−1) × t_j) by the angle between the two tangents (not at all where they are parallel), then made square to t_j again.
  3. A rectangular duct. Its section stays upright: the rectangle W along s by H along u, centred on q_j, with its width level. At each point s = unit(t × Z), except that: where the tangent is vertical (|t × Z| ≤ 10⁻⁶), s is the previous point's (at a run that starts vertical, that of the first point that is not; on a run that is vertical throughout, the x axis); and where s would point away from the previous point's (s · s_prev < −10⁻⁹), it is reversed. So the section never turns end over end, and turns about the vertical only along a vertical piece that the run leaves in another plan direction than it came in (by 90° from −x to +y, say).
  4. The mesh. The rings, each of n points in order counter-clockwise about the tangent, are joined by a four-sided face between each two rings at each side; the first ring, reversed, and the last close its ends. Every face turns counter-clockwise seen from outside.
  5. The body is the section swept along the rounded path. Which points stand on each ring, and how many, is the reference implementation's faceting, not the rule: it draws a circle with n = 20 points for a duct and n = 14 for a pipe, point j at q_j + (W/2)(s cos(2πj/n) + u sin(2πj/n)), and a rectangle's corners at q_j ∓ (W/2) s ∓ (H/2) u in the order (−, −), (+, −), (+, +), (−, +). A compiler MAY facet the body otherwise, within section 12.5. The orientation of a rectangle (rule 3) is the rule.

12.4 Quantities

  1. The volume is the section's area, π W² / 4 for a round run or W H for a rectangular one, times the length of the rounded path as sampled (12.2.5): the sum of the distances between its consecutive points. It is the space the run takes, its insulation included; the fittings between runs are not in it. The volume a faceted body holds is less (by about 1.6 % for a circle of 20 points, 3.3 % for one of 14) and is not the quantity.
  2. The length is measured along the run's own points in 3D, Σ |P_(i+1) − P_i|, each bend taken as its corner, as pipe and duct schedules measure it.

12.5 Conformance

A compiler's body for a run conforms when its volume (12.4.1) is within 0.5 % of the rule's, its length (12.4.2) is the rule's, and each side of its bounding box lies within 2 % of the outer width W (and at least 1 mm) of the bounding box of the reference faceting of 12.3.5. Vertex counts need not match.

packages/openeik/test/fixtures/run-bodies.json holds eighteen runs (straight, bent in plan, climbing, sloped, with short pieces and with insulation, a ventilation main with several bends and a climb) with the volume, length, bounding box and vertex count the rule gives, and the reference implementation's own results; tools/run-bodies-reference.mjs writes it from a checkout of the reference implementation.

Reference: runPoints, runOuter (with runInsulation in src/core/elements/mepInsulation.ts), bendRadius (the outer width), runLength and runSolids in src/core/elements/mep.ts; roundedPath, tangents, frames, tubeRings, boxRings, ringsToMesh, ringCells and pathLength in src/core/geometry/sweep.ts. The volume is runSolids' volumeMm3.

12.6 Terminals and components in a pipe or a duct

A terminal (Part II, 6.9) is its declared geometry, where it or its type has one (Part II, 12.1), placed in the frame of a category placed at a point (section 11.1). Otherwise its body is a box of its type's terminal.width across the way it faces, terminal.depth along it and terminal.height high: centred on its point in plan, turned to the way it faces, from its base, elev(levelId) + offset, up. A terminal whose type gives no width, depth or height has no body. A component in a pipe or a duct stands where its run puts it, by the rules below; a duct part that names no duct (Part II, 6.9) is a terminal as this paragraph says.

The washbasin of a type 600 mm wide, 450 deep and 200 high, at [2000, 1000] facing [2000, 0], its offset 750 on level 0: its box runs from x 1700 to 2300, y 775 to 1225, and z 750 to 950.

A component in a pipe or a duct (Part II, 6.9) is its declared geometry, where it or its type has one (Part II, 12.1), placed in the frame of a category placed at a point: its origin at its point, x along its way, z up. Otherwise its body is a box w across its way, its type's terminal.depth along it and h high, centred on its point in plan, from z − h/2 to z + h/2, where z is its point's height. In a pipe, w and h are its type's terminal.width and terminal.height. In a duct they are its size across (Part II, 6.9): w = max(terminal.width, W + 20) and h = max(terminal.height, H + 20), where W and H are its duct's outer width and height (12.1, rule 3).

  1. A component's body is placed from its run and along, where Part II, 6.9 puts its point and its way, not from its stored profile and offset; a component in a duct is sized from its duct and its type, not from its stored width and height (Part II, 6.9, rule 8).
  2. Its run's body runs through it: a component cuts nothing, and the run's length and volume (12.4) are the run's whole. Its volume is the box's.
  3. A component whose hostId names no element of its run's category, a pipe for a type that mounts on a pipe and a duct for one that mounts on a duct, or whose run has no line or no outer size (12.1, rules 1 to 3), has no body.

The valve of Part II, 6.9, 60 mm across, 80 mm along and 70 mm high, 2000 mm along its pipe on a piece that runs +x at 2400 mm: its box runs from x 2960 to 3040, y 970 to 1030 and z 2365 to 2435.

The damper of Part II, 6.9, of a type 145 mm across, 200 mm along and 145 mm high, in a duct 400 mm wide and 200 mm high without insulation: w = max(145, 420) = 420 and h = max(145, 220) = 220. 1500 mm along its duct, on a piece that runs +x at 2700 mm, its box runs from x 2400 to 2600, y 4790 to 5210 and z 2590 to 2810.

These are closed-form rules, of class A (section 2). The reference implementation draws some terminals in a shape of their own (a WC, a basin, a round ceiling outlet), and keeps the box as the shape's proxy for sections, elevations and collision: the shape is reference behaviour (class C), and the box is the body this section derives (decided on 24 October 2026).

Reference: terminalSolids in src/core/elements/mep.ts, which boxes a terminal of its type's size from its base, turned to its facing, a component centred on its point, and a component in a duct as large across as its stored width and height; terminalAt in src/core/elements/terminalPlacement.ts, which places a terminal's point, facing and base; and placedOnRun and componentBody in src/core/elements/runComponents.ts (Stomra 633 and 676).

12.7 What is derived (informative)

These are derived from pipes, ducts and terminals, and never stored (section 1):

  • A heating terminal's water flow, in l/h, from its power P in W and its design difference ΔT in K: q = 3.6 P / (c ΔT), with c the heat of a litre of water per kelvin, 4.18 kJ/(kg·K) × 0.988 kg/l. 500 W at 10 K is 44 l/h.
  • The heat of a system: its terminals counted, their power and their flow summed.
  • The metres of insulation by code, from the runs' lengths (12.4, rule 2).
  • A tag's text: a template filled in, a room tag's rows (Part II, 13.3 and 13.4), a component's tag (its mark, else its type's code, and its run's size).
  • A schedule of components, by mark, type, system and size.

How each is worked out and shown is the application's. An application that keeps derived results keeps them in its own folder with a result manifest (Part VI, 8), never in a record.

Reference: terminalHeat, heatTagText and heatList in src/core/elements/mepHeat.ts; mepLists and terminalTag in src/core/elements/mep.ts; componentList in src/core/elements/runComponents.ts.