mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-04 13:30:48 +00:00
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 changed files with 88 additions and 27 deletions
|
@ -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<LayerNodeIdentifier>,
|
||||
selected_layers_with_position: HashMap<LayerNodeIdentifier, [DVec2; 2]>,
|
||||
editing_layer: Option<LayerNodeIdentifier>,
|
||||
snap_manager: SnapManager,
|
||||
auto_panning: AutoPanning,
|
||||
dragging_endpoint: Option<LineEnd>,
|
||||
}
|
||||
|
||||
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::<HashMap<LayerNodeIdentifier, [DVec2; 2]>>();
|
||||
|
||||
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<Message>) {
|
||||
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
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue