use std::rc::Rc;
use de_types::path::Path;
use parry2d::math::Point;
#[derive(Clone)]
pub(crate) struct PointChain {
prev: Option<Rc<Self>>,
point: Point<f32>,
length: f32,
}
impl PointChain {
pub(crate) fn extended(chain: &Rc<Self>, point: Point<f32>) -> Self {
let length = chain.length() + (point - chain.point()).magnitude();
Self::new(Some(Rc::clone(chain)), point, length)
}
pub(crate) fn first(point: Point<f32>) -> Self {
Self::new(None, point, 0.)
}
fn new(prev: Option<Rc<Self>>, point: Point<f32>, length: f32) -> Self {
Self {
prev,
point,
length,
}
}
pub(crate) fn prev(&self) -> Option<&Rc<Self>> {
self.prev.as_ref()
}
pub(crate) fn point(&self) -> Point<f32> {
self.point
}
pub(crate) fn length(&self) -> f32 {
self.length
}
pub(crate) fn iter(&self) -> Predecessors {
Predecessors::new(self)
}
pub(crate) fn to_path(&self) -> Path {
Path::new(
self.length(),
self.iter().map(|t| t.point().into()).collect(),
)
}
}
pub(crate) struct Predecessors<'a> {
chain: Option<&'a PointChain>,
}
impl<'a> Predecessors<'a> {
fn new(chain: &'a PointChain) -> Self {
Self { chain: Some(chain) }
}
}
impl<'a> Iterator for Predecessors<'a> {
type Item = &'a PointChain;
fn next(&mut self) -> Option<Self::Item> {
let next = self.chain;
self.chain = self.chain.and_then(|c| c.prev()).map(Rc::as_ref);
next
}
}
#[cfg(test)]
mod tests {
use glam::Vec2;
use super::*;
#[test]
fn test_chain() {
let chain = PointChain::first(Point::new(1., 2.));
assert!(chain.prev().is_none());
assert_eq!(chain.point(), Point::new(1., 2.));
assert_eq!(chain.length(), 0.);
let collected: Vec<Point<f32>> = chain.iter().map(|p| p.point()).collect();
assert_eq!(collected, vec![Point::new(1., 2.)]);
let chain = PointChain::extended(&Rc::new(chain), Point::new(3., 2.));
assert!(chain.prev().is_some());
assert_eq!(chain.point(), Point::new(3., 2.));
assert_eq!(chain.length(), 2.);
let collected: Vec<Point<f32>> = chain.iter().map(|p| p.point()).collect();
assert_eq!(collected, vec![Point::new(3., 2.), Point::new(1., 2.)]);
}
#[test]
fn test_to_path() {
let chain = PointChain::extended(
&Rc::new(PointChain::first(Point::new(1., 2.))),
Point::new(3., 2.),
);
let path = chain.to_path();
assert_eq!(path.length(), 2.);
assert_eq!(path.waypoints(), &[Vec2::new(3., 2.), Vec2::new(1., 2.)]);
}
}