use crate::{
Coord, CoordNum, Line, LineString, MultiLineString, MultiPolygon, Polygon, Rect, Triangle,
};
use core::slice;
use std::fmt::Debug;
use std::iter;
pub trait LinesIter<'a> {
type Scalar: CoordNum;
type Iter: Iterator<Item = Line<Self::Scalar>>;
fn lines_iter(&'a self) -> Self::Iter;
}
impl<'a, T: CoordNum + 'a> LinesIter<'a> for Line<T> {
type Scalar = T;
type Iter = iter::Copied<iter::Once<&'a Line<Self::Scalar>>>;
fn lines_iter(&'a self) -> Self::Iter {
iter::once(self).copied()
}
}
impl<'a, T: CoordNum + 'a> LinesIter<'a> for LineString<T> {
type Scalar = T;
type Iter = LineStringIter<'a, Self::Scalar>;
fn lines_iter(&'a self) -> Self::Iter {
LineStringIter::new(self)
}
}
#[derive(Debug)]
pub struct LineStringIter<'a, T: CoordNum>(slice::Windows<'a, Coord<T>>);
impl<'a, T: CoordNum> LineStringIter<'a, T> {
fn new(line_string: &'a LineString<T>) -> Self {
Self(line_string.0.windows(2))
}
}
impl<'a, T: CoordNum> Iterator for LineStringIter<'a, T> {
type Item = Line<T>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|w| {
unsafe { Line::new(*w.get_unchecked(0), *w.get_unchecked(1)) }
})
}
}
type MultiLineStringIter<'a, T> =
iter::Flatten<MapLinesIter<'a, slice::Iter<'a, LineString<T>>, LineString<T>>>;
impl<'a, T: CoordNum + 'a> LinesIter<'a> for MultiLineString<T> {
type Scalar = T;
type Iter = MultiLineStringIter<'a, Self::Scalar>;
fn lines_iter(&'a self) -> Self::Iter {
MapLinesIter(self.0.iter()).flatten()
}
}
type PolygonIter<'a, T> = iter::Chain<
LineStringIter<'a, T>,
iter::Flatten<MapLinesIter<'a, slice::Iter<'a, LineString<T>>, LineString<T>>>,
>;
impl<'a, T: CoordNum + 'a> LinesIter<'a> for Polygon<T> {
type Scalar = T;
type Iter = PolygonIter<'a, Self::Scalar>;
fn lines_iter(&'a self) -> Self::Iter {
self.exterior()
.lines_iter()
.chain(MapLinesIter(self.interiors().iter()).flatten())
}
}
type MultiPolygonIter<'a, T> =
iter::Flatten<MapLinesIter<'a, slice::Iter<'a, Polygon<T>>, Polygon<T>>>;
impl<'a, T: CoordNum + 'a> LinesIter<'a> for MultiPolygon<T> {
type Scalar = T;
type Iter = MultiPolygonIter<'a, Self::Scalar>;
fn lines_iter(&'a self) -> Self::Iter {
MapLinesIter(self.0.iter()).flatten()
}
}
impl<'a, T: CoordNum + 'a> LinesIter<'a> for Rect<T> {
type Scalar = T;
type Iter = <[Line<Self::Scalar>; 4] as IntoIterator>::IntoIter;
fn lines_iter(&'a self) -> Self::Iter {
self.to_lines().into_iter()
}
}
impl<'a, T: CoordNum + 'a> LinesIter<'a> for Triangle<T> {
type Scalar = T;
type Iter = <[Line<Self::Scalar>; 3] as IntoIterator>::IntoIter;
fn lines_iter(&'a self) -> Self::Iter {
self.to_lines().into_iter()
}
}
#[derive(Debug)]
pub struct MapLinesIter<'a, Iter1: Iterator<Item = &'a Iter2>, Iter2: 'a + LinesIter<'a>>(Iter1);
impl<'a, Iter1: Iterator<Item = &'a Iter2>, Iter2: LinesIter<'a>> Iterator
for MapLinesIter<'a, Iter1, Iter2>
{
type Item = Iter2::Iter;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|g| g.lines_iter())
}
}
#[cfg(test)]
mod test {
use super::LinesIter;
use crate::{
coord, line_string, polygon, Line, LineString, MultiLineString, MultiPolygon, Rect,
Triangle,
};
#[test]
fn test_line() {
let line = Line::new(coord! { x: 0., y: 0. }, coord! { x: 5., y: 10. });
let want = vec![Line::new(coord! { x: 0., y: 0. }, coord! { x: 5., y: 10. })];
assert_eq!(want, line.lines_iter().collect::<Vec<_>>());
}
#[test]
fn test_empty_line_string() {
let ls: LineString = line_string![];
assert_eq!(Vec::<Line>::new(), ls.lines_iter().collect::<Vec<_>>());
}
#[test]
fn test_open_line_string() {
let ls = line_string![(x: 0., y: 0.), (x: 1., y: 1.), (x:2., y: 2.)];
let want = vec![
Line::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 1. }),
Line::new(coord! { x: 1., y: 1. }, coord! { x: 2., y: 2. }),
];
assert_eq!(want, ls.lines_iter().collect::<Vec<_>>());
}
#[test]
fn test_closed_line_string() {
let mut ls = line_string![(x: 0., y: 0.), (x: 1., y: 1.), (x:2., y: 2.)];
ls.close();
let want = vec![
Line::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 1. }),
Line::new(coord! { x: 1., y: 1. }, coord! { x: 2., y: 2. }),
Line::new(coord! { x: 2., y: 2. }, coord! { x: 0., y: 0. }),
];
assert_eq!(want, ls.lines_iter().collect::<Vec<_>>());
}
#[test]
fn test_multi_line_string() {
let mls = MultiLineString::new(vec![
line_string![],
line_string![(x: 0., y: 0.), (x: 1., y: 1.)],
line_string![(x: 0., y: 0.), (x: 1., y: 1.), (x:2., y: 2.)],
]);
let want = vec![
Line::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 1. }),
Line::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 1. }),
Line::new(coord! { x: 1., y: 1. }, coord! { x: 2., y: 2. }),
];
assert_eq!(want, mls.lines_iter().collect::<Vec<_>>());
}
#[test]
fn test_polygon() {
let p = polygon!(
exterior: [(x: 0., y: 0.), (x: 0., y: 10.), (x: 10., y: 10.), (x: 10., y: 0.)],
interiors: [
[(x: 1., y: 1.), (x: 1., y: 2.), (x: 2., y: 2.), (x: 2., y: 1.)],
[(x: 3., y: 3.), (x: 5., y: 3.), (x: 5., y: 5.), (x: 3., y: 5.)],
],
);
let want = vec![
Line::new(coord! { x: 0., y: 0. }, coord! { x: 0., y: 10. }),
Line::new(coord! { x: 0., y: 10. }, coord! { x: 10., y: 10. }),
Line::new(coord! { x: 10., y: 10. }, coord! { x: 10., y: 0. }),
Line::new(coord! { x: 10., y: 0. }, coord! { x: 0., y: 0. }),
Line::new(coord! { x: 1., y: 1. }, coord! { x: 1., y: 2. }),
Line::new(coord! { x: 1., y: 2. }, coord! { x: 2., y: 2. }),
Line::new(coord! { x: 2., y: 2. }, coord! { x: 2., y: 1. }),
Line::new(coord! { x: 2., y: 1. }, coord! { x: 1., y: 1. }),
Line::new(coord! { x: 3., y: 3. }, coord! { x: 5., y: 3. }),
Line::new(coord! { x: 5., y: 3. }, coord! { x: 5., y: 5. }),
Line::new(coord! { x: 5., y: 5. }, coord! { x: 3., y: 5. }),
Line::new(coord! { x: 3., y: 5. }, coord! { x: 3., y: 3. }),
];
assert_eq!(want, p.lines_iter().collect::<Vec<_>>());
}
#[test]
fn test_multi_polygon() {
let mp = MultiPolygon::new(vec![
polygon!(
exterior: [(x: 0., y: 0.), (x: 0., y: 10.), (x: 10., y: 10.), (x: 10., y: 0.)],
interiors: [[(x: 1., y: 1.), (x: 1., y: 2.), (x: 2., y: 2.), (x: 2., y: 1.)]],
),
polygon!(
exterior: [(x: 3., y: 3.), (x: 5., y: 3.), (x: 5., y: 5.), (x: 3., y: 5.)],
interiors: [],
),
]);
let want = vec![
Line::new(coord! { x: 0., y: 0. }, coord! { x: 0., y: 10. }),
Line::new(coord! { x: 0., y: 10. }, coord! { x: 10., y: 10. }),
Line::new(coord! { x: 10., y: 10. }, coord! { x: 10., y: 0. }),
Line::new(coord! { x: 10., y: 0. }, coord! { x: 0., y: 0. }),
Line::new(coord! { x: 1., y: 1. }, coord! { x: 1., y: 2. }),
Line::new(coord! { x: 1., y: 2. }, coord! { x: 2., y: 2. }),
Line::new(coord! { x: 2., y: 2. }, coord! { x: 2., y: 1. }),
Line::new(coord! { x: 2., y: 1. }, coord! { x: 1., y: 1. }),
Line::new(coord! { x: 3., y: 3. }, coord! { x: 5., y: 3. }),
Line::new(coord! { x: 5., y: 3. }, coord! { x: 5., y: 5. }),
Line::new(coord! { x: 5., y: 5. }, coord! { x: 3., y: 5. }),
Line::new(coord! { x: 3., y: 5. }, coord! { x: 3., y: 3. }),
];
assert_eq!(want, mp.lines_iter().collect::<Vec<_>>());
}
#[test]
fn test_rect() {
let rect = Rect::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 2. });
let want = rect.to_polygon().lines_iter().collect::<Vec<_>>();
assert_eq!(want, rect.lines_iter().collect::<Vec<_>>());
}
#[test]
fn test_triangle() {
let triangle = Triangle::new(
coord! { x: 0., y: 0. },
coord! { x: 1., y: 2. },
coord! { x: 2., y: 3. },
);
let want = triangle.to_polygon().lines_iter().collect::<Vec<_>>();
assert_eq!(want, triangle.lines_iter().collect::<Vec<_>>());
}
}