Find & Navigate — Indoor Wayfinding Guide¶
Type "Find fire pump" in BIM OOTB. Walk to it from the front door.
Live demo: BIM OOTB
— load any building, type Find door in the search bar, click Navigate.
How to Use¶
- Type
Find <anything>in the search bar (or say it into the mic) - Amber panel shows matching elements — filter by type, storey, name
- Tap a result to highlight it (camera stays put)
- Tap Navigate — camera snaps to main entrance, first-person view
- Tap Walk arrow (or press arrow-up) — advance one waypoint at a time
- HUD shows: direction arrow, distance remaining, step N of M
- Arrival: target element highlighted, info panel opens
- Close search bar to exit navigation and return to orbit view
Voice input (mic button) adds spoken turn-by-turn cues on top of the visual HUD.
Route Template¶
When a building finishes streaming, the viewer auto-builds a walking path template for every storey. This is what powers the turn-by-turn directions.
What Gets Built¶
Your IFC data (walls, doors, columns)
│
▼
Occupancy Grid ─── 2m cells, walls = blocked, doors = walkable
│
▼
Route Template ─── graph of ~15-80 nodes per storey
│ (doors, corridor junctions, dead ends)
│ connected by BFS-discovered edges
▼
A* Pathfinding ─── shortest path on the graph, not the grid
│
▼
Waypoints ───────── ~4m steps, labelled by nearest room name
Template Structure (JSON-like)¶
{
storey: "Level 1",
nodes: [
{ id: "door_0", type: "door", x: 3.0, y: -0.2, label: "Main Entrance" },
{ id: "junction_5", type: "junction", x: 5.4, y: -5.0, label: "Corridor" },
{ id: "door_3", type: "door", x: 6.3, y: -9.7, label: "Office 201" },
{ id: "endpoint_8", type: "endpoint", x: 9.1, y: -2.3, label: "End" }
],
edges: [
{ from: 0, to: 1, cost: 6.2 }, // metres
{ from: 1, to: 2, cost: 5.8 },
{ from: 1, to: 3, cost: 4.9 }
]
}
Node types:
- door — every IfcDoor position on the storey
- junction — walkable cell with 3+ open neighbours (corridor intersection)
- endpoint — walkable cell with 1 open neighbour (dead end)
Labels come from the nearest IfcSpace or IfcRoom within 10m.
If none exist in the IFC, labels default to the node type ("Door", "Junction").
Where to Find It Locally¶
The template lives in browser memory (no file on disk). To inspect or edit:
- Open browser DevTools → Console
- After streaming completes, run:
// View all cached templates
APP._nav.gridCache // occupancy grids per storey
APP.getRouteTemplate("Level 1") // route template for a storey
// Inspect nodes and edges
var t = APP.getRouteTemplate("Level 1");
console.table(t.nodes); // id, type, x, y, label
console.table(t.edges); // from, to, cost
// Force rebuild (e.g. after editing DB)
APP.preProcessRouteTemplates();
How to Customise¶
The template is a plain graph. Edit it in the console or write a script:
var t = APP.getRouteTemplate("Level 1");
// Add a custom waypoint (e.g. reception desk)
t.nodes.push({ id: "custom_1", type: "junction",
x: 7.5, y: -3.0, label: "Reception" });
// Connect it to nearest existing node
t.edges.push({ from: t.nodes.length - 1, to: 1, cost: 4.0 });
// Lower cost = preferred path (makes A* favour this route)
t.edges[0].cost = 1.0; // make this corridor preferred
Changes persist until page reload. To make permanent changes, add
IfcSpace / IfcRoom elements to your IFC — they auto-label nodes.
Tuning¶
| Parameter | Default | Where | Effect |
|---|---|---|---|
CELL_SIZE |
2m | navigate.js line ~640 | Grid resolution. Smaller = finer but slower |
MAX_BFS |
80 cells | navigate.js §SECTION B | Max search radius for edge discovery |
STEP |
4m | navigate.js §SECTION C | Walk-step distance per tap |
EYE_HEIGHT |
1.6m | navigate.js §SECTION D | Camera height during walk |
Console Debug Logs¶
Open DevTools → Console. Filter by §NAV or §GRID or §ROUTE:
| Tag | What it shows |
|---|---|
§NAV_PREPROCESS |
All storeys processed, total nodes/edges |
§GRID_BUILD |
Grid size, occupied/walkable cells, door count |
§ROUTE_TEMPLATE |
Nodes, edges, types per storey |
§NAV_DIAG |
Pre-flight: target, camera, offsets, DB state |
§NAV_ENTRANCE |
Which door was picked as start, distance from centre |
§NAV_WAYPOINTS_DUMP |
Full path: [Start → Door → Door → Destination] |
§NAV_MOVE_CAM |
Camera position at each waypoint step |
Code Structure (for developers)¶
navigate.js (~1,850 lines) is a single setupNavigate(APP) closure.
Four sections, top to bottom:
navigate.js
├── SECTION A: FIND PANEL UI (lines ~10–630)
│ CSS injection, DOM creation, search/filter/select logic
│ Key functions:
│ openFindPanel(term) — reset state, populate dropdowns, run search
│ closeFindPanel() — hide panel, stop nav, restore orbit
│ runSearch() — SQL query → nav.results[], render list
│ selectResult(idx) — highlight element, set active card
│ populateDropdowns() — type/storey selects from DB
│ findMainEntrance() — pick exterior door (furthest from centre)
│
├── SECTION B: OCCUPANCY GRID + A* (lines ~640–880)
│ Grid-based pathfinding (no meshes needed, DB only)
│ Key functions:
│ buildGrid(storey) — 2m cells from wall/column positions
│ astar(grid, start, end) — 8-connected A* on Uint8Array
│ toCell(grid, x, y) — IFC coords → grid cell
│ fromCell(grid, c, r) — grid cell → IFC coords
│
├── SECTION C: ROUTE TEMPLATE + PATH (lines ~880–1370)
│ Graph skeleton + multi-storey path builder
│ Key functions:
│ buildRouteTemplate(storey) — extract door/junction/endpoint graph
│ graphAStar(template, s, e) — A* on ~15 nodes (not 5K cells)
│ graphPathToWaypoints() — graph path → 4m-step walk waypoints
│ buildPath(start, target, storey) — multi-storey orchestrator
│ buildSingleStoreyPath(s, e, storey) — template → grid → line fallback
│ labelNodes(nodes, storey) — tag nodes with IfcSpace/IfcRoom names
│
├── SECTION D: TURN-BY-TURN ENGINE (lines ~1370–1850)
│ Camera control, HUD, direction cues, voice
│ Key functions:
│ startNavigation(target) — pick entrance, build path, snap camera
│ advanceNavStep() — next waypoint, off-path repath if >8m
│ stopNavigation() — full exit: walk mode off, orbit restored
│ moveCameraToWaypoint(idx) — ifc2three transform, lerp or snap
│ onArrival() — highlight, info panel, fade HUD
│ getDirectionCue() — bearing delta → left/right/straight/stairs
│ speak(text) — SpeechSynthesis if voice mode
│
└── PRE-PROCESS HOOK (end of file)
MutationObserver on #s-active → preProcessRouteTemplates()
Integration with other modules¶
nlp.js ──"Find X"──→ APP.openFindPanel(term) ──→ navigate.js
(proxy in main.js lazy-loads navigate.js on first call)
walk.js ──────────→ APP.cacheStoreyLevels() used by buildPath for floor Z
APP.findNearestDoorPosition() used by walk mode (not nav)
APP.walkOrientTick() render loop, no-op if no anchor
scene.js ─────────→ APP.ifc2three(x,y,z) IFC → Three.js coord transform
APP.modelOffset origin shift
main.js ──────────→ APP.loadNavigate() lazy-load navigate.js (78KB)
_navProxy openFindPanel before load
animate() loop calls walkOrientTick each frame
State object¶
All navigation state lives in nav (closure variable, exposed as APP._nav):
nav.results // search results [{guid, ifc_class, element_name, storey, cx, cy, cz}]
nav.activeIdx // selected result index (-1 = none)
nav.waypoints // [{x, y, z, storey, label?, transition?}]
nav.stepIdx // current waypoint during walk
nav.active // true while navigating
nav.voiceMode // true if input was via mic
nav.gridCache // { "Level 1": {grid, cols, rows, minX, minY, doorCells} }
Route templates cached separately in routeTemplateCache["Level 1"].
Modifying the pathfinding¶
To change how paths are computed, edit Section B or C. The fallback chain is:
buildSingleStoreyPath()
1. Try route template graph A* ← edit buildRouteTemplate / graphAStar
2. Try grid A* ← edit buildGrid / astar
3. Fall back to straight line ← interpolateLine (always works)
Each layer logs its result with a § tag, so you can see in the console
which layer produced the path and why earlier layers failed.
Modifying the UI¶
The find panel is built with document.createElement in Section A (no framework).
CSS is injected at the top of setupNavigate(). To restyle: edit the CSS string
at lines ~10–82. Panel DOM IDs: #find-panel, #find-name, #find-type,
#find-storey, #find-results, #find-navigate-btn, #find-count.
HUD elements: #nav-hud, #nav-direction-cue, #nav-bottom-bar.
Source Files¶
| File | What |
|---|---|
deploy/dev/navigate.js |
Find panel + grid + A* + turn-by-turn engine |
deploy/dev/walk.js |
Walk mode, door finder, storey levels |
deploy/dev/nlp.js |
"Find X" intercept, voice flag |
| Algorithm spec | prompts/S233_find_and_navigate.md §B |
| Tests | deploy/dev/tests/specs/17-find-navigate.spec.js (26 tests) |
Copyright (c) 2025-2026 Redhuan D. Oon. MIT Licensed.