From f6e592da5bf952ae5beffe392960c63f3e2b92f6 Mon Sep 17 00:00:00 2001 From: 0SlowPoke0 <142654792+0SlowPoke0@users.noreply.github.com> Date: Mon, 26 May 2025 14:45:31 +0530 Subject: [PATCH] New nodes: 'Point Inside Shape' and 'Close Path' (#2673) * add point inside shape,close path,and disabled node to layer conversion * removed the usage one_instance * code review --- .../node_graph/node_graph_message_handler.rs | 5 -- node-graph/gcore/src/vector/vector_data.rs | 48 ++++++++++++++++++- node-graph/gcore/src/vector/vector_nodes.rs | 17 +++++++ 3 files changed, 64 insertions(+), 6 deletions(-) diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs index e480016f..1c4c4267 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs @@ -1214,11 +1214,6 @@ impl<'a> MessageHandler> for NodeGrap if is_stack_wire(&wire) { stack_wires.push(wire) } else { node_wires.push(wire) } } - // Auto convert node to layer when inserting on a single stack wire - if stack_wires.len() == 1 && node_wires.is_empty() { - network_interface.set_to_node_or_layer(&selected_node_id, selection_network_path, true) - } - let overlapping_wire = if network_interface.is_layer(&selected_node_id, selection_network_path) { if stack_wires.len() == 1 { stack_wires.first() diff --git a/node-graph/gcore/src/vector/vector_data.rs b/node-graph/gcore/src/vector/vector_data.rs index 1745c146..480ae8f7 100644 --- a/node-graph/gcore/src/vector/vector_data.rs +++ b/node-graph/gcore/src/vector/vector_data.rs @@ -2,7 +2,7 @@ mod attributes; mod indexed; mod modification; -use super::misc::point_to_dvec2; +use super::misc::{dvec2_to_point, point_to_dvec2}; use super::style::{PathStyle, Stroke}; use crate::instances::Instances; use crate::{AlphaBlending, Color, GraphicGroupTable}; @@ -12,6 +12,7 @@ use core::borrow::Borrow; use dyn_any::DynAny; use glam::{DAffine2, DVec2}; pub use indexed::VectorDataIndex; +use kurbo::{Affine, Shape}; pub use modification::*; use std::collections::HashMap; @@ -193,6 +194,23 @@ impl VectorData { vector_data } + pub fn close_subpaths(&mut self) { + let segments_to_add: Vec<_> = self + .stroke_bezier_paths() + .filter(|subpath| !subpath.closed) + .filter_map(|subpath| { + let (first, last) = subpath.manipulator_groups().first().zip(subpath.manipulator_groups().last())?; + let (start, end) = self.point_domain.resolve_id(first.id).zip(self.point_domain.resolve_id(last.id))?; + Some((start, end)) + }) + .collect(); + + for (start, end) in segments_to_add { + let segment_id = self.segment_domain.next_id().next_id(); + self.segment_domain.push(segment_id, start, end, bezier_rs::BezierHandles::Linear, StrokeId::ZERO); + } + } + /// Compute the bounding boxes of the subpaths without any transform pub fn bounding_box(&self) -> Option<[DVec2; 2]> { self.bounding_box_with_transform(DAffine2::IDENTITY) @@ -316,6 +334,34 @@ impl VectorData { self.point_domain.resolve_id(point).map_or(0, |point| self.segment_domain.connected_count(point)) } + pub fn check_point_inside_shape(&self, vector_data_transform: DAffine2, point: DVec2) -> bool { + let bez_paths: Vec<_> = self + .stroke_bezpath_iter() + .map(|mut bezpath| { + // TODO: apply transform to points instead of modifying the paths + bezpath.apply_affine(Affine::new(vector_data_transform.to_cols_array())); + bezpath.close_path(); + let bbox = bezpath.bounding_box(); + (bezpath, bbox) + }) + .collect(); + + // Check against all paths the point is contained in to compute the correct winding number + let mut number = 0; + + for (shape, bbox) in bez_paths { + if bbox.x0 > point.x || bbox.y0 > point.y || bbox.x1 < point.x || bbox.y1 < point.y { + continue; + } + + let winding = shape.winding(dvec2_to_point(point)); + number += winding; + } + + // Non-zero fill rule + number != 0 + } + /// Points that can be extended from. /// /// This is usually only points with exactly one connection unless vector meshes are enabled. diff --git a/node-graph/gcore/src/vector/vector_nodes.rs b/node-graph/gcore/src/vector/vector_nodes.rs index 1c642a4b..0295141d 100644 --- a/node-graph/gcore/src/vector/vector_nodes.rs +++ b/node-graph/gcore/src/vector/vector_nodes.rs @@ -1715,6 +1715,23 @@ fn bevel(_: impl Ctx, source: VectorDataTable, #[default(10.)] distance: Length) result } +#[node_macro::node(category("Vector"), path(graphene_core::vector))] +fn close_path(_: impl Ctx, source: VectorDataTable) -> VectorDataTable { + let mut new_table = VectorDataTable::empty(); + + for mut source_instance in source.instance_iter() { + source_instance.instance.close_subpaths(); + new_table.push(source_instance); + } + + new_table +} + +#[node_macro::node(category("Vector"), path(graphene_core::vector))] +fn point_inside(_: impl Ctx, source: VectorDataTable, point: DVec2) -> bool { + source.instance_iter().any(|instance| instance.instance.check_point_inside_shape(instance.transform, point)) +} + #[node_macro::node(name("Merge by Distance"), category("Vector"), path(graphene_core::vector))] fn merge_by_distance(_: impl Ctx, source: VectorDataTable, #[default(10.)] distance: Length) -> VectorDataTable { let source_transform = source.transform();