retrofire_core/
math.rs

1//! Linear algebra and other useful mathematics.
2//!
3//! Includes [vectors][self::vec], [matrices][mat], [colors][color],
4//! [angles][angle], [Bezier splines][spline] and [pseudo-random numbers][rand],
5//! as well as support for custom [varying][vary] types and utilities such as
6//! approximate equality comparisons.
7//!
8//! This library is more strongly typed than many other similar math libraries.
9//! It aims  to diagnose at compile time many errors that might otherwise only
10//! manifest as graphical glitches, runtime panics, or even – particularly in
11//! languages that are unsafe-by-default – undefined behavior.
12//!
13//! In particular, vectors and colors are tagged with a type that represents
14//! the *space* they're embedded in, and values in different spaces cannot be
15//! mixed without explicit conversion (transformation). Matrices, similarly,
16//! are tagged by both source and destination space, and can only be applied
17//! to matching vectors. Angles are strongly typed as well, to allow working
18//! with different angular units without confusion.
19
20pub use {
21    angle::{
22        Angle, PolarVec, SphericalVec, degs, polar, rads, spherical, turns,
23    },
24    approx::ApproxEq,
25    color::{Color, Color3, Color3f, Color4, Color4f, rgb, rgba},
26    mat::{
27        Apply, Mat2x2, Mat3x3, Mat4x4, Matrix, orthographic, perspective,
28        scale, scale3, translate, translate3, viewport,
29    },
30    param::Parametric,
31    point::{Point, Point2, Point2u, Point3, pt2, pt3},
32    space::{Affine, Linear},
33    spline::{BezierSpline, CubicBezier, smootherstep, smoothstep},
34    vary::Vary,
35    vec::{Vec2, Vec2i, Vec3, Vec3i, Vector, splat, vec2, vec3},
36};
37#[cfg(feature = "fp")]
38pub use {
39    angle::{acos, asin, atan2},
40    mat::{orient_y, orient_z, rotate, rotate_x, rotate_y, rotate_z, rotate2},
41};
42
43/// Implements an operator trait in terms of an op-assign trait.
44macro_rules! impl_op {
45    ($trait:ident :: $method:ident, $self:ident, $rhs:ty, $op:tt) => {
46        impl_op!($trait::$method, $self, $rhs, $op, bound=Linear);
47    };
48    ($trait:ident :: $method:ident, $self:ident, $rhs:ty, $op:tt, bound=$bnd:path) => {
49        impl<R, Sp> $trait<$rhs> for $self<R, Sp>
50        where
51            Self: $bnd,
52        {
53            type Output = Self;
54            /// TODO
55            #[inline]
56            fn $method(mut self, rhs: $rhs) -> Self {
57                self $op rhs; self
58            }
59        }
60    };
61}
62
63pub mod angle;
64pub mod approx;
65pub mod color;
66pub mod float;
67pub mod mat;
68pub mod param;
69pub mod point;
70pub mod rand;
71pub mod space;
72pub mod spline;
73pub mod vary;
74pub mod vec;
75
76/// Trait for linear interpolation between two values.
77pub trait Lerp: Sized {
78    /// Linearly interpolates between `self` and `other`.
79    ///
80    /// if `t` = 0, returns `self`; if `t` = 1, returns `other`.
81    /// For 0 < `t` < 1, returns the weighted average of `self` and `other`
82    /// ```text
83    /// (1 - t) * self + t * other
84    /// ```
85    ///
86    /// This method does not panic if `t < 0.0` or `t > 1.0`, or if `t`
87    /// is `NaN`, but the return value in those cases is unspecified.
88    /// Individual implementations may offer stronger guarantees.
89    ///
90    /// # Examples
91    /// ```
92    /// use retrofire_core::math::Lerp;
93    ///
94    /// assert_eq!(f32::lerp(&1.0, &5.0, 0.25), 2.0);
95    /// ```
96    fn lerp(&self, other: &Self, t: f32) -> Self;
97
98    /// Returns the (unweighted) average of `self` and `other`.
99    ///
100    /// # Examples
101    /// ```
102    /// use retrofire_core::math::{Lerp, pt2, Point2};
103    ///
104    /// let a: Point2 = pt2(-1.0, 2.0);
105    /// let b = pt2(3.0, -2.0);
106    /// assert_eq!(a.midpoint(&b), pt2(1.0, 0.0));
107    /// ```
108    fn midpoint(&self, other: &Self) -> Self {
109        self.lerp(other, 0.5)
110    }
111}
112
113/// Linearly interpolates between two values.
114///
115/// For examples and more information, see [`Lerp::lerp`].
116#[inline]
117pub fn lerp<T: Lerp>(t: f32, from: T, to: T) -> T {
118    from.lerp(&to, t)
119}
120
121/// Returns the relative position of `t` between `min` and `max`.
122///
123/// That is, returns 0 when `t` = `min`, 1 when `t` = `max`, and linearly
124/// interpolates in between.
125///
126/// The result is unspecified if any of the parameters is non-finite, or if
127/// `min` = `max`.
128///
129/// # Examples
130/// ```
131/// use retrofire_core::math::inv_lerp;
132///
133/// // Two is one fourth of the way from one to five
134/// assert_eq!(inv_lerp(2.0, 1.0, 5.0), 0.25);
135///
136/// // Zero is halfway between -2 and 2
137/// assert_eq!(inv_lerp(0.0, -2.0, 2.0), 0.5);
138/// ```
139#[inline]
140pub fn inv_lerp(t: f32, min: f32, max: f32) -> f32 {
141    (t - min) / (max - min)
142}
143
144impl<T> Lerp for T
145where
146    T: Affine<Diff: Linear<Scalar = f32>>,
147{
148    /// Linearly interpolates between `self` and `other`.
149    ///
150    /// if `t` = 0, returns `self`; if `t` = 1, returns `other`.
151    /// For 0 < `t` < 1, returns the affine combination
152    /// ```text
153    /// (1 - t) * self + t * other
154    /// ```
155    /// or rearranged:
156    /// ```text
157    /// self + t * (other - self)
158    /// ```
159    ///
160    /// If `t < 0.0` or `t > 1.0`, returns the appropriate extrapolated value.
161    /// If `t` is NaN, the result is unspecified.
162    ///
163    /// # Examples
164    /// ```
165    /// use retrofire_core::math::*;
166    ///
167    /// assert_eq!(2.0.lerp(&5.0, 0.0), 2.0);
168    /// assert_eq!(2.0.lerp(&5.0, 0.25), 2.75);
169    /// assert_eq!(2.0.lerp(&5.0, 0.75), 4.25);
170    /// assert_eq!(2.0.lerp(&5.0, 1.0), 5.0);
171    ///
172    /// let v0: Vec2 = vec2(-2.0, 1.0);
173    /// let v1 = vec2(3.0, -1.0);
174    /// assert_eq!(v0.lerp(&v1, 0.8), vec2(2.0, -0.6));
175    ///
176    /// let p0: Point2 = pt2(-10.0, 5.0);
177    /// let p1 = pt2(-5.0, 0.0);
178    /// assert_eq!(p0.lerp(&p1, 0.4),pt2(-8.0, 3.0));
179    /// ```
180    fn lerp(&self, other: &Self, t: f32) -> Self {
181        self.add(&other.sub(self).mul(t))
182    }
183}
184
185impl Lerp for () {
186    fn lerp(&self, _: &Self, _: f32) {}
187}
188
189impl<U: Lerp, V: Lerp> Lerp for (U, V) {
190    fn lerp(&self, (u, v): &Self, t: f32) -> Self {
191        (self.0.lerp(u, t), self.1.lerp(v, t))
192    }
193}