1mod 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)] 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}