interpolated 0.1.2

Generic, smooth value interpolation and easing functions for Rust.
Documentation
//! Smooth interpolation between values over time.
//!
//! The `Interpolated<T>` type animates changes between values of type `T`
//! by sampling an easing function over a specified duration.
//!
//! # Type Parameters
//!
//! - `T`: Value type that implements `Copy`, `Add<Output = T>`, `Sub<Output = T>`,
//!   and `Mul<f32, Output = T>`, such as floats or vectors.
//!
//! # Examples
//!
//! ```rust
//! use interpolated::Interpolated;
//! use interpolated::linear;
//! use std::time::Duration;
//!
//! // Animate a `f32` from 0.0 to 1.0 over 1 second.
//! let mut interp = Interpolated::new(0.0f32);
//! interp.set_duration(Duration::from_secs(1));
//! interp.transition = linear;
//! interp.set(1.0);
//!
//! // In your update loop...
//! while !interp.is_finished() {
//!     let current = interp.value();
//!     println!("Value: {}", current);
//!     // sleep or wait...
//! }
//! assert_eq!(interp.value(), 1.0);
//! ```

use std::ops::{Add, Sub, Mul};
use std::time::{Duration, Instant};

use crate::functions::EasingFn;

/// A value that interpolates smoothly from a start to an end over time
/// using an easing function.
pub struct Interpolated<T> {
    start:      T,
    end:        T,
    start_time: Instant,
    /// Interpolation speed, equal to \(1.0 / \text{duration\_seconds}\).
    pub speed: f32,
    /// Easing function applied to the normalized time \(t \in [0,1]\).
    pub transition: EasingFn,
}

impl<T> Interpolated<T>
where
    T: Copy + Add<Output = T> + Sub<Output = T> + Mul<f32, Output = T>,
{
    /// Create a new interpolator with no movement (instantaneous).
    ///
    /// The initial `start` and `end` values are set to `initial`,
    /// and the default transition is linear.
    pub fn new(initial: T) -> Self {
        let now = Instant::now();
        Interpolated {
            start:        initial,
            end:          initial,
            start_time:   now,
            speed:        1.0,
            transition:   crate::functions::linear,
        }
    }

    /// Set the total duration for the interpolation.
    ///
    /// The `speed` is computed as \(1.0 / \text{duration\_seconds}\).
    pub fn set_duration(&mut self, d: Duration) {
        self.speed = 1.0 / d.as_secs_f32();
    }

    /// Set a new target value, restarting the interpolation from the current value.
    ///
    /// Captures the current value as the new `start` and sets `end` to `new_end`.
    pub fn set(&mut self, new_end: T) {
        self.start = self.value();
        self.end = new_end;
        self.start_time = Instant::now();
    }

    /// Retrieve the current interpolated value according to the easing function.
    ///
    /// Returns `end` once the elapsed time reaches the duration.
    pub fn value(&self) -> T {
        let t = (Instant::now() - self.start_time).as_secs_f32() * self.speed;
        if t >= 1.0 {
            return self.end;
        }
        let r = (self.transition)(t.clamp(0.0, 1.0));
        self.start + (self.end - self.start) * r
    }

    /// Check if the interpolation has finished (elapsed time ≥ duration).
    pub fn is_finished(&self) -> bool {
        (Instant::now() - self.start_time).as_secs_f32() * self.speed >= 1.0
    }
}