i_slint_core/items/flickable/
data_ringbuffer.rs1use crate::Coord;
8use crate::animations::Instant;
9use crate::lengths::LogicalPoint;
10use crate::lengths::LogicalPx;
11use core::time::Duration;
12use euclid::Vector2D;
13
14#[derive(Debug)]
16pub(crate) struct PositionTimeRingBuffer<const N: usize> {
17 curr_index: usize,
19 full: bool,
21 values: [(Instant, LogicalPoint); N],
22}
23
24impl<const N: usize> Default for PositionTimeRingBuffer<N> {
25 fn default() -> Self {
26 Self { curr_index: 0, full: false, values: [(Instant::now(), LogicalPoint::default()); N] }
27 }
28}
29
30impl<const N: usize> PositionTimeRingBuffer<N> {
31 pub fn empty(&self) -> bool {
33 !(self.full || self.curr_index > 0)
34 }
35
36 pub fn push(&mut self, time: Instant, value: LogicalPoint) {
38 if self.curr_index < self.values.len() {
39 self.values[self.curr_index] = (time, value);
40 }
41 self.curr_index += 1;
42 if self.curr_index >= N {
43 self.full = true;
44 self.curr_index = 0;
45 }
46 }
47
48 fn latest_index(&self) -> usize {
50 if self.curr_index > 0 { self.curr_index - 1 } else { N - 1 }
51 }
52
53 pub fn last_time(&self) -> Option<Instant> {
55 if !self.empty() { Some(self.values[self.latest_index()].0) } else { None }
56 }
57
58 pub fn diff(&self) -> (Duration, Vector2D<Coord, LogicalPx>) {
60 if self.full {
61 let oldest = self.values[self.curr_index];
62 let newest = if self.curr_index > 0 {
63 self.values[self.curr_index - 1]
64 } else {
65 self.values[self.values.len() - 1]
66 };
67 (newest.0.duration_since(oldest.0), newest.1 - oldest.1)
68 } else {
69 let oldest = self.values[0];
70 let newest = self.values[usize::max(0, self.curr_index - 1)];
71 (newest.0.duration_since(oldest.0), newest.1 - oldest.1)
72 }
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79 use crate::animations::Instant;
80 use crate::lengths::LogicalPoint;
81 use core::time::Duration;
82
83 #[test]
84 fn test_empty_buffer() {
85 let buffer: PositionTimeRingBuffer<5> = PositionTimeRingBuffer::default();
86 assert!(buffer.empty());
87 assert_eq!(buffer.curr_index, 0);
88 assert!(!buffer.full);
89 assert_eq!(buffer.last_time(), None);
90 }
91
92 #[test]
93 fn test_push_single_element() {
94 let mut buffer: PositionTimeRingBuffer<5> = PositionTimeRingBuffer::default();
95 let time = Instant::now();
96 let point = LogicalPoint::new(10.0, 20.0);
97
98 buffer.push(time, point);
99
100 assert!(!buffer.empty());
101 assert_eq!(buffer.curr_index, 1);
102 assert!(!buffer.full);
103 assert_eq!(buffer.latest_index(), 0);
104 assert_eq!(buffer.last_time(), Some(time));
105
106 assert_eq!(buffer.diff(), (Duration::from_millis(0), Vector2D::new(0., 0.)));
107 }
108
109 #[test]
111 fn test_push_two_elements() {
112 let mut buffer: PositionTimeRingBuffer<5> = PositionTimeRingBuffer::default();
113 let time = Instant::now();
114
115 buffer.push(time, LogicalPoint::new(10.0, 20.0));
116 buffer.push(time + Duration::from_millis(13), LogicalPoint::new(13.0, -5.0));
117
118 assert!(!buffer.empty());
119 assert_eq!(buffer.curr_index, 2);
120 assert!(!buffer.full);
121 assert_eq!(buffer.latest_index(), 1);
122 assert_eq!(buffer.last_time(), Some(time + Duration::from_millis(13)));
123
124 assert_eq!(buffer.diff(), (Duration::from_millis(13), Vector2D::new(3., -25.)));
125 }
126
127 #[test]
128 fn test_push_until_full() {
129 let mut buffer: PositionTimeRingBuffer<5> = PositionTimeRingBuffer::default();
130 let base_time = Instant::now();
131
132 for i in 0..5 {
134 let time = base_time + Duration::from_millis(i * 3 as u64);
135 let point = LogicalPoint::new(i as f32, -2. * i as f32);
136 buffer.push(time, point);
137 }
138
139 assert!(!buffer.empty());
140 assert_eq!(buffer.curr_index, 0);
141 assert!(buffer.full);
142 assert_eq!(buffer.last_time(), Some(base_time + Duration::from_millis(4 * 3)));
143 assert_eq!(buffer.latest_index(), 4);
144
145 assert_eq!(buffer.diff(), (Duration::from_millis(12), Vector2D::new(4., -8.)));
146 }
147
148 #[test]
149 fn test_push_beyond_capacity() {
150 const CAP: usize = 5;
151 let mut buffer: PositionTimeRingBuffer<CAP> = PositionTimeRingBuffer::default();
152 let base_time = Instant::now();
153
154 for i in 0..(CAP + 2) {
156 let time = base_time + Duration::from_millis(i as u64);
157 let point = LogicalPoint::new(i as f32, i as f32 * 2. + 100.);
158 buffer.push(time, point);
159 }
160
161 assert!(!buffer.empty());
162 assert!(buffer.full);
163 assert_eq!(buffer.curr_index, 2);
164 assert_eq!(buffer.latest_index(), 1);
165 assert_eq!(buffer.last_time(), Some(base_time + Duration::from_millis(6)));
166
167 assert_eq!(buffer.diff(), (Duration::from_millis(4), Vector2D::new(4., 4. * 2.)));
168 }
169
170 #[test]
171 fn test_push_beyond_capacity_wrap_back() {
172 const CAP: usize = 5;
173 let mut buffer: PositionTimeRingBuffer<CAP> = PositionTimeRingBuffer::default();
174 let base_time = Instant::now();
175
176 for i in 0..CAP {
178 let time = base_time + Duration::from_millis(i as u64);
179 let point = LogicalPoint::new(i as f32 * 3., i as f32 * -2. + 100.);
180 buffer.push(time, point);
181 }
182
183 assert!(!buffer.empty());
184 assert!(buffer.full);
185 assert_eq!(buffer.curr_index, 0);
186 assert_eq!(buffer.latest_index(), CAP - 1);
187 assert_eq!(buffer.last_time(), Some(base_time + Duration::from_millis(4)));
188
189 assert_eq!(buffer.diff(), (Duration::from_millis(4), Vector2D::new(4. * 3., 4. * -2.)));
191 }
192}