use crate::Coord;
use crate::animations::Instant;
use crate::lengths::{LogicalPx, LogicalVector};
use core::time::Duration;
use euclid::Vector2D;
#[derive(Debug)]
pub(crate) struct VelocityRingBuffer<const N: usize> {
curr_index: usize,
full: bool,
values: [(Instant, Vector2D<Coord, LogicalPx>); N],
}
impl<const N: usize> Default for VelocityRingBuffer<N> {
fn default() -> Self {
Self { curr_index: 0, full: false, values: [(Instant::now(), Vector2D::default()); N] }
}
}
impl<const N: usize> VelocityRingBuffer<N> {
pub fn empty(&self) -> bool {
!(self.full || self.curr_index > 0)
}
pub fn push(&mut self, time: Instant, value: LogicalVector) {
if self.curr_index < self.values.len() {
self.values[self.curr_index] = (time, value);
}
self.curr_index += 1;
if self.curr_index >= N {
self.full = true;
self.curr_index = 0;
}
}
fn latest_index(&self) -> usize {
if self.curr_index > 0 { self.curr_index - 1 } else { N - 1 }
}
fn len(&self) -> usize {
if self.full { N } else { self.curr_index }
}
pub fn last_time(&self) -> Option<Instant> {
if !self.empty() { Some(self.values[self.latest_index()].0) } else { None }
}
pub fn mean_velocity(&self) -> LogicalVector {
let len = self.len();
if len < 2 {
return Default::default();
}
let oldest_index = if self.full { self.curr_index } else { 0 };
let newest_index = self.latest_index();
let duration = self.values[newest_index].0.duration_since(self.values[oldest_index].0);
if duration == Duration::ZERO {
return Default::default();
}
let mut total_delta = LogicalVector::default();
let mut index = (oldest_index + 1) % N;
for _ in 1..len {
total_delta += self.values[index].1;
index = (index + 1) % N;
}
(total_delta.cast::<f32>() / duration.as_secs_f32()).cast::<Coord>()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::animations::Instant;
use core::time::Duration;
#[test]
fn test_empty_buffer() {
let buffer: VelocityRingBuffer<5> = VelocityRingBuffer::default();
assert!(buffer.empty());
assert_eq!(buffer.curr_index, 0);
assert!(!buffer.full);
assert_eq!(buffer.last_time(), None);
assert_eq!(buffer.mean_velocity(), Vector2D::default());
}
#[test]
fn test_push_single_element() {
let mut buffer: VelocityRingBuffer<5> = VelocityRingBuffer::default();
let time = Instant::now();
let delta = Vector2D::new(10.0, 20.0);
buffer.push(time, delta);
assert!(!buffer.empty());
assert_eq!(buffer.curr_index, 1);
assert!(!buffer.full);
assert_eq!(buffer.latest_index(), 0);
assert_eq!(buffer.last_time(), Some(time));
assert_eq!(buffer.mean_velocity(), Vector2D::default());
}
#[test]
fn test_push_two_elements() {
let mut buffer: VelocityRingBuffer<5> = VelocityRingBuffer::default();
let time = Instant::now();
buffer.push(time, Vector2D::new(10.0, 20.0));
buffer.push(time + Duration::from_millis(100), Vector2D::new(13.0, -5.0));
assert!(!buffer.empty());
assert_eq!(buffer.curr_index, 2);
assert!(!buffer.full);
assert_eq!(buffer.latest_index(), 1);
assert_eq!(buffer.last_time(), Some(time + Duration::from_millis(100)));
assert_eq!(buffer.mean_velocity(), Vector2D::new(130.0, -50.0));
}
#[test]
fn test_push_until_full() {
let mut buffer: VelocityRingBuffer<5> = VelocityRingBuffer::default();
let base_time = Instant::now();
for i in 0..5 {
let time = base_time + Duration::from_millis(i * 100);
buffer.push(time, Vector2D::new(1.0, -2.0));
}
assert!(!buffer.empty());
assert_eq!(buffer.curr_index, 0);
assert!(buffer.full);
assert_eq!(buffer.last_time(), Some(base_time + Duration::from_millis(400)));
assert_eq!(buffer.latest_index(), 4);
assert_eq!(buffer.mean_velocity(), Vector2D::new(10.0, -20.0));
}
#[test]
fn test_push_beyond_capacity() {
const CAP: usize = 5;
let mut buffer: VelocityRingBuffer<CAP> = VelocityRingBuffer::default();
let base_time = Instant::now();
for i in 0..(CAP + 2) {
let time = base_time + Duration::from_millis(i as u64 * 100);
buffer.push(time, Vector2D::new(1.0, 2.0));
}
assert!(!buffer.empty());
assert!(buffer.full);
assert_eq!(buffer.curr_index, 2);
assert_eq!(buffer.latest_index(), 1);
assert_eq!(buffer.last_time(), Some(base_time + Duration::from_millis(600)));
assert_eq!(buffer.mean_velocity(), Vector2D::new(10.0, 20.0));
}
#[test]
fn test_push_beyond_capacity_wrap_back() {
const CAP: usize = 5;
let mut buffer: VelocityRingBuffer<CAP> = VelocityRingBuffer::default();
let base_time = Instant::now();
for i in 0..CAP {
let time = base_time + Duration::from_millis(i as u64 * 100);
buffer.push(time, Vector2D::new(3.0, -2.0));
}
assert!(!buffer.empty());
assert!(buffer.full);
assert_eq!(buffer.curr_index, 0);
assert_eq!(buffer.latest_index(), CAP - 1);
assert_eq!(buffer.last_time(), Some(base_time + Duration::from_millis(400)));
assert_eq!(buffer.mean_velocity(), Vector2D::new(30.0, -20.0));
}
}