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#[non_exhaustive]
211#[derive(Debug, Clone, Copy, PartialEq, Eq)]
212pub enum LoopMode {
213 Once,
215 Repeat,
217 PingPong,
219}
220
221#[derive(Clone, Copy)]
222struct KeyframeStop {
223 position: f64,
224 value: f64,
225}
226
227pub struct Keyframes {
253 duration_ticks: u64,
254 start_tick: u64,
255 stops: Vec<KeyframeStop>,
256 default_easing: fn(f64) -> f64,
257 segment_easing: Vec<fn(f64) -> f64>,
258 loop_mode: LoopMode,
259 done: bool,
260 on_complete: Option<Box<dyn FnMut()>>,
261}
262
263impl Keyframes {
264 pub fn new(duration_ticks: u64) -> Self {
270 Self {
271 duration_ticks,
272 start_tick: 0,
273 stops: Vec::new(),
274 default_easing: ease_linear,
275 segment_easing: Vec::new(),
276 loop_mode: LoopMode::Once,
277 done: false,
278 on_complete: None,
279 }
280 }
281
282 pub fn stop(mut self, position: f64, value: f64) -> Self {
286 self.stops.push(KeyframeStop {
287 position: clamp01(position),
288 value,
289 });
290 if self.stops.len() >= 2 {
291 self.segment_easing.push(self.default_easing);
292 }
293 self.stops.sort_by(|a, b| a.position.total_cmp(&b.position));
294 self
295 }
296
297 pub fn easing(mut self, f: fn(f64) -> f64) -> Self {
302 self.default_easing = f;
303 self.segment_easing.fill(f);
304 self
305 }
306
307 pub fn segment_easing(mut self, segment_index: usize, f: fn(f64) -> f64) -> Self {
312 if let Some(slot) = self.segment_easing.get_mut(segment_index) {
313 *slot = f;
314 }
315 self
316 }
317
318 pub fn loop_mode(mut self, mode: LoopMode) -> Self {
320 self.loop_mode = mode;
321 self
322 }
323
324 pub fn on_complete(mut self, f: impl FnMut() + 'static) -> Self {
326 self.on_complete = Some(Box::new(f));
327 self
328 }
329
330 pub fn value(&mut self, tick: u64) -> f64 {
332 let was_done = self.done;
333 if self.stops.is_empty() {
334 self.done = true;
335 if !was_done && self.done {
336 if let Some(cb) = &mut self.on_complete {
337 cb();
338 }
339 }
340 return 0.0;
341 }
342 if self.stops.len() == 1 {
343 self.done = true;
344 if !was_done && self.done {
345 if let Some(cb) = &mut self.on_complete {
346 cb();
347 }
348 }
349 return self.stops[0].value;
350 }
351
352 let stops = &self.stops;
353
354 let end_value = stops.last().map_or(0.0, |s| s.value);
355 let loop_tick = match map_loop_tick(
356 tick,
357 self.start_tick,
358 self.duration_ticks,
359 self.loop_mode,
360 &mut self.done,
361 ) {
362 Some(v) => v,
363 None => {
364 if !was_done && self.done {
365 if let Some(cb) = &mut self.on_complete {
366 cb();
367 }
368 }
369 return end_value;
370 }
371 };
372
373 let progress = loop_tick as f64 / self.duration_ticks as f64;
374
375 if progress <= stops[0].position {
376 return stops[0].value;
377 }
378 if progress >= 1.0 {
379 return end_value;
380 }
381
382 for i in 0..(stops.len() - 1) {
383 let a = stops[i];
384 let b = stops[i + 1];
385 if progress <= b.position {
386 let span = b.position - a.position;
387 if span <= f64::EPSILON {
388 return b.value;
389 }
390 let local = clamp01((progress - a.position) / span);
391 let easing = self
392 .segment_easing
393 .get(i)
394 .copied()
395 .unwrap_or(self.default_easing);
396 let eased = easing(local);
397 return lerp(a.value, b.value, eased);
398 }
399 }
400
401 end_value
402 }
403
404 pub fn is_done(&self) -> bool {
406 self.done
407 }
408
409 pub fn reset(&mut self, tick: u64) {
411 self.start_tick = tick;
412 self.done = false;
413 }
414}
415
416#[derive(Clone, Copy)]
417struct SequenceSegment {
418 from: f64,
419 to: f64,
420 duration_ticks: u64,
421 easing: fn(f64) -> f64,
422}
423
424pub struct Sequence {
443 segments: Vec<SequenceSegment>,
444 loop_mode: LoopMode,
445 start_tick: u64,
446 done: bool,
447 on_complete: Option<Box<dyn FnMut()>>,
448}
449
450impl Default for Sequence {
451 fn default() -> Self {
452 Self::new()
453 }
454}
455
456impl Sequence {
457 pub fn new() -> Self {
462 Self {
463 segments: Vec::new(),
464 loop_mode: LoopMode::Once,
465 start_tick: 0,
466 done: false,
467 on_complete: None,
468 }
469 }
470
471 pub fn then(mut self, from: f64, to: f64, duration_ticks: u64, easing: fn(f64) -> f64) -> Self {
473 self.segments.push(SequenceSegment {
474 from,
475 to,
476 duration_ticks,
477 easing,
478 });
479 self
480 }
481
482 pub fn loop_mode(mut self, mode: LoopMode) -> Self {
484 self.loop_mode = mode;
485 self
486 }
487
488 pub fn on_complete(mut self, f: impl FnMut() + 'static) -> Self {
490 self.on_complete = Some(Box::new(f));
491 self
492 }
493
494 pub fn value(&mut self, tick: u64) -> f64 {
496 let was_done = self.done;
497 if self.segments.is_empty() {
498 self.done = true;
499 if !was_done && self.done {
500 if let Some(cb) = &mut self.on_complete {
501 cb();
502 }
503 }
504 return 0.0;
505 }
506
507 let total_duration = self
508 .segments
509 .iter()
510 .fold(0_u64, |acc, s| acc.saturating_add(s.duration_ticks));
511 let end_value = self.segments.last().map_or(0.0, |s| s.to);
512
513 let loop_tick = match map_loop_tick(
514 tick,
515 self.start_tick,
516 total_duration,
517 self.loop_mode,
518 &mut self.done,
519 ) {
520 Some(v) => v,
521 None => {
522 if !was_done && self.done {
523 if let Some(cb) = &mut self.on_complete {
524 cb();
525 }
526 }
527 return end_value;
528 }
529 };
530
531 let mut remaining = loop_tick;
532 for segment in &self.segments {
533 if segment.duration_ticks == 0 {
534 continue;
535 }
536 if remaining < segment.duration_ticks {
537 let progress = remaining as f64 / segment.duration_ticks as f64;
538 let eased = (segment.easing)(clamp01(progress));
539 return lerp(segment.from, segment.to, eased);
540 }
541 remaining -= segment.duration_ticks;
542 }
543
544 end_value
545 }
546
547 pub fn is_done(&self) -> bool {
549 self.done
550 }
551
552 pub fn reset(&mut self, tick: u64) {
554 self.start_tick = tick;
555 self.done = false;
556 }
557}
558
559pub struct Stagger {
583 from: f64,
584 to: f64,
585 duration_ticks: u64,
586 start_tick: u64,
587 delay_ticks: u64,
588 easing: fn(f64) -> f64,
589 loop_mode: LoopMode,
590 item_count: usize,
591 done: bool,
592 on_complete: Option<Box<dyn FnMut()>>,
593}
594
595impl Stagger {
596 pub fn new(from: f64, to: f64, duration_ticks: u64) -> Self {
600 Self {
601 from,
602 to,
603 duration_ticks,
604 start_tick: 0,
605 delay_ticks: 0,
606 easing: ease_linear,
607 loop_mode: LoopMode::Once,
608 item_count: 0,
609 done: false,
610 on_complete: None,
611 }
612 }
613
614 pub fn easing(mut self, f: fn(f64) -> f64) -> Self {
616 self.easing = f;
617 self
618 }
619
620 pub fn delay(mut self, ticks: u64) -> Self {
622 self.delay_ticks = ticks;
623 self
624 }
625
626 pub fn loop_mode(mut self, mode: LoopMode) -> Self {
629 self.loop_mode = mode;
630 self
631 }
632
633 pub fn on_complete(mut self, f: impl FnMut() + 'static) -> Self {
635 self.on_complete = Some(Box::new(f));
636 self
637 }
638
639 pub fn items(mut self, count: usize) -> Self {
645 self.item_count = count;
646 self
647 }
648
649 pub fn value(&mut self, tick: u64, item_index: usize) -> f64 {
651 let was_done = self.done;
652 if item_index >= self.item_count {
653 self.item_count = item_index + 1;
654 }
655
656 let total_cycle = self.total_cycle_ticks();
657
658 let effective_tick = if self.loop_mode == LoopMode::Once {
659 tick
660 } else {
661 let elapsed = tick.wrapping_sub(self.start_tick);
662 let mapped = match self.loop_mode {
663 LoopMode::Repeat => {
664 if total_cycle == 0 {
665 0
666 } else {
667 elapsed % total_cycle
668 }
669 }
670 LoopMode::PingPong => {
671 if total_cycle == 0 {
672 0
673 } else {
674 let full = total_cycle.saturating_mul(2);
675 let phase = elapsed % full;
676 if phase < total_cycle {
677 phase
678 } else {
679 full - phase
680 }
681 }
682 }
683 LoopMode::Once => unreachable!(),
684 };
685 self.start_tick.wrapping_add(mapped)
686 };
687
688 let delay = self.delay_ticks.wrapping_mul(item_index as u64);
689 let item_start = self.start_tick.wrapping_add(delay);
690
691 if effective_tick < item_start {
692 self.done = false;
693 return self.from;
694 }
695
696 if self.duration_ticks == 0 {
697 self.done = true;
698 if !was_done && self.done {
699 if let Some(cb) = &mut self.on_complete {
700 cb();
701 }
702 }
703 return self.to;
704 }
705
706 let elapsed = effective_tick - item_start;
707 if elapsed >= self.duration_ticks {
708 self.done = true;
709 if !was_done && self.done {
710 if let Some(cb) = &mut self.on_complete {
711 cb();
712 }
713 }
714 return self.to;
715 }
716
717 self.done = false;
718 let progress = elapsed as f64 / self.duration_ticks as f64;
719 let eased = (self.easing)(clamp01(progress));
720 lerp(self.from, self.to, eased)
721 }
722
723 fn total_cycle_ticks(&self) -> u64 {
724 let max_delay = self
725 .delay_ticks
726 .wrapping_mul(self.item_count.saturating_sub(1) as u64);
727 self.duration_ticks.saturating_add(max_delay)
728 }
729
730 pub fn is_done(&self) -> bool {
732 self.done
733 }
734
735 pub fn reset(&mut self, tick: u64) {
737 self.start_tick = tick;
738 self.done = false;
739 }
740}
741
742fn map_loop_tick(
743 tick: u64,
744 start_tick: u64,
745 duration_ticks: u64,
746 loop_mode: LoopMode,
747 done: &mut bool,
748) -> Option<u64> {
749 if duration_ticks == 0 {
750 *done = true;
751 return None;
752 }
753
754 let elapsed = tick.wrapping_sub(start_tick);
755 match loop_mode {
756 LoopMode::Once => {
757 if elapsed >= duration_ticks {
758 *done = true;
759 None
760 } else {
761 *done = false;
762 Some(elapsed)
763 }
764 }
765 LoopMode::Repeat => {
766 *done = false;
767 Some(elapsed % duration_ticks)
768 }
769 LoopMode::PingPong => {
770 *done = false;
771 let cycle = duration_ticks.saturating_mul(2);
772 if cycle == 0 {
773 return Some(0);
774 }
775 let phase = elapsed % cycle;
776 if phase < duration_ticks {
777 Some(phase)
778 } else {
779 Some(cycle - phase)
780 }
781 }
782 }
783}
784
785pub struct Spring {
811 value: f64,
812 target: f64,
813 velocity: f64,
814 stiffness: f64,
815 damping: f64,
816 settled: bool,
817 on_settle: Option<Box<dyn FnMut()>>,
818}
819
820impl Spring {
821 pub fn new(initial: f64, stiffness: f64, damping: f64) -> Self {
826 Self {
827 value: initial,
828 target: initial,
829 velocity: 0.0,
830 stiffness,
831 damping,
832 settled: true,
833 on_settle: None,
834 }
835 }
836
837 pub fn on_settle(mut self, f: impl FnMut() + 'static) -> Self {
839 self.on_settle = Some(Box::new(f));
840 self
841 }
842
843 pub fn set_target(&mut self, target: f64) {
845 self.target = target;
846 self.settled = self.is_settled();
847 }
848
849 pub fn tick(&mut self) {
853 let displacement = self.target - self.value;
854 let spring_force = displacement * self.stiffness;
855 self.velocity = (self.velocity + spring_force) * self.damping;
856 self.value += self.velocity;
857
858 let is_settled = self.is_settled();
859 if !self.settled && is_settled {
860 self.settled = true;
861 if let Some(cb) = &mut self.on_settle {
862 cb();
863 }
864 }
865 }
866
867 pub fn value(&self) -> f64 {
869 self.value
870 }
871
872 pub fn is_settled(&self) -> bool {
877 (self.target - self.value).abs() < 0.01 && self.velocity.abs() < 0.01
878 }
879}
880
881fn clamp01(t: f64) -> f64 {
882 t.clamp(0.0, 1.0)
883}
884
885#[cfg(test)]
886mod tests {
887 use super::*;
888 use std::cell::Cell;
889 use std::rc::Rc;
890
891 fn assert_endpoints(f: fn(f64) -> f64) {
892 assert_eq!(f(0.0), 0.0);
893 assert_eq!(f(1.0), 1.0);
894 }
895
896 #[test]
897 fn easing_functions_have_expected_endpoints() {
898 let easing_functions: [fn(f64) -> f64; 9] = [
899 ease_linear,
900 ease_in_quad,
901 ease_out_quad,
902 ease_in_out_quad,
903 ease_in_cubic,
904 ease_out_cubic,
905 ease_in_out_cubic,
906 ease_out_elastic,
907 ease_out_bounce,
908 ];
909
910 for easing in easing_functions {
911 assert_endpoints(easing);
912 }
913 }
914
915 #[test]
916 fn tween_returns_start_middle_end_values() {
917 let mut tween = Tween::new(0.0, 10.0, 10);
918 tween.reset(100);
919
920 assert_eq!(tween.value(100), 0.0);
921 assert_eq!(tween.value(105), 5.0);
922 assert_eq!(tween.value(110), 10.0);
923 assert!(tween.is_done());
924 }
925
926 #[test]
927 fn tween_reset_restarts_animation() {
928 let mut tween = Tween::new(0.0, 1.0, 10);
929 tween.reset(0);
930 let _ = tween.value(10);
931 assert!(tween.is_done());
932
933 tween.reset(20);
934 assert!(!tween.is_done());
935 assert_eq!(tween.value(20), 0.0);
936 assert_eq!(tween.value(30), 1.0);
937 assert!(tween.is_done());
938 }
939
940 #[test]
941 fn tween_on_complete_fires_once() {
942 let count = Rc::new(Cell::new(0));
943 let callback_count = Rc::clone(&count);
944 let mut tween = Tween::new(0.0, 10.0, 10).on_complete(move || {
945 callback_count.set(callback_count.get() + 1);
946 });
947
948 tween.reset(0);
949 assert_eq!(count.get(), 0);
950
951 assert_eq!(tween.value(5), 5.0);
952 assert_eq!(count.get(), 0);
953
954 assert_eq!(tween.value(10), 10.0);
955 assert_eq!(count.get(), 1);
956
957 assert_eq!(tween.value(11), 10.0);
958 assert_eq!(count.get(), 1);
959 }
960
961 #[test]
962 fn spring_settles_to_target() {
963 let mut spring = Spring::new(0.0, 0.2, 0.85);
964 spring.set_target(10.0);
965
966 for _ in 0..300 {
967 spring.tick();
968 if spring.is_settled() {
969 break;
970 }
971 }
972
973 assert!(spring.is_settled());
974 assert!((spring.value() - 10.0).abs() < 0.01);
975 }
976
977 #[test]
978 fn spring_on_settle_fires_once() {
979 let count = Rc::new(Cell::new(0));
980 let callback_count = Rc::clone(&count);
981 let mut spring = Spring::new(0.0, 0.2, 0.85).on_settle(move || {
982 callback_count.set(callback_count.get() + 1);
983 });
984 spring.set_target(10.0);
985
986 for _ in 0..500 {
987 spring.tick();
988 if spring.is_settled() {
989 break;
990 }
991 }
992
993 assert!(spring.is_settled());
994 assert_eq!(count.get(), 1);
995
996 for _ in 0..50 {
997 spring.tick();
998 }
999
1000 assert_eq!(count.get(), 1);
1001 }
1002
1003 #[test]
1004 fn lerp_interpolates_values() {
1005 assert_eq!(lerp(0.0, 10.0, 0.0), 0.0);
1006 assert_eq!(lerp(0.0, 10.0, 0.5), 5.0);
1007 assert_eq!(lerp(0.0, 10.0, 1.0), 10.0);
1008 }
1009
1010 #[test]
1011 fn keyframes_interpolates_across_multiple_stops() {
1012 let mut keyframes = Keyframes::new(100)
1013 .stop(0.0, 0.0)
1014 .stop(0.3, 100.0)
1015 .stop(0.7, 50.0)
1016 .stop(1.0, 80.0)
1017 .easing(ease_linear);
1018
1019 keyframes.reset(0);
1020 assert_eq!(keyframes.value(0), 0.0);
1021 assert_eq!(keyframes.value(15), 50.0);
1022 assert_eq!(keyframes.value(30), 100.0);
1023 assert_eq!(keyframes.value(50), 75.0);
1024 assert_eq!(keyframes.value(70), 50.0);
1025 assert_eq!(keyframes.value(85), 65.0);
1026 assert_eq!(keyframes.value(100), 80.0);
1027 assert!(keyframes.is_done());
1028 }
1029
1030 #[test]
1031 fn keyframes_repeat_loop_restarts() {
1032 let mut keyframes = Keyframes::new(10)
1033 .stop(0.0, 0.0)
1034 .stop(1.0, 10.0)
1035 .loop_mode(LoopMode::Repeat);
1036
1037 keyframes.reset(0);
1038 assert_eq!(keyframes.value(5), 5.0);
1039 assert_eq!(keyframes.value(10), 0.0);
1040 assert_eq!(keyframes.value(12), 2.0);
1041 assert!(!keyframes.is_done());
1042 }
1043
1044 #[test]
1045 fn keyframes_pingpong_reverses_direction() {
1046 let mut keyframes = Keyframes::new(10)
1047 .stop(0.0, 0.0)
1048 .stop(1.0, 10.0)
1049 .loop_mode(LoopMode::PingPong);
1050
1051 keyframes.reset(0);
1052 assert_eq!(keyframes.value(8), 8.0);
1053 assert_eq!(keyframes.value(10), 10.0);
1054 assert_eq!(keyframes.value(12), 8.0);
1055 assert_eq!(keyframes.value(15), 5.0);
1056 assert!(!keyframes.is_done());
1057 }
1058
1059 #[test]
1060 fn sequence_chains_segments_in_order() {
1061 let mut sequence = Sequence::new()
1062 .then(0.0, 100.0, 30, ease_linear)
1063 .then(100.0, 50.0, 20, ease_linear)
1064 .then(50.0, 200.0, 40, ease_linear);
1065
1066 sequence.reset(0);
1067 assert_eq!(sequence.value(15), 50.0);
1068 assert_eq!(sequence.value(30), 100.0);
1069 assert_eq!(sequence.value(40), 75.0);
1070 assert_eq!(sequence.value(50), 50.0);
1071 assert_eq!(sequence.value(70), 125.0);
1072 assert_eq!(sequence.value(90), 200.0);
1073 assert!(sequence.is_done());
1074 }
1075
1076 #[test]
1077 fn sequence_loop_modes_repeat_and_pingpong_work() {
1078 let mut repeat = Sequence::new()
1079 .then(0.0, 10.0, 10, ease_linear)
1080 .loop_mode(LoopMode::Repeat);
1081 repeat.reset(0);
1082 assert_eq!(repeat.value(12), 2.0);
1083 assert!(!repeat.is_done());
1084
1085 let mut pingpong = Sequence::new()
1086 .then(0.0, 10.0, 10, ease_linear)
1087 .loop_mode(LoopMode::PingPong);
1088 pingpong.reset(0);
1089 assert_eq!(pingpong.value(12), 8.0);
1090 assert!(!pingpong.is_done());
1091 }
1092
1093 #[test]
1094 fn stagger_applies_per_item_delay() {
1095 let mut stagger = Stagger::new(0.0, 100.0, 20).easing(ease_linear).delay(5);
1096
1097 stagger.reset(0);
1098 assert_eq!(stagger.value(4, 3), 0.0);
1099 assert_eq!(stagger.value(15, 3), 0.0);
1100 assert_eq!(stagger.value(20, 3), 25.0);
1101 assert_eq!(stagger.value(35, 3), 100.0);
1102 assert!(stagger.is_done());
1103 }
1104}