vflow2 Documentation

IFC Analysis Tools ← Back to App

3D/2D Viewer

Purpose

Interactive web-based viewer for IFC building models with multiple overlay systems. Uses Three.js for rendering and web-ifc for parsing IFC geometry in the browser.

File

templates/viewer.html — single-page application, self-contained.

Selection-aware shell (persistent Inspector + context ribbon)

The viewer is selection-aware: the same selection is visible across every view through two shared UI elements:

Both subscribe to the same in-memory selectionStore. Views dispatch selections on click (3D canvas, sidebar element, Heizlast surface row, Roombook row). The Inspector + ribbon are pure views over the existing global bundles (heizlastData, sizingData, roombookData, presetsData, elementData) — they never cache. A bundleBus fires whenever any bundle is reassigned, so editing a U-value in the Heizlast table re-renders the Inspector without a reselect. See CLAUDE.md §"Selection-aware viewer" for the data-flow diagram.

3D view with wall selected — Inspector shows pset data, ribbon shows breadcrumb

Heizlast with a surface row selected — Inspector shows Φ breakdown, ribbon shows one-line context, per-room search visible above the surface table

Layout management (CSS grid, no z-index war)

Element Grid area Notes
Top-nav header (row 1) unchanged five-tab nav
Main view area main (row 2 left) shrinks when Inspector is expanded
Context ribbon ribbon (row 3 left) 42 px; hides nothing unless empty
Inspector panel inspector (rows 2+3 right) 320 px expanded / 32 px collapsed rail

Minimum viewport width for the full three-column layout: 1280 px. Below that, Inspector auto-collapses to a 32 px rail on load. Mobile is explicitly out of scope — the collapsed rail still shows selection + job badges for awareness.

Detail view at default layout — Inspector placeholder on the right, jobs section at the bottom of Inspector

App shell: Detail / 3D / Roombook / Presets / Heizlast / Export / Versionen

Served by /project-viewer/<project>. Top nav swaps between views without reloading; the IFC is downloaded once on page load and shared across views.

View Hash Content
Detail (default) #detail Project name, files + sizes, cache status dots, model-load indicator, shortcut buttons. Top of Detail shows the 3-Step Workflow Wizard (1 Overrides prüfen · 2 Review exportieren · 3 Revision hochladen), source-agnostic.
3D #3d The Three.js viewer (sections below)
Roombook #roombook Typed table of IfcSpaces: name / long_name / purpose (text) · usage_type / shielding (enum) · design_temp / ach_min / volume (number) — IFC + override columns, renderer driven by backend field_specs
Presets #presets Per-model Heizlast project parameters: Standort card (city dropdown + PLZ input → auto-fills θ_e from /climate/de), θ_e, n₅₀, ΔU_TB, shielding default, reheat, U-value defaults per surface type (wall external/internal/to-unheated, window, door external/internal, floor ground/internal, roof, ceiling to unheated). Save-on-blur via /heizlast_presets/<file>
Heizlast #heizlast Top band with building total Φ_HL + presets summary; per-storey room cards (collapsible). Each card shows usage chip, θ_int, V, Φ_HL with Φ_T/Φ_V/Φ_RH breakdown. Expandable body has two tabs: Flächen (default — the auto-extracted bounding surfaces with inline editable area/U/adjacency/f_k cells; U-source chips pset / preset / default / override; click-row → 3D highlight) and Auslegung (Heizflächen sizing — see Heizflächenauslegung). The → IFC column is a single status dot per row (green = at least one field flagged for writeback); clicking jumps to the Export panel.
Export #export Export Preview — tree of every value that will be stamped into the review IFC, grouped by IfcSpace / IfcWall / IfcWindow / IfcDoor / IfcSlab / IfcProject. Per-leaf + per-group + global toggles. See IFC write-back for field semantics.
Versionen #versions Architect-package timeline. Each card = one architect IFC + its caches + a nested list of stamped review IFCs. See Version workflow.

Detail view

Roombook view

Heizlast surface table with active row and 3D link-through

3D view with a highlighted wall selected from the Heizlast row

Heizlast export dropdown: CSV, Excel (XLSX), Drucken/PDF

Heizlastprotokoll print preview in a new tab

Presets Standort card: city dropdown + PLZ autofill for θ_e

Presets Grund / Erdreich card: λ_g and wall thickness for EN ISO 13370

Heizlast surface table with ISO 13370 U_bf chip and P (m) column on ground-floor rows

Auslegung tab inside a Heizlast room card — Richtwerte disclaimer chip + default candidate with coverage badge

Two sizing candidates side-by-side: FBH vs. Heizkörper Type 22 H600×L1000

Variante-hinzufügen menu: Heizkörper + FBH enabled, Wand/Decke/Handtuch disabled with "Noch nicht implementiert"

FBH overload on small high-load rooms — orange Grenze-überschritten warning

Systemtemperatur & Auslegung card in Presets — θ_V/θ_R, caps, radiator defaults

Views

3D viewer with storey panel

3D Perspective View (default)

2D Plan View

2D plan view

Overlay Systems

Element Sidebar

Space Containers

Connection Graph

MEP Cable Routes

Installation Zones

Storey Filter

Storey filter — single storey selected

Background Job System

Job monitor on the Heizlast tab — one job running, two done

The viewer never blocks on a heavy compute. Every expensive GET (/spaces, /connection_graph, /wall_zones, /roombook, /heizlast) either returns the cached result (200) or enqueues a single-worker FIFO job (202 + job_id); the client polls and re-fetches once done. See CLAUDE.md §"Job Runner System" for the backend side and documentation/06_api_endpoints.md for the endpoint contract.

Coordinate Mapping

The Problem

IFC files use one coordinate system. web-ifc (with COORDINATE_TO_ORIGIN) may remap and shift axes when loading geometry. All backend data (routes, graph, zones, spaces) is computed in IFC world coordinates, but must be transformed to match the Three.js scene.

Auto-Detection Algorithm

  1. Compute IFC world bounding box (from /spaces/ endpoint: ifcWorldMin/Max)
  2. Compute Three.js model bounding box (THREE.Box3.setFromObject)
  3. Try all 48 permutation+sign combinations (6 axis permutations × 8 sign combos)
  4. For each candidate: check size match (tolerance 1.0m), compute offset
  5. Pick mapping with minimum error (transformed space bbox fits within model bbox)

Result

coordMapping = {
  perm: [0, 2, 1],     // perm[threeAxis] = ifcAxis
  sgn:  [1, -1, 1],    // sign flip per axis
  off:  [0.0, 3.2, -10.4]  // translation offset
}

function ifcToThree(ifcPoint) {
  return new THREE.Vector3(
    sgn[0] * ifcPoint[perm[0]] + off[0],
    sgn[1] * ifcPoint[perm[1]] + off[1],
    sgn[2] * ifcPoint[perm[2]] + off[2],
  );
}

Usage

All overlay rendering functions call ifcToThree() to transform IFC coordinates before creating Three.js geometry. This single function handles the full mapping.

Performance Optimizations

Finding rooms / surfaces (Ctrl+F replacement)

Every long list has a built-in search input — this replaces browser native Ctrl+F in anticipation of per-list virtualization:

Roombook with search bar above the table

Result counts render next to each search input (24 / 312).

Glossar-Tooltips (Hover-Erklärungen)

Every technical abbreviation in the viewer — q, Grenze, A_eff, f_aktiv, ΔT_ln, n, θ_e, n₅₀, ΔU_TB, Φ_HL/T/V/RH, U-Quelle chips, f_k, P (m), and more — carries a dotted underline and reveals a tooltip on hover or keyboard focus. Each entry has a short description, optional formula, and a standards reference (DIN EN 12831, EN 442, EN 1264, EN ISO 13370).

Auslegung candidates with glossary tooltip on "Grenze" — shows definition, unit, EN 1264 reference

Presets view with every form label as a glossary chip; tooltip hovering on "n₅₀"

Architecture (templates/viewer.html): - A single GLOSSARY object in module scope is the source of truth for every term's label, unit, short description, optional formula, and ref. - The gl(key, override?) helper emits a <span class="gl-term" data-gl="…"> chip that the renderSizingCandidateRow, renderSurfaceTableIntoContainer, renderHeizlast, renderPresets, renderInspectorSurface, and renderInspectorRoom call sites wrap around labels. - One page-level #glTooltip div; document-level delegated listeners for mouseover / mouseout / focusin / focusout / Escape drive it. No dependencies. - Keyboard-accessible via tabindex="0" on each chip.

Coverage today — Auslegung candidate rows, Heizlast column headers, the .u-src chip, Heizlast top band, per-room header totals, Inspector surface and room cards, all Presets form labels (PRESET_FIELDS[i].gl key).

Known gaps / Phase 3: - Extend to Roombook column headers (usage_type, shielding, ach_min etc.). - A dedicated /docs/glossary reference page for print-out / onboarding. - English translations (currently German-only).

Phase 2 follow-ups (not shipped in this pass)

Triggers for revisiting documented in the plan file (plan-mode-weve-prancy-rabin.md):

State Management

Key JavaScript variables in the module scope:

Variable Type Purpose
elementMeshes Map Three.js meshes per IFC element
elementData Map Element metadata
coordMapping {perm, sgn, off} IFC↔Three.js coordinate transform
routeGroup THREE.Group Cable route overlay meshes
connGraphGroup THREE.Group Connection graph overlay
wallZoneGroup THREE.Group Installation zone overlays (renderOrder 1)
ceilingZoneGroup THREE.Group Ceiling zone overlays (renderOrder 1)
edgeGroup THREE.Group Wall outlines for 2D plan (child of allMeshGroup)
is2DPlan boolean Current view mode
wallZoneMode string "normal" | "false_ceiling" | "false_floor"
zoneDataCache object | null Cached zone JSON for fast storey rebuilds
storeyData array Storey list from /storeys/ endpoint
activeStoreyID number | null Selected storey (null = all)