aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshay <[email protected]>2021-03-28 08:28:58 +0100
committerAkshay <[email protected]>2021-03-28 08:28:58 +0100
commite18777e32448f465748535690666055c179d5d5f (patch)
tree4211900a8cc2f5642dac459a92fca7eac8bf36e6
parent3e249a2086e7763a366d4cad70bd3c8e7dc9181f (diff)
add better brush drawing feedback
-rw-r--r--src/app.rs107
-rw-r--r--src/bitmap.rs28
-rw-r--r--src/brush.rs36
-rw-r--r--src/lisp/eval.rs2
-rw-r--r--src/lisp/expr.rs2
-rw-r--r--src/lisp/prelude.rs13
-rw-r--r--src/main.rs2
-rw-r--r--src/utils.rs2
8 files changed, 131 insertions, 61 deletions
diff --git a/src/app.rs b/src/app.rs
index 7bca6ba..42e63de 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -1,5 +1,5 @@
1use crate::{ 1use crate::{
2 bitmap::{MapPoint, Pixmap}, 2 bitmap::{positive_angle_with_x, MapPoint, Pixmap},
3 brush::Brush, 3 brush::Brush,
4 command::CommandBox, 4 command::CommandBox,
5 consts::{colors::*, FONT_PATH}, 5 consts::{colors::*, FONT_PATH},
@@ -35,7 +35,6 @@ pub enum Mode {
35 35
36pub struct AppState<'ctx, 'file> { 36pub struct AppState<'ctx, 'file> {
37 pub active_color: bool, 37 pub active_color: bool,
38 pub brush_size: u8,
39 pub brush: Brush, 38 pub brush: Brush,
40 pub canvas: Canvas<Window>, 39 pub canvas: Canvas<Window>,
41 pub command_box: CommandBox, 40 pub command_box: CommandBox,
@@ -146,8 +145,9 @@ impl<'ctx, 'file> AppState<'ctx, 'file> {
146 &mut self, 145 &mut self,
147 center: P, 146 center: P,
148 val: bool, 147 val: bool,
148 brush_size: u8,
149 ) -> Result<Vec<ModifyRecord>, ()> { 149 ) -> Result<Vec<ModifyRecord>, ()> {
150 let radius = self.brush_size as u32; 150 let radius = brush_size as u32;
151 let center = self.idx_at_coord(center).ok_or(())?; 151 let center = self.idx_at_coord(center).ok_or(())?;
152 let dither_level = self.dither_level; 152 let dither_level = self.dither_level;
153 153
@@ -172,6 +172,7 @@ impl<'ctx, 'file> AppState<'ctx, 'file> {
172 start: MapPoint, 172 start: MapPoint,
173 end: P, 173 end: P,
174 val: bool, 174 val: bool,
175 brush_size: u8,
175 ) -> Result<Vec<ModifyRecord>, ()> { 176 ) -> Result<Vec<ModifyRecord>, ()> {
176 let MapPoint { x, y } = start; 177 let MapPoint { x, y } = start;
177 let end = self.idx_at_coord(end).ok_or(())?; 178 let end = self.idx_at_coord(end).ok_or(())?;
@@ -182,7 +183,7 @@ impl<'ctx, 'file> AppState<'ctx, 'file> {
182 183
183 let mut line_modify_record = vec![]; 184 let mut line_modify_record = vec![];
184 for point in line.into_iter().chain(sym_line) { 185 for point in line.into_iter().chain(sym_line) {
185 let circle_around_point = self.pixmap.get_circle(point, self.brush_size as u32, true); 186 let circle_around_point = self.pixmap.get_circle(point, brush_size as u32, true);
186 for c in circle_around_point 187 for c in circle_around_point
187 .into_iter() 188 .into_iter()
188 .filter(|&pt| dither::bayer(dither_level, pt)) 189 .filter(|&pt| dither::bayer(dither_level, pt))
@@ -257,15 +258,15 @@ impl<'ctx, 'file> AppState<'ctx, 'file> {
257 ); 258 );
258 } 259 }
259 260
260 pub fn increase_brush_size(&mut self) { 261 // pub fn increase_brush_size(&mut self) {
261 self.brush_size += 1; 262 // self.brush_size += 1;
262 } 263 // }
263 264
264 pub fn decrease_brush_size(&mut self) { 265 // pub fn decrease_brush_size(&mut self) {
265 if self.brush_size > 0 { 266 // if self.brush_size > 0 {
266 self.brush_size -= 1; 267 // self.brush_size -= 1;
267 } 268 // }
268 } 269 // }
269 270
270 pub fn reduce_intensity(&mut self) { 271 pub fn reduce_intensity(&mut self) {
271 if self.dither_level > 0 { 272 if self.dither_level > 0 {
@@ -343,9 +344,8 @@ impl<'ctx, 'file> AppState<'ctx, 'file> {
343 format!("---, ---") 344 format!("---, ---")
344 }; 345 };
345 let status_text = format!( 346 let status_text = format!(
346 "[DITHER {}][BRUSH {}][SYM {}][PT {}][ACTIVE {}][KIND {}]", 347 "[DITHER {}][SYM {}][PT {}][ACTIVE {}][KIND {}]",
347 self.dither_level, 348 self.dither_level,
348 self.brush_size + 1,
349 self.symmetry, 349 self.symmetry,
350 mouse_coords, 350 mouse_coords,
351 if self.active_color { "WHT" } else { "BLK" }, 351 if self.active_color { "WHT" } else { "BLK" },
@@ -405,22 +405,51 @@ impl<'ctx, 'file> AppState<'ctx, 'file> {
405 } 405 }
406 406
407 fn draw_mouse(&mut self) { 407 fn draw_mouse(&mut self) {
408 let brush_size = self.brush_size;
409 let cs = self.zoom as u32; 408 let cs = self.zoom as u32;
410 let pt = self.idx_at_coord(self.mouse); 409 let pt = self.idx_at_coord(self.mouse);
411 if let Some(center) = pt { 410 if matches!(self.brush, Brush::Circle { .. } | Brush::Line { .. }) {
412 let circle = self.pixmap.get_circle(center, brush_size as u32, false); 411 let size = self.brush.size().unwrap();
413 for MapPoint { x, y } in circle.into_iter() { 412 if let Some(center) = pt {
414 self.canvas.set_draw_color(PINK); 413 let circle = self.pixmap.get_circle(center, size as u32, false);
415 self.canvas 414 for MapPoint { x, y } in circle.into_iter() {
416 .fill_rect(Rect::new( 415 self.canvas.set_draw_color(PINK);
417 x as i32 * cs as i32 + self.start.x(), 416 self.canvas
418 y as i32 * cs as i32 + self.start.y(), 417 .fill_rect(Rect::new(
419 cs, 418 x as i32 * cs as i32 + self.start.x(),
420 cs, 419 y as i32 * cs as i32 + self.start.y(),
421 )) 420 cs,
422 .unwrap(); 421 cs,
422 ))
423 .unwrap();
424 }
425 }
426 }
427 match self.brush {
428 Brush::Line { start, size, .. } => {
429 let size = self.zoom as u32 * size as u32;
430 if let (Some(from), Some(to)) = (start, pt) {
431 let line = self.pixmap.get_line(from, to.into());
432 draw_text(
433 &mut self.canvas,
434 self.ttf_context,
435 format!("{}°", positive_angle_with_x(from, to.into())),
436 PINK,
437 (self.mouse.0 as u32 + size, self.mouse.1 as u32 + size),
438 );
439 for MapPoint { x, y } in line.into_iter() {
440 self.canvas.set_draw_color(PINK);
441 self.canvas
442 .fill_rect(Rect::new(
443 x as i32 * cs as i32 + self.start.x(),
444 y as i32 * cs as i32 + self.start.y(),
445 cs,
446 cs,
447 ))
448 .unwrap();
449 }
450 }
423 } 451 }
452 _ => {}
424 } 453 }
425 } 454 }
426 455
@@ -485,9 +514,7 @@ impl<'ctx, 'file> AppState<'ctx, 'file> {
485 ev.push_event(Event::Quit { timestamp: 0u32 }) 514 ev.push_event(Event::Quit { timestamp: 0u32 })
486 .expect("ohno unable to quit"); 515 .expect("ohno unable to quit");
487 } 516 }
488}
489 517
490impl<'ctx, 'file> AppState<'ctx, 'file> {
491 pub fn init( 518 pub fn init(
492 width: u32, 519 width: u32,
493 height: u32, 520 height: u32,
@@ -499,7 +526,7 @@ impl<'ctx, 'file> AppState<'ctx, 'file> {
499 let video_subsystem = context.video().unwrap(); 526 let video_subsystem = context.video().unwrap();
500 527
501 let window = video_subsystem 528 let window = video_subsystem
502 .window("Pixel editor", 200, 200) 529 .window("Pixel editor", 500, 500)
503 .position_centered() 530 .position_centered()
504 .resizable() 531 .resizable()
505 .opengl() 532 .opengl()
@@ -517,8 +544,7 @@ impl<'ctx, 'file> AppState<'ctx, 'file> {
517 let pixmap = Pixmap::new_with(width, height, data); 544 let pixmap = Pixmap::new_with(width, height, data);
518 Self { 545 Self {
519 active_color: true, 546 active_color: true,
520 brush_size: 0, 547 brush: Brush::new(0),
521 brush: Brush::new(),
522 canvas, 548 canvas,
523 command_box: CommandBox::new(), 549 command_box: CommandBox::new(),
524 context, 550 context,
@@ -594,8 +620,8 @@ impl<'ctx, 'file> AppState<'ctx, 'file> {
594 self.zoom_out(cursor); 620 self.zoom_out(cursor);
595 } 621 }
596 // brush ops 622 // brush ops
597 Keycode::Q => self.decrease_brush_size(), 623 Keycode::Q => self.brush.shrink(),
598 Keycode::E => self.increase_brush_size(), 624 Keycode::E => self.brush.grow(),
599 Keycode::Num1 => self.reduce_intensity(), 625 Keycode::Num1 => self.reduce_intensity(),
600 Keycode::Num3 => self.increase_intensity(), 626 Keycode::Num3 => self.increase_intensity(),
601 // flip color 627 // flip color
@@ -650,7 +676,7 @@ impl<'ctx, 'file> AppState<'ctx, 'file> {
650 }; 676 };
651 match self.brush { 677 match self.brush {
652 Brush::Circle { size } => { 678 Brush::Circle { size } => {
653 if let Ok(o) = self.paint_point(pt, val) { 679 if let Ok(o) = self.paint_point(pt, val, size) {
654 self.current_operation.extend(o); 680 self.current_operation.extend(o);
655 } 681 }
656 } 682 }
@@ -666,7 +692,7 @@ impl<'ctx, 'file> AppState<'ctx, 'file> {
666 extend, 692 extend,
667 }; 693 };
668 } else if let Ok(o) = 694 } else if let Ok(o) =
669 self.paint_line(start.unwrap(), pt, val) 695 self.paint_line(start.unwrap(), pt, val, size)
670 { 696 {
671 self.current_operation.extend(o); 697 self.current_operation.extend(o);
672 self.brush = Brush::Line { 698 self.brush = Brush::Line {
@@ -705,16 +731,21 @@ impl<'ctx, 'file> AppState<'ctx, 'file> {
705 Event::MouseMotion { 731 Event::MouseMotion {
706 x, y, mousestate, .. 732 x, y, mousestate, ..
707 } => { 733 } => {
734 let size = match self.brush {
735 Brush::Circle { size } => size,
736 Brush::Line { size, .. } => size,
737 _ => continue,
738 };
708 if mousestate.is_mouse_button_pressed(MouseButton::Left) { 739 if mousestate.is_mouse_button_pressed(MouseButton::Left) {
709 let pt = (x, y); 740 let pt = (x, y);
710 let val = self.active_color; 741 let val = self.active_color;
711 if let Ok(o) = self.paint_point(pt, val) { 742 if let Ok(o) = self.paint_point(pt, val, size) {
712 self.current_operation.extend(o); 743 self.current_operation.extend(o);
713 } 744 }
714 } else if mousestate.is_mouse_button_pressed(MouseButton::Right) { 745 } else if mousestate.is_mouse_button_pressed(MouseButton::Right) {
715 let pt = (x, y); 746 let pt = (x, y);
716 let val = !self.active_color; 747 let val = !self.active_color;
717 if let Ok(o) = self.paint_point(pt, val) { 748 if let Ok(o) = self.paint_point(pt, val, size) {
718 self.current_operation.extend(o); 749 self.current_operation.extend(o);
719 } 750 }
720 } 751 }
diff --git a/src/bitmap.rs b/src/bitmap.rs
index 41af745..dc66d73 100644
--- a/src/bitmap.rs
+++ b/src/bitmap.rs
@@ -76,6 +76,14 @@ pub enum Axis {
76 Y, 76 Y,
77} 77}
78 78
79#[derive(Debug, Copy, Clone)]
80pub enum Quadrant {
81 I,
82 II,
83 III,
84 IV,
85}
86
79impl MapPoint { 87impl MapPoint {
80 #[inline] 88 #[inline]
81 pub fn scale(self, c: u32) -> MapPoint { 89 pub fn scale(self, c: u32) -> MapPoint {
@@ -98,6 +106,17 @@ impl MapPoint {
98 pub fn reflect(self, around: MapPoint) -> MapPoint { 106 pub fn reflect(self, around: MapPoint) -> MapPoint {
99 around.scale(2) - self 107 around.scale(2) - self
100 } 108 }
109
110 #[inline]
111 pub fn quadrant(self, origin: MapPoint) -> Quadrant {
112 match (self, origin) {
113 _ if self.x >= origin.x && self.y >= origin.y => Quadrant::I,
114 _ if self.x < origin.x && self.y >= origin.y => Quadrant::II,
115 _ if self.x < origin.x && self.y < origin.y => Quadrant::III,
116 _ if self.x >= origin.x && self.y < origin.y => Quadrant::IV,
117 _ => unreachable!("unexpected quadrant!"),
118 }
119 }
101} 120}
102 121
103impl<T> Pixmap<T> 122impl<T> Pixmap<T>
@@ -278,3 +297,12 @@ pub fn mirror_figure(figure: &[MapPoint], line: u32, axis: Axis) -> Vec<MapPoint
278pub fn reflect_figure(figure: &[MapPoint], around: MapPoint) -> Vec<MapPoint> { 297pub fn reflect_figure(figure: &[MapPoint], around: MapPoint) -> Vec<MapPoint> {
279 figure.iter().map(|pt| pt.reflect(around)).collect() 298 figure.iter().map(|pt| pt.reflect(around)).collect()
280} 299}
300
301pub fn positive_angle_with_x(start: MapPoint, end: MapPoint) -> u32 {
302 if end.x == start.x {
303 return 90;
304 }
305 let numer = (end.y as f64 - start.y as f64).abs();
306 let denum = (end.x as f64 - start.x as f64).abs();
307 (numer / denum).atan().to_degrees() as u32
308}
diff --git a/src/brush.rs b/src/brush.rs
index 98c2b23..1a365b1 100644
--- a/src/brush.rs
+++ b/src/brush.rs
@@ -23,27 +23,28 @@ pub enum Brush {
23} 23}
24 24
25impl Brush { 25impl Brush {
26 pub fn grow(&mut self) -> Result<(), BrushError> { 26 pub fn grow(&mut self) {
27 match self { 27 match self {
28 Brush::Line { ref mut size, .. } => *size += 1, 28 Brush::Line { ref mut size, .. } => *size += 1,
29 Brush::Circle { ref mut size, .. } => *size += 1, 29 Brush::Circle { ref mut size, .. } => *size += 1,
30 Brush::Custom { ref mut size, .. } => *size += 1, 30 Brush::Custom { ref mut size, .. } => *size += 1,
31 _ => return Err(BrushError::CannotIncreaseSize), 31 _ => (),
32 } 32 }
33 Ok(())
34 } 33 }
35 pub fn shrink(&mut self) -> Result<(), BrushError> { 34
35 pub fn shrink(&mut self) {
36 match self { 36 match self {
37 Brush::Line { ref mut size, .. } => *size += size.saturating_sub(1), 37 Brush::Line { ref mut size, .. } => *size = size.saturating_sub(1),
38 Brush::Circle { ref mut size, .. } => *size += size.saturating_sub(1), 38 Brush::Circle { ref mut size, .. } => *size = size.saturating_sub(1),
39 Brush::Custom { ref mut size, .. } => *size += size.saturating_sub(1), 39 Brush::Custom { ref mut size, .. } => *size = size.saturating_sub(1),
40 _ => return Err(BrushError::CannotIncreaseSize), 40 _ => (),
41 } 41 }
42 Ok(())
43 } 42 }
44 pub fn new() -> Self { 43
45 Brush::Circle { size: 0 } 44 pub fn new(size: u8) -> Self {
45 Brush::Circle { size }
46 } 46 }
47
47 pub fn line(size: u8, extend: bool) -> Self { 48 pub fn line(size: u8, extend: bool) -> Self {
48 Brush::Line { 49 Brush::Line {
49 size, 50 size,
@@ -51,9 +52,18 @@ impl Brush {
51 extend, 52 extend,
52 } 53 }
53 } 54 }
55
54 pub fn is_line(&self) -> bool { 56 pub fn is_line(&self) -> bool {
55 matches!(self, Self::Line { .. }) 57 matches!(self, Self::Line { .. })
56 } 58 }
59
60 pub fn size(&self) -> Option<u8> {
61 match self {
62 Brush::Line { size, .. } => Some(size.clone()),
63 Brush::Circle { size } => Some(size.clone()),
64 _ => None,
65 }
66 }
57} 67}
58 68
59impl fmt::Display for Brush { 69impl fmt::Display for Brush {
@@ -67,7 +77,3 @@ impl fmt::Display for Brush {
67 } 77 }
68 } 78 }
69} 79}
70
71pub enum BrushError {
72 CannotIncreaseSize,
73}
diff --git a/src/lisp/eval.rs b/src/lisp/eval.rs
index 6ffff23..f7f918a 100644
--- a/src/lisp/eval.rs
+++ b/src/lisp/eval.rs
@@ -4,7 +4,7 @@ use crate::{
4 error::{EvalError, LispError}, 4 error::{EvalError, LispError},
5 expr::{Arity, Ident, LispExpr, LispFunction}, 5 expr::{Arity, Ident, LispExpr, LispFunction},
6 number::LispNumber, 6 number::LispNumber,
7 EnvList, Environment, 7 Environment,
8 }, 8 },
9}; 9};
10 10
diff --git a/src/lisp/expr.rs b/src/lisp/expr.rs
index 9ef9f65..8fd794e 100644
--- a/src/lisp/expr.rs
+++ b/src/lisp/expr.rs
@@ -5,7 +5,7 @@ use crate::lisp::{
5 error::{EvalError, LispError}, 5 error::{EvalError, LispError},
6 eval::lookup_extended, 6 eval::lookup_extended,
7 number::LispNumber, 7 number::LispNumber,
8 EnvList, Environment, 8 EnvList,
9}; 9};
10 10
11#[derive(Debug, Copy, PartialEq, Clone)] 11#[derive(Debug, Copy, PartialEq, Clone)]
diff --git a/src/lisp/prelude.rs b/src/lisp/prelude.rs
index d2a6ae6..bdaf639 100644
--- a/src/lisp/prelude.rs
+++ b/src/lisp/prelude.rs
@@ -9,7 +9,7 @@ use crate::{
9 primitive, 9 primitive,
10}; 10};
11 11
12use std::{convert::TryInto, fs::File, io::Write, path::PathBuf}; 12use std::{convert::TryInto, fs::File, io::Write};
13 13
14use log::info; 14use log::info;
15 15
@@ -196,13 +196,18 @@ pub fn new_env() -> Environment {
196 196
197 primitive!(env, Arity::Atmost(1), "brush", |args, app| { 197 primitive!(env, Arity::Atmost(1), "brush", |args, app| {
198 info!("brush {}", &args[0]); 198 info!("brush {}", &args[0]);
199 let old_size = if matches!(app.brush, Brush::Line { .. } | Brush::Circle { .. }) {
200 app.brush.size().unwrap()
201 } else {
202 0
203 };
199 if let [LispExpr::Quote(kind, _)] = args { 204 if let [LispExpr::Quote(kind, _)] = args {
200 if is_ident(kind) { 205 if is_ident(kind) {
201 match (&**kind).as_ref() { 206 match (&**kind).as_ref() {
202 "fill" => app.brush = Brush::Fill, 207 "fill" => app.brush = Brush::Fill,
203 "circle" => app.brush = Brush::new(), 208 "circle" => app.brush = Brush::new(old_size),
204 "line" => app.brush = Brush::line(0, false), 209 "line" => app.brush = Brush::line(old_size, false),
205 "line-extend" => app.brush = Brush::line(0, true), 210 "line-extend" => app.brush = Brush::line(old_size, true),
206 _ => return Err(EvalError::CustomInternal("unknown brush type").into()), 211 _ => return Err(EvalError::CustomInternal("unknown brush type").into()),
207 } 212 }
208 } 213 }
diff --git a/src/main.rs b/src/main.rs
index 34f087a..31f64f1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -16,7 +16,7 @@ use std::{
16 env, 16 env,
17 fs::OpenOptions, 17 fs::OpenOptions,
18 io::{Cursor, Read}, 18 io::{Cursor, Read},
19 path::{Path, PathBuf}, 19 path::PathBuf,
20}; 20};
21 21
22use log::{error, info}; 22use log::{error, info};
diff --git a/src/utils.rs b/src/utils.rs
index 71a9eea..902f939 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -30,7 +30,7 @@ pub fn draw_text<S: AsRef<str>>(
30 let texture = texture_creator 30 let texture = texture_creator
31 .create_texture_from_surface(&surface) 31 .create_texture_from_surface(&surface)
32 .unwrap(); 32 .unwrap();
33 let (width, height) = font.size_of_latin1(text.as_bytes()).unwrap(); 33 let (width, height) = font.size_of(&text).unwrap();
34 let area = rect!(x, y, width, height); 34 let area = rect!(x, y, width, height);
35 canvas.copy(&texture, None, area).unwrap(); 35 canvas.copy(&texture, None, area).unwrap();
36 width 36 width