Add ToolData and rectangle tool (#64)

This commit is contained in:
TrueDoctor 2021-04-07 09:25:23 +02:00 committed by Keavon Chambers
parent fafea371ab
commit 599d478a5c
7 changed files with 103 additions and 17 deletions

View File

@ -1,11 +1,12 @@
pub mod operation; pub mod operation;
pub use kurbo::{Circle, Point}; pub use kurbo::{Circle, Point, Rect};
pub use operation::Operation; pub use operation::Operation;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum SvgElement { pub enum SvgElement {
Circle(Circle), Circle(Circle),
Rect(Rect),
} }
impl SvgElement { impl SvgElement {
@ -14,6 +15,9 @@ impl SvgElement {
Self::Circle(c) => { Self::Circle(c) => {
format!(r#"<circle cx="{}" cy="{}" r="{}" style="fill: #fff;" />"#, c.center.x, c.center.y, c.radius) format!(r#"<circle cx="{}" cy="{}" r="{}" style="fill: #fff;" />"#, c.center.x, c.center.y, c.radius)
} }
Self::Rect(r) => {
format!(r#"<rect x="{}" y="{}" width="{}" height="{}" style="fill: #fff;" />"#, r.min_x(), r.min_y(), r.width(), r.height())
}
} }
} }
} }
@ -36,6 +40,11 @@ impl Document {
radius: r, 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()); update_frontend(self.render());
} }
} }

View File

@ -1,3 +1,4 @@
pub enum Operation { pub enum Operation {
AddCircle { cx: f64, cy: f64, r: f64 }, AddCircle { cx: f64, cy: f64, r: f64 },
AddRect { x0: f64, y0: f64, x1: f64, y1: f64 },
} }

View File

