Add some unit tests.

This commit is contained in:
Dimitri Sabadie 2018-08-05 17:11:44 +02:00
parent 118a11b911
commit 44154d9728
No known key found for this signature in database
GPG Key ID: DE58C386A8DB2883
2 changed files with 184 additions and 15 deletions

View File

@ -21,14 +21,14 @@
//! 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_keys(vec![start, end]);
//! let end = Key::new(1., 10., Interpolation::default());
//! 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 wont be used. You can in theory use any [`Interpolation`] you want for the last
//! key.
//! key. We use the default one because we dont care.
//!
//! # Interpolate values
//!
@ -43,11 +43,26 @@
//! # 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_keys(vec![start, end]);
//! # let spline = Spline::from_vec(vec![start, end]);
//! 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);
//! ```
//!
//! Its possible that you want to get a value even if youre 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::f32::consts;
@ -55,13 +70,14 @@ use std::ops::{Add, Div, Mul, Sub};
/// A spline control point.
///
/// This type associates a value at a given time. It also contains an interpolation object used to
/// determine how to interpolate values on the segment defined by this key and the next one.
/// This type associates a value at a given interpolation parameter value. It also contains an
/// 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)]
pub struct Key<T> {
/// f32 at which the [`Key`] should be reached.
/// Interpolation parameter at which the [`Key`] should be reached.
pub t: f32,
/// Actual value.
/// Held value.
pub value: T,
/// Interpolation mode.
pub interpolation: Interpolation
@ -109,14 +125,25 @@ impl Default for Interpolation {
pub struct Spline<T>(Vec<Key<T>>);
impl<T> Spline<T> {
/// Create a new spline out of keys. The keys dont have to be sorted because theyre sorted by
/// this function.
pub fn from_keys(mut keys: Vec<Key<T>>) -> Self {
/// Create a new spline out of keys. The keys dont have to be sorted even though its recommended
/// to provide ascending sorted ones (for performance purposes).
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));
Spline(keys)
}
/// Create a new spline by consuming an `Iterater<Item = Key<T>>`. They keys dont have to be
/// sorted.
///
/// # Note on iterators
///
/// Its 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.
pub fn keys(&self) -> &[Key<T>] {
&self.0
@ -124,6 +151,11 @@ impl<T> Spline<T> {
/// Sample a spline at a given time.
///
/// 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)*
/// 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
///
/// `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.
pub fn sample(&self, t: f32) -> Option<T> where T: Interpolate {
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];
match cp0.interpolation {
@ -198,6 +229,8 @@ impl<T> Spline<T> {
}
/// Iterator over spline keys.
///
/// This iterator type assures you to iterate over sorted keys.
pub struct Iter<'a, T> where T: 'a {
anim_param: &'a Spline<T>,
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)
}
// 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
View 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.);
}