56 Commits
2.0 ... 3.5

Author SHA1 Message Date
bfb1cc14bb Merge pull request #53 from phaazon/release/3.5
Release/3.5
2020-11-23 22:38:09 +01:00
5836f778f4 Prepare 3.5. 2020-11-23 22:29:22 +01:00
7b5c08d9fa Add support for nalgebra-0.23. 2020-11-23 22:29:22 +01:00
fe78bf2eb6 Merge pull request #50 from iwikal/glam
Add impl-glam feature
2020-11-23 22:20:50 +01:00
4b3a06d66e Update glam 2020-11-02 04:04:06 +01:00
c50a9274a5 glam::Quat::lerp normalizes internally 2020-10-25 22:11:25 +01:00
3ffe6106ec Add impl-glam feature 2020-10-25 22:00:13 +01:00
45244628ac Prepare 3.4.2. 2020-10-24 23:44:25 +02:00
69c166e630 Merge pull request #49 from phaazon/dependabot/cargo/master/simba-gte-0.1.2-and-lt-0.4
Update simba requirement from >=0.1.2, <0.3 to >=0.1.2, <0.4
2020-10-24 23:41:07 +02:00
da4c02202a Update simba requirement from >=0.1.2, <0.3 to >=0.1.2, <0.4
Updates the requirements on [simba](https://github.com/dimforge/simba) to permit the latest version.
- [Release notes](https://github.com/dimforge/simba/releases)
- [Changelog](https://github.com/dimforge/simba/blob/master/CHANGELOG)
- [Commits](https://github.com/dimforge/simba/compare/v0.1.2...v0.3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-24 16:24:09 +00:00
05a3862e30 Prepare 3.4.1. 2020-09-05 15:25:16 +02:00
37ca7b1e2d Merge pull request #45 from phaazon/dependabot/cargo/master/simba-0.2.0
Update simba requirement from 0.1.2 to 0.2.0
2020-09-05 15:24:24 +02:00
680c863ce1 Merge pull request #46 from phaazon/dependabot/cargo/master/nalgebra-0.22
Update nalgebra requirement from 0.21 to 0.22
2020-09-05 15:24:18 +02:00
209d4fc7c5 Add support for nalgebra-0.22. 2020-09-05 15:16:33 +02:00
51769e1b12 Add support of simba-0.2. 2020-09-05 15:15:28 +02:00
c32edbd4cb Update nalgebra requirement from 0.21 to 0.22
Updates the requirements on [nalgebra](https://github.com/rustsim/nalgebra) to permit the latest version.
- [Release notes](https://github.com/rustsim/nalgebra/releases)
- [Changelog](https://github.com/dimforge/nalgebra/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/rustsim/nalgebra/compare/v0.21.0...v0.22.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-25 19:28:24 +00:00
a175e86db7 Update simba requirement from 0.1.2 to 0.2.0
Updates the requirements on [simba](https://github.com/dimforge/simba) to permit the latest version.
- [Release notes](https://github.com/dimforge/simba/releases)
- [Changelog](https://github.com/dimforge/simba/blob/master/CHANGELOG)
- [Commits](https://github.com/dimforge/simba/compare/v0.1.2...v0.2.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-25 18:46:32 +00:00
ebfc15d8af Prepare 3.4. 2020-05-21 20:11:36 +02:00
5b92d7b715 Merge pull request #43 from phaazon/dependabot/cargo/master/float-cmp-0.8
Update float-cmp requirement from 0.6 to 0.8
2020-05-21 20:04:34 +02:00
8f7cc9e711 Bump float-cmp upper bound to accept float-cmp-0.8. 2020-05-21 19:59:48 +02:00
9d930d6f16 Update float-cmp requirement from 0.6 to 0.8
Updates the requirements on [float-cmp](https://github.com/mikedilger/float-cmp) to permit the latest version.
- [Release notes](https://github.com/mikedilger/float-cmp/releases)
- [Commits](https://github.com/mikedilger/float-cmp/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-04 22:42:05 +00:00
0afebc3319 Synchronize README. 2020-04-29 03:27:10 +02:00
4a2f349954 Prepare 3.3.0. 2020-04-09 23:51:07 +02:00
85ac489636 Add the dependabot setup. 2020-04-09 22:39:50 +02:00
aea9011296 Merge pull request #41 from alexbool/update-nalgebra-0.21
bump nalgebra
2020-04-09 22:15:50 +02:00
04247d8706 bump nalgebra 2020-04-06 17:17:40 +03:00
0fcdbacaf3 Prepare version 3.2. 2020-03-19 01:36:13 +01:00
89dfb61272 Add rustfmt.toml and reformat. 2020-03-19 01:22:26 +01:00
1bcf1de99e Merge pull request #40 from alexbool/update-nalgebra-0.20
Update nalgebra 0.20 (take 2)
2020-03-19 01:21:59 +01:00
4630f44d6c run rustfmt 2020-03-18 13:59:22 +03:00
efe9272816 minor: unused warning 2020-03-17 13:07:02 +03:00
036d7df3eb fix Copy trait bound with nalgebra 0.20 2020-03-17 13:06:41 +03:00
a33dbf9fde Activate CI for PRs, too. 2020-03-16 16:54:43 +01:00
dfa1e6a745 Update the CI steps. 2020-03-16 16:45:33 +01:00
f04ea0fefa Revert "Merge pull request #39 from alexbool/update-nalgebra-0.20"
This reverts commit 8ceb8d768c, reversing
changes made to d80de42d2f.
2020-03-16 16:43:20 +01:00
8ceb8d768c Merge pull request #39 from alexbool/update-nalgebra-0.20
update nalgebra version
2020-03-16 16:34:12 +01:00
c93109e28b update nalgebra version 2020-03-14 15:28:14 +03:00
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
22 changed files with 584 additions and 157 deletions

12
.dependabot/config.yml Normal file
View File

@ -0,0 +1,12 @@
version: 1
update_configs:
- package_manager: "rust:cargo"
directory: "."
update_schedule: "live"
target_branch: "master"
default_reviewers:
- "phaazon"
default_assignees:
- "phaazon"
default_labels:
- "dependency-update"

View File

@ -1,5 +1,5 @@
name: CI
on: [push]
on: [push, pull_request]
jobs:
build-linux:
@ -13,7 +13,6 @@ jobs:
run: |
cargo test --verbose --all-features
build-windows:
runs-on: windows-latest
steps:
@ -26,21 +25,30 @@ jobs:
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/env
cargo build --verbose --all-features
- name: Test
run: |
. ~/.cargo/env
cargo test --verbose --all-features
check-readme:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Install cargo-sync-readme
run: cargo install --force cargo-sync-readme
- name: Check
run: cargo sync-readme -c
- name: Install dependencies
run: |
cargo install --force cargo-sync-readme
rustup component add rustfmt
- name: cargo sync-readme
run: |
cargo sync-readme -c
- name: rustfmt
run: cargo fmt -- --check

View File

@ -1,6 +1,127 @@
# 2.0.0
# Changelog
> Mon Sep 24th 2019
<!-- vim-markdown-toc GFM -->
* [3.5](#35)
* [3.4.2](#342)
* [3.4.1](#341)
* [3.4](#34)
* [3.3](#33)
* [3.2](#32)
* [3.1](#31)
* [3.0](#30)
* [Major changes](#major-changes)
* [Patch changes](#patch-changes)
* [2.2](#22)
* [2.1.1](#211)
* [2.1](#21)
* [2.0.1](#201)
* [2.0](#20)
* [Major changes](#major-changes-1)
* [Minor changes](#minor-changes)
* [1.0](#10)
* [Major changes](#major-changes-2)
* [Minor changes](#minor-changes-1)
* [Patch changes](#patch-changes-1)
* [0.2.3](#023)
* [0.2.2](#022)
* [0.2.1](#021)
* [0.2](#02)
* [0.1.1](#011)
* [0.1](#01)
<!-- vim-markdown-toc -->
# 3.5
> Nov 23rd, 2020
- Add support for [glam](https://crates.io/crates/glam) via the `"impl-glam"` feature gate.
- Support of `nalgebra-0.23`.
# 3.4.2
> Oct 24th, 2020
- Support of `simba-0.3`.
# 3.4.1
> Sep 5th, 2020
- Support of `simba-0.2`.
- Support of `nalgebra-0.22`.
# 3.4
> Thu May 21st 2020
- Add support for `float-cmp-0.7` and `float-cmp-0.8`. Because this uses a SemVer range, if you
already have a `Cargo.lock`, dont forget to update `splines` with `cargo update --aggressive`.
# 3.3
> Thu Apr 10th 2020
- Add support for `nalgebra-0.21`.
# 3.2
> Thu Mar 19th 2020
- Add support for `nalgebra-0.20`.
- Add support for `float-cmp-0.6`.
# 3.1
> Sat Jan 26th 2020
- Add support for `nalgebra-0.19`.
# 3.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
> 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
> Mon Sep 23rd 2019
## Major changes

View File

@ -1,6 +1,6 @@
[package]
name = "splines"
version = "2.0.0"
version = "3.5.0"
license = "BSD-3-Clause"
authors = ["Dimitri Sabadie <dimitri.sabadie@gmail.com>"]
description = "Spline interpolation made easy"
@ -22,17 +22,30 @@ maintenance = { status = "actively-developed" }
[features]
default = ["std"]
impl-cgmath = ["cgmath"]
impl-nalgebra = ["alga", "nalgebra", "num-traits"]
impl-glam = ["glam"]
impl-nalgebra = ["nalgebra", "num-traits", "simba"]
serialization = ["serde", "serde_derive"]
std = []
[dependencies]
alga = { version = "0.9", optional = true }
cgmath = { version = "0.17", optional = true }
nalgebra = { version = ">=0.14, <0.19", optional = true }
glam = { version = "0.10", optional = true }
nalgebra = { version = ">=0.21, <0.24", optional = true }
num-traits = { version = "0.2", optional = true }
serde = { version = "1", optional = true }
serde_derive = { version = "1", optional = true }
simba = { version = ">=0.1.2, <0.4", optional = true }
[dev-dependencies]
float-cmp = ">=0.6, < 0.9"
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

@ -24,7 +24,7 @@ is picked from its lower control point.
# Quickly create splines
```
```rust
use splines::{Interpolation, Key, Spline};
let start = Key::new(0., 0., Interpolation::Linear);
@ -46,7 +46,7 @@ value.
If you try to sample in out-of-bounds sampling parameter, youll get no value.
```
```rust
assert_eq!(spline.sample(0.), Some(0.));
assert_eq!(spline.clamped_sample(1.), Some(10.));
assert_eq!(spline.sample(1.1), None);
@ -56,7 +56,7 @@ Its possible that you want to get a value even if youre out-of-bounds. Thi
important for simulations / animations. Feel free to use the `Spline::clamped_interpolation` for
that purpose.
```
```rust
assert_eq!(spline.clamped_sample(-0.9), Some(0.)); // clamped to the first key
assert_eq!(spline.clamped_sample(1.1), Some(10.)); // clamped to the last key
```
@ -90,6 +90,9 @@ So heres a list of currently supported features and how to enable them:
- **[cgmath](https://crates.io/crates/cgmath) implementors.**
- Adds some useful implementations of `Interpolate` for some cgmath types.
- Enable with the `"impl-cgmath"` feature.
- **[glam](https://crates.io/crates/glam) implementors.**
- Adds some useful implementations of `Interpolate` for some glam types.
- Enable with the `"impl-glam"` feature.
- **[nalgebra](https://crates.io/crates/nalgebra) implementors.**
- Adds some useful implementations of `Interpolate` for some nalgebra types.
- Enable with the `"impl-nalgebra"` feature.

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

@ -3,7 +3,10 @@ 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 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.));

View File

@ -1,11 +1,12 @@
#[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!{
let value = json! {
[
{
"t": 0,

15
rustfmt.toml Normal file
View File

@ -0,0 +1,15 @@
edition = "2018"
fn_args_layout = "Tall"
force_explicit_abi = true
hard_tabs = false
max_width = 100
merge_derives = true
newline_style = "Unix"
remove_nested_parens = true
reorder_imports = true
reorder_modules = true
tab_spaces = 2
use_field_init_shorthand = true
use_small_heuristics = "Default"
use_try_shorthand = true

View File

@ -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,7 +50,10 @@ impl_interpolate_vec!(Vector2);
impl_interpolate_vec!(Vector3);
impl_interpolate_vec!(Vector4);
impl<T> Linear<T> for Quaternion<T> where T: BaseFloat {
impl<T> Linear<T> for Quaternion<T>
where
T: BaseFloat,
{
#[inline(always)]
fn outer_mul(self, t: T) -> Self {
self * t
@ -63,7 +66,10 @@ impl<T> Linear<T> for Quaternion<T> where T: BaseFloat {
}
impl<T> Interpolate<T> for Quaternion<T>
where Self: InnerSpace<Scalar = T>, T: Additive + BaseFloat + One {
where
Self: InnerSpace<Scalar = T>,
T: Additive + BaseFloat + One,
{
#[inline(always)]
fn lerp(a: Self, b: Self, t: T) -> Self {
a.nlerp(b, t)

88
src/glam.rs Normal file
View File

@ -0,0 +1,88 @@
use glam::{Quat, Vec2, Vec3, Vec3A, Vec4};
use crate::interpolate::{
cubic_bezier_def, cubic_hermite_def, quadratic_bezier_def, Interpolate, Linear,
};
macro_rules! impl_interpolate_vec {
($($t:tt)*) => {
impl Linear<f32> for $($t)* {
#[inline(always)]
fn outer_mul(self, t: f32) -> Self {
self * t
}
#[inline(always)]
fn outer_div(self, t: f32) -> Self {
self / t
}
}
impl Interpolate<f32> for $($t)* {
#[inline(always)]
fn lerp(a: Self, b: Self, t: f32) -> Self {
a.lerp(b, t)
}
#[inline(always)]
fn cubic_hermite(
x: (Self, f32),
a: (Self, f32),
b: (Self, f32),
y: (Self, f32),
t: f32,
) -> Self {
cubic_hermite_def(x, a, b, y, t)
}
#[inline(always)]
fn quadratic_bezier(a: Self, u: Self, b: Self, t: f32) -> Self {
quadratic_bezier_def(a, u, b, t)
}
#[inline(always)]
fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: f32) -> Self {
cubic_bezier_def(a, u, v, b, t)
}
}
}
}
impl_interpolate_vec!(Vec2);
impl_interpolate_vec!(Vec3);
impl_interpolate_vec!(Vec3A);
impl_interpolate_vec!(Vec4);
impl Linear<f32> for Quat {
#[inline(always)]
fn outer_mul(self, t: f32) -> Self {
self * t
}
#[inline(always)]
fn outer_div(self, t: f32) -> Self {
self / t
}
}
impl Interpolate<f32> for Quat {
#[inline(always)]
fn lerp(a: Self, b: Self, t: f32) -> Self {
a.lerp(b, t)
}
#[inline(always)]
fn cubic_hermite(x: (Self, f32), a: (Self, f32), b: (Self, f32), y: (Self, f32), t: f32) -> Self {
cubic_hermite_def(x, a, b, y, t)
}
#[inline(always)]
fn quadratic_bezier(a: Self, u: Self, b: Self, t: f32) -> Self {
quadratic_bezier_def(a, u, b, t)
}
#[inline(always)]
fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: f32) -> Self {
cubic_bezier_def(a, u, v, b, t)
}
}

View File

@ -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.
@ -45,7 +53,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;
@ -68,17 +76,9 @@ pub trait Interpolate<T>: Sized + Copy {
/// Set of types that support additions and subtraction.
///
/// The [`Copy`] trait is also a supertrait as its 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 {
@ -101,7 +101,7 @@ macro_rules! impl_linear_simple {
self / t
}
}
}
};
}
impl_linear_simple!(f32);
@ -119,7 +119,7 @@ macro_rules! impl_linear_cast {
self / t as $q
}
}
}
};
}
impl_linear_cast!(f32, f64);
@ -139,7 +139,7 @@ macro_rules! impl_one_float {
1.
}
}
}
};
}
impl_one_float!(f32);
@ -198,8 +198,10 @@ impl Trigo for f64 {
///
/// `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 {
where
V: Linear<T>,
T: Additive + Mul<T, Output = T> + One,
{
// some stupid generic constants, because Rust doesnt have polymorphic literals…
let one_t = T::one();
let two_t = one_t + one_t; // lolololol
@ -215,15 +217,20 @@ where V: Linear<T>,
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 {
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)
@ -233,14 +240,19 @@ where V: Linear<T>,
///
/// `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 {
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 {
@ -262,7 +274,7 @@ macro_rules! impl_interpolate_simple {
cubic_bezier_def(a, u, v, b, t)
}
}
}
};
}
impl_interpolate_simple!(f32);
@ -275,8 +287,20 @@ macro_rules! impl_interpolate_via {
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 {
@ -287,7 +311,7 @@ macro_rules! impl_interpolate_via {
cubic_bezier_def(a, u, v, b, t as $v)
}
}
}
};
}
impl_interpolate_via!(f32, f64);

View File

@ -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.
///
@ -40,8 +41,20 @@ pub enum Interpolation<T, V> {
/// 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.
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
__NonExhaustive,
}
impl<T, V> Default for Interpolation<T, V> {

View File

@ -11,9 +11,13 @@ 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 {
pub struct Iter<'a, T, V>
where
T: 'a,
V: 'a,
{
spline: &'a Spline<T, V>,
i: usize
i: usize,
}
impl<'a, T, V> Iterator for Iter<'a, T, V> {
@ -35,10 +39,6 @@ impl<'a, T, V> IntoIterator for &'a Spline<T, V> {
type IntoIter = Iter<'a, T, V>;
fn into_iter(self) -> Self::IntoIter {
Iter {
spline: self,
i: 0
}
Iter { spline: self, i: 0 }
}
}

View File

@ -6,7 +6,8 @@
//! Splines constructed with this crate have the property that its 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;
@ -26,12 +27,16 @@ pub struct Key<T, V> {
/// Carried value.
pub value: V,
/// Interpolation mode.
pub interpolation: Interpolation<T, V>
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 }
Key {
t,
value,
interpolation,
}
}
}

View File

@ -91,6 +91,9 @@
//! - **[cgmath](https://crates.io/crates/cgmath) implementors.**
//! - Adds some useful implementations of `Interpolate` for some cgmath types.
//! - Enable with the `"impl-cgmath"` feature.
//! - **[glam](https://crates.io/crates/glam) implementors.**
//! - Adds some useful implementations of `Interpolate` for some glam types.
//! - Enable with the `"impl-glam"` feature.
//! - **[nalgebra](https://crates.io/crates/nalgebra) implementors.**
//! - Adds some useful implementations of `Interpolate` for some nalgebra types.
//! - Enable with the `"impl-nalgebra"` feature.
@ -106,14 +109,19 @@
#![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;
#[cfg(feature = "impl-glam")]
mod glam;
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;

View File

@ -1,16 +1,22 @@
use alga::general::{ClosedAdd, ClosedDiv, ClosedMul, ClosedSub};
use nalgebra::{Scalar, Vector, Vector1, Vector2, Vector3, Vector4, Vector5, Vector6};
use num_traits as nt;
use simba::scalar::{ClosedAdd, ClosedDiv, ClosedMul, ClosedSub};
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

View File

@ -1,13 +1,19 @@
//! 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::{Interpolate, Additive, One, Trigo};
use crate::interpolate::{Additive, Interpolate, One, Trigo};
use crate::interpolation::Interpolation;
use crate::key::Key;
@ -29,13 +35,21 @@ 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));
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 dont have to be sorted even though its recommended
/// to provide ascending sorted ones (for performance purposes).
pub fn from_vec(keys: Vec<Key<T, V>>) -> Self where T: PartialOrd {
pub fn from_vec(keys: Vec<Key<T, V>>) -> Self
where
T: PartialOrd,
{
let mut spline = Spline(keys);
spline.internal_sort();
spline
@ -48,7 +62,11 @@ impl<T, V> Spline<T, V> {
///
/// Its 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 {
pub fn from_iter<I>(iter: I) -> Self
where
I: Iterator<Item = Key<T, V>>,
T: PartialOrd,
{
Self::from_vec(iter.collect())
}
@ -69,7 +87,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 +102,11 @@ 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>
where T: Additive + One + Trigo + Mul<T, Output = T> + Div<T, Output = T> + PartialOrd,
V: Interpolate<T> {
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];
@ -95,14 +115,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 +133,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,33 +148,54 @@ 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)))
}
}
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
///
@ -160,22 +205,29 @@ 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>
where T: Additive + One + Trigo + Mul<T, Output = T> + Div<T, Output = T> + PartialOrd,
V: Interpolate<T> {
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;
}
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
}
@ -183,8 +235,20 @@ 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 {
pub fn add(&mut self, key: Key<T, V>)
where
T: PartialOrd,
{
self.0.push(key);
self.internal_sort();
}
@ -207,14 +271,10 @@ impl<T, V> Spline<T, V> {
/// 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>>
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
T: PartialOrd,
{
let key = self.remove(index)?;
self.add(f(&key));
@ -230,7 +290,7 @@ impl<T, V> Spline<T, V> {
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
interpolation: &mut key.interpolation,
})
}
}
@ -249,17 +309,19 @@ pub struct KeyMut<'a, 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 {
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 {
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();
@ -269,7 +331,7 @@ fn search_lower_cp<T, V>(cps: &[Key<T, V>], t: T) -> Option<usize> where T: Part
loop {
let cp = &cps[i];
let cp1 = &cps[i+1];
let cp1 = &cps[i + 1];
if t >= cp1.t {
if i >= len - 2 {

View File

@ -1,7 +1,9 @@
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 +18,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 +35,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 +151,34 @@ 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() {
use float_cmp::approx_eq;
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 +192,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;