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