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}