emath/
history.rs

1use std::collections::VecDeque;
2
3/// This struct tracks recent values of some time series.
4///
5/// It can be used as a smoothing filter for e.g. latency, fps etc,
6/// or to show a log or graph of recent events.
7///
8/// It has a minimum and maximum length, as well as a maximum storage time.
9/// * The minimum length is to ensure you have enough data for an estimate.
10/// * The maximum length is to make sure the history doesn't take up too much space.
11/// * The maximum age is to make sure the estimate isn't outdated.
12///
13/// Time difference between values can be zero, but never negative.
14///
15/// This can be used for things like smoothed averages (for e.g. FPS)
16/// or for smoothed velocity (e.g. mouse pointer speed).
17/// All times are in seconds.
18#[derive(Clone, Debug)]
19#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
20pub struct History<T> {
21    /// In elements, i.e. of `values.len()`.
22    /// The length is initially zero, but once past `min_len` will not shrink below it.
23    min_len: usize,
24
25    /// In elements, i.e. of `values.len()`.
26    max_len: usize,
27
28    /// In seconds.
29    max_age: f32,
30
31    /// Total number of elements seen ever
32    total_count: u64,
33
34    /// (time, value) pairs, oldest front, newest back.
35    /// Time difference between values can be zero, but never negative.
36    values: VecDeque<(f64, T)>,
37}
38
39impl<T> History<T>
40where
41    T: Copy,
42{
43    /// Example:
44    /// ```
45    /// # use emath::History;
46    /// # fn now() -> f64 { 0.0 }
47    /// // Drop events that are older than one second,
48    /// // as long we keep at least two events. Never keep more than a hundred events.
49    /// let mut history = History::new(2..100, 1.0);
50    /// assert_eq!(history.average(), None);
51    /// history.add(now(), 40.0_f32);
52    /// history.add(now(), 44.0_f32);
53    /// assert_eq!(history.average(), Some(42.0));
54    /// ```
55    pub fn new(length_range: std::ops::Range<usize>, max_age: f32) -> Self {
56        Self {
57            min_len: length_range.start,
58            max_len: length_range.end,
59            max_age,
60            total_count: 0,
61            values: Default::default(),
62        }
63    }
64
65    #[inline]
66    pub fn max_len(&self) -> usize {
67        self.max_len
68    }
69
70    #[inline]
71    pub fn max_age(&self) -> f32 {
72        self.max_age
73    }
74
75    #[inline]
76    pub fn is_empty(&self) -> bool {
77        self.values.is_empty()
78    }
79
80    /// Current number of values kept in history
81    #[inline]
82    pub fn len(&self) -> usize {
83        self.values.len()
84    }
85
86    /// Total number of values seen.
87    /// Includes those that have been discarded due to `max_len` or `max_age`.
88    #[inline]
89    pub fn total_count(&self) -> u64 {
90        self.total_count
91    }
92
93    pub fn latest(&self) -> Option<T> {
94        self.values.back().map(|(_, value)| *value)
95    }
96
97    pub fn latest_mut(&mut self) -> Option<&mut T> {
98        self.values.back_mut().map(|(_, value)| value)
99    }
100
101    /// Amount of time contained from start to end in this [`History`].
102    pub fn duration(&self) -> f32 {
103        if let (Some(front), Some(back)) = (self.values.front(), self.values.back()) {
104            (back.0 - front.0) as f32
105        } else {
106            0.0
107        }
108    }
109
110    /// `(time, value)` pairs
111    /// Time difference between values can be zero, but never negative.
112    // TODO(emilk): impl IntoIter
113    pub fn iter(&self) -> impl ExactSizeIterator<Item = (f64, T)> + '_ {
114        self.values.iter().map(|(time, value)| (*time, *value))
115    }
116
117    pub fn values(&self) -> impl ExactSizeIterator<Item = T> + '_ {
118        self.values.iter().map(|(_time, value)| *value)
119    }
120
121    #[inline]
122    pub fn clear(&mut self) {
123        self.values.clear();
124    }
125
126    /// Values must be added with a monotonically increasing time, or at least not decreasing.
127    pub fn add(&mut self, now: f64, value: T) {
128        if let Some((last_time, _)) = self.values.back() {
129            debug_assert!(*last_time <= now, "Time shouldn't move backwards");
130        }
131        self.total_count += 1;
132        self.values.push_back((now, value));
133        self.flush(now);
134    }
135
136    /// Mean time difference between values in this [`History`].
137    pub fn mean_time_interval(&self) -> Option<f32> {
138        if let (Some(first), Some(last)) = (self.values.front(), self.values.back()) {
139            let n = self.len();
140            if n >= 2 {
141                Some((last.0 - first.0) as f32 / ((n - 1) as f32))
142            } else {
143                None
144            }
145        } else {
146            None
147        }
148    }
149
150    // Mean number of events per second.
151    pub fn rate(&self) -> Option<f32> {
152        self.mean_time_interval().map(|time| 1.0 / time)
153    }
154
155    /// Remove samples that are too old.
156    pub fn flush(&mut self, now: f64) {
157        while self.values.len() > self.max_len {
158            self.values.pop_front();
159        }
160        while self.values.len() > self.min_len {
161            if let Some((front_time, _)) = self.values.front() {
162                if *front_time < now - (self.max_age as f64) {
163                    self.values.pop_front();
164                } else {
165                    break;
166                }
167            } else {
168                break;
169            }
170        }
171    }
172}
173
174impl<T> History<T>
175where
176    T: Copy,
177    T: std::iter::Sum,
178    T: std::ops::Div<f32, Output = T>,
179{
180    #[inline]
181    pub fn sum(&self) -> T {
182        self.values().sum()
183    }
184
185    pub fn average(&self) -> Option<T> {
186        let num = self.len();
187        if num > 0 {
188            Some(self.sum() / (num as f32))
189        } else {
190            None
191        }
192    }
193}
194
195impl<T> History<T>
196where
197    T: Copy,
198    T: std::iter::Sum,
199    T: std::ops::Div<f32, Output = T>,
200    T: std::ops::Mul<f32, Output = T>,
201{
202    /// Average times rate.
203    /// If you are keeping track of individual sizes of things (e.g. bytes),
204    /// this will estimate the bandwidth (bytes per second).
205    pub fn bandwidth(&self) -> Option<T> {
206        Some(self.average()? * self.rate()?)
207    }
208}
209
210impl<T, Vel> History<T>
211where
212    T: Copy,
213    T: std::ops::Sub<Output = Vel>,
214    Vel: std::ops::Div<f32, Output = Vel>,
215{
216    /// Calculate a smooth velocity (per second) over the entire time span.
217    /// Calculated as the last value minus the first value over the elapsed time between them.
218    pub fn velocity(&self) -> Option<Vel> {
219        if let (Some(first), Some(last)) = (self.values.front(), self.values.back()) {
220            let dt = (last.0 - first.0) as f32;
221            if dt > 0.0 {
222                Some((last.1 - first.1) / dt)
223            } else {
224                None
225            }
226        } else {
227            None
228        }
229    }
230}