1pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
15 a + (b - a) * t
16}
17
18pub fn inverse_lerp(a: f32, b: f32, v: f32) -> f32 {
20 if (b - a).abs() < f32::EPSILON {
21 0.0
22 } else {
23 (v - a) / (b - a)
24 }
25}
26
27pub fn remap(v: f32, in_a: f32, in_b: f32, out_a: f32, out_b: f32) -> f32 {
29 lerp(out_a, out_b, inverse_lerp(in_a, in_b, v))
30}
31
32pub fn remap_clamped(v: f32, in_a: f32, in_b: f32, out_a: f32, out_b: f32) -> f32 {
34 lerp(out_a, out_b, inverse_lerp(in_a, in_b, v).clamp(0.0, 1.0))
35}
36
37pub fn smoothstep(e0: f32, e1: f32, v: f32) -> f32 {
39 let t = inverse_lerp(e0, e1, v).clamp(0.0, 1.0);
40 t * t * (3.0 - 2.0 * t)
41}
42
43pub fn approach(current: f32, target: f32, max_delta: f32) -> f32 {
45 let d = target - current;
46 if d.abs() <= max_delta {
47 target
48 } else {
49 current + d.signum() * max_delta
50 }
51}
52
53pub fn damp(current: f32, target: f32, lambda: f32, dt: f32) -> f32 {
56 lerp(current, target, 1.0 - (-lambda.max(0.0) * dt.max(0.0)).exp())
57}
58
59pub fn wrap_degrees(angle: f32) -> f32 {
61 let a = angle.rem_euclid(360.0);
62 if a > 180.0 {
63 a - 360.0
64 } else {
65 a
66 }
67}
68
69pub fn delta_degrees(from: f32, to: f32) -> f32 {
71 wrap_degrees(to - from)
72}
73
74pub fn approach_degrees(current: f32, target: f32, max_delta: f32) -> f32 {
76 current + approach(0.0, delta_degrees(current, target), max_delta)
77}
78
79pub fn smooth_damp(current: f32, target: f32, velocity: f32, smooth_time: f32, dt: f32) -> (f32, f32) {
89 let omega = 2.0 / smooth_time.max(1e-4);
90 let x = omega * dt;
91 let exp = 1.0 / (1.0 + x + 0.48 * x * x + 0.235 * x * x * x);
92 let change = current - target;
93 let temp = (velocity + omega * change) * dt;
94 let new_velocity = (velocity - omega * temp) * exp;
95 let out = target + (change + temp) * exp;
96 if (target - current > 0.0) == (out > target) {
98 return (target, 0.0);
99 }
100 (out, new_velocity)
101}
102
103pub fn ease_smooth(t: f32) -> f32 {
105 let t = t.clamp(0.0, 1.0);
106 t * t * (3.0 - 2.0 * t)
107}
108
109pub fn ease_smoother(t: f32) -> f32 {
111 let t = t.clamp(0.0, 1.0);
112 t * t * t * (t * (t * 6.0 - 15.0) + 10.0)
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn helpers_behave() {
121 assert_eq!(inverse_lerp(2.0, 2.0, 5.0), 0.0);
122 assert_eq!(remap_clamped(20.0, 0.0, 10.0, 0.0, 1.0), 1.0);
123 assert_eq!(smoothstep(0.0, 1.0, 0.5), 0.5);
124 assert_eq!(approach(9.0, 10.0, 3.0), 10.0);
125 assert!(damp(0.0, 1.0, 10.0, 0.016) > 0.1);
126 assert!((approach_degrees(350.0, 10.0, 5.0) - 355.0).abs() < 1e-4);
127 }
128}