1use 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
89pub 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 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 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 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#[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 #[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}