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},
|
navmesh::{BinavvertexNodeIndex, Navmesh, NavvertexIndex},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The navcord is a structure that holds the movable non-borrowing
|
/// The navcord is a data structure that holds the movable non-borrowing data of
|
||||||
/// data of the currently running routing process.
|
/// 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
|
/// The name "navcord" is a shortening of "navigation cord", by analogy to
|
||||||
/// "navmesh" being a shortening of "navigation mesh".
|
/// "navmesh" being a shortening of "navigation mesh".
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Navcord {
|
pub struct Navcord {
|
||||||
|
/// The layout edit which we are currently recording.
|
||||||
pub recorder: LayoutEdit,
|
pub recorder: LayoutEdit,
|
||||||
/// The currently attempted path.
|
/// The currently attempted path.
|
||||||
pub path: Vec<NavvertexIndex>,
|
pub path: Vec<NavvertexIndex>,
|
||||||
|
|
@ -37,7 +42,7 @@ pub struct Navcord {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Navcord {
|
impl Navcord {
|
||||||
/// Creates a new navcord.
|
/// Create a new navcord.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
recorder: LayoutEdit,
|
recorder: LayoutEdit,
|
||||||
source: FixedDotIndex,
|
source: FixedDotIndex,
|
||||||
|
|
@ -52,6 +57,7 @@ impl Navcord {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// From the current head `head` wrap a new head around the navvertex `around`.
|
||||||
fn wrap(
|
fn wrap(
|
||||||
&mut self,
|
&mut self,
|
||||||
layout: &mut Layout<impl AccessRules>,
|
layout: &mut Layout<impl AccessRules>,
|
||||||
|
|
@ -86,6 +92,8 @@ impl Navcord {
|
||||||
.map_err(NavcorderException::CannotDraw)
|
.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() -> matches!(self.head, Head::Cane(..)))]
|
||||||
#[debug_ensures(ret.is_ok() -> self.path.len() == old(self.path.len() + 1))]
|
#[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()))]
|
#[debug_ensures(ret.is_err() -> self.path.len() == old(self.path.len()))]
|
||||||
|
|
@ -96,11 +104,17 @@ impl Navcord {
|
||||||
to: NavvertexIndex,
|
to: NavvertexIndex,
|
||||||
) -> Result<(), NavcorderException> {
|
) -> Result<(), NavcorderException> {
|
||||||
self.head = self.wrap(layout, navmesh, self.head, to)?.into();
|
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);
|
self.path.push(to);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retreat the navcord and the currently routed band by one step.
|
||||||
#[debug_ensures(self.path.len() == old(self.path.len() - 1))]
|
#[debug_ensures(self.path.len() == old(self.path.len() - 1))]
|
||||||
pub fn step_back<R: AccessRules>(
|
pub fn step_back<R: AccessRules>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
@ -113,6 +127,9 @@ impl Navcord {
|
||||||
return Err(NavcorderException::CannotWrap);
|
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();
|
self.path.pop();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,31 @@
|
||||||
|
|
||||||
use std::ops::ControlFlow;
|
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 = ()> {
|
pub trait Step<Ctx, B, C = ()> {
|
||||||
type Error;
|
type Error;
|
||||||
|
|
||||||
|
/// Advance the stepper's state by one step.
|
||||||
fn step(&mut self, context: &mut Ctx) -> Result<ControlFlow<B, C>, Self::Error>;
|
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> {
|
fn finish(&mut self, context: &mut Ctx) -> Result<B, Self::Error> {
|
||||||
loop {
|
loop {
|
||||||
if let ControlFlow::Break(outcome) = self.step(context)? {
|
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> {
|
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>;
|
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> {
|
pub trait Abort<C> {
|
||||||
|
/// Abort the stepper.
|
||||||
fn abort(&mut self, context: &mut C);
|
fn abort(&mut self, context: &mut C);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue