feat(router/navmesh): Reduce number of navedges

This provides a speedup, but may break squeezing through under bends in
some corner cases.
This commit is contained in:
Mikolaj Wielgus 2025-11-16 23:38:00 +01:00
parent 63e1f7f25a
commit 1694227135
5 changed files with 79 additions and 19 deletions

View File

@ -2,10 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use std::iter::Skip;
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use itertools::{Itertools, Permutations};
use specctra_core::mesadata::AccessMesadata; use specctra_core::mesadata::AccessMesadata;
use crate::autorouter::{ use crate::autorouter::{

View File

@ -10,6 +10,7 @@ use crate::{
drawing::{ drawing::{
graph::{GetMaybeNet, MakePrimitiveRef}, graph::{GetMaybeNet, MakePrimitiveRef},
primitive::MakePrimitiveShape, primitive::MakePrimitiveShape,
seg::SeqLooseSegIndex,
Collision, Infringement, Collision, Infringement,
}, },
geometry::{ geometry::{
@ -105,17 +106,24 @@ impl<CW: Clone, Cel: Copy, R: AccessRules> Drawing<CW, Cel, R> {
v.push(joints.0.into()); v.push(joints.0.into());
v.push(joints.1.into()); v.push(joints.1.into());
if let Some(seg0) = self.primitive(joints.0).seg() { v.extend(
v.push(seg0.into()); self.collect_bend_bow_segs(bend)
} .map(Into::<PrimitiveIndex>::into),
);
if let Some(seg1) = self.primitive(joints.1).seg() {
v.push(seg1.into());
}
v v
} }
pub fn collect_bend_bow_segs(
&self,
bend: LooseBendIndex,
) -> impl Iterator<Item = SeqLooseSegIndex> {
let joints = self.primitive(bend).joints();
self.primitive(joints.0)
.seg()
.into_iter()
.chain(self.primitive(joints.1).seg().into_iter())
}
pub fn cut( pub fn cut(
&self, &self,
line: Line, line: Line,

View File

@ -249,6 +249,7 @@ impl Navmesh {
for prenavedge in prenavmesh.triangulation().edge_references() { for prenavedge in prenavmesh.triangulation().edge_references() {
Self::add_prenavedge_to_repr_as_quadrinavedges( Self::add_prenavedge_to_repr_as_quadrinavedges(
layout,
&mut graph, &mut graph,
&prenavnode_to_navnodes, &prenavnode_to_navnodes,
&overlapping_prenavnodes_unions, &overlapping_prenavnodes_unions,
@ -266,6 +267,7 @@ impl Navmesh {
// quadrinavedges exist for every one of them. // quadrinavedges exist for every one of them.
for constraint in prenavmesh.constraints() { for constraint in prenavmesh.constraints() {
Self::add_prenavedge_to_repr_as_quadrinavedges( Self::add_prenavedge_to_repr_as_quadrinavedges(
layout,
&mut graph, &mut graph,
&prenavnode_to_navnodes, &prenavnode_to_navnodes,
&overlapping_prenavnodes_unions, &overlapping_prenavnodes_unions,
@ -364,26 +366,27 @@ impl Navmesh {
PrenavmeshNodeIndex, PrenavmeshNodeIndex,
Vec<(NodeIndex<usize>, NodeIndex<usize>)>, Vec<(NodeIndex<usize>, NodeIndex<usize>)>,
>, >,
trianvertex: PrenavmeshNodeIndex, prenavnode: PrenavmeshNodeIndex,
node: BinavnodeNodeIndex, binavnode: BinavnodeNodeIndex,
) { ) {
let navnode1 = graph.add_node(NavnodeWeight { let navnode1 = graph.add_node(NavnodeWeight {
binavnode: node, binavnode,
maybe_sense: Some(RotationSense::Counterclockwise), maybe_sense: Some(RotationSense::Counterclockwise),
}); });
let navnode2 = graph.add_node(NavnodeWeight { let navnode2 = graph.add_node(NavnodeWeight {
binavnode: node, binavnode,
maybe_sense: Some(RotationSense::Clockwise), maybe_sense: Some(RotationSense::Clockwise),
}); });
prenavnode_to_navnodes prenavnode_to_navnodes
.get_mut(&trianvertex) .get_mut(&prenavnode)
.unwrap() .unwrap()
.push((navnode1, navnode2)); .push((navnode1, navnode2));
} }
fn add_prenavedge_to_repr_as_quadrinavedges( fn add_prenavedge_to_repr_as_quadrinavedges(
layout: &Layout<impl AccessRules>,
graph: &mut UnGraph<NavnodeWeight, (), usize>, graph: &mut UnGraph<NavnodeWeight, (), usize>,
prenavnode_to_navnodes: &BTreeMap< prenavnode_to_navnodes: &BTreeMap<
PrenavmeshNodeIndex, PrenavmeshNodeIndex,
@ -403,6 +406,7 @@ impl Navmesh {
)); ));
Self::add_prenavedge_as_quadrinavedges( Self::add_prenavedge_as_quadrinavedges(
layout,
graph, graph,
prenavnode_to_navnodes, prenavnode_to_navnodes,
from_prenavnode_repr, from_prenavnode_repr,
@ -411,6 +415,7 @@ impl Navmesh {
} }
fn add_prenavedge_as_quadrinavedges( fn add_prenavedge_as_quadrinavedges(
layout: &Layout<impl AccessRules>,
graph: &mut UnGraph<NavnodeWeight, (), usize>, graph: &mut UnGraph<NavnodeWeight, (), usize>,
prenavnode_to_navnodes: &BTreeMap< prenavnode_to_navnodes: &BTreeMap<
PrenavmeshNodeIndex, PrenavmeshNodeIndex,
@ -420,15 +425,58 @@ impl Navmesh {
to_prenavnode: PrenavmeshNodeIndex, to_prenavnode: PrenavmeshNodeIndex,
) { ) {
for (from_navnode1, from_navnode2) in prenavnode_to_navnodes[&from_prenavnode].iter() { for (from_navnode1, from_navnode2) in prenavnode_to_navnodes[&from_prenavnode].iter() {
for (to_navnode1, to_navnode2) in prenavnode_to_navnodes[&to_prenavnode].iter() { let from_binavnode = graph.node_weight(*from_navnode1).unwrap().binavnode;
if let Some((to_navnode1, to_navnode2)) = prenavnode_to_navnodes[&to_prenavnode]
.iter()
.find(|(to_navnode1, _)| {
let to_binavnode = graph.node_weight(*to_navnode1).unwrap().binavnode;
Self::are_binavnodes_joined(layout, from_binavnode, to_binavnode)
})
{
// Add binavedge.
graph.update_edge(*from_navnode1, *to_navnode1, ()); graph.update_edge(*from_navnode1, *to_navnode1, ());
graph.update_edge(*from_navnode2, *to_navnode2, ());
continue;
}
for (to_navnode1, to_navnode2) in prenavnode_to_navnodes[&to_prenavnode].iter() {
// Add binavedge.
graph.update_edge(*from_navnode1, *to_navnode1, ());
graph.update_edge(*from_navnode2, *to_navnode2, ());
// Add two more navedges to upgrade the binavedge to form a
// quadrinavedge.
graph.update_edge(*from_navnode1, *to_navnode2, ()); graph.update_edge(*from_navnode1, *to_navnode2, ());
graph.update_edge(*from_navnode2, *to_navnode1, ()); graph.update_edge(*from_navnode2, *to_navnode1, ());
graph.update_edge(*from_navnode2, *to_navnode2, ());
} }
} }
} }
fn are_binavnodes_joined(
layout: &Layout<impl AccessRules>,
from_binavnode: BinavnodeNodeIndex,
to_binavnode: BinavnodeNodeIndex,
) -> bool {
let BinavnodeNodeIndex::LooseBend(from_bend) = from_binavnode else {
return false;
};
let BinavnodeNodeIndex::LooseBend(to_bend) = to_binavnode else {
return false;
};
let from_bend_bow_segs: Vec<_> =
layout.drawing().collect_bend_bow_segs(from_bend).collect();
let to_bend_bow_segs: Vec<_> = layout.drawing().collect_bend_bow_segs(to_bend).collect();
from_bend_bow_segs
.iter()
.any(|seg| to_bend_bow_segs.contains(seg))
}
/// Returns the origin node. /// Returns the origin node.
pub fn origin(&self) -> FixedDotIndex { pub fn origin(&self) -> FixedDotIndex {
self.origin self.origin

View File

@ -16,7 +16,7 @@ use crate::{
geometry::primitive::PrimitiveShape, geometry::primitive::PrimitiveShape,
graph::GenericIndex, graph::GenericIndex,
layout::{poly::PolyWeight, Layout}, layout::{poly::PolyWeight, Layout},
stepper::{Abort, EstimateProgress}, stepper::Abort,
}; };
use super::{ use super::{

View File

@ -77,6 +77,13 @@ impl<
> 0) > 0)
} }
pub fn exists_constraint(&self, from: I, to: I) -> bool {
self.cdt.exists_constraint(
self.trianvertex_to_handle[from].unwrap(),
self.trianvertex_to_handle[to].unwrap(),
)
}
pub fn intersects_constraint(&self, from: &VW, to: &VW) -> bool { pub fn intersects_constraint(&self, from: &VW, to: &VW) -> bool {
self.cdt self.cdt
.intersects_constraint(from.position(), to.position()) .intersects_constraint(from.position(), to.position())