1use crate::Duration;
7use crate::animations::core::{Animatable, AnimationMode};
8use crate::animations::spring::{Spring, SpringState};
9use crate::keyframes::KeyframeAnimation;
10use crate::pool::{ConfigHandle, global};
11use crate::prelude::{AnimationConfig, LoopMode, Tween};
12use crate::sequence::AnimationSequence;
13use std::sync::Arc;
14
15#[derive(Clone)]
18pub enum AnimationState<T: Animatable> {
19 Idle,
21 Running {
23 mode: AnimationMode,
24 config_handle: ConfigHandle,
25 },
26 Sequence {
28 sequence: Arc<AnimationSequence<T>>,
29 config_handle: ConfigHandle,
30 },
31 Keyframes {
33 animation: Arc<KeyframeAnimation<T>>,
34 config_handle: ConfigHandle,
35 },
36}
37
38impl<T: Animatable + Send + 'static> AnimationState<T> {
39 pub fn new_idle() -> Self {
41 Self::Idle
42 }
43
44 pub fn new_running(mode: AnimationMode, config_handle: ConfigHandle) -> Self {
46 Self::Running {
47 mode,
48 config_handle,
49 }
50 }
51
52 pub fn new_sequence(sequence: Arc<AnimationSequence<T>>, config_handle: ConfigHandle) -> Self {
54 Self::Sequence {
55 sequence,
56 config_handle,
57 }
58 }
59
60 pub fn new_keyframes(
62 animation: Arc<KeyframeAnimation<T>>,
63 config_handle: ConfigHandle,
64 ) -> Self {
65 Self::Keyframes {
66 animation,
67 config_handle,
68 }
69 }
70
71 pub fn is_active(&self) -> bool {
73 !matches!(self, Self::Idle)
74 }
75
76 pub fn config_handle(&self) -> Option<&ConfigHandle> {
78 match self {
79 Self::Idle => None,
80 Self::Running { config_handle, .. } => Some(config_handle),
81 Self::Sequence { config_handle, .. } => Some(config_handle),
82 Self::Keyframes { config_handle, .. } => Some(config_handle),
83 }
84 }
85
86 pub fn update(&mut self, dt: f32, motion: &mut crate::Motion<T>) -> bool {
89 match self {
90 Self::Idle => false,
91 Self::Running {
92 mode,
93 config_handle,
94 } => {
95 let mode = *mode;
96 let config_handle = config_handle.clone();
97 self.update_running(mode, &config_handle, dt, motion)
98 }
99 Self::Sequence {
100 sequence,
101 config_handle,
102 } => {
103 let sequence = sequence.clone();
104 let config_handle = config_handle.clone();
105 self.update_sequence(sequence, &config_handle, dt, motion)
106 }
107 Self::Keyframes {
108 animation,
109 config_handle,
110 } => {
111 let animation = animation.clone();
112 let config_handle = config_handle.clone();
113 self.update_keyframes(animation, &config_handle, dt, motion)
114 }
115 }
116 }
117
118 fn update_running(
120 &mut self,
121 mode: AnimationMode,
122 config_handle: &ConfigHandle,
123 dt: f32,
124 motion: &mut crate::Motion<T>,
125 ) -> bool {
126 const MIN_DELTA: f32 = 1.0 / 240.0;
128 if dt < MIN_DELTA {
129 return true;
130 }
131
132 let config = global::get_config_ref(config_handle).unwrap_or_default();
134
135 if motion.delay_elapsed < config.delay {
137 motion.delay_elapsed += Duration::from_secs_f32(dt);
138 return true;
139 }
140
141 let completed = match mode {
142 AnimationMode::Spring(spring) => {
143 let spring_result = self.update_spring(motion, spring, dt);
144 matches!(spring_result, SpringState::Completed)
145 }
146 AnimationMode::Tween(tween) => self.update_tween(motion, tween, dt),
147 };
148
149 if completed {
150 if let Some(sequence) = motion.sequence.as_ref() {
152 let sequence_clone = sequence.clone();
154 if let Some(new_mode) =
155 self.advance_sequence_step(&sequence_clone, config_handle, motion)
156 {
157 *self = Self::Running {
159 mode: new_mode,
160 config_handle: config_handle.clone(),
161 };
162 return true;
163 } else {
164 return false;
166 }
167 }
168
169 self.handle_completion(motion, &config)
171 } else {
172 true
173 }
174 }
175
176 fn advance_sequence_step(
179 &mut self,
180 sequence: &Arc<AnimationSequence<T>>,
181 config_handle: &ConfigHandle,
182 motion: &mut crate::Motion<T>,
183 ) -> Option<AnimationMode> {
184 if sequence.advance_step() {
185 if let Some(step) = sequence.current_step_data() {
187 let target = step.target;
188 let config = (*step.config).clone();
189 let mode = config.mode;
190
191 motion.initial = motion.current;
193 motion.target = target;
194 motion.running = true;
195 motion.elapsed = Duration::default();
196 motion.delay_elapsed = Duration::default();
197 motion.velocity = T::default();
198
199 global::modify_config(config_handle, |pooled_config| {
201 *pooled_config = config;
202 });
203
204 return Some(mode);
205 }
206 } else {
207 sequence.execute_completion();
210 motion.running = false;
211 motion.current_loop = 0;
212 motion.velocity = T::default();
213 motion.sequence = None;
214 motion.keyframe_animation = None;
215 *self = Self::Idle;
216 }
217
218 None
219 }
220
221 fn update_sequence(
223 &mut self,
224 sequence: Arc<AnimationSequence<T>>,
225 _config_handle: &ConfigHandle,
226 dt: f32,
227 motion: &mut crate::Motion<T>,
228 ) -> bool {
229 if !motion.running {
230 if let Some(new_mode) =
231 self.advance_sequence_step(&sequence, &global::get_config(), motion)
232 {
233 *self = Self::Running {
235 mode: new_mode,
236 config_handle: global::get_config(),
237 };
238 return true;
239 } else {
240 return false;
242 }
243 }
244
245 if let Self::Running {
248 mode,
249 config_handle,
250 } = self
251 {
252 let mode = *mode;
253 let config_handle = config_handle.clone();
254 self.update_running(mode, &config_handle, dt, motion)
255 } else {
256 false
258 }
259 }
260
261 fn update_keyframes(
263 &mut self,
264 animation: Arc<KeyframeAnimation<T>>,
265 config_handle: &ConfigHandle,
266 dt: f32,
267 motion: &mut crate::Motion<T>,
268 ) -> bool {
269 let progress =
270 (motion.elapsed.as_secs_f32() / animation.duration.as_secs_f32()).clamp(0.0, 1.0);
271
272 let (start, end) = if animation.keyframes.is_empty() {
273 return false;
275 } else {
276 animation
277 .keyframes
278 .windows(2)
279 .find(|w| progress >= w[0].offset && progress <= w[1].offset)
280 .map(|w| (&w[0], &w[1]))
281 .unwrap_or_else(|| {
282 if progress <= animation.keyframes[0].offset {
283 let first = &animation.keyframes[0];
284 (first, first)
285 } else {
286 let last = animation
287 .keyframes
288 .last()
289 .expect("Keyframes vector should not be empty here");
290 (last, last)
291 }
292 })
293 };
294
295 let local_progress = if start.offset == end.offset {
296 1.0
297 } else {
298 (progress - start.offset) / (end.offset - start.offset)
299 };
300
301 let eased_progress = end
302 .easing
303 .map_or(local_progress, |ease| (ease)(local_progress, 0.0, 1.0, 1.0));
304
305 motion.current = start.value.interpolate(&end.value, eased_progress);
306 motion.elapsed += Duration::from_secs_f32(dt);
307
308 if progress >= 1.0 {
309 let config = global::get_config_ref(config_handle).unwrap_or_default();
310 self.handle_completion(motion, &config)
311 } else {
312 true
313 }
314 }
315
316 fn update_spring(&self, motion: &mut crate::Motion<T>, spring: Spring, dt: f32) -> SpringState {
318 let epsilon = motion.get_epsilon();
319
320 let delta = motion.target - motion.current;
322 if delta.magnitude() < epsilon && motion.velocity.magnitude() < epsilon {
323 motion.current = motion.target;
324 motion.velocity = T::default();
325 return SpringState::Completed;
326 }
327
328 #[cfg(feature = "web")]
329 {
330 let stiffness = spring.stiffness;
332 let damping = spring.damping;
333 let mass_inv = 1.0 / spring.mass;
334
335 const FIXED_DT: f32 = 1.0 / 120.0;
336 let steps = ((dt / FIXED_DT) as usize).max(1);
337 let step_dt = dt / steps as f32;
338
339 for _ in 0..steps {
340 let force = delta * stiffness;
341 let damping_force = motion.velocity * damping;
342 motion.velocity = motion.velocity + (force - damping_force) * (mass_inv * step_dt);
343 motion.current = motion.current + motion.velocity * step_dt;
344 }
345 }
346
347 #[cfg(not(feature = "web"))]
348 {
349 let (new_pos, new_vel) = self.perform_rk4_integration(
351 motion.current,
352 motion.velocity,
353 motion.target,
354 &spring,
355 dt,
356 );
357 motion.current = new_pos;
358 motion.velocity = new_vel;
359 }
360
361 self.check_spring_completion(motion)
362 }
363
364 fn check_spring_completion(&self, motion: &mut crate::Motion<T>) -> SpringState {
366 let epsilon = motion.get_epsilon();
367 let epsilon_sq = epsilon * epsilon;
368 let velocity_sq = motion.velocity.magnitude().powi(2);
369 let delta = motion.target - motion.current;
370 let delta_sq = delta.magnitude().powi(2);
371
372 if velocity_sq < epsilon_sq && delta_sq < epsilon_sq {
373 motion.current = motion.target;
374 motion.velocity = T::default();
375 SpringState::Completed
376 } else {
377 SpringState::Active
378 }
379 }
380
381 fn update_tween(&self, motion: &mut crate::Motion<T>, tween: Tween, dt: f32) -> bool {
383 let elapsed_secs = motion.elapsed.as_secs_f32() + dt;
384 motion.elapsed = Duration::from_secs_f32(elapsed_secs);
385 let duration_secs = tween.duration.as_secs_f32();
386
387 let progress = if duration_secs == 0.0 {
388 1.0
389 } else {
390 (elapsed_secs * (1.0 / duration_secs)).min(1.0)
391 };
392
393 if progress <= 0.0 {
394 motion.current = motion.initial;
395 return false;
396 } else if progress >= 1.0 {
397 motion.current = motion.target;
398 return true;
399 }
400
401 let eased_progress = (tween.easing)(progress, 0.0, 1.0, 1.0);
402 match eased_progress {
403 0.0 => motion.current = motion.initial,
404 1.0 => motion.current = motion.target,
405 _ => motion.current = motion.initial.interpolate(&motion.target, eased_progress),
406 }
407
408 progress >= 1.0
409 }
410
411 fn handle_completion(
413 &mut self,
414 motion: &mut crate::Motion<T>,
415 config: &AnimationConfig,
416 ) -> bool {
417 let should_continue = match config.loop_mode.unwrap_or(LoopMode::None) {
418 LoopMode::None => {
419 motion.running = false;
420 *self = Self::Idle;
421 false
422 }
423 LoopMode::Infinite => {
424 motion.current = motion.initial;
425 motion.elapsed = Duration::default();
426 motion.velocity = T::default();
427 motion.running = true; true
429 }
430 LoopMode::Times(count) => {
431 motion.current_loop += 1;
432 if motion.current_loop >= count {
433 motion.running = false;
434 motion.current_loop = 0;
435 motion.velocity = T::default();
436 motion.sequence = None;
437 motion.keyframe_animation = None;
438 *self = Self::Idle;
439 false
440 } else {
441 motion.current = motion.initial;
442 motion.elapsed = Duration::default();
443 motion.velocity = T::default();
444 motion.running = true; true
446 }
447 }
448 LoopMode::Alternate => {
449 motion.reverse = !motion.reverse;
450 std::mem::swap(&mut motion.initial, &mut motion.target);
452 motion.current = motion.initial;
454 motion.elapsed = Duration::default();
455 motion.velocity = T::default();
456 motion.running = true; true
458 }
459 LoopMode::AlternateTimes(count) => {
460 motion.current_loop += 1;
461 if motion.current_loop >= count * 2 {
462 motion.running = false;
463 motion.current_loop = 0;
464 motion.velocity = T::default();
465 motion.sequence = None;
466 motion.keyframe_animation = None;
467 *self = Self::Idle;
468 false
469 } else {
470 motion.reverse = !motion.reverse;
471 std::mem::swap(&mut motion.initial, &mut motion.target);
473 motion.current = motion.initial;
475 motion.elapsed = Duration::default();
476 motion.velocity = T::default();
477 motion.running = true; true
479 }
480 }
481 };
482
483 if !should_continue {
484 if let Some(ref f) = config.on_complete {
485 if let Ok(mut guard) = f.lock() {
486 guard();
487 }
488 }
489 }
490
491 should_continue
492 }
493
494 #[cfg(not(feature = "web"))]
496 fn perform_rk4_integration(
497 &self,
498 current_pos: T,
499 current_vel: T,
500 target: T,
501 spring: &Spring,
502 dt: f32,
503 ) -> (T, T) {
504 use crate::pool::SpringIntegrator;
506 let mut integrator = SpringIntegrator::new();
507 integrator.integrate_rk4(current_pos, current_vel, target, spring, dt)
508 }
509}
510
511impl<T: Animatable> Default for AnimationState<T> {
512 fn default() -> Self {
513 Self::Idle
514 }
515}
516
517#[cfg(test)]
518mod tests {
519 #![allow(clippy::unwrap_used)]
520 #![allow(clippy::arc_with_non_send_sync)]
521 use super::*;
522 use crate::Motion;
523 use crate::animations::core::AnimationMode;
524 use crate::animations::spring::Spring;
525 use crate::keyframes::KeyframeAnimation;
526 use crate::prelude::Tween;
527 use crate::sequence::{AnimationSequence, AnimationStep};
528 use std::sync::{Arc, Mutex};
529
530 #[test]
531 fn test_animation_state_idle() {
532 let state = AnimationState::<f32>::new_idle();
533 assert!(!state.is_active());
534 assert!(state.config_handle().is_none());
535
536 let mut motion = Motion::new(0.0f32);
537 let mut state = state;
538 assert!(!state.update(1.0 / 60.0, &mut motion));
539 }
540
541 #[test]
542 fn test_animation_state_running() {
543 let config_handle = global::get_config();
544 let mode = AnimationMode::Tween(Tween::default());
545
546 let state = AnimationState::<f32>::new_running(mode, config_handle.clone());
547 assert!(state.is_active());
548 assert!(state.config_handle().is_some());
549
550 let mut motion = Motion::new(0.0f32);
551 motion.target = 100.0f32;
552 motion.running = true;
553
554 let mut state = state;
555 let should_continue = state.update(1.0 / 60.0, &mut motion);
556
557 assert!(should_continue);
559
560 global::return_config(config_handle);
561 }
562
563 #[test]
564 fn test_animation_state_sequence() {
565 let steps = vec![
566 AnimationStep {
567 target: 10.0f32,
568 config: Arc::new(AnimationConfig::default()),
569 predicted_next: None,
570 },
571 AnimationStep {
572 target: 20.0f32,
573 config: Arc::new(AnimationConfig::default()),
574 predicted_next: None,
575 },
576 ];
577
578 let sequence = Arc::new(AnimationSequence::from_steps(steps));
579 let config_handle = global::get_config();
580
581 let state = AnimationState::<f32>::new_sequence(sequence, config_handle.clone());
582 assert!(state.is_active());
583
584 let mut motion = Motion::new(0.0f32);
585 motion.running = false; let mut state = state;
588 let should_continue = state.update(1.0 / 60.0, &mut motion);
589
590 assert!(should_continue);
592 assert!(matches!(state, AnimationState::Running { .. }));
593
594 global::return_config(config_handle);
595 }
596
597 #[test]
598 fn test_animation_state_keyframes() {
599 let mut animation = KeyframeAnimation::new(Duration::from_secs(1));
600 animation = animation.add_keyframe(0.0f32, 0.0, None).unwrap();
601 animation = animation.add_keyframe(100.0f32, 1.0, None).unwrap();
602
603 let config_handle = global::get_config();
604 let state =
605 AnimationState::<f32>::new_keyframes(Arc::new(animation), config_handle.clone());
606 assert!(state.is_active());
607
608 let mut motion = Motion::new(0.0f32);
609 motion.elapsed = Duration::from_millis(500); let mut state = state;
612 let should_continue = state.update(1.0 / 60.0, &mut motion);
613
614 assert!(should_continue);
616 assert!(motion.current > 0.0 && motion.current < 100.0);
618
619 global::return_config(config_handle);
620 }
621
622 #[test]
623 fn test_animation_state_spring_completion() {
624 let config_handle = global::get_config();
625 global::modify_config(&config_handle, |config| {
626 config.mode = AnimationMode::Spring(Spring::default());
627 });
628
629 let mode = AnimationMode::Spring(Spring::default());
630 let state = AnimationState::<f32>::new_running(mode, config_handle.clone());
631
632 let mut motion = Motion::new(0.0f32);
633 motion.target = 0.0f32; motion.velocity = 0.0f32; motion.running = true;
636
637 let mut state = state;
638 let should_continue = state.update(1.0 / 60.0, &mut motion);
639
640 assert!(!should_continue);
642 assert!(matches!(state, AnimationState::Idle));
643
644 global::return_config(config_handle);
645 }
646
647 #[test]
648 fn test_loop_mode_infinite() {
649 use crate::Motion;
650 use crate::animations::core::AnimationMode;
651 use crate::prelude::{AnimationConfig, LoopMode, Tween};
652
653 let mut motion = Motion::new(0.0f32);
654
655 motion.animate_to(
656 100.0,
657 AnimationConfig::new(AnimationMode::Tween(Tween::default()))
658 .with_loop(LoopMode::Infinite),
659 );
660
661 assert!(motion.is_running());
663
664 let dt = 1.0 / 60.0; for i in 0..30 {
669 let should_continue = motion.update(dt);
670 println!(
671 "Frame {}: value={:.2}, running={}, should_continue={}",
672 i,
673 motion.get_value(),
674 motion.is_running(),
675 should_continue
676 );
677
678 assert!(
680 should_continue,
681 "Animation stopped unexpectedly at frame {i}"
682 );
683 assert!(
684 motion.is_running(),
685 "Motion should still be running at frame {i}"
686 );
687 }
688 }
689
690 #[test]
691 fn test_loop_mode_times() {
692 use crate::Motion;
693 use crate::animations::core::AnimationMode;
694 use crate::prelude::{AnimationConfig, LoopMode, Tween};
695
696 let mut motion = Motion::new(0.0f32);
697
698 motion.animate_to(
699 100.0,
700 AnimationConfig::new(AnimationMode::Tween(Tween::default()))
701 .with_loop(LoopMode::Times(2)), );
703
704 assert!(motion.is_running());
706
707 let dt = 1.0 / 60.0; let mut completed_loops = 0;
709 let mut last_value = motion.get_value();
710
711 for i in 0..40 {
713 let should_continue = motion.update(dt);
714 let current_value = motion.get_value();
715
716 if current_value < last_value && last_value > 50.0 {
718 completed_loops += 1;
719 println!("Detected loop completion #{completed_loops} at frame {i}");
720 }
721
722 last_value = current_value;
723
724 println!(
725 "Frame {}: value={:.2}, running={}, should_continue={}, loops={}",
726 i,
727 current_value,
728 motion.is_running(),
729 should_continue,
730 completed_loops
731 );
732
733 if !should_continue {
735 assert!(
737 completed_loops >= 1,
738 "Animation should have completed at least 1 loop"
739 );
740 assert!(
741 !motion.is_running(),
742 "Motion should not be running after completion"
743 );
744 break;
745 }
746 }
747
748 assert!(
750 completed_loops >= 1,
751 "Animation should have completed at least 1 loop, but only completed {completed_loops}"
752 );
753 }
754
755 #[test]
756 fn test_loop_mode_alternate() {
757 use crate::Motion;
758 use crate::animations::core::AnimationMode;
759 use crate::prelude::{AnimationConfig, LoopMode, Tween};
760
761 let mut motion = Motion::new(0.0f32);
762
763 motion.animate_to(
764 100.0,
765 AnimationConfig::new(AnimationMode::Tween(Tween::default()))
766 .with_loop(LoopMode::Alternate),
767 );
768
769 assert!(motion.is_running());
771
772 let dt = 1.0 / 60.0; let mut direction_changes = 0;
774 let mut last_value = motion.get_value();
775 let mut going_up = true;
776
777 for i in 0..60 {
779 let should_continue = motion.update(dt);
780 let current_value = motion.get_value();
781
782 if going_up && current_value < last_value && last_value > 50.0 {
784 going_up = false;
786 direction_changes += 1;
787 println!(
788 "Direction change #{direction_changes} at frame {i}: going DOWN (value: {current_value:.2})"
789 );
790 } else if !going_up && current_value > last_value && last_value < 50.0 {
791 going_up = true;
793 direction_changes += 1;
794 println!(
795 "Direction change #{direction_changes} at frame {i}: going UP (value: {current_value:.2})"
796 );
797 }
798
799 last_value = current_value;
800
801 println!(
802 "Frame {i}: value={current_value:.2}, running={}, should_continue={should_continue}, direction={}",
803 motion.is_running(),
804 if going_up { "UP" } else { "DOWN" }
805 );
806
807 assert!(
809 should_continue,
810 "Animation stopped unexpectedly at frame {i}"
811 );
812 assert!(
813 motion.is_running(),
814 "Motion should still be running at frame {i}"
815 );
816
817 if direction_changes >= 3 {
819 break;
820 }
821 }
822
823 assert!(
825 direction_changes >= 2,
826 "Animation should have alternated direction at least twice, but only changed {direction_changes} times"
827 );
828 }
829
830 #[test]
831 fn test_loop_mode_alternate_times() {
832 use crate::Motion;
833 use crate::animations::core::AnimationMode;
834 use crate::prelude::{AnimationConfig, LoopMode, Tween};
835
836 let mut motion = Motion::new(0.0f32);
837
838 motion.animate_to(
839 100.0,
840 AnimationConfig::new(AnimationMode::Tween(Tween::default()))
841 .with_loop(LoopMode::AlternateTimes(2)), );
843
844 assert!(motion.is_running());
846
847 let dt = 1.0 / 60.0; let mut direction_changes = 0;
849 let mut last_value = motion.get_value();
850 let mut going_up = true;
851
852 for i in 0..120 {
854 let should_continue = motion.update(dt);
855 let current_value = motion.get_value();
856
857 if going_up && current_value < last_value && last_value > 50.0 {
859 going_up = false;
861 direction_changes += 1;
862 println!(
863 "Direction change #{direction_changes} at frame {i}: going DOWN (value: {current_value:.2})"
864 );
865 } else if !going_up && current_value > last_value && last_value < 50.0 {
866 going_up = true;
868 direction_changes += 1;
869 println!(
870 "Direction change #{direction_changes} at frame {i}: going UP (value: {current_value:.2})"
871 );
872 }
873
874 last_value = current_value;
875
876 println!(
877 "Frame {i}: value={current_value:.2}, running={}, should_continue={should_continue}, direction={}",
878 motion.is_running(),
879 if going_up { "UP" } else { "DOWN" }
880 );
881
882 if !should_continue {
884 assert!(
885 !motion.is_running(),
886 "Motion should not be running after completion"
887 );
888 println!("Animation completed after {direction_changes} direction changes");
889 break;
890 }
891 }
892
893 assert!(
896 direction_changes >= 3,
897 "Animation should have alternated at least 3 times for AlternateTimes(2), but only changed {direction_changes} times"
898 );
899 }
900
901 #[test]
902 fn test_animation_state_tween_completion() {
903 let config_handle = global::get_config();
904 global::modify_config(&config_handle, |config| {
905 config.mode = AnimationMode::Tween(Tween::default());
906 });
907
908 let mode = AnimationMode::Tween(Tween::default());
909 let state = AnimationState::<f32>::new_running(mode, config_handle.clone());
910
911 let mut motion = Motion::new(0.0f32);
912 motion.target = 100.0f32;
913 motion.running = true;
914 motion.elapsed = Duration::from_secs(2); let mut state = state;
917 let should_continue = state.update(1.0 / 60.0, &mut motion);
918
919 assert!(!should_continue);
921 assert!(matches!(state, AnimationState::Idle));
922 assert_eq!(motion.current, motion.target);
923
924 global::return_config(config_handle);
925 }
926
927 #[test]
928 fn test_animation_state_loop_infinite() {
929 let config_handle = global::get_config();
930 global::modify_config(&config_handle, |config| {
931 config.mode = AnimationMode::Tween(Tween::default());
932 config.loop_mode = Some(LoopMode::Infinite);
933 });
934
935 let mode = AnimationMode::Tween(Tween::default());
936 let state = AnimationState::<f32>::new_running(mode, config_handle.clone());
937
938 let mut motion = Motion::new(0.0f32);
939 motion.target = 100.0f32;
940 motion.running = true;
941 motion.elapsed = Duration::from_secs(2); let mut state = state;
944 let should_continue = state.update(1.0 / 60.0, &mut motion);
945
946 assert!(should_continue);
948 assert!(matches!(state, AnimationState::Running { .. }));
949 assert_eq!(motion.current, motion.initial);
951 assert_eq!(motion.elapsed, Duration::default());
952
953 global::return_config(config_handle);
954 }
955
956 #[test]
957 fn test_animation_state_completion_callback() {
958 let callback_executed = Arc::new(Mutex::new(false));
959 let callback_executed_clone = callback_executed.clone();
960
961 let config_handle = global::get_config();
962 global::modify_config(&config_handle, |config| {
963 config.mode = AnimationMode::Tween(Tween::default());
964 config.on_complete = Some(Arc::new(Mutex::new(Box::new(move || {
965 *callback_executed_clone.lock().unwrap() = true;
966 }))));
967 });
968
969 let mode = AnimationMode::Tween(Tween::default());
970 let state = AnimationState::<f32>::new_running(mode, config_handle.clone());
971
972 let mut motion = Motion::new(0.0f32);
973 motion.target = 100.0f32;
974 motion.running = true;
975 motion.elapsed = Duration::from_secs(2); let mut state = state;
978 let should_continue = state.update(1.0 / 60.0, &mut motion);
979
980 assert!(!should_continue);
982 assert!(*callback_executed.lock().unwrap());
983
984 global::return_config(config_handle);
985 }
986
987 #[test]
988 fn test_animation_state_delay_handling() {
989 let config_handle = global::get_config();
990 global::modify_config(&config_handle, |config| {
991 config.mode = AnimationMode::Tween(Tween::default());
992 config.delay = Duration::from_millis(100);
993 });
994
995 let mode = AnimationMode::Tween(Tween::default());
996 let state = AnimationState::<f32>::new_running(mode, config_handle.clone());
997
998 let mut motion = Motion::new(0.0f32);
999 motion.target = 100.0f32;
1000 motion.running = true;
1001 motion.delay_elapsed = Duration::from_millis(50); let mut state = state;
1004 let should_continue = state.update(1.0 / 60.0, &mut motion);
1005
1006 assert!(should_continue);
1008 assert_eq!(motion.current, motion.initial); global::return_config(config_handle);
1011 }
1012}