use num_traits::{Float, FromPrimitive};
pub struct FloatInterpolator<T> {
start: T,
end: T,
steps: usize,
current_step: usize,
}
impl<T: FloatInterpolation> FloatInterpolator<T> {
pub fn new(start: T, end: T, steps: usize) -> Self {
Self {
start,
end,
steps,
current_step: 0,
}
}
}
impl<T: FloatInterpolation> Iterator for FloatInterpolator<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.current_step > self.steps {
return None;
}
let t = T::from_usize(self.current_step).unwrap() / T::from_usize(self.steps).unwrap();
let interpolated = self.start.lerp(self.end, t);
self.current_step += 1;
Some(interpolated)
}
}
pub trait FloatInterpolation: Float + FromPrimitive {
fn lerp(self, end: Self, t: Self) -> Self {
self * (Self::one() - t) + end * t
}
fn lerp_iter(self, end: Self, step_size: Self) -> FloatInterpolator<Self> {
let steps = ((end - self) / step_size).abs().round().to_usize().unwrap();
FloatInterpolator::new(self, end, steps)
}
fn lerp_iter_fixed(self, end: Self, steps: usize) -> FloatInterpolator<Self> {
FloatInterpolator::new(self, end, steps)
}
fn sin01(self) -> Self {
(self.sin() + Self::one()) / (Self::one() + Self::one())
}
fn cos01(self) -> Self {
(self.cos() + Self::one()) / (Self::one() + Self::one())
}
fn clamp01(self) -> Self {
self.max(Self::zero()).min(Self::one())
}
fn linlin(self, from_start: Self, from_end: Self, to_start: Self, to_end: Self) -> Self {
let normalized = (self - from_start) / (from_end - from_start);
to_start + normalized * (to_end - to_start)
}
}
impl FloatInterpolation for f32 {}
impl FloatInterpolation for f64 {}
pub fn fractions(steps: usize) -> FloatInterpolator<f32> {
0.0.lerp_iter_fixed(1.0, steps)
}
pub fn fractions_exclusive(steps: usize) -> impl Iterator<Item = f32> {
(0..steps).map(move |i| i as f32 / steps as f32)
}
pub fn linspace(start: f32, end: f32, count: usize) -> FloatInterpolator<f32> {
start.lerp_iter_fixed(end, count.saturating_sub(1))
}