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    /// Returns the value at the start of the tween.
6    ///
7    /// # Returns
8    ///
9    /// - `T`: The start value.
10    pub fn get_from(&self) -> T {
11        self.from
12    }
13
14    /// Returns the value at the end of the tween.
15    ///
16    /// # Returns
17    ///
18    /// - `T`: The end value.
19    pub fn get_to(&self) -> T {
20        self.to
21    }
22
23    /// Returns the easing curve applied to the normalized time.
24    ///
25    /// # Returns
26    ///
27    /// - `Easing`: The easing curve.
28    pub fn get_easing(&self) -> Easing {
29        self.easing
30    }
31
32    /// Returns the start delay in seconds before interpolation begins.
33    ///
34    /// # Returns
35    ///
36    /// - `f64`: The delay in seconds.
37    pub fn get_delay(&self) -> f64 {
38        self.delay
39    }
40
41    /// Returns the time elapsed since creation, including the delay phase.
42    ///
43    /// # Returns
44    ///
45    /// - `f64`: The elapsed time in seconds.
46    pub fn get_elapsed(&self) -> f64 {
47        self.elapsed
48    }
49
50    /// Returns a mutable reference to the elapsed time.
51    ///
52    /// # Returns
53    ///
54    /// - `&mut f64`: The mutable elapsed time in seconds.
55    pub fn get_elapsed_mut(&mut self) -> &mut f64 {
56        &mut self.elapsed
57    }
58
59    /// Returns what happens when the tween reaches the end of its duration.
60    ///
61    /// # Returns
62    ///
63    /// - `AnimationMode`: The completion mode.
64    pub fn get_mode(&self) -> AnimationMode {
65        self.mode
66    }
67
68    /// Returns the current playback direction for ping-pong mode.
69    ///
70    /// # Returns
71    ///
72    /// - `f64`: The playback direction (1.0 = forward, -1.0 = backward).
73    pub fn get_direction(&self) -> f64 {
74        self.direction
75    }
76
77    /// Returns a reference to the optional completion callback slot.
78    ///
79    /// # Returns
80    ///
81    /// - `&Option<Rc<dyn Fn()>>`: The completion callback slot.
82    pub fn try_get_on_complete(&self) -> &Option<Rc<dyn Fn()>> {
83        &self.on_complete
84    }
85
86    /// Sets the easing curve applied to the normalized time.
87    ///
88    /// # Arguments
89    ///
90    /// - `Easing`: The easing curve to apply.
91    pub fn set_easing(&mut self, easing: Easing) {
92        self.easing = easing;
93    }
94
95    /// Sets the start delay in seconds before interpolation begins.
96    ///
97    /// # Arguments
98    ///
99    /// - `f64`: The delay in seconds.
100    pub fn set_delay(&mut self, delay: f64) {
101        self.delay = delay;
102    }
103
104    /// Sets the time elapsed since creation.
105    ///
106    /// # Arguments
107    ///
108    /// - `f64`: The elapsed time in seconds.
109    pub fn set_elapsed(&mut self, elapsed: f64) {
110        self.elapsed = elapsed;
111    }
112
113    /// Sets the current playback state.
114    ///
115    /// # Arguments
116    ///
117    /// - `TweenState`: The new playback state.
118    pub fn set_state(&mut self, state: TweenState) {
119        self.state = state;
120    }
121
122    /// Sets what happens when the tween reaches the end of its duration.
123    ///
124    /// # Arguments
125    ///
126    /// - `AnimationMode`: The completion mode.
127    pub fn set_mode(&mut self, mode: AnimationMode) {
128        self.mode = mode;
129    }
130
131    /// Sets the current playback direction for ping-pong mode.
132    ///
133    /// # Arguments
134    ///
135    /// - `f64`: The playback direction (1.0 = forward, -1.0 = backward).
136    pub fn set_direction(&mut self, direction: f64) {
137        self.direction = direction;
138    }
139
140    /// Sets the optional completion callback.
141    ///
142    /// # Arguments
143    ///
144    /// - `Option<Rc<dyn Fn()>>`: The completion callback.
145    pub fn set_on_complete(&mut self, on_complete: Option<Rc<dyn Fn()>>) {
146        self.on_complete = on_complete;
147    }
148
149    /// Creates a new linear tween from `from` to `to` over `duration` seconds.
150    ///
151    /// The tween starts in the `Delayed` state only when a delay is later
152    /// attached via [`Tween::with_delay`]; by default it starts `Running`.
153    ///
154    /// # Arguments
155    ///
156    /// - `T: Interpolable + Copy` - The start value.
157    /// - `T: Interpolable + Copy` - The end value.
158    /// - `f64` - The interpolation duration in seconds.
159    ///
160    /// # Returns
161    ///
162    /// - `Tween<T: Interpolable + Copy>` - The new tween.
163    pub fn create(from: T, to: T, duration: f64) -> Tween<T> {
164        Tween {
165            from,
166            to,
167            duration: duration.max(0.0),
168            easing: Easing::Linear,
169            delay: 0.0,
170            elapsed: 0.0,
171            state: TweenState::Running,
172            mode: AnimationMode::Once,
173            direction: TWEEN_DIRECTION_FORWARD,
174            on_complete: None,
175        }
176    }
177
178    /// Sets the easing curve, replacing the default `Easing::Linear`.
179    ///
180    /// # Arguments
181    ///
182    /// - `Easing` - The easing curve to apply.
183    ///
184    /// # Returns
185    ///
186    /// - `Tween<T>` - The tween, for chaining.
187    pub fn with_easing(mut self, easing: Easing) -> Tween<T> {
188        self.set_easing(easing);
189        self
190    }
191
192    /// Sets a start delay in seconds. While the delay elapses the tween
193    /// reports its `from` value and stays in the `Delayed` state.
194    ///
195    /// # Arguments
196    ///
197    /// - `f64` - The delay in seconds.
198    ///
199    /// # Returns
200    ///
201    /// - `Tween<T>` - The tween, for chaining.
202    pub fn with_delay(mut self, delay: f64) -> Tween<T> {
203        self.set_delay(delay.max(0.0));
204        if self.get_delay() > 0.0
205            && self.get_state() == TweenState::Running
206            && self.get_elapsed() == 0.0
207        {
208            self.set_state(TweenState::Delayed);
209        }
210        self
211    }
212
213    /// Sets the completion mode (`Once`, `Loop`, or `PingPong`), replacing
214    /// the default `AnimationMode::Once`.
215    ///
216    /// # Arguments
217    ///
218    /// - `AnimationMode` - The completion mode.
219    ///
220    /// # Returns
221    ///
222    /// - `Tween<T>` - The tween, for chaining.
223    pub fn with_mode(mut self, mode: AnimationMode) -> Tween<T> {
224        self.set_mode(mode);
225        self
226    }
227
228    /// Attaches a callback fired every time the tween completes a cycle
229    /// (once for `Once` mode, every wrap for `Loop` and `PingPong`).
230    ///
231    /// # Arguments
232    ///
233    /// - `Rc<dyn Fn()>` - The completion callback.
234    ///
235    /// # Returns
236    ///
237    /// - `Tween<T>` - The tween, for chaining.
238    pub fn with_on_complete(mut self, on_complete: Rc<dyn Fn()>) -> Tween<T> {
239        self.set_on_complete(Some(on_complete));
240        self
241    }
242
243    /// Advances the tween by the given delta time and returns the current
244    /// eased value.
245    ///
246    /// Has no effect while the tween is `Paused` or `Finished`.
247    ///
248    /// # Arguments
249    ///
250    /// - `f64` - The time elapsed since the last update, in seconds.
251    ///
252    /// # Returns
253    ///
254    /// - `T: Interpolable + Copy` - The current interpolated value.
255    pub fn update(&mut self, delta_time: f64) -> T {
256        if self.get_state() == TweenState::Paused || self.get_state() == TweenState::Finished {
257            return self.value();
258        }
259        *self.get_elapsed_mut() += delta_time.max(0.0);
260        if self.get_state() == TweenState::Delayed {
261            if self.get_elapsed() < self.get_delay() {
262                return self.get_from();
263            }
264            self.set_state(TweenState::Running);
265        }
266        let active_elapsed: f64 = self.get_elapsed() - self.get_delay();
267        if self.get_duration() <= 0.0 || active_elapsed >= self.get_duration() {
268            self.complete_cycle(active_elapsed);
269        }
270        self.value()
271    }
272
273    /// Returns the current interpolated value without advancing time.
274    ///
275    /// # Returns
276    ///
277    /// - `T: Interpolable + Copy` - The current eased value.
278    pub fn value(&self) -> T {
279        let progress: f64 = self.eased_progress();
280        if self.get_direction() == TWEEN_DIRECTION_BACKWARD {
281            return self.get_from().lerp(self.get_to(), 1.0 - progress);
282        }
283        self.get_from().lerp(self.get_to(), progress)
284    }
285
286    /// Returns the eased progress of the current cycle in the range 0.0 to 1.0.
287    ///
288    /// # Returns
289    ///
290    /// - `f64` - The eased progress.
291    pub fn eased_progress(&self) -> f64 {
292        if self.get_duration() <= 0.0 {
293            return 1.0;
294        }
295        let active_elapsed: f64 = (self.get_elapsed() - self.get_delay()).max(0.0);
296        let raw: f64 = (active_elapsed / self.get_duration()).min(1.0);
297        self.get_easing().evaluate(raw)
298    }
299
300    /// Returns the raw (uneased) progress of the current cycle.
301    ///
302    /// # Returns
303    ///
304    /// - `f64` - The raw progress in the range 0.0 to 1.0.
305    pub fn raw_progress(&self) -> f64 {
306        if self.get_duration() <= 0.0 {
307            return 1.0;
308        }
309        ((self.get_elapsed() - self.get_delay()).max(0.0) / self.get_duration()).min(1.0)
310    }
311
312    /// Pauses the tween.
313    pub fn pause(&mut self) {
314        if self.get_state() == TweenState::Running || self.get_state() == TweenState::Delayed {
315            self.set_state(TweenState::Paused);
316        }
317    }
318
319    /// Resumes a paused tween.
320    pub fn resume(&mut self) {
321        if self.get_state() == TweenState::Paused {
322            if self.get_elapsed() < self.get_delay() {
323                self.set_state(TweenState::Delayed);
324            } else {
325                self.set_state(TweenState::Running);
326            }
327        }
328    }
329
330    /// Resets the tween to its initial state so it can be replayed.
331    pub fn reset(&mut self) {
332        self.set_elapsed(0.0);
333        self.set_direction(TWEEN_DIRECTION_FORWARD);
334        self.set_state(if self.get_delay() > 0.0 {
335            TweenState::Delayed
336        } else {
337            TweenState::Running
338        });
339    }
340
341    /// Returns whether the tween has finished (`AnimationMode::Once` only).
342    ///
343    /// # Returns
344    ///
345    /// - `bool` - True if the tween is finished.
346    pub fn is_finished(&self) -> bool {
347        self.get_state() == TweenState::Finished
348    }
349
350    /// Returns the current playback state.
351    ///
352    /// # Returns
353    ///
354    /// - `TweenState` - The playback state.
355    pub fn get_state(&self) -> TweenState {
356        self.state
357    }
358
359    /// Returns the configured duration in seconds.
360    ///
361    /// # Returns
362    ///
363    /// - `f64` - The duration.
364    pub fn get_duration(&self) -> f64 {
365        self.duration
366    }
367
368    /// Handles a completed cycle according to the configured mode.
369    ///
370    /// # Arguments
371    ///
372    /// - `f64` - The active (post-delay) elapsed time at completion.
373    fn complete_cycle(&mut self, active_elapsed: f64) {
374        let overflow: f64 = if self.get_duration() > 0.0 {
375            active_elapsed % self.get_duration()
376        } else {
377            0.0
378        };
379        match self.get_mode() {
380            AnimationMode::Once => {
381                self.set_elapsed(self.get_delay() + self.get_duration());
382                self.set_state(TweenState::Finished);
383            }
384            AnimationMode::Loop => {
385                self.set_elapsed(self.get_delay() + overflow);
386            }
387            AnimationMode::PingPong => {
388                self.set_elapsed(self.get_delay() + overflow);
389                self.set_direction(-self.get_direction());
390            }
391        }
392        if let Some(on_complete) = self.try_get_on_complete() {
393            on_complete();
394        }
395    }
396}
397
398/// Forwards `Tween::update` through the [`Updatable`] trait so tweens can
399/// participate in the same generic update loop as entities, animators,
400/// scenes, and physics worlds.
401impl<T: Interpolable + Copy> Updatable for Tween<T> {
402    /// Advances the simulation by `delta_time` seconds.
403    ///
404    /// # Arguments
405    ///
406    /// - `f64` - Seconds elapsed since the previous update.
407    fn update(&mut self, delta_time: f64) {
408        let _: T = Tween::update(self, delta_time);
409    }
410}
411
412impl<T: Interpolable + Copy> Clone for Tween<T> {
413    /// Clones the [`Tween`] by reusing shared, cheap-to-clone state where possible.
414    ///
415    /// # Returns
416    ///
417    /// - `Tween<T>` - A clone that shares the same underlying storage where applicable.
418    fn clone(&self) -> Tween<T> {
419        Tween {
420            from: self.get_from(),
421            to: self.get_to(),
422            duration: self.get_duration(),
423            easing: self.get_easing(),
424            delay: self.get_delay(),
425            elapsed: self.get_elapsed(),
426            state: self.get_state(),
427            mode: self.get_mode(),
428            direction: self.get_direction(),
429            on_complete: self.try_get_on_complete().clone(),
430        }
431    }
432}
433
434impl<T: Interpolable + Copy + Debug> Debug for Tween<T> {
435    /// Formats the [`Tween`] via the supplied formatter.
436    ///
437    /// # Arguments
438    ///
439    /// - `&mut Formatter<'_>` - The formatter receiving the formatted output.
440    ///
441    /// # Returns
442    ///
443    /// - `fmt::Result` - Result of the formatting operation.
444    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
445        formatter
446            .debug_struct("Tween")
447            .field("from", &self.from)
448            .field("to", &self.to)
449            .field("duration", &self.duration)
450            .field("easing", &self.easing)
451            .field("delay", &self.delay)
452            .field("elapsed", &self.elapsed)
453            .field("state", &self.state)
454            .field("mode", &self.mode)
455            .field("direction", &self.direction)
456            .finish_non_exhaustive()
457    }
458}