{"version":3,"file":"index.mjs","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":[],"mappings":"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"}