mirror of https://codeberg.org/topola/topola.git
refactor(router/navmesh): Factor out some repeating code
This commit is contained in:
parent
ebd115c3dd
commit
9742740b9e
|
|
@ -24,7 +24,7 @@ use topola::{
|
||||||
layout::poly::MakePolygon,
|
layout::poly::MakePolygon,
|
||||||
math::{Circle, RotationSense},
|
math::{Circle, RotationSense},
|
||||||
router::{
|
router::{
|
||||||
navmesh::{BinavnodeNodeIndex, NavnodeIndex},
|
navmesh::{BinavnodeNodeIndex, NavmeshTriangulationConstraint, NavnodeIndex},
|
||||||
ng::pie,
|
ng::pie,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -443,7 +443,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 (from_weight, to_weight) in navmesh.constraints() {
|
for NavmeshTriangulationConstraint(from_weight, to_weight) in
|
||||||
|
navmesh.constraints()
|
||||||
|
{
|
||||||
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();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ use crate::{
|
||||||
graph::{GetMaybeNet, MakePrimitive, PrimitiveIndex},
|
graph::{GetMaybeNet, MakePrimitive, PrimitiveIndex},
|
||||||
primitive::{GetCore, GetJoints, MakePrimitiveShape, Primitive},
|
primitive::{GetCore, GetJoints, MakePrimitiveShape, Primitive},
|
||||||
rules::AccessRules,
|
rules::AccessRules,
|
||||||
|
seg::{FixedSegIndex, LoneLooseSegIndex, SegIndex, SeqLooseSegIndex},
|
||||||
Drawing,
|
Drawing,
|
||||||
},
|
},
|
||||||
geometry::{shape::AccessShape, GetLayer},
|
geometry::{shape::AccessShape, GetLayer},
|
||||||
|
|
@ -107,12 +108,28 @@ impl From<TrianvertexNodeIndex> for BinavnodeNodeIndex {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct TrianvertexWeight {
|
pub struct TrianvertexWeight {
|
||||||
pub node: TrianvertexNodeIndex,
|
pub node: TrianvertexNodeIndex,
|
||||||
pub pos: Point,
|
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 {
|
impl GetTrianvertexNodeIndex<TrianvertexNodeIndex> for TrianvertexWeight {
|
||||||
fn node_index(&self) -> TrianvertexNodeIndex {
|
fn node_index(&self) -> TrianvertexNodeIndex {
|
||||||
self.node
|
self.node
|
||||||
|
|
@ -126,6 +143,49 @@ impl HasPosition for TrianvertexWeight {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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.
|
||||||
///
|
///
|
||||||
|
|
@ -148,6 +208,8 @@ 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
|
||||||
|
|
@ -169,10 +231,10 @@ pub struct Navmesh {
|
||||||
|
|
||||||
/// Original triangulation stored for debugging purposes.
|
/// Original triangulation stored for debugging purposes.
|
||||||
// XXX: Maybe have a way to compile this out in release?
|
// XXX: Maybe have a way to compile this out in release?
|
||||||
triangulation: Triangulation<TrianvertexNodeIndex, TrianvertexWeight, ()>,
|
triangulation: NavmeshTriangulation,
|
||||||
// Original triangulation constraints stored for debugging purposes.
|
// Original triangulation constraints stored for debugging purposes.
|
||||||
// XXX: Maybe have a way to compile this out in release?
|
// XXX: Maybe have a way to compile this out in release?
|
||||||
constraints: Vec<(TrianvertexWeight, TrianvertexWeight)>,
|
constraints: Vec<NavmeshTriangulationConstraint>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Navmesh {
|
impl Navmesh {
|
||||||
|
|
@ -183,8 +245,8 @@ impl Navmesh {
|
||||||
destination: FixedDotIndex,
|
destination: FixedDotIndex,
|
||||||
options: RouterOptions,
|
options: RouterOptions,
|
||||||
) -> Result<Self, NavmeshError> {
|
) -> Result<Self, NavmeshError> {
|
||||||
let mut triangulation: Triangulation<TrianvertexNodeIndex, TrianvertexWeight, ()> =
|
let mut triangulation: NavmeshTriangulation =
|
||||||
Triangulation::new(layout.drawing().geometry().graph().node_bound());
|
NavmeshTriangulation::new(layout.drawing().geometry().graph().node_bound());
|
||||||
let mut constraints = vec![];
|
let mut constraints = vec![];
|
||||||
|
|
||||||
let layer = layout.drawing().primitive(origin).layer();
|
let layer = layout.drawing().primitive(origin).layer();
|
||||||
|
|
@ -200,62 +262,28 @@ impl Navmesh {
|
||||||
{
|
{
|
||||||
match node {
|
match node {
|
||||||
PrimitiveIndex::FixedDot(dot) => {
|
PrimitiveIndex::FixedDot(dot) => {
|
||||||
triangulation.add_vertex(TrianvertexWeight {
|
triangulation
|
||||||
node: dot.into(),
|
.add_vertex(TrianvertexWeight::new_from_fixed_dot(layout, dot))?;
|
||||||
pos: primitive.shape().center(),
|
|
||||||
})?;
|
|
||||||
}
|
}
|
||||||
PrimitiveIndex::LoneLooseSeg(seg) => {
|
PrimitiveIndex::LoneLooseSeg(seg) => {
|
||||||
let (from_dot, to_dot) = layout.drawing().primitive(seg).joints();
|
Self::add_constraint(
|
||||||
let (from_weight, to_weight) = (
|
&mut triangulation,
|
||||||
TrianvertexWeight {
|
&mut constraints,
|
||||||
node: from_dot.into(),
|
NavmeshTriangulationConstraint::new_from_lone_loose_seg(
|
||||||
pos: from_dot.primitive(layout.drawing()).shape().center(),
|
layout, seg,
|
||||||
},
|
),
|
||||||
TrianvertexWeight {
|
)?;
|
||||||
node: to_dot.into(),
|
|
||||||
pos: to_dot.primitive(layout.drawing()).shape().center(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
triangulation
|
|
||||||
.add_constraint_edge(from_weight.clone(), to_weight.clone())?;
|
|
||||||
constraints.push((from_weight, to_weight));
|
|
||||||
}
|
}
|
||||||
PrimitiveIndex::SeqLooseSeg(seg) => {
|
PrimitiveIndex::SeqLooseSeg(seg) => {
|
||||||
let (from_joint, to_joint) = layout.drawing().primitive(seg).joints();
|
Self::add_constraint(
|
||||||
|
&mut triangulation,
|
||||||
let from_dot = match from_joint {
|
&mut constraints,
|
||||||
DotIndex::Fixed(dot) => dot,
|
NavmeshTriangulationConstraint::new_from_seq_loose_seg(layout, seg),
|
||||||
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();
|
|
||||||
let (from_weight, to_weight) = (
|
|
||||||
TrianvertexWeight {
|
|
||||||
node: from_dot.into(),
|
|
||||||
pos: from_dot.primitive(layout.drawing()).shape().center(),
|
|
||||||
},
|
|
||||||
TrianvertexWeight {
|
|
||||||
node: to_dot.into(),
|
|
||||||
pos: to_dot.primitive(layout.drawing()).shape().center(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
triangulation
|
|
||||||
.add_constraint_edge(from_weight.clone(), to_weight.clone())?;
|
|
||||||
constraints.push((from_weight, to_weight));
|
|
||||||
}
|
}
|
||||||
PrimitiveIndex::FixedBend(bend) => {
|
PrimitiveIndex::FixedBend(bend) => {
|
||||||
triangulation.add_vertex(TrianvertexWeight {
|
triangulation
|
||||||
node: bend.into(),
|
.add_vertex(TrianvertexWeight::new_from_fixed_bend(layout, bend))?;
|
||||||
pos: primitive.shape().center(),
|
|
||||||
})?;
|
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
@ -272,33 +300,28 @@ impl Navmesh {
|
||||||
|| Some(primitive_net) != maybe_net
|
|| Some(primitive_net) != maybe_net
|
||||||
{
|
{
|
||||||
// If you have a band that was routed from a polygonal pad,
|
// If you have a band that was routed from a polygonal pad,
|
||||||
// upon another routing some of the constraint edges created
|
// when you will start a new routing some of the constraint
|
||||||
// from the loose segs band will intersect some of the
|
// edges created from the loose segs of a band will
|
||||||
// constraint edges created from the fixed segs constituting
|
// intersect some of the constraint edges created from the
|
||||||
// the pad boundary.
|
// fixed segs constituting the pad boundary.
|
||||||
//
|
//
|
||||||
// Such constraint intersections are erroneous and cause
|
// Such constraint intersections are erroneous and cause
|
||||||
// Spade to throw a panic at runtime. So, to prevent this
|
// Spade to throw a panic at runtime. So, to prevent this
|
||||||
// from occuring, we iterate over the layout for the second
|
// from occuring, we iterate over the layout for the second
|
||||||
// time, after all the constraint edges from bands have
|
// time, after all the constraint edges from bands have been
|
||||||
// been placed, and only then add constraint edges created
|
// placed, and only then add constraint edges created from
|
||||||
// from fixed segs, but only ones that do not cause an
|
// fixed segs that do not cause an intersection.
|
||||||
// intersection.
|
|
||||||
match node {
|
match node {
|
||||||
PrimitiveIndex::FixedSeg(seg) => {
|
PrimitiveIndex::FixedSeg(seg) => {
|
||||||
let (from_dot, to_dot) = layout.drawing().primitive(seg).joints();
|
let constraint =
|
||||||
|
NavmeshTriangulationConstraint::new_from_fixed_seg(layout, seg);
|
||||||
|
|
||||||
let from_weight = TrianvertexWeight {
|
if !triangulation.intersects_constraint(&constraint.0, &constraint.1) {
|
||||||
node: from_dot.into(),
|
Self::add_constraint(
|
||||||
pos: from_dot.primitive(layout.drawing()).shape().center(),
|
&mut triangulation,
|
||||||
};
|
&mut constraints,
|
||||||
let to_weight = TrianvertexWeight {
|
constraint,
|
||||||
node: to_dot.into(),
|
);
|
||||||
pos: to_dot.primitive(layout.drawing()).shape().center(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if !triangulation.intersects_constraint(&from_weight, &to_weight) {
|
|
||||||
triangulation.add_constraint_edge(from_weight, to_weight)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
|
|
@ -317,12 +340,22 @@ impl Navmesh {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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_triangulation(
|
||||||
layout: &Layout<impl AccessRules>,
|
layout: &Layout<impl AccessRules>,
|
||||||
triangulation: Triangulation<TrianvertexNodeIndex, TrianvertexWeight, ()>,
|
triangulation: NavmeshTriangulation,
|
||||||
origin: FixedDotIndex,
|
origin: FixedDotIndex,
|
||||||
destination: FixedDotIndex,
|
destination: FixedDotIndex,
|
||||||
constraints: Vec<(TrianvertexWeight, TrianvertexWeight)>,
|
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();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue