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}