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