1use nalgebra::{ComplexField, Point, RealField, Scalar};
25use num_traits::AsPrimitive;
26
27use crate::{array, Vec};
28
29#[cfg_attr(
45 feature = "tracing",
46 tracing::instrument("Plot Bresenham Line", skip_all)
47)]
48pub fn plot_bresenham_line<F, T, const N: usize>(
49 start_point: Point<F, N>,
50 end_point: Point<F, N>,
51) -> Vec<Point<T, N>>
52where
53 F: RealField + AsPrimitive<usize> + AsPrimitive<T>,
54 usize: AsPrimitive<F>,
55 T: Scalar + Copy,
56{
57 let deltas: [F; N] =
58 array::from_fn(|idx| <F as ComplexField>::abs(end_point[idx] - start_point[idx]));
59 let steps: [F; N] = array::from_fn(|idx| {
60 if end_point[idx] > start_point[idx] {
61 F::one()
62 } else {
63 -F::one()
64 }
65 });
66 let primary_axis = deltas
67 .iter()
68 .enumerate()
69 .max_by(|a, b| a.1.partial_cmp(b.1).unwrap())
70 .unwrap()
71 .0;
72
73 let mut current_point = start_point;
74 let mut errors = Vec::from([F::zero(); N]);
75 let mut points = Vec::with_capacity(<F as AsPrimitive<usize>>::as_(
76 deltas[primary_axis] + F::one(),
77 ));
78 while <F as ComplexField>::abs(current_point[primary_axis] - end_point[primary_axis])
79 >= F::one()
80 {
81 points.push(current_point.map(|element| element.as_()));
82
83 for axis in 0..N {
84 if axis == primary_axis {
85 continue;
86 }
87
88 errors[axis] += deltas[axis] / deltas[primary_axis];
89
90 if errors[axis] >= F::one() - (F::one() / (N + 1).as_()) {
91 current_point[axis] += steps[axis];
92 errors[axis] -= F::one();
93 }
94 }
95
96 current_point[primary_axis] += steps[primary_axis];
97 }
98
99 points.push(end_point.map(|element| element.as_()));
100 points
101}
102
103#[cfg(feature = "pregenerated")]
104macro_rules! impl_bresenham_algorithm {
105 ($precision:expr, doc $doc:tt, $nd:expr, $out:expr) => {
106 ::paste::paste! {
107 #[doc = "A premade variant of the bresenham line function for " $doc "-precision floating-point arithmetic, returns a [`Vec`] of [`Point`]s with inner type " $out "."]
108 pub fn [<plot_$nd d_$out _bresenham_line>](start_point: Point<$precision, $nd>, end_point: Point<$precision, $nd>) -> Vec<Point<$out, $nd>> {
109 super::plot_bresenham_line::<$precision, $out, $nd>(start_point, end_point)
110 }
111 }
112 };
113
114 ($prec:expr, doc $doc:tt, $nd:expr) => {
115 impl_bresenham_algorithm!($prec, doc $doc, $nd, i32);
116 impl_bresenham_algorithm!($prec, doc $doc, $nd, i64);
117 impl_bresenham_algorithm!($prec, doc $doc, $nd, isize);
118
119 impl_bresenham_algorithm!($prec, doc $doc, $nd, u32);
120 impl_bresenham_algorithm!($prec, doc $doc, $nd, u64);
121 impl_bresenham_algorithm!($prec, doc $doc, $nd, usize);
122
123 impl_bresenham_algorithm!($prec, doc $doc, $nd, f32);
124 impl_bresenham_algorithm!($prec, doc $doc, $nd, f64);
125 };
126
127 ($prec:expr, doc $doc:tt) => {
128 ::paste::paste! {
129 pub(super) mod [<$doc _precision>] {
130 use nalgebra::Point;
131 use crate::Vec;
132
133 impl_bresenham_algorithm!($prec, doc $doc, 2);
134 impl_bresenham_algorithm!($prec, doc $doc, 3);
135 }
136 }
137 }
138}
139
140#[cfg(feature = "pregenerated")]
141impl_bresenham_algorithm!(f32, doc single);
142#[cfg(feature = "pregenerated")]
143impl_bresenham_algorithm!(f64, doc double);
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148 use nalgebra::{Point2, Point3};
149
150 fn calculate_expected_vec_size<const N: usize>(
151 start: Point<f32, N>,
152 end: Point<f32, N>,
153 ) -> usize {
154 let arr: [f32; N] = array::from_fn(|idx| f32::abs(end[idx] - start[idx]));
155 arr.into_iter().max_by(|a, b| a.total_cmp(b)).unwrap() as usize + 1
156 }
157
158 #[test]
159 fn test_plot_bresenham_line_2d_nonsteep_pos() {
160 let start = Point2::new(0.0f32, 0.0f32);
161 let end = Point2::new(10.0f32, 3.0f32);
162 let res = plot_bresenham_line(start, end);
163 assert_eq!(
164 res,
165 Vec::<Point2<isize>>::from([
166 Point2::new(0, 0),
167 Point2::new(1, 0),
168 Point2::new(2, 0),
169 Point2::new(3, 1),
170 Point2::new(4, 1),
171 Point2::new(5, 1),
172 Point2::new(6, 2),
173 Point2::new(7, 2),
174 Point2::new(8, 2),
175 Point2::new(9, 3),
176 Point2::new(10, 3),
177 ])
178 );
179 }
180
181 #[test]
182 fn test_plot_bresenham_line_2d_steep_pos() {
183 let start = Point2::new(0.0f32, 0.0f32);
184 let end = Point2::new(3.0f32, 10.0f32);
185 let res = plot_bresenham_line(start, end);
186 assert_eq!(res.len(), calculate_expected_vec_size(start, end));
187 assert_eq!(
188 res,
189 Vec::<Point2<isize>>::from([
190 Point2::new(0, 0),
191 Point2::new(0, 1),
192 Point2::new(0, 2),
193 Point2::new(1, 3),
194 Point2::new(1, 4),
195 Point2::new(1, 5),
196 Point2::new(2, 6),
197 Point2::new(2, 7),
198 Point2::new(2, 8),
199 Point2::new(3, 9),
200 Point2::new(3, 10),
201 ])
202 );
203 }
204
205 #[test]
206 fn test_plot_bresenham_line_2d_nonsteep_neg() {
207 let start = Point2::new(0.0f32, 0.0f32);
208 let end = Point2::new(-10.0f32, -3.0f32);
209 let res = plot_bresenham_line(start, end);
210 assert_eq!(res.len(), calculate_expected_vec_size(start, end));
211 assert_eq!(
212 res,
213 Vec::<Point2<isize>>::from([
214 Point2::new(0, 0),
215 Point2::new(-1, 0),
216 Point2::new(-2, 0),
217 Point2::new(-3, -1),
218 Point2::new(-4, -1),
219 Point2::new(-5, -1),
220 Point2::new(-6, -2),
221 Point2::new(-7, -2),
222 Point2::new(-8, -2),
223 Point2::new(-9, -3),
224 Point2::new(-10, -3),
225 ])
226 );
227 }
228
229 #[test]
230 fn test_plot_bresenham_line_2d_steep_neg() {
231 let start = Point2::new(0.0f32, 0.0f32);
232 let end = Point2::new(-3.0f32, -10.0f32);
233 let res = plot_bresenham_line(start, end);
234 assert_eq!(res.len(), calculate_expected_vec_size(start, end));
235 assert_eq!(
236 res,
237 Vec::<Point2<isize>>::from([
238 Point2::new(0, 0),
239 Point2::new(0, -1),
240 Point2::new(0, -2),
241 Point2::new(-1, -3),
242 Point2::new(-1, -4),
243 Point2::new(-1, -5),
244 Point2::new(-2, -6),
245 Point2::new(-2, -7),
246 Point2::new(-2, -8),
247 Point2::new(-3, -9),
248 Point2::new(-3, -10),
249 ])
250 );
251 }
252
253 #[test]
254 fn test_plot_bresenham_line_3d_x() {
255 let start = Point3::new(0.0f32, 0.0f32, 0.0f32);
256 let end = Point3::new(-3.0f32, -10.0f32, 7.0f32);
257 let res = plot_bresenham_line(start, end);
258 assert_eq!(res.len(), calculate_expected_vec_size(start, end));
259 assert_eq!(
260 res,
261 Vec::<Point3<isize>>::from([
262 Point3::new(0, 0, 0),
263 Point3::new(0, -1, 0),
264 Point3::new(0, -2, 1),
265 Point3::new(-1, -3, 2),
266 Point3::new(-1, -4, 3),
267 Point3::new(-1, -5, 3),
268 Point3::new(-2, -6, 4),
269 Point3::new(-2, -7, 5),
270 Point3::new(-2, -8, 5),
271 Point3::new(-2, -9, 6),
272 Point3::new(-3, -10, 7),
273 ])
274 );
275 }
276
277 #[test]
278 fn test_small_deltas() {
279 let start = Point3::new(512.0, 512.0, 512.0);
280 let end = Point3::new(512.5, 511.294, 512.1);
281
282 let res: Vec<Point3<usize>> = plot_bresenham_line(start, end);
283 assert_eq!(res.len(), 1)
284 }
285}