1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
//! Closed shape thick segment iterator.
use crate::{
geometry::Point,
primitives::common::{LineJoin, StrokeOffset, ThickSegment},
};
/// Closed shape thick segments iterator.
///
/// Iterates over all line segments in the polyline, returning a 6-sided shape as a [`ThickSegment`]
/// for each segment. These are tessellated and are used to produce scanline intersections.
///
/// Unlike [`ThickSegmentIter`], this iterator closes the shape with a final line between the
/// start and end points.
///
/// [`ThickSegment`]: super::thick_segment::ThickSegment
/// [`ThickSegmentIter`]: super::thick_segment_iter::ThickSegmentIter
#[derive(Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
pub struct ClosedThickSegmentIter<'a> {
windows: core::slice::Windows<'a, Point>,
first_join: LineJoin,
start_join: LineJoin,
width: u32,
stroke_offset: StrokeOffset,
points: &'a [Point],
stop: bool,
idx: usize,
}
static EMPTY: &[Point; 0] = &[];
impl<'a> ClosedThickSegmentIter<'a> {
/// Create a new thick segments iterator.
pub fn new(points: &'a [Point], width: u32, stroke_offset: StrokeOffset) -> Self {
if let [start, end] = points {
// Single line segment.
let start_join = LineJoin::start(*start, *end, width, stroke_offset);
Self {
windows: EMPTY.windows(3),
start_join,
width,
stroke_offset,
points,
stop: false,
first_join: start_join,
idx: 1,
}
} else if points.is_empty() {
Self::empty()
} else {
let windows = points.windows(3);
let start_join = LineJoin::from_points(
*points.last().unwrap(),
points[0],
points[1],
width,
stroke_offset,
);
Self {
windows,
start_join,
width,
stroke_offset,
points,
stop: false,
first_join: start_join,
idx: 1,
}
}
}
/// Empty
fn empty() -> Self {
Self {
windows: EMPTY.windows(3),
start_join: LineJoin::empty(),
width: 0,
stroke_offset: StrokeOffset::None,
points: EMPTY,
stop: true,
first_join: LineJoin::empty(),
idx: 1,
}
}
}
impl<'a> Iterator for ClosedThickSegmentIter<'a> {
type Item = ThickSegment;
fn next(&mut self) -> Option<Self::Item> {
if self.stop {
return None;
}
self.idx += 1;
let end_join = if let Some([start, mid, end]) = self.windows.next() {
LineJoin::from_points(*start, *mid, *end, self.width, self.stroke_offset)
} else if self.idx == self.points.len() {
// The join at the end of the line. This will become the start join of the closing
// segment.
let start = self.points.get(self.points.len() - 2)?;
let mid = self.points.last()?;
let end = self.points.first()?;
LineJoin::from_points(*start, *mid, *end, self.width, self.stroke_offset)
} else {
// Final closing line between start/end.
self.stop = true;
self.first_join
};
let segment = ThickSegment::new(self.start_join, end_join);
self.start_join = end_join;
Some(segment)
}
}