22 Commits
1.1.1 ... 3.1

Author SHA1 Message Date
d80de42d2f Prepare 3.1.0. 2020-01-26 21:19:25 +01:00
2e6a5a0dfb Merge pull request #38 from alexbool/update-nalgebra
update nalgebra
2020-01-26 21:18:30 +01:00
62147d5348 update nalgebra 2020-01-26 22:42:05 +03:00
2dfc11c908 Fix CHANGELOG entry date. 2019-10-22 21:43:50 +02:00
0c23df7bf0 Merge pull request #35 from phaazon/fix/bézier
Fix Bézier interpolation.
2019-10-22 21:09:15 +02:00
3b6ddc5ea6 Update integration tests for stroke Bézier. 2019-10-22 20:59:46 +02:00
824afef513 Fix Bézier interpolation. 2019-10-22 20:23:36 +02:00
f2b356b78d Working on tests. 2019-10-22 18:13:51 +02:00
955050ecee Fix examples. 2019-10-22 13:34:11 +02:00
22e75c6901 Fix Bézier interpolation. 2019-10-22 13:34:10 +02:00
425433cd5b Merge pull request #33 from phaazon/feature/stroke-bezier
Add Interpolation::StrokeBezier.
2019-10-17 17:26:16 +02:00
cc0a9580ab Add Interpolation::StrokeBezier. 2019-10-17 17:23:46 +02:00
05e131baad 2.1.1 2019-10-17 01:49:34 +02:00
0a15fb48a3 Add missing LICENSE file. 2019-10-17 01:45:53 +02:00
ebc6e16aef Merge pull request #32 from phaazon/feature/sample-key
Add Spline::sample_with_key and Spline::clamped_sample_with_key.
2019-09-30 12:59:11 +02:00
cae599e0d7 Add Spline::sample_with_key and Spline::clamped_sample_with_key. 2019-09-30 12:49:36 +02:00
336c1c7e80 Merge pull request #31 from phaazon/fix/bezier-interpolation
Fix/bezier interpolation
2019-09-24 21:36:04 +02:00
ea29e08836 Fix cubic Bézier interpolation. 2019-09-24 21:31:18 +02:00
3ab98420c8 Remove unneeded comments. 2019-09-24 17:40:01 +02:00
1bfd9a0e7c Merge pull request #29 from phaazon/release/2.0.0
2.0.0.
2019-09-24 10:59:00 +02:00
7846177471 Fix CI. 2019-09-24 10:44:45 +02:00
6f65be125b 2.0.0. 2019-09-24 10:42:03 +02:00
18 changed files with 207 additions and 171 deletions

View File

@ -8,12 +8,11 @@ jobs:
- uses: actions/checkout@v1
- name: Build
run: |
cargo build --verbose
cargo build --verbose --features bezier
cargo build --verbose --all-features
- name: Test
run: |
cargo test --verbose
cargo test --verbose --features bezier
cargo test --verbose --all-features
build-windows:
runs-on: windows-latest
@ -21,25 +20,25 @@ jobs:
- uses: actions/checkout@v1
- name: Build
run: |
cargo build --verbose
cargo build --verbose --features bezier
cargo build --verbose --all-features
- name: Test
run: |
cargo test --verbose
cargo test --verbose --features bezier
cargo test --verbose --all-features
build-macosx:
runs-on: macosx-latest
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1
- name: Rust requirements
run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal
- name: Build
run: |
cargo build --verbose
cargo build --verbose --features bezier
. ~/.cargo/env
cargo build --verbose --all-features
- name: Test
run: |
cargo test --verbose
cargo test --verbose --features bezier
. ~/.cargo/env
cargo test --verbose --all-features
check-readme:
runs-on: ubuntu-latest

View File

