Add some unit tests.
This commit is contained in:
		
							
								
								
									
										96
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										96
									
								
								src/lib.rs
									
									
									
									
									
								
							| @@ -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 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 | ||||
| //! | ||||
| @@ -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); | ||||
| //! ``` | ||||
| //! | ||||
| //! 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::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 don’t have to be sorted because they’re sorted by | ||||
|   /// this function. | ||||
|   pub fn from_keys(mut keys: Vec<Key<T>>) -> Self { | ||||
|   /// Create a new spline out of keys. The keys don’t have to be sorted even though it’s 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 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. | ||||
|   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 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 | ||||
|   /// | ||||
|   /// `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
									
								
							
							
						
						
									
										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.); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user