diff --git a/editor/src/messages/portfolio/document/node_graph/graph_operation_message.rs b/editor/src/messages/portfolio/document/node_graph/graph_operation_message.rs index ef9af0bc..8348ccf6 100644 --- a/editor/src/messages/portfolio/document/node_graph/graph_operation_message.rs +++ b/editor/src/messages/portfolio/document/node_graph/graph_operation_message.rs @@ -94,4 +94,5 @@ pub enum VectorDataModification { SetManipulatorHandleMirroring { id: ManipulatorGroupId, mirror_angle: bool }, SetManipulatorPosition { point: ManipulatorPointId, position: DVec2 }, ToggleManipulatorHandleMirroring { id: ManipulatorGroupId }, + UpdateSubpaths { subpaths: Vec> }, } diff --git a/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs index 80b4f83d..ad59e26b 100644 --- a/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs @@ -378,6 +378,11 @@ impl<'a> ModifyInputsContext<'a> { } fn delete_layer(&mut self, id: NodeId) { + if !self.network.nodes.contains_key(&id) { + warn!("Deleting layer node that does not exist"); + return; + } + let mut new_input = None; let post_node = self.outwards_links.get(&id).and_then(|links| links.first().copied()); let mut delete_nodes = vec![id]; diff --git a/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler/transform_utils.rs b/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler/transform_utils.rs index 00bfffd7..495195e6 100644 --- a/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler/transform_utils.rs +++ b/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler/transform_utils.rs @@ -316,6 +316,7 @@ impl<'a> VectorModificationState<'a> { VectorDataModification::SetManipulatorHandleMirroring { id, mirror_angle } => self.set_mirror(id, mirror_angle), VectorDataModification::SetManipulatorPosition { point, position } => self.set_position(point, position), VectorDataModification::ToggleManipulatorHandleMirroring { id } => self.toggle_mirror(id), + VectorDataModification::UpdateSubpaths { subpaths } => *self.subpaths = subpaths, } } } diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index 06aadf4d..9ae4a61a 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -712,7 +712,7 @@ impl PortfolioMessageHandler { } pub fn poll_node_graph_evaluation(&mut self, responses: &mut VecDeque) { - let Some(active_document) = self.active_document_id.and_then(|id| self.documents.get_mut(&id)) else { + let Some(active_document) = self.active_document_id.and_then(|id| self.documents.get_mut(&id)) else { warn!("Polling node graph with no document"); return; }; diff --git a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs index 4f5ad90f..1ee0563c 100644 --- a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs +++ b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs @@ -28,7 +28,7 @@ pub fn new_custom_layer(network: NodeNetwork, layer_path: Vec, response responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path }); } -pub fn set_manipulator_mirror_angle(manipulator_groups: &Vec>, layer_path: &[u64], mirror_angle: bool, responses: &mut VecDeque) { +pub fn set_manipulator_mirror_angle(manipulator_groups: &[ManipulatorGroup], layer_path: &[u64], mirror_angle: bool, responses: &mut VecDeque) { for manipulator_group in manipulator_groups { responses.add(GraphOperationMessage::Vector { layer: layer_path.to_owned(), diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index 6228c0c9..200424b2 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -153,7 +153,7 @@ impl ShapeState { // Sets the selected points to all points for the corresponding intersection pub fn select_all_anchors(&mut self, document: &Document, layer: LayerNodeIdentifier) { - let Some(subpaths) = get_subpaths(layer, document) else { return }; + let Some(subpaths) = get_subpaths(layer, document) else { return }; let Some(state) = self.selected_shape_state.get_mut(&layer) else { return }; for manipulator in get_manipulator_groups(subpaths) { state.select_point(ManipulatorPointId::new(manipulator.id, SelectedType::Anchor)) @@ -375,7 +375,7 @@ impl ShapeState { continue; } - let Some(group) =get_manipulator_from_id(subpaths,point.group) else { continue }; + let Some(group) = get_manipulator_from_id(subpaths, point.group) else { continue }; let mut move_point = |point: ManipulatorPointId| { let Some(previous_position) = point.manipulator_type.get_position(group) else { return }; @@ -430,7 +430,7 @@ impl ShapeState { /// Delete selected and mirrored handles with zero length when the drag stops. pub fn delete_selected_handles_with_zero_length(&self, document: &Document, opposing_handle_lengths: &Option, responses: &mut VecDeque) { for (&layer, state) in &self.selected_shape_state { - let Some(subpaths) = get_subpaths(layer, document) else { continue }; + let Some(subpaths) = get_subpaths(layer, document) else { continue }; let Some(mirror_angle) = get_mirror_handles(layer, document) else { continue }; let opposing_handle_lengths = opposing_handle_lengths.as_ref().and_then(|lengths| lengths.get(&layer)); @@ -443,7 +443,7 @@ impl ShapeState { continue; } - let Some(group) = get_manipulator_from_id(subpaths,point.group) else { continue }; + let Some(group) = get_manipulator_from_id(subpaths, point.group) else { continue }; let anchor_position = transform.transform_point2(group.anchor); @@ -521,7 +521,7 @@ impl ShapeState { /// Reset the opposing handle lengths. pub fn reset_opposing_handle_lengths(&self, document: &Document, opposing_handle_lengths: &OpposingHandleLengths, responses: &mut VecDeque) { for (&layer, state) in &self.selected_shape_state { - let Some(subpaths) = get_subpaths(layer, document) else { continue }; + let Some(subpaths) = get_subpaths(layer, document) else { continue }; let Some(mirror_angle) = get_mirror_handles(layer, document) else { continue }; let Some(opposing_handle_lengths) = opposing_handle_lengths.get(&layer) else { continue }; diff --git a/editor/src/messages/tool/tool_messages/brush_tool.rs b/editor/src/messages/tool/tool_messages/brush_tool.rs index f2d8d826..fafbecf7 100644 --- a/editor/src/messages/tool/tool_messages/brush_tool.rs +++ b/editor/src/messages/tool/tool_messages/brush_tool.rs @@ -219,7 +219,7 @@ impl LayoutHolder for BrushTool { impl<'a> MessageHandler> for BrushTool { fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque, tool_data: &mut ToolActionHandlerData<'a>) { - let ToolMessage::Brush(BrushToolMessage::UpdateOptions(action)) = message else{ + let ToolMessage::Brush(BrushToolMessage::UpdateOptions(action)) = message else { self.fsm_state.process_event(message, &mut self.data, tool_data, &self.options, responses, true); return; }; diff --git a/editor/src/messages/tool/tool_messages/ellipse_tool.rs b/editor/src/messages/tool/tool_messages/ellipse_tool.rs index d6e63554..4a712935 100644 --- a/editor/src/messages/tool/tool_messages/ellipse_tool.rs +++ b/editor/src/messages/tool/tool_messages/ellipse_tool.rs @@ -112,7 +112,7 @@ impl LayoutHolder for EllipseTool { impl<'a> MessageHandler> for EllipseTool { fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque, tool_data: &mut ToolActionHandlerData<'a>) { - let ToolMessage::Ellipse(EllipseToolMessage::UpdateOptions(action)) = message else{ + let ToolMessage::Ellipse(EllipseToolMessage::UpdateOptions(action)) = message else { self.fsm_state.process_event(message, &mut self.data, tool_data, &self.options, responses, true); return; }; diff --git a/editor/src/messages/tool/tool_messages/eyedropper_tool.rs b/editor/src/messages/tool/tool_messages/eyedropper_tool.rs index 88192c03..db668ff1 100644 --- a/editor/src/messages/tool/tool_messages/eyedropper_tool.rs +++ b/editor/src/messages/tool/tool_messages/eyedropper_tool.rs @@ -84,7 +84,7 @@ impl Fsm for EyedropperToolFsmState { fn transition(self, event: ToolMessage, _tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionHandlerData, _tool_options: &(), responses: &mut VecDeque) -> Self { let ToolActionHandlerData { global_tool_data, input, .. } = tool_action_data; - let ToolMessage::Eyedropper(event) = event else{ + let ToolMessage::Eyedropper(event) = event else { return self; }; match (self, event) { diff --git a/editor/src/messages/tool/tool_messages/fill_tool.rs b/editor/src/messages/tool/tool_messages/fill_tool.rs index 3fb02700..c125477b 100644 --- a/editor/src/messages/tool/tool_messages/fill_tool.rs +++ b/editor/src/messages/tool/tool_messages/fill_tool.rs @@ -69,8 +69,8 @@ impl Fsm for FillToolFsmState { return self; }; let Some((layer_identifier, _)) = document.document_legacy.metadata.click(input.mouse.position) else { - return self; - }; + return self; + }; let layer = layer_identifier.to_path(); let color = match event { diff --git a/editor/src/messages/tool/tool_messages/freehand_tool.rs b/editor/src/messages/tool/tool_messages/freehand_tool.rs index 7124416e..4c74fb10 100644 --- a/editor/src/messages/tool/tool_messages/freehand_tool.rs +++ b/editor/src/messages/tool/tool_messages/freehand_tool.rs @@ -1,4 +1,5 @@ use super::tool_prelude::*; +use crate::messages::portfolio::document::node_graph::VectorDataModification; use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType}; use crate::messages::tool::common_functionality::graph_modification_utils; @@ -6,6 +7,7 @@ use document_legacy::LayerId; use graphene_core::vector::style::{Fill, Stroke}; use graphene_core::Color; +use bezier_rs::ManipulatorGroup; use glam::DVec2; use serde::{Deserialize, Serialize}; @@ -117,7 +119,7 @@ impl LayoutHolder for FreehandTool { impl<'a> MessageHandler> for FreehandTool { fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque, tool_data: &mut ToolActionHandlerData<'a>) { - let ToolMessage::Freehand(FreehandToolMessage::UpdateOptions(action)) = message else{ + let ToolMessage::Freehand(FreehandToolMessage::UpdateOptions(action)) = message else { self.fsm_state.process_event(message, &mut self.data, tool_data, &self.options, responses, true); return; }; @@ -174,9 +176,10 @@ impl ToolTransition for FreehandTool { #[derive(Clone, Debug, Default)] struct FreehandToolData { - points: Vec, + last_point: DVec2, + dragged: bool, weight: f64, - path: Option>, + layer_path: Option>, } impl Fsm for FreehandToolFsmState { @@ -197,40 +200,42 @@ impl Fsm for FreehandToolFsmState { (FreehandToolFsmState::Ready, FreehandToolMessage::DragStart) => { responses.add(DocumentMessage::StartTransaction); responses.add(DocumentMessage::DeselectAllLayers); - tool_data.path = Some(document.get_path_for_new_layer()); + tool_data.layer_path = Some(document.get_path_for_new_layer()); let pos = transform.inverse().transform_point2(input.mouse.position); - tool_data.points.push(pos); + tool_data.dragged = false; + tool_data.last_point = pos; tool_data.weight = tool_options.line_weight; - add_polyline(tool_data, tool_options.stroke.active_color(), tool_options.fill.active_color(), responses); + add_polyline([pos], tool_data, tool_options.stroke.active_color(), tool_options.fill.active_color(), responses); FreehandToolFsmState::Drawing } (FreehandToolFsmState::Drawing, FreehandToolMessage::PointerMove) => { let pos = transform.inverse().transform_point2(input.mouse.position); - if tool_data.points.last() != Some(&pos) { - tool_data.points.push(pos); + if tool_data.last_point != pos { + if let Some(layer) = tool_data.layer_path.clone() { + let manipulator_group = ManipulatorGroup::new_anchor(pos); + let modification = VectorDataModification::AddEndManipulatorGroup { subpath_index: 0, manipulator_group }; + responses.add(GraphOperationMessage::Vector { layer, modification }); + tool_data.dragged = true; + tool_data.last_point = pos; + } } - add_polyline(tool_data, tool_options.stroke.active_color(), tool_options.fill.active_color(), responses); - FreehandToolFsmState::Drawing } (FreehandToolFsmState::Drawing, FreehandToolMessage::DragStop | FreehandToolMessage::Abort) => { - if tool_data.points.len() >= 2 { - responses.add(remove_preview(tool_data)); - add_polyline(tool_data, tool_options.stroke.active_color(), tool_options.fill.active_color(), responses); + if tool_data.dragged { responses.add(DocumentMessage::CommitTransaction); } else { responses.add(DocumentMessage::AbortTransaction); } - tool_data.path = None; - tool_data.points.clear(); + tool_data.layer_path = None; FreehandToolFsmState::Ready } @@ -259,14 +264,10 @@ impl Fsm for FreehandToolFsmState { } } -fn remove_preview(data: &FreehandToolData) -> Message { - GraphOperationMessage::DeleteLayer { id: data.path.clone().unwrap()[0] }.into() -} +fn add_polyline(anchors: impl IntoIterator, data: &FreehandToolData, stroke_color: Option, fill_color: Option, responses: &mut VecDeque) { + let subpath = bezier_rs::Subpath::from_anchors(anchors, false); -fn add_polyline(data: &FreehandToolData, stroke_color: Option, fill_color: Option, responses: &mut VecDeque) { - let subpath = bezier_rs::Subpath::from_anchors(data.points.iter().copied(), false); - - let layer_path = data.path.clone().unwrap(); + let layer_path = data.layer_path.clone().unwrap(); graph_modification_utils::new_vector_layer(vec![subpath], layer_path.clone(), responses); responses.add(GraphOperationMessage::FillSet { diff --git a/editor/src/messages/tool/tool_messages/imaginate_tool.rs b/editor/src/messages/tool/tool_messages/imaginate_tool.rs index 4b98c1ba..1b4c8f99 100644 --- a/editor/src/messages/tool/tool_messages/imaginate_tool.rs +++ b/editor/src/messages/tool/tool_messages/imaginate_tool.rs @@ -112,7 +112,7 @@ impl Fsm for ImaginateToolFsmState { ) -> Self { let shape_data = &mut tool_data.data; - let ToolMessage::Imaginate(event) = event else{ + let ToolMessage::Imaginate(event) = event else { return self; }; match (self, event) { diff --git a/editor/src/messages/tool/tool_messages/navigate_tool.rs b/editor/src/messages/tool/tool_messages/navigate_tool.rs index 481782c0..b9a0a132 100644 --- a/editor/src/messages/tool/tool_messages/navigate_tool.rs +++ b/editor/src/messages/tool/tool_messages/navigate_tool.rs @@ -104,7 +104,7 @@ impl Fsm for NavigateToolFsmState { _tool_options: &Self::ToolOptions, responses: &mut VecDeque, ) -> Self { - let ToolMessage::Navigate(navigate) = message else{ + let ToolMessage::Navigate(navigate) = message else { return self; }; diff --git a/editor/src/messages/tool/tool_messages/pen_tool.rs b/editor/src/messages/tool/tool_messages/pen_tool.rs index 6190547b..2319fbdd 100644 --- a/editor/src/messages/tool/tool_messages/pen_tool.rs +++ b/editor/src/messages/tool/tool_messages/pen_tool.rs @@ -218,7 +218,9 @@ impl PenToolData { self.subpath_index = subpath_index; // Stop the handles on the first point from mirroring - let Some(subpaths) = get_subpaths (LayerNodeIdentifier::from_path(layer, document.network()), &document.document_legacy) else { return }; + let Some(subpaths) = get_subpaths(LayerNodeIdentifier::from_path(layer, document.network()), &document.document_legacy) else { + return; + }; let manipulator_groups = subpaths[subpath_index].manipulator_groups(); let Some(last_handle) = (if from_start { manipulator_groups.first() } else { manipulator_groups.last() }) else { return; diff --git a/editor/src/messages/tool/tool_messages/spline_tool.rs b/editor/src/messages/tool/tool_messages/spline_tool.rs index 49d3109c..a5267d17 100644 --- a/editor/src/messages/tool/tool_messages/spline_tool.rs +++ b/editor/src/messages/tool/tool_messages/spline_tool.rs @@ -1,5 +1,6 @@ use super::tool_prelude::*; use crate::consts::DRAG_THRESHOLD; +use crate::messages::portfolio::document::node_graph::VectorDataModification; use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType}; use crate::messages::tool::common_functionality::graph_modification_utils; use crate::messages::tool::common_functionality::snapping::SnapManager; @@ -192,22 +193,14 @@ impl Fsm for SplineToolFsmState { type ToolData = SplineToolData; type ToolOptions = SplineOptions; - fn transition( - self, - event: ToolMessage, - tool_data: &mut Self::ToolData, - ToolActionHandlerData { + fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionHandlerData, tool_options: &Self::ToolOptions, responses: &mut VecDeque) -> Self { + let ToolActionHandlerData { document, global_tool_data, input, render_data, .. - }: &mut ToolActionHandlerData, - tool_options: &Self::ToolOptions, - responses: &mut VecDeque, - ) -> Self { - use SplineToolFsmState::*; - use SplineToolMessage::*; + } = tool_action_data; let transform = document.document_legacy.metadata.document_to_viewport; @@ -215,11 +208,11 @@ impl Fsm for SplineToolFsmState { return self; }; match (self, event) { - (_, CanvasTransformed) => { + (_, SplineToolMessage::CanvasTransformed) => { tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, render_data), true, true); self } - (Ready, DragStart) => { + (SplineToolFsmState::Ready, SplineToolMessage::DragStart) => { responses.add(DocumentMessage::StartTransaction); responses.add(DocumentMessage::DeselectAllLayers); tool_data.path = Some(document.get_path_for_new_layer()); @@ -235,11 +228,11 @@ impl Fsm for SplineToolFsmState { tool_data.weight = tool_options.line_weight; - add_spline(tool_data, true, tool_options.fill.active_color(), tool_options.stroke.active_color(), responses); + add_spline(tool_data, tool_options.fill.active_color(), tool_options.stroke.active_color(), responses); - Drawing + SplineToolFsmState::Drawing } - (Drawing, DragStop) => { + (SplineToolFsmState::Drawing, SplineToolMessage::DragStop) => { let snapped_position = tool_data.snap_manager.snap_position(responses, document, input.mouse.position); let pos = transform.inverse().transform_point2(snapped_position); @@ -250,25 +243,22 @@ impl Fsm for SplineToolFsmState { } } - responses.add(remove_preview(tool_data)); - add_spline(tool_data, true, tool_options.fill.active_color(), tool_options.stroke.active_color(), responses); + update_spline(tool_data, true, responses); - Drawing + SplineToolFsmState::Drawing } - (Drawing, PointerMove) => { + (SplineToolFsmState::Drawing, SplineToolMessage::PointerMove) => { let snapped_position = tool_data.snap_manager.snap_position(responses, document, input.mouse.position); let pos = transform.inverse().transform_point2(snapped_position); tool_data.next_point = pos; - responses.add(remove_preview(tool_data)); - add_spline(tool_data, true, tool_options.fill.active_color(), tool_options.stroke.active_color(), responses); + update_spline(tool_data, true, responses); - Drawing + SplineToolFsmState::Drawing } - (Drawing, Confirm) | (Drawing, Abort) => { + (SplineToolFsmState::Drawing, SplineToolMessage::Confirm | SplineToolMessage::Abort) => { if tool_data.points.len() >= 2 { - responses.add(remove_preview(tool_data)); - add_spline(tool_data, false, tool_options.fill.active_color(), tool_options.stroke.active_color(), responses); + update_spline(tool_data, false, responses); responses.add(DocumentMessage::CommitTransaction); } else { responses.add(DocumentMessage::AbortTransaction); @@ -278,9 +268,9 @@ impl Fsm for SplineToolFsmState { tool_data.points.clear(); tool_data.snap_manager.cleanup(responses); - Ready + SplineToolFsmState::Ready } - (_, WorkingColorChanged) => { + (_, SplineToolMessage::WorkingColorChanged) => { responses.add(SplineToolMessage::UpdateOptions(SplineOptionsUpdate::WorkingColors( Some(global_tool_data.primary_color), Some(global_tool_data.secondary_color), @@ -308,25 +298,11 @@ impl Fsm for SplineToolFsmState { } } -fn remove_preview(tool_data: &SplineToolData) -> Message { - Operation::DeleteLayer { - path: tool_data.path.clone().unwrap(), - } - .into() -} - -fn add_spline(tool_data: &SplineToolData, show_preview: bool, fill_color: Option, stroke_color: Option, responses: &mut VecDeque) { - let mut points = tool_data.points.clone(); - if show_preview { - points.push(tool_data.next_point) - } - - let subpath = bezier_rs::Subpath::new_cubic_spline(points); - - let layer_path = tool_data.path.clone().unwrap(); - let manipulator_groups = subpath.manipulator_groups().to_vec(); - graph_modification_utils::new_vector_layer(vec![subpath], layer_path.clone(), responses); - graph_modification_utils::set_manipulator_mirror_angle(&manipulator_groups, &layer_path, true, responses); +fn add_spline(tool_data: &SplineToolData, fill_color: Option, stroke_color: Option, responses: &mut VecDeque) { + let Some(layer_path) = tool_data.path.clone() else { + return; + }; + graph_modification_utils::new_vector_layer(vec![], layer_path.clone(), responses); responses.add(GraphOperationMessage::FillSet { layer: layer_path.clone(), @@ -334,7 +310,25 @@ fn add_spline(tool_data: &SplineToolData, show_preview: bool, fill_color: Option }); responses.add(GraphOperationMessage::StrokeSet { - layer: layer_path.clone(), + layer: layer_path, stroke: Stroke::new(stroke_color, tool_data.weight), }); } + +fn update_spline(tool_data: &SplineToolData, show_preview: bool, responses: &mut VecDeque) { + let mut points = tool_data.points.clone(); + if show_preview { + points.push(tool_data.next_point) + } + + let subpath = bezier_rs::Subpath::new_cubic_spline(points); + + let Some(layer) = tool_data.path.clone() else { + return; + }; + + graph_modification_utils::set_manipulator_mirror_angle(subpath.manipulator_groups(), &layer, true, responses); + let subpaths = vec![subpath]; + let modification = VectorDataModification::UpdateSubpaths { subpaths }; + responses.add(GraphOperationMessage::Vector { layer, modification }); +}