bb_geometry/
vector3d.rs

1use crate::ALMOST_ZERO;
2use rand::Rng;
3
4#[derive(Debug)]
5pub struct Vector3D {
6    pub x: f64,
7    pub y: f64,
8    pub z: f64,
9}
10
11pub const X: Vector3D = Vector3D {
12    x: 1.0,
13    y: 0.0,
14    z: 0.0,
15};
16
17pub const Y: Vector3D = Vector3D {
18    x: 0.0,
19    y: 1.0,
20    z: 0.0,
21};
22
23pub const Z: Vector3D = Vector3D {
24    x: 0.0,
25    y: 0.0,
26    z: 1.0,
27};
28
29impl Vector3D {
30    /// This creates a new 3d vector with the four given entries.
31    pub fn new(x: f64, y: f64, z: f64) -> Vector3D {
32        Vector3D { x, y, z }
33    }
34
35
36    /// This gives back the l1 length of a 3d vector, i.e. the maximum of absolute values of its components.
37    ///
38    /// # Example:
39    /// ```
40    /// use bb_geometry::vector3d::Vector3D;
41    /// let length = Vector3D::new(-5.0, -2.2, 0.0).length_l1();
42    /// assert_eq!(length, 5.0);
43    /// ```
44    pub fn length_l1(&self) -> f64 {
45        vec![self.x, self.y, self.z]
46            .iter()
47            .map(|i| i.abs())
48            .reduce(f64::max)
49            .unwrap()
50    }
51
52    /// This checks whether a 3d vector is close to the zero vector, up to `crate::ALMOST_ZERO`
53    ///
54    /// # Example:
55    /// ```
56    ///use bb_geometry::ALMOST_ZERO;
57    ///use bb_geometry::vector3d::Vector3D;
58    /// let a = Vector3D::new(ALMOST_ZERO, ALMOST_ZERO, ALMOST_ZERO);
59    /// let b = Vector3D::new(0.99 * ALMOST_ZERO, 0.99 * ALMOST_ZERO, 0.99 * ALMOST_ZERO);
60    /// assert_eq!(a.is_close_to_zero(), false);
61    /// assert_eq!(b.is_close_to_zero(), true);
62    /// ```
63    pub fn is_close_to_zero(self: &Vector3D) -> bool {
64        self.length_l1() < ALMOST_ZERO
65    }
66
67    /// This adds two 3d vectors
68    ///
69    /// # Example:
70    /// ```
71    ///use bb_geometry::vector3d::Vector3D;
72    /// let a = Vector3D::new(1.0, 2.0,  4.0);
73    /// let b = Vector3D::new(-1.0, 4.0,  -3.1);
74    /// let c = Vector3D::new(0.0, 6.0,  0.9);
75    /// assert!(a.plus(&b).almost_equals(&c));
76    /// ```
77    pub fn plus(self: &Vector3D, a: &Vector3D) -> Vector3D {
78        Vector3D {
79            x: self.x + a.x,
80            y: self.y + a.y,
81            z: self.z + a.z,
82        }
83    }
84
85    /// This subtracts two 3d vectors
86    ///
87    /// # Example:
88    /// ```
89    ///use bb_geometry::vector3d::Vector3D;
90    /// let a = Vector3D::new(1.0, 2.0,  4.0);
91    /// let b = Vector3D::new(-1.0, 4.0, -3.1);
92    /// let c = Vector3D::new(2.0, -2.0,  7.1);
93    /// assert!(a.minus(&b).almost_equals(&c));
94    /// ```
95    pub fn minus(self: &Vector3D, a: &Vector3D) -> Vector3D {
96        Vector3D {
97            x: self.x - a.x,
98            y: self.y - a.y,
99            z: self.z - a.z,
100        }
101    }
102
103    /// This compares two 3d vectors up to `crate::ALMOST_ZERO`.
104    ///
105    /// # Example:
106    /// ```
107    ///use bb_geometry::vector3d::Vector3D;
108    /// let a = Vector3D::new(1.0, 2.0, 3.0);
109    /// let b = Vector3D::new(1.00001, 2.0, 3.0);
110    /// let c = Vector3D::new(1.0000000001, 2.0, 3.0);
111    /// assert_eq!(a.almost_equals(&b), false);
112    /// assert_eq!(a.almost_equals(&c), true);
113    /// ```
114    pub fn almost_equals(self: &Vector3D, a: &Vector3D) -> bool {
115        self.minus(a).is_close_to_zero()
116    }
117
118    /// This replaces a 3d vector with its negative
119    ///
120    /// # Examples
121    ///
122    /// ```
123    /// use bb_geometry::vector3d::Vector3D;
124    /// let a = Vector3D::new(1.0, 2.0, 3.0);
125    /// let b = Vector3D::new(-1.0, -2.0, -3.0);
126    /// assert!(a.revert().almost_equals(&b));
127    /// ```
128    pub fn revert(&self) -> Vector3D {
129        Vector3D {
130            x: -self.x,
131            y: -self.y,
132            z: -self.z,
133        }
134    }
135
136    /// This generates a 3d vector of length n containing randomly generated 4d vectors.
137    ///
138    /// # Example:
139    /// ```
140    ///use bb_geometry::vector3d::Vector3D;
141    /// let n = 10;
142    /// let list = Vector3D::generate_random_vectors(n);
143    /// assert_eq!(list.len(), n);
144    /// ```
145    pub fn generate_random_vectors(n: usize) -> Vec<Vector3D> {
146        (0..n).map(|_| Self::random_vector()).collect()
147    }
148
149
150    /// This generates a random 3d vector with entire between -1.0 and 1.0.
151    ///
152    /// # Example:
153    /// ```
154    ///use bb_geometry::vector3d::Vector3D;
155    /// let a = Vector3D::random_vector();
156    /// assert!(a.length_l1() <= 1.0);
157    /// ```
158    pub fn random_vector() -> Vector3D {
159        let mut rng = rand::rng();
160        Vector3D::new(
161            rng.random_range(-1.0..=1.0),
162            rng.random_range(-1.0..=1.0),
163            rng.random_range(-1.0..=1.0),
164        )
165    }
166
167    pub fn from_reference(vector_reference: &Vector3D) -> Vector3D {
168        Vector3D::new(vector_reference.x, vector_reference.y, vector_reference.z)
169    }
170
171    pub fn components(&self) -> [f64; 3] {
172        [self.x, self.y, self.z]
173    }
174}
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179
180    #[test]
181    fn plus_and_minus_are_opposite() {
182        // given
183        let a = Vector3D::random_vector();
184        let b = Vector3D::random_vector();
185
186        // when
187        let c = a.plus(&b).minus(&b);
188
189        // then
190        assert!(c.almost_equals(&a));
191    }
192
193    #[test]
194    fn minus_and_revert() {
195        // given
196        let a = Vector3D::random_vector();
197        let b = Vector3D::random_vector();
198
199        // when
200        let c = a.minus(&(b.revert()));
201
202        // then
203        assert!(a.plus(&b).almost_equals(&c));
204    }
205
206    #[test]
207    fn from_reference_delivers_identical_vector() {
208        // given
209        let a = Vector3D::random_vector();
210
211        // when
212        let b = Vector3D::from_reference(&a);
213
214        // then
215        assert!(b.almost_equals(&a));
216    }
217}