Skip to content

Commit

Permalink
Separate the Merge node from the Boolean Operation node (GraphiteEdit…
Browse files Browse the repository at this point in the history
…or#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 <[email protected]>
  • Loading branch information
adamgerhant and Keavon authored Aug 16, 2024
1 parent fa981a0 commit 33739b9
Show file tree
Hide file tree
Showing 13 changed files with 59 additions and 272 deletions.
2 changes: 1 addition & 1 deletion demo-artwork/isometric-fountain.graphite

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion demo-artwork/painted-dreams.graphite

Large diffs are not rendered by default.

34 changes: 13 additions & 21 deletions editor/src/messages/portfolio/document/document_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::messages::tool::utility_types::ToolType;
use crate::node_graph_executor::NodeGraphExecutor;

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::ImageFrame;
use graphene_core::vector::style::ViewMode;
Expand Down Expand Up @@ -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 folder_id = NodeId(generate_uuid());
let new_group_node = super::node_graph::document_node_types::resolve_document_node_type("Boolean Operation")
.expect("Failed to create merge node")
.node_template_input_override([
Some(NodeInput::value(TaggedValue::VectorData(graphene_std::vector::VectorData::empty()), true)),
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,
let boolean_operation_layer = LayerNodeIdentifier::new_unchecked(folder_id);
responses.add(GraphOperationMessage::NewBooleanOperationLayer {
id: folder_id,
operation,
parent,
insert_index,
});

responses.add(NodeGraphMessage::SetDisplayNameImpl {
node_id: folder_id,
alias: "Boolean Operation".to_string(),
});
// 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 => {
let id = NodeId(generate_uuid());
Expand Down Expand Up @@ -651,10 +642,11 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
if self.graph_view_overlay_open {
responses.add(NodeGraphMessage::ShiftNodes {
node_ids: self.network_interface.selected_nodes(&[]).unwrap().selected_nodes().cloned().collect(),
displacement_x: delta_x.signum() as i32,
displacement_y: delta_y.signum() as i32,
displacement_x: if delta_x == 0.0 { 0 } else { delta_x.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),
});

return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ pub enum GraphOperationMessage {
parent: LayerNodeIdentifier,
insert_index: usize,
},
NewBooleanOperationLayer {
id: NodeId,
operation: graphene_std::vector::misc::BooleanOperation,
parent: LayerNodeIdentifier,
insert_index: usize,
},
NewCustomLayer {
id: NodeId,
nodes: Vec<(NodeId, NodeTemplate)>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
network_interface.move_layer_to_stack(layer, parent, insert_index, &[]);
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 } => {
let mut modify_inputs = ModifyInputsContext::new(network_interface, responses);
let layer = modify_inputs.create_layer(id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,18 @@ impl<'a> ModifyInputsContext<'a> {
self.network_interface.insert_node(new_id, artboard_node_template, &[]);
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) {
let shape = resolve_document_node_type("Shape")
.expect("Shape node does not exist")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3719,201 +3719,20 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
category: "Vector",
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 {
identifier: "Boolean Operation",
node_template: NodeTemplate {
document_node: DocumentNode {
inputs: vec![
NodeInput::value(TaggedValue::GraphicGroup(GraphicGroup::EMPTY), true),
NodeInput::value(TaggedValue::GraphicGroup(GraphicGroup::EMPTY), true),
NodeInput::value(TaggedValue::BooleanOperation(vector::misc::BooleanOperation::Union), false),
],
implementation: DocumentNodeImplementation::Network(NodeNetwork {
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()
}),
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_std::vector::BooleanOperationNode<_>")),
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
network_metadata: Some(NodeNetworkMetadata {
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()],
input_names: vec!["Group of Paths".to_string(), "Operation".to_string()],
output_names: vec!["Vector".to_string()],
node_type_metadata: NodeTypePersistentMetadata::layer(IVec2::new(0, 0)),
..Default::default()
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1043,7 +1043,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
responses.add(NodeGraphMessage::SendGraph);
}
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 } => {
responses.add(DocumentMessage::StartTransaction);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
pub fn boolean_operation_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let group_of_paths_index = 0;
let operation_index = 1;

vec![LayoutGroup::Row { widgets: lower_vector_data }, operation]
}
let mut widgets = start_widgets(document_node, node_id, group_of_paths_index, "Group of Paths", FrontendGraphDataType::Graphic, true);

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 operation = boolean_operation_radio_buttons(document_node, node_id, 2, "Operation", 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: vector_data }, operation]
vec![LayoutGroup::Row { widgets }, operation]
}

pub fn copy_to_points_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
log::error!("Could not get node {node_id} in set_visibility");
return;
Expand Down
6 changes: 1 addition & 5 deletions frontend/src/utility-functions/computational-geometry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@ export function booleanIntersect(path1: string, path2: string): string {
return booleanOperation(path1, path2, "intersect");
}

export function booleanDifference(path1: string, path2: string): string {
return booleanOperation(path1, path2, "exclude");
}

function booleanOperation(path1: string, path2: string, operation: "unite" | "subtract" | "intersect" | "exclude"): string {
function booleanOperation(path1: string, path2: string, operation: "unite" | "subtract" | "intersect"): string {
const paperPath1 = new paper.CompoundPath(path1);
const paperPath2 = new paper.CompoundPath(path2);
const result = paperPath1[operation](paperPath2);
Expand Down
Loading

0 comments on commit 33739b9

Please sign in to comment.