Emitting location zones as IFC entities¶
Status: decided and built. The property/quantity write-back ships (#2508
item 3b), and so does the IfcSpatialZone emission this document argued for:
packages/create/src/in-store/spatial-zone.ts for the schema half,
apps/viewer/src/lib/zones/emit-spatial-zones.ts for the frame half.
IfcZone emission remains refused on schema grounds.
Issue #2508 asks the question rather than assuming the answer: "Whether
user-drawn takt boxes should become IfcZone is a genuine modelling
question, not an obvious yes". This is the argument.
What a location zone is¶
A zone (apps/viewer/src/lib/zones/types.ts) is a user-drawn oriented box in
the viewer's world frame: a construction section, a takt area, a delivery
sequence. It has a name, a colour, a stable id, and no relationship to the
model's spatial structure. Elements are classified into it geometrically, by
where their geometry actually is, and an element can be in several zone sets at
once ("Section 2" and "Takt Area B").
Three properties matter for what follows:
- A zone is not a room. Nothing bounds it, nothing was designed to it, and it usually cuts through walls and slabs rather than following them.
- An element can belong to several zones at once, which is the entire point of #2508: the straddlers are the interesting population.
- Zones are planning state, not design state. They change when the programme changes, several times a week, and two people planning the same building will draw different ones.
IfcZone: no¶
IfcZone is a grouping of spaces. Its IsGroupedBy
(IfcRelAssignsToGroup) accepts IfcSpace, IfcSpatialZone and nested
IfcZone only; a wall is not an admissible member. This repo already reads it
that way: extractGroupMembersOnDemand
(packages/parser/src/on-demand-extractors.ts) documents an IfcZone's
members as the IfcSpace / IfcSpatialZone ones, and the GROUPS panel (#1672)
browses them on that assumption.
So emitting takt boxes as IfcZone with walls and slabs assigned to them
produces a file that our own reader would mis-describe and a validator would
reject. The attraction is only that the word matches; the semantics do not.
Refused.
IfcGroup: possible, and it says nothing¶
IfcRelAssignsToGroup onto a plain IfcGroup accepts any
IfcObjectDefinition, so the schema permits assigning walls to a group named
"Takt Area B". It is the unconstrained fallback, and that is also its problem:
a plain group carries no spatial meaning, so a receiving tool learns that these
elements are associated, not that they occupy a region. It also cannot
express a straddler honestly, because group membership is a yes/no while a
straddling wall is 40% one and 60% the other.
Worth having only if a specific downstream consumer asks for groups. It is a transport, not a model.
IfcSpatialZone: yes, and it is a separate PR¶
IfcSpatialZone (IFC4) is exactly this concept: a spatial region that is
"not necessarily bounded by physical elements", intended for thermal,
lighting, occupancy and construction zones, with a PredefinedType that
includes CONSTRUCTION. It carries an ObjectPlacement and a
Representation, so the box a user drew can be emitted as geometry rather than
described in prose. This repo already surfaces it (#1094, #1075).
Two schema details make it fit where IfcZone does not:
- Elements are attached with
IfcRelReferencedInSpatialStructure, which is many-to-many and additive. It does not disturbIfcRelContainedInSpatialStructure, which is exclusive and holds the building's real hierarchy. The parser already indexes the referenced relation (packages/parser/src/columnar-parser-indexes.ts), so a file we emit is a file we can read back. - A straddler can be referenced in both zones, which is the truthful statement of the topology.
What it does not solve¶
Referencing says this element reaches this zone. It cannot say this element
is 40% in this zone, which is the number #2508 exists to produce. So an
IfcSpatialZone emission is complementary to the quantity write-back, never a
replacement for it: the zone entity carries the region, the quantity set
carries the split.
Why it took its own PR¶
Each of these was a reason to defer it, and each is now a property of what shipped:
- It writes new spatial entities into someone else's model, which is a heavier act than adding property sets. So it is a separate, explicitly labelled button, and its inverse removes exactly what it wrote.
- It needs a placement chain and a swept-solid representation in IFC Z-up,
converted out of the viewer's Y-up frame. That conversion is the part only
the viewer can do, and it is the part that fails invisibly, so it lives in
one exported function with a test per shift
(
emit-spatial-zones.test.ts). A model federation alignment re-based is refused rather than written by another file's origin. - Zones change several times a week (property 3 above). Baking planning state into a design model stays a deliberate act: nothing emits on load, on assignment, or on export.
What ships instead, and why it is the right default¶
The property and quantity write-back (#2508 item 3b,
apps/viewer/src/lib/zones/writeback.ts):
- is additive and reversible: it touches no spatial structure and the panel removes exactly what it wrote;
- expresses the straddler honestly, as a per-zone volume breakdown that sums to the whole;
- lands in the one place every downstream tool already looks, which is what the reporter of #1763 was doing by hand in a spreadsheet.
What the IfcSpatialZone emission does¶
- One
IfcSpatialZoneper zone,PredefinedType = CONSTRUCTION,LongName= the zone set's name,Name= the zone's name. Nine attributes, not ten: the type derives fromIfcSpatialElementrather thanIfcSpatialStructureElement, so it has noCompositionType- which is the schema saying a zone is not part of the containment hierarchy. - An absolute placement (
PlacementRelTo = $) rather than one chained to the site: the zone's coordinates are world coordinates already, because the mesh pipeline resolved every placement chain to world before the user drew the box against it. Inverting that chain to recover a local offset would be arithmetic with nothing to gain. - Representation as an extruded rectangle profile carrying the zone's rotation
in the placement rather than in the profile, so a receiving tool reads a
rectangle as one. The viewer's
rotationYbecomesRotationZon the way in: the output is Z-up, and the axis swap flips the sign with it. A prism zone emits its convex footprint as anIfcArbitraryClosedProfileDef, with the points made relative to the placement. IfcRelReferencedInSpatialStructureper zone, listing every element the assignment says it touches (not just the elements whose home it is).- Emitted alongside the property sets, never instead of them.
Not done, and deliberately: aggregating the set's zones under one IfcZone.
That is admissible once every member is an IfcSpatialZone, and it is the one
legitimate use of IfcZone here, but nothing yet asks for the extra grouping
level.
How a re-run finds its own zones¶
Each emitted zone carries IfcRoot.Description = "IfcLite zone set <id>", the
zone set's stable id. A re-run sweeps by that rather than by the set's name, so
renaming a set between runs replaces its zones instead of leaving a second,
obsolete copy under the old name. The name still goes in LongName, which is
what a receiving tool shows.
The limit that remains: the sweep only touches entities THIS SESSION created. Zones from a re-imported earlier export are left alone and would be duplicated, which is what keeps a later run from gutting a model that already contains zones.