Create dimension animation manager
Singleton manager for dimension state
Controller for frame updates
OptionalcommittedQuality: () => number | null
Optional probe for the worst-served laddered node's committed energy.
Supplied by the wiring in input/.../dimension-navigation/setup.ts, which
has the scene. Absent (or returning null) falls back to cadence-only
feedback, which is the honest behaviour when nothing on screen is stamped.
PrivateanimationAnimation state per dimension (sparse map)
PrivateisWhether we've registered with AnimationController
PrivatedimensionCached dimension ranges for performance
PrivatedefaultThe detail a fresh dimension state starts with (see setDefaultLadderDepth).
PrivatelastTimestamp of the previous animation frame (for the frame-interval EWMA).
PrivateframeEWMA of rAF frame intervals in ms; 0 until two frames have been seen.
PrivatependingPer-dimension update tracking — prevents advancing faster than data loads
PrivatestepPlayhead steps taken so far (one per setDimensionValue); see onFrame.
PrivateremoveUnsubscribe function for sceneDimsManager listener
Private_True while dispose() runs — suppresses the pause() refine re-trigger (a torn-down scene must not receive a fresh dimension update).
PrivatesceneSingleton manager for dimension state
PrivateanimationController for frame updates
Private OptionalcommittedOptional probe for the worst-served laddered node's committed energy.
Supplied by the wiring in input/.../dimension-navigation/setup.ts, which
has the scene. Absent (or returning null) falls back to cadence-only
feedback, which is the honest behaviour when nothing on screen is stamped.
PrivateupdateUpdate cached dimension ranges
PrivateensureRegister frame callback with animation controller Called automatically when an animation starts; pause() drops it again once nothing plays (see unregister)
PrivateunregisterDrop the per-frame callback (only ours, not others) so the loop can idle.
PrivatetickThe tick period for a dimension, floored at the configured minimum frame time.
PrivatenoteTrack the display's frame interval (an EWMA of rAF deltas). Deltas above
MAX_FRAME_INTERVAL_MS are gaps (a stopped loop, a hidden tab), not
vsync, and are ignored.
PrivateisSchedule-based tick gate (#2944 A7). Ticks are due on a fixed grid
(nextDue += period) rather than "one period after the frame that last
ticked": the old elapsed-since-last-tick gate drifted by up to a vsync per
tick, so 10 fps achieved ~9.2 and 30 fps ~23 on a 60 Hz display. A frame
within half a vsync of the due time takes the tick (the nearest frame
wins). When the playhead is more than a period late — a data stall, or a
pending update that held the gate — the schedule resyncs from now instead
of bursting catch-up ticks. Advances the schedule when it returns true.
PrivateonPer-frame callback - update all active animations Called by AnimationController on each animation frame (~60 FPS)
whether any playhead stepped this frame (a new slice is drawn). The step's dims listener also wakes the loop, so this does not change WHETHER the frame renders; it keeps the callback's contract honest.
PrivatestepMove one playhead (listeners start the data load) and count the step for onFrame — only when the value actually changed: a tick that wraps back onto the same value (a single-timepoint dim) draws no new slice.
PrivateupdateUpdate a single dimension's animation
Includes frame synchronization: if a data update is still in progress, this method skips the frame to prevent advancing animation faster than data loading can keep up. This ensures complete rendering of each frame.
Dimension index to update
Current timestamp in milliseconds
PEEK the value the next playback tick would move to — loop/bounce/ backward aware, WITHOUT mutating any animation state. Used by the t+1 slice prefetcher to warm the S-cache for the upcoming tick.
The predicted next value, or null when the dimension is not playing, dims/ranges are unavailable, or the next step would STOP playback (loop mode 'once' at its boundary — nothing to prefetch).
Indices of every dimension currently playing.
The frame period (ms) of the FASTEST playing dimension (1000 / targetFPS),
or null when nothing plays. The LOD registry sizes its playback aspiration
against it (LODGroupRegistryDeps.getPlaybackPeriodMs).
Whether ANY dimension is currently playing (== getFrameBudgetMs() !== null).
Start or resume animation for a dimension
Dimension index to animate
Optionaloptions: PlayOptions
Animation options (FPS, loop mode, direction)
True if animation started, false if already playing
Pause animation for a dimension
Dimension index to pause
True if animation was paused, false if already paused
Per-tick LOD time budget for the progressive loaders, or null when no
dimension animation is playing (normal full-refinement behavior).
While playing, each update pass should finish within the animation frame
window so every tick commits a frame (see the pacing gate in
updateDimension). The budget is a fraction of the frame window of the
FASTEST currently-playing dimension (config.dimensionAnimation.playback),
floored at minBudgetMs. Read per-update by the dims listener and
threaded to the loaders as a per-pass directive — never persisted.
Playback "detail" for the current tick: the pinned additive-ladder depth
(rungs) the progressive loaders must load for every frame, or null when no
playing dimension pins one (time-budgeted streaming). With several playing
dimensions the DEEPEST pin wins — a pinned dim must never be drawn below
its setting because another dim plays on Auto. Threaded to the loaders as
ViewState.ladderDepth by updateAllNDNodes, and to the t+1 shadow
prefetch, so the next frame's S-cache entry carries the pinned prefix.
The detail a SCRUB pass (a slider drag, a keyboard step, the pause
re-trigger — any slice change while nothing plays) is pinned to: the
deepest explicit pin among all dimensions' settings, else 'auto' if any
dimension asks for it, else null. Dimensions never touched inherit
getDefaultLadderDepth. The orchestrator pins the scrub pass to it
and follows up with an unpinned settle pass once the scrub is quiet, so a
paused view still refines to the full ladder.
The detail a dimension starts with before anyone sets it: the scene's
authored viewer_config.playback_lod_depth when present, else
config.dimensionAnimation.defaults.ladderDepth. Setting it also updates
every dimension still on the previous default, so an authored value read
after the slider panel created its states still takes effect.
Set the playback detail for a dimension: a pinned additive-ladder depth
(rungs, Infinity = whole ladder) every frame is drawn at while it plays or
scrubs, 'auto' for the energy rule, or null for Fast (time-budgeted
streaming). Takes effect on the next tick.
Toggle play/pause for a dimension
Dimension index to toggle
Optionaloptions: PlayOptions
Animation options (only used if starting)
True if now playing, false if now paused
Stop animation and remove state for a dimension
Dimension index to stop
Check if a dimension is currently animating
Dimension index to check
True if animating, false otherwise
Get animation state for a dimension
Dimension index
Animation state, or undefined if not animating
Set target FPS for a dimension
Dimension index
Target FPS (clamped to valid range)
Increase speed to next preset or by increment
Dimension index
Decrease speed to previous preset or by decrement
Dimension index
PrivatecreateFresh per-dimension state seeded from config.dimensionAnimation.defaults. Single source for play()/setTargetFPS()/setLoopMode()/setStepSize() so a new default (like stepSize) cannot be forgotten at one call site.
PrivateresolveThe per-tick step handed to advanceDimensionValue: the user's explicit override when set (quantized to the authored grid for discrete dims, one cell minimum — see #1520), else the authored step for discrete dims, else null (continuous fps-derived increment). MUST be used by BOTH updateDimension and peekNextValue — the playhead and the t+1 prefetch have to agree.
Set (or clear, with null) the per-dimension step override. Drives the animation per-tick increment AND the [ / ] keyboard navigation; the slider wheel/drag deliberately stay on the dimension's own base step. Values are validated (finite, > 0) and clamped to the dimension's range width so one step can never overshoot the whole range.
The per-dimension step override, or null when on Auto / no state yet.
Set loop mode for a dimension
Dimension index
Loop mode to set
Clean up all animations and unregister from animation controller
Manages animation state for multiple dimensions