bb-geometry 0.3.0

A small crate containing some data structures and methods for 4d Euclidean geometry.
Documentation
use crate::ALMOST_ZERO;
use rand::Rng;

#[derive(Debug)]
pub struct Vector4D {
    pub x: f64,
    pub y: f64,
    pub z: f64,
    pub t: f64,
}

pub const E_X: Vector4D = Vector4D {
    x: 1.0,
    y: 0.0,
    z: 0.0,
    t: 0.0,
};

pub const E_Y: Vector4D = Vector4D {
    x: 0.0,
    y: 1.0,
    z: 0.0,
    t: 0.0,
};

pub const E_Z: Vector4D = Vector4D {
    x: 0.0,
    y: 0.0,
    z: 1.0,
    t: 0.0,
};

pub const E_T: Vector4D = Vector4D {
    x: 0.0,
    y: 0.0,
    z: 0.0,
    t: 1.0,
};

impl Vector4D {
    /// This creates a new 4d vector with the four given entries.
    pub fn new(x: f64, y: f64, z: f64, t: f64) -> Vector4D {
        Vector4D { x, y, z, t }
    }


    /// This gives back the l1 length of a 4d vector, i.e. the maximum of absolute values of its components.
    ///
    /// # Example:
    /// ```
    /// use bb_geometry::vector4d::Vector4D;
    /// let length = Vector4D::new(-5.0, 4.3, -2.2, 0.0).length_l1();
    /// assert_eq!(length, 5.0);
    /// ```
    pub fn length_l1(&self) -> f64 {
        vec![self.x, self.y, self.z, self.t]
            .iter()
            .map(|i| i.abs())
            .reduce(f64::max)
            .unwrap()
    }

    /// This checks whether a 4d vector is close to the zero vector, up to `crate::ALMOST_ZERO`
    ///
    /// # Example:
    /// ```
    ///use bb_geometry::ALMOST_ZERO;
    ///use bb_geometry::vector4d::Vector4D;
    /// let a = Vector4D::new(ALMOST_ZERO, ALMOST_ZERO, ALMOST_ZERO, ALMOST_ZERO);
    /// let b = Vector4D::new(0.99 * ALMOST_ZERO, 0.99 * ALMOST_ZERO, 0.99 * ALMOST_ZERO, 0.99 * ALMOST_ZERO);
    /// assert_eq!(a.is_close_to_zero(), false);
    /// assert_eq!(b.is_close_to_zero(), true);
    /// ```
    pub fn is_close_to_zero(self: &Vector4D) -> bool {
        self.length_l1() < ALMOST_ZERO
    }

    /// This adds two 4d vectors
    ///
    /// # Example:
    /// ```
    ///use bb_geometry::vector4d::Vector4D;
    /// let a = Vector4D::new(1.0, 2.0, 3.0, 4.0);
    /// let b = Vector4D::new(-1.0, 4.0, 2.5, -3.1);
    /// let c = Vector4D::new(0.0, 6.0, 5.5, 0.9);
    /// assert!(a.plus(&b).almost_equals(&c));
    /// ```
    pub fn plus(self: &Vector4D, a: &Vector4D) -> Vector4D {
        Vector4D {
            x: self.x + a.x,
            y: self.y + a.y,
            z: self.z + a.z,
            t: self.t + a.t,
        }
    }

    /// This subtracts two 4d vectors
    ///
    /// # Example:
    /// ```
    ///use bb_geometry::vector4d::Vector4D;
    /// let a = Vector4D::new(1.0, 2.0, 3.0, 4.0);
    /// let b = Vector4D::new(-1.0, 4.0, 2.5, -3.1);
    /// let c = Vector4D::new(2.0, -2.0, 0.5, 7.1);
    /// assert!(a.minus(&b).almost_equals(&c));
    /// ```
    pub fn minus(self: &Vector4D, a: &Vector4D) -> Vector4D {
        Vector4D {
            x: self.x - a.x,
            y: self.y - a.y,
            z: self.z - a.z,
            t: self.t - a.t,
        }
    }

    /// This compares two 4d vectors up to `crate::ALMOST_ZERO`.
    ///
    /// # Example:
    /// ```
    ///use bb_geometry::vector4d::Vector4D;
    /// let a = Vector4D::new(1.0, 2.0, 3.0, 4.0);
    /// let b = Vector4D::new(1.00001, 2.0, 3.0, 4.0);
    /// let c = Vector4D::new(1.0000000001, 2.0, 3.0, 4.0);
    /// assert_eq!(a.almost_equals(&b), false);
    /// assert_eq!(a.almost_equals(&c), true);
    /// ```
    pub fn almost_equals(self: &Vector4D, a: &Vector4D) -> bool {
        self.minus(a).is_close_to_zero()
    }

    /// This replaces a 4d vector with its negative
    ///
    /// # Examples
    ///
    /// ```
    /// use bb_geometry::vector4d::Vector4D;
    /// let a = Vector4D::new(1.0, 2.0, 3.0, 4.0);
    /// let b = Vector4D::new(-1.0, -2.0, -3.0, -4.0);
    /// assert!(a.revert().almost_equals(&b));
    /// ```
    pub fn revert(&self) -> Vector4D {
        Vector4D {
            x: -self.x,
            y: -self.y,
            z: -self.z,
            t: -self.t,
        }
    }

    /// This generates a 4d vector of length n containing randomly generated 4d vectors.
    ///
    /// # Example:
    /// ```
    ///use bb_geometry::vector4d::Vector4D;
    /// let n = 10;
    /// let list = Vector4D::generate_random_vectors(n);
    /// assert_eq!(list.len(), n);
    /// ```
    pub fn generate_random_vectors(n: usize) -> Vec<Vector4D> {
        (0..n).map(|_| Self::random_vector()).collect()
    }


    /// This generates a random 4d vector with entire between -1.0 and 1.0.
    ///
    /// # Example:
    /// ```
    ///use bb_geometry::vector4d::Vector4D;
    /// let a = Vector4D::random_vector();
    /// assert!(a.length_l1() <= 1.0);
    /// ```
    pub fn random_vector() -> Vector4D {
        let mut rng = rand::rng();
        Vector4D::new(
            rng.random_range(-1.0..=1.0),
            rng.random_range(-1.0..=1.0),
            rng.random_range(-1.0..=1.0),
            rng.random_range(-1.0..=1.0),
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn plus_and_minus_are_opposite() {
        // given
        let a = Vector4D::random_vector();
        let b = Vector4D::random_vector();

        // when
        let c = a.plus(&b).minus(&b);

        // then
        assert!(c.almost_equals(&a));
    }

    #[test]
    fn minus_and_revert() {
        // given
        let a = Vector4D::random_vector();
        let b = Vector4D::random_vector();

        // when
        let c = a.minus(&(b.revert()));

        // then
        assert!(a.plus(&b).almost_equals(&c));
    }

}