diff --git a/src/autorouter/autoroute.rs b/src/autorouter/autoroute.rs index 1078bac..cbdbc8f 100644 --- a/src/autorouter/autoroute.rs +++ b/src/autorouter/autoroute.rs @@ -10,7 +10,7 @@ use crate::{ drawing::{band::BandTermsegIndex, graph::PrimitiveIndex}, geometry::primitive::PrimitiveShape, router::{navmesh::Navmesh, route::RouteStepper, trace::TraceStepper, Router}, - stepper::{PollStep, Step, StepError}, + stepper::{PollStep, Step, StepError, StepResult}, }; use super::{ @@ -18,32 +18,6 @@ use super::{ Autorouter, AutorouterError, AutorouterOptions, }; -/// Represents the current status of the autoroute operation. -pub enum AutorouteStatus { - /// The autoroute is currently running and in progress. - Running, - /// A specific segment has been successfully routed. - Routed(BandTermsegIndex), - /// The autoroute process has completed successfully. - Finished, -} - -impl TryInto<()> for AutorouteStatus { - type Error = (); - /// Attempts to get the [`Result`] from the [`AutorouteStatus`]. - /// - /// This implementation allows transitioning from [`AutorouteStatus`] to a - /// [`Result`]. It returns success for the [`AutorouteStatus::Finished`] state - /// or an error for [`AutorouteStatus::Running`] or [`AutorouteStatus::Routed`] states. - fn try_into(self) -> Result<(), ()> { - match self { - AutorouteStatus::Running => Err(()), - AutorouteStatus::Routed(..) => Err(()), - AutorouteStatus::Finished => Ok(()), - } - } -} - /// Manages the autorouting process across multiple ratlines. pub struct AutorouteExecutionStepper { /// An iterator over ratlines that tracks which segments still need to be routed. @@ -91,15 +65,20 @@ impl StepError for AutorouteExecutionStepper { type Error = AutorouterError; } -impl Step, AutorouteStatus> for AutorouteExecutionStepper { - fn step(&mut self, autorouter: &mut Autorouter) -> Result { +impl Step, Result<(), AutorouterError>, BandTermsegIndex> + for AutorouteExecutionStepper +{ + fn step( + &mut self, + autorouter: &mut Autorouter, + ) -> StepResult> { let Some(curr_ratline) = self.curr_ratline else { - return Ok(AutorouteStatus::Finished); + return StepResult::Completed(Ok(())); }; let Some(ref mut route) = self.route else { // Shouldn't happen. - return Ok(AutorouteStatus::Finished); + return StepResult::Completed(Ok(())); }; let (source, target) = autorouter.ratline_endpoints(curr_ratline); @@ -109,9 +88,9 @@ impl Step, AutorouteStatus> for AutorouteExecut Router::new(autorouter.board.layout_mut(), self.options.router_options); match route.poll_step(&mut router) { - Poll::Pending => return Ok(AutorouteStatus::Running), + Poll::Pending => return StepResult::Pending, Poll::Ready(Ok(band_termseg)) => band_termseg, - Poll::Ready(Err(err)) => return Err(err.into()), + Poll::Ready(Err(err)) => return StepResult::Completed(Err(err.into())), } }; @@ -136,13 +115,16 @@ impl Step, AutorouteStatus> for AutorouteExecut Router::new(autorouter.board.layout_mut(), self.options.router_options); self.curr_ratline = Some(new_ratline); - self.route = Some(router.route(source, target, 100.0)?); + self.route = Some(match router.route(source, target, 100.0) { + Ok(x) => x, + Err(err) => return StepResult::Completed(Err(err.into())), + }); } else { self.curr_ratline = None; //return Ok(AutorouteStatus::Finished); } - Ok(AutorouteStatus::Routed(band_termseg)) + StepResult::Yielded(band_termseg) } } diff --git a/src/autorouter/compare_detours.rs b/src/autorouter/compare_detours.rs index f4c6479..767c2a0 100644 --- a/src/autorouter/compare_detours.rs +++ b/src/autorouter/compare_detours.rs @@ -12,11 +12,11 @@ use crate::{ geometry::{primitive::PrimitiveShape, shape::MeasureLength}, graph::MakeRef, router::{navmesh::Navmesh, trace::TraceStepper}, - stepper::{PollStep, Step, StepError}, + stepper::{PollStep, Step, StepError, StepResult}, }; use super::{ - autoroute::{AutorouteExecutionStepper, AutorouteStatus}, + autoroute::AutorouteExecutionStepper, invoker::{GetGhosts, GetMaybeNavmesh, GetMaybeTrace, GetObstacles}, Autorouter, AutorouterError, AutorouterOptions, }; @@ -65,9 +65,9 @@ impl PollStep, (f64, f64)> for CompareDetoursEx return Poll::Ready(Ok((self.total_length1, self.total_length2))); } - match self.autoroute.step(autorouter)? { - AutorouteStatus::Running => Poll::Pending, - AutorouteStatus::Routed(band_termseg) => { + match self.autoroute.step(autorouter) { + StepResult::Pending => Poll::Pending, + StepResult::Yielded(band_termseg) => { let length = band_termseg .ref_(autorouter.board.layout().drawing()) .length(); @@ -80,7 +80,8 @@ impl PollStep, (f64, f64)> for CompareDetoursEx Poll::Pending } - AutorouteStatus::Finished => { + StepResult::Completed(Err(err)) => return Poll::Ready(Err(err)), + StepResult::Completed(Ok(())) => { if let Some(next_autoroute) = self.next_autoroute.take() { autorouter.undo_autoroute_ratlines(vec![self.ratline1, self.ratline2])?; self.autoroute = next_autoroute; diff --git a/src/autorouter/execution.rs b/src/autorouter/execution.rs index e4b2c31..4699ee1 100644 --- a/src/autorouter/execution.rs +++ b/src/autorouter/execution.rs @@ -5,11 +5,11 @@ use serde::{Deserialize, Serialize}; use crate::{ board::mesadata::AccessMesadata, layout::via::ViaWeight, - stepper::{EvalImmut, PollStep, Step, StepError}, + stepper::{EvalImmut, PollStep, Step, StepError, StepResult}, }; use super::{ - autoroute::{AutorouteExecutionStepper, AutorouteStatus}, + autoroute::AutorouteExecutionStepper, compare_detours::CompareDetoursExecutionStepper, invoker::{Invoker, InvokerError}, measure_length::MeasureLength, @@ -45,10 +45,11 @@ impl ExecutionStepper { autorouter: &mut Autorouter, ) -> Poll> { match self { - ExecutionStepper::Autoroute(autoroute) => match autoroute.step(autorouter)? { - AutorouteStatus::Running => Poll::Pending, - AutorouteStatus::Routed(..) => Poll::Pending, - AutorouteStatus::Finished => Poll::Ready("finished autorouting".to_string()), + ExecutionStepper::Autoroute(autoroute) => match autoroute.step(autorouter) { + StepResult::Pending => Poll::Pending, + StepResult::Yielded(..) => Poll::Pending, + StepResult::Completed(Err(err)) => return Poll::Ready(Err(err.into())), + StepResult::Completed(Ok(())) => Poll::Ready("finished autorouting".to_string()), }, ExecutionStepper::PlaceVia(place_via) => { place_via.doit(autorouter)?; diff --git a/src/router/astar.rs b/src/router/astar.rs index ce43637..5eed6de 100644 --- a/src/router/astar.rs +++ b/src/router/astar.rs @@ -15,7 +15,7 @@ use thiserror::Error; use std::cmp::Ordering; -use crate::stepper::{Step, StepError}; +use crate::stepper::{Step, StepError, StepResult}; #[derive(Copy, Clone, Debug)] pub struct MinScored(pub K, pub T); @@ -144,36 +144,11 @@ pub enum AstarError { NotFound, } -#[derive(Debug)] -pub enum AstarStatus -where - G: GraphBase, - G::NodeId: Eq + Hash, - for<'a> &'a G: IntoEdges + MakeEdgeRef, - K: Measure + Copy, -{ +#[derive(Clone, Debug)] +pub enum AstarYield { Probing, Probed, Visited, - Finished(K, Vec, R), -} - -impl TryInto<(K, Vec, R)> for AstarStatus -where - G: GraphBase, - G::NodeId: Eq + Hash, - for<'a> &'a G: IntoEdges + MakeEdgeRef, - K: Measure + Copy, -{ - type Error = (); - fn try_into(self) -> Result<(K, Vec, R), ()> { - match self { - AstarStatus::Probing => Err(()), - AstarStatus::Probed => Err(()), - AstarStatus::Visited => Err(()), - AstarStatus::Finished(cost, path, result) => Ok((cost, path, result)), - } - } } impl Astar @@ -213,14 +188,18 @@ where type Error = AstarError; } -impl> Step> for Astar +impl> + Step, R), AstarError>, AstarYield> for Astar where G: GraphBase, G::NodeId: Eq + Hash, for<'a> &'a G: IntoEdges + MakeEdgeRef, K: Measure + Copy, { - fn step(&mut self, strategy: &mut S) -> Result, AstarError> { + fn step( + &mut self, + strategy: &mut S, + ) -> StepResult, R), AstarError>> { if let Some(curr_node) = self.maybe_curr_node { if self.is_probing { strategy.remove_probe(&self.graph); @@ -242,7 +221,7 @@ where // No need to add neighbors that we have already reached through a // shorter path than now. if *entry.get() <= next_score { - return Ok(AstarStatus::Probed); + return StepResult::Yielded(AstarYield::Probed); } entry.insert(next_score); } @@ -257,23 +236,23 @@ where self.visit_next.push(MinScored(next_estimate_score, next)); self.is_probing = true; - return Ok(AstarStatus::Probing); + return StepResult::Yielded(AstarYield::Probing); } - return Ok(AstarStatus::Probed); + return StepResult::Yielded(AstarYield::Probed); } self.maybe_curr_node = None; } let Some(MinScored(estimate_score, node)) = self.visit_next.pop() else { - return Err(AstarError::NotFound); + return StepResult::Completed(Err(AstarError::NotFound)); }; if let Some(result) = strategy.is_goal(&self.graph, node, &self.path_tracker) { let path = self.path_tracker.reconstruct_path_to(node); let cost = self.scores[&node]; - return Ok(AstarStatus::Finished(cost, path, result)); + return StepResult::Completed(Ok((cost, path, result))); } match self.estimate_scores.entry(node) { @@ -281,7 +260,7 @@ where // If the node has already been visited with an equal or lower score than // now, then we do not need to re-visit it. if *entry.get() <= estimate_score { - return Ok(AstarStatus::Visited); + return StepResult::Yielded(AstarYield::Visited); } entry.insert(estimate_score); } @@ -293,6 +272,6 @@ where self.maybe_curr_node = Some(node); self.edge_ids = self.graph.edges(node).map(|edge| edge.id()).collect(); - Ok(AstarStatus::Visited) + StepResult::Yielded(AstarYield::Visited) } } diff --git a/src/router/route.rs b/src/router/route.rs index 68e3c57..10aa010 100644 --- a/src/router/route.rs +++ b/src/router/route.rs @@ -6,13 +6,13 @@ use crate::{ }, geometry::primitive::PrimitiveShape, router::{ - astar::{Astar, AstarError, AstarStatus}, + astar::{Astar, AstarError}, navmesh::{Navmesh, NavmeshError}, trace::TraceStepper, tracer::Tracer, Router, RouterAstarStrategy, }, - stepper::{PollStep, Step, StepError}, + stepper::{PollStep, Step, StepError, StepResult}, }; pub struct RouteStepper { @@ -85,9 +85,10 @@ impl<'a, R: AccessRules> PollStep, BandTermsegIndex> for RouteStep let target = self.astar.graph.destination(); let mut strategy = RouterAstarStrategy::new(tracer, &mut self.trace, target); - let result = match self.astar.step(&mut strategy)? { - AstarStatus::Probing | AstarStatus::Probed | AstarStatus::Visited => Poll::Pending, - AstarStatus::Finished(_cost, _path, band) => Poll::Ready(Ok(band)), + let result = match self.astar.step(&mut strategy) { + StepResult::Pending | StepResult::Yielded(_) => Poll::Pending, + StepResult::Completed(Ok((_, _, band))) => Poll::Ready(Ok(band)), + StepResult::Completed(Err(err)) => Poll::Ready(Err(err.into())), }; self.ghosts = strategy.probe_ghosts; diff --git a/src/stepper.rs b/src/stepper.rs index 6307909..388bded 100644 --- a/src/stepper.rs +++ b/src/stepper.rs @@ -1,13 +1,53 @@ +use core::convert::Infallible; use core::task::Poll; +/// Result of a single stepper "step". +/// +/// This is like a mix of [`ControlFlow`](core::ops::ControlFlow) and [`Poll`]. +#[must_use] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum StepResult { + /// The stepper didn't produce any additional result yet, + /// try again later (e.g. next frame). + /// + /// This can be used to display intermediate context states to the user + /// to visualize stepper progress + Pending, + + /// The stepper suspended with a value (produced an intermediate result), + /// this can be used to indicate a kind of progress. + Yielded(Y), + + /// The stepper completed. + Completed(S), +} + +impl From> for StepResult { + fn from(x: Poll) -> StepResult { + match x { + Poll::Pending => StepResult::Pending, + Poll::Ready(y) => StepResult::Completed(y), + } + } +} + +pub trait Step { + fn step(&mut self, context: &mut C) -> StepResult; + + /// Run a stepper to completion. + fn finish(&mut self, context: &mut C) -> S { + loop { + if let StepResult::Completed(outcome) = self.step(context) { + return outcome; + } + } + } +} + pub trait StepError { type Error; } -pub trait Step: StepError { - fn step(&mut self, context: &mut C) -> Result; -} - // Note that PollStep's `S` is usually not the same as Step's `S`. pub trait PollStep: StepError { fn poll_step(&mut self, context: &mut C) -> Poll>; @@ -21,13 +61,15 @@ pub trait PollStep: StepError { } } -impl> Step> for Stepper { +impl> Step::Error>, Infallible> + for Stepper +{ #[inline] - fn step(&mut self, context: &mut C) -> Result, Self::Error> { - Ok(match self.poll_step(context) { - Poll::Pending => Poll::Pending, - Poll::Ready(x) => Poll::Ready(x?), - }) + fn step( + &mut self, + context: &mut C, + ) -> StepResult::Error>> { + self.poll_step(context).into() } }