1 | //! An iterator over all line intersections with a given scanline. |
2 | |
3 | use crate::{ |
4 | geometry::Point, |
5 | primitives::common::{LineJoin, Scanline, StrokeOffset, ThickSegment}, |
6 | }; |
7 | |
8 | /// Scanline intersections iterator. |
9 | /// |
10 | /// This iterator returns multiple `Line`s corresponding to the filled in areas of a polyline |
11 | /// defined by the `points` parameter. |
12 | /// |
13 | /// The result is one line of a filled polygon. |
14 | #[derive (Clone, Debug)] |
15 | #[cfg_attr (feature = "defmt" , derive(::defmt::Format))] |
16 | pub struct ScanlineIntersections<'a> { |
17 | points: &'a [Point], |
18 | remaining_points: &'a [Point], |
19 | next_start_join: Option<LineJoin>, |
20 | width: u32, |
21 | scanline: Scanline, |
22 | } |
23 | |
24 | const EMPTY: &[Point; 3] = &[Point::zero(); 3]; |
25 | |
26 | impl<'a> ScanlineIntersections<'a> { |
27 | /// New |
28 | pub fn new(points: &'a [Point], width: u32, scanline_y: i32) -> Self { |
29 | let next_start_join = match points { |
30 | [first, second, ..] => { |
31 | Some(LineJoin::start(*first, *second, width, StrokeOffset::None)) |
32 | } |
33 | _ => None, |
34 | }; |
35 | |
36 | Self { |
37 | next_start_join, |
38 | width, |
39 | points, |
40 | remaining_points: points, |
41 | scanline: Scanline::new_empty(scanline_y), |
42 | } |
43 | } |
44 | |
45 | /// Empty scanline iterator. |
46 | pub(in crate::primitives) const fn empty() -> Self { |
47 | Self { |
48 | next_start_join: None, |
49 | width: 0, |
50 | points: EMPTY, |
51 | remaining_points: EMPTY, |
52 | scanline: Scanline::new_empty(0), |
53 | } |
54 | } |
55 | |
56 | /// Reset scanline iterator with a new scanline. |
57 | pub(in crate::primitives) fn reset_with_new_scanline(&mut self, scanline_y: i32) { |
58 | *self = Self::new(self.points, self.width, scanline_y); |
59 | } |
60 | |
61 | fn next_segment(&mut self) -> Option<ThickSegment> { |
62 | let start_join = self.next_start_join?; |
63 | |
64 | let end_join = match self.remaining_points { |
65 | [start, mid, end, ..] => { |
66 | LineJoin::from_points(*start, *mid, *end, self.width, StrokeOffset::None) |
67 | } |
68 | [start, end] => LineJoin::end(*start, *end, self.width, StrokeOffset::None), |
69 | _ => return None, |
70 | }; |
71 | |
72 | self.remaining_points = self.remaining_points.get(1..)?; |
73 | |
74 | let segment = ThickSegment::new(start_join, end_join); |
75 | |
76 | self.next_start_join = Some(end_join); |
77 | |
78 | Some(segment) |
79 | } |
80 | } |
81 | |
82 | /// This iterator loops through all scanline intersections for all segments. If two intersections |
83 | /// are adjacent or overlapping, an accumulator line is extended. This repeats until the next |
84 | /// intersection does not touch the current accumulator. At this point, the accumulated line |
85 | /// segment is returned, and is reset to the next segment. |
86 | /// |
87 | /// This process reduces the number of draw calls for adjacent scanlines, whilst preventing overdraw |
88 | /// from overlapping scanline segments. |
89 | /// |
90 | /// ```text |
91 | /// # Adjacent - merge |
92 | /// A---AB+++B |
93 | /// ⇓ |
94 | /// A--------A |
95 | /// |
96 | /// # Overlapping - merge |
97 | /// A---B+++A+++B |
98 | /// ⇓ |
99 | /// A-----------A |
100 | /// |
101 | /// # Separate - leave alone |
102 | /// A---A B---B |
103 | /// ⇓ |
104 | /// A---A B---B |
105 | /// ``` |
106 | impl<'a> Iterator for ScanlineIntersections<'a> { |
107 | type Item = Scanline; |
108 | |
109 | fn next(&mut self) -> Option<Self::Item> { |
110 | while let Some(segment: ThickSegment) = self.next_segment() { |
111 | let next_scanline: Scanline = segment.intersection(self.scanline.y); |
112 | |
113 | if !self.scanline.try_extend(&next_scanline) { |
114 | let ret: Scanline = self.scanline.clone(); |
115 | self.scanline = next_scanline; |
116 | |
117 | return Some(ret); |
118 | } |
119 | } |
120 | |
121 | // No more segments - return the final accumulated line. |
122 | self.scanline.try_take() |
123 | } |
124 | } |
125 | |