Compare commits
	
		
			60 Commits
		
	
	
		
			3.5.4
			...
			EmbarkStud
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | da43539b70 | ||
|  | eca09f1baf | ||
|  | e1b78070c6 | ||
|  | cdc48a49a7 | ||
|  | decd85dba2 | ||
|  | df9815a464 | ||
|  | ca8e797932 | ||
|  | 5b746aaf57 | ||
|  | 88a7ee7a8d | ||
|  | 5463fd11d6 | ||
|  | d9770ad60b | ||
|  | 3dfea81856 | ||
|  | 604dcc6e27 | ||
|  | 8c952ae242 | ||
|  | b52643b5d7 | ||
|  | dc6ef0a5cc | ||
|  | 043a8608c3 | ||
|  | 295043e5af | ||
|  | 1c249215c9 | ||
|  | 5a7e74d79c | ||
|  | 2012105a72 | ||
|  | f25ebb2c64 | ||
|  | e9c1de389f | ||
|  | bdeaefd9f9 | ||
|  | ace0f4ec50 | ||
|  | b056a4e9a7 | ||
|  | 042253ab9c | ||
|  | 270f225394 | ||
|  | 138828e798 | ||
|  | 0ca1c5aa48 | ||
|  | f4c8be33b9 | ||
|  | 26bd5c88eb | ||
|  | a4cd49fd20 | ||
|  | 09bc7069b2 | ||
|  | 8dc8606bf0 | ||
|  | fc2f53200f | ||
|  | b3836975c3 | ||
|  | 322d271499 | ||
|  | e64298dc88 | ||
|  | 32e5122339 | ||
|  | 1be94935cf | ||
|  | 06f6e4b578 | ||
|  | 87e27e732d | ||
|  | fb678f9613 | ||
|  | ee4230340b | ||
|  | f585119883 | ||
|  | 25d5c5217e | ||
|  | 355178f5fa | ||
|  | b92c28cfbb | ||
|  | 695caf0cca | ||
|  | 3e85a1f026 | ||
|  | 0ccc3c0956 | ||
|  | 3d43e4c644 | ||
|  | 29833f0ebb | ||
|  | 80fb6fbe28 | ||
|  | cc3ac349b4 | ||
|  | 395dff34ee | ||
|  | 469a785767 | ||
|  | dd7ae34670 | ||
|  | b0a6e3d5e9 | 
| @@ -1,12 +0,0 @@ | ||||
| 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" | ||||
							
								
								
									
										25
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| version: 2 | ||||
| updates: | ||||
| - package-ecosystem: cargo | ||||
|   directory: "/." | ||||
|   schedule: | ||||
|     interval: daily | ||||
|     time: "04:00" | ||||
|   open-pull-requests-limit: 10 | ||||
|   target-branch: master | ||||
|   reviewers: | ||||
|   - phaazon | ||||
|   assignees: | ||||
|   - phaazon | ||||
|   labels: | ||||
|   - dependency-update | ||||
|   ignore: | ||||
|   - dependency-name: glam | ||||
|     versions: | ||||
|     - 0.13.0 | ||||
|   - dependency-name: nalgebra | ||||
|     versions: | ||||
|     - 0.25.0 | ||||
|   - dependency-name: cgmath | ||||
|     versions: | ||||
|     - 0.18.0 | ||||
							
								
								
									
										22
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -6,38 +6,22 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v1 | ||||
|       - name: Build | ||||
|         run: | | ||||
|           cargo build --verbose --all-features | ||||
|       - name: Test | ||||
|         run: | | ||||
|           cargo test --verbose --all-features | ||||
|         run: cargo test --verbose --all-features | ||||
|  | ||||
|   build-windows: | ||||
|     runs-on: windows-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v1 | ||||
|       - name: Build | ||||
|         run: | | ||||
|           cargo build --verbose --all-features | ||||
|       - name: Test | ||||
|         run: | | ||||
|           cargo test --verbose --all-features | ||||
|         run: cargo test --verbose --all-features | ||||
|  | ||||
|   build-macosx: | ||||
|     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 | ||||
|         run: cargo test --verbose --all-features | ||||
|  | ||||
|   quality: | ||||
|     runs-on: ubuntu-latest | ||||
|   | ||||
							
								
								
									
										95
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										95
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,7 +1,14 @@ | ||||
| # Changelog | ||||
|  | ||||
| <!-- vim-markdown-toc GFM --> | ||||
|  | ||||
| * [4.1.2](#412) | ||||
| * [4.1.1](#411) | ||||
| * [4.1](#41) | ||||
| * [4.0.3](#403) | ||||
| * [4.0.2](#402) | ||||
| * [4.0.1](#401) | ||||
| * [4.0](#40) | ||||
|   * [Major changes](#major-changes) | ||||
|   * [Patch changes](#patch-changes) | ||||
| * [3.5.4](#354) | ||||
| * [3.5.3](#353) | ||||
| * [3.5.2](#352) | ||||
| @@ -14,19 +21,19 @@ | ||||
| * [3.2](#32) | ||||
| * [3.1](#31) | ||||
| * [3.0](#30) | ||||
|   * [Major changes](#major-changes) | ||||
|   * [Patch changes](#patch-changes) | ||||
|   * [Major changes](#major-changes-1) | ||||
|   * [Patch changes](#patch-changes-1) | ||||
| * [2.2](#22) | ||||
| * [2.1.1](#211) | ||||
| * [2.1](#21) | ||||
| * [2.0.1](#201) | ||||
| * [2.0](#20) | ||||
|   * [Major changes](#major-changes-1) | ||||
|   * [Major changes](#major-changes-2) | ||||
|   * [Minor changes](#minor-changes) | ||||
| * [1.0](#10) | ||||
|   * [Major changes](#major-changes-2) | ||||
|   * [Major changes](#major-changes-3) | ||||
|   * [Minor changes](#minor-changes-1) | ||||
|   * [Patch changes](#patch-changes-1) | ||||
|   * [Patch changes](#patch-changes-2) | ||||
| * [0.2.3](#023) | ||||
| * [0.2.2](#022) | ||||
| * [0.2.1](#021) | ||||
| @@ -34,7 +41,79 @@ | ||||
| * [0.1.1](#011) | ||||
| * [0.1](#01) | ||||
|  | ||||
| <!-- vim-markdown-toc --> | ||||
| # 4.2.0 | ||||
|  | ||||
| > Feb 1, 2023 | ||||
|  | ||||
| - Add support for `glam-0.22`. | ||||
| - Add support for `nalgebra-0.32`. | ||||
| - Add deprecation lints for `impl-*` feature gates. Those shouldn’t be used anymore and the `*` variant should be  | ||||
|   preferred. For instance, if you used `impl-cgmath`, you should just use the `cgmath` feature gate now. | ||||
|  | ||||
| # 4.1.1 | ||||
|  | ||||
| > Jul 27, 2022 | ||||
|  | ||||
| - Internal enhancement of sampling splines by looking for control points. That brings the lookup from _O(N)_ to | ||||
|   _O(log(N))_. That is super embarassing because it should have been the default from the very first commit. Sorry | ||||
|   about that. | ||||
| - Fix hermite cubic interpolation. | ||||
| - Add support for `glam-0.21`. | ||||
| - Add support for `nalgebra-0.31`. | ||||
|  | ||||
| # 4.1 | ||||
|  | ||||
| > Mar 28, 2022 | ||||
|  | ||||
| - Support for edition 2021. | ||||
| - Bump `float-cmp` dependency. | ||||
| - Bump `glam` dependency. | ||||
| - Bump `nalgebra` dependency. | ||||
| - Simplify the CI. | ||||
|  | ||||
| # 4.0.3 | ||||
|  | ||||
| > Jul 11, 2021 | ||||
|  | ||||
| - Add more implementors for `Interpolate`. | ||||
|  | ||||
| # 4.0.2 | ||||
|  | ||||
| > Jul 11, 2021 | ||||
|  | ||||
| - **Yanked.** | ||||
|  | ||||
| # 4.0.1 | ||||
|  | ||||
| > Jul 11, 2021 | ||||
|  | ||||
| - Add support up to `glam-0.17`. | ||||
| - Add support up to `nalgebra-0.27`. | ||||
| - Replace the name of some feature gates: | ||||
|   - `serialization` becomes `serde`. | ||||
|   - `impl-*` becomes `*`. | ||||
|   - The previous feature gates are kept around to prevent a breaking change but will eventually be removed in the next | ||||
|     major update. | ||||
|  | ||||
| # 4.0 | ||||
|  | ||||
| > Mar 05, 2021 | ||||
|  | ||||
| ## Major changes | ||||
|  | ||||
| - Switch the `Interpolation` enum to `#[non_exhaustive]` to allow adding more interpolation modes (if any) in the | ||||
|   future. | ||||
| - Introduce `SampledWithKey`, which is a more elegant / typed way to access a sample along with its associated key | ||||
|   index. | ||||
| - Refactor the `Interpolate` trait and add the `Interpolator` trait. | ||||
|  | ||||
| ## Patch changes | ||||
|  | ||||
| - Highly simplify the various implementors (`cgmath`, `nalgebra` and `glam`) so that maintenance is easy. | ||||
| - Expose the `impl_Interpolate` macro, allowing to implement the API all at once if a type implements the various | ||||
|   `std::ops:*` traits. Since most of the crates do, this macro makes it really easy to add support for a crate. | ||||
| - Drop `simba` as a direct dependency. | ||||
| - Drop `num-traits` as a direct dependency. | ||||
|  | ||||
| # 3.5.4 | ||||
|  | ||||
|   | ||||
							
								
								
									
										29
									
								
								Cargo.toml
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								Cargo.toml
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| [package] | ||||
| name = "splines" | ||||
| version = "3.5.4" | ||||
| version = "4.2.0" | ||||
| license = "BSD-3-Clause" | ||||
| authors = ["Dimitri Sabadie <dimitri.sabadie@gmail.com>"] | ||||
| description = "Spline interpolation made easy" | ||||
| @@ -11,41 +11,32 @@ repository = "https://github.com/phaazon/splines" | ||||
| documentation = "https://docs.rs/splines" | ||||
| readme = "README.md" | ||||
|  | ||||
| edition = "2018" | ||||
|  | ||||
| [badges] | ||||
| travis-ci = { repository = "phaazon/splines", branch = "master" } | ||||
| is-it-maintained-issue-resolution = { repository = "phaazon/splines" } | ||||
| is-it-maintained-open-issues = { repository = "phaazon/splines" } | ||||
| maintenance = { status = "actively-developed" } | ||||
| edition = "2021" | ||||
|  | ||||
| [features] | ||||
| default = ["std"] | ||||
| impl-cgmath = ["cgmath"] | ||||
| impl-glam = ["glam"] | ||||
| impl-nalgebra = ["nalgebra", "num-traits", "simba"] | ||||
| serialization = ["serde", "serde_derive"] | ||||
| impl-nalgebra = ["nalgebra"] | ||||
| serialization = ["serde"] | ||||
| std = [] | ||||
|  | ||||
| [dependencies] | ||||
| cgmath = { version = ">=0.17, <0.19", optional = true } | ||||
| glam = { version = ">=0.10, <0.12", optional = true } | ||||
| nalgebra = { version = ">=0.21, <0.25", 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.5", optional = true } | ||||
| glam = { version = ">=0.10, <0.25", optional = true } | ||||
| nalgebra = { version = ">=0.21, <0.33", optional = true } | ||||
| serde =  { version = "1", features = ["derive"], optional = true } | ||||
|  | ||||
| [dev-dependencies] | ||||
| float-cmp = ">=0.6, < 0.9" | ||||
| float-cmp = ">=0.6, < 0.10" | ||||
| serde_json = "1" | ||||
|  | ||||
| [package.metadata.docs.rs] | ||||
| all-features = true | ||||
| features = ["std", "cgmath", "glam", "nalgebra", "serde"] | ||||
|  | ||||
| [[example]] | ||||
| name = "hello-world" | ||||
|  | ||||
| [[example]] | ||||
| name = "serialization" | ||||
| required-features = ["serialization"] | ||||
| required-features = ["serde"] | ||||
|   | ||||
							
								
								
									
										10
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								README.md
									
									
									
									
									
								
							| @@ -83,19 +83,19 @@ not. It’s especially important to see how it copes with the documentation. | ||||
|  | ||||
| So here’s a list of currently supported features and how to enable them: | ||||
|  | ||||
|   - **Serialization / deserialization.** | ||||
|   - **Serde.** | ||||
|     - This feature implements both the `Serialize` and `Deserialize` traits from `serde` for all | ||||
|       types exported by this crate. | ||||
|     - Enable with the `"serialization"` feature. | ||||
|     - Enable with the `"serde"` feature. | ||||
|   - **[cgmath](https://crates.io/crates/cgmath) implementors.** | ||||
|     - Adds some useful implementations of `Interpolate` for some cgmath types. | ||||
|     - Enable with the `"impl-cgmath"` feature. | ||||
|     - Enable with the `"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. | ||||
|     - Enable with the `"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. | ||||
|     - Enable with the `"nalgebra"` feature. | ||||
|   - **Standard library / no standard library.** | ||||
|     - It’s possible to compile against the standard library or go on your own without it. | ||||
|     - Compiling with the standard library is enabled by default. | ||||
|   | ||||
							
								
								
									
										101
									
								
								src/cgmath.rs
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								src/cgmath.rs
									
									
									
									
									
								
							| @@ -1,92 +1,15 @@ | ||||
| use cgmath::{ | ||||
|   BaseFloat, BaseNum, InnerSpace, Quaternion, Vector1, Vector2, Vector3, Vector4, VectorSpace, | ||||
| }; | ||||
| use crate::impl_Interpolate; | ||||
|  | ||||
| use crate::interpolate::{ | ||||
|   cubic_bezier_def, cubic_hermite_def, quadratic_bezier_def, Additive, Interpolate, Linear, One, | ||||
| }; | ||||
| use cgmath::{Quaternion, Vector1, Vector2, Vector3, Vector4}; | ||||
|  | ||||
| macro_rules! impl_interpolate_vec { | ||||
|   ($($t:tt)*) => { | ||||
|     impl<T> Linear<T> for $($t)*<T> where T: BaseNum { | ||||
|       #[inline(always)] | ||||
|       fn outer_mul(self, t: T) -> Self { | ||||
|         self * t | ||||
|       } | ||||
| impl_Interpolate!(f32, Vector1<f32>, std::f32::consts::PI); | ||||
| impl_Interpolate!(f32, Vector2<f32>, std::f32::consts::PI); | ||||
| impl_Interpolate!(f32, Vector3<f32>, std::f32::consts::PI); | ||||
| impl_Interpolate!(f32, Vector4<f32>, std::f32::consts::PI); | ||||
| impl_Interpolate!(f32, Quaternion<f32>, std::f32::consts::PI); | ||||
|  | ||||
|       #[inline(always)] | ||||
|       fn outer_div(self, t: T) -> Self { | ||||
|         self / t | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     impl<T> Interpolate<T> for $($t)*<T> | ||||
|     where Self: InnerSpace<Scalar = T>, T: Additive + BaseFloat + One { | ||||
|       #[inline(always)] | ||||
|       fn lerp(a: Self, b: Self, t: T) -> Self { | ||||
|         a.lerp(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 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) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| impl_interpolate_vec!(Vector1); | ||||
| impl_interpolate_vec!(Vector2); | ||||
| impl_interpolate_vec!(Vector3); | ||||
| impl_interpolate_vec!(Vector4); | ||||
|  | ||||
| impl<T> Linear<T> for Quaternion<T> | ||||
| where | ||||
|   T: BaseFloat, | ||||
| { | ||||
|   #[inline(always)] | ||||
|   fn outer_mul(self, t: T) -> Self { | ||||
|     self * t | ||||
|   } | ||||
|  | ||||
|   #[inline(always)] | ||||
|   fn outer_div(self, t: T) -> Self { | ||||
|     self / t | ||||
|   } | ||||
| } | ||||
|  | ||||
| impl<T> Interpolate<T> for Quaternion<T> | ||||
| where | ||||
|   Self: InnerSpace<Scalar = T>, | ||||
|   T: Additive + BaseFloat + One, | ||||
| { | ||||
|   #[inline(always)] | ||||
|   fn lerp(a: Self, b: Self, t: T) -> Self { | ||||
|     a.nlerp(b, t) | ||||
|   } | ||||
|  | ||||
|   #[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 cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: T) -> Self { | ||||
|     cubic_bezier_def(a, u, v, b, t) | ||||
|   } | ||||
| } | ||||
| impl_Interpolate!(f64, Vector1<f64>, std::f64::consts::PI); | ||||
| impl_Interpolate!(f64, Vector2<f64>, std::f64::consts::PI); | ||||
| impl_Interpolate!(f64, Vector3<f64>, std::f64::consts::PI); | ||||
| impl_Interpolate!(f64, Vector4<f64>, std::f64::consts::PI); | ||||
| impl_Interpolate!(f64, Quaternion<f64>, std::f64::consts::PI); | ||||
|   | ||||
							
								
								
									
										92
									
								
								src/glam.rs
									
									
									
									
									
								
							
							
						
						
									
										92
									
								
								src/glam.rs
									
									
									
									
									
								
							| @@ -1,88 +1,8 @@ | ||||
| use crate::impl_Interpolate; | ||||
| 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) | ||||
|   } | ||||
| } | ||||
| impl_Interpolate!(f32, Vec2, std::f32::consts::PI); | ||||
| impl_Interpolate!(f32, Vec3, std::f32::consts::PI); | ||||
| impl_Interpolate!(f32, Vec3A, std::f32::consts::PI); | ||||
| impl_Interpolate!(f32, Vec4, std::f32::consts::PI); | ||||
| impl_Interpolate!(f32, Quat, std::f32::consts::PI); | ||||
|   | ||||
| @@ -42,277 +42,196 @@ use core::ops::{Add, Mul, Sub}; | ||||
| 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. | ||||
| /// Types that can be used as interpolator in splines. | ||||
| /// | ||||
| /// `T` is the variable used to sample with. Typical implementations use [`f32`] or [`f64`], but | ||||
| /// you’re free to use the ones you like. Feel free to have a look at [`Spline::sample`] for | ||||
| /// instance to know which trait your type must implement to be usable. | ||||
| /// An interpolator value is like the fabric on which control keys (and sampled values) live on. | ||||
| pub trait Interpolator: Sized + Copy + PartialOrd { | ||||
|   /// Normalize the interpolator. | ||||
|   fn normalize(self, start: Self, end: Self) -> Self; | ||||
| } | ||||
|  | ||||
| macro_rules! impl_Interpolator { | ||||
|   ($t:ty) => { | ||||
|     impl Interpolator for $t { | ||||
|       fn normalize(self, start: Self, end: Self) -> Self { | ||||
|         (self - start) / (end - start) | ||||
|       } | ||||
|     } | ||||
|   }; | ||||
| } | ||||
|  | ||||
| impl_Interpolator!(f32); | ||||
| impl_Interpolator!(f64); | ||||
|  | ||||
| /// Values that can be interpolated. Implementing this trait is required to perform sampling on splines. | ||||
| /// | ||||
| /// [`Spline::sample`]: crate::spline::Spline::sample | ||||
| pub trait Interpolate<T>: Sized + Copy + Linear<T> { | ||||
| /// `T` is the interpolator used to sample with. Typical implementations use [`f32`] or [`f64`], but | ||||
| /// you’re free to use the ones you like. | ||||
| pub trait Interpolate<T>: Sized + Copy { | ||||
|   /// Step interpolation. | ||||
|   fn step(t: T, threshold: T, a: Self, b: Self) -> Self; | ||||
|  | ||||
|   /// Linear interpolation. | ||||
|   fn lerp(a: Self, b: Self, t: T) -> Self; | ||||
|   fn lerp(t: T, a: Self, b: Self) -> Self; | ||||
|  | ||||
|   /// Cosine interpolation. | ||||
|   fn cosine(t: T, a: Self, b: Self) -> 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) | ||||
|   } | ||||
|   fn cubic_hermite(t: T, x: (T, Self), a: (T, Self), b: (T, Self), y: (T, Self)) -> Self; | ||||
|  | ||||
|   /// Quadratic Bézier interpolation. | ||||
|   fn quadratic_bezier(a: Self, u: Self, b: Self, t: T) -> Self; | ||||
|   /// | ||||
|   /// `a` is the first point; `b` is the second point and `u` is the tangent of `a` to the curve. | ||||
|   fn quadratic_bezier(t: T, a: Self, u: Self, b: Self) -> Self; | ||||
|  | ||||
|   /// Cubic Bézier interpolation. | ||||
|   fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: T) -> Self; | ||||
|   /// | ||||
|   /// `a` is the first point; `b` is the second point; `u` is the output tangent of `a` to the curve and `v` is the | ||||
|   /// input tangent of `b` to the curve. | ||||
|   fn cubic_bezier(t: T, a: Self, u: Self, v: Self, b: Self) -> Self; | ||||
|  | ||||
|   /// Cubic Bézier interpolation – special case for non-explicit second tangent. | ||||
|   /// | ||||
|   /// This version does the same computation as [`Interpolate::cubic_bezier`] but computes the second tangent by | ||||
|   /// inversing it (typical when the next point uses a Bézier interpolation, where input and output tangents are | ||||
|   /// mirrored for the same key). | ||||
|   fn cubic_bezier_mirrored(t: T, a: Self, u: Self, v: Self, b: Self) -> Self; | ||||
| } | ||||
|  | ||||
| /// Set of types that support additions and subtraction. | ||||
| /// | ||||
| /// The [`Copy`] trait is also a supertrait as it’s likely to be used everywhere. | ||||
| pub trait Additive: Copy + Add<Self, Output = Self> + Sub<Self, Output = Self> {} | ||||
|  | ||||
| impl<T> Additive for T where T: Copy + Add<Self, Output = Self> + Sub<Self, Output = Self> {} | ||||
|  | ||||
| /// Set of additive types that support outer multiplication and division, making them linear. | ||||
| pub trait Linear<T>: Additive { | ||||
|   /// Apply an outer multiplication law. | ||||
|   fn outer_mul(self, t: T) -> Self; | ||||
|  | ||||
|   /// Apply an outer 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 | ||||
| #[macro_export] | ||||
| macro_rules! impl_Interpolate { | ||||
|   ($t:ty, $v:ty, $pi:expr) => { | ||||
|     impl $crate::interpolate::Interpolate<$t> for $v { | ||||
|       fn step(t: $t, threshold: $t, a: Self, b: Self) -> Self { | ||||
|         if t < threshold { | ||||
|           a | ||||
|         } else { | ||||
|           b | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       /// 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 | ||||
|       fn cosine(t: $t, a: Self, b: Self) -> Self { | ||||
|         let cos_nt = (1. - (t * $pi).cos()) * 0.5; | ||||
|         <Self as $crate::interpolate::Interpolate<$t>>::lerp(cos_nt, a, b) | ||||
|       } | ||||
|  | ||||
|       /// Apply an outer division law. | ||||
|       fn outer_div(self, t: $t) -> Self { | ||||
|         self / t as $q | ||||
|       } | ||||
|     } | ||||
|   }; | ||||
| } | ||||
|  | ||||
| impl_linear_cast!(f32, f64); | ||||
| 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; | ||||
| } | ||||
|  | ||||
| macro_rules! impl_one_float { | ||||
|   ($t:ty) => { | ||||
|     impl One for $t { | ||||
|       #[inline(always)] | ||||
|       fn one() -> Self { | ||||
|         1. | ||||
|       } | ||||
|     } | ||||
|   }; | ||||
| } | ||||
|  | ||||
| impl_one_float!(f32); | ||||
| impl_one_float!(f64); | ||||
|  | ||||
| /// Types with a sane definition of π and cosine. | ||||
| pub trait Trigo { | ||||
|   /// π. | ||||
|   fn pi() -> 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() | ||||
|     } | ||||
|  | ||||
|     #[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() | ||||
|     } | ||||
|  | ||||
|     #[cfg(not(feature = "std"))] | ||||
|     { | ||||
|       unsafe { cosf64(self) } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| /// Default implementation of [`Interpolate::cubic_hermite`]. | ||||
| /// | ||||
| /// `V` is the value being interpolated. `T` is the sampling value (also sometimes called time). | ||||
| pub fn cubic_hermite_def<V, T>(x: (V, T), a: (V, T), b: (V, T), y: (V, T), t: T) -> V | ||||
| where | ||||
|   V: Linear<T>, | ||||
|   T: Additive + Mul<T, Output = T> + One, | ||||
| { | ||||
|   // some stupid generic constants, because Rust doesn’t have polymorphic literals… | ||||
|   let one_t = T::one(); | ||||
|   let two_t = one_t + one_t; // lolololol | ||||
|   let three_t = two_t + one_t; // megalol | ||||
|  | ||||
|   // 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); | ||||
|  | ||||
|   a.0.outer_mul(two_t3 - three_t2 + one_t) | ||||
|     + m0.outer_mul(t3 - t2 * two_t + t) | ||||
|     + b.0.outer_mul(three_t2 - two_t3) | ||||
|     + m1.outer_mul(t3 - t2) | ||||
| } | ||||
|  | ||||
| /// Default implementation of [`Interpolate::quadratic_bezier`]. | ||||
| /// | ||||
| /// `V` is the value being interpolated. `T` is the sampling value (also sometimes called time). | ||||
| pub fn quadratic_bezier_def<V, T>(a: V, u: V, b: V, t: T) -> V | ||||
| where | ||||
|   V: Linear<T>, | ||||
|   T: Additive + Mul<T, Output = T> + One, | ||||
| { | ||||
|   let one_t = T::one() - t; | ||||
|   let one_t_2 = one_t * one_t; | ||||
|   u + (a - u).outer_mul(one_t_2) + (b - u).outer_mul(t * t) | ||||
| } | ||||
|  | ||||
| /// Default implementation of [`Interpolate::cubic_bezier`]. | ||||
| /// | ||||
| /// `V` is the value being interpolated. `T` is the sampling value (also sometimes called time). | ||||
| pub fn cubic_bezier_def<V, T>(a: V, u: V, v: V, b: V, t: T) -> V | ||||
| where | ||||
|   V: Linear<T>, | ||||
|   T: Additive + Mul<T, Output = T> + One, | ||||
| { | ||||
|   let one_t = T::one() - t; | ||||
|   let one_t_2 = one_t * one_t; | ||||
|   let one_t_3 = one_t_2 * one_t; | ||||
|   let three = T::one() + T::one() + T::one(); | ||||
|  | ||||
|   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 { | ||||
|       fn lerp(t: $t, a: Self, b: Self) -> 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(t: $t, x: ($t, Self), a: ($t, Self), b: ($t, Self), y: ($t, Self)) -> Self { | ||||
|         // sampler stuff | ||||
|         let two_t = t * 2.; | ||||
|         let three_t = t * 3.; | ||||
|         let t2 = t * t; | ||||
|         let t3 = t2 * t; | ||||
|         let two_t3 = t2 * two_t; | ||||
|         let two_t2 = t * two_t; | ||||
|         let three_t2 = t * three_t; | ||||
|  | ||||
|         // tangents | ||||
|         let m0 = (b.1 - x.1) / (b.0 - x.0) * (b.0 - a.0); | ||||
|         let m1 = (y.1 - a.1) / (y.0 - a.0) * (b.0 - a.0); | ||||
|  | ||||
|         a.1 * (two_t3 - three_t2 + 1.) | ||||
|           + m0 * (t3 - two_t2 + t) | ||||
|           + b.1 * (three_t2 - two_t3) | ||||
|           + m1 * (t3 - t2) | ||||
|       } | ||||
|  | ||||
|       fn quadratic_bezier(a: Self, u: Self, b: Self, t: $t) -> Self { | ||||
|         quadratic_bezier_def(a, u, b, t) | ||||
|       fn quadratic_bezier(t: $t, a: Self, u: Self, b: Self) -> Self { | ||||
|         let one_t = 1. - t; | ||||
|         let one_t2 = one_t * one_t; | ||||
|  | ||||
|         u + (a - u) * one_t2 + (b - u) * t * 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(t: $t, a: Self, u: Self, v: Self, b: Self) -> Self { | ||||
|         let one_t = 1. - t; | ||||
|         let one_t2 = one_t * one_t; | ||||
|         let one_t3 = one_t2 * one_t; | ||||
|         let t2 = t * t; | ||||
|  | ||||
|         a * one_t3 + (u * one_t2 * t + v * one_t * t2) * 3. + b * t2 * t | ||||
|       } | ||||
|  | ||||
|       fn cubic_bezier_mirrored(t: $t, a: Self, u: Self, v: Self, b: Self) -> Self { | ||||
|         <Self as $crate::interpolate::Interpolate<$t>>::cubic_bezier(t, a, u, b + b - v, b) | ||||
|       } | ||||
|     } | ||||
|   }; | ||||
| } | ||||
|  | ||||
| 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 | ||||
| #[macro_export] | ||||
| macro_rules! impl_InterpolateT { | ||||
|   ($t:ty, $v:ty, $pi:expr) => { | ||||
|     impl $crate::interpolate::Interpolate<$t> for $v { | ||||
|       fn step(t: $t, threshold: $t, a: Self, b: Self) -> Self { | ||||
|         if t < threshold { | ||||
|           a | ||||
|         } else { | ||||
|           b | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       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 cosine(t: $t, a: Self, b: Self) -> Self { | ||||
|         let cos_nt = (1. - (t * $pi).cos()) * 0.5; | ||||
|         <Self as $crate::interpolate::Interpolate<$t>>::lerp(cos_nt, a, b) | ||||
|       } | ||||
|  | ||||
|       fn quadratic_bezier(a: Self, u: Self, b: Self, t: $t) -> Self { | ||||
|         quadratic_bezier_def(a, u, b, t as $v) | ||||
|       fn lerp(t: $t, a: Self, b: Self) -> Self { | ||||
|         let t = Self::from(t); | ||||
|         a * (1. - t) + b * t | ||||
|       } | ||||
|  | ||||
|       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_hermite(t: $t, x: ($t, Self), a: ($t, Self), b: ($t, Self), y: ($t, Self)) -> Self { | ||||
|         // sampler stuff | ||||
|         let t = Self::from(t); | ||||
|         let two_t = t * 2.; | ||||
|         let three_t = t * 3.; | ||||
|         let t2 = t * t; | ||||
|         let t3 = t2 * t; | ||||
|         let two_t3 = t2 * two_t; | ||||
|         let two_t2 = t * two_t; | ||||
|         let three_t2 = t * three_t; | ||||
|  | ||||
|         // tangents | ||||
|         let m0 = (b.1 - x.1) / (Self::from(b.0 - x.0)) * (Self::from(b.0 - a.0)); | ||||
|         let m1 = (y.1 - a.1) / (Self::from(y.0 - a.0)) * (Self::from(b.0 - a.0)); | ||||
|  | ||||
|         a.1 * (two_t3 - three_t2 + 1.) | ||||
|           + m0 * (t3 - two_t2 + t) | ||||
|           + b.1 * (three_t2 - two_t3) | ||||
|           + m1 * (t3 - t2) | ||||
|       } | ||||
|  | ||||
|       fn quadratic_bezier(t: $t, a: Self, u: Self, b: Self) -> Self { | ||||
|         let t = Self::from(t); | ||||
|         let one_t = 1. - t; | ||||
|         let one_t2 = one_t * one_t; | ||||
|  | ||||
|         u + (a - u) * one_t2 + (b - u) * t * t | ||||
|       } | ||||
|  | ||||
|       fn cubic_bezier(t: $t, a: Self, u: Self, v: Self, b: Self) -> Self { | ||||
|         let t = Self::from(t); | ||||
|         let one_t = 1. - t; | ||||
|         let one_t2 = one_t * one_t; | ||||
|         let one_t3 = one_t2 * one_t; | ||||
|         let t2 = t * t; | ||||
|  | ||||
|         a * one_t3 + (u * one_t2 * t + v * one_t * t2) * 3. + b * t2 * t | ||||
|       } | ||||
|  | ||||
|       fn cubic_bezier_mirrored(t: $t, a: Self, u: Self, v: Self, b: Self) -> Self { | ||||
|         <Self as $crate::interpolate::Interpolate<$t>>::cubic_bezier(t, a, u, b + b - v, b) | ||||
|       } | ||||
|     } | ||||
|   }; | ||||
| } | ||||
|  | ||||
| impl_interpolate_via!(f32, f64); | ||||
| impl_interpolate_via!(f64, f32); | ||||
| impl_Interpolate!(f32, f32, std::f32::consts::PI); | ||||
| impl_Interpolate!(f64, f64, std::f64::consts::PI); | ||||
| impl_InterpolateT!(f32, f64, std::f32::consts::PI); | ||||
|   | ||||
| @@ -1,14 +1,18 @@ | ||||
| //! Available interpolation modes. | ||||
|  | ||||
| #[cfg(feature = "serialization")] | ||||
| use serde_derive::{Deserialize, Serialize}; | ||||
| #[cfg(any(feature = "serialization", feature = "serde"))] | ||||
| use serde::{Deserialize, Serialize}; | ||||
|  | ||||
| /// Available kind of interpolations. | ||||
| /// | ||||
| /// Feel free to visit each variant for more documentation. | ||||
| #[non_exhaustive] | ||||
| #[derive(Copy, Clone, Debug, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))] | ||||
| #[cfg_attr(feature = "serialization", serde(rename_all = "snake_case"))] | ||||
| #[cfg_attr( | ||||
|   any(feature = "serialization", feature = "serde"), | ||||
|   derive(Deserialize, Serialize), | ||||
|   serde(rename_all = "snake_case") | ||||
| )] | ||||
| pub enum Interpolation<T, V> { | ||||
|   /// Hold a [`Key`] until the sampling value passes the normalized step threshold, in which | ||||
|   /// case the next key is used. | ||||
| @@ -20,12 +24,16 @@ pub enum Interpolation<T, V> { | ||||
|   /// | ||||
|   /// [`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 | ||||
| @@ -41,6 +49,7 @@ pub enum Interpolation<T, V> { | ||||
|   ///   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 | ||||
| @@ -53,8 +62,6 @@ pub enum Interpolation<T, V> { | ||||
|   /// | ||||
|   /// Stroke Bézier interpolation is always a cubic Bézier interpolation by default. | ||||
|   StrokeBezier(V, V), | ||||
|   #[doc(hidden)] | ||||
|   __NonExhaustive, | ||||
| } | ||||
|  | ||||
| impl<T, V> Default for Interpolation<T, V> { | ||||
|   | ||||
							
								
								
									
										14
									
								
								src/key.rs
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								src/key.rs
									
									
									
									
									
								
							| @@ -1,15 +1,14 @@ | ||||
| //! Spline control points. | ||||
| //! | ||||
| //! A control point associates to a “sampling value” (a.k.a. time) a carriede value that can be | ||||
| //! A control point associates to a “sampling value” (a.k.a. time) a carried value that can be | ||||
| //! interpolated along the curve made by the control points. | ||||
| //! | ||||
| //! 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}; | ||||
|  | ||||
| use crate::interpolation::Interpolation; | ||||
| #[cfg(any(feature = "serialization", feature = "serde"))] | ||||
| use serde::{Deserialize, Serialize}; | ||||
|  | ||||
| /// A spline control point. | ||||
| /// | ||||
| @@ -19,8 +18,11 @@ use crate::interpolation::Interpolation; | ||||
| /// | ||||
| /// [`Interpolation`]: crate::interpolation::Interpolation | ||||
| #[derive(Copy, Clone, Debug, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))] | ||||
| #[cfg_attr(feature = "serialization", serde(rename_all = "snake_case"))] | ||||
| #[cfg_attr( | ||||
|   any(feature = "serialization", feature = "serde"), | ||||
|   derive(Deserialize, Serialize), | ||||
|   serde(rename_all = "snake_case") | ||||
| )] | ||||
| pub struct Key<T, V> { | ||||
|   /// Interpolation parameter at which the [`Key`] should be reached. | ||||
|   pub t: T, | ||||
|   | ||||
							
								
								
									
										27
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								src/lib.rs
									
									
									
									
									
								
							| @@ -84,19 +84,19 @@ | ||||
| //! | ||||
| //! So here’s a list of currently supported features and how to enable them: | ||||
| //! | ||||
| //!   - **Serialization / deserialization.** | ||||
| //!   - **Serde.** | ||||
| //!     - This feature implements both the `Serialize` and `Deserialize` traits from `serde` for all | ||||
| //!       types exported by this crate. | ||||
| //!     - Enable with the `"serialization"` feature. | ||||
| //!     - Enable with the `"serde"` feature. | ||||
| //!   - **[cgmath](https://crates.io/crates/cgmath) implementors.** | ||||
| //!     - Adds some useful implementations of `Interpolate` for some cgmath types. | ||||
| //!     - Enable with the `"impl-cgmath"` feature. | ||||
| //!     - Enable with the `"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. | ||||
| //!     - Enable with the `"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. | ||||
| //!     - Enable with the `"nalgebra"` feature. | ||||
| //!   - **Standard library / no standard library.** | ||||
| //!     - It’s possible to compile against the standard library or go on your own without it. | ||||
| //!     - Compiling with the standard library is enabled by default. | ||||
| @@ -108,19 +108,30 @@ | ||||
| #![cfg_attr(not(feature = "std"), no_std)] | ||||
| #![cfg_attr(not(feature = "std"), feature(alloc))] | ||||
| #![cfg_attr(not(feature = "std"), feature(core_intrinsics))] | ||||
| #![cfg_attr( | ||||
|   any( | ||||
|     feature = "impl-cgmath", | ||||
|     feature = "impl-glam", | ||||
|     feature = "impl-nalgebra" | ||||
|   ), | ||||
|   deprecated( | ||||
|     since = "4.2.0", | ||||
|     note = "you are using an impl-* feature gate; please switch to * (e.g. impl-cgmath becomes cgmath)" | ||||
|   ) | ||||
| )] | ||||
|  | ||||
| #[cfg(not(feature = "std"))] | ||||
| extern crate alloc; | ||||
|  | ||||
| #[cfg(feature = "impl-cgmath")] | ||||
| #[cfg(any(feature = "impl-cgmath", feature = "cgmath"))] | ||||
| mod cgmath; | ||||
| #[cfg(feature = "impl-glam")] | ||||
| #[cfg(any(feature = "impl-glam", feature = "glam"))] | ||||
| mod glam; | ||||
| pub mod interpolate; | ||||
| pub mod interpolation; | ||||
| pub mod iter; | ||||
| pub mod key; | ||||
| #[cfg(feature = "impl-nalgebra")] | ||||
| #[cfg(any(feature = "impl-nalgebra", feature = "nalgebra"))] | ||||
| mod nalgebra; | ||||
| pub mod spline; | ||||
|  | ||||
|   | ||||
| @@ -1,70 +1,18 @@ | ||||
| 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::impl_Interpolate; | ||||
| use nalgebra::{Quaternion, Vector1, Vector2, Vector3, Vector4, Vector5, Vector6}; | ||||
|  | ||||
| use crate::interpolate::{ | ||||
|   cubic_bezier_def, cubic_hermite_def, quadratic_bezier_def, Additive, Interpolate, Linear, One, | ||||
| }; | ||||
| impl_Interpolate!(f32, Vector1<f32>, std::f32::consts::PI); | ||||
| impl_Interpolate!(f32, Vector2<f32>, std::f32::consts::PI); | ||||
| impl_Interpolate!(f32, Vector3<f32>, std::f32::consts::PI); | ||||
| impl_Interpolate!(f32, Vector4<f32>, std::f32::consts::PI); | ||||
| impl_Interpolate!(f32, Vector5<f32>, std::f32::consts::PI); | ||||
| impl_Interpolate!(f32, Vector6<f32>, std::f32::consts::PI); | ||||
| impl_Interpolate!(f32, Quaternion<f32>, std::f32::consts::PI); | ||||
|  | ||||
| macro_rules! impl_interpolate_vector { | ||||
|   ($($t:tt)*) => { | ||||
|     // implement Linear | ||||
|     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 | ||||
|       } | ||||
|  | ||||
|       #[inline(always)] | ||||
|       fn outer_div(self, t: T) -> Self { | ||||
|         self / t | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     impl<T, V> Interpolate<T> for $($t)*<V> | ||||
|     where Self: Linear<T>, | ||||
|           T: Additive + One + Mul<T, Output = T>, | ||||
|           V: nt::One + | ||||
|              nt::Zero + | ||||
|              Additive + | ||||
|              Scalar + | ||||
|              ClosedAdd + | ||||
|              ClosedMul + | ||||
|              ClosedSub + | ||||
|              Interpolate<T> { | ||||
|       #[inline(always)] | ||||
|       fn lerp(a: Self, b: Self, t: T) -> Self { | ||||
|         Vector::zip_map(&a, &b, |c1, c2| Interpolate::lerp(c1, c2, 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 cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: T) -> Self { | ||||
|         cubic_bezier_def(a, u, v, b, t) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| impl_interpolate_vector!(Vector1); | ||||
| impl_interpolate_vector!(Vector2); | ||||
| impl_interpolate_vector!(Vector3); | ||||
| impl_interpolate_vector!(Vector4); | ||||
| impl_interpolate_vector!(Vector5); | ||||
| impl_interpolate_vector!(Vector6); | ||||
| impl_Interpolate!(f64, Vector1<f64>, std::f64::consts::PI); | ||||
| impl_Interpolate!(f64, Vector2<f64>, std::f64::consts::PI); | ||||
| impl_Interpolate!(f64, Vector3<f64>, std::f64::consts::PI); | ||||
| impl_Interpolate!(f64, Vector4<f64>, std::f64::consts::PI); | ||||
| impl_Interpolate!(f64, Vector5<f64>, std::f64::consts::PI); | ||||
| impl_Interpolate!(f64, Vector6<f64>, std::f64::consts::PI); | ||||
| impl_Interpolate!(f64, Quaternion<f64>, std::f64::consts::PI); | ||||
|   | ||||
							
								
								
									
										172
									
								
								src/spline.rs
									
									
									
									
									
								
							
							
						
						
									
										172
									
								
								src/spline.rs
									
									
									
									
									
								
							| @@ -1,21 +1,19 @@ | ||||
| //! Spline curves and operations. | ||||
|  | ||||
| #[cfg(feature = "std")] | ||||
| use crate::interpolate::{Interpolate, Interpolator}; | ||||
| use crate::interpolation::Interpolation; | ||||
| use crate::key::Key; | ||||
| #[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(any(feature = "serialization", feature = "serde"))] | ||||
| use serde::{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; | ||||
| use crate::key::Key; | ||||
|  | ||||
| /// Spline curve used to provide interpolation between control points (keys). | ||||
| /// | ||||
| @@ -30,7 +28,10 @@ use crate::key::Key; | ||||
| ///   - [`Spline::clamped_sample`]: behaves like [`Spline::sample`] but will return either the first | ||||
| ///     or last key if out of bound; it will return `None` if not enough key. | ||||
| #[derive(Debug, Clone)] | ||||
| #[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))] | ||||
| #[cfg_attr( | ||||
|   any(feature = "serialization", feature = "serde"), | ||||
|   derive(Deserialize, Serialize) | ||||
| )] | ||||
| pub struct Spline<T, V>(pub(crate) Vec<Key<T, V>>); | ||||
|  | ||||
| impl<T, V> Spline<T, V> { | ||||
| @@ -55,6 +56,13 @@ impl<T, V> Spline<T, V> { | ||||
|     spline | ||||
|   } | ||||
|  | ||||
|   /// Clear the spline by removing all keys. Keeps the underlying allocated storage, so adding | ||||
|   /// new keys should be faster than creating a new [`Spline`] | ||||
|   #[inline] | ||||
|   pub fn clear(&mut self) { | ||||
|     self.0.clear() | ||||
|   } | ||||
|  | ||||
|   /// Create a new spline by consuming an `Iterater<Item = Key<T>>`. They keys don’t have to be | ||||
|   /// sorted. | ||||
|   /// | ||||
| @@ -102,40 +110,38 @@ impl<T, V> Spline<T, V> { | ||||
|   /// sampling impossible. For instance, [`Interpolation::CatmullRom`] requires *four* keys. If | ||||
|   /// you’re near the beginning of the spline or its end, ensure you have enough keys around to make | ||||
|   /// the sampling. | ||||
|   pub fn sample_with_key(&self, t: T) -> Option<(V, &Key<T, V>, Option<&Key<T, V>>)> | ||||
|   pub fn sample_with_key(&self, t: T) -> Option<SampledWithKey<V>> | ||||
|   where | ||||
|     T: Additive + One + Trigo + Mul<T, Output = T> + Div<T, Output = T> + PartialOrd, | ||||
|     V: Additive + Interpolate<T>, | ||||
|     T: Interpolator, | ||||
|     V: Interpolate<T>, | ||||
|   { | ||||
|     let keys = &self.0; | ||||
|     let i = search_lower_cp(keys, t)?; | ||||
|     let cp0 = &keys[i]; | ||||
|  | ||||
|     match cp0.interpolation { | ||||
|     let 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 }; | ||||
|         let nt = t.normalize(cp0.t, cp1.t); | ||||
|         let value = V::step(nt, threshold, cp0.value, cp1.value); | ||||
|  | ||||
|         Some((value, cp0, Some(cp1))) | ||||
|         Some(value) | ||||
|       } | ||||
|  | ||||
|       Interpolation::Linear => { | ||||
|         let cp1 = &keys[i + 1]; | ||||
|         let nt = normalize_time(t, cp0, cp1); | ||||
|         let value = Interpolate::lerp(cp0.value, cp1.value, nt); | ||||
|         let nt = t.normalize(cp0.t, cp1.t); | ||||
|         let value = V::lerp(nt, cp0.value, cp1.value); | ||||
|  | ||||
|         Some((value, cp0, Some(cp1))) | ||||
|         Some(value) | ||||
|       } | ||||
|  | ||||
|       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); | ||||
|         let nt = t.normalize(cp0.t, cp1.t); | ||||
|         let value = V::cosine(nt, cp0.value, cp1.value); | ||||
|  | ||||
|         Some((value, cp0, Some(cp1))) | ||||
|         Some(value) | ||||
|       } | ||||
|  | ||||
|       Interpolation::CatmullRom => { | ||||
| @@ -147,51 +153,47 @@ impl<T, V> Spline<T, V> { | ||||
|           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), | ||||
|           let nt = t.normalize(cp0.t, cp1.t); | ||||
|           let value = V::cubic_hermite( | ||||
|             nt, | ||||
|             (cpm0.t, cpm0.value), | ||||
|             (cp0.t, cp0.value), | ||||
|             (cp1.t, cp1.value), | ||||
|             (cpm1.t, cpm1.value), | ||||
|           ); | ||||
|  | ||||
|           Some((value, cp0, Some(cp1))) | ||||
|           Some(value) | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       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 nt = t.normalize(cp0.t, cp1.t); | ||||
|  | ||||
|         let value = match cp1.interpolation { | ||||
|           Interpolation::Bezier(v) => { | ||||
|             Interpolate::cubic_bezier(cp0.value, u, cp1.value + cp1.value - v, cp1.value, nt) | ||||
|           } | ||||
|           Interpolation::Bezier(v) => V::cubic_bezier_mirrored(nt, cp0.value, u, v, cp1.value), | ||||
|  | ||||
|           Interpolation::StrokeBezier(v, _) => { | ||||
|             Interpolate::cubic_bezier(cp0.value, u, v, cp1.value, nt) | ||||
|           } | ||||
|           Interpolation::StrokeBezier(v, _) => V::cubic_bezier(nt, cp0.value, u, v, cp1.value), | ||||
|  | ||||
|           _ => Interpolate::quadratic_bezier(cp0.value, u, cp1.value, nt), | ||||
|           _ => V::quadratic_bezier(nt, cp0.value, u, cp1.value), | ||||
|         }; | ||||
|  | ||||
|         Some((value, cp0, Some(cp1))) | ||||
|         Some(value) | ||||
|       } | ||||
|     }; | ||||
|  | ||||
|       Interpolation::__NonExhaustive => unreachable!(), | ||||
|     } | ||||
|     value.map(|value| SampledWithKey { value, key: i }) | ||||
|   } | ||||
|  | ||||
|   /// 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>, | ||||
|     T: Interpolator, | ||||
|     V: Interpolate<T>, | ||||
|   { | ||||
|     self.sample_with_key(t).map(|(v, _, _)| v) | ||||
|     self.sample_with_key(t).map(|sampled| sampled.value) | ||||
|   } | ||||
|  | ||||
|   /// Sample a spline at a given time with clamping, returning the interpolated value along with its | ||||
| @@ -205,10 +207,10 @@ impl<T, V> Spline<T, V> { | ||||
|   /// # Error | ||||
|   /// | ||||
|   /// This function returns [`None`] if you have no key. | ||||
|   pub fn clamped_sample_with_key(&self, t: T) -> Option<(V, &Key<T, V>, Option<&Key<T, V>>)> | ||||
|   pub fn clamped_sample_with_key(&self, t: T) -> Option<SampledWithKey<V>> | ||||
|   where | ||||
|     T: Additive + One + Trigo + Mul<T, Output = T> + Div<T, Output = T> + PartialOrd, | ||||
|     V: Additive + Interpolate<T>, | ||||
|     T: Interpolator, | ||||
|     V: Interpolate<T>, | ||||
|   { | ||||
|     if self.0.is_empty() { | ||||
|       return None; | ||||
| @@ -216,18 +218,22 @@ impl<T, V> Spline<T, V> { | ||||
|  | ||||
|     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 | ||||
|         let sampled = SampledWithKey { | ||||
|           value: first.value, | ||||
|           key: 0, | ||||
|         }; | ||||
|         Some((first.value, &first, second)) | ||||
|         Some(sampled) | ||||
|       } else { | ||||
|         let last = self.0.last().unwrap(); | ||||
|  | ||||
|         if t >= last.t { | ||||
|           Some((last.value, &last, None)) | ||||
|           let sampled = SampledWithKey { | ||||
|             value: last.value, | ||||
|             key: self.0.len() - 1, | ||||
|           }; | ||||
|           Some(sampled) | ||||
|         } else { | ||||
|           None | ||||
|         } | ||||
| @@ -238,10 +244,10 @@ 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>, | ||||
|     T: Interpolator, | ||||
|     V: Interpolate<T>, | ||||
|   { | ||||
|     self.clamped_sample_with_key(t).map(|(v, _, _)| v) | ||||
|     self.clamped_sample_with_key(t).map(|sampled| sampled.value) | ||||
|   } | ||||
|  | ||||
|   /// Add a key into the spline. | ||||
| @@ -295,11 +301,22 @@ impl<T, V> Spline<T, V> { | ||||
|   } | ||||
| } | ||||
|  | ||||
| /// A sampled value along with its key index. | ||||
| #[derive(Clone, Debug, Eq, Hash, PartialEq)] | ||||
| pub struct SampledWithKey<V> { | ||||
|   /// Sampled value. | ||||
|   pub value: V, | ||||
|  | ||||
|   /// Key index. | ||||
|   pub key: usize, | ||||
| } | ||||
|  | ||||
| /// A mutable [`Key`]. | ||||
| /// | ||||
| /// Mutable keys allow to edit the carried values and the interpolation mode but not the actual | ||||
| /// 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`]. | ||||
| #[derive(Debug)] | ||||
| pub struct KeyMut<'a, T, V> { | ||||
|   /// Carried value. | ||||
|   pub value: &'a mut V, | ||||
| @@ -307,48 +324,21 @@ pub struct KeyMut<'a, T, V> { | ||||
|   pub interpolation: &'a mut Interpolation<T, V>, | ||||
| } | ||||
|  | ||||
| // Normalize a time ([0;1]) given two control points. | ||||
| #[inline(always)] | ||||
| pub(crate) fn normalize_time<T, V>(t: T, cp: &Key<T, V>, cp1: &Key<T, V>) -> T | ||||
| where | ||||
|   T: Additive + Div<T, Output = T> + PartialEq, | ||||
| { | ||||
|   assert!(cp1.t != cp.t, "overlapping keys"); | ||||
|   (t - cp.t) / (cp1.t - cp.t) | ||||
| } | ||||
|  | ||||
| // Find the lower control point corresponding to a given time. | ||||
| // It has the property to have a timestamp smaller or equal to t | ||||
| fn search_lower_cp<T, V>(cps: &[Key<T, V>], t: T) -> Option<usize> | ||||
| where | ||||
|   T: PartialOrd, | ||||
| { | ||||
|   let mut i = 0; | ||||
|   let len = cps.len(); | ||||
|  | ||||
|   if len < 2 { | ||||
|     return None; | ||||
|   } | ||||
|  | ||||
|   loop { | ||||
|     let cp = &cps[i]; | ||||
|     let cp1 = &cps[i + 1]; | ||||
|  | ||||
|     if t >= cp1.t { | ||||
|       if i >= len - 2 { | ||||
|         return None; | ||||
|       } | ||||
|  | ||||
|       i += 1; | ||||
|     } else if t < cp.t { | ||||
|       if i == 0 { | ||||
|         return None; | ||||
|       } | ||||
|  | ||||
|       i -= 1; | ||||
|     } else { | ||||
|       break; // found | ||||
|     } | ||||
|   match cps.binary_search_by(|key| key.t.partial_cmp(&t).unwrap()) { | ||||
|     Err(i) if i >= len => None, | ||||
|     Err(i) if i == 0 => None, | ||||
|     Err(i) => Some(i - 1), | ||||
|     Ok(i) if i == len - 1 => None, | ||||
|     Ok(i) => Some(i), | ||||
|   } | ||||
|  | ||||
|   Some(i) | ||||
| } | ||||
|   | ||||
							
								
								
									
										43
									
								
								tests/cgmath.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								tests/cgmath.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| #![cfg(feature = "cgmath")] | ||||
|  | ||||
| use cgmath as cg; | ||||
| use splines::{Interpolation, Key, Spline}; | ||||
|  | ||||
| #[test] | ||||
| fn cgmath_vector_interpolation() { | ||||
|   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); | ||||
|  | ||||
|   assert_eq!(Interpolate::lerp(0., start, end), start); | ||||
|   assert_eq!(Interpolate::lerp(1., start, end), end); | ||||
|   assert_eq!(Interpolate::lerp(0.5, start, end), mid); | ||||
| } | ||||
|  | ||||
| #[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.)); | ||||
| } | ||||
| @@ -1,9 +1,4 @@ | ||||
| use splines::{Interpolation, Key, Spline}; | ||||
| 
 | ||||
| #[cfg(feature = "cgmath")] | ||||
| use cgmath as cg; | ||||
| #[cfg(feature = "nalgebra")] | ||||
| use nalgebra as na; | ||||
| use splines::{spline::SampledWithKey, Interpolation, Key, Spline}; | ||||
| 
 | ||||
| #[test] | ||||
| fn step_interpolation_f32() { | ||||
| @@ -18,8 +13,14 @@ 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))); | ||||
|   assert_eq!( | ||||
|     spline.sample_with_key(0.2), | ||||
|     Some(SampledWithKey { value: 10., key: 0 }) | ||||
|   ); | ||||
|   assert_eq!( | ||||
|     spline.clamped_sample_with_key(1.), | ||||
|     Some(SampledWithKey { value: 10., key: 1 }) | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| @@ -35,8 +36,14 @@ 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))); | ||||
|   assert_eq!( | ||||
|     spline.sample_with_key(0.2), | ||||
|     Some(SampledWithKey { value: 10., key: 0 }) | ||||
|   ); | ||||
|   assert_eq!( | ||||
|     spline.clamped_sample_with_key(1.), | ||||
|     Some(SampledWithKey { value: 10., key: 1 }) | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| @@ -151,61 +158,6 @@ fn several_interpolations_several_keys() { | ||||
|   assert_eq!(spline.clamped_sample(11.), Some(4.)); | ||||
| } | ||||
| 
 | ||||
| #[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; | ||||
| 
 | ||||
|   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); | ||||
| } | ||||
| 
 | ||||
| #[cfg(feature = "nalgebra")] | ||||
| #[test] | ||||
| fn nalgebra_vector_interpolation() { | ||||
|   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); | ||||
| 
 | ||||
|   assert_eq!(Interpolate::lerp(start, end, 0.0), start); | ||||
|   assert_eq!(Interpolate::lerp(start, end, 1.0), end); | ||||
|   assert_eq!(Interpolate::lerp(start, end, 0.5), mid); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn add_key_empty() { | ||||
|   let mut spline: Spline<f32, f32> = Spline::from_vec(vec![]); | ||||
							
								
								
									
										16
									
								
								tests/nalgebra.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								tests/nalgebra.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| #![cfg(feature = "nalgebra")] | ||||
|  | ||||
| use nalgebra as na; | ||||
|  | ||||
| #[test] | ||||
| fn nalgebra_vector_interpolation() { | ||||
|   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); | ||||
|  | ||||
|   assert_eq!(Interpolate::lerp(0., start, end), start); | ||||
|   assert_eq!(Interpolate::lerp(1., start, end), end); | ||||
|   assert_eq!(Interpolate::lerp(0.5, start, end), mid); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user