1 | //! Thick segment iterator. |
2 | |
3 | use crate::{ |
4 | geometry::Point, |
5 | primitives::common::{JoinKind, LineJoin, StrokeOffset, ThickSegment}, |
6 | }; |
7 | |
8 | /// Thick segments iterator. |
9 | /// |
10 | /// Iterates over all line segments in the polyline, returning a 6-sided shape as a [`ThickSegment`] |
11 | /// for each segment. These are tessellated and are used to produce scanline intersections. |
12 | /// |
13 | /// [`ThickSegment`]: super::thick_segment::ThickSegment |
14 | #[derive (Clone, Debug)] |
15 | #[cfg_attr (feature = "defmt" , derive(::defmt::Format))] |
16 | pub struct ThickSegmentIter<'a> { |
17 | windows: core::slice::Windows<'a, Point>, |
18 | start_join: LineJoin, |
19 | end_join: LineJoin, |
20 | width: u32, |
21 | stroke_offset: StrokeOffset, |
22 | points: &'a [Point], |
23 | stop: bool, |
24 | } |
25 | |
26 | static EMPTY: &[Point; 0] = &[]; |
27 | |
28 | impl<'a> ThickSegmentIter<'a> { |
29 | /// Create a new thick segments iterator. |
30 | pub fn new(points: &'a [Point], width: u32, _stroke_offset: StrokeOffset) -> Self { |
31 | // Fix stroke alignment to None. There are issues with degenerate joints when using |
32 | // Inside/Outside stroke alignment on polylines, so this is disabled for now. |
33 | let stroke_offset = StrokeOffset::None; |
34 | |
35 | let mut windows = points.windows(3); |
36 | |
37 | if let Some([start, mid, end]) = windows.next() { |
38 | let start_join = LineJoin::start(*start, *mid, width, stroke_offset); |
39 | let end_join = LineJoin::from_points(*start, *mid, *end, width, stroke_offset); |
40 | |
41 | Self { |
42 | windows, |
43 | start_join, |
44 | end_join, |
45 | width, |
46 | stroke_offset, |
47 | points, |
48 | stop: false, |
49 | } |
50 | } else if let [start, end] = points { |
51 | // Single line segment. |
52 | let start_join = LineJoin::start(*start, *end, width, stroke_offset); |
53 | let end_join = LineJoin::end(*start, *end, width, stroke_offset); |
54 | |
55 | Self { |
56 | windows: EMPTY.windows(3), |
57 | start_join, |
58 | end_join, |
59 | width, |
60 | stroke_offset, |
61 | points, |
62 | stop: false, |
63 | } |
64 | } else { |
65 | // Points must be at least 2 in length to make a polyline iterator out of. |
66 | Self::empty() |
67 | } |
68 | } |
69 | |
70 | /// Empty |
71 | fn empty() -> Self { |
72 | Self { |
73 | windows: EMPTY.windows(3), |
74 | start_join: LineJoin::empty(), |
75 | end_join: LineJoin::empty(), |
76 | width: 0, |
77 | stroke_offset: StrokeOffset::None, |
78 | points: EMPTY, |
79 | stop: true, |
80 | } |
81 | } |
82 | } |
83 | |
84 | impl<'a> Iterator for ThickSegmentIter<'a> { |
85 | type Item = ThickSegment; |
86 | |
87 | fn next(&mut self) -> Option<Self::Item> { |
88 | if self.stop { |
89 | return None; |
90 | } |
91 | |
92 | let segment = ThickSegment::new(self.start_join, self.end_join); |
93 | |
94 | self.start_join = self.end_join; |
95 | |
96 | if let Some([start, mid, end]) = self.windows.next() { |
97 | self.end_join = |
98 | LineJoin::from_points(*start, *mid, *end, self.width, self.stroke_offset); |
99 | } else if self.end_join.kind != JoinKind::End { |
100 | let start = *self.points.get(self.points.len() - 2)?; |
101 | let end = *self.points.last()?; |
102 | |
103 | self.end_join = LineJoin::end(start, end, self.width, self.stroke_offset); |
104 | } else { |
105 | self.stop = true; |
106 | } |
107 | |
108 | Some(segment) |
109 | } |
110 | } |
111 | |