diff --git a/editor/src/dispatcher.rs b/editor/src/dispatcher.rs index a8891033e..9408d7d92 100644 --- a/editor/src/dispatcher.rs +++ b/editor/src/dispatcher.rs @@ -140,6 +140,7 @@ impl Dispatcher { let graphene_std::renderer::RenderMetadata { upstream_footprints: footprints, local_transforms, + first_instance_source_id, click_targets, clip_targets, } = render_metadata; @@ -149,6 +150,7 @@ impl Dispatcher { DocumentMessage::UpdateUpstreamTransforms { upstream_footprints: footprints, local_transforms, + first_instance_source_id, }, DocumentMessage::UpdateClickTargets { click_targets }, DocumentMessage::UpdateClipTargets { clip_targets }, diff --git a/editor/src/messages/portfolio/document/document_message.rs b/editor/src/messages/portfolio/document/document_message.rs index 18ede64be..ae3576d2a 100644 --- a/editor/src/messages/portfolio/document/document_message.rs +++ b/editor/src/messages/portfolio/document/document_message.rs @@ -182,6 +182,7 @@ pub enum DocumentMessage { UpdateUpstreamTransforms { upstream_footprints: HashMap, local_transforms: HashMap, + first_instance_source_id: HashMap>, }, UpdateClickTargets { click_targets: HashMap>, diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 81f631dfe..f306663a0 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -1305,8 +1305,10 @@ impl MessageHandler> for DocumentMessag DocumentMessage::UpdateUpstreamTransforms { upstream_footprints, local_transforms, + first_instance_source_id, } => { self.network_interface.update_transforms(upstream_footprints, local_transforms); + self.network_interface.update_first_instance_source_id(first_instance_source_id); } DocumentMessage::UpdateClickTargets { click_targets } => { // TODO: Allow non layer nodes to have click targets diff --git a/editor/src/messages/portfolio/document/overlays/utility_functions.rs b/editor/src/messages/portfolio/document/overlays/utility_functions.rs index 1e012b867..463971c65 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_functions.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_functions.rs @@ -119,7 +119,7 @@ pub fn path_overlays(document: &DocumentMessageHandler, draw_handles: DrawHandle for layer in document.network_interface.selected_nodes().selected_layers(document.metadata()) { let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue }; - let transform = document.metadata().transform_to_viewport(layer); + let transform = document.metadata().transform_to_viewport_if_feeds(layer, &document.network_interface); if display_path { overlay_context.outline_vector(&vector_data, transform); } @@ -196,7 +196,7 @@ pub fn path_endpoint_overlays(document: &DocumentMessageHandler, shape_editor: & continue; }; //let document_to_viewport = document.navigation_handler.calculate_offset_transform(overlay_context.size / 2., &document.document_ptz); - let transform = document.metadata().transform_to_viewport(layer); + let transform = document.metadata().transform_to_viewport_if_feeds(layer, &document.network_interface); let selected = shape_editor.selected_shape_state.get(&layer); let is_selected = |selected: Option<&SelectedLayerState>, point: ManipulatorPointId| selected.is_some_and(|selected| selected.is_point_selected(point)); diff --git a/editor/src/messages/portfolio/document/utility_types/document_metadata.rs b/editor/src/messages/portfolio/document/utility_types/document_metadata.rs index 23887db3e..eefac546f 100644 --- a/editor/src/messages/portfolio/document/utility_types/document_metadata.rs +++ b/editor/src/messages/portfolio/document/utility_types/document_metadata.rs @@ -1,6 +1,8 @@ use super::network_interface::NodeNetworkInterface; use crate::messages::portfolio::document::graph_operation::transform_utils; use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext; +use crate::messages::portfolio::document::utility_types::network_interface::FlowType; +use crate::messages::tool::common_functionality::graph_modification_utils; use glam::{DAffine2, DVec2}; use graph_craft::document::NodeId; use graphene_std::math::quad::Quad; @@ -16,10 +18,11 @@ use std::num::NonZeroU64; // TODO: To avoid storing a stateful snapshot of some other system's state (which is easily to accidentally get out of sync), // TODO: it might be better to have a system that can query the state of the node network on demand. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct DocumentMetadata { pub upstream_footprints: HashMap, pub local_transforms: HashMap, + pub first_instance_source_ids: HashMap>, pub structure: HashMap, pub click_targets: HashMap>, pub clip_targets: HashSet, @@ -28,20 +31,6 @@ pub struct DocumentMetadata { pub document_to_viewport: DAffine2, } -impl Default for DocumentMetadata { - fn default() -> Self { - Self { - upstream_footprints: HashMap::new(), - local_transforms: HashMap::new(), - structure: HashMap::new(), - vector_modify: HashMap::new(), - click_targets: HashMap::new(), - clip_targets: HashSet::new(), - document_to_viewport: DAffine2::IDENTITY, - } - } -} - // ================================= // DocumentMetadata: Layer iterators // ================================= @@ -91,6 +80,38 @@ impl DocumentMetadata { footprint * local_transform } + pub fn transform_to_viewport_if_feeds(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> DAffine2 { + // We're not allowed to convert the root parent to a node id + if layer == LayerNodeIdentifier::ROOT_PARENT { + return self.document_to_viewport; + } + + let footprint = self.upstream_footprints.get(&layer.to_node()).map(|footprint| footprint.transform).unwrap_or(self.document_to_viewport); + + let mut use_local = true; + let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, network_interface); + info!("feeds"); + if let Some(path_node) = graph_layer.upstream_node_id_from_name("Path") { + info!("Path node {path_node:?} "); + if let Some(&source) = self.first_instance_source_ids.get(&layer.to_node()) { + if !network_interface + .upstream_flow_back_from_nodes(vec![path_node], &[], FlowType::HorizontalFlow) + .any(|upstream| Some(upstream) == source) + { + use_local = false; + } + info!("Path node {path_node:?} source {source:?} {use_local}"); + } + } + let local_transform = use_local.then(|| self.local_transforms.get(&layer.to_node()).copied()).flatten().unwrap_or_default(); + + footprint * local_transform + } + + pub fn transform_to_document_if_feeds(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> DAffine2 { + self.document_to_viewport.inverse() * self.transform_to_viewport_if_feeds(layer, network_interface) + } + pub fn transform_to_viewport_with_first_transform_node_if_group(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> DAffine2 { let footprint = self.upstream_footprints.get(&layer.to_node()).map(|footprint| footprint.transform).unwrap_or(self.document_to_viewport); let local_transform = self.local_transforms.get(&layer.to_node()).copied(); diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface.rs b/editor/src/messages/portfolio/document/utility_types/network_interface.rs index c4255120a..2d2c09662 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface.rs @@ -3513,6 +3513,11 @@ impl NodeNetworkInterface { self.document_metadata.local_transforms = local_transforms; } + /// Update the cached first instance source id of the layers + pub fn update_first_instance_source_id(&mut self, new: HashMap>) { + self.document_metadata.first_instance_source_ids = new; + } + /// Update the cached click targets of the layers pub fn update_click_targets(&mut self, new_click_targets: HashMap>) { self.document_metadata.click_targets = new_click_targets; diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index 14ae8898a..581c89908 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -212,15 +212,15 @@ impl ClosestSegment { self.bezier_point_to_viewport } - pub fn closest_point(&self, document_metadata: &DocumentMetadata) -> DVec2 { - let transform = document_metadata.transform_to_viewport(self.layer); + pub fn closest_point(&self, document_metadata: &DocumentMetadata, network_interface: &NodeNetworkInterface) -> DVec2 { + let transform = document_metadata.transform_to_viewport_if_feeds(self.layer, network_interface); let bezier_point = self.bezier.evaluate(TValue::Parametric(self.t)); transform.transform_point2(bezier_point) } /// Updates this [`ClosestSegment`] with the viewport-space location of the closest point on the segment to the given mouse position. - pub fn update_closest_point(&mut self, document_metadata: &DocumentMetadata, mouse_position: DVec2) { - let transform = document_metadata.transform_to_viewport(self.layer); + pub fn update_closest_point(&mut self, document_metadata: &DocumentMetadata, network_interface: &NodeNetworkInterface, mouse_position: DVec2) { + let transform = document_metadata.transform_to_viewport_if_feeds(self.layer, network_interface); let layer_mouse_pos = transform.inverse().transform_point2(mouse_position); let t = self.bezier.project(layer_mouse_pos).clamp(0., 1.); @@ -239,9 +239,9 @@ impl ClosestSegment { tolerance.powi(2) < self.distance_squared(mouse_position) } - pub fn handle_positions(&self, document_metadata: &DocumentMetadata) -> (Option, Option) { + pub fn handle_positions(&self, document_metadata: &DocumentMetadata, network_interface: &NodeNetworkInterface) -> (Option, Option) { // Transform to viewport space - let transform = document_metadata.transform_to_viewport(self.layer); + let transform = document_metadata.transform_to_viewport_if_feeds(self.layer, network_interface); // Split the Bezier at the parameter `t` let [first, second] = self.bezier.split(TValue::Parametric(self.t)); @@ -307,7 +307,7 @@ impl ClosestSegment { } pub fn calculate_perp(&self, document: &DocumentMessageHandler) -> DVec2 { - let tangent = if let (Some(handle1), Some(handle2)) = self.handle_positions(document.metadata()) { + let tangent = if let (Some(handle1), Some(handle2)) = self.handle_positions(document.metadata(), &document.network_interface) { (handle1 - handle2).try_normalize() } else { let [first_point, last_point] = self.points(); @@ -339,7 +339,7 @@ impl ClosestSegment { break_colinear_molding: bool, temporary_adjacent_handles_while_molding: Option<[Option; 2]>, ) -> Option<[Option; 2]> { - let transform = document.metadata().transform_to_viewport(self.layer); + let transform = document.metadata().transform_to_viewport_if_feeds(self.layer, &document.network_interface); let start = self.bezier.start; let end = self.bezier.end; @@ -507,7 +507,7 @@ impl ShapeState { continue; }; - let to_document = document.metadata().transform_to_document(*layer); + let to_document = document.metadata().transform_to_document_if_feeds(*layer, &document.network_interface); for &selected in &state.selected_points { let source = match selected { @@ -564,7 +564,11 @@ impl ShapeState { let already_selected = selected_shape_state.is_point_selected(manipulator_point_id); // Offset to snap the selected point to the cursor - let offset = mouse_position - network_interface.document_metadata().transform_to_viewport(layer).transform_point2(point_position); + let offset = mouse_position + - network_interface + .document_metadata() + .transform_to_viewport_if_feeds(layer, network_interface) + .transform_point2(point_position); // This is selecting the manipulator only for now, next to generalize to points @@ -621,7 +625,11 @@ impl ShapeState { let already_selected = selected_shape_state.is_point_selected(manipulator_point_id); // Offset to snap the selected point to the cursor - let offset = mouse_position - network_interface.document_metadata().transform_to_viewport(layer).transform_point2(point_position); + let offset = mouse_position + - network_interface + .document_metadata() + .transform_to_viewport_if_feeds(layer, network_interface) + .transform_point2(point_position); // Gather current selection information let points = self @@ -653,7 +661,7 @@ impl ShapeState { let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { return; }; - let to_viewport = document.metadata().transform_to_viewport(layer); + let to_viewport = document.metadata().transform_to_viewport_if_feeds(layer, &document.network_interface); let layer_mouse = to_viewport.inverse().transform_point2(mouse); let state = self.selected_shape_state.entry(layer).or_default(); @@ -875,7 +883,7 @@ impl ShapeState { } let vector_data = network_interface.compute_modified_vector(layer)?; - let transform = network_interface.document_metadata().transform_to_document(layer).inverse(); + let transform = network_interface.document_metadata().transform_to_document_if_feeds(layer, network_interface).inverse(); let position = transform.transform_point2(new_position); let current_position = point.get_position(&vector_data)?; let delta = position - current_position; @@ -1026,7 +1034,7 @@ impl ShapeState { let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue; }; - let transform = document.metadata().transform_to_document(layer); + let transform = document.metadata().transform_to_document_if_feeds(layer, &document.network_interface); for &point in layer_state.selected_points.iter() { let Some(handles) = point.get_handle_pair(&vector_data) else { continue }; @@ -1116,8 +1124,8 @@ impl ShapeState { let opposing_handles = handle_lengths.as_ref().and_then(|handle_lengths| handle_lengths.get(&layer)); - let transform_to_viewport_space = document.metadata().transform_to_viewport(layer); - let transform_to_document_space = document.metadata().transform_to_document(layer); + let transform_to_viewport_space = document.metadata().transform_to_viewport_if_feeds(layer, &document.network_interface); + let transform_to_document_space = document.metadata().transform_to_document_if_feeds(layer, &document.network_interface); let delta_transform = if in_viewport_space { transform_to_viewport_space } else { @@ -1210,7 +1218,7 @@ impl ShapeState { .iter() .filter_map(|(&layer, state)| { let vector_data = document.network_interface.compute_modified_vector(layer)?; - let transform = document.metadata().transform_to_document(layer); + let transform = document.metadata().transform_to_document_if_feeds(layer, &document.network_interface); let opposing_handle_lengths = vector_data .colinear_manipulators .iter() @@ -1575,7 +1583,7 @@ impl ShapeState { let mut manipulator_point = None; let vector_data = network_interface.compute_modified_vector(layer)?; - let viewspace = network_interface.document_metadata().transform_to_viewport(layer); + let viewspace = network_interface.document_metadata().transform_to_viewport_if_feeds(layer, network_interface); // Handles for (segment_id, bezier, _, _) in vector_data.segment_bezier_iter() { @@ -1611,7 +1619,7 @@ impl ShapeState { /// Find the `t` value along the path segment we have clicked upon, together with that segment ID. fn closest_segment(&self, network_interface: &NodeNetworkInterface, layer: LayerNodeIdentifier, position: glam::DVec2, tolerance: f64) -> Option { - let transform = network_interface.document_metadata().transform_to_viewport(layer); + let transform = network_interface.document_metadata().transform_to_viewport_if_feeds(layer, network_interface); let layer_pos = transform.inverse().transform_point2(position); let tolerance = tolerance + 0.5; @@ -1785,7 +1793,7 @@ impl ShapeState { pub fn flip_smooth_sharp(&self, network_interface: &NodeNetworkInterface, target: glam::DVec2, tolerance: f64, responses: &mut VecDeque) -> bool { let mut process_layer = |layer| { let vector_data = network_interface.compute_modified_vector(layer)?; - let transform_to_screenspace = network_interface.document_metadata().transform_to_viewport(layer); + let transform_to_screenspace = network_interface.document_metadata().transform_to_viewport_if_feeds(layer, network_interface); let mut result = None; let mut closest_distance_squared = tolerance * tolerance; @@ -1889,7 +1897,7 @@ impl ShapeState { let vector_data = network_interface.compute_modified_vector(layer); let Some(vector_data) = vector_data else { continue }; - let transform = network_interface.document_metadata().transform_to_viewport(layer); + let transform = network_interface.document_metadata().transform_to_viewport_if_feeds(layer, network_interface); assert_eq!(vector_data.segment_domain.ids().len(), vector_data.start_point().count()); assert_eq!(vector_data.segment_domain.ids().len(), vector_data.end_point().count()); diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index dbce924fc..54a6ceb4c 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -718,7 +718,7 @@ impl PathToolData { let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue; }; - let transform = document.metadata().transform_to_document(layer); + let transform = document.metadata().transform_to_document_if_feeds(layer, &document.network_interface); let mut layer_manipulators = HashSet::with_hasher(NoHashBuilder); for point in state.selected_points() { @@ -828,7 +828,7 @@ impl PathToolData { let selected_handle = selection.selected_points().next()?.as_handle()?; let handle_id = selected_handle.to_manipulator_point(); - let layer_to_document = document.metadata().transform_to_document(*layer); + let layer_to_document = document.metadata().transform_to_document_if_feeds(*layer, &document.network_interface); let vector_data = document.network_interface.compute_modified_vector(*layer)?; let handle_position_local = selected_handle.to_manipulator_point().get_position(&vector_data)?; @@ -870,7 +870,7 @@ impl PathToolData { let anchor = handle_id.get_anchor(&vector_data); let (angle, anchor_position) = calculate_adjacent_anchor_tangent(handle_id, anchor, adjacent_anchor, &vector_data); - let layer_to_document = document.metadata().transform_to_document(*layer); + let layer_to_document = document.metadata().transform_to_document_if_feeds(*layer, &document.network_interface); self.adjacent_anchor_offset = handle_id .get_anchor_position(&vector_data) @@ -1017,7 +1017,7 @@ impl PathToolData { } // If already hovering on a segment, then recalculate its closest point else if let Some(closest_segment) = &mut self.segment { - closest_segment.update_closest_point(document.metadata(), position); + closest_segment.update_closest_point(document.metadata(), &document.network_interface, position); if closest_segment.too_far(position, SEGMENT_INSERTION_DISTANCE) { self.segment = None; @@ -1084,7 +1084,7 @@ impl PathToolData { let layer = sliding_point_info.layer; let Some(vector_data) = network_interface.compute_modified_vector(layer) else { return }; - let transform = network_interface.document_metadata().transform_to_viewport(layer); + let transform = network_interface.document_metadata().transform_to_viewport_if_feeds(layer, network_interface); let layer_pos = transform.inverse().transform_point2(target_position); let segments = sliding_point_info.connected_segments; @@ -1412,7 +1412,7 @@ impl Fsm for PathToolFsmState { if let Some(closest_segment) = &tool_data.segment { if tool_options.path_editing_mode.segment_editing_mode { - let transform = document.metadata().transform_to_viewport(closest_segment.layer()); + let transform = document.metadata().transform_to_viewport_if_feeds(closest_segment.layer(), &document.network_interface); overlay_context.outline_overlay_bezier(closest_segment.bezier(), transform); @@ -1430,7 +1430,7 @@ impl Fsm for PathToolFsmState { } } else { let perp = closest_segment.calculate_perp(document); - let point = closest_segment.closest_point(document.metadata()); + let point = closest_segment.closest_point(document.metadata(), &document.network_interface); // Draw an X on the segment if tool_data.delete_segment_pressed { @@ -2280,7 +2280,10 @@ fn get_selection_status(network_interface: &NodeNetworkInterface, shape_state: & return SelectionStatus::None; }; - let coordinates = network_interface.document_metadata().transform_to_document(layer).transform_point2(local_position); + let coordinates = network_interface + .document_metadata() + .transform_to_document_if_feeds(layer, network_interface) + .transform_point2(local_position); let manipulator_angle = if vector_data.colinear(point) { ManipulatorAngle::Colinear } else { ManipulatorAngle::Free }; return SelectionStatus::One(SingleSelectedPoint { diff --git a/node-graph/gstd/src/wasm_application_io.rs b/node-graph/gstd/src/wasm_application_io.rs index 82b526f18..903ecd0d2 100644 --- a/node-graph/gstd/src/wasm_application_io.rs +++ b/node-graph/gstd/src/wasm_application_io.rs @@ -278,12 +278,7 @@ async fn render<'a: 'n, T: 'n + GraphicElementRendered + WasmNotSend>( #[cfg(all(feature = "vello", not(test)))] let use_vello = use_vello && surface_handle.is_some(); - let mut metadata = RenderMetadata { - upstream_footprints: HashMap::new(), - local_transforms: HashMap::new(), - click_targets: HashMap::new(), - clip_targets: HashSet::new(), - }; + let mut metadata = RenderMetadata::default(); data.collect_metadata(&mut metadata, footprint, None); let output_format = render_config.export_format; diff --git a/node-graph/gsvg-renderer/src/renderer.rs b/node-graph/gsvg-renderer/src/renderer.rs index 95e9890a8..70f7c1820 100644 --- a/node-graph/gsvg-renderer/src/renderer.rs +++ b/node-graph/gsvg-renderer/src/renderer.rs @@ -198,6 +198,7 @@ pub fn to_transform(transform: DAffine2) -> usvg::Transform { pub struct RenderMetadata { pub upstream_footprints: HashMap, pub local_transforms: HashMap, + pub first_instance_source_id: HashMap>, pub click_targets: HashMap>, pub clip_targets: HashSet, } @@ -1090,6 +1091,7 @@ impl GraphicElementRendered for GraphicElement { metadata.upstream_footprints.insert(element_id, footprint); // TODO: Find a way to handle more than one row of the graphical data table if let Some(vector_data) = vector_data.instance_ref_iter().next() { + metadata.first_instance_source_id.insert(element_id, *vector_data.source_node_id); metadata.local_transforms.insert(element_id, *vector_data.transform); } }