diff --git a/editor/src/messages/tool/tool_messages/line_tool.rs b/editor/src/messages/tool/tool_messages/line_tool.rs index 56823be80b..e979dec9e2 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, NodeGraphLayer}; use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapConstraint, SnapData, SnapManager, SnapTypeConfiguration}; use graph_craft::document::{value::TaggedValue, NodeId, NodeInput}; @@ -142,15 +142,23 @@ enum LineToolFsmState { Drawing, } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum LineEnd { + Start, + End, +} + #[derive(Clone, Debug, Default)] struct LineToolData { drag_start: DVec2, drag_current: DVec2, angle: f64, weight: f64, - layer: Option, + selected_layers_with_position: HashMap, + editing_layer: Option, snap_manager: SnapManager, auto_panning: AutoPanning, + dragging_endpoint: Option, } impl Fsm for LineToolFsmState { @@ -166,6 +174,30 @@ 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); + + tool_data.selected_layers_with_position = document + .network_interface + .selected_nodes(&[]) + .unwrap() + .selected_visible_and_unlocked_layers(&document.network_interface) + .filter_map(|layer| { + 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 [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.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])) + }) + .collect::>(); + self } (LineToolFsmState::Ready, LineToolMessage::DragStart) => { @@ -173,6 +205,27 @@ 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, [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 drag_start = input.mouse.position; + let [start, end] = [document_start, document_end].map(|point| transform.transform_point2(*point)); + + 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; + + if start_click || 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; + } + } + responses.add(DocumentMessage::StartTransaction); let node_type = resolve_document_node_type("Line").expect("Line node does not exist"); @@ -194,19 +247,39 @@ 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, 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 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); let keyboard = &input.keyboard; - let ignore = if let Some(layer) = tool_data.layer { vec![layer] } else { vec![] }; + let ignore = vec![layer]; 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.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 { + 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 = [ @@ -240,14 +313,16 @@ impl Fsm for LineToolFsmState { } (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 = None; LineToolFsmState::Ready } (LineToolFsmState::Drawing, LineToolMessage::Abort) => { tool_data.snap_manager.cleanup(responses); - responses.add(DocumentMessage::AbortTransaction); - tool_data.layer = None; + tool_data.editing_layer.take(); + if tool_data.dragging_endpoint.is_none() { + responses.add(DocumentMessage::AbortTransaction); + } LineToolFsmState::Ready } (_, LineToolMessage::WorkingColorChanged) => { @@ -287,9 +362,8 @@ 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) { - 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)]; +fn generate_line(tool_data: &mut LineToolData, snap_data: SnapData, lock_angle: bool, snap_angle: bool, center: bool) -> [DVec2; 2] { + 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(); @@ -347,18 +421,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 }