pixel_map/shapes/
line_iterator.rs

1#[cfg(feature = "serialize")]
2use serde::{Deserialize, Serialize};
3
4use super::ILine;
5use crate::{exclusive_irect, Direction};
6use bevy_math::{ivec2, IRect, IVec2};
7
8pub fn plot_line<F>(x0: i32, y0: i32, x1: i32, y1: i32, mut plot: F)
9where
10    F: FnMut(i32, i32),
11{
12    let dx = (x1 - x0).abs();
13    let dy = (y1 - y0).abs();
14    let mut x = x0;
15    let mut y = y0;
16    let mut xi = 1;
17    let mut yi = 1;
18
19    if x1 < x0 {
20        xi = -1;
21    }
22
23    if y1 < y0 {
24        yi = -1;
25    }
26
27    let mut err = dx - dy;
28    let mut e2: i32;
29
30    while x != x1 || y != y1 {
31        plot(x, y);
32        e2 = err * 2;
33        if e2 > -dy {
34            err -= dy;
35            x += xi;
36        }
37        if e2 < dx {
38            err += dx;
39            y += yi;
40        }
41    }
42
43    plot(x1, y1);
44}
45
46#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
47#[derive(Debug, Clone, PartialEq)]
48pub enum LinePixelIterator {
49    Axis(AxisLineIterator),
50    Angle(AngleLineIterator),
51}
52
53impl LinePixelIterator {
54    #[inline]
55    #[must_use]
56    pub fn new(line: &ILine) -> Self {
57        match AxisLineIterator::new(line) {
58            Some(iter) => LinePixelIterator::Axis(iter),
59            None => LinePixelIterator::Angle(AngleLineIterator::new(line)),
60        }
61    }
62
63    #[inline]
64    #[must_use]
65    pub fn peek(&self) -> Option<IVec2> {
66        match self {
67            LinePixelIterator::Axis(iter) => iter.peek(),
68            LinePixelIterator::Angle(iter) => iter.peek(),
69        }
70    }
71
72    /// Seek the iterator to the first point on the line that is on the given bounds, and return it.
73    /// Calling next() after this will return the point beyond the bounds, if there is one
74    /// for the line segment.
75    /// Returns None if the end of the iterator is reached without touching the bounds.
76    #[inline]
77    pub fn seek_bounds(&mut self, bounds: &IRect) -> Option<IVec2> {
78        match self {
79            LinePixelIterator::Axis(iter) => iter.seek_bounds(bounds),
80            LinePixelIterator::Angle(iter) => iter.seek_bounds(bounds),
81        }
82    }
83}
84
85impl Iterator for LinePixelIterator {
86    type Item = IVec2;
87
88    #[inline]
89    fn next(&mut self) -> Option<Self::Item> {
90        match self {
91            LinePixelIterator::Axis(iter) => iter.next(),
92            LinePixelIterator::Angle(iter) => iter.next(),
93        }
94    }
95}
96
97#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
98#[derive(Debug, Clone, PartialEq)]
99pub struct AxisLineIterator {
100    point: IVec2,
101    direction: Direction,
102    end: IVec2,
103    finished: bool,
104}
105
106impl AxisLineIterator {
107    #[inline]
108    #[must_use]
109    pub fn new(line: &ILine) -> Option<Self> {
110        let direction = line.axis_alignment()?;
111        Some(Self {
112            point: line.start(),
113            direction,
114            end: line.end(),
115            finished: false,
116        })
117    }
118
119    #[inline]
120    #[must_use]
121    pub fn peek(&self) -> Option<IVec2> {
122        if self.finished {
123            return None;
124        }
125        Some(self.point)
126    }
127
128    pub fn seek_bounds(&mut self, bounds: &IRect) -> Option<IVec2> {
129        let point = self.next()?;
130
131        let top = bounds.max.y - 1;
132        let left = bounds.min.x;
133        let right = bounds.max.x - 1;
134        let bottom = bounds.min.y;
135
136        let result = match self.direction {
137            Direction::North => Some(IVec2::new(point.x, self.end.y.min(top))),
138            Direction::East => Some(IVec2::new(self.end.x.min(right), point.y)),
139            Direction::South => Some(IVec2::new(point.x, self.end.y.max(bottom))),
140            Direction::West => Some(IVec2::new(self.end.x.max(left), point.y)),
141            _ => None,
142        };
143
144        match result {
145            Some(point) => {
146                // Move the iterator to the calculated point
147                self.point = point;
148
149                // Consume the point
150                self.next()
151            }
152            None => None,
153        }
154    }
155}
156
157impl Iterator for AxisLineIterator {
158    type Item = IVec2;
159
160    #[inline]
161    fn next(&mut self) -> Option<Self::Item> {
162        if self.finished {
163            None
164        } else {
165            let result = self.point;
166            if self.point == self.end {
167                self.finished = true;
168            } else {
169                self.point += self.direction.unit();
170            }
171            Some(result)
172        }
173    }
174}
175
176#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
177#[derive(Debug, Clone, PartialEq)]
178pub struct AngleLineIterator {
179    end: IVec2,
180    dist: IVec2,
181    point: IVec2,
182    xi: i32,
183    yi: i32,
184    err: i32,
185    e2: i32,
186    finished: bool,
187}
188
189impl AngleLineIterator {
190    #[inline]
191    #[must_use]
192    pub fn new(line: &ILine) -> Self {
193        let x0 = line.start().x;
194        let y0 = line.start().y;
195        let x1 = line.end().x;
196        let y1 = line.end().y;
197        let dist = ivec2((x1 - x0).abs(), (y1 - y0).abs());
198        let xi = if x1 < x0 { -1 } else { 1 };
199        let yi = if y1 < y0 { -1 } else { 1 };
200        AngleLineIterator {
201            end: line.end(),
202            dist,
203            point: line.start(),
204            xi,
205            yi,
206            err: dist.x - dist.y,
207            e2: 0,
208            finished: false,
209        }
210    }
211
212    #[inline]
213    #[must_use]
214    pub fn peek(&self) -> Option<IVec2> {
215        if self.finished {
216            return None;
217        }
218        Some(self.point)
219    }
220
221    #[inline]
222    pub fn seek_bounds(&mut self, bounds: &IRect) -> Option<IVec2> {
223        let bounds = exclusive_irect(bounds);
224        while let Some(point) = self.next() {
225            if let Some(next) = self.peek() {
226                if !bounds.contains(next) {
227                    return Some(point);
228                }
229            } else {
230                return Some(point);
231            }
232        }
233        None
234    }
235}
236
237impl Iterator for AngleLineIterator {
238    type Item = IVec2;
239
240    fn next(&mut self) -> Option<Self::Item> {
241        if self.finished {
242            None
243        } else {
244            let result = self.point;
245            if self.point == self.end {
246                self.finished = true;
247            } else {
248                self.e2 = self.err * 2;
249                if self.e2 > -self.dist.y {
250                    self.err -= self.dist.y;
251                    self.point += ivec2(self.xi, 0);
252                }
253                if self.e2 < self.dist.x {
254                    self.err += self.dist.x;
255                    self.point += ivec2(0, self.yi);
256                }
257            }
258            Some(result)
259        }
260    }
261}
262
263#[cfg(test)]
264mod test {
265    use super::*;
266    use crate::iline;
267    use bevy_math::IVec2;
268
269    #[test]
270    fn test_plot_line_north() {
271        let mut points = Vec::new();
272        plot_line(0, 0, 0, 10, |x, y| points.push((x, y)));
273        assert_eq!(points.len(), 11);
274        assert_eq!(points[0], (0, 0));
275        assert_eq!(points[1], (0, 1));
276        assert_eq!(points[2], (0, 2));
277        assert_eq!(points[3], (0, 3));
278        assert_eq!(points[4], (0, 4));
279        assert_eq!(points[5], (0, 5));
280        assert_eq!(points[6], (0, 6));
281        assert_eq!(points[7], (0, 7));
282        assert_eq!(points[8], (0, 8));
283        assert_eq!(points[9], (0, 9));
284        assert_eq!(points[10], (0, 10));
285    }
286
287    #[test]
288    fn test_plot_line_nw() {
289        let mut points = Vec::new();
290        plot_line(0, 0, 10, 10, |x, y| points.push((x, y)));
291        assert_eq!(points.len(), 11);
292        assert_eq!(points[0], (0, 0));
293        assert_eq!(points[1], (1, 1));
294        assert_eq!(points[2], (2, 2));
295        assert_eq!(points[3], (3, 3));
296        assert_eq!(points[4], (4, 4));
297        assert_eq!(points[5], (5, 5));
298        assert_eq!(points[6], (6, 6));
299        assert_eq!(points[7], (7, 7));
300        assert_eq!(points[8], (8, 8));
301        assert_eq!(points[9], (9, 9));
302        assert_eq!(points[10], (10, 10));
303    }
304
305    #[test]
306    fn test_plot_line_ne() {
307        let mut points = Vec::new();
308        plot_line(10, 0, 0, 10, |x, y| points.push((x, y)));
309        assert_eq!(points.len(), 11);
310        assert_eq!(points[0], (10, 0));
311        assert_eq!(points[1], (9, 1));
312        assert_eq!(points[2], (8, 2));
313        assert_eq!(points[3], (7, 3));
314        assert_eq!(points[4], (6, 4));
315        assert_eq!(points[5], (5, 5));
316        assert_eq!(points[6], (4, 6));
317        assert_eq!(points[7], (3, 7));
318        assert_eq!(points[8], (2, 8));
319        assert_eq!(points[9], (1, 9));
320        assert_eq!(points[10], (0, 10));
321    }
322
323    #[test]
324    fn test_plot_line_west() {
325        let mut points = Vec::new();
326        plot_line(10, 0, 0, 0, |x, y| points.push((x, y)));
327        assert_eq!(points.len(), 11);
328        assert_eq!(points[0], (10, 0));
329        assert_eq!(points[1], (9, 0));
330        assert_eq!(points[2], (8, 0));
331        assert_eq!(points[3], (7, 0));
332        assert_eq!(points[4], (6, 0));
333        assert_eq!(points[5], (5, 0));
334        assert_eq!(points[6], (4, 0));
335        assert_eq!(points[7], (3, 0));
336        assert_eq!(points[8], (2, 0));
337        assert_eq!(points[9], (1, 0));
338        assert_eq!(points[10], (0, 0));
339    }
340
341    #[test]
342    fn test_plot_line_east() {
343        let mut points = Vec::new();
344        plot_line(0, 0, 10, 0, |x, y| points.push((x, y)));
345        assert_eq!(points.len(), 11);
346        assert_eq!(points[0], (0, 0));
347        assert_eq!(points[1], (1, 0));
348        assert_eq!(points[2], (2, 0));
349        assert_eq!(points[3], (3, 0));
350        assert_eq!(points[4], (4, 0));
351        assert_eq!(points[5], (5, 0));
352        assert_eq!(points[6], (6, 0));
353        assert_eq!(points[7], (7, 0));
354        assert_eq!(points[8], (8, 0));
355        assert_eq!(points[9], (9, 0));
356        assert_eq!(points[10], (10, 0));
357    }
358
359    #[test]
360    fn test_plot_line_sw() {
361        let mut points = Vec::new();
362        plot_line(0, 10, 10, 0, |x, y| points.push((x, y)));
363        assert_eq!(points.len(), 11);
364        assert_eq!(points[0], (0, 10));
365        assert_eq!(points[1], (1, 9));
366        assert_eq!(points[2], (2, 8));
367        assert_eq!(points[3], (3, 7));
368        assert_eq!(points[4], (4, 6));
369        assert_eq!(points[5], (5, 5));
370        assert_eq!(points[6], (6, 4));
371        assert_eq!(points[7], (7, 3));
372        assert_eq!(points[8], (8, 2));
373        assert_eq!(points[9], (9, 1));
374        assert_eq!(points[10], (10, 0));
375    }
376
377    #[test]
378    fn test_plot_line_se() {
379        let mut points = Vec::new();
380        plot_line(10, 10, 0, 0, |x, y| points.push((x, y)));
381        assert_eq!(points.len(), 11);
382        assert_eq!(points[0], (10, 10));
383        assert_eq!(points[1], (9, 9));
384        assert_eq!(points[2], (8, 8));
385        assert_eq!(points[3], (7, 7));
386        assert_eq!(points[4], (6, 6));
387        assert_eq!(points[5], (5, 5));
388        assert_eq!(points[6], (4, 4));
389        assert_eq!(points[7], (3, 3));
390        assert_eq!(points[8], (2, 2));
391        assert_eq!(points[9], (1, 1));
392        assert_eq!(points[10], (0, 0));
393    }
394
395    #[test]
396    fn test_plot_line_south() {
397        let mut points = Vec::new();
398        plot_line(0, 10, 0, 0, |x, y| points.push((x, y)));
399        assert_eq!(points.len(), 11);
400        assert_eq!(points[0], (0, 10));
401        assert_eq!(points[1], (0, 9));
402        assert_eq!(points[2], (0, 8));
403        assert_eq!(points[3], (0, 7));
404        assert_eq!(points[4], (0, 6));
405        assert_eq!(points[5], (0, 5));
406        assert_eq!(points[6], (0, 4));
407        assert_eq!(points[7], (0, 3));
408        assert_eq!(points[8], (0, 2));
409        assert_eq!(points[9], (0, 1));
410        assert_eq!(points[10], (0, 0));
411    }
412
413    fn iters_for_line(line: &ILine) -> Vec<LinePixelIterator> {
414        let mut iters = vec![LinePixelIterator::Angle(AngleLineIterator::new(line))];
415        if let Some(axis_line_iter) = AxisLineIterator::new(line) {
416            iters.push(LinePixelIterator::Axis(axis_line_iter));
417        }
418        iters
419    }
420
421    #[test]
422    fn test_iterate_line() {
423        #[derive(Debug)]
424        struct TestCase {
425            line: ILine,
426            unit: IVec2,
427        }
428
429        let test_cases = vec![
430            TestCase {
431                line: iline((0, 0), (0, 10)), // N
432                unit: (0, 1).into(),
433            },
434            TestCase {
435                line: iline((0, 0), (10, 10)), // NE
436                unit: (1, 1).into(),
437            },
438            TestCase {
439                line: iline((0, 0), (10, 0)), // E
440                unit: (1, 0).into(),
441            },
442            TestCase {
443                line: iline((0, 0), (10, -10)), // SE
444                unit: (1, -1).into(),
445            },
446            TestCase {
447                line: iline((0, 0), (0, -10)), // S
448                unit: (0, -1).into(),
449            },
450            TestCase {
451                line: iline((0, 0), (-10, -10)), // SW
452                unit: (-1, -1).into(),
453            },
454            TestCase {
455                line: iline((0, 0), (-10, 0)), // W
456                unit: (-1, 0).into(),
457            },
458            TestCase {
459                line: iline((0, 0), (-10, 10)), // NW
460                unit: (-1, 1).into(),
461            },
462        ];
463
464        for test_case in test_cases {
465            let iters = iters_for_line(&test_case.line);
466            for mut iter in iters {
467                let mut current = IVec2::default();
468                while let Some(p) = iter.peek() {
469                    assert_eq!(p, current, "{:?}, Iter: {:?}", test_case, iter);
470                    let n = iter.next().unwrap();
471                    assert_eq!(n, current, "{:?}, Iter: {:?}", test_case, iter);
472
473                    current += test_case.unit;
474                }
475                assert_eq!(iter.peek(), None, "{:?}, Iter: {:?}", test_case, iter);
476                assert_eq!(iter.peek(), None, "{:?}, Iter: {:?}", test_case, iter);
477                assert_eq!(iter.next(), None, "{:?}, Iter: {:?}", test_case, iter);
478                assert_eq!(iter.next(), None, "{:?}, Iter: {:?}", test_case, iter);
479            }
480        }
481    }
482
483    #[test]
484    fn test_seek_bounds() {
485        #[derive(Debug)]
486        struct TestCase {
487            name: String,
488            line: ILine,
489            seek_bounds_ops: Vec<SeekBoundsOp>,
490        }
491
492        #[derive(Debug)]
493        struct SeekBoundsOp {
494            bounds: IRect,
495            expected_result: Option<IVec2>,
496            expected_next: Option<IVec2>,
497        }
498
499        let test_cases = vec![
500            TestCase {
501                name: "N".to_string(),
502                line: iline((0, 0), (0, 10)),
503                seek_bounds_ops: vec![
504                    SeekBoundsOp {
505                        bounds: IRect::new(0, 0, 2, 2),
506                        expected_result: Some((0, 1).into()),
507                        expected_next: Some((0, 2).into()),
508                    },
509                    SeekBoundsOp {
510                        bounds: IRect::new(0, 2, 4, 6),
511                        expected_result: Some((0, 5).into()),
512                        expected_next: Some((0, 6).into()),
513                    },
514                    SeekBoundsOp {
515                        bounds: IRect::new(0, 6, 6, 12),
516                        expected_result: Some((0, 10).into()),
517                        expected_next: None,
518                    },
519                ],
520            },
521            TestCase {
522                name: "E".to_string(),
523                line: iline((0, 0), (10, 0)),
524                seek_bounds_ops: vec![
525                    SeekBoundsOp {
526                        bounds: IRect::new(0, 0, 2, 2),
527                        expected_result: Some((1, 0).into()),
528                        expected_next: Some((2, 0).into()),
529                    },
530                    SeekBoundsOp {
531                        bounds: IRect::new(2, 0, 6, 4),
532                        expected_result: Some((5, 0).into()),
533                        expected_next: Some((6, 0).into()),
534                    },
535                    SeekBoundsOp {
536                        bounds: IRect::new(6, 0, 12, 6),
537                        expected_result: Some((10, 0).into()),
538                        expected_next: None,
539                    },
540                ],
541            },
542            TestCase {
543                name: "S".to_string(),
544                line: iline((0, 0), (0, -10)),
545                seek_bounds_ops: vec![
546                    SeekBoundsOp {
547                        bounds: IRect::new(0, -2, 2, 0),
548                        expected_result: Some((0, -2).into()),
549                        expected_next: Some((0, -3).into()),
550                    },
551                    SeekBoundsOp {
552                        bounds: IRect::new(0, -6, 4, -2),
553                        expected_result: Some((0, -6).into()),
554                        expected_next: Some((0, -7).into()),
555                    },
556                    SeekBoundsOp {
557                        bounds: IRect::new(0, -12, 6, -6),
558                        expected_result: Some((0, -10).into()),
559                        expected_next: None,
560                    },
561                ],
562            },
563            TestCase {
564                name: "W".to_string(),
565                line: iline((0, 0), (-10, 0)),
566                seek_bounds_ops: vec![
567                    SeekBoundsOp {
568                        bounds: IRect::new(-2, 0, 0, 2),
569                        expected_result: Some((-2, 0).into()),
570                        expected_next: Some((-3, 0).into()),
571                    },
572                    SeekBoundsOp {
573                        bounds: IRect::new(-6, 0, -2, 4),
574                        expected_result: Some((-6, 0).into()),
575                        expected_next: Some((-7, 0).into()),
576                    },
577                    SeekBoundsOp {
578                        bounds: IRect::new(-12, 0, -6, 6),
579                        expected_result: Some((-10, 0).into()),
580                        expected_next: None,
581                    },
582                ],
583            },
584            TestCase {
585                name: "NE".to_string(),
586                line: iline((0, 0), (10, 10)),
587                seek_bounds_ops: vec![
588                    SeekBoundsOp {
589                        bounds: IRect::new(0, 0, 2, 2),
590                        expected_result: Some((1, 1).into()),
591                        expected_next: Some((2, 2).into()),
592                    },
593                    SeekBoundsOp {
594                        bounds: IRect::new(2, 2, 6, 6),
595                        expected_result: Some((5, 5).into()),
596                        expected_next: Some((6, 6).into()),
597                    },
598                    SeekBoundsOp {
599                        bounds: IRect::new(6, 6, 12, 12),
600                        expected_result: Some((10, 10).into()),
601                        expected_next: None,
602                    },
603                ],
604            },
605            TestCase {
606                name: "NW".to_string(),
607                line: iline((10, 0), (0, 10)),
608                seek_bounds_ops: vec![
609                    SeekBoundsOp {
610                        bounds: IRect::new(8, 0, 10, 2),
611                        expected_result: Some((9, 1).into()),
612                        expected_next: Some((8, 2).into()),
613                    },
614                    SeekBoundsOp {
615                        bounds: IRect::new(4, 2, 8, 6),
616                        expected_result: Some((5, 5).into()),
617                        expected_next: Some((4, 6).into()),
618                    },
619                    SeekBoundsOp {
620                        bounds: IRect::new(-2, 6, 4, 12),
621                        expected_result: Some((0, 10).into()),
622                        expected_next: None,
623                    },
624                ],
625            },
626            TestCase {
627                name: "SW".to_string(),
628                line: iline((0, 0), (-10, -10)),
629                seek_bounds_ops: vec![
630                    SeekBoundsOp {
631                        bounds: IRect::new(-2, -2, 0, 0),
632                        expected_result: Some((-2, -2).into()),
633                        expected_next: Some((-3, -3).into()),
634                    },
635                    SeekBoundsOp {
636                        bounds: IRect::new(-6, -6, -2, -2),
637                        expected_result: Some((-6, -6).into()),
638                        expected_next: Some((-7, -7).into()),
639                    },
640                    SeekBoundsOp {
641                        bounds: IRect::new(-12, -12, -6, -6),
642                        expected_result: Some((-10, -10).into()),
643                        expected_next: None,
644                    },
645                ],
646            },
647            TestCase {
648                name: "SE".to_string(),
649                line: iline((0, 0), (10, -10)),
650                seek_bounds_ops: vec![
651                    SeekBoundsOp {
652                        bounds: IRect::new(0, -2, 2, 0),
653                        expected_result: Some((1, -1).into()),
654                        expected_next: Some((2, -2).into()),
655                    },
656                    SeekBoundsOp {
657                        bounds: IRect::new(2, -6, 6, -2),
658                        expected_result: Some((5, -5).into()),
659                        expected_next: Some((6, -6).into()),
660                    },
661                    SeekBoundsOp {
662                        bounds: IRect::new(6, -12, 12, -6),
663                        expected_result: Some((10, -10).into()),
664                        expected_next: None,
665                    },
666                ],
667            },
668        ];
669
670        for test_case in test_cases {
671            let iters = iters_for_line(&test_case.line);
672            for mut iter in iters {
673                for op in &test_case.seek_bounds_ops {
674                    let p = iter.seek_bounds(&op.bounds);
675                    assert_eq!(
676                        p, op.expected_result,
677                        "{}: Iter: {:?} Result: Line: {:?}, op: {:?}",
678                        &test_case.name, &iter, &test_case.line, op
679                    );
680                    assert_eq!(
681                        iter.next(),
682                        op.expected_next,
683                        "{}: Iter: {:?} Next: Line: {:?}, op: {:?}",
684                        &test_case.name,
685                        &iter,
686                        &test_case.line,
687                        op
688                    );
689                }
690            }
691        }
692    }
693
694    #[test]
695    fn test_angle_line_iterator() {
696        let test_cases = vec![
697            (0, 10, 0, 0),
698            (10, 0, 0, 0),
699            (0, 0, 10, 0),
700            (10, 10, 0, 0),
701            (0, 10, 0, 0),
702            (5, 5, 20, 10),
703            (10, 5, 5, 20),
704            (0, 0, 0, 0),
705            (0, 0, 10, 10),
706            (0, 0, -10, 10),
707            (0, 0, -10, -10),
708            (0, 0, 10, -10),
709        ];
710        for test_case in test_cases {
711            let line = iline(
712                IVec2::new(test_case.0, test_case.1),
713                IVec2::new(test_case.2, test_case.3),
714            );
715            let mut it = AngleLineIterator::new(&line);
716            plot_line(
717                test_case.0,
718                test_case.1,
719                test_case.2,
720                test_case.3,
721                |x, y| assert_eq!(it.next(), Some((x, y).into())),
722            );
723            assert_eq!(it.next(), None);
724            assert_eq!(it.next(), None);
725        }
726    }
727}