citro3d/math/
fvec.rs

1//! Floating-point vectors.
2
3use std::fmt;
4
5/// A vector of `f32`s.
6///
7/// # Layout
8/// Note that this matches the PICA layout so is actually WZYX, this means using it
9/// in vertex data as an attribute it will be reversed
10///
11/// It is guaranteed to have the same layout as [`citro3d_sys::C3D_FVec`] in memory
12#[derive(Clone, Copy)]
13#[doc(alias = "C3D_FVec")]
14#[repr(transparent)]
15pub struct FVec<const N: usize>(pub(crate) citro3d_sys::C3D_FVec);
16
17/// A 3-vector of `f32`s.
18pub type FVec3 = FVec<3>;
19
20/// A 4-vector of `f32`s.
21pub type FVec4 = FVec<4>;
22
23impl<const N: usize> fmt::Debug for FVec<N> {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        let inner = unsafe { self.0.__bindgen_anon_1 };
26        let type_name = std::any::type_name::<Self>().split("::").last().unwrap();
27        f.debug_tuple(type_name).field(&inner).finish()
28    }
29}
30
31impl<const N: usize> FVec<N> {
32    /// The vector's `x` component (also called the `i` component of `ijk[r]`).
33    #[doc(alias = "i")]
34    pub fn x(self) -> f32 {
35        unsafe { self.0.__bindgen_anon_1.x }
36    }
37
38    /// The vector's `y` component (also called the `j` component of `ijk[r]`).
39    #[doc(alias = "j")]
40    pub fn y(self) -> f32 {
41        unsafe { self.0.__bindgen_anon_1.y }
42    }
43
44    /// The vector's `i` component (also called the `k` component of `ijk[r]`).
45    #[doc(alias = "k")]
46    pub fn z(self) -> f32 {
47        unsafe { self.0.__bindgen_anon_1.z }
48    }
49}
50
51impl FVec4 {
52    /// The vector's `w` component (also called `r` for the real component of `ijk[r]`).
53    #[doc(alias = "r")]
54    pub fn w(self) -> f32 {
55        unsafe { self.0.__bindgen_anon_1.w }
56    }
57
58    /// Wrap a raw [`citro3d_sys::C3D_FVec`]
59    pub fn from_raw(raw: citro3d_sys::C3D_FVec) -> Self {
60        Self(raw)
61    }
62
63    /// Create a new [`FVec4`] from its components.
64    ///
65    /// # Example
66    /// ```
67    /// # let _runner = test_runner::GdbRunner::default();
68    /// # use citro3d::math::FVec4;
69    /// let v = FVec4::new(1.0, 2.0, 3.0, 4.0);
70    /// ```
71    #[doc(alias = "FVec4_New")]
72    pub fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
73        Self(unsafe { citro3d_sys::FVec4_New(x, y, z, w) })
74    }
75
76    /// Create a new [`FVec4`], setting each component to `v`.
77    ///
78    /// # Example
79    /// ```
80    /// # let _runner = test_runner::GdbRunner::default();
81    /// # use citro3d::math::FVec4;
82    /// # use approx::assert_abs_diff_eq;
83    /// let v = FVec4::splat(1.0);
84    /// assert_abs_diff_eq!(v, FVec4::new(1.0, 1.0, 1.0, 1.0));
85    /// ```
86    pub fn splat(v: f32) -> Self {
87        Self::new(v, v, v, v)
88    }
89
90    /// Divide the vector's XYZ components by its W component.
91    ///
92    /// # Example
93    /// ```
94    /// # let _runner = test_runner::GdbRunner::default();
95    /// # use citro3d::math::FVec4;
96    /// # use approx::assert_abs_diff_eq;
97    /// let v = FVec4::new(2.0, 4.0, 6.0, 2.0);
98    /// assert_abs_diff_eq!(v.perspective_divide(), FVec4::new(1.0, 2.0, 3.0, 1.0));
99    /// ```
100    #[doc(alias = "FVec4_PerspDivide")]
101    pub fn perspective_divide(self) -> Self {
102        Self(unsafe { citro3d_sys::FVec4_PerspDivide(self.0) })
103    }
104
105    /// The dot product of two vectors.
106    ///
107    /// # Example
108    /// ```
109    /// # let _runner = test_runner::GdbRunner::default();
110    /// # use citro3d::math::FVec4;
111    /// # use approx::assert_abs_diff_eq;
112    /// let v1 = FVec4::new(1.0, 2.0, 3.0, 4.0);
113    /// let v2 = FVec4::new(1.0, 0.5, 1.0, 0.5);
114    /// assert_abs_diff_eq!(v1.dot(v2), 7.0);
115    /// ```
116    #[doc(alias = "FVec4_Dot")]
117    pub fn dot(self, rhs: Self) -> f32 {
118        unsafe { citro3d_sys::FVec4_Dot(self.0, rhs.0) }
119    }
120
121    /// The magnitude of the vector.
122    ///
123    /// # Example
124    /// ```
125    /// # let _runner = test_runner::GdbRunner::default();
126    /// # use citro3d::math::FVec4;
127    /// # use approx::assert_abs_diff_eq;
128    /// let v = FVec4::splat(1.0);
129    /// assert_abs_diff_eq!(v.magnitude(), 2.0);
130    /// ```
131    #[doc(alias = "FVec4_Magnitude")]
132    pub fn magnitude(self) -> f32 {
133        unsafe { citro3d_sys::FVec4_Magnitude(self.0) }
134    }
135
136    /// Normalize the vector to a magnitude of `1.0`.
137    ///
138    /// # Example
139    /// ```
140    /// # let _runner = test_runner::GdbRunner::default();
141    /// # use citro3d::math::FVec4;
142    /// # use approx::assert_abs_diff_eq;
143    /// let v = FVec4::new(1.0, 2.0, 2.0, 4.0);
144    /// assert_abs_diff_eq!(v.normalize(), FVec4::new(0.2, 0.4, 0.4, 0.8));
145    /// ```
146    #[doc(alias = "FVec4_Normalize")]
147    pub fn normalize(self) -> Self {
148        Self(unsafe { citro3d_sys::FVec4_Normalize(self.0) })
149    }
150}
151
152impl From<FVec4> for [f32; 4] {
153    fn from(value: FVec4) -> Self {
154        [value.x(), value.y(), value.z(), value.w()]
155    }
156}
157
158impl FVec3 {
159    /// Create a new [`FVec3`] from its components.
160    ///
161    /// # Example
162    /// ```
163    /// # let _runner = test_runner::GdbRunner::default();
164    /// # use citro3d::math::FVec3;
165    /// let v = FVec3::new(1.0, 2.0, 3.0);
166    /// ```
167    #[doc(alias = "FVec3_New")]
168    pub fn new(x: f32, y: f32, z: f32) -> Self {
169        Self(unsafe { citro3d_sys::FVec3_New(x, y, z) })
170    }
171
172    /// Create a new [`FVec3`], setting each component to the given `v`.
173    ///
174    /// # Example
175    /// ```
176    /// # let _runner = test_runner::GdbRunner::default();
177    /// # use citro3d::math::FVec3;
178    /// let v = FVec3::splat(1.0);
179    /// ```
180    pub fn splat(v: f32) -> Self {
181        Self::new(v, v, v)
182    }
183
184    /// The distance between two points in 3D space.
185    ///
186    /// # Example
187    /// ```
188    /// # let _runner = test_runner::GdbRunner::default();
189    /// # use citro3d::math::FVec3;
190    /// # use approx::assert_abs_diff_eq;
191    /// let l = FVec3::new(1.0, 3.0, 4.0);
192    /// let r = FVec3::new(0.0, 1.0, 2.0);
193    ///
194    /// assert_abs_diff_eq!(l.distance(r), 3.0);
195    /// ```
196    #[doc(alias = "FVec3_Distance")]
197    pub fn distance(self, rhs: Self) -> f32 {
198        unsafe { citro3d_sys::FVec3_Distance(self.0, rhs.0) }
199    }
200
201    /// The cross product of two 3D vectors.
202    ///
203    /// # Example
204    /// ```
205    /// # let _runner = test_runner::GdbRunner::default();
206    /// # use citro3d::math::FVec3;
207    /// # use approx::assert_abs_diff_eq;
208    /// let l = FVec3::new(1.0, 0.0, 0.0);
209    /// let r = FVec3::new(0.0, 1.0, 0.0);
210    /// assert_abs_diff_eq!(l.cross(r), FVec3::new(0.0, 0.0, 1.0));
211    /// ```
212    #[doc(alias = "FVec3_Cross")]
213    pub fn cross(self, rhs: Self) -> Self {
214        Self(unsafe { citro3d_sys::FVec3_Cross(self.0, rhs.0) })
215    }
216
217    /// The dot product of two vectors.
218    ///
219    /// # Example
220    /// ```
221    /// # let _runner = test_runner::GdbRunner::default();
222    /// # use citro3d::math::FVec3;
223    /// # use approx::assert_abs_diff_eq;
224    /// let l = FVec3::new(1.0, 2.0, 3.0);
225    /// let r = FVec3::new(3.0, 2.0, 1.0);
226    /// assert_abs_diff_eq!(l.dot(r), 10.0);
227    /// ```
228    #[doc(alias = "FVec3_Dot")]
229    pub fn dot(self, rhs: Self) -> f32 {
230        unsafe { citro3d_sys::FVec3_Dot(self.0, rhs.0) }
231    }
232
233    /// The magnitude of the vector.
234    ///
235    /// # Example
236    /// ```
237    /// # let _runner = test_runner::GdbRunner::default();
238    /// # use citro3d::math::FVec3;
239    /// # use approx::assert_abs_diff_eq;
240    /// let v = FVec3::splat(3.0f32.sqrt());
241    /// assert_abs_diff_eq!(v.magnitude(), 3.0);
242    /// ```
243    #[doc(alias = "FVec3_Magnitude")]
244    pub fn magnitude(self) -> f32 {
245        unsafe { citro3d_sys::FVec3_Magnitude(self.0) }
246    }
247
248    /// Normalize the vector to a magnitude of `1.0`.
249    ///
250    /// # Example
251    /// ```
252    /// # let _runner = test_runner::GdbRunner::default();
253    /// # use citro3d::math::FVec3;
254    /// # use approx::assert_abs_diff_eq;
255    /// let v = FVec3::splat(1.0);
256    /// assert_abs_diff_eq!(v.normalize(), FVec3::splat(1.0 / 3.0_f32.sqrt()));
257    /// ```
258    #[doc(alias = "FVec3_Normalize")]
259    pub fn normalize(self) -> Self {
260        Self(unsafe { citro3d_sys::FVec3_Normalize(self.0) })
261    }
262}
263
264#[cfg(feature = "glam")]
265impl From<glam::Vec4> for FVec4 {
266    fn from(value: glam::Vec4) -> Self {
267        Self::new(value.x, value.y, value.z, value.w)
268    }
269}
270#[cfg(feature = "glam")]
271impl From<glam::Vec3> for FVec3 {
272    fn from(value: glam::Vec3) -> Self {
273        Self::new(value.x, value.y, value.z)
274    }
275}
276#[cfg(feature = "glam")]
277impl From<FVec4> for glam::Vec4 {
278    fn from(value: FVec4) -> Self {
279        glam::Vec4::new(value.x(), value.y(), value.z(), value.w())
280    }
281}
282
283#[cfg(feature = "glam")]
284impl From<FVec3> for glam::Vec3 {
285    fn from(value: FVec3) -> Self {
286        glam::Vec3::new(value.x(), value.y(), value.z())
287    }
288}
289
290#[cfg(test)]
291mod tests {
292    use approx::assert_abs_diff_eq;
293
294    use super::*;
295
296    #[test]
297    fn fvec4() {
298        let v = FVec4::new(1.0, 2.0, 3.0, 4.0);
299        let actual = [v.x(), v.y(), v.z(), v.w()];
300        let expected = [1.0, 2.0, 3.0, 4.0];
301        assert_abs_diff_eq!(&actual[..], &expected[..]);
302    }
303
304    #[test]
305    fn fvec3() {
306        let v = FVec3::new(1.0, 2.0, 3.0);
307        let actual = [v.x(), v.y(), v.z()];
308        let expected = [1.0, 2.0, 3.0];
309        assert_abs_diff_eq!(&actual[..], &expected[..]);
310    }
311}