bresenham_zip/
zip_3d.rs

1//! Package with the logic of the three-dimensional BresenhamZip
2
3mod builder_3d;
4
5use std::fmt::{Debug, Formatter};
6use line_drawing::Bresenham3d;
7use crate::{Point3, SignedNum};
8use crate::util::Point;
9
10pub use builder_3d::Builder3d;
11
12pub struct Bresenham3dZip<T> {
13	a: Bresenham3d<T>,
14	b: Bresenham3d<T>,
15	prev_a: Point3<T>,
16	prev_b: Point3<T>,
17	goal: T,
18	axis: u8
19}
20
21impl<T: SignedNum> Bresenham3dZip<T> {
22
23	#[inline]
24	pub(crate) fn new<'a>(start: Point3<T>, end1: Point3<T>, end2: Point3<T>, axis: u8) -> Self {
25		Self {
26			a: Bresenham3d::new(start, end1),
27			b: Bresenham3d::new(start, end2),
28			prev_a: start,
29			prev_b: start,
30			goal: end1.nth(axis),
31			axis
32		}
33	}
34
35}
36
37impl<T: SignedNum> Iterator for Bresenham3dZip<T> {
38	type Item = (Point3<T>, Point3<T>);
39
40	#[allow(clippy::while_let_on_iterator)]  // needs to be like that to keep using the iterator
41	fn next(&mut self) -> Option<Self::Item> {
42		let axis = self.axis;
43
44		let mut a = None;
45		while let Some(point) = self.a.next() {
46			if (point.nth(axis) - self.prev_a.nth(axis)).abs() > T::zero() {
47				a = Some(self.prev_a);
48				self.prev_a = point;
49				break;
50			}
51			self.prev_a = point;
52		}
53
54		let mut b = None;
55		while let Some(point) = self.b.next() {
56			if (point.nth(axis) - self.prev_b.nth(axis)).abs() > T::zero() {
57				b = Some(self.prev_b);
58				self.prev_b = point;
59				break;
60			}
61			self.prev_b = point;
62		}
63
64		if let Some(point) = a {
65			Some((point, b.unwrap()))
66		} else if self.prev_a.nth(axis) == self.goal {
67			self.goal -= T::one();
68			Some((self.prev_a, self.prev_b))
69		} else { None }
70	}
71}
72
73impl<T: SignedNum> Debug for Bresenham3dZip<T> {
74	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
75		write!(f, "Bresenham3dZip [ ({:?}, {:?}, {:?}), ({:?}, {:?}, {:?}) ]. Goal: {:?}",
76		  self.prev_a.0, self.prev_a.1, self.prev_a.2,
77		  self.prev_b.0, self.prev_b.1, self.prev_b.2,
78			self.goal
79		)
80	}
81}
82
83#[cfg(test)]
84mod tests {
85	use super::Bresenham3dZip;
86
87	macro_rules! symmetric {
88	    ($a:tt, $b: tt, $c: tt, $axis: tt, $axis1: tt, $axis2: tt) => {
89				let mut for_a = 50;
90				let mut for_b = 50;
91				let mut matching = 50;
92
93				for (a, b) in Bresenham3dZip::new($a, $b, $c, $axis) {
94			    assert_eq!(for_a, a.$axis1);
95			    assert_eq!(for_a, a.$axis2);
96			    assert_eq!(for_b, b.$axis1);
97			    assert_eq!(for_b, b.$axis2);
98					assert_eq!(matching, a.$axis);
99					assert_eq!(matching, b.$axis);
100
101					for_a -= 1;
102					for_b += 1;
103					matching += 1;
104				}
105	    };
106	}
107
108	macro_rules! asymmetric {
109	    ($a: tt, $b: tt, $c: tt, $axis: tt, $axis1: tt, $axis2: tt) => {
110				let mut for_a_1 = 50;
111				let mut for_a_2 = 50;
112				let mut for_b_1 = 50;
113				let mut for_b_2 = 50;
114				let mut matching = 50;
115
116				for (a, b) in Bresenham3dZip::new($a, $b, $c, $axis) {
117					assert!(a.$axis1 <= for_a_1);
118					assert!(a.$axis2 <= for_a_2);
119					assert!(b.$axis1 >= for_b_1);
120					assert!(b.$axis2 >= for_b_2);
121					assert_eq!(matching, a.$axis);
122					assert_eq!(a.$axis, b.$axis);
123
124					for_a_1 = a.$axis1;
125					for_a_2 = a.$axis2;
126					for_b_1 = b.$axis1;
127					for_b_2 = b.$axis2;
128					matching += 1;
129				}
130	    };
131	}
132
133	macro_rules! inverted {
134	    ($a:tt, $b: tt, $c: tt, $axis: tt, $axis1: tt, $axis2: tt) => {
135		    let mut for_a = 50;
136				let mut for_b = 50;
137				let mut matching = 50;
138
139				for (a, b) in Bresenham3dZip::new($a, $b, $c, $axis) {
140					assert_eq!(for_a, a.$axis1);
141					assert_eq!(for_a, a.$axis2);
142					assert_eq!(for_b, b.$axis1);
143					assert_eq!(for_b, b.$axis2);
144					assert_eq!(matching, a.$axis);
145					assert_eq!(a.$axis, b.$axis);
146
147					for_a -= 1;
148					for_b += 1;
149					matching -= 1;
150				}
151	    };
152	}
153
154	mod x_axis {
155		use super::Bresenham3dZip;
156
157		#[test]
158		fn symmetric() {
159			symmetric!((50, 50, 50), (100, 0, 0), (100, 100, 100), 0, 1, 2);
160		}
161
162		#[test]
163		fn asymmetric() {
164			asymmetric!((50, 50, 50), (400, 0, 10), (400, 800, 200), 0, 1, 2);
165		}
166
167		#[test]
168		fn inverted() {
169			inverted!((50, 50, 50), (0, 0, 0), (0, 100, 100), 0, 1, 2);
170		}
171	}
172
173	mod y_axis {
174		use super::Bresenham3dZip;
175
176		#[test]
177		fn symmetric() {
178			symmetric!((50, 50, 50), (0, 100, 0), (100, 100, 100), 1, 0, 2);
179		}
180
181		#[test]
182		fn asymmetric() {
183			asymmetric!((50, 50, 50), (0, 400, 10), (800, 400, 200), 1, 0, 2);
184		}
185
186		#[test]
187		fn inverted() {
188			inverted!((50, 50, 50), (0, 0, 0), (100, 0, 100), 1, 0, 2);
189		}
190	}
191
192	mod z_axis {
193		use super::Bresenham3dZip;
194
195		#[test]
196		fn symmetric() {
197			symmetric!((50, 50, 50), (0, 0, 100), (100, 100, 100), 2, 0, 1);
198		}
199
200		#[test]
201		fn asymmetric() {
202			asymmetric!((50, 50, 50), (0, 10, 400), (800, 200, 400), 2, 0, 1);
203		}
204
205		#[test]
206		fn inverted() {
207			inverted!((50, 50, 50), (0, 0, 0), (100, 100, 0), 2, 0, 1);
208		}
209	}
210
211}