use super::{Intersect, XPathScanner};
pub struct ScanIterator<'a> {
row: &'a [Intersect],
idx: usize,
count: i32,
eo_mask: i32,
}
impl<'a> ScanIterator<'a> {
#[must_use]
pub fn new(scanner: &'a XPathScanner, y: i32) -> Self {
let row = scanner.row(y);
let eo_mask = if scanner.eo { 1i32 } else { !0i32 };
Self {
row,
idx: 0,
count: 0,
eo_mask,
}
}
}
impl Iterator for ScanIterator<'_> {
type Item = (i32, i32);
fn next(&mut self) -> Option<(i32, i32)> {
let (x0, phase2_start) = loop {
if self.idx >= self.row.len() {
return None;
}
if (self.count & self.eo_mask) != 0 {
let x0 = self.row[self.idx].x0;
break (x0, self.idx);
}
let new_count = self.count.wrapping_add(self.row[self.idx].count);
self.idx += 1;
self.count = new_count;
if (self.count & self.eo_mask) != 0 {
let x0 = self.row[self.idx - 1].x0;
break (x0, self.idx - 1);
}
};
let mut x1 = self.row[phase2_start].x1;
if self.idx == phase2_start {
self.count = self.count.wrapping_add(self.row[self.idx].count);
self.idx += 1;
}
while self.idx < self.row.len() && (self.count & self.eo_mask) != 0 {
x1 = x1.max(self.row[self.idx].x1);
self.count = self.count.wrapping_add(self.row[self.idx].count);
self.idx += 1;
}
Some((x0, x1))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::path::PathBuilder;
use crate::scanner::XPathScanner;
use crate::xpath::XPath;
fn identity() -> [f64; 6] {
[1.0, 0.0, 0.0, 1.0, 0.0, 0.0]
}
fn rect_xpath(x0: f64, y0: f64, x1: f64, y1: f64) -> XPath {
let mut b = PathBuilder::new();
b.move_to(x0, y0).unwrap();
b.line_to(x1, y0).unwrap();
b.line_to(x1, y1).unwrap();
b.line_to(x0, y1).unwrap();
b.close(false).unwrap();
XPath::new(&b.build(), &identity(), 1.0, true)
}
#[test]
fn rect_span_single_row() {
let xpath = rect_xpath(2.0, 1.0, 6.0, 3.0);
let scanner = XPathScanner::new(&xpath, false, 0, 10);
let spans: Vec<_> = ScanIterator::new(&scanner, 2).collect();
assert!(!spans.is_empty(), "expected at least one span on y=2");
let (x0, x1) = spans[0];
assert!(x0 <= 2, "x0={x0}");
assert!(x1 >= 4, "x1={x1}");
}
#[test]
fn out_of_range_y_empty() {
let xpath = rect_xpath(0.0, 0.0, 4.0, 4.0);
let scanner = XPathScanner::new(&xpath, false, 0, 10);
assert!(ScanIterator::new(&scanner, 100).next().is_none());
}
#[test]
fn coalescing_adjacent_nz() {
use crate::scanner::Intersect;
let row = vec![
Intersect {
x0: 0,
x1: 3,
count: -1,
},
Intersect {
x0: 4,
x1: 7,
count: 1,
},
];
let scanner_stub = XPathScanner {
eo: false,
x_min: 0,
x_max: 7,
y_min: 0,
y_max: 0,
row_start: vec![0, 2],
intersects: row,
};
let spans: Vec<_> = ScanIterator::new(&scanner_stub, 0).collect();
assert_eq!(spans, vec![(0, 7)]);
}
#[test]
fn two_disjoint_spans_nz() {
use crate::scanner::Intersect;
let row = vec![
Intersect {
x0: 0,
x1: 2,
count: -1,
},
Intersect {
x0: 3,
x1: 5,
count: 1,
},
Intersect {
x0: 8,
x1: 10,
count: -1,
},
Intersect {
x0: 11,
x1: 13,
count: 1,
},
];
let scanner_stub = XPathScanner {
eo: false,
x_min: 0,
x_max: 13,
y_min: 0,
y_max: 0,
row_start: vec![0, 4],
intersects: row,
};
let spans: Vec<_> = ScanIterator::new(&scanner_stub, 0).collect();
assert_eq!(spans, vec![(0, 5), (8, 13)]);
}
#[test]
fn eo_winding_two_spans() {
use crate::scanner::Intersect;
let row = vec![
Intersect {
x0: 0,
x1: 2,
count: 1,
},
Intersect {
x0: 3,
x1: 5,
count: 1,
},
Intersect {
x0: 8,
x1: 10,
count: 1,
},
Intersect {
x0: 11,
x1: 13,
count: 1,
},
];
let scanner_stub = XPathScanner {
eo: true,
x_min: 0,
x_max: 13,
y_min: 0,
y_max: 0,
row_start: vec![0, 4],
intersects: row,
};
let spans: Vec<_> = ScanIterator::new(&scanner_stub, 0).collect();
assert_eq!(spans, vec![(0, 5), (8, 13)]);
}
#[test]
fn already_inside_residue() {
use crate::scanner::Intersect;
let row = vec![Intersect {
x0: 5,
x1: 7,
count: 1,
}];
let scanner_stub = XPathScanner {
eo: false,
x_min: 5,
x_max: 7,
y_min: 0,
y_max: 0,
row_start: vec![0, 1],
intersects: row,
};
let mut it = ScanIterator::new(&scanner_stub, 0);
it.count = -1;
assert_eq!(it.next(), Some((5, 7)));
assert_eq!(it.next(), None);
}
}