diff --git a/core/document/src/lib.rs b/core/document/src/lib.rs index fe1eaf57..77e49525 100644 --- a/core/document/src/lib.rs +++ b/core/document/src/lib.rs @@ -1,11 +1,12 @@ pub mod operation; -pub use kurbo::{Circle, Point}; +pub use kurbo::{Circle, Point, Rect}; pub use operation::Operation; #[derive(Debug, Clone, PartialEq)] pub enum SvgElement { Circle(Circle), + Rect(Rect), } impl SvgElement { @@ -14,6 +15,9 @@ impl SvgElement { Self::Circle(c) => { format!(r#""#, c.center.x, c.center.y, c.radius) } + Self::Rect(r) => { + format!(r#""#, r.min_x(), r.min_y(), r.width(), r.height()) + } } } } @@ -36,6 +40,11 @@ impl Document { radius: r, })); + update_frontend(self.render()); + } + Operation::AddRect { x0, y0, x1, y1 } => { + self.svg.push(SvgElement::Rect(Rect::from_points(Point::new(x0, y0), Point::new(x1, y1)))); + update_frontend(self.render()); } } diff --git a/core/document/src/operation.rs b/core/document/src/operation.rs index c0d24003..08b5eb23 100644 --- a/core/document/src/operation.rs +++ b/core/document/src/operation.rs @@ -1,3 +1,4 @@ pub enum Operation { AddCircle { cx: f64, cy: f64, r: f64 }, + AddRect { x0: f64, y0: f64, x1: f64, y1: f64 }, } diff --git a/core/editor/src/dispatcher/events.rs b/core/editor/src/dispatcher/events.rs index 0a62920f..deb95b4b 100644 --- a/core/editor/src/dispatcher/events.rs +++ b/core/editor/src/dispatcher/events.rs @@ -55,6 +55,14 @@ pub struct ViewportPosition { pub y: u32, } +impl ViewportPosition { + pub fn distance(&self, other: &Self) -> f64 { + let x_diff = other.x as f64 - self.x as f64; + let y_diff = other.y as f64 - self.y as f64; + f64::sqrt(x_diff * x_diff + y_diff * y_diff) + } +} + #[derive(Debug, Copy, Clone, Default, Eq, PartialEq)] pub struct TracePoint { pub mouse_state: MouseState, diff --git a/core/editor/src/tools/ellipse.rs b/core/editor/src/tools/ellipse.rs index 4b7bc2be..915d3370 100644 --- a/core/editor/src/tools/ellipse.rs +++ b/core/editor/src/tools/ellipse.rs @@ -1,5 +1,5 @@ -use crate::events::MouseKeys; use crate::events::{Event, Response}; +use crate::events::{MouseKeys, ViewportPosition}; use crate::tools::{Fsm, Tool}; use crate::Document; use document_core::Operation; @@ -7,13 +7,14 @@ use document_core::Operation; #[derive(Default)] pub struct Ellipse { fsm_state: EllipseToolFsmState, + data: EllipseToolData, } impl Tool for Ellipse { fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec, Vec) { let mut responses = Vec::new(); let mut operations = Vec::new(); - self.fsm_state = self.fsm_state.transition(event, document, &mut responses, &mut operations); + self.fsm_state = self.fsm_state.transition(event, document, &mut self.data, &mut responses, &mut operations); (responses, operations) } @@ -23,7 +24,6 @@ impl Tool for Ellipse { enum EllipseToolFsmState { Ready, LmbDown, - TransformSelected, } impl Default for EllipseToolFsmState { @@ -31,18 +31,29 @@ impl Default for EllipseToolFsmState { EllipseToolFsmState::Ready } } +#[derive(Clone, Debug, Default)] +struct EllipseToolData { + drag_start: ViewportPosition, +} impl Fsm for EllipseToolFsmState { - fn transition(self, event: &Event, document: &Document, responses: &mut Vec, operations: &mut Vec) -> Self { + type ToolData = EllipseToolData; + + fn transition(self, event: &Event, document: &Document, data: &mut Self::ToolData, responses: &mut Vec, operations: &mut Vec) -> Self { match (self, event) { - (EllipseToolFsmState::Ready, Event::MouseDown(mouse_state)) if mouse_state.mouse_keys.contains(MouseKeys::LEFT) => EllipseToolFsmState::LmbDown, + (EllipseToolFsmState::Ready, Event::MouseDown(mouse_state)) if mouse_state.mouse_keys.contains(MouseKeys::LEFT) => { + data.drag_start = mouse_state.position; + EllipseToolFsmState::LmbDown + } // TODO - Check for left mouse button (EllipseToolFsmState::LmbDown, Event::MouseUp(mouse_state)) => { + let r = data.drag_start.distance(&mouse_state.position); + log::info!("draw ellipse with radius: {:.2}", r); operations.push(Operation::AddCircle { - cx: mouse_state.position.x as f64, - cy: mouse_state.position.y as f64, - r: 10.0, + cx: data.drag_start.x as f64, + cy: data.drag_start.y as f64, + r: data.drag_start.distance(&mouse_state.position), }); EllipseToolFsmState::Ready diff --git a/core/editor/src/tools/mod.rs b/core/editor/src/tools/mod.rs index 3ffd3eb0..3390f900 100644 --- a/core/editor/src/tools/mod.rs +++ b/core/editor/src/tools/mod.rs @@ -21,7 +21,8 @@ pub trait Tool { } pub trait Fsm { - fn transition(self, event: &Event, document: &Document, responses: &mut Vec, operations: &mut Vec) -> Self; + type ToolData; + fn transition(self, event: &Event, document: &Document, data: &mut Self::ToolData, responses: &mut Vec, operations: &mut Vec) -> Self; } pub struct ToolFsmState { @@ -43,7 +44,7 @@ impl Default for ToolFsmState { trace: Trace::new(), primary_color: Color::BLACK, secondary_color: Color::WHITE, - active_tool_type: ToolType::Ellipse, + active_tool_type: ToolType::Rectangle, tools: gen_tools_hash_map! { Select => select::Select, Crop => crop::Crop, diff --git a/core/editor/src/tools/rectangle.rs b/core/editor/src/tools/rectangle.rs index 2383e77b..679030b0 100644 --- a/core/editor/src/tools/rectangle.rs +++ b/core/editor/src/tools/rectangle.rs @@ -1,18 +1,68 @@ use crate::events::{Event, Response}; -use crate::tools::Tool; +use crate::events::{MouseKeys, ViewportPosition}; +use crate::tools::{Fsm, Tool}; use crate::Document; use document_core::Operation; #[derive(Default)] -pub struct Rectangle; +pub struct Rectangle { + fsm_state: RectangleToolFsmState, + data: RectangleToolData, +} impl Tool for Rectangle { fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec, Vec) { - todo!(); + let mut responses = Vec::new(); + let mut operations = Vec::new(); + self.fsm_state = self.fsm_state.transition(event, document, &mut self.data, &mut responses, &mut operations); + + (responses, operations) } } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] enum RectangleToolFsmState { Ready, - Dragging, + LmbDown, +} + +impl Default for RectangleToolFsmState { + fn default() -> Self { + RectangleToolFsmState::Ready + } +} +#[derive(Clone, Debug, Default)] +struct RectangleToolData { + drag_start: ViewportPosition, +} + +impl Fsm for RectangleToolFsmState { + type ToolData = RectangleToolData; + + fn transition(self, event: &Event, document: &Document, data: &mut Self::ToolData, responses: &mut Vec, operations: &mut Vec) -> Self { + match (self, event) { + (RectangleToolFsmState::Ready, Event::MouseDown(mouse_state)) if mouse_state.mouse_keys.contains(MouseKeys::LEFT) => { + data.drag_start = mouse_state.position; + RectangleToolFsmState::LmbDown + } + + // TODO - Check for left mouse button + (RectangleToolFsmState::LmbDown, Event::MouseUp(mouse_state)) => { + let r = data.drag_start.distance(&mouse_state.position); + log::info!("draw rectangle with radius: {:.2}", r); + let start = data.drag_start; + let end = mouse_state.position; + operations.push(Operation::AddRect { + x0: start.x as f64, + y0: start.y as f64, + x1: end.x as f64, + y1: end.y as f64, + }); + + RectangleToolFsmState::Ready + } + + _ => self, + } + } } diff --git a/core/editor/src/tools/select.rs b/core/editor/src/tools/select.rs index 3c7f307c..06ed7806 100644 --- a/core/editor/src/tools/select.rs +++ b/core/editor/src/tools/select.rs @@ -7,13 +7,14 @@ use document_core::Operation; #[derive(Default)] pub struct Select { fsm_state: SelectToolFsmState, + data: SelectToolData, } impl Tool for Select { fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec, Vec) { let mut responses = Vec::new(); let mut operations = Vec::new(); - self.fsm_state = self.fsm_state.transition(event, document, &mut responses, &mut operations); + self.fsm_state = self.fsm_state.transition(event, document, &mut self.data, &mut responses, &mut operations); (responses, operations) } @@ -32,8 +33,13 @@ impl Default for SelectToolFsmState { } } +#[derive(Default)] +struct SelectToolData; + impl Fsm for SelectToolFsmState { - fn transition(self, event: &Event, document: &Document, responses: &mut Vec, operations: &mut Vec) -> Self { + type ToolData = SelectToolData; + + fn transition(self, event: &Event, document: &Document, data: &mut Self::ToolData, responses: &mut Vec, operations: &mut Vec) -> Self { match (self, event) { (SelectToolFsmState::Ready, Event::MouseDown(mouse_state)) if mouse_state.mouse_keys.contains(MouseKeys::LEFT) => SelectToolFsmState::LmbDown,