1#![doc = include_str!("../README.md")]
2
3mod next_after;
4
5use crate::next_after::NextAfter;
6use rtree_rs::{RTree, Rect as RTreeRect};
7
8#[derive(Copy, Clone, Debug)]
9pub struct Point {
10 pub x: f64,
11 pub y: f64,
12}
13
14#[derive(Copy, Clone, Debug)]
15pub struct Rect {
16 pub min: Point,
17 pub max: Point,
18}
19
20impl Rect {
21 pub fn contains_point(&self, p: Point) -> bool {
22 return p.x >= self.min.x && p.x <= self.max.x && p.y >= self.min.y && p.y <= self.max.y;
23 }
24
25 pub fn intersects_rect(&self, other: Rect) -> bool {
26 if self.min.y > other.max.y || self.max.y < other.min.y {
27 return false;
28 }
29 if self.min.x > other.max.x || self.max.x < other.min.x {
30 return false;
31 }
32 return true;
33 }
34
35 pub fn nw(&self) -> Point {
36 Point {
37 x: self.min.x,
38 y: self.max.y,
39 }
40 }
41
42 pub fn sw(&self) -> Point {
43 Point {
44 x: self.min.x,
45 y: self.min.y,
46 }
47 }
48
49 pub fn se(&self) -> Point {
50 Point {
51 x: self.max.x,
52 y: self.min.y,
53 }
54 }
55
56 pub fn ne(&self) -> Point {
57 Point {
58 x: self.max.x,
59 y: self.max.y,
60 }
61 }
62
63 pub fn south(&self) -> Segment {
64 Segment {
65 a: self.sw(),
66 b: self.se(),
67 }
68 }
69
70 pub fn east(&self) -> Segment {
71 Segment {
72 a: self.se(),
73 b: self.ne(),
74 }
75 }
76
77 pub fn north(&self) -> Segment {
78 Segment {
79 a: self.ne(),
80 b: self.nw(),
81 }
82 }
83
84 pub fn west(&self) -> Segment {
85 Segment {
86 a: self.nw(),
87 b: self.sw(),
88 }
89 }
90
91 pub fn segment_at(&self, index: i64) -> Segment {
92 match index {
93 0 => self.south(),
94 1 => self.east(),
95 2 => self.north(),
96 3 => self.west(),
97 _ => self.south(), }
99 }
100}
101
102#[derive(Copy, Clone, Debug)]
103pub struct PolygonBuildOptions {
104 pub enable_rtree: bool,
105 pub enable_compressed_quad: bool,
106 pub enable_y_stripes: bool,
107 pub rtree_min_segments: usize,
108}
109
110impl Default for PolygonBuildOptions {
111 fn default() -> Self {
112 Self {
113 enable_rtree: false,
114 enable_compressed_quad: true,
115 enable_y_stripes: false,
116 rtree_min_segments: 64,
117 }
118 }
119}
120
121#[derive(Clone, Debug, Default, PartialEq, Eq)]
122pub struct YStripesBuildStats {
123 pub segment_count: usize,
124 pub stripe_count: usize,
125 pub assigned_item_count: usize,
126 pub max_bucket_len: usize,
127}
128
129#[derive(Clone, Debug, Default, PartialEq, Eq)]
130pub struct RingBuildStats {
131 pub segment_count: usize,
132 pub below_threshold: bool,
133 pub used_rtree: bool,
134 pub used_compressed_quad: bool,
135 pub used_y_stripes: bool,
136 pub y_stripes: Option<YStripesBuildStats>,
137}
138
139#[derive(Clone, Debug, Default, PartialEq, Eq)]
140pub struct PolygonIndexStats {
141 pub exterior: RingBuildStats,
142 pub holes: Vec<RingBuildStats>,
143}
144
145struct RingIndex {
146 x_min: f64,
147 x_max: f64,
148 seg_count: usize,
149 rtree: Option<RTree<2, f64, usize>>,
150 compressed_quad: Option<CompressedQuadIndex>,
151 y_stripes: Option<YStripesIndex>,
152}
153
154impl RingIndex {
155 fn search_candidates(&self, point_y: f64, out: &mut Vec<usize>) {
156 let query_rect = Rect {
157 min: Point {
158 x: self.x_min,
159 y: point_y,
160 },
161 max: Point {
162 x: self.x_max,
163 y: point_y,
164 },
165 };
166
167 let mut seen = vec![false; self.seg_count];
168
169 if let Some(tree) = self.rtree.as_ref() {
170 let query = RTreeRect::new([self.x_min, point_y], [self.x_max, point_y]);
171 for item in tree.search(query) {
172 let idx = *item.data;
173 if idx < self.seg_count && !seen[idx] {
174 seen[idx] = true;
175 out.push(idx);
176 }
177 }
178 }
179
180 if let Some(index) = self.y_stripes.as_ref() {
181 let mut tmp = Vec::new();
182 index.search(point_y, &mut tmp);
183 for idx in tmp {
184 if idx < self.seg_count && !seen[idx] {
185 seen[idx] = true;
186 out.push(idx);
187 }
188 }
189 }
190
191 if let Some(index) = self.compressed_quad.as_ref() {
192 let mut tmp = Vec::new();
193 index.search_intersects(query_rect, &mut tmp);
194 for idx in tmp {
195 if idx < self.seg_count && !seen[idx] {
196 seen[idx] = true;
197 out.push(idx);
198 }
199 }
200 }
201 }
202}
203
204#[derive(Clone, Copy, Default)]
205struct YStripe {
206 start: usize,
207 count: usize,
208}
209
210struct YStripesIndex {
211 min_y: f64,
212 height: f64,
213 stripes: Vec<YStripe>,
214 indexes: Vec<usize>,
215 y_ranges: Vec<(f64, f64)>,
216}
217
218impl YStripesIndex {
219 fn build(ring: &[Point], seg_rects: &[Rect]) -> Option<(Self, YStripesBuildStats)> {
220 if seg_rects.is_empty() {
221 return None;
222 }
223
224 let mut min_y = seg_rects[0].min.y;
225 let mut max_y = seg_rects[0].max.y;
226 for rect in seg_rects.iter().copied() {
227 min_y = min_y.min(rect.min.y);
228 max_y = max_y.max(rect.max.y);
229 }
230
231 let mut stripe_count = calc_y_stripe_count(ring, seg_rects.len());
232 if stripe_count == 0 {
233 stripe_count = 1;
234 }
235
236 let height = max_y - min_y;
237 let mut counts = vec![0usize; stripe_count];
238 for rect in seg_rects.iter().copied() {
239 let (start, end) = stripe_bounds_for_rect(rect, min_y, height, stripe_count);
240 for stripe in start..=end {
241 counts[stripe] += 1;
242 }
243 }
244
245 let mut stripes = vec![YStripe::default(); stripe_count];
246 let mut offset = 0usize;
247 for (stripe, count) in stripes.iter_mut().zip(&counts) {
248 stripe.start = offset;
249 stripe.count = 0;
250 offset += *count;
251 }
252
253 let mut indexes = vec![0usize; offset];
254 for (idx, rect) in seg_rects.iter().copied().enumerate() {
255 let (start, end) = stripe_bounds_for_rect(rect, min_y, height, stripe_count);
256 for stripe_index in start..=end {
257 let stripe = &mut stripes[stripe_index];
258 indexes[stripe.start + stripe.count] = idx;
259 stripe.count += 1;
260 }
261 }
262
263 let mut stats = YStripesBuildStats {
264 segment_count: seg_rects.len(),
265 stripe_count,
266 assigned_item_count: indexes.len(),
267 max_bucket_len: counts.into_iter().max().unwrap_or(0),
268 };
269 if stats.stripe_count == 0 {
270 stats.stripe_count = 1;
271 }
272
273 let y_ranges = seg_rects
274 .iter()
275 .map(|rect| (rect.min.y, rect.max.y))
276 .collect::<Vec<_>>();
277
278 Some((
279 Self {
280 min_y,
281 height,
282 stripes,
283 indexes,
284 y_ranges,
285 },
286 stats,
287 ))
288 }
289
290 fn search(&self, y: f64, out: &mut Vec<usize>) {
291 if self.height == 0.0 {
292 if y != self.min_y {
293 return;
294 }
295 } else if y < self.min_y || y > self.min_y + self.height {
296 return;
297 }
298
299 let stripe_index = if self.height == 0.0 {
300 0
301 } else {
302 let raw = ((y - self.min_y) / self.height * self.stripes.len() as f64).floor() as isize;
303 raw.clamp(0, self.stripes.len() as isize - 1) as usize
304 };
305 let stripe = self.stripes[stripe_index];
306 let end = stripe.start + stripe.count;
307 for idx in &self.indexes[stripe.start..end] {
308 let (seg_min_y, seg_max_y) = self.y_ranges[*idx];
309 if y >= seg_min_y && y <= seg_max_y {
310 out.push(*idx);
311 }
312 }
313 }
314}
315
316fn stripe_bounds_for_rect(
317 rect: Rect,
318 min_y: f64,
319 height: f64,
320 stripe_count: usize,
321) -> (usize, usize) {
322 if stripe_count <= 1 || height == 0.0 {
323 return (0, 0);
324 }
325
326 let last = stripe_count - 1;
327 let start = (((rect.min.y - min_y) / height) * stripe_count as f64).floor() as isize;
328 let end = (((rect.max.y - min_y) / height) * stripe_count as f64).floor() as isize;
329 (
330 start.clamp(0, last as isize) as usize,
331 end.clamp(0, last as isize) as usize,
332 )
333}
334
335fn calc_ring_area_and_perimeter(ring: &[Point]) -> (f64, f64) {
336 let seg_count = ring_segment_count(ring);
337 if seg_count == 0 {
338 return (0.0, 0.0);
339 }
340
341 let mut signed_area = 0.0;
342 let mut perimeter = 0.0;
343 for i in 0..seg_count {
344 let a = ring[i];
345 let b = ring[i + 1];
346 signed_area += a.x * b.y - b.x * a.y;
347 perimeter += ((b.x - a.x).powi(2) + (b.y - a.y).powi(2)).sqrt();
348 }
349 (signed_area.abs() * 0.5, perimeter)
350}
351
352fn calc_y_stripe_count(ring: &[Point], seg_count: usize) -> usize {
353 let (area, perimeter) = calc_ring_area_and_perimeter(ring);
354 let mut score = 0.0;
355 if perimeter > 0.0 {
356 score = (area * std::f64::consts::PI * 4.0) / (perimeter * perimeter);
357 }
358 ((seg_count as f64 * score).floor() as usize).max(32)
359}
360
361const Q_MAX_ITEMS: usize = 12;
362const Q_MAX_DEPTH: usize = 64;
363
364#[derive(Default)]
365struct QuadNode {
366 split: bool,
367 items: Vec<usize>,
368 quads: [Option<Box<QuadNode>>; 4],
369}
370
371impl QuadNode {
372 fn new() -> Self {
373 Self {
374 split: false,
375 items: Vec::new(),
376 quads: [None, None, None, None],
377 }
378 }
379}
380
381struct CompressedQuadIndex {
382 bounds: Rect,
383 seg_rects: Vec<Rect>,
384 data: Vec<u8>,
385}
386
387impl CompressedQuadIndex {
388 fn build(ring: &[Point]) -> Option<Self> {
389 let seg_count = ring_segment_count(ring);
390 if seg_count == 0 {
391 return None;
392 }
393
394 let mut min_x = ring[0].x;
395 let mut min_y = ring[0].y;
396 let mut max_x = ring[0].x;
397 let mut max_y = ring[0].y;
398
399 for p in ring.iter().take(seg_count) {
400 if p.x < min_x {
401 min_x = p.x;
402 }
403 if p.y < min_y {
404 min_y = p.y;
405 }
406 if p.x > max_x {
407 max_x = p.x;
408 }
409 if p.y > max_y {
410 max_y = p.y;
411 }
412 }
413
414 let bounds = Rect {
415 min: Point { x: min_x, y: min_y },
416 max: Point { x: max_x, y: max_y },
417 };
418
419 let mut seg_rects = Vec::with_capacity(seg_count);
420 for i in 0..seg_count {
421 seg_rects.push(segment_at_for_slice(ring, i).rect());
422 }
423
424 let mut root = QuadNode::new();
425 for i in 0..seg_rects.len() {
426 insert_quad_node(&mut root, bounds, &seg_rects, i, 0);
427 }
428
429 let mut data = Vec::with_capacity(seg_rects.len() * 2);
430 compress_quad_node(&root, &mut data);
431
432 Some(Self {
433 bounds,
434 seg_rects,
435 data,
436 })
437 }
438
439 fn search_intersects(&self, query: Rect, out: &mut Vec<usize>) {
440 if !self.bounds.intersects_rect(query) {
441 return;
442 }
443 let _ = self.search_intersects_from(0, self.bounds, query, out);
444 }
445
446 fn search_intersects_from(
447 &self,
448 mut addr: usize,
449 bounds: Rect,
450 query: Rect,
451 out: &mut Vec<usize>,
452 ) -> Option<usize> {
453 let (nitems, next_addr) = read_uvarint(&self.data, addr)?;
454 addr = next_addr;
455
456 let mut last: usize = 0;
457 for _ in 0..nitems {
458 let (delta, next_addr) = read_uvarint(&self.data, addr)?;
459 addr = next_addr;
460 last = last.checked_add(delta as usize)?;
461 let seg_rect = self.seg_rects.get(last)?;
462 if seg_rect.intersects_rect(query) {
463 out.push(last);
464 }
465 }
466
467 let split = *self.data.get(addr)?;
468 addr += 1;
469 if split == 0 {
470 return Some(addr);
471 }
472 if split != 1 {
473 return None;
474 }
475
476 for q in 0..4 {
477 let (qsize, next_addr) = read_uvarint(&self.data, addr)?;
478 addr = next_addr;
479 if qsize == 0 {
480 continue;
481 }
482
483 let qsize = usize::try_from(qsize).ok()?;
484 let qbounds = quad_bounds(bounds, q);
485 let child_start = addr;
486 let child_end = child_start.checked_add(qsize)?;
487 if child_end > self.data.len() {
488 return None;
489 }
490
491 if qbounds.intersects_rect(query) {
492 let _ = self.search_intersects_from(child_start, qbounds, query, out)?;
493 }
494 addr = child_end;
495 }
496
497 Some(addr)
498 }
499}
500
501fn ring_segment_count(ring: &[Point]) -> usize {
502 ring.len().saturating_sub(1)
503}
504
505fn segment_at_for_slice(ring: &[Point], index: usize) -> Segment {
506 Segment {
507 a: ring[index],
508 b: ring[index + 1],
509 }
510}
511
512fn build_ring_index(
513 ring: &[Point],
514 options: &PolygonBuildOptions,
515) -> (Option<RingIndex>, RingBuildStats) {
516 let seg_count = ring_segment_count(ring);
517 let index_requested =
518 options.enable_rtree || options.enable_compressed_quad || options.enable_y_stripes;
519 let mut stats = RingBuildStats {
520 segment_count: seg_count,
521 below_threshold: index_requested && seg_count < options.rtree_min_segments,
522 ..RingBuildStats::default()
523 };
524
525 if !index_requested || ring.is_empty() || seg_count < options.rtree_min_segments {
526 return (None, stats);
527 }
528
529 let mut x_min = ring[0].x;
530 let mut x_max = ring[0].x;
531 for p in ring.iter().take(seg_count) {
532 if p.x < x_min {
533 x_min = p.x;
534 }
535 if p.x > x_max {
536 x_max = p.x;
537 }
538 }
539
540 let rtree = if options.enable_rtree {
541 stats.used_rtree = true;
542 let mut tree: RTree<2, f64, usize> = RTree::new();
543 for i in 0..seg_count {
544 let seg_rect = segment_at_for_slice(ring, i).rect();
545 tree.insert(
546 RTreeRect::new(
547 [seg_rect.min.x, seg_rect.min.y],
548 [seg_rect.max.x, seg_rect.max.y],
549 ),
550 i,
551 );
552 }
553 Some(tree)
554 } else {
555 None
556 };
557
558 let compressed_quad = if options.enable_compressed_quad {
559 match CompressedQuadIndex::build(ring) {
560 Some(index) => {
561 stats.used_compressed_quad = true;
562 Some(index)
563 }
564 None => None,
565 }
566 } else {
567 None
568 };
569
570 let (y_stripes, y_stripes_stats) = if options.enable_y_stripes {
571 let mut seg_rects = Vec::with_capacity(seg_count);
572 for i in 0..seg_count {
573 seg_rects.push(segment_at_for_slice(ring, i).rect());
574 }
575 match YStripesIndex::build(ring, &seg_rects) {
576 Some((index, stripe_stats)) => {
577 stats.used_y_stripes = true;
578 (Some(index), Some(stripe_stats))
579 }
580 None => (None, None),
581 }
582 } else {
583 (None, None)
584 };
585 stats.y_stripes = y_stripes_stats;
586
587 if rtree.is_none() && compressed_quad.is_none() && y_stripes.is_none() {
588 return (None, stats);
589 }
590
591 (
592 Some(RingIndex {
593 x_min,
594 x_max,
595 seg_count,
596 rtree,
597 compressed_quad,
598 y_stripes,
599 }),
600 stats,
601 )
602}
603
604fn rings_contains_point(
605 ring: &[Point],
606 ring_index: Option<&RingIndex>,
607 point: Point,
608 allow_on_edge: bool,
609) -> bool {
610 let mut inside: bool = false;
611
612 if let Some(index) = ring_index {
613 let mut candidates = Vec::new();
614 index.search_candidates(point.y, &mut candidates);
615 for i in candidates {
616 let seg = segment_at_for_slice(ring, i);
617 let res: RaycastResult = raycast(&seg, point);
618 if res.on {
619 inside = allow_on_edge;
620 break;
621 }
622 if res.inside {
623 inside = !inside;
624 }
625 }
626 return inside;
627 }
628
629 let ray_rect = Rect {
630 min: Point {
631 x: std::f64::NEG_INFINITY,
632 y: point.y,
633 },
634 max: Point {
635 x: std::f64::INFINITY,
636 y: point.y,
637 },
638 };
639
640 for pair in ring.windows(2) {
641 let seg = Segment {
642 a: pair[0],
643 b: pair[1],
644 };
645
646 if seg.rect().intersects_rect(ray_rect) {
647 let res: RaycastResult = raycast(&seg, point);
648 if res.on {
649 inside = allow_on_edge;
650 break;
651 }
652 if res.inside {
653 inside = !inside;
654 }
655 }
656 }
657
658 return inside;
659}
660
661fn choose_quad(bounds: Rect, rect: Rect) -> Option<usize> {
662 let mid_x = (bounds.min.x + bounds.max.x) / 2.0;
663 let mid_y = (bounds.min.y + bounds.max.y) / 2.0;
664
665 if rect.max.x < mid_x {
666 if rect.max.y < mid_y {
667 return Some(2);
668 }
669 if rect.min.y < mid_y {
670 return None;
671 }
672 return Some(0);
673 }
674
675 if rect.min.x < mid_x {
676 return None;
677 }
678
679 if rect.max.y < mid_y {
680 return Some(3);
681 }
682 if rect.min.y < mid_y {
683 return None;
684 }
685 Some(1)
686}
687
688fn quad_bounds(mut bounds: Rect, q: usize) -> Rect {
689 let center_x = (bounds.min.x + bounds.max.x) / 2.0;
690 let center_y = (bounds.min.y + bounds.max.y) / 2.0;
691
692 match q {
693 0 => {
694 bounds.min.y = center_y;
695 bounds.max.x = center_x;
696 }
697 1 => {
698 bounds.min.x = center_x;
699 bounds.min.y = center_y;
700 }
701 2 => {
702 bounds.max.x = center_x;
703 bounds.max.y = center_y;
704 }
705 3 => {
706 bounds.min.x = center_x;
707 bounds.max.y = center_y;
708 }
709 _ => {}
710 }
711 bounds
712}
713
714fn insert_quad_node(
715 node: &mut QuadNode,
716 bounds: Rect,
717 seg_rects: &[Rect],
718 item: usize,
719 depth: usize,
720) {
721 if depth == Q_MAX_DEPTH {
722 node.items.push(item);
723 return;
724 }
725
726 let item_rect = seg_rects[item];
727 if node.split {
728 if let Some(q) = choose_quad(bounds, item_rect) {
729 let qbounds = quad_bounds(bounds, q);
730 if node.quads[q].is_none() {
731 node.quads[q] = Some(Box::new(QuadNode::new()));
732 }
733 if let Some(quad) = node.quads[q].as_deref_mut() {
734 insert_quad_node(quad, qbounds, seg_rects, item, depth + 1);
735 }
736 } else {
737 node.items.push(item);
738 }
739 return;
740 }
741
742 if node.items.len() == Q_MAX_ITEMS {
743 let existing = std::mem::take(&mut node.items);
744 node.split = true;
745 for i in existing {
746 let rect = seg_rects[i];
747 if let Some(q) = choose_quad(bounds, rect) {
748 let qbounds = quad_bounds(bounds, q);
749 if node.quads[q].is_none() {
750 node.quads[q] = Some(Box::new(QuadNode::new()));
751 }
752 if let Some(quad) = node.quads[q].as_deref_mut() {
753 insert_quad_node(quad, qbounds, seg_rects, i, depth + 1);
754 }
755 } else {
756 node.items.push(i);
757 }
758 }
759 insert_quad_node(node, bounds, seg_rects, item, depth);
760 return;
761 }
762
763 node.items.push(item);
764}
765
766fn append_uvarint(dst: &mut Vec<u8>, mut x: u64) {
767 while x >= 0x80 {
768 dst.push((x as u8 & 0x7f) | 0x80);
769 x >>= 7;
770 }
771 dst.push(x as u8);
772}
773
774fn read_uvarint(data: &[u8], mut addr: usize) -> Option<(u64, usize)> {
775 let mut x: u64 = 0;
776 let mut shift = 0;
777
778 loop {
779 let b = *data.get(addr)?;
780 addr += 1;
781
782 if shift == 70 {
783 return None;
784 }
785
786 x |= ((b & 0x7f) as u64) << shift;
787 if b < 0x80 {
788 return Some((x, addr));
789 }
790 shift += 7;
791 }
792}
793
794fn compress_quad_node(node: &QuadNode, dst: &mut Vec<u8>) {
795 let mut items = node.items.clone();
796 items.sort_unstable();
797
798 append_uvarint(dst, items.len() as u64);
799 let mut last = 0usize;
800 for item in items {
801 append_uvarint(dst, (item - last) as u64);
802 last = item;
803 }
804
805 if !node.split {
806 dst.push(0);
807 return;
808 }
809
810 dst.push(1);
811 for q in 0..4 {
812 if let Some(child) = node.quads[q].as_deref() {
813 let mut child_bytes = Vec::new();
814 compress_quad_node(child, &mut child_bytes);
815 append_uvarint(dst, child_bytes.len() as u64);
816 dst.extend_from_slice(&child_bytes);
817 } else {
818 append_uvarint(dst, 0);
819 }
820 }
821}
822
823pub struct Polygon {
824 exterior: Vec<Point>,
825 holes: Vec<Vec<Point>>,
826 rect: Rect,
827 options: PolygonBuildOptions,
828 exterior_index: Option<RingIndex>,
829 hole_indexes: Vec<Option<RingIndex>>,
830 index_stats: PolygonIndexStats,
831}
832
833impl Polygon {
834 fn compute_rect(exterior: &[Point]) -> Rect {
835 let mut minx: f64 = exterior[0].x;
836 let mut miny: f64 = exterior[0].y;
837 let mut maxx: f64 = exterior[0].x;
838 let mut maxy: f64 = exterior[0].y;
839
840 for p in exterior.iter() {
841 if p.x < minx {
842 minx = p.x;
843 }
844 if p.y < miny {
845 miny = p.y;
846 }
847 if p.x > maxx {
848 maxx = p.x;
849 }
850 if p.y > maxy {
851 maxy = p.y;
852 }
853 }
854
855 Rect {
856 min: Point { x: minx, y: miny },
857 max: Point { x: maxx, y: maxy },
858 }
859 }
860
861 fn rebuild_cache(&mut self) {
862 self.rect = Self::compute_rect(&self.exterior);
863 let (exterior_index, exterior_stats) = build_ring_index(&self.exterior, &self.options);
864 self.exterior_index = exterior_index;
865 self.index_stats.exterior = exterior_stats;
866
867 let hole_indexes_and_stats: Vec<(Option<RingIndex>, RingBuildStats)> = self
868 .holes
869 .iter()
870 .map(|hole| build_ring_index(hole, &self.options))
871 .collect();
872 let (hole_indexes, hole_stats): (Vec<Option<RingIndex>>, Vec<RingBuildStats>) =
873 hole_indexes_and_stats.into_iter().unzip();
874 self.hole_indexes = hole_indexes;
875 self.index_stats.holes = hole_stats;
876 }
877
878 fn contains_point_normal(&self, p: Point) -> bool {
883 if !rings_contains_point(&self.exterior, self.exterior_index.as_ref(), p, false) {
884 return false;
885 }
886
887 for (hole, hole_index) in self.holes.iter().zip(self.hole_indexes.iter()) {
888 if rings_contains_point(hole, hole_index.as_ref(), p, false) {
889 return false;
890 }
891 }
892
893 return true;
894 }
895
896 pub fn contains_point(&self, p: Point) -> bool {
898 if !self.rect.contains_point(p) {
899 return false;
900 }
901
902 return self.contains_point_normal(p);
903 }
904
905 pub fn new(
953 exterior: Vec<Point>,
954 holes: Vec<Vec<Point>>,
955 options: Option<PolygonBuildOptions>,
956 ) -> Polygon {
957 let mut poly = Polygon {
958 exterior,
959 holes,
960 rect: Rect {
961 min: Point { x: 0.0, y: 0.0 },
962 max: Point { x: 0.0, y: 0.0 },
963 },
964 options: options.unwrap_or_default(),
965 exterior_index: None,
966 hole_indexes: Vec::new(),
967 index_stats: PolygonIndexStats::default(),
968 };
969 poly.rebuild_cache();
970 poly
971 }
972
973 pub fn exterior(&self) -> &[Point] {
974 &self.exterior
975 }
976
977 pub fn holes(&self) -> &[Vec<Point>] {
978 &self.holes
979 }
980
981 pub fn rect(&self) -> Rect {
982 self.rect
983 }
984
985 pub fn options(&self) -> PolygonBuildOptions {
986 self.options
987 }
988
989 pub fn index_stats(&self) -> &PolygonIndexStats {
990 &self.index_stats
991 }
992
993 pub fn set_exterior(&mut self, exterior: Vec<Point>) {
994 self.exterior = exterior;
995 self.rebuild_cache();
996 }
997
998 pub fn set_holes(&mut self, holes: Vec<Vec<Point>>) {
999 self.holes = holes;
1000 self.rebuild_cache();
1001 }
1002
1003 pub fn set_options(&mut self, options: PolygonBuildOptions) {
1004 self.options = options;
1005 self.rebuild_cache();
1006 }
1007}
1008
1009#[derive(Copy, Clone, Debug)]
1010pub struct Segment {
1011 pub a: Point,
1012 pub b: Point,
1013}
1014
1015impl Segment {
1016 pub fn rect(&self) -> Rect {
1017 let mut min_x: f64 = self.a.x;
1018 let mut min_y: f64 = self.a.y;
1019 let mut max_x: f64 = self.b.x;
1020 let mut max_y: f64 = self.b.y;
1021
1022 if min_x > max_x {
1023 let actual_min_x = max_x;
1024 let actual_max_x = min_x;
1025 min_x = actual_min_x;
1026 max_x = actual_max_x;
1027 }
1028
1029 if min_y > max_y {
1030 let actual_min_y = max_y;
1031 let actual_max_y = min_y;
1032 min_y = actual_min_y;
1033 max_y = actual_max_y;
1034 }
1035
1036 return Rect {
1037 min: Point { x: min_x, y: min_y },
1038 max: Point { x: max_x, y: max_y },
1039 };
1040 }
1041}
1042
1043pub struct RaycastResult {
1044 inside: bool, on: bool, }
1047
1048pub fn raycast(seg: &Segment, point: Point) -> RaycastResult {
1049 let mut p = point;
1050 let a = seg.a;
1051 let b = seg.b;
1052
1053 if a.y < b.y && (p.y < a.y || p.y > b.y) {
1055 return RaycastResult {
1056 inside: false,
1057 on: false,
1058 };
1059 } else if a.y > b.y && (p.y < b.y || p.y > a.y) {
1060 return RaycastResult {
1061 inside: false,
1062 on: false,
1063 };
1064 }
1065
1066 if a.y == b.y {
1068 if a.x == b.x {
1069 if p.x == a.x && p.y == a.y {
1070 return RaycastResult {
1071 inside: false,
1072 on: true,
1073 };
1074 }
1075 return RaycastResult {
1076 inside: false,
1077 on: false,
1078 };
1079 }
1080 if p.y == b.y {
1081 if a.x < b.x {
1084 if p.x >= a.x && p.x <= b.x {
1085 return RaycastResult {
1086 inside: false,
1087 on: true,
1088 };
1089 }
1090 } else if p.x >= b.x && p.x <= a.x {
1091 return RaycastResult {
1092 inside: false,
1093 on: true,
1094 };
1095 }
1096 }
1097 }
1098 if a.x == b.x && p.x == b.x {
1099 if a.y < b.y {
1102 if p.y >= a.y && p.y <= b.y {
1103 return RaycastResult {
1104 inside: false,
1105 on: true,
1106 };
1107 }
1108 } else if p.y >= b.y && p.y <= a.y {
1109 return RaycastResult {
1110 inside: false,
1111 on: true,
1112 };
1113 }
1114 }
1115 if (p.x - a.x) / (b.x - a.x) == (p.y - a.y) / (b.y - a.y) {
1116 return RaycastResult {
1117 inside: false,
1118 on: true,
1119 };
1120 }
1121
1122 while p.y == a.y || p.y == b.y {
1124 p.y = p.y.next_after(std::f64::INFINITY);
1125 }
1126
1127 if a.y < b.y {
1128 if p.y < a.y || p.y > b.y {
1129 return RaycastResult {
1130 inside: false,
1131 on: false,
1132 };
1133 }
1134 } else if p.y < b.y || p.y > a.y {
1135 return RaycastResult {
1136 inside: false,
1137 on: false,
1138 };
1139 }
1140 if a.x > b.x {
1141 if p.x >= a.x {
1142 return RaycastResult {
1143 inside: false,
1144 on: false,
1145 };
1146 }
1147 if p.x <= b.x {
1148 return RaycastResult {
1149 inside: true,
1150 on: false,
1151 };
1152 }
1153 } else {
1154 if p.x >= b.x {
1155 return RaycastResult {
1156 inside: false,
1157 on: false,
1158 };
1159 }
1160 if p.x <= a.x {
1161 return RaycastResult {
1162 inside: true,
1163 on: false,
1164 };
1165 }
1166 }
1167 if a.y < b.y {
1168 if (p.y - a.y) / (p.x - a.x) >= (b.y - a.y) / (b.x - a.x) {
1169 return RaycastResult {
1170 inside: true,
1171 on: false,
1172 };
1173 }
1174 } else if (p.y - b.y) / (p.x - b.x) >= (a.y - b.y) / (a.x - b.x) {
1175 return RaycastResult {
1176 inside: true,
1177 on: false,
1178 };
1179 }
1180 return RaycastResult {
1181 inside: false,
1182 on: false,
1183 };
1184}
1185
1186#[cfg(test)]
1187mod tests {
1188 use super::*;
1189
1190 fn square(min: f64, max: f64) -> Vec<Point> {
1191 vec![
1192 Point { x: min, y: min },
1193 Point { x: min, y: max },
1194 Point { x: max, y: max },
1195 Point { x: max, y: min },
1196 Point { x: min, y: min },
1197 ]
1198 }
1199
1200 fn polygon_with_segments(segments: usize) -> Vec<Point> {
1201 let mut ring = Vec::with_capacity(segments + 1);
1202 for i in 0..segments {
1203 let theta = (i as f64) / (segments as f64) * std::f64::consts::TAU;
1204 ring.push(Point {
1205 x: theta.cos(),
1206 y: theta.sin(),
1207 });
1208 }
1209 ring.push(ring[0]);
1210 ring
1211 }
1212
1213 fn scan_candidates(ring: &[Point], y: f64, x_min: f64, x_max: f64) -> Vec<usize> {
1214 let mut out = Vec::new();
1215 let query = Rect {
1216 min: Point { x: x_min, y },
1217 max: Point { x: x_max, y },
1218 };
1219 for (i, pair) in ring.windows(2).enumerate() {
1220 let seg = Segment {
1221 a: pair[0],
1222 b: pair[1],
1223 };
1224 if seg.rect().intersects_rect(query) {
1225 out.push(i);
1226 }
1227 }
1228 out
1229 }
1230
1231 #[test]
1232 fn rings_contains_point_allow_on_edge() {
1233 let ring = square(0.0, 10.0);
1234 let on_edge = Point { x: 0.0, y: 5.0 };
1235 assert!(rings_contains_point(&ring, None, on_edge, true));
1236 assert!(!rings_contains_point(&ring, None, on_edge, false));
1237 }
1238
1239 #[test]
1240 fn polygon_contains_basic_in_and_out() {
1241 let poly = Polygon::new(square(0.0, 10.0), vec![], None);
1242 assert!(poly.contains_point(Point { x: 5.0, y: 5.0 }));
1243 assert!(!poly.contains_point(Point { x: 20.0, y: 5.0 }));
1244 }
1245
1246 #[test]
1247 fn polygon_contains_with_hole() {
1248 let poly = Polygon::new(square(0.0, 10.0), vec![square(3.0, 7.0)], None);
1249 assert!(poly.contains_point(Point { x: 1.0, y: 1.0 }));
1250 assert!(!poly.contains_point(Point { x: 5.0, y: 5.0 }));
1251 }
1252
1253 #[test]
1254 fn indexed_and_non_indexed_results_match() {
1255 let ring = polygon_with_segments(128);
1256 let p_in = Point { x: 0.2, y: 0.1 };
1257 let p_out = Point { x: 2.0, y: 0.0 };
1258
1259 let p1 = Polygon::new(
1260 ring.clone(),
1261 vec![],
1262 Some(PolygonBuildOptions {
1263 enable_rtree: false,
1264 enable_compressed_quad: false,
1265 enable_y_stripes: false,
1266 rtree_min_segments: 64,
1267 }),
1268 );
1269
1270 let p2 = Polygon::new(
1271 ring,
1272 vec![],
1273 Some(PolygonBuildOptions {
1274 enable_rtree: true,
1275 enable_compressed_quad: true,
1276 enable_y_stripes: false,
1277 rtree_min_segments: 64,
1278 }),
1279 );
1280
1281 assert_eq!(p1.contains_point(p_in), p2.contains_point(p_in));
1282 assert_eq!(p1.contains_point(p_out), p2.contains_point(p_out));
1283 }
1284
1285 #[test]
1286 fn rtree_and_compressed_quad_and_both_match_baseline() {
1287 let ring = polygon_with_segments(160);
1288 let points = [
1289 Point { x: 0.2, y: 0.1 },
1290 Point { x: -0.4, y: -0.3 },
1291 Point { x: 1.2, y: 0.0 },
1292 ];
1293
1294 let base = Polygon::new(
1295 ring.clone(),
1296 vec![],
1297 Some(PolygonBuildOptions {
1298 enable_rtree: false,
1299 enable_compressed_quad: false,
1300 enable_y_stripes: false,
1301 rtree_min_segments: 64,
1302 }),
1303 );
1304 let only_rtree = Polygon::new(
1305 ring.clone(),
1306 vec![],
1307 Some(PolygonBuildOptions {
1308 enable_rtree: true,
1309 enable_compressed_quad: false,
1310 enable_y_stripes: false,
1311 rtree_min_segments: 64,
1312 }),
1313 );
1314 let only_compressed = Polygon::new(
1315 ring.clone(),
1316 vec![],
1317 Some(PolygonBuildOptions {
1318 enable_rtree: false,
1319 enable_compressed_quad: true,
1320 enable_y_stripes: false,
1321 rtree_min_segments: 64,
1322 }),
1323 );
1324 let both = Polygon::new(
1325 ring,
1326 vec![],
1327 Some(PolygonBuildOptions {
1328 enable_rtree: true,
1329 enable_compressed_quad: true,
1330 enable_y_stripes: false,
1331 rtree_min_segments: 64,
1332 }),
1333 );
1334
1335 for p in points {
1336 let expected = base.contains_point(p);
1337 assert_eq!(only_rtree.contains_point(p), expected);
1338 assert_eq!(only_compressed.contains_point(p), expected);
1339 assert_eq!(both.contains_point(p), expected);
1340 }
1341 }
1342
1343 #[test]
1344 fn threshold_boundaries_63_64_65_are_consistent() {
1345 let ring = polygon_with_segments(65);
1346 let p_in = Point { x: 0.2, y: 0.0 };
1347 let p_out = Point { x: 1.5, y: 0.0 };
1348
1349 let p63 = Polygon::new(
1350 ring.clone(),
1351 vec![],
1352 Some(PolygonBuildOptions {
1353 enable_rtree: true,
1354 enable_compressed_quad: true,
1355 enable_y_stripes: false,
1356 rtree_min_segments: 63,
1357 }),
1358 );
1359 let p64 = Polygon::new(
1360 ring.clone(),
1361 vec![],
1362 Some(PolygonBuildOptions {
1363 enable_rtree: true,
1364 enable_compressed_quad: true,
1365 enable_y_stripes: false,
1366 rtree_min_segments: 64,
1367 }),
1368 );
1369 let p65 = Polygon::new(
1370 ring,
1371 vec![],
1372 Some(PolygonBuildOptions {
1373 enable_rtree: true,
1374 enable_compressed_quad: true,
1375 enable_y_stripes: false,
1376 rtree_min_segments: 65,
1377 }),
1378 );
1379
1380 assert_eq!(p63.contains_point(p_in), p64.contains_point(p_in));
1381 assert_eq!(p64.contains_point(p_in), p65.contains_point(p_in));
1382
1383 assert_eq!(p63.contains_point(p_out), p64.contains_point(p_out));
1384 assert_eq!(p64.contains_point(p_out), p65.contains_point(p_out));
1385 }
1386
1387 #[test]
1388 fn setters_rebuild_cache_and_keep_correct_results() {
1389 let mut poly = Polygon::new(square(0.0, 10.0), vec![], None);
1390 assert!(poly.contains_point(Point { x: 1.0, y: 1.0 }));
1391
1392 poly.set_exterior(square(20.0, 30.0));
1393 assert!(!poly.contains_point(Point { x: 1.0, y: 1.0 }));
1394 assert!(poly.contains_point(Point { x: 21.0, y: 21.0 }));
1395
1396 poly.set_holes(vec![square(22.0, 24.0)]);
1397 assert!(!poly.contains_point(Point { x: 23.0, y: 23.0 }));
1398
1399 poly.set_options(PolygonBuildOptions {
1400 enable_rtree: false,
1401 enable_compressed_quad: false,
1402 enable_y_stripes: false,
1403 rtree_min_segments: 64,
1404 });
1405 assert!(!poly.contains_point(Point { x: 23.0, y: 23.0 }));
1406 assert!(poly.contains_point(Point { x: 25.0, y: 25.0 }));
1407 }
1408
1409 #[test]
1410 fn compressed_quad_candidates_match_scan() {
1411 let ring = polygon_with_segments(256);
1412 let options = PolygonBuildOptions {
1413 enable_rtree: false,
1414 enable_compressed_quad: true,
1415 enable_y_stripes: false,
1416 rtree_min_segments: 64,
1417 };
1418 let (index, stats) = build_ring_index(&ring, &options);
1419 let index = index.unwrap();
1420
1421 assert!(stats.used_compressed_quad);
1422 assert!(!stats.below_threshold);
1423
1424 for y in [-1.2, -1.0, -0.75, -0.2, 0.0, 0.4, 0.99, 1.0, 1.2] {
1425 let mut from_index = Vec::new();
1426 index.search_candidates(y, &mut from_index);
1427 from_index.sort_unstable();
1428
1429 let mut from_scan = scan_candidates(&ring, y, index.x_min, index.x_max);
1430 from_scan.sort_unstable();
1431
1432 assert_eq!(from_index, from_scan);
1433 }
1434 }
1435
1436 #[test]
1437 fn y_stripes_candidates_match_scan() {
1438 let ring = polygon_with_segments(256);
1439 let options = PolygonBuildOptions {
1440 enable_rtree: false,
1441 enable_compressed_quad: false,
1442 enable_y_stripes: true,
1443 rtree_min_segments: 64,
1444 };
1445 let (index, stats) = build_ring_index(&ring, &options);
1446 let index = index.unwrap();
1447
1448 assert!(stats.used_y_stripes);
1449 assert!(!stats.below_threshold);
1450 let y_stats = stats.y_stripes.as_ref().unwrap();
1451 assert_eq!(y_stats.segment_count, 256);
1452 assert!(y_stats.stripe_count >= 32);
1453 assert!(y_stats.assigned_item_count >= 256);
1454 assert!(y_stats.max_bucket_len > 0);
1455
1456 for y in [-1.2, -1.0, -0.75, -0.2, 0.0, 0.4, 0.99, 1.0, 1.2] {
1457 let mut from_index = Vec::new();
1458 index.search_candidates(y, &mut from_index);
1459 from_index.sort_unstable();
1460
1461 let mut from_scan = scan_candidates(&ring, y, index.x_min, index.x_max);
1462 from_scan.sort_unstable();
1463
1464 assert_eq!(from_index, from_scan);
1465 }
1466 }
1467
1468 #[test]
1469 fn polygon_index_stats_capture_threshold_for_compressed_quad() {
1470 let indexed = Polygon::new(
1471 polygon_with_segments(256),
1472 vec![polygon_with_segments(32)],
1473 Some(PolygonBuildOptions {
1474 enable_rtree: false,
1475 enable_compressed_quad: true,
1476 enable_y_stripes: false,
1477 rtree_min_segments: 64,
1478 }),
1479 );
1480 let stats = indexed.index_stats();
1481 assert!(stats.exterior.used_compressed_quad);
1482 assert!(!stats.exterior.below_threshold);
1483 assert_eq!(stats.exterior.segment_count, 256);
1484 assert_eq!(stats.holes.len(), 1);
1485 assert!(stats.holes[0].below_threshold);
1486 assert!(!stats.holes[0].used_compressed_quad);
1487 }
1488
1489 #[test]
1490 fn polygon_index_stats_capture_y_stripes_metrics() {
1491 let indexed = Polygon::new(
1492 polygon_with_segments(256),
1493 vec![polygon_with_segments(32)],
1494 Some(PolygonBuildOptions {
1495 enable_rtree: false,
1496 enable_compressed_quad: false,
1497 enable_y_stripes: true,
1498 rtree_min_segments: 64,
1499 }),
1500 );
1501 let stats = indexed.index_stats();
1502 assert!(stats.exterior.used_y_stripes);
1503 assert!(!stats.exterior.below_threshold);
1504 assert_eq!(stats.exterior.segment_count, 256);
1505 assert!(stats.exterior.y_stripes.is_some());
1506 assert_eq!(stats.holes.len(), 1);
1507 assert!(stats.holes[0].below_threshold);
1508 assert!(!stats.holes[0].used_y_stripes);
1509 assert!(stats.holes[0].y_stripes.is_none());
1510 }
1511}