Skip to main content

aura_anim_iced/state/
progress.rs

1use crate::{AnimationHandle, Duration};
2
3/// Active state transition metadata tracked by a [`StateAnimator`](crate::StateAnimator).
4#[derive(Debug, Clone, Copy, PartialEq)]
5pub struct ActiveStateTransition<S>
6where
7    S: Copy + Eq,
8{
9    handle: AnimationHandle,
10    from: S,
11    to: S,
12    started_at: Duration,
13    duration: Option<Duration>,
14}
15
16impl<S> ActiveStateTransition<S>
17where
18    S: Copy + Eq,
19{
20    pub(crate) const fn new(
21        handle: AnimationHandle,
22        from: S,
23        to: S,
24        started_at: Duration,
25        duration: Option<Duration>,
26    ) -> Self {
27        Self {
28            handle,
29            from,
30            to,
31            started_at,
32            duration,
33        }
34    }
35
36    /// Returns the runtime handle for this transition.
37    #[must_use]
38    pub const fn handle(&self) -> AnimationHandle {
39        self.handle
40    }
41
42    /// Returns the state this transition started from.
43    #[must_use]
44    pub const fn from(&self) -> S {
45        self.from
46    }
47
48    /// Returns the state this transition targets.
49    #[must_use]
50    pub const fn to(&self) -> S {
51        self.to
52    }
53
54    /// Returns the runtime timestamp when this transition started.
55    #[must_use]
56    pub const fn started_at(&self) -> Duration {
57        self.started_at
58    }
59
60    /// Returns the finite transition duration, if known.
61    #[must_use]
62    pub const fn duration(&self) -> Option<Duration> {
63        self.duration
64    }
65
66    /// Samples progress for this transition at a runtime timestamp.
67    #[must_use]
68    pub fn progress_at(&self, timestamp: Duration) -> StateTransitionProgress<S> {
69        let elapsed = timestamp
70            .checked_sub(self.started_at)
71            .unwrap_or(Duration::ZERO);
72        let progress = self
73            .duration
74            .map(|duration| normalized_progress(elapsed, duration));
75
76        StateTransitionProgress {
77            handle: self.handle,
78            from: self.from,
79            to: self.to,
80            elapsed,
81            duration: self.duration,
82            progress,
83        }
84    }
85}
86
87/// Sampled progress for an active state transition.
88#[derive(Debug, Clone, Copy, PartialEq)]
89pub struct StateTransitionProgress<S>
90where
91    S: Copy + Eq,
92{
93    handle: AnimationHandle,
94    from: S,
95    to: S,
96    elapsed: Duration,
97    duration: Option<Duration>,
98    progress: Option<f32>,
99}
100
101impl<S> StateTransitionProgress<S>
102where
103    S: Copy + Eq,
104{
105    /// Returns the runtime handle for this transition.
106    #[must_use]
107    pub const fn handle(&self) -> AnimationHandle {
108        self.handle
109    }
110
111    /// Returns the state this transition started from.
112    #[must_use]
113    pub const fn from(&self) -> S {
114        self.from
115    }
116
117    /// Returns the state this transition targets.
118    #[must_use]
119    pub const fn to(&self) -> S {
120        self.to
121    }
122
123    /// Returns elapsed runtime time since the transition started.
124    #[must_use]
125    pub const fn elapsed(&self) -> Duration {
126        self.elapsed
127    }
128
129    /// Returns the finite transition duration, if known.
130    #[must_use]
131    pub const fn duration(&self) -> Option<Duration> {
132        self.duration
133    }
134
135    /// Returns normalized progress in `[0.0, 1.0]` for finite transitions.
136    #[must_use]
137    pub const fn progress(&self) -> Option<f32> {
138        self.progress
139    }
140}
141
142fn normalized_progress(elapsed: Duration, duration: Duration) -> f32 {
143    let duration_ms = duration.as_millis();
144
145    if duration_ms <= 0.0 {
146        return 1.0;
147    }
148
149    #[allow(
150        clippy::cast_possible_truncation,
151        reason = "State transition progress is normalized for UI consumption."
152    )]
153    {
154        (elapsed.as_millis() / duration_ms).clamp(0.0, 1.0) as f32
155    }
156}