tg_geom/
ring.rs

1//! Closed ring type for polygon boundaries and holes.
2
3use core::marker::PhantomData;
4use core::ptr::NonNull;
5
6use crate::error::{Error, Result};
7use crate::{IndexKind, Point, Rect, Segment};
8
9macro_rules! impl_ring_methods {
10    ($get_ptr:expr) => {
11        #[inline]
12        pub fn rect(&self) -> Rect {
13            let ptr = $get_ptr(self);
14            let r = unsafe { tg_geom_sys::tg_ring_rect(ptr) };
15            r.into()
16        }
17
18        #[inline]
19        pub fn num_points(&self) -> usize {
20            let ptr = $get_ptr(self);
21            unsafe { tg_geom_sys::tg_ring_num_points(ptr) as usize }
22        }
23
24        #[inline]
25        pub fn point_at(&self, index: usize) -> Option<Point> {
26            if index >= self.num_points() {
27                None
28            } else {
29                let ptr = $get_ptr(self);
30                let p = unsafe { tg_geom_sys::tg_ring_point_at(ptr, index as libc::c_int) };
31                Some(p.into())
32            }
33        }
34
35        #[inline]
36        pub fn points(&self) -> &[Point] {
37            let ptr = $get_ptr(self);
38            let raw = unsafe { tg_geom_sys::tg_ring_points(ptr) };
39            if raw.is_null() {
40                &[]
41            } else {
42                unsafe { core::slice::from_raw_parts(raw as *const Point, self.num_points()) }
43            }
44        }
45
46        #[inline]
47        pub fn num_segments(&self) -> usize {
48            let ptr = $get_ptr(self);
49            unsafe { tg_geom_sys::tg_ring_num_segments(ptr) as usize }
50        }
51
52        #[inline]
53        pub fn segment_at(&self, index: usize) -> Option<Segment> {
54            if index >= self.num_segments() {
55                None
56            } else {
57                let ptr = $get_ptr(self);
58                let s = unsafe { tg_geom_sys::tg_ring_segment_at(ptr, index as libc::c_int) };
59                Some(s.into())
60            }
61        }
62
63        #[inline]
64        pub fn convex(&self) -> bool {
65            let ptr = $get_ptr(self);
66            unsafe { tg_geom_sys::tg_ring_convex(ptr) }
67        }
68
69        #[inline]
70        pub fn clockwise(&self) -> bool {
71            let ptr = $get_ptr(self);
72            unsafe { tg_geom_sys::tg_ring_clockwise(ptr) }
73        }
74
75        #[inline]
76        pub fn area(&self) -> f64 {
77            let ptr = $get_ptr(self);
78            unsafe { tg_geom_sys::tg_ring_area(ptr) }
79        }
80
81        #[inline]
82        pub fn perimeter(&self) -> f64 {
83            let ptr = $get_ptr(self);
84            unsafe { tg_geom_sys::tg_ring_perimeter(ptr) }
85        }
86    };
87}
88
89/// An owned closed ring.
90pub struct Ring {
91    ptr: NonNull<tg_geom_sys::tg_ring>,
92}
93
94impl Ring {
95    impl_ring_methods!(|s: &Self| s.ptr.as_ptr());
96
97    crate::utils::impl_nearest_segment!(tg_geom_sys::tg_ring_nearest_segment);
98
99    pub fn new(points: &[Point]) -> Result<Self> {
100        Self::new_ix(points, IndexKind::Default)
101    }
102
103    pub fn new_ix(points: &[Point], ix: IndexKind) -> Result<Self> {
104        let ptr = unsafe {
105            tg_geom_sys::tg_ring_new_ix(
106                points.as_ptr() as *const tg_geom_sys::tg_point,
107                points.len() as libc::c_int,
108                ix.into(),
109            )
110        };
111        NonNull::new(ptr)
112            .map(|ptr| Self { ptr })
113            .ok_or(Error::OutOfMemory)
114    }
115
116    /// # Safety
117    /// The pointer must be valid and not owned elsewhere.
118    pub unsafe fn from_raw(ptr: *mut tg_geom_sys::tg_ring) -> Option<Self> {
119        NonNull::new(ptr).map(|ptr| Self { ptr })
120    }
121
122    #[inline]
123    pub fn as_ptr(&self) -> *const tg_geom_sys::tg_ring {
124        self.ptr.as_ptr()
125    }
126
127    pub fn into_raw(self) -> *mut tg_geom_sys::tg_ring {
128        let ptr = self.ptr.as_ptr();
129        core::mem::forget(self);
130        ptr
131    }
132
133    pub fn copy(&self) -> Result<Self> {
134        let ptr = unsafe { tg_geom_sys::tg_ring_copy(self.ptr.as_ptr()) };
135        NonNull::new(ptr)
136            .map(|ptr| Self { ptr })
137            .ok_or(Error::CopyFailed)
138    }
139
140    pub fn clone_ref(&self) -> Result<Self> {
141        let ptr = unsafe { tg_geom_sys::tg_ring_clone(self.ptr.as_ptr()) };
142        NonNull::new(ptr)
143            .map(|ptr| Self { ptr })
144            .ok_or(Error::CopyFailed)
145    }
146
147    pub fn memsize(&self) -> usize {
148        unsafe { tg_geom_sys::tg_ring_memsize(self.ptr.as_ptr()) }
149    }
150
151    pub fn index_spread(&self) -> i32 {
152        unsafe { tg_geom_sys::tg_ring_index_spread(self.ptr.as_ptr()) }
153    }
154
155    pub fn index_num_levels(&self) -> i32 {
156        unsafe { tg_geom_sys::tg_ring_index_num_levels(self.ptr.as_ptr()) }
157    }
158
159    pub fn index_level_num_rects(&self, level: i32) -> i32 {
160        unsafe { tg_geom_sys::tg_ring_index_level_num_rects(self.ptr.as_ptr(), level) }
161    }
162
163    pub fn index_level_rect(&self, level: i32, rect_idx: i32) -> Rect {
164        let r =
165            unsafe { tg_geom_sys::tg_ring_index_level_rect(self.ptr.as_ptr(), level, rect_idx) };
166        r.into()
167    }
168
169    pub fn iter_points(&self) -> impl Iterator<Item = Point> + '_ {
170        self.points().iter().copied()
171    }
172
173    pub fn iter_segments(&self) -> impl Iterator<Item = Segment> + '_ {
174        (0..self.num_segments()).map(move |i| self.segment_at(i).unwrap())
175    }
176
177    /// Return `false` to stop iteration.
178    pub fn line_search<F>(&self, line: &crate::Line, mut iter: F)
179    where
180        F: FnMut(Segment, usize, Segment, usize) -> bool,
181    {
182        unsafe extern "C" fn trampoline<F>(
183            aseg: tg_geom_sys::tg_segment, aidx: libc::c_int, bseg: tg_geom_sys::tg_segment,
184            bidx: libc::c_int, udata: *mut libc::c_void,
185        ) -> bool
186        where
187            F: FnMut(Segment, usize, Segment, usize) -> bool,
188        {
189            let iter = unsafe { &mut *(udata as *mut F) };
190            iter(aseg.into(), aidx as usize, bseg.into(), bidx as usize)
191        }
192
193        unsafe {
194            tg_geom_sys::tg_ring_line_search(
195                self.ptr.as_ptr(),
196                line.as_ptr(),
197                Some(trampoline::<F>),
198                core::ptr::from_mut(&mut iter).cast(),
199            );
200        }
201    }
202
203    /// Return `false` to stop iteration.
204    pub fn ring_search<F>(&self, other: &Ring, mut iter: F)
205    where
206        F: FnMut(Segment, usize, Segment, usize) -> bool,
207    {
208        unsafe extern "C" fn trampoline<F>(
209            aseg: tg_geom_sys::tg_segment, aidx: libc::c_int, bseg: tg_geom_sys::tg_segment,
210            bidx: libc::c_int, udata: *mut libc::c_void,
211        ) -> bool
212        where
213            F: FnMut(Segment, usize, Segment, usize) -> bool,
214        {
215            let iter = unsafe { &mut *(udata as *mut F) };
216            iter(aseg.into(), aidx as usize, bseg.into(), bidx as usize)
217        }
218
219        unsafe {
220            tg_geom_sys::tg_ring_ring_search(
221                self.ptr.as_ptr(),
222                other.ptr.as_ptr(),
223                Some(trampoline::<F>),
224                core::ptr::from_mut(&mut iter).cast(),
225            );
226        }
227    }
228}
229
230impl Drop for Ring {
231    fn drop(&mut self) {
232        unsafe { tg_geom_sys::tg_ring_free(self.ptr.as_ptr()) }
233    }
234}
235
236impl core::fmt::Debug for Ring {
237    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
238        f.debug_struct("Ring")
239            .field("num_points", &self.num_points())
240            .field("area", &self.area())
241            .field("convex", &self.convex())
242            .field("clockwise", &self.clockwise())
243            .finish()
244    }
245}
246
247unsafe impl Send for Ring {}
248unsafe impl Sync for Ring {}
249
250/// A borrowed reference to a ring.
251#[derive(Clone, Copy)]
252pub struct RingRef<'a> {
253    ptr: *const tg_geom_sys::tg_ring,
254    _marker: PhantomData<&'a ()>,
255}
256
257unsafe impl Send for RingRef<'_> {}
258unsafe impl Sync for RingRef<'_> {}
259
260impl RingRef<'_> {
261    impl_ring_methods!(|s: &Self| s.ptr);
262
263    /// # Safety
264    /// The pointer must be valid for the lifetime `'a`.
265    #[inline]
266    pub unsafe fn from_raw(ptr: *const tg_geom_sys::tg_ring) -> Option<Self> {
267        if ptr.is_null() {
268            None
269        } else {
270            Some(Self {
271                ptr,
272                _marker: PhantomData,
273            })
274        }
275    }
276
277    #[inline]
278    pub fn as_ptr(&self) -> *const tg_geom_sys::tg_ring {
279        self.ptr
280    }
281
282    pub fn to_owned(&self) -> Result<Ring> {
283        let ptr = unsafe { tg_geom_sys::tg_ring_copy(self.ptr) };
284        NonNull::new(ptr)
285            .map(|ptr| Ring { ptr })
286            .ok_or(Error::CopyFailed)
287    }
288}
289
290#[cfg(test)]
291mod tests {
292    use super::*;
293
294    fn p(x: f64, y: f64) -> Point {
295        Point::new(x, y)
296    }
297
298    fn r(min_x: f64, min_y: f64, max_x: f64, max_y: f64) -> Rect {
299        Rect::from_coords(min_x, min_y, max_x, max_y)
300    }
301
302    fn rectangle() -> Vec<Point> {
303        vec![
304            p(0.0, 0.0),
305            p(10.0, 0.0),
306            p(10.0, 10.0),
307            p(0.0, 10.0),
308            p(0.0, 0.0),
309        ]
310    }
311
312    fn octagon() -> Vec<Point> {
313        vec![
314            p(3.0, 0.0),
315            p(7.0, 0.0),
316            p(10.0, 3.0),
317            p(10.0, 7.0),
318            p(7.0, 10.0),
319            p(3.0, 10.0),
320            p(0.0, 7.0),
321            p(0.0, 3.0),
322            p(3.0, 0.0),
323        ]
324    }
325
326    fn concave1() -> Vec<Point> {
327        vec![
328            p(5.0, 0.0),
329            p(10.0, 0.0),
330            p(10.0, 10.0),
331            p(0.0, 10.0),
332            p(0.0, 5.0),
333            p(5.0, 5.0),
334            p(5.0, 0.0),
335        ]
336    }
337
338    fn triangle() -> Vec<Point> {
339        vec![p(0.0, 0.0), p(10.0, 0.0), p(5.0, 10.0), p(0.0, 0.0)]
340    }
341
342    #[test]
343    fn test_ring_new() {
344        let ring = Ring::new(&rectangle()).unwrap();
345        assert_eq!(ring.num_points(), 5);
346        assert_eq!(ring.num_segments(), 4);
347    }
348
349    #[test]
350    fn test_ring_not_closed() {
351        let points = vec![p(1.0, 1.0), p(2.0, 1.0), p(2.0, 2.0), p(1.0, 2.0)];
352        let ring = Ring::new(&points).unwrap();
353        assert!(ring.num_points() >= 4);
354    }
355
356    #[test]
357    fn test_ring_rect() {
358        let ring = Ring::new(&octagon()).unwrap();
359        assert_eq!(ring.rect(), r(0.0, 0.0, 10.0, 10.0));
360    }
361
362    #[test]
363    fn test_ring_clockwise() {
364        let ccw = Ring::new(&[
365            p(0.0, 0.0),
366            p(10.0, 0.0),
367            p(10.0, 10.0),
368            p(0.0, 10.0),
369            p(0.0, 0.0),
370        ])
371        .unwrap();
372        assert!(!ccw.clockwise());
373
374        let cw = Ring::new(&[
375            p(0.0, 0.0),
376            p(0.0, 10.0),
377            p(10.0, 10.0),
378            p(10.0, 0.0),
379            p(0.0, 0.0),
380        ])
381        .unwrap();
382        assert!(cw.clockwise());
383    }
384
385    #[test]
386    fn test_ring_convex() {
387        let octagon_ring = Ring::new(&octagon()).unwrap();
388        assert!(octagon_ring.convex());
389
390        let concave_ring = Ring::new(&concave1()).unwrap();
391        assert!(!concave_ring.convex());
392
393        let triangle_ring = Ring::new(&triangle()).unwrap();
394        assert!(triangle_ring.convex());
395    }
396
397    #[test]
398    fn test_ring_area_perimeter() {
399        let u1_ring = Ring::new(&[p(0.0, 10.0), p(0.0, 0.0), p(10.0, 0.0), p(10.0, 10.0)]).unwrap();
400        assert_eq!(u1_ring.perimeter(), 40.0);
401        assert_eq!(u1_ring.area(), 100.0);
402
403        let rect_ring = Ring::new(&rectangle()).unwrap();
404        assert_eq!(rect_ring.perimeter(), 40.0);
405        assert_eq!(rect_ring.area(), 100.0);
406    }
407
408    #[test]
409    fn test_ring_points() {
410        let ring = Ring::new(&octagon()).unwrap();
411        let points = ring.points();
412        assert_eq!(points.len(), ring.num_points());
413
414        for (i, &p2) in points.iter().enumerate() {
415            let p1 = ring.point_at(i).unwrap();
416            assert_eq!(p1, p2);
417        }
418    }
419
420    #[test]
421    fn test_ring_point_at_bounds() {
422        let ring = Ring::new(&rectangle()).unwrap();
423        assert!(ring.point_at(0).is_some());
424        assert!(ring.point_at(4).is_some());
425        assert!(ring.point_at(5).is_none());
426        assert!(ring.point_at(100).is_none());
427    }
428
429    #[test]
430    fn test_ring_segment_at() {
431        let ring = Ring::new(&rectangle()).unwrap();
432        assert_eq!(ring.num_segments(), 4);
433
434        let seg0 = ring.segment_at(0).unwrap();
435        assert_eq!(seg0.a, p(0.0, 0.0));
436        assert_eq!(seg0.b, p(10.0, 0.0));
437
438        assert!(ring.segment_at(4).is_none());
439    }
440
441    #[test]
442    fn test_ring_index_properties() {
443        let ring = Ring::new_ix(&octagon(), IndexKind::Natural).unwrap();
444        assert!(ring.memsize() > 0);
445        assert!(ring.index_num_levels() >= 0);
446    }
447
448    #[test]
449    fn test_ring_copy() {
450        let ring = Ring::new(&octagon()).unwrap();
451        let copy = ring.copy().unwrap();
452        assert_eq!(ring.num_points(), copy.num_points());
453        assert_eq!(ring.area(), copy.area());
454    }
455
456    #[test]
457    fn test_ring_clone_ref() {
458        let ring = Ring::new(&octagon()).unwrap();
459        let cloned = ring.clone_ref().unwrap();
460        assert_eq!(ring.num_points(), cloned.num_points());
461    }
462
463    #[test]
464    fn test_ring_iter_points() {
465        let ring = Ring::new(&rectangle()).unwrap();
466        let points: Vec<Point> = ring.iter_points().collect();
467        assert_eq!(points.len(), 5);
468    }
469
470    #[test]
471    fn test_ring_iter_segments() {
472        let ring = Ring::new(&rectangle()).unwrap();
473        let segments: Vec<Segment> = ring.iter_segments().collect();
474        assert_eq!(segments.len(), 4);
475    }
476
477    #[test]
478    fn test_ring_ring_search() {
479        let ring1 = Ring::new(&[
480            p(0.0, 0.0),
481            p(5.0, 0.0),
482            p(5.0, 5.0),
483            p(0.0, 5.0),
484            p(0.0, 0.0),
485        ])
486        .unwrap();
487        let ring2 = Ring::new(&[
488            p(2.5, 2.5),
489            p(7.5, 2.5),
490            p(7.5, 7.5),
491            p(2.5, 7.5),
492            p(2.5, 2.5),
493        ])
494        .unwrap();
495
496        let mut count = 0;
497        ring1.ring_search(&ring2, |_seg1, _idx1, _seg2, _idx2| {
498            count += 1;
499            true
500        });
501        assert!(count > 0);
502    }
503
504    #[test]
505    fn test_ring_debug() {
506        let ring = Ring::new(&rectangle()).unwrap();
507        let debug_str = format!("{ring:?}");
508        assert!(debug_str.contains("Ring"));
509        assert!(debug_str.contains("num_points"));
510    }
511
512    #[test]
513    fn test_ring_send_sync() {
514        fn assert_send<T: Send>() {}
515        fn assert_sync<T: Sync>() {}
516        assert_send::<Ring>();
517        assert_sync::<Ring>();
518    }
519
520    #[test]
521    fn test_ring_index_kinds() {
522        let points = octagon();
523
524        let ring_default = Ring::new(&points).unwrap();
525        let ring_none = Ring::new_ix(&points, IndexKind::None).unwrap();
526        let ring_natural = Ring::new_ix(&points, IndexKind::Natural).unwrap();
527
528        assert_eq!(ring_default.num_points(), ring_none.num_points());
529        assert_eq!(ring_default.num_points(), ring_natural.num_points());
530        assert_eq!(ring_default.area(), ring_none.area());
531        assert_eq!(ring_default.area(), ring_natural.area());
532    }
533
534    #[test]
535    fn test_ring_raw_pointer() {
536        let ring = Ring::new(&rectangle()).unwrap();
537        let num_points = ring.num_points();
538        let ptr = ring.into_raw();
539        let recovered = unsafe { Ring::from_raw(ptr).unwrap() };
540        assert_eq!(recovered.num_points(), num_points);
541    }
542
543    fn point_distance_rect(point: Point, rect: Rect) -> f64 {
544        let x = point.x.clamp(rect.min.x, rect.max.x);
545        let y = point.y.clamp(rect.min.y, rect.max.y);
546        let dx = point.x - x;
547        let dy = point.y - y;
548        (dx * dx + dy * dy).sqrt()
549    }
550
551    fn point_distance_segment(point: Point, seg: Segment) -> f64 {
552        let dx = seg.b.x - seg.a.x;
553        let dy = seg.b.y - seg.a.y;
554        let len_sq = dx * dx + dy * dy;
555
556        if len_sq == 0.0 {
557            let dx = point.x - seg.a.x;
558            let dy = point.y - seg.a.y;
559            return (dx * dx + dy * dy).sqrt();
560        }
561
562        let t = ((point.x - seg.a.x) * dx + (point.y - seg.a.y) * dy) / len_sq;
563        let t = t.clamp(0.0, 1.0);
564
565        let closest_x = seg.a.x + t * dx;
566        let closest_y = seg.a.y + t * dy;
567        let dx = point.x - closest_x;
568        let dy = point.y - closest_y;
569        (dx * dx + dy * dy).sqrt()
570    }
571
572    #[test]
573    fn test_ring_nearest_segment_basic() {
574        let ring = Ring::new_ix(&octagon(), IndexKind::Natural).unwrap();
575        let test_point = p(5.0, 5.0);
576        let num_segments = ring.num_segments();
577
578        let mut visited = vec![false; num_segments];
579        let mut last_dist = -1.0f64;
580        let mut count = 0usize;
581
582        let completed = ring.nearest_segment(
583            |rect| {
584                let dist = point_distance_rect(test_point, rect);
585                (dist, false)
586            },
587            |seg| {
588                let dist = point_distance_segment(test_point, seg);
589                (dist, false)
590            },
591            |seg, dist, idx| {
592                assert!(idx < num_segments);
593                assert!(count == 0 || dist >= last_dist - 1e-10);
594                assert!(!visited[idx]);
595                visited[idx] = true;
596
597                let original_seg = ring.segment_at(idx).unwrap();
598                assert_eq!(seg.a, original_seg.a);
599                assert_eq!(seg.b, original_seg.b);
600
601                last_dist = dist;
602                count += 1;
603                true
604            },
605        );
606
607        assert!(completed);
608        assert_eq!(count, num_segments);
609
610        let visited_count = visited.iter().filter(|&&v| v).count();
611        assert_eq!(visited_count, count);
612    }
613
614    #[test]
615    fn test_ring_nearest_segment_early_stop() {
616        let ring = Ring::new_ix(&octagon(), IndexKind::Natural).unwrap();
617        let test_point = p(5.0, 5.0);
618        let stop_at = 3;
619        let mut count = 0;
620
621        let success = ring.nearest_segment(
622            |rect| (point_distance_rect(test_point, rect), false),
623            |seg| (point_distance_segment(test_point, seg), false),
624            |_seg, _dist, _idx| {
625                count += 1;
626                count < stop_at
627            },
628        );
629
630        assert!(success);
631        assert_eq!(count, stop_at);
632        assert!(count < ring.num_segments());
633    }
634
635    #[test]
636    fn test_ring_nearest_segment_no_index() {
637        let ring = Ring::new_ix(&octagon(), IndexKind::None).unwrap();
638        let test_point = p(5.0, 5.0);
639        let mut count = 0;
640
641        let completed = ring.nearest_segment(
642            |rect| (point_distance_rect(test_point, rect), false),
643            |seg| (point_distance_segment(test_point, seg), false),
644            |_seg, _dist, _idx| {
645                count += 1;
646                true
647            },
648        );
649
650        assert!(completed);
651        assert_eq!(count, ring.num_segments());
652    }
653
654    #[test]
655    fn test_ring_nearest_segment_closest_first() {
656        let ring = Ring::new(&rectangle()).unwrap();
657        let test_point = p(5.0, 0.1);
658
659        let mut first_dist: Option<f64> = None;
660
661        ring.nearest_segment(
662            |rect| (point_distance_rect(test_point, rect), false),
663            |seg| (point_distance_segment(test_point, seg), false),
664            |_seg, dist, _idx| {
665                if first_dist.is_none() {
666                    first_dist = Some(dist);
667                }
668                false
669            },
670        );
671
672        let dist = first_dist.expect("Should find at least one segment");
673        assert!(dist < 0.2);
674    }
675}