1use crate::{Animatable, Animation, AnimationState, IntoMotionAnimation, timing::Duration};
4
5pub struct Field<S, V> {
10 name: &'static str,
11 get: fn(&S) -> &V,
12 get_mut: fn(&mut S) -> &mut V,
13}
14
15impl<S, V> Copy for Field<S, V> {}
16
17impl<S, V> Clone for Field<S, V> {
18 fn clone(&self) -> Self {
19 *self
20 }
21}
22
23impl<S, V> Field<S, V> {
24 #[must_use]
26 pub const fn new(name: &'static str, get: fn(&S) -> &V, get_mut: fn(&mut S) -> &mut V) -> Self {
27 Self { name, get, get_mut }
28 }
29
30 #[must_use]
32 pub const fn name(self) -> &'static str {
33 self.name
34 }
35
36 fn get(self, value: &S) -> &V {
37 (self.get)(value)
38 }
39
40 fn set(self, value: &mut S, field_value: V) {
41 *(self.get_mut)(value) = field_value;
42 }
43}
44
45trait FieldAnimationFactory<S>: 'static {
46 fn name(&self) -> &'static str;
47 fn build(self: Box<Self>, initial: &S) -> Box<dyn FieldAnimation<S>>;
48}
49
50struct TypedFieldAnimationFactory<S, V, Source, Kind> {
51 field: Field<S, V>,
52 source: Source,
53 marker: std::marker::PhantomData<fn() -> Kind>,
54}
55
56impl<S, V, Source, Kind> FieldAnimationFactory<S> for TypedFieldAnimationFactory<S, V, Source, Kind>
57where
58 S: Animatable,
59 V: Animatable,
60 Source: IntoMotionAnimation<V, Kind> + 'static,
61 Kind: 'static,
62{
63 fn name(&self) -> &'static str {
64 self.field.name()
65 }
66
67 fn build(self: Box<Self>, initial: &S) -> Box<dyn FieldAnimation<S>> {
68 let Self { field, source, .. } = *self;
69 let animation = source.into_motion_animation(field.get(initial));
70
71 Box::new(TypedFieldAnimation { field, animation })
72 }
73}
74
75trait FieldAnimation<S>: 'static {
76 fn value_into(&self, output: &mut S);
77 fn state(&self) -> AnimationState;
78 fn duration(&self) -> Option<Duration>;
79 fn advance(&mut self, delta: Duration) -> Duration;
80 fn pause(&mut self);
81 fn resume(&mut self);
82 fn cancel(&mut self);
83 fn seek(&mut self, progress: f32);
84 fn finish(&mut self);
85 fn set_rate(&mut self, rate: f64);
86}
87
88struct TypedFieldAnimation<S, V, A> {
89 field: Field<S, V>,
90 animation: A,
91}
92
93impl<S, V, A> FieldAnimation<S> for TypedFieldAnimation<S, V, A>
94where
95 S: Animatable,
96 V: Animatable,
97 A: Animation<V>,
98{
99 fn value_into(&self, output: &mut S) {
100 self.field.set(output, self.animation.value().clone());
101 }
102
103 fn state(&self) -> AnimationState {
104 self.animation.state()
105 }
106
107 fn duration(&self) -> Option<Duration> {
108 self.animation.duration()
109 }
110
111 fn advance(&mut self, delta: Duration) -> Duration {
112 self.animation.advance(delta)
113 }
114
115 fn pause(&mut self) {
116 self.animation.pause();
117 }
118
119 fn resume(&mut self) {
120 self.animation.resume();
121 }
122
123 fn cancel(&mut self) {
124 self.animation.cancel();
125 }
126
127 fn seek(&mut self, progress: f32) {
128 self.animation.seek(progress);
129 }
130
131 fn finish(&mut self) {
132 self.animation.finish();
133 }
134
135 fn set_rate(&mut self, rate: f64) {
136 self.animation.set_rate(rate);
137 }
138}
139
140pub struct Fields<S: Animatable> {
146 factories: Vec<Box<dyn FieldAnimationFactory<S>>>,
147}
148
149impl<S: Animatable> Fields<S> {
150 #[must_use]
152 pub fn new() -> Self {
153 Self {
154 factories: Vec::new(),
155 }
156 }
157
158 #[must_use]
164 pub fn animate<V, Source, Kind>(mut self, field: Field<S, V>, source: Source) -> Self
165 where
166 V: Animatable,
167 Source: IntoMotionAnimation<V, Kind> + 'static,
168 Kind: 'static,
169 {
170 let factory = Box::new(TypedFieldAnimationFactory {
171 field,
172 source,
173 marker: std::marker::PhantomData::<fn() -> Kind>,
174 });
175 if let Some(existing) = self.factories.iter_mut().find(|f| f.name() == field.name()) {
176 *existing = factory;
177 } else {
178 self.factories.push(factory);
179 }
180 self
181 }
182
183 #[must_use]
185 pub fn len(&self) -> usize {
186 self.factories.len()
187 }
188
189 #[must_use]
191 pub fn is_empty(&self) -> bool {
192 self.factories.is_empty()
193 }
194
195 pub(crate) fn build(self, initial: &S) -> FieldsAnimation<S> {
196 let children: Vec<_> = self
197 .factories
198 .into_iter()
199 .map(|factory| factory.build(initial))
200 .collect();
201
202 let state = if children.is_empty() {
203 AnimationState::Idle
204 } else {
205 AnimationState::Running
206 };
207
208 let mut animation = FieldsAnimation {
209 current: initial.clone(),
210 children,
211 state,
212 };
213 animation.sample();
214 animation
215 }
216}
217
218impl<S: Animatable> Default for Fields<S> {
219 fn default() -> Self {
220 Self::new()
221 }
222}
223
224#[must_use]
226pub fn fields<S: Animatable>() -> Fields<S> {
227 Fields::new()
228}
229
230pub struct FieldsAnimation<S: Animatable> {
232 current: S,
233 children: Vec<Box<dyn FieldAnimation<S>>>,
234 state: AnimationState,
235}
236
237impl<S: Animatable> FieldsAnimation<S> {
238 fn sample(&mut self) {
239 for child in &self.children {
240 child.value_into(&mut self.current);
241 }
242 }
243}
244
245impl<S: Animatable> Animation<S> for FieldsAnimation<S> {
246 fn value(&self) -> &S {
247 &self.current
248 }
249
250 fn state(&self) -> AnimationState {
251 self.state
252 }
253
254 fn duration(&self) -> Option<Duration> {
255 self.children
256 .iter()
257 .map(|child| child.duration())
258 .try_fold(Duration::ZERO, |longest, duration| {
259 duration.map(|duration| longest.max(duration))
260 })
261 }
262
263 fn tick(&mut self, delta: Duration) {
264 self.advance(delta);
265 }
266
267 fn advance(&mut self, delta: Duration) -> Duration {
268 if self.state != AnimationState::Running {
269 return delta;
270 }
271 if self.children.is_empty() {
272 self.state = AnimationState::Completed;
273 return delta;
274 }
275
276 let mut overflow = delta;
277 let mut completed = true;
278 for child in &mut self.children {
279 overflow = overflow.min(child.advance(delta));
280 completed &= child.state() == AnimationState::Completed;
281 }
282 self.sample();
283
284 if completed {
285 self.state = AnimationState::Completed;
286 overflow
287 } else {
288 Duration::ZERO
289 }
290 }
291
292 fn pause(&mut self) {
293 if self.state == AnimationState::Running {
294 for child in &mut self.children {
295 child.pause();
296 }
297 self.state = AnimationState::Paused;
298 }
299 }
300
301 fn resume(&mut self) {
302 if self.state == AnimationState::Paused {
303 for child in &mut self.children {
304 child.resume();
305 }
306 self.state = AnimationState::Running;
307 }
308 }
309
310 fn cancel(&mut self) {
311 if matches!(self.state, AnimationState::Running | AnimationState::Paused) {
312 for child in &mut self.children {
313 child.cancel();
314 }
315 self.state = AnimationState::Canceled;
316 }
317 }
318
319 fn seek(&mut self, progress: f32) {
320 let progress = if progress.is_nan() {
321 0.0
322 } else {
323 progress.clamp(0.0, 1.0)
324 };
325 let duration = self.duration();
326
327 for child in &mut self.children {
328 #[allow(clippy::cast_possible_truncation)]
329 let child_progress = match (duration, child.duration()) {
330 (Some(total), Some(child_duration)) if !child_duration.is_zero() => {
331 (total.as_secs() * f64::from(progress) / child_duration.as_secs())
332 .clamp(0.0, 1.0) as f32
333 }
334 (Some(_), Some(_)) => 1.0,
335 _ => progress,
336 };
337 child.seek(child_progress);
338 }
339 self.sample();
340 self.state = if progress >= 1.0 {
341 AnimationState::Completed
342 } else {
343 AnimationState::Running
344 };
345 }
346
347 fn finish(&mut self) {
348 for child in &mut self.children {
349 child.finish();
350 }
351 self.sample();
352 self.state = AnimationState::Completed;
353 }
354
355 fn set_rate(&mut self, rate: f64) {
356 for child in &mut self.children {
357 child.set_rate(rate);
358 }
359 }
360
361 fn into_value(self: Box<Self>) -> S {
362 self.current
363 }
364}
365
366#[cfg(test)]
367mod tests {
368 use super::{Field, fields};
369 use crate::{
370 Animation, AnimationState, Tween,
371 timing::{Duration, Timing},
372 };
373 use float_cmp::assert_approx_eq;
374
375 #[derive(Clone)]
376 struct Position {
377 x: f32,
378 y: f32,
379 fixed: f32,
380 }
381
382 impl crate::Interpolate for Position {
383 fn interpolate_progress(
384 from: &Self,
385 to: &Self,
386 progress: crate::InterpolationProgress,
387 ) -> Self {
388 Self {
389 x: f32::interpolate_progress(&from.x, &to.x, progress),
390 y: f32::interpolate_progress(&from.y, &to.y, progress),
391 fixed: f32::interpolate_progress(&from.fixed, &to.fixed, progress),
392 }
393 }
394 }
395
396 const X: Field<Position, f32> = Field::new("x", |value| &value.x, |value| &mut value.x);
397 const Y: Field<Position, f32> = Field::new("y", |value| &value.y, |value| &mut value.y);
398
399 #[test]
400 fn fields_run_independently_and_preserve_unregistered_values() {
401 let initial = Position {
402 x: 0.0,
403 y: 10.0,
404 fixed: 7.0,
405 };
406 let mut animation = fields()
407 .animate(X, |from| Tween::between(from, 100.0, Timing::new(100.0)))
408 .animate(Y, |from| Tween::between(from, 210.0, Timing::new(200.0)))
409 .build(&initial);
410
411 animation.tick(Duration::from_millis(100.0));
412
413 assert_approx_eq!(f32, animation.value().x, 100.0);
414 assert_approx_eq!(f32, animation.value().y, 110.0);
415 assert_approx_eq!(f32, animation.value().fixed, 7.0);
416 assert_eq!(animation.state(), AnimationState::Running);
417 }
418
419 #[test]
420 fn duplicate_field_registration_keeps_the_last_animation() {
421 let initial = Position {
422 x: 0.0,
423 y: 0.0,
424 fixed: 0.0,
425 };
426 let mut animation = fields()
427 .animate(X, |from| Tween::between(from, 10.0, Timing::new(100.0)))
428 .animate(X, |from| Tween::between(from, 20.0, Timing::new(100.0)))
429 .build(&initial);
430
431 assert_eq!(animation.children.len(), 1);
432 animation.finish();
433 assert_approx_eq!(f32, animation.value().x, 20.0);
434 }
435
436 #[test]
437 fn unregistered_fields_retain_their_original_value() {
438 let initial = Position {
439 x: 0.0,
440 y: 0.0,
441 fixed: 7.0,
442 };
443 let mut animation = fields()
444 .animate(Y, |from| Tween::between(from, 10.0, Timing::new(100.0)))
445 .build(&initial);
446
447 animation.tick(Duration::from_millis(100.0));
448 assert_eq!(animation.state(), AnimationState::Completed);
449 assert_approx_eq!(f32, animation.value().x, 0.0);
450 assert_approx_eq!(f32, animation.value().y, 10.0);
451 assert_approx_eq!(f32, animation.value().fixed, 7.0);
452 }
453}