From ef37a6552a71f86eb1e393b61a3bbb5d81815783 Mon Sep 17 00:00:00 2001 From: Akshay Date: Sat, 13 Mar 2021 22:54:21 +0530 Subject: factor out line and circle drawing into bitmap --- src/app.rs | 115 +++++++++++++++++++++++++++------------------------------- src/bitmap.rs | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/undo.rs | 8 ++-- 3 files changed, 160 insertions(+), 66 deletions(-) diff --git a/src/app.rs b/src/app.rs index c0d3018..3c57f27 100644 --- a/src/app.rs +++ b/src/app.rs @@ -103,61 +103,30 @@ impl<'ctx> AppState<'ctx> { center: P, val: bool, ) -> Result, ()> { - let radius = self.brush_size; - if radius == 1 { - let center_point: Point = center.into(); - return Ok(if let Some(pt) = self.idx_at_coord(center_point) { - let old_val = self.pixmap.set(pt, val); - Ok(ModifyRecord::new(pt, old_val, val)) - } else { - Err(()) - } - .map(|x| vec![x])?); - } else { - if let Some(center_on_grid) = self.idx_at_coord(center) { - // center_on_grid is now a coordinate on the drawing grid - let (x0, y0) = (center_on_grid.0 as i64, center_on_grid.1 as i64); - let (mut dx, mut dy, mut err) = (radius as i64, 0i64, 1 - radius as i64); - let mut circle = vec![]; - let mut old_vals = vec![]; - while dx >= dy { - circle.push((x0 + dx, y0 + dy)); - circle.push((x0 - dx, y0 + dy)); - circle.push((x0 + dx, y0 - dy)); - circle.push((x0 - dx, y0 - dy)); - circle.push((x0 + dy, y0 + dx)); - circle.push((x0 - dy, y0 + dx)); - circle.push((x0 + dy, y0 - dx)); - circle.push((x0 - dy, y0 - dx)); - dy = dy + 1; - if err < 0 { - err = err + 2 * dy + 1; - } else { - dx -= 1; - err += 2 * (dy - dx) + 1; - } - } - // circle's insides - for x in 0..radius as i64 { - for y in 0..radius as i64 { - if x.pow(2) + y.pow(2) < (radius as i64).pow(2) { - circle.push((x0 + x, y0 + y)); - circle.push((x0 - x, y0 + y)); - circle.push((x0 + x, y0 - y)); - circle.push((x0 - x, y0 - y)); - } - } - } - for circumference_pt in circle { - if let Ok(mp) = MapPoint::try_from(circumference_pt) { - let old_val = self.pixmap.set(mp, val); - old_vals.push(ModifyRecord::new((mp.x, mp.y), old_val, val)); - } - } - return Ok(old_vals); + let radius = self.brush_size as u32; + let center = self.idx_at_coord(center).ok_or(())?; + let mut circle_modify_record = vec![]; + for point in self.pixmap.get_circle(center, radius) { + let old_val = self.pixmap.set(point, val); + circle_modify_record.push(ModifyRecord::new(point, old_val, val)); + } + Ok(circle_modify_record) + } + + fn paint_line>(&mut self, start: P, end: P) -> Result, ()> { + let start = self.idx_at_coord(start).ok_or(())?; + let end = self.idx_at_coord(end).ok_or(())?; + let line_coords = self.pixmap.get_line(start, end); + let mut line_modify_record = vec![]; + let val = self.active_color; + for point in line_coords { + let circle_around_point = self.pixmap.get_circle(point, self.brush_size as u32); + for c in circle_around_point { + let old_val = self.pixmap.set(c, val); + line_modify_record.push(ModifyRecord::new(c, old_val, val)); } } - return Err(()); + Ok(line_modify_record) } fn apply_operation(&mut self, op: Operation, op_kind: OpKind) { @@ -176,6 +145,17 @@ impl<'ctx> AppState<'ctx> { } } + fn commit_operation(&mut self) { + if !self.current_operation.is_empty() { + let op = self + .current_operation + .drain(..) + .filter(|v| !v.old_val == v.val) + .collect::>(); + self.undo_stack.push(op); + } + } + fn zoom_in(&mut self, p: (i32, i32)) { // attempt to center around cursor if let Some(p) = self.idx_at_coord(p) { @@ -192,8 +172,8 @@ impl<'ctx> AppState<'ctx> { self.brush_size += 1; } - fn descrease_brush_size(&mut self) { - if self.brush_size > 1 { + fn decrease_brush_size(&mut self) { + if self.brush_size > 0 { self.brush_size -= 1; } } @@ -296,7 +276,7 @@ impl<'ctx> AppState<'ctx> { Self { start: Point::new(60, 60), zoom: 5, - brush_size: 1, + brush_size: 0, pixmap, grid: Grid::new(), canvas, @@ -339,12 +319,25 @@ impl<'ctx> AppState<'ctx> { self.modify(|e| e.zoom_out(cursor)); } // brush ops - Keycode::Q => self.modify(|e| e.descrease_brush_size()), - Keycode::E => self.modify(|e| e.increase_brush_size()), + Keycode::Q => self.decrease_brush_size(), + Keycode::E => self.increase_brush_size(), // flip color Keycode::X => self.modify(|e| e.change_active_color()), // toggle grid Keycode::Tab => self.modify(|e| e.toggle_grid()), + // line drawing + Keycode::F => self.modify(|e| { + let end = (mouse.x(), mouse.y()).into(); + if let Some(start) = e.last_point { + if let Ok(o) = e.paint_line(start, end) { + e.commit_operation(); + e.current_operation = + o.into_iter().filter(|v| !v.old_val == v.val).collect(); + e.commit_operation(); + e.last_point = Some(end); + } + } + }), // exit Keycode::Escape => break 'running, // undo & redo @@ -381,9 +374,7 @@ impl<'ctx> AppState<'ctx> { Event::MouseMotion { x, y, mousestate, .. } => { - let is_left = mousestate.is_mouse_button_pressed(MouseButton::Left); - let is_right = mousestate.is_mouse_button_pressed(MouseButton::Right); - if is_left { + if mousestate.is_mouse_button_pressed(MouseButton::Left) { self.modify(|e| { let pt = (x, y); let val = e.active_color; @@ -391,7 +382,7 @@ impl<'ctx> AppState<'ctx> { e.current_operation.extend(o); } }); - } else if is_right { + } else if mousestate.is_mouse_button_pressed(MouseButton::Right) { self.modify(|e| { let pt = (x, y); let val = !e.active_color; diff --git a/src/bitmap.rs b/src/bitmap.rs index 01a39d9..8ff311b 100644 --- a/src/bitmap.rs +++ b/src/bitmap.rs @@ -1,4 +1,7 @@ -use std::convert::{From, Into, TryFrom}; +use std::{ + convert::{From, Into, TryFrom}, + ops::Sub, +}; #[derive(Debug)] pub struct Pixmap { @@ -59,6 +62,7 @@ where data, } } + pub fn new_with(width: u32, height: u32, start: T) -> Self { let data = vec![start; (width * height) as usize]; Pixmap { @@ -67,18 +71,22 @@ where data, } } + pub fn is_inside>(&self, pt: P) -> bool { let MapPoint { x, y } = pt.into(); x < self.width && y < self.height } + pub fn idx>(&self, pt: P) -> usize { let MapPoint { x, y } = pt.into(); (y * self.width + x) as usize } + pub fn get>(&self, pt: P) -> T { let idx = self.idx(pt); self.data[idx] } + pub fn set>(&mut self, pt: P, new_val: T) -> T { let pt = pt.into(); let old_val = self.get(pt); @@ -86,4 +94,97 @@ where self.data[idx] = new_val; old_val } + + pub fn get_circle>(&self, center: P, radius: u32) -> Vec { + let mut circle: Vec<(i64, i64)> = vec![]; + let MapPoint { x, y } = center.into(); + let x = x as i64; + let y = y as i64; + let (mut dx, mut dy, mut err) = (radius as i64, 0i64, 1 - radius as i64); + while dx >= dy { + circle.push((x + dx, y + dy)); + circle.push((x - dx, y + dy)); + circle.push((x + dx, y - dy)); + circle.push((x - dx, y - dy)); + circle.push((x + dy, y + dx)); + circle.push((x - dy, y + dx)); + circle.push((x + dy, y - dx)); + circle.push((x - dy, y - dx)); + dy = dy + 1; + if err < 0 { + err = err + 2 * dy + 1; + } else { + dx -= 1; + err += 2 * (dy - dx) + 1; + } + } + for xi in 0..radius as i64 { + for yi in 0..radius as i64 { + if xi.pow(2) + yi.pow(2) < (radius as i64).pow(2) { + circle.push((x + xi, y + yi)); + circle.push((x - xi, y + yi)); + circle.push((x + xi, y - yi)); + circle.push((x - xi, y - yi)); + } + } + } + circle + .into_iter() + .flat_map(|pt| MapPoint::try_from(pt)) + .filter(|&pt| self.is_inside(pt)) + .collect() + } + + pub fn get_line>(&self, start: P, end: P) -> Vec { + let MapPoint { x: x1, y: y1 } = start.into(); + let MapPoint { x: x2, y: y2 } = end.into(); + let mut coordinates = vec![]; + let dx = abs_difference(x1, x2) as i64; + let dy = abs_difference(y1, y2) as i64; + let sx = { + if x1 < x2 { + 1 + } else { + -1 + } + }; + let sy = { + if y1 < y2 { + 1 + } else { + -1 + } + }; + let mut err: i64 = dx - dy; + let mut current_x = x1 as i64; + let mut current_y = y1 as i64; + loop { + coordinates.push((current_x, current_y)); + if current_x == x2 as i64 && current_y == y2 as i64 { + break; + } + let e2 = 2 * err; + if e2 > -dy { + err -= dy; + current_x += sx; + } + if e2 < dx { + err += dx; + current_y += sy; + } + } + coordinates + .into_iter() + .flat_map(|pt| MapPoint::try_from(pt)) + .filter(|&pt| self.is_inside(pt)) + .collect() + } +} + +fn abs_difference + Ord>(x: T, y: T) -> T { + if x < y { + y - x + } else { + x - y + } } diff --git a/src/undo.rs b/src/undo.rs index b590312..ae96a66 100644 --- a/src/undo.rs +++ b/src/undo.rs @@ -1,14 +1,16 @@ +use crate::bitmap::MapPoint; + #[derive(Copy, Clone, Debug)] pub struct ModifyRecord { - pub point: (u32, u32), + pub point: MapPoint, pub old_val: bool, pub val: bool, } impl ModifyRecord { - pub fn new(point: (u32, u32), old_val: bool, val: bool) -> Self { + pub fn new>(point: P, old_val: bool, val: bool) -> Self { ModifyRecord { - point, + point: point.into(), old_val, val, } -- cgit v1.2.3