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}