From 2b89b786a97b788ef8063b2777c6bc65a207033f Mon Sep 17 00:00:00 2001 From: Akshay Date: Fri, 2 Apr 2021 12:36:30 +0530 Subject: fix rare index error, more functional flood fill algo --- src/app.rs | 41 ++++++++++++++++++++++++----------------- src/bitmap.rs | 39 ++++++++++++++++++++++----------------- 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/src/app.rs b/src/app.rs index 84869a3..725a45a 100644 --- a/src/app.rs +++ b/src/app.rs @@ -176,8 +176,10 @@ impl<'ctx> AppState<'ctx> { .chain(sym_circle) .filter(|&pt| dither::bayer(dither_level, pt)) { - let old_val = self.pixmap.set(point, val); - modify_record.push(PaintRecord::new(point, old_val, val)); + if self.pixmap.contains(point) { + let old_val = self.pixmap.set(point, val); + modify_record.push(PaintRecord::new(point, old_val, val)); + } } Ok(modify_record) } @@ -203,8 +205,10 @@ impl<'ctx> AppState<'ctx> { .into_iter() .filter(|&pt| dither::bayer(dither_level, pt)) { - let old_val = self.pixmap.set(c, val); - line_modify_record.push(PaintRecord::new(c, old_val, val)); + if self.pixmap.contains(c) { + let old_val = self.pixmap.set(c, val); + line_modify_record.push(PaintRecord::new(c, old_val, val)); + } } } Ok(line_modify_record) @@ -353,9 +357,11 @@ impl<'ctx> AppState<'ctx> { container.place(&mut padding_box, HorAlign::Right, VertAlign::Center); padding_box.place(&mut primary, HorAlign::Center, VertAlign::Center); - self.canvas.set_draw_color(if !self.active_color { WHITE } else { BLACK }); + self.canvas + .set_draw_color(if !self.active_color { WHITE } else { BLACK }); self.canvas.fill_rect(primary.area()).unwrap(); - self.canvas.set_draw_color(if self.active_color { WHITE } else { BLACK }); + self.canvas + .set_draw_color(if self.active_color { WHITE } else { BLACK }); let brush_box = (0..8) .map(|x| (0..8).map(|y| (x, y).into()).collect::>()) @@ -364,9 +370,11 @@ impl<'ctx> AppState<'ctx> { .collect::>(); for pt in brush_box { - let canvas_pt = Point::from(primary.start) - + Point::from((pt.x as i32 * 2, pt.y as i32 * 2)); - self.canvas.fill_rect(rect!(canvas_pt.x(), canvas_pt.y(), 2, 2)).unwrap(); + let canvas_pt = + Point::from(primary.start) + Point::from((pt.x as i32 * 2, pt.y as i32 * 2)); + self.canvas + .fill_rect(rect!(canvas_pt.x(), canvas_pt.y(), 2, 2)) + .unwrap(); } let mouse_coords = if let Some((x, y)) = self.idx_at_coord(self.mouse) { @@ -735,20 +743,19 @@ impl<'ctx> AppState<'ctx> { Brush::Fill => { if let Some(c) = contact { let target = self.pixmap.get(c); - let mut operation = vec![]; - self.pixmap.flood_fill( - c, - target, - self.active_color, - &mut operation, - ); + let replacement = self.active_color; + let operation = + self.pixmap.flood_fill(c, target, replacement); + for o in operation.iter() { + self.pixmap.set(o.clone(), replacement); + } self.current_operation.extend( operation .into_iter() .map(|point| PaintRecord { point, old: target, - new: self.active_color, + new: replacement, }) .collect::>(), ) diff --git a/src/bitmap.rs b/src/bitmap.rs index dc66d73..6aa6b12 100644 --- a/src/bitmap.rs +++ b/src/bitmap.rs @@ -1,4 +1,5 @@ use std::{ + collections::HashSet, convert::{From, Into, TryFrom}, ops::{Add, Sub}, }; @@ -10,7 +11,7 @@ pub struct Pixmap { pub data: Vec, } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub struct MapPoint { pub x: u32, pub y: u32, @@ -249,22 +250,26 @@ where .collect() } - pub fn flood_fill( - &mut self, - start: MapPoint, - target: T, - replacement: T, - pts: &mut Vec, - ) { - if !self.contains(start) || self.get(start) != target || self.get(start) == replacement { - return; - } else { - pts.push(start); - self.set(start, replacement); - for (x, y) in [(1, 0), (0, 1), (-1, 0), (0, -1)].iter() { - let dir = MapPoint::try_from((start.x as i64 - x, start.y as i64 + y)); - if let Ok(pt) = dir { - self.flood_fill(pt, target, replacement, pts); + pub fn flood_fill(&mut self, start: MapPoint, target: T, replacement: T) -> Vec { + let mut queue = vec![start]; + let mut area = HashSet::new(); + loop { + if queue.is_empty() { + break area.drain().collect(); + } else { + let last = queue.pop().unwrap(); + area.insert(last); + for (x, y) in [(1, 0), (0, 1), (-1, 0), (0, -1)].iter() { + let dir = MapPoint::try_from((last.x as i64 + x, last.y as i64 + y)); + if let Ok(pt) = dir { + if self.contains(pt) + && self.get(pt) == target + && self.get(pt) != replacement + && !area.contains(&pt) + { + queue.push(pt); + } + } } } } -- cgit v1.2.3