Skip to content

Commit 8f7dd20

Browse files
Nitish-botKeavon
andauthored
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 <[email protected]>
1 parent 27cf168 commit 8f7dd20

File tree

1 file changed

+88
-27
lines changed

1 file changed

+88
-27
lines changed

editor/src/messages/tool/tool_messages/line_tool.rs

Lines changed: 88 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use super::tool_prelude::*;
2-
use crate::consts::{DEFAULT_STROKE_WIDTH, LINE_ROTATE_SNAP_ANGLE};
2+
use crate::consts::{BOUNDS_SELECT_THRESHOLD, DEFAULT_STROKE_WIDTH, LINE_ROTATE_SNAP_ANGLE};
33
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
44
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
55
use crate::messages::portfolio::document::utility_types::{document_metadata::LayerNodeIdentifier, network_interface::InputConnector};
66
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
77
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
8-
use crate::messages::tool::common_functionality::graph_modification_utils;
8+
use crate::messages::tool::common_functionality::graph_modification_utils::{self, NodeGraphLayer};
99
use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapConstraint, SnapData, SnapManager, SnapTypeConfiguration};
1010

1111
use graph_craft::document::{value::TaggedValue, NodeId, NodeInput};
@@ -142,15 +142,23 @@ enum LineToolFsmState {
142142
Drawing,
143143
}
144144

145+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
146+
enum LineEnd {
147+
Start,
148+
End,
149+
}
150+
145151
#[derive(Clone, Debug, Default)]
146152
struct LineToolData {
147153
drag_start: DVec2,
148154
drag_current: DVec2,
149155
angle: f64,
150156
weight: f64,
151-
layer: Option<LayerNodeIdentifier>,
157+
selected_layers_with_position: HashMap<LayerNodeIdentifier, [DVec2; 2]>,
158+
editing_layer: Option<LayerNodeIdentifier>,
152159
snap_manager: SnapManager,
153160
auto_panning: AutoPanning,
161+
dragging_endpoint: Option<LineEnd>,
154162
}
155163

