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}