1use std::{marker::PhantomData, sync::Arc};
4
5use crate::{Duration, Point, Time, Transform, Vector2};
6
7mod easing;
8mod interval;
9mod keyframes;
10mod primitives;
11mod spring;
12#[cfg(test)]
13mod tests;
14
15pub use easing::Easing;
16pub use interval::Interval;
17pub use keyframes::Keyframes;
18pub use primitives::{
19 Extrapolate, FollowPath, Noise, NoiseBuilder, Oscillator, OscillatorBuilder, PathSample,
20 Waveform, piecewise, remap, try_piecewise, try_remap, try_value_noise, value_noise,
21};
22pub use spring::{Spring, SpringBuilder};
23
24const BEFORE_START: Time = Time::from_ticks(-1);
25
26pub trait Interpolate: Clone {
31 fn interpolate(&self, target: &Self, progress: f64) -> Self;
37}
38
39impl Interpolate for f32 {
40 fn interpolate(&self, target: &Self, progress: f64) -> Self {
41 let progress = progress as f32;
42 self * (1.0 - progress) + target * progress
43 }
44}
45
46impl Interpolate for f64 {
47 fn interpolate(&self, target: &Self, progress: f64) -> Self {
48 self * (1.0 - progress) + target * progress
49 }
50}
51
52impl Interpolate for Point {
53 fn interpolate(&self, target: &Self, progress: f64) -> Self {
54 Point::interpolate(self, target, progress)
55 }
56}
57
58impl Interpolate for Vector2 {
59 fn interpolate(&self, target: &Self, progress: f64) -> Self {
60 Vector2::interpolate(self, target, progress)
61 }
62}
63
64impl Interpolate for Transform {
65 fn interpolate(&self, target: &Self, progress: f64) -> Self {
66 Transform::interpolate(self, target, progress)
67 }
68}
69
70impl<Value: Interpolate, const COUNT: usize> Interpolate for [Value; COUNT] {
71 fn interpolate(&self, target: &Self, progress: f64) -> Self {
72 std::array::from_fn(|index| self[index].interpolate(&target[index], progress))
73 }
74}
75
76impl Interpolate for crate::Color {
77 fn interpolate(&self, target: &Self, progress: f64) -> Self {
78 crate::Color::interpolate(self, target, progress)
79 }
80}
81
82impl Interpolate for crate::Size {
83 fn interpolate(&self, target: &Self, progress: f64) -> Self {
84 Self::new(
85 self.width.interpolate(&target.width, progress),
86 self.height.interpolate(&target.height, progress),
87 )
88 }
89}
90
91impl Interpolate for crate::Rect {
92 fn interpolate(&self, target: &Self, progress: f64) -> Self {
93 Self::new(
94 self.left.interpolate(&target.left, progress),
95 self.top.interpolate(&target.top, progress),
96 self.right.interpolate(&target.right, progress),
97 self.bottom.interpolate(&target.bottom, progress),
98 )
99 }
100}
101
102impl<First: Interpolate, Second: Interpolate> Interpolate for (First, Second) {
103 fn interpolate(&self, target: &Self, progress: f64) -> Self {
104 (
105 self.0.interpolate(&target.0, progress),
106 self.1.interpolate(&target.1, progress),
107 )
108 }
109}
110
111pub trait Signal<Value>: Send + Sync {
117 fn sample(&self, time: Time) -> Value;
119
120 fn sample_at(&self, elapsed: Duration) -> Value {
124 self.sample(elapsed.after_origin())
125 }
126
127 fn map<Output, Function>(self, function: Function) -> Map<Self, Function, Value>
129 where
130 Self: Sized,
131 Function: Fn(Value) -> Output + Send + Sync,
132 {
133 Map {
134 source: self,
135 function,
136 input: PhantomData,
137 }
138 }
139
140 fn zip<OtherValue, Other>(self, other: Other) -> Zip<Self, Other>
142 where
143 Self: Sized,
144 Other: Signal<OtherValue>,
145 {
146 Zip {
147 first: self,
148 second: other,
149 }
150 }
151
152 fn boxed(self) -> Arc<dyn Signal<Value>>
154 where
155 Self: Sized + 'static,
156 {
157 Arc::new(self)
158 }
159}
160
161pub trait FiniteAnimation<Value>: Signal<Value> {
167 fn duration(&self) -> Duration;
169
170 fn delay(self, offset: Duration) -> Delay<Self>
175 where
176 Self: Sized,
177 {
178 self.try_delay(offset)
179 .unwrap_or_else(|error| panic!("{}", error.message))
180 }
181
182 fn try_delay(self, offset: Duration) -> Result<Delay<Self>, crate::ValidationError>
184 where
185 Self: Sized,
186 {
187 let duration = self
188 .duration()
189 .ticks()
190 .checked_add(offset.ticks())
191 .map(Duration::from_ticks)
192 .filter(|duration| duration.checked_offset().is_some())
193 .ok_or_else(|| {
194 crate::ValidationError::invalid(
195 "animation",
196 "delay",
197 "animation delay overflows the tick range",
198 )
199 })?;
200 Ok(Delay {
201 source: self,
202 offset,
203 duration,
204 })
205 }
206
207 fn speed(self, factor: f64) -> Speed<Self>
214 where
215 Self: Sized,
216 {
217 self.try_speed(factor)
218 .unwrap_or_else(|error| panic!("{}", error.message))
219 }
220
221 fn try_speed(self, factor: f64) -> Result<Speed<Self>, crate::ValidationError>
224 where
225 Self: Sized,
226 {
227 if !(factor.is_finite() && factor > 0.0) {
228 return Err(crate::ValidationError::invalid(
229 "animation",
230 "speed",
231 "animation speed must be finite and positive",
232 ));
233 }
234 let source_duration = self.duration();
235 let duration = source_duration.try_div(factor)?;
236 Ok(Speed {
237 source: self,
238 factor,
239 source_duration,
240 duration,
241 })
242 }
243
244 fn then<Next: FiniteAnimation<Value>>(self, next: Next) -> Then<Self, Next>
247 where
248 Self: Sized,
249 {
250 let first_duration = self.duration();
251 let next_duration = next.duration();
252 Then {
253 first: self,
254 next,
255 first_duration,
256 duration: Duration::from_ticks(
257 first_duration
258 .ticks()
259 .checked_add(next_duration.ticks())
260 .filter(|ticks| i64::try_from(*ticks).is_ok())
261 .expect("animation duration overflow"),
262 ),
263 }
264 }
265
266 fn reverse(self) -> Reverse<Self>
268 where
269 Self: Sized,
270 {
271 let duration = self.duration();
272 Reverse {
273 source: self,
274 duration,
275 }
276 }
277
278 fn repeat(self, count: u32) -> Repeat<Self>
282 where
283 Self: Sized,
284 {
285 let source_duration = self.duration();
286 let duration = cycle_duration(source_duration, count);
287 Repeat {
288 source: self,
289 count,
290 source_duration,
291 duration,
292 }
293 }
294
295 fn ping_pong(self) -> PingPong<Self>
298 where
299 Self: Sized,
300 {
301 let source_duration = self.duration();
302 let duration = cycle_duration(source_duration, 2);
303 PingPong {
304 source: self,
305 source_duration,
306 duration,
307 }
308 }
309
310 fn boxed_finite(self) -> Arc<dyn FiniteAnimation<Value>>
312 where
313 Self: Sized + 'static,
314 {
315 Arc::new(self)
316 }
317}
318
319impl<Value, Function> Signal<Value> for Function
320where
321 Function: Fn(Time) -> Value + Send + Sync,
322{
323 fn sample(&self, time: Time) -> Value {
324 self(time)
325 }
326}
327
328impl<Value> Signal<Value> for Arc<dyn Signal<Value> + '_> {
329 fn sample(&self, time: Time) -> Value {
330 self.as_ref().sample(time)
331 }
332}
333
334impl<Value> Signal<Value> for Arc<dyn FiniteAnimation<Value> + '_> {
335 fn sample(&self, time: Time) -> Value {
336 self.as_ref().sample(time)
337 }
338}
339
340impl<Value> FiniteAnimation<Value> for Arc<dyn FiniteAnimation<Value> + '_> {
341 fn duration(&self) -> Duration {
342 self.as_ref().duration()
343 }
344}
345
346#[derive(Clone, Debug)]
348pub struct Constant<Value> {
349 value: Value,
350}
351
352impl<Value> Constant<Value> {
353 pub const fn new(value: Value) -> Self {
355 Self { value }
356 }
357}
358
359impl<Value: Clone + Send + Sync> Signal<Value> for Constant<Value> {
360 fn sample(&self, _time: Time) -> Value {
361 self.value.clone()
362 }
363}
364
365#[derive(Clone, Debug)]
378pub struct Tween<Value> {
379 from: Value,
380 to: Value,
381 interval: Interval,
382 easing: Easing,
383}
384
385impl<Value> Tween<Value> {
386 pub fn new(from: Value, to: Value, duration: Duration) -> Self {
389 Self {
390 from,
391 to,
392 interval: Interval::new(Time::ZERO, duration),
393 easing: Easing::Linear,
394 }
395 }
396
397 pub fn easing(mut self, easing: Easing) -> Self {
399 self.easing = easing;
400 self
401 }
402}
403
404impl<Value: Interpolate + Send + Sync> Signal<Value> for Tween<Value> {
405 fn sample(&self, time: Time) -> Value {
406 let progress = self.interval.progress(time);
407 if progress <= 0.0 {
408 return self.from.clone();
409 }
410 if progress >= 1.0 {
411 return self.to.clone();
412 }
413 self.from.interpolate(&self.to, self.easing.apply(progress))
414 }
415}
416
417impl<Value: Interpolate + Send + Sync> FiniteAnimation<Value> for Tween<Value> {
418 fn duration(&self) -> Duration {
419 self.interval.duration()
420 }
421}
422
423#[derive(Clone, Debug)]
425pub struct Map<Source, Function, Input> {
426 source: Source,
427 function: Function,
428 input: PhantomData<fn(Input)>,
429}
430
431impl<Input, Output, Source, Function> Signal<Output> for Map<Source, Function, Input>
432where
433 Source: Signal<Input>,
434 Function: Fn(Input) -> Output + Send + Sync,
435{
436 fn sample(&self, time: Time) -> Output {
437 (self.function)(self.source.sample(time))
438 }
439}
440
441impl<Input, Output, Source, Function> FiniteAnimation<Output> for Map<Source, Function, Input>
442where
443 Source: FiniteAnimation<Input>,
444 Function: Fn(Input) -> Output + Send + Sync,
445{
446 fn duration(&self) -> Duration {
447 self.source.duration()
448 }
449}
450
451#[derive(Clone, Debug)]
453pub struct Zip<First, Second> {
454 first: First,
455 second: Second,
456}
457
458impl<FirstValue, SecondValue, First, Second> Signal<(FirstValue, SecondValue)>
459 for Zip<First, Second>
460where
461 First: Signal<FirstValue>,
462 Second: Signal<SecondValue>,
463{
464 fn sample(&self, time: Time) -> (FirstValue, SecondValue) {
465 (self.first.sample(time), self.second.sample(time))
466 }
467}
468
469impl<FirstValue, SecondValue, First, Second> FiniteAnimation<(FirstValue, SecondValue)>
470 for Zip<First, Second>
471where
472 First: FiniteAnimation<FirstValue>,
473 Second: FiniteAnimation<SecondValue>,
474{
475 fn duration(&self) -> Duration {
476 self.first.duration().max(self.second.duration())
477 }
478}
479
480fn cycle_duration(duration: Duration, count: u32) -> Duration {
482 duration
483 .checked_mul(u64::from(count))
484 .filter(|duration| duration.checked_offset().is_some())
485 .expect("animation duration overflow")
486}
487
488fn at(duration: Duration) -> Time {
490 duration.after_origin()
491}
492
493#[derive(Clone, Debug)]
495pub struct Delay<Source> {
496 source: Source,
497 offset: Duration,
498 duration: Duration,
499}
500
501impl<Value, Source: FiniteAnimation<Value>> Signal<Value> for Delay<Source> {
502 fn sample(&self, time: Time) -> Value {
503 let local = if time < at(self.offset) {
504 BEFORE_START
505 } else if time >= at(self.duration) {
506 at(self.duration - self.offset)
507 } else {
508 time - self.offset
509 };
510 self.source.sample(local)
511 }
512}
513
514impl<Value, Source: FiniteAnimation<Value>> FiniteAnimation<Value> for Delay<Source> {
515 fn duration(&self) -> Duration {
516 self.duration
517 }
518}
519
520#[derive(Clone, Debug)]
522pub struct Speed<Source> {
523 source: Source,
524 factor: f64,
525 source_duration: Duration,
526 duration: Duration,
527}
528
529impl<Value, Source: FiniteAnimation<Value>> Signal<Value> for Speed<Source> {
530 fn sample(&self, time: Time) -> Value {
531 let local = if time < Time::ZERO {
532 BEFORE_START
533 } else if time >= at(self.duration) {
534 at(self.source_duration)
535 } else if self.factor == 1.0 {
536 time
537 } else {
538 Time::from_ticks(
539 (time.ticks() as f64 * self.factor)
540 .round()
541 .clamp(0.0, self.source_duration.ticks() as f64) as i64,
542 )
543 };
544 self.source.sample(local)
545 }
546}
547
548impl<Value, Source: FiniteAnimation<Value>> FiniteAnimation<Value> for Speed<Source> {
549 fn duration(&self) -> Duration {
550 self.duration
551 }
552}
553
554#[derive(Clone, Debug)]
556pub struct Then<First, Next> {
557 first: First,
558 next: Next,
559 first_duration: Duration,
560 duration: Duration,
561}
562
563impl<Value, First: FiniteAnimation<Value>, Next: FiniteAnimation<Value>> Signal<Value>
564 for Then<First, Next>
565{
566 fn sample(&self, time: Time) -> Value {
567 if time < Time::ZERO {
568 self.first.sample(BEFORE_START)
569 } else if time >= at(self.duration) {
570 self.next.sample(at(self.duration - self.first_duration))
571 } else if time < at(self.first_duration) {
572 self.first.sample(time)
573 } else {
574 self.next.sample(time - self.first_duration)
575 }
576 }
577}
578
579impl<Value, First: FiniteAnimation<Value>, Next: FiniteAnimation<Value>> FiniteAnimation<Value>
580 for Then<First, Next>
581{
582 fn duration(&self) -> Duration {
583 self.duration
584 }
585}
586
587#[derive(Clone, Debug)]
589pub struct Reverse<Source> {
590 source: Source,
591 duration: Duration,
592}
593
594impl<Value, Source: FiniteAnimation<Value>> Signal<Value> for Reverse<Source> {
595 fn sample(&self, time: Time) -> Value {
596 let local = if time < Time::ZERO {
597 at(self.duration)
598 } else if time >= at(self.duration) {
599 BEFORE_START
600 } else {
601 at(self.duration) - time.since_origin()
602 };
603 self.source.sample(local)
604 }
605}
606
607impl<Value, Source: FiniteAnimation<Value>> FiniteAnimation<Value> for Reverse<Source> {
608 fn duration(&self) -> Duration {
609 self.duration
610 }
611}
612
613#[derive(Clone, Debug)]
615pub struct Repeat<Source> {
616 source: Source,
617 count: u32,
618 source_duration: Duration,
619 duration: Duration,
620}
621
622impl<Value, Source: FiniteAnimation<Value>> Signal<Value> for Repeat<Source> {
623 fn sample(&self, time: Time) -> Value {
624 let local = if self.count == 0 || time < Time::ZERO {
625 BEFORE_START
626 } else if time >= at(self.duration) {
627 at(self.source_duration)
628 } else {
629 Time::from_ticks(time.ticks() % at(self.source_duration).ticks())
630 };
631 self.source.sample(local)
632 }
633}
634
635impl<Value, Source: FiniteAnimation<Value>> FiniteAnimation<Value> for Repeat<Source> {
636 fn duration(&self) -> Duration {
637 self.duration
638 }
639}
640
641#[derive(Clone, Debug)]
643pub struct PingPong<Source> {
644 source: Source,
645 source_duration: Duration,
646 duration: Duration,
647}
648
649impl<Value, Source: FiniteAnimation<Value>> Signal<Value> for PingPong<Source> {
650 fn sample(&self, time: Time) -> Value {
651 let local = if time < Time::ZERO || time >= at(self.duration) {
652 BEFORE_START
653 } else if time <= at(self.source_duration) {
654 time
655 } else {
656 at(self.duration) - time.since_origin()
657 };
658 self.source.sample(local)
659 }
660}
661
662impl<Value, Source: FiniteAnimation<Value>> FiniteAnimation<Value> for PingPong<Source> {
663 fn duration(&self) -> Duration {
664 self.duration
665 }
666}
667
668#[cfg(test)]
669mod interpolation_tests {
670 use super::*;
671
672 #[test]
673 fn interpolation_preserves_double_precision_progress() {
674 #[derive(Clone)]
675 struct CustomValue(f64);
676
677 impl Interpolate for CustomValue {
678 fn interpolate(&self, _target: &Self, progress: f64) -> Self {
679 Self(progress)
680 }
681 }
682
683 fn blend<Value: Interpolate>(from: Value, to: Value, progress: f64) -> Value {
684 from.interpolate(&to, progress)
685 }
686
687 let progress = 0.500_000_000_1;
688 assert_ne!(progress, f64::from(progress as f32));
689 assert_eq!(blend(0.0_f64, 1.0, progress), progress);
690 assert_eq!(
691 blend(CustomValue(0.0), CustomValue(1.0), progress).0,
692 progress
693 );
694 let point = blend(Point::ORIGIN, Point::new(1.0, 2.0), progress);
695 assert_eq!(point.x, progress);
696 assert_eq!(point.y, 2.0 * progress);
697 assert_eq!(
698 blend(Vector2::ZERO, Vector2::new(1.0, 0.0), progress).x,
699 progress
700 );
701 let transform = blend(
702 Transform::IDENTITY,
703 Transform::translate(1.0, 0.0),
704 progress,
705 );
706 assert_eq!(transform.apply(Point::ORIGIN).x, progress);
707 let array = blend([0.0_f64; 2], [1.0_f64; 2], progress);
708 assert_eq!(array, [progress; 2]);
709 assert_eq!(
710 blend((0.0_f64, 0.0_f64), (1.0, 2.0), progress),
711 (progress, 2.0 * progress)
712 );
713 }
714}