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}