Compare commits

...

139 Commits
2.0 ... master

Author SHA1 Message Date
35881a8122 fix no_std 2024-01-05 15:50:26 +01:00
Dimitri Sabadie
f6fa4ecbce
Merge pull request #102 from phaazon/release/4.3.1
Prepare v4.3.1.
2023-11-22 19:13:05 +01:00
Dimitri Sabadie
e2f220ec15
Prepare v4.3.1. 2023-11-22 19:10:19 +01:00
Dimitri Sabadie
c3670d6b0a
Merge pull request #100 from perlindgren/master
Default for Spline
2023-11-22 00:09:57 +01:00
Per Lindgren
c6ba8476f2 Default for Spline 2023-10-27 21:20:24 +02:00
Dimitri Sabadie
389f4d182d
Update changelog (links). 2023-09-23 16:25:54 +02:00
Dimitri Sabadie
8af9151dac
Changelog fix. 2023-09-23 16:10:14 +02:00
Dimitri Sabadie
a82cf85619
Merge pull request #99 from phaazon/release/v4.3.0
Prepare v4.3.0.
2023-09-23 15:57:48 +02:00
Dimitri Sabadie
37d3cc5f29
Prepare v4.3.0. 2023-09-23 15:54:10 +02:00
Dimitri Sabadie
1e70ab882c
Rustfmt. 2023-09-23 15:40:58 +02:00
Dimitri Sabadie
2179c8300f
Merge pull request #98 from EmbarkStudios/master
Add `clear` method to `Spline`
2023-09-23 15:40:26 +02:00
Dimitri Sabadie
e7a9723ae0
Fix rustfmt. 2023-09-23 13:01:18 +02:00
Gray Olson
eca09f1baf add clear method to Spline 2023-09-13 15:52:43 +02:00
Dimitri Sabadie
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
dependabot[bot]
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
Dimitri Sabadie
decd85dba2
Merge pull request #95 from phaazon/update-deps
Bump dependencies (cgmath, nalgebra) to latest.
2023-02-01 19:41:51 +01:00
Dimitri Sabadie
df9815a464
Add deprecation lints if using impl-* feature gates. 2023-02-01 19:29:31 +01:00
Dimitri Sabadie
ca8e797932
Prepare version 4.2.0. 2023-02-01 19:19:45 +01:00
Dimitri Sabadie
5b746aaf57
Bump dependencies (cgmath, nalgebra) to latest. 2023-02-01 19:12:11 +01:00
Dimitri Sabadie
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
Dimitri Sabadie
d9770ad60b
Merge pull request #90 from phaazon/release/4.1.1
Release/4.1.1
2022-07-27 00:49:00 +02:00
Dimitri Sabadie
3dfea81856 Update changelog. 2022-07-27 00:42:42 +02:00
Dimitri Sabadie
604dcc6e27 Increment version number. 2022-07-27 00:42:30 +02:00
Dimitri Sabadie
8c952ae242
Merge pull request #86 from hokwangchoi/master
Fix cubic_hermite interpolation
2022-07-27 00:42:23 +02:00
Dimitri Sabadie
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
dependabot[bot]
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
Dimitri Sabadie
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
Dimitri Sabadie
295043e5af rustfmt 2022-07-27 00:08:26 +02:00
Dimitri Sabadie
1c249215c9
Merge pull request #89 from timethy/interpolation-made-fast
Interpolation made fast
2022-07-27 00:02:17 +02:00
Tim Taubner
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
dependabot[bot]
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
Hokwang Choi
f25ebb2c64 remove unfinished test 2022-06-07 12:50:18 -04:00
Hokwang Choi
e9c1de389f scale tangent 2022-06-07 12:47:17 -04:00
Hokwang Choi
bdeaefd9f9 fix cubic_hermite coefficients & powers 2022-06-07 12:01:25 -04:00
Dimitri Sabadie
ace0f4ec50
Merge pull request #84 from phaazon/release/4.1
4.1.0.
2022-03-28 11:43:48 +02:00
Dimitri Sabadie
b056a4e9a7
4.1.0. 2022-03-28 11:38:25 +02:00
Dimitri Sabadie
042253ab9c
Merge pull request #83 from phaazon/nalgebra-update
Bump nalgebra dependency.
2022-03-25 09:14:31 +01:00
Dimitri Sabadie
270f225394
Bump nalgebra dependency. 2022-03-24 17:09:20 +01:00
Dimitri Sabadie
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
Dimitri Sabadie
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
Dimitri Sabadie
f4c8be33b9
Merge pull request #82 from phaazon/edition-2021
Edition 2021
2022-03-22 20:20:01 +01:00
Dimitri Sabadie
26bd5c88eb
Update and simplify the CI. 2022-03-22 16:15:39 +01:00
Dimitri Sabadie
a4cd49fd20
Switch to edition 2021. 2022-03-22 16:14:28 +01:00
dependabot[bot]
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
dependabot[bot]
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
Dimitri Sabadie
fc2f53200f
Fix previous commit (new implementors). 2021-07-11 17:46:59 +02:00
Dimitri Sabadie
b3836975c3
Add more implementors for Interpolate. 2021-07-11 17:44:05 +02:00
Dimitri Sabadie
322d271499
Merge pull request #73 from phaazon/next/release
Prepare 4.0.1.
2021-07-11 16:54:24 +02:00
Dimitri Sabadie
e64298dc88
Prepare 4.0.1. 2021-07-11 16:50:28 +02:00
Dimitri Sabadie
32e5122339
Merge pull request #72 from phaazon/update-dependencies
Update all dependencies to latest version.
2021-07-11 16:47:34 +02:00
Dimitri Sabadie
1be94935cf
Update all dependencies to latest version. 2021-07-11 16:27:24 +02:00
Dimitri Sabadie
06f6e4b578
Merge pull request #71 from phaazon/better-feature-gates
Provide better feature gates.
2021-07-11 16:25:45 +02:00
Dimitri Sabadie
87e27e732d
Synchronize README. 2021-07-11 16:22:42 +02:00
Dimitri Sabadie
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
Dimitri Sabadie
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
Dimitri Sabadie
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
dependabot-preview[bot]
25d5c5217e
Upgrade to GitHub-native Dependabot 2021-04-29 15:36:58 +00:00
dependabot-preview[bot]
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
Dimitri Sabadie
b92c28cfbb
Merge pull request #63 from phaazon/refactor
Refactor
2021-03-05 02:50:14 +01:00
Dimitri Sabadie
695caf0cca
Prepare 4.0. 2021-03-05 02:44:49 +01:00
Dimitri Sabadie
3e85a1f026
Update and fix implementors for the new API. 2021-03-05 02:05:36 +01:00
Dimitri Sabadie
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
Dimitri Sabadie
3d43e4c644
Code hygiene. 2021-02-28 19:02:54 +01:00
Dimitri Sabadie
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
Dimitri Sabadie
80fb6fbe28
Tyyyyyyyyypo. 2021-02-27 23:58:01 +01:00
Dimitri Sabadie
cc3ac349b4
Cleanup integration tests. 2021-02-27 23:52:51 +01:00
Dimitri Sabadie
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
Dimitri Sabadie
469a785767
Code hygiene. 2021-02-27 23:09:50 +01:00
Dimitri Sabadie
dd7ae34670
Remove __NonExhaustive and replace with #[non_exhaustive]. 2021-02-27 23:09:19 +01:00
dependabot-preview[bot]
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
Dimitri Sabadie
e1998fda56
Merge pull request #59 from phaazon/release/3.5.4
Prepare 3.5.4.
2021-02-27 22:51:25 +01:00
Dimitri Sabadie
e7c3003dcf
Prepare 3.5.4. 2021-02-27 22:42:12 +01:00
Dimitri Sabadie
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
dependabot-preview[bot]
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
Dimitri Sabadie
c0d9a0b540
Prepare 3.5.3. 2021-01-16 17:04:27 +01:00
Dimitri Sabadie
4f0bf51b5c
Fix README. 2021-01-16 17:03:28 +01:00
Dimitri Sabadie
cc2b9c75a0
Prepare 3.5.2. 2021-01-01 16:59:52 +01:00
Dimitri Sabadie
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
dependabot-preview[bot]
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
Dimitri Sabadie
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
Dimitri Sabadie
73df77380b
Prepare 3.5.1. 2020-12-05 16:34:56 +01:00
dependabot-preview[bot]
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
Dimitri Sabadie
bfb1cc14bb
Merge pull request #53 from phaazon/release/3.5
Release/3.5
2020-11-23 22:38:09 +01:00
Dimitri Sabadie
5836f778f4
Prepare 3.5. 2020-11-23 22:29:22 +01:00
Dimitri Sabadie
7b5c08d9fa
Add support for nalgebra-0.23. 2020-11-23 22:29:22 +01:00
Dimitri Sabadie
fe78bf2eb6
Merge pull request #50 from iwikal/glam
Add impl-glam feature
2020-11-23 22:20:50 +01:00
Joel Nordström
4b3a06d66e Update glam 2020-11-02 04:04:06 +01:00
Joel Nordström
c50a9274a5 glam::Quat::lerp normalizes internally 2020-10-25 22:11:25 +01:00
Joel Nordström
3ffe6106ec Add impl-glam feature 2020-10-25 22:00:13 +01:00
Dimitri Sabadie
45244628ac
Prepare 3.4.2. 2020-10-24 23:44:25 +02:00
Dimitri Sabadie
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
dependabot-preview[bot]
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
Dimitri Sabadie
05a3862e30
Prepare 3.4.1. 2020-09-05 15:25:16 +02:00
Dimitri Sabadie
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
Dimitri Sabadie
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
Dimitri Sabadie
209d4fc7c5
Add support for nalgebra-0.22. 2020-09-05 15:16:33 +02:00
Dimitri Sabadie
51769e1b12
Add support of simba-0.2. 2020-09-05 15:15:28 +02:00
dependabot-preview[bot]
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
dependabot-preview[bot]
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
Dimitri Sabadie
ebfc15d8af
Prepare 3.4. 2020-05-21 20:11:36 +02:00
Dimitri Sabadie
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
Dimitri Sabadie
8f7cc9e711
Bump float-cmp upper bound to accept float-cmp-0.8. 2020-05-21 19:59:48 +02:00
dependabot-preview[bot]
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
Dimitri Sabadie
0afebc3319
Synchronize README. 2020-04-29 03:27:10 +02:00
Dimitri Sabadie
4a2f349954
Prepare 3.3.0. 2020-04-09 23:51:07 +02:00
Dimitri Sabadie
85ac489636
Add the dependabot setup. 2020-04-09 22:39:50 +02:00
Dimitri Sabadie
aea9011296
Merge pull request #41 from alexbool/update-nalgebra-0.21
bump nalgebra
2020-04-09 22:15:50 +02:00
Alexander Bulaev
04247d8706 bump nalgebra 2020-04-06 17:17:40 +03:00
Dimitri Sabadie
0fcdbacaf3
Prepare version 3.2. 2020-03-19 01:36:13 +01:00
Dimitri Sabadie
89dfb61272
Add rustfmt.toml and reformat. 2020-03-19 01:22:26 +01:00
Dimitri Sabadie
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
Alexander Bulaev
4630f44d6c run rustfmt 2020-03-18 13:59:22 +03:00
Alexander Bulaev
efe9272816 minor: unused warning 2020-03-17 13:07:02 +03:00
Alexander Bulaev
036d7df3eb fix Copy trait bound with nalgebra 0.20 2020-03-17 13:06:41 +03:00
Dimitri Sabadie
a33dbf9fde
Activate CI for PRs, too. 2020-03-16 16:54:43 +01:00
Dimitri Sabadie
dfa1e6a745
Update the CI steps. 2020-03-16 16:45:33 +01:00
Dimitri Sabadie
f04ea0fefa
Revert "Merge pull request #39 from alexbool/update-nalgebra-0.20"
This reverts commit 8ceb8d768c1a425886aabefca6aead4baf9757c1, reversing
changes made to d80de42d2fa8996c39d30acfd9419560c8fa6df3.
2020-03-16 16:43:20 +01:00
Dimitri Sabadie
8ceb8d768c
Merge pull request #39 from alexbool/update-nalgebra-0.20
update nalgebra version
2020-03-16 16:34:12 +01:00
Alexander Bulaev
c93109e28b update nalgebra version 2020-03-14 15:28:14 +03:00
Dimitri Sabadie
d80de42d2f
Prepare 3.1.0. 2020-01-26 21:19:25 +01:00
Dimitri Sabadie
2e6a5a0dfb
Merge pull request #38 from alexbool/update-nalgebra
update nalgebra
2020-01-26 21:18:30 +01:00
Alexander Bulaev
62147d5348 update nalgebra 2020-01-26 22:42:05 +03:00
Dimitri Sabadie
2dfc11c908
Fix CHANGELOG entry date. 2019-10-22 21:43:50 +02:00
Dimitri Sabadie
0c23df7bf0
Merge pull request #35 from phaazon/fix/bézier
Fix Bézier interpolation.
2019-10-22 21:09:15 +02:00
Dimitri Sabadie
3b6ddc5ea6
Update integration tests for stroke Bézier. 2019-10-22 20:59:46 +02:00
Dimitri Sabadie
824afef513
Fix Bézier interpolation. 2019-10-22 20:23:36 +02:00
Dimitri Sabadie
f2b356b78d
Working on tests. 2019-10-22 18:13:51 +02:00
Dimitri Sabadie
955050ecee
Fix examples. 2019-10-22 13:34:11 +02:00
Dimitri Sabadie
22e75c6901
Fix Bézier interpolation. 2019-10-22 13:34:10 +02:00
Dimitri Sabadie
425433cd5b
Merge pull request #33 from phaazon/feature/stroke-bezier
Add Interpolation::StrokeBezier.
2019-10-17 17:26:16 +02:00
Dimitri Sabadie
cc0a9580ab
Add Interpolation::StrokeBezier. 2019-10-17 17:23:46 +02:00
Dimitri Sabadie
05e131baad
2.1.1 2019-10-17 01:49:34 +02:00
Dimitri Sabadie
0a15fb48a3
Add missing LICENSE file. 2019-10-17 01:45:53 +02:00
Dimitri Sabadie
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
Dimitri Sabadie
cae599e0d7
Add Spline::sample_with_key and Spline::clamped_sample_with_key. 2019-09-30 12:49:36 +02:00
Dimitri Sabadie
336c1c7e80
Merge pull request #31 from phaazon/fix/bezier-interpolation
Fix/bezier interpolation
2019-09-24 21:36:04 +02:00
Dimitri Sabadie
ea29e08836
Fix cubic Bézier interpolation. 2019-09-24 21:31:18 +02:00
Dimitri Sabadie
3ab98420c8
Remove unneeded comments. 2019-09-24 17:40:01 +02:00
24 changed files with 887 additions and 600 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

