tg_geom/
poly.rs

1//! Polygon type with exterior boundary and optional holes.
2
3use core::marker::PhantomData;
4use core::ptr::NonNull;
5
6use crate::error::{Error, Result};
7use crate::{Rect, Ring, RingRef};
8
9macro_rules! impl_poly_methods {
10    ($get_ptr:expr, $life:lifetime) => {
11        #[inline]
12        pub fn rect(&self) -> Rect {
13            let ptr = $get_ptr(self);
14            let r = unsafe { tg_geom_sys::tg_poly_rect(ptr) };
15            r.into()
16        }
17
18        #[inline]
19        pub fn num_holes(&self) -> usize {
20            let ptr = $get_ptr(self);
21            unsafe { tg_geom_sys::tg_poly_num_holes(ptr) as usize }
22        }
23
24        #[inline]
25        pub fn clockwise(&self) -> bool {
26            let ptr = $get_ptr(self);
27            unsafe { tg_geom_sys::tg_poly_clockwise(ptr) }
28        }
29
30        #[inline]
31        pub fn exterior(&self) -> RingRef<$life> {
32            let ptr = $get_ptr(self);
33            let ptr = unsafe { tg_geom_sys::tg_poly_exterior(ptr) };
34            unsafe { RingRef::from_raw(ptr).unwrap() }
35        }
36
37        #[inline]
38        pub fn hole_at(&self, index: usize) -> Option<RingRef<$life>> {
39            if index >= self.num_holes() {
40                None
41            } else {
42                let ptr = $get_ptr(self);
43                let ptr = unsafe { tg_geom_sys::tg_poly_hole_at(ptr, index as libc::c_int) };
44                unsafe { RingRef::from_raw(ptr) }
45            }
46        }
47    };
48}
49
50/// An owned polygon.
51pub struct Poly {
52    ptr: NonNull<tg_geom_sys::tg_poly>,
53}
54
55impl Poly {
56    impl_poly_methods!(|s: &Self| s.ptr.as_ptr(), '_);
57
58    pub fn new(exterior: &Ring, holes: &[&Ring]) -> Result<Self> {
59        let hole_ptrs: Vec<*const tg_geom_sys::tg_ring> =
60            holes.iter().map(|r| r.as_ptr()).collect();
61
62        let ptr = unsafe {
63            tg_geom_sys::tg_poly_new(
64                exterior.as_ptr(),
65                hole_ptrs.as_ptr(),
66                holes.len() as libc::c_int,
67            )
68        };
69        NonNull::new(ptr)
70            .map(|ptr| Self { ptr })
71            .ok_or(Error::OutOfMemory)
72    }
73
74    pub fn new_simple(exterior: &Ring) -> Result<Self> {
75        let ptr = unsafe { tg_geom_sys::tg_poly_new(exterior.as_ptr(), core::ptr::null(), 0) };
76        NonNull::new(ptr)
77            .map(|ptr| Self { ptr })
78            .ok_or(Error::OutOfMemory)
79    }
80
81    /// # Safety
82    /// The pointer must be valid and not owned elsewhere.
83    pub unsafe fn from_raw(ptr: *mut tg_geom_sys::tg_poly) -> Option<Self> {
84        NonNull::new(ptr).map(|ptr| Self { ptr })
85    }
86
87    #[inline]
88    pub fn as_ptr(&self) -> *const tg_geom_sys::tg_poly {
89        self.ptr.as_ptr()
90    }
91
92    pub fn into_raw(self) -> *mut tg_geom_sys::tg_poly {
93        let ptr = self.ptr.as_ptr();
94        core::mem::forget(self);
95        ptr
96    }
97
98    pub fn copy(&self) -> Result<Self> {
99        let ptr = unsafe { tg_geom_sys::tg_poly_copy(self.ptr.as_ptr()) };
100        NonNull::new(ptr)
101            .map(|ptr| Self { ptr })
102            .ok_or(Error::CopyFailed)
103    }
104
105    pub fn clone_ref(&self) -> Result<Self> {
106        let ptr = unsafe { tg_geom_sys::tg_poly_clone(self.ptr.as_ptr()) };
107        NonNull::new(ptr)
108            .map(|ptr| Self { ptr })
109            .ok_or(Error::CopyFailed)
110    }
111
112    pub fn memsize(&self) -> usize {
113        unsafe { tg_geom_sys::tg_poly_memsize(self.ptr.as_ptr()) }
114    }
115
116    pub fn iter_holes(&self) -> impl Iterator<Item = RingRef<'_>> {
117        (0..self.num_holes()).map(move |i| self.hole_at(i).unwrap())
118    }
119}
120
121impl Drop for Poly {
122    fn drop(&mut self) {
123        unsafe { tg_geom_sys::tg_poly_free(self.ptr.as_ptr()) }
124    }
125}
126
127impl core::fmt::Debug for Poly {
128    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
129        f.debug_struct("Poly")
130            .field("num_holes", &self.num_holes())
131            .field("rect", &self.rect())
132            .field("clockwise", &self.clockwise())
133            .finish()
134    }
135}
136
137unsafe impl Send for Poly {}
138unsafe impl Sync for Poly {}
139
140/// A borrowed reference to a polygon.
141#[derive(Clone, Copy)]
142pub struct PolyRef<'a> {
143    ptr: *const tg_geom_sys::tg_poly,
144    _marker: PhantomData<&'a ()>,
145}
146
147unsafe impl Send for PolyRef<'_> {}
148unsafe impl Sync for PolyRef<'_> {}
149
150impl<'a> PolyRef<'a> {
151    impl_poly_methods!(|s: &Self| s.ptr, 'a);
152
153    /// # Safety
154    /// The pointer must be valid for the lifetime `'a`.
155    #[inline]
156    pub unsafe fn from_raw(ptr: *const tg_geom_sys::tg_poly) -> Option<Self> {
157        if ptr.is_null() {
158            None
159        } else {
160            Some(Self {
161                ptr,
162                _marker: PhantomData,
163            })
164        }
165    }
166
167    #[inline]
168    pub fn as_ptr(&self) -> *const tg_geom_sys::tg_poly {
169        self.ptr
170    }
171
172    pub fn to_owned(&self) -> Result<Poly> {
173        let ptr = unsafe { tg_geom_sys::tg_poly_copy(self.ptr) };
174        NonNull::new(ptr)
175            .map(|ptr| Poly { ptr })
176            .ok_or(Error::CopyFailed)
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183    use crate::Point;
184
185    fn p(x: f64, y: f64) -> Point {
186        Point::new(x, y)
187    }
188
189    fn r(min_x: f64, min_y: f64, max_x: f64, max_y: f64) -> Rect {
190        Rect::from_coords(min_x, min_y, max_x, max_y)
191    }
192
193    fn octagon() -> Vec<Point> {
194        vec![
195            p(3.0, 0.0),
196            p(7.0, 0.0),
197            p(10.0, 3.0),
198            p(10.0, 7.0),
199            p(7.0, 10.0),
200            p(3.0, 10.0),
201            p(0.0, 7.0),
202            p(0.0, 3.0),
203            p(3.0, 0.0),
204        ]
205    }
206
207    fn small_hole() -> Vec<Point> {
208        vec![
209            p(4.0, 4.0),
210            p(6.0, 4.0),
211            p(6.0, 6.0),
212            p(4.0, 6.0),
213            p(4.0, 4.0),
214        ]
215    }
216
217    fn rect_ring() -> Vec<Point> {
218        vec![
219            p(0.0, 0.0),
220            p(10.0, 0.0),
221            p(10.0, 10.0),
222            p(0.0, 10.0),
223            p(0.0, 0.0),
224        ]
225    }
226
227    #[test]
228    fn test_poly_rect() {
229        let exterior = Ring::new(&octagon()).unwrap();
230        let poly = Poly::new_simple(&exterior).unwrap();
231        assert_eq!(poly.rect(), r(0.0, 0.0, 10.0, 10.0));
232    }
233
234    #[test]
235    fn test_poly_rect_with_hole() {
236        let exterior = Ring::new(&octagon()).unwrap();
237        let hole = Ring::new(&small_hole()).unwrap();
238        let poly = Poly::new(&exterior, &[&hole]).unwrap();
239        assert_eq!(poly.rect(), r(0.0, 0.0, 10.0, 10.0));
240    }
241
242    #[test]
243    fn test_poly_exterior_holes() {
244        let exterior = Ring::new(&octagon()).unwrap();
245        let poly = Poly::new_simple(&exterior).unwrap();
246        assert_eq!(poly.num_holes(), 0);
247        assert_eq!(poly.exterior().num_points(), 9);
248    }
249
250    #[test]
251    fn test_poly_with_hole() {
252        let exterior = Ring::new(&octagon()).unwrap();
253        let hole = Ring::new(&small_hole()).unwrap();
254        let poly = Poly::new(&exterior, &[&hole]).unwrap();
255
256        assert_eq!(poly.num_holes(), 1);
257        let hole_ref = poly.hole_at(0).unwrap();
258        assert_eq!(hole_ref.num_points(), 5);
259    }
260
261    #[test]
262    fn test_poly_hole_at_bounds() {
263        let exterior = Ring::new(&octagon()).unwrap();
264        let hole = Ring::new(&small_hole()).unwrap();
265        let poly = Poly::new(&exterior, &[&hole]).unwrap();
266
267        assert!(poly.hole_at(0).is_some());
268        assert!(poly.hole_at(1).is_none());
269        assert!(poly.hole_at(100).is_none());
270    }
271
272    #[test]
273    fn test_poly_no_holes() {
274        let exterior = Ring::new(&rect_ring()).unwrap();
275        let poly = Poly::new_simple(&exterior).unwrap();
276        assert_eq!(poly.num_holes(), 0);
277        assert!(poly.hole_at(0).is_none());
278    }
279
280    #[test]
281    fn test_poly_clockwise() {
282        let ccw_exterior = Ring::new(&[
283            p(0.0, 0.0),
284            p(10.0, 0.0),
285            p(10.0, 10.0),
286            p(0.0, 10.0),
287            p(0.0, 0.0),
288        ])
289        .unwrap();
290        let poly_ccw = Poly::new_simple(&ccw_exterior).unwrap();
291        assert!(!poly_ccw.clockwise());
292
293        let cw_exterior = Ring::new(&[
294            p(0.0, 0.0),
295            p(0.0, 10.0),
296            p(10.0, 10.0),
297            p(10.0, 0.0),
298            p(0.0, 0.0),
299        ])
300        .unwrap();
301        let poly_cw = Poly::new_simple(&cw_exterior).unwrap();
302        assert!(poly_cw.clockwise());
303    }
304
305    #[test]
306    fn test_poly_copy() {
307        let exterior = Ring::new(&octagon()).unwrap();
308        let hole = Ring::new(&small_hole()).unwrap();
309        let poly = Poly::new(&exterior, &[&hole]).unwrap();
310
311        let copy = poly.copy().unwrap();
312        assert_eq!(poly.num_holes(), copy.num_holes());
313        assert_eq!(poly.rect(), copy.rect());
314    }
315
316    #[test]
317    fn test_poly_clone_ref() {
318        let exterior = Ring::new(&octagon()).unwrap();
319        let poly = Poly::new_simple(&exterior).unwrap();
320        let cloned = poly.clone_ref().unwrap();
321        assert_eq!(poly.num_holes(), cloned.num_holes());
322    }
323
324    #[test]
325    fn test_poly_memsize() {
326        let exterior = Ring::new(&octagon()).unwrap();
327        let poly = Poly::new_simple(&exterior).unwrap();
328        assert!(poly.memsize() > 0);
329    }
330
331    #[test]
332    fn test_poly_iter_holes() {
333        let exterior = Ring::new(&rect_ring()).unwrap();
334        let hole1 = Ring::new(&[
335            p(2.0, 2.0),
336            p(3.0, 2.0),
337            p(3.0, 3.0),
338            p(2.0, 3.0),
339            p(2.0, 2.0),
340        ])
341        .unwrap();
342        let hole2 = Ring::new(&[
343            p(6.0, 6.0),
344            p(7.0, 6.0),
345            p(7.0, 7.0),
346            p(6.0, 7.0),
347            p(6.0, 6.0),
348        ])
349        .unwrap();
350        let poly = Poly::new(&exterior, &[&hole1, &hole2]).unwrap();
351
352        let holes: Vec<RingRef<'_>> = poly.iter_holes().collect();
353        assert_eq!(holes.len(), 2);
354    }
355
356    #[test]
357    fn test_poly_debug() {
358        let exterior = Ring::new(&octagon()).unwrap();
359        let poly = Poly::new_simple(&exterior).unwrap();
360        let debug_str = format!("{poly:?}");
361        assert!(debug_str.contains("Poly"));
362        assert!(debug_str.contains("num_holes"));
363    }
364
365    #[test]
366    fn test_poly_send_sync() {
367        fn assert_send<T: Send>() {}
368        fn assert_sync<T: Sync>() {}
369        assert_send::<Poly>();
370        assert_sync::<Poly>();
371    }
372
373    #[test]
374    fn test_poly_raw_pointer() {
375        let exterior = Ring::new(&octagon()).unwrap();
376        let poly = Poly::new_simple(&exterior).unwrap();
377        let num_holes = poly.num_holes();
378        let ptr = poly.into_raw();
379        let recovered = unsafe { Poly::from_raw(ptr).unwrap() };
380        assert_eq!(recovered.num_holes(), num_holes);
381    }
382
383    #[test]
384    fn test_polyref_to_owned() {
385        let exterior = Ring::new(&octagon()).unwrap();
386        let poly = Poly::new_simple(&exterior).unwrap();
387
388        let poly_ref = unsafe { PolyRef::from_raw(poly.as_ptr()).unwrap() };
389        let owned = poly_ref.to_owned().unwrap();
390        assert_eq!(owned.num_holes(), 0);
391    }
392}