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 rtree_min_segments: usize,
107}
108
109impl Default for PolygonBuildOptions {
110 fn default() -> Self {
111 Self {
112 enable_rtree: true,
113 enable_compressed_quad: true,
114 rtree_min_segments: 64,
115 }
116 }
117}
118
119struct RingIndex {
120 x_min: f64,
121 x_max: f64,
122 seg_count: usize,
123 rtree: Option<RTree<2, f64, usize>>,
124 compressed_quad: Option<CompressedQuadIndex>,
125}
126
127impl RingIndex {
128 fn search_candidates(&self, point_y: f64, out: &mut Vec<usize>) {
129 let query_rect = Rect {
130 min: Point {
131 x: self.x_min,
132 y: point_y,
133 },
134 max: Point {
135 x: self.x_max,
136 y: point_y,
137 },
138 };
139
140 let mut seen = vec![false; self.seg_count];
141
142 if let Some(tree) = self.rtree.as_ref() {
143 let query = RTreeRect::new([self.x_min, point_y], [self.x_max, point_y]);
144 for item in tree.search(query) {
145 let idx = *item.data;
146 if idx < self.seg_count && !seen[idx] {
147 seen[idx] = true;
148 out.push(idx);
149 }
150 }
151 }
152
153 if let Some(index) = self.compressed_quad.as_ref() {
154 let mut tmp = Vec::new();
155 index.search_intersects(query_rect, &mut tmp);
156 for idx in tmp {
157 if idx < self.seg_count && !seen[idx] {
158 seen[idx] = true;
159 out.push(idx);
160 }
161 }
162 }
163 }
164}
165
166const Q_MAX_ITEMS: usize = 12;
167const Q_MAX_DEPTH: usize = 64;
168
169#[derive(Default)]
170struct QuadNode {
171 split: bool,
172 items: Vec<usize>,
173 quads: [Option<Box<QuadNode>>; 4],
174}
175
176impl QuadNode {
177 fn new() -> Self {
178 Self {
179 split: false,
180 items: Vec::new(),
181 quads: [None, None, None, None],
182 }
183 }
184}
185
186struct CompressedQuadIndex {
187 bounds: Rect,
188 seg_rects: Vec<Rect>,
189 data: Vec<u8>,
190}
191
192impl CompressedQuadIndex {
193 fn build(ring: &[Point]) -> Option<Self> {
194 let seg_count = ring_segment_count(ring);
195 if seg_count == 0 {
196 return None;
197 }
198
199 let mut min_x = ring[0].x;
200 let mut min_y = ring[0].y;
201 let mut max_x = ring[0].x;
202 let mut max_y = ring[0].y;
203
204 for p in ring.iter().take(seg_count) {
205 if p.x < min_x {
206 min_x = p.x;
207 }
208 if p.y < min_y {
209 min_y = p.y;
210 }
211 if p.x > max_x {
212 max_x = p.x;
213 }
214 if p.y > max_y {
215 max_y = p.y;
216 }
217 }
218
219 let bounds = Rect {
220 min: Point { x: min_x, y: min_y },
221 max: Point { x: max_x, y: max_y },
222 };
223
224 let mut seg_rects = Vec::with_capacity(seg_count);
225 for i in 0..seg_count {
226 seg_rects.push(segment_at_for_slice(ring, i).rect());
227 }
228
229 let mut root = QuadNode::new();
230 for i in 0..seg_rects.len() {
231 insert_quad_node(&mut root, bounds, &seg_rects, i, 0);
232 }
233
234 let mut data = Vec::with_capacity(seg_rects.len() * 2);
235 compress_quad_node(&root, &mut data);
236
237 Some(Self {
238 bounds,
239 seg_rects,
240 data,
241 })
242 }
243
244 fn search_intersects(&self, query: Rect, out: &mut Vec<usize>) {
245 if !self.bounds.intersects_rect(query) {
246 return;
247 }
248 let _ = self.search_intersects_from(0, self.bounds, query, out);
249 }
250
251 fn search_intersects_from(
252 &self,
253 mut addr: usize,
254 bounds: Rect,
255 query: Rect,
256 out: &mut Vec<usize>,
257 ) -> Option<usize> {
258 let (nitems, next_addr) = read_uvarint(&self.data, addr)?;
259 addr = next_addr;
260
261 let mut last: usize = 0;
262 for _ in 0..nitems {
263 let (delta, next_addr) = read_uvarint(&self.data, addr)?;
264 addr = next_addr;
265 last = last.checked_add(delta as usize)?;
266 let seg_rect = self.seg_rects.get(last)?;
267 if seg_rect.intersects_rect(query) {
268 out.push(last);
269 }
270 }
271
272 let split = *self.data.get(addr)?;
273 addr += 1;
274 if split == 0 {
275 return Some(addr);
276 }
277 if split != 1 {
278 return None;
279 }
280
281 for q in 0..4 {
282 let (qsize, next_addr) = read_uvarint(&self.data, addr)?;
283 addr = next_addr;
284 if qsize == 0 {
285 continue;
286 }
287
288 let qsize = usize::try_from(qsize).ok()?;
289 let qbounds = quad_bounds(bounds, q);
290 let child_start = addr;
291 let child_end = child_start.checked_add(qsize)?;
292 if child_end > self.data.len() {
293 return None;
294 }
295
296 if qbounds.intersects_rect(query) {
297 let _ = self.search_intersects_from(child_start, qbounds, query, out)?;
298 }
299 addr = child_end;
300 }
301
302 Some(addr)
303 }
304}
305
306fn ring_segment_count(ring: &[Point]) -> usize {
307 ring.len().saturating_sub(1)
308}
309
310fn segment_at_for_slice(ring: &[Point], index: usize) -> Segment {
311 Segment {
312 a: ring[index],
313 b: ring[index + 1],
314 }
315}
316
317fn build_ring_index(ring: &[Point], options: &PolygonBuildOptions) -> Option<RingIndex> {
318 if !options.enable_rtree && !options.enable_compressed_quad {
319 return None;
320 }
321 if ring.is_empty() {
322 return None;
323 }
324
325 let seg_count = ring_segment_count(ring);
326 if seg_count < options.rtree_min_segments {
327 return None;
328 }
329
330 let mut x_min = ring[0].x;
331 let mut x_max = ring[0].x;
332 for p in ring.iter().take(seg_count) {
333 if p.x < x_min {
334 x_min = p.x;
335 }
336 if p.x > x_max {
337 x_max = p.x;
338 }
339 }
340
341 let rtree = if options.enable_rtree {
342 let mut tree: RTree<2, f64, usize> = RTree::new();
343 for i in 0..seg_count {
344 let seg_rect = segment_at_for_slice(ring, i).rect();
345 tree.insert(
346 RTreeRect::new(
347 [seg_rect.min.x, seg_rect.min.y],
348 [seg_rect.max.x, seg_rect.max.y],
349 ),
350 i,
351 );
352 }
353 Some(tree)
354 } else {
355 None
356 };
357
358 let compressed_quad = if options.enable_compressed_quad {
359 CompressedQuadIndex::build(ring)
360 } else {
361 None
362 };
363
364 if rtree.is_none() && compressed_quad.is_none() {
365 return None;
366 }
367
368 Some(RingIndex {
369 x_min,
370 x_max,
371 seg_count,
372 rtree,
373 compressed_quad,
374 })
375}
376
377fn rings_contains_point(
378 ring: &[Point],
379 ring_index: Option<&RingIndex>,
380 point: Point,
381 allow_on_edge: bool,
382) -> bool {
383 let mut inside: bool = false;
384
385 if let Some(index) = ring_index {
386 let mut candidates = Vec::new();
387 index.search_candidates(point.y, &mut candidates);
388 for i in candidates {
389 let seg = segment_at_for_slice(ring, i);
390 let res: RaycastResult = raycast(&seg, point);
391 if res.on {
392 inside = allow_on_edge;
393 break;
394 }
395 if res.inside {
396 inside = !inside;
397 }
398 }
399 return inside;
400 }
401
402 let ray_rect = Rect {
403 min: Point {
404 x: std::f64::NEG_INFINITY,
405 y: point.y,
406 },
407 max: Point {
408 x: std::f64::INFINITY,
409 y: point.y,
410 },
411 };
412
413 for pair in ring.windows(2) {
414 let seg = Segment {
415 a: pair[0],
416 b: pair[1],
417 };
418
419 if seg.rect().intersects_rect(ray_rect) {
420 let res: RaycastResult = raycast(&seg, point);
421 if res.on {
422 inside = allow_on_edge;
423 break;
424 }
425 if res.inside {
426 inside = !inside;
427 }
428 }
429 }
430
431 return inside;
432}
433
434fn choose_quad(bounds: Rect, rect: Rect) -> Option<usize> {
435 let mid_x = (bounds.min.x + bounds.max.x) / 2.0;
436 let mid_y = (bounds.min.y + bounds.max.y) / 2.0;
437
438 if rect.max.x < mid_x {
439 if rect.max.y < mid_y {
440 return Some(2);
441 }
442 if rect.min.y < mid_y {
443 return None;
444 }
445 return Some(0);
446 }
447
448 if rect.min.x < mid_x {
449 return None;
450 }
451
452 if rect.max.y < mid_y {
453 return Some(3);
454 }
455 if rect.min.y < mid_y {
456 return None;
457 }
458 Some(1)
459}
460
461fn quad_bounds(mut bounds: Rect, q: usize) -> Rect {
462 let center_x = (bounds.min.x + bounds.max.x) / 2.0;
463 let center_y = (bounds.min.y + bounds.max.y) / 2.0;
464
465 match q {
466 0 => {
467 bounds.min.y = center_y;
468 bounds.max.x = center_x;
469 }
470 1 => {
471 bounds.min.x = center_x;
472 bounds.min.y = center_y;
473 }
474 2 => {
475 bounds.max.x = center_x;
476 bounds.max.y = center_y;
477 }
478 3 => {
479 bounds.min.x = center_x;
480 bounds.max.y = center_y;
481 }
482 _ => {}
483 }
484 bounds
485}
486
487fn insert_quad_node(
488 node: &mut QuadNode,
489 bounds: Rect,
490 seg_rects: &[Rect],
491 item: usize,
492 depth: usize,
493) {
494 if depth == Q_MAX_DEPTH {
495 node.items.push(item);
496 return;
497 }
498
499 let item_rect = seg_rects[item];
500 if node.split {
501 if let Some(q) = choose_quad(bounds, item_rect) {
502 let qbounds = quad_bounds(bounds, q);
503 if node.quads[q].is_none() {
504 node.quads[q] = Some(Box::new(QuadNode::new()));
505 }
506 if let Some(quad) = node.quads[q].as_deref_mut() {
507 insert_quad_node(quad, qbounds, seg_rects, item, depth + 1);
508 }
509 } else {
510 node.items.push(item);
511 }
512 return;
513 }
514
515 if node.items.len() == Q_MAX_ITEMS {
516 let existing = std::mem::take(&mut node.items);
517 node.split = true;
518 for i in existing {
519 let rect = seg_rects[i];
520 if let Some(q) = choose_quad(bounds, rect) {
521 let qbounds = quad_bounds(bounds, q);
522 if node.quads[q].is_none() {
523 node.quads[q] = Some(Box::new(QuadNode::new()));
524 }
525 if let Some(quad) = node.quads[q].as_deref_mut() {
526 insert_quad_node(quad, qbounds, seg_rects, i, depth + 1);
527 }
528 } else {
529 node.items.push(i);
530 }
531 }
532 insert_quad_node(node, bounds, seg_rects, item, depth);
533 return;
534 }
535
536 node.items.push(item);
537}
538
539fn append_uvarint(dst: &mut Vec<u8>, mut x: u64) {
540 while x >= 0x80 {
541 dst.push((x as u8 & 0x7f) | 0x80);
542 x >>= 7;
543 }
544 dst.push(x as u8);
545}
546
547fn read_uvarint(data: &[u8], mut addr: usize) -> Option<(u64, usize)> {
548 let mut x: u64 = 0;
549 let mut shift = 0;
550
551 loop {
552 let b = *data.get(addr)?;
553 addr += 1;
554
555 if shift == 70 {
556 return None;
557 }
558
559 x |= ((b & 0x7f) as u64) << shift;
560 if b < 0x80 {
561 return Some((x, addr));
562 }
563 shift += 7;
564 }
565}
566
567fn compress_quad_node(node: &QuadNode, dst: &mut Vec<u8>) {
568 let mut items = node.items.clone();
569 items.sort_unstable();
570
571 append_uvarint(dst, items.len() as u64);
572 let mut last = 0usize;
573 for item in items {
574 append_uvarint(dst, (item - last) as u64);
575 last = item;
576 }
577
578 if !node.split {
579 dst.push(0);
580 return;
581 }
582
583 dst.push(1);
584 for q in 0..4 {
585 if let Some(child) = node.quads[q].as_deref() {
586 let mut child_bytes = Vec::new();
587 compress_quad_node(child, &mut child_bytes);
588 append_uvarint(dst, child_bytes.len() as u64);
589 dst.extend_from_slice(&child_bytes);
590 } else {
591 append_uvarint(dst, 0);
592 }
593 }
594}
595
596pub struct Polygon {
597 exterior: Vec<Point>,
598 holes: Vec<Vec<Point>>,
599 rect: Rect,
600 options: PolygonBuildOptions,
601 exterior_index: Option<RingIndex>,
602 hole_indexes: Vec<Option<RingIndex>>,
603}
604
605impl Polygon {
606 fn compute_rect(exterior: &[Point]) -> Rect {
607 let mut minx: f64 = exterior[0].x;
608 let mut miny: f64 = exterior[0].y;
609 let mut maxx: f64 = exterior[0].x;
610 let mut maxy: f64 = exterior[0].y;
611
612 for p in exterior.iter() {
613 if p.x < minx {
614 minx = p.x;
615 }
616 if p.y < miny {
617 miny = p.y;
618 }
619 if p.x > maxx {
620 maxx = p.x;
621 }
622 if p.y > maxy {
623 maxy = p.y;
624 }
625 }
626
627 Rect {
628 min: Point { x: minx, y: miny },
629 max: Point { x: maxx, y: maxy },
630 }
631 }
632
633 fn rebuild_cache(&mut self) {
634 self.rect = Self::compute_rect(&self.exterior);
635 self.exterior_index = build_ring_index(&self.exterior, &self.options);
636
637 self.hole_indexes = self
638 .holes
639 .iter()
640 .map(|hole| build_ring_index(hole, &self.options))
641 .collect();
642 }
643
644 fn contains_point_normal(&self, p: Point) -> bool {
649 if !rings_contains_point(&self.exterior, self.exterior_index.as_ref(), p, false) {
650 return false;
651 }
652
653 for (hole, hole_index) in self.holes.iter().zip(self.hole_indexes.iter()) {
654 if rings_contains_point(hole, hole_index.as_ref(), p, false) {
655 return false;
656 }
657 }
658
659 return true;
660 }
661
662 pub fn contains_point(&self, p: Point) -> bool {
664 if !self.rect.contains_point(p) {
665 return false;
666 }
667
668 return self.contains_point_normal(p);
669 }
670
671 pub fn new(
719 exterior: Vec<Point>,
720 holes: Vec<Vec<Point>>,
721 options: Option<PolygonBuildOptions>,
722 ) -> Polygon {
723 let mut poly = Polygon {
724 exterior,
725 holes,
726 rect: Rect {
727 min: Point { x: 0.0, y: 0.0 },
728 max: Point { x: 0.0, y: 0.0 },
729 },
730 options: options.unwrap_or_default(),
731 exterior_index: None,
732 hole_indexes: Vec::new(),
733 };
734 poly.rebuild_cache();
735 poly
736 }
737
738 pub fn exterior(&self) -> &[Point] {
739 &self.exterior
740 }
741
742 pub fn holes(&self) -> &[Vec<Point>] {
743 &self.holes
744 }
745
746 pub fn rect(&self) -> Rect {
747 self.rect
748 }
749
750 pub fn options(&self) -> PolygonBuildOptions {
751 self.options
752 }
753
754 pub fn set_exterior(&mut self, exterior: Vec<Point>) {
755 self.exterior = exterior;
756 self.rebuild_cache();
757 }
758
759 pub fn set_holes(&mut self, holes: Vec<Vec<Point>>) {
760 self.holes = holes;
761 self.rebuild_cache();
762 }
763
764 pub fn set_options(&mut self, options: PolygonBuildOptions) {
765 self.options = options;
766 self.rebuild_cache();
767 }
768}
769
770#[derive(Copy, Clone, Debug)]
771pub struct Segment {
772 pub a: Point,
773 pub b: Point,
774}
775
776impl Segment {
777 pub fn rect(&self) -> Rect {
778 let mut min_x: f64 = self.a.x;
779 let mut min_y: f64 = self.a.y;
780 let mut max_x: f64 = self.b.x;
781 let mut max_y: f64 = self.b.y;
782
783 if min_x > max_x {
784 let actual_min_x = max_x;
785 let actual_max_x = min_x;
786 min_x = actual_min_x;
787 max_x = actual_max_x;
788 }
789
790 if min_y > max_y {
791 let actual_min_y = max_y;
792 let actual_max_y = min_y;
793 min_y = actual_min_y;
794 max_y = actual_max_y;
795 }
796
797 return Rect {
798 min: Point { x: min_x, y: min_y },
799 max: Point { x: max_x, y: max_y },
800 };
801 }
802}
803
804pub struct RaycastResult {
805 inside: bool, on: bool, }
808
809pub fn raycast(seg: &Segment, point: Point) -> RaycastResult {
810 let mut p = point;
811 let a = seg.a;
812 let b = seg.b;
813
814 if a.y < b.y && (p.y < a.y || p.y > b.y) {
816 return RaycastResult {
817 inside: false,
818 on: false,
819 };
820 } else if a.y > b.y && (p.y < b.y || p.y > a.y) {
821 return RaycastResult {
822 inside: false,
823 on: false,
824 };
825 }
826
827 if a.y == b.y {
829 if a.x == b.x {
830 if p.x == a.x && p.y == a.y {
831 return RaycastResult {
832 inside: false,
833 on: true,
834 };
835 }
836 return RaycastResult {
837 inside: false,
838 on: false,
839 };
840 }
841 if p.y == b.y {
842 if a.x < b.x {
845 if p.x >= a.x && p.x <= b.x {
846 return RaycastResult {
847 inside: false,
848 on: true,
849 };
850 }
851 } else if p.x >= b.x && p.x <= a.x {
852 return RaycastResult {
853 inside: false,
854 on: true,
855 };
856 }
857 }
858 }
859 if a.x == b.x && p.x == b.x {
860 if a.y < b.y {
863 if p.y >= a.y && p.y <= b.y {
864 return RaycastResult {
865 inside: false,
866 on: true,
867 };
868 }
869 } else if p.y >= b.y && p.y <= a.y {
870 return RaycastResult {
871 inside: false,
872 on: true,
873 };
874 }
875 }
876 if (p.x - a.x) / (b.x - a.x) == (p.y - a.y) / (b.y - a.y) {
877 return RaycastResult {
878 inside: false,
879 on: true,
880 };
881 }
882
883 while p.y == a.y || p.y == b.y {
885 p.y = p.y.next_after(std::f64::INFINITY);
886 }
887
888 if a.y < b.y {
889 if p.y < a.y || p.y > b.y {
890 return RaycastResult {
891 inside: false,
892 on: false,
893 };
894 }
895 } else if p.y < b.y || p.y > a.y {
896 return RaycastResult {
897 inside: false,
898 on: false,
899 };
900 }
901 if a.x > b.x {
902 if p.x >= a.x {
903 return RaycastResult {
904 inside: false,
905 on: false,
906 };
907 }
908 if p.x <= b.x {
909 return RaycastResult {
910 inside: true,
911 on: false,
912 };
913 }
914 } else {
915 if p.x >= b.x {
916 return RaycastResult {
917 inside: false,
918 on: false,
919 };
920 }
921 if p.x <= a.x {
922 return RaycastResult {
923 inside: true,
924 on: false,
925 };
926 }
927 }
928 if a.y < b.y {
929 if (p.y - a.y) / (p.x - a.x) >= (b.y - a.y) / (b.x - a.x) {
930 return RaycastResult {
931 inside: true,
932 on: false,
933 };
934 }
935 } else if (p.y - b.y) / (p.x - b.x) >= (a.y - b.y) / (a.x - b.x) {
936 return RaycastResult {
937 inside: true,
938 on: false,
939 };
940 }
941 return RaycastResult {
942 inside: false,
943 on: false,
944 };
945}
946
947#[cfg(test)]
948mod tests {
949 use super::*;
950
951 fn square(min: f64, max: f64) -> Vec<Point> {
952 vec![
953 Point { x: min, y: min },
954 Point { x: min, y: max },
955 Point { x: max, y: max },
956 Point { x: max, y: min },
957 Point { x: min, y: min },
958 ]
959 }
960
961 fn polygon_with_segments(segments: usize) -> Vec<Point> {
962 let mut ring = Vec::with_capacity(segments + 1);
963 for i in 0..segments {
964 let theta = (i as f64) / (segments as f64) * std::f64::consts::TAU;
965 ring.push(Point {
966 x: theta.cos(),
967 y: theta.sin(),
968 });
969 }
970 ring.push(ring[0]);
971 ring
972 }
973
974 fn scan_candidates(ring: &[Point], y: f64, x_min: f64, x_max: f64) -> Vec<usize> {
975 let mut out = Vec::new();
976 let query = Rect {
977 min: Point { x: x_min, y },
978 max: Point { x: x_max, y },
979 };
980 for (i, pair) in ring.windows(2).enumerate() {
981 let seg = Segment {
982 a: pair[0],
983 b: pair[1],
984 };
985 if seg.rect().intersects_rect(query) {
986 out.push(i);
987 }
988 }
989 out
990 }
991
992 #[test]
993 fn rings_contains_point_allow_on_edge() {
994 let ring = square(0.0, 10.0);
995 let on_edge = Point { x: 0.0, y: 5.0 };
996 assert!(rings_contains_point(&ring, None, on_edge, true));
997 assert!(!rings_contains_point(&ring, None, on_edge, false));
998 }
999
1000 #[test]
1001 fn polygon_contains_basic_in_and_out() {
1002 let poly = Polygon::new(square(0.0, 10.0), vec![], None);
1003 assert!(poly.contains_point(Point { x: 5.0, y: 5.0 }));
1004 assert!(!poly.contains_point(Point { x: 20.0, y: 5.0 }));
1005 }
1006
1007 #[test]
1008 fn polygon_contains_with_hole() {
1009 let poly = Polygon::new(square(0.0, 10.0), vec![square(3.0, 7.0)], None);
1010 assert!(poly.contains_point(Point { x: 1.0, y: 1.0 }));
1011 assert!(!poly.contains_point(Point { x: 5.0, y: 5.0 }));
1012 }
1013
1014 #[test]
1015 fn indexed_and_non_indexed_results_match() {
1016 let ring = polygon_with_segments(128);
1017 let p_in = Point { x: 0.2, y: 0.1 };
1018 let p_out = Point { x: 2.0, y: 0.0 };
1019
1020 let p1 = Polygon::new(
1021 ring.clone(),
1022 vec![],
1023 Some(PolygonBuildOptions {
1024 enable_rtree: false,
1025 enable_compressed_quad: false,
1026 rtree_min_segments: 64,
1027 }),
1028 );
1029
1030 let p2 = Polygon::new(
1031 ring,
1032 vec![],
1033 Some(PolygonBuildOptions {
1034 enable_rtree: true,
1035 enable_compressed_quad: true,
1036 rtree_min_segments: 64,
1037 }),
1038 );
1039
1040 assert_eq!(p1.contains_point(p_in), p2.contains_point(p_in));
1041 assert_eq!(p1.contains_point(p_out), p2.contains_point(p_out));
1042 }
1043
1044 #[test]
1045 fn rtree_and_compressed_quad_and_both_match_baseline() {
1046 let ring = polygon_with_segments(160);
1047 let points = [
1048 Point { x: 0.2, y: 0.1 },
1049 Point { x: -0.4, y: -0.3 },
1050 Point { x: 1.2, y: 0.0 },
1051 ];
1052
1053 let base = Polygon::new(
1054 ring.clone(),
1055 vec![],
1056 Some(PolygonBuildOptions {
1057 enable_rtree: false,
1058 enable_compressed_quad: false,
1059 rtree_min_segments: 64,
1060 }),
1061 );
1062 let only_rtree = Polygon::new(
1063 ring.clone(),
1064 vec![],
1065 Some(PolygonBuildOptions {
1066 enable_rtree: true,
1067 enable_compressed_quad: false,
1068 rtree_min_segments: 64,
1069 }),
1070 );
1071 let only_compressed = Polygon::new(
1072 ring.clone(),
1073 vec![],
1074 Some(PolygonBuildOptions {
1075 enable_rtree: false,
1076 enable_compressed_quad: true,
1077 rtree_min_segments: 64,
1078 }),
1079 );
1080 let both = Polygon::new(
1081 ring,
1082 vec![],
1083 Some(PolygonBuildOptions {
1084 enable_rtree: true,
1085 enable_compressed_quad: true,
1086 rtree_min_segments: 64,
1087 }),
1088 );
1089
1090 for p in points {
1091 let expected = base.contains_point(p);
1092 assert_eq!(only_rtree.contains_point(p), expected);
1093 assert_eq!(only_compressed.contains_point(p), expected);
1094 assert_eq!(both.contains_point(p), expected);
1095 }
1096 }
1097
1098 #[test]
1099 fn threshold_boundaries_63_64_65_are_consistent() {
1100 let ring = polygon_with_segments(65);
1101 let p_in = Point { x: 0.2, y: 0.0 };
1102 let p_out = Point { x: 1.5, y: 0.0 };
1103
1104 let p63 = Polygon::new(
1105 ring.clone(),
1106 vec![],
1107 Some(PolygonBuildOptions {
1108 enable_rtree: true,
1109 enable_compressed_quad: true,
1110 rtree_min_segments: 63,
1111 }),
1112 );
1113 let p64 = Polygon::new(
1114 ring.clone(),
1115 vec![],
1116 Some(PolygonBuildOptions {
1117 enable_rtree: true,
1118 enable_compressed_quad: true,
1119 rtree_min_segments: 64,
1120 }),
1121 );
1122 let p65 = Polygon::new(
1123 ring,
1124 vec![],
1125 Some(PolygonBuildOptions {
1126 enable_rtree: true,
1127 enable_compressed_quad: true,
1128 rtree_min_segments: 65,
1129 }),
1130 );
1131
1132 assert_eq!(p63.contains_point(p_in), p64.contains_point(p_in));
1133 assert_eq!(p64.contains_point(p_in), p65.contains_point(p_in));
1134
1135 assert_eq!(p63.contains_point(p_out), p64.contains_point(p_out));
1136 assert_eq!(p64.contains_point(p_out), p65.contains_point(p_out));
1137 }
1138
1139 #[test]
1140 fn setters_rebuild_cache_and_keep_correct_results() {
1141 let mut poly = Polygon::new(square(0.0, 10.0), vec![], None);
1142 assert!(poly.contains_point(Point { x: 1.0, y: 1.0 }));
1143
1144 poly.set_exterior(square(20.0, 30.0));
1145 assert!(!poly.contains_point(Point { x: 1.0, y: 1.0 }));
1146 assert!(poly.contains_point(Point { x: 21.0, y: 21.0 }));
1147
1148 poly.set_holes(vec![square(22.0, 24.0)]);
1149 assert!(!poly.contains_point(Point { x: 23.0, y: 23.0 }));
1150
1151 poly.set_options(PolygonBuildOptions {
1152 enable_rtree: false,
1153 enable_compressed_quad: false,
1154 rtree_min_segments: 64,
1155 });
1156 assert!(!poly.contains_point(Point { x: 23.0, y: 23.0 }));
1157 assert!(poly.contains_point(Point { x: 25.0, y: 25.0 }));
1158 }
1159
1160 #[test]
1161 fn compressed_quad_candidates_match_scan() {
1162 let ring = polygon_with_segments(256);
1163 let options = PolygonBuildOptions {
1164 enable_rtree: false,
1165 enable_compressed_quad: true,
1166 rtree_min_segments: 64,
1167 };
1168 let index = build_ring_index(&ring, &options).unwrap();
1169
1170 for y in [-1.2, -1.0, -0.75, -0.2, 0.0, 0.4, 0.99, 1.0, 1.2] {
1171 let mut from_index = Vec::new();
1172 index.search_candidates(y, &mut from_index);
1173 from_index.sort_unstable();
1174
1175 let mut from_scan = scan_candidates(&ring, y, index.x_min, index.x_max);
1176 from_scan.sort_unstable();
1177
1178 assert_eq!(from_index, from_scan);
1179 }
1180 }
1181}