eazy_data/interpolation/trigonometric/sinusoidal.rs
1//! Trigonometric Smoothstep (C∞ continuity).
2//!
3//! From Inigo Quilez: https://iquilezles.org/articles/smoothsteps/
4//!
5//! Uses trigonometric functions for infinitely smooth transitions.
6//! Has C∞ continuity (all derivatives are zero at endpoints).
7//!
8//! - **Sinusoidal**: `0.5 - 0.5*cos(π*x)` - the smoothstep function
9//! - **InvSinusoidal**: `acos(1-2*x)/π` - the inverse function
10
11use crate::easing::Curve;
12
13use libm::{acosf, cosf};
14
15/// Trigonometric Smoothstep: `0.5 - 0.5*cos(π*x)`
16///
17/// C∞ continuous smoothstep using cosine.
18/// All derivatives are zero at x=0 and x=1.
19///
20/// #### examples.
21///
22/// ```
23/// use eazy::Curve;
24/// use eazy::interpolation::trigonometric::sinusoidal::Sinusoidal;
25///
26/// let p = Sinusoidal.y(0.5);
27/// assert!((p - 0.5).abs() < 0.0001);
28/// ```
29#[derive(Debug)]
30pub struct Sinusoidal;
31
32impl Curve for Sinusoidal {
33 #[inline(always)]
34 fn y(&self, p: f32) -> f32 {
35 0.5 - 0.5 * cosf(core::f32::consts::PI * p)
36 }
37}
38
39#[test]
40fn test_sinusoidal() {
41 // Sinusoidal(0) = 0
42 assert_eq!(Sinusoidal.y(0.0), 0.0);
43 // Sinusoidal(1) = 1
44 assert!((Sinusoidal.y(1.0) - 1.0).abs() < 0.0001);
45 // Sinusoidal(0.5) = 0.5 (symmetric)
46 assert!((Sinusoidal.y(0.5) - 0.5).abs() < 0.0001);
47}
48
49/// Inverse Trigonometric Smoothstep: `acos(1-2*x)/π`
50///
51/// Maps output values back to input values.
52/// `InvSinusoidal(Sinusoidal(x)) = x`
53///
54/// #### examples.
55///
56/// ```
57/// use eazy::Curve;
58/// use eazy::interpolation::trigonometric::sinusoidal::{Sinusoidal, InvSinusoidal};
59///
60/// let x = 0.3;
61/// let y = Sinusoidal.y(x);
62/// let x_back = InvSinusoidal.y(y);
63/// assert!((x - x_back).abs() < 0.0001);
64/// ```
65#[derive(Debug)]
66pub struct InvSinusoidal;
67
68impl Curve for InvSinusoidal {
69 #[inline(always)]
70 fn y(&self, p: f32) -> f32 {
71 acosf(1.0 - 2.0 * p) / core::f32::consts::PI
72 }
73}
74
75#[test]
76fn test_inv_sinusoidal() {
77 // InvSinusoidal(0) = 0
78 assert_eq!(InvSinusoidal.y(0.0), 0.0);
79 // InvSinusoidal(1) = 1
80 assert!((InvSinusoidal.y(1.0) - 1.0).abs() < 0.0001);
81 // Round-trip: InvSinusoidal(Sinusoidal(0.3)) ≈ 0.3
82 let x = 0.3;
83 let round_trip = InvSinusoidal.y(Sinusoidal.y(x));
84 assert!((x - round_trip).abs() < 0.0001);
85}