nD BIM Analysis β 4D Schedule, 5D Cost, 6D Carbon, 7D FM, 8D Safety¶
β Back to the User Guide Β· Home
Foundation: Enterprise Β· LTU A-House Β· DATA_MODEL
What We Have¶
The Federated Model DB (_extracted.db) drives all nD analytics
from a single SQLite file. All IFC disciplines live in this one DB.
No IFC file open, no geometry iterator, no RAM spike.
Scale proof: 1,063,563 elements across 787 buildings β all 5 dimensions in 8.7 seconds.
Proven Results (LTU A-House β 125,997 elements, 8 disciplines)¶
5D Costing β Executive Summary with Material + Labour + Equipment per discipline. Grand Total: RM 67M (CIDB 2024 standard rates). 133 QTO line items. Generated in ~2 seconds.
4D Schedule β 224 construction tasks, precedence-linked by phase. Substructure β Superstructure β MEP Rough-in β Architecture β MEP Final β Finishes.
4D Dashboard β S-Curve, resource workload, milestones β all from one query on the DB.
The nD Engine¶
A single abstract engine reads user-editable JSON templates and applies formulas
to any IFC class found in the extracted DB. Zero hardcoded IFC classes β all configuration
lives in templates/. Users edit JSON, the engine applies formulas.
python3 scripts/nD_engine.py <database_path> --all
python3 scripts/nD_engine.py <database_path> --dims 4D,5D
python3 scripts/nD_engine.py <database_path> --dims 6D # auto-runs 5D if needed
| Dimension | Template | Input | Output Table |
|---|---|---|---|
| 4D Schedule | templates/4D_phases.json |
elements_meta |
construction_schedule |
| 5D Costing | templates/5D_rates.json |
elements_meta + elements_rtree |
simple_qto |
| 6D Carbon | templates/6D_carbon.json |
elements_meta + simple_qto |
carbon_audit |
| 7D FM/Lifecycle | templates/7D_lifecycle.json |
elements_meta |
asset_register |
| 8D Safety | templates/8D_safety.json |
elements_meta + construction_schedule |
hazard_register |
Master formula file: templates/nD_formulas.json β measurement rules, cost formulas,
dimension registry.
Self-Healing Pipeline¶
If a dimension needs a prerequisite table that doesn't exist, the engine auto-generates
it. Request 6D on a fresh DB β engine detects simple_qto missing β runs 5D first β
then 6D. Request 8D β auto-runs 4D for the schedule. No manual ordering needed.
How IFC Data Becomes nD Data¶
Every dimension derives from the same elements_meta table that extractIFCtoDB.py
creates during IFC extraction. The key column is ifc_class β the JSON templates map
each class to phase/cost/carbon/lifecycle/hazard rules.
IFC file ββ extractIFCtoDB.py ββ elements_meta (ifc_class, discipline, storey, ...)
β
ββββββββββββ¬ββββββββ¬ββββββββΌββββββββ¬ββββββββ
β β β β β β
4D 5D 6D 7D 8D Viewer
phase cost/qty carbon asset hazard 3D mesh
from from from from from
ifc_class ifc_class ifc_class ifc_class ifc_class
β β β β β
templates/ JSON β user-editable
If an extracted DB is empty (0 bytes, no tables), re-run extraction:
python3 scripts/extractIFCtoDB_open.py path/to/model.ifc path/to/output_extracted.db
Fleet Test Results β 37 Buildings + 1M City¶
37/37 individual buildings PASS. Sandbox city-wide 1M PASS. Zero failures.
How to Run Tests¶
# Unit test β single building (28 witnesses)
python3 scripts/test_nD_engine.py
python3 scripts/test_nD_engine.py --db DAGCompiler/lib/input/Hospital_extracted.db
# Fleet test β all 37 buildings
python3 scripts/test_nD_fleet.py
# Fleet + 1M sandbox city (takes ~14 seconds total)
python3 scripts/test_nD_fleet.py --include-sandbox
Output locations:
- Tables written into the extracted DB itself (temp copy during tests)
- Fleet report: scripts/nD_fleet_report.txt
- Debug log: scripts/nD_engine_log.txt
- For production runs, tables are written directly into the target _extracted.db
Test Witnesses¶
| Witness | What it proves |
|---|---|
| W-TEMPLATE | All 6 JSON templates load, all have _default fallback |
| W-5D_COSTED | Every IFC class gets a cost (zero uncosted) |
| W-4D_NO_UNKNOWN | Zero tasks with phase='Unknown' (was 64% Unknown before) |
| W-6D_CARBON | Carbon total > 0 and cross-checks with SUM(qty Γ factor) |
| W-7D_ASSETS | Asset count == element count (1:1 mapping) |
| W-8D_SAFETY | Every hazard row has a risk_level |
| W-AUTO_PREREQ | Requesting 6D auto-generates 5D if missing |
Sandbox City β 1,063,563 Elements, 8.7 Seconds¶
The sandbox contains 787 buildings (13 CBD archetypes + suburb tile instances) across 48 IFC classes and 6 disciplines.
5D β City-Wide Cost: RM 1.59 Billion¶
| Discipline | QTO Lines | Elements | Cost (RM) |
|---|---|---|---|
| ARC | 576 | 686,396 | 1,407,873,431 |
| MEP | 192 | 243,304 | 98,035,356 |
| STR | 130 | 123,427 | 76,154,312 |
| ELEC | 33 | 6,129 | 2,922,681 |
| FP | 17 | 3,709 | 1,854,500 |
| ACMV | 9 | 598 | 299,000 |
Top 10 most expensive IFC classes (city-wide):
| IFC Class | Elements | Cost (RM) |
|---|---|---|
| IfcSlab | 39,392 | 821,441,213 |
| IfcDoor | 46,064 | 131,282,400 |
| IfcWallStandardCase | 45,216 | 130,362,836 |
| IfcWindow | 59,043 | 93,287,940 |
| IfcFlowTerminal | 20,707 | 72,474,500 |
| IfcCovering | 131,595 | 48,305,718 |
| IfcOpeningElement | 78,286 | 39,143,000 |
| IfcColumn | 7,731 | 37,227,725 |
| IfcMember | 47,498 | 32,234,992 |
| IfcBuildingElementProxy | 37,829 | 32,154,650 |
4D β City-Wide Schedule: 957 Tasks¶
| Phase | Tasks | Total Duration (days) |
|---|---|---|
| Substructure | 15 | 366 |
| Superstructure | 211 | 16,004 |
| MEP Rough-in | 142 | 13,203 |
| Architecture | 447 | 63,308 |
| MEP Final | 84 | 4,169 |
| Finishes | 58 | 13,762 |
Zero Unknown phase. Every IFC class mapped via template or _default fallback.
6D β City-Wide Carbon: 434,460 tCO2e¶
| Discipline | Embodied Carbon (tCO2e) |
|---|---|
| ARC | 424,239 |
| STR | 6,603 |
| MEP | 3,249 |
| FP | 185 |
| ELEC | 154 |
| ACMV | 30 |
7D β City-Wide Asset Register: 1,063,563 Assets¶
| Discipline | Assets | Avg Lifespan | Service Interval |
|---|---|---|---|
| ARC | 686,396 | 29 yr | 12 mo |
| MEP | 243,304 | 23 yr | 7 mo |
| STR | 123,427 | 39 yr | 41 mo |
| ELEC | 6,129 | 15 yr | 10 mo |
| FP | 3,709 | 30 yr | 6 mo |
| ACMV | 598 | 30 yr | 6 mo |
8D β City-Wide Safety: 957 Hazard Entries¶
| Risk Level | Entries | Elements |
|---|---|---|
| LOW | 713 | 985,450 |
| MEDIUM | 86 | 39,392 |
| HIGH | 147 | 38,492 |
| VERY HIGH | 11 | 229 |
Browser 4D/5D HTML β Zero-Install Analytics¶
The nD engine outputs feed a fully browser-native HTML analytics page (boq_charts.html).
No server. No install. Open the link β charts render, Excel exports.
Performance¶
| Step | Mechanism | Speed |
|---|---|---|
| DB load (first visit) | Fetch from OCI β written to IndexedDB | Network-bound |
| DB load (return visit) | Read from IndexedDB (bim_ootb_cache) |
~0ms β local |
| DB load (after IFC import) | IndexedDB import:// key β no OCI needed |
~0ms β local |
| 9 charts rendered | sql.js WASM queries + Chart.js | < 1 second |
| 5D Excel export | ExcelJS in-browser, no server | 2β3 seconds |
| 4D Excel export | ExcelJS in-browser, no server | 1β2 seconds |
After the first load, the DB is cached in IndexedDB. Subsequent visits and every Excel export
read locally β zero network round-trip. Β§CHARTS_DB_SOURCE source=idb|import|oci is logged
to the console and embedded in the downloaded export log for auditability.
Browser HTML mode ready β here in exotic language (Localisation).
Mobile mode granted on-the-fly.
Where the page's format is kept, and how to edit it¶
The 4D/5D page, its Excel files and the MEP bill are open source (MIT) and editable at source in the bim-ootb repository (paths below at origin/main, checked 2026-11-01). The 4D/5D pages are live pages and Excel downloads, not one composed HTML file like the Modeller's Regulatory Report.
| Output | How reached | Format kept at source | Guard |
|---|---|---|---|
4D/5D page (live page, boq_charts.html) |
Viewer Inspect drawer "4D / 5D" (key 4): viewer/panels.js:1524 calls A.export4D5D, viewer/tools.js:596-611 opens boq_charts.html?db=...&bld=... in a new tab. The Modeller's "Edit rates" link opens the Viewer rate editor (modeller/dxf_import_ui.js:796) |
Self-contained in viewer/boq_charts.html: look = inline <style> at lines 10-39; page skeleton and toolbar lines 42-55; panel titles built in renderCharts() from line 1535 (cost by discipline 1570, cost components 1584, task distribution 1608, task count 1625, phase duration 1642, resource workload 1659, S-curve 1673, milestones 1717, Gantt 1753, site resources 1892, variation impact 1934, 5D Bill of Quantities 1979). Titles are translation keys (_TRL.t_*) filled by viewer/locale_loader.js |
tests/specs/05-charts.spec.js, 22-4d-audit.spec.js (Playwright); modeller/tests/witness_dxf_shed_chain.js S5 (the page opens on a DXF-built database with tasks > 0). No witness found that compares the page's section list to a stated one |
| 4D Schedule Excel | "4D" button on the page (boq_charts.html:47, save4D()) |
viewer/export_4d.js: 3 sheets (lines 27, 43, 69); file blob line 144 |
tests/specs/06-excel-export.spec.js 6.3 |
| 5D Cost Excel | "5D" button (boq_charts.html:46, save5D()) |
viewer/export_5d.js: sheets Cover, Executive Summary, Material, Labour, Equipment (lines 46-178), 5D-BOQ 200, Work Packages 260, per-discipline BOQ 311, Provenance 340, Variation Order 401, Variance Summary 439; filename line 465 |
tests/specs/06-excel-export.spec.js 6.4 |
| MEP Bill of Quantities (HTML) | "MEP" button on the page (boq_charts.html:2239-2245; road models get model_check_report.html instead) |
viewer/mep_report.html: inline <style> line 8, toolbar 48-52, sections R, S, T, U, V, W, Y at lines 69-75, assembled from line 397. "Share Report" (line 89) saves the page as the self-contained file MEP_BOQ_<building>_<date>.html; "CSV" saves MEP_BOQ_<building>_<date>.csv |
tests/specs/25-mep-report.spec.js |
Things to know before editing:
- Several places build 4D/5D content, there is no single composer. The page, the two Excel exports and the MEP report are four separate pieces of code. They share the numbers (
viewer/rates.js,viewer/schedule_author.js,viewer/analysis_sidecar.jskeeps the quantity basis consistent withexport_5d.js) but not the layout. A wording or colour change must be made in each file. This is a known difference from the Regulatory Report, which has one composer. - Not part of 4D/5D:
modeller/load_summary.html(Export > Load Summary) is the structural load page, not a schedule or cost page; its numbers come frommodeller/load_summary.jsand it is guarded bymodeller/tests/witness_dxf_load_summary.js. - No file on disk is produced by the 4D/5D page itself except the Excel files and the MEP HTML/CSV; the page is saved by sharing its link (the clipboard button), not as an HTML file.
What you can edit before the page or the Excel files are generated (Viewer Settings, "Edit Project JSON"; viewer/panels.js:2298-2301):
- 5D Rate Pack (
rates/<pack>.json, saved in the browser underjson_rates_<pack>, read byviewer/rates.js:756). - 4D Schedule Template (phases, durations;
rates/4D_template.json, saved underjson_4d_template, read byviewer/tm_sched_xray.js:907). - 4D Sequence and Labour Rules (
rates/sequence_rules.json, saved underjson_sequence_rules, read byviewer/rates.js:912). - The active rate pack is chosen in the "5D Rate Pack" section (
viewer/panels.js:1863), or with?rates=<pack>&edit=rateson the Viewer URL (viewer/panels.js:2423, witnessmodeller/tests/witness_edit_rates_link.js).
Witness viewer/tests/witness_json_4d5d.js (W-JSON-4D5D) proves each reference JSON survives the editor unchanged, that the editor saves under the keys the loaders read, and that a saved copy applies to its own pack only.
The Generated 4D Movie β Support Order Is a Measured Invariant¶
The tables above are the task-level 4D (the Python nD_engine.py path β phases, durations,
S-curve). The viewer's Time Machine plays a second, finer thing: an element-level schedule
generated in the browser from the same extracted DB β every element gets its own start and finish
from real geometry, real labour rates and real crew caps, and the movie reveals the building in that
order. Nothing is authored by hand; drop an IFC and the film exists.
The hard part of that is not the arithmetic. It is that an element must never appear before the thing holding it up. Get it wrong and the film shows a roof over open air, a duct hanging in a void, a stair flight floating between storeys β the single defect that makes an automated 4D look fake to anyone who has been on a site.
The invariant¶
No element appears before the first element it physically touches appears.
"Touches" is geometric and class-blind: bearing below, carrier above, or embedded within β the element's real bounding geometry against every other element's, with no whitelist of IFC classes and no dependence on the model author having modelled relationships correctly. Elements that form the ground layer of their own footprint are exempt: they rest on soil, which no IFC file contains.
Measured, on every shipped building¶
Before this invariant was enforced, the schedule looked clean by every internal check the engine had β and the film still had visible hangings, because the old audit only compared an element against the supports it already knew about (structure and walls), which is a small slice of what an element actually rests on. The census below counts the real thing: elements whose neighbours are all still invisible at the moment they appear.
| Building | Elements | Appeared with nothing they touch on screen | After |
|---|---|---|---|
| Terminal | 48,428 | 161 | 0 |
| Hospital | 63,182 | 165 | 0 |
| Duplex | 1,119 | 19 | 0 |
| HHS Office (federated) | 6,839 | 156 | 0 |
| Clinic | 16,071 | 345 | 0 |
| LTU A-House | 122,330 | 4,605 | 0 |
| JKR | 8,985 | 110 | 0 |
| Total | 266,954 | 5,561 | 0 |
Verification is a witness (witness_midair_zero.js, 22/22 across the seven buildings) that
re-derives the contact geometry itself rather than asking the scheduler whether it is happy β a
mis-wired or no-op repair fails the witness instead of certifying itself. Cost is a one-time
0.02β1.8 s at generation, scaling with element count.
What this does not claim¶
- Orphans are reported, not repaired. 972 elements across the seven buildings touch nothing anywhere in the model at any time. No schedule can fix an element that has no neighbour; that is an authoring/extraction fact, and it is printed rather than hidden.
- The invariant is "has appeared", not "is finished". An element may arrive while its support is still being installed β which is exactly how real trades work, and how the viewer draws it (an in-progress element is on screen, lit as the active work front). The stricter "wait for the support to complete" rule was built and measured: it moves elements by up to 103 days, still leaves violations because the supports move too, and converges only by serialising the whole project β destroying the overlapping trade train that makes a construction programme realistic. It is therefore deliberately not enforced, and the population it would affect is reported every run.
- Seven buildings, not "all buildings". The invariant is structural and class-blind, so it should hold generally β but what is measured is the seven shipped fixtures, 266,954 elements.
No Other Player Does This¶
What the industry charges for nD analytics¶
| Capability | Industry tool | Cost per seat/yr | Our speed |
|---|---|---|---|
| 5D BOQ from BIM | CostX, Cubicost, iTWO | $10-50K | 8.7s for 1M elements |
| 4D Schedule from BIM | Synchro, Navisworks, Asta | $5-15K | same run |
| 6D Carbon from BIM | OneClick LCA, Tally | $5-15K | same run |
| 7D Asset register | Maximo, Archibus, Planon | $20-100K | same run |
| 8D Safety register | Manual spreadsheets | Weeks of HSE time | same run |
| All 5 dimensions combined | $60-180K/yr | 8.7 seconds, free |
What makes this different¶
-
Speed β 1M elements, 5 dimensions, 8.7 seconds. Commercial tools take hours per dimension on models this size. Most cannot even load 1M elements.
-
All dimensions in one pass β Commercial tools are siloed. 4D is one vendor, 5D another, 6D another. Each needs its own model import, its own licence. We run all 5 from the same SQLite file in one command.
-
User-modifiable templates β JSON files, not proprietary rate databases locked behind vendor UIs. A QS edits
5D_rates.jsonin any text editor. A sustainability consultant edits6D_carbon.json. No vendor lock-in, no format conversion. -
Self-healing dependencies β Request 6D, engine auto-generates 5D. Request 8D, engine auto-generates 4D. No manual pipeline orchestration.
-
Open source β Zero licence cost. SQLite databases. Python scripts. JSON templates. Everything auditable, everything modifiable.
Template Editing β How to Customize¶
All rules live in JSON files under templates/. Edit with any text editor.
Editing Templates¶
The JSON templates are designed to be human-readable. Each entry maps an IFC class
to its nD properties. The _default key catches any unmapped class.
Direct editing (any text editor β VS Code, nano, Notepad++):
# Edit material rates
code templates/5D_rates.json
# Edit carbon factors
code templates/6D_carbon.json
Validation β run the engine after editing to verify:
python3 scripts/nD_engine.py mydb.db --dims 5D
# Check scripts/nD_engine_log.txt for Β§5D_RATE lines showing which rates applied
Country-specific templates β copy the defaults and swap rates:
cp -r templates/ templates_UK/
# Edit templates_UK/5D_rates.json β change currency, rates to BCIS/RICS
# Edit templates_UK/6D_carbon.json β change factors to BREEAM
python3 scripts/nD_engine.py mydb.db --all --templates templates_UK/
Adding a New IFC Class¶
Add one entry per template:
// 4D_phases.json β ifc_class_rules:
"IfcNewClass": {"phase": "MEP Rough-in", "sequence": 5, "predecessors": ["IfcSlab"],
"resource": "HVAC_TECH", "productivity": 12.0}
// 5D_rates.json β material_rates:
"IfcNewClass": {"rate": 250.00, "unit": "M", "spec": "Your specification"}
// 6D_carbon.json β carbon_factors:
"IfcNewClass": {"factor": 15.0, "per": "M", "note": "Source reference"}
// 7D_lifecycle.json β lifespan_rules:
"IfcNewClass": {"lifespan_years": 20, "warranty_months": 24,
"service_interval_months": 6, "note": "Maintenance note"}
Measurement Rules¶
In nD_formulas.json, IFC classes are grouped by how they're measured:
- linear: height from bounding box (maxZ - minZ) β meters
- area: footprint from bounding box (dX * dY) β mΒ²
- volume: full bounding box (dX * dY * dZ) β mΒ³
- count: element count β EA (default for unmapped classes)
nD Dimensions β What Each Produces¶
4D β Construction Schedule¶
Phase mapping + productivity rates β task durations β Gantt-ready data.
Calendar: Mon-Sat working, Sunday off, 8hr/day (configurable in 4D_phases.json).
5D β Cost Estimation (BOQ)¶
Material rates + labor productivity + equipment allocation β total cost per element. Currency: MYR (Malaysian Ringgit), CIDB 2024 rates. Swap template for other countries.
6D β Sustainability / Carbon Audit¶
Embodied carbon factors (ICE Database v3.0) β kgCO2e per element β project total. Operational carbon: Malaysia grid factor 0.585 kgCO2/kWh (TNB 2024).
7D β Facility Management / Asset Lifecycle¶
Per-element asset register: GUID β asset tag, warranty, service interval, replacement year. Source: RICS Building Maintenance Guide + JKR Malaysia standards.
8D β Safety / Hazard Register¶
Phase Γ IFC class β hazard classification, risk level, PPE requirements, permit flags. Source: DOSH Malaysia OSHA 1994, CIDB Site Safety Handbook.
Debug Logging¶
Every run produces scripts/nD_engine_log.txt with Β§-prefixed log lines:
Β§ENGINE_START, Β§ENGINE_DB, Β§ENGINE_DIMS β session metadata
Β§TEMPLATE_LOADED β which templates were read
Β§5D_IFC_CLASSES, Β§5D_MEASURE β per-class measurement type resolution
Β§5D_INSERT, Β§5D_RATE β what was inserted and at what rate
Β§5D_UNCOSTED β IFC classes with no rate (uses _default)
Β§4D_DEFAULT β classes using _default fallback (was "Unknown")
Β§4D_PHASE β task count per phase
Β§6D_ROW β per-row carbon calculation
Β§7D_ELEMENTS β total asset count
Β§8D_ROW β per-row hazard classification
Β§ENGINE_DONE β summary with row counts
Read the log after every run. Exit code alone is not evidence.
Architecture¶
IFC files βββ extractIFCtoDB.py βββ _extracted.db
β
ββββββββββββββββββββββββββΌβββββββββββββββββββββββββ
β β β
nD Engine Bonsai Viewer Java Backend
(scripts/nD_engine.py) (bbox + mesh) (REST + Excel)
β β β
ββββββββββββΌβββββββββββ β β
β β β β β 3D viewport /api/boq/
4D 5D 6D 7D 8D 125K elements /api/schedule/
schedule qto carbon asset safety smooth nav
β
JSON Templates
(templates/*.json)
Files¶
| File | Role |
|---|---|
scripts/nD_engine.py |
Abstract nD engine β reads templates, queries DB, writes tables |
scripts/test_nD_engine.py |
Unit test β 28 witnesses on single building |
scripts/test_nD_fleet.py |
Fleet test β all buildings + sandbox |
scripts/nD_fleet_report.txt |
Fleet test results (regenerated each run) |
scripts/nD_engine_log.txt |
Debug log (regenerated each run) |
templates/nD_formulas.json |
Master: dimension registry, measurement rules, formulas |
templates/4D_phases.json |
Phase sequence, predecessors, productivity, calendar |
templates/5D_rates.json |
Material, labor, equipment rates (MYR CIDB 2024) |
templates/6D_carbon.json |
Embodied carbon factors (ICE Database v3.0) |
templates/7D_lifecycle.json |
Warranty, service interval, lifespan (RICS/JKR) |
templates/8D_safety.json |
Hazard class, risk matrix, PPE (DOSH/CIDB) |
scripts/schedule_database_schema.py |
Schema for construction_schedule table |
Legacy (still functional, superseded by nD engine)¶
| File | Role |
|---|---|
scripts/simple_qto_extract.py |
Original 5D β hardcoded rates |
scripts/schedule_generator.py |
Original 4D β hardcoded phase rules |
What's Done¶
- Excel export β Township Excel: Cover Sheet, Executive Summary (archetype Γ instances),
30 per-archetype BOQ sheets, Schedule - Township. Auto-generated by
nD_engine.py --all. - Township mode β auto-detects multi-building DBs (
COUNT(DISTINCT building) > 1). Archetype dedup: suburb tilesS{row}_{col}_{type}β archetype, CBDT0_{name}β name. Compute BOQ once per archetype, multiply by instance count. 786 buildings β 30 archetypes. All 5 output tables (simple_qto, construction_schedule, carbon_audit, asset_register, hazard_register) now includebuildingcolumn. Single-building DBs unaffected. - 28/28 unit tests PASS β backwards compatible, no regression.
4D Ghost Glass β Live Construction Animation (S240b)¶
"Watch your building get built."
The Gantt chart (boq_charts.html) now drives a live construction animation in the 3D viewer via BroadcastChannel. Two browser tabs, side by side β drag a timeline scrubber on the Gantt and the building constructs itself in real time.
How It Works¶
- User opens the viewer (Tab 1) and BOQ charts (Tab 2) for the same building
- The Gantt chart shows all construction tasks as horizontal bars, colour-coded by phase
- An orange vertical scrub line overlays the Gantt β drag it left/right to scrub time
- On first touch (Day 1+), the building turns into a glass shell (0.03 opacity)
- As the scrubber crosses each task's start day, those elements snap to solid with a bright glow
- Elements already built restore to normal solid material β the building fills in progressively
- Drag back to Day 0 β building fully restores to normal
Visual States¶
| State | Appearance | When |
|---|---|---|
| GLASS | Near-invisible shell (0.03 opacity, pale blue) | Future β not yet constructed |
| ACTIVE | Bright glow, shines through all geometry (depthTest:false) |
Currently being constructed |
| BUILT | Solid original material (full opacity) | Already constructed |
Active elements use rotating highlight colours (orange β green β red β yellow β cyan)
cycling per task, so MEP rough-in is visually distinct from structural steel. The glow
uses depthTest:false (the same technique as clash detection) so pipes and ducts behind
walls are visible through the structure.
Controls¶
[ βΆ Play ] [ βΈ Pause ] Speed: [1Γ] [2Γ] [4Γ] [8Γ] Day 42 / 312 Superstructure: Beam (STR)
- Drag on Gantt β scrub the orange line across the timeline
- Play button β auto-advance through all tasks at selected speed
- Day 0 β building fully restores (no glass, normal materials)
- Click a Gantt bar β highlights just that task's elements in the viewer
- Copy URL β share the exact chart view with anyone
CTFL Audit Gate¶
Before animation starts, an 8-check audit validates the schedule quality:
| Check | What |
|---|---|
| COVERAGE | Every IFC class maps to a SEQUENCE_RULE (no defaults) |
| PHASE_ORDER | No phase inversions per storey |
| STOREY_ORDER | Bottom-up within each phase |
| DURATION | All tasks 1β120 days |
| OVERLAP | No same-trade + same-class overlap on same storey |
| TOTAL | Project duration β€ 3Γ serial sum |
| LABELS | No truncated or undefined labels |
| GUIDS | Every task resolves to β₯1 element GUID |
Failed checks show a yellow warning banner. Console Β§4D_AUDIT_* tags show details.
Architecture¶
boq_charts.html index.html (viewer)
ββββββββββββββββββββ ββββββββββββββββββββ
β generateSchedule β β ghostglass.js β
β audit4DSchedule β BroadcastChannel β setupGhostGlass β
β GUID resolution βββββ bim_4d βββββββΊβ makeGlass/Active β
β scrubber + Play β 4D_PLAY/SEEK/RESETβ makeBuilt/restoreβ
β Copy URL button β β material cache β
ββββββββββββββββββββ ββββββββββββββββββββ
rates.js main.js
SEQUENCE_RULES BroadcastChannel
LABOR_RATES listener +
PHASE_COLORS delegation
No server. No WebSocket. Pure BroadcastChannel between same-origin tabs.
ghostglass.js is a pure renderer β no internal timer. The Gantt controls all pacing.
What This Replaces¶
| Capability | Commercial tool | Cost/yr | Our approach |
|---|---|---|---|
| 4D playback | Synchro Pro | $25K | Ghost glass animation |
| Construction sim | Navisworks TimeLiner | $8K | Drag scrubber on Gantt |
| Phase visualization | Custom Gantt tools | $5-15K | BroadcastChannel sync |
| Total | $38-48K | Free, browser-native |
Files¶
| File | Role |
|---|---|
viewer/ghostglass.js |
Glass-to-solid animation engine |
viewer/boq_charts.html |
Audit, GUID resolution, scrubber, Play/Pause |
viewer/main.js |
BroadcastChannel listener, ghostglass delegation |
viewer/rates.js |
SEQUENCE_RULES, LABOR_RATES, PHASE_COLORS (50+ IFC classes) |
viewer/tools.js |
Hover preserves 4D phase colours |
tests/specs/22-4d-audit.spec.js |
8 Playwright tests for audit gate |
4D Time Machine β Interactive Construction Playback¶
The Time Machine (time_machine.js, tm_*.js) brings 4D simulation directly into the 3D viewer.
No separate app, no schedule file import, no Blender rendering. One button, any IFC model.
How It Works¶
- User drops any IFC file into the viewer
- Opens the Time Machine (the clock pill in the Inspect drawer, or key T)
kernel_opstable is auto-populated with a weighted construction sequence derived from:SEQUENCE_RULESβ phase assignment per IFC class (Substructure β Superstructure β MEP β Architecture β Finishes)LABOR_RATESβ productivity per trade (units/day β seconds per element)- Spatial sort β
element_transforms.center_z(bottom-up), then X/Y - Storey rank β basements first, ground floor, upper levels, roof last
- The building constructs itself from an empty site β element by element
Playback Controls¶
| Button | Action |
|---|---|
| ββ | Jump to empty site (hour 0, day 1) |
| β | Deconstruct backwards at mode speed |
| β | Stop β slider becomes active for scrubbing |
| βΆ | Build forward from current position |
| βΆβΆ | Jump to fully built |
DAY / HR / MIN β controls both playback speed and slider scope:
- DAY mode: playback advances 1 hour per tick (24 ticks = 1 day). Slider scrubs across project days.
- HR mode: playback advances 1 minute per tick. Slider = 24 hours within the stopped day.
- MIN mode: finest grain. Slider = 60 seconds within the stopped minute.
Visual States¶
Each element exists in one of three visual states:
- Future (not yet placed) β invisible
- Frontier (being installed) β orange glow, see-through,
depthTest: false,renderOrder: 999 - Recently placed (within 3 ticks) β faded yellow linger, then snaps to solid
- Built β solid, original material restored
Weighted Durations & Parallel Trades¶
Element install time is derived from LABOR_RATES.productivity:
| Trade | Example Class | Productivity | Install Time |
|---|---|---|---|
| STEEL_ERECTOR | IfcColumn | 6/day | ~80 min |
| CONCRETE_GANG | IfcSlab | 35/day | ~14 min |
| MASON | IfcWall | 12/day | ~40 min |
| ELECTRICIAN | IfcOutlet | 25/day | ~19 min |
| CARPENTER | IfcDoor | 6/day | ~80 min |
Parallel trades: different phases on the same storey overlap when the predecessor
phase finishes. MEP rough-in starts on ground floor while superstructure continues on
upper floors. This matches the Gantt chart bar layout in boq_charts.html.
Kernel Ops β The Fossil Record¶
Every construction event is logged in the kernel_ops SQLite table:
CREATE TABLE kernel_ops (
id INTEGER PRIMARY KEY,
timestamp INTEGER NOT NULL, -- start time (ms)
op_type TEXT NOT NULL, -- 'ELEMENT_PLACE', 'VIEW_FILTER', 'ELEMENT_PICK', ...
parameters TEXT NOT NULL, -- JSON: {phase, cls, name, storey, _end_ts}
input_guids TEXT, -- JSON array of affected GUIDs
output_guid TEXT, -- primary element GUID
undone INTEGER DEFAULT 0
);
The same table also captures live user activity:
- ELEMENT_PICK β element clicked in viewer
- VIEW_FILTER β storey or discipline filter toggled
- SECTION_CUT β section plane saved
- GRID_MOVE β grid line dragged in 2D editor
This makes kernel_ops a complete audit trail of both synthetic construction
and real user interaction β queryable, replayable, exportable.
Performance¶
The time machine is pure data playback β no geometry upload, no shader recompilation.
Each frame toggles mesh.visible and swaps a cloned material for the frontier highlight.
InstancedMesh elements use the zero-scale matrix technique (same as ghostglass).
Cost: one scene.traverse() per frame. GPU load identical to a static scene.
Shareable 4D Links¶
The Time Machine supports URL parameters for shareable construction playback:
| URL Parameter | Behaviour |
|---|---|
?tm=1 |
Open viewer with Time Machine panel active |
?tm=play |
Open and auto-play construction from empty site |
The π Share button (top-left of panel) copies the current viewer URL with ?tm=play appended β recipient opens the link, the building loads, and construction plays automatically. Zero setup, one click.
Example:
.../viewer/viewer.html?db=buildings/Duplex_extracted.db&tm=play
Architecture¶
β³ press
β
ββ kernel_ops empty? β injectGantt()
β ββ reads elements_meta + element_transforms
β ββ sorts: SEQUENCE_RULES.sequence β storeyRank β center_z
β ββ applies LABOR_RATES productivity β weighted duration per element
β ββ schedules: sequential within (phase, storey), parallel across phases
β ββ writes ELEMENT_PLACE ops with start_ts + _end_ts
β
ββ kernel_ops populated β loadOps() β playback ready
ββ renderAtTime(cursor) β traverse scene, set visibility + highlights
ββ playTick() β advance cursor by tickMs(), loop
ββ slider scrub β maps DAY/HR/MIN to cursor position
Files¶
| File | Role |
|---|---|
viewer/time_machine.js |
Time machine panel, injection, playback, highlighting |
viewer/kernel_ops.js |
commitOp(), undo/redo, replay infrastructure |
viewer/precision_cam.js |
Precision camera (fine move + zoom reset) for detail inspection |
viewer/picking.js |
Logs ELEMENT_PICK to kernel_ops |
viewer/panels.js |
Logs VIEW_FILTER to kernel_ops |
viewer/section_cut.js |
Logs SECTION_CUT to kernel_ops |
viewer/rates.js |
SEQUENCE_RULES, LABOR_RATES β drives injection weighting |
Where the roadmap landed¶
Items that were once listed here as next steps and have shipped in the browser Viewer:
- Browser nD engine β the 4D schedule, 5D cost and the Excel exports run in the browser on the same
JSON references (
viewer/rates.js,viewer/export_5d.js,viewer/export_4d.js); no server. - Community rate templates β 16 national 5D rate packs ship (UK BCIS, US RSMeans, Malaysia CIDB, β¦) under
viewer/rates/. - Template editor β Viewer Settings β Edit Project JSON edits the rate pack, 4D template and sequence/labour rules in the browser (see above).
- SMM work sections β carried in the rate packs (
smm_sections).
Copyright (c) 2025-2026 Redhuan D. Oon. MIT Licensed.