Add some unit tests.
This commit is contained in:
parent
118a11b911
commit
44154d9728
96
src/lib.rs
96
src/lib.rs
@ -21,14 +21,14 @@
|
|||||||
//! use splines::{Interpolation, Key, Spline};
|
//! use splines::{Interpolation, Key, Spline};
|
||||||
//!
|
//!
|
||||||
//! let start = Key::new(0., 0., Interpolation::Linear);
|
//! let start = Key::new(0., 0., Interpolation::Linear);
|
||||||
//! let end = Key::new(1., 10., Interpolation::Linear);
|
//! let end = Key::new(1., 10., Interpolation::default());
|
||||||
//! let spline = Spline::from_keys(vec![start, end]);
|
//! let spline = Spline::from_vec(vec![start, end]);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! You will notice that we used `Interpolation::Linear` for both the keys. The first key `start`’s
|
//! You will notice that we used `Interpolation::Linear` for the first key. The first key `start`’s
|
||||||
//! interpolation will be used for the whole segment defined by those two keys. The `end`’s
|
//! interpolation will be used for the whole segment defined by those two keys. The `end`’s
|
||||||
//! interpolation won’t be used. You can in theory use any [`Interpolation`] you want for the last
|
//! interpolation won’t be used. You can in theory use any [`Interpolation`] you want for the last
|
||||||
//! key.
|
//! key. We use the default one because we don’t care.
|
||||||
//!
|
//!
|
||||||
//! # Interpolate values
|
//! # Interpolate values
|
||||||
//!
|
//!
|
||||||
@ -43,11 +43,26 @@
|
|||||||
//! # use splines::{Interpolation, Key, Spline};
|
//! # use splines::{Interpolation, Key, Spline};
|
||||||
//! # let start = Key::new(0., 0., Interpolation::Linear);
|
//! # let start = Key::new(0., 0., Interpolation::Linear);
|
||||||
//! # let end = Key::new(1., 10., Interpolation::Linear);
|
//! # let end = Key::new(1., 10., Interpolation::Linear);
|
||||||
//! # let spline = Spline::from_keys(vec![start, end]);
|
//! # let spline = Spline::from_vec(vec![start, end]);
|
||||||
//! assert_eq!(spline.sample(0.), Some(0.));
|
//! assert_eq!(spline.sample(0.), Some(0.));
|
||||||
//! assert_eq!(spline.sample(1.), Some(10.));
|
//! assert_eq!(spline.clamped_sample(1.), 10.);
|
||||||
//! assert_eq!(spline.sample(1.1), None);
|
//! assert_eq!(spline.sample(1.1), None);
|
||||||
//! ```
|
//! ```
|
||||||
|
//!
|
||||||
|
//! It’s possible that you want to get a value even if you’re out-of-bounds. This is especially
|
||||||
|
//! important for simulations / animations. Feel free to use the `Spline::clamped_interpolation` for
|
||||||
|
//! that purpose.
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use splines::{Interpolation, Key, Spline};
|
||||||
|
//! # let start = Key::new(0., 0., Interpolation::Linear);
|
||||||
|
//! # let end = Key::new(1., 10., Interpolation::Linear);
|
||||||
|
//! # let spline = Spline::from_vec(vec![start, end]);
|
||||||
|
//! assert_eq!(spline.clamped_sample(-0.9), 0.); // clamped to the first key
|
||||||
|
//! assert_eq!(spline.clamped_sample(1.1), 10.); // clamped to the last key
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Feel free to have a look at the rest of the documentation for advanced usage.
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::f32::consts;
|
use std::f32::consts;
|
||||||
@ -55,13 +70,14 @@ use std::ops::{Add, Div, Mul, Sub};
|
|||||||
|
|
||||||
/// A spline control point.
|
/// A spline control point.
|
||||||
///
|
///
|
||||||
/// This type associates a value at a given time. It also contains an interpolation object used to
|
/// This type associates a value at a given interpolation parameter value. It also contains an
|
||||||
/// determine how to interpolate values on the segment defined by this key and the next one.
|
/// interpolation hint used to determine how to interpolate values on the segment defined by this
|
||||||
|
/// key and the next one – if existing.
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct Key<T> {
|
pub struct Key<T> {
|
||||||
/// f32 at which the [`Key`] should be reached.
|
/// Interpolation parameter at which the [`Key`] should be reached.
|
||||||
pub t: f32,
|
pub t: f32,
|
||||||
/// Actual value.
|
/// Held value.
|
||||||
pub value: T,
|
pub value: T,
|
||||||
/// Interpolation mode.
|
/// Interpolation mode.
|
||||||
pub interpolation: Interpolation
|
pub interpolation: Interpolation
|
||||||
@ -109,14 +125,25 @@ impl Default for Interpolation {
|
|||||||
pub struct Spline<T>(Vec<Key<T>>);
|
pub struct Spline<T>(Vec<Key<T>>);
|
||||||
|
|
||||||
impl<T> Spline<T> {
|
impl<T> Spline<T> {
|
||||||
/// Create a new spline out of keys. The keys don’t have to be sorted because they’re sorted by
|
/// Create a new spline out of keys. The keys don’t have to be sorted even though it’s recommended
|
||||||
/// this function.
|
/// to provide ascending sorted ones (for performance purposes).
|
||||||
pub fn from_keys(mut keys: Vec<Key<T>>) -> Self {
|
pub fn from_vec(mut keys: Vec<Key<T>>) -> Self {
|
||||||
keys.sort_by(|k0, k1| k0.t.partial_cmp(&k1.t).unwrap_or(Ordering::Less));
|
keys.sort_by(|k0, k1| k0.t.partial_cmp(&k1.t).unwrap_or(Ordering::Less));
|
||||||
|
|
||||||
Spline(keys)
|
Spline(keys)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new spline by consuming an `Iterater<Item = Key<T>>`. They keys don’t have to be
|
||||||
|
/// sorted.
|
||||||
|
///
|
||||||
|
/// # Note on iterators
|
||||||
|
///
|
||||||
|
/// It’s valid to use any iterator that implements `Iterator<Item = Key<T>>`. However, you should
|
||||||
|
/// use `Spline::from_vec` if you are passing a `Vec<_>`. This will remove dynamic allocations.
|
||||||
|
pub fn from_iter<I>(iter: I) -> Self where I: Iterator<Item = Key<T>> {
|
||||||
|
Self::from_vec(iter.collect())
|
||||||
|
}
|
||||||
|
|
||||||
/// Retrieve the keys of a spline.
|
/// Retrieve the keys of a spline.
|
||||||
pub fn keys(&self) -> &[Key<T>] {
|
pub fn keys(&self) -> &[Key<T>] {
|
||||||
&self.0
|
&self.0
|
||||||
@ -124,6 +151,11 @@ impl<T> Spline<T> {
|
|||||||
|
|
||||||
/// Sample a spline at a given time.
|
/// Sample a spline at a given time.
|
||||||
///
|
///
|
||||||
|
/// The current implementation, based on immutability, cannot perform in constant time. This means
|
||||||
|
/// that sampling’s processing complexity is currently *O(log n)*. It’s possible to achieve *O(1)*
|
||||||
|
/// performance by using a slightly different spline type. If you are interested by this feature,
|
||||||
|
/// an implementation for a dedicated type is foreseen yet not started yet.
|
||||||
|
///
|
||||||
/// # Return
|
/// # Return
|
||||||
///
|
///
|
||||||
/// `None` if you try to sample a value at a time that has no key associated with. That can also
|
/// `None` if you try to sample a value at a time that has no key associated with. That can also
|
||||||
@ -133,8 +165,7 @@ impl<T> Spline<T> {
|
|||||||
/// sampling.
|
/// sampling.
|
||||||
pub fn sample(&self, t: f32) -> Option<T> where T: Interpolate {
|
pub fn sample(&self, t: f32) -> Option<T> where T: Interpolate {
|
||||||
let keys = &self.0;
|
let keys = &self.0;
|
||||||
let i = keys.binary_search_by(|key| key.t.partial_cmp(&t).unwrap_or(Ordering::Less)).ok()?;
|
let i = search_lower_cp(keys, t)?;
|
||||||
|
|
||||||
let cp0 = &keys[i];
|
let cp0 = &keys[i];
|
||||||
|
|
||||||
match cp0.interpolation {
|
match cp0.interpolation {
|
||||||
@ -198,6 +229,8 @@ impl<T> Spline<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Iterator over spline keys.
|
/// Iterator over spline keys.
|
||||||
|
///
|
||||||
|
/// This iterator type assures you to iterate over sorted keys.
|
||||||
pub struct Iter<'a, T> where T: 'a {
|
pub struct Iter<'a, T> where T: 'a {
|
||||||
anim_param: &'a Spline<T>,
|
anim_param: &'a Spline<T>,
|
||||||
i: usize
|
i: usize
|
||||||
@ -275,3 +308,36 @@ pub(crate) fn normalize_time<T>(t: f32, cp: &Key<T>, cp1: &Key<T>) -> f32 {
|
|||||||
|
|
||||||
(t - cp.t) / (cp1.t - cp.t)
|
(t - cp.t) / (cp1.t - cp.t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find the lower control point corresponding to a given time.
|
||||||
|
fn search_lower_cp<T>(cps: &[Key<T>], t: f32) -> Option<usize> {
|
||||||
|
let mut i = 0;
|
||||||
|
let len = cps.len();
|
||||||
|
|
||||||
|
if len < 2 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let cp = &cps[i];
|
||||||
|
let cp1 = &cps[i+1];
|
||||||
|
|
||||||
|
if t >= cp1.t {
|
||||||
|
if i >= len - 2 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
} else if t < cp.t {
|
||||||
|
if i == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
i -= 1;
|
||||||
|
} else {
|
||||||
|
break; // found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(i)
|
||||||
|
}
|
||||||
|
103
tests/mod.rs
Normal file
103
tests/mod.rs
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
extern crate splines;
|
||||||
|
|
||||||
|
use splines::{Interpolation, Key, Spline};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn step_interpolation_0() {
|
||||||
|
let start = Key::new(0., 0., Interpolation::Step(0.));
|
||||||
|
let end = Key::new(1., 10., Interpolation::default());
|
||||||
|
let spline = Spline::from_vec(vec![start, end]);
|
||||||
|
|
||||||
|
assert_eq!(spline.sample(0.), Some(10.));
|
||||||
|
assert_eq!(spline.sample(0.1), Some(10.));
|
||||||
|
assert_eq!(spline.sample(0.2), Some(10.));
|
||||||
|
assert_eq!(spline.sample(0.5), Some(10.));
|
||||||
|
assert_eq!(spline.sample(0.9), Some(10.));
|
||||||
|
assert_eq!(spline.sample(1.), None);
|
||||||
|
assert_eq!(spline.clamped_sample(1.), 10.);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn step_interpolation_0_5() {
|
||||||
|
let start = Key::new(0., 0., Interpolation::Step(0.5));
|
||||||
|
let end = Key::new(1., 10., Interpolation::default());
|
||||||
|
let spline = Spline::from_vec(vec![start, end]);
|
||||||
|
|
||||||
|
assert_eq!(spline.sample(0.), Some(0.));
|
||||||
|
assert_eq!(spline.sample(0.1), Some(0.));
|
||||||
|
assert_eq!(spline.sample(0.2), Some(0.));
|
||||||
|
assert_eq!(spline.sample(0.5), Some(10.));
|
||||||
|
assert_eq!(spline.sample(0.9), Some(10.));
|
||||||
|
assert_eq!(spline.sample(1.), None);
|
||||||
|
assert_eq!(spline.clamped_sample(1.), 10.);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn step_interpolation_0_75() {
|
||||||
|
let start = Key::new(0., 0., Interpolation::Step(0.75));
|
||||||
|
let end = Key::new(1., 10., Interpolation::default());
|
||||||
|
let spline = Spline::from_vec(vec![start, end]);
|
||||||
|
|
||||||
|
assert_eq!(spline.sample(0.), Some(0.));
|
||||||
|
assert_eq!(spline.sample(0.1), Some(0.));
|
||||||
|
assert_eq!(spline.sample(0.2), Some(0.));
|
||||||
|
assert_eq!(spline.sample(0.5), Some(0.));
|
||||||
|
assert_eq!(spline.sample(0.9), Some(10.));
|
||||||
|
assert_eq!(spline.sample(1.), None);
|
||||||
|
assert_eq!(spline.clamped_sample(1.), 10.);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn step_interpolation_1() {
|
||||||
|
let start = Key::new(0., 0., Interpolation::Step(1.));
|
||||||
|
let end = Key::new(1., 10., Interpolation::default());
|
||||||
|
let spline = Spline::from_vec(vec![start, end]);
|
||||||
|
|
||||||
|
assert_eq!(spline.sample(0.), Some(0.));
|
||||||
|
assert_eq!(spline.sample(0.1), Some(0.));
|
||||||
|
assert_eq!(spline.sample(0.2), Some(0.));
|
||||||
|
assert_eq!(spline.sample(0.5), Some(0.));
|
||||||
|
assert_eq!(spline.sample(0.9), Some(0.));
|
||||||
|
assert_eq!(spline.sample(1.), None);
|
||||||
|
assert_eq!(spline.clamped_sample(1.), 10.);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn linear_interpolation() {
|
||||||
|
let start = Key::new(0., 0., Interpolation::Linear);
|
||||||
|
let end = Key::new(1., 10., Interpolation::default());
|
||||||
|
let spline = Spline::from_vec(vec![start, end]);
|
||||||
|
|
||||||
|
assert_eq!(spline.sample(0.), Some(0.));
|
||||||
|
assert_eq!(spline.sample(0.1), Some(1.));
|
||||||
|
assert_eq!(spline.sample(0.2), Some(2.));
|
||||||
|
assert_eq!(spline.sample(0.5), Some(5.));
|
||||||
|
assert_eq!(spline.sample(0.9), Some(9.));
|
||||||
|
assert_eq!(spline.sample(1.), None);
|
||||||
|
assert_eq!(spline.clamped_sample(1.), 10.);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn linear_interpolation_several_keys() {
|
||||||
|
let start = Key::new(0., 0., Interpolation::Linear);
|
||||||
|
let k1 = Key::new(1., 5., Interpolation::Linear);
|
||||||
|
let k2 = Key::new(2., 0., Interpolation::Linear);
|
||||||
|
let k3 = Key::new(3., 1., Interpolation::Linear);
|
||||||
|
let k4 = Key::new(10., 2., Interpolation::Linear);
|
||||||
|
let end = Key::new(11., 4., Interpolation::default());
|
||||||
|
let spline = Spline::from_vec(vec![start, k1, k2, k3, k4, end]);
|
||||||
|
|
||||||
|
assert_eq!(spline.sample(0.), Some(0.));
|
||||||
|
assert_eq!(spline.sample(0.1), Some(0.5));
|
||||||
|
assert_eq!(spline.sample(0.2), Some(1.));
|
||||||
|
assert_eq!(spline.sample(0.5), Some(2.5));
|
||||||
|
assert_eq!(spline.sample(0.9), Some(4.5));
|
||||||
|
assert_eq!(spline.sample(1.), Some(5.));
|
||||||
|
assert_eq!(spline.sample(1.5), Some(2.5));
|
||||||
|
assert_eq!(spline.sample(2.), Some(0.));
|
||||||
|
assert_eq!(spline.sample(2.75), Some(0.75));
|
||||||
|
assert_eq!(spline.sample(3.), Some(1.));
|
||||||
|
assert_eq!(spline.sample(6.5), Some(1.5));
|
||||||
|
assert_eq!(spline.sample(10.), Some(2.));
|
||||||
|
assert_eq!(spline.clamped_sample(11.), 4.);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user