Merge pull request #40 from alexbool/update-nalgebra-0.20
Update nalgebra 0.20 (take 2)
This commit is contained in:
		| @@ -3,9 +3,12 @@ extern crate splines; | ||||
| use splines::{Interpolation, Key, Spline}; | ||||
|  | ||||
| fn main() { | ||||
|   let keys = vec![Key::new(0., 0., Interpolation::default()), Key::new(5., 1., Interpolation::default())]; | ||||
|   let spline = Spline::from_vec(keys); | ||||
|     let keys = vec![ | ||||
|         Key::new(0., 0., Interpolation::default()), | ||||
|         Key::new(5., 1., Interpolation::default()), | ||||
|     ]; | ||||
|     let spline = Spline::from_vec(keys); | ||||
|  | ||||
|   println!("value at 0: {:?}", spline.clamped_sample(0.)); | ||||
|   println!("value at 3: {:?}", spline.clamped_sample(3.)); | ||||
|     println!("value at 0: {:?}", spline.clamped_sample(0.)); | ||||
|     println!("value at 3: {:?}", spline.clamped_sample(3.)); | ||||
| } | ||||
|   | ||||
| @@ -1,30 +1,31 @@ | ||||
| #[macro_use] extern crate serde_json; | ||||
| #[macro_use] | ||||
| extern crate serde_json; | ||||
| extern crate splines; | ||||
|  | ||||
| use serde_json::from_value; | ||||
| use splines::Spline; | ||||
|  | ||||
| fn main() { | ||||
|   let value = json!{ | ||||
|     [ | ||||
|       { | ||||
|         "t": 0, | ||||
|         "interpolation": "linear", | ||||
|         "value": 0 | ||||
|       }, | ||||
|       { | ||||
|         "t": 1, | ||||
|         "interpolation": { "step": 0.5 }, | ||||
|         "value": 1 | ||||
|       }, | ||||
|       { | ||||
|         "t": 5, | ||||
|         "interpolation": "cosine", | ||||
|         "value": 10 | ||||
|       }, | ||||
|     ] | ||||
|   }; | ||||
|     let value = json! { | ||||
|       [ | ||||
|         { | ||||
|           "t": 0, | ||||
|           "interpolation": "linear", | ||||
|           "value": 0 | ||||
|         }, | ||||
|         { | ||||
|           "t": 1, | ||||
|           "interpolation": { "step": 0.5 }, | ||||
|           "value": 1 | ||||
|         }, | ||||
|         { | ||||
|           "t": 5, | ||||
|           "interpolation": "cosine", | ||||
|           "value": 10 | ||||
|         }, | ||||
|       ] | ||||
|     }; | ||||
|  | ||||
|   let spline = from_value::<Spline<f32, f32>>(value); | ||||
|   println!("{:?}", spline); | ||||
|     let spline = from_value::<Spline<f32, f32>>(value); | ||||
|     println!("{:?}", spline); | ||||
| } | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| use cgmath::{ | ||||
|   BaseFloat, BaseNum, InnerSpace, Quaternion, Vector1, Vector2, Vector3, Vector4, VectorSpace | ||||
|     BaseFloat, BaseNum, InnerSpace, Quaternion, Vector1, Vector2, Vector3, Vector4, VectorSpace, | ||||
| }; | ||||
|  | ||||
| use crate::interpolate::{ | ||||
|   Additive, Interpolate, Linear, One, cubic_bezier_def, cubic_hermite_def, quadratic_bezier_def | ||||
|     cubic_bezier_def, cubic_hermite_def, quadratic_bezier_def, Additive, Interpolate, Linear, One, | ||||
| }; | ||||
|  | ||||
| macro_rules! impl_interpolate_vec { | ||||
| @@ -50,37 +50,43 @@ impl_interpolate_vec!(Vector2); | ||||
| impl_interpolate_vec!(Vector3); | ||||
| impl_interpolate_vec!(Vector4); | ||||
|  | ||||
| impl<T> Linear<T> for Quaternion<T> where T: BaseFloat { | ||||
|   #[inline(always)] | ||||
|   fn outer_mul(self, t: T) -> Self { | ||||
|     self * t | ||||
|   } | ||||
| impl<T> Linear<T> for Quaternion<T> | ||||
| where | ||||
|     T: BaseFloat, | ||||
| { | ||||
|     #[inline(always)] | ||||
|     fn outer_mul(self, t: T) -> Self { | ||||
|         self * t | ||||
|     } | ||||
|  | ||||
|   #[inline(always)] | ||||
|   fn outer_div(self, t: T) -> Self { | ||||
|     self / t | ||||
|   } | ||||
|     #[inline(always)] | ||||
|     fn outer_div(self, t: T) -> Self { | ||||
|         self / t | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T> Interpolate<T> for Quaternion<T> | ||||
| where Self: InnerSpace<Scalar = T>, T: Additive + BaseFloat + One { | ||||
|   #[inline(always)] | ||||
|   fn lerp(a: Self, b: Self, t: T) -> Self { | ||||
|     a.nlerp(b, t) | ||||
|   } | ||||
| where | ||||
|     Self: InnerSpace<Scalar = T>, | ||||
|     T: Additive + BaseFloat + One, | ||||
| { | ||||
|     #[inline(always)] | ||||
|     fn lerp(a: Self, b: Self, t: T) -> Self { | ||||
|         a.nlerp(b, t) | ||||
|     } | ||||
|  | ||||
|   #[inline(always)] | ||||
|   fn cubic_hermite(x: (Self, T), a: (Self, T), b: (Self, T), y: (Self, T), t: T) -> Self { | ||||
|     cubic_hermite_def(x, a, b, y, t) | ||||
|   } | ||||
|     #[inline(always)] | ||||
|     fn cubic_hermite(x: (Self, T), a: (Self, T), b: (Self, T), y: (Self, T), t: T) -> Self { | ||||
|         cubic_hermite_def(x, a, b, y, t) | ||||
|     } | ||||
|  | ||||
|   #[inline(always)] | ||||
|   fn quadratic_bezier(a: Self, u: Self, b: Self, t: T) -> Self { | ||||
|     quadratic_bezier_def(a, u, b, t) | ||||
|   } | ||||
|     #[inline(always)] | ||||
|     fn quadratic_bezier(a: Self, u: Self, b: Self, t: T) -> Self { | ||||
|         quadratic_bezier_def(a, u, b, t) | ||||
|     } | ||||
|  | ||||
|   #[inline(always)] | ||||
|   fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: T) -> Self { | ||||
|     cubic_bezier_def(a, u, v, b, t) | ||||
|   } | ||||
|     #[inline(always)] | ||||
|     fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: T) -> Self { | ||||
|         cubic_bezier_def(a, u, v, b, t) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -28,14 +28,22 @@ | ||||
| //! [`Trigo`]: crate::interpolate::Trigo | ||||
| //! [num-traits]: https://crates.io/crates/num-traits | ||||
|  | ||||
| #[cfg(feature = "std")] use std::f32; | ||||
| #[cfg(not(feature = "std"))] use core::f32; | ||||
| #[cfg(not(feature = "std"))] use core::intrinsics::cosf32; | ||||
| #[cfg(feature = "std")] use std::f64; | ||||
| #[cfg(not(feature = "std"))] use core::f64; | ||||
| #[cfg(not(feature = "std"))] use core::intrinsics::cosf64; | ||||
| #[cfg(feature = "std")] use std::ops::{Add, Mul, Sub}; | ||||
| #[cfg(not(feature = "std"))] use core::ops::{Add, Mul, Sub}; | ||||
| #[cfg(not(feature = "std"))] | ||||
| use core::f32; | ||||
| #[cfg(not(feature = "std"))] | ||||
| use core::f64; | ||||
| #[cfg(not(feature = "std"))] | ||||
| use core::intrinsics::cosf32; | ||||
| #[cfg(not(feature = "std"))] | ||||
| use core::intrinsics::cosf64; | ||||
| #[cfg(not(feature = "std"))] | ||||
| use core::ops::{Add, Mul, Sub}; | ||||
| #[cfg(feature = "std")] | ||||
| use std::f32; | ||||
| #[cfg(feature = "std")] | ||||
| use std::f64; | ||||
| #[cfg(feature = "std")] | ||||
| use std::ops::{Add, Mul, Sub}; | ||||
|  | ||||
| /// Keys that can be interpolated in between. Implementing this trait is required to perform | ||||
| /// sampling on splines. | ||||
| @@ -46,80 +54,72 @@ | ||||
| /// | ||||
| /// [`Spline::sample`]: crate::spline::Spline::sample | ||||
| pub trait Interpolate<T>: Sized + Copy + Linear<T> { | ||||
|   /// Linear interpolation. | ||||
|   fn lerp(a: Self, b: Self, t: T) -> Self; | ||||
|     /// Linear interpolation. | ||||
|     fn lerp(a: Self, b: Self, t: T) -> Self; | ||||
|  | ||||
|   /// Cubic hermite interpolation. | ||||
|   /// | ||||
|   /// Default to [`lerp`]. | ||||
|   /// | ||||
|   /// [`lerp`]: Interpolate::lerp | ||||
|   fn cubic_hermite(_: (Self, T), a: (Self, T), b: (Self, T), _: (Self, T), t: T) -> Self { | ||||
|     Self::lerp(a.0, b.0, t) | ||||
|   } | ||||
|     /// Cubic hermite interpolation. | ||||
|     /// | ||||
|     /// Default to [`lerp`]. | ||||
|     /// | ||||
|     /// [`lerp`]: Interpolate::lerp | ||||
|     fn cubic_hermite(_: (Self, T), a: (Self, T), b: (Self, T), _: (Self, T), t: T) -> Self { | ||||
|         Self::lerp(a.0, b.0, t) | ||||
|     } | ||||
|  | ||||
|   /// Quadratic Bézier interpolation. | ||||
|   fn quadratic_bezier(a: Self, u: Self, b: Self, t: T) -> Self; | ||||
|     /// Quadratic Bézier interpolation. | ||||
|     fn quadratic_bezier(a: Self, u: Self, b: Self, t: T) -> Self; | ||||
|  | ||||
|   /// Cubic Bézier interpolation. | ||||
|   fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: T) -> Self; | ||||
|     /// Cubic Bézier interpolation. | ||||
|     fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: T) -> Self; | ||||
| } | ||||
|  | ||||
| /// Set of types that support additions and subtraction. | ||||
| /// | ||||
| /// The [`Copy`] trait is also a supertrait as it’s likely to be used everywhere. | ||||
| pub trait Additive: | ||||
|   Copy + | ||||
|   Add<Self, Output = Self> + | ||||
|   Sub<Self, Output = Self> { | ||||
| } | ||||
| pub trait Additive: Copy + Add<Self, Output = Self> + Sub<Self, Output = Self> {} | ||||
|  | ||||
| impl<T> Additive for T | ||||
| where T: Copy + | ||||
|          Add<Self, Output = Self> + | ||||
|          Sub<Self, Output = Self> { | ||||
| } | ||||
| impl<T> Additive for T where T: Copy + Add<Self, Output = Self> + Sub<Self, Output = Self> {} | ||||
|  | ||||
| /// Set of additive types that support outer multiplication and division, making them linear. | ||||
| pub trait Linear<T>: Additive { | ||||
|   /// Apply an outer multiplication law. | ||||
|   fn outer_mul(self, t: T) -> Self; | ||||
|     /// Apply an outer multiplication law. | ||||
|     fn outer_mul(self, t: T) -> Self; | ||||
|  | ||||
|   /// Apply an outer division law. | ||||
|   fn outer_div(self, t: T) -> Self; | ||||
|     /// Apply an outer division law. | ||||
|     fn outer_div(self, t: T) -> Self; | ||||
| } | ||||
|  | ||||
| macro_rules! impl_linear_simple { | ||||
|   ($t:ty) => { | ||||
|     impl Linear<$t> for $t { | ||||
|       fn outer_mul(self, t: $t) -> Self { | ||||
|         self * t | ||||
|       } | ||||
|     ($t:ty) => { | ||||
|         impl Linear<$t> for $t { | ||||
|             fn outer_mul(self, t: $t) -> Self { | ||||
|                 self * t | ||||
|             } | ||||
|  | ||||
|       /// Apply an outer division law. | ||||
|       fn outer_div(self, t: $t) -> Self { | ||||
|         self / t | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|             /// Apply an outer division law. | ||||
|             fn outer_div(self, t: $t) -> Self { | ||||
|                 self / t | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| impl_linear_simple!(f32); | ||||
| impl_linear_simple!(f64); | ||||
|  | ||||
| macro_rules! impl_linear_cast { | ||||
|   ($t:ty, $q:ty) => { | ||||
|     impl Linear<$t> for $q { | ||||
|       fn outer_mul(self, t: $t) -> Self { | ||||
|         self * t as $q | ||||
|       } | ||||
|     ($t:ty, $q:ty) => { | ||||
|         impl Linear<$t> for $q { | ||||
|             fn outer_mul(self, t: $t) -> Self { | ||||
|                 self * t as $q | ||||
|             } | ||||
|  | ||||
|       /// Apply an outer division law. | ||||
|       fn outer_div(self, t: $t) -> Self { | ||||
|         self / t as $q | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|             /// Apply an outer division law. | ||||
|             fn outer_div(self, t: $t) -> Self { | ||||
|                 self / t as $q | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| impl_linear_cast!(f32, f64); | ||||
| @@ -127,19 +127,19 @@ impl_linear_cast!(f64, f32); | ||||
|  | ||||
| /// Types with a neutral element for multiplication. | ||||
| pub trait One { | ||||
|   /// The neutral element for the multiplicative monoid — typically called `1`. | ||||
|   fn one() -> Self; | ||||
|     /// The neutral element for the multiplicative monoid — typically called `1`. | ||||
|     fn one() -> Self; | ||||
| } | ||||
|  | ||||
| macro_rules! impl_one_float { | ||||
|   ($t:ty) => { | ||||
|     impl One for $t { | ||||
|       #[inline(always)] | ||||
|       fn one() -> Self { | ||||
|         1. | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|     ($t:ty) => { | ||||
|         impl One for $t { | ||||
|             #[inline(always)] | ||||
|             fn one() -> Self { | ||||
|                 1. | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| impl_one_float!(f32); | ||||
| @@ -147,147 +147,177 @@ impl_one_float!(f64); | ||||
|  | ||||
| /// Types with a sane definition of π and cosine. | ||||
| pub trait Trigo { | ||||
|   /// π. | ||||
|   fn pi() -> Self; | ||||
|     /// π. | ||||
|     fn pi() -> Self; | ||||
|  | ||||
|   /// Cosine of the argument. | ||||
|   fn cos(self) -> Self; | ||||
|     /// Cosine of the argument. | ||||
|     fn cos(self) -> Self; | ||||
| } | ||||
|  | ||||
| impl Trigo for f32 { | ||||
|   #[inline(always)] | ||||
|   fn pi() -> Self { | ||||
|     f32::consts::PI | ||||
|   } | ||||
|  | ||||
|   #[inline(always)] | ||||
|   fn cos(self) -> Self { | ||||
|     #[cfg(feature = "std")] | ||||
|     { | ||||
|       self.cos() | ||||
|     #[inline(always)] | ||||
|     fn pi() -> Self { | ||||
|         f32::consts::PI | ||||
|     } | ||||
|  | ||||
|     #[cfg(not(feature = "std"))] | ||||
|     { | ||||
|       unsafe { cosf32(self) } | ||||
|     #[inline(always)] | ||||
|     fn cos(self) -> Self { | ||||
|         #[cfg(feature = "std")] | ||||
|         { | ||||
|             self.cos() | ||||
|         } | ||||
|  | ||||
|         #[cfg(not(feature = "std"))] | ||||
|         { | ||||
|             unsafe { cosf32(self) } | ||||
|         } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| impl Trigo for f64 { | ||||
|   #[inline(always)] | ||||
|   fn pi() -> Self { | ||||
|     f64::consts::PI | ||||
|   } | ||||
|  | ||||
|   #[inline(always)] | ||||
|   fn cos(self) -> Self { | ||||
|     #[cfg(feature = "std")] | ||||
|     { | ||||
|       self.cos() | ||||
|     #[inline(always)] | ||||
|     fn pi() -> Self { | ||||
|         f64::consts::PI | ||||
|     } | ||||
|  | ||||
|     #[cfg(not(feature = "std"))] | ||||
|     { | ||||
|       unsafe { cosf64(self) } | ||||
|     #[inline(always)] | ||||
|     fn cos(self) -> Self { | ||||
|         #[cfg(feature = "std")] | ||||
|         { | ||||
|             self.cos() | ||||
|         } | ||||
|  | ||||
|         #[cfg(not(feature = "std"))] | ||||
|         { | ||||
|             unsafe { cosf64(self) } | ||||
|         } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| /// Default implementation of [`Interpolate::cubic_hermite`]. | ||||
| /// | ||||
| /// `V` is the value being interpolated. `T` is the sampling value (also sometimes called time). | ||||
| pub fn cubic_hermite_def<V, T>(x: (V, T), a: (V, T), b: (V, T), y: (V, T), t: T) -> V | ||||
| where V: Linear<T>, | ||||
|       T: Additive + Mul<T, Output = T> + One { | ||||
|   // some stupid generic constants, because Rust doesn’t have polymorphic literals… | ||||
|   let one_t = T::one(); | ||||
|   let two_t = one_t + one_t; // lolololol | ||||
|   let three_t = two_t + one_t; // megalol | ||||
| where | ||||
|     V: Linear<T>, | ||||
|     T: Additive + Mul<T, Output = T> + One, | ||||
| { | ||||
|     // some stupid generic constants, because Rust doesn’t have polymorphic literals… | ||||
|     let one_t = T::one(); | ||||
|     let two_t = one_t + one_t; // lolololol | ||||
|     let three_t = two_t + one_t; // megalol | ||||
|  | ||||
|   // sampler stuff | ||||
|   let t2 = t * t; | ||||
|   let t3 = t2 * t; | ||||
|   let two_t3 = t3 * two_t; | ||||
|   let three_t2 = t2 * three_t; | ||||
|     // sampler stuff | ||||
|     let t2 = t * t; | ||||
|     let t3 = t2 * t; | ||||
|     let two_t3 = t3 * two_t; | ||||
|     let three_t2 = t2 * three_t; | ||||
|  | ||||
|   // tangents | ||||
|   let m0 = (b.0 - x.0).outer_div(b.1 - x.1); | ||||
|   let m1 = (y.0 - a.0).outer_div(y.1 - a.1); | ||||
|     // tangents | ||||
|     let m0 = (b.0 - x.0).outer_div(b.1 - x.1); | ||||
|     let m1 = (y.0 - a.0).outer_div(y.1 - a.1); | ||||
|  | ||||
|   a.0.outer_mul(two_t3 - three_t2 + one_t) + m0.outer_mul(t3 - t2 * two_t + t) + b.0.outer_mul(three_t2 - two_t3) + m1.outer_mul(t3 - t2) | ||||
|     a.0.outer_mul(two_t3 - three_t2 + one_t) | ||||
|         + m0.outer_mul(t3 - t2 * two_t + t) | ||||
|         + b.0.outer_mul(three_t2 - two_t3) | ||||
|         + m1.outer_mul(t3 - t2) | ||||
| } | ||||
|  | ||||
| /// Default implementation of [`Interpolate::quadratic_bezier`]. | ||||
| /// | ||||
| /// `V` is the value being interpolated. `T` is the sampling value (also sometimes called time). | ||||
| pub fn quadratic_bezier_def<V, T>(a: V, u: V, b: V, t: T) -> V | ||||
| where V: Linear<T>, | ||||
|       T: Additive + Mul<T, Output = T> + One { | ||||
|   let one_t = T::one() - t; | ||||
|   let one_t_2 = one_t * one_t; | ||||
|   u + (a - u).outer_mul(one_t_2) + (b - u).outer_mul(t * t) | ||||
| where | ||||
|     V: Linear<T>, | ||||
|     T: Additive + Mul<T, Output = T> + One, | ||||
| { | ||||
|     let one_t = T::one() - t; | ||||
|     let one_t_2 = one_t * one_t; | ||||
|     u + (a - u).outer_mul(one_t_2) + (b - u).outer_mul(t * t) | ||||
| } | ||||
|  | ||||
| /// Default implementation of [`Interpolate::cubic_bezier`]. | ||||
| /// | ||||
| /// `V` is the value being interpolated. `T` is the sampling value (also sometimes called time). | ||||
| pub fn cubic_bezier_def<V, T>(a: V, u: V, v: V, b: V, t: T) -> V | ||||
| where V: Linear<T>, | ||||
|       T: Additive + Mul<T, Output = T> + One { | ||||
|   let one_t = T::one() - t; | ||||
|   let one_t_2 = one_t * one_t; | ||||
|   let one_t_3 = one_t_2 * one_t; | ||||
|   let three = T::one() + T::one() + T::one(); | ||||
| where | ||||
|     V: Linear<T>, | ||||
|     T: Additive + Mul<T, Output = T> + One, | ||||
| { | ||||
|     let one_t = T::one() - t; | ||||
|     let one_t_2 = one_t * one_t; | ||||
|     let one_t_3 = one_t_2 * one_t; | ||||
|     let three = T::one() + T::one() + T::one(); | ||||
|  | ||||
|   a.outer_mul(one_t_3) + u.outer_mul(three * one_t_2 * t) + v.outer_mul(three * one_t * t * t) + b.outer_mul(t * t * t) | ||||
|     a.outer_mul(one_t_3) | ||||
|         + u.outer_mul(three * one_t_2 * t) | ||||
|         + v.outer_mul(three * one_t * t * t) | ||||
|         + b.outer_mul(t * t * t) | ||||
| } | ||||
|  | ||||
| macro_rules! impl_interpolate_simple { | ||||
|   ($t:ty) => { | ||||
|     impl Interpolate<$t> for $t { | ||||
|       fn lerp(a: Self, b: Self, t: $t) -> Self { | ||||
|         a * (1. - t) + b * t | ||||
|       } | ||||
|     ($t:ty) => { | ||||
|         impl Interpolate<$t> for $t { | ||||
|             fn lerp(a: Self, b: Self, t: $t) -> Self { | ||||
|                 a * (1. - t) + b * t | ||||
|             } | ||||
|  | ||||
|       fn cubic_hermite(x: (Self, $t), a: (Self, $t), b: (Self, $t), y: (Self, $t), t: $t) -> Self { | ||||
|         cubic_hermite_def(x, a, b, y, t) | ||||
|       } | ||||
|             fn cubic_hermite( | ||||
|                 x: (Self, $t), | ||||
|                 a: (Self, $t), | ||||
|                 b: (Self, $t), | ||||
|                 y: (Self, $t), | ||||
|                 t: $t, | ||||
|             ) -> Self { | ||||
|                 cubic_hermite_def(x, a, b, y, t) | ||||
|             } | ||||
|  | ||||
|       fn quadratic_bezier(a: Self, u: Self, b: Self, t: $t) -> Self { | ||||
|         quadratic_bezier_def(a, u, b, t) | ||||
|       } | ||||
|             fn quadratic_bezier(a: Self, u: Self, b: Self, t: $t) -> Self { | ||||
|                 quadratic_bezier_def(a, u, b, t) | ||||
|             } | ||||
|  | ||||
|       fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: $t) -> Self { | ||||
|         cubic_bezier_def(a, u, v, b, t) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|             fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: $t) -> Self { | ||||
|                 cubic_bezier_def(a, u, v, b, t) | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| impl_interpolate_simple!(f32); | ||||
| impl_interpolate_simple!(f64); | ||||
|  | ||||
| macro_rules! impl_interpolate_via { | ||||
|   ($t:ty, $v:ty) => { | ||||
|     impl Interpolate<$t> for $v { | ||||
|       fn lerp(a: Self, b: Self, t: $t) -> Self { | ||||
|         a * (1. - t as $v) + b * t as $v | ||||
|       } | ||||
|     ($t:ty, $v:ty) => { | ||||
|         impl Interpolate<$t> for $v { | ||||
|             fn lerp(a: Self, b: Self, t: $t) -> Self { | ||||
|                 a * (1. - t as $v) + b * t as $v | ||||
|             } | ||||
|  | ||||
|       fn cubic_hermite((x, xt): (Self, $t), (a, at): (Self, $t), (b, bt): (Self, $t), (y, yt): (Self, $t), t: $t) -> Self { | ||||
|         cubic_hermite_def((x, xt as $v), (a, at as $v), (b, bt as $v), (y, yt as $v), t as $v) | ||||
|       } | ||||
|             fn cubic_hermite( | ||||
|                 (x, xt): (Self, $t), | ||||
|                 (a, at): (Self, $t), | ||||
|                 (b, bt): (Self, $t), | ||||
|                 (y, yt): (Self, $t), | ||||
|                 t: $t, | ||||
|             ) -> Self { | ||||
|                 cubic_hermite_def( | ||||
|                     (x, xt as $v), | ||||
|                     (a, at as $v), | ||||
|                     (b, bt as $v), | ||||
|                     (y, yt as $v), | ||||
|                     t as $v, | ||||
|                 ) | ||||
|             } | ||||
|  | ||||
|       fn quadratic_bezier(a: Self, u: Self, b: Self, t: $t) -> Self { | ||||
|         quadratic_bezier_def(a, u, b, t as $v) | ||||
|       } | ||||
|             fn quadratic_bezier(a: Self, u: Self, b: Self, t: $t) -> Self { | ||||
|                 quadratic_bezier_def(a, u, b, t as $v) | ||||
|             } | ||||
|  | ||||
|       fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: $t) -> Self { | ||||
|         cubic_bezier_def(a, u, v, b, t as $v) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|             fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: $t) -> Self { | ||||
|                 cubic_bezier_def(a, u, v, b, t as $v) | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| impl_interpolate_via!(f32, f64); | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| //! Available interpolation modes. | ||||
|  | ||||
| #[cfg(feature = "serialization")] use serde_derive::{Deserialize, Serialize}; | ||||
| #[cfg(feature = "serialization")] | ||||
| use serde_derive::{Deserialize, Serialize}; | ||||
|  | ||||
| /// Available kind of interpolations. | ||||
| /// | ||||
| @@ -9,56 +10,56 @@ | ||||
| #[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))] | ||||
| #[cfg_attr(feature = "serialization", serde(rename_all = "snake_case"))] | ||||
| pub enum Interpolation<T, V> { | ||||
|   /// Hold a [`Key`] until the sampling value passes the normalized step threshold, in which | ||||
|   /// case the next key is used. | ||||
|   /// | ||||
|   /// > Note: if you set the threshold to `0.5`, the first key will be used until half the time | ||||
|   /// > between the two keys; the second key will be in used afterwards. If you set it to `1.0`, the | ||||
|   /// > first key will be kept until the next key. Set it to `0.` and the first key will never be | ||||
|   /// > used. | ||||
|   /// | ||||
|   /// [`Key`]: crate::key::Key | ||||
|   Step(T), | ||||
|   /// Linear interpolation between a key and the next one. | ||||
|   Linear, | ||||
|   /// Cosine interpolation between a key and the next one. | ||||
|   Cosine, | ||||
|   /// Catmull-Rom interpolation, performing a cubic Hermite interpolation using four keys. | ||||
|   CatmullRom, | ||||
|   /// Bézier interpolation. | ||||
|   /// | ||||
|   /// A control point that uses such an interpolation is associated with an extra point. The segmant | ||||
|   /// connecting both is called the _tangent_ of this point. The part of the spline defined between | ||||
|   /// this control point and the next one will be interpolated across with Bézier interpolation. Two | ||||
|   /// cases are possible: | ||||
|   /// | ||||
|   /// - The next control point also has a Bézier interpolation mode. In this case, its tangent is | ||||
|   ///   used for the interpolation process. This is called _cubic Bézier interpolation_ and it | ||||
|   ///   kicks ass. | ||||
|   /// - The next control point doesn’t have a Bézier interpolation mode set. In this case, the | ||||
|   ///   tangent used for the next control point is defined as the segment connecting that control | ||||
|   ///   point and the current control point’s associated point. This is called _quadratic Bézer | ||||
|   ///   interpolation_ and it kicks ass too, but a bit less than cubic. | ||||
|   Bezier(V), | ||||
|   /// A special Bézier interpolation using an _input tangent_ and an _output tangent_. | ||||
|   /// | ||||
|   /// With this kind of interpolation, a control point has an input tangent, which has the same role | ||||
|   /// as the one defined by [`Interpolation::Bezier`], and an output tangent, which has the same | ||||
|   /// role defined by the next key’s [`Interpolation::Bezier`] if present, normally. | ||||
|   /// | ||||
|   /// What it means is that instead of setting the output tangent as the next key’s Bézier tangent, | ||||
|   /// this interpolation mode allows you to manually set the output tangent. That will yield more | ||||
|   /// control on the tangents but might generate discontinuities. Use with care. | ||||
|   /// | ||||
|   /// Stroke Bézier interpolation is always a cubic Bézier interpolation by default. | ||||
|   StrokeBezier(V, V), | ||||
|   #[doc(hidden)] | ||||
|   __NonExhaustive | ||||
|     /// Hold a [`Key`] until the sampling value passes the normalized step threshold, in which | ||||
|     /// case the next key is used. | ||||
|     /// | ||||
|     /// > Note: if you set the threshold to `0.5`, the first key will be used until half the time | ||||
|     /// > between the two keys; the second key will be in used afterwards. If you set it to `1.0`, the | ||||
|     /// > first key will be kept until the next key. Set it to `0.` and the first key will never be | ||||
|     /// > used. | ||||
|     /// | ||||
|     /// [`Key`]: crate::key::Key | ||||
|     Step(T), | ||||
|     /// Linear interpolation between a key and the next one. | ||||
|     Linear, | ||||
|     /// Cosine interpolation between a key and the next one. | ||||
|     Cosine, | ||||
|     /// Catmull-Rom interpolation, performing a cubic Hermite interpolation using four keys. | ||||
|     CatmullRom, | ||||
|     /// Bézier interpolation. | ||||
|     /// | ||||
|     /// A control point that uses such an interpolation is associated with an extra point. The segmant | ||||
|     /// connecting both is called the _tangent_ of this point. The part of the spline defined between | ||||
|     /// this control point and the next one will be interpolated across with Bézier interpolation. Two | ||||
|     /// cases are possible: | ||||
|     /// | ||||
|     /// - The next control point also has a Bézier interpolation mode. In this case, its tangent is | ||||
|     ///   used for the interpolation process. This is called _cubic Bézier interpolation_ and it | ||||
|     ///   kicks ass. | ||||
|     /// - The next control point doesn’t have a Bézier interpolation mode set. In this case, the | ||||
|     ///   tangent used for the next control point is defined as the segment connecting that control | ||||
|     ///   point and the current control point’s associated point. This is called _quadratic Bézer | ||||
|     ///   interpolation_ and it kicks ass too, but a bit less than cubic. | ||||
|     Bezier(V), | ||||
|     /// A special Bézier interpolation using an _input tangent_ and an _output tangent_. | ||||
|     /// | ||||
|     /// With this kind of interpolation, a control point has an input tangent, which has the same role | ||||
|     /// as the one defined by [`Interpolation::Bezier`], and an output tangent, which has the same | ||||
|     /// role defined by the next key’s [`Interpolation::Bezier`] if present, normally. | ||||
|     /// | ||||
|     /// What it means is that instead of setting the output tangent as the next key’s Bézier tangent, | ||||
|     /// this interpolation mode allows you to manually set the output tangent. That will yield more | ||||
|     /// control on the tangents but might generate discontinuities. Use with care. | ||||
|     /// | ||||
|     /// Stroke Bézier interpolation is always a cubic Bézier interpolation by default. | ||||
|     StrokeBezier(V, V), | ||||
|     #[doc(hidden)] | ||||
|     __NonExhaustive, | ||||
| } | ||||
|  | ||||
| impl<T, V> Default for Interpolation<T, V> { | ||||
|   /// [`Interpolation::Linear`] is the default. | ||||
|   fn default() -> Self { | ||||
|     Interpolation::Linear | ||||
|   } | ||||
|     /// [`Interpolation::Linear`] is the default. | ||||
|     fn default() -> Self { | ||||
|         Interpolation::Linear | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										38
									
								
								src/iter.rs
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								src/iter.rs
									
									
									
									
									
								
							| @@ -11,34 +11,34 @@ use crate::{Key, Spline}; | ||||
| /// Iterator over spline keys. | ||||
| /// | ||||
| /// This iterator type is guaranteed to iterate over sorted keys. | ||||
| pub struct Iter<'a, T, V> where T: 'a, V: 'a { | ||||
|   spline: &'a Spline<T, V>, | ||||
|   i: usize | ||||
| pub struct Iter<'a, T, V> | ||||
| where | ||||
|     T: 'a, | ||||
|     V: 'a, | ||||
| { | ||||
|     spline: &'a Spline<T, V>, | ||||
|     i: usize, | ||||
| } | ||||
|  | ||||
| impl<'a, T, V> Iterator for Iter<'a, T, V> { | ||||
|   type Item = &'a Key<T, V>; | ||||
|     type Item = &'a Key<T, V>; | ||||
|  | ||||
|   fn next(&mut self) -> Option<Self::Item> { | ||||
|     let r = self.spline.0.get(self.i); | ||||
|     fn next(&mut self) -> Option<Self::Item> { | ||||
|         let r = self.spline.0.get(self.i); | ||||
|  | ||||
|     if let Some(_) = r { | ||||
|       self.i += 1; | ||||
|         if let Some(_) = r { | ||||
|             self.i += 1; | ||||
|         } | ||||
|  | ||||
|         r | ||||
|     } | ||||
|  | ||||
|     r | ||||
|   } | ||||
| } | ||||
|  | ||||
| impl<'a, T, V> IntoIterator for &'a Spline<T, V> { | ||||
|   type Item = &'a Key<T, V>; | ||||
|   type IntoIter = Iter<'a, T, V>; | ||||
|     type Item = &'a Key<T, V>; | ||||
|     type IntoIter = Iter<'a, T, V>; | ||||
|  | ||||
|   fn into_iter(self) -> Self::IntoIter { | ||||
|     Iter { | ||||
|       spline: self, | ||||
|       i: 0 | ||||
|     fn into_iter(self) -> Self::IntoIter { | ||||
|         Iter { spline: self, i: 0 } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										27
									
								
								src/key.rs
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								src/key.rs
									
									
									
									
									
								
							| @@ -6,7 +6,8 @@ | ||||
| //! Splines constructed with this crate have the property that it’s possible to change the | ||||
| //! interpolation mode on a key-based way, allowing you to implement and encode complex curves. | ||||
|  | ||||
| #[cfg(feature = "serialization")] use serde_derive::{Deserialize, Serialize}; | ||||
| #[cfg(feature = "serialization")] | ||||
| use serde_derive::{Deserialize, Serialize}; | ||||
|  | ||||
| use crate::interpolation::Interpolation; | ||||
|  | ||||
| @@ -21,17 +22,21 @@ use crate::interpolation::Interpolation; | ||||
| #[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))] | ||||
| #[cfg_attr(feature = "serialization", serde(rename_all = "snake_case"))] | ||||
| pub struct Key<T, V> { | ||||
|   /// Interpolation parameter at which the [`Key`] should be reached. | ||||
|   pub t: T, | ||||
|   /// Carried value. | ||||
|   pub value: V, | ||||
|   /// Interpolation mode. | ||||
|   pub interpolation: Interpolation<T, V> | ||||
|     /// Interpolation parameter at which the [`Key`] should be reached. | ||||
|     pub t: T, | ||||
|     /// Carried value. | ||||
|     pub value: V, | ||||
|     /// Interpolation mode. | ||||
|     pub interpolation: Interpolation<T, V>, | ||||
| } | ||||
|  | ||||
| impl<T, V> Key<T, V> { | ||||
|   /// Create a new key. | ||||
|   pub fn new(t: T, value: V, interpolation: Interpolation<T, V>) -> Self { | ||||
|     Key { t, value, interpolation } | ||||
|   } | ||||
|     /// Create a new key. | ||||
|     pub fn new(t: T, value: V, interpolation: Interpolation<T, V>) -> Self { | ||||
|         Key { | ||||
|             t, | ||||
|             value, | ||||
|             interpolation, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -106,14 +106,17 @@ | ||||
| #![cfg_attr(not(feature = "std"), feature(alloc))] | ||||
| #![cfg_attr(not(feature = "std"), feature(core_intrinsics))] | ||||
|  | ||||
| #[cfg(not(feature = "std"))] extern crate alloc; | ||||
| #[cfg(not(feature = "std"))] | ||||
| extern crate alloc; | ||||
|  | ||||
| #[cfg(feature = "impl-cgmath")] mod cgmath; | ||||
| #[cfg(feature = "impl-cgmath")] | ||||
| mod cgmath; | ||||
| pub mod interpolate; | ||||
| pub mod interpolation; | ||||
| pub mod iter; | ||||
| pub mod key; | ||||
| #[cfg(feature = "impl-nalgebra")] mod nalgebra; | ||||
| #[cfg(feature = "impl-nalgebra")] | ||||
| mod nalgebra; | ||||
| pub mod spline; | ||||
|  | ||||
| pub use crate::interpolate::Interpolate; | ||||
|   | ||||
| @@ -4,13 +4,19 @@ use num_traits as nt; | ||||
| use std::ops::Mul; | ||||
|  | ||||
| use crate::interpolate::{ | ||||
|   Interpolate, Linear, Additive, One, cubic_bezier_def, cubic_hermite_def, quadratic_bezier_def | ||||
|     cubic_bezier_def, cubic_hermite_def, quadratic_bezier_def, Additive, Interpolate, Linear, One, | ||||
| }; | ||||
|  | ||||
| macro_rules! impl_interpolate_vector { | ||||
|   ($($t:tt)*) => { | ||||
|     // implement Linear | ||||
|     impl<T> Linear<T> for $($t)*<T> where T: Scalar + ClosedAdd + ClosedSub + ClosedMul + ClosedDiv { | ||||
|     impl<T> Linear<T> for $($t)*<T> | ||||
|     where T: Scalar + | ||||
|              Copy + | ||||
|              ClosedAdd + | ||||
|              ClosedSub + | ||||
|              ClosedMul + | ||||
|              ClosedDiv { | ||||
|       #[inline(always)] | ||||
|       fn outer_mul(self, t: T) -> Self { | ||||
|         self * t | ||||
|   | ||||
							
								
								
									
										541
									
								
								src/spline.rs
									
									
									
									
									
								
							
							
						
						
									
										541
									
								
								src/spline.rs
									
									
									
									
									
								
							| @@ -1,11 +1,17 @@ | ||||
| //! Spline curves and operations. | ||||
|  | ||||
| #[cfg(feature = "serialization")] use serde_derive::{Deserialize, Serialize}; | ||||
| #[cfg(not(feature = "std"))] use alloc::vec::Vec; | ||||
| #[cfg(feature = "std")] use std::cmp::Ordering; | ||||
| #[cfg(feature = "std")] use std::ops::{Div, Mul}; | ||||
| #[cfg(not(feature = "std"))] use core::ops::{Div, Mul}; | ||||
| #[cfg(not(feature = "std"))] use core::cmp::Ordering; | ||||
| #[cfg(not(feature = "std"))] | ||||
| use alloc::vec::Vec; | ||||
| #[cfg(not(feature = "std"))] | ||||
| use core::cmp::Ordering; | ||||
| #[cfg(not(feature = "std"))] | ||||
| use core::ops::{Div, Mul}; | ||||
| #[cfg(feature = "serialization")] | ||||
| use serde_derive::{Deserialize, Serialize}; | ||||
| #[cfg(feature = "std")] | ||||
| use std::cmp::Ordering; | ||||
| #[cfg(feature = "std")] | ||||
| use std::ops::{Div, Mul}; | ||||
|  | ||||
| use crate::interpolate::{Additive, Interpolate, One, Trigo}; | ||||
| use crate::interpolation::Interpolation; | ||||
| @@ -28,237 +34,268 @@ use crate::key::Key; | ||||
| pub struct Spline<T, V>(pub(crate) Vec<Key<T, V>>); | ||||
|  | ||||
| impl<T, V> Spline<T, V> { | ||||
|   /// Internal sort to ensure invariant of sorting keys is valid. | ||||
|   fn internal_sort(&mut self) where T: PartialOrd { | ||||
|     self.0.sort_by(|k0, k1| k0.t.partial_cmp(&k1.t).unwrap_or(Ordering::Less)); | ||||
|   } | ||||
|     /// Internal sort to ensure invariant of sorting keys is valid. | ||||
|     fn internal_sort(&mut self) | ||||
|     where | ||||
|         T: PartialOrd, | ||||
|     { | ||||
|         self.0 | ||||
|             .sort_by(|k0, k1| k0.t.partial_cmp(&k1.t).unwrap_or(Ordering::Less)); | ||||
|     } | ||||
|  | ||||
|   /// 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(keys: Vec<Key<T, V>>) -> Self where T: PartialOrd { | ||||
|     let mut spline = Spline(keys); | ||||
|     spline.internal_sort(); | ||||
|     spline | ||||
|   } | ||||
|     /// 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(keys: Vec<Key<T, V>>) -> Self | ||||
|     where | ||||
|         T: PartialOrd, | ||||
|     { | ||||
|         let mut spline = Spline(keys); | ||||
|         spline.internal_sort(); | ||||
|         spline | ||||
|     } | ||||
|  | ||||
|   /// 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`]. | ||||
|   pub fn from_iter<I>(iter: I) -> Self where I: Iterator<Item = Key<T, V>>, T: PartialOrd { | ||||
|     Self::from_vec(iter.collect()) | ||||
|   } | ||||
|     /// 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`]. | ||||
|     pub fn from_iter<I>(iter: I) -> Self | ||||
|     where | ||||
|         I: Iterator<Item = Key<T, V>>, | ||||
|         T: PartialOrd, | ||||
|     { | ||||
|         Self::from_vec(iter.collect()) | ||||
|     } | ||||
|  | ||||
|   /// Retrieve the keys of a spline. | ||||
|   pub fn keys(&self) -> &[Key<T, V>] { | ||||
|     &self.0 | ||||
|   } | ||||
|     /// Retrieve the keys of a spline. | ||||
|     pub fn keys(&self) -> &[Key<T, V>] { | ||||
|         &self.0 | ||||
|     } | ||||
|  | ||||
|   /// Number of keys. | ||||
|   #[inline(always)] | ||||
|   pub fn len(&self) -> usize { | ||||
|     self.0.len() | ||||
|   } | ||||
|     /// Number of keys. | ||||
|     #[inline(always)] | ||||
|     pub fn len(&self) -> usize { | ||||
|         self.0.len() | ||||
|     } | ||||
|  | ||||
|   /// Check whether the spline has no key. | ||||
|   #[inline(always)] | ||||
|   pub fn is_empty(&self) -> bool { | ||||
|     self.0.is_empty() | ||||
|   } | ||||
|     /// Check whether the spline has no key. | ||||
|     #[inline(always)] | ||||
|     pub fn is_empty(&self) -> bool { | ||||
|         self.0.is_empty() | ||||
|     } | ||||
|  | ||||
|   /// 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 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 | ||||
|   /// happen if you try to sample between two keys with a specific interpolation mode that makes the | ||||
|   /// sampling impossible. For instance, [`Interpolation::CatmullRom`] requires *four* keys. If | ||||
|   /// you’re near the beginning of the spline or its end, ensure you have enough keys around to make | ||||
|   /// the sampling. | ||||
|   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: Additive + Interpolate<T> { | ||||
|     let keys = &self.0; | ||||
|     let i = search_lower_cp(keys, t)?; | ||||
|     let cp0 = &keys[i]; | ||||
|     /// 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 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 | ||||
|     /// happen if you try to sample between two keys with a specific interpolation mode that makes the | ||||
|     /// sampling impossible. For instance, [`Interpolation::CatmullRom`] requires *four* keys. If | ||||
|     /// you’re near the beginning of the spline or its end, ensure you have enough keys around to make | ||||
|     /// the sampling. | ||||
|     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: Additive + Interpolate<T>, | ||||
|     { | ||||
|         let keys = &self.0; | ||||
|         let i = search_lower_cp(keys, t)?; | ||||
|         let cp0 = &keys[i]; | ||||
|  | ||||
|     match cp0.interpolation { | ||||
|       Interpolation::Step(threshold) => { | ||||
|         let cp1 = &keys[i + 1]; | ||||
|         let nt = normalize_time(t, cp0, cp1); | ||||
|         let value = if nt < threshold { cp0.value } else { cp1.value }; | ||||
|         match cp0.interpolation { | ||||
|             Interpolation::Step(threshold) => { | ||||
|                 let cp1 = &keys[i + 1]; | ||||
|                 let nt = normalize_time(t, cp0, cp1); | ||||
|                 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((value, cp0, Some(cp1))) | ||||
|       } | ||||
|  | ||||
|       Interpolation::Cosine => { | ||||
|         let two_t = T::one() + T::one(); | ||||
|         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((value, cp0, Some(cp1))) | ||||
|       } | ||||
|  | ||||
|       Interpolation::CatmullRom => { | ||||
|         // We need at least four points for Catmull Rom; ensure we have them, otherwise, return | ||||
|         // None. | ||||
|         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 = 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((value, cp0, Some(cp1))) | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       Interpolation::Bezier(u) | Interpolation::StrokeBezier(_, u) => { | ||||
|         // We need to check the next control point to see whether we want quadratic or cubic Bezier. | ||||
|         let cp1 = &keys[i + 1]; | ||||
|         let nt = normalize_time(t, cp0, cp1); | ||||
|  | ||||
|         let value = | ||||
|           match cp1.interpolation { | ||||
|             Interpolation::Bezier(v) => { | ||||
|               Interpolate::cubic_bezier(cp0.value, u, cp1.value + cp1.value - v, cp1.value, nt) | ||||
|                 Some((value, cp0, Some(cp1))) | ||||
|             } | ||||
|  | ||||
|             Interpolation::StrokeBezier(v, _) => { | ||||
|               Interpolate::cubic_bezier(cp0.value, u, v, cp1.value, nt) | ||||
|             Interpolation::Linear => { | ||||
|                 let cp1 = &keys[i + 1]; | ||||
|                 let nt = normalize_time(t, cp0, cp1); | ||||
|                 let value = Interpolate::lerp(cp0.value, cp1.value, nt); | ||||
|  | ||||
|                 Some((value, cp0, Some(cp1))) | ||||
|             } | ||||
|  | ||||
|             _ => Interpolate::quadratic_bezier(cp0.value, u, cp1.value, nt) | ||||
|           }; | ||||
|             Interpolation::Cosine => { | ||||
|                 let two_t = T::one() + T::one(); | ||||
|                 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((value, cp0, Some(cp1))) | ||||
|       } | ||||
|                 Some((value, cp0, Some(cp1))) | ||||
|             } | ||||
|  | ||||
|       Interpolation::__NonExhaustive => unreachable!(), | ||||
|     } | ||||
|   } | ||||
|             Interpolation::CatmullRom => { | ||||
|                 // We need at least four points for Catmull Rom; ensure we have them, otherwise, return | ||||
|                 // None. | ||||
|                 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 = 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, | ||||
|                     ); | ||||
|  | ||||
|   /// 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: Additive + Interpolate<T> { | ||||
|     self.sample_with_key(t).map(|(v, _, _)| v) | ||||
|   } | ||||
|                     Some((value, cp0, Some(cp1))) | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|   /// Sample a spline at a given time with clamping, returning the interpolated value along with its | ||||
|   /// associated key. | ||||
|   /// | ||||
|   /// # Return | ||||
|   /// | ||||
|   /// If you sample before the first key or after the last one, return the first key or the last | ||||
|   /// one, respectively. Otherwise, behave the same way as [`Spline::sample`]. | ||||
|   /// | ||||
|   /// # Error | ||||
|   /// | ||||
|   /// This function returns [`None`] if you have no key. | ||||
|   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: Additive + Interpolate<T> { | ||||
|     if self.0.is_empty() { | ||||
|       return None; | ||||
|     } | ||||
|             Interpolation::Bezier(u) | Interpolation::StrokeBezier(_, u) => { | ||||
|                 // We need to check the next control point to see whether we want quadratic or cubic Bezier. | ||||
|                 let cp1 = &keys[i + 1]; | ||||
|                 let nt = normalize_time(t, cp0, cp1); | ||||
|  | ||||
|     self.sample_with_key(t).or_else(move || { | ||||
|       let first = self.0.first().unwrap(); | ||||
|       if t <= first.t { | ||||
|         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(); | ||||
|                 let value = match cp1.interpolation { | ||||
|                     Interpolation::Bezier(v) => Interpolate::cubic_bezier( | ||||
|                         cp0.value, | ||||
|                         u, | ||||
|                         cp1.value + cp1.value - v, | ||||
|                         cp1.value, | ||||
|                         nt, | ||||
|                     ), | ||||
|  | ||||
|         if t >= last.t { | ||||
|           Some((last.value, &last, None)) | ||||
|         } else { | ||||
|           None | ||||
|                     Interpolation::StrokeBezier(v, _) => { | ||||
|                         Interpolate::cubic_bezier(cp0.value, u, v, cp1.value, nt) | ||||
|                     } | ||||
|  | ||||
|                     _ => 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. | ||||
|   pub fn clamped_sample(&self, t: T) -> Option<V> | ||||
|   where T: Additive + One + Trigo + Mul<T, Output = T> + Div<T, Output = T> + PartialOrd, | ||||
|         V: Additive + 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); | ||||
|     self.internal_sort(); | ||||
|   } | ||||
|  | ||||
|   /// Remove a key from the spline. | ||||
|   pub fn remove(&mut self, index: usize) -> Option<Key<T, V>> { | ||||
|     if index >= self.0.len() { | ||||
|       None | ||||
|     } else { | ||||
|       Some(self.0.remove(index)) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /// Update a key and return the key already present. | ||||
|   /// | ||||
|   /// The key is updated — if present — with the provided function. | ||||
|   /// | ||||
|   /// # Notes | ||||
|   /// | ||||
|   /// That function makes sense only if you want to change the interpolator (i.e. [`Key::t`]) of | ||||
|   /// your key. If you just want to change the interpolation mode or the carried value, consider | ||||
|   /// using the [`Spline::get_mut`] method instead as it will be way faster. | ||||
|   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) | ||||
|   } | ||||
|     /// 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: Additive + Interpolate<T>, | ||||
|     { | ||||
|         self.sample_with_key(t).map(|(v, _, _)| v) | ||||
|     } | ||||
|  | ||||
|   /// Get a key at a given index. | ||||
|   pub fn get(&self, index: usize) -> Option<&Key<T, V>> { | ||||
|     self.0.get(index) | ||||
|   } | ||||
|     /// Sample a spline at a given time with clamping, returning the interpolated value along with its | ||||
|     /// associated key. | ||||
|     /// | ||||
|     /// # Return | ||||
|     /// | ||||
|     /// If you sample before the first key or after the last one, return the first key or the last | ||||
|     /// one, respectively. Otherwise, behave the same way as [`Spline::sample`]. | ||||
|     /// | ||||
|     /// # Error | ||||
|     /// | ||||
|     /// This function returns [`None`] if you have no key. | ||||
|     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: Additive + Interpolate<T>, | ||||
|     { | ||||
|         if self.0.is_empty() { | ||||
|             return None; | ||||
|         } | ||||
|  | ||||
|   /// Mutably get a key at a given 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 | ||||
|     }) | ||||
|   } | ||||
|         self.sample_with_key(t).or_else(move || { | ||||
|             let first = self.0.first().unwrap(); | ||||
|             if t <= first.t { | ||||
|                 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, &last, None)) | ||||
|                 } else { | ||||
|                     None | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     /// 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: Additive + 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); | ||||
|         self.internal_sort(); | ||||
|     } | ||||
|  | ||||
|     /// Remove a key from the spline. | ||||
|     pub fn remove(&mut self, index: usize) -> Option<Key<T, V>> { | ||||
|         if index >= self.0.len() { | ||||
|             None | ||||
|         } else { | ||||
|             Some(self.0.remove(index)) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Update a key and return the key already present. | ||||
|     /// | ||||
|     /// The key is updated — if present — with the provided function. | ||||
|     /// | ||||
|     /// # Notes | ||||
|     /// | ||||
|     /// That function makes sense only if you want to change the interpolator (i.e. [`Key::t`]) of | ||||
|     /// your key. If you just want to change the interpolation mode or the carried value, consider | ||||
|     /// using the [`Spline::get_mut`] method instead as it will be way faster. | ||||
|     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) | ||||
|     } | ||||
|  | ||||
|     /// Get a key at a given index. | ||||
|     pub fn get(&self, index: usize) -> Option<&Key<T, V>> { | ||||
|         self.0.get(index) | ||||
|     } | ||||
|  | ||||
|     /// Mutably get a key at a given 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, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A mutable [`Key`]. | ||||
| @@ -267,52 +304,54 @@ impl<T, V> Spline<T, V> { | ||||
| /// interpolator value as it would invalidate the internal structure of the [`Spline`]. If you | ||||
| /// want to achieve this, you’re advised to use [`Spline::replace`]. | ||||
| pub struct KeyMut<'a, T, V> { | ||||
|   /// Carried value. | ||||
|   pub value: &'a mut V, | ||||
|   /// Interpolation mode to use for that key. | ||||
|   pub interpolation: &'a mut Interpolation<T, V>, | ||||
|     /// Carried value. | ||||
|     pub value: &'a mut V, | ||||
|     /// Interpolation mode to use for that key. | ||||
|     pub interpolation: &'a mut Interpolation<T, V>, | ||||
| } | ||||
|  | ||||
| // Normalize a time ([0;1]) given two control points. | ||||
| #[inline(always)] | ||||
| pub(crate) fn normalize_time<T, V>( | ||||
|   t: T, | ||||
|   cp: &Key<T, V>, | ||||
|   cp1: &Key<T, V> | ||||
| ) -> T where T: Additive + Div<T, Output = T> + PartialEq { | ||||
|   assert!(cp1.t != cp.t, "overlapping keys"); | ||||
|   (t - cp.t) / (cp1.t - cp.t) | ||||
| pub(crate) fn normalize_time<T, V>(t: T, cp: &Key<T, V>, cp1: &Key<T, V>) -> T | ||||
| where | ||||
|     T: Additive + Div<T, Output = T> + PartialEq, | ||||
| { | ||||
|     assert!(cp1.t != cp.t, "overlapping keys"); | ||||
|     (t - cp.t) / (cp1.t - cp.t) | ||||
| } | ||||
|  | ||||
| // Find the lower control point corresponding to a given time. | ||||
| fn search_lower_cp<T, V>(cps: &[Key<T, V>], t: T) -> Option<usize> where T: PartialOrd { | ||||
|   let mut i = 0; | ||||
|   let len = cps.len(); | ||||
| fn search_lower_cp<T, V>(cps: &[Key<T, V>], t: T) -> Option<usize> | ||||
| where | ||||
|     T: PartialOrd, | ||||
| { | ||||
|     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 { | ||||
|     if len < 2 { | ||||
|         return None; | ||||
|       } | ||||
|  | ||||
|       i += 1; | ||||
|     } else if t < cp.t { | ||||
|       if i == 0 { | ||||
|         return None; | ||||
|       } | ||||
|  | ||||
|       i -= 1; | ||||
|     } else { | ||||
|       break; // found | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Some(i) | ||||
|     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) | ||||
| } | ||||
|   | ||||
							
								
								
									
										335
									
								
								tests/mod.rs
									
									
									
									
									
								
							
							
						
						
									
										335
									
								
								tests/mod.rs
									
									
									
									
									
								
							| @@ -1,244 +1,255 @@ | ||||
| use float_cmp::approx_eq; | ||||
| use splines::{Interpolation, Key, Spline}; | ||||
|  | ||||
| #[cfg(feature = "cgmath")] use cgmath as cg; | ||||
| #[cfg(feature = "nalgebra")] use nalgebra as na; | ||||
| #[cfg(feature = "cgmath")] | ||||
| use cgmath as cg; | ||||
| #[cfg(feature = "nalgebra")] | ||||
| use nalgebra as na; | ||||
|  | ||||
| #[test] | ||||
| fn step_interpolation_f32() { | ||||
|   let start = Key::new(0., 0., Interpolation::Step(0.)); | ||||
|   let end = Key::new(1., 10., Interpolation::default()); | ||||
|   let spline = Spline::<f32, _>::from_vec(vec![start, end]); | ||||
|     let start = Key::new(0., 0., Interpolation::Step(0.)); | ||||
|     let end = Key::new(1., 10., Interpolation::default()); | ||||
|     let spline = Spline::<f32, _>::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.), Some(10.)); | ||||
|   assert_eq!(spline.sample_with_key(0.2), Some((10., &start, Some(&end)))); | ||||
|   assert_eq!(spline.clamped_sample_with_key(1.), Some((10., &end, None))); | ||||
|     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.), Some(10.)); | ||||
|     assert_eq!(spline.sample_with_key(0.2), Some((10., &start, Some(&end)))); | ||||
|     assert_eq!(spline.clamped_sample_with_key(1.), Some((10., &end, None))); | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn step_interpolation_f64() { | ||||
|   let start = Key::new(0., 0., Interpolation::Step(0.)); | ||||
|   let end = Key::new(1., 10., Interpolation::default()); | ||||
|   let spline = Spline::<f64, _>::from_vec(vec![start, end]); | ||||
|     let start = Key::new(0., 0., Interpolation::Step(0.)); | ||||
|     let end = Key::new(1., 10., Interpolation::default()); | ||||
|     let spline = Spline::<f64, _>::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.), Some(10.)); | ||||
|   assert_eq!(spline.sample_with_key(0.2), Some((10., &start, Some(&end)))); | ||||
|   assert_eq!(spline.clamped_sample_with_key(1.), Some((10., &end, None))); | ||||
|     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.), Some(10.)); | ||||
|     assert_eq!(spline.sample_with_key(0.2), Some((10., &start, Some(&end)))); | ||||
|     assert_eq!(spline.clamped_sample_with_key(1.), Some((10., &end, None))); | ||||
| } | ||||
|  | ||||
| #[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]); | ||||
|     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.), Some(10.)); | ||||
|     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.), Some(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]); | ||||
|     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.), Some(10.)); | ||||
|     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.), Some(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]); | ||||
|     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.), Some(10.)); | ||||
|     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.), Some(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]); | ||||
|     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.), Some(10.)); | ||||
|     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.), Some(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]); | ||||
|     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.), Some(4.)); | ||||
|     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.), Some(4.)); | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn several_interpolations_several_keys() { | ||||
|   let start = Key::new(0., 0., Interpolation::Step(0.5)); | ||||
|   let k1 = Key::new(1., 5., Interpolation::Linear); | ||||
|   let k2 = Key::new(2., 0., Interpolation::Step(0.1)); | ||||
|   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]); | ||||
|     let start = Key::new(0., 0., Interpolation::Step(0.5)); | ||||
|     let k1 = Key::new(1., 5., Interpolation::Linear); | ||||
|     let k2 = Key::new(2., 0., Interpolation::Step(0.1)); | ||||
|     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.)); | ||||
|   assert_eq!(spline.sample(0.2), Some(0.)); | ||||
|   assert_eq!(spline.sample(0.5), Some(5.)); | ||||
|   assert_eq!(spline.sample(0.9), Some(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.05), Some(0.)); | ||||
|   assert_eq!(spline.sample(2.099), Some(0.)); | ||||
|   assert_eq!(spline.sample(2.75), Some(1.)); | ||||
|   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.), Some(4.)); | ||||
|     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(5.)); | ||||
|     assert_eq!(spline.sample(0.9), Some(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.05), Some(0.)); | ||||
|     assert_eq!(spline.sample(2.099), Some(0.)); | ||||
|     assert_eq!(spline.sample(2.75), Some(1.)); | ||||
|     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.), Some(4.)); | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "cgmath")] | ||||
| #[test] | ||||
| fn stroke_bezier_straight() { | ||||
|   let keys = vec![ | ||||
|     Key::new(0.0, cg::Vector2::new(0., 1.), Interpolation::StrokeBezier(cg::Vector2::new(0., 1.), cg::Vector2::new(0., 1.))), | ||||
|     Key::new(5.0, cg::Vector2::new(5., 1.), Interpolation::StrokeBezier(cg::Vector2::new(5., 1.), cg::Vector2::new(5., 1.))) | ||||
|   ]; | ||||
|   let spline = Spline::from_vec(keys); | ||||
|     use float_cmp::approx_eq; | ||||
|  | ||||
|   assert!(approx_eq!(f32, spline.clamped_sample(0.0).unwrap().y, 1.)); | ||||
|   assert!(approx_eq!(f32, spline.clamped_sample(1.0).unwrap().y, 1.)); | ||||
|   assert!(approx_eq!(f32, spline.clamped_sample(2.0).unwrap().y, 1.)); | ||||
|   assert!(approx_eq!(f32, spline.clamped_sample(3.0).unwrap().y, 1.)); | ||||
|   assert!(approx_eq!(f32, spline.clamped_sample(4.0).unwrap().y, 1.)); | ||||
|   assert!(approx_eq!(f32, spline.clamped_sample(5.0).unwrap().y, 1.)); | ||||
|     let keys = vec![ | ||||
|         Key::new( | ||||
|             0.0, | ||||
|             cg::Vector2::new(0., 1.), | ||||
|             Interpolation::StrokeBezier(cg::Vector2::new(0., 1.), cg::Vector2::new(0., 1.)), | ||||
|         ), | ||||
|         Key::new( | ||||
|             5.0, | ||||
|             cg::Vector2::new(5., 1.), | ||||
|             Interpolation::StrokeBezier(cg::Vector2::new(5., 1.), cg::Vector2::new(5., 1.)), | ||||
|         ), | ||||
|     ]; | ||||
|     let spline = Spline::from_vec(keys); | ||||
|  | ||||
|     assert!(approx_eq!(f32, spline.clamped_sample(0.0).unwrap().y, 1.)); | ||||
|     assert!(approx_eq!(f32, spline.clamped_sample(1.0).unwrap().y, 1.)); | ||||
|     assert!(approx_eq!(f32, spline.clamped_sample(2.0).unwrap().y, 1.)); | ||||
|     assert!(approx_eq!(f32, spline.clamped_sample(3.0).unwrap().y, 1.)); | ||||
|     assert!(approx_eq!(f32, spline.clamped_sample(4.0).unwrap().y, 1.)); | ||||
|     assert!(approx_eq!(f32, spline.clamped_sample(5.0).unwrap().y, 1.)); | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "cgmath")] | ||||
| #[test] | ||||
| fn cgmath_vector_interpolation() { | ||||
|   use splines::Interpolate; | ||||
|     use splines::Interpolate; | ||||
|  | ||||
|   let start = cg::Vector2::new(0.0, 0.0); | ||||
|   let mid = cg::Vector2::new(0.5, 0.5); | ||||
|   let end = cg::Vector2::new(1.0, 1.0); | ||||
|     let start = cg::Vector2::new(0.0, 0.0); | ||||
|     let mid = cg::Vector2::new(0.5, 0.5); | ||||
|     let end = cg::Vector2::new(1.0, 1.0); | ||||
|  | ||||
|   assert_eq!(Interpolate::lerp(start, end, 0.0), start); | ||||
|   assert_eq!(Interpolate::lerp(start, end, 1.0), end); | ||||
|   assert_eq!(Interpolate::lerp(start, end, 0.5), mid); | ||||
|     assert_eq!(Interpolate::lerp(start, end, 0.0), start); | ||||
|     assert_eq!(Interpolate::lerp(start, end, 1.0), end); | ||||
|     assert_eq!(Interpolate::lerp(start, end, 0.5), mid); | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "nalgebra")] | ||||
| #[test] | ||||
| fn nalgebra_vector_interpolation() { | ||||
|   use splines::Interpolate; | ||||
|     use splines::Interpolate; | ||||
|  | ||||
|   let start = na::Vector2::new(0.0, 0.0); | ||||
|   let mid = na::Vector2::new(0.5, 0.5); | ||||
|   let end = na::Vector2::new(1.0, 1.0); | ||||
|     let start = na::Vector2::new(0.0, 0.0); | ||||
|     let mid = na::Vector2::new(0.5, 0.5); | ||||
|     let end = na::Vector2::new(1.0, 1.0); | ||||
|  | ||||
|   assert_eq!(Interpolate::lerp(start, end, 0.0), start); | ||||
|   assert_eq!(Interpolate::lerp(start, end, 1.0), end); | ||||
|   assert_eq!(Interpolate::lerp(start, end, 0.5), mid); | ||||
|     assert_eq!(Interpolate::lerp(start, end, 0.0), start); | ||||
|     assert_eq!(Interpolate::lerp(start, end, 1.0), end); | ||||
|     assert_eq!(Interpolate::lerp(start, end, 0.5), mid); | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn add_key_empty() { | ||||
|   let mut spline: Spline<f32, f32> = Spline::from_vec(vec![]); | ||||
|   spline.add(Key::new(0., 0., Interpolation::Linear)); | ||||
|     let mut spline: Spline<f32, f32> = Spline::from_vec(vec![]); | ||||
|     spline.add(Key::new(0., 0., Interpolation::Linear)); | ||||
|  | ||||
|   assert_eq!(spline.keys(), &[Key::new(0., 0., Interpolation::Linear)]); | ||||
|     assert_eq!(spline.keys(), &[Key::new(0., 0., Interpolation::Linear)]); | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn add_key() { | ||||
|   let start = Key::new(0., 0., Interpolation::Step(0.5)); | ||||
|   let k1 = Key::new(1., 5., Interpolation::Linear); | ||||
|   let k2 = Key::new(2., 0., Interpolation::Step(0.1)); | ||||
|   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 new = Key::new(2.4, 40., Interpolation::Linear); | ||||
|   let mut spline = Spline::from_vec(vec![start, k1, k2.clone(), k3, k4, end]); | ||||
|     let start = Key::new(0., 0., Interpolation::Step(0.5)); | ||||
|     let k1 = Key::new(1., 5., Interpolation::Linear); | ||||
|     let k2 = Key::new(2., 0., Interpolation::Step(0.1)); | ||||
|     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 new = Key::new(2.4, 40., Interpolation::Linear); | ||||
|     let mut spline = Spline::from_vec(vec![start, k1, k2.clone(), k3, k4, end]); | ||||
|  | ||||
|   assert_eq!(spline.keys(), &[start, k1, k2, k3, k4, end]); | ||||
|   spline.add(new); | ||||
|   assert_eq!(spline.keys(), &[start, k1, k2, new, k3, k4, end]); | ||||
|     assert_eq!(spline.keys(), &[start, k1, k2, k3, k4, end]); | ||||
|     spline.add(new); | ||||
|     assert_eq!(spline.keys(), &[start, k1, k2, new, k3, k4, end]); | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn remove_element_empty() { | ||||
|   let mut spline: Spline<f32, f32> = Spline::from_vec(vec![]); | ||||
|   let removed = spline.remove(0); | ||||
|     let mut spline: Spline<f32, f32> = Spline::from_vec(vec![]); | ||||
|     let removed = spline.remove(0); | ||||
|  | ||||
|   assert_eq!(removed, None); | ||||
|   assert!(spline.is_empty()); | ||||
|     assert_eq!(removed, None); | ||||
|     assert!(spline.is_empty()); | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn remove_element() { | ||||
|   let start = Key::new(0., 0., Interpolation::Step(0.5)); | ||||
|   let k1 = Key::new(1., 5., Interpolation::Linear); | ||||
|   let k2 = Key::new(2., 0., Interpolation::Step(0.1)); | ||||
|   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 mut spline = Spline::from_vec(vec![start, k1, k2.clone(), k3, k4, end]); | ||||
|   let removed = spline.remove(2); | ||||
|     let start = Key::new(0., 0., Interpolation::Step(0.5)); | ||||
|     let k1 = Key::new(1., 5., Interpolation::Linear); | ||||
|     let k2 = Key::new(2., 0., Interpolation::Step(0.1)); | ||||
|     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 mut spline = Spline::from_vec(vec![start, k1, k2.clone(), k3, k4, end]); | ||||
|     let removed = spline.remove(2); | ||||
|  | ||||
|   assert_eq!(removed, Some(k2)); | ||||
|   assert_eq!(spline.len(), 5); | ||||
|     assert_eq!(removed, Some(k2)); | ||||
|     assert_eq!(spline.len(), 5); | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user