1use crate::traits::{AnimationTime, FloatRepresentable, Interpolable};
2#[derive(Clone, Debug, Default)]
44pub struct Animated<T, Time>
45where
46 T: FloatRepresentable + Clone + Copy + PartialEq,
47 Time: AnimationTime,
48{
49 animation: Animation<Time>,
50 pub value: T,
51 last_value: T,
52}
53
54impl<T, Time> Animated<T, Time>
55where
56 T: FloatRepresentable + Clone + Copy + PartialEq,
57 Time: AnimationTime,
58{
59 pub fn new_with_settings(value: T, duration_ms: f32, easing: Easing) -> Self {
61 let mut animation = Animation::default(value.float_value());
62 animation.settings.duration_ms = duration_ms;
63 animation.settings.easing = easing;
64 Animated {
65 value,
66 last_value: value,
67 animation,
68 }
69 }
70 pub fn new(value: T) -> Self {
72 Self {
73 value,
74 last_value: value,
75 animation: Animation::default(value.float_value()),
76 }
77 }
78 pub fn duration(mut self, duration_ms: f32) -> Self {
80 self.animation.settings.duration_ms = duration_ms;
81 self
82 }
83 pub fn easing(mut self, easing: Easing) -> Self {
85 self.animation.settings.easing = easing;
86 self
87 }
88 pub fn delay(mut self, delay_ms: f32) -> Self {
90 self.animation.delay_ms = delay_ms;
91 self
92 }
93 pub fn repeat(mut self, count: u32) -> Self {
96 self.animation.repetitions = count;
97 self
98 }
99 pub fn repeat_forever(mut self) -> Self {
101 self.animation.repeat_forever = true;
102 self
103 }
104 pub fn auto_reverse(mut self) -> Self {
106 self.animation.auto_reverse_repetitions = true;
107 self
108 }
109 pub fn auto_start(mut self, new_value: T, at: Time) -> Self {
111 self.transition(new_value, at);
112 self
113 }
114 pub fn asymmetric_duration(mut self, duration_ms: f32) -> Self {
116 self.animation.asymmetric_settings = Some(AnimationSettings {
117 duration_ms,
118 easing: self
119 .animation
120 .asymmetric_settings
121 .map(|a| a.easing)
122 .unwrap_or(self.animation.settings.easing),
123 });
124 self
125 }
126 pub fn asymmetric_easing(mut self, easing: Easing) -> Self {
128 self.animation.asymmetric_settings = Some(AnimationSettings {
129 duration_ms: self
130 .animation
131 .asymmetric_settings
132 .map(|a| a.duration_ms)
133 .unwrap_or(self.animation.settings.duration_ms),
134 easing,
135 });
136 self
137 }
138 pub fn transition(&mut self, new_value: T, at: Time) {
140 self.transition_internal(new_value, at, false);
141 }
142 pub fn transition_instantaneous(&mut self, new_value: T, at: Time) {
145 self.transition_internal(new_value, at, true);
146 }
147 fn transition_internal(&mut self, new_value: T, at: Time, instantaneous: bool) {
148 if self.value != new_value {
149 self.last_value = self.value;
150 self.value = new_value;
151 self.animation
152 .transition(new_value.float_value(), at, instantaneous)
153 }
154 }
155 pub fn in_progress(&self, time: Time) -> bool {
157 self.animation.in_progress(time)
158 }
159 pub fn animate<I>(&self, map: impl Fn(T) -> I, time: Time) -> I
161 where
162 I: Interpolable,
163 {
164 let interrupted_range = self.value.float_value() - self.last_value.float_value();
176 let unit_interrupt_value = if interrupted_range == 0. {
177 0.
178 } else {
179 (self.animation.origin - self.last_value.float_value()) / interrupted_range
180 };
181 let interrupt_interpolable =
182 map(self.last_value).interpolated(map(self.value), unit_interrupt_value);
183 interrupt_interpolable
184 .interpolated(map(self.value), self.animation.eased_unit_progress(time))
185 }
186 #[allow(dead_code)]
188 fn linear_progress(&self, time: Time) -> f32 {
189 self.animation.linear_progress(time)
190 }
191 #[allow(dead_code)]
192 fn eased_progress(&self, time: Time) -> f32 {
193 self.animation.eased_progress(time)
194 }
195}
196
197impl<T, Time> Animated<T, Time>
198where
199 T: FloatRepresentable + Clone + Copy + PartialEq,
200 Time: AnimationTime,
201{
202 pub fn animate_if_eq<I>(&self, value: T, equal: I, default: I, time: Time) -> I
205 where
206 I: Interpolable + Clone,
207 {
208 self.animate(
209 |v| {
210 if v == value {
211 equal.clone()
212 } else {
213 default.clone()
214 }
215 },
216 time,
217 )
218 }
219}
220
221impl<T, Time> Animated<T, Time>
222where
223 T: FloatRepresentable + Clone + Copy + PartialEq,
224 T: Interpolable,
225 Time: AnimationTime,
226{
227 pub fn animate_wrapped(&self, time: Time) -> T {
228 self.animate(|v| v, time)
229 }
230}
231
232impl<Time> Animated<bool, Time>
233where
234 Time: AnimationTime,
235{
236 pub fn animate_bool<I>(&self, false_value: I, true_value: I, time: Time) -> I
238 where
239 I: Interpolable + Clone,
240 {
241 self.animate(
242 move |b| {
243 if b {
244 true_value.clone()
245 } else {
246 false_value.clone()
247 }
248 },
249 time,
250 )
251 }
252}
253
254#[derive(Clone, Copy, Debug, Default)]
255struct Animation<Time>
256where
257 Time: AnimationTime,
258{
259 origin: f32,
260 destination: f32,
261 delay_ms: f32,
262 settings: AnimationSettings,
263 asymmetric_settings: Option<AnimationSettings>,
264 repetitions: u32,
265 auto_reverse_repetitions: bool,
266 repeat_forever: bool,
267 transition_time: Option<Time>,
268}
269
270#[derive(Clone, Copy, Debug, Default)]
271struct AnimationSettings {
272 duration_ms: f32,
273 easing: Easing,
274}
275
276impl<Time> Animation<Time>
277where
278 Time: AnimationTime,
279{
280 fn default(origin: f32) -> Self {
281 Animation {
282 origin,
283 destination: origin,
284 settings: AnimationSettings {
285 duration_ms: 100.,
286 easing: Easing::EaseInOut,
287 },
288 asymmetric_settings: None,
289 delay_ms: 0.,
290 repetitions: 1,
291 auto_reverse_repetitions: false,
292 repeat_forever: false,
293 transition_time: None,
294 }
295 }
296
297 fn transition(&mut self, destination: f32, time: Time, instantaneous: bool) {
298 if self.destination != destination {
299 if instantaneous {
300 self.origin = destination;
301 self.destination = destination;
302 return;
303 }
304 if self.in_progress(time) {
305 let eased_progress = self.eased_progress(time);
306 self.origin = eased_progress;
307 } else {
308 self.origin = self.destination;
309 }
310 self.transition_time = Some(time);
311 self.destination = destination;
312 }
313 }
314
315 fn current_progress(&self, time: Time) -> Progress {
316 let Some(transition_time) = self.transition_time else {
317 return Progress {
318 linear_unit_progress: 0.,
319 eased_unit_progress: 0.,
320 complete: true,
321 };
322 };
323 let elapsed = f32::max(0., time.elapsed_since(transition_time) - self.delay_ms);
324
325 let settings;
326 let elapsed_current;
327 let auto_reversing;
328
329 if self.auto_reverse_repetitions {
330 let asymmetry = self.asymmetric_settings.unwrap_or(self.settings);
331 let combined_durations = self.settings.duration_ms + asymmetry.duration_ms;
332 let first_animation = elapsed % combined_durations - self.settings.duration_ms < 0.;
333 if first_animation {
334 elapsed_current = elapsed % combined_durations;
335 settings = self.settings;
336 auto_reversing = false;
337 } else {
338 settings = asymmetry;
339 elapsed_current = elapsed % combined_durations - self.settings.duration_ms;
340 auto_reversing = true;
341 }
342 } else if self.destination < self.origin {
343 settings = self.asymmetric_settings.unwrap_or(self.settings);
344 elapsed_current = elapsed;
345 auto_reversing = false;
346 } else {
347 settings = self.settings;
348 elapsed_current = elapsed;
349 auto_reversing = false;
350 }
351
352 let total_duration = self.total_duration();
353 if total_duration == 0. {
354 return Progress {
355 linear_unit_progress: 1.,
356 eased_unit_progress: settings.easing.value(1.),
357 complete: true,
358 };
359 }
360
361 let complete = !self.repeat_forever && elapsed >= total_duration;
362 let repeat = elapsed_current / settings.duration_ms;
363 let progress = if complete { 1. } else { repeat % 1. };
364 if auto_reversing && !complete {
365 Progress {
366 linear_unit_progress: 1. - progress,
367 eased_unit_progress: settings.easing.value(1. - progress),
368 complete,
369 }
370 } else {
371 Progress {
372 linear_unit_progress: progress,
373 eased_unit_progress: settings.easing.value(progress),
374 complete,
375 }
376 }
377 }
378
379 fn linear_unit_progress(&self, time: Time) -> f32 {
380 self.current_progress(time).linear_unit_progress
381 }
382
383 fn eased_unit_progress(&self, time: Time) -> f32 {
384 self.current_progress(time).eased_unit_progress
385 }
386
387 fn total_duration(&self) -> f32 {
388 let true_repetitions = if self.auto_reverse_repetitions {
389 (self.repetitions * 2) + 1
390 } else {
391 self.repetitions
392 } as f32;
393 if true_repetitions > 1. {
394 if true_repetitions % 2. == 0. {
395 self.settings.duration_ms * (true_repetitions * 0.5)
396 + self
397 .asymmetric_settings
398 .unwrap_or(self.settings)
399 .duration_ms
400 * (true_repetitions * 0.5)
401 } else {
402 self.settings.duration_ms * ((true_repetitions - true_repetitions % 2.) * 0.5)
403 + self
404 .asymmetric_settings
405 .unwrap_or(self.settings)
406 .duration_ms
407 * ((true_repetitions - true_repetitions % 2.) * 0.5)
408 + self.settings.duration_ms
409 }
410 } else if self.destination < self.origin {
411 self.asymmetric_settings
412 .unwrap_or(self.settings)
413 .duration_ms
414 * true_repetitions
415 } else {
416 self.settings.duration_ms * true_repetitions
417 }
418 }
419
420 fn linear_progress(&self, time: Time) -> f32 {
421 self.origin + (self.linear_unit_progress(time) * self.progress_range())
422 }
423
424 fn eased_progress(&self, time: Time) -> f32 {
425 self.origin + (self.eased_unit_progress(time) * self.progress_range())
426 }
427
428 fn progress_range(&self) -> f32 {
429 self.destination - self.origin
430 }
431
432 fn in_progress(&self, time: Time) -> bool {
433 !self.current_progress(time).complete
434 }
435}
436
437struct Progress {
438 linear_unit_progress: f32,
439 eased_unit_progress: f32,
440 complete: bool,
441}
442
443#[derive(Clone, Copy, Debug, PartialEq, Default)]
444pub enum Easing {
445 #[default]
446 Linear,
447 EaseIn,
448 EaseOut,
449 EaseInOut,
450 EaseInQuad,
451 EaseOutQuad,
452 EaseInOutQuad,
453 EaseInCubic,
454 EaseOutCubic,
455 EaseInOutCubic,
456 EaseInQuart,
457 EaseOutQuart,
458 EaseInOutQuart,
459 EaseInQuint,
460 EaseOutQuint,
461 EaseInOutQuint,
462 EaseInExpo,
463 EaseOutExpo,
464 EaseInOutExpo,
465 EaseInCirc,
466 EaseOutCirc,
467 EaseInOutCirc,
468 EaseInBack,
469 EaseOutBack,
470 EaseInOutBack,
471 EaseInElastic,
472 EaseOutElastic,
473 EaseInOutElastic,
474 EaseInBounce,
475 EaseOutBounce,
476 EaseInOutBounce,
477 Custom(fn(f32) -> f32),
478}
479
480impl Easing {
481 pub fn value(self, x: f32) -> f32 {
482 const PI: f32 = std::f32::consts::PI;
483
484 match self {
485 Easing::Linear => x,
486 Easing::EaseIn => 1.0 - f32::cos((x * PI) / 2.0),
487 Easing::EaseOut => f32::sin((x * PI) / 2.0),
488 Easing::EaseInOut => -(f32::cos(PI * x) - 1.0) / 2.0,
489 Easing::EaseInQuad => x.powi(2),
490 Easing::EaseOutQuad => 1.0 - (1.0 - x) * (1.0 - x),
491 Easing::EaseInOutQuad => {
492 if x < 0.5 {
493 2.0 * x.powi(2)
494 } else {
495 1.0 - (-2.0 * x + 2.0).powi(2) / 2.0
496 }
497 }
498 Easing::EaseInCubic => x.powi(3),
499 Easing::EaseOutCubic => 1.0 - (1.0 - x).powi(3),
500 Easing::EaseInOutCubic => {
501 if x < 0.5 {
502 4.0 * x.powi(3)
503 } else {
504 1.0 - (-2.0 * x + 2.0).powi(3) / 2.0
505 }
506 }
507 Easing::EaseInQuart => x.powi(4),
508 Easing::EaseOutQuart => 1.0 - (1.0 - x).powi(4),
509 Easing::EaseInOutQuart => {
510 if x < 0.5 {
511 8.0 * x.powi(4)
512 } else {
513 1.0 - (-2.0 * x + 2.0).powi(4) / 2.0
514 }
515 }
516 Easing::EaseInQuint => x.powi(5),
517 Easing::EaseOutQuint => 1.0 - (1.0 - x).powi(5),
518 Easing::EaseInOutQuint => {
519 if x < 0.5 {
520 16.0 * x.powi(5)
521 } else {
522 1.0 - (-2.0 * x + 2.0).powi(5) / 2.0
523 }
524 }
525 Easing::EaseInExpo => {
526 if x == 0.0 {
527 0.0
528 } else {
529 (2.0_f32).powf(10.0 * x - 10.0)
530 }
531 }
532 Easing::EaseOutExpo => {
533 if x == 1.0 {
534 1.0
535 } else {
536 1.0 - (2.0_f32).powf(-10.0 * x)
537 }
538 }
539 Easing::EaseInOutExpo => match x {
540 0.0 => 0.0,
541 1.0 => 1.0,
542 x if x < 0.5 => (2.0_f32).powf(20.0 * x - 10.0) / 2.0,
543 _ => (2.0 - (2.0_f32).powf(-20.0 * x + 10.0)) / 2.0,
544 },
545 Easing::EaseInCirc => 1.0 - (1.0 - x.powi(2)).sqrt(),
546 Easing::EaseOutCirc => (1.0 - (x - 1.0).powi(2)).sqrt(),
547 Easing::EaseInOutCirc => {
548 if x < 0.5 {
549 (1.0 - (1.0 - (2.0 * x).powi(2)).sqrt()) / 2.0
550 } else {
551 (1.0 + (1.0 - (-2.0 * x + 2.0).powi(2)).sqrt()) / 2.0
552 }
553 }
554 Easing::EaseInBack => {
555 const C1: f32 = 1.70158;
556 const C3: f32 = C1 + 1.0;
557
558 C3 * x.powi(3) - C1 * x.powi(2)
559 }
560 Easing::EaseOutBack => {
561 const C1: f32 = 1.70158;
562 const C3: f32 = C1 + 1.0;
563
564 1.0 + C3 * (x - 1.0).powi(3) + C1 * (x - 1.0).powi(2)
565 }
566 Easing::EaseInOutBack => {
567 const C1: f32 = 1.70158;
568 const C2: f32 = C1 * 1.525;
569
570 if x < 0.5 {
571 ((2.0 * x).powi(2) * ((C2 + 1.0) * 2.0 * x - C2)) / 2.0
572 } else {
573 ((2.0 * x - 2.0).powi(2) * ((C2 + 1.0) * (x * 2.0 - 2.0) + C2) + 2.0) / 2.0
574 }
575 }
576 Easing::EaseInElastic => {
577 const C4: f32 = (2.0 * PI) / 3.0;
578
579 if x == 0.0 {
580 0.0
581 } else if x == 1.0 {
582 1.0
583 } else {
584 -(2.0_f32.powf(10.0 * x - 10.0)) * f32::sin((x * 10.0 - 10.75) * C4)
585 }
586 }
587 Easing::EaseOutElastic => {
588 const C4: f32 = (2.0 * PI) / 3.0;
589
590 if x == 0.0 {
591 0.0
592 } else if x == 1.0 {
593 1.0
594 } else {
595 2.0_f32.powf(-10.0 * x) * f32::sin((x * 10.0 - 0.75) * C4) + 1.0
596 }
597 }
598 Easing::EaseInOutElastic => {
599 const C5: f32 = (2.0 * PI) / 4.5;
600
601 if x == 0.0 {
602 0.0
603 } else if x == 1.0 {
604 1.0
605 } else if x < 0.5 {
606 -(2.0_f32.powf(20.0 * x - 10.0) * f32::sin((20.0 * x - 11.125) * C5)) / 2.0
607 } else {
608 (2.0_f32.powf(-20.0 * x + 10.0) * f32::sin((20.0 * x - 11.125) * C5)) / 2.0
609 + 1.0
610 }
611 }
612 Easing::EaseInBounce => 1.0 - Self::EaseOutBounce.value(1.0 - x),
613 Easing::EaseOutBounce => {
614 const N1: f32 = 7.5625;
615 const D1: f32 = 2.75;
616
617 if x < 1.0 / D1 {
618 N1 * x.powi(2)
619 } else if x < 2.0 / D1 {
620 N1 * (x - 1.5 / D1).powi(2) + 0.75
621 } else if x < 2.5 / D1 {
622 N1 * (x - 2.25 / D1).powi(2) + 0.9375
623 } else {
624 N1 * (x - 2.625 / D1).powi(2) + 0.984375
625 }
626 }
627 Easing::EaseInOutBounce => {
628 if x < 0.5 {
629 (1.0 - Self::EaseOutBounce.value(1.0 - 2.0 * x)) / 2.0
630 } else {
631 (1.0 + Self::EaseOutBounce.value(2.0 * x - 1.0)) / 2.0
632 }
633 }
634 Easing::Custom(f) => f(x),
635 }
636 }
637}
638
639#[cfg(test)]
640mod tests {
641 use super::*;
642
643 #[test]
644 fn test_repeat_forever() {
645 let mut anim = Animated::new(0.)
646 .duration(1000.)
647 .easing(Easing::Linear)
648 .repeat_forever();
649
650 anim.transition(10.0, 0.0);
651
652 assert_eq!(anim.animate_wrapped(0.0), 0.0);
654 assert_eq!(anim.animate_wrapped(500.0), 5.0);
655 assert_eq!(anim.animate_wrapped(1000.0), 0.0);
656 assert_eq!(anim.animate_wrapped(1500.0), 5.0);
657 assert_eq!(anim.animate_wrapped(2000.0), 0.0);
658 assert_eq!(anim.animate_wrapped(2500.0), 5.0);
659
660 assert!(anim.in_progress(10000.0));
662 }
663
664 fn plot_easing(easing: Easing) {
665 const WIDTH: usize = 80;
666 const HEIGHT: usize = 40;
667 let mut plot = vec![vec![' '; WIDTH]; HEIGHT];
668
669 for x in 0..WIDTH {
670 let t = x as f32 / (WIDTH - 1) as f32;
671 let y = easing.value(t);
672 let y_scaled = ((1.0 - y) * (HEIGHT - 20) as f32).round() as usize + 10;
673 let y_scaled = y_scaled.min(HEIGHT - 1);
674 plot[y_scaled][x] = '*';
675 }
676
677 println!("\nPlot for {:?}:", easing);
678 for row in plot {
679 println!("{}", row.iter().collect::<String>());
680 }
681 println!();
682 }
683
684 #[test]
685 fn visualize_all_easings() {
686 let easings = [
687 Easing::Linear,
688 Easing::EaseIn,
689 Easing::EaseOut,
690 Easing::EaseInOut,
691 Easing::EaseInQuad,
692 Easing::EaseOutQuad,
693 Easing::EaseInOutQuad,
694 Easing::EaseInCubic,
695 Easing::EaseOutCubic,
696 Easing::EaseInOutCubic,
697 Easing::EaseInQuart,
698 Easing::EaseOutQuart,
699 Easing::EaseInOutQuart,
700 Easing::EaseInQuint,
701 Easing::EaseOutQuint,
702 Easing::EaseInOutQuint,
703 Easing::EaseInExpo,
704 Easing::EaseOutExpo,
705 Easing::EaseInOutExpo,
706 Easing::EaseInCirc,
707 Easing::EaseOutCirc,
708 Easing::EaseInOutCirc,
709 Easing::EaseInBack,
710 Easing::EaseOutBack,
711 Easing::EaseInOutBack,
712 Easing::EaseInElastic,
713 Easing::EaseOutElastic,
714 Easing::EaseInOutElastic,
715 Easing::EaseInBounce,
716 Easing::EaseOutBounce,
717 Easing::EaseInOutBounce,
718 ];
719
720 for easing in &easings {
721 plot_easing(*easing);
722 }
723 }
724
725 #[test]
726 fn test_custom_easing() {
727 let custom_ease = Easing::Custom(|x| x.powi(2)); assert_eq!(custom_ease.value(0.0), 0.0);
729 assert_eq!(custom_ease.value(0.5), 0.25);
730 assert_eq!(custom_ease.value(1.0), 1.0);
731 }
732
733 #[test]
734 fn test_linear_progress() {
735 let mut anim = Animated::new(0.).duration(1000.).easing(Easing::Linear);
736 anim.transition(10.0, 0.0);
737
738 assert_eq!(anim.animate_wrapped(0.0), 0.0);
739 assert_eq!(anim.animate_wrapped(500.0), 5.0);
740 assert_eq!(anim.animate_wrapped(1000.0), 10.0);
741 assert_eq!(anim.animate_wrapped(1500.0), 10.0); }
743
744 #[test]
745 fn test_eased_progress_with_easing() {
746 let mut anim = Animated::new(0.).duration(1000.).easing(Easing::EaseIn);
747 anim.transition(10.0, 0.0);
748
749 assert_eq!(anim.animate_wrapped(0.0), 0.0);
750 assert!(anim.animate_wrapped(500.0) < 5.0); assert_eq!(anim.animate_wrapped(1000.0), 10.0);
752 }
753
754 #[test]
755 fn test_in_progress() {
756 let mut anim = Animated::new(0.).duration(1000.).easing(Easing::EaseIn);
757 assert!(!anim.in_progress(0.0));
758
759 anim.transition(10.0, 0.0);
760 assert!(anim.in_progress(0.0));
761 assert!(anim.in_progress(500.0));
762 assert!(!anim.in_progress(1000.0));
763 }
764
765 #[test]
766 fn test_repetitions() {
767 let mut anim = Animated::new(0.)
768 .duration(1000.)
769 .easing(Easing::Linear)
770 .repeat(3);
771 anim.transition(10.0, 0.0);
772
773 assert_eq!(anim.animate_wrapped(1500.0), 5.0); assert_eq!(anim.animate_wrapped(3000.0), 10.0); assert_eq!(anim.animate_wrapped(3500.0), 10.0); assert!(!anim.in_progress(5001.));
777 }
778
779 #[test]
780 fn test_auto_reverse_repetitions() {
781 let mut anim = Animated::new(0.)
782 .duration(1000.)
783 .easing(Easing::Linear)
784 .auto_reverse()
785 .repeat(2);
786 anim.transition(10.0, 0.0);
787
788 assert_eq!(anim.animate_wrapped(500.0), 5.0); assert_eq!(anim.animate_wrapped(1500.0), 5.0); assert_eq!(anim.animate_wrapped(2500.0), 5.0); assert_eq!(anim.animate_wrapped(3500.0), 5.0); assert_eq!(anim.animate_wrapped(4000.0), 0.0); assert!(!anim.in_progress(5000.));
794 }
795
796 #[test]
797 fn test_auto_reverse_repetitions_bool() {
798 let anim = Animated::new(false)
799 .duration(1000.)
800 .auto_start(true, 0.)
801 .repeat(2)
802 .auto_reverse();
803 let map = |b| if b { 1. } else { 0. };
804 anim.animate(map, 500.);
805 assert_eq!(anim.animate(map, 500.0), 0.5); assert_eq!(anim.animate(map, 1500.0), 0.5); assert_eq!(anim.animate(map, 2500.0), 0.5); assert_eq!(anim.animate(map, 3500.0), 0.5); assert!(anim.in_progress(3500.));
810 assert_eq!(anim.animate(map, 4000.0), 0.0); assert!(!anim.in_progress(5000.01));
812 }
813
814 #[test]
815 fn test_delay() {
816 let mut anim = Animated::new(0.)
817 .duration(1000.)
818 .easing(Easing::Linear)
819 .delay(500.);
820 anim.transition(10.0, 0.0);
821
822 assert_eq!(anim.animate_wrapped(250.0), 0.0); assert_eq!(anim.animate_wrapped(750.0), 2.5); assert_eq!(anim.animate_wrapped(1500.0), 10.0); }
826
827 #[test]
828 fn test_interruption() {
829 let mut anim = Animated::new(0.).duration(1000.).easing(Easing::Linear);
830 anim.transition(10.0, 0.0);
831
832 assert_eq!(anim.animate_wrapped(500.0), 5.0);
833
834 anim.transition(20.0, 500.0); assert_eq!(anim.animate_wrapped(1000.0), 12.5); assert_eq!(anim.animate_wrapped(1500.0), 20.0); }
838
839 #[test]
840 fn test_instant_animation() {
841 let mut anim = Animated::new(0.).duration(0.).easing(Easing::Linear);
842 assert_eq!(anim.animate_wrapped(0.0), 0.0);
843 anim.transition(10.0, 0.0);
846 assert_eq!(anim.animate_wrapped(0.0), 10.0);
847 }
848
849 #[test]
850 fn test_progression() {
851 let mut anim = Animated::new(0.).duration(1.).easing(Easing::Linear);
852 anim.transition(10.0, 0.5);
855 assert_eq!(anim.value, 10.);
856 assert_eq!(anim.animate_wrapped(1.0), 5.0);
857 assert_eq!(anim.animate_wrapped(1.5), 10.0);
858
859 anim.transition(0.0, 1.5);
861 assert_eq!(anim.value, 0.);
862 assert_eq!(anim.linear_progress(2.5), 0.0);
863
864 anim.transition(10.0, 3.);
866 assert_eq!(anim.value, 10.);
867 assert!(approximately_equal(anim.animate_wrapped(3.), 0.0));
868 assert!(approximately_equal(anim.animate_wrapped(3.2), 2.0));
869 assert!(approximately_equal(anim.animate_wrapped(3.8), 8.0));
870 assert!(approximately_equal(anim.animate_wrapped(4.0), 10.0));
871 }
872
873 #[test]
874 fn test_progression_negative() {
875 let mut anim = Animated::new(0.).duration(1.).easing(Easing::EaseInOut);
876
877 anim.transition(-10.0, 0.0);
878 assert_eq!(anim.animate_wrapped(0.5), -5.0);
879 assert_eq!(anim.animate_wrapped(1.0), -10.0);
880
881 assert!(anim.eased_progress(0.25) > anim.linear_progress(0.25));
882 assert!(anim.eased_progress(0.5) == anim.linear_progress(0.5));
883 assert!(anim.eased_progress(0.75) < anim.linear_progress(0.75));
884
885 anim.transition(0.0, 1.0);
886 assert_eq!(anim.animate_wrapped(1.5), -5.0);
887 assert_eq!(anim.animate_wrapped(2.0), 0.0);
888 }
889
890 #[test]
891 fn test_multiple_interrupts_start_forward() {
892 let mut anim = Animated::new(0.).duration(1.).easing(Easing::EaseInOut);
893 anim.transition(1.0, 0.);
894 assert!(anim.in_progress(0.5));
895 let progress_at_interrupt = anim.eased_progress(0.5);
896 assert_eq!(progress_at_interrupt, Easing::EaseInOut.value(0.5));
897 anim.transition(0.0, 0.5);
898 assert_eq!(anim.eased_progress(0.5), progress_at_interrupt);
899 assert!(anim.in_progress(0.7));
900 anim.transition(1.0, 0.7);
901 assert!(anim.in_progress(0.9));
902 }
903
904 #[test]
905 fn test_asymmetric() {
906 let mut anim = Animated::new(0.)
907 .duration(1000.)
908 .easing(Easing::Linear)
909 .asymmetric_duration(2000.)
910 .asymmetric_easing(Easing::EaseInOut);
911
912 anim.transition(10.0, 0.0);
913 assert_eq!(anim.animate_wrapped(500.0), 5.0); assert_eq!(anim.animate_wrapped(1000.0), 10.); anim.transition(0.0, 1000.0);
917 assert!(anim.animate_wrapped(1500.0) > 7.5); assert_eq!(anim.animate_wrapped(2000.0), 5.0); assert!(anim.animate_wrapped(2500.0) < 2.5); assert_eq!(anim.animate_wrapped(3000.0), 0.0); anim.transition(10.0, 3000.0);
923 assert_eq!(anim.animate_wrapped(3250.0), 2.5); assert_eq!(anim.animate_wrapped(3500.0), 5.0); assert_eq!(anim.animate_wrapped(3750.0), 7.5); assert_eq!(anim.animate_wrapped(4000.0), 10.0); }
928
929 #[test]
930 fn test_asymmetric_auto_reversal() {
931 let mut anim = Animated::new(0.)
932 .duration(1000.)
933 .easing(Easing::Linear)
934 .asymmetric_duration(2000.)
935 .auto_reverse()
936 .repeat(1);
937
938 anim.transition(10.0, 0.0);
939
940 assert_eq!(anim.animate_wrapped(500.0), 5.0); assert_eq!(anim.animate_wrapped(1000.0), 10.); assert_eq!(anim.animate_wrapped(1500.0), 7.5); assert_eq!(anim.animate_wrapped(2000.0), 5.0); assert_eq!(anim.animate_wrapped(2500.0), 2.5); assert_eq!(anim.animate_wrapped(3000.0), 0.0); assert_eq!(anim.animate_wrapped(3250.0), 2.5); assert_eq!(anim.animate_wrapped(3500.0), 5.0); assert_eq!(anim.animate_wrapped(3750.0), 7.5); assert_eq!(anim.animate_wrapped(4000.0), 10.0); assert!(anim.eased_progress(3250.0) == anim.linear_progress(3250.0)); assert!(anim.eased_progress(3500.0) == anim.linear_progress(3500.0)); assert!(anim.eased_progress(3750.0) == anim.linear_progress(3750.0)); }
960
961 #[test]
962 fn test_auto_reversal() {
963 let mut anim = Animated::new(0.)
964 .duration(1000.)
965 .auto_reverse()
966 .repeat(1)
967 .easing(Easing::Linear);
968
969 anim.transition(10.0, 0.0);
970
971 assert_eq!(anim.animate_wrapped(0.0), 0.0);
972
973 assert_eq!(anim.animate_wrapped(250.0), 2.5); assert_eq!(anim.animate_wrapped(500.0), 5.0); assert_eq!(anim.animate_wrapped(750.0), 7.5); assert_eq!(anim.animate_wrapped(1000.0), 10.0); assert_eq!(anim.animate_wrapped(1250.0), 7.5); assert_eq!(anim.animate_wrapped(1500.0), 5.0); assert_eq!(anim.animate_wrapped(1750.0), 2.5); assert_eq!(anim.animate_wrapped(2000.0), 0.0); assert_eq!(anim.animate_wrapped(2250.0), 2.5); assert_eq!(anim.animate_wrapped(2500.0), 5.0); assert_eq!(anim.animate_wrapped(2750.0), 7.5); assert_eq!(anim.animate_wrapped(3000.0), 10.0); }
991
992 #[test]
993 fn test_negative_values() {
994 let mut anim = Animated::new(0.).duration(1000.).easing(Easing::Linear);
995 anim.transition(-10.0, 0.0);
996 assert_eq!(anim.animate_wrapped(0.0), 0.0);
997 assert_eq!(anim.animate_wrapped(500.0), -5.0);
998 assert_eq!(anim.animate_wrapped(1000.0), -10.0);
999 anim.transition(-5.0, 1000.0);
1000 assert_eq!(anim.animate_wrapped(1000.0), -10.0);
1001 assert_eq!(anim.animate_wrapped(1500.0), -7.5);
1002 assert_eq!(anim.animate_wrapped(2000.0), -5.0);
1003 }
1004
1005 #[test]
1006 fn test_negative_to_positive_transition() {
1007 let mut anim = Animated::new(-5.).duration(1000.).easing(Easing::Linear);
1008 anim.transition(5.0, 0.0);
1009 assert_eq!(anim.animate_wrapped(0.0), -5.0);
1010 assert_eq!(anim.animate_wrapped(500.0), 0.0);
1011 assert_eq!(anim.animate_wrapped(1000.0), 5.0);
1012 }
1013
1014 #[test]
1015 fn test_interruption_with_negative_values() {
1016 let mut anim = Animated::new(0.).duration(1000.).easing(Easing::Linear);
1017 anim.transition(-10.0, 0.0);
1018 assert_eq!(anim.animate_wrapped(250.0), -2.5);
1019 anim.transition(5.0, 250.0); assert_eq!(anim.animate_wrapped(750.0), 1.25); assert_eq!(anim.animate_wrapped(1250.0), 5.0); }
1023
1024 #[test]
1025 fn test_multiple_interruptions() {
1026 let mut anim = Animated::new(0.).duration(1000.).easing(Easing::Linear);
1027
1028 anim.transition(10.0, 0.0);
1029 assert_eq!(anim.animate_wrapped(500.0), 5.);
1030
1031 anim.transition(15.0, 500.0); assert_eq!(anim.animate_wrapped(1000.0), 10.); anim.transition(0.0, 1000.0); assert!(approximately_equal(anim.animate_wrapped(1500.0), 5.)); assert_eq!(anim.animate_wrapped(2000.0), 0.0); }
1038
1039 #[test]
1040 fn test_interrupt_unchanged_destination() {
1041 let mut anim_a = Animated::new(0.).duration(1000.).easing(Easing::Linear);
1044 let mut anim_b = Animated::new(0.).duration(1000.).easing(Easing::Linear);
1045 anim_a.transition(10., 0.);
1046 anim_b.transition(10., 0.);
1047
1048 anim_a.transition(10., 250.);
1049 assert_eq!(anim_a.linear_progress(250.), anim_b.linear_progress(250.));
1050 assert_eq!(anim_a.linear_progress(500.), anim_b.linear_progress(500.));
1051 assert_eq!(anim_a.linear_progress(750.), anim_b.linear_progress(750.));
1052 assert_eq!(anim_a.linear_progress(1000.), anim_b.linear_progress(1000.));
1053 }
1054
1055 #[test]
1056 fn test_interruption_with_direction_change() {
1057 let mut anim = Animated::new(0.).duration(1000.).easing(Easing::Linear);
1058 anim.transition(10.0, 0.0);
1059 assert_eq!(anim.animate_wrapped(500.0), 5.0);
1060 anim.transition(-5.0, 500.0); assert!(approximately_equal(anim.animate_wrapped(1000.0), 0.0)); assert_eq!(anim.animate_wrapped(1500.0), -5.0); }
1064
1065 #[test]
1066 fn test_zero_duration_transition() {
1067 let mut anim = Animated::new(0.).duration(0.).easing(Easing::Linear);
1068 anim.transition(10.0, 0.0);
1069 assert_eq!(anim.animate_wrapped(0.0), 10.0); assert!(!anim.in_progress(0.0)); }
1072
1073 #[test]
1074 fn test_interruption_at_completion() {
1075 let mut anim = Animated::new(0.).duration(1000.).easing(Easing::Linear);
1076 anim.transition(10.0, 0.0);
1077 assert_eq!(anim.animate_wrapped(1000.0), 10.0); anim.transition(20.0, 1000.0); assert_eq!(anim.animate_wrapped(1500.0), 15.0); assert_eq!(anim.animate_wrapped(2000.0), 20.0); }
1082
1083 #[test]
1084 fn test_animate() {
1085 let mut anim = Animated::new(0.0f32)
1086 .duration(1000.0)
1087 .easing(Easing::Linear);
1088 anim.transition(10.0, 0.0);
1089
1090 let result = anim.animate_wrapped(500.0);
1091 assert_eq!(result, 5.0);
1092
1093 let result = anim.animate(|v| v * 2.0, 750.0);
1094 assert_eq!(result, 15.0);
1095 }
1096
1097 #[test]
1098 fn test_animate_if_eq() {
1099 let mut anim = Animated::new(0.0f32)
1100 .duration(1000.0)
1101 .easing(Easing::Linear);
1102 anim.transition(10.0, 0.0);
1103
1104 let result = anim.animate_if_eq(10.0, 100.0, 0.0, 500.0);
1105 assert_eq!(result, 50.0);
1106
1107 let result = anim.animate_if_eq(5.0, 100.0, 0.0, 500.0);
1108 assert_eq!(result, 0.0);
1109 }
1110
1111 #[test]
1112 fn test_animate_bool() {
1113 let mut anim = Animated::new(false).duration(1000.0).easing(Easing::Linear);
1114 anim.transition(true, 0.0);
1115
1116 let result = anim.animate_bool(0.0, 10.0, 500.0);
1117 assert_eq!(result, 5.0);
1118
1119 let result = anim.animate_bool(0.0, 10.0, 1000.0);
1120 assert_eq!(result, 10.0);
1121 }
1122
1123 #[test]
1124 fn test_animate_with_interruption() {
1125 let mut anim = Animated::new(0.0f32)
1126 .duration(1000.0)
1127 .easing(Easing::Linear);
1128 anim.transition(10.0, 0.0);
1129
1130 let result = anim.animate_wrapped(500.0);
1131 assert_eq!(result, 5.0);
1132
1133 anim.transition(20.0, 500.0);
1134 let result = anim.animate(|v| v, 1000.0);
1135 assert_eq!(result, 12.5);
1136
1137 let result = anim.animate_wrapped(1500.0);
1138 assert_eq!(result, 20.0);
1139 }
1140
1141 #[test]
1142 fn test_animate_with_custom_easing() {
1143 let custom_ease = Easing::Custom(|x| x.powi(2)); let mut anim = Animated::new(0.0f32).duration(1000.0).easing(custom_ease);
1145 anim.transition(10.0, 0.0);
1146
1147 let result = anim.animate(|v| v, 500.0);
1148 assert_eq!(result, 2.5); let result = anim.animate(|v| v, 750.0);
1151 assert_eq!(result, 5.625); }
1153
1154 #[test]
1155 fn test_no_change_after_completion() {
1156 let anim = Animated::new(false)
1157 .duration(400.)
1158 .auto_start(true, 0.)
1159 .repeat(2)
1160 .auto_reverse();
1161 assert_eq!(anim.animate_bool(0., 10., 800.), 0.);
1163 assert_eq!(anim.animate_bool(0., 10., 1000.), 5.);
1164 assert_eq!(anim.animate_bool(0., 10., 1200.), 10.);
1165 assert_eq!(anim.animate_bool(0., 10., 1400.), 5.);
1166 assert_eq!(anim.animate_bool(0., 10., 1600.), 0.);
1167 assert_eq!(anim.animate_bool(0., 10., 1800.), 5.);
1168
1169 assert_eq!(anim.animate_bool(0., 10., 2000.), 10.);
1171
1172 assert_eq!(anim.animate_bool(0., 10., 2200.), 10.);
1174 assert_eq!(anim.animate_bool(0., 10., 2400.), 10.);
1175 assert_eq!(anim.animate_bool(0., 10., 2600.), 10.);
1176 assert_eq!(anim.animate_bool(0., 10., 2800.), 10.);
1177 assert_eq!(anim.animate_bool(0., 10., 3000.), 10.);
1178 }
1179
1180 fn approximately_equal(a: f32, b: f32) -> bool {
1181 let close = f32::abs(a - b) < 1e-5;
1182 if !close {
1183 dbg!(a, b);
1184 }
1185 close
1186 }
1187}