tg_geom/
point.rs

1//! 2D point type.
2
3use crate::Rect;
4
5/// X and Y coordinates.
6#[derive(Debug, Clone, Copy, PartialEq)]
7#[repr(C)]
8pub struct Point {
9    pub x: f64,
10    pub y: f64,
11}
12
13impl Point {
14    #[inline]
15    pub const fn new(x: f64, y: f64) -> Self {
16        Self { x, y }
17    }
18
19    /// Returns a zero-area bounding rectangle.
20    #[inline]
21    pub fn rect(self) -> Rect {
22        let r = unsafe { tg_geom_sys::tg_point_rect(self.into()) };
23        r.into()
24    }
25
26    #[inline]
27    pub fn intersects_rect(self, rect: Rect) -> bool {
28        unsafe { tg_geom_sys::tg_point_intersects_rect(self.into(), rect.into()) }
29    }
30}
31
32impl From<tg_geom_sys::tg_point> for Point {
33    #[inline]
34    fn from(p: tg_geom_sys::tg_point) -> Self {
35        Self { x: p.x, y: p.y }
36    }
37}
38
39impl From<Point> for tg_geom_sys::tg_point {
40    #[inline]
41    fn from(p: Point) -> Self {
42        Self { x: p.x, y: p.y }
43    }
44}
45
46impl From<(f64, f64)> for Point {
47    #[inline]
48    fn from((x, y): (f64, f64)) -> Self {
49        Self { x, y }
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    fn p(x: f64, y: f64) -> Point {
58        Point::new(x, y)
59    }
60
61    fn r(min_x: f64, min_y: f64, max_x: f64, max_y: f64) -> Rect {
62        Rect::from_coords(min_x, min_y, max_x, max_y)
63    }
64
65    #[test]
66    fn test_point_rect() {
67        let pt = p(5.0, 5.0);
68        let rect = pt.rect();
69        assert_eq!(rect, r(5.0, 5.0, 5.0, 5.0));
70    }
71
72    #[test]
73    fn test_point_intersects_rect() {
74        assert!(p(5.0, 5.0).intersects_rect(r(5.0, 5.0, 5.0, 5.0)));
75        assert!(p(5.0, 5.0).intersects_rect(r(0.0, 0.0, 10.0, 10.0)));
76        assert!(p(0.0, 0.0).intersects_rect(r(0.0, 0.0, 10.0, 10.0)));
77        assert!(!p(-1.0, 0.0).intersects_rect(r(0.0, 0.0, 10.0, 10.0)));
78        assert!(!p(11.0, 5.0).intersects_rect(r(0.0, 0.0, 10.0, 10.0)));
79        assert!(!p(5.0, 11.0).intersects_rect(r(0.0, 0.0, 10.0, 10.0)));
80        assert!(!p(5.0, -1.0).intersects_rect(r(0.0, 0.0, 10.0, 10.0)));
81    }
82
83    #[test]
84    fn test_point_new() {
85        let pt = Point::new(1.5, 2.5);
86        assert_eq!(pt.x, 1.5);
87        assert_eq!(pt.y, 2.5);
88    }
89
90    #[test]
91    fn test_point_from_tuple() {
92        let pt: Point = (3.0, 4.0).into();
93        assert_eq!(pt.x, 3.0);
94        assert_eq!(pt.y, 4.0);
95    }
96
97    #[test]
98    fn test_point_eq() {
99        assert_eq!(p(1.0, 2.0), p(1.0, 2.0));
100        assert_ne!(p(1.0, 2.0), p(1.0, 3.0));
101        assert_ne!(p(1.0, 2.0), p(2.0, 2.0));
102    }
103
104    #[test]
105    fn test_point_copy() {
106        let pt1 = p(1.0, 2.0);
107        let pt2 = pt1;
108        assert_eq!(pt1, pt2);
109    }
110
111    #[test]
112    fn test_point_negative_coords() {
113        let pt = p(-5.0, -10.0);
114        assert_eq!(pt.x, -5.0);
115        assert_eq!(pt.y, -10.0);
116        let rect = pt.rect();
117        assert_eq!(rect.min.x, -5.0);
118        assert_eq!(rect.min.y, -10.0);
119    }
120
121    #[test]
122    fn test_point_zero() {
123        let pt = p(0.0, 0.0);
124        assert_eq!(pt.rect(), r(0.0, 0.0, 0.0, 0.0));
125        assert!(pt.intersects_rect(r(-1.0, -1.0, 1.0, 1.0)));
126    }
127
128    #[test]
129    fn test_point_sys_roundtrip() {
130        let pt = p(1.5, 2.5);
131        let sys_pt: tg_geom_sys::tg_point = pt.into();
132        let back: Point = sys_pt.into();
133        assert_eq!(pt, back);
134    }
135
136    #[test]
137    fn test_point_debug() {
138        let pt = p(1.0, 2.0);
139        let debug_str = format!("{pt:?}");
140        assert!(debug_str.contains("Point"));
141        assert!(debug_str.contains("1"));
142        assert!(debug_str.contains("2"));
143    }
144}