1use crate::fragment::arc;
2use crate::fragment::line;
3use crate::fragment::thick;
4use crate::fragment::Cell;
5use crate::fragment::Fragment;
6use crate::string_buffer::StringBuffer;
7use crate::unicode_map::FRAGMENT_CHAR;
8pub use border::Border;
9use std::collections::HashMap;
10
11mod border;
12
13#[derive(Debug)]
14pub struct Canvas {
15 cells: HashMap<(usize, usize), Vec<Fragment>>,
16}
17
18impl Canvas {
19 pub fn new() -> Self {
20 Canvas {
21 cells: HashMap::new(),
22 }
23 }
24
25 pub fn draw_horizontal_line(
26 &mut self,
27 start: (usize, usize),
28 end: (usize, usize),
29 use_thick: bool,
30 ) {
31 let (x1, y1) = start;
32 let (x2, y2) = end;
33 assert_eq!(y1, y2, "horizontal line must have the same y1 and y2");
34 let (x1, x2) = if x1 > x2 { (x2, x1) } else { (x1, x2) };
36 let k = Cell::K;
37 let m = Cell::M;
38 let o = Cell::O;
39 let mo = if use_thick { thick(m, o) } else { line(m, o) };
40 let km = if use_thick { thick(k, m) } else { line(k, m) };
41
42 let width = x2 - x1 + 1;
43 for i in 0..width {
44 if let Some(existing) = self.cells.get_mut(&(x1 + i, y1)) {
45 if i == 0 {
46 existing.push(mo);
47 } else if i == width - 1 {
48 existing.push(km);
49 } else {
50 existing.push(km);
51 existing.push(mo);
52 }
53 } else {
54 if i == 0 {
55 self.cells.insert((x1 + i, y1), vec![mo]);
56 } else if i == width - 1 {
57 self.cells.insert((x1 + i, y1), vec![km]);
58 } else {
59 self.cells.insert((x1 + i, y1), vec![mo, km]);
60 }
61 }
62 }
63 }
64
65 pub fn eraser_horizontal_line(
67 &mut self,
68 start: (usize, usize),
69 end: (usize, usize),
70 use_thick: bool,
71 ) {
72 let (x1, y1) = start;
73 let (x2, y2) = end;
74 assert_eq!(y1, y2, "horizontal line must have the same y1 and y2");
75 let (x1, x2) = if x1 > x2 { (x2, x1) } else { (x1, x2) };
77 let k = Cell::K;
78 let m = Cell::M;
79 let o = Cell::O;
80 let mo = if use_thick { thick(m, o) } else { line(m, o) };
81 let km = if use_thick { thick(k, m) } else { line(k, m) };
82
83 let width = x2 - x1 + 1;
84 for i in 0..width {
85 if let Some(existing) = self.cells.get_mut(&(x1 + i, y1)) {
86 if i == 0 {
87 existing.retain(|line| *line != mo);
88 } else if i == width - 1 {
89 existing.retain(|line| *line != km);
90 } else {
91 existing.retain(|line| *line != km);
92 existing.retain(|line| *line != mo);
93 }
94 }
95 }
96 }
97
98 pub fn draw_vertical_line(
99 &mut self,
100 start: (usize, usize),
101 end: (usize, usize),
102 use_thick: bool,
103 ) {
104 let (x1, y1) = start;
105 let (x2, y2) = end;
106 assert_eq!(x1, x2, "veritcal line must have the same x1 and x2");
107 let (y1, y2) = if y1 > y2 { (y2, y1) } else { (y1, y2) };
109
110 let c = Cell::C;
111 let m = Cell::M;
112 let w = Cell::W;
113
114 let mw = if use_thick { thick(m, w) } else { line(m, w) };
115 let cm = if use_thick { thick(c, m) } else { line(c, m) };
116
117 let height = y2 - y1 + 1;
118 for j in 0..height {
119 if let Some(existing) = self.cells.get_mut(&(x1, y1 + j)) {
120 if j == 0 {
121 existing.push(mw);
122 } else if j == height - 1 {
123 existing.push(cm);
124 } else {
125 existing.push(cm);
126 existing.push(mw);
127 }
128 } else {
129 if j == 0 {
130 self.cells.insert((x1, y1 + j), vec![mw]);
131 } else if j == height - 1 {
132 self.cells.insert((x1, y1 + j), vec![cm]);
133 } else {
134 self.cells.insert((x1, y1 + j), vec![mw, cm]);
135 }
136 }
137 }
138 }
139
140 pub fn draw_rect(
141 &mut self,
142 start: (usize, usize),
143 end: (usize, usize),
144 border: Border,
145 ) {
146 let (x1, y1) = start;
147 let (x2, y2) = end;
148 if border.has_top {
149 self.draw_horizontal_line(
150 (x1, y1),
151 (x2, y1),
152 border.use_thick_border,
153 );
154 }
155 if border.has_bottom {
156 self.draw_horizontal_line(
157 (x1, y2),
158 (x2, y2),
159 border.use_thick_border,
160 );
161 }
162
163 if border.has_left {
164 self.draw_vertical_line(
165 (x1, y1),
166 (x1, y2),
167 border.use_thick_border,
168 );
169 }
170 if border.has_right {
171 self.draw_vertical_line(
172 (x2, y1),
173 (x2, y2),
174 border.use_thick_border,
175 );
176 }
177
178 if !border.use_thick_border {
179 let o = Cell::O;
180 let w = Cell::W;
181 let k = Cell::K;
182 let c = Cell::C;
183 if border.is_top_left_rounded {
184 self.cells.insert((x1, y1), vec![arc(o, w)]);
185 }
186 if border.is_top_right_rounded {
187 self.cells.insert((x2, y1), vec![arc(w, k)]);
188 }
189 if border.is_bottom_left_rounded {
190 self.cells.insert((x1, y2), vec![arc(c, o)]);
191 }
192 if border.is_bottom_right_rounded {
193 self.cells.insert((x2, y2), vec![arc(k, c)]);
194 }
195 }
196 }
197
198 pub fn draw_text(&mut self, start: (usize, usize), text: &str) {
199 let (x, y) = start;
200 for (i, ch) in text.chars().enumerate() {
201 self.cells.insert((x + i, y), vec![Fragment::Char(ch)]);
202 }
203 }
204
205 fn resolve(fragments: &[Fragment]) -> Option<char> {
206 let mut fragments = fragments.to_owned();
207 fragments.sort();
208 fragments.dedup();
209 FRAGMENT_CHAR.get(&fragments).map(|c| *c)
210 }
211
212 pub fn get_cells<'a>(
213 &'a self,
214 ) -> Box<dyn Iterator<Item = (usize, usize, char)> + 'a> {
215 let mut cells = self.cells.iter().collect::<Vec<_>>();
216 cells.sort_by(|a, b| a.0.cmp(&b.0).then(a.1.cmp(&b.1)));
217 Box::new(cells.into_iter().flat_map(|((x, y), frags)| {
218 Self::resolve(frags).map(|ch| (*x, *y, ch))
219 }))
220 }
221
222 pub fn dump(&self) -> String {
223 let mut sb = StringBuffer::new();
224 let cells = self.get_cells();
225 cells.for_each(|(x, y, ch)| sb.add_char(x as i32, y as i32, ch));
226 sb.to_string()
227 }
228}
229
230#[cfg(test)]
231mod test {
232 use super::*;
233
234 #[test]
235 fn rect1() {
236 let mut canvas = Canvas::new();
237 canvas.draw_rect((0, 0), (2, 2), Border::thin());
238 let mut cells = canvas
239 .cells
240 .iter()
241 .map(|((x, y), frag)| (x, y, frag))
242 .collect::<Vec<_>>();
243 cells.sort_by(|a, b| a.1.cmp(b.1).then(a.0.cmp(b.0)));
244 println!("cells: {:#?}", cells);
245 assert_eq!(cells.len(), 8);
246 let char_cells: Vec<(usize, usize, char)> =
247 canvas.get_cells().collect();
248 println!("char cells: {:#?}", char_cells);
249 println!("dump: \n{}", canvas.dump());
250 assert_eq!(
251 "┌─┐\n\
252 │ │\n\
253 └─┘",
254 canvas.dump()
255 );
256 }
257 #[test]
258 fn rect2() {
259 let mut canvas = Canvas::new();
260 canvas.draw_rect((0, 0), (4, 2), Border::thick());
261 let mut cells = canvas
262 .cells
263 .iter()
264 .map(|((x, y), frag)| (x, y, frag))
265 .collect::<Vec<_>>();
266 cells.sort_by(|a, b| a.1.cmp(b.1).then(a.0.cmp(b.0)));
267 println!("cells: {:#?}", cells);
268 assert_eq!(cells.len(), 12);
269 let char_cells: Vec<(usize, usize, char)> =
270 canvas.get_cells().collect();
271 println!("char cells: {:#?}", char_cells);
272 println!("dump: \n{}", canvas.dump());
273 assert_eq!(
274 "┏━━━┓\n\
275 ┃ ┃\n\
276 ┗━━━┛",
277 canvas.dump()
278 );
279 }
280
281 #[test]
282 fn rect3() {
283 let mut canvas = Canvas::new();
284 canvas.draw_rect((0, 0), (6, 2), Border::rounded());
285 let mut cells = canvas
286 .cells
287 .iter()
288 .map(|((x, y), frag)| (x, y, frag))
289 .collect::<Vec<_>>();
290 cells.sort_by(|a, b| a.1.cmp(b.1).then(a.0.cmp(b.0)));
291 println!("cells: {:#?}", cells);
292 assert_eq!(cells.len(), 16);
293 let char_cells: Vec<(usize, usize, char)> =
294 canvas.get_cells().collect();
295 println!("char cells: {:#?}", char_cells);
296 println!("dump: \n{}", canvas.dump());
297 assert_eq!(
298 "╭─────╮\n\
299 │ │\n\
300 ╰─────╯",
301 canvas.dump()
302 );
303 }
304
305 #[test]
306 fn crossing() {
307 let mut canvas = Canvas::new();
308
309 canvas.draw_rect((0, 0), (8, 4), Border::rounded());
310 canvas.draw_horizontal_line((0, 2), (8, 2), true);
311 canvas.draw_vertical_line((4, 0), (4, 4), false);
312 let mut cells = canvas
313 .cells
314 .iter()
315 .map(|((x, y), frag)| (x, y, frag))
316 .collect::<Vec<_>>();
317 cells.sort_by(|a, b| a.1.cmp(b.1).then(a.0.cmp(b.0)));
318 println!("dump: \n{}", canvas.dump());
319 assert_eq!(
320 "╭───┬───╮\n\
321 │ │ │\n\
322 ┝━━━┿━━━┥\n\
323 │ │ │\n\
324 ╰───┴───╯",
325 canvas.dump()
326 );
327 }
328
329 #[test]
330 fn test_horizontal_line() {
331 let mut canvas = Canvas::new();
332 canvas.draw_horizontal_line((0, 0), (2, 0), false);
333 let mut cells = canvas
334 .cells
335 .iter()
336 .map(|((x, y), frag)| (x, y, frag))
337 .collect::<Vec<_>>();
338 cells.sort_by(|a, b| a.1.cmp(b.1).then(a.0.cmp(b.0)));
339 println!("cells: {:#?}", cells);
340 assert_eq!(cells.len(), 3);
341 }
342
343 #[test]
344 fn test_vertical_line() {
345 let mut canvas = Canvas::new();
346 canvas.draw_vertical_line((0, 0), (0, 2), false);
347 let mut cells = canvas
348 .cells
349 .iter()
350 .map(|((x, y), frag)| (x, y, frag))
351 .collect::<Vec<_>>();
352 cells.sort_by(|a, b| a.1.cmp(b.1).then(a.0.cmp(b.0)));
353 println!("cells: {:#?}", cells);
354 assert_eq!(cells.len(), 3);
355 }
356}