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: NoAttributes = crate::path::Path::builder();
82
83 path.begin(at:point(x:0.0, y:0.0));
84 path.line_to(point(x:1.0, y:0.0));
85 path.line_to(point(x:1.0, y:1.0));
86 path.line_to(point(x:0.0, y:1.0));
87 path.close();
88
89 path.begin(at:point(x:0.0, y:0.0));
90 path.line_to(point(x:0.0, y:1.0));
91 path.line_to(point(x:1.0, y:1.0));
92 path.line_to(point(x:1.0, y:0.0));
93 path.close();
94
95 let path: Path = path.build();
96
97 let mut iter: 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}
103