argui_animation/
timing.rs1use crate::Duration;
2use std::fmt;
3
4#[derive(Clone, Copy, Debug, PartialEq)]
5pub enum Iterations {
6 Finite(f64),
7 Infinite,
8}
9
10impl Default for Iterations {
11 fn default() -> Self {
12 Self::Finite(1.0)
13 }
14}
15
16#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
17pub enum Direction {
18 #[default]
19 Normal,
20 Reverse,
21 Alternate,
22 AlternateReverse,
23}
24
25#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
26pub enum FillMode {
27 #[default]
28 None,
29 Forwards,
30 Backwards,
31 Both,
32}
33
34impl FillMode {
35 pub(crate) const fn fills_before(self) -> bool {
36 matches!(self, Self::Backwards | Self::Both)
37 }
38
39 pub(crate) const fn fills_after(self) -> bool {
40 matches!(self, Self::Forwards | Self::Both)
41 }
42}
43
44#[derive(Clone, Copy, Debug, PartialEq)]
45pub struct Timing {
46 pub duration: Duration,
47 pub delay: Duration,
48 pub end_delay: Duration,
49 pub iterations: Iterations,
50 pub direction: Direction,
51 pub fill: FillMode,
52 pub playback_rate: f64,
53}
54
55impl Timing {
56 #[must_use]
57 pub const fn new(duration: Duration) -> Self {
58 Self {
59 duration,
60 delay: Duration::ZERO,
61 end_delay: Duration::ZERO,
62 iterations: Iterations::Finite(1.0),
63 direction: Direction::Normal,
64 fill: FillMode::None,
65 playback_rate: 1.0,
66 }
67 }
68
69 #[must_use]
70 pub const fn delay(mut self, delay: Duration) -> Self {
71 self.delay = delay;
72 self
73 }
74
75 #[must_use]
76 pub const fn end_delay(mut self, end_delay: Duration) -> Self {
77 self.end_delay = end_delay;
78 self
79 }
80
81 #[must_use]
82 pub const fn iterations(mut self, iterations: Iterations) -> Self {
83 self.iterations = iterations;
84 self
85 }
86
87 #[must_use]
88 pub const fn direction(mut self, direction: Direction) -> Self {
89 self.direction = direction;
90 self
91 }
92
93 #[must_use]
94 pub const fn fill(mut self, fill: FillMode) -> Self {
95 self.fill = fill;
96 self
97 }
98
99 #[must_use]
100 pub const fn playback_rate(mut self, playback_rate: f64) -> Self {
101 self.playback_rate = playback_rate;
102 self
103 }
104
105 pub(crate) fn validate(self) -> Result<Self, TimingError> {
106 let iterations_valid = match self.iterations {
107 Iterations::Finite(value) => value.is_finite() && value > 0.0,
108 Iterations::Infinite => true,
109 };
110 if self.duration == Duration::ZERO {
111 Err(TimingError::ZeroDuration)
112 } else if !iterations_valid {
113 Err(TimingError::InvalidIterations)
114 } else if !self.playback_rate.is_finite() || self.playback_rate == 0.0 {
115 Err(TimingError::InvalidPlaybackRate)
116 } else {
117 Ok(self)
118 }
119 }
120
121 pub(crate) fn active_seconds(self) -> f64 {
122 match self.iterations {
123 Iterations::Finite(iterations) => self.duration.as_secs_f64() * iterations,
124 Iterations::Infinite => f64::INFINITY,
125 }
126 }
127
128 pub(crate) fn total_seconds(self) -> f64 {
129 self.delay.as_secs_f64() + self.active_seconds() + self.end_delay.as_secs_f64()
130 }
131}
132
133#[derive(Clone, Copy, Debug, Eq, PartialEq)]
134pub enum TimingError {
135 ZeroDuration,
136 InvalidIterations,
137 InvalidPlaybackRate,
138 InvalidKeyframes,
139}
140
141impl fmt::Display for TimingError {
142 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
143 match self {
144 Self::ZeroDuration => {
145 formatter.write_str("animation duration must be greater than zero")
146 }
147 Self::InvalidIterations => {
148 formatter.write_str("animation iterations must be positive and finite, or infinite")
149 }
150 Self::InvalidPlaybackRate => {
151 formatter.write_str("animation playback rate must be finite and non-zero")
152 }
153 Self::InvalidKeyframes => {
154 formatter.write_str("keyframes must be finite, sorted, and cover offsets 0..=1")
155 }
156 }
157 }
158}
159
160impl std::error::Error for TimingError {}