Migrate 'Scatter Points' and 'Boolean Operation' from subgraphs to memoized proto nodes (#4066)
This commit is contained in:
parent
e0368435b9
commit
df8c2125d9
|
|
@ -2144,7 +2144,7 @@ impl DocumentMessageHandler {
|
||||||
network_interface.upstream_flow_back_from_nodes(vec![selected_id.to_node()], &[], FlowType::HorizontalFlow).find(|id| {
|
network_interface.upstream_flow_back_from_nodes(vec![selected_id.to_node()], &[], FlowType::HorizontalFlow).find(|id| {
|
||||||
network_interface
|
network_interface
|
||||||
.reference(id, &[])
|
.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))
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -145,10 +145,12 @@ impl<'a> ModifyInputsContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
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::vector::misc::BooleanOperation, layer: LayerNodeIdentifier) {
|
||||||
let boolean = resolve_network_node_type("Boolean Operation").expect("Boolean node does not exist").node_template_input_override([
|
let boolean = resolve_proto_node_type(graphene_std::path_bool_nodes::boolean_operation::IDENTIFIER)
|
||||||
Some(NodeInput::value(TaggedValue::Graphic(Default::default()), true)),
|
.expect("Boolean node does not exist")
|
||||||
Some(NodeInput::value(TaggedValue::BooleanOperation(operation), false)),
|
.node_template_input_override([
|
||||||
]);
|
Some(NodeInput::value(TaggedValue::Graphic(Default::default()), true)),
|
||||||
|
Some(NodeInput::value(TaggedValue::BooleanOperation(operation), false)),
|
||||||
|
]);
|
||||||
|
|
||||||
let boolean_id = NodeId::new();
|
let boolean_id = NodeId::new();
|
||||||
self.network_interface.insert_node(boolean_id, boolean, &[]);
|
self.network_interface.insert_node(boolean_id, boolean, &[]);
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use super::utility_types::FrontendNodeType;
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::utility_types::network_interface::{
|
use crate::messages::portfolio::document::utility_types::network_interface::{
|
||||||
DocumentNodeMetadata, DocumentNodePersistentMetadata, InputMetadata, NodeNetworkInterface, NodeNetworkMetadata, NodeNetworkPersistentMetadata, NodeTemplate, NodeTypePersistentMetadata,
|
DocumentNodeMetadata, DocumentNodePersistentMetadata, InputMetadata, NodeNetworkInterface, NodeNetworkMetadata, NodeNetworkPersistentMetadata, NodeTemplate, NodeTypePersistentMetadata,
|
||||||
NumberInputSettings, Vec2InputSettings, WidgetOverride,
|
Vec2InputSettings, WidgetOverride,
|
||||||
};
|
};
|
||||||
use crate::messages::portfolio::utility_types::CachedData;
|
use crate::messages::portfolio::utility_types::CachedData;
|
||||||
use crate::messages::prelude::Message;
|
use crate::messages::prelude::Message;
|
||||||
|
|
@ -1870,169 +1870,6 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
|
||||||
description: Cow::Borrowed("TODO"),
|
description: Cow::Borrowed("TODO"),
|
||||||
properties: None,
|
properties: None,
|
||||||
},
|
},
|
||||||
DocumentNodeDefinition {
|
|
||||||
identifier: "Boolean Operation",
|
|
||||||
category: "Vector: Modifier",
|
|
||||||
node_template: NodeTemplate {
|
|
||||||
document_node: DocumentNode {
|
|
||||||
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
|
||||||
exports: vec![NodeInput::node(NodeId(1), 0)],
|
|
||||||
nodes: vec![
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::import(concrete!(Table<Vector>), 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<Vector>), 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)
|
document_node_derive::post_process_nodes(custom)
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,6 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
|
||||||
"graphene_core::transform::FreezeRealTimeNode",
|
"graphene_core::transform::FreezeRealTimeNode",
|
||||||
"graphene_core::transform_nodes::BoundlessFootprintNode",
|
"graphene_core::transform_nodes::BoundlessFootprintNode",
|
||||||
"graphene_core::transform_nodes::FreezeRealTimeNode",
|
"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",
|
"graphene_core::vector::SubpathSegmentLengthsNode",
|
||||||
"core_types::vector::SubpathSegmentLengthsNode",
|
"core_types::vector::SubpathSegmentLengthsNode",
|
||||||
],
|
],
|
||||||
|
|
@ -910,8 +909,8 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
|
||||||
aliases: &["graphene_core::vector::PointsToPolylineNode"],
|
aliases: &["graphene_core::vector::PointsToPolylineNode"],
|
||||||
},
|
},
|
||||||
NodeReplacement {
|
NodeReplacement {
|
||||||
node: graphene_std::vector::poisson_disk_points::IDENTIFIER,
|
node: graphene_std::vector::scatter_points::IDENTIFIER,
|
||||||
aliases: &["graphene_core::vector::PoissonDiskPointsNode"],
|
aliases: &["graphene_core::vector::PoissonDiskPointsNode", "core_types::vector::PoissonDiskPointsNode"],
|
||||||
},
|
},
|
||||||
NodeReplacement {
|
NodeReplacement {
|
||||||
node: graphene_std::vector::position_on_path::IDENTIFIER,
|
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.
|
/// 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.
|
/// 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<()> {
|
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.
|
// Collapse the legacy "Sample Polyline" wrapper network into the standalone `sample_polyline` proto node.
|
||||||
// The proto now computes per-bezpath segment lengths inline, so the wrapper's separate `subpath_segment_lengths`
|
// 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
|
// 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)
|
if let Some(DefinitionIdentifier::Network(name)) = document.network_interface.reference(node_id, network_path)
|
||||||
&& name == "Sample Polyline"
|
&& name == "Sample Polyline"
|
||||||
&& node.inputs.len() == 7
|
&& 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(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
// 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.
|
/// 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<I: graphic_types::IntoGraphicTable + 'n + Send + Clone>(
|
async fn boolean_operation<I: graphic_types::IntoGraphicTable + 'n + Send + Clone>(
|
||||||
_: impl Ctx,
|
_: impl Ctx,
|
||||||
/// The table of vector paths to perform the boolean operation on. Nested tables are automatically flattened.
|
/// The table of vector paths to perform the boolean operation on. Nested tables are automatically flattened.
|
||||||
|
|
|
||||||
|
|
@ -1780,14 +1780,15 @@ async fn tangent_on_path(
|
||||||
if radians { angle } else { angle.to_degrees() }
|
if radians { angle } else { angle.to_degrees() }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category(""), path(core_types::vector))]
|
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector), memoize)]
|
||||||
async fn poisson_disk_points(
|
async fn scatter_points(
|
||||||
_: impl Ctx,
|
_: impl Ctx,
|
||||||
content: Table<Vector>,
|
content: Table<Vector>,
|
||||||
#[unit(" px")]
|
#[unit(" px")]
|
||||||
#[default(10.)]
|
#[default(10.)]
|
||||||
#[hard_min(0.01)]
|
#[hard_min(0.01)]
|
||||||
separation_disk_diameter: f64,
|
#[range((1., 100.))]
|
||||||
|
separation: f64,
|
||||||
seed: SeedValue,
|
seed: SeedValue,
|
||||||
) -> Table<Vector> {
|
) -> Table<Vector> {
|
||||||
let mut rng = rand::rngs::StdRng::seed_from_u64(seed.into());
|
let mut rng = rand::rngs::StdRng::seed_from_u64(seed.into());
|
||||||
|
|
@ -1813,7 +1814,7 @@ async fn poisson_disk_points(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for point in bezpath_algorithms::poisson_disk_points(i, &path_with_bounding_boxes, separation_disk_diameter, || rng.random::<f64>()) {
|
for point in bezpath_algorithms::poisson_disk_points(i, &path_with_bounding_boxes, separation, || rng.random::<f64>()) {
|
||||||
result.point_domain.push(PointId::generate(), point);
|
result.point_domain.push(PointId::generate(), point);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3120,7 +3121,7 @@ mod test {
|
||||||
}
|
}
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn poisson() {
|
async fn poisson() {
|
||||||
let poisson_points = super::poisson_disk_points(
|
let poisson_points = super::scatter_points(
|
||||||
Footprint::default(),
|
Footprint::default(),
|
||||||
vector_node_from_bezpath(Ellipse::from_rect(Rect::new(-50., -50., 50., 50.)).to_path(DEFAULT_ACCURACY)),
|
vector_node_from_bezpath(Ellipse::from_rect(Rect::new(-50., -50., 50., 50.)).to_path(DEFAULT_ACCURACY)),
|
||||||
10. * std::f64::consts::SQRT_2,
|
10. * std::f64::consts::SQRT_2,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue