Add Spline::sample_with_key and Spline::clamped_sample_with_key.

This commit is contained in:
Dimitri Sabadie
2019-09-30 12:49:36 +02:00
parent 336c1c7e80
commit cae599e0d7
4 changed files with 56 additions and 17 deletions

View File

@ -69,7 +69,8 @@ impl<T, V> Spline<T, V> {
self.0.is_empty()
}
/// Sample a spline at a given time.
/// Sample a spline at a given time, returning the interpolated value along with its associated
/// key.
///
/// The current implementation, based on immutability, cannot perform in constant time. This means
/// that samplings processing complexity is currently *O(log n)*. Its possible to achieve *O(1)*
@ -84,7 +85,7 @@ impl<T, V> Spline<T, V> {
/// youre near the beginning of the spline or its end, ensure you have enough keys around to make
/// the sampling.
///
pub fn sample(&self, t: T) -> Option<V>
pub fn sample_with_key(&self, t: T) -> Option<(V, &Key<T, V>, Option<&Key<T, V>>)>
where T: Additive + One + Trigo + Mul<T, Output = T> + Div<T, Output = T> + PartialOrd,
V: Interpolate<T> {
let keys = &self.0;
@ -95,14 +96,17 @@ impl<T, V> Spline<T, V> {
Interpolation::Step(threshold) => {
let cp1 = &keys[i + 1];
let nt = normalize_time(t, cp0, cp1);
Some(if nt < threshold { cp0.value } else { cp1.value })
let value = if nt < threshold { cp0.value } else { cp1.value };
Some((value, cp0, Some(cp1)))
}
Interpolation::Linear => {
let cp1 = &keys[i + 1];
let nt = normalize_time(t, cp0, cp1);
let value = Interpolate::lerp(cp0.value, cp1.value, nt);
Some(Interpolate::lerp(cp0.value, cp1.value, nt))
Some((value, cp0, Some(cp1)))
}
Interpolation::Cosine => {
@ -110,8 +114,9 @@ impl<T, V> Spline<T, V> {
let cp1 = &keys[i + 1];
let nt = normalize_time(t, cp0, cp1);
let cos_nt = (T::one() - (nt * T::pi()).cos()) / two_t;
let value = Interpolate::lerp(cp0.value, cp1.value, cos_nt);
Some(Interpolate::lerp(cp0.value, cp1.value, cos_nt))
Some((value, cp0, Some(cp1)))
}
Interpolation::CatmullRom => {
@ -124,8 +129,9 @@ impl<T, V> Spline<T, V> {
let cpm0 = &keys[i - 1];
let cpm1 = &keys[i + 2];
let nt = normalize_time(t, cp0, cp1);
let value = Interpolate::cubic_hermite((cpm0.value, cpm0.t), (cp0.value, cp0.t), (cp1.value, cp1.t), (cpm1.value, cpm1.t), nt);
Some(Interpolate::cubic_hermite((cpm0.value, cpm0.t), (cp0.value, cp0.t), (cp1.value, cp1.t), (cpm1.value, cpm1.t), nt))
Some((value, cp0, Some(cp1)))
}
}
@ -134,18 +140,30 @@ impl<T, V> Spline<T, V> {
let cp1 = &keys[i + 1];
let nt = normalize_time(t, cp0, cp1);
if let Interpolation::Bezier(v) = cp1.interpolation {
Some(Interpolate::cubic_bezier(cp0.value, u, v, cp1.value, nt))
} else {
Some(Interpolate::quadratic_bezier(cp0.value, u, cp1.value, nt))
}
let value =
if let Interpolation::Bezier(v) = cp1.interpolation {
Interpolate::cubic_bezier(cp0.value, u, v, cp1.value, nt)
} else {
Interpolate::quadratic_bezier(cp0.value, u, cp1.value, nt)
};
Some((value, cp0, Some(cp1)))
}
Interpolation::__NonExhaustive => unreachable!(),
}
}
/// Sample a spline at a given time with clamping.
/// Sample a spline at a given time.
///
pub fn sample(&self, t: T) -> Option<V>
where T: Additive + One + Trigo + Mul<T, Output = T> + Div<T, Output = T> + PartialOrd,
V: Interpolate<T> {
self.sample_with_key(t).map(|(v, _, _)| v)
}
/// Sample a spline at a given time with clamping, returning the interpolated value along with its
/// associated key.
///
/// # Return
///
@ -155,22 +173,23 @@ impl<T, V> Spline<T, V> {
/// # Error
///
/// This function returns [`None`] if you have no key.
pub fn clamped_sample(&self, t: T) -> Option<V>
pub fn clamped_sample_with_key(&self, t: T) -> Option<(V, &Key<T, V>, Option<&Key<T, V>>)>
where T: Additive + One + Trigo + Mul<T, Output = T> + Div<T, Output = T> + PartialOrd,
V: Interpolate<T> {
if self.0.is_empty() {
return None;
}
self.sample(t).or_else(move || {
self.sample_with_key(t).or_else(move || {
let first = self.0.first().unwrap();
if t <= first.t {
Some(first.value)
let second = if self.0.len() >= 2 { Some(&self.0[1]) } else { None };
Some((first.value, &first, second))
} else {
let last = self.0.last().unwrap();
if t >= last.t {
Some(last.value)
Some((last.value, &last, None))
} else {
None
}
@ -178,6 +197,13 @@ impl<T, V> Spline<T, V> {
})
}
/// Sample a spline at a given time with clamping.
pub fn clamped_sample(&self, t: T) -> Option<V>
where T: Additive + One + Trigo + Mul<T, Output = T> + Div<T, Output = T> + PartialOrd,
V: Interpolate<T> {
self.clamped_sample_with_key(t).map(|(v, _, _)| v)
}
/// Add a key into the spline.
pub fn add(&mut self, key: Key<T, V>) where T: PartialOrd {
self.0.push(key);