retrofire_core/math/
space.rs

1//! Linear (vector) spaces and affine spaces.
2//!
3//! TODO
4
5use core::marker::PhantomData;
6
7use crate::math::vary::{Iter, Vary};
8
9/// Trait for types representing elements of an affine space.
10///
11/// # TODO
12/// * More documentation, definition of affine space
13pub trait Affine: Sized {
14    /// The space that `Self` is the element type of.
15    type Space;
16    /// The (signed) difference of two values of `Self`.
17    /// `Diff` must have the same dimension as `Self`.
18    type Diff: Linear;
19
20    /// The dimension of `Self`.
21    const DIM: usize;
22
23    /// Adds `diff` to `self` component-wise.
24    ///
25    /// `add` is commutative and associative.
26    fn add(&self, diff: &Self::Diff) -> Self;
27
28    /// Subtracts `other` from `self`, returning the (signed) difference.
29    ///
30    /// `sub` is anti-commutative: `v.sub(w) == w.sub(v).neg()`.
31    fn sub(&self, other: &Self) -> Self::Diff;
32}
33
34/// Trait for types representing elements of a linear space (vector space).
35///
36/// A `Linear` type is a type that is `Affine` and
37/// additionally satisfies the following conditions:
38///
39/// * The difference type [`Diff`][Affine::Diff] is equal to `Self`
40/// * The type has an additive identity, returned by the [`zero`][Self::zero] method
41/// * Every value has an additive inverse, returned by the [`neg`][Self::neg] method
42///
43/// # TODO
44/// * More documentation
45pub trait Linear: Affine<Diff = Self> {
46    /// The scalar type associated with `Self`.
47    type Scalar: Sized;
48
49    /// Returns the additive identity of `Self`.
50    fn zero() -> Self;
51
52    /// Returns the additive inverse of `self`.
53    fn neg(&self) -> Self;
54
55    /// Multiplies all components of `self` by `scalar`.
56    ///
57    /// `mul` is commutative and associative, and distributes over
58    /// `add` and `sub` (up to rounding errors):
59    /// ```
60    /// # use retrofire_core::math::space::{Affine, Linear};
61    /// # let [v, w, x, a] = [1.0f32, 2.0, 3.0, 4.0];
62    /// v.mul(w) == w.mul(v);
63    /// v.mul(w).mul(x) == v.mul(w.mul(x));
64    /// v.mul(a).add(&w.mul(a)) == v.add(&w).mul(a);
65    /// v.mul(a).sub(&w.mul(a)) == v.add(&w).sub(&a);
66    /// ```
67    fn mul(&self, scalar: Self::Scalar) -> Self;
68}
69
70/// Tag type for real vector spaces (Euclidean spaces) of dimension `DIM`.
71/// For example, the type `Real<3>` corresponds to ℝ³.
72#[derive(Copy, Clone, Default, Eq, PartialEq)]
73pub struct Real<const DIM: usize, Basis = ()>(PhantomData<Basis>);
74
75/// Tag type for the projective 4-space over reals, 𝗣<sub>4</sub>(ℝ).
76/// The properties of this space make it useful for implementing perspective
77/// projection. Clipping is also done in the projective space.
78#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
79pub struct Proj4;
80
81impl Affine for f32 {
82    type Space = ();
83    type Diff = Self;
84    const DIM: usize = 1;
85
86    fn add(&self, other: &f32) -> f32 {
87        self + other
88    }
89    fn sub(&self, other: &f32) -> f32 {
90        self - other
91    }
92}
93
94impl Linear for f32 {
95    type Scalar = f32;
96
97    fn zero() -> f32 {
98        0.0
99    }
100    fn neg(&self) -> f32 {
101        -*self
102    }
103    fn mul(&self, rhs: f32) -> f32 {
104        self * rhs
105    }
106}
107
108impl Affine for i32 {
109    type Space = ();
110    type Diff = Self;
111    const DIM: usize = 1;
112
113    fn add(&self, rhs: &i32) -> i32 {
114        self + rhs
115    }
116    fn sub(&self, rhs: &i32) -> i32 {
117        self - rhs
118    }
119}
120
121impl Linear for i32 {
122    type Scalar = Self;
123
124    fn zero() -> i32 {
125        0
126    }
127    fn neg(&self) -> i32 {
128        -self
129    }
130    fn mul(&self, rhs: i32) -> i32 {
131        self * rhs
132    }
133}
134
135impl Affine for u32 {
136    type Space = ();
137    type Diff = i32;
138    const DIM: usize = 1;
139
140    fn add(&self, rhs: &i32) -> u32 {
141        let (res, o) = self.overflowing_add_signed(*rhs);
142        debug_assert!(!o, "overflow adding {rhs}_i32 to {self}_u32");
143        res
144    }
145
146    fn sub(&self, rhs: &u32) -> i32 {
147        let diff = *self as i64 - *rhs as i64;
148        debug_assert!(
149            i32::try_from(diff).is_ok(),
150            "overflow subtracting {rhs}_u32 from {self}_u32"
151        );
152        diff as i32
153    }
154}
155
156impl<V: Clone> Vary for V
157where
158    Self: Linear<Scalar = f32>,
159{
160    type Iter = Iter<Self>;
161    type Diff = <Self as Affine>::Diff;
162
163    #[inline]
164    fn vary(self, step: Self::Diff, n: Option<u32>) -> Self::Iter {
165        Iter { val: self, step, n }
166    }
167
168    fn dv_dt(&self, other: &Self, recip_dt: f32) -> Self::Diff {
169        other.sub(self).mul(recip_dt)
170    }
171
172    /// Adds `delta` to `self`.
173    #[inline]
174    fn step(&self, delta: &Self::Diff) -> Self {
175        self.add(delta)
176    }
177
178    fn z_div(&self, z: f32) -> Self {
179        self.mul(z.recip())
180    }
181}
182
183#[cfg(test)]
184mod tests {
185    use super::*;
186
187    mod f32 {
188        use super::*;
189
190        #[test]
191        fn affine_ops() {
192            assert_eq!(f32::DIM, 1);
193
194            assert_eq!(1_f32.add(&2_f32), 3_f32);
195            assert_eq!(3_f32.add(&-2_f32), 1_f32);
196
197            assert_eq!(3_f32.sub(&2_f32), 1_f32);
198            assert_eq!(1_f32.sub(&4_f32), -3_f32);
199        }
200
201        #[test]
202        fn linear_ops() {
203            assert_eq!(f32::zero(), 0.0);
204
205            assert_eq!(2_f32.neg(), -2_f32);
206            assert_eq!(-3_f32.neg(), 3_f32);
207
208            assert_eq!(3_f32.mul(2_f32), 6_f32);
209            assert_eq!(3_f32.mul(0.5_f32), 1.5_f32);
210            assert_eq!(3_f32.mul(-2_f32), -6_f32);
211        }
212    }
213
214    mod i32 {
215        use super::*;
216
217        #[test]
218        fn affine_ops() {
219            assert_eq!(i32::DIM, 1);
220
221            assert_eq!(1_i32.add(&2_i32), 3_i32);
222            assert_eq!(2_i32.add(&-3_i32), -1_i32);
223
224            assert_eq!(3_i32.sub(&2_i32), 1_i32);
225            assert_eq!(3_i32.sub(&4_i32), -1_i32);
226        }
227
228        #[test]
229        fn linear_ops() {
230            assert_eq!(i32::zero(), 0);
231
232            assert_eq!(2_i32.neg(), -2_i32);
233            assert_eq!(-3_i32.neg(), 3_i32);
234
235            assert_eq!(3_i32.mul(2_i32), 6_i32);
236            assert_eq!(2_i32.mul(-3_i32), -6_i32);
237        }
238    }
239
240    mod u32 {
241        use super::*;
242
243        #[test]
244        fn affine_ops() {
245            assert_eq!(u32::DIM, 1);
246
247            assert_eq!(1_u32.add(&2_i32), 3_u32);
248            assert_eq!(3_u32.add(&-2_i32), 1_u32);
249
250            assert_eq!(3_u32.sub(&2_u32), 1_i32);
251            assert_eq!(3_u32.sub(&4_u32), -1_i32);
252        }
253
254        #[test]
255        #[should_panic]
256        fn affine_add_underflow_should_panic() {
257            _ = 3_u32.add(&-4_i32);
258        }
259
260        #[test]
261        #[should_panic]
262        fn affine_add_overflow_should_panic() {
263            _ = (u32::MAX / 2 + 2).add(&i32::MAX);
264        }
265
266        #[test]
267        #[should_panic]
268        fn affine_sub_underflow_should_panic() {
269            _ = 3_u32.sub(&u32::MAX);
270        }
271
272        #[test]
273        #[should_panic]
274        fn affine_sub_overflow_should_panic() {
275            _ = u32::MAX.sub(&1_u32);
276        }
277    }
278}