tg-geom 0.0.0

Rust bindings for the `TG` Geometry library.
Documentation
//! Line segment type.

use crate::{Point, Rect};

/// Directed from point `a` to point `b`.
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub struct Segment {
    pub a: Point,
    pub b: Point,
}

impl Segment {
    #[inline]
    pub const fn new(a: Point, b: Point) -> Self {
        Self { a, b }
    }

    #[inline]
    pub fn rect(self) -> Rect {
        let r = unsafe { tg_geom_sys::tg_segment_rect(self.into()) };
        r.into()
    }

    #[inline]
    pub fn intersects_segment(self, other: Segment) -> bool {
        unsafe { tg_geom_sys::tg_segment_intersects_segment(self.into(), other.into()) }
    }
}

impl From<tg_geom_sys::tg_segment> for Segment {
    #[inline]
    fn from(s: tg_geom_sys::tg_segment) -> Self {
        Self {
            a: s.a.into(),
            b: s.b.into(),
        }
    }
}

impl From<Segment> for tg_geom_sys::tg_segment {
    #[inline]
    fn from(s: Segment) -> Self {
        Self {
            a: s.a.into(),
            b: s.b.into(),
        }
    }
}

impl From<(Point, Point)> for Segment {
    #[inline]
    fn from((a, b): (Point, Point)) -> Self {
        Self { a, b }
    }
}

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

    fn p(x: f64, y: f64) -> Point {
        Point::new(x, y)
    }

    fn s(ax: f64, ay: f64, bx: f64, by: f64) -> Segment {
        Segment::new(p(ax, ay), p(bx, by))
    }

    #[test]
    fn test_segment_new() {
        let seg = Segment::new(p(1.0, 2.0), p(3.0, 4.0));
        assert_eq!(seg.a, p(1.0, 2.0));
        assert_eq!(seg.b, p(3.0, 4.0));
    }

    #[test]
    fn test_segment_rect() {
        let seg1 = s(1.0, 2.0, 4.0, 6.0);
        let rect1 = seg1.rect();
        assert_eq!(rect1.min, p(1.0, 2.0));
        assert_eq!(rect1.max, p(4.0, 6.0));

        let seg2 = s(4.0, 6.0, 1.0, 2.0);
        let rect2 = seg2.rect();
        assert_eq!(rect2.min, p(1.0, 2.0));
        assert_eq!(rect2.max, p(4.0, 6.0));

        let seg3 = s(5.0, 5.0, 5.0, 5.0);
        let rect3 = seg3.rect();
        assert_eq!(rect3.min, rect3.max);

        let seg4 = s(0.0, 5.0, 10.0, 5.0);
        let rect4 = seg4.rect();
        assert_eq!(rect4.min.y, rect4.max.y);

        let seg5 = s(5.0, 0.0, 5.0, 10.0);
        let rect5 = seg5.rect();
        assert_eq!(rect5.min.x, rect5.max.x);
    }

    #[test]
    fn test_segment_intersects_segment_crossing() {
        let seg1 = s(0.0, 0.0, 1.0, 1.0);
        let seg2 = s(0.0, 1.0, 1.0, 0.0);
        assert!(seg1.intersects_segment(seg2));
    }

    #[test]
    fn test_segment_intersects_segment_no_intersection() {
        let seg1 = s(0.0, 0.0, 1.0, 1.0);
        let seg2 = s(2.0, 2.0, 3.0, 3.0);
        assert!(!seg1.intersects_segment(seg2));
    }

    #[test]
    fn test_segment_intersects_segment_parallel() {
        let seg1 = s(0.0, 0.0, 1.0, 0.0);
        let seg2 = s(0.0, 1.0, 1.0, 1.0);
        assert!(!seg1.intersects_segment(seg2));
    }

    #[test]
    fn test_segment_intersects_segment_collinear_overlap() {
        let seg1 = s(0.0, 0.0, 2.0, 0.0);
        let seg2 = s(1.0, 0.0, 3.0, 0.0);
        assert!(seg1.intersects_segment(seg2));
    }

    #[test]
    fn test_segment_intersects_segment_endpoint_touch() {
        let seg1 = s(0.0, 0.0, 1.0, 0.0);
        let seg2 = s(1.0, 0.0, 2.0, 0.0);
        assert!(seg1.intersects_segment(seg2));
    }

    #[test]
    fn test_segment_intersects_segment_t_intersection() {
        let seg1 = s(0.0, 0.0, 10.0, 0.0);
        let seg2 = s(5.0, 0.0, 5.0, 10.0);
        assert!(seg1.intersects_segment(seg2));
    }

    #[test]
    fn test_segment_intersects_segment_self() {
        let seg = s(0.0, 0.0, 1.0, 1.0);
        assert!(seg.intersects_segment(seg));
    }

    #[test]
    fn test_segment_intersects_segment_vertical() {
        let seg1 = s(4.0, 4.0, 4.0, 4.0);
        let seg2 = s(4.0, 4.0, 4.0, 4.0);
        assert!(seg1.intersects_segment(seg2));

        let seg3 = s(5.0, 0.0, 5.0, 10.0);
        let seg4 = s(0.0, 5.0, 10.0, 5.0);
        assert!(seg3.intersects_segment(seg4));
    }

    #[test]
    fn test_segment_from_points() {
        let seg: Segment = (p(1.0, 2.0), p(3.0, 4.0)).into();
        assert_eq!(seg.a, p(1.0, 2.0));
        assert_eq!(seg.b, p(3.0, 4.0));
    }

    #[test]
    fn test_segment_sys_roundtrip() {
        let seg = s(1.0, 2.0, 3.0, 4.0);
        let sys_seg: tg_geom_sys::tg_segment = seg.into();
        let back: Segment = sys_seg.into();
        assert_eq!(seg, back);
    }

    #[test]
    fn test_segment_eq() {
        assert_eq!(s(0.0, 0.0, 1.0, 1.0), s(0.0, 0.0, 1.0, 1.0));
        assert_ne!(s(0.0, 0.0, 1.0, 1.0), s(0.0, 0.0, 2.0, 2.0));
    }

    #[test]
    fn test_segment_debug() {
        let seg = s(1.0, 2.0, 3.0, 4.0);
        let debug_str = format!("{seg:?}");
        assert!(debug_str.contains("Segment"));
    }

    #[test]
    fn test_segment_negative_coords() {
        let seg = s(-5.0, -3.0, -1.0, -7.0);
        let rect = seg.rect();
        assert_eq!(rect.min.x, -5.0);
        assert_eq!(rect.min.y, -7.0);
        assert_eq!(rect.max.x, -1.0);
        assert_eq!(rect.max.y, -3.0);
    }
}