1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use crate::geom::{Vector, Scalar};
use std::cmp::{Eq, PartialEq};

#[derive(Clone, Copy, Default, Debug, Deserialize, Serialize)]
///A line with a starting and end point
pub struct Line {
    /// The starting point
    pub a: Vector,
    /// The end point
    pub b: Vector,
    /// The thickness, used only for rendering and not collision
    pub t: f32,
}

impl Line {
    ///Create a new line with a start- and an endpoint 
    pub fn new(start: impl Into<Vector>, end: impl Into<Vector>) -> Line {
        Line {
            a: start.into(),
            b: end.into(),
            t: 1.0
        }
    }

    ///Create a line with a changed thickness
    #[must_use]
    pub fn with_thickness(self, thickness: impl Scalar) -> Line {
        Line {
            t: thickness.float(),
            ..self
        }
    }
}

impl PartialEq for Line {
    fn eq(&self, other: &Line) -> bool {
        self.a == other.a && self.b == other.b
    }
}

impl Eq for Line {}


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

    #[test]
    fn overlap_rectangle() {
        let rect = &Rectangle::new_sized((1, 1));
        let line_in = Line::new((0.5, 0.5), (3.0, 3.0));
        let line_on = Line::new(Vector::ZERO, Vector::X * 2);
        let line_not = Line::new((10, 10), (12, 12));
        assert!(line_in.overlaps(rect));
        assert!(line_on.overlaps(rect));
        assert!(!line_not.overlaps(rect));
    }

    #[test]
    fn overlap_circle() {
        let circle = &Circle::new((0, 0), 1);
        let line_on = Line::new((-1, 1), (1, 1));
        let line_in = Line::new(Vector::ZERO, Vector::X * 2);
        let line_not = Line::new((10, 10), (12, 12));
        assert!(line_in.overlaps(circle));
        assert!(line_on.overlaps(circle));
        assert!(!line_not.overlaps(circle));
    }

    #[test]
    fn overlap_line() {
        let line = Line::new(Vector::ZERO, Vector::X);
        let line_parallel = Line::new((0.0, 0.5), (1.0, 0.5));
        let line_cross = Line::new((0.5, -1.0), (0.5, 1.0));
        let line_touch = Line::new((0.0, -1.0), (0.0, 1.0));
        let line_away = Line::new((4, 2), (6, 9));
        assert!(!line.overlaps(&line_parallel));
        assert!(line.overlaps(&line_cross));
        assert!(line.overlaps(&line_touch));
        assert!(!line.overlaps(&line_away));
    }

    #[test]
    fn contains() {
        let line = Line::new(Vector::ZERO, Vector::X);
        let v_on = Vector::new(0.3, 0.0);
        let v_close = Vector::new(0.999, 0.1);
        let v_off = Vector::new(3, 5);
        assert!(line.contains(v_on));
        assert!(!line.contains(v_close));
        assert!(!line.contains(v_off));
    }

    #[test]
    fn constraint() {
        let line = Line::new((5, 5), (10, 7));
        let fits = Rectangle::new((0, 0), (15, 15));
        let not_fit = Rectangle::new((0, 0), (9, 6));
        let fits_line = line.constrain(&fits);
        let not_fits_line = line.constrain(&not_fit);
        assert_eq!(line.a, fits_line.a);
        assert_eq!(line.b, fits_line.b);
        assert_eq!(Vector::new(4, 4), not_fits_line.a);
        assert_eq!(Vector::new(9, 6), not_fits_line.b);
    }

    #[test]
    fn translate() {
        let line = Line::new(Vector::ZERO, Vector::ONE).translate((3, 5));
        assert_eq!(line.a.x, 3.0);
        assert_eq!(line.a.y, 5.0);
        assert_eq!(line.b.x, 4.0);
        assert_eq!(line.b.y, 6.0);
    }
}