Skip to main content

moldyn_core/
vec3.rs

1//! This module defines the [Vec3] struct, which represents a mathematical three-
2//! dimensional vector.
3//!
4//! See the C++ implementation of the Vec3 class for comparison:
5//! <https://github.com/Anatoly03/MolSim-WS25-GroupA/blob/assignment5-local-backup/src/core/math/Vec3.h>
6
7use serde::{Deserialize, Serialize, de::Visitor};
8use std::{
9    fmt::Display,
10    marker::PhantomData,
11    ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
12};
13
14/// A struct representing a three-dimensional [mathematical vector](https://en.wikipedia.org/wiki/Vector_%28mathematics_and_physics%29).
15/// These are used to represent particle information, such as position
16/// or velocity, in three-dimensional space.
17#[derive(Debug, PartialEq, Clone, Serialize, Default)]
18pub struct Vec3<T = f64> {
19    pub x: T,
20    pub y: T,
21    pub z: T,
22}
23
24// consider alternative implementation (overkill for this project):
25// pub struct IVec<const N: usize, T = f64> ([T; N]);
26
27// generic vec3 method
28impl<T> Vec3<T> {
29    /// Creates a new [Vec3] instance with the given x, y, and z components.
30    ///
31    /// # Example
32    ///
33    /// ```rust
34    /// use moldyn_core::Vec3;
35    ///
36    /// let v: Vec3<f64> = Vec3::new(1.0, 2.0, 3.0);
37    /// let w: Vec3<i32> = Vec3::new(1, 2, 3);
38    ///
39    /// assert_eq!(v.x, 1.0);
40    /// assert_eq!(v.y, 2.0);
41    /// assert_eq!(v.z, 3.0);
42    /// assert_eq!(w.x, 1);
43    /// assert_eq!(w.y, 2);
44    /// assert_eq!(w.z, 3);
45    /// ```
46    pub fn new(x: T, y: T, z: T) -> Self {
47        Self { x, y, z }
48    }
49
50    /// Creates a new [Vec3] instance with all components set to zero. Requires that
51    /// the generic type implements the [Default] trait.
52    pub fn zero() -> Self
53    where
54        T: Default,
55    {
56        Self {
57            x: T::default(),
58            y: T::default(),
59            z: T::default(),
60        }
61    }
62
63    /// Maps the individual components of the [Vec3] instance to a new type
64    /// with the provided lambda expression.
65    pub fn map<U, F>(self, f: F) -> Vec3<U>
66    where
67        F: Fn(T) -> U,
68    {
69        Vec3 {
70            x: f(self.x),
71            y: f(self.y),
72            z: f(self.z),
73        }
74    }
75}
76
77//
78// THE FOLLOWING ARE TRAIT IMPLEMENTATIONS FOR THE [Vec3] STRUCT
79//
80
81// vector negation
82impl<T: Neg<Output = T>> Neg for Vec3<T> {
83    type Output = Self;
84
85    /// Implements the unary negation operator `-` for the [Vec3] class.
86    ///
87    /// # Example
88    ///
89    /// ```rust
90    /// use moldyn_core::Vec3;
91    ///
92    /// let v: Vec3<f64> = Vec3::new(1.0, 2.0, 3.0);
93    /// let w: Vec3<i32> = Vec3::new(1, 2, 3);
94    ///
95    /// assert_eq!(-v, Vec3::new(-1.0, -2.0, -3.0));
96    /// assert_eq!(-w, Vec3::new(-1, -2, -3));
97    /// ```
98    fn neg(self) -> Self::Output {
99        Self {
100            x: -self.x,
101            y: -self.y,
102            z: -self.z,
103        }
104    }
105}
106
107// vector addition
108impl<T: Add<Output = T>> Add for Vec3<T> {
109    type Output = Self;
110
111    /// Implements the addition operator `+` for the [Vec3] class.
112    ///
113    /// # Example
114    ///
115    /// ```rust
116    /// use moldyn_core::Vec3;
117    ///
118    /// let v: Vec3<f64> = Vec3::new(1.0, 2.0, 3.0);
119    /// let w: Vec3<f64> = Vec3::new(4.0, 5.0, 6.0);
120    ///
121    /// assert_eq!(v + w, Vec3::new(5.0, 7.0, 9.0));
122    /// ```
123    fn add(self, other: Self) -> Self::Output {
124        Self {
125            x: self.x + other.x,
126            y: self.y + other.y,
127            z: self.z + other.z,
128        }
129    }
130}
131
132// vector addition assignment
133impl<T: AddAssign> AddAssign for Vec3<T> {
134    /// Implements the addition-assign operation `+=` for the [Vec3] class.
135    fn add_assign(&mut self, rhs: Self) {
136        self.x += rhs.x;
137        self.y += rhs.y;
138        self.z += rhs.z;
139    }
140}
141
142// vector subtraction
143impl<T: Sub<Output = T>> Sub for Vec3<T> {
144    type Output = Self;
145
146    /// Implements the subtraction operator `-` for the [Vec3] class.
147    ///
148    /// # Example
149    ///
150    /// ```rust
151    /// use moldyn_core::Vec3;
152    ///
153    /// let v: Vec3<f64> = Vec3::new(1.0, 2.0, 3.0);
154    /// let w: Vec3<f64> = Vec3::new(4.0, 5.0, 6.0);
155    ///
156    /// assert_eq!(v - w, Vec3::new(-3.0, -3.0, -3.0));
157    /// ```
158    fn sub(self, other: Self) -> Self::Output {
159        Self {
160            x: self.x - other.x,
161            y: self.y - other.y,
162            z: self.z - other.z,
163        }
164    }
165}
166
167// vector subtraction assignment
168impl<T: SubAssign> SubAssign for Vec3<T> {
169    /// Implements the subtraction-assign operation `-=` for the [Vec3] class.
170    fn sub_assign(&mut self, rhs: Self) {
171        self.x -= rhs.x;
172        self.y -= rhs.y;
173        self.z -= rhs.z;
174    }
175}
176
177// multiplication with scalar
178impl<T: Mul<Output = T> + Copy> Mul<T> for Vec3<T> {
179    type Output = Self;
180
181    /// Implements the scalar multiplication operator `*` for the [Vec3] class.
182    ///
183    /// # Example
184    ///
185    /// ```rust
186    /// use moldyn_core::Vec3;
187    ///
188    /// let v: Vec3<f64> = Vec3::new(1.0, 2.0, 3.0);
189    /// let w: Vec3<f64> = Vec3::new(2.0, 4.0, 6.0);
190    ///
191    /// assert_eq!(v * 2.0, w);
192    /// ```
193    fn mul(self, scalar: T) -> Self::Output {
194        Self {
195            x: self.x * scalar,
196            y: self.y * scalar,
197            z: self.z * scalar,
198        }
199    }
200}
201
202// multiplication with scalar assignment
203impl<T: MulAssign + Copy> MulAssign<T> for Vec3<T> {
204    /// Implements the multiplication-assign operation `*=` for the [Vec3] class.
205    fn mul_assign(&mut self, rhs: T) {
206        self.x *= rhs;
207        self.y *= rhs;
208        self.z *= rhs;
209    }
210}
211
212// vector division by scalar
213impl<T: Div<Output = T> + Copy> Div<T> for Vec3<T> {
214    type Output = Self;
215
216    /// Implements the division operator `/` for the [Vec3] class, allowing division of a vector
217    /// by a scalar.
218    ///
219    /// # Example
220    ///
221    /// ```
222    /// use moldyn_core::Vec3;
223    ///
224    /// let v: Vec3<f64> = Vec3::new(2.0, 4.0, 6.0);
225    /// let w: Vec3<f64> = Vec3::new(1.0, 2.0, 3.0);
226    ///
227    /// assert_eq!(v / 2.0, w);
228    /// ```
229    fn div(self, scalar: T) -> Self::Output {
230        Self {
231            x: self.x / scalar,
232            y: self.y / scalar,
233            z: self.z / scalar,
234        }
235    }
236}
237
238// division by scalar assignment
239impl<T: DivAssign + Copy> DivAssign<T> for Vec3<T> {
240    /// Implements the division-assign operation `/=` for the [Vec3] class.
241    fn div_assign(&mut self, rhs: T) {
242        self.x /= rhs;
243        self.y /= rhs;
244        self.z /= rhs;
245    }
246}
247
248//
249// THE FOLLOWING IMPLEMENTS DESERIALIZATION FOR THE [Vec3] STRUCT
250// FROM A THREE-ELEMENT SEQUENCE AND THE X-Y-Z OBJECT
251//
252
253// TODO test (i love my code after hyperfixations, it looks so gloriously clean, but it exhausts my ability to test)
254// see serde deserialization: https://serde.rs/impl-deserialize.html
255
256// We need phantom data because of generics.
257// see https://doc.rust-lang.org/stable/nomicon/phantom-data.html
258struct Vec3Visitor<T>(PhantomData<T>);
259
260impl<'de, T> Visitor<'de> for Vec3Visitor<T>
261where
262    T: Deserialize<'de> + Default,
263{
264    type Value = Vec3<T>;
265
266    // Format a message stating what data this Visitor expects to receive.
267    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
268        formatter.write_str("3-element array [x, y, z] or object {x, y, z}")
269    }
270
271    /// We deserialize three-element sequences into [Vec3] instances by mapping
272    /// the first element to x, the second to y, and the third to z. Missing values
273    /// are treated as zeroes.
274    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
275    where
276        A: serde::de::SeqAccess<'de>,
277    {
278        let x = match seq.next_element() {
279            // [x, ...]
280            Ok(Some(value)) => value,
281            // []
282            Ok(None) => return Ok(Vec3::default()),
283            Err(e) => return Err(e),
284        };
285
286        let y = match seq.next_element() {
287            // [x, y, ...]
288            Ok(Some(value)) => value,
289            // [x]
290            Ok(None) => {
291                return Ok(Vec3 {
292                    x,
293                    ..Default::default()
294                });
295            }
296            Err(e) => return Err(e),
297        };
298
299        let z = match seq.next_element() {
300            // [x, y, z, ...]
301            Ok(Some(value)) => value,
302            // [x, y]
303            Ok(None) => {
304                return Ok(Vec3 {
305                    x,
306                    y,
307                    ..Default::default()
308                });
309            }
310            Err(e) => return Err(e),
311        };
312
313        Ok(Vec3 { x, y, z })
314    }
315
316    /// We deserialize maps into [Vec3] instances by looking for keys "x", "y",
317    /// and "z" respectively. If any of these keys are missing, default to zero.
318    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
319    where
320        A: serde::de::MapAccess<'de>,
321    {
322        let mut x = None;
323        let mut y = None;
324        let mut z = None;
325
326        while let Some(key) = map.next_key::<String>()? {
327            match key.as_str() {
328                "x" => x = Some(map.next_value()?),
329                "y" => y = Some(map.next_value()?),
330                "z" => z = Some(map.next_value()?),
331                _ => {
332                    let _: serde::de::IgnoredAny = map.next_value()?;
333                }
334            }
335        }
336
337        // default to zero
338        let x = x.unwrap_or(T::default());
339        let y = y.unwrap_or(T::default());
340        let z = z.unwrap_or(T::default());
341
342        Ok(Vec3 { x, y, z })
343    }
344}
345
346impl<'de, T: Deserialize<'de>> Deserialize<'de> for Vec3<T>
347where
348    T: Deserialize<'de> + Default,
349{
350    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
351    where
352        D: serde::Deserializer<'de>,
353    {
354        deserializer.deserialize_any(Vec3Visitor(PhantomData))
355    }
356}
357
358impl<T: Display> Display for Vec3<T> {
359    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
360        write!(f, "[{}, {}, {}]", self.x, self.y, self.z)
361    }
362}
363
364macro_rules! impl_for_primitives {
365    ($($t:ty),*) => {$(
366impl Vec3<$t> {
367    /// Creates the dot product of two [Vec3] instances of the same primitive type.
368    ///
369    /// # Example
370    ///
371    /// ```rust
372    /// use moldyn_core::Vec3;
373    ///
374    /// let v = Vec3::new(1.0, 2.0, 3.0);
375    /// let w = Vec3::new(4.0, 5.0, 6.0);
376    ///
377    /// // TODO think about example output
378    /// ```
379    pub fn dot(&self, other: &Self) -> $t {
380        self.x * other.x + self.y * other.y + self.z * other.z
381    }
382
383    /// Creates the cross product of two [Vec3] instances of the same primitive type.
384    ///
385    /// # Example
386    ///
387    /// ```rust
388    /// use moldyn_core::Vec3;
389    ///
390    /// let v = Vec3::new(1.0, 2.0, 3.0);
391    /// let w = Vec3::new(4.0, 5.0, 6.0);
392    ///
393    /// // TODO think about example output
394    /// ```
395    pub fn cross(&self, other: &Self) -> Self {
396        Self {
397            x: self.y * other.z - self.z * other.y,
398            y: self.z * other.x - self.x * other.z,
399            z: self.x * other.y - self.y * other.x
400        }
401    }
402
403    /// Computes the length (magnitude) of the [Vec3] instance.
404    ///
405    /// # Example
406    ///
407    /// ```rust
408    /// use moldyn_core::Vec3;
409    ///
410    #[doc = stringify!(let v: Vec3<$t> = Vec3::<$t>::new(2.0 as $t, 2.0 as $t, 1.0 as $t);)]
411    ///
412    /// assert_eq!(v.length(), 3.0);
413    /// ```
414    pub fn length(&self) -> f64 {
415        // https://stackoverflow.com/a/29864160
416        (self.dot(self) as f64).sqrt()
417    }
418
419    /// TODO: rethink: old codebase became buggy because we used the wrong method.
420    pub fn length2(&self) -> $t {
421        self.dot(self)
422    }
423
424    /// Normalizes the [Vec3] instance to have a length of 1,
425    /// returning an option containing a new [Vec3] instance. If
426    /// the original vector is zero-length, returns None to avoid
427    /// division by zero.
428    ///
429    /// **Note: I've made this decision with the intention of
430    /// propagating divisions by zero upwards the stack. I do
431    /// not know if this will turn out to be useful.**
432    pub fn normal(&self) -> Option<Self> {
433        // divide by zero actually panics: https://internals.rust-lang.org/t/question-why-does-dividing-by-zero-have-no-safety-guarantees/19189
434        match self.length() {
435            0.0 => None,
436            len => Some(Self {
437                x: self.x / len as $t,
438                y: self.y / len as $t,
439                z: self.z / len as $t,
440            }),
441        }
442    }
443}
444
445impl Copy for Vec3<$t> {}
446    )*};
447}
448
449impl_for_primitives!(
450    u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64
451);