maker_panel/features/
repeating.rs

1use geo::{Coordinate, MultiPolygon};
2use std::fmt;
3
4/// A feature which is repeated in a tiling fashion.
5#[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    /// Constructs a new tiling feature.
15    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    /// Returns a new tiling feature with the given v-score setting.
26    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}