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