egui,autorouter,router: display infringement shapes (aka. "ghosts")

This commit is contained in:
Mikolaj Wielgus 2024-07-03 21:00:50 +02:00
parent 727eb37c6e
commit b6cb89c017
7 changed files with 141 additions and 82 deletions

View File

@ -1,14 +1,16 @@
use petgraph::graph::EdgeIndex; use petgraph::graph::EdgeIndex;
use crate::{ use crate::{
autorouter::{
invoker::{GetMaybeNavmesh, GetMaybeTrace},
Autorouter, AutorouterError, AutorouterStatus,
},
board::mesadata::AccessMesadata, board::mesadata::AccessMesadata,
geometry::primitive::PrimitiveShape,
router::{navmesh::Navmesh, route::Route, trace::Trace, Router, RouterStatus}, router::{navmesh::Navmesh, route::Route, trace::Trace, Router, RouterStatus},
}; };
use super::{
invoker::{GetGhosts, GetMaybeNavmesh, GetMaybeTrace},
Autorouter, AutorouterError, AutorouterStatus,
};
pub struct Autoroute { pub struct Autoroute {
ratlines_iter: Box<dyn Iterator<Item = EdgeIndex<usize>>>, ratlines_iter: Box<dyn Iterator<Item = EdgeIndex<usize>>>,
route: Option<Route>, route: Option<Route>,
@ -83,14 +85,6 @@ impl Autoroute {
Ok(AutorouterStatus::Running) Ok(AutorouterStatus::Running)
} }
pub fn navmesh(&self) -> Option<&Navmesh> {
self.route.as_ref().map(|route| route.navmesh())
}
pub fn trace(&self) -> Option<&Trace> {
self.route.as_ref().map(|route| route.trace())
}
} }
impl GetMaybeNavmesh for Autoroute { impl GetMaybeNavmesh for Autoroute {
@ -104,3 +98,9 @@ impl GetMaybeTrace for Autoroute {
self.route.as_ref().map(|route| route.trace()) self.route.as_ref().map(|route| route.trace())
} }
} }
impl GetGhosts for Autoroute {
fn ghosts(&self) -> &[PrimitiveShape] {
self.route.as_ref().map_or(&[], |route| route.ghosts())
}
}

View File

