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
9impl 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
47impl 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
87impl<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 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
126impl 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#[cfg(feature = "approx")]
202#[doc(cfg(feature = "approx"))]
203impl AbsDiffEq for Matrix4 {
204 type Epsilon = f32;
205
206 fn default_epsilon() -> Self::Epsilon {
207 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}