Skip to main content

motion_core/
math.rs

1//! Deterministic authoring mathematics and interpolation.
2
3pub use libm::{atan2, cos, exp, hypot, pow, sin, sqrt, tan};
4/// Single-precision variants for pixel-level texture generation.
5pub use libm::{cosf, expf, hypotf, sinf, sqrtf};
6
7/// Blends values with their established interpolation policy, including overshoot.
8pub fn lerp<Value: crate::Interpolate>(start: &Value, end: &Value, progress: f64) -> Value {
9    start.interpolate(end, progress)
10}
11
12/// Restricts progress to [0, 1], preserving NaN.
13pub fn clamp01(value: f64) -> f64 {
14    value.clamp(0.0, 1.0)
15}
16
17/// Cubic Hermite progress; equal edges produce a hard step, and NaN is preserved.
18pub fn smoothstep(edge0: f64, edge1: f64, value: f64) -> f64 {
19    if value.is_nan() {
20        return value;
21    }
22    if edge0 == edge1 {
23        return f64::from(value >= edge1);
24    }
25    let progress = clamp01((value - edge0) / (edge1 - edge0));
26    progress * progress * (3.0 - 2.0 * progress)
27}
28
29#[cfg(test)]
30mod tests {
31    use super::*;
32
33    #[test]
34    fn deterministic_helpers_preserve_interpolation_and_interval_boundaries() {
35        assert_eq!(sin(0.25).to_bits(), libm::sin(0.25).to_bits());
36        assert_eq!(lerp(&2.0, &6.0, 1.5), 8.0);
37        assert_eq!(smoothstep(2.0, 4.0, 3.0), 0.5);
38        assert_eq!(smoothstep(2.0, 4.0, 0.0), 0.0);
39        assert_eq!(smoothstep(2.0, 4.0, 5.0), 1.0);
40        assert_eq!(smoothstep(1.0, 1.0, 1.0), 1.0);
41        assert!(clamp01(f64::NAN).is_nan());
42    }
43}