/** * Closed-form temporal splat motion — the CPU twin (spec truth) the GPU `SpacetimeSplatSource` * mirrors in TSL and every offline gate grades against. Two track models, both defined in * `.ai/SPLAT_VIDEO_FORMAT.md` §4: * * 1. `stg` (STG / splaTV import model), with `dt = time - trbfCenter`: * position(t) = base + m1·dt + m2·dt² + m3·dt³ (cubic, 9 motion floats) * rotation(t) = normalize(rotStatic + omega·dt) (linear angular velocity, 4 floats) * temporalOpacity(t) = exp(-(dt / trbfScale)²) (TRBF; trbfScale is the pre-exp'd width) * opacity(t) = baseOpacity · temporalOpacity(t) * Scale is static (not animated), matching STG/splatv. * * 2. `keyframes` (the USV-native model): windowed uniform Catmull-Rom position knots (deltas * from the decoded static base), hemisphere-aligned nlerp rotation knots, and a flat-top * per-splat lifespan `(birth, death, ramp)` evaluated as a smoothstep window — persistent * objects stay fully visible over their whole lifespan (no TRBF mid-life fade). * * @module SpacetimeMotion */ /** Three numeric components in xyz order. Typed arrays are accepted as inputs. */ export type SplatVector3Input = ArrayLike; /** Four numeric components in xyzw order. Typed arrays are accepted as inputs. */ export type SplatQuaternionInput = ArrayLike; /** Nine STG position coefficients: linear xyz, quadratic xyz, then cubic xyz. */ export type SpacetimeMotionCoefficients = ArrayLike; /** Concrete xyz tuple returned by the CPU motion evaluators. */ export type SplatVector3 = [number, number, number]; /** Concrete xyzw quaternion tuple returned by the CPU motion evaluators. */ export type SplatQuaternion = [number, number, number, number]; /** Complete STG Gaussian input evaluated by {@link evaluateSpacetimeGaussian}. */ export interface SpacetimeGaussianInput { base: SplatVector3Input; motion: SpacetimeMotionCoefficients; rotation: SplatQuaternionInput; omega: SplatQuaternionInput; opacity?: number; trbfCenter: number; trbfScale: number; } /** Evaluated STG Gaussian pose and temporal opacity at one absolute time. */ export interface SpacetimeGaussianResult { position: SplatVector3; rotation: SplatQuaternion; opacity: number; temporalOpacity: number; dt: number; } /** Segment coordinates for a continuous frame inside a keyframe window. */ export interface SplatWindowSegment { segment: number; u: number; } /** Decoded keyframe window fields consumed by the CPU motion evaluator. */ export interface KeyframeWindowInput { frameStart: number; knotStride: number; knotCount: number; dynamicCount: number; positionDeltas: ArrayLike; rotations?: ArrayLike | null; } /** Optional flat-top lifespan, expressed in timeline frames. */ export interface SplatLifespanInput { birth: number; death: number; ramp: number; } /** Static pose and dynamic-tail index for one keyframe-tracked Gaussian. */ export interface KeyframeGaussianInput { index: number; basePosition: SplatVector3Input; baseRotation: SplatQuaternionInput; baseOpacity?: number; lifespan?: SplatLifespanInput | null; } /** Evaluated keyframe Gaussian pose and lifespan weight at one frame. */ export interface KeyframeGaussianResult { position: SplatVector3; rotation: SplatQuaternion; opacity: number; weight: number; segment: number; u: number; } /** * Evaluate the cubic position polynomial. * * @param {ArrayLike} base - Base position [x, y, z] at dt = 0. * @param {ArrayLike} motion - 9 coefficients: [lin xyz, quad xyz, cubic xyz]. * @param {number} dt - Time relative to the Gaussian's TRBF center (time - trbfCenter). * @returns {number[]} Animated position [x, y, z]. */ export declare function evaluateSpacetimePosition(base: SplatVector3Input, motion: SpacetimeMotionCoefficients, dt: number): SplatVector3; /** * Evaluate the linear-velocity quaternion rotation and normalize it. * * @param {ArrayLike} rotStatic - Static quaternion [x, y, z, w] at dt = 0. * @param {ArrayLike} omega - Angular-velocity coefficients [x, y, z, w]. * @param {number} dt - Time relative to the TRBF center. * @returns {number[]} Normalized animated quaternion [x, y, z, w]. */ export declare function evaluateSpacetimeRotation(rotStatic: SplatQuaternionInput, omega: SplatQuaternionInput, dt: number): SplatQuaternion; /** * Evaluate the TRBF temporal opacity multiplier in [0, 1] (1 at the center, decaying with |dt|). * * @param {number} dt - Time relative to the TRBF center. * @param {number} trbfScale - The (already exponentiated) temporal width; must be non-zero. * @returns {number} Temporal opacity factor. */ export declare function evaluateSpacetimeTemporalOpacity(dt: number, trbfScale: number): number; /** * Evaluate a full Spacetime Gaussian at an absolute time. * * @param {Object} gaussian - { base:[x,y,z], motion:[9], rotation:[x,y,z,w], omega:[x,y,z,w], opacity, trbfCenter, trbfScale }. * @param {number} time - Absolute playback time (same units as trbfCenter). * @returns {{ position:number[], rotation:number[], opacity:number, temporalOpacity:number, dt:number }} */ export declare function evaluateSpacetimeGaussian(gaussian: SpacetimeGaussianInput, time: number): SpacetimeGaussianResult; /** * Map a continuous frame position to its Catmull-Rom segment inside a window. * * `segment = clamp(floor((frame - frameStart) / knotStride), 0, segments - 1)`, * `u = ((frame - frameStart) - segment·knotStride) / knotStride` clamped to [0, 1] — the * final window's grid pads past the clip's last frame with duplicated knots, so clamping u * reproduces the held pose instead of extrapolating. * * @param {number} frame - Continuous frame position (time · frameRate), window-absolute. * @param {number} frameStart - The window's first frame. * @param {number} knotStride - Frames between knots (K). * @param {number} segments - Segment count (knotCount - 3). * @returns {{ segment:number, u:number }} Segment index and its local parameter. */ export declare function evaluateWindowSegment(frame: number, frameStart: number, knotStride: number, segments: number): SplatWindowSegment; /** * Evaluate a uniform Catmull-Rom segment: `p(u)` through control points P1→P2 with tangents * `(P2-P0)/2` and `(P3-P1)/2` — C1 across shared knots (phantom knots supply P0/P3 at window * boundaries). * * @param {ArrayLike} p0 - Control point before the segment. * @param {ArrayLike} p1 - Segment start. * @param {ArrayLike} p2 - Segment end. * @param {ArrayLike} p3 - Control point after the segment. * @param {number} u - Local parameter in [0, 1]. * @returns {number[]} Interpolated [x, y, z]. */ export declare function evaluateCatmullRom(p0: SplatVector3Input, p1: SplatVector3Input, p2: SplatVector3Input, p3: SplatVector3Input, u: number): SplatVector3; /** * Hemisphere-aligned normalized quaternion lerp between two knots. The alignment (negate the * far knot when the dot product is negative) is MANDATORY: smallest-three storage canonicalizes * each knot independently, so adjacent decoded knots can be antipodal — plain nlerp would pass * near the zero quaternion and smear the covariance. * * @param {ArrayLike} q1 - Segment-start quaternion [x, y, z, w]. * @param {ArrayLike} q2 - Segment-end quaternion [x, y, z, w]. * @param {number} u - Local parameter in [0, 1]. * @returns {number[]} Normalized interpolated quaternion [x, y, z, w]. */ export declare function evaluateKeyframeRotation(q1: SplatQuaternionInput, q2: SplatQuaternionInput, u: number): SplatQuaternion; /** * Flat-top lifespan weight in [0, 1]: fully 1 over [birth, death], smoothstep ramps of * `ramp` frames outside, branch-free in the GPU twin. Unlike the TRBF this never fades a * persistent splat mid-life. * * @param {number} birth - First fully-visible frame. * @param {number} death - Last fully-visible frame. * @param {number} ramp - Ramp length in frames (0 ⇒ hard cut). * @param {number} frame - Continuous frame position. * @returns {number} Lifespan weight. */ export declare function evaluateFlatTopWeight(birth: number, death: number, ramp: number, frame: number): number; /** * Evaluate a keyframe-tracked Gaussian against a decoded window (SplatVideoCodec.decodeWindow * output) — the parity surface the GPU resolve pass is graded against. * * @param {Object} window - { frameStart, knotStride, knotCount, dynamicCount, positionDeltas, rotations } (knot-major). * @param {Object} splat - { index, basePosition:[x,y,z], baseRotation:[x,y,z,w], baseOpacity, lifespan:{birth,death,ramp}|null }. * @param {number} frame - Continuous frame position on the clip timeline. * @returns {{ position:number[], rotation:number[], opacity:number, weight:number, segment:number, u:number }} */ export declare function evaluateKeyframeGaussian(window: KeyframeWindowInput, splat: KeyframeGaussianInput, frame: number): KeyframeGaussianResult;