maker_panel/features/
repeating.rs1use geo::{Coordinate, MultiPolygon};
2use std::fmt;
3
4#[derive(Debug, Clone)]
6pub struct Tile<U = super::Rect> {
7 inner: U,
8 direction: crate::Direction,
9 amt: usize,
10 v_score: bool,
11}
12
13impl<U: super::Feature> Tile<U> {
14 pub fn new(inner: U, direction: crate::Direction, amt: usize) -> Self {
16 let v_score = false;
17 Self {
18 inner,
19 direction,
20 amt,
21 v_score,
22 }
23 }
24
25 pub fn v_score(mut self, v_score: bool) -> Self {
27 self.v_score = v_score;
28 self
29 }
30}
31
32impl<U: super::Feature> fmt::Display for Tile<U> {
33 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
34 write!(
35 f,
36 "repeating::Tile<{}>({} = {})",
37 self.inner, self.direction, self.amt
38 )
39 }
40}
41
42impl<U: super::Feature + Clone> super::Feature for Tile<U> {
43 fn name(&self) -> &'static str {
44 "repeating::Tile"
45 }
46
47 fn edge_subtract(&self) -> Option<MultiPolygon<f64>> {
48 match self.inner.edge_subtract() {
49 Some(sub_geo) => {
50 let mut out = sub_geo.clone();
51
52 use geo::{bounding_rect::BoundingRect, translate::Translate};
53 let bounds = match self.inner.edge_union() {
54 Some(edge_geo) => edge_geo.bounding_rect().unwrap(),
55 None => sub_geo.clone().bounding_rect().unwrap(),
56 };
57
58 for i in 0..self.amt {
59 let mut next = sub_geo.clone();
60 let (x, y) = self.direction.offset(bounds);
61 next.translate_inplace(i as f64 * x, i as f64 * y);
62
63 use geo_booleanop::boolean::BooleanOp;
64 out = out.union(&next);
65 }
66 Some(out)
67 }
68
69 None => None,
70 }
71 }
72
73 fn edge_union(&self) -> Option<MultiPolygon<f64>> {
74 match self.inner.edge_union() {
75 Some(edge_geo) => {
76 let mut out = edge_geo.clone();
77 use geo::{bounding_rect::BoundingRect, translate::Translate};
78 let bounds = edge_geo.bounding_rect().unwrap();
79
80 for i in 0..self.amt {
81 let mut next = edge_geo.clone();
82 let (x, y) = self.direction.offset(bounds);
83 next.translate_inplace(i as f64 * x, i as f64 * y);
84
85 use geo_booleanop::boolean::BooleanOp;
86 out = out.union(&next);
87 }
88 Some(out)
89 }
90 None => None,
91 }
92 }
93
94 fn translate(&mut self, v: Coordinate<f64>) {
95 self.inner.translate(v)
96 }
97
98 fn interior(&self) -> Vec<super::InnerAtom> {
99 let inner = self.inner.interior();
100 let mut out = Vec::with_capacity(inner.len() * self.amt);
101
102 let bounds = match self.inner.edge_union() {
103 Some(edge_geo) => {
104 use geo::bounding_rect::BoundingRect;
105 edge_geo.bounding_rect().unwrap()
106 }
107 None => {
108 use geo::{bounding_rect::BoundingRect, Geometry, GeometryCollection};
109 let bounds = Geometry::GeometryCollection(GeometryCollection(
110 inner
111 .iter()
112 .map(|a| a.bounds())
113 .filter(|b| b.is_some())
114 .map(|b| Geometry::Rect(b.unwrap()))
115 .collect(),
116 ));
117 bounds.bounding_rect().unwrap()
118 }
119 };
120
121 for i in 0..self.amt {
122 let (x, y) = self.direction.offset(bounds);
123 let (x, y) = (i as f64 * x, i as f64 * y);
124
125 for v in inner.iter() {
126 let mut v = v.clone();
127 v.translate(x, y);
128 out.push(v);
129 }
130
131 if self.v_score && i < self.amt - 1 {
132 let (x, y) = (x + bounds.width() / 2., y + bounds.height() / 2.);
133
134 out.push(match self.direction {
135 crate::Direction::Left | crate::Direction::Right => {
136 super::InnerAtom::VScoreV(x)
137 }
138 crate::Direction::Down | crate::Direction::Up => super::InnerAtom::VScoreH(y),
139 })
140 }
141 }
142 out
143 }
144}