use super::{timed::TimeCmp, Duration, InterpError, Motion, TimePoint, Waypoint};
use cached::{Cached, UnboundCache};
use sorted_vec::{FindOrInsert, SortedSet};
use std::cell::RefCell;
use std::rc::Rc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FindWaypoint {
Exact(usize),
Approaching(usize),
BeforeStart,
AfterFinish,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum RemovalError {
OutOfBounds,
Depleting,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum MutateError {
OutOfBounds,
InvalidTimeChange,
}
#[derive(Clone, PartialEq, Eq)]
pub struct Trajectory<W: Waypoint> {
waypoints: SortedSet<TimeCmp<W>>,
indefinite_initial_time: bool,
indefinite_finish_time: bool,
}
impl<W: Waypoint> Trajectory<W> {
pub fn new(start: W, finish: W) -> Result<Self, ()> {
if start.time() == finish.time() {
return Result::Err(());
}
let mut result = Self {
waypoints: SortedSet::new(),
indefinite_initial_time: false,
indefinite_finish_time: false,
};
result.waypoints.push(TimeCmp(start));
result.waypoints.push(TimeCmp(finish));
return Result::Ok(result);
}
pub fn with_indefinite_initial_time(mut self, value: bool) -> Self {
self.indefinite_initial_time = value;
self
}
pub fn set_indefinite_initial_time(&mut self, value: bool) {
self.indefinite_initial_time = value;
}
pub fn has_indefinite_initial_time(&self) -> bool {
self.indefinite_initial_time
}
pub fn with_indefinite_finish_time(mut self, value: bool) -> Self {
self.indefinite_finish_time = value;
self
}
pub fn set_indefinite_finish_time(&mut self, value: bool) {
self.indefinite_finish_time = value;
}
pub fn has_indefinite_finish_time(&self) -> bool {
self.indefinite_finish_time
}
pub fn hold(from: W, until: TimePoint) -> Result<Self, ()> {
if from.time() == until {
return Result::Err(());
}
let mut finish = from.clone();
finish.set_time(until);
return Self::new(from, finish);
}
pub fn from_iter<I: std::iter::IntoIterator<Item = W>>(iter: I) -> Result<Self, ()> {
let mut result = Self {
waypoints: SortedSet::new(),
indefinite_initial_time: false,
indefinite_finish_time: false,
};
for element in iter {
result.waypoints.push(TimeCmp(element));
}
if result.waypoints.len() < 2 {
return Result::Err(());
} else {
return Result::Ok(result);
}
}
pub fn insert(&mut self, waypoint: W) -> Result<usize, usize> {
match self.waypoints.find_or_push(TimeCmp(waypoint)) {
FindOrInsert::Found(index) => Result::Err(index),
FindOrInsert::Inserted(index) => Result::Ok(index),
}
}
pub fn insert_or_assign(&mut self, waypoint: W) -> usize {
return self.waypoints.replace(TimeCmp(waypoint)).0;
}
pub fn remove_index(&mut self, index: usize) -> Result<(), RemovalError> {
if self.waypoints.len() <= 2 {
return Result::Err(RemovalError::Depleting);
}
if index >= self.waypoints.len() {
return Result::Err(RemovalError::OutOfBounds);
}
self.waypoints.remove_index(index);
return Result::Ok(());
}
pub fn find(&self, time: &TimePoint) -> FindWaypoint {
match self
.waypoints
.binary_search_by(|x| x.partial_cmp(time).unwrap())
{
Result::Ok(index) => return FindWaypoint::Exact(index),
Result::Err(index) => {
if index == 0 {
return FindWaypoint::BeforeStart;
} else if index == self.waypoints.len() {
return FindWaypoint::AfterFinish;
} else {
return FindWaypoint::Approaching(index);
}
}
}
}
pub fn adjust_times(&mut self, by: Duration) {
unsafe {
let vec = self.waypoints.get_unchecked_mut_vec();
for element in vec.iter_mut() {
let new_time = element.0.time() + by;
element.0.set_time(new_time);
}
}
}
pub fn get(&self, index: usize) -> Option<&W> {
return self.waypoints.get(index).map(|x| &x.0);
}
pub fn duration(&self) -> Option<Duration> {
if let (Some(initial), Some(finish)) = (self.initial_time(), self.finish_time()) {
Some(finish - initial)
} else {
None
}
}
pub fn motion_duration(&self) -> Duration {
self.finish_motion().time() - self.initial_motion().time()
}
pub fn initial_motion(&self) -> &W {
&self.waypoints.first().unwrap().0
}
pub fn finish_motion(&self) -> &W {
&self.waypoints.last().unwrap().0
}
pub fn initial_time(&self) -> Option<TimePoint> {
if self.indefinite_initial_time {
None
} else {
Some(self.initial_motion_time())
}
}
pub fn initial_motion_time(&self) -> TimePoint {
self.initial_motion().time()
}
pub fn finish_time(&self) -> Option<TimePoint> {
if self.indefinite_finish_time {
None
} else {
Some(self.finish_motion_time())
}
}
pub fn finish_motion_time(&self) -> TimePoint {
self.finish_motion().time()
}
pub fn mutate_waypoint<F: FnOnce(&mut W)>(
&mut self,
index: usize,
f: F,
) -> Result<(), MutateError> {
if index >= self.waypoints.len() {
return Result::Err(MutateError::OutOfBounds);
}
let mut lower_bound_opt = Option::None;
let mut upper_bound_opt = Option::None;
unsafe {
let vec = self.waypoints.get_unchecked_mut_vec();
if index > 0 {
lower_bound_opt = Some(vec.get_unchecked(index - 1).0.time().clone());
}
if index < vec.len() - 1 {
upper_bound_opt = Some(vec.get_unchecked(index + 1).0.time().clone());
}
let wp = vec.get_unchecked_mut(index);
let original_time = wp.0.time().clone();
f(&mut wp.0);
if let Some(lower_bound) = lower_bound_opt {
if wp.0.time() <= lower_bound {
wp.0.set_time(original_time);
return Result::Err(MutateError::InvalidTimeChange);
}
}
if let Some(upper_bound) = upper_bound_opt {
if wp.0.time() >= upper_bound {
wp.0.set_time(original_time);
return Result::Err(MutateError::InvalidTimeChange);
}
}
}
return Result::Ok(());
}
pub fn len(&self) -> usize {
return self.waypoints.len();
}
pub fn reserve(&mut self, additional: usize) {
self.waypoints.reserve(additional);
}
pub fn capacity(&self) -> usize {
return self.waypoints.capacity();
}
pub fn motion<'a>(&'a self) -> TrajectoryMotion<'a, W> {
return TrajectoryMotion {
trajectory: self,
motion_cache: RefCell::new(UnboundCache::new()),
};
}
pub fn iter(&self) -> TrajectoryIter<'_, W> {
TrajectoryIter::new(self, self.initial_motion_time(), None)
}
pub fn iter_from(&self, time: TimePoint) -> TrajectoryIter<'_, W> {
TrajectoryIter::new(self, time, None)
}
pub fn iter_range(&self, from_time: TimePoint, to_time: TimePoint) -> TrajectoryIter<'_, W> {
TrajectoryIter::new(self, from_time, Some(to_time))
}
}
impl<W: Waypoint> std::fmt::Debug for Trajectory<W> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("Trajectory")
.field("indefinite_initial_time", &self.indefinite_initial_time)
.field("indefinite_finish_time", &self.indefinite_finish_time)
.field("waypoints", &DebugWaypoints(&self.waypoints))
.finish()
}
}
struct DebugWaypoints<'a, W: Waypoint>(&'a SortedSet<TimeCmp<W>>);
impl<'a, W: Waypoint> std::fmt::Debug for DebugWaypoints<'a, W> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut builder = f.debug_list();
for wp in self.0.iter() {
builder.entry(&wp.0);
}
builder.finish()
}
}
impl<W: Waypoint> std::ops::Deref for Trajectory<W> {
type Target = [TimeCmp<W>];
fn deref(&self) -> &Self::Target {
&self.waypoints
}
}
pub struct TrajectoryMotion<'a, W: Waypoint> {
trajectory: &'a Trajectory<W>,
motion_cache: RefCell<UnboundCache<usize, Rc<W::Motion>>>,
}
impl<'a, W: Waypoint> TrajectoryMotion<'a, W> {
fn find_motion_segment(&self, time: &TimePoint) -> Result<MotionSegment<W>, InterpError> {
match self.trajectory.find(time) {
FindWaypoint::Exact(index) => {
if index == 0 {
return Ok(MotionSegment::Interp(self.get_motion_segment(index + 1)));
} else {
return Ok(MotionSegment::Interp(self.get_motion_segment(index)));
}
}
FindWaypoint::Approaching(index) => {
return Ok(MotionSegment::Interp(self.get_motion_segment(index)));
}
FindWaypoint::BeforeStart => {
if self.trajectory.has_indefinite_initial_time() {
return Ok(MotionSegment::Holding(
self.trajectory.initial_motion().position(),
));
}
}
FindWaypoint::AfterFinish => {
if self.trajectory.has_indefinite_finish_time() {
return Ok(MotionSegment::Holding(
self.trajectory.finish_motion().position(),
));
}
}
}
Err(InterpError::OutOfBounds {
range: [
self.trajectory.initial_motion_time(),
self.trajectory.finish_motion_time(),
],
request: *time,
})
}
fn get_motion_segment(&self, index: usize) -> Rc<W::Motion> {
return self
.motion_cache
.borrow_mut()
.cache_get_or_set_with(index, || {
unsafe {
let wp0 = self.trajectory.get_unchecked(index - 1);
let wp1 = self.trajectory.get_unchecked(index);
return Rc::new(wp0.interpolate(wp1));
}
})
.clone();
}
}
enum MotionSegment<W: Waypoint> {
Interp(Rc<W::Motion>),
Holding(W::Position),
}
impl<'a, W: Waypoint> Motion<W::Position, W::Velocity> for TrajectoryMotion<'a, W> {
fn compute_position(&self, time: &TimePoint) -> Result<W::Position, InterpError> {
match self.find_motion_segment(time)? {
MotionSegment::Interp(motion) => motion.compute_position(time),
MotionSegment::Holding(p) => Ok(p),
}
}
fn compute_velocity(&self, time: &TimePoint) -> Result<W::Velocity, InterpError> {
match self.find_motion_segment(time)? {
MotionSegment::Interp(motion) => motion.compute_velocity(time),
MotionSegment::Holding(_) => Ok(W::zero_velocity()),
}
}
}
pub struct TrajectoryIter<'a, W: Waypoint> {
trajectory: &'a Trajectory<W>,
next_element: TrajectoryIterNext,
begin: TimePoint,
until: Option<TimePoint>,
}
impl<'a, W: Waypoint> TrajectoryIter<'a, W> {
pub fn pairs(self) -> TrajectoryIterPairs<'a, W> {
TrajectoryIterPairs {
base: self,
previous: None,
}
}
fn new(trajectory: &'a Trajectory<W>, begin: TimePoint, until: Option<TimePoint>) -> Self {
let next_element = match trajectory.find(&begin) {
FindWaypoint::BeforeStart => {
if trajectory.indefinite_initial_time {
TrajectoryIterNext::PreInitial(begin)
} else {
TrajectoryIterNext::Index(0)
}
}
FindWaypoint::Exact(index) => TrajectoryIterNext::Index(index),
FindWaypoint::Approaching(index) => TrajectoryIterNext::Index(index - 1),
FindWaypoint::AfterFinish => TrajectoryIterNext::StartPostFinish(begin),
};
Self {
trajectory,
next_element,
begin,
until,
}
}
}
enum TrajectoryIterNext {
PreInitial(TimePoint),
Index(usize),
StartPostFinish(TimePoint),
ReachedPostFinish(TimePoint),
Depleted,
}
impl<'a, W: Waypoint> Iterator for TrajectoryIter<'a, W> {
type Item = W;
fn next(&mut self) -> Option<W> {
match self.next_element {
TrajectoryIterNext::PreInitial(t) => {
if let Some(t_f) = self.until {
if t_f < t {
self.next_element = TrajectoryIterNext::Depleted;
return None;
}
}
let mut wp = self.trajectory.initial_motion().clone();
wp.set_time(t);
self.next_element = TrajectoryIterNext::Index(0);
Some(wp)
}
TrajectoryIterNext::Index(index) => {
let wp = self.trajectory.get(index).map(|wp| wp.clone());
if let (Some(t_f), Some(wp)) = (self.until, &wp) {
if t_f <= wp.time() {
self.next_element = TrajectoryIterNext::Depleted;
if wp.time() < self.begin {
return None;
}
return Some(wp.clone());
}
}
self.next_element = if index + 1 < self.trajectory.len() {
TrajectoryIterNext::Index(index + 1)
} else {
if let Some(t_f) = self.until {
TrajectoryIterNext::ReachedPostFinish(t_f)
} else {
TrajectoryIterNext::Depleted
}
};
wp
}
TrajectoryIterNext::StartPostFinish(t) => {
if !self.trajectory.has_indefinite_finish_time() {
self.next_element = TrajectoryIterNext::Depleted;
return None;
}
let mut wp = self.trajectory.finish_motion().clone();
wp.set_time(t);
if let Some(t_f) = self.until {
self.next_element = TrajectoryIterNext::ReachedPostFinish(t_f);
} else {
self.next_element = TrajectoryIterNext::Depleted;
}
return Some(wp);
}
TrajectoryIterNext::ReachedPostFinish(t) => {
if !self.trajectory.has_indefinite_finish_time() {
self.next_element = TrajectoryIterNext::Depleted;
return None;
}
let mut wp = self.trajectory.finish_motion().clone();
wp.set_time(t);
self.next_element = TrajectoryIterNext::Depleted;
return Some(wp);
}
TrajectoryIterNext::Depleted => None,
}
}
}
pub struct TrajectoryIterPairs<'a, W: Waypoint> {
base: TrajectoryIter<'a, W>,
previous: Option<W>,
}
impl<'a, W: Waypoint> Iterator for TrajectoryIterPairs<'a, W> {
type Item = [W; 2];
fn next(&mut self) -> Option<[W; 2]> {
let previous = match &self.previous {
Some(wp) => wp.clone(),
None => match self.base.next() {
Some(wp) => wp,
None => return None,
},
};
match self.base.next() {
Some(wp) => {
self.previous = Some(wp.clone());
Some([previous, wp])
}
None => None,
}
}
}