lyon_algorithms/
winding.rs

1// Compute the winding of a path.
2
3use crate::geom::vector;
4use crate::path::{PathEvent, Winding};
5
6/// Compute the winding of the next sub-path.
7///
8/// The sub-path is expected to have a non-null area and no self-intersections, otherwise
9/// the result is unspecified.
10///
11/// The iterator is advanced so that `compute_winding` can be called multiple times
12/// to process the successive sub-paths of a path.
13///
14/// Returns `None` if there is no more sub-path or if the the iterator is malformed.
15pub fn compute_winding<Iter>(path: &mut Iter) -> Option<Winding>
16where
17    Iter: Iterator<Item = PathEvent>,
18{
19    let first = if let Some(PathEvent::Begin { at }) = path.next() {
20        at
21    } else {
22        return None;
23    };
24    let mut area = 0.0;
25    let mut v0 = vector(0.0, 0.0);
26
27    for evt in path {
28        match evt {
29            PathEvent::Begin { .. } => {
30                return None;
31            }
32            PathEvent::End { last, first, .. } => {
33                let v1 = last - first;
34                area += v0.cross(v1);
35
36                return if area > 0.0 {
37                    Some(Winding::Positive)
38                } else {
39                    Some(Winding::Negative)
40                };
41            }
42            PathEvent::Line { to, .. } => {
43                let v1 = to - first;
44                area += v0.cross(v1);
45                v0 = v1;
46            }
47            PathEvent::Quadratic { ctrl, to, .. } => {
48                let v1 = ctrl - first;
49                let v2 = to - first;
50                area += v0.cross(v1) + v1.cross(v2);
51                v0 = v2;
52            }
53            PathEvent::Cubic {
54                ctrl1, ctrl2, to, ..
55            } => {
56                let v1 = ctrl1 - first;
57                let v2 = ctrl2 - first;
58                let v3 = to - first;
59                area += v0.cross(v1) + v1.cross(v2) + v2.cross(v3);
60                v0 = v3;
61            }
62        };
63    }
64
65    None
66}
67
68/// Iterator over the sub-path windings of a path.
69pub struct Windings<Iter = PathEvent>(pub Iter);
70
71impl<Iter: Iterator<Item = PathEvent>> Iterator for Windings<Iter> {
72    type Item = Winding;
73    fn next(&mut self) -> Option<Winding> {
74        compute_winding(&mut self.0)
75    }
76}
77
78#[test]
79fn path_winding() {
80    use crate::geom::point;
81    let mut path = crate::path::Path::builder();
82
83    path.begin(point(0.0, 0.0));
84    path.line_to(point(1.0, 0.0));
85    path.line_to(point(1.0, 1.0));
86    path.line_to(point(0.0, 1.0));
87    path.close();
88
89    path.begin(point(0.0, 0.0));
90    path.line_to(point(0.0, 1.0));
91    path.line_to(point(1.0, 1.0));
92    path.line_to(point(1.0, 0.0));
93    path.close();
94
95    let path = path.build();
96
97    let mut iter = path.iter();
98
99    assert_eq!(compute_winding(&mut iter), Some(Winding::Positive));
100    assert_eq!(compute_winding(&mut iter), Some(Winding::Negative));
101    assert_eq!(compute_winding(&mut iter), None);
102}