@ -55,6 +55,14 @@ pub struct ViewportPosition {
pub y: u32, 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)] #[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
pub struct TracePoint { pub struct TracePoint {
pub mouse_state: MouseState, pub mouse_state: MouseState,

View File

@ -1,5 +1,5 @@
use crate::events::MouseKeys;
use crate::events::{Event, Response}; use crate::events::{Event, Response};
use crate::events::{MouseKeys, ViewportPosition};
use crate::tools::{Fsm, Tool}; use crate::tools::{Fsm, Tool};
use crate::Document; use crate::Document;
use document_core::Operation; use document_core::Operation;
@ -7,13 +7,14 @@ use document_core::Operation;
#[derive(Default)] #[derive(Default)]
pub struct Ellipse { pub struct Ellipse {
fsm_state: EllipseToolFsmState, fsm_state: EllipseToolFsmState,
data: EllipseToolData,
} }
impl Tool for Ellipse { impl Tool for Ellipse {
fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec<Response>, Vec<Operation>) { fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec<Response>, Vec<Operation>) {
let mut responses = Vec::new(); let mut responses = Vec::new();
let mut operations = 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) (responses, operations)
} }
@ -23,7 +24,6 @@ impl Tool for Ellipse {
enum EllipseToolFsmState { enum EllipseToolFsmState {
Ready, Ready,
LmbDown, LmbDown,
TransformSelected,
} }
impl Default for EllipseToolFsmState { impl Default for EllipseToolFsmState {
@ -31,18 +31,29 @@ impl Default for EllipseToolFsmState {
EllipseToolFsmState::Ready EllipseToolFsmState::Ready
} }
} }
#[derive(Clone, Debug, Default)]
struct EllipseToolData {
drag_start: ViewportPosition,
}
impl Fsm for EllipseToolFsmState { impl Fsm for EllipseToolFsmState {
fn transition(self, event: &Event, document: &Document, responses: &mut Vec<Response>, operations: &mut Vec<Operation>) -> Self { type ToolData = EllipseToolData;
fn transition(self, event: &Event, document: &Document, data: &mut Self::ToolData, responses: &mut Vec<Response>, operations: &mut Vec<Operation>) -> Self {
match (self, event) { 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 // TODO - Check for left mouse button
(EllipseToolFsmState::LmbDown, Event::MouseUp(mouse_state)) => { (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 { operations.push(Operation::AddCircle {
cx: mouse_state.position.x as f64, cx: data.drag_start.x as f64,
cy: mouse_state.position.y as f64, cy: data.drag_start.y as f64,
r: 10.0, r: data.drag_start.distance(&mouse_state.position),
}); });
EllipseToolFsmState::Ready EllipseToolFsmState::Ready

View File

@ -21,7 +21,8 @@ pub trait Tool {
} }
pub trait Fsm { pub trait Fsm {
fn transition(self, event: &Event, document: &Document, responses: &mut Vec<Response>, operations: &mut Vec<Operation>) -> Self; type ToolData;
fn transition(self, event: &Event, document: &Document, data: &mut Self::ToolData, responses: &mut Vec<Response>, operations: &mut Vec<Operation>) -> Self;
} }
pub struct ToolFsmState { pub struct ToolFsmState {
@ -43,7 +44,7 @@ impl Default for ToolFsmState {
trace: Trace::new(), trace: Trace::new(),
primary_color: Color::BLACK, primary_color: Color::BLACK,
secondary_color: Color::WHITE, secondary_color: Color::WHITE,
active_tool_type: ToolType::Ellipse, active_tool_type: ToolType::Rectangle,
tools: gen_tools_hash_map! { tools: gen_tools_hash_map! {
Select => select::Select, Select => select::Select,
Crop => crop::Crop, Crop => crop::Crop,

View File

@ -1,18 +1,68 @@
use crate::events::{Event, Response}; use crate::events::{Event, Response};
use crate::tools::Tool; use crate::events::{MouseKeys, ViewportPosition};
use crate::tools::{Fsm, Tool};
use crate::Document; use crate::Document;
use document_core::Operation; use document_core::Operation;
#[derive(Default)] #[derive(Default)]
pub struct Rectangle; pub struct Rectangle {
fsm_state: RectangleToolFsmState,
data: RectangleToolData,
}
impl Tool for Rectangle { impl Tool for Rectangle {
fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec<Response>, Vec<Operation>) { fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec<Response>, Vec<Operation>) {
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 { enum RectangleToolFsmState {
Ready, 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<Response>, operations: &mut Vec<Operation>) -> 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,
}
}
} }

View File

@ -7,13 +7,14 @@ use document_core::Operation;
#[derive(Default)] #[derive(Default)]
pub struct Select { pub struct Select {
fsm_state: SelectToolFsmState, fsm_state: SelectToolFsmState,
data: SelectToolData,
} }
impl Tool for Select { impl Tool for Select {
fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec<Response>, Vec<Operation>) { fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec<Response>, Vec<Operation>) {
let mut responses = Vec::new(); let mut responses = Vec::new();
let mut operations = 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) (responses, operations)
} }
@ -32,8 +33,13 @@ impl Default for SelectToolFsmState {
} }
} }
#[derive(Default)]
struct SelectToolData;
impl Fsm for SelectToolFsmState { impl Fsm for SelectToolFsmState {
fn transition(self, event: &Event, document: &Document, responses: &mut Vec<Response>, operations: &mut Vec<Operation>) -> Self { type ToolData = SelectToolData;
fn transition(self, event: &Event, document: &Document, data: &mut Self::ToolData, responses: &mut Vec<Response>, operations: &mut Vec<Operation>) -> Self {
match (self, event) { match (self, event) {
(SelectToolFsmState::Ready, Event::MouseDown(mouse_state)) if mouse_state.mouse_keys.contains(MouseKeys::LEFT) => SelectToolFsmState::LmbDown, (SelectToolFsmState::Ready, Event::MouseDown(mouse_state)) if mouse_state.mouse_keys.contains(MouseKeys::LEFT) => SelectToolFsmState::LmbDown,