Skip to main content

euv_engine/tween/
impl.rs

1use super::*;
2
3/// Implements creation, playback control, and value sampling for `Tween`.
4impl<T: Interpolable + Copy> Tween<T> {
5    /// Creates a new linear tween from `from` to `to` over `duration` seconds.
6    ///
7    /// The tween starts in the `Delayed` state only when a delay is later
8    /// attached via [`Tween::with_delay`]; by default it starts `Running`.
9    ///
10    /// # Arguments
11    ///
12    /// - `T` - The start value.
13    /// - `T` - The end value.
14    /// - `f64` - The interpolation duration in seconds.
15    ///
16    /// # Returns
17    ///
18    /// - `Tween<T>` - The new tween.
19    pub fn create(from: T, to: T, duration: f64) -> Tween<T> {
20        Tween {
21            from,
22            to,
23            duration: duration.max(0.0),
24            easing: Easing::Linear,
25            delay: 0.0,
26            elapsed: 0.0,
27            state: TweenState::Running,
28            mode: AnimationMode::Once,
29            direction: TWEEN_DIRECTION_FORWARD,
30            on_complete: None,
31        }
32    }
33
34    /// Sets the easing curve, replacing the default `Easing::Linear`.
35    ///
36    /// # Arguments
37    ///
38    /// - `Easing` - The easing curve to apply.
39    ///
40    /// # Returns
41    ///
42    /// - `Tween<T>` - The tween, for chaining.
43    pub fn with_easing(mut self, easing: Easing) -> Tween<T> {
44        self.easing = easing;
45        self
46    }
47
48    /// Sets a start delay in seconds. While the delay elapses the tween
49    /// reports its `from` value and stays in the `Delayed` state.
50    ///
51    /// # Arguments
52    ///
53    /// - `f64` - The delay in seconds.
54    ///
55    /// # Returns
56    ///
57    /// - `Tween<T>` - The tween, for chaining.
58    pub fn with_delay(mut self, delay: f64) -> Tween<T> {
59        self.delay = delay.max(0.0);
60        if self.delay > 0.0 && self.state == TweenState::Running && self.elapsed == 0.0 {
61            self.state = TweenState::Delayed;
62        }
63        self
64    }
65
66    /// Sets the completion mode (`Once`, `Loop`, or `PingPong`), replacing
67    /// the default `AnimationMode::Once`.
68    ///
69    /// # Arguments
70    ///
71    /// - `AnimationMode` - The completion mode.
72    ///
73    /// # Returns
74    ///
75    /// - `Tween<T>` - The tween, for chaining.
76    pub fn with_mode(mut self, mode: AnimationMode) -> Tween<T> {
77        self.mode = mode;
78        self
79    }
80
81    /// Attaches a callback fired every time the tween completes a cycle
82    /// (once for `Once` mode, every wrap for `Loop` and `PingPong`).
83    ///
84    /// # Arguments
85    ///
86    /// - `Rc<dyn Fn()>` - The completion callback.
87    ///
88    /// # Returns
89    ///
90    /// - `Tween<T>` - The tween, for chaining.
91    pub fn with_on_complete(mut self, on_complete: Rc<dyn Fn()>) -> Tween<T> {
92        self.on_complete = Some(on_complete);
93        self
94    }
95
96    /// Advances the tween by the given delta time and returns the current
97    /// eased value.
98    ///
99    /// Has no effect while the tween is `Paused` or `Finished`.
100    ///
101    /// # Arguments
102    ///
103    /// - `f64` - The time elapsed since the last update, in seconds.
104    ///
105    /// # Returns
106    ///
107    /// - `T` - The current interpolated value.
108    pub fn update(&mut self, delta_time: f64) -> T {
109        if self.state == TweenState::Paused || self.state == TweenState::Finished {
110            return self.value();
111        }
112        self.elapsed += delta_time.max(0.0);
113        if self.state == TweenState::Delayed {
114            if self.elapsed < self.delay {
115                return self.from;
116            }
117            self.state = TweenState::Running;
118        }
119        let active_elapsed: f64 = self.elapsed - self.delay;
120        if self.duration <= 0.0 || active_elapsed >= self.duration {
121            self.complete_cycle(active_elapsed);
122        }
123        self.value()
124    }
125
126    /// Returns the current interpolated value without advancing time.
127    ///
128    /// # Returns
129    ///
130    /// - `T` - The current eased value.
131    pub fn value(&self) -> T {
132        let progress: f64 = self.eased_progress();
133        if self.direction == TWEEN_DIRECTION_BACKWARD {
134            return self.from.lerp(self.to, 1.0 - progress);
135        }
136        self.from.lerp(self.to, progress)
137    }
138
139    /// Returns the eased progress of the current cycle in the range 0.0 to 1.0.
140    ///
141    /// # Returns
142    ///
143    /// - `f64` - The eased progress.
144    pub fn eased_progress(&self) -> f64 {
145        if self.duration <= 0.0 {
146            return 1.0;
147        }
148        let active_elapsed: f64 = (self.elapsed - self.delay).max(0.0);
149        let raw: f64 = (active_elapsed / self.duration).min(1.0);
150        self.easing.evaluate(raw)
151    }
152
153    /// Returns the raw (uneased) progress of the current cycle.
154    ///
155    /// # Returns
156    ///
157    /// - `f64` - The raw progress in the range 0.0 to 1.0.
158    pub fn raw_progress(&self) -> f64 {
159        if self.duration <= 0.0 {
160            return 1.0;
161        }
162        ((self.elapsed - self.delay).max(0.0) / self.duration).min(1.0)
163    }
164
165    /// Pauses the tween.
166    pub fn pause(&mut self) {
167        if self.state == TweenState::Running || self.state == TweenState::Delayed {
168            self.state = TweenState::Paused;
169        }
170    }
171
172    /// Resumes a paused tween.
173    pub fn resume(&mut self) {
174        if self.state == TweenState::Paused {
175            if self.elapsed < self.delay {
176                self.state = TweenState::Delayed;
177            } else {
178                self.state = TweenState::Running;
179            }
180        }
181    }
182
183    /// Resets the tween to its initial state so it can be replayed.
184    pub fn reset(&mut self) {
185        self.elapsed = 0.0;
186        self.direction = TWEEN_DIRECTION_FORWARD;
187        self.state = if self.delay > 0.0 {
188            TweenState::Delayed
189        } else {
190            TweenState::Running
191        };
192    }
193
194    /// Returns whether the tween has finished (`AnimationMode::Once` only).
195    ///
196    /// # Returns
197    ///
198    /// - `bool` - True if the tween is finished.
199    pub fn is_finished(&self) -> bool {
200        self.state == TweenState::Finished
201    }
202
203    /// Returns the current playback state.
204    ///
205    /// # Returns
206    ///
207    /// - `TweenState` - The playback state.
208    pub fn get_state(&self) -> TweenState {
209        self.state
210    }
211
212    /// Returns the configured duration in seconds.
213    ///
214    /// # Returns
215    ///
216    /// - `f64` - The duration.
217    pub fn get_duration(&self) -> f64 {
218        self.duration
219    }
220
221    /// Handles a completed cycle according to the configured mode.
222    ///
223    /// # Arguments
224    ///
225    /// - `f64` - The active (post-delay) elapsed time at completion.
226    fn complete_cycle(&mut self, active_elapsed: f64) {
227        let overflow: f64 = if self.duration > 0.0 {
228            active_elapsed % self.duration
229        } else {
230            0.0
231        };
232        match self.mode {
233            AnimationMode::Once => {
234                self.elapsed = self.delay + self.duration;
235                self.state = TweenState::Finished;
236            }
237            AnimationMode::Loop => {
238                self.elapsed = self.delay + overflow;
239            }
240            AnimationMode::PingPong => {
241                self.elapsed = self.delay + overflow;
242                self.direction = -self.direction;
243            }
244        }
245        if let Some(on_complete) = &self.on_complete {
246            on_complete();
247        }
248    }
249}
250
251/// Forwards `Tween::update` through the [`Updatable`] trait so tweens can
252/// participate in the same generic update loop as entities, animators,
253/// scenes, and physics worlds.
254impl<T: Interpolable + Copy> Updatable for Tween<T> {
255    fn update(&mut self, delta_time: f64) {
256        let _: T = Tween::update(self, delta_time);
257    }
258}