From cdb16d8ba0f6da225f1808c703011c2062df7e22 Mon Sep 17 00:00:00 2001 From: Nitish-bot <86357181+Nitish-bot@users.noreply.github.com> Date: Mon, 10 Feb 2025 22:20:03 +0530 Subject: [PATCH 1/8] Initial attempt --- .../messages/tool/tool_messages/line_tool.rs | 68 ++++++++++++++++++- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/line_tool.rs b/editor/src/messages/tool/tool_messages/line_tool.rs index 56823be80b..4d7c76d2c6 100644 --- a/editor/src/messages/tool/tool_messages/line_tool.rs +++ b/editor/src/messages/tool/tool_messages/line_tool.rs @@ -1,11 +1,11 @@ use super::tool_prelude::*; -use crate::consts::{DEFAULT_STROKE_WIDTH, LINE_ROTATE_SNAP_ANGLE}; +use crate::consts::{BOUNDS_SELECT_THRESHOLD, DEFAULT_STROKE_WIDTH, LINE_ROTATE_SNAP_ANGLE}; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::utility_types::{document_metadata::LayerNodeIdentifier, network_interface::InputConnector}; 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; +use crate::messages::tool::common_functionality::graph_modification_utils::{self, is_layer_fed_by_node_of_name, NodeGraphLayer}; use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapConstraint, SnapData, SnapManager, SnapTypeConfiguration}; use graph_craft::document::{value::TaggedValue, NodeId, NodeInput}; @@ -120,6 +120,7 @@ impl<'a> MessageHandler> for LineToo match self.fsm_state { LineToolFsmState::Ready => actions!(LineToolMessageDiscriminant; DragStart, PointerMove), LineToolFsmState::Drawing => actions!(LineToolMessageDiscriminant; DragStop, PointerMove, Abort), + LineToolFsmState::Editing => actions!(LineToolMessageDiscriminant; DragStop, PointerMove, Abort), } } } @@ -140,6 +141,7 @@ enum LineToolFsmState { #[default] Ready, Drawing, + Editing, } #[derive(Clone, Debug, Default)] @@ -151,6 +153,8 @@ struct LineToolData { layer: Option, snap_manager: SnapManager, auto_panning: AutoPanning, + layer_start: Option, + layer_end: Option, } impl Fsm for LineToolFsmState { @@ -166,6 +170,29 @@ impl Fsm for LineToolFsmState { match (self, event) { (_, LineToolMessage::Overlays(mut overlay_context)) => { tool_data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context); + + if let Some(layer) = tool_data.layer { + let inputs = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Line").unwrap(); + let (Some(&TaggedValue::DVec2(start)), Some(&TaggedValue::DVec2(end))) = (inputs[1].as_value(), inputs[2].as_value()) else { + return self; + }; + + let transform = document.metadata().document_to_viewport; + + tool_data.layer_start = Some(transform.transform_point2(start)); + tool_data.layer_end = Some(transform.transform_point2(end)); + + overlay_context.square(tool_data.layer_start.unwrap(), Some(6.), None, None); + overlay_context.square(tool_data.layer_end.unwrap(), Some(6.), None, None); + } else { + tool_data.layer = document + .network_interface + .selected_nodes(&[]) + .unwrap() + .selected_visible_and_unlocked_layers(&document.network_interface) + .find(|layer| is_layer_fed_by_node_of_name(*layer, &document.network_interface, "Line")); + } + self } (LineToolFsmState::Ready, LineToolMessage::DragStart) => { @@ -173,6 +200,24 @@ impl Fsm for LineToolFsmState { let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default()); tool_data.drag_start = snapped.snapped_point_document; + if let (Some(start), Some(end)) = (tool_data.layer_start, tool_data.layer_end) { + let transform = document.metadata().document_to_viewport; + let viewport_x = transform.transform_vector2(DVec2::X).normalize_or_zero() * BOUNDS_SELECT_THRESHOLD; + let viewport_y = transform.transform_vector2(DVec2::Y).normalize_or_zero() * BOUNDS_SELECT_THRESHOLD; + + let threshold_x = transform.inverse().transform_vector2(viewport_x).length(); + let threshold_y = transform.inverse().transform_vector2(viewport_y).length(); + + let start_bool = (tool_data.drag_start.y - start.y).abs() < threshold_y && (tool_data.drag_start.x - start.x).abs() < threshold_x; + let end_bool = (tool_data.drag_start.y - end.y).abs() < threshold_y && (tool_data.drag_start.x - end.x).abs() < threshold_x; + + debug!("Start: {:?}, End: {:?}", start_bool, end_bool); + + if start_bool || end_bool { + return LineToolFsmState::Editing; + } + }; + responses.add(DocumentMessage::StartTransaction); let node_type = resolve_document_node_type("Line").expect("Line node does not exist"); @@ -217,6 +262,16 @@ impl Fsm for LineToolFsmState { LineToolFsmState::Drawing } + (LineToolFsmState::Editing, LineToolMessage::PointerMove { center, lock_angle, snap_angle }) => { + tool_data.drag_current = input.mouse.position; + let keyboard = &input.keyboard; + let ignore = if let Some(layer) = tool_data.layer { vec![layer] } else { vec![] }; + let snap_data = SnapData::ignore(document, input, &ignore); + debug!("Editing line"); + generate_line(tool_data, snap_data, keyboard.key(lock_angle), keyboard.key(snap_angle), keyboard.key(center), responses); + + LineToolFsmState::Editing + } (_, LineToolMessage::PointerMove { .. }) => { tool_data.snap_manager.preview_draw(&SnapData::new(document, input), input.mouse.position); responses.add(OverlaysMessage::Draw); @@ -245,6 +300,7 @@ impl Fsm for LineToolFsmState { LineToolFsmState::Ready } (LineToolFsmState::Drawing, LineToolMessage::Abort) => { + tool_data.layer.take(); tool_data.snap_manager.cleanup(responses); responses.add(DocumentMessage::AbortTransaction); tool_data.layer = None; @@ -277,6 +333,14 @@ impl Fsm for LineToolFsmState { HintInfo::keys([Key::Control], "Lock Angle"), ]), ]), + LineToolFsmState::Editing => HintData(vec![ + HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]), + HintGroup(vec![ + HintInfo::keys([Key::Shift], "15° Increments"), + HintInfo::keys([Key::Alt], "From Center"), + HintInfo::keys([Key::Control], "Lock Angle"), + ]), + ]), }; responses.add(FrontendMessage::UpdateInputHints { hint_data }); From 2c4c5429c4712cfa639bd21642aadae0741d58e8 Mon Sep 17 00:00:00 2001 From: Nitish-bot <86357181+Nitish-bot@users.noreply.github.com> Date: Tue, 11 Feb 2025 20:19:38 +0530 Subject: [PATCH 2/8] Allow editing and display overlays --- .../messages/tool/tool_messages/line_tool.rs | 93 ++++++++++--------- 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/line_tool.rs b/editor/src/messages/tool/tool_messages/line_tool.rs index 4d7c76d2c6..97addfdcf5 100644 --- a/editor/src/messages/tool/tool_messages/line_tool.rs +++ b/editor/src/messages/tool/tool_messages/line_tool.rs @@ -153,8 +153,10 @@ struct LineToolData { layer: Option, snap_manager: SnapManager, auto_panning: AutoPanning, - layer_start: Option, - layer_end: Option, + line_start: Option, + line_end: Option, + start_click: bool, + end_click: bool, } impl Fsm for LineToolFsmState { @@ -179,11 +181,13 @@ impl Fsm for LineToolFsmState { let transform = document.metadata().document_to_viewport; - tool_data.layer_start = Some(transform.transform_point2(start)); - tool_data.layer_end = Some(transform.transform_point2(end)); + tool_data.line_start = Some(start); + tool_data.line_end = Some(end); - overlay_context.square(tool_data.layer_start.unwrap(), Some(6.), None, None); - overlay_context.square(tool_data.layer_end.unwrap(), Some(6.), None, None); + if (start.x - end.x).abs() > f64::EPSILON * 1000. && (start.y - end.y).abs() > f64::EPSILON * 1000. { + overlay_context.square(transform.transform_point2(start), Some(6.), None, None); + overlay_context.square(transform.transform_point2(end), Some(6.), None, None); + } } else { tool_data.layer = document .network_interface @@ -200,22 +204,24 @@ impl Fsm for LineToolFsmState { let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default()); tool_data.drag_start = snapped.snapped_point_document; - if let (Some(start), Some(end)) = (tool_data.layer_start, tool_data.layer_end) { + if let (Some(start), Some(end)) = (tool_data.line_start, tool_data.line_end) { let transform = document.metadata().document_to_viewport; + let viewport_x = transform.transform_vector2(DVec2::X).normalize_or_zero() * BOUNDS_SELECT_THRESHOLD; let viewport_y = transform.transform_vector2(DVec2::Y).normalize_or_zero() * BOUNDS_SELECT_THRESHOLD; - let threshold_x = transform.inverse().transform_vector2(viewport_x).length(); let threshold_y = transform.inverse().transform_vector2(viewport_y).length(); - let start_bool = (tool_data.drag_start.y - start.y).abs() < threshold_y && (tool_data.drag_start.x - start.x).abs() < threshold_x; - let end_bool = (tool_data.drag_start.y - end.y).abs() < threshold_y && (tool_data.drag_start.x - end.x).abs() < threshold_x; + tool_data.start_click = (tool_data.drag_start.y - start.y).abs() < threshold_y && (tool_data.drag_start.x - start.x).abs() < threshold_x; + tool_data.end_click = (tool_data.drag_start.y - end.y).abs() < threshold_y && (tool_data.drag_start.x - end.x).abs() < threshold_x; - debug!("Start: {:?}, End: {:?}", start_bool, end_bool); - - if start_bool || end_bool { - return LineToolFsmState::Editing; + if tool_data.start_click { + tool_data.drag_start = end; + } else if tool_data.end_click { + tool_data.drag_start = start; } + + return LineToolFsmState::Editing; }; responses.add(DocumentMessage::StartTransaction); @@ -245,13 +251,31 @@ impl Fsm for LineToolFsmState { LineToolFsmState::Drawing } - (LineToolFsmState::Drawing, LineToolMessage::PointerMove { center, snap_angle, lock_angle }) => { + (LineToolFsmState::Drawing | LineToolFsmState::Editing, LineToolMessage::PointerMove { center, snap_angle, lock_angle }) => { tool_data.drag_current = input.mouse.position; // tool_data.snap_manager.snap_position(responses, document, input.mouse.position); let keyboard = &input.keyboard; let ignore = if let Some(layer) = tool_data.layer { vec![layer] } else { vec![] }; let snap_data = SnapData::ignore(document, input, &ignore); - generate_line(tool_data, snap_data, keyboard.key(lock_angle), keyboard.key(snap_angle), keyboard.key(center), responses); + let mut document_points = generate_line(tool_data, snap_data, keyboard.key(lock_angle), keyboard.key(snap_angle), keyboard.key(center)); + + if tool_data.start_click { + document_points.swap(0, 1); + } + + let Some(node_id) = graph_modification_utils::get_line_id(tool_data.layer.unwrap(), &document.network_interface) else { + return LineToolFsmState::Ready; + }; + + 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); // Auto-panning let messages = [ @@ -260,28 +284,18 @@ impl Fsm for LineToolFsmState { ]; tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses); - LineToolFsmState::Drawing - } - (LineToolFsmState::Editing, LineToolMessage::PointerMove { center, lock_angle, snap_angle }) => { - tool_data.drag_current = input.mouse.position; - let keyboard = &input.keyboard; - let ignore = if let Some(layer) = tool_data.layer { vec![layer] } else { vec![] }; - let snap_data = SnapData::ignore(document, input, &ignore); - debug!("Editing line"); - generate_line(tool_data, snap_data, keyboard.key(lock_angle), keyboard.key(snap_angle), keyboard.key(center), responses); - - LineToolFsmState::Editing + self } (_, LineToolMessage::PointerMove { .. }) => { tool_data.snap_manager.preview_draw(&SnapData::new(document, input), input.mouse.position); responses.add(OverlaysMessage::Draw); self } - (LineToolFsmState::Drawing, LineToolMessage::PointerOutsideViewport { .. }) => { + (LineToolFsmState::Drawing | LineToolFsmState::Editing, LineToolMessage::PointerOutsideViewport { .. }) => { // Auto-panning let _ = tool_data.auto_panning.shift_viewport(input, responses); - LineToolFsmState::Drawing + self } (state, LineToolMessage::PointerOutsideViewport { center, lock_angle, snap_angle }) => { // Auto-panning @@ -293,13 +307,13 @@ impl Fsm for LineToolFsmState { state } - (LineToolFsmState::Drawing, LineToolMessage::DragStop) => { + (LineToolFsmState::Drawing | LineToolFsmState::Editing, LineToolMessage::DragStop) => { tool_data.snap_manager.cleanup(responses); input.mouse.finish_transaction(tool_data.drag_start, responses); tool_data.layer = None; LineToolFsmState::Ready } - (LineToolFsmState::Drawing, LineToolMessage::Abort) => { + (LineToolFsmState::Drawing | LineToolFsmState::Editing, LineToolMessage::Abort) => { tool_data.layer.take(); tool_data.snap_manager.cleanup(responses); responses.add(DocumentMessage::AbortTransaction); @@ -351,7 +365,7 @@ impl Fsm for LineToolFsmState { } } -fn generate_line(tool_data: &mut LineToolData, snap_data: SnapData, lock_angle: bool, snap_angle: bool, center: bool, responses: &mut VecDeque) { +fn generate_line(tool_data: &mut LineToolData, 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.drag_start, document_to_viewport.inverse().transform_point2(tool_data.drag_current)]; @@ -411,18 +425,5 @@ fn generate_line(tool_data: &mut LineToolData, snap_data: SnapData, lock_angle: snap.update_indicator(snapped); } - let Some(node_id) = graph_modification_utils::get_line_id(tool_data.layer.unwrap(), &snap_data.document.network_interface) else { - return; - }; - - 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); + document_points } From 24d04b12a56cb65f0ba948115251c6d55528e497 Mon Sep 17 00:00:00 2001 From: Nitish-bot <86357181+Nitish-bot@users.noreply.github.com> Date: Tue, 11 Feb 2025 20:47:09 +0530 Subject: [PATCH 3/8] Fix modifier keys --- .../messages/tool/tool_messages/line_tool.rs | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/line_tool.rs b/editor/src/messages/tool/tool_messages/line_tool.rs index 97addfdcf5..5c8e88394d 100644 --- a/editor/src/messages/tool/tool_messages/line_tool.rs +++ b/editor/src/messages/tool/tool_messages/line_tool.rs @@ -215,13 +215,10 @@ impl Fsm for LineToolFsmState { tool_data.start_click = (tool_data.drag_start.y - start.y).abs() < threshold_y && (tool_data.drag_start.x - start.x).abs() < threshold_x; tool_data.end_click = (tool_data.drag_start.y - end.y).abs() < threshold_y && (tool_data.drag_start.x - end.x).abs() < threshold_x; - if tool_data.start_click { - tool_data.drag_start = end; - } else if tool_data.end_click { - tool_data.drag_start = start; + if tool_data.start_click || tool_data.end_click { + tool_data.drag_start = if tool_data.start_click { end } else { start }; + return LineToolFsmState::Editing; } - - return LineToolFsmState::Editing; }; responses.add(DocumentMessage::StartTransaction); @@ -310,14 +307,18 @@ impl Fsm for LineToolFsmState { (LineToolFsmState::Drawing | LineToolFsmState::Editing, LineToolMessage::DragStop) => { tool_data.snap_manager.cleanup(responses); input.mouse.finish_transaction(tool_data.drag_start, responses); - tool_data.layer = None; + tool_data.layer.take(); LineToolFsmState::Ready } - (LineToolFsmState::Drawing | LineToolFsmState::Editing, LineToolMessage::Abort) => { - tool_data.layer.take(); + (LineToolFsmState::Drawing, LineToolMessage::Abort) => { tool_data.snap_manager.cleanup(responses); responses.add(DocumentMessage::AbortTransaction); - tool_data.layer = None; + tool_data.layer.take(); + LineToolFsmState::Ready + } + (LineToolFsmState::Editing, LineToolMessage::Abort) => { + tool_data.snap_manager.cleanup(responses); + tool_data.layer.take(); LineToolFsmState::Ready } (_, LineToolMessage::WorkingColorChanged) => { From 8b155b29feba77e9fe16f851c6f0972b512f8aa9 Mon Sep 17 00:00:00 2001 From: Nitish-bot <86357181+Nitish-bot@users.noreply.github.com> Date: Tue, 11 Feb 2025 22:32:46 +0530 Subject: [PATCH 4/8] Handles show up correctly when layer is transformed --- .../messages/tool/tool_messages/line_tool.rs | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/line_tool.rs b/editor/src/messages/tool/tool_messages/line_tool.rs index 5c8e88394d..1f494d94bb 100644 --- a/editor/src/messages/tool/tool_messages/line_tool.rs +++ b/editor/src/messages/tool/tool_messages/line_tool.rs @@ -174,12 +174,28 @@ impl Fsm for LineToolFsmState { tool_data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context); if let Some(layer) = tool_data.layer { - let inputs = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Line").unwrap(); - let (Some(&TaggedValue::DVec2(start)), Some(&TaggedValue::DVec2(end))) = (inputs[1].as_value(), inputs[2].as_value()) else { + let graph_layer = NodeGraphLayer::new(layer, &document.network_interface); + let node_inputs = graph_layer.find_node_inputs("Line").expect("Tool data layer should have a Line node"); + let transform_inputs = graph_layer.find_node_inputs("Transform"); + let (Some(&TaggedValue::DVec2(start)), Some(&TaggedValue::DVec2(end))) = (node_inputs[1].as_value(), node_inputs[2].as_value()) else { return self; }; - let transform = document.metadata().document_to_viewport; + let mut transform = document.metadata().document_to_viewport; + + if let Some(transform_inputs) = transform_inputs { + let Some(&TaggedValue::DVec2(translation)) = transform_inputs[1].as_value() else { + return self; + }; + let Some(&TaggedValue::F64(angle)) = transform_inputs[2].as_value() else { + return self; + }; + let Some(&TaggedValue::DVec2(scale)) = transform_inputs[3].as_value() else { + return self; + }; + + transform = transform * DAffine2::from_scale_angle_translation(scale, angle, translation); + } tool_data.line_start = Some(start); tool_data.line_end = Some(end); From 24eb3519f171ac4641f5cabf10b6c04c2d26be51 Mon Sep 17 00:00:00 2001 From: Nitish-bot <86357181+Nitish-bot@users.noreply.github.com> Date: Fri, 14 Feb 2025 15:19:26 +0530 Subject: [PATCH 5/8] Add multi-layer editing support and cleanup --- .../messages/tool/tool_messages/line_tool.rs | 140 +++++++++--------- 1 file changed, 69 insertions(+), 71 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/line_tool.rs b/editor/src/messages/tool/tool_messages/line_tool.rs index 1f494d94bb..f93fc3e8d0 100644 --- a/editor/src/messages/tool/tool_messages/line_tool.rs +++ b/editor/src/messages/tool/tool_messages/line_tool.rs @@ -5,7 +5,7 @@ use crate::messages::portfolio::document::overlays::utility_types::OverlayContex use crate::messages::portfolio::document::utility_types::{document_metadata::LayerNodeIdentifier, network_interface::InputConnector}; 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, is_layer_fed_by_node_of_name, NodeGraphLayer}; +use crate::messages::tool::common_functionality::graph_modification_utils::{self, NodeGraphLayer}; use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapConstraint, SnapData, SnapManager, SnapTypeConfiguration}; use graph_craft::document::{value::TaggedValue, NodeId, NodeInput}; @@ -120,7 +120,6 @@ impl<'a> MessageHandler> for LineToo match self.fsm_state { LineToolFsmState::Ready => actions!(LineToolMessageDiscriminant; DragStart, PointerMove), LineToolFsmState::Drawing => actions!(LineToolMessageDiscriminant; DragStop, PointerMove, Abort), - LineToolFsmState::Editing => actions!(LineToolMessageDiscriminant; DragStop, PointerMove, Abort), } } } @@ -141,7 +140,6 @@ enum LineToolFsmState { #[default] Ready, Drawing, - Editing, } #[derive(Clone, Debug, Default)] @@ -150,11 +148,10 @@ struct LineToolData { drag_current: DVec2, angle: f64, weight: f64, - layer: Option, + selected_layers_with_position: HashMap, + editing_layer: Option, snap_manager: SnapManager, auto_panning: AutoPanning, - line_start: Option, - line_end: Option, start_click: bool, end_click: bool, } @@ -173,45 +170,41 @@ impl Fsm for LineToolFsmState { (_, LineToolMessage::Overlays(mut overlay_context)) => { tool_data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context); - if let Some(layer) = tool_data.layer { - let graph_layer = NodeGraphLayer::new(layer, &document.network_interface); - let node_inputs = graph_layer.find_node_inputs("Line").expect("Tool data layer should have a Line node"); - let transform_inputs = graph_layer.find_node_inputs("Transform"); - let (Some(&TaggedValue::DVec2(start)), Some(&TaggedValue::DVec2(end))) = (node_inputs[1].as_value(), node_inputs[2].as_value()) else { - return self; - }; - - let mut transform = document.metadata().document_to_viewport; - - if let Some(transform_inputs) = transform_inputs { - let Some(&TaggedValue::DVec2(translation)) = transform_inputs[1].as_value() else { - return self; - }; - let Some(&TaggedValue::F64(angle)) = transform_inputs[2].as_value() else { - return self; - }; - let Some(&TaggedValue::DVec2(scale)) = transform_inputs[3].as_value() else { - return self; + tool_data.selected_layers_with_position = document + .network_interface + .selected_nodes(&[]) + .unwrap() + .selected_visible_and_unlocked_layers(&document.network_interface) + .filter_map(|layer| { + let graph_layer = NodeGraphLayer::new(layer, &document.network_interface); + let node_inputs = graph_layer.find_node_inputs("Line")?; + let transform_inputs = graph_layer.find_node_inputs("Transform"); + + let (Some(&TaggedValue::DVec2(start)), Some(&TaggedValue::DVec2(end))) = (node_inputs[1].as_value(), node_inputs[2].as_value()) else { + return None; }; - transform = transform * DAffine2::from_scale_angle_translation(scale, angle, translation); - } + let mut document_transform = DAffine2::IDENTITY; + if let Some(transform_inputs) = transform_inputs { + let (Some(&TaggedValue::DVec2(translation)), Some(&TaggedValue::F64(angle)), Some(&TaggedValue::DVec2(scale))) = + (transform_inputs[1].as_value(), transform_inputs[2].as_value(), transform_inputs[3].as_value()) + else { + return None; + }; - tool_data.line_start = Some(start); - tool_data.line_end = Some(end); + document_transform = DAffine2::from_scale_angle_translation(scale, angle, translation); + } - if (start.x - end.x).abs() > f64::EPSILON * 1000. && (start.y - end.y).abs() > f64::EPSILON * 1000. { - overlay_context.square(transform.transform_point2(start), Some(6.), None, None); - overlay_context.square(transform.transform_point2(end), Some(6.), None, None); - } - } else { - tool_data.layer = document - .network_interface - .selected_nodes(&[]) - .unwrap() - .selected_visible_and_unlocked_layers(&document.network_interface) - .find(|layer| is_layer_fed_by_node_of_name(*layer, &document.network_interface, "Line")); - } + let viewport_transform = document.metadata().document_to_viewport; + let transform = document_transform * viewport_transform; + if (start.x - end.x).abs() > f64::EPSILON * 1000. && (start.y - end.y).abs() > f64::EPSILON * 1000. { + overlay_context.square(transform.transform_point2(start), Some(6.), None, None); + overlay_context.square(transform.transform_point2(end), Some(6.), None, None); + } + + Some((layer, [start, end])) + }) + .collect::>(); self } @@ -220,22 +213,35 @@ impl Fsm for LineToolFsmState { let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default()); tool_data.drag_start = snapped.snapped_point_document; - if let (Some(start), Some(end)) = (tool_data.line_start, tool_data.line_end) { - let transform = document.metadata().document_to_viewport; - + for (layer, [start, end]) in tool_data.selected_layers_with_position.iter() { + let transform = document.metadata().transform_to_viewport(*layer); let viewport_x = transform.transform_vector2(DVec2::X).normalize_or_zero() * BOUNDS_SELECT_THRESHOLD; let viewport_y = transform.transform_vector2(DVec2::Y).normalize_or_zero() * BOUNDS_SELECT_THRESHOLD; let threshold_x = transform.inverse().transform_vector2(viewport_x).length(); let threshold_y = transform.inverse().transform_vector2(viewport_y).length(); + let graph_layer = NodeGraphLayer::new(*layer, &document.network_interface); + let transform_inputs = graph_layer.find_node_inputs("Transform"); + + let mut transform = DAffine2::IDENTITY; + if let Some(transform_inputs) = transform_inputs { + if let (Some(&TaggedValue::DVec2(translation)), Some(&TaggedValue::F64(angle)), Some(&TaggedValue::DVec2(scale))) = + (transform_inputs[1].as_value(), transform_inputs[2].as_value(), transform_inputs[3].as_value()) + { + transform = DAffine2::from_scale_angle_translation(scale, angle, translation); + }; + } + + let [start, end] = [start, end].map(|point| transform.transform_point2(*point)); tool_data.start_click = (tool_data.drag_start.y - start.y).abs() < threshold_y && (tool_data.drag_start.x - start.x).abs() < threshold_x; tool_data.end_click = (tool_data.drag_start.y - end.y).abs() < threshold_y && (tool_data.drag_start.x - end.x).abs() < threshold_x; if tool_data.start_click || tool_data.end_click { tool_data.drag_start = if tool_data.start_click { end } else { start }; - return LineToolFsmState::Editing; + tool_data.editing_layer = Some(*layer); + return LineToolFsmState::Drawing; } - }; + } responses.add(DocumentMessage::StartTransaction); @@ -258,17 +264,21 @@ impl Fsm for LineToolFsmState { tool_options.stroke.apply_stroke(tool_options.line_weight, layer, responses); - tool_data.layer = Some(layer); + tool_data.editing_layer = Some(layer); tool_data.angle = 0.; tool_data.weight = tool_options.line_weight; LineToolFsmState::Drawing } - (LineToolFsmState::Drawing | LineToolFsmState::Editing, LineToolMessage::PointerMove { center, snap_angle, lock_angle }) => { - tool_data.drag_current = input.mouse.position; // tool_data.snap_manager.snap_position(responses, document, input.mouse.position); + (LineToolFsmState::Drawing, LineToolMessage::PointerMove { center, snap_angle, lock_angle }) => { + if tool_data.editing_layer.is_none() { + return LineToolFsmState::Ready; + } + + tool_data.drag_current = document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position); // tool_data.snap_manager.snap_position(responses, document, input.mouse.position); let keyboard = &input.keyboard; - let ignore = if let Some(layer) = tool_data.layer { vec![layer] } else { vec![] }; + let ignore = vec![tool_data.editing_layer.unwrap()]; let snap_data = SnapData::ignore(document, input, &ignore); let mut document_points = generate_line(tool_data, snap_data, keyboard.key(lock_angle), keyboard.key(snap_angle), keyboard.key(center)); @@ -276,7 +286,7 @@ impl Fsm for LineToolFsmState { document_points.swap(0, 1); } - let Some(node_id) = graph_modification_utils::get_line_id(tool_data.layer.unwrap(), &document.network_interface) else { + let Some(node_id) = graph_modification_utils::get_line_id(tool_data.editing_layer.unwrap(), &document.network_interface) else { return LineToolFsmState::Ready; }; @@ -304,7 +314,7 @@ impl Fsm for LineToolFsmState { responses.add(OverlaysMessage::Draw); self } - (LineToolFsmState::Drawing | LineToolFsmState::Editing, LineToolMessage::PointerOutsideViewport { .. }) => { + (LineToolFsmState::Drawing, LineToolMessage::PointerOutsideViewport { .. }) => { // Auto-panning let _ = tool_data.auto_panning.shift_viewport(input, responses); @@ -320,21 +330,18 @@ impl Fsm for LineToolFsmState { state } - (LineToolFsmState::Drawing | LineToolFsmState::Editing, LineToolMessage::DragStop) => { + (LineToolFsmState::Drawing, LineToolMessage::DragStop) => { tool_data.snap_manager.cleanup(responses); + tool_data.editing_layer.take(); input.mouse.finish_transaction(tool_data.drag_start, responses); - tool_data.layer.take(); LineToolFsmState::Ready } (LineToolFsmState::Drawing, LineToolMessage::Abort) => { tool_data.snap_manager.cleanup(responses); - responses.add(DocumentMessage::AbortTransaction); - tool_data.layer.take(); - LineToolFsmState::Ready - } - (LineToolFsmState::Editing, LineToolMessage::Abort) => { - tool_data.snap_manager.cleanup(responses); - tool_data.layer.take(); + tool_data.editing_layer.take(); + if !tool_data.start_click && !tool_data.end_click { + responses.add(DocumentMessage::AbortTransaction); + } LineToolFsmState::Ready } (_, LineToolMessage::WorkingColorChanged) => { @@ -364,14 +371,6 @@ impl Fsm for LineToolFsmState { HintInfo::keys([Key::Control], "Lock Angle"), ]), ]), - LineToolFsmState::Editing => HintData(vec![ - HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]), - HintGroup(vec![ - HintInfo::keys([Key::Shift], "15° Increments"), - HintInfo::keys([Key::Alt], "From Center"), - HintInfo::keys([Key::Control], "Lock Angle"), - ]), - ]), }; responses.add(FrontendMessage::UpdateInputHints { hint_data }); @@ -383,8 +382,7 @@ impl Fsm for LineToolFsmState { } fn generate_line(tool_data: &mut LineToolData, 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.drag_start, document_to_viewport.inverse().transform_point2(tool_data.drag_current)]; + let mut document_points = [tool_data.drag_start, 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(); From 513ae3461d56aa4b603bfe0cebe087b3460867f2 Mon Sep 17 00:00:00 2001 From: Nitish-bot <86357181+Nitish-bot@users.noreply.github.com> Date: Sun, 16 Feb 2025 20:31:42 +0530 Subject: [PATCH 6/8] Fix transform breaking the handles --- .../messages/tool/tool_messages/line_tool.rs | 65 +++++++------------ 1 file changed, 22 insertions(+), 43 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/line_tool.rs b/editor/src/messages/tool/tool_messages/line_tool.rs index f93fc3e8d0..ad183d07de 100644 --- a/editor/src/messages/tool/tool_messages/line_tool.rs +++ b/editor/src/messages/tool/tool_messages/line_tool.rs @@ -152,8 +152,8 @@ struct LineToolData { editing_layer: Option, snap_manager: SnapManager, auto_panning: AutoPanning, - start_click: bool, - end_click: bool, + // True means dragging end point, false means dragging start point + dragging_end: Option, } impl Fsm for LineToolFsmState { @@ -176,27 +176,13 @@ impl Fsm for LineToolFsmState { .unwrap() .selected_visible_and_unlocked_layers(&document.network_interface) .filter_map(|layer| { - let graph_layer = NodeGraphLayer::new(layer, &document.network_interface); - let node_inputs = graph_layer.find_node_inputs("Line")?; - let transform_inputs = graph_layer.find_node_inputs("Transform"); + let node_inputs = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Line")?; let (Some(&TaggedValue::DVec2(start)), Some(&TaggedValue::DVec2(end))) = (node_inputs[1].as_value(), node_inputs[2].as_value()) else { return None; }; - let mut document_transform = DAffine2::IDENTITY; - if let Some(transform_inputs) = transform_inputs { - let (Some(&TaggedValue::DVec2(translation)), Some(&TaggedValue::F64(angle)), Some(&TaggedValue::DVec2(scale))) = - (transform_inputs[1].as_value(), transform_inputs[2].as_value(), transform_inputs[3].as_value()) - else { - return None; - }; - - document_transform = DAffine2::from_scale_angle_translation(scale, angle, translation); - } - - let viewport_transform = document.metadata().document_to_viewport; - let transform = document_transform * viewport_transform; + let transform = document.metadata().transform_to_viewport(layer); if (start.x - end.x).abs() > f64::EPSILON * 1000. && (start.y - end.y).abs() > f64::EPSILON * 1000. { overlay_context.square(transform.transform_point2(start), Some(6.), None, None); overlay_context.square(transform.transform_point2(end), Some(6.), None, None); @@ -213,31 +199,22 @@ impl Fsm for LineToolFsmState { let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default()); tool_data.drag_start = snapped.snapped_point_document; - for (layer, [start, end]) in tool_data.selected_layers_with_position.iter() { + for (layer, [document_start, document_end]) in tool_data.selected_layers_with_position.iter() { let transform = document.metadata().transform_to_viewport(*layer); let viewport_x = transform.transform_vector2(DVec2::X).normalize_or_zero() * BOUNDS_SELECT_THRESHOLD; let viewport_y = transform.transform_vector2(DVec2::Y).normalize_or_zero() * BOUNDS_SELECT_THRESHOLD; let threshold_x = transform.inverse().transform_vector2(viewport_x).length(); let threshold_y = transform.inverse().transform_vector2(viewport_y).length(); - let graph_layer = NodeGraphLayer::new(*layer, &document.network_interface); - let transform_inputs = graph_layer.find_node_inputs("Transform"); + let drag_start = input.mouse.position; + let [start, end] = [document_start, document_end].map(|point| transform.transform_point2(*point)); - let mut transform = DAffine2::IDENTITY; - if let Some(transform_inputs) = transform_inputs { - if let (Some(&TaggedValue::DVec2(translation)), Some(&TaggedValue::F64(angle)), Some(&TaggedValue::DVec2(scale))) = - (transform_inputs[1].as_value(), transform_inputs[2].as_value(), transform_inputs[3].as_value()) - { - transform = DAffine2::from_scale_angle_translation(scale, angle, translation); - }; - } + let start_click = (drag_start.y - start.y).abs() < threshold_y && (drag_start.x - start.x).abs() < threshold_x; + let end_click = (drag_start.y - end.y).abs() < threshold_y && (drag_start.x - end.x).abs() < threshold_x; - let [start, end] = [start, end].map(|point| transform.transform_point2(*point)); - tool_data.start_click = (tool_data.drag_start.y - start.y).abs() < threshold_y && (tool_data.drag_start.x - start.x).abs() < threshold_x; - tool_data.end_click = (tool_data.drag_start.y - end.y).abs() < threshold_y && (tool_data.drag_start.x - end.x).abs() < threshold_x; - - if tool_data.start_click || tool_data.end_click { - tool_data.drag_start = if tool_data.start_click { end } else { start }; + if start_click || end_click { + tool_data.dragging_end = Some(end_click); + tool_data.drag_start = if end_click { *document_start } else { *document_end }; tool_data.editing_layer = Some(*layer); return LineToolFsmState::Drawing; } @@ -271,22 +248,24 @@ impl Fsm for LineToolFsmState { LineToolFsmState::Drawing } (LineToolFsmState::Drawing, LineToolMessage::PointerMove { center, snap_angle, lock_angle }) => { - if tool_data.editing_layer.is_none() { + let Some(layer) = tool_data.editing_layer else { return LineToolFsmState::Ready; - } + }; - tool_data.drag_current = document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position); // tool_data.snap_manager.snap_position(responses, document, input.mouse.position); + tool_data.drag_current = document.metadata().transform_to_viewport(layer).inverse().transform_point2(input.mouse.position); let keyboard = &input.keyboard; - let ignore = vec![tool_data.editing_layer.unwrap()]; + let ignore = vec![layer]; let snap_data = SnapData::ignore(document, input, &ignore); let mut document_points = generate_line(tool_data, snap_data, keyboard.key(lock_angle), keyboard.key(snap_angle), keyboard.key(center)); - if tool_data.start_click { - document_points.swap(0, 1); + if let Some(dragging_end) = tool_data.dragging_end { + if !dragging_end { + document_points.swap(0, 1); + } } - let Some(node_id) = graph_modification_utils::get_line_id(tool_data.editing_layer.unwrap(), &document.network_interface) else { + let Some(node_id) = graph_modification_utils::get_line_id(layer, &document.network_interface) else { return LineToolFsmState::Ready; }; @@ -339,7 +318,7 @@ impl Fsm for LineToolFsmState { (LineToolFsmState::Drawing, LineToolMessage::Abort) => { tool_data.snap_manager.cleanup(responses); tool_data.editing_layer.take(); - if !tool_data.start_click && !tool_data.end_click { + if tool_data.dragging_end.is_none() { responses.add(DocumentMessage::AbortTransaction); } LineToolFsmState::Ready From 4222d8b67df89ae7882dc41dabeded3b0fa42c43 Mon Sep 17 00:00:00 2001 From: Nitish-bot <86357181+Nitish-bot@users.noreply.github.com> Date: Sun, 16 Feb 2025 20:57:36 +0530 Subject: [PATCH 7/8] line between handles --- editor/src/messages/tool/tool_messages/line_tool.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/line_tool.rs b/editor/src/messages/tool/tool_messages/line_tool.rs index ad183d07de..9108effc8a 100644 --- a/editor/src/messages/tool/tool_messages/line_tool.rs +++ b/editor/src/messages/tool/tool_messages/line_tool.rs @@ -182,10 +182,11 @@ impl Fsm for LineToolFsmState { return None; }; - let transform = document.metadata().transform_to_viewport(layer); + let [viewport_start, viewport_end] = [start, end].map(|point| document.metadata().transform_to_viewport(layer).transform_point2(point)); if (start.x - end.x).abs() > f64::EPSILON * 1000. && (start.y - end.y).abs() > f64::EPSILON * 1000. { - overlay_context.square(transform.transform_point2(start), Some(6.), None, None); - overlay_context.square(transform.transform_point2(end), Some(6.), None, None); + overlay_context.line(viewport_start, viewport_end, None); + overlay_context.square(viewport_start, Some(6.), None, None); + overlay_context.square(viewport_end, Some(6.), None, None); } Some((layer, [start, end])) From 5195e63c19bef6ff42f5300e03bd281aa104dcdb Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Thu, 6 Mar 2025 04:31:15 -0800 Subject: [PATCH 8/8] Code review --- .../messages/tool/tool_messages/line_tool.rs | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/line_tool.rs b/editor/src/messages/tool/tool_messages/line_tool.rs index 9108effc8a..e979dec9e2 100644 --- a/editor/src/messages/tool/tool_messages/line_tool.rs +++ b/editor/src/messages/tool/tool_messages/line_tool.rs @@ -142,6 +142,12 @@ enum LineToolFsmState { Drawing, } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum LineEnd { + Start, + End, +} + #[derive(Clone, Debug, Default)] struct LineToolData { drag_start: DVec2, @@ -152,8 +158,7 @@ struct LineToolData { editing_layer: Option, snap_manager: SnapManager, auto_panning: AutoPanning, - // True means dragging end point, false means dragging start point - dragging_end: Option, + dragging_endpoint: Option, } impl Fsm for LineToolFsmState { @@ -214,7 +219,7 @@ impl Fsm for LineToolFsmState { let end_click = (drag_start.y - end.y).abs() < threshold_y && (drag_start.x - end.x).abs() < threshold_x; if start_click || end_click { - tool_data.dragging_end = Some(end_click); + tool_data.dragging_endpoint = Some(if end_click { LineEnd::End } else { LineEnd::Start }); tool_data.drag_start = if end_click { *document_start } else { *document_end }; tool_data.editing_layer = Some(*layer); return LineToolFsmState::Drawing; @@ -249,9 +254,7 @@ impl Fsm for LineToolFsmState { LineToolFsmState::Drawing } (LineToolFsmState::Drawing, LineToolMessage::PointerMove { center, snap_angle, lock_angle }) => { - let Some(layer) = tool_data.editing_layer else { - return LineToolFsmState::Ready; - }; + let Some(layer) = tool_data.editing_layer else { return LineToolFsmState::Ready }; tool_data.drag_current = document.metadata().transform_to_viewport(layer).inverse().transform_point2(input.mouse.position); @@ -260,10 +263,8 @@ impl Fsm for LineToolFsmState { let snap_data = SnapData::ignore(document, input, &ignore); let mut document_points = generate_line(tool_data, snap_data, keyboard.key(lock_angle), keyboard.key(snap_angle), keyboard.key(center)); - if let Some(dragging_end) = tool_data.dragging_end { - if !dragging_end { - document_points.swap(0, 1); - } + if tool_data.dragging_endpoint == Some(LineEnd::Start) { + document_points.swap(0, 1); } let Some(node_id) = graph_modification_utils::get_line_id(layer, &document.network_interface) else { @@ -287,7 +288,7 @@ impl Fsm for LineToolFsmState { ]; tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses); - self + LineToolFsmState::Drawing } (_, LineToolMessage::PointerMove { .. }) => { tool_data.snap_manager.preview_draw(&SnapData::new(document, input), input.mouse.position); @@ -298,7 +299,7 @@ impl Fsm for LineToolFsmState { // Auto-panning let _ = tool_data.auto_panning.shift_viewport(input, responses); - self + LineToolFsmState::Drawing } (state, LineToolMessage::PointerOutsideViewport { center, lock_angle, snap_angle }) => { // Auto-panning @@ -319,7 +320,7 @@ impl Fsm for LineToolFsmState { (LineToolFsmState::Drawing, LineToolMessage::Abort) => { tool_data.snap_manager.cleanup(responses); tool_data.editing_layer.take(); - if tool_data.dragging_end.is_none() { + if tool_data.dragging_endpoint.is_none() { responses.add(DocumentMessage::AbortTransaction); } LineToolFsmState::Ready