Skip to main content

euv_engine/easing/
impl.rs

1use super::*;
2
3/// Implements easing curve evaluation for `Easing`.
4impl Easing {
5    /// Evaluates the easing curve at the given normalized time.
6    ///
7    /// The input is clamped into the range 0.0 to 1.0 before evaluation,
8    /// so callers may pass slightly out-of-range accumulators without
9    /// producing out-of-range output.
10    ///
11    /// # Arguments
12    ///
13    /// - `f64` - The normalized time, typically in the range 0.0 to 1.0.
14    ///
15    /// # Returns
16    ///
17    /// - `f64` - The eased value, where 0.0 maps to the start and 1.0 to the end.
18    pub fn evaluate(&self, t: f64) -> f64 {
19        let t: f64 = Numeric::clamp(t, 0.0, 1.0);
20        match self {
21            Easing::Linear => t,
22            Easing::InQuad => t * t,
23            Easing::OutQuad => 1.0 - (1.0 - t) * (1.0 - t),
24            Easing::InOutQuad => {
25                if t < 0.5 {
26                    2.0 * t * t
27                } else {
28                    1.0 - (-2.0 * t + 2.0).powi(2) / 2.0
29                }
30            }
31            Easing::InCubic => t * t * t,
32            Easing::OutCubic => 1.0 - (1.0 - t).powi(3),
33            Easing::InOutCubic => {
34                if t < 0.5 {
35                    4.0 * t * t * t
36                } else {
37                    1.0 - (-2.0 * t + 2.0).powi(3) / 2.0
38                }
39            }
40            Easing::InQuart => t * t * t * t,
41            Easing::OutQuart => 1.0 - (1.0 - t).powi(4),
42            Easing::InOutQuart => {
43                if t < 0.5 {
44                    8.0 * t * t * t * t
45                } else {
46                    1.0 - (-2.0 * t + 2.0).powi(4) / 2.0
47                }
48            }
49            Easing::InQuint => t * t * t * t * t,
50            Easing::OutQuint => 1.0 - (1.0 - t).powi(5),
51            Easing::InOutQuint => {
52                if t < 0.5 {
53                    16.0 * t * t * t * t * t
54                } else {
55                    1.0 - (-2.0 * t + 2.0).powi(5) / 2.0
56                }
57            }
58            Easing::InSine => 1.0 - (t * HALF_PI).cos(),
59            Easing::OutSine => (t * HALF_PI).sin(),
60            Easing::InOutSine => -((t * PI).cos() - 1.0) / 2.0,
61            Easing::InExpo => {
62                if t == 0.0 {
63                    0.0
64                } else {
65                    2.0_f64.powf(10.0 * t - 10.0)
66                }
67            }
68            Easing::OutExpo => {
69                if t == 1.0 {
70                    1.0
71                } else {
72                    1.0 - 2.0_f64.powf(-10.0 * t)
73                }
74            }
75            Easing::InOutExpo => {
76                if t == 0.0 {
77                    0.0
78                } else if t == 1.0 {
79                    1.0
80                } else if t < 0.5 {
81                    2.0_f64.powf(20.0 * t - 10.0) / 2.0
82                } else {
83                    (2.0 - 2.0_f64.powf(-20.0 * t + 10.0)) / 2.0
84                }
85            }
86            Easing::InCirc => 1.0 - (1.0 - t * t).sqrt(),
87            Easing::OutCirc => (1.0 - (t - 1.0).powi(2)).sqrt(),
88            Easing::InOutCirc => {
89                if t < 0.5 {
90                    (1.0 - (1.0 - (2.0 * t).powi(2)).sqrt()) / 2.0
91                } else {
92                    ((1.0 - (-2.0 * t + 2.0).powi(2)).sqrt() + 1.0) / 2.0
93                }
94            }
95            Easing::InBack => {
96                let c: f64 = BACK_OVERSHOOT;
97                (c + 1.0) * t * t * t - c * t * t
98            }
99            Easing::OutBack => {
100                let c: f64 = BACK_OVERSHOOT;
101                1.0 + (c + 1.0) * (t - 1.0).powi(3) + c * (t - 1.0).powi(2)
102            }
103            Easing::InOutBack => {
104                let c: f64 = BACK_OVERSHOOT_INOUT;
105                if t < 0.5 {
106                    ((2.0 * t).powi(2) * ((c + 1.0) * 2.0 * t - c)) / 2.0
107                } else {
108                    ((2.0 * t - 2.0).powi(2) * ((c + 1.0) * (2.0 * t - 2.0) + c) + 2.0) / 2.0
109                }
110            }
111            Easing::InElastic => {
112                if t == 0.0 {
113                    0.0
114                } else if t == 1.0 {
115                    1.0
116                } else {
117                    -2.0_f64.powf(10.0 * t - 10.0) * ((t * 10.0 - 10.75) * ELASTIC_PERIOD).sin()
118                }
119            }
120            Easing::OutElastic => {
121                if t == 0.0 {
122                    0.0
123                } else if t == 1.0 {
124                    1.0
125                } else {
126                    2.0_f64.powf(-10.0 * t) * ((t * 10.0 - 0.75) * ELASTIC_PERIOD).sin() + 1.0
127                }
128            }
129            Easing::InOutElastic => {
130                if t == 0.0 {
131                    0.0
132                } else if t == 1.0 {
133                    1.0
134                } else if t < 0.5 {
135                    -(2.0_f64.powf(20.0 * t - 10.0)
136                        * ((20.0 * t - 11.125) * ELASTIC_PERIOD_INOUT).sin())
137                        / 2.0
138                } else {
139                    (2.0_f64.powf(-20.0 * t + 10.0)
140                        * ((20.0 * t - 11.125) * ELASTIC_PERIOD_INOUT).sin())
141                        / 2.0
142                        + 1.0
143                }
144            }
145            Easing::InBounce => 1.0 - Easing::OutBounce.evaluate(1.0 - t),
146            Easing::OutBounce => Easing::bounce_out(t),
147            Easing::InOutBounce => {
148                if t < 0.5 {
149                    (1.0 - Easing::bounce_out(1.0 - 2.0 * t)) / 2.0
150                } else {
151                    (1.0 + Easing::bounce_out(2.0 * t - 1.0)) / 2.0
152                }
153            }
154        }
155    }
156
157    /// Applies this easing curve to interpolate between two scalar values.
158    ///
159    /// # Arguments
160    ///
161    /// - `f64` - The start value.
162    /// - `f64` - The end value.
163    /// - `f64` - The normalized time, typically in the range 0.0 to 1.0.
164    ///
165    /// # Returns
166    ///
167    /// - `f64` - The eased interpolation between `start` and `end`.
168    pub fn interpolate(&self, start: f64, end: f64, t: f64) -> f64 {
169        Numeric::lerp(start, end, self.evaluate(t))
170    }
171
172    /// The shared `OutBounce` curve, factored out so `InBounce` and
173    /// `InOutBounce` can mirror it without duplicating the subdivision table.
174    ///
175    /// # Arguments
176    ///
177    /// - `f64` - The normalized time in the range 0.0 to 1.0.
178    ///
179    /// # Returns
180    ///
181    /// - `f64` - The bounced value.
182    fn bounce_out(t: f64) -> f64 {
183        let n: f64 = BOUNCE_DIVISIONS;
184        if t < 1.0 / n {
185            7.5625 * t * t
186        } else if t < 2.0 / n {
187            let t: f64 = t - 1.5 / n;
188            7.5625 * t * t + 0.75
189        } else if t < 2.5 / n {
190            let t: f64 = t - 2.25 / n;
191            7.5625 * t * t + 0.9375
192        } else {
193            let t: f64 = t - 2.625 / n;
194            7.5625 * t * t + 0.984375
195        }
196    }
197}