bb_geometry/
vector3d.rs

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