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 FVec3 {
153    /// Create a new [`FVec3`] from its components.
154    ///
155    /// # Example
156    /// ```
157    /// # let _runner = test_runner::GdbRunner::default();
158    /// # use citro3d::math::FVec3;
159    /// let v = FVec3::new(1.0, 2.0, 3.0);
160    /// ```
161    #[doc(alias = "FVec3_New")]
162    pub fn new(x: f32, y: f32, z: f32) -> Self {
163        Self(unsafe { citro3d_sys::FVec3_New(x, y, z) })
164    }
165
166    /// Create a new [`FVec3`], setting each component to the given `v`.
167    ///
168    /// # Example
169    /// ```
170    /// # let _runner = test_runner::GdbRunner::default();
171    /// # use citro3d::math::FVec3;
172    /// let v = FVec3::splat(1.0);
173    /// ```
174    pub fn splat(v: f32) -> Self {
175        Self::new(v, v, v)
176    }
177
178    /// The distance between two points in 3D space.
179    ///
180    /// # Example
181    /// ```
182    /// # let _runner = test_runner::GdbRunner::default();
183    /// # use citro3d::math::FVec3;
184    /// # use approx::assert_abs_diff_eq;
185    /// let l = FVec3::new(1.0, 3.0, 4.0);
186    /// let r = FVec3::new(0.0, 1.0, 2.0);
187    ///
188    /// assert_abs_diff_eq!(l.distance(r), 3.0);
189    /// ```
190    #[doc(alias = "FVec3_Distance")]
191    pub fn distance(self, rhs: Self) -> f32 {
192        unsafe { citro3d_sys::FVec3_Distance(self.0, rhs.0) }
193    }
194
195    /// The cross product of two 3D vectors.
196    ///
197    /// # Example
198    /// ```
199    /// # let _runner = test_runner::GdbRunner::default();
200    /// # use citro3d::math::FVec3;
201    /// # use approx::assert_abs_diff_eq;
202    /// let l = FVec3::new(1.0, 0.0, 0.0);
203    /// let r = FVec3::new(0.0, 1.0, 0.0);
204    /// assert_abs_diff_eq!(l.cross(r), FVec3::new(0.0, 0.0, 1.0));
205    /// ```
206    #[doc(alias = "FVec3_Cross")]
207    pub fn cross(self, rhs: Self) -> Self {
208        Self(unsafe { citro3d_sys::FVec3_Cross(self.0, rhs.0) })
209    }
210
211    /// The dot product of two vectors.
212    ///
213    /// # Example
214    /// ```
215    /// # let _runner = test_runner::GdbRunner::default();
216    /// # use citro3d::math::FVec3;
217    /// # use approx::assert_abs_diff_eq;
218    /// let l = FVec3::new(1.0, 2.0, 3.0);
219    /// let r = FVec3::new(3.0, 2.0, 1.0);
220    /// assert_abs_diff_eq!(l.dot(r), 10.0);
221    /// ```
222    #[doc(alias = "FVec3_Dot")]
223    pub fn dot(self, rhs: Self) -> f32 {
224        unsafe { citro3d_sys::FVec3_Dot(self.0, rhs.0) }
225    }
226
227    /// The magnitude of the vector.
228    ///
229    /// # Example
230    /// ```
231    /// # let _runner = test_runner::GdbRunner::default();
232    /// # use citro3d::math::FVec3;
233    /// # use approx::assert_abs_diff_eq;
234    /// let v = FVec3::splat(3.0f32.sqrt());
235    /// assert_abs_diff_eq!(v.magnitude(), 3.0);
236    /// ```
237    #[doc(alias = "FVec3_Magnitude")]
238    pub fn magnitude(self) -> f32 {
239        unsafe { citro3d_sys::FVec3_Magnitude(self.0) }
240    }
241
242    /// Normalize the vector to a magnitude of `1.0`.
243    ///
244    /// # Example
245    /// ```
246    /// # let _runner = test_runner::GdbRunner::default();
247    /// # use citro3d::math::FVec3;
248    /// # use approx::assert_abs_diff_eq;
249    /// let v = FVec3::splat(1.0);
250    /// assert_abs_diff_eq!(v.normalize(), FVec3::splat(1.0 / 3.0_f32.sqrt()));
251    /// ```
252    #[doc(alias = "FVec3_Normalize")]
253    pub fn normalize(self) -> Self {
254        Self(unsafe { citro3d_sys::FVec3_Normalize(self.0) })
255    }
256}
257
258#[cfg(feature = "glam")]
259impl From<glam::Vec4> for FVec4 {
260    fn from(value: glam::Vec4) -> Self {
261        Self::new(value.x, value.y, value.z, value.w)
262    }
263}
264#[cfg(feature = "glam")]
265impl From<glam::Vec3> for FVec3 {
266    fn from(value: glam::Vec3) -> Self {
267        Self::new(value.x, value.y, value.z)
268    }
269}
270#[cfg(feature = "glam")]
271impl From<FVec4> for glam::Vec4 {
272    fn from(value: FVec4) -> Self {
273        glam::Vec4::new(value.x(), value.y(), value.z(), value.w())
274    }
275}
276
277#[cfg(feature = "glam")]
278impl From<FVec3> for glam::Vec3 {
279    fn from(value: FVec3) -> Self {
280        glam::Vec3::new(value.x(), value.y(), value.z())
281    }
282}
283
284#[cfg(test)]
285mod tests {
286    use approx::assert_abs_diff_eq;
287
288    use super::*;
289
290    #[test]
291    fn fvec4() {
292        let v = FVec4::new(1.0, 2.0, 3.0, 4.0);
293        let actual = [v.x(), v.y(), v.z(), v.w()];
294        let expected = [1.0, 2.0, 3.0, 4.0];
295        assert_abs_diff_eq!(&actual[..], &expected[..]);
296    }
297
298    #[test]
299    fn fvec3() {
300        let v = FVec3::new(1.0, 2.0, 3.0);
301        let actual = [v.x(), v.y(), v.z()];
302        let expected = [1.0, 2.0, 3.0];
303        assert_abs_diff_eq!(&actual[..], &expected[..]);
304    }
305}