mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-19 19:08:05 +08:00
Add a movable canvas with matricies (#175)
* Convert polygon and rectangle tool to kurbo::BezPath * Add glam * Add affine transform to elipse and remove circle * Format * Add svg group and add matrix for group * Convert all operations to use matricies * Work uses same transform as root * Format * Frontend fixed to render changes to working colors when changed from backend (#180) * Backend and Frontend modification to show working color mods * Remove comments & change precedence for tool and doc actions * Add keybind for resetting work colors * Minor Frontend changes * Remove early sample "greet" code * Add a contributing section to the project README * Add moving document around * Add document transform for tools * Update to GraphiteEditor's fork * Use write in foreach for rendering group / folder * Add missing TranslateDown action * Use points for line operation * Format * Add todo to change to shape's aspect ratio * Remove empty if * Initial pass at refactor * Fix polyline test * Use document message to modify document transform * Messages -> Operations * Transform layer * Format * Use DAffine2::IDENTITY * Clean up kurbo generation for line and rect * Use .into for rectangle points * Rename cols to transform * Rename other cols to transform * Add todo for into_iter * Remove unnecessary clone Co-authored-by: akshay1992kalbhor <akshay1992kalbhor@gmail.com> Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
committed by
Keavon Chambers
parent
923e63c045
commit
bb3293af43
@@ -34,7 +34,7 @@ impl Dispatcher {
|
||||
}
|
||||
match message {
|
||||
NoOp => (),
|
||||
Document(message) => self.document_message_handler.process_action(message, (), &mut self.messages),
|
||||
Document(message) => self.document_message_handler.process_action(message, &self.input_preprocessor, &mut self.messages),
|
||||
Global(message) => self.global_message_handler.process_action(message, (), &mut self.messages),
|
||||
Tool(message) => self
|
||||
.tool_message_handler
|
||||
|
||||
@@ -60,9 +60,10 @@ impl Document {
|
||||
let folder = self.document.document_folder(path)?;
|
||||
let self_layer_data = &mut self.layer_data;
|
||||
let entries = folder
|
||||
.as_folder()?
|
||||
.layers()
|
||||
.iter()
|
||||
.zip(folder.layer_ids.iter())
|
||||
.zip(folder.as_folder()?.layer_ids.iter())
|
||||
.rev()
|
||||
.map(|(layer, id)| {
|
||||
let path = [path, &[*id]].concat();
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
use crate::message_prelude::*;
|
||||
use crate::{
|
||||
input::{mouse::ViewportPosition, InputPreprocessor},
|
||||
message_prelude::*,
|
||||
};
|
||||
use document_core::{DocumentResponse, LayerId, Operation as DocumentOperation};
|
||||
use glam::{DAffine2, DVec2};
|
||||
use log::info;
|
||||
|
||||
use crate::document::Document;
|
||||
use std::collections::VecDeque;
|
||||
@@ -25,6 +30,9 @@ pub enum DocumentMessage {
|
||||
ExportDocument,
|
||||
RenderDocument,
|
||||
Undo,
|
||||
MouseMove,
|
||||
TranslateDown,
|
||||
TranslateUp,
|
||||
}
|
||||
|
||||
impl From<DocumentOperation> for DocumentMessage {
|
||||
@@ -42,6 +50,8 @@ impl From<DocumentOperation> for Message {
|
||||
pub struct DocumentMessageHandler {
|
||||
documents: Vec<Document>,
|
||||
active_document: usize,
|
||||
mmb_down: bool,
|
||||
mouse_pos: ViewportPosition,
|
||||
}
|
||||
|
||||
impl DocumentMessageHandler {
|
||||
@@ -78,12 +88,14 @@ impl Default for DocumentMessageHandler {
|
||||
Self {
|
||||
documents: vec![Document::default()],
|
||||
active_document: 0,
|
||||
mmb_down: false,
|
||||
mouse_pos: ViewportPosition::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageHandler<DocumentMessage, ()> for DocumentMessageHandler {
|
||||
fn process_action(&mut self, message: DocumentMessage, _data: (), responses: &mut VecDeque<Message>) {
|
||||
impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHandler {
|
||||
fn process_action(&mut self, message: DocumentMessage, ipp: &InputPreprocessor, responses: &mut VecDeque<Message>) {
|
||||
use DocumentMessage::*;
|
||||
match message {
|
||||
DeleteLayer(path) => responses.push_back(DocumentOperation::DeleteLayer { path }.into()),
|
||||
@@ -224,7 +236,7 @@ impl MessageHandler<DocumentMessage, ()> for DocumentMessageHandler {
|
||||
}
|
||||
Undo => {
|
||||
// this is a temporary fix and will be addressed by #123
|
||||
if let Some(id) = self.active_document().document.root.list_layers().last() {
|
||||
if let Some(id) = self.active_document().document.root.as_folder().unwrap().list_layers().last() {
|
||||
responses.push_back(DocumentOperation::DeleteLayer { path: vec![*id] }.into())
|
||||
}
|
||||
}
|
||||
@@ -259,14 +271,32 @@ impl MessageHandler<DocumentMessage, ()> for DocumentMessageHandler {
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
TranslateDown => {
|
||||
self.mmb_down = true;
|
||||
self.mouse_pos = ipp.mouse.position;
|
||||
}
|
||||
TranslateUp => {
|
||||
self.mmb_down = false;
|
||||
}
|
||||
MouseMove => {
|
||||
if self.mmb_down {
|
||||
let delta = DVec2::new(ipp.mouse.position.x as f64 - self.mouse_pos.x as f64, ipp.mouse.position.y as f64 - self.mouse_pos.y as f64);
|
||||
let operation = DocumentOperation::TransformLayer {
|
||||
path: vec![],
|
||||
transform: DAffine2::from_translation(delta).to_cols_array(),
|
||||
};
|
||||
responses.push_back(operation.into());
|
||||
self.mouse_pos = ipp.mouse.position;
|
||||
}
|
||||
}
|
||||
message => todo!("document_action_handler does not implement: {}", message.to_discriminant().global_name()),
|
||||
}
|
||||
}
|
||||
fn actions(&self) -> ActionList {
|
||||
if self.active_document().layer_data.values().any(|data| data.selected) {
|
||||
actions!(DocumentMessageDiscriminant; Undo, DeleteSelectedLayers, DuplicateSelectedLayers, RenderDocument, ExportDocument, NewDocument, CloseActiveDocument, NextDocument, PrevDocument)
|
||||
actions!(DocumentMessageDiscriminant; Undo, DeleteSelectedLayers, DuplicateSelectedLayers, RenderDocument, ExportDocument, NewDocument, CloseActiveDocument, NextDocument, PrevDocument, MouseMove, TranslateUp, TranslateDown)
|
||||
} else {
|
||||
actions!(DocumentMessageDiscriminant; Undo, RenderDocument, ExportDocument, NewDocument, CloseActiveDocument, NextDocument, PrevDocument)
|
||||
actions!(DocumentMessageDiscriminant; Undo, RenderDocument, ExportDocument, NewDocument, CloseActiveDocument, NextDocument, PrevDocument, MouseMove, TranslateUp, TranslateDown)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,6 @@ impl From<&LayerDataTypes> for LayerType {
|
||||
match data {
|
||||
Folder(_) => LayerType::Folder,
|
||||
Shape(_) => LayerType::Shape,
|
||||
Circle(_) => LayerType::Circle,
|
||||
Rect(_) => LayerType::Rect,
|
||||
Line(_) => LayerType::Line,
|
||||
PolyLine(_) => LayerType::PolyLine,
|
||||
|
||||
@@ -168,6 +168,9 @@ impl Default for Mapping {
|
||||
entry! {action=DocumentMessage::DeleteSelectedLayers, key_down=KeyBackspace},
|
||||
entry! {action=DocumentMessage::ExportDocument, key_down=KeyS, modifiers=[KeyControl, KeyShift]},
|
||||
entry! {action=DocumentMessage::ExportDocument, key_down=KeyE, modifiers=[KeyControl]},
|
||||
entry! {action=DocumentMessage::MouseMove, message=InputMapperMessage::PointerMove},
|
||||
entry! {action=DocumentMessage::TranslateDown, key_down=Mmb},
|
||||
entry! {action=DocumentMessage::TranslateUp, key_up=Mmb},
|
||||
entry! {action=DocumentMessage::NewDocument, key_down=KeyN, modifiers=[KeyShift]},
|
||||
entry! {action=DocumentMessage::NextDocument, key_down=KeyTab, modifiers=[KeyShift]},
|
||||
entry! {action=DocumentMessage::CloseActiveDocument, key_down=KeyW, modifiers=[KeyShift]},
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::input::{mouse::ViewportPosition, InputPreprocessor};
|
||||
use crate::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
|
||||
use crate::{message_prelude::*, SvgDocument};
|
||||
use document_core::{layers::style, Operation};
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Ellipse {
|
||||
@@ -58,7 +59,8 @@ struct EllipseToolData {
|
||||
impl Fsm for EllipseToolFsmState {
|
||||
type ToolData = EllipseToolData;
|
||||
|
||||
fn transition(self, event: ToolMessage, _document: &SvgDocument, tool_data: &DocumentToolData, data: &mut Self::ToolData, input: &InputPreprocessor, responses: &mut VecDeque<Message>) -> Self {
|
||||
fn transition(self, event: ToolMessage, document: &SvgDocument, tool_data: &DocumentToolData, data: &mut Self::ToolData, input: &InputPreprocessor, responses: &mut VecDeque<Message>) -> Self {
|
||||
let transform = document.root.transform;
|
||||
use EllipseMessage::*;
|
||||
use EllipseToolFsmState::*;
|
||||
if let ToolMessage::Ellipse(event) = event {
|
||||
@@ -73,7 +75,7 @@ impl Fsm for EllipseToolFsmState {
|
||||
data.drag_current = input.mouse.position;
|
||||
|
||||
responses.push_back(Operation::ClearWorkingFolder.into());
|
||||
responses.push_back(make_operation(data, tool_data));
|
||||
responses.push_back(make_operation(data, tool_data, transform));
|
||||
|
||||
Dragging
|
||||
}
|
||||
@@ -83,7 +85,7 @@ impl Fsm for EllipseToolFsmState {
|
||||
responses.push_back(Operation::ClearWorkingFolder.into());
|
||||
// TODO - introduce comparison threshold when operating with canvas coordinates (https://github.com/GraphiteEditor/Graphite/issues/100)
|
||||
if data.drag_start != data.drag_current {
|
||||
responses.push_back(make_operation(data, tool_data));
|
||||
responses.push_back(make_operation(data, tool_data, transform));
|
||||
responses.push_back(Operation::CommitTransaction.into());
|
||||
}
|
||||
|
||||
@@ -97,13 +99,13 @@ impl Fsm for EllipseToolFsmState {
|
||||
}
|
||||
(Ready, LockAspectRatio) => update_state_no_op(&mut data.constrain_to_circle, true, Ready),
|
||||
(Ready, UnlockAspectRatio) => update_state_no_op(&mut data.constrain_to_circle, false, Ready),
|
||||
(Dragging, LockAspectRatio) => update_state(|data| &mut data.constrain_to_circle, true, tool_data, data, responses, Dragging),
|
||||
(Dragging, UnlockAspectRatio) => update_state(|data| &mut data.constrain_to_circle, false, tool_data, data, responses, Dragging),
|
||||
(Dragging, LockAspectRatio) => update_state(|data| &mut data.constrain_to_circle, true, tool_data, data, responses, Dragging, transform),
|
||||
(Dragging, UnlockAspectRatio) => update_state(|data| &mut data.constrain_to_circle, false, tool_data, data, responses, Dragging, transform),
|
||||
|
||||
(Ready, Center) => update_state_no_op(&mut data.center_around_cursor, true, Ready),
|
||||
(Ready, UnCenter) => update_state_no_op(&mut data.center_around_cursor, false, Ready),
|
||||
(Dragging, Center) => update_state(|data| &mut data.center_around_cursor, true, tool_data, data, responses, Dragging),
|
||||
(Dragging, UnCenter) => update_state(|data| &mut data.center_around_cursor, false, tool_data, data, responses, Dragging),
|
||||
(Dragging, Center) => update_state(|data| &mut data.center_around_cursor, true, tool_data, data, responses, Dragging, transform),
|
||||
(Dragging, UnCenter) => update_state(|data| &mut data.center_around_cursor, false, tool_data, data, responses, Dragging, transform),
|
||||
_ => self,
|
||||
}
|
||||
} else {
|
||||
@@ -124,16 +126,17 @@ fn update_state(
|
||||
data: &mut EllipseToolData,
|
||||
responses: &mut VecDeque<Message>,
|
||||
new_state: EllipseToolFsmState,
|
||||
transform: DAffine2,
|
||||
) -> EllipseToolFsmState {
|
||||
*(state(data)) = value;
|
||||
|
||||
responses.push_back(Operation::ClearWorkingFolder.into());
|
||||
responses.push_back(make_operation(&data, tool_data));
|
||||
responses.push_back(make_operation(&data, tool_data, transform));
|
||||
|
||||
new_state
|
||||
}
|
||||
|
||||
fn make_operation(data: &EllipseToolData, tool_data: &DocumentToolData) -> Message {
|
||||
fn make_operation(data: &EllipseToolData, tool_data: &DocumentToolData, transform: DAffine2) -> Message {
|
||||
let x0 = data.drag_start.x as f64;
|
||||
let y0 = data.drag_start.y as f64;
|
||||
let x1 = data.drag_current.x as f64;
|
||||
@@ -147,12 +150,10 @@ fn make_operation(data: &EllipseToolData, tool_data: &DocumentToolData) -> Messa
|
||||
let (x2, y2) = (x0 + (x1 - x0).signum() * diameter, y0 + (y1 - y0).signum() * diameter);
|
||||
((x0 + x2) * 0.5, (y0 + y2) * 0.5, diameter * 0.5)
|
||||
};
|
||||
Operation::AddCircle {
|
||||
Operation::AddEllipse {
|
||||
path: vec![],
|
||||
insert_index: -1,
|
||||
cx,
|
||||
cy,
|
||||
r,
|
||||
transform: (transform.inverse() * glam::DAffine2::from_scale_angle_translation(DVec2::new(r, r), 0., DVec2::new(cx, cy))).to_cols_array(),
|
||||
style: style::PathStyle::new(None, Some(style::Fill::new(tool_data.primary_color))),
|
||||
}
|
||||
} else {
|
||||
@@ -161,11 +162,7 @@ fn make_operation(data: &EllipseToolData, tool_data: &DocumentToolData) -> Messa
|
||||
Operation::AddEllipse {
|
||||
path: vec![],
|
||||
insert_index: -1,
|
||||
cx,
|
||||
cy,
|
||||
rx,
|
||||
ry,
|
||||
rot: 0.0,
|
||||
transform: (transform.inverse() * glam::DAffine2::from_scale_angle_translation(DVec2::new(rx, ry), 0., DVec2::new(cx, cy))).to_cols_array(),
|
||||
style: style::PathStyle::new(None, Some(style::Fill::new(tool_data.primary_color))),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::input::{mouse::ViewportPosition, InputPreprocessor};
|
||||
use crate::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
|
||||
use crate::{message_prelude::*, SvgDocument};
|
||||
use document_core::{layers::style, Operation};
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
use std::f64::consts::PI;
|
||||
|
||||
@@ -63,7 +64,8 @@ struct LineToolData {
|
||||
impl Fsm for LineToolFsmState {
|
||||
type ToolData = LineToolData;
|
||||
|
||||
fn transition(self, event: ToolMessage, _document: &SvgDocument, tool_data: &DocumentToolData, data: &mut Self::ToolData, input: &InputPreprocessor, responses: &mut VecDeque<Message>) -> Self {
|
||||
fn transition(self, event: ToolMessage, document: &SvgDocument, tool_data: &DocumentToolData, data: &mut Self::ToolData, input: &InputPreprocessor, responses: &mut VecDeque<Message>) -> Self {
|
||||
let transform = document.root.transform;
|
||||
use LineMessage::*;
|
||||
use LineToolFsmState::*;
|
||||
if let ToolMessage::Line(event) = event {
|
||||
@@ -80,7 +82,7 @@ impl Fsm for LineToolFsmState {
|
||||
data.drag_current = input.mouse.position;
|
||||
|
||||
responses.push_back(Operation::ClearWorkingFolder.into());
|
||||
responses.push_back(make_operation(data, tool_data));
|
||||
responses.push_back(make_operation(data, tool_data, transform));
|
||||
|
||||
Dragging
|
||||
}
|
||||
@@ -90,7 +92,7 @@ impl Fsm for LineToolFsmState {
|
||||
responses.push_back(Operation::ClearWorkingFolder.into());
|
||||
// TODO - introduce comparison threshold when operating with canvas coordinates (https://github.com/GraphiteEditor/Graphite/issues/100)
|
||||
if data.drag_start != data.drag_current {
|
||||
responses.push_back(make_operation(data, tool_data));
|
||||
responses.push_back(make_operation(data, tool_data, transform));
|
||||
responses.push_back(Operation::CommitTransaction.into());
|
||||
}
|
||||
|
||||
@@ -104,18 +106,18 @@ impl Fsm for LineToolFsmState {
|
||||
}
|
||||
(Ready, LockAngle) => update_state_no_op(&mut data.lock_angle, true, Ready),
|
||||
(Ready, UnlockAngle) => update_state_no_op(&mut data.lock_angle, false, Ready),
|
||||
(Dragging, LockAngle) => update_state(|data| &mut data.lock_angle, true, tool_data, data, responses, Dragging),
|
||||
(Dragging, UnlockAngle) => update_state(|data| &mut data.lock_angle, false, tool_data, data, responses, Dragging),
|
||||
(Dragging, LockAngle) => update_state(|data| &mut data.lock_angle, true, tool_data, data, responses, Dragging, transform),
|
||||
(Dragging, UnlockAngle) => update_state(|data| &mut data.lock_angle, false, tool_data, data, responses, Dragging, transform),
|
||||
|
||||
(Ready, SnapToAngle) => update_state_no_op(&mut data.snap_angle, true, Ready),
|
||||
(Ready, UnSnapToAngle) => update_state_no_op(&mut data.snap_angle, false, Ready),
|
||||
(Dragging, SnapToAngle) => update_state(|data| &mut data.snap_angle, true, tool_data, data, responses, Dragging),
|
||||
(Dragging, UnSnapToAngle) => update_state(|data| &mut data.snap_angle, false, tool_data, data, responses, Dragging),
|
||||
(Dragging, SnapToAngle) => update_state(|data| &mut data.snap_angle, true, tool_data, data, responses, Dragging, transform),
|
||||
(Dragging, UnSnapToAngle) => update_state(|data| &mut data.snap_angle, false, tool_data, data, responses, Dragging, transform),
|
||||
|
||||
(Ready, Center) => update_state_no_op(&mut data.center_around_cursor, true, Ready),
|
||||
(Ready, UnCenter) => update_state_no_op(&mut data.center_around_cursor, false, Ready),
|
||||
(Dragging, Center) => update_state(|data| &mut data.center_around_cursor, true, tool_data, data, responses, Dragging),
|
||||
(Dragging, UnCenter) => update_state(|data| &mut data.center_around_cursor, false, tool_data, data, responses, Dragging),
|
||||
(Dragging, Center) => update_state(|data| &mut data.center_around_cursor, true, tool_data, data, responses, Dragging, transform),
|
||||
(Dragging, UnCenter) => update_state(|data| &mut data.center_around_cursor, false, tool_data, data, responses, Dragging, transform),
|
||||
_ => self,
|
||||
}
|
||||
} else {
|
||||
@@ -136,16 +138,17 @@ fn update_state(
|
||||
data: &mut LineToolData,
|
||||
responses: &mut VecDeque<Message>,
|
||||
new_state: LineToolFsmState,
|
||||
transform: DAffine2,
|
||||
) -> LineToolFsmState {
|
||||
*(state(data)) = value;
|
||||
|
||||
responses.push_back(Operation::ClearWorkingFolder.into());
|
||||
responses.push_back(make_operation(data, tool_data));
|
||||
responses.push_back(make_operation(data, tool_data, transform));
|
||||
|
||||
new_state
|
||||
}
|
||||
|
||||
fn make_operation(data: &mut LineToolData, tool_data: &DocumentToolData) -> Message {
|
||||
fn make_operation(data: &mut LineToolData, tool_data: &DocumentToolData, transform: DAffine2) -> Message {
|
||||
let x0 = data.drag_start.x as f64;
|
||||
let y0 = data.drag_start.y as f64;
|
||||
let x1 = data.drag_current.x as f64;
|
||||
@@ -174,10 +177,7 @@ fn make_operation(data: &mut LineToolData, tool_data: &DocumentToolData) -> Mess
|
||||
Operation::AddLine {
|
||||
path: vec![],
|
||||
insert_index: -1,
|
||||
x0,
|
||||
y0,
|
||||
x1,
|
||||
y1,
|
||||
transform: (transform.inverse() * glam::DAffine2::from_scale_angle_translation(DVec2::new(x1 - x0, y1 - y0), 0., DVec2::new(x0, y0))).to_cols_array(),
|
||||
style: style::PathStyle::new(Some(style::Stroke::new(tool_data.primary_color, 5.)), None),
|
||||
}
|
||||
.into()
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use crate::input::{mouse::ViewportPosition, InputPreprocessor};
|
||||
use crate::input::InputPreprocessor;
|
||||
use crate::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
|
||||
use crate::{message_prelude::*, SvgDocument};
|
||||
use document_core::{layers::style, Operation};
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Pen {
|
||||
@@ -46,14 +47,17 @@ impl Default for PenToolFsmState {
|
||||
}
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct PenToolData {
|
||||
points: Vec<ViewportPosition>,
|
||||
next_point: ViewportPosition,
|
||||
points: Vec<DAffine2>,
|
||||
next_point: DAffine2,
|
||||
}
|
||||
|
||||
impl Fsm for PenToolFsmState {
|
||||
type ToolData = PenToolData;
|
||||
|
||||
fn transition(self, event: ToolMessage, _document: &SvgDocument, tool_data: &DocumentToolData, data: &mut Self::ToolData, input: &InputPreprocessor, responses: &mut VecDeque<Message>) -> Self {
|
||||
fn transition(self, event: ToolMessage, document: &SvgDocument, tool_data: &DocumentToolData, data: &mut Self::ToolData, input: &InputPreprocessor, responses: &mut VecDeque<Message>) -> Self {
|
||||
let transform = document.root.transform;
|
||||
let pos = transform.inverse() * DAffine2::from_translation(DVec2::new(input.mouse.position.x as f64, input.mouse.position.y as f64));
|
||||
|
||||
use PenMessage::*;
|
||||
use PenToolFsmState::*;
|
||||
if let ToolMessage::Pen(event) = event {
|
||||
@@ -61,16 +65,16 @@ impl Fsm for PenToolFsmState {
|
||||
(Ready, DragStart) => {
|
||||
responses.push_back(Operation::MountWorkingFolder { path: vec![] }.into());
|
||||
|
||||
data.points.push(input.mouse.position);
|
||||
data.next_point = input.mouse.position;
|
||||
data.points.push(pos);
|
||||
data.next_point = pos;
|
||||
|
||||
Dragging
|
||||
}
|
||||
(Dragging, DragStop) => {
|
||||
// TODO - introduce comparison threshold when operating with canvas coordinates (https://github.com/GraphiteEditor/Graphite/issues/100)
|
||||
if data.points.last() != Some(&input.mouse.position) {
|
||||
data.points.push(input.mouse.position);
|
||||
data.next_point = input.mouse.position;
|
||||
if data.points.last() != Some(&pos) {
|
||||
data.points.push(pos);
|
||||
data.next_point = pos;
|
||||
}
|
||||
|
||||
responses.push_back(Operation::ClearWorkingFolder.into());
|
||||
@@ -79,7 +83,7 @@ impl Fsm for PenToolFsmState {
|
||||
Dragging
|
||||
}
|
||||
(Dragging, MouseMove) => {
|
||||
data.next_point = input.mouse.position;
|
||||
data.next_point = pos;
|
||||
|
||||
responses.push_back(Operation::ClearWorkingFolder.into());
|
||||
responses.push_back(make_operation(data, tool_data, true));
|
||||
@@ -116,13 +120,14 @@ impl Fsm for PenToolFsmState {
|
||||
}
|
||||
|
||||
fn make_operation(data: &PenToolData, tool_data: &DocumentToolData, show_preview: bool) -> Message {
|
||||
let mut points: Vec<(f64, f64)> = data.points.iter().map(|p| (p.x as f64, p.y as f64)).collect();
|
||||
let mut points: Vec<(f64, f64)> = data.points.iter().map(|p| (p.translation.x, p.translation.y)).collect();
|
||||
if show_preview {
|
||||
points.push((data.next_point.x as f64, data.next_point.y as f64))
|
||||
points.push((data.next_point.translation.x, data.next_point.translation.y))
|
||||
}
|
||||
Operation::AddPen {
|
||||
path: vec![],
|
||||
insert_index: -1,
|
||||
transform: DAffine2::IDENTITY.to_cols_array(),
|
||||
points,
|
||||
style: style::PathStyle::new(Some(style::Stroke::new(tool_data.primary_color, 5.)), Some(style::Fill::none())),
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::input::{mouse::ViewportPosition, InputPreprocessor};
|
||||
use crate::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
|
||||
use crate::{message_prelude::*, SvgDocument};
|
||||
use document_core::{layers::style, Operation};
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Rectangle {
|
||||
@@ -57,7 +58,8 @@ struct RectangleToolData {
|
||||
impl Fsm for RectangleToolFsmState {
|
||||
type ToolData = RectangleToolData;
|
||||
|
||||
fn transition(self, event: ToolMessage, _document: &SvgDocument, tool_data: &DocumentToolData, data: &mut Self::ToolData, input: &InputPreprocessor, responses: &mut VecDeque<Message>) -> Self {
|
||||
fn transition(self, event: ToolMessage, document: &SvgDocument, tool_data: &DocumentToolData, data: &mut Self::ToolData, input: &InputPreprocessor, responses: &mut VecDeque<Message>) -> Self {
|
||||
let transform = document.root.transform;
|
||||
use RectangleMessage::*;
|
||||
use RectangleToolFsmState::*;
|
||||
if let ToolMessage::Rectangle(event) = event {
|
||||
@@ -72,7 +74,7 @@ impl Fsm for RectangleToolFsmState {
|
||||
data.drag_current = input.mouse.position;
|
||||
|
||||
responses.push_back(Operation::ClearWorkingFolder.into());
|
||||
responses.push_back(make_operation(data, tool_data));
|
||||
responses.push_back(make_operation(data, tool_data, transform));
|
||||
|
||||
Dragging
|
||||
}
|
||||
@@ -82,7 +84,7 @@ impl Fsm for RectangleToolFsmState {
|
||||
responses.push_back(Operation::ClearWorkingFolder.into());
|
||||
// TODO - introduce comparison threshold when operating with canvas coordinates (https://github.com/GraphiteEditor/Graphite/issues/100)
|
||||
if data.drag_start != data.drag_current {
|
||||
responses.push_back(make_operation(data, tool_data));
|
||||
responses.push_back(make_operation(data, tool_data, transform));
|
||||
responses.push_back(Operation::CommitTransaction.into());
|
||||
}
|
||||
|
||||
@@ -96,13 +98,13 @@ impl Fsm for RectangleToolFsmState {
|
||||
}
|
||||
(Ready, LockAspectRatio) => update_state_no_op(&mut data.constrain_to_square, true, Ready),
|
||||
(Ready, UnlockAspectRatio) => update_state_no_op(&mut data.constrain_to_square, false, Ready),
|
||||
(Dragging, LockAspectRatio) => update_state(|data| &mut data.constrain_to_square, true, tool_data, data, responses, Dragging),
|
||||
(Dragging, UnlockAspectRatio) => update_state(|data| &mut data.constrain_to_square, false, tool_data, data, responses, Dragging),
|
||||
(Dragging, LockAspectRatio) => update_state(|data| &mut data.constrain_to_square, true, tool_data, data, responses, Dragging, transform),
|
||||
(Dragging, UnlockAspectRatio) => update_state(|data| &mut data.constrain_to_square, false, tool_data, data, responses, Dragging, transform),
|
||||
|
||||
(Ready, Center) => update_state_no_op(&mut data.center_around_cursor, true, Ready),
|
||||
(Ready, UnCenter) => update_state_no_op(&mut data.center_around_cursor, false, Ready),
|
||||
(Dragging, Center) => update_state(|data| &mut data.center_around_cursor, true, tool_data, data, responses, Dragging),
|
||||
(Dragging, UnCenter) => update_state(|data| &mut data.center_around_cursor, false, tool_data, data, responses, Dragging),
|
||||
(Dragging, Center) => update_state(|data| &mut data.center_around_cursor, true, tool_data, data, responses, Dragging, transform),
|
||||
(Dragging, UnCenter) => update_state(|data| &mut data.center_around_cursor, false, tool_data, data, responses, Dragging, transform),
|
||||
_ => self,
|
||||
}
|
||||
} else {
|
||||
@@ -123,16 +125,17 @@ fn update_state(
|
||||
data: &mut RectangleToolData,
|
||||
responses: &mut VecDeque<Message>,
|
||||
new_state: RectangleToolFsmState,
|
||||
transform: DAffine2,
|
||||
) -> RectangleToolFsmState {
|
||||
*(state(data)) = value;
|
||||
|
||||
responses.push_back(Operation::ClearWorkingFolder.into());
|
||||
responses.push_back(make_operation(data, tool_data));
|
||||
responses.push_back(make_operation(data, tool_data, transform));
|
||||
|
||||
new_state
|
||||
}
|
||||
|
||||
fn make_operation(data: &RectangleToolData, tool_data: &DocumentToolData) -> Message {
|
||||
fn make_operation(data: &RectangleToolData, tool_data: &DocumentToolData, transform: DAffine2) -> Message {
|
||||
let x0 = data.drag_start.x as f64;
|
||||
let y0 = data.drag_start.y as f64;
|
||||
let x1 = data.drag_current.x as f64;
|
||||
@@ -161,10 +164,7 @@ fn make_operation(data: &RectangleToolData, tool_data: &DocumentToolData) -> Mes
|
||||
Operation::AddRect {
|
||||
path: vec![],
|
||||
insert_index: -1,
|
||||
x0,
|
||||
y0,
|
||||
x1,
|
||||
y1,
|
||||
transform: (transform.inverse() * glam::DAffine2::from_scale_angle_translation(DVec2::new(x1 - x0, y1 - y0), 0., DVec2::new(x0, y0))).to_cols_array(),
|
||||
style: style::PathStyle::new(None, Some(style::Fill::new(tool_data.primary_color))),
|
||||
}
|
||||
.into()
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::input::{mouse::ViewportPosition, InputPreprocessor};
|
||||
use crate::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
|
||||
use crate::{message_prelude::*, SvgDocument};
|
||||
use document_core::{layers::style, Operation};
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Shape {
|
||||
@@ -59,7 +60,8 @@ struct ShapeToolData {
|
||||
impl Fsm for ShapeToolFsmState {
|
||||
type ToolData = ShapeToolData;
|
||||
|
||||
fn transition(self, event: ToolMessage, _document: &SvgDocument, tool_data: &DocumentToolData, data: &mut Self::ToolData, input: &InputPreprocessor, responses: &mut VecDeque<Message>) -> Self {
|
||||
fn transition(self, event: ToolMessage, document: &SvgDocument, tool_data: &DocumentToolData, data: &mut Self::ToolData, input: &InputPreprocessor, responses: &mut VecDeque<Message>) -> Self {
|
||||
let transform = document.root.transform;
|
||||
use ShapeMessage::*;
|
||||
use ShapeToolFsmState::*;
|
||||
if let ToolMessage::Shape(event) = event {
|
||||
@@ -76,7 +78,7 @@ impl Fsm for ShapeToolFsmState {
|
||||
(Dragging, MouseMove) => {
|
||||
data.drag_current = input.mouse.position;
|
||||
responses.push_back(Operation::ClearWorkingFolder.into());
|
||||
responses.push_back(make_operation(data, tool_data));
|
||||
responses.push_back(make_operation(data, tool_data, transform));
|
||||
|
||||
Dragging
|
||||
}
|
||||
@@ -85,7 +87,7 @@ impl Fsm for ShapeToolFsmState {
|
||||
responses.push_back(Operation::ClearWorkingFolder.into());
|
||||
// TODO - introduce comparison threshold when operating with canvas coordinates (https://github.com/GraphiteEditor/Graphite/issues/100)
|
||||
if data.drag_start != data.drag_current {
|
||||
responses.push_back(make_operation(data, tool_data));
|
||||
responses.push_back(make_operation(data, tool_data, transform));
|
||||
responses.push_back(Operation::CommitTransaction.into());
|
||||
}
|
||||
|
||||
@@ -99,13 +101,13 @@ impl Fsm for ShapeToolFsmState {
|
||||
|
||||
(Ready, LockAspectRatio) => update_state_no_op(&mut data.constrain_to_square, true, Ready),
|
||||
(Ready, UnlockAspectRatio) => update_state_no_op(&mut data.constrain_to_square, false, Ready),
|
||||
(Dragging, LockAspectRatio) => update_state(|data| &mut data.constrain_to_square, true, tool_data, data, responses, Dragging),
|
||||
(Dragging, UnlockAspectRatio) => update_state(|data| &mut data.constrain_to_square, false, tool_data, data, responses, Dragging),
|
||||
(Dragging, LockAspectRatio) => update_state(|data| &mut data.constrain_to_square, true, tool_data, data, responses, Dragging, transform),
|
||||
(Dragging, UnlockAspectRatio) => update_state(|data| &mut data.constrain_to_square, false, tool_data, data, responses, Dragging, transform),
|
||||
|
||||
(Ready, Center) => update_state_no_op(&mut data.center_around_cursor, true, Ready),
|
||||
(Ready, UnCenter) => update_state_no_op(&mut data.center_around_cursor, false, Ready),
|
||||
(Dragging, Center) => update_state(|data| &mut data.center_around_cursor, true, tool_data, data, responses, Dragging),
|
||||
(Dragging, UnCenter) => update_state(|data| &mut data.center_around_cursor, false, tool_data, data, responses, Dragging),
|
||||
(Dragging, Center) => update_state(|data| &mut data.center_around_cursor, true, tool_data, data, responses, Dragging, transform),
|
||||
(Dragging, UnCenter) => update_state(|data| &mut data.center_around_cursor, false, tool_data, data, responses, Dragging, transform),
|
||||
_ => self,
|
||||
}
|
||||
} else {
|
||||
@@ -126,28 +128,30 @@ fn update_state(
|
||||
data: &mut ShapeToolData,
|
||||
responses: &mut VecDeque<Message>,
|
||||
new_state: ShapeToolFsmState,
|
||||
transform: DAffine2,
|
||||
) -> ShapeToolFsmState {
|
||||
*(state(data)) = value;
|
||||
|
||||
responses.push_back(Operation::ClearWorkingFolder.into());
|
||||
responses.push_back(make_operation(data, tool_data));
|
||||
responses.push_back(make_operation(data, tool_data, transform));
|
||||
|
||||
new_state
|
||||
}
|
||||
|
||||
fn make_operation(data: &ShapeToolData, tool_data: &DocumentToolData) -> Message {
|
||||
fn make_operation(data: &ShapeToolData, tool_data: &DocumentToolData, transform: DAffine2) -> Message {
|
||||
let x0 = data.drag_start.x as f64;
|
||||
let y0 = data.drag_start.y as f64;
|
||||
let x1 = data.drag_current.x as f64;
|
||||
let y1 = data.drag_current.y as f64;
|
||||
|
||||
let (x0, y0, x1, y1) = if data.constrain_to_square {
|
||||
// TODO: Use regular polygon's aspect ration for constraining rather than a square.
|
||||
let (x0, y0, x1, y1, equal_sides) = if data.constrain_to_square {
|
||||
let (x_dir, y_dir) = ((x1 - x0).signum(), (y1 - y0).signum());
|
||||
let max_dist = f64::max((x1 - x0).abs(), (y1 - y0).abs());
|
||||
if data.center_around_cursor {
|
||||
(x0 - max_dist * x_dir, y0 - max_dist * y_dir, x0 + max_dist * x_dir, y0 + max_dist * y_dir)
|
||||
(x0 - max_dist * x_dir, y0 - max_dist * y_dir, x0 + max_dist * x_dir, y0 + max_dist * y_dir, true)
|
||||
} else {
|
||||
(x0, y0, x0 + max_dist * x_dir, y0 + max_dist * y_dir)
|
||||
(x0, y0, x0 + max_dist * x_dir, y0 + max_dist * y_dir, true)
|
||||
}
|
||||
} else {
|
||||
let (x0, y0) = if data.center_around_cursor {
|
||||
@@ -158,16 +162,14 @@ fn make_operation(data: &ShapeToolData, tool_data: &DocumentToolData) -> Message
|
||||
} else {
|
||||
(x0, y0)
|
||||
};
|
||||
(x0, y0, x1, y1)
|
||||
(x0, y0, x1, y1, false)
|
||||
};
|
||||
|
||||
Operation::AddShape {
|
||||
path: vec![],
|
||||
insert_index: -1,
|
||||
x0,
|
||||
y0,
|
||||
x1,
|
||||
y1,
|
||||
transform: (transform.inverse() * glam::DAffine2::from_scale_angle_translation(DVec2::new(x1 - x0, y1 - y0), 0., DVec2::new(x0, y0))).to_cols_array(),
|
||||
equal_sides,
|
||||
sides: data.sides,
|
||||
style: style::PathStyle::new(None, Some(style::Fill::new(tool_data.primary_color))),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user