use std::{
cmp::Ordering,
collections::{BTreeMap, BTreeSet},
fmt::Debug,
};
use crate::{
node::{Node, Role},
Edge, Geometry, IsClose, Shape, Vertex,
};
#[derive(Debug)]
struct EnumeratedEdge<'a, T>
where
T: 'a + Geometry,
{
edge: T::Edge<'a>,
index: usize,
}
struct EnumeratedEdgesIterator<'a, T>
where
T: Geometry,
{
graph: &'a Graph<T>,
next: Option<usize>,
start: usize,
}
impl<'a, T> Iterator for EnumeratedEdgesIterator<'a, T>
where
T: Geometry,
{
type Item = EnumeratedEdge<'a, T>;
fn next(&mut self) -> Option<Self::Item> {
let current = self.next?;
let node = self.graph.nodes[current].as_ref()?;
self.next = (node.next != self.start).then_some(node.next);
Some(EnumeratedEdge {
edge: T::Edge::new(&node.vertex, &self.graph.nodes[node.next].as_ref()?.vertex),
index: current,
})
}
}
#[derive(Debug)]
struct EdgeIntersection<T>
where
T: Geometry,
{
vertex: T::Vertex,
subject: usize,
clip: usize,
}
struct EdgeIntersections<T>
where
T: Geometry,
{
all: Vec<EdgeIntersection<T>>,
by_edge: BTreeMap<usize, Vec<usize>>,
}
impl<T> Default for EdgeIntersections<T>
where
T: Geometry,
{
fn default() -> Self {
Self {
all: Default::default(),
by_edge: Default::default(),
}
}
}
impl<T> EdgeIntersections<T>
where
T: Geometry,
{
fn with_intersection(mut self, intersection: EdgeIntersection<T>) -> Self {
let index = self.all.len();
match self.by_edge.get_mut(&intersection.subject) {
Some(group) => group.push(index),
None => {
self.by_edge.insert(intersection.subject, vec![index]);
}
};
match self.by_edge.get_mut(&intersection.clip) {
Some(group) => group.push(index),
None => {
self.by_edge.insert(intersection.clip, vec![index]);
}
};
self.all.push(intersection);
self
}
}
#[derive(Debug, PartialEq)]
struct PartialOrdKey<T>(T);
impl<T> PartialOrd for PartialOrdKey<T>
where
T: PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T> Eq for PartialOrdKey<T> where T: PartialEq {}
impl<T> Ord for PartialOrdKey<T>
where
T: PartialOrd,
{
fn cmp(&self, other: &Self) -> Ordering {
self.0.partial_cmp(&other.0).unwrap_or(Ordering::Less)
}
}
impl<T> From<T> for PartialOrdKey<T> {
fn from(value: T) -> Self {
Self(value)
}
}
#[derive(Debug)]
struct PartialOrdBTreeMap<K, V>(BTreeMap<PartialOrdKey<K>, V>);
impl<K, V> PartialOrdBTreeMap<K, V>
where
K: PartialOrd,
{
fn insert(&mut self, key: K, value: V) {
self.0.insert(key.into(), value);
}
fn get(&self, key: K) -> Option<&V> {
self.0.get(&key.into())
}
}
impl<K, V> PartialOrdBTreeMap<K, V> {
fn new() -> Self {
Self(BTreeMap::new())
}
}
pub(super) struct Graph<T>
where
T: Geometry,
{
pub(super) nodes: Vec<Option<Node<T>>>,
}
impl<T> Default for Graph<T>
where
T: Geometry,
{
fn default() -> Self {
Self {
nodes: Default::default(),
}
}
}
impl<T> Graph<T>
where
T: Geometry,
{
fn edges(&self, boundary: &Role) -> impl Iterator<Item = EnumeratedEdge<T>> {
let start = match boundary {
Role::Clip(index) | Role::Subject(index) => *index,
};
EnumeratedEdgesIterator {
graph: self,
start,
next: Some(start),
}
}
pub(super) fn position_where(&self, f: impl Fn(&Node<T>) -> bool) -> Option<usize> {
self.nodes
.iter()
.enumerate()
.filter_map(|(index, slot)| slot.as_ref().map(|node| (index, node)))
.find(|(_, node)| f(node))
.map(|(start, _)| start)
}
}
pub(super) struct GraphBuilder<'a, T>
where
T: Geometry,
{
graph: Graph<T>,
boundaries: Vec<Role>,
tolerance: &'a <T::Vertex as IsClose>::Tolerance,
}
impl<'a, T> GraphBuilder<'a, T>
where
T: Geometry,
{
pub(super) fn new(tolerance: &'a <T::Vertex as IsClose>::Tolerance) -> Self {
Self {
graph: Default::default(),
boundaries: Default::default(),
tolerance,
}
}
}
impl<T> GraphBuilder<'_, T>
where
T: Geometry,
T::Vertex: Copy + PartialEq + PartialOrd,
<T::Vertex as Vertex>::Scalar: Copy + PartialOrd,
{
pub(super) fn build(mut self) -> Graph<T> {
let intersections = self.intersections();
let mut visited = PartialOrdBTreeMap::new();
for (edge, mut intersection_indexes) in intersections.by_edge {
let &Node {
vertex: first,
role,
next,
..
} = self.graph.nodes[edge]
.as_ref()
.expect("edge vertex should exist");
let last = self.graph.nodes[next]
.as_ref()
.expect("edge endpoint should exist")
.vertex;
intersection_indexes.sort_by(|&a, &b| {
first
.distance(&intersections.all[a].vertex)
.partial_cmp(&first.distance(&intersections.all[b].vertex))
.unwrap_or(Ordering::Equal)
});
intersection_indexes
.chunk_by(|&a, &b| intersections.all[a].vertex == intersections.all[b].vertex)
.fold(edge, |previous, chunk| {
let intersection_point = intersections.all[chunk[0]].vertex;
let index = if intersection_point == first {
edge
} else if intersection_point == last {
next
} else {
self.graph.nodes.len()
};
visited.insert((edge, intersection_point), index);
let siblings = chunk
.iter()
.map(|&index| {
if edge == intersections.all[index].clip {
intersections.all[index].subject
} else {
intersections.all[index].clip
}
})
.filter_map(|edge| {
visited.get((edge, intersection_point))
})
.copied()
.inspect(|&sibling| {
self.graph.nodes[sibling]
.as_mut()
.expect("sibling should exist")
.siblings
.insert(index);
})
.collect();
if [first, last].contains(&intersection_point) {
self.graph.nodes[index]
.as_mut()
.expect("endpoint node should exists")
.siblings
.extend(siblings);
} else {
let next = self.graph.nodes[previous]
.as_ref()
.expect("previous node should exist")
.next;
self.graph.nodes[previous]
.as_mut()
.expect("previous node should exist")
.next = index;
self.graph.nodes.push(Some(Node {
vertex: intersection_point,
role,
next,
siblings,
}));
}
index
});
}
self.graph
}
}
impl<T> GraphBuilder<'_, T>
where
T: Geometry,
{
fn intersections(&self) -> EdgeIntersections<T> {
let mut intersections = EdgeIntersections::default();
for subject_boundary in self.boundaries.iter().filter(|p| p.is_subject()) {
for clip_boundary in self.boundaries.iter().filter(|p| !p.is_subject()) {
for subject_edge in self.graph.edges(subject_boundary) {
for clip_edge in self.graph.edges(clip_boundary) {
if let Some(intersection) = subject_edge
.edge
.intersection(&clip_edge.edge, self.tolerance)
{
intersections = intersections.with_intersection(EdgeIntersection {
vertex: intersection,
subject: subject_edge.index,
clip: clip_edge.index,
});
}
}
}
}
}
intersections
}
}
impl<T> GraphBuilder<'_, T>
where
T: Geometry + IntoIterator<Item = T::Vertex>,
{
pub(super) fn with_subject(self, shape: Shape<T>) -> Self {
self.with_shape(shape, Role::Subject)
}
pub(super) fn with_clip(self, shape: Shape<T>) -> Self {
self.with_shape(shape, Role::Clip)
}
fn with_shape(mut self, shape: Shape<T>, role: impl Fn(usize) -> Role) -> Self {
self.graph.nodes.reserve(shape.total_vertices());
self.boundaries.reserve(shape.boundaries.len());
for boundary in shape.boundaries {
let offset = self.graph.nodes.len();
let role = role(offset);
self.boundaries.push(role);
let total_vertices = boundary.total_vertices();
for (index, point) in boundary.into_iter().enumerate() {
self.graph.nodes.push(Some(Node {
vertex: point,
role,
next: offset + ((index + 1) % total_vertices),
siblings: BTreeSet::new(),
}));
}
}
self
}
}