@ -1,10 +1,62 @@
# 1.1.1
# 3.1.0
> Mon Sep 22rd 2019
> San Jan 26th 2020
- Add support for `nalgebra-0.19`.
# 3.0.0
> Tue Oct 22th 2019
## Major changes
- Sampling now requires the value of the key to be `Linear<T>` for `Interpolate<T>`. That is needed
to ease some interpolation mode (especially Bézier).
## Patch changes
- Fix Bézier interpolation when the next key is Bézier too.
# 2.2.0
> Mon Oct 17th 2019
- Add `Interpolation::StrokeBezier`.
# 2.1.1
> Mon Oct 17th 2019
- Licensing support in the crate.
# 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
- Fix the cubic Bézier curve interpolation. The “output” tangent is now taken by mirroring the
next keys tangent around its control point.
# 2.0.0
> Mon Sep 23rd 2019
## Major changes
- Add support for [Bézier curves](https://en.wikipedia.org/wiki/B%C3%A9zier_curve).
- Because of Bézier curves, the `Interpolation` type now has one more type variable to know how we
should interpolate with Bézier.
## Minor changes
- Add support for [Bézier curves](https://en.wikipedia.org/wiki/B%C3%A9zier_curve). This is
normally a breaking change so its currently disabled by default and available via the
`"bezier"` feature-gate.
- Add `Spline::get`, `Spline::get_mut` and `Spline::replace`.
# 1.0

View File

@ -1,6 +1,6 @@
[package]
name = "splines"
version = "1.1.1"
version = "3.1.0"
license = "BSD-3-Clause"
authors = ["Dimitri Sabadie <dimitri.sabadie@gmail.com>"]
description = "Spline interpolation made easy"
@ -21,7 +21,6 @@ maintenance = { status = "actively-developed" }
[features]
default = ["std"]
bezier = []
impl-cgmath = ["cgmath"]
impl-nalgebra = ["alga", "nalgebra", "num-traits"]
serialization = ["serde", "serde_derive"]
@ -30,10 +29,21 @@ std = []
[dependencies]
alga = { version = "0.9", optional = true }
cgmath = { version = "0.17", optional = true }
nalgebra = { version = ">=0.14, <0.19", optional = true }
nalgebra = { version = ">=0.14, <0.20", optional = true }
num-traits = { version = "0.2", optional = true }
serde = { version = "1", optional = true }
serde_derive = { version = "1", optional = true }
[dev-dependencies]
float-cmp = "0.5"
serde_json = "1"
[package.metadata.docs.rs]
all-features = true
[[example]]
name = "hello-world"
[[example]]
name = "serialization"
required-features = ["serialization"]

30
LICENSE Normal file
View File

@ -0,0 +1,30 @@
Copyright (c) 2019, Dimitri Sabadie <dimitri.sabadie@gmail.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Dimitri Sabadie <dimitri.sabadie@gmail.com> nor the names of other
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -98,12 +98,6 @@ So heres a list of currently supported features and how to enable them:
- Compiling with the standard library is enabled by default.
- Use `default-features = []` in your `Cargo.toml` to disable.
- Enable explicitly with the `"std"` feature.
- **Extra interpolation modes.**
- In order not to introduce breaking changes, some feature-gates are added to augment the
[`Interpolation`] enum.
- Those feature-gates will disappear on the next major release of the crate.
- The following lists all currently available:
- `"bezier"`: [Bézier curves](https://en.wikipedia.org/wiki/B%C3%A9zier_curve).
[`Interpolation`]: crate::interpolation::Interpolation

View File

@ -1,7 +0,0 @@
[package]
name = "hello-world"
version = "0.2.0"
authors = ["Dimitri Sabadie <dimitri.sabadie@gmail.com>"]
[dependencies]
splines = "1.0.0-rc.2"

View File

@ -1,8 +0,0 @@
[package]
name = "serialization"
version = "0.2.0"
authors = ["Dimitri Sabadie <dimitri.sabadie@gmail.com>"]
[dependencies]
serde_json = "1"
splines = { version = "1.0.0-rc.2", features = ["serialization"] }

View File

@ -1,9 +0,0 @@
[workspace]
members = [
"01-hello-world",
"02-serialization"
]
[patch.crates-io]
splines = { path = ".." }

View File

@ -32,13 +32,11 @@ macro_rules! impl_interpolate_vec {
cubic_hermite_def(x, a, b, y, t)
}
#[cfg(feature = "bezier")]
#[inline(always)]
fn quadratic_bezier(a: Self, u: Self, b: Self, t: T) -> Self {
quadratic_bezier_def(a, u, b, t)
}
#[cfg(feature = "bezier")]
#[inline(always)]
fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: T) -> Self {
cubic_bezier_def(a, u, v, b, t)
@ -76,13 +74,11 @@ where Self: InnerSpace<Scalar = T>, T: Additive + BaseFloat + One {
cubic_hermite_def(x, a, b, y, t)
}
#[cfg(feature = "bezier")]
#[inline(always)]
fn quadratic_bezier(a: Self, u: Self, b: Self, t: T) -> Self {
quadratic_bezier_def(a, u, b, t)
}
#[cfg(feature = "bezier")]
#[inline(always)]
fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: T) -> Self {
cubic_bezier_def(a, u, v, b, t)

View File

@ -45,7 +45,7 @@
/// instance to know which trait your type must implement to be usable.
///
/// [`Spline::sample`]: crate::spline::Spline::sample
pub trait Interpolate<T>: Sized + Copy {
pub trait Interpolate<T>: Sized + Copy + Linear<T> {
/// Linear interpolation.
fn lerp(a: Self, b: Self, t: T) -> Self;
@ -59,11 +59,9 @@ pub trait Interpolate<T>: Sized + Copy {
}
/// Quadratic Bézier interpolation.
#[cfg(feature = "bezier")]
fn quadratic_bezier(a: Self, u: Self, b: Self, t: T) -> Self;
/// Cubic Bézier interpolation.
#[cfg(feature = "bezier")]
fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: T) -> Self;
}
@ -223,7 +221,6 @@ where V: Linear<T>,
/// Default implementation of [`Interpolate::quadratic_bezier`].
///
/// `V` is the value being interpolated. `T` is the sampling value (also sometimes called time).
#[cfg(feature = "bezier")]
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 {
@ -235,7 +232,6 @@ where V: Linear<T>,
/// Default implementation of [`Interpolate::cubic_bezier`].
///
/// `V` is the value being interpolated. `T` is the sampling value (also sometimes called time).
#[cfg(feature = "bezier")]
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 {
@ -258,12 +254,10 @@ macro_rules! impl_interpolate_simple {
cubic_hermite_def(x, a, b, y, t)
}
#[cfg(feature = "bezier")]
fn quadratic_bezier(a: Self, u: Self, b: Self, t: $t) -> Self {
quadratic_bezier_def(a, u, b, t)
}
#[cfg(feature = "bezier")]
fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: $t) -> Self {
cubic_bezier_def(a, u, v, b, t)
}
@ -285,12 +279,10 @@ macro_rules! impl_interpolate_via {
cubic_hermite_def((x, xt as $v), (a, at as $v), (b, bt as $v), (y, yt as $v), t as $v)
}
#[cfg(feature = "bezier")]
fn quadratic_bezier(a: Self, u: Self, b: Self, t: $t) -> Self {
quadratic_bezier_def(a, u, b, t as $v)
}
#[cfg(feature = "bezier")]
fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: $t) -> Self {
cubic_bezier_def(a, u, v, b, t as $v)
}

View File

@ -5,7 +5,6 @@
/// Available kind of interpolations.
///
/// Feel free to visit each variant for more documentation.
#[cfg(feature = "bezier")]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serialization", serde(rename_all = "snake_case"))]
@ -40,48 +39,26 @@ pub enum Interpolation<T, V> {
/// tangent used for the next control point is defined as the segment connecting that control
/// point and the current control points associated point. This is called _quadratic Bézer
/// interpolation_ and it kicks ass too, but a bit less than cubic.
#[cfg(feature = "bezier")]
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 keys [`Interpolation::Bezier`] if present, normally.
///
/// What it means is that instead of setting the output tangent as the next keys 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
}
/// Available kind of interpolations.
///
/// Feel free to visit each variant for more documentation.
#[cfg(not(feature = "bezier"))]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serialization", serde(rename_all = "snake_case"))]
pub enum Interpolation<T> {
/// 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,
}
#[cfg(feature = "bezier")]
impl<T, V> Default for Interpolation<T, V> {
/// [`Interpolation::Linear`] is the default.
fn default() -> Self {
Interpolation::Linear
}
}
#[cfg(not(feature = "bezier"))]
impl<T> Default for Interpolation<T> {
/// [`Interpolation::Linear`] is the default.
fn default() -> Self {
Interpolation::Linear
}
}

View File

@ -17,7 +17,6 @@ use crate::interpolation::Interpolation;
/// key and the next one if existing. Have a look at [`Interpolation`] for further details.
///
/// [`Interpolation`]: crate::interpolation::Interpolation
#[cfg(feature = "bezier")]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serialization", serde(rename_all = "snake_case"))]
@ -30,36 +29,9 @@ pub struct Key<T, V> {
pub interpolation: Interpolation<T, V>
}
/// A spline control point.
///
/// This type associates a value at a given interpolation parameter value. It also contains an
/// interpolation mode used to determine how to interpolate values on the segment defined by this
/// key and the next one if existing. Have a look at [`Interpolation`] for further details.
///
/// [`Interpolation`]: crate::interpolation::Interpolation
#[cfg(not(feature = "bezier"))]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[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>
}
impl<T, V> Key<T, V> {
/// Create a new key.
#[cfg(feature = "bezier")]
pub fn new(t: T, value: V, interpolation: Interpolation<T, V>) -> Self {
Key { t, value, interpolation }
}
/// Create a new key.
#[cfg(not(feature = "bezier"))]
pub fn new(t: T, value: V, interpolation: Interpolation<T>) -> Self {
Key { t, value, interpolation }
}
}

View File

@ -99,12 +99,6 @@
//! - Compiling with the standard library is enabled by default.
//! - Use `default-features = []` in your `Cargo.toml` to disable.
//! - Enable explicitly with the `"std"` feature.
//! - **Extra interpolation modes.**
//! - In order not to introduce breaking changes, some feature-gates are added to augment the
//! [`Interpolation`] enum.
//! - Those feature-gates will disappear on the next major release of the crate.
//! - The following lists all currently available:
//! - `"bezier"`: [Bézier curves](https://en.wikipedia.org/wiki/B%C3%A9zier_curve).
//!
//! [`Interpolation`]: crate::interpolation::Interpolation

View File

@ -43,13 +43,11 @@ macro_rules! impl_interpolate_vector {
cubic_hermite_def(x, a, b, y, t)
}
#[cfg(feature = "bezier")]
#[inline(always)]
fn quadratic_bezier(a: Self, u: Self, b: Self, t: T) -> Self {
quadratic_bezier_def(a, u, b, t)
}
#[cfg(feature = "bezier")]
#[inline(always)]
fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: T) -> Self {
cubic_bezier_def(a, u, v, b, t)

View File

@ -7,7 +7,7 @@
#[cfg(not(feature = "std"))] use core::ops::{Div, Mul};
#[cfg(not(feature = "std"))] use core::cmp::Ordering;
use crate::interpolate::{Interpolate, Additive, One, Trigo};
use crate::interpolate::{Additive, Interpolate, One, Trigo};
use crate::interpolation::Interpolation;
use crate::key::Key;
@ -69,7 +69,8 @@ impl<T, V> Spline<T, V> {
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 samplings processing complexity is currently *O(log n)*. Its possible to achieve *O(1)*
@ -83,10 +84,9 @@ impl<T, V> Spline<T, V> {
/// sampling impossible. For instance, [`Interpolation::CatmullRom`] requires *four* keys. If
/// youre 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<V>
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: Interpolate<T> {
V: Additive + Interpolate<T> {
let keys = &self.0;
let i = search_lower_cp(keys, t)?;
let cp0 = &keys[i];
@ -95,14 +95,17 @@ impl<T, V> Spline<T, V> {
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 +113,9 @@ impl<T, V> Spline<T, V> {
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,32 +128,47 @@ impl<T, V> Spline<T, V> {
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)))
}
}
#[cfg(feature = "bezier")]
Interpolation::Bezier(u) => {
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);
if let Interpolation::Bezier(v) = cp1.interpolation {
Some(Interpolate::cubic_bezier(cp0.value, u, v, cp1.value, nt))
//let one_nt = T::one() - nt;
//let one_nt_2 = one_nt * one_nt;
//let one_nt_3 = one_nt_2 * one_nt;
//let three_one_nt_2 = one_nt_2 + one_nt_2 + one_nt_2; // one_nt_2 * 3
//let r = cp0.value * one_nt_3;
} else {
Some(Interpolate::quadratic_bezier(cp0.value, u, cp1.value, nt))
}
let value =
match cp1.interpolation {
Interpolation::Bezier(v) => {
Interpolate::cubic_bezier(cp0.value, u, cp1.value + cp1.value - v, cp1.value, nt)
}
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.
/// 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)
}
/// Sample a spline at a given time with clamping, returning the interpolated value along with its
/// associated key.
///
/// # Return
///
@ -159,22 +178,23 @@ impl<T, V> Spline<T, V> {
/// # Error
///
/// This function returns [`None`] if you have no key.
pub fn clamped_sample(&self, t: T) -> Option<V>
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: Interpolate<T> {
V: Additive + Interpolate<T> {
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
}
@ -182,6 +202,13 @@ impl<T, V> Spline<T, V> {
})
}
/// 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);
@ -243,10 +270,7 @@ pub struct KeyMut<'a, T, V> {
/// Carried value.
pub value: &'a mut V,
/// Interpolation mode to use for that key.
#[cfg(feature = "bezier")]
pub interpolation: &'a mut Interpolation<T, V>,
#[cfg(not(feature = "bezier"))]
pub interpolation: &'a mut Interpolation<T>,
}
// Normalize a time ([0;1]) given two control points.

View File

@ -1,7 +1,8 @@
use float_cmp::approx_eq;
use splines::{Interpolation, Key, Spline};
#[cfg(feature = "impl-cgmath")] use cgmath as cg;
#[cfg(feature = "impl-nalgebra")] use nalgebra as na;
#[cfg(feature = "cgmath")] use cgmath as cg;
#[cfg(feature = "nalgebra")] use nalgebra as na;
#[test]
fn step_interpolation_f32() {
@ -16,6 +17,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 +34,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]
@ -145,7 +150,24 @@ fn several_interpolations_several_keys() {
assert_eq!(spline.clamped_sample(11.), Some(4.));
}
#[cfg(feature = "impl-cgmath")]
#[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);
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;
@ -159,7 +181,7 @@ fn cgmath_vector_interpolation() {
assert_eq!(Interpolate::lerp(start, end, 0.5), mid);
}
#[cfg(feature = "impl-nalgebra")]
#[cfg(feature = "nalgebra")]
#[test]
fn nalgebra_vector_interpolation() {
use splines::Interpolate;