svg_tikz/
tiled.rs

1use super::*;
2
3pub struct Tiled<'a, P: Processor> {
4    default: &'a mut P,
5    is_tiled: usize,
6    group_depth: usize,
7    tile_buf: Vec<Element>,
8    tile_x0: f32,
9    tile_y0: f32,
10    tile_x1: f32,
11    tile_y1: f32,
12    x0: f32,
13    x1: f32,
14    y0: f32,
15    y1: f32,
16    style: TiledStyle
17}
18
19#[derive(Clone, Copy, Debug)]
20pub struct TiledStyle {
21    pub h_grid_margin: f32,
22    pub v_grid_margin: f32,
23    pub tile_margin: f32,
24    pub grid_color: Option<[u8; 3]>,
25}
26
27impl Default for TiledStyle {
28    fn default() -> Self {
29        TiledStyle {
30            tile_margin: 0.1,
31            h_grid_margin: 10.,
32            v_grid_margin: 10.,
33            grid_color: Some([200; 3]),
34        }
35    }
36}
37
38impl<'a, P: Processor> Tiled<'a, P> {
39    pub fn new(p: &'a mut P, style: TiledStyle) -> Self {
40        Tiled {
41            default: p,
42            is_tiled: std::usize::MAX,
43            group_depth: 0,
44            tile_buf: Vec::new(),
45            tile_x0: std::f32::INFINITY,
46            tile_y0: std::f32::INFINITY,
47            tile_x1: -std::f32::INFINITY,
48            tile_y1: -std::f32::INFINITY,
49            x0: std::f32::INFINITY,
50            y0: std::f32::INFINITY,
51            x1: -std::f32::INFINITY,
52            y1: -std::f32::INFINITY,
53            style
54        }
55    }
56
57    fn draw_tile(&mut self, x: f32, y: f32, style: &Style) {
58        self.tile_buf.push(Element::Rectangle {
59            style: Style {
60                fill_color: style.stroke_color.clone(),
61                stroke_color: Some([0, 0, 0]),
62                fill_opacity: style.opacity.clone(),
63                .. Style::default()
64            },
65            x0: x - 5. + self.style.tile_margin,
66            y0: y - 5. + self.style.tile_margin,
67            x1: x + 5. - self.style.tile_margin,
68            y1: y + 5. - self.style.tile_margin,
69        });
70        self.x1 = self.x1.max(x);
71        self.y1 = self.y1.max(y);
72        self.x0 = self.x0.min(x);
73        self.y0 = self.y0.min(y);
74        self.tile_x0 = self.tile_x0.min(x - self.style.h_grid_margin);
75        self.tile_y0 = self.tile_y0.min(y - self.style.v_grid_margin);
76        self.tile_x1 = self.tile_x1.max(x + self.style.h_grid_margin);
77        self.tile_y1 = self.tile_y1.max(y + self.style.v_grid_margin);
78    }
79
80    fn line(&mut self, current_is_drawn: &mut bool, mut x0: f32, mut y0: f32, x: f32, y: f32, style: &Style) {
81        debug!("=> {:?} {:?}", x, y);
82        // If the line has a non-zero horizontal component.
83        if x.round() > x0.round() {
84            if *current_is_drawn {
85                x0 += 10.
86            }
87            while x0.round() <= x.round() {
88                self.draw_tile(x0, y0, &style);
89                x0 += 10.
90            }
91            *current_is_drawn = true;
92        } else if x.round() < x0.round() {
93            if *current_is_drawn {
94                x0 -= 10.
95            }
96            while x0.round() >= x.round() {
97                self.draw_tile(x0, y0, &style);
98                x0 -= 10.
99            }
100            *current_is_drawn = true;
101        }
102
103        // If the line has a non-zero vertical component.
104        if y.round() > y0.round() {
105            if *current_is_drawn {
106                y0 += 10.
107            }
108            while y0.round() <= y.round() {
109                self.draw_tile(x, y0, &style);
110                y0 += 10.
111            }
112            *current_is_drawn = true;
113        } else if y.round() < y0.round() {
114            if *current_is_drawn {
115                y0 -= 10.
116            }
117            while y0.round() >= y.round() {
118                self.draw_tile(x, y0, &style);
119                y0 -= 10.
120            }
121            *current_is_drawn = true;
122        }
123    }
124
125    fn path(
126        &mut self,
127        close: bool,
128        path: &[PathElement],
129        style: &Style,
130    ) -> Result<(), failure::Error> {
131        let (mut x0, mut y0) = (0., 0.);
132        let mut initial = None;
133        let mut current_is_drawn = true;
134        for elt in path.iter() {
135            match *elt {
136                PathElement::Move { x, y } => {
137                    debug!("MV {:?} {:?}", x, y);
138                    if !current_is_drawn {
139                        self.draw_tile(x0, y0, &style);
140                    }
141                    current_is_drawn = false;
142                    if close {
143                        if let Some((mut x, mut y)) = initial.take() {
144                            // Don't draw the last tile
145                            if x0 > x {
146                                x += 1.
147                            } else if x0 < x {
148                                x -= 1.
149                            }
150                            if y0 > x {
151                                y += 1.
152                            } else if y0 < x {
153                                y -= 1.
154                            }
155                            self.line(&mut current_is_drawn, x0, y0, x, y, style)
156                        }
157                    }
158                    initial = Some((x, y));
159                    x0 = x;
160                    y0 = y;
161                }
162                PathElement::Bezier3 { .. } => {}
163                PathElement::Line { x, y } => {
164                    self.line(&mut current_is_drawn, x0, y0, x, y, style);
165                    x0 = x;
166                    y0 = y;
167                }
168            }
169        }
170        if close {
171            if let Some((mut x, mut y)) = initial.take() {
172                if x0 > x {
173                    x += 1.
174                } else if x0 < x {
175                    x -= 1.
176                }
177                if y0 > x {
178                    y += 1.
179                } else if y0 < x {
180                    y -= 1.
181                }
182                self.line(&mut current_is_drawn, x0, y0, x, y, style)
183            }
184        }
185        Ok(())
186    }
187
188    fn tiled_element(&mut self, element: Element) -> Result<(), failure::Error> {
189        debug!("tiled_element: {:?}", element);
190        match element {
191            Element::Path {
192                close,
193                ref style,
194                ref path,
195            } => {
196                if style.stroke_color.is_some() {
197                    self.path(close, path, style)?;
198                    self.tile_buf.push(element);
199                } else {
200                    // Else, if the path is not stroked, just compute the grid's bounding box.
201                    for elt in path.iter() {
202                        match *elt {
203                            PathElement::Move { x, y } => {
204                                self.x1 = self.x1.max(x);
205                                self.y1 = self.y1.max(y);
206                                self.x0 = self.x0.min(x);
207                                self.y0 = self.y0.min(y);
208                            }
209                            PathElement::Bezier3 {
210                                x,
211                                y,
212                                x1,
213                                y1,
214                                x2,
215                                y2,
216                            } => {
217                                self.x1 = self.x1.max(x).max(x1).max(x2);
218                                self.y1 = self.y1.max(y).max(y1).max(y2);
219                                self.x0 = self.x0.min(x).min(x1).min(x2);
220                                self.y0 = self.y0.min(y).min(y1).min(y2);
221                            }
222                            PathElement::Line { x, y } => {
223                                self.x1 = self.x1.max(x);
224                                self.y1 = self.y1.max(y);
225                                self.x0 = self.x0.min(x);
226                                self.y0 = self.y0.min(y);
227                            }
228                        }
229                    }
230                    self.tile_buf.push(element)
231                }
232            }
233            e => self.tile_buf.push(e),
234        }
235        Ok(())
236    }
237}
238
239impl<'a, P: Processor> Processor for Tiled<'a, P> {
240    fn process(&mut self, element: Element) -> Result<(), failure::Error> {
241        debug!("element {:?}", element);
242        match element {
243            Element::BeginGroup { id } => {
244                self.group_depth += 1;
245                if id.contains("tiled") {
246                    self.is_tiled = self.group_depth;
247                }
248                self.tile_buf.push(Element::BeginGroup { id })
249            }
250            Element::EndGroup => {
251                self.group_depth -= 1;
252                if self.group_depth < self.is_tiled {
253                    self.is_tiled = std::usize::MAX
254                }
255                self.tile_buf.push(Element::EndGroup)
256            }
257            e => {
258                if self.group_depth >= self.is_tiled {
259                    self.tiled_element(e)?
260                } else {
261                    self.tile_buf.push(e)
262                }
263            }
264        }
265        Ok(())
266    }
267
268    fn finish(&mut self) -> Result<(), failure::Error> {
269        debug!("finish, outputting grid");
270        let mut x0 = self.tile_x0 - 5.;
271        let mut y0 = self.tile_y0 - 5.;
272        self.x0 = self.x0.min(x0);
273        self.y0 = self.y0.min(y0);
274        let xoff = ((x0 - self.x0) / 10.).round() * 10.;
275        let yoff = ((y0 - self.y0) / 10.).round() * 10.;
276        x0 -= xoff;
277        y0 -= yoff;
278        let x1 = self.x1.max(self.tile_x1);
279        let y1 = self.y1.max(self.tile_y1);
280        let w = ((x1 - x0) / 10.).round() * 10.;
281        let h = ((y1 - y0) / 10.).round() * 10.;
282
283        let style = Style {
284            stroke_color: self.style.grid_color.clone(),
285            .. Style::default()
286        };
287
288        self.default.process(Element::Rectangle {
289            style: style.clone(),
290            x0: self.tile_x0 - 5. - xoff,
291            y0: self.tile_y0 - 5. - yoff,
292            x1: self.tile_x0 - 5. - xoff + w,
293            y1: self.tile_y0 - 5. - yoff + h,
294        })?;
295
296
297        x0 += 10.;
298        while x0.round() < x1.round() {
299            self.default.process(Element::Path {
300                close: false,
301                style: style.clone(),
302                path: vec![
303                    PathElement::Move { x: x0, y: y0 + h },
304                    PathElement::Line { x: x0, y: y0 },
305                ],
306            })?;
307            x0 += 10.;
308        }
309
310        y0 += 10.;
311        while y0.round() < y1.round() {
312            self.default.process(Element::Path {
313                close: false,
314                style: style.clone(),
315                path: vec![
316                    PathElement::Move {
317                        x: self.tile_x0 - 5. - xoff,
318                        y: y0,
319                    },
320                    PathElement::Line {
321                        x: self.tile_x0 - 5. - xoff + w,
322                        y: y0,
323                    },
324                ],
325            })?;
326            y0 += 10.;
327        }
328
329        for elt in self.tile_buf.drain(..) {
330            self.default.process(elt)?
331        }
332        self.default.finish()
333    }
334}