1use crate::color::Color;
2use crate::scene::Transform;
3use glam::{Quat, Vec2, Vec3, Vec4};
4
5pub trait Lerp: Clone + Send + Sync + 'static {
8 fn lerp_by(&self, other: &Self, t: f32) -> Self;
9}
10
11impl Lerp for f32 {
12 fn lerp_by(&self, o: &Self, t: f32) -> Self {
13 self + (o - self) * t
14 }
15}
16impl Lerp for f64 {
17 fn lerp_by(&self, o: &Self, t: f32) -> Self {
18 self + (o - self) * t as f64
19 }
20}
21impl Lerp for Vec2 {
22 fn lerp_by(&self, o: &Self, t: f32) -> Self {
23 self.lerp(*o, t)
24 }
25}
26impl Lerp for Vec3 {
27 fn lerp_by(&self, o: &Self, t: f32) -> Self {
28 self.lerp(*o, t)
29 }
30}
31impl Lerp for Vec4 {
32 fn lerp_by(&self, o: &Self, t: f32) -> Self {
33 self.lerp(*o, t)
34 }
35}
36impl Lerp for Quat {
37 fn lerp_by(&self, o: &Self, t: f32) -> Self {
38 self.slerp(*o, t)
39 }
40}
41impl Lerp for Color {
42 fn lerp_by(&self, o: &Self, t: f32) -> Self {
43 self.lerp(*o, t)
44 }
45}
46impl Lerp for Transform {
47 fn lerp_by(&self, o: &Self, t: f32) -> Self {
48 self.lerp(o, t)
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
55pub enum EaseFunction {
56 Linear,
57 Step,
58 QuadIn,
59 QuadOut,
60 QuadInOut,
61 CubicIn,
62 CubicOut,
63 CubicInOut,
64 SineIn,
65 SineOut,
66 SineInOut,
67 ExpoIn,
68 ExpoOut,
69 ExpoInOut,
70 ElasticIn,
71 ElasticOut,
72 BackIn,
73 BackOut,
74 BackInOut,
75 BounceOut,
76 BounceIn,
77}
78
79impl EaseFunction {
80 pub fn apply(self, t: f32) -> f32 {
81 let t = t.clamp(0.0, 1.0);
82 match self {
83 EaseFunction::Linear => t,
84 EaseFunction::Step => {
85 if t < 1.0 {
86 0.0
87 } else {
88 1.0
89 }
90 },
91 EaseFunction::QuadIn => t * t,
92 EaseFunction::QuadOut => t * (2.0 - t),
93 EaseFunction::QuadInOut => {
94 if t < 0.5 {
95 2.0 * t * t
96 } else {
97 -1.0 + (4.0 - 2.0 * t) * t
98 }
99 },
100 EaseFunction::CubicIn => t * t * t,
101 EaseFunction::CubicOut => {
102 let s = t - 1.0;
103 s * s * s + 1.0
104 },
105 EaseFunction::CubicInOut => {
106 if t < 0.5 {
107 4.0 * t * t * t
108 } else {
109 (t - 1.0) * (2.0 * t - 2.0) * (2.0 * t - 2.0) + 1.0
110 }
111 },
112 EaseFunction::SineIn => 1.0 - ((t * std::f32::consts::FRAC_PI_2).cos()),
113 EaseFunction::SineOut => (t * std::f32::consts::FRAC_PI_2).sin(),
114 EaseFunction::SineInOut => 0.5 * (1.0 - (std::f32::consts::PI * t).cos()),
115 EaseFunction::ExpoIn => {
116 if t == 0.0 {
117 0.0
118 } else {
119 (2.0_f32).powf(10.0 * t - 10.0)
120 }
121 },
122 EaseFunction::ExpoOut => {
123 if t == 1.0 {
124 1.0
125 } else {
126 1.0 - (2.0_f32).powf(-10.0 * t)
127 }
128 },
129 EaseFunction::ExpoInOut => {
130 if t == 0.0 {
131 return 0.0;
132 }
133 if t == 1.0 {
134 return 1.0;
135 }
136 if t < 0.5 {
137 (2.0_f32).powf(20.0 * t - 10.0) / 2.0
138 } else {
139 (2.0 - (2.0_f32).powf(-20.0 * t + 10.0)) / 2.0
140 }
141 },
142 EaseFunction::ElasticIn => {
143 let c = 2.0 * std::f32::consts::PI / 3.0;
144 if t == 0.0 {
145 0.0
146 } else if t == 1.0 {
147 1.0
148 } else {
149 -(2.0_f32).powf(10.0 * t - 10.0) * ((10.0 * t - 10.75) * c).sin()
150 }
151 },
152 EaseFunction::ElasticOut => {
153 let c = 2.0 * std::f32::consts::PI / 3.0;
154 if t == 0.0 {
155 0.0
156 } else if t == 1.0 {
157 1.0
158 } else {
159 (2.0_f32).powf(-10.0 * t) * ((10.0 * t - 0.75) * c).sin() + 1.0
160 }
161 },
162 EaseFunction::BackIn => {
163 let c1 = 1.70158;
164 let c3 = c1 + 1.0;
165 c3 * t * t * t - c1 * t * t
166 },
167 EaseFunction::BackOut => {
168 let c1 = 1.70158;
169 let c3 = c1 + 1.0;
170 1.0 + c3 * (t - 1.0).powi(3) + c1 * (t - 1.0).powi(2)
171 },
172 EaseFunction::BackInOut => {
173 let c2 = 1.70158 * 1.525;
174 if t < 0.5 {
175 ((2.0 * t).powi(2) * ((c2 + 1.0) * 2.0 * t - c2)) / 2.0
176 } else {
177 ((2.0 * t - 2.0).powi(2) * ((c2 + 1.0) * (2.0 * t - 2.0) + c2) + 2.0) / 2.0
178 }
179 },
180 EaseFunction::BounceOut => bounce_out(t),
181 EaseFunction::BounceIn => 1.0 - bounce_out(1.0 - t),
182 }
183 }
184}
185
186fn bounce_out(t: f32) -> f32 {
187 let n1 = 7.5625;
188 let d1 = 2.75;
189 if t < 1.0 / d1 {
190 n1 * t * t
191 } else if t < 2.0 / d1 {
192 let t = t - 1.5 / d1;
193 n1 * t * t + 0.75
194 } else if t < 2.5 / d1 {
195 let t = t - 2.25 / d1;
196 n1 * t * t + 0.9375
197 } else {
198 let t = t - 2.625 / d1;
199 n1 * t * t + 0.984375
200 }
201}
202
203#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
206pub struct Keyframe<T: Lerp> {
207 pub time: f32,
208 pub value: T,
209 pub ease: EaseFunction,
210}
211
212pub struct Track<T: Lerp> {
213 pub keyframes: Vec<Keyframe<T>>,
214}
215
216impl<T: Lerp> Track<T> {
217 pub fn new() -> Self {
218 Self { keyframes: Vec::new() }
219 }
220
221 pub fn add(mut self, time: f32, value: T, ease: EaseFunction) -> Self {
222 self.keyframes.push(Keyframe { time, value, ease });
223 self.keyframes
224 .sort_by(|a, b| a.time.partial_cmp(&b.time).unwrap());
225 self
226 }
227
228 pub fn sample(&self, t: f32) -> Option<T> {
229 if self.keyframes.is_empty() {
230 return None;
231 }
232 if t <= self.keyframes[0].time {
233 return Some(self.keyframes[0].value.clone());
234 }
235 let last = self.keyframes.last().unwrap();
236 if t >= last.time {
237 return Some(last.value.clone());
238 }
239 for i in 0..self.keyframes.len() - 1 {
240 let kf0 = &self.keyframes[i];
241 let kf1 = &self.keyframes[i + 1];
242 if t >= kf0.time && t <= kf1.time {
243 let local = (t - kf0.time) / (kf1.time - kf0.time);
244 let eased = kf0.ease.apply(local);
245 return Some(kf0.value.lerp_by(&kf1.value, eased));
246 }
247 }
248 None
249 }
250
251 pub fn duration(&self) -> f32 {
252 self.keyframes.last().map(|k| k.time).unwrap_or(0.0)
253 }
254}
255
256impl<T: Lerp> Default for Track<T> {
257 fn default() -> Self {
258 Self::new()
259 }
260}
261
262#[derive(Debug, Default)]
266pub struct Timeline {
267 pub time: f32,
268 pub playing: bool,
269 pub looping: bool,
270 pub speed: f32,
271 duration: f32,
272}
273
274impl Timeline {
275 pub fn new(duration: f32) -> Self {
276 Self {
277 time: 0.0,
278 playing: false,
279 looping: false,
280 speed: 1.0,
281 duration,
282 }
283 }
284
285 pub fn play(&mut self) {
286 self.playing = true;
287 }
288
289 pub fn pause(&mut self) {
290 self.playing = false;
291 }
292
293 pub fn stop(&mut self) {
294 self.playing = false;
295 self.time = 0.0;
296 }
297
298 pub fn tick(&mut self, dt: f32) {
299 if !self.playing {
300 return;
301 }
302 self.time += dt * self.speed;
303 if self.time >= self.duration {
304 if self.looping {
305 self.time %= self.duration;
306 } else {
307 self.time = self.duration;
308 self.playing = false;
309 }
310 }
311 }
312
313 pub fn normalized_time(&self) -> f32 {
314 if self.duration <= 0.0 {
315 return 0.0;
316 }
317 (self.time / self.duration).clamp(0.0, 1.0)
318 }
319}