use {Point, SignedNum};
use steps::Steps;
pub struct WalkGrid<T> {
point: Point<T>,
ix: f32,
iy: f32,
sign_x: T,
sign_y: T,
ny: f32,
nx: f32,
}
impl<T: SignedNum> WalkGrid<T> {
#[inline]
pub fn new(start: Point<T>, end: Point<T>) -> WalkGrid<T> {
let (dx, dy) = (end.0 - start.0, end.1 - start.1);
WalkGrid {
point: start,
ix: 0.0,
iy: 0.0,
sign_x: dx.signum(),
sign_y: dy.signum(),
nx: dx.abs().to_f32().unwrap(),
ny: dy.abs().to_f32().unwrap(),
}
}
#[inline]
pub fn steps(self) -> Steps<Point<T>, WalkGrid<T>> {
Steps::new(self)
}
}
impl<T: SignedNum> Iterator for WalkGrid<T> {
type Item = Point<T>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.ix <= self.nx && self.iy <= self.ny {
let point = self.point;
if (0.5 + self.ix) / self.nx < (0.5 + self.iy) / self.ny {
self.point.0 += self.sign_x;
self.ix += 1.0;
} else {
self.point.1 += self.sign_y;
self.iy += 1.0;
}
Some(point)
} else {
None
}
}
}
pub struct Supercover<T> {
point: Point<T>,
ix: f32,
iy: f32,
sign_x: T,
sign_y: T,
ny: f32,
nx: f32,
}
impl<T: SignedNum> Supercover<T> {
#[inline]
pub fn new(start: Point<T>, end: Point<T>) -> Self {
let (dx, dy) = (end.0 - start.0, end.1 - start.1);
Self {
point: start,
ix: 0.0,
iy: 0.0,
sign_x: dx.signum(),
sign_y: dy.signum(),
nx: dx.abs().to_f32().unwrap(),
ny: dy.abs().to_f32().unwrap(),
}
}
#[inline]
pub fn steps(self) -> Steps<Point<T>, Self> {
Steps::new(self)
}
}
impl<T: SignedNum> Iterator for Supercover<T> {
type Item = Point<T>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.ix <= self.nx && self.iy <= self.ny {
let point = self.point;
let comparison = ((0.5 + self.ix) / self.nx) - ((0.5 + self.iy) / self.ny);
if comparison == 0.0 {
self.point.0 += self.sign_x;
self.point.1 += self.sign_y;
self.ix += 1.0;
self.iy += 1.0;
} else if comparison < 0.0 {
self.point.0 += self.sign_x;
self.ix += 1.0;
} else {
self.point.1 += self.sign_y;
self.iy += 1.0;
}
Some(point)
} else {
None
}
}
}
#[test]
fn walk_grid_tests() {
use fuzzing::reverse_slice;
let walk_grid = |a, b| WalkGrid::new(a, b).collect::<Vec<_>>();
assert_eq!(
walk_grid((0, 0), (2, 2)),
[(0, 0), (0, 1), (1, 1), (1, 2), (2, 2)]
);
assert_eq!(
walk_grid((0, 0), (3, 2)),
[(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (3, 2)]
);
assert_eq!(
walk_grid((0, 0), (0, 5)),
[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]
);
assert_eq!(
walk_grid((0, 0), (5, 0)),
[(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0)]
);
assert_ne!(
walk_grid((0, 0), (2, 2)),
reverse_slice(&walk_grid((2, 2), (0, 0)))
);
}
#[test]
fn supercover_tests() {
let walk_grid = |a, b| WalkGrid::new(a, b).collect::<Vec<_>>();
let supercover = |a, b| Supercover::new(a, b).collect::<Vec<_>>();
assert_eq!(
supercover((0, 0), (5, 5)),
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]
);
assert_eq!(supercover((0, 0), (3, 1)), [(0, 0), (1, 0), (2, 1), (3, 1)]);
assert_eq!(
supercover((0, 0), (0, 5)),
[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]
);
assert_eq!(
supercover((0, 0), (5, 0)),
[(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0)]
);
assert_ne!(walk_grid((0, 0), (-10, 10)), supercover((0, 0), (-10, 10)));
assert_ne!(
walk_grid((20, 10), (10, 20)),
supercover((20, 10), (10, 20))
);
assert_eq!(supercover((0, 0), (4, 5)), walk_grid((0, 0), (4, 5)));
}