Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shape tools refactor #2454

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 10 additions & 23 deletions editor/src/messages/input_mapper/input_mappings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,12 @@ pub fn input_mappings() -> Mapping {
entry!(KeyDown(MouseRight); action_dispatch=GradientToolMessage::Abort),
entry!(KeyDown(Escape); action_dispatch=GradientToolMessage::Abort),
//
// RectangleToolMessage
entry!(KeyDown(MouseLeft); action_dispatch=RectangleToolMessage::DragStart),
entry!(KeyUp(MouseLeft); action_dispatch=RectangleToolMessage::DragStop),
entry!(KeyDown(MouseRight); action_dispatch=RectangleToolMessage::Abort),
entry!(KeyDown(Escape); action_dispatch=RectangleToolMessage::Abort),
entry!(PointerMove; refresh_keys=[Alt, Shift], action_dispatch=RectangleToolMessage::PointerMove { center: Alt, lock_ratio: Shift }),
// ShapeToolMessage
entry!(KeyDown(MouseLeft); action_dispatch=ShapeToolMessage::DragStart),
entry!(KeyUp(MouseLeft); action_dispatch=ShapeToolMessage::DragStop),
entry!(KeyDown(MouseRight); action_dispatch=ShapeToolMessage::Abort),
entry!(KeyDown(Escape); action_dispatch=ShapeToolMessage::Abort),
entry!(PointerMove; refresh_keys=[Alt, Shift, Control], action_dispatch=ShapeToolMessage::PointerMove ([Alt, Shift, Control, Shift])),
//
// ImaginateToolMessage
entry!(KeyDown(MouseLeft); action_dispatch=ImaginateToolMessage::DragStart),
Expand All @@ -184,27 +184,13 @@ pub fn input_mappings() -> Mapping {
entry!(KeyDown(Escape); action_dispatch=ImaginateToolMessage::Abort),
entry!(PointerMove; refresh_keys=[Alt, Shift], action_dispatch=ImaginateToolMessage::Resize { center: Alt, lock_ratio: Shift }),
//
// EllipseToolMessage
entry!(KeyDown(MouseLeft); action_dispatch=EllipseToolMessage::DragStart),
entry!(KeyUp(MouseLeft); action_dispatch=EllipseToolMessage::DragStop),
entry!(KeyDown(MouseRight); action_dispatch=EllipseToolMessage::Abort),
entry!(KeyDown(Escape); action_dispatch=EllipseToolMessage::Abort),
entry!(PointerMove; refresh_keys=[Alt, Shift], action_dispatch=EllipseToolMessage::PointerMove { center: Alt, lock_ratio: Shift }),
//
// PolygonToolMessage
entry!(KeyDown(MouseLeft); action_dispatch=PolygonToolMessage::DragStart),
entry!(KeyUp(MouseLeft); action_dispatch=PolygonToolMessage::DragStop),
entry!(KeyDown(MouseRight); action_dispatch=PolygonToolMessage::Abort),
entry!(KeyDown(Escape); action_dispatch=PolygonToolMessage::Abort),
entry!(PointerMove; refresh_keys=[Alt, Shift], action_dispatch=PolygonToolMessage::PointerMove { center: Alt, lock_ratio: Shift }),
//
// LineToolMessage
entry!(KeyDown(MouseLeft); action_dispatch=LineToolMessage::DragStart),
entry!(KeyUp(MouseLeft); action_dispatch=LineToolMessage::DragStop),
entry!(KeyDown(MouseRight); action_dispatch=LineToolMessage::Abort),
entry!(KeyDown(Escape); action_dispatch=LineToolMessage::Abort),
entry!(PointerMove; refresh_keys=[Control, Alt, Shift], action_dispatch=LineToolMessage::PointerMove { center: Alt, lock_angle: Control, snap_angle: Shift }),
//
// PathToolMessage
entry!(KeyDown(Delete); modifiers=[Accel], action_dispatch=PathToolMessage::DeleteAndBreakPath),
entry!(KeyDown(Backspace); modifiers=[Accel], action_dispatch=PathToolMessage::DeleteAndBreakPath),
Expand Down Expand Up @@ -305,9 +291,10 @@ pub fn input_mappings() -> Mapping {
entry!(KeyDown(KeyA); action_dispatch=ToolMessage::ActivateToolPath),
entry!(KeyDown(KeyP); action_dispatch=ToolMessage::ActivateToolPen),
entry!(KeyDown(KeyN); action_dispatch=ToolMessage::ActivateToolFreehand),
entry!(KeyDown(KeyL); action_dispatch=ToolMessage::ActivateToolLine),
entry!(KeyDown(KeyM); action_dispatch=ToolMessage::ActivateToolRectangle),
entry!(KeyDown(KeyE); action_dispatch=ToolMessage::ActivateToolEllipse),
entry!(KeyDown(KeyU); action_dispatch=ToolMessage::ActivateToolShape),
entry!(KeyDown(KeyM); action_dispatch=ToolMessage::ActivateShapeRectangle),
entry!(KeyDown(KeyL); action_dispatch=ToolMessage::ActivateShapeLine),
entry!(KeyDown(KeyE); action_dispatch=ToolMessage::ActivateShapeEllipse),
entry!(KeyDown(KeyY); action_dispatch=ToolMessage::ActivateToolPolygon),
entry!(KeyDown(KeyB); action_dispatch=ToolMessage::ActivateToolBrush),
entry!(KeyDown(KeyX); modifiers=[Accel, Shift], action_dispatch=ToolMessage::ResetColors),
Expand Down
4 changes: 1 addition & 3 deletions editor/src/messages/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,17 @@ pub use crate::messages::broadcast::broadcast_event::{BroadcastEvent, BroadcastE
pub use crate::messages::message::{Message, MessageDiscriminant};
pub use crate::messages::tool::tool_messages::artboard_tool::{ArtboardToolMessage, ArtboardToolMessageDiscriminant};
pub use crate::messages::tool::tool_messages::brush_tool::{BrushToolMessage, BrushToolMessageDiscriminant};
pub use crate::messages::tool::tool_messages::ellipse_tool::{EllipseToolMessage, EllipseToolMessageDiscriminant};
pub use crate::messages::tool::tool_messages::eyedropper_tool::{EyedropperToolMessage, EyedropperToolMessageDiscriminant};
pub use crate::messages::tool::tool_messages::fill_tool::{FillToolMessage, FillToolMessageDiscriminant};
pub use crate::messages::tool::tool_messages::freehand_tool::{FreehandToolMessage, FreehandToolMessageDiscriminant};
pub use crate::messages::tool::tool_messages::gradient_tool::{GradientToolMessage, GradientToolMessageDiscriminant};
pub use crate::messages::tool::tool_messages::imaginate_tool::{ImaginateToolMessage, ImaginateToolMessageDiscriminant};
pub use crate::messages::tool::tool_messages::line_tool::{LineToolMessage, LineToolMessageDiscriminant};
pub use crate::messages::tool::tool_messages::navigate_tool::{NavigateToolMessage, NavigateToolMessageDiscriminant};
pub use crate::messages::tool::tool_messages::path_tool::{PathToolMessage, PathToolMessageDiscriminant};
pub use crate::messages::tool::tool_messages::pen_tool::{PenToolMessage, PenToolMessageDiscriminant};
pub use crate::messages::tool::tool_messages::polygon_tool::{PolygonToolMessage, PolygonToolMessageDiscriminant};
pub use crate::messages::tool::tool_messages::rectangle_tool::{RectangleToolMessage, RectangleToolMessageDiscriminant};
pub use crate::messages::tool::tool_messages::select_tool::{SelectToolMessage, SelectToolMessageDiscriminant};
pub use crate::messages::tool::tool_messages::shape_tool::{ShapeToolMessage, ShapeToolMessageDiscriminant};
pub use crate::messages::tool::tool_messages::spline_tool::{SplineToolMessage, SplineToolMessageDiscriminant};
pub use crate::messages::tool::tool_messages::text_tool::{TextToolMessage, TextToolMessageDiscriminant};

Expand Down
2 changes: 1 addition & 1 deletion editor/src/messages/tool/common_functionality/resize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use glam::{DAffine2, DVec2, Vec2Swizzles};
#[derive(Clone, Debug, Default)]
pub struct Resize {
/// Stored as a document position so the start doesn't move if the canvas is panned.
drag_start: DVec2,
pub drag_start: DVec2,
pub layer: Option<LayerNodeIdentifier>,
pub snap_manager: SnapManager,
}
Expand Down
1 change: 1 addition & 0 deletions editor/src/messages/tool/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod tool_message;
mod tool_message_handler;

pub mod common_functionality;
pub mod shapes;
pub mod tool_messages;
pub mod transform_layer;
pub mod utility_types;
Expand Down
53 changes: 53 additions & 0 deletions editor/src/messages/tool/shapes/ellipse_shape.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use super::*;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
use crate::messages::tool::common_functionality::graph_modification_utils;
use crate::messages::tool::tool_messages::tool_prelude::*;
use glam::DAffine2;
use graph_craft::document::NodeInput;
use graph_craft::document::value::TaggedValue;
use std::collections::VecDeque;

#[derive(Default)]
pub struct Ellipse;

impl Ellipse {
pub fn create_node() -> NodeTemplate {
let node_type = resolve_document_node_type("Ellipse").expect("Ellipse node does not exist");
node_type.node_template_input_override([None, Some(NodeInput::value(TaggedValue::F64(0.5), false)), Some(NodeInput::value(TaggedValue::F64(0.5), false))])
}

pub fn update_shape(
document: &DocumentMessageHandler,
ipp: &InputPreprocessorMessageHandler,
layer: LayerNodeIdentifier,
shape_tool_data: &mut ShapeToolData,
modifier: ShapeToolModifierKey,
responses: &mut VecDeque<Message>,
) -> bool {
let (center, lock_ratio) = (modifier[0], modifier[1]);
if let Some([start, end]) = shape_tool_data.data.calculate_points(document, ipp, center, lock_ratio) {
let Some(node_id) = graph_modification_utils::get_ellipse_id(layer, &document.network_interface) else {
return true;
};

responses.add(NodeGraphMessage::SetInput {
input_connector: InputConnector::node(node_id, 1),
input: NodeInput::value(TaggedValue::F64(((start.x - end.x) / 2.).abs()), false),
});
responses.add(NodeGraphMessage::SetInput {
input_connector: InputConnector::node(node_id, 2),
input: NodeInput::value(TaggedValue::F64(((start.y - end.y) / 2.).abs()), false),
});
responses.add(GraphOperationMessage::TransformSet {
layer,
transform: DAffine2::from_translation(start.midpoint(end)),
transform_in: TransformIn::Local,
skip_rerender: false,
});
}
false
}
}
129 changes: 129 additions & 0 deletions editor/src/messages/tool/shapes/line_shape.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use super::*;
use crate::consts::LINE_ROTATE_SNAP_ANGLE;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
use crate::messages::tool::common_functionality::graph_modification_utils;
use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapConstraint, SnapData, SnapTypeConfiguration};
use crate::messages::tool::tool_messages::shape_tool::ShapeToolData;
use crate::messages::tool::tool_messages::tool_prelude::*;
use glam::DVec2;
use graph_craft::document::NodeInput;
use graph_craft::document::value::TaggedValue;
use std::collections::VecDeque;

#[derive(Clone, Debug, Default)]
pub enum LineEnd {
#[default]
Start,
End,
}

#[derive(Default)]
pub struct Line;

impl Line {
pub fn create_node(document: &DocumentMessageHandler, init_data: LineInitData) -> NodeTemplate {
let drag_start = init_data.drag_start;
let node_type = resolve_document_node_type("Line").expect("Line node does not exist");
node_type.node_template_input_override([
None,
Some(NodeInput::value(TaggedValue::DVec2(document.metadata().document_to_viewport.transform_point2(drag_start)), false)),
Some(NodeInput::value(TaggedValue::DVec2(document.metadata().document_to_viewport.transform_point2(drag_start)), false)),
])
}

pub fn update_shape(
document: &DocumentMessageHandler,
ipp: &InputPreprocessorMessageHandler,
layer: LayerNodeIdentifier,
shape_tool_data: &mut ShapeToolData,
modifier: ShapeToolModifierKey,
responses: &mut VecDeque<Message>,
) -> bool {
let (center, snap_angle, lock_angle) = (modifier[0], modifier[3], modifier[2]);
shape_tool_data.drag_current = ipp.mouse.position;
let keyboard = &ipp.keyboard;
let ignore = vec![layer];
let snap_data = SnapData::ignore(document, ipp, &ignore);
let document_points = generate_line(shape_tool_data, snap_data, keyboard.key(lock_angle), keyboard.key(snap_angle), keyboard.key(center));

let Some(node_id) = graph_modification_utils::get_line_id(layer, &document.network_interface) else {
return true;
};

responses.add(NodeGraphMessage::SetInput {
input_connector: InputConnector::node(node_id, 1),
input: NodeInput::value(TaggedValue::DVec2(document_points[0]), false),
});
responses.add(NodeGraphMessage::SetInput {
input_connector: InputConnector::node(node_id, 2),
input: NodeInput::value(TaggedValue::DVec2(document_points[1]), false),
});
responses.add(NodeGraphMessage::RunDocumentGraph);
false
}
}

fn generate_line(tool_data: &mut ShapeToolData, snap_data: SnapData, lock_angle: bool, snap_angle: bool, center: bool) -> [DVec2; 2] {
let document_to_viewport = snap_data.document.metadata().document_to_viewport;
let mut document_points = [tool_data.data.drag_start, document_to_viewport.inverse().transform_point2(tool_data.drag_current)];

let mut angle = -(document_points[1] - document_points[0]).angle_to(DVec2::X);
let mut line_length = (document_points[1] - document_points[0]).length();

if lock_angle {
angle = tool_data.angle;
} else if snap_angle {
let snap_resolution = LINE_ROTATE_SNAP_ANGLE.to_radians();
angle = (angle / snap_resolution).round() * snap_resolution;
}

tool_data.angle = angle;

if lock_angle {
let angle_vec = DVec2::new(angle.cos(), angle.sin());
line_length = (document_points[1] - document_points[0]).dot(angle_vec);
}

document_points[1] = document_points[0] + line_length * DVec2::new(angle.cos(), angle.sin());

let constrained = snap_angle || lock_angle;
let snap = &mut tool_data.data.snap_manager;

let near_point = SnapCandidatePoint::handle_neighbors(document_points[1], [tool_data.data.drag_start]);
let far_point = SnapCandidatePoint::handle_neighbors(2. * document_points[0] - document_points[1], [tool_data.data.drag_start]);
let config = SnapTypeConfiguration::default();

if constrained {
let constraint = SnapConstraint::Line {
origin: document_points[0],
direction: document_points[1] - document_points[0],
};
if center {
let snapped = snap.constrained_snap(&snap_data, &near_point, constraint, config);
let snapped_far = snap.constrained_snap(&snap_data, &far_point, constraint, config);
let best = if snapped_far.other_snap_better(&snapped) { snapped } else { snapped_far };
document_points[1] = document_points[0] * 2. - best.snapped_point_document;
document_points[0] = best.snapped_point_document;
snap.update_indicator(best);
} else {
let snapped = snap.constrained_snap(&snap_data, &near_point, constraint, config);
document_points[1] = snapped.snapped_point_document;
snap.update_indicator(snapped);
}
} else if center {
let snapped = snap.free_snap(&snap_data, &near_point, config);
let snapped_far = snap.free_snap(&snap_data, &far_point, config);
let best = if snapped_far.other_snap_better(&snapped) { snapped } else { snapped_far };
document_points[1] = document_points[0] * 2. - best.snapped_point_document;
document_points[0] = best.snapped_point_document;
snap.update_indicator(best);
} else {
let snapped = snap.free_snap(&snap_data, &near_point, config);
document_points[1] = snapped.snapped_point_document;
snap.update_indicator(snapped);
}

document_points
}
59 changes: 59 additions & 0 deletions editor/src/messages/tool/shapes/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
pub mod ellipse_shape;
pub mod line_shape;
pub mod rectangle_shape;

pub use super::shapes::ellipse_shape::Ellipse;
pub use super::shapes::line_shape::{Line, LineEnd};
pub use super::shapes::rectangle_shape::Rectangle;
pub use super::tool_messages::shape_tool::ShapeToolData;
use super::tool_messages::tool_prelude::*;
use glam::DVec2;

#[derive(Debug, Clone, Copy, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum ShapeType {
#[default]
Rectangle,
Ellipse,
Line,
}

impl ShapeType {
pub fn name(&self) -> String {
match self {
Self::Line => "Line",
Self::Rectangle => "Rectangle",
Self::Ellipse => "Ellipse",
}.into()
}

pub fn tooltip(&self) -> String {
match self {
Self::Line => "Line tool",
Self::Rectangle => "Rectangle tool",
Self::Ellipse => "Ellipse tool",
}.into()
}

pub fn icon_name(&self) -> String {
match self {
Self::Line => "VectorLineTool",
Self::Rectangle => "VectorRectangleTool",
Self::Ellipse => "VectorEllipseTool",
}.into()
}

pub fn tool_type(&self) -> crate::messages::tool::utility_types::ToolType {
match self {
Self::Line => ToolType::Line,
Self::Rectangle => ToolType::Rectangle,
Self::Ellipse => ToolType::Ellipse,
}
}
}

pub struct LineInitData {
pub drag_start: DVec2,
}

// Center, Lock Ratio, Lock Angle, Snap Angle
pub type ShapeToolModifierKey = [Key; 4];
53 changes: 53 additions & 0 deletions editor/src/messages/tool/shapes/rectangle_shape.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use super::*;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
use crate::messages::tool::common_functionality::graph_modification_utils;
use crate::messages::tool::tool_messages::tool_prelude::*;
use glam::DAffine2;
use graph_craft::document::NodeInput;
use graph_craft::document::value::TaggedValue;
use std::collections::VecDeque;

#[derive(Default)]
pub struct Rectangle;

impl Rectangle {
pub fn create_node() -> NodeTemplate {
let node_type = resolve_document_node_type("Rectangle").expect("Rectangle node does not exist");
node_type.node_template_input_override([None, Some(NodeInput::value(TaggedValue::F64(1.), false)), Some(NodeInput::value(TaggedValue::F64(1.), false))])
}

pub fn update_shape(
document: &DocumentMessageHandler,
ipp: &InputPreprocessorMessageHandler,
layer: LayerNodeIdentifier,
shape_tool_data: &mut ShapeToolData,
modifier: ShapeToolModifierKey,
responses: &mut VecDeque<Message>,
) -> bool {
let (center, lock_ratio) = (modifier[0], modifier[1]);
if let Some([start, end]) = shape_tool_data.data.calculate_points(document, ipp, center, lock_ratio) {
let Some(node_id) = graph_modification_utils::get_rectangle_id(layer, &document.network_interface) else {
return true;
};

responses.add(NodeGraphMessage::SetInput {
input_connector: InputConnector::node(node_id, 1),
input: NodeInput::value(TaggedValue::F64((start.x - end.x).abs()), false),
});
responses.add(NodeGraphMessage::SetInput {
input_connector: InputConnector::node(node_id, 2),
input: NodeInput::value(TaggedValue::F64((start.y - end.y).abs()), false),
});
responses.add(GraphOperationMessage::TransformSet {
layer,
transform: DAffine2::from_translation(start.midpoint(end)),
transform_in: TransformIn::Local,
skip_rerender: false,
});
}
false
}
}
Loading