use std::{
collections::VecDeque,
ops::{Add, AddAssign, Mul},
time::Duration,
};
use bevy_derive::{Deref, DerefMut};
use bevy_reflect::prelude::*;
use bevy_utils::Instant;
#[derive(Debug, Clone, Copy, Reflect)]
pub struct Smoothing {
pub pan: Duration,
pub orbit: Duration,
pub zoom: Duration,
}
impl Default for Smoothing {
fn default() -> Self {
Smoothing {
pan: Duration::from_millis(20),
orbit: Duration::from_millis(40),
zoom: Duration::from_millis(80),
}
}
}
#[derive(Debug, Clone, Reflect, Deref, DerefMut)]
pub struct InputQueue<T>(pub VecDeque<InputStreamEntry<T>>);
#[derive(Debug, Clone, Reflect)]
pub struct InputStreamEntry<T> {
time: Instant,
sample: T,
fraction_remaining: f32,
smoothed_value: T,
}
impl<T: Copy + Default + Add<Output = T> + AddAssign<T> + Mul<f32, Output = T>> Default
for InputQueue<T>
{
fn default() -> Self {
let start = Instant::now();
let interval = Duration::from_secs_f32(1.0 / 60.0);
let mut queue = VecDeque::default();
for i in 1..Self::MAX_EVENTS {
queue.push_back(InputStreamEntry {
time: start - interval.mul_f32(i as f32),
sample: T::default(),
fraction_remaining: 1.0,
smoothed_value: T::default(),
})
}
Self(queue)
}
}
impl<T: Copy + Default + Add<Output = T> + AddAssign<T> + Mul<f32, Output = T>> InputQueue<T> {
const MAX_EVENTS: usize = 256;
pub fn process_input(&mut self, new_input: T, smoothing: Duration) {
let now = Instant::now();
let queue = &mut self.0;
let window_size = queue
.iter()
.enumerate()
.find(|(_i, entry)| now.duration_since(entry.time) > smoothing)
.map(|(i, _)| i) .unwrap_or(0)
+ 1;
let range_end = (window_size - 1).clamp(0, queue.len());
let target_fraction = 1.0 / window_size as f32;
let mut smoothed_value = new_input * target_fraction;
for entry in queue.range_mut(..range_end) {
let this_fraction = entry.fraction_remaining.min(target_fraction);
smoothed_value += entry.sample * this_fraction;
entry.fraction_remaining = (entry.fraction_remaining - this_fraction).max(0.0);
}
for old_entry in queue
.range_mut(range_end..)
.filter(|e| e.fraction_remaining > 0.0)
{
smoothed_value += old_entry.sample * old_entry.fraction_remaining;
old_entry.fraction_remaining = 0.0;
}
queue.truncate(Self::MAX_EVENTS - 1);
queue.push_front(InputStreamEntry {
time: now,
sample: new_input,
fraction_remaining: 1.0 - target_fraction,
smoothed_value,
})
}
pub fn latest_smoothed(&self) -> Option<T> {
self.iter_smoothed().next().map(|(_, val)| val)
}
pub fn iter_smoothed(&self) -> impl Iterator<Item = (Instant, T)> + '_ {
self.0
.iter()
.map(|entry| (entry.time, entry.smoothed_value))
}
pub fn iter_unsmoothed(&self) -> impl Iterator<Item = (Instant, T)> + '_ {
self.0.iter().map(|entry| (entry.time, entry.sample))
}
pub fn average_smoothed_value(&self, window: Duration) -> T {
let now = Instant::now();
let mut count = 0;
let sum = self
.iter_smoothed()
.filter(|(t, _)| now.duration_since(*t) < window)
.map(|(_, smoothed_value)| smoothed_value)
.reduce(|acc, v| {
count += 1;
acc + v
})
.unwrap_or_default();
sum * (1.0 / count as f32)
}
pub fn approx_smoothed(&self, window: Duration, mut modifier: impl FnMut(&mut T)) -> T {
let now = Instant::now();
let n_elements = &mut 0;
self.iter_unsmoothed()
.filter(|(time, _)| now.duration_since(*time) < window)
.map(|(_, value)| {
*n_elements += 1;
let mut value = value;
modifier(&mut value);
value
})
.reduce(|acc, v| acc + v)
.unwrap_or_default()
* (1.0 / *n_elements as f32)
}
}