1#[cfg(feature = "glam")]
2use glam::{vec2, vec3, Vec2, Vec3};
3
4use core::option::Option;
5
6pub use lerpable_derive::Lerpable;
7
8pub fn step<T: Clone, LerpMethod>(this: &T, other: &T, pct: &LerpMethod) -> T
9where
10 LerpMethod: IsLerpingMethod,
11{
12 if pct.has_lerp_stepped() {
13 other.clone()
14 } else {
15 this.clone()
16 }
17}
18
19pub fn lerp<T, LerpMethod>(start: T, end: T, pct: &LerpMethod) -> T
20where
21 T: std::ops::Mul<f64, Output = T> + std::ops::Add<Output = T>,
22 f64: std::ops::Mul<T, Output = T>,
23 LerpMethod: IsLerpingMethod,
24{
25 let pct = pct.lerp_pct();
26 (1.0 - pct) * start + pct * end
27}
28
29pub fn lerp_vecs<T, LerpMethod>(this: &[T], other: &[T], pct: &LerpMethod) -> Vec<T>
30where
31 T: Clone + Lerpable,
32 LerpMethod: IsLerpingMethod,
33{
34 let mut v = vec![];
35 let this_len = this.len();
37 let other_len = other.len();
38 let count = if this_len == other_len {
41 this_len
42 } else {
43 lerp(this_len as f64, other_len as f64, pct).round() as usize
44 };
45 for i in 0..count {
46 let result = match (i >= this_len, i >= other_len) {
47 (true, true) => unreachable!(),
48 (true, false) => {
49 let emerge_pct = pct.partial_lerp_pct(i, count);
50 other[i].lerp_partial(emerge_pct)
51 }
52 (false, true) => {
53 let emerge_pct = pct.partial_lerp_pct(i, count);
54 this[i].lerp_partial(emerge_pct)
55 }
56 (false, false) => this[i].lerpify(&other[i], pct),
57 };
58 v.push(result);
59 }
60 v
61}
62
63pub trait IsLerpingMethod: Clone {
69 fn has_lerp_stepped(&self) -> bool;
70
71 fn partial_lerp_pct(&self, i: usize, total: usize) -> f64;
72
73 fn lerp_pct(&self) -> f64;
74
75 fn with_lerp_pct(&self, pct: f64) -> Self; }
77
78impl IsLerpingMethod for f64 {
79 fn has_lerp_stepped(&self) -> bool {
80 *self > 0.5
81 }
82
83 fn lerp_pct(&self) -> f64 {
84 *self
85 }
86
87 fn partial_lerp_pct(&self, i: usize, total: usize) -> f64 {
88 *self * total as f64 - i as f64
90 }
91
92 fn with_lerp_pct(&self, pct: f64) -> Self {
93 pct
94 }
95}
96
97impl IsLerpingMethod for f32 {
98 fn has_lerp_stepped(&self) -> bool {
99 *self > 0.5
100 }
101
102 fn lerp_pct(&self) -> f64 {
103 *self as f64
104 }
105
106 fn partial_lerp_pct(&self, i: usize, total: usize) -> f64 {
107 *self as f64 * total as f64 - i as f64
110 }
111
112 fn with_lerp_pct(&self, pct: f64) -> Self {
113 pct as f32
114 }
115}
116
117pub trait Lerpable: Sized + Clone {
118 fn lerpify<T: IsLerpingMethod>(&self, other: &Self, pct: &T) -> Self;
119
120 fn lerp_partial<T: IsLerpingMethod>(&self, _pct: T) -> Self {
123 self.clone()
124 }
125}
126
127macro_rules! impl_lerpable {
128 ($t:ty) => {
129 impl Lerpable for $t {
130 fn lerpify<T: IsLerpingMethod>(&self, other: &Self, pct: &T) -> Self {
131 lerp(*self as f64, *other as f64, pct) as $t
132 }
133 }
134 };
135}
136
137impl_lerpable!(usize);
138impl_lerpable!(u8);
139impl_lerpable!(u16);
140impl_lerpable!(u64);
141impl_lerpable!(i32);
142impl_lerpable!(i64);
143impl_lerpable!(f32);
144impl_lerpable!(f64);
145
146impl<T: Lerpable + Clone> Lerpable for Vec<T> {
147 fn lerpify<LerpMethod: IsLerpingMethod>(&self, other: &Self, method: &LerpMethod) -> Self {
148 if self.is_empty() || other.is_empty() {
149 return self.clone();
150 }
151 lerp_vecs(self, other, method)
152 }
153}
154
155impl<T: Lerpable + Clone> Lerpable for Option<T> {
156 fn lerpify<LerpMethod: IsLerpingMethod>(&self, other: &Self, method: &LerpMethod) -> Self {
157 match (self, other) {
158 (Option::None, Option::Some(_)) => {
159 if method.lerp_pct() < 0.5 {
160 None
161 } else {
162 self.clone()
163 }
164 }
165 (Option::Some(_), Option::None) => {
166 if method.lerp_pct() < 0.5 {
167 self.clone()
168 } else {
169 None
170 }
171 }
172 (Option::Some(a), Option::Some(b)) => Some(a.lerpify(b, method)),
173 (Option::None, Option::None) => None,
174 }
175 }
176}
177
178impl Lerpable for bool {
179 fn lerpify<LerpMethod: IsLerpingMethod>(&self, other: &Self, method: &LerpMethod) -> Self {
180 step(self, other, method)
181 }
182}
183
184impl Lerpable for String {
185 fn lerpify<LerpMethod: IsLerpingMethod>(&self, other: &Self, method: &LerpMethod) -> Self {
186 step(self, other, method)
187 }
188}
189
190#[cfg(feature = "glam")]
191impl Lerpable for Vec2 {
192 fn lerpify<LerpMethod: IsLerpingMethod>(&self, other: &Self, method: &LerpMethod) -> Self {
193 vec2(
194 self.x.lerpify(&other.x, method),
195 self.y.lerpify(&other.y, method),
196 )
197 .into()
198 }
199}
200
201#[cfg(feature = "glam")]
202impl Lerpable for Vec3 {
203 fn lerpify<T: IsLerpingMethod>(&self, other: &Self, method: &T) -> Self {
204 vec3(
205 self.x.lerpify(&other.x, method),
206 self.y.lerpify(&other.y, method),
207 self.z.lerpify(&other.z, method),
208 )
209 .into()
210 }
211}