@ -12,6 +12,8 @@ use crate::{
Autorouter, AutorouterError, AutorouterStatus, Autorouter, AutorouterError, AutorouterStatus,
}, },
board::mesadata::AccessMesadata, board::mesadata::AccessMesadata,
drawing::graph::PrimitiveIndex,
geometry::primitive::PrimitiveShape,
layout::via::ViaWeight, layout::via::ViaWeight,
router::{navmesh::Navmesh, trace::Trace}, router::{navmesh::Navmesh, trace::Trace},
}; };
@ -26,6 +28,11 @@ pub trait GetMaybeTrace {
fn maybe_trace(&self) -> Option<&Trace>; fn maybe_trace(&self) -> Option<&Trace>;
} }
#[enum_dispatch]
pub trait GetGhosts {
fn ghosts(&self) -> &[PrimitiveShape];
}
#[derive(Error, Debug, Clone)] #[derive(Error, Debug, Clone)]
pub enum InvokerError { pub enum InvokerError {
#[error(transparent)] #[error(transparent)]
@ -46,7 +53,7 @@ pub enum Command {
PlaceVia(ViaWeight), PlaceVia(ViaWeight),
} }
#[enum_dispatch(GetMaybeNavmesh, GetMaybeTrace)] #[enum_dispatch(GetMaybeNavmesh, GetMaybeTrace, GetGhosts)]
pub enum Execute { pub enum Execute {
Autoroute(Autoroute), Autoroute(Autoroute),
PlaceVia(PlaceVia), PlaceVia(PlaceVia),
@ -129,6 +136,12 @@ impl GetMaybeTrace for ExecuteWithStatus {
} }
} }
impl GetGhosts for ExecuteWithStatus {
fn ghosts(&self) -> &[PrimitiveShape] {
self.execute.ghosts()
}
}
pub struct Invoker<M: AccessMesadata> { pub struct Invoker<M: AccessMesadata> {
autorouter: Autorouter<M>, autorouter: Autorouter<M>,
history: History, history: History,

View File

@ -1,13 +1,16 @@
use crate::{ use crate::{
autorouter::{
invoker::{GetMaybeNavmesh, GetMaybeTrace},
Autorouter, AutorouterError,
},
board::mesadata::AccessMesadata, board::mesadata::AccessMesadata,
drawing::graph::PrimitiveIndex,
geometry::primitive::PrimitiveShape,
layout::via::ViaWeight, layout::via::ViaWeight,
router::{navmesh::Navmesh, trace::Trace}, router::{navmesh::Navmesh, trace::Trace},
}; };
use super::{
invoker::{GetGhosts, GetMaybeNavmesh, GetMaybeTrace},
Autorouter, AutorouterError,
};
#[derive(Debug)] #[derive(Debug)]
pub struct PlaceVia { pub struct PlaceVia {
weight: ViaWeight, weight: ViaWeight,
@ -37,3 +40,9 @@ impl GetMaybeTrace for PlaceVia {
None None
} }
} }
impl GetGhosts for PlaceVia {
fn ghosts(&self) -> &[PrimitiveShape] {
&[]
}
}

View File

@ -4,7 +4,9 @@ use petgraph::{
visit::{EdgeRef, IntoEdgeReferences}, visit::{EdgeRef, IntoEdgeReferences},
}; };
use topola::{ use topola::{
autorouter::invoker::{Command, ExecuteWithStatus, GetMaybeNavmesh, GetMaybeTrace, Invoker}, autorouter::invoker::{
Command, ExecuteWithStatus, GetGhosts, GetMaybeNavmesh, GetMaybeTrace, Invoker,
},
board::mesadata::AccessMesadata, board::mesadata::AccessMesadata,
drawing::{ drawing::{
graph::{MakePrimitive, PrimitiveIndex}, graph::{MakePrimitive, PrimitiveIndex},
@ -213,11 +215,11 @@ impl Viewport {
} }
} }
/*for ghost in shared_data.ghosts.iter() {
painter.paint_primitive(&ghost, egui::Color32::from_rgb(75, 75, 150));
}*/
if let Some(execute) = maybe_execute { if let Some(execute) = maybe_execute {
for ghost in execute.ghosts().iter() {
painter.paint_primitive(&ghost, egui::Color32::from_rgb(75, 75, 150));
}
if let Some(navmesh) = execute.maybe_navmesh() { if let Some(navmesh) = execute.maybe_navmesh() {
if let (origin, destination) = (navmesh.origin(), navmesh.destination()) { if let (origin, destination) = (navmesh.origin(), navmesh.destination()) {
painter.paint_dot( painter.paint_dot(

View File

@ -96,7 +96,7 @@ where
} }
} }
pub trait AstarStrategy<G, K, PS, PE, R> pub trait AstarStrategy<G, K, R>
where where
G: GraphBase, G: GraphBase,
G::NodeId: Eq + Hash, G::NodeId: Eq + Hash,
@ -108,7 +108,7 @@ where
&mut self, &mut self,
graph: &'a G, graph: &'a G,
edge: <&'a G as IntoEdgeReferences>::EdgeRef, edge: <&'a G as IntoEdgeReferences>::EdgeRef,
) -> Result<(K, PS), PE>; ) -> Option<K>;
fn estimate_cost(&mut self, graph: &G, node: G::NodeId) -> K; fn estimate_cost(&mut self, graph: &G, node: G::NodeId) -> K;
} }
@ -140,14 +140,14 @@ pub enum AstarError {
} }
#[derive(Debug)] #[derive(Debug)]
pub enum AstarStatus<G, K, PS, PE, R> pub enum AstarStatus<G, K, R>
where where
G: GraphBase, G: GraphBase,
G::NodeId: Eq + Hash, G::NodeId: Eq + Hash,
for<'a> &'a G: IntoEdges<NodeId = G::NodeId, EdgeId = G::EdgeId> + MakeEdgeRef, for<'a> &'a G: IntoEdges<NodeId = G::NodeId, EdgeId = G::EdgeId> + MakeEdgeRef,
K: Measure + Copy, K: Measure + Copy,
{ {
Probed(Result<PS, PE>), Probed,
Visited, Visited,
Finished(K, Vec<G::NodeId>, R), Finished(K, Vec<G::NodeId>, R),
} }
@ -159,11 +159,7 @@ where
for<'a> &'a G: IntoEdges<NodeId = G::NodeId, EdgeId = G::EdgeId> + MakeEdgeRef, for<'a> &'a G: IntoEdges<NodeId = G::NodeId, EdgeId = G::EdgeId> + MakeEdgeRef,
K: Measure + Copy, K: Measure + Copy,
{ {
pub fn new<PS, PE, R>( pub fn new<R>(graph: G, start: G::NodeId, strategy: &mut impl AstarStrategy<G, K, R>) -> Self {
graph: G,
start: G::NodeId,
strategy: &mut impl AstarStrategy<G, K, PS, PE, R>,
) -> Self {
let mut this = Self { let mut this = Self {
graph, graph,
visit_next: BinaryHeap::new(), visit_next: BinaryHeap::new(),
@ -183,10 +179,10 @@ where
this this
} }
pub fn step<PS, PE, R>( pub fn step<R>(
&mut self, &mut self,
strategy: &mut impl AstarStrategy<G, K, PS, PE, R>, strategy: &mut impl AstarStrategy<G, K, R>,
) -> Result<AstarStatus<G, K, PS, PE, R>, AstarError> { ) -> Result<AstarStatus<G, K, R>, AstarError> {
if let Some(curr_node) = self.maybe_curr_node { if let Some(curr_node) = self.maybe_curr_node {
if let Some(edge_id) = self.edge_ids.pop_front() { if let Some(edge_id) = self.edge_ids.pop_front() {
// This lookup can be unwrapped without fear of panic since the node was // This lookup can be unwrapped without fear of panic since the node was
@ -194,37 +190,34 @@ where
let node_score = self.scores[&curr_node]; let node_score = self.scores[&curr_node];
let edge = (&self.graph).edge_ref(edge_id); let edge = (&self.graph).edge_ref(edge_id);
match strategy.probe(&self.graph, edge) { if let Some(edge_cost) = strategy.probe(&self.graph, edge) {
Ok((edge_cost, probe_status)) => { let next = edge.target();
let next = edge.target(); let next_score = node_score + edge_cost;
let next_score = node_score + edge_cost;
match self.scores.entry(next) { match self.scores.entry(next) {
Occupied(mut entry) => { Occupied(mut entry) => {
// No need to add neighbors that we have already reached through a // No need to add neighbors that we have already reached through a
// shorter path than now. // shorter path than now.
if *entry.get() <= next_score { if *entry.get() <= next_score {
return Ok(AstarStatus::Probed(Ok(probe_status))); return Ok(AstarStatus::Probed);
}
entry.insert(next_score);
}
Vacant(entry) => {
entry.insert(next_score);
} }
entry.insert(next_score);
}
Vacant(entry) => {
entry.insert(next_score);
} }
self.path_tracker.set_predecessor(next, curr_node);
let next_estimate_score =
next_score + strategy.estimate_cost(&self.graph, next);
self.visit_next.push(MinScored(next_estimate_score, next));
return Ok(AstarStatus::Probed(Ok(probe_status)));
} }
Err(probe_err) => return Ok(AstarStatus::Probed(Err(probe_err))),
self.path_tracker.set_predecessor(next, curr_node);
let next_estimate_score =
next_score + strategy.estimate_cost(&self.graph, next);
self.visit_next.push(MinScored(next_estimate_score, next));
} }
} else {
self.maybe_curr_node = None; return Ok(AstarStatus::Probed);
} }
self.maybe_curr_node = None;
} }
let Some(MinScored(estimate_score, node)) = self.visit_next.pop() else { let Some(MinScored(estimate_score, node)) = self.visit_next.pop() else {
@ -258,10 +251,10 @@ where
} }
} }
pub fn astar<G, K, PS, PE, R>( pub fn astar<G, K, R>(
graph: G, graph: G,
start: G::NodeId, start: G::NodeId,
strategy: &mut impl AstarStrategy<G, K, PS, PE, R>, strategy: &mut impl AstarStrategy<G, K, R>,
) -> Result<(K, Vec<G::NodeId>, R), AstarError> ) -> Result<(K, Vec<G::NodeId>, R), AstarError>
where where
G: GraphBase, G: GraphBase,

View File

@ -14,7 +14,10 @@ use crate::{
primitive::MakePrimitiveShape, primitive::MakePrimitiveShape,
rules::AccessRules, rules::AccessRules,
}, },
geometry::{primitive::AccessPrimitiveShape, shape::AccessShape}, geometry::{
primitive::{AccessPrimitiveShape, PrimitiveShape},
shape::AccessShape,
},
graph::GetPetgraphIndex, graph::GetPetgraphIndex,
layout::Layout, layout::Layout,
router::{ router::{
@ -33,6 +36,7 @@ use crate::{
pub struct Route { pub struct Route {
astar: Astar<Navmesh, f64>, astar: Astar<Navmesh, f64>,
trace: Trace, trace: Trace,
ghosts: Vec<PrimitiveShape>,
} }
impl Route { impl Route {
@ -60,8 +64,13 @@ impl Route {
let mut strategy = RouterAstarStrategy::new(tracer, &mut trace, target); let mut strategy = RouterAstarStrategy::new(tracer, &mut trace, target);
let astar = Astar::new(navmesh, source_navvertex, &mut strategy); let astar = Astar::new(navmesh, source_navvertex, &mut strategy);
let ghosts = vec![];
Self { astar, trace } Self {
astar,
trace,
ghosts,
}
} }
pub fn step( pub fn step(
@ -72,10 +81,13 @@ impl Route {
let target = self.astar.graph.destination(); let target = self.astar.graph.destination();
let mut strategy = RouterAstarStrategy::new(tracer, &mut self.trace, target); let mut strategy = RouterAstarStrategy::new(tracer, &mut self.trace, target);
match self.astar.step(&mut strategy)? { let result = match self.astar.step(&mut strategy)? {
AstarStatus::Probed(..) | AstarStatus::Visited => Ok(RouterStatus::Running), AstarStatus::Probed | AstarStatus::Visited => Ok(RouterStatus::Running),
AstarStatus::Finished(_cost, _path, band) => Ok(RouterStatus::Finished(band)), AstarStatus::Finished(_cost, _path, band) => Ok(RouterStatus::Finished(band)),
} };
self.ghosts = strategy.ghosts;
result
} }
pub fn navmesh(&self) -> &Navmesh { pub fn navmesh(&self) -> &Navmesh {
@ -85,4 +97,8 @@ impl Route {
pub fn trace(&self) -> &Trace { pub fn trace(&self) -> &Trace {
&self.trace &self.trace
} }
pub fn ghosts(&self) -> &[PrimitiveShape] {
&self.ghosts
}
} }

View File

@ -10,20 +10,23 @@ use crate::{
head::{GetFace, Head}, head::{GetFace, Head},
primitive::MakePrimitiveShape, primitive::MakePrimitiveShape,
rules::AccessRules, rules::AccessRules,
Collision, Infringement, LayoutException,
}, },
geometry::{ geometry::{
primitive::AccessPrimitiveShape, primitive::{AccessPrimitiveShape, PrimitiveShape},
shape::{AccessShape, MeasureLength}, shape::{AccessShape, MeasureLength},
}, },
graph::GetPetgraphIndex, graph::GetPetgraphIndex,
layout::Layout, layout::Layout,
router::{ };
astar::{AstarError, AstarStatus, AstarStrategy, PathTracker},
navmesh::{Navmesh, NavmeshEdgeReference, NavmeshError, NavvertexIndex}, use super::{
route::Route, astar::{AstarError, AstarStrategy, PathTracker},
trace::Trace, draw::DrawException,
tracer::Tracer, navmesh::{Navmesh, NavmeshEdgeReference, NavmeshError, NavvertexIndex},
}, route::Route,
trace::Trace,
tracer::{Tracer, TracerException},
}; };
#[derive(Error, Debug, Clone)] #[derive(Error, Debug, Clone)]
@ -44,6 +47,7 @@ pub struct RouterAstarStrategy<'a, R: AccessRules> {
pub tracer: Tracer<'a, R>, pub tracer: Tracer<'a, R>,
pub trace: &'a mut Trace, pub trace: &'a mut Trace,
pub target: FixedDotIndex, pub target: FixedDotIndex,
pub ghosts: Vec<PrimitiveShape>,
} }
impl<'a, R: AccessRules> RouterAstarStrategy<'a, R> { impl<'a, R: AccessRules> RouterAstarStrategy<'a, R> {
@ -52,6 +56,7 @@ impl<'a, R: AccessRules> RouterAstarStrategy<'a, R> {
tracer, tracer,
trace, trace,
target, target,
ghosts: vec![],
} }
} }
@ -71,7 +76,7 @@ impl<'a, R: AccessRules> RouterAstarStrategy<'a, R> {
} }
} }
impl<'a, R: AccessRules> AstarStrategy<Navmesh, f64, (), (), BandFirstSegIndex> impl<'a, R: AccessRules> AstarStrategy<Navmesh, f64, BandFirstSegIndex>
for RouterAstarStrategy<'a, R> for RouterAstarStrategy<'a, R>
{ {
fn is_goal( fn is_goal(
@ -92,9 +97,9 @@ impl<'a, R: AccessRules> AstarStrategy<Navmesh, f64, (), (), BandFirstSegIndex>
.ok() .ok()
} }
fn probe(&mut self, navmesh: &Navmesh, edge: NavmeshEdgeReference) -> Result<(f64, ()), ()> { fn probe(&mut self, navmesh: &Navmesh, edge: NavmeshEdgeReference) -> Option<f64> {
if edge.target().petgraph_index() == self.target.petgraph_index() { if edge.target().petgraph_index() == self.target.petgraph_index() {
return Err(()); return None;
} }
let prev_bihead_length = self.bihead_length(); let prev_bihead_length = self.bihead_length();
@ -106,11 +111,32 @@ impl<'a, R: AccessRules> AstarStrategy<Navmesh, f64, (), (), BandFirstSegIndex>
let probe_length = self.bihead_length() - prev_bihead_length; let probe_length = self.bihead_length() - prev_bihead_length;
if result.is_ok() { match result {
self.trace.undo_step(&mut self.tracer); Ok(..) => {
Ok((probe_length, ())) self.trace.undo_step(&mut self.tracer);
} else { Some(probe_length)
Err(()) }
Err(err) => {
if let TracerException::CannotDraw(draw_err) = err {
let layout_err = match draw_err {
DrawException::NoTangents(..) => return None,
DrawException::CannotFinishIn(.., layout_err) => layout_err,
DrawException::CannotWrapAround(.., layout_err) => layout_err,
};
let (ghost, ..) = match layout_err {
LayoutException::NoTangents(..) => return None,
LayoutException::Infringement(Infringement(ghost, obstacle)) => {
(ghost, obstacle)
}
LayoutException::Collision(Collision(ghost, obstacle)) => (ghost, obstacle),
LayoutException::AlreadyConnected(..) => return None,
};
self.ghosts = vec![ghost];
}
None
}
} }
} }