From b1ee9cf41249d9d81f0a74e523b7542a8b72838a Mon Sep 17 00:00:00 2001 From: indierusty Date: Sat, 28 Jun 2025 08:39:18 +0530 Subject: [PATCH] refactor tables to calculate the index of the the row in the vec of instances --- .../node_graph/document_node_definitions.rs | 4 +- .../utility_types/document_metadata.rs | 6 +- .../utility_types/network_interface.rs | 26 +- .../portfolio/portfolio_message_handler.rs | 2 +- .../gcore/src/graphic_element/renderer.rs | 301 +++++++++--------- .../src/vector/vector_data/modification.rs | 6 +- node-graph/graph-craft/src/document/value.rs | 4 +- 7 files changed, 184 insertions(+), 165 deletions(-) diff --git a/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs b/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs index 8c4fa5735..7321dff86 100644 --- a/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs +++ b/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs @@ -1481,7 +1481,7 @@ fn static_nodes() -> Vec { ..Default::default() }, DocumentNode { - inputs: vec![NodeInput::node(NodeId(0), 0), NodeInput::network(concrete!(graphene_std::vector::VectorDataInstancesModification), 1)], + inputs: vec![NodeInput::node(NodeId(0), 0), NodeInput::network(concrete!(graphene_std::vector::VectorDataModification), 1)], manual_composition: Some(generic!(T)), implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::vector::vector_data::modification::PathModifyNode")), ..Default::default() @@ -1495,7 +1495,7 @@ fn static_nodes() -> Vec { }), inputs: vec![ NodeInput::value(TaggedValue::VectorData(VectorDataTable::default()), true), - NodeInput::value(TaggedValue::VectorDataInstancesModification(Default::default()), false), + NodeInput::value(TaggedValue::VectorDataModification(Default::default()), false), ], ..Default::default() }, 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 8eb3defc8..55022cc2f 100644 --- a/editor/src/messages/portfolio/document/utility_types/document_metadata.rs +++ b/editor/src/messages/portfolio/document/utility_types/document_metadata.rs @@ -5,7 +5,7 @@ use glam::{DAffine2, DVec2}; use graph_craft::document::NodeId; use graphene_std::renderer::{ClickTarget, ClickTargetType, Quad}; use graphene_std::transform::Footprint; -use graphene_std::vector::{PointId, VectorData}; +use graphene_std::vector::{PointId, VectorData, VectorDataTable}; use std::collections::{HashMap, HashSet}; use std::num::NonZeroU64; @@ -20,9 +20,9 @@ pub struct DocumentMetadata { pub upstream_footprints: HashMap, pub local_transforms: HashMap, pub structure: HashMap, - pub click_targets: HashMap>, + pub click_targets: HashMap>>, pub clip_targets: HashSet, - pub vector_modify: HashMap, + pub vector_modify: HashMap, /// Transform from document space to viewport space. pub document_to_viewport: DAffine2, } 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 1052300bd..0197ae254 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface.rs @@ -14,7 +14,7 @@ use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, No use graph_craft::{Type, concrete}; use graphene_std::renderer::{ClickTarget, ClickTargetType, Quad}; use graphene_std::transform::Footprint; -use graphene_std::vector::{PointId, VectorData, VectorModificationType}; +use graphene_std::vector::{PointId, VectorData, VectorDataTable, VectorModificationType}; use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypes; use interpreted_executor::node_registry::NODE_REGISTRY; use serde_json::{Value, json}; @@ -3178,15 +3178,27 @@ impl NodeNetworkInterface { (layer_widths, chain_widths, has_left_input_wire) } + pub fn compute_modified_vector_table(&self, layer: LayerNodeIdentifier) -> Option { + todo!() + } + pub fn compute_modified_vector(&self, layer: LayerNodeIdentifier) -> Option { let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, self); - if let Some(vector_data) = graph_layer.upstream_node_id_from_name("Path").and_then(|node| self.document_metadata.vector_modify.get(&node)) { - let mut modified = vector_data.clone(); - if let Some(TaggedValue::VectorModification(modification)) = graph_layer.find_input("Path", 1) { - modification.apply(&mut modified); + let vector_data = graph_layer.upstream_node_id_from_name("Path").and_then(|node| self.document_metadata.vector_modify.get(&node)); + let modification = graph_layer.find_input("Path", 1); + + match (vector_data, modification) { + (Some(vector_data), Some(TaggedValue::VectorDataModification(modification))) => { + let mut vector_data = vector_data.clone(); + for (index, vector_data_instance) in vector_data.instance_mut_iter().enumerate() { + if let Some(vector_modification) = modification.get(&index) { + vector_modification.apply(vector_data_instance.instance); + } + } + return Some(vector_data); } - return Some(modified); + _ => {} } self.document_metadata @@ -3361,7 +3373,7 @@ impl NodeNetworkInterface { }; { let mut value = node.inputs.get_mut(1).and_then(|input| input.as_value_mut()); - let Some(TaggedValue::VectorModification(modification)) = value.as_deref_mut() else { + let Some(TaggedValue::VectorDataModification(modification)) = value.as_deref_mut() else { panic!("Path node does not have modification input"); }; diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index e834f0da2..4ff35422c 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -745,7 +745,7 @@ impl MessageHandler> for PortfolioMes let path_node_type = resolve_document_node_type("Path").expect("Path node does not exist."); let path_node = path_node_type.node_template_input_override([ Some(NodeInput::value(TaggedValue::VectorData(VectorDataTable::new(vector_data)), true)), - Some(NodeInput::value(TaggedValue::VectorModification(Default::default()), false)), + Some(NodeInput::value(TaggedValue::VectorDataModification(Default::default()), false)), ]); // Get the "Spline" node definition and wire it up with the "Path" node as input diff --git a/node-graph/gcore/src/graphic_element/renderer.rs b/node-graph/gcore/src/graphic_element/renderer.rs index 165f5a5ed..dd7c7c1d8 100644 --- a/node-graph/gcore/src/graphic_element/renderer.rs +++ b/node-graph/gcore/src/graphic_element/renderer.rs @@ -8,7 +8,7 @@ use crate::transform::{Footprint, Transform}; use crate::uuid::{NodeId, generate_uuid}; use crate::vector::style::{Fill, Stroke, StrokeAlign, ViewMode}; use crate::vector::{PointId, VectorDataTable}; -use crate::{Artboard, ArtboardGroupTable, Color, GraphicElement, GraphicGroupTable}; +use crate::{ArtboardGroupTable, Color, GraphicElement, GraphicGroupTable}; use base64::Engine; use bezier_rs::Subpath; use dyn_any::DynAny; @@ -354,7 +354,7 @@ pub fn to_transform(transform: DAffine2) -> usvg::Transform { pub struct RenderMetadata { pub upstream_footprints: HashMap, pub local_transforms: HashMap, - pub click_targets: HashMap>, + pub click_targets: HashMap>>, pub clip_targets: HashSet, } @@ -367,7 +367,7 @@ pub trait GraphicElementRendered { fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> Option<[DVec2; 2]>; /// The upstream click targets for each layer are collected during the render so that they do not have to be calculated for each click detection. - fn add_upstream_click_targets(&self, _click_targets: &mut Vec) {} + fn add_upstream_click_targets(&self, _click_targets: &mut HashMap>) {} // TODO: Store all click targets in a vec which contains the AABB, click target, and path // fn add_click_targets(&self, click_targets: &mut Vec<([DVec2; 2], ClickTarget, Vec)>, current_path: Option) {} @@ -524,14 +524,16 @@ impl GraphicElementRendered for GraphicGroupTable { } if let Some(graphic_group_id) = element_id { - let mut all_upstream_click_targets = Vec::new(); + let mut all_upstream_click_targets = HashMap::new(); - for instance in self.instance_ref_iter() { - let mut new_click_targets = Vec::new(); + for (index, instance) in self.instance_ref_iter().enumerate() { + let mut new_click_targets = HashMap::new(); instance.instance.add_upstream_click_targets(&mut new_click_targets); - for click_target in new_click_targets.iter_mut() { - click_target.apply_transform(*instance.transform) + for (_, click_targets) in new_click_targets.iter_mut() { + for click_target in click_targets { + click_target.apply_transform(*instance.transform) + } } all_upstream_click_targets.extend(new_click_targets); @@ -541,14 +543,16 @@ impl GraphicElementRendered for GraphicGroupTable { } } - fn add_upstream_click_targets(&self, click_targets: &mut Vec) { + fn add_upstream_click_targets(&self, click_targets: &mut HashMap>) { for instance in self.instance_ref_iter() { - let mut new_click_targets = Vec::new(); + let mut new_click_targets = HashMap::new(); instance.instance.add_upstream_click_targets(&mut new_click_targets); - for click_target in new_click_targets.iter_mut() { - click_target.apply_transform(*instance.transform) + for (_, click_targets) in new_click_targets.iter_mut() { + for click_target in click_targets { + click_target.apply_transform(*instance.transform) + } } click_targets.extend(new_click_targets); @@ -905,7 +909,7 @@ impl GraphicElementRendered for VectorDataTable { } fn collect_metadata(&self, metadata: &mut RenderMetadata, mut footprint: Footprint, element_id: Option) { - for instance in self.instance_ref_iter() { + for (index, instance) in self.instance_ref_iter().enumerate() { let instance_transform = *instance.transform; let instance = instance.instance; @@ -938,7 +942,10 @@ impl GraphicElementRendered for VectorDataTable { .chain(single_anchors_targets.into_iter()) .collect::>(); - metadata.click_targets.insert(element_id, click_targets); + // metadata.click_targets.insert(element_id, ); + metadata.click_targets.entry(element_id).and_modify(|click_target_map| { + click_target_map.insert(index, click_targets); + }); } if let Some(upstream_graphic_group) = &instance.upstream_graphic_group { @@ -948,8 +955,8 @@ impl GraphicElementRendered for VectorDataTable { } } - fn add_upstream_click_targets(&self, click_targets: &mut Vec) { - for instance in self.instance_ref_iter() { + fn add_upstream_click_targets(&self, click_targets: &mut HashMap>) { + for (index, instance) in self.instance_ref_iter().enumerate() { let stroke_width = instance.instance.style.stroke().as_ref().map_or(0., Stroke::weight); let filled = instance.instance.style.fill() != &Fill::None; let fill = |mut subpath: Subpath<_>| { @@ -958,11 +965,15 @@ impl GraphicElementRendered for VectorDataTable { } subpath }; - click_targets.extend(instance.instance.stroke_bezier_paths().map(fill).map(|subpath| { - let mut click_target = ClickTarget::new_with_subpath(subpath, stroke_width); - click_target.apply_transform(*instance.transform); - click_target - })); + + click_targets + .entry(index) + .or_insert(Vec::new()) + .extend(instance.instance.stroke_bezier_paths().map(fill).map(|subpath| { + let mut click_target = ClickTarget::new_with_subpath(subpath, stroke_width); + click_target.apply_transform(*instance.transform); + click_target + })); // For free-floating anchors, we need to add a click target for each let single_anchors_targets = instance.instance.point_domain.ids().iter().filter_map(|&point_id| { @@ -977,7 +988,8 @@ impl GraphicElementRendered for VectorDataTable { click_target.apply_transform(*instance.transform); Some(click_target) }); - click_targets.extend(single_anchors_targets); + + click_targets.entry(index).or_insert(Vec::new()).extend(single_anchors_targets); } } @@ -992,143 +1004,133 @@ impl GraphicElementRendered for VectorDataTable { } } -impl GraphicElementRendered for Artboard { +impl GraphicElementRendered for ArtboardGroupTable { fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) { - if !render_params.hide_artboards { - // Background - render.leaf_tag("rect", |attributes| { - attributes.push("fill", format!("#{}", self.background.to_rgb_hex_srgb_from_gamma())); - if self.background.a() < 1. { - attributes.push("fill-opacity", ((self.background.a() * 1000.).round() / 1000.).to_string()); - } - attributes.push("x", self.location.x.min(self.location.x + self.dimensions.x).to_string()); - attributes.push("y", self.location.y.min(self.location.y + self.dimensions.y).to_string()); - attributes.push("width", self.dimensions.x.abs().to_string()); - attributes.push("height", self.dimensions.y.abs().to_string()); - }); + for artboard in self.instance_ref_iter() { + let artboard = artboard.instance; + + if !render_params.hide_artboards { + // Background + render.leaf_tag("rect", |attributes| { + attributes.push("fill", format!("#{}", artboard.background.to_rgb_hex_srgb_from_gamma())); + if artboard.background.a() < 1. { + attributes.push("fill-opacity", ((artboard.background.a() * 1000.).round() / 1000.).to_string()); + } + attributes.push("x", artboard.location.x.min(artboard.location.x + artboard.dimensions.x).to_string()); + attributes.push("y", artboard.location.y.min(artboard.location.y + artboard.dimensions.y).to_string()); + attributes.push("width", artboard.dimensions.x.abs().to_string()); + attributes.push("height", artboard.dimensions.y.abs().to_string()); + }); + } + + // Contents group (includes the artwork but not the background) + render.parent_tag( + // SVG group tag + "g", + // Group tag attributes + |attributes| { + let matrix = format_transform_matrix(artboard.transform()); + if !matrix.is_empty() { + attributes.push("transform", matrix); + } + + if artboard.clip { + let id = format!("artboard-{}", generate_uuid()); + let selector = format!("url(#{id})"); + + write!( + &mut attributes.0.svg_defs, + r##""##, + artboard.dimensions.x, artboard.dimensions.y, + ) + .unwrap(); + attributes.push("clip-path", selector); + } + }, + // Artboard contents + |render| { + artboard.graphic_group.render_svg(render, render_params); + }, + ); } - - // Contents group (includes the artwork but not the background) - render.parent_tag( - // SVG group tag - "g", - // Group tag attributes - |attributes| { - let matrix = format_transform_matrix(self.transform()); - if !matrix.is_empty() { - attributes.push("transform", matrix); - } - - if self.clip { - let id = format!("artboard-{}", generate_uuid()); - let selector = format!("url(#{id})"); - - write!( - &mut attributes.0.svg_defs, - r##""##, - self.dimensions.x, self.dimensions.y, - ) - .unwrap(); - attributes.push("clip-path", selector); - } - }, - // Artboard contents - |render| { - self.graphic_group.render_svg(render, render_params); - }, - ); } #[cfg(feature = "vello")] fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) { use vello::peniko; - - // Render background - let color = peniko::Color::new([self.background.r(), self.background.g(), self.background.b(), self.background.a()]); - let [a, b] = [self.location.as_dvec2(), self.location.as_dvec2() + self.dimensions.as_dvec2()]; - let rect = kurbo::Rect::new(a.x.min(b.x), a.y.min(b.y), a.x.max(b.x), a.y.max(b.y)); - - scene.push_layer(peniko::Mix::Normal, 1., kurbo::Affine::new(transform.to_cols_array()), &rect); - scene.fill(peniko::Fill::NonZero, kurbo::Affine::new(transform.to_cols_array()), color, None, &rect); - scene.pop_layer(); - - if self.clip { - let blend_mode = peniko::BlendMode::new(peniko::Mix::Clip, peniko::Compose::SrcOver); - scene.push_layer(blend_mode, 1., kurbo::Affine::new(transform.to_cols_array()), &rect); - } - // Since the graphic group's transform is right multiplied in when rendering the graphic group, we just need to right multiply by the offset here. - let child_transform = transform * DAffine2::from_translation(self.location.as_dvec2()); - self.graphic_group.render_to_vello(scene, child_transform, context, render_params); - if self.clip { - scene.pop_layer(); - } - } - - fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> Option<[DVec2; 2]> { - let artboard_bounds = (transform * Quad::from_box([self.location.as_dvec2(), self.location.as_dvec2() + self.dimensions.as_dvec2()])).bounding_box(); - if self.clip { - Some(artboard_bounds) - } else { - [self.graphic_group.bounding_box(transform, include_stroke), Some(artboard_bounds)] - .into_iter() - .flatten() - .reduce(Quad::combine_bounds) - } - } - - fn collect_metadata(&self, metadata: &mut RenderMetadata, mut footprint: Footprint, element_id: Option) { - if let Some(element_id) = element_id { - let subpath = Subpath::new_rect(DVec2::ZERO, self.dimensions.as_dvec2()); - metadata.click_targets.insert(element_id, vec![ClickTarget::new_with_subpath(subpath, 0.)]); - metadata.upstream_footprints.insert(element_id, footprint); - metadata.local_transforms.insert(element_id, DAffine2::from_translation(self.location.as_dvec2())); - if self.clip { - metadata.clip_targets.insert(element_id); - } - } - footprint.transform *= self.transform(); - self.graphic_group.collect_metadata(metadata, footprint, None); - } - - fn add_upstream_click_targets(&self, click_targets: &mut Vec) { - let subpath_rectangle = Subpath::new_rect(DVec2::ZERO, self.dimensions.as_dvec2()); - click_targets.push(ClickTarget::new_with_subpath(subpath_rectangle, 0.)); - } - - fn contains_artboard(&self) -> bool { - true - } -} - -impl GraphicElementRendered for ArtboardGroupTable { - fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) { - for artboard in self.instance_ref_iter() { - artboard.instance.render_svg(render, render_params); - } - } - - #[cfg(feature = "vello")] - fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) { for instance in self.instance_ref_iter() { - instance.instance.render_to_vello(scene, transform, context, render_params); + let instance = instance.instance; + + // Render background + let color = peniko::Color::new([instance.background.r(), instance.background.g(), instance.background.b(), instance.background.a()]); + let [a, b] = [instance.location.as_dvec2(), instance.location.as_dvec2() + instance.dimensions.as_dvec2()]; + let rect = kurbo::Rect::new(a.x.min(b.x), a.y.min(b.y), a.x.max(b.x), a.y.max(b.y)); + + scene.push_layer(peniko::Mix::Normal, 1., kurbo::Affine::new(transform.to_cols_array()), &rect); + scene.fill(peniko::Fill::NonZero, kurbo::Affine::new(transform.to_cols_array()), color, None, &rect); + scene.pop_layer(); + + if instance.clip { + let blend_mode = peniko::BlendMode::new(peniko::Mix::Clip, peniko::Compose::SrcOver); + scene.push_layer(blend_mode, 1., kurbo::Affine::new(transform.to_cols_array()), &rect); + } + // Since the graphic group's transform is right multiplied in when rendering the graphic group, we just need to right multiply by the offset here. + let child_transform = transform * DAffine2::from_translation(instance.location.as_dvec2()); + instance.graphic_group.render_to_vello(scene, child_transform, context, render_params); + if instance.clip { + scene.pop_layer(); + } } } fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> Option<[DVec2; 2]> { self.instance_ref_iter() - .filter_map(|instance| instance.instance.bounding_box(transform, include_stroke)) + .filter_map(|instance| { + let instance = instance.instance; + let artboard_bounds = (transform * Quad::from_box([instance.location.as_dvec2(), instance.location.as_dvec2() + instance.dimensions.as_dvec2()])).bounding_box(); + if instance.clip { + Some(artboard_bounds) + } else { + [instance.graphic_group.bounding_box(transform, include_stroke), Some(artboard_bounds)] + .into_iter() + .flatten() + .reduce(Quad::combine_bounds) + } + }) .reduce(Quad::combine_bounds) } - fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, _element_id: Option) { - for instance in self.instance_ref_iter() { - instance.instance.collect_metadata(metadata, footprint, *instance.source_node_id); + fn collect_metadata(&self, metadata: &mut RenderMetadata, mut footprint: Footprint, element_id: Option) { + for (index, instance) in self.instance_ref_iter().enumerate() { + let instance = instance.instance; + + if let Some(element_id) = element_id { + let subpath = Subpath::new_rect(DVec2::ZERO, instance.dimensions.as_dvec2()); + + metadata + .click_targets + .entry(element_id) + .or_insert(HashMap::new()) + .entry(index) + .or_insert(Vec::new()) + .push(ClickTarget::new_with_subpath(subpath, 0.)); + + metadata.upstream_footprints.insert(element_id, footprint); + metadata.local_transforms.insert(element_id, DAffine2::from_translation(instance.location.as_dvec2())); + if instance.clip { + metadata.clip_targets.insert(element_id); + } + } + footprint.transform *= instance.transform(); + instance.graphic_group.collect_metadata(metadata, footprint, None); } } - fn add_upstream_click_targets(&self, click_targets: &mut Vec) { - for instance in self.instance_ref_iter() { - instance.instance.add_upstream_click_targets(click_targets); + fn add_upstream_click_targets(&self, click_targets: &mut HashMap>) { + for (index, instance) in self.instance_ref_iter().enumerate() { + let subpath_rectangle = Subpath::new_rect(DVec2::ZERO, instance.instance.dimensions.as_dvec2()); + + click_targets.entry(index).or_insert(Vec::new()).push(ClickTarget::new_with_subpath(subpath_rectangle, 0.)); } } @@ -1205,7 +1207,9 @@ impl GraphicElementRendered for RasterDataTable { let Some(element_id) = element_id else { return }; let subpath = Subpath::new_rect(DVec2::ZERO, DVec2::ONE); - metadata.click_targets.insert(element_id, vec![ClickTarget::new_with_subpath(subpath, 0.)]); + let mut click_targets = HashMap::new(); + click_targets.insert(0, vec![ClickTarget::new_with_subpath(subpath, 0.)]); + metadata.click_targets.insert(element_id, click_targets); 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(image) = self.instance_ref_iter().next() { @@ -1213,9 +1217,9 @@ impl GraphicElementRendered for RasterDataTable { } } - fn add_upstream_click_targets(&self, click_targets: &mut Vec) { + fn add_upstream_click_targets(&self, click_targets: &mut HashMap>) { let subpath = Subpath::new_rect(DVec2::ZERO, DVec2::ONE); - click_targets.push(ClickTarget::new_with_subpath(subpath, 0.)); + click_targets.entry(0).or_insert(Vec::new()).push(ClickTarget::new_with_subpath(subpath, 0.)); } fn to_graphic_element(&self) -> GraphicElement { @@ -1272,7 +1276,10 @@ impl GraphicElementRendered for RasterDataTable { let Some(element_id) = element_id else { return }; let subpath = Subpath::new_rect(DVec2::ZERO, DVec2::ONE); - metadata.click_targets.insert(element_id, vec![ClickTarget::new_with_subpath(subpath, 0.)]); + let mut click_targets = HashMap::new(); + click_targets.insert(0, vec![ClickTarget::new_with_subpath(subpath, 0.)]); + + metadata.click_targets.insert(element_id, click_targets); 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(image) = self.instance_ref_iter().next() { @@ -1280,9 +1287,9 @@ impl GraphicElementRendered for RasterDataTable { } } - fn add_upstream_click_targets(&self, click_targets: &mut Vec) { + fn add_upstream_click_targets(&self, click_targets: &mut HashMap>) { let subpath = Subpath::new_rect(DVec2::ZERO, DVec2::ONE); - click_targets.push(ClickTarget::new_with_subpath(subpath, 0.)); + click_targets.entry(0).or_insert(Vec::new()).push(ClickTarget::new_with_subpath(subpath, 0.)); } } @@ -1355,7 +1362,7 @@ impl GraphicElementRendered for GraphicElement { } } - fn add_upstream_click_targets(&self, click_targets: &mut Vec) { + fn add_upstream_click_targets(&self, click_targets: &mut HashMap>) { match self { GraphicElement::VectorData(vector_data) => vector_data.add_upstream_click_targets(click_targets), GraphicElement::RasterDataCPU(raster) => raster.add_upstream_click_targets(click_targets), diff --git a/node-graph/gcore/src/vector/vector_data/modification.rs b/node-graph/gcore/src/vector/vector_data/modification.rs index 2907729ed..856351434 100644 --- a/node-graph/gcore/src/vector/vector_data/modification.rs +++ b/node-graph/gcore/src/vector/vector_data/modification.rs @@ -5,7 +5,7 @@ use crate::uuid::generate_uuid; use bezier_rs::BezierHandles; use dyn_any::DynAny; use kurbo::{BezPath, PathEl, Point}; -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, HashMap, HashSet}; use std::hash::BuildHasher; /// Represents a procedural change to the [`PointDomain`] in [`VectorData`]. @@ -418,11 +418,11 @@ impl Hash for VectorModification { } } -pub type VectorDataInstancesModification = HashMap; +pub type VectorDataModification = BTreeMap; /// A node that applies a procedural modification to some [`VectorData`]. #[node_macro::node(category(""))] -async fn path_modify(_ctx: impl Ctx, mut vector_data: VectorDataTable, modification: VectorDataInstancesModification) -> VectorDataTable { +async fn path_modify(_ctx: impl Ctx, mut vector_data: VectorDataTable, modification: VectorDataModification) -> VectorDataTable { for (index, vector_data_instance) in vector_data.instance_mut_iter().enumerate() { if let Some(vector_modification) = modification.get(&index) { vector_modification.apply(vector_data_instance.instance); diff --git a/node-graph/graph-craft/src/document/value.rs b/node-graph/graph-craft/src/document/value.rs index 8c30e9f1d..18c23f175 100644 --- a/node-graph/graph-craft/src/document/value.rs +++ b/node-graph/graph-craft/src/document/value.rs @@ -11,7 +11,7 @@ use graphene_core::raster_types::CPU; use graphene_core::renderer::RenderMetadata; use graphene_core::transform::ReferencePoint; use graphene_core::uuid::NodeId; -use graphene_core::vector::VectorDataInstancesModification; +use graphene_core::vector::VectorDataModification; use graphene_core::vector::style::Fill; use graphene_core::{Color, MemoHash, Node, Type}; use std::fmt::Display; @@ -215,7 +215,7 @@ tagged_value! { DocumentNode(DocumentNode), Curve(graphene_core::raster::curve::Curve), Footprint(graphene_core::transform::Footprint), - VectorDataInstancesModification(VectorDataInstancesModification), + VectorDataModification(VectorDataModification), FontCache(Arc), // ========== // ENUM TYPES