use crate::{Axis, Point3, SignedNum};
use crate::error::Error;
use crate::util::Point;
use crate::zip_3d::Bresenham3dZip;
const MAX_ACCEPTED_AXIS: u8 = 2;
#[derive(Debug)]
pub struct Builder3d<T> {
start: Option<Point3<T>>,
end_a: Option<Point3<T>>,
end_b: Option<Point3<T>>,
axis: u8
}
impl<T: SignedNum> Builder3d<T> {
pub fn new() -> Builder3d<T> {
Self {
start: None,
end_a: None,
end_b: None,
axis: MAX_ACCEPTED_AXIS + 1
}
}
pub fn axis(&mut self, axis: Axis) -> &mut Builder3d<T> {
match axis {
Axis::X => self.axis = 0,
Axis::Y => self.axis = 1,
Axis::Z => self.axis = 2,
};
self
}
pub fn start_point(&mut self, start: Point3<T>) -> &mut Builder3d<T> {
self.start = Some(start);
self
}
pub fn first_ending_point(&mut self, end: Point3<T>) -> &mut Builder3d<T> {
self.end_a = Some(end);
self
}
pub fn second_ending_point(&mut self, end: Point3<T>) -> &mut Builder3d<T> {
self.end_b = Some(end);
self
}
pub fn build<'a, 'b>(&'b self) -> Result<Bresenham3dZip<T>, Error<'a, T>> {
if self.axis > MAX_ACCEPTED_AXIS {
return Err(Error::InvalidOrMissingAxis("X, Y, Z"));
}
let axis = self.axis;
match (&self.start, &self.end_a, &self.end_b) {
(None, _, _) => Err(Error::MissingPoint("starting point")),
(_, None, _) => Err(Error::MissingPoint("first ending point")),
(_, _, None) => Err(Error::MissingPoint("second ending point")),
(Some(start), Some(end_a), Some(end_b)) => {
if end_a.nth(axis) != end_b.nth(axis) {
Err(match axis {
0 => Error::InvalidX(end_a.0, end_b.0),
1 => Error::InvalidY(end_a.1, end_b.1),
2 => Error::InvalidZ(end_a.2, end_b.2),
_ => unreachable!()
})
} else {
Ok(Bresenham3dZip::new(*start, *end_a, *end_b, self.axis))
}
}
}
}
}
#[cfg(test)]
mod test {
use crate::{Axis, build_zip};
use crate::error::Error;
use crate::zip_3d::Builder3d;
#[test]
fn missing_axis() {
let expected = Error::InvalidOrMissingAxis("X, Y, Z");
assert_eq!(expected, Builder3d::<i32>::new().build().unwrap_err());
}
#[test]
fn missing_point() {
let builder = &mut Builder3d::new();
builder.axis(Axis::Z);
if let Err(err) = builder.build() {
assert_eq!(err, Error::MissingPoint("starting point"));
}
builder.start_point((50, 50, 50));
if let Err(err) = builder.build() {
assert_eq!(err, Error::MissingPoint("first ending point"));
}
builder.first_ending_point((0, 100, 100));
if let Err(err) = builder.build() {
assert_eq!(err, Error::MissingPoint("second ending point"));
}
}
#[test]
fn invalid_points() {
assert_eq!(build_zip!(3D:X - (50,50,50) -> (100,0,0), (0,0,0)).unwrap_err(), Error::InvalidX(100, 0));
assert_eq!(build_zip!(3D:Y - (50,50,50) -> (0,0,0), (0,100,0)).unwrap_err(), Error::InvalidY(0, 100));
assert_eq!(build_zip!(3D:Z - (50,50,0) -> (0,0,50), (0,0,100)).unwrap_err(), Error::InvalidZ(50, 100));
}
#[test]
fn valid() {
assert_eq!(format!("{:?}", build_zip!(3D:X - (50, 50, 50) -> (0, 0, 0), (0, 100, 200)).unwrap()),
"Bresenham3dZip [ (50, 50, 50), (50, 50, 50) ]. Goal: 0");
let built = Builder3d::new()
.axis(Axis::X)
.axis(Axis::Y)
.start_point((25, 25, 25))
.second_ending_point((50, 50, 50))
.start_point((10, 10, 10))
.first_ending_point((0, 100, 0))
.second_ending_point((100, 100, 100))
.build();
assert_eq!(format!("{:?}", built.unwrap()), "Bresenham3dZip [ (10, 10, 10), (10, 10, 10) ]. Goal: 100");
}
}