1pub use lerpable_derive::Lerpable;
2
3pub fn step<T: Clone, LerpMethod>(this: &T, other: &T, pct: &LerpMethod) -> T
4where
5 LerpMethod: IsLerpingMethod,
6{
7 if pct.has_lerp_stepped() {
8 other.clone()
9 } else {
10 this.clone()
11 }
12}
13
14pub fn lerp<T, LerpMethod>(start: T, end: T, pct: &LerpMethod) -> T
15where
16 T: std::ops::Mul<f64, Output = T> + std::ops::Add<Output = T>,
17 f64: std::ops::Mul<T, Output = T>,
18 LerpMethod: IsLerpingMethod,
19{
20 let pct = pct.lerp_pct();
21 (1.0 - pct) * start + pct * end
22}
23
24pub fn lerp_vecs<T, LerpMethod>(this: &[T], other: &[T], pct: &LerpMethod) -> Vec<T>
25where
26 T: Clone + Lerpable,
27 LerpMethod: IsLerpingMethod,
28{
29 let mut v = vec![];
30 let this_len = this.len();
32 let other_len = other.len();
33 let count = if this_len == other_len {
36 this_len
37 } else {
38 lerp(this_len as f64, other_len as f64, pct).round() as usize
39 };
40 for i in 0..count {
41 let result = match (i >= this_len, i >= other_len) {
42 (true, true) => unreachable!(),
43 (true, false) => {
44 let emerge_pct = pct.partial_lerp_pct(i, count);
45 other[i].lerp_partial(emerge_pct)
46 }
47 (false, true) => {
48 let emerge_pct = pct.partial_lerp_pct(i, count);
49 this[i].lerp_partial(emerge_pct)
50 }
51 (false, false) => this[i].lerpify(&other[i], pct),
52 };
53 v.push(result);
54 }
55 v
56}
57
58pub trait IsLerpingMethod: Clone {
64 fn has_lerp_stepped(&self) -> bool;
65
66 fn partial_lerp_pct(&self, i: usize, total: usize) -> f64;
67
68 fn lerp_pct(&self) -> f64;
69
70 fn with_lerp_pct(&self, pct: f64) -> Self; }
72
73impl IsLerpingMethod for f64 {
74 fn has_lerp_stepped(&self) -> bool {
75 *self > 0.5
76 }
77
78 fn lerp_pct(&self) -> f64 {
79 *self
80 }
81
82 fn partial_lerp_pct(&self, i: usize, total: usize) -> f64 {
83 *self * total as f64 - i as f64
85 }
86
87 fn with_lerp_pct(&self, pct: f64) -> Self {
88 pct
89 }
90}
91
92impl IsLerpingMethod for f32 {
93 fn has_lerp_stepped(&self) -> bool {
94 *self > 0.5
95 }
96
97 fn lerp_pct(&self) -> f64 {
98 *self as f64
99 }
100
101 fn partial_lerp_pct(&self, i: usize, total: usize) -> f64 {
102 *self as f64 * total as f64 - i as f64
105 }
106
107 fn with_lerp_pct(&self, pct: f64) -> Self {
108 pct as f32
109 }
110}
111
112pub trait Lerpable: Sized + Clone {
113 fn lerpify<T: IsLerpingMethod>(&self, other: &Self, pct: &T) -> Self;
114
115 fn lerp_partial<T: IsLerpingMethod>(&self, _pct: T) -> Self {
118 self.clone()
119 }
120}
121
122macro_rules! impl_lerpable {
123 ($t:ty) => {
124 impl Lerpable for $t {
125 fn lerpify<T: IsLerpingMethod>(&self, other: &Self, pct: &T) -> Self {
126 lerp(*self as f64, *other as f64, pct) as $t
127 }
128 }
129 };
130}
131
132impl_lerpable!(usize);
133impl_lerpable!(u8);
134impl_lerpable!(u16);
135impl_lerpable!(u64);
136impl_lerpable!(i32);
137impl_lerpable!(i64);
138impl_lerpable!(f32);
139impl_lerpable!(f64);
140
141impl<T: Lerpable + Clone> Lerpable for Vec<T> {
142 fn lerpify<LerpMethod: IsLerpingMethod>(&self, other: &Self, method: &LerpMethod) -> Self {
143 if self.is_empty() || other.is_empty() {
144 return self.clone();
145 }
146 lerp_vecs(self, other, method)
147 }
148}
149
150impl Lerpable for bool {
151 fn lerpify<LerpMethod: IsLerpingMethod>(&self, other: &Self, method: &LerpMethod) -> Self {
152 step(self, other, method)
153 }
154}
155
156impl Lerpable for String {
157 fn lerpify<LerpMethod: IsLerpingMethod>(&self, other: &Self, method: &LerpMethod) -> Self {
158 step(self, other, method)
159 }
160}