@seatlayer/core 0.28.3 → 0.29.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.
- package/dist/chunk-5MLADB2N.js +53 -0
- package/dist/chunk-5MLADB2N.js.map +1 -0
- package/dist/index.cjs +921 -117
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -1463
- package/dist/index.d.ts +51 -1463
- package/dist/index.js +930 -161
- package/dist/index.js.map +1 -1
- package/dist/types-B-tpUFqz.d.cts +1552 -0
- package/dist/types-B-tpUFqz.d.ts +1552 -0
- package/dist/view3d/index.cjs +1972 -0
- package/dist/view3d/index.cjs.map +1 -0
- package/dist/view3d/index.d.cts +176 -0
- package/dist/view3d/index.d.ts +176 -0
- package/dist/view3d/index.js +1906 -0
- package/dist/view3d/index.js.map +1 -0
- package/package.json +22 -2
|
@@ -0,0 +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/core/units.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 * Real-world scale primitives — the ONE place the app fixes chart-unit ↔ metre ↔\n * renderer-world scale, and the section 3D-geometry resolver that rides on it.\n *\n * Everything that converts between chart units, metres, and renderer \"world\"\n * units derives from {@link METRES_PER_CHART_UNIT}. Renderer world units and\n * chart units are 1:1, so metres → world is exactly {@link CHART_UNITS_PER_METRE}\n * (the single m→world conversion constant Phase B consumers use).\n *\n * This is a leaf module (types only) so both `layout.ts` and `sections.ts` can\n * import it without the two forming an import cycle.\n */\nimport type { SectionObject } from './types';\n\n/**\n * Chart units → metres. seatSpacing 24 ≈ 0.55 m (a real seat pitch). This single\n * constant anchors every real-world scale in the app; `generatePanorama` and the\n * iso lift both derive from it instead of re-declaring their own 0.55/24.\n */\nexport const METRES_PER_CHART_UNIT = 0.55 / 24;\n\n/**\n * Metres → chart units, i.e. metres → renderer world units (world == chart units\n * in the engine). THE single m→world conversion constant: the iso view lifts an\n * elevated section by `sectionGeometry(section).height × CHART_UNITS_PER_METRE`.\n */\nexport const CHART_UNITS_PER_METRE = 1 / METRES_PER_CHART_UNIT;\n\n/**\n * Renderer world units a section lifts per {@link SectionObject.elevation} tier in\n * the legacy iso view (mirror of `SeatmapRenderer`'s old `LIFT_PER_STEP`). Kept so\n * the tier→metres fallback below reproduces today's iso look byte-for-byte.\n */\nexport const LIFT_PER_STEP_WORLD = 58;\n\n/**\n * Metres of front-edge height one elevation tier represents. Chosen (not guessed)\n * so `elevation × TIER_HEIGHT_M` metres, scaled back through\n * {@link CHART_UNITS_PER_METRE}, equals the legacy `elevation × LIFT_PER_STEP`\n * world lift exactly — an un-authored chart stays pixel-identical. ≈ 1.329 m/tier.\n */\nexport const TIER_HEIGHT_M = LIFT_PER_STEP_WORLD * METRES_PER_CHART_UNIT;\n\n/** Canonical authored section-geometry bounds. Keep every UI/API/renderer on\n * these constants so a producer cannot silently invent a second unit system. */\nexport const SECTION_ELEVATION_TIER_MIN = 0;\nexport const SECTION_ELEVATION_TIER_MAX = 3;\nexport const SECTION_HEIGHT_MIN_M = 0;\nexport const SECTION_HEIGHT_MAX_M = 120;\nexport const SECTION_RAKE_MIN_DEG = 0;\nexport const SECTION_RAKE_MAX_DEG = 45;\n\n/** Curated charts released before the height field used coarse levels 4–7.\n * Preserve their established lift while drafts/templates migrate to an explicit\n * height. Values above 7 came from broken compiler multipliers and must never be\n * interpreted as physical tiers. */\nexport const LEGACY_SECTION_ELEVATION_TIER_MAX = 7;\n\n/**\n * Seated spectator eye height above the tier floor (arena ≈ 1.20 m; theatre refs\n * use 1.15 m). Also the flat-ground baseline eye height, so a flat, ground-level\n * seat produces zero elevation offset in the 360° stage-pitch math (back-compat).\n */\nexport const SEATED_EYE_HEIGHT_M = 1.2;\n\n/**\n * Resolve a section's real 3D geometry, applying the legacy-elevation fallback so\n * old charts and new charts share ONE code path. Every consumer (iso lift, 360°\n * eye-height, author lint, any 2D depth cue) must call this and never read the raw\n * {@link SectionObject.height}/{@link SectionObject.rake} fields — that is what\n * keeps un-authored charts rendering identically to today.\n *\n * - `height`: authored absolute metres if present, else owning-floor base height\n * plus `elevation × TIER_HEIGHT_M`.\n * - `rake`: authored degrees if present, else 0 (flat).\n *\n * Malformed values are bounded here as a last defensive barrier. Structural\n * validation still reports them so drafts can be repaired instead of silently\n * persisting a renderer-only interpretation.\n *\n * Pure — no document mutation, no side effects.\n */\nexport interface SectionGeometryContext {\n /** Absolute physical height of the owning floor above the venue datum. */\n floorBaseHeightM?: number;\n}\n\nfunction finiteClamped(value: number | undefined, min: number, max: number, fallback: number): number {\n return Number.isFinite(value) ? Math.max(min, Math.min(max, value as number)) : fallback;\n}\n\n/** Canonical 0–3 tier exposed by Designer/shared operations. */\nexport function sectionElevationTier(value: number | undefined): number {\n if (!Number.isFinite(value)) return SECTION_ELEVATION_TIER_MIN;\n return Math.max(\n SECTION_ELEVATION_TIER_MIN,\n Math.min(SECTION_ELEVATION_TIER_MAX, Math.round(value as number)),\n );\n}\n\n/** Runtime-only compatibility tier. Released curated charts used integers 4–7;\n * preserve those while bounding compiler mistakes such as 12/55/220 to the\n * canonical maximum. New documents must store only {@link sectionElevationTier}. */\nfunction compatibleAutomaticTier(value: number | undefined): number {\n if (!Number.isFinite(value) || !Number.isInteger(value) || (value as number) < 0) return 0;\n if ((value as number) <= LEGACY_SECTION_ELEVATION_TIER_MAX) return value as number;\n return SECTION_ELEVATION_TIER_MAX;\n}\n\nexport function sectionGeometry(\n section: Pick<SectionObject, 'elevation' | 'height' | 'rake'>,\n context: SectionGeometryContext = {},\n): {\n height: number;\n rake: number;\n} {\n const floorBaseHeightM = finiteClamped(\n context.floorBaseHeightM,\n SECTION_HEIGHT_MIN_M,\n SECTION_HEIGHT_MAX_M,\n 0,\n );\n const automaticHeight = Math.min(\n SECTION_HEIGHT_MAX_M,\n floorBaseHeightM + compatibleAutomaticTier(section.elevation) * TIER_HEIGHT_M,\n );\n const height = section.height === undefined\n ? automaticHeight\n : finiteClamped(section.height, SECTION_HEIGHT_MIN_M, SECTION_HEIGHT_MAX_M, automaticHeight);\n const rake = finiteClamped(section.rake, SECTION_RAKE_MIN_DEG, SECTION_RAKE_MAX_DEG, 0);\n return { height, rake };\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":["import_ogl","import_ogl","norm","now","earcut","topY","import_ogl","import_ogl","sub","import_ogl","import_ogl","now","DEG"]}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { C as ChartDoc, E as ExpandedSeat } from '../types-B-tpUFqz.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Dirty-flag render loop. A frame is drawn only while the camera is moving /
|
|
5
|
+
* damping or an availability update arrived; when idle, no rAF is scheduled at
|
|
6
|
+
* all (zero CPU/GPU when parked). Tracks an FPS EMA over frames actually
|
|
7
|
+
* rendered — it reads as "idle" when nothing is scheduled.
|
|
8
|
+
*/
|
|
9
|
+
interface RenderLoopStats {
|
|
10
|
+
fps: number;
|
|
11
|
+
rendered: number;
|
|
12
|
+
idle: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Slice 3 hand-off — the DOM panorama overlay the fly-to-seat cinematic
|
|
17
|
+
* dissolves into. Decoupled by design: the CALLER supplies the equirectangular
|
|
18
|
+
* image (via mountVenue3D's getSeatView), so view3d never imports the app's
|
|
19
|
+
* panorama generator and the chunk stays lean.
|
|
20
|
+
*
|
|
21
|
+
* Technique (mirrors SeatPicker.openSeatView, reimplemented small): an equirect
|
|
22
|
+
* image panned with `repeat-x`; the initial horizontal offset is set so the
|
|
23
|
+
* panorama's bearing matches the final camera yaw — the dissolve reads as the
|
|
24
|
+
* same view sharpening, not a cut. CSS opacity fade is compositor-only.
|
|
25
|
+
*/
|
|
26
|
+
interface SeatView {
|
|
27
|
+
url: string;
|
|
28
|
+
/** Bearing (deg, 0 = facing the focal/stage) the panorama should open centred
|
|
29
|
+
* on, to match the camera's final yaw. Default 0 (both face the stage). */
|
|
30
|
+
initialBearingDeg?: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* view3d analytics — a tiny, decoupled event emitter for the venue view. The
|
|
35
|
+
* caller (app/harness) supplies `onAnalytics`; this class owns the per-mount
|
|
36
|
+
* state (first-orbit latch, panorama dwell timing) and, crucially, wraps EVERY
|
|
37
|
+
* callback invocation in try/catch so a throwing analytics sink can never break
|
|
38
|
+
* rendering. No DOM, no GL — unit-testable in isolation.
|
|
39
|
+
*/
|
|
40
|
+
type Analytics3DCallback = (event: string, props?: Record<string, unknown>) => void;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* view3d palette + seat-state model — the single source of colour truth for the
|
|
44
|
+
* OGL venue view. Pure data (no GPU, no DOM) so the scene builder and the unit
|
|
45
|
+
* tests share one definition. Colours are linear-ish RGB triplets in 0..1.
|
|
46
|
+
*
|
|
47
|
+
* Look brief (docs/3d-usp-strategy §3): desaturated cool greys for structure,
|
|
48
|
+
* one warm accent for the stage, availability colours only on seats.
|
|
49
|
+
*/
|
|
50
|
+
type SeatState3D = 'available' | 'held' | 'sold' | 'selected' | 'dimmed';
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Pure geometry primitives for the view3d scene — no OGL, no DOM, so the whole
|
|
54
|
+
* scene-model builder is unit-testable in a plain runtime.
|
|
55
|
+
*
|
|
56
|
+
* Coordinate convention: chart units (x, y) map to world metres as
|
|
57
|
+
* worldX = x * METRES_PER_CHART_UNIT
|
|
58
|
+
* worldZ = y * METRES_PER_CHART_UNIT
|
|
59
|
+
* worldY = up (height in metres)
|
|
60
|
+
* i.e. the chart's audience-depth (+y) becomes world +Z, and Y is the vertical.
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
interface MeshData {
|
|
64
|
+
/** Non-indexed triangle soup: 3 floats per vertex. */
|
|
65
|
+
position: Float32Array;
|
|
66
|
+
normal: Float32Array;
|
|
67
|
+
/** Baked vertex colour incl. AO, 3 floats per vertex. */
|
|
68
|
+
color: Float32Array;
|
|
69
|
+
/** Vertex count (position.length / 3). */
|
|
70
|
+
count: number;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Pure builder for the instanced seat cloud. Produces the per-instance arrays a
|
|
75
|
+
* single OGL InstancedMesh consumes (one draw call for every seat), plus the
|
|
76
|
+
* seatId → instanceIndex map that `setAvailability` uses to patch only the seats
|
|
77
|
+
* that actually changed via a sub-range `bufferSubData` upload.
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
interface SeatInstanceData {
|
|
81
|
+
count: number;
|
|
82
|
+
/** vec3 per instance: world (x, y, z) in metres. */
|
|
83
|
+
iPosition: Float32Array;
|
|
84
|
+
/** float per instance: index into the seat-state colour LUT. */
|
|
85
|
+
iState: Float32Array;
|
|
86
|
+
/** seatId → instance index (drives targeted availability updates). */
|
|
87
|
+
idToIndex: Map<string, number>;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface SceneModel {
|
|
91
|
+
/** Every non-seat surface merged into one triangle soup (1 draw call). */
|
|
92
|
+
solids: MeshData;
|
|
93
|
+
seats: SeatInstanceData;
|
|
94
|
+
bounds: {
|
|
95
|
+
/** World-metre venue centre (camera target). */
|
|
96
|
+
center: [number, number, number];
|
|
97
|
+
/** Half-diagonal of the horizontal footprint, metres (camera fit). */
|
|
98
|
+
radius: number;
|
|
99
|
+
groundY: number;
|
|
100
|
+
};
|
|
101
|
+
/** 5 × vec3 flat LUT for the seat fragment shader. */
|
|
102
|
+
stateColorLUT: number[];
|
|
103
|
+
seatCount: number;
|
|
104
|
+
/** Venue focal point in world metres (cinematic look-at target). */
|
|
105
|
+
focalWorld: [number, number, number];
|
|
106
|
+
}
|
|
107
|
+
interface SceneModelInput {
|
|
108
|
+
doc: ChartDoc;
|
|
109
|
+
seats: ExpandedSeat[];
|
|
110
|
+
/** Optional initial per-seat state (default all available). */
|
|
111
|
+
initialState?: (seat: ExpandedSeat) => SeatState3D;
|
|
112
|
+
}
|
|
113
|
+
declare function buildSceneModel(input: SceneModelInput): SceneModel;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* view3d — the sole dynamic-import boundary for the lazy OGL venue-view chunk.
|
|
117
|
+
*
|
|
118
|
+
* const { mountVenue3D } = await import('../view3d');
|
|
119
|
+
* const handle = mountVenue3D(container, { doc, seats }, { onSeatPick, getSeatView });
|
|
120
|
+
* await handle.flyToSeat(seatId);
|
|
121
|
+
*
|
|
122
|
+
* Read-only 3D of any chart, fed entirely from the existing height contract.
|
|
123
|
+
* Slice 1: orbit camera, extruded tiers/stage/GA, instanced seat dots, sub-range
|
|
124
|
+
* availability, dispose + context-loss survival. Slice 2: GPU color-pick. Slice
|
|
125
|
+
* 3: the fly-to-seat cinematic that dissolves into the view-from-seat panorama.
|
|
126
|
+
*/
|
|
127
|
+
|
|
128
|
+
interface Venue3DInput {
|
|
129
|
+
doc: ChartDoc;
|
|
130
|
+
/** Expanded seats (from `expandChart`) — carry x/y + resolved eyeHeightM. */
|
|
131
|
+
seats: ExpandedSeat[];
|
|
132
|
+
/** Optional initial per-seat state (default all available). */
|
|
133
|
+
initialState?: (seat: ExpandedSeat) => SeatState3D;
|
|
134
|
+
}
|
|
135
|
+
interface Venue3DOptions {
|
|
136
|
+
/** Fired on a tap that hits a seat (GPU color-pick). Not fired on empty taps. */
|
|
137
|
+
onSeatPick?: (seatId: string) => void;
|
|
138
|
+
/**
|
|
139
|
+
* Supplies the view-from-seat panorama for the cinematic hand-off. Decoupled:
|
|
140
|
+
* the caller (app/harness) owns panorama generation; view3d never imports it.
|
|
141
|
+
* Called at PICK time to pre-render, so flyToSeat has zero wait on landing.
|
|
142
|
+
*/
|
|
143
|
+
getSeatView?: (seatId: string) => SeatView | Promise<SeatView>;
|
|
144
|
+
/**
|
|
145
|
+
* Decoupled analytics sink. Emits the venue-view journey: `3d_opened`,
|
|
146
|
+
* `3d_orbit_engaged` (first user gesture), `3d_seat_picked`,
|
|
147
|
+
* `3d_cinematic_played`/`_skipped`/`_cancelled`, `3d_panorama_opened`/`_closed`.
|
|
148
|
+
* Every invocation is wrapped in try/catch — a throwing sink never breaks
|
|
149
|
+
* rendering. Absent = no events emitted.
|
|
150
|
+
*/
|
|
151
|
+
onAnalytics?: Analytics3DCallback;
|
|
152
|
+
}
|
|
153
|
+
interface Venue3DStats extends RenderLoopStats {
|
|
154
|
+
drawCalls: number;
|
|
155
|
+
seatCount: number;
|
|
156
|
+
}
|
|
157
|
+
interface Venue3DHandle {
|
|
158
|
+
dispose(): void;
|
|
159
|
+
setAvailability(updates: {
|
|
160
|
+
seatId: string;
|
|
161
|
+
state: SeatState3D;
|
|
162
|
+
}[]): void;
|
|
163
|
+
setSelection(seatIds: string[]): void;
|
|
164
|
+
/** Fly the camera from the overview into `seatId` and dissolve into its
|
|
165
|
+
* view-from-seat panorama. Resolves at flight end; a drag cancels it, a second
|
|
166
|
+
* call retargets, dispose resolves early. Reduced-motion → a short fade. */
|
|
167
|
+
flyToSeat(seatId: string): Promise<void>;
|
|
168
|
+
resize(): void;
|
|
169
|
+
stats(): Venue3DStats;
|
|
170
|
+
loseContextForTest(): void;
|
|
171
|
+
/** Test hook: force (or clear) the reduced-motion path. */
|
|
172
|
+
setReducedMotionForTest(value: boolean | null): void;
|
|
173
|
+
}
|
|
174
|
+
declare function mountVenue3D(container: HTMLElement, input: Venue3DInput, opts?: Venue3DOptions): Venue3DHandle;
|
|
175
|
+
|
|
176
|
+
export { type Analytics3DCallback, type SeatState3D, type SeatView, type Venue3DHandle, type Venue3DInput, type Venue3DOptions, type Venue3DStats, buildSceneModel, mountVenue3D };
|