diff --git a/examples/hello-world.rs b/examples/hello-world.rs index 4c5c977..db994ff 100644 --- a/examples/hello-world.rs +++ b/examples/hello-world.rs @@ -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.)); } diff --git a/examples/serialization.rs b/examples/serialization.rs index c8b8fc6..db46cb2 100644 --- a/examples/serialization.rs +++ b/examples/serialization.rs @@ -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::>(value); - println!("{:?}", spline); + let spline = from_value::>(value); + println!("{:?}", spline); } diff --git a/src/cgmath.rs b/src/cgmath.rs index f200026..c67c8f5 100644 --- a/src/cgmath.rs +++ b/src/cgmath.rs @@ -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 Linear for Quaternion where T: BaseFloat { - #[inline(always)] - fn outer_mul(self, t: T) -> Self { - self * t - } +impl Linear for Quaternion +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 Interpolate for Quaternion -where Self: InnerSpace, T: Additive + BaseFloat + One { - #[inline(always)] - fn lerp(a: Self, b: Self, t: T) -> Self { - a.nlerp(b, t) - } +where + Self: InnerSpace, + 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) + } } diff --git a/src/interpolate.rs b/src/interpolate.rs index 8c79f28..f97757c 100644 --- a/src/interpolate.rs +++ b/src/interpolate.rs @@ -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: Sized + Copy + Linear { - /// 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 + - Sub { -} +pub trait Additive: Copy + Add + Sub {} -impl Additive for T -where T: Copy + - Add + - Sub { -} +impl Additive for T where T: Copy + Add + Sub {} /// Set of additive types that support outer multiplication and division, making them linear. pub trait Linear: 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(x: (V, T), a: (V, T), b: (V, T), y: (V, T), t: T) -> V -where V: Linear, - T: Additive + Mul + 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: Additive + Mul + 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(a: V, u: V, b: V, t: T) -> V -where V: Linear, - T: Additive + Mul + 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: Additive + Mul + 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(a: V, u: V, v: V, b: V, t: T) -> V -where V: Linear, - T: Additive + Mul + 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: Additive + Mul + 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); diff --git a/src/interpolation.rs b/src/interpolation.rs index 7d30586..1fc57fa 100644 --- a/src/interpolation.rs +++ b/src/interpolation.rs @@ -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 { - /// 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 Default for Interpolation { - /// [`Interpolation::Linear`] is the default. - fn default() -> Self { - Interpolation::Linear - } + /// [`Interpolation::Linear`] is the default. + fn default() -> Self { + Interpolation::Linear + } } diff --git a/src/iter.rs b/src/iter.rs index a342194..c92cfa8 100644 --- a/src/iter.rs +++ b/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, - i: usize +pub struct Iter<'a, T, V> +where + T: 'a, + V: 'a, +{ + spline: &'a Spline, + i: usize, } impl<'a, T, V> Iterator for Iter<'a, T, V> { - type Item = &'a Key; + type Item = &'a Key; - fn next(&mut self) -> Option { - let r = self.spline.0.get(self.i); + fn next(&mut self) -> Option { + 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 { - type Item = &'a Key; - type IntoIter = Iter<'a, T, V>; + type Item = &'a Key; + 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 } } - } } - diff --git a/src/key.rs b/src/key.rs index 76f09f6..890a931 100644 --- a/src/key.rs +++ b/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 { - /// Interpolation parameter at which the [`Key`] should be reached. - pub t: T, - /// Carried value. - pub value: V, - /// Interpolation mode. - pub interpolation: Interpolation + /// Interpolation parameter at which the [`Key`] should be reached. + pub t: T, + /// Carried value. + pub value: V, + /// Interpolation mode. + pub interpolation: Interpolation, } impl Key { - /// Create a new key. - pub fn new(t: T, value: V, interpolation: Interpolation) -> Self { - Key { t, value, interpolation } - } + /// Create a new key. + pub fn new(t: T, value: V, interpolation: Interpolation) -> Self { + Key { + t, + value, + interpolation, + } + } } diff --git a/src/lib.rs b/src/lib.rs index 065ae71..f2ec257 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/nalgebra.rs b/src/nalgebra.rs index 67c4580..b692ebe 100644 --- a/src/nalgebra.rs +++ b/src/nalgebra.rs @@ -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 Linear for $($t)* where T: Scalar + ClosedAdd + ClosedSub + ClosedMul + ClosedDiv { + impl Linear for $($t)* + where T: Scalar + + Copy + + ClosedAdd + + ClosedSub + + ClosedMul + + ClosedDiv { #[inline(always)] fn outer_mul(self, t: T) -> Self { self * t diff --git a/src/spline.rs b/src/spline.rs index 5bc2674..28a2b92 100644 --- a/src/spline.rs +++ b/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(pub(crate) Vec>); impl Spline { - /// 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>) -> 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>) -> Self + where + T: PartialOrd, + { + let mut spline = Spline(keys); + spline.internal_sort(); + spline + } - /// Create a new spline by consuming an `Iterater>`. They keys don’t have to be - /// sorted. - /// - /// # Note on iterators - /// - /// It’s valid to use any iterator that implements `Iterator>`. However, you should - /// use [`Spline::from_vec`] if you are passing a [`Vec`]. - pub fn from_iter(iter: I) -> Self where I: Iterator>, T: PartialOrd { - Self::from_vec(iter.collect()) - } + /// Create a new spline by consuming an `Iterater>`. They keys don’t have to be + /// sorted. + /// + /// # Note on iterators + /// + /// It’s valid to use any iterator that implements `Iterator>`. However, you should + /// use [`Spline::from_vec`] if you are passing a [`Vec`]. + pub fn from_iter(iter: I) -> Self + where + I: Iterator>, + T: PartialOrd, + { + Self::from_vec(iter.collect()) + } - /// Retrieve the keys of a spline. - pub fn keys(&self) -> &[Key] { - &self.0 - } + /// Retrieve the keys of a spline. + pub fn keys(&self) -> &[Key] { + &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, Option<&Key>)> - where T: Additive + One + Trigo + Mul + Div + PartialOrd, - V: Additive + Interpolate { - 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, Option<&Key>)> + where + T: Additive + One + Trigo + Mul + Div + PartialOrd, + V: Additive + Interpolate, + { + 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 - where T: Additive + One + Trigo + Mul + Div + PartialOrd, - V: Additive + Interpolate { - 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, Option<&Key>)> - where T: Additive + One + Trigo + Mul + Div + PartialOrd, - V: Additive + Interpolate { - 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 - where T: Additive + One + Trigo + Mul + Div + PartialOrd, - V: Additive + Interpolate { - self.clamped_sample_with_key(t).map(|(v, _, _)| v) - } - - /// Add a key into the spline. - pub fn add(&mut self, key: Key) where T: PartialOrd { - self.0.push(key); - self.internal_sort(); - } - - /// Remove a key from the spline. - pub fn remove(&mut self, index: usize) -> Option> { - 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( - &mut self, - index: usize, - f: F - ) -> Option> - where - F: FnOnce(&Key) -> Key, - 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 + where + T: Additive + One + Trigo + Mul + Div + PartialOrd, + V: Additive + Interpolate, + { + self.sample_with_key(t).map(|(v, _, _)| v) + } - /// Get a key at a given index. - pub fn get(&self, index: usize) -> Option<&Key> { - 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, Option<&Key>)> + where + T: Additive + One + Trigo + Mul + Div + PartialOrd, + V: Additive + Interpolate, + { + if self.0.is_empty() { + return None; + } - /// Mutably get a key at a given index. - pub fn get_mut(&mut self, index: usize) -> Option> { - 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 + where + T: Additive + One + Trigo + Mul + Div + PartialOrd, + V: Additive + Interpolate, + { + self.clamped_sample_with_key(t).map(|(v, _, _)| v) + } + + /// Add a key into the spline. + pub fn add(&mut self, key: Key) + where + T: PartialOrd, + { + self.0.push(key); + self.internal_sort(); + } + + /// Remove a key from the spline. + pub fn remove(&mut self, index: usize) -> Option> { + 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(&mut self, index: usize, f: F) -> Option> + where + F: FnOnce(&Key) -> Key, + 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> { + self.0.get(index) + } + + /// Mutably get a key at a given index. + pub fn get_mut(&mut self, index: usize) -> Option> { + self.0.get_mut(index).map(|key| KeyMut { + value: &mut key.value, + interpolation: &mut key.interpolation, + }) + } } /// A mutable [`Key`]. @@ -267,52 +304,54 @@ impl Spline { /// 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, + /// Carried value. + pub value: &'a mut V, + /// Interpolation mode to use for that key. + pub interpolation: &'a mut Interpolation, } // Normalize a time ([0;1]) given two control points. #[inline(always)] -pub(crate) fn normalize_time( - t: T, - cp: &Key, - cp1: &Key -) -> T where T: Additive + Div + PartialEq { - assert!(cp1.t != cp.t, "overlapping keys"); - (t - cp.t) / (cp1.t - cp.t) +pub(crate) fn normalize_time(t: T, cp: &Key, cp1: &Key) -> T +where + T: Additive + Div + 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(cps: &[Key], t: T) -> Option where T: PartialOrd { - let mut i = 0; - let len = cps.len(); +fn search_lower_cp(cps: &[Key], t: T) -> Option +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) } diff --git a/tests/mod.rs b/tests/mod.rs index dddeca0..3aa3827 100644 --- a/tests/mod.rs +++ b/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::::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::::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::::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::::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 = Spline::from_vec(vec![]); - spline.add(Key::new(0., 0., Interpolation::Linear)); + let mut spline: Spline = 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 = Spline::from_vec(vec![]); - let removed = spline.remove(0); + let mut spline: Spline = 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); }