moho 0.0.9

Barebones Rust Game Library
use super::{Intersect, Line, Shape, Rectangle};

use glm;

#[derive(Clone, Copy)]
pub struct Circle {
    pub radius: f64,
    pub center: glm::DVec2,
}

impl Shape for Circle {
    fn center(&self) -> glm::DVec2 {
        self.center
    }

    fn contains(&self, point: &glm::DVec2) -> bool {
        let distance = glm::distance(self.center, *point);
        distance < self.radius
    }
}

impl Intersect<Rectangle> for Circle {
    fn intersects(&self, other: &Rectangle) -> bool {
        self.contains(&other.top_left) || other.contains(&self.center) ||
        other.get_lines().iter().any(|l| self.intersects(l))
    }
}

impl Intersect<Circle> for Circle {
    fn intersects(&self, other: &Circle) -> bool {
        let distance = self.distance(other);
        distance < (self.radius + other.radius)
    }
}

impl Intersect<Line> for Circle {
    fn intersects(&self, other: &Line) -> bool {
        let length = other.1 - other.0;
        let dist_center = other.0 - self.center;
        let len_sq = glm::dot(length, length);
        let b = 2_f64 * glm::dot(dist_center, length);
        let c = glm::dot(dist_center, dist_center) - self.radius * self.radius;
        let mut discriminant = b * b - 4_f64 * len_sq * c;

        if discriminant < 0_f64 {
            return false;
        }

        discriminant = discriminant.sqrt();

        let t1 = (-b - discriminant) / (2_f64 * len_sq);
        let t2 = (-b + discriminant) / (2_f64 * len_sq);

        t1 >= 0_f64 && t1 <= 1_f64 || t2 >= 0_f64 && t2 <= 1_f64
    }
}

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

    #[test]
    fn circle_no_contains() {
        let circle = Circle {
            radius: 1_f64,
            center: glm::dvec2(2_f64, 0_f64),
        };
        let point = glm::dvec2(4_f64, 4_f64);
        assert!(!circle.contains(&point));
    }

    #[test]
    fn circle_contains() {
        let circle = Circle {
            radius: 3_f64,
            center: glm::dvec2(2_f64, 3_f64),
        };
        let point = glm::dvec2(4_f64, 4_f64);
        assert!(circle.contains(&point));
    }

    #[test]
    fn circle_circle_no_intersect() {
        let circle_a = Circle {
            radius: 3_f64,
            center: glm::dvec2(2_f64, 3_f64),
        };

        let circle_b = Circle {
            radius: 1_f64,
            center: glm::dvec2(4_f64, 7_f64),
        };

        assert!(!circle_a.intersects(&circle_b));
        assert!(!circle_b.intersects(&circle_a));
    }

    #[test]
    fn circle_circle_intersect() {
        let circle_a = Circle {
            radius: 3_f64,
            center: glm::dvec2(2_f64, 3_f64),
        };

        let circle_b = Circle {
            radius: 1_f64,
            center: glm::dvec2(1_f64, 5_f64),
        };

        assert!(circle_a.intersects(&circle_b));
        assert!(circle_b.intersects(&circle_a));
    }

    #[test]
    fn rectangle_circle_no_intersect() {
        let rectangle = Rectangle {
            dims: glm::dvec2(5_f64, 2_f64),
            top_left: glm::dvec2(3.5, -1.),
        };

        let circle = Circle {
            radius: 3_f64,
            center: glm::dvec2(1_f64, 3_f64),
        };

        assert!(!circle.intersects(&rectangle));
    }

    #[test]
    fn rectangle_inside_circle() {
        let rectangle = Rectangle {
            dims: glm::dvec2(1_f64, 2_f64),
            top_left: glm::dvec2(2.5, 4_f64),
        };

        let circle = Circle {
            radius: 5_f64,
            center: glm::dvec2(2_f64, 3_f64),
        };

        assert!(circle.intersects(&rectangle));
    }

    #[test]
    fn circle_inside_rectangle() {
        let rectangle = Rectangle {
            dims: glm::dvec2(5_f64, 7_f64),
            top_left: glm::dvec2(1.5, -1.5),
        };

        let circle = Circle {
            radius: 1_f64,
            center: glm::dvec2(5_f64, 3_f64),
        };

        assert!(circle.intersects(&rectangle));
    }

    #[test]
    fn rectangle_circle_intersect() {
        let rectangle = Rectangle {
            dims: glm::dvec2(2_f64, 2_f64),
            top_left: glm::dvec2(4_f64, 2_f64),
        };

        let circle = Circle {
            radius: 2_f64,
            center: glm::dvec2(2_f64, 3_f64),
        };

        assert!(circle.intersects(&rectangle));
    }
}