View File

@ -1,46 +1,38 @@
name: CI
on: [push]
on: [push, pull_request]
jobs:
build-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Build
run: |
cargo build --verbose --all-features
- name: Test
run: |
cargo test --verbose --all-features
run: cargo test --verbose --all-features
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
- name: Build
run: |
cargo build --verbose --all-features
- name: Test
run: |
cargo test --verbose --all-features
run: cargo test --verbose --all-features
build-macosx:
runs-on: macosx-latest
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1
- name: Build
run: |
cargo build --verbose --all-features
- name: Test
run: |
cargo test --verbose --all-features
run: cargo test --verbose --all-features
check-readme:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Install cargo-sync-readme
run: cargo install --force cargo-sync-readme
- name: Check
run: cargo sync-readme -c
- name: Install dependencies
run: |
cargo install --force cargo-sync-readme
rustup component add rustfmt
- name: cargo sync-readme
run: |
cargo sync-readme -c
- name: rustfmt
run: cargo fmt -- --check

View File

@ -1,6 +1,249 @@
# 2.0.0
# Changelog
> Mon Sep 24th 2019
* [4.3.1](#431)
* [4.3](#43)
* [4.2](#42)
* [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.3.1
> Nov 22, 2023
- Add `Default` implementation for `Spline`. [c6ba847](https://github.com/phaazon/splines/commit/c6ba847)
# 4.3
> Sep 23, 2023
- Add support for `glam-0.23` and `glam-0.24`. [cdc48a4](https://github.com/phaazon/splines/commit/cdc48a4)
- Add `Spline::clear` to clear a spline keys without deallocating its internal storage. [eca09f1](https://github.com/phaazon/splines/commit/eca09f1)
# 4.2
> 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

View File

@ -1,6 +1,6 @@
[package]
name = "splines"
version = "2.0.0"
version = "4.3.1"
license = "BSD-3-Clause"
authors = ["Dimitri Sabadie <dimitri.sabadie@gmail.com>"]
description = "Spline interpolation made easy"
@ -11,28 +11,32 @@ repository = "https://github.com/phaazon/splines"
documentation = "https://docs.rs/splines"
readme = "README.md"
edition = "2018"
[badges]
travis-ci = { repository = "phaazon/splines", branch = "master" }
is-it-maintained-issue-resolution = { repository = "phaazon/splines" }
is-it-maintained-open-issues = { repository = "phaazon/splines" }
maintenance = { status = "actively-developed" }
edition = "2021"
[features]
default = ["std"]
impl-cgmath = ["cgmath"]
impl-nalgebra = ["alga", "nalgebra", "num-traits"]
serialization = ["serde", "serde_derive"]
std = []
impl-glam = ["glam"]
impl-nalgebra = ["nalgebra"]
serialization = ["serde"]
std = ["nalgebra/std"]
[dependencies]
alga = { version = "0.9", optional = true }
cgmath = { version = "0.17", optional = true }
nalgebra = { version = ">=0.14, <0.19", optional = true }
num-traits = { version = "0.2", optional = true }
serde = { version = "1", optional = true }
serde_derive = { version = "1", optional = true }
cgmath = { version = ">=0.17, <0.19", optional = true }
glam = { version = ">=0.10, <0.25", optional = true }
nalgebra = { version = ">=0.21, <0.33", default-features = false, optional = true }
serde = { version = "1", features = ["derive"], optional = true }
[dev-dependencies]
float-cmp = ">=0.6, < 0.10"
serde_json = "1"
[package.metadata.docs.rs]
all-features = true
features = ["std", "cgmath", "glam", "nalgebra", "serde"]
[[example]]
name = "hello-world"
[[example]]
name = "serialization"
required-features = ["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.

View File

@ -24,7 +24,7 @@ is picked from its lower control point.
# Quickly create splines
```
```rust
use splines::{Interpolation, Key, Spline};
let start = Key::new(0., 0., Interpolation::Linear);
@ -46,7 +46,7 @@ value.
If you try to sample in out-of-bounds sampling parameter, youll get no value.
```
```rust
assert_eq!(spline.sample(0.), Some(0.));
assert_eq!(spline.clamped_sample(1.), Some(10.));
assert_eq!(spline.sample(1.1), None);
@ -56,7 +56,7 @@ Its possible that you want to get a value even if youre out-of-bounds. Thi
important for simulations / animations. Feel free to use the `Spline::clamped_interpolation` for
that purpose.
```
```rust
assert_eq!(spline.clamped_sample(-0.9), Some(0.)); // clamped to the first key
assert_eq!(spline.clamped_sample(1.1), Some(10.)); // clamped to the last key
```
@ -66,7 +66,7 @@ assert_eq!(spline.clamped_sample(1.1), Some(10.)); // clamped to the last key
[`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.
implement. See [the documentation of this module](https://docs.rs/splines/latest/splines/interpolate/) for further details.
# Features and customization
@ -83,16 +83,19 @@ 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:
- **Serialization / deserialization.**
- **Serde.**
- This feature implements both the `Serialize` and `Deserialize` traits from `serde` for all
types exported by this crate.
- Enable with the `"serialization"` feature.
- Enable with the `"serde"` feature.
- **[cgmath](https://crates.io/crates/cgmath) implementors.**
- Adds some useful implementations of `Interpolate` for some cgmath types.
- Enable with the `"impl-cgmath"` feature.
- Enable with the `"cgmath"` feature.
- **[glam](https://crates.io/crates/glam) implementors.**
- Adds some useful implementations of `Interpolate` for some glam types.
- Enable with the `"glam"` feature.
- **[nalgebra](https://crates.io/crates/nalgebra) implementors.**
- Adds some useful implementations of `Interpolate` for some nalgebra types.
- Enable with the `"impl-nalgebra"` feature.
- Enable with the `"nalgebra"` feature.
- **Standard library / no standard library.**
- Its possible to compile against the standard library or go on your own without it.
- Compiling with the standard library is enabled by default.

View File

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

View File

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

View File

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

View File

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

View File

@ -1,11 +1,12 @@
#[macro_use] extern crate serde_json;
#[macro_use]
extern crate serde_json;
extern crate splines;
use serde_json::from_value;
use splines::Spline;
fn main() {
let value = json!{
let value = json! {
[
{
"t": 0,

15
rustfmt.toml Normal file
View File

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

View File

@ -1,86 +1,15 @@
use cgmath::{
BaseFloat, BaseNum, InnerSpace, Quaternion, Vector1, Vector2, Vector3, Vector4, VectorSpace
};
use crate::impl_Interpolate;
use crate::interpolate::{
Additive, Interpolate, Linear, One, cubic_bezier_def, cubic_hermite_def, quadratic_bezier_def
};
use cgmath::{Quaternion, Vector1, Vector2, Vector3, Vector4};
macro_rules! impl_interpolate_vec {
($($t:tt)*) => {
impl<T> Linear<T> for $($t)*<T> where T: BaseNum {
#[inline(always)]
fn outer_mul(self, t: T) -> Self {
self * t
}
impl_Interpolate!(f32, Vector1<f32>, std::f32::consts::PI);
impl_Interpolate!(f32, Vector2<f32>, std::f32::consts::PI);
impl_Interpolate!(f32, Vector3<f32>, std::f32::consts::PI);
impl_Interpolate!(f32, Vector4<f32>, std::f32::consts::PI);
impl_Interpolate!(f32, Quaternion<f32>, std::f32::consts::PI);
#[inline(always)]
fn outer_div(self, t: T) -> Self {
self / t
}
}
impl<T> Interpolate<T> for $($t)*<T>
where Self: InnerSpace<Scalar = T>, T: Additive + BaseFloat + One {
#[inline(always)]
fn lerp(a: Self, b: Self, t: T) -> Self {
a.lerp(b, t)
}
#[inline(always)]
fn cubic_hermite(x: (Self, T), a: (Self, T), b: (Self, T), y: (Self, T), t: T) -> Self {
cubic_hermite_def(x, a, b, y, t)
}
#[inline(always)]
fn quadratic_bezier(a: Self, u: Self, b: Self, t: T) -> Self {
quadratic_bezier_def(a, u, b, t)
}
#[inline(always)]
fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: T) -> Self {
cubic_bezier_def(a, u, v, b, t)
}
}
}
}
impl_interpolate_vec!(Vector1);
impl_interpolate_vec!(Vector2);
impl_interpolate_vec!(Vector3);
impl_interpolate_vec!(Vector4);
impl<T> Linear<T> for Quaternion<T> where T: BaseFloat {
#[inline(always)]
fn outer_mul(self, t: T) -> Self {
self * t
}
#[inline(always)]
fn outer_div(self, t: T) -> Self {
self / t
}
}
impl<T> Interpolate<T> for Quaternion<T>
where Self: InnerSpace<Scalar = T>, T: Additive + BaseFloat + One {
#[inline(always)]
fn lerp(a: Self, b: Self, t: T) -> Self {
a.nlerp(b, t)
}
#[inline(always)]
fn cubic_hermite(x: (Self, T), a: (Self, T), b: (Self, T), y: (Self, T), t: T) -> Self {
cubic_hermite_def(x, a, b, y, t)
}
#[inline(always)]
fn quadratic_bezier(a: Self, u: Self, b: Self, t: T) -> Self {
quadratic_bezier_def(a, u, b, t)
}
#[inline(always)]
fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: T) -> Self {
cubic_bezier_def(a, u, v, b, t)
}
}
impl_Interpolate!(f64, Vector1<f64>, std::f64::consts::PI);
impl_Interpolate!(f64, Vector2<f64>, std::f64::consts::PI);
impl_Interpolate!(f64, Vector3<f64>, std::f64::consts::PI);
impl_Interpolate!(f64, Vector4<f64>, std::f64::consts::PI);
impl_Interpolate!(f64, Quaternion<f64>, std::f64::consts::PI);

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);

View File

@ -28,267 +28,220 @@
//! [`Trigo`]: crate::interpolate::Trigo
//! [num-traits]: https://crates.io/crates/num-traits
#[cfg(feature = "std")] use std::f32;
#[cfg(not(feature = "std"))] use core::f32;
#[cfg(not(feature = "std"))] use core::intrinsics::cosf32;
#[cfg(feature = "std")] use std::f64;
#[cfg(not(feature = "std"))] use core::f64;
#[cfg(not(feature = "std"))] use core::intrinsics::cosf64;
#[cfg(feature = "std")] use std::ops::{Add, Mul, Sub};
#[cfg(not(feature = "std"))] use core::ops::{Add, Mul, Sub};
#[cfg(not(feature = "std"))]
use core::f32;
#[cfg(not(feature = "std"))]
use core::f64;
#[cfg(not(feature = "std"))]
use core::intrinsics::cosf32;
#[cfg(not(feature = "std"))]
use core::intrinsics::cosf64;
#[cfg(not(feature = "std"))]
use core::ops::{Add, Mul, Sub};
#[cfg(feature = "std")]
use std::f32;
#[cfg(feature = "std")]
use std::f64;
/// Keys that can be interpolated in between. Implementing this trait is required to perform
/// sampling on splines.
/// Types that can be used as interpolator in splines.
///
/// `T` is the variable used to sample with. Typical implementations use [`f32`] or [`f64`], but
/// youre free to use the ones you like. Feel free to have a look at [`Spline::sample`] for
/// instance to know which trait your type must implement to be usable.
/// An interpolator value is like the fabric on which control keys (and sampled values) live on.
pub trait Interpolator: Sized + Copy + PartialOrd {
/// Normalize the interpolator.
fn normalize(self, start: Self, end: Self) -> Self;
}
macro_rules! impl_Interpolator {
($t:ty) => {
impl Interpolator for $t {
fn normalize(self, start: Self, end: Self) -> Self {
(self - start) / (end - start)
}
}
};
}
impl_Interpolator!(f32);
impl_Interpolator!(f64);
/// Values that can be interpolated. Implementing this trait is required to perform sampling on splines.
///
/// [`Spline::sample`]: crate::spline::Spline::sample
/// `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(a: Self, b: Self, t: T) -> Self;
fn lerp(t: T, a: Self, b: Self) -> Self;
/// Cosine interpolation.
fn cosine(t: T, a: Self, b: Self) -> Self;
/// Cubic hermite interpolation.
///
/// Default to [`lerp`].
///
/// [`lerp`]: Interpolate::lerp
fn cubic_hermite(_: (Self, T), a: (Self, T), b: (Self, T), _: (Self, T), t: T) -> Self {
Self::lerp(a.0, b.0, t)
}
fn cubic_hermite(t: T, x: (T, Self), a: (T, Self), b: (T, Self), y: (T, Self)) -> Self;
/// Quadratic Bézier interpolation.
fn quadratic_bezier(a: Self, u: Self, b: Self, t: T) -> Self;
///
/// `a` is the first point; `b` is the second point and `u` is the tangent of `a` to the curve.
fn quadratic_bezier(t: T, a: Self, u: Self, b: Self) -> Self;
/// Cubic Bézier interpolation.
fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: T) -> Self;
///
/// `a` is the first point; `b` is the second point; `u` is the output tangent of `a` to the curve and `v` is the
/// input tangent of `b` to the curve.
fn cubic_bezier(t: T, a: Self, u: Self, v: Self, b: Self) -> Self;
/// Cubic Bézier interpolation special case for non-explicit second tangent.
///
/// This version does the same computation as [`Interpolate::cubic_bezier`] but computes the second tangent by
/// inversing it (typical when the next point uses a Bézier interpolation, where input and output tangents are
/// mirrored for the same key).
fn cubic_bezier_mirrored(t: T, a: Self, u: Self, v: Self, b: Self) -> Self;
}
/// Set of types that support additions and subtraction.
///
/// The [`Copy`] trait is also a supertrait as its likely to be used everywhere.
pub trait Additive:
Copy +
Add<Self, Output = Self> +
Sub<Self, Output = Self> {
}
impl<T> Additive for T
where T: Copy +
Add<Self, Output = Self> +
Sub<Self, Output = Self> {
}
/// Set of additive types that support outer multiplication and division, making them linear.
pub trait Linear<T>: Additive {
/// Apply an outer multiplication law.
fn outer_mul(self, t: T) -> Self;
/// Apply an outer division law.
fn outer_div(self, t: T) -> Self;
}
macro_rules! impl_linear_simple {
($t:ty) => {
impl Linear<$t> for $t {
fn outer_mul(self, t: $t) -> Self {
self * t
#[macro_export]
macro_rules! impl_Interpolate {
($t:ty, $v:ty, $pi:expr) => {
impl $crate::interpolate::Interpolate<$t> for $v {
fn step(t: $t, threshold: $t, a: Self, b: Self) -> Self {
if t < threshold {
a
} else {
b
}
}
/// Apply an outer division law.
fn outer_div(self, t: $t) -> Self {
self / t
#[cfg(feature = "std")]
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)
}
}
}
}
impl_linear_simple!(f32);
impl_linear_simple!(f64);
macro_rules! impl_linear_cast {
($t:ty, $q:ty) => {
impl Linear<$t> for $q {
fn outer_mul(self, t: $t) -> Self {
self * t as $q
#[cfg(not(feature = "std"))]
fn cosine(t: $t, a: Self, b: Self) -> Self {
unimplemented!();
}
/// Apply an outer division law.
fn outer_div(self, t: $t) -> Self {
self / t as $q
}
}
}
}
impl_linear_cast!(f32, f64);
impl_linear_cast!(f64, f32);
/// Types with a neutral element for multiplication.
pub trait One {
/// The neutral element for the multiplicative monoid — typically called `1`.
fn one() -> Self;
}
macro_rules! impl_one_float {
($t:ty) => {
impl One for $t {
#[inline(always)]
fn one() -> Self {
1.
}
}
}
}
impl_one_float!(f32);
impl_one_float!(f64);
/// Types with a sane definition of π and cosine.
pub trait Trigo {
/// π.
fn pi() -> Self;
/// Cosine of the argument.
fn cos(self) -> Self;
}
impl Trigo for f32 {
#[inline(always)]
fn pi() -> Self {
f32::consts::PI
}
#[inline(always)]
fn cos(self) -> Self {
#[cfg(feature = "std")]
{
self.cos()
}
#[cfg(not(feature = "std"))]
{
unsafe { cosf32(self) }
}
}
}
impl Trigo for f64 {
#[inline(always)]
fn pi() -> Self {
f64::consts::PI
}
#[inline(always)]
fn cos(self) -> Self {
#[cfg(feature = "std")]
{
self.cos()
}
#[cfg(not(feature = "std"))]
{
unsafe { cosf64(self) }
}
}
}
/// Default implementation of [`Interpolate::cubic_hermite`].
///
/// `V` is the value being interpolated. `T` is the sampling value (also sometimes called time).
pub fn cubic_hermite_def<V, T>(x: (V, T), a: (V, T), b: (V, T), y: (V, T), t: T) -> V
where V: Linear<T>,
T: Additive + Mul<T, Output = T> + One {
// some stupid generic constants, because Rust doesnt have polymorphic literals…
let one_t = T::one();
let two_t = one_t + one_t; // lolololol
let three_t = two_t + one_t; // megalol
// sampler stuff
let t2 = t * t;
let t3 = t2 * t;
let two_t3 = t3 * two_t;
let three_t2 = t2 * three_t;
// tangents
let m0 = (b.0 - x.0).outer_div(b.1 - x.1);
let m1 = (y.0 - a.0).outer_div(y.1 - a.1);
a.0.outer_mul(two_t3 - three_t2 + one_t) + m0.outer_mul(t3 - t2 * two_t + t) + b.0.outer_mul(three_t2 - two_t3) + m1.outer_mul(t3 - t2)
}
/// Default implementation of [`Interpolate::quadratic_bezier`].
///
/// `V` is the value being interpolated. `T` is the sampling value (also sometimes called time).
pub fn quadratic_bezier_def<V, T>(a: V, u: V, b: V, t: T) -> V
where V: Linear<T>,
T: Additive + Mul<T, Output = T> + One {
let one_t = T::one() - t;
let one_t_2 = one_t * one_t;
u + (a - u).outer_mul(one_t_2) + (b - u).outer_mul(t * t)
}
/// Default implementation of [`Interpolate::cubic_bezier`].
///
/// `V` is the value being interpolated. `T` is the sampling value (also sometimes called time).
pub fn cubic_bezier_def<V, T>(a: V, u: V, v: V, b: V, t: T) -> V
where V: Linear<T>,
T: Additive + Mul<T, Output = T> + One {
let one_t = T::one() - t;
let one_t_2 = one_t * one_t;
let one_t_3 = one_t_2 * one_t;
let three = T::one() + T::one() + T::one();
a.outer_mul(one_t_3) + u.outer_mul(three * one_t_2 * t) + v.outer_mul(three * one_t * t * t) + b.outer_mul(t * t * t)
}
macro_rules! impl_interpolate_simple {
($t:ty) => {
impl Interpolate<$t> for $t {
fn lerp(a: Self, b: Self, t: $t) -> Self {
fn lerp(t: $t, a: Self, b: Self) -> Self {
a * (1. - t) + b * t
}
fn cubic_hermite(x: (Self, $t), a: (Self, $t), b: (Self, $t), y: (Self, $t), t: $t) -> Self {
cubic_hermite_def(x, a, b, y, t)
fn cubic_hermite(t: $t, x: ($t, Self), a: ($t, Self), b: ($t, Self), y: ($t, Self)) -> Self {
// sampler stuff
let two_t = t * 2.;
let three_t = t * 3.;
let t2 = t * t;
let t3 = t2 * t;
let two_t3 = t2 * two_t;
let two_t2 = t * two_t;
let three_t2 = t * three_t;
// tangents
let m0 = (b.1 - x.1) / (b.0 - x.0) * (b.0 - a.0);
let m1 = (y.1 - a.1) / (y.0 - a.0) * (b.0 - a.0);
a.1 * (two_t3 - three_t2 + 1.)
+ m0 * (t3 - two_t2 + t)
+ b.1 * (three_t2 - two_t3)
+ m1 * (t3 - t2)
}
fn quadratic_bezier(a: Self, u: Self, b: Self, t: $t) -> Self {
quadratic_bezier_def(a, u, b, t)
fn quadratic_bezier(t: $t, a: Self, u: Self, b: Self) -> Self {
let one_t = 1. - t;
let one_t2 = one_t * one_t;
u + (a - u) * one_t2 + (b - u) * t * t
}
fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: $t) -> Self {
cubic_bezier_def(a, u, v, b, t)
fn cubic_bezier(t: $t, a: Self, u: Self, v: Self, b: Self) -> Self {
let one_t = 1. - t;
let one_t2 = one_t * one_t;
let one_t3 = one_t2 * one_t;
let t2 = t * t;
a * one_t3 + (u * one_t2 * t + v * one_t * t2) * 3. + b * t2 * t
}
fn cubic_bezier_mirrored(t: $t, a: Self, u: Self, v: Self, b: Self) -> Self {
<Self as $crate::interpolate::Interpolate<$t>>::cubic_bezier(t, a, u, b + b - v, b)
}
}
}
};
}
impl_interpolate_simple!(f32);
impl_interpolate_simple!(f64);
macro_rules! impl_interpolate_via {
($t:ty, $v:ty) => {
impl Interpolate<$t> for $v {
fn lerp(a: Self, b: Self, t: $t) -> Self {
a * (1. - t as $v) + b * t as $v
#[macro_export]
macro_rules! impl_InterpolateT {
($t:ty, $v:ty, $pi:expr) => {
impl $crate::interpolate::Interpolate<$t> for $v {
fn step(t: $t, threshold: $t, a: Self, b: Self) -> Self {
if t < threshold {
a
} else {
b
}
}
fn cubic_hermite((x, xt): (Self, $t), (a, at): (Self, $t), (b, bt): (Self, $t), (y, yt): (Self, $t), t: $t) -> Self {
cubic_hermite_def((x, xt as $v), (a, at as $v), (b, bt as $v), (y, yt as $v), t as $v)
#[cfg(feature = "std")]
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)
}
#[cfg(not(feature = "std"))]
fn cosine(t: $t, a: Self, b: Self) -> Self {
unimplemented!()
}
fn quadratic_bezier(a: Self, u: Self, b: Self, t: $t) -> Self {
quadratic_bezier_def(a, u, b, t as $v)
fn lerp(t: $t, a: Self, b: Self) -> Self {
let t = Self::from(t);
a * (1. - t) + b * t
}
fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: $t) -> Self {
cubic_bezier_def(a, u, v, b, t as $v)
fn cubic_hermite(t: $t, x: ($t, Self), a: ($t, Self), b: ($t, Self), y: ($t, Self)) -> Self {
// sampler stuff
let t = Self::from(t);
let two_t = t * 2.;
let three_t = t * 3.;
let t2 = t * t;
let t3 = t2 * t;
let two_t3 = t2 * two_t;
let two_t2 = t * two_t;
let three_t2 = t * three_t;
// tangents
let m0 = (b.1 - x.1) / (Self::from(b.0 - x.0)) * (Self::from(b.0 - a.0));
let m1 = (y.1 - a.1) / (Self::from(y.0 - a.0)) * (Self::from(b.0 - a.0));
a.1 * (two_t3 - three_t2 + 1.)
+ m0 * (t3 - two_t2 + t)
+ b.1 * (three_t2 - two_t3)
+ m1 * (t3 - t2)
}
fn quadratic_bezier(t: $t, a: Self, u: Self, b: Self) -> Self {
let t = Self::from(t);
let one_t = 1. - t;
let one_t2 = one_t * one_t;
u + (a - u) * one_t2 + (b - u) * t * t
}
fn cubic_bezier(t: $t, a: Self, u: Self, v: Self, b: Self) -> Self {
let t = Self::from(t);
let one_t = 1. - t;
let one_t2 = one_t * one_t;
let one_t3 = one_t2 * one_t;
let t2 = t * t;
a * one_t3 + (u * one_t2 * t + v * one_t * t2) * 3. + b * t2 * t
}
fn cubic_bezier_mirrored(t: $t, a: Self, u: Self, v: Self, b: Self) -> Self {
<Self as $crate::interpolate::Interpolate<$t>>::cubic_bezier(t, a, u, b + b - v, b)
}
}
}
};
}
impl_interpolate_via!(f32, f64);
impl_interpolate_via!(f64, f32);
impl_Interpolate!(f32, f32, f32::consts::PI);
impl_Interpolate!(f64, f64, f64::consts::PI);
impl_InterpolateT!(f32, f64, f32::consts::PI);

View File

@ -1,13 +1,18 @@
//! Available interpolation modes.
#[cfg(feature = "serialization")] use serde_derive::{Deserialize, Serialize};
#[cfg(any(feature = "serialization", feature = "serde"))]
use serde::{Deserialize, Serialize};
/// Available kind of interpolations.
///
/// Feel free to visit each variant for more documentation.
#[non_exhaustive]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serialization", serde(rename_all = "snake_case"))]
#[cfg_attr(
any(feature = "serialization", feature = "serde"),
derive(Deserialize, Serialize),
serde(rename_all = "snake_case")
)]
pub enum Interpolation<T, V> {
/// Hold a [`Key`] until the sampling value passes the normalized step threshold, in which
/// case the next key is used.
@ -19,12 +24,16 @@ pub enum Interpolation<T, V> {
///
/// [`Key`]: crate::key::Key
Step(T),
/// Linear interpolation between a key and the next one.
Linear,
/// Cosine interpolation between a key and the next one.
Cosine,
/// Catmull-Rom interpolation, performing a cubic Hermite interpolation using four keys.
CatmullRom,
/// Bézier interpolation.
///
/// A control point that uses such an interpolation is associated with an extra point. The segmant
@ -40,8 +49,19 @@ pub enum Interpolation<T, V> {
/// point and the current control points associated point. This is called _quadratic Bézer
/// interpolation_ and it kicks ass too, but a bit less than cubic.
Bezier(V),
#[doc(hidden)]
__NonExhaustive
/// 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> {

View File

@ -11,9 +11,13 @@ use crate::{Key, Spline};
/// Iterator over spline keys.
///
/// This iterator type is guaranteed to iterate over sorted keys.
pub struct Iter<'a, T, V> where T: 'a, V: 'a {
pub struct Iter<'a, T, V>
where
T: 'a,
V: 'a,
{
spline: &'a Spline<T, V>,
i: usize
i: usize,
}
impl<'a, T, V> Iterator for Iter<'a, T, V> {
@ -35,10 +39,6 @@ impl<'a, T, V> IntoIterator for &'a Spline<T, V> {
type IntoIter = Iter<'a, T, V>;
fn into_iter(self) -> Self::IntoIter {
Iter {
spline: self,
i: 0
}
Iter { spline: self, i: 0 }
}
}

View File

@ -1,14 +1,14 @@
//! Spline control points.
//!
//! A control point associates to a “sampling value” (a.k.a. time) a carriede value that can be
//! A control point associates to a “sampling value” (a.k.a. time) a carried value that can be
//! interpolated along the curve made by the control points.
//!
//! Splines constructed with this crate have the property that its possible to change the
//! interpolation mode on a key-based way, allowing you to implement and encode complex curves.
#[cfg(feature = "serialization")] use serde_derive::{Deserialize, Serialize};
use crate::interpolation::Interpolation;
#[cfg(any(feature = "serialization", feature = "serde"))]
use serde::{Deserialize, Serialize};
/// A spline control point.
///
@ -18,20 +18,27 @@ use crate::interpolation::Interpolation;
///
/// [`Interpolation`]: crate::interpolation::Interpolation
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serialization", serde(rename_all = "snake_case"))]
#[cfg_attr(
any(feature = "serialization", feature = "serde"),
derive(Deserialize, Serialize),
serde(rename_all = "snake_case")
)]
pub struct Key<T, V> {
/// Interpolation parameter at which the [`Key`] should be reached.
pub t: T,
/// Carried value.
pub value: V,
/// Interpolation mode.
pub interpolation: Interpolation<T, V>
pub interpolation: Interpolation<T, V>,
}
impl<T, V> Key<T, V> {
/// Create a new key.
pub fn new(t: T, value: V, interpolation: Interpolation<T, V>) -> Self {
Key { t, value, interpolation }
Key {
t,
value,
interpolation,
}
}
}

View File

@ -84,16 +84,19 @@
//!
//! So heres a list of currently supported features and how to enable them:
//!
//! - **Serialization / deserialization.**
//! - **Serde.**
//! - This feature implements both the `Serialize` and `Deserialize` traits from `serde` for all
//! types exported by this crate.
//! - Enable with the `"serialization"` feature.
//! - Enable with the `"serde"` feature.
//! - **[cgmath](https://crates.io/crates/cgmath) implementors.**
//! - Adds some useful implementations of `Interpolate` for some cgmath types.
//! - Enable with the `"impl-cgmath"` feature.
//! - Enable with the `"cgmath"` feature.
//! - **[glam](https://crates.io/crates/glam) implementors.**
//! - Adds some useful implementations of `Interpolate` for some glam types.
//! - Enable with the `"glam"` feature.
//! - **[nalgebra](https://crates.io/crates/nalgebra) implementors.**
//! - Adds some useful implementations of `Interpolate` for some nalgebra types.
//! - Enable with the `"impl-nalgebra"` feature.
//! - Enable with the `"nalgebra"` feature.
//! - **Standard library / no standard library.**
//! - Its possible to compile against the standard library or go on your own without it.
//! - Compiling with the standard library is enabled by default.
@ -105,15 +108,31 @@
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(not(feature = "std"), feature(alloc))]
#![cfg_attr(not(feature = "std"), feature(core_intrinsics))]
#![cfg_attr(
any(
feature = "impl-cgmath",
feature = "impl-glam",
feature = "impl-nalgebra"
),
deprecated(
since = "4.2.0",
note = "you are using an impl-* feature gate; please switch to * (e.g. impl-cgmath becomes cgmath)"
)
)]
#[cfg(not(feature = "std"))] extern crate alloc;
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(feature = "impl-cgmath")] mod 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(feature = "impl-nalgebra")] mod nalgebra;
#[cfg(any(feature = "impl-nalgebra", feature = "nalgebra"))]
mod nalgebra;
pub mod spline;
pub use crate::interpolate::Interpolate;

View File

@ -1,64 +1,27 @@
use alga::general::{ClosedAdd, ClosedDiv, ClosedMul, ClosedSub};
use nalgebra::{Scalar, Vector, Vector1, Vector2, Vector3, Vector4, Vector5, Vector6};
use num_traits as nt;
use std::ops::Mul;
#[cfg(not(feature = "std"))]
use core::f32;
#[cfg(not(feature = "std"))]
use core::f64;
#[cfg(feature = "std")]
use std::f32;
#[cfg(feature = "std")]
use std::f64;
use crate::interpolate::{
Interpolate, Linear, Additive, One, cubic_bezier_def, cubic_hermite_def, quadratic_bezier_def
};
use crate::impl_Interpolate;
use nalgebra::{Quaternion, Vector1, Vector2, Vector3, Vector4, Vector5, Vector6};
macro_rules! impl_interpolate_vector {
($($t:tt)*) => {
// implement Linear
impl<T> Linear<T> for $($t)*<T> where T: Scalar + ClosedAdd + ClosedSub + ClosedMul + ClosedDiv {
#[inline(always)]
fn outer_mul(self, t: T) -> Self {
self * t
}
impl_Interpolate!(f32, Vector1<f32>, f32::consts::PI);
impl_Interpolate!(f32, Vector2<f32>, f32::consts::PI);
impl_Interpolate!(f32, Vector3<f32>, f32::consts::PI);
impl_Interpolate!(f32, Vector4<f32>, f32::consts::PI);
impl_Interpolate!(f32, Vector5<f32>, f32::consts::PI);
impl_Interpolate!(f32, Vector6<f32>, f32::consts::PI);
impl_Interpolate!(f32, Quaternion<f32>, f32::consts::PI);
#[inline(always)]
fn outer_div(self, t: T) -> Self {
self / t
}
}
impl<T, V> Interpolate<T> for $($t)*<V>
where Self: Linear<T>,
T: Additive + One + Mul<T, Output = T>,
V: nt::One +
nt::Zero +
Additive +
Scalar +
ClosedAdd +
ClosedMul +
ClosedSub +
Interpolate<T> {
#[inline(always)]
fn lerp(a: Self, b: Self, t: T) -> Self {
Vector::zip_map(&a, &b, |c1, c2| Interpolate::lerp(c1, c2, t))
}
#[inline(always)]
fn cubic_hermite(x: (Self, T), a: (Self, T), b: (Self, T), y: (Self, T), t: T) -> Self {
cubic_hermite_def(x, a, b, y, t)
}
#[inline(always)]
fn quadratic_bezier(a: Self, u: Self, b: Self, t: T) -> Self {
quadratic_bezier_def(a, u, b, t)
}
#[inline(always)]
fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: T) -> Self {
cubic_bezier_def(a, u, v, b, t)
}
}
}
}
impl_interpolate_vector!(Vector1);
impl_interpolate_vector!(Vector2);
impl_interpolate_vector!(Vector3);
impl_interpolate_vector!(Vector4);
impl_interpolate_vector!(Vector5);
impl_interpolate_vector!(Vector6);
impl_Interpolate!(f64, Vector1<f64>, f64::consts::PI);
impl_Interpolate!(f64, Vector2<f64>, f64::consts::PI);
impl_Interpolate!(f64, Vector3<f64>, f64::consts::PI);
impl_Interpolate!(f64, Vector4<f64>, f64::consts::PI);
impl_Interpolate!(f64, Vector5<f64>, f64::consts::PI);
impl_Interpolate!(f64, Vector6<f64>, f64::consts::PI);
impl_Interpolate!(f64, Quaternion<f64>, f64::consts::PI);

View File

@ -1,15 +1,19 @@
//! Spline curves and operations.
#[cfg(feature = "serialization")] use serde_derive::{Deserialize, Serialize};
#[cfg(not(feature = "std"))] use alloc::vec::Vec;
#[cfg(feature = "std")] use std::cmp::Ordering;
#[cfg(feature = "std")] use std::ops::{Div, Mul};
#[cfg(not(feature = "std"))] use core::ops::{Div, Mul};
#[cfg(not(feature = "std"))] use core::cmp::Ordering;
use crate::interpolate::{Interpolate, Additive, One, Trigo};
// #[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).
///
@ -23,24 +27,42 @@ use crate::key::Key;
/// 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(feature = "serialization", derive(Deserialize, Serialize))]
#[derive(Debug, Clone, Default)]
#[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));
fn internal_sort(&mut self)
where
T: PartialOrd,
{
self
.0
.sort_by(|k0, k1| k0.t.partial_cmp(&k1.t).unwrap_or(Ordering::Less));
}
/// Create a new spline out of keys. The keys dont have to be sorted even though its recommended
/// to provide ascending sorted ones (for performance purposes).
pub fn from_vec(keys: Vec<Key<T, V>>) -> Self where T: PartialOrd {
pub fn from_vec(keys: Vec<Key<T, V>>) -> Self
where
T: PartialOrd,
{
let mut spline = Spline(keys);
spline.internal_sort();
spline
}
/// 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.
///
@ -48,7 +70,11 @@ impl<T, V> Spline<T, V> {
///
/// Its valid to use any iterator that implements `Iterator<Item = Key<T>>`. However, you should
/// use [`Spline::from_vec`] if you are passing a [`Vec`].
pub fn from_iter<I>(iter: I) -> Self where I: Iterator<Item = Key<T, V>>, T: PartialOrd {
pub fn from_iter<I>(iter: I) -> Self
where
I: Iterator<Item = Key<T, V>>,
T: PartialOrd,
{
Self::from_vec(iter.collect())
}
@ -69,7 +95,8 @@ impl<T, V> Spline<T, V> {
self.0.is_empty()
}
/// Sample a spline at a given time.
/// Sample a spline at a given time, returning the interpolated value along with its associated
/// key.
///
/// The current implementation, based on immutability, cannot perform in constant time. This means
/// that samplings processing complexity is currently *O(log n)*. Its possible to achieve *O(1)*
@ -83,35 +110,38 @@ impl<T, V> Spline<T, V> {
/// sampling impossible. For instance, [`Interpolation::CatmullRom`] requires *four* keys. If
/// youre near the beginning of the spline or its end, ensure you have enough keys around to make
/// the sampling.
///
pub fn sample(&self, t: T) -> Option<V>
where T: Additive + One + Trigo + Mul<T, Output = T> + Div<T, Output = T> + PartialOrd,
V: Interpolate<T> {
pub fn sample_with_key(&self, t: T) -> Option<SampledWithKey<V>>
where
T: Interpolator,
V: Interpolate<T>,
{
let keys = &self.0;
let i = search_lower_cp(keys, t)?;
let cp0 = &keys[i];
match cp0.interpolation {
let value = match cp0.interpolation {
Interpolation::Step(threshold) => {
let cp1 = &keys[i + 1];
let nt = normalize_time(t, cp0, cp1);
Some(if nt < threshold { cp0.value } else { cp1.value })
let nt = t.normalize(cp0.t, cp1.t);
let value = V::step(nt, threshold, cp0.value, cp1.value);
Some(value)
}
Interpolation::Linear => {
let cp1 = &keys[i + 1];
let nt = normalize_time(t, cp0, cp1);
let nt = t.normalize(cp0.t, cp1.t);
let value = V::lerp(nt, cp0.value, cp1.value);
Some(Interpolate::lerp(cp0.value, cp1.value, nt))
Some(value)
}
Interpolation::Cosine => {
let two_t = T::one() + T::one();
let cp1 = &keys[i + 1];
let nt = normalize_time(t, cp0, cp1);
let cos_nt = (T::one() - (nt * T::pi()).cos()) / two_t;
let nt = t.normalize(cp0.t, cp1.t);
let value = V::cosine(nt, cp0.value, cp1.value);
Some(Interpolate::lerp(cp0.value, cp1.value, cos_nt))
Some(value)
}
Interpolation::CatmullRom => {
@ -123,34 +153,51 @@ impl<T, V> Spline<T, V> {
let cp1 = &keys[i + 1];
let cpm0 = &keys[i - 1];
let cpm1 = &keys[i + 2];
let nt = normalize_time(t, cp0, cp1);
let 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(Interpolate::cubic_hermite((cpm0.value, cpm0.t), (cp0.value, cp0.t), (cp1.value, cp1.t), (cpm1.value, cpm1.t), nt))
Some(value)
}
}
Interpolation::Bezier(u) => {
Interpolation::Bezier(u) | Interpolation::StrokeBezier(_, u) => {
// We need to check the next control point to see whether we want quadratic or cubic Bezier.
let cp1 = &keys[i + 1];
let nt = normalize_time(t, cp0, cp1);
let nt = t.normalize(cp0.t, cp1.t);
if let Interpolation::Bezier(v) = cp1.interpolation {
Some(Interpolate::cubic_bezier(cp0.value, u, v, cp1.value, nt))
//let one_nt = T::one() - nt;
//let one_nt_2 = one_nt * one_nt;
//let one_nt_3 = one_nt_2 * one_nt;
//let three_one_nt_2 = one_nt_2 + one_nt_2 + one_nt_2; // one_nt_2 * 3
//let r = cp0.value * one_nt_3;
} else {
Some(Interpolate::quadratic_bezier(cp0.value, u, cp1.value, nt))
}
let value = match cp1.interpolation {
Interpolation::Bezier(v) => 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)
}
};
Interpolation::__NonExhaustive => unreachable!(),
}
value.map(|value| SampledWithKey { value, key: i })
}
/// Sample a spline at a given time with clamping.
/// Sample a spline at a given time.
///
pub fn sample(&self, t: T) -> Option<V>
where
T: 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
///
@ -160,22 +207,33 @@ impl<T, V> Spline<T, V> {
/// # Error
///
/// This function returns [`None`] if you have no key.
pub fn clamped_sample(&self, t: T) -> Option<V>
where T: Additive + One + Trigo + Mul<T, Output = T> + Div<T, Output = T> + PartialOrd,
V: Interpolate<T> {
pub fn clamped_sample_with_key(&self, t: T) -> Option<SampledWithKey<V>>
where
T: Interpolator,
V: Interpolate<T>,
{
if self.0.is_empty() {
return None;
}
self.sample(t).or_else(move || {
self.sample_with_key(t).or_else(move || {
let first = self.0.first().unwrap();
if t <= first.t {
Some(first.value)
let sampled = SampledWithKey {
value: first.value,
key: 0,
};
Some(sampled)
} else {
let last = self.0.last().unwrap();
if t >= last.t {
Some(last.value)
let sampled = SampledWithKey {
value: last.value,
key: self.0.len() - 1,
};
Some(sampled)
} else {
None
}
@ -183,8 +241,20 @@ impl<T, V> Spline<T, V> {
})
}
/// Sample a spline at a given time with clamping.
pub fn clamped_sample(&self, t: T) -> Option<V>
where
T: 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 {
pub fn add(&mut self, key: Key<T, V>)
where
T: PartialOrd,
{
self.0.push(key);
self.internal_sort();
}
@ -207,14 +277,10 @@ impl<T, V> Spline<T, V> {
/// That function makes sense only if you want to change the interpolator (i.e. [`Key::t`]) of
/// your key. If you just want to change the interpolation mode or the carried value, consider
/// using the [`Spline::get_mut`] method instead as it will be way faster.
pub fn replace<F>(
&mut self,
index: usize,
f: F
) -> Option<Key<T, V>>
pub fn replace<F>(&mut self, index: usize, f: F) -> Option<Key<T, V>>
where
F: FnOnce(&Key<T, V>) -> Key<T, V>,
T: PartialOrd
T: PartialOrd,
{
let key = self.remove(index)?;
self.add(f(&key));
@ -230,16 +296,27 @@ impl<T, V> Spline<T, V> {
pub fn get_mut(&mut self, index: usize) -> Option<KeyMut<T, V>> {
self.0.get_mut(index).map(|key| KeyMut {
value: &mut key.value,
interpolation: &mut key.interpolation
interpolation: &mut key.interpolation,
})
}
}
/// 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,
@ -247,46 +324,21 @@ pub struct KeyMut<'a, T, V> {
pub interpolation: &'a mut Interpolation<T, V>,
}
// Normalize a time ([0;1]) given two control points.
#[inline(always)]
pub(crate) fn normalize_time<T, V>(
t: T,
cp: &Key<T, V>,
cp1: &Key<T, V>
) -> T where T: Additive + Div<T, Output = T> + PartialEq {
assert!(cp1.t != cp.t, "overlapping keys");
(t - cp.t) / (cp1.t - cp.t)
}
// Find the lower control point corresponding to a given time.
fn search_lower_cp<T, V>(cps: &[Key<T, V>], t: T) -> Option<usize> where T: PartialOrd {
let mut i = 0;
// 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;
}
loop {
let cp = &cps[i];
let cp1 = &cps[i+1];
if t >= cp1.t {
if i >= len - 2 {
return None;
}
i += 1;
} else if t < cp.t {
if i == 0 {
return None;
}
i -= 1;
} else {
break; // found
}
match cps.binary_search_by(|key| key.t.partial_cmp(&t).unwrap()) {
Err(i) if i >= len => None,
Err(i) if i == 0 => None,
Err(i) => Some(i - 1),
Ok(i) if i == len - 1 => None,
Ok(i) => Some(i),
}
Some(i)
}

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

View File

@ -1,7 +1,4 @@
use splines::{Interpolation, Key, Spline};
#[cfg(feature = "impl-cgmath")] use cgmath as cg;
#[cfg(feature = "impl-nalgebra")] use nalgebra as na;
use splines::{spline::SampledWithKey, Interpolation, Key, Spline};
#[test]
fn step_interpolation_f32() {
@ -16,6 +13,14 @@ fn step_interpolation_f32() {
assert_eq!(spline.sample(0.9), Some(10.));
assert_eq!(spline.sample(1.), None);
assert_eq!(spline.clamped_sample(1.), Some(10.));
assert_eq!(
spline.sample_with_key(0.2),
Some(SampledWithKey { value: 10., key: 0 })
);
assert_eq!(
spline.clamped_sample_with_key(1.),
Some(SampledWithKey { value: 10., key: 1 })
);
}
#[test]
@ -31,6 +36,14 @@ fn step_interpolation_f64() {
assert_eq!(spline.sample(0.9), Some(10.));
assert_eq!(spline.sample(1.), None);
assert_eq!(spline.clamped_sample(1.), Some(10.));
assert_eq!(
spline.sample_with_key(0.2),
Some(SampledWithKey { value: 10., key: 0 })
);
assert_eq!(
spline.clamped_sample_with_key(1.),
Some(SampledWithKey { value: 10., key: 1 })
);
}
#[test]
@ -145,34 +158,6 @@ fn several_interpolations_several_keys() {
assert_eq!(spline.clamped_sample(11.), Some(4.));
}
#[cfg(feature = "impl-cgmath")]
#[test]
fn cgmath_vector_interpolation() {
use splines::Interpolate;
let start = cg::Vector2::new(0.0, 0.0);
let mid = cg::Vector2::new(0.5, 0.5);
let end = cg::Vector2::new(1.0, 1.0);
assert_eq!(Interpolate::lerp(start, end, 0.0), start);
assert_eq!(Interpolate::lerp(start, end, 1.0), end);
assert_eq!(Interpolate::lerp(start, end, 0.5), mid);
}
#[cfg(feature = "impl-nalgebra")]
#[test]
fn nalgebra_vector_interpolation() {
use splines::Interpolate;
let start = na::Vector2::new(0.0, 0.0);
let mid = na::Vector2::new(0.5, 0.5);
let end = na::Vector2::new(1.0, 1.0);
assert_eq!(Interpolate::lerp(start, end, 0.0), start);
assert_eq!(Interpolate::lerp(start, end, 1.0), end);
assert_eq!(Interpolate::lerp(start, end, 0.5), mid);
}
#[test]
fn add_key_empty() {
let mut spline: Spline<f32, f32> = Spline::from_vec(vec![]);

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