Allow the Line tool to drag start and end points of line layers (#2278)
* Initial attempt * Allow editing and display overlays * Fix modifier keys * Handles show up correctly when layer is transformed * Add multi-layer editing support and cleanup * Fix transform breaking the handles * line between handles * Code review --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
27cf1682bc
commit
8f7dd2021d
|
|
@ -1,11 +1,11 @@
|
||||||
use super::tool_prelude::*;
|
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::node_graph::document_node_definitions::resolve_document_node_type;
|
||||||
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
|
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::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::auto_panning::AutoPanning;
|
||||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
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 crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapConstraint, SnapData, SnapManager, SnapTypeConfiguration};
|
||||||
|
|
||||||
use graph_craft::document::{value::TaggedValue, NodeId, NodeInput};
|
use graph_craft::document::{value::TaggedValue, NodeId, NodeInput};
|
||||||
|
|
@ -142,15 +142,23 @@ enum LineToolFsmState {
|
||||||
Drawing,
|
Drawing,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
enum LineEnd {
|
||||||
|
Start,
|
||||||
|
End,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct LineToolData {
|
struct LineToolData {
|
||||||
drag_start: DVec2,
|
drag_start: DVec2,
|
||||||
drag_current: DVec2,
|
drag_current: DVec2,
|
||||||
angle: f64,
|
angle: f64,
|
||||||
weight: f64,
|
weight: f64,
|
||||||
layer: Option<LayerNodeIdentifier>,
|
selected_layers_with_position: HashMap<LayerNodeIdentifier, [DVec2; 2]>,
|
||||||
|
editing_layer: Option<LayerNodeIdentifier>,
|
||||||
snap_manager: SnapManager,
|
snap_manager: SnapManager,
|
||||||
auto_panning: AutoPanning,
|
auto_panning: AutoPanning,
|
||||||
|
dragging_endpoint: Option<LineEnd>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fsm for LineToolFsmState {
|
impl Fsm for LineToolFsmState {
|
||||||
|
|
@ -166,6 +174,30 @@ impl Fsm for LineToolFsmState {
|
||||||
match (self, event) {
|
match (self, event) {
|
||||||
(_, LineToolMessage::Overlays(mut overlay_context)) => {
|
(_, LineToolMessage::Overlays(mut overlay_context)) => {
|
||||||
tool_data.snap_manager.draw_overlays(SnapData::new(document, input), &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::<HashMap<LayerNodeIdentifier, [DVec2; 2]>>();
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
(LineToolFsmState::Ready, LineToolMessage::DragStart) => {
|
(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());
|
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default());
|
||||||
tool_data.drag_start = snapped.snapped_point_document;
|
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);
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
|
|
||||||
let node_type = resolve_document_node_type("Line").expect("Line node does not exist");
|
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_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.angle = 0.;
|
||||||
tool_data.weight = tool_options.line_weight;
|
tool_data.weight = tool_options.line_weight;
|
||||||
|
|
||||||
LineToolFsmState::Drawing
|
LineToolFsmState::Drawing
|
||||||
}
|
}
|
||||||
(LineToolFsmState::Drawing, LineToolMessage::PointerMove { center, snap_angle, lock_angle }) => {
|
(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 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);
|
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
|
// Auto-panning
|
||||||
let messages = [
|
let messages = [
|
||||||
|
|
@ -240,14 +313,16 @@ impl Fsm for LineToolFsmState {
|
||||||
}
|
}
|
||||||
(LineToolFsmState::Drawing, LineToolMessage::DragStop) => {
|
(LineToolFsmState::Drawing, LineToolMessage::DragStop) => {
|
||||||
tool_data.snap_manager.cleanup(responses);
|
tool_data.snap_manager.cleanup(responses);
|
||||||
|
tool_data.editing_layer.take();
|
||||||
input.mouse.finish_transaction(tool_data.drag_start, responses);
|
input.mouse.finish_transaction(tool_data.drag_start, responses);
|
||||||
tool_data.layer = None;
|
|
||||||
LineToolFsmState::Ready
|
LineToolFsmState::Ready
|
||||||
}
|
}
|
||||||
(LineToolFsmState::Drawing, LineToolMessage::Abort) => {
|
(LineToolFsmState::Drawing, LineToolMessage::Abort) => {
|
||||||
tool_data.snap_manager.cleanup(responses);
|
tool_data.snap_manager.cleanup(responses);
|
||||||
responses.add(DocumentMessage::AbortTransaction);
|
tool_data.editing_layer.take();
|
||||||
tool_data.layer = None;
|
if tool_data.dragging_endpoint.is_none() {
|
||||||
|
responses.add(DocumentMessage::AbortTransaction);
|
||||||
|
}
|
||||||
LineToolFsmState::Ready
|
LineToolFsmState::Ready
|
||||||
}
|
}
|
||||||
(_, LineToolMessage::WorkingColorChanged) => {
|
(_, 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<Message>) {
|
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, tool_data.drag_current];
|
||||||
let mut document_points = [tool_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 angle = -(document_points[1] - document_points[0]).angle_to(DVec2::X);
|
||||||
let mut line_length = (document_points[1] - document_points[0]).length();
|
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);
|
snap.update_indicator(snapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(node_id) = graph_modification_utils::get_line_id(tool_data.layer.unwrap(), &snap_data.document.network_interface) else {
|
document_points
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue