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.
Views

3D Perspective View (default)
- Orbit controls: drag to rotate, scroll to zoom, right-drag to pan
- Click element in 3D → highlights blue, dims others, shows properties panel
- Click element in sidebar → flies camera to element, highlights it
- Click empty space → deselects all
2D Plan View

- Toggle: "2D Plan" / "3D View" button
- Orthographic camera looking straight down
- Section plane clips walls at ~40% height (shows door openings)
- White edge outlines on walls (
EdgesGeometry) —edgeGroupis a child ofallMeshGroup(not scene root) so outlines inherit the centering offset - Darkened slab color for contrast
- Pan and zoom (rotation disabled)
- Brighter ambient lighting for flat appearance
Overlay Systems
Element Sidebar
- Groups elements by IFC type (IfcDoor, IfcSlab, IfcSpace, IfcWall, etc.)
- Eye button (👁) per element: hide/show in 3D
- Search filter: live text filtering
- Properties panel: shows Type, Name, GlobalId, ExpressID on selection
Space Containers
- Green transparent boxes showing room volumes
- Hidden by default, visible when a space is clicked in the sidebar
- Geometry computed server-side (
/spaces/endpoint) from IFCIfcExtrudedAreaSolid - Positioned using auto-detected coordinate mapping (
perm/sgn/off)
Connection Graph
- "Calculate Graph" button: computes the graph server-side, saves JSON cache. Becomes "Recalculate Graph" after first run
- "Show Graph" button: appears after cache exists, loads and renders the overlay. Toggles to "Hide Graph"
- Green spheres: internal connection points
- Orange spheres: external junction points (where elements meet)
- Green/orange lines: internal/external edges
- Data from
/connection_graph/<filename>endpoint (?force=1to recompute)
MEP Cable Routes
- Toggle: "Show Routes" / "Hide Routes"
- Orange cylinder tubes: cable path segments
- Yellow emissive spheres: light fixtures
- White boxes: switches
- Data from
/analyzeendpoint withtool=mep_routing
Installation Zones
- "Calculate Zones" button: computes zones server-side via background job. Becomes "Recalculate Zones" after first run
- Checkboxes: "Wall Zones", "Ceiling Zones" (independent toggles)
- Dark red/yellow/green/blue overlays on wall and ceiling surfaces
- Wall zones: horizontal/vertical installation corridors per DIN 18015-3
- Ceiling zones: ZD-r perimeter bands (solid ceiling) or full fly zone (false ceiling)
- Options panel with checkboxes: "Abgehängte Decke", "Doppelter Boden"
- Batched rendering: all quads per color category merged into one geometry (~8 meshes total)
- Zone data cached in JS; storey filter rebuilds merged geometry from cache
- Data from
/wall_zones/<filename>?mode=...endpoint
Storey Filter

- Radio-button panel (top-left overlay, below model panel)
- Lists all
IfcBuildingStoreyelements with name and elevation - "All Storeys" (default) or select one storey to isolate
- Filters: meshes, edge lines, wall zones, and ceiling zones
- Panel hidden when IFC file has no storeys
- Data from
/storeys/<filename>endpoint
Background Job System
- Bottom-left floating panel shows running/completed/failed jobs
- Each entry: operation name, current phase with progress (e.g., "3/6"), elapsed time, spinner
- Polls
GET /jobs/<id>every 5 seconds while jobs are active - On page load, reconnects to running jobs via
GET /jobs?filename=&project= - On completion: auto-fetches result, renders visualization, re-enables button
- Panel auto-hides 5 seconds after all jobs finish
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
- Compute IFC world bounding box (from
/spaces/endpoint:ifcWorldMin/Max) - Compute Three.js model bounding box (
THREE.Box3.setFromObject) - Try all 48 permutation+sign combinations (6 axis permutations × 8 sign combos)
- For each candidate: check size match (tolerance 1.0m), compute offset
- 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
- Material sharing: materials cached by RGBA color key (~50 shared instances instead
of one per geometry). Uses
MeshLambertMaterial(cheaper than PBRMeshStandardMaterial). - Frustum culling: bounding boxes computed per geometry so Three.js skips off-screen meshes
- Renderer settings: shadows disabled, pixel ratio capped at 2,
powerPreference: 'high-performance' - Zone batching: all zone quads per color category merged into single
BufferGeometry(~8 meshes instead of ~50,000 for large models)
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) |