1use std::f64::consts::PI;
8
9pub fn lerp(a: f64, b: f64, t: f64) -> f64 {
14 a + (b - a) * t
15}
16
17pub fn ease_linear(t: f64) -> f64 {
19 clamp01(t)
20}
21
22pub fn ease_in_quad(t: f64) -> f64 {
24 let t = clamp01(t);
25 t * t
26}
27
28pub fn ease_out_quad(t: f64) -> f64 {
30 let t = clamp01(t);
31 1.0 - (1.0 - t) * (1.0 - t)
32}
33
34pub fn ease_in_out_quad(t: f64) -> f64 {
36 let t = clamp01(t);
37 if t < 0.5 {
38 2.0 * t * t
39 } else {
40 1.0 - (-2.0 * t + 2.0).powi(2) / 2.0
41 }
42}
43
44pub fn ease_in_cubic(t: f64) -> f64 {
46 let t = clamp01(t);
47 t * t * t
48}
49
50pub fn ease_out_cubic(t: f64) -> f64 {
52 let t = clamp01(t);
53 1.0 - (1.0 - t).powi(3)
54}
55
56pub fn ease_in_out_cubic(t: f64) -> f64 {
58 let t = clamp01(t);
59 if t < 0.5 {
60 4.0 * t * t * t
61 } else {
62 1.0 - (-2.0 * t + 2.0).powi(3) / 2.0
63 }
64}
65
66pub fn ease_out_elastic(t: f64) -> f64 {
68 let t = clamp01(t);
69 if t == 0.0 {
70 0.0
71 } else if t == 1.0 {
72 1.0
73 } else {
74 let c4 = (2.0 * PI) / 3.0;
75 2f64.powf(-10.0 * t) * ((t * 10.0 - 0.75) * c4).sin() + 1.0
76 }
77}
78
79pub fn ease_out_bounce(t: f64) -> f64 {
81 let t = clamp01(t);
82 let n1 = 7.5625;
83 let d1 = 2.75;
84
85 if t < 1.0 / d1 {
86 n1 * t * t
87 } else if t < 2.0 / d1 {
88 let t = t - 1.5 / d1;
89 n1 * t * t + 0.75
90 } else if t < 2.5 / d1 {
91 let t = t - 2.25 / d1;
92 n1 * t * t + 0.9375
93 } else {
94 let t = t - 2.625 / d1;
95 n1 * t * t + 0.984_375
96 }
97}
98
99pub struct Tween {
119 from: f64,
120 to: f64,
121 duration_ticks: u64,
122 start_tick: u64,
123 easing: fn(f64) -> f64,
124 done: bool,
125 on_complete: Option<Box<dyn FnMut()>>,
126}
127
128impl Tween {
129 pub fn new(from: f64, to: f64, duration_ticks: u64) -> Self {
135 Self {
136 from,
137 to,
138 duration_ticks,
139 start_tick: 0,
140 easing: ease_linear,
141 done: false,
142 on_complete: None,
143 }
144 }
145
146 pub fn easing(mut self, f: fn(f64) -> f64) -> Self {
151 self.easing = f;
152 self
153 }
154
155 pub fn on_complete(mut self, f: impl FnMut() + 'static) -> Self {
157 self.on_complete = Some(Box::new(f));
158 self
159 }
160
161 pub fn value(&mut self, tick: u64) -> f64 {
166 let was_done = self.done;
167 if self.done {
168 return self.to;
169 }
170
171 if self.duration_ticks == 0 {
172 self.done = true;
173 if !was_done && self.done {
174 if let Some(cb) = &mut self.on_complete {
175 cb();
176 }
177 }
178 return self.to;
179 }
180
181 let elapsed = tick.wrapping_sub(self.start_tick);
182 if elapsed >= self.duration_ticks {
183 self.done = true;
184 if !was_done && self.done {
185 if let Some(cb) = &mut self.on_complete {
186 cb();
187 }
188 }
189 return self.to;
190 }
191
192 let progress = elapsed as f64 / self.duration_ticks as f64;
193 let eased = (self.easing)(clamp01(progress));
194 lerp(self.from, self.to, eased)
195 }
196
197 pub fn is_done(&self) -> bool {
199 self.done
200 }
201
202 pub fn reset(&mut self, tick: u64) {
204 self.start_tick = tick;
205 self.done = false;
206 }
207}
208
209#[derive(Debug, Clone, Copy, PartialEq, Eq)]
211pub enum LoopMode {
212 Once,
214 Repeat,
216 PingPong,
218}
219
220#[derive(Clone, Copy)]
221struct KeyframeStop {
222 position: f64,
223 value: f64,
224}
225
226pub struct Keyframes {
252 duration_ticks: u64,
253 start_tick: u64,
254 stops: Vec<KeyframeStop>,
255 default_easing: fn(f64) -> f64,
256 segment_easing: Vec<fn(f64) -> f64>,
257 loop_mode: LoopMode,
258 done: bool,
259 on_complete: Option<Box<dyn FnMut()>>,
260}
261
262impl Keyframes {
263 pub fn new(duration_ticks: u64) -> Self {
269 Self {
270 duration_ticks,
271 start_tick: 0,
272 stops: Vec::new(),
273 default_easing: ease_linear,
274 segment_easing: Vec::new(),
275 loop_mode: LoopMode::Once,
276 done: false,
277 on_complete: None,
278 }
279 }
280
281 pub fn stop(mut self, position: f64, value: f64) -> Self {
285 self.stops.push(KeyframeStop {
286 position: clamp01(position),
287 value,
288 });
289 if self.stops.len() >= 2 {
290 self.segment_easing.push(self.default_easing);
291 }
292 self.stops.sort_by(|a, b| a.position.total_cmp(&b.position));
293 self
294 }
295
296 pub fn easing(mut self, f: fn(f64) -> f64) -> Self {
301 self.default_easing = f;
302 self.segment_easing.fill(f);
303 self
304 }
305
306 pub fn segment_easing(mut self, segment_index: usize, f: fn(f64) -> f64) -> Self {
311 if let Some(slot) = self.segment_easing.get_mut(segment_index) {
312 *slot = f;
313 }
314 self
315 }
316
317 pub fn loop_mode(mut self, mode: LoopMode) -> Self {
319 self.loop_mode = mode;
320 self
321 }
322
323 pub fn on_complete(mut self, f: impl FnMut() + 'static) -> Self {
325 self.on_complete = Some(Box::new(f));
326 self
327 }
328
329 pub fn value(&mut self, tick: u64) -> f64 {
331 let was_done = self.done;
332 if self.stops.is_empty() {
333 self.done = true;
334 if !was_done && self.done {
335 if let Some(cb) = &mut self.on_complete {
336 cb();
337 }
338 }
339 return 0.0;
340 }
341 if self.stops.len() == 1 {
342 self.done = true;
343 if !was_done && self.done {
344 if let Some(cb) = &mut self.on_complete {
345 cb();
346 }
347 }
348 return self.stops[0].value;
349 }
350
351 let stops = &self.stops;
352
353 let end_value = stops.last().map_or(0.0, |s| s.value);
354 let loop_tick = match map_loop_tick(
355 tick,
356 self.start_tick,
357 self.duration_ticks,
358 self.loop_mode,
359 &mut self.done,
360 ) {
361 Some(v) => v,
362 None => {
363 if !was_done && self.done {
364 if let Some(cb) = &mut self.on_complete {
365 cb();
366 }
367 }
368 return end_value;
369 }
370 };
371
372 let progress = loop_tick as f64 / self.duration_ticks as f64;
373
374 if progress <= stops[0].position {
375 return stops[0].value;
376 }
377 if progress >= 1.0 {
378 return end_value;
379 }
380
381 for i in 0..(stops.len() - 1) {
382 let a = stops[i];
383 let b = stops[i + 1];
384 if progress <= b.position {
385 let span = b.position - a.position;
386 if span <= f64::EPSILON {
387 return b.value;
388 }
389 let local = clamp01((progress - a.position) / span);
390 let easing = self
391 .segment_easing
392 .get(i)
393 .copied()
394 .unwrap_or(self.default_easing);
395 let eased = easing(local);
396 return lerp(a.value, b.value, eased);
397 }
398 }
399
400 end_value
401 }
402
403 pub fn is_done(&self) -> bool {
405 self.done
406 }
407
408 pub fn reset(&mut self, tick: u64) {
410 self.start_tick = tick;
411 self.done = false;
412 }
413}
414
415#[derive(Clone, Copy)]
416struct SequenceSegment {
417 from: f64,
418 to: f64,
419 duration_ticks: u64,
420 easing: fn(f64) -> f64,
421}
422
423pub struct Sequence {
442 segments: Vec<SequenceSegment>,
443 loop_mode: LoopMode,
444 start_tick: u64,
445 done: bool,
446 on_complete: Option<Box<dyn FnMut()>>,
447}
448
449impl Default for Sequence {
450 fn default() -> Self {
451 Self::new()
452 }
453}
454
455impl Sequence {
456 pub fn new() -> Self {
461 Self {
462 segments: Vec::new(),
463 loop_mode: LoopMode::Once,
464 start_tick: 0,
465 done: false,
466 on_complete: None,
467 }
468 }
469
470 pub fn then(mut self, from: f64, to: f64, duration_ticks: u64, easing: fn(f64) -> f64) -> Self {
472 self.segments.push(SequenceSegment {
473 from,
474 to,
475 duration_ticks,
476 easing,
477 });
478 self
479 }
480
481 pub fn loop_mode(mut self, mode: LoopMode) -> Self {
483 self.loop_mode = mode;
484 self
485 }
486
487 pub fn on_complete(mut self, f: impl FnMut() + 'static) -> Self {
489 self.on_complete = Some(Box::new(f));
490 self
491 }
492
493 pub fn value(&mut self, tick: u64) -> f64 {
495 let was_done = self.done;
496 if self.segments.is_empty() {
497 self.done = true;
498 if !was_done && self.done {
499 if let Some(cb) = &mut self.on_complete {
500 cb();
501 }
502 }
503 return 0.0;
504 }
505
506 let total_duration = self
507 .segments
508 .iter()
509 .fold(0_u64, |acc, s| acc.saturating_add(s.duration_ticks));
510 let end_value = self.segments.last().map_or(0.0, |s| s.to);
511
512 let loop_tick = match map_loop_tick(
513 tick,
514 self.start_tick,
515 total_duration,
516 self.loop_mode,
517 &mut self.done,
518 ) {
519 Some(v) => v,
520 None => {
521 if !was_done && self.done {
522 if let Some(cb) = &mut self.on_complete {
523 cb();
524 }
525 }
526 return end_value;
527 }
528 };
529
530 let mut remaining = loop_tick;
531 for segment in &self.segments {
532 if segment.duration_ticks == 0 {
533 continue;
534 }
535 if remaining < segment.duration_ticks {
536 let progress = remaining as f64 / segment.duration_ticks as f64;
537 let eased = (segment.easing)(clamp01(progress));
538 return lerp(segment.from, segment.to, eased);
539 }
540 remaining -= segment.duration_ticks;
541 }
542
543 end_value
544 }
545
546 pub fn is_done(&self) -> bool {
548 self.done
549 }
550
551 pub fn reset(&mut self, tick: u64) {
553 self.start_tick = tick;
554 self.done = false;
555 }
556}
557
558pub struct Stagger {
582 from: f64,
583 to: f64,
584 duration_ticks: u64,
585 start_tick: u64,
586 delay_ticks: u64,
587 easing: fn(f64) -> f64,
588 loop_mode: LoopMode,
589 item_count: usize,
590 done: bool,
591 on_complete: Option<Box<dyn FnMut()>>,
592}
593
594impl Stagger {
595 pub fn new(from: f64, to: f64, duration_ticks: u64) -> Self {
599 Self {
600 from,
601 to,
602 duration_ticks,
603 start_tick: 0,
604 delay_ticks: 0,
605 easing: ease_linear,
606 loop_mode: LoopMode::Once,
607 item_count: 0,
608 done: false,
609 on_complete: None,
610 }
611 }
612
613 pub fn easing(mut self, f: fn(f64) -> f64) -> Self {
615 self.easing = f;
616 self
617 }
618
619 pub fn delay(mut self, ticks: u64) -> Self {
621 self.delay_ticks = ticks;
622 self
623 }
624
625 pub fn loop_mode(mut self, mode: LoopMode) -> Self {
628 self.loop_mode = mode;
629 self
630 }
631
632 pub fn on_complete(mut self, f: impl FnMut() + 'static) -> Self {
634 self.on_complete = Some(Box::new(f));
635 self
636 }
637
638 pub fn items(mut self, count: usize) -> Self {
644 self.item_count = count;
645 self
646 }
647
648 pub fn value(&mut self, tick: u64, item_index: usize) -> f64 {
650 let was_done = self.done;
651 if item_index >= self.item_count {
652 self.item_count = item_index + 1;
653 }
654
655 let total_cycle = self.total_cycle_ticks();
656
657 let effective_tick = if self.loop_mode == LoopMode::Once {
658 tick
659 } else {
660 let elapsed = tick.wrapping_sub(self.start_tick);
661 let mapped = match self.loop_mode {
662 LoopMode::Repeat => {
663 if total_cycle == 0 {
664 0
665 } else {
666 elapsed % total_cycle
667 }
668 }
669 LoopMode::PingPong => {
670 if total_cycle == 0 {
671 0
672 } else {
673 let full = total_cycle.saturating_mul(2);
674 let phase = elapsed % full;
675 if phase < total_cycle {
676 phase
677 } else {
678 full - phase
679 }
680 }
681 }
682 LoopMode::Once => unreachable!(),
683 };
684 self.start_tick.wrapping_add(mapped)
685 };
686
687 let delay = self.delay_ticks.wrapping_mul(item_index as u64);
688 let item_start = self.start_tick.wrapping_add(delay);
689
690 if effective_tick < item_start {
691 self.done = false;
692 return self.from;
693 }
694
695 if self.duration_ticks == 0 {
696 self.done = true;
697 if !was_done && self.done {
698 if let Some(cb) = &mut self.on_complete {
699 cb();
700 }
701 }
702 return self.to;
703 }
704
705 let elapsed = effective_tick - item_start;
706 if elapsed >= self.duration_ticks {
707 self.done = true;
708 if !was_done && self.done {
709 if let Some(cb) = &mut self.on_complete {
710 cb();
711 }
712 }
713 return self.to;
714 }
715
716 self.done = false;
717 let progress = elapsed as f64 / self.duration_ticks as f64;
718 let eased = (self.easing)(clamp01(progress));
719 lerp(self.from, self.to, eased)
720 }
721
722 fn total_cycle_ticks(&self) -> u64 {
723 let max_delay = self
724 .delay_ticks
725 .wrapping_mul(self.item_count.saturating_sub(1) as u64);
726 self.duration_ticks.saturating_add(max_delay)
727 }
728
729 pub fn is_done(&self) -> bool {
731 self.done
732 }
733
734 pub fn reset(&mut self, tick: u64) {
736 self.start_tick = tick;
737 self.done = false;
738 }
739}
740
741fn map_loop_tick(
742 tick: u64,
743 start_tick: u64,
744 duration_ticks: u64,
745 loop_mode: LoopMode,
746 done: &mut bool,
747) -> Option<u64> {
748 if duration_ticks == 0 {
749 *done = true;
750 return None;
751 }
752
753 let elapsed = tick.wrapping_sub(start_tick);
754 match loop_mode {
755 LoopMode::Once => {
756 if elapsed >= duration_ticks {
757 *done = true;
758 None
759 } else {
760 *done = false;
761 Some(elapsed)
762 }
763 }
764 LoopMode::Repeat => {
765 *done = false;
766 Some(elapsed % duration_ticks)
767 }
768 LoopMode::PingPong => {
769 *done = false;
770 let cycle = duration_ticks.saturating_mul(2);
771 if cycle == 0 {
772 return Some(0);
773 }
774 let phase = elapsed % cycle;
775 if phase < duration_ticks {
776 Some(phase)
777 } else {
778 Some(cycle - phase)
779 }
780 }
781 }
782}
783
784pub struct Spring {
810 value: f64,
811 target: f64,
812 velocity: f64,
813 stiffness: f64,
814 damping: f64,
815 settled: bool,
816 on_settle: Option<Box<dyn FnMut()>>,
817}
818
819impl Spring {
820 pub fn new(initial: f64, stiffness: f64, damping: f64) -> Self {
825 Self {
826 value: initial,
827 target: initial,
828 velocity: 0.0,
829 stiffness,
830 damping,
831 settled: true,
832 on_settle: None,
833 }
834 }
835
836 pub fn on_settle(mut self, f: impl FnMut() + 'static) -> Self {
838 self.on_settle = Some(Box::new(f));
839 self
840 }
841
842 pub fn set_target(&mut self, target: f64) {
844 self.target = target;
845 self.settled = self.is_settled();
846 }
847
848 pub fn tick(&mut self) {
852 let displacement = self.target - self.value;
853 let spring_force = displacement * self.stiffness;
854 self.velocity = (self.velocity + spring_force) * self.damping;
855 self.value += self.velocity;
856
857 let is_settled = self.is_settled();
858 if !self.settled && is_settled {
859 self.settled = true;
860 if let Some(cb) = &mut self.on_settle {
861 cb();
862 }
863 }
864 }
865
866 pub fn value(&self) -> f64 {
868 self.value
869 }
870
871 pub fn is_settled(&self) -> bool {
876 (self.target - self.value).abs() < 0.01 && self.velocity.abs() < 0.01
877 }
878}
879
880fn clamp01(t: f64) -> f64 {
881 t.clamp(0.0, 1.0)
882}
883
884#[cfg(test)]
885mod tests {
886 use super::*;
887 use std::cell::Cell;
888 use std::rc::Rc;
889
890 fn assert_endpoints(f: fn(f64) -> f64) {
891 assert_eq!(f(0.0), 0.0);
892 assert_eq!(f(1.0), 1.0);
893 }
894
895 #[test]
896 fn easing_functions_have_expected_endpoints() {
897 let easing_functions: [fn(f64) -> f64; 9] = [
898 ease_linear,
899 ease_in_quad,
900 ease_out_quad,
901 ease_in_out_quad,
902 ease_in_cubic,
903 ease_out_cubic,
904 ease_in_out_cubic,
905 ease_out_elastic,
906 ease_out_bounce,
907 ];
908
909 for easing in easing_functions {
910 assert_endpoints(easing);
911 }
912 }
913
914 #[test]
915 fn tween_returns_start_middle_end_values() {
916 let mut tween = Tween::new(0.0, 10.0, 10);
917 tween.reset(100);
918
919 assert_eq!(tween.value(100), 0.0);
920 assert_eq!(tween.value(105), 5.0);
921 assert_eq!(tween.value(110), 10.0);
922 assert!(tween.is_done());
923 }
924
925 #[test]
926 fn tween_reset_restarts_animation() {
927 let mut tween = Tween::new(0.0, 1.0, 10);
928 tween.reset(0);
929 let _ = tween.value(10);
930 assert!(tween.is_done());
931
932 tween.reset(20);
933 assert!(!tween.is_done());
934 assert_eq!(tween.value(20), 0.0);
935 assert_eq!(tween.value(30), 1.0);
936 assert!(tween.is_done());
937 }
938
939 #[test]
940 fn tween_on_complete_fires_once() {
941 let count = Rc::new(Cell::new(0));
942 let callback_count = Rc::clone(&count);
943 let mut tween = Tween::new(0.0, 10.0, 10).on_complete(move || {
944 callback_count.set(callback_count.get() + 1);
945 });
946
947 tween.reset(0);
948 assert_eq!(count.get(), 0);
949
950 assert_eq!(tween.value(5), 5.0);
951 assert_eq!(count.get(), 0);
952
953 assert_eq!(tween.value(10), 10.0);
954 assert_eq!(count.get(), 1);
955
956 assert_eq!(tween.value(11), 10.0);
957 assert_eq!(count.get(), 1);
958 }
959
960 #[test]
961 fn spring_settles_to_target() {
962 let mut spring = Spring::new(0.0, 0.2, 0.85);
963 spring.set_target(10.0);
964
965 for _ in 0..300 {
966 spring.tick();
967 if spring.is_settled() {
968 break;
969 }
970 }
971
972 assert!(spring.is_settled());
973 assert!((spring.value() - 10.0).abs() < 0.01);
974 }
975
976 #[test]
977 fn spring_on_settle_fires_once() {
978 let count = Rc::new(Cell::new(0));
979 let callback_count = Rc::clone(&count);
980 let mut spring = Spring::new(0.0, 0.2, 0.85).on_settle(move || {
981 callback_count.set(callback_count.get() + 1);
982 });
983 spring.set_target(10.0);
984
985 for _ in 0..500 {
986 spring.tick();
987 if spring.is_settled() {
988 break;
989 }
990 }
991
992 assert!(spring.is_settled());
993 assert_eq!(count.get(), 1);
994
995 for _ in 0..50 {
996 spring.tick();
997 }
998
999 assert_eq!(count.get(), 1);
1000 }
1001
1002 #[test]
1003 fn lerp_interpolates_values() {
1004 assert_eq!(lerp(0.0, 10.0, 0.0), 0.0);
1005 assert_eq!(lerp(0.0, 10.0, 0.5), 5.0);
1006 assert_eq!(lerp(0.0, 10.0, 1.0), 10.0);
1007 }
1008
1009 #[test]
1010 fn keyframes_interpolates_across_multiple_stops() {
1011 let mut keyframes = Keyframes::new(100)
1012 .stop(0.0, 0.0)
1013 .stop(0.3, 100.0)
1014 .stop(0.7, 50.0)
1015 .stop(1.0, 80.0)
1016 .easing(ease_linear);
1017
1018 keyframes.reset(0);
1019 assert_eq!(keyframes.value(0), 0.0);
1020 assert_eq!(keyframes.value(15), 50.0);
1021 assert_eq!(keyframes.value(30), 100.0);
1022 assert_eq!(keyframes.value(50), 75.0);
1023 assert_eq!(keyframes.value(70), 50.0);
1024 assert_eq!(keyframes.value(85), 65.0);
1025 assert_eq!(keyframes.value(100), 80.0);
1026 assert!(keyframes.is_done());
1027 }
1028
1029 #[test]
1030 fn keyframes_repeat_loop_restarts() {
1031 let mut keyframes = Keyframes::new(10)
1032 .stop(0.0, 0.0)
1033 .stop(1.0, 10.0)
1034 .loop_mode(LoopMode::Repeat);
1035
1036 keyframes.reset(0);
1037 assert_eq!(keyframes.value(5), 5.0);
1038 assert_eq!(keyframes.value(10), 0.0);
1039 assert_eq!(keyframes.value(12), 2.0);
1040 assert!(!keyframes.is_done());
1041 }
1042
1043 #[test]
1044 fn keyframes_pingpong_reverses_direction() {
1045 let mut keyframes = Keyframes::new(10)
1046 .stop(0.0, 0.0)
1047 .stop(1.0, 10.0)
1048 .loop_mode(LoopMode::PingPong);
1049
1050 keyframes.reset(0);
1051 assert_eq!(keyframes.value(8), 8.0);
1052 assert_eq!(keyframes.value(10), 10.0);
1053 assert_eq!(keyframes.value(12), 8.0);
1054 assert_eq!(keyframes.value(15), 5.0);
1055 assert!(!keyframes.is_done());
1056 }
1057
1058 #[test]
1059 fn sequence_chains_segments_in_order() {
1060 let mut sequence = Sequence::new()
1061 .then(0.0, 100.0, 30, ease_linear)
1062 .then(100.0, 50.0, 20, ease_linear)
1063 .then(50.0, 200.0, 40, ease_linear);
1064
1065 sequence.reset(0);
1066 assert_eq!(sequence.value(15), 50.0);
1067 assert_eq!(sequence.value(30), 100.0);
1068 assert_eq!(sequence.value(40), 75.0);
1069 assert_eq!(sequence.value(50), 50.0);
1070 assert_eq!(sequence.value(70), 125.0);
1071 assert_eq!(sequence.value(90), 200.0);
1072 assert!(sequence.is_done());
1073 }
1074
1075 #[test]
1076 fn sequence_loop_modes_repeat_and_pingpong_work() {
1077 let mut repeat = Sequence::new()
1078 .then(0.0, 10.0, 10, ease_linear)
1079 .loop_mode(LoopMode::Repeat);
1080 repeat.reset(0);
1081 assert_eq!(repeat.value(12), 2.0);
1082 assert!(!repeat.is_done());
1083
1084 let mut pingpong = Sequence::new()
1085 .then(0.0, 10.0, 10, ease_linear)
1086 .loop_mode(LoopMode::PingPong);
1087 pingpong.reset(0);
1088 assert_eq!(pingpong.value(12), 8.0);
1089 assert!(!pingpong.is_done());
1090 }
1091
1092 #[test]
1093 fn stagger_applies_per_item_delay() {
1094 let mut stagger = Stagger::new(0.0, 100.0, 20).easing(ease_linear).delay(5);
1095
1096 stagger.reset(0);
1097 assert_eq!(stagger.value(4, 3), 0.0);
1098 assert_eq!(stagger.value(15, 3), 0.0);
1099 assert_eq!(stagger.value(20, 3), 25.0);
1100 assert_eq!(stagger.value(35, 3), 100.0);
1101 assert!(stagger.is_done());
1102 }
1103}