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
//! An iterator over all line intersections with a given scanline.
use crate::{
geometry::Point,
primitives::common::{LineJoin, Scanline, StrokeOffset, ThickSegment},
};
/// Scanline intersections iterator.
///
/// This iterator returns multiple `Line`s corresponding to the filled in areas of a polyline
/// defined by the `points` parameter.
///
/// The result is one line of a filled polygon.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
pub struct ScanlineIntersections<'a> {
points: &'a [Point],
remaining_points: &'a [Point],
next_start_join: Option<LineJoin>,
width: u32,
scanline: Scanline,
}
const EMPTY: &[Point; 3] = &[Point::zero(); 3];
impl<'a> ScanlineIntersections<'a> {
/// New
pub fn new(points: &'a [Point], width: u32, scanline_y: i32) -> Self {
let next_start_join = match points {
[first, second, ..] => {
Some(LineJoin::start(*first, *second, width, StrokeOffset::None))
}
_ => None,
};
Self {
next_start_join,
width,
points,
remaining_points: points,
scanline: Scanline::new_empty(scanline_y),
}
}
/// Empty scanline iterator.
pub(in crate::primitives) const fn empty() -> Self {
Self {
next_start_join: None,
width: 0,
points: EMPTY,
remaining_points: EMPTY,
scanline: Scanline::new_empty(0),
}
}
/// Reset scanline iterator with a new scanline.
pub(in crate::primitives) fn reset_with_new_scanline(&mut self, scanline_y: i32) {
*self = Self::new(self.points, self.width, scanline_y);
}
fn next_segment(&mut self) -> Option<ThickSegment> {
let start_join = self.next_start_join?;
let end_join = match self.remaining_points {
[start, mid, end, ..] => {
LineJoin::from_points(*start, *mid, *end, self.width, StrokeOffset::None)
}
[start, end] => LineJoin::end(*start, *end, self.width, StrokeOffset::None),
_ => return None,
};
self.remaining_points = self.remaining_points.get(1..)?;
let segment = ThickSegment::new(start_join, end_join);
self.next_start_join = Some(end_join);
Some(segment)
}
}
/// This iterator loops through all scanline intersections for all segments. If two intersections
/// are adjacent or overlapping, an accumulator line is extended. This repeats until the next
/// intersection does not touch the current accumulator. At this point, the accumulated line
/// segment is returned, and is reset to the next segment.
///
/// This process reduces the number of draw calls for adjacent scanlines, whilst preventing overdraw
/// from overlapping scanline segments.
///
/// ```text
/// # Adjacent - merge
/// A---AB+++B
/// ⇓
/// A--------A
///
/// # Overlapping - merge
/// A---B+++A+++B
/// ⇓
/// A-----------A
///
/// # Separate - leave alone
/// A---A B---B
/// ⇓
/// A---A B---B
/// ```
impl<'a> Iterator for ScanlineIntersections<'a> {
type Item = Scanline;
fn next(&mut self) -> Option<Self::Item> {
while let Some(segment) = self.next_segment() {
let next_scanline = segment.intersection(self.scanline.y);
if !self.scanline.try_extend(&next_scanline) {
let ret = self.scanline.clone();
self.scanline = next_scanline;
return Some(ret);
}
}
// No more segments - return the final accumulated line.
self.scanline.try_take()
}
}