tui_temp_fork/widgets/canvas/
mod.rs1mod 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::buffer::Buffer;
13use crate::layout::Rect;
14use crate::style::{Color, Style};
15use crate::widgets::{Block, Widget};
16
17pub const DOTS: [[u16; 2]; 4] = [
18 [0x0001, 0x0008],
19 [0x0002, 0x0010],
20 [0x0004, 0x0020],
21 [0x0040, 0x0080],
22];
23pub const BRAILLE_OFFSET: u16 = 0x2800;
24pub const BRAILLE_BLANK: char = '⠀';
25
26pub trait Shape<'a> {
28 fn color(&self) -> Color;
30 fn points(&'a self) -> Box<dyn Iterator<Item = (f64, f64)> + 'a>;
32}
33
34pub struct Label<'a> {
36 pub x: f64,
37 pub y: f64,
38 pub text: &'a str,
39 pub color: Color,
40}
41
42struct Layer {
43 string: String,
44 colors: Vec<Color>,
45}
46
47struct Grid {
48 cells: Vec<u16>,
49 colors: Vec<Color>,
50}
51
52impl Grid {
53 fn new(width: usize, height: usize) -> Grid {
54 Grid {
55 cells: vec![BRAILLE_OFFSET; width * height],
56 colors: vec![Color::Reset; width * height],
57 }
58 }
59
60 fn save(&self) -> Layer {
61 Layer {
62 string: String::from_utf16(&self.cells).unwrap(),
63 colors: self.colors.clone(),
64 }
65 }
66
67 fn reset(&mut self) {
68 for c in &mut self.cells {
69 *c = BRAILLE_OFFSET;
70 }
71 for c in &mut self.colors {
72 *c = Color::Reset;
73 }
74 }
75}
76
77pub struct Context<'a> {
79 width: u16,
80 height: u16,
81 x_bounds: [f64; 2],
82 y_bounds: [f64; 2],
83 grid: Grid,
84 dirty: bool,
85 layers: Vec<Layer>,
86 labels: Vec<Label<'a>>,
87}
88
89impl<'a> Context<'a> {
90 pub fn draw<'b, S>(&mut self, shape: &'b S)
92 where
93 S: Shape<'b>,
94 {
95 self.dirty = true;
96 let left = self.x_bounds[0];
97 let right = self.x_bounds[1];
98 let bottom = self.y_bounds[0];
99 let top = self.y_bounds[1];
100 for (x, y) in shape
101 .points()
102 .filter(|&(x, y)| !(x <= left || x >= right || y <= bottom || y >= top))
103 {
104 let dy = ((top - y) * f64::from(self.height - 1) * 4.0 / (top - bottom)) as usize;
105 let dx = ((x - left) * f64::from(self.width - 1) * 2.0 / (right - left)) as usize;
106 let index = dy / 4 * self.width as usize + dx / 2;
107 self.grid.cells[index] |= DOTS[dy % 4][dx % 2];
108 self.grid.colors[index] = shape.color();
109 }
110 }
111
112 pub fn layer(&mut self) {
114 self.layers.push(self.grid.save());
115 self.grid.reset();
116 self.dirty = false;
117 }
118
119 pub fn print(&mut self, x: f64, y: f64, text: &'a str, color: Color) {
121 self.labels.push(Label { x, y, text, color });
122 }
123
124 fn finish(&mut self) {
126 if self.dirty {
127 self.layer()
128 }
129 }
130}
131
132pub struct Canvas<'a, F>
172where
173 F: Fn(&mut Context),
174{
175 block: Option<Block<'a>>,
176 x_bounds: [f64; 2],
177 y_bounds: [f64; 2],
178 painter: Option<F>,
179 background_color: Color,
180}
181
182impl<'a, F> Default for Canvas<'a, F>
183where
184 F: Fn(&mut Context),
185{
186 fn default() -> Canvas<'a, F> {
187 Canvas {
188 block: None,
189 x_bounds: [0.0, 0.0],
190 y_bounds: [0.0, 0.0],
191 painter: None,
192 background_color: Color::Reset,
193 }
194 }
195}
196
197impl<'a, F> Canvas<'a, F>
198where
199 F: Fn(&mut Context),
200{
201 pub fn block(mut self, block: Block<'a>) -> Canvas<'a, F> {
202 self.block = Some(block);
203 self
204 }
205 pub fn x_bounds(mut self, bounds: [f64; 2]) -> Canvas<'a, F> {
206 self.x_bounds = bounds;
207 self
208 }
209 pub fn y_bounds(mut self, bounds: [f64; 2]) -> Canvas<'a, F> {
210 self.y_bounds = bounds;
211 self
212 }
213
214 pub fn paint(mut self, f: F) -> Canvas<'a, F> {
216 self.painter = Some(f);
217 self
218 }
219
220 pub fn background_color(mut self, color: Color) -> Canvas<'a, F> {
221 self.background_color = color;
222 self
223 }
224}
225
226impl<'a, F> Widget for Canvas<'a, F>
227where
228 F: Fn(&mut Context),
229{
230 fn draw(&mut self, area: Rect, buf: &mut Buffer) {
231 let canvas_area = match self.block {
232 Some(ref mut b) => {
233 b.draw(area, buf);
234 b.inner(area)
235 }
236 None => area,
237 };
238
239 let width = canvas_area.width as usize;
240 let height = canvas_area.height as usize;
241
242 if let Some(ref painter) = self.painter {
243 let mut ctx = Context {
245 x_bounds: self.x_bounds,
246 y_bounds: self.y_bounds,
247 width: canvas_area.width,
248 height: canvas_area.height,
249 grid: Grid::new(width, height),
250 dirty: false,
251 layers: Vec::new(),
252 labels: Vec::new(),
253 };
254 painter(&mut ctx);
256 ctx.finish();
257
258 for layer in ctx.layers {
260 for (i, (ch, color)) in layer
261 .string
262 .chars()
263 .zip(layer.colors.into_iter())
264 .enumerate()
265 {
266 if ch != BRAILLE_BLANK {
267 let (x, y) = (i % width, i / width);
268 buf.get_mut(x as u16 + canvas_area.left(), y as u16 + canvas_area.top())
269 .set_char(ch)
270 .set_fg(color)
271 .set_bg(self.background_color);
272 }
273 }
274 }
275
276 let style = Style::default().bg(self.background_color);
278 for label in ctx.labels.iter().filter(|l| {
279 !(l.x < self.x_bounds[0]
280 || l.x > self.x_bounds[1]
281 || l.y < self.y_bounds[0]
282 || l.y > self.y_bounds[1])
283 }) {
284 let dy = ((self.y_bounds[1] - label.y) * f64::from(canvas_area.height - 1)
285 / (self.y_bounds[1] - self.y_bounds[0])) as u16;
286 let dx = ((label.x - self.x_bounds[0]) * f64::from(canvas_area.width - 1)
287 / (self.x_bounds[1] - self.x_bounds[0])) as u16;
288 buf.set_string(
289 dx + canvas_area.left(),
290 dy + canvas_area.top(),
291 label.text,
292 style.fg(label.color),
293 );
294 }
295 }
296 }
297}