bymsdfgen_core/raster/
scanline.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum FillRule {
6 NonZero,
7 Odd,
9 Positive,
10 Negative,
11}
12
13impl FillRule {
14 #[inline]
15 pub fn interpret(self, intersections: i32) -> bool {
16 match self {
17 FillRule::NonZero => intersections != 0,
18 FillRule::Odd => intersections & 1 != 0,
19 FillRule::Positive => intersections > 0,
20 FillRule::Negative => intersections < 0,
21 }
22 }
23}
24
25#[derive(Debug, Clone, Copy)]
26pub struct Intersection {
27 pub x: f64,
28 pub direction: i32,
30}
31
32#[derive(Debug, Clone, Default)]
34pub struct Scanline {
35 intersections: Vec<Intersection>,
36}
37
38impl Scanline {
39 pub fn new() -> Self {
40 Scanline::default()
41 }
42
43 pub fn set_intersections(&mut self, mut intersections: Vec<Intersection>) {
45 intersections.sort_by(|a, b| a.x.partial_cmp(&b.x).unwrap_or(std::cmp::Ordering::Equal));
46 let mut total = 0;
47 for i in &mut intersections {
48 total += i.direction;
49 i.direction = total;
50 }
51 self.intersections = intersections;
52 }
53
54 #[inline]
56 fn move_to(&self, x: f64) -> Option<usize> {
57 if self.intersections.is_empty() {
58 return None;
59 }
60 let count = self.intersections.partition_point(|i| i.x <= x);
61 if count == 0 { None } else { Some(count - 1) }
62 }
63
64 pub fn count_intersections(&self, x: f64) -> i32 {
65 match self.move_to(x) {
66 Some(i) => i as i32 + 1,
67 None => 0,
68 }
69 }
70
71 pub fn sum_intersections(&self, x: f64) -> i32 {
72 match self.move_to(x) {
73 Some(i) => self.intersections[i].direction,
74 None => 0,
75 }
76 }
77
78 pub fn filled(&self, x: f64, fill_rule: FillRule) -> bool {
79 fill_rule.interpret(self.sum_intersections(x))
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn fill_inside_outside() {
89 let mut s = Scanline::new();
90 s.set_intersections(vec![
91 Intersection {
92 x: 1.0,
93 direction: 1,
94 },
95 Intersection {
96 x: 3.0,
97 direction: -1,
98 },
99 ]);
100 assert!(!s.filled(0.0, FillRule::NonZero));
101 assert!(s.filled(2.0, FillRule::NonZero));
102 assert!(!s.filled(4.0, FillRule::NonZero));
103 }
104}