DAGeVu Modeller β User Guide¶
β Back to the User Guide Β· Home
DAGeVu is a browser BIM authoring surface that sits beside the read-only viewer. You don't edit a dead file β every action (drop a component, cut a window, route a duct, move a wall) is one signed operation folded into a verifiable hash-chain. The 3D you see is a pure fold of that operation log, so every edit is exact, reversible, and provable β never invented. The same signed-log engine drives the Kernel-ERP.
Open it: red1oon.github.io/bim-ootb/modeller/modeller.html (desktop β the B-rep kernel is heavy). The Home pill returns to the Matrix landing.
Every tool on this page is proven by a real-user, end-to-end test β the tool is driven through the production path with real mouse/keyboard and the result checked by numbers (the op-log, the scene graph, the pixels), not by eye. The screenshots are real frames captured from those test runs β nothing here is a mock-up.
The big idea β open a building and edit it¶
Don't draw a model up from a blank grid. π Open a real building's ARC (architectural) model β its digital twin β and edit that. The other disciplines and dimensions aren't re-drawn; they follow: structure, MEP, the 4D schedule, the 5D cost and the ERP auto-complete by crawling against your ARC (the Walk tools), and every edit and every follow is one signed operation the enterprise folds from.
The workspace¶

- Outliner (left) β the BOM tree of the opened building (
Building β Level β Room β ARC β element), plus the Walk rows and a live find filter. Collapse it with its chevron to free the canvas. - Canvas (centre) β the 3D building on the construction grid. Click an element to select it; Shift-drag marquee-selects many. Elements cast real shadows onto each other and the ground, so massing and floor-to-floor relationships read correctly at a glance β not a flat, shadowless render.
- Pill toolbar (right edge) β an icon rail. Tap β― (bottom-right) to fan the pills open; hover a pill for its name; ? Help lists every tool + shortcut.
Esccancels any active mode. - History scrubber (bottom) β the signed op-log is the timeline. Drag it to travel through every edit. The status line beneath it echoes what each op did (e.g.
scaled #183 X Γ1.50 verify=true).
How to read it: the op-log is the model, the 3D is a fold of it, and the slider is the single honest retreat across every tool below.
Getting started β your first five minutes¶
- Open the app β red1oon.github.io/bim-ootb/modeller/modeller.html (desktop; the B-rep kernel is heavy). Tap β― at the bottom-right to fan the pill toolbar open.
- Open a building β tap π Open and pick a resident building (e.g. Duplex). Its ARC model loads onto the grid and its BOM tree fills the Outliner on the left. You're now editing a real building, not a blank canvas.
- Frame it β press
F(or tap Fit) to zoom the whole building into view. - Select something β click an element (a wall, a door). It highlights; the status line names it. Shift-drag to marquee-select many.
- Do one edit β tap Move, drag an axis arrow, and release. Watch the status line print the signed op
(e.g.
moved #142 Ξ(0.50,0.00,0.00) verify=true). That edit is now one fact in the op-log. - Undo it β drag the history scrubber (bottom) one notch left. The edit reverses exactly, because the 3D is a pure fold of the log. Drag right to redo.
That's the whole loop: open β select β tool β commit β scrub. Every tool below is a variation on it. Press
? Help any time for the live list of pills and shortcuts, or Esc to cancel a mode.
Open your own building β straight from an .ifc¶
The resident buildings are there so you can start in one click, but π Open takes your own model too.
Besides a resident, it accepts an .ifc file and a local .db.
- Tap π Open.
- Choose FROM IFC and pick your
.ifcβ or pick a local.dbyou exported earlier. - The building lands on the grid the same way a resident does: it walks the structure and seeds the editable ARC substrate, so you can select and edit immediately.
Two things to know. First, an IFC opens filtered to ARC β the architectural model only, the single editable substrate; the other disciplines follow by walking, exactly as they do for a resident. Second, it's parsed by the same engine the Viewer uses β not a second, differently-behaved importer β so a building reads the same in both tools.
Because an IFC carries more than the pre-filtered resident .db does, an IFC-opened building often shows
more ARC elements than the same building opened as a resident. That's the source file being richer, not
a discrepancy.
Outliner β find, focus, and manage what's shown¶
The Outliner is more than a tree of rows β it's the fastest way to find an element and control what the canvas shows, especially on a large building.
- Click a row to select that element and fly the camera to it β this works even for a row with no direct canvas widget of its own; it flies to the element's real position, not a fallback toast.
- Selecting anything expands its ancestors automatically β you never have to manually drill down through Building β Level β Room to see where a selected element sits in the tree.
- The selected element gets a highlighted outline on the canvas, in addition to the existing status-line name β easier to spot on a busy, similarly-coloured floor.
- Type in the find filter and the tree narrows to matches β everything that doesn't match dims on the canvas too, so filtering the list and seeing where those elements actually are happen together.
- Toggle the eye icon on any row to hide or show that element (and its children) on the canvas without deleting it β useful for looking inside a wall or floor you'd otherwise have to work around.
- The tree stays responsive on large buildings β rows render only as you scroll near them, so opening a building with thousands of elements doesn't stall the panel.
Building Parts β Stairway / Lift Shaft / Plant Room¶
Below the BOM tree, a Building Parts row appears in the Outliner whenever the open building actually contains one of three part types β it's the Modeller-side twin of the Viewer's Find-panel Parts axis (same building, same categories, same underlying match rules), so a building looks the same way in both tools. Each category is independently gated: a building with stairs but no lift shows only Stairway; a small house typically shows Stairway and, if the ARC data has HVAC-plant elements, Plant Room β no empty categories, no clutter.
- Stairway β every
IfcStair/IfcRampelement. - Lift Shaft β elements named for a lift/elevator (works across a few languages β e.g. "liftdeur").
- Plant Room β HVAC-plant elements (ducts, fans, air-handling units, dampers, chillers, pumps).
Click Building Parts to select and zoom to every real element across all three categories at once, which also expands the tree to show them; click Stairway (or Lift Shaft / Plant Room) the same way to zoom to just that category; click one specific item inside a category to fly the camera to that exact element, same as any other Outliner row.

Realistic glass¶
Windows render as real glass now β see-through, not a solid opaque panel the same colour as the wall
around them. The Modeller reads each element's real material transparency (the same material_rgba alpha
value the Viewer already used) and renders it faithfully: an element with real glass data gets a
transparent material at its true opacity, everything else is unaffected.

What a wall is made of¶
A plain-looking wall box in the Modeller is real geometry, not a shortcut β the file itself only ever described the wall as one outer shape plus a list of layer thicknesses, never as separate layer shapes.
Take a real one: the party wall between two Duplex units (2O2Fr$t4X7Zf8NOew3FNbT) is built from 7
layers β plasterboard (16mm), metal stud (41mm), block (193mm), an air gap (50mm), block again (193mm),
metal stud (41mm), plasterboard (16mm). That's real, measured construction. But the source file never
draws those 7 slabs as 7 shapes β it draws one outer box the size of the whole stack, and records the
7 thicknesses as a side list ("this box is made of, in order..."). The Modeller renders exactly what's
there: one box β 14 triangles for this wall (a perfectly plain box tessellates to 12, two per face on
six faces, and 35 of the Duplex's walls are exactly that; this one's outline carries two more). That's
not a placeholder standing in for the real wall. It is the real wall, drawn at the detail the file
actually authored.
You can tell a real box from a broken one by whether it can be cut. A door or window cut through a wall only works if the wall has a real shape to cut β a stand-in box has nothing behind it to carve. In the Duplex, 18 walls carry a door or window hole and each ends up with 28β120 triangles once the cut is applied; that's proof the geometry underneath is real, not a fallback.
Why the Viewer looks richer on the same building. Open Duplex in the Viewer and you'll see far more going on β because the Viewer loads everything the Modeller deliberately doesn't: pipes, ducts, and other services (904 extra parts). The walls themselves are identical in both β same boxes, same 12 triangles each. The Modeller is scoped to architecture + structure by design; it isn't missing wall detail the Viewer somehow has.
Why a July fix looked dramatic on one building and invisible on another. An earlier pass removed fake placeholder boxes β geometry the pipeline invented when it couldn't resolve a real shape. On one test building (heavily irregular massing) that fix visibly cleaned up the model. On the Duplex it changed nothing you could see, because a plain wall's honestly-tessellated box and a fake placeholder box are both 12 triangles β removing the fake ones simply left the real ones exactly as they were.
What changes next. The file-side link from a wall to its own layer list is now extracted
(rel_material_layer_set), so the data needed to draw those 7 slabs individually β instead of one box β
already exists. Once that slicing ships (Β§LOD400-LAYERS-REAL), this same party wall will render as 7
stacked slabs whose thicknesses sum to the wall's true 550mm, not a single block.
Assemble & draw¶
Insert β assemble from the catalog¶
Commits GEOM_INSERT.
The fastest way to build is to assemble, not draw.
- Tap Insert (the cube pill) to open the BOM catalog on the left.
- Search or filter (All Β· Structure Β· Openings Β· Furniture Β· Sets), then click a component β or a whole Set (an assembly that drops its entire recipe at once).
- Aim on the grid (press R to rotate, set Elev for storey height) β a ghost previews the landing β then click to place.

The component lands as one signed GEOM_INSERT (a LOD-200 box that lazily refines to its real library
mesh). An assembly drops as one grouped op of N seated, oriented parts β a door takes its host wall's
facing automatically. Undo removes it exactly.

Sketch β Extrude β draw a wall¶
Commits GEOM_EXTRUDE_POLY (a real occt B-rep solid).
- Tap Sketch and click β₯3 points on the grid to lay a closed profile.
- Set the depth, then tap Extrude.

The solved polygon is swept to depth and committed as one signed operation.

Type an exact dimension (rect / square)¶
Cycle Constrain to Rect or Square before or during your 4 clicks β once the profile closes, W / H / β fields appear showing the sketch's current width, height, and corner angle. Type a new number into any of them and press Enter: the WHOLE profile re-solves from that one value, not just the one edge β a rectangle at 90Β°, or a true parallelogram at any other typed angle.


A click that lands near an earlier point in the same sketch welds the new point exactly onto it
(the status line reads welded to pΒ«NΒ») instead of adding a near-duplicate point a few centimetres off β
useful for closing a hand-drawn polygon precisely back onto its own start.

Circle β Extrude β draw a cylinder¶
Commits GEOM_EXTRUDE_POLY with a circle profile (a real occt cylinder, not a tessellated polygon).
- Cycle Constrain to Circle.
- Click a centre point, then click a second point to set the radius β or type an exact radius into the field that appears.
- Set the depth, then tap Extrude.

The circle sweeps into a genuine cylindrical solid (makeCircleEdge β a real curved B-rep face), the same
signed-op path as a polygon extrude.

Route β Sweep-Run β lay a run / duct¶
Commits GEOM_SWEEP.
- Tap Route and click β₯2 points to lay a spine.
- Set the profile size, then tap Sweep-Run.

The profile is swept along the spine (an occt pipe) as one signed op β the way MEP runs are authored.

Cut β open a wall¶
Commits GEOM_CUT (a child of the selected wall).
- Click a wall to select it.
- Tap Cut β a window void is derived from the wall and subtracted.

The opening shows immediately; undo closes it to the exact original frame. (Cutting a seeded ARC wall promotes its measured box to a B-rep just-in-time, so the subtraction is exact β never approximated.)

Fillet β round a solid's edge¶
Commits GEOM_FILLET.
- Select a B-rep solid and tap Fillet β its edges become pickable markers.
- Click an edge (or several), set the radius, then tap Apply.

The picked edge is rounded in place (the wall's triangle count grows exactly where the round lands); undo restores it.

Transform¶
Every transform below β move, scale, rotate, grid-stretch, delete β commits as one signed operation in the same tamper-evident log that drives undo/redo/history-scrub, and a move or stretch is checked against the building's own recovered relationships before it settles: a hosted door rides its host wall rather than divorcing from it, and a delta-based conformity gate flags only what the edit actually broke (RED) or softly disturbed (ORANGE) β never a pre-existing condition the building already shipped with. That combination β open a complete, real, production IFC and safely edit part of it β is what a Bonsai/FreeCAD-style direct editor doesn't do. Full dated disclosure: Event-Sourced Geometry & the Graph-Cascade Conformity Layer.
Select an element and tap Move to raise the transform gizmo β the shared handle for moving, scaling and rotating.

While you drag any handle, a floating readout follows your cursor showing the live delta (distance moved, scale factor, or degrees turned) β the same number the status line prints on release, just visible mid-drag instead of only after.
Move¶
Commits GEOM_MOVE {dx,dy,dz}.
- Select an element to raise the transform gizmo.
- Drag an axis arrow (X/Y/Z, grid-snapped) β or nudge with the arrow keys for a free move.
- Release to commit. A moved host drags its hosted fillings (a door rides its wall); undo is exact to the micron.

Snap-to-geometry¶
While dragging the ground-plane hub (not a single axis arrow), the handle also snaps to nearby
vertices, edge-midpoints, and face-centers of OTHER elements β not just the grid. A small marker
appears at the candidate point, and the status line reads snap-to-geometry; releasing commits the exact
snapped coordinate, read from the other element's own real bounding box (never invented).

Multi-select¶
Shift-drag an empty-space marquee to select every element whose on-screen footprint falls inside the box (shift-click adds/removes one at a time). The transform gizmo then acts on the WHOLE set β a group move/rotate/scale commits the same delta to every selected element about their shared centre.

Scale¶
Commits GEOM_SCALE {fx,fy,fz}.
- Select a single component to raise the gizmo.
- Drag a cube handle to stretch that axis β it's edge-anchored, so the opposite face stays put. The preview follows the element's own local axes (not the world grid), so a rotated element previews and commits identically β no snap-back or mismatch on release.
- Release to commit. The rendered extent grows by exactly the committed factor (the status line reads the signed factor).

Rotate¶
Commits GEOM_ROTATE {drot}.
- Select an element to raise the gizmo.
- Drag the yellow yaw ring to spin the selection about its centre β 15Β° snap, hold
Shiftfor a free angle. - Release to commit. The rendered footprint turns by exactly the committed angle.

Keyboard-arm a handle (T / S)¶
With an element selected, press T to arm the rotate ring or S to arm the scale cubes directly
β no need to first tap Move and then find the right handle. Arming is a pure affordance: the armed handle
lights at full opacity while the rest of the gizmo dims, and dragging it commits through the exact same
GEOM_ROTATE/GEOM_SCALE path as clicking the handle normally would. Press the same key again (or Esc)
to disarm. R is reserved for Insert, so it doesn't arm anything here.
Grid-Stretch¶
Commits GEOM_GRID_MOVE.
- Tap Move Grid.
- Drag a gridline.
- Release to commit. Walls attached to that line recompose β a span stretches, an attached wall translates β as one signed operation. A hosted door or window rides its wall rather than stretching or divorcing from it, same as Move.
What that recompose actually does, in plain terms:
- Walls fill the gap, never leave one. Every wall on the dragged gridline is classified against it first β a wall that only touches the gridline translates with it, a wall that spans across to another gridline stretches, with its far end staying anchored on that other gridline. Either way the wall keeps meeting the gridline; it can't detach or overshoot.
- Only the grabbed bay moves. The edit is scoped to the walls actually connected to the dragged gridline β a wall elsewhere in the building that happens to sit at the same coordinate, but belongs to an unrelated room, is left alone.
- Hosted doors and windows ride, never distort. A filling keeps its own size; it moves by its own share of the stretch (see below), it never scales and never divorces from its host.
- Furniture and fixtures stay put. They aren't part of the grid at all β a couch sitting in a room doesn't drag when the room's wall stretches.
The ride is not a guess about what looks hosted. It follows the authored hostβopeningβfilling chain recovered verbatim from the building's own IFC, so a door rides the wall its designer actually put it in β and where a building's author never declared that relationship, the modeller says so rather than inventing one. In practice that means the ride is exact on SampleHouse (all 7 hosted openings) and Duplex (36 of its 38), and partial on SampleCastle, whose window-frame walls are consumed by their own openings and so aren't separate things to ride.

On a real building, not a diagram¶
The two frames above are a clean teaching diagram (Move Grid on a synthetic scratch wall). The mechanic works identically against a real, fully-loaded building β no separate mode, nothing cleared first. Here it is on Duplex's own ground floor, near the stair: a column grid aligned to a real wall's own measured edge, then that gridline dragged for real.
Witnessed end-to-end (modeller/tests/witness_e2e_gridmove_real.js, 8/8 Β§-tagged assertions green, real
pg.mouse downβmoveβup, no synthetic shortcuts):
- A real mouse drag on gridline "2" landed at delta = 0.5000 m. The governed wall's own rendered Y-extent grew from 2.920 m β 3.420 m β exactly the committed op's delta, read back off the live mesh, not assumed from the drag alone.
- Its hosted door β a real
rel_fills_hostedge recovered from the IFC, not a proximity guess β rode 0.273 m, not the wall's full 0.500 m: Β§STRETCH-RIDE keeps a filling's proportional position along its host, so it moves by its own anchored share of the stretch, never scales, never divorces. - The gesture landed as one signed
GEOM_GRID_MOVEplus 9 induced riderGEOM_MOVEs (other walls the same gridline governs, several carrying their own hosted doors) β one Ctrl+Z undoes the whole group, verified: cursor and wall extent both restored byte-exact. verifyChainheld throughout. The drag-session cache (Β§SCALE_CHECK_FIX) is built once per gesture and reused every pointermove frame, not rebuilt per frame, cited from its own log line rather than re-profiled.
The conformity gate runs on every drag, real building included β it doesn't get a pass because the input is a
big, dense floor plan. This particular 0.5 m test drag pushed the wall into real clashes against neighbouring
structure; the app's own status line reports the count instantly (recomposed=16 Β§STRETCH-RIDE riders=9
verify=true 23 RED) rather than silently accepting a bad edit β visible in the "after" frame below.

Roofs recompose the same way. SampleHouse's barrel-vault roof spans the two gridlines bracketing the
building; dragging the far one 0.5000 m grows the roof's own rendered X-extent from 14.8410 m β 15.3410
m β exactly the drag, with the near edge held fixed at the other gridline β while the wall it sits on rides
along in the same recomposed group (recomposed=8, Β§STRETCH-RIDE riders=7). Undo restores the roof's extent
byte-exact. Witnessed end-to-end: modeller/tests/witness_e2e_gridmove_roof.js, 8/8 Β§-tagged assertions green.

Delete¶
Soft-deletes from the signed log (reversible).
- Select the feature β its children (hosted fillings) come along.
- Tap Delete (or press
Del). - The feature hides from the model β the signed payload is never rewritten, so the chain stays valid. Redo (
Ctrl+Y) brings it back exactly.

Room Move¶
Commits GEOM_ROOM_MOVE {spaceGuid,dx,dy,members[]}.
Moving furniture around a room one piece at a time divorces the room from its contents the moment anything gets left behind. Room Move grabs the whole room instead β everything it actually contains, plus anything sitting freestanding inside its footprint, plus any hosted door or window riding a wall that moves with it β and translates it all by one shared delta, as a single signed operation with one-step undo.
- Open the Outliner's BOM Tree and expand down to the room you want (Building β Storey β Room).
An
IfcSpacehas no rendered mesh of its own, so the room's Outliner row β not a click in the 3D view β is the grab target. - Click the βΆ icon next to the room's name. It enumerates every real member for that room right then (logged to the status line and the console) and arms the drag; a room with nothing honest to move (zero members across every leg) refuses the grab instead of dragging an empty box.
- Drag anywhere on the canvas β it's the ground-plane delta that moves the room, not where inside the room you happened to click.
- Release to commit. Everything the grab found moves together, as one signed operation with one-step undo
(
Esccancels an armed grab before you release).
Membership is never a proximity guess: an element joins the move only because a real recovered relationship says so β
- every element with a real
rel_contained_in_spaceedge into the room, - a bounding wall, if the building's extracted schema carries a real space-boundary edge for it (most don't yet β a room's walls stay put rather than being swept in by nearness),
- a freestanding, non-structural item with no containment edge whose real footprint centre falls inside the
room (tagged
derived-footprintin the op, so it's always visible which leg brought each element in), and - any door/window hosted by a wall that's already a member, carried over the same one-hop hostβfilling ride Grid-Stretch uses.
Because the whole set shares one rigid (dx,dy) β never a per-member recompute β the room's contents stay in
the same relative arrangement, and the BOM needs zero work to follow: no containment edge changes, so the
same tree replay is valid before and after. The move is in-plane only (a dz is refused outright β moving a
room to a different storey is a bigger, separate decision this tool doesn't make) and it snaps back to
nothing on an empty room (nothing honest to move). Like every other geometry edit, a room move still runs
through the modeller's conformity gate β if the delta pushes a member into another element's real footprint,
the status line names the clash the same way Move or Grid-Stretch would.
Verified on SampleHouse's "1 - Living room" (witness_room_move.js, 10/10): 16 real members β 12 via
rel_contained_in_space, 4 via derived-footprint β move as one GEOM_ROOM_MOVE, every member's rendered
centre landing on the committed delta to within float32 precision (max error 2.4Γ10β»β· m). Round-tripping
(dx,dy) then (βdx,βdy) (witness_room_move_roundtrip.js, 7/7) returns all 16 members to their exact
starting position β 0.000 mm residual. The Outliner grab-to-drag UI above is verified the same way, with real
pointer input instead of a direct engine call: witness_e2e_roommove_ui.js (9/9, real Duplex) clicks a real
βΆ glyph, drags a real mouse gesture across the canvas, and asserts the committed op's member list is
byte-identical to what the grab enumerated, the moved elements' rendered centres shifted by exactly the
committed delta, and one scrub undoes it exactly.

Item Drag¶
Commits GEOM_MOVE {parent,dx,dy,dz}.
A free single-item drag for one non-structural piece β a fixture, fitting, or loose furnishing β placed by eye onto a real surface rather than nudged axis-by-axis. It's a dedicated tool (its own Drag Item toolbar button), not the Move gizmo from the Transform section above β Move is an ungated axis drag for anything already selected; Item Drag is specifically gated by real product data and a real drop surface, so it stays a separate button rather than silently changing what Move already does.
- Tap Drag Item.
- Click a fixture or fitting in the 3D view to grab it. Two refusals happen before anything can move, and neither ever falls back to a guess:
- No real dimensions, no lift. The drag session looks up the item's real product dimensions before it
starts; with nothing to match, it throws
WalkerGapError, refuses the grab, and the item never leaves its spot β never a placeholder box standing in for a real size. - Not a valid subject. A wall, or an element already hosting a door or window, is refused as a drag subject outright β those move via Grid-Stretch or Room Move instead.
- Drag across the canvas. The preview tints green where the drop would be valid, red where it wouldn't β a wall-mounted item must find an actual wall face under the cursor (a floor item an actual slab, a ceiling item an actual soffit), and a drop that collides with another element's real footprint is refused the same way, with every refusal reporting what it hit.
- Release. A valid drop commits one ordinary
GEOM_MOVEβ item-drag introduces no new op type, so it plays back through the exact same fold and undo path as the Move gizmo. An invalid release snaps the item straight back to its pre-drag position, uncommitted (Esccancels an armed grab before you release).
On today's building data, every already-placed fixture in the log honestly refuses at step 2 β the product identity a walker used to place it lives only in a transient value at commit time, never written to the signed log, so there's nothing left afterward for a generic pick to match against. That's not a UI gap, it's the same refuse-don't-fabricate rule the rest of the modeller runs on: a future catalog-drop flow that still holds the product id at drag time reaches the real match/commit path shown below; a plain pick on existing content reaches the refusal path, cleanly, every time.
witness_item_drag_gate.js (9/9, real SampleHouse data) proves both refusals live at the engine level: a
session with no product hint throws WalkerGapError and adds zero rows to the op-log; a matched item (a real
sink, 0.5Γ0.45Γ0.2 m from ad_product_dim) that collides with a neighbour is refused with the colliding fids
named and the log length unchanged; the same item dropped against a real wall face commits exactly one
GEOM_MOVE with a snappedPos read off that wall's own geometry. witness_e2e_itemdrag_ui.js (8/8, real
Duplex) proves the UI above end-to-end with real pointer input: a real Drag Item arm + real canvas pick
on existing content refuses cleanly (zero ops); a held product hint reaches a real wall face found by
scanning with the tool's own validity check and commits one GEOM_MOVE, the dragged mesh's rendered centre
landing on the exact committed delta; the same grab dropped onto another element's real box refuses the
commit and leaves the mesh exactly where it started.

Generate β walkers fill the ARC¶
When you open a bare ARC building, the other disciplines fill themselves in. You pick a discipline that is absent β Structure, Electrical, Fire-Protection, Plumbing, HVAC β and the modeller walks it: it places that trade's elements at the measured cadence of a real coordinated building, chains the runs it can, gates the clashes, and honestly refuses when the building has nothing to hang the trade on. Nothing is invented β every placement uses a spacing/clearance rule mined from a real IFC model.

A normal (non-X-ray) view of this same walk is honestly near-empty: a real electrical outlet or light
lives inside a room, not poking through an exterior wall, so from outside the sealed shell it's naturally
occluded β same as it would be in a real building. Tap X-ray (or press X) to see the walk actually
landed, as captured here: structure fades to near-transparent glass and the fixtures glow through it in
their discipline colour, room by room. This is the corrected placement too (2026-07-12): a previous
version of this pipeline had a containment bug where roughly a quarter of placements landed outside the
building's own walls β fixed (mesh-recovered true-midpoint host binding) and independently verified 5
separate ways (containment count, real-oracle walk-back match, measured-pattern conformance, wall-clearance
margin, mirror-symmetry residual on the Duplex's own A/B twin layout).
How real is the fixture mesh, up close? It depends on whether the rule-set was mined from this
building. Duplex is the building duplex_rules.db's residential standard was mined from, so its own
walked fixtures resolve to their own real extracted device mesh β a genuine ceiling fan, motor housing and
all, not a box:

SampleCastle walks the same residential rule-set (it's a different building β see the table below), so it has no catalog match for its own fixture classes and falls back honestly to a measured box, sized from that class's real dimensions but not a finished model:

Guessing at a fixture's real shape when no mesh is mined is exactly what this project's non-invent rule forbids β a plain box, correctly sized and positioned, beats an invented model every time.
One engine, two standards. A single walker drives every discipline; the discipline is just a data filter. It carries two measured rule-sets and auto-selects by building class:
| Building class | Standard used | Mined from | Why |
|---|---|---|---|
| House / residential (SampleHouse, Duplex, SampleCastle) | residential (duplex_rules.db) |
the Duplex's own real MEP β 908 elements | tight residential cadence (~0.5 m trade separation) |
| Large / complex (airport, hospital) | large-complex (terminal_rules.db) |
a real airport terminal | sparse, plenum-scale cadence |
On open you'll see Β§DW-PROV print the standard and its provenance, so you always know which rule-set is
driving the walk. The tables below are the evidence that the walk produces sensible numbers β every
figure traces to a witnessed Β§-log (no estimates).

What a walk actually places¶
Status-line results per discipline on the three residential buildings
(build/logs/witness_disc_walk_generalize.log). Placed = elements seated; chains = runs linked
end-to-end; the note is what the walk reported when a trade had nothing to route:
| Building (storeys) | Discipline | Placed | Chains | Honesty note |
|---|---|---|---|---|
| SampleHouse (3) | Fire-Protection | 55 | 0 | β |
| Electrical | 159 | 0 | β | |
| Structure | 30 | 1 | members linked, no measured gap | |
| Plumbing | 6 | 0 | ~no pipes in a small house β honest | |
| Duplex (5) | Fire-Protection | 303 | 0 | β |
| Electrical | 771 | 0 | β | |
| Structure | 162 | 1 | members linked | |
| Plumbing | 10 | 0 | sparse β honest | |
| SampleCastle (7) | Fire-Protection | 2 665 | 0 | β |
| Electrical | 8 123 | 0 | β | |
| Structure | 1 836 | 1 | members linked | |
| Plumbing | 14 | 0 | sparse β honest |
Counts scale with footprint because the cadence is constant β the same measured spacing tiles a bigger floor. Walking an unknown discipline returns REFUSE β no measured rule rather than guessing.
Why the right standard matters β clash collapse¶
The walk gates clashes between the fixtures it placed. Using the right standard for the building class
collapses the phantom clashes a mismatched (too-wide) standard would raise β same layout, only the
clearance standard changes (witness_disc_walk_duplex_generalize.log, gated irreducible residual):
| Building | Residual @ large-complex standard | Residual @ residential standard | Collapse |
|---|---|---|---|
| SampleHouse | 9 | 4 | 56 % |
| Duplex | 32 | 2 | 94 % |
| SampleCastle | 501 | 3 | 99.4 % |
Every residual is flagged, none silent β the walk never hides a clash to make the number look good.
Does the residential standard reproduce a real house's MEP?¶
The residential standard was mined from the Duplex's own MEP, so the acid test is whether it re-grows
that MEP (witness_duplex_rules.log, Β§DXM-RT):
| Discipline | Verdict | What it means |
|---|---|---|
| Plumbing | β GREEN (4/4 classes) | the bulk of a house's MEP reproduces β segments 0.93, fittings 0.85 |
| Electrical | π‘ WEAK | fixtures (n = 89) GREEN; the sparse 8-segment conduit is honestly WEAK |
| HVAC | π΄ RED (n = 2) | a house has ~no ductwork β honest RED, not a failure |
RED/WEAK here are honesty, not breakage: a single-family house genuinely has almost no ducting, so the walk declines to fabricate it.
Does it generalize to a building it never saw?¶
We routed the residential rules (mined from the Duplex) onto held-out buildings and scored each against
that building's own pipes (witness_generalize_curve_*.log, Β§GC). Precision is the don't-fabricate
score: of the joins the walker drew, how many land on a real pipe touch.
| Building | Type | In/out of domain | Segments | Precision @0.15 m | Fabricated |
|---|---|---|---|---|---|
| Duplex (self-consistency ceiling) | house | β | 358 | 0.969 | 0 |
| LTU_AHouse | house | in-domain | 32 138 | 0.839 | 0 |
| WBDG_Office | office | out-of-domain | 2 241 | 0.749 | 0 |
| Clinic | healthcare | out-of-domain | 4 906 | 0.705 | 0 |
| HHS_Office | office | out-of-domain | 1 380 | 0.620 | 0 |
The model degrades gracefully and never collapses. On every building 0 joins were fabricated and 0 exceeded the gap bound. An ARC-only building with no pipes routes 0, never a guess.
Walk them all at once¶
You don't have to pick the trades one at a time. Under Not extracted β walk from β¦ in the Outliner, the first row is βΆβΆ Walk ALL Disciplines; its sub-label tells you how many trades the building is missing (e.g. 4 not extracted Β· x-ray reveal).
- Open a bare ARC building.
- Click βΆβΆ Walk ALL Disciplines.
It walks every absent discipline in turn, through exactly the same production path a single click uses β same rules, same gating, same honest refusal when a trade has nothing to hang on. The difference is the presentation: X-ray brackets the whole run so you can watch it land, and each discipline's placements flash amber and settle into their own trade colour just before they're committed, so you can see which trade just filled in rather than watching one undifferentiated wave.
X-ray is restored afterwards even if a discipline refuses part-way. On a very large building the per-element flash is dropped in favour of one batched hold-and-settle β the geometry committed is identical either way, only the reveal animation is coarser.
Seed-Trunk β route a service trunk¶
After walking a discipline, route its service trunk from a real entry.
- In the Outliner, open the Route trunk row for the walked discipline.
- A popup offers the real service entry elements (doors / stairs) β confirm the default or choose one.
- Tap Route βΆ.

A corridor-aware trunk is routed from that entry through the walked fixtures β around walls, through real doors, up risers between storeys.

Deeper proof. The full mining, round-trip, boundary and generalization analysis lives in the resume cards
prompts/RESUME_DX_MEP_RESIDENTIAL_STANDARD.md,RESUME_TERMINAL_RULE_MINING.mdandRESUME_DISC_WALKER_ENVELOPE_BOUND.md, each backed by the witnessedbuild/logs/set summarised above.
History β the slider is the timeline¶
- Drag the history scrubber back to undo, forward to redo β it scrubs the signed op-log and re-folds the geometry deterministically. It is a strict superset of the keyboard shortcuts.
Ctrl+Zundo Β·Ctrl+Y(orCtrl+Shift+Z) redo Β·Deldelete selection Β·Ffit Β·Esccancel mode Β·Rrotate a pending insert.- Every retreat is exact because the geometry is a pure fold of the log β there is no separate "undo buffer" to drift out of sync.
Share an issue β BCF export¶
Tap the BCF pill to export your current view as a BCF 2.1 file (.bcfzip) β the open
buildingSMART format for exchanging issues between BIM tools. The export carries your live camera
viewpoint and the real IfcGuids of whatever's selected, so the file opens correctly in Navisworks,
Solibri, BIMcollab, Revit (via plugin), or Trimble Connect β never a synthetic ID that only means
something inside this app. See Clash Detection Β§8.5 for exporting a
whole batch of detected clashes as one topic set.
Toolbar β icon index¶
The toolbar is a β― pill rail at the right edge: tap β― to fan the pills, hover for a name, and open
? Help for the live registry (icon Β· name Β· shortcut). Esc always cancels the current mode.
| Icon | Does |
|---|---|
| β― Toolbar | Fan the pills open / closed |
| ? Help | Toolbar & shortcuts β the live pill registry |
| Home | Back to the Matrix landing |
| Fit | Zoom to fit β the selection, or the whole scene (F) |
| Iso | Cycle the view: Iso β Top |
| Grid | Show / add a construction grid |
| Move Grid | Drag a gridline β attached walls recompose (GEOM_GRID_MOVE) |
| Move | Transform gizmo β move / scale / rotate the selection (M) |
| Sketch | Start a 2D sketch |
| Extrude | Push a sketch profile into a solid (GEOM_EXTRUDE_POLY) |
| Axis | Set the constraint intent the solver enforces on the sketch |
| Cut | Cut an opening in the selected wall (GEOM_CUT) |
| Route | Lay a spine to sweep a profile along (e.g. an MEP run) |
| Sweep Run | Sweep the profile along the route (GEOM_SWEEP) |
| Fillet | Round a selected solid's picked edges (GEOM_FILLET) |
| Apply | Commit the pending fillet / chamfer |
| Insert | Insert a library component β assemble, don't draw (GEOM_INSERT) |
| LOD 200 | Refine the last-placed component's level of detail (same signed row) β appears once you enter Insert, not on the resting rail |
| IFC | Export the authored model as IFC4 |
| Undo / Redo | Undo (Ctrl+Z) Β· Redo (Ctrl+Y) |
| Delete | Delete the selection (Del) |
| Clear | Empty the scene |
| Sound | Toggle authoring sound feedback |
| Connect | Connect Scene β share selection / timeline with the Viewer & ERP (opt-in) |
Troubleshooting¶
Each row below is a real behaviour the tool suite exercised (and, where it was a defect, fixed) β not a hypothetical. If you hit something else, ? Help lists every tool and shortcut, and the history scrubber is always a safe retreat.
| Symptom | Why | What to do |
|---|---|---|
| A pill looks stuck in the top-left corner and won't click | A mode-revealed pill (Extrude, Sweep-Run, Apply) is shown only after you start its mode; the rail lays them out on reveal. | Start the mode first (e.g. finish the Sketch before reaching for Extrude). The rail re-positions the pill as it appears β if one still looks stranded, toggle the β― rail closed and open. |
| Cut / Scale seems to do nothing on a wall from an opened building | A seeded ARC wall is a baked box, not a B-rep. Cut promotes it to a B-rep just-in-time so the subtraction is exact; a rotated/non-box insert is refused up-front (logged) rather than approximated. | Nothing to do for a normal axis-aligned wall β the op commits and renders. If a specific insert is refused, it isn't box-like enough to cut exactly; sketch the void instead. |
| A Walk seems to hang on a big building | A discipline walk places hundreds of fixtures; they commit as one batched signed group, so a large walk takes a couple of seconds, not a frozen minute. | Wait for the batch β the status line reports the result when it lands (e.g. ELEC β 267 placed across 5 storeys Β· 0 routed). Scrub the slider back to clear them. |
| I want to "make an opening" but there's no Opening tool | Opening is not a separate authoring tool β the real "cut a window/door" is the Cut tool (GEOM_CUT). |
Select the wall, tap Cut. GEOM_OPENING is only a legacy sample primitive, never a user action. |
| Fillet won't pick an edge | Fillet needs a B-rep solid; a seeded ARC insert has no worker edges to round. | Author a solid first (Sketch β Extrude), select it, then Fillet β its edges become pickable markers. |
| An edit didn't land the way I expected | Every edit is a signed op; nothing is silently applied. | Drag the history scrubber back one notch to undo it exactly, then retry. There is no separate undo buffer to drift out of sync. |
Collaborate on the design β the Teams overlay¶
The Modeller shares one signed op-log with the Viewer and the Kernel-ERP, so the same Teams overlay rides on it: git-style design branches ("you build that wing, I build this"), a spatial merge gate that flags where two branches clash, identity-coloured who-dots on elements, and the tabbed Outliner (Tree / Chat / Dashboard). Off by default, pixel-identical until you toggle it on. β Teams Overlay guide (with screenshots).
Part of BIM OOTB. Copyright (c) 2025β2026 Redhuan D. Oon. MIT Licensed.