183 Commits

Author SHA1 Message Date
da43539b70 Run rustfmt. 2023-09-23 13:01:41 +02:00
eca09f1baf add clear method to Spline 2023-09-13 15:52:43 +02:00
e1b78070c6 Merge pull request #97 from phaazon/dependabot/cargo/master/glam-gte-0.10-and-lt-0.25
Update glam requirement from >=0.10, <0.23 to >=0.10, <0.25
2023-05-30 10:27:54 +02:00
cdc48a49a7 Update glam requirement from >=0.10, <0.23 to >=0.10, <0.25
Updates the requirements on [glam](https://github.com/bitshifter/glam-rs) to permit the latest version.
- [Release notes](https://github.com/bitshifter/glam-rs/releases)
- [Changelog](https://github.com/bitshifter/glam-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitshifter/glam-rs/compare/0.10.0...0.24.0)

---
updated-dependencies:
- dependency-name: glam
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-24 04:59:27 +00:00
decd85dba2 Merge pull request #95 from phaazon/update-deps
Bump dependencies (cgmath, nalgebra) to latest.
2023-02-01 19:41:51 +01:00
df9815a464 Add deprecation lints if using impl-* feature gates. 2023-02-01 19:29:31 +01:00
ca8e797932 Prepare version 4.2.0. 2023-02-01 19:19:45 +01:00
5b746aaf57 Bump dependencies (cgmath, nalgebra) to latest. 2023-02-01 19:12:11 +01:00
88a7ee7a8d Merge pull request #91 from timethy/patch-1
Update CHANGELOG.md
2022-07-27 16:29:24 +02:00
Tim
5463fd11d6 Update CHANGELOG.md
Small typo
2022-07-27 09:22:51 +02:00
d9770ad60b Merge pull request #90 from phaazon/release/4.1.1
Release/4.1.1
2022-07-27 00:49:00 +02:00
3dfea81856 Update changelog. 2022-07-27 00:42:42 +02:00
604dcc6e27 Increment version number. 2022-07-27 00:42:30 +02:00
8c952ae242 Merge pull request #86 from hokwangchoi/master
Fix cubic_hermite interpolation
2022-07-27 00:42:23 +02:00
b52643b5d7 Merge pull request #85 from phaazon/dependabot/cargo/master/nalgebra-gte-0.21-and-lt-0.32
Update nalgebra requirement from >=0.21, <0.31 to >=0.21, <0.32
2022-07-27 00:41:30 +02:00
dc6ef0a5cc Update nalgebra requirement from >=0.21, <0.31 to >=0.21, <0.32
Updates the requirements on [nalgebra](https://github.com/dimforge/nalgebra) to permit the latest version.
- [Release notes](https://github.com/dimforge/nalgebra/releases)
- [Changelog](https://github.com/dimforge/nalgebra/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/dimforge/nalgebra/compare/v0.21.0...v0.31.0)

---
updated-dependencies:
- dependency-name: nalgebra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-26 22:10:25 +00:00
043a8608c3 Merge pull request #88 from phaazon/dependabot/cargo/master/glam-gte-0.10-and-lt-0.22
Update glam requirement from >=0.10, <0.21 to >=0.10, <0.22
2022-07-27 00:09:22 +02:00
295043e5af rustfmt 2022-07-27 00:08:26 +02:00
1c249215c9 Merge pull request #89 from timethy/interpolation-made-fast
Interpolation made fast
2022-07-27 00:02:17 +02:00
5a7e74d79c Binary Search to achieve promised logarithmic running time for sample (instead of linear!)
(cherry picked from commit ffcf289f713d5d92a74038ffa2fb6059d9995175)
2022-06-23 16:58:30 +02:00
2012105a72 Update glam requirement from >=0.10, <0.21 to >=0.10, <0.22
Updates the requirements on [glam](https://github.com/bitshifter/glam-rs) to permit the latest version.
- [Release notes](https://github.com/bitshifter/glam-rs/releases)
- [Changelog](https://github.com/bitshifter/glam-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitshifter/glam-rs/compare/0.10.0...0.21.1)

---
updated-dependencies:
- dependency-name: glam
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-23 07:31:20 +00:00
f25ebb2c64 remove unfinished test 2022-06-07 12:50:18 -04:00
e9c1de389f scale tangent 2022-06-07 12:47:17 -04:00
bdeaefd9f9 fix cubic_hermite coefficients & powers 2022-06-07 12:01:25 -04:00
ace0f4ec50 Merge pull request #84 from phaazon/release/4.1
4.1.0.
2022-03-28 11:43:48 +02:00
b056a4e9a7 4.1.0. 2022-03-28 11:38:25 +02:00
042253ab9c Merge pull request #83 from phaazon/nalgebra-update
Bump nalgebra dependency.
2022-03-25 09:14:31 +01:00
270f225394 Bump nalgebra dependency. 2022-03-24 17:09:20 +01:00
138828e798 Merge pull request #79 from phaazon/dependabot/cargo/master/glam-gte-0.10-and-lt-0.21
Update glam requirement from >=0.10, <0.18 to >=0.10, <0.21
2022-03-22 20:34:31 +01:00
0ca1c5aa48 Merge pull request #75 from phaazon/dependabot/cargo/master/float-cmp-gte-0.6-and-lt-0.10
Update float-cmp requirement from >=0.6, < 0.9 to >=0.6, < 0.10
2022-03-22 20:33:36 +01:00
f4c8be33b9 Merge pull request #82 from phaazon/edition-2021
Edition 2021
2022-03-22 20:20:01 +01:00
26bd5c88eb Update and simplify the CI. 2022-03-22 16:15:39 +01:00
a4cd49fd20 Switch to edition 2021. 2022-03-22 16:14:28 +01:00
09bc7069b2 Update glam requirement from >=0.10, <0.18 to >=0.10, <0.21
Updates the requirements on [glam](https://github.com/bitshifter/glam-rs) to permit the latest version.
- [Release notes](https://github.com/bitshifter/glam-rs/releases)
- [Changelog](https://github.com/bitshifter/glam-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitshifter/glam-rs/compare/0.10.0...0.20.0)

---
updated-dependencies:
- dependency-name: glam
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-02 04:13:32 +00:00
8dc8606bf0 Update float-cmp requirement from >=0.6, < 0.9 to >=0.6, < 0.10
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)

---
updated-dependencies:
- dependency-name: float-cmp
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-22 04:19:29 +00:00
fc2f53200f Fix previous commit (new implementors). 2021-07-11 17:46:59 +02:00
b3836975c3 Add more implementors for Interpolate. 2021-07-11 17:44:05 +02:00
322d271499 Merge pull request #73 from phaazon/next/release
Prepare 4.0.1.
2021-07-11 16:54:24 +02:00
e64298dc88 Prepare 4.0.1. 2021-07-11 16:50:28 +02:00
32e5122339 Merge pull request #72 from phaazon/update-dependencies
Update all dependencies to latest version.
2021-07-11 16:47:34 +02:00
1be94935cf Update all dependencies to latest version. 2021-07-11 16:27:24 +02:00
06f6e4b578 Merge pull request #71 from phaazon/better-feature-gates
Provide better feature gates.
2021-07-11 16:25:45 +02:00
87e27e732d Synchronize README. 2021-07-11 16:22:42 +02:00
fb678f9613 Provide better feature gates.
- `serialization` becomes `serde`.
- `impl-nalgebra` becomes `nalgebra`.
- `impl-cgmath` becomes `cgmath`.
- `impl-glam` becomes `glam`.

Already existing feature gates are kept around so that we don’t
introduce a breaking change.
2021-07-11 16:18:47 +02:00
ee4230340b Merge pull request #64 from phaazon/dependabot/cargo/master/glam-gte-0.10-and-lt-0.15
Update glam requirement from >=0.10, <0.13 to >=0.10, <0.15
2021-07-11 16:09:46 +02:00
f585119883 Merge pull request #66 from phaazon/dependabot/add-v2-config-file
Upgrade to GitHub-native Dependabot
2021-05-04 00:32:44 +02:00
25d5c5217e Upgrade to GitHub-native Dependabot 2021-04-29 15:36:58 +00:00
355178f5fa Update glam requirement from >=0.10, <0.13 to >=0.10, <0.15
Updates the requirements on [glam](https://github.com/bitshifter/glam-rs) to permit the latest version.
- [Release notes](https://github.com/bitshifter/glam-rs/releases)
- [Changelog](https://github.com/bitshifter/glam-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bitshifter/glam-rs/compare/0.10.0...0.14.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-04-09 09:54:49 +00:00
b92c28cfbb Merge pull request #63 from phaazon/refactor
Refactor
2021-03-05 02:50:14 +01:00
695caf0cca Prepare 4.0. 2021-03-05 02:44:49 +01:00
3e85a1f026 Update and fix implementors for the new API. 2021-03-05 02:05:36 +01:00
0ccc3c0956 Refactor the Interpolate trait and add the Interpolator trait.
This commit represents 99% of the rework. From now on, implementing the
API requires to provide the various interpolation implementations. This
is actually a good thing, because people will now be able to either use
the `impl_Interpolate!` macro, which implements the interpolation in a
very “math” way (using std::ops::* traits and float literals), or by
providing their own.
2021-03-05 02:03:46 +01:00
3d43e4c644 Code hygiene. 2021-02-28 19:02:54 +01:00
29833f0ebb Merge pull request #57 from phaazon/dependabot/cargo/master/glam-gte-0.10-and-lt-0.13
Update glam requirement from >=0.10, <0.12 to >=0.10, <0.13
2021-02-28 18:57:15 +01:00
80fb6fbe28 Tyyyyyyyyypo. 2021-02-27 23:58:01 +01:00
cc3ac349b4 Cleanup integration tests. 2021-02-27 23:52:51 +01:00
395dff34ee Introduce the concept of SampledWithKey.
This allows to type more correctly the output of the `*_with_key`
functions.
2021-02-27 23:38:54 +01:00
469a785767 Code hygiene. 2021-02-27 23:09:50 +01:00
dd7ae34670 Remove __NonExhaustive and replace with #[non_exhaustive]. 2021-02-27 23:09:19 +01:00
b0a6e3d5e9 Update glam requirement from >=0.10, <0.12 to >=0.10, <0.13
Updates the requirements on [glam](https://github.com/bitshifter/glam-rs) to permit the latest version.
- [Release notes](https://github.com/bitshifter/glam-rs/releases)
- [Changelog](https://github.com/bitshifter/glam-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bitshifter/glam-rs/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-27 21:52:54 +00:00
e1998fda56 Merge pull request #59 from phaazon/release/3.5.4
Prepare 3.5.4.
2021-02-27 22:51:25 +01:00
e7c3003dcf Prepare 3.5.4. 2021-02-27 22:42:12 +01:00
cf6ae61859 Merge pull request #58 from phaazon/dependabot/cargo/master/simba-gte-0.1.2-and-lt-0.5
Update simba requirement from >=0.1.2, <0.4 to >=0.1.2, <0.5
2021-02-27 22:37:00 +01:00
3640078d12 Update simba requirement from >=0.1.2, <0.4 to >=0.1.2, <0.5
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/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-26 15:46:43 +00:00
c0d9a0b540 Prepare 3.5.3. 2021-01-16 17:04:27 +01:00
4f0bf51b5c Fix README. 2021-01-16 17:03:28 +01:00
cc2b9c75a0 Prepare 3.5.2. 2021-01-01 16:59:52 +01:00
60952acb73 Merge pull request #55 from phaazon/dependabot/cargo/master/nalgebra-gte-0.21-and-lt-0.25
Update nalgebra requirement from >=0.21, <0.24 to >=0.21, <0.25
2021-01-01 03:07:31 +01:00
fb67b32959 Update nalgebra requirement from >=0.21, <0.24 to >=0.21, <0.25
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.24.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-30 14:33:27 +00:00
ab543b61e8 Merge pull request #54 from phaazon/dependabot/cargo/master/glam-0.11
Update glam requirement from 0.10 to 0.11
2020-12-05 16:39:28 +01:00
73df77380b Prepare 3.5.1. 2020-12-05 16:34:56 +01:00
08bcf902a4 Update glam requirement from 0.10 to 0.11
Updates the requirements on [glam](https://github.com/bitshifter/glam-rs) to permit the latest version.
- [Release notes](https://github.com/bitshifter/glam-rs/releases)
- [Changelog](https://github.com/bitshifter/glam-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bitshifter/glam-rs/compare/0.10.0...0.11.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-25 20:41:04 +00:00
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
1bfd9a0e7c Merge pull request #29 from phaazon/release/2.0.0
2.0.0.
2019-09-24 10:59:00 +02:00
7846177471 Fix CI. 2019-09-24 10:44:45 +02:00
6f65be125b 2.0.0. 2019-09-24 10:42:03 +02:00
5d0ebc0777 Merge pull request #28 from phaazon/feature/mutation
Feature/mutation
2019-09-23 21:12:06 +02:00
4fdbfa6189 Fix 1.1. 2019-09-23 20:56:56 +02:00
7dbc85a312 Add key getters (immutable & mutable). 2019-09-23 20:34:39 +02:00
03031a1e92 Yank notation. 2019-09-23 19:53:52 +02:00
54eb89ae96 Merge pull request #27 from phaazon/feature/extra-splines
Feature/extra splines
2019-09-23 17:13:22 +02:00
51ab8022f9 Fix CI. 2019-09-23 17:10:40 +02:00
b78be8cba3 Prepare 1.1. 2019-09-23 17:09:09 +02:00
fd05dd0419 Update readme. 2019-09-23 17:08:32 +02:00
b05582d653 Add Bézier curves. 2019-09-23 17:06:32 +02:00
e76f18ac5b 1.0.0. 2019-09-22 19:15:57 +02:00
8e6af2cee9 Merge pull request #26 from phaazon/feature/add-key
Implement Spline::add.
2019-09-22 19:05:15 +02:00
a6e77a3d09 Remove Travis CI. 2019-09-22 18:22:12 +02:00
510881b5c6 Implement Spline::add.
Fixes #23.
2019-09-22 18:21:20 +02:00
1eed163277 Doc typo. 2019-09-22 18:13:52 +02:00
311efa5b26 Synchronize README. 2019-09-21 14:42:08 +02:00
c98b493993 Add support for removing a key. #24 2019-09-21 14:42:08 +02:00
c818b4c810 Add GitHub CI. 2019-09-21 14:19:21 +02:00
7644177398 1.0.0-rc.3. 2019-04-25 11:37:49 +02:00
3d0a0c570e Fix nalgebra implementor.
Point must be removed because it is not additive.
2019-04-25 11:37:49 +02:00
bdb9a68c3b 1.0.0-rc.2. 2019-04-23 18:43:30 +02:00
e7ecc9819a Documentation, step 4. 2019-04-23 18:43:30 +02:00
e88da58a87 Step 3 of doc cleanup. 2019-04-23 18:43:30 +02:00
6ae3918eb1 Second pass of doc cleanup. 2019-04-23 18:43:30 +02:00
dcd82f7301 First doc cleanup. 2019-04-23 18:43:30 +02:00
8de0f10572 1.0.0-rc.1. 2019-04-21 19:20:15 +02:00
476f762c5f Bump cgmath dependency. 2019-04-21 19:05:51 +02:00
6ee68b4d56 Build without std but do not test (yet). 2019-04-21 18:51:43 +02:00
609ebb0f37 Cleanup. 2019-04-21 18:51:43 +02:00
305ce7ac93 Align and reformat. 2019-04-21 18:51:43 +02:00
70d6cf2081 Implement impl-cgmath. 2019-04-21 18:51:43 +02:00
9d5971a5f7 Remove nalgebra point interpolation. 2019-04-21 18:51:43 +02:00
65a713c51b Implement impl-nalgebra feature. 2019-04-21 18:51:43 +02:00
427895ab10 The cubic_hermite_def function is a bit fucked as impossible to use. 2019-04-21 18:51:43 +02:00
99068fb2d0 Refactor all types in their own modules. 2019-04-21 18:51:43 +02:00
935565ca22 Add f64-key unit test. #12 2019-04-19 13:07:55 +02:00
f4a90b82bc Fix unit tests. 2019-04-19 13:04:55 +02:00
5b70d6921c Refactor polymorphic sampling code. 2019-04-19 13:04:55 +02:00
48623701a7 Fix some documentation. 2019-04-19 13:04:55 +02:00
b548566802 Add support for std/no_std num-traits. 2019-04-19 13:04:55 +02:00
f3bd7cee24 Add support for polymorphic sampling type. 2019-04-19 13:04:55 +02:00
2b5aac42dd Fix example for clamped_sample change. 2019-04-16 17:40:08 +02:00
55e792a98b Make Spline<T>::clamped_sample return Option<T> instead. #9 2019-04-16 17:40:08 +02:00
bc329fe736 Migrate to Rust 2018. 2019-04-13 21:54:17 +02:00
ed222e001d Fix a typo in the top-level documentation. 2019-04-13 21:54:17 +02:00
a3a2919eb4 0.2.3. 2018-10-13 03:31:44 +02:00
37cf89b566 Fix the nalgebra dependency to accept 0.14, 0.15 and 0.16. 2018-10-13 01:05:13 +02:00
77ccf0a47b Add support for nalgebra along with some tests.
Feature-gated with impl-nalgebra.
2018-10-13 01:05:13 +02:00
766066d9ed 0.2.2. 2018-09-30 21:38:49 +02:00
882b9e7b34 minor corrections in README.md 2018-09-30 21:32:59 +02:00
0dcfe48415 minor spelling corrections 2018-09-30 21:32:59 +02:00
24cd0d7fca bumped version numbers in examples for splines dependancy 2018-09-30 21:32:22 +02:00
3cd65dce54 Fix feature documentation in both README and lib.rs. 2018-09-20 11:56:57 +02:00
27 changed files with 1654 additions and 597 deletions

25
.github/dependabot.yml vendored Normal file
View 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

38
.github/workflows/ci.yaml vendored Normal file
View File

@ -0,0 +1,38 @@
name: CI
on: [push, pull_request]
jobs:
build-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Test
run: cargo test --verbose --all-features
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
- name: Test
run: cargo test --verbose --all-features
build-macosx:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1
- name: Test
run: cargo test --verbose --all-features
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- 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,24 +0,0 @@
language: rust
rust:
- stable
- beta
- nightly
os:
- linux
- osx
script:
- rustc --version
- cargo --version
- echo "Testing default crate configuration"
- cargo build --verbose
- cargo test --verbose
- cd examples && cargo check --verbose
- echo "Testing feature serialization"
- cargo build --verbose --features serialization
- cargo test --verbose --features serialization
- echo "Testing without std"
- cargo build --verbose --no-default-features
- cargo test --verbose --no-default-features

View File

@ -1,21 +1,307 @@
# Changelog
* [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)
* [3.5.1](#351)
* [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-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-2)
* [Minor changes](#minor-changes)
* [1.0](#10)
* [Major changes](#major-changes-3)
* [Minor changes](#minor-changes-1)
* [Patch changes](#patch-changes-2)
* [0.2.3](#023)
* [0.2.2](#022)
* [0.2.1](#021)
* [0.2](#02)
* [0.1.1](#011)
* [0.1](#01)
# 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 shouldnt 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
> Feb 27, 2021
- Support of `cgmath-0.18`.
# 3.5.3
> Jan 16, 2021
- Resynchronize and fix links in the README (fix in `cargo sync-readme`).
# 3.5.2
> Fri Jan 01, 2021
- Support of `nalgebra-0.24`.
# 3.5.1
> Dec 5th, 2020
- Support of `glam-0.11`.
# 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
- Add support for [Bézier curves](https://en.wikipedia.org/wiki/B%C3%A9zier_curve).
- Because of Bézier curves, the `Interpolation` type now has one more type variable to know how we
should interpolate with Bézier.
## Minor changes
- Add `Spline::get`, `Spline::get_mut` and `Spline::replace`.
# 1.0
> Sun Sep 22nd 2019
## Major changes
- Make `Spline::clamped_sample` failible via `Option` instead of panicking.
- Add support for polymorphic sampling type.
## Minor changes
- Add the `std` feature (and hence support for `no_std`).
- Add `impl-nalgebra` feature.
- Add `impl-cgmath` feature.
- Add support for adding keys to splines.
- Add support for removing keys from splines.
## Patch changes
- Migrate to Rust 2018.
- Documentation typo fixes.
# 0.2.3
> Sat 13th October 2018
- Add the `"impl-nalgebra"` feature gate. It gives access to some implementors for the `nalgebra`
crate.
- Enhance the documentation.
# 0.2.2
> Sun 30th September 2018
- Bump version numbers (`splines-0.2`) in examples.
- Fix several typos in the documentation.
# 0.2.1
> Thu 20th September 2018
- Enhance the features documentation.
# 0.2
> Thu 6th September 2018
- Add the `"std"` feature gate, that can be used to compile with the standard library.
- Add the `"impl-cgmath"` in order to make it optional, if wanted, the `cgmath` dependency.
- Enhance the documentation.
- Add the `"std"` feature gate, that can be used to compile with the standard library.
- Add the `"impl-cgmath"` feature gate in order to make optional, if wanted, the `cgmath`
dependency.
- Enhance the documentation.
## 0.1.1
# 0.1.1
> Wed 8th August 2018
- Add a feature gate, `"serialization"`, that can be used to automatically derive `Serialize` and
`Deserialize` from the [serde](https://crates.io/crates/serde) crate.
- Enhance the documentation.
- Add a feature gate, `"serialization"`, that can be used to automatically derive `Serialize` and
`Deserialize` from the [serde](https://crates.io/crates/serde) crate.
- Enhance the documentation.
# 0.1
> Sunday 5th August 2018
- Initial revision.
- Initial revision.

View File

@ -1,6 +1,6 @@
[package]
name = "splines"
version = "0.2.0"
version = "4.2.0"
license = "BSD-3-Clause"
authors = ["Dimitri Sabadie <dimitri.sabadie@gmail.com>"]
description = "Spline interpolation made easy"
@ -11,26 +11,32 @@ repository = "https://github.com/phaazon/splines"
documentation = "https://docs.rs/splines"
readme = "README.md"
[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"]
serialization = ["serde", "serde_derive"]
std = []
default = ["std"]
impl-cgmath = ["cgmath"]
impl-glam = ["glam"]
impl-nalgebra = ["nalgebra"]
serialization = ["serde"]
std = []
[dependencies.cgmath]
version = "0.16"
optional = true
[dependencies]
cgmath = { version = ">=0.17, <0.19", 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 }
[dependencies.serde]
version = "1"
optional = true
[dev-dependencies]
float-cmp = ">=0.6, < 0.10"
serde_json = "1"
[dependencies.serde_derive]
version = "1"
optional = true
[package.metadata.docs.rs]
features = ["std", "cgmath", "glam", "nalgebra", "serde"]
[[example]]
name = "hello-world"
[[example]]
name = "serialization"
required-features = ["serde"]

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.

112
README.md
View File

@ -1,19 +1,107 @@
# splines
This crate provides [splines](https://en.wikipedia.org/wiki/Spline_(mathematics)), mathematic curves
defined piecewise through control keys a.k.a. knots.
Feel free to dig in the [online documentation](https://docs.rs/splines) for further information.
## A note on features
<!-- cargo-sync-readme start -->
This crate has features! Heres a comprehensive list of what you can enable:
# Spline interpolation made easy.
- **Serialization / deserialization.**
+ This feature implements both the `Serialize` and `Deserialize` traits from `serde`.
+ Enable with the `"serialization"` feature.
- **Standard library / no stdandard library**
+ Its possible to compile against the standard library or go on your own without it.
+ Compiling with the standard library is enabled by default.
+ Use `defaut-features = []` in your `Cargo.toml` to disable.
+ Enable explicitly with the `"std"` feataure.
This crate exposes splines for which each sections can be interpolated independently of each
other i.e. its possible to interpolate with a linear interpolator on one section and then
switch to a cubic Hermite interpolator for the next section.
Most of the crate consists of three types:
- [`Key`], which represents the control points by which the spline must pass.
- [`Interpolation`], the type of possible interpolation for each segment.
- [`Spline`], a spline from which you can *sample* points by interpolation.
When adding control points, you add new sections. Two control points define a section i.e.
its not possible to define a spline without at least two control points. Every time you add a
new control point, a new section is created. Each section is assigned an interpolation mode that
is picked from its lower control point.
# Quickly create splines
```rust
use splines::{Interpolation, Key, Spline};
let start = Key::new(0., 0., Interpolation::Linear);
let end = Key::new(1., 10., Interpolation::default());
let spline = Spline::from_vec(vec![start, end]);
```
You will notice that we used `Interpolation::Linear` for the first key. The first key `start`s
interpolation will be used for the whole segment defined by those two keys. The `end`s
interpolation wont be used. You can in theory use any [`Interpolation`] you want for the last
key. We use the default one because we dont care.
# Interpolate values
The whole purpose of splines is to interpolate discrete values to yield continuous ones. This is
usually done with the [`Spline::sample`] method. This method expects the sampling parameter
(often, this will be the time of your simulation) as argument and will yield an interpolated
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);
```
Its possible that you want to get a value even if youre out-of-bounds. This is especially
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
```
# Polymorphic sampling types
[`Spline`] curves are parametered both by the carried value (being interpolated) but also the
sampling type. Its very typical to use `f32` or `f64` but really, you can in theory use any
kind of type; that type must, however, implement a contract defined by a set of traits to
implement. See [the documentation of this module](https://docs.rs/splines/latest/splines/interpolate/) for further details.
# Features and customization
This crate was written with features baked in and hidden behind feature-gates. The idea is that
the default configuration (i.e. you just add `"splines = …"` to your `Cargo.toml`) will always
give you the minimal, core and raw concepts of what splines, keys / knots and interpolation
modes are. However, you might want more. Instead of letting other people do the extra work to
add implementations for very famous and useful traits and do it in less efficient way, because
they wouldnt have access to the internals of this crate, its possible to enable features in an
ad hoc way.
This mechanism is not final and this is currently an experiment to see how people like it or
not. Its especially important to see how it copes with the documentation.
So heres a list of currently supported features and how to enable them:
- **Serde.**
- This feature implements both the `Serialize` and `Deserialize` traits from `serde` for all
types exported by this crate.
- 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 `"cgmath"` feature.
- **[glam](https://crates.io/crates/glam) implementors.**
- Adds some useful implementations of `Interpolate` for some glam types.
- 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 `"nalgebra"` feature.
- **Standard library / no standard library.**
- Its possible to compile against the standard library or go on your own without it.
- Compiling with the standard library is enabled by default.
- Use `default-features = []` in your `Cargo.toml` to disable.
- Enable explicitly with the `"std"` feature.
[`Interpolation`]: crate::interpolation::Interpolation
<!-- cargo-sync-readme end -->

View File

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

View File

@ -1,11 +0,0 @@
extern crate splines;
use splines::{Interpolation, Key, Spline};
fn main() {
let keys = vec![Key::new(0., 0., Interpolation::default()), Key::new(5., 1., Interpolation::default())];
let spline = Spline::from_vec(keys);
println!("value at 0: {}", spline.clamped_sample(0.));
println!("value at 3: {}", spline.clamped_sample(3.));
}

View File

@ -1,11 +0,0 @@
[package]
name = "serialization"
version = "0.1.0"
authors = ["Dimitri Sabadie <dimitri.sabadie@gmail.com>"]
[dependencies]
serde_json = "1"
[dependencies.splines]
version = "0.1"
features = ["serialization"]

View File

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

14
examples/hello-world.rs Normal file
View File

@ -0,0 +1,14 @@
extern crate splines;
use splines::{Interpolation, Key, Spline};
fn main() {
let keys = vec![
Key::new(0., 0., Interpolation::default()),
Key::new(5., 1., Interpolation::default()),
];
let spline = Spline::from_vec(keys);
println!("value at 0: {:?}", spline.clamped_sample(0.));
println!("value at 3: {:?}", spline.clamped_sample(3.));
}

View File

@ -1,11 +1,12 @@
#[macro_use] extern crate serde_json;
#[macro_use]
extern crate serde_json;
extern crate splines;
use serde_json::{Value, from_value};
use serde_json::from_value;
use splines::Spline;
fn main() {
let value = json!{
let value = json! {
[
{
"t": 0,
@ -25,6 +26,6 @@ fn main() {
]
};
let spline = from_value::<Spline<f32>>(value);
let spline = from_value::<Spline<f32, f32>>(value);
println!("{:?}", spline);
}

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

15
src/cgmath.rs Normal file
View File

@ -0,0 +1,15 @@
use crate::impl_Interpolate;
use cgmath::{Quaternion, Vector1, Vector2, Vector3, Vector4};
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);
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);

8
src/glam.rs Normal file
View File

@ -0,0 +1,8 @@
use crate::impl_Interpolate;
use glam::{Quat, Vec2, Vec3, Vec3A, Vec4};
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);

237
src/interpolate.rs Normal file
View File

@ -0,0 +1,237 @@
//! The [`Interpolate`] trait and associated symbols.
//!
//! The [`Interpolate`] trait is the central concept of the crate. It enables a spline to be
//! sampled at by interpolating in between control points.
//!
//! In order for a type to be used in [`Spline<K, V>`], some properties must be met about the `K`
//! type must implementing several traits:
//!
//! - [`One`], giving a neutral element for the multiplication monoid.
//! - [`Additive`], making the type additive (i.e. one can add or subtract with it).
//! - [`Linear`], unlocking linear combinations, required for interpolating.
//! - [`Trigo`], a trait giving *π* and *cosine*, required for e.g. cosine interpolation.
//!
//! Feel free to have a look at current implementors for further help.
//!
//! > *Why doesnt this crate use [num-traits] instead of
//! > defining its own traits?*
//!
//! The reason for this is quite simple: this crate provides a `no_std` support, which is not
//! currently available easily with [num-traits]. Also, if something changes in [num-traits] with
//! those traits, it would make this whole crate unstable.
//!
//! [`Interpolate`]: crate::interpolate::Interpolate
//! [`Spline<K, V>`]: crate::spline::Spline
//! [`One`]: crate::interpolate::One
//! [`Additive`]: crate::interpolate::Additive
//! [`Linear`]: crate::interpolate::Linear
//! [`Trigo`]: crate::interpolate::Trigo
//! [num-traits]: https://crates.io/crates/num-traits
#[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;
/// Types that can be used as interpolator in splines.
///
/// 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.
///
/// `T` is the interpolator used to sample with. Typical implementations use [`f32`] or [`f64`], but
/// youre 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(t: T, a: Self, b: Self) -> Self;
/// Cosine interpolation.
fn cosine(t: T, a: Self, b: Self) -> Self;
/// Cubic hermite interpolation.
fn cubic_hermite(t: T, x: (T, Self), a: (T, Self), b: (T, Self), y: (T, Self)) -> Self;
/// Quadratic Bézier interpolation.
///
/// `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.
///
/// `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;
}
#[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
}
}
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 lerp(t: $t, a: Self, b: Self) -> Self {
a * (1. - t) + b * 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(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(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)
}
}
};
}
#[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 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 lerp(t: $t, a: Self, b: Self) -> Self {
let t = Self::from(t);
a * (1. - t) + b * t
}
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!(f32, f32, std::f32::consts::PI);
impl_Interpolate!(f64, f64, std::f64::consts::PI);
impl_InterpolateT!(f32, f64, std::f32::consts::PI);

72
src/interpolation.rs Normal file
View File

@ -0,0 +1,72 @@
//! Available interpolation modes.
#[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(
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.
///
/// > Note: if you set the threshold to `0.5`, the first key will be used until half the time
/// > between the two keys; the second key will be in used afterwards. If you set it to `1.0`, the
/// > first key will be kept until the next key. Set it to `0.` and the first key will never be
/// > used.
///
/// [`Key`]: crate::key::Key
Step(T),
/// Linear interpolation between a key and the next one.
Linear,
/// Cosine interpolation between a key and the next one.
Cosine,
/// Catmull-Rom interpolation, performing a cubic Hermite interpolation using four keys.
CatmullRom,
/// Bézier interpolation.
///
/// A control point that uses such an interpolation is associated with an extra point. The segmant
/// connecting both is called the _tangent_ of this point. The part of the spline defined between
/// this control point and the next one will be interpolated across with Bézier interpolation. Two
/// cases are possible:
///
/// - The next control point also has a Bézier interpolation mode. In this case, its tangent is
/// used for the interpolation process. This is called _cubic Bézier interpolation_ and it
/// kicks ass.
/// - The next control point doesnt have a Bézier interpolation mode set. In this case, the
/// tangent used for the next control point is defined as the segment connecting that control
/// point and the current control 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),
}
impl<T, V> Default for Interpolation<T, V> {
/// [`Interpolation::Linear`] is the default.
fn default() -> Self {
Interpolation::Linear
}
}

44
src/iter.rs Normal file
View File

@ -0,0 +1,44 @@
//! Spline [`Iterator`], in a nutshell.
//!
//! You can iterate over a [`Spline<K, V>`]s keys with the [`IntoIterator`] trait on
//! `&Spline<K, V>`. This gives you iterated [`Key<K, V>`] keys.
//!
//! [`Spline<K, V>`]: crate::spline::Spline
//! [`Key<K, V>`]: crate::key::Key
use crate::{Key, Spline};
/// Iterator over spline keys.
///
/// This iterator type is guaranteed to iterate over sorted keys.
pub struct Iter<'a, T, V>
where
T: 'a,
V: 'a,
{
spline: &'a Spline<T, V>,
i: usize,
}
impl<'a, T, V> Iterator for Iter<'a, T, V> {
type Item = &'a Key<T, V>;
fn next(&mut self) -> Option<Self::Item> {
let r = self.spline.0.get(self.i);
if let Some(_) = r {
self.i += 1;
}
r
}
}
impl<'a, T, V> IntoIterator for &'a Spline<T, V> {
type Item = &'a Key<T, V>;
type IntoIter = Iter<'a, T, V>;
fn into_iter(self) -> Self::IntoIter {
Iter { spline: self, i: 0 }
}
}

44
src/key.rs Normal file
View File

@ -0,0 +1,44 @@
//! Spline control points.
//!
//! 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 its possible to change the
//! interpolation mode on a key-based way, allowing you to implement and encode complex curves.
use crate::interpolation::Interpolation;
#[cfg(any(feature = "serialization", feature = "serde"))]
use serde::{Deserialize, Serialize};
/// A spline control point.
///
/// This type associates a value at a given interpolation parameter value. It also contains an
/// interpolation mode used to determine how to interpolate values on the segment defined by this
/// key and the next one if existing. Have a look at [`Interpolation`] for further details.
///
/// [`Interpolation`]: crate::interpolation::Interpolation
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[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,
/// Carried value.
pub value: V,
/// Interpolation mode.
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,
}
}
}

View File

@ -33,11 +33,11 @@
//! # Interpolate values
//!
//! The whole purpose of splines is to interpolate discrete values to yield continuous ones. This is
//! usually done with the `Spline::sample` method. This method expects the interpolation parameter
//! usually done with the [`Spline::sample`] method. This method expects the sampling parameter
//! (often, this will be the time of your simulation) as argument and will yield an interpolated
//! value.
//!
//! If you try to sample in out-of-bounds interpolation parameter, youll get no value.
//! If you try to sample in out-of-bounds sampling parameter, youll get no value.
//!
//! ```
//! # use splines::{Interpolation, Key, Spline};
@ -45,7 +45,7 @@
//! # let end = Key::new(1., 10., Interpolation::Linear);
//! # let spline = Spline::from_vec(vec![start, end]);
//! assert_eq!(spline.sample(0.), Some(0.));
//! assert_eq!(spline.clamped_sample(1.), 10.);
//! assert_eq!(spline.clamped_sample(1.), Some(10.));
//! assert_eq!(spline.sample(1.1), None);
//! ```
//!
@ -58,13 +58,21 @@
//! # let start = Key::new(0., 0., Interpolation::Linear);
//! # let end = Key::new(1., 10., Interpolation::Linear);
//! # let spline = Spline::from_vec(vec![start, end]);
//! assert_eq!(spline.clamped_sample(-0.9), 0.); // clamped to the first key
//! assert_eq!(spline.clamped_sample(1.1), 10.); // clamped to the last key
//! 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
//! ```
//!
//! # Polymorphic sampling types
//!
//! [`Spline`] curves are parametered both by the carried value (being interpolated) but also the
//! sampling type. Its very typical to use `f32` or `f64` but really, you can in theory use any
//! kind of type; that type must, however, implement a contract defined by a set of traits to
//! implement. See [the documentation of this module](crate::interpolate) for further details.
//!
//! # Features and customization
//!
//! This crate was written with features baked in and hidden behind feature-gates. The idea is that
//! the default configuration (i.e. you just add `"spline = …"` to your `Cargo.toml`) will always
//! the default configuration (i.e. you just add `"splines = …"` to your `Cargo.toml`) will always
//! give you the minimal, core and raw concepts of what splines, keys / knots and interpolation
//! modes are. However, you might want more. Instead of letting other people do the extra work to
//! add implementations for very famous and useful traits and do it in less efficient way, because
@ -76,368 +84,58 @@
//!
//! So heres a list of currently supported features and how to enable them:
//!
//! - **Serialization / deserialization.**
//! + This feature implements both the `Serialize` and `Deserialize` traits from `serde` for all
//! - **Serde.**
//! - This feature implements both the `Serialize` and `Deserialize` traits from `serde` for all
//! types exported by this crate.
//! + Enable with the `"serialization"` feature.
//! - **[cgmath](https://crates.io/crates/cgmath) implementors
//! + Adds some usefull implementations of `Interpolate` for some cgmath types.
//! + Enable with the `"impl-cgmath"` feature.
//! - **Standard library / no stdandard library**
//! + Its possible to compile against the standard library or go on your own without it.
//! + Compiling with the standard library is enabled by default.
//! + Use `defaut-features = []` in your `Cargo.toml` to disable.
//! + Enable explicitly with the `"std"` feataure.
//! - 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 `"cgmath"` feature.
//! - **[glam](https://crates.io/crates/glam) implementors.**
//! - Adds some useful implementations of `Interpolate` for some glam types.
//! - 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 `"nalgebra"` feature.
//! - **Standard library / no standard library.**
//! - Its possible to compile against the standard library or go on your own without it.
//! - Compiling with the standard library is enabled by default.
//! - Use `default-features = []` in your `Cargo.toml` to disable.
//! - Enable explicitly with the `"std"` feature.
//!
//! [`Interpolation`]: crate::interpolation::Interpolation
#![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)"
)
)]
// on no_std, we also need the alloc crate for Vec
#[cfg(not(feature = "std"))] extern crate alloc;
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(feature = "impl-cgmath")] extern crate cgmath;
#[cfg(any(feature = "impl-cgmath", feature = "cgmath"))]
mod cgmath;
#[cfg(any(feature = "impl-glam", feature = "glam"))]
mod glam;
pub mod interpolate;
pub mod interpolation;
pub mod iter;
pub mod key;
#[cfg(any(feature = "impl-nalgebra", feature = "nalgebra"))]
mod nalgebra;
pub mod spline;
#[cfg(feature = "serialization")] extern crate serde;
#[cfg(feature = "serialization")] #[macro_use] extern crate serde_derive;
#[cfg(feature = "impl-cgmath")] use cgmath::{InnerSpace, Quaternion, Vector2, Vector3, Vector4};
#[cfg(feature = "std")] use std::cmp::Ordering;
#[cfg(feature = "std")] use std::f32::consts;
#[cfg(feature = "std")] use std::ops::{Add, Div, Mul, Sub};
#[cfg(not(feature = "std"))] use alloc::vec::Vec;
#[cfg(not(feature = "std"))] use core::cmp::Ordering;
#[cfg(not(feature = "std"))] use core::f32::consts;
#[cfg(not(feature = "std"))] use core::ops::{Add, Div, Mul, Sub};
/// A spline control point.
///
/// This type associates a value at a given interpolation parameter value. It also contains an
/// interpolation hint used to determine how to interpolate values on the segment defined by this
/// key and the next one if existing.
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serialization", serde(rename_all = "snake_case"))]
pub struct Key<T> {
/// Interpolation parameter at which the [`Key`] should be reached.
pub t: f32,
/// Held value.
pub value: T,
/// Interpolation mode.
pub interpolation: Interpolation
}
impl<T> Key<T> {
/// Create a new key.
pub fn new(t: f32, value: T, interpolation: Interpolation) -> Self {
Key {
t: t,
value: value,
interpolation: interpolation
}
}
}
/// Interpolation mode.
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serialization", serde(rename_all = "snake_case"))]
pub enum Interpolation {
/// Hold a [`Key`] until the time passes the normalized step threshold, in which case the next
/// key is used.
///
/// *Note: if you set the threshold to `0.5`, the first key will be used until the time is half
/// between the two keys; the second key will be in used afterwards. If you set it to `1.0`, the
/// first key will be kept until the next key. Set it to `0.` and the first key will never be
/// used.*
Step(f32),
/// Linear interpolation between a key and the next one.
Linear,
/// Cosine interpolation between a key and the next one.
Cosine,
/// Catmull-Rom interpolation.
CatmullRom
}
impl Default for Interpolation {
/// `Interpolation::Linear` is the default.
fn default() -> Self {
Interpolation::Linear
}
}
/// Spline curve used to provide interpolation between control points (keys).
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))]
pub struct Spline<T>(Vec<Key<T>>);
impl<T> Spline<T> {
/// 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(mut keys: Vec<Key<T>>) -> Self {
keys.sort_by(|k0, k1| k0.t.partial_cmp(&k1.t).unwrap_or(Ordering::Less));
Spline(keys)
}
/// Create a new spline by consuming an `Iterater<Item = Key<T>>`. They keys dont have to be
/// sorted.
///
/// # Note on iterators
///
/// 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<_>`. This will remove dynamic allocations.
pub fn from_iter<I>(iter: I) -> Self where I: Iterator<Item = Key<T>> {
Self::from_vec(iter.collect())
}
/// Retrieve the keys of a spline.
pub fn keys(&self) -> &[Key<T>] {
&self.0
}
/// Sample a spline at a given time.
///
/// 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)*
/// performance by using a slightly different spline type. If you are interested by this feature,
/// an implementation for a dedicated type is foreseen yet not started yet.
///
/// # Return
///
/// `None` if you try to sample a value at a time that has no key associated with. That can also
/// happen if you try to sample between two keys with a specific interpolation mode that make the
/// sampling impossible. For instance, `Interpolate::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: f32) -> Option<T> where T: Interpolate {
let keys = &self.0;
let i = search_lower_cp(keys, t)?;
let cp0 = &keys[i];
match cp0.interpolation {
Interpolation::Step(threshold) => {
let cp1 = &keys[i+1];
let nt = normalize_time(t, cp0, cp1);
Some(if nt < threshold { cp0.value } else { cp1.value })
},
Interpolation::Linear => {
let cp1 = &keys[i+1];
let nt = normalize_time(t, cp0, cp1);
Some(Interpolate::lerp(cp0.value, cp1.value, nt))
},
Interpolation::Cosine => {
let cp1 = &keys[i+1];
let nt = normalize_time(t, cp0, cp1);
let cos_nt = {
#[cfg(feature = "std")]
{
(1. - f32::cos(nt * consts::PI)) * 0.5
}
#[cfg(not(feature = "std"))]
{
use core::intrinsics::cosf32;
unsafe { (1. - cosf32(nt * consts::PI)) * 0.5 }
}
};
Some(Interpolate::lerp(cp0.value, cp1.value, cos_nt))
},
Interpolation::CatmullRom => {
// We need at least four points for Catmull Rom; ensure we have them, otherwise, return
// None.
if i == 0 || i >= keys.len() - 2 {
None
} else {
let cp1 = &keys[i+1];
let cpm0 = &keys[i-1];
let cpm1 = &keys[i+2];
let nt = normalize_time(t, cp0, cp1);
Some(Interpolate::cubic_hermite((cpm0.value, cpm0.t), (cp0.value, cp0.t), (cp1.value, cp1.t), (cpm1.value, cpm1.t), nt))
}
}
}
}
/// Sample a spline at a given time with clamping.
///
/// # Return
///
/// If you sample before the first key or after the last one, return the first key or the last
/// one, respectively. Otherwise, behave the same way as `Spline::sample`.
///
/// # Panic
///
/// This function panics if you have no key.
pub fn clamped_sample(&self, t: f32) -> T where T: Interpolate {
let first = self.0.first().unwrap();
let last = self.0.last().unwrap();
if t <= first.t {
return first.value;
} else if t >= last.t {
return last.value;
}
self.sample(t).unwrap()
}
}
/// Iterator over spline keys.
///
/// This iterator type assures you to iterate over sorted keys.
pub struct Iter<'a, T> where T: 'a {
anim_param: &'a Spline<T>,
i: usize
}
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a Key<T>;
fn next(&mut self) -> Option<Self::Item> {
let r = self.anim_param.0.get(self.i);
if let Some(_) = r {
self.i += 1;
}
r
}
}
impl<'a, T> IntoIterator for &'a Spline<T> {
type Item = &'a Key<T>;
type IntoIter = Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
Iter {
anim_param: self,
i: 0
}
}
}
/// Keys that can be interpolated in between. Implementing this trait is required to perform
/// sampling on splines.
pub trait Interpolate: Copy {
/// Linear interpolation.
fn lerp(a: Self, b: Self, t: f32) -> Self;
/// Cubic hermite interpolation.
///
/// Default to `Self::lerp`.
fn cubic_hermite(_: (Self, f32), a: (Self, f32), b: (Self, f32), _: (Self, f32), t: f32) -> Self {
Self::lerp(a.0, b.0, t)
}
}
impl Interpolate for f32 {
fn lerp(a: Self, b: Self, t: f32) -> Self {
a * (1. - t) + b * t
}
fn cubic_hermite(x: (Self, f32), a: (Self, f32), b: (Self, f32), y: (Self, f32), t: f32) -> Self {
cubic_hermite(x, a, b, y, t)
}
}
#[cfg(feature = "impl-cgmath")]
impl Interpolate for Vector2<f32> {
fn lerp(a: Self, b: Self, t: f32) -> Self {
a.lerp(b, t)
}
fn cubic_hermite(x: (Self, f32), a: (Self, f32), b: (Self, f32), y: (Self, f32), t: f32) -> Self {
cubic_hermite(x, a, b, y, t)
}
}
#[cfg(feature = "impl-cgmath")]
impl Interpolate for Vector3<f32> {
fn lerp(a: Self, b: Self, t: f32) -> Self {
a.lerp(b, t)
}
fn cubic_hermite(x: (Self, f32), a: (Self, f32), b: (Self, f32), y: (Self, f32), t: f32) -> Self {
cubic_hermite(x, a, b, y, t)
}
}
#[cfg(feature = "impl-cgmath")]
impl Interpolate for Vector4<f32> {
fn lerp(a: Self, b: Self, t: f32) -> Self {
a.lerp(b, t)
}
fn cubic_hermite(x: (Self, f32), a: (Self, f32), b: (Self, f32), y: (Self, f32), t: f32) -> Self {
cubic_hermite(x, a, b, y, t)
}
}
#[cfg(feature = "impl-cgmath")]
impl Interpolate for Quaternion<f32> {
fn lerp(a: Self, b: Self, t: f32) -> Self {
a.nlerp(b, t)
}
}
// Default implementation of Interpolate::cubic_hermit.
pub(crate) fn cubic_hermite<T>(x: (T, f32), a: (T, f32), b: (T, f32), y: (T, f32), t: f32) -> T
where T: Copy + Add<Output = T> + Sub<Output = T> + Mul<f32, Output = T> + Div<f32, Output = T> {
// time stuff
let t2 = t * t;
let t3 = t2 * t;
let two_t3 = 2. * t3;
let three_t2 = 3. * t2;
// tangents
let m0 = (b.0 - x.0) / (b.1 - x.1);
let m1 = (y.0 - a.0) / (y.1 - a.1);
a.0 * (two_t3 - three_t2 + 1.) + m0 * (t3 - 2. * t2 + t) + b.0 * (-two_t3 + three_t2) + m1 * (t3 - t2)
}
// Normalize a time ([0;1]) given two control points.
#[inline(always)]
pub(crate) fn normalize_time<T>(t: f32, cp: &Key<T>, cp1: &Key<T>) -> f32 {
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>(cps: &[Key<T>], t: f32) -> Option<usize> {
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
}
}
Some(i)
}
pub use crate::interpolate::Interpolate;
pub use crate::interpolation::Interpolation;
pub use crate::key::Key;
pub use crate::spline::Spline;

18
src/nalgebra.rs Normal file
View File

@ -0,0 +1,18 @@
use crate::impl_Interpolate;
use nalgebra::{Quaternion, Vector1, Vector2, Vector3, Vector4, Vector5, Vector6};
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);
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);

344
src/spline.rs Normal file
View File

@ -0,0 +1,344 @@
//! 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(any(feature = "serialization", feature = "serde"))]
use serde::{Deserialize, Serialize};
#[cfg(feature = "std")]
use std::cmp::Ordering;
/// Spline curve used to provide interpolation between control points (keys).
///
/// Splines are made out of control points ([`Key`]). When creating a [`Spline`] with
/// [`Spline::from_vec`] or [`Spline::from_iter`], the keys dont have to be sorted (they are sorted
/// automatically by the sampling value).
///
/// You can sample from a spline with several functions:
///
/// - [`Spline::sample`]: allows you to sample from a spline. If not enough keys are available
/// for the required interpolation mode, you get `None`.
/// - [`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(
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> {
/// Internal sort to ensure invariant of sorting keys is valid.
fn internal_sort(&mut self)
where
T: PartialOrd,
{
self
.0
.sort_by(|k0, k1| k0.t.partial_cmp(&k1.t).unwrap_or(Ordering::Less));
}
/// Create a new spline out of keys. The keys 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,
{
let mut spline = Spline(keys);
spline.internal_sort();
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 dont have to be
/// sorted.
///
/// # Note on iterators
///
/// 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,
{
Self::from_vec(iter.collect())
}
/// Retrieve the keys of a spline.
pub fn keys(&self) -> &[Key<T, V>] {
&self.0
}
/// Number of keys.
#[inline(always)]
pub fn len(&self) -> usize {
self.0.len()
}
/// Check whether the spline has no key.
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
/// 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)*
/// performance by using a slightly different spline type. If you are interested by this feature,
/// an implementation for a dedicated type is foreseen yet not started yet.
///
/// # Return
///
/// `None` if you try to sample a value at a time that has no key associated with. That can also
/// happen if you try to sample between two keys with a specific interpolation mode that makes the
/// sampling impossible. For instance, [`Interpolation::CatmullRom`] requires *four* keys. If
/// youre 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<SampledWithKey<V>>
where
T: Interpolator,
V: Interpolate<T>,
{
let keys = &self.0;
let i = search_lower_cp(keys, t)?;
let cp0 = &keys[i];
let value = match cp0.interpolation {
Interpolation::Step(threshold) => {
let cp1 = &keys[i + 1];
let nt = t.normalize(cp0.t, cp1.t);
let value = V::step(nt, threshold, cp0.value, cp1.value);
Some(value)
}
Interpolation::Linear => {
let cp1 = &keys[i + 1];
let nt = t.normalize(cp0.t, cp1.t);
let value = V::lerp(nt, cp0.value, cp1.value);
Some(value)
}
Interpolation::Cosine => {
let cp1 = &keys[i + 1];
let nt = t.normalize(cp0.t, cp1.t);
let value = V::cosine(nt, cp0.value, cp1.value);
Some(value)
}
Interpolation::CatmullRom => {
// We need at least four points for Catmull Rom; ensure we have them, otherwise, return
// None.
if i == 0 || i >= keys.len() - 2 {
None
} else {
let cp1 = &keys[i + 1];
let cpm0 = &keys[i - 1];
let cpm1 = &keys[i + 2];
let nt = 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)
}
}
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 = t.normalize(cp0.t, cp1.t);
let value = match cp1.interpolation {
Interpolation::Bezier(v) => V::cubic_bezier_mirrored(nt, cp0.value, u, v, cp1.value),
Interpolation::StrokeBezier(v, _) => V::cubic_bezier(nt, cp0.value, u, v, cp1.value),
_ => V::quadratic_bezier(nt, cp0.value, u, cp1.value),
};
Some(value)
}
};
value.map(|value| SampledWithKey { value, key: i })
}
/// Sample a spline at a given time.
///
pub fn sample(&self, t: T) -> Option<V>
where
T: Interpolator,
V: Interpolate<T>,
{
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
/// associated key.
///
/// # Return
///
/// If you sample before the first key or after the last one, return the first key or the last
/// one, respectively. Otherwise, behave the same way as [`Spline::sample`].
///
/// # Error
///
/// This function returns [`None`] if you have no key.
pub fn clamped_sample_with_key(&self, t: T) -> Option<SampledWithKey<V>>
where
T: Interpolator,
V: Interpolate<T>,
{
if self.0.is_empty() {
return None;
}
self.sample_with_key(t).or_else(move || {
let first = self.0.first().unwrap();
if t <= first.t {
let sampled = SampledWithKey {
value: first.value,
key: 0,
};
Some(sampled)
} else {
let last = self.0.last().unwrap();
if t >= last.t {
let sampled = SampledWithKey {
value: last.value,
key: self.0.len() - 1,
};
Some(sampled)
} else {
None
}
}
})
}
/// Sample a spline at a given time with clamping.
pub fn clamped_sample(&self, t: T) -> Option<V>
where
T: Interpolator,
V: Interpolate<T>,
{
self.clamped_sample_with_key(t).map(|sampled| sampled.value)
}
/// Add a key into the spline.
pub fn add(&mut self, key: Key<T, V>)
where
T: PartialOrd,
{
self.0.push(key);
self.internal_sort();
}
/// Remove a key from the spline.
pub fn remove(&mut self, index: usize) -> Option<Key<T, V>> {
if index >= self.0.len() {
None
} else {
Some(self.0.remove(index))
}
}
/// Update a key and return the key already present.
///
/// The key is updated — if present — with the provided function.
///
/// # Notes
///
/// That function makes sense only if you want to change the interpolator (i.e. [`Key::t`]) of
/// your key. If you just want to change the interpolation mode or the carried value, consider
/// using the [`Spline::get_mut`] method instead as it will be way faster.
pub fn replace<F>(&mut self, index: usize, f: F) -> Option<Key<T, V>>
where
F: FnOnce(&Key<T, V>) -> Key<T, V>,
T: PartialOrd,
{
let key = self.remove(index)?;
self.add(f(&key));
Some(key)
}
/// Get a key at a given index.
pub fn get(&self, index: usize) -> Option<&Key<T, V>> {
self.0.get(index)
}
/// Mutably get a key at a given index.
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,
})
}
}
/// 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, youre advised to use [`Spline::replace`].
#[derive(Debug)]
pub struct KeyMut<'a, T, V> {
/// Carried value.
pub value: &'a mut V,
/// Interpolation mode to use for that key.
pub interpolation: &'a mut Interpolation<T, V>,
}
// 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 len = cps.len();
if len < 2 {
return None;
}
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),
}
}

43
tests/cgmath.rs Normal file
View 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.));
}

207
tests/integ.rs Normal file
View File

@ -0,0 +1,207 @@
use splines::{spline::SampledWithKey, Interpolation, Key, Spline};
#[test]
fn step_interpolation_f32() {
let start = Key::new(0., 0., Interpolation::Step(0.));
let end = Key::new(1., 10., Interpolation::default());
let spline = Spline::<f32, _>::from_vec(vec![start, end]);
assert_eq!(spline.sample(0.), Some(10.));
assert_eq!(spline.sample(0.1), Some(10.));
assert_eq!(spline.sample(0.2), Some(10.));
assert_eq!(spline.sample(0.5), Some(10.));
assert_eq!(spline.sample(0.9), Some(10.));
assert_eq!(spline.sample(1.), None);
assert_eq!(spline.clamped_sample(1.), Some(10.));
assert_eq!(
spline.sample_with_key(0.2),
Some(SampledWithKey { value: 10., key: 0 })
);
assert_eq!(
spline.clamped_sample_with_key(1.),
Some(SampledWithKey { value: 10., key: 1 })
);
}
#[test]
fn step_interpolation_f64() {
let start = Key::new(0., 0., Interpolation::Step(0.));
let end = Key::new(1., 10., Interpolation::default());
let spline = Spline::<f64, _>::from_vec(vec![start, end]);
assert_eq!(spline.sample(0.), Some(10.));
assert_eq!(spline.sample(0.1), Some(10.));
assert_eq!(spline.sample(0.2), Some(10.));
assert_eq!(spline.sample(0.5), Some(10.));
assert_eq!(spline.sample(0.9), Some(10.));
assert_eq!(spline.sample(1.), None);
assert_eq!(spline.clamped_sample(1.), Some(10.));
assert_eq!(
spline.sample_with_key(0.2),
Some(SampledWithKey { value: 10., key: 0 })
);
assert_eq!(
spline.clamped_sample_with_key(1.),
Some(SampledWithKey { value: 10., key: 1 })
);
}
#[test]
fn step_interpolation_0_5() {
let start = Key::new(0., 0., Interpolation::Step(0.5));
let end = Key::new(1., 10., Interpolation::default());
let spline = Spline::from_vec(vec![start, end]);
assert_eq!(spline.sample(0.), Some(0.));
assert_eq!(spline.sample(0.1), Some(0.));
assert_eq!(spline.sample(0.2), Some(0.));
assert_eq!(spline.sample(0.5), Some(10.));
assert_eq!(spline.sample(0.9), Some(10.));
assert_eq!(spline.sample(1.), None);
assert_eq!(spline.clamped_sample(1.), Some(10.));
}
#[test]
fn step_interpolation_0_75() {
let start = Key::new(0., 0., Interpolation::Step(0.75));
let end = Key::new(1., 10., Interpolation::default());
let spline = Spline::from_vec(vec![start, end]);
assert_eq!(spline.sample(0.), Some(0.));
assert_eq!(spline.sample(0.1), Some(0.));
assert_eq!(spline.sample(0.2), Some(0.));
assert_eq!(spline.sample(0.5), Some(0.));
assert_eq!(spline.sample(0.9), Some(10.));
assert_eq!(spline.sample(1.), None);
assert_eq!(spline.clamped_sample(1.), Some(10.));
}
#[test]
fn step_interpolation_1() {
let start = Key::new(0., 0., Interpolation::Step(1.));
let end = Key::new(1., 10., Interpolation::default());
let spline = Spline::from_vec(vec![start, end]);
assert_eq!(spline.sample(0.), Some(0.));
assert_eq!(spline.sample(0.1), Some(0.));
assert_eq!(spline.sample(0.2), Some(0.));
assert_eq!(spline.sample(0.5), Some(0.));
assert_eq!(spline.sample(0.9), Some(0.));
assert_eq!(spline.sample(1.), None);
assert_eq!(spline.clamped_sample(1.), Some(10.));
}
#[test]
fn linear_interpolation() {
let start = Key::new(0., 0., Interpolation::Linear);
let end = Key::new(1., 10., Interpolation::default());
let spline = Spline::from_vec(vec![start, end]);
assert_eq!(spline.sample(0.), Some(0.));
assert_eq!(spline.sample(0.1), Some(1.));
assert_eq!(spline.sample(0.2), Some(2.));
assert_eq!(spline.sample(0.5), Some(5.));
assert_eq!(spline.sample(0.9), Some(9.));
assert_eq!(spline.sample(1.), None);
assert_eq!(spline.clamped_sample(1.), Some(10.));
}
#[test]
fn linear_interpolation_several_keys() {
let start = Key::new(0., 0., Interpolation::Linear);
let k1 = Key::new(1., 5., Interpolation::Linear);
let k2 = Key::new(2., 0., Interpolation::Linear);
let k3 = Key::new(3., 1., Interpolation::Linear);
let k4 = Key::new(10., 2., Interpolation::Linear);
let end = Key::new(11., 4., Interpolation::default());
let spline = Spline::from_vec(vec![start, k1, k2, k3, k4, end]);
assert_eq!(spline.sample(0.), Some(0.));
assert_eq!(spline.sample(0.1), Some(0.5));
assert_eq!(spline.sample(0.2), Some(1.));
assert_eq!(spline.sample(0.5), Some(2.5));
assert_eq!(spline.sample(0.9), Some(4.5));
assert_eq!(spline.sample(1.), Some(5.));
assert_eq!(spline.sample(1.5), Some(2.5));
assert_eq!(spline.sample(2.), Some(0.));
assert_eq!(spline.sample(2.75), Some(0.75));
assert_eq!(spline.sample(3.), Some(1.));
assert_eq!(spline.sample(6.5), Some(1.5));
assert_eq!(spline.sample(10.), Some(2.));
assert_eq!(spline.clamped_sample(11.), Some(4.));
}
#[test]
fn several_interpolations_several_keys() {
let start = Key::new(0., 0., Interpolation::Step(0.5));
let k1 = Key::new(1., 5., Interpolation::Linear);
let k2 = Key::new(2., 0., Interpolation::Step(0.1));
let k3 = Key::new(3., 1., Interpolation::Linear);
let k4 = Key::new(10., 2., Interpolation::Linear);
let end = Key::new(11., 4., Interpolation::default());
let spline = Spline::from_vec(vec![start, k1, k2, k3, k4, end]);
assert_eq!(spline.sample(0.), Some(0.));
assert_eq!(spline.sample(0.1), Some(0.));
assert_eq!(spline.sample(0.2), Some(0.));
assert_eq!(spline.sample(0.5), Some(5.));
assert_eq!(spline.sample(0.9), Some(5.));
assert_eq!(spline.sample(1.), Some(5.));
assert_eq!(spline.sample(1.5), Some(2.5));
assert_eq!(spline.sample(2.), Some(0.));
assert_eq!(spline.sample(2.05), Some(0.));
assert_eq!(spline.sample(2.099), Some(0.));
assert_eq!(spline.sample(2.75), Some(1.));
assert_eq!(spline.sample(3.), Some(1.));
assert_eq!(spline.sample(6.5), Some(1.5));
assert_eq!(spline.sample(10.), Some(2.));
assert_eq!(spline.clamped_sample(11.), Some(4.));
}
#[test]
fn add_key_empty() {
let mut spline: Spline<f32, f32> = Spline::from_vec(vec![]);
spline.add(Key::new(0., 0., Interpolation::Linear));
assert_eq!(spline.keys(), &[Key::new(0., 0., Interpolation::Linear)]);
}
#[test]
fn add_key() {
let start = Key::new(0., 0., Interpolation::Step(0.5));
let k1 = Key::new(1., 5., Interpolation::Linear);
let k2 = Key::new(2., 0., Interpolation::Step(0.1));
let k3 = Key::new(3., 1., Interpolation::Linear);
let k4 = Key::new(10., 2., Interpolation::Linear);
let end = Key::new(11., 4., Interpolation::default());
let new = Key::new(2.4, 40., Interpolation::Linear);
let mut spline = Spline::from_vec(vec![start, k1, k2.clone(), k3, k4, end]);
assert_eq!(spline.keys(), &[start, k1, k2, k3, k4, end]);
spline.add(new);
assert_eq!(spline.keys(), &[start, k1, k2, new, k3, k4, end]);
}
#[test]
fn remove_element_empty() {
let mut spline: Spline<f32, f32> = Spline::from_vec(vec![]);
let removed = spline.remove(0);
assert_eq!(removed, None);
assert!(spline.is_empty());
}
#[test]
fn remove_element() {
let start = Key::new(0., 0., Interpolation::Step(0.5));
let k1 = Key::new(1., 5., Interpolation::Linear);
let k2 = Key::new(2., 0., Interpolation::Step(0.1));
let k3 = Key::new(3., 1., Interpolation::Linear);
let k4 = Key::new(10., 2., Interpolation::Linear);
let end = Key::new(11., 4., Interpolation::default());
let mut spline = Spline::from_vec(vec![start, k1, k2.clone(), k3, k4, end]);
let removed = spline.remove(2);
assert_eq!(removed, Some(k2));
assert_eq!(spline.len(), 5);
}

View File

@ -1,130 +0,0 @@
extern crate splines;
use splines::{Interpolation, Key, Spline};
#[test]
fn step_interpolation_0() {
let start = Key::new(0., 0., Interpolation::Step(0.));
let end = Key::new(1., 10., Interpolation::default());
let spline = Spline::from_vec(vec![start, end]);
assert_eq!(spline.sample(0.), Some(10.));
assert_eq!(spline.sample(0.1), Some(10.));
assert_eq!(spline.sample(0.2), Some(10.));
assert_eq!(spline.sample(0.5), Some(10.));
assert_eq!(spline.sample(0.9), Some(10.));
assert_eq!(spline.sample(1.), None);
assert_eq!(spline.clamped_sample(1.), 10.);
}
#[test]
fn step_interpolation_0_5() {
let start = Key::new(0., 0., Interpolation::Step(0.5));
let end = Key::new(1., 10., Interpolation::default());
let spline = Spline::from_vec(vec![start, end]);
assert_eq!(spline.sample(0.), Some(0.));
assert_eq!(spline.sample(0.1), Some(0.));
assert_eq!(spline.sample(0.2), Some(0.));
assert_eq!(spline.sample(0.5), Some(10.));
assert_eq!(spline.sample(0.9), Some(10.));
assert_eq!(spline.sample(1.), None);
assert_eq!(spline.clamped_sample(1.), 10.);
}
#[test]
fn step_interpolation_0_75() {
let start = Key::new(0., 0., Interpolation::Step(0.75));
let end = Key::new(1., 10., Interpolation::default());
let spline = Spline::from_vec(vec![start, end]);
assert_eq!(spline.sample(0.), Some(0.));
assert_eq!(spline.sample(0.1), Some(0.));
assert_eq!(spline.sample(0.2), Some(0.));
assert_eq!(spline.sample(0.5), Some(0.));
assert_eq!(spline.sample(0.9), Some(10.));
assert_eq!(spline.sample(1.), None);
assert_eq!(spline.clamped_sample(1.), 10.);
}
#[test]
fn step_interpolation_1() {
let start = Key::new(0., 0., Interpolation::Step(1.));
let end = Key::new(1., 10., Interpolation::default());
let spline = Spline::from_vec(vec![start, end]);
assert_eq!(spline.sample(0.), Some(0.));
assert_eq!(spline.sample(0.1), Some(0.));
assert_eq!(spline.sample(0.2), Some(0.));
assert_eq!(spline.sample(0.5), Some(0.));
assert_eq!(spline.sample(0.9), Some(0.));
assert_eq!(spline.sample(1.), None);
assert_eq!(spline.clamped_sample(1.), 10.);
}
#[test]
fn linear_interpolation() {
let start = Key::new(0., 0., Interpolation::Linear);
let end = Key::new(1., 10., Interpolation::default());
let spline = Spline::from_vec(vec![start, end]);
assert_eq!(spline.sample(0.), Some(0.));
assert_eq!(spline.sample(0.1), Some(1.));
assert_eq!(spline.sample(0.2), Some(2.));
assert_eq!(spline.sample(0.5), Some(5.));
assert_eq!(spline.sample(0.9), Some(9.));
assert_eq!(spline.sample(1.), None);
assert_eq!(spline.clamped_sample(1.), 10.);
}
#[test]
fn linear_interpolation_several_keys() {
let start = Key::new(0., 0., Interpolation::Linear);
let k1 = Key::new(1., 5., Interpolation::Linear);
let k2 = Key::new(2., 0., Interpolation::Linear);
let k3 = Key::new(3., 1., Interpolation::Linear);
let k4 = Key::new(10., 2., Interpolation::Linear);
let end = Key::new(11., 4., Interpolation::default());
let spline = Spline::from_vec(vec![start, k1, k2, k3, k4, end]);
assert_eq!(spline.sample(0.), Some(0.));
assert_eq!(spline.sample(0.1), Some(0.5));
assert_eq!(spline.sample(0.2), Some(1.));
assert_eq!(spline.sample(0.5), Some(2.5));
assert_eq!(spline.sample(0.9), Some(4.5));
assert_eq!(spline.sample(1.), Some(5.));
assert_eq!(spline.sample(1.5), Some(2.5));
assert_eq!(spline.sample(2.), Some(0.));
assert_eq!(spline.sample(2.75), Some(0.75));
assert_eq!(spline.sample(3.), Some(1.));
assert_eq!(spline.sample(6.5), Some(1.5));
assert_eq!(spline.sample(10.), Some(2.));
assert_eq!(spline.clamped_sample(11.), 4.);
}
#[test]
fn several_interpolations_several_keys() {
let start = Key::new(0., 0., Interpolation::Step(0.5));
let k1 = Key::new(1., 5., Interpolation::Linear);
let k2 = Key::new(2., 0., Interpolation::Step(0.1));
let k3 = Key::new(3., 1., Interpolation::Linear);
let k4 = Key::new(10., 2., Interpolation::Linear);
let end = Key::new(11., 4., Interpolation::default());
let spline = Spline::from_vec(vec![start, k1, k2, k3, k4, end]);
assert_eq!(spline.sample(0.), Some(0.));
assert_eq!(spline.sample(0.1), Some(0.));
assert_eq!(spline.sample(0.2), Some(0.));
assert_eq!(spline.sample(0.5), Some(5.));
assert_eq!(spline.sample(0.9), Some(5.));
assert_eq!(spline.sample(1.), Some(5.));
assert_eq!(spline.sample(1.5), Some(2.5));
assert_eq!(spline.sample(2.), Some(0.));
assert_eq!(spline.sample(2.05), Some(0.));
assert_eq!(spline.sample(2.1), Some(0.));
assert_eq!(spline.sample(2.75), Some(1.));
assert_eq!(spline.sample(3.), Some(1.));
assert_eq!(spline.sample(6.5), Some(1.5));
assert_eq!(spline.sample(10.), Some(2.));
assert_eq!(spline.clamped_sample(11.), 4.);
}

16
tests/nalgebra.rs Normal file
View 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);
}