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}