citro3d/math/
ops.rs

1use std::mem::MaybeUninit;
2use std::ops::{Add, Div, Mul, Neg, Sub};
3
4#[cfg(feature = "approx")]
5use approx::AbsDiffEq;
6
7use super::{FVec, FVec3, FVec4, Matrix4};
8
9// region: FVec4 math operators
10
11impl Add for FVec4 {
12    type Output = Self;
13
14    #[doc(alias = "FVec4_Add")]
15    fn add(self, rhs: Self) -> Self::Output {
16        Self(unsafe { citro3d_sys::FVec4_Add(self.0, rhs.0) })
17    }
18}
19
20impl Sub for FVec4 {
21    type Output = Self;
22
23    #[doc(alias = "FVec4_Subtract")]
24    fn sub(self, rhs: Self) -> Self::Output {
25        Self(unsafe { citro3d_sys::FVec4_Subtract(self.0, rhs.0) })
26    }
27}
28
29impl Neg for FVec4 {
30    type Output = Self;
31
32    #[doc(alias = "FVec4_Negate")]
33    fn neg(self) -> Self::Output {
34        Self(unsafe { citro3d_sys::FVec4_Negate(self.0) })
35    }
36}
37
38impl Mul<f32> for FVec4 {
39    type Output = Self;
40
41    #[doc(alias = "FVec4_Scale")]
42    fn mul(self, rhs: f32) -> Self::Output {
43        Self(unsafe { citro3d_sys::FVec4_Scale(self.0, rhs) })
44    }
45}
46
47// endregion
48
49// region: FVec3 math operators
50
51impl Add for FVec3 {
52    type Output = Self;
53
54    #[doc(alias = "FVec3_Add")]
55    fn add(self, rhs: Self) -> Self::Output {
56        Self(unsafe { citro3d_sys::FVec3_Add(self.0, rhs.0) })
57    }
58}
59
60impl Sub for FVec3 {
61    type Output = Self;
62
63    #[doc(alias = "FVec3_Subtract")]
64    fn sub(self, rhs: Self) -> Self::Output {
65        Self(unsafe { citro3d_sys::FVec3_Subtract(self.0, rhs.0) })
66    }
67}
68
69impl Neg for FVec3 {
70    type Output = Self;
71
72    #[doc(alias = "FVec3_Negate")]
73    fn neg(self) -> Self::Output {
74        Self(unsafe { citro3d_sys::FVec3_Negate(self.0) })
75    }
76}
77
78impl Mul<f32> for FVec3 {
79    type Output = Self;
80
81    #[doc(alias = "FVec3_Scale")]
82    fn mul(self, rhs: f32) -> Self::Output {
83        Self(unsafe { citro3d_sys::FVec3_Scale(self.0, rhs) })
84    }
85}
86
87// endregion
88
89impl<const N: usize> Div<f32> for FVec<N>
90where
91    FVec<N>: Mul<f32>,
92{
93    type Output = <Self as Mul<f32>>::Output;
94
95    fn div(self, rhs: f32) -> Self::Output {
96        self * (1.0 / rhs)
97    }
98}
99
100impl<const N: usize> PartialEq for FVec<N> {
101    fn eq(&self, other: &Self) -> bool {
102        let range = (4 - N)..;
103        unsafe { self.0.c[range.clone()] == other.0.c[range] }
104    }
105}
106
107impl<const N: usize> Eq for FVec<N> {}
108
109#[cfg(feature = "approx")]
110impl<const N: usize> AbsDiffEq for FVec<N> {
111    type Epsilon = f32;
112
113    fn default_epsilon() -> Self::Epsilon {
114        // See https://docs.rs/almost/latest/almost/#why-another-crate
115        // for rationale of using this over just EPSILON
116        f32::EPSILON.sqrt()
117    }
118
119    fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
120        let range = (4 - N)..;
121        let (lhs, rhs) = unsafe { (&self.0.c[range.clone()], &other.0.c[range]) };
122        lhs.abs_diff_eq(rhs, epsilon)
123    }
124}
125
126// region: Matrix math operators
127
128impl Add<Matrix4> for Matrix4 {
129    type Output = Matrix4;
130
131    #[doc(alias = "Mtx_Add")]
132    fn add(self, rhs: Matrix4) -> Self::Output {
133        let mut out = MaybeUninit::uninit();
134        unsafe {
135            citro3d_sys::Mtx_Add(out.as_mut_ptr(), self.as_raw(), rhs.as_raw());
136            Matrix4::from_raw(out.assume_init())
137        }
138    }
139}
140
141impl Sub<Matrix4> for Matrix4 {
142    type Output = Matrix4;
143
144    #[doc(alias = "Mtx_Subtract")]
145    fn sub(self, rhs: Matrix4) -> Self::Output {
146        let mut out = MaybeUninit::uninit();
147        unsafe {
148            citro3d_sys::Mtx_Subtract(out.as_mut_ptr(), self.as_raw(), rhs.as_raw());
149            Matrix4::from_raw(out.assume_init())
150        }
151    }
152}
153
154impl Mul<Matrix4> for Matrix4 {
155    type Output = Matrix4;
156
157    #[doc(alias = "Mtx_Multiply")]
158    fn mul(self, rhs: Matrix4) -> Self::Output {
159        let mut out = MaybeUninit::uninit();
160        unsafe {
161            citro3d_sys::Mtx_Multiply(out.as_mut_ptr(), self.as_raw(), rhs.as_raw());
162            Matrix4::from_raw(out.assume_init())
163        }
164    }
165}
166
167impl Mul<Matrix4> for &Matrix4 {
168    type Output = Matrix4;
169
170    fn mul(self, rhs: Matrix4) -> Self::Output {
171        *self * rhs
172    }
173}
174
175impl Mul<FVec4> for &Matrix4 {
176    type Output = FVec4;
177
178    #[doc(alias = "Mtx_MultiplyFVec4")]
179    fn mul(self, rhs: FVec4) -> Self::Output {
180        FVec(unsafe { citro3d_sys::Mtx_MultiplyFVec4(self.as_raw(), rhs.0) })
181    }
182}
183
184impl Mul<FVec3> for &Matrix4 {
185    type Output = FVec4;
186
187    #[doc(alias = "Mtx_MultiplyFVecH")]
188    fn mul(self, rhs: FVec3) -> Self::Output {
189        FVec(unsafe { citro3d_sys::Mtx_MultiplyFVecH(self.as_raw(), rhs.0) })
190    }
191}
192
193impl PartialEq<Matrix4> for Matrix4 {
194    fn eq(&self, other: &Matrix4) -> bool {
195        self.rows_wzyx() == other.rows_wzyx()
196    }
197}
198
199// endregion
200
201#[cfg(feature = "approx")]
202#[doc(cfg(feature = "approx"))]
203impl AbsDiffEq for Matrix4 {
204    type Epsilon = f32;
205
206    fn default_epsilon() -> Self::Epsilon {
207        // See https://docs.rs/almost/latest/almost/#why-another-crate
208        // for rationale of using this over just EPSILON
209        f32::EPSILON.sqrt()
210    }
211
212    fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
213        self.rows_wzyx()
214            .into_iter()
215            .zip(other.rows_wzyx())
216            .all(|(l, r)| l.abs_diff_eq(&r, epsilon))
217    }
218}
219
220#[cfg(test)]
221mod tests {
222    use approx::assert_abs_diff_eq;
223
224    use super::*;
225
226    #[test]
227    fn fvec3() {
228        let l = FVec3::splat(1.0);
229        let r = FVec3::splat(2.0);
230
231        assert_abs_diff_eq!(l + r, FVec3::splat(3.0));
232        assert_abs_diff_eq!(l - r, FVec3::splat(-1.0));
233        assert_abs_diff_eq!(-l, FVec3::splat(-1.0));
234        assert_abs_diff_eq!(l * 1.5, FVec3::splat(1.5));
235        assert_abs_diff_eq!(l / 2.0, FVec3::splat(0.5));
236    }
237
238    #[test]
239    fn fvec4() {
240        let l = FVec4::splat(1.0);
241        let r = FVec4::splat(2.0);
242
243        assert_abs_diff_eq!(l + r, FVec4::splat(3.0));
244        assert_abs_diff_eq!(l - r, FVec4::splat(-1.0));
245        assert_abs_diff_eq!(-l, FVec4::splat(-1.0));
246        assert_abs_diff_eq!(l * 1.5, FVec4::splat(1.5));
247        assert_abs_diff_eq!(l / 2.0, FVec4::splat(0.5));
248    }
249
250    #[test]
251    fn matrix4() {
252        let l = Matrix4::diagonal(1.0, 2.0, 3.0, 4.0);
253        let r = Matrix4::identity();
254
255        assert_abs_diff_eq!(l * r, l);
256        assert_abs_diff_eq!(l + r, Matrix4::diagonal(2.0, 3.0, 4.0, 5.0));
257        assert_abs_diff_eq!(l - r, Matrix4::diagonal(0.0, 1.0, 2.0, 3.0));
258    }
259}