blockworx_paint/
progress.rs1use std::time::Duration;
6
7use blockworx_geom::{Bounded, Bounds};
8pub struct ProgressBounds;
9
10impl Bounds for ProgressBounds {
11 const MIN: f32 = 0.0;
12 const MAX: f32 = 1.0;
13}
14
15#[derive(
19 Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
20)]
21#[serde(transparent)]
22pub struct Progress(Bounded<ProgressBounds>);
23
24impl Progress {
25 pub const fn new(value: f32) -> Self {
26 Self(Bounded::new(value))
27 }
28
29 pub const fn get(self) -> f32 {
30 self.0.get()
31 }
32
33 pub const fn zero() -> Self {
34 Self(Bounded::min_value())
35 }
36
37 pub const fn one() -> Self {
38 Self(Bounded::max_value())
39 }
40
41 pub fn is_complete(self) -> bool {
42 self == Self::one()
43 }
44
45 pub fn through(elapsed: Duration, total: Duration) -> Self {
47 if total.is_zero() {
48 return Self::one();
49 }
50 Self::new(elapsed.div_duration_f32(total))
51 }
52
53 #[must_use]
56 pub fn complement(self) -> Self {
57 Self::new(1.0 - self.get())
58 }
59
60 #[must_use]
63 pub fn eased(self) -> Self {
64 let t = self.get();
65 Self::new(t * t * (3.0 - 2.0 * t))
66 }
67}
68
69impl From<Progress> for f32 {
70 fn from(progress: Progress) -> f32 {
71 progress.get()
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn through_is_the_clamped_ratio() {
81 let total = Duration::from_secs(2);
82 assert_eq!(Progress::through(Duration::from_secs(1), total).get(), 0.5);
83 assert_eq!(
84 Progress::through(Duration::from_secs(5), total),
85 Progress::one()
86 );
87 assert_eq!(Progress::through(Duration::ZERO, total), Progress::zero());
88 assert_eq!(
89 Progress::through(Duration::from_secs(1), Duration::ZERO),
90 Progress::one()
91 );
92 }
93
94 #[test]
95 fn complement_mirrors_around_the_midpoint() {
96 assert_eq!(Progress::new(0.25).complement().get(), 0.75);
97 assert_eq!(Progress::one().complement(), Progress::zero());
98 }
99}