{"version":3,"file":"index.cjs","sources":["../src/Rk4thSpring.ts"],"sourcesContent":["import { Callback, Options, Preset, PresetName } from \"./types\";\n\n/**\n * @class Rk4thSpring\n * @preserve\n * A spring animation class that simulates a damped harmonic oscillator.\n */\nexport class Rk4thSpring {\n    // Physics constants\n    mass: number;\n    tension: number;\n    friction: number;\n    precision: number;\n\n    // Animation state\n    value: number;\n    velocity: number;\n    targetValue: number;\n\n    // Animation frame tracking\n    lastTime: number | null;\n    isAnimating: boolean;\n    frameCallback: Callback | null;\n\n    // Event callbacks\n    onStart: Callback | null;\n    onUpdate: Callback | null;\n    onEnd: Callback | null;\n\n    constructor(options: Options = {}) {\n        const {\n            mass = 1,\n            tension = 170,\n            friction = null,\n            precision = 0.01,\n            initialValue = 0,\n            velocity = 0,\n            targetValue = 0,\n            onStart = null,\n            onUpdate = null,\n            onEnd = null,\n        } = options;\n\n        // Physics constants\n        this.mass = mass;\n        this.tension = tension;\n        this.friction = friction !== null ? friction : this.calculateCriticalDamping();\n        this.precision = precision;\n\n        // Animation state\n        this.value = initialValue;\n        this.velocity = velocity;\n        this.targetValue = targetValue;\n\n        // Animation frame tracking\n        this.lastTime = null;\n        this.isAnimating = false;\n        this.frameCallback = null;\n\n        // Event callbacks\n        this.onStart = onStart;\n        this.onUpdate = onUpdate;\n        this.onEnd = onEnd;\n\n        // Bind the animate method to optimize performance\n        this.animate = this.animate.bind(this);\n    }\n\n    static presets: { [key in PresetName]: Preset } = {\n        gentle: {mass: 1, tension: 120},\n        wobbly: {mass: 1, tension: 180},\n        stiff: {mass: 1, tension: 210},\n        slow: {mass: 1, tension: 280},\n        molasses: {mass: 1, tension: 280, friction: 120},\n    };\n\n    /**\n     * Creates a new SpringAnimation instance using a preset.\n     * @param presetName - The name of the preset to use.\n     * @param options - Additional options to override preset defaults.\n     * @returns A new SpringAnimation instance.\n     */\n    static createWithPreset(\n        presetName: PresetName,\n        options: Options = {}\n    ): Rk4thSpring {\n        const preset = Rk4thSpring.presets[presetName];\n        if (!preset) {\n            throw new Error(`Preset ${presetName} not found.`);\n        }\n        return new Rk4thSpring({...preset, ...options});\n    }\n\n    /**\n     * Applies a preset configuration to the current instance.\n     * @param presetName - The name of the preset to apply.\n     */\n    applyPreset(presetName: PresetName): void {\n        const preset = Rk4thSpring.presets[presetName];\n        if (preset) {\n            this.mass = preset.mass;\n            this.tension = preset.tension;\n            this.friction =\n                preset.friction !== undefined\n                    ? preset.friction\n                    : this.calculateCriticalDamping();\n        }\n    }\n\n    /**\n     * Calculates the critical damping coefficient.\n     * @returns The critical damping coefficient.\n     */\n    calculateCriticalDamping(): number {\n        return 2 * Math.sqrt(this.mass * this.tension);\n    }\n\n    /**\n     * Calculates the spring force using Hooke's law.\n     * @param distance - The distance from the equilibrium position.\n     * @returns The spring force.\n     */\n    calculateSpringForce(distance: number): number {\n        return -this.tension * distance;\n    }\n\n    /**\n     * Calculates the damping force.\n     * @param velocity - The current velocity.\n     * @returns The damping force.\n     */\n    calculateDampingForce(velocity: number): number {\n        return -this.friction * velocity;\n    }\n\n    /**\n     * Helper method to calculate acceleration.\n     * @param position - The current position.\n     * @param velocity - The current velocity.\n     * @returns The acceleration.\n     */\n    acceleration(position: number, velocity: number): number {\n        const distance = position - this.targetValue;\n        const springForce = this.calculateSpringForce(distance);\n        const dampingForce = this.calculateDampingForce(velocity);\n        return (springForce + dampingForce) / this.mass;\n    }\n\n    /**\n     * Updates the spring physics using the Runge-Kutta 4th Order method.\n     * @param deltaTime - The time elapsed since the last update.\n     * @returns Whether the animation should stop.\n     */\n    update(deltaTime: number): boolean {\n        const k1_v = this.acceleration(this.value, this.velocity) * deltaTime;\n        const k1_x = this.velocity * deltaTime;\n\n        const k2_v =\n            this.acceleration(this.value + 0.5 * k1_x, this.velocity + 0.5 * k1_v) *\n            deltaTime;\n        const k2_x = (this.velocity + 0.5 * k1_v) * deltaTime;\n\n        const k3_v =\n            this.acceleration(this.value + 0.5 * k2_x, this.velocity + 0.5 * k2_v) *\n            deltaTime;\n        const k3_x = (this.velocity + 0.5 * k2_v) * deltaTime;\n\n        const k4_v =\n            this.acceleration(this.value + k3_x, this.velocity + k3_v) * deltaTime;\n        const k4_x = (this.velocity + k3_v) * deltaTime;\n\n        this.velocity += (1 / 6) * (k1_v + 2 * k2_v + 2 * k3_v + k4_v);\n        this.value += (1 / 6) * (k1_x + 2 * k2_x + 2 * k3_x + k4_x);\n\n        const distance = this.value - this.targetValue;\n\n        // Energy-based stopping condition\n        const kineticEnergy = 0.5 * this.mass * this.velocity ** 2;\n        const potentialEnergy = 0.5 * this.tension * distance ** 2;\n        const totalEnergy = kineticEnergy + potentialEnergy;\n\n        return totalEnergy < this.precision;\n    }\n\n    /**\n     * Starts the spring animation towards the target value.\n     * @param targetValue - The value to animate towards.\n     * @param callback - The callback function to execute on each frame.\n     */\n    start(targetValue: number, callback?: (value: number) => void): void {\n        this.targetValue = targetValue;\n        this.frameCallback = callback || null;\n\n        if (!this.isAnimating) {\n            this.isAnimating = true;\n            this.lastTime =\n                typeof performance !== \"undefined\" ? performance.now() : Date.now();\n            if (this.onStart) {\n                this.onStart(this.value);\n            }\n            this.animate();\n        }\n    }\n\n    /**\n     * The animation loop that updates the physics and handles callbacks.\n     */\n    animate(): void {\n        if (!this.isAnimating) return;\n\n        const currentTime =\n            typeof performance !== \"undefined\" ? performance.now() : Date.now();\n        const deltaTime = (currentTime - (this.lastTime as number)) / 1000; // Convert to seconds\n        this.lastTime = currentTime;\n\n        const shouldStop = this.update(deltaTime);\n\n        if (this.frameCallback) {\n            this.frameCallback(this.value);\n        }\n\n        if (this.onUpdate) {\n            this.onUpdate(this.value);\n        }\n\n        if (shouldStop) {\n            this.isAnimating = false;\n            this.velocity = 0;\n            this.value = this.targetValue;\n            if (this.frameCallback) {\n                this.frameCallback(this.value);\n            }\n            if (this.onEnd) {\n                this.onEnd(this.value);\n            }\n        } else {\n            this.requestAnimationFrame();\n        }\n    }\n\n\n    private requestAnimationFrame() {\n        requestAnimationFrame(this.animate);\n    }\n\n    /**\n     * Sets the current value of the animation.\n     * @param value - The new value to set.\n     * @param resetVelocity - Whether to reset the velocity to zero.\n     */\n    setValue(value: number, resetVelocity: boolean = false): void {\n        this.value = value;\n        if (resetVelocity) {\n            this.velocity = 0;\n        }\n    }\n\n    /**\n     * Stops the animation.\n     */\n    stop(): void {\n        this.isAnimating = false;\n    }\n\n}\n"],"names":["_Rk4thSpring","constructor","options","mass","tension","friction","precision","initialValue","velocity","targetValue","onStart","onUpdate","onEnd","this","calculateCriticalDamping","value","lastTime","isAnimating","frameCallback","animate","bind","createWithPreset","presetName","preset","presets","Error","applyPreset","Math","sqrt","calculateSpringForce","distance","calculateDampingForce","acceleration","position","update","deltaTime","k1_v","k1_x","k2_v","k2_x","k3_v","k3_x","k4_v","k4_x","start","callback","performance","now","Date","currentTime","shouldStop","requestAnimationFrame","setValue","resetVelocity","stop","gentle","wobbly","stiff","slow","molasses","Rk4thSpring"],"mappings":"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"}