2019-04-23 12:22:59 +02:00
|
|
|
|
//! The [`Interpolate`] trait and associated symbols.
|
|
|
|
|
//!
|
|
|
|
|
//! The [`Interpolate`] trait is the central concept of the crate. It enables a spline to be
|
|
|
|
|
//! sampled at by interpolating in between control points.
|
|
|
|
|
//!
|
2019-04-23 14:22:16 +02:00
|
|
|
|
//! In order for a type to be used in [`Spline<K, V>`], some properties must be met about the `K`
|
|
|
|
|
//! type must implementing several traits:
|
2019-04-23 12:22:59 +02:00
|
|
|
|
//!
|
2019-04-23 14:22:16 +02:00
|
|
|
|
//! - [`One`], giving a neutral element for the multiplication monoid.
|
|
|
|
|
//! - [`Additive`], making the type additive (i.e. one can add or subtract with it).
|
|
|
|
|
//! - [`Linear`], unlocking linear combinations, required for interpolating.
|
|
|
|
|
//! - [`Trigo`], a trait giving *π* and *cosine*, required for e.g. cosine interpolation.
|
|
|
|
|
//!
|
|
|
|
|
//! Feel free to have a look at current implementors for further help.
|
|
|
|
|
//!
|
|
|
|
|
//! > *Why doesn’t this crate use [num-traits] instead of
|
|
|
|
|
//! > defining its own traits?*
|
|
|
|
|
//!
|
|
|
|
|
//! The reason for this is quite simple: this crate provides a `no_std` support, which is not
|
|
|
|
|
//! currently available easily with [num-traits]. Also, if something changes in [num-traits] with
|
|
|
|
|
//! those traits, it would make this whole crate unstable.
|
2019-04-23 12:22:59 +02:00
|
|
|
|
//!
|
|
|
|
|
//! [`Interpolate`]: crate::interpolate::Interpolate
|
|
|
|
|
//! [`Spline<K, V>`]: crate::spline::Spline
|
|
|
|
|
//! [`One`]: crate::interpolate::One
|
|
|
|
|
//! [`Additive`]: crate::interpolate::Additive
|
|
|
|
|
//! [`Linear`]: crate::interpolate::Linear
|
|
|
|
|
//! [`Trigo`]: crate::interpolate::Trigo
|
2019-04-23 14:22:16 +02:00
|
|
|
|
//! [num-traits]: https://crates.io/crates/num-traits
|
2019-04-23 12:22:59 +02:00
|
|
|
|
|
2019-04-21 17:54:24 +02:00
|
|
|
|
#[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};
|
2019-04-19 13:39:37 +02:00
|
|
|
|
|
|
|
|
|
/// Keys that can be interpolated in between. Implementing this trait is required to perform
|
|
|
|
|
/// sampling on splines.
|
|
|
|
|
///
|
2019-04-23 12:22:59 +02:00
|
|
|
|
/// `T` is the variable used to sample with. Typical implementations use [`f32`] or [`f64`], but
|
|
|
|
|
/// you’re free to use the ones you like. Feel free to have a look at [`Spline::sample`] for
|
|
|
|
|
/// instance to know which trait your type must implement to be usable.
|
|
|
|
|
///
|
|
|
|
|
/// [`Spline::sample`]: crate::spline::Spline::sample
|
2019-04-19 13:39:37 +02:00
|
|
|
|
pub trait Interpolate<T>: Sized + Copy {
|
|
|
|
|
/// Linear interpolation.
|
|
|
|
|
fn lerp(a: Self, b: Self, t: T) -> Self;
|
|
|
|
|
|
|
|
|
|
/// Cubic hermite interpolation.
|
|
|
|
|
///
|
2019-04-23 12:22:59 +02:00
|
|
|
|
/// Default to [`lerp`].
|
|
|
|
|
///
|
|
|
|
|
/// [`lerp`]: Interpolate::lerp
|
2019-04-19 13:39:37 +02:00
|
|
|
|
fn cubic_hermite(_: (Self, T), a: (Self, T), b: (Self, T), _: (Self, T), t: T) -> Self {
|
|
|
|
|
Self::lerp(a.0, b.0, t)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-23 12:22:59 +02:00
|
|
|
|
/// Set of types that support additions and subtraction.
|
|
|
|
|
///
|
|
|
|
|
/// The [`Copy`] trait is also a supertrait as it’s likely to be used everywhere.
|
2019-04-21 17:54:24 +02:00
|
|
|
|
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> {
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-23 12:22:59 +02:00
|
|
|
|
/// Set of additive types that support outer multiplication and division, making them linear.
|
|
|
|
|
pub trait Linear<T>: Additive {
|
2019-04-21 17:54:24 +02:00
|
|
|
|
/// 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
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Apply an outer division law.
|
|
|
|
|
fn outer_div(self, t: $t) -> Self {
|
|
|
|
|
self / t
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl_linear_simple!(f32);
|
|
|
|
|
impl_linear_simple!(f64);
|
|
|
|
|
|
|
|
|
|
macro_rules! impl_linear_cast {
|
|
|
|
|
($t:ty, $q:ty) => {
|
|
|
|
|
impl Linear<$t> for $q {
|
|
|
|
|
fn outer_mul(self, t: $t) -> Self {
|
|
|
|
|
self * t as $q
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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 {
|
2019-04-23 12:22:59 +02:00
|
|
|
|
/// The neutral element for the multiplicative monoid — typically called `1`.
|
2019-04-21 17:54:24 +02:00
|
|
|
|
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) }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-23 14:22:16 +02:00
|
|
|
|
/// 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
|
2019-04-23 12:22:59 +02:00
|
|
|
|
where V: Linear<T>,
|
2019-04-21 17:54:24 +02:00
|
|
|
|
T: Additive + Mul<T, Output = T> + One {
|
2019-04-19 13:39:37 +02:00
|
|
|
|
// some stupid generic constants, because Rust doesn’t have polymorphic literals…
|
2019-04-21 17:54:24 +02:00
|
|
|
|
let one_t = T::one();
|
|
|
|
|
let two_t = one_t + one_t; // lolololol
|
|
|
|
|
let three_t = two_t + one_t; // megalol
|
2019-04-19 13:39:37 +02:00
|
|
|
|
|
|
|
|
|
// sampler stuff
|
|
|
|
|
let t2 = t * t;
|
|
|
|
|
let t3 = t2 * t;
|
|
|
|
|
let two_t3 = t3 * two_t;
|
|
|
|
|
let three_t2 = t2 * three_t;
|
|
|
|
|
|
|
|
|
|
// tangents
|
2019-04-21 17:54:24 +02:00
|
|
|
|
let m0 = (b.0 - x.0).outer_div(b.1 - x.1);
|
|
|
|
|
let m1 = (y.0 - a.0).outer_div(y.1 - a.1);
|
2019-04-19 13:39:37 +02:00
|
|
|
|
|
2019-04-21 17:54:24 +02:00
|
|
|
|
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)
|
2019-04-19 13:39:37 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
macro_rules! impl_interpolate_simple {
|
|
|
|
|
($t:ty) => {
|
|
|
|
|
impl Interpolate<$t> for $t {
|
|
|
|
|
fn lerp(a: Self, b: Self, t: $t) -> 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)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl_interpolate_via!(f32, f64);
|
|
|
|
|
impl_interpolate_via!(f64, f32);
|