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#[cfg(feature = "std")] use std::f32;
32#[cfg(not(feature = "std"))] use core::f32;
33#[cfg(not(feature = "std"))] use core::intrinsics::cosf32;
34#[cfg(feature = "std")] use std::f64;
35#[cfg(not(feature = "std"))] use core::f64;
36#[cfg(not(feature = "std"))] use core::intrinsics::cosf64;
37#[cfg(feature = "std")] use std::ops::{Add, Mul, Sub};
38#[cfg(not(feature = "std"))] use core::ops::{Add, Mul, Sub};
39
40/// Keys that can be interpolated in between. Implementing this trait is required to perform
41/// sampling on splines.
42///
43/// `T` is the variable used to sample with. Typical implementations use [`f32`] or [`f64`], but
44/// you’re free to use the ones you like. Feel free to have a look at [`Spline::sample`] for
45/// instance to know which trait your type must implement to be usable.
46///
47/// [`Spline::sample`]: crate::spline::Spline::sample
48pub trait Interpolate<T>: Sized + Copy {
49  /// Linear interpolation.
50  fn lerp(a: Self, b: Self, t: T) -> Self;
51
52  /// Cubic hermite interpolation.
53  ///
54  /// Default to [`lerp`].
55  ///
56  /// [`lerp`]: Interpolate::lerp
57  fn cubic_hermite(_: (Self, T), a: (Self, T), b: (Self, T), _: (Self, T), t: T) -> Self {
58    Self::lerp(a.0, b.0, t)
59  }
60
61  /// Quadratic Bézier interpolation.
62  fn quadratic_bezier(a: Self, u: Self, b: Self, t: T) -> Self;
63
64  /// Cubic Bézier interpolation.
65  fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: T) -> Self;
66}
67
68/// Set of types that support additions and subtraction.
69///
70/// The [`Copy`] trait is also a supertrait as it’s likely to be used everywhere.
71pub trait Additive:
72  Copy +
73  Add<Self, Output = Self> +
74  Sub<Self, Output = Self> {
75}
76
77impl<T> Additive for T
78where T: Copy +
79         Add<Self, Output = Self> +
80         Sub<Self, Output = Self> {
81}
82
83/// Set of additive types that support outer multiplication and division, making them linear.
84pub trait Linear<T>: Additive {
85  /// Apply an outer multiplication law.
86  fn outer_mul(self, t: T) -> Self;
87
88  /// Apply an outer division law.
89  fn outer_div(self, t: T) -> Self;
90}
91
92macro_rules! impl_linear_simple {
93  ($t:ty) => {
94    impl Linear<$t> for $t {
95      fn outer_mul(self, t: $t) -> Self {
96        self * t
97      }
98
99      /// Apply an outer division law.
100      fn outer_div(self, t: $t) -> Self {
101        self / t
102      }
103    }
104  }
105}
106
107impl_linear_simple!(f32);
108impl_linear_simple!(f64);
109
110macro_rules! impl_linear_cast {
111  ($t:ty, $q:ty) => {
112    impl Linear<$t> for $q {
113      fn outer_mul(self, t: $t) -> Self {
114        self * t as $q
115      }
116
117      /// Apply an outer division law.
118      fn outer_div(self, t: $t) -> Self {
119        self / t as $q
120      }
121    }
122  }
123}
124
125impl_linear_cast!(f32, f64);
126impl_linear_cast!(f64, f32);
127
128/// Types with a neutral element for multiplication.
129pub trait One {
130  /// The neutral element for the multiplicative monoid — typically called `1`.
131  fn one() -> Self;
132}
133
134macro_rules! impl_one_float {
135  ($t:ty) => {
136    impl One for $t {
137      #[inline(always)]
138      fn one() -> Self {
139        1.
140      }
141    }
142  }
143}
144
145impl_one_float!(f32);
146impl_one_float!(f64);
147
148/// Types with a sane definition of π and cosine.
149pub trait Trigo {
150  /// π.
151  fn pi() -> Self;
152
153  /// Cosine of the argument.
154  fn cos(self) -> Self;
155}
156
157impl Trigo for f32 {
158  #[inline(always)]
159  fn pi() -> Self {
160    f32::consts::PI
161  }
162
163  #[inline(always)]
164  fn cos(self) -> Self {
165    #[cfg(feature = "std")]
166    {
167      self.cos()
168    }
169
170    #[cfg(not(feature = "std"))]
171    {
172      unsafe { cosf32(self) }
173    }
174  }
175}
176
177impl Trigo for f64 {
178  #[inline(always)]
179  fn pi() -> Self {
180    f64::consts::PI
181  }
182
183  #[inline(always)]
184  fn cos(self) -> Self {
185    #[cfg(feature = "std")]
186    {
187      self.cos()
188    }
189
190    #[cfg(not(feature = "std"))]
191    {
192      unsafe { cosf64(self) }
193    }
194  }
195}
196
197/// Default implementation of [`Interpolate::cubic_hermite`].
198///
199/// `V` is the value being interpolated. `T` is the sampling value (also sometimes called time).
200pub fn cubic_hermite_def<V, T>(x: (V, T), a: (V, T), b: (V, T), y: (V, T), t: T) -> V
201where V: Linear<T>,
202      T: Additive + Mul<T, Output = T> + One {
203  // some stupid generic constants, because Rust doesn’t have polymorphic literals…
204  let one_t = T::one();
205  let two_t = one_t + one_t; // lolololol
206  let three_t = two_t + one_t; // megalol
207
208  // sampler stuff
209  let t2 = t * t;
210  let t3 = t2 * t;
211  let two_t3 = t3 * two_t;
212  let three_t2 = t2 * three_t;
213
214  // tangents
215  let m0 = (b.0 - x.0).outer_div(b.1 - x.1);
216  let m1 = (y.0 - a.0).outer_div(y.1 - a.1);
217
218  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)
219}
220
221/// Default implementation of [`Interpolate::quadratic_bezier`].
222///
223/// `V` is the value being interpolated. `T` is the sampling value (also sometimes called time).
224pub fn quadratic_bezier_def<V, T>(a: V, u: V, b: V, t: T) -> V
225where V: Linear<T>,
226      T: Additive + Mul<T, Output = T> + One {
227  let one_t = T::one() - t;
228  let one_t_2 = one_t * one_t;
229  u + (a - u).outer_mul(one_t_2) + (b - u).outer_mul(t * t)
230}
231
232/// Default implementation of [`Interpolate::cubic_bezier`].
233///
234/// `V` is the value being interpolated. `T` is the sampling value (also sometimes called time).
235pub fn cubic_bezier_def<V, T>(a: V, u: V, v: V, b: V, t: T) -> V
236where V: Linear<T>,
237      T: Additive + Mul<T, Output = T> + One {
238  let one_t = T::one() - t;
239  let one_t_2 = one_t * one_t;
240  let one_t_3 = one_t_2 * one_t;
241  let three = T::one() + T::one() + T::one();
242
243  // mirror the “output” tangent based on the next key “input” tangent
244  let v_ = b + b - v;
245
246  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)
247}
248
249macro_rules! impl_interpolate_simple {
250  ($t:ty) => {
251    impl Interpolate<$t> for $t {
252      fn lerp(a: Self, b: Self, t: $t) -> Self {
253        a * (1. - t) + b * t
254      }
255
256      fn cubic_hermite(x: (Self, $t), a: (Self, $t), b: (Self, $t), y: (Self, $t), t: $t) -> Self {
257        cubic_hermite_def(x, a, b, y, t)
258      }
259
260      fn quadratic_bezier(a: Self, u: Self, b: Self, t: $t) -> Self {
261        quadratic_bezier_def(a, u, b, t)
262      }
263
264      fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: $t) -> Self {
265        cubic_bezier_def(a, u, v, b, t)
266      }
267    }
268  }
269}
270
271impl_interpolate_simple!(f32);
272impl_interpolate_simple!(f64);
273
274macro_rules! impl_interpolate_via {
275  ($t:ty, $v:ty) => {
276    impl Interpolate<$t> for $v {
277      fn lerp(a: Self, b: Self, t: $t) -> Self {
278        a * (1. - t as $v) + b * t as $v
279      }
280
281      fn cubic_hermite((x, xt): (Self, $t), (a, at): (Self, $t), (b, bt): (Self, $t), (y, yt): (Self, $t), t: $t) -> Self {
282        cubic_hermite_def((x, xt as $v), (a, at as $v), (b, bt as $v), (y, yt as $v), t as $v)
283      }
284
285      fn quadratic_bezier(a: Self, u: Self, b: Self, t: $t) -> Self {
286        quadratic_bezier_def(a, u, b, t as $v)
287      }
288
289      fn cubic_bezier(a: Self, u: Self, v: Self, b: Self, t: $t) -> Self {
290        cubic_bezier_def(a, u, v, b, t as $v)
291      }
292    }
293  }
294}
295
296impl_interpolate_via!(f32, f64);
297impl_interpolate_via!(f64, f32);