mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-07-08 00:05:00 +00:00
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
This commit is contained in:
parent
3496e22f55
commit
f6e592da5b
3 changed files with 64 additions and 6 deletions
|
@ -1214,11 +1214,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> 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()
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue