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:

FunctionWhat it draws
drawHubSpokeWireframeBright blue glowing wireframe — hub concentric circles + spoke center lines + hub-ID labels.
drawZoneDebugColored 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):

  1. Glow pass — GLOW_COLOR, alpha 0.15, lineWidth 6 * cam.zoom.
  2. Core pass — WIRE_COLOR, alpha 0.6, lineWidth 1.5 * cam.zoom.

Hub draw order (per visible hub, with 50px off-screen cull margin):

  1. Outer glow — alpha 0.12, lineWidth 8 * cam.zoom.
  2. Outer rim — alpha 0.7, lineWidth 2 * cam.zoom.
  3. Concentric rings at 75%, 50%, 25% of radius — alpha 0.2, lineWidth 1 * cam.zoom.
  4. Center crosshair — length min(sr * 0.15, 20 * cam.zoom), alpha 0.8, lineWidth 1.5 * cam.zoom.
  5. Center dot — radius 3 * cam.zoom, alpha 0.9, filled WIRE_COLOR.
  6. Hub-ID label — white, alpha 0.6, monospace max(9, round(11 * cam.zoom))px, centered, offset sr + 14 * cam.zoom below 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:

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

  • engine/world/chunk-manager.ts — owner of LevelData, 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.halfW appears 6+ times across both functions and is duplicated by every other Canvas-2D renderer in the engine. Candidate for a worldToScreen(cam, x, y) helper or a project(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 for isCircleVisible(cam, sx, sy, sr, margin).
  • ZONE_COLORS palette 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 shared engine/world/zone-palette.ts consumed by both.