1use std::char;
25use std::cmp;
26use std::f32;
27
28extern crate fnv;
29use fnv::FnvHashMap;
30
31extern crate colored;
32pub use colored::Color as PixelColor;
33use colored::Colorize;
34
35static PIXEL_MAP: [[u8; 2]; 4] = [[0x01, 0x08], [0x02, 0x10], [0x04, 0x20], [0x40, 0x80]];
36
37#[derive(Clone, Debug, PartialEq, Eq)]
39pub struct Canvas {
40 chars: FnvHashMap<(u16, u16), (u8, char, bool, PixelColor)>,
41 width: u16,
42 height: u16,
43}
44
45impl Canvas {
46 pub fn new(width: u32, height: u32) -> Canvas {
51 Canvas {
52 chars: FnvHashMap::default(),
53 width: (width / 2) as u16,
54 height: (height / 4) as u16,
55 }
56 }
57
58 pub fn clear(&mut self) {
60 self.chars.clear();
61 }
62
63 pub fn set(&mut self, x: u32, y: u32) {
65 let (row, col) = ((x / 2) as u16, (y / 4) as u16);
66 let a = self
67 .chars
68 .entry((row, col))
69 .or_insert((0, ' ', false, PixelColor::White));
70 a.0 |= PIXEL_MAP[y as usize % 4][x as usize % 2];
71 a.1 = ' ';
72 a.2 = false;
73 a.3 = PixelColor::White;
74 }
75
76 pub fn set_colored(&mut self, x: u32, y: u32, color: PixelColor) {
79 let (row, col) = ((x / 2) as u16, (y / 4) as u16);
80 let a = self
81 .chars
82 .entry((row, col))
83 .or_insert((0, ' ', false, PixelColor::White));
84 a.0 |= PIXEL_MAP[y as usize % 4][x as usize % 2];
85 a.1 = ' ';
86 a.2 = true;
87 a.3 = color;
88 }
89
90 pub fn set_char(&mut self, x: u32, y: u32, c: char) {
92 let (row, col) = ((x / 2) as u16, (y / 4) as u16);
93 let a = self
94 .chars
95 .entry((row, col))
96 .or_insert((0, ' ', false, PixelColor::White));
97 a.0 = 0;
98 a.1 = c;
99 a.2 = false;
100 a.3 = PixelColor::White;
101 }
102
103 pub fn text(&mut self, x: u32, y: u32, max_width: u32, text: &str) {
105 for (i, c) in text.chars().enumerate() {
106 let w = i as u32 * 2;
107 if w > max_width {
108 return;
109 }
110 self.set_char(x + w, y, c);
111 }
112 }
113
114 pub fn unset(&mut self, x: u32, y: u32) {
116 let (row, col) = ((x / 2) as u16, (y / 4) as u16);
117 let a = self
118 .chars
119 .entry((row, col))
120 .or_insert((0, ' ', false, PixelColor::White));
121 a.0 &= !PIXEL_MAP[y as usize % 4][x as usize % 2];
122 }
123
124 pub fn toggle(&mut self, x: u32, y: u32) {
126 let (row, col) = ((x / 2) as u16, (y / 4) as u16);
127 let a = self
128 .chars
129 .entry((row, col))
130 .or_insert((0, ' ', false, PixelColor::White));
131 a.0 ^= PIXEL_MAP[y as usize % 4][x as usize % 2];
132 }
133
134 pub fn get(&self, x: u32, y: u32) -> bool {
136 let (row, col) = ((x / 2) as u16, (y / 4) as u16);
137 self.chars.get(&(row, col)).map_or(false, |a| {
138 let dot_index = PIXEL_MAP[y as usize % 4][x as usize % 2];
139 a.0 & dot_index != 0
140 })
141 }
142
143 pub fn rows(&self) -> Vec<String> {
148 let mut maxrow = self.width;
149 let mut maxcol = self.height;
150 for &(x, y) in self.chars.keys() {
151 if x > maxrow {
152 maxrow = x;
153 }
154 if y > maxcol {
155 maxcol = y;
156 }
157 }
158
159 let mut result = Vec::with_capacity(maxcol as usize + 1);
160 for y in 0..=maxcol {
161 let mut row = String::with_capacity(maxrow as usize + 1);
162 for x in 0..=maxrow {
163 let cell =
164 self.chars
165 .get(&(x, y))
166 .cloned()
167 .unwrap_or((0, ' ', false, PixelColor::White));
168 match cell {
169 (0, _, _, _) => row.push(cell.1),
170 (_, _, false, _) => row.push(char::from_u32(0x2800 + cell.0 as u32).unwrap()),
171 (_, _, true, _) => {
172 row = format!(
173 "{0}{1}",
174 row,
175 String::from(char::from_u32(0x2800 + cell.0 as u32).unwrap())
176 .color(cell.3)
177 )
178 }
179 };
180 }
181 result.push(row);
182 }
183 result
184 }
185
186 pub fn frame(&self) -> String {
188 self.rows().join("\n")
189 }
190
191 pub fn line(&mut self, x1: u32, y1: u32, x2: u32, y2: u32) {
193 let xdiff = cmp::max(x1, x2) - cmp::min(x1, x2);
194 let ydiff = cmp::max(y1, y2) - cmp::min(y1, y2);
195 let xdir = if x1 <= x2 { 1 } else { -1 };
196 let ydir = if y1 <= y2 { 1 } else { -1 };
197
198 let r = cmp::max(xdiff, ydiff);
199
200 for i in 0..=r {
201 let mut x = x1 as i32;
202 let mut y = y1 as i32;
203
204 if ydiff != 0 {
205 y += ((i * ydiff) / r) as i32 * ydir;
206 }
207 if xdiff != 0 {
208 x += ((i * xdiff) / r) as i32 * xdir;
209 }
210
211 self.set(x as u32, y as u32);
212 }
213 }
214
215 pub fn line_colored(&mut self, x1: u32, y1: u32, x2: u32, y2: u32, color: PixelColor) {
218 let xdiff = cmp::max(x1, x2) - cmp::min(x1, x2);
219 let ydiff = cmp::max(y1, y2) - cmp::min(y1, y2);
220 let xdir = if x1 <= x2 { 1 } else { -1 };
221 let ydir = if y1 <= y2 { 1 } else { -1 };
222
223 let r = cmp::max(xdiff, ydiff);
224
225 for i in 0..=r {
226 let mut x = x1 as i32;
227 let mut y = y1 as i32;
228
229 if ydiff != 0 {
230 y += ((i * ydiff) / r) as i32 * ydir;
231 }
232 if xdiff != 0 {
233 x += ((i * xdiff) / r) as i32 * xdir;
234 }
235
236 self.set_colored(x as u32, y as u32, color);
237 }
238 }
239}
240
241pub struct Turtle {
243 pub x: f32,
244 pub y: f32,
245 pub brush: bool,
246 pub use_color: bool,
247 pub brush_color: PixelColor,
248 pub rotation: f32,
249 pub cvs: Canvas,
250}
251
252impl Turtle {
253 pub fn new(x: f32, y: f32) -> Turtle {
257 Turtle {
258 cvs: Canvas::new(0, 0),
259 x: x,
260 y: y,
261 brush: true,
262 use_color: false,
263 brush_color: PixelColor::White,
264 rotation: 0.0,
265 }
266 }
267
268 pub fn from_canvas(x: f32, y: f32, cvs: Canvas) -> Turtle {
272 Turtle {
273 cvs: cvs,
274 x: x,
275 y: y,
276 brush: true,
277 use_color: false,
278 brush_color: PixelColor::White,
279 rotation: 0.0,
280 }
281 }
282
283 pub fn width(mut self, width: u32) -> Turtle {
285 self.cvs.width = width as u16;
286 self
287 }
288
289 pub fn height(mut self, height: u32) -> Turtle {
291 self.cvs.height = height as u16;
292 self
293 }
294
295 pub fn up(&mut self) {
297 self.brush = false;
298 }
299
300 pub fn down(&mut self) {
302 self.brush = true;
303 }
304
305 pub fn toggle(&mut self) {
307 self.brush = !self.brush;
308 }
309
310 pub fn color(&mut self, brush_color: PixelColor) {
312 self.use_color = true;
313 self.brush_color = brush_color;
314 }
315
316 pub fn clean_brush(&mut self) {
318 self.use_color = false;
319 }
320
321 pub fn forward(&mut self, dist: f32) {
323 let x = self.x + degrees_to_radians(self.rotation).cos() * dist;
324 let y = self.y + degrees_to_radians(self.rotation).sin() * dist;
325 self.teleport(x, y);
326 }
327
328 pub fn back(&mut self, dist: f32) {
330 self.forward(-dist);
331 }
332
333 pub fn teleport(&mut self, x: f32, y: f32) {
338 if self.brush {
339 if self.use_color {
340 self.cvs.line_colored(
341 cmp::max(0, self.x.round() as i32) as u32,
342 cmp::max(0, self.y.round() as i32) as u32,
343 cmp::max(0, x.round() as i32) as u32,
344 cmp::max(0, y.round() as i32) as u32,
345 self.brush_color,
346 );
347 } else {
348 self.cvs.line(
349 cmp::max(0, self.x.round() as i32) as u32,
350 cmp::max(0, self.y.round() as i32) as u32,
351 cmp::max(0, x.round() as i32) as u32,
352 cmp::max(0, y.round() as i32) as u32,
353 );
354 }
355 }
356
357 self.x = x;
358 self.y = y;
359 }
360
361 pub fn right(&mut self, angle: f32) {
363 self.rotation += angle;
364 }
365
366 pub fn left(&mut self, angle: f32) {
368 self.rotation -= angle;
369 }
370
371 pub fn frame(&self) -> String {
373 self.cvs.frame()
374 }
375}
376
377fn degrees_to_radians(deg: f32) -> f32 {
378 deg * (f32::consts::PI / 180.0f32)
379}