const PIP_EPSILON: f64 = 1e-10;
pub fn bbox(ring: &[(f64, f64)]) -> (f64, f64, f64, f64) {
let mut min_lat = f64::MAX;
let mut max_lat = f64::MIN;
let mut min_lon = f64::MAX;
let mut max_lon = f64::MIN;
for &(lat, lon) in ring {
min_lat = min_lat.min(lat);
max_lat = max_lat.max(lat);
min_lon = min_lon.min(lon);
max_lon = max_lon.max(lon);
}
(min_lat, max_lat, min_lon, max_lon)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PipResult {
Inside,
Outside,
OnBoundary,
}
fn point_on_segment(px: f64, py: f64, x1: f64, y1: f64, x2: f64, y2: f64) -> bool {
let cross = (py - y1) * (x2 - x1) - (px - x1) * (y2 - y1);
if cross.abs() > PIP_EPSILON {
return false;
}
let (min_x, max_x) = if x1 < x2 { (x1, x2) } else { (x2, x1) };
let (min_y, max_y) = if y1 < y2 { (y1, y2) } else { (y2, y1) };
px >= min_x - PIP_EPSILON
&& px <= max_x + PIP_EPSILON
&& py >= min_y - PIP_EPSILON
&& py <= max_y + PIP_EPSILON
}
fn ray_cast(px: f64, py: f64, ring: &[(f64, f64)]) -> bool {
let mut inside = false;
let n = ring.len();
for i in 0..n {
let (x1, y1) = ring[i];
let (x2, y2) = ring[(i + 1) % n];
if (y1 > py) != (y2 > py) && px < (x2 - x1) * (py - y1) / (y2 - y1) + x1 {
inside = !inside;
}
}
inside
}
pub fn point_in_ring(px: f64, py: f64, ring: &[(f64, f64)]) -> PipResult {
let n = ring.len();
if n < 3 {
return PipResult::Outside;
}
for i in 0..n {
let (x1, y1) = ring[i];
let (x2, y2) = ring[(i + 1) % n];
if point_on_segment(px, py, x1, y1, x2, y2) {
return PipResult::OnBoundary;
}
}
if ray_cast(px, py, ring) {
PipResult::Inside
} else {
PipResult::Outside
}
}
pub fn point_in_polygon(
px: f64,
py: f64,
exterior: &[(f64, f64)],
interiors: &[&[(f64, f64)]],
) -> bool {
match point_in_ring(px, py, exterior) {
PipResult::Outside => return false,
PipResult::OnBoundary => return true,
PipResult::Inside => {}
}
for hole in interiors {
match point_in_ring(px, py, hole) {
PipResult::Outside => {}
PipResult::OnBoundary | PipResult::Inside => return false,
}
}
true
}
pub fn serialize_vertices(ring: &[(f64, f64)]) -> Vec<u8> {
let mut buf = Vec::with_capacity(ring.len() * 16);
for &(lat, lon) in ring {
buf.extend_from_slice(&lat.to_le_bytes());
buf.extend_from_slice(&lon.to_le_bytes());
}
buf
}
pub fn deserialize_vertices(blob: &[u8]) -> Vec<(f64, f64)> {
let n = blob.len() / 16;
let mut ring = Vec::with_capacity(n);
for i in 0..n {
let lat = f64::from_le_bytes(blob[i * 16..i * 16 + 8].try_into().unwrap());
let lon = f64::from_le_bytes(blob[i * 16 + 8..i * 16 + 16].try_into().unwrap());
ring.push((lat, lon));
}
ring
}