diff options
-rw-r--r-- | src/app.rs | 41 | ||||
-rw-r--r-- | src/bitmap.rs | 39 |
2 files changed, 46 insertions, 34 deletions
@@ -176,8 +176,10 @@ impl<'ctx> AppState<'ctx> { | |||
176 | .chain(sym_circle) | 176 | .chain(sym_circle) |
177 | .filter(|&pt| dither::bayer(dither_level, pt)) | 177 | .filter(|&pt| dither::bayer(dither_level, pt)) |
178 | { | 178 | { |
179 | let old_val = self.pixmap.set(point, val); | 179 | if self.pixmap.contains(point) { |
180 | modify_record.push(PaintRecord::new(point, old_val, val)); | 180 | let old_val = self.pixmap.set(point, val); |
181 | modify_record.push(PaintRecord::new(point, old_val, val)); | ||
182 | } | ||
181 | } | 183 | } |
182 | Ok(modify_record) | 184 | Ok(modify_record) |
183 | } | 185 | } |
@@ -203,8 +205,10 @@ impl<'ctx> AppState<'ctx> { | |||
203 | .into_iter() | 205 | .into_iter() |
204 | .filter(|&pt| dither::bayer(dither_level, pt)) | 206 | .filter(|&pt| dither::bayer(dither_level, pt)) |
205 | { | 207 | { |
206 | let old_val = self.pixmap.set(c, val); | 208 | if self.pixmap.contains(c) { |
207 | line_modify_record.push(PaintRecord::new(c, old_val, val)); | 209 | let old_val = self.pixmap.set(c, val); |
210 | line_modify_record.push(PaintRecord::new(c, old_val, val)); | ||
211 | } | ||
208 | } | 212 | } |
209 | } | 213 | } |
210 | Ok(line_modify_record) | 214 | Ok(line_modify_record) |
@@ -353,9 +357,11 @@ impl<'ctx> AppState<'ctx> { | |||
353 | container.place(&mut padding_box, HorAlign::Right, VertAlign::Center); | 357 | container.place(&mut padding_box, HorAlign::Right, VertAlign::Center); |
354 | padding_box.place(&mut primary, HorAlign::Center, VertAlign::Center); | 358 | padding_box.place(&mut primary, HorAlign::Center, VertAlign::Center); |
355 | 359 | ||
356 | self.canvas.set_draw_color(if !self.active_color { WHITE } else { BLACK }); | 360 | self.canvas |
361 | .set_draw_color(if !self.active_color { WHITE } else { BLACK }); | ||
357 | self.canvas.fill_rect(primary.area()).unwrap(); | 362 | self.canvas.fill_rect(primary.area()).unwrap(); |
358 | self.canvas.set_draw_color(if self.active_color { WHITE } else { BLACK }); | 363 | self.canvas |
364 | .set_draw_color(if self.active_color { WHITE } else { BLACK }); | ||
359 | 365 | ||
360 | let brush_box = (0..8) | 366 | let brush_box = (0..8) |
361 | .map(|x| (0..8).map(|y| (x, y).into()).collect::<Vec<MapPoint>>()) | 367 | .map(|x| (0..8).map(|y| (x, y).into()).collect::<Vec<MapPoint>>()) |
@@ -364,9 +370,11 @@ impl<'ctx> AppState<'ctx> { | |||
364 | .collect::<Vec<_>>(); | 370 | .collect::<Vec<_>>(); |
365 | 371 | ||
366 | for pt in brush_box { | 372 | for pt in brush_box { |
367 | let canvas_pt = Point::from(primary.start) | 373 | let canvas_pt = |
368 | + Point::from((pt.x as i32 * 2, pt.y as i32 * 2)); | 374 | Point::from(primary.start) + Point::from((pt.x as i32 * 2, pt.y as i32 * 2)); |
369 | self.canvas.fill_rect(rect!(canvas_pt.x(), canvas_pt.y(), 2, 2)).unwrap(); | 375 | self.canvas |
376 | .fill_rect(rect!(canvas_pt.x(), canvas_pt.y(), 2, 2)) | ||
377 | .unwrap(); | ||
370 | } | 378 | } |
371 | 379 | ||
372 | let mouse_coords = if let Some((x, y)) = self.idx_at_coord(self.mouse) { | 380 | let mouse_coords = if let Some((x, y)) = self.idx_at_coord(self.mouse) { |
@@ -735,20 +743,19 @@ impl<'ctx> AppState<'ctx> { | |||
735 | Brush::Fill => { | 743 | Brush::Fill => { |
736 | if let Some(c) = contact { | 744 | if let Some(c) = contact { |
737 | let target = self.pixmap.get(c); | 745 | let target = self.pixmap.get(c); |
738 | let mut operation = vec![]; | 746 | let replacement = self.active_color; |
739 | self.pixmap.flood_fill( | 747 | let operation = |
740 | c, | 748 | self.pixmap.flood_fill(c, target, replacement); |
741 | target, | 749 | for o in operation.iter() { |
742 | self.active_color, | 750 | self.pixmap.set(o.clone(), replacement); |
743 | &mut operation, | 751 | } |
744 | ); | ||
745 | self.current_operation.extend( | 752 | self.current_operation.extend( |
746 | operation | 753 | operation |
747 | .into_iter() | 754 | .into_iter() |
748 | .map(|point| PaintRecord { | 755 | .map(|point| PaintRecord { |
749 | point, | 756 | point, |
750 | old: target, | 757 | old: target, |
751 | new: self.active_color, | 758 | new: replacement, |
752 | }) | 759 | }) |
753 | .collect::<Vec<PaintRecord>>(), | 760 | .collect::<Vec<PaintRecord>>(), |
754 | ) | 761 | ) |
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 @@ | |||
1 | use std::{ | 1 | use std::{ |
2 | collections::HashSet, | ||
2 | convert::{From, Into, TryFrom}, | 3 | convert::{From, Into, TryFrom}, |
3 | ops::{Add, Sub}, | 4 | ops::{Add, Sub}, |
4 | }; | 5 | }; |
@@ -10,7 +11,7 @@ pub struct Pixmap<T> { | |||
10 | pub data: Vec<T>, | 11 | pub data: Vec<T>, |
11 | } | 12 | } |
12 | 13 | ||
13 | #[derive(Copy, Clone, Debug)] | 14 | #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] |
14 | pub struct MapPoint { | 15 | pub struct MapPoint { |
15 | pub x: u32, | 16 | pub x: u32, |
16 | pub y: u32, | 17 | pub y: u32, |
@@ -249,22 +250,26 @@ where | |||
249 | .collect() | 250 | .collect() |
250 | } | 251 | } |
251 | 252 | ||
252 | pub fn flood_fill( | 253 | pub fn flood_fill(&mut self, start: MapPoint, target: T, replacement: T) -> Vec<MapPoint> { |
253 | &mut self, | 254 | let mut queue = vec![start]; |
254 | start: MapPoint, | 255 | let mut area = HashSet::new(); |
255 | target: T, | 256 | loop { |
256 | replacement: T, | 257 | if queue.is_empty() { |
257 | pts: &mut Vec<MapPoint>, | 258 | break area.drain().collect(); |
258 | ) { | 259 | } else { |
259 | if !self.contains(start) || self.get(start) != target || self.get(start) == replacement { | 260 | let last = queue.pop().unwrap(); |
260 | return; | 261 | area.insert(last); |
261 | } else { | 262 | for (x, y) in [(1, 0), (0, 1), (-1, 0), (0, -1)].iter() { |
262 | pts.push(start); | 263 | let dir = MapPoint::try_from((last.x as i64 + x, last.y as i64 + y)); |
263 | self.set(start, replacement); | 264 | if let Ok(pt) = dir { |
264 | for (x, y) in [(1, 0), (0, 1), (-1, 0), (0, -1)].iter() { | 265 | if self.contains(pt) |
265 | let dir = MapPoint::try_from((start.x as i64 - x, start.y as i64 + y)); | 266 | && self.get(pt) == target |
266 | if let Ok(pt) = dir { | 267 | && self.get(pt) != replacement |
267 | self.flood_fill(pt, target, replacement, pts); | 268 | && !area.contains(&pt) |
269 | { | ||
270 | queue.push(pt); | ||
271 | } | ||
272 | } | ||
268 | } | 273 | } |
269 | } | 274 | } |
270 | } | 275 | } |