// SPDX-FileCopyrightText: 2024 Topola contributors // // SPDX-License-Identifier: MIT use std::ops::IndexMut; use std::{cmp::Ordering, marker::PhantomData}; use geo::algorithm::line_measures::{Distance, Euclidean}; use geo::{point, Point}; use petgraph::visit; use spade::{ handles::FixedVertexHandle, ConstrainedDelaunayTriangulation, HasPosition, InsertionError, }; use crate::graph::GetIndex; pub trait GetTrianvertexNodeIndex { fn node_index(&self) -> I; } // No `Debug` derive because `spade::ConstrainedDelaunayTriangulation` does // not implement `Debug`, though it could (and `spade::DelaunayTriangulation` // actually does). #[derive(Clone)] pub struct Triangulation + HasPosition, EW: Default> { cdt: ConstrainedDelaunayTriangulation, trianvertex_to_handle: M, index_marker: PhantomData, } impl< I: GetIndex, M: IndexMut>, VW: GetTrianvertexNodeIndex + HasPosition, EW: Default, > Triangulation { pub fn new(trianvertex_to_handle: M) -> Self { Self { cdt: as spade::Triangulation>::new(), trianvertex_to_handle, index_marker: PhantomData, } } pub fn add_vertex(&mut self, weight: VW) -> Result<(), InsertionError> { let index = weight.node_index(); self.trianvertex_to_handle[index] = Some(spade::Triangulation::insert(&mut self.cdt, weight)?); Ok(()) } pub fn add_constraint_edge(&mut self, from: VW, to: VW) -> Result { let from_index = from.node_index(); let to_index = to.node_index(); // It is possible for one or both constraint edge endpoint vertices to // not exist in the triangulation even after everything has been added. // This can happen if the constraint was formed from a band wrapped // over a polygonal pad that is the routing origin or destination, since // in such situation the vertices of the pad boundary are not added to // the triangulation. // // To prevent this from causing a panic at runtime, we idempotently add // the constraint edge endpoint vertices to triangulation before adding // the edge itself. self.add_vertex(from)?; self.add_vertex(to)?; Ok(self.cdt.add_constraint( self.trianvertex_to_handle[from_index].unwrap(), self.trianvertex_to_handle[to_index].unwrap(), )) } pub fn intersects_constraint(&self, from: &VW, to: &VW) -> bool { self.cdt .intersects_constraint(from.position(), to.position()) } pub fn weight(&self, vertex: I) -> &VW { spade::Triangulation::s(&self.cdt).vertex_data(self.trianvertex_to_handle[vertex].unwrap()) } pub fn weight_mut(&mut self, vertex: I) -> &mut VW { spade::Triangulation::vertex_data_mut( &mut self.cdt, self.trianvertex_to_handle[vertex].unwrap(), ) } fn vertex(&self, handle: FixedVertexHandle) -> I { spade::Triangulation::vertex(&self.cdt, handle) .as_ref() .node_index() } fn handle(&self, vertex: I) -> FixedVertexHandle { self.trianvertex_to_handle[vertex].unwrap() } pub fn position(&self, vertex: I) -> Point<::Scalar> where ::Scalar: geo::CoordNum, { let position = spade::Triangulation::vertex(&self.cdt, self.handle(vertex)).position(); point! {x: position.x, y: position.y} } } impl< I: Copy + PartialEq + GetIndex, M: IndexMut>, VW: GetTrianvertexNodeIndex + HasPosition, EW: Default, > visit::GraphBase for Triangulation { type NodeId = I; type EdgeId = (I, I); } #[derive(Debug, Clone, Copy)] pub struct TriangulationEdgeWeightWrapper { length: f64, pub weight: EW, } impl PartialEq for TriangulationEdgeWeightWrapper { fn eq(&self, other: &Self) -> bool { self.length.eq(&other.length) } } impl PartialOrd for TriangulationEdgeWeightWrapper { fn partial_cmp(&self, other: &Self) -> Option { self.length.partial_cmp(&other.length) } } impl< I: Copy + PartialEq + GetIndex, M: IndexMut>, VW: GetTrianvertexNodeIndex + HasPosition, EW: Copy + Default, > visit::Data for Triangulation { type NodeWeight = VW; type EdgeWeight = TriangulationEdgeWeightWrapper; } #[derive(Debug, Clone, Copy, PartialEq)] pub struct TriangulationEdgeReference { from: I, to: I, weight: TriangulationEdgeWeightWrapper, } impl visit::EdgeRef for TriangulationEdgeReference { type NodeId = I; type EdgeId = (I, I); type Weight = TriangulationEdgeWeightWrapper; fn source(&self) -> Self::NodeId { self.from } fn target(&self) -> Self::NodeId { self.to } fn weight(&self) -> &Self::Weight { &self.weight } fn id(&self) -> Self::EdgeId { (self.from, self.to) } } impl< 'a, I: Copy + PartialEq + GetIndex, M: IndexMut>, VW: GetTrianvertexNodeIndex + HasPosition, EW: Default, > visit::IntoNeighbors for &'a Triangulation { type Neighbors = Box + 'a>; fn neighbors(self, vertex: Self::NodeId) -> Self::Neighbors { Box::new( spade::Triangulation::vertex(&self.cdt, self.handle(vertex)) .out_edges() .map(|handle| self.vertex(handle.to().fix())), ) } } impl< 'a, I: Copy + PartialEq + GetIndex, M: IndexMut>, VW: GetTrianvertexNodeIndex + HasPosition, EW: Copy + Default, > visit::IntoEdgeReferences for &'a Triangulation { type EdgeRef = TriangulationEdgeReference; type EdgeReferences = Box> + 'a>; fn edge_references(self) -> Self::EdgeReferences { Box::new(spade::Triangulation::directed_edges(&self.cdt).map(|edge| { let from = self.vertex(edge.from().fix()); let to = self.vertex(edge.to().fix()); TriangulationEdgeReference { from, to, weight: TriangulationEdgeWeightWrapper { length: Euclidean::distance(&self.position(from), &self.position(to)), weight: *edge.data(), }, } })) } } impl< 'a, I: Copy + PartialEq + GetIndex, M: IndexMut>, VW: GetTrianvertexNodeIndex + HasPosition, EW: Copy + Default, > visit::IntoEdges for &'a Triangulation { type Edges = Box> + 'a>; fn edges(self, node: Self::NodeId) -> Self::Edges { Box::new( spade::Triangulation::vertex(&self.cdt, self.handle(node)) .out_edges() .map(|edge| { let from = self.vertex(edge.from().fix()); let to = self.vertex(edge.to().fix()); TriangulationEdgeReference { from, to, weight: TriangulationEdgeWeightWrapper { length: Euclidean::distance(&self.position(from), &self.position(to)), weight: *edge.data(), }, } }), ) } } impl< 'a, I: Copy + PartialEq + GetIndex, M: IndexMut>, VW: GetTrianvertexNodeIndex + HasPosition, EW: Default, > visit::IntoNodeIdentifiers for &'a Triangulation { type NodeIdentifiers = Box + 'a>; fn node_identifiers(self) -> Self::NodeIdentifiers { Box::new( spade::Triangulation::fixed_vertices(&self.cdt).map(|vertex| { spade::Triangulation::s(&self.cdt) .vertex_data(vertex) .node_index() }), ) } } #[derive(Debug, PartialEq)] pub struct TriangulationVertexReference<'a, I, VW> { index: I, weight: &'a VW, } impl Clone for TriangulationVertexReference<'_, I, VW> { fn clone(&self) -> Self { Self { index: self.index.clone(), weight: self.weight, } } } impl Copy for TriangulationVertexReference<'_, I, VW> {} impl visit::NodeRef for TriangulationVertexReference<'_, I, VW> { type NodeId = I; type Weight = VW; fn id(&self) -> Self::NodeId { self.index } fn weight(&self) -> &Self::Weight { self.weight } } impl< 'a, I: Copy + PartialEq + GetIndex, M: IndexMut>, VW: GetTrianvertexNodeIndex + HasPosition, EW: Copy + Default, > visit::IntoNodeReferences for &'a Triangulation { type NodeRef = TriangulationVertexReference<'a, I, VW>; type NodeReferences = Box> + 'a>; fn node_references(self) -> Self::NodeReferences { Box::new( spade::Triangulation::fixed_vertices(&self.cdt).map(|vertex| { let weight = spade::Triangulation::s(&self.cdt).vertex_data(vertex); TriangulationVertexReference { index: weight.node_index(), weight, } }), ) } } impl< I: Copy + PartialEq + GetIndex + std::fmt::Debug, M: IndexMut>, VW: GetTrianvertexNodeIndex + HasPosition, EW: Default, > visit::NodeIndexable for &Triangulation { fn node_bound(&self) -> usize { //self.cdt.num_vertices() spade::Triangulation::num_vertices(&self.cdt) //FixedVertexHandle::max() //Self::new_internal(u32::MAX) } fn to_index(&self, node: I) -> usize { self.trianvertex_to_handle[node].unwrap().index() } fn from_index(&self, index: usize) -> I { spade::Triangulation::s(&self.cdt) .vertex_data(FixedVertexHandle::from_index(index)) .node_index() } }