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