#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FillRule {
NonZero,
Odd,
Positive,
Negative,
}
impl FillRule {
#[inline]
pub fn interpret(self, intersections: i32) -> bool {
match self {
FillRule::NonZero => intersections != 0,
FillRule::Odd => intersections & 1 != 0,
FillRule::Positive => intersections > 0,
FillRule::Negative => intersections < 0,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Intersection {
pub x: f64,
pub direction: i32,
}
#[derive(Debug, Clone, Default)]
pub struct Scanline {
intersections: Vec<Intersection>,
}
impl Scanline {
pub fn new() -> Self {
Scanline::default()
}
pub fn set_intersections(&mut self, mut intersections: Vec<Intersection>) {
intersections.sort_by(|a, b| a.x.partial_cmp(&b.x).unwrap_or(std::cmp::Ordering::Equal));
let mut total = 0;
for i in &mut intersections {
total += i.direction;
i.direction = total;
}
self.intersections = intersections;
}
#[inline]
fn move_to(&self, x: f64) -> Option<usize> {
if self.intersections.is_empty() {
return None;
}
let count = self.intersections.partition_point(|i| i.x <= x);
if count == 0 { None } else { Some(count - 1) }
}
pub fn count_intersections(&self, x: f64) -> i32 {
match self.move_to(x) {
Some(i) => i as i32 + 1,
None => 0,
}
}
pub fn sum_intersections(&self, x: f64) -> i32 {
match self.move_to(x) {
Some(i) => self.intersections[i].direction,
None => 0,
}
}
pub fn filled(&self, x: f64, fill_rule: FillRule) -> bool {
fill_rule.interpret(self.sum_intersections(x))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fill_inside_outside() {
let mut s = Scanline::new();
s.set_intersections(vec![
Intersection {
x: 1.0,
direction: 1,
},
Intersection {
x: 3.0,
direction: -1,
},
]);
assert!(!s.filled(0.0, FillRule::NonZero));
assert!(s.filled(2.0, FillRule::NonZero));
assert!(!s.filled(4.0, FillRule::NonZero));
}
}