eazy_keyframe/
keyframe.rs1use eazy_core::{Curve, Easing};
7use eazy_tweener::Tweenable;
8
9#[derive(Debug, Clone)]
26pub struct Keyframe<T: Tweenable> {
27 time: f32,
29 value: T,
31 easing: Option<Easing>,
34}
35
36impl<T: Tweenable> Keyframe<T> {
37 pub fn new(time: f32, value: T) -> Self {
39 Self {
40 time,
41 value,
42 easing: None,
43 }
44 }
45
46 pub fn with_easing(mut self, easing: Easing) -> Self {
48 self.easing = Some(easing);
49 self
50 }
51
52 pub fn set_easing(&mut self, easing: Option<Easing>) {
54 self.easing = easing;
55 }
56
57 pub fn time(&self) -> f32 {
59 self.time
60 }
61
62 pub fn value(&self) -> T {
64 self.value
65 }
66
67 pub fn easing(&self) -> Option<&Easing> {
69 self.easing.as_ref()
70 }
71
72 pub fn has_easing(&self) -> bool {
74 self.easing.is_some()
75 }
76
77 #[inline]
101 pub fn tween_to(&self, next: &Keyframe<T>, time: f32) -> T {
102 if time <= self.time {
104 return self.value;
105 }
106
107 if time >= next.time {
109 return next.value;
110 }
111
112 if next.time <= self.time {
114 return next.value;
115 }
116
117 let t = (time - self.time) / (next.time - self.time);
119
120 let eased_t = match &next.easing {
122 Some(easing) => easing.y(t),
123 None => t, };
125
126 self.value.lerp(next.value, eased_t)
127 }
128}
129
130impl<T: Tweenable> PartialEq for Keyframe<T> {
131 fn eq(&self, other: &Self) -> bool {
132 self.time == other.time
133 }
134}
135
136impl<T: Tweenable> From<(f32, T)> for Keyframe<T> {
140 #[inline]
141 fn from((time, value): (f32, T)) -> Self {
142 Keyframe::new(time, value)
143 }
144}
145
146impl<T: Tweenable> From<(f32, T, Easing)> for Keyframe<T> {
148 #[inline]
149 fn from((time, value, easing): (f32, T, Easing)) -> Self {
150 Keyframe::new(time, value).with_easing(easing)
151 }
152}
153
154impl<T: Tweenable> PartialOrd for Keyframe<T> {
155 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
156 self.time.partial_cmp(&other.time)
157 }
158}
159
160pub fn keyframe<T: Tweenable>(time: f32, value: T) -> Keyframe<T> {
162 Keyframe::new(time, value)
163}
164
165pub fn keyframe_eased<T: Tweenable>(
167 time: f32,
168 value: T,
169 easing: Easing,
170) -> Keyframe<T> {
171 Keyframe::new(time, value).with_easing(easing)
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177
178 #[test]
179 fn test_keyframe_creation() {
180 let kf = Keyframe::new(0.5, 100.0_f32);
181
182 assert_eq!(kf.time(), 0.5);
183 assert_eq!(kf.value(), 100.0);
184 assert!(kf.easing().is_none());
185 }
186
187 #[test]
188 fn test_keyframe_with_easing() {
189 let kf = Keyframe::new(0.5, 100.0_f32).with_easing(Easing::OutBounce);
190
191 assert!(kf.has_easing());
192 }
193
194 #[test]
195 fn test_keyframe_ordering() {
196 let kf1 = Keyframe::new(0.2, 50.0_f32);
197 let kf2 = Keyframe::new(0.5, 100.0_f32);
198 let kf3 = Keyframe::new(0.8, 150.0_f32);
199
200 assert!(kf1 < kf2);
201 assert!(kf2 < kf3);
202 assert!(kf1 < kf3);
203 }
204
205 #[test]
206 fn test_keyframe_array() {
207 let kf = Keyframe::new(0.5, [1.0_f32, 2.0, 3.0]);
208
209 assert_eq!(kf.value(), [1.0, 2.0, 3.0]);
210 }
211
212 #[test]
213 fn test_tween_to_linear() {
214 let kf1 = Keyframe::new(0.0, 0.0_f32);
215 let kf2 = Keyframe::new(1.0, 100.0_f32);
216
217 assert_eq!(kf1.tween_to(&kf2, 0.0), 0.0);
219 assert_eq!(kf1.tween_to(&kf2, 0.5), 50.0);
220 assert_eq!(kf1.tween_to(&kf2, 1.0), 100.0);
221 }
222
223 #[test]
224 fn test_tween_to_clamping() {
225 let kf1 = Keyframe::new(0.2, 10.0_f32);
226 let kf2 = Keyframe::new(0.8, 80.0_f32);
227
228 assert_eq!(kf1.tween_to(&kf2, 0.0), 10.0);
230 assert_eq!(kf1.tween_to(&kf2, 0.1), 10.0);
231
232 assert_eq!(kf1.tween_to(&kf2, 0.9), 80.0);
234 assert_eq!(kf1.tween_to(&kf2, 1.0), 80.0);
235 }
236
237 #[test]
238 fn test_tween_to_with_easing() {
239 let kf1 = Keyframe::new(0.0, 0.0_f32);
240 let kf2 = Keyframe::new(1.0, 100.0_f32).with_easing(Easing::InQuadratic);
241
242 let value = kf1.tween_to(&kf2, 0.5);
244
245 assert_eq!(value, 25.0);
246 }
247
248 #[test]
249 fn test_tween_to_array() {
250 let kf1 = Keyframe::new(0.0, [0.0_f32, 0.0, 0.0]);
251 let kf2 = Keyframe::new(1.0, [100.0, 200.0, 300.0]);
252
253 let value = kf1.tween_to(&kf2, 0.5);
254
255 assert_eq!(value, [50.0, 100.0, 150.0]);
256 }
257}