Skip to main content

geometry_rs/
lib.rs

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