mirror of https://codeberg.org/topola/topola.git
refactor(router/navmesh): Split out navmesh triangulation into "prenavmesh" module
This commit is contained in:
parent
9742740b9e
commit
29dc59df04
|
|
@ -73,6 +73,7 @@ allowed_scopes = [
|
||||||
"router/ng/floating",
|
"router/ng/floating",
|
||||||
"router/ng/poly",
|
"router/ng/poly",
|
||||||
"router/ng/router",
|
"router/ng/router",
|
||||||
|
"router/prenavmesh",
|
||||||
"router/route",
|
"router/route",
|
||||||
"router/router",
|
"router/router",
|
||||||
"router/thetastar",
|
"router/thetastar",
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,9 @@ use topola::{
|
||||||
layout::poly::MakePolygon,
|
layout::poly::MakePolygon,
|
||||||
math::{Circle, RotationSense},
|
math::{Circle, RotationSense},
|
||||||
router::{
|
router::{
|
||||||
navmesh::{BinavnodeNodeIndex, NavmeshTriangulationConstraint, NavnodeIndex},
|
navmesh::{BinavnodeNodeIndex, NavnodeIndex},
|
||||||
ng::pie,
|
ng::pie,
|
||||||
|
prenavmesh::PrenavmeshConstraint,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -411,7 +412,9 @@ impl Viewport {
|
||||||
if let Some(thetastar) = activity.maybe_thetastar() {
|
if let Some(thetastar) = activity.maybe_thetastar() {
|
||||||
let navmesh = thetastar.graph();
|
let navmesh = thetastar.graph();
|
||||||
|
|
||||||
for edge in navmesh.triangulation().edge_references() {
|
for edge in
|
||||||
|
navmesh.prenavmesh().triangulation().edge_references()
|
||||||
|
{
|
||||||
let from = PrimitiveIndex::from(BinavnodeNodeIndex::from(
|
let from = PrimitiveIndex::from(BinavnodeNodeIndex::from(
|
||||||
edge.source(),
|
edge.source(),
|
||||||
))
|
))
|
||||||
|
|
@ -443,8 +446,8 @@ impl Viewport {
|
||||||
if let Some(thetastar) = activity.maybe_thetastar() {
|
if let Some(thetastar) = activity.maybe_thetastar() {
|
||||||
let navmesh = thetastar.graph();
|
let navmesh = thetastar.graph();
|
||||||
|
|
||||||
for NavmeshTriangulationConstraint(from_weight, to_weight) in
|
for PrenavmeshConstraint(from_weight, to_weight) in
|
||||||
navmesh.constraints()
|
navmesh.prenavmesh().constraints().iter()
|
||||||
{
|
{
|
||||||
let from = from_weight.pos + [100.0, 100.0].into();
|
let from = from_weight.pos + [100.0, 100.0].into();
|
||||||
let to = to_weight.pos + [100.0, 100.0].into();
|
let to = to_weight.pos + [100.0, 100.0].into();
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ pub mod navcord;
|
||||||
pub mod navcorder;
|
pub mod navcorder;
|
||||||
pub mod navmesh;
|
pub mod navmesh;
|
||||||
pub mod ng;
|
pub mod ng;
|
||||||
|
pub mod prenavmesh;
|
||||||
mod route;
|
mod route;
|
||||||
mod router;
|
mod router;
|
||||||
pub mod thetastar;
|
pub mod thetastar;
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ use std::collections::BTreeMap;
|
||||||
|
|
||||||
use derive_getters::Getters;
|
use derive_getters::Getters;
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
use geo::Point;
|
|
||||||
use petgraph::{
|
use petgraph::{
|
||||||
data::DataMap,
|
data::DataMap,
|
||||||
graph::UnGraph,
|
graph::UnGraph,
|
||||||
|
|
@ -16,29 +15,31 @@ use petgraph::{
|
||||||
IntoNodeIdentifiers, NodeIndexable,
|
IntoNodeIdentifiers, NodeIndexable,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use spade::{HasPosition, InsertionError, Point2};
|
use spade::InsertionError;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
drawing::{
|
drawing::{
|
||||||
bend::{FixedBendIndex, LooseBendIndex},
|
bend::{FixedBendIndex, LooseBendIndex},
|
||||||
dot::{DotIndex, FixedDotIndex},
|
dot::FixedDotIndex,
|
||||||
gear::{GearIndex, GetNextGear},
|
gear::{GearIndex, GetNextGear},
|
||||||
graph::{GetMaybeNet, MakePrimitive, PrimitiveIndex},
|
graph::{GetMaybeNet, MakePrimitive, PrimitiveIndex},
|
||||||
primitive::{GetCore, GetJoints, MakePrimitiveShape, Primitive},
|
primitive::Primitive,
|
||||||
rules::AccessRules,
|
rules::AccessRules,
|
||||||
seg::{FixedSegIndex, LoneLooseSegIndex, SegIndex, SeqLooseSegIndex},
|
|
||||||
Drawing,
|
Drawing,
|
||||||
},
|
},
|
||||||
geometry::{shape::AccessShape, GetLayer},
|
geometry::GetLayer,
|
||||||
graph::{GetPetgraphIndex, MakeRef},
|
graph::{GetPetgraphIndex, MakeRef},
|
||||||
layout::Layout,
|
layout::Layout,
|
||||||
math::RotationSense,
|
math::RotationSense,
|
||||||
router::thetastar::MakeEdgeRef,
|
router::thetastar::MakeEdgeRef,
|
||||||
triangulation::{GetTrianvertexNodeIndex, Triangulation},
|
triangulation::Triangulation,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::RouterOptions;
|
use super::{
|
||||||
|
prenavmesh::{Prenavmesh, PrenavmeshConstraint, PrenavmeshNodeIndex, PrenavmeshWeight},
|
||||||
|
RouterOptions,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
|
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
pub struct NavnodeIndex(pub NodeIndex<usize>);
|
pub struct NavnodeIndex(pub NodeIndex<usize>);
|
||||||
|
|
@ -66,6 +67,15 @@ pub enum BinavnodeNodeIndex {
|
||||||
LooseBend(LooseBendIndex),
|
LooseBend(LooseBendIndex),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<PrenavmeshNodeIndex> for BinavnodeNodeIndex {
|
||||||
|
fn from(trianvertex: PrenavmeshNodeIndex) -> Self {
|
||||||
|
match trianvertex {
|
||||||
|
PrenavmeshNodeIndex::FixedDot(dot) => BinavnodeNodeIndex::FixedDot(dot),
|
||||||
|
PrenavmeshNodeIndex::FixedBend(bend) => BinavnodeNodeIndex::FixedBend(bend),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<BinavnodeNodeIndex> for PrimitiveIndex {
|
impl From<BinavnodeNodeIndex> for PrimitiveIndex {
|
||||||
fn from(vertex: BinavnodeNodeIndex) -> Self {
|
fn from(vertex: BinavnodeNodeIndex) -> Self {
|
||||||
match vertex {
|
match vertex {
|
||||||
|
|
@ -86,106 +96,6 @@ impl From<BinavnodeNodeIndex> for GearIndex {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trianvertices are the vertices of the triangulation before it is converted
|
|
||||||
/// to the navmesh by multiplying each of them into more vertices (called
|
|
||||||
/// navnodes). Every trianvertex corresponds to one or more binavnodes on
|
|
||||||
/// the navmesh.
|
|
||||||
///
|
|
||||||
/// The name "trianvertex" is a shortening of "triangulation vertex".
|
|
||||||
#[enum_dispatch(GetPetgraphIndex, MakePrimitive)]
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
|
||||||
pub enum TrianvertexNodeIndex {
|
|
||||||
FixedDot(FixedDotIndex),
|
|
||||||
FixedBend(FixedBendIndex),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TrianvertexNodeIndex> for BinavnodeNodeIndex {
|
|
||||||
fn from(trianvertex: TrianvertexNodeIndex) -> Self {
|
|
||||||
match trianvertex {
|
|
||||||
TrianvertexNodeIndex::FixedDot(dot) => BinavnodeNodeIndex::FixedDot(dot),
|
|
||||||
TrianvertexNodeIndex::FixedBend(bend) => BinavnodeNodeIndex::FixedBend(bend),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct TrianvertexWeight {
|
|
||||||
pub node: TrianvertexNodeIndex,
|
|
||||||
pub pos: Point,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TrianvertexWeight {
|
|
||||||
fn new_from_fixed_dot(layout: &Layout<impl AccessRules>, dot: FixedDotIndex) -> Self {
|
|
||||||
Self {
|
|
||||||
node: dot.into(),
|
|
||||||
pos: dot.primitive(layout.drawing()).shape().center(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_from_fixed_bend(layout: &Layout<impl AccessRules>, bend: FixedBendIndex) -> Self {
|
|
||||||
Self {
|
|
||||||
node: bend.into(),
|
|
||||||
pos: bend.primitive(layout.drawing()).shape().center(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GetTrianvertexNodeIndex<TrianvertexNodeIndex> for TrianvertexWeight {
|
|
||||||
fn node_index(&self) -> TrianvertexNodeIndex {
|
|
||||||
self.node
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasPosition for TrianvertexWeight {
|
|
||||||
type Scalar = f64;
|
|
||||||
fn position(&self) -> Point2<Self::Scalar> {
|
|
||||||
Point2::new(self.pos.x(), self.pos.y())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct NavmeshTriangulationConstraint(pub TrianvertexWeight, pub TrianvertexWeight);
|
|
||||||
|
|
||||||
impl NavmeshTriangulationConstraint {
|
|
||||||
fn new_from_fixed_dot_pair(
|
|
||||||
layout: &Layout<impl AccessRules>,
|
|
||||||
from_dot: FixedDotIndex,
|
|
||||||
to_dot: FixedDotIndex,
|
|
||||||
) -> Self {
|
|
||||||
Self(
|
|
||||||
TrianvertexWeight::new_from_fixed_dot(layout, from_dot),
|
|
||||||
TrianvertexWeight::new_from_fixed_dot(layout, to_dot),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_from_lone_loose_seg(layout: &Layout<impl AccessRules>, seg: LoneLooseSegIndex) -> Self {
|
|
||||||
let (from_dot, to_dot) = layout.drawing().primitive(seg).joints();
|
|
||||||
Self::new_from_fixed_dot_pair(layout, from_dot, to_dot)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_from_seq_loose_seg(layout: &Layout<impl AccessRules>, seg: SeqLooseSegIndex) -> Self {
|
|
||||||
let (from_joint, to_joint) = layout.drawing().primitive(seg).joints();
|
|
||||||
|
|
||||||
let from_dot = match from_joint {
|
|
||||||
DotIndex::Fixed(dot) => dot,
|
|
||||||
DotIndex::Loose(dot) => {
|
|
||||||
let bend = layout.drawing().primitive(dot).bend();
|
|
||||||
|
|
||||||
layout.drawing().primitive(bend).core()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let to_bend = layout.drawing().primitive(to_joint).bend();
|
|
||||||
let to_dot = layout.drawing().primitive(to_bend).core();
|
|
||||||
Self::new_from_fixed_dot_pair(layout, from_dot, to_dot)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_from_fixed_seg(layout: &Layout<impl AccessRules>, seg: FixedSegIndex) -> Self {
|
|
||||||
let (from_dot, to_dot) = layout.drawing().primitive(seg).joints();
|
|
||||||
Self::new_from_fixed_dot_pair(layout, from_dot, to_dot)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The terms "navnode" and "navmesh vertex", "navmesh node", "navigation
|
/// The terms "navnode" and "navmesh vertex", "navmesh node", "navigation
|
||||||
/// vertex", "navigation node" are all equivalent.
|
/// vertex", "navigation node" are all equivalent.
|
||||||
///
|
///
|
||||||
|
|
@ -208,8 +118,6 @@ pub enum NavmeshError {
|
||||||
Insertion(#[from] InsertionError),
|
Insertion(#[from] InsertionError),
|
||||||
}
|
}
|
||||||
|
|
||||||
type NavmeshTriangulation = Triangulation<TrianvertexNodeIndex, TrianvertexWeight, ()>;
|
|
||||||
|
|
||||||
/// The navmesh holds the entire Topola's search space represented as a graph.
|
/// The navmesh holds the entire Topola's search space represented as a graph.
|
||||||
/// Topola's routing works by navigating over this graph with a pathfinding
|
/// Topola's routing works by navigating over this graph with a pathfinding
|
||||||
/// algorithm such as A* while drawing a track segment (always a cane except
|
/// algorithm such as A* while drawing a track segment (always a cane except
|
||||||
|
|
@ -229,12 +137,8 @@ pub struct Navmesh {
|
||||||
#[getter(skip)]
|
#[getter(skip)]
|
||||||
destination_navnode: NavnodeIndex,
|
destination_navnode: NavnodeIndex,
|
||||||
|
|
||||||
/// Original triangulation stored for debugging purposes.
|
/// Original constrainted triangulation stored for debugging purposes.
|
||||||
// XXX: Maybe have a way to compile this out in release?
|
prenavmesh: Prenavmesh,
|
||||||
triangulation: NavmeshTriangulation,
|
|
||||||
// Original triangulation constraints stored for debugging purposes.
|
|
||||||
// XXX: Maybe have a way to compile this out in release?
|
|
||||||
constraints: Vec<NavmeshTriangulationConstraint>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Navmesh {
|
impl Navmesh {
|
||||||
|
|
@ -245,117 +149,15 @@ impl Navmesh {
|
||||||
destination: FixedDotIndex,
|
destination: FixedDotIndex,
|
||||||
options: RouterOptions,
|
options: RouterOptions,
|
||||||
) -> Result<Self, NavmeshError> {
|
) -> Result<Self, NavmeshError> {
|
||||||
let mut triangulation: NavmeshTriangulation =
|
let prenavmesh = Prenavmesh::new(layout, origin, destination, options)?;
|
||||||
NavmeshTriangulation::new(layout.drawing().geometry().graph().node_bound());
|
Self::new_from_prenavmesh(layout, prenavmesh, origin, destination, options)
|
||||||
let mut constraints = vec![];
|
|
||||||
|
|
||||||
let layer = layout.drawing().primitive(origin).layer();
|
|
||||||
let maybe_net = layout.drawing().primitive(origin).maybe_net();
|
|
||||||
|
|
||||||
for node in layout.drawing().layer_primitive_nodes(layer) {
|
|
||||||
let primitive = node.primitive(layout.drawing());
|
|
||||||
|
|
||||||
if let Some(primitive_net) = primitive.maybe_net() {
|
|
||||||
if node == origin.into()
|
|
||||||
|| node == destination.into()
|
|
||||||
|| Some(primitive_net) != maybe_net
|
|
||||||
{
|
|
||||||
match node {
|
|
||||||
PrimitiveIndex::FixedDot(dot) => {
|
|
||||||
triangulation
|
|
||||||
.add_vertex(TrianvertexWeight::new_from_fixed_dot(layout, dot))?;
|
|
||||||
}
|
|
||||||
PrimitiveIndex::LoneLooseSeg(seg) => {
|
|
||||||
Self::add_constraint(
|
|
||||||
&mut triangulation,
|
|
||||||
&mut constraints,
|
|
||||||
NavmeshTriangulationConstraint::new_from_lone_loose_seg(
|
|
||||||
layout, seg,
|
|
||||||
),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
PrimitiveIndex::SeqLooseSeg(seg) => {
|
|
||||||
Self::add_constraint(
|
|
||||||
&mut triangulation,
|
|
||||||
&mut constraints,
|
|
||||||
NavmeshTriangulationConstraint::new_from_seq_loose_seg(layout, seg),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
PrimitiveIndex::FixedBend(bend) => {
|
|
||||||
triangulation
|
|
||||||
.add_vertex(TrianvertexWeight::new_from_fixed_bend(layout, bend))?;
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for node in layout.drawing().layer_primitive_nodes(layer) {
|
|
||||||
let primitive = node.primitive(layout.drawing());
|
|
||||||
|
|
||||||
if let Some(primitive_net) = primitive.maybe_net() {
|
|
||||||
if node == origin.into()
|
|
||||||
|| node == destination.into()
|
|
||||||
|| Some(primitive_net) != maybe_net
|
|
||||||
{
|
|
||||||
// If you have a band that was routed from a polygonal pad,
|
|
||||||
// when you will start a new routing some of the constraint
|
|
||||||
// edges created from the loose segs of a band will
|
|
||||||
// intersect some of the constraint edges created from the
|
|
||||||
// fixed segs constituting the pad boundary.
|
|
||||||
//
|
|
||||||
// Such constraint intersections are erroneous and cause
|
|
||||||
// Spade to throw a panic at runtime. So, to prevent this
|
|
||||||
// from occuring, we iterate over the layout for the second
|
|
||||||
// time, after all the constraint edges from bands have been
|
|
||||||
// placed, and only then add constraint edges created from
|
|
||||||
// fixed segs that do not cause an intersection.
|
|
||||||
match node {
|
|
||||||
PrimitiveIndex::FixedSeg(seg) => {
|
|
||||||
let constraint =
|
|
||||||
NavmeshTriangulationConstraint::new_from_fixed_seg(layout, seg);
|
|
||||||
|
|
||||||
if !triangulation.intersects_constraint(&constraint.0, &constraint.1) {
|
|
||||||
Self::add_constraint(
|
|
||||||
&mut triangulation,
|
|
||||||
&mut constraints,
|
|
||||||
constraint,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Self::new_from_triangulation(
|
|
||||||
layout,
|
|
||||||
triangulation,
|
|
||||||
origin,
|
|
||||||
destination,
|
|
||||||
constraints,
|
|
||||||
options,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_constraint(
|
fn new_from_prenavmesh(
|
||||||
triangulation: &mut NavmeshTriangulation,
|
|
||||||
constraints: &mut Vec<NavmeshTriangulationConstraint>,
|
|
||||||
constraint: NavmeshTriangulationConstraint,
|
|
||||||
) -> Result<(), InsertionError> {
|
|
||||||
triangulation.add_constraint_edge(constraint.0, constraint.1)?;
|
|
||||||
constraints.push(constraint);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_from_triangulation(
|
|
||||||
layout: &Layout<impl AccessRules>,
|
layout: &Layout<impl AccessRules>,
|
||||||
triangulation: NavmeshTriangulation,
|
prenavmesh: Prenavmesh,
|
||||||
origin: FixedDotIndex,
|
origin: FixedDotIndex,
|
||||||
destination: FixedDotIndex,
|
destination: FixedDotIndex,
|
||||||
constraints: Vec<NavmeshTriangulationConstraint>,
|
|
||||||
options: RouterOptions,
|
options: RouterOptions,
|
||||||
) -> Result<Self, NavmeshError> {
|
) -> Result<Self, NavmeshError> {
|
||||||
let mut graph: UnGraph<NavnodeWeight, (), usize> = UnGraph::default();
|
let mut graph: UnGraph<NavnodeWeight, (), usize> = UnGraph::default();
|
||||||
|
|
@ -364,7 +166,7 @@ impl Navmesh {
|
||||||
|
|
||||||
let mut map = BTreeMap::new();
|
let mut map = BTreeMap::new();
|
||||||
|
|
||||||
for trianvertex in triangulation.node_identifiers() {
|
for trianvertex in prenavmesh.triangulation().node_identifiers() {
|
||||||
if trianvertex == origin.into() {
|
if trianvertex == origin.into() {
|
||||||
let navnode = graph.add_node(NavnodeWeight {
|
let navnode = graph.add_node(NavnodeWeight {
|
||||||
node: trianvertex.into(),
|
node: trianvertex.into(),
|
||||||
|
|
@ -431,7 +233,7 @@ impl Navmesh {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for edge in triangulation.edge_references() {
|
for edge in prenavmesh.triangulation().edge_references() {
|
||||||
Self::add_trianedge_to_graph_as_quadrinavedge(
|
Self::add_trianedge_to_graph_as_quadrinavedge(
|
||||||
&mut graph,
|
&mut graph,
|
||||||
&map,
|
&map,
|
||||||
|
|
@ -447,7 +249,7 @@ impl Navmesh {
|
||||||
//
|
//
|
||||||
// So now we go over all the constraints and make sure that
|
// So now we go over all the constraints and make sure that
|
||||||
// quadrinavedges exist for every one of them.
|
// quadrinavedges exist for every one of them.
|
||||||
for constraint in constraints.iter() {
|
for constraint in prenavmesh.constraints() {
|
||||||
Self::add_trianedge_to_graph_as_quadrinavedge(
|
Self::add_trianedge_to_graph_as_quadrinavedge(
|
||||||
&mut graph,
|
&mut graph,
|
||||||
&map,
|
&map,
|
||||||
|
|
@ -462,15 +264,14 @@ impl Navmesh {
|
||||||
origin_navnode: NavnodeIndex(origin_navnode.unwrap()),
|
origin_navnode: NavnodeIndex(origin_navnode.unwrap()),
|
||||||
destination,
|
destination,
|
||||||
destination_navnode: NavnodeIndex(destination_navnode.unwrap()),
|
destination_navnode: NavnodeIndex(destination_navnode.unwrap()),
|
||||||
triangulation,
|
prenavmesh,
|
||||||
constraints,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_trianvertex_to_graph_and_map_as_binavnode(
|
fn add_trianvertex_to_graph_and_map_as_binavnode(
|
||||||
graph: &mut UnGraph<NavnodeWeight, (), usize>,
|
graph: &mut UnGraph<NavnodeWeight, (), usize>,
|
||||||
map: &mut BTreeMap<TrianvertexNodeIndex, Vec<(NodeIndex<usize>, NodeIndex<usize>)>>,
|
map: &mut BTreeMap<PrenavmeshNodeIndex, Vec<(NodeIndex<usize>, NodeIndex<usize>)>>,
|
||||||
trianvertex: TrianvertexNodeIndex,
|
trianvertex: PrenavmeshNodeIndex,
|
||||||
node: BinavnodeNodeIndex,
|
node: BinavnodeNodeIndex,
|
||||||
) {
|
) {
|
||||||
let navnode1 = graph.add_node(NavnodeWeight {
|
let navnode1 = graph.add_node(NavnodeWeight {
|
||||||
|
|
@ -490,9 +291,9 @@ impl Navmesh {
|
||||||
|
|
||||||
fn add_trianedge_to_graph_as_quadrinavedge(
|
fn add_trianedge_to_graph_as_quadrinavedge(
|
||||||
graph: &mut UnGraph<NavnodeWeight, (), usize>,
|
graph: &mut UnGraph<NavnodeWeight, (), usize>,
|
||||||
map: &BTreeMap<TrianvertexNodeIndex, Vec<(NodeIndex<usize>, NodeIndex<usize>)>>,
|
map: &BTreeMap<PrenavmeshNodeIndex, Vec<(NodeIndex<usize>, NodeIndex<usize>)>>,
|
||||||
from_trianvertex: TrianvertexNodeIndex,
|
from_trianvertex: PrenavmeshNodeIndex,
|
||||||
to_trianvertex: TrianvertexNodeIndex,
|
to_trianvertex: PrenavmeshNodeIndex,
|
||||||
) {
|
) {
|
||||||
for (from_navnode1, from_navnode2) in map[&from_trianvertex].iter() {
|
for (from_navnode1, from_navnode2) in map[&from_trianvertex].iter() {
|
||||||
for (to_navnode1, to_navnode2) in map[&to_trianvertex].iter() {
|
for (to_navnode1, to_navnode2) in map[&to_trianvertex].iter() {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,223 @@
|
||||||
|
// SPDX-FileCopyrightText: 2025 Topola contributors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
|
use derive_getters::Getters;
|
||||||
|
use enum_dispatch::enum_dispatch;
|
||||||
|
use geo::Point;
|
||||||
|
use petgraph::{stable_graph::NodeIndex, visit::NodeIndexable};
|
||||||
|
use spade::{HasPosition, InsertionError, Point2};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
drawing::{
|
||||||
|
bend::FixedBendIndex,
|
||||||
|
dot::{DotIndex, FixedDotIndex},
|
||||||
|
graph::{GetMaybeNet, MakePrimitive, PrimitiveIndex},
|
||||||
|
primitive::{GetCore, GetJoints, MakePrimitiveShape, Primitive},
|
||||||
|
rules::AccessRules,
|
||||||
|
seg::{FixedSegIndex, LoneLooseSegIndex, SeqLooseSegIndex},
|
||||||
|
Drawing,
|
||||||
|
},
|
||||||
|
geometry::{shape::AccessShape, GetLayer},
|
||||||
|
graph::GetPetgraphIndex,
|
||||||
|
layout::Layout,
|
||||||
|
triangulation::{GetTrianvertexNodeIndex, Triangulation},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{navmesh::NavmeshError, RouterOptions};
|
||||||
|
|
||||||
|
/// Prenavmesh nodes are the vertices of constrained Delaunay triangulation
|
||||||
|
/// before it is converted to the navmesh, which is done by multiplying each
|
||||||
|
/// of the prenavmesh nodes into more nodes, called navnodes.
|
||||||
|
#[enum_dispatch(GetPetgraphIndex, MakePrimitive)]
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
|
pub enum PrenavmeshNodeIndex {
|
||||||
|
FixedDot(FixedDotIndex),
|
||||||
|
FixedBend(FixedBendIndex),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct PrenavmeshWeight {
|
||||||
|
pub node: PrenavmeshNodeIndex,
|
||||||
|
pub pos: Point,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GetTrianvertexNodeIndex<PrenavmeshNodeIndex> for PrenavmeshWeight {
|
||||||
|
fn node_index(&self) -> PrenavmeshNodeIndex {
|
||||||
|
self.node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasPosition for PrenavmeshWeight {
|
||||||
|
type Scalar = f64;
|
||||||
|
fn position(&self) -> Point2<Self::Scalar> {
|
||||||
|
Point2::new(self.pos.x(), self.pos.y())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrenavmeshWeight {
|
||||||
|
pub fn new_from_fixed_dot(layout: &Layout<impl AccessRules>, dot: FixedDotIndex) -> Self {
|
||||||
|
Self {
|
||||||
|
node: dot.into(),
|
||||||
|
pos: dot.primitive(layout.drawing()).shape().center(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_from_fixed_bend(layout: &Layout<impl AccessRules>, bend: FixedBendIndex) -> Self {
|
||||||
|
Self {
|
||||||
|
node: bend.into(),
|
||||||
|
pos: bend.primitive(layout.drawing()).shape().center(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct PrenavmeshConstraint(pub PrenavmeshWeight, pub PrenavmeshWeight);
|
||||||
|
|
||||||
|
impl PrenavmeshConstraint {
|
||||||
|
pub fn new_from_fixed_dot_pair(
|
||||||
|
layout: &Layout<impl AccessRules>,
|
||||||
|
from_dot: FixedDotIndex,
|
||||||
|
to_dot: FixedDotIndex,
|
||||||
|
) -> Self {
|
||||||
|
Self(
|
||||||
|
PrenavmeshWeight::new_from_fixed_dot(layout, from_dot),
|
||||||
|
PrenavmeshWeight::new_from_fixed_dot(layout, to_dot),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_from_lone_loose_seg(
|
||||||
|
layout: &Layout<impl AccessRules>,
|
||||||
|
seg: LoneLooseSegIndex,
|
||||||
|
) -> Self {
|
||||||
|
let (from_dot, to_dot) = layout.drawing().primitive(seg).joints();
|
||||||
|
Self::new_from_fixed_dot_pair(layout, from_dot, to_dot)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_from_seq_loose_seg(
|
||||||
|
layout: &Layout<impl AccessRules>,
|
||||||
|
seg: SeqLooseSegIndex,
|
||||||
|
) -> Self {
|
||||||
|
let (from_joint, to_joint) = layout.drawing().primitive(seg).joints();
|
||||||
|
|
||||||
|
let from_dot = match from_joint {
|
||||||
|
DotIndex::Fixed(dot) => dot,
|
||||||
|
DotIndex::Loose(dot) => {
|
||||||
|
let bend = layout.drawing().primitive(dot).bend();
|
||||||
|
|
||||||
|
layout.drawing().primitive(bend).core()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let to_bend = layout.drawing().primitive(to_joint).bend();
|
||||||
|
let to_dot = layout.drawing().primitive(to_bend).core();
|
||||||
|
Self::new_from_fixed_dot_pair(layout, from_dot, to_dot)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_from_fixed_seg(layout: &Layout<impl AccessRules>, seg: FixedSegIndex) -> Self {
|
||||||
|
let (from_dot, to_dot) = layout.drawing().primitive(seg).joints();
|
||||||
|
Self::new_from_fixed_dot_pair(layout, from_dot, to_dot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Getters)]
|
||||||
|
pub struct Prenavmesh {
|
||||||
|
triangulation: Triangulation<PrenavmeshNodeIndex, PrenavmeshWeight, ()>,
|
||||||
|
constraints: Vec<PrenavmeshConstraint>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Prenavmesh {
|
||||||
|
pub fn new(
|
||||||
|
layout: &Layout<impl AccessRules>,
|
||||||
|
origin: FixedDotIndex,
|
||||||
|
destination: FixedDotIndex,
|
||||||
|
_options: RouterOptions,
|
||||||
|
) -> Result<Self, NavmeshError> {
|
||||||
|
let mut this = Self {
|
||||||
|
triangulation: Triangulation::new(layout.drawing().geometry().graph().node_bound()),
|
||||||
|
constraints: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
let layer = layout.drawing().primitive(origin).layer();
|
||||||
|
let maybe_net = layout.drawing().primitive(origin).maybe_net();
|
||||||
|
|
||||||
|
for node in layout.drawing().layer_primitive_nodes(layer) {
|
||||||
|
let primitive = node.primitive(layout.drawing());
|
||||||
|
|
||||||
|
if let Some(primitive_net) = primitive.maybe_net() {
|
||||||
|
if node == origin.into()
|
||||||
|
|| node == destination.into()
|
||||||
|
|| Some(primitive_net) != maybe_net
|
||||||
|
{
|
||||||
|
match node {
|
||||||
|
PrimitiveIndex::FixedDot(dot) => {
|
||||||
|
this.triangulation
|
||||||
|
.add_vertex(PrenavmeshWeight::new_from_fixed_dot(layout, dot))?;
|
||||||
|
}
|
||||||
|
PrimitiveIndex::LoneLooseSeg(seg) => {
|
||||||
|
this.add_constraint(PrenavmeshConstraint::new_from_lone_loose_seg(
|
||||||
|
layout, seg,
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
PrimitiveIndex::SeqLooseSeg(seg) => {
|
||||||
|
this.add_constraint(PrenavmeshConstraint::new_from_seq_loose_seg(
|
||||||
|
layout, seg,
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
PrimitiveIndex::FixedBend(bend) => {
|
||||||
|
this.triangulation
|
||||||
|
.add_vertex(PrenavmeshWeight::new_from_fixed_bend(layout, bend))?;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for node in layout.drawing().layer_primitive_nodes(layer) {
|
||||||
|
let primitive = node.primitive(layout.drawing());
|
||||||
|
|
||||||
|
if let Some(primitive_net) = primitive.maybe_net() {
|
||||||
|
if node == origin.into()
|
||||||
|
|| node == destination.into()
|
||||||
|
|| Some(primitive_net) != maybe_net
|
||||||
|
{
|
||||||
|
// If you have a band that was routed from a polygonal pad,
|
||||||
|
// when you will start a new routing some of the constraint
|
||||||
|
// edges created from the loose segs of a band will
|
||||||
|
// intersect some of the constraint edges created from the
|
||||||
|
// fixed segs constituting the pad boundary.
|
||||||
|
//
|
||||||
|
// Such constraint intersections are erroneous and cause
|
||||||
|
// Spade to throw a panic at runtime. So, to prevent this
|
||||||
|
// from occuring, we iterate over the layout for the second
|
||||||
|
// time, after all the constraint edges from bands have been
|
||||||
|
// placed, and only then add constraint edges created from
|
||||||
|
// fixed segs that do not cause an intersection.
|
||||||
|
match node {
|
||||||
|
PrimitiveIndex::FixedSeg(seg) => {
|
||||||
|
let constraint = PrenavmeshConstraint::new_from_fixed_seg(layout, seg);
|
||||||
|
|
||||||
|
if !this
|
||||||
|
.triangulation
|
||||||
|
.intersects_constraint(&constraint.0, &constraint.1)
|
||||||
|
{
|
||||||
|
this.add_constraint(constraint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_constraint(&mut self, constraint: PrenavmeshConstraint) -> Result<(), InsertionError> {
|
||||||
|
self.triangulation
|
||||||
|
.add_constraint_edge(constraint.0, constraint.1)?;
|
||||||
|
self.constraints.push(constraint);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue