splines/
interpolate.rs

1//! The [`Interpolate`] trait and associated symbols.
2//!
3//! The [`Interpolate`] trait is the central concept of the crate. It enables a spline to be
4//! sampled at by interpolating in between control points.
5//!
6//! In order for a type to be used in [`Spline<K, V>`], some properties must be met about the `K`
7//! type must implementing several traits:
8//!
9//!   - [`One`], giving a neutral element for the multiplication monoid.
10//!   - [`Additive`], making the type additive (i.e. one can add or subtract with it).
11//!   - [`Linear`], unlocking linear combinations, required for interpolating.
12//!   - [`Trigo`], a trait giving *π* and *cosine*, required for e.g. cosine interpolation.
13//!
14//! Feel free to have a look at current implementors for further help.
15//!
16//! > *Why doesn’t this crate use [num-traits] instead of
17//! > defining its own traits?*
18//!
19//! The reason for this is quite simple: this crate provides a `no_std` support, which is not
20//! currently available easily with [num-traits]. Also, if something changes in [num-traits] with
21//! those traits, it would make this whole crate unstable.
22//!
23//! [`Interpolate`]: crate::interpolate::Interpolate
24//! [`Spline<K, V>`]: crate::spline::Spline
25//! [`One`]: crate::interpolate::One
26//! [`Additive`]: crate::interpolate::Additive
27//! [`Linear`]: crate::interpolate::Linear
28//! [`Trigo`]: crate::interpolate::Trigo
29//! [num-traits]: https://crates.io/crates/num-traits
30
31/// Types that can be used as interpolator in splines.
32///
33/// An interpolator value is like the fabric on which control keys (and sampled values) live on.
34pub trait Interpolator: Sized + Copy + PartialOrd {
35  /// Normalize the interpolator.
36  fn normalize(self, start: Self, end: Self) -> Self;
37}
38
39macro_rules! impl_Interpolator {
40  ($t:ty) => {
41    impl Interpolator for $t {
42      fn normalize(self, start: Self, end: Self) -> Self {
43        (self - start) / (end - start)
44      }
45    }
46  };
47}
48
49impl_Interpolator!(f32);
50impl_Interpolator!(f64);
51
52/// Values that can be interpolated. Implementing this trait is required to perform sampling on splines.
53///
54/// `T` is the interpolator used to sample with. Typical implementations use [`f32`] or [`f64`].
55pub trait Interpolate<T>: Sized + Copy {
56  /// Step interpolation.
57  fn step(t: T, threshold: T, a: Self, b: Self) -> Self;
58
59  /// Linear interpolation.
60  fn lerp(t: T, a: Self, b: Self) -> Self;
61
62  /// Cosine interpolation.
63  fn cosine(t: T, a: Self, b: Self) -> Self;
64
65  /// Cubic hermite interpolation.
66  fn cubic_hermite(t: T, x: (T, Self), a: (T, Self), b: (T, Self), y: (T, Self)) -> Self;
67
68  /// Quadratic Bézier interpolation.
69  ///
70  /// `a` is the first point; `b` is the second point and `u` is the tangent of `a` to the curve.
71  fn quadratic_bezier(t: T, a: Self, u: Self, b: Self) -> Self;
72
73  /// Cubic Bézier interpolation.
74  ///
75  /// `a` is the first point; `b` is the second point; `u` is the output tangent of `a` to the curve and `v` is the
76  /// input tangent of `b` to the curve.
77  fn cubic_bezier(t: T, a: Self, u: Self, v: Self, b: Self) -> Self;
78
79  /// Cubic Bézier interpolation – special case for non-explicit second tangent.
80  ///
81  /// This version does the same computation as [`Interpolate::cubic_bezier`] but computes the second tangent by
82  /// inversing it (typical when the next point uses a Bézier interpolation, where input and output tangents are
83  /// mirrored for the same key).
84  fn cubic_bezier_mirrored(t: T, a: Self, u: Self, v: Self, b: Self) -> Self;
85}
86
87#[macro_export]
88macro_rules! impl_Interpolate {
89  ($t:ty, $v:ty, $pi:expr) => {
90    // prevents implementing Interpolate if none of "std" or "num-traits" is provided
91    #[cfg(any(feature = "std", feature = "num-traits"))]
92    impl $crate::interpolate::Interpolate<$t> for $v {
93      fn step(t: $t, threshold: $t, a: Self, b: Self) -> Self {
94        if t < threshold { a } else { b }
95      }
96
97      fn cosine(t: $t, a: Self, b: Self) -> Self {
98        #[cfg(feature = "std")]
99        let cos_nt = (1. - (t * $pi).cos()) * 0.5;
100        #[cfg(all(not(feature = "std"), feature = "num-traits"))]
101        let cos_nt = (1. - num_traits::Float::cos(t * $pi)) * 0.5;
102        <Self as $crate::interpolate::Interpolate<$t>>::lerp(cos_nt, a, b)
103      }
104
105      fn lerp(t: $t, a: Self, b: Self) -> Self {
106        a * (1. - t) + b * t
107      }
108
109      fn cubic_hermite(t: $t, x: ($t, Self), a: ($t, Self), b: ($t, Self), y: ($t, Self)) -> Self {
110        // sampler stuff
111        let two_t = t * 2.;
112        let three_t = t * 3.;
113        let t2 = t * t;
114        let t3 = t2 * t;
115        let two_t3 = t2 * two_t;
116        let two_t2 = t * two_t;
117        let three_t2 = t * three_t;
118
119        // tangents
120        let m0 = (b.1 - x.1) / (b.0 - x.0) * (b.0 - a.0);
121        let m1 = (y.1 - a.1) / (y.0 - a.0) * (b.0 - a.0);
122
123        a.1 * (two_t3 - three_t2 + 1.)
124          + m0 * (t3 - two_t2 + t)
125          + b.1 * (three_t2 - two_t3)
126          + m1 * (t3 - t2)
127      }
128
129      fn quadratic_bezier(t: $t, a: Self, u: Self, b: Self) -> Self {
130        let one_t = 1. - t;
131        let one_t2 = one_t * one_t;
132
133        u + (a - u) * one_t2 + (b - u) * t * t
134      }
135
136      fn cubic_bezier(t: $t, a: Self, u: Self, v: Self, b: Self) -> Self {
137        let one_t = 1. - t;
138        let one_t2 = one_t * one_t;
139        let one_t3 = one_t2 * one_t;
140        let t2 = t * t;
141
142        a * one_t3 + (u * one_t2 * t + v * one_t * t2) * 3. + b * t2 * t
143      }
144
145      fn cubic_bezier_mirrored(t: $t, a: Self, u: Self, v: Self, b: Self) -> Self {
146        <Self as $crate::interpolate::Interpolate<$t>>::cubic_bezier(t, a, u, b + b - v, b)
147      }
148    }
149  };
150}
151
152#[macro_export]
153macro_rules! impl_InterpolateT {
154  ($t:ty, $v:ty, $pi:expr) => {
155    // prevents implementing Interpolate of none of "std" or "num-traits" is provided
156    #[cfg(any(feature = "std", feature = "num-traits"))]
157    impl $crate::interpolate::Interpolate<$t> for $v {
158      fn step(t: $t, threshold: $t, a: Self, b: Self) -> Self {
159        if t < threshold { a } else { b }
160      }
161
162      fn cosine(t: $t, a: Self, b: Self) -> Self {
163        #[cfg(feature = "std")]
164        let cos_nt = (1. - (t * $pi).cos()) * 0.5;
165        #[cfg(all(not(feature = "std"), feature = "num-traits"))]
166        let cos_nt = (1. - num_traits::Float::cos(t * $pi)) * 0.5;
167        <Self as $crate::interpolate::Interpolate<$t>>::lerp(cos_nt, a, b)
168      }
169
170      fn lerp(t: $t, a: Self, b: Self) -> Self {
171        let t = Self::from(t);
172        a * (1. - t) + b * t
173      }
174
175      fn cubic_hermite(t: $t, x: ($t, Self), a: ($t, Self), b: ($t, Self), y: ($t, Self)) -> Self {
176        // sampler stuff
177        let t = Self::from(t);
178        let two_t = t * 2.;
179        let three_t = t * 3.;
180        let t2 = t * t;
181        let t3 = t2 * t;
182        let two_t3 = t2 * two_t;
183        let two_t2 = t * two_t;
184        let three_t2 = t * three_t;
185
186        // tangents
187        let m0 = (b.1 - x.1) / (Self::from(b.0 - x.0)) * (Self::from(b.0 - a.0));
188        let m1 = (y.1 - a.1) / (Self::from(y.0 - a.0)) * (Self::from(b.0 - a.0));
189
190        a.1 * (two_t3 - three_t2 + 1.)
191          + m0 * (t3 - two_t2 + t)
192          + b.1 * (three_t2 - two_t3)
193          + m1 * (t3 - t2)
194      }
195
196      fn quadratic_bezier(t: $t, a: Self, u: Self, b: Self) -> Self {
197        let t = Self::from(t);
198        let one_t = 1. - t;
199        let one_t2 = one_t * one_t;
200
201        u + (a - u) * one_t2 + (b - u) * t * t
202      }
203
204      fn cubic_bezier(t: $t, a: Self, u: Self, v: Self, b: Self) -> Self {
205        let t = Self::from(t);
206        let one_t = 1. - t;
207        let one_t2 = one_t * one_t;
208        let one_t3 = one_t2 * one_t;
209        let t2 = t * t;
210
211        a * one_t3 + (u * one_t2 * t + v * one_t * t2) * 3. + b * t2 * t
212      }
213
214      fn cubic_bezier_mirrored(t: $t, a: Self, u: Self, v: Self, b: Self) -> Self {
215        <Self as $crate::interpolate::Interpolate<$t>>::cubic_bezier(t, a, u, b + b - v, b)
216      }
217    }
218  };
219}
220
221impl_Interpolate!(f32, f32, core::f32::consts::PI);
222impl_Interpolate!(f64, f64, core::f64::consts::PI);
223impl_InterpolateT!(f32, f64, core::f32::consts::PI);