dot_canvas/
grid.rs

1use crate::Shape;
2
3/// ```ignore
4///      0 1 2 3 4           B C D          BRAILLE ⣿
5///     0┌─┬─┬─┬─┐        A┌─┬─┬─┬─┐E       ┌       ┐
6///     1├─┼─┼─┼─┤         │ │ │ │ │         ─▮───▮─
7///     2├─┼─┼─┼─┤        F├─G─H─I─┤J         │   │
8///     3├─┼─┼─┼─┤         │ │ │ │ │         ─▮───▮─
9///     4├─┼─┼─┼─┤        K├─L─M─N─┤O         │   │
10///     5├─┼─┼─┼─┤         │ │ │ │ │         ─▮───▮─
11///     6├─┼─┼─┼─┤        P├─Q─R─S─┤T         │   │
12///     7├─┼─┼─┼─┤         │ │ │ │ │         ─▮───▮─
13///     8└─┴─┴─┴─┘        U└─┴─┴─┴─┘Y       └       ┘
14/// ```                      V W X
15
16pub const DOTS: [[u16; 2]; 4] = [
17    [0x0001, 0x0008],
18    [0x0002, 0x0010],
19    [0x0004, 0x0020],
20    [0x0040, 0x0080],
21];
22pub const BRAILLE_OFFSET: u16 = 0x2800;
23
24pub struct Grid {
25    width: usize,
26    cells: Vec<u16>,
27}
28
29impl Grid {
30    pub fn new(width: usize, height: usize) -> Grid {
31        Grid {
32            width,
33            cells: vec![BRAILLE_OFFSET; width * height],
34        }
35    }
36
37    pub fn to_string(&self) -> String {
38        let mut buf = String::new();
39        for (i, cell) in self.cells.iter().enumerate() {
40            if i != 0 && i % self.width == 0 {
41                buf.push('\n');
42            }
43            let ch = String::from_utf16(&[*cell]).unwrap();
44            if ch == "\u{2800}" {
45                buf.push(' ');
46            } else {
47                buf.push_str(&ch);
48            }
49        }
50        buf
51    }
52
53    pub fn reset(&mut self) {
54        for c in &mut self.cells {
55            *c = BRAILLE_OFFSET;
56        }
57    }
58}
59
60/// Holds the state of the Canvas when painting to it.
61/// width of 1 cell text is 0.5 and height is 1.0
62pub struct Context {
63    width: f32,
64    height: f32,
65    x_bounds: (f32, f32),
66    y_bounds: (f32, f32),
67    grid: Grid,
68}
69
70impl Context {
71    pub fn new(width: f32, height: f32) -> Self {
72        let width = width * 2.0;
73        Context {
74            width,
75            height,
76            x_bounds: (0.0, width),
77            y_bounds: (0.0, height),
78            grid: Grid::new(width as usize, height as usize),
79        }
80    }
81
82    pub fn to_string(&self) -> String {
83        self.grid.to_string()
84    }
85
86    /// Draw any object that may implement the Shape trait
87    pub fn draw<'b, S>(&mut self, shape: &'b S)
88    where
89        S: Shape<'b>,
90    {
91        let (left, right) = self.x_bounds;
92        let (top, bottom) = self.y_bounds;
93        for (x, y) in shape
94            .points()
95            .map(|(x, y)| (2.0 * x, y))
96            .filter(|&(x, y)| x >= left && x < right && y >= top && y < bottom)
97        {
98            let dy =
99                ((top - y) * (self.height) * 4.0 / (top - bottom)) as usize;
100            let dx =
101                ((x - left) * (self.width) * 2.0 / (right - left)) as usize;
102            let index = dy / 4 * self.width as usize + dx / 2;
103            let dy_index = dy % 4;
104            let dx_index = dx % 2;
105            let braille = DOTS[dy_index][dx_index];
106            self.grid.cells[index] |= braille;
107        }
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114    use crate::line::Line;
115
116    #[test]
117    fn draw_cell_horizontal_line() {
118        let mut context = Context::new(0.5, 1.0);
119
120        context.draw(&Line {
121            x1: 0.0,
122            y1: 0.5,
123            x2: 1.0,
124            y2: 0.5,
125        });
126        let result = context.to_string();
127        assert_eq!(result, "⠤");
128    }
129
130    #[test]
131    fn draw_cell_vertical_line() {
132        let mut context = Context::new(0.5, 1.0);
133
134        context.draw(&Line {
135            x1: 0.125,
136            y1: 0.0,
137            x2: 0.125,
138            y2: 1.0,
139        });
140        let result = context.to_string();
141        assert_eq!(result, "⡇");
142    }
143
144    #[test]
145    fn draw_cell_slant_line1() {
146        let mut context = Context::new(0.5, 1.0);
147
148        context.draw(&Line {
149            x1: 0.0,
150            y1: 0.0,
151            x2: 0.5,
152            y2: 1.0,
153        });
154        let result = context.to_string();
155        assert_eq!(result, "⢣");
156    }
157    #[test]
158    fn draw_cell_slant_line2() {
159        let mut context = Context::new(0.5, 1.0);
160
161        context.draw(&Line {
162            x1: 0.0,
163            y1: 1.0,
164            x2: 0.5,
165            y2: 0.0,
166        });
167        let result = context.to_string();
168        assert_eq!(result, "⡰");
169    }
170
171    #[test]
172    fn draw_cell_slant_line3() {
173        let mut context = Context::new(0.5, 1.0);
174
175        context.draw(&Line {
176            x1: 0.0,
177            y1: 0.75,
178            x2: 0.5,
179            y2: 0.375,
180        });
181        let result = context.to_string();
182        assert_eq!(result, "⡠");
183    }
184
185    #[test]
186    fn draw_horizontal_lines() {
187        let width = 10;
188        let height = 1;
189        let mut context = Context::new(width as f32, height as f32);
190
191        context.draw(&Line {
192            x1: 0.0,
193            y1: 0.5,
194            x2: width as f32,
195            y2: 0.5,
196        });
197        let result = context.to_string();
198        assert_eq!(result, "⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤");
199    }
200
201    #[test]
202    fn draw_vertical_lines() {
203        let width = 1.0;
204        let height = 10.0;
205        let mut context = Context::new(width as f32, height as f32);
206
207        context.draw(&Line {
208            x1: 0.5,
209            y1: 0.0,
210            x2: 0.5,
211            y2: height,
212        });
213        let result = context.to_string();
214        let expected =
215            [" ⡇", " ⡇", " ⡇", " ⡇", " ⡇", " ⡇", " ⡇", " ⡇", " ⡇", " ⡇"];
216        assert_eq!(result, expected.join("\n"));
217    }
218
219    #[test]
220    fn draw_slash_lines() {
221        let width = 10.0;
222        let height = 10.0;
223
224        let mut context = Context::new(width as f32, height as f32);
225        context.draw(&Line {
226            x1: 0.0,
227            y1: 0.0,
228            x2: width,
229            y2: height,
230        });
231        let result = context.to_string();
232
233        let expected = [
234            "⠑⢄                  ",
235            "  ⠑⢄                ",
236            "    ⠑⢄              ",
237            "      ⠑⢄            ",
238            "        ⠑⢄          ",
239            "          ⠑⢄        ",
240            "            ⠑⢄      ",
241            "              ⠑⢄    ",
242            "                ⠑⢄  ",
243            "                  ⠑⢄",
244        ];
245
246        assert_eq!(result, expected.join("\n"));
247    }
248
249    #[test]
250    fn draw_slash_lines2() {
251        let width = 10.0;
252        let height = 10.0;
253        let mut context = Context::new(width as f32, height as f32);
254
255        context.draw(&Line {
256            x1: width,
257            y1: height,
258            x2: 0.0,
259            y2: 0.0,
260        });
261        let result = context.to_string();
262        println!("{}", result);
263        let expected = [
264            "⠐⢄                  ",
265            "  ⠑⢄                ",
266            "    ⠑⢄              ",
267            "      ⠑⢄            ",
268            "        ⠑⢄          ",
269            "          ⠑⢄        ",
270            "            ⠑⢄      ",
271            "              ⠑⢄    ",
272            "                ⠑⢄  ",
273            "                  ⠑⢄",
274        ];
275        assert_eq!(result, expected.join("\n"));
276    }
277
278    #[test]
279    fn draw_slant_lines1() {
280        let width = 10.0;
281        let height = 10.0;
282        let mut context = Context::new(width as f32, height as f32);
283
284        context.draw(&Line {
285            x1: 0.0,
286            y1: height,
287            x2: width,
288            y2: 0.0,
289        });
290        let result = context.to_string();
291        println!("{}", result);
292
293        let expected = [
294            "                  ⢀⠔",
295            "                ⢀⠔⠁ ",
296            "              ⢀⠔⠁   ",
297            "            ⢀⠔⠁     ",
298            "          ⢀⠔⠁       ",
299            "        ⢀⠔⠁         ",
300            "      ⢀⠔⠁           ",
301            "    ⢀⠔⠁             ",
302            "  ⢀⠔⠁               ",
303            "⢀⠔⠁                 ",
304        ];
305
306        assert_eq!(result, expected.join("\n"));
307    }
308
309    #[test]
310    fn draw_slant_lines2() {
311        let width = 10.0;
312        let height = 10.0;
313        let mut context = Context::new(width as f32, height as f32);
314
315        context.draw(&Line {
316            x1: width - 0.5,
317            y1: 0.0,
318            x2: 0.0,
319            y2: height,
320        });
321        let result = context.to_string();
322        println!("{}", result);
323
324        let expected = [
325            "                 ⢀⠔⠁",
326            "               ⢀⠔⠁  ",
327            "             ⢀⠔⠁    ",
328            "           ⢀⠔⠁      ",
329            "         ⢀⠔⠁        ",
330            "        ⡠⠊          ",
331            "      ⡠⠊            ",
332            "    ⡠⠊              ",
333            "  ⡠⠊                ",
334            "⡠⠊                  ",
335        ];
336
337        assert_eq!(result, expected.join("\n"));
338    }
339}