1use core::time::Duration;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
11pub enum TimerMode {
12 #[default]
14 Once,
15 Repeating,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Default)]
21pub struct Stopwatch {
22 elapsed: f32,
23 paused: bool,
24}
25
26impl Stopwatch {
27 pub const fn new() -> Self {
29 Self {
30 elapsed: 0.0,
31 paused: false,
32 }
33 }
34
35 #[inline]
37 pub fn tick(&mut self, dt: f32) -> &mut Self {
38 if !self.paused {
39 self.elapsed += dt;
40 }
41 self
42 }
43
44 #[inline]
46 pub fn tick_duration(&mut self, duration: Duration) -> &mut Self {
47 self.tick(duration.as_secs_f32())
48 }
49
50 #[inline]
52 pub fn elapsed_secs(&self) -> f32 {
53 self.elapsed
54 }
55
56 #[inline]
58 pub fn elapsed(&self) -> Duration {
59 Duration::from_secs_f32(self.elapsed.max(0.0))
60 }
61
62 #[inline]
64 pub fn set_elapsed(&mut self, time: f32) {
65 self.elapsed = time;
66 }
67
68 #[inline]
70 pub fn reset(&mut self) {
71 self.elapsed = 0.0;
72 }
73
74 #[inline]
76 pub fn pause(&mut self) {
77 self.paused = true;
78 }
79
80 #[inline]
82 pub fn unpause(&mut self) {
83 self.paused = false;
84 }
85
86 #[inline]
88 pub fn is_paused(&self) -> bool {
89 self.paused
90 }
91}
92
93#[derive(Debug, Clone, Copy, PartialEq)]
95pub struct Timer {
96 stopwatch: Stopwatch,
97 duration: f32,
98 mode: TimerMode,
99 just_finished: bool,
100}
101
102impl Default for Timer {
103 fn default() -> Self {
104 Self::from_seconds(1.0, TimerMode::Once)
105 }
106}
107
108impl Timer {
109 pub const fn from_seconds(duration: f32, mode: TimerMode) -> Self {
111 Self {
112 stopwatch: Stopwatch::new(),
113 duration: if duration > 0.0 { duration } else { 0.0 },
114 mode,
115 just_finished: false,
116 }
117 }
118
119 pub fn new(duration: Duration, mode: TimerMode) -> Self {
121 Self::from_seconds(duration.as_secs_f32(), mode)
122 }
123
124 pub fn tick(&mut self, dt: f32) -> &mut Self {
126 if self.stopwatch.is_paused() {
127 self.just_finished = false;
128 return self;
129 }
130
131 if self.mode == TimerMode::Once && self.is_finished() {
132 self.just_finished = false;
133 return self;
134 }
135
136 self.stopwatch.tick(dt);
137
138 if self.stopwatch.elapsed_secs() >= self.duration {
139 self.just_finished = true;
140 match self.mode {
141 TimerMode::Once => {
142 self.stopwatch.set_elapsed(self.duration);
143 }
144 TimerMode::Repeating => {
145 if self.duration > 0.0 {
146 let rem = self.stopwatch.elapsed_secs() % self.duration;
147 self.stopwatch.set_elapsed(rem);
148 } else {
149 self.stopwatch.set_elapsed(0.0);
150 }
151 }
152 }
153 } else {
154 self.just_finished = false;
155 }
156
157 self
158 }
159
160 #[inline]
162 pub fn tick_duration(&mut self, duration: Duration) -> &mut Self {
163 self.tick(duration.as_secs_f32())
164 }
165
166 #[inline]
168 pub fn is_finished(&self) -> bool {
169 self.stopwatch.elapsed_secs() >= self.duration
170 }
171
172 #[inline]
174 pub fn just_finished(&self) -> bool {
175 self.just_finished
176 }
177
178 #[inline]
180 pub fn fraction(&self) -> f32 {
181 if self.duration <= 0.0 {
182 1.0
183 } else {
184 (self.stopwatch.elapsed_secs() / self.duration).clamp(0.0, 1.0)
185 }
186 }
187
188 #[inline]
190 pub fn remaining_secs(&self) -> f32 {
191 (self.duration - self.stopwatch.elapsed_secs()).max(0.0)
192 }
193
194 #[inline]
196 pub fn reset(&mut self) {
197 self.stopwatch.reset();
198 self.just_finished = false;
199 }
200
201 #[inline]
203 pub fn pause(&mut self) {
204 self.stopwatch.pause();
205 }
206
207 #[inline]
209 pub fn unpause(&mut self) {
210 self.stopwatch.unpause();
211 }
212
213 #[inline]
215 pub fn is_paused(&self) -> bool {
216 self.stopwatch.is_paused()
217 }
218
219 #[inline]
221 pub fn elapsed_secs(&self) -> f32 {
222 self.stopwatch.elapsed_secs()
223 }
224
225 #[inline]
227 pub fn duration_secs(&self) -> f32 {
228 self.duration
229 }
230}
231
232#[derive(Debug, Clone, Copy, PartialEq)]
237pub struct FixedTimestep {
238 step: f32,
239 accumulator: f32,
240 max_substeps: u32,
241}
242
243impl FixedTimestep {
244 pub const fn from_hz(hz: f32, max_substeps: u32) -> Self {
247 let step = if hz > 0.0 { 1.0 / hz } else { 1.0 / 60.0 };
248 Self {
249 step,
250 accumulator: 0.0,
251 max_substeps,
252 }
253 }
254
255 pub const fn from_step(step: f32, max_substeps: u32) -> Self {
257 Self {
258 step: if step > 0.0 { step } else { 0.01666667 },
259 accumulator: 0.0,
260 max_substeps,
261 }
262 }
263
264 pub fn update(&mut self, frame_dt: f32) -> FixedStepIter {
266 self.accumulator += frame_dt;
267 let mut steps = 0;
268 while self.accumulator >= self.step && steps < self.max_substeps {
269 self.accumulator -= self.step;
270 steps += 1;
271 }
272
273 if self.accumulator >= self.step {
275 self.accumulator = 0.0;
276 }
277
278 FixedStepIter {
279 step: self.step,
280 remaining: steps,
281 }
282 }
283
284 #[inline]
287 pub fn alpha(&self) -> f32 {
288 (self.accumulator / self.step).clamp(0.0, 1.0)
289 }
290
291 #[inline]
293 pub fn step_secs(&self) -> f32 {
294 self.step
295 }
296}
297
298pub struct FixedStepIter {
300 step: f32,
301 remaining: u32,
302}
303
304impl Iterator for FixedStepIter {
305 type Item = f32;
306
307 #[inline]
308 fn next(&mut self) -> Option<Self::Item> {
309 if self.remaining > 0 {
310 self.remaining -= 1;
311 Some(self.step)
312 } else {
313 None
314 }
315 }
316}
317
318#[cfg(test)]
319mod tests {
320 use super::*;
321
322 #[test]
323 fn test_stopwatch() {
324 let mut sw = Stopwatch::new();
325 assert_eq!(sw.elapsed_secs(), 0.0);
326 sw.tick(0.5);
327 assert_eq!(sw.elapsed_secs(), 0.5);
328 sw.pause();
329 sw.tick(0.5);
330 assert_eq!(sw.elapsed_secs(), 0.5);
331 sw.unpause();
332 sw.tick(0.5);
333 assert_eq!(sw.elapsed_secs(), 1.0);
334 sw.reset();
335 assert_eq!(sw.elapsed_secs(), 0.0);
336 }
337
338 #[test]
339 fn test_timer_once() {
340 let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
341 assert!(!timer.is_finished());
342 assert!(!timer.just_finished());
343 assert_eq!(timer.fraction(), 0.0);
344
345 timer.tick(0.5);
346 assert!(!timer.is_finished());
347 assert_eq!(timer.fraction(), 0.5);
348 assert_eq!(timer.remaining_secs(), 0.5);
349
350 timer.tick(0.5);
351 assert!(timer.is_finished());
352 assert!(timer.just_finished());
353 assert_eq!(timer.fraction(), 1.0);
354
355 timer.tick(0.5);
356 assert!(timer.is_finished());
357 assert!(!timer.just_finished());
358 }
359
360 #[test]
361 fn test_timer_repeating() {
362 let mut timer = Timer::from_seconds(1.0, TimerMode::Repeating);
363 timer.tick(0.7);
364 assert!(!timer.just_finished());
365
366 timer.tick(0.5); assert!(timer.just_finished());
368 assert!((timer.elapsed_secs() - 0.2).abs() < 1e-5);
369 }
370
371 #[test]
372 fn test_fixed_timestep() {
373 let mut fixed = FixedTimestep::from_hz(60.0, 4); let step = fixed.step_secs();
375
376 let count = fixed.update(step * 1.5).count();
378 assert_eq!(count, 1);
379 assert!(fixed.alpha() > 0.4 && fixed.alpha() < 0.6);
380
381 let count2 = fixed.update(step * 0.5).count();
383 assert_eq!(count2, 1);
384 }
385
386 #[test]
387 fn test_stopwatch_duration_controls_and_pause() {
388 let mut sw = Stopwatch::new();
389 sw.tick_duration(Duration::from_millis(250));
390 assert!((sw.elapsed_secs() - 0.25).abs() < 1e-4);
391 assert_eq!(sw.elapsed(), Duration::from_secs_f32(0.25));
392 sw.set_elapsed(3.0);
393 assert_eq!(sw.elapsed_secs(), 3.0);
394 sw.pause();
395 assert!(sw.is_paused());
396 sw.tick(1.0);
397 assert_eq!(sw.elapsed_secs(), 3.0);
398 sw.unpause();
399 assert!(!sw.is_paused());
400 sw.reset();
401 assert_eq!(sw.elapsed_secs(), 0.0);
402 }
403
404 #[test]
405 fn test_timer_pause_duration_and_reset_paths() {
406 let mut timer = Timer::new(Duration::from_millis(500), TimerMode::Repeating);
407 assert!((timer.duration_secs() - 0.5).abs() < 1e-4);
408 timer.tick(0.1);
409 assert!((timer.elapsed_secs() - 0.1).abs() < 1e-4);
410 timer.pause();
411 assert!(timer.is_paused());
412 timer.tick(0.2);
413 assert!((timer.elapsed_secs() - 0.1).abs() < 1e-4);
414 timer.unpause();
415 timer.tick_duration(Duration::from_millis(500));
416 assert!(timer.just_finished());
417 timer.reset();
418 assert!(!timer.is_finished());
419 assert_eq!(timer.elapsed_secs(), 0.0);
420 let _ = Timer::default();
421 }
422}