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}