1use std::{marker::PhantomData, sync::Arc};
4
5use crate::{Duration, Point, Time, Transform, Vector2};
6
7mod easing;
8mod keyframes;
9mod primitives;
10mod spring;
11#[cfg(test)]
12mod tests;
13
14pub use easing::Easing;
15pub use keyframes::Keyframes;
16pub use primitives::{
17 Extrapolate, FollowPath, Noise, Oscillator, PathSample, Waveform, piecewise, remap, value_noise,
18};
19pub use spring::Spring;
20
21const BEFORE_START: Time = Time::from_ticks(-1);
22
23pub trait Interpolate: Clone {
28 fn interpolate(&self, target: &Self, progress: f64) -> Self;
34}
35
36impl Interpolate for f32 {
37 fn interpolate(&self, target: &Self, progress: f64) -> Self {
38 let progress = progress as f32;
39 self * (1.0 - progress) + target * progress
40 }
41}
42
43impl Interpolate for f64 {
44 fn interpolate(&self, target: &Self, progress: f64) -> Self {
45 self * (1.0 - progress) + target * progress
46 }
47}
48
49impl Interpolate for Point {
50 fn interpolate(&self, target: &Self, progress: f64) -> Self {
51 Point::interpolate(self, target, progress)
52 }
53}
54
55impl Interpolate for Vector2 {
56 fn interpolate(&self, target: &Self, progress: f64) -> Self {
57 Vector2::interpolate(self, target, progress)
58 }
59}
60
61impl Interpolate for Transform {
62 fn interpolate(&self, target: &Self, progress: f64) -> Self {
63 Transform::interpolate(self, target, progress)
64 }
65}
66
67impl<Value: Interpolate, const COUNT: usize> Interpolate for [Value; COUNT] {
68 fn interpolate(&self, target: &Self, progress: f64) -> Self {
69 std::array::from_fn(|index| self[index].interpolate(&target[index], progress))
70 }
71}
72
73impl Interpolate for crate::Color {
74 fn interpolate(&self, target: &Self, progress: f64) -> Self {
75 crate::Color::interpolate(self, target, progress)
76 }
77}
78
79impl Interpolate for crate::Size {
80 fn interpolate(&self, target: &Self, progress: f64) -> Self {
81 Self::new(
82 self.width.interpolate(&target.width, progress),
83 self.height.interpolate(&target.height, progress),
84 )
85 }
86}
87
88impl Interpolate for crate::Rect {
89 fn interpolate(&self, target: &Self, progress: f64) -> Self {
90 Self::new(
91 self.left.interpolate(&target.left, progress),
92 self.top.interpolate(&target.top, progress),
93 self.right.interpolate(&target.right, progress),
94 self.bottom.interpolate(&target.bottom, progress),
95 )
96 }
97}
98
99impl<First: Interpolate, Second: Interpolate> Interpolate for (First, Second) {
100 fn interpolate(&self, target: &Self, progress: f64) -> Self {
101 (
102 self.0.interpolate(&target.0, progress),
103 self.1.interpolate(&target.1, progress),
104 )
105 }
106}
107
108pub trait Animation<Value>: Send + Sync {
114 fn sample(&self, time: Time) -> Value;
116
117 fn duration(&self) -> Option<Duration> {
119 None
120 }
121
122 fn map<Output, Function>(self, function: Function) -> Map<Self, Function, Value>
124 where
125 Self: Sized,
126 Function: Fn(Value) -> Output + Send + Sync,
127 {
128 Map {
129 source: self,
130 function,
131 input: PhantomData,
132 }
133 }
134
135 fn zip<OtherValue, Other>(self, other: Other) -> Zip<Self, Other>
137 where
138 Self: Sized,
139 Other: Animation<OtherValue>,
140 {
141 Zip {
142 first: self,
143 second: other,
144 }
145 }
146
147 fn delay(self, offset: Duration) -> Delay<Self>
151 where
152 Self: Sized,
153 {
154 assert!(offset >= Time::ZERO, "animation delay must be nonnegative");
155 let duration = checked_duration(self.duration()).map(|duration| duration + offset);
156 Delay {
157 source: self,
158 offset,
159 duration,
160 }
161 }
162
163 fn speed(self, factor: f64) -> Speed<Self>
167 where
168 Self: Sized,
169 {
170 assert!(
171 factor.is_finite() && factor > 0.0,
172 "animation speed must be finite and positive"
173 );
174 let source_duration = checked_duration(self.duration());
175 let duration = source_duration.map(|duration| duration / factor);
176 Speed {
177 source: self,
178 factor,
179 source_duration,
180 duration,
181 }
182 }
183
184 fn then<Next: Animation<Value>>(self, next: Next) -> Then<Self, Next>
187 where
188 Self: Sized,
189 {
190 let first_duration = finite_duration(self.duration(), "then");
191 let next_duration = finite_duration(next.duration(), "then");
192 Then {
193 first: self,
194 next,
195 first_duration,
196 duration: first_duration + next_duration,
197 }
198 }
199
200 fn reverse(self) -> Reverse<Self>
202 where
203 Self: Sized,
204 {
205 let duration = finite_duration(self.duration(), "reverse");
206 Reverse {
207 source: self,
208 duration,
209 }
210 }
211
212 fn repeat(self, count: u32) -> Repeat<Self>
216 where
217 Self: Sized,
218 {
219 let source_duration = finite_duration(self.duration(), "repeat");
220 let duration = cycle_duration(source_duration, count);
221 Repeat {
222 source: self,
223 count,
224 source_duration,
225 duration,
226 }
227 }
228
229 fn ping_pong(self) -> PingPong<Self>
232 where
233 Self: Sized,
234 {
235 let source_duration = finite_duration(self.duration(), "ping_pong");
236 let duration = cycle_duration(source_duration, 2);
237 PingPong {
238 source: self,
239 source_duration,
240 duration,
241 }
242 }
243
244 fn boxed(self) -> Arc<dyn Animation<Value>>
246 where
247 Self: Sized + 'static,
248 {
249 Arc::new(self)
250 }
251}
252
253impl<Value, Function> Animation<Value> for Function
254where
255 Function: Fn(Time) -> Value + Send + Sync,
256{
257 fn sample(&self, time: Time) -> Value {
258 self(time)
259 }
260}
261
262impl<Value> Animation<Value> for Arc<dyn Animation<Value> + '_> {
263 fn sample(&self, time: Time) -> Value {
264 self.as_ref().sample(time)
265 }
266
267 fn duration(&self) -> Option<Duration> {
268 self.as_ref().duration()
269 }
270}
271
272#[derive(Clone, Debug)]
274pub struct Constant<Value> {
275 value: Value,
276}
277
278impl<Value> Constant<Value> {
279 pub const fn new(value: Value) -> Self {
281 Self { value }
282 }
283}
284
285impl<Value: Clone + Send + Sync> Animation<Value> for Constant<Value> {
286 fn sample(&self, _time: Time) -> Value {
287 self.value.clone()
288 }
289}
290
291#[derive(Clone, Debug)]
304pub struct Tween<Value> {
305 from: Value,
306 to: Value,
307 duration: Duration,
308 easing: Easing,
309}
310
311impl<Value> Tween<Value> {
312 pub fn new(from: Value, to: Value, duration: Duration) -> Self {
315 Self::try_new(from, to, duration).unwrap_or_else(|error| panic!("{}", error.message))
316 }
317
318 pub fn try_new(
320 from: Value,
321 to: Value,
322 duration: Duration,
323 ) -> Result<Self, crate::ValidationError> {
324 if duration < Time::ZERO {
325 return Err(crate::ValidationError::invalid(
326 "tween",
327 "duration",
328 "tween duration must be nonnegative",
329 ));
330 }
331 Ok(Self {
332 from,
333 to,
334 duration,
335 easing: Easing::Linear,
336 })
337 }
338
339 pub fn easing(mut self, easing: Easing) -> Self {
342 self.easing = easing.validated();
343 self
344 }
345
346 pub fn try_easing(mut self, easing: Easing) -> Result<Self, crate::ValidationError> {
348 easing.try_validate()?;
349 self.easing = easing;
350 Ok(self)
351 }
352}
353
354impl<Value: Interpolate + Send + Sync> Animation<Value> for Tween<Value> {
355 fn sample(&self, time: Time) -> Value {
356 if time < Time::ZERO {
357 return self.from.clone();
358 }
359 if time >= self.duration {
360 return self.to.clone();
361 }
362 if time == Time::ZERO {
363 return self.from.clone();
364 }
365 let progress = self.easing.apply(time / self.duration);
366 self.from.interpolate(&self.to, progress)
367 }
368
369 fn duration(&self) -> Option<Duration> {
370 Some(self.duration)
371 }
372}
373
374#[derive(Clone, Debug)]
376pub struct Map<Source, Function, Input> {
377 source: Source,
378 function: Function,
379 input: PhantomData<fn(Input)>,
380}
381
382impl<Input, Output, Source, Function> Animation<Output> for Map<Source, Function, Input>
383where
384 Source: Animation<Input>,
385 Function: Fn(Input) -> Output + Send + Sync,
386{
387 fn sample(&self, time: Time) -> Output {
388 (self.function)(self.source.sample(time))
389 }
390
391 fn duration(&self) -> Option<Duration> {
392 self.source.duration()
393 }
394}
395
396#[derive(Clone, Debug)]
398pub struct Zip<First, Second> {
399 first: First,
400 second: Second,
401}
402
403impl<FirstValue, SecondValue, First, Second> Animation<(FirstValue, SecondValue)>
404 for Zip<First, Second>
405where
406 First: Animation<FirstValue>,
407 Second: Animation<SecondValue>,
408{
409 fn sample(&self, time: Time) -> (FirstValue, SecondValue) {
410 (self.first.sample(time), self.second.sample(time))
411 }
412
413 fn duration(&self) -> Option<Duration> {
414 Some(self.first.duration()?.max(self.second.duration()?))
415 }
416}
417
418fn checked_duration(duration: Option<Duration>) -> Option<Duration> {
419 if let Some(duration) = duration {
420 assert!(
421 duration >= Time::ZERO,
422 "animation duration must be nonnegative"
423 );
424 }
425 duration
426}
427
428fn finite_duration(duration: Option<Duration>, operation: &str) -> Duration {
429 checked_duration(duration).unwrap_or_else(|| panic!("{operation} requires a finite animation"))
430}
431
432fn cycle_duration(duration: Duration, count: u32) -> Duration {
433 Time::from_ticks(
434 duration
435 .ticks()
436 .checked_mul(i64::from(count))
437 .expect("animation duration overflow"),
438 )
439}
440
441#[derive(Clone, Debug)]
443pub struct Delay<Source> {
444 source: Source,
445 offset: Duration,
446 duration: Option<Duration>,
447}
448
449impl<Value, Source: Animation<Value>> Animation<Value> for Delay<Source> {
450 fn sample(&self, time: Time) -> Value {
451 let local = match self.duration {
452 Some(_) if time < self.offset => BEFORE_START,
453 Some(duration) if time >= duration => duration - self.offset,
454 _ => time - self.offset,
455 };
456 self.source.sample(local)
457 }
458
459 fn duration(&self) -> Option<Duration> {
460 self.duration
461 }
462}
463
464#[derive(Clone, Debug)]
466pub struct Speed<Source> {
467 source: Source,
468 factor: f64,
469 source_duration: Option<Duration>,
470 duration: Option<Duration>,
471}
472
473impl<Value, Source: Animation<Value>> Animation<Value> for Speed<Source> {
474 fn sample(&self, time: Time) -> Value {
475 let local = match (self.duration, self.source_duration) {
476 (Some(_), _) if time < Time::ZERO => BEFORE_START,
477 (Some(duration), Some(source_duration)) if time >= duration => source_duration,
478 (Some(_), Some(_)) if self.factor == 1.0 => time,
479 (Some(_), Some(source_duration)) => Time::from_ticks(
480 (time.ticks() as f64 * self.factor)
481 .round()
482 .clamp(0.0, source_duration.ticks() as f64) as i64,
483 ),
484 _ => time * self.factor,
485 };
486 self.source.sample(local)
487 }
488
489 fn duration(&self) -> Option<Duration> {
490 self.duration
491 }
492}
493
494#[derive(Clone, Debug)]
496pub struct Then<First, Next> {
497 first: First,
498 next: Next,
499 first_duration: Duration,
500 duration: Duration,
501}
502
503impl<Value, First: Animation<Value>, Next: Animation<Value>> Animation<Value>
504 for Then<First, Next>
505{
506 fn sample(&self, time: Time) -> Value {
507 if time < Time::ZERO {
508 self.first.sample(BEFORE_START)
509 } else if time >= self.duration {
510 self.next.sample(self.duration - self.first_duration)
511 } else if time < self.first_duration {
512 self.first.sample(time)
513 } else {
514 self.next.sample(time - self.first_duration)
515 }
516 }
517
518 fn duration(&self) -> Option<Duration> {
519 Some(self.duration)
520 }
521}
522
523#[derive(Clone, Debug)]
525pub struct Reverse<Source> {
526 source: Source,
527 duration: Duration,
528}
529
530impl<Value, Source: Animation<Value>> Animation<Value> for Reverse<Source> {
531 fn sample(&self, time: Time) -> Value {
532 let local = if time < Time::ZERO {
533 self.duration
534 } else if time >= self.duration {
535 BEFORE_START
536 } else {
537 self.duration - time
538 };
539 self.source.sample(local)
540 }
541
542 fn duration(&self) -> Option<Duration> {
543 Some(self.duration)
544 }
545}
546
547#[derive(Clone, Debug)]
549pub struct Repeat<Source> {
550 source: Source,
551 count: u32,
552 source_duration: Duration,
553 duration: Duration,
554}
555
556impl<Value, Source: Animation<Value>> Animation<Value> for Repeat<Source> {
557 fn sample(&self, time: Time) -> Value {
558 let local = if self.count == 0 || time < Time::ZERO {
559 BEFORE_START
560 } else if time >= self.duration {
561 self.source_duration
562 } else {
563 Time::from_ticks(time.ticks() % self.source_duration.ticks())
564 };
565 self.source.sample(local)
566 }
567
568 fn duration(&self) -> Option<Duration> {
569 Some(self.duration)
570 }
571}
572
573#[derive(Clone, Debug)]
575pub struct PingPong<Source> {
576 source: Source,
577 source_duration: Duration,
578 duration: Duration,
579}
580
581impl<Value, Source: Animation<Value>> Animation<Value> for PingPong<Source> {
582 fn sample(&self, time: Time) -> Value {
583 let local = if time < Time::ZERO || time >= self.duration {
584 BEFORE_START
585 } else if time <= self.source_duration {
586 time
587 } else {
588 self.duration - time
589 };
590 self.source.sample(local)
591 }
592
593 fn duration(&self) -> Option<Duration> {
594 Some(self.duration)
595 }
596}
597
598#[cfg(test)]
599mod interpolation_tests {
600 use super::*;
601
602 #[test]
603 fn interpolation_preserves_double_precision_progress() {
604 #[derive(Clone)]
605 struct CustomValue(f64);
606
607 impl Interpolate for CustomValue {
608 fn interpolate(&self, _target: &Self, progress: f64) -> Self {
609 Self(progress)
610 }
611 }
612
613 fn blend<Value: Interpolate>(from: Value, to: Value, progress: f64) -> Value {
614 from.interpolate(&to, progress)
615 }
616
617 let progress = 0.500_000_000_1;
618 assert_ne!(progress, f64::from(progress as f32));
619 assert_eq!(blend(0.0_f64, 1.0, progress), progress);
620 assert_eq!(
621 blend(CustomValue(0.0), CustomValue(1.0), progress).0,
622 progress
623 );
624 let point = blend(Point::ORIGIN, Point::new(1.0, 2.0), progress);
625 assert_eq!(point.x, progress);
626 assert_eq!(point.y, 2.0 * progress);
627 assert_eq!(
628 blend(Vector2::ZERO, Vector2::new(1.0, 0.0), progress).x,
629 progress
630 );
631 let transform = blend(
632 Transform::IDENTITY,
633 Transform::translate(1.0, 0.0),
634 progress,
635 );
636 assert_eq!(transform.apply(Point::ORIGIN).x, progress);
637 let array = blend([0.0_f64; 2], [1.0_f64; 2], progress);
638 assert_eq!(array, [progress; 2]);
639 assert_eq!(
640 blend((0.0_f64, 0.0_f64), (1.0, 2.0), progress),
641 (progress, 2.0 * progress)
642 );
643 }
644}