From e18777e32448f465748535690666055c179d5d5f Mon Sep 17 00:00:00 2001 From: Akshay Date: Sun, 28 Mar 2021 12:58:58 +0530 Subject: add better brush drawing feedback --- src/app.rs | 107 +++++++++++++++++++++++++++++++++------------------- src/bitmap.rs | 28 ++++++++++++++ src/brush.rs | 36 ++++++++++-------- src/lisp/eval.rs | 2 +- src/lisp/expr.rs | 2 +- src/lisp/prelude.rs | 13 +++++-- src/main.rs | 2 +- src/utils.rs | 2 +- 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 @@ use crate::{ - bitmap::{MapPoint, Pixmap}, + bitmap::{positive_angle_with_x, MapPoint, Pixmap}, brush::Brush, command::CommandBox, consts::{colors::*, FONT_PATH}, @@ -35,7 +35,6 @@ pub enum Mode { pub struct AppState<'ctx, 'file> { pub active_color: bool, - pub brush_size: u8, pub brush: Brush, pub canvas: Canvas, pub command_box: CommandBox, @@ -146,8 +145,9 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { &mut self, center: P, val: bool, + brush_size: u8, ) -> Result, ()> { - let radius = self.brush_size as u32; + let radius = brush_size as u32; let center = self.idx_at_coord(center).ok_or(())?; let dither_level = self.dither_level; @@ -172,6 +172,7 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { start: MapPoint, end: P, val: bool, + brush_size: u8, ) -> Result, ()> { let MapPoint { x, y } = start; let end = self.idx_at_coord(end).ok_or(())?; @@ -182,7 +183,7 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { let mut line_modify_record = vec![]; for point in line.into_iter().chain(sym_line) { - let circle_around_point = self.pixmap.get_circle(point, self.brush_size as u32, true); + let circle_around_point = self.pixmap.get_circle(point, brush_size as u32, true); for c in circle_around_point .into_iter() .filter(|&pt| dither::bayer(dither_level, pt)) @@ -257,15 +258,15 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { ); } - pub fn increase_brush_size(&mut self) { - self.brush_size += 1; - } + // pub fn increase_brush_size(&mut self) { + // self.brush_size += 1; + // } - pub fn decrease_brush_size(&mut self) { - if self.brush_size > 0 { - self.brush_size -= 1; - } - } + // pub fn decrease_brush_size(&mut self) { + // if self.brush_size > 0 { + // self.brush_size -= 1; + // } + // } pub fn reduce_intensity(&mut self) { if self.dither_level > 0 { @@ -343,9 +344,8 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { format!("---, ---") }; let status_text = format!( - "[DITHER {}][BRUSH {}][SYM {}][PT {}][ACTIVE {}][KIND {}]", + "[DITHER {}][SYM {}][PT {}][ACTIVE {}][KIND {}]", self.dither_level, - self.brush_size + 1, self.symmetry, mouse_coords, if self.active_color { "WHT" } else { "BLK" }, @@ -405,22 +405,51 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { } fn draw_mouse(&mut self) { - let brush_size = self.brush_size; let cs = self.zoom as u32; let pt = self.idx_at_coord(self.mouse); - if let Some(center) = pt { - let circle = self.pixmap.get_circle(center, brush_size as u32, false); - for MapPoint { x, y } in circle.into_iter() { - self.canvas.set_draw_color(PINK); - self.canvas - .fill_rect(Rect::new( - x as i32 * cs as i32 + self.start.x(), - y as i32 * cs as i32 + self.start.y(), - cs, - cs, - )) - .unwrap(); + if matches!(self.brush, Brush::Circle { .. } | Brush::Line { .. }) { + let size = self.brush.size().unwrap(); + if let Some(center) = pt { + let circle = self.pixmap.get_circle(center, size as u32, false); + for MapPoint { x, y } in circle.into_iter() { + self.canvas.set_draw_color(PINK); + self.canvas + .fill_rect(Rect::new( + x as i32 * cs as i32 + self.start.x(), + y as i32 * cs as i32 + self.start.y(), + cs, + cs, + )) + .unwrap(); + } + } + } + match self.brush { + Brush::Line { start, size, .. } => { + let size = self.zoom as u32 * size as u32; + if let (Some(from), Some(to)) = (start, pt) { + let line = self.pixmap.get_line(from, to.into()); + draw_text( + &mut self.canvas, + self.ttf_context, + format!("{}°", positive_angle_with_x(from, to.into())), + PINK, + (self.mouse.0 as u32 + size, self.mouse.1 as u32 + size), + ); + for MapPoint { x, y } in line.into_iter() { + self.canvas.set_draw_color(PINK); + self.canvas + .fill_rect(Rect::new( + x as i32 * cs as i32 + self.start.x(), + y as i32 * cs as i32 + self.start.y(), + cs, + cs, + )) + .unwrap(); + } + } } + _ => {} } } @@ -485,9 +514,7 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { ev.push_event(Event::Quit { timestamp: 0u32 }) .expect("ohno unable to quit"); } -} -impl<'ctx, 'file> AppState<'ctx, 'file> { pub fn init( width: u32, height: u32, @@ -499,7 +526,7 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { let video_subsystem = context.video().unwrap(); let window = video_subsystem - .window("Pixel editor", 200, 200) + .window("Pixel editor", 500, 500) .position_centered() .resizable() .opengl() @@ -517,8 +544,7 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { let pixmap = Pixmap::new_with(width, height, data); Self { active_color: true, - brush_size: 0, - brush: Brush::new(), + brush: Brush::new(0), canvas, command_box: CommandBox::new(), context, @@ -594,8 +620,8 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { self.zoom_out(cursor); } // brush ops - Keycode::Q => self.decrease_brush_size(), - Keycode::E => self.increase_brush_size(), + Keycode::Q => self.brush.shrink(), + Keycode::E => self.brush.grow(), Keycode::Num1 => self.reduce_intensity(), Keycode::Num3 => self.increase_intensity(), // flip color @@ -650,7 +676,7 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { }; match self.brush { Brush::Circle { size } => { - if let Ok(o) = self.paint_point(pt, val) { + if let Ok(o) = self.paint_point(pt, val, size) { self.current_operation.extend(o); } } @@ -666,7 +692,7 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { extend, }; } else if let Ok(o) = - self.paint_line(start.unwrap(), pt, val) + self.paint_line(start.unwrap(), pt, val, size) { self.current_operation.extend(o); self.brush = Brush::Line { @@ -705,16 +731,21 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { Event::MouseMotion { x, y, mousestate, .. } => { + let size = match self.brush { + Brush::Circle { size } => size, + Brush::Line { size, .. } => size, + _ => continue, + }; if mousestate.is_mouse_button_pressed(MouseButton::Left) { let pt = (x, y); let val = self.active_color; - if let Ok(o) = self.paint_point(pt, val) { + if let Ok(o) = self.paint_point(pt, val, size) { self.current_operation.extend(o); } } else if mousestate.is_mouse_button_pressed(MouseButton::Right) { let pt = (x, y); let val = !self.active_color; - if let Ok(o) = self.paint_point(pt, val) { + if let Ok(o) = self.paint_point(pt, val, size) { self.current_operation.extend(o); } } 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 { Y, } +#[derive(Debug, Copy, Clone)] +pub enum Quadrant { + I, + II, + III, + IV, +} + impl MapPoint { #[inline] pub fn scale(self, c: u32) -> MapPoint { @@ -98,6 +106,17 @@ impl MapPoint { pub fn reflect(self, around: MapPoint) -> MapPoint { around.scale(2) - self } + + #[inline] + pub fn quadrant(self, origin: MapPoint) -> Quadrant { + match (self, origin) { + _ if self.x >= origin.x && self.y >= origin.y => Quadrant::I, + _ if self.x < origin.x && self.y >= origin.y => Quadrant::II, + _ if self.x < origin.x && self.y < origin.y => Quadrant::III, + _ if self.x >= origin.x && self.y < origin.y => Quadrant::IV, + _ => unreachable!("unexpected quadrant!"), + } + } } impl Pixmap @@ -278,3 +297,12 @@ pub fn mirror_figure(figure: &[MapPoint], line: u32, axis: Axis) -> Vec Vec { figure.iter().map(|pt| pt.reflect(around)).collect() } + +pub fn positive_angle_with_x(start: MapPoint, end: MapPoint) -> u32 { + if end.x == start.x { + return 90; + } + let numer = (end.y as f64 - start.y as f64).abs(); + let denum = (end.x as f64 - start.x as f64).abs(); + (numer / denum).atan().to_degrees() as u32 +} 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 { } impl Brush { - pub fn grow(&mut self) -> Result<(), BrushError> { + pub fn grow(&mut self) { match self { Brush::Line { ref mut size, .. } => *size += 1, Brush::Circle { ref mut size, .. } => *size += 1, Brush::Custom { ref mut size, .. } => *size += 1, - _ => return Err(BrushError::CannotIncreaseSize), + _ => (), } - Ok(()) } - pub fn shrink(&mut self) -> Result<(), BrushError> { + + pub fn shrink(&mut self) { match self { - Brush::Line { ref mut size, .. } => *size += size.saturating_sub(1), - Brush::Circle { ref mut size, .. } => *size += size.saturating_sub(1), - Brush::Custom { ref mut size, .. } => *size += size.saturating_sub(1), - _ => return Err(BrushError::CannotIncreaseSize), + Brush::Line { ref mut size, .. } => *size = size.saturating_sub(1), + Brush::Circle { ref mut size, .. } => *size = size.saturating_sub(1), + Brush::Custom { ref mut size, .. } => *size = size.saturating_sub(1), + _ => (), } - Ok(()) } - pub fn new() -> Self { - Brush::Circle { size: 0 } + + pub fn new(size: u8) -> Self { + Brush::Circle { size } } + pub fn line(size: u8, extend: bool) -> Self { Brush::Line { size, @@ -51,9 +52,18 @@ impl Brush { extend, } } + pub fn is_line(&self) -> bool { matches!(self, Self::Line { .. }) } + + pub fn size(&self) -> Option { + match self { + Brush::Line { size, .. } => Some(size.clone()), + Brush::Circle { size } => Some(size.clone()), + _ => None, + } + } } impl fmt::Display for Brush { @@ -67,7 +77,3 @@ impl fmt::Display for Brush { } } } - -pub enum BrushError { - CannotIncreaseSize, -} 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::{ error::{EvalError, LispError}, expr::{Arity, Ident, LispExpr, LispFunction}, number::LispNumber, - EnvList, Environment, + Environment, }, }; 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::{ error::{EvalError, LispError}, eval::lookup_extended, number::LispNumber, - EnvList, Environment, + EnvList, }; #[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::{ primitive, }; -use std::{convert::TryInto, fs::File, io::Write, path::PathBuf}; +use std::{convert::TryInto, fs::File, io::Write}; use log::info; @@ -196,13 +196,18 @@ pub fn new_env() -> Environment { primitive!(env, Arity::Atmost(1), "brush", |args, app| { info!("brush {}", &args[0]); + let old_size = if matches!(app.brush, Brush::Line { .. } | Brush::Circle { .. }) { + app.brush.size().unwrap() + } else { + 0 + }; if let [LispExpr::Quote(kind, _)] = args { if is_ident(kind) { match (&**kind).as_ref() { "fill" => app.brush = Brush::Fill, - "circle" => app.brush = Brush::new(), - "line" => app.brush = Brush::line(0, false), - "line-extend" => app.brush = Brush::line(0, true), + "circle" => app.brush = Brush::new(old_size), + "line" => app.brush = Brush::line(old_size, false), + "line-extend" => app.brush = Brush::line(old_size, true), _ => return Err(EvalError::CustomInternal("unknown brush type").into()), } } 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::{ env, fs::OpenOptions, io::{Cursor, Read}, - path::{Path, PathBuf}, + path::PathBuf, }; use 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>( let texture = texture_creator .create_texture_from_surface(&surface) .unwrap(); - let (width, height) = font.size_of_latin1(text.as_bytes()).unwrap(); + let (width, height) = font.size_of(&text).unwrap(); let area = rect!(x, y, width, height); canvas.copy(&texture, None, area).unwrap(); width -- cgit v1.2.3