comfy_core/timer.rs
1#![allow(dead_code)]
2use std::time::Duration;
3
4// Most code taken from bevy's timer. It doesn't exactly math the latest API which switched from
5// `repeating: bool` to enums.
6
7/// Tracks elapsed time. Enters the finished state once `duration` is reached.
8///
9/// Non repeating timers will stop tracking and stay in the finished state until reset.
10/// Repeating timers will only be in the finished state on each tick `duration` is reached or
11/// exceeded, and can still be reset at any given point.
12///
13/// Paused timers will not have elapsed time increased.
14#[derive(Clone, Debug, Default)]
15pub struct Timer {
16 stopwatch: Stopwatch,
17 duration: Duration,
18 repeating: bool,
19 finished: bool,
20 times_finished: u32,
21}
22
23impl Timer {
24 /// Creates a new timer with a given duration.
25 ///
26 /// See also [`Timer::from_seconds`](Timer::from_seconds).
27 pub fn new(duration: Duration, repeating: bool) -> Self {
28 Self { duration, repeating, ..Default::default() }
29 }
30
31 /// Creates a new timer with a given duration in seconds.
32 ///
33 /// # Example
34 /// ```
35 /// # use comfy_core::{Timer, Stopwatch};
36 /// let mut timer = Timer::from_seconds(1.0, false);
37 /// ```
38 pub fn from_seconds(duration: f32, repeating: bool) -> Self {
39 Self {
40 duration: Duration::from_secs_f32(duration),
41 repeating,
42 ..Default::default()
43 }
44 }
45
46 /// Returns `true` if the timer has reached its duration.
47 ///
48 /// # Examples
49 /// ```
50 /// # use comfy_core::{Timer, Stopwatch};
51 /// use std::time::Duration;
52 /// let mut timer = Timer::from_seconds(1.0, false);
53 /// timer.tick(Duration::from_secs_f32(1.5));
54 /// assert!(timer.finished());
55 /// timer.tick(Duration::from_secs_f32(0.5));
56 /// assert!(timer.finished());
57 /// ```
58 #[inline]
59 pub fn finished(&self) -> bool {
60 self.finished
61 }
62
63 /// Returns `true` only on the tick the timer reached its duration.
64 ///
65 /// # Examples
66 /// ```
67 /// # use comfy_core::{Timer, Stopwatch};
68 /// use std::time::Duration;
69 /// let mut timer = Timer::from_seconds(1.0, false);
70 /// timer.tick(Duration::from_secs_f32(1.5));
71 /// assert!(timer.just_finished());
72 /// timer.tick(Duration::from_secs_f32(0.5));
73 /// assert!(!timer.just_finished());
74 /// ```
75 #[inline]
76 pub fn just_finished(&self) -> bool {
77 self.times_finished > 0
78 }
79
80 /// Returns the time elapsed on the timer. Guaranteed to be between 0.0 and `duration`.
81 /// Will only equal `duration` when the timer is finished and non repeating.
82 ///
83 /// See also [`Stopwatch::elapsed`](Stopwatch::elapsed).
84 ///
85 /// # Examples
86 /// ```
87 /// # use comfy_core::{Timer, Stopwatch};
88 /// use std::time::Duration;
89 /// let mut timer = Timer::from_seconds(1.0, false);
90 /// timer.tick(Duration::from_secs_f32(0.5));
91 /// assert_eq!(timer.elapsed(), Duration::from_secs_f32(0.5));
92 /// ```
93 #[inline]
94 pub fn elapsed(&self) -> Duration {
95 self.stopwatch.elapsed()
96 }
97
98 /// Returns the time elapsed on the timer as a `f32`.
99 /// See also [`Timer::elapsed`](Timer::elapsed).
100 #[inline]
101 pub fn elapsed_secs(&self) -> f32 {
102 self.stopwatch.elapsed_secs()
103 }
104
105 /// Sets the elapsed time of the timer without any other considerations.
106 ///
107 /// #
108 /// ```
109 /// # use comfy_core::{Timer, Stopwatch};
110 /// use std::time::Duration;
111 /// let mut timer = Timer::from_seconds(1.0, false);
112 /// timer.set_elapsed(Duration::from_secs(2));
113 /// assert_eq!(timer.elapsed(), Duration::from_secs(2));
114 /// // the timer is not finished even if the elapsed time is greater than the duration.
115 /// assert!(!timer.finished());
116 /// ```
117 #[inline]
118 pub fn set_elapsed(&mut self, time: Duration) {
119 self.stopwatch.set_elapsed(time);
120 }
121
122 /// Returns the duration of the timer.
123 ///
124 /// # Examples
125 /// ```
126 /// # use comfy_core::{Timer, Stopwatch};
127 /// use std::time::Duration;
128 /// let timer = Timer::new(Duration::from_secs(1), false);
129 /// assert_eq!(timer.duration(), Duration::from_secs(1));
130 /// ```
131 #[inline]
132 pub fn duration(&self) -> Duration {
133 self.duration
134 }
135
136 /// Sets the duration of the timer.
137 ///
138 /// # Examples
139 /// ```
140 /// # use comfy_core::{Timer, Stopwatch};
141 /// use std::time::Duration;
142 /// let mut timer = Timer::from_seconds(1.5, false);
143 /// timer.set_duration(Duration::from_secs(1));
144 /// assert_eq!(timer.duration(), Duration::from_secs(1));
145 /// ```
146 #[inline]
147 pub fn set_duration(&mut self, duration: Duration) {
148 self.duration = duration;
149 }
150
151 /// Returns `true` if the timer is repeating.
152 ///
153 /// # Examples
154 /// ```
155 /// # use comfy_core::{Timer, Stopwatch};
156 /// let mut timer = Timer::from_seconds(1.0, true);
157 /// assert!(timer.repeating());
158 /// ```
159 #[inline]
160 pub fn repeating(&self) -> bool {
161 self.repeating
162 }
163
164 /// Sets whether the timer is repeating or not.
165 ///
166 /// # Examples
167 /// ```
168 /// # use comfy_core::{Timer, Stopwatch};
169 /// let mut timer = Timer::from_seconds(1.0, true);
170 /// timer.set_repeating(false);
171 /// assert!(!timer.repeating());
172 /// ```
173 #[inline]
174 pub fn set_repeating(&mut self, repeating: bool) {
175 if !self.repeating && repeating && self.finished {
176 self.stopwatch.reset();
177 self.finished = self.just_finished();
178 }
179 self.repeating = repeating;
180 }
181
182 /// Advance the timer by `delta` seconds.
183 /// Non repeating timer will clamp at duration.
184 /// Repeating timer will wrap around.
185 ///
186 /// See also [`Stopwatch::tick`](Stopwatch::tick).
187 ///
188 /// # Examples
189 /// ```
190 /// # use comfy_core::{Timer, Stopwatch};
191 /// use std::time::Duration;
192 /// let mut timer = Timer::from_seconds(1.0, false);
193 /// let mut repeating = Timer::from_seconds(1.0, true);
194 /// timer.tick(Duration::from_secs_f32(1.5));
195 /// repeating.tick(Duration::from_secs_f32(1.5));
196 /// assert_eq!(timer.elapsed_secs(), 1.0);
197 /// assert_eq!(repeating.elapsed_secs(), 0.5);
198 /// ```
199 pub fn tick(&mut self, delta: Duration) -> &Self {
200 if self.paused() {
201 return self;
202 }
203
204 if !self.repeating() && self.finished() {
205 self.times_finished = 0;
206 return self;
207 }
208
209 self.stopwatch.tick(delta);
210 self.finished = self.elapsed() >= self.duration();
211
212 if self.finished() {
213 if self.repeating() {
214 self.times_finished = (self.elapsed().as_nanos() /
215 self.duration().as_nanos())
216 as u32;
217 // Duration does not have a modulo
218 self.set_elapsed(
219 self.elapsed() - self.duration() * self.times_finished,
220 );
221 } else {
222 self.times_finished = 1;
223 self.set_elapsed(self.duration());
224 }
225 } else {
226 self.times_finished = 0;
227 }
228
229 self
230 }
231
232 pub fn tick_secs(&mut self, delta: f32) -> &Self {
233 self.tick(Duration::from_secs_f32(delta))
234 }
235
236 /// Pauses the Timer. Disables the ticking of the timer.
237 ///
238 /// See also [`Stopwatch::pause`](Stopwatch::pause).
239 ///
240 /// # Examples
241 /// ```
242 /// # use comfy_core::{Timer, Stopwatch};
243 /// use std::time::Duration;
244 /// let mut timer = Timer::from_seconds(1.0, false);
245 /// timer.pause();
246 /// timer.tick(Duration::from_secs_f32(0.5));
247 /// assert_eq!(timer.elapsed_secs(), 0.0);
248 /// ```
249 #[inline]
250 pub fn pause(&mut self) {
251 self.stopwatch.pause();
252 }
253
254 /// Unpauses the Timer. Resumes the ticking of the timer.
255 ///
256 /// See also [`Stopwatch::unpause()`](Stopwatch::unpause).
257 ///
258 /// # Examples
259 /// ```
260 /// # use comfy_core::{Timer, Stopwatch};
261 /// use std::time::Duration;
262 /// let mut timer = Timer::from_seconds(1.0, false);
263 /// timer.pause();
264 /// timer.tick(Duration::from_secs_f32(0.5));
265 /// timer.unpause();
266 /// timer.tick(Duration::from_secs_f32(0.5));
267 /// assert_eq!(timer.elapsed_secs(), 0.5);
268 /// ```
269 #[inline]
270 pub fn unpause(&mut self) {
271 self.stopwatch.unpause();
272 }
273
274 /// Returns `true` if the timer is paused.
275 ///
276 /// See also [`Stopwatch::paused`](Stopwatch::paused).
277 ///
278 /// # Examples
279 /// ```
280 /// # use comfy_core::{Timer, Stopwatch};
281 /// let mut timer = Timer::from_seconds(1.0, false);
282 /// assert!(!timer.paused());
283 /// timer.pause();
284 /// assert!(timer.paused());
285 /// timer.unpause();
286 /// assert!(!timer.paused());
287 /// ```
288 #[inline]
289 pub fn paused(&self) -> bool {
290 self.stopwatch.paused()
291 }
292
293 /// Resets the timer. the reset doesn't affect the `paused` state of the timer.
294 ///
295 /// See also [`Stopwatch::reset`](Stopwatch::reset).
296 ///
297 /// Examples
298 /// ```
299 /// # use comfy_core::{Timer, Stopwatch};
300 /// use std::time::Duration;
301 /// let mut timer = Timer::from_seconds(1.0, false);
302 /// timer.tick(Duration::from_secs_f32(1.5));
303 /// timer.reset();
304 /// assert!(!timer.finished());
305 /// assert!(!timer.just_finished());
306 /// assert_eq!(timer.elapsed_secs(), 0.0);
307 /// ```
308 pub fn reset(&mut self) {
309 self.stopwatch.reset();
310 self.finished = false;
311 self.times_finished = 0;
312 }
313
314 /// Returns the percentage of the timer elapsed time (goes from 0.0 to 1.0).
315 ///
316 /// # Examples
317 /// ```
318 /// # use comfy_core::{Timer, Stopwatch};
319 /// use std::time::Duration;
320 /// let mut timer = Timer::from_seconds(2.0, false);
321 /// timer.tick(Duration::from_secs_f32(0.5));
322 /// assert_eq!(timer.percent(), 0.25);
323 /// ```
324 #[inline]
325 pub fn percent(&self) -> f32 {
326 self.elapsed().as_secs_f32() / self.duration().as_secs_f32()
327 }
328
329 /// Returns the percentage of the timer remaining time (goes from 0.0 to 1.0).
330 ///
331 /// # Examples
332 /// ```
333 /// # use comfy_core::{Timer, Stopwatch};
334 /// use std::time::Duration;
335 /// let mut timer = Timer::from_seconds(2.0, false);
336 /// timer.tick(Duration::from_secs_f32(0.5));
337 /// assert_eq!(timer.percent_left(), 0.75);
338 /// ```
339 #[inline]
340 pub fn percent_left(&self) -> f32 {
341 1.0 - self.percent()
342 }
343
344 /// Returns the number of times a repeating timer
345 /// finished during the last [`tick`](Timer<T>::tick) call.
346 ///
347 /// For non repeating-timers, this method will only ever
348 /// return 0 or 1.
349 ///
350 /// # Examples
351 /// ```
352 /// # use comfy_core::{Timer, Stopwatch};
353 /// use std::time::Duration;
354 /// let mut timer = Timer::from_seconds(1.0, true);
355 /// timer.tick(Duration::from_secs_f32(6.0));
356 /// assert_eq!(timer.times_finished(), 6);
357 /// timer.tick(Duration::from_secs_f32(2.0));
358 /// assert_eq!(timer.times_finished(), 2);
359 /// timer.tick(Duration::from_secs_f32(0.5));
360 /// assert_eq!(timer.times_finished(), 0);
361 /// ```
362 #[inline]
363 pub fn times_finished(&self) -> u32 {
364 self.times_finished
365 }
366}
367
368#[cfg(test)]
369#[allow(clippy::float_cmp)]
370mod tests {
371 use super::*;
372
373 #[test]
374 fn non_repeating_timer() {
375 let mut t = Timer::from_seconds(10.0, false);
376 // Tick once, check all attributes
377 t.tick(Duration::from_secs_f32(0.25));
378 assert_eq!(t.elapsed_secs(), 0.25);
379 assert_eq!(t.duration(), Duration::from_secs_f32(10.0));
380 assert!(!t.finished());
381 assert!(!t.just_finished());
382 assert_eq!(t.times_finished(), 0);
383 assert!(!t.repeating());
384 assert_eq!(t.percent(), 0.025);
385 assert_eq!(t.percent_left(), 0.975);
386 // Ticking while paused changes nothing
387 t.pause();
388 t.tick(Duration::from_secs_f32(500.0));
389 assert_eq!(t.elapsed_secs(), 0.25);
390 assert_eq!(t.duration(), Duration::from_secs_f32(10.0));
391 assert!(!t.finished());
392 assert!(!t.just_finished());
393 assert_eq!(t.times_finished(), 0);
394 assert!(!t.repeating());
395 assert_eq!(t.percent(), 0.025);
396 assert_eq!(t.percent_left(), 0.975);
397 // Tick past the end and make sure elapsed doesn't go past 0.0 and other things update
398 t.unpause();
399 t.tick(Duration::from_secs_f32(500.0));
400 assert_eq!(t.elapsed_secs(), 10.0);
401 assert!(t.finished());
402 assert!(t.just_finished());
403 assert_eq!(t.times_finished(), 1);
404 assert_eq!(t.percent(), 1.0);
405 assert_eq!(t.percent_left(), 0.0);
406 // Continuing to tick when finished should only change just_finished
407 t.tick(Duration::from_secs_f32(1.0));
408 assert_eq!(t.elapsed_secs(), 10.0);
409 assert!(t.finished());
410 assert!(!t.just_finished());
411 assert_eq!(t.times_finished(), 0);
412 assert_eq!(t.percent(), 1.0);
413 assert_eq!(t.percent_left(), 0.0);
414 }
415
416 #[test]
417 fn repeating_timer() {
418 let mut t = Timer::from_seconds(2.0, true);
419 // Tick once, check all attributes
420 t.tick(Duration::from_secs_f32(0.75));
421 assert_eq!(t.elapsed_secs(), 0.75);
422 assert_eq!(t.duration(), Duration::from_secs_f32(2.0));
423 assert!(!t.finished());
424 assert!(!t.just_finished());
425 assert_eq!(t.times_finished(), 0);
426 assert!(t.repeating());
427 assert_eq!(t.percent(), 0.375);
428 assert_eq!(t.percent_left(), 0.625);
429 // Tick past the end and make sure elapsed wraps
430 t.tick(Duration::from_secs_f32(1.5));
431 assert_eq!(t.elapsed_secs(), 0.25);
432 assert!(t.finished());
433 assert!(t.just_finished());
434 assert_eq!(t.times_finished(), 1);
435 assert_eq!(t.percent(), 0.125);
436 assert_eq!(t.percent_left(), 0.875);
437 // Continuing to tick should turn off both finished & just_finished for repeating timers
438 t.tick(Duration::from_secs_f32(1.0));
439 assert_eq!(t.elapsed_secs(), 1.25);
440 assert!(!t.finished());
441 assert!(!t.just_finished());
442 assert_eq!(t.times_finished(), 0);
443 assert_eq!(t.percent(), 0.625);
444 assert_eq!(t.percent_left(), 0.375);
445 }
446
447 #[test]
448 fn times_finished_repeating() {
449 let mut t = Timer::from_seconds(1.0, true);
450 assert_eq!(t.times_finished(), 0);
451 t.tick(Duration::from_secs_f32(3.5));
452 assert_eq!(t.times_finished(), 3);
453 assert_eq!(t.elapsed_secs(), 0.5);
454 assert!(t.finished());
455 assert!(t.just_finished());
456 t.tick(Duration::from_secs_f32(0.2));
457 assert_eq!(t.times_finished(), 0);
458 }
459
460 #[test]
461 fn times_finished() {
462 let mut t = Timer::from_seconds(1.0, false);
463 assert_eq!(t.times_finished(), 0);
464 t.tick(Duration::from_secs_f32(1.5));
465 assert_eq!(t.times_finished(), 1);
466 t.tick(Duration::from_secs_f32(0.5));
467 assert_eq!(t.times_finished(), 0);
468 }
469
470 #[test]
471 fn times_finished_precise() {
472 let mut t = Timer::from_seconds(0.01, true);
473 let duration = Duration::from_secs_f64(0.333);
474
475 // total duration: 0.333 => 33 times finished
476 t.tick(duration);
477 assert_eq!(t.times_finished(), 33);
478 // total duration: 0.666 => 33 times finished
479 t.tick(duration);
480 assert_eq!(t.times_finished(), 33);
481 // total duration: 0.999 => 33 times finished
482 t.tick(duration);
483 assert_eq!(t.times_finished(), 33);
484 // total duration: 1.332 => 34 times finished
485 t.tick(duration);
486 assert_eq!(t.times_finished(), 34);
487 }
488}
489
490/// A Stopwatch is a struct that track elapsed time when started.
491///
492/// # Examples
493///
494/// ```
495/// # use comfy_core::{Timer, Stopwatch};
496/// use std::time::Duration;
497/// let mut stopwatch = Stopwatch::new();
498/// assert_eq!(stopwatch.elapsed_secs(), 0.0);
499///
500/// stopwatch.tick(Duration::from_secs_f32(1.0)); // tick one second
501/// assert_eq!(stopwatch.elapsed_secs(), 1.0);
502///
503/// stopwatch.pause();
504/// stopwatch.tick(Duration::from_secs_f32(1.0)); // paused stopwatches don't tick
505/// assert_eq!(stopwatch.elapsed_secs(), 1.0);
506///
507/// stopwatch.reset(); // reset the stopwatch
508/// assert!(stopwatch.paused());
509/// assert_eq!(stopwatch.elapsed_secs(), 0.0);
510/// ```
511#[derive(Clone, Debug, Default)]
512pub struct Stopwatch {
513 elapsed: Duration,
514 paused: bool,
515}
516
517impl Stopwatch {
518 /// Create a new unpaused `Stopwatch` with no elapsed time.
519 ///
520 /// # Examples
521 /// ```
522 /// # use comfy_core::{Timer, Stopwatch};
523 /// let stopwatch = Stopwatch::new();
524 /// assert_eq!(stopwatch.elapsed_secs(), 0.0);
525 /// assert_eq!(stopwatch.paused(), false);
526 /// ```
527 pub fn new() -> Self {
528 Default::default()
529 }
530
531 /// Returns the elapsed time since the last [`reset`](Stopwatch::reset)
532 /// of the stopwatch.
533 ///
534 /// # Examples
535 /// ```
536 /// # use comfy_core::{Timer, Stopwatch};
537 /// use std::time::Duration;
538 /// let mut stopwatch = Stopwatch::new();
539 /// stopwatch.tick(Duration::from_secs(1));
540 /// assert_eq!(stopwatch.elapsed(), Duration::from_secs(1));
541 /// ```
542 ///
543 /// # See Also
544 ///
545 /// [`elapsed_secs`](Stopwatch::elapsed) - if a `f32` value is desirable instead.
546 #[inline]
547 pub fn elapsed(&self) -> Duration {
548 self.elapsed
549 }
550
551 /// Returns the elapsed time since the last [`reset`](Stopwatch::reset)
552 /// of the stopwatch, in seconds.
553 ///
554 /// # Examples
555 /// ```
556 /// # use comfy_core::{Timer, Stopwatch};
557 /// use std::time::Duration;
558 /// let mut stopwatch = Stopwatch::new();
559 /// stopwatch.tick(Duration::from_secs(1));
560 /// assert_eq!(stopwatch.elapsed_secs(), 1.0);
561 /// ```
562 ///
563 /// # See Also
564 ///
565 /// [`elapsed`](Stopwatch::elapsed) - if a `Duration` is desirable instead.
566 #[inline]
567 pub fn elapsed_secs(&self) -> f32 {
568 self.elapsed().as_secs_f32()
569 }
570
571 /// Sets the elapsed time of the stopwatch.
572 ///
573 /// # Examples
574 /// ```
575 /// # use comfy_core::{Timer, Stopwatch};
576 /// use std::time::Duration;
577 /// let mut stopwatch = Stopwatch::new();
578 /// stopwatch.set_elapsed(Duration::from_secs_f32(1.0));
579 /// assert_eq!(stopwatch.elapsed_secs(), 1.0);
580 /// ```
581 #[inline]
582 pub fn set_elapsed(&mut self, time: Duration) {
583 self.elapsed = time;
584 }
585
586 /// Advance the stopwatch by `delta` seconds.
587 /// If the stopwatch is paused, ticking will not have any effect
588 /// on elapsed time.
589 ///
590 /// # Examples
591 /// ```
592 /// # use comfy_core::{Timer, Stopwatch};
593 /// use std::time::Duration;
594 /// let mut stopwatch = Stopwatch::new();
595 /// stopwatch.tick(Duration::from_secs_f32(1.5));
596 /// assert_eq!(stopwatch.elapsed_secs(), 1.5);
597 /// ```
598 pub fn tick(&mut self, delta: Duration) -> &Self {
599 if !self.paused() {
600 self.elapsed += delta;
601 }
602 self
603 }
604
605 /// Pauses the stopwatch. Any call to [`tick`](Stopwatch::tick) while
606 /// paused will not have any effect on the elapsed time.
607 ///
608 /// # Examples
609 /// ```
610 /// # use comfy_core::{Timer, Stopwatch};
611 /// use std::time::Duration;
612 /// let mut stopwatch = Stopwatch::new();
613 /// stopwatch.pause();
614 /// stopwatch.tick(Duration::from_secs_f32(1.5));
615 /// assert!(stopwatch.paused());
616 /// assert_eq!(stopwatch.elapsed_secs(), 0.0);
617 /// ```
618 #[inline]
619 pub fn pause(&mut self) {
620 self.paused = true;
621 }
622
623 /// Unpauses the stopwatch. Resume the effect of ticking on elapsed time.
624 ///
625 /// # Examples
626 /// ```
627 /// # use comfy_core::{Timer, Stopwatch};
628 /// use std::time::Duration;
629 /// let mut stopwatch = Stopwatch::new();
630 /// stopwatch.pause();
631 /// stopwatch.tick(Duration::from_secs_f32(1.0));
632 /// stopwatch.unpause();
633 /// stopwatch.tick(Duration::from_secs_f32(1.0));
634 /// assert!(!stopwatch.paused());
635 /// assert_eq!(stopwatch.elapsed_secs(), 1.0);
636 /// ```
637 #[inline]
638 pub fn unpause(&mut self) {
639 self.paused = false;
640 }
641
642 /// Returns `true` if the stopwatch is paused.
643 ///
644 /// # Examples
645 /// ```
646 /// # use comfy_core::{Timer, Stopwatch};
647 /// let mut stopwatch = Stopwatch::new();
648 /// assert!(!stopwatch.paused());
649 /// stopwatch.pause();
650 /// assert!(stopwatch.paused());
651 /// stopwatch.unpause();
652 /// assert!(!stopwatch.paused());
653 /// ```
654 #[inline]
655 pub fn paused(&self) -> bool {
656 self.paused
657 }
658
659 /// Resets the stopwatch.
660 ///
661 /// # Examples
662 /// ```
663 /// # use comfy_core::{Timer, Stopwatch};
664 /// use std::time::Duration;
665 /// let mut stopwatch = Stopwatch::new();
666 /// stopwatch.tick(Duration::from_secs_f32(1.5));
667 /// stopwatch.reset();
668 /// assert_eq!(stopwatch.elapsed_secs(), 0.0);
669 /// ```
670 #[inline]
671 pub fn reset(&mut self) {
672 self.elapsed = Default::default();
673 }
674}