1mod line;
2mod map;
3mod points;
4mod rectangle;
5mod world;
6
7pub use self::line::Line;
8pub use self::map::{Map, MapResolution};
9pub use self::points::Points;
10pub use self::rectangle::Rectangle;
11
12use crate::{
13 buffer::Buffer,
14 layout::Rect,
15 style::{Color, Style},
16 symbols,
17 text::Spans,
18 widgets::{Block, Widget},
19};
20use std::fmt::Debug;
21
22pub trait Shape {
24 fn draw(&self, painter: &mut Painter);
25}
26
27#[derive(Debug, Clone)]
29pub struct Label<'a> {
30 x: f64,
31 y: f64,
32 spans: Spans<'a>,
33}
34
35#[derive(Debug, Clone)]
36struct Layer {
37 string: String,
38 colors: Vec<Color>,
39}
40
41trait Grid: Debug {
42 fn resolution(&self) -> (f64, f64);
45 fn paint(&mut self, x: usize, y: usize, color: Color);
46 fn save(&self) -> Layer;
47 fn reset(&mut self);
48}
49
50#[derive(Debug, Clone)]
51struct BrailleGrid {
52 width: u16,
53 height: u16,
54 cells: Vec<u16>,
55 colors: Vec<Color>,
56}
57
58impl BrailleGrid {
59 fn new(width: u16, height: u16) -> BrailleGrid {
60 let length = usize::from(width * height);
61 BrailleGrid {
62 width,
63 height,
64 cells: vec![symbols::braille::BLANK; length],
65 colors: vec![Color::Reset; length],
66 }
67 }
68}
69
70impl Grid for BrailleGrid {
71 fn resolution(&self) -> (f64, f64) {
80 (
81 f64::from(self.width) * 2.0 - 1.0,
82 f64::from(self.height) * 4.0 - 1.0,
83 )
84 }
85
86 fn save(&self) -> Layer {
87 Layer {
88 string: String::from_utf16(&self.cells).unwrap(),
89 colors: self.colors.clone(),
90 }
91 }
92
93 fn reset(&mut self) {
94 for c in &mut self.cells {
95 *c = symbols::braille::BLANK;
96 }
97 for c in &mut self.colors {
98 *c = Color::Reset;
99 }
100 }
101
102 fn paint(&mut self, x: usize, y: usize, color: Color) {
103 let index = y / 4 * self.width as usize + x / 2;
104 if let Some(c) = self.cells.get_mut(index) {
105 *c |= symbols::braille::DOTS[y % 4][x % 2];
106 }
107 if let Some(c) = self.colors.get_mut(index) {
108 *c = color;
109 }
110 }
111}
112
113#[derive(Debug, Clone)]
114struct CharGrid {
115 width: u16,
116 height: u16,
117 cells: Vec<char>,
118 colors: Vec<Color>,
119 cell_char: char,
120}
121
122impl CharGrid {
123 fn new(width: u16, height: u16, cell_char: char) -> CharGrid {
124 let length = usize::from(width * height);
125 CharGrid {
126 width,
127 height,
128 cells: vec![' '; length],
129 colors: vec![Color::Reset; length],
130 cell_char,
131 }
132 }
133}
134
135impl Grid for CharGrid {
136 fn resolution(&self) -> (f64, f64) {
145 (f64::from(self.width) - 1.0, f64::from(self.height) - 1.0)
146 }
147
148 fn save(&self) -> Layer {
149 Layer {
150 string: self.cells.iter().collect(),
151 colors: self.colors.clone(),
152 }
153 }
154
155 fn reset(&mut self) {
156 for c in &mut self.cells {
157 *c = ' ';
158 }
159 for c in &mut self.colors {
160 *c = Color::Reset;
161 }
162 }
163
164 fn paint(&mut self, x: usize, y: usize, color: Color) {
165 let index = y * self.width as usize + x;
166 if let Some(c) = self.cells.get_mut(index) {
167 *c = self.cell_char;
168 }
169 if let Some(c) = self.colors.get_mut(index) {
170 *c = color;
171 }
172 }
173}
174
175#[derive(Debug)]
176pub struct Painter<'a, 'b> {
177 context: &'a mut Context<'b>,
178 resolution: (f64, f64),
179}
180
181impl<'a, 'b> Painter<'a, 'b> {
182 pub fn get_point(&self, x: f64, y: f64) -> Option<(usize, usize)> {
202 let left = self.context.x_bounds[0];
203 let right = self.context.x_bounds[1];
204 let top = self.context.y_bounds[1];
205 let bottom = self.context.y_bounds[0];
206 if x < left || x > right || y < bottom || y > top {
207 return None;
208 }
209 let width = (self.context.x_bounds[1] - self.context.x_bounds[0]).abs();
210 let height = (self.context.y_bounds[1] - self.context.y_bounds[0]).abs();
211 if width == 0.0 || height == 0.0 {
212 return None;
213 }
214 let x = ((x - left) * self.resolution.0 / width) as usize;
215 let y = ((top - y) * self.resolution.1 / height) as usize;
216 Some((x, y))
217 }
218
219 pub fn paint(&mut self, x: usize, y: usize, color: Color) {
230 self.context.grid.paint(x, y, color);
231 }
232}
233
234impl<'a, 'b> From<&'a mut Context<'b>> for Painter<'a, 'b> {
235 fn from(context: &'a mut Context<'b>) -> Painter<'a, 'b> {
236 let resolution = context.grid.resolution();
237 Painter {
238 context,
239 resolution,
240 }
241 }
242}
243
244#[derive(Debug)]
246pub struct Context<'a> {
247 x_bounds: [f64; 2],
248 y_bounds: [f64; 2],
249 grid: Box<dyn Grid>,
250 dirty: bool,
251 layers: Vec<Layer>,
252 labels: Vec<Label<'a>>,
253}
254
255impl<'a> Context<'a> {
256 pub fn new(
257 width: u16,
258 height: u16,
259 x_bounds: [f64; 2],
260 y_bounds: [f64; 2],
261 marker: symbols::Marker,
262 ) -> Context<'a> {
263 let grid: Box<dyn Grid> = match marker {
264 symbols::Marker::Dot => Box::new(CharGrid::new(width, height, '•')),
265 symbols::Marker::Block => Box::new(CharGrid::new(width, height, '▄')),
266 symbols::Marker::Braille => Box::new(BrailleGrid::new(width, height)),
267 };
268 Context {
269 x_bounds,
270 y_bounds,
271 grid,
272 dirty: false,
273 layers: Vec::new(),
274 labels: Vec::new(),
275 }
276 }
277
278 pub fn draw<S>(&mut self, shape: &S)
280 where
281 S: Shape,
282 {
283 self.dirty = true;
284 let mut painter = Painter::from(self);
285 shape.draw(&mut painter);
286 }
287
288 pub fn layer(&mut self) {
290 self.layers.push(self.grid.save());
291 self.grid.reset();
292 self.dirty = false;
293 }
294
295 pub fn print<T>(&mut self, x: f64, y: f64, spans: T)
297 where
298 T: Into<Spans<'a>>,
299 {
300 self.labels.push(Label {
301 x,
302 y,
303 spans: spans.into(),
304 });
305 }
306
307 fn finish(&mut self) {
309 if self.dirty {
310 self.layer()
311 }
312 }
313}
314
315pub struct Canvas<'a, F>
351where
352 F: Fn(&mut Context),
353{
354 block: Option<Block<'a>>,
355 x_bounds: [f64; 2],
356 y_bounds: [f64; 2],
357 painter: Option<F>,
358 background_color: Color,
359 marker: symbols::Marker,
360}
361
362impl<'a, F> Default for Canvas<'a, F>
363where
364 F: Fn(&mut Context),
365{
366 fn default() -> Canvas<'a, F> {
367 Canvas {
368 block: None,
369 x_bounds: [0.0, 0.0],
370 y_bounds: [0.0, 0.0],
371 painter: None,
372 background_color: Color::Reset,
373 marker: symbols::Marker::Braille,
374 }
375 }
376}
377
378impl<'a, F> Canvas<'a, F>
379where
380 F: Fn(&mut Context),
381{
382 pub fn block(mut self, block: Block<'a>) -> Canvas<'a, F> {
383 self.block = Some(block);
384 self
385 }
386
387 pub fn x_bounds(mut self, bounds: [f64; 2]) -> Canvas<'a, F> {
388 self.x_bounds = bounds;
389 self
390 }
391
392 pub fn y_bounds(mut self, bounds: [f64; 2]) -> Canvas<'a, F> {
393 self.y_bounds = bounds;
394 self
395 }
396
397 pub fn paint(mut self, f: F) -> Canvas<'a, F> {
399 self.painter = Some(f);
400 self
401 }
402
403 pub fn background_color(mut self, color: Color) -> Canvas<'a, F> {
404 self.background_color = color;
405 self
406 }
407
408 pub fn marker(mut self, marker: symbols::Marker) -> Canvas<'a, F> {
424 self.marker = marker;
425 self
426 }
427}
428
429impl<'a, F> Widget for Canvas<'a, F>
430where
431 F: Fn(&mut Context),
432{
433 fn render(&mut self, area: Rect, buf: &mut Buffer) {
434 let canvas_area = match self.block.as_mut() {
435 Some(b) => {
436 let inner_area = b.inner(area);
437 b.render(area, buf);
438 inner_area
439 }
440 None => area,
441 };
442
443 buf.set_style(canvas_area, Style::default().bg(self.background_color));
444
445 let width = canvas_area.width as usize;
446
447 let painter = match self.painter {
448 Some(ref p) => p,
449 None => return,
450 };
451
452 let mut ctx = Context::new(
454 canvas_area.width,
455 canvas_area.height,
456 self.x_bounds,
457 self.y_bounds,
458 self.marker,
459 );
460 painter(&mut ctx);
462 ctx.finish();
463
464 for layer in ctx.layers {
466 for (i, (ch, color)) in layer
467 .string
468 .chars()
469 .zip(layer.colors.into_iter())
470 .enumerate()
471 {
472 if ch != ' ' && ch != '\u{2800}' {
473 let (x, y) = (i % width, i / width);
474 buf.get_mut(x as u16 + canvas_area.left(), y as u16 + canvas_area.top())
475 .set_char(ch)
476 .set_fg(color);
477 }
478 }
479 }
480
481 let left = self.x_bounds[0];
483 let right = self.x_bounds[1];
484 let top = self.y_bounds[1];
485 let bottom = self.y_bounds[0];
486 let width = (self.x_bounds[1] - self.x_bounds[0]).abs();
487 let height = (self.y_bounds[1] - self.y_bounds[0]).abs();
488 let resolution = {
489 let width = f64::from(canvas_area.width - 1);
490 let height = f64::from(canvas_area.height - 1);
491 (width, height)
492 };
493 for label in ctx
494 .labels
495 .iter()
496 .filter(|l| l.x >= left && l.x <= right && l.y <= top && l.y >= bottom)
497 {
498 let x = ((label.x - left) * resolution.0 / width) as u16 + canvas_area.left();
499 let y = ((top - label.y) * resolution.1 / height) as u16 + canvas_area.top();
500 buf.set_spans(x, y, &label.spans, canvas_area.right() - x);
501 }
502 }
503}