skymask_rs/
lib.rs

1#![doc = include_str!("../README.md")]
2pub mod data;
3pub mod utils;
4pub use data::read_shp;
5use num_traits::{Float, FloatConst};
6pub use rangemap::RangeMap;
7use std::cmp::{max, min, Ordering::*};
8use std::collections::BinaryHeap;
9use std::fmt::Debug;
10use utils::float_cmp;
11pub use utils::{ProjLine, ProjSegment};
12
13/// Computes the skymask from a set of projected segments.  
14/// This function takes an iterator of projected segments and an epsilon value for floating-point comparisons.  
15/// It returns a `RangeMap` that represents the skymask with dom `[-pi, pi)`.
16/// ## Type Parameters
17/// - `T`: The numeric type used for coordinates.
18/// - `LT`: The line type used for projections.
19/// ## Parameters
20/// - `lines`: An iterator over `ProjSegment<T, LT>` items representing the projected segments.
21/// - `eps`: A tolerance value for floating-point comparisons.
22/// ## Returns
23/// A `RangeMap<T, LT>` representing the skymask with dom `[-pi, pi)`.
24pub fn skymask<T, LT>(lines: impl Iterator<Item = ProjSegment<T, LT>>, eps: T) -> RangeMap<T, LT>
25where
26    T: Copy + Float + FloatConst + Ord + Debug,
27    LT: ProjLine<T> + Debug,
28{
29    let mut heap = BinaryHeap::from_iter(lines);
30    let mut rmap: RangeMap<T, LT> = RangeMap::new();
31    let (mut lower, mut fill_all) = (T::infinity(), false);
32
33    while let Some(l) = heap.pop() {
34        if fill_all {
35            if l.top_endpoint() < lower {
36                break;
37            }
38        } else {
39            if rmap.gaps(&(-T::PI()..T::PI())).next().is_none() {
40                fill_all = true;
41            }
42        }
43        let doms = [l.dom, (l.dom.0, T::PI()), (-T::PI(), l.dom.1)];
44        for &(dom_s, dom_e) in &doms[if l.dom.0 < l.dom.1 { 0..1 } else { 1..3 }] {
45            let mut updates = vec![];
46            for (seg_dom, seg_func) in rmap.overlapping(dom_s..dom_e) {
47                let dom = max(dom_s, seg_dom.start)..min(dom_e, seg_dom.end);
48                match (
49                    float_cmp(l.line.at(dom.start), seg_func.at(dom.start), eps),
50                    float_cmp(l.line.at(dom.end), seg_func.at(dom.end), eps),
51                ) {
52                    (Less | Equal, Less | Equal) => {}
53                    (Greater | Equal, Greater | Equal) => {
54                        updates.push((dom, l.line));
55                    }
56                    (Less, Greater) => {
57                        let cross = l.line.cross_point(seg_func, &dom).unwrap();
58                        updates.push((cross..dom.end, l.line));
59                    }
60                    (Greater, Less) => {
61                        let cross = l.line.cross_point(seg_func, &dom).unwrap();
62                        updates.push((dom.start..cross, l.line));
63                    }
64                }
65            }
66            if !fill_all {
67                for gap in rmap.gaps(&(dom_s..dom_e)) {
68                    updates.push((gap, l.line));
69                }
70            }
71            updates.into_iter().for_each(|(dom, func)| {
72                lower = min(lower, min(func.at(dom.start), func.at(dom.end)));
73                rmap.insert(dom, func);
74            });
75        }
76    }
77    rmap
78}