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/poly",
|
||||
"router/ng/router",
|
||||
"router/prenavmesh",
|
||||
"router/route",
|
||||
"router/router",
|
||||
"router/thetastar",
|
||||
|
|
|
|||
|
|
@ -24,8 +24,9 @@ use topola::{
|
|||
layout::poly::MakePolygon,
|
||||
math::{Circle, RotationSense},
|
||||
router::{
|
||||
navmesh::{BinavnodeNodeIndex, NavmeshTriangulationConstraint, NavnodeIndex},
|
||||
navmesh::{BinavnodeNodeIndex, NavnodeIndex},
|
||||
ng::pie,
|
||||
prenavmesh::PrenavmeshConstraint,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -411,7 +412,9 @@ impl Viewport {
|
|||
if let Some(thetastar) = activity.maybe_thetastar() {
|
||||
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(
|
||||
edge.source(),
|
||||
))
|
||||
|
|
@ -443,8 +446,8 @@ impl Viewport {
|
|||
if let Some(thetastar) = activity.maybe_thetastar() {
|
||||
let navmesh = thetastar.graph();
|
||||
|
||||
for NavmeshTriangulationConstraint(from_weight, to_weight) in
|
||||
navmesh.constraints()
|
||||
for PrenavmeshConstraint(from_weight, to_weight) in
|
||||
navmesh.prenavmesh().constraints().iter()
|
||||
{
|
||||
let from = from_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 navmesh;
|
||||
pub mod ng;
|
||||
pub mod prenavmesh;
|
||||
mod route;
|
||||
mod router;
|
||||
pub mod thetastar;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ use std::collections::BTreeMap;
|
|||
|
||||
use derive_getters::Getters;
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use geo::Point;
|
||||
use petgraph::{
|
||||
data::DataMap,
|
||||
graph::UnGraph,
|
||||
|
|
@ -16,29 +15,31 @@ use petgraph::{
|
|||
IntoNodeIdentifiers, NodeIndexable,
|
||||
},
|
||||
};
|
||||
use spade::{HasPosition, InsertionError, Point2};
|
||||
use spade::InsertionError;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{
|
||||
drawing::{
|
||||
bend::{FixedBendIndex, LooseBendIndex},
|
||||
dot::{DotIndex, FixedDotIndex},
|
||||
dot::FixedDotIndex,
|
||||
gear::{GearIndex, GetNextGear},
|
||||
graph::{GetMaybeNet, MakePrimitive, PrimitiveIndex},
|
||||
primitive::{GetCore, GetJoints, MakePrimitiveShape, Primitive},
|
||||
primitive::Primitive,
|
||||
rules::AccessRules,
|
||||
seg::{FixedSegIndex, LoneLooseSegIndex, SegIndex, SeqLooseSegIndex},
|
||||
Drawing,
|
||||
},
|
||||
geometry::{shape::AccessShape, GetLayer},
|
||||
geometry::GetLayer,
|
||||
graph::{GetPetgraphIndex, MakeRef},
|
||||
layout::Layout,
|
||||
math::RotationSense,
|
||||
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)]
|
||||
pub struct NavnodeIndex(pub NodeIndex<usize>);
|
||||
|
|
@ -66,6 +67,15 @@ pub enum BinavnodeNodeIndex {
|
|||
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 {
|
||||
fn from(vertex: BinavnodeNodeIndex) -> Self {
|
||||
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
|
||||
/// vertex", "navigation node" are all equivalent.
|
||||
///
|
||||
|
|
@ -208,8 +118,6 @@ pub enum NavmeshError {
|
|||
Insertion(#[from] InsertionError),
|
||||
}
|
||||
|
||||
type NavmeshTriangulation = Triangulation<TrianvertexNodeIndex, TrianvertexWeight, ()>;
|
||||
|
||||
/// 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
|
||||
/// algorithm such as A* while drawing a track segment (always a cane except
|
||||
|
|
@ -229,12 +137,8 @@ pub struct Navmesh {
|
|||
#[getter(skip)]
|
||||
destination_navnode: NavnodeIndex,
|
||||
|
||||
/// Original triangulation stored for debugging purposes.
|
||||
// XXX: Maybe have a way to compile this out in release?
|
||||
triangulation: NavmeshTriangulation,
|
||||
// Original triangulation constraints stored for debugging purposes.
|
||||
// XXX: Maybe have a way to compile this out in release?
|
||||
constraints: Vec<NavmeshTriangulationConstraint>,
|
||||
/// Original constrainted triangulation stored for debugging purposes.
|
||||
prenavmesh: Prenavmesh,
|
||||
}
|
||||
|
||||
impl Navmesh {
|
||||
|
|
@ -245,117 +149,15 @@ impl Navmesh {
|
|||
destination: FixedDotIndex,
|
||||
options: RouterOptions,
|
||||
) -> Result<Self, NavmeshError> {
|
||||
let mut triangulation: NavmeshTriangulation =
|
||||
NavmeshTriangulation::new(layout.drawing().geometry().graph().node_bound());
|
||||
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,
|
||||
)
|
||||
let prenavmesh = Prenavmesh::new(layout, origin, destination, options)?;
|
||||
Self::new_from_prenavmesh(layout, prenavmesh, origin, destination, options)
|
||||
}
|
||||
|
||||
fn add_constraint(
|
||||
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(
|
||||
fn new_from_prenavmesh(
|
||||
layout: &Layout<impl AccessRules>,
|
||||
triangulation: NavmeshTriangulation,
|
||||
prenavmesh: Prenavmesh,
|
||||
origin: FixedDotIndex,
|
||||
destination: FixedDotIndex,
|
||||
constraints: Vec<NavmeshTriangulationConstraint>,
|
||||
options: RouterOptions,
|
||||
) -> Result<Self, NavmeshError> {
|
||||
let mut graph: UnGraph<NavnodeWeight, (), usize> = UnGraph::default();
|
||||
|
|
@ -364,7 +166,7 @@ impl Navmesh {
|
|||
|
||||
let mut map = BTreeMap::new();
|
||||
|
||||
for trianvertex in triangulation.node_identifiers() {
|
||||
for trianvertex in prenavmesh.triangulation().node_identifiers() {
|
||||
if trianvertex == origin.into() {
|
||||
let navnode = graph.add_node(NavnodeWeight {
|
||||
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(
|
||||
&mut graph,
|
||||
&map,
|
||||
|
|
@ -447,7 +249,7 @@ impl Navmesh {
|
|||
//
|
||||
// So now we go over all the constraints and make sure that
|
||||
// quadrinavedges exist for every one of them.
|
||||
for constraint in constraints.iter() {
|
||||
for constraint in prenavmesh.constraints() {
|
||||
Self::add_trianedge_to_graph_as_quadrinavedge(
|
||||
&mut graph,
|
||||
&map,
|
||||
|
|
@ -462,15 +264,14 @@ impl Navmesh {
|
|||
origin_navnode: NavnodeIndex(origin_navnode.unwrap()),
|
||||
destination,
|
||||
destination_navnode: NavnodeIndex(destination_navnode.unwrap()),
|
||||
triangulation,
|
||||
constraints,
|
||||
prenavmesh,
|
||||
})
|
||||
}
|
||||
|
||||
fn add_trianvertex_to_graph_and_map_as_binavnode(
|
||||
graph: &mut UnGraph<NavnodeWeight, (), usize>,
|
||||
map: &mut BTreeMap<TrianvertexNodeIndex, Vec<(NodeIndex<usize>, NodeIndex<usize>)>>,
|
||||
trianvertex: TrianvertexNodeIndex,
|
||||
map: &mut BTreeMap<PrenavmeshNodeIndex, Vec<(NodeIndex<usize>, NodeIndex<usize>)>>,
|
||||
trianvertex: PrenavmeshNodeIndex,
|
||||
node: BinavnodeNodeIndex,
|
||||
) {
|
||||
let navnode1 = graph.add_node(NavnodeWeight {
|
||||
|
|
@ -490,9 +291,9 @@ impl Navmesh {
|
|||
|
||||
fn add_trianedge_to_graph_as_quadrinavedge(
|
||||
graph: &mut UnGraph<NavnodeWeight, (), usize>,
|
||||
map: &BTreeMap<TrianvertexNodeIndex, Vec<(NodeIndex<usize>, NodeIndex<usize>)>>,
|
||||
from_trianvertex: TrianvertexNodeIndex,
|
||||
to_trianvertex: TrianvertexNodeIndex,
|
||||
map: &BTreeMap<PrenavmeshNodeIndex, Vec<(NodeIndex<usize>, NodeIndex<usize>)>>,
|
||||
from_trianvertex: PrenavmeshNodeIndex,
|
||||
to_trianvertex: PrenavmeshNodeIndex,
|
||||
) {
|
||||
for (from_navnode1, from_navnode2) in map[&from_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