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);