maker_panel/features/
negative.rs1use geo::{Coordinate, MultiPolygon};
2use std::fmt;
3
4#[derive(Debug, Clone)]
6pub struct Negative<U = super::Unit> {
7 features: Vec<U>,
8}
9
10impl<U: super::Feature + fmt::Debug + Clone> Negative<U> {
11 pub fn new(features: Vec<U>) -> Self {
12 Self { features }
13 }
14}
15
16impl<U> fmt::Display for Negative<U>
17where
18 U: super::Feature + fmt::Debug + Clone,
19{
20 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
21 write!(f, "Negative({:?})", self.features)
22 }
23}
24
25impl<U> super::Feature for Negative<U>
26where
27 U: super::Feature + fmt::Debug + Clone,
28{
29 fn name(&self) -> &'static str {
30 "negative"
31 }
32
33 fn edge_union(&self) -> Option<MultiPolygon<f64>> {
34 self.features
35 .iter()
36 .map(|f| match f.edge_subtract() {
37 Some(edge_geo) => Some(edge_geo.clone()),
38 None => None,
39 })
40 .filter(|f| f.is_some())
41 .map(|f| f.unwrap())
42 .fold(None, |mut acc, g| {
43 use geo_booleanop::boolean::BooleanOp;
44 if let Some(current) = acc {
45 acc = Some(g.union(¤t));
46 } else {
47 acc = Some(g);
48 };
49 acc
50 })
51 }
52
53 fn edge_subtract(&self) -> Option<MultiPolygon<f64>> {
54 self.features
55 .iter()
56 .map(|f| match f.edge_union() {
57 Some(edge_geo) => Some(edge_geo.clone()),
58 None => None,
59 })
60 .filter(|f| f.is_some())
61 .map(|f| f.unwrap())
62 .fold(None, |mut acc, g| {
63 use geo_booleanop::boolean::BooleanOp;
64 if let Some(current) = acc {
65 acc = Some(g.union(¤t));
66 } else {
67 acc = Some(g);
68 };
69 acc
70 })
71 }
72
73 fn translate(&mut self, v: Coordinate<f64>) {
74 for e in self.features.iter_mut() {
75 e.translate(v);
76 }
77 }
78
79 fn interior(&self) -> Vec<super::InnerAtom> {
80 vec![]
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87 use crate::features::{Feature, Rect};
88
89 #[test]
90 fn not_union() {
91 let a = Negative::new(vec![
92 Rect::with_center([0., 0.].into(), 2., 3.),
93 Rect::with_center([0., 0.].into(), 3., 2.),
94 ]);
95
96 assert_eq!(a.edge_union(), None,);
97 assert!(a.edge_subtract().is_some());
98 }
99
100 #[test]
101 fn basic() {
102 let a = Negative::new(vec![
103 Rect::with_center([0., 0.].into(), 1., 1.),
104 Rect::with_center([0.5, 0.].into(), 1., 1.),
105 ]);
106
107 assert_eq!(
108 a.edge_subtract(),
109 Some(geo::MultiPolygon::from(vec![geo::Polygon::new(
110 geo::LineString(vec![
111 geo::Coordinate { x: -0.5, y: -0.5 },
112 geo::Coordinate { x: 0.0, y: -0.5 },
113 geo::Coordinate { x: 0.5, y: -0.5 },
114 geo::Coordinate { x: 1.0, y: -0.5 },
115 geo::Coordinate { x: 1.0, y: 0.5 },
116 geo::Coordinate { x: 0.5, y: 0.5 },
117 geo::Coordinate { x: 0.0, y: 0.5 },
118 geo::Coordinate { x: -0.5, y: 0.5 },
119 geo::Coordinate { x: -0.5, y: -0.5 }
120 ]),
121 vec![],
122 )])),
123 );
124 }
125}