use std::fmt::{Debug, Formatter};
use line_drawing::{Bresenham, Point};
use crate::bresenham::error::Error;
use crate::SignedNum;
pub struct BresenhamZipX<T> {
top: Bresenham<T>,
bottom: Bresenham<T>,
prev_top: Point<T>,
prev_bottom: Point<T>,
goal: T
}
impl<T: SignedNum> BresenhamZipX<T> {
#[inline]
pub fn new(start: Point<T>, end_top: Point<T>, end_bottom: Point<T>) -> Result<Self, Error<T>> {
if end_top.0 != end_bottom.0 {
return Err(Error::InvalidX(end_top.0, end_bottom.0))
}
Ok(Self {
top: Bresenham::new(start, end_top),
bottom: Bresenham::new(start, end_bottom),
prev_top: start,
prev_bottom: start,
goal: end_top.0
})
}
}
impl<T: SignedNum> Iterator for BresenhamZipX<T> {
type Item = (Point<T>, Point<T>);
#[allow(clippy::while_let_on_iterator)] fn next(&mut self) -> Option<Self::Item> {
let mut top = None;
while let Some(point) = self.top.next() {
if (point.0 - self.prev_top.0).abs() > T::zero() {
top = Some(self.prev_top);
self.prev_top = point;
break;
}
self.prev_top = point;
}
let mut bottom = None;
while let Some(point) = self.bottom.next() {
if (point.0 - self.prev_bottom.0).abs() > T::zero() {
bottom = Some(self.prev_bottom);
self.prev_bottom = point;
break;
}
self.prev_bottom = point;
}
if let Some(top_point) = top {
Some((top_point, bottom.unwrap()))
} else if self.prev_top.0 == self.goal {
self.goal -= T::one();
Some((self.prev_top, self.prev_bottom))
} else { None }
}
}
impl<T: SignedNum> Debug for BresenhamZipX<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "BresenhamZipX [
previous_top_point: ({:?}, {:?}),
previous_bottom_point: ({:?}, {:?})
]", self.prev_top.0, self.prev_top.1, self.prev_bottom.0, self.prev_bottom.1)
}
}
#[cfg(test)]
mod tests {
use crate::bresenham::BresenhamZipX;
use crate::bresenham::error::Error;
#[test]
fn invalid_x() {
let result = BresenhamZipX::new((0,0), (1,1), (2,2));
assert_eq!(result.unwrap_err(), Error::InvalidX(1,2));
}
#[test]
fn symmetric() {
let mut expected_top_y = 50;
let mut expected_bottom_y = 50;
let mut expected_x = 50;
for (top, bottom) in BresenhamZipX::new((50, 50), (100, 0), (100, 100)).unwrap() {
assert_eq!(expected_top_y, top.1);
assert_eq!(expected_bottom_y, bottom.1);
assert_eq!(expected_x, top.0);
assert_eq!(top.0, bottom.0);
expected_top_y -= 1;
expected_bottom_y += 1;
expected_x += 1;
}
}
#[test]
fn asymmetric() {
let mut expected_top_y = 50;
let mut expected_bottom_y = 50;
let mut expected_x = 50;
for (top, bottom) in BresenhamZipX::new((50, 50), (400, 0), (400, 800)).unwrap() {
assert!(top.1 <= expected_top_y);
assert!(bottom.1 >= expected_bottom_y);
assert_eq!(expected_x, top.0);
assert_eq!(top.0, bottom.0);
expected_top_y = top.1;
expected_bottom_y = bottom.1;
expected_x += 1;
}
}
#[test]
fn inverted() {
let mut expected_top_y = 50;
let mut expected_bottom_y = 50;
let mut expected_x = 50;
for (top, bottom) in BresenhamZipX::new((50, 50), (0, 0), (0, 100)).unwrap() {
assert_eq!(expected_top_y, top.1);
assert_eq!(expected_bottom_y, bottom.1);
assert_eq!(expected_x, top.0);
assert_eq!(top.0, bottom.0);
expected_top_y -= 1;
expected_bottom_y += 1;
expected_x -= 1;
}
}
}