aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkshay <[email protected]>2021-04-11 11:01:18 +0100
committerAkshay <[email protected]>2021-04-11 11:01:18 +0100
commitb0685c1638044b85dc7e8b07555a7b639b54d69a (patch)
tree3ba4f28a1a8f4c1b409df2d4f5082056ece2f8e6
parent8d5fecc23f4e986c74295c58473c6ea8d840d955 (diff)
add rect select brush and keybinds
-rw-r--r--src/app.rs176
-rw-r--r--src/bitmap.rs2
-rw-r--r--src/brush.rs19
-rw-r--r--src/utils.rs16
4 files changed, 163 insertions, 50 deletions
diff --git a/src/app.rs b/src/app.rs
index e8c5260..83e30e8 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -1,6 +1,6 @@
1use crate::{ 1use crate::{
2 bitmap::{positive_angle_with_x, Axis, MapPoint, Pixmap}, 2 bitmap::{positive_angle_with_x, abs_difference, Axis, MapPoint, Pixmap},
3 brush::{Brush, CircleBrush, LineBrush}, 3 brush::{Brush, CircleBrush, LineBrush, RectSelectBrush},
4 cache::Cache, 4 cache::Cache,
5 command::CommandBox, 5 command::CommandBox,
6 consts::{colors::*, ANGLE, FONT_PATH, RC_PATH, STDLIB_PATH}, 6 consts::{colors::*, ANGLE, FONT_PATH, RC_PATH, STDLIB_PATH},
@@ -152,6 +152,7 @@ impl<'ctx> AppState<'ctx> {
152 Brush::Circle(_) => Brush::line(0, false), 152 Brush::Circle(_) => Brush::line(0, false),
153 Brush::Line(LineBrush { extend: false, .. }) => Brush::line(0, true), 153 Brush::Line(LineBrush { extend: false, .. }) => Brush::line(0, true),
154 Brush::Line(LineBrush { extend: true, .. }) => Brush::Fill, 154 Brush::Line(LineBrush { extend: true, .. }) => Brush::Fill,
155 Brush::Fill => Brush::rect(),
155 _ => Brush::new(0), 156 _ => Brush::new(0),
156 } 157 }
157 } 158 }
@@ -444,34 +445,54 @@ impl<'ctx> AppState<'ctx> {
444 } 445 }
445 } 446 }
446 } 447 }
447 if let Brush::Line(LineBrush { start, size, .. }) = self.brush { 448 match self.brush {
448 let size = self.zoom as u32 * (size as u32 + 5); 449 Brush::Line(LineBrush { start, size, .. }) => {
449 if let (Some(from), Some(to)) = (start, pt) { 450 let size = self.zoom as u32 * (size as u32 + 5);
450 let line = self.pixmap.get_line(from, to.into()); 451 if let (Some(from), Some(to)) = (start, pt) {
451 let angle = positive_angle_with_x(from, to.into()); 452 let line = self.pixmap.get_line(from, to.into());
453 let angle = positive_angle_with_x(from, to.into());
454 draw_text(
455 &mut self.canvas,
456 self.ttf_context,
457 format!(
458 "{:.width$}°",
459 angle,
460 width = if (angle - ANGLE).abs() < 1e-3 { 3 } else { 0 }
461 ),
462 PINK,
463 (self.mouse.0 + size as i32, self.mouse.1 + size as i32),
464 );
465 for MapPoint { x, y } in line.into_iter() {
466 self.canvas.set_draw_color(PINK);
467 self.canvas
468 .fill_rect(Rect::new(
469 x as i32 * cs as i32 + self.start.x(),
470 y as i32 * cs as i32 + self.start.y(),
471 cs,
472 cs,
473 ))
474 .unwrap();
475 }
476 }
477 }
478 Brush::RectSelect(RectSelectBrush { start: Some(s), end: Some(e), active_end }) => {
479 self.canvas.set_draw_color(PINK);
480 let (width, height) = (abs_difference(s.x, e.x), abs_difference(s.y, e.y));
481 let MapPoint{x: start_x, y: start_y} = utils::rect_coords(s, e).0;
482 let start_loc_x = self.start.x() + (start_x * cs) as i32;
483 let start_loc_y = self.start.y() + (start_y * cs) as i32;
452 draw_text( 484 draw_text(
453 &mut self.canvas, 485 &mut self.canvas,
454 self.ttf_context, 486 self.ttf_context,
455 format!( 487 format!("{}x{}", width, height),
456 "{:.width$}°",
457 angle,
458 width = if (angle - ANGLE).abs() < 1e-3 { 3 } else { 0 }
459 ),
460 PINK, 488 PINK,
461 (self.mouse.0 + size as i32, self.mouse.1 + size as i32), 489 (start_loc_x, start_loc_y - 20),
462 ); 490 );
463 for MapPoint { x, y } in line.into_iter() { 491 self.canvas.draw_rect(
464 self.canvas.set_draw_color(PINK); 492 rect!(start_loc_x, start_loc_y, width * cs, height * cs)
465 self.canvas 493 ).unwrap();
466 .fill_rect(Rect::new(
467 x as i32 * cs as i32 + self.start.x(),
468 y as i32 * cs as i32 + self.start.y(),
469 cs,
470 cs,
471 ))
472 .unwrap();
473 }
474 } 494 }
495 _ => ()
475 } 496 }
476 } 497 }
477 498
@@ -699,27 +720,37 @@ impl<'ctx> AppState<'ctx> {
699 self.pixmap.invert(); 720 self.pixmap.invert();
700 self.undo_stack.push(ModifyRecord::Invert); 721 self.undo_stack.push(ModifyRecord::Invert);
701 } 722 }
723 // cycle through brushes
702 Keycode::F => self.cycle_brush(), 724 Keycode::F => self.cycle_brush(),
703 // bucket fill tool 725 // change rect select active end
726 Keycode::O => {
727 match &mut self.brush {
728 Brush::RectSelect(RectSelectBrush {
729 active_end,
730 ..
731 }) => *active_end = !*active_end,
732 _ => (),
733 };
734 }
704 Keycode::V => self.cycle_symmetry(), 735 Keycode::V => self.cycle_symmetry(),
705 // undo & redo 736 // undo
706 Keycode::U => { 737 Keycode::U => {
707 if let Some(op) = self.undo_stack.undo() { 738 if let Some(op) = self.undo_stack.undo() {
708 self.apply_operation(op, OpKind::Undo); 739 self.apply_operation(op, OpKind::Undo);
709 } 740 }
710 } 741 }
742 // redo
711 Keycode::R => { 743 Keycode::R => {
712 if let Some(op) = self.undo_stack.redo() { 744 if let Some(op) = self.undo_stack.redo() {
713 self.apply_operation(op, OpKind::Redo); 745 self.apply_operation(op, OpKind::Redo);
714 } 746 }
715 } 747 }
716 // export to file 748 Keycode::Escape => {
717 Keycode::N => { 749 match self.brush {
718 let image = self.export(); 750 Brush::RectSelect(_) => self.brush = Brush::rect(),
719 let encoded = image.encode().unwrap(); 751 _ => ()
720 let mut buffer = File::create("test.obi").unwrap(); 752 }
721 eprintln!("writing to file"); 753 continue;
722 buffer.write_all(&encoded[..]).unwrap();
723 } 754 }
724 _ if keymod == Mod::LCTRLMOD || keymod == Mod::RCTRLMOD => { 755 _ if keymod == Mod::LCTRLMOD || keymod == Mod::RCTRLMOD => {
725 self.brush = Brush::line( 756 self.brush = Brush::line(
@@ -783,6 +814,37 @@ impl<'ctx> AppState<'ctx> {
783 }); 814 });
784 } 815 }
785 } 816 }
817 Brush::RectSelect(RectSelectBrush {
818 start,
819 end,
820 active_end,
821 }) => {
822 if start.is_none() {
823 self.brush = Brush::RectSelect(RectSelectBrush {
824 start: contact,
825 end,
826 active_end,
827 });
828 } else if end.is_none() {
829 self.brush = Brush::RectSelect(RectSelectBrush {
830 start,
831 end: contact,
832 active_end,
833 });
834 } else if active_end {
835 self.brush = Brush::RectSelect(RectSelectBrush {
836 start,
837 end: contact,
838 active_end,
839 });
840 } else {
841 self.brush = Brush::RectSelect(RectSelectBrush {
842 start: contact,
843 end,
844 active_end,
845 });
846 };
847 }
786 Brush::Fill => { 848 Brush::Fill => {
787 if let Some(c) = contact { 849 if let Some(c) = contact {
788 // this `get` is unchecked because contact is checked 850 // this `get` is unchecked because contact is checked
@@ -815,22 +877,44 @@ impl<'ctx> AppState<'ctx> {
815 Event::MouseMotion { 877 Event::MouseMotion {
816 x, y, mousestate, .. 878 x, y, mousestate, ..
817 } => { 879 } => {
818 let size = match self.brush {
819 Brush::Circle(CircleBrush { size }) => size,
820 Brush::Line(LineBrush { size, .. }) => size,
821 _ => continue,
822 };
823 if mousestate.is_mouse_button_pressed(MouseButton::Left) { 880 if mousestate.is_mouse_button_pressed(MouseButton::Left) {
824 let pt = (x, y); 881 match self.brush {
825 let val = self.active_color; 882 Brush::RectSelect(RectSelectBrush{start, end, active_end}) => {
826 if let Ok(o) = self.paint_point(pt, val, size) { 883 if active_end {
827 self.current_operation.extend(o); 884 self.brush = Brush::RectSelect(RectSelectBrush{
885 start,
886 end: self.idx_at_coord((x, y)).map(MapPoint::from),
887 active_end
888 });
889 } else {
890 self.brush = Brush::RectSelect(RectSelectBrush{
891 start: self.idx_at_coord((x, y)).map(MapPoint::from),
892 end,
893 active_end
894 });
895 }
896 },
897 Brush::Circle(CircleBrush { size })
898 | Brush::Line(LineBrush { size, .. }) => {
899 let pt = (x, y);
900 let val = self.active_color;
901 if let Ok(o) = self.paint_point(pt, val, size) {
902 self.current_operation.extend(o);
903 }
904 },
905 _ => ()
828 } 906 }
829 } else if mousestate.is_mouse_button_pressed(MouseButton::Right) { 907 } else if mousestate.is_mouse_button_pressed(MouseButton::Right) {
830 let pt = (x, y); 908 match self.brush {
831 let val = !self.active_color; 909 Brush::Circle(CircleBrush { size })
832 if let Ok(o) = self.paint_point(pt, val, size) { 910 | Brush::Line(LineBrush { size, .. }) => {
833 self.current_operation.extend(o); 911 let pt = (x, y);
912 let val = !self.active_color;
913 if let Ok(o) = self.paint_point(pt, val, size) {
914 self.current_operation.extend(o);
915 }
916 },
917 _ => ()
834 } 918 }
835 } 919 }
836 } 920 }
diff --git a/src/bitmap.rs b/src/bitmap.rs
index ba5c8f1..0b1754a 100644
--- a/src/bitmap.rs
+++ b/src/bitmap.rs
@@ -306,7 +306,7 @@ impl Pixmap<bool> {
306 } 306 }
307} 307}
308 308
309fn abs_difference<T: Sub<Output = T> + Ord>(x: T, y: T) -> T { 309pub fn abs_difference<T: Sub<Output = T> + Ord>(x: T, y: T) -> T {
310 if x < y { 310 if x < y {
311 y - x 311 y - x
312 } else { 312 } else {
diff --git a/src/brush.rs b/src/brush.rs
index 8557ba7..8d3ee1c 100644
--- a/src/brush.rs
+++ b/src/brush.rs
@@ -25,8 +25,9 @@ pub struct CircleBrush {
25 25
26#[derive(Debug, Copy, Clone)] 26#[derive(Debug, Copy, Clone)]
27pub struct RectSelectBrush { 27pub struct RectSelectBrush {
28 pub start: MapPoint, 28 pub start: Option<MapPoint>,
29 pub end: MapPoint, 29 pub end: Option<MapPoint>,
30 pub active_end: bool,
30} 31}
31 32
32impl Brush { 33impl Brush {
@@ -60,8 +61,20 @@ impl Brush {
60 }) 61 })
61 } 62 }
62 63
64 pub fn rect() -> Self {
65 Brush::RectSelect(RectSelectBrush {
66 start: None,
67 end: None,
68 active_end: true,
69 })
70 }
71
63 pub fn is_line(&self) -> bool { 72 pub fn is_line(&self) -> bool {
64 matches!(self, Self::Line { .. }) 73 matches!(self, Self::Line(_))
74 }
75
76 pub fn is_rect(&self) -> bool {
77 matches!(self, Self::RectSelect(_))
65 } 78 }
66 79
67 pub fn size(&self) -> Option<u8> { 80 pub fn size(&self) -> Option<u8> {
diff --git a/src/utils.rs b/src/utils.rs
index 8c3b144..0825c8c 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -1,6 +1,7 @@
1use crate::{ 1use crate::{
2 app::AppState, 2 app::AppState,
3 consts::FONT_PATH, 3 consts::FONT_PATH,
4 bitmap::{abs_difference, MapPoint},
4 lisp::{ 5 lisp::{
5 error::{EvalError, LispError, ParseError}, 6 error::{EvalError, LispError, ParseError},
6 eval::Evaluator, 7 eval::Evaluator,
@@ -132,6 +133,21 @@ pub fn compress<T: PartialEq + Clone>(scanline: &[T]) -> Vec<(T, usize)> {
132 runs 133 runs
133} 134}
134 135
136pub fn rect_coords(s: MapPoint, e: MapPoint) -> (MapPoint, MapPoint) {
137 let (width, height) = (abs_difference(s.x, e.x), abs_difference(s.y, e.y));
138 let start_loc = if e.x >= s.x && e.y >= s.y {
139 (s.x, s.y)
140 } else if e.x >= s.x && e.y < s.y {
141 (s.x, e.y)
142 } else if e.x < s.x && e.y >= s.y {
143 (e.x, s.y)
144 } else {
145 (e.x, e.y)
146 }.into();
147 let end_loc = start_loc + (width, height).into();
148 (start_loc, end_loc)
149}
150
135#[cfg(test)] 151#[cfg(test)]
136mod tests { 152mod tests {
137 use super::compress; 153 use super::compress;