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}