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, PartialEq)]
9pub struct Point<T = f64> {
10 pub x: T,
11 pub y: T,
12}
13
14pub type I32Point = Point<i32>;
16
17pub trait CoordStorage: Copy + PartialOrd {
27 fn to_f64(self) -> f64;
30 fn to_i64(self) -> i64;
33}
34
35impl CoordStorage for f64 {
36 #[inline(always)]
37 fn to_f64(self) -> f64 {
38 self
39 }
40
41 #[inline(always)]
42 fn to_i64(self) -> i64 {
43 self as i64
44 }
45}
46
47impl CoordStorage for i32 {
48 #[inline(always)]
49 fn to_f64(self) -> f64 {
50 f64::from(self)
51 }
52
53 #[inline(always)]
54 fn to_i64(self) -> i64 {
55 i64::from(self)
56 }
57}
58
59pub trait ContainsPoint<Q> {
60 fn contains_point(&self, point: Q) -> bool;
61}
62
63#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
64pub enum I32RaycastMode {
65 #[default]
66 Float,
67 Integer,
68}
69
70pub type I32Polygon = Polygon<i32>;
75
76#[inline]
77fn ring_contains_point_integer<T: CoordStorage>(
78 ring: &[Point<T>],
79 point: Point<i64>,
80 allow_on_edge: bool,
81) -> bool {
82 let mut inside = false;
83 for pair in ring.windows(2) {
84 let ax = pair[0].x.to_i64();
85 let ay = pair[0].y.to_i64();
86 let bx = pair[1].x.to_i64();
87 let by = pair[1].y.to_i64();
88 let cross = (bx - ax) * (point.y - ay) - (by - ay) * (point.x - ax);
89 if cross == 0
90 && point.x >= ax.min(bx)
91 && point.x <= ax.max(bx)
92 && point.y >= ay.min(by)
93 && point.y <= ay.max(by)
94 {
95 return allow_on_edge;
96 }
97 if (ay > point.y) != (by > point.y) && (cross > 0) == (by > ay) {
98 inside = !inside;
99 }
100 }
101 inside
102}
103
104#[derive(Copy, Clone, Debug)]
105pub struct Rect {
106 pub min: Point,
107 pub max: Point,
108}
109
110impl Rect {
111 pub fn contains_point(&self, p: Point) -> bool {
112 return p.x >= self.min.x && p.x <= self.max.x && p.y >= self.min.y && p.y <= self.max.y;
113 }
114
115 pub fn intersects_rect(&self, other: Rect) -> bool {
116 if self.min.y > other.max.y || self.max.y < other.min.y {
117 return false;
118 }
119 if self.min.x > other.max.x || self.max.x < other.min.x {
120 return false;
121 }
122 return true;
123 }
124
125 pub fn nw(&self) -> Point {
126 Point {
127 x: self.min.x,
128 y: self.max.y,
129 }
130 }
131
132 pub fn sw(&self) -> Point {
133 Point {
134 x: self.min.x,
135 y: self.min.y,
136 }
137 }
138
139 pub fn se(&self) -> Point {
140 Point {
141 x: self.max.x,
142 y: self.min.y,
143 }
144 }
145
146 pub fn ne(&self) -> Point {
147 Point {
148 x: self.max.x,
149 y: self.max.y,
150 }
151 }
152
153 pub fn south(&self) -> Segment {
154 Segment {
155 a: self.sw(),
156 b: self.se(),
157 }
158 }
159
160 pub fn east(&self) -> Segment {
161 Segment {
162 a: self.se(),
163 b: self.ne(),
164 }
165 }
166
167 pub fn north(&self) -> Segment {
168 Segment {
169 a: self.ne(),
170 b: self.nw(),
171 }
172 }
173
174 pub fn west(&self) -> Segment {
175 Segment {
176 a: self.nw(),
177 b: self.sw(),
178 }
179 }
180
181 pub fn segment_at(&self, index: i64) -> Segment {
182 match index {
183 0 => self.south(),
184 1 => self.east(),
185 2 => self.north(),
186 3 => self.west(),
187 _ => self.south(), }
189 }
190}
191
192#[derive(Copy, Clone, Debug)]
193pub struct PolygonBuildOptions {
194 pub enable_rtree: bool,
195 pub enable_compressed_quad: bool,
196 pub enable_y_stripes: bool,
197 pub rtree_min_segments: usize,
198}
199
200impl Default for PolygonBuildOptions {
201 fn default() -> Self {
202 Self {
203 enable_rtree: false,
204 enable_compressed_quad: true,
205 enable_y_stripes: false,
206 rtree_min_segments: 64,
207 }
208 }
209}
210
211#[derive(Clone, Debug, Default, PartialEq, Eq)]
212pub struct YStripesBuildStats {
213 pub segment_count: usize,
214 pub stripe_count: usize,
215 pub assigned_item_count: usize,
216 pub max_bucket_len: usize,
217}
218
219#[derive(Clone, Debug, Default, PartialEq, Eq)]
220pub struct RingBuildStats {
221 pub segment_count: usize,
222 pub below_threshold: bool,
223 pub used_rtree: bool,
224 pub used_compressed_quad: bool,
225 pub used_y_stripes: bool,
226 pub y_stripes: Option<YStripesBuildStats>,
227}
228
229#[derive(Clone, Debug, Default, PartialEq, Eq)]
230pub struct PolygonIndexStats {
231 pub exterior: RingBuildStats,
232 pub holes: Vec<RingBuildStats>,
233}
234
235struct RingIndex {
236 x_min: f64,
237 x_max: f64,
238 seg_count: usize,
239 rtree: Option<RTree<2, f64, usize>>,
240 compressed_quad: Option<CompressedQuadIndex>,
241 y_stripes: Option<YStripesIndex>,
242}
243
244impl RingIndex {
245 fn search_candidates<T: CoordStorage>(
246 &self,
247 ring: &[Point<T>],
248 point_y: f64,
249 out: &mut Vec<usize>,
250 ) {
251 match (
254 self.rtree.as_ref(),
255 self.y_stripes.as_ref(),
256 self.compressed_quad.as_ref(),
257 ) {
258 (None, Some(index), None) => {
259 index.search(ring, point_y, out);
260 return;
261 }
262 (Some(tree), None, None) => {
263 let query = RTreeRect::new([self.x_min, point_y], [self.x_max, point_y]);
264 out.extend(
265 tree.search(query)
266 .map(|item| *item.data)
267 .filter(|&idx| idx < self.seg_count),
268 );
269 return;
270 }
271 (None, None, Some(index)) => {
272 let query_rect = Rect {
273 min: Point {
274 x: self.x_min,
275 y: point_y,
276 },
277 max: Point {
278 x: self.x_max,
279 y: point_y,
280 },
281 };
282 index.search_intersects(query_rect, out);
283 return;
284 }
285 _ => {}
286 }
287
288 let query_rect = Rect {
289 min: Point {
290 x: self.x_min,
291 y: point_y,
292 },
293 max: Point {
294 x: self.x_max,
295 y: point_y,
296 },
297 };
298
299 let mut seen = vec![false; self.seg_count];
300
301 if let Some(tree) = self.rtree.as_ref() {
302 let query = RTreeRect::new([self.x_min, point_y], [self.x_max, point_y]);
303 for item in tree.search(query) {
304 let idx = *item.data;
305 if idx < self.seg_count && !seen[idx] {
306 seen[idx] = true;
307 out.push(idx);
308 }
309 }
310 }
311
312 if let Some(index) = self.y_stripes.as_ref() {
313 let mut tmp = Vec::new();
314 index.search(ring, point_y, &mut tmp);
315 for idx in tmp {
316 if idx < self.seg_count && !seen[idx] {
317 seen[idx] = true;
318 out.push(idx);
319 }
320 }
321 }
322
323 if let Some(index) = self.compressed_quad.as_ref() {
324 let mut tmp = Vec::new();
325 index.search_intersects(query_rect, &mut tmp);
326 for idx in tmp {
327 if idx < self.seg_count && !seen[idx] {
328 seen[idx] = true;
329 out.push(idx);
330 }
331 }
332 }
333 }
334}
335
336#[derive(Clone, Copy, Default)]
337struct YStripe {
338 start: u32,
339 count: u32,
340}
341
342struct YStripesIndex {
343 min_y: f64,
344 height: f64,
345 stripes: Vec<YStripe>,
346 indexes: Vec<u32>,
347}
348
349impl YStripesIndex {
350 fn build<T: CoordStorage>(
351 ring: &[Point<T>],
352 seg_rects: &[Rect],
353 ) -> Option<(Self, YStripesBuildStats)> {
354 if seg_rects.is_empty() {
355 return None;
356 }
357
358 let mut min_y = seg_rects[0].min.y;
359 let mut max_y = seg_rects[0].max.y;
360 for rect in seg_rects.iter().copied() {
361 min_y = min_y.min(rect.min.y);
362 max_y = max_y.max(rect.max.y);
363 }
364
365 let mut stripe_count = calc_y_stripe_count(ring, seg_rects.len());
366 if stripe_count == 0 {
367 stripe_count = 1;
368 }
369
370 let height = max_y - min_y;
371 let mut counts = vec![0usize; stripe_count];
372 for rect in seg_rects.iter().copied() {
373 let (start, end) = stripe_bounds_for_rect(rect, min_y, height, stripe_count);
374 for stripe in start..=end {
375 counts[stripe] += 1;
376 }
377 }
378
379 let mut stripes = vec![YStripe::default(); stripe_count];
380 let mut offset = 0usize;
381 for (stripe, count) in stripes.iter_mut().zip(&counts) {
382 stripe.start = u32::try_from(offset).ok()?;
383 stripe.count = 0;
384 offset += *count;
385 }
386 if u32::try_from(offset).is_err() || u32::try_from(seg_rects.len()).is_err() {
388 return None;
389 }
390
391 let mut indexes = vec![0u32; offset];
392 for (idx, rect) in seg_rects.iter().copied().enumerate() {
393 let (start, end) = stripe_bounds_for_rect(rect, min_y, height, stripe_count);
394 for stripe_index in start..=end {
395 let stripe = &mut stripes[stripe_index];
396 indexes[(stripe.start + stripe.count) as usize] = idx as u32;
397 stripe.count += 1;
398 }
399 }
400
401 let mut stats = YStripesBuildStats {
402 segment_count: seg_rects.len(),
403 stripe_count,
404 assigned_item_count: indexes.len(),
405 max_bucket_len: counts.into_iter().max().unwrap_or(0),
406 };
407 if stats.stripe_count == 0 {
408 stats.stripe_count = 1;
409 }
410
411 Some((
412 Self {
413 min_y,
414 height,
415 stripes,
416 indexes,
417 },
418 stats,
419 ))
420 }
421
422 fn search<T: CoordStorage>(&self, ring: &[Point<T>], y: f64, out: &mut Vec<usize>) {
423 if self.height == 0.0 {
424 if y != self.min_y {
425 return;
426 }
427 } else if y < self.min_y || y > self.min_y + self.height {
428 return;
429 }
430
431 let stripe_index = if self.height == 0.0 {
432 0
433 } else {
434 let raw = ((y - self.min_y) / self.height * self.stripes.len() as f64).floor() as isize;
435 raw.clamp(0, self.stripes.len() as isize - 1) as usize
436 };
437 let stripe = self.stripes[stripe_index];
438 let start = stripe.start as usize;
439 let end = start + stripe.count as usize;
440 for idx in &self.indexes[start..end] {
441 let idx = *idx as usize;
442 let a_y = ring[idx].y.to_f64();
446 let b_y = ring[idx + 1].y.to_f64();
447 let (seg_min_y, seg_max_y) = if a_y <= b_y { (a_y, b_y) } else { (b_y, a_y) };
448 if y >= seg_min_y && y <= seg_max_y {
449 out.push(idx);
450 }
451 }
452 }
453}
454
455fn stripe_bounds_for_rect(
456 rect: Rect,
457 min_y: f64,
458 height: f64,
459 stripe_count: usize,
460) -> (usize, usize) {
461 if stripe_count <= 1 || height == 0.0 {
462 return (0, 0);
463 }
464
465 let last = stripe_count - 1;
466 let start = (((rect.min.y - min_y) / height) * stripe_count as f64).floor() as isize;
467 let end = (((rect.max.y - min_y) / height) * stripe_count as f64).floor() as isize;
468 (
469 start.clamp(0, last as isize) as usize,
470 end.clamp(0, last as isize) as usize,
471 )
472}
473
474fn calc_ring_area_and_perimeter<T: CoordStorage>(ring: &[Point<T>]) -> (f64, f64) {
475 let seg_count = ring_segment_count(ring);
476 if seg_count == 0 {
477 return (0.0, 0.0);
478 }
479
480 let mut signed_area = 0.0;
483 let mut perimeter = 0.0;
484 for i in 0..seg_count {
485 let a = Point {
486 x: ring[i].x.to_f64(),
487 y: ring[i].y.to_f64(),
488 };
489 let b = Point {
490 x: ring[i + 1].x.to_f64(),
491 y: ring[i + 1].y.to_f64(),
492 };
493 signed_area += a.x * b.y - b.x * a.y;
494 perimeter += ((b.x - a.x).powi(2) + (b.y - a.y).powi(2)).sqrt();
495 }
496 (signed_area.abs() * 0.5, perimeter)
497}
498
499fn calc_y_stripe_count<T: CoordStorage>(ring: &[Point<T>], seg_count: usize) -> usize {
500 let (area, perimeter) = calc_ring_area_and_perimeter(ring);
501 let mut score = 0.0;
502 if perimeter > 0.0 {
503 score = (area * std::f64::consts::PI * 4.0) / (perimeter * perimeter);
504 }
505 ((seg_count as f64 * score).floor() as usize).max(32)
506}
507
508const Q_MAX_ITEMS: usize = 12;
509const Q_MAX_DEPTH: usize = 64;
510
511#[derive(Default)]
512struct QuadNode {
513 split: bool,
514 items: Vec<usize>,
515 quads: [Option<Box<QuadNode>>; 4],
516}
517
518impl QuadNode {
519 fn new() -> Self {
520 Self {
521 split: false,
522 items: Vec::new(),
523 quads: [None, None, None, None],
524 }
525 }
526}
527
528struct CompressedQuadIndex {
529 bounds: Rect,
530 seg_rects: Vec<Rect>,
531 data: Vec<u8>,
532}
533
534impl CompressedQuadIndex {
535 fn build<T: CoordStorage>(ring: &[Point<T>]) -> Option<Self> {
536 let seg_count = ring_segment_count(ring);
537 if seg_count == 0 {
538 return None;
539 }
540
541 let mut min_x = ring[0].x.to_f64();
542 let mut min_y = ring[0].y.to_f64();
543 let mut max_x = min_x;
544 let mut max_y = min_y;
545
546 for p in ring.iter().take(seg_count) {
547 let x = p.x.to_f64();
548 let y = p.y.to_f64();
549 if x < min_x {
550 min_x = x;
551 }
552 if y < min_y {
553 min_y = y;
554 }
555 if x > max_x {
556 max_x = x;
557 }
558 if y > max_y {
559 max_y = y;
560 }
561 }
562
563 let bounds = Rect {
564 min: Point { x: min_x, y: min_y },
565 max: Point { x: max_x, y: max_y },
566 };
567
568 let mut seg_rects = Vec::with_capacity(seg_count);
569 for i in 0..seg_count {
570 seg_rects.push(segment_at_for_slice(ring, i).rect());
571 }
572
573 let mut root = QuadNode::new();
574 for i in 0..seg_rects.len() {
575 insert_quad_node(&mut root, bounds, &seg_rects, i, 0);
576 }
577
578 let mut data = Vec::with_capacity(seg_rects.len() * 2);
579 compress_quad_node(&root, &mut data);
580
581 Some(Self {
582 bounds,
583 seg_rects,
584 data,
585 })
586 }
587
588 fn search_intersects(&self, query: Rect, out: &mut Vec<usize>) {
589 if !self.bounds.intersects_rect(query) {
590 return;
591 }
592 let _ = self.search_intersects_from(0, self.bounds, query, out);
593 }
594
595 fn search_intersects_from(
596 &self,
597 mut addr: usize,
598 bounds: Rect,
599 query: Rect,
600 out: &mut Vec<usize>,
601 ) -> Option<usize> {
602 let (nitems, next_addr) = read_uvarint(&self.data, addr)?;
603 addr = next_addr;
604
605 let mut last: usize = 0;
606 for _ in 0..nitems {
607 let (delta, next_addr) = read_uvarint(&self.data, addr)?;
608 addr = next_addr;
609 last = last.checked_add(delta as usize)?;
610 let seg_rect = self.seg_rects.get(last)?;
611 if seg_rect.intersects_rect(query) {
612 out.push(last);
613 }
614 }
615
616 let split = *self.data.get(addr)?;
617 addr += 1;
618 if split == 0 {
619 return Some(addr);
620 }
621 if split != 1 {
622 return None;
623 }
624
625 for q in 0..4 {
626 let (qsize, next_addr) = read_uvarint(&self.data, addr)?;
627 addr = next_addr;
628 if qsize == 0 {
629 continue;
630 }
631
632 let qsize = usize::try_from(qsize).ok()?;
633 let qbounds = quad_bounds(bounds, q);
634 let child_start = addr;
635 let child_end = child_start.checked_add(qsize)?;
636 if child_end > self.data.len() {
637 return None;
638 }
639
640 if qbounds.intersects_rect(query) {
641 let _ = self.search_intersects_from(child_start, qbounds, query, out)?;
642 }
643 addr = child_end;
644 }
645
646 Some(addr)
647 }
648}
649
650fn ring_segment_count<T>(ring: &[Point<T>]) -> usize {
651 ring.len().saturating_sub(1)
652}
653
654fn segment_at_for_slice<T: CoordStorage>(ring: &[Point<T>], index: usize) -> Segment {
658 Segment {
659 a: Point {
660 x: ring[index].x.to_f64(),
661 y: ring[index].y.to_f64(),
662 },
663 b: Point {
664 x: ring[index + 1].x.to_f64(),
665 y: ring[index + 1].y.to_f64(),
666 },
667 }
668}
669
670fn build_ring_index<T: CoordStorage>(
671 ring: &[Point<T>],
672 options: &PolygonBuildOptions,
673) -> (Option<RingIndex>, RingBuildStats) {
674 let seg_count = ring_segment_count(ring);
675 let index_requested =
676 options.enable_rtree || options.enable_compressed_quad || options.enable_y_stripes;
677 let mut stats = RingBuildStats {
678 segment_count: seg_count,
679 below_threshold: index_requested && seg_count < options.rtree_min_segments,
680 ..RingBuildStats::default()
681 };
682
683 if !index_requested || ring.is_empty() || seg_count < options.rtree_min_segments {
684 return (None, stats);
685 }
686
687 let mut x_min = ring[0].x.to_f64();
688 let mut x_max = x_min;
689 for p in ring.iter().take(seg_count) {
690 let x = p.x.to_f64();
691 if x < x_min {
692 x_min = x;
693 }
694 if x > x_max {
695 x_max = x;
696 }
697 }
698
699 let rtree = if options.enable_rtree {
700 stats.used_rtree = true;
701 let mut tree: RTree<2, f64, usize> = RTree::new();
702 for i in 0..seg_count {
703 let seg_rect = segment_at_for_slice(ring, i).rect();
704 tree.insert(
705 RTreeRect::new(
706 [seg_rect.min.x, seg_rect.min.y],
707 [seg_rect.max.x, seg_rect.max.y],
708 ),
709 i,
710 );
711 }
712 Some(tree)
713 } else {
714 None
715 };
716
717 let compressed_quad = if options.enable_compressed_quad {
718 match CompressedQuadIndex::build(ring) {
719 Some(index) => {
720 stats.used_compressed_quad = true;
721 Some(index)
722 }
723 None => None,
724 }
725 } else {
726 None
727 };
728
729 let (y_stripes, y_stripes_stats) = if options.enable_y_stripes {
730 let mut seg_rects = Vec::with_capacity(seg_count);
731 for i in 0..seg_count {
732 seg_rects.push(segment_at_for_slice(ring, i).rect());
733 }
734 match YStripesIndex::build(ring, &seg_rects) {
735 Some((index, stripe_stats)) => {
736 stats.used_y_stripes = true;
737 (Some(index), Some(stripe_stats))
738 }
739 None => (None, None),
740 }
741 } else {
742 (None, None)
743 };
744 stats.y_stripes = y_stripes_stats;
745
746 if rtree.is_none() && compressed_quad.is_none() && y_stripes.is_none() {
747 return (None, stats);
748 }
749
750 (
751 Some(RingIndex {
752 x_min,
753 x_max,
754 seg_count,
755 rtree,
756 compressed_quad,
757 y_stripes,
758 }),
759 stats,
760 )
761}
762
763fn rings_contains_point<T: CoordStorage>(
764 ring: &[Point<T>],
765 ring_index: Option<&RingIndex>,
766 point: Point,
767 allow_on_edge: bool,
768) -> bool {
769 let mut inside: bool = false;
770
771 if let Some(index) = ring_index {
772 let mut candidates = Vec::new();
773 index.search_candidates(ring, point.y, &mut candidates);
774 for i in candidates {
775 let seg = segment_at_for_slice(ring, i);
776 let res: RaycastResult = raycast(&seg, point);
777 if res.on {
778 inside = allow_on_edge;
779 break;
780 }
781 if res.inside {
782 inside = !inside;
783 }
784 }
785 return inside;
786 }
787
788 for pair in ring.windows(2) {
789 let seg = Segment {
790 a: Point {
791 x: pair[0].x.to_f64(),
792 y: pair[0].y.to_f64(),
793 },
794 b: Point {
795 x: pair[1].x.to_f64(),
796 y: pair[1].y.to_f64(),
797 },
798 };
799
800 let min_y = seg.a.y.min(seg.b.y);
804 let max_y = seg.a.y.max(seg.b.y);
805 if point.y < min_y || point.y > max_y {
806 continue;
807 }
808
809 let res: RaycastResult = raycast(&seg, point);
810 if res.on {
811 inside = allow_on_edge;
812 break;
813 }
814 if res.inside {
815 inside = !inside;
816 }
817 }
818
819 return inside;
820}
821
822fn choose_quad(bounds: Rect, rect: Rect) -> Option<usize> {
823 let mid_x = (bounds.min.x + bounds.max.x) / 2.0;
824 let mid_y = (bounds.min.y + bounds.max.y) / 2.0;
825
826 if rect.max.x < mid_x {
827 if rect.max.y < mid_y {
828 return Some(2);
829 }
830 if rect.min.y < mid_y {
831 return None;
832 }
833 return Some(0);
834 }
835
836 if rect.min.x < mid_x {
837 return None;
838 }
839
840 if rect.max.y < mid_y {
841 return Some(3);
842 }
843 if rect.min.y < mid_y {
844 return None;
845 }
846 Some(1)
847}
848
849fn quad_bounds(mut bounds: Rect, q: usize) -> Rect {
850 let center_x = (bounds.min.x + bounds.max.x) / 2.0;
851 let center_y = (bounds.min.y + bounds.max.y) / 2.0;
852
853 match q {
854 0 => {
855 bounds.min.y = center_y;
856 bounds.max.x = center_x;
857 }
858 1 => {
859 bounds.min.x = center_x;
860 bounds.min.y = center_y;
861 }
862 2 => {
863 bounds.max.x = center_x;
864 bounds.max.y = center_y;
865 }
866 3 => {
867 bounds.min.x = center_x;
868 bounds.max.y = center_y;
869 }
870 _ => {}
871 }
872 bounds
873}
874
875fn insert_quad_node(
876 node: &mut QuadNode,
877 bounds: Rect,
878 seg_rects: &[Rect],
879 item: usize,
880 depth: usize,
881) {
882 if depth == Q_MAX_DEPTH {
883 node.items.push(item);
884 return;
885 }
886
887 let item_rect = seg_rects[item];
888 if node.split {
889 if let Some(q) = choose_quad(bounds, item_rect) {
890 let qbounds = quad_bounds(bounds, q);
891 if node.quads[q].is_none() {
892 node.quads[q] = Some(Box::new(QuadNode::new()));
893 }
894 if let Some(quad) = node.quads[q].as_deref_mut() {
895 insert_quad_node(quad, qbounds, seg_rects, item, depth + 1);
896 }
897 } else {
898 node.items.push(item);
899 }
900 return;
901 }
902
903 if node.items.len() == Q_MAX_ITEMS {
904 let existing = std::mem::take(&mut node.items);
905 node.split = true;
906 for i in existing {
907 let rect = seg_rects[i];
908 if let Some(q) = choose_quad(bounds, rect) {
909 let qbounds = quad_bounds(bounds, q);
910 if node.quads[q].is_none() {
911 node.quads[q] = Some(Box::new(QuadNode::new()));
912 }
913 if let Some(quad) = node.quads[q].as_deref_mut() {
914 insert_quad_node(quad, qbounds, seg_rects, i, depth + 1);
915 }
916 } else {
917 node.items.push(i);
918 }
919 }
920 insert_quad_node(node, bounds, seg_rects, item, depth);
921 return;
922 }
923
924 node.items.push(item);
925}
926
927fn append_uvarint(dst: &mut Vec<u8>, mut x: u64) {
928 while x >= 0x80 {
929 dst.push((x as u8 & 0x7f) | 0x80);
930 x >>= 7;
931 }
932 dst.push(x as u8);
933}
934
935fn read_uvarint(data: &[u8], mut addr: usize) -> Option<(u64, usize)> {
936 let mut x: u64 = 0;
937 let mut shift = 0;
938
939 loop {
940 let b = *data.get(addr)?;
941 addr += 1;
942
943 if shift == 70 {
944 return None;
945 }
946
947 x |= ((b & 0x7f) as u64) << shift;
948 if b < 0x80 {
949 return Some((x, addr));
950 }
951 shift += 7;
952 }
953}
954
955fn compress_quad_node(node: &QuadNode, dst: &mut Vec<u8>) {
956 let mut items = node.items.clone();
957 items.sort_unstable();
958
959 append_uvarint(dst, items.len() as u64);
960 let mut last = 0usize;
961 for item in items {
962 append_uvarint(dst, (item - last) as u64);
963 last = item;
964 }
965
966 if !node.split {
967 dst.push(0);
968 return;
969 }
970
971 dst.push(1);
972 for q in 0..4 {
973 if let Some(child) = node.quads[q].as_deref() {
974 let mut child_bytes = Vec::new();
975 compress_quad_node(child, &mut child_bytes);
976 append_uvarint(dst, child_bytes.len() as u64);
977 dst.extend_from_slice(&child_bytes);
978 } else {
979 append_uvarint(dst, 0);
980 }
981 }
982}
983
984pub struct Polygon<T: CoordStorage = f64> {
992 exterior: Vec<Point<T>>,
993 holes: Vec<Vec<Point<T>>>,
994 min: Point<T>,
997 max: Point<T>,
998 scale: f64,
999 raycast_mode: I32RaycastMode,
1000 options: PolygonBuildOptions,
1001 exterior_index: Option<RingIndex>,
1002 hole_indexes: Vec<Option<RingIndex>>,
1003 index_stats: PolygonIndexStats,
1004}
1005
1006impl<T: CoordStorage> Polygon<T> {
1007 fn compute_bounds(exterior: &[Point<T>]) -> (Point<T>, Point<T>) {
1008 let mut min = exterior[0];
1009 let mut max = exterior[0];
1010
1011 for p in exterior.iter() {
1012 if p.x < min.x {
1013 min.x = p.x;
1014 }
1015 if p.y < min.y {
1016 min.y = p.y;
1017 }
1018 if p.x > max.x {
1019 max.x = p.x;
1020 }
1021 if p.y > max.y {
1022 max.y = p.y;
1023 }
1024 }
1025
1026 (min, max)
1027 }
1028
1029 fn build(
1030 exterior: Vec<Point<T>>,
1031 holes: Vec<Vec<Point<T>>>,
1032 scale: f64,
1033 raycast_mode: I32RaycastMode,
1034 options: Option<PolygonBuildOptions>,
1035 ) -> Self {
1036 let (min, max) = Self::compute_bounds(&exterior);
1037 let mut poly = Self {
1038 exterior,
1039 holes,
1040 min,
1041 max,
1042 scale,
1043 raycast_mode,
1044 options: options.unwrap_or_default(),
1045 exterior_index: None,
1046 hole_indexes: Vec::new(),
1047 index_stats: PolygonIndexStats::default(),
1048 };
1049 poly.rebuild_cache();
1050 poly
1051 }
1052
1053 fn rebuild_cache(&mut self) {
1054 let (min, max) = Self::compute_bounds(&self.exterior);
1055 self.min = min;
1056 self.max = max;
1057 let (exterior_index, exterior_stats) = build_ring_index(&self.exterior, &self.options);
1058 self.exterior_index = exterior_index;
1059 self.index_stats.exterior = exterior_stats;
1060
1061 let hole_indexes_and_stats: Vec<(Option<RingIndex>, RingBuildStats)> = self
1062 .holes
1063 .iter()
1064 .map(|hole| build_ring_index(hole, &self.options))
1065 .collect();
1066 let (hole_indexes, hole_stats): (Vec<Option<RingIndex>>, Vec<RingBuildStats>) =
1067 hole_indexes_and_stats.into_iter().unzip();
1068 self.hole_indexes = hole_indexes;
1069 self.index_stats.holes = hole_stats;
1070 }
1071
1072 fn contains_point_normal(&self, p: Point, allow_on_edge: bool) -> bool {
1082 if !rings_contains_point(
1083 &self.exterior,
1084 self.exterior_index.as_ref(),
1085 p,
1086 allow_on_edge,
1087 ) {
1088 return false;
1089 }
1090
1091 for (hole, hole_index) in self.holes.iter().zip(self.hole_indexes.iter()) {
1092 if rings_contains_point(hole, hole_index.as_ref(), p, false) {
1093 return false;
1094 }
1095 }
1096
1097 return true;
1098 }
1099
1100 pub fn contains_point(&self, p: Point) -> bool {
1108 self.contains_point_with_edge_rule(p, false)
1109 }
1110
1111 pub fn contains_point_allow_on_edge(&self, p: Point) -> bool {
1120 self.contains_point_with_edge_rule(p, true)
1121 }
1122
1123 fn contains_point_with_edge_rule(&self, p: Point, allow_on_edge: bool) -> bool {
1124 match self.raycast_mode {
1125 I32RaycastMode::Float => self.contains_point_float(p, allow_on_edge),
1126 I32RaycastMode::Integer => self.contains_point_integer(p, allow_on_edge),
1127 }
1128 }
1129
1130 fn contains_point_float(&self, p: Point, allow_on_edge: bool) -> bool {
1131 let scaled = Point {
1134 x: p.x * self.scale,
1135 y: p.y * self.scale,
1136 };
1137 if !(scaled.x >= self.min.x.to_f64()
1140 && scaled.x <= self.max.x.to_f64()
1141 && scaled.y >= self.min.y.to_f64()
1142 && scaled.y <= self.max.y.to_f64())
1143 {
1144 return false;
1145 }
1146
1147 return self.contains_point_normal(scaled, allow_on_edge);
1148 }
1149
1150 fn contains_point_integer(&self, p: Point, allow_on_edge: bool) -> bool {
1153 if !p.x.is_finite() || !p.y.is_finite() {
1154 return false;
1155 }
1156 let scaled = Point::<i64> {
1157 x: (p.x * self.scale).round() as i64,
1158 y: (p.y * self.scale).round() as i64,
1159 };
1160 if scaled.x < self.min.x.to_i64()
1161 || scaled.x > self.max.x.to_i64()
1162 || scaled.y < self.min.y.to_i64()
1163 || scaled.y > self.max.y.to_i64()
1164 || !ring_contains_point_integer(&self.exterior, scaled, allow_on_edge)
1165 {
1166 return false;
1167 }
1168 !self
1169 .holes
1170 .iter()
1171 .any(|ring| ring_contains_point_integer(ring, scaled, false))
1172 }
1173 pub fn exterior(&self) -> &[Point<T>] {
1174 &self.exterior
1175 }
1176
1177 pub fn holes(&self) -> &[Vec<Point<T>>] {
1178 &self.holes
1179 }
1180
1181 pub fn rect(&self) -> Rect {
1184 Rect {
1185 min: Point {
1186 x: self.min.x.to_f64(),
1187 y: self.min.y.to_f64(),
1188 },
1189 max: Point {
1190 x: self.max.x.to_f64(),
1191 y: self.max.y.to_f64(),
1192 },
1193 }
1194 }
1195
1196 pub fn scale(&self) -> f64 {
1199 self.scale
1200 }
1201
1202 pub fn options(&self) -> PolygonBuildOptions {
1203 self.options
1204 }
1205
1206 pub fn index_stats(&self) -> &PolygonIndexStats {
1207 &self.index_stats
1208 }
1209
1210 pub fn set_exterior(&mut self, exterior: Vec<Point<T>>) {
1211 self.exterior = exterior;
1212 self.rebuild_cache();
1213 }
1214
1215 pub fn set_holes(&mut self, holes: Vec<Vec<Point<T>>>) {
1216 self.holes = holes;
1217 self.rebuild_cache();
1218 }
1219
1220 pub fn set_options(&mut self, options: PolygonBuildOptions) {
1221 self.options = options;
1222 self.rebuild_cache();
1223 }
1224}
1225
1226impl Polygon {
1227 pub fn new(
1275 exterior: Vec<Point>,
1276 holes: Vec<Vec<Point>>,
1277 options: Option<PolygonBuildOptions>,
1278 ) -> Polygon {
1279 Self::build(exterior, holes, 1.0, I32RaycastMode::Float, options)
1280 }
1281}
1282
1283impl I32Polygon {
1284 pub fn new_with_mode(
1290 exterior: Vec<I32Point>,
1291 holes: Vec<Vec<I32Point>>,
1292 scale: f64,
1293 raycast_mode: I32RaycastMode,
1294 ) -> Self {
1295 Self::new_with_options(exterior, holes, scale, raycast_mode, None)
1296 }
1297
1298 pub fn new_with_options(
1303 exterior: Vec<I32Point>,
1304 holes: Vec<Vec<I32Point>>,
1305 scale: f64,
1306 raycast_mode: I32RaycastMode,
1307 options: Option<PolygonBuildOptions>,
1308 ) -> Self {
1309 assert!(!exterior.is_empty(), "polygon exterior must not be empty");
1310 assert!(
1311 scale.is_finite() && scale > 0.0,
1312 "scale must be positive and finite"
1313 );
1314 Self::build(exterior, holes, scale, raycast_mode, options)
1315 }
1316}
1317
1318impl<T: CoordStorage> ContainsPoint<Point> for Polygon<T> {
1319 fn contains_point(&self, point: Point) -> bool {
1320 Polygon::contains_point(self, point)
1321 }
1322}
1323
1324#[derive(Copy, Clone, Debug)]
1325pub struct Segment {
1326 pub a: Point,
1327 pub b: Point,
1328}
1329
1330impl Segment {
1331 pub fn rect(&self) -> Rect {
1332 let mut min_x: f64 = self.a.x;
1333 let mut min_y: f64 = self.a.y;
1334 let mut max_x: f64 = self.b.x;
1335 let mut max_y: f64 = self.b.y;
1336
1337 if min_x > max_x {
1338 let actual_min_x = max_x;
1339 let actual_max_x = min_x;
1340 min_x = actual_min_x;
1341 max_x = actual_max_x;
1342 }
1343
1344 if min_y > max_y {
1345 let actual_min_y = max_y;
1346 let actual_max_y = min_y;
1347 min_y = actual_min_y;
1348 max_y = actual_max_y;
1349 }
1350
1351 return Rect {
1352 min: Point { x: min_x, y: min_y },
1353 max: Point { x: max_x, y: max_y },
1354 };
1355 }
1356}
1357
1358pub struct RaycastResult {
1359 inside: bool, on: bool, }
1362
1363pub fn raycast(seg: &Segment, point: Point) -> RaycastResult {
1364 let mut p = point;
1365 let a = seg.a;
1366 let b = seg.b;
1367
1368 if a.y < b.y && (p.y < a.y || p.y > b.y) {
1370 return RaycastResult {
1371 inside: false,
1372 on: false,
1373 };
1374 } else if a.y > b.y && (p.y < b.y || p.y > a.y) {
1375 return RaycastResult {
1376 inside: false,
1377 on: false,
1378 };
1379 }
1380
1381 if a.y == b.y {
1383 if a.x == b.x {
1384 if p.x == a.x && p.y == a.y {
1385 return RaycastResult {
1386 inside: false,
1387 on: true,
1388 };
1389 }
1390 return RaycastResult {
1391 inside: false,
1392 on: false,
1393 };
1394 }
1395 if p.y == b.y {
1396 if a.x < b.x {
1399 if p.x >= a.x && p.x <= b.x {
1400 return RaycastResult {
1401 inside: false,
1402 on: true,
1403 };
1404 }
1405 } else if p.x >= b.x && p.x <= a.x {
1406 return RaycastResult {
1407 inside: false,
1408 on: true,
1409 };
1410 }
1411 }
1412 }
1413 if a.x == b.x && p.x == b.x {
1414 if a.y < b.y {
1417 if p.y >= a.y && p.y <= b.y {
1418 return RaycastResult {
1419 inside: false,
1420 on: true,
1421 };
1422 }
1423 } else if p.y >= b.y && p.y <= a.y {
1424 return RaycastResult {
1425 inside: false,
1426 on: true,
1427 };
1428 }
1429 }
1430 if (p.x - a.x) / (b.x - a.x) == (p.y - a.y) / (b.y - a.y) {
1431 return RaycastResult {
1432 inside: false,
1433 on: true,
1434 };
1435 }
1436
1437 while p.y == a.y || p.y == b.y {
1439 p.y = p.y.next_after(std::f64::INFINITY);
1440 }
1441
1442 if a.y < b.y {
1443 if p.y < a.y || p.y > b.y {
1444 return RaycastResult {
1445 inside: false,
1446 on: false,
1447 };
1448 }
1449 } else if p.y < b.y || p.y > a.y {
1450 return RaycastResult {
1451 inside: false,
1452 on: false,
1453 };
1454 }
1455 if a.x > b.x {
1456 if p.x >= a.x {
1457 return RaycastResult {
1458 inside: false,
1459 on: false,
1460 };
1461 }
1462 if p.x <= b.x {
1463 return RaycastResult {
1464 inside: true,
1465 on: false,
1466 };
1467 }
1468 } else {
1469 if p.x >= b.x {
1470 return RaycastResult {
1471 inside: false,
1472 on: false,
1473 };
1474 }
1475 if p.x <= a.x {
1476 return RaycastResult {
1477 inside: true,
1478 on: false,
1479 };
1480 }
1481 }
1482 if a.y < b.y {
1483 if (p.y - a.y) / (p.x - a.x) >= (b.y - a.y) / (b.x - a.x) {
1484 return RaycastResult {
1485 inside: true,
1486 on: false,
1487 };
1488 }
1489 } else if (p.y - b.y) / (p.x - b.x) >= (a.y - b.y) / (a.x - b.x) {
1490 return RaycastResult {
1491 inside: true,
1492 on: false,
1493 };
1494 }
1495 return RaycastResult {
1496 inside: false,
1497 on: false,
1498 };
1499}
1500
1501#[cfg(test)]
1502mod tests {
1503 use super::*;
1504
1505 fn square(min: f64, max: f64) -> Vec<Point> {
1506 vec![
1507 Point { x: min, y: min },
1508 Point { x: min, y: max },
1509 Point { x: max, y: max },
1510 Point { x: max, y: min },
1511 Point { x: min, y: min },
1512 ]
1513 }
1514
1515 fn polygon_with_segments(segments: usize) -> Vec<Point> {
1516 let mut ring = Vec::with_capacity(segments + 1);
1517 for i in 0..segments {
1518 let theta = (i as f64) / (segments as f64) * std::f64::consts::TAU;
1519 ring.push(Point {
1520 x: theta.cos(),
1521 y: theta.sin(),
1522 });
1523 }
1524 ring.push(ring[0]);
1525 ring
1526 }
1527
1528 fn scan_candidates(ring: &[Point], y: f64, x_min: f64, x_max: f64) -> Vec<usize> {
1529 let mut out = Vec::new();
1530 let query = Rect {
1531 min: Point { x: x_min, y },
1532 max: Point { x: x_max, y },
1533 };
1534 for (i, pair) in ring.windows(2).enumerate() {
1535 let seg = Segment {
1536 a: pair[0],
1537 b: pair[1],
1538 };
1539 if seg.rect().intersects_rect(query) {
1540 out.push(i);
1541 }
1542 }
1543 out
1544 }
1545
1546 #[test]
1547 fn rings_contains_point_allow_on_edge() {
1548 let ring = square(0.0, 10.0);
1549 let on_edge = Point { x: 0.0, y: 5.0 };
1550 assert!(rings_contains_point(&ring, None, on_edge, true));
1551 assert!(!rings_contains_point(&ring, None, on_edge, false));
1552 }
1553
1554 #[test]
1555 fn polygon_contains_basic_in_and_out() {
1556 let poly = Polygon::new(square(0.0, 10.0), vec![], None);
1557 assert!(poly.contains_point(Point { x: 5.0, y: 5.0 }));
1558 assert!(!poly.contains_point(Point { x: 20.0, y: 5.0 }));
1559 }
1560
1561 #[test]
1562 fn polygon_contains_with_hole() {
1563 let poly = Polygon::new(square(0.0, 10.0), vec![square(3.0, 7.0)], None);
1564 assert!(poly.contains_point(Point { x: 1.0, y: 1.0 }));
1565 assert!(!poly.contains_point(Point { x: 5.0, y: 5.0 }));
1566 }
1567
1568 fn shifted_square(min: f64, max: f64, dx: f64) -> Vec<Point> {
1569 square(min, max)
1570 .into_iter()
1571 .map(|p| Point {
1572 x: p.x + dx,
1573 y: p.y,
1574 })
1575 .collect()
1576 }
1577
1578 #[test]
1582 fn polygon_allow_on_edge_closes_shared_border() {
1583 for options in [
1584 None,
1585 Some(PolygonBuildOptions {
1588 enable_rtree: true,
1589 enable_compressed_quad: true,
1590 enable_y_stripes: false,
1591 rtree_min_segments: 1,
1592 }),
1593 Some(PolygonBuildOptions {
1594 enable_rtree: false,
1595 enable_compressed_quad: false,
1596 enable_y_stripes: true,
1597 rtree_min_segments: 1,
1598 }),
1599 ] {
1600 let left = Polygon::new(square(0.0, 10.0), vec![], options);
1601 let right = Polygon::new(shifted_square(0.0, 10.0, 10.0), vec![], options);
1602
1603 for p in [
1604 Point { x: 10.0, y: 5.0 }, Point { x: 10.0, y: 10.0 }, Point { x: 10.0, y: 0.0 },
1607 ] {
1608 assert!(!left.contains_point(p), "{p:?} {options:?}");
1609 assert!(!right.contains_point(p), "{p:?} {options:?}");
1610 assert!(left.contains_point_allow_on_edge(p), "{p:?} {options:?}");
1611 assert!(right.contains_point_allow_on_edge(p), "{p:?} {options:?}");
1612 }
1613
1614 for p in [
1616 Point { x: 5.0, y: 5.0 },
1617 Point { x: 15.0, y: 5.0 },
1618 Point { x: 25.0, y: 5.0 },
1619 Point { x: 10.0, y: 25.0 },
1620 ] {
1621 assert_eq!(
1622 left.contains_point(p),
1623 left.contains_point_allow_on_edge(p),
1624 "{p:?} {options:?}"
1625 );
1626 assert_eq!(
1627 right.contains_point(p),
1628 right.contains_point_allow_on_edge(p),
1629 "{p:?} {options:?}"
1630 );
1631 }
1632 }
1633 }
1634
1635 #[test]
1639 fn polygon_allow_on_edge_leaves_hole_boundary_alone() {
1640 let poly = Polygon::new(square(0.0, 10.0), vec![square(3.0, 7.0)], None);
1641 let fill = Polygon::new(square(3.0, 7.0), vec![], None);
1642
1643 for p in [Point { x: 3.0, y: 5.0 }, Point { x: 3.0, y: 3.0 }] {
1644 assert!(poly.contains_point(p), "{p:?}");
1645 assert!(poly.contains_point_allow_on_edge(p), "{p:?}");
1646 assert!(!fill.contains_point(p), "{p:?}");
1647 assert!(fill.contains_point_allow_on_edge(p), "{p:?}");
1648 }
1649
1650 let inner = Point { x: 5.0, y: 5.0 };
1652 assert!(!poly.contains_point(inner));
1653 assert!(!poly.contains_point_allow_on_edge(inner));
1654 }
1655
1656 #[test]
1657 fn i32_polygon_allow_on_edge_closes_shared_border() {
1658 let rect_i32 = |x_min, x_max| {
1659 vec![
1660 I32Point { x: x_min, y: 0 },
1661 I32Point {
1662 x: x_min,
1663 y: 1_000_000,
1664 },
1665 I32Point {
1666 x: x_max,
1667 y: 1_000_000,
1668 },
1669 I32Point { x: x_max, y: 0 },
1670 I32Point { x: x_min, y: 0 },
1671 ]
1672 };
1673 for mode in [I32RaycastMode::Float, I32RaycastMode::Integer] {
1674 let left = I32Polygon::new_with_mode(rect_i32(0, 750_000), vec![], 1e5, mode);
1675 let right = I32Polygon::new_with_mode(rect_i32(750_000, 1_500_000), vec![], 1e5, mode);
1676
1677 let on_edge = Point { x: 7.5, y: 5.0 };
1678 assert!(!left.contains_point(on_edge), "{mode:?}");
1679 assert!(!right.contains_point(on_edge), "{mode:?}");
1680 assert!(left.contains_point_allow_on_edge(on_edge), "{mode:?}");
1681 assert!(right.contains_point_allow_on_edge(on_edge), "{mode:?}");
1682
1683 let outside = Point { x: 20.0, y: 5.0 };
1684 assert!(!left.contains_point_allow_on_edge(outside), "{mode:?}");
1685 assert!(!right.contains_point_allow_on_edge(outside), "{mode:?}");
1686 }
1687 }
1688
1689 #[test]
1690 fn i32_polygon_contains_with_hole_and_fractional_query() {
1691 let square = |min, max| {
1692 vec![
1693 I32Point { x: min, y: min },
1694 I32Point { x: min, y: max },
1695 I32Point { x: max, y: max },
1696 I32Point { x: max, y: min },
1697 I32Point { x: min, y: min },
1698 ]
1699 };
1700 for mode in [I32RaycastMode::Float, I32RaycastMode::Integer] {
1701 let poly = I32Polygon::new_with_mode(
1702 square(0, 1_000_000),
1703 vec![square(400_000, 600_000)],
1704 1e5,
1705 mode,
1706 );
1707 assert!(poly.contains_point(Point {
1708 x: 1.23456,
1709 y: 2.34567
1710 }));
1711 assert!(!poly.contains_point(Point { x: 5.0, y: 5.0 }));
1712 assert!(!poly.contains_point(Point { x: 11.0, y: 5.0 }));
1713 }
1714 }
1715
1716 #[test]
1717 fn indexed_and_non_indexed_results_match() {
1718 let ring = polygon_with_segments(128);
1719 let p_in = Point { x: 0.2, y: 0.1 };
1720 let p_out = Point { x: 2.0, y: 0.0 };
1721
1722 let p1 = Polygon::new(
1723 ring.clone(),
1724 vec![],
1725 Some(PolygonBuildOptions {
1726 enable_rtree: false,
1727 enable_compressed_quad: false,
1728 enable_y_stripes: false,
1729 rtree_min_segments: 64,
1730 }),
1731 );
1732
1733 let p2 = Polygon::new(
1734 ring,
1735 vec![],
1736 Some(PolygonBuildOptions {
1737 enable_rtree: true,
1738 enable_compressed_quad: true,
1739 enable_y_stripes: false,
1740 rtree_min_segments: 64,
1741 }),
1742 );
1743
1744 assert_eq!(p1.contains_point(p_in), p2.contains_point(p_in));
1745 assert_eq!(p1.contains_point(p_out), p2.contains_point(p_out));
1746 }
1747
1748 #[test]
1749 fn rtree_and_compressed_quad_and_both_match_baseline() {
1750 let ring = polygon_with_segments(160);
1751 let points = [
1752 Point { x: 0.2, y: 0.1 },
1753 Point { x: -0.4, y: -0.3 },
1754 Point { x: 1.2, y: 0.0 },
1755 ];
1756
1757 let base = Polygon::new(
1758 ring.clone(),
1759 vec![],
1760 Some(PolygonBuildOptions {
1761 enable_rtree: false,
1762 enable_compressed_quad: false,
1763 enable_y_stripes: false,
1764 rtree_min_segments: 64,
1765 }),
1766 );
1767 let only_rtree = Polygon::new(
1768 ring.clone(),
1769 vec![],
1770 Some(PolygonBuildOptions {
1771 enable_rtree: true,
1772 enable_compressed_quad: false,
1773 enable_y_stripes: false,
1774 rtree_min_segments: 64,
1775 }),
1776 );
1777 let only_compressed = Polygon::new(
1778 ring.clone(),
1779 vec![],
1780 Some(PolygonBuildOptions {
1781 enable_rtree: false,
1782 enable_compressed_quad: true,
1783 enable_y_stripes: false,
1784 rtree_min_segments: 64,
1785 }),
1786 );
1787 let both = Polygon::new(
1788 ring,
1789 vec![],
1790 Some(PolygonBuildOptions {
1791 enable_rtree: true,
1792 enable_compressed_quad: true,
1793 enable_y_stripes: false,
1794 rtree_min_segments: 64,
1795 }),
1796 );
1797
1798 for p in points {
1799 let expected = base.contains_point(p);
1800 assert_eq!(only_rtree.contains_point(p), expected);
1801 assert_eq!(only_compressed.contains_point(p), expected);
1802 assert_eq!(both.contains_point(p), expected);
1803 }
1804 }
1805
1806 #[test]
1807 fn threshold_boundaries_63_64_65_are_consistent() {
1808 let ring = polygon_with_segments(65);
1809 let p_in = Point { x: 0.2, y: 0.0 };
1810 let p_out = Point { x: 1.5, y: 0.0 };
1811
1812 let p63 = Polygon::new(
1813 ring.clone(),
1814 vec![],
1815 Some(PolygonBuildOptions {
1816 enable_rtree: true,
1817 enable_compressed_quad: true,
1818 enable_y_stripes: false,
1819 rtree_min_segments: 63,
1820 }),
1821 );
1822 let p64 = Polygon::new(
1823 ring.clone(),
1824 vec![],
1825 Some(PolygonBuildOptions {
1826 enable_rtree: true,
1827 enable_compressed_quad: true,
1828 enable_y_stripes: false,
1829 rtree_min_segments: 64,
1830 }),
1831 );
1832 let p65 = Polygon::new(
1833 ring,
1834 vec![],
1835 Some(PolygonBuildOptions {
1836 enable_rtree: true,
1837 enable_compressed_quad: true,
1838 enable_y_stripes: false,
1839 rtree_min_segments: 65,
1840 }),
1841 );
1842
1843 assert_eq!(p63.contains_point(p_in), p64.contains_point(p_in));
1844 assert_eq!(p64.contains_point(p_in), p65.contains_point(p_in));
1845
1846 assert_eq!(p63.contains_point(p_out), p64.contains_point(p_out));
1847 assert_eq!(p64.contains_point(p_out), p65.contains_point(p_out));
1848 }
1849
1850 #[test]
1851 fn setters_rebuild_cache_and_keep_correct_results() {
1852 let mut poly = Polygon::new(square(0.0, 10.0), vec![], None);
1853 assert!(poly.contains_point(Point { x: 1.0, y: 1.0 }));
1854
1855 poly.set_exterior(square(20.0, 30.0));
1856 assert!(!poly.contains_point(Point { x: 1.0, y: 1.0 }));
1857 assert!(poly.contains_point(Point { x: 21.0, y: 21.0 }));
1858
1859 poly.set_holes(vec![square(22.0, 24.0)]);
1860 assert!(!poly.contains_point(Point { x: 23.0, y: 23.0 }));
1861
1862 poly.set_options(PolygonBuildOptions {
1863 enable_rtree: false,
1864 enable_compressed_quad: false,
1865 enable_y_stripes: false,
1866 rtree_min_segments: 64,
1867 });
1868 assert!(!poly.contains_point(Point { x: 23.0, y: 23.0 }));
1869 assert!(poly.contains_point(Point { x: 25.0, y: 25.0 }));
1870 }
1871
1872 #[test]
1873 fn compressed_quad_candidates_match_scan() {
1874 let ring = polygon_with_segments(256);
1875 let options = PolygonBuildOptions {
1876 enable_rtree: false,
1877 enable_compressed_quad: true,
1878 enable_y_stripes: false,
1879 rtree_min_segments: 64,
1880 };
1881 let (index, stats) = build_ring_index(&ring, &options);
1882 let index = index.unwrap();
1883
1884 assert!(stats.used_compressed_quad);
1885 assert!(!stats.below_threshold);
1886
1887 for y in [-1.2, -1.0, -0.75, -0.2, 0.0, 0.4, 0.99, 1.0, 1.2] {
1888 let mut from_index = Vec::new();
1889 index.search_candidates(&ring, y, &mut from_index);
1890 from_index.sort_unstable();
1891
1892 let mut from_scan = scan_candidates(&ring, y, index.x_min, index.x_max);
1893 from_scan.sort_unstable();
1894
1895 assert_eq!(from_index, from_scan);
1896 }
1897 }
1898
1899 #[test]
1900 fn y_stripes_candidates_match_scan() {
1901 let ring = polygon_with_segments(256);
1902 let options = PolygonBuildOptions {
1903 enable_rtree: false,
1904 enable_compressed_quad: false,
1905 enable_y_stripes: true,
1906 rtree_min_segments: 64,
1907 };
1908 let (index, stats) = build_ring_index(&ring, &options);
1909 let index = index.unwrap();
1910
1911 assert!(stats.used_y_stripes);
1912 assert!(!stats.below_threshold);
1913 let y_stats = stats.y_stripes.as_ref().unwrap();
1914 assert_eq!(y_stats.segment_count, 256);
1915 assert!(y_stats.stripe_count >= 32);
1916 assert!(y_stats.assigned_item_count >= 256);
1917 assert!(y_stats.max_bucket_len > 0);
1918
1919 for y in [-1.2, -1.0, -0.75, -0.2, 0.0, 0.4, 0.99, 1.0, 1.2] {
1920 let mut from_index = Vec::new();
1921 index.search_candidates(&ring, y, &mut from_index);
1922 from_index.sort_unstable();
1923
1924 let mut from_scan = scan_candidates(&ring, y, index.x_min, index.x_max);
1925 from_scan.sort_unstable();
1926
1927 assert_eq!(from_index, from_scan);
1928 }
1929 }
1930
1931 #[test]
1932 fn polygon_index_stats_capture_threshold_for_compressed_quad() {
1933 let indexed = Polygon::new(
1934 polygon_with_segments(256),
1935 vec![polygon_with_segments(32)],
1936 Some(PolygonBuildOptions {
1937 enable_rtree: false,
1938 enable_compressed_quad: true,
1939 enable_y_stripes: false,
1940 rtree_min_segments: 64,
1941 }),
1942 );
1943 let stats = indexed.index_stats();
1944 assert!(stats.exterior.used_compressed_quad);
1945 assert!(!stats.exterior.below_threshold);
1946 assert_eq!(stats.exterior.segment_count, 256);
1947 assert_eq!(stats.holes.len(), 1);
1948 assert!(stats.holes[0].below_threshold);
1949 assert!(!stats.holes[0].used_compressed_quad);
1950 }
1951
1952 fn i32_polygon_with_segments(segments: usize, scale: f64) -> Vec<I32Point> {
1953 let mut ring = Vec::with_capacity(segments + 1);
1954 for i in 0..segments {
1955 let theta = (i as f64) / (segments as f64) * std::f64::consts::TAU;
1956 ring.push(I32Point {
1957 x: (theta.cos() * scale).round() as i32,
1958 y: (theta.sin() * scale).round() as i32,
1959 });
1960 }
1961 ring.push(ring[0]);
1962 ring
1963 }
1964
1965 fn y_stripes_options() -> PolygonBuildOptions {
1966 PolygonBuildOptions {
1967 enable_rtree: false,
1968 enable_compressed_quad: false,
1969 enable_y_stripes: true,
1970 rtree_min_segments: 64,
1971 }
1972 }
1973
1974 #[test]
1975 fn i32_y_stripes_matches_linear_scan() {
1976 let scale = 1e5;
1977 let ring = i32_polygon_with_segments(256, scale);
1978 let hole = i32_polygon_with_segments(96, scale * 0.4);
1979
1980 let linear = I32Polygon::new_with_mode(
1981 ring.clone(),
1982 vec![hole.clone()],
1983 scale,
1984 I32RaycastMode::Float,
1985 );
1986 let indexed = I32Polygon::new_with_options(
1987 ring,
1988 vec![hole],
1989 scale,
1990 I32RaycastMode::Float,
1991 Some(y_stripes_options()),
1992 );
1993 assert!(indexed.index_stats().exterior.used_y_stripes);
1994 assert!(indexed.index_stats().holes[0].used_y_stripes);
1995
1996 for i in 0..64 {
1999 let theta = (i as f64) / 64.0 * std::f64::consts::TAU;
2000 for r in [0.0, 0.2, 0.39999, 0.4, 0.40001, 0.7, 0.99999, 1.0, 1.1] {
2001 let p = Point {
2002 x: theta.cos() * r,
2003 y: theta.sin() * r,
2004 };
2005 assert_eq!(
2006 linear.contains_point(p),
2007 indexed.contains_point(p),
2008 "mismatch at {p:?}"
2009 );
2010 let snapped = Point {
2011 x: (p.x * scale).round() / scale,
2012 y: (p.y * scale).round() / scale,
2013 };
2014 assert_eq!(
2015 linear.contains_point(snapped),
2016 indexed.contains_point(snapped),
2017 "mismatch at snapped {snapped:?}"
2018 );
2019 }
2020 }
2021 }
2022
2023 #[test]
2024 fn i32_y_stripes_matches_f64_y_stripes_away_from_boundary() {
2025 let scale = 1e5;
2026 let int_ring = i32_polygon_with_segments(256, scale);
2027 let float_ring: Vec<Point> = int_ring
2028 .iter()
2029 .map(|p| Point {
2030 x: f64::from(p.x) / scale,
2031 y: f64::from(p.y) / scale,
2032 })
2033 .collect();
2034
2035 let int_poly = I32Polygon::new_with_options(
2036 int_ring,
2037 vec![],
2038 scale,
2039 I32RaycastMode::Float,
2040 Some(y_stripes_options()),
2041 );
2042 let float_poly = Polygon::new(float_ring, vec![], Some(y_stripes_options()));
2043
2044 for i in 0..256 {
2045 let theta = (i as f64) / 256.0 * std::f64::consts::TAU;
2046 for r in [0.0, 0.3, 0.6, 0.9, 1.1, 1.5] {
2047 let p = Point {
2048 x: theta.cos() * r,
2049 y: theta.sin() * r,
2050 };
2051 assert_eq!(
2052 float_poly.contains_point(p),
2053 int_poly.contains_point(p),
2054 "mismatch at {p:?}"
2055 );
2056 }
2057 }
2058 }
2059
2060 #[test]
2061 fn i32_y_stripes_candidates_match_scan() {
2062 let scale = 1e5;
2063 let ring = i32_polygon_with_segments(256, scale);
2064 let (index, stats) = build_ring_index(&ring, &y_stripes_options());
2065 let index = index.unwrap();
2066 assert!(stats.used_y_stripes);
2067
2068 let float_ring: Vec<Point> = ring
2069 .iter()
2070 .map(|p| Point {
2071 x: f64::from(p.x),
2072 y: f64::from(p.y),
2073 })
2074 .collect();
2075
2076 for y in [
2077 -1.2e5, -1e5, -0.75e5, -0.2e5, 0.0, 0.4e5, 0.99e5, 1e5, 1.2e5, 33333.0,
2078 ] {
2079 let mut from_index = Vec::new();
2080 index.search_candidates(&ring, y, &mut from_index);
2081 from_index.sort_unstable();
2082
2083 let mut from_scan = scan_candidates(&float_ring, y, index.x_min, index.x_max);
2084 from_scan.sort_unstable();
2085
2086 assert_eq!(from_index, from_scan, "candidate mismatch at y={y}");
2087 }
2088 }
2089
2090 #[test]
2091 fn polygon_index_stats_capture_y_stripes_metrics() {
2092 let indexed = Polygon::new(
2093 polygon_with_segments(256),
2094 vec![polygon_with_segments(32)],
2095 Some(PolygonBuildOptions {
2096 enable_rtree: false,
2097 enable_compressed_quad: false,
2098 enable_y_stripes: true,
2099 rtree_min_segments: 64,
2100 }),
2101 );
2102 let stats = indexed.index_stats();
2103 assert!(stats.exterior.used_y_stripes);
2104 assert!(!stats.exterior.below_threshold);
2105 assert_eq!(stats.exterior.segment_count, 256);
2106 assert!(stats.exterior.y_stripes.is_some());
2107 assert_eq!(stats.holes.len(), 1);
2108 assert!(stats.holes[0].below_threshold);
2109 assert!(!stats.holes[0].used_y_stripes);
2110 assert!(stats.holes[0].y_stripes.is_none());
2111 }
2112}