re_video/
time.rs

1/// The number of time units per second.
2#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
3pub struct Timescale(u64);
4
5impl Timescale {
6    pub const NANOSECOND: Self = Self(1_000_000_000);
7
8    pub const fn new(v: u64) -> Self {
9        Self(v)
10    }
11}
12
13/// A value in time units.
14#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
15pub struct Time(pub i64);
16
17impl re_byte_size::SizeBytes for Time {
18    fn heap_size_bytes(&self) -> u64 {
19        0
20    }
21
22    fn is_pod() -> bool {
23        true
24    }
25}
26
27/// Round a `f64` to the nearest `i64`.
28///
29/// Does not have exactly the same result as `round`, don't use in contexts where you care!
30/// Workaround for `f64::round` not being `const`.
31const fn const_round_f64(v: f64) -> i64 {
32    if v > 0.0 {
33        (v + 0.5) as i64
34    } else {
35        (v - 0.5) as i64
36    }
37}
38
39impl Time {
40    pub const ZERO: Self = Self(0);
41    pub const MAX: Self = Self(i64::MAX);
42    pub const MIN: Self = Self(i64::MIN);
43
44    /// Create a new value in _time units_.
45    ///
46    /// ⚠️ Don't use this for regular timestamps in seconds/milliseconds/etc.,
47    /// use the proper constructors for those instead!
48    /// This only exists for cases where you already have a value expressed in time units,
49    /// such as those received from the `WebCodecs` APIs.
50    #[inline]
51    pub fn new(v: i64) -> Self {
52        Self(v)
53    }
54
55    #[inline]
56    pub const fn from_secs(secs_since_start: f64, timescale: Timescale) -> Self {
57        Self(const_round_f64(secs_since_start * timescale.0 as f64))
58    }
59
60    #[inline]
61    pub const fn from_millis(millis_since_start: f64, timescale: Timescale) -> Self {
62        Self::from_secs(millis_since_start / 1e3, timescale)
63    }
64
65    #[inline]
66    pub const fn from_micros(micros_since_start: f64, timescale: Timescale) -> Self {
67        Self::from_secs(micros_since_start / 1e6, timescale)
68    }
69
70    #[inline]
71    pub const fn from_nanos(nanos_since_start: i64, timescale: Timescale) -> Self {
72        Self::from_secs(nanos_since_start as f64 / 1e9, timescale)
73    }
74
75    /// Convert to a duration
76    #[inline]
77    pub fn duration(self, timescale: Timescale) -> std::time::Duration {
78        std::time::Duration::from_nanos(self.into_nanos(timescale) as _)
79    }
80
81    #[inline]
82    pub fn into_secs(self, timescale: Timescale) -> f64 {
83        self.0 as f64 / timescale.0 as f64
84    }
85
86    #[inline]
87    pub fn into_millis(self, timescale: Timescale) -> f64 {
88        self.into_secs(timescale) * 1e3
89    }
90
91    #[inline]
92    pub fn into_micros(self, timescale: Timescale) -> f64 {
93        self.into_secs(timescale) * 1e6
94    }
95
96    #[inline]
97    pub fn into_nanos(self, timescale: Timescale) -> i64 {
98        (self.into_secs(timescale) * 1e9).round() as i64
99    }
100}
101
102impl std::fmt::Debug for Time {
103    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104        self.0.fmt(f)
105    }
106}
107
108impl std::ops::Add for Time {
109    type Output = Self;
110
111    #[inline]
112    fn add(self, rhs: Self) -> Self::Output {
113        Self(self.0.saturating_add(rhs.0))
114    }
115}
116
117impl std::ops::Sub for Time {
118    type Output = Self;
119
120    #[inline]
121    fn sub(self, rhs: Self) -> Self::Output {
122        Self(self.0.saturating_sub(rhs.0))
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    #[test]
131    fn test_const_round_f64() {
132        assert_eq!(const_round_f64(1.5), 2);
133        assert_eq!(const_round_f64(2.5), 3);
134        assert_eq!(const_round_f64(1.499999999), 1);
135        assert_eq!(const_round_f64(2.499999999), 2);
136        assert_eq!(const_round_f64(-1.5), -2);
137        assert_eq!(const_round_f64(-2.5), -3);
138        assert_eq!(const_round_f64(-1.499999999), -1);
139        assert_eq!(const_round_f64(-2.499999999), -2);
140    }
141}