refactor(triangulation): Don't use `.graph()` when creating triangulations

This commit is contained in:
Mikolaj Wielgus 2025-09-14 22:02:22 +02:00
parent 71fdec2f06
commit f1e65267da
5 changed files with 139 additions and 35 deletions

View File

@ -2,12 +2,15 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use std::collections::BTreeMap; use std::{
collections::BTreeMap,
ops::{Index, IndexMut},
};
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use geo::Point; use geo::Point;
use petgraph::{data::Element, prelude::StableUnGraph, visit::NodeIndexable}; use petgraph::{data::Element, prelude::StableUnGraph, visit::NodeIndexable};
use spade::{HasPosition, InsertionError, Point2}; use spade::{handles::FixedVertexHandle, HasPosition, InsertionError, Point2};
use crate::{ use crate::{
autorouter::conncomps::Conncomps, autorouter::conncomps::Conncomps,
@ -64,6 +67,41 @@ impl HasPosition for RatvertexWeight {
} }
} }
#[derive(Clone)]
struct RatvertexToHandleMap {
fixed_dot_to_handle: Box<[Option<FixedVertexHandle>]>,
poly_to_handle: Box<[Option<FixedVertexHandle>]>,
}
impl RatvertexToHandleMap {
pub fn new(fixed_dot_bound: usize, poly_bound: usize) -> Self {
Self {
fixed_dot_to_handle: vec![None; fixed_dot_bound].into_boxed_slice(),
poly_to_handle: vec![None; poly_bound].into_boxed_slice(),
}
}
}
impl Index<RatvertexIndex> for RatvertexToHandleMap {
type Output = Option<FixedVertexHandle>;
fn index(&self, ratvertex: RatvertexIndex) -> &Self::Output {
match ratvertex {
RatvertexIndex::FixedDot(dot) => &self.fixed_dot_to_handle[dot.index()],
RatvertexIndex::Poly(bend) => &self.poly_to_handle[bend.index()],
}
}
}
impl IndexMut<RatvertexIndex> for RatvertexToHandleMap {
fn index_mut(&mut self, ratvertex: RatvertexIndex) -> &mut Self::Output {
match ratvertex {
RatvertexIndex::FixedDot(dot) => &mut self.fixed_dot_to_handle[dot.index()],
RatvertexIndex::Poly(bend) => &mut self.poly_to_handle[bend.index()],
}
}
}
pub struct Ratsnest { pub struct Ratsnest {
graph: StableUnGraph<RatvertexWeight, RatlineWeight, usize>, graph: StableUnGraph<RatvertexWeight, RatlineWeight, usize>,
} }
@ -77,7 +115,6 @@ impl Ratsnest {
}; };
let mut triangulations = BTreeMap::new(); let mut triangulations = BTreeMap::new();
let node_bound = layout.drawing().geometry().graph().node_bound();
for layer in 0..layout.drawing().layer_count() { for layer in 0..layout.drawing().layer_count() {
let mut handle_ratvertex_weight = let mut handle_ratvertex_weight =
@ -85,7 +122,12 @@ impl Ratsnest {
if let Some(net) = maybe_net { if let Some(net) = maybe_net {
triangulations triangulations
.entry((layer, net)) .entry((layer, net))
.or_insert_with(|| Triangulation::new(node_bound)) .or_insert_with(|| {
Triangulation::new(RatvertexToHandleMap::new(
layout.drawing().geometry().dot_index_bound(),
layout.drawing().geometry().compound_index_bound(),
))
})
.add_vertex(RatvertexWeight { vertex, pos })?; .add_vertex(RatvertexWeight { vertex, pos })?;
} }
Ok(()) Ok(())

View File

@ -530,6 +530,10 @@ impl<
pub fn bend_index_bound(&self) -> usize { pub fn bend_index_bound(&self) -> usize {
self.graph.node_bound() self.graph.node_bound()
} }
pub fn compound_index_bound(&self) -> usize {
self.graph.node_bound()
}
} }
impl<PW: Copy + Retag<Index = PI>, DW, SW, BW, CW, Cel, PI, DI, SI, BI> impl<PW: Copy + Retag<Index = PI>, DW, SW, BW, CW, Cel, PI, DI, SI, BI>

View File

@ -13,7 +13,7 @@ use petgraph::{
unionfind::UnionFind, unionfind::UnionFind,
visit::{ visit::{
Data, EdgeRef, GraphBase, IntoEdgeReferences, IntoEdges, IntoNeighbors, Data, EdgeRef, GraphBase, IntoEdgeReferences, IntoEdges, IntoNeighbors,
IntoNodeIdentifiers, NodeIndexable, Walker, IntoNodeIdentifiers, Walker,
}, },
}; };
use spade::InsertionError; use spade::InsertionError;
@ -163,7 +163,8 @@ impl Navmesh {
let mut prenavnode_to_navnodes = BTreeMap::new(); let mut prenavnode_to_navnodes = BTreeMap::new();
let mut overlapping_prenavnodes_unions = let mut overlapping_prenavnodes_unions =
UnionFind::new(prenavmesh.triangulation().node_bound()); // FIXME: This bound is incorrect. You can't actually unionize dots with bends.
UnionFind::new(layout.drawing().geometry().dot_index_bound());
for prenavnode in prenavmesh.triangulation().node_identifiers() { for prenavnode in prenavmesh.triangulation().node_identifiers() {
if prenavnode == origin.into() { if prenavnode == origin.into() {

View File

@ -2,11 +2,13 @@
// //
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
use std::ops::{Index, IndexMut};
use derive_getters::Getters; use derive_getters::Getters;
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use geo::Point; use geo::Point;
use petgraph::visit::NodeIndexable; use petgraph::visit::NodeIndexable;
use spade::{HasPosition, InsertionError, Point2}; use spade::{handles::FixedVertexHandle, HasPosition, InsertionError, Point2};
use crate::{ use crate::{
drawing::{ drawing::{
@ -128,9 +130,44 @@ impl PrenavmeshConstraint {
} }
} }
#[derive(Clone)]
pub struct PrenavnodeToHandleMap {
fixed_dot_to_handle: Box<[Option<FixedVertexHandle>]>,
fixed_bend_to_handle: Box<[Option<FixedVertexHandle>]>,
}
impl PrenavnodeToHandleMap {
pub fn new(fixed_dot_bound: usize, fixed_bend_bound: usize) -> Self {
Self {
fixed_dot_to_handle: vec![None; fixed_dot_bound].into_boxed_slice(),
fixed_bend_to_handle: vec![None; fixed_bend_bound].into_boxed_slice(),
}
}
}
impl Index<PrenavmeshNodeIndex> for PrenavnodeToHandleMap {
type Output = Option<FixedVertexHandle>;
fn index(&self, prenavnode: PrenavmeshNodeIndex) -> &Self::Output {
match prenavnode {
PrenavmeshNodeIndex::FixedDot(dot) => &self.fixed_dot_to_handle[dot.index()],
PrenavmeshNodeIndex::FixedBend(bend) => &self.fixed_bend_to_handle[bend.index()],
}
}
}
impl IndexMut<PrenavmeshNodeIndex> for PrenavnodeToHandleMap {
fn index_mut(&mut self, prenavnode: PrenavmeshNodeIndex) -> &mut Self::Output {
match prenavnode {
PrenavmeshNodeIndex::FixedDot(dot) => &mut self.fixed_dot_to_handle[dot.index()],
PrenavmeshNodeIndex::FixedBend(bend) => &mut self.fixed_bend_to_handle[bend.index()],
}
}
}
#[derive(Clone, Getters)] #[derive(Clone, Getters)]
pub struct Prenavmesh { pub struct Prenavmesh {
triangulation: Triangulation<PrenavmeshNodeIndex, PrenavmeshWeight, ()>, triangulation: Triangulation<PrenavmeshNodeIndex, PrenavnodeToHandleMap, PrenavmeshWeight, ()>,
constraints: Vec<PrenavmeshConstraint>, constraints: Vec<PrenavmeshConstraint>,
} }
@ -142,7 +179,10 @@ impl Prenavmesh {
_options: RouterOptions, _options: RouterOptions,
) -> Result<Self, NavmeshError> { ) -> Result<Self, NavmeshError> {
let mut this = Self { let mut this = Self {
triangulation: Triangulation::new(layout.drawing().geometry().graph().node_bound()), triangulation: Triangulation::new(PrenavnodeToHandleMap::new(
layout.drawing().geometry().dot_index_bound(),
layout.drawing().geometry().bend_index_bound(),
)),
constraints: vec![], constraints: vec![],
}; };

View File

@ -2,6 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use std::ops::IndexMut;
use std::{cmp::Ordering, marker::PhantomData}; use std::{cmp::Ordering, marker::PhantomData};
use geo::algorithm::line_measures::{Distance, Euclidean}; use geo::algorithm::line_measures::{Distance, Euclidean};
@ -21,33 +22,37 @@ pub trait GetTrianvertexNodeIndex<I> {
// not implement `Debug`, though it could (and `spade::DelaunayTriangulation` // not implement `Debug`, though it could (and `spade::DelaunayTriangulation`
// actually does). // actually does).
#[derive(Clone)] #[derive(Clone)]
pub struct Triangulation<I, VW: GetTrianvertexNodeIndex<I> + HasPosition, EW: Default> { pub struct Triangulation<I, M, VW: GetTrianvertexNodeIndex<I> + HasPosition, EW: Default> {
cdt: ConstrainedDelaunayTriangulation<VW, EW>, cdt: ConstrainedDelaunayTriangulation<VW, EW>,
trianvertex_to_handle: Box<[Option<FixedVertexHandle>]>, trianvertex_to_handle: M,
index_marker: PhantomData<I>, index_marker: PhantomData<I>,
} }
impl<I: GetIndex, VW: GetTrianvertexNodeIndex<I> + HasPosition, EW: Default> impl<
Triangulation<I, VW, EW> I: GetIndex,
M: IndexMut<I, Output = Option<FixedVertexHandle>>,
VW: GetTrianvertexNodeIndex<I> + HasPosition,
EW: Default,
> Triangulation<I, M, VW, EW>
{ {
pub fn new(node_bound: usize) -> Self { pub fn new(trianvertex_to_handle: M) -> Self {
Self { Self {
cdt: <ConstrainedDelaunayTriangulation<VW, EW> as spade::Triangulation>::new(), cdt: <ConstrainedDelaunayTriangulation<VW, EW> as spade::Triangulation>::new(),
trianvertex_to_handle: vec![None; node_bound].into_boxed_slice(), trianvertex_to_handle,
index_marker: PhantomData, index_marker: PhantomData,
} }
} }
pub fn add_vertex(&mut self, weight: VW) -> Result<(), InsertionError> { pub fn add_vertex(&mut self, weight: VW) -> Result<(), InsertionError> {
let index = weight.node_index().index(); let index = weight.node_index();
self.trianvertex_to_handle[index] = self.trianvertex_to_handle[index] =
Some(spade::Triangulation::insert(&mut self.cdt, weight)?); Some(spade::Triangulation::insert(&mut self.cdt, weight)?);
Ok(()) Ok(())
} }
pub fn add_constraint_edge(&mut self, from: VW, to: VW) -> Result<bool, InsertionError> { pub fn add_constraint_edge(&mut self, from: VW, to: VW) -> Result<bool, InsertionError> {
let from_index = from.node_index().index(); let from_index = from.node_index();
let to_index = to.node_index().index(); let to_index = to.node_index();
// It is possible for one or both constraint edge endpoint vertices to // It is possible for one or both constraint edge endpoint vertices to
// not exist in the triangulation even after everything has been added. // not exist in the triangulation even after everything has been added.
@ -74,14 +79,13 @@ impl<I: GetIndex, VW: GetTrianvertexNodeIndex<I> + HasPosition, EW: Default>
} }
pub fn weight(&self, vertex: I) -> &VW { pub fn weight(&self, vertex: I) -> &VW {
spade::Triangulation::s(&self.cdt) spade::Triangulation::s(&self.cdt).vertex_data(self.trianvertex_to_handle[vertex].unwrap())
.vertex_data(self.trianvertex_to_handle[vertex.index()].unwrap())
} }
pub fn weight_mut(&mut self, vertex: I) -> &mut VW { pub fn weight_mut(&mut self, vertex: I) -> &mut VW {
spade::Triangulation::vertex_data_mut( spade::Triangulation::vertex_data_mut(
&mut self.cdt, &mut self.cdt,
self.trianvertex_to_handle[vertex.index()].unwrap(), self.trianvertex_to_handle[vertex].unwrap(),
) )
} }
@ -92,7 +96,7 @@ impl<I: GetIndex, VW: GetTrianvertexNodeIndex<I> + HasPosition, EW: Default>
} }
fn handle(&self, vertex: I) -> FixedVertexHandle { fn handle(&self, vertex: I) -> FixedVertexHandle {
self.trianvertex_to_handle[vertex.index()].unwrap() self.trianvertex_to_handle[vertex].unwrap()
} }
pub fn position(&self, vertex: I) -> Point<<VW as HasPosition>::Scalar> pub fn position(&self, vertex: I) -> Point<<VW as HasPosition>::Scalar>
@ -104,8 +108,12 @@ impl<I: GetIndex, VW: GetTrianvertexNodeIndex<I> + HasPosition, EW: Default>
} }
} }
impl<I: Copy + PartialEq + GetIndex, VW: GetTrianvertexNodeIndex<I> + HasPosition, EW: Default> impl<
visit::GraphBase for Triangulation<I, VW, EW> I: Copy + PartialEq + GetIndex,
M: IndexMut<I, Output = Option<FixedVertexHandle>>,
VW: GetTrianvertexNodeIndex<I> + HasPosition,
EW: Default,
> visit::GraphBase for Triangulation<I, M, VW, EW>
{ {
type NodeId = I; type NodeId = I;
type EdgeId = (I, I); type EdgeId = (I, I);
@ -131,9 +139,10 @@ impl<EW> PartialOrd for TriangulationEdgeWeightWrapper<EW> {
impl< impl<
I: Copy + PartialEq + GetIndex, I: Copy + PartialEq + GetIndex,
M: IndexMut<I, Output = Option<FixedVertexHandle>>,
VW: GetTrianvertexNodeIndex<I> + HasPosition, VW: GetTrianvertexNodeIndex<I> + HasPosition,
EW: Copy + Default, EW: Copy + Default,
> visit::Data for Triangulation<I, VW, EW> > visit::Data for Triangulation<I, M, VW, EW>
{ {
type NodeWeight = VW; type NodeWeight = VW;
type EdgeWeight = TriangulationEdgeWeightWrapper<EW>; type EdgeWeight = TriangulationEdgeWeightWrapper<EW>;
@ -171,9 +180,10 @@ impl<I: Copy, EW: Copy> visit::EdgeRef for TriangulationEdgeReference<I, EW> {
impl< impl<
'a, 'a,
I: Copy + PartialEq + GetIndex, I: Copy + PartialEq + GetIndex,
M: IndexMut<I, Output = Option<FixedVertexHandle>>,
VW: GetTrianvertexNodeIndex<I> + HasPosition, VW: GetTrianvertexNodeIndex<I> + HasPosition,
EW: Default, EW: Default,
> visit::IntoNeighbors for &'a Triangulation<I, VW, EW> > visit::IntoNeighbors for &'a Triangulation<I, M, VW, EW>
{ {
type Neighbors = Box<dyn Iterator<Item = I> + 'a>; type Neighbors = Box<dyn Iterator<Item = I> + 'a>;
@ -189,9 +199,10 @@ impl<
impl< impl<
'a, 'a,
I: Copy + PartialEq + GetIndex, I: Copy + PartialEq + GetIndex,
M: IndexMut<I, Output = Option<FixedVertexHandle>>,
VW: GetTrianvertexNodeIndex<I> + HasPosition<Scalar = f64>, VW: GetTrianvertexNodeIndex<I> + HasPosition<Scalar = f64>,
EW: Copy + Default, EW: Copy + Default,
> visit::IntoEdgeReferences for &'a Triangulation<I, VW, EW> > visit::IntoEdgeReferences for &'a Triangulation<I, M, VW, EW>
{ {
type EdgeRef = TriangulationEdgeReference<I, EW>; type EdgeRef = TriangulationEdgeReference<I, EW>;
type EdgeReferences = Box<dyn Iterator<Item = TriangulationEdgeReference<I, EW>> + 'a>; type EdgeReferences = Box<dyn Iterator<Item = TriangulationEdgeReference<I, EW>> + 'a>;
@ -216,9 +227,10 @@ impl<
impl< impl<
'a, 'a,
I: Copy + PartialEq + GetIndex, I: Copy + PartialEq + GetIndex,
M: IndexMut<I, Output = Option<FixedVertexHandle>>,
VW: GetTrianvertexNodeIndex<I> + HasPosition<Scalar = f64>, VW: GetTrianvertexNodeIndex<I> + HasPosition<Scalar = f64>,
EW: Copy + Default, EW: Copy + Default,
> visit::IntoEdges for &'a Triangulation<I, VW, EW> > visit::IntoEdges for &'a Triangulation<I, M, VW, EW>
{ {
type Edges = Box<dyn Iterator<Item = TriangulationEdgeReference<I, EW>> + 'a>; type Edges = Box<dyn Iterator<Item = TriangulationEdgeReference<I, EW>> + 'a>;
@ -246,9 +258,10 @@ impl<
impl< impl<
'a, 'a,
I: Copy + PartialEq + GetIndex, I: Copy + PartialEq + GetIndex,
M: IndexMut<I, Output = Option<FixedVertexHandle>>,
VW: GetTrianvertexNodeIndex<I> + HasPosition, VW: GetTrianvertexNodeIndex<I> + HasPosition,
EW: Default, EW: Default,
> visit::IntoNodeIdentifiers for &'a Triangulation<I, VW, EW> > visit::IntoNodeIdentifiers for &'a Triangulation<I, M, VW, EW>
{ {
type NodeIdentifiers = Box<dyn Iterator<Item = I> + 'a>; type NodeIdentifiers = Box<dyn Iterator<Item = I> + 'a>;
@ -296,9 +309,10 @@ impl<I: Copy, VW> visit::NodeRef for TriangulationVertexReference<'_, I, VW> {
impl< impl<
'a, 'a,
I: Copy + PartialEq + GetIndex, I: Copy + PartialEq + GetIndex,
M: IndexMut<I, Output = Option<FixedVertexHandle>>,
VW: GetTrianvertexNodeIndex<I> + HasPosition, VW: GetTrianvertexNodeIndex<I> + HasPosition,
EW: Copy + Default, EW: Copy + Default,
> visit::IntoNodeReferences for &'a Triangulation<I, VW, EW> > visit::IntoNodeReferences for &'a Triangulation<I, M, VW, EW>
{ {
type NodeRef = TriangulationVertexReference<'a, I, VW>; type NodeRef = TriangulationVertexReference<'a, I, VW>;
type NodeReferences = Box<dyn Iterator<Item = TriangulationVertexReference<'a, I, VW>> + 'a>; type NodeReferences = Box<dyn Iterator<Item = TriangulationVertexReference<'a, I, VW>> + 'a>;
@ -318,22 +332,25 @@ impl<
impl< impl<
I: Copy + PartialEq + GetIndex + std::fmt::Debug, I: Copy + PartialEq + GetIndex + std::fmt::Debug,
M: IndexMut<I, Output = Option<FixedVertexHandle>>,
VW: GetTrianvertexNodeIndex<I> + HasPosition, VW: GetTrianvertexNodeIndex<I> + HasPosition,
EW: Default, EW: Default,
> visit::NodeIndexable for &Triangulation<I, VW, EW> > visit::NodeIndexable for &Triangulation<I, M, VW, EW>
{ {
fn node_bound(&self) -> usize { fn node_bound(&self) -> usize {
//spade::Triangulation::num_vertices(&self.triangulation) //self.cdt.num_vertices()
self.trianvertex_to_handle.len() spade::Triangulation::num_vertices(&self.cdt)
//FixedVertexHandle::max()
//Self::new_internal(u32::MAX)
} }
fn to_index(&self, node: I) -> usize { fn to_index(&self, node: I) -> usize {
node.index() self.trianvertex_to_handle[node].unwrap().index()
} }
fn from_index(&self, index: usize) -> I { fn from_index(&self, index: usize) -> I {
spade::Triangulation::s(&self.cdt) spade::Triangulation::s(&self.cdt)
.vertex_data(self.trianvertex_to_handle[index].unwrap()) .vertex_data(FixedVertexHandle::from_index(index))
.node_index() .node_index()
} }
} }