use crate::{
domain::{ArrivalKeyring, Connectable, Extrapolator, KeyedSpace, Reversible},
error::ThisError,
graph::{Edge, Graph},
templates::GraphMotion,
};
use std::borrow::Borrow;
#[derive(Debug, Clone)]
pub struct LazyGraphMotion<S, G, E, R, C> {
pub motion: GraphMotion<S, G, E>,
pub keyring: R,
pub chain: C,
}
impl<S, G, E, R, C, State, Action, Goal> Connectable<State, Action, Goal>
for LazyGraphMotion<S, G, E, R, C>
where
S: KeyedSpace<G::Key>,
S::Key: Borrow<G::Key>,
S::State: Into<State> + Clone,
State: Borrow<S::State> + Clone,
G: Graph,
G::Key: Clone,
G::EdgeAttributes: Clone,
E: Extrapolator<S::Waypoint, G::Vertex, G::EdgeAttributes, G::Key>,
E::Extrapolation: Into<Action>,
Action: Into<E::Extrapolation>,
R: ArrivalKeyring<G::Key, G::Key, Goal>,
C: Connectable<State, Action, Goal>,
{
type Connections<'a>
= LazyGraphMotionConnections<'a, S, G, E, R, C, State, Action, Goal>
where
Self: 'a,
Self::ConnectionError: 'a,
State: 'a,
Action: 'a,
Goal: 'a;
type ConnectionError =
LazyGraphMotionError<G::Key, R::ArrivalKeyError, E::ExtrapolationError, C::ConnectionError>;
fn connect<'a>(&'a self, from_state: State, to_target: &'a Goal) -> Self::Connections<'a>
where
Self: 'a,
Self::ConnectionError: 'a,
State: 'a,
Action: 'a,
Goal: 'a,
{
let from_spatial_state = from_state.borrow().clone();
let from_key_ref = self.motion.space.key_for(&from_spatial_state);
let from_key = from_key_ref.borrow().borrow();
let arrival_keys = self
.keyring
.get_arrival_keys(&from_key, to_target)
.into_iter();
LazyGraphMotionConnections::<'a, S, G, E, R, C, State, Action, Goal> {
current_iter: None,
lazy_edges: None,
arrival_keys,
chained_connections: self
.chain
.connect(from_state.clone(), to_target)
.into_iter(),
motion: &self.motion,
from_state: from_spatial_state.clone(),
_ignore: Default::default(),
}
}
}
impl<S, G, E, R, C> Reversible for LazyGraphMotion<S, G, E, R, C>
where
S: Reversible,
G: Reversible,
E: Reversible,
R: Reversible,
C: Reversible,
{
type ReversalError = LazyGraphMotionReversalError<
<GraphMotion<S, G, E> as Reversible>::ReversalError,
R::ReversalError,
C::ReversalError,
>;
fn reversed(&self) -> Result<Self, Self::ReversalError>
where
Self: Sized,
{
Ok(LazyGraphMotion {
motion: self
.motion
.reversed()
.map_err(LazyGraphMotionReversalError::GraphMotion)?,
keyring: self
.keyring
.reversed()
.map_err(LazyGraphMotionReversalError::Keyring)?,
chain: self
.chain
.reversed()
.map_err(LazyGraphMotionReversalError::Chain)?,
})
}
}
pub struct LazyGraphMotionConnections<'a, S, G, E, R, C, State, Action, Goal>
where
S: 'a + KeyedSpace<G::Key>,
S::Key: 'a + Borrow<G::Key>,
S::State: 'a + Into<State> + Clone,
State: 'a + Borrow<S::State> + Clone,
G: 'a + Graph,
G::Key: 'a + Clone,
G::EdgeAttributes: 'a + Clone,
E: 'a + Extrapolator<S::Waypoint, G::Vertex, G::EdgeAttributes, G::Key>,
E::Extrapolation: 'a + Into<Action>,
Action: 'a + Into<E::Extrapolation>,
R: 'a + ArrivalKeyring<G::Key, G::Key, Goal>,
R::ArrivalKeyError: 'a,
C: 'a + Connectable<State, Action, Goal>,
State: 'a,
Action: 'a,
Goal: 'a,
{
current_iter:
Option<Extrapolations<G::Key, <E::ExtrapolationIter<'a> as IntoIterator>::IntoIter>>,
lazy_edges: Option<<G::LazyEdgeIter<'a> as IntoIterator>::IntoIter>,
arrival_keys: <R::ArrivalKeys<'a> as IntoIterator>::IntoIter,
chained_connections: <C::Connections<'a> as IntoIterator>::IntoIter,
motion: &'a GraphMotion<S, G, E>,
from_state: S::State,
_ignore: std::marker::PhantomData<fn(State, Action)>,
}
impl<'a, S, G, E, R, C, State, Action, Goal> Iterator
for LazyGraphMotionConnections<'a, S, G, E, R, C, State, Action, Goal>
where
S: 'a + KeyedSpace<G::Key>,
S::Key: 'a + Borrow<G::Key>,
S::State: 'a + Into<State> + Clone,
State: 'a + Borrow<S::State> + Clone,
G: 'a + Graph,
G::Key: 'a + Clone,
G::EdgeAttributes: 'a + Clone,
E: 'a + Extrapolator<S::Waypoint, G::Vertex, G::EdgeAttributes, G::Key>,
E::Extrapolation: 'a + Into<Action>,
Action: 'a + Into<E::Extrapolation>,
R: 'a + ArrivalKeyring<G::Key, G::Key, Goal>,
R::ArrivalKeyError: 'a,
C: 'a + Connectable<State, Action, Goal>,
State: 'a,
Action: 'a,
Goal: 'a,
{
type Item = Result<
(Action, State),
LazyGraphMotionError<G::Key, R::ArrivalKeyError, E::ExtrapolationError, C::ConnectionError>,
>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(current_iter) = self.current_iter.as_mut() {
if let Some(result) = current_iter.extrapolations.next() {
match result {
Ok((action, waypoint)) => {
let to_vertex = current_iter.to_vertex.clone();
let state = self.motion.space.make_keyed_state(to_vertex, waypoint);
return Some(Ok((action.into(), state.into())));
}
Err(err) => {
return Some(Err(LazyGraphMotionError::Extrapolator(err)));
}
}
}
}
self.current_iter = None;
if let Some(lazy_edges) = self.lazy_edges.as_mut() {
if let Some(edge) = lazy_edges.next() {
let to_vertex = edge.to_vertex();
let Some(v) = self.motion.graph.vertex(to_vertex) else {
return Some(Err(LazyGraphMotionError::MissingVertex(to_vertex.clone())));
};
let extrapolations = self.motion.extrapolator.extrapolate(
self.motion.space.waypoint(&self.from_state).borrow(),
v.borrow(),
&edge.attributes(),
(
Some(
self.motion
.space
.key_for(&self.from_state)
.borrow()
.borrow(),
),
Some(&to_vertex),
),
);
self.current_iter = Some(Extrapolations {
to_vertex: to_vertex.clone(),
extrapolations: extrapolations.into_iter(),
});
continue;
}
}
if let Some(result) = self.arrival_keys.next() {
let to_vertex = match result {
Ok(to_vertex) => to_vertex,
Err(err) => {
return Some(Err(LazyGraphMotionError::Keyring(err)));
}
};
let from_key_ref = self.motion.space.key_for(&self.from_state);
let from_key = from_key_ref.borrow().borrow();
let lazy_edges = self.motion.graph.lazy_edges_between(from_key, &to_vertex);
self.lazy_edges = Some(lazy_edges.into_iter());
continue;
}
if let Some(next_in_chain) = self.chained_connections.next() {
return Some(next_in_chain.map_err(LazyGraphMotionError::Chain));
}
return None;
}
}
}
struct Extrapolations<K, E> {
to_vertex: K,
extrapolations: E,
}
#[derive(Debug, ThisError)]
pub enum LazyGraphMotionError<K, R, E, C> {
#[error("The graph is missing the requested vertex [{0:?}]")]
MissingVertex(K),
#[error("The keyring experienced an error:\n{0:?}")]
Keyring(R),
#[error("The extrapolator experienced an error:\n{0:?}")]
Extrapolator(E),
#[error("The chained connector experienced an error:\n{0:?}")]
Chain(C),
}
#[derive(Debug, ThisError)]
pub enum LazyGraphMotionReversalError<M, R, C> {
#[error("The graph motion had a reversal error:\n{0:?}")]
GraphMotion(M),
#[error("The keyring had a reversal error:\n{0:?}")]
Keyring(R),
#[error("The chained connector had a reversal error:\n{0:?}")]
Chain(C),
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{graph::occupancy::*, motion::se2::*};
use std::sync::Arc;
#[test]
fn test_lazy_graph_motion_connect() {
let cell_size = 0.1;
let mut visibility = Visibility::new(SparseGrid::new(cell_size), 1.0);
visibility.change_cells(&[(Cell::new(22, 3), true)].into_iter().collect());
let graph = VisibilityGraph::new(Arc::new(visibility), []);
let extrapolator = DifferentialDriveLineFollow::new(1.0, 1.0).unwrap();
let lazy = LazyGraphMotion {
motion: GraphMotion {
space: DiscreteSpaceTimeSE2::<Cell, 100>::new(),
graph: graph.clone(),
extrapolator,
},
keyring: (),
chain: (),
};
let from_cell = Cell::new(2, 3);
let from_p = from_cell.center_point(cell_size);
let from_state = StateSE2::new(
from_cell,
WaypointSE2::new_f64(0.0, from_p.x, from_p.y, 0.0),
);
for (to_cell, expected_connections) in [
(Cell::new(46, 3), 0), (Cell::new(22, 3), 0), (Cell::new(13, 3), 0), (Cell::new(22, 70), 1),
(Cell::new(-30, -30), 1),
(Cell::new(-60, 20), 1),
] {
let r: Result<Vec<(DifferentialDriveLineFollowMotion, _)>, _> =
lazy.connect(from_state, &to_cell).into_iter().collect();
let connections = r.unwrap();
assert_eq!(expected_connections, connections.len());
}
}
}