From dabdef7bed21c3b9d74ba8221ffe8092541bd1e3 Mon Sep 17 00:00:00 2001 From: 0SlowPoke0 Date: Sun, 24 Aug 2025 22:02:20 +0530 Subject: [PATCH] multi-layer drag and repeat --- .../graph_operation_message.rs | 5 +- .../graph_operation_message_handler.rs | 4 +- .../document/graph_operation/utility_types.rs | 11 +++ .../shapes/shape_utility.rs | 12 +++ .../tool/tool_messages/operation_tool.rs | 78 ++++++++++++++++--- 5 files changed, 96 insertions(+), 14 deletions(-) diff --git a/editor/src/messages/portfolio/document/graph_operation/graph_operation_message.rs b/editor/src/messages/portfolio/document/graph_operation/graph_operation_message.rs index 6251e74124..a304d0f4bc 100644 --- a/editor/src/messages/portfolio/document/graph_operation/graph_operation_message.rs +++ b/editor/src/messages/portfolio/document/graph_operation/graph_operation_message.rs @@ -22,8 +22,11 @@ pub enum GraphOperationMessage { layer: LayerNodeIdentifier, fill: Fill, }, - RepeatSet { + CircularRepeatSet { layer: LayerNodeIdentifier, + angle: f64, + radius: f64, + count: u32, }, BlendingFillSet { layer: LayerNodeIdentifier, diff --git a/editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs b/editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs index fd7bcddb23..bb25241272 100644 --- a/editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs +++ b/editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs @@ -37,9 +37,9 @@ impl MessageHandler> for modify_inputs.fill_set(fill); } } - GraphOperationMessage::RepeatSet { layer } => { + GraphOperationMessage::CircularRepeatSet { layer, angle, radius, count } => { if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) { - modify_inputs.create_node("Repeat"); + modify_inputs.circular_repeat_set(angle, radius, count); } } GraphOperationMessage::BlendingFillSet { layer, fill } => { diff --git a/editor/src/messages/portfolio/document/graph_operation/utility_types.rs b/editor/src/messages/portfolio/document/graph_operation/utility_types.rs index 4079fefd32..49a0674723 100644 --- a/editor/src/messages/portfolio/document/graph_operation/utility_types.rs +++ b/editor/src/messages/portfolio/document/graph_operation/utility_types.rs @@ -394,6 +394,17 @@ impl<'a> ModifyInputsContext<'a> { self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(stroke.dash_offset), false), true); } + pub fn circular_repeat_set(&mut self, angle: f64, radius: f64, count: u32) { + let Some(circular_repeat_node_id) = self.existing_node_id("Circular Repeat", true) else { return }; + + let input_connector = InputConnector::node(circular_repeat_node_id, graphene_std::vector::circular_repeat::AngleOffsetInput::INDEX); + self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(angle), false), true); + let input_connector = InputConnector::node(circular_repeat_node_id, graphene_std::vector::circular_repeat::RadiusInput::INDEX); + self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(radius), false), true); + let input_connector = InputConnector::node(circular_repeat_node_id, graphene_std::vector::circular_repeat::CountInput::INDEX); + self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::U32(count), false), false); + } + /// Update the transform value of the upstream Transform node based a change to its existing value and the given parent transform. /// A new Transform node is created if one does not exist, unless it would be given the identity transform. pub fn transform_change_with_parent(&mut self, transform: DAffine2, transform_in: TransformIn, parent_transform: DAffine2, skip_rerender: bool) { diff --git a/editor/src/messages/tool/common_functionality/shapes/shape_utility.rs b/editor/src/messages/tool/common_functionality/shapes/shape_utility.rs index 17c7f8e574..5dfad7d169 100644 --- a/editor/src/messages/tool/common_functionality/shapes/shape_utility.rs +++ b/editor/src/messages/tool/common_functionality/shapes/shape_utility.rs @@ -230,6 +230,18 @@ pub fn extract_star_parameters(layer: Option, document: &Do Some((sides, radius_1, radius_2)) } +pub fn extract_circular_repeat_parameters(layer: Option, document: &DocumentMessageHandler) -> Option<(f64, f64, u32)> { + let node_inputs = NodeGraphLayer::new(layer?, &document.network_interface).find_node_inputs("Circular Repeat")?; + + let (Some(&TaggedValue::F64(angle)), Some(&TaggedValue::F64(radius)), Some(&TaggedValue::U32(count))) = + (node_inputs.get(1)?.as_value(), node_inputs.get(2)?.as_value(), node_inputs.get(3)?.as_value()) + else { + return None; + }; + + Some((angle, radius, count)) +} + /// Extract the node input values of Polygon. /// Returns an option of (sides, radius). pub fn extract_polygon_parameters(layer: Option, document: &DocumentMessageHandler) -> Option<(u32, f64)> { diff --git a/editor/src/messages/tool/tool_messages/operation_tool.rs b/editor/src/messages/tool/tool_messages/operation_tool.rs index e4c06616cb..5228edb6aa 100644 --- a/editor/src/messages/tool/tool_messages/operation_tool.rs +++ b/editor/src/messages/tool/tool_messages/operation_tool.rs @@ -8,6 +8,7 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye use crate::messages::tool::common_functionality::auto_panning::AutoPanning; use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType}; use crate::messages::tool::common_functionality::graph_modification_utils::{self, find_spline, merge_layers, merge_points}; +use crate::messages::tool::common_functionality::shapes::shape_utility::{extract_circular_repeat_parameters, extract_star_parameters}; use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapData, SnapManager, SnapTypeConfiguration, SnappedPoint}; use crate::messages::tool::common_functionality::utility_functions::{closest_point, should_extend}; use graph_craft::document::{NodeId, NodeInput}; @@ -49,7 +50,6 @@ pub enum OperationToolMessage { Confirm, DragStart, DragStop, - MergeEndpoints, PointerMove, PointerOutsideViewport, Undo, @@ -222,10 +222,19 @@ impl ToolTransition for OperationTool { } #[derive(Clone, Debug, Default)] -struct OperationToolData {} +struct OperationToolData { + drag_start: DVec2, + clicked_layer: LayerNodeIdentifier, + layers_dragging: Vec<(LayerNodeIdentifier, f64)>, + initial_center: DVec2, +} impl OperationToolData { - fn cleanup(&mut self) {} + fn cleanup(&mut self) { + self.layers_dragging.clear(); + } + + fn modify_circular_repeat(&mut self) {} } impl Fsm for OperationToolFsmState { @@ -251,19 +260,66 @@ impl Fsm for OperationToolFsmState { let ToolMessage::Operation(event) = event else { return self }; match (self, event) { - (_, OperationToolMessage::Overlays { context: mut overlay_context }) => self, + (_, OperationToolMessage::Overlays { context: mut overlay_context }) => {} (OperationToolFsmState::Ready, OperationToolMessage::DragStart) => { + let selected_layers = document + .network_interface + .selected_nodes() + .selected_layers(document.metadata()) + .collect::>(); let Some(layer) = document.click(&input) else { return self }; - responses.add(GraphOperationMessage::RepeatSet { layer }); - responses.add(NodeGraphMessage::RunDocumentGraph); + let viewport = document.metadata().transform_to_viewport(layer); + + if selected_layers.contains(&layer) { + // store all + tool_data.layers_dragging = selected_layers + .iter() + .map(|layer| { + let (_angle_offset, radius, _count) = extract_circular_repeat_parameters(Some(*layer), document).unwrap_or((0.0, 0.0, 6)); + (*layer, radius) + }) + .collect::>(); + } else { + // deselect all the layer and store the clicked layer for repeat and dragging + + responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] }); + let (_angle_offset, radius, _count) = extract_circular_repeat_parameters(Some(layer), document).unwrap_or((0.0, 0.0, 6)); + tool_data.layers_dragging = vec![(layer, radius)]; + } + tool_data.drag_start = input.mouse.position; + tool_data.clicked_layer = layer; + tool_data.initial_center = viewport.transform_point2(DVec2::ZERO); + OperationToolFsmState::Drawing } - (OperationToolFsmState::Drawing, OperationToolMessage::DragStop) => OperationToolFsmState::Drawing, - (OperationToolFsmState::Drawing, OperationToolMessage::PointerMove) => OperationToolFsmState::Drawing, - (_, OperationToolMessage::PointerMove) => { - log::info!("hello"); - self + (OperationToolFsmState::Drawing, OperationToolMessage::DragStop) => { + tool_data.cleanup(); + OperationToolFsmState::Ready } + (OperationToolFsmState::Drawing, OperationToolMessage::PointerMove) => { + // Don't add the repeat node unless dragging more that 5 px + if tool_data.drag_start.distance(input.mouse.position) < 5. { + return self; + }; + + let viewport = document.metadata().transform_to_viewport(tool_data.clicked_layer); + let center = viewport.transform_point2(DVec2::ZERO); + let sign = (input.mouse.position - tool_data.initial_center).dot(tool_data.drag_start - tool_data.initial_center).signum(); + let delta = viewport.inverse().transform_vector2(input.mouse.position - tool_data.drag_start).length() * sign; + log::info!("{:?}", delta); + for (layer, initial_radius) in &tool_data.layers_dragging { + responses.add(GraphOperationMessage::CircularRepeatSet { + layer: *layer, + angle: 0., + radius: initial_radius + delta, + count: 6, + }); + } + responses.add(NodeGraphMessage::RunDocumentGraph); + + OperationToolFsmState::Drawing + } + (_, OperationToolMessage::PointerMove) => self, (OperationToolFsmState::Drawing, OperationToolMessage::PointerOutsideViewport) => OperationToolFsmState::Drawing, (state, OperationToolMessage::PointerOutsideViewport) => state,