156164
impl Fsm for LineToolFsmState {
@@ -166,13 +174,58 @@ impl Fsm for LineToolFsmState {
166174
match (self, event) {
167175
(_, LineToolMessage::Overlays(mut overlay_context)) => {
168176
tool_data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context);
177+
178+
tool_data.selected_layers_with_position = document
179+
.network_interface
180+
.selected_nodes(&[])
181+
.unwrap()
182+
.selected_visible_and_unlocked_layers(&document.network_interface)
183+
.filter_map(|layer| {
184+
let node_inputs = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Line")?;
185+
186+
let (Some(&TaggedValue::DVec2(start)), Some(&TaggedValue::DVec2(end))) = (node_inputs[1].as_value(), node_inputs[2].as_value()) else {
187+
return None;
188+
};
189+
190+
let [viewport_start, viewport_end] = [start, end].map(|point| document.metadata().transform_to_viewport(layer).transform_point2(point));
191+
if (start.x - end.x).abs() > f64::EPSILON * 1000. && (start.y - end.y).abs() > f64::EPSILON * 1000. {
192+
overlay_context.line(viewport_start, viewport_end, None);
193+
overlay_context.square(viewport_start, Some(6.), None, None);
194+
overlay_context.square(viewport_end, Some(6.), None, None);
195+
}
196+
197+
Some((layer, [start, end]))
198+
})
199+
.collect::<HashMap<LayerNodeIdentifier, [DVec2; 2]>>();
200+
169201
self
170202
}
171203
(LineToolFsmState::Ready, LineToolMessage::DragStart) => {
172204
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
173205
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default());
174206
tool_data.drag_start = snapped.snapped_point_document;
175207

208+
for (layer, [document_start, document_end]) in tool_data.selected_layers_with_position.iter() {
209+
let transform = document.metadata().transform_to_viewport(*layer);
210+
let viewport_x = transform.transform_vector2(DVec2::X).normalize_or_zero() * BOUNDS_SELECT_THRESHOLD;
211+
let viewport_y = transform.transform_vector2(DVec2::Y).normalize_or_zero() * BOUNDS_SELECT_THRESHOLD;
212+
let threshold_x = transform.inverse().transform_vector2(viewport_x).length();
213+
let threshold_y = transform.inverse().transform_vector2(viewport_y).length();
214+
215+
let drag_start = input.mouse.position;
216+
let [start, end] = [document_start, document_end].map(|point| transform.transform_point2(*point));
217+
218+
let start_click = (drag_start.y - start.y).abs() < threshold_y && (drag_start.x - start.x).abs() < threshold_x;
219+
let end_click = (drag_start.y - end.y).abs() < threshold_y && (drag_start.x - end.x).abs() < threshold_x;
220+
221+
if start_click || end_click {
222+
tool_data.dragging_endpoint = Some(if end_click { LineEnd::End } else { LineEnd::Start });
223+
tool_data.drag_start = if end_click { *document_start } else { *document_end };
224+
tool_data.editing_layer = Some(*layer);
225+
return LineToolFsmState::Drawing;
226+
}
227+
}
228+
176229
responses.add(DocumentMessage::StartTransaction);
177230

178231
let node_type = resolve_document_node_type("Line").expect("Line node does not exist");
@@ -194,19 +247,39 @@ impl Fsm for LineToolFsmState {
194247

195248
tool_options.stroke.apply_stroke(tool_options.line_weight, layer, responses);
196249

197-
tool_data.layer = Some(layer);
250+
tool_data.editing_layer = Some(layer);
198251
tool_data.angle = 0.;
199252
tool_data.weight = tool_options.line_weight;
200253

201254
LineToolFsmState::Drawing
202255
}
203256
(LineToolFsmState::Drawing, LineToolMessage::PointerMove { center, snap_angle, lock_angle }) => {
204-
tool_data.drag_current = input.mouse.position; // tool_data.snap_manager.snap_position(responses, document, input.mouse.position);
257+
let Some(layer) = tool_data.editing_layer else { return LineToolFsmState::Ready };
258+
259+
tool_data.drag_current = document.metadata().transform_to_viewport(layer).inverse().transform_point2(input.mouse.position);
205260

206261
let keyboard = &input.keyboard;
207-
let ignore = if let Some(layer) = tool_data.layer { vec![layer] } else { vec![] };
262+
let ignore = vec![layer];
208263
let snap_data = SnapData::ignore(document, input, &ignore);
209-
generate_line(tool_data, snap_data, keyboard.key(lock_angle), keyboard.key(snap_angle), keyboard.key(center), responses);
264+
let mut document_points = generate_line(tool_data, snap_data, keyboard.key(lock_angle), keyboard.key(snap_angle), keyboard.key(center));
265+
266+
if tool_data.dragging_endpoint == Some(LineEnd::Start) {
267+
document_points.swap(0, 1);
268+
}
269+
270+
let Some(node_id) = graph_modification_utils::get_line_id(layer, &document.network_interface) else {
271+
return LineToolFsmState::Ready;
272+
};
273+
274+
responses.add(NodeGraphMessage::SetInput {
275+
input_connector: InputConnector::node(node_id, 1),
276+
input: NodeInput::value(TaggedValue::DVec2(document_points[0]), false),
277+
});
278+
responses.add(NodeGraphMessage::SetInput {
279+
input_connector: InputConnector::node(node_id, 2),
280+
input: NodeInput::value(TaggedValue::DVec2(document_points[1]), false),
281+
});
282+
responses.add(NodeGraphMessage::RunDocumentGraph);
210283

211284
// Auto-panning
212285
let messages = [
@@ -240,14 +313,16 @@ impl Fsm for LineToolFsmState {
240313
}
241314
(LineToolFsmState::Drawing, LineToolMessage::DragStop) => {
242315
tool_data.snap_manager.cleanup(responses);
316+
tool_data.editing_layer.take();
243317
input.mouse.finish_transaction(tool_data.drag_start, responses);
244-
tool_data.layer = None;
245318
LineToolFsmState::Ready
246319
}
247320
(LineToolFsmState::Drawing, LineToolMessage::Abort) => {
248321
tool_data.snap_manager.cleanup(responses);
249-
responses.add(DocumentMessage::AbortTransaction);
250-
tool_data.layer = None;
322+
tool_data.editing_layer.take();
323+
if tool_data.dragging_endpoint.is_none() {
324+
responses.add(DocumentMessage::AbortTransaction);
325+
}
251326
LineToolFsmState::Ready
252327
}
253328
(_, LineToolMessage::WorkingColorChanged) => {
@@ -287,9 +362,8 @@ impl Fsm for LineToolFsmState {
287362
}
288363
}
289364

290-
fn generate_line(tool_data: &mut LineToolData, snap_data: SnapData, lock_angle: bool, snap_angle: bool, center: bool, responses: &mut VecDeque<Message>) {
291-
let document_to_viewport = snap_data.document.metadata().document_to_viewport;
292-
let mut document_points = [tool_data.drag_start, document_to_viewport.inverse().transform_point2(tool_data.drag_current)];
365+
fn generate_line(tool_data: &mut LineToolData, snap_data: SnapData, lock_angle: bool, snap_angle: bool, center: bool) -> [DVec2; 2] {
366+
let mut document_points = [tool_data.drag_start, tool_data.drag_current];
293367

294368
let mut angle = -(document_points[1] - document_points[0]).angle_to(DVec2::X);
295369
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:
347421
snap.update_indicator(snapped);
348422
}
349423

350-
let Some(node_id) = graph_modification_utils::get_line_id(tool_data.layer.unwrap(), &snap_data.document.network_interface) else {
351-
return;
352-
};
353-
354-
responses.add(NodeGraphMessage::SetInput {
355-
input_connector: InputConnector::node(node_id, 1),
356-
input: NodeInput::value(TaggedValue::DVec2(document_points[0]), false),
357-
});
358-
responses.add(NodeGraphMessage::SetInput {
359-
input_connector: InputConnector::node(node_id, 2),
360-
input: NodeInput::value(TaggedValue::DVec2(document_points[1]), false),
361-
});
362-
363-
responses.add(NodeGraphMessage::RunDocumentGraph);
424+
document_points
364425
}

0 commit comments

Comments
 (0)