use crate::{
data_structures::{BoxIndex, BoxIndexAccessor},
geometry::{IntersectionOfSegmentsRobust, intersection_of_segments_robust},
};
use alloc::{collections::BTreeMap, rc::Rc, vec, vec::Vec};
use core::{cell::RefCell, cmp::Ordering};
use libm::{fmax, fmin};
use s2json::{BBox, FullXY, GetXY, NewXY, Point};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Segment {
pub id: usize,
pub poly_index: usize,
pub ring_index: usize,
pub from: usize,
pub to: usize,
pub bbox: BBox,
}
impl BoxIndexAccessor for Segment {
fn bbox(&self) -> BBox {
self.bbox
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Intersection {
pub segment1: Segment,
pub segment2: Segment,
pub point: Point,
pub u: f64,
pub t: f64,
}
#[derive(Debug, Clone, PartialEq)]
pub struct RingIntersection {
pub from: usize,
pub to: usize,
pub point: Rc<RefCell<Point>>,
pub t: f64,
pub t_vec: Point,
pub t_angle: f64,
}
impl RingIntersection {
pub fn new<P: FullXY>(
from: usize,
to: usize,
point: Rc<RefCell<Point>>,
t: f64,
t_vec: &P,
t_angle: f64,
) -> Self {
RingIntersection { from, to, point, t, t_vec: t_vec.into(), t_angle }
}
}
pub type RingIntersectionLookup = BTreeMap<usize, BTreeMap<usize, Vec<RingIntersection>>>;
pub fn polygons_intersections<P: GetXY + PartialEq>(
vector_polygons: &[Vec<Vec<P>>],
include_self_intersections: bool,
) -> Vec<Intersection> {
let mut res: Vec<Intersection> = vec![];
let segments = build_polygon_segments(vector_polygons);
let box_index = BoxIndex::new(segments.clone(), None);
for segment1 in segments {
let potential_intersections = box_index.search(
&segment1.bbox(),
Some(|seg: &Segment| {
seg.id != segment1.id
&& seg.id > segment1.id
&& (if !include_self_intersections {
seg.poly_index != segment1.poly_index
} else {
seg.ring_index != segment1.ring_index
|| (seg.from != segment1.from
&& seg.to != segment1.to
&& seg.to != segment1.from
&& seg.from != segment1.to)
})
}),
);
for segment2 in potential_intersections {
if let Some(int_point) =
find_polygon_intersection(vector_polygons, &segment1, &segment2)
{
let IntersectionOfSegmentsRobust { point, u, t, .. } = int_point;
res.push(Intersection { segment1, segment2, point, u, t });
}
}
}
res
}
pub fn build_polygon_segments<P: GetXY>(vector_polygons: &[Vec<Vec<P>>]) -> Vec<Segment> {
let mut segments = vec![];
for (p, polygon) in vector_polygons.iter().enumerate() {
for (r, ring) in polygon.iter().enumerate() {
for s in 0..ring.len() - 1 {
let from = &ring[s];
let to = &ring[s + 1];
segments.push(Segment {
id: segments.len(),
poly_index: p,
ring_index: r,
from: s,
to: s + 1,
bbox: BBox::new(
fmin(from.x(), to.x()),
fmin(from.y(), to.y()),
fmax(from.x(), to.x()),
fmax(from.y(), to.y()),
),
});
}
}
}
segments
}
pub fn polygons_intersections_lookup<P: FullXY>(
vector_polygons: &[Vec<Vec<P>>],
segment_filter: Option<impl Fn(&Segment, &Segment) -> bool>,
) -> RingIntersectionLookup {
let segments = build_polygon_segments(vector_polygons);
let mut ring_intersect_lookup: RingIntersectionLookup = BTreeMap::new();
let box_index = BoxIndex::new(segments.clone(), None);
for seg1 in segments {
let potential_intersections = box_index.search(
&seg1.bbox(),
Some(|seg2: &Segment| {
segment_filter.as_ref().map(|f| f(&seg1, seg2)).unwrap_or_else(|| {
seg2.id != seg1.id &&
seg2.id > seg1.id &&
seg2.poly_index != seg1.poly_index
})
}),
);
for seg2 in potential_intersections {
let p_int = find_polygon_intersection::<P, P>(vector_polygons, &seg1, &seg2);
if let Some(int) = p_int {
let IntersectionOfSegmentsRobust { u, t, point, u_angle, t_angle, u_vec, t_vec } =
int;
if u == t && (u == 0. || u == 1.) {
continue;
}
let p = Rc::new(RefCell::new((&point).into()));
let s1 = ring_intersect_lookup
.entry(seg1.poly_index)
.or_default()
.entry(seg1.ring_index)
.or_default();
s1.push(RingIntersection::new(seg1.from, seg1.to, p.clone(), u, &u_vec, u_angle));
let s2 = ring_intersect_lookup
.entry(seg2.poly_index)
.or_default()
.entry(seg2.ring_index)
.or_default();
s2.push(RingIntersection::new(seg2.from, seg2.to, p, t, &t_vec, t_angle));
}
}
}
for (_, polys) in ring_intersect_lookup.iter_mut() {
for (_, intersections) in polys.iter_mut() {
*intersections = clean_intersections(intersections);
}
}
ring_intersect_lookup
}
pub fn find_polygon_intersection<P: GetXY + PartialEq, Q: NewXY + Clone>(
vector_polygons: &[Vec<Vec<P>>],
segment1: &Segment,
segment2: &Segment,
) -> Option<IntersectionOfSegmentsRobust<Q>> {
let p1 = &vector_polygons[segment1.poly_index][segment1.ring_index][segment1.from];
let p2 = &vector_polygons[segment1.poly_index][segment1.ring_index][segment1.to];
let q1 = &vector_polygons[segment2.poly_index][segment2.ring_index][segment2.from];
let q2 = &vector_polygons[segment2.poly_index][segment2.ring_index][segment2.to];
intersection_of_segments_robust(
(p1, p2),
(q1, q2),
segment1.poly_index == segment2.poly_index && segment1.ring_index == segment2.ring_index,
)
}
pub fn polygons_intersections_ref<P: GetXY + PartialEq>(
vector_polygons: &[&Vec<Vec<P>>],
include_self_intersections: bool,
) -> Vec<Intersection> {
let mut res: Vec<Intersection> = vec![];
let segments = build_polygon_segments_ref(vector_polygons);
let box_index = BoxIndex::new(segments.clone(), None);
for segment1 in segments {
let potential_intersections = box_index.search(
&segment1.bbox(),
Some(|seg: &Segment| {
seg.id != segment1.id
&& seg.id > segment1.id
&& (if !include_self_intersections {
seg.poly_index != segment1.poly_index
} else {
seg.ring_index != segment1.ring_index
|| (seg.from != segment1.from
&& seg.to != segment1.to
&& seg.to != segment1.from
&& seg.from != segment1.to)
})
}),
);
for segment2 in potential_intersections {
if let Some(int_point) = find_intersection_ref(vector_polygons, &segment1, &segment2) {
res.push(Intersection {
segment1,
segment2,
point: int_point.point,
u: int_point.u,
t: int_point.t,
});
}
}
}
res
}
pub fn build_polygon_segments_ref<P: GetXY>(vector_polygons: &[&Vec<Vec<P>>]) -> Vec<Segment> {
let mut segments = vec![];
for (p, polygon) in vector_polygons.iter().enumerate() {
for (r, ring) in polygon.iter().enumerate() {
for s in 0..ring.len() - 1 {
let from = &ring[s];
let to = &ring[s + 1];
segments.push(Segment {
id: segments.len(),
poly_index: p,
ring_index: r,
from: s,
to: s + 1,
bbox: BBox::new(
fmin(from.x(), to.x()),
fmin(from.y(), to.y()),
fmax(from.x(), to.x()),
fmax(from.y(), to.y()),
),
});
}
}
}
segments
}
fn find_intersection_ref<P: GetXY + PartialEq, Q: NewXY + Clone>(
vector_polygons: &[&Vec<Vec<P>>],
segment1: &Segment,
segment2: &Segment,
) -> Option<IntersectionOfSegmentsRobust<Q>> {
let p1 = &vector_polygons[segment1.poly_index][segment1.ring_index][segment1.from];
let p2 = &vector_polygons[segment1.poly_index][segment1.ring_index][segment1.to];
let q1 = &vector_polygons[segment2.poly_index][segment2.ring_index][segment2.from];
let q2 = &vector_polygons[segment2.poly_index][segment2.ring_index][segment2.to];
intersection_of_segments_robust(
(p1, p2),
(q1, q2),
segment1.poly_index == segment2.poly_index && segment1.ring_index == segment2.ring_index,
)
}
fn clean_intersections(intersections: &mut [RingIntersection]) -> Vec<RingIntersection> {
if intersections.is_empty() {
return vec![];
}
intersections
.sort_by(|a, b| a.from.cmp(&b.from).then(a.t.partial_cmp(&b.t).unwrap_or(Ordering::Equal)));
let mut dedup_ints: Vec<RingIntersection> = vec![];
for int in intersections.iter() {
if dedup_ints.iter().any(|c| c.from == int.from && c.t == int.t && c.point == int.point) {
continue;
}
dedup_ints.push(int.clone());
}
if dedup_ints.len() == 2 {
let first = &dedup_ints[0];
let second = &dedup_ints[1];
if (first.t == 0. || first.t == 1.)
&& (second.t == 0. || second.t == 1.)
&& first.point == second.point
{
return vec![];
}
}
update_intersection_points(&mut dedup_ints);
dedup_ints
}
fn update_intersection_points(intersections: &mut [RingIntersection]) {
let mut starts = vec![];
let mut ends = vec![];
for i in 1..intersections.len() {
let int = &intersections[i];
let prev = &intersections[i - 1];
if int.from != prev.from || int.t == 0. || int.t == 1. || prev.t == 0. || prev.t == 1. {
continue;
}
if i != 0 && int.point == prev.point && int.t != prev.t {
if int.t <= 0.5 {
starts.push(i);
} else {
ends.push(i - 1);
}
}
}
for (i, start) in starts.into_iter().enumerate() {
let RingIntersection { point, t_vec, .. } = &mut intersections[start];
let point = &mut point.borrow_mut();
if t_vec.0 != 0.0 {
(0..=(i + 1)).for_each(|_| {
point.0 = if t_vec.0 > 0.0 { point.0.next_up() } else { point.0.next_down() }
});
}
if t_vec.1 != 0.0 {
(0..=(i + 1)).for_each(|_| {
point.1 = if t_vec.1 > 0.0 { point.1.next_up() } else { point.1.next_down() }
});
}
}
for (i, end) in ends.into_iter().enumerate() {
let RingIntersection { point, t_vec, .. } = &mut intersections[end];
let point = &mut point.borrow_mut();
if t_vec.0 != 0.0 {
(0..=(i + 1)).for_each(|_| {
point.0 = if t_vec.0 < 0.0 { point.0.next_up() } else { point.0.next_down() }
});
}
if t_vec.1 != 0.0 {
(0..=(i + 1)).for_each(|_| {
point.1 = if t_vec.1 < 0.0 { point.1.next_up() } else { point.1.next_down() }
});
}
}
}