tg-geom 0.0.0

Rust bindings for the `TG` Geometry library.
Documentation
//! 2D point type.

use crate::Rect;

/// X and Y coordinates.
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub struct Point {
    pub x: f64,
    pub y: f64,
}

impl Point {
    #[inline]
    pub const fn new(x: f64, y: f64) -> Self {
        Self { x, y }
    }

    /// Returns a zero-area bounding rectangle.
    #[inline]
    pub fn rect(self) -> Rect {
        let r = unsafe { tg_geom_sys::tg_point_rect(self.into()) };
        r.into()
    }

    #[inline]
    pub fn intersects_rect(self, rect: Rect) -> bool {
        unsafe { tg_geom_sys::tg_point_intersects_rect(self.into(), rect.into()) }
    }
}

impl From<tg_geom_sys::tg_point> for Point {
    #[inline]
    fn from(p: tg_geom_sys::tg_point) -> Self {
        Self { x: p.x, y: p.y }
    }
}

impl From<Point> for tg_geom_sys::tg_point {
    #[inline]
    fn from(p: Point) -> Self {
        Self { x: p.x, y: p.y }
    }
}

impl From<(f64, f64)> for Point {
    #[inline]
    fn from((x, y): (f64, f64)) -> Self {
        Self { x, y }
    }
}

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

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

    fn r(min_x: f64, min_y: f64, max_x: f64, max_y: f64) -> Rect {
        Rect::from_coords(min_x, min_y, max_x, max_y)
    }

    #[test]
    fn test_point_rect() {
        let pt = p(5.0, 5.0);
        let rect = pt.rect();
        assert_eq!(rect, r(5.0, 5.0, 5.0, 5.0));
    }

    #[test]
    fn test_point_intersects_rect() {
        assert!(p(5.0, 5.0).intersects_rect(r(5.0, 5.0, 5.0, 5.0)));
        assert!(p(5.0, 5.0).intersects_rect(r(0.0, 0.0, 10.0, 10.0)));
        assert!(p(0.0, 0.0).intersects_rect(r(0.0, 0.0, 10.0, 10.0)));
        assert!(!p(-1.0, 0.0).intersects_rect(r(0.0, 0.0, 10.0, 10.0)));
        assert!(!p(11.0, 5.0).intersects_rect(r(0.0, 0.0, 10.0, 10.0)));
        assert!(!p(5.0, 11.0).intersects_rect(r(0.0, 0.0, 10.0, 10.0)));
        assert!(!p(5.0, -1.0).intersects_rect(r(0.0, 0.0, 10.0, 10.0)));
    }

    #[test]
    fn test_point_new() {
        let pt = Point::new(1.5, 2.5);
        assert_eq!(pt.x, 1.5);
        assert_eq!(pt.y, 2.5);
    }

    #[test]
    fn test_point_from_tuple() {
        let pt: Point = (3.0, 4.0).into();
        assert_eq!(pt.x, 3.0);
        assert_eq!(pt.y, 4.0);
    }

    #[test]
    fn test_point_eq() {
        assert_eq!(p(1.0, 2.0), p(1.0, 2.0));
        assert_ne!(p(1.0, 2.0), p(1.0, 3.0));
        assert_ne!(p(1.0, 2.0), p(2.0, 2.0));
    }

    #[test]
    fn test_point_copy() {
        let pt1 = p(1.0, 2.0);
        let pt2 = pt1;
        assert_eq!(pt1, pt2);
    }

    #[test]
    fn test_point_negative_coords() {
        let pt = p(-5.0, -10.0);
        assert_eq!(pt.x, -5.0);
        assert_eq!(pt.y, -10.0);
        let rect = pt.rect();
        assert_eq!(rect.min.x, -5.0);
        assert_eq!(rect.min.y, -10.0);
    }

    #[test]
    fn test_point_zero() {
        let pt = p(0.0, 0.0);
        assert_eq!(pt.rect(), r(0.0, 0.0, 0.0, 0.0));
        assert!(pt.intersects_rect(r(-1.0, -1.0, 1.0, 1.0)));
    }

    #[test]
    fn test_point_sys_roundtrip() {
        let pt = p(1.5, 2.5);
        let sys_pt: tg_geom_sys::tg_point = pt.into();
        let back: Point = sys_pt.into();
        assert_eq!(pt, back);
    }

    #[test]
    fn test_point_debug() {
        let pt = p(1.0, 2.0);
        let debug_str = format!("{pt:?}");
        assert!(debug_str.contains("Point"));
        assert!(debug_str.contains("1"));
        assert!(debug_str.contains("2"));
    }
}