router: create new `Router` object, a wrapper over `Layout`

This commit is contained in:
Mikolaj Wielgus 2024-06-28 14:44:46 +02:00
parent 244367c4d7
commit da1438195a
4 changed files with 208 additions and 137 deletions

View File

@ -15,7 +15,7 @@ use crate::{
Layout, NodeIndex, Layout, NodeIndex,
}, },
math::Circle, math::Circle,
router::{navmesh::Navmesh, Route, RouterError}, router::{navmesh::Navmesh, route::Route, Router, RouterError},
}; };
#[derive(Debug)] #[derive(Debug)]
@ -129,8 +129,8 @@ impl<M: MesadataTrait> Board<M> {
.unwrap() .unwrap()
.to_string(); .to_string();
let mut router = Route::new_from_navmesh(self.layout_mut(), navmesh, 100.0); let mut router = Router::new(self.layout_mut());
let result = router.route_band(self.layout_mut(), 100.0); let result = router.route(navmesh.source(), navmesh.target(), width);
if let Ok(band) = result { if let Ok(band) = result {
self.pinname_pair_to_band self.pinname_pair_to_band

View File

@ -2,7 +2,8 @@ pub mod astar;
pub mod draw; pub mod draw;
pub mod navmesh; pub mod navmesh;
pub mod route; pub mod route;
pub mod router;
pub mod trace; pub mod trace;
pub mod tracer; pub mod tracer;
//pub use router::*; pub use router::*;

View File

@ -27,141 +27,28 @@ use crate::{
}, },
trace::Trace, trace::Trace,
tracer::Tracer, tracer::Tracer,
Router, RouterAstarStrategy, RouterError, RouterStatus,
}, },
}; };
#[derive(Error, Debug, Clone)]
#[error("routing failed")]
pub enum RouterError {
Navmesh(#[from] NavmeshError),
Astar(#[from] AstarError),
}
pub struct Route { pub struct Route {
astar: Astar<Navmesh, f64>, astar: Astar<Navmesh, f64>,
trace: Trace, trace: Trace,
} }
struct RouterAstarStrategy<'a, R: RulesTrait> {
pub tracer: Tracer<'a, R>,
pub trace: &'a mut Trace,
pub target: FixedDotIndex,
}
impl<'a, R: RulesTrait> RouterAstarStrategy<'a, R> {
pub fn new(tracer: Tracer<'a, R>, trace: &'a mut Trace, target: FixedDotIndex) -> Self {
Self {
tracer,
trace,
target,
}
}
fn bihead_length(&self) -> f64 {
self.head_length(&self.trace.head)
+ match self.trace.head.face() {
DotIndex::Fixed(..) => 0.0,
DotIndex::Loose(face) => {
self.head_length(&self.tracer.layout.drawing().guide().rear_head(face))
}
}
}
fn head_length(&self, head: &Head) -> f64 {
match head {
Head::Bare(..) => 0.0,
Head::Cane(cane_head) => {
self.tracer
.layout
.drawing()
.primitive(cane_head.cane.seg)
.shape()
.length()
+ self
.tracer
.layout
.drawing()
.primitive(cane_head.cane.bend)
.shape()
.length()
}
}
}
}
impl<'a, R: RulesTrait> AstarStrategy<Navmesh, f64, BandFirstSegIndex>
for RouterAstarStrategy<'a, R>
{
fn is_goal(
&mut self,
navmesh: &Navmesh,
vertex: NavvertexIndex,
tracker: &PathTracker<Navmesh>,
) -> Option<BandFirstSegIndex> {
let new_path = tracker.reconstruct_path_to(vertex);
let width = self.trace.width;
self.tracer
.rework_path(navmesh, &mut self.trace, &new_path[..], width)
.unwrap();
self.tracer
.finish(navmesh, &mut self.trace, self.target, width)
.ok()
}
fn edge_cost(&mut self, navmesh: &Navmesh, edge: NavmeshEdgeReference) -> Option<f64> {
if edge.target().petgraph_index() == self.target.petgraph_index() {
return None;
}
let prev_bihead_length = self.bihead_length();
let width = self.trace.width;
let result = self
.trace
.step(&mut self.tracer, navmesh, edge.target(), width);
let probe_length = self.bihead_length() - prev_bihead_length;
if result.is_ok() {
self.trace.undo_step(&mut self.tracer);
Some(probe_length)
} else {
None
}
}
fn estimate_cost(&mut self, navmesh: &Navmesh, vertex: NavvertexIndex) -> f64 {
let start_point = PrimitiveIndex::from(navmesh.node_weight(vertex).unwrap().node)
.primitive(self.tracer.layout.drawing())
.shape()
.center();
let end_point = self
.tracer
.layout
.drawing()
.primitive(self.target)
.shape()
.center();
end_point.euclidean_distance(&start_point)
}
}
impl Route { impl Route {
pub fn new( pub fn new(
layout: &mut Layout<impl RulesTrait>, router: &mut Router<impl RulesTrait>,
from: FixedDotIndex, from: FixedDotIndex,
to: FixedDotIndex, to: FixedDotIndex,
width: f64, width: f64,
) -> Result<Self, RouterError> { ) -> Result<Self, RouterError> {
let navmesh = Navmesh::new(layout, from, to)?; let navmesh = Navmesh::new(router.layout(), from, to)?;
Ok(Self::new_from_navmesh(layout, navmesh, width)) Ok(Self::new_from_navmesh(router, navmesh, width))
} }
pub fn new_from_navmesh( pub fn new_from_navmesh(
layout: &mut Layout<impl RulesTrait>, router: &mut Router<impl RulesTrait>,
navmesh: Navmesh, navmesh: Navmesh,
width: f64, width: f64,
) -> Self { ) -> Self {
@ -169,7 +56,7 @@ impl Route {
let source_navvertex = navmesh.source_navvertex(); let source_navvertex = navmesh.source_navvertex();
let target = navmesh.target(); let target = navmesh.target();
let mut tracer = Tracer::new(layout); let mut tracer = Tracer::new(router.layout_mut());
let mut trace = tracer.start(source, source_navvertex, width); let mut trace = tracer.start(source, source_navvertex, width);
let mut strategy = RouterAstarStrategy::new(tracer, &mut trace, target); let mut strategy = RouterAstarStrategy::new(tracer, &mut trace, target);
@ -178,24 +65,17 @@ impl Route {
Self { astar, trace } Self { astar, trace }
} }
pub fn route_band( pub fn step(
&mut self, &mut self,
layout: &mut Layout<impl RulesTrait>, router: &mut Router<impl RulesTrait>,
_width: f64, ) -> Result<RouterStatus, RouterError> {
) -> Result<BandFirstSegIndex, RouterError> { let tracer = Tracer::new(router.layout_mut());
let tracer = Tracer::new(layout);
let target = self.astar.graph.target(); let target = self.astar.graph.target();
let mut strategy = RouterAstarStrategy::new(tracer, &mut self.trace, target); let mut strategy = RouterAstarStrategy::new(tracer, &mut self.trace, target);
loop { match self.astar.step(&mut strategy)? {
let status = match self.astar.step(&mut strategy) { AstarStatus::Running => Ok(RouterStatus::Running),
Ok(status) => status, AstarStatus::Finished(_cost, _path, band) => Ok(RouterStatus::Finished(band)),
Err(err) => return Err(err.into()),
};
if let AstarStatus::Finished(_cost, _path, band) = status {
return Ok(band);
}
} }
} }
} }

190
src/router/router.rs Normal file
View File

@ -0,0 +1,190 @@
use geo::EuclideanDistance;
use petgraph::{data::DataMap, visit::EdgeRef};
use thiserror::Error;
use crate::{
drawing::{
band::BandFirstSegIndex,
dot::{DotIndex, FixedDotIndex},
graph::{MakePrimitive, PrimitiveIndex},
guide::{Head, HeadTrait},
primitive::MakePrimitiveShape,
rules::RulesTrait,
},
geometry::{primitive::PrimitiveShapeTrait, shape::ShapeTrait},
graph::GetPetgraphIndex,
layout::Layout,
router::{
astar::{AstarError, AstarStatus, AstarStrategy, PathTracker},
navmesh::{Navmesh, NavmeshEdgeReference, NavmeshError, NavvertexIndex},
route::Route,
trace::Trace,
tracer::Tracer,
},
};
#[derive(Error, Debug, Clone)]
#[error("routing failed")]
pub enum RouterError {
Navmesh(#[from] NavmeshError),
Astar(#[from] AstarError),
}
pub enum RouterStatus {
Running,
Finished(BandFirstSegIndex),
}
pub struct RouterAstarStrategy<'a, R: RulesTrait> {
pub tracer: Tracer<'a, R>,
pub trace: &'a mut Trace,
pub target: FixedDotIndex,
}
impl<'a, R: RulesTrait> RouterAstarStrategy<'a, R> {
pub fn new(tracer: Tracer<'a, R>, trace: &'a mut Trace, target: FixedDotIndex) -> Self {
Self {
tracer,
trace,
target,
}
}
fn bihead_length(&self) -> f64 {
self.head_length(&self.trace.head)
+ match self.trace.head.face() {
DotIndex::Fixed(..) => 0.0,
DotIndex::Loose(face) => {
self.head_length(&self.tracer.layout.drawing().guide().rear_head(face))
}
}
}
fn head_length(&self, head: &Head) -> f64 {
match head {
Head::Bare(..) => 0.0,
Head::Cane(cane_head) => {
self.tracer
.layout
.drawing()
.primitive(cane_head.cane.seg)
.shape()
.length()
+ self
.tracer
.layout
.drawing()
.primitive(cane_head.cane.bend)
.shape()
.length()
}
}
}
}
impl<'a, R: RulesTrait> AstarStrategy<Navmesh, f64, BandFirstSegIndex>
for RouterAstarStrategy<'a, R>
{
fn is_goal(
&mut self,
navmesh: &Navmesh,
vertex: NavvertexIndex,
tracker: &PathTracker<Navmesh>,
) -> Option<BandFirstSegIndex> {
let new_path = tracker.reconstruct_path_to(vertex);
let width = self.trace.width;
self.tracer
.rework_path(navmesh, &mut self.trace, &new_path[..], width)
.unwrap();
self.tracer
.finish(navmesh, &mut self.trace, self.target, width)
.ok()
}
fn edge_cost(&mut self, navmesh: &Navmesh, edge: NavmeshEdgeReference) -> Option<f64> {
if edge.target().petgraph_index() == self.target.petgraph_index() {
return None;
}
let prev_bihead_length = self.bihead_length();
let width = self.trace.width;
let result = self
.trace
.step(&mut self.tracer, navmesh, edge.target(), width);
let probe_length = self.bihead_length() - prev_bihead_length;
if result.is_ok() {
self.trace.undo_step(&mut self.tracer);
Some(probe_length)
} else {
None
}
}
fn estimate_cost(&mut self, navmesh: &Navmesh, vertex: NavvertexIndex) -> f64 {
let start_point = PrimitiveIndex::from(navmesh.node_weight(vertex).unwrap().node)
.primitive(self.tracer.layout.drawing())
.shape()
.center();
let end_point = self
.tracer
.layout
.drawing()
.primitive(self.target)
.shape()
.center();
end_point.euclidean_distance(&start_point)
}
}
pub struct Router<'a, R: RulesTrait> {
layout: &'a mut Layout<R>,
}
impl<'a, R: RulesTrait> Router<'a, R> {
pub fn new(layout: &'a mut Layout<R>) -> Self {
Self { layout }
}
pub fn route(
&mut self,
from: FixedDotIndex,
to: FixedDotIndex,
width: f64,
) -> Result<BandFirstSegIndex, RouterError> {
let mut route = self.route_walk(from, to, width)?;
loop {
let status = match route.step(self) {
Ok(status) => status,
Err(err) => return Err(err),
};
if let RouterStatus::Finished(band) = status {
return Ok(band);
}
}
}
pub fn route_walk(
&mut self,
from: FixedDotIndex,
to: FixedDotIndex,
width: f64,
) -> Result<Route, RouterError> {
Route::new(self, from, to, width)
}
pub fn layout_mut(&mut self) -> &mut Layout<R> {
&mut self.layout
}
pub fn layout(&self) -> &Layout<R> {
&self.layout
}
}