mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-01 03:28:29 +08:00
Separate the Merge node from the Boolean Operation node (#1933)
* Rework boolean operation node * Set Boolean Operation name for layer * Remove memoize * Update both demo artworks that use booleans * Delete dead code, rename input connectors * Remove more dead code --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
Keavon Chambers
parent
fa981a0897
commit
33739b9ad4
Generated
+1
-1
File diff suppressed because one or more lines are too long
Generated
+1
-1
File diff suppressed because one or more lines are too long
@@ -24,7 +24,7 @@ use crate::messages::tool::utility_types::ToolType;
|
|||||||
use crate::node_graph_executor::NodeGraphExecutor;
|
use crate::node_graph_executor::NodeGraphExecutor;
|
||||||
|
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graph_craft::document::{NodeId, NodeInput, NodeNetwork, OldNodeNetwork};
|
use graph_craft::document::{NodeId, NodeNetwork, OldNodeNetwork};
|
||||||
use graphene_core::raster::BlendMode;
|
use graphene_core::raster::BlendMode;
|
||||||
use graphene_core::raster::ImageFrame;
|
use graphene_core::raster::ImageFrame;
|
||||||
use graphene_core::vector::style::ViewMode;
|
use graphene_core::vector::style::ViewMode;
|
||||||
@@ -329,28 +329,19 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
let insert_index = DocumentMessageHandler::get_calculated_insert_index(self.metadata(), self.network_interface.selected_nodes(&[]).unwrap(), parent);
|
let insert_index = DocumentMessageHandler::get_calculated_insert_index(self.metadata(), self.network_interface.selected_nodes(&[]).unwrap(), parent);
|
||||||
|
|
||||||
let folder_id = NodeId(generate_uuid());
|
let folder_id = NodeId(generate_uuid());
|
||||||
let new_group_node = super::node_graph::document_node_types::resolve_document_node_type("Boolean Operation")
|
let boolean_operation_layer = LayerNodeIdentifier::new_unchecked(folder_id);
|
||||||
.expect("Failed to create merge node")
|
responses.add(GraphOperationMessage::NewBooleanOperationLayer {
|
||||||
.node_template_input_override([
|
id: folder_id,
|
||||||
Some(NodeInput::value(TaggedValue::VectorData(graphene_std::vector::VectorData::empty()), true)),
|
operation,
|
||||||
Some(NodeInput::value(TaggedValue::VectorData(graphene_std::vector::VectorData::empty()), true)),
|
|
||||||
Some(NodeInput::value(TaggedValue::BooleanOperation(operation), false)),
|
|
||||||
]);
|
|
||||||
responses.add(NodeGraphMessage::InsertNode {
|
|
||||||
node_id: folder_id,
|
|
||||||
node_template: new_group_node,
|
|
||||||
});
|
|
||||||
let new_group_folder = LayerNodeIdentifier::new_unchecked(folder_id);
|
|
||||||
|
|
||||||
// Move the boolean operation to the correct position
|
|
||||||
responses.add(NodeGraphMessage::MoveLayerToStack {
|
|
||||||
layer: new_group_folder,
|
|
||||||
parent,
|
parent,
|
||||||
insert_index,
|
insert_index,
|
||||||
});
|
});
|
||||||
|
responses.add(NodeGraphMessage::SetDisplayNameImpl {
|
||||||
|
node_id: folder_id,
|
||||||
|
alias: "Boolean Operation".to_string(),
|
||||||
|
});
|
||||||
// Move all shallowest selected layers as children
|
// Move all shallowest selected layers as children
|
||||||
responses.add(DocumentMessage::MoveSelectedLayersToGroup { parent: new_group_folder });
|
responses.add(DocumentMessage::MoveSelectedLayersToGroup { parent: boolean_operation_layer });
|
||||||
}
|
}
|
||||||
DocumentMessage::CreateEmptyFolder => {
|
DocumentMessage::CreateEmptyFolder => {
|
||||||
let id = NodeId(generate_uuid());
|
let id = NodeId(generate_uuid());
|
||||||
@@ -651,10 +642,11 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
if self.graph_view_overlay_open {
|
if self.graph_view_overlay_open {
|
||||||
responses.add(NodeGraphMessage::ShiftNodes {
|
responses.add(NodeGraphMessage::ShiftNodes {
|
||||||
node_ids: self.network_interface.selected_nodes(&[]).unwrap().selected_nodes().cloned().collect(),
|
node_ids: self.network_interface.selected_nodes(&[]).unwrap().selected_nodes().cloned().collect(),
|
||||||
displacement_x: delta_x.signum() as i32,
|
displacement_x: if delta_x == 0.0 { 0 } else { delta_x.signum() as i32 },
|
||||||
displacement_y: delta_y.signum() as i32,
|
displacement_y: if delta_y == 0.0 { 0 } else { delta_y.signum() as i32 },
|
||||||
move_upstream: ipp.keyboard.get(Key::Shift as usize),
|
move_upstream: ipp.keyboard.get(Key::Shift as usize),
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,6 +71,12 @@ pub enum GraphOperationMessage {
|
|||||||
parent: LayerNodeIdentifier,
|
parent: LayerNodeIdentifier,
|
||||||
insert_index: usize,
|
insert_index: usize,
|
||||||
},
|
},
|
||||||
|
NewBooleanOperationLayer {
|
||||||
|
id: NodeId,
|
||||||
|
operation: graphene_std::vector::misc::BooleanOperation,
|
||||||
|
parent: LayerNodeIdentifier,
|
||||||
|
insert_index: usize,
|
||||||
|
},
|
||||||
NewCustomLayer {
|
NewCustomLayer {
|
||||||
id: NodeId,
|
id: NodeId,
|
||||||
nodes: Vec<(NodeId, NodeTemplate)>,
|
nodes: Vec<(NodeId, NodeTemplate)>,
|
||||||
|
|||||||
+7
@@ -145,6 +145,13 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
|
|||||||
network_interface.move_layer_to_stack(layer, parent, insert_index, &[]);
|
network_interface.move_layer_to_stack(layer, parent, insert_index, &[]);
|
||||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||||
}
|
}
|
||||||
|
GraphOperationMessage::NewBooleanOperationLayer { id, operation, parent, insert_index } => {
|
||||||
|
let mut modify_inputs = ModifyInputsContext::new(network_interface, responses);
|
||||||
|
let layer = modify_inputs.create_layer(id);
|
||||||
|
modify_inputs.insert_boolean_data(operation, layer);
|
||||||
|
network_interface.move_layer_to_stack(layer, parent, insert_index, &[]);
|
||||||
|
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||||
|
}
|
||||||
GraphOperationMessage::NewCustomLayer { id, nodes, parent, insert_index } => {
|
GraphOperationMessage::NewCustomLayer { id, nodes, parent, insert_index } => {
|
||||||
let mut modify_inputs = ModifyInputsContext::new(network_interface, responses);
|
let mut modify_inputs = ModifyInputsContext::new(network_interface, responses);
|
||||||
let layer = modify_inputs.create_layer(id);
|
let layer = modify_inputs.create_layer(id);
|
||||||
|
|||||||
@@ -132,6 +132,18 @@ impl<'a> ModifyInputsContext<'a> {
|
|||||||
self.network_interface.insert_node(new_id, artboard_node_template, &[]);
|
self.network_interface.insert_node(new_id, artboard_node_template, &[]);
|
||||||
LayerNodeIdentifier::new(new_id, self.network_interface)
|
LayerNodeIdentifier::new(new_id, self.network_interface)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn insert_boolean_data(&mut self, operation: graphene_std::vector::misc::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::GraphicGroup::EMPTY), true)),
|
||||||
|
Some(NodeInput::value(TaggedValue::BooleanOperation(operation), false)),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let boolean_id = NodeId(generate_uuid());
|
||||||
|
self.network_interface.insert_node(boolean_id, boolean, &[]);
|
||||||
|
self.network_interface.move_node_to_chain_start(&boolean_id, layer, &[]);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn insert_vector_data(&mut self, subpaths: Vec<Subpath<PointId>>, layer: LayerNodeIdentifier) {
|
pub fn insert_vector_data(&mut self, subpaths: Vec<Subpath<PointId>>, layer: LayerNodeIdentifier) {
|
||||||
let shape = resolve_document_node_type("Shape")
|
let shape = resolve_document_node_type("Shape")
|
||||||
.expect("Shape node does not exist")
|
.expect("Shape node does not exist")
|
||||||
|
|||||||
@@ -3719,201 +3719,20 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
category: "Vector",
|
category: "Vector",
|
||||||
properties: node_properties::circular_repeat_properties,
|
properties: node_properties::circular_repeat_properties,
|
||||||
},
|
},
|
||||||
DocumentNodeDefinition {
|
|
||||||
identifier: "Binary Boolean Operation",
|
|
||||||
node_template: NodeTemplate {
|
|
||||||
document_node: DocumentNode {
|
|
||||||
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
|
||||||
exports: vec![NodeInput::node(NodeId(1), 0)],
|
|
||||||
nodes: [
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![
|
|
||||||
NodeInput::network(concrete!(graphene_core::vector::VectorData), 0),
|
|
||||||
NodeInput::network(concrete!(graphene_core::vector::VectorData), 1),
|
|
||||||
NodeInput::network(concrete!(vector::misc::BooleanOperation), 2),
|
|
||||||
],
|
|
||||||
implementation: DocumentNodeImplementation::proto("graphene_std::vector::BinaryBooleanOperationNode<_, _>"),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::node(NodeId(0), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::memo::ImpureMemoNode<_, _, _>")),
|
|
||||||
manual_composition: Some(concrete!(Footprint)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(id, node)| (NodeId(id as u64), node))
|
|
||||||
.collect(),
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
inputs: vec![
|
|
||||||
NodeInput::value(TaggedValue::VectorData(graphene_core::vector::VectorData::empty()), true),
|
|
||||||
NodeInput::value(TaggedValue::VectorData(graphene_core::vector::VectorData::empty()), true),
|
|
||||||
NodeInput::value(TaggedValue::BooleanOperation(vector::misc::BooleanOperation::Union), false),
|
|
||||||
],
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
persistent_node_metadata: DocumentNodePersistentMetadata {
|
|
||||||
network_metadata: Some(NodeNetworkMetadata {
|
|
||||||
persistent_metadata: NodeNetworkPersistentMetadata {
|
|
||||||
node_metadata: [
|
|
||||||
DocumentNodeMetadata {
|
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
|
||||||
display_name: "BinaryBooleanOperation".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-17, -3)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNodeMetadata {
|
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
|
||||||
display_name: "MemoizeImpure".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-10, -3)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(id, node)| (NodeId(id as u64), node))
|
|
||||||
.collect(),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
input_names: vec!["Upper Vector Data".to_string(), "Lower Vector Data".to_string(), "Operation".to_string()],
|
|
||||||
output_names: vec!["Vector".to_string()],
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
category: "Vector",
|
|
||||||
properties: node_properties::binary_boolean_operation_properties,
|
|
||||||
},
|
|
||||||
DocumentNodeDefinition {
|
DocumentNodeDefinition {
|
||||||
identifier: "Boolean Operation",
|
identifier: "Boolean Operation",
|
||||||
node_template: NodeTemplate {
|
node_template: NodeTemplate {
|
||||||
document_node: DocumentNode {
|
document_node: DocumentNode {
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
NodeInput::value(TaggedValue::GraphicGroup(GraphicGroup::EMPTY), true),
|
|
||||||
NodeInput::value(TaggedValue::GraphicGroup(GraphicGroup::EMPTY), true),
|
NodeInput::value(TaggedValue::GraphicGroup(GraphicGroup::EMPTY), true),
|
||||||
NodeInput::value(TaggedValue::BooleanOperation(vector::misc::BooleanOperation::Union), false),
|
NodeInput::value(TaggedValue::BooleanOperation(vector::misc::BooleanOperation::Union), false),
|
||||||
],
|
],
|
||||||
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::vector::BooleanOperationNode<_>")),
|
||||||
exports: vec![NodeInput::node(NodeId(5), 0)],
|
|
||||||
nodes: [
|
|
||||||
// Primary (bottom) input type coercion
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::network(generic!(T), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::proto("graphene_core::ToGraphicGroupNode"),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
// Secondary (left) input type coercion
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::network(generic!(T), 1), NodeInput::network(concrete!(vector::misc::BooleanOperation), 2)],
|
|
||||||
implementation: DocumentNodeImplementation::proto("graphene_std::vector::BooleanOperationNode<_>"),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::node(NodeId(1), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::proto("graphene_core::ToGraphicElementNode"),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::node(NodeId(2), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::memo::ImpureMemoNode<_, _, _>")),
|
|
||||||
manual_composition: Some(concrete!(Footprint)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
// The monitor node is used to display a thumbnail in the UI
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::node(NodeId(3), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::proto("graphene_core::memo::MonitorNode<_, _, _>"),
|
|
||||||
manual_composition: Some(generic!(T)),
|
|
||||||
skip_deduplication: true,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNode {
|
|
||||||
manual_composition: Some(concrete!(Footprint)),
|
|
||||||
inputs: vec![NodeInput::node(NodeId(0), 0), NodeInput::node(NodeId(4), 0)],
|
|
||||||
implementation: DocumentNodeImplementation::proto("graphene_core::ConstructLayerNode<_, _>"),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(id, node)| (NodeId(id as u64), node))
|
|
||||||
.collect(),
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
persistent_node_metadata: DocumentNodePersistentMetadata {
|
persistent_node_metadata: DocumentNodePersistentMetadata {
|
||||||
network_metadata: Some(NodeNetworkMetadata {
|
input_names: vec!["Group of Paths".to_string(), "Operation".to_string()],
|
||||||
persistent_metadata: NodeNetworkPersistentMetadata {
|
|
||||||
node_metadata: [
|
|
||||||
DocumentNodeMetadata {
|
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
|
||||||
display_name: "ToGraphicGroup".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-9, -3)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNodeMetadata {
|
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
|
||||||
display_name: "BooleanOperation".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-16, -1)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNodeMetadata {
|
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
|
||||||
display_name: "ToGraphicElement".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-9, -1)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNodeMetadata {
|
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
|
||||||
display_name: "MemoizeImpure".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-2, -1)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNodeMetadata {
|
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
|
||||||
display_name: "Monitor".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(5, -1)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
DocumentNodeMetadata {
|
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
|
||||||
display_name: "ConstructLayer".to_string(),
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(12, -3)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(id, node)| (NodeId(id as u64), node))
|
|
||||||
.collect(),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
input_names: vec!["Graphical Data".to_string(), "Vector Data".to_string(), "Operation".to_string()],
|
|
||||||
output_names: vec!["Vector".to_string()],
|
output_names: vec!["Vector".to_string()],
|
||||||
node_type_metadata: NodeTypePersistentMetadata::layer(IVec2::new(0, 0)),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1043,7 +1043,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||||||
responses.add(NodeGraphMessage::SendGraph);
|
responses.add(NodeGraphMessage::SendGraph);
|
||||||
}
|
}
|
||||||
NodeGraphMessage::SetDisplayNameImpl { node_id, alias } => {
|
NodeGraphMessage::SetDisplayNameImpl { node_id, alias } => {
|
||||||
network_interface.set_display_name(&node_id, selection_network_path, alias);
|
network_interface.set_display_name(&node_id, alias, selection_network_path);
|
||||||
}
|
}
|
||||||
NodeGraphMessage::TogglePreview { node_id } => {
|
NodeGraphMessage::TogglePreview { node_id } => {
|
||||||
responses.add(DocumentMessage::StartTransaction);
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
|
|||||||
@@ -2398,18 +2398,18 @@ pub fn circular_repeat_properties(document_node: &DocumentNode, node_id: NodeId,
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn binary_boolean_operation_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
|
||||||
let lower_vector_data = vector_widget(document_node, node_id, 1, "Lower Vector Data", true);
|
|
||||||
let operation = boolean_operation_radio_buttons(document_node, node_id, 2, "Operation", true);
|
|
||||||
|
|
||||||
vec![LayoutGroup::Row { widgets: lower_vector_data }, operation]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn boolean_operation_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn boolean_operation_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let vector_data = vector_widget(document_node, node_id, 1, "Vector Data", true);
|
let group_of_paths_index = 0;
|
||||||
let operation = boolean_operation_radio_buttons(document_node, node_id, 2, "Operation", true);
|
let operation_index = 1;
|
||||||
|
|
||||||
vec![LayoutGroup::Row { widgets: vector_data }, operation]
|
let mut widgets = start_widgets(document_node, node_id, group_of_paths_index, "Group of Paths", FrontendGraphDataType::Graphic, true);
|
||||||
|
|
||||||
|
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||||
|
widgets.push(TextLabel::new("The output of a layer stack, which contains all elements to operate on").widget_holder());
|
||||||
|
|
||||||
|
let operation = boolean_operation_radio_buttons(document_node, node_id, operation_index, "Operation", true);
|
||||||
|
|
||||||
|
vec![LayoutGroup::Row { widgets }, operation]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_to_points_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn copy_to_points_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
|
|||||||
@@ -3027,7 +3027,7 @@ impl NodeNetworkInterface {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
pub fn set_display_name(&mut self, node_id: &NodeId, network_path: &[NodeId], display_name: String) {
|
pub fn set_display_name(&mut self, node_id: &NodeId, display_name: String, network_path: &[NodeId]) {
|
||||||
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else {
|
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else {
|
||||||
log::error!("Could not get node {node_id} in set_visibility");
|
log::error!("Could not get node {node_id} in set_visibility");
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -16,11 +16,7 @@ export function booleanIntersect(path1: string, path2: string): string {
|
|||||||
return booleanOperation(path1, path2, "intersect");
|
return booleanOperation(path1, path2, "intersect");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function booleanDifference(path1: string, path2: string): string {
|
function booleanOperation(path1: string, path2: string, operation: "unite" | "subtract" | "intersect"): string {
|
||||||
return booleanOperation(path1, path2, "exclude");
|
|
||||||
}
|
|
||||||
|
|
||||||
function booleanOperation(path1: string, path2: string, operation: "unite" | "subtract" | "intersect" | "exclude"): string {
|
|
||||||
const paperPath1 = new paper.CompoundPath(path1);
|
const paperPath1 = new paper.CompoundPath(path1);
|
||||||
const paperPath2 = new paper.CompoundPath(path2);
|
const paperPath2 = new paper.CompoundPath(path2);
|
||||||
const result = paperPath1[operation](paperPath2);
|
const result = paperPath1[operation](paperPath2);
|
||||||
|
|||||||
@@ -2,58 +2,20 @@ use crate::Node;
|
|||||||
|
|
||||||
use bezier_rs::{ManipulatorGroup, Subpath};
|
use bezier_rs::{ManipulatorGroup, Subpath};
|
||||||
use graphene_core::transform::Transform;
|
use graphene_core::transform::Transform;
|
||||||
|
use graphene_core::vector::misc::BooleanOperation;
|
||||||
pub use graphene_core::vector::*;
|
pub use graphene_core::vector::*;
|
||||||
use graphene_core::Color;
|
use graphene_core::{Color, GraphicElement, GraphicGroup};
|
||||||
use graphene_core::{transform::Footprint, GraphicGroup};
|
|
||||||
use graphene_core::{vector::misc::BooleanOperation, GraphicElement};
|
|
||||||
|
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
pub struct BinaryBooleanOperationNode<LowerVectorData, BooleanOp> {
|
|
||||||
lower_vector_data: LowerVectorData,
|
|
||||||
boolean_operation: BooleanOp,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[node_macro::node_fn(BinaryBooleanOperationNode)]
|
|
||||||
async fn binary_boolean_operation_node(upper_vector_data: VectorData, lower_vector_data: impl Node<Footprint, Output = VectorData>, boolean_operation: BooleanOperation) -> VectorData {
|
|
||||||
let lower_vector_data = self.lower_vector_data.eval(Footprint::default()).await;
|
|
||||||
let transform_of_lower_into_space_of_upper = upper_vector_data.transform.inverse() * lower_vector_data.transform;
|
|
||||||
|
|
||||||
let upper_path_string = to_svg_string(&upper_vector_data, DAffine2::IDENTITY);
|
|
||||||
let lower_path_string = to_svg_string(&lower_vector_data, transform_of_lower_into_space_of_upper);
|
|
||||||
|
|
||||||
let mut use_lower_style = false;
|
|
||||||
|
|
||||||
#[allow(unused_unsafe)]
|
|
||||||
let result = unsafe {
|
|
||||||
match boolean_operation {
|
|
||||||
BooleanOperation::Union => boolean_union(upper_path_string, lower_path_string),
|
|
||||||
BooleanOperation::SubtractFront => {
|
|
||||||
use_lower_style = true;
|
|
||||||
boolean_subtract(lower_path_string, upper_path_string)
|
|
||||||
}
|
|
||||||
BooleanOperation::SubtractBack => boolean_subtract(upper_path_string, lower_path_string),
|
|
||||||
BooleanOperation::Intersect => boolean_intersect(upper_path_string, lower_path_string),
|
|
||||||
BooleanOperation::Difference => boolean_difference(upper_path_string, lower_path_string),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut result = from_svg_string(&result);
|
|
||||||
result.transform = upper_vector_data.transform;
|
|
||||||
result.style = if use_lower_style { lower_vector_data.style } else { upper_vector_data.style };
|
|
||||||
result.alpha_blending = if use_lower_style { lower_vector_data.alpha_blending } else { upper_vector_data.alpha_blending };
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct BooleanOperationNode<BooleanOp> {
|
pub struct BooleanOperationNode<BooleanOp> {
|
||||||
boolean_operation: BooleanOp,
|
operation: BooleanOp,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node_fn(BooleanOperationNode)]
|
#[node_macro::node_fn(BooleanOperationNode)]
|
||||||
fn boolean_operation_node(graphic_group: GraphicGroup, boolean_operation: BooleanOperation) -> VectorData {
|
fn boolean_operation_node(group_of_paths: GraphicGroup, operation: BooleanOperation) -> VectorData {
|
||||||
fn vector_from_image<T: Transform>(image_frame: T) -> VectorData {
|
fn vector_from_image<T: Transform>(image_frame: T) -> VectorData {
|
||||||
let corner1 = DVec2::ZERO;
|
let corner1 = DVec2::ZERO;
|
||||||
let corner2 = DVec2::new(1., 1.);
|
let corner2 = DVec2::new(1., 1.);
|
||||||
@@ -212,7 +174,7 @@ fn boolean_operation_node(graphic_group: GraphicGroup, boolean_operation: Boolea
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The first index is the bottom of the stack
|
// The first index is the bottom of the stack
|
||||||
boolean_operation_on_vector_data(&collect_vector_data(&graphic_group), boolean_operation)
|
boolean_operation_on_vector_data(&collect_vector_data(&group_of_paths), operation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_svg_string(vector: &VectorData, transform: DAffine2) -> String {
|
fn to_svg_string(vector: &VectorData, transform: DAffine2) -> String {
|
||||||
@@ -288,8 +250,6 @@ extern "C" {
|
|||||||
fn boolean_subtract(path1: String, path2: String) -> String;
|
fn boolean_subtract(path1: String, path2: String) -> String;
|
||||||
#[wasm_bindgen(js_name = booleanIntersect)]
|
#[wasm_bindgen(js_name = booleanIntersect)]
|
||||||
fn boolean_intersect(path1: String, path2: String) -> String;
|
fn boolean_intersect(path1: String, path2: String) -> String;
|
||||||
#[wasm_bindgen(js_name = booleanDifference)]
|
|
||||||
fn boolean_difference(path1: String, path2: String) -> String;
|
|
||||||
}
|
}
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
fn boolean_union(_path1: String, _path2: String) -> String {
|
fn boolean_union(_path1: String, _path2: String) -> String {
|
||||||
@@ -303,7 +263,3 @@ fn boolean_subtract(_path1: String, _path2: String) -> String {
|
|||||||
fn boolean_intersect(_path1: String, _path2: String) -> String {
|
fn boolean_intersect(_path1: String, _path2: String) -> String {
|
||||||
String::from("M0,0 L1,0 L1,1 L0,1 Z")
|
String::from("M0,0 L1,0 L1,1 L0,1 Z")
|
||||||
}
|
}
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
fn boolean_difference(_path1: String, _path2: String) -> String {
|
|
||||||
String::from("M0,0 L1,0 L1,1 L0,1 Z")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -686,7 +686,6 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||||||
register_node!(graphene_core::vector::BoundingBoxNode, input: VectorData, params: []),
|
register_node!(graphene_core::vector::BoundingBoxNode, input: VectorData, params: []),
|
||||||
register_node!(graphene_core::vector::SolidifyStrokeNode, input: VectorData, params: []),
|
register_node!(graphene_core::vector::SolidifyStrokeNode, input: VectorData, params: []),
|
||||||
register_node!(graphene_core::vector::CircularRepeatNode<_, _, _>, input: VectorData, params: [f64, f64, u32]),
|
register_node!(graphene_core::vector::CircularRepeatNode<_, _, _>, input: VectorData, params: [f64, f64, u32]),
|
||||||
async_node!(graphene_std::vector::BinaryBooleanOperationNode<_, _>, input: VectorData, output: VectorData, fn_params: [Footprint => VectorData, () => graphene_core::vector::misc::BooleanOperation]),
|
|
||||||
register_node!(graphene_std::vector::BooleanOperationNode<_>, input: GraphicGroup, fn_params: [() => graphene_core::vector::misc::BooleanOperation]),
|
register_node!(graphene_std::vector::BooleanOperationNode<_>, input: GraphicGroup, fn_params: [() => graphene_core::vector::misc::BooleanOperation]),
|
||||||
vec![(
|
vec![(
|
||||||
ProtoNodeIdentifier::new("graphene_core::transform::CullNode<_>"),
|
ProtoNodeIdentifier::new("graphene_core::transform::CullNode<_>"),
|
||||||
|
|||||||
Reference in New Issue
Block a user