@seatlayer/core 0.29.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/view3d/index.ts","../../src/view3d/gl/context.ts","../../src/view3d/palette.ts","../../src/view3d/camera/orbit.ts","../../src/view3d/loop.ts","../../src/view3d/lod.ts","../../src/view3d/scene/geometry.ts","../../src/view3d/scene/seatInstances.ts","../../src/view3d/scene/sceneModel.ts","../../src/view3d/scene/build.ts","../../src/view3d/scene/materials.ts","../../src/view3d/pick/pickPipeline.ts","../../src/view3d/pick/encode.ts","../../src/view3d/pick/selection.ts","../../src/view3d/camera/cinematic.ts","../../src/view3d/camera/cinematicMath.ts","../../src/view3d/crossfade/panorama.ts","../../src/view3d/analytics.ts"],"sourcesContent":["/**\n * view3d — the sole dynamic-import boundary for the lazy OGL venue-view chunk.\n *\n * const { mountVenue3D } = await import('../view3d');\n * const handle = mountVenue3D(container, { doc, seats }, { onSeatPick, getSeatView });\n * await handle.flyToSeat(seatId);\n *\n * Read-only 3D of any chart, fed entirely from the existing height contract.\n * Slice 1: orbit camera, extruded tiers/stage/GA, instanced seat dots, sub-range\n * availability, dispose + context-loss survival. Slice 2: GPU color-pick. Slice\n * 3: the fly-to-seat cinematic that dissolves into the view-from-seat panorama.\n */\n\nimport { Quat, Vec3 } from 'ogl';\nimport type { ChartDoc, ExpandedSeat } from '../core/types';\nimport { GLContext } from './gl/context';\nimport { OrbitCamera } from './camera/orbit';\nimport { RenderLoop, type RenderLoopStats } from './loop';\nimport { computeSeatLod } from './lod';\nimport { buildSceneModel, type SceneModel } from './scene/sceneModel';\nimport { buildGpuScene, type GpuScene } from './scene/build';\nimport { applySeatStates } from './scene/seatInstances';\nimport { PickPipeline } from './pick/pickPipeline';\nimport { pickPixelCoords } from './pick/encode';\nimport { diffSelection, mergeAvailabilityIntoSelection } from './pick/selection';\nimport { Cinematic, buildWaypoints, lookAtQuat, FLIGHT_DURATION_MS, FOV_END, type Vec3Arr } from './camera/cinematic';\nimport { mountPanorama, type PanoramaHandle, type SeatView } from './crossfade/panorama';\nimport { Analytics3D, type Analytics3DCallback } from './analytics';\nimport type { SeatState3D } from './palette';\n\nexport type { SeatState3D } from './palette';\nexport type { SeatView } from './crossfade/panorama';\nexport type { Analytics3DCallback } from './analytics';\nexport { buildSceneModel } from './scene/sceneModel';\n\n/** Seat eye height above its deck: SEATED_EYE_HEIGHT_M (1.2) − seat lift (0.18). */\nconst SEAT_EYE_ABOVE_DECK = 1.02;\n\nexport interface Venue3DInput {\n doc: ChartDoc;\n /** Expanded seats (from `expandChart`) — carry x/y + resolved eyeHeightM. */\n seats: ExpandedSeat[];\n /** Optional initial per-seat state (default all available). */\n initialState?: (seat: ExpandedSeat) => SeatState3D;\n}\n\nexport interface Venue3DOptions {\n /** Fired on a tap that hits a seat (GPU color-pick). Not fired on empty taps. */\n onSeatPick?: (seatId: string) => void;\n /**\n * Supplies the view-from-seat panorama for the cinematic hand-off. Decoupled:\n * the caller (app/harness) owns panorama generation; view3d never imports it.\n * Called at PICK time to pre-render, so flyToSeat has zero wait on landing.\n */\n getSeatView?: (seatId: string) => SeatView | Promise<SeatView>;\n /**\n * Decoupled analytics sink. Emits the venue-view journey: `3d_opened`,\n * `3d_orbit_engaged` (first user gesture), `3d_seat_picked`,\n * `3d_cinematic_played`/`_skipped`/`_cancelled`, `3d_panorama_opened`/`_closed`.\n * Every invocation is wrapped in try/catch — a throwing sink never breaks\n * rendering. Absent = no events emitted.\n */\n onAnalytics?: Analytics3DCallback;\n}\n\nexport interface Venue3DStats extends RenderLoopStats {\n drawCalls: number;\n seatCount: number;\n}\n\nexport interface Venue3DHandle {\n dispose(): void;\n setAvailability(updates: { seatId: string; state: SeatState3D }[]): void;\n setSelection(seatIds: string[]): void;\n /** Fly the camera from the overview into `seatId` and dissolve into its\n * view-from-seat panorama. Resolves at flight end; a drag cancels it, a second\n * call retargets, dispose resolves early. Reduced-motion → a short fade. */\n flyToSeat(seatId: string): Promise<void>;\n resize(): void;\n stats(): Venue3DStats;\n loseContextForTest(): void;\n /** Test hook: force (or clear) the reduced-motion path. */\n setReducedMotionForTest(value: boolean | null): void;\n}\n\nconst DEG = Math.PI / 180;\nconst TAP_SLOP = 6;\nconst TAP_MS = 500;\n\nexport function mountVenue3D(\n container: HTMLElement,\n input: Venue3DInput,\n opts: Venue3DOptions = {},\n): Venue3DHandle {\n const model: SceneModel = buildSceneModel(input);\n const analytics = new Analytics3D(opts.onAnalytics);\n\n const seatIdByIndex: string[] = new Array(model.seats.count);\n for (const [id, idx] of model.seats.idToIndex) seatIdByIndex[idx] = id;\n\n // Seat → owning section (for the 3d_seat_picked event); resolved from the\n // expanded seats that already carry sectionId.\n const sectionIdBySeatId = new Map<string, string | undefined>();\n for (const s of input.seats) sectionIdBySeatId.set(s.id, s.sectionId);\n\n // Whether the chart carries any real 3D relief (authored heights/rake or\n // elevated floors) vs. degrading to flat slabs — reported with 3d_opened.\n const hasHeights = ((): boolean => {\n if (input.doc.floors?.some((f) => (f.baseHeightM ?? 0) > 0)) return true;\n const objs = input.doc.floors?.flatMap((f) => f.objects) ?? input.doc.objects;\n return objs.some((o) => o.type === 'section'\n && (((o as { height?: number }).height ?? 0) > 0 || ((o as { rake?: number }).rake ?? 0) > 0));\n })();\n\n let gpu: GpuScene | null = null;\n let pick: PickPipeline | null = null;\n let contextLost = false;\n let frozen = false; // GL render paused while the panorama is up\n let disposed = false;\n let selection = new Map<string, number>();\n let panorama: PanoramaHandle | null = null;\n const prefetch = new Map<string, Promise<SeatView>>();\n let flightGen = 0;\n let reducedForced: boolean | null = null;\n\n const rebuildGpu = (): void => {\n gpu = buildGpuScene(glctx.gl, model);\n pick = new PickPipeline(glctx.renderer, gpu.seatGeometry, gpu.solidGeometry, model.seats.count);\n };\n\n const glctx = new GLContext(container, {\n onContextLost: () => {\n contextLost = true;\n loop.stop();\n gpu = null;\n pick = null;\n },\n onContextRestored: () => {\n rebuildGpu();\n contextLost = false;\n loop.requestRender();\n },\n });\n\n const orbit = new OrbitCamera(\n glctx.gl,\n glctx.canvas,\n () => loop.requestRender(),\n () => analytics.orbitEngaged(), // first real drag/wheel/pinch (not the intro ease)\n );\n // setAspect BEFORE frame so the fit clears the horizontal FOV too (centred with\n // margin on a wide designer canvas rather than parked low-left).\n orbit.setAspect(glctx.aspect);\n orbit.frame(model.bounds, true);\n\n const cinematic = new Cinematic(orbit.camera);\n\n rebuildGpu();\n\n const loop = new RenderLoop((/* dt */) => {\n if (contextLost || !gpu || frozen) return false;\n const flying = cinematic.active;\n const moving = flying ? cinematic.update(performance.now()) : orbit.update();\n\n const lod = computeSeatLod(orbit.currentDistance, model.bounds.radius);\n const u = gpu.seatProgram.uniforms;\n u.uSeatScale.value = lod.scale;\n u.uSeatFade.value = lod.fade;\n u.uPixelToWorld.value = (2 * Math.tan((orbit.camera.fov * DEG) / 2)) / Math.max(1, glctx.pixelHeight);\n\n glctx.renderer.render({ scene: gpu.background, clear: true });\n glctx.renderer.render({ scene: gpu.main, camera: orbit.camera, clear: false });\n return moving;\n });\n\n const ro = typeof ResizeObserver !== 'undefined'\n ? new ResizeObserver(() => handle.resize())\n : null;\n ro?.observe(container);\n\n const setSelection = (ids: string[]): void => {\n const baseStateIndex = (id: string): number | undefined => {\n const idx = model.seats.idToIndex.get(id);\n return idx === undefined ? undefined : model.seats.iState[idx];\n };\n const { updates, next } = diffSelection(selection, ids, baseStateIndex);\n selection = next;\n if (updates.length) {\n const runs = applySeatStates(model.seats, updates);\n if (gpu) gpu.uploadSeatStateRuns(runs);\n loop.requestRender();\n }\n };\n\n // --- cinematic / panorama ---\n const reducedMotion = (): boolean => {\n if (reducedForced !== null) return reducedForced;\n return typeof window !== 'undefined' && !!window.matchMedia\n && window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n };\n\n const PREFETCH_CAP = 8;\n const ensureSeatView = (seatId: string): Promise<SeatView> | null => {\n if (!opts.getSeatView) return null;\n let p = prefetch.get(seatId);\n if (!p) {\n p = Promise.resolve(opts.getSeatView(seatId));\n prefetch.set(seatId, p);\n // Bound the cache (LRU-ish): drop the oldest inserted entries past the cap.\n while (prefetch.size > PREFETCH_CAP) {\n const oldest = prefetch.keys().next().value as string | undefined;\n if (oldest === undefined) break;\n prefetch.delete(oldest);\n }\n }\n return p;\n };\n\n const seatEyeWorld = (idx: number): Vec3Arr => [\n model.seats.iPosition[idx * 3],\n model.seats.iPosition[idx * 3 + 1] + SEAT_EYE_ABOVE_DECK,\n model.seats.iPosition[idx * 3 + 2],\n ];\n\n const placeCameraFinal = (finalPos: Vec3Arr, focal: Vec3Arr): void => {\n orbit.camera.position.set(finalPos[0], finalPos[1], finalPos[2]);\n orbit.camera.lookAt(new Vec3(focal[0], focal[1], focal[2]));\n orbit.camera.fov = FOV_END;\n orbit.camera.updateProjectionMatrix();\n };\n\n const openPanorama = async (seatId: string, fadeMs: number, gen: number): Promise<void> => {\n const viewPromise = ensureSeatView(seatId);\n if (!viewPromise) { orbit.syncFromCamera(); return; } // no panorama source\n frozen = true;\n loop.stop(); // freeze the GL at the seat pose; panorama fades in over it\n let view: SeatView;\n try {\n view = await viewPromise;\n } catch {\n // Only unfreeze if we still own the flight — a retarget during the await\n // has already reset `frozen` and taken over the loop.\n if (!disposed && gen === flightGen) { frozen = false; orbit.resumeAfterFlight(model.focalWorld); loop.requestRender(); }\n return;\n }\n // Superseded during the await (retarget/cancel) or disposed: bail WITHOUT\n // touching frozen/loop — the newer flight owns the freeze state now, and\n // mounting this stale seat's panorama would be wrong.\n if (disposed || gen !== flightGen) return;\n panorama = mountPanorama(container, view, {\n fadeMs,\n seatLabel: seatId,\n onClose: () => {\n panorama = null;\n frozen = false;\n analytics.panoramaClosed();\n orbit.resumeAfterFlight(model.focalWorld);\n loop.requestRender();\n },\n });\n analytics.panoramaOpened();\n };\n\n const cancelFlight = (): void => {\n flightGen++; // supersede any pending .then(openPanorama)\n if (cinematic.active) {\n cinematic.cancel();\n orbit.resumeAfterFlight(model.focalWorld);\n }\n };\n\n const flyToSeat = (seatId: string): Promise<void> => {\n if (disposed || !gpu) return Promise.resolve();\n const idx = model.seats.idToIndex.get(seatId);\n if (idx === undefined) return Promise.resolve();\n // Reset the freeze unconditionally: a previous flight may have set frozen=true\n // inside openPanorama's pre-await window without a panorama ever mounting.\n if (panorama) { panorama.dispose(); panorama = null; }\n frozen = false;\n\n const gen = ++flightGen;\n const seatEye = seatEyeWorld(idx);\n const focal = model.focalWorld;\n const start: Vec3Arr = [orbit.camera.position.x, orbit.camera.position.y, orbit.camera.position.z];\n const { waypoints, finalPos } = buildWaypoints(start, seatEye, focal, model.bounds.center, model.bounds.radius);\n\n if (reducedMotion()) {\n // a11y: no flight — snap to the seat pose, short dissolve to the panorama.\n placeCameraFinal(finalPos, focal);\n loop.requestRender();\n analytics.cinematicSkipped();\n return openPanorama(seatId, 300, gen).then(() => { if (!disposed) orbit.syncFromCamera(); });\n }\n\n const startQuat = new Quat().copy(orbit.camera.quaternion);\n const endQuat = lookAtQuat(orbit.camera, finalPos, focal);\n loop.requestRender();\n return cinematic.start(waypoints, startQuat, endQuat).then(() => {\n if (disposed || gen !== flightGen) return; // disposed or superseded (retarget/cancel)\n analytics.cinematicPlayed(FLIGHT_DURATION_MS);\n return openPanorama(seatId, 400, gen);\n });\n };\n\n // --- Tap → pick / flight-cancel ---\n let downX = 0, downY = 0, downT = 0, downId = -1, moved = false, suppressTap = false;\n const onDown = (e: PointerEvent): void => {\n if (downId !== -1) return;\n downId = e.pointerId; downX = e.clientX; downY = e.clientY; downT = performance.now(); moved = false;\n // A press during a flight cancels it (damped stop) instead of picking.\n suppressTap = cinematic.active;\n if (cinematic.active) { analytics.cinematicCancelled(); cancelFlight(); }\n };\n const onMove = (e: PointerEvent): void => {\n if (e.pointerId !== downId) return;\n if (Math.hypot(e.clientX - downX, e.clientY - downY) > TAP_SLOP) moved = true;\n };\n const onUp = (e: PointerEvent): void => {\n if (e.pointerId !== downId) return;\n const isTap = !moved && performance.now() - downT < TAP_MS;\n downId = -1;\n if (suppressTap) { suppressTap = false; return; }\n if (!isTap || !gpu || !pick) return;\n pick.syncFromSeatProgram(gpu.seatProgram);\n const rect = glctx.canvas.getBoundingClientRect();\n const dpr = glctx.renderer.dpr;\n const { x, y } = pickPixelCoords(e.clientX, e.clientY, rect, dpr, glctx.gl.drawingBufferWidth, glctx.gl.drawingBufferHeight);\n const radius = Math.max(2, Math.round(8 * dpr));\n const idx = pick.pick(orbit.camera, x, y, radius);\n if (idx < 0 || idx >= seatIdByIndex.length) {\n if (selection.size) setSelection([]);\n return;\n }\n const seatId = seatIdByIndex[idx];\n if (selection.has(seatId) && selection.size === 1) setSelection([]);\n else setSelection([seatId]);\n ensureSeatView(seatId); // pre-render the panorama the moment the seat is picked\n analytics.seatPicked(seatId, sectionIdBySeatId.get(seatId));\n opts.onSeatPick?.(seatId);\n };\n glctx.canvas.addEventListener('pointerdown', onDown);\n glctx.canvas.addEventListener('pointermove', onMove);\n glctx.canvas.addEventListener('pointerup', onUp);\n glctx.canvas.addEventListener('pointercancel', onUp);\n\n loop.requestRender();\n\n const handle: Venue3DHandle = {\n setAvailability(updates) {\n const passthrough = mergeAvailabilityIntoSelection(selection, updates);\n const runs = applySeatStates(model.seats, passthrough);\n if (runs.length && gpu) gpu.uploadSeatStateRuns(runs);\n loop.requestRender();\n },\n setSelection,\n flyToSeat,\n resize() {\n const { width, height } = glctx.resize();\n orbit.setAspect(width / Math.max(1, height));\n loop.requestRender();\n },\n stats() {\n return {\n ...loop.stats(),\n drawCalls: gpu ? gpu.drawCalls : 0,\n seatCount: model.seatCount,\n };\n },\n loseContextForTest() {\n glctx.simulateContextLossCycle();\n },\n setReducedMotionForTest(value) {\n reducedForced = value;\n },\n dispose() {\n disposed = true;\n cancelFlight();\n loop.stop();\n ro?.disconnect();\n if (panorama) { panorama.dispose(); panorama = null; }\n glctx.canvas.removeEventListener('pointerdown', onDown);\n glctx.canvas.removeEventListener('pointermove', onMove);\n glctx.canvas.removeEventListener('pointerup', onUp);\n glctx.canvas.removeEventListener('pointercancel', onUp);\n orbit.dispose();\n if (pick) pick.dispose();\n if (gpu) gpu.dispose();\n gpu = null;\n pick = null;\n glctx.dispose();\n },\n };\n\n analytics.opened(model.seatCount, hasHeights);\n return handle;\n}\n","/**\n * OGL renderer + canvas lifecycle. Owns the single WebGL2 context (reused across\n * open/close so an embed never exhausts the browser's ~16-context cap), DPR\n * capping, resize, and WebGL context-loss survival.\n *\n * Context loss is handled by preventing the default (so the browser will restore)\n * and delegating rebuild to the caller: the JS-side SceneModel is the source of\n * truth, so `onContextRestored` re-uploads all GPU resources from it.\n */\n\nimport { Renderer } from 'ogl';\nimport type { OGLRenderingContext } from 'ogl';\nimport { BACKGROUND } from '../palette';\n\nexport interface GLContextOptions {\n onContextLost: () => void;\n onContextRestored: () => void;\n}\n\n/** DPR ceiling: 2.0, or 1.5 on low-memory devices (fragment cost is DPR²). */\nfunction computeDpr(): number {\n const raw = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n const mem = (navigator as unknown as { deviceMemory?: number }).deviceMemory;\n const cap = typeof mem === 'number' && mem <= 4 ? 1.5 : 2.0;\n return Math.min(raw, cap);\n}\n\nexport class GLContext {\n readonly renderer: Renderer;\n readonly gl: OGLRenderingContext;\n readonly canvas: HTMLCanvasElement;\n private container: HTMLElement;\n private lostHandler: (e: Event) => void;\n private restoredHandler: () => void;\n\n constructor(container: HTMLElement, opts: GLContextOptions) {\n this.container = container;\n this.canvas = document.createElement('canvas');\n this.canvas.style.display = 'block';\n this.canvas.style.width = '100%';\n this.canvas.style.height = '100%';\n this.canvas.style.touchAction = 'none';\n\n this.renderer = new Renderer({\n canvas: this.canvas,\n dpr: computeDpr(),\n alpha: false,\n antialias: false,\n depth: true,\n stencil: false,\n powerPreference: 'high-performance',\n webgl: 2,\n });\n this.gl = this.renderer.gl;\n this.gl.clearColor(BACKGROUND.top[0], BACKGROUND.top[1], BACKGROUND.top[2], 1);\n\n container.appendChild(this.canvas);\n\n this.lostHandler = (e: Event) => {\n e.preventDefault();\n opts.onContextLost();\n };\n this.restoredHandler = () => opts.onContextRestored();\n this.canvas.addEventListener('webglcontextlost', this.lostHandler, false);\n this.canvas.addEventListener('webglcontextrestored', this.restoredHandler, false);\n\n this.resize();\n }\n\n /** Match the drawing buffer to the container's CSS box. */\n resize(): { width: number; height: number } {\n const w = Math.max(1, this.container.clientWidth || this.canvas.clientWidth || 1);\n const h = Math.max(1, this.container.clientHeight || this.canvas.clientHeight || 1);\n this.renderer.setSize(w, h);\n return { width: w, height: h };\n }\n\n get pixelHeight(): number {\n return this.renderer.height * this.renderer.dpr;\n }\n\n get aspect(): number {\n return this.renderer.width / Math.max(1, this.renderer.height);\n }\n\n dispose(): void {\n this.canvas.removeEventListener('webglcontextlost', this.lostHandler, false);\n this.canvas.removeEventListener('webglcontextrestored', this.restoredHandler, false);\n const ext = this.gl.getExtension('WEBGL_lose_context');\n if (ext) ext.loseContext();\n if (this.canvas.parentNode) this.canvas.parentNode.removeChild(this.canvas);\n }\n\n /**\n * Test hook: force a full loss→restore cycle. `restoreContext()` must be called\n * only AFTER the browser has dispatched `webglcontextlost` (calling it too soon\n * makes the browser drop the restore request), so we sequence it off a one-shot\n * listener rather than a fixed timeout.\n */\n simulateContextLossCycle(): void {\n const ext = this.gl.getExtension('WEBGL_lose_context') as\n | { loseContext(): void; restoreContext?: () => void }\n | null;\n if (!ext) return;\n ext.loseContext();\n // Chrome drops a restore requested too soon after loseContext(); a short\n // delay lets the loss settle before we ask for the context back.\n setTimeout(() => { if (ext.restoreContext) ext.restoreContext(); }, 300);\n }\n}\n","/**\n * view3d palette + seat-state model — the single source of colour truth for the\n * OGL venue view. Pure data (no GPU, no DOM) so the scene builder and the unit\n * tests share one definition. Colours are linear-ish RGB triplets in 0..1.\n *\n * Look brief (docs/3d-usp-strategy §3): desaturated cool greys for structure,\n * one warm accent for the stage, availability colours only on seats.\n */\n\nexport type SeatState3D = 'available' | 'held' | 'sold' | 'selected' | 'dimmed';\n\n/** Fixed LUT order — the per-instance `iState` float indexes this array, and the\n * fragment shader's `uStateColors` uniform is uploaded in exactly this order. */\nexport const SEAT_STATES: SeatState3D[] = ['available', 'held', 'sold', 'selected', 'dimmed'];\n\nexport function seatStateIndex(state: SeatState3D): number {\n const i = SEAT_STATES.indexOf(state);\n return i < 0 ? 0 : i;\n}\n\nexport type RGB = [number, number, number];\n\n/** Availability colours — the only saturated colours in the scene. */\nexport const SEAT_STATE_COLORS: Record<SeatState3D, RGB> = {\n available: [0.24, 0.82, 0.52],\n held: [0.95, 0.66, 0.22],\n sold: [0.34, 0.39, 0.45],\n selected: [0.24, 0.74, 1.0],\n dimmed: [0.28, 0.32, 0.37],\n};\n\n/** Flat LUT (5 × vec3) for the seat fragment shader uniform. */\nexport function seatStateColorLUT(): number[] {\n const out: number[] = [];\n for (const s of SEAT_STATES) out.push(...SEAT_STATE_COLORS[s]);\n return out;\n}\n\n/** Colour for a state index (from `iState`) — used to fill the per-instance\n * `iColor` attribute CPU-side, avoiding a dynamically-indexed array uniform. */\nexport function seatStateColorByIndex(index: number): RGB {\n const state = SEAT_STATES[index] ?? 'available';\n return SEAT_STATE_COLORS[state];\n}\n\n/** Structure palette — cool desaturated greys + one warm stage accent. */\nexport const STRUCTURE = {\n ground: [0.07, 0.085, 0.11] as RGB,\n tierTop: [0.24, 0.28, 0.34] as RGB,\n tierWall: [0.17, 0.20, 0.25] as RGB,\n stageTop: [0.42, 0.36, 0.26] as RGB, // warm, slightly emissive read\n stageWall: [0.26, 0.22, 0.16] as RGB,\n decorTop: [0.22, 0.25, 0.29] as RGB,\n decorWall: [0.15, 0.17, 0.20] as RGB,\n gaTop: [0.24, 0.28, 0.33] as RGB,\n gaWall: [0.16, 0.19, 0.23] as RGB,\n} as const;\n\n/** Background vertical gradient (matches the app's dark UI). */\nexport const BACKGROUND = {\n top: [0.05, 0.06, 0.08] as RGB,\n bottom: [0.10, 0.12, 0.15] as RGB,\n};\n\n/** Parse `#rrggbb` (or `#rgb`) to linear-ish 0..1 RGB; null on anything else. */\nexport function hexToRgb(hex: string | undefined): RGB | null {\n if (!hex) return null;\n let h = hex.trim();\n if (h[0] === '#') h = h.slice(1);\n if (h.length === 3) h = h.split('').map((c) => c + c).join('');\n if (h.length !== 6 || /[^0-9a-fA-F]/.test(h)) return null;\n const n = parseInt(h, 16);\n return [((n >> 16) & 255) / 255, ((n >> 8) & 255) / 255, (n & 255) / 255];\n}\n\n/** Mix two colours (a*(1-t) + b*t). */\nexport function mix(a: RGB, b: RGB, t: number): RGB {\n return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t];\n}\n\n/** Desaturate toward its own luma by `amount` (0 = unchanged, 1 = grey). */\nexport function desaturate(c: RGB, amount: number): RGB {\n const l = 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2];\n return mix(c, [l, l, l], amount);\n}\n\n/** Scale a colour by a scalar (baked vertex AO), clamped to [0,1]. */\nexport function scaleRgb(c: RGB, k: number): RGB {\n return [Math.min(1, c[0] * k), Math.min(1, c[1] * k), Math.min(1, c[2] * k)];\n}\n","/**\n * Orbit + dolly camera, damped, always centred on the venue focal point. Drag =\n * azimuth/polar; wheel/pinch = dolly. Touch: 1-finger orbit, 2-finger pinch\n * dolly (+ pan). No desktop pan for v1. Polar clamped [15°,80°]; distance clamped\n * to bounds-derived limits; initial framing = a 3/4 view fitted to bounds.\n */\n\nimport { Camera, Vec3 } from 'ogl';\nimport type { OGLRenderingContext } from 'ogl';\n\nconst DEG = Math.PI / 180;\nconst POLAR_MIN = 15 * DEG;\nconst POLAR_MAX = 80 * DEG;\nconst DAMP = 0.12;\nconst FOV = 35;\n/** Fit multiplier past a tight bounds-sphere fit. The 3/4 tilt makes the near\n * ground edge overhang below the fitted sphere, so a wide-shallow layout needs\n * more than a nominal 10% or its front row clips — this clears it while keeping\n * the venue centred with a comfortable margin. */\nconst FRAME_MARGIN = 1.25;\n\nexport interface OrbitBounds {\n center: [number, number, number];\n radius: number;\n}\n\nexport class OrbitCamera {\n readonly camera: Camera;\n readonly fovY = FOV;\n private target = new Vec3();\n private azimuth = -30 * DEG;\n private polar = 55 * DEG;\n private distance = 10;\n private azT = -30 * DEG;\n private polT = 55 * DEG;\n private distT = 10;\n private minDist = 1;\n private maxDist = 100;\n private canvas: HTMLElement;\n private requestRender: () => void;\n /** Fired on the FIRST real user-driven orbit/dolly gesture (drag/wheel/pinch),\n * latched so it can drive a one-shot analytics event. Not the intro ease. */\n private onGesture?: () => void;\n private gestureFired = false;\n\n private dragging = false;\n private lastX = 0;\n private lastY = 0;\n private activePointers = new Map<number, { x: number; y: number }>();\n private pinchDist = 0;\n\n private onPointerDown: (e: PointerEvent) => void;\n private onPointerMove: (e: PointerEvent) => void;\n private onPointerUp: (e: PointerEvent) => void;\n private onWheel: (e: WheelEvent) => void;\n\n constructor(gl: OGLRenderingContext, canvas: HTMLElement, requestRender: () => void, onGesture?: () => void) {\n this.camera = new Camera(gl, { fov: FOV, near: 0.1, far: 5000, aspect: 1 });\n this.canvas = canvas;\n this.requestRender = requestRender;\n this.onGesture = onGesture;\n\n this.onPointerDown = (e) => {\n // Guard: a synthetic/stale pointer id has no active pointer to capture.\n try { this.canvas.setPointerCapture?.(e.pointerId); } catch { /* no active pointer */ }\n this.activePointers.set(e.pointerId, { x: e.clientX, y: e.clientY });\n if (this.activePointers.size === 1) {\n this.dragging = true;\n this.lastX = e.clientX;\n this.lastY = e.clientY;\n } else if (this.activePointers.size === 2) {\n this.dragging = false;\n this.pinchDist = this.currentPinchDistance();\n }\n };\n this.onPointerMove = (e) => {\n if (!this.activePointers.has(e.pointerId)) return;\n this.activePointers.set(e.pointerId, { x: e.clientX, y: e.clientY });\n if (this.activePointers.size >= 2) {\n const d = this.currentPinchDistance();\n // Fingers apart (d grows) → zoom in (distance shrinks).\n if (this.pinchDist > 0) { this.dollyBy(Math.exp((this.pinchDist - d) * 0.005)); this.fireGesture(); }\n this.pinchDist = d;\n return;\n }\n if (!this.dragging) return;\n const dx = e.clientX - this.lastX;\n const dy = e.clientY - this.lastY;\n this.lastX = e.clientX;\n this.lastY = e.clientY;\n if (dx !== 0 || dy !== 0) this.fireGesture();\n this.azT -= dx * 0.006;\n this.polT = Math.max(POLAR_MIN, Math.min(POLAR_MAX, this.polT - dy * 0.006));\n this.requestRender();\n };\n this.onPointerUp = (e) => {\n this.activePointers.delete(e.pointerId);\n try { this.canvas.releasePointerCapture?.(e.pointerId); } catch { /* no active pointer */ }\n if (this.activePointers.size < 2) this.pinchDist = 0;\n if (this.activePointers.size === 0) this.dragging = false;\n };\n this.onWheel = (e) => {\n e.preventDefault();\n // Normalise wheel delta across px / line / page modes to ~±1 per notch,\n // then zoom multiplicatively so every notch makes a real difference.\n const unit = e.deltaMode === 1 ? 16 : e.deltaMode === 2 ? 100 : 1;\n const norm = (e.deltaY * unit) / 100;\n this.dollyBy(Math.exp(norm * 0.4));\n this.fireGesture();\n };\n\n canvas.addEventListener('pointerdown', this.onPointerDown);\n canvas.addEventListener('pointermove', this.onPointerMove);\n canvas.addEventListener('pointerup', this.onPointerUp);\n canvas.addEventListener('pointercancel', this.onPointerUp);\n canvas.addEventListener('wheel', this.onWheel, { passive: false });\n }\n\n /** One-shot: notify the first real user gesture (drives 3d_orbit_engaged). */\n private fireGesture(): void {\n if (this.gestureFired) return;\n this.gestureFired = true;\n this.onGesture?.();\n }\n\n private currentPinchDistance(): number {\n const pts = [...this.activePointers.values()];\n if (pts.length < 2) return 0;\n return Math.hypot(pts[0].x - pts[1].x, pts[0].y - pts[1].y);\n }\n\n /** Multiply target distance by `factor` (proportional zoom — same feel near\n * and far), clamped so you can swoop right down among the seats. */\n private dollyBy(factor: number): void {\n this.distT = Math.max(this.minDist, Math.min(this.maxDist, this.distT * factor));\n this.requestRender();\n }\n\n /**\n * Fit a flattering 3/4 view to the bounds sphere. With `intro`, the camera\n * STARTS nearly top-down (matching the 2D map's orientation) and further out,\n * then the damped `update()` eases it up into the 3/4 architectural angle and\n * dollies in — the venue \"stands up\" instead of teleporting (~600ms).\n */\n frame(bounds: OrbitBounds, intro = false): void {\n this.target.set(bounds.center[0], bounds.center[1], bounds.center[2]);\n const r = Math.max(1, bounds.radius);\n // Aspect-aware fit: the bounds sphere must clear BOTH the vertical and the\n // (aspect-narrowed) horizontal FOV, so a wide designer canvas frames the\n // chart centred with margin instead of parking it low-left. `setAspect` must\n // run before `frame` for the horizontal term to be correct.\n const halfV = (this.fovY * DEG) / 2;\n const aspect = this.camera.aspect || 1;\n const halfH = Math.atan(Math.tan(halfV) * aspect);\n const fit = Math.max(r / Math.tan(halfV), r / Math.tan(halfH));\n this.azT = -30 * DEG;\n this.polT = 55 * DEG;\n this.distT = fit * FRAME_MARGIN;\n // Low min so you can swoop down close enough that seat dots are big, tappable\n // targets (\"into your section\"); generous max to pull right back out.\n this.minDist = Math.max(2, r * 0.12);\n this.maxDist = fit * 4;\n if (intro) {\n this.azimuth = this.azT; // no spin — just tilt up + dolly in\n this.polar = 12 * DEG; // near top-down, like the flat 2D view\n this.distance = this.distT * 1.7;\n } else {\n this.azimuth = this.azT;\n this.polar = this.polT;\n this.distance = this.distT;\n }\n this.applyPosition();\n }\n\n setAspect(aspect: number): void {\n this.camera.perspective({ aspect });\n }\n\n /** Damp toward targets; returns true while still moving. */\n update(): boolean {\n const da = this.azT - this.azimuth;\n const dp = this.polT - this.polar;\n const dd = this.distT - this.distance;\n const moving = Math.abs(da) > 1e-4 || Math.abs(dp) > 1e-4 || Math.abs(dd) > 1e-4;\n this.azimuth += da * DAMP;\n this.polar += dp * DAMP;\n this.distance += dd * DAMP;\n if (moving) this.applyPosition();\n return moving;\n }\n\n /** Distance from camera to target (for LOD). */\n get currentDistance(): number {\n return this.distance;\n }\n\n /**\n * Re-derive the orbit's spherical state from the camera's CURRENT pose (after a\n * cinematic flight leaves it somewhere arbitrary), so a subsequent drag damps\n * from where it actually is with no snap. Does not move the camera.\n */\n syncFromCamera(): void {\n const dx = this.camera.position.x - this.target.x;\n const dy = this.camera.position.y - this.target.y;\n const dz = this.camera.position.z - this.target.z;\n const dist = Math.hypot(dx, dy, dz) || 1;\n const polar = Math.max(POLAR_MIN, Math.min(POLAR_MAX, Math.acos(Math.max(-1, Math.min(1, dy / dist)))));\n // Distance is NOT clamped here: a flight can park closer than minDist, and\n // clamping would jump the camera radially on the very first drag. The clamp\n // applies lazily from the next user-driven dolly (see dollyBy).\n this.distance = this.distT = dist;\n this.polar = this.polT = polar;\n this.azimuth = this.azT = Math.atan2(dx, dz);\n }\n\n /** Point the orbit pivot at a new world target without moving the camera. */\n setTarget(target: [number, number, number]): void {\n this.target.set(target[0], target[1], target[2]);\n }\n\n /** Restore the base FOV (a flight ends pushed-in) and re-sync orbit state. A\n * flight ends looking at `target` (the venue focal), so re-pivot there first —\n * otherwise the first drag would `lookAt(bounds.center)` and pop the aim. */\n resumeAfterFlight(target?: [number, number, number]): void {\n this.camera.perspective({ fov: this.fovY, aspect: this.camera.aspect });\n if (target) this.target.set(target[0], target[1], target[2]);\n this.syncFromCamera();\n }\n\n private applyPosition(): void {\n const sp = Math.sin(this.polar);\n const x = this.target.x + this.distance * sp * Math.sin(this.azimuth);\n const y = this.target.y + this.distance * Math.cos(this.polar);\n const z = this.target.z + this.distance * sp * Math.cos(this.azimuth);\n this.camera.position.set(x, y, z);\n this.camera.lookAt(this.target);\n }\n\n dispose(): void {\n this.canvas.removeEventListener('pointerdown', this.onPointerDown);\n this.canvas.removeEventListener('pointermove', this.onPointerMove);\n this.canvas.removeEventListener('pointerup', this.onPointerUp);\n this.canvas.removeEventListener('pointercancel', this.onPointerUp);\n this.canvas.removeEventListener('wheel', this.onWheel);\n this.activePointers.clear();\n }\n}\n","/**\n * Dirty-flag render loop. A frame is drawn only while the camera is moving /\n * damping or an availability update arrived; when idle, no rAF is scheduled at\n * all (zero CPU/GPU when parked). Tracks an FPS EMA over frames actually\n * rendered — it reads as \"idle\" when nothing is scheduled.\n */\n\nexport interface RenderLoopStats {\n fps: number;\n rendered: number;\n idle: boolean;\n}\n\nexport class RenderLoop {\n private frame: (dt: number) => boolean;\n private rafId = 0;\n private running = false;\n private lastTime = 0;\n private fpsEma = 0;\n private rendered = 0;\n\n /** `frame(dt)` renders one frame and returns true if another is needed. */\n constructor(frame: (dt: number) => boolean) {\n this.frame = frame;\n }\n\n requestRender(): void {\n if (this.running) return;\n this.running = true;\n this.lastTime = 0;\n this.rafId = requestAnimationFrame(this.tick);\n }\n\n private tick = (now: number): void => {\n const dt = this.lastTime ? (now - this.lastTime) / 1000 : 1 / 60;\n this.lastTime = now;\n if (dt > 0) {\n const instFps = 1 / dt;\n this.fpsEma = this.fpsEma ? this.fpsEma * 0.9 + instFps * 0.1 : instFps;\n }\n this.rendered++;\n const again = this.frame(dt);\n if (again) {\n this.rafId = requestAnimationFrame(this.tick);\n } else {\n this.running = false;\n this.rafId = 0;\n }\n };\n\n stats(): RenderLoopStats {\n return { fps: this.running ? Math.round(this.fpsEma) : 0, rendered: this.rendered, idle: !this.running };\n }\n\n stop(): void {\n if (this.rafId) cancelAnimationFrame(this.rafId);\n this.rafId = 0;\n this.running = false;\n }\n}\n","/**\n * Distance-based seat level-of-detail (v1). Beyond a bounds-derived threshold the\n * dots shrink and fade toward the tier colour (both uniform-driven, no geometry\n * change); below it they stay full. The POINTS fallback rung can come later.\n */\n\nexport interface SeatLod {\n /** Multiplier on the seat world radius (1 = full). */\n scale: number;\n /** Fade toward the tier/fade colour (0 = pure state colour). */\n fade: number;\n}\n\nexport function computeSeatLod(distance: number, radius: number): SeatLod {\n const near = radius * 1.4;\n const far = radius * 3.2;\n if (distance <= near) return { scale: 1, fade: 0 };\n const t = Math.min(1, (distance - near) / Math.max(1e-3, far - near));\n return {\n scale: 1 - t * 0.4,\n fade: t * 0.55,\n };\n}\n","/**\n * Pure geometry primitives for the view3d scene — no OGL, no DOM, so the whole\n * scene-model builder is unit-testable in a plain runtime.\n *\n * Coordinate convention: chart units (x, y) map to world metres as\n * worldX = x * METRES_PER_CHART_UNIT\n * worldZ = y * METRES_PER_CHART_UNIT\n * worldY = up (height in metres)\n * i.e. the chart's audience-depth (+y) becomes world +Z, and Y is the vertical.\n */\n\nimport earcut from 'earcut';\nimport type { Point } from '../../core/types';\nimport { METRES_PER_CHART_UNIT } from '../../core/units';\nimport type { RGB } from '../palette';\n\nexport const M = METRES_PER_CHART_UNIT;\n\nexport interface MeshData {\n /** Non-indexed triangle soup: 3 floats per vertex. */\n position: Float32Array;\n normal: Float32Array;\n /** Baked vertex colour incl. AO, 3 floats per vertex. */\n color: Float32Array;\n /** Vertex count (position.length / 3). */\n count: number;\n}\n\n/** Accumulates flat-shaded, per-vertex-coloured triangles. */\nexport class MeshBuilder {\n private pos: number[] = [];\n private nor: number[] = [];\n private col: number[] = [];\n\n /** One triangle with a shared (flat) normal and per-vertex colours. */\n tri(\n p0: readonly [number, number, number],\n p1: readonly [number, number, number],\n p2: readonly [number, number, number],\n n: readonly [number, number, number],\n c0: RGB,\n c1: RGB = c0,\n c2: RGB = c0,\n ): void {\n this.pos.push(p0[0], p0[1], p0[2], p1[0], p1[1], p1[2], p2[0], p2[1], p2[2]);\n this.nor.push(n[0], n[1], n[2], n[0], n[1], n[2], n[0], n[1], n[2]);\n this.col.push(c0[0], c0[1], c0[2], c1[0], c1[1], c1[2], c2[0], c2[1], c2[2]);\n }\n\n get vertexCount(): number {\n return this.pos.length / 3;\n }\n\n build(): MeshData {\n return {\n position: new Float32Array(this.pos),\n normal: new Float32Array(this.nor),\n color: new Float32Array(this.col),\n count: this.pos.length / 3,\n };\n }\n}\n\n/** Face normal of a triangle (right-handed). */\nexport function faceNormal(\n a: readonly [number, number, number],\n b: readonly [number, number, number],\n c: readonly [number, number, number],\n): [number, number, number] {\n const ux = b[0] - a[0], uy = b[1] - a[1], uz = b[2] - a[2];\n const vx = c[0] - a[0], vy = c[1] - a[1], vz = c[2] - a[2];\n let nx = uy * vz - uz * vy;\n let ny = uz * vx - ux * vz;\n let nz = ux * vy - uy * vx;\n const len = Math.hypot(nx, ny, nz) || 1;\n nx /= len; ny /= len; nz /= len;\n return [nx, ny, nz];\n}\n\nexport interface Triangulation {\n /** Outline points followed by every hole's points, in order. */\n pts: Point[];\n /** Triangle vertex indices into `pts` (length is a multiple of 3). */\n tris: number[];\n}\n\n/** Triangulate a closed polygon with optional holes via earcut. */\nexport function triangulate(outline: Point[], holes?: Point[][]): Triangulation {\n const pts: Point[] = [...outline];\n const flat: number[] = [];\n for (const p of outline) flat.push(p.x, p.y);\n const holeIndices: number[] = [];\n if (holes) {\n for (const hole of holes) {\n if (hole.length < 3) continue;\n holeIndices.push(pts.length);\n for (const p of hole) {\n pts.push(p);\n flat.push(p.x, p.y);\n }\n }\n }\n const tris = earcut(flat, holeIndices.length ? holeIndices : undefined, 2);\n return { pts, tris };\n}\n\n/** Centroid of a point ring (average — good enough for wall orientation). */\nexport function centroid(pts: Point[]): Point {\n let x = 0, y = 0;\n for (const p of pts) { x += p.x; y += p.y; }\n const n = pts.length || 1;\n return { x: x / n, y: y / n };\n}\n\n/** Signed area of a ring (shoelace). Positive = CCW, negative = CW, ~0 = degenerate. */\nexport function signedArea(pts: Point[]): number {\n let a = 0;\n for (let i = 0, n = pts.length; i < n; i++) {\n const p = pts[i], q = pts[(i + 1) % n];\n a += p.x * q.y - q.x * p.y;\n }\n return a / 2;\n}\n\n/** Return the ring wound counter-clockwise (reversed copy if it was CW), so\n * CW and CCW inputs of the same polygon extrude to identical geometry. */\nexport function toCCW(pts: Point[]): Point[] {\n return signedArea(pts) < 0 ? [...pts].reverse() : pts;\n}\n\n/**\n * Extrude a closed polygon into a prism: a (possibly sloped) top cap, a bottom\n * cap, and side walls with a baked top→bottom AO gradient.\n *\n * `topY(p)` returns the world-metre height of the top surface at chart-point `p`\n * (constant for a slab, rake-sloped for a raked tier). `bottomY` is the floor.\n */\nexport function extrudePrism(\n builder: MeshBuilder,\n outlineIn: Point[],\n holesIn: Point[][] | undefined,\n topY: (p: Point) => number,\n bottomY: number,\n colTop: RGB,\n colWall: RGB,\n ao: { top: number; wallBottom: number; bottomCap: number },\n): void {\n // Guard degenerate/near-collinear polygons (zero visible area) — a free-hand\n // or generated outline can collapse to a sliver and would emit garbage tris.\n if (!outlineIn || outlineIn.length < 3) return;\n if (Math.abs(signedArea(outlineIn)) < 1e-4) return;\n // Normalise winding so CW and CCW inputs produce identical geometry (the solid\n // program also disables culling, but this keeps the emitted mesh deterministic).\n const outline = toCCW(outlineIn);\n const holes = holesIn?.map((h) => toCCW(h)).filter((h) => h.length >= 3 && Math.abs(signedArea(h)) >= 1e-4);\n const { pts, tris } = triangulate(outline, holes);\n const cTop: RGB = [colTop[0] * ao.top, colTop[1] * ao.top, colTop[2] * ao.top];\n const cBot: RGB = [colTop[0] * ao.bottomCap, colTop[1] * ao.bottomCap, colTop[2] * ao.bottomCap];\n const cWallTop: RGB = [colWall[0] * ao.top, colWall[1] * ao.top, colWall[2] * ao.top];\n const cWallBot: RGB = [colWall[0] * ao.wallBottom, colWall[1] * ao.wallBottom, colWall[2] * ao.wallBottom];\n\n // Top + bottom caps.\n for (let i = 0; i < tris.length; i += 3) {\n const a = pts[tris[i]], b = pts[tris[i + 1]], c = pts[tris[i + 2]];\n const at: [number, number, number] = [a.x * M, topY(a), a.y * M];\n const bt: [number, number, number] = [b.x * M, topY(b), b.y * M];\n const ct: [number, number, number] = [c.x * M, topY(c), c.y * M];\n let n = faceNormal(at, bt, ct);\n if (n[1] < 0) n = [-n[0], -n[1], -n[2]]; // caps face up\n builder.tri(at, bt, ct, n, cTop);\n // Bottom cap (reversed winding, faces down).\n const ab: [number, number, number] = [a.x * M, bottomY, a.y * M];\n const bb: [number, number, number] = [b.x * M, bottomY, b.y * M];\n const cb: [number, number, number] = [c.x * M, bottomY, c.y * M];\n builder.tri(ab, cb, bb, [0, -1, 0], cBot);\n }\n\n // Side walls. Orient outline walls away from the outline centroid; hole walls\n // face into the hole (flip). Vertical walls → horizontal normals.\n const oc = centroid(outline);\n const rings: Array<{ ring: Point[]; flip: boolean }> = [{ ring: outline, flip: false }];\n if (holes) for (const h of holes) if (h.length >= 3) rings.push({ ring: h, flip: true });\n\n for (const { ring, flip } of rings) {\n for (let i = 0; i < ring.length; i++) {\n const a = ring[i];\n const b = ring[(i + 1) % ring.length];\n const dx = (b.x - a.x) * M;\n const dz = (b.y - a.y) * M;\n let nx = dz, nz = -dx;\n const nl = Math.hypot(nx, nz) || 1;\n nx /= nl; nz /= nl;\n // Orient outward from the outline centroid.\n const mx = (a.x + b.x) / 2 - oc.x;\n const mz = (a.y + b.y) / 2 - oc.y;\n let dot = nx * mx + nz * mz;\n if (flip) dot = -dot;\n if (dot < 0) { nx = -nx; nz = -nz; }\n const n: [number, number, number] = [nx, 0, nz];\n\n const aTop: [number, number, number] = [a.x * M, topY(a), a.y * M];\n const bTop: [number, number, number] = [b.x * M, topY(b), b.y * M];\n const aBot: [number, number, number] = [a.x * M, bottomY, a.y * M];\n const bBot: [number, number, number] = [b.x * M, bottomY, b.y * M];\n builder.tri(aTop, bTop, bBot, n, cWallTop, cWallTop, cWallBot);\n builder.tri(aTop, bBot, aBot, n, cWallTop, cWallBot, cWallBot);\n }\n }\n}\n\n/** Merge several MeshData buffers into one (single draw call). */\nexport function mergeMeshData(parts: MeshData[]): MeshData {\n let total = 0;\n for (const p of parts) total += p.count;\n const position = new Float32Array(total * 3);\n const normal = new Float32Array(total * 3);\n const color = new Float32Array(total * 3);\n let off = 0;\n for (const p of parts) {\n position.set(p.position, off * 3);\n normal.set(p.normal, off * 3);\n color.set(p.color, off * 3);\n off += p.count;\n }\n return { position, normal, color, count: total };\n}\n\n/** Sample an ellipse (chart units) into a closed polygon of `seg` points. */\nexport function ellipsePolygon(cx: number, cy: number, rx: number, ry: number, seg = 28): Point[] {\n const out: Point[] = [];\n for (let i = 0; i < seg; i++) {\n const a = (i / seg) * Math.PI * 2;\n out.push({ x: cx + rx * Math.cos(a), y: cy + ry * Math.sin(a) });\n }\n return out;\n}\n\n/** Axis-aligned rectangle (chart units) as a closed polygon. */\nexport function rectPolygon(x: number, y: number, w: number, h: number): Point[] {\n return [\n { x, y },\n { x: x + w, y },\n { x: x + w, y: y + h },\n { x, y: y + h },\n ];\n}\n","/**\n * Pure builder for the instanced seat cloud. Produces the per-instance arrays a\n * single OGL InstancedMesh consumes (one draw call for every seat), plus the\n * seatId → instanceIndex map that `setAvailability` uses to patch only the seats\n * that actually changed via a sub-range `bufferSubData` upload.\n */\n\nimport type { ExpandedSeat } from '../../core/types';\nimport { SEATED_EYE_HEIGHT_M } from '../../core/units';\nimport { M } from './geometry';\nimport { seatStateIndex, type SeatState3D } from '../palette';\n\n/** Lift so a dot sits clearly ON the tier deck (which is drawn ~0.28 m below the\n * resolved surface), never occluded by its own cap. */\nconst SEAT_SURFACE_LIFT_M = 0.18;\n\nexport interface SeatInstanceData {\n count: number;\n /** vec3 per instance: world (x, y, z) in metres. */\n iPosition: Float32Array;\n /** float per instance: index into the seat-state colour LUT. */\n iState: Float32Array;\n /** seatId → instance index (drives targeted availability updates). */\n idToIndex: Map<string, number>;\n}\n\n/**\n * Resolve a seat's surface height in world metres. Prefer the section-resolved\n * eye height (already floor-base + rake-rise aware) minus the seated-eye offset\n * so the dot lands on the seating surface; fall back to ground for flat charts.\n */\nfunction seatSurfaceY(seat: ExpandedSeat): number {\n const eye = seat.eyeHeightM;\n if (Number.isFinite(eye)) {\n return Math.max(0, (eye as number) - SEATED_EYE_HEIGHT_M) + SEAT_SURFACE_LIFT_M;\n }\n return SEAT_SURFACE_LIFT_M;\n}\n\nexport function buildSeatInstances(\n seats: ExpandedSeat[],\n initial?: (seat: ExpandedSeat) => SeatState3D,\n): SeatInstanceData {\n const count = seats.length;\n const iPosition = new Float32Array(count * 3);\n const iState = new Float32Array(count);\n const idToIndex = new Map<string, number>();\n for (let i = 0; i < count; i++) {\n const seat = seats[i];\n iPosition[i * 3] = seat.x * M;\n iPosition[i * 3 + 1] = seatSurfaceY(seat);\n iPosition[i * 3 + 2] = seat.y * M;\n iState[i] = seatStateIndex(initial ? initial(seat) : 'available');\n idToIndex.set(seat.id, i);\n }\n return { count, iPosition, iState, idToIndex };\n}\n\n/** Contiguous run of instance indices to upload in a single bufferSubData call. */\nexport interface DirtyRun {\n start: number;\n /** number of instances (floats, since iState is 1 float/instance). */\n length: number;\n}\n\n/**\n * Apply state updates to the CPU `iState` array and return the coalesced\n * contiguous runs that changed — the caller uploads exactly those ranges and\n * never the whole buffer.\n */\nexport function applySeatStates(\n data: SeatInstanceData,\n updates: Array<{ seatId: string; state: SeatState3D }>,\n): DirtyRun[] {\n const changed: number[] = [];\n for (const u of updates) {\n const idx = data.idToIndex.get(u.seatId);\n if (idx === undefined) continue;\n const v = seatStateIndex(u.state);\n if (data.iState[idx] !== v) {\n data.iState[idx] = v;\n changed.push(idx);\n }\n }\n if (!changed.length) return [];\n changed.sort((a, b) => a - b);\n const runs: DirtyRun[] = [];\n let start = changed[0];\n let prev = changed[0];\n for (let i = 1; i < changed.length; i++) {\n const idx = changed[i];\n if (idx === prev) continue;\n if (idx === prev + 1) { prev = idx; continue; }\n runs.push({ start, length: prev - start + 1 });\n start = idx;\n prev = idx;\n }\n runs.push({ start, length: prev - start + 1 });\n return runs;\n}\n","/**\n * The JS-side source of truth for the 3D scene — a pure, GPU-free description\n * built once from the chart's existing height contract. Everything the renderer\n * uploads (merged solid geometry, the instanced seat cloud, camera-framing\n * bounds, the seat-state colour LUT) is derived here, so it survives a WebGL\n * context loss: on `webglcontextrestored` the renderer simply re-uploads from\n * this model without recomputing anything.\n *\n * Feeds 100% from `sectionGeometry` / `Floor.baseHeightM` / `ExpandedSeat`\n * (docs/3d-program-workorder §Architecture) — no new chart data is invented.\n */\n\nimport type { ChartDoc, ChartObject, ExpandedSeat, Point, SectionObject } from '../../core/types';\nimport { sectionGeometry } from '../../core/units';\nimport { seatStateColorLUT, STRUCTURE, hexToRgb, mix, desaturate, scaleRgb, type RGB } from '../palette';\nimport {\n MeshBuilder, extrudePrism, mergeMeshData, ellipsePolygon, rectPolygon, M, type MeshData,\n} from './geometry';\nimport { buildSeatInstances, type SeatInstanceData } from './seatInstances';\n\n/** One resolved plane of geometry — a single-floor chart is one of these. */\ninterface FloorUnit {\n objects: ChartObject[];\n focal: Point;\n baseHeightM: number;\n}\n\nexport interface SceneModel {\n /** Every non-seat surface merged into one triangle soup (1 draw call). */\n solids: MeshData;\n seats: SeatInstanceData;\n bounds: {\n /** World-metre venue centre (camera target). */\n center: [number, number, number];\n /** Half-diagonal of the horizontal footprint, metres (camera fit). */\n radius: number;\n groundY: number;\n };\n /** 5 × vec3 flat LUT for the seat fragment shader. */\n stateColorLUT: number[];\n seatCount: number;\n /** Venue focal point in world metres (cinematic look-at target). */\n focalWorld: [number, number, number];\n}\n\nfunction floorUnits(doc: ChartDoc): FloorUnit[] {\n if (doc.floors?.length) {\n return doc.floors.map((f) => ({\n objects: f.objects,\n focal: f.focalPoint ?? doc.focalPoint,\n baseHeightM: f.baseHeightM ?? 0,\n }));\n }\n return [{ objects: doc.objects, focal: doc.focalPoint, baseHeightM: 0 }];\n}\n\nconst AO = { top: 1.0, wallBottom: 0.5, bottomCap: 0.4 };\n\n/** Seats sit on the deck, so the deck surface is drawn this far BELOW the seat\n * dots (which lift SEAT_SURFACE_LIFT_M above the same resolved surface). Keeping\n * the deck under the dots stops the tier cap from occluding its own seats. */\nconst DECK_DROP_M = 0.28;\n/** Hard ceiling on rake rise so a mis-authored / arc-wrapped section can never\n * produce a runaway spike (defect guard). */\nconst MAX_TIER_RISE_M = 25;\n\n/**\n * Fold a surface's 2D fill colour into the dark structure palette: desaturate\n * ~40 %, darken, then ground it in the neutral structure grey so a tier top\n * reads architectural — a recognisable hue (purple/green/orange) but muted, not\n * candy-coloured paint. Risers/walls stay neutral concrete; baked AO still\n * multiplies these per vertex downstream. `null` fill ⇒ the neutral grey.\n */\nfunction tintTop(fill: RGB | null, neutral: RGB): RGB {\n if (!fill) return neutral;\n const muted = scaleRgb(desaturate(fill, 0.4), 0.62);\n return mix(neutral, muted, 0.72);\n}\n\n/**\n * Resolve each section's 2D paint colour, keyed by logical section id\n * (`logicalSectionId ?? id`, matching how expanded seats attribute `sectionId`):\n * the count-weighted mix of its member seats' category colours — the same source\n * the 2D renderer blends into a section's block fill. An explicit `section.color`\n * override is applied later (it wins in `sectionFill`).\n */\nfunction resolveSectionFills(doc: ChartDoc, seats: ExpandedSeat[]): Map<string, RGB> {\n const catColor = new Map<string, string>();\n for (const c of doc.categories ?? []) catColor.set(c.key, c.color);\n const counts = new Map<string, Map<string, number>>();\n for (const s of seats) {\n if (!s.sectionId) continue;\n let m = counts.get(s.sectionId);\n if (!m) { m = new Map(); counts.set(s.sectionId, m); }\n m.set(s.categoryKey, (m.get(s.categoryKey) ?? 0) + 1);\n }\n const out = new Map<string, RGB>();\n for (const [sid, byCat] of counts) {\n let r = 0, g = 0, b = 0, w = 0;\n for (const [key, n] of byCat) {\n const rgb = hexToRgb(catColor.get(key));\n if (!rgb) continue;\n r += rgb[0] * n; g += rgb[1] * n; b += rgb[2] * n; w += n;\n }\n if (w > 0) out.set(sid, [r / w, g / w, b / w]);\n }\n return out;\n}\n\n/** A section's fill: explicit `color` override wins, else the member-category mix. */\nfunction sectionFill(section: SectionObject, byLogical: Map<string, RGB>): RGB | null {\n return hexToRgb(section.color) ?? byLogical.get(section.logicalSectionId ?? section.id) ?? null;\n}\n\n/** Extrude one section into the shared builder (rake-sloped or flat slab). */\nfunction buildTier(builder: MeshBuilder, section: SectionObject, unit: FloorUnit, fill: RGB | null): void {\n if (!section.outline || section.outline.length < 3) return;\n const geo = sectionGeometry(section, { floorBaseHeightM: unit.baseHeightM });\n const bottomY = unit.baseHeightM;\n const rakeRad = (geo.rake * Math.PI) / 180;\n const flat = geo.rake <= 0.01 && geo.height <= bottomY + 0.001;\n const colTop = tintTop(fill, STRUCTURE.tierTop);\n\n if (flat) {\n // Thin slab whose top stays below the seat dots (which sit at ~lift height).\n const topY = bottomY + 0.05;\n extrudePrism(builder, section.outline, section.holes, () => topY, bottomY, colTop, STRUCTURE.tierWall, AO);\n return;\n }\n\n // Front edge = the section's OWN minimum focal distance (mirrors the\n // assignEyeHeights sightline model: rise grows with drawn radial depth). For\n // arc/ring sections whose outline wraps the focal point this per-vertex delta\n // is bounded by MAX_TIER_RISE_M so no vertex spikes into a fin.\n let frontDist = Infinity;\n for (const p of section.outline) {\n const d = Math.hypot(p.x - unit.focal.x, p.y - unit.focal.y);\n if (d < frontDist) frontDist = d;\n }\n const tan = Math.tan(rakeRad);\n const topY = (p: Point): number => {\n const d = Math.hypot(p.x - unit.focal.x, p.y - unit.focal.y);\n const depthM = Math.max(0, d - frontDist) * M;\n const rise = Math.min(depthM * tan, MAX_TIER_RISE_M);\n return Math.max(bottomY + 0.05, geo.height + rise - DECK_DROP_M);\n };\n extrudePrism(builder, section.outline, section.holes, topY, bottomY, colTop, STRUCTURE.tierWall, AO);\n}\n\n/** Resolve a shape object to a closed chart-unit polygon (or null to skip). */\nfunction shapePolygon(shape: Extract<ChartObject, { type: 'shape' }>): Point[] | null {\n if (shape.kind === 'polygon' && shape.points && shape.points.length >= 3) return shape.points;\n if (shape.kind === 'rect' && shape.width && shape.height) {\n return rectPolygon(shape.x ?? 0, shape.y ?? 0, shape.width, shape.height);\n }\n if (shape.kind === 'ellipse' && shape.width && shape.height) {\n const cx = (shape.x ?? 0) + shape.width / 2;\n const cy = (shape.y ?? 0) + shape.height / 2;\n return ellipsePolygon(cx, cy, shape.width / 2, shape.height / 2);\n }\n return null; // line / polyline are stroke-only\n}\n\nfunction buildShape(builder: MeshBuilder, shape: Extract<ChartObject, { type: 'shape' }>, base: number): void {\n const poly = shapePolygon(shape);\n if (!poly) return;\n const isStage = shape.role === 'stage';\n const height = isStage ? base + 1.0 : base + 0.25;\n const colTop = isStage ? STRUCTURE.stageTop : STRUCTURE.decorTop;\n const colWall = isStage ? STRUCTURE.stageWall : STRUCTURE.decorWall;\n extrudePrism(builder, poly, undefined, () => height, base, colTop, colWall, AO);\n}\n\nfunction buildGa(builder: MeshBuilder, ga: Extract<ChartObject, { type: 'gaArea' }>, base: number, fill: RGB | null): void {\n if (!ga.points || ga.points.length < 3) return;\n const colTop = tintTop(fill, STRUCTURE.gaTop);\n extrudePrism(builder, ga.points, ga.holes, () => base + 0.15, base, colTop, STRUCTURE.gaWall, AO);\n}\n\n/** Compute the horizontal chart-unit footprint over everything drawable. */\nfunction chartFootprint(units: FloorUnit[], seats: ExpandedSeat[]): { minX: number; minY: number; maxX: number; maxY: number } {\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;\n const acc = (x: number, y: number): void => {\n if (x < minX) minX = x; if (y < minY) minY = y;\n if (x > maxX) maxX = x; if (y > maxY) maxY = y;\n };\n for (const s of seats) acc(s.x, s.y);\n for (const u of units) {\n for (const o of u.objects) {\n if (o.type === 'section') for (const p of o.outline) acc(p.x, p.y);\n else if (o.type === 'shape' && o.points) for (const p of o.points) acc(p.x, p.y);\n else if (o.type === 'gaArea') for (const p of o.points) acc(p.x, p.y);\n }\n }\n if (!Number.isFinite(minX)) { minX = -100; minY = -100; maxX = 100; maxY = 100; }\n return { minX, minY, maxX, maxY };\n}\n\nexport interface SceneModelInput {\n doc: ChartDoc;\n seats: ExpandedSeat[];\n /** Optional initial per-seat state (default all available). */\n initialState?: (seat: ExpandedSeat) => import('../palette').SeatState3D;\n}\n\nexport function buildSceneModel(input: SceneModelInput): SceneModel {\n const { doc, seats } = input;\n const units = floorUnits(doc);\n const builder = new MeshBuilder();\n\n // Ground slab sized to the footprint (+ margin), sitting at datum 0.\n const fp = chartFootprint(units, seats);\n const padU = Math.max(60, (fp.maxX - fp.minX + fp.maxY - fp.minY) * 0.06);\n const groundPoly = rectPolygon(fp.minX - padU, fp.minY - padU, (fp.maxX - fp.minX) + padU * 2, (fp.maxY - fp.minY) + padU * 2);\n extrudePrism(builder, groundPoly, undefined, () => 0, -0.4, STRUCTURE.ground, STRUCTURE.ground, AO);\n\n // Per-section 2D fill colours (member-category mix), carried onto tier tops.\n const sectionFills = resolveSectionFills(doc, seats);\n const catColor = new Map<string, string>();\n for (const c of doc.categories ?? []) catColor.set(c.key, c.color);\n\n for (const unit of units) {\n for (const o of unit.objects) {\n if (o.type === 'section') buildTier(builder, o, unit, sectionFill(o, sectionFills));\n else if (o.type === 'shape') buildShape(builder, o, unit.baseHeightM);\n else if (o.type === 'gaArea') buildGa(builder, o, unit.baseHeightM, hexToRgb(catColor.get(o.categoryKey)));\n }\n }\n\n const solids = mergeMeshData([builder.build()]);\n const seatData: SeatInstanceData = buildSeatInstances(seats, input.initialState);\n\n const cx = ((fp.minX + fp.maxX) / 2) * M;\n const cz = ((fp.minY + fp.maxY) / 2) * M;\n const radius = 0.5 * Math.hypot((fp.maxX - fp.minX) * M, (fp.maxY - fp.minY) * M) || 10;\n\n const focal = doc.focalPoint ?? { x: (fp.minX + fp.maxX) / 2, y: (fp.minY + fp.maxY) / 2 };\n\n return {\n solids,\n seats: seatData,\n bounds: { center: [cx, radius * 0.08, cz], radius, groundY: 0 },\n stateColorLUT: seatStateColorLUT(),\n seatCount: seats.length,\n // Look-at target ~1.5 m up so a seated camera aims slightly down at the stage.\n focalWorld: [focal.x * M, 1.5, focal.y * M],\n };\n}\n","/**\n * Builds (and rebuilds) all GPU resources from a SceneModel. Kept separate from\n * the model so a context-loss restore can throw the old GpuScene away and call\n * `buildGpuScene(gl, model)` again — the model never changes.\n *\n * Draw calls: background (1) + merged solids (1) + instanced seats (1) = 3.\n */\n\nimport { Geometry, Mesh, Program, Transform, type OGLRenderingContext } from 'ogl';\nimport { BACKGROUND, seatStateColorByIndex } from '../palette';\nimport { createBackgroundProgram, createSeatProgram, createSolidProgram } from './materials';\nimport type { SceneModel } from './sceneModel';\nimport type { DirtyRun } from './seatInstances';\n\n/** Fill an iColor buffer range from the current iState values (state → colour). */\nfunction writeSeatColors(iColor: Float32Array, iState: Float32Array, start: number, count: number): void {\n for (let i = start; i < start + count; i++) {\n const c = seatStateColorByIndex(iState[i]);\n iColor[i * 3] = c[0];\n iColor[i * 3 + 1] = c[1];\n iColor[i * 3 + 2] = c[2];\n }\n}\n\n// Two-triangle quad in [-1,1] (billboard base).\nconst SEAT_QUAD = new Float32Array([-1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1]);\n// Fullscreen triangle.\nconst BG_TRI = new Float32Array([-1, -1, 3, -1, -1, 3]);\n\nexport interface GpuScene {\n /** Main scene (solids + seats), drawn with the camera. */\n main: Transform;\n /** Background scene, drawn first without depth. */\n background: Transform;\n seatProgram: Program;\n /** Shared instanced seat geometry (reused by the pick pass — no buffer copy). */\n seatGeometry: Geometry;\n /** Merged solid geometry (reused as the pick occluder). */\n solidGeometry: Geometry;\n drawCalls: number;\n /** Upload only the changed instance-state ranges (never the whole buffer). */\n uploadSeatStateRuns(runs: DirtyRun[]): void;\n dispose(): void;\n}\n\nexport function buildGpuScene(gl: OGLRenderingContext, model: SceneModel): GpuScene {\n const main = new Transform();\n const background = new Transform();\n\n // --- Background ---\n const bgGeo = new Geometry(gl, { position: { size: 2, data: BG_TRI } });\n const bgProg = createBackgroundProgram(gl, BACKGROUND.top as unknown as number[], BACKGROUND.bottom as unknown as number[]);\n const bgMesh = new Mesh(gl, { geometry: bgGeo, program: bgProg });\n bgMesh.frustumCulled = false;\n bgMesh.setParent(background);\n\n // --- Solids (floor + tiers + stage + décor + GA, merged) ---\n const solidGeo = new Geometry(gl, {\n position: { size: 3, data: model.solids.position },\n normal: { size: 3, data: model.solids.normal },\n color: { size: 3, data: model.solids.color },\n });\n const solidProg = createSolidProgram(gl);\n const solidMesh = new Mesh(gl, { geometry: solidGeo, program: solidProg });\n solidMesh.frustumCulled = false;\n solidMesh.setParent(main);\n\n // --- Seats (one instanced billboard mesh) ---\n // Per-instance colour resolved CPU-side from iState (no dynamically-indexed\n // array uniform — OGL only binds an array uniform whose value is a plain\n // Array, and a dynamic LUT index is best avoided anyway).\n const seatProg = createSeatProgram(gl);\n const iColor = new Float32Array(model.seats.count * 3);\n writeSeatColors(iColor, model.seats.iState, 0, model.seats.count);\n const seatGeo = new Geometry(gl, {\n position: { size: 2, data: SEAT_QUAD },\n iOffset: { size: 3, data: model.seats.iPosition, instanced: 1 },\n iColor: { size: 3, data: iColor, instanced: 1 },\n });\n const seatMesh = new Mesh(gl, { geometry: seatGeo, program: seatProg });\n seatMesh.frustumCulled = false;\n if (model.seats.count > 0) seatMesh.setParent(main);\n\n const colorAttr = seatGeo.attributes.iColor;\n\n return {\n main,\n background,\n seatProgram: seatProg,\n seatGeometry: seatGeo,\n solidGeometry: solidGeo,\n drawCalls: 3,\n uploadSeatStateRuns(runs: DirtyRun[]): void {\n if (!runs.length) return;\n // Refresh only the changed instance colours from the (already-mutated)\n // iState, then upload just those contiguous ranges — never the whole buffer.\n for (const run of runs) writeSeatColors(iColor, model.seats.iState, run.start, run.length);\n const buffer = colorAttr.buffer;\n if (!buffer) {\n // Not uploaded yet (no draw has happened) — full upload on next draw.\n colorAttr.needsUpdate = true;\n return;\n }\n // Direct bufferSubData: OGL's render-state boundBuffer cache is not touched\n // here, which is safe because OGL rebinds attribute buffers per draw via the\n // geometry's VAO; if a future dynamic attribute relies on the cache, rebind\n // through OGL instead. 3 floats per instance (vec3 iColor).\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n for (const run of runs) {\n const sub = iColor.subarray(run.start * 3, (run.start + run.length) * 3);\n gl.bufferSubData(gl.ARRAY_BUFFER, run.start * 3 * Float32Array.BYTES_PER_ELEMENT, sub);\n }\n },\n dispose(): void {\n // OGL geometries/programs delete their GL resources on remove().\n bgGeo.remove();\n bgProg.remove();\n solidGeo.remove();\n solidProg.remove();\n seatGeo.remove();\n seatProg.remove();\n },\n };\n}\n","/**\n * Inline GLSL (WebGL2 / GLSL ES 3.00) for the three scene programs. Zero\n * textures, zero shadow maps, zero post: a procedural matcap-style hemisphere +\n * warm key + fresnel rim on solids, a soft top-lit round dot for seats, and a\n * vertical-gradient + vignette background. OGL injects the built-in matrix\n * uniforms (modelViewMatrix / projectionMatrix / normalMatrix) by name.\n */\n\nimport { Program } from 'ogl';\nimport type { OGLRenderingContext } from 'ogl';\n\nconst SOLID_VERT = /* glsl */ `#version 300 es\nprecision highp float;\nin vec3 position;\nin vec3 normal;\nin vec3 color;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat3 normalMatrix;\nout vec3 vColor;\nout vec3 vNormalView;\nout vec3 vPosView;\nvoid main() {\n vec4 mv = modelViewMatrix * vec4(position, 1.0);\n vPosView = mv.xyz;\n vNormalView = normalize(normalMatrix * normal);\n vColor = color;\n gl_Position = projectionMatrix * mv;\n}`;\n\nconst SOLID_FRAG = /* glsl */ `#version 300 es\nprecision highp float;\nin vec3 vColor;\nin vec3 vNormalView;\nin vec3 vPosView;\nout vec4 fragColor;\nvoid main() {\n vec3 N = normalize(vNormalView);\n vec3 V = normalize(-vPosView);\n float hemi = 0.5 + 0.5 * N.y; // sky/ground gradient\n vec3 L = normalize(vec3(0.4, 0.85, 0.55)); // warm key, view space\n float key = max(dot(N, L), 0.0);\n vec3 base = vColor * (0.60 + 0.32 * hemi) + vColor * key * 0.32;\n float fres = pow(1.0 - max(dot(N, V), 0.0), 3.0);\n base += vec3(0.26, 0.31, 0.38) * fres * 0.35; // cool rim, restrained\n fragColor = vec4(base, 1.0);\n}`;\n\nconst SEAT_VERT = /* glsl */ `#version 300 es\nprecision highp float;\nin vec2 position; // quad corner in [-1,1]\nin vec3 iOffset; // per-instance world position\nin vec3 iColor; // per-instance state colour (resolved CPU-side)\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform float uSeatRadius;\nuniform float uSeatScale;\nuniform float uMinPixels;\nuniform float uPixelToWorld; // (2*tan(fovY/2)) / viewportHeightPx\nout vec2 vUv;\nout vec3 vColor;\nvoid main() {\n vec4 mv = modelViewMatrix * vec4(iOffset, 1.0);\n float depth = max(-mv.z, 0.001);\n float minR = uMinPixels * depth * uPixelToWorld; // screen-space floor\n float r = max(uSeatRadius * uSeatScale, minR);\n mv.xy += position * r; // camera-facing billboard\n vUv = position;\n vColor = iColor;\n gl_Position = projectionMatrix * mv;\n}`;\n\nconst SEAT_FRAG = /* glsl */ `#version 300 es\nprecision highp float;\nin vec2 vUv;\nin vec3 vColor;\nuniform float uSeatFade; // fade toward tier colour with distance (LOD)\nuniform vec3 uFadeColor;\nout vec4 fragColor;\nvoid main() {\n float d = length(vUv);\n if (d > 1.0) discard;\n float alpha = smoothstep(1.0, 0.72, d);\n float shade = 0.80 + 0.28 * (0.5 - vUv.y * 0.5); // subtle top-lit\n vec3 c = vColor * shade;\n c = mix(c, uFadeColor, uSeatFade);\n fragColor = vec4(c, alpha);\n}`;\n\nconst BG_VERT = /* glsl */ `#version 300 es\nprecision highp float;\nin vec2 position;\nout vec2 vUv;\nvoid main() {\n vUv = position * 0.5 + 0.5;\n gl_Position = vec4(position, 0.999, 1.0);\n}`;\n\nconst BG_FRAG = /* glsl */ `#version 300 es\nprecision highp float;\nin vec2 vUv;\nuniform vec3 uTop;\nuniform vec3 uBottom;\nout vec4 fragColor;\nvoid main() {\n vec3 col = mix(uBottom, uTop, vUv.y);\n vec2 c = vUv - 0.5;\n float vig = 1.0 - dot(c, c) * 0.85; // soft vignette\n fragColor = vec4(col * vig, 1.0);\n}`;\n\n// --- GPU pick pass ---------------------------------------------------------\n// Seats encode gl_InstanceID+1 as an RGB colour (no extra per-instance buffer);\n// solids write pure black + depth first so a seat occluded by a tier reads as\n// \"no hit\". Same billboard maths as the display seat program.\nconst SEAT_PICK_VERT = /* glsl */ `#version 300 es\nprecision highp float;\nin vec2 position;\nin vec3 iOffset;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform float uSeatRadius;\nuniform float uSeatScale;\nuniform float uMinPixels;\nuniform float uPixelToWorld;\nout vec2 vUv;\nflat out vec3 vPick;\nvoid main() {\n int id = gl_InstanceID + 1; // 0 reserved for no-hit\n vPick = vec3(float(id & 255), float((id >> 8) & 255), float((id >> 16) & 255)) / 255.0;\n vec4 mv = modelViewMatrix * vec4(iOffset, 1.0);\n float depth = max(-mv.z, 0.001);\n float minR = uMinPixels * depth * uPixelToWorld;\n float r = max(uSeatRadius * uSeatScale, minR);\n mv.xy += position * r;\n vUv = position;\n gl_Position = projectionMatrix * mv;\n}`;\n\nconst SEAT_PICK_FRAG = /* glsl */ `#version 300 es\nprecision highp float;\nin vec2 vUv;\nflat in vec3 vPick;\nout vec4 fragColor;\nvoid main() {\n if (length(vUv) > 1.0) discard; // round hit-mask matches the dot\n fragColor = vec4(vPick, 1.0);\n}`;\n\nconst PICK_DEPTH_VERT = /* glsl */ `#version 300 es\nprecision highp float;\nin vec3 position;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nvoid main() {\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}`;\n\nconst PICK_DEPTH_FRAG = /* glsl */ `#version 300 es\nprecision highp float;\nout vec4 fragColor;\nvoid main() { fragColor = vec4(0.0, 0.0, 0.0, 1.0); }`;\n\nexport function createSeatPickProgram(gl: OGLRenderingContext): Program {\n return new Program(gl, {\n vertex: SEAT_PICK_VERT,\n fragment: SEAT_PICK_FRAG,\n transparent: false,\n depthTest: true,\n depthWrite: true,\n cullFace: false,\n uniforms: {\n uSeatRadius: { value: 0.22 },\n uSeatScale: { value: 1 },\n uMinPixels: { value: 2.5 },\n uPixelToWorld: { value: 0.002 },\n },\n });\n}\n\n/** Occluder pass: solids to black + depth so occluded seats read as no-hit. */\nexport function createPickDepthProgram(gl: OGLRenderingContext): Program {\n return new Program(gl, {\n vertex: PICK_DEPTH_VERT,\n fragment: PICK_DEPTH_FRAG,\n transparent: false,\n depthTest: true,\n depthWrite: true,\n cullFace: false,\n });\n}\n\nexport function createSolidProgram(gl: OGLRenderingContext): Program {\n return new Program(gl, {\n // No backface culling: free-hand section polygons are stored in raw click\n // order (either winding), so a culled solid would render see-through. The\n // shader lights both faces and closed opaque prisms + depth test keep\n // overdraw negligible; extrudePrism also normalises winding as a belt.\n vertex: SOLID_VERT,\n fragment: SOLID_FRAG,\n cullFace: false,\n depthTest: true,\n depthWrite: true,\n });\n}\n\nexport function createSeatProgram(gl: OGLRenderingContext): Program {\n return new Program(gl, {\n vertex: SEAT_VERT,\n fragment: SEAT_FRAG,\n transparent: true,\n depthTest: true,\n depthWrite: false,\n cullFace: false,\n uniforms: {\n uSeatRadius: { value: 0.22 },\n uSeatScale: { value: 1 },\n uMinPixels: { value: 2.5 },\n uPixelToWorld: { value: 0.002 },\n uSeatFade: { value: 0 },\n uFadeColor: { value: new Float32Array([0.32, 0.37, 0.43]) },\n },\n });\n}\n\nexport function createBackgroundProgram(gl: OGLRenderingContext, top: number[], bottom: number[]): Program {\n return new Program(gl, {\n vertex: BG_VERT,\n fragment: BG_FRAG,\n depthTest: false,\n depthWrite: false,\n cullFace: false,\n uniforms: {\n uTop: { value: new Float32Array(top) },\n uBottom: { value: new Float32Array(bottom) },\n },\n });\n}\n","/**\n * GPU color-pick (Slice 2). On a TAP (not hover, not drag) the seat instance\n * index is rendered as RGB into a small scissored offscreen target and a single\n * pixel is read back → O(1) regardless of seat count. Solids are drawn first as\n * black + depth so a seat occluded by a tier reads as \"no hit\".\n *\n * The pick meshes reuse the display geometry buffers (iOffset / position), so no\n * per-seat data is duplicated on the GPU.\n */\n\nimport { Geometry, Mesh, Program, RenderTarget, Transform } from 'ogl';\nimport type { Camera, OGLRenderingContext, Renderer } from 'ogl';\nimport { createPickDepthProgram, createSeatPickProgram } from '../scene/materials';\nimport { BACKGROUND } from '../palette';\nimport { pickNearestFromBuffer } from './encode';\n\nconst SYNC_KEYS = ['uSeatRadius', 'uSeatScale', 'uMinPixels', 'uPixelToWorld'] as const;\n\nexport class PickPipeline {\n private gl: OGLRenderingContext;\n private renderer: Renderer;\n private seatProg: Program;\n private depthProg: Program;\n private seatScene = new Transform();\n private solidScene = new Transform();\n private target: RenderTarget | null = null;\n private maxIndex: number;\n\n constructor(renderer: Renderer, seatGeo: Geometry, solidGeo: Geometry, seatCount: number) {\n this.renderer = renderer;\n this.gl = renderer.gl;\n this.maxIndex = seatCount;\n this.seatProg = createSeatPickProgram(this.gl);\n this.depthProg = createPickDepthProgram(this.gl);\n const seatMesh = new Mesh(this.gl, { geometry: seatGeo, program: this.seatProg });\n seatMesh.frustumCulled = false;\n seatMesh.setParent(this.seatScene);\n const solidMesh = new Mesh(this.gl, { geometry: solidGeo, program: this.depthProg });\n solidMesh.frustumCulled = false;\n solidMesh.setParent(this.solidScene);\n }\n\n /** Match the display seat sizing so the pick mask lines up with the dots. */\n syncFromSeatProgram(seatProgram: Program): void {\n for (const k of SYNC_KEYS) this.seatProg.uniforms[k].value = seatProgram.uniforms[k].value;\n }\n\n private ensureTarget(): RenderTarget {\n const w = this.gl.drawingBufferWidth;\n const h = this.gl.drawingBufferHeight;\n if (this.target && (this.target.width !== w || this.target.height !== h)) {\n this.destroyTarget();\n }\n if (!this.target) {\n this.target = new RenderTarget(this.gl, { width: w, height: h, depth: true });\n }\n return this.target;\n }\n\n private destroyTarget(): void {\n if (!this.target) return;\n const gl = this.gl;\n if (this.target.buffer) gl.deleteFramebuffer(this.target.buffer);\n for (const t of this.target.textures ?? []) if (t.texture) gl.deleteTexture(t.texture);\n if (this.target.depthBuffer) gl.deleteRenderbuffer(this.target.depthBuffer);\n this.target = null;\n }\n\n /**\n * Read back the seat instance index NEAREST framebuffer pixel (px, py), or -1.\n * `radius` is the tap tolerance in buffer px: a box of side (2·radius+1) is\n * rendered + read so a tap that lands between the ~2px overview dots still\n * finds the closest seat. px/py/radius are bottom-left-origin buffer pixels.\n */\n pick(camera: Camera, px: number, py: number, radius: number): number {\n const gl = this.gl;\n const target = this.ensureTarget();\n const bw = gl.drawingBufferWidth;\n const bh = gl.drawingBufferHeight;\n const x0 = Math.max(0, px - radius);\n const y0 = Math.max(0, py - radius);\n const boxW = Math.max(1, Math.min(bw, px + radius + 1) - x0);\n const boxH = Math.max(1, Math.min(bh, py + radius + 1) - y0);\n\n gl.enable(gl.SCISSOR_TEST);\n gl.scissor(x0, y0, boxW, boxH);\n // Clear the pick target to TRUE BLACK so empty + occluded pixels decode to\n // no-hit structurally (not by a range guard). Occluders drawn black + depth\n // first, then seats (pick colours) depth-tested.\n const [br, bg, bb] = BACKGROUND.top;\n gl.clearColor(0, 0, 0, 1);\n this.renderer.render({ scene: this.solidScene, camera, target, clear: true });\n this.renderer.render({ scene: this.seatScene, camera, target, clear: false });\n gl.clearColor(br, bg, bb, 1); // restore the display clear colour\n gl.disable(gl.SCISSOR_TEST);\n\n const buf = new Uint8Array(boxW * boxH * 4);\n this.renderer.bindFramebuffer(target);\n gl.readPixels(x0, y0, boxW, boxH, gl.RGBA, gl.UNSIGNED_BYTE, buf);\n this.renderer.bindFramebuffer();\n\n return pickNearestFromBuffer(buf, boxW, boxH, px - x0, py - y0, this.maxIndex);\n }\n\n dispose(): void {\n this.destroyTarget();\n this.seatProg.remove();\n this.depthProg.remove();\n }\n}\n","/**\n * GPU color-pick id encoding — pure, DOM-free, so the round-trip and the tap →\n * framebuffer pixel maths are unit-testable. The seat instance index is offset\n * by +1 so id 0 is reserved for \"no hit\" (the cleared black background).\n */\n\n/** instanceIndex → normalised RGB (0..1) the pick shader writes. */\nexport function encodePickId(instanceIndex: number): [number, number, number] {\n const id = instanceIndex + 1;\n return [(id & 255) / 255, ((id >> 8) & 255) / 255, ((id >> 16) & 255) / 255];\n}\n\n/** RGB bytes (0..255) read back → instanceIndex, or -1 for the no-hit clear. */\nexport function decodePickRGB(r: number, g: number, b: number): number {\n const id = r + (g << 8) + (b << 16);\n return id === 0 ? -1 : id - 1;\n}\n\n/**\n * Scan a readback window (RGBA, bottom-left origin, row-major) for the seat hit\n * NEAREST the tap centre. A single tap on a low-res overview lands between ~2px\n * dots, so we read a small box and pick the closest non-empty seat instead of a\n * single pixel. `centerI/centerJ` are the tap's box-local pixel coords.\n * `maxIndex` bounds valid indices (defence against a stray decode).\n */\nexport function pickNearestFromBuffer(\n pixels: Uint8Array,\n boxW: number,\n boxH: number,\n centerI: number,\n centerJ: number,\n maxIndex: number,\n): number {\n let best = -1;\n let bestDist = Infinity;\n for (let j = 0; j < boxH; j++) {\n for (let i = 0; i < boxW; i++) {\n const o = (j * boxW + i) * 4;\n const idx = decodePickRGB(pixels[o], pixels[o + 1], pixels[o + 2]);\n if (idx < 0 || idx >= maxIndex) continue;\n const di = i - centerI;\n const dj = j - centerJ;\n const d = di * di + dj * dj;\n if (d < bestDist) { bestDist = d; best = idx; }\n }\n }\n return best;\n}\n\n/**\n * Map a tap in CSS pixels (relative to the canvas bounding rect) to a\n * bottom-left-origin framebuffer pixel, clamped in range. `rect` is the canvas\n * getBoundingClientRect; `dpr` the renderer device-pixel-ratio.\n */\nexport function pickPixelCoords(\n clientX: number,\n clientY: number,\n rect: { left: number; top: number; width: number; height: number },\n dpr: number,\n bufferWidth: number,\n bufferHeight: number,\n): { x: number; y: number } {\n const cssX = clientX - rect.left;\n const cssY = clientY - rect.top;\n const x = Math.round(cssX * dpr);\n // WebGL framebuffer origin is bottom-left → flip Y.\n const y = Math.round((rect.height - cssY) * dpr);\n return {\n x: Math.max(0, Math.min(bufferWidth - 1, x)),\n y: Math.max(0, Math.min(bufferHeight - 1, y)),\n };\n}\n","/**\n * Pure selection-state diffing. Selection is a colour layer over availability:\n * a selected seat shows the 'selected' colour and, on deselect, restores the\n * base availability state it had when it was selected (remembered in `prev`).\n * Kept DOM/GPU-free so the transitions are unit-testable.\n */\n\nimport { SEAT_STATES, seatStateIndex, type SeatState3D } from '../palette';\n\nexport interface SelectionUpdate {\n seatId: string;\n state: SeatState3D;\n}\n\n/**\n * Reconcile an availability update against the current selection. A selected\n * seat that changes availability must STAY 'selected' on screen while its\n * remembered base state is updated (so a later deselect restores the CURRENT\n * availability, not the pre-change one). Mutates `selection` in place and\n * returns the updates that should actually be written to iState (the\n * non-selected ones — selected seats keep their 'selected' colour).\n */\nexport function mergeAvailabilityIntoSelection(\n selection: Map<string, number>,\n updates: SelectionUpdate[],\n): SelectionUpdate[] {\n const passthrough: SelectionUpdate[] = [];\n for (const u of updates) {\n if (selection.has(u.seatId)) selection.set(u.seatId, seatStateIndex(u.state));\n else passthrough.push(u);\n }\n return passthrough;\n}\n\nexport interface SelectionDiff {\n updates: SelectionUpdate[];\n /** New seatId → remembered base-state index map. */\n next: Map<string, number>;\n}\n\n/**\n * Diff the current selection (`prev`: seatId → base-state index) against the\n * desired seat ids. `baseStateIndex` reads the seat's CURRENT state index (used\n * only for newly-selected seats, which are not yet recoloured).\n */\nexport function diffSelection(\n prev: Map<string, number>,\n desiredIds: string[],\n baseStateIndex: (seatId: string) => number | undefined,\n): SelectionDiff {\n const desired = new Set<string>();\n for (const id of desiredIds) {\n if (prev.has(id) || baseStateIndex(id) !== undefined) desired.add(id);\n }\n const next = new Map(prev);\n const updates: SelectionUpdate[] = [];\n\n // Deselect: restore base availability for ids leaving the selection.\n for (const [id, base] of prev) {\n if (!desired.has(id)) {\n updates.push({ seatId: id, state: SEAT_STATES[base] ?? 'available' });\n next.delete(id);\n }\n }\n // Select: remember base state, then recolour as selected.\n for (const id of desired) {\n if (next.has(id)) continue; // already selected → unchanged\n const base = baseStateIndex(id);\n if (base === undefined) continue;\n next.set(id, base);\n updates.push({ seatId: id, state: 'selected' });\n }\n return { updates, next };\n}\n","/**\n * Slice 3 — the purchase-moment fly-to-seat cinematic controller. One continuous\n * shot from the venue overview into the picked seat: a catmull-rom position\n * spline (smootherstep timing) + a look-at quaternion slerp whose orientation\n * slightly LEADS the position, with a gentle FOV push-in. The pure maths lives\n * in cinematicMath.ts (tested); this drives the OGL camera with it.\n *\n * Technique locked in docs/3d-usp-strategy-2026-07-23.md §3.\n */\n\nimport { Quat, Vec3 } from 'ogl';\nimport type { Camera } from 'ogl';\nimport {\n FLIGHT_DURATION_MS, ORIENTATION_LEAD,\n sampleFlight, orientationLeadT, type Vec3Arr,\n} from './cinematicMath';\n\nexport {\n FLIGHT_DURATION_MS, FOV_START, FOV_END, ORIENTATION_LEAD,\n smootherstep, orientationLeadT, catmullRom, buildWaypoints, sampleFlight,\n type Vec3Arr, type FlightSample,\n} from './cinematicMath';\n\n/** Look-at quaternion from `from` toward `to`, computed via the OGL camera\n * (save/restore) — no manual quat maths. Synchronous, no render between. */\nexport function lookAtQuat(camera: Camera, from: Vec3Arr, to: Vec3Arr): Quat {\n const savedPos = camera.position.clone();\n const savedQuat = new Quat().copy(camera.quaternion);\n camera.position.set(from[0], from[1], from[2]);\n camera.lookAt(new Vec3(to[0], to[1], to[2]));\n const q = new Quat().copy(camera.quaternion);\n camera.position.copy(savedPos);\n camera.quaternion.copy(savedQuat);\n return q;\n}\n\n/** Drives the OGL camera along a flight. Integrated with the render loop: the\n * loop calls update() each frame while active. */\nexport class Cinematic {\n active = false;\n private camera: Camera;\n private waypoints: Vec3Arr[] = [];\n private startQuat = new Quat();\n private endQuat = new Quat();\n private outQuat = new Quat();\n private startTime = 0;\n private duration = FLIGHT_DURATION_MS;\n private resolveFn: (() => void) | null = null;\n\n constructor(camera: Camera) {\n this.camera = camera;\n }\n\n /** Begin (or retarget) a flight. Resolves when it lands or is cancelled. */\n start(waypoints: Vec3Arr[], startQuat: Quat, endQuat: Quat, duration = FLIGHT_DURATION_MS): Promise<void> {\n this.settle(); // resolve any in-flight promise before retargeting\n this.waypoints = waypoints;\n this.startQuat.copy(startQuat);\n this.endQuat.copy(endQuat);\n this.duration = duration;\n this.startTime = performance.now();\n this.active = true;\n return new Promise<void>((res) => { this.resolveFn = res; });\n }\n\n /** Advance the flight, mutating the camera. Returns true while still flying. */\n update(now: number): boolean {\n if (!this.active) return false;\n const u = Math.min(1, (now - this.startTime) / this.duration);\n const { pos, fov, eased } = sampleFlight(this.waypoints, u);\n this.camera.position.set(pos[0], pos[1], pos[2]);\n this.outQuat.copy(this.startQuat).slerp(this.endQuat, orientationLeadT(eased, ORIENTATION_LEAD));\n this.camera.quaternion.copy(this.outQuat);\n this.camera.fov = fov;\n this.camera.updateProjectionMatrix();\n if (u >= 1) { this.settle(); return false; }\n return true;\n }\n\n /** Stop where we are (no snap) — the camera keeps its current pose. */\n cancel(): void {\n this.settle();\n }\n\n private settle(): void {\n this.active = false;\n const r = this.resolveFn;\n this.resolveFn = null;\n if (r) r();\n }\n}\n","/**\n * Pure, DOM/GPU-free maths for the fly-to-seat cinematic — spline, timing, and\n * waypoint construction. Split from cinematic.ts (which imports OGL) so this is\n * unit-testable in a plain runtime.\n */\n\nexport const FLIGHT_DURATION_MS = 2500;\nexport const FOV_START = 35;\nexport const FOV_END = 28;\n/** Orientation t leads position t by this much (clamped) — aim before arrival. */\nexport const ORIENTATION_LEAD = 0.15;\n/** Final approach: this far behind the seat (≈2–3 rows) and above its eye. */\nconst BACK_M = 2.5;\nconst ABOVE_EYE_M = 1.5;\n\nexport type Vec3Arr = [number, number, number];\n\nfunction sub(a: Vec3Arr, b: Vec3Arr): Vec3Arr { return [a[0] - b[0], a[1] - b[1], a[2] - b[2]]; }\nfunction add(a: Vec3Arr, b: Vec3Arr): Vec3Arr { return [a[0] + b[0], a[1] + b[1], a[2] + b[2]]; }\nfunction scale(a: Vec3Arr, k: number): Vec3Arr { return [a[0] * k, a[1] * k, a[2] * k]; }\nfunction norm(a: Vec3Arr): Vec3Arr {\n const l = Math.hypot(a[0], a[1], a[2]);\n return l > 1e-6 ? [a[0] / l, a[1] / l, a[2] / l] : [0, 0, 0];\n}\n\n/** Smootherstep (Ken Perlin) — zero 1st & 2nd derivatives at the ends. */\nexport function smootherstep(t: number): number {\n const x = Math.max(0, Math.min(1, t));\n return x * x * x * (x * (x * 6 - 15) + 10);\n}\n\n/** Orientation t = position t nudged ahead by `lead`, clamped to [0,1]. */\nexport function orientationLeadT(posT: number, lead: number): number {\n return Math.max(0, Math.min(1, posT + lead));\n}\n\n/**\n * Uniform multi-segment Catmull-Rom passing THROUGH every waypoint. `u` in\n * [0,1] spans the whole path; endpoints are duplicated for tangents.\n */\nexport function catmullRom(points: Vec3Arr[], u: number): Vec3Arr {\n const n = points.length;\n if (n === 0) return [0, 0, 0];\n if (n === 1) return [...points[0]];\n const cu = Math.max(0, Math.min(1, u));\n const segCount = n - 1;\n let seg = Math.floor(cu * segCount);\n if (seg >= segCount) seg = segCount - 1;\n const t = cu * segCount - seg;\n const p0 = points[Math.max(0, seg - 1)];\n const p1 = points[seg];\n const p2 = points[seg + 1];\n const p3 = points[Math.min(n - 1, seg + 2)];\n const t2 = t * t;\n const t3 = t2 * t;\n const out: Vec3Arr = [0, 0, 0];\n for (let i = 0; i < 3; i++) {\n out[i] = 0.5 * (\n 2 * p1[i]\n + (-p0[i] + p2[i]) * t\n + (2 * p0[i] - 5 * p1[i] + 4 * p2[i] - p3[i]) * t2\n + (-p0[i] + 3 * p1[i] - 3 * p2[i] + p3[i]) * t3\n );\n }\n return out;\n}\n\n/**\n * Build the flight waypoints from the current camera position to a seat. The\n * mid arc is pushed OUTSIDE the venue bounds sphere and high up, so the swoop\n * never clips through the tier solids; the final anchor sits behind + above the\n * seat, looking toward the focal point.\n */\nexport function buildWaypoints(\n start: Vec3Arr,\n seatEye: Vec3Arr,\n focal: Vec3Arr,\n center: Vec3Arr,\n radius: number,\n): { waypoints: Vec3Arr[]; finalPos: Vec3Arr } {\n let away = norm(sub(seatEye, focal)); // \"behind\" the seat (away from stage)\n if (away[0] === 0 && away[1] === 0 && away[2] === 0) away = [0, 0, 1];\n const finalPos = add(add(seatEye, scale(away, BACK_M)), [0, ABOVE_EYE_M, 0]);\n\n const horiz: Vec3Arr = [seatEye[0] - center[0], 0, seatEye[2] - center[2]];\n let hn = norm(horiz);\n if (hn[0] === 0 && hn[2] === 0) hn = [away[0], 0, away[2]];\n const r = Math.max(1, radius);\n const arc: Vec3Arr = [\n center[0] + hn[0] * r * 1.3,\n center[1] + r * 0.75,\n center[2] + hn[2] * r * 1.3,\n ];\n return { waypoints: [start, arc, finalPos], finalPos };\n}\n\nexport interface FlightSample {\n pos: Vec3Arr;\n fov: number;\n /** Eased position parameter (drives the orientation lead). */\n eased: number;\n}\n\n/** Sample the flight at raw parameter `u` in [0,1]. */\nexport function sampleFlight(waypoints: Vec3Arr[], u: number, fovStart = FOV_START, fovEnd = FOV_END): FlightSample {\n const eased = smootherstep(u);\n const pos = catmullRom(waypoints, eased);\n const fovT = smootherstep(Math.max(0, Math.min(1, (u - 0.66) / 0.34))); // push-in over final third\n return { pos, fov: fovStart + (fovEnd - fovStart) * fovT, eased };\n}\n","/**\n * Slice 3 hand-off — the DOM panorama overlay the fly-to-seat cinematic\n * dissolves into. Decoupled by design: the CALLER supplies the equirectangular\n * image (via mountVenue3D's getSeatView), so view3d never imports the app's\n * panorama generator and the chunk stays lean.\n *\n * Technique (mirrors SeatPicker.openSeatView, reimplemented small): an equirect\n * image panned with `repeat-x`; the initial horizontal offset is set so the\n * panorama's bearing matches the final camera yaw — the dissolve reads as the\n * same view sharpening, not a cut. CSS opacity fade is compositor-only.\n */\n\nexport interface SeatView {\n url: string;\n /** Bearing (deg, 0 = facing the focal/stage) the panorama should open centred\n * on, to match the camera's final yaw. Default 0 (both face the stage). */\n initialBearingDeg?: number;\n}\n\nexport interface PanoramaHandle {\n /** Fade out and return to the (frozen) 3D view; calls opts.onClose after. */\n close(): void;\n /** Immediate teardown (dispose) — no fade, no onClose. */\n dispose(): void;\n}\n\n/**\n * Vertical field of view (deg) the windowed panorama shows. The source image is\n * a full 180° equirect sphere; showing it raw wastes ~⅔ of the frame on dead sky\n * and black floor, with the horizon content band squished into the middle. We\n * instead scale the image so only this central slice fills the viewport height,\n * horizon-centred, and let the user drag pitch within ±`MAX_PITCH_DEG`.\n */\nexport const VFOV_DEG = 70;\n/** Users may look this far up/down from the horizon; well inside the image so\n * the clamp never reveals past its top/bottom edge. */\nexport const MAX_PITCH_DEG = 35;\n\n/**\n * Horizontal background-position (px) that centres `bearingDeg` in the viewport,\n * assuming the equirect image's yaw 0 sits at its horizontal centre. `bgW` is the\n * full scaled image width representing 360° — so this is invariant to the vertical\n * FOV windowing (which scales width and height by the same factor). `repeat-x`\n * handles the wrap, so any real value is valid.\n */\nexport function bearingToOffsetPx(bearingDeg: number, viewportW: number, bgW: number): number {\n const col = (0.5 + bearingDeg / 360) * bgW; // image column (px) for the bearing\n return viewportW / 2 - col;\n}\n\n/**\n * Full scaled image height (px) so that a `vfovDeg`-tall slice fills `viewportH`.\n * The image spans 180° vertically, so height = viewportH · 180/vfov.\n */\nexport function windowedBgHeight(viewportH: number, vfovDeg: number = VFOV_DEG): number {\n return viewportH * (180 / vfovDeg);\n}\n\n/**\n * background-position Y (px) that centres the image's horizon (its vertical\n * centre) in the viewport, offset by `pitchPx` (deviation from the horizon,\n * clamped to ±`MAX_PITCH_DEG`). Positive `pitchPx` looks up.\n */\nexport function horizonOffsetPy(viewportH: number, bgH: number, pitchPx: number): number {\n return (viewportH - bgH) / 2 + clampPitchPx(pitchPx, bgH);\n}\n\n/** Clamp a pitch drag (px) to ±MAX_PITCH_DEG of image travel, and never past the\n * image edge. `bgH` px map the full 180°, so a degree is `bgH/180` px. */\nexport function clampPitchPx(pitchPx: number, bgH: number): number {\n const limit = (MAX_PITCH_DEG / 180) * bgH;\n return Math.max(-limit, Math.min(limit, pitchPx));\n}\n\nexport interface PanoramaOptions {\n fadeMs?: number;\n seatLabel?: string;\n onClose?: () => void;\n}\n\nexport function mountPanorama(container: HTMLElement, view: SeatView, opts: PanoramaOptions = {}): PanoramaHandle {\n const fadeMs = opts.fadeMs ?? 400;\n const bearing = view.initialBearingDeg ?? 0;\n\n const root = document.createElement('div');\n root.setAttribute('role', 'dialog');\n root.setAttribute('aria-label', opts.seatLabel ? `View from ${opts.seatLabel}` : 'View from seat');\n Object.assign(root.style, {\n position: 'absolute', inset: '0', zIndex: '10', opacity: '0',\n transition: `opacity ${fadeMs}ms ease`, background: '#05070c',\n overflow: 'hidden', touchAction: 'none',\n } as CSSStyleDeclaration);\n\n const pano = document.createElement('div');\n Object.assign(pano.style, {\n position: 'absolute', inset: '0',\n backgroundImage: `url(\"${view.url}\")`, backgroundRepeat: 'repeat-x',\n cursor: 'grab',\n } as CSSStyleDeclaration);\n root.appendChild(pano);\n\n // Close affordance.\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.setAttribute('aria-label', 'Close');\n closeBtn.textContent = '✕';\n Object.assign(closeBtn.style, {\n position: 'absolute', top: '12px', right: '12px', zIndex: '2',\n width: '34px', height: '34px', borderRadius: '999px', cursor: 'pointer',\n border: '1px solid rgba(255,255,255,0.25)', background: 'rgba(8,12,18,0.6)',\n color: '#e6edf3', fontSize: '15px', lineHeight: '1',\n } as CSSStyleDeclaration);\n root.appendChild(closeBtn);\n\n const hint = document.createElement('div');\n hint.textContent = 'Drag to look around · Esc to close';\n Object.assign(hint.style, {\n position: 'absolute', bottom: '12px', left: '0', right: '0', textAlign: 'center',\n color: 'rgba(230,237,243,0.7)', font: '12px ui-sans-serif, system-ui, sans-serif',\n pointerEvents: 'none',\n } as CSSStyleDeclaration);\n root.appendChild(hint);\n\n container.appendChild(root);\n\n // Layout: window a ~70° vertical slice of the sphere (horizon-centred) so the\n // venue fills the frame instead of floating in dead sky + black floor. The\n // image is scaled so that slice is exactly the viewport height; width scales by\n // the same factor, so `bearingToOffsetPx` stays correct. `pitchPx` is the\n // vertical drag deviation from the horizon, clamped to ±35°.\n let bgW = 0;\n let bgH = 0;\n let posX = 0;\n let pitchPx = 0;\n const layout = (): void => {\n const vh = root.clientHeight || 1;\n const vw = root.clientWidth || 1;\n const natW = img.naturalWidth || vw * 2;\n const natH = img.naturalHeight || vh;\n bgH = windowedBgHeight(vh);\n bgW = bgH * (natW / natH);\n pano.style.backgroundSize = `${bgW}px ${bgH}px`;\n if (!posInitialised) { posX = bearingToOffsetPx(bearing, vw, bgW); posInitialised = true; }\n pitchPx = clampPitchPx(pitchPx, bgH);\n pano.style.backgroundPosition = `${posX}px ${horizonOffsetPy(vh, bgH, pitchPx)}px`;\n };\n let posInitialised = false;\n\n const applyPos = (): void => {\n const vh = root.clientHeight || 1;\n pitchPx = clampPitchPx(pitchPx, bgH);\n pano.style.backgroundPosition = `${posX}px ${horizonOffsetPy(vh, bgH, pitchPx)}px`;\n };\n\n const img = new Image();\n img.onload = layout;\n img.src = view.url;\n // If it's already cached, onload may not fire — lay out on next frame too.\n requestAnimationFrame(layout);\n\n // Pan: horizontal (repeat-x wraps seamlessly) + vertical pitch (clamped ±35°).\n let dragging = false;\n let lastX = 0;\n let lastY = 0;\n const onDown = (e: PointerEvent): void => {\n dragging = true; lastX = e.clientX; lastY = e.clientY; pano.style.cursor = 'grabbing';\n try { pano.setPointerCapture?.(e.pointerId); } catch { /* no active pointer */ }\n };\n const onMove = (e: PointerEvent): void => {\n if (!dragging) return;\n posX += e.clientX - lastX;\n pitchPx += e.clientY - lastY;\n lastX = e.clientX;\n lastY = e.clientY;\n applyPos();\n };\n const onUp = (e: PointerEvent): void => {\n dragging = false; pano.style.cursor = 'grab';\n try { pano.releasePointerCapture?.(e.pointerId); } catch { /* no active pointer */ }\n };\n pano.addEventListener('pointerdown', onDown);\n pano.addEventListener('pointermove', onMove);\n pano.addEventListener('pointerup', onUp);\n pano.addEventListener('pointercancel', onUp);\n\n let closed = false;\n let disposed = false;\n let fadeTimer = 0;\n const removeListeners = (): void => {\n pano.removeEventListener('pointerdown', onDown);\n pano.removeEventListener('pointermove', onMove);\n pano.removeEventListener('pointerup', onUp);\n pano.removeEventListener('pointercancel', onUp);\n window.removeEventListener('keydown', onKey);\n };\n const teardown = (): void => {\n if (fadeTimer) { window.clearTimeout(fadeTimer); fadeTimer = 0; }\n removeListeners();\n if (root.parentNode) root.parentNode.removeChild(root);\n };\n const close = (): void => {\n if (closed) return;\n closed = true;\n root.style.opacity = '0';\n // Guard the fade callback: a dispose() (or a retarget that disposes us) inside\n // the fade window clears the timer AND flips `disposed`, so a stray fire can\n // never call onClose into a newer flight/panorama.\n const done = (): void => {\n fadeTimer = 0;\n if (disposed) return;\n teardown();\n opts.onClose?.();\n };\n fadeTimer = window.setTimeout(done, fadeMs);\n };\n const onKey = (e: KeyboardEvent): void => {\n if (e.key === 'Escape') { e.stopPropagation(); close(); }\n };\n window.addEventListener('keydown', onKey);\n closeBtn.addEventListener('click', close);\n\n // Fade in on the next frame (0 → 1).\n requestAnimationFrame(() => { root.style.opacity = '1'; });\n\n return {\n close,\n dispose(): void { closed = true; disposed = true; teardown(); },\n };\n}\n","/**\n * view3d analytics — a tiny, decoupled event emitter for the venue view. The\n * caller (app/harness) supplies `onAnalytics`; this class owns the per-mount\n * state (first-orbit latch, panorama dwell timing) and, crucially, wraps EVERY\n * callback invocation in try/catch so a throwing analytics sink can never break\n * rendering. No DOM, no GL — unit-testable in isolation.\n */\n\nexport type Analytics3DCallback = (event: string, props?: Record<string, unknown>) => void;\n\nconst now = (): number =>\n (typeof performance !== 'undefined' && performance.now ? performance.now() : Date.now());\n\nexport class Analytics3D {\n private cb?: Analytics3DCallback;\n private orbitLatched = false;\n private panoramaOpenedAt = 0;\n\n constructor(cb?: Analytics3DCallback) {\n this.cb = cb;\n }\n\n /** The single guarded emit point — analytics must never throw into the loop. */\n private emit(event: string, props?: Record<string, unknown>): void {\n if (!this.cb) return;\n try {\n this.cb(event, props);\n } catch {\n /* analytics sink threw — swallow so rendering is never affected */\n }\n }\n\n opened(seats: number, hasHeights: boolean): void {\n this.emit('3d_opened', { seats, hasHeights });\n }\n\n /** First user-driven orbit/dolly per mount only (the intro ease is not user\n * input, so callers must gate this on real pointer/wheel gestures). */\n orbitEngaged(): void {\n if (this.orbitLatched) return;\n this.orbitLatched = true;\n this.emit('3d_orbit_engaged');\n }\n\n seatPicked(seatId: string, sectionId: string | undefined): void {\n this.emit('3d_seat_picked', { seatId, sectionId });\n }\n\n cinematicPlayed(durationMs: number): void {\n this.emit('3d_cinematic_played', { durationMs, reducedMotion: false });\n }\n\n cinematicSkipped(): void {\n this.emit('3d_cinematic_skipped', { reducedMotion: true });\n }\n\n cinematicCancelled(): void {\n this.emit('3d_cinematic_cancelled');\n }\n\n panoramaOpened(): void {\n this.panoramaOpenedAt = now();\n this.emit('3d_panorama_opened');\n }\n\n panoramaClosed(): void {\n const viewMs = this.panoramaOpenedAt ? Math.round(now() - this.panoramaOpenedAt) : 0;\n this.panoramaOpenedAt = 0;\n this.emit('3d_panorama_closed', { viewMs });\n }\n}\n"],"mappings":"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","names":["Quat","Vec3","norm","now","topY","sub","Mesh","Transform","Transform","Mesh","Vec3","Vec3","now","DEG","Vec3","Quat"]}
1
+ {"version":3,"sources":["../../src/view3d/index.ts","../../src/view3d/gl/context.ts","../../src/view3d/palette.ts","../../src/view3d/camera/orbit.ts","../../src/view3d/loop.ts","../../src/view3d/lod.ts","../../src/view3d/scene/geometry.ts","../../src/view3d/scene/seatInstances.ts","../../src/view3d/theme.ts","../../src/view3d/labels.ts","../../src/view3d/scene/sceneModel.ts","../../src/core/rake.ts","../../src/view3d/scene/surface.ts","../../src/view3d/scene/deckBands.ts","../../src/view3d/labelOverlay.ts","../../src/view3d/scene/build.ts","../../src/view3d/scene/materials.ts","../../src/view3d/pick/pickPipeline.ts","../../src/view3d/pick/encode.ts","../../src/view3d/pick/selection.ts","../../src/view3d/camera/cinematic.ts","../../src/view3d/camera/cinematicMath.ts","../../src/view3d/crossfade/panorama.ts","../../src/view3d/analytics.ts"],"sourcesContent":["/**\n * view3d — the sole dynamic-import boundary for the lazy OGL venue-view chunk.\n *\n * const { mountVenue3D } = await import('../view3d');\n * const handle = mountVenue3D(container, { doc, seats }, { onSeatPick, getSeatView });\n * await handle.flyToSeat(seatId);\n *\n * Read-only 3D of any chart, fed entirely from the existing height contract.\n * Slice 1: orbit camera, extruded tiers/stage/GA, instanced seat dots, sub-range\n * availability, dispose + context-loss survival. Slice 2: GPU color-pick. Slice\n * 3: the fly-to-seat cinematic that dissolves into the view-from-seat panorama.\n */\n\nimport { Quat, Vec3 } from 'ogl';\nimport type { ChartDoc, ExpandedSeat } from '../core/types';\nimport { GLContext } from './gl/context';\nimport { OrbitCamera } from './camera/orbit';\nimport { RenderLoop, type RenderLoopStats } from './loop';\nimport { computeSeatLod } from './lod';\nimport { SEAT_DOT_RADIUS_M } from './scene/seatInstances';\nimport { buildSceneModel, type SceneModel, type SceneZone, type SceneFloor } from './scene/sceneModel';\nimport { LabelOverlay } from './labelOverlay';\nimport { buildGpuScene, type GpuScene } from './scene/build';\nimport { applySeatStates } from './scene/seatInstances';\nimport { PickPipeline } from './pick/pickPipeline';\nimport { pickPixelCoords } from './pick/encode';\nimport { diffSelection, mergeAvailabilityIntoSelection } from './pick/selection';\nimport { Cinematic, buildWaypoints, lookAtQuat, FLIGHT_DURATION_MS, FOV_END, type Vec3Arr } from './camera/cinematic';\nimport { mountPanorama, type PanoramaHandle, type SeatView } from './crossfade/panorama';\nimport { Analytics3D, type Analytics3DCallback } from './analytics';\nimport type { SeatState3D } from './palette';\n\nexport type { SeatState3D } from './palette';\nexport type { SeatView } from './crossfade/panorama';\nexport type { Analytics3DCallback } from './analytics';\nexport { buildSceneModel } from './scene/sceneModel';\n\n/** Seat eye height above its deck: SEATED_EYE_HEIGHT_M (1.2) − seat lift (0.18). */\nconst SEAT_EYE_ABOVE_DECK = 1.02;\n\nexport interface Venue3DInput {\n doc: ChartDoc;\n /** Expanded seats (from `expandChart`) — carry x/y + resolved eyeHeightM. */\n seats: ExpandedSeat[];\n /** Optional initial per-seat state (default all available). */\n initialState?: (seat: ExpandedSeat) => SeatState3D;\n}\n\nexport interface Venue3DOptions {\n /** Fired on a tap that hits a seat (GPU color-pick). Not fired on empty taps. */\n onSeatPick?: (seatId: string) => void;\n /**\n * Supplies the view-from-seat panorama for the cinematic hand-off. Decoupled:\n * the caller (app/harness) owns panorama generation; view3d never imports it.\n * Called at PICK time to pre-render, so flyToSeat has zero wait on landing.\n */\n getSeatView?: (seatId: string) => SeatView | Promise<SeatView>;\n /**\n * Decoupled analytics sink. Emits the venue-view journey: `3d_opened`,\n * `3d_orbit_engaged` (first user gesture), `3d_seat_picked`,\n * `3d_cinematic_played`/`_skipped`/`_cancelled`, `3d_panorama_opened`/`_closed`.\n * Every invocation is wrapped in try/catch — a throwing sink never breaks\n * rendering. Absent = no events emitted.\n */\n onAnalytics?: Analytics3DCallback;\n}\n\nexport interface Venue3DStats extends RenderLoopStats {\n drawCalls: number;\n seatCount: number;\n}\n\nexport interface Venue3DHandle {\n dispose(): void;\n setAvailability(updates: { seatId: string; state: SeatState3D }[]): void;\n setSelection(seatIds: string[]): void;\n /** Fly the camera from the overview into `seatId` and dissolve into its\n * view-from-seat panorama. Resolves at flight end; a drag cancels it, a second\n * call retargets, dispose resolves early. Reduced-motion → a short fade. */\n flyToSeat(seatId: string): Promise<void>;\n resize(): void;\n stats(): Venue3DStats;\n loseContextForTest(): void;\n /** Test hook: force (or clear) the reduced-motion path. */\n setReducedMotionForTest(value: boolean | null): void;\n /** The venue's zones (id, label, colour, seat count) in authored order. */\n zones(): SceneZone[];\n /**\n * Frame a zone: the camera moves to sit over that zone looking at what the\n * zone faces. Returns false for an unknown or empty zone.\n *\n * This is the navigation the venue's own structure implies — a buyer picks\n * \"Grand Circle\", not a set of coordinates — and it is what the 2D renderer's\n * farthest LOD rung already offers. Approaching from the zone's focal side\n * means the seats face the camera rather than presenting their backs.\n */\n focusZone(zoneId: string): boolean;\n /** The venue's floors (id, name, seat count) in authored order. */\n floors(): SceneFloor[];\n /**\n * Isolate one floor, or pass null to show the whole venue.\n *\n * Every shipped multi-floor chart puts its floors at the same base height and\n * takes relief from the sections, so all three of an opera house draw at once\n * and the balcony sits over the parterre. Unfocused floors are DIMMED rather\n * than hidden, so the buyer keeps the venue as context while looking at the\n * level they are booking. Returns false for an unknown index.\n */\n focusFloor(index: number | null): boolean;\n}\n\nconst DEG = Math.PI / 180;\nconst TAP_SLOP = 6;\nconst TAP_MS = 500;\n\nexport function mountVenue3D(\n container: HTMLElement,\n input: Venue3DInput,\n opts: Venue3DOptions = {},\n): Venue3DHandle {\n const model: SceneModel = buildSceneModel(input);\n const analytics = new Analytics3D(opts.onAnalytics);\n\n const seatIdByIndex: string[] = new Array(model.seats.count);\n for (const [id, idx] of model.seats.idToIndex) seatIdByIndex[idx] = id;\n\n // Seat → owning section (for the 3d_seat_picked event); resolved from the\n // expanded seats that already carry sectionId.\n const sectionIdBySeatId = new Map<string, string | undefined>();\n for (const s of input.seats) sectionIdBySeatId.set(s.id, s.sectionId);\n\n // Whether the chart carries any real 3D relief (authored heights/rake or\n // elevated floors) vs. degrading to flat slabs — reported with 3d_opened.\n const hasHeights = ((): boolean => {\n if (input.doc.floors?.some((f) => (f.baseHeightM ?? 0) > 0)) return true;\n const objs = input.doc.floors?.flatMap((f) => f.objects) ?? input.doc.objects;\n return objs.some((o) => o.type === 'section'\n && (((o as { height?: number }).height ?? 0) > 0 || ((o as { rake?: number }).rake ?? 0) > 0));\n })();\n\n let gpu: GpuScene | null = null;\n let pick: PickPipeline | null = null;\n let contextLost = false;\n let frozen = false; // GL render paused while the panorama is up\n let disposed = false;\n let selection = new Map<string, number>();\n let panorama: PanoramaHandle | null = null;\n const prefetch = new Map<string, Promise<SeatView>>();\n let flightGen = 0;\n let reducedForced: boolean | null = null;\n /** Focused floor, or -1 for the whole venue. Survives a context restore. */\n let focusedFloor = -1;\n\n const rebuildGpu = (): void => {\n gpu = buildGpuScene(glctx.gl, model);\n pick = new PickPipeline(glctx.renderer, gpu.seatGeometry, gpu.solidGeometry, model.seats.count);\n // Apply the chart's authored theme to everything outside the scene graph:\n // the clear colour (visible for a frame before the background draws, and on\n // any frame the scene does not cover) and the pick pass's restore.\n glctx.setClearColor(model.theme.background.top);\n pick.setRestoreClear(model.theme.background.top);\n // Seat size is authored too (`ChartTheme.seatScale`) — bigger seats for\n // charts with longer labels.\n gpu.seatProgram.uniforms.uSeatRadius.value = SEAT_DOT_RADIUS_M * model.theme.seatScale;\n // A context restore rebuilds the GPU scene, so re-apply the focused floor\n // rather than silently reverting the buyer to the whole venue.\n gpu.seatProgram.uniforms.uFocusFloor.value = focusedFloor;\n gpu.solidProgram.uniforms.uFocusFloor.value = focusedFloor;\n };\n\n const glctx = new GLContext(container, {\n onContextLost: () => {\n contextLost = true;\n loop.stop();\n gpu = null;\n pick = null;\n },\n onContextRestored: () => {\n rebuildGpu();\n contextLost = false;\n loop.requestRender();\n },\n });\n\n const orbit = new OrbitCamera(\n glctx.gl,\n glctx.canvas,\n () => loop.requestRender(),\n () => analytics.orbitEngaged(), // first real drag/wheel/pinch (not the intro ease)\n );\n // setAspect BEFORE frame so the fit clears the horizontal FOV too (centred with\n // margin on a wide designer canvas rather than parked low-left).\n orbit.setAspect(glctx.aspect);\n // Enter from the stage side (camera behind the stage, every tier facing you).\n // When the focal sits at the centre (in-the-round) there is no stage side —\n // fall back to the fixed architectural angle.\n const stageAzimuth = ((): number | undefined => {\n const dx = model.focalWorld[0] - model.bounds.center[0];\n const dz = model.focalWorld[2] - model.bounds.center[2];\n return Math.hypot(dx, dz) > model.bounds.radius * 0.12 ? Math.atan2(dx, dz) : undefined;\n })();\n orbit.frame(model.bounds, true, stageAzimuth);\n\n const cinematic = new Cinematic(orbit.camera);\n\n // Labels are DOM, projected from world anchors — see labels.ts for why. The\n // container must establish a positioning context or the overlay would anchor\n // to the page instead of the canvas.\n if (!container.style.position || container.style.position === 'static') {\n container.style.position = 'relative';\n }\n const labelOverlay = new LabelOverlay(container, {\n fontFamily: input.doc.theme?.fontFamily,\n ink: input.doc.theme?.textColor,\n });\n labelOverlay.setLabels(model.labels);\n\n rebuildGpu();\n\n const loop = new RenderLoop((/* dt */) => {\n if (contextLost || !gpu || frozen) return false;\n const flying = cinematic.active;\n const moving = flying ? cinematic.update(performance.now()) : orbit.update();\n\n const lod = computeSeatLod(orbit.currentDistance, model.bounds.radius);\n const u = gpu.seatProgram.uniforms;\n u.uSeatScale.value = lod.scale;\n u.uSeatFade.value = lod.fade;\n u.uPixelToWorld.value = (2 * Math.tan((orbit.camera.fov * DEG) / 2)) / Math.max(1, glctx.pixelHeight);\n\n glctx.renderer.render({ scene: gpu.background, clear: true });\n glctx.renderer.render({ scene: gpu.main, camera: orbit.camera, clear: false });\n // After the render, so the camera's matrices are the ones just drawn with —\n // projecting from stale matrices makes labels lag the venue by a frame.\n labelOverlay.update(\n orbit.camera.projectionViewMatrix as unknown as ArrayLike<number>,\n glctx.canvas.clientWidth || 1,\n glctx.canvas.clientHeight || 1,\n orbit.currentDistance,\n model.bounds.radius,\n );\n return moving;\n });\n\n const ro = typeof ResizeObserver !== 'undefined'\n ? new ResizeObserver(() => handle.resize())\n : null;\n ro?.observe(container);\n\n const setSelection = (ids: string[]): void => {\n const baseStateIndex = (id: string): number | undefined => {\n const idx = model.seats.idToIndex.get(id);\n return idx === undefined ? undefined : model.seats.iState[idx];\n };\n const { updates, next } = diffSelection(selection, ids, baseStateIndex);\n selection = next;\n if (updates.length) {\n const runs = applySeatStates(model.seats, updates);\n if (gpu) gpu.uploadSeatStateRuns(runs);\n loop.requestRender();\n }\n };\n\n // --- cinematic / panorama ---\n const reducedMotion = (): boolean => {\n if (reducedForced !== null) return reducedForced;\n return typeof window !== 'undefined' && !!window.matchMedia\n && window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n };\n\n const PREFETCH_CAP = 8;\n const ensureSeatView = (seatId: string): Promise<SeatView> | null => {\n if (!opts.getSeatView) return null;\n let p = prefetch.get(seatId);\n if (!p) {\n p = Promise.resolve(opts.getSeatView(seatId));\n prefetch.set(seatId, p);\n // Bound the cache (LRU-ish): drop the oldest inserted entries past the cap.\n while (prefetch.size > PREFETCH_CAP) {\n const oldest = prefetch.keys().next().value as string | undefined;\n if (oldest === undefined) break;\n prefetch.delete(oldest);\n }\n }\n return p;\n };\n\n const seatEyeWorld = (idx: number): Vec3Arr => [\n model.seats.iPosition[idx * 3],\n model.seats.iPosition[idx * 3 + 1] + SEAT_EYE_ABOVE_DECK,\n model.seats.iPosition[idx * 3 + 2],\n ];\n\n const placeCameraFinal = (finalPos: Vec3Arr, focal: Vec3Arr): void => {\n orbit.camera.position.set(finalPos[0], finalPos[1], finalPos[2]);\n orbit.camera.lookAt(new Vec3(focal[0], focal[1], focal[2]));\n orbit.camera.fov = FOV_END;\n orbit.camera.updateProjectionMatrix();\n };\n\n // --- arrival chip: the flight HOLDS in the live scene; the painted 360 is\n // an explicit tap away. (Owner call 2026-07-24: the real scene at the seat\n // IS the payoff; the generated panorama undersold it as an auto-landing.)\n let arriveChip: HTMLButtonElement | null = null;\n const removeArriveChip = (): void => {\n arriveChip?.remove();\n arriveChip = null;\n };\n const showArriveChip = (seatId: string, gen: number): void => {\n removeArriveChip();\n if (disposed || !opts.getSeatView) return; // no 360 source → nothing to offer\n const chip = document.createElement('button');\n chip.type = 'button';\n chip.textContent = '◉ View in 360°';\n chip.setAttribute('aria-label', `Open the 360° view from seat ${seatId}`);\n Object.assign(chip.style, {\n position: 'absolute', left: '50%', bottom: '18px', transform: 'translateX(-50%)',\n minHeight: '44px', padding: '10px 18px', borderRadius: '999px',\n background: 'rgba(12,18,32,0.78)', color: '#eef1f8',\n border: '1px solid rgba(150,165,205,0.4)', backdropFilter: 'blur(6px)',\n font: '600 13px/1 inherit', cursor: 'pointer', zIndex: '4',\n } as Partial<CSSStyleDeclaration>);\n chip.addEventListener('click', () => {\n if (disposed || gen !== flightGen) { removeArriveChip(); return; }\n removeArriveChip();\n void openPanorama(seatId, 400, gen);\n });\n container.appendChild(chip);\n arriveChip = chip;\n };\n\n // --- overview chip: always-available \"take me home\" control. Free orbit can\n // strand you behind the shell staring at walls; one tap glides back to the\n // stage-side 3/4 framing. (Owner: \"we don't have much control in 3D\".)\n const overviewChip = document.createElement('button');\n overviewChip.type = 'button';\n overviewChip.textContent = '⌂ Overview';\n overviewChip.setAttribute('aria-label', 'Return to the venue overview');\n Object.assign(overviewChip.style, {\n position: 'absolute', right: '14px', bottom: '18px',\n minHeight: '40px', padding: '8px 14px', borderRadius: '999px',\n background: 'rgba(12,18,32,0.72)', color: '#c9d4ea',\n border: '1px solid rgba(150,165,205,0.35)', backdropFilter: 'blur(6px)',\n font: '600 12.5px/1 inherit', cursor: 'pointer', zIndex: '4',\n } as Partial<CSSStyleDeclaration>);\n overviewChip.addEventListener('click', () => {\n if (disposed || frozen) return; // panorama owns the screen while frozen\n cancelFlight();\n removeArriveChip();\n orbit.frameSoft(model.bounds, stageAzimuth);\n loop.requestRender();\n });\n container.appendChild(overviewChip);\n\n const openPanorama = async (seatId: string, fadeMs: number, gen: number): Promise<void> => {\n const viewPromise = ensureSeatView(seatId);\n if (!viewPromise) { orbit.syncFromCamera(); return; } // no panorama source\n frozen = true;\n loop.stop(); // freeze the GL at the seat pose; panorama fades in over it\n let view: SeatView;\n try {\n view = await viewPromise;\n } catch {\n // Only unfreeze if we still own the flight — a retarget during the await\n // has already reset `frozen` and taken over the loop.\n if (!disposed && gen === flightGen) { frozen = false; orbit.resumeAfterFlight(model.focalWorld); loop.requestRender(); }\n return;\n }\n // Superseded during the await (retarget/cancel) or disposed: bail WITHOUT\n // touching frozen/loop — the newer flight owns the freeze state now, and\n // mounting this stale seat's panorama would be wrong.\n if (disposed || gen !== flightGen) return;\n removeArriveChip();\n panorama = mountPanorama(container, view, {\n fadeMs,\n seatLabel: seatId,\n onClose: () => {\n panorama = null;\n frozen = false;\n analytics.panoramaClosed();\n orbit.resumeAfterFlight(model.focalWorld);\n loop.requestRender();\n // Back in the live scene at the seat — offer the 360 again.\n showArriveChip(seatId, flightGen);\n },\n });\n analytics.panoramaOpened();\n };\n\n const cancelFlight = (): void => {\n flightGen++; // supersede any pending .then(openPanorama)\n if (cinematic.active) {\n cinematic.cancel();\n orbit.resumeAfterFlight(model.focalWorld);\n }\n };\n\n const flyToSeat = (seatId: string): Promise<void> => {\n if (disposed || !gpu) return Promise.resolve();\n const idx = model.seats.idToIndex.get(seatId);\n if (idx === undefined) return Promise.resolve();\n // Reset the freeze unconditionally: a previous flight may have set frozen=true\n // inside openPanorama's pre-await window without a panorama ever mounting.\n if (panorama) { panorama.dispose(); panorama = null; }\n frozen = false;\n\n const gen = ++flightGen;\n removeArriveChip();\n const seatEye = seatEyeWorld(idx);\n const focal = model.focalWorld;\n const start: Vec3Arr = [orbit.camera.position.x, orbit.camera.position.y, orbit.camera.position.z];\n const { waypoints, finalPos } = buildWaypoints(start, seatEye, focal, model.bounds.center, model.bounds.radius);\n\n if (reducedMotion()) {\n // a11y: no flight — snap straight to the seat pose in the live scene.\n placeCameraFinal(finalPos, focal);\n loop.requestRender();\n analytics.cinematicSkipped();\n if (!disposed) orbit.syncFromCamera();\n showArriveChip(seatId, gen);\n return Promise.resolve();\n }\n\n const startQuat = new Quat().copy(orbit.camera.quaternion);\n const endQuat = lookAtQuat(orbit.camera, finalPos, focal);\n loop.requestRender();\n return cinematic.start(waypoints, startQuat, endQuat).then(() => {\n if (disposed || gen !== flightGen) return; // disposed or superseded (retarget/cancel)\n analytics.cinematicPlayed(FLIGHT_DURATION_MS);\n // Arrive and HOLD in the live 3D scene — sitting in the crowd, looking at\n // the show, still free to orbit. The painted 360 is the chip, not the\n // landing: the real scene is the payoff moment.\n orbit.resumeAfterFlight(model.focalWorld);\n loop.requestRender();\n showArriveChip(seatId, gen);\n });\n };\n\n // --- Tap → pick / flight-cancel ---\n let downX = 0, downY = 0, downT = 0, downId = -1, moved = false, suppressTap = false;\n const onDown = (e: PointerEvent): void => {\n if (downId !== -1) return;\n downId = e.pointerId; downX = e.clientX; downY = e.clientY; downT = performance.now(); moved = false;\n // A press during a flight cancels it (damped stop) instead of picking.\n suppressTap = cinematic.active;\n if (cinematic.active) { analytics.cinematicCancelled(); cancelFlight(); }\n };\n const onMove = (e: PointerEvent): void => {\n if (e.pointerId !== downId) return;\n if (Math.hypot(e.clientX - downX, e.clientY - downY) > TAP_SLOP) moved = true;\n };\n const onUp = (e: PointerEvent): void => {\n if (e.pointerId !== downId) return;\n const isTap = !moved && performance.now() - downT < TAP_MS;\n downId = -1;\n if (suppressTap) { suppressTap = false; return; }\n if (!isTap || !gpu || !pick) return;\n pick.syncFromSeatProgram(gpu.seatProgram);\n const rect = glctx.canvas.getBoundingClientRect();\n const dpr = glctx.renderer.dpr;\n const { x, y } = pickPixelCoords(e.clientX, e.clientY, rect, dpr, glctx.gl.drawingBufferWidth, glctx.gl.drawingBufferHeight);\n const radius = Math.max(2, Math.round(8 * dpr));\n const idx = pick.pick(orbit.camera, x, y, radius);\n if (idx < 0 || idx >= seatIdByIndex.length) {\n if (selection.size) setSelection([]);\n return;\n }\n const seatId = seatIdByIndex[idx];\n if (selection.has(seatId) && selection.size === 1) setSelection([]);\n else setSelection([seatId]);\n ensureSeatView(seatId); // pre-render the panorama the moment the seat is picked\n analytics.seatPicked(seatId, sectionIdBySeatId.get(seatId));\n opts.onSeatPick?.(seatId);\n };\n glctx.canvas.addEventListener('pointerdown', onDown);\n glctx.canvas.addEventListener('pointermove', onMove);\n glctx.canvas.addEventListener('pointerup', onUp);\n glctx.canvas.addEventListener('pointercancel', onUp);\n\n loop.requestRender();\n\n const handle: Venue3DHandle = {\n setAvailability(updates) {\n const passthrough = mergeAvailabilityIntoSelection(selection, updates);\n const runs = applySeatStates(model.seats, passthrough);\n if (runs.length && gpu) gpu.uploadSeatStateRuns(runs);\n loop.requestRender();\n },\n setSelection,\n flyToSeat,\n resize() {\n const { width, height } = glctx.resize();\n orbit.setAspect(width / Math.max(1, height));\n loop.requestRender();\n },\n stats() {\n return {\n ...loop.stats(),\n drawCalls: gpu ? gpu.drawCalls : 0,\n seatCount: model.seatCount,\n };\n },\n loseContextForTest() {\n glctx.simulateContextLossCycle();\n },\n floors(): SceneFloor[] {\n return model.floors;\n },\n focusFloor(index: number | null): boolean {\n if (index !== null && !model.floors.some((f) => f.index === index)) return false;\n const value = index ?? -1;\n if (gpu) {\n gpu.seatProgram.uniforms.uFocusFloor.value = value;\n gpu.solidProgram.uniforms.uFocusFloor.value = value;\n }\n focusedFloor = value;\n if (index !== null) {\n const f = model.floors[index];\n if (f.seatCount > 0) {\n cinematic.cancel();\n orbit.frame({ center: f.center, radius: f.radius * 1.25 });\n }\n }\n loop.requestRender();\n return true;\n },\n zones(): SceneZone[] {\n return model.zones;\n },\n focusZone(zoneId: string): boolean {\n const zone = model.zones.find((z) => z.id === zoneId);\n if (!zone || zone.seatCount === 0) return false;\n cinematic.cancel();\n // Approach from the side the zone faces, so its seats present their fronts\n // rather than their backs — the same reasoning as the venue's intro shot.\n const dx = zone.focalWorld[0] - zone.center[0];\n const dz = zone.focalWorld[2] - zone.center[2];\n const azimuth = Math.hypot(dx, dz) > zone.radius * 0.12 ? Math.atan2(dx, dz) : undefined;\n // Padded past the zone's own radius: framing exactly to its edge reads as\n // cropped, and a buyer needs the neighbouring geometry to know where in the\n // venue they have landed.\n orbit.frame({ center: zone.center, radius: zone.radius * 1.25 }, false, azimuth);\n loop.requestRender();\n return true;\n },\n setReducedMotionForTest(value) {\n reducedForced = value;\n },\n dispose() {\n disposed = true;\n removeArriveChip();\n overviewChip.remove();\n cancelFlight();\n loop.stop();\n labelOverlay.dispose();\n ro?.disconnect();\n if (panorama) { panorama.dispose(); panorama = null; }\n glctx.canvas.removeEventListener('pointerdown', onDown);\n glctx.canvas.removeEventListener('pointermove', onMove);\n glctx.canvas.removeEventListener('pointerup', onUp);\n glctx.canvas.removeEventListener('pointercancel', onUp);\n orbit.dispose();\n if (pick) pick.dispose();\n if (gpu) gpu.dispose();\n gpu = null;\n pick = null;\n glctx.dispose();\n },\n };\n\n analytics.opened(model.seatCount, hasHeights);\n return handle;\n}\n","/**\n * OGL renderer + canvas lifecycle. Owns the single WebGL2 context (reused across\n * open/close so an embed never exhausts the browser's ~16-context cap), DPR\n * capping, resize, and WebGL context-loss survival.\n *\n * Context loss is handled by preventing the default (so the browser will restore)\n * and delegating rebuild to the caller: the JS-side SceneModel is the source of\n * truth, so `onContextRestored` re-uploads all GPU resources from it.\n */\n\nimport { Renderer } from 'ogl';\nimport type { OGLRenderingContext } from 'ogl';\nimport { BACKGROUND } from '../palette';\n\nexport interface GLContextOptions {\n onContextLost: () => void;\n onContextRestored: () => void;\n}\n\n/** DPR ceiling: 2.0, or 1.5 on low-memory devices (fragment cost is DPR²). */\nfunction computeDpr(): number {\n const raw = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n const mem = (navigator as unknown as { deviceMemory?: number }).deviceMemory;\n const cap = typeof mem === 'number' && mem <= 4 ? 1.5 : 2.0;\n return Math.min(raw, cap);\n}\n\nexport class GLContext {\n readonly renderer: Renderer;\n readonly gl: OGLRenderingContext;\n readonly canvas: HTMLCanvasElement;\n private container: HTMLElement;\n /** Current clear colour, so a context restore repaints the themed background. */\n private clearRgb: [number, number, number] = [0, 0, 0];\n private lostHandler: (e: Event) => void;\n private restoredHandler: () => void;\n\n /**\n * Repaint the clear colour from the chart's theme.\n *\n * The clear shows for one frame before the background triangle draws, and on\n * any frame the scene does not cover — so leaving it at the library default\n * flashes SeatLayer grey into a white-labelled venue.\n */\n setClearColor(rgb: readonly number[]): void {\n this.clearRgb = [rgb[0], rgb[1], rgb[2]];\n this.gl.clearColor(rgb[0], rgb[1], rgb[2], 1);\n }\n\n /** The clear colour currently set (the pick pass restores through this). */\n get clearColor(): readonly number[] {\n return this.clearRgb;\n }\n\n constructor(container: HTMLElement, opts: GLContextOptions) {\n this.container = container;\n this.canvas = document.createElement('canvas');\n this.canvas.style.display = 'block';\n this.canvas.style.width = '100%';\n this.canvas.style.height = '100%';\n this.canvas.style.touchAction = 'none';\n\n this.renderer = new Renderer({\n canvas: this.canvas,\n dpr: computeDpr(),\n alpha: false,\n antialias: false,\n depth: true,\n stencil: false,\n powerPreference: 'high-performance',\n webgl: 2,\n });\n this.gl = this.renderer.gl;\n this.clearRgb = [BACKGROUND.top[0], BACKGROUND.top[1], BACKGROUND.top[2]];\n this.gl.clearColor(this.clearRgb[0], this.clearRgb[1], this.clearRgb[2], 1);\n\n container.appendChild(this.canvas);\n\n\n\n this.lostHandler = (e: Event) => {\n e.preventDefault();\n opts.onContextLost();\n };\n this.restoredHandler = () => opts.onContextRestored();\n this.canvas.addEventListener('webglcontextlost', this.lostHandler, false);\n this.canvas.addEventListener('webglcontextrestored', this.restoredHandler, false);\n\n this.resize();\n }\n\n /** Match the drawing buffer to the container's CSS box. */\n resize(): { width: number; height: number } {\n const w = Math.max(1, this.container.clientWidth || this.canvas.clientWidth || 1);\n const h = Math.max(1, this.container.clientHeight || this.canvas.clientHeight || 1);\n this.renderer.setSize(w, h);\n return { width: w, height: h };\n }\n\n get pixelHeight(): number {\n return this.renderer.height * this.renderer.dpr;\n }\n\n get aspect(): number {\n return this.renderer.width / Math.max(1, this.renderer.height);\n }\n\n dispose(): void {\n this.canvas.removeEventListener('webglcontextlost', this.lostHandler, false);\n this.canvas.removeEventListener('webglcontextrestored', this.restoredHandler, false);\n const ext = this.gl.getExtension('WEBGL_lose_context');\n if (ext) ext.loseContext();\n if (this.canvas.parentNode) this.canvas.parentNode.removeChild(this.canvas);\n }\n\n /**\n * Test hook: force a full loss→restore cycle. `restoreContext()` must be called\n * only AFTER the browser has dispatched `webglcontextlost` (calling it too soon\n * makes the browser drop the restore request), so we sequence it off a one-shot\n * listener rather than a fixed timeout.\n */\n simulateContextLossCycle(): void {\n const ext = this.gl.getExtension('WEBGL_lose_context') as\n | { loseContext(): void; restoreContext?: () => void }\n | null;\n if (!ext) return;\n ext.loseContext();\n // Chrome drops a restore requested too soon after loseContext(); a short\n // delay lets the loss settle before we ask for the context back.\n setTimeout(() => { if (ext.restoreContext) ext.restoreContext(); }, 300);\n }\n}\n","/**\n * view3d palette + seat-state model — the single source of colour truth for the\n * OGL venue view. Pure data (no GPU, no DOM) so the scene builder and the unit\n * tests share one definition. Colours are linear-ish RGB triplets in 0..1.\n *\n * Look brief (docs/3d-usp-strategy §3): desaturated cool greys for structure,\n * one warm accent for the stage, availability colours only on seats.\n */\n\nexport type SeatState3D = 'available' | 'held' | 'sold' | 'selected' | 'dimmed';\n\n/** Fixed LUT order — the per-instance `iState` float indexes this array, and the\n * fragment shader's `uStateColors` uniform is uploaded in exactly this order. */\nexport const SEAT_STATES: SeatState3D[] = ['available', 'held', 'sold', 'selected', 'dimmed'];\n\nexport function seatStateIndex(state: SeatState3D): number {\n const i = SEAT_STATES.indexOf(state);\n return i < 0 ? 0 : i;\n}\n\nexport type RGB = [number, number, number];\n\n/** Availability colours — the only saturated colours in the scene. */\nexport const SEAT_STATE_COLORS: Record<SeatState3D, RGB> = {\n available: [0.24, 0.82, 0.52],\n held: [0.95, 0.66, 0.22],\n sold: [0.34, 0.39, 0.45],\n selected: [0.24, 0.74, 1.0],\n dimmed: [0.28, 0.32, 0.37],\n};\n\n/** Flat LUT (5 × vec3) for the seat fragment shader uniform. */\nexport function seatStateColorLUT(): number[] {\n const out: number[] = [];\n for (const s of SEAT_STATES) out.push(...SEAT_STATE_COLORS[s]);\n return out;\n}\n\n/** Colour for a state index (from `iState`) — used to fill the per-instance\n * `iColor` attribute CPU-side, avoiding a dynamically-indexed array uniform. */\nexport function seatStateColorByIndex(index: number): RGB {\n const state = SEAT_STATES[index] ?? 'available';\n return SEAT_STATE_COLORS[state];\n}\n\n/** Structure palette — cool desaturated greys + one warm stage accent. */\nexport const STRUCTURE = {\n ground: [0.07, 0.085, 0.11] as RGB,\n tierTop: [0.24, 0.28, 0.34] as RGB,\n tierWall: [0.17, 0.20, 0.25] as RGB,\n stageTop: [0.42, 0.36, 0.26] as RGB, // warm, slightly emissive read\n stageWall: [0.26, 0.22, 0.16] as RGB,\n decorTop: [0.22, 0.25, 0.29] as RGB,\n decorWall: [0.15, 0.17, 0.20] as RGB,\n gaTop: [0.24, 0.28, 0.33] as RGB,\n gaWall: [0.16, 0.19, 0.23] as RGB,\n /** Exhibition / trade-show booth stand. */\n boothTop: [0.30, 0.33, 0.38] as RGB,\n boothWall: [0.20, 0.23, 0.27] as RGB,\n /** Banquet table top — warmer, so a laid table reads apart from structure. */\n tableTop: [0.38, 0.34, 0.29] as RGB,\n tableWall: [0.24, 0.21, 0.18] as RGB,\n} as const;\n\n/** Background vertical gradient (matches the app's dark UI). */\nexport const BACKGROUND = {\n top: [0.05, 0.06, 0.08] as RGB,\n bottom: [0.10, 0.12, 0.15] as RGB,\n};\n\n/** Parse `#rrggbb` (or `#rgb`) to linear-ish 0..1 RGB; null on anything else. */\nexport function hexToRgb(hex: string | undefined): RGB | null {\n if (!hex) return null;\n let h = hex.trim();\n if (h[0] === '#') h = h.slice(1);\n if (h.length === 3) h = h.split('').map((c) => c + c).join('');\n if (h.length !== 6 || /[^0-9a-fA-F]/.test(h)) return null;\n const n = parseInt(h, 16);\n return [((n >> 16) & 255) / 255, ((n >> 8) & 255) / 255, (n & 255) / 255];\n}\n\n/** Mix two colours (a*(1-t) + b*t). */\nexport function mix(a: RGB, b: RGB, t: number): RGB {\n return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t];\n}\n\n/** Desaturate toward its own luma by `amount` (0 = unchanged, 1 = grey). */\nexport function desaturate(c: RGB, amount: number): RGB {\n const l = 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2];\n return mix(c, [l, l, l], amount);\n}\n\n/** Scale a colour by a scalar (baked vertex AO), clamped to [0,1]. */\nexport function scaleRgb(c: RGB, k: number): RGB {\n return [Math.min(1, c[0] * k), Math.min(1, c[1] * k), Math.min(1, c[2] * k)];\n}\n","/**\n * Orbit + dolly camera, damped, always centred on the venue focal point. Drag =\n * azimuth/polar; wheel/pinch = dolly. Touch: 1-finger orbit, 2-finger pinch\n * dolly (+ pan). No desktop pan for v1. Polar clamped [15°,80°]; distance clamped\n * to bounds-derived limits; initial framing = a 3/4 view fitted to bounds.\n */\n\nimport { Camera, Vec3 } from 'ogl';\nimport type { OGLRenderingContext } from 'ogl';\n\nconst DEG = Math.PI / 180;\nconst POLAR_MIN = 15 * DEG;\nconst POLAR_MAX = 80 * DEG;\nconst DAMP = 0.12;\nconst FOV = 35;\n/** Fit multiplier past a tight bounds-sphere fit. The 3/4 tilt makes the near\n * ground edge overhang below the fitted sphere, so a wide-shallow layout needs\n * more than a nominal 10% or its front row clips — this clears it while keeping\n * the venue centred with a comfortable margin. */\nconst FRAME_MARGIN = 1.25;\n\nexport interface OrbitBounds {\n center: [number, number, number];\n radius: number;\n}\n\nexport class OrbitCamera {\n readonly camera: Camera;\n readonly fovY = FOV;\n private target = new Vec3();\n private azimuth = -30 * DEG;\n private polar = 55 * DEG;\n private distance = 10;\n private azT = -30 * DEG;\n private polT = 55 * DEG;\n private distT = 10;\n private minDist = 1;\n private maxDist = 100;\n private canvas: HTMLElement;\n private requestRender: () => void;\n /** Fired on the FIRST real user-driven orbit/dolly gesture (drag/wheel/pinch),\n * latched so it can drive a one-shot analytics event. Not the intro ease. */\n private onGesture?: () => void;\n private gestureFired = false;\n\n private dragging = false;\n private lastX = 0;\n private lastY = 0;\n private activePointers = new Map<number, { x: number; y: number }>();\n private pinchDist = 0;\n\n private onPointerDown: (e: PointerEvent) => void;\n private onPointerMove: (e: PointerEvent) => void;\n private onPointerUp: (e: PointerEvent) => void;\n private onWheel: (e: WheelEvent) => void;\n\n constructor(gl: OGLRenderingContext, canvas: HTMLElement, requestRender: () => void, onGesture?: () => void) {\n this.camera = new Camera(gl, { fov: FOV, near: 0.1, far: 5000, aspect: 1 });\n this.canvas = canvas;\n this.requestRender = requestRender;\n this.onGesture = onGesture;\n\n this.onPointerDown = (e) => {\n // Guard: a synthetic/stale pointer id has no active pointer to capture.\n try { this.canvas.setPointerCapture?.(e.pointerId); } catch { /* no active pointer */ }\n this.activePointers.set(e.pointerId, { x: e.clientX, y: e.clientY });\n if (this.activePointers.size === 1) {\n this.dragging = true;\n this.lastX = e.clientX;\n this.lastY = e.clientY;\n } else if (this.activePointers.size === 2) {\n this.dragging = false;\n this.pinchDist = this.currentPinchDistance();\n }\n };\n this.onPointerMove = (e) => {\n if (!this.activePointers.has(e.pointerId)) return;\n this.activePointers.set(e.pointerId, { x: e.clientX, y: e.clientY });\n if (this.activePointers.size >= 2) {\n const d = this.currentPinchDistance();\n // Fingers apart (d grows) → zoom in (distance shrinks).\n if (this.pinchDist > 0) { this.dollyBy(Math.exp((this.pinchDist - d) * 0.005)); this.fireGesture(); }\n this.pinchDist = d;\n return;\n }\n if (!this.dragging) return;\n const dx = e.clientX - this.lastX;\n const dy = e.clientY - this.lastY;\n this.lastX = e.clientX;\n this.lastY = e.clientY;\n if (dx !== 0 || dy !== 0) this.fireGesture();\n this.azT -= dx * 0.006;\n this.polT = Math.max(POLAR_MIN, Math.min(POLAR_MAX, this.polT - dy * 0.006));\n this.requestRender();\n };\n this.onPointerUp = (e) => {\n this.activePointers.delete(e.pointerId);\n try { this.canvas.releasePointerCapture?.(e.pointerId); } catch { /* no active pointer */ }\n if (this.activePointers.size < 2) this.pinchDist = 0;\n if (this.activePointers.size === 0) this.dragging = false;\n };\n this.onWheel = (e) => {\n e.preventDefault();\n // Normalise wheel delta across px / line / page modes to ~±1 per notch,\n // then zoom multiplicatively so every notch makes a real difference.\n const unit = e.deltaMode === 1 ? 16 : e.deltaMode === 2 ? 100 : 1;\n const norm = (e.deltaY * unit) / 100;\n this.dollyBy(Math.exp(norm * 0.4));\n this.fireGesture();\n };\n\n canvas.addEventListener('pointerdown', this.onPointerDown);\n canvas.addEventListener('pointermove', this.onPointerMove);\n canvas.addEventListener('pointerup', this.onPointerUp);\n canvas.addEventListener('pointercancel', this.onPointerUp);\n canvas.addEventListener('wheel', this.onWheel, { passive: false });\n }\n\n /** One-shot: notify the first real user gesture (drives 3d_orbit_engaged). */\n private fireGesture(): void {\n if (this.gestureFired) return;\n this.gestureFired = true;\n this.onGesture?.();\n }\n\n private currentPinchDistance(): number {\n const pts = [...this.activePointers.values()];\n if (pts.length < 2) return 0;\n return Math.hypot(pts[0].x - pts[1].x, pts[0].y - pts[1].y);\n }\n\n /** Multiply target distance by `factor` (proportional zoom — same feel near\n * and far), clamped so you can swoop right down among the seats. */\n private dollyBy(factor: number): void {\n this.distT = Math.max(this.minDist, Math.min(this.maxDist, this.distT * factor));\n this.requestRender();\n }\n\n /**\n * Fit a flattering 3/4 view to the bounds sphere. With `intro`, the camera\n * STARTS nearly top-down (matching the 2D map's orientation) and further out,\n * then the damped `update()` eases it up into the 3/4 architectural angle and\n * dollies in — the venue \"stands up\" instead of teleporting (~600ms).\n */\n frame(bounds: OrbitBounds, intro = false, stageAzimuth?: number): void {\n this.target.set(bounds.center[0], bounds.center[1], bounds.center[2]);\n const r = Math.max(1, bounds.radius);\n // Aspect-aware fit: the bounds sphere must clear BOTH the vertical and the\n // (aspect-narrowed) horizontal FOV, so a wide designer canvas frames the\n // chart centred with margin instead of parking it low-left. `setAspect` must\n // run before `frame` for the horizontal term to be correct.\n const halfV = (this.fovY * DEG) / 2;\n const aspect = this.camera.aspect || 1;\n const halfH = Math.atan(Math.tan(halfV) * aspect);\n const fit = Math.max(r / Math.tan(halfV), r / Math.tan(halfH));\n // Enter from the STAGE side when the caller knows where the stage is: the\n // camera stands behind the stage looking into the bowl, so every tier faces\n // you on arrival. A fixed azimuth made the entry view a coin flip — charts\n // oriented the other way opened staring at the back of the shell.\n this.azT = stageAzimuth ?? -30 * DEG;\n this.polT = 55 * DEG;\n this.distT = fit * FRAME_MARGIN;\n // Low min so you can swoop down close enough that seat dots are big, tappable\n // targets (\"into your section\"); generous max to pull right back out.\n this.minDist = Math.max(2, r * 0.12);\n this.maxDist = fit * 4;\n if (intro) {\n this.azimuth = this.azT; // no spin — just tilt up + dolly in\n this.polar = 12 * DEG; // near top-down, like the flat 2D view\n this.distance = this.distT * 1.7;\n } else {\n this.azimuth = this.azT;\n this.polar = this.polT;\n this.distance = this.distT;\n }\n this.applyPosition();\n }\n\n setAspect(aspect: number): void {\n this.camera.perspective({ aspect });\n }\n\n /**\n * Damped return to the framed overview from wherever the camera is now (a\n * seat, behind the shell, anywhere): re-pivot on the venue centre without\n * moving the camera, then glide targets back to the 3/4 architectural pose.\n */\n frameSoft(bounds: OrbitBounds, stageAzimuth?: number): void {\n this.target.set(bounds.center[0], bounds.center[1], bounds.center[2]);\n this.syncFromCamera(); // re-derive pose around the new pivot — no snap\n this.camera.perspective({ fov: this.fovY, aspect: this.camera.aspect });\n const r = Math.max(1, bounds.radius);\n const halfV = (this.fovY * DEG) / 2;\n const aspect = this.camera.aspect || 1;\n const halfH = Math.atan(Math.tan(halfV) * aspect);\n const fit = Math.max(r / Math.tan(halfV), r / Math.tan(halfH));\n this.azT = stageAzimuth ?? this.azimuth;\n this.polT = 55 * DEG;\n this.distT = fit * FRAME_MARGIN;\n }\n\n /** Damp toward targets; returns true while still moving. */\n update(): boolean {\n const da = this.azT - this.azimuth;\n const dp = this.polT - this.polar;\n const dd = this.distT - this.distance;\n const moving = Math.abs(da) > 1e-4 || Math.abs(dp) > 1e-4 || Math.abs(dd) > 1e-4;\n this.azimuth += da * DAMP;\n this.polar += dp * DAMP;\n this.distance += dd * DAMP;\n if (moving) this.applyPosition();\n return moving;\n }\n\n /** Distance from camera to target (for LOD). */\n get currentDistance(): number {\n return this.distance;\n }\n\n /**\n * Re-derive the orbit's spherical state from the camera's CURRENT pose (after a\n * cinematic flight leaves it somewhere arbitrary), so a subsequent drag damps\n * from where it actually is with no snap. Does not move the camera.\n */\n syncFromCamera(): void {\n const dx = this.camera.position.x - this.target.x;\n const dy = this.camera.position.y - this.target.y;\n const dz = this.camera.position.z - this.target.z;\n const dist = Math.hypot(dx, dy, dz) || 1;\n const polar = Math.max(POLAR_MIN, Math.min(POLAR_MAX, Math.acos(Math.max(-1, Math.min(1, dy / dist)))));\n // Distance is NOT clamped here: a flight can park closer than minDist, and\n // clamping would jump the camera radially on the very first drag. The clamp\n // applies lazily from the next user-driven dolly (see dollyBy).\n this.distance = this.distT = dist;\n this.polar = this.polT = polar;\n this.azimuth = this.azT = Math.atan2(dx, dz);\n }\n\n /** Point the orbit pivot at a new world target without moving the camera. */\n setTarget(target: [number, number, number]): void {\n this.target.set(target[0], target[1], target[2]);\n }\n\n /** Restore the base FOV (a flight ends pushed-in) and re-sync orbit state. A\n * flight ends looking at `target` (the venue focal), so re-pivot there first —\n * otherwise the first drag would `lookAt(bounds.center)` and pop the aim. */\n resumeAfterFlight(target?: [number, number, number]): void {\n this.camera.perspective({ fov: this.fovY, aspect: this.camera.aspect });\n if (target) this.target.set(target[0], target[1], target[2]);\n this.syncFromCamera();\n }\n\n private applyPosition(): void {\n const sp = Math.sin(this.polar);\n const x = this.target.x + this.distance * sp * Math.sin(this.azimuth);\n const y = this.target.y + this.distance * Math.cos(this.polar);\n const z = this.target.z + this.distance * sp * Math.cos(this.azimuth);\n this.camera.position.set(x, y, z);\n this.camera.lookAt(this.target);\n }\n\n dispose(): void {\n this.canvas.removeEventListener('pointerdown', this.onPointerDown);\n this.canvas.removeEventListener('pointermove', this.onPointerMove);\n this.canvas.removeEventListener('pointerup', this.onPointerUp);\n this.canvas.removeEventListener('pointercancel', this.onPointerUp);\n this.canvas.removeEventListener('wheel', this.onWheel);\n this.activePointers.clear();\n }\n}\n","/**\n * Dirty-flag render loop. A frame is drawn only while the camera is moving /\n * damping or an availability update arrived; when idle, no rAF is scheduled at\n * all (zero CPU/GPU when parked). Tracks an FPS EMA over frames actually\n * rendered — it reads as \"idle\" when nothing is scheduled.\n */\n\nexport interface RenderLoopStats {\n fps: number;\n rendered: number;\n idle: boolean;\n}\n\nexport class RenderLoop {\n private frame: (dt: number) => boolean;\n private rafId = 0;\n private running = false;\n private lastTime = 0;\n private fpsEma = 0;\n private rendered = 0;\n\n /** `frame(dt)` renders one frame and returns true if another is needed. */\n constructor(frame: (dt: number) => boolean) {\n this.frame = frame;\n }\n\n requestRender(): void {\n if (this.running) return;\n this.running = true;\n this.lastTime = 0;\n this.rafId = requestAnimationFrame(this.tick);\n }\n\n private tick = (now: number): void => {\n const dt = this.lastTime ? (now - this.lastTime) / 1000 : 1 / 60;\n this.lastTime = now;\n if (dt > 0) {\n const instFps = 1 / dt;\n this.fpsEma = this.fpsEma ? this.fpsEma * 0.9 + instFps * 0.1 : instFps;\n }\n this.rendered++;\n const again = this.frame(dt);\n if (again) {\n this.rafId = requestAnimationFrame(this.tick);\n } else {\n this.running = false;\n this.rafId = 0;\n }\n };\n\n stats(): RenderLoopStats {\n return { fps: this.running ? Math.round(this.fpsEma) : 0, rendered: this.rendered, idle: !this.running };\n }\n\n stop(): void {\n if (this.rafId) cancelAnimationFrame(this.rafId);\n this.rafId = 0;\n this.running = false;\n }\n}\n","/**\n * Distance-based seat level-of-detail (v1). Beyond a bounds-derived threshold the\n * dots shrink and fade toward the tier colour (both uniform-driven, no geometry\n * change); below it they stay full. The POINTS fallback rung can come later.\n */\n\nexport interface SeatLod {\n /** Multiplier on the seat world radius (1 = full). */\n scale: number;\n /** Fade toward the tier/fade colour (0 = pure state colour). */\n fade: number;\n}\n\nexport function computeSeatLod(distance: number, radius: number): SeatLod {\n const near = radius * 1.4;\n const far = radius * 3.2;\n if (distance <= near) return { scale: 1, fade: 0 };\n const t = Math.min(1, (distance - near) / Math.max(1e-3, far - near));\n return {\n scale: 1 - t * 0.4,\n fade: t * 0.55,\n };\n}\n","/**\n * Pure geometry primitives for the view3d scene — no OGL, no DOM, so the whole\n * scene-model builder is unit-testable in a plain runtime.\n *\n * Coordinate convention: chart units (x, y) map to world metres as\n * worldX = x * METRES_PER_CHART_UNIT\n * worldZ = y * METRES_PER_CHART_UNIT\n * worldY = up (height in metres)\n * i.e. the chart's audience-depth (+y) becomes world +Z, and Y is the vertical.\n */\n\nimport earcut from 'earcut';\nimport polygonClipping from 'polygon-clipping';\nimport type { Point } from '../../core/types';\nimport { CHART_UNITS_PER_METRE, METRES_PER_CHART_UNIT } from '../../core/units';\nimport type { RGB } from '../palette';\n\nexport const M = METRES_PER_CHART_UNIT;\n\nexport interface MeshData {\n /** Non-indexed triangle soup: 3 floats per vertex. */\n position: Float32Array;\n normal: Float32Array;\n /** Baked vertex colour incl. AO, 3 floats per vertex. */\n color: Float32Array;\n /**\n * Owning floor index per vertex.\n *\n * Lets one merged mesh be dimmed per floor without splitting it into a draw\n * call per floor. A multi-floor chart (the opera house has three) draws every\n * floor at once, so the balcony sits over the parterre and hides it; isolating\n * one is the only way to look at the level you are actually booking.\n */\n floor: Float32Array;\n /** Vertex count (position.length / 3). */\n count: number;\n}\n\n/**\n * Thinnest triangle the mesh will accept, world metres.\n *\n * Measured as the shortest altitude. Every path that triangulates a polygon can\n * emit degenerate slivers from inputs that are geometrically valid: earcut fans\n * near-collinear runs, and a boolean union of overlapping shapes leaves\n * zero-width SLITS where two boundaries nearly coincide — measured at 9.74 m long\n * and 0.5 mm wide when the block footprints were introduced, with aspect ratios\n * to 4.6 million. Such a triangle has a numerically meaningless face normal and\n * flat-shades as a dark hairline across the surface.\n *\n * Cleaning each producer separately does not work: the slit's vertices are far\n * apart, so point-merging and Douglas-Peucker both legitimately keep them. A\n * guard at the point of emission catches every producer at once, and dropping a\n * 2 mm-wide triangle cannot open a visible hole — it is sub-pixel at any view\n * that could ever show it.\n */\nconst MIN_TRI_ALTITUDE_M = 0.002;\n\n/** True when a triangle is too thin to contribute anything but shading noise. */\nfunction isDegenerate(\n p0: readonly [number, number, number],\n p1: readonly [number, number, number],\n p2: readonly [number, number, number],\n): boolean {\n const e0 = Math.hypot(p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]);\n const e1 = Math.hypot(p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]);\n const e2 = Math.hypot(p0[0] - p2[0], p0[1] - p2[1], p0[2] - p2[2]);\n const longest = Math.max(e0, e1, e2);\n if (longest < 1e-9) return true;\n const s = (e0 + e1 + e2) / 2;\n const area = Math.sqrt(Math.max(0, s * (s - e0) * (s - e1) * (s - e2)));\n return (2 * area) / longest < MIN_TRI_ALTITUDE_M;\n}\n\n/** Accumulates flat-shaded, per-vertex-coloured triangles. */\nexport class MeshBuilder {\n private pos: number[] = [];\n private nor: number[] = [];\n private col: number[] = [];\n private flr: number[] = [];\n /** Floor index stamped onto every triangle emitted from now on. */\n private currentFloor = 0;\n\n /** Stamp subsequent triangles as belonging to `index`. */\n setFloor(index: number): void {\n this.currentFloor = index;\n }\n\n /** One triangle with a shared (flat) normal and per-vertex colours. */\n tri(\n p0: readonly [number, number, number],\n p1: readonly [number, number, number],\n p2: readonly [number, number, number],\n n: readonly [number, number, number],\n c0: RGB,\n c1: RGB = c0,\n c2: RGB = c0,\n ): void {\n if (isDegenerate(p0, p1, p2)) return;\n this.pos.push(p0[0], p0[1], p0[2], p1[0], p1[1], p1[2], p2[0], p2[1], p2[2]);\n this.nor.push(n[0], n[1], n[2], n[0], n[1], n[2], n[0], n[1], n[2]);\n this.col.push(c0[0], c0[1], c0[2], c1[0], c1[1], c1[2], c2[0], c2[1], c2[2]);\n this.flr.push(this.currentFloor, this.currentFloor, this.currentFloor);\n }\n\n /** One triangle with independent per-vertex normals (smooth shading). */\n triN(\n p0: readonly [number, number, number],\n p1: readonly [number, number, number],\n p2: readonly [number, number, number],\n n0: readonly [number, number, number],\n n1: readonly [number, number, number],\n n2: readonly [number, number, number],\n c0: RGB,\n c1: RGB = c0,\n c2: RGB = c0,\n ): void {\n if (isDegenerate(p0, p1, p2)) return;\n this.pos.push(p0[0], p0[1], p0[2], p1[0], p1[1], p1[2], p2[0], p2[1], p2[2]);\n this.nor.push(n0[0], n0[1], n0[2], n1[0], n1[1], n1[2], n2[0], n2[1], n2[2]);\n this.col.push(c0[0], c0[1], c0[2], c1[0], c1[1], c1[2], c2[0], c2[1], c2[2]);\n this.flr.push(this.currentFloor, this.currentFloor, this.currentFloor);\n }\n\n get vertexCount(): number {\n return this.pos.length / 3;\n }\n\n build(): MeshData {\n return {\n position: new Float32Array(this.pos),\n normal: new Float32Array(this.nor),\n color: new Float32Array(this.col),\n floor: new Float32Array(this.flr),\n count: this.pos.length / 3,\n };\n }\n}\n\n/** Face normal of a triangle (right-handed). */\nexport function faceNormal(\n a: readonly [number, number, number],\n b: readonly [number, number, number],\n c: readonly [number, number, number],\n): [number, number, number] {\n const ux = b[0] - a[0], uy = b[1] - a[1], uz = b[2] - a[2];\n const vx = c[0] - a[0], vy = c[1] - a[1], vz = c[2] - a[2];\n let nx = uy * vz - uz * vy;\n let ny = uz * vx - ux * vz;\n let nz = ux * vy - uy * vx;\n const len = Math.hypot(nx, ny, nz) || 1;\n nx /= len; ny /= len; nz /= len;\n return [nx, ny, nz];\n}\n\nexport interface Triangulation {\n /** Outline points followed by every hole's points, in order. */\n pts: Point[];\n /** Triangle vertex indices into `pts` (length is a multiple of 3). */\n tris: number[];\n}\n\n/** Triangulate a closed polygon with optional holes via earcut. */\nexport function triangulate(outline: Point[], holes?: Point[][]): Triangulation {\n const pts: Point[] = [...outline];\n const flat: number[] = [];\n for (const p of outline) flat.push(p.x, p.y);\n const holeIndices: number[] = [];\n if (holes) {\n for (const hole of holes) {\n if (hole.length < 3) continue;\n holeIndices.push(pts.length);\n for (const p of hole) {\n pts.push(p);\n flat.push(p.x, p.y);\n }\n }\n }\n const tris = earcut(flat, holeIndices.length ? holeIndices : undefined, 2);\n return { pts, tris };\n}\n\n/** Centroid of a point ring (average — good enough for wall orientation). */\nexport function centroid(pts: Point[]): Point {\n let x = 0, y = 0;\n for (const p of pts) { x += p.x; y += p.y; }\n const n = pts.length || 1;\n return { x: x / n, y: y / n };\n}\n\n/** Signed area of a ring (shoelace). Positive = CCW, negative = CW, ~0 = degenerate. */\nexport function signedArea(pts: Point[]): number {\n let a = 0;\n for (let i = 0, n = pts.length; i < n; i++) {\n const p = pts[i], q = pts[(i + 1) % n];\n a += p.x * q.y - q.x * p.y;\n }\n return a / 2;\n}\n\n/** Return the ring wound counter-clockwise (reversed copy if it was CW), so\n * CW and CCW inputs of the same polygon extrude to identical geometry. */\nexport function toCCW(pts: Point[]): Point[] {\n return signedArea(pts) < 0 ? [...pts].reverse() : pts;\n}\n\n/** Recursion ceiling for uniform cap subdivision (4^d triangles per seed). */\nconst MAX_CAP_SPLIT_DEPTH = 3;\n\n/** Max deviation of the plane from the surface, sampled over a triangle. */\nfunction capDeviation(a: Point, b: Point, c: Point, topY: (p: Point) => number): number {\n const ya = topY(a), yb = topY(b), yc = topY(c);\n const edge = (p: Point, q: Point, yp: number, yq: number): number =>\n Math.abs(topY({ x: (p.x + q.x) / 2, y: (p.y + q.y) / 2 }) - (yp + yq) / 2);\n const cx3 = (a.x + b.x + c.x) / 3, cy3 = (a.y + b.y + c.y) / 3;\n const yCen = (ya + yb + yc) / 3;\n let worst = Math.max(\n edge(a, b, ya, yb), edge(b, c, yb, yc), edge(c, a, yc, ya),\n Math.abs(topY({ x: cx3, y: cy3 }) - yCen),\n );\n // Mid-edge-to-centroid probes: the deviation is concave and zero at the\n // vertices, so its maximum lies in the interior and edge samples alone\n // under-report it — worst at the fold where a flat front plateau meets a rake.\n const probe = (px: number, py: number, yp: number): void => {\n const d = Math.abs(topY({ x: (px + cx3) / 2, y: (py + cy3) / 2 }) - (yp + yCen) / 2);\n if (d > worst) worst = d;\n };\n probe((a.x + b.x) / 2, (a.y + b.y) / 2, (ya + yb) / 2);\n probe((b.x + c.x) / 2, (b.y + c.y) / 2, (yb + yc) / 2);\n probe((c.x + a.x) / 2, (c.y + a.y) / 2, (yc + ya) / 2);\n return worst;\n}\n\n/** Worst deviation over a triangle uniformly subdivided `depth` times. */\nfunction maxDevAtDepth(a: Point, b: Point, c: Point, topY: (p: Point) => number, depth: number): number {\n if (depth <= 0) return capDeviation(a, b, c, topY);\n const ab = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };\n const bc = { x: (b.x + c.x) / 2, y: (b.y + c.y) / 2 };\n const ca = { x: (c.x + a.x) / 2, y: (c.y + a.y) / 2 };\n return Math.max(\n maxDevAtDepth(a, ab, ca, topY, depth - 1),\n maxDevAtDepth(ab, b, bc, topY, depth - 1),\n maxDevAtDepth(ca, bc, c, topY, depth - 1),\n maxDevAtDepth(ab, bc, ca, topY, depth - 1),\n );\n}\n\n/**\n * Emit one cap triangle uniformly subdivided `depth` times (1→4 each level).\n *\n * Uniform, at a depth shared by every triangle in the section — NOT adaptive.\n * Adaptive refinement was tried first and produced visible dark hairlines across\n * the decks: two triangles sharing an edge each pick which edge to split\n * independently, so one can split a shared edge while its neighbour does not.\n * That leaves a T-junction, and the crack between them shows the background\n * through the deck. A uniform depth guarantees both sides of every shared edge\n * are subdivided identically, so the mesh stays watertight by construction.\n */\nfunction emitCapUniform(\n builder: MeshBuilder,\n a: Point, b: Point, c: Point,\n topY: (p: Point) => number,\n colTop: RGB,\n depth: number,\n topN?: (p: Point) => [number, number, number],\n): void {\n if (depth > 0) {\n const ab = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };\n const bc = { x: (b.x + c.x) / 2, y: (b.y + c.y) / 2 };\n const ca = { x: (c.x + a.x) / 2, y: (c.y + a.y) / 2 };\n emitCapUniform(builder, a, ab, ca, topY, colTop, depth - 1, topN);\n emitCapUniform(builder, ab, b, bc, topY, colTop, depth - 1, topN);\n emitCapUniform(builder, ca, bc, c, topY, colTop, depth - 1, topN);\n emitCapUniform(builder, ab, bc, ca, topY, colTop, depth - 1, topN);\n return;\n }\n const at: [number, number, number] = [a.x * M, topY(a), a.y * M];\n const bt: [number, number, number] = [b.x * M, topY(b), b.y * M];\n const ct: [number, number, number] = [c.x * M, topY(c), c.y * M];\n if (topN) {\n // Analytic per-vertex normals: continuous across every shared edge, so the\n // deck shades as the smooth surface it approximates rather than as facets,\n // and a degenerate triangle (whose face normal is numerically meaningless)\n // still shades identically to its neighbours.\n builder.triN(at, bt, ct, topN(a), topN(b), topN(c), colTop);\n return;\n }\n let n = faceNormal(at, bt, ct);\n if (n[1] < 0) n = [-n[0], -n[1], -n[2]]; // caps face up\n builder.tri(at, bt, ct, n, colTop);\n}\n/** Ceiling on inserted points per ring edge, so a pathological outline cannot\n * explode the vertex count. */\nconst MAX_RING_SPLIT_DEPTH = 8;\n\n/**\n * Insert points along a ring's edges until each sub-edge's midpoint height is\n * within `maxError` of the straight interpolation between its ends.\n *\n * This is what lets the wall tops follow the same curved surface as the cap. A\n * wall's top edge is a straight line between two outline vertices; on a raked\n * tier the true surface bows away from that line, so an undensified ring leaves\n * the wall top hanging above or below the cap it is supposed to meet. Densifying\n * once, before triangulation, fixes cap boundary and wall top together — they\n * are built from the same point list.\n */\nfunction densifyRing(ring: Point[], topY: (p: Point) => number, maxError: number): Point[] {\n if (!Number.isFinite(maxError)) return ring;\n const out: Point[] = [];\n const emit = (p: Point, q: Point, yp: number, yq: number, depth: number): void => {\n if (depth > 0) {\n const mid: Point = { x: (p.x + q.x) / 2, y: (p.y + q.y) / 2 };\n const ym = topY(mid);\n if (Math.abs(ym - (yp + yq) / 2) > maxError) {\n emit(p, mid, yp, ym, depth - 1);\n emit(mid, q, ym, yq, depth - 1);\n return;\n }\n }\n out.push(p); // q is emitted by the next edge (ring is closed)\n };\n for (let i = 0; i < ring.length; i++) {\n const p = ring[i], q = ring[(i + 1) % ring.length];\n emit(p, q, topY(p), topY(q), MAX_RING_SPLIT_DEPTH);\n }\n return out;\n}\n\n/**\n * Drop ring points that sit closer than `eps` to the previous kept point, and\n * points that are collinear with their neighbours to within `eps`.\n *\n * This is the SOURCE of the remaining slivers, and it is upstream of shading.\n * `outsetRing`'s closing union re-emits intersection points that can land within\n * nanometres of an input vertex, and `densifyRing` then splits around them; a\n * point pair 1e-7 chart units apart is a zero-area triangle to earcut, which\n * fans it against a distant vertex. Measured aspect ratios reached 8.7e6.\n *\n * Removing them cannot move the outline perceptibly — `eps` is a fraction of a\n * millimetre in world metres — but it removes the degeneracy earcut amplifies.\n * Collinear removal is the same argument: a point exactly on the segment between\n * its neighbours carries no shape, and it is what turns one clean triangle into\n * a needle plus a remainder.\n */\nfunction dedupeRing(ring: Point[], eps: number): Point[] {\n if (ring.length < 3) return ring;\n const out: Point[] = [];\n for (const p of ring) {\n const last = out[out.length - 1];\n if (last && Math.hypot(p.x - last.x, p.y - last.y) < eps) continue;\n out.push(p);\n }\n // Close-the-loop duplicate.\n while (out.length > 2 && Math.hypot(out[0].x - out[out.length - 1].x, out[0].y - out[out.length - 1].y) < eps) {\n out.pop();\n }\n if (out.length < 3) return ring;\n // Collinear pass: drop p when its perpendicular distance to (prev,next) < eps.\n const keep: Point[] = [];\n for (let i = 0; i < out.length; i++) {\n const prev = keep.length ? keep[keep.length - 1] : out[(i - 1 + out.length) % out.length];\n const p = out[i];\n const next = out[(i + 1) % out.length];\n const ax = next.x - prev.x, ay = next.y - prev.y;\n const len = Math.hypot(ax, ay);\n if (len > eps) {\n const cross = Math.abs((p.x - prev.x) * ay - (p.y - prev.y) * ax) / len;\n if (cross < eps) continue;\n }\n keep.push(p);\n }\n return keep.length >= 3 ? keep : out;\n}\n\n/** Ring cleanup tolerance in chart units (~0.1 mm in world metres). */\nconst RING_EPS_U = 1e-3 * CHART_UNITS_PER_METRE * 0.1;\n\n/**\n * Extrude a closed polygon into a prism: a (possibly sloped) top cap, a bottom\n * cap, and side walls with a baked top→bottom AO gradient.\n *\n * `topY(p)` returns the world-metre height of the top surface at chart-point `p`\n * (constant for a slab, rake-sloped for a raked tier). `bottomY` is the floor.\n *\n * `maxCapError` (metres) bounds how far the drawn cap may deviate from `topY`;\n * omit it (or pass Infinity) for a flat top, where the cap is exact by\n * construction and no subdivision is wanted.\n */\nexport function extrudePrism(\n builder: MeshBuilder,\n outlineIn: Point[],\n holesIn: Point[][] | undefined,\n topY: (p: Point) => number,\n bottomY: number,\n colTop: RGB,\n colWall: RGB,\n ao: { top: number; wallBottom: number; bottomCap: number },\n maxCapError: number | undefined = Infinity,\n topNormal?: (p: Point) => [number, number, number],\n): void {\n // Guard degenerate/near-collinear polygons (zero visible area) — a free-hand\n // or generated outline can collapse to a sliver and would emit garbage tris.\n if (!outlineIn || outlineIn.length < 3) return;\n if (maxCapError === undefined) maxCapError = Infinity;\n if (Math.abs(signedArea(outlineIn)) < 1e-4) return;\n // Normalise winding so CW and CCW inputs produce identical geometry (the solid\n // program also disables culling, but this keeps the emitted mesh deterministic).\n // Densify the rings against the surface FIRST, so the cap boundary and the\n // wall tops are generated from one point list and cannot part company.\n const outline = dedupeRing(densifyRing(dedupeRing(toCCW(outlineIn), RING_EPS_U), topY, maxCapError), RING_EPS_U);\n const holes = holesIn?.map((h) => dedupeRing(densifyRing(dedupeRing(toCCW(h), RING_EPS_U), topY, maxCapError), RING_EPS_U))\n .filter((h) => h.length >= 3 && Math.abs(signedArea(h)) >= 1e-4);\n const { pts, tris } = triangulate(outline, holes);\n const cTop: RGB = [colTop[0] * ao.top, colTop[1] * ao.top, colTop[2] * ao.top];\n const cBot: RGB = [colTop[0] * ao.bottomCap, colTop[1] * ao.bottomCap, colTop[2] * ao.bottomCap];\n const cWallTop: RGB = [colWall[0] * ao.top, colWall[1] * ao.top, colWall[2] * ao.top];\n const cWallBot: RGB = [colWall[0] * ao.wallBottom, colWall[1] * ao.wallBottom, colWall[2] * ao.wallBottom];\n\n // Resolve ONE subdivision depth for the whole section: the smallest depth at\n // which every cap triangle tracks the surface within `maxCapError`. Shared by\n // all triangles so the cap stays watertight (see emitCapUniform).\n let capDepth = 0;\n if (Number.isFinite(maxCapError)) {\n for (; capDepth < MAX_CAP_SPLIT_DEPTH; capDepth++) {\n let worst = 0;\n for (let i = 0; i < tris.length; i += 3) {\n const d = maxDevAtDepth(pts[tris[i]], pts[tris[i + 1]], pts[tris[i + 2]], topY, capDepth);\n if (d > worst) worst = d;\n }\n if (worst <= maxCapError) break;\n }\n }\n\n // Top + bottom caps.\n for (let i = 0; i < tris.length; i += 3) {\n const a = pts[tris[i]], b = pts[tris[i + 1]], c = pts[tris[i + 2]];\n emitCapUniform(builder, a, b, c, topY, cTop, capDepth, topNormal);\n // Bottom cap (reversed winding, faces down).\n const ab: [number, number, number] = [a.x * M, bottomY, a.y * M];\n const bb: [number, number, number] = [b.x * M, bottomY, b.y * M];\n const cb: [number, number, number] = [c.x * M, bottomY, c.y * M];\n builder.tri(ab, cb, bb, [0, -1, 0], cBot);\n }\n\n // Side walls. Orient outline walls away from the outline centroid; hole walls\n // face into the hole (flip). Vertical walls → horizontal normals.\n // Wall tops must land on exactly the vertices the cap's boundary produced, or\n // a crack opens along the top edge. Uniform cap subdivision splits every\n // boundary edge into 2^capDepth equal parts, so split the wall rings the same\n // way and both meshes share the identical point list.\n const splitRing = (ring: Point[]): Point[] => {\n if (capDepth <= 0) return ring;\n const n = 1 << capDepth;\n const out: Point[] = [];\n for (let i = 0; i < ring.length; i++) {\n const a = ring[i], b = ring[(i + 1) % ring.length];\n for (let k = 0; k < n; k++) {\n const t = k / n;\n out.push({ x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t });\n }\n }\n return out;\n };\n const oc = centroid(outline);\n const rings: Array<{ ring: Point[]; flip: boolean }> = [{ ring: splitRing(outline), flip: false }];\n if (holes) for (const h of holes) if (h.length >= 3) rings.push({ ring: splitRing(h), flip: true });\n\n for (const { ring, flip } of rings) {\n for (let i = 0; i < ring.length; i++) {\n const a = ring[i];\n const b = ring[(i + 1) % ring.length];\n const dx = (b.x - a.x) * M;\n const dz = (b.y - a.y) * M;\n let nx = dz, nz = -dx;\n const nl = Math.hypot(nx, nz) || 1;\n nx /= nl; nz /= nl;\n // Orient outward from the outline centroid.\n const mx = (a.x + b.x) / 2 - oc.x;\n const mz = (a.y + b.y) / 2 - oc.y;\n let dot = nx * mx + nz * mz;\n if (flip) dot = -dot;\n if (dot < 0) { nx = -nx; nz = -nz; }\n const n: [number, number, number] = [nx, 0, nz];\n\n const aTop: [number, number, number] = [a.x * M, topY(a), a.y * M];\n const bTop: [number, number, number] = [b.x * M, topY(b), b.y * M];\n const aBot: [number, number, number] = [a.x * M, bottomY, a.y * M];\n const bBot: [number, number, number] = [b.x * M, bottomY, b.y * M];\n builder.tri(aTop, bTop, bBot, n, cWallTop, cWallTop, cWallBot);\n builder.tri(aTop, bBot, aBot, n, cWallTop, cWallBot, cWallBot);\n }\n }\n}\n\n/** Merge several MeshData buffers into one (single draw call). */\nexport function mergeMeshData(parts: MeshData[]): MeshData {\n let total = 0;\n for (const p of parts) total += p.count;\n const position = new Float32Array(total * 3);\n const normal = new Float32Array(total * 3);\n const color = new Float32Array(total * 3);\n const floor = new Float32Array(total);\n let off = 0;\n for (const p of parts) {\n position.set(p.position, off * 3);\n normal.set(p.normal, off * 3);\n color.set(p.color, off * 3);\n floor.set(p.floor, off);\n off += p.count;\n }\n return { position, normal, color, floor, count: total };\n}\n\n/**\n * Grow a ring outward by `d` chart units — a MITER offset, not a dilation.\n *\n * Seat dots have a real world radius, so a seat authored hard against its\n * section boundary overhangs the deck and appears to float in the aisle. Seats\n * are chart data and must not move, so the DECK grows to meet them.\n *\n * The first implementation was a true Minkowski dilation (union of the ring, a\n * quad per edge, a disc per vertex). It was geometrically correct and visually\n * wrong: it tripled a 30-point outline to ~90 points with clusters of\n * near-duplicates where discs meet quads, and earcut fans such clusters into\n * needle triangles — 10,787 of 23,621 slivers at up to 20,650:1, which\n * flat-shade into dark hairlines streaking across the deck. Decimating the\n * result then cut the very corners the dilation had just rounded, putting seats\n * back over the edge. Dilate-then-decimate was fighting itself.\n *\n * A miter offset emits exactly ONE point per input vertex instead: each vertex\n * moves along its angle bisector by `d / cos(half-angle)`, which lands precisely\n * on the intersection of the two offset edges. Corners stay sharp and exact, the\n * point count does not grow, so there is nothing to decimate and no sliver\n * source. Sharp spikes are bevelled at `miterLimit`, and the single union at the\n * end resolves the self-intersections a miter offset produces where a concave\n * outline folds over itself.\n *\n * Holes are deliberately left alone: eroding them needs the complement and they\n * are rare in practice. A seat authored on a hole's rim can still overhang it.\n */\nexport function outsetRing(ring: Point[], d: number, miterLimit = 2.5): Point[] {\n if (d <= 0 || ring.length < 3) return ring;\n const r = toCCW(ring);\n const n = r.length;\n // Outward unit normal of each edge (valid because the ring is CCW).\n const nrm: Array<{ x: number; y: number }> = [];\n for (let i = 0; i < n; i++) {\n const a = r[i], b = r[(i + 1) % n];\n const dx = b.x - a.x, dy = b.y - a.y;\n const len = Math.hypot(dx, dy) || 1;\n nrm.push({ x: dy / len, y: -dx / len });\n }\n const out: [number, number][] = [];\n for (let i = 0; i < n; i++) {\n const p = r[i];\n const nPrev = nrm[(i - 1 + n) % n]; // edge arriving at p\n const nCur = nrm[i]; // edge leaving p\n let mx = nPrev.x + nCur.x, my = nPrev.y + nCur.y;\n const ml = Math.hypot(mx, my);\n // Bevel a spike (or a full reversal) into the two edge-offset points rather\n // than letting the miter shoot off to infinity.\n const bevel = (): void => {\n out.push([p.x + nPrev.x * d, p.y + nPrev.y * d]);\n out.push([p.x + nCur.x * d, p.y + nCur.y * d]);\n };\n if (ml < 1e-9) { bevel(); continue; }\n mx /= ml; my /= ml;\n const cosHalf = mx * nPrev.x + my * nPrev.y;\n const scale = 1 / Math.max(cosHalf, 1e-6);\n if (!Number.isFinite(scale) || scale > miterLimit) { bevel(); continue; }\n out.push([p.x + mx * d * scale, p.y + my * d * scale]);\n }\n if (out.length < 3) return ring;\n try {\n // Self-union: resolves the loops a miter offset folds into concave corners.\n const merged = polygonClipping.union([[...out, out[0]]]);\n let best: [number, number][] | null = null, bestArea = -Infinity;\n for (const poly of merged) {\n const area = Math.abs(signedArea(poly[0].map(([x, y]) => ({ x, y }))));\n if (area > bestArea) { bestArea = area; best = poly[0]; }\n }\n if (!best) return ring;\n const pts = best.map(([x, y]) => ({ x, y }));\n // union() repeats the first point to close the ring; drop the duplicate.\n if (pts.length > 1) {\n const f = pts[0], l = pts[pts.length - 1];\n if (Math.abs(f.x - l.x) < 1e-9 && Math.abs(f.y - l.y) < 1e-9) pts.pop();\n }\n return pts.length >= 3 ? pts : ring;\n } catch {\n return ring; // never let a degenerate outline break the whole scene\n }\n}\n\n/** Sample an ellipse (chart units) into a closed polygon of `seg` points. */\nexport function ellipsePolygon(cx: number, cy: number, rx: number, ry: number, seg = 28): Point[] {\n const out: Point[] = [];\n for (let i = 0; i < seg; i++) {\n const a = (i / seg) * Math.PI * 2;\n out.push({ x: cx + rx * Math.cos(a), y: cy + ry * Math.sin(a) });\n }\n return out;\n}\n\n/** Axis-aligned rectangle (chart units) as a closed polygon. */\nexport function rectPolygon(x: number, y: number, w: number, h: number): Point[] {\n return [\n { x, y },\n { x: x + w, y },\n { x: x + w, y: y + h },\n { x, y: y + h },\n ];\n}\n","/**\n * Pure builder for the instanced seat cloud. Produces the per-instance arrays a\n * single OGL InstancedMesh consumes (one draw call for every seat), plus the\n * seatId → instanceIndex map that `setAvailability` uses to patch only the seats\n * that actually changed via a sub-range `bufferSubData` upload.\n */\n\nimport { accessibilityRingColor, type ExpandedSeat } from '../../core/types';\nimport { hexToRgb } from '../palette';\nimport { SEATED_EYE_HEIGHT_M } from '../../core/units';\nimport { M } from './geometry';\nimport { seatStateIndex, type SeatState3D } from '../palette';\nimport type { VenueSurfaces } from './surface';\n\n/**\n * World radius of a seat dot in metres — the single source for the shader\n * uniform AND for how far a section deck is padded so its seats sit on it.\n * (The shader may grow this with distance to hold `uMinPixels`; this is the\n * near-field base.)\n */\nexport const SEAT_DOT_RADIUS_M = 0.22;\n\n/**\n * Fraction of a seat's nearest-neighbour spacing that its dot may occupy.\n *\n * Below 0.5 two adjacent dots cannot touch even when both sit at their ceiling,\n * so a gap always survives between rows. 0.42 leaves that gap visible rather\n * than hairline.\n */\nexport const SEAT_PITCH_FRACTION = 0.42;\n\nexport interface SeatInstanceData {\n count: number;\n /** vec3 per instance: world (x, y, z) in metres. */\n iPosition: Float32Array;\n /** float per instance: index into the seat-state colour LUT. */\n iState: Float32Array;\n /**\n * float per instance: the largest world radius this dot may take, metres.\n *\n * The shader enforces `uMinPixels` by GROWING a dot's world radius with depth,\n * which is what merges rows into a solid mass at range — measured mean seat\n * spacing is 0.53–0.58 m against a 0.44 m dot diameter, so there is very little\n * slack to spend before neighbours touch. A global cap cannot fix it: spacing\n * is a property of the chart, and varies between sections of the same venue\n * (measured min 0.21 m on the amphitheatre against a 0.58 m mean).\n *\n * So the ceiling travels per seat, derived from that seat's own nearest\n * neighbour. A dot grows to hold its minimum pixel size and then STOPS,\n * whatever the distance. Past that point holding legibility is the LOD ladder's\n * job (fade toward the tier tint), not the dot's.\n */\n iMaxRadius: Float32Array;\n /**\n * vec3 per instance: accommodation ring colour, or (0,0,0) for none.\n *\n * 2D draws a coloured ring around every seat with an accessibility type, and\n * 3D drew nothing at all — so a wheelchair space, a companion seat or a\n * lift-armrest seat was indistinguishable from any other the moment a buyer\n * switched to the 3D view. A ring mirrors the 2D treatment exactly, needs no\n * texture, and costs one instanced attribute rather than a draw call.\n */\n iRing: Float32Array;\n /** float per instance: owning floor index, for per-floor isolation. */\n iFloor: Float32Array;\n /** seatId → instance index (drives targeted availability updates). */\n idToIndex: Map<string, number>;\n}\n\n/**\n * Nearest-neighbour distance in chart units for every seat, via a uniform grid.\n *\n * Linear in seat count for realistic charts (the grid cell is sized to the mean\n * spacing, so each lookup touches a bounded neighbourhood) — the naive pairwise\n * version is 14k² on the Uber Arena and would show up in build time.\n */\nfunction nearestNeighbourSpacing(seats: ExpandedSeat[]): Float64Array {\n const n = seats.length;\n const out = new Float64Array(n);\n if (n < 2) { out.fill(Infinity); return out; }\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;\n for (const s of seats) {\n if (s.x < minX) minX = s.x;\n if (s.y < minY) minY = s.y;\n if (s.x > maxX) maxX = s.x;\n if (s.y > maxY) maxY = s.y;\n }\n const w = Math.max(maxX - minX, 1e-6), h = Math.max(maxY - minY, 1e-6);\n // Aim at ~1 seat per cell: cell = sqrt(area / count).\n const cell = Math.max(Math.sqrt((w * h) / n), 1e-6);\n const cols = Math.max(1, Math.ceil(w / cell) + 1);\n const rows = Math.max(1, Math.ceil(h / cell) + 1);\n const buckets = new Map<number, number[]>();\n const cellOf = (x: number, y: number): number => {\n const cx = Math.min(cols - 1, Math.max(0, Math.floor((x - minX) / cell)));\n const cy = Math.min(rows - 1, Math.max(0, Math.floor((y - minY) / cell)));\n return cy * cols + cx;\n };\n for (let i = 0; i < n; i++) {\n const k = cellOf(seats[i].x, seats[i].y);\n const b = buckets.get(k);\n if (b) b.push(i); else buckets.set(k, [i]);\n }\n for (let i = 0; i < n; i++) {\n const s = seats[i];\n const cx = Math.min(cols - 1, Math.max(0, Math.floor((s.x - minX) / cell)));\n const cy = Math.min(rows - 1, Math.max(0, Math.floor((s.y - minY) / cell)));\n let best = Infinity;\n // Widen the ring until a hit is found — a sparse region (a lone box seat)\n // would otherwise report Infinity and lose its ceiling entirely.\n for (let r = 1; r <= 4; r++) {\n for (let gy = cy - r; gy <= cy + r; gy++) {\n if (gy < 0 || gy >= rows) continue;\n for (let gx = cx - r; gx <= cx + r; gx++) {\n if (gx < 0 || gx >= cols) continue;\n // Only the newly-added ring after the first pass.\n if (r > 1 && Math.abs(gy - cy) < r && Math.abs(gx - cx) < r) continue;\n const b = buckets.get(gy * cols + gx);\n if (!b) continue;\n for (const j of b) {\n if (j === i) continue;\n const d = Math.hypot(seats[j].x - s.x, seats[j].y - s.y);\n if (d < best) best = d;\n }\n }\n }\n if (Number.isFinite(best) && best <= r * cell) break;\n }\n out[i] = best;\n }\n return out;\n}\n\n/**\n * Resolve a seat's surface height in world metres — the fallback path used only\n * when no venue surface was supplied (direct callers, tests). Prefer the\n * section-resolved eye height minus the seated-eye offset.\n *\n * There is deliberately NO lift constant here any more. A dot is placed exactly\n * ON the deck, and the seat shader offsets the billboard by its own radius along\n * screen-up so the dot RESTS on that point at every distance. A fixed lift could\n * not do that: the shader enforces `uMinPixels`, so a dot's world radius grows\n * with depth and any constant clearance is eventually smaller than the dot.\n */\nfunction seatSurfaceY(seat: ExpandedSeat): number {\n const eye = seat.eyeHeightM;\n if (Number.isFinite(eye)) return Math.max(0, (eye as number) - SEATED_EYE_HEIGHT_M);\n return 0;\n}\n\nexport function buildSeatInstances(\n seats: ExpandedSeat[],\n initial?: (seat: ExpandedSeat) => SeatState3D,\n surfaces?: VenueSurfaces,\n seatFloor?: Float32Array,\n): SeatInstanceData {\n const count = seats.length;\n const iPosition = new Float32Array(count * 3);\n const iState = new Float32Array(count);\n const iMaxRadius = new Float32Array(count);\n const iRing = new Float32Array(count * 3);\n const idToIndex = new Map<string, number>();\n const spacing = nearestNeighbourSpacing(seats);\n for (let i = 0; i < count; i++) {\n const seat = seats[i];\n // The seat's OWN row pitch when its section resolved into rows; the raw\n // nearest-neighbour distance only as a fallback for unstructured seats.\n // See `VenueSurfaces.seatPitchU` for why the raw measure is wrong.\n const resolved = surfaces?.seatPitchU(i);\n const pitchM = (resolved ?? spacing[i]) * M;\n // The ceiling is allowed BELOW the base radius. A chart authored tighter\n // than the 0.22 m default dot (measured 0.21 m minimum spacing on the\n // amphitheatre) overlaps at every distance today, including close up, and\n // shrinking those dots is the only thing that separates them. A small floor\n // keeps a pathologically dense chart's seats visible rather than vanishing.\n // A seat with no resolvable neighbour keeps the base radius, not an\n // unbounded one.\n iMaxRadius[i] = Number.isFinite(pitchM)\n ? Math.max(0.06, Math.min(SEAT_DOT_RADIUS_M, pitchM * SEAT_PITCH_FRACTION))\n : SEAT_DOT_RADIUS_M;\n iPosition[i * 3] = seat.x * M;\n // The venue surface wins when present: it is the same function the tier cap\n // is built from, which is what guarantees a seat can never sit inside it.\n iPosition[i * 3 + 1] = surfaces ? surfaces.seatDeckY(i) : seatSurfaceY(seat);\n iPosition[i * 3 + 2] = seat.y * M;\n iState[i] = seatStateIndex(initial ? initial(seat) : 'available');\n // Same source of truth as 2D: the ring colour is derived from the seat's\n // accessibility types, so the two views cannot disagree about which seat is\n // an accessible one.\n if (seat.accessibility?.length) {\n const rgb = hexToRgb(accessibilityRingColor(seat.accessibility));\n if (rgb) { iRing[i * 3] = rgb[0]; iRing[i * 3 + 1] = rgb[1]; iRing[i * 3 + 2] = rgb[2]; }\n }\n idToIndex.set(seat.id, i);\n }\n return {\n count, iPosition, iState, iMaxRadius, iRing, idToIndex,\n iFloor: seatFloor ?? new Float32Array(count),\n };\n}\n\n/** Contiguous run of instance indices to upload in a single bufferSubData call. */\nexport interface DirtyRun {\n start: number;\n /** number of instances (floats, since iState is 1 float/instance). */\n length: number;\n}\n\n/**\n * Instances worth re-uploading just to avoid another GL call.\n *\n * Runs are merged across gaps up to this size. A `bufferSubData` has fixed\n * driver overhead regardless of how few bytes it carries, so uploading a handful\n * of unchanged instances inside one call is far cheaper than issuing a second.\n *\n * The case this exists for is a live socket delta on a big venue, where the\n * changed seats are SCATTERED rather than contiguous. Measured on a 50,400-seat\n * chart before merging: 1,000 scattered seats produced 983 separate uploads and\n * 5,000 produced 4,438 — thousands of GL calls in a single frame to move a few\n * kilobytes. A block going on sale already coalesced well; random churn did not.\n */\nconst RUN_MERGE_GAP = 64;\n\n/**\n * Apply state updates to the CPU `iState` array and return the coalesced\n * contiguous runs that changed — the caller uploads exactly those ranges and\n * never the whole buffer.\n */\nexport function applySeatStates(\n data: SeatInstanceData,\n updates: Array<{ seatId: string; state: SeatState3D }>,\n): DirtyRun[] {\n const changed: number[] = [];\n for (const u of updates) {\n const idx = data.idToIndex.get(u.seatId);\n if (idx === undefined) continue;\n const v = seatStateIndex(u.state);\n if (data.iState[idx] !== v) {\n data.iState[idx] = v;\n changed.push(idx);\n }\n }\n if (!changed.length) return [];\n changed.sort((a, b) => a - b);\n const runs: DirtyRun[] = [];\n let start = changed[0];\n let prev = changed[0];\n for (let i = 1; i < changed.length; i++) {\n const idx = changed[i];\n if (idx === prev) continue;\n // Bridge small gaps: the unchanged instances in between are rewritten from\n // `iState`, which already holds their current value, so this is a no-op for\n // them and saves a GL call. See RUN_MERGE_GAP.\n if (idx <= prev + RUN_MERGE_GAP) { prev = idx; continue; }\n runs.push({ start, length: prev - start + 1 });\n start = idx;\n prev = idx;\n }\n runs.push({ start, length: prev - start + 1 });\n return runs;\n}\n","/**\n * Resolve a chart's authored theme into the 3D scene's colours.\n *\n * Every chart already carries a `ChartTheme` — background, brand accent, seat\n * scale — and the 2D renderer and the picker chrome honour it. The 3D view did\n * not: `palette.ts` was a fixed set of constants, so a white-labelled event\n * rendered in SeatLayer's own dark grey whatever the organizer had branded. That\n * is a visible gap in a paid feature, and it is the kind of thing a customer\n * notices immediately when they switch from the 2D map to the 3D view.\n *\n * The palette in `palette.ts` stays the DEFAULT and the reference. This module\n * only rebases it, so an unthemed chart is byte-for-byte what it was.\n *\n * ## How structure is rebased\n *\n * Structure colours are not replaced by the brand colour — a venue rendered in\n * flat brand paint reads as a diagram, not a building, and the look brief is\n * deliberately \"desaturated greys for structure, saturated colour only on\n * seats\". Instead each structure colour is blended a little way toward the\n * authored background, so the whole venue picks up the brand's cast and sits in\n * its own light, while keeping the tonal relationships (tier above wall, stage\n * warmer than tier) that make the geometry readable.\n */\n\nimport type { ChartTheme } from '../core/types';\nimport { BACKGROUND, SEAT_STATE_COLORS, STRUCTURE, hexToRgb, mix, scaleRgb, type RGB, type SeatState3D } from './palette';\n\n/** How far a structure colour is pulled toward the authored background. */\nconst BACKGROUND_INFLUENCE = 0.15;\n\n/**\n * The background gradient's two stops as multiples of the authored colour.\n *\n * Fitted to the existing hand-tuned gradient on the default `#0e1117`, whose\n * per-channel ratios are 0.91/0.90/0.89 for the top stop and 1.82/1.80/1.66 for\n * the bottom. A single scalar cannot reproduce a hand-picked triple exactly, so\n * these are the best fit: an authored background lands within ~0.01 of the old\n * look, and an UNTHEMED chart bypasses this path entirely and stays identical.\n * What matters is that a themed chart gets the same vertical falloff around its\n * own colour rather than a flat wash.\n */\nconst BG_TOP_SCALE = 0.9;\nconst BG_BOTTOM_SCALE = 1.76;\n\n/** Bounds on the authored seat-size multiplier, matching `ChartTheme.seatScale`. */\nconst SEAT_SCALE_MIN = 0.7;\nconst SEAT_SCALE_MAX = 1.6;\n\nexport interface Theme3D {\n background: { top: RGB; bottom: RGB };\n structure: typeof STRUCTURE;\n seatStates: Record<SeatState3D, RGB>;\n /** Multiplier on the seat dot's world radius. */\n seatScale: number;\n}\n\n/** The unthemed default — the palette exactly as authored in `palette.ts`. */\nexport function defaultTheme3D(): Theme3D {\n return {\n background: { top: [...BACKGROUND.top] as RGB, bottom: [...BACKGROUND.bottom] as RGB },\n structure: STRUCTURE,\n seatStates: { ...SEAT_STATE_COLORS },\n seatScale: 1,\n };\n}\n\nexport function resolveTheme3D(theme: ChartTheme | undefined): Theme3D {\n const base = defaultTheme3D();\n if (!theme) return base;\n\n const bg = hexToRgb(theme.background);\n if (bg) {\n base.background = {\n top: scaleRgb(bg, BG_TOP_SCALE),\n bottom: scaleRgb(bg, BG_BOTTOM_SCALE),\n };\n // Rebase every structure colour onto the authored background. Done as one\n // pass over the palette rather than field by field, so a colour added to\n // STRUCTURE later is themed automatically instead of silently staying fixed.\n const rebased: Record<string, RGB> = {};\n for (const [key, value] of Object.entries(STRUCTURE)) {\n rebased[key] = mix(value as RGB, bg, BACKGROUND_INFLUENCE);\n }\n base.structure = rebased as unknown as typeof STRUCTURE;\n }\n\n // Selection is the one seat colour a brand owns: it is the buyer's own\n // choice reflected back, and the picker chrome already paints it in `accent`.\n // Availability, held and sold stay fixed — they carry MEANING, and letting a\n // brand recolour \"sold\" would let a chart mislead about what is for sale.\n const selection = hexToRgb(theme.selectionColor) ?? hexToRgb(theme.accent);\n if (selection) base.seatStates = { ...base.seatStates, selected: selection };\n\n const scale = theme.seatScale;\n if (typeof scale === 'number' && Number.isFinite(scale)) {\n base.seatScale = Math.min(SEAT_SCALE_MAX, Math.max(SEAT_SCALE_MIN, scale));\n }\n return base;\n}\n\n/** Flat LUT (5 × vec3) for the seat fragment shader, in `SEAT_STATES` order. */\nexport function themeSeatColorLUT(theme: Theme3D, order: readonly SeatState3D[]): number[] {\n const out: number[] = [];\n for (const s of order) out.push(...theme.seatStates[s]);\n return out;\n}\n","/**\n * Venue labels — anchors, level-of-detail, and world→screen projection.\n *\n * ## Why labels are DOM, not geometry\n *\n * The renderer is deliberately texture-free at three draw calls. Drawing text on\n * the GPU means a signed-distance font atlas: a texture, another shader, a build\n * asset, and a resolution ceiling — a lot of machinery for the few dozen labels a\n * venue actually needs. Projecting anchors and positioning DOM elements costs\n * nothing when a chart has no labels, and buys properties the GPU path cannot:\n *\n * - **Real text.** A screen reader can read the venue's structure. That is the\n * accessibility gap in 3D, not just a rendering convenience.\n * - Crisp at any device pixel ratio and any zoom, with no atlas to outgrow.\n * - `ChartTheme.fontFamily`, i18n and RTL come from the browser.\n *\n * This module is the pure half — what to label, where its anchor sits, and when\n * it should show. The overlay that positions elements lives in `index.ts`.\n *\n * ## Why the rungs mirror 2D\n *\n * 2D melts through zones → sections → seats. Labels follow the same idea for the\n * same reason: at a distance a buyer needs to know which part of the venue they\n * are looking at, and up close they need to know which block and which door. A\n * label set that does not thin out with distance turns a 51-section arena into\n * unreadable confetti.\n */\n\nimport type { Point } from '../core/types';\n\nexport type LabelKind = 'zone' | 'section' | 'annotation' | 'booth';\n\nexport interface SceneLabel {\n id: string;\n kind: LabelKind;\n text: string;\n /** World-metre anchor the label is pinned to. */\n anchor: [number, number, number];\n /** Authored colour (`#rrggbb`), when the object carries one. */\n color?: string;\n /** Authored rotation in degrees, for annotations that specify one. */\n rotation?: number;\n}\n\n/**\n * Distance thresholds as multiples of the venue radius, matching the seat LOD's\n * scale so labels and seats thin out together rather than fighting.\n *\n * Zone labels are the far rung and switch OFF close in, where they would sit on\n * top of the section labels that have become more useful. Section labels are the\n * middle rung. Annotations and booth labels are wayfinding — only legible, and\n * only wanted, once the buyer is actually in that part of the venue.\n */\nconst ZONE_MIN_DISTANCE = 1.15;\nconst SECTION_MAX_DISTANCE = 2.2;\nconst NEAR_MAX_DISTANCE = 0.85;\n\n/** Which label kinds should show at this camera distance. */\nexport function visibleLabelKinds(distance: number, venueRadius: number): Set<LabelKind> {\n const r = Math.max(1e-6, venueRadius);\n const d = distance / r;\n const out = new Set<LabelKind>();\n if (d >= ZONE_MIN_DISTANCE) out.add('zone');\n if (d <= SECTION_MAX_DISTANCE) out.add('section');\n if (d <= NEAR_MAX_DISTANCE) { out.add('annotation'); out.add('booth'); }\n return out;\n}\n\nexport interface Projected {\n /** CSS pixels from the container's left/top. */\n x: number;\n y: number;\n /** Normalised depth; smaller is nearer. */\n depth: number;\n /** False when the anchor is behind the camera or outside the frustum. */\n visible: boolean;\n}\n\n/**\n * Project a world point through a column-major 4x4 view-projection matrix.\n *\n * Behind-camera points are reported invisible rather than mirrored to the far\n * side of the screen, which is what a naive divide by a negative w produces —\n * a label for the section behind you appearing over the stage in front of you.\n */\nexport function projectToScreen(\n viewProjection: ArrayLike<number>,\n p: readonly [number, number, number],\n width: number,\n height: number,\n): Projected {\n const m = viewProjection;\n const x = p[0], y = p[1], z = p[2];\n const cx = m[0] * x + m[4] * y + m[8] * z + m[12];\n const cy = m[1] * x + m[5] * y + m[9] * z + m[13];\n const cz = m[2] * x + m[6] * y + m[10] * z + m[14];\n const cw = m[3] * x + m[7] * y + m[11] * z + m[15];\n if (!(cw > 1e-6)) return { x: 0, y: 0, depth: Infinity, visible: false };\n const ndcX = cx / cw, ndcY = cy / cw, ndcZ = cz / cw;\n const inside = ndcX >= -1.05 && ndcX <= 1.05 && ndcY >= -1.05 && ndcY <= 1.05 && ndcZ <= 1;\n return {\n x: (ndcX * 0.5 + 0.5) * width,\n y: (1 - (ndcY * 0.5 + 0.5)) * height,\n depth: ndcZ,\n visible: inside,\n };\n}\n\n/**\n * Drop labels that would overlap, nearest kept.\n *\n * Without this a dense venue paints its section names on top of each other and\n * every one of them becomes unreadable — worse than showing fewer. Nearest-wins\n * because the label a buyer is closest to is the one they are asking about.\n *\n * The test is RECTANGULAR, not radial. A label is a line of text: wide and\n * short. A single radius big enough to stop two names colliding side by side is\n * far bigger than the vertical gap they actually need, so a radial test threw\n * away labels that were stacked but perfectly readable — on the amphitheatre it\n * dropped \"Terrace\" purely for sitting between the other two concentric zones.\n */\nexport function cullOverlapping<T extends { screen: Projected }>(\n items: T[],\n separationX: number,\n separationY: number = separationX,\n): T[] {\n const kept: T[] = [];\n const ordered = [...items].sort((a, b) => a.screen.depth - b.screen.depth);\n for (const item of ordered) {\n let clash = false;\n for (const k of kept) {\n const dx = Math.abs(item.screen.x - k.screen.x);\n const dy = Math.abs(item.screen.y - k.screen.y);\n // Only a genuine box overlap counts: near on BOTH axes.\n if (dx < separationX && dy < separationY) { clash = true; break; }\n }\n if (!clash) kept.push(item);\n }\n return kept;\n}\n\n/** Mean of a point set, or null when empty. */\nexport function centroidOf(points: readonly Point[]): Point | null {\n if (!points.length) return null;\n let x = 0, y = 0;\n for (const p of points) { x += p.x; y += p.y; }\n return { x: x / points.length, y: y / points.length };\n}\n","/**\n * The JS-side source of truth for the 3D scene — a pure, GPU-free description\n * built once from the chart's existing height contract. Everything the renderer\n * uploads (merged solid geometry, the instanced seat cloud, camera-framing\n * bounds, the seat-state colour LUT) is derived here, so it survives a WebGL\n * context loss: on `webglcontextrestored` the renderer simply re-uploads from\n * this model without recomputing anything.\n *\n * Feeds 100% from `sectionGeometry` / `Floor.baseHeightM` / `ExpandedSeat`\n * (docs/3d-program-workorder §Architecture) — no new chart data is invented.\n */\n\nimport type { ChartDoc, ChartObject, ExpandedSeat, Point, SectionObject } from '../../core/types';\nimport { CHART_UNITS_PER_METRE } from '../../core/units';\nimport { SEAT_STATES, STRUCTURE, hexToRgb, mix, desaturate, scaleRgb, type RGB } from '../palette';\nimport { resolveTheme3D, themeSeatColorLUT, type Theme3D } from '../theme';\nimport { centroidOf, type SceneLabel } from '../labels';\nimport {\n MeshBuilder, extrudePrism, mergeMeshData, ellipsePolygon, rectPolygon, outsetRing, M, type MeshData,\n} from './geometry';\nimport polygonClipping from 'polygon-clipping';\nimport { buildSeatInstances, SEAT_DOT_RADIUS_M, type SeatInstanceData } from './seatInstances';\nimport { buildVenueSurfaces, CAP_MAX_ERROR_M, type SectionSurface } from './surface';\nimport { emitDeckBands, deckFootprints } from './deckBands';\n\n/** One resolved plane of geometry — a single-floor chart is one of these. */\ninterface FloorUnit {\n objects: ChartObject[];\n focal: Point;\n baseHeightM: number;\n}\n\n/**\n * A navigable zone — the venue's own top-level grouping (Orchestra, Lower Bowl,\n * Hall A), resolved for camera framing and for the section/zone LOD rung.\n *\n * Every shipped chart authors zones and gives every section one, and the 2D\n * renderer uses them as its farthest LOD rung. 3D ignored them entirely, so the\n * one structure a buyer navigates by (\"take me to the Grand Circle\") had no\n * representation at all in the 3D view.\n */\nexport interface SceneZone {\n id: string;\n label: string;\n /** Authored zone colour, or null when the chart leaves it to the category mix. */\n color: RGB | null;\n /** Section object ids belonging to this zone. */\n sectionIds: string[];\n /** Seats resolved into this zone. */\n seatCount: number;\n /** World-metre centre of the zone's seats (camera target). */\n center: [number, number, number];\n /** Half-diagonal of its footprint, world metres (camera fit). */\n radius: number;\n /** What this zone faces — its authored focal, else the venue's. */\n focalWorld: [number, number, number];\n}\n\n/** A navigable floor — a logical level of the venue. */\nexport interface SceneFloor {\n index: number;\n id: string;\n label: string;\n seatCount: number;\n center: [number, number, number];\n radius: number;\n}\n\nexport interface SceneModel {\n /** Every non-seat surface merged into one triangle soup (1 draw call). */\n solids: MeshData;\n seats: SeatInstanceData;\n bounds: {\n /** World-metre venue centre (camera target). */\n center: [number, number, number];\n /** Half-diagonal of the horizontal footprint, metres (camera fit). */\n radius: number;\n groundY: number;\n };\n /** 5 × vec3 flat LUT for the seat fragment shader. */\n stateColorLUT: number[];\n /** Resolved colours for this chart's authored theme (background, seat scale). */\n theme: Theme3D;\n seatCount: number;\n /** Venue focal point in world metres (cinematic look-at target). */\n focalWorld: [number, number, number];\n /** The venue's zones, in authored order. Empty when the chart has none. */\n zones: SceneZone[];\n /**\n * The venue's floors, in authored order. A single-floor chart reports one.\n *\n * Every shipped multi-floor template puts all its floors at baseHeightM 0 and\n * takes height from the sections instead, so floors are a logical grouping,\n * not a physical stack. What they need is ISOLATION: draw all three of an\n * opera house at once and the balcony sits over the parterre.\n */\n floors: SceneFloor[];\n /**\n * Everything worth naming, with a world anchor: zones, sections, authored\n * wayfinding text, and booths. Rendered as a DOM overlay — see `labels.ts` for\n * why text is not geometry here.\n */\n labels: SceneLabel[];\n}\n\nfunction floorUnits(doc: ChartDoc): FloorUnit[] {\n if (doc.floors?.length) {\n return doc.floors.map((f) => ({\n objects: f.objects,\n focal: f.focalPoint ?? doc.focalPoint,\n baseHeightM: f.baseHeightM ?? 0,\n }));\n }\n return [{ objects: doc.objects, focal: doc.focalPoint, baseHeightM: 0 }];\n}\n\nconst AO = { top: 1.0, wallBottom: 0.5, bottomCap: 0.4 };\n\n// The cap's tessellation tolerance lives beside the surface it approximates\n// (surface.ts), together with the seat clearance derived from it.\n\n/**\n * Fold a surface's 2D fill colour into the dark structure palette: desaturate\n * ~40 %, darken, then ground it in the neutral structure grey so a tier top\n * reads architectural — a recognisable hue (purple/green/orange) but muted, not\n * candy-coloured paint. Risers/walls stay neutral concrete; baked AO still\n * multiplies these per vertex downstream. `null` fill ⇒ the neutral grey.\n */\nfunction tintTop(fill: RGB | null, neutral: RGB): RGB {\n if (!fill) return neutral;\n const muted = scaleRgb(desaturate(fill, 0.4), 0.62);\n return mix(neutral, muted, 0.72);\n}\n\n/**\n * Resolve each section's 2D paint colour, keyed by logical section id\n * (`logicalSectionId ?? id`, matching how expanded seats attribute `sectionId`):\n * the count-weighted mix of its member seats' category colours — the same source\n * the 2D renderer blends into a section's block fill. An explicit `section.color`\n * override is applied later (it wins in `sectionFill`).\n */\nfunction resolveSectionFills(doc: ChartDoc, seats: ExpandedSeat[]): Map<string, RGB> {\n const catColor = new Map<string, string>();\n for (const c of doc.categories ?? []) catColor.set(c.key, c.color);\n const counts = new Map<string, Map<string, number>>();\n for (const s of seats) {\n if (!s.sectionId) continue;\n let m = counts.get(s.sectionId);\n if (!m) { m = new Map(); counts.set(s.sectionId, m); }\n m.set(s.categoryKey, (m.get(s.categoryKey) ?? 0) + 1);\n }\n const out = new Map<string, RGB>();\n for (const [sid, byCat] of counts) {\n let r = 0, g = 0, b = 0, w = 0;\n for (const [key, n] of byCat) {\n const rgb = hexToRgb(catColor.get(key));\n if (!rgb) continue;\n r += rgb[0] * n; g += rgb[1] * n; b += rgb[2] * n; w += n;\n }\n if (w > 0) out.set(sid, [r / w, g / w, b / w]);\n }\n return out;\n}\n\n/** A section's fill: explicit `color` override wins, else the member-category mix. */\nfunction sectionFill(section: SectionObject, byLogical: Map<string, RGB>): RGB | null {\n return hexToRgb(section.color) ?? byLogical.get(section.logicalSectionId ?? section.id) ?? null;\n}\n\n/**\n * Extrude one section into the shared builder, capped by ITS OWN seating surface.\n *\n * The height function is not recomputed here — it comes from `surface.ts`, the\n * same object the seat dots stand on. That is the whole point: there is one\n * definition of the seating surface, and the cap, the walls and the seats all\n * read it, so no cross-file offset constant has to be kept in agreement.\n */\ninterface RingBox { minX: number; minY: number; maxX: number; maxY: number }\n\nfunction bboxOfRing(ring: readonly Point[]): RingBox {\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;\n for (const p of ring) {\n if (p.x < minX) minX = p.x;\n if (p.y < minY) minY = p.y;\n if (p.x > maxX) maxX = p.x;\n if (p.y > maxY) maxY = p.y;\n }\n return { minX, minY, maxX, maxY };\n}\n\nfunction boxesOverlap(a: RingBox, b: RingBox): boolean {\n return a.minX <= b.maxX && b.minX <= a.maxX && a.minY <= b.maxY && b.minY <= a.maxY;\n}\n\n/**\n * A section's padded outline with every OTHER section's authored outline removed.\n *\n * Deck padding grows a section by 0.33 m so a seat authored hard against its\n * boundary still rests on deck. Where two sections abut, that growth crosses into\n * the neighbour — and if the neighbour is lower, the overhang covers its seats.\n * Measured on the concert hall: 27 stalls-terrace seats buried by 0.20 m beneath\n * the terrace beside them, which sits 0.60 m higher.\n *\n * Clipping to the section's own padded ring is not enough, because the padding IS\n * the intrusion. The rule that works is: grow to hold your own seats, but never\n * over another section's floor.\n */\nfunction paddedFootprint(section: SectionObject, siblings: readonly SectionObject[]): Point[][] {\n const __tp = performance.now();\n const padded = outsetRing(section.outline, SEAT_DOT_RADIUS_M * 1.5 * CHART_UNITS_PER_METRE);\n __t('outsetRing', __tp);\n // Only siblings whose bounding box actually overlaps can remove any area, and\n // a polygon boolean is expensive. Differencing against EVERY sibling made\n // scene build quadratic in section count: measured 2.7 s at 50k seats / 40\n // sections and 11.7 s at 99k / 60, where doubling the venue cost 4.3x the\n // time. A venue's sections are laid out around it, so almost no pair overlaps\n // and this prunes nearly all of the work.\n const box = bboxOfRing(padded);\n const others = siblings.filter((o) => o !== section\n && o.outline && o.outline.length >= 3\n && boxesOverlap(box, bboxOfRing(o.outline)));\n if (!others.length) return [padded];\n const toRing = (pts: readonly Point[]): [number, number][] => {\n const r = pts.map((p) => [p.x, p.y] as [number, number]);\n r.push(r[0]);\n return r;\n };\n try {\n const diff = polygonClipping.difference([toRing(padded)], ...others.map((o) => [toRing(o.outline)]));\n const out: Point[][] = [];\n for (const poly of diff) {\n if (!poly.length) continue;\n const pts = poly[0].map(([x, y]) => ({ x, y }));\n if (pts.length > 1) {\n const f = pts[0], l = pts[pts.length - 1];\n if (Math.abs(f.x - l.x) < 1e-9 && Math.abs(f.y - l.y) < 1e-9) pts.pop();\n }\n if (pts.length >= 3) out.push(pts);\n }\n return out.length ? out : [padded];\n } catch {\n return [padded];\n }\n}\n\n/** Intersect each block footprint with the section's own allowed area. */\nfunction clipFootprints(\n blocks: ReturnType<typeof deckFootprints>,\n allowed: Point[][],\n): ReturnType<typeof deckFootprints> {\n if (!allowed.length) return blocks;\n const toRing = (pts: readonly Point[]): [number, number][] => {\n const r = pts.map((p) => [p.x, p.y] as [number, number]);\n r.push(r[0]);\n return r;\n };\n const allowedPolys = allowed.map((a) => [toRing(a)]);\n const out: ReturnType<typeof deckFootprints> = [];\n for (const b of blocks) {\n let pieces;\n try {\n pieces = polygonClipping.intersection([toRing(b.outline)], ...[allowedPolys.flat()]);\n } catch {\n out.push(b);\n continue;\n }\n for (const poly of pieces) {\n if (!poly.length) continue;\n const pts = poly[0].map(([x, y]) => ({ x, y }));\n if (pts.length > 1) {\n const f = pts[0], l = pts[pts.length - 1];\n if (Math.abs(f.x - l.x) < 1e-9 && Math.abs(f.y - l.y) < 1e-9) pts.pop();\n }\n if (pts.length >= 3) out.push({ outline: pts, holes: [], topY: b.topY });\n }\n }\n return out.length ? out : blocks;\n}\n\nfunction buildTier(\n builder: MeshBuilder,\n section: SectionObject,\n unit: FloorUnit,\n fill: RGB | null,\n surface: SectionSurface | undefined,\n claimed: ClaimedArea,\n siblings: readonly SectionObject[],\n S: typeof STRUCTURE,\n): void {\n if (!section.outline || section.outline.length < 3) return;\n const bottomY = unit.baseHeightM;\n const colTop = tintTop(fill, S.tierTop);\n if (!surface) return;\n const topY = (p: Point): number => surface.deckAt(p.x, p.y);\n // Pad the deck so a seat authored hard against the section boundary rests ON\n // its own section instead of overhanging into the aisle. Seats are chart data\n // and never move; the deck grows to meet them. 1.5x the dot footprint: enough\n // that a dot sitting exactly on the boundary still lands clear of the edge,\n // and small against real aisle widths.\n const footprint = paddedFootprint(section, siblings);\n const outline = footprint[0] ?? section.outline;\n\n // A section whose rows resolved into levels is drawn as GEOMETRY, not as a\n // tessellated field: a flat landing prism carrying one level ribbon per row,\n // with a riser between consecutive ribbons. See `deckBands.ts` for why a\n // refined cap cannot work once every row has to be level.\n if (surface.rowLevels.length >= 2) {\n // One base prism per BLOCK, hugging the ribbons above it, instead of one flat\n // plate over the section's whole authored outline. The outline reaches well\n // past the seating, so a single plate stuck out around the stands as a slab\n // and cut every block's end square through it. Each block also stands on its\n // OWN lowest row rather than the section's.\n // Clipped to the section's own footprint like the treads are: a block base is\n // built from a ribbon union carrying its own outward margin, so it overhangs\n // for the same reason and buries a lower neighbour's seats the same way.\n const __tf = performance.now();\n const blocks = clipFootprints(deckFootprints(surface.rowLevels, unit.focal), footprint);\n __t('deckFootprints', __tf);\n // Explicit UP normals rather than face normals. A block footprint is a\n // boolean union of ~100 overlapping ribbons, and such a union always leaves\n // some very thin triangles along its seams; deriving a normal from one by\n // cross product is numerically meaningless and shades as a dark hairline.\n // The top is FLAT, so the correct normal is known outright and the thin\n // triangles become harmless.\n const capUp = (): [number, number, number] => [0, 1, 0];\n for (const b of blocks) {\n extrudePrism(builder, b.outline, b.holes, () => b.topY, bottomY,\n colTop, S.tierWall, AO, undefined, capUp);\n }\n // Nothing resolved (a degenerate row set) — fall back to the outline so the\n // section is never simply missing.\n if (!blocks.length) {\n extrudePrism(builder, outline, section.holes, () => surface.landingY, bottomY,\n colTop, S.tierWall, AO);\n }\n const __tb = performance.now();\n emitDeckBands(builder, surface.rowLevels, unit.focal, surface.landingY, {\n tread: [colTop[0] * AO.top, colTop[1] * AO.top, colTop[2] * AO.top],\n // Risers read as the structure they are, a shade below their tread, which\n // is what makes the stepping legible from a low angle.\n riser: [colTop[0] * 0.72, colTop[1] * 0.72, colTop[2] * 0.72],\n }, footprint.length === 1 ? outline : footprint.flat());\n __t('emitDeckBands', __tb);\n return;\n }\n\n // A flat section's cap is exact at any tessellation, so it is left\n // unsubdivided (Infinity) and stays identical to the pre-surface mesh —\n // legacy height-less charts must not shift by a single vertex.\n const maxErr = surface.flat ? Infinity : CAP_MAX_ERROR_M;\n const topN = (p: Point): [number, number, number] => surface.normalAt(p.x, p.y);\n\n // Take only the ground this section has not already lost to an earlier one.\n //\n // Every flat section's top sits at exactly the same height, and the deck\n // padding above grows each one by 0.33 m, so neighbouring sections OVERLAP.\n // Two coplanar surfaces at identical depth is a z-fight, and it renders as a\n // serrated comb of interpenetrating teeth along every shared boundary — the\n // artifact visible on the flat chart. Depth-biasing them apart would hide it\n // rather than fix it, and would break down as soon as a chart has many\n // sections. Removing the overlap means there is nothing to fight over.\n for (const ring of claimed.subtract(outline)) {\n extrudePrism(builder, ring, section.holes, topY, bottomY, colTop, S.tierWall, AO, maxErr, topN);\n }\n}\n\n/**\n * The ground already taken by sections drawn so far, so a later section can be\n * clipped to what is left. Only meaningful between COPLANAR surfaces, which is\n * why raked sections (each at its own height) never reach this.\n */\nclass ClaimedArea {\n private rings: Array<[number, number][][]> = [];\n private boxes: RingBox[] = [];\n\n /** `ring` minus everything claimed so far; then claim what is returned. */\n subtract(ring: Point[]): Point[][] {\n const closed: [number, number][] = ring.map((p) => [p.x, p.y]);\n if (closed.length < 3) return [];\n closed.push(closed[0]);\n const box = bboxOfRing(ring);\n // Same prune as `paddedFootprint`: a claim that cannot overlap cannot remove\n // anything, and without this the boolean work is quadratic in section count.\n const overlapping = this.rings.filter((_, i) => boxesOverlap(box, this.boxes[i]));\n let pieces: Array<[number, number][][]> = [[closed]];\n if (overlapping.length) {\n try {\n const diff = polygonClipping.difference([closed], ...overlapping);\n pieces = diff.map((poly) => poly.map((r) => r.map(([x, y]) => [x, y] as [number, number])));\n } catch {\n pieces = [[closed]]; // a degenerate outline must not drop the section\n }\n }\n this.rings.push([closed]);\n this.boxes.push(box);\n const out: Point[][] = [];\n for (const poly of pieces) {\n if (!poly.length) continue;\n const pts = poly[0].map(([x, y]) => ({ x, y }));\n if (pts.length > 1) {\n const f = pts[0], l = pts[pts.length - 1];\n if (Math.abs(f.x - l.x) < 1e-9 && Math.abs(f.y - l.y) < 1e-9) pts.pop();\n }\n if (pts.length >= 3) out.push(pts);\n }\n return out;\n }\n}\n\n/**\n * Height of a booth stand, world metres — a partition wall, not a table.\n *\n * Trade-show and exhibition charts are entirely booths (267 across the two\n * shipped templates), and before this they drew nothing at all: the whole venue\n * rendered as empty floor. A booth is a sellable unit the buyer picks, so it has\n * to be a solid the camera can approach and the pointer can hit.\n */\nconst BOOTH_HEIGHT_M = 2.4;\n\n/** How far a label floats above the thing it names, world metres. */\nconst ZONE_LABEL_LIFT_M = 6;\nconst SECTION_LABEL_LIFT_M = 2.2;\nconst ANNOTATION_LIFT_M = 0.1;\nconst BOOTH_LABEL_LIFT_M = 0.3;\n\n/** Height of a banquet table top, world metres (standard dining height). */\nconst TABLE_HEIGHT_M = 0.75;\n\n/** Resolve a booth to its closed chart-unit polygon, honouring a custom outline. */\nfunction boothPolygon(booth: Extract<ChartObject, { type: 'booth' }>): Point[] | null {\n // A custom outline wins and ignores rotation, exactly as the 2D renderer does\n // (see BoothObject.points) — L-shaped and island units on an expo floor.\n if (booth.points && booth.points.length >= 3) return booth.points;\n const { center, width, height, rotation } = booth;\n if (!width || !height) return null;\n const a = ((rotation ?? 0) * Math.PI) / 180;\n const cos = Math.cos(a), sin = Math.sin(a);\n const hw = width / 2, hh = height / 2;\n return [[-hw, -hh], [hw, -hh], [hw, hh], [-hw, hh]].map(([lx, ly]) => ({\n x: center.x + lx * cos - ly * sin,\n y: center.y + lx * sin + ly * cos,\n }));\n}\n\nfunction buildBooth(\n builder: MeshBuilder,\n booth: Extract<ChartObject, { type: 'booth' }>,\n base: number,\n fill: RGB | null,\n S: typeof STRUCTURE,\n): void {\n const poly = boothPolygon(booth);\n if (!poly) return;\n extrudePrism(builder, poly, undefined, () => base + BOOTH_HEIGHT_M, base,\n tintTop(fill, S.boothTop), S.boothWall, AO);\n}\n\n/** Resolve a table to its closed chart-unit polygon. */\nfunction tablePolygon(table: Extract<ChartObject, { type: 'table' }>): Point[] | null {\n if (table.shape === 'round') {\n const r = table.radius;\n if (!r) return null;\n return ellipsePolygon(table.center.x, table.center.y, r, r, 24);\n }\n const { width, height, rotation, center } = table;\n if (!width || !height) return null;\n const a = ((rotation ?? 0) * Math.PI) / 180;\n const cos = Math.cos(a), sin = Math.sin(a);\n const hw = width / 2, hh = height / 2;\n return [[-hw, -hh], [hw, -hh], [hw, hh], [-hw, hh]].map(([lx, ly]) => ({\n x: center.x + lx * cos - ly * sin,\n y: center.y + lx * sin + ly * cos,\n }));\n}\n\nfunction buildTable(\n builder: MeshBuilder,\n table: Extract<ChartObject, { type: 'table' }>,\n base: number,\n fill: RGB | null,\n S: typeof STRUCTURE,\n): void {\n const poly = tablePolygon(table);\n if (!poly) return;\n // Banquet and club charts (77 tables across the shipped templates) drew their\n // chairs floating around nothing. The table is what makes the arrangement read\n // as a table rather than a ring of stray seats.\n extrudePrism(builder, poly, undefined, () => base + TABLE_HEIGHT_M, base,\n tintTop(fill, S.tableTop), S.tableWall, AO);\n}\n\n/** Resolve a shape object to a closed chart-unit polygon (or null to skip). */\nfunction shapePolygon(shape: Extract<ChartObject, { type: 'shape' }>): Point[] | null {\n if (shape.kind === 'polygon' && shape.points && shape.points.length >= 3) return shape.points;\n if (shape.kind === 'rect' && shape.width && shape.height) {\n return rectPolygon(shape.x ?? 0, shape.y ?? 0, shape.width, shape.height);\n }\n if (shape.kind === 'ellipse' && shape.width && shape.height) {\n const cx = (shape.x ?? 0) + shape.width / 2;\n const cy = (shape.y ?? 0) + shape.height / 2;\n return ellipsePolygon(cx, cy, shape.width / 2, shape.height / 2);\n }\n return null; // line / polyline are stroke-only\n}\n\nfunction buildShape(builder: MeshBuilder, shape: Extract<ChartObject, { type: 'shape' }>, base: number, S: typeof STRUCTURE): void {\n const poly = shapePolygon(shape);\n if (!poly) return;\n const isStage = shape.role === 'stage';\n const height = isStage ? base + 1.0 : base + 0.25;\n const colTop = isStage ? S.stageTop : S.decorTop;\n const colWall = isStage ? S.stageWall : S.decorWall;\n extrudePrism(builder, poly, undefined, () => height, base, colTop, colWall, AO);\n}\n\nfunction buildGa(builder: MeshBuilder, ga: Extract<ChartObject, { type: 'gaArea' }>, base: number, fill: RGB | null, S: typeof STRUCTURE): void {\n if (!ga.points || ga.points.length < 3) return;\n const colTop = tintTop(fill, S.gaTop);\n extrudePrism(builder, ga.points, ga.holes, () => base + 0.15, base, colTop, S.gaWall, AO);\n}\n\n/** Compute the horizontal chart-unit footprint over everything drawable. */\nfunction chartFootprint(units: FloorUnit[], seats: ExpandedSeat[]): { minX: number; minY: number; maxX: number; maxY: number } {\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;\n const acc = (x: number, y: number): void => {\n if (x < minX) minX = x; if (y < minY) minY = y;\n if (x > maxX) maxX = x; if (y > maxY) maxY = y;\n };\n for (const s of seats) acc(s.x, s.y);\n for (const u of units) {\n for (const o of u.objects) {\n if (o.type === 'section') for (const p of o.outline) acc(p.x, p.y);\n else if (o.type === 'shape' && o.points) for (const p of o.points) acc(p.x, p.y);\n else if (o.type === 'gaArea') for (const p of o.points) acc(p.x, p.y);\n else if (o.type === 'booth') { const b = boothPolygon(o); if (b) for (const p of b) acc(p.x, p.y); }\n else if (o.type === 'table') { const t = tablePolygon(o); if (t) for (const p of t) acc(p.x, p.y); }\n }\n }\n if (!Number.isFinite(minX)) { minX = -100; minY = -100; maxX = 100; maxY = 100; }\n return { minX, minY, maxX, maxY };\n}\n\nexport interface SceneModelInput {\n doc: ChartDoc;\n seats: ExpandedSeat[];\n /** Optional initial per-seat state (default all available). */\n initialState?: (seat: ExpandedSeat) => import('../palette').SeatState3D;\n}\n\nexport const __PROF: Record<string, number> = {};\nconst __t = (k: string, t0: number): void => {\n __PROF[k] = (__PROF[k] ?? 0) + (performance.now() - t0);\n};\n\nexport function buildSceneModel(input: SceneModelInput): SceneModel {\n for (const k of Object.keys(__PROF)) delete __PROF[k];\n const { doc, seats } = input;\n // Resolved once and threaded down, so no builder reaches for the module-level\n // palette and quietly ignores an organizer's branding.\n const theme = resolveTheme3D(doc.theme);\n const S = theme.structure;\n const units = floorUnits(doc);\n const builder = new MeshBuilder();\n\n // Ground slab sized to the footprint (+ margin), sitting at datum 0.\n const fp = chartFootprint(units, seats);\n const padU = Math.max(60, (fp.maxX - fp.minX + fp.maxY - fp.minY) * 0.06);\n const groundPoly = rectPolygon(fp.minX - padU, fp.minY - padU, (fp.maxX - fp.minX) + padU * 2, (fp.maxY - fp.minY) + padU * 2);\n extrudePrism(builder, groundPoly, undefined, () => 0, -0.4, S.ground, S.ground, AO);\n\n // Per-section 2D fill colours (member-category mix), carried onto tier tops.\n const sectionFills = resolveSectionFills(doc, seats);\n const catColor = new Map<string, string>();\n for (const c of doc.categories ?? []) catColor.set(c.key, c.color);\n\n // ONE seating surface per section, resolved from the same model layout.ts uses\n // for eye heights. Consumed below by the tier caps and by the seat instances.\n const __t0 = performance.now();\n const surfaces = buildVenueSurfaces(units, seats);\n __t('surfaces', __t0);\n /** Owning floor per seat, for per-instance floor isolation. */\n const seatFloor = new Float32Array(seats.length);\n\n // Coplanar sections must not overlap (see ClaimedArea). One claim per floor:\n // sections on different floors sit at different heights and cannot z-fight.\n for (let unitIndex = 0; unitIndex < units.length; unitIndex++) {\n const unit = units[unitIndex];\n builder.setFloor(unitIndex);\n const claimed = new ClaimedArea();\n const siblings = unit.objects.filter((o): o is SectionObject => o.type === 'section' && !!o.outline && o.outline.length >= 3);\n for (const o of unit.objects) {\n if (o.type === 'section') {\n const t = performance.now();\n buildTier(builder, o, unit, sectionFill(o, sectionFills), surfaces.bySection.get(o.id), claimed, siblings, S);\n __t('buildTier', t);\n }\n else if (o.type === 'shape') buildShape(builder, o, unit.baseHeightM, S);\n else if (o.type === 'gaArea') buildGa(builder, o, unit.baseHeightM, hexToRgb(catColor.get(o.categoryKey)), S);\n else if (o.type === 'booth') buildBooth(builder, o, unit.baseHeightM, hexToRgb(catColor.get(o.categoryKey)), S);\n else if (o.type === 'table') buildTable(builder, o, unit.baseHeightM, hexToRgb(catColor.get(o.categoryKey)), S);\n }\n }\n\n const focal = doc.focalPoint ?? { x: (fp.minX + fp.maxX) / 2, y: (fp.minY + fp.maxY) / 2 };\n\n // NOTE — per-row stepped treads (./rowSteps.ts) are DISABLED pending the\n // unified-surface rework. See docs/handover-2026-07-25-3d-surface-rework.md.\n //\n // They layered a second surface on top of the section prism, and the two could\n // not be kept in agreement across chart shapes: treads became large flat plates\n // that swallowed the stepping, sliver triangles appeared where seat ordering\n // failed, and seat dots were sliced by their own tread. The fix is one surface\n // function per section consumed by cap + steps + dots, not a second layer.\n // The module is kept (with its measurements in the doc comments) as input to\n // that rework; re-enabling it as-is is not the intended path.\n\n const __tm = performance.now();\n const solids = mergeMeshData([builder.build()]);\n __t('meshBuild+merge', __tm);\n const __ts = performance.now();\n const seatData: SeatInstanceData = buildSeatInstances(seats, input.initialState, surfaces, seatFloor);\n __t('seatInstances', __ts);\n\n // --- Zones -----------------------------------------------------------------\n // Resolved from the SEATS, not the section outlines: a zone's meaning to a\n // buyer is the seats in it, and framing on the outlines would include the\n // aisles and margins a section is drawn with.\n const zoneDefs = doc.zones ?? [];\n const zones: SceneZone[] = [];\n if (zoneDefs.length) {\n const sectionZone = new Map<string, string>();\n for (const unit of units) {\n for (const o of unit.objects) {\n if (o.type === 'section' && o.zone) sectionZone.set(o.id, o.zone);\n }\n }\n interface Acc { n: number; minX: number; minY: number; maxX: number; maxY: number; sumY: number }\n const acc = new Map<string, Acc>();\n for (let i = 0; i < seats.length; i++) {\n const s = seats[i];\n // Prefer the seat's own resolved zone; fall back to its owning section's.\n const owner = surfaces.seatOwner[i];\n const zid = s.zoneId ?? (owner ? sectionZone.get(owner) : undefined);\n if (!zid) continue;\n let a = acc.get(zid);\n if (!a) { a = { n: 0, minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity, sumY: 0 }; acc.set(zid, a); }\n a.n++;\n if (s.x < a.minX) a.minX = s.x;\n if (s.y < a.minY) a.minY = s.y;\n if (s.x > a.maxX) a.maxX = s.x;\n if (s.y > a.maxY) a.maxY = s.y;\n a.sumY += surfaces.seatDeckY(i);\n }\n for (const z of zoneDefs) {\n const a = acc.get(z.id);\n const sectionIds: string[] = [];\n for (const [secId, zid] of sectionZone) if (zid === z.id) sectionIds.push(secId);\n if (!a || a.n === 0) {\n // A zone with no seats still exists (an empty hall, a zone of GA areas);\n // report it rather than dropping it, so navigation can show it as empty.\n zones.push({\n id: z.id, label: z.label, color: hexToRgb(z.color), sectionIds, seatCount: 0,\n center: [0, 0, 0], radius: 0,\n focalWorld: [(z.focalPoint ?? focal).x * M, 1.5, (z.focalPoint ?? focal).y * M],\n });\n continue;\n }\n zones.push({\n id: z.id,\n label: z.label,\n color: hexToRgb(z.color),\n sectionIds,\n seatCount: a.n,\n center: [((a.minX + a.maxX) / 2) * M, a.sumY / a.n, ((a.minY + a.maxY) / 2) * M],\n radius: 0.5 * Math.hypot((a.maxX - a.minX) * M, (a.maxY - a.minY) * M) || 1,\n // A zone may face its own point (`ZoneDef.focalPoint`); the documented\n // fallback is the floor/chart focal.\n focalWorld: [(z.focalPoint ?? focal).x * M, 1.5, (z.focalPoint ?? focal).y * M],\n });\n }\n }\n\n // --- Labels ----------------------------------------------------------------\n const labels: SceneLabel[] = [];\n for (const z of zones) {\n if (z.seatCount === 0) continue;\n // Zone labels float above the seating they name, so they read as belonging\n // to the whole block rather than to whichever seat is under them.\n labels.push({\n id: `zone:${z.id}`,\n kind: 'zone',\n text: z.label,\n anchor: [z.center[0], z.center[1] + ZONE_LABEL_LIFT_M, z.center[2]],\n color: doc.zones?.find((d) => d.id === z.id)?.color,\n });\n }\n {\n // A section is named over its own SEATS, at their mean deck height. Its\n // outline centroid would drift into the aisles a concave section wraps, and\n // on a raked tier the label would sit at the wrong height entirely.\n const acc = new Map<string, { n: number; x: number; y: number; deck: number }>();\n for (let i = 0; i < seats.length; i++) {\n const owner = surfaces.seatOwner[i];\n if (!owner) continue;\n let a = acc.get(owner);\n if (!a) { a = { n: 0, x: 0, y: 0, deck: 0 }; acc.set(owner, a); }\n a.n++; a.x += seats[i].x; a.y += seats[i].y; a.deck += surfaces.seatDeckY(i);\n }\n for (const unit of units) {\n for (const o of unit.objects) {\n if (o.type !== 'section') continue;\n const a = acc.get(o.id);\n if (!a || a.n === 0) continue;\n labels.push({\n id: `section:${o.id}`,\n kind: 'section',\n // The buyer-facing name wins over the technical one, as it does in 2D.\n text: o.displayLabel || o.label || o.id,\n anchor: [(a.x / a.n) * M, a.deck / a.n + SECTION_LABEL_LIFT_M, (a.y / a.n) * M],\n });\n }\n }\n }\n for (const unit of units) {\n for (const o of unit.objects) {\n if (o.type === 'text') {\n // Authored wayfinding sits just above the floor it annotates — a door or\n // aisle name belongs to the ground, not to the air above it.\n if (!o.text) continue;\n labels.push({\n id: `text:${o.id}`,\n kind: 'annotation',\n text: o.text,\n anchor: [o.position.x * M, unit.baseHeightM + ANNOTATION_LIFT_M, o.position.y * M],\n color: o.color,\n rotation: o.rotation,\n });\n } else if (o.type === 'booth') {\n // A booth is a sellable unit the buyer picks by NAME, so it is labelled\n // on top of its own stand rather than left as an anonymous box.\n const poly = boothPolygon(o);\n if (!poly) continue;\n const c = centroidOf(poly);\n if (!c) continue;\n labels.push({\n id: `booth:${o.id}`,\n kind: 'booth',\n text: o.displayLabel || o.label || o.id,\n anchor: [c.x * M, unit.baseHeightM + BOOTH_HEIGHT_M + BOOTH_LABEL_LIFT_M, c.y * M],\n });\n }\n }\n }\n\n // --- Floors ----------------------------------------------------------------\n const floors: SceneFloor[] = [];\n {\n const sectionFloor = new Map<string, number>();\n for (let ui = 0; ui < units.length; ui++) {\n for (const o of units[ui].objects) if (o.type === 'section') sectionFloor.set(o.id, ui);\n }\n const acc = units.map(() => ({ n: 0, minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity, sumY: 0 }));\n for (let i = 0; i < seats.length; i++) {\n const owner = surfaces.seatOwner[i];\n const ui = owner !== null ? sectionFloor.get(owner) : undefined;\n if (ui === undefined) continue;\n const a = acc[ui];\n const s = seats[i];\n a.n++;\n if (s.x < a.minX) a.minX = s.x;\n if (s.y < a.minY) a.minY = s.y;\n if (s.x > a.maxX) a.maxX = s.x;\n if (s.y > a.maxY) a.maxY = s.y;\n a.sumY += surfaces.seatDeckY(i);\n seatFloor[i] = ui;\n }\n for (let ui = 0; ui < units.length; ui++) {\n const a = acc[ui];\n const f = doc.floors?.[ui];\n floors.push({\n index: ui,\n id: f?.id ?? `floor-${ui}`,\n label: f?.name ?? (units.length > 1 ? `Floor ${ui + 1}` : 'Venue'),\n seatCount: a.n,\n center: a.n ? [((a.minX + a.maxX) / 2) * M, a.sumY / a.n, ((a.minY + a.maxY) / 2) * M] : [0, 0, 0],\n radius: a.n ? (0.5 * Math.hypot((a.maxX - a.minX) * M, (a.maxY - a.minY) * M) || 1) : 0,\n });\n }\n }\n\n const cx = ((fp.minX + fp.maxX) / 2) * M;\n const cz = ((fp.minY + fp.maxY) / 2) * M;\n const radius = 0.5 * Math.hypot((fp.maxX - fp.minX) * M, (fp.maxY - fp.minY) * M) || 10;\n\n return {\n solids,\n seats: seatData,\n bounds: { center: [cx, radius * 0.08, cz], radius, groundY: 0 },\n stateColorLUT: themeSeatColorLUT(theme, SEAT_STATES),\n theme,\n seatCount: seats.length,\n // Look-at target ~1.5 m up so a seated camera aims slightly down at the stage.\n focalWorld: [focal.x * M, 1.5, focal.y * M],\n zones,\n labels,\n floors,\n };\n}\n","/**\n * A section's RAKE FIELD — the scalar \"depth\" that its seating rises along.\n *\n * ## The defect this exists to fix\n *\n * Both the eye-height model (`layout.ts`) and the 3D seating surface\n * (`view3d/scene/surface.ts`) used to raise a seat by its radial distance from\n * ONE venue focal point. That is only correct when a section's rows are\n * concentric arcs about that focal.\n *\n * Measured on the amphitheatre gallery, whose rows are straight blocks: a single\n * row's seats span a radial-distance range of 105 chart units on the\n * centre-facing blocks and 240 on the side blocks. Since height is a function of\n * that distance, one row's seats landed at DIFFERENT heights — a within-row deck\n * spread of 2.30 m on the side blocks against 1.05 m on the centre-facing ones.\n * On screen the rows visibly tilt, and the tilt differs block to block, which is\n * exactly the left/right asymmetry the owner reported.\n *\n * ## Why a per-section AXIS is not enough\n *\n * The first attempt fitted one straight rake axis per section by PCA over its row\n * centroids, and scored it against the radial model. It did not fix the\n * amphitheatre, because `sec-gall` is a SINGLE section containing six wedge\n * blocks at six different orientations. No single axis — straight or radial —\n * fits them all, so the fit correctly fell back to radial and the tilt survived.\n * Sections containing several blocks are normal, not exotic.\n *\n * ## The model that does work\n *\n * Depth is defined by the section's OWN ROWS. Each row gets one depth value (the\n * mean distance of its seats from the focal), and the field at an arbitrary point\n * is an inverse-distance blend of the nearest rows' depths.\n *\n * The properties that matter:\n *\n * - **Exact on a row.** A point on a row has zero distance to it, so it takes\n * that row's depth exactly and every seat in the row lands at ONE height.\n * The tilt cannot come back, whatever the row's orientation.\n * - **Orientation-free.** Straight blocks, arcs, fans, in-the-round and blocks\n * at six different angles in one section all work, because nothing is fitted\n * to a direction.\n * - **Continuous.** It is a field over the plane, not a per-seat lookup. The 3D\n * cap is tessellated at points BETWEEN seats and needs a value and a gradient\n * there; a per-row lookup would leave the deck undefined between rows and put\n * the seats back off the surface they stand on.\n *\n * That last point is why quantising per row inside `assignEyeHeights` was tried\n * and removed: the renderer cannot follow a step function it has no definition\n * for, and the two models diverging is the trap that made every offset constant\n * chart-specific.\n */\n\nimport type { Point } from './types';\n\n/** A resolved row: its seats as an ordered polyline, plus its depth ordinate. */\nexport interface RakeRowFit {\n /** The row's seats, ordered along the row. */\n readonly pts: readonly Point[];\n /** Mean distance of the row's seats from the focal — what rise is a function of. */\n readonly depth: number;\n}\n\nexport interface SectionRake {\n readonly kind: 'radial' | 'rows';\n /** Depth ordinate at a chart-unit point; rise is a function of this. */\n depthAt(x: number, y: number): number;\n /** Unit gradient direction of `depthAt` — turns a rake angle into a normal. */\n gradientAt(x: number, y: number): readonly [number, number];\n /** Number of rows the field was built from (0 for the radial fallback). */\n readonly rowCount: number;\n /**\n * The section's rows, ORDERED BY DEPTH (front first).\n *\n * The deck is built directly from these — one level ribbon per row — rather\n * than by tessellating `depthAt`. See `deckBands.ts` for why: a section holding\n * several blocks at different heights has a genuine cliff between them, and no\n * continuous field can be both level on every row and trackable by refinement.\n */\n readonly rows: readonly RakeRowFit[];\n /** Index into `rows` of the row nearest a point, or -1 when there are none. */\n nearestRow(x: number, y: number): number;\n}\n\n/** One row's member points, in any order. */\nexport interface RakeRow {\n points: Point[];\n}\n\n/** How many nearest rows contribute to a blended sample. */\nconst BLEND_ROWS = 3;\n\n/**\n * A row further than this multiple of the nearest row's distance contributes\n * nothing. Just above 2 so the two rows bracketing a point midway between them\n * both count (their distances differ by at most the row pitch), while a row on\n * the far side of an aisle or beyond the last row does not.\n */\nconst BLEND_DISTANCE_RATIO = 2.2;\n\n/** Step used for the finite-difference gradient, chart units. */\nconst GRAD_STEP = 0.5;\n\nfunction radialRake(focal: Point): SectionRake {\n return {\n kind: 'radial',\n rowCount: 0,\n rows: [],\n nearestRow: () => -1,\n depthAt: (x, y) => Math.hypot(x - focal.x, y - focal.y),\n gradientAt: (x, y) => {\n const dx = x - focal.x, dy = y - focal.y;\n const d = Math.hypot(dx, dy);\n // At the focal the gradient is undefined; the surface is flat there anyway.\n return d < 1e-9 ? [0, 0] : [dx / d, dy / d];\n },\n };\n}\n\ninterface RowFit {\n /**\n * The row as an ordered POLYLINE through its own seats — not a straight chord.\n *\n * A chord was tried first and broke the arena: its upper-bowl rows are strong\n * arcs, so a seat at the end of a row sits far from the chord between the row's\n * extremes. The blend below then treated that seat as \"between rows\" and gave\n * it a neighbouring row's depth, which put the within-row spread UP from 0.07 m\n * to 2.83 m. Distance must be measured to the row's real shape, so that every\n * one of its seats reads as being exactly on it.\n */\n pts: Point[];\n /** Row centroid, and the radius of a circle about it containing the row. */\n cx: number;\n cy: number;\n radius: number;\n /** The row's depth ordinate — mean distance of its seats from the focal. */\n depth: number;\n}\n\n/**\n * Reduce a row to a segment plus a depth.\n *\n * The direction comes from the row's own extent (the two furthest-apart seats),\n * not from a fitted line: it is exact for a straight row, good enough for a\n * gently curved one, and cannot be thrown off by an outlier the way a\n * least-squares fit through few points can.\n */\nfunction fitRow(points: Point[], focal: Point): RowFit | null {\n if (!points.length) return null;\n let cx = 0, cy = 0, depth = 0;\n for (const p of points) {\n cx += p.x; cy += p.y;\n depth += Math.hypot(p.x - focal.x, p.y - focal.y);\n }\n const n = points.length;\n cx /= n; cy /= n; depth /= n;\n\n // Order the seats along the row so consecutive pairs are real segments. The\n // chord direction is only used for this ORDERING, never for distance, so a\n // curved row orders correctly even though its chord is a poor fit to it.\n let ax = points[0], far = -1;\n for (const p of points) {\n const d = Math.hypot(p.x - cx, p.y - cy);\n if (d > far) { far = d; ax = p; }\n }\n let dx = ax.x - cx, dy = ax.y - cy;\n const len = Math.hypot(dx, dy);\n if (len > 1e-9) { dx /= len; dy /= len; } else { dx = 1; dy = 0; }\n const pts = [...points].sort((p, q) =>\n ((p.x - cx) * dx + (p.y - cy) * dy) - ((q.x - cx) * dx + (q.y - cy) * dy));\n\n let radius = 0;\n for (const p of pts) {\n const d = Math.hypot(p.x - cx, p.y - cy);\n if (d > radius) radius = d;\n }\n return { pts, cx, cy, radius, depth };\n}\n\n/** Distance from a point to a row's polyline. */\nfunction distToRow(r: RowFit, x: number, y: number): number {\n const pts = r.pts;\n if (pts.length === 1) return Math.hypot(x - pts[0].x, y - pts[0].y);\n let best = Infinity;\n for (let i = 0; i + 1 < pts.length; i++) {\n const a = pts[i], b = pts[i + 1];\n const vx = b.x - a.x, vy = b.y - a.y;\n const len2 = vx * vx + vy * vy;\n let t = len2 > 1e-12 ? ((x - a.x) * vx + (y - a.y) * vy) / len2 : 0;\n if (t < 0) t = 0; else if (t > 1) t = 1;\n const d = Math.hypot(x - (a.x + t * vx), y - (a.y + t * vy));\n if (d < best) best = d;\n }\n return best;\n}\n\n/** How many rows survive the cheap bounding-circle prefilter. */\nconst CANDIDATE_ROWS = 8;\n\n/**\n * Inverse-distance blend of the nearest rows' depths.\n *\n * Only the nearest few rows contribute, so a distant block on the far side of the\n * venue cannot drag a section's near rows. Squared inverse distance makes the\n * nearest row dominate quickly, which keeps the field flat ALONG a row and\n * varying across it — the shape a rake actually has.\n */\nfunction sampleRows(rows: RowFit[], x: number, y: number): number {\n // Prefilter on each row's bounding circle. `centroidDist - radius` is a true\n // LOWER bound on the polyline distance, so this cannot discard a row that would\n // have won — it just keeps the exact polyline test off ~90 % of the rows, which\n // is what makes the field affordable per cap vertex.\n const candD = new Array<number>(CANDIDATE_ROWS).fill(Infinity);\n const candI = new Array<number>(CANDIDATE_ROWS).fill(-1);\n for (let i = 0; i < rows.length; i++) {\n const r = rows[i];\n const lower = Math.hypot(x - r.cx, y - r.cy) - r.radius;\n for (let k = 0; k < CANDIDATE_ROWS; k++) {\n if (lower < candD[k]) {\n for (let j = CANDIDATE_ROWS - 1; j > k; j--) { candD[j] = candD[j - 1]; candI[j] = candI[j - 1]; }\n candD[k] = lower; candI[k] = i;\n break;\n }\n }\n }\n\n // Nearest BLEND_ROWS by insertion (the list is short and this avoids a sort).\n const bestD = new Array<number>(BLEND_ROWS).fill(Infinity);\n const bestI = new Array<number>(BLEND_ROWS).fill(-1);\n for (const i of candI) {\n if (i < 0) continue;\n const d = distToRow(rows[i], x, y);\n for (let k = 0; k < BLEND_ROWS; k++) {\n if (d < bestD[k]) {\n for (let j = BLEND_ROWS - 1; j > k; j--) { bestD[j] = bestD[j - 1]; bestI[j] = bestI[j - 1]; }\n bestD[k] = d; bestI[k] = i;\n break;\n }\n }\n }\n if (bestI[0] < 0) return 0;\n // Exactly on a row: take its depth, so a seat is never blended off its own row.\n if (bestD[0] < 1e-6) return rows[bestI[0]].depth;\n\n // Blend only across rows at COMPARABLE distance. Absolute inverse-distance\n // weighting was tried and produced the cap's worst error: in the empty parts of\n // a section outline — beyond the last row, or the gap between two blocks — the\n // two nearest rows can both be hundreds of units away, so their weights stay\n // comparable and the field goes on interpolating between them across the void.\n // Measured, 79 of the gallery's 97 out-of-tolerance sample points sat more than\n // 100 units from ANY seat, and a cap triangle spanning that region missed the\n // surface by up to 10.84 m.\n //\n // A relative cutoff makes the far field settle to the nearest row's depth, i.e.\n // locally CONSTANT, which a flat triangle interpolates exactly. Inside the\n // seating the nearest rows are all within the cutoff, so blending is unchanged\n // and rows stay level.\n const cutoff = bestD[0] * BLEND_DISTANCE_RATIO;\n let num = 0, den = 0;\n for (let k = 0; k < BLEND_ROWS; k++) {\n const i = bestI[k];\n if (i < 0 || bestD[k] > cutoff) continue;\n const w = 1 / (bestD[k] * bestD[k]);\n num += w * rows[i].depth;\n den += w;\n }\n return den > 0 ? num / den : rows[bestI[0]].depth;\n}\n\nfunction rowsRake(rows: RowFit[]): SectionRake {\n const depthAt = (x: number, y: number): number => sampleRows(rows, x, y);\n const nearestRow = (x: number, y: number): number => {\n let best = Infinity, bestI = -1;\n for (let i = 0; i < rows.length; i++) {\n // Bounding-circle lower bound first; only test the polyline if it can win.\n const r = rows[i];\n if (Math.hypot(x - r.cx, y - r.cy) - r.radius >= best) continue;\n const d = distToRow(r, x, y);\n if (d < best) { best = d; bestI = i; }\n }\n return bestI;\n };\n return {\n kind: 'rows',\n rowCount: rows.length,\n rows,\n nearestRow,\n depthAt,\n gradientAt: (x, y) => {\n // Central differences: the blend has no closed form, and shading only needs\n // the direction. A half-unit step is far below row pitch, so this tracks\n // the field rather than smoothing across rows.\n const gx = (depthAt(x + GRAD_STEP, y) - depthAt(x - GRAD_STEP, y)) / (2 * GRAD_STEP);\n const gy = (depthAt(x, y + GRAD_STEP) - depthAt(x, y - GRAD_STEP)) / (2 * GRAD_STEP);\n const len = Math.hypot(gx, gy);\n return len < 1e-9 ? [0, 0] : [gx / len, gy / len];\n },\n };\n}\n\n/**\n * Build a section's rake field from its rows.\n *\n * `rows` should be the section's member seats grouped by row. With fewer than two\n * usable rows there is nothing to build a field from, so the radial model is kept\n * — which is also how every chart behaved before this, leaving a row-less or\n * single-row section unchanged.\n */\nexport function buildSectionRake(rows: RakeRow[], focal: Point): SectionRake {\n const fits: RowFit[] = [];\n for (const r of rows) {\n const f = fitRow(r.points, focal);\n if (f) fits.push(f);\n }\n if (fits.length < 2) return radialRake(focal);\n // Front-first ordering is the contract `rows` promises, and the deck builder\n // relies on it to pair each ribbon with the riser below it.\n fits.sort((a, b) => a.depth - b.depth);\n return rowsRake(fits);\n}\n","/**\n * THE seating surface — one function per section, consumed by everything.\n *\n * Before this module the venue had three independently-computed surfaces (tier\n * cap, row treads, seat dots) held apart by hardcoded offsets that had to agree,\n * and never did across chart shapes. Two measured defects came out of that:\n *\n * - The tier cap was a raw earcut triangulation of the section outline with a\n * *radial, nonlinear* height evaluated only at outline vertices. A 90 m chord\n * across a curved bowl is a flat plane through the seating: 23 % of arena\n * seats and 48 % of amphitheatre seats rendered UNDER their own tier cap\n * (worst overshoot 5.54 m).\n * - The cap took each section's front-edge datum from its OUTLINE vertices and\n * the doc focal, while `layout.ts`'s `assignEyeHeights` takes it from the\n * section's SEATS and each seat's own `focalPoint`. Different datum ⇒ a\n * per-section constant disagreement even where tessellation was exact.\n *\n * `deckAt(x, y)` here is the single definition of \"the height of the walkable\n * seating surface\". It mirrors `assignEyeHeights` exactly — same front-edge\n * datum, same drawn-radial-depth rise — so a seat dot and the deck it stands on\n * can no longer be computed from different models. The cap is tessellated\n * against this same function (see `extrudePrism`'s `maxCapError`), so the drawn\n * triangle mesh tracks it rather than chording across it.\n *\n * Pure: no OGL, no DOM. Unit-tested in workers/api/test/view3dSurface.test.ts.\n */\n\nimport type { ChartObject, ExpandedSeat, Point, SectionObject } from '../../core/types';\nimport { pointInPolygonWithHoles } from '../../core/layout';\nimport { buildSectionRake } from '../../core/rake';\nimport { resolveSection, distanceToPolyline, type ResolvedRow } from '../../core/venueStructure';\nimport { CHART_UNITS_PER_METRE, METRES_PER_CHART_UNIT, SEATED_EYE_HEIGHT_M, sectionGeometry } from '../../core/units';\nimport { outsetRing } from './geometry';\nimport { SEAT_DOT_RADIUS_M } from './seatInstances';\n\n/** Top of the thin slab drawn for a section with no authored height/rake. Seats\n * on a flat chart rest on this, so it is part of the surface definition rather\n * than a constant duplicated by the seat builder. */\nexport const FLAT_SLAB_TOP_M = 0.05;\n\n/**\n * Subdivision TARGET for the drawn tier cap, metres — how closely it tries to\n * track `deckAt`. Consumed by `sceneModel` when it extrudes a tier.\n *\n * A target, not a guarantee: refinement is uniform per section (the only way to\n * stay watertight — see `emitCapUniform`) and bounded by a triangle budget, so a\n * section whose surface folds sharply stops refining before it reaches this.\n * {@link SEAT_CLEARANCE_M} is what actually protects the seats.\n */\nexport const CAP_MAX_ERROR_M = 0.05;\n\n/**\n * Clearance between a seat dot's origin and the deck it stands on, metres.\n *\n * The cap's error is ONE-SIDED and that is what makes a single clearance work.\n * `deckAt` is a cone — front-edge height plus a linear function of radial\n * distance — which is convex, so a triangle interpolating it lies at or above it\n * everywhere inside the triangle, never below. The cap can therefore only ever\n * float above the true surface, and only a seat needs protecting from it.\n *\n * The size, however, is MEASURED rather than proven. Refinement aims at\n * {@link CAP_MAX_ERROR_M} but is uniform per section and capped by a triangle\n * budget, and the residual peaks at the surface's fold, where a section's flat\n * front plateau turns into its rake. Measured worst cap error across all four\n * harness charts is 13 cm. 15 cm carries that while staying under the ~22 cm\n * seat-dot radius, so it is invisible. THIS is the number that must exceed the\n * cap's real error — the test suite asserts exactly that relationship.\n *\n * The test suite enforces the real invariant directly — zero buried seats on\n * every harness chart — so a regression in either number fails loudly rather\n * than silently eating the margin. If a chart ever needs a tighter cap, the fix\n * is fold-aware refinement (splitting triangles that straddle `frontU`), not a\n * bigger clearance.\n */\nexport const SEAT_CLEARANCE_M = 0.15;\n\n/**\n * How far outside its authored outline a section still owns seats, chart units.\n *\n * Matches the deck padding in `buildTier` exactly (1.5 seat-dot radii), because\n * it answers the same question: the deck is drawn at that padded ring, so a seat\n * standing on drawn deck must belong to the section that drew it. Any smaller and\n * boundary seats are orphaned onto the floor beneath their own tier.\n */\nexport const SEAT_OWNERSHIP_PAD_U = SEAT_DOT_RADIUS_M * 1.5 * CHART_UNITS_PER_METRE;\n\n/** One section's resolved seating surface. */\nexport interface SectionSurface {\n readonly sectionId: string;\n /** World-metre height of the seating deck at a chart-unit point. */\n deckAt(x: number, y: number): number;\n /**\n * ANALYTIC unit normal of the deck at a chart-unit point, world axes (y up).\n *\n * The drawn cap is a piecewise-linear approximation of `deckAt`, so its FACE\n * normals are discontinuous across every shared edge — measured at 46 % of cap\n * edges breaking >2°, worst 110°, which flat-shades into the dark streaks\n * across a raked deck. Worse, the near-degenerate triangles earcut emits at a\n * densified boundary (aspect ratios to 8.7e6) have numerically meaningless\n * face normals, so they shade as random dark needles.\n *\n * `deckAt` is a cone and therefore differentiable everywhere except its fold,\n * so the true normal is available in closed form and costs nothing. Shading the\n * cap with it instead of the face normal makes the surface read as smooth\n * regardless of tessellation AND makes a sliver's shading harmless — it now\n * matches its neighbours even when its own geometry is degenerate.\n */\n normalAt(x: number, y: number): [number, number, number];\n /** True when the section carries no rake — the deck is a constant plane. */\n readonly flat: boolean;\n /** Bottom of the section prism (floor base), world metres. */\n readonly bottomY: number;\n /**\n * The section's rows with the world-metre LEVEL each one sits at, front first.\n *\n * This is what the deck is actually built from. A row is a level ribbon and\n * consecutive ribbons are joined by a riser, so the drawn surface passes\n * exactly through every row instead of approximating a field between them —\n * and because a ribbon is horizontal, its cap error against the seats it\n * carries is zero by construction rather than by measurement.\n *\n * Empty for a flat section, and for a section with fewer than two rows (which\n * keeps the old smooth-cone path, since there is no row structure to build on).\n */\n readonly rowLevels: readonly { readonly pts: readonly Point[]; readonly y: number; readonly depth: number; readonly blockId: number }[];\n /** The landing height for the parts of the outline that hold no rows. */\n readonly landingY: number;\n}\n\nexport interface VenueSurfaces {\n /** Surface by section object id (NOT logical id — heights are per object). */\n bySection: Map<string, SectionSurface>;\n /** Owning section object id per seat index, or null when a seat sits outside\n * every section (free-standing rows). Parallel to the input seat array. */\n seatOwner: Array<string | null>;\n /** Deck height in world metres for seat index i (falls back to the seat's own\n * resolved eye height when it owns no section). */\n seatDeckY(i: number): number;\n /**\n * Seat pitch in CHART UNITS for seat index i, or undefined when its section\n * has no resolved rows.\n *\n * The spacing a seat actually has to itself — the smaller of its own row's\n * seat spacing and the gap to the neighbouring row. Callers size a seat's\n * marker from this.\n *\n * It is emphatically NOT the distance to the nearest other seat. That measure\n * shrank 80 amphitheatre dots to as little as 0.089 m, every one of them at a\n * WEDGE BOUNDARY: the last seat of one wedge sits 0.212 m from the first seat\n * of the next across an aisle, far closer than the 0.55 m spacing inside either\n * row. Two seats being near each other across an aisle says nothing about how\n * much room either one has.\n */\n seatPitchU(i: number): number | undefined;\n}\n\ninterface Bbox { minX: number; minY: number; maxX: number; maxY: number }\n\nfunction bboxOf(pts: Point[]): Bbox {\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;\n for (const p of pts) {\n if (p.x < minX) minX = p.x;\n if (p.y < minY) minY = p.y;\n if (p.x > maxX) maxX = p.x;\n if (p.y > maxY) maxY = p.y;\n }\n return { minX, minY, maxX, maxY };\n}\n\n/** Hard ceiling on rake rise so a mis-authored or focal-wrapping section can\n * never produce a runaway spike (defect guard, carried over from buildTier). */\nconst MAX_TIER_RISE_M = 25;\n\nexport interface SurfaceUnit {\n objects: ChartObject[];\n focal: Point;\n baseHeightM: number;\n}\n\n/**\n * Resolve every section's seating surface and each seat's owning section.\n *\n * The front-edge datum is taken from the section's member SEATS when it has any\n * (matching `assignEyeHeights`), and only falls back to the outline's nearest\n * vertex for a section with no seats — where nothing can disagree with it.\n */\nexport function buildVenueSurfaces(units: SurfaceUnit[], seats: ExpandedSeat[]): VenueSurfaces {\n const bySection = new Map<string, SectionSurface>();\n const seatOwner = new Array<string | null>(seats.length).fill(null);\n const seatDeck = new Float64Array(seats.length);\n /** Level of the seat's own row, when its section resolved into rows. */\n const seatRowLevel = new Array<number | undefined>(seats.length).fill(undefined);\n /** Pitch of the seat's own row (chart units), when resolved. */\n const seatPitch = new Array<number | undefined>(seats.length).fill(undefined);\n\n // --- Pass 1: ownership + per-section seat front distance -------------------\n interface Acc {\n section: SectionObject;\n unit: SurfaceUnit;\n /** Nearest drawn focal distance among member seats (chart units). */\n frontU: number;\n hasSeats: boolean;\n /** Member seat positions grouped by row — the input the rake axis is fitted from. */\n rows: Map<string, Point[]>;\n /** Indices of this section's member seats, for the structure resolver. */\n seatIndices: number[];\n }\n const acc = new Map<string, Acc>();\n const boxes: Array<{ id: string; box: Bbox; section: SectionObject; unit: SurfaceUnit; outline: Point[] }> = [];\n for (const unit of units) {\n for (const o of unit.objects) {\n if (o.type !== 'section' || !o.outline || o.outline.length < 3) continue;\n // Ownership is tested against the PADDED outline — the same ring the deck\n // is actually drawn with (see `buildTier`'s `outsetRing`). Testing the raw\n // outline instead left seats authored just outside it unowned, so they fell\n // to the floor while the drawn deck covered them: measured as 9 buried\n // cinema seats and a 0.20 m drop on the uber arena. A seat resting on drawn\n // deck belongs to the section that drew it.\n const owned = outsetRing(o.outline, SEAT_OWNERSHIP_PAD_U);\n acc.set(o.id, { section: o, unit, frontU: Infinity, hasSeats: false, rows: new Map(), seatIndices: [] });\n boxes.push({ id: o.id, box: bboxOf(owned), section: o, unit, outline: owned });\n }\n }\n\n for (let i = 0; i < seats.length; i++) {\n const s = seats[i];\n for (const b of boxes) {\n // Bbox prefilter keeps the 14k-seat charts cheap; the polygon test only\n // runs for genuine candidates.\n if (s.x < b.box.minX || s.x > b.box.maxX || s.y < b.box.minY || s.y > b.box.maxY) continue;\n if (!pointInPolygonWithHoles({ x: s.x, y: s.y }, b.outline, b.section.holes)) continue;\n seatOwner[i] = b.id;\n const a = acc.get(b.id)!;\n a.hasSeats = true;\n const f = s.focalPoint ?? b.unit.focal;\n const d = Math.hypot(s.x - f.x, s.y - f.y);\n if (d < a.frontU) a.frontU = d;\n // Group by row, exactly as assignEyeHeights does — the rake axis is fitted\n // from the same grouping in both files, or the two models diverge again.\n a.seatIndices.push(i);\n const rowKey = s.rowId || `__seat-${i}`;\n const arr = a.rows.get(rowKey);\n if (arr) arr.push({ x: s.x, y: s.y }); else a.rows.set(rowKey, [{ x: s.x, y: s.y }]);\n break; // first containing section wins, exactly as assignEyeHeights does\n }\n }\n\n // --- Pass 2: build one surface function per section ------------------------\n for (const [id, a] of acc) {\n const geo = sectionGeometry(a.section, { floorBaseHeightM: a.unit.baseHeightM });\n const bottomY = a.unit.baseHeightM;\n const rakeTan = geo.rake > 0 ? Math.tan((geo.rake * Math.PI) / 180) : 0;\n // An authored override wins over inference (see `SectionObject.surfaceKind`).\n // `rakedRows` cannot manufacture relief a section does not carry, so it only\n // prevents a raked section from being flattened, never the reverse.\n const inferredFlat = geo.rake <= 0.01 && geo.height <= bottomY + 0.001;\n const kind = a.section.surfaceKind;\n const flat = kind === 'flat' ? true : kind === 'rakedRows' ? geo.rake > 0.01 ? false : inferredFlat : inferredFlat;\n\n // The section's own rake axis, fitted from its rows. `layout.ts` fits the\n // identical axis from the identical grouping; DEPTH, the front datum and the\n // rise all then come from one field, which is what keeps a seat dot and the\n // deck it stands on from being computed under different models.\n const rake = buildSectionRake([...a.rows.values()].map((points) => ({ points })), a.unit.focal);\n\n let frontU = Infinity;\n if (a.hasSeats) {\n for (const pts of a.rows.values()) {\n for (const p of pts) {\n const d = rake.depthAt(p.x, p.y);\n if (d < frontU) frontU = d;\n }\n }\n }\n if (!a.hasSeats || !Number.isFinite(frontU)) {\n frontU = Infinity;\n for (const p of a.section.outline) {\n const d = rake.depthAt(p.x, p.y);\n if (d < frontU) frontU = d;\n }\n }\n const flatTop = bottomY + FLAT_SLAB_TOP_M;\n const baseFloor = bottomY + FLAT_SLAB_TOP_M;\n\n /** Height for a depth ordinate measured from the venue focal (fallback path). */\n const levelFor = (depthU: number): number => {\n const depthM = Math.max(0, depthU - frontU) * METRES_PER_CHART_UNIT;\n const rise = Math.min(depthM * rakeTan, MAX_TIER_RISE_M);\n return Math.max(baseFloor, geo.height + rise);\n };\n\n /** Height for a depth measured back from a BLOCK's own front row. */\n const levelForBlockDepth = (blockDepthU: number): number => {\n const depthM = Math.max(0, blockDepthU) * METRES_PER_CHART_UNIT;\n const rise = Math.min(depthM * rakeTan, MAX_TIER_RISE_M);\n return Math.max(baseFloor, geo.height + rise);\n };\n\n // One level per row, from the resolved structure. THIS is the deck.\n //\n // Depth is measured inside the row's own BLOCK, so every block of a tier\n // starts at the tier's authored height. Measuring it from the venue focal\n // (which is what `rake.depthAt` does, and what this used to do) raised a\n // bowl's side wedges as though they were further back: sec-gall's six blocks\n // are one tier authored at 6.20 m and started at 6.35 to 10.65 m.\n const structure = a.hasSeats\n ? resolveSection(id, seats, a.seatIndices, a.unit.focal)\n : { sectionId: id, rows: [] as ResolvedRow[], blockCount: 0 };\n const rowLevels = flat\n ? []\n : structure.rows.map((r) => ({\n pts: r.pts,\n y: levelForBlockDepth(r.blockDepth),\n depth: r.blockDepth,\n blockId: r.blockId,\n }));\n const landingY = rowLevels.length ? rowLevels[0].y : flatTop;\n\n // Bounding circles over the rows, so the nearest-row lookup below prunes\n // instead of walking every polyline in the section. A 50k venue has sections\n // with thousands of rows and this is called per cap sample.\n const rowBounds = rowLevels.map((r) => {\n let cx = 0, cy = 0;\n for (const p of r.pts) { cx += p.x; cy += p.y; }\n const n = r.pts.length || 1;\n cx /= n; cy /= n;\n let rad = 0;\n for (const p of r.pts) {\n const d = Math.hypot(p.x - cx, p.y - cy);\n if (d > rad) rad = d;\n }\n return { cx, cy, rad };\n });\n\n const deckAt = flat\n ? (): number => flatTop\n : rowLevels.length >= 2\n ? (x: number, y: number): number => {\n // The deck is the level of the row you are standing on. A step\n // function, deliberately: it is the drawn geometry, not an\n // approximation of it, so a seat and its ribbon cannot disagree.\n let best = Infinity, bestY = landingY;\n for (let i = 0; i < rowLevels.length; i++) {\n const b = rowBounds[i];\n // Admissible lower bound — cannot discard a row that would have won.\n if (Math.hypot(x - b.cx, y - b.cy) - b.rad >= best) continue;\n const d = distanceToPolyline(rowLevels[i].pts, x, y);\n if (d < best) { best = d; bestY = rowLevels[i].y; }\n }\n return bestY;\n }\n : (x: number, y: number): number => levelFor(rake.depthAt(x, y));\n\n // The gradient of the same expression. Height rises with radial distance\n // only, so the surface tilts purely along the outward radial direction and\n // the slope is exactly `rakeTan` — except on the flat front plateau\n // (d <= frontU), past the runaway clamp, and where the baseFloor floor wins,\n // all of which are locally horizontal.\n const UP: [number, number, number] = [0, 1, 0];\n // Every ribbon and every landing is HORIZONTAL, so the deck's normal is up\n // everywhere and the analytic-normal machinery has nothing left to correct.\n // The only non-horizontal deck surfaces are the risers, and those carry their\n // own normals from the band builder.\n const normalAt = rowLevels.length >= 2 || flat\n ? (): [number, number, number] => UP\n : (x: number, y: number): [number, number, number] => {\n const d = rake.depthAt(x, y);\n if (d <= frontU) return UP;\n const depthM = (d - frontU) * METRES_PER_CHART_UNIT;\n if (depthM * rakeTan >= MAX_TIER_RISE_M) return UP; // clamped: flat again\n if (geo.height + depthM * rakeTan <= baseFloor) return UP; // floored: flat\n const [gx, gy] = rake.gradientAt(x, y);\n if (gx === 0 && gy === 0) return UP;\n const inv = 1 / Math.hypot(rakeTan, 1);\n return [-gx * rakeTan * inv, inv, -gy * rakeTan * inv];\n };\n\n // A seat takes its own row's level DIRECTLY. Searching for the nearest row\n // would give the same answer almost always and the wrong one occasionally\n // (where two blocks interleave), and it would cost a polyline search per seat\n // on a chart that may hold 50,000 of them. Row membership is already known.\n if (!flat) {\n for (const r of structure.rows) {\n const y = levelForBlockDepth(r.blockDepth);\n for (const si of r.seatIndices) seatRowLevel[si] = y;\n }\n }\n\n // Per-row pitch: the median spacing ALONG the row (immune to the aisle, which\n // falls between rows, not inside one) capped by the gap to the nearest other\n // row, so a dot cannot grow into the row in front either.\n for (const r of structure.rows) {\n const gaps: number[] = [];\n for (let k = 1; k < r.pts.length; k++) {\n const d = Math.hypot(r.pts[k].x - r.pts[k - 1].x, r.pts[k].y - r.pts[k - 1].y);\n if (d > 1e-6) gaps.push(d);\n }\n gaps.sort((x, y) => x - y);\n const along = gaps.length ? gaps[Math.floor(gaps.length / 2)] : Infinity;\n\n // Measured against rows in the SAME BLOCK only. A row in a neighbouring\n // block runs BESIDE this one, not in front of it, and near a block boundary\n // it can pass within a hair of the probe: measured across the catalog that\n // collapsed the pitch to 0.04 mm on the opera house and 0.8 mm on the\n // esports arena, which then drove every dot there to the visibility floor.\n // This is the same mistake as sizing a seat against a neighbour across an\n // aisle, one level up.\n let across = Infinity;\n const probe = r.pts[Math.floor(r.pts.length / 2)];\n if (probe) {\n for (const other of structure.rows) {\n if (other === r || other.blockId !== r.blockId) continue;\n const d = distanceToPolyline(other.pts, probe.x, probe.y);\n if (d > 1e-6 && d < across) across = d;\n }\n }\n // Never below half the in-row spacing. Two rows can genuinely be authored\n // on top of each other (chairs around a table cross their neighbours'\n // arcs), and a seat's own row spacing is still real when that happens.\n const pitch = Math.min(along, Math.max(across, along * 0.5));\n if (Number.isFinite(pitch) && pitch > 0) {\n for (const si of r.seatIndices) seatPitch[si] = pitch;\n }\n }\n\n bySection.set(id, { sectionId: id, deckAt, normalAt, flat, bottomY, rowLevels, landingY });\n }\n\n // --- Pass 3: bake each seat's deck height from its owner's surface ---------\n for (let i = 0; i < seats.length; i++) {\n const ownerId = seatOwner[i];\n const s = seats[i];\n if (ownerId) {\n const own = seatRowLevel[i];\n seatDeck[i] = (own ?? bySection.get(ownerId)!.deckAt(s.x, s.y)) + SEAT_CLEARANCE_M;\n continue;\n }\n // Seat outside every section: fall back to its own resolved eye height, the\n // only surface information it carries.\n const eye = s.eyeHeightM;\n seatDeck[i] = Number.isFinite(eye) ? Math.max(0, (eye as number) - SEATED_EYE_HEIGHT_M) : 0;\n }\n\n return {\n bySection,\n seatOwner,\n seatDeckY: (i: number): number => seatDeck[i],\n seatPitchU: (i: number): number | undefined => seatPitch[i],\n };\n}\n","/**\n * The raked deck, built from a section's ROWS instead of from its outline.\n *\n * ## Why the deck is not a tessellated surface any more\n *\n * The previous deck was one cap over the section outline, tessellated against a\n * height field and refined until it tracked it. That works only while the field\n * is smooth and convex. Once height is defined so that every row is LEVEL — the\n * fix for rows visibly tilting, see `core/rake.ts` — the field stops being\n * either:\n *\n * - A section commonly holds several blocks at different heights (the\n * amphitheatre gallery is one section holding six wedges). Any field that is\n * level on each row must JUMP between blocks. Measured, that jump drove the\n * cap's worst error to 11 m against a 0.15 m bound.\n * - Smoothing the jump away puts the tilt straight back. The two requirements\n * are contradictory, so no amount of refinement resolves it.\n *\n * The jump is not an artefact — it is a step between two blocks, which is a\n * WALL. Walls are geometry. So the deck is emitted as geometry that already has\n * the right shape rather than as a surface something has to approximate:\n *\n * - one **ribbon** per row, horizontal, at that row's level;\n * - a **riser** dropping from each ribbon's front edge to the level below;\n * - a flat **landing** under everything, for the parts of the outline that hold\n * no rows (aisles, margins, the gaps between blocks).\n *\n * Every deck vertex therefore lies on a row, where the height is exactly defined.\n * A seat's clearance above its own ribbon is exact by construction instead of\n * measured against a tessellation error, and because every ribbon is horizontal\n * the whole deck shades with a single up normal.\n */\n\nimport type { Point } from '../../core/types';\nimport type { RGB } from '../palette';\nimport polygonClipping from 'polygon-clipping';\nimport { CHART_UNITS_PER_METRE } from '../../core/units';\nimport { SEAT_DOT_RADIUS_M } from './seatInstances';\nimport earcut from 'earcut';\nimport { MeshBuilder, M } from './geometry';\n\n/** A row ready to draw: its seats as a polyline, and the level it sits at. */\nexport interface BandRow {\n readonly pts: readonly Point[];\n /** World-metre height of this row's ribbon. */\n readonly y: number;\n /**\n * The row's depth ordinate, chart units — its MEAN distance from the focal.\n *\n * Carried through rather than recomputed here. Deriving it from the row's first\n * seat was tried and buried 20 seats: in a section holding several blocks, one\n * row's first seat and the next row's first seat can be in different blocks\n * entirely, so the implied row gap was wrong by a large factor and a ribbon\n * reached forward far enough to cover the row in front of it.\n */\n readonly depth: number;\n /**\n * Which block (stand) this row belongs to.\n *\n * Neighbour and riser resolution must stay inside a block. Across blocks the\n * nearest row is often the one across an aisle, at a different level, so a\n * ribbon would size itself against a stand it is not part of and a riser would\n * step down onto the wrong deck.\n */\n readonly blockId: number;\n}\n\n/**\n * How far a ribbon reaches FORWARD, as a fraction of the gap to the row in front.\n *\n * Deliberately past the halfway line. A ribbon is built by offsetting its own\n * row's polyline, and the ribbon in front is built by offsetting a different\n * polyline with a different vertex count, so their edges do not land on exactly\n * the same curve. Meeting them at 0.5 each would leave hairline gaps showing the\n * landing through the deck. Overlapping instead is free: the ribbon behind is\n * HIGHER, so it simply covers the seam, and the riser closes the vertical face.\n */\nconst FRONT_REACH = 0.58;\n\n/**\n * Smallest step worth drawing a riser for, world metres.\n *\n * A 1e-4 threshold was tried and emitted 142 needle triangles with aspect ratios\n * to 17,776: risers 5 m long and 0.3 mm tall, where two rows resolved to almost\n * the same level. Such a step is invisible, and the ribbons already overlap\n * enough to close the seam without it. 1 cm is below anything a viewer can see\n * and far above the degenerate range.\n */\nconst MIN_RISER_M = 0.01;\n\n/**\n * Smallest half-width a ribbon may have, chart units.\n *\n * A seat's dot has a real world radius, so a ribbon narrower than that leaves the\n * dot's rim hanging over the aisle beside it — the same defect the deck padding\n * (`outsetRing`) exists to prevent on the cap path, and it is the same 1.5x\n * margin here. Two orchestra seats were still failing on ribbon width alone\n * before this floor; row pitch happened to be barely over a dot diameter there.\n */\nconst MIN_REACH_U = SEAT_DOT_RADIUS_M * 1.5 * CHART_UNITS_PER_METRE;\n\n/** How far a ribbon reaches BACK, as a fraction of the gap to the row behind. */\nconst BACK_REACH = 0.5;\n\n/**\n * How far a ribbon extends past its end seats, as a fraction of the SEAT spacing\n * along the row — not of the row gap.\n *\n * The row gap was tried and buried 23 seats. A section can hold several blocks\n * side by side (the amphitheatre gallery holds six), and the aisle between two\n * blocks is often narrower than a row gap, so a ribbon extended by a row gap\n * reached over its neighbour's seats — which sit at a DIFFERENT level, so the\n * overhanging ribbon painted over them. Seat spacing is the right measure: it is\n * exactly enough to carry the end seat's own dot and cannot cross an aisle.\n */\nconst END_REACH = 0.6;\n\n/**\n * Outward normals along a polyline, pointing in the direction of increasing\n * depth (away from `focal`).\n *\n * Taken from the row's own local direction rather than from the height field's\n * gradient: the field is a step function now, so its gradient is zero almost\n * everywhere and undefined on the steps.\n */\nfunction rowNormals(pts: readonly Point[], focal: Point): Array<readonly [number, number]> {\n const n = pts.length;\n const raw: Array<readonly [number, number]> = [];\n for (let i = 0; i < n; i++) {\n // Central difference gives a smooth normal along a curved row; the ends fall\n // back to their single adjacent segment.\n const a = pts[Math.max(0, i - 1)];\n const b = pts[Math.min(n - 1, i + 1)];\n let dx = b.x - a.x, dy = b.y - a.y;\n const len = Math.hypot(dx, dy);\n if (len < 1e-9) { raw.push([0, 0]); continue; }\n dx /= len; dy /= len;\n raw.push([-dy, dx]);\n }\n\n // Orient the WHOLE ROW at once, from the sum of the per-vertex tests, rather\n // than flipping each vertex on its own.\n //\n // Per-vertex orientation was tried and left a hole in the deck. The test is\n // \"does this normal point away from the focal\", and its value passes through\n // zero when a row runs radially — straight at the stage rather than across it.\n // The amphitheatre orchestra has such rows, and there the sign flipped between\n // two adjacent vertices, so the quad between them was built with its two ends\n // offset in OPPOSITE directions: a bow-tie that covers neither side properly.\n // One seat's dot rim ended up over the void.\n //\n // A row is a single object with one front and one back, so the decision belongs\n // to the row. Summing is also the stable form of the same test: it is dominated\n // by the vertices where the answer is unambiguous.\n let vote = 0;\n for (let i = 0; i < n; i++) {\n vote += (pts[i].x - focal.x) * raw[i][0] + (pts[i].y - focal.y) * raw[i][1];\n }\n const flip = vote < 0;\n return flip ? raw.map(([x, y]) => [-x, -y] as const) : raw;\n}\n\n/** Extend a polyline past both ends along its end tangents by `by` chart units. */\nfunction extendEnds(pts: readonly Point[], by: number): Point[] {\n if (pts.length < 2 || by <= 0) return [...pts];\n const out = [...pts];\n const dir = (p: Point, q: Point): Point => {\n const dx = q.x - p.x, dy = q.y - p.y;\n const len = Math.hypot(dx, dy) || 1;\n return { x: dx / len, y: dy / len };\n };\n const head = dir(out[1], out[0]);\n const tail = dir(out[out.length - 2], out[out.length - 1]);\n out.unshift({ x: out[0].x + head.x * by, y: out[0].y + head.y * by });\n out.push({\n x: out[out.length - 1].x + tail.x * by,\n y: out[out.length - 1].y + tail.y * by,\n });\n return out;\n}\n\n/** Perpendicular distance from a point to a row's polyline, chart units. */\nfunction distToPolyline(pts: readonly Point[], x: number, y: number): number {\n if (pts.length === 1) return Math.hypot(x - pts[0].x, y - pts[0].y);\n let best = Infinity;\n for (let i = 0; i + 1 < pts.length; i++) {\n const a = pts[i], b = pts[i + 1];\n const vx = b.x - a.x, vy = b.y - a.y;\n const len2 = vx * vx + vy * vy;\n let t = len2 > 1e-12 ? ((x - a.x) * vx + (y - a.y) * vy) / len2 : 0;\n if (t < 0) t = 0; else if (t > 1) t = 1;\n const d = Math.hypot(x - (a.x + t * vx), y - (a.y + t * vy));\n if (d < best) best = d;\n }\n return best;\n}\n\n/** A row's SPATIAL neighbours: how wide its ribbon is, and what it steps down to. */\ninterface Neighbourhood {\n /** Perpendicular distance to the nearest other row, chart units. */\n pitch: number;\n /** World-metre level of the nearest row in FRONT, or null if this row is first. */\n belowY: number | null;\n}\n\n/**\n * Resolve each row's neighbours by POSITION, not by order in the array.\n *\n * `rows` arrives sorted by depth across the whole section, and a section\n * routinely holds several blocks. Two consecutive entries in that ordering are\n * usually the same row index in two DIFFERENT blocks, at almost the same depth —\n * so treating them as neighbours gave a row pitch of nearly zero. Measured, that\n * produced ribbons a couple of centimetres wide (so seat rims hung off the deck\n * in 394 of 400 samples) and risers 5 m long by 0.3 mm tall.\n *\n * The spatially nearest row is the real neighbour whatever block it is in, and\n * the nearest row that is closer to the focal is the one this ribbon steps down\n * onto.\n */\nfunction neighbourhoods(rows: readonly BandRow[]): Neighbourhood[] {\n // Probe from points ON the row, at a quarter, a half and three quarters along\n // it. The centroid was tried and broke the arena: its rows are strong arcs, and\n // an arc's centroid lies well inside the curve rather than on it, so distances\n // measured from there are not the row pitch at all (arena rims went 0 -> 272\n // off deck). A point on the polyline measures the true perpendicular gap, and\n // three of them keep one odd row end from setting the whole ribbon's width.\n const probesOf = (pts: readonly Point[]): Point[] => {\n const n = pts.length;\n if (n <= 2) return [...pts];\n return [pts[Math.floor(n * 0.25)], pts[Math.floor(n * 0.5)], pts[Math.floor(n * 0.75)]];\n };\n\n const out: Neighbourhood[] = [];\n for (let i = 0; i < rows.length; i++) {\n const probes = probesOf(rows[i].pts);\n const pitches: number[] = [];\n let bestFrontD = Infinity, belowY: number | null = null;\n for (const c of probes) {\n let nearest = Infinity;\n for (let j = 0; j < rows.length; j++) {\n // Neighbours are rows at a DIFFERENT LEVEL, not rows in the same block.\n //\n // \"Same block\" was tried and broke the concert hall: block detection\n // legitimately fragments one stand into several (sec-terr-0's 12 rows\n // resolved into 4), and a fragment's front neighbour then sits in another\n // block and was skipped — so the pitch came from a distant row, the\n // ribbon reached far forward, and it buried the seats of the row in front\n // by 0.20 m.\n //\n // Level is the property that actually distinguishes the two cases: a\n // lateral neighbour across an aisle shares this row's level (it is the\n // same row, split), while the row in front or behind does not.\n if (j === i || Math.abs(rows[j].y - rows[i].y) < 1e-3) continue;\n const d = distToPolyline(rows[j].pts, c.x, c.y);\n if (d < nearest) nearest = d;\n // \"In front\" = nearer the focal, i.e. a smaller depth ordinate.\n if (rows[j].depth < rows[i].depth && d < bestFrontD) {\n bestFrontD = d;\n belowY = rows[j].y;\n }\n }\n if (Number.isFinite(nearest) && nearest > 0) pitches.push(nearest);\n }\n pitches.sort((a, b) => a - b);\n // Median: robust to one probe landing beside an aisle or a short row.\n const pitch = pitches.length ? pitches[Math.floor(pitches.length / 2)] : 1;\n out.push({ pitch, belowY });\n }\n return out;\n}\n\nexport interface BandColors {\n /** Ribbon (tread) top colour, AO already applied. */\n tread: RGB;\n /** Riser face colour, AO already applied. */\n riser: RGB;\n}\n\n\n/** One row's ribbon: the polyline it is built on, its normals, and its reaches. */\ninterface Ribbon {\n pts: Point[];\n nrm: Array<readonly [number, number]>;\n front: number;\n back: number;\n}\n\n/**\n * Resolve one row's ribbon geometry.\n *\n * Shared by the mesh emitter and the footprint builder so the drawn ribbons and\n * the block outline extruded beneath them are derived from ONE computation. They\n * disagreeing is the same class of bug as the cap and the seats disagreeing.\n */\nfunction ribbonOf(rows: readonly BandRow[], i: number, nbrs: Neighbourhood[], focal: Point): Ribbon | null {\n const row = rows[i];\n // A \"row\" of one seat is a free-standing seat, not a row — it still needs a\n // deck under it, so give it a short segment across the view direction and let\n // the normal ribbon path carry it. Skipping these left their dots hanging\n // over the landing.\n const rowPts = row.pts.length >= 2\n ? [...row.pts]\n : row.pts.length === 1\n ? ((): Point[] => {\n const p = row.pts[0];\n let dx = p.x - focal.x, dy = p.y - focal.y;\n const len = Math.hypot(dx, dy) || 1;\n dx /= len; dy /= len;\n // Across the line of sight, half a pitch each way.\n const h = Math.max(nbrs[i].pitch, 1e-3) * 0.5;\n return [{ x: p.x + dy * h, y: p.y - dx * h }, { x: p.x - dy * h, y: p.y + dx * h }];\n })()\n : [];\n if (rowPts.length < 2) return null;\n\n let seatSpan = 0, spanN = 0;\n for (let k = 1; k < rowPts.length; k++) {\n const d = Math.hypot(rowPts[k].x - rowPts[k - 1].x, rowPts[k].y - rowPts[k - 1].y);\n if (d > 1e-6) { seatSpan += d; spanN++; }\n }\n const spacing = spanN > 0 ? seatSpan / spanN : 0;\n const pts = extendEnds(rowPts, Math.max(spacing * END_REACH, MIN_REACH_U));\n return {\n pts,\n nrm: rowNormals(pts, focal),\n front: Math.max(nbrs[i].pitch * FRONT_REACH, MIN_REACH_U),\n back: Math.max(nbrs[i].pitch * BACK_REACH, MIN_REACH_U),\n };\n}\n\n/** A block: the footprint its ribbons cover, and the level its base sits at. */\nexport interface DeckFootprint {\n /** Outer ring, chart units. */\n outline: Point[];\n /** Any interior rings (an enclosed gap between rows). */\n holes: Point[][];\n /** World-metre height of this block's lowest ribbon — the top of its base. */\n topY: number;\n}\n\n/**\n * The footprint of each BLOCK in a section, as the union of its ribbons.\n *\n * This replaces extruding the section's whole authored outline under the deck.\n * That outline is drawn generously — it reaches past the last row and across the\n * gaps between blocks — so a single flat plate at the front row's level stuck out\n * around the seating as a large slab, and every block's end read as a square cut\n * through that slab rather than as the edge of a stand.\n *\n * Unioning the ribbons instead yields one polygon per block automatically (the\n * amphitheatre's three sections resolve to 4, 5 and 6 blocks — exactly their\n * wedge counts), each hugging its own seating, and each carrying its own base\n * level rather than sharing the section's. Costs 7–13 ms per section at build\n * time, which is paid once.\n */\nexport function deckFootprints(rows: readonly BandRow[], focal: Point): DeckFootprint[] {\n if (rows.length < 2) return [];\n const nbrs = neighbourhoods(rows);\n const rings: Array<[number, number][][]> = [];\n const ribbons: Array<Ribbon | null> = [];\n for (let i = 0; i < rows.length; i++) {\n const r = ribbonOf(rows, i, nbrs, focal);\n ribbons.push(r);\n if (!r) continue;\n const f: [number, number][] = [];\n const b: [number, number][] = [];\n const rf = r.front + FOOTPRINT_MARGIN_U;\n const rb = r.back + FOOTPRINT_MARGIN_U;\n for (let k = 0; k < r.pts.length; k++) {\n const p = r.pts[k], n = r.nrm[k];\n f.push([p.x - n[0] * rf, p.y - n[1] * rf]);\n b.push([p.x + n[0] * rb, p.y + n[1] * rb]);\n }\n const ring = [...f, ...b.reverse()];\n if (ring.length < 3) continue;\n ring.push(ring[0]); // polygon-clipping wants closed rings\n rings.push([ring]);\n }\n if (!rings.length) return [];\n\n let merged: ReturnType<typeof polygonClipping.union>;\n try {\n merged = polygonClipping.union(rings[0], ...rings.slice(1));\n } catch {\n return []; // never let a degenerate row set break the whole scene\n }\n\n const out: DeckFootprint[] = [];\n for (const poly of merged) {\n if (!poly.length || poly[0].length < 4) continue;\n const toPts = (ring: readonly [number, number][]): Point[] => {\n const pts = ring.map(([x, y]) => ({ x, y }));\n // union() repeats the first point to close the ring; drop the duplicate.\n const first = pts[0], last = pts[pts.length - 1];\n if (pts.length > 1 && Math.abs(first.x - last.x) < 1e-9 && Math.abs(first.y - last.y) < 1e-9) pts.pop();\n return pts;\n };\n const outline = toPts(poly[0]);\n if (outline.length < 3) continue;\n // This block's base is its own lowest row, not the section's — otherwise a\n // block set high in the bowl would be drawn standing on the front block's floor.\n let topY = Infinity;\n for (let i = 0; i < rows.length; i++) {\n const r = ribbons[i];\n if (!r) continue;\n const mid = r.pts[Math.floor(r.pts.length / 2)];\n if (pointInRing(outline, mid.x, mid.y) && rows[i].y < topY) topY = rows[i].y;\n }\n if (!Number.isFinite(topY)) continue;\n out.push({\n outline: simplifyRing(outline, FOOTPRINT_TOLERANCE_U),\n holes: poly.slice(1).map(toPts).map((h) => simplifyRing(h, FOOTPRINT_TOLERANCE_U))\n .filter((h) => h.length >= 3),\n topY,\n });\n }\n return out;\n}\n\n/**\n * Douglas-Peucker simplification tolerance for a block outline, chart units\n * (~7 mm in world metres).\n *\n * Unioning ~100 overlapping ribbons leaves long runs of near-collinear vertices\n * along a block's boundary, and earcut fans those into needles — measured aspect\n * ratios of 18,893 and 4,576,013, the same defect that flat-shades as dark\n * hairlines. The block outline carries no detail at this scale (the SEATS are\n * carried by the ribbons above it, not by this base), so simplifying is free.\n */\nconst FOOTPRINT_TOLERANCE_U = 0.3;\n\n/**\n * Extra width given to a ribbon before it is unioned into a block footprint,\n * chart units (~3 cm world).\n *\n * Unioning ribbons at their exact width makes neighbouring ribbons meet\n * TANGENTIALLY, and a boolean union of tangential shapes emits zero-width slits\n * along the seam — metres long, well under a millimetre wide. Overlapping them\n * decisively removes the seam instead of leaving one to clean up afterwards. The\n * footprint is only the base beneath the ribbons, so a 3 cm margin is invisible.\n */\nconst FOOTPRINT_MARGIN_U = 1.5;\n\n/** Douglas-Peucker on an open point run. */\nfunction simplifyRun(pts: Point[], tol: number): Point[] {\n if (pts.length < 3) return pts;\n const a = pts[0], b = pts[pts.length - 1];\n const dx = b.x - a.x, dy = b.y - a.y;\n const len = Math.hypot(dx, dy);\n let worst = -1, worstI = -1;\n for (let i = 1; i < pts.length - 1; i++) {\n const p = pts[i];\n const d = len > 1e-12\n ? Math.abs((p.x - a.x) * dy - (p.y - a.y) * dx) / len\n : Math.hypot(p.x - a.x, p.y - a.y);\n if (d > worst) { worst = d; worstI = i; }\n }\n if (worst <= tol || worstI < 0) return [a, b];\n const left = simplifyRun(pts.slice(0, worstI + 1), tol);\n const right = simplifyRun(pts.slice(worstI), tol);\n return [...left.slice(0, -1), ...right];\n}\n\n/** Simplify a closed ring, keeping it closed. */\nfunction simplifyRing(ring: Point[], tol: number): Point[] {\n if (ring.length < 4) return ring;\n // Split at the two extreme points so the closing edge is simplified too.\n const out = simplifyRun([...ring, ring[0]], tol);\n out.pop();\n return out.length >= 3 ? out : ring;\n}\n\n/** Even-odd point-in-ring test. */\nfunction pointInRing(ring: readonly Point[], x: number, y: number): boolean {\n let inside = false;\n for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {\n const a = ring[i], b = ring[j];\n if ((a.y > y) !== (b.y > y) && x < ((b.x - a.x) * (y - a.y)) / (b.y - a.y) + a.x) inside = !inside;\n }\n return inside;\n}\n\n/**\n * Cheap containment test for the clip region, so the boolean is only run for the\n * quads that actually straddle a boundary.\n */\nclass ClipTest {\n private rings: Point[][];\n private minX = Infinity; private minY = Infinity;\n private maxX = -Infinity; private maxY = -Infinity;\n\n constructor(rings: Point[][]) {\n this.rings = rings;\n for (const r of rings) {\n for (const p of r) {\n if (p.x < this.minX) this.minX = p.x;\n if (p.y < this.minY) this.minY = p.y;\n if (p.x > this.maxX) this.maxX = p.x;\n if (p.y > this.maxY) this.maxY = p.y;\n }\n }\n }\n\n /** True when every point lies strictly inside ONE ring of the clip region. */\n containsAll(pts: readonly Point[]): boolean {\n for (const p of pts) {\n if (p.x < this.minX || p.x > this.maxX || p.y < this.minY || p.y > this.maxY) return false;\n }\n // A quad spanning two disjoint rings must go through the boolean, so the\n // test demands a SINGLE ring contain all of it.\n for (const ring of this.rings) {\n let all = true;\n for (const p of pts) {\n if (!pointInRing(ring, p.x, p.y)) { all = false; break; }\n }\n if (all) return true;\n }\n return false;\n }\n}\n\n/**\n * Emit a horizontal quad clipped to `clipRing`, triangulated.\n *\n * Treads are horizontal, so every piece shades with the same up normal whatever\n * shape the clip leaves behind — which is what makes clipping cheap here.\n */\nfunction emitClippedQuad(\n builder: MeshBuilder,\n clipRing: [number, number][][],\n clipTest: ClipTest,\n quad: Point[],\n y: number,\n color: RGB,\n): void {\n // FAST PATH: a quad wholly inside the clip contributes nothing to clip, and\n // the overwhelming majority are — only ribbons at a section's edge straddle it.\n // Running a polygon boolean per ribbon SEGMENT regardless cost ~100,000\n // intersections on a 99k-seat venue and dominated scene build (8.5 s of 11 s).\n if (clipTest.containsAll(quad)) {\n const UPF = [0, 1, 0] as const;\n const a = quad[0], b = quad[1], c = quad[2], d = quad[3];\n builder.tri([a.x * M, y, a.y * M], [b.x * M, y, b.y * M], [c.x * M, y, c.y * M], UPF, color);\n builder.tri([a.x * M, y, a.y * M], [c.x * M, y, c.y * M], [d.x * M, y, d.y * M], UPF, color);\n return;\n }\n const ring: [number, number][] = quad.map((p) => [p.x, p.y]);\n ring.push(ring[0]);\n let pieces: ReturnType<typeof polygonClipping.intersection>;\n try {\n pieces = polygonClipping.intersection([ring], clipRing);\n } catch {\n return; // a degenerate quad simply contributes nothing\n }\n const UP = [0, 1, 0] as const;\n for (const poly of pieces) {\n if (!poly.length || poly[0].length < 4) continue;\n const outer = poly[0];\n const flat: number[] = [];\n const pts: Array<[number, number]> = [];\n for (let i = 0; i < outer.length - 1; i++) { // drop the repeated closing point\n flat.push(outer[i][0], outer[i][1]);\n pts.push([outer[i][0], outer[i][1]]);\n }\n if (pts.length < 3) continue;\n const tris = earcut(flat, undefined, 2);\n for (let i = 0; i < tris.length; i += 3) {\n const a = pts[tris[i]], b = pts[tris[i + 1]], c = pts[tris[i + 2]];\n builder.tri(\n [a[0] * M, y, a[1] * M],\n [b[0] * M, y, b[1] * M],\n [c[0] * M, y, c[1] * M],\n UP, color,\n );\n }\n }\n}\n\n/**\n * Emit one section's ribbons and risers.\n *\n * `rows` must be ordered front first (nearest the focal), which is the contract\n * `SectionRake.rows` provides.\n */\nexport function emitDeckBands(\n builder: MeshBuilder,\n rows: readonly BandRow[],\n focal: Point,\n landingY: number,\n colors: BandColors,\n clip?: readonly Point[],\n): void {\n if (rows.length < 2) return;\n const nbrs = neighbourhoods(rows);\n const UP = [0, 1, 0] as const;\n const clipRing: [number, number][][] | null = clip && clip.length >= 3\n ? [[...clip.map((p) => [p.x, p.y] as [number, number]), [clip[0].x, clip[0].y]]]\n : null;\n const clipTest = clip && clip.length >= 3 ? new ClipTest([[...clip]]) : null;\n\n for (let i = 0; i < rows.length; i++) {\n const row = rows[i];\n const rib = ribbonOf(rows, i, nbrs, focal);\n if (!rib) continue;\n const { pts, nrm, front, back } = rib;\n\n // The level below this ribbon: the spatially nearest row in front of it, or\n // the landing when nothing is in front (this row is its block's first).\n const belowY = nbrs[i].belowY ?? landingY;\n\n for (let k = 0; k + 1 < pts.length; k++) {\n const p = pts[k], q = pts[k + 1];\n const np = nrm[k], nq = nrm[k + 1];\n // Skip degenerate input: a duplicated seat position, or a vertex whose\n // normal collapsed. Emitting these produced needle triangles with aspect\n // ratios to 17,776 — meaningless geometry that flat-shades as dark streaks,\n // the exact defect the ring cleanup removed from the cap path.\n if (Math.hypot(q.x - p.x, q.y - p.y) < 1e-6) continue;\n if ((np[0] === 0 && np[1] === 0) || (nq[0] === 0 && nq[1] === 0)) continue;\n\n const pF: [number, number, number] = [(p.x - np[0] * front) * M, row.y, (p.y - np[1] * front) * M];\n const qF: [number, number, number] = [(q.x - nq[0] * front) * M, row.y, (q.y - nq[1] * front) * M];\n const pB: [number, number, number] = [(p.x + np[0] * back) * M, row.y, (p.y + np[1] * back) * M];\n const qB: [number, number, number] = [(q.x + nq[0] * back) * M, row.y, (q.y + nq[1] * back) * M];\n\n // Tread: horizontal, so one shared up normal is exact.\n if (clipRing && clipTest) {\n // A ribbon reaches past its own row on all four sides, and near a section\n // boundary that reach crosses into the NEXT section. Where the neighbour\n // sits lower, the overhang covers its seats: 27 concert-hall seats were\n // buried by 0.20 m under the terrace beside them, which is 0.60 m higher.\n // Clipping to the section's own padded outline keeps a deck inside the\n // section that drew it.\n emitClippedQuad(builder, clipRing, clipTest!, [\n { x: p.x - np[0] * front, y: p.y - np[1] * front },\n { x: q.x - nq[0] * front, y: q.y - nq[1] * front },\n { x: q.x + nq[0] * back, y: q.y + nq[1] * back },\n { x: p.x + np[0] * back, y: p.y + np[1] * back },\n ], row.y, colors.tread);\n } else {\n builder.tri(pF, qF, qB, UP, colors.tread);\n builder.tri(pF, qB, pB, UP, colors.tread);\n }\n\n // Riser: the vertical face under the ribbon's front edge. Skipped when it\n // would be inverted or degenerate (a level or descending step).\n if (row.y > belowY + MIN_RISER_M) {\n const pFd: [number, number, number] = [pF[0], belowY, pF[2]];\n const qFd: [number, number, number] = [qF[0], belowY, qF[2]];\n // Faces the focal — the direction the audience looks from.\n const rn: readonly [number, number, number] = [-np[0], 0, -np[1]];\n builder.tri(pF, qF, qFd, rn, colors.riser);\n builder.tri(pF, qFd, pFd, rn, colors.riser);\n }\n }\n }\n}\n","/**\n * The DOM half of venue labels: one absolutely-positioned element per visible\n * label, repositioned from projected 3D anchors as the camera moves.\n *\n * Kept out of `index.ts` so the render loop stays about rendering, and out of\n * `labels.ts` so the anchor/LOD logic stays pure and testable.\n *\n * ## Accessibility\n *\n * These are real DOM text nodes, which is the point. A screen reader can read the\n * venue's structure — zones, sections, booth names — where a GPU-drawn glyph is\n * invisible to it. The overlay itself is `aria-hidden` only for the decorative\n * pointer-events layer; the labels are a live region-free list of static text,\n * announced in document order.\n *\n * Pointer events pass straight through: a label must never eat a seat tap.\n */\n\nimport { cullOverlapping, projectToScreen, visibleLabelKinds, type SceneLabel } from './labels';\n\n/**\n * Minimum gap before the farther label is dropped, CSS px — wide and short,\n * matching the shape of a line of text rather than a disc around it.\n */\nconst SEPARATION_X_PX = 88;\nconst SEPARATION_Y_PX = 20;\n\n/** Per-kind styling. Sizes are CSS px at a nominal viewport. */\nconst KIND_STYLE: Record<SceneLabel['kind'], { size: number; weight: string; opacity: number }> = {\n zone: { size: 15, weight: '600', opacity: 0.95 },\n section: { size: 12, weight: '500', opacity: 0.88 },\n booth: { size: 11, weight: '500', opacity: 0.85 },\n annotation: { size: 11, weight: '400', opacity: 0.75 },\n};\n\nexport interface LabelOverlayOptions {\n /** `ChartTheme.fontFamily`, when the chart authors one. */\n fontFamily?: string;\n /** Ink colour for labels that carry no authored colour. */\n ink?: string;\n}\n\nexport class LabelOverlay {\n private root: HTMLDivElement;\n private nodes = new Map<string, HTMLDivElement>();\n private labels: SceneLabel[] = [];\n private opts: LabelOverlayOptions;\n\n constructor(container: HTMLElement, opts: LabelOverlayOptions = {}) {\n this.opts = opts;\n this.root = document.createElement('div');\n this.root.setAttribute('data-view3d-labels', '');\n const s = this.root.style;\n s.position = 'absolute';\n s.inset = '0';\n // Never intercept a seat tap — the canvas below owns all pointer input.\n s.pointerEvents = 'none';\n s.overflow = 'hidden';\n if (opts.fontFamily) s.fontFamily = opts.fontFamily;\n container.appendChild(this.root);\n }\n\n setLabels(labels: SceneLabel[]): void {\n this.labels = labels;\n for (const [id, node] of this.nodes) {\n if (!labels.some((l) => l.id === id)) { node.remove(); this.nodes.delete(id); }\n }\n }\n\n /**\n * Reposition every label for the current camera.\n *\n * `viewProjection` is column-major, as OGL supplies it.\n */\n update(\n viewProjection: ArrayLike<number>,\n width: number,\n height: number,\n cameraDistance: number,\n venueRadius: number,\n ): void {\n if (!this.labels.length) return;\n const kinds = visibleLabelKinds(cameraDistance, venueRadius);\n\n const candidates: Array<{ label: SceneLabel; screen: ReturnType<typeof projectToScreen> }> = [];\n for (const label of this.labels) {\n if (!kinds.has(label.kind)) continue;\n const screen = projectToScreen(viewProjection, label.anchor, width, height);\n if (!screen.visible) continue;\n candidates.push({ label, screen });\n }\n\n // Nearest-wins declutter, then paint. Everything not kept is hidden rather\n // than removed, so a small camera move does not thrash the DOM.\n const kept = cullOverlapping(candidates, SEPARATION_X_PX, SEPARATION_Y_PX);\n const keptIds = new Set(kept.map((k) => k.label.id));\n\n for (const { label, screen } of kept) {\n const node = this.nodeFor(label);\n const st = node.style;\n st.display = '';\n st.transform = `translate(-50%, -50%) translate(${screen.x.toFixed(1)}px, ${screen.y.toFixed(1)}px)`;\n }\n for (const [id, node] of this.nodes) {\n if (!keptIds.has(id)) node.style.display = 'none';\n }\n }\n\n private nodeFor(label: SceneLabel): HTMLDivElement {\n let node = this.nodes.get(label.id);\n if (node) return node;\n node = document.createElement('div');\n node.textContent = label.text;\n node.setAttribute('data-label-kind', label.kind);\n const style = KIND_STYLE[label.kind];\n const s = node.style;\n s.position = 'absolute';\n s.left = '0';\n s.top = '0';\n s.whiteSpace = 'nowrap';\n s.fontSize = `${style.size}px`;\n s.fontWeight = style.weight;\n s.opacity = String(style.opacity);\n s.color = label.color ?? this.opts.ink ?? '#e8edf5';\n // A soft dark halo keeps a label legible over both a pale deck and the dark\n // background, without a plate that would clutter a dense venue.\n s.textShadow = '0 1px 3px rgba(0,0,0,0.85), 0 0 8px rgba(0,0,0,0.55)';\n s.letterSpacing = label.kind === 'zone' ? '0.08em' : '0.02em';\n if (label.kind === 'zone') s.textTransform = 'uppercase';\n s.display = 'none';\n this.root.appendChild(node);\n this.nodes.set(label.id, node);\n return node;\n }\n\n dispose(): void {\n this.root.remove();\n this.nodes.clear();\n }\n}\n","/**\n * Builds (and rebuilds) all GPU resources from a SceneModel. Kept separate from\n * the model so a context-loss restore can throw the old GpuScene away and call\n * `buildGpuScene(gl, model)` again — the model never changes.\n *\n * Draw calls: background (1) + merged solids (1) + instanced seats (1) = 3.\n */\n\nimport { Geometry, Mesh, Program, Transform, type OGLRenderingContext } from 'ogl';\nimport { SEAT_STATES, type RGB } from '../palette';\nimport { createBackgroundProgram, createSeatProgram, createSolidProgram } from './materials';\nimport type { SceneModel } from './sceneModel';\nimport type { DirtyRun } from './seatInstances';\n\n/**\n * Fill an iColor buffer range from the current iState values (state → colour).\n *\n * Reads the THEME's colours, not the module palette, so an organizer's brand\n * selection colour applies to the 3D seats as it already does in the picker.\n */\nfunction writeSeatColors(iColor: Float32Array, iState: Float32Array, start: number, count: number, states: readonly RGB[]): void {\n for (let i = start; i < start + count; i++) {\n const c = states[iState[i]] ?? states[0];\n iColor[i * 3] = c[0];\n iColor[i * 3 + 1] = c[1];\n iColor[i * 3 + 2] = c[2];\n }\n}\n\n// Two-triangle quad in [-1,1] (billboard base).\nconst SEAT_QUAD = new Float32Array([-1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1]);\n// Fullscreen triangle.\nconst BG_TRI = new Float32Array([-1, -1, 3, -1, -1, 3]);\n\nexport interface GpuScene {\n /** Main scene (solids + seats), drawn with the camera. */\n main: Transform;\n /** Background scene, drawn first without depth. */\n background: Transform;\n seatProgram: Program;\n solidProgram: Program;\n /** Shared instanced seat geometry (reused by the pick pass — no buffer copy). */\n seatGeometry: Geometry;\n /** Merged solid geometry (reused as the pick occluder). */\n solidGeometry: Geometry;\n drawCalls: number;\n /** Upload only the changed instance-state ranges (never the whole buffer). */\n uploadSeatStateRuns(runs: DirtyRun[]): void;\n dispose(): void;\n}\n\nexport function buildGpuScene(gl: OGLRenderingContext, model: SceneModel): GpuScene {\n const main = new Transform();\n const background = new Transform();\n\n // --- Background ---\n const bgGeo = new Geometry(gl, { position: { size: 2, data: BG_TRI } });\n // The chart's own background, not the library's — see theme.ts.\n const bgProg = createBackgroundProgram(gl, model.theme.background.top as number[], model.theme.background.bottom as number[]);\n const bgMesh = new Mesh(gl, { geometry: bgGeo, program: bgProg });\n bgMesh.frustumCulled = false;\n bgMesh.setParent(background);\n\n // --- Solids (floor + tiers + stage + décor + GA, merged) ---\n const solidGeo = new Geometry(gl, {\n position: { size: 3, data: model.solids.position },\n normal: { size: 3, data: model.solids.normal },\n color: { size: 3, data: model.solids.color },\n floorIndex: { size: 1, data: model.solids.floor },\n });\n const solidProg = createSolidProgram(gl);\n const solidMesh = new Mesh(gl, { geometry: solidGeo, program: solidProg });\n solidMesh.frustumCulled = false;\n solidMesh.setParent(main);\n\n // --- Seats (one instanced billboard mesh) ---\n // Per-instance colour resolved CPU-side from iState (no dynamically-indexed\n // array uniform — OGL only binds an array uniform whose value is a plain\n // Array, and a dynamic LUT index is best avoided anyway).\n const seatProg = createSeatProgram(gl);\n const iColor = new Float32Array(model.seats.count * 3);\n const stateColors: RGB[] = SEAT_STATES.map((st) => model.theme.seatStates[st]);\n writeSeatColors(iColor, model.seats.iState, 0, model.seats.count, stateColors);\n const seatGeo = new Geometry(gl, {\n position: { size: 2, data: SEAT_QUAD },\n iOffset: { size: 3, data: model.seats.iPosition, instanced: 1 },\n iColor: { size: 3, data: iColor, instanced: 1 },\n // Per-seat world-radius ceiling: what stops distant rows merging into one\n // mass when the shader grows a dot to hold its minimum pixel size.\n iMaxRadius: { size: 1, data: model.seats.iMaxRadius, instanced: 1 },\n // Accommodation ring colour; (0,0,0) means the seat carries no access type.\n iRing: { size: 3, data: model.seats.iRing, instanced: 1 },\n iFloor: { size: 1, data: model.seats.iFloor, instanced: 1 },\n });\n const seatMesh = new Mesh(gl, { geometry: seatGeo, program: seatProg });\n seatMesh.frustumCulled = false;\n if (model.seats.count > 0) seatMesh.setParent(main);\n\n const colorAttr = seatGeo.attributes.iColor;\n\n return {\n main,\n background,\n seatProgram: seatProg,\n solidProgram: solidProg,\n seatGeometry: seatGeo,\n solidGeometry: solidGeo,\n drawCalls: 3,\n uploadSeatStateRuns(runs: DirtyRun[]): void {\n if (!runs.length) return;\n // Refresh only the changed instance colours from the (already-mutated)\n // iState, then upload just those contiguous ranges — never the whole buffer.\n for (const run of runs) writeSeatColors(iColor, model.seats.iState, run.start, run.length, stateColors);\n const buffer = colorAttr.buffer;\n if (!buffer) {\n // Not uploaded yet (no draw has happened) — full upload on next draw.\n colorAttr.needsUpdate = true;\n return;\n }\n // Direct bufferSubData: OGL's render-state boundBuffer cache is not touched\n // here, which is safe because OGL rebinds attribute buffers per draw via the\n // geometry's VAO; if a future dynamic attribute relies on the cache, rebind\n // through OGL instead. 3 floats per instance (vec3 iColor).\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n for (const run of runs) {\n const sub = iColor.subarray(run.start * 3, (run.start + run.length) * 3);\n gl.bufferSubData(gl.ARRAY_BUFFER, run.start * 3 * Float32Array.BYTES_PER_ELEMENT, sub);\n }\n },\n dispose(): void {\n // OGL geometries/programs delete their GL resources on remove().\n bgGeo.remove();\n bgProg.remove();\n solidGeo.remove();\n solidProg.remove();\n seatGeo.remove();\n seatProg.remove();\n },\n };\n}\n","/**\n * Inline GLSL (WebGL2 / GLSL ES 3.00) for the three scene programs. Zero\n * textures, zero shadow maps, zero post: a procedural matcap-style hemisphere +\n * warm key + fresnel rim on solids, a soft top-lit round dot for seats, and a\n * vertical-gradient + vignette background. OGL injects the built-in matrix\n * uniforms (modelViewMatrix / projectionMatrix / normalMatrix) by name.\n */\n\nimport { Program } from 'ogl';\nimport { SEAT_DOT_RADIUS_M } from './seatInstances';\nimport type { OGLRenderingContext } from 'ogl';\n\nconst SOLID_VERT = /* glsl */ `#version 300 es\nprecision highp float;\nin vec3 position;\nin vec3 normal;\nin vec3 color;\nin float floorIndex;\nuniform mat4 modelMatrix;\nuniform float uFocusFloor; // -1 = show every floor\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat3 normalMatrix;\nout vec3 vColor;\nout vec3 vNormalWorld;\nout vec3 vNormalView;\nout vec3 vPosView;\nout float vDim;\nvoid main() {\n // Per-floor isolation without splitting the merged mesh into a draw call per\n // floor: a floor that is not the focused one is dimmed, not hidden, so the\n // buyer keeps the whole venue as context while looking at one level.\n vDim = (uFocusFloor < -0.5 || abs(floorIndex - uFocusFloor) < 0.5) ? 0.0 : 1.0;\n vec4 mv = modelViewMatrix * vec4(position, 1.0);\n vPosView = mv.xyz;\n vNormalView = normalize(normalMatrix * normal);\n // World normal drives the key + hemisphere so the lighting stays welded to the\n // venue as the camera orbits (the scene has no non-uniform scale, so mat3 of\n // the model matrix is the correct normal transform).\n vNormalWorld = normalize(mat3(modelMatrix) * normal);\n vColor = color;\n gl_Position = projectionMatrix * mv;\n}`;\n\nconst SOLID_FRAG = /* glsl */ `#version 300 es\nprecision highp float;\nin vec3 vColor;\nin vec3 vNormalWorld;\nin vec3 vNormalView;\nin vec3 vPosView;\nin float vDim;\nuniform vec3 uKeyDir; // WORLD space, unit, points at the light\nout vec4 fragColor;\nvoid main() {\n vec3 N = normalize(vNormalWorld);\n vec3 V = normalize(-vPosView);\n float hemi = 0.5 + 0.5 * N.y; // sky/ground gradient about WORLD up\n float key = max(dot(N, uKeyDir), 0.0); // fixed key — does not orbit with you\n // Low opposite fill so faces turned away from the key keep their form instead\n // of crushing to a single flat value.\n vec3 fillDir = normalize(vec3(-uKeyDir.x, 0.25, -uKeyDir.z));\n float fill = max(dot(N, fillDir), 0.0);\n vec3 base = vColor * (0.52 + 0.34 * hemi) + vColor * key * 0.34 + vColor * fill * 0.10;\n float fres = pow(1.0 - max(dot(normalize(vNormalView), V), 0.0), 3.0);\n base += vec3(0.26, 0.31, 0.38) * fres * 0.35; // cool rim, restrained (view-dependent by design)\n // Unfocused floors fall back toward the background rather than vanishing.\n base = mix(base, base * 0.45, vDim);\n fragColor = vec4(base, 1.0);\n}`;\n\nconst SEAT_VERT = /* glsl */ `#version 300 es\nprecision highp float;\nin vec2 position; // quad corner in [-1,1]\nin vec3 iOffset; // per-instance world position\nin vec3 iColor; // per-instance state colour (resolved CPU-side)\nin float iMaxRadius; // per-instance world-radius ceiling (seat pitch derived)\nin vec3 iRing; // accommodation ring colour; (0,0,0) = not accessible\nin float iFloor; // owning floor index\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform float uSeatRadius;\nuniform float uSeatScale;\nuniform float uMinPixels;\nuniform float uPixelToWorld; // (2*tan(fovY/2)) / viewportHeightPx\nuniform float uFocusFloor; // -1 = show every floor\nout vec2 vUv;\nout vec3 vColor;\nout float vBudget; // 1 = dot holds its minimum pixel size, <1 = it cannot\nout vec3 vRing;\nout float vDim;\nvoid main() {\n vec4 mv = modelViewMatrix * vec4(iOffset, 1.0);\n float depth = max(-mv.z, 0.001);\n float minR = uMinPixels * depth * uPixelToWorld; // screen-space floor\n // Grow to hold the pixel floor, but never past this seat's own pitch ceiling:\n // unbounded growth is what merges neighbouring rows into one mass at range.\n float r = min(max(uSeatRadius * uSeatScale, minR), iMaxRadius);\n // How much of the requested pixel floor the dot could actually afford. Below 1\n // it is losing legibility to distance, and the fragment stage dissolves it\n // toward the tier top rather than letting a sub-pixel dot alias and shimmer.\n vBudget = minR > 0.0 ? clamp(r / minR, 0.0, 1.0) : 1.0;\n mv.xy += position * r; // camera-facing billboard\n // Seat the dot ON the deck instead of centring it in the deck. iOffset is the\n // exact surface point, so half the billboard would otherwise sit below the cap\n // and be clipped by it — and because uMinPixels grows r with distance, no\n // constant world-space lift can prevent that at every range. Offsetting by r\n // along the screen projection of WORLD up is self-correcting: it is full at a\n // grazing view (where slicing happens) and vanishes looking straight down\n // (where the dot must stay centred on its seat).\n mv.xy += normalize(vec3(modelViewMatrix * vec4(0.0, 1.0, 0.0, 0.0))).xy * r;\n vUv = position;\n vColor = iColor;\n vRing = iRing;\n vDim = (uFocusFloor < -0.5 || abs(iFloor - uFocusFloor) < 0.5) ? 0.0 : 1.0;\n gl_Position = projectionMatrix * mv;\n}`;\n\nconst SEAT_FRAG = /* glsl */ `#version 300 es\nprecision highp float;\nin vec2 vUv;\nin vec3 vColor;\nin float vBudget;\nin vec3 vRing;\nin float vDim;\nuniform float uSeatFade; // fade toward tier colour with distance (LOD)\nuniform vec3 uFadeColor;\nout vec4 fragColor;\nvoid main() {\n float d = length(vUv);\n if (d > 1.0) discard;\n float alpha = smoothstep(1.0, 0.72, d);\n float shade = 0.80 + 0.28 * (0.5 - vUv.y * 0.5); // subtle top-lit\n vec3 c = vColor * shade;\n c = mix(c, uFadeColor, uSeatFade);\n // Accommodation ring — the 3D echo of the coloured ring 2D draws around every\n // accessible seat. Painted INSIDE the dot's own radius rather than outside it,\n // so an accessible seat still respects the row-pitch ceiling and cannot grow\n // into its neighbour just for carrying a ring.\n float ringMask = step(0.001, dot(vRing, vRing));\n float ring = smoothstep(0.58, 0.70, d) * (1.0 - smoothstep(0.90, 1.0, d));\n c = mix(c, vRing, ring * ringMask * 0.95);\n // A dot that can no longer afford its pixel floor dissolves instead of\n // aliasing; the tier cap underneath already carries the section's category\n // tint, so the block reads as coloured seating rather than empty concrete.\n alpha *= smoothstep(0.35, 1.0, vBudget);\n // Seats on an unfocused floor recede with their structure.\n c = mix(c, uFadeColor, vDim * 0.75);\n alpha *= mix(1.0, 0.30, vDim);\n fragColor = vec4(c, alpha);\n}`;\n\nconst BG_VERT = /* glsl */ `#version 300 es\nprecision highp float;\nin vec2 position;\nout vec2 vUv;\nvoid main() {\n vUv = position * 0.5 + 0.5;\n gl_Position = vec4(position, 0.999, 1.0);\n}`;\n\nconst BG_FRAG = /* glsl */ `#version 300 es\nprecision highp float;\nin vec2 vUv;\nuniform vec3 uTop;\nuniform vec3 uBottom;\nout vec4 fragColor;\nvoid main() {\n vec3 col = mix(uBottom, uTop, vUv.y);\n vec2 c = vUv - 0.5;\n float vig = 1.0 - dot(c, c) * 0.85; // soft vignette\n fragColor = vec4(col * vig, 1.0);\n}`;\n\n// --- GPU pick pass ---------------------------------------------------------\n// Seats encode gl_InstanceID+1 as an RGB colour (no extra per-instance buffer);\n// solids write pure black + depth first so a seat occluded by a tier reads as\n// \"no hit\". Same billboard maths as the display seat program.\nconst SEAT_PICK_VERT = /* glsl */ `#version 300 es\nprecision highp float;\nin vec2 position;\nin vec3 iOffset;\nin float iMaxRadius;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform float uSeatRadius;\nuniform float uSeatScale;\nuniform float uMinPixels;\nuniform float uPixelToWorld;\nout vec2 vUv;\nflat out vec3 vPick;\nvoid main() {\n int id = gl_InstanceID + 1; // 0 reserved for no-hit\n vPick = vec3(float(id & 255), float((id >> 8) & 255), float((id >> 16) & 255)) / 255.0;\n vec4 mv = modelViewMatrix * vec4(iOffset, 1.0);\n float depth = max(-mv.z, 0.001);\n float minR = uMinPixels * depth * uPixelToWorld;\n float r = min(max(uSeatRadius * uSeatScale, minR), iMaxRadius);\n mv.xy += position * r;\n // Must match SEAT_VERT exactly, or the hit mask drifts off the drawn dot.\n mv.xy += normalize(vec3(modelViewMatrix * vec4(0.0, 1.0, 0.0, 0.0))).xy * r;\n vUv = position;\n gl_Position = projectionMatrix * mv;\n}`;\n\nconst SEAT_PICK_FRAG = /* glsl */ `#version 300 es\nprecision highp float;\nin vec2 vUv;\nflat in vec3 vPick;\nout vec4 fragColor;\nvoid main() {\n if (length(vUv) > 1.0) discard; // round hit-mask matches the dot\n fragColor = vec4(vPick, 1.0);\n}`;\n\nconst PICK_DEPTH_VERT = /* glsl */ `#version 300 es\nprecision highp float;\nin vec3 position;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nvoid main() {\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}`;\n\nconst PICK_DEPTH_FRAG = /* glsl */ `#version 300 es\nprecision highp float;\nout vec4 fragColor;\nvoid main() { fragColor = vec4(0.0, 0.0, 0.0, 1.0); }`;\n\nexport function createSeatPickProgram(gl: OGLRenderingContext): Program {\n return new Program(gl, {\n vertex: SEAT_PICK_VERT,\n fragment: SEAT_PICK_FRAG,\n transparent: false,\n depthTest: true,\n depthWrite: true,\n cullFace: false,\n uniforms: {\n uSeatRadius: { value: SEAT_DOT_RADIUS_M },\n uSeatScale: { value: 1 },\n uMinPixels: { value: 2.5 },\n uPixelToWorld: { value: 0.002 },\n },\n });\n}\n\n/** Occluder pass: solids to black + depth so occluded seats read as no-hit. */\nexport function createPickDepthProgram(gl: OGLRenderingContext): Program {\n return new Program(gl, {\n vertex: PICK_DEPTH_VERT,\n fragment: PICK_DEPTH_FRAG,\n transparent: false,\n depthTest: true,\n depthWrite: true,\n cullFace: false,\n });\n}\n\nexport function createSolidProgram(gl: OGLRenderingContext): Program {\n return new Program(gl, {\n // No backface culling: free-hand section polygons are stored in raw click\n // order (either winding), so a culled solid would render see-through. The\n // shader lights both faces and closed opaque prisms + depth test keep\n // overdraw negligible; extrudePrism also normalises winding as a belt.\n vertex: SOLID_VERT,\n fragment: SOLID_FRAG,\n cullFace: false,\n depthTest: true,\n depthWrite: true,\n uniforms: {\n // High and off-axis, in world space: reads as a house rig rather than a\n // headlamp welded to the camera.\n uKeyDir: { value: new Float32Array([0.38, 0.86, 0.34]) },\n uFocusFloor: { value: -1 },\n },\n });\n}\n\nexport function createSeatProgram(gl: OGLRenderingContext): Program {\n return new Program(gl, {\n vertex: SEAT_VERT,\n fragment: SEAT_FRAG,\n transparent: true,\n depthTest: true,\n depthWrite: false,\n cullFace: false,\n uniforms: {\n uSeatRadius: { value: SEAT_DOT_RADIUS_M },\n uSeatScale: { value: 1 },\n uMinPixels: { value: 2.5 },\n uPixelToWorld: { value: 0.002 },\n uSeatFade: { value: 0 },\n uFocusFloor: { value: -1 },\n uFadeColor: { value: new Float32Array([0.32, 0.37, 0.43]) },\n },\n });\n}\n\nexport function createBackgroundProgram(gl: OGLRenderingContext, top: number[], bottom: number[]): Program {\n return new Program(gl, {\n vertex: BG_VERT,\n fragment: BG_FRAG,\n depthTest: false,\n depthWrite: false,\n cullFace: false,\n uniforms: {\n uTop: { value: new Float32Array(top) },\n uBottom: { value: new Float32Array(bottom) },\n },\n });\n}\n","/**\n * GPU color-pick (Slice 2). On a TAP (not hover, not drag) the seat instance\n * index is rendered as RGB into a small scissored offscreen target and a single\n * pixel is read back → O(1) regardless of seat count. Solids are drawn first as\n * black + depth so a seat occluded by a tier reads as \"no hit\".\n *\n * The pick meshes reuse the display geometry buffers (iOffset / position), so no\n * per-seat data is duplicated on the GPU.\n */\n\nimport { Geometry, Mesh, Program, RenderTarget, Transform } from 'ogl';\nimport type { Camera, OGLRenderingContext, Renderer } from 'ogl';\nimport { createPickDepthProgram, createSeatPickProgram } from '../scene/materials';\n\nimport { pickNearestFromBuffer } from './encode';\n\nconst SYNC_KEYS = ['uSeatRadius', 'uSeatScale', 'uMinPixels', 'uPixelToWorld'] as const;\n\nexport class PickPipeline {\n private gl: OGLRenderingContext;\n private renderer: Renderer;\n private seatProg: Program;\n private depthProg: Program;\n private seatScene = new Transform();\n private solidScene = new Transform();\n private target: RenderTarget | null = null;\n private maxIndex: number;\n\n /** Display clear colour to restore after the pick pass (theme-dependent). */\n private restoreClear: readonly number[] = [0, 0, 0];\n\n /** Follow the theme's background when the scene is (re)built. */\n setRestoreClear(rgb: readonly number[]): void {\n this.restoreClear = [rgb[0], rgb[1], rgb[2]];\n }\n\n constructor(renderer: Renderer, seatGeo: Geometry, solidGeo: Geometry, seatCount: number) {\n this.renderer = renderer;\n this.gl = renderer.gl;\n this.maxIndex = seatCount;\n this.seatProg = createSeatPickProgram(this.gl);\n this.depthProg = createPickDepthProgram(this.gl);\n const seatMesh = new Mesh(this.gl, { geometry: seatGeo, program: this.seatProg });\n seatMesh.frustumCulled = false;\n seatMesh.setParent(this.seatScene);\n const solidMesh = new Mesh(this.gl, { geometry: solidGeo, program: this.depthProg });\n solidMesh.frustumCulled = false;\n solidMesh.setParent(this.solidScene);\n }\n\n /** Match the display seat sizing so the pick mask lines up with the dots. */\n syncFromSeatProgram(seatProgram: Program): void {\n for (const k of SYNC_KEYS) this.seatProg.uniforms[k].value = seatProgram.uniforms[k].value;\n }\n\n private ensureTarget(): RenderTarget {\n const w = this.gl.drawingBufferWidth;\n const h = this.gl.drawingBufferHeight;\n if (this.target && (this.target.width !== w || this.target.height !== h)) {\n this.destroyTarget();\n }\n if (!this.target) {\n this.target = new RenderTarget(this.gl, { width: w, height: h, depth: true });\n }\n return this.target;\n }\n\n private destroyTarget(): void {\n if (!this.target) return;\n const gl = this.gl;\n if (this.target.buffer) gl.deleteFramebuffer(this.target.buffer);\n for (const t of this.target.textures ?? []) if (t.texture) gl.deleteTexture(t.texture);\n if (this.target.depthBuffer) gl.deleteRenderbuffer(this.target.depthBuffer);\n this.target = null;\n }\n\n /**\n * Read back the seat instance index NEAREST framebuffer pixel (px, py), or -1.\n * `radius` is the tap tolerance in buffer px: a box of side (2·radius+1) is\n * rendered + read so a tap that lands between the ~2px overview dots still\n * finds the closest seat. px/py/radius are bottom-left-origin buffer pixels.\n */\n pick(camera: Camera, px: number, py: number, radius: number): number {\n const gl = this.gl;\n const target = this.ensureTarget();\n const bw = gl.drawingBufferWidth;\n const bh = gl.drawingBufferHeight;\n const x0 = Math.max(0, px - radius);\n const y0 = Math.max(0, py - radius);\n const boxW = Math.max(1, Math.min(bw, px + radius + 1) - x0);\n const boxH = Math.max(1, Math.min(bh, py + radius + 1) - y0);\n\n gl.enable(gl.SCISSOR_TEST);\n gl.scissor(x0, y0, boxW, boxH);\n // Clear the pick target to TRUE BLACK so empty + occluded pixels decode to\n // no-hit structurally (not by a range guard). Occluders drawn black + depth\n // first, then seats (pick colours) depth-tested.\n // Restore whatever the display clear colour currently is — the theme may have\n // replaced it, and restoring the library default would flash grey on a\n // white-labelled chart every time the pointer moves.\n const [br, bg, bb] = this.restoreClear;\n gl.clearColor(0, 0, 0, 1);\n this.renderer.render({ scene: this.solidScene, camera, target, clear: true });\n this.renderer.render({ scene: this.seatScene, camera, target, clear: false });\n gl.clearColor(br, bg, bb, 1); // restore the display clear colour\n gl.disable(gl.SCISSOR_TEST);\n\n const buf = new Uint8Array(boxW * boxH * 4);\n this.renderer.bindFramebuffer(target);\n gl.readPixels(x0, y0, boxW, boxH, gl.RGBA, gl.UNSIGNED_BYTE, buf);\n this.renderer.bindFramebuffer();\n\n return pickNearestFromBuffer(buf, boxW, boxH, px - x0, py - y0, this.maxIndex);\n }\n\n dispose(): void {\n this.destroyTarget();\n this.seatProg.remove();\n this.depthProg.remove();\n }\n}\n","/**\n * GPU color-pick id encoding — pure, DOM-free, so the round-trip and the tap →\n * framebuffer pixel maths are unit-testable. The seat instance index is offset\n * by +1 so id 0 is reserved for \"no hit\" (the cleared black background).\n */\n\n/** instanceIndex → normalised RGB (0..1) the pick shader writes. */\nexport function encodePickId(instanceIndex: number): [number, number, number] {\n const id = instanceIndex + 1;\n return [(id & 255) / 255, ((id >> 8) & 255) / 255, ((id >> 16) & 255) / 255];\n}\n\n/** RGB bytes (0..255) read back → instanceIndex, or -1 for the no-hit clear. */\nexport function decodePickRGB(r: number, g: number, b: number): number {\n const id = r + (g << 8) + (b << 16);\n return id === 0 ? -1 : id - 1;\n}\n\n/**\n * Scan a readback window (RGBA, bottom-left origin, row-major) for the seat hit\n * NEAREST the tap centre. A single tap on a low-res overview lands between ~2px\n * dots, so we read a small box and pick the closest non-empty seat instead of a\n * single pixel. `centerI/centerJ` are the tap's box-local pixel coords.\n * `maxIndex` bounds valid indices (defence against a stray decode).\n */\nexport function pickNearestFromBuffer(\n pixels: Uint8Array,\n boxW: number,\n boxH: number,\n centerI: number,\n centerJ: number,\n maxIndex: number,\n): number {\n let best = -1;\n let bestDist = Infinity;\n for (let j = 0; j < boxH; j++) {\n for (let i = 0; i < boxW; i++) {\n const o = (j * boxW + i) * 4;\n const idx = decodePickRGB(pixels[o], pixels[o + 1], pixels[o + 2]);\n if (idx < 0 || idx >= maxIndex) continue;\n const di = i - centerI;\n const dj = j - centerJ;\n const d = di * di + dj * dj;\n if (d < bestDist) { bestDist = d; best = idx; }\n }\n }\n return best;\n}\n\n/**\n * Map a tap in CSS pixels (relative to the canvas bounding rect) to a\n * bottom-left-origin framebuffer pixel, clamped in range. `rect` is the canvas\n * getBoundingClientRect; `dpr` the renderer device-pixel-ratio.\n */\nexport function pickPixelCoords(\n clientX: number,\n clientY: number,\n rect: { left: number; top: number; width: number; height: number },\n dpr: number,\n bufferWidth: number,\n bufferHeight: number,\n): { x: number; y: number } {\n const cssX = clientX - rect.left;\n const cssY = clientY - rect.top;\n const x = Math.round(cssX * dpr);\n // WebGL framebuffer origin is bottom-left → flip Y.\n const y = Math.round((rect.height - cssY) * dpr);\n return {\n x: Math.max(0, Math.min(bufferWidth - 1, x)),\n y: Math.max(0, Math.min(bufferHeight - 1, y)),\n };\n}\n","/**\n * Pure selection-state diffing. Selection is a colour layer over availability:\n * a selected seat shows the 'selected' colour and, on deselect, restores the\n * base availability state it had when it was selected (remembered in `prev`).\n * Kept DOM/GPU-free so the transitions are unit-testable.\n */\n\nimport { SEAT_STATES, seatStateIndex, type SeatState3D } from '../palette';\n\nexport interface SelectionUpdate {\n seatId: string;\n state: SeatState3D;\n}\n\n/**\n * Reconcile an availability update against the current selection. A selected\n * seat that changes availability must STAY 'selected' on screen while its\n * remembered base state is updated (so a later deselect restores the CURRENT\n * availability, not the pre-change one). Mutates `selection` in place and\n * returns the updates that should actually be written to iState (the\n * non-selected ones — selected seats keep their 'selected' colour).\n */\nexport function mergeAvailabilityIntoSelection(\n selection: Map<string, number>,\n updates: SelectionUpdate[],\n): SelectionUpdate[] {\n const passthrough: SelectionUpdate[] = [];\n for (const u of updates) {\n if (selection.has(u.seatId)) selection.set(u.seatId, seatStateIndex(u.state));\n else passthrough.push(u);\n }\n return passthrough;\n}\n\nexport interface SelectionDiff {\n updates: SelectionUpdate[];\n /** New seatId → remembered base-state index map. */\n next: Map<string, number>;\n}\n\n/**\n * Diff the current selection (`prev`: seatId → base-state index) against the\n * desired seat ids. `baseStateIndex` reads the seat's CURRENT state index (used\n * only for newly-selected seats, which are not yet recoloured).\n */\nexport function diffSelection(\n prev: Map<string, number>,\n desiredIds: string[],\n baseStateIndex: (seatId: string) => number | undefined,\n): SelectionDiff {\n const desired = new Set<string>();\n for (const id of desiredIds) {\n if (prev.has(id) || baseStateIndex(id) !== undefined) desired.add(id);\n }\n const next = new Map(prev);\n const updates: SelectionUpdate[] = [];\n\n // Deselect: restore base availability for ids leaving the selection.\n for (const [id, base] of prev) {\n if (!desired.has(id)) {\n updates.push({ seatId: id, state: SEAT_STATES[base] ?? 'available' });\n next.delete(id);\n }\n }\n // Select: remember base state, then recolour as selected.\n for (const id of desired) {\n if (next.has(id)) continue; // already selected → unchanged\n const base = baseStateIndex(id);\n if (base === undefined) continue;\n next.set(id, base);\n updates.push({ seatId: id, state: 'selected' });\n }\n return { updates, next };\n}\n","/**\n * Slice 3 — the purchase-moment fly-to-seat cinematic controller. One continuous\n * shot from the venue overview into the picked seat: a catmull-rom position\n * spline (smootherstep timing) + a look-at quaternion slerp whose orientation\n * slightly LEADS the position, with a gentle FOV push-in. The pure maths lives\n * in cinematicMath.ts (tested); this drives the OGL camera with it.\n *\n * Technique locked in docs/3d-usp-strategy-2026-07-23.md §3.\n */\n\nimport { Quat, Vec3 } from 'ogl';\nimport type { Camera } from 'ogl';\nimport {\n FLIGHT_DURATION_MS, ORIENTATION_LEAD,\n sampleFlight, orientationLeadT, type Vec3Arr,\n} from './cinematicMath';\n\nexport {\n FLIGHT_DURATION_MS, FOV_START, FOV_END, ORIENTATION_LEAD,\n smootherstep, orientationLeadT, catmullRom, buildWaypoints, sampleFlight,\n type Vec3Arr, type FlightSample,\n} from './cinematicMath';\n\n/** Look-at quaternion from `from` toward `to`, computed via the OGL camera\n * (save/restore) — no manual quat maths. Synchronous, no render between. */\nexport function lookAtQuat(camera: Camera, from: Vec3Arr, to: Vec3Arr): Quat {\n const savedPos = camera.position.clone();\n const savedQuat = new Quat().copy(camera.quaternion);\n camera.position.set(from[0], from[1], from[2]);\n camera.lookAt(new Vec3(to[0], to[1], to[2]));\n const q = new Quat().copy(camera.quaternion);\n camera.position.copy(savedPos);\n camera.quaternion.copy(savedQuat);\n return q;\n}\n\n/** Drives the OGL camera along a flight. Integrated with the render loop: the\n * loop calls update() each frame while active. */\nexport class Cinematic {\n active = false;\n private camera: Camera;\n private waypoints: Vec3Arr[] = [];\n private startQuat = new Quat();\n private endQuat = new Quat();\n private outQuat = new Quat();\n private startTime = 0;\n private duration = FLIGHT_DURATION_MS;\n private resolveFn: (() => void) | null = null;\n\n constructor(camera: Camera) {\n this.camera = camera;\n }\n\n /** Begin (or retarget) a flight. Resolves when it lands or is cancelled. */\n start(waypoints: Vec3Arr[], startQuat: Quat, endQuat: Quat, duration = FLIGHT_DURATION_MS): Promise<void> {\n this.settle(); // resolve any in-flight promise before retargeting\n this.waypoints = waypoints;\n this.startQuat.copy(startQuat);\n this.endQuat.copy(endQuat);\n this.duration = duration;\n this.startTime = performance.now();\n this.active = true;\n return new Promise<void>((res) => { this.resolveFn = res; });\n }\n\n /** Advance the flight, mutating the camera. Returns true while still flying. */\n update(now: number): boolean {\n if (!this.active) return false;\n const u = Math.min(1, (now - this.startTime) / this.duration);\n const { pos, fov, eased } = sampleFlight(this.waypoints, u);\n this.camera.position.set(pos[0], pos[1], pos[2]);\n this.outQuat.copy(this.startQuat).slerp(this.endQuat, orientationLeadT(eased, ORIENTATION_LEAD));\n this.camera.quaternion.copy(this.outQuat);\n this.camera.fov = fov;\n this.camera.updateProjectionMatrix();\n if (u >= 1) { this.settle(); return false; }\n return true;\n }\n\n /** Stop where we are (no snap) — the camera keeps its current pose. */\n cancel(): void {\n this.settle();\n }\n\n private settle(): void {\n this.active = false;\n const r = this.resolveFn;\n this.resolveFn = null;\n if (r) r();\n }\n}\n","/**\n * Pure, DOM/GPU-free maths for the fly-to-seat cinematic — spline, timing, and\n * waypoint construction. Split from cinematic.ts (which imports OGL) so this is\n * unit-testable in a plain runtime.\n */\n\nexport const FLIGHT_DURATION_MS = 2500;\nexport const FOV_START = 35;\nexport const FOV_END = 28;\n/** Orientation t leads position t by this much (clamped) — aim before arrival. */\nexport const ORIENTATION_LEAD = 0.15;\n/** Final approach: this far behind the seat (≈2–3 rows) and above its eye. */\nconst BACK_M = 2.5;\nconst ABOVE_EYE_M = 1.5;\n\nexport type Vec3Arr = [number, number, number];\n\nfunction sub(a: Vec3Arr, b: Vec3Arr): Vec3Arr { return [a[0] - b[0], a[1] - b[1], a[2] - b[2]]; }\nfunction add(a: Vec3Arr, b: Vec3Arr): Vec3Arr { return [a[0] + b[0], a[1] + b[1], a[2] + b[2]]; }\nfunction scale(a: Vec3Arr, k: number): Vec3Arr { return [a[0] * k, a[1] * k, a[2] * k]; }\nfunction norm(a: Vec3Arr): Vec3Arr {\n const l = Math.hypot(a[0], a[1], a[2]);\n return l > 1e-6 ? [a[0] / l, a[1] / l, a[2] / l] : [0, 0, 0];\n}\n\n/** Smootherstep (Ken Perlin) — zero 1st & 2nd derivatives at the ends. */\nexport function smootherstep(t: number): number {\n const x = Math.max(0, Math.min(1, t));\n return x * x * x * (x * (x * 6 - 15) + 10);\n}\n\n/** Orientation t = position t nudged ahead by `lead`, clamped to [0,1]. */\nexport function orientationLeadT(posT: number, lead: number): number {\n return Math.max(0, Math.min(1, posT + lead));\n}\n\n/**\n * Uniform multi-segment Catmull-Rom passing THROUGH every waypoint. `u` in\n * [0,1] spans the whole path; endpoints are duplicated for tangents.\n */\nexport function catmullRom(points: Vec3Arr[], u: number): Vec3Arr {\n const n = points.length;\n if (n === 0) return [0, 0, 0];\n if (n === 1) return [...points[0]];\n const cu = Math.max(0, Math.min(1, u));\n const segCount = n - 1;\n let seg = Math.floor(cu * segCount);\n if (seg >= segCount) seg = segCount - 1;\n const t = cu * segCount - seg;\n const p0 = points[Math.max(0, seg - 1)];\n const p1 = points[seg];\n const p2 = points[seg + 1];\n const p3 = points[Math.min(n - 1, seg + 2)];\n const t2 = t * t;\n const t3 = t2 * t;\n const out: Vec3Arr = [0, 0, 0];\n for (let i = 0; i < 3; i++) {\n out[i] = 0.5 * (\n 2 * p1[i]\n + (-p0[i] + p2[i]) * t\n + (2 * p0[i] - 5 * p1[i] + 4 * p2[i] - p3[i]) * t2\n + (-p0[i] + 3 * p1[i] - 3 * p2[i] + p3[i]) * t3\n );\n }\n return out;\n}\n\n/**\n * Build the flight waypoints from the current camera position to a seat. The\n * mid arc is pushed OUTSIDE the venue bounds sphere and high up, so the swoop\n * never clips through the tier solids; the final anchor sits behind + above the\n * seat, looking toward the focal point.\n */\nexport function buildWaypoints(\n start: Vec3Arr,\n seatEye: Vec3Arr,\n focal: Vec3Arr,\n center: Vec3Arr,\n radius: number,\n): { waypoints: Vec3Arr[]; finalPos: Vec3Arr } {\n let away = norm(sub(seatEye, focal)); // \"behind\" the seat (away from stage)\n if (away[0] === 0 && away[1] === 0 && away[2] === 0) away = [0, 0, 1];\n const finalPos = add(add(seatEye, scale(away, BACK_M)), [0, ABOVE_EYE_M, 0]);\n\n const horiz: Vec3Arr = [seatEye[0] - center[0], 0, seatEye[2] - center[2]];\n let hn = norm(horiz);\n if (hn[0] === 0 && hn[2] === 0) hn = [away[0], 0, away[2]];\n const r = Math.max(1, radius);\n const arc: Vec3Arr = [\n center[0] + hn[0] * r * 1.3,\n center[1] + r * 0.75,\n center[2] + hn[2] * r * 1.3,\n ];\n return { waypoints: [start, arc, finalPos], finalPos };\n}\n\nexport interface FlightSample {\n pos: Vec3Arr;\n fov: number;\n /** Eased position parameter (drives the orientation lead). */\n eased: number;\n}\n\n/** Sample the flight at raw parameter `u` in [0,1]. */\nexport function sampleFlight(waypoints: Vec3Arr[], u: number, fovStart = FOV_START, fovEnd = FOV_END): FlightSample {\n const eased = smootherstep(u);\n const pos = catmullRom(waypoints, eased);\n const fovT = smootherstep(Math.max(0, Math.min(1, (u - 0.66) / 0.34))); // push-in over final third\n return { pos, fov: fovStart + (fovEnd - fovStart) * fovT, eased };\n}\n","/**\n * Slice 3 hand-off — the DOM panorama overlay the fly-to-seat cinematic\n * dissolves into. Decoupled by design: the CALLER supplies the equirectangular\n * image (via mountVenue3D's getSeatView), so view3d never imports the app's\n * panorama generator and the chunk stays lean.\n *\n * Technique (mirrors SeatPicker.openSeatView, reimplemented small): an equirect\n * image panned with `repeat-x`; the initial horizontal offset is set so the\n * panorama's bearing matches the final camera yaw — the dissolve reads as the\n * same view sharpening, not a cut. CSS opacity fade is compositor-only.\n */\n\nexport interface SeatView {\n url: string;\n /** Bearing (deg, 0 = facing the focal/stage) the panorama should open centred\n * on, to match the camera's final yaw. Default 0 (both face the stage). */\n initialBearingDeg?: number;\n}\n\nexport interface PanoramaHandle {\n /** Fade out and return to the (frozen) 3D view; calls opts.onClose after. */\n close(): void;\n /** Immediate teardown (dispose) — no fade, no onClose. */\n dispose(): void;\n}\n\n/**\n * Vertical field of view (deg) the windowed panorama shows. The source image is\n * a full 180° equirect sphere; showing it raw wastes ~⅔ of the frame on dead sky\n * and black floor, with the horizon content band squished into the middle. We\n * instead scale the image so only this central slice fills the viewport height,\n * horizon-centred, and let the user drag pitch within ±`MAX_PITCH_DEG`.\n */\nexport const VFOV_DEG = 70;\n/** Users may look this far up/down from the horizon; well inside the image so\n * the clamp never reveals past its top/bottom edge. */\nexport const MAX_PITCH_DEG = 35;\n\n/**\n * Horizontal background-position (px) that centres `bearingDeg` in the viewport,\n * assuming the equirect image's yaw 0 sits at its horizontal centre. `bgW` is the\n * full scaled image width representing 360° — so this is invariant to the vertical\n * FOV windowing (which scales width and height by the same factor). `repeat-x`\n * handles the wrap, so any real value is valid.\n */\nexport function bearingToOffsetPx(bearingDeg: number, viewportW: number, bgW: number): number {\n const col = (0.5 + bearingDeg / 360) * bgW; // image column (px) for the bearing\n return viewportW / 2 - col;\n}\n\n/**\n * Full scaled image height (px) so that a `vfovDeg`-tall slice fills `viewportH`.\n * The image spans 180° vertically, so height = viewportH · 180/vfov.\n */\nexport function windowedBgHeight(viewportH: number, vfovDeg: number = VFOV_DEG): number {\n return viewportH * (180 / vfovDeg);\n}\n\n/**\n * background-position Y (px) that centres the image's horizon (its vertical\n * centre) in the viewport, offset by `pitchPx` (deviation from the horizon,\n * clamped to ±`MAX_PITCH_DEG`). Positive `pitchPx` looks up.\n */\nexport function horizonOffsetPy(viewportH: number, bgH: number, pitchPx: number): number {\n return (viewportH - bgH) / 2 + clampPitchPx(pitchPx, bgH);\n}\n\n/** Clamp a pitch drag (px) to ±MAX_PITCH_DEG of image travel, and never past the\n * image edge. `bgH` px map the full 180°, so a degree is `bgH/180` px. */\nexport function clampPitchPx(pitchPx: number, bgH: number): number {\n const limit = (MAX_PITCH_DEG / 180) * bgH;\n return Math.max(-limit, Math.min(limit, pitchPx));\n}\n\nexport interface PanoramaOptions {\n fadeMs?: number;\n seatLabel?: string;\n onClose?: () => void;\n}\n\nexport function mountPanorama(container: HTMLElement, view: SeatView, opts: PanoramaOptions = {}): PanoramaHandle {\n const fadeMs = opts.fadeMs ?? 400;\n const bearing = view.initialBearingDeg ?? 0;\n\n const root = document.createElement('div');\n root.setAttribute('role', 'dialog');\n root.setAttribute('aria-label', opts.seatLabel ? `View from ${opts.seatLabel}` : 'View from seat');\n Object.assign(root.style, {\n position: 'absolute', inset: '0', zIndex: '10', opacity: '0',\n transition: `opacity ${fadeMs}ms ease`, background: '#05070c',\n overflow: 'hidden', touchAction: 'none',\n } as CSSStyleDeclaration);\n\n const pano = document.createElement('div');\n Object.assign(pano.style, {\n position: 'absolute', inset: '0',\n backgroundImage: `url(\"${view.url}\")`, backgroundRepeat: 'repeat-x',\n cursor: 'grab',\n } as CSSStyleDeclaration);\n root.appendChild(pano);\n\n // Close affordance.\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.setAttribute('aria-label', 'Close');\n closeBtn.textContent = '✕';\n Object.assign(closeBtn.style, {\n position: 'absolute', top: '12px', right: '12px', zIndex: '2',\n width: '34px', height: '34px', borderRadius: '999px', cursor: 'pointer',\n border: '1px solid rgba(255,255,255,0.25)', background: 'rgba(8,12,18,0.6)',\n color: '#e6edf3', fontSize: '15px', lineHeight: '1',\n } as CSSStyleDeclaration);\n root.appendChild(closeBtn);\n\n const hint = document.createElement('div');\n hint.textContent = 'Drag to look around · Esc to close';\n Object.assign(hint.style, {\n position: 'absolute', bottom: '12px', left: '0', right: '0', textAlign: 'center',\n color: 'rgba(230,237,243,0.7)', font: '12px ui-sans-serif, system-ui, sans-serif',\n pointerEvents: 'none',\n } as CSSStyleDeclaration);\n root.appendChild(hint);\n\n container.appendChild(root);\n\n // Layout: window a ~70° vertical slice of the sphere (horizon-centred) so the\n // venue fills the frame instead of floating in dead sky + black floor. The\n // image is scaled so that slice is exactly the viewport height; width scales by\n // the same factor, so `bearingToOffsetPx` stays correct. `pitchPx` is the\n // vertical drag deviation from the horizon, clamped to ±35°.\n let bgW = 0;\n let bgH = 0;\n let posX = 0;\n let pitchPx = 0;\n const layout = (): void => {\n const vh = root.clientHeight || 1;\n const vw = root.clientWidth || 1;\n const natW = img.naturalWidth || vw * 2;\n const natH = img.naturalHeight || vh;\n bgH = windowedBgHeight(vh);\n bgW = bgH * (natW / natH);\n pano.style.backgroundSize = `${bgW}px ${bgH}px`;\n if (!posInitialised) { posX = bearingToOffsetPx(bearing, vw, bgW); posInitialised = true; }\n pitchPx = clampPitchPx(pitchPx, bgH);\n pano.style.backgroundPosition = `${posX}px ${horizonOffsetPy(vh, bgH, pitchPx)}px`;\n };\n let posInitialised = false;\n\n const applyPos = (): void => {\n const vh = root.clientHeight || 1;\n pitchPx = clampPitchPx(pitchPx, bgH);\n pano.style.backgroundPosition = `${posX}px ${horizonOffsetPy(vh, bgH, pitchPx)}px`;\n };\n\n const img = new Image();\n img.onload = layout;\n img.src = view.url;\n // If it's already cached, onload may not fire — lay out on next frame too.\n requestAnimationFrame(layout);\n\n // Pan: horizontal (repeat-x wraps seamlessly) + vertical pitch (clamped ±35°).\n let dragging = false;\n let lastX = 0;\n let lastY = 0;\n const onDown = (e: PointerEvent): void => {\n dragging = true; lastX = e.clientX; lastY = e.clientY; pano.style.cursor = 'grabbing';\n try { pano.setPointerCapture?.(e.pointerId); } catch { /* no active pointer */ }\n };\n const onMove = (e: PointerEvent): void => {\n if (!dragging) return;\n posX += e.clientX - lastX;\n pitchPx += e.clientY - lastY;\n lastX = e.clientX;\n lastY = e.clientY;\n applyPos();\n };\n const onUp = (e: PointerEvent): void => {\n dragging = false; pano.style.cursor = 'grab';\n try { pano.releasePointerCapture?.(e.pointerId); } catch { /* no active pointer */ }\n };\n pano.addEventListener('pointerdown', onDown);\n pano.addEventListener('pointermove', onMove);\n pano.addEventListener('pointerup', onUp);\n pano.addEventListener('pointercancel', onUp);\n\n let closed = false;\n let disposed = false;\n let fadeTimer = 0;\n const removeListeners = (): void => {\n pano.removeEventListener('pointerdown', onDown);\n pano.removeEventListener('pointermove', onMove);\n pano.removeEventListener('pointerup', onUp);\n pano.removeEventListener('pointercancel', onUp);\n window.removeEventListener('keydown', onKey);\n };\n const teardown = (): void => {\n if (fadeTimer) { window.clearTimeout(fadeTimer); fadeTimer = 0; }\n removeListeners();\n if (root.parentNode) root.parentNode.removeChild(root);\n };\n const close = (): void => {\n if (closed) return;\n closed = true;\n root.style.opacity = '0';\n // Guard the fade callback: a dispose() (or a retarget that disposes us) inside\n // the fade window clears the timer AND flips `disposed`, so a stray fire can\n // never call onClose into a newer flight/panorama.\n const done = (): void => {\n fadeTimer = 0;\n if (disposed) return;\n teardown();\n opts.onClose?.();\n };\n fadeTimer = window.setTimeout(done, fadeMs);\n };\n const onKey = (e: KeyboardEvent): void => {\n if (e.key === 'Escape') { e.stopPropagation(); close(); }\n };\n window.addEventListener('keydown', onKey);\n closeBtn.addEventListener('click', close);\n\n // Fade in on the next frame (0 → 1).\n requestAnimationFrame(() => { root.style.opacity = '1'; });\n\n return {\n close,\n dispose(): void { closed = true; disposed = true; teardown(); },\n };\n}\n","/**\n * view3d analytics — a tiny, decoupled event emitter for the venue view. The\n * caller (app/harness) supplies `onAnalytics`; this class owns the per-mount\n * state (first-orbit latch, panorama dwell timing) and, crucially, wraps EVERY\n * callback invocation in try/catch so a throwing analytics sink can never break\n * rendering. No DOM, no GL — unit-testable in isolation.\n */\n\nexport type Analytics3DCallback = (event: string, props?: Record<string, unknown>) => void;\n\nconst now = (): number =>\n (typeof performance !== 'undefined' && performance.now ? performance.now() : Date.now());\n\nexport class Analytics3D {\n private cb?: Analytics3DCallback;\n private orbitLatched = false;\n private panoramaOpenedAt = 0;\n\n constructor(cb?: Analytics3DCallback) {\n this.cb = cb;\n }\n\n /** The single guarded emit point — analytics must never throw into the loop. */\n private emit(event: string, props?: Record<string, unknown>): void {\n if (!this.cb) return;\n try {\n this.cb(event, props);\n } catch {\n /* analytics sink threw — swallow so rendering is never affected */\n }\n }\n\n opened(seats: number, hasHeights: boolean): void {\n this.emit('3d_opened', { seats, hasHeights });\n }\n\n /** First user-driven orbit/dolly per mount only (the intro ease is not user\n * input, so callers must gate this on real pointer/wheel gestures). */\n orbitEngaged(): void {\n if (this.orbitLatched) return;\n this.orbitLatched = true;\n this.emit('3d_orbit_engaged');\n }\n\n seatPicked(seatId: string, sectionId: string | undefined): void {\n this.emit('3d_seat_picked', { seatId, sectionId });\n }\n\n cinematicPlayed(durationMs: number): void {\n this.emit('3d_cinematic_played', { durationMs, reducedMotion: false });\n }\n\n cinematicSkipped(): void {\n this.emit('3d_cinematic_skipped', { reducedMotion: true });\n }\n\n cinematicCancelled(): void {\n this.emit('3d_cinematic_cancelled');\n }\n\n panoramaOpened(): void {\n this.panoramaOpenedAt = now();\n this.emit('3d_panorama_opened');\n }\n\n panoramaClosed(): void {\n const viewMs = this.panoramaOpenedAt ? Math.round(now() - this.panoramaOpenedAt) : 0;\n this.panoramaOpenedAt = 0;\n this.emit('3d_panorama_closed', { viewMs });\n }\n}\n"],"mappings":"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","names":["Quat","Vec3","norm","now","scale","scale","polygonClipping","polygonClipping","earcut","polygonClipping","earcut","polygonClipping","sub","Mesh","Transform","Transform","Mesh","Vec3","Vec3","now","DEG","Vec3","Quat"]}