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}