tg_geom/
segment.rs

1//! Line segment type.
2
3use crate::{Point, Rect};
4
5/// Directed from point `a` to point `b`.
6#[derive(Debug, Clone, Copy, PartialEq)]
7#[repr(C)]
8pub struct Segment {
9    pub a: Point,
10    pub b: Point,
11}
12
13impl Segment {
14    #[inline]
15    pub const fn new(a: Point, b: Point) -> Self {
16        Self { a, b }
17    }
18
19    #[inline]
20    pub fn rect(self) -> Rect {
21        let r = unsafe { tg_geom_sys::tg_segment_rect(self.into()) };
22        r.into()
23    }
24
25    #[inline]
26    pub fn intersects_segment(self, other: Segment) -> bool {
27        unsafe { tg_geom_sys::tg_segment_intersects_segment(self.into(), other.into()) }
28    }
29}
30
31impl From<tg_geom_sys::tg_segment> for Segment {
32    #[inline]
33    fn from(s: tg_geom_sys::tg_segment) -> Self {
34        Self {
35            a: s.a.into(),
36            b: s.b.into(),
37        }
38    }
39}
40
41impl From<Segment> for tg_geom_sys::tg_segment {
42    #[inline]
43    fn from(s: Segment) -> Self {
44        Self {
45            a: s.a.into(),
46            b: s.b.into(),
47        }
48    }
49}
50
51impl From<(Point, Point)> for Segment {
52    #[inline]
53    fn from((a, b): (Point, Point)) -> Self {
54        Self { a, b }
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    fn p(x: f64, y: f64) -> Point {
63        Point::new(x, y)
64    }
65
66    fn s(ax: f64, ay: f64, bx: f64, by: f64) -> Segment {
67        Segment::new(p(ax, ay), p(bx, by))
68    }
69
70    #[test]
71    fn test_segment_new() {
72        let seg = Segment::new(p(1.0, 2.0), p(3.0, 4.0));
73        assert_eq!(seg.a, p(1.0, 2.0));
74        assert_eq!(seg.b, p(3.0, 4.0));
75    }
76
77    #[test]
78    fn test_segment_rect() {
79        let seg1 = s(1.0, 2.0, 4.0, 6.0);
80        let rect1 = seg1.rect();
81        assert_eq!(rect1.min, p(1.0, 2.0));
82        assert_eq!(rect1.max, p(4.0, 6.0));
83
84        let seg2 = s(4.0, 6.0, 1.0, 2.0);
85        let rect2 = seg2.rect();
86        assert_eq!(rect2.min, p(1.0, 2.0));
87        assert_eq!(rect2.max, p(4.0, 6.0));
88
89        let seg3 = s(5.0, 5.0, 5.0, 5.0);
90        let rect3 = seg3.rect();
91        assert_eq!(rect3.min, rect3.max);
92
93        let seg4 = s(0.0, 5.0, 10.0, 5.0);
94        let rect4 = seg4.rect();
95        assert_eq!(rect4.min.y, rect4.max.y);
96
97        let seg5 = s(5.0, 0.0, 5.0, 10.0);
98        let rect5 = seg5.rect();
99        assert_eq!(rect5.min.x, rect5.max.x);
100    }
101
102    #[test]
103    fn test_segment_intersects_segment_crossing() {
104        let seg1 = s(0.0, 0.0, 1.0, 1.0);
105        let seg2 = s(0.0, 1.0, 1.0, 0.0);
106        assert!(seg1.intersects_segment(seg2));
107    }
108
109    #[test]
110    fn test_segment_intersects_segment_no_intersection() {
111        let seg1 = s(0.0, 0.0, 1.0, 1.0);
112        let seg2 = s(2.0, 2.0, 3.0, 3.0);
113        assert!(!seg1.intersects_segment(seg2));
114    }
115
116    #[test]
117    fn test_segment_intersects_segment_parallel() {
118        let seg1 = s(0.0, 0.0, 1.0, 0.0);
119        let seg2 = s(0.0, 1.0, 1.0, 1.0);
120        assert!(!seg1.intersects_segment(seg2));
121    }
122
123    #[test]
124    fn test_segment_intersects_segment_collinear_overlap() {
125        let seg1 = s(0.0, 0.0, 2.0, 0.0);
126        let seg2 = s(1.0, 0.0, 3.0, 0.0);
127        assert!(seg1.intersects_segment(seg2));
128    }
129
130    #[test]
131    fn test_segment_intersects_segment_endpoint_touch() {
132        let seg1 = s(0.0, 0.0, 1.0, 0.0);
133        let seg2 = s(1.0, 0.0, 2.0, 0.0);
134        assert!(seg1.intersects_segment(seg2));
135    }
136
137    #[test]
138    fn test_segment_intersects_segment_t_intersection() {
139        let seg1 = s(0.0, 0.0, 10.0, 0.0);
140        let seg2 = s(5.0, 0.0, 5.0, 10.0);
141        assert!(seg1.intersects_segment(seg2));
142    }
143
144    #[test]
145    fn test_segment_intersects_segment_self() {
146        let seg = s(0.0, 0.0, 1.0, 1.0);
147        assert!(seg.intersects_segment(seg));
148    }
149
150    #[test]
151    fn test_segment_intersects_segment_vertical() {
152        let seg1 = s(4.0, 4.0, 4.0, 4.0);
153        let seg2 = s(4.0, 4.0, 4.0, 4.0);
154        assert!(seg1.intersects_segment(seg2));
155
156        let seg3 = s(5.0, 0.0, 5.0, 10.0);
157        let seg4 = s(0.0, 5.0, 10.0, 5.0);
158        assert!(seg3.intersects_segment(seg4));
159    }
160
161    #[test]
162    fn test_segment_from_points() {
163        let seg: Segment = (p(1.0, 2.0), p(3.0, 4.0)).into();
164        assert_eq!(seg.a, p(1.0, 2.0));
165        assert_eq!(seg.b, p(3.0, 4.0));
166    }
167
168    #[test]
169    fn test_segment_sys_roundtrip() {
170        let seg = s(1.0, 2.0, 3.0, 4.0);
171        let sys_seg: tg_geom_sys::tg_segment = seg.into();
172        let back: Segment = sys_seg.into();
173        assert_eq!(seg, back);
174    }
175
176    #[test]
177    fn test_segment_eq() {
178        assert_eq!(s(0.0, 0.0, 1.0, 1.0), s(0.0, 0.0, 1.0, 1.0));
179        assert_ne!(s(0.0, 0.0, 1.0, 1.0), s(0.0, 0.0, 2.0, 2.0));
180    }
181
182    #[test]
183    fn test_segment_debug() {
184        let seg = s(1.0, 2.0, 3.0, 4.0);
185        let debug_str = format!("{seg:?}");
186        assert!(debug_str.contains("Segment"));
187    }
188
189    #[test]
190    fn test_segment_negative_coords() {
191        let seg = s(-5.0, -3.0, -1.0, -7.0);
192        let rect = seg.rect();
193        assert_eq!(rect.min.x, -5.0);
194        assert_eq!(rect.min.y, -7.0);
195        assert_eq!(rect.max.x, -1.0);
196        assert_eq!(rect.max.y, -3.0);
197    }
198}