use super::super::{CoordNode, Edge, LineIntersection, LineIntersector};
use crate::{Coord, GeoFloat, Line};
use std::cell::{Ref, RefCell};
pub(crate) struct SegmentIntersector<F>
where
F: GeoFloat,
{
line_intersector: Box<dyn LineIntersector<F>>,
edges_are_from_same_geometry: bool,
proper_intersection_point: Option<Coord<F>>,
has_proper_interior_intersection: bool,
boundary_nodes: Option<[Vec<CoordNode<F>>; 2]>,
}
impl<F> SegmentIntersector<F>
where
F: GeoFloat,
{
fn is_adjacent_segments(i1: usize, i2: usize) -> bool {
i1.abs_diff(i2) == 1
}
pub fn new(
line_intersector: Box<dyn LineIntersector<F>>,
edges_are_from_same_geometry: bool,
) -> SegmentIntersector<F> {
SegmentIntersector {
line_intersector,
edges_are_from_same_geometry,
has_proper_interior_intersection: false,
proper_intersection_point: None,
boundary_nodes: None,
}
}
pub fn set_boundary_nodes(
&mut self,
boundary_nodes_0: Vec<CoordNode<F>>,
boundary_nodes_1: Vec<CoordNode<F>>,
) {
debug_assert!(
self.boundary_nodes.is_none(),
"Should only set boundaries between geometries once"
);
self.boundary_nodes = Some([boundary_nodes_0, boundary_nodes_1]);
}
pub fn has_proper_intersection(&self) -> bool {
self.proper_intersection_point.is_some()
}
pub fn has_proper_interior_intersection(&self) -> bool {
self.has_proper_interior_intersection
}
fn is_trivial_intersection(
&self,
intersection: LineIntersection<F>,
edge0: &RefCell<Edge<F>>,
segment_index_0: usize,
edge1: &RefCell<Edge<F>>,
segment_index_1: usize,
) -> bool {
if edge0.as_ptr() != edge1.as_ptr() {
return false;
}
if matches!(intersection, LineIntersection::Collinear { .. }) {
return false;
}
if Self::is_adjacent_segments(segment_index_0, segment_index_1) {
return true;
}
let edge0 = edge0.borrow();
if edge0.is_closed() {
let max_segment_index = edge0.coords().len() - 1;
if (segment_index_0 == 0 && segment_index_1 == max_segment_index)
|| (segment_index_1 == 0 && segment_index_0 == max_segment_index)
{
return true;
}
}
false
}
pub fn add_intersections(
&mut self,
edge0: &RefCell<Edge<F>>,
segment_index_0: usize,
edge1: &RefCell<Edge<F>>,
segment_index_1: usize,
) {
if edge0.as_ptr() == edge1.as_ptr() && segment_index_0 == segment_index_1 {
return;
}
let line_0 = Line::new(
edge0.borrow().coords()[segment_index_0],
edge0.borrow().coords()[segment_index_0 + 1],
);
let line_1 = Line::new(
edge1.borrow().coords()[segment_index_1],
edge1.borrow().coords()[segment_index_1 + 1],
);
let intersection = self.line_intersector.compute_intersection(line_0, line_1);
if intersection.is_none() {
return;
}
let intersection = intersection.unwrap();
if !self.edges_are_from_same_geometry {
edge0.borrow_mut().mark_as_unisolated();
edge1.borrow_mut().mark_as_unisolated();
}
if !self.is_trivial_intersection(
intersection,
edge0,
segment_index_0,
edge1,
segment_index_1,
) {
if self.edges_are_from_same_geometry || !intersection.is_proper() {
edge0
.borrow_mut()
.add_intersections(intersection, line_0, segment_index_0);
edge1
.borrow_mut()
.add_intersections(intersection, line_1, segment_index_1);
}
if let LineIntersection::SinglePoint {
is_proper: true,
intersection: intersection_coord,
} = intersection
{
self.proper_intersection_point = Some(intersection_coord);
if !self.is_boundary_point(&intersection_coord, &self.boundary_nodes) {
self.has_proper_interior_intersection = true
}
}
}
}
fn is_boundary_point(
&self,
intersection: &Coord<F>,
boundary_nodes: &Option<[Vec<CoordNode<F>>; 2]>,
) -> bool {
match &boundary_nodes {
Some(boundary_nodes) => boundary_nodes
.iter()
.flatten()
.any(|node| intersection == node.coordinate()),
None => false,
}
}
}