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