mathpak 0.4.0

Rust Math Routines, a simple to use math focused library written in rust
Documentation
use std::cmp::Ordering;

use float_cmp::{ApproxEq, F64Margin};

use super::point::Point;

#[derive(Debug, Clone, Copy)]
pub struct Point2 {
    x: f64,
    y: f64,
}

impl Point2 {
    pub fn new(x: f64, y: f64) -> Self {
        Self { x, y }
    }

    pub fn x(&self) -> f64 {
        self.x
    }

    pub fn y(&self) -> f64 {
        self.y
    }
}

impl Point for Point2 {
    /// Determine if `x` and `y` are normal or finite numbers
    fn is_valid(&self) -> bool {
        (self.x.is_normal() || self.x.is_finite()) && (self.y.is_normal() || self.y.is_finite())
    }

    /// Add `other` to self
    fn add(&mut self, other: Point2) {
        self.x += other.x;
        self.y += other.y;
    }

    /// Subtract `other` from self
    fn sub(&mut self, other: Point2) {
        self.x -= other.x;
        self.y -= other.y;
    }

    /// Calculate the distance between `self` and `other`
    fn distance(&self, other: Point2) -> f64 {
        ((other.x - self.x).powi(2) + (other.y - self.y).powi(2)).sqrt()
    }

    /// Calculate the distance between `self` and the origin
    fn distance_from_origin(&self) -> f64 {
        self.distance(Point2::new(0.0, 0.0))
    }

    /// Scale `self` by `factor`
    fn scale(&mut self, factor: f64) {
        self.x *= factor;
        self.y *= factor;
    }

    /// Linearly interpolate between `self` and `other` by `t`
    /// `t` should be in the range [0, 1]
    fn lerp(&self, other: Point2, t: f64) -> Point2 {
        Point2::new(
            self.x + (other.x - self.x) * t,
            self.y + (other.y - self.y) * t,
        )
    }

    /// Compare `self` and `other` with a margin of error
    fn is_equal_marginal(&self, other: Point2, margin: F64Margin) -> bool {
        self.x.approx_eq(other.x, margin) && self.y.approx_eq(other.y, margin)
    }
}

impl PartialEq<Self> for Point2 {
    fn eq(&self, other: &Self) -> bool {
        self.x == other.x && self.y == other.y
    }
}

impl PartialOrd<Self> for Point2 {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        if !self.is_valid() || !other.is_valid() {
            return None;
        }

        Some(if self == other {
            Ordering::Equal
        } else if self.x > other.x && self.y > other.y {
            Ordering::Greater
        } else {
            Ordering::Less
        })
    }
}

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

    #[test]
    fn point2d_eq() {
        let point1 = Point2::new(1.0, 2.0);
        let point2 = Point2::new(1.0, 2.0);
        assert_eq!(point1, point2);
    }

    #[test]
    fn point2d_compare() {
        let p1 = Point2::new(1.0, 2.0);
        let p2 = Point2::new(3.0, 4.0);
        assert_eq!(p1.partial_cmp(&p2), Some(Ordering::Less));
        assert_eq!(p2.partial_cmp(&p1), Some(Ordering::Greater));
    }

    #[test]
    fn point2d_add() {
        let mut p1 = Point2::new(1.0, 2.0);
        let p2 = Point2::new(3.0, 4.0);
        p1.add(p2);
        assert_eq!(p1, Point2::new(4.0, 6.0));
    }

    #[test]
    fn point2d_sub() {
        let mut p1 = Point2::new(1.0, 2.0);
        let p2 = Point2::new(3.0, 4.0);
        p1.sub(p2);
        assert_eq!(p1, Point2::new(-2.0, -2.0));
    }

    #[test]
    fn point2d_distance() {
        let p1 = Point2::new(1.0, 2.0);
        let p2 = Point2::new(4.0, 6.0);
        assert_eq!(p1.distance(p2), 5.0);
    }

    #[test]
    fn point2d_distance_from_origin() {
        let p = Point2::new(3.0, 4.0);
        assert_eq!(p.distance_from_origin(), 5.0);
    }

    #[test]
    fn point2d_scale() {
        let mut p = Point2::new(1.0, 2.0);
        p.scale(2.0);
        assert_eq!(p, Point2::new(2.0, 4.0));
    }

    #[test]
    fn point2d_lerp() {
        let p1 = Point2::new(0.0, 2.0);
        let p2 = Point2::new(2.0, 1.0);
        assert_eq!(p1.lerp(p2, 0.38), Point2::new(0.76, 1.62));
    }

    #[test]
    fn point2d_is_equal_marginal() {
        let p1 = Point2::new(1.0, 2.0);
        let p2 = Point2::new(1.0000000000000001, 2.0);
        assert!(p1.is_equal_marginal(p2, F64Margin::default()));
    }
}