From 81db7697375f35baaf8e574250219e665c9a3c50 Mon Sep 17 00:00:00 2001 From: Oliver Davies Date: Sun, 6 Apr 2025 02:00:55 -0700 Subject: [PATCH] Minor fixes for 'Round Corners' and 'Mirror' nodes (#2510) * Fix round corners node not properly maintaing click targets, added keep_original bool to mirror node * Fixed fix for the theta angle * Add upgrade script for Mirror node --------- Co-authored-by: Keavon Chambers --- .../portfolio/portfolio_message_handler.rs | 16 ++++++++ node-graph/gcore/src/vector/vector_nodes.rs | 41 +++++++++++++------ 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index f8a8eed4..a74d6ce8 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -806,6 +806,22 @@ impl MessageHandler> for PortfolioMes .set_input(&InputConnector::node(*node_id, 2), NodeInput::value(TaggedValue::Bool(false), false), network_path); } + // Upgrade the Mirror node to add the `keep_original` boolean input + if reference == "Mirror" && inputs_count == 3 { + let node_definition = resolve_document_node_type(reference).unwrap(); + let document_node = node_definition.default_node_template().document_node; + document.network_interface.replace_implementation(node_id, network_path, document_node.implementation.clone()); + + let old_inputs = document.network_interface.replace_inputs(node_id, document_node.inputs.clone(), network_path); + + document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path); + document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path); + document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[2].clone(), network_path); + document + .network_interface + .set_input(&InputConnector::node(*node_id, 3), NodeInput::value(TaggedValue::Bool(true), false), network_path); + } + // Upgrade artboard name being passed as hidden value input to "To Artboard" if reference == "Artboard" && upgrade_from_before_returning_nested_click_targets { let label = document.network_interface.frontend_display_name(node_id, network_path); diff --git a/node-graph/gcore/src/vector/vector_nodes.rs b/node-graph/gcore/src/vector/vector_nodes.rs index d22c83aa..9358a542 100644 --- a/node-graph/gcore/src/vector/vector_nodes.rs +++ b/node-graph/gcore/src/vector/vector_nodes.rs @@ -342,17 +342,21 @@ async fn mirror( #[implementations(VectorDataTable, GraphicGroupTable)] instance: Instances, #[default(0., 0.)] center: DVec2, #[range((-90., 90.))] angle: Angle, + #[default(true)] keep_original: bool, ) -> GraphicGroupTable where Instances: GraphicElementRendered, { let mut result_table = GraphicGroupTable::default(); - let Some(bounding_box) = instance.bounding_box(DAffine2::IDENTITY) else { return result_table }; + // The mirror center is based on the bounding box for now + let Some(bounding_box) = instance.bounding_box(DAffine2::IDENTITY) else { return result_table }; let mirror_center = (bounding_box[0] + bounding_box[1]) / 2. + center; - // Normalize direction vector + + // Normalize the direction vector let normal = DVec2::from_angle(angle.to_radians()); - // Create reflection matrix + + // Create the reflection matrix let reflection = DAffine2::from_mat2_translation( glam::DMat2::from_cols( DVec2::new(1. - 2. * normal.x * normal.x, -2. * normal.y * normal.x), @@ -360,15 +364,20 @@ where ), DVec2::ZERO, ); + // Apply reflection around the center point let modification = DAffine2::from_translation(mirror_center) * reflection * DAffine2::from_translation(-mirror_center); - // Add original instance to result - let original_element = instance.to_graphic_element().clone(); - result_table.push(original_element); + + // Add original instance depending on the keep_original flag + if keep_original { + result_table.push(instance.to_graphic_element()); + } + // Create and add mirrored instance - let mut mirrored_element = instance.to_graphic_element().clone(); + let mut mirrored_element = instance.to_graphic_element(); mirrored_element.new_ids_from_hash(None); - // Finally, apply the transformation to the mirrored instance + + // Apply the transformation to the mirrored instance let mirrored_instance = result_table.push(mirrored_element); *mirrored_instance.transform = modification; @@ -393,6 +402,7 @@ async fn round_corners( let source_transform = source.transform(); let source_transform_inverse = source_transform.inverse(); let source = source.one_instance().instance; + let upstream_graphics_group = source.upstream_graphic_group.clone(); // Flip the roundness to help with user intuition let roundness = 1. - roundness; @@ -402,11 +412,14 @@ async fn round_corners( let mut result = VectorData::empty(); result.style = source.style.clone(); + // Grab the initial point ID as a stable starting point + let mut initial_point_id = source.point_domain.ids().first().copied().unwrap_or(PointId::generate()); + for mut subpath in source.stroke_bezier_paths() { subpath.apply_transform(source_transform); + // End if not enough points for corner rounding if subpath.manipulator_groups().len() < 3 { - // Not enough points for corner rounding result.append_subpath(subpath, false); continue; } @@ -450,28 +463,30 @@ async fn round_corners( let p1 = curr - dir1 * distance_along_edge; let p2 = curr + dir2 * distance_along_edge; - // Add first point with out handle + // Add first point (coming into the rounded corner) new_groups.push(ManipulatorGroup { anchor: p1, in_handle: None, out_handle: Some(curr - dir1 * distance_along_edge * roundness), - id: PointId::generate(), + id: initial_point_id.next_id(), }); - // Add second point with in handle + // Add second point (coming out of the rounded corner) new_groups.push(ManipulatorGroup { anchor: p2, in_handle: Some(curr + dir2 * distance_along_edge * roundness), out_handle: None, - id: PointId::generate(), + id: initial_point_id.next_id(), }); } + // One subpath for each shape let mut rounded_subpath = Subpath::new(new_groups, is_closed); rounded_subpath.apply_transform(source_transform_inverse); result.append_subpath(rounded_subpath, false); } + result.upstream_graphic_group = upstream_graphics_group; let mut result_table = VectorDataTable::new(result); *result_table.transform_mut() = source_transform; result_table