Skip to content

Commit 674db97

Browse files
indierustyKeavon
andauthored
Implement merging pairs of {paths, splines} with the Pen and Spline tools (#2292)
* refactor * impl find_spline function * impl merge_layers() to merge two spline layer to one spline layer * impl merging spline to another spline which are not in the same layer * impl merging of spline with path * impl merge spline start endpoint and last endpoint * fix naming * fix handle transformation * refactor * fix merging with path with only one segment * refactor --------- Co-authored-by: Keavon Chambers <[email protected]>
1 parent fb13d58 commit 674db97

File tree

2 files changed

+209
-615
lines changed

2 files changed

+209
-615
lines changed

editor/src/messages/tool/common_functionality/graph_modification_utils.rs

Lines changed: 105 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,34 +10,62 @@ use graphene_core::raster::image::ImageFrame;
1010
use graphene_core::raster::BlendMode;
1111
use graphene_core::text::{Font, TypesettingConfig};
1212
use graphene_core::vector::style::Gradient;
13-
use graphene_core::vector::PointId;
1413
use graphene_core::Color;
14+
use graphene_std::vector::{ManipulatorPointId, PointId, SegmentId, VectorModificationType};
1515

1616
use glam::DVec2;
1717
use std::collections::VecDeque;
1818

19-
pub fn merge_layers(document: &DocumentMessageHandler, current_layer: LayerNodeIdentifier, other_layer: LayerNodeIdentifier, responses: &mut VecDeque<Message>) {
19+
/// Returns the ID of the first Spline node in the horizontal flow which is not followed by a `Path` node, or `None` if none exists.
20+
pub fn find_spline(document: &DocumentMessageHandler, layer: LayerNodeIdentifier) -> Option<NodeId> {
21+
document
22+
.network_interface
23+
.upstream_flow_back_from_nodes([layer.to_node()].to_vec(), &[], FlowType::HorizontalFlow)
24+
.map(|node_id| (document.network_interface.reference(&node_id, &[]).unwrap(), node_id))
25+
.take_while(|(reference, _)| reference.as_ref().is_some_and(|node_ref| node_ref != "Path"))
26+
.find(|(reference, _)| reference.as_ref().is_some_and(|node_ref| node_ref == "Spline"))
27+
.map(|node| node.1)
28+
}
29+
30+
/// Merge `second_layer` to the `first_layer`.
31+
pub fn merge_layers(document: &DocumentMessageHandler, first_layer: LayerNodeIdentifier, second_layer: LayerNodeIdentifier, responses: &mut VecDeque<Message>) {
32+
if first_layer == second_layer {
33+
return;
34+
}
2035
// Calculate the downstream transforms in order to bring the other vector data into the same layer space
21-
let current_transform = document.metadata().downstream_transform_to_document(current_layer);
22-
let other_transform = document.metadata().downstream_transform_to_document(other_layer);
36+
let first_layer_transform = document.metadata().downstream_transform_to_document(first_layer);
37+
let second_layer_transform = document.metadata().downstream_transform_to_document(second_layer);
2338

2439
// Represents the change in position that would occur if the other layer was moved below the current layer
25-
let transform_delta = current_transform * other_transform.inverse();
40+
let transform_delta = first_layer_transform * second_layer_transform.inverse();
2641
let offset = transform_delta.inverse();
2742
responses.add(GraphOperationMessage::TransformChange {
28-
layer: other_layer,
43+
layer: second_layer,
2944
transform: offset,
3045
transform_in: TransformIn::Local,
3146
skip_rerender: false,
3247
});
3348

34-
// Move the other layer below the current layer for positioning purposes
35-
let current_layer_parent = current_layer.parent(document.metadata()).unwrap();
36-
let current_layer_index = current_layer_parent.children(document.metadata()).position(|child| child == current_layer).unwrap();
49+
let mut current_and_other_layer_is_spline = false;
50+
51+
match (find_spline(document, first_layer), find_spline(document, second_layer)) {
52+
(Some(current_layer_spline), Some(other_layer_spline)) => {
53+
responses.add(NodeGraphMessage::DeleteNodes {
54+
node_ids: [current_layer_spline, other_layer_spline].to_vec(),
55+
delete_children: false,
56+
});
57+
current_and_other_layer_is_spline = true;
58+
}
59+
_ => {}
60+
}
61+
62+
// Move the `second_layer` below the `first_layer` for positioning purposes
63+
let first_layer_parent = first_layer.parent(document.metadata()).unwrap();
64+
let first_layer_index = first_layer_parent.children(document.metadata()).position(|child| child == first_layer).unwrap();
3765
responses.add(NodeGraphMessage::MoveLayerToStack {
38-
layer: other_layer,
39-
parent: current_layer_parent,
40-
insert_index: current_layer_index + 1,
66+
layer: second_layer,
67+
parent: first_layer_parent,
68+
insert_index: first_layer_index + 1,
4169
});
4270

4371
// Merge the inputs of the two layers
@@ -55,14 +83,14 @@ pub fn merge_layers(document: &DocumentMessageHandler, current_layer: LayerNodeI
5583
});
5684
responses.add(NodeGraphMessage::MoveNodeToChainStart {
5785
node_id: merge_node_id,
58-
parent: current_layer,
86+
parent: first_layer,
5987
});
6088
responses.add(NodeGraphMessage::ConnectUpstreamOutputToInput {
61-
downstream_input: InputConnector::node(other_layer.to_node(), 1),
89+
downstream_input: InputConnector::node(second_layer.to_node(), 1),
6290
input_connector: InputConnector::node(merge_node_id, 1),
6391
});
6492
responses.add(NodeGraphMessage::DeleteNodes {
65-
node_ids: vec![other_layer.to_node()],
93+
node_ids: vec![second_layer.to_node()],
6694
delete_children: false,
6795
});
6896

@@ -77,7 +105,7 @@ pub fn merge_layers(document: &DocumentMessageHandler, current_layer: LayerNodeI
77105
});
78106
responses.add(NodeGraphMessage::MoveNodeToChainStart {
79107
node_id: flatten_node_id,
80-
parent: current_layer,
108+
parent: first_layer,
81109
});
82110

83111
// Add a path node after the flatten node
@@ -91,9 +119,25 @@ pub fn merge_layers(document: &DocumentMessageHandler, current_layer: LayerNodeI
91119
});
92120
responses.add(NodeGraphMessage::MoveNodeToChainStart {
93121
node_id: path_node_id,
94-
parent: current_layer,
122+
parent: first_layer,
95123
});
96124

125+
// Add a Spline node after the Path node if both the layers we are merging is spline.
126+
if current_and_other_layer_is_spline {
127+
let spline_node_id = NodeId::new();
128+
let spline_node = document_node_definitions::resolve_document_node_type("Spline")
129+
.expect("Failed to create Spline node")
130+
.default_node_template();
131+
responses.add(NodeGraphMessage::InsertNode {
132+
node_id: spline_node_id,
133+
node_template: spline_node,
134+
});
135+
responses.add(NodeGraphMessage::MoveNodeToChainStart {
136+
node_id: spline_node_id,
137+
parent: first_layer,
138+
});
139+
}
140+
97141
// Add a transform node to ensure correct tooling modifications
98142
let transform_node_id = NodeId::new();
99143
let transform_node = document_node_definitions::resolve_document_node_type("Transform")
@@ -105,14 +149,57 @@ pub fn merge_layers(document: &DocumentMessageHandler, current_layer: LayerNodeI
105149
});
106150
responses.add(NodeGraphMessage::MoveNodeToChainStart {
107151
node_id: transform_node_id,
108-
parent: current_layer,
152+
parent: first_layer,
109153
});
110154

111155
responses.add(NodeGraphMessage::RunDocumentGraph);
112156
responses.add(Message::StartBuffer);
113157
responses.add(PenToolMessage::RecalculateLatestPointsPosition);
114158
}
115159

160+
/// Merge the `first_endpoint` with `second_endpoint`.
161+
pub fn merge_points(document: &DocumentMessageHandler, layer: LayerNodeIdentifier, first_endpoint: PointId, second_endpont: PointId, responses: &mut VecDeque<Message>) {
162+
let transform = document.metadata().transform_to_document(layer);
163+
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { return };
164+
165+
let segment = vector_data.segment_bezier_iter().find(|(_, _, start, end)| *end == second_endpont || *start == second_endpont);
166+
let Some((segment, _, mut segment_start_point, mut segment_end_point)) = segment else {
167+
log::error!("Could not get the segment for second_endpoint.");
168+
return;
169+
};
170+
171+
let mut handles = [None; 2];
172+
if let Some(handle_position) = ManipulatorPointId::PrimaryHandle(segment).get_position(&vector_data) {
173+
let anchor_position = ManipulatorPointId::Anchor(segment_start_point).get_position(&vector_data).unwrap();
174+
let handle_position = transform.transform_point2(handle_position);
175+
let anchor_position = transform.transform_point2(anchor_position);
176+
let anchor_to_handle = handle_position - anchor_position;
177+
handles[0] = Some(anchor_to_handle);
178+
}
179+
if let Some(handle_position) = ManipulatorPointId::EndHandle(segment).get_position(&vector_data) {
180+
let anchor_position = ManipulatorPointId::Anchor(segment_end_point).get_position(&vector_data).unwrap();
181+
let handle_position = transform.transform_point2(handle_position);
182+
let anchor_position = transform.transform_point2(anchor_position);
183+
let anchor_to_handle = handle_position - anchor_position;
184+
handles[1] = Some(anchor_to_handle);
185+
}
186+
187+
if segment_start_point == second_endpont {
188+
core::mem::swap(&mut segment_start_point, &mut segment_end_point);
189+
handles.reverse();
190+
}
191+
192+
let modification_type = VectorModificationType::RemovePoint { id: second_endpont };
193+
responses.add(GraphOperationMessage::Vector { layer, modification_type });
194+
let modification_type = VectorModificationType::RemoveSegment { id: segment };
195+
responses.add(GraphOperationMessage::Vector { layer, modification_type });
196+
197+
let points = [segment_start_point, first_endpoint];
198+
let id = SegmentId::generate();
199+
let modification_type = VectorModificationType::InsertSegment { id, points, handles };
200+
responses.add(GraphOperationMessage::Vector { layer, modification_type });
201+
}
202+
116203
/// Create a new vector layer.
117204
pub fn new_vector_layer(subpaths: Vec<Subpath<PointId>>, id: NodeId, parent: LayerNodeIdentifier, responses: &mut VecDeque<Message>) -> LayerNodeIdentifier {
118205
let insert_index = 0;

0 commit comments

Comments
 (0)