rust_motion_animation/
easing.rs1pub type EasingFn = fn(f64) -> f64;
8
9#[inline]
13pub fn linear(t: f64) -> f64 {
14 t
15}
16
17#[inline]
19pub fn ease_in_quad(t: f64) -> f64 {
20 t * t
21}
22
23#[inline]
25pub fn ease_out_quad(t: f64) -> f64 {
26 t * (2.0 - t)
27}
28
29#[inline]
31pub fn ease_in_out_quad(t: f64) -> f64 {
32 if t < 0.5 {
33 2.0 * t * t
34 } else {
35 -1.0 + (4.0 - 2.0 * t) * t
36 }
37}
38
39#[inline]
41pub fn ease_in_cubic(t: f64) -> f64 {
42 t * t * t
43}
44
45#[inline]
47pub fn ease_out_cubic(t: f64) -> f64 {
48 let t1 = t - 1.0;
49 t1 * t1 * t1 + 1.0
50}
51
52#[inline]
54pub fn ease_in_out_cubic(t: f64) -> f64 {
55 if t < 0.5 {
56 4.0 * t * t * t
57 } else {
58 (t - 1.0) * (2.0 * t - 2.0) * (2.0 * t - 2.0) + 1.0
59 }
60}
61
62#[inline]
64pub fn ease_in_sine(t: f64) -> f64 {
65 1.0 - (t * std::f64::consts::FRAC_PI_2).cos()
66}
67
68#[inline]
70pub fn ease_out_sine(t: f64) -> f64 {
71 (t * std::f64::consts::FRAC_PI_2).sin()
72}
73
74#[inline]
76pub fn ease_in_out_sine(t: f64) -> f64 {
77 -(((std::f64::consts::PI * t).cos() - 1.0) / 2.0)
78}
79
80pub fn bezier(x1: f64, y1: f64, x2: f64, y2: f64) -> impl Fn(f64) -> f64 {
93 move |t: f64| {
94 if t <= 0.0 {
95 return 0.0;
96 }
97 if t >= 1.0 {
98 return 1.0;
99 }
100
101 let cx = 3.0 * x1;
103 let bx = 3.0 * (x2 - x1) - cx;
104 let ax = 1.0 - cx - bx;
105
106 let cy = 3.0 * y1;
107 let by = 3.0 * (y2 - y1) - cy;
108 let ay = 1.0 - cy - by;
109
110 let poly_x = |u: f64| ((ax * u + bx) * u + cx) * u;
111 let poly_x_deriv = |u: f64| (3.0 * ax * u + 2.0 * bx) * u + cx;
112 let poly_y = |u: f64| ((ay * u + by) * u + cy) * u;
113
114 let mut u = t;
116 for _ in 0..8 {
117 let x_err = poly_x(u) - t;
118 let d = poly_x_deriv(u);
119 if d.abs() < 1e-12 {
120 break;
121 }
122 u -= x_err / d;
123 }
124 poly_y(u)
125 }
126}
127
128pub fn ease() -> impl Fn(f64) -> f64 {
132 bezier(0.25, 0.1, 0.25, 1.0)
133}
134
135pub fn ease_in() -> impl Fn(f64) -> f64 {
137 bezier(0.42, 0.0, 1.0, 1.0)
138}
139
140pub fn ease_out() -> impl Fn(f64) -> f64 {
142 bezier(0.0, 0.0, 0.58, 1.0)
143}
144
145pub fn ease_in_out() -> impl Fn(f64) -> f64 {
147 bezier(0.42, 0.0, 0.58, 1.0)
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[test]
155 fn linear_identity() {
156 assert!((linear(0.5) - 0.5).abs() < 1e-10);
157 }
158
159 #[test]
160 fn bezier_endpoints() {
161 let f = bezier(0.25, 0.1, 0.25, 1.0);
162 assert!((f(0.0)).abs() < 1e-10);
163 assert!((f(1.0) - 1.0).abs() < 1e-10);
164 }
165
166 #[test]
167 fn bezier_midpoint_reasonable() {
168 let f = ease_out();
169 assert!(f(0.5) > 0.5);
171 }
172}