From 390de72aa21e01f0c833e7fcc5bfa469f12cb9a0 Mon Sep 17 00:00:00 2001 From: Enrico Lumetti Date: Sun, 28 Feb 2021 18:49:08 +0100 Subject: [PATCH] Improve canvas API and fix erasing of multiple elements --- src/canvas.rs | 43 +++++++++++++++++++++++++++++++++++-------- src/main.rs | 7 +------ src/tool.rs | 44 +++++++++++++++++++++++++++++++------------- src/widget/canvas.rs | 20 ++++++++++++-------- 4 files changed, 79 insertions(+), 35 deletions(-) diff --git a/src/canvas.rs b/src/canvas.rs index 0c9ea65..fc1e3e4 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -16,7 +16,7 @@ use std::vec::Vec; -use im::Vector; +use im::{vector, Vector}; use serde::de::{Deserializer, SeqAccess, Visitor}; use serde::ser::Serializer; @@ -128,20 +128,43 @@ impl CanvasElement { } } } - - pub fn get_path_mut(&mut self) -> Option<&mut Path> { - match self { - CanvasElement::Freehand { path, .. } => Some(path), - } - } } #[derive(Clone, druid::Data)] pub struct Canvas { - pub elements: Vector, + elements: Vector, + content_size: druid::Size, } impl Canvas { + pub fn new() -> Self { + Canvas { + elements: vector![], + content_size: druid::Size::new(0.0, 0.0), + } + } + + pub fn new_with_elements(elements: Vector) -> Self { + let mut cv = Canvas::new(); + for e in elements { + cv.add_element(e); + } + cv + } + + pub fn add_element(&mut self, element: CanvasElement) { + self.content_size = self + .content_size + .to_rect() + .union(element.bounding_box()) + .size(); + self.elements.push_back(element); + } + + pub fn elements(&self) -> &Vector { + &self.elements + } + /// Find all CanvasElement that intersect with rect pub fn find_intersections(&self, rect: druid::Rect) -> Vec { let mut found_elements = Vec::::new(); @@ -157,6 +180,10 @@ impl Canvas { found_elements } + + pub fn content_size(&self) -> druid::Size { + self.content_size + } } impl Serialize for Path { diff --git a/src/main.rs b/src/main.rs index bc5a27d..5380ae8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -49,12 +49,7 @@ pub fn main() { color: Color::rgb(255.0, 0.0, 0.0), }; let canvas_data = StilettoState { - canvas: CanvasState { - versioned_canvas: VersionedCanvas::new(Canvas { - elements: vector![], - }), - tool_ctx: CanvasToolCtx::new(default_pen_params.clone()), - }, + canvas: CanvasState::new(CanvasToolCtx::new(default_pen_params.clone())), tool_icons: vector![ CanvasToolIconState { tool_params: default_pen_params, diff --git a/src/tool.rs b/src/tool.rs index d22870e..dec5a63 100644 --- a/src/tool.rs +++ b/src/tool.rs @@ -91,14 +91,22 @@ impl CanvasToolCtx { self.state = CanvasToolState::Erasing; } (CanvasToolState::Erasing, Event::MouseMove(mouse_event)) => { - let eraser_rect = druid::Rect::from_center_size(mouse_event.pos, (5.0, 5.0)); - let elements = vcanvas.get().find_intersections(eraser_rect); - - if !elements.is_empty() { - vcanvas.update(|canvas: &mut Canvas| { - for i in elements { - canvas.elements.remove(i); + let eraser_rect = + druid::Rect::from_center_size(mouse_event.pos, (5.0, 5.0)); + let old_elements = vcanvas.get().elements(); + let mut new_elements = old_elements.clone(); + new_elements.retain(|elem| { + // Check if the element intersects the eraser rect + if elem.bounding_box().intersect(eraser_rect).area() > 0.0 { + if elem.intersects_rect(eraser_rect) { + return false; } + } + return true; + }); + if new_elements.len() != old_elements.len() { + vcanvas.update(|canvas: &mut Canvas| { + *canvas = Canvas::new_with_elements(new_elements); }); } } @@ -138,17 +146,27 @@ impl CanvasToolCtx { }, Event::MouseMove(mouse_event), ) => { - current_path - .get_path_mut() - .unwrap() - .kurbo_path - .line_to((mouse_event.pos.x, mouse_event.pos.y)); + if let CanvasElement::Freehand { ref mut path, .. } = current_path { + path.kurbo_path + .line_to((mouse_event.pos.x, mouse_event.pos.y)); + } } (CanvasToolState::DrawingFreehand { .. }, Event::MouseUp(_)) => { vcanvas.update(move |canvas: &mut Canvas| { let current_state = std::mem::replace(&mut self.state, CanvasToolState::Idle); if let CanvasToolState::DrawingFreehand { current_path, .. } = current_state { - canvas.elements.push_back(current_path); + if let CanvasElement::Freehand { + mut path, + mut thickness, + stroke_color, + } = current_path + { + canvas.add_element(CanvasElement::Freehand { + path, + thickness, + stroke_color, + }); + } } }); } diff --git a/src/widget/canvas.rs b/src/widget/canvas.rs index 67b06e0..ece8ac0 100644 --- a/src/widget/canvas.rs +++ b/src/widget/canvas.rs @@ -31,6 +31,12 @@ pub struct CanvasState { } impl CanvasState { + pub fn new(tool_ctx: CanvasToolCtx) -> Self { + CanvasState { + versioned_canvas: VersionedCanvas::new(Canvas::new()), + tool_ctx, + } + } pub fn perform_undo(&mut self) { //if !self.is_drawing() { self.versioned_canvas.undo(); @@ -50,7 +56,7 @@ impl CanvasState { canvas_elements: self .versioned_canvas .get() - .elements + .elements() .iter() .cloned() .collect(), @@ -58,9 +64,9 @@ impl CanvasState { } pub fn set_from_snapshot(&mut self, snapshot: DocumentSnapshot) { - self.versioned_canvas = VersionedCanvas::new(Canvas { - elements: Vector::from(snapshot.canvas_elements), - }); + self.versioned_canvas = VersionedCanvas::new(Canvas::new_with_elements(Vector::from( + snapshot.canvas_elements, + ))); } pub fn set_tool_ctx(&mut self, ctx: CanvasToolCtx) { @@ -128,11 +134,9 @@ impl Widget for CanvasWidget { // (ctx.size() returns the size of the layout rect we're painting in) let size = ctx.size(); let rect = size.to_rect(); - // Note: ctx also has a `clear` method, but that clears the whole context, - // and we only want to clear this widget's area. - ctx.fill(rect, &Color::WHITE); - for element in data.versioned_canvas.get().elements.iter() { + ctx.fill(rect, &Color::WHITE); + for element in data.versioned_canvas.get().elements().iter() { element.draw(ctx); } if data.tool_ctx.needs_repaint() {