ezu_features/ops/
boolean.rs1use i_overlay::core::fill_rule::FillRule;
5use i_overlay::core::overlay_rule::OverlayRule;
6use i_overlay::float::single::SingleFloatOverlay;
7
8use crate::Polygon;
9
10use super::convert::{polygon_to_f, polygons_from_shapes};
11
12#[derive(Debug, Clone, Copy)]
13pub enum BoolOp {
14 Union,
15 Intersection,
16 Difference,
17 SymmetricDifference,
18}
19
20impl BoolOp {
21 fn to_overlay_rule(self) -> OverlayRule {
22 match self {
23 BoolOp::Union => OverlayRule::Union,
24 BoolOp::Intersection => OverlayRule::Intersect,
25 BoolOp::Difference => OverlayRule::Difference,
26 BoolOp::SymmetricDifference => OverlayRule::Xor,
27 }
28 }
29}
30
31pub fn polygon_boolean(a: &[Polygon], b: &[Polygon], op: BoolOp) -> Vec<Polygon> {
35 if a.is_empty() && b.is_empty() {
36 return Vec::new();
37 }
38 let subj: Vec<Vec<Vec<[f64; 2]>>> = a.iter().map(polygon_to_f).collect();
42 let clip: Vec<Vec<Vec<[f64; 2]>>> = b.iter().map(polygon_to_f).collect();
43 let result = subj.overlay(&clip, op.to_overlay_rule(), FillRule::EvenOdd);
44 polygons_from_shapes(&result)
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50
51 fn rect(x0: i32, y0: i32, x1: i32, y1: i32) -> Polygon {
52 Polygon {
53 exterior: vec![(x0, y0), (x1, y0), (x1, y1), (x0, y1)],
54 holes: vec![],
55 }
56 }
57
58 #[test]
59 fn union_of_overlapping_rects_yields_one_polygon() {
60 let a = vec![rect(0, 0, 10, 10)];
61 let b = vec![rect(5, 5, 15, 15)];
62 let u = polygon_boolean(&a, &b, BoolOp::Union);
63 assert_eq!(u.len(), 1, "union should merge into 1 polygon: {u:?}");
64 }
65
66 #[test]
67 fn intersection_returns_overlap_region() {
68 let a = vec![rect(0, 0, 10, 10)];
69 let b = vec![rect(5, 5, 15, 15)];
70 let i = polygon_boolean(&a, &b, BoolOp::Intersection);
71 assert_eq!(i.len(), 1);
72 let mut min_x = i32::MAX;
74 let mut max_x = i32::MIN;
75 for &(x, _) in &i[0].exterior {
76 min_x = min_x.min(x);
77 max_x = max_x.max(x);
78 }
79 assert_eq!((min_x, max_x), (5, 10));
80 }
81
82 #[test]
83 fn difference_removes_b_from_a() {
84 let a = vec![rect(0, 0, 10, 10)];
85 let b = vec![rect(5, 0, 15, 10)];
86 let d = polygon_boolean(&a, &b, BoolOp::Difference);
87 assert_eq!(d.len(), 1);
88 let mut max_x = i32::MIN;
89 for &(x, _) in &d[0].exterior {
90 max_x = max_x.max(x);
91 }
92 assert_eq!(max_x, 5);
93 }
94}