Skip to content

Commit 4a83067

Browse files
authored
Fix Path tool's Path node transform calculation by skipping local transform based on first instance source ID (#2843)
* First instance source id * Set source node id and migrations
1 parent 88c059a commit 4a83067

File tree

15 files changed

+126
-58
lines changed

15 files changed

+126
-58
lines changed

editor/src/dispatcher.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ impl Dispatcher {
140140
let graphene_std::renderer::RenderMetadata {
141141
upstream_footprints: footprints,
142142
local_transforms,
143+
first_instance_source_id,
143144
click_targets,
144145
clip_targets,
145146
} = render_metadata;
@@ -149,6 +150,7 @@ impl Dispatcher {
149150
DocumentMessage::UpdateUpstreamTransforms {
150151
upstream_footprints: footprints,
151152
local_transforms,
153+
first_instance_source_id,
152154
},
153155
DocumentMessage::UpdateClickTargets { click_targets },
154156
DocumentMessage::UpdateClipTargets { clip_targets },

editor/src/messages/portfolio/document/document_message.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ pub enum DocumentMessage {
182182
UpdateUpstreamTransforms {
183183
upstream_footprints: HashMap<NodeId, Footprint>,
184184
local_transforms: HashMap<NodeId, DAffine2>,
185+
first_instance_source_id: HashMap<NodeId, Option<NodeId>>,
185186
},
186187
UpdateClickTargets {
187188
click_targets: HashMap<NodeId, Vec<ClickTarget>>,

editor/src/messages/portfolio/document/document_message_handler.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,8 +1305,10 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
13051305
DocumentMessage::UpdateUpstreamTransforms {
13061306
upstream_footprints,
13071307
local_transforms,
1308+
first_instance_source_id,
13081309
} => {
13091310
self.network_interface.update_transforms(upstream_footprints, local_transforms);
1311+
self.network_interface.update_first_instance_source_id(first_instance_source_id);
13101312
}
13111313
DocumentMessage::UpdateClickTargets { click_targets } => {
13121314
// TODO: Allow non layer nodes to have click targets

editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1472,7 +1472,11 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
14721472
..Default::default()
14731473
},
14741474
DocumentNode {
1475-
inputs: vec![NodeInput::node(NodeId(0), 0), NodeInput::network(concrete!(graphene_std::vector::VectorModification), 1)],
1475+
inputs: vec![
1476+
NodeInput::node(NodeId(0), 0),
1477+
NodeInput::network(concrete!(graphene_std::vector::VectorModification), 1),
1478+
NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath),
1479+
],
14761480
manual_composition: Some(generic!(T)),
14771481
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::vector::vector_data::modification::PathModifyNode")),
14781482
..Default::default()

editor/src/messages/portfolio/document/overlays/utility_functions.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ pub fn path_overlays(document: &DocumentMessageHandler, draw_handles: DrawHandle
119119

120120
for layer in document.network_interface.selected_nodes().selected_layers(document.metadata()) {
121121
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue };
122-
let transform = document.metadata().transform_to_viewport(layer);
122+
let transform = document.metadata().transform_to_viewport_if_feeds(layer, &document.network_interface);
123123
if display_path {
124124
overlay_context.outline_vector(&vector_data, transform);
125125
}
@@ -196,7 +196,7 @@ pub fn path_endpoint_overlays(document: &DocumentMessageHandler, shape_editor: &
196196
continue;
197197
};
198198
//let document_to_viewport = document.navigation_handler.calculate_offset_transform(overlay_context.size / 2., &document.document_ptz);
199-
let transform = document.metadata().transform_to_viewport(layer);
199+
let transform = document.metadata().transform_to_viewport_if_feeds(layer, &document.network_interface);
200200
let selected = shape_editor.selected_shape_state.get(&layer);
201201
let is_selected = |selected: Option<&SelectedLayerState>, point: ManipulatorPointId| selected.is_some_and(|selected| selected.is_point_selected(point));
202202

editor/src/messages/portfolio/document/utility_types/document_metadata.rs

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use super::network_interface::NodeNetworkInterface;
22
use crate::messages::portfolio::document::graph_operation::transform_utils;
33
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
4+
use crate::messages::portfolio::document::utility_types::network_interface::FlowType;
5+
use crate::messages::tool::common_functionality::graph_modification_utils;
46
use glam::{DAffine2, DVec2};
57
use graph_craft::document::NodeId;
68
use graphene_std::math::quad::Quad;
@@ -16,10 +18,11 @@ use std::num::NonZeroU64;
1618

1719
// TODO: To avoid storing a stateful snapshot of some other system's state (which is easily to accidentally get out of sync),
1820
// TODO: it might be better to have a system that can query the state of the node network on demand.
19-
#[derive(Debug, Clone)]
21+
#[derive(Debug, Clone, Default)]
2022
pub struct DocumentMetadata {
2123
pub upstream_footprints: HashMap<NodeId, Footprint>,
2224
pub local_transforms: HashMap<NodeId, DAffine2>,
25+
pub first_instance_source_ids: HashMap<NodeId, Option<NodeId>>,
2326
pub structure: HashMap<LayerNodeIdentifier, NodeRelations>,
2427
pub click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>,
2528
pub clip_targets: HashSet<NodeId>,
@@ -28,20 +31,6 @@ pub struct DocumentMetadata {
2831
pub document_to_viewport: DAffine2,
2932
}
3033

31-
impl Default for DocumentMetadata {
32-
fn default() -> Self {
33-
Self {
34-
upstream_footprints: HashMap::new(),
35-
local_transforms: HashMap::new(),
36-
structure: HashMap::new(),
37-
vector_modify: HashMap::new(),
38-
click_targets: HashMap::new(),
39-
clip_targets: HashSet::new(),
40-
document_to_viewport: DAffine2::IDENTITY,
41-
}
42-
}
43-
}
44-
4534
// =================================
4635
// DocumentMetadata: Layer iterators
4736
// =================================
@@ -91,6 +80,36 @@ impl DocumentMetadata {
9180
footprint * local_transform
9281
}
9382

83+
pub fn transform_to_viewport_if_feeds(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> DAffine2 {
84+
// We're not allowed to convert the root parent to a node id
85+
if layer == LayerNodeIdentifier::ROOT_PARENT {
86+
return self.document_to_viewport;
87+
}
88+
89+
let footprint = self.upstream_footprints.get(&layer.to_node()).map(|footprint| footprint.transform).unwrap_or(self.document_to_viewport);
90+
91+
let mut use_local = true;
92+
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, network_interface);
93+
if let Some(path_node) = graph_layer.upstream_node_id_from_name("Path") {
94+
if let Some(&source) = self.first_instance_source_ids.get(&layer.to_node()) {
95+
if !network_interface
96+
.upstream_flow_back_from_nodes(vec![path_node], &[], FlowType::HorizontalFlow)
97+
.any(|upstream| Some(upstream) == source)
98+
{
99+
use_local = false;
100+
info!("Local transform is invalid — using the identity for the local transform instead")
101+
}
102+
}
103+
}
104+
let local_transform = use_local.then(|| self.local_transforms.get(&layer.to_node()).copied()).flatten().unwrap_or_default();
105+
106+
footprint * local_transform
107+
}
108+
109+
pub fn transform_to_document_if_feeds(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> DAffine2 {
110+
self.document_to_viewport.inverse() * self.transform_to_viewport_if_feeds(layer, network_interface)
111+
}
112+
94113
pub fn transform_to_viewport_with_first_transform_node_if_group(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> DAffine2 {
95114
let footprint = self.upstream_footprints.get(&layer.to_node()).map(|footprint| footprint.transform).unwrap_or(self.document_to_viewport);
96115
let local_transform = self.local_transforms.get(&layer.to_node()).copied();

editor/src/messages/portfolio/document/utility_types/network_interface.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,27 @@ impl PartialEq for NodeNetworkInterface {
6060
}
6161
}
6262

63+
impl NodeNetworkInterface {
64+
/// Add DocumentNodePath input to the PathModifyNode protonode
65+
pub fn migrate_path_modify_node(&mut self) {
66+
fix_network(&mut self.network);
67+
fn fix_network(network: &mut NodeNetwork) {
68+
for node in network.nodes.values_mut() {
69+
if let Some(network) = node.implementation.get_network_mut() {
70+
fix_network(network);
71+
}
72+
if let DocumentNodeImplementation::ProtoNode(protonode) = &node.implementation {
73+
if protonode.name.contains("PathModifyNode") {
74+
if node.inputs.len() < 3 {
75+
node.inputs.push(NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath));
76+
}
77+
}
78+
}
79+
}
80+
}
81+
}
82+
}
83+
6384
// Public immutable getters for the network interface
6485
impl NodeNetworkInterface {
6586
// TODO: Make private and use .field_name getter methods
@@ -3513,6 +3534,11 @@ impl NodeNetworkInterface {
35133534
self.document_metadata.local_transforms = local_transforms;
35143535
}
35153536

3537+
/// Update the cached first instance source id of the layers
3538+
pub fn update_first_instance_source_id(&mut self, new: HashMap<NodeId, Option<NodeId>>) {
3539+
self.document_metadata.first_instance_source_ids = new;
3540+
}
3541+
35163542
/// Update the cached click targets of the layers
35173543
pub fn update_click_targets(&mut self, new_click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>) {
35183544
self.document_metadata.click_targets = new_click_targets;

editor/src/messages/portfolio/document_migration.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ pub fn document_migration_reset_node_definition(document_serialized_content: &st
190190
}
191191

192192
pub fn document_migration_upgrades(document: &mut DocumentMessageHandler, reset_node_definitions_on_open: bool) {
193+
document.network_interface.migrate_path_modify_node();
194+
193195
let network = document.network_interface.document_network().clone();
194196

195197
// Apply string replacements to each node

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

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -212,15 +212,15 @@ impl ClosestSegment {
212212
self.bezier_point_to_viewport
213213
}
214214

215-
pub fn closest_point(&self, document_metadata: &DocumentMetadata) -> DVec2 {
216-
let transform = document_metadata.transform_to_viewport(self.layer);
215+
pub fn closest_point(&self, document_metadata: &DocumentMetadata, network_interface: &NodeNetworkInterface) -> DVec2 {
216+
let transform = document_metadata.transform_to_viewport_if_feeds(self.layer, network_interface);
217217
let bezier_point = self.bezier.evaluate(TValue::Parametric(self.t));
218218
transform.transform_point2(bezier_point)
219219
}
220220

221221
/// Updates this [`ClosestSegment`] with the viewport-space location of the closest point on the segment to the given mouse position.
222-
pub fn update_closest_point(&mut self, document_metadata: &DocumentMetadata, mouse_position: DVec2) {
223-
let transform = document_metadata.transform_to_viewport(self.layer);
222+
pub fn update_closest_point(&mut self, document_metadata: &DocumentMetadata, network_interface: &NodeNetworkInterface, mouse_position: DVec2) {
223+
let transform = document_metadata.transform_to_viewport_if_feeds(self.layer, network_interface);
224224
let layer_mouse_pos = transform.inverse().transform_point2(mouse_position);
225225

226226
let t = self.bezier.project(layer_mouse_pos).clamp(0., 1.);
@@ -239,9 +239,9 @@ impl ClosestSegment {
239239
tolerance.powi(2) < self.distance_squared(mouse_position)
240240
}
241241

242-
pub fn handle_positions(&self, document_metadata: &DocumentMetadata) -> (Option<DVec2>, Option<DVec2>) {
242+
pub fn handle_positions(&self, document_metadata: &DocumentMetadata, network_interface: &NodeNetworkInterface) -> (Option<DVec2>, Option<DVec2>) {
243243
// Transform to viewport space
244-
let transform = document_metadata.transform_to_viewport(self.layer);
244+
let transform = document_metadata.transform_to_viewport_if_feeds(self.layer, network_interface);
245245

246246
// Split the Bezier at the parameter `t`
247247
let [first, second] = self.bezier.split(TValue::Parametric(self.t));
@@ -307,7 +307,7 @@ impl ClosestSegment {
307307
}
308308

309309
pub fn calculate_perp(&self, document: &DocumentMessageHandler) -> DVec2 {
310-
let tangent = if let (Some(handle1), Some(handle2)) = self.handle_positions(document.metadata()) {
310+
let tangent = if let (Some(handle1), Some(handle2)) = self.handle_positions(document.metadata(), &document.network_interface) {
311311
(handle1 - handle2).try_normalize()
312312
} else {
313313
let [first_point, last_point] = self.points();
@@ -339,7 +339,7 @@ impl ClosestSegment {
339339
break_colinear_molding: bool,
340340
temporary_adjacent_handles_while_molding: Option<[Option<HandleId>; 2]>,
341341
) -> Option<[Option<HandleId>; 2]> {
342-
let transform = document.metadata().transform_to_viewport(self.layer);
342+
let transform = document.metadata().transform_to_viewport_if_feeds(self.layer, &document.network_interface);
343343

344344
let start = self.bezier.start;
345345
let end = self.bezier.end;
@@ -507,7 +507,7 @@ impl ShapeState {
507507
continue;
508508
};
509509

510-
let to_document = document.metadata().transform_to_document(*layer);
510+
let to_document = document.metadata().transform_to_document_if_feeds(*layer, &document.network_interface);
511511

512512
for &selected in &state.selected_points {
513513
let source = match selected {
@@ -564,7 +564,11 @@ impl ShapeState {
564564
let already_selected = selected_shape_state.is_point_selected(manipulator_point_id);
565565

566566
// Offset to snap the selected point to the cursor
567-
let offset = mouse_position - network_interface.document_metadata().transform_to_viewport(layer).transform_point2(point_position);
567+
let offset = mouse_position
568+
- network_interface
569+
.document_metadata()
570+
.transform_to_viewport_if_feeds(layer, network_interface)
571+
.transform_point2(point_position);
568572

569573
// This is selecting the manipulator only for now, next to generalize to points
570574

@@ -621,7 +625,11 @@ impl ShapeState {
621625
let already_selected = selected_shape_state.is_point_selected(manipulator_point_id);
622626

623627
// Offset to snap the selected point to the cursor
624-
let offset = mouse_position - network_interface.document_metadata().transform_to_viewport(layer).transform_point2(point_position);
628+
let offset = mouse_position
629+
- network_interface
630+
.document_metadata()
631+
.transform_to_viewport_if_feeds(layer, network_interface)
632+
.transform_point2(point_position);
625633

626634
// Gather current selection information
627635
let points = self
@@ -653,7 +661,7 @@ impl ShapeState {
653661
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
654662
return;
655663
};
656-
let to_viewport = document.metadata().transform_to_viewport(layer);
664+
let to_viewport = document.metadata().transform_to_viewport_if_feeds(layer, &document.network_interface);
657665
let layer_mouse = to_viewport.inverse().transform_point2(mouse);
658666
let state = self.selected_shape_state.entry(layer).or_default();
659667

@@ -875,7 +883,7 @@ impl ShapeState {
875883
}
876884

877885
let vector_data = network_interface.compute_modified_vector(layer)?;
878-
let transform = network_interface.document_metadata().transform_to_document(layer).inverse();
886+
let transform = network_interface.document_metadata().transform_to_document_if_feeds(layer, network_interface).inverse();
879887
let position = transform.transform_point2(new_position);
880888
let current_position = point.get_position(&vector_data)?;
881889
let delta = position - current_position;
@@ -1026,7 +1034,7 @@ impl ShapeState {
10261034
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
10271035
continue;
10281036
};
1029-
let transform = document.metadata().transform_to_document(layer);
1037+
let transform = document.metadata().transform_to_document_if_feeds(layer, &document.network_interface);
10301038

10311039
for &point in layer_state.selected_points.iter() {
10321040
let Some(handles) = point.get_handle_pair(&vector_data) else { continue };
@@ -1116,8 +1124,8 @@ impl ShapeState {
11161124

11171125
let opposing_handles = handle_lengths.as_ref().and_then(|handle_lengths| handle_lengths.get(&layer));
11181126

1119-
let transform_to_viewport_space = document.metadata().transform_to_viewport(layer);
1120-
let transform_to_document_space = document.metadata().transform_to_document(layer);
1127+
let transform_to_viewport_space = document.metadata().transform_to_viewport_if_feeds(layer, &document.network_interface);
1128+
let transform_to_document_space = document.metadata().transform_to_document_if_feeds(layer, &document.network_interface);
11211129
let delta_transform = if in_viewport_space {
11221130
transform_to_viewport_space
11231131
} else {
@@ -1210,7 +1218,7 @@ impl ShapeState {
12101218
.iter()
12111219
.filter_map(|(&layer, state)| {
12121220
let vector_data = document.network_interface.compute_modified_vector(layer)?;
1213-
let transform = document.metadata().transform_to_document(layer);
1221+
let transform = document.metadata().transform_to_document_if_feeds(layer, &document.network_interface);
12141222
let opposing_handle_lengths = vector_data
12151223
.colinear_manipulators
12161224
.iter()
@@ -1575,7 +1583,7 @@ impl ShapeState {
15751583
let mut manipulator_point = None;
15761584

15771585
let vector_data = network_interface.compute_modified_vector(layer)?;
1578-
let viewspace = network_interface.document_metadata().transform_to_viewport(layer);
1586+
let viewspace = network_interface.document_metadata().transform_to_viewport_if_feeds(layer, network_interface);
15791587

15801588
// Handles
15811589
for (segment_id, bezier, _, _) in vector_data.segment_bezier_iter() {
@@ -1611,7 +1619,7 @@ impl ShapeState {
16111619

16121620
/// Find the `t` value along the path segment we have clicked upon, together with that segment ID.
16131621
fn closest_segment(&self, network_interface: &NodeNetworkInterface, layer: LayerNodeIdentifier, position: glam::DVec2, tolerance: f64) -> Option<ClosestSegment> {
1614-
let transform = network_interface.document_metadata().transform_to_viewport(layer);
1622+
let transform = network_interface.document_metadata().transform_to_viewport_if_feeds(layer, network_interface);
16151623
let layer_pos = transform.inverse().transform_point2(position);
16161624

16171625
let tolerance = tolerance + 0.5;
@@ -1785,7 +1793,7 @@ impl ShapeState {
17851793
pub fn flip_smooth_sharp(&self, network_interface: &NodeNetworkInterface, target: glam::DVec2, tolerance: f64, responses: &mut VecDeque<Message>) -> bool {
17861794
let mut process_layer = |layer| {
17871795
let vector_data = network_interface.compute_modified_vector(layer)?;
1788-
let transform_to_screenspace = network_interface.document_metadata().transform_to_viewport(layer);
1796+
let transform_to_screenspace = network_interface.document_metadata().transform_to_viewport_if_feeds(layer, network_interface);
17891797

17901798
let mut result = None;
17911799
let mut closest_distance_squared = tolerance * tolerance;
@@ -1889,7 +1897,7 @@ impl ShapeState {
18891897

18901898
let vector_data = network_interface.compute_modified_vector(layer);
18911899
let Some(vector_data) = vector_data else { continue };
1892-
let transform = network_interface.document_metadata().transform_to_viewport(layer);
1900+
let transform = network_interface.document_metadata().transform_to_viewport_if_feeds(layer, network_interface);
18931901

18941902
assert_eq!(vector_data.segment_domain.ids().len(), vector_data.start_point().count());
18951903
assert_eq!(vector_data.segment_domain.ids().len(), vector_data.end_point().count());

0 commit comments

Comments
 (0)