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, 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        // Segment indices and stripe offsets are stored as u32.
252        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            // Recompute the segment's y range from its endpoints instead of
308            // storing it; the endpoints are about to be fetched for the
309            // raycast anyway.
310            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    /// Point-In-Polygon check, the normal way.
883    /// It's most used algorithm implementation, port from Go's [geojson]
884    ///
885    /// [geojson]: https://github.com/tidwall/geojson
886    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    /// Do point-in-polygon search.
901    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    /// Create a new Polygon instance from exterior and holes.
910    ///
911    /// Example:
912    ///
913    /// ```rust
914    /// use std::vec;
915    /// use geometry_rs;
916    /// let poly = geometry_rs::Polygon::new(
917    ///     vec![
918    ///         geometry_rs::Point {
919    ///             x: 90.48826291293898,
920    ///             y: 45.951129815858565,
921    ///         },
922    ///         geometry_rs::Point {
923    ///             x: 90.48826291293898,
924    ///             y: 27.99437617512571,
925    ///         },
926    ///         geometry_rs::Point {
927    ///             x: 122.83201291294,
928    ///             y: 27.99437617512571,
929    ///         },
930    ///         geometry_rs::Point {
931    ///             x: 122.83201291294,
932    ///             y: 45.951129815858565,
933    ///         },
934    ///         geometry_rs::Point {
935    ///             x: 90.48826291293898,
936    ///             y: 45.951129815858565,
937    ///         },
938    ///     ],
939    ///     vec![],
940    ///     None,
941    /// );
942    ///
943    /// let p_out = geometry_rs::Point {
944    ///     x: 130.74216916294148,
945    ///     y: 37.649011392900306,
946    /// };
947    ///
948    /// print!("{:?}\n", poly.contains_point(p_out));
949    ///
950    /// let p_in = geometry_rs::Point {
951    ///     x: 99.9804504129416,
952    ///     y: 39.70716466970461,
953    /// };
954    /// print!("{:?}\n", poly.contains_point(p_in));
955    /// ```
956    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, // point on the left
1049    on: bool,     // point is directly on top of
1050}
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    // make sure that the point is inside the segment bounds
1058    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    // test if point is in on the segment
1071    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            // horizontal segment
1086            // check if the point in on the line
1087            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        // vertical segment
1104        // check if the point in on the line
1105        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    // do the actual raycast here.
1127    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}