bridge-debug-overlays.ts
Canvas 2D debug visualizers for the hub/spoke bridge generator and the precomputed zone grid. Dev/playground only — never drawn in shipped builds. Lives outside the regular rendering layer because it draws on top of everything for design-iteration visibility.
Purpose
Two exported functions render LevelData (from ./world/chunk-manager) overlays onto a CanvasRenderingContext2D:
| Function | What it draws |
|---|---|
drawHubSpokeWireframe | Bright blue glowing wireframe — hub concentric circles + spoke center lines + hub-ID labels. |
drawZoneDebug | Colored fill cells over the zone grid + green/red hub-circle outlines keyed by illumination. |
CamView contract
Local interface — not exported. Required shape for both functions:
{ x, y, zoom, halfW, halfH }
x, y = camera center in world coords; zoom = world-to-screen scale; halfW, halfH = half viewport in pixels. Both functions transform world coords with (w - cam.x) * cam.zoom + cam.halfW (X) and the Y analog.
drawHubSpokeWireframe(ctx, ld, cam, visHubs)
Renders only hubs whose index appears in the visHubs: Set<number> arg; spokes draw if either endpoint is in visHubs. Reads ld.generation.hubs and ld.generation.spokes.
Palette: WIRE_COLOR = '#38bdf8' (sky-400), GLOW_COLOR = '#0ea5e9' (sky-500).
Spoke draw order (per spoke):
- Glow pass —
GLOW_COLOR, alpha 0.15, lineWidth6 * cam.zoom. - Core pass —
WIRE_COLOR, alpha 0.6, lineWidth1.5 * cam.zoom.
Hub draw order (per visible hub, with 50px off-screen cull margin):
- Outer glow — alpha 0.12, lineWidth
8 * cam.zoom. - Outer rim — alpha 0.7, lineWidth
2 * cam.zoom. - Concentric rings at 75%, 50%, 25% of radius — alpha 0.2, lineWidth
1 * cam.zoom. - Center crosshair — length
min(sr * 0.15, 20 * cam.zoom), alpha 0.8, lineWidth1.5 * cam.zoom. - Center dot — radius
3 * cam.zoom, alpha 0.9, filledWIRE_COLOR. - Hub-ID label — white, alpha 0.6, monospace
max(9, round(11 * cam.zoom))px, centered, offsetsr + 14 * cam.zoombelow center.
Resets globalAlpha = 1 then ctx.restore() at end.
drawZoneDebug(ctx, ld, cam)
Reads ld.zoneGrid (originX, originY, cellSize, cols, rows, grid as flat string[]) and ld.generation.hubs + ld.illuminationMap (Map<hubId, boolean>).
Zoom gate: early-returns if cam.zoom < 0.15 (illegible + expensive at far zoom).
Cell range computation: maps viewport bounds cam.x ± cam.halfW / cam.zoom (and Y analog) into grid col/row indices, clamped to [0, cols-1] / [0, rows-1].
Zone palette:
| Zone | Color |
|---|---|
wilds | #334155 (slate) |
hub_dark | #dc2626 (red) |
hub_lit | #16a34a (green) |
spoke_dark | #1d4ed8 (blue) |
spoke_lit | #7c3aed (purple) |
Unknown zone string falls back to #334155. Cells filled at globalAlpha = 0.25.
Hub outlines pass (alpha 0.5, lineWidth 2): stroked circle per hub at radius hub.r * cam.zoom. Color = green #16a34a if illuminationMap.get(hub.id) === true, else red #dc2626. Off-screen hubs (no margin) culled.
Side-effects
Both functions wrap their work in ctx.save() / ctx.restore() and reset globalAlpha. No state outside the passed CanvasRenderingContext2D. No allocations per call beyond local destructuring.
Callers
Playground/dev-mode renderer only. Not invoked from production game loop. Grep drawHubSpokeWireframe / drawZoneDebug to find dev gating.
Related
engine/world/chunk-manager.ts— owner ofLevelData,hubs,spokes,zoneGrid,illuminationMap.engine/world.md(wiki) — hub/spoke world generation overview.
EXTRACT-CANDIDATE
- World-to-screen transform
(w - cam.x) * cam.zoom + cam.halfWappears 6+ times across both functions and is duplicated by every other Canvas-2D renderer in the engine. Candidate for aworldToScreen(cam, x, y)helper or aproject(cam)returning a closure. - Off-screen cull check (
sx + sr < margin || sx - sr > cam.halfW*2 + margin) appears in both functions with slightly different margins (50 vs 0). Candidate forisCircleVisible(cam, sx, sy, sr, margin). ZONE_COLORSpalette is hardcoded inline; if zone semantics ever move (e.g., new zone types from bridge generator), production renderers will diverge from debug. Candidate for a sharedengine/world/zone-palette.tsconsumed by both.