@penner/responsive-easing 0.0.3 → 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/fuse/back-defaults.ts","../src/fuse/bounce.ts","../src/fuse/spring.ts","../../fast-bezier-easing/dist/index.js","../src/lib/curves/power-back-math.ts","../src/fuse/powerBack.ts","../src/fuse/tails.ts","../src/lib/cachedGetter.ts","../src/fuse/rem/EasingRem.ts","../src/fuse/rem/SwimRem.ts","../src/fuse/heads.ts","../src/fuse/pulse.ts","../src/fuse/bezier-join-constraint.ts","../src/fuse/rem/PowerRem.ts","../src/fuse/core/constants.ts","../src/fuse/core/edge-cases.ts","../src/fuse/continuity/join-height.ts","../src/fuse/core/pulse-tail-bridge.ts","../src/fuse/core/cruise.ts","../src/fuse/core/spring-head-fallback.ts","../src/fuse/strategies/spring-to-spring.ts","../src/fuse/strategies/complementary-curve.ts","../src/fuse/composeFuse.ts","../src/fuse/rem/BackRem.ts","../src/fuse/rem/PowerBackRem.ts","../src/fuse/rem/BezierRem.ts","../src/fuse/rem/ExpoRem.ts","../src/fuse/rem/SpringRem.ts","../src/fuse/rem/BounceRem.ts","../src/fuse/rem/getEasingRem.ts","../src/fuse/fuse.ts","../src/fuse/index.ts"],"sourcesContent":["/**\n * Default Back easing functions.\n *\n * These represent the standard cubic Back easing (n=3) with the classic\n * Penner strength parameter of 1.70158, which produces ~10% overshoot.\n */\n\n/** Classic Penner Back easing strength parameter (~10% overshoot) */\nconst DEFAULT_STRENGTH = 1.70158;\nconst DEFAULT_STRENGTH_PLUS_1 = DEFAULT_STRENGTH + 1;\n\n/** Default ease-in Back: f(u) = (strength+1)u³ - strength·u² */\nexport const defaultEaseInBack = (u: number): number =>\n DEFAULT_STRENGTH_PLUS_1 * u * u * u - DEFAULT_STRENGTH * u * u;\n\n/** Default ease-in Back derivative: f'(u) = 3(strength+1)u² - 2·strength·u */\nexport const defaultEaseInBackDerivative = (u: number): number =>\n 3 * DEFAULT_STRENGTH_PLUS_1 * u * u - 2 * DEFAULT_STRENGTH * u;\n","/**\n * Hard-surface bounce pulse tail strategy\n *\n * Models parabolic bouncing physics with total amplitude decay over N bounces.\n * This is a pulse tail with net-zero displacement - it oscillates and settles back to the target.\n * Each bounce follows projectile motion with constant downward acceleration,\n * and velocity is reduced by a restitution coefficient at each impact.\n *\n * The decay parameter now represents the total decay from first to last bounce,\n * making it more intuitive: decay=90 means the last bounce is 10% of the first.\n *\n * For single bounce (N=1), decay directly scales the bounce energy:\n * - decay=0: full energy bounce\n * - decay=50: half energy bounce\n * - decay=100: no bounce (settles immediately)\n */\n\nimport type { TailStrategy, TailBuildParams, TailBuildResult } from './types';\nimport type { NormalizedProgress } from '../lib/types/units';\nimport { clamp01 } from '../lib/rem/core/utils';\nimport { FUSE_JOIN_MIN, FUSE_JOIN_MAX } from './index';\n\nimport {\n defaultEaseInBack,\n defaultEaseInBackDerivative,\n} from './back-defaults';\n\n/**\n * Hard-surface bounce pulse tail strategy\n *\n * Creates a bouncing effect where the tail follows parabolic trajectories,\n * simulating gravity-based bouncing off a hard surface. The amplitude\n * decreases by a total percentage over all bounces.\n *\n * This is a pulse tail with net-zero displacement - it oscillates around\n * the target position and settles back to it.\n *\n * Physics model:\n * - Each bounce is a parabola (projectile motion)\n * - Constant downward acceleration throughout\n * - Velocity reversal and reduction at each impact\n * - Total decay applied across all N bounces (not per bounce)\n */\nexport const BounceTail: TailStrategy = {\n // Public id uses the new 'bounce' name\n id: 'bounce',\n label: 'Bounce (hard-surface)',\n\n build({\n joinTime,\n bounces,\n decayPct,\n L = defaultEaseInBack,\n Ld = defaultEaseInBackDerivative,\n }: TailBuildParams): TailBuildResult {\n // Edge case: a ≈ 0 should be handled by fuse, but guard here too\n if (joinTime < FUSE_JOIN_MIN) {\n // No head phase - return linear easing\n const p = (u: number): number => clamp01(u);\n const v = (_u: number): number => 1;\n return {\n p,\n v,\n meta: { r: 0, finalPct: 0 },\n };\n }\n\n // Edge case: a ≈ 1 means no tail phase - return head only\n if (joinTime > FUSE_JOIN_MAX) {\n const p = (u: number): number => {\n if (u >= 1) return 1;\n return L(u);\n };\n const v = (u: number): number => {\n if (u >= 1) return 0;\n return Ld(u);\n };\n return {\n p,\n v,\n meta: { r: 0, finalPct: 0 },\n };\n }\n\n // Support fractional bounces: generate ceil(N) full parabolic segments,\n // then truncate the last one proportionally. This enables smooth slider dragging.\n const fullCount = Math.ceil(bounces);\n const fracPart = bounces - Math.floor(bounces);\n\n // Convert total decay percentage to per-bounce restitution coefficient\n // decayPct is now the total decay from first to last bounce\n // E.g., decayPct=90 means the last bounce is 10% of the first\n // Uses fractional bounces (not fullCount) so r varies continuously during dragging\n let r: NormalizedProgress;\n if (bounces <= 1) {\n // Single bounce - scale the bounce by decay percentage\n // decay=0% means full energy bounce, decay=100% means no bounce\n r = clamp01(1 - decayPct / 100) as NormalizedProgress;\n } else {\n const finalRatio: NormalizedProgress = clamp01(\n 1 - decayPct / 100,\n ) as NormalizedProgress;\n // Since velocity ratio r compounds to height ratio r²,\n // and we have N-1 decay steps between N bounces:\n // r^(2(N-1)) = finalRatio\n if (finalRatio === 0) {\n // 100% decay - completely inelastic\n r = 0;\n } else {\n r = Math.pow(finalRatio, 1 / (2 * (bounces - 1)));\n }\n }\n\n // Head position and velocity functions (scaled to [0, a])\n const leftPos = (u: number) => L(u / joinTime);\n const leftVel = (u: number) => Ld(u / joinTime) / joinTime;\n\n // Velocity at the join point (from the head)\n const vMinus = leftVel(joinTime);\n\n // First outgoing velocity magnitude (after initial \"impact\")\n const v0 = Math.abs(-r * vMinus);\n\n // Special case: if r=0 (100% decay), there's no bouncing - just settle immediately\n if (r === 0 || v0 === 0) {\n const p = (u: number): number => {\n if (u <= joinTime) return leftPos(u);\n return 1; // Settled at target\n };\n\n const v = (u: number): number => {\n if (u <= joinTime) return leftVel(u);\n return 0; // No velocity in tail\n };\n\n return {\n p,\n v,\n meta: {\n r: 0,\n finalPct: 0,\n g: 0,\n edges: [joinTime, 1],\n },\n };\n }\n\n // Available time for all bounces\n const T = 1 - joinTime;\n const floorCount = Math.floor(bounces);\n\n // Geometric series for the USED portion of bounces (including fractional last bounce).\n // This ensures the visible curve fills the entire tail time T — no flat region.\n // For integer bounces, this equals the standard geometric series.\n // For fractional (e.g., 2.5), it sums the first floor(N) full terms\n // plus fracPart of the next term, so gravity stretches the curve to fill T.\n const geom =\n Math.abs(1 - r) < 1e-6\n ? bounces\n : (1 - Math.pow(r, floorCount)) / (1 - r) +\n fracPart * Math.pow(r, floorCount);\n\n // Solve for acceleration that fits used bounces in time T\n // Total time = sum of flight times = (2*v0/g) * geom\n let g = ((2 * v0) / T) * geom;\n\n // Calculate the actual fall distance by finding minimum position in head\n // For heads with anticipation (like backIn), the minimum can be < 0\n // The bounce can't exceed the fall height: distance from min to target (1.0)\n let minHeadPos = 0;\n const sampleCount = 50; // Sample head to find minimum\n for (let i = 0; i <= sampleCount; i++) {\n const u = (i / sampleCount) * joinTime;\n const pos = leftPos(u);\n if (pos < minHeadPos) {\n minHeadPos = pos;\n }\n }\n\n // Maximum bounce height is the fall distance from lowest point to settled position\n // Fall distance = 1.0 - minHeadPos (if minHeadPos is negative, this is > 1.0)\n const maxAllowedHeight = 1.0 - minHeadPos;\n\n // Check if first bounce would exceed the physical fall distance\n // Maximum height is h_max = v0² / (2g)\n // This can happen when join point is very small (head compressed, high velocity)\n const theoreticalFirstBounce = (v0 * v0) / (2 * g);\n let v0_clamped = v0;\n let shouldClampHeight = false;\n let settleTime = 1; // Time when bounces finish (normally at u=1)\n\n if (theoreticalFirstBounce > maxAllowedHeight) {\n // Clamp the velocity to achieve max height equal to fall distance\n // From h = v² / (2g), we get v = sqrt(2*g*h)\n v0_clamped = Math.sqrt(2 * g * maxAllowedHeight);\n shouldClampHeight = true;\n\n // Recalculate geometric series with clamped velocity (using used portion)\n const geom_clamped =\n Math.abs(1 - r) < 1e-6\n ? bounces\n : (1 - Math.pow(r, floorCount)) / (1 - r) +\n fracPart * Math.pow(r, floorCount);\n\n // With clamped v0, bounces will finish earlier\n // Calculate actual time taken with clamped velocity\n const actualTimeTaken = (2 * v0_clamped * geom_clamped) / g;\n settleTime = joinTime + actualTimeTaken;\n\n // Ensure settleTime doesn't exceed 1.0\n if (settleTime > 1) {\n settleTime = 1;\n }\n }\n\n // Use clamped velocity for all subsequent calculations\n const v0_final = v0_clamped;\n\n // Calculate duration of each bounce segment\n // Flight time for bounce n: t_n = 2 * v_n / g = 2 * v0 * r^n / g\n const segDur = Array.from(\n { length: fullCount },\n (_, n) => (2 * v0_final * Math.pow(r, n)) / g,\n );\n\n // Calculate time boundaries for each segment\n const edges = [joinTime];\n segDur.forEach(dt => edges.push(edges[edges.length - 1] + dt));\n\n // If clamping occurred, bounces finish early at settleTime\n // Otherwise, ensure final edge is exactly 1\n if (shouldClampHeight) {\n edges[edges.length - 1] = settleTime;\n } else {\n edges[edges.length - 1] = 1;\n }\n\n // Velocity at start of bounce n (just after impact n-1)\n const vSeg = (n: number) => -v0_final * Math.pow(r, n);\n\n /**\n * Position function for the tail (right side, u > a)\n */\n const rightPos = (u: number): number => {\n // Past the settle time (from height clamping or fractional truncation), hold at 1.0\n if (u >= settleTime) {\n return 1;\n }\n\n // Find which bounce segment we're in\n let n = edges.findIndex(\n (e, i) => i < edges.length - 1 && u <= edges[i + 1],\n );\n if (n < 0) n = fullCount - 1; // Fallback to last segment\n\n // Time since start of this bounce\n const u0 = edges[n];\n const t = u - u0;\n\n // Parabolic motion: s = v*t + 0.5*g*t²\n // Position relative to the settled position (1.0)\n const s = vSeg(n) * t + 0.5 * g * t * t;\n return 1 + s;\n };\n\n /**\n * Velocity function for the tail\n */\n const rightVel = (u: number): number => {\n // Past the settle time (from height clamping or fractional truncation), velocity is 0\n if (u >= settleTime) {\n return 0;\n }\n\n // Find which bounce segment we're in\n let n = edges.findIndex(\n (e, i) => i < edges.length - 1 && u <= edges[i + 1],\n );\n if (n < 0) n = fullCount - 1;\n\n // Time since start of this bounce\n const u0 = edges[n];\n const t = u - u0;\n\n // Velocity: v = v0 + g*t\n return vSeg(n) + g * t;\n };\n\n /**\n * Combined easing function (head + tail)\n */\n const p = (u: number): number => {\n if (u <= joinTime) return leftPos(u);\n return rightPos(u);\n };\n\n /**\n * Combined velocity function (head + tail)\n */\n const v = (u: number): number => {\n if (u <= joinTime) return leftVel(u);\n return rightVel(u);\n };\n\n // Calculate final amplitude as percentage of initial\n // After N bounces with total decay, the final bounce height is:\n const finalPct = fullCount <= 1 ? 100 : 100 * (1 - decayPct / 100);\n\n return {\n p,\n v,\n meta: {\n r,\n finalPct,\n g,\n edges,\n clamped: shouldClampHeight,\n settleTime,\n },\n };\n },\n};\n","/**\n * Spring (phase-locked) pulse tail strategy\n *\n * Models spring-like oscillation with total amplitude decay over N half-cycles.\n * This is a pulse tail with net-zero displacement - it oscillates and settles back to the target.\n * Phase-locked at zero crossings for stable endpoint behavior.\n * Includes robustness against endpoint degeneracy.\n *\n * The decay parameter now represents the total decay from first to last peak,\n * making it more intuitive: decay=90 means the last peak is 10% of the first.\n *\n * For single cycle (N=1), decay directly scales the oscillation amplitude:\n * - decay=0: full amplitude oscillation\n * - decay=50: half amplitude oscillation\n * - decay=100: no oscillation (settles immediately)\n */\n\nimport type { TailStrategy, TailBuildParams, TailBuildResult } from './types';\nimport type { NormalizedProgress } from '../lib/types/units';\nimport { clamp01 } from '../lib/rem/core/utils';\nimport { FUSE_JOIN_MIN, FUSE_JOIN_MAX } from './index';\nimport { amplitudeRatio, dampingRate } from '@penner/easing';\n\n/**\n * Type for internal test helpers stored on globalThis\n * @internal\n */\ninterface SpringTestHelpers {\n calculateBridgeAmplitude?: () => number;\n calculateNonBridgeBaseline?: (y: number) => number;\n calculatePulsePosition?: (pulseY: number) => number;\n calculatePulseVelocity?: (pulseY: number) => number;\n calculateScalingFactor?: () => number;\n}\n\n/**\n * Extended globalThis with test helpers\n * @internal\n */\ninterface GlobalWithTestHelpers {\n __SPRING_TEST_HELPERS__?: SpringTestHelpers;\n}\n\nimport {\n defaultEaseInBack,\n defaultEaseInBackDerivative,\n} from './back-defaults';\n\n// ── Critically damped DHO (bounces ≤ 0) ─────────────────────────────\n//\n// General solution for critically damped DHO settling at target = 1:\n// position(τ) = 1 + (x₀ + B·τ) · e^(−ω₀·τ)\n// velocity(τ) = (B − ω₀·(x₀ + B·τ)) · e^(−ω₀·τ)\n// where x₀ = P − 1 (initial displacement from target)\n// B = v₀ + ω₀·x₀ (from initial conditions)\n// v₀ = head velocity in local τ time\n// ω₀ = 12 (gives ~99.992% settled at τ = 1)\n\n/** Fixed damping rate for critically damped spring */\nconst CRITICALLY_DAMPED_OMEGA = 12;\n\n/**\n * Build a critically damped (bounces ≤ 0) tail segment.\n *\n * Uses the general DHO solution with initial velocity from the head,\n * providing automatic C¹ continuity at the join point.\n */\nfunction buildCriticallyDamped({\n joinTime,\n L = defaultEaseInBack,\n Ld = defaultEaseInBackDerivative,\n bridge,\n}: Pick<TailBuildParams, 'joinTime' | 'L' | 'Ld' | 'bridge'>): TailBuildResult {\n const omega = CRITICALLY_DAMPED_OMEGA;\n\n // Phase boundaries\n const headEnd = bridge ? bridge.joinHeight : 1;\n const pulseStart = bridge ? joinTime + bridge.duration : joinTime;\n const alpha = 1 - pulseStart; // remaining timeline for DHO\n\n // Compute P (joinHeight) for head scaling\n // For bridge mode: P = bridge.joinHeight (already determined)\n // For non-bridge: we need to solve for P\n let P: number;\n\n if (bridge) {\n P = bridge.joinHeight;\n } else {\n // Non-bridge: head ends at P, tail starts at P with matching velocity.\n // The DHO adapts to any (P, v₀), so P is a free parameter.\n // Use the same C¹ formula as the oscillatory non-bridge case:\n // P · Ld(1) / joinTime = tailVelocity_at_start / (1 - joinTime)\n //\n // Tail velocity at τ=0: B − ω₀·x₀ = v₀ (by definition)\n // In global time: v₀ / (1 - joinTime)\n // Head velocity at join: P · Ld(1) / joinTime\n // These match by construction, so any P works for C¹.\n //\n // Choose P via the same formula used for oscillatory tails,\n // but with the DHO derivative at τ=0.\n // DHO at τ=0: position = 1 + x₀ = P, velocity (local) = B − ω₀·x₀ = v₀\n // where v₀ = P·Ld(1)·(1-joinTime)/joinTime\n //\n // For a natural-looking curve, we want P < 1 so the DHO has room to settle.\n // A good heuristic: use the head's natural endpoint.\n // P = Ld(1) * joinTime / (Ld(1) + omega * (1-joinTime)) ensures the DHO\n // doesn't overshoot excessively.\n const Ld1 = Ld(1);\n const denom = Ld1 * (1 - joinTime) + omega * joinTime;\n if (Math.abs(denom) < 1e-9) {\n P = 0.5; // fallback\n } else {\n P = (omega * joinTime) / denom;\n }\n // Clamp to reasonable range\n P = Math.max(0.01, Math.min(0.999, P));\n }\n\n // DHO initial conditions\n // Bridge mode: DHO starts at 1.0 (bridge endpoint) with bridge velocity\n // Non-bridge: DHO starts at P with head velocity\n const dhoStart = bridge ? 1.0 : P;\n const x0 = dhoStart - 1; // displacement from target\n const v0_local = bridge\n ? bridge.velocity * alpha // bridge velocity scaled to τ time\n : (P * Ld(1) * (1 - joinTime)) / joinTime; // head velocity in global, scaled to τ\n const B_coeff = v0_local + omega * x0;\n\n // DHO position in local τ ∈ [0, 1]\n const dhoPosition = (tau: number): number => {\n if (tau <= 0) return dhoStart;\n if (tau >= 1) return 1; // clamped endpoint\n const decay = Math.exp(-omega * tau);\n return 1 + (x0 + B_coeff * tau) * decay;\n };\n\n // DHO velocity in local τ\n const dhoVelocity = (tau: number): number => {\n if (tau <= 0) return v0_local;\n if (tau >= 1) return 0;\n const decay = Math.exp(-omega * tau);\n return (B_coeff - omega * (x0 + B_coeff * tau)) * decay;\n };\n\n // Head position and velocity\n const headP = bridge ? bridge.joinHeight : P;\n const Lin = (u: number) => headP * L(u / joinTime);\n const LinPrime = (u: number) => (headP / joinTime) * Ld(u / joinTime);\n\n const p = (u: number): number => {\n if (u <= 0) return 0;\n if (u >= 1) return 1;\n if (u <= joinTime) return Lin(u);\n if (bridge && u <= pulseStart) {\n return bridge.joinHeight + bridge.velocity * (u - joinTime);\n }\n const tau = (u - pulseStart) / alpha;\n return dhoPosition(tau);\n };\n\n const v = (u: number): number => {\n if (u <= 0 || u >= 1) return 0;\n if (u <= joinTime) return LinPrime(u);\n if (bridge && u <= pulseStart) {\n return bridge.velocity;\n }\n const tau = (u - pulseStart) / alpha;\n return dhoVelocity(tau) / alpha; // chain rule: d/du = (1/alpha) · d/dτ\n };\n\n return {\n p,\n v,\n meta: {\n r: 0,\n finalPct: 0,\n k: omega,\n B: 0,\n P,\n guard: 'critically-damped',\n useBridge: !!bridge,\n },\n };\n}\n\n// ═══════════════════════════════════════════════════════════════════════\n// Non-bridge spring tail strategies\n//\n// These functions build the spring tail when no bridge is provided.\n// The active strategy is selected by the dispatch in build() below.\n// ═══════════════════════════════════════════════════════════════════════\n\n/**\n * Shared setup for non-bridge spring tail strategies.\n * Computes P (joinHeight), v₀, and DHO physics parameters.\n */\nexport function setupNonBridgeTail(\n joinTime: number,\n bounces: number,\n decayPct: number,\n Ld: (u: number) => number,\n) {\n const totalDecay = clamp01(decayPct / 100);\n const Nbase = bounces + 0.5;\n const r = amplitudeRatio(totalDecay, Nbase);\n const N = Nbase;\n const omegaD = Math.PI * N;\n const gamma = dampingRate(N, r);\n\n const alpha = 1 - joinTime;\n const isTailOnly = joinTime <= FUSE_JOIN_MIN * 10; // ~0.001\n\n // Scale to fit N half-cycles in tail duration α\n const w = alpha > 0 ? omegaD / alpha : omegaD;\n const g = alpha > 0 ? gamma / alpha : gamma;\n\n const Ld1 = Ld(1);\n let P: number;\n if (isTailOnly) {\n P = 0;\n } else {\n const headSlope = Ld1 / joinTime;\n const tailFreq = omegaD;\n const denom = headSlope * (1 - joinTime) + tailFreq * joinTime;\n P = Math.abs(denom) < 1e-9 ? 0.5 : (tailFreq * joinTime) / denom;\n P = Math.max(0.01, Math.min(0.999, P));\n }\n\n const v0 = isTailOnly ? 0 : (P * Ld1) / joinTime;\n const finalPct = bounces <= 1 ? 100 : 100 * (1 - decayPct / 100);\n\n return { P, v0, r, N, w, g, alpha, finalPct };\n}\n\n// ── Strategy A: General underdamped DHO (cos + sin) ─────────────────\n//\n// Uses the exact DHO solution with two coefficients (A, C) to encode\n// both initial position and velocity. Provides automatic C¹ continuity.\n//\n// Pros: Physically exact, automatic velocity matching, unified formula.\n// Cons: Endpoint doesn't land on a zero-crossing (sin term residual\n// proportional to initial velocity). Phase correction would fix\n// the endpoint but shifts the frequency, changing visible bounce count.\n//\n// Formula:\n// position(t) = 1 + e^(-g·t) · [A·cos(w·t) + C·sin(w·t)]\n// A = P − 1, C = (v₀ + g·A) / w\n\nfunction buildGeneralDHO({\n joinTime,\n bounces,\n decayPct,\n L = defaultEaseInBack,\n Ld = defaultEaseInBackDerivative,\n}: Omit<TailBuildParams, 'bridge'>): TailBuildResult {\n const { P, v0, r, w, g, alpha, finalPct } = setupNonBridgeTail(\n joinTime, bounces, decayPct, Ld,\n );\n\n const A = P - 1;\n const C = w > 0 ? (v0 + g * A) / w : 0;\n\n const cosCoeff = C * w - g * A;\n const sinCoeff = -(A * w + g * C);\n\n const dhoPosition = (tau: number): number => {\n if (tau <= 0) return P;\n const t = tau * alpha;\n const envelope = Math.exp(-g * t);\n return 1 + envelope * (A * Math.cos(w * t) + C * Math.sin(w * t));\n };\n\n const dhoVelocity = (tau: number): number => {\n if (tau <= 0) return v0;\n if (tau >= 1) return 0;\n const t = tau * alpha;\n const envelope = Math.exp(-g * t);\n return envelope * (cosCoeff * Math.cos(w * t) + sinCoeff * Math.sin(w * t));\n };\n\n const Lin = (u: number) => P * L(u / joinTime);\n const LinPrime = (u: number) => (P / joinTime) * Ld(u / joinTime);\n\n const p = (u: number): number => {\n if (u <= 0) return 0;\n if (u >= 1) return 1;\n if (u <= joinTime) return Lin(u);\n return dhoPosition((u - joinTime) / alpha);\n };\n\n const v = (u: number): number => {\n if (u <= 0 || u >= 1) return 0;\n if (u <= joinTime) return LinPrime(u);\n return dhoVelocity((u - joinTime) / alpha);\n };\n\n return {\n p, v,\n meta: { r, finalPct, k: g, B: w, P, guard: 'general-dho' },\n };\n}\n\n// ── Strategy B: Settling curve + phase-locked sine ──────────────────\n//\n// Separates the tail into two superimposed components:\n// 1. A critically damped settling curve (P → 1.0) that absorbs the\n// head's velocity and provides C¹ continuity.\n// 2. A phase-locked damped sine oscillation that starts and ends at\n// zero-crossings, giving a clean endpoint and predictable bounce count.\n//\n// Pros: Phase-locked endpoints (no residual), stable bounce count,\n// smooth curvature (no linear bridge).\n// Cons: Two superimposed curves; amplitude of sine must be tuned\n// relative to the settling displacement.\n//\n// Formula:\n// position(t) = settle(t) + amplitude · e^(-g·t) · sin(w·t)\n// settle(t) = 1 + (P − 1 + B_s·t) · e^(-ω₀·t) [critically damped DHO]\n// amplitude = (1 − P) [scales with displacement]\n\nfunction buildSettlingSine({\n joinTime,\n bounces,\n decayPct,\n L = defaultEaseInBack,\n Ld = defaultEaseInBackDerivative,\n}: Omit<TailBuildParams, 'bridge'>): TailBuildResult {\n const { P, v0, r, w, g, alpha, finalPct } = setupNonBridgeTail(\n joinTime, bounces, decayPct, Ld,\n );\n\n // Oscillation amplitude scales with displacement from target\n const amp = 1 - P;\n\n // ── Settling curve (critically damped DHO: P → 1.0) ──\n // Uses a fixed ω₀ that settles well within the tail duration.\n // The settling curve must absorb the head's velocity MINUS the sine's\n // initial velocity contribution (amp·w from sin'(0)=w), so that\n // settleVel(0) + sineVel(0) = v0 for C¹ continuity.\n const omega0 = CRITICALLY_DAMPED_OMEGA / alpha; // scale to tail duration\n const x0 = P - 1;\n const sineV0 = amp * w; // sine derivative at t=0: amp · w · cos(0)\n const settleV0 = v0 - sineV0; // settling curve absorbs the remainder\n const B_s = settleV0 + omega0 * x0; // from initial conditions\n\n const settlePos = (t: number): number =>\n 1 + (x0 + B_s * t) * Math.exp(-omega0 * t);\n\n const settleVel = (t: number): number => {\n const env = Math.exp(-omega0 * t);\n return (B_s - omega0 * (x0 + B_s * t)) * env;\n };\n\n // ── Phase-locked sine oscillation ──\n // sin(0) = 0 and sin(w·α) = sin(π·N) ≈ 0 for N = bounces + 0.5\n // → oscillation starts and ends on zero-crossings.\n // Amplitude (amp, declared above) scales with displacement (1 − P).\n\n // Precompute sine oscillation derivative coefficients\n // d/dt [amp · e^(-g·t) · sin(w·t)] = amp · e^(-g·t) · [w·cos(w·t) − g·sin(w·t)]\n const sinePos = (t: number): number =>\n amp * Math.exp(-g * t) * Math.sin(w * t);\n\n const sineVel = (t: number): number =>\n amp * Math.exp(-g * t) * (w * Math.cos(w * t) - g * Math.sin(w * t));\n\n // ── Combined ──\n const tailPos = (t: number): number => settlePos(t) + sinePos(t);\n const tailVel = (t: number): number => settleVel(t) + sineVel(t);\n\n // Head\n const Lin = (u: number) => P * L(u / joinTime);\n const LinPrime = (u: number) => (P / joinTime) * Ld(u / joinTime);\n\n const p = (u: number): number => {\n if (u <= 0) return 0;\n if (u >= 1) return 1;\n if (u <= joinTime) return Lin(u);\n const t = (u - joinTime); // real tail time\n return tailPos(t);\n };\n\n const v = (u: number): number => {\n if (u <= 0 || u >= 1) return 0;\n if (u <= joinTime) return LinPrime(u);\n const t = (u - joinTime);\n return tailVel(t);\n };\n\n return {\n p, v,\n meta: { r, finalPct, k: g, B: w, P, guard: 'settling-sine' },\n };\n}\n\n// ── Strategy E: Quadratic bridge + phase-locked sine ────────────────\n//\n// Three-phase tail: quadratic (parabolic) bridge → phase-locked damped sine.\n//\n// The quadratic bridge matches C¹ at the head (position + velocity) and\n// C⁰ at the oscillation (position only). It has constant acceleration\n// (no inflection points), producing a smooth parabolic arc.\n//\n// The bridge's exit velocity determines the oscillation amplitude:\n// amplitude = bridgeExitVelocity / w_osc\n// This naturally negotiates between the head's velocity and the oscillation.\n//\n// Quadratic: p(t) = ½at² + v₀t + P\n// a = 2(1 - P - v₀·d) / d² (from endpoint constraint p(d) = 1.0)\n//\n// Pros: C¹ at head, smooth constant-acceleration curve, no S-shape inflection,\n// bridge exit velocity naturally scales oscillation amplitude.\n// Cons: C⁰ (not C¹) at bridge→oscillation boundary. Velocity gap there is\n// typically small since the bridge transitions near a zero-crossing.\n\nfunction buildQuadraticSine({\n joinTime,\n bounces,\n decayPct,\n L = defaultEaseInBack,\n Ld = defaultEaseInBackDerivative,\n}: Omit<TailBuildParams, 'bridge'>): TailBuildResult {\n // Get physics parameters from shared setup.\n // We override P and v0 below — setupNonBridgeTail's P formula is designed\n // for Strategy A (direct DHO where amplitude = 1−P), not for Strategy E\n // (quadratic bridge + sine) where the bridge handles the transition from\n // P to 1.0 independently of the oscillation amplitude.\n const setup = setupNonBridgeTail(joinTime, bounces, decayPct, Ld);\n const { r, g, alpha, finalPct } = setup;\n\n const Ld1 = Ld(1);\n\n // ── P for the quadratic bridge strategy ──\n //\n // Two constraints determine P:\n //\n // 1. Bridge duration target: choose P so dLinear = (1−P)/v₀ ≈ d_target\n // (~1/8 of a half-cycle, capped at 10% of the tail). Higher P → shorter\n // bridge → more oscillation time → less visible C² kink.\n // Formula: P = j / (j + Ld₁·d_target).\n //\n // 2. Amplitude cap: the oscillation amplitude ≈ P·Ld₁·α / (j·bounces·π).\n // When bounces is low or joinTime is small, the head velocity can be\n // very high relative to the oscillation frequency, creating excessive\n // overshoot. Cap P so amplitude stays ≤ ampMax.\n // Formula: P ≤ ampMax·j·bounces·π / (Ld₁·α).\n //\n // For backward head velocity (Ld₁ ≤ 0), fall back to setupNonBridgeTail's\n // formula which balances head slope against tail frequency for the\n // dip-and-recovery bridge shape.\n let P: number;\n if (Ld1 > 1e-6) {\n const dTarget = Math.min(\n alpha / (8 * Math.max(bounces, 1)),\n alpha * 0.1,\n );\n const P_bridge = joinTime / (joinTime + Ld1 * dTarget);\n\n const ampMax = 0.5;\n const P_amp = bounces > 0\n ? (ampMax * joinTime * bounces * Math.PI) / (Ld1 * alpha)\n : 0.5;\n\n P = Math.min(P_bridge, P_amp);\n P = Math.max(0.01, Math.min(0.999, P));\n } else {\n P = setup.P;\n }\n const v0 = (P * Ld1) / joinTime;\n\n // ── Bridge duration (dynamic) ──\n //\n // For v₀ > 0 (head moving forward):\n // d_linear = (1-P)/v₀ — time to coast from P to 1.0 at constant v₀.\n // Using d ≥ d_linear ensures the bridge decelerates or coasts (a ≤ 0),\n // preventing zig-zag artifacts from opposing acceleration.\n //\n // For v₀ ≤ 0 (head moving backward, e.g., some Bezier curves):\n // The bridge must dip then recover — a parabolic arc that honors the\n // negative velocity for C¹ continuity. Use the quarter-cycle duration\n // to give the quadratic room for the dip-and-recovery.\n //\n // d_quarter: one quarter-cycle = α/(2·bounces+1), a natural proportion.\n // d_max: cap at half the tail to leave room for oscillation.\n const dQuarter = bounces > 0 ? alpha / (2 * bounces + 1) : alpha;\n const dMax = bounces > 0 ? alpha * 0.5 : alpha;\n const dMin = 1e-6;\n\n let d: number;\n if (v0 > 1e-6) {\n // Forward velocity: use coast time, capped\n const dLinear = (1 - P) / v0;\n d = Math.max(dMin, Math.min(dMax, dLinear));\n } else {\n // Backward or zero velocity: use quarter-cycle for dip-and-recovery\n d = Math.max(dMin, Math.min(dMax, dQuarter));\n }\n\n // ── Quadratic bridge: p(t) = ½at² + v₀t + P ──\n // Constraints: p(0) = P, p'(0) = v₀, p(d) = 1.0\n // Solving for a: ½a·d² + v₀·d + P = 1 → a = 2(1 - P - v₀·d) / d²\n const accel = d > 0 ? 2 * (1 - P - v0 * d) / (d * d) : 0;\n\n const bridgePos = (t: number): number =>\n 0.5 * accel * t * t + v0 * t + P;\n\n const bridgeVel = (t: number): number =>\n accel * t + v0;\n\n // Bridge exit velocity → determines oscillation amplitude\n const vBridgeEnd = accel * d + v0;\n\n // ── Oscillation parameters ──\n const alphaOsc = alpha - d;\n const wOsc = alphaOsc > 0 ? bounces * Math.PI / alphaOsc : 0;\n\n // Amplitude derived from bridge exit velocity:\n // osc'(0) = amp · w_osc, so amp = vBridgeEnd / w_osc\n const amp = wOsc > 0 ? vBridgeEnd / wOsc : 0;\n\n // ── Phase-locked damped sine oscillation ──\n const oscPos = (t: number): number =>\n 1 + amp * Math.exp(-g * t) * Math.sin(wOsc * t);\n\n const oscVel = (t: number): number =>\n amp * Math.exp(-g * t) * (wOsc * Math.cos(wOsc * t) - g * Math.sin(wOsc * t));\n\n // ── Head ──\n const Lin = (u: number) => P * L(u / joinTime);\n const LinPrime = (u: number) => (P / joinTime) * Ld(u / joinTime);\n\n // ── Combined ──\n const bridgeEnd = joinTime + d;\n\n const p = (u: number): number => {\n if (u <= 0) return 0;\n if (u >= 1) return 1;\n if (u <= joinTime) return Lin(u);\n if (u <= bridgeEnd) {\n const t = u - joinTime;\n return bridgePos(t);\n }\n const t = u - bridgeEnd;\n return oscPos(t);\n };\n\n const v = (u: number): number => {\n if (u <= 0 || u >= 1) return 0;\n if (u <= joinTime) return LinPrime(u);\n if (u <= bridgeEnd) {\n const t = u - joinTime;\n return bridgeVel(t);\n }\n const t = u - bridgeEnd;\n return oscVel(t);\n };\n\n return {\n p, v,\n meta: { r, finalPct, k: g, B: wOsc, P, guard: 'quadratic-sine' },\n };\n}\n\n// ── Active strategy selection ───────────────────────────────────────\n// Change this to swap between approaches:\n// buildGeneralDHO — Strategy A: exact DHO, automatic C¹, endpoint residual\n// buildSettlingSine — Strategy B: settling DHO + sine superposition (experimental)\n// buildHermiteSine — Strategy D: Hermite bridge + phase-locked sine (in spring-hermite-bridge.ts)\n// buildQuadraticSine — Strategy E: quadratic bridge + phase-locked sine\nconst buildUnderdampedTail = buildQuadraticSine;\n\n/**\n * Spring phase-locked pulse tail strategy\n *\n * Creates a spring-like oscillation where the tail follows a damped sinusoid\n * that's phase-locked at the START to ensure C¹ continuity with the head.\n * The amplitude decreases by a total percentage over all half-cycles.\n *\n * This is a pulse tail with net-zero displacement - it oscillates around\n * the target position and settles back to it.\n *\n * Physics model:\n * - Damped harmonic oscillator: A*e^(-kt)*sin(Bt)\n * - START-LOCKED: Sine wave always begins at zero-crossing (sin(0) = 0)\n * - Phase-locked so that N half-cycles fit exactly in the remaining time\n * - Total decay applied across all N half-cycles (not per half-cycle)\n *\n * The start-locking ensures that the oscillation begins at exactly zero,\n * maintaining smooth C¹ continuity at the join point even with fractional\n * bounce counts. For integer bounces, the endpoint also lands on a zero-crossing.\n * For fractional bounces, the endpoint may be slightly off zero but is visually\n * acceptable during parameter adjustment (and snaps to integer on release).\n */\nexport const SpringZeroLockedTail: TailStrategy = {\n // Public id uses the new 'spring' name\n id: 'spring',\n label: 'Spring (phase-locked; bounce = half-cycle)',\n\n /**\n * Calculate the natural starting velocity for spring oscillations\n *\n * This is adaptive, blending the head's ending velocity with spring frequency\n * to produce visually balanced bridges that respond to both the head's characteristics\n * and the spring's oscillation rate.\n *\n * Strategy: Equal weight to head velocity and frequency (both at 1.0 by default)\n * Formula: headVelocity * 1.0 + 1.0 * N^1.0 * 1.0 = headVelocity + N\n */\n getNaturalStartVelocity(\n { bounces, decayPct }: { bounces: number; decayPct: number },\n headVelocityFn: (u: number) => number,\n tuning?: {\n headWeight?: number;\n freqWeight?: number;\n freqExponent?: number;\n baseMultiplier?: number;\n },\n ): number {\n const N = bounces; // Use N internally for math notation\n\n // Critically damped: DHO accepts any initial velocity, but there's\n // no oscillation frequency to drive the bridge. Return just the head\n // velocity so bridge calculation degrades gracefully.\n if (N <= 0) {\n return headVelocityFn(1);\n }\n\n // Calculate per-half-cycle amplitude ratio (same as in build)\n let r: NormalizedProgress;\n if (N <= 1) {\n r = clamp01(1 - decayPct / 100) as NormalizedProgress;\n } else {\n const finalRatio: NormalizedProgress = clamp01(\n 1 - decayPct / 100,\n ) as NormalizedProgress;\n if (finalRatio === 0) {\n r = 0;\n } else {\n r = Math.pow(finalRatio, 1 / (N - 1));\n }\n }\n\n // If r=0, there's no oscillation - return zero velocity\n if (r === 0) return 0;\n\n // Get the head's ending velocity - this reflects overshoot/anticipation\n const headVelocity = headVelocityFn(1);\n\n // Apply tuning parameters with defaults\n const headWeight = tuning?.headWeight ?? 1.0;\n const freqWeight = tuning?.freqWeight ?? 1.0;\n const freqExponent = tuning?.freqExponent ?? 1.0;\n const baseMultiplier = tuning?.baseMultiplier ?? 1.0;\n\n // Frequency factor: grows faster than sqrt(N) for small N\n // N=1 → 1.0, N=2 → 1.62, N=4 → 2.64, N=8 → 5.28\n const frequencyFactor = Math.pow(N, freqExponent);\n\n // Blend head velocity and frequency influence\n // Formula: headVelocity * headWeight + baseMultiplier * frequencyFactor * freqWeight\n return (\n headVelocity * headWeight + baseMultiplier * frequencyFactor * freqWeight\n );\n },\n\n build({\n joinTime,\n bounces,\n decayPct,\n L = defaultEaseInBack,\n Ld = defaultEaseInBackDerivative,\n bridge,\n }: TailBuildParams): TailBuildResult {\n // Edge case: a ≈ 0 should be handled by fuse, but guard here too\n if (joinTime < FUSE_JOIN_MIN) {\n // No head phase - return linear easing\n const p = (u: number): number => clamp01(u);\n const v = (_u: number): number => 1;\n return {\n p,\n v,\n meta: {\n r: 0,\n finalPct: 0,\n k: 0,\n B: 0,\n P: 0,\n },\n };\n }\n\n // Edge case: a ≈ 1 means no tail phase - return head only\n if (joinTime > FUSE_JOIN_MAX) {\n const p = (u: number): number => {\n if (u >= 1) return 1;\n return L(u);\n };\n const v = (u: number): number => {\n if (u >= 1) return 0;\n return Ld(u);\n };\n return {\n p,\n v,\n meta: {\n r: 0,\n finalPct: 0,\n k: 0,\n B: 0,\n P: 1,\n },\n };\n }\n\n // ── Critically damped (bounces ≤ 0) ──────────────────────────────────\n // General critically damped DHO settling at target = 1:\n // position(τ) = 1 + (x₀ + (v₀ + ω₀·x₀)·τ) · e^(−ω₀·τ)\n // where x₀ = P − 1 (initial displacement from target),\n // v₀ = head velocity in local τ time,\n // ω₀ = 12 (damping rate, ~99.992% settled at τ = 1).\n // Decay is ignored — no oscillation peaks to decay between.\n if (bounces <= 0) {\n return buildCriticallyDamped({ joinTime, L, Ld, bridge });\n }\n\n // ── Underdamped DHO with general initial conditions ────────────────\n // When no bridge is provided, use the unified DHO formula that\n // naturally handles any starting position/velocity from the head.\n // This replaces the phase-locked sine + linear baseline drift.\n if (!bridge) {\n return buildUnderdampedTail({ joinTime, bounces, decayPct, L, Ld });\n }\n\n // Convert total decay percentage to per-half-cycle amplitude ratio\n // decayPct is now the total decay from first to last peak\n // E.g., decayPct=90 means the last peak is 10% of the first\n let r: NormalizedProgress;\n if (bounces <= 1) {\n // Single half-cycle - scale the oscillation by decay percentage\n // decay=0% means full amplitude, decay=100% means no oscillation\n r = clamp01(1 - decayPct / 100) as NormalizedProgress;\n } else {\n const finalRatio: NormalizedProgress = clamp01(\n 1 - decayPct / 100,\n ) as NormalizedProgress;\n // Per half-cycle ratio compounds over N-1 steps:\n // r^(N-1) = finalRatio\n if (finalRatio === 0) {\n // 100% decay - completely damped\n r = 0;\n } else {\n r = Math.pow(finalRatio, 1 / (bounces - 1));\n }\n }\n\n // Determine phase boundaries based on bridge mode\n const headEnd = bridge ? bridge.joinHeight : 1; // Head ends at joinHeight (bridge) or 1 (no bridge)\n const pulseStart = bridge ? joinTime + bridge.duration : joinTime; // Pulse starts after bridge or at join\n\n // Special case: if r=0 (100% decay), there's no oscillation - just settle immediately\n if (r === 0) {\n // Head position and velocity functions\n const Lin = (u: number) => headEnd * L(u / joinTime);\n const LinPrime = (u: number) => (headEnd / joinTime) * Ld(u / joinTime);\n\n const p = (u: number): number => {\n if (u <= joinTime) return Lin(u);\n if (bridge && u <= pulseStart) {\n // Bridge phase: constant velocity line\n return bridge.joinHeight + bridge.velocity * (u - joinTime);\n }\n return 1; // Settled at target\n };\n\n const v = (u: number): number => {\n if (u <= joinTime) return LinPrime(u);\n if (bridge && u <= pulseStart) {\n return bridge.velocity;\n }\n return 0; // No velocity in tail\n };\n\n return {\n p,\n v,\n meta: {\n r: 0,\n finalPct: 0,\n k: Infinity,\n B: 0,\n P: headEnd,\n guard: 'no-oscillation',\n useBridge: !!bridge,\n },\n };\n }\n\n // Remaining timeline after join (and bridge, if any)\n const alpha = 1 - pulseStart;\n\n // Target: N half-cycles across alpha → base angular frequency\n // No phase perturbation - let the sine naturally start at zero\n const B = bounces <= 0 || alpha <= 0 ? 0 : (bounces * Math.PI) / alpha;\n\n // Map y∈[0,1] to x∈[0,alpha] with oscillation starting at zero\n // Decay rate k is computed to give amplitude ratio r per half-cycle\n const k = r <= 0 ? 50 : (B / Math.PI) * -Math.log(r);\n\n /**\n * Oscillation function:\n * - E_sin: e^(-kx) * sin(Bx) - starts at zero\n */\n const E_sin = (x: number): number => Math.exp(-k * x) * Math.sin(B * x);\n\n /**\n * Derivative\n */\n const Ed_sin = (x: number): number =>\n Math.exp(-k * x) * (B * Math.cos(B * x) - k * Math.sin(B * x));\n\n // Track any robustness guards applied\n let guardMessage = '';\n\n /**\n * Normalized oscillation functions (y ∈ [0,1])\n *\n * Using sine-based oscillation for both bridge and non-bridge cases:\n * - R_sin: starts at zero (sin(0)=0), oscillates ±1 around 0\n * - For bridge: amplitude chosen to match bridge velocity\n * - For non-bridge: doubled amplitude to make oscillation reach above 1.0\n *\n * Normalization by FIRST PEAK amplitude for consistent scaling:\n * - First peak for sin: at x = π/(2B) where sin(Bx) = 1\n */\n const firstPeakX_sin = Math.PI / (2 * B);\n const firstPeakAmplitude_sin = Math.exp(-k * firstPeakX_sin);\n const denom_sin =\n firstPeakAmplitude_sin > 1e-9 ? firstPeakAmplitude_sin : 1.0;\n\n if (firstPeakAmplitude_sin <= 1e-9) {\n guardMessage = 'high-decay-fallback';\n }\n\n // Sine-based oscillation: starts at zero\n const R_sin = (y: number): number => E_sin(alpha * y) / denom_sin;\n const Rd_sin = (y: number): number =>\n (alpha * Ed_sin(alpha * y)) / denom_sin;\n\n /**\n * Calculate oscillation amplitude for bridge mode\n *\n * For C¹ continuity, the pulse must start with velocity matching the head's ending velocity.\n * When joinHeight is clamped near 1.0, the head gets horizontally scaled,\n * changing its slope: v_head_end = (joinHeight / joinTime) * Ld(1)\n *\n * @internal Exported only for testing purposes\n */\n const calculateBridgeAmplitude = (): number => {\n if (!bridge || B === 0) return 0;\n const headEndVelocity = (bridge.joinHeight / joinTime) * Ld(1);\n return (headEndVelocity * denom_sin) / B;\n };\n\n /**\n * Calculate baseline position for non-bridge mode\n *\n * The baseline drifts linearly from P (at y=0) to 1.0 (at y=1)\n *\n * @internal Exported only for testing purposes\n */\n const calculateNonBridgeBaseline = (y: number): number => {\n return P + (1 - P) * y;\n };\n\n /**\n * Calculate pulse phase position\n *\n * @param pulseY - Normalized time in pulse phase (0 to 1)\n * @returns Position value\n *\n * @internal Exported only for testing purposes\n */\n const calculatePulsePosition = (pulseY: number): number => {\n if (bridge) {\n // Bridge case: oscillate starting from position 1 (where bridge ended)\n const amplitude = calculateBridgeAmplitude();\n return 1 + amplitude * R_sin(pulseY);\n } else {\n // No bridge: use sine with linear baseline shift\n // Baseline drifts from P (at y=0) to 1.0 (at y=1)\n // Combined with oscillation around this drifting baseline\n // Oscillation amplitude: (1-P) to make peaks reach reasonably above baseline\n const baseline = calculateNonBridgeBaseline(pulseY);\n return baseline + (1 - P) * R_sin(pulseY);\n }\n };\n\n /**\n * Calculate pulse phase velocity\n *\n * @param pulseY - Normalized time in pulse phase (0 to 1)\n * @returns Velocity value\n *\n * @internal Exported only for testing purposes\n */\n const calculatePulseVelocity = (pulseY: number): number => {\n if (bridge) {\n // Bridge case: velocity matches actual head ending velocity\n const amplitude = calculateBridgeAmplitude();\n return (amplitude / (1 - pulseStart)) * Rd_sin(pulseY);\n } else {\n // No bridge: baseline drift + oscillation\n // velocity = (1-P) * [1 + Rd_sin(y)] / (1 - joinTime)\n return ((1 - P) * (1 + Rd_sin(pulseY))) / (1 - pulseStart);\n }\n };\n\n /**\n * Calculate C¹ scaling factor P\n *\n * For bridge mode: P is derived from matching pulse velocity to bridge velocity\n * For non-bridge mode: P is derived from matching head and tail velocities at join\n *\n * @internal Exported only for testing purposes\n */\n const calculateScalingFactor = (): number => {\n if (bridge) {\n // In bridge mode:\n // - Head ends at joinHeight\n // - Bridge goes from joinHeight to 1\n // - Pulse starts at position 1 with velocity = bridge.velocity\n //\n // Pulse velocity at start: ((1 - P) / (1 - pulseStart)) * Rd(0) = bridge.velocity\n // Solve for P:\n // (1 - P) = bridge.velocity * (1 - pulseStart) / Rd(0)\n // P = 1 - bridge.velocity * (1 - pulseStart) / Rd(0)\n\n const Rd0 = Rd_sin(0); // Derivative of normalized pulse at its start (using sine)\n const alpha = 1 - pulseStart;\n\n if (Math.abs(Rd0) < 1e-9) {\n // Edge case: pulse has zero initial velocity\n // Fall back to simple scaling\n return headEnd;\n } else {\n return 1 - (bridge.velocity * alpha) / Rd0;\n }\n } else {\n // Non-bridge case: C¹ scaling with sine-based oscillation + linear baseline drift\n // Position: baseline(y) + (1-P)*R_sin(y) where baseline(y) = P + (1-P)*y\n // Velocity: (1-P) * [1 + Rd_sin(y)] / (1-joinTime)\n //\n // At join point (y=0):\n // Head velocity: P * Ld(1) / joinTime\n // Tail velocity: (1-P) * [1 + Rd_sin(0)] / (1-joinTime)\n //\n // Setting equal for C¹ continuity:\n // P * Ld(1) / joinTime = (1-P) * [1 + Rd_sin(0)] / (1-joinTime)\n //\n // Solving for P:\n // P * Ld(1) * (1-joinTime) = (1-P) * [1 + Rd_sin(0)] * joinTime\n // P * Ld(1) * (1-joinTime) = [1 + Rd_sin(0)] * joinTime - P * [1 + Rd_sin(0)] * joinTime\n // P * [Ld(1)*(1-joinTime) + (1 + Rd_sin(0))*joinTime] = [1 + Rd_sin(0)] * joinTime\n\n const Ld1 = Ld(1);\n const Rd0_sin = Rd_sin(0);\n const factor = 1 + Rd0_sin;\n\n if (Math.abs(Ld1 * (1 - joinTime) + factor * joinTime) < 1e-9) {\n // Edge case: denominator near zero\n return 0.5;\n } else {\n return (\n (factor * joinTime) / (Ld1 * (1 - joinTime) + factor * joinTime)\n );\n }\n }\n };\n\n // C¹ scaling at the join point (or bridge end if using bridge)\n const P = calculateScalingFactor();\n\n // Store helper functions for testing (only exported from module, not on strategy object)\n const g = globalThis as GlobalWithTestHelpers;\n if (typeof g.__SPRING_TEST_HELPERS__ === 'object') {\n g.__SPRING_TEST_HELPERS__.calculateBridgeAmplitude =\n calculateBridgeAmplitude;\n g.__SPRING_TEST_HELPERS__.calculateNonBridgeBaseline =\n calculateNonBridgeBaseline;\n g.__SPRING_TEST_HELPERS__.calculatePulsePosition = calculatePulsePosition;\n g.__SPRING_TEST_HELPERS__.calculatePulseVelocity = calculatePulseVelocity;\n g.__SPRING_TEST_HELPERS__.calculateScalingFactor = calculateScalingFactor;\n }\n\n /**\n * Combined easing function (head + optional bridge + tail)\n */\n const p = (u: number): number => {\n if (u <= joinTime) {\n // In bridge mode, head ends at joinHeight; otherwise at P\n const headEnd = bridge ? bridge.joinHeight : P;\n return headEnd * L(u / joinTime);\n }\n if (bridge && u <= pulseStart) {\n // Bridge phase: constant velocity line from joinHeight to 1\n return bridge.joinHeight + bridge.velocity * (u - joinTime);\n }\n // Pulse phase\n const pulseY = (u - pulseStart) / (1 - pulseStart);\n return calculatePulsePosition(pulseY);\n };\n\n /**\n * Combined velocity function (head + optional bridge + tail)\n */\n const v = (u: number): number => {\n if (u <= joinTime) {\n // In bridge mode, head velocity scaled by joinHeight; otherwise by P\n const headEnd = bridge ? bridge.joinHeight : P;\n return (headEnd / joinTime) * Ld(u / joinTime);\n }\n if (bridge && u <= pulseStart) {\n return bridge.velocity;\n }\n const pulseY = (u - pulseStart) / (1 - pulseStart);\n return calculatePulseVelocity(pulseY);\n };\n\n // Calculate final amplitude as percentage of initial\n // After N half-cycles with total decay, the final peak amplitude is:\n const finalPct = bounces <= 1 ? 100 : 100 * (1 - decayPct / 100);\n\n return {\n p,\n v,\n meta: {\n r,\n finalPct,\n k,\n B,\n P,\n guard: guardMessage || 'ok',\n useBridge: !!bridge,\n },\n };\n },\n};\n\n// Backwards-compatible aliases\nexport const ElasticZeroLockedTail = SpringZeroLockedTail;\nexport const SpringTail = SpringZeroLockedTail;\n\n/**\n * Internal helper functions - exported only for testing purposes\n *\n * These functions are implementation details of the spring tail strategy.\n * They are exposed to enable focused unit testing of individual calculations\n * but should not be used directly in production code.\n *\n * @internal\n */\nexport const __INTERNAL_FOR_TESTING__ = {\n /**\n * Enable helper function capture during build() execution\n * Call this before invoking SpringZeroLockedTail.build() in tests\n */\n enableHelperCapture() {\n (globalThis as GlobalWithTestHelpers).__SPRING_TEST_HELPERS__ = {};\n },\n\n /**\n * Get captured helper functions from the most recent build() call\n * Returns undefined if enableHelperCapture() wasn't called first\n */\n getCapturedHelpers() {\n return (globalThis as GlobalWithTestHelpers).__SPRING_TEST_HELPERS__ as\n | SpringTestHelpers\n | undefined;\n },\n\n /**\n * Disable helper function capture and clean up\n */\n disableHelperCapture() {\n delete (globalThis as GlobalWithTestHelpers).__SPRING_TEST_HELPERS__;\n },\n};\n","function Z(e, h, i, b) {\n const d = Math.PI / 2, p = (s) => Math.abs(s) < 222e-17, v = (s) => {\n const t = Math.sign(s), r = s * t;\n return ((((-0.021641405 * r + 0.077981383) * r + -0.213301322) * r + d) * Math.sqrt(1 - r) - d) * t + d;\n }, D = (s) => {\n if (Math.abs(s) < 1) {\n const t = s * s, r = t * s, z = r * t;\n return s + r / 6 + z / 120;\n }\n return Math.sinh(s);\n }, I = e - i, X = h - b, _ = 3 * e, A = 3 * h, g = 3 * I + 1, l = -_ - 3 * I, k = _, y = 3 * X + 1, w = -A - 3 * X, L = A, E = 1 / g, q = l * E, F = k * E, o = q / -3 * q + F, M = q / 3, S = (2 * M * M - F) * M, P = Math.sign(o), Y = Math.sign(l);\n let a = 1, n = 1, B = 1, c, f, u;\n if (p(g) && p(l))\n c = 1, a = 1 / k, n = 0, f = 1, u = 0;\n else if (p(g))\n c = 2, a = Y, n = -(k / (2 * l)), f = 1 / l, u = n * n;\n else if (p(o))\n c = 3, n = -M, f = E, u = -S;\n else {\n if (g < 0)\n c = 4;\n else if (o > 0)\n c = 5;\n else if (o < 0)\n c = 6, B = Y;\n else\n throw new Error(\"Invalid curve type\");\n a = -2 * P * B * Math.sqrt(Math.abs(o) / 3), n = -M, f = 3 * P * E / (o * a), u = 3 * P * (-S / o / a);\n }\n const N = y * a * a * a, O = (3 * y * n + w) * a * a, T = ((3 * y * n + 2 * w) * n + L) * a, U = ((y * n + w) * n + L) * n;\n return (s) => {\n if (s <= 0) return 0;\n if (s >= 1) return 1;\n let t = f * s + u;\n switch (c) {\n case 1:\n break;\n case 2:\n t = Math.sqrt(Math.max(0, t));\n break;\n case 3:\n t = Math.cbrt(t);\n break;\n case 4:\n t = Math.cos(\n v(Math.max(-1, Math.min(1, t))) / 3 - 2.094395102393195\n );\n break;\n case 5:\n t = D(Math.log(t + Math.sqrt(t * t + 1)) / 3);\n break;\n case 6:\n t = t >= 1 ? (\n // Inlined fastAcosh: Math.acosh(phi) would be Math.log(phi + Math.sqrt(phi * phi - 1))\n Math.cosh(Math.log(t + Math.sqrt(t * t - 1)) / 3)\n ) : Math.cos(v(Math.max(-1, t)) / 3);\n break;\n default:\n throw new Error(\"Invalid curve type\");\n }\n return ((N * t + O) * t + T) * t + U;\n };\n}\nfunction j(e, h, i, b) {\n if (e < 0 || e > 1 || i < 0 || i > 1)\n throw new Error(\"x1 & x2 must be in [0, 1]\");\n return e === h && i === b ? (m) => m : Z(e, h, i, b);\n}\nexport {\n j as bezierEasing,\n j as default\n};\n//# sourceMappingURL=index.js.map\n","/**\n * Power-Back Easing Mathematics\n *\n * Extracted from fuse/powerBack.ts - provides pure mathematical functions for\n * the unified Power-Back easing family.\n *\n * Unified family that combines Power's exponent with Back's anticipation:\n * f(u; strength) = (strength+1)u^n − strength·u^(n-1) for all n > 1\n *\n * This mathematically unifies Power and Back easing families, where:\n * - n is the power exponent (controls acceleration profile)\n * - strength is the anticipation parameter (controls overshoot depth)\n * - x = strength/(strength+1) is the normalized anticipation parameter, x ∈ [0, 1)\n *\n * Special cases:\n * - strength = 0: pure power easing u^n\n * - n ≤ 1: pure power (no overshoot possible)\n * - n = 2: quadratic with anticipation\n * - n = 3: cubic (classic Back)\n * - n > 3: higher order with stronger acceleration\n */\n\nimport type { PowerBackParams, PowerBackResult } from './types';\nimport type {\n NormalizedTime,\n NormalizedProgress,\n NormalizedVelocity,\n} from '@penner/smart-primitive';\n\n/**\n * Forward mapping: compute overshoot from x and exponent\n *\n * Given x = s/(s+1) and exponent n, calculates the overshoot depth.\n *\n * Mathematical derivation:\n * Critical point: u* = (n-1)x/n\n * Overshoot: O = x^n(n-1)^(n-1) / [n^n(1-x)]\n *\n * @param x - Parameter x = s/(s+1), where x ∈ [0, 1) as NormalizedProgress\n * @param n - Power exponent, must be > 1 for overshoot to exist\n * @returns Overshoot depth O = |min f_n(u; s)| as NormalizedProgress\n */\nexport function overshootFromX(\n x: NormalizedProgress,\n n: number,\n): NormalizedProgress {\n if (x <= 0) return 0;\n if (x >= 1) return Number.POSITIVE_INFINITY;\n if (n <= 1) return 0;\n\n // Handle edge case where n approaches 1\n if (n - 1 < 1e-10) {\n // As n→1, overshoot approaches 0 smoothly\n const s = x / (1 - x);\n return Math.max(0, s * Math.pow(n - 1, 2));\n }\n\n // Unified closed-form overshoot formula for all n > 1:\n // O = x^n(n-1)^(n-1) / [n^n(1-x)]\n const c_n = Math.pow((n - 1) / n, n - 1) / n;\n return (Math.pow(x, n) / (1 - x)) * c_n;\n}\n\n/**\n * Inverse mapping: find parameter s given overshoot and exponent\n *\n * Uses bisection on x = s/(s+1) to solve the inverse problem:\n * given target overshoot O and exponent n, find s such that f_n(u; s)\n * has minimum value −O.\n *\n * Strategy:\n * - Re-parameterize s with x = s/(s+1), so s = x/(1−x), x ∈ [0,1)\n * - For fixed n, overshoot O(x; n) is strictly increasing in x\n * - Use bisection to solve O(x; n) = target\n *\n * @param overshoot - Target overshoot depth O (0 to ~1) as NormalizedProgress\n * @param exponent - Power exponent n (must be > 1 for Back behavior)\n * @returns The strength parameter (≥ 0) for the PowerBack anticipation term\n */\nexport function solvePowerBackStrength(\n overshoot: NormalizedProgress,\n exponent: number,\n): number {\n if (overshoot <= 0) return 0;\n if (exponent <= 1) return 0; // No overshoot possible for n ≤ 1\n\n // Bisection solver for all n > 1\n const target = overshoot;\n let lo = 0;\n let hi = 1 - 1e-9;\n\n // Bisection on x = s/(s+1)\n for (let i = 0; i < 60; i++) {\n const mid = 0.5 * (lo + hi);\n const Omid = overshootFromX(mid, exponent);\n if (Omid < target) {\n lo = mid;\n } else {\n hi = mid;\n }\n }\n\n const x = 0.5 * (lo + hi);\n return x / (1 - x);\n}\n\n/**\n * Evaluate power-back easing at a specific time\n *\n * Evaluates f_n(u; s) = (s+1)u^n − su^(n-1)\n *\n * @param u - Normalized time (0 to 1) as NormalizedTime\n * @param strength - Strength parameter (≥ 0)\n * @param n - Power exponent (n > 1 for anticipation)\n * @returns Easing value as NormalizedProgress\n */\nexport function evalPowerBackIn(\n u: NormalizedTime,\n strength: number,\n n: number,\n): NormalizedProgress {\n // Clamp to bounds\n if (u <= 0) return 0;\n if (u >= 1) return 1;\n\n // Factored form: u^(n-1)[(strength+1)u - strength]\n const strengthPlus1 = strength + 1;\n const base = Math.pow(u, n - 1);\n const factor = strengthPlus1 * u - strength;\n\n return base * factor;\n}\n\n/**\n * Evaluate power-back velocity (derivative) at a specific time\n *\n * Derivative: f'_n(u; s) = nu^(n-1)(s+1) - (n-1)su^(n-2)\n * Factored: u^(n-2)[n(s+1)u - (n-1)s]\n *\n * @param u - Normalized time (0 to 1) as NormalizedTime\n * @param strength - Strength parameter (≥ 0)\n * @param n - Power exponent (n > 1 for anticipation)\n * @returns Velocity (slope) as NormalizedVelocity\n */\nexport function evalPowerBackVelocity(\n u: NormalizedTime,\n strength: number,\n n: number,\n): NormalizedVelocity {\n if (u <= 0) {\n // At u=0, derivative is 0 for n>1, undefined for n=1\n return n > 1 ? 0 : strength + 1;\n }\n if (u >= 1) {\n // At u=1, derivative is (strength+1)*n - (n-1)*strength = n + strength\n return n + strength;\n }\n\n const strengthPlus1 = strength + 1;\n const base = Math.pow(u, n - 2);\n const factor = n * strengthPlus1 * u - (n - 1) * strength;\n\n return base * factor;\n}\n\n/**\n * Calculate power-back parameters given overshoot and exponent\n *\n * Convenience function that computes all relevant parameters.\n *\n * @param params - Power-back configuration\n * @returns Power-back result with calculated parameters\n */\nexport function calculatePowerBackParams(\n params: PowerBackParams,\n): PowerBackResult {\n const { exponent, overshoot } = params;\n\n // Compute strength parameter\n const strength = solvePowerBackStrength(overshoot, exponent);\n\n // Compute x parameter\n const x = strength / (strength + 1);\n\n // Critical point (minimum location)\n const u_star = exponent > 1 ? ((exponent - 1) * x) / exponent : 0;\n\n // Verify overshoot calculation\n const calculatedOvershoot = overshootFromX(x, exponent);\n\n return {\n strength,\n xParameter: x,\n criticalPoint: u_star,\n calculatedOvershoot,\n };\n}\n\n/**\n * Create a simple power easing (no anticipation)\n *\n * Evaluates u^n\n *\n * @param u - Normalized time (0 to 1) as NormalizedTime\n * @param n - Power exponent\n * @returns Easing value as NormalizedProgress\n */\nexport function evalPowerIn(u: NormalizedTime, n: number): NormalizedProgress {\n if (u <= 0) return 0;\n if (u >= 1) return 1;\n return Math.pow(u, n);\n}\n\n/**\n * Evaluate power easing velocity (derivative)\n *\n * Derivative: n*u^(n-1)\n *\n * @param u - Normalized time (0 to 1) as NormalizedTime\n * @param n - Power exponent\n * @returns Velocity (slope) as NormalizedVelocity\n */\nexport function evalPowerVelocity(\n u: NormalizedTime,\n n: number,\n): NormalizedVelocity {\n if (u <= 0) {\n return n > 1 ? 0 : 1;\n }\n if (u >= 1) {\n return n;\n }\n return n * Math.pow(u, n - 1);\n}\n","/**\n * Power Back easing mathematics\n *\n * Unified family that combines Power's exponent parameter with Back's anticipation:\n *\n * f(u; strength) = (strength+1)u^n − strength·u^(n-1) for all n > 1\n *\n * Factored form:\n * f(u; strength) = u^(n-1)[(strength+1)u - strength]\n * f(u; strength) = (strength+1)u^(n-1)(u - x) where x = strength/(strength+1)\n *\n * Where:\n * - n is the power exponent (controls acceleration profile)\n * - strength is the anticipation parameter (controls overshoot depth)\n * - x = strength/(strength+1) is the normalized anticipation parameter, x ∈ [0, 1)\n *\n * This mathematically unifies the Power and Back easing families. For n > 1,\n * the curve exhibits anticipation (dips below 0) before accelerating to 1.\n *\n * Key properties:\n * - Zero crossing at u = x = strength/(strength+1)\n * - Critical point (minimum) at u* = (n-1)x/n\n * - Overshoot O = x^n(n-1)^(n-1) / [n^n(1-x)]\n * - Always reaches f(1) = 1\n *\n * Special cases:\n * - strength = 0: pure power easing u^n\n * - n ≤ 1: pure power (no overshoot possible)\n * - n → 1⁺: correction exponent → 0, f(u) → (strength+1)u - strength (line)\n * - n = 2: f(u) = (strength+1)u² - strength·u\n * - n = 3: f(u) = (strength+1)u³ - strength·u² (classic Back)\n * - n > 3: correction exponent n-1 continues to grow\n */\n\n/**\n * Forward mapping: compute overshoot O from x = s/(s+1) and exponent n.\n *\n * For all n > 1, uses the unified formula f_n(u; s) = (s+1)u^n − su^(n-1)\n *\n * Mathematical derivation:\n * Critical point: u* = (n-1)x/n\n * Overshoot: O = x^n(n-1)^(n-1) / [n^n(1-x)]\n *\n * This closed-form solution works for all n > 1, providing:\n * - Exact calculation (no numerical solving)\n * - Consistent behavior across all exponents\n * - Natural limiting behavior as n → 1⁺ and n → ∞\n *\n * @param x - Parameter x = s/(s+1), where x ∈ [0, 1)\n * @param n - Power exponent, must be > 1 for overshoot to exist\n * @returns Overshoot depth O = |min f_n(u; s)|\n */\nexport function overshootFromX(x: number, n: number): number {\n if (x <= 0) return 0;\n if (x >= 1) return Number.POSITIVE_INFINITY;\n if (n <= 1) return 0;\n\n // Handle edge case where n approaches 1\n if (n - 1 < 1e-10) {\n // As n→1, overshoot approaches 0 smoothly\n const s = x / (1 - x);\n return Math.max(0, s * Math.pow(n - 1, 2));\n }\n\n // Unified closed-form overshoot formula for all n > 1:\n // O = x^n(n-1)^(n-1) / [n^n(1-x)]\n const c_n = Math.pow((n - 1) / n, n - 1) / n;\n return (Math.pow(x, n) / (1 - x)) * c_n;\n}\n\nimport { solvePowerBackStrength } from '../lib/curves/power-back-math';\nexport { solvePowerBackStrength };\n\n/**\n * Evaluate the unified power-Back ease-in at point u.\n *\n * Unified formula for all n > 1:\n * f_n(u; s) = (s+1)u^n − su^(n-1)\n *\n * Factored form (computational):\n * f_n(u; s) = u^(n-1)[(s+1)u - s]\n *\n * This saves one Math.pow() call and reveals the geometric structure:\n * - Zero crossing at u = s/(s+1)\n * - Amplitude scaling by (s+1)\n * - Power envelope u^(n-1)\n *\n * For s = 0 or n ≤ 1: falls back to pure power u^n\n *\n * @param u - Normalized time parameter [0, 1]\n * @param exponent - Power exponent n\n * @param strength - Strength parameter (from solvePowerBackStrength)\n * @returns Position at time u\n */\nexport function evalPowerBackIn(\n u: number,\n exponent: number,\n strength: number,\n): number {\n const n = exponent;\n\n if (strength === 0 || n <= 1) {\n // Pure power ease-in\n return Math.pow(u, Math.max(n, 0));\n }\n\n // Handle edge case where n approaches 1\n if (n - 1 < 1e-10) {\n // As n→1, f(u) → (strength+1)u - strength (linear)\n return (strength + 1) * u - strength;\n }\n\n // Unified formula for all n > 1, using factored form:\n // f(u) = u^(n-1)[(strength+1)u - strength]\n const u_n_minus_1 = Math.pow(u, n - 1);\n const result = u_n_minus_1 * ((strength + 1) * u - strength);\n\n // Ensure positive zero (avoid -0 at u=0)\n return result === 0 ? 0 : result;\n}\n\n/**\n * Compute velocity (derivative) of power-Back at point u.\n *\n * Unified derivative for all n > 1:\n * f'_n(u; s) = u^(n-2)[n(s+1)u - (n-1)s]\n *\n * Expanded form:\n * f'_n(u; s) = n(s+1)u^(n-1) − s(n-1)u^(n-2)\n *\n * Properties:\n * - For n > 2: f'(0) = 0 (zero initial velocity, classic Back behavior)\n * - For 1 < n ≤ 2: f'(0) is non-zero or undefined (sharper start)\n * - Critical point at u* = (n-1)s / [n(s+1)] = (n-1)x/n\n *\n * @param u - Normalized time parameter [0, 1]\n * @param exponent - Power exponent n\n * @param strength - Strength parameter\n * @returns Velocity at time u\n */\nexport function evalPowerBackVelocity(\n u: number,\n exponent: number,\n strength: number,\n): number {\n const n = exponent;\n\n if (strength === 0 || n <= 1) {\n // Pure power: derivative is n*u^(n-1)\n return n * Math.pow(u, n - 1);\n }\n\n // Handle edge case where n approaches 1\n if (n - 1 < 1e-10) {\n // As n→1, f'(u) → (strength+1) (constant velocity)\n return strength + 1;\n }\n\n // For n close to 2, handle potential numerical issues with u^(n-2)\n if (Math.abs(n - 2) < 1e-10) {\n // At n=2: f'(u) = 2(strength+1)u - strength\n return 2 * (strength + 1) * u - strength;\n }\n\n // Unified derivative for all n > 1:\n // f'(u) = n(strength+1)u^(n-1) - strength(n-1)u^(n-2)\n if (u === 0 && n > 2) {\n // For n > 2, both terms vanish at u=0\n return 0;\n }\n\n if (u === 0 && n <= 2) {\n // For 1 < n ≤ 2, the second term dominates\n // Return a large value to represent the discontinuity\n return n * (strength + 1) * Math.pow(1e-10, n - 1);\n }\n\n return n * (strength + 1) * Math.pow(u, n - 1) - strength * (n - 1) * Math.pow(u, n - 2);\n}\n\n/**\n * Compute the u-coordinate of the local minimum (critical point) for power-back ease-in.\n *\n * The critical point occurs where f'(u) = 0:\n * u* = (n-1)s / [n(s+1)] = (n-1)x/n\n *\n * where x = s/(s+1) is the zero-crossing position.\n *\n * This is the position of the \"bulge\" in the overshoot curve - the deepest\n * point of anticipation before the curve rises toward 1.\n *\n * @param exponent - Power exponent n (must be > 1)\n * @param overshoot - Overshoot percentage (0-1), e.g., 0.3 for 30%\n * @returns u-coordinate of local minimum, or 0 if no overshoot exists\n */\nexport function getLocalMinimumPosition(\n exponent: number,\n overshoot: number,\n): number {\n if (overshoot <= 0 || exponent <= 1) return 0;\n\n const n = exponent;\n const strength = solvePowerBackStrength(overshoot, n);\n\n if (strength <= 0) return 0;\n\n // u* = (n-1)*strength / [n*(strength+1)]\n return ((n - 1) * strength) / (n * (strength + 1));\n}\n\n/**\n * Compute the canonical grab point on a pure power ease-in curve u^n.\n *\n * This is the point of maximum perpendicular distance from the diagonal (p = u),\n * found by maximizing u^n − u:\n *\n * d/du [u^n − u] = n·u^(n-1) − 1 = 0\n * → u_peak = n^(−1/(n−1))\n * → p_peak = u_peak^n = n^(−n/(n−1))\n *\n * This serves as the natural \"visual center\" of the curve's bend — the intrinsic\n * analog of the extremum for the zero-overshoot case. Used to position the\n * affordance dot when overshoot/anticipation is 0.\n *\n * For the ease-out mirror: u_tail = 1 − u_peak, p_tail = 1 − p_peak.\n *\n * @param exponent - Power exponent n (> 1 for a meaningful result)\n * @returns { u, p } coordinates of the canonical point on the pure power curve\n */\nexport function getPowerCurveCanonicalPoint(exponent: number): {\n u: number;\n p: number;\n} {\n const n = exponent;\n if (n <= 1) {\n // For n ≤ 1, the curve is convex (or linear); use midpoint as fallback\n return { u: 0.5, p: Math.pow(0.5, Math.max(n, 0)) };\n }\n const u = Math.pow(n, -1 / (n - 1));\n return { u, p: Math.pow(u, n) };\n}\n\n/**\n * Find the exponent that places the local minimum at a target u-coordinate.\n *\n * This is the inverse of getLocalMinimumPosition: given a fixed overshoot\n * and a desired position for the local minimum, find the exponent n that\n * achieves that position.\n *\n * The relationship between u*, n, and x is:\n * u* = (n-1)x/n\n *\n * where x = s/(s+1) depends on both n and overshoot O.\n *\n * Since x depends on n (through the overshoot equation), we use bisection\n * to solve for n numerically.\n *\n * @param overshoot - Fixed overshoot percentage (0-1), e.g., 0.3 for 30%\n * @param targetUMin - Desired u-coordinate of local minimum (0-1)\n * @param minExponent - Minimum allowed exponent (default 1.01)\n * @param maxExponent - Maximum allowed exponent (default 10)\n * @returns Exponent n that places the minimum at targetUMin, or null if impossible\n */\nexport function findExponentForMinimumPosition(\n overshoot: number,\n targetUMin: number,\n minExponent = 1.01,\n maxExponent = 10,\n): number | null {\n // Edge cases\n if (overshoot <= 0) return null; // No minimum exists without overshoot\n if (targetUMin <= 0) return null; // Minimum can't be at or before 0\n if (targetUMin >= 1) return null; // Minimum must be before the end\n\n // Compute the local minimum position for a given exponent\n const computeUMin = (n: number): number => {\n const strength = solvePowerBackStrength(overshoot, n);\n if (strength <= 0) return 0;\n // u* = (n-1)*strength / [n*(strength+1)]\n return ((n - 1) * strength) / (n * (strength + 1));\n };\n\n // Check if target is achievable within bounds\n const uMinAtLow = computeUMin(minExponent);\n const uMinAtHigh = computeUMin(maxExponent);\n\n // As exponent increases, u* moves toward x (the zero crossing)\n // For fixed overshoot, larger n means u* is closer to x\n // The relationship is monotonic, but we need to check bounds\n\n // If target is outside the achievable range, return boundary exponent\n if (targetUMin <= uMinAtLow && uMinAtLow > 0) {\n // Target is below what we can achieve - return min exponent\n return minExponent;\n }\n if (targetUMin >= uMinAtHigh) {\n // Target is above what we can achieve - return max exponent\n return maxExponent;\n }\n\n // Bisection search for the exponent\n let lo = minExponent;\n let hi = maxExponent;\n\n for (let i = 0; i < 50; i++) {\n const mid = 0.5 * (lo + hi);\n const uMinMid = computeUMin(mid);\n\n if (Math.abs(uMinMid - targetUMin) < 1e-9) {\n return mid;\n }\n\n // u* increases with n for fixed overshoot\n if (uMinMid < targetUMin) {\n lo = mid;\n } else {\n hi = mid;\n }\n }\n\n return 0.5 * (lo + hi);\n}\n","/**\n * Predefined regular tail easing strategies for fuse\n *\n * These are \"out\" easing variants with net displacement (move from one position to another).\n * Each tail includes both the easing function and its derivative for C¹ continuity.\n *\n * For pulse tails with net-zero displacement (oscillate and settle), see HardBounceTail and ElasticZeroLockedTail.\n */\n\nimport type {\n EasingFn,\n NormalizedTime,\n NormalizedProgress,\n} from '../lib/types/units';\nimport type { VelocityFn } from '@penner/easing';\nimport { makeExpoEaseOut, createVelocityFn, solveBackStrength } from '@penner/easing';\nimport bezierEasing from '@penner/fast-bezier-easing';\nimport { solvePowerBackStrength, evalPowerBackVelocity } from './powerBack';\n\n/**\n * Tail strategy interface for regular (non-pulse) tails\n *\n * Regular tails have net displacement - they move from one position to another.\n * In contrast, pulse tails oscillate around a target and settle back to it (net-zero displacement).\n */\nexport interface RegularTailStrategy {\n /**\n * Unique identifier for this tail strategy\n */\n id: string;\n\n /**\n * Human-readable label\n */\n label: string;\n\n /**\n * The easing function (should be an \"out\" variant)\n */\n easingFn: EasingFn;\n\n /**\n * Derivative of the easing function\n */\n velocityFn: VelocityFn;\n\n /**\n * Optional configuration object\n */\n config?: Record<string, unknown>;\n}\n\n/**\n * Create a back (overshoot) tail strategy - ease out\n */\nexport function createBackTail(overshoot = 0.1): RegularTailStrategy {\n const strength = solveBackStrength(overshoot);\n\n // easeOutBack: t = 1 - u, then apply easeInBack formula\n // easeInBack(t) = (strength+1)*t³ - strength*t²\n // easeOutBack(u) = 1 - easeInBack(1-u)\n const easing = (u: number): number => {\n const t = 1 - u;\n return 1 - ((strength + 1) * t * t * t - strength * t * t);\n };\n\n // Derivative of easeOutBack\n const derivative = (u: number): number => {\n const t = 1 - u;\n return (strength + 1) * 3 * t * t - strength * 2 * t;\n };\n\n return {\n id: 'back-out',\n label: `Back Out (${(overshoot * 100).toFixed(0)}%)`,\n easingFn: easing,\n velocityFn: derivative,\n config: { overshoot },\n };\n}\n\n/**\n * Create a power ease-out tail strategy with a configurable exponent\n */\nexport function createPowerTail(exponent = 2): RegularTailStrategy {\n const n = Math.max(0.1, exponent); // Ensure exponent is at least 0.1\n\n return {\n id: 'power-out',\n label: `Power Out (t^${n})`,\n easingFn: (u: number) => {\n const t = 1 - u;\n return 1 - Math.pow(t, n);\n },\n velocityFn: (u: number) => {\n const t = 1 - u;\n return n * Math.pow(t, n - 1);\n },\n config: { exponent: n },\n };\n}\n\n/**\n * Create a power-back ease-out tail strategy\n *\n * Combines power curve (exponent) with back overshoot for ease-out.\n * Uses the unified power-back mathematics to maintain C¹ continuity with head phases.\n *\n * For ease-out, we reverse the ease-in formula:\n * easeOutPowerBack(u) = 1 - easeInPowerBack(1 - u)\n *\n * @param exponent - Power exponent n (must be > 1 for overshoot, default 2)\n * @param overshoot - Overshoot depth O (0-1, default 0.1)\n */\nexport function createPowerBackTail(\n exponent = 2,\n overshoot = 0.1,\n): RegularTailStrategy {\n const n = Math.max(0.1, exponent); // Ensure exponent is at least 0.1\n\n // If no overshoot requested, fall back to pure power\n if (overshoot <= 0) {\n return createPowerTail(exponent);\n }\n\n // If exponent ≤ 1, overshoot not possible - fall back to pure power\n if (n <= 1) {\n return createPowerTail(exponent);\n }\n\n // General case: n > 1 with non-zero overshoot\n const strength = solvePowerBackStrength(overshoot, n);\n\n // For ease-out, reverse the ease-in:\n // easeOutPowerBack(u) = 1 - easeInPowerBack(1-u)\n const easingFn = (u: number): number => {\n const t = 1 - u;\n\n if (strength === 0 || n <= 1) {\n // Pure power ease-out: 1 - (1-u)^n\n return 1 - Math.pow(t, n);\n }\n\n // Handle edge case where n approaches 1\n if (n - 1 < 1e-10) {\n const easeInVal = (strength + 1) * t - strength;\n return 1 - easeInVal;\n }\n\n // Unified power-back ease-in formula: u^(n-1)[(strength+1)u - strength]\n // Applied to (1-u), then subtracted from 1\n const u_n_minus_1 = Math.pow(t, n - 1);\n const easeInVal = u_n_minus_1 * ((strength + 1) * t - strength);\n return 1 - easeInVal;\n };\n\n // For velocity (derivative of ease-out), we need:\n // d/du[1 - easeInPowerBack(1-u)]\n // = easeInPowerBack'(1-u)\n const velocityFn = (u: number): number => {\n const t = 1 - u;\n return evalPowerBackVelocity(t, n, strength);\n };\n\n return {\n id: 'power-back-out',\n label: `Power Back Out (${(overshoot * 100).toFixed(0)}%, n=${n.toFixed(2)})`,\n easingFn,\n velocityFn,\n config: { exponent: n, overshoot, strength },\n };\n}\n\n/**\n * Create a Bezier tail strategy with custom control points\n *\n * Uses cubic Bezier curve with locked endpoints.\n * In the tail phase, the curve goes from (joinTime, joinHeight) to (1, 1).\n * The control points (x1, y1) and (x2, y2) are specified in normalized coordinates (0-1)\n * relative to the tail segment.\n *\n * IMPORTANT: For C¹ continuity, the slope from p0 to p1 must match the slope\n * from the head's p2 to p3. This constraint is enforced at the fuse level,\n * not in this factory function.\n *\n * @param x1 - First control point X coordinate (0 to 1, normalized time)\n * @param y1 - First control point Y coordinate (0 to 1, normalized progress)\n * @param x2 - Second control point X coordinate (0 to 1, normalized time)\n * @param y2 - Second control point Y coordinate (0 to 1, normalized progress)\n */\nexport function createBezierTail(\n x1: NormalizedTime = 0.5,\n y1: NormalizedProgress = 0,\n x2: NormalizedTime = 1,\n y2: NormalizedProgress = 0.5,\n): RegularTailStrategy {\n // Create the Bezier easing function using fast-bezier-easing\n // Note: This creates a normalized bezier from (0,0) to (1,1)\n // The fuse logic will transform this to work in the tail's coordinate space\n const bezier = bezierEasing(x1, y1, x2, y2);\n\n const velocityFn = createVelocityFn(bezier, {\n step: 1e-8,\n boundarySample: 0.001,\n });\n\n return {\n id: 'bezier-out',\n label: `Bezier (${x1.toFixed(2)}, ${y1.toFixed(2)}, ${x2.toFixed(2)}, ${y2.toFixed(2)})`,\n easingFn: bezier,\n velocityFn,\n config: { x1, y1, x2, y2 },\n };\n}\n\n/**\n * Create an exponential ease-out tail strategy with a configurable decay parameter.\n *\n * Uses the canonical normalized expo ease-out from @penner/easing.\n * The analytical derivative is computed for C¹ continuity at the fuse join point.\n *\n * @param decay - Fraction of initial velocity lost over the segment, in [0, 1].\n * - 0 = linear (no decay)\n * - 0.95 = typical smooth deceleration (default)\n * - approaching 1 = very steep initial curve\n */\nexport function createExpoTail(decay = 0.95): RegularTailStrategy {\n const d = Math.max(0, Math.min(1, decay));\n\n // decay = 0 => linear\n if (d === 0) {\n return {\n id: 'expo-out',\n label: 'Expo Out (linear)',\n easingFn: (u: number) => {\n if (u <= 0) return 0;\n if (u >= 1) return 1;\n return u;\n },\n velocityFn: () => 1,\n config: { decay: d },\n };\n }\n\n // decay = 1 => step-like limit\n if (d === 1) {\n return {\n id: 'expo-out',\n label: 'Expo Out (step)',\n easingFn: (u: number) => {\n if (u > 0) return 1;\n return 0;\n },\n velocityFn: () => 0,\n config: { decay: d },\n };\n }\n\n const negK = Math.log1p(-d);\n const scale = 1 / Math.expm1(negK);\n\n const easingFn = makeExpoEaseOut(d);\n\n // Analytical derivative: f'(t) = negK * exp(negK * t) * scale\n const velocityFn = (u: number): number => {\n return negK * Math.exp(negK * u) * scale;\n };\n\n return {\n id: 'expo-out',\n label: `Expo Out (${(d * 100).toFixed(0)}%)`,\n easingFn,\n velocityFn,\n config: { decay: d },\n };\n}\n\n/**\n * Map of all predefined regular tail strategy factories\n *\n * Regular tails have net displacement - they move from one position to another.\n * These factories create tail strategies with easingFn/velocityFn for use by createSimpleFuse.\n *\n * For pulse tails with net-zero displacement (oscillate and settle), use:\n * - HardBounceTail (bounce) - gravity-based bouncing physics\n * - ElasticZeroLockedTail (spring) - spring-like oscillation\n */\nexport const tailStrategies = {\n back: createBackTail,\n power: createPowerTail,\n 'power-back': createPowerBackTail,\n bezier: createBezierTail,\n expo: createExpoTail,\n} as const;\n\n/**\n * Regular tail type union (tails with net displacement)\n */\nexport type TailType = keyof typeof tailStrategies;\n\n/**\n * Get a tail strategy by type\n */\nexport function getTailStrategy(\n type: TailType,\n config?: {\n overshoot?: number;\n exponent?: number;\n x1?: number;\n y1?: number;\n x2?: number;\n y2?: number;\n decay?: number;\n },\n): RegularTailStrategy {\n const factory = tailStrategies[type];\n\n if (type === 'back') {\n return factory(config?.overshoot ?? 0.1);\n }\n\n if (type === 'power') {\n return factory(config?.exponent ?? 2);\n }\n\n if (type === 'power-back') {\n return factory(config?.exponent ?? 2, config?.overshoot ?? 0.1);\n }\n\n if (type === 'expo') {\n return factory(config?.decay ?? 0.95);\n }\n\n // type === 'bezier'\n return factory(\n config?.x1 ?? 0.5,\n config?.y1 ?? 0,\n config?.x2 ?? 1,\n config?.y2 ?? 0.5,\n );\n}\n","/** Closure-cached lazy getter: caches the result in a closure variable */\nexport function cachedGetter<T extends object, K extends string & keyof T>(\n target: T,\n key: K,\n compute: () => T[K],\n): void {\n let cached: T[K] | undefined;\n Object.defineProperty(target, key, {\n get: () => (cached ??= compute()),\n configurable: true,\n enumerable: true,\n });\n}\n","/**\n * EasingRem — abstract base class for Responsive Easing Modules.\n *\n * Each easing type (Power, Back, PowerBack, Bezier, Expo, Spring, Bounce)\n * is a concrete subclass that provides easeIn/easeOut functions, velocity\n * derivatives, intrinsic metadata, and UI knob specs.\n *\n * REMs are immutable: params are frozen at construction, `with()` returns\n * a new instance.\n */\n\nimport type {\n EasingFn,\n VelocityFn,\n NormalizedTime,\n NormalizedProgress,\n} from '../../lib/types/units';\nimport type { KnobSpec } from '../../lib/rem/core/knobs';\nimport { createVelocityFn } from '@penner/easing';\nimport { cachedGetter } from '../../lib/cachedGetter';\n\n// ─── Velocity Boundary ───────────────────────────────────────────────\n\n/**\n * Velocity constraint at a boundary.\n * Kept as a union (not boolean) for future extensibility —\n * e.g., Bezier joins may need a 'tangent-coupled' constraint.\n */\nexport type VelocityConstraint = 'zero' | 'nonzero';\n\n/** Velocity boundary metadata for an easing variant */\nexport interface VelocityBoundary {\n readonly start: VelocityConstraint;\n readonly end: VelocityConstraint;\n}\n\n// ─── REM Metadata ────────────────────────────────────────────────────\n\n/**\n * Intrinsic REM metadata — objective facts, not derived heuristics.\n * Composition rules (e.g., \"which REMs can be heads?\") are inferred\n * from these facts by the Fuse logic, not baked into the metadata.\n */\nexport interface EasingRemMetadata {\n /** Which easing variants this REM can produce */\n // TODO: future variants may include easeInOut and easeOutIn\n readonly variants: {\n readonly easeIn: boolean;\n readonly easeOut: boolean;\n };\n /** Which movement modes this REM supports\n * (e.g., Spring supports both: natively pulse, transition via bridge) */\n readonly modes: {\n readonly transition: boolean;\n readonly pulse: boolean;\n };\n}\n\n// ─── EasingRem Base Class ────────────────────────────────────────────\n\nexport abstract class EasingRem<P extends object = Record<string, unknown>> {\n /** Unique identifier for this REM type (e.g., 'power', 'back') */\n abstract readonly kind: string;\n\n /** Intrinsic metadata: variants, modes */\n abstract readonly metadata: EasingRemMetadata;\n\n /** Knob specs for dynamic UI generation (practical slider ranges, not math constraints) */\n abstract readonly knobSpecs: readonly KnobSpec[];\n\n /** Velocity boundary for ease-in variant (can depend on current params) */\n abstract readonly easeInBoundary: VelocityBoundary;\n\n /** Immutable parameter object */\n readonly params: Readonly<P>;\n\n constructor(params: P) {\n this.params = Object.freeze({ ...params });\n\n // Lazy velocity defaults: numerical derivative of easeIn/easeOut.\n // Cached getters resolve on first access, after subclass\n // overrides of easeIn/easeOut are in place.\n // Subclasses with analytical derivatives can shadow these\n // by calling cachedGetter() after super().\n cachedGetter(this, 'easeInVelocity', () => createVelocityFn(this.easeIn));\n cachedGetter(this, 'easeOutVelocity', () => createVelocityFn(this.easeOut));\n }\n\n /** Ease-in function */\n abstract easeIn(u: NormalizedTime): NormalizedProgress;\n\n /** Ease-in velocity — default: lazy numerical derivative of easeIn.\n * Subclasses may override with an analytical derivative. */\n declare easeInVelocity: VelocityFn;\n\n /** Ease-out function — default: reverse of easeIn */\n easeOut = (u: NormalizedTime): NormalizedProgress => {\n return 1 - this.easeIn(1 - u);\n };\n\n /** Ease-out velocity — default: lazy numerical derivative of easeOut.\n * Automatically correct even when easeOut is overridden. */\n declare easeOutVelocity: VelocityFn;\n\n /** Velocity boundary for ease-out variant — default: reversed easeIn */\n get easeOutBoundary(): VelocityBoundary {\n return {\n start: this.easeInBoundary.end,\n end: this.easeInBoundary.start,\n };\n }\n\n /** Returns a new REM with patched params (immutable update) */\n abstract with(overrides: Partial<P>): EasingRem<P>;\n}\n","/**\n * SwimRem — Swimming easing Responsive Easing Module.\n *\n * Models rhythmic pulsed propulsion through viscous fluid (fish, frog, jellyfish).\n * Delegates to swim() from @penner/easing which uses RK4 numerical integration.\n *\n * Parameters:\n * strokes — number of kick/glide cycles (1 = frog leap, 3 = fish cruising, 5+ = jellyfish)\n * effort — duty cycle fraction (kick-to-glide ratio within each stroke)\n * drag — damping coefficient (water viscosity / drag)\n *\n * Velocity is computed numerically by the base class (no analytical derivative\n * exists for RK4-integrated curves).\n */\n\nimport type { NormalizedTime, NormalizedProgress } from '../../lib/types/units';\nimport type {\n KnobSpec,\n NumberKnobSpec,\n PercentKnobSpec,\n} from '../../lib/rem/core/knobs';\nimport { swim, type SwimConfig } from '@penner/easing';\nimport {\n EasingRem,\n type EasingRemMetadata,\n type VelocityBoundary,\n} from './EasingRem';\n\n/** Derived from SwimConfig — single source of truth for swim parameter names. */\nexport type SwimRemParams = Readonly<Required<SwimConfig>>;\n\nexport class SwimRem extends EasingRem<SwimRemParams> {\n static readonly STROKES_KNOB: NumberKnobSpec = {\n key: 'strokes',\n label: 'Strokes',\n type: 'number',\n default: 2,\n min: 1,\n max: 10,\n step: 0.05,\n isPrimary: true,\n } as const;\n\n static readonly EFFORT_KNOB: PercentKnobSpec = {\n key: 'effort',\n label: 'Effort',\n type: 'percent',\n default: 0.4,\n min: 0.05,\n max: 0.95,\n step: 0.01,\n } as const;\n\n static readonly DRAG_KNOB: NumberKnobSpec = {\n key: 'drag',\n label: 'Drag',\n type: 'number',\n default: 6,\n min: 0.5,\n max: 30,\n step: 0.1,\n } as const;\n\n readonly kind = 'swim' as const;\n\n readonly metadata: EasingRemMetadata = {\n variants: { easeIn: true, easeOut: true },\n modes: { transition: true, pulse: false },\n };\n\n readonly knobSpecs: readonly KnobSpec[] = [\n SwimRem.STROKES_KNOB,\n SwimRem.EFFORT_KNOB,\n SwimRem.DRAG_KNOB,\n ];\n\n readonly easeInBoundary: VelocityBoundary = {\n start: 'zero',\n end: 'nonzero',\n };\n\n constructor(params: SwimRemParams) {\n super(params);\n // Build the easing function once via RK4 integration\n const fn = swim(params);\n this.easeIn = (u: NormalizedTime): NormalizedProgress => fn(u);\n }\n\n // Declared as instance property, assigned in constructor\n declare easeIn: (u: NormalizedTime) => NormalizedProgress;\n\n with(overrides: Partial<SwimRemParams>): SwimRem {\n return new SwimRem({ ...this.params, ...overrides });\n }\n\n static create(init: Partial<SwimRemParams> = {}): SwimRem {\n return new SwimRem({\n strokes: init.strokes ?? 2,\n effort: init.effort ?? 0.2,\n drag: init.drag ?? 6,\n });\n }\n}\n","/**\n * Predefined head easing strategies for fuse\n *\n * These are \"in\" easing variants that can be used as the head phase of a fuse.\n * Each head includes both the easing function and its derivative for C¹ continuity.\n */\n\nimport type {\n EasingFn,\n NormalizedVelocity,\n NormalizedTime,\n NormalizedProgress,\n} from '../lib/types/units';\nimport type { VelocityFn } from '@penner/easing';\nimport bezierEasing from '@penner/fast-bezier-easing';\nimport {\n reverseEasingFn,\n createVelocityFn,\n solveBackStrength,\n} from '@penner/easing';\nimport { SwimRem, type SwimRemParams } from './rem/SwimRem';\nimport { SpringZeroLockedTail } from './spring';\nimport {\n solvePowerBackStrength,\n evalPowerBackIn,\n evalPowerBackVelocity,\n} from './powerBack';\n\n/**\n * Head strategy interface\n * Combines an easing function with its derivative\n */\nexport interface HeadStrategy {\n /**\n * Unique identifier for this head strategy\n */\n id: string;\n\n /**\n * Human-readable label\n */\n label: string;\n\n /**\n * The easing function (should be an \"in\" variant)\n */\n easingFn: EasingFn;\n\n /**\n * Derivative of the easing function\n */\n velocityFn: VelocityFn;\n\n /**\n * Optional configuration object (e.g., for backIn's overshoot parameter)\n */\n config?: Record<string, unknown>;\n\n /**\n * Get the natural ending velocity for this head type (optional, for pulse heads like spring)\n *\n * This is used in bridge mode to calculate the appropriate ending position and\n * bridge parameters to connect smoothly to the tail.\n *\n * @param params - Parameters for calculating natural velocity (N cycles, decay %)\n * @param tailVelocityFn - The tail's velocity function for adaptive bridge calculation\n * @param tuning - Optional tuning parameters for bridge velocity calculation\n * @returns The natural ending velocity magnitude, or undefined if not applicable\n */\n getNaturalEndVelocity?: (\n params: { bounces: number; decayPct: number },\n tailVelocityFn: VelocityFn,\n tuning?: {\n tailWeight?: number;\n freqWeight?: number;\n freqExponent?: number;\n baseMultiplier?: number;\n },\n ) => NormalizedVelocity;\n}\n\n/**\n * Create a back (overshoot) head strategy\n */\nexport function createBackHead(overshoot = 0.1): HeadStrategy {\n const strength = solveBackStrength(overshoot);\n const strengthPlus1 = strength + 1;\n\n return {\n id: 'back',\n label: `Back (${(overshoot * 100).toFixed(0)}% overshoot)`,\n easingFn: (u: number) => u * u * (strengthPlus1 * u - strength),\n velocityFn: (u: number) => u * (3 * strengthPlus1 * u - 2 * strength),\n config: { overshoot, strength },\n };\n}\n\n/**\n * Create a power head strategy with a configurable exponent\n */\nexport function createPowerHead(exponent = 2): HeadStrategy {\n const n = Math.max(0.1, exponent); // Ensure exponent is at least 0.1\n\n return {\n id: 'power',\n label: `Power (t^${n})`,\n easingFn: (u: number) => Math.pow(u, n),\n velocityFn: (u: number) => n * Math.pow(u, n - 1),\n config: { exponent: n },\n };\n}\n\n/**\n * Create a Bezier head strategy with custom control points\n *\n * Uses cubic Bezier curve with locked endpoints at (0,0) and (1,1).\n * The control points (x1, y1) and (x2, y2) define the curve shape.\n *\n * @param x1 - First control point X coordinate (0 to 1, normalized time)\n * @param y1 - First control point Y coordinate (can be outside 0-1 for overshoot, normalized progress)\n * @param x2 - Second control point X coordinate (0 to 1, normalized time)\n * @param y2 - Second control point Y coordinate (can be outside 0-1 for overshoot, normalized progress)\n */\nexport function createBezierHead(\n x1: NormalizedTime = 0.42,\n y1: NormalizedProgress = 0,\n x2: NormalizedTime = 1,\n y2: NormalizedProgress = 1,\n): HeadStrategy {\n // Create the Bezier easing function using fast-bezier-easing\n const bezier = bezierEasing(x1, y1, x2, y2);\n\n const velocityFn = createVelocityFn(bezier, {\n step: 1e-8,\n boundarySample: 0.001,\n });\n\n return {\n id: 'bezier',\n label: `Bezier (${x1.toFixed(2)}, ${y1.toFixed(2)}, ${x2.toFixed(2)}, ${y2.toFixed(2)})`,\n easingFn: bezier,\n velocityFn,\n config: { x1, y1, x2, y2 },\n };\n}\n\n/**\n * Create a Spring head strategy with oscillating anticipation\n *\n * Uses reverseEasingFn on the Spring tail to create an \"ease-in\" variant\n * with oscillating backward motion before accelerating forward.\n * This creates a unique \"spring coiling up\" effect.\n *\n * IMPORTANT: The spring head is normalized to go from 0 to 1 in its own space.\n * When used in a fuse, fuse will scale it appropriately and add bridge\n * logic to match the tail's starting velocity.\n *\n * The reversal works as follows:\n * - Build a spring tail that oscillates in the second half (after join)\n * - Reverse it so oscillations appear in the first half\n * - The bridge logic must be applied at the fuse level, not here\n *\n * @param bounces - Number of oscillations (half-cycles)\n * @param decayPct - Total decay percentage from first to last oscillation (0-100)\n */\nexport function createSpringHead(bounces = 4, decayPct = 95): HeadStrategy {\n // Build a spring tail configuration WITHOUT bridge mode\n // We'll apply bridge logic at the fuse level for spring heads\n // Use a minimal head phase (near 0) so most of the curve is the spring oscillation\n const springTailResult = SpringZeroLockedTail.build({\n joinTime: 0.0001, // Minimal head phase - almost all oscillation\n bounces: bounces,\n decayPct,\n // NO bridge - we'll handle that in fuse for spring heads\n bridge: undefined,\n });\n\n // Reverse the spring tail to create oscillating anticipation\n // reverseEasingFn: t => 1 - fn(1 - t)\n // This makes oscillations appear at the start instead of the end\n const easingFn = reverseEasingFn(springTailResult.p);\n\n // Reverse the velocity function as well\n // Derivative of reverseEasingFn: d/dt[1 - f(1-t)] = f'(1-t)\n const velocityFn = (u: number): number => {\n return springTailResult.v(1 - u);\n };\n\n return {\n id: 'spring',\n label: `Spring (${bounces} bounces, ${decayPct}% decay)`,\n easingFn,\n velocityFn,\n config: { bounces, decayPct },\n /**\n * Calculate the natural ending velocity for spring head oscillations\n *\n * Due to 180° rotation symmetry, spring head preserves the same velocity\n * magnitude as spring tail. The velocity represents the rate of change\n * at the boundary between the oscillating and non-oscillating phases.\n */\n getNaturalEndVelocity(params, tailVelocityFn, tuning) {\n if (!SpringZeroLockedTail.getNaturalStartVelocity) {\n return 0;\n }\n\n // 180° rotation preserves slope, so use same calculation\n return SpringZeroLockedTail.getNaturalStartVelocity(\n params,\n tailVelocityFn,\n tuning,\n );\n },\n };\n}\n\n/**\n * Create a Power Back head strategy\n *\n * Combines power-law acceleration with anticipation/overshoot.\n * Uses two different formulas depending on the exponent:\n *\n * For 1 < n ≤ 2: f_n(u; s) = (s+1)u^n − su (non-zero initial velocity)\n * For n > 2: f_n(u; s) = (s+1)u^n − su² (zero initial velocity)\n *\n * where n is the exponent and s is derived from the overshoot parameter.\n *\n * Special cases:\n * - overshoot = 0: pure power easing\n * - exponent ≤ 1: gracefully degrades to power (no overshoot possible)\n * - 1 < exponent ≤ 2: immediate backward motion\n * - exponent > 2: starts from rest, classic Back behavior\n *\n * @param overshoot - Anticipation depth (0-1), default 0.3 (30%)\n * @param exponent - Power exponent (0.1-10), default 3\n */\nexport function createPowerBackHead(\n overshoot = 0.3,\n exponent = 3,\n): HeadStrategy {\n // If no overshoot requested, fall back to pure power\n if (overshoot <= 0) {\n return createPowerHead(exponent);\n }\n\n // If exponent ≤ 1, overshoot not possible - fall back to pure power\n if (exponent <= 1) {\n return createPowerHead(exponent);\n }\n\n // General case: n > 1 with non-zero overshoot\n const strength = solvePowerBackStrength(overshoot, exponent);\n\n return {\n id: 'power-back',\n label: `Power Back (${(overshoot * 100).toFixed(0)}% overshoot, n=${exponent.toFixed(1)})`,\n easingFn: (u: number) => evalPowerBackIn(u, exponent, strength),\n velocityFn: (u: number) => evalPowerBackVelocity(u, exponent, strength),\n config: { overshoot, exponent, strength },\n };\n}\n\n/**\n * Create a Swim head strategy — rhythmic pulsed propulsion through viscous fluid\n */\nexport function createSwimHead(\n config?: Partial<SwimRemParams>,\n): HeadStrategy {\n const rem = SwimRem.create(config);\n return {\n id: 'swim',\n label: 'Swim',\n easingFn: rem.easeIn,\n velocityFn: rem.easeInVelocity,\n config: { ...rem.params },\n };\n}\n\n/**\n * Registry of all available head strategies\n */\nexport const headStrategies = {\n back: createBackHead,\n power: createPowerHead,\n bezier: createBezierHead,\n spring: createSpringHead,\n 'power-back': createPowerBackHead,\n swim: createSwimHead,\n} as const;\n\n/**\n * Get a head strategy by ID\n */\nexport function getHeadStrategy(\n id: keyof typeof headStrategies,\n config?: Record<string, unknown>,\n): HeadStrategy {\n switch (id) {\n case 'back': {\n const overshoot =\n typeof config?.overshoot === 'number' ? config.overshoot : 0.1;\n return createBackHead(overshoot);\n }\n case 'power': {\n const exponent = typeof config?.exponent === 'number' ? config.exponent : 2;\n return createPowerHead(exponent);\n }\n case 'bezier': {\n const x1 = typeof config?.x1 === 'number' ? config.x1 : 0.42;\n const y1 = typeof config?.y1 === 'number' ? config.y1 : 0;\n const x2 = typeof config?.x2 === 'number' ? config.x2 : 1;\n const y2 = typeof config?.y2 === 'number' ? config.y2 : 1;\n return createBezierHead(x1, y1, x2, y2);\n }\n case 'spring': {\n const bounces = typeof config?.bounces === 'number' ? config.bounces : 4;\n const decayPct =\n typeof config?.decayPct === 'number' ? config.decayPct : 95;\n return createSpringHead(bounces, decayPct);\n }\n case 'power-back': {\n const overshoot =\n typeof config?.overshoot === 'number' ? config.overshoot : 0.3;\n const exponent = typeof config?.exponent === 'number' ? config.exponent : 3;\n return createPowerBackHead(overshoot, exponent);\n }\n case 'swim':\n return createSwimHead(config as Partial<SwimRemParams>);\n default:\n return createBackHead(0.1);\n }\n}\n\n/**\n * Default head strategy (back with 10% overshoot)\n */\nexport const defaultHead = createBackHead(0.1);\n","/**\n * Pulse motion construction functions\n *\n * Pulse motions start and end at the same value (net-zero displacement: 0→peak→0).\n * These utilities construct pulse easing functions from standard easing curves.\n *\n * Key concept: A pulse is NOT a simple transform of a transition (e.g., `1 - f(t)`\n * produces a 1→0 inverted transition, not a 0→peak→0 pulse). Pulse construction\n * requires building both phases explicitly.\n *\n * The primary pattern is \"out-in\": an ease-out head (decelerating to v=0 at peak)\n * followed by an ease-in tail (accelerating from v=0 at peak back to origin).\n * This guarantees C¹ continuity at the peak automatically since both sides have v=0.\n */\n\nimport type { EasingFn } from '../lib/types/units';\nimport type { HeadType } from './types';\nimport { getHeadStrategy } from './heads';\nimport { getTailStrategy } from './tails';\n\n/**\n * Easing types that support out-in pulse construction.\n * Spring is excluded because it doesn't have v=0 at its boundaries.\n */\nexport type PulseEasingType = 'power' | 'back' | 'power-back' | 'bezier';\n\n/**\n * Configuration for easing parameters in a pulse\n */\nexport interface PulseEasingConfig {\n /** Overshoot amount for 'back' or 'power-back' type (0-1) */\n overshoot?: number;\n /** Exponent for 'power' or 'power-back' type */\n exponent?: number;\n /** Bezier control point x1 */\n x1?: number;\n /** Bezier control point y1 */\n y1?: number;\n /** Bezier control point x2 */\n x2?: number;\n /** Bezier control point y2 */\n y2?: number;\n}\n\n/**\n * Configuration for createSimplePulse\n */\nexport interface SimplePulseConfig {\n /**\n * Easing type for the head phase (ease-out: decelerates to peak).\n * Internally uses a tail strategy (ease-out function) for the head phase\n * of the pulse, since the head of a pulse rises and decelerates to a stop.\n * @default 'power'\n */\n headType?: PulseEasingType;\n\n /** Configuration for head easing */\n headConfig?: PulseEasingConfig;\n\n /**\n * Easing type for the tail phase (ease-in: accelerates from peak back to origin).\n * Internally uses a head strategy (ease-in function) for the tail phase\n * of the pulse, since the tail of a pulse accelerates away from the peak.\n * Ignored when mirror is true.\n * @default 'power'\n */\n tailType?: PulseEasingType;\n\n /** Configuration for tail easing. Ignored when mirror is true. */\n tailConfig?: PulseEasingConfig;\n\n /**\n * Join time where the peak occurs in normalized time [0,1].\n * @default 0.5\n */\n joinTime?: number;\n\n /**\n * When true, the tail phase mirrors the head phase (time-reversed),\n * creating a symmetric pulse. tailType/tailConfig are ignored.\n * @default false\n */\n mirror?: boolean;\n}\n\n/**\n * Create an out-in pulse easing function (smooth hill shape)\n *\n * Constructs a pulse that starts at 0, rises to a peak at the join point,\n * and returns smoothly to 0. C¹ continuity at the peak is guaranteed because\n * both the head (ease-out) and tail (ease-in) have zero velocity there.\n *\n * The head uses an ease-out function (fast start, decelerates to v=0 at peak).\n * The tail uses an ease-in function applied as `1 - inEasing(τ)` to create\n * the 1→0 return path (starts at v=0 at peak, accelerates back to origin).\n *\n * @param outEasing - Head easing (ease-out: 0→1 with deceleration, v=0 at end)\n * @param inEasing - Tail easing (ease-in: 0→1 with acceleration, v=0 at start).\n * Applied as `1 - inEasing(τ)` to create 1→0 return path.\n * @param joinTime - Where the peak occurs in normalized time [0,1]\n * @returns Pulse easing function: f(0)=0, f(join)≈1, f(1)=0\n *\n * @example\n * ```ts\n * // Smooth symmetric pop using quadratic curves\n * const pop = createOutInPulse(\n * (t) => 1 - (1-t)**2, // easeOutQuad (head)\n * (t) => t**2, // easeInQuad (tail)\n * 0.5 // peak at midpoint\n * );\n * // pop(0) === 0, pop(0.5) === 1, pop(1) === 0\n *\n * // Asymmetric pulse: fast rise, slow fall\n * const asymmetric = createOutInPulse(\n * (t) => 1 - (1-t)**2, // easeOutQuad (head: fast rise)\n * (t) => t**3, // easeInCubic (tail: slow return)\n * 0.3 // peak at 30% (short head, long tail)\n * );\n * ```\n */\nexport function createOutInPulse(\n outEasing: EasingFn,\n inEasing: EasingFn,\n joinTime: number,\n): EasingFn {\n // Clamp joinTime to valid range\n const join = Math.max(0.001, Math.min(0.999, joinTime));\n\n return (t: number): number => {\n if (t <= 0) return 0;\n if (t >= 1) return 0;\n\n if (t <= join) {\n // Head phase: scale time to [0,1], apply out easing (rises 0→1)\n const u = t / join;\n return outEasing(u);\n } else {\n // Tail phase: scale time to [0,1], apply reversed in easing (falls 1→0)\n const τ = (t - join) / (1 - join);\n return 1 - inEasing(τ);\n }\n };\n}\n\n/**\n * Create an out-in pulse with mirrored (symmetric) tail\n *\n * The head uses an ease-out function, and the tail is the time-reversed\n * head: `outEasing(1 - τ)`. This creates a perfectly symmetric pulse shape.\n *\n * @param outEasing - Head easing (ease-out: 0→1 with deceleration)\n * @param joinTime - Where the peak occurs in normalized time [0,1]\n * @returns Symmetric pulse easing function: f(0)=0, f(join)≈1, f(1)=0\n *\n * @example\n * ```ts\n * // Symmetric quadratic pop\n * const pop = createMirroredPulse(\n * (t) => 1 - (1-t)**2, // easeOutQuad\n * 0.5 // peak at midpoint → perfectly symmetric\n * );\n * ```\n */\nexport function createMirroredPulse(\n outEasing: EasingFn,\n joinTime: number,\n): EasingFn {\n const join = Math.max(0.001, Math.min(0.999, joinTime));\n\n return (t: number): number => {\n if (t <= 0) return 0;\n if (t >= 1) return 0;\n\n if (t <= join) {\n const u = t / join;\n return outEasing(u);\n } else {\n // Time-reversed head: outEasing(1-τ) goes from 1→0\n const τ = (t - join) / (1 - join);\n return outEasing(1 - τ);\n }\n };\n}\n\n/**\n * Normalize a pulse easing function by its maximum positive value\n *\n * Samples the pulse function and scales it so that the maximum positive\n * excursion equals exactly 1.0. This separates shape from scale:\n * the editor controls shape (normalized), CSS keyframes control scale.\n *\n * For well-formed out-in pulses (like createOutInPulse output with standard\n * easings), normalization is typically a no-op since the peak is already at 1.0.\n * This is most useful for pulses constructed from Back easings with overshoot,\n * where the curve may exceed 1.0 or have irregular peaks.\n *\n * @param pulseFn - The pulse easing function to normalize\n * @param sampleCount - Number of samples for finding the maximum (default: 200)\n * @returns Normalized pulse function where max positive value = 1.0\n *\n * @example\n * ```ts\n * // Back easing may overshoot beyond 1.0\n * const rawPulse = createOutInPulse(backOut, backIn, 0.5);\n * const normalized = normalizePulse(rawPulse);\n * // normalized peak is exactly 1.0, overshoots are proportionally scaled\n * ```\n */\nexport function normalizePulse(\n pulseFn: EasingFn,\n sampleCount = 200,\n): EasingFn {\n // Find max positive value by sampling\n let maxPos = 0;\n for (let i = 0; i <= sampleCount; i++) {\n const t = i / sampleCount;\n maxPos = Math.max(maxPos, pulseFn(t));\n }\n\n // If no positive values found, return as-is (degenerate case)\n if (maxPos <= 0) return pulseFn;\n\n // If already normalized (max ≈ 1.0), return as-is to avoid unnecessary wrapping\n if (Math.abs(maxPos - 1.0) < 1e-10) return pulseFn;\n\n // Return scaled version\n const scale = 1 / maxPos;\n return (t: number): number => pulseFn(t) * scale;\n}\n\n/**\n * Normalize a pulse easing function so its value at the join point equals 1.0\n *\n * Unlike normalizePulse (which normalizes by maximum positive value),\n * this preserves overshoot above 1.0 as intentional motion. The 1.0 line\n * represents the \"target position\" (the CSS keyframe value), and overshoots\n * beyond it are meaningful — the element briefly exceeds its target before\n * returning, which is the expressive quality of Back easings.\n *\n * For well-formed out-in pulses (built from standard 0→1 easings),\n * the join value is already 1.0 by construction, so this is a no-op.\n * It's useful for pulses built from non-standard or composed easings\n * where the join value may differ from 1.0.\n *\n * Comparison:\n * - normalizePulse: max value → 1.0 (overshoot absorbed into normalization)\n * - normalizePulseToJoin: join value → 1.0 (overshoot preserved above 1.0)\n *\n * @param pulseFn - The pulse easing function to normalize\n * @param joinTime - The join time where the pulse peaks\n * @returns Normalized pulse function where f(joinTime) = 1.0\n *\n * @example\n * ```ts\n * // Back pulse: join at 1.0, overshoots to 1.15\n * const backPulse = createSimplePulse({\n * headType: 'back', headConfig: { overshoot: 0.2 },\n * mirror: true, joinTime: 0.5,\n * });\n * const normalized = normalizePulseToJoin(backPulse, 0.5);\n * // normalized(0.5) === 1.0, overshoots above 1.0 preserved\n * ```\n */\nexport function normalizePulseToJoin(\n pulseFn: EasingFn,\n joinTime: number,\n): EasingFn {\n const joinValue = pulseFn(joinTime);\n\n // If join value is zero or negative, can't normalize (degenerate)\n if (joinValue <= 0) return pulseFn;\n\n // If already normalized (join value ≈ 1.0), return as-is\n if (Math.abs(joinValue - 1.0) < 1e-10) return pulseFn;\n\n // Scale so that f(joinTime) = 1.0\n const scale = 1 / joinValue;\n return (t: number): number => pulseFn(t) * scale;\n}\n\n/**\n * Configuration for spring/bounce return functions\n */\nexport interface PulseReturnConfig {\n /** Number of half-cycles (spring) or bounces. @default 4 */\n bounces?: number;\n /** Total decay percentage from first to last oscillation (0-100). @default 90 */\n decay?: number;\n}\n\n/**\n * Create a spring return function: 1→0 with damped oscillation\n *\n * Models a spring released from displacement — starts at position 1 (peak),\n * oscillates with decreasing amplitude, and settles at 0 (origin).\n * Uses the same bounces/decay parameterization as fuse.spring().\n *\n * The initial velocity is exactly zero — the (k/ω)*sin correction term\n * cancels the -k contribution from the exponential envelope — making\n * this perfectly C¹-compatible with an ease-out head in a pulse.\n *\n * Physics: damped harmonic oscillator released from rest:\n * `e^(-kt) * [cos(ωt) + (k/ω)*sin(ωt)]`\n * The (k/ω)*sin term cancels the -k velocity from the exponential envelope,\n * giving exact v=0 at t=0 regardless of decay rate.\n * - ω = Nπ (N half-cycles in unit time)\n * - k derived from decay rate (same formula as fuse Spring)\n *\n * @param config - Spring parameters (bounces, decay)\n * @returns EasingFn going from 1→≈0 with damped spring oscillation\n *\n * @example\n * ```ts\n * const springReturn = createSpringReturn({ bounces: 4, decay: 90 });\n * // springReturn(0) ≈ 1, springReturn(1) ≈ 0\n * // Oscillates around 0 with decreasing amplitude\n *\n * // Use in a pulse:\n * const springPulse = createPulseWithReturn(quad.out, springReturn, 0.3);\n * ```\n */\nexport function createSpringReturn(config: PulseReturnConfig = {}): EasingFn {\n const { bounces = 4, decay = 90 } = config;\n const N = Math.max(0.5, bounces);\n\n // Per-half-cycle amplitude ratio (same formula as fuse Spring)\n const finalRatio = Math.max(0, 1 - decay / 100);\n const r =\n N <= 1\n ? finalRatio\n : finalRatio === 0\n ? 0\n : Math.pow(finalRatio, 1 / (N - 1));\n\n // Angular frequency: N half-cycles in unit time\n const omega = N * Math.PI;\n\n // Decay rate from amplitude ratio\n const k = r <= 0 ? 50 : (omega / Math.PI) * -Math.log(r);\n\n // Ratio k/ω for the sine correction term\n const kOverOmega = k / omega;\n\n // Endpoint value for linear correction (ensure exact zero at t=1)\n // f_raw(1) = e^(-k) * [cos(ω) + (k/ω)*sin(ω)]\n const endRaw =\n Math.exp(-k) * (Math.cos(omega) + kOverOmega * Math.sin(omega));\n\n return (t: number): number => {\n if (t <= 0) return 1;\n if (t >= 1) return 0;\n\n // Damped harmonic oscillator released from rest:\n // f(t) = e^(-kt) * [cos(ωt) + (k/ω)*sin(ωt)]\n // f(0) = 1, f'(0) = 0 (exact)\n const envelope = Math.exp(-k * t);\n const raw = envelope * (Math.cos(omega * t) + kOverOmega * Math.sin(omega * t));\n\n // Linear correction so f(1) = 0 exactly\n return raw - endRaw * t;\n };\n}\n\n/**\n * Create a bounce return function: 1→0 with parabolic bouncing\n *\n * Models a ball dropped from height 1 onto a floor at 0 — starts at rest\n * at position 1, falls under gravity, bounces off the floor with decreasing\n * energy, and settles at 0. Uses the same bounces/decay parameterization\n * as fuse.bounce().\n *\n * The initial velocity is exactly zero (free fall from rest), making this\n * perfectly C¹-compatible with an ease-out head in a pulse.\n *\n * Physics: parabolic trajectories under constant gravity, with\n * velocity reduced by restitution coefficient at each impact.\n *\n * @param config - Bounce parameters (bounces, decay)\n * @returns EasingFn going from 1→0 with bounce physics\n *\n * @example\n * ```ts\n * const bounceReturn = createBounceReturn({ bounces: 4, decay: 90 });\n * // bounceReturn(0) = 1, bounceReturn(1) = 0\n * // Drops and bounces on floor at 0 with decreasing height\n *\n * // Use in a pulse:\n * const bouncePulse = createPulseWithReturn(quad.out, bounceReturn, 0.3);\n * ```\n */\nexport function createBounceReturn(config: PulseReturnConfig = {}): EasingFn {\n const { bounces = 4, decay = 90 } = config;\n const N = Math.max(1, Math.round(bounces));\n\n // Per-bounce restitution coefficient (same formula as fuse Bounce)\n // Height ratio = r², velocity ratio = r\n const finalRatio = Math.max(0, 1 - decay / 100);\n const r =\n N <= 1\n ? Math.sqrt(finalRatio)\n : finalRatio === 0\n ? 0\n : Math.pow(finalRatio, 1 / (2 * (N - 1)));\n\n if (r === 0) {\n // No bouncing — just fall to floor\n // Parabola: 1 - t² (reaches 0 at t=1)\n return (t: number): number => {\n if (t <= 0) return 1;\n if (t >= 1) return 0;\n return 1 - t * t;\n };\n }\n\n // Segment timing: first drop + N bounces\n // First drop: h = 1 - 0.5*g*t², reaches floor when t_drop = sqrt(2/g)\n // Bounce n: flight time = 2*v_n/g = 2*r^n * v_impact/g\n // where v_impact = sqrt(2g) (velocity at first impact)\n //\n // Total time = t_drop + sum_{n=1..N} (2*r^n * t_drop)\n // = t_drop * [1 + 2*r*(1-r^N)/(1-r)]\n\n const geomSum =\n Math.abs(1 - r) < 1e-6\n ? 1 + 2 * N\n : 1 + (2 * r * (1 - Math.pow(r, N))) / (1 - r);\n\n // Solve for g such that total time = 1\n // t_drop = sqrt(2/g), total = t_drop * geomSum = 1\n // → sqrt(2/g) = 1/geomSum → g = 2*geomSum²\n const g = 2 * geomSum * geomSum;\n const tDrop = 1 / geomSum;\n const vImpact = g * tDrop; // = sqrt(2g)\n\n // Build segment edges\n const edges: number[] = [0, tDrop];\n for (let n = 1; n <= N; n++) {\n const flightTime = (2 * Math.pow(r, n) * vImpact) / g;\n edges.push(edges[edges.length - 1] + flightTime);\n }\n // Ensure last edge is exactly 1\n edges[edges.length - 1] = 1;\n\n return (t: number): number => {\n if (t <= 0) return 1;\n if (t >= 1) return 0;\n\n // First segment: free fall from rest at height 1\n if (t <= tDrop) {\n return 1 - 0.5 * g * t * t;\n }\n\n // Find which bounce segment we're in\n let seg = 0;\n for (let i = 1; i < edges.length - 1; i++) {\n if (t <= edges[i + 1]) {\n seg = i;\n break;\n }\n }\n if (seg === 0) seg = edges.length - 2; // fallback to last segment\n\n // Bounce n (1-indexed): launches from floor at 0 with v_n = r^n * vImpact\n const n = seg;\n const v_n = Math.pow(r, n) * vImpact;\n const dt = t - edges[seg];\n\n // Parabolic: h = v_n*dt - 0.5*g*dt²\n return v_n * dt - 0.5 * g * dt * dt;\n };\n}\n\n/**\n * Create a pulse using an ease-out head and a return function (1→0) for the tail\n *\n * More general than createOutInPulse: the tail is a \"return function\" that\n * goes directly from 1 to 0, rather than an ease-in that gets inverted.\n * This is the natural API for spring and bounce return tails.\n *\n * @param headEasing - Head easing (ease-out: 0→1, decelerating to v≈0 at peak)\n * @param returnFn - Tail return function (1→0, settling at origin)\n * @param joinTime - Where the peak occurs in normalized time [0,1]\n * @returns Pulse easing function: f(0)=0, f(join)≈1, f(1)≈0\n *\n * @example\n * ```ts\n * // Quad rise → spring return (C¹ at peak)\n * const springPulse = createPulseWithReturn(\n * quad.out,\n * createSpringReturn({ bounces: 4, decay: 90 }),\n * 0.3,\n * );\n *\n * // Linear rise → bounce drop (C¹ at peak)\n * const bouncePulse = createPulseWithReturn(\n * linear,\n * createBounceReturn({ bounces: 4, decay: 85 }),\n * 0.2,\n * );\n * ```\n */\nexport function createPulseWithReturn(\n headEasing: EasingFn,\n returnFn: EasingFn,\n joinTime: number,\n): EasingFn {\n const join = Math.max(0.001, Math.min(0.999, joinTime));\n\n return (t: number): number => {\n if (t <= 0) return 0;\n if (t >= 1) return 0;\n\n if (t <= join) {\n // Head phase: scale time to [0,1], apply out easing (rises 0→1)\n const u = t / join;\n return headEasing(u);\n } else {\n // Tail phase: scale time to [0,1], apply return function (falls 1→0)\n const τ = (t - join) / (1 - join);\n return returnFn(τ);\n }\n };\n}\n\n/**\n * Create a simple out-in pulse using named easing types\n *\n * Convenience function that wraps createOutInPulse with the existing\n * head/tail strategy system. Uses head/tail terminology consistent with\n * fuse transitions:\n * - Head = first phase (ease-out, rises to peak) — uses tail strategies internally\n * - Tail = second phase (ease-in, returns to origin) — uses head strategies internally\n *\n * The out-in pattern guarantees C¹ continuity at the peak because both\n * the ease-out head and ease-in tail have zero velocity there.\n *\n * @param config - Configuration for the pulse\n * @returns Pulse easing function: f(0)=0, f(peak)=1, f(1)=0\n *\n * @example\n * ```ts\n * // Simple quadratic pop (symmetric)\n * const pop = createSimplePulse({\n * headType: 'power',\n * headConfig: { exponent: 2 },\n * mirror: true,\n * joinTime: 0.5,\n * });\n *\n * // Heartbeat: back overshoot on both phases\n * const heartbeat = createSimplePulse({\n * headType: 'back',\n * headConfig: { overshoot: 0.15 },\n * tailType: 'back',\n * tailConfig: { overshoot: 0.1 },\n * joinTime: 0.4,\n * });\n *\n * // Asymmetric: fast linear rise, slow cubic fall\n * const asymmetric = createSimplePulse({\n * headType: 'power',\n * headConfig: { exponent: 1 },\n * tailType: 'power',\n * tailConfig: { exponent: 3 },\n * joinTime: 0.25,\n * });\n * ```\n */\nexport function createSimplePulse(config: SimplePulseConfig = {}): EasingFn {\n const {\n headType = 'power',\n headConfig,\n tailType = 'power',\n tailConfig,\n joinTime = 0.5,\n mirror = false,\n } = config;\n\n // Get head easing (ease-out from tail strategies, since pulse head decelerates to peak)\n const headTail = getTailStrategy(headType, headConfig);\n\n if (mirror) {\n // Symmetric pulse: tail is time-reversed head\n return createMirroredPulse(headTail.easingFn, joinTime);\n }\n\n // Get tail easing (ease-in from head strategies, since pulse tail accelerates from peak)\n // Exclude 'spring' type since it doesn't have v=0 at start\n const tailHead = getHeadStrategy(\n tailType as Exclude<HeadType, 'spring'>,\n tailConfig as Record<string, unknown> | undefined,\n );\n\n return createOutInPulse(headTail.easingFn, tailHead.easingFn, joinTime);\n}\n\n/**\n * Pulse easing family\n *\n * Provides convenient access to pulse easing construction.\n * Pulse easings start and end at the same value (net-zero displacement).\n *\n * @example\n * ```ts\n * import { pulse } from '@penner/responsive-easing';\n *\n * // Simple symmetric pop\n * const pop = pulse.simple({\n * headType: 'power',\n * headConfig: { exponent: 2 },\n * mirror: true,\n * });\n *\n * // Heartbeat with back easings\n * const heartbeat = pulse.simple({\n * headType: 'back',\n * tailType: 'back',\n * joinTime: 0.4,\n * });\n * ```\n */\nexport const pulse = {\n /**\n * Create a simple out-in pulse using named easing types\n */\n simple: createSimplePulse,\n\n /**\n * Create an out-in pulse from raw easing functions\n */\n outIn: createOutInPulse,\n\n /**\n * Create a mirrored (symmetric) pulse from a single out easing\n */\n mirrored: createMirroredPulse,\n\n /**\n * Normalize a pulse by its maximum positive value\n */\n normalize: normalizePulse,\n\n /**\n * Normalize a pulse so its value at the join point equals 1.0\n */\n normalizeToJoin: normalizePulseToJoin,\n\n /**\n * Create a pulse using a head easing and a return function (1→0) tail\n */\n withReturn: createPulseWithReturn,\n\n /**\n * Create a spring return function (1→0 with damped oscillation)\n */\n springReturn: createSpringReturn,\n\n /**\n * Create a bounce return function (1→0 with parabolic bouncing)\n */\n bounceReturn: createBounceReturn,\n} as const;\n","/**\n * Bezier-to-Bezier Join Constraint Utilities\n *\n * When fusing two Bezier curves with the custom join strategy:\n * - joinHeight = joinTime (diagonal constraint)\n * - Symmetric smooth node: control points use simple coordinate inversion in local [0,1] space\n * - tail_p1 = (1 - head_p2.x, 1 - head_p2.y) and vice versa\n * - C¹ continuity via collinear control points (shared slope)\n * - Predictable UI: slider values mirror each other\n *\n * These utilities help UI implementations maintain the constraint bidirectionally:\n * - Moving head p2 automatically calculates tail p1\n * - Moving tail p1 automatically calculates head p2\n */\n\n/**\n * Control point in 2D space\n */\nexport interface Point2D {\n x: number;\n y: number;\n}\n\n/**\n * Result from constraint calculation\n */\nexport interface ConstraintResult {\n /** Whether the calculation was successful */\n isValid: boolean;\n /** The calculated control point */\n point?: Point2D;\n /** Optional warning message */\n warning?: string;\n}\n\n/**\n * Calculate tail p1 from head p2 to maintain symmetric smooth node\n *\n * Given the head's second control point (p2), this calculates where the tail's\n * first control point (p1) should be placed to maintain a symmetric smooth Bezier node.\n *\n * Uses simple coordinate symmetry in normalized [0,1] space:\n * - tail_p1 = (1 - head_p2.x, 1 - head_p2.y)\n * - Control points are collinear (C¹ continuity)\n * - Slider values mirror each other (predictable UI)\n * - Creates \"smooth\" or \"symmetrical\" node like in design tools\n *\n * @param headP2 - Head's second control point in normalized coordinates [0,1]\n * @param _joinTime - Join point time (not used in calculation, but kept for API consistency)\n * @returns Calculated tail p1 position\n *\n * @example\n * ```ts\n * // User drags head p2 to (0.8, 0.9)\n * const result = calculateTailP1FromHeadP2(\n * { x: 0.8, y: 0.9 },\n * 0.6\n * );\n * // Result: tail p1 = (0.2, 0.1) - simple inversion\n * // Update UI: set tail p1 sliders to result.point.x, result.point.y\n * ```\n */\nexport function calculateTailP1FromHeadP2(\n headP2: Point2D,\n _joinTime: number,\n): ConstraintResult {\n // Simple coordinate symmetry in normalized [0,1] space\n // This creates mirrored slider values for predictable UI\n return {\n isValid: true,\n point: {\n x: 1 - headP2.x,\n y: 1 - headP2.y,\n },\n };\n}\n\n/**\n * Calculate head p2 from tail p1 to maintain symmetric smooth node\n *\n * Given the tail's first control point (p1), this calculates where the head's\n * second control point (p2) should be placed to maintain a symmetric smooth Bezier node.\n *\n * Uses simple coordinate symmetry in normalized [0,1] space:\n * - head_p2 = (1 - tail_p1.x, 1 - tail_p1.y)\n * - Control points are collinear (C¹ continuity)\n * - Slider values mirror each other (predictable UI)\n * - Creates \"smooth\" or \"symmetrical\" node like in design tools\n *\n * @param tailP1 - Tail's first control point in normalized coordinates [0,1]\n * @param _joinTime - Join point time (not used in calculation, but kept for API consistency)\n * @returns Calculated head p2 position\n *\n * @example\n * ```ts\n * // User drags tail p1 to (0.3, 0.7)\n * const result = calculateHeadP2FromTailP1(\n * { x: 0.3, y: 0.7 },\n * 0.6\n * );\n * // Result: head p2 = (0.7, 0.3) - simple inversion\n * // Update UI: set head p2 sliders to result.point.x, result.point.y\n * ```\n */\nexport function calculateHeadP2FromTailP1(\n tailP1: Point2D,\n _joinTime: number,\n): ConstraintResult {\n // Simple coordinate symmetry in normalized [0,1] space\n // This creates mirrored slider values for predictable UI\n return {\n isValid: true,\n point: {\n x: 1 - tailP1.x,\n y: 1 - tailP1.y,\n },\n };\n}\n\n/**\n * Get the common slope at the join point\n *\n * Useful for visualization or debugging.\n *\n * @param headP2 - Head's second control point in normalized coordinates [0,1]\n * @param joinTime - Join point time\n * @returns The slope value (dy/dx)\n */\nexport function getJoinSlope(headP2: Point2D, joinTime: number): number {\n const joinHeight = joinTime;\n const head_p2x_global = headP2.x * joinTime;\n const head_p2y_global = headP2.y * joinHeight;\n const dx = joinTime - head_p2x_global;\n const dy = joinHeight - head_p2y_global;\n\n if (Math.abs(dx) < 1e-8) {\n return Number.POSITIVE_INFINITY;\n }\n\n return dy / dx;\n}\n\n/**\n * Verify that head p2 and tail p1 maintain C¹ continuity\n *\n * Useful for validation in tests or UI.\n *\n * @param headP2 - Head's second control point in normalized coordinates [0,1]\n * @param tailP1 - Tail's first control point in normalized coordinates [0,1]\n * @param joinTime - Join point time\n * @param tolerance - Acceptable slope difference (default: 1e-6)\n * @returns True if the slopes match within tolerance\n */\nexport function verifyContinuity(\n headP2: Point2D,\n tailP1: Point2D,\n joinTime: number,\n tolerance = 1e-6,\n): boolean {\n const joinHeight = joinTime;\n\n // Calculate slope from head p2 to join point\n const head_p2x_global = headP2.x * joinTime;\n const head_p2y_global = headP2.y * joinHeight;\n const head_dx = joinTime - head_p2x_global;\n const head_dy = joinHeight - head_p2y_global;\n\n // Calculate slope from join point to tail p1\n const tail_p1x_global = joinTime + tailP1.x * (1 - joinTime);\n const tail_p1y_global = joinHeight + tailP1.y * (1 - joinHeight);\n const tail_dx = tail_p1x_global - joinTime;\n const tail_dy = tail_p1y_global - joinHeight;\n\n // Handle vertical slopes\n const headVertical = Math.abs(head_dx) < tolerance;\n const tailVertical = Math.abs(tail_dx) < tolerance;\n\n if (headVertical && tailVertical) {\n return true; // Both vertical\n }\n\n if (headVertical || tailVertical) {\n return false; // One vertical, one not\n }\n\n // Compare slopes\n const headSlope = head_dy / head_dx;\n const tailSlope = tail_dy / tail_dx;\n\n return Math.abs(headSlope - tailSlope) < tolerance;\n}\n","/**\n * PowerRem — Power easing Responsive Easing Module.\n *\n * f(u) = u^n where n is the exponent parameter.\n * Supports both ease-in and ease-out, transition and pulse modes.\n */\n\nimport type { NormalizedTime, NormalizedProgress } from '../../lib/types/units';\nimport type { KnobSpec, NumberKnobSpec } from '../../lib/rem/core/knobs';\nimport {\n EasingRem,\n type EasingRemMetadata,\n type VelocityBoundary,\n} from './EasingRem';\n\nexport interface PowerRemParams {\n readonly exponent: number;\n}\n\nexport class PowerRem extends EasingRem<PowerRemParams> {\n static readonly EXPONENT_KNOB: NumberKnobSpec = {\n key: 'exponent',\n label: 'Exponent',\n type: 'number',\n default: 2,\n min: 0.1,\n max: 10,\n step: 0.01,\n isPrimary: true,\n } as const;\n\n readonly kind = 'power' as const;\n\n readonly metadata: EasingRemMetadata = {\n variants: { easeIn: true, easeOut: true },\n modes: { transition: true, pulse: true },\n };\n\n readonly knobSpecs: readonly KnobSpec[] = [PowerRem.EXPONENT_KNOB];\n\n constructor(params: PowerRemParams) {\n super(params);\n }\n\n get easeInBoundary(): VelocityBoundary {\n const n = this.params.exponent;\n // For n > 1: starts at zero velocity (anchored), ends at nonzero\n // For n <= 1: starts at nonzero (steep or linear start)\n return {\n start: n > 1 ? 'zero' : 'nonzero',\n end: 'nonzero',\n };\n }\n\n easeIn = (u: NormalizedTime): NormalizedProgress => {\n return Math.pow(u, this.params.exponent);\n };\n\n easeInVelocity = (u: NormalizedTime): number => {\n const n = this.params.exponent;\n return n * Math.pow(u, n - 1);\n };\n\n // Analytical: d/du[1 - easeIn(1-u)] = easeInVelocity(1-u)\n easeOutVelocity = (u: NormalizedTime): number => {\n return this.easeInVelocity(1 - u);\n };\n\n with(overrides: Partial<PowerRemParams>): PowerRem {\n return new PowerRem({\n ...this.params,\n ...overrides,\n exponent: Math.max(0.1, overrides.exponent ?? this.params.exponent),\n });\n }\n\n static create(init: Partial<PowerRemParams> = {}): PowerRem {\n const exponent: number = Math.max(0.1, init.exponent ?? 2);\n return new PowerRem({ exponent });\n }\n}\n","/**\n * Named constants for fuse easing calculations\n *\n * These replace magic numbers throughout the codebase for better maintainability.\n */\n\n/**\n * Default join time (50% through the timeline)\n */\nexport const DEFAULT_JOIN_TIME = 0.5;\n\n/**\n * Default number of oscillations for pulse easings\n */\nexport const DEFAULT_BOUNCES = 4;\n\n/**\n * Default decay percentage (0-100)\n * Represents total decay from first to last oscillation\n */\nexport const DEFAULT_DECAY_PERCENT = 95;\n\n/**\n * Default overshoot for back easing\n */\nexport const DEFAULT_BACK_OVERSHOOT = 0.1;\n\n/**\n * Default exponent for power easing\n */\nexport const DEFAULT_POWER_EXPONENT = 2;\n\n/**\n * Threshold for detecting join at start boundary\n */\nexport const JOIN_AT_START_THRESHOLD = 0.0001;\n\n/**\n * Threshold for detecting join at end boundary\n */\nexport const JOIN_AT_END_THRESHOLD = 0.9999;\n\n/**\n * Minimum valid join time\n */\nexport const MIN_JOIN_TIME = 0;\n\n/**\n * Maximum valid join time\n */\nexport const MAX_JOIN_TIME = 1;\n\n/**\n * Minimum bounces value\n */\nexport const MIN_BOUNCES = 1;\n\n/**\n * Maximum bounces value\n */\nexport const MAX_BOUNCES = 10;\n\n/**\n * Minimum decay percentage\n */\nexport const MIN_DECAY_PERCENT = 0;\n\n/**\n * Maximum decay percentage\n */\nexport const MAX_DECAY_PERCENT = 100;\n\n/**\n * Floating point epsilon for comparisons\n */\nexport const EPSILON = 1e-10;\n\n/**\n * Bridge tuning defaults\n */\nexport const DEFAULT_BRIDGE_TUNING = {\n headWeight: 1.0,\n freqWeight: 1.0,\n freqExponent: 1.0,\n baseMultiplier: 1.0,\n} as const;\n","/**\n * Edge case detection and handling for fuse join points\n *\n * These pure functions handle special cases when the join point\n * is at or very near the timeline boundaries (0 or 1).\n */\n\nimport type { EasingFn } from '../../lib/types/units';\nimport {\n JOIN_AT_START_THRESHOLD,\n JOIN_AT_END_THRESHOLD,\n MIN_JOIN_TIME,\n MAX_JOIN_TIME,\n} from './constants';\n\n/**\n * Result of boundary detection\n */\nexport interface BoundaryDetection {\n /** True if join is at or very close to start (0) */\n atStart: boolean;\n /** True if join is at or very close to end (1) */\n atEnd: boolean;\n}\n\n/**\n * Detect if join point is at a timeline boundary\n *\n * Uses epsilon-based comparison for floating point safety.\n *\n * @param joinTime - Join point in normalized time [0,1]\n * @returns Boundary detection result\n *\n * @example\n * ```ts\n * isJoinAtBoundary(0) // { atStart: true, atEnd: false }\n * isJoinAtBoundary(1) // { atStart: false, atEnd: true }\n * isJoinAtBoundary(0.0001) // { atStart: true, atEnd: false }\n * isJoinAtBoundary(0.5) // { atStart: false, atEnd: false }\n * ```\n */\nexport function isJoinAtBoundary(joinTime: number): BoundaryDetection {\n // Handle NaN\n if (Number.isNaN(joinTime)) {\n return { atStart: false, atEnd: false };\n }\n\n // Clamp to valid range for boundary detection\n const clamped = Math.max(MIN_JOIN_TIME, Math.min(MAX_JOIN_TIME, joinTime));\n\n return {\n atStart: clamped <= JOIN_AT_START_THRESHOLD,\n atEnd: clamped >= JOIN_AT_END_THRESHOLD,\n };\n}\n\n/**\n * Create a fallback easing for edge cases\n *\n * When join is at a boundary, we need to return just the head or tail,\n * not a fused combination.\n *\n * @param atStart - If true, join is at start (return tail only)\n * @param atEnd - If true, join is at end (return head only)\n * @param headEasing - Head easing function\n * @param tailEasing - Tail easing function\n * @returns Appropriate fallback easing\n *\n * @example\n * ```ts\n * // Join at start → use tail only\n * createEdgeCaseFallback(true, false, head, tail) // returns tail\n *\n * // Join at end → use head only\n * createEdgeCaseFallback(false, true, head, tail) // returns head\n *\n * // Not at boundary → no fallback\n * createEdgeCaseFallback(false, false, head, tail) // returns null\n * ```\n */\nexport function createEdgeCaseFallback(\n atStart: boolean,\n atEnd: boolean,\n headEasing: EasingFn,\n tailEasing: EasingFn,\n): EasingFn | null {\n // Join at start → entire timeline is tail phase\n if (atStart) {\n return tailEasing;\n }\n\n // Join at end → entire timeline is head phase\n if (atEnd) {\n return headEasing;\n }\n\n // Not at boundary → no fallback needed\n return null;\n}\n","/**\n * Join height calculation for C¹ continuity\n *\n * Calculates the intermediate height (y-value) at the join point where\n * the head and tail phases meet with matching velocities (C¹ continuity).\n *\n * This ensures a smooth transition without abrupt changes in slope.\n */\n\n/**\n * Configuration for join height calculation\n */\nexport interface JoinHeightConfig {\n /**\n * Join time coordinate (0-1) where head ends and tail begins\n */\n joinTime: number;\n\n /**\n * Head easing velocity at its end point (u=1 in head's local time)\n * This represents the slope of the head easing at the join point.\n * Can be negative - creates a natural reflection/dip-and-recover effect.\n */\n headVelocityAtEnd: number;\n\n /**\n * Tail easing velocity at its start point (u=0 in tail's local time)\n * This represents the slope of the tail easing at the join point.\n * Must be positive (ease-out behavior).\n */\n tailVelocityAtStart: number;\n}\n\n/**\n * Result from join height calculation\n */\nexport interface JoinHeightResult {\n /**\n * The calculated join height (y-value) at the join point\n * Range: typically (0, 1) but can be outside for certain velocity ratios\n */\n joinHeight: number;\n\n /**\n * Whether the calculation is valid (no division by zero or invalid inputs)\n */\n isValid: boolean;\n\n /**\n * Optional warning message if the result is unusual but not invalid\n * e.g., joinHeight outside [0,1] range\n */\n warning?: string;\n}\n\n/**\n * Calculate join height for C¹ continuity between head and tail\n *\n * For a fused easing with head and tail phases:\n * - Head phase: p(u) = joinHeight * headEasing(u/joinTime) for u ∈ [0, joinTime]\n * - Tail phase: p(u) = joinHeight + (1-joinHeight) * tailEasing(τ) where τ = (u-joinTime)/(1-joinTime)\n *\n * For C¹ continuity, velocities must match at the join point:\n * joinHeight * headVelocity / joinTime = (1-joinHeight) * tailVelocity / (1-joinTime)\n *\n * Solving for joinHeight:\n * joinHeight = (joinTime * tailVelocity) / [headVelocity * (1-joinTime) + joinTime * tailVelocity]\n *\n * **Negative Head Velocity:**\n * When headVelocityAtEnd < 0 (downward slope), this creates a natural reflection effect:\n * - The curve dips below the join height at the transition point\n * - The tail then \"pulls back up\" creating a valley/dip-and-recover shape\n * - This is mathematically valid and produces smooth C¹ continuity\n *\n * @param config - Configuration including join time and velocities\n * @returns Join height result with validity flag and optional warning\n *\n * @example\n * ```ts\n * // Standard positive slope connection\n * const result1 = calculateJoinHeightForContinuity({\n * joinTime: 0.6,\n * headVelocityAtEnd: 3.0, // Head ends with positive velocity\n * tailVelocityAtStart: 2.0, // Tail starts with positive velocity\n * });\n *\n * // Reflection/dip behavior with negative head slope\n * const result2 = calculateJoinHeightForContinuity({\n * joinTime: 0.6,\n * headVelocityAtEnd: -2.0, // Head ends with negative velocity (downward)\n * tailVelocityAtStart: 2.0, // Tail still pulls upward\n * });\n * // result2.joinHeight may be > 1, creating natural dip effect\n *\n * if (result.isValid) {\n * console.log(`Join height: ${result.joinHeight}`);\n * // Use result.joinHeight to scale head and tail phases\n * }\n * ```\n */\nexport function calculateJoinHeightForContinuity(\n config: JoinHeightConfig,\n): JoinHeightResult {\n const { joinTime, headVelocityAtEnd, tailVelocityAtStart } = config;\n\n // Validate inputs\n if (joinTime <= 0 || joinTime >= 1) {\n return {\n joinHeight: 0,\n isValid: false,\n warning: `Invalid joinTime: ${joinTime}. Must be in range (0, 1).`,\n };\n }\n\n // Zero head velocity is invalid (horizontal line - no direction)\n if (headVelocityAtEnd === 0) {\n return {\n joinHeight: 0,\n isValid: false,\n warning: `Invalid headVelocityAtEnd: ${headVelocityAtEnd}. Must be non-zero.`,\n };\n }\n\n // Note: Negative headVelocityAtEnd is now supported (creates reflection/dip behavior)\n // Only reject if tail velocity is non-positive (tail must be ease-out)\n if (tailVelocityAtStart <= 0) {\n return {\n joinHeight: 0,\n isValid: false,\n warning: `Invalid tailVelocityAtStart: ${tailVelocityAtStart}. Must be positive.`,\n };\n }\n\n // Calculate denominator: headVelocity * (1-joinTime) + joinTime * tailVelocity\n const denominator =\n headVelocityAtEnd * (1 - joinTime) + joinTime * tailVelocityAtStart;\n\n // Check for division by zero (should not happen with positive velocities)\n if (Math.abs(denominator) < 1e-10) {\n return {\n joinHeight: 0,\n isValid: false,\n warning: 'Denominator too close to zero. Cannot calculate join height.',\n };\n }\n\n // Calculate join height: (joinTime * tailVelocity) / denominator\n const joinHeight = (joinTime * tailVelocityAtStart) / denominator;\n\n // Check if join height is in typical range\n let warning: string | undefined;\n if (joinHeight < 0) {\n warning = `Join height is negative (${joinHeight.toFixed(4)}). This may indicate incompatible velocities.`;\n } else if (joinHeight > 1) {\n // joinHeight > 1 can occur naturally with negative head velocity (creates dip-and-recover)\n if (headVelocityAtEnd < 0) {\n warning = `Join height exceeds 1 (${joinHeight.toFixed(4)}) due to negative head slope. This creates a natural reflection/dip effect.`;\n } else {\n warning = `Join height exceeds 1 (${joinHeight.toFixed(4)}). This may cause overshoot beyond target.`;\n }\n } else if (headVelocityAtEnd < 0 && joinHeight <= 1) {\n // Inform about reflection behavior when head slope is negative\n warning = `Head ends with negative slope (${headVelocityAtEnd.toFixed(4)}). Curve will dip below join height before tail recovers.`;\n }\n\n return {\n joinHeight,\n isValid: true,\n warning,\n };\n}\n\n/**\n * Calculate join height with simplified parameters (direct velocity values)\n *\n * This is a convenience function that extracts velocities from easing functions\n * and calls the main calculation.\n *\n * @param joinTime - Join time coordinate (0-1)\n * @param headVelocityFn - Head easing velocity function\n * @param tailVelocityFn - Tail easing velocity function\n * @returns Join height result\n *\n * @example\n * ```ts\n * const result = calculateJoinHeight(\n * 0.6,\n * (u) => 2 * u, // Linear head: velocity = 2 at u=1\n * (u) => 3 * (1 - u), // Linear tail: velocity = 3 at u=0\n * );\n * ```\n */\nexport function calculateJoinHeight(\n joinTime: number,\n headVelocityFn: (u: number) => number,\n tailVelocityFn: (u: number) => number,\n): JoinHeightResult {\n const headVelocityAtEnd = headVelocityFn(1);\n const tailVelocityAtStart = tailVelocityFn(0);\n\n return calculateJoinHeightForContinuity({\n joinTime,\n headVelocityAtEnd,\n tailVelocityAtStart,\n });\n}\n","/**\n * Calculate bridge parameters for connecting head to pulse tail\n *\n * For pulse tails (spring, bounce), a linear bridge connects the head easing\n * to the pulse oscillations, ensuring C¹ continuity (matching position and velocity).\n *\n * The bridge calculation solves for three parameters:\n * 1. joinHeight: The y-coordinate where the bridge starts (where head ends)\n * 2. bridgeDuration: How long the bridge segment lasts in normalized time\n * 3. bridgeVelocity: The constant velocity of the linear bridge\n *\n * Constraints:\n * - Head ending velocity = Bridge velocity (C¹ at head-bridge boundary)\n * - Bridge velocity = Pulse starting velocity (C¹ at bridge-pulse boundary)\n * - Bridge must fit in available time: join + bridgeDuration < 1\n * - Bridge must reach target: joinHeight + bridgeVelocity * bridgeDuration = 1\n *\n * @module fuse/core/pulse-tail-bridge\n */\n\nimport type { TailBuildParams } from '../types';\n\n/**\n * Result of pulse tail bridge calculation\n */\nexport interface PulseTailBridgeResult {\n /**\n * Whether bridge parameters were successfully calculated\n */\n isValid: boolean;\n\n /**\n * Bridge parameters (only present if isValid is true)\n */\n bridgeParams?: TailBuildParams['bridge'];\n\n /**\n * Reason for failure (only present if isValid is false)\n */\n failureReason?:\n | 'invalid_pulse_velocity'\n | 'invalid_bridge_duration'\n | 'bridge_exceeds_timeline'\n | 'bounce_tail_unsupported';\n}\n\n/**\n * Calculate bridge parameters for connecting head easing to pulse tail\n *\n * The bridge is a linear segment that connects the head easing to the pulse\n * oscillations, maintaining C¹ continuity (matching position and velocity).\n *\n * For C¹ continuity, all three velocities must match:\n * - Head ending velocity (at u=1 in head's local time)\n * - Bridge constant velocity\n * - Pulse starting velocity (adaptive to head)\n *\n * The bridge velocity is set to match the pulse's natural starting velocity,\n * which is adaptively calculated based on the head's ending velocity.\n *\n * @param params - Bridge calculation parameters\n * @param params.joinTime - Time coordinate where head ends (0-1)\n * @param params.headVelocityAtEnd - Head easing velocity at u=1 (in head's local time)\n * @param params.pulseNaturalStartVelocity - Pulse's adaptive starting velocity\n * @param params.isBounceStrategy - Whether this is a bounce tail (bridge not supported)\n *\n * @returns Bridge calculation result with validity status and parameters\n *\n * @example\n * ```ts\n * const result = calculatePulseTailBridge({\n * joinTime: 0.6,\n * headVelocityAtEnd: 2.5,\n * pulseNaturalStartVelocity: 8.0,\n * isBounceStrategy: false,\n * });\n *\n * if (result.isValid) {\n * const { joinHeight, duration, velocity } = result.bridgeParams!;\n * // joinHeight: join height (where bridge starts)\n * // duration: bridge segment duration\n * // velocity: constant bridge velocity\n * }\n * ```\n */\nexport function calculatePulseTailBridge(params: {\n joinTime: number;\n headVelocityAtEnd: number;\n pulseNaturalStartVelocity: number;\n isBounceStrategy: boolean;\n}): PulseTailBridgeResult {\n const {\n joinTime,\n headVelocityAtEnd,\n pulseNaturalStartVelocity,\n isBounceStrategy,\n } = params;\n\n // Bridge mode doesn't make sense for bounce because bounce must start at position 1 (the \"floor\")\n // to create the collision effect. A bridge would start at a lower position, breaking the physics.\n if (isBounceStrategy) {\n return {\n isValid: false,\n failureReason: 'bounce_tail_unsupported',\n };\n }\n\n // Pulse velocity must be positive for a valid bridge\n if (pulseNaturalStartVelocity <= 0) {\n return {\n isValid: false,\n failureReason: 'invalid_pulse_velocity',\n };\n }\n\n // For C¹ continuity, all three velocities must match:\n // v_head_end = v_bridge = v_pulse_start\n // We set bridge velocity to match the pulse's natural start velocity\n const bridgeVelocity = pulseNaturalStartVelocity;\n\n // Solve for joinHeight (join height) from head velocity equation:\n // v_head_end = joinHeight * Lin'(1) / join = v_bridge\n // joinHeight = v_bridge * join / Lin'(1)\n let joinHeight = (bridgeVelocity * joinTime) / headVelocityAtEnd;\n\n // Validate join height is in valid range [0, 1]\n // If joinHeight < 0, the head has negative ending velocity (like some Bezier curves)\n // In this case, clamp joinHeight to 1.0 to create a dip-and-recover pattern\n if (joinHeight < 0) {\n joinHeight = 1.0;\n }\n\n // If joinHeight >= 1, clamp it slightly below 1.0 to force bridge usage\n // This ensures the spring tail always uses the bridge strategy, which\n // behaves correctly with oscillations centered around 1.0\n //\n // Context: When joinHeight approaches 1.0 (typically with high joinTime),\n // the bridge becomes very small but is still essential for correct\n // oscillation behavior. Without the bridge, the spring tail would oscillate\n // around P (the head endpoint) instead of around 1.0 (the target).\n // By clamping joinHeight just below 1.0, we ensure a tiny bridge is always\n // created, maintaining the correct oscillation centering.\n const MAX_JOIN_HEIGHT = 0.9999;\n if (joinHeight >= 1.0) {\n joinHeight = MAX_JOIN_HEIGHT;\n }\n\n // Calculate bridge duration from position constraint:\n // joinHeight + v_bridge * bridge_duration = 1\n // bridge_duration = (1 - joinHeight) / v_bridge\n const bridgeDuration = (1 - joinHeight) / bridgeVelocity;\n\n // Ensure bridge duration is positive (should be guaranteed by joinHeight validation)\n if (bridgeDuration <= 0) {\n return {\n isValid: false,\n failureReason: 'invalid_bridge_duration',\n };\n }\n\n // Ensure bridge fits in remaining timeline\n if (joinTime + bridgeDuration >= 1) {\n return {\n isValid: false,\n failureReason: 'bridge_exceeds_timeline',\n };\n }\n\n // Valid bridge parameters calculated\n return {\n isValid: true,\n bridgeParams: {\n joinHeight,\n duration: bridgeDuration,\n velocity: bridgeVelocity,\n },\n };\n}\n","/**\n * Cruise control — insert a constant-velocity phase when the natural\n * join velocity exceeds a user-defined maxSpeed.\n *\n * The easing is split into three phases:\n * head [0, tStar] → cruise [tStar, tC] → tail [tC, 1]\n *\n * The head ends early (at tStar, where it naturally reaches cruiseSpeed),\n * then cruises at constant velocity through the nominal joinTime,\n * until the tail starts at tC.\n */\n\nimport type { EasingFn } from '../../lib/types/units';\n\n// ─── Types ──────────────────────────────────────────────────────────\n\nexport interface CruiseInput {\n /** Nominal join time in [0, 1] */\n joinTime: number;\n /** Head easing velocity at u = 1 (end of head in local coords) */\n headVelocityAtEnd: number;\n /** Tail easing velocity at u = 0 (start of tail in local coords) */\n tailVelocityAtStart: number;\n /** User-requested maximum velocity at the join */\n maxSpeed: number;\n /** Natural join height from C¹ continuity (without cruise) */\n naturalJoinHeight: number;\n /** Whether the natural join height was valid */\n naturalJoinHeightIsValid: boolean;\n}\n\nexport interface CruiseParams {\n /** Effective cruise velocity (may be clamped above maxSpeed for feasibility) */\n cruiseSpeed: number;\n /** Time where head ends and cruise begins */\n tStar: number;\n /** Time where cruise ends and tail begins */\n tC: number;\n /** Position at end of head / start of cruise */\n headHeight: number;\n /** Position at end of cruise / start of tail */\n tailStartPos: number;\n}\n\n// ─── Pure functions ─────────────────────────────────────────────────\n\n/**\n * Whether cruise mode should be attempted.\n * Cruise requires finite positive maxSpeed and positive velocities at the join.\n */\nexport function shouldAttemptCruise(\n maxSpeed: number | undefined,\n headVelocityAtEnd: number,\n tailVelocityAtStart: number,\n): boolean {\n return (\n maxSpeed !== undefined &&\n Number.isFinite(maxSpeed) &&\n maxSpeed > 0 &&\n headVelocityAtEnd > 0 &&\n tailVelocityAtStart > 0\n );\n}\n\n/**\n * Compute the natural velocity at the join point from the C¹ join height.\n * Returns 0 when the natural join height is invalid.\n */\nexport function computeNaturalJoinVelocity(\n naturalJoinHeight: number,\n isValid: boolean,\n headVelocityAtEnd: number,\n joinTime: number,\n): number {\n if (!isValid) return 0;\n return (naturalJoinHeight * headVelocityAtEnd) / joinTime;\n}\n\n/**\n * Compute the minimum feasible cruise speed.\n *\n * This is the speed below which the three-phase constraint system\n * (head + cruise + tail = total displacement of 1) has no solution.\n */\nexport function computeMinCruiseSpeed(\n headVelocityAtEnd: number,\n tailVelocityAtStart: number,\n joinTime: number,\n): number {\n const Ld1 = headVelocityAtEnd;\n return (\n 1 /\n (1 / Ld1 +\n (joinTime - joinTime / Ld1) +\n (1 - joinTime) / tailVelocityAtStart)\n );\n}\n\n/**\n * Solve for the three-phase cruise parameters: tStar, tC, headHeight, tailStartPos.\n *\n * Returns null if the solution is degenerate or produces invalid geometry.\n */\nexport function solveCruiseParams(input: CruiseInput): CruiseParams | null {\n const {\n joinTime,\n headVelocityAtEnd,\n tailVelocityAtStart,\n maxSpeed,\n naturalJoinHeight,\n naturalJoinHeightIsValid,\n } = input;\n\n const naturalVelocity = computeNaturalJoinVelocity(\n naturalJoinHeight,\n naturalJoinHeightIsValid,\n headVelocityAtEnd,\n joinTime,\n );\n\n // Cruise not needed if natural velocity is within limit\n if (naturalVelocity <= maxSpeed) return null;\n\n const Ld1 = headVelocityAtEnd;\n const minSpeed = computeMinCruiseSpeed(Ld1, tailVelocityAtStart, joinTime);\n const cruiseSpeed = Math.max(maxSpeed, Math.max(minSpeed, 1));\n\n // Solve for tStar: time where head ends\n let tStar: number;\n if (Math.abs(1 / Ld1 - 1) < 1e-9) {\n // Linear head IS the cruise\n tStar = 0;\n } else {\n // Closed-form: proportional split (joinTime - t*) / (t_c - t*) = joinTime,\n // i.e. t* = joinTime*(1-t_c)/(1-joinTime), substituted into the total\n // displacement constraint to yield t_c = (R - jK) / (M - jK).\n const K = 1 / Ld1 - 1;\n const M = 1 - 1 / tailVelocityAtStart;\n const R = 1 / cruiseSpeed - 1 / tailVelocityAtStart;\n const jK = (joinTime * K) / (1 - joinTime);\n const denom = M - jK;\n\n let tC: number;\n if (Math.abs(denom) < 1e-9) {\n tC = joinTime;\n } else {\n tC = (R - jK) / denom;\n }\n\n tC = Math.max(joinTime, Math.min(1 - 0.01, tC));\n\n tStar = (joinTime * (1 - tC)) / (1 - joinTime);\n tStar = Math.max(0, Math.min(joinTime, tStar));\n }\n\n const headHeight = tStar > 0 ? (cruiseSpeed * tStar) / Ld1 : 0;\n const tC =\n tStar > 0\n ? joinTime + ((joinTime - tStar) * (1 - joinTime)) / joinTime\n : joinTime +\n (1 - joinTime) *\n (1 - 1 / (cruiseSpeed * (1 / tailVelocityAtStart)));\n\n const tCClamped = Math.max(joinTime, Math.min(1 - 0.01, tC));\n const joinHeight = headHeight + cruiseSpeed * (joinTime - tStar);\n const tailStartPos = joinHeight + cruiseSpeed * (tCClamped - joinTime);\n const tailDuration = 1 - tCClamped;\n\n // Guard: invalid geometry\n if (tailStartPos <= 0 || tailStartPos >= 1 || tailDuration <= 0.01) {\n return null;\n }\n\n return {\n cruiseSpeed,\n tStar,\n tC: tCClamped,\n headHeight,\n tailStartPos,\n };\n}\n\n/**\n * Build the three-phase cruise easing function from solved parameters.\n */\nexport function buildCruiseEasing(\n params: CruiseParams,\n headEasingFn: EasingFn,\n tailEasingFn: EasingFn,\n): EasingFn {\n const { cruiseSpeed, tStar, tC, headHeight, tailStartPos } = params;\n const tailDuration = 1 - tC;\n\n return (u: number): number => {\n if (u <= 0) return 0;\n if (u >= 1) return 1;\n\n if (tStar > 0 && u <= tStar) {\n // Head easing phase\n const localTime = u / tStar;\n return headHeight * headEasingFn(localTime);\n } else if (u <= tC) {\n // Cruise phase (constant velocity, spans the nominal join point)\n const cruiseStart = tStar > 0 ? headHeight : 0;\n return cruiseStart + cruiseSpeed * (u - tStar);\n } else {\n // Tail easing phase\n const tau = (u - tC) / tailDuration;\n return tailStartPos + (1 - tailStartPos) * tailEasingFn(tau);\n }\n };\n}\n","/**\n * Fallback strategy for spring head + spring tail when bridge solution fails\n *\n * This fallback uses a simpler vertical scaling approach instead of the\n * proportional bridge allocation. It's used when the bridge-based solution\n * produces invalid parameters (e.g., bridge extends past timeline).\n *\n * Strategy:\n * 1. Build a raw spring head by creating a spring tail and reversing it\n * 2. Scale the spring head vertically to fit [0, joinHeight]\n * 3. Build spring tail from joinHeight to 1 (without bridge)\n *\n * @module fuse/core/spring-head-fallback\n */\n\nimport type { EasingFn } from '../../lib/types/units';\nimport { reverseEasingFn } from '@penner/easing';\n\n/**\n * Parameters for building spring head fallback\n */\nexport interface SpringHeadFallbackParams {\n /**\n * Time coordinate where head ends and tail begins (0-1)\n */\n joinTime: number;\n\n /**\n * Number of oscillation half-cycles in head spring\n */\n headBounces: number;\n\n /**\n * Total decay percentage for head spring (0-99)\n */\n headDecay: number;\n\n /**\n * Number of oscillation half-cycles in tail spring\n */\n tailBounces: number;\n\n /**\n * Total decay percentage for tail spring (0-99)\n */\n tailDecay: number;\n\n /**\n * Spring tail build function (strategy pattern dependency injection)\n * Must have a build() method that returns { p: EasingFn, v: VelocityFn }\n */\n springTailBuilder: {\n build(params: {\n joinTime: number;\n bounces: number;\n decayPct: number;\n L?: EasingFn;\n Ld?: (u: number) => number;\n bridge?: unknown;\n }): {\n p: EasingFn;\n v: (u: number) => number;\n };\n };\n}\n\n/**\n * Create spring + spring fuse using vertical scaling fallback\n *\n * This is a simpler alternative to the proportional bridge approach.\n * Used when bridge parameters are invalid (e.g., bridge too long).\n *\n * The approach:\n * 1. Build a full spring tail at join=0.5 with head parameters\n * 2. Reverse it to get spring head oscillations\n * 3. Extract oscillating portion and scale to [0, 1] in head's time\n * 4. Scale vertically to [0, joinHeight]\n * 5. Build tail spring from joinHeight to 1 using scaled head as base\n *\n * @param params - Fallback construction parameters\n * @returns A fused easing function combining scaled spring head and spring tail\n *\n * @example\n * ```ts\n * const easing = createSpringHeadFallback({\n * joinTime: 0.6,\n * headBounces: 4,\n * headDecay: 95,\n * tailBounces: 3,\n * tailDecay: 80,\n * springTailBuilder: ElasticZeroLockedTail,\n * });\n * ```\n */\nexport function createSpringHeadFallback(\n params: SpringHeadFallbackParams,\n): EasingFn {\n const {\n joinTime,\n headBounces,\n headDecay,\n tailBounces,\n tailDecay,\n springTailBuilder,\n } = params;\n\n // Build raw spring head pulse by creating a spring tail and reversing it\n // Use a=0.5 for proper normalization (tail phase is [0.5, 1])\n const headPulseFullResult = springTailBuilder.build({\n joinTime: 0.5,\n bounces: headBounces,\n decayPct: headDecay,\n bridge: undefined,\n });\n\n // The tail phase is u ∈ [0.5, 1], which has the spring oscillations\n // When reversed, this becomes the head phase\n const fullReversed = reverseEasingFn(headPulseFullResult.p);\n\n // Extract the oscillating part: in the reversed curve,\n // the oscillations are at u ∈ [0, 0.5] (mirrored from original [0.5, 1])\n // Map our [0, 1] to [0, 0.5] in the reversed curve\n const rawSpringHead = (u: number): number => {\n return fullReversed(u * 0.5);\n };\n\n const rawSpringHeadVelocity = (u: number): number => {\n // Velocity in reversed: v_reversed(t) = v_original(1 - t)\n // We're sampling at t = u * 0.5\n // Original sample point: 1 - (u * 0.5) = 1 - 0.5*u\n // Time compression factor: 0.5\n return headPulseFullResult.v(1 - u * 0.5) * 0.5;\n };\n\n // Scale the spring head to fit in [0, joinTime] time range and [0, joinHeight] position range\n const headScale = joinTime;\n\n const scaledSpringHead = (u: number): number => {\n if (u <= 0) return 0;\n if (u >= joinTime) return joinTime;\n // Map [0, joinTime] to [0, 1], evaluate raw spring head, scale to joinHeight\n return headScale * rawSpringHead(u / joinTime);\n };\n\n const scaledSpringHeadVelocity = (u: number): number => {\n if (u <= 0 || u >= joinTime) return 0;\n // Time derivative includes factor of 1/joinTime from chain rule\n return (headScale / joinTime) * rawSpringHeadVelocity(u / joinTime);\n };\n\n // Build the tail spring using the scaled head as the base easing\n const tailResult = springTailBuilder.build({\n joinTime,\n bounces: tailBounces,\n decayPct: tailDecay,\n L: scaledSpringHead,\n Ld: scaledSpringHeadVelocity,\n bridge: undefined,\n });\n\n return tailResult.p;\n}\n","/**\n * Spring-to-Spring Fuse Strategy\n *\n * Handles the special case where both head and tail are spring (pulse) easings.\n * Uses a double-bridge approach with proportional time allocation for smooth\n * C¹ continuity between the two oscillating phases.\n *\n * Structure: [head pulse] → [head bridge to joinHeight] → [tail bridge from joinHeight] → [tail pulse]\n *\n * The join position (joinHeight) is calculated to ensure proportional bridge allocation:\n * - For symmetric parameters (join=0.5, same N and decay), joinHeight = 0.5 (perfect symmetry)\n * - For asymmetric parameters, joinHeight = joinTime (proportional allocation)\n *\n * This approach maintains 180° rotational symmetry when parameters are symmetric.\n */\n\nimport { reverseEasingFn } from '@penner/easing';\nimport type { EasingFn } from '../../lib/types/units';\nimport { ElasticZeroLockedTail } from '../spring';\n\n/**\n * Configuration for spring-to-spring fuse creation\n */\nexport interface SpringToSpringConfig {\n /**\n * Join time coordinate (0-1) where head ends and tail begins\n */\n joinTime: number;\n\n /**\n * Number of bounces (half-cycles) in the head spring\n */\n headBounces: number;\n\n /**\n * Total decay percentage (0-100) for the head spring\n * The last peak is (100 - headDecay)% of the first peak\n */\n headDecay: number;\n\n /**\n * Number of bounces (half-cycles) in the tail spring\n */\n tailBounces: number;\n\n /**\n * Total decay percentage (0-100) for the tail spring\n * The last peak is (100 - tailDecay)% of the first peak\n */\n tailDecay: number;\n\n /**\n * Optional tuning parameters for bridge velocity calculation\n * Controls how bridge velocity adapts to spring characteristics\n */\n tuning?: {\n headWeight?: number;\n freqWeight?: number;\n freqExponent?: number;\n baseMultiplier?: number;\n };\n}\n\n/**\n * Result from spring-to-spring fuse creation\n */\nexport interface SpringToSpringResult {\n /**\n * The composite easing function combining both springs with bridges\n * Returns null if parameters are invalid\n */\n easingFn: EasingFn | null;\n\n /**\n * Diagnostic information about the fuse construction\n */\n diagnostics?: {\n joinPosition: number;\n headBridgeDuration: number;\n tailBridgeDuration: number;\n headPulseDuration: number;\n tailPulseDuration: number;\n bridgeVelocity: number;\n };\n}\n\n/**\n * Bridge velocity constant for spring-spring fuses\n *\n * When building raw spring pulses with a=0.5, the velocity at the oscillation\n * boundary (u=0.5) is approximately 9.4 across different N and decay values.\n * A bridge velocity of 10 provides smooth C¹ continuity for all parameter combinations.\n *\n * This is an empirically determined constant that balances bridge duration\n * with visual smoothness.\n */\nconst SPRING_SPRING_BRIDGE_VELOCITY = 10;\n\n/**\n * Create a spring-to-spring fuse with double-bridge connection\n *\n * This handles the unique case where both head and tail are pulse (spring) easings.\n * Unlike transition easings that can connect directly via C¹ math, pulse easings\n * require bridge segments to connect smoothly.\n *\n * Strategy:\n * 1. Build raw spring heads/tails using a=0.5 normalization\n * 2. Reverse the head spring to get anticipation effect\n * 3. Calculate proportional bridge allocation based on joinTime\n * 4. Create composite function with four phases:\n * - Head pulse (oscillations)\n * - Head bridge (linear ramp to joinPosition)\n * - Tail bridge (linear ramp from joinPosition)\n * - Tail pulse (oscillations)\n *\n * Returns null if parameters are invalid (negative durations, bridge doesn't fit).\n *\n * @param config - Configuration for the spring-to-spring fuse\n * @returns Result with easing function and diagnostics, or null if invalid\n *\n * @example\n * ```ts\n * // Symmetric spring-spring fuse (perfect 180° symmetry)\n * const result = createSpringToSpringFuse({\n * joinTime: 0.5,\n * headOscillations: 4,\n * headDecay: 95,\n * tailOscillations: 4,\n * tailDecay: 95,\n * });\n *\n * if (result.easingFn) {\n * const value = result.easingFn(0.5); // Should be ~0.5 for symmetric params\n * console.log(result.diagnostics); // Inspect bridge allocation\n * }\n * ```\n */\nexport function createSpringToSpringFuse(\n config: SpringToSpringConfig,\n): SpringToSpringResult {\n const { joinTime, headBounces, headDecay, tailBounces, tailDecay, tuning } =\n config;\n\n // Validate inputs\n if (joinTime <= 0 || joinTime >= 1) {\n return { easingFn: null };\n }\n\n if (headBounces < 1 || tailBounces < 1) {\n return { easingFn: null };\n }\n\n // Build raw spring head pulse\n // Strategy: Build a spring tail, then reverse it\n // Use a=0.5 for proper normalization\n const headPulseFullResult = ElasticZeroLockedTail.build({\n joinTime: 0.5,\n bounces: headBounces,\n decayPct: headDecay,\n bridge: undefined,\n });\n\n // Build raw spring tail pulse\n const tailPulseFullResult = ElasticZeroLockedTail.build({\n joinTime: 0.5,\n bounces: tailBounces,\n decayPct: tailDecay,\n bridge: undefined,\n });\n\n // The tail phase is u ∈ [0.5, 1], which has the spring oscillations\n // When reversed, this becomes the head phase\n // Reverse the entire function first\n const fullReversed = reverseEasingFn(headPulseFullResult.p);\n\n // Now extract the oscillating part: in the reversed curve,\n // the oscillations are at u ∈ [0, 0.5] (mirrored from original [0.5, 1])\n // Map our [0, 1] to [0, 0.5] in the reversed curve\n const rawSpringHead = (u: number): number => {\n return fullReversed(u * 0.5);\n };\n\n const rawSpringHeadVelocity = (u: number): number => {\n // Velocity in reversed: v_reversed(t) = v_original(1 - t)\n // We're sampling at t = u * 0.5\n // Original sample point: 1 - (u * 0.5) = 1 - 0.5*u\n // Time compression factor: 0.5\n return headPulseFullResult.v(1 - u * 0.5) * 0.5;\n };\n\n // Bridge velocity: empirically determined constant for spring-spring fuses\n const bridgeVelocity = SPRING_SPRING_BRIDGE_VELOCITY;\n\n if (bridgeVelocity <= 0) {\n return { easingFn: null };\n }\n\n // Solve for symmetric bridge allocation\n // For 180° symmetry when join=0.5 and parameters match:\n // - joinPosition should be at 0.5 (midpoint)\n // - Head bridge duration = Tail bridge duration\n // - Bridge durations proportional to available time\n\n // Constraints:\n // Head: [0, join] = [pulse: 0 to headPulseDuration] + [bridge: headPulseDuration to join, rising 0 to joinPosition]\n // Tail: [join, 1] = [bridge: join to join+tailBridgeDuration, rising joinPosition to 1] + [pulse: join+tailBridgeDuration to 1]\n\n // For proportional bridges: headBridgeDuration/join = tailBridgeDuration/(1-join)\n // Bridge velocities both = bridgeVelocity, so:\n // joinPosition = bridgeVelocity * headBridgeDuration\n // 1 - joinPosition = bridgeVelocity * tailBridgeDuration\n\n // From proportional constraint: headBridgeDuration/join = tailBridgeDuration/(1-join)\n // So: headBridgeDuration = join * tailBridgeDuration / (1-join)\n // Substituting into bridge equations:\n // joinPosition = bridgeVelocity * join * tailBridgeDuration / (1-join)\n // 1 - joinPosition = bridgeVelocity * tailBridgeDuration\n\n // From second equation: tailBridgeDuration = (1 - joinPosition) / bridgeVelocity\n // Substituting into first:\n // joinPosition = bridgeVelocity * join * (1-joinPosition) / (bridgeVelocity * (1-join))\n // joinPosition = join * (1-joinPosition) / (1-join)\n // joinPosition * (1-join) = join * (1-joinPosition)\n // joinPosition - joinPosition*join = join - joinPosition*join\n // joinPosition = join\n\n // So joinPosition = join for proportional symmetry!\n const joinPosition = joinTime;\n\n // Bridge durations\n const headBridgeDuration = joinPosition / bridgeVelocity;\n const tailBridgeDuration = (1 - joinPosition) / bridgeVelocity;\n\n // Head pulse time\n const headPulseDuration = joinTime - headBridgeDuration;\n\n // Tail pulse time\n const tailPulseStart = joinTime + tailBridgeDuration;\n const tailPulseDuration = 1 - tailPulseStart;\n\n // Validate: all times must be positive\n if (\n headPulseDuration <= 0 ||\n headBridgeDuration <= 0 ||\n tailBridgeDuration <= 0 ||\n tailPulseDuration <= 0\n ) {\n return { easingFn: null };\n }\n\n // Calculate scaling factors for proportional scaling\n const headScale = 2 * headPulseDuration;\n const tailScale = 2 * tailPulseDuration;\n\n // Calculate actual pulse endpoints for proper bridge connections\n const headPulseEndY = rawSpringHead(1.0); // Where head pulse ends\n const tailPulseStartValue = tailPulseFullResult.p(0.5); // Where tail starts in original\n\n // Build composite function with proportionally scaled pulses and two bridges\n const easingFn = (u: number): number => {\n if (u <= 0) return 0;\n if (u >= 1) return 1;\n\n if (u <= headPulseDuration) {\n // Head pulse phase: proportionally scaled\n // Time: map [0, headPulseDuration] to [0, 1]\n // Amplitude: scale by headScale\n const localTime = u / headPulseDuration;\n return headScale * rawSpringHead(localTime);\n } else if (u <= joinTime) {\n // Head bridge phase: linear ramp from head pulse end to joinPosition\n const headPulseEnd = headScale * headPulseEndY;\n const bridgeProgress = (u - headPulseDuration) / headBridgeDuration;\n return headPulseEnd + (joinPosition - headPulseEnd) * bridgeProgress;\n } else if (u <= tailPulseStart) {\n // Tail bridge phase: linear ramp from joinPosition to tail pulse start\n // Tail pulse starts at position 1 - tailScale * (1 - start_in_normalized)\n const tailPulseStartY = 1 - tailScale * (1 - tailPulseStartValue);\n const bridgeProgress = (u - joinTime) / tailBridgeDuration;\n return joinPosition + (tailPulseStartY - joinPosition) * bridgeProgress;\n } else {\n // Tail pulse phase: proportionally scaled\n // Time: map [tailPulseStart, 1] to [0, 1] for local time, then to [0, 0.5] for raw tail\n const localTime = (u - tailPulseStart) / tailPulseDuration;\n\n // Extract the oscillating part [0.5, 1] and proportionally scale amplitude\n const rawTailValue = tailPulseFullResult.p(0.5 + localTime * 0.5);\n\n // The tail oscillates around 1 in its natural space\n // Scale the oscillation amplitude by tailScale and position around 1\n return 1 - tailScale * (1 - rawTailValue);\n }\n };\n\n return {\n easingFn,\n diagnostics: {\n joinPosition,\n headBridgeDuration,\n tailBridgeDuration,\n headPulseDuration,\n tailPulseDuration,\n bridgeVelocity,\n },\n };\n}\n","/**\n * Complementary Curve Strategy for Spring Head + Pulse Tail\n *\n * Handles the special case where a spring (pulse) head is combined with a pulse tail\n * (bounce or spring). Uses a \"build + reverse\" approach to create the correct anticipation\n * effect.\n *\n * Strategy: Build power head + pulse tail at (1-join), then reverse the entire result.\n * This leverages existing pulse tail logic while creating the spring head anticipation.\n */\n\nimport { reverseEasingFn } from '@penner/easing';\nimport type { EasingFn } from '../../lib/types/units';\nimport type { TailStrategy } from '../types';\n\n/**\n * Configuration for complementary curve fuse creation\n */\nexport interface ComplementaryCurveConfig {\n /**\n * Join time coordinate (0-1) where head ends and tail begins\n */\n joinTime: number;\n\n /**\n * Spring head configuration\n */\n springHead: {\n /**\n * Total decay percentage (0-100) for the spring head\n */\n decay: number;\n };\n\n /**\n * Pulse tail configuration\n */\n pulseTail: {\n /**\n * Tail strategy (must be a pulse type: spring or bounce)\n */\n strategy: TailStrategy;\n\n /**\n * Number of bounces in the tail\n */\n bounces: number;\n\n /**\n * Total decay percentage (0-100) for the tail\n */\n decay: number;\n };\n\n /**\n * Whether to use bridge mode for the tail\n */\n useBridge?: boolean;\n\n /**\n * Optional tuning parameters for bridge velocity calculation\n */\n bridgeTuning?: {\n headWeight?: number;\n freqWeight?: number;\n freqExponent?: number;\n baseMultiplier?: number;\n };\n}\n\n/**\n * Power head easing factory for complementary curves\n * Creates a simple linear (exponent=1) easing for the complementary build\n */\ninterface PowerHeadStrategy {\n easingFn: EasingFn;\n velocityFn: (u: number) => number;\n}\n\n/**\n * Create a linear power head for complementary curve building\n * This is used as the temporary head when building the complementary curve\n */\nfunction createLinearPowerHead(): PowerHeadStrategy {\n return {\n easingFn: (u: number) => u,\n velocityFn: () => 1,\n };\n}\n\n/**\n * Create a fuse using complementary curve approach\n *\n * This strategy is used when combining a spring head with a pulse tail (bounce or spring).\n * Direct combination is difficult because spring heads require special handling.\n *\n * Instead, we:\n * 1. Build a power head + pulse tail fuse at join point (1-joinTime)\n * 2. Reverse the entire curve\n * 3. The reversed curve has a spring head effect + pulse tail\n *\n * Why this works:\n * - Reversing a pulse tail creates a pulse head (anticipation effect)\n * - Reversing a power tail creates a power head (ease-in)\n * - The spring head bounces come from reversing the power+pulse combination\n *\n * @param config - Complementary curve configuration\n * @param fuseImpl - The fuse implementation to use (passed to avoid circular dependency)\n * @returns Fused easing function with spring head + pulse tail\n *\n * @example\n * ```ts\n * // Spring head + bounce tail\n * const fuse = createComplementaryCurveFuse(\n * {\n * joinTime: 0.6,\n * springHead: { decay: 95 },\n * pulseTail: {\n * strategy: BounceTail,\n * bounces: 4,\n * decay: 90\n * },\n * useBridge: true,\n * },\n * fuse // Pass the fuse function\n * );\n * ```\n */\n/**\n * Configuration type accepted by fuse for building the complementary curve\n */\ninterface PulseFuseArgs {\n head: EasingFn;\n headVelocityFn?: (u: number) => number;\n join: number;\n bounces: number;\n decay: number;\n useBridge?: boolean;\n bridgeTuning?: {\n headWeight?: number;\n freqWeight?: number;\n freqExponent?: number;\n baseMultiplier?: number;\n };\n}\n\nexport function createComplementaryCurveFuse(\n config: ComplementaryCurveConfig,\n fuseImpl: (strategy: TailStrategy, config: PulseFuseArgs) => EasingFn,\n): EasingFn {\n const { joinTime, pulseTail, useBridge = true, bridgeTuning } = config;\n\n // Validate inputs\n if (joinTime <= 0 || joinTime >= 1) {\n throw new Error(`Invalid joinTime: ${joinTime}. Must be in range (0, 1).`);\n }\n\n if (pulseTail.bounces < 1) {\n throw new Error(\n `Invalid pulse tail bounces: ${pulseTail.bounces}. Must be >= 1.`,\n );\n }\n\n // Create a linear power head for the complementary curve\n const complementaryHead = createLinearPowerHead();\n\n // Build complementary curve: power head + pulse tail at flipped join point\n // We flip the join point because after reversal, it will be in the correct position\n const complementaryJoinTime = 1 - joinTime;\n\n // Build the complementary fuse\n const fusedComplementary = fuseImpl(pulseTail.strategy, {\n head: complementaryHead.easingFn,\n headVelocityFn: complementaryHead.velocityFn,\n join: complementaryJoinTime,\n bounces: pulseTail.bounces,\n decay: pulseTail.decay,\n useBridge,\n bridgeTuning,\n });\n\n // Reverse to get spring head + pulse tail\n // After reversal:\n // - The power tail becomes a power head (ease-in)\n // - The pulse tail bounces become spring head bounces (anticipation)\n // - The join point is at the correct position\n return reverseEasingFn(fusedComplementary);\n}\n\n/**\n * Check if a tail strategy is a pulse type (requires complementary curve for spring head)\n *\n * @param strategy - Tail strategy to check\n * @returns True if the strategy is a pulse type (has bounces and settles)\n */\nexport function isPulseTailStrategy(strategy: TailStrategy): boolean {\n return typeof strategy.getNaturalStartVelocity === 'function';\n}\n","/**\n * composeFuse — Core composition function for fused easings.\n *\n * Takes a head REM and a tail REM (both EasingRem instances) plus options,\n * and produces an EasingKit with easing function, velocity function, and metadata.\n *\n * Dispatches on concrete `kind` values to select the right composition strategy.\n * As heuristics mature, dispatch can shift toward abstract REM metadata.\n */\n\nimport type { EasingFn, NormalizedTime } from '../lib/types/units';\nimport { reverseEasingFn } from '@penner/easing';\nimport bezierEasing from '@penner/fast-bezier-easing';\nimport { EasingRem } from './rem/EasingRem';\nimport { easingKit, type EasingKit } from '@penner/easing';\nimport type { BezierRemParams } from './rem/BezierRem';\nimport type { SpringRemParams } from './rem/SpringRem';\nimport type { BounceRemParams } from './rem/BounceRem';\nimport { PowerRem } from './rem/PowerRem';\nimport { isJoinAtBoundary } from './core/edge-cases';\nimport { calculateJoinHeightForContinuity } from './continuity/join-height';\nimport { calculatePulseTailBridge } from './core/pulse-tail-bridge';\nimport {\n shouldAttemptCruise,\n solveCruiseParams,\n buildCruiseEasing,\n} from './core/cruise';\nimport { createSpringHeadFallback } from './core/spring-head-fallback';\nimport { createSpringToSpringFuse } from './strategies/spring-to-spring';\nimport { createComplementaryCurveFuse } from './strategies/complementary-curve';\nimport { BounceTail } from './bounce';\nimport { SpringZeroLockedTail as ElasticZeroLockedTail } from './spring';\nimport {\n createOutInPulse,\n createPulseWithReturn,\n createSpringReturn,\n createBounceReturn,\n} from './pulse';\n\n/**\n * Type-safe params access after dispatching on `rem.kind`.\n * Centralizes the `unknown` bridge required by the erased generic on EasingRem.\n */\nfunction remParams<T>(rem: EasingRem): T {\n return rem.params as unknown as T;\n}\n\n// ─── Options ─────────────────────────────────────────────────────────\n\nexport interface ComposeFuseOptions {\n /** Join point in normalized time [0,1]. @default 0.6 */\n joinTime?: NormalizedTime;\n /** Movement mode. @default 'transition' */\n movement?: 'transition' | 'pulse';\n /** Mirror head as tail (symmetric inOut). @default false */\n mirror?: boolean;\n /** Enable bridge for pulse tails. @default true */\n useBridge?: boolean;\n /** Bridge tuning parameters */\n bridgeTuning?: {\n headWeight?: number;\n freqWeight?: number;\n freqExponent?: number;\n baseMultiplier?: number;\n };\n /** Allow generalized back tail when joinHeight is clamped to 1.0 */\n allowGeneralizedBackTail?: boolean;\n /** Maximum velocity at the join point for cruise control */\n maxSpeed?: number;\n}\n\n/**\n * Compose a fused easing from a head REM and tail REM.\n *\n * This is the core composition entry point used by fuse(FuseConfig).\n */\nexport function composeFuse(\n headRem: EasingRem,\n tailRem: EasingRem,\n options: ComposeFuseOptions = {},\n): EasingKit {\n const {\n joinTime = 0.5,\n movement = 'transition',\n mirror = false,\n useBridge = true,\n bridgeTuning,\n allowGeneralizedBackTail = false,\n maxSpeed,\n } = options;\n\n const meta = {\n movement,\n headKind: headRem.kind,\n tailKind: tailRem.kind,\n joinTime,\n mirror,\n };\n\n // ── Pulse mode ──\n if (movement === 'pulse') {\n return composePulse(headRem, tailRem, joinTime, meta);\n }\n\n // ── Transition mode ──\n return composeTransition(headRem, tailRem, {\n joinTime,\n mirror,\n useBridge,\n bridgeTuning,\n allowGeneralizedBackTail,\n maxSpeed,\n meta,\n });\n}\n\n// ─── Pulse composition ──────────────────────────────────────────────\n\nfunction composePulse(\n headRem: EasingRem,\n tailRem: EasingRem,\n joinTime: NormalizedTime,\n meta: Record<string, unknown>,\n): EasingKit {\n // Rising phase: head's ease-out (decelerates to v=0 at peak)\n // Spring head has no suitable ease-out for pulse rising — fall back to power\n const risingRem =\n headRem.kind === 'spring'\n ? PowerRem.create({\n exponent: remParams<SpringRemParams>(headRem).bounces ?? 2,\n })\n : headRem;\n const risingEasing = risingRem.easeOut;\n\n let easingFn: EasingFn;\n\n if (tailRem.kind === 'spring') {\n const { bounces, decay } = remParams<SpringRemParams>(tailRem);\n easingFn = createPulseWithReturn(\n risingEasing,\n createSpringReturn({ bounces, decay: decay * 100 }),\n joinTime,\n );\n } else if (tailRem.kind === 'bounce') {\n const { bounces, decay } = remParams<BounceRemParams>(tailRem);\n easingFn = createPulseWithReturn(\n risingEasing,\n createBounceReturn({ bounces, decay: decay * 100 }),\n joinTime,\n );\n } else {\n // Regular tail: falling phase uses tail's ease-in (accelerates from v=0 at peak)\n easingFn = createOutInPulse(risingEasing, tailRem.easeIn, joinTime);\n }\n\n return easingKit(easingFn, meta);\n}\n\n// ─── Transition composition ─────────────────────────────────────────\n\ninterface TransitionContext {\n joinTime: NormalizedTime;\n mirror: boolean;\n useBridge: boolean;\n bridgeTuning?: ComposeFuseOptions['bridgeTuning'];\n allowGeneralizedBackTail: boolean;\n maxSpeed?: number;\n meta: Record<string, unknown>;\n}\n\nfunction composeTransition(\n headRem: EasingRem,\n tailRem: EasingRem,\n ctx: TransitionContext,\n): EasingKit {\n const { joinTime, mirror } = ctx;\n\n // ── Boundary cases ──\n const boundary = isJoinAtBoundary(joinTime);\n\n if (boundary.atStart) {\n // Join at start → no head phase\n if (tailRem.kind === 'spring' || tailRem.kind === 'bounce') {\n // Pulse tails need to build with minimal head — continue to normal flow\n } else {\n // Regular tail — return linear fallback\n return easingKit(u => Math.max(0, Math.min(1, u)), ctx.meta);\n }\n }\n\n if (boundary.atEnd) {\n // Join at end → head only\n return easingKit(headRem.easeIn, ctx.meta);\n }\n\n // ── Spring head dispatch ──\n if (headRem.kind === 'spring') {\n return composeSpringHead(headRem, tailRem, ctx);\n }\n\n // ── Oscillatory tail dispatch (spring/bounce with non-spring head) ──\n if (tailRem.kind === 'spring' || tailRem.kind === 'bounce') {\n return composeOscillatoryTail(headRem, tailRem, ctx);\n }\n\n // ── Bezier+Bezier special join ──\n if (headRem.kind === 'bezier' && tailRem.kind === 'bezier') {\n return composeBezierBezier(headRem, tailRem, ctx);\n }\n\n // ── Regular head + regular tail ──\n return composeRegular(headRem, tailRem, ctx);\n}\n\n// ─── Spring head composition ────────────────────────────────────────\n\nfunction composeSpringHead(\n headRem: EasingRem,\n tailRem: EasingRem,\n ctx: TransitionContext,\n): EasingKit {\n const { joinTime, useBridge, bridgeTuning } = ctx;\n const headParams = remParams<SpringRemParams>(headRem);\n const headSpringBounces = headParams.bounces ?? 4;\n const headSpringDecay = (headParams.decay ?? 0.95) * 100;\n\n // Spring head + spring tail\n if (tailRem.kind === 'spring') {\n const tailParams = remParams<SpringRemParams>(tailRem);\n const result = createSpringToSpringFuse({\n joinTime,\n headBounces: headSpringBounces,\n headDecay: headSpringDecay,\n tailBounces: tailParams.bounces,\n tailDecay: tailParams.decay * 100,\n tuning: bridgeTuning,\n });\n\n if (result.easingFn) {\n return easingKit(result.easingFn, ctx.meta);\n }\n\n // Fallback\n const fallback = createSpringHeadFallback({\n joinTime,\n headBounces: headSpringBounces,\n headDecay: headSpringDecay,\n tailBounces: tailParams.bounces,\n tailDecay: tailParams.decay * 100,\n springTailBuilder: ElasticZeroLockedTail,\n });\n return easingKit(fallback, ctx.meta);\n }\n\n // Spring head + bounce tail — complementary curve approach\n if (tailRem.kind === 'bounce') {\n const tailParams = remParams<BounceRemParams>(tailRem);\n const easingFn = createComplementaryCurveFuse(\n {\n joinTime,\n // TODO: springHead.decay uses tailParams — should it use headParams instead?\n // This path is currently disabled (spring head not exposed in Fuse app).\n // When re-enabling, verify whether the spring head's own decay should drive\n // the complementary curve, rather than mirroring the bounce tail's decay.\n springHead: { decay: tailParams.decay * 100 },\n pulseTail: {\n strategy: BounceTail,\n bounces: tailParams.bounces,\n decay: tailParams.decay * 100,\n },\n useBridge,\n bridgeTuning,\n },\n (tailStrategy, fuseConfig) => {\n // Recursive composition for the complementary curve — use the old TailStrategy.build path\n return buildFromTailStrategy(tailStrategy, fuseConfig);\n },\n );\n return easingKit(easingFn, ctx.meta);\n }\n\n // Spring head + regular tail — complementary curve via reversal\n const tailEasingFn = ctx.mirror\n ? reverseEasingFn(headRem.easeIn)\n : tailRem.easeOut;\n const tailVelocityFn = ctx.mirror\n ? (u: number) => headRem.easeInVelocity(1 - u)\n : tailRem.easeOutVelocity;\n\n // Build complementary curve: reverse(tail-as-head + spring tail at 1-J)\n const complementaryHeadEaseIn = reverseEasingFn(tailEasingFn);\n const complementaryHeadVelocity = (u: number) => tailVelocityFn(1 - u);\n const joinComplementary = 1 - joinTime;\n\n const fusedComplementary = buildFromTailStrategy(ElasticZeroLockedTail, {\n head: complementaryHeadEaseIn,\n headVelocityFn: complementaryHeadVelocity,\n joinTime: joinComplementary,\n bounces: headSpringBounces,\n decay: headSpringDecay,\n useBridge: true,\n bridgeTuning,\n });\n\n const easingFn = reverseEasingFn(fusedComplementary);\n return easingKit(easingFn, ctx.meta);\n}\n\n// ─── Oscillatory tail (spring/bounce) with regular head ─────────────\n\nfunction composeOscillatoryTail(\n headRem: EasingRem,\n tailRem: EasingRem,\n ctx: TransitionContext,\n): EasingKit {\n const { joinTime, useBridge, bridgeTuning } = ctx;\n const tailParams = remParams<SpringRemParams | BounceRemParams>(tailRem);\n const bounces = tailParams.bounces;\n const decayPct = tailParams.decay * 100;\n\n // Get the TailStrategy from the REM class\n const strategy =\n tailRem.kind === 'spring'\n ? (tailRem.constructor as typeof import('./rem/SpringRem').SpringRem)\n .tailStrategy\n : (tailRem.constructor as typeof import('./rem/BounceRem').BounceRem)\n .tailStrategy;\n\n const headEaseIn = headRem.easeIn;\n const headVelocityFn = headRem.easeInVelocity;\n\n // Calculate bridge parameters if applicable.\n // Skip bridge for spring tails: the unified DHO handles C¹ continuity\n // directly via general initial conditions, no bridge needed.\n // Bridge is still used for bounce tails.\n let bridgeParams: import('./types').TailBuildParams['bridge'] | undefined;\n const isSpring = tailRem.kind === 'spring';\n\n if (useBridge && !isSpring && strategy.getNaturalStartVelocity) {\n const vPulseNatural = strategy.getNaturalStartVelocity(\n { bounces, decayPct },\n headVelocityFn,\n bridgeTuning,\n );\n\n const bridgeResult = calculatePulseTailBridge({\n joinTime,\n headVelocityAtEnd: headVelocityFn(1),\n pulseNaturalStartVelocity: vPulseNatural,\n isBounceStrategy: tailRem.kind === 'bounce',\n });\n\n if (bridgeResult.isValid) {\n bridgeParams = bridgeResult.bridgeParams;\n }\n }\n\n // Build the tail using the strategy\n const result = strategy.build({\n joinTime,\n bounces,\n decayPct,\n L: headEaseIn,\n Ld: headVelocityFn,\n bridge: bridgeParams,\n });\n\n return easingKit(result.p, ctx.meta);\n}\n\n// ─── Bezier + Bezier ────────────────────────────────────────────────\n\nfunction composeBezierBezier(\n headRem: EasingRem,\n tailRem: EasingRem,\n ctx: TransitionContext,\n): EasingKit {\n const { joinTime } = ctx;\n const headParams = remParams<BezierRemParams>(headRem);\n const tailParams = remParams<BezierRemParams>(tailRem);\n\n // Bezier-to-Bezier constraint: joinHeight = joinTime\n const joinHeight = joinTime;\n\n // Transform head Bezier control points to fused coordinate space\n const head_p1x = headParams.x1 * joinTime;\n const head_p1y = headParams.y1 * joinHeight;\n const head_p2x = headParams.x2 * joinTime;\n const head_p2y = headParams.y2 * joinHeight;\n const head_p3x = joinTime;\n const head_p3y = joinHeight;\n\n // Calculate slope at join point from head's p2 to p3\n let slope: number;\n if (Math.abs(head_p3x - head_p2x) < 1e-8) {\n slope = 1e8;\n } else {\n slope = (head_p3y - head_p2y) / (head_p3x - head_p2x);\n }\n\n // Calculate tail's p1 control point in global coordinates.\n // Use the user's x1 for positioning, but always enforce the slope\n // constraint on y1 to guarantee C¹ continuity at the join point.\n const tail_p1x_global = joinTime + tailParams.x1 * (1 - joinTime);\n const tail_p1y_global = joinHeight + slope * (tail_p1x_global - joinTime);\n\n // Transform tail's p2 from normalized to global coordinates\n const tail_p2x_global = joinTime + tailParams.x2 * (1 - joinTime);\n const tail_p2y_global = joinHeight + tailParams.y2 * (1 - joinHeight);\n\n // Create Bezier curves in normalized space\n const head_cp1x_norm = head_p1x / joinTime;\n const head_cp1y_norm = head_p1y / joinHeight;\n const head_cp2x_norm = head_p2x / joinTime;\n const head_cp2y_norm = head_p2y / joinHeight;\n\n const headBezier = bezierEasing(\n head_cp1x_norm,\n head_cp1y_norm,\n head_cp2x_norm,\n head_cp2y_norm,\n );\n\n const tail_cp1x_norm = (tail_p1x_global - joinTime) / (1 - joinTime);\n const tail_cp1y_norm = (tail_p1y_global - joinHeight) / (1 - joinHeight);\n const tail_cp2x_norm = (tail_p2x_global - joinTime) / (1 - joinTime);\n const tail_cp2y_norm = (tail_p2y_global - joinHeight) / (1 - joinHeight);\n\n const tailBezier = bezierEasing(\n tail_cp1x_norm,\n tail_cp1y_norm,\n tail_cp2x_norm,\n tail_cp2y_norm,\n );\n\n const easingFn: EasingFn = u => {\n if (u <= 0) return 0;\n if (u >= 1) return 1;\n\n if (u <= joinTime) {\n const localTime = u / joinTime;\n return joinHeight * headBezier(localTime);\n } else {\n const tau = (u - joinTime) / (1 - joinTime);\n return joinHeight + (1 - joinHeight) * tailBezier(tau);\n }\n };\n\n return easingKit(easingFn, ctx.meta);\n}\n\n// ─── Regular head + regular tail ────────────────────────────────────\n\nfunction composeRegular(\n headRem: EasingRem,\n tailRem: EasingRem,\n ctx: TransitionContext,\n): EasingKit {\n const { joinTime, mirror, allowGeneralizedBackTail, maxSpeed } = ctx;\n\n // Resolve head\n const headEasingFn = headRem.easeIn;\n const headVelocityFn = headRem.easeInVelocity;\n\n // Resolve tail\n let tailEasingFn: EasingFn;\n let tailVelocityFn: (u: number) => number;\n\n if (mirror) {\n tailEasingFn = reverseEasingFn(headEasingFn);\n tailVelocityFn = (u: number) => headVelocityFn(1 - u);\n } else {\n tailEasingFn = tailRem.easeOut;\n tailVelocityFn = tailRem.easeOutVelocity;\n }\n\n // Calculate join height for C¹ continuity\n const headVelocityAtEnd = headVelocityFn(1);\n const tailVelocityAtStart = tailVelocityFn(0);\n\n // ── Cruise control ──\n if (shouldAttemptCruise(maxSpeed, headVelocityAtEnd, tailVelocityAtStart)) {\n const naturalJH = calculateJoinHeightForContinuity({\n joinTime,\n headVelocityAtEnd,\n tailVelocityAtStart,\n });\n\n const cruiseParams = solveCruiseParams({\n joinTime,\n headVelocityAtEnd,\n tailVelocityAtStart,\n maxSpeed: maxSpeed!,\n naturalJoinHeight: naturalJH.joinHeight,\n naturalJoinHeightIsValid: naturalJH.isValid,\n });\n\n if (cruiseParams) {\n const easingFn = buildCruiseEasing(\n cruiseParams,\n headEasingFn,\n tailEasingFn,\n );\n return easingKit(easingFn, ctx.meta);\n }\n }\n\n // ── Standard composition (no cruise) ──\n\n let joinHeight: number;\n let useGeneralizedBackTail = false;\n let effectiveHeadVelocityAtEnd = headVelocityAtEnd;\n\n if (\n headVelocityAtEnd < 0 &&\n (tailRem.kind === 'power' || tailRem.kind === 'back')\n ) {\n // Negative head velocity with Power or Back tail: clamp join height to 1.0\n joinHeight = 1.0;\n useGeneralizedBackTail = true;\n effectiveHeadVelocityAtEnd = headVelocityAtEnd * (joinHeight / joinTime);\n } else {\n const joinHeightResult = calculateJoinHeightForContinuity({\n joinTime,\n headVelocityAtEnd,\n tailVelocityAtStart,\n });\n\n if (!joinHeightResult.isValid) {\n return easingKit((u: number) => Math.max(0, Math.min(1, u)), ctx.meta);\n }\n\n const unclampedJoinHeight = joinHeightResult.joinHeight;\n joinHeight = Math.max(0, Math.min(1, unclampedJoinHeight));\n\n if (\n allowGeneralizedBackTail &&\n joinHeight >= 0.9999 &&\n (tailRem.kind === 'power-back' ||\n tailRem.kind === 'back' ||\n tailRem.kind === 'power')\n ) {\n useGeneralizedBackTail = true;\n joinHeight = 1.0;\n effectiveHeadVelocityAtEnd = headVelocityAtEnd * (joinHeight / joinTime);\n }\n }\n\n const easingFn: EasingFn = (u: number): number => {\n if (u <= 0) return 0;\n if (u >= 1) return 1;\n\n if (u <= joinTime) {\n const localTime = u / joinTime;\n return joinHeight * headEasingFn(localTime);\n } else {\n const tau = (u - joinTime) / (1 - joinTime);\n\n if (useGeneralizedBackTail) {\n const v0 = effectiveHeadVelocityAtEnd * (1 - joinTime);\n const C = joinHeight;\n const B = v0;\n const s = (3 * (1 - C) - 2 * B) / (B - (1 - C));\n const A = 1 - B - C;\n return A * ((s + 1) * tau * tau * tau - s * tau * tau) + B * tau + C;\n } else {\n return joinHeight + (1 - joinHeight) * tailEasingFn(tau);\n }\n }\n };\n\n return easingKit(easingFn, ctx.meta);\n}\n\n// ─── Low-level bridge to TailStrategy (transitional) ────────────────\n\n/**\n * Build an easing function from a TailStrategy + BaseFuseConfig.\n * This is the transitional bridge used by spring-head composition paths\n * that still need the old TailStrategy.build() interface.\n */\nfunction buildFromTailStrategy(\n strategy: import('./types').TailStrategy,\n config: {\n head?: EasingFn;\n headVelocityFn?: (u: number) => number;\n joinTime?: NormalizedTime;\n bounces?: number;\n decay?: number;\n useBridge?: boolean;\n bridgeTuning?: ComposeFuseOptions['bridgeTuning'];\n },\n): EasingFn {\n const {\n head: customHead = (u: number) => u,\n headVelocityFn: customHeadVelocityFn = () => 1,\n joinTime = 0.5,\n bounces = 4,\n decay = 95,\n useBridge = true,\n bridgeTuning,\n } = config;\n\n // Calculate bridge parameters if applicable\n let bridgeParams: import('./types').TailBuildParams['bridge'] | undefined;\n\n if (useBridge && strategy.getNaturalStartVelocity) {\n const vPulseNatural = strategy.getNaturalStartVelocity(\n { bounces, decayPct: decay },\n customHeadVelocityFn,\n bridgeTuning,\n );\n\n const bridgeResult = calculatePulseTailBridge({\n joinTime,\n headVelocityAtEnd: customHeadVelocityFn(1),\n pulseNaturalStartVelocity: vPulseNatural,\n isBounceStrategy: strategy === BounceTail,\n });\n\n if (bridgeResult.isValid) {\n bridgeParams = bridgeResult.bridgeParams;\n }\n }\n\n const result = strategy.build({\n joinTime,\n bounces,\n decayPct: decay,\n L: customHead,\n Ld: customHeadVelocityFn,\n bridge: bridgeParams,\n });\n\n return result.p;\n}\n","/**\n * BackRem — Back (overshoot) easing Responsive Easing Module.\n *\n * Ease-in: f(u) = u² * ((strength+1)u - strength) where strength is derived from the overshoot parameter.\n * Creates anticipation by dipping below 0 before accelerating forward.\n */\n\nimport {\n solveBackStrength,\n backEaseIn,\n backEaseInVelocity,\n} from '@penner/easing';\nimport type {\n NormalizedTime,\n NormalizedProgress,\n NormalizedVelocity,\n} from '../../lib/types/units';\nimport type { KnobSpec, PercentKnobSpec } from '../../lib/rem/core/knobs';\nimport {\n EasingRem,\n type EasingRemMetadata,\n type VelocityBoundary,\n} from './EasingRem';\n\nexport interface BackRemParams {\n /** Overshoot depth, normalized 0-1 (e.g., 0.1 = 10% overshoot) */\n readonly overshoot: number;\n}\n\nexport class BackRem extends EasingRem<BackRemParams> {\n static readonly OVERSHOOT_KNOB: PercentKnobSpec = {\n key: 'overshoot',\n label: 'Overshoot',\n type: 'percent',\n default: 0.1,\n min: 0,\n max: 1,\n step: 0.01,\n isPrimary: true,\n } as const;\n\n readonly kind = 'back' as const;\n\n readonly metadata: EasingRemMetadata = {\n variants: { easeIn: true, easeOut: true },\n modes: { transition: true, pulse: true },\n };\n\n readonly knobSpecs: readonly KnobSpec[] = [BackRem.OVERSHOOT_KNOB];\n\n readonly easeInBoundary: VelocityBoundary = {\n start: 'zero',\n end: 'nonzero',\n };\n\n /** Cached strength parameter derived from overshoot */\n private readonly strength: number;\n\n constructor(params: BackRemParams) {\n super(params);\n this.strength = solveBackStrength(params.overshoot);\n }\n\n easeIn = (u: NormalizedTime): NormalizedProgress => {\n return backEaseIn(u, this.strength);\n };\n\n easeInVelocity = (u: NormalizedTime): NormalizedVelocity => {\n return backEaseInVelocity(u, this.strength);\n };\n\n // Analytical: d/du[1 - easeIn(1-u)] = easeInVelocity(1-u)\n easeOutVelocity = (u: NormalizedTime): NormalizedVelocity => {\n return backEaseInVelocity(1 - u, this.strength);\n };\n\n with(overrides: Partial<BackRemParams>): BackRem {\n return new BackRem({ ...this.params, ...overrides });\n }\n\n static create(init: Partial<BackRemParams> = {}): BackRem {\n const overshoot: number = init.overshoot ?? 0.1;\n return new BackRem({ overshoot });\n }\n}\n","/**\n * PowerBackRem — Power-Back easing Responsive Easing Module.\n *\n * Combines power-law acceleration with overshoot.\n * Unified formula: f_n(u; s) = u^(n-1)[(s+1)u - s]\n *\n * Falls back to pure power when overshoot=0 or exponent<=1.\n */\n\nimport type { NormalizedTime, NormalizedProgress } from '../../lib/types/units';\nimport type {\n KnobSpec,\n NumberKnobSpec,\n PercentKnobSpec,\n} from '../../lib/rem/core/knobs';\nimport {\n EasingRem,\n type EasingRemMetadata,\n type VelocityBoundary,\n} from './EasingRem';\nimport {\n solvePowerBackStrength,\n evalPowerBackIn,\n evalPowerBackVelocity,\n} from '../powerBack';\n\nexport interface PowerBackRemParams {\n /** Power exponent (>= 0.1) */\n readonly exponent: number;\n /** Overshoot depth, normalized 0-1 */\n readonly overshoot: number;\n}\n\nexport class PowerBackRem extends EasingRem<PowerBackRemParams> {\n static readonly EXPONENT_KNOB: NumberKnobSpec = {\n key: 'exponent',\n label: 'Exponent',\n type: 'number',\n default: 3,\n min: 0.1,\n max: 10,\n step: 0.01,\n isPrimary: true,\n } as const;\n\n static readonly OVERSHOOT_KNOB: PercentKnobSpec = {\n key: 'overshoot',\n label: 'Overshoot',\n type: 'percent',\n default: 0,\n min: 0,\n max: 1,\n step: 0.01,\n } as const;\n\n readonly kind = 'power-back' as const;\n\n readonly metadata: EasingRemMetadata = {\n variants: { easeIn: true, easeOut: true },\n modes: { transition: true, pulse: true },\n };\n\n readonly knobSpecs: readonly KnobSpec[] = [\n PowerBackRem.EXPONENT_KNOB,\n PowerBackRem.OVERSHOOT_KNOB,\n ];\n\n /** Cached strength parameter derived from overshoot + exponent */\n private readonly strength: number;\n\n constructor(params: PowerBackRemParams) {\n super(params);\n this.strength = solvePowerBackStrength(params.overshoot, params.exponent);\n }\n\n get easeInBoundary(): VelocityBoundary {\n const n = this.params.exponent;\n // For n > 2: starts at zero velocity\n // For 1 < n <= 2 with overshoot: starts at nonzero (immediate backward motion)\n // For n <= 1 or overshoot=0: same as pure power\n const startIsZero = n > 2 || this.params.overshoot <= 0;\n return {\n start: startIsZero ? 'zero' : 'nonzero',\n end: 'nonzero',\n };\n }\n\n easeIn = (u: NormalizedTime): NormalizedProgress => {\n return evalPowerBackIn(u, this.params.exponent, this.strength);\n };\n\n easeInVelocity = (u: NormalizedTime): number => {\n return evalPowerBackVelocity(u, this.params.exponent, this.strength);\n };\n\n // Analytical: d/du[1 - easeIn(1-u)] = easeInVelocity(1-u)\n easeOutVelocity = (u: NormalizedTime): number => {\n return this.easeInVelocity(1 - u);\n };\n\n with(overrides: Partial<PowerBackRemParams>): PowerBackRem {\n return new PowerBackRem({\n ...this.params,\n ...overrides,\n exponent: Math.max(0.1, overrides.exponent ?? this.params.exponent),\n });\n }\n\n static create(init: Partial<PowerBackRemParams> = {}): PowerBackRem {\n const exponent: number = Math.max(0.1, init.exponent ?? 3);\n const overshoot: number = init.overshoot ?? 0.3;\n return new PowerBackRem({ exponent, overshoot });\n }\n}\n","/**\n * BezierRem — Cubic Bezier easing Responsive Easing Module.\n *\n * Uses cubic Bezier curve with locked endpoints at (0,0) and (1,1).\n * Control points (x1, y1) and (x2, y2) define the curve shape.\n * Velocity is computed via numerical central difference.\n */\n\nimport type { NormalizedTime, NormalizedProgress } from '../../lib/types/units';\nimport type { KnobSpec, NumberKnobSpec } from '../../lib/rem/core/knobs';\nimport { createVelocityFn } from '@penner/easing';\nimport bezierEasing from '@penner/fast-bezier-easing';\nimport {\n EasingRem,\n type EasingRemMetadata,\n type VelocityBoundary,\n} from './EasingRem';\nimport { cachedGetter } from '../../lib/cachedGetter';\n\nexport interface BezierRemParams {\n readonly x1: NormalizedTime;\n readonly y1: NormalizedProgress;\n readonly x2: NormalizedTime;\n readonly y2: NormalizedProgress;\n}\n\nexport class BezierRem extends EasingRem<BezierRemParams> {\n static readonly X1_KNOB: NumberKnobSpec = {\n key: 'x1',\n label: 'X1',\n type: 'number',\n default: 0.42,\n min: 0,\n max: 1,\n step: 0.01,\n } as const;\n\n static readonly Y1_KNOB: NumberKnobSpec = {\n key: 'y1',\n label: 'Y1',\n type: 'number',\n default: 0,\n min: -0.5,\n max: 1.5,\n step: 0.01,\n } as const;\n\n static readonly X2_KNOB: NumberKnobSpec = {\n key: 'x2',\n label: 'X2',\n type: 'number',\n default: 1,\n min: 0,\n max: 1,\n step: 0.01,\n } as const;\n\n static readonly Y2_KNOB: NumberKnobSpec = {\n key: 'y2',\n label: 'Y2',\n type: 'number',\n default: 1,\n min: -0.5,\n max: 1.5,\n step: 0.01,\n } as const;\n\n readonly kind = 'bezier' as const;\n\n readonly metadata: EasingRemMetadata = {\n variants: { easeIn: true, easeOut: true },\n modes: { transition: true, pulse: true },\n };\n\n readonly knobSpecs: readonly KnobSpec[] = [\n BezierRem.X1_KNOB,\n BezierRem.Y1_KNOB,\n BezierRem.X2_KNOB,\n BezierRem.Y2_KNOB,\n ];\n\n /** Both ends depend on control points — always nonzero */\n readonly easeInBoundary: VelocityBoundary = {\n start: 'nonzero',\n end: 'nonzero',\n };\n\n // Bezier is general-purpose — easeIn and easeOut are the same curve,\n // directly defined by the stored control points (no reversal).\n private readonly bezier = bezierEasing(\n this.params.x1,\n this.params.y1,\n this.params.x2,\n this.params.y2,\n );\n\n easeIn = (u: NormalizedTime): NormalizedProgress => this.bezier(u);\n easeOut = (u: NormalizedTime): NormalizedProgress => this.bezier(u);\n\n constructor(params: BezierRemParams) {\n super(params);\n\n // Bezier curves need boundarySample for stable numerical derivatives at endpoints.\n // Single lazy velocity function for both easeIn and easeOut, since they share a curve.\n const velocityFn = () =>\n createVelocityFn(this.bezier, { boundarySample: 0.001 });\n cachedGetter(this, 'easeInVelocity', velocityFn);\n cachedGetter(this, 'easeOutVelocity', velocityFn);\n }\n\n with(overrides: Partial<BezierRemParams>): BezierRem {\n return new BezierRem({ ...this.params, ...overrides });\n }\n\n static create(init: Partial<BezierRemParams> = {}): BezierRem {\n return new BezierRem({\n x1: init.x1 ?? 0.42,\n y1: init.y1 ?? 0,\n x2: init.x2 ?? 1,\n y2: init.y2 ?? 1,\n });\n }\n}\n","/**\n * ExpoRem — Exponential ease-out Responsive Easing Module.\n *\n * Uses canonical normalized exponential decay: f(u) = (1 - e^(-ku)) / (1 - e^(-k))\n * Tail-only: supports easeOut natively, easeIn is the mathematical reverse.\n *\n * Special cases:\n * decay = 0 → linear\n * decay = 1 → step-like limit\n */\n\nimport type { NormalizedTime, NormalizedProgress } from '../../lib/types/units';\nimport type { KnobSpec, PercentKnobSpec } from '../../lib/rem/core/knobs';\nimport { makeExpoEaseOut } from '@penner/easing';\nimport {\n EasingRem,\n type EasingRemMetadata,\n type VelocityBoundary,\n} from './EasingRem';\n\nexport interface ExpoRemParams {\n /** Decay fraction (0-1). 0 = linear, 0.95 = typical, approaching 1 = very steep. */\n readonly decay: number;\n}\n\nexport class ExpoRem extends EasingRem<ExpoRemParams> {\n static readonly DECAY_KNOB: PercentKnobSpec = {\n key: 'decay',\n label: 'Decay',\n type: 'percent',\n default: 0.95,\n min: 0.8,\n max: 0.999,\n step: 0.001,\n isPrimary: true,\n } as const;\n\n readonly kind = 'expo' as const;\n\n readonly metadata: EasingRemMetadata = {\n variants: { easeIn: false, easeOut: true },\n modes: { transition: true, pulse: false },\n };\n\n readonly knobSpecs: readonly KnobSpec[] = [ExpoRem.DECAY_KNOB];\n\n readonly easeInBoundary: VelocityBoundary = {\n start: 'nonzero',\n end: 'nonzero',\n };\n\n /** Cached ease-out function and derivative constants */\n private readonly easeOutFn: (u: number) => number;\n private readonly negK: number;\n private readonly scale: number;\n private readonly d: number;\n\n constructor(params: ExpoRemParams) {\n super(params);\n this.d = Math.max(0, Math.min(1, params.decay));\n\n if (this.d === 0) {\n // Linear\n this.easeOutFn = (u: number) => Math.max(0, Math.min(1, u));\n this.negK = 0;\n this.scale = 1;\n } else if (this.d === 1) {\n // Step-like\n this.easeOutFn = (u: number) => (u > 0 ? 1 : 0);\n this.negK = -Infinity;\n this.scale = 0;\n } else {\n this.negK = Math.log1p(-this.d);\n this.scale = 1 / Math.expm1(this.negK);\n this.easeOutFn = makeExpoEaseOut(this.d);\n }\n }\n\n /** easeIn is the reverse of easeOut (not natively supported, metadata.variants.easeIn = false) */\n easeIn = (u: NormalizedTime): NormalizedProgress => {\n return 1 - this.easeOutFn(1 - u);\n };\n\n easeInVelocity = (u: NormalizedTime): number => {\n return this.easeOutVelocityImpl(1 - u);\n };\n\n easeOut = (u: NormalizedTime): NormalizedProgress => {\n return this.easeOutFn(u);\n };\n\n easeOutVelocity = (u: NormalizedTime): number => {\n return this.easeOutVelocityImpl(u);\n };\n\n private easeOutVelocityImpl(u: number): number {\n if (this.d === 0) return 1;\n if (this.d === 1) return 0;\n return this.negK * Math.exp(this.negK * u) * this.scale;\n }\n\n with(overrides: Partial<ExpoRemParams>): ExpoRem {\n return new ExpoRem({ ...this.params, ...overrides });\n }\n\n static create(init: Partial<ExpoRemParams> = {}): ExpoRem {\n return new ExpoRem({ decay: init.decay ?? 0.95 });\n }\n}\n","/**\n * SpringRem — Spring (damped oscillation) easing Responsive Easing Module.\n *\n * Ease-out only: models energy dissipation via damped sinusoidal oscillation.\n * The spring settles to the target position over N half-cycles with total decay.\n *\n * Internally delegates to SpringZeroLockedTail.build() with minimal head defaults\n * for the standalone easeOut function. Full fuse composition uses the\n * TailCategoryModule layer which provides proper head context.\n */\n\nimport type { NormalizedTime, NormalizedProgress } from '../../lib/types/units';\nimport type {\n KnobSpec,\n NumberKnobSpec,\n PercentKnobSpec,\n} from '../../lib/rem/core/knobs';\nimport type { EasingFn } from '../../lib/types/units';\nimport { SpringZeroLockedTail } from '../spring';\nimport {\n EasingRem,\n type EasingRemMetadata,\n type VelocityBoundary,\n} from './EasingRem';\n\nexport interface SpringRemParams {\n /** Number of half-cycles in the oscillation */\n readonly bounces: number;\n /** Total decay from first to last peak, normalized 0-1 (e.g., 0.95 = last peak is 5% of first) */\n readonly decay: number;\n}\n\n/** Minimal linear head for standalone spring easeOut */\nconst MINIMAL_HEAD: EasingFn = (u: number) => u;\nconst MINIMAL_HEAD_VELOCITY = (_u: number) => 1;\nconst MINIMAL_JOIN_TIME = 0.0001;\n\nexport class SpringRem extends EasingRem<SpringRemParams> {\n static readonly BOUNCES_KNOB: NumberKnobSpec = {\n key: 'bounces',\n label: 'Bounces',\n type: 'number',\n default: 4,\n min: 0,\n max: 10,\n step: 1,\n isPrimary: true,\n } as const;\n\n static readonly DECAY_KNOB: PercentKnobSpec = {\n key: 'decay',\n label: 'Decay',\n type: 'percent',\n default: 0.95,\n min: 0,\n max: 0.99,\n step: 0.01,\n } as const;\n\n readonly kind = 'spring' as const;\n\n readonly metadata: EasingRemMetadata = {\n variants: { easeIn: false, easeOut: true },\n modes: { transition: true, pulse: true },\n };\n\n readonly knobSpecs: readonly KnobSpec[] = [\n SpringRem.BOUNCES_KNOB,\n SpringRem.DECAY_KNOB,\n ];\n\n readonly easeInBoundary: VelocityBoundary = {\n start: 'nonzero',\n end: 'nonzero',\n };\n\n /** Cached standalone easeOut function built with minimal head defaults */\n private readonly builtP: EasingFn;\n private readonly builtV: (u: number) => number;\n\n constructor(params: SpringRemParams) {\n super(params);\n\n // Build standalone spring using minimal head\n const result = SpringZeroLockedTail.build({\n joinTime: MINIMAL_JOIN_TIME,\n bounces: params.bounces,\n decayPct: params.decay * 100, // TailBuildParams uses 0-100\n L: MINIMAL_HEAD,\n Ld: MINIMAL_HEAD_VELOCITY,\n });\n\n this.builtP = result.p;\n this.builtV = result.v;\n }\n\n /** easeIn is the reverse of easeOut (not natively supported) */\n easeIn = (u: NormalizedTime): NormalizedProgress => {\n return 1 - this.builtP(1 - u);\n };\n\n easeInVelocity = (u: NormalizedTime): number => {\n return this.builtV(1 - u);\n };\n\n easeOut = (u: NormalizedTime): NormalizedProgress => {\n return this.builtP(u);\n };\n\n easeOutVelocity = (u: NormalizedTime): number => {\n return this.builtV(u);\n };\n\n /** Access the underlying TailStrategy for full fuse composition */\n static get tailStrategy(): typeof SpringZeroLockedTail {\n return SpringZeroLockedTail;\n }\n\n with(overrides: Partial<SpringRemParams>): SpringRem {\n return new SpringRem({ ...this.params, ...overrides });\n }\n\n static create(init: Partial<SpringRemParams> = {}): SpringRem {\n return new SpringRem({\n bounces: init.bounces ?? 4,\n decay: init.decay ?? 0.95,\n });\n }\n}\n","/**\n * BounceRem — Hard-surface bounce easing Responsive Easing Module.\n *\n * Ease-out only: models energy dissipation via parabolic bouncing physics.\n * Each bounce follows projectile motion with gravity and restitution.\n *\n * Internally delegates to BounceTail.build() with minimal head defaults\n * for the standalone easeOut function.\n */\n\nimport type { NormalizedTime, NormalizedProgress } from '../../lib/types/units';\nimport type {\n KnobSpec,\n NumberKnobSpec,\n PercentKnobSpec,\n} from '../../lib/rem/core/knobs';\nimport type { EasingFn } from '../../lib/types/units';\nimport { BounceTail } from '../bounce';\nimport {\n EasingRem,\n type EasingRemMetadata,\n type VelocityBoundary,\n} from './EasingRem';\n\nexport interface BounceRemParams {\n /** Number of bounce impacts */\n readonly bounces: number;\n /** Total decay from first to last bounce, normalized 0-1 */\n readonly decay: number;\n}\n\n/** Minimal linear head for standalone bounce easeOut */\nconst MINIMAL_HEAD: EasingFn = (u: number) => u;\nconst MINIMAL_HEAD_VELOCITY = (_u: number) => 1;\nconst MINIMAL_JOIN_TIME = 0.0001;\n\nexport class BounceRem extends EasingRem<BounceRemParams> {\n static readonly BOUNCES_KNOB: NumberKnobSpec = {\n key: 'bounces',\n label: 'Bounces',\n type: 'number',\n default: 4,\n min: 1,\n max: 10,\n step: 1,\n isPrimary: true,\n } as const;\n\n static readonly DECAY_KNOB: PercentKnobSpec = {\n key: 'decay',\n label: 'Decay',\n type: 'percent',\n default: 0.95,\n min: 0,\n max: 0.99,\n step: 0.01,\n } as const;\n\n readonly kind = 'bounce' as const;\n\n readonly metadata: EasingRemMetadata = {\n variants: { easeIn: false, easeOut: true },\n modes: { transition: true, pulse: true },\n };\n\n readonly knobSpecs: readonly KnobSpec[] = [\n BounceRem.BOUNCES_KNOB,\n BounceRem.DECAY_KNOB,\n ];\n\n readonly easeInBoundary: VelocityBoundary = {\n start: 'nonzero',\n end: 'nonzero',\n };\n\n /** Cached standalone easeOut function built with minimal head defaults */\n private readonly builtP: EasingFn;\n private readonly builtV: (u: number) => number;\n\n constructor(params: BounceRemParams) {\n super(params);\n\n const result = BounceTail.build({\n joinTime: MINIMAL_JOIN_TIME,\n bounces: params.bounces,\n decayPct: params.decay * 100,\n L: MINIMAL_HEAD,\n Ld: MINIMAL_HEAD_VELOCITY,\n });\n\n this.builtP = result.p;\n this.builtV = result.v;\n }\n\n easeIn = (u: NormalizedTime): NormalizedProgress => {\n return 1 - this.builtP(1 - u);\n };\n\n easeInVelocity = (u: NormalizedTime): number => {\n return this.builtV(1 - u);\n };\n\n easeOut = (u: NormalizedTime): NormalizedProgress => {\n return this.builtP(u);\n };\n\n easeOutVelocity = (u: NormalizedTime): number => {\n return this.builtV(u);\n };\n\n /** Access the underlying TailStrategy for full fuse composition */\n static get tailStrategy(): typeof BounceTail {\n return BounceTail;\n }\n\n with(overrides: Partial<BounceRemParams>): BounceRem {\n return new BounceRem({ ...this.params, ...overrides });\n }\n\n static create(init: Partial<BounceRemParams> = {}): BounceRem {\n return new BounceRem({\n bounces: init.bounces ?? 4,\n decay: init.decay ?? 0.95,\n });\n }\n}\n","/**\n * getEasingRem — Registry/factory for resolving easing kind + params to an EasingRem instance.\n */\n\nimport { type EasingRem } from './EasingRem';\nimport { PowerRem, type PowerRemParams } from './PowerRem';\nimport { BackRem, type BackRemParams } from './BackRem';\nimport { PowerBackRem, type PowerBackRemParams } from './PowerBackRem';\nimport { BezierRem, type BezierRemParams } from './BezierRem';\nimport { ExpoRem, type ExpoRemParams } from './ExpoRem';\nimport { SpringRem, type SpringRemParams } from './SpringRem';\nimport { BounceRem, type BounceRemParams } from './BounceRem';\nimport { SwimRem, type SwimRemParams } from './SwimRem';\nimport type { EasingKind } from '../types';\n\n/** Map from kind to its params type */\nexport interface EasingParamsMap {\n power: Partial<PowerRemParams>;\n back: Partial<BackRemParams>;\n 'power-back': Partial<PowerBackRemParams>;\n bezier: Partial<BezierRemParams>;\n expo: Partial<ExpoRemParams>;\n spring: Partial<SpringRemParams>;\n bounce: Partial<BounceRemParams>;\n swim: Partial<SwimRemParams>;\n}\n\n/** Mapped type: each entry's `create` accepts the correct params for its kind. */\ntype RemClassMap = {\n [K in EasingKind]: { create(init?: EasingParamsMap[K]): EasingRem };\n};\n\nconst remClasses: RemClassMap = {\n power: PowerRem,\n back: BackRem,\n 'power-back': PowerBackRem,\n bezier: BezierRem,\n expo: ExpoRem,\n spring: SpringRem,\n bounce: BounceRem,\n swim: SwimRem,\n};\n\n/**\n * Create an EasingRem instance from a kind string and params.\n *\n * @param kind - The easing type identifier\n * @param params - Optional parameters (uses defaults if omitted)\n * @returns A new EasingRem instance\n *\n * @example\n * const rem = getEasingRem('power', { exponent: 3 });\n * const rem2 = getEasingRem('back'); // uses defaults\n */\nexport function getEasingRem<K extends EasingKind>(\n kind: K,\n params?: EasingParamsMap[K],\n): EasingRem {\n return remClasses[kind].create(params);\n}\n","/**\n * fuse — Create a fused easing from a serializable FuseConfig.\n *\n * A plain JSON-style object that fully describes a fused easing — no class\n * instances, no functions, just data. Easy to serialize, share via URL,\n * and pass to the fuse() factory.\n *\n * All percentage-like values (overshoot, decay) are normalized to [0, 1].\n */\n\nimport type { NormalizedTime } from '../lib/types/units';\nimport type { EasingKind } from './types';\nimport type { EasingKit } from '@penner/easing';\nimport type { PowerRemParams } from './rem/PowerRem';\nimport type { BackRemParams } from './rem/BackRem';\nimport type { PowerBackRemParams } from './rem/PowerBackRem';\nimport type { BezierRemParams } from './rem/BezierRem';\nimport type { ExpoRemParams } from './rem/ExpoRem';\nimport type { SpringRemParams } from './rem/SpringRem';\nimport type { BounceRemParams } from './rem/BounceRem';\nimport type { SwimRemParams } from './rem/SwimRem';\n\n// ─── EasingDef discriminated union ───────────────────────────────────\n\n/** Base for all easing definitions — discriminated on `kind` */\ninterface EasingDefBase<K extends EasingKind> {\n readonly kind: K;\n}\n\n/** Per-kind definitions with type-safe params (all normalized 0-1) */\nexport interface PowerDef extends EasingDefBase<'power'>, PowerRemParams {}\nexport interface BackDef extends EasingDefBase<'back'>, BackRemParams {}\nexport interface PowerBackDef\n extends EasingDefBase<'power-back'>,\n PowerBackRemParams {}\nexport interface BezierDef extends EasingDefBase<'bezier'>, BezierRemParams {}\nexport interface ExpoDef extends EasingDefBase<'expo'>, ExpoRemParams {}\nexport interface SpringDef extends EasingDefBase<'spring'>, SpringRemParams {}\nexport interface BounceDef extends EasingDefBase<'bounce'>, BounceRemParams {}\nexport interface SwimDef extends EasingDefBase<'swim'>, SwimRemParams {}\n\n/** Discriminated union of all easing definitions */\nexport type AnyEasingDef =\n | PowerDef\n | BackDef\n | PowerBackDef\n | BezierDef\n | ExpoDef\n | SpringDef\n | BounceDef\n | SwimDef;\n\n// ─── FuseConfig ──────────────────────────────────────────────────────\n\n/**\n * Serializable fuse configuration — generic over head and tail types.\n *\n * @example\n * const config: FuseConfig = {\n * head: { kind: 'power-back', exponent: 3, overshoot: 0.15 },\n * tail: { kind: 'spring', bounces: 4, decay: 0.95 },\n * joinTime: 0.6,\n * };\n * const result = fuse(config);\n */\nexport interface FuseConfig<\n H extends AnyEasingDef = AnyEasingDef,\n T extends AnyEasingDef = AnyEasingDef,\n> {\n /** @default 'transition' — most common; pulse is the special case */\n movement?: 'transition' | 'pulse';\n head: H;\n tail: T;\n joinTime?: NormalizedTime;\n /** When true, the tail is created by reversing the head easing (easeIn → easeOut),\n * so both phases use the same curve shape. The tail EasingDef is ignored.\n * This produces symmetric \"inOut\" style curves from a single head definition.\n * Only applies to regular tails (not spring/bounce pulse tails). */\n mirror?: boolean;\n /** Enable bridge for pulse tails. @default true */\n useBridge?: boolean;\n /** Bridge tuning parameters */\n bridgeTuning?: {\n headWeight?: number;\n freqWeight?: number;\n freqExponent?: number;\n baseMultiplier?: number;\n };\n /** Allow generalized back tail when joinHeight is clamped to 1.0 */\n allowGeneralizedBackTail?: boolean;\n /**\n * Maximum velocity at the join point. When the head's natural ending\n * velocity exceeds this, a constant-velocity cruise phase is inserted\n * between the head and tail. The head ends early (when it hits maxSpeed),\n * then cruises at maxSpeed until the join point.\n *\n * Only applies to transition tails (power, back, power-back).\n * When undefined or Infinity, no cruise — current behavior.\n */\n maxSpeed?: number;\n}\n\n// ─── fuse factory ──────────────────────────────────────────────\n\nimport { composeFuse } from './composeFuse';\nimport { getEasingRem } from './rem/getEasingRem';\n\n/**\n * Strip `kind` from an EasingDef, returning the params for getEasingRem.\n */\nfunction defToParams(def: AnyEasingDef): Record<string, unknown> {\n const { kind: _, ...params } = def;\n return params;\n}\n\n/**\n * Create a fused easing from a serializable FuseConfig.\n *\n * Resolves each EasingDef to an EasingRem, then delegates to composeFuse\n * for composition. Returns an EasingKit with position, velocity, and metadata.\n *\n * @example\n * const result = fuse({\n * head: { kind: 'power', exponent: 2 },\n * tail: { kind: 'back', overshoot: 0.1 },\n * joinTime: 0.5,\n * });\n * const y = result.easingFn(0.5);\n */\nexport function fuse(config: FuseConfig): EasingKit {\n const {\n movement = 'transition',\n head,\n tail,\n joinTime = 0.5,\n mirror = false,\n useBridge,\n bridgeTuning,\n allowGeneralizedBackTail,\n maxSpeed,\n } = config;\n\n const headRem = getEasingRem(head.kind, defToParams(head));\n const tailRem = getEasingRem(tail.kind, defToParams(tail));\n\n return composeFuse(headRem, tailRem, {\n movement,\n joinTime,\n mirror,\n useBridge,\n bridgeTuning,\n allowGeneralizedBackTail,\n maxSpeed,\n });\n}\n","/**\n * Fuse module - head/tail strategy registries, constraint utilities, and REM-based fuse creation\n *\n * Provides strategy registries for head and tail easings, Bezier constraint utilities\n * for UI integration, pulse motion construction, and the REM (Responsive Easing Module)\n * API for creating fused easings with responsive breakpoints.\n */\n\n/**\n * Epsilon threshold for join point edge cases\n *\n * When the join point is within this threshold of 0 or 1,\n * special handling is applied to avoid division by zero and\n * numerical instability:\n * - join < FUSE_JOIN_EPSILON: treat as pure tail (no head phase)\n * - join > 1 - FUSE_JOIN_EPSILON: treat as pure head (no tail phase)\n *\n * This value is chosen to be small enough to not affect normal usage\n * but large enough to catch edge cases before they cause numerical issues.\n */\nconst FUSE_JOIN_EPSILON = 0.000001;\n\n/**\n * Minimum valid join point (inclusive)\n * Any join value below this is treated as 0 (tail only)\n */\nconst FUSE_JOIN_MIN = FUSE_JOIN_EPSILON;\n\n/**\n * Maximum valid join point (inclusive)\n * Any join value above this is treated as 1 (head only)\n */\nconst FUSE_JOIN_MAX = 1 - FUSE_JOIN_EPSILON;\n\n// Export for use in tests and other modules\nexport { FUSE_JOIN_EPSILON, FUSE_JOIN_MIN, FUSE_JOIN_MAX };\n\nimport type { EasingKind } from './types';\nimport { BounceTail } from './bounce';\nimport { ElasticZeroLockedTail } from './spring';\nimport {\n createBackTail,\n createPowerTail,\n createPowerBackTail,\n createBezierTail,\n createExpoTail,\n} from './tails';\n\n// Re-export types\nexport type {\n TailStrategy,\n TailBuildParams,\n TailBuildResult,\n EasingKind,\n HeadType,\n TailType,\n RegularTailType,\n PulseTailType,\n} from './types';\n\n// Re-export head strategies\nexport type { HeadStrategy } from './heads';\nexport {\n headStrategies,\n getHeadStrategy,\n createBackHead,\n createPowerHead,\n createPowerBackHead,\n createBezierHead,\n createSwimHead,\n} from './heads';\n\n// Re-export power-back mathematics utilities\nexport {\n getLocalMinimumPosition,\n findExponentForMinimumPosition,\n getPowerCurveCanonicalPoint,\n solvePowerBackStrength,\n overshootFromX,\n evalPowerBackIn,\n evalPowerBackVelocity,\n} from './powerBack';\n\n// Re-export regular tail strategy factories (net displacement)\nexport type { RegularTailStrategy } from './tails';\nexport {\n tailStrategies as regularTailStrategies,\n getTailStrategy,\n createBackTail,\n createPowerTail,\n createPowerBackTail,\n createBezierTail,\n createExpoTail,\n} from './tails';\n\n// Re-export pulse tail strategy objects (net-zero displacement)\nexport { BounceTail } from './bounce';\nexport {\n SpringZeroLockedTail as ElasticZeroLockedTail,\n SpringTail,\n} from './spring';\n\n// Re-export pulse motion construction (net-zero displacement easing functions)\nexport {\n pulse,\n createOutInPulse,\n createMirroredPulse,\n createSimplePulse,\n createPulseWithReturn,\n createSpringReturn,\n createBounceReturn,\n normalizePulse,\n normalizePulseToJoin,\n} from './pulse';\nexport type {\n PulseEasingType,\n PulseEasingConfig,\n PulseReturnConfig,\n SimplePulseConfig,\n} from './pulse';\n\n// Re-export Bezier join constraint utilities\nexport type { Point2D, ConstraintResult } from './bezier-join-constraint';\nexport {\n calculateTailP1FromHeadP2,\n calculateHeadP2FromTailP1,\n getJoinSlope,\n verifyContinuity,\n} from './bezier-join-constraint';\n\n/**\n * Clamp Bezier y2 value based on tail type and feature flags\n *\n * When combining Bezier head with power-back/back/power tails, y2 must be < 1.0\n * to prevent negative end slope (which breaks tail behavior). However, y2 can be\n * negative to allow flexibility in curve design.\n *\n * Since power-back is the generalized form containing both power and back as\n * special cases, all three tail types use the same clamping logic.\n *\n * UI components should use this function to constrain slider/control point ranges.\n *\n * @param y2 - The desired y2 value\n * @param tailType - The tail type being used\n * @param allowGeneralizedBackTail - Whether to allow y2 >= 1.0 (advanced feature)\n * @returns The clamped y2 value\n *\n * @example\n * ```ts\n * // In UI code:\n * const clampedY2 = clampBezierY2ForTail(userY2, 'power-back', false);\n * // Use clampedY2 for slider max or control point bounds\n * ```\n */\nexport function clampBezierY2ForTail(\n y2: number,\n tailType: EasingKind,\n allowGeneralizedBackTail = false,\n): number {\n // Power-back is the generalized form containing both power and back\n // All three use the same clamping: max only, allow negative y2\n if (\n !allowGeneralizedBackTail &&\n (tailType === 'power-back' || tailType === 'back' || tailType === 'power')\n ) {\n // Only clamp maximum to prevent negative slope\n // Allow negative y2 for curve flexibility\n return Math.min(1, y2);\n }\n return y2;\n}\n\n/**\n * Clamp Bezier y1 value based on head type and feature flags\n *\n * When combining power-back head with Bezier tail, y1 must be > 0 to prevent\n * negative start slope (which breaks power-back head behavior). However, y1 can\n * exceed 1.0 to allow flexibility in curve design.\n *\n * UI components should use this function to constrain slider/control point ranges.\n *\n * @param y1 - The desired y1 value\n * @param headType - The head type being used\n * @param allowGeneralizedBackHead - Whether to allow y1 <= 0 (advanced feature)\n * @returns The clamped y1 value\n *\n * @example\n * ```ts\n * // In UI code:\n * const clampedY1 = clampBezierY1ForHead(userY1, 'power-back', false);\n * // Use clampedY1 for slider min or control point bounds\n * ```\n */\nexport function clampBezierY1ForHead(\n y1: number,\n headType: EasingKind,\n allowGeneralizedBackHead = false,\n): number {\n // Only clamp for power-back head when flag is false\n if (!allowGeneralizedBackHead && headType === 'power-back') {\n // Power-back head: only clamp minimum to prevent negative slope\n // Allow y1 > 1.0 for curve flexibility\n return Math.max(0, y1);\n }\n return y1;\n}\n\n/**\n * Unified registry of all tail strategies\n *\n * Includes both regular tail factories (back, power) with net displacement,\n * and pulse tail strategy objects (bounce, spring) with net-zero displacement.\n *\n * Note: These have different APIs:\n * - back/power: Factory functions that return regular tail strategies (move from position A to B)\n * - bounce/spring: Strategy objects with build() methods for pulse tails (oscillate and settle)\n *\n * @example\n * ```ts\n * // Regular tails (net displacement)\n * const backTail = allTailStrategies.back(0.1); // overshoot parameter\n * const powerTail = allTailStrategies.power(3); // exponent parameter\n * ```\n */\nexport const allTailStrategies = {\n // Regular tail factories (net displacement)\n back: createBackTail,\n power: createPowerTail,\n 'power-back': createPowerBackTail,\n bezier: createBezierTail,\n expo: createExpoTail,\n // Pulse tail strategy objects (net-zero displacement)\n bounce: BounceTail,\n spring: ElasticZeroLockedTail,\n} as const;\n\n// REM (Responsive Easing Module) exports\nexport { composeFuse, type ComposeFuseOptions } from './composeFuse';\nexport { fuse } from './fuse';\nexport type {\n FuseConfig,\n AnyEasingDef,\n PowerDef,\n BackDef,\n PowerBackDef,\n BezierDef,\n ExpoDef,\n SpringDef,\n BounceDef,\n SwimDef,\n} from './fuse';\nexport type { EasingKit } from '@penner/easing';\nexport type {\n VelocityBoundary,\n VelocityConstraint,\n EasingRemMetadata,\n} from './rem/EasingRem';\nexport { EasingRem } from './rem/EasingRem';\nexport { getEasingRem } from './rem/getEasingRem';\nexport type { EasingParamsMap } from './rem/getEasingRem';\nexport { PowerRem } from './rem/PowerRem';\nexport { BackRem } from './rem/BackRem';\nexport { PowerBackRem } from './rem/PowerBackRem';\nexport { BezierRem } from './rem/BezierRem';\nexport { ExpoRem } from './rem/ExpoRem';\nexport { SpringRem } from './rem/SpringRem';\nexport { BounceRem } from './rem/BounceRem';\nexport { SwimRem } from './rem/SwimRem';\n"],"names":["DEFAULT_STRENGTH","DEFAULT_STRENGTH_PLUS_1","defaultEaseInBack","u","defaultEaseInBackDerivative","BounceTail","joinTime","bounces","decayPct","L","Ld","FUSE_JOIN_MIN","clamp01","_u","FUSE_JOIN_MAX","fullCount","fracPart","r","finalRatio","leftPos","leftVel","vMinus","v0","T","floorCount","geom","g","minHeadPos","sampleCount","i","pos","maxAllowedHeight","theoreticalFirstBounce","v0_clamped","shouldClampHeight","settleTime","geom_clamped","actualTimeTaken","v0_final","segDur","_","n","edges","dt","vSeg","rightPos","e","u0","t","rightVel","p","v","finalPct","CRITICALLY_DAMPED_OMEGA","buildCriticallyDamped","bridge","omega","pulseStart","alpha","P","denom","dhoStart","x0","v0_local","B_coeff","dhoPosition","tau","decay","dhoVelocity","headP","Lin","LinPrime","setupNonBridgeTail","totalDecay","Nbase","amplitudeRatio","N","omegaD","gamma","dampingRate","isTailOnly","w","Ld1","headSlope","tailFreq","buildQuadraticSine","setup","dTarget","P_bridge","P_amp","dQuarter","dMax","dMin","d","dLinear","accel","bridgePos","bridgeVel","vBridgeEnd","alphaOsc","wOsc","amp","oscPos","oscVel","bridgeEnd","buildUnderdampedTail","SpringZeroLockedTail","headVelocityFn","tuning","headVelocity","headWeight","freqWeight","freqExponent","baseMultiplier","frequencyFactor","headEnd","B","k","E_sin","x","Ed_sin","guardMessage","firstPeakX_sin","firstPeakAmplitude_sin","denom_sin","R_sin","y","Rd_sin","calculateBridgeAmplitude","calculateNonBridgeBaseline","calculatePulsePosition","pulseY","calculatePulseVelocity","calculateScalingFactor","Rd0","factor","ElasticZeroLockedTail","Z","h","b","s","D","z","I","X","A","l","q","F","o","M","Y","a","c","f","O","U","j","m","overshootFromX","c_n","solvePowerBackStrength","overshoot","exponent","target","lo","hi","mid","evalPowerBackIn","strength","result","evalPowerBackVelocity","getLocalMinimumPosition","getPowerCurveCanonicalPoint","findExponentForMinimumPosition","targetUMin","minExponent","maxExponent","computeUMin","uMinAtLow","uMinAtHigh","uMinMid","createBackTail","solveBackStrength","easing","derivative","createPowerTail","createPowerBackTail","easingFn","velocityFn","createBezierTail","x1","y1","x2","y2","bezier","bezierEasing","createVelocityFn","createExpoTail","negK","scale","makeExpoEaseOut","tailStrategies","getTailStrategy","type","config","factory","cachedGetter","key","compute","cached","EasingRem","params","SwimRem","fn","swim","overrides","init","createBackHead","strengthPlus1","createOutInPulse","outEasing","inEasing","join","τ","createSpringReturn","kOverOmega","endRaw","createBounceReturn","geomSum","tDrop","vImpact","flightTime","seg","v_n","createPulseWithReturn","headEasing","returnFn","calculateTailP1FromHeadP2","headP2","_joinTime","calculateHeadP2FromTailP1","tailP1","PowerRem","JOIN_AT_START_THRESHOLD","JOIN_AT_END_THRESHOLD","MIN_JOIN_TIME","MAX_JOIN_TIME","isJoinAtBoundary","clamped","calculateJoinHeightForContinuity","headVelocityAtEnd","tailVelocityAtStart","denominator","joinHeight","warning","calculatePulseTailBridge","pulseNaturalStartVelocity","isBounceStrategy","bridgeVelocity","bridgeDuration","shouldAttemptCruise","maxSpeed","computeNaturalJoinVelocity","naturalJoinHeight","isValid","computeMinCruiseSpeed","solveCruiseParams","input","naturalJoinHeightIsValid","minSpeed","cruiseSpeed","tStar","K","R","jK","tC","headHeight","tCClamped","tailStartPos","tailDuration","buildCruiseEasing","headEasingFn","tailEasingFn","localTime","createSpringHeadFallback","headBounces","headDecay","tailBounces","tailDecay","springTailBuilder","headPulseFullResult","fullReversed","reverseEasingFn","rawSpringHead","rawSpringHeadVelocity","headScale","scaledSpringHead","scaledSpringHeadVelocity","SPRING_SPRING_BRIDGE_VELOCITY","createSpringToSpringFuse","tailPulseFullResult","joinPosition","headBridgeDuration","tailBridgeDuration","headPulseDuration","tailPulseStart","tailPulseDuration","tailScale","headPulseEndY","tailPulseStartValue","headPulseEnd","bridgeProgress","tailPulseStartY","rawTailValue","createLinearPowerHead","createComplementaryCurveFuse","fuseImpl","pulseTail","useBridge","bridgeTuning","complementaryHead","complementaryJoinTime","fusedComplementary","remParams","rem","composeFuse","headRem","tailRem","options","movement","mirror","allowGeneralizedBackTail","meta","composePulse","composeTransition","risingEasing","easingKit","ctx","boundary","composeSpringHead","composeOscillatoryTail","composeBezierBezier","composeRegular","headParams","headSpringBounces","headSpringDecay","tailParams","fallback","tailStrategy","fuseConfig","buildFromTailStrategy","tailVelocityFn","complementaryHeadEaseIn","complementaryHeadVelocity","joinComplementary","strategy","headEaseIn","bridgeParams","isSpring","vPulseNatural","bridgeResult","head_p1x","head_p1y","head_p2x","head_p2y","head_p3x","head_p3y","slope","tail_p1x_global","tail_p1y_global","tail_p2x_global","tail_p2y_global","head_cp1x_norm","head_cp1y_norm","head_cp2x_norm","head_cp2y_norm","headBezier","tail_cp1x_norm","tail_cp1y_norm","tail_cp2x_norm","tail_cp2y_norm","tailBezier","naturalJH","cruiseParams","useGeneralizedBackTail","effectiveHeadVelocityAtEnd","joinHeightResult","unclampedJoinHeight","C","customHead","customHeadVelocityFn","BackRem","backEaseIn","backEaseInVelocity","PowerBackRem","BezierRem","ExpoRem","MINIMAL_HEAD","MINIMAL_HEAD_VELOCITY","MINIMAL_JOIN_TIME","SpringRem","BounceRem","remClasses","getEasingRem","kind","defToParams","def","fuse","head","tail","FUSE_JOIN_EPSILON","clampBezierY2ForTail","tailType","clampBezierY1ForHead","headType","allowGeneralizedBackHead"],"mappings":"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"}