logo
  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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//! 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)]
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 = if let Some([first, second]) = points.get(0..1) {
        //     Some(LineJoin::start(*first, *second, width, StrokeOffset::None))
        // } else {
        //     None
        // };

        // MSRV: Use subslice patterns when we bump to at least 1.42.0
        let next_start_join = match points.get(0..2) {
            Some([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) 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,
        // };

        // MSRV: Use subslice patterns when we bump to at least 1.42.0
        let end_join = self
            .remaining_points
            .get(0..3)
            .or_else(|| self.remaining_points.get(0..2))?;

        let end_join = match end_join {
            [start, mid, end] => {
                LineJoin::from_points(*start, *mid, *end, self.width, StrokeOffset::None)
            }
            [mid, end] => LineJoin::end(*mid, *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()
    }
}