mirror of https://codeberg.org/topola/topola.git
docs(stepper): Explain how stepper traits and navcord work
Navcord is special because we advance its state in a way that is similar to how steppers are stepped but it actually is not a stepper.
This commit is contained in:
parent
47371fdf3f
commit
0df68ee80c
|
|
@ -20,13 +20,18 @@ use super::{
|
|||
navmesh::{BinavvertexNodeIndex, Navmesh, NavvertexIndex},
|
||||
};
|
||||
|
||||
/// The navcord is a structure that holds the movable non-borrowing
|
||||
/// data of the currently running routing process.
|
||||
/// The navcord is a data structure that holds the movable non-borrowing data of
|
||||
/// the currently running routing process.
|
||||
///
|
||||
/// Note that this data structure is not a stepper, since steppers always
|
||||
/// progress linearly, whereas `Navcord` branches out to different states
|
||||
/// depending on the value of the `to` argument of the `.step_to(...)` method.
|
||||
///
|
||||
/// The name "navcord" is a shortening of "navigation cord", by analogy to
|
||||
/// "navmesh" being a shortening of "navigation mesh".
|
||||
#[derive(Debug)]
|
||||
pub struct Navcord {
|
||||
/// The layout edit which we are currently recording.
|
||||
pub recorder: LayoutEdit,
|
||||
/// The currently attempted path.
|
||||
pub path: Vec<NavvertexIndex>,
|
||||
|
|
@ -37,7 +42,7 @@ pub struct Navcord {
|
|||
}
|
||||
|
||||
impl Navcord {
|
||||
/// Creates a new navcord.
|
||||
/// Create a new navcord.
|
||||
pub fn new(
|
||||
recorder: LayoutEdit,
|
||||
source: FixedDotIndex,
|
||||
|
|
@ -52,6 +57,7 @@ impl Navcord {
|
|||
}
|
||||
}
|
||||
|
||||
/// From the current head `head` wrap a new head around the navvertex `around`.
|
||||
fn wrap(
|
||||
&mut self,
|
||||
layout: &mut Layout<impl AccessRules>,
|
||||
|
|
@ -86,6 +92,8 @@ impl Navcord {
|
|||
.map_err(NavcorderException::CannotDraw)
|
||||
}
|
||||
|
||||
/// Advance the navcord and the currently routed band by one step to the
|
||||
/// navvertex `to`.
|
||||
#[debug_ensures(ret.is_ok() -> matches!(self.head, Head::Cane(..)))]
|
||||
#[debug_ensures(ret.is_ok() -> self.path.len() == old(self.path.len() + 1))]
|
||||
#[debug_ensures(ret.is_err() -> self.path.len() == old(self.path.len()))]
|
||||
|
|
@ -96,11 +104,17 @@ impl Navcord {
|
|||
to: NavvertexIndex,
|
||||
) -> Result<(), NavcorderException> {
|
||||
self.head = self.wrap(layout, navmesh, self.head, to)?.into();
|
||||
|
||||
// Now that the new head has been created, push the navvertex
|
||||
// `to` onto the currently attempted path to start from it on
|
||||
// the next `.step_to(...)` call or retreat from it later using
|
||||
// `.step_back(...)`.
|
||||
self.path.push(to);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Retreat the navcord and the currently routed band by one step.
|
||||
#[debug_ensures(self.path.len() == old(self.path.len() - 1))]
|
||||
pub fn step_back<R: AccessRules>(
|
||||
&mut self,
|
||||
|
|
@ -113,6 +127,9 @@ impl Navcord {
|
|||
return Err(NavcorderException::CannotWrap);
|
||||
}
|
||||
|
||||
// Now that the last head of the currently routed band was deleted, pop
|
||||
// the last navvertex from the currently attempted path so that it is up
|
||||
// to date.
|
||||
self.path.pop();
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,31 @@
|
|||
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
/// This trait represents a linearly advanceable state whose advancement may
|
||||
/// break or fail with many different return values, and to which part of
|
||||
/// the information, called the context, has to be supplied on each call as a
|
||||
/// mutable reference argument.
|
||||
///
|
||||
/// An object that implements this trait is called a "stepper".
|
||||
///
|
||||
/// Steppers always progress linearly, that is, it is presumed that the context
|
||||
/// does not change between calls in a way that can affect the stepper's future
|
||||
/// states. Advanceable data structures designed for uses where the future state
|
||||
/// intentionally *may* change from the information supplied as arguments are
|
||||
/// not considered steppers. An example of such an advanceable non-stepper is
|
||||
/// the [`navcord::Navcord`] struct, as it does not progress linearly because
|
||||
/// it branches out by on each call taking in a changeable `to` argument that
|
||||
/// affects the future states.
|
||||
///
|
||||
/// Petgraph's counterpart of this trait is its
|
||||
/// [`petgraph::visit::Walker<Context>`] trait.
|
||||
pub trait Step<Ctx, B, C = ()> {
|
||||
type Error;
|
||||
|
||||
/// Advance the stepper's state by one step.
|
||||
fn step(&mut self, context: &mut Ctx) -> Result<ControlFlow<B, C>, Self::Error>;
|
||||
|
||||
/// Advance the stepper step-by-step in a loop until it fails or breaks.
|
||||
fn finish(&mut self, context: &mut Ctx) -> Result<B, Self::Error> {
|
||||
loop {
|
||||
if let ControlFlow::Break(outcome) = self.step(context)? {
|
||||
|
|
@ -18,10 +38,17 @@ pub trait Step<Ctx, B, C = ()> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Steppers that may be stepped backwards implement this trait.
|
||||
pub trait StepBack<C, S, E> {
|
||||
/// Retreat the stepper's state by one step.
|
||||
fn step_back(&mut self, context: &mut C) -> Result<S, E>;
|
||||
}
|
||||
|
||||
/// Steppers that may be aborted implement this trait.
|
||||
///
|
||||
/// Aborting a stepper puts it in a state where stepping or stepping back always
|
||||
/// fails.
|
||||
pub trait Abort<C> {
|
||||
/// Abort the stepper.
|
||||
fn abort(&mut self, context: &mut C);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue