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` - The start value.
157    /// - `f64` - The interpolation duration in seconds.
158    ///
159    /// # Returns
160    ///
161    /// - `Tween<T>` - The new tween.
162    pub fn create(from: T, to: T, duration: f64) -> Tween<T> {
163        Tween {
164            from,
165            to,
166            duration: duration.max(0.0),
167            easing: Easing::Linear,
168            delay: 0.0,
169            elapsed: 0.0,
170            state: TweenState::Running,
171            mode: AnimationMode::Once,
172            direction: TWEEN_DIRECTION_FORWARD,
173            on_complete: None,
174        }
175    }
176
177    /// Sets the easing curve, replacing the default `Easing::Linear`.
178    ///
179    /// # Arguments
180    ///
181    /// - `Easing` - The easing curve to apply.
182    ///
183    /// # Returns
184    ///
185    /// - `Tween<T>` - The tween, for chaining.
186    pub fn with_easing(mut self, easing: Easing) -> Tween<T> {
187        self.set_easing(easing);
188        self
189    }
190
191    /// Sets a start delay in seconds. While the delay elapses the tween
192    /// reports its `from` value and stays in the `Delayed` state.
193    ///
194    /// # Arguments
195    ///
196    /// - `f64` - The delay in seconds.
197    ///
198    /// # Returns
199    ///
200    /// - `Tween<T>` - The tween, for chaining.
201    pub fn with_delay(mut self, delay: f64) -> Tween<T> {
202        self.set_delay(delay.max(0.0));
203        if self.get_delay() > 0.0
204            && self.get_state() == TweenState::Running
205            && self.get_elapsed() == 0.0
206        {
207            self.set_state(TweenState::Delayed);
208        }
209        self
210    }
211
212    /// Sets the completion mode (`Once`, `Loop`, or `PingPong`), replacing
213    /// the default `AnimationMode::Once`.
214    ///
215    /// # Arguments
216    ///
217    /// - `AnimationMode` - The completion mode.
218    ///
219    /// # Returns
220    ///
221    /// - `Tween<T>` - The tween, for chaining.
222    pub fn with_mode(mut self, mode: AnimationMode) -> Tween<T> {
223        self.set_mode(mode);
224        self
225    }
226
227    /// Attaches a callback fired every time the tween completes a cycle
228    /// (once for `Once` mode, every wrap for `Loop` and `PingPong`).
229    ///
230    /// # Arguments
231    ///
232    /// - `Rc<dyn Fn()>` - The completion callback.
233    ///
234    /// # Returns
235    ///
236    /// - `Tween<T>` - The tween, for chaining.
237    pub fn with_on_complete(mut self, on_complete: Rc<dyn Fn()>) -> Tween<T> {
238        self.set_on_complete(Some(on_complete));
239        self
240    }
241
242    /// Advances the tween by the given delta time and returns the current
243    /// eased value.
244    ///
245    /// Has no effect while the tween is `Paused` or `Finished`.
246    ///
247    /// # Arguments
248    ///
249    /// - `f64` - The time elapsed since the last update, in seconds.
250    ///
251    /// # Returns
252    ///
253    /// - `T` - The current interpolated value.
254    pub fn update(&mut self, delta_time: f64) -> T {
255        if self.get_state() == TweenState::Paused || self.get_state() == TweenState::Finished {
256            return self.value();
257        }
258        *self.get_elapsed_mut() += delta_time.max(0.0);
259        if self.get_state() == TweenState::Delayed {
260            if self.get_elapsed() < self.get_delay() {
261                return self.get_from();
262            }
263            self.set_state(TweenState::Running);
264        }
265        let active_elapsed: f64 = self.get_elapsed() - self.get_delay();
266        if self.get_duration() <= 0.0 || active_elapsed >= self.get_duration() {
267            self.complete_cycle(active_elapsed);
268        }
269        self.value()
270    }
271
272    /// Returns the current interpolated value without advancing time.
273    ///
274    /// # Returns
275    ///
276    /// - `T` - The current eased value.
277    pub fn value(&self) -> T {
278        let progress: f64 = self.eased_progress();
279        if self.get_direction() == TWEEN_DIRECTION_BACKWARD {
280            return self.get_from().lerp(self.get_to(), 1.0 - progress);
281        }
282        self.get_from().lerp(self.get_to(), progress)
283    }
284
285    /// Returns the eased progress of the current cycle in the range 0.0 to 1.0.
286    ///
287    /// # Returns
288    ///
289    /// - `f64` - The eased progress.
290    pub fn eased_progress(&self) -> f64 {
291        if self.get_duration() <= 0.0 {
292            return 1.0;
293        }
294        let active_elapsed: f64 = (self.get_elapsed() - self.get_delay()).max(0.0);
295        let raw: f64 = (active_elapsed / self.get_duration()).min(1.0);
296        self.get_easing().evaluate(raw)
297    }
298
299    /// Returns the raw (uneased) progress of the current cycle.
300    ///
301    /// # Returns
302    ///
303    /// - `f64` - The raw progress in the range 0.0 to 1.0.
304    pub fn raw_progress(&self) -> f64 {
305        if self.get_duration() <= 0.0 {
306            return 1.0;
307        }
308        ((self.get_elapsed() - self.get_delay()).max(0.0) / self.get_duration()).min(1.0)
309    }
310
311    /// Pauses the tween.
312    pub fn pause(&mut self) {
313        if self.get_state() == TweenState::Running || self.get_state() == TweenState::Delayed {
314            self.set_state(TweenState::Paused);
315        }
316    }
317
318    /// Resumes a paused tween.
319    pub fn resume(&mut self) {
320        if self.get_state() == TweenState::Paused {
321            if self.get_elapsed() < self.get_delay() {
322                self.set_state(TweenState::Delayed);
323            } else {
324                self.set_state(TweenState::Running);
325            }
326        }
327    }
328
329    /// Resets the tween to its initial state so it can be replayed.
330    pub fn reset(&mut self) {
331        self.set_elapsed(0.0);
332        self.set_direction(TWEEN_DIRECTION_FORWARD);
333        self.set_state(if self.get_delay() > 0.0 {
334            TweenState::Delayed
335        } else {
336            TweenState::Running
337        });
338    }
339
340    /// Returns whether the tween has finished (`AnimationMode::Once` only).
341    ///
342    /// # Returns
343    ///
344    /// - `bool` - True if the tween is finished.
345    pub fn is_finished(&self) -> bool {
346        self.get_state() == TweenState::Finished
347    }
348
349    /// Returns the current playback state.
350    ///
351    /// # Returns
352    ///
353    /// - `TweenState` - The playback state.
354    pub fn get_state(&self) -> TweenState {
355        self.state
356    }
357
358    /// Returns the configured duration in seconds.
359    ///
360    /// # Returns
361    ///
362    /// - `f64` - The duration.
363    pub fn get_duration(&self) -> f64 {
364        self.duration
365    }
366
367    /// Handles a completed cycle according to the configured mode.
368    ///
369    /// # Arguments
370    ///
371    /// - `f64` - The active (post-delay) elapsed time at completion.
372    fn complete_cycle(&mut self, active_elapsed: f64) {
373        let overflow: f64 = if self.get_duration() > 0.0 {
374            active_elapsed % self.get_duration()
375        } else {
376            0.0
377        };
378        match self.get_mode() {
379            AnimationMode::Once => {
380                self.set_elapsed(self.get_delay() + self.get_duration());
381                self.set_state(TweenState::Finished);
382            }
383            AnimationMode::Loop => {
384                self.set_elapsed(self.get_delay() + overflow);
385            }
386            AnimationMode::PingPong => {
387                self.set_elapsed(self.get_delay() + overflow);
388                self.set_direction(-self.get_direction());
389            }
390        }
391        if let Some(on_complete) = self.try_get_on_complete() {
392            on_complete();
393        }
394    }
395}
396
397/// Forwards `Tween::update` through the [`Updatable`] trait so tweens can
398/// participate in the same generic update loop as entities, animators,
399/// scenes, and physics worlds.
400impl<T: Interpolable + Copy> Updatable for Tween<T> {
401    /// Advances the simulation by `delta_time` seconds.
402    ///
403    /// # Arguments
404    ///
405    /// - `f64` - Seconds elapsed since the previous update.
406    fn update(&mut self, delta_time: f64) {
407        let _: T = Tween::update(self, delta_time);
408    }
409}
410
411impl<T: Interpolable + Copy> Clone for Tween<T> {
412    /// Clones the [`Tween`] by reusing shared, cheap-to-clone state where possible.
413    ///
414    /// # Returns
415    ///
416    /// - `Tween<T>` - A clone that shares the same underlying storage where applicable.
417    fn clone(&self) -> Tween<T> {
418        Tween {
419            from: self.get_from(),
420            to: self.get_to(),
421            duration: self.get_duration(),
422            easing: self.get_easing(),
423            delay: self.get_delay(),
424            elapsed: self.get_elapsed(),
425            state: self.get_state(),
426            mode: self.get_mode(),
427            direction: self.get_direction(),
428            on_complete: self.try_get_on_complete().clone(),
429        }
430    }
431}
432
433impl<T: Interpolable + Copy + Debug> Debug for Tween<T> {
434    /// Formats the [`Tween`] via the supplied formatter.
435    ///
436    /// # Arguments
437    ///
438    /// - `&mut Formatter<'_>` - The formatter receiving the formatted output.
439    ///
440    /// # Returns
441    ///
442    /// - `fmt::Result` - Result of the formatting operation.
443    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
444        formatter
445            .debug_struct(TWEEN_DEBUG_NAME)
446            .field(TWEEN_FIELD_FROM, &self.from)
447            .field(TWEEN_FIELD_TO, &self.to)
448            .field(TWEEN_FIELD_DURATION, &self.duration)
449            .field(TWEEN_FIELD_EASING, &self.easing)
450            .field(TWEEN_FIELD_DELAY, &self.delay)
451            .field(TWEEN_FIELD_ELAPSED, &self.elapsed)
452            .field(TWEEN_FIELD_STATE, &self.state)
453            .field(TWEEN_FIELD_MODE, &self.mode)
454            .field(TWEEN_FIELD_DIRECTION, &self.direction)
455            .finish_non_exhaustive()
456    }
457}