easings 0.1.0

Robert Penner's easing functions
Documentation
//! There are a few ports of Robert Penner's easing functions in `crates.io`. This one is different
//! in that all functions take a `t ∈ [0, 1]` and return a value in the interval `[0, 1]`.
//!
//! Derived from https://github.com/warrenm/AHEasing/blob/master/AHEasing/easing.c
use std::f64::consts::PI;

/// Modeled after the line `y = x`
#[inline]
pub fn linear(t: f64) -> f64 {
    t
}

/// Modeled after the parabola `y = x^2`
#[inline]
pub fn quadratic_in(t: f64) -> f64 {
    t * t
}

/// Modeled after the parabola `y = -x^2 + 2x`
#[inline]
pub fn quadratic_out(t: f64) -> f64 {
    -(t * (t - 2.))
}

/// Modeled after the piecewise quadratic
/// ```text
/// y = (1/2)((2x)^2)             ; [0, 0.5)
/// y = -(1/2)((2x-1)*(2x-3) - 1) ; [0.5, 1]
/// ```
#[inline]
pub fn quadratic_in_out(t: f64) -> f64 {
    if t < 0.5 {
        2. * t * t
    } else {
        (-2. * t * t) + (4. * t) - 1.
    }
}

/// Modeled after the cubic y = x^3
#[inline]
pub fn cubic_in(t: f64) -> f64 {
    t * t * t
}

/// Modeled after the cubic y = (x - 1)^3 + 1
#[inline]
pub fn cubic_out(t: f64) -> f64 {
    let f = t - 1.;
    f * f * f + 1.
}

/// Modeled after the piecewise cubic
/// ```text
/// y = (1/2)((2x)^3)       ; [0, 0.5)
/// y = (1/2)((2x-2)^3 + 2) ; [0.5, 1]
/// ```
#[inline]
pub fn cubic_in_out(t: f64) -> f64 {
    if t < 0.5 {
        4. * t * t * t
    } else {
        let f = (2. * t) - 2.;
        0.5 * f * f * f + 1.
    }
}

/// Modeled after the quartic y = x^4
#[inline]
pub fn quartic_in(t: f64) -> f64 {
    t * t * t * t
}

/// Modeled after the quartic y = 1 - (x - 1)^4
#[inline]
pub fn quartic_out(t: f64) -> f64 {
    let f = t - 1.;
    f * f * f + (1. - t) + 1.
}

/// Modeled after the piecewise quartic
/// ```text
/// y = (1/2)((2x)^4)        ; [0, 0.5)
/// y = -(1/2)((2x-2)^4 - 2) ; [0.5, 1]
/// ```
#[inline]
pub fn quartic_in_out(t: f64) -> f64 {
    if t < 0.5 {
        8. * t * t * t * t
    } else {
        let f = t - 1.;
        -8. * f * f * f * f + 1.
    }
}

/// Modeled after the quintic y = x^5
#[inline]
pub fn quintic_in(t: f64) -> f64 {
    t * t * t * t * t
}

/// Modeled after the quintic y = (x - 1)^5 + 1
#[inline]
pub fn quintic_out(t: f64) -> f64 {
    let f = t - 1.;
    f * f * f * f * f + 1.
}

/// Modeled after the piecewise quintic
/// ```text
/// y = (1/2)((2x)^5)       ; [0, 0.5)
/// y = (1/2)((2x-2)^5 + 2) ; [0.5, 1]
/// ```
#[inline]
pub fn quintic_in_out(t: f64) -> f64 {
    if t < 0.5 {
        16. * t * t * t * t * t
    } else {
        let f = (2. * t) - 2.;
        0.5 * f * f * f * f * f + 1.
    }
}

/// Modeled after quarter-cycle of sine wave
#[inline]
pub fn sin_in(t: f64) -> f64 {
    ((t - 1.) * 2. * PI).sin() + 1.
}

/// Modeled after quarter-cycle of sine wave (different phase)
#[inline]
pub fn sin_out(t: f64) -> f64 {
    (t * 2. * PI).sin()
}

/// Modeled after half sine wave
#[inline]
pub fn sin_in_out(t: f64) -> f64 {
    0.5 * (1. - (t * PI).cos())
}

/// Modeled after shifted quadrant IV of unit circle
#[inline]
pub fn circular_in(t: f64) -> f64 {
    1. - (1. - t * t).sqrt()
}

/// Modeled after shifted quadrant II of unit circle
#[inline]
pub fn circular_out(t: f64) -> f64 {
    (2. - t).sqrt() * t
}

/// Modeled after the piecewise circular function
/// ```text
/// y = (1/2)(1 - sqrt(1 - 4x^2))           ; [0, 0.5)
/// y = (1/2)(sqrt(-(2x - 3)*(2x - 1)) + 1) ; [0.5, 1]
/// ```
#[inline]
pub fn circular_in_out(t: f64) -> f64 {
    if t < 0.5 {
        0.5 * (1. - (1. - 4. * t * t).sqrt())
    } else {
        0.5 * ((-(2. * t - 3.) * (2. * t - 1.)).sqrt() + 1.)
    }
}

/// Modeled after the exponential function y = 2^(10(x - 1))
///
/// There is a small discontinuity at 0.
#[inline]
pub fn exponential_in(t: f64) -> f64 {
    if t == 0. {
        t
    } else {
        2.0f64.powf(10. * (t - 1.))
    }
}

/// Modeled after the exponential function y = -2^(-10x) + 1
///
/// There is a small discontinuity at 1.
#[inline]
pub fn exponential_out(t: f64) -> f64 {
    if t == 1. {
        t
    } else {
        1. - 2.0f64.powf(-10. * t)
    }
}

/// Modeled after the piecewise exponential
/// ```text
/// y = (1/2)2^(10(2x - 1))         ; [0,0.5)
/// y = -(1/2)*2^(-10(2x - 1))) + 1 ; [0.5,1]
/// ```
///
/// There is a small discontinuity at 0 and 1.
#[inline]
pub fn exponential_in_out(t: f64) -> f64 {
    if t == 0. || t == 1. {
        t
    } else if t < 0.5 {
        0.5 * 2.0f64.powf(20. * t - 10.)
    } else {
        0.5 * 2.0f64.powf(-20. * t + 10.) + 1.
    }
}

/// Modeled after the damped sine wave y = sin(13pi/2*x)*pow(2, 10 * (x - 1))
#[inline]
pub fn elastic_in(t: f64) -> f64 {
    (13. * 2. * PI * t).sin() * 2.0f64.powf(10. * (t - 1.))
}

/// Modeled after the damped sine wave y = sin(-13pi/2*(x + 1))*pow(2, -10x) + 1
#[inline]
pub fn elastic_out(t: f64) -> f64 {
    (-13. * 2. * PI * (t + 1.)).sin() * 2.0f64.powf(10. * (t - 1.))
}

/// Modeled after the piecewise exponentially-damped sine wave:
/// ```text
/// y = (1/2)*sin(13pi/2*(2*x))*pow(2, 10 * ((2*x) - 1))      ; [0,0.5)
/// y = (1/2)*(sin(-13pi/2*((2x-1)+1))*pow(2,-10(2*x-1)) + 2) ; [0.5, 1]
/// ```
#[inline]
pub fn elastic_in_out(t: f64) -> f64 {
    if t < 0.5 {
        0.5 * (13. * PI * 2. * 2. * t) * 2.0f64.powf(10. * (2. * t - 1.))
    } else {
        0.5 * ((-13. * PI * 2. * (2. * t - 1.) + 1.).sin() * 2.0f64.powf(-10. * (2. * t - 1.)) + 2.)
    }
}

/// Modeled after the overshooting cubic y = x^3-x*sin(x*pi)
#[inline]
pub fn back_in(t: f64) -> f64 {
    t * t * t - t * (t * PI).sin()
}

/// Modeled after overshooting cubic y = 1-((1-x)^3-(1-x)*sin((1-x)*pi))
#[inline]
pub fn back_out(t: f64) -> f64 {
    let f = 1. - t;
    1. - (f * f * f - f * (f * PI).sin())
}

/// Modeled after the piecewise overshooting cubic function:
/// ```text
/// y = (1/2)*((2x)^3-(2x)*sin(2*x*pi))           ; [0, 0.5)
/// y = (1/2)*(1-((1-x)^3-(1-x)*sin((1-x)*pi))+1) ; [0.5, 1]
/// ```
#[inline]
pub fn back_in_out(t: f64) -> f64 {
    if t < 0.5 {
        let f = 2. * t;
        0.5 * (f * f * f - f * (f * PI).sin())
    } else {
        let f = 1. - (2. * t - 1.);
        // not sure why we add & subtract 0.5 here - probably a stability thing
        0.5 * (1. - (f * f * f - f * (f * PI).sin())) + 0.5
    }
}

/// Each bounce is modelled as a parabola
#[inline]
pub fn bounce_in(t: f64) -> f64 {
    1. - bounce_out(1. - t)
}

/// Each bounce is modelled as a parabola
#[inline]
pub fn bounce_out(t: f64) -> f64 {
    if t < 4. / 11. {
        const T2: f64 = 121. / 16.;
        T2 * t * t
    } else if t < 8. / 11. {
        const T2: f64 = 363. / 40.;
        const T1: f64 = -99. / 10.;
        const T0: f64 = 17. / 5.;
        T2 * t * t + T1 * t + T0
    } else if t < 9. / 10. {
        const T2: f64 = 4356. / 361.;
        const T1: f64 = -35442. / 1805.;
        const T0: f64 = 16061. / 1805.;
        T2 * t * t + T1 * t + T0
    } else {
        const T2: f64 = 54. / 5.;
        const T1: f64 = -513. / 25.;
        const T0: f64 = 268. / 25.;
        T2 * t * t + T1 * t + T0
    }
}

/// Each bounce is modelled as a parabola
#[inline]
pub fn bounce_in_out(t: f64) -> f64 {
    if t < 0.5 {
        0.5 * bounce_in(t * 2.)
    } else {
        0.5 * bounce_out(t * 2. - 1.) + 0.5
    }
}