Extract gpath_bool from mod gstd::ops path_bool-related nodes (#2762)

* cargo shear

* Extract `gpath_bool` from `mod gstd::ops` path_bool-related nodes
This commit is contained in:
Firestar99 2025-06-27 15:12:08 +02:00 committed by GitHub
parent 9cf8d2cd05
commit ffc6c5532b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 128 additions and 107 deletions

30
Cargo.lock generated
View file

@ -2132,6 +2132,7 @@ dependencies = [
"graph-craft",
"graphene-application-io",
"graphene-core",
"graphene-path-bool",
"iai-callgrind",
"js-sys",
"log",
@ -2214,12 +2215,26 @@ dependencies = [
"wgpu",
]
[[package]]
name = "graphene-path-bool"
version = "0.1.0"
dependencies = [
"bezier-rs",
"dyn-any",
"glam",
"graphene-core",
"log",
"node-macro",
"path-bool",
"serde",
"specta",
]
[[package]]
name = "graphene-std"
version = "0.1.0"
dependencies = [
"base64 0.22.1",
"bezier-rs",
"bytemuck",
"dyn-any",
"fastnoise-lite",
@ -2228,16 +2243,15 @@ dependencies = [
"graph-craft",
"graphene-application-io",
"graphene-core",
"graphene-path-bool",
"image",
"log",
"ndarray",
"node-macro",
"path-bool",
"rand 0.9.0",
"rand_chacha 0.9.0",
"reqwest",
"tokio",
"usvg",
"vello",
"wasm-bindgen",
"wasm-bindgen-futures",
@ -2313,7 +2327,6 @@ dependencies = [
name = "graphite-wasm"
version = "0.0.0"
dependencies = [
"glam",
"graph-craft",
"graphene-std",
"graphite-editor",
@ -2948,6 +2961,7 @@ dependencies = [
"glam",
"graph-craft",
"graphene-core",
"graphene-path-bool",
"graphene-std",
"log",
"once_cell",
@ -4481,17 +4495,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
name = "preprocessor"
version = "0.1.0"
dependencies = [
"base64 0.22.1",
"dyn-any",
"futures",
"glam",
"graph-craft",
"graphene-std",
"interpreted-executor",
"log",
"serde",
"serde_json",
"tokio",
]
[[package]]

View file

@ -7,6 +7,7 @@ members = [
"node-graph/gapplication-io",
"node-graph/gcore",
"node-graph/gstd",
"node-graph/gpath-bool",
"node-graph/graph-craft",
"node-graph/graphene-cli",
"node-graph/interpreted-executor",
@ -23,6 +24,7 @@ default-members = [
"frontend/wasm",
"node-graph/gcore",
"node-graph/gstd",
"node-graph/gpath-bool",
"node-graph/graph-craft",
"node-graph/graphene-cli",
"node-graph/interpreted-executor",
@ -39,6 +41,7 @@ math-parser = { path = "libraries/math-parser" }
path-bool = { path = "libraries/path-bool" }
graphene-application-io = { path = "node-graph/gapplication-io" }
graphene-core = { path = "node-graph/gcore" }
graphene-path-bool = { path = "node-graph/gpath-bool" }
graph-craft = { path = "node-graph/graph-craft" }
graphene-std = { path = "node-graph/gstd" }
interpreted-executor = { path = "node-graph/interpreted-executor" }

View file

@ -30,11 +30,12 @@ use glam::{DAffine2, DVec2, IVec2};
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeInput, NodeNetwork, OldNodeNetwork};
use graphene_std::math::quad::Quad;
use graphene_std::path_bool::{boolean_intersect, path_bool_lib};
use graphene_std::raster::BlendMode;
use graphene_std::raster_types::{Raster, RasterDataTable};
use graphene_std::vector::PointId;
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
use graphene_std::vector::style::ViewMode;
use graphene_std::vector::{PointId, path_bool_lib};
use std::time::Duration;
pub struct DocumentMessageData<'a> {
@ -2962,7 +2963,7 @@ impl<'a> ClickXRayIter<'a> {
// We do this on this using the target area to reduce computation (as the target area is usually very simple).
if clip && intersects {
let clip_path = click_targets_to_path_lib_segments(click_targets.iter().flat_map(|x| x.iter()), transform);
let subtracted = graphene_std::vector::boolean_intersect(path, clip_path).into_iter().flatten().collect::<Vec<_>>();
let subtracted = boolean_intersect(path, clip_path).into_iter().flatten().collect::<Vec<_>>();
if subtracted.is_empty() {
use_children = false;
} else {

View file

@ -79,7 +79,7 @@ pub enum GraphOperationMessage {
},
NewBooleanOperationLayer {
id: NodeId,
operation: graphene_std::vector::misc::BooleanOperation,
operation: graphene_std::path_bool::BooleanOperation,
parent: LayerNodeIdentifier,
insert_index: usize,
},

View file

@ -10,8 +10,8 @@ use glam::{DAffine2, DVec2, IVec2};
use graph_craft::document::{NodeId, NodeInput};
use graphene_std::Color;
use graphene_std::renderer::Quad;
use graphene_std::renderer::convert_usvg_path::convert_usvg_path;
use graphene_std::text::{Font, TypesettingConfig};
use graphene_std::vector::convert_usvg_path;
use graphene_std::vector::style::{Fill, Gradient, GradientStops, GradientType, PaintOrder, Stroke, StrokeAlign, StrokeCap, StrokeJoin};
#[derive(Debug, Clone)]

View file

@ -134,7 +134,7 @@ impl<'a> ModifyInputsContext<'a> {
LayerNodeIdentifier::new(new_id, self.network_interface, &[])
}
pub fn insert_boolean_data(&mut self, operation: graphene_std::vector::misc::BooleanOperation, layer: LayerNodeIdentifier) {
pub fn insert_boolean_data(&mut self, operation: graphene_std::path_bool::BooleanOperation, layer: LayerNodeIdentifier) {
let boolean = resolve_document_node_type("Boolean Operation").expect("Boolean node does not exist").node_template_input_override([
Some(NodeInput::value(TaggedValue::GraphicGroup(graphene_std::GraphicGroupTable::default()), true)),
Some(NodeInput::value(TaggedValue::BooleanOperation(operation), false)),

View file

@ -1734,7 +1734,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
nodes: vec![
DocumentNode {
inputs: vec![NodeInput::network(concrete!(VectorDataTable), 0), NodeInput::network(concrete!(vector::style::Fill), 1)],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::vector::BooleanOperationNode")),
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_path_bool::BooleanOperationNode")),
manual_composition: Some(generic!(T)),
..Default::default()
},
@ -1765,7 +1765,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}),
inputs: vec![
NodeInput::value(TaggedValue::GraphicGroup(GraphicGroupTable::default()), true),
NodeInput::value(TaggedValue::BooleanOperation(vector::misc::BooleanOperation::Union), false),
NodeInput::value(TaggedValue::BooleanOperation(path_bool::BooleanOperation::Union), false),
],
..Default::default()
},

View file

@ -13,6 +13,7 @@ use graph_craft::document::value::TaggedValue;
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput};
use graphene_std::animation::RealTimeMode;
use graphene_std::ops::XY;
use graphene_std::path_bool::BooleanOperation;
use graphene_std::raster::curve::Curve;
use graphene_std::raster::{
BlendMode, CellularDistanceFunction, CellularReturnType, Color, DomainWarpType, FractalType, LuminanceCalculation, NoiseType, RedGreenBlue, RedGreenBlueAlpha, RelativeAbsolute,
@ -22,8 +23,8 @@ use graphene_std::raster_types::{CPU, GPU, RasterDataTable};
use graphene_std::text::Font;
use graphene_std::transform::{Footprint, ReferencePoint};
use graphene_std::vector::VectorDataTable;
use graphene_std::vector::misc::GridType;
use graphene_std::vector::misc::{ArcType, MergeByDistanceAlgorithm};
use graphene_std::vector::misc::{BooleanOperation, GridType};
use graphene_std::vector::misc::{CentroidType, PointSpacingType};
use graphene_std::vector::style::{Fill, FillChoice, FillType, GradientStops};
use graphene_std::vector::style::{GradientType, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin};

View file

@ -693,5 +693,5 @@ impl PTZ {
#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum GroupFolderType {
Layer,
BooleanOperation(graphene_std::vector::misc::BooleanOperation),
BooleanOperation(graphene_std::path_bool::BooleanOperation),
}

View file

@ -70,6 +70,7 @@ const REPLACEMENTS: &[(&str, &str)] = &[
("graphene_core::transform::CullNode", "graphene_core::ops::IdentityNode"),
("graphene_std::raster::MaskImageNode", "graphene_std::raster::MaskNode"),
("graphene_core::vector::FlattenVectorElementsNode", "graphene_core::vector::FlattenPathNode"),
("graphene_std::vector::BooleanOperationNode", "graphene_path_bool::BooleanOperationNode"),
];
pub fn document_migration_string_preprocessing(document_serialized_content: String) -> String {

View file

@ -4,7 +4,7 @@ use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis, GroupFolderType};
use crate::messages::prelude::*;
use graphene_std::vector::misc::BooleanOperation;
use graphene_std::path_bool::BooleanOperation;
#[derive(Debug, Clone, Default)]
pub struct MenuBarMessageHandler {

View file

@ -22,10 +22,10 @@ use crate::messages::tool::common_functionality::utility_functions::{resize_boun
use bezier_rs::Subpath;
use glam::DMat2;
use graph_craft::document::NodeId;
use graphene_std::path_bool::BooleanOperation;
use graphene_std::renderer::Quad;
use graphene_std::renderer::Rect;
use graphene_std::transform::ReferencePoint;
use graphene_std::vector::misc::BooleanOperation;
use std::fmt;
#[derive(Default)]

View file

@ -35,7 +35,6 @@ wasm-bindgen = { workspace = true }
serde-wasm-bindgen = { workspace = true }
js-sys = { workspace = true }
wasm-bindgen-futures = { workspace = true }
glam = { workspace = true }
math-parser = { workspace = true }
wgpu = { workspace = true }
web-sys = { workspace = true }

View file

@ -1,3 +1,5 @@
pub mod convert_usvg_path;
use crate::instances::Instance;
pub use crate::math::quad::Quad;
pub use crate::math::rect::Rect;

View file

@ -0,0 +1,47 @@
use crate::vector::PointId;
use bezier_rs::{ManipulatorGroup, Subpath};
use glam::DVec2;
pub fn convert_usvg_path(path: &usvg::Path) -> Vec<Subpath<PointId>> {
let mut subpaths = Vec::new();
let mut groups = Vec::new();
let mut points = path.data().points().iter();
let to_vec = |p: &usvg::tiny_skia_path::Point| DVec2::new(p.x as f64, p.y as f64);
for verb in path.data().verbs() {
match verb {
usvg::tiny_skia_path::PathVerb::Move => {
subpaths.push(Subpath::new(std::mem::take(&mut groups), false));
let Some(start) = points.next().map(to_vec) else { continue };
groups.push(ManipulatorGroup::new(start, Some(start), Some(start)));
}
usvg::tiny_skia_path::PathVerb::Line => {
let Some(end) = points.next().map(to_vec) else { continue };
groups.push(ManipulatorGroup::new(end, Some(end), Some(end)));
}
usvg::tiny_skia_path::PathVerb::Quad => {
let Some(handle) = points.next().map(to_vec) else { continue };
let Some(end) = points.next().map(to_vec) else { continue };
if let Some(last) = groups.last_mut() {
last.out_handle = Some(last.anchor + (2. / 3.) * (handle - last.anchor));
}
groups.push(ManipulatorGroup::new(end, Some(end + (2. / 3.) * (handle - end)), Some(end)));
}
usvg::tiny_skia_path::PathVerb::Cubic => {
let Some(first_handle) = points.next().map(to_vec) else { continue };
let Some(second_handle) = points.next().map(to_vec) else { continue };
let Some(end) = points.next().map(to_vec) else { continue };
if let Some(last) = groups.last_mut() {
last.out_handle = Some(first_handle);
}
groups.push(ManipulatorGroup::new(end, Some(second_handle), Some(end)));
}
usvg::tiny_skia_path::PathVerb::Close => {
subpaths.push(Subpath::new(std::mem::take(&mut groups), true));
}
}
}
subpaths.push(Subpath::new(groups, false));
subpaths
}

View file

@ -13,22 +13,6 @@ pub enum CentroidType {
Length,
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, Hash, DynAny, specta::Type, node_macro::ChoiceType)]
#[widget(Radio)]
pub enum BooleanOperation {
#[default]
#[icon("BooleanUnion")]
Union,
#[icon("BooleanSubtractFront")]
SubtractFront,
#[icon("BooleanSubtractBack")]
SubtractBack,
#[icon("BooleanIntersect")]
Intersect,
#[icon("BooleanDifference")]
Difference,
}
pub trait AsU64 {
fn as_u64(&self) -> u64;
}

View file

@ -0,0 +1,19 @@
[package]
name = "graphene-path-bool"
version = "0.1.0"
edition = "2024"
description = "graphene path bool nodes"
authors = ["Graphite Authors <contact@graphite.rs>"]
license = "MIT OR Apache-2.0"
[dependencies]
# Local dependencies
dyn-any = { workspace = true }
bezier-rs = { workspace = true }
graphene-core = { workspace = true }
node-macro = { workspace = true }
glam = { workspace = true }
specta = { workspace = true }
log = { workspace = true }
path-bool = { workspace = true }
serde = { workspace = true }

View file

@ -1,10 +1,10 @@
use bezier_rs::{ManipulatorGroup, Subpath};
use dyn_any::DynAny;
use glam::{DAffine2, DVec2};
use graphene_core::instances::{Instance, InstanceRef};
use graphene_core::vector::algorithms::merge_by_distance::MergeByDistanceExt;
use graphene_core::vector::misc::BooleanOperation;
use graphene_core::vector::style::Fill;
pub use graphene_core::vector::*;
use graphene_core::vector::{PointId, VectorData, VectorDataTable};
use graphene_core::{Color, Ctx, GraphicElement, GraphicGroupTable};
pub use path_bool as path_bool_lib;
use path_bool::{FillRule, PathBooleanOperation};
@ -14,6 +14,22 @@ use std::ops::Mul;
// TODO: since before we used a Vec of single-row tables and now we use a single table
// TODO: with multiple rows while still assuming a single row for the boolean operations.
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, Hash, DynAny, specta::Type, node_macro::ChoiceType)]
#[widget(Radio)]
pub enum BooleanOperation {
#[default]
#[icon("BooleanUnion")]
Union,
#[icon("BooleanSubtractFront")]
SubtractFront,
#[icon("BooleanSubtractBack")]
SubtractBack,
#[icon("BooleanIntersect")]
Intersect,
#[icon("BooleanDifference")]
Difference,
}
/// Combines the geometric forms of one or more closed paths into a new vector path that results from cutting or joining the paths by the chosen method.
#[node_macro::node(category(""))]
async fn boolean_operation<I: Into<GraphicGroupTable> + 'n + Send + Clone>(
@ -356,50 +372,6 @@ fn from_path(path_data: &[Path]) -> VectorData {
VectorData::from_subpaths(all_subpaths, false)
}
pub fn convert_usvg_path(path: &usvg::Path) -> Vec<Subpath<PointId>> {
let mut subpaths = Vec::new();
let mut groups = Vec::new();
let mut points = path.data().points().iter();
let to_vec = |p: &usvg::tiny_skia_path::Point| DVec2::new(p.x as f64, p.y as f64);
for verb in path.data().verbs() {
match verb {
usvg::tiny_skia_path::PathVerb::Move => {
subpaths.push(Subpath::new(std::mem::take(&mut groups), false));
let Some(start) = points.next().map(to_vec) else { continue };
groups.push(ManipulatorGroup::new(start, Some(start), Some(start)));
}
usvg::tiny_skia_path::PathVerb::Line => {
let Some(end) = points.next().map(to_vec) else { continue };
groups.push(ManipulatorGroup::new(end, Some(end), Some(end)));
}
usvg::tiny_skia_path::PathVerb::Quad => {
let Some(handle) = points.next().map(to_vec) else { continue };
let Some(end) = points.next().map(to_vec) else { continue };
if let Some(last) = groups.last_mut() {
last.out_handle = Some(last.anchor + (2. / 3.) * (handle - last.anchor));
}
groups.push(ManipulatorGroup::new(end, Some(end + (2. / 3.) * (handle - end)), Some(end)));
}
usvg::tiny_skia_path::PathVerb::Cubic => {
let Some(first_handle) = points.next().map(to_vec) else { continue };
let Some(second_handle) = points.next().map(to_vec) else { continue };
let Some(end) = points.next().map(to_vec) else { continue };
if let Some(last) = groups.last_mut() {
last.out_handle = Some(first_handle);
}
groups.push(ManipulatorGroup::new(end, Some(second_handle), Some(end)));
}
usvg::tiny_skia_path::PathVerb::Close => {
subpaths.push(Subpath::new(std::mem::take(&mut groups), true));
}
}
}
subpaths.push(Subpath::new(groups, false));
subpaths
}
type Path = Vec<path_bool::PathSegment>;
fn boolean_union(a: Path, b: Path) -> Vec<Path> {

View file

@ -16,6 +16,7 @@ loading = ["serde_json"]
# Local dependencies
dyn-any = { workspace = true }
graphene-core = { workspace = true }
graphene-path-bool = { workspace = true }
graphene-application-io = { workspace = true }
# Workspace dependencies

View file

@ -247,7 +247,7 @@ tagged_value! {
GradientType(graphene_core::vector::style::GradientType),
ReferencePoint(graphene_core::transform::ReferencePoint),
CentroidType(graphene_core::vector::misc::CentroidType),
BooleanOperation(graphene_core::vector::misc::BooleanOperation),
BooleanOperation(graphene_path_bool::BooleanOperation),
}
impl TaggedValue {

View file

@ -28,18 +28,16 @@ dyn-any = { workspace = true }
graph-craft = { workspace = true }
wgpu-executor = { workspace = true }
graphene-core = { workspace = true }
graphene-path-bool = { workspace = true }
graphene-application-io = { workspace = true }
# Workspace dependencies
fastnoise-lite = { workspace = true }
log = { workspace = true }
bezier-rs = { workspace = true }
path-bool = { workspace = true }
glam = { workspace = true }
node-macro = { workspace = true }
reqwest = { workspace = true }
futures = { workspace = true }
usvg = { workspace = true }
rand_chacha = { workspace = true }
rand = { workspace = true }
bytemuck = { workspace = true }

View file

@ -6,9 +6,10 @@ pub mod http;
pub mod image_color_palette;
pub mod raster;
pub mod text;
pub mod vector;
#[cfg(feature = "wasm")]
pub mod wasm_application_io;
pub use graphene_application_io as application_io;
pub use graphene_core::vector;
pub use graphene_core::*;
pub use graphene_path_bool as path_bool;

View file

@ -14,6 +14,7 @@ graphene-std = { workspace = true }
graph-craft = { workspace = true }
wgpu-executor = { workspace = true }
graphene-core = { workspace = true }
graphene-path-bool = { workspace = true }
dyn-any = { workspace = true }
# Workspace dependencies

View file

@ -60,7 +60,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Vec<f64>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => BlendMode]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::transform::ReferencePoint]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::BooleanOperation]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_path_bool::BooleanOperation]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Option<Color>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::style::Fill]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::style::StrokeCap]),

View file

@ -7,23 +7,8 @@ license = "MIT OR Apache-2.0"
[features]
[dependencies]
# Local dependencies
dyn-any = { path = "../../libraries/dyn-any", features = [
"log-bad-types",
"rc",
"glam",
] }
# Workspace dependencies
graphene-std = { workspace = true, features = ["gpu"] }
graph-craft = { workspace = true }
interpreted-executor = { workspace = true }
log = { workspace = true }
futures = { workspace = true }
glam = { workspace = true }
base64 = { workspace = true }
# Optional workspace dependencies
serde = { workspace = true, optional = true }
tokio = { workspace = true, optional = true }
serde_json = { workspace = true, optional = true }