use crate::interpolate::{Interpolate, Interpolator};
use crate::interpolation::Interpolation;
use crate::key::Key;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::cmp::Ordering;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct Spline<T, V>(pub(crate) Vec<Key<T, V>>);
impl<T, V> Spline<T, V> {
fn internal_sort(&mut self)
where
T: PartialOrd,
{
self
.0
.sort_by(|k0, k1| k0.t.partial_cmp(&k1.t).unwrap_or(Ordering::Less));
}
pub fn from_vec(keys: Vec<Key<T, V>>) -> Self
where
T: PartialOrd,
{
let mut spline = Spline(keys);
spline.internal_sort();
spline
}
#[inline]
pub fn clear(&mut self) {
self.0.clear()
}
pub fn keys(&self) -> &[Key<T, V>] {
&self.0
}
#[inline(always)]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn sample_with_key(&self, t: T) -> Option<SampledWithKey<V>>
where
T: Interpolator,
V: Interpolate<T>,
{
let keys = &self.0;
let i = search_lower_cp(keys, t)?;
let cp0 = &keys[i];
let value = match cp0.interpolation {
Interpolation::Step(threshold) => {
let cp1 = &keys[i + 1];
let nt = t.normalize(cp0.t, cp1.t);
let value = V::step(nt, threshold, cp0.value, cp1.value);
Some(value)
}
Interpolation::Linear => {
let cp1 = &keys[i + 1];
let nt = t.normalize(cp0.t, cp1.t);
let value = V::lerp(nt, cp0.value, cp1.value);
Some(value)
}
Interpolation::Cosine => {
let cp1 = &keys[i + 1];
let nt = t.normalize(cp0.t, cp1.t);
let value = V::cosine(nt, cp0.value, cp1.value);
Some(value)
}
Interpolation::CatmullRom => {
if i == 0 || i >= keys.len() - 2 {
None
} else {
let cp1 = &keys[i + 1];
let cpm0 = &keys[i - 1];
let cpm1 = &keys[i + 2];
let nt = t.normalize(cp0.t, cp1.t);
let value = V::cubic_hermite(
nt,
(cpm0.t, cpm0.value),
(cp0.t, cp0.value),
(cp1.t, cp1.value),
(cpm1.t, cpm1.value),
);
Some(value)
}
}
Interpolation::Bezier(u) | Interpolation::StrokeBezier(_, u) => {
let cp1 = &keys[i + 1];
let nt = t.normalize(cp0.t, cp1.t);
let value = match cp1.interpolation {
Interpolation::Bezier(v) => V::cubic_bezier_mirrored(nt, cp0.value, u, v, cp1.value),
Interpolation::StrokeBezier(v, _) => V::cubic_bezier(nt, cp0.value, u, v, cp1.value),
_ => V::quadratic_bezier(nt, cp0.value, u, cp1.value),
};
Some(value)
}
};
value.map(|value| SampledWithKey { value, key: i })
}
pub fn sample(&self, t: T) -> Option<V>
where
T: Interpolator,
V: Interpolate<T>,
{
self.sample_with_key(t).map(|sampled| sampled.value)
}
pub fn clamped_sample_with_key(&self, t: T) -> Option<SampledWithKey<V>>
where
T: Interpolator,
V: Interpolate<T>,
{
if self.0.is_empty() {
return None;
}
self.sample_with_key(t).or_else(move || {
let first = self.0.first().unwrap();
if t <= first.t {
let sampled = SampledWithKey {
value: first.value,
key: 0,
};
Some(sampled)
} else {
let last = self.0.last().unwrap();
if t >= last.t {
let sampled = SampledWithKey {
value: last.value,
key: self.0.len() - 1,
};
Some(sampled)
} else {
None
}
}
})
}
pub fn clamped_sample(&self, t: T) -> Option<V>
where
T: Interpolator,
V: Interpolate<T>,
{
self.clamped_sample_with_key(t).map(|sampled| sampled.value)
}
pub fn add(&mut self, key: Key<T, V>)
where
T: PartialOrd,
{
self.0.push(key);
self.internal_sort();
}
pub fn remove(&mut self, index: usize) -> Option<Key<T, V>> {
if index >= self.0.len() {
None
} else {
Some(self.0.remove(index))
}
}
pub fn replace<F>(&mut self, index: usize, f: F) -> Option<Key<T, V>>
where
F: FnOnce(&Key<T, V>) -> Key<T, V>,
T: PartialOrd,
{
let key = self.remove(index)?;
self.add(f(&key));
Some(key)
}
pub fn get(&self, index: usize) -> Option<&Key<T, V>> {
self.0.get(index)
}
pub fn get_mut(&mut self, index: usize) -> Option<KeyMut<T, V>> {
self.0.get_mut(index).map(|key| KeyMut {
value: &mut key.value,
interpolation: &mut key.interpolation,
})
}
}
impl<T, V> FromIterator<Key<T, V>> for Spline<T, V>
where
T: PartialOrd,
{
fn from_iter<I: IntoIterator<Item = Key<T, V>>>(iter: I) -> Self {
Self::from_vec(iter.into_iter().collect())
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct SampledWithKey<V> {
pub value: V,
pub key: usize,
}
#[derive(Debug)]
pub struct KeyMut<'a, T, V> {
pub value: &'a mut V,
pub interpolation: &'a mut Interpolation<T, V>,
}
fn search_lower_cp<T, V>(cps: &[Key<T, V>], t: T) -> Option<usize>
where
T: PartialOrd,
{
let len = cps.len();
if len < 2 {
return None;
}
match cps.binary_search_by(|key| key.t.partial_cmp(&t).unwrap()) {
Err(i) if i >= len => None,
Err(0) => None,
Err(i) => Some(i - 1),
Ok(i) if i == len - 1 => None,
Ok(i) => Some(i),
}
}