From cae599e0d70f87d871a4518b5544a2d4596457ce Mon Sep 17 00:00:00 2001 From: Dimitri Sabadie Date: Mon, 30 Sep 2019 12:49:36 +0200 Subject: [PATCH] Add Spline::sample_with_key and Spline::clamped_sample_with_key. --- CHANGELOG.md | 9 ++++++++ Cargo.toml | 2 +- src/spline.rs | 58 +++++++++++++++++++++++++++++++++++++-------------- tests/mod.rs | 4 ++++ 4 files changed, 56 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e48e0ed..6ba7647 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# 2.1 + +> Mon Sep 30th 2019 + +- Add `Spline::sample_with_key` and `Spline::clamped_sample_with_key`. Those methods allow one to + perform the regular `Spline::sample` and `Spline::clamped_sample` but also retreive the base + key that was used to perform the interpolation. The key can be inspected to get the base time, + interpolation, etc. The next key is also returned, if present. + # 2.0.1 > Tue Sep 24th 2019 diff --git a/Cargo.toml b/Cargo.toml index cd82274..1a4255d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "splines" -version = "2.0.1" +version = "2.1.0" license = "BSD-3-Clause" authors = ["Dimitri Sabadie "] description = "Spline interpolation made easy" diff --git a/src/spline.rs b/src/spline.rs index 01463e9..819c4da 100644 --- a/src/spline.rs +++ b/src/spline.rs @@ -69,7 +69,8 @@ impl Spline { self.0.is_empty() } - /// Sample a spline at a given time. + /// 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)* @@ -84,7 +85,7 @@ impl Spline { /// you’re near the beginning of the spline or its end, ensure you have enough keys around to make /// the sampling. /// - pub fn sample(&self, t: T) -> Option + pub fn sample_with_key(&self, t: T) -> Option<(V, &Key, Option<&Key>)> where T: Additive + One + Trigo + Mul + Div + PartialOrd, V: Interpolate { let keys = &self.0; @@ -95,14 +96,17 @@ impl Spline { Interpolation::Step(threshold) => { let cp1 = &keys[i + 1]; let nt = normalize_time(t, cp0, cp1); - Some(if nt < threshold { cp0.value } else { cp1.value }) + 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(Interpolate::lerp(cp0.value, cp1.value, nt)) + Some((value, cp0, Some(cp1))) } Interpolation::Cosine => { @@ -110,8 +114,9 @@ impl Spline { 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(Interpolate::lerp(cp0.value, cp1.value, cos_nt)) + Some((value, cp0, Some(cp1))) } Interpolation::CatmullRom => { @@ -124,8 +129,9 @@ impl Spline { 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(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))) } } @@ -134,18 +140,30 @@ impl Spline { let cp1 = &keys[i + 1]; let nt = normalize_time(t, cp0, cp1); - if let Interpolation::Bezier(v) = cp1.interpolation { - Some(Interpolate::cubic_bezier(cp0.value, u, v, cp1.value, nt)) - } else { - Some(Interpolate::quadratic_bezier(cp0.value, u, cp1.value, nt)) - } + let value = + if let Interpolation::Bezier(v) = cp1.interpolation { + Interpolate::cubic_bezier(cp0.value, u, v, cp1.value, nt) + } else { + 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. + /// Sample a spline at a given time. + /// + pub fn sample(&self, t: T) -> Option + where T: Additive + One + Trigo + Mul + Div + PartialOrd, + V: Interpolate { + self.sample_with_key(t).map(|(v, _, _)| v) + } + + /// Sample a spline at a given time with clamping, returning the interpolated value along with its + /// associated key. /// /// # Return /// @@ -155,22 +173,23 @@ impl Spline { /// # Error /// /// This function returns [`None`] if you have no key. - pub fn clamped_sample(&self, t: T) -> Option + pub fn clamped_sample_with_key(&self, t: T) -> Option<(V, &Key, Option<&Key>)> where T: Additive + One + Trigo + Mul + Div + PartialOrd, V: Interpolate { if self.0.is_empty() { return None; } - self.sample(t).or_else(move || { + self.sample_with_key(t).or_else(move || { let first = self.0.first().unwrap(); if t <= first.t { - Some(first.value) + 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) + Some((last.value, &last, None)) } else { None } @@ -178,6 +197,13 @@ impl Spline { }) } + /// 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: 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); diff --git a/tests/mod.rs b/tests/mod.rs index fbbdcff..91007a6 100644 --- a/tests/mod.rs +++ b/tests/mod.rs @@ -16,6 +16,8 @@ fn step_interpolation_f32() { 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] @@ -31,6 +33,8 @@ fn step_interpolation_f64() { 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]