/** * Fixed-route 3D-tile pre-loading (spec: "Routing & Tracking" adjacency; * public issue #16's flyby pop-in / "blurry then clear" LOD refinement). * * A story's camera route is fully known before it plays, so the tiles every * future viewport needs can be fetched AND parsed ahead of time. The whole * mechanism rides public loaders.gl surface: `Tileset3D.selectTiles()` * accepts arbitrary viewports (traversal queues real tile loads into the * tileset cache), `isLoaded()` signals the queue draining, and the flight * arc is sampled with the SAME interpolator deck's fly-to uses * (`flyToViewport`), so the zoom-out-then-in phase — where refinement blur * is worst — is sampled on the true path, not a straight line. * * What warming buys: network + parse eliminated on arrival. What it can't: * permanent GPU residency — deck uploads buffers for SELECTED tiles only, * so far tiles' VRAM is freed when selection returns to the parked camera; * the arrival cost after warming is a sub-frame re-upload. The tileset's * cache budget (default 32 MB) bounds how much parsed content survives to * playback — `budgetMB` raises it for the take (raise-and-keep: restoring * it mid-playback would evict exactly the tiles being warmed for), and * `load-options='{"tileset":{"maximumMemoryUsage":512}}'` is the authored, * persistent form of the same knob. */ import { WebMercatorViewport } from "@deck.gl/core"; export interface CameraKeyframe { longitude: number; latitude: number; zoom: number; pitch?: number; bearing?: number; } /** Warm-pass viewports are minted with this id prefix, and warm cleanup evicts traversal state by it — one constant so the two sides can't drift (and real viewport ids must simply never start with it). */ export declare const WARM_VIEWPORT_PREFIX = "warm-"; /** `undefined`/`null`/`""` → undefined; otherwise `Number()` with a finite guard. The one "what counts as a number" rule shared by action payloads and camera-field reading. */ export declare function coerceNumber(value: unknown): number | undefined; /** * One point on the fly-to arc between two keyframes, at fraction `t` ∈ [0,1] * — deck's own flight math (lng/lat/zoom via flyToViewport; pitch/bearing * lerp linearly, matching FlyToInterpolator's treatment of them as plain * transition props). The shared core of route sampling (by sample index) * and the paced-flyby driver's cameraAtTime (by story time). */ export declare function interpolateFlight(a: CameraKeyframe, b: CameraKeyframe, t: number, width: number, height: number): CameraKeyframe; /** * Sample the flight path through `keyframes` as renderer viewports — * `samplesPerLeg` points per consecutive pair via `interpolateFlight`. * Layout contract: index 0 is the starting keyframe and every * `samplesPerLeg`-th viewport thereafter lands exactly on an authored * keyframe — `destinationViewports` depends on it. */ export declare function sampleFlightViewports(keyframes: readonly CameraKeyframe[], width: number, height: number, samplesPerLeg?: number): WebMercatorViewport[]; /** The sampled viewports that sit ON authored keyframes (the fly-to destinations, where the camera arrives and lingers) — rides sampleFlightViewports' layout contract above. */ export declare function destinationViewports(viewports: readonly T[], samplesPerLeg: number): T[]; /** The slice of loaders.gl's Tileset3D the wait/raise driver touches — structural so tests fake it (and a pinned-version bump that changes the shape fails loudly at the type level); selection itself is owned by WarmableTile3DLayer (in-band). */ export interface WarmableTileset { isLoaded(): boolean; _cacheBytes?: number; _cacheOverflowBytes?: number; options?: { maximumScreenSpaceError?: number; }; memoryAdjustedScreenSpaceError?: number; /** Per-viewport-id traversal state loaders.gl retains forever — warm ids are evicted after the pass. */ frameStateData?: Record; roots?: Record; } export interface WarmOptions { /** Raise the tileset cache budget to this many MB (raise-and-keep). */ budgetMB?: number; /** Warm at a coarser LOD target: maximumScreenSpaceError × this for the pass (default 2 ≈ one level coarser), restored after. Raises BOTH the option and memoryAdjustedScreenSpaceError — the field traversal actually refines against. */ sseFactor?: number; /** Deadline; resolves {timedOut:true} with a partial warm rather than hanging (default 90s — background work). */ timeoutMs?: number; now?: () => number; sleep?: (ms: number) => Promise; } /** * Raise budgets, wait for the LAYER-driven warm to drain, restore SSE, evict * warm traversal state. Selection is NOT driven here — WarmableTile3DLayer * merges the warm viewports into its own update (loaders.gl's multi-viewport * API), so there is exactly one selection caller and nothing to race. */ export declare function warmTileset(tileset: WarmableTileset, opts?: WarmOptions): Promise<{ timedOut: boolean; }>; /** The camera fields a fly-to step can author — attribute strings or payload values; `center` is the documented `"[lng, lat]"` form, bare longitude/latitude accepted too. */ export interface CameraStepFields { center?: unknown; longitude?: unknown; latitude?: unknown; zoom?: unknown; pitch?: unknown; bearing?: unknown; } /** * The camera fields a fly-to authors, coerced — per-field undefined when * unauthored, null when NO camera field is authored at all. The ONE reading * of "what does this fly-to mean" shared by the fly-to action (which merges * only authored fields into the view) and `flyToTarget` (which inherits the * rest from the previous keyframe). `center` accepts the documented * `"[lng, lat]"` JSON form or an array; bare longitude/latitude ride as the * fallback. */ export declare function readCameraFields(fields: CameraStepFields): { longitude?: number; latitude?: number; zoom?: number; pitch?: number; bearing?: number; } | null; /** * The destination a fly-to step describes, with unset fields inherited from * `prev`. Returns null when the step authors no camera field at all * (nothing to move). */ export declare function flyToTarget(fields: CameraStepFields, prev: CameraKeyframe): CameraKeyframe | null; /** A story's camera keyframes from its DOM (route warming's input) — the same fold as buildCameraLegs, keeping the two step→camera walks one. */ export declare function extractStoryKeyframes(storyEl: Element, current: CameraKeyframe): CameraKeyframe[]; /** One camera-moving fly-to step as an arc segment over its own timeline interval. */ export interface CameraLeg { from: CameraKeyframe; to: CameraKeyframe; start: number; end: number; } /** * Camera legs from a story's built steps + timeline intervals (index- * aligned arrays): each camera-moving fly-to becomes an arc leg over its own * interval; every other step leaves the camera parked at the previous * destination. Pure — the paced driver's route model. */ export declare function buildCameraLegs(steps: readonly ({ action: string; } & CameraStepFields)[], intervals: readonly { start: number; end: number; }[], initial: CameraKeyframe): CameraLeg[]; /** * The camera the paced driver owns at story-time `t` — parked at `initial` * before the first leg, on the true fly-to arc mid-leg, at the destination * between/after legs. Leg starts are non-decreasing in authoring order * (timeline math guarantees it), so the last leg with start ≤ t is active. */ export declare function cameraAtTime(legs: readonly CameraLeg[], initial: CameraKeyframe, t: number, width: number, height: number): CameraKeyframe;