diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 7c83ae6c9e..101eecb72c 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -2144,7 +2144,7 @@ impl DocumentMessageHandler { network_interface.upstream_flow_back_from_nodes(vec![selected_id.to_node()], &[], FlowType::HorizontalFlow).find(|id| { network_interface .reference(id, &[]) - .is_some_and(|reference| reference == DefinitionIdentifier::Network("Boolean Operation".into())) + .is_some_and(|reference| reference == DefinitionIdentifier::ProtoNode(graphene_std::path_bool_nodes::boolean_operation::IDENTIFIER)) }) }); diff --git a/editor/src/messages/portfolio/document/graph_operation/utility_types.rs b/editor/src/messages/portfolio/document/graph_operation/utility_types.rs index 26903bd89e..101577d4c7 100644 --- a/editor/src/messages/portfolio/document/graph_operation/utility_types.rs +++ b/editor/src/messages/portfolio/document/graph_operation/utility_types.rs @@ -145,10 +145,12 @@ impl<'a> ModifyInputsContext<'a> { } pub fn insert_boolean_data(&mut self, operation: graphene_std::vector::misc::BooleanOperation, layer: LayerNodeIdentifier) { - let boolean = resolve_network_node_type("Boolean Operation").expect("Boolean node does not exist").node_template_input_override([ - Some(NodeInput::value(TaggedValue::Graphic(Default::default()), true)), - Some(NodeInput::value(TaggedValue::BooleanOperation(operation), false)), - ]); + let boolean = resolve_proto_node_type(graphene_std::path_bool_nodes::boolean_operation::IDENTIFIER) + .expect("Boolean node does not exist") + .node_template_input_override([ + Some(NodeInput::value(TaggedValue::Graphic(Default::default()), true)), + Some(NodeInput::value(TaggedValue::BooleanOperation(operation), false)), + ]); let boolean_id = NodeId::new(); self.network_interface.insert_node(boolean_id, boolean, &[]); diff --git a/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs b/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs index 793ba02910..6168c5bda5 100644 --- a/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs +++ b/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs @@ -6,7 +6,7 @@ use super::utility_types::FrontendNodeType; use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::portfolio::document::utility_types::network_interface::{ DocumentNodeMetadata, DocumentNodePersistentMetadata, InputMetadata, NodeNetworkInterface, NodeNetworkMetadata, NodeNetworkPersistentMetadata, NodeTemplate, NodeTypePersistentMetadata, - NumberInputSettings, Vec2InputSettings, WidgetOverride, + Vec2InputSettings, WidgetOverride, }; use crate::messages::portfolio::utility_types::CachedData; use crate::messages::prelude::Message; @@ -1870,169 +1870,6 @@ fn document_node_definitions() -> HashMap), 0), NodeInput::import(concrete!(vector::style::Fill), 1)], - implementation: DocumentNodeImplementation::ProtoNode(path_bool_nodes::boolean_operation::IDENTIFIER), - call_argument: generic!(T), - ..Default::default() - }, - DocumentNode { - inputs: vec![NodeInput::node(NodeId(0), 0)], - implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER), - call_argument: generic!(T), - ..Default::default() - }, - ] - .into_iter() - .enumerate() - .map(|(id, node)| (NodeId(id as u64), node)) - .collect(), - ..Default::default() - }), - inputs: vec![ - NodeInput::value(TaggedValue::Graphic(Default::default()), 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 { - node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)), - ..Default::default() - }, - ..Default::default() - }, - DocumentNodeMetadata { - persistent_metadata: DocumentNodePersistentMetadata { - node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)), - ..Default::default() - }, - ..Default::default() - }, - ] - .into_iter() - .enumerate() - .map(|(id, node)| (NodeId(id as u64), node)) - .collect(), - ..Default::default() - }, - ..Default::default() - }), - input_metadata: vec![("Content", "TODO").into(), ("Operation", "TODO").into()], - output_names: vec!["Vector".to_string()], - ..Default::default() - }, - }, - description: Cow::Borrowed("TODO"), - properties: None, - }, - DocumentNodeDefinition { - identifier: "Scatter Points", - category: "Vector: Modifier", - node_template: NodeTemplate { - document_node: DocumentNode { - implementation: DocumentNodeImplementation::Network(NodeNetwork { - exports: vec![NodeInput::node(NodeId(1), 0)], - nodes: [ - DocumentNode { - inputs: vec![ - NodeInput::import(concrete!(Table), 0), - NodeInput::import(concrete!(f64), 1), - NodeInput::import(concrete!(u32), 2), - ], - call_argument: generic!(T), - implementation: DocumentNodeImplementation::ProtoNode(vector::poisson_disk_points::IDENTIFIER), - ..Default::default() - }, - DocumentNode { - inputs: vec![NodeInput::node(NodeId(0), 0)], - implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER), - call_argument: generic!(T), - ..Default::default() - }, - ] - .into_iter() - .enumerate() - .map(|(id, node)| (NodeId(id as u64), node)) - .collect(), - ..Default::default() - }), - inputs: vec![ - NodeInput::value(TaggedValue::Vector(Default::default()), true), - NodeInput::value(TaggedValue::F64(10.), false), - NodeInput::value(TaggedValue::U32(0), false), - ], - ..Default::default() - }, - persistent_node_metadata: DocumentNodePersistentMetadata { - network_metadata: Some(NodeNetworkMetadata { - persistent_metadata: NodeNetworkPersistentMetadata { - node_metadata: [ - DocumentNodeMetadata { - persistent_metadata: DocumentNodePersistentMetadata { - node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(0, 0)), - ..Default::default() - }, - ..Default::default() - }, - DocumentNodeMetadata { - persistent_metadata: DocumentNodePersistentMetadata { - node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(7, 0)), - ..Default::default() - }, - ..Default::default() - }, - ] - .into_iter() - .enumerate() - .map(|(id, node)| (NodeId(id as u64), node)) - .collect(), - ..Default::default() - }, - ..Default::default() - }), - input_metadata: vec![ - ("Content", "TODO").into(), - InputMetadata::with_name_description_override( - "Separation", - "TODO", - WidgetOverride::Number(NumberInputSettings { - min: Some(0.01), - mode: NumberInputMode::Range, - range_min: Some(1.), - range_max: Some(100.), - ..Default::default() - }), - ), - InputMetadata::with_name_description_override( - "Seed", - "TODO", - WidgetOverride::Number(NumberInputSettings { - min: Some(0.), - is_integer: true, - ..Default::default() - }), - ), - ], - output_names: vec!["Vector".to_string()], - ..Default::default() - }, - }, - description: Cow::Borrowed("TODO"), - properties: None, - }, ]; document_node_derive::post_process_nodes(custom) diff --git a/editor/src/messages/portfolio/document_migration.rs b/editor/src/messages/portfolio/document_migration.rs index 5092f0e968..3173f60183 100644 --- a/editor/src/messages/portfolio/document_migration.rs +++ b/editor/src/messages/portfolio/document_migration.rs @@ -94,7 +94,6 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[ "graphene_core::transform::FreezeRealTimeNode", "graphene_core::transform_nodes::BoundlessFootprintNode", "graphene_core::transform_nodes::FreezeRealTimeNode", - // `subpath_segment_lengths` was inlined into the `sample_polyline` proto; old "Sample Polyline" subnetworks pass through unchanged. "graphene_core::vector::SubpathSegmentLengthsNode", "core_types::vector::SubpathSegmentLengthsNode", ], @@ -910,8 +909,8 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[ aliases: &["graphene_core::vector::PointsToPolylineNode"], }, NodeReplacement { - node: graphene_std::vector::poisson_disk_points::IDENTIFIER, - aliases: &["graphene_core::vector::PoissonDiskPointsNode"], + node: graphene_std::vector::scatter_points::IDENTIFIER, + aliases: &["graphene_core::vector::PoissonDiskPointsNode", "core_types::vector::PoissonDiskPointsNode"], }, NodeReplacement { node: graphene_std::vector::position_on_path::IDENTIFIER, @@ -2036,10 +2035,10 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], /// definition by its old reference name, swaps it to a still-supported implementation, and preserves the user's inputs. /// After this runs, the node's reference resolves cleanly so the rest of `migrate_node` proceeds normally. fn migrate_removed_catalog_definitions(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], document: &mut DocumentMessageHandler) -> Option<()> { - // Collapse the legacy "Sample Polyline" wrapper network into the standalone `sample_polyline` proto. - // The proto now computes per-bezpath segment lengths inline, so the wrapper's separate `subpath_segment_lengths` + // Collapse the legacy "Sample Polyline" wrapper network into the standalone `sample_polyline` proto node. + // The proto node now computes per-bezpath segment lengths inline, so the wrapper's separate `subpath_segment_lengths` // and `Memo` nodes are no longer needed. The 7 user-facing inputs are positionally identical between the - // old wrapper and the new proto. + // old wrapper and the new proto node. if let Some(DefinitionIdentifier::Network(name)) = document.network_interface.reference(node_id, network_path) && name == "Sample Polyline" && node.inputs.len() == 7 @@ -2052,6 +2051,38 @@ fn migrate_removed_catalog_definitions(node_id: &NodeId, node: &DocumentNode, ne } } + // Collapse the legacy "Scatter Points" wrapper network into the standalone `scatter_points` proto node. + // The wrapper's trailing `Memo` node is now produced automatically by the `memoize` attribute on the + // proto node, so the wrapper itself is redundant. The 3 user-facing inputs are positionally identical + // between the old wrapper and the new proto node. + if let Some(DefinitionIdentifier::Network(name)) = document.network_interface.reference(node_id, network_path) + && name == "Scatter Points" + && node.inputs.len() == 3 + { + let mut node_template = resolve_proto_node_type(graphene_std::vector::scatter_points::IDENTIFIER)?.default_node_template(); + document.network_interface.replace_implementation(node_id, network_path, &mut node_template); + let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?; + for (index, input) in old_inputs.iter().take(3).enumerate() { + document.network_interface.set_input(&InputConnector::node(*node_id, index), input.clone(), network_path); + } + } + + // Collapse the legacy "Boolean Operation" wrapper network into the standalone `boolean_operation` proto node. + // The wrapper's trailing `Memo` node is now produced automatically by the `memoize` attribute on the + // proto node, so the wrapper itself is redundant. The 2 user-facing inputs are positionally identical + // between the old wrapper and the new proto node. + if let Some(DefinitionIdentifier::Network(name)) = document.network_interface.reference(node_id, network_path) + && name == "Boolean Operation" + && node.inputs.len() == 2 + { + let mut node_template = resolve_proto_node_type(graphene_std::path_bool_nodes::boolean_operation::IDENTIFIER)?.default_node_template(); + document.network_interface.replace_implementation(node_id, network_path, &mut node_template); + let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?; + for (index, input) in old_inputs.iter().take(2).enumerate() { + document.network_interface.set_input(&InputConnector::node(*node_id, index), input.clone(), network_path); + } + } + Some(()) } diff --git a/node-graph/nodes/path-bool/src/lib.rs b/node-graph/nodes/path-bool/src/lib.rs index 45facca241..4076ed5586 100644 --- a/node-graph/nodes/path-bool/src/lib.rs +++ b/node-graph/nodes/path-bool/src/lib.rs @@ -18,7 +18,7 @@ pub use vector_types::vector::misc::BooleanOperation; // TODO: with multiple rows while still assuming a single row for the boolean operations. /// 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(""))] +#[node_macro::node(category("Vector: Modifier"), memoize)] async fn boolean_operation( _: impl Ctx, /// The table of vector paths to perform the boolean operation on. Nested tables are automatically flattened. diff --git a/node-graph/nodes/vector/src/vector_nodes.rs b/node-graph/nodes/vector/src/vector_nodes.rs index 4a7c1c3610..d6dc5bfa39 100644 --- a/node-graph/nodes/vector/src/vector_nodes.rs +++ b/node-graph/nodes/vector/src/vector_nodes.rs @@ -1780,14 +1780,15 @@ async fn tangent_on_path( if radians { angle } else { angle.to_degrees() } } -#[node_macro::node(category(""), path(core_types::vector))] -async fn poisson_disk_points( +#[node_macro::node(category("Vector: Modifier"), path(core_types::vector), memoize)] +async fn scatter_points( _: impl Ctx, content: Table, #[unit(" px")] #[default(10.)] #[hard_min(0.01)] - separation_disk_diameter: f64, + #[range((1., 100.))] + separation: f64, seed: SeedValue, ) -> Table { let mut rng = rand::rngs::StdRng::seed_from_u64(seed.into()); @@ -1813,7 +1814,7 @@ async fn poisson_disk_points( continue; } - for point in bezpath_algorithms::poisson_disk_points(i, &path_with_bounding_boxes, separation_disk_diameter, || rng.random::()) { + for point in bezpath_algorithms::poisson_disk_points(i, &path_with_bounding_boxes, separation, || rng.random::()) { result.point_domain.push(PointId::generate(), point); } } @@ -3120,7 +3121,7 @@ mod test { } #[tokio::test] async fn poisson() { - let poisson_points = super::poisson_disk_points( + let poisson_points = super::scatter_points( Footprint::default(), vector_node_from_bezpath(Ellipse::from_rect(Rect::new(-50., -50., 50., 50.)).to_path(DEFAULT_ACCURACY)), 10. * std::f64::consts::SQRT_2,