Hide the Cull node by integrating it into all generator nodes (#1538)

* Hide the Cull node by integrating it into all generator nodes

* Remove internal Cull node from Mandelbrot
This commit is contained in:
Keavon Chambers 2023-12-30 12:28:06 -08:00 committed by GitHub
parent 0e49388312
commit 1cc23320a5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 445 additions and 65 deletions

View file

@ -1353,7 +1353,7 @@ impl DocumentMessageHandler {
.max(100.)
.range_min(Some(0.))
.range_max(Some(100.))
.mode(NumberInputMode::Range)
.mode_range()
.on_update(|number_input: &NumberInput| {
if let Some(value) = number_input.value {
DocumentMessage::SetOpacityForSelectedLayers { opacity: value / 100. }.into()

View file

@ -190,7 +190,6 @@ impl<'a> ModifyInputsContext<'a> {
let node_type = resolve_document_node_type("Shape").expect("Shape node does not exist");
node_type.to_document_node_default_inputs([Some(NodeInput::value(TaggedValue::Subpaths(subpaths), false))], Default::default())
};
let cull = resolve_document_node_type("Cull").expect("Cull node does not exist").default_document_node();
let transform = resolve_document_node_type("Transform").expect("Transform node does not exist").default_document_node();
let fill = resolve_document_node_type("Fill").expect("Fill node does not exist").default_document_node();
let stroke = resolve_document_node_type("Stroke").expect("Stroke node does not exist").default_document_node();
@ -201,10 +200,8 @@ impl<'a> ModifyInputsContext<'a> {
self.insert_node_before(fill_id, stroke_id, 0, fill, IVec2::new(-8, 0));
let transform_id = NodeId(generate_uuid());
self.insert_node_before(transform_id, fill_id, 0, transform, IVec2::new(-8, 0));
let cull_id = NodeId(generate_uuid());
self.insert_node_before(cull_id, transform_id, 0, cull, IVec2::new(-8, 0));
let shape_id = NodeId(generate_uuid());
self.insert_node_before(shape_id, cull_id, 0, shape, IVec2::new(-8, 0));
self.insert_node_before(shape_id, transform_id, 0, shape, IVec2::new(-8, 0));
self.responses.add(NodeGraphMessage::SendGraph { should_rerender: true });
}
@ -218,7 +215,6 @@ impl<'a> ModifyInputsContext<'a> {
],
Default::default(),
);
let cull = resolve_document_node_type("Cull").expect("Cull node does not exist").default_document_node();
let transform = resolve_document_node_type("Transform").expect("Transform node does not exist").default_document_node();
let fill = resolve_document_node_type("Fill").expect("Fill node does not exist").default_document_node();
let stroke = resolve_document_node_type("Stroke").expect("Stroke node does not exist").default_document_node();
@ -229,10 +225,8 @@ impl<'a> ModifyInputsContext<'a> {
self.insert_node_before(fill_id, stroke_id, 0, fill, IVec2::new(-8, 0));
let transform_id = NodeId(generate_uuid());
self.insert_node_before(transform_id, fill_id, 0, transform, IVec2::new(-8, 0));
let cull_id = NodeId(generate_uuid());
self.insert_node_before(cull_id, transform_id, 0, cull, IVec2::new(-8, 0));
let text_id = NodeId(generate_uuid());
self.insert_node_before(text_id, cull_id, 0, text, IVec2::new(-8, 0));
self.insert_node_before(text_id, transform_id, 0, text, IVec2::new(-8, 0));
self.responses.add(NodeGraphMessage::SendGraph { should_rerender: true });
}
@ -241,15 +235,14 @@ impl<'a> ModifyInputsContext<'a> {
let node_type = resolve_document_node_type("Image").expect("Image node does not exist");
node_type.to_document_node_default_inputs([Some(NodeInput::value(TaggedValue::ImageFrame(image_frame), false))], Default::default())
};
let sample = resolve_document_node_type("Sample").expect("Sample node does not exist").default_document_node();
let transform = resolve_document_node_type("Transform").expect("Transform node does not exist").default_document_node();
let transform_id = NodeId(generate_uuid());
self.insert_node_before(transform_id, layer, 0, transform, IVec2::new(-8, 0));
let sample_id = NodeId(generate_uuid());
self.insert_node_before(sample_id, transform_id, 0, sample, IVec2::new(-8, 0));
let image_id = NodeId(generate_uuid());
self.insert_node_before(image_id, sample_id, 0, image, IVec2::new(-8, 0));
self.insert_node_before(image_id, transform_id, 0, image, IVec2::new(-8, 0));
self.responses.add(NodeGraphMessage::SendGraph { should_rerender: true });
}

View file

@ -264,6 +264,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
manual_composition: Some(concrete!(Footprint)),
..Default::default()
},
// TODO: Does this need an internal Cull node to be added to its implementation?
DocumentNodeDefinition {
name: "Input Frame",
category: "Ignore",
@ -285,7 +286,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
category: "Structural",
implementation: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![NodeId(0), NodeId(0)],
outputs: vec![NodeOutput::new(NodeId(1), 0)],
outputs: vec![NodeOutput::new(NodeId(2), 0)],
nodes: [
DocumentNode {
name: "Load Resource".to_string(),
@ -299,6 +300,13 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_std::wasm_application_io::DecodeImageNode")),
..Default::default()
},
DocumentNode {
name: "Cull".to_string(),
inputs: vec![NodeInput::node(NodeId(1), 0)],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::transform::CullNode<_>")),
manual_composition: Some(concrete!(Footprint)),
..Default::default()
},
]
.into_iter()
.enumerate()
@ -562,7 +570,30 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNodeDefinition {
name: "Image Frame",
category: "General",
implementation: NodeImplementation::proto("graphene_std::raster::ImageFrameNode<_, _>"),
implementation: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![NodeId(0), NodeId(0)],
outputs: vec![NodeOutput::new(NodeId(1), 0)],
nodes: vec![
DocumentNode {
name: "Image Frame".to_string(),
inputs: vec![NodeInput::Network(concrete!(graphene_core::raster::Image<Color>)), NodeInput::Network(concrete!(DAffine2))],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::vector::generator_nodes::ImageFrameNode<_, _>")),
..Default::default()
},
DocumentNode {
name: "Cull".to_string(),
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::transform::CullNode<_>")),
manual_composition: Some(concrete!(Footprint)),
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (NodeId(id as u64), node))
.collect(),
..Default::default()
}),
inputs: vec![
DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType::value("Transform", TaggedValue::DAffine2(DAffine2::IDENTITY), true),
@ -574,7 +605,64 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNodeDefinition {
name: "Noise Pattern",
category: "General",
implementation: NodeImplementation::proto("graphene_std::raster::NoisePatternNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _>"),
implementation: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![
NodeId(0),
NodeId(0),
NodeId(0),
NodeId(0),
NodeId(0),
NodeId(0),
NodeId(0),
NodeId(0),
NodeId(0),
NodeId(0),
NodeId(0),
NodeId(0),
NodeId(0),
NodeId(0),
NodeId(0),
NodeId(0),
],
outputs: vec![NodeOutput::new(NodeId(1), 0)],
nodes: vec![
DocumentNode {
name: "Noise Pattern".to_string(),
inputs: vec![
NodeInput::Network(concrete!(())),
NodeInput::Network(concrete!(UVec2)),
NodeInput::Network(concrete!(u32)),
NodeInput::Network(concrete!(f32)),
NodeInput::Network(concrete!(graphene_core::raster::NoiseType)),
NodeInput::Network(concrete!(graphene_core::raster::FractalType)),
NodeInput::Network(concrete!(f32)),
NodeInput::Network(concrete!(graphene_core::raster::FractalType)),
NodeInput::Network(concrete!(u32)),
NodeInput::Network(concrete!(f32)),
NodeInput::Network(concrete!(f32)),
NodeInput::Network(concrete!(f32)),
NodeInput::Network(concrete!(f32)),
NodeInput::Network(concrete!(graphene_core::raster::CellularDistanceFunction)),
NodeInput::Network(concrete!(graphene_core::raster::CellularReturnType)),
NodeInput::Network(concrete!(f32)),
],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_std::raster::NoisePatternNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _>")),
..Default::default()
},
DocumentNode {
name: "Cull".to_string(),
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::transform::CullNode<_>")),
manual_composition: Some(concrete!(Footprint)),
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (NodeId(id as u64), node))
.collect(),
..Default::default()
}),
inputs: vec![
DocumentInputType::value("None", TaggedValue::None, false),
// All
@ -601,6 +689,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
properties: node_properties::noise_pattern_properties,
..Default::default()
},
// TODO: This needs to work with resolution-aware (raster with footprint, post-Cull node) data.
DocumentNodeDefinition {
name: "Mask",
category: "Image Adjustments",
@ -613,6 +702,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
properties: node_properties::mask_properties,
..Default::default()
},
// TODO: This needs to work with resolution-aware (raster with footprint, post-Cull node) data.
DocumentNodeDefinition {
name: "Insert Channel",
category: "Image Adjustments",
@ -626,6 +716,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
properties: node_properties::insert_channel_properties,
..Default::default()
},
// TODO: This needs to work with resolution-aware (raster with footprint, post-Cull node) data.
DocumentNodeDefinition {
name: "Combine Channels",
category: "Image Adjustments",
@ -643,6 +734,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}],
..Default::default()
},
// TODO: This needs to work with resolution-aware (raster with footprint, post-Cull node) data.
DocumentNodeDefinition {
name: "Blend",
category: "Image Adjustments",
@ -871,7 +963,35 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNodeDefinition {
name: "Brush",
category: "Brush",
implementation: NodeImplementation::proto("graphene_std::brush::BrushNode<_, _, _>"),
implementation: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![NodeId(0), NodeId(0), NodeId(0), NodeId(0)],
outputs: vec![NodeOutput::new(NodeId(1), 0)],
nodes: vec![
DocumentNode {
name: "Brush".to_string(),
inputs: vec![
NodeInput::Network(concrete!(graphene_core::raster::ImageFrame<Color>)),
NodeInput::Network(concrete!(graphene_core::raster::ImageFrame<Color>)),
NodeInput::Network(concrete!(Vec<graphene_core::vector::brush_stroke::BrushStroke>)),
NodeInput::Network(concrete!(BrushCache)),
],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_std::brush::BrushNode<_, _, _>")),
..Default::default()
},
DocumentNode {
name: "Cull".to_string(),
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::transform::CullNode<_>")),
manual_composition: Some(concrete!(Footprint)),
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (NodeId(id as u64), node))
.collect(),
..Default::default()
}),
inputs: vec![
DocumentInputType::value("Background", TaggedValue::ImageFrame(ImageFrame::empty()), true),
DocumentInputType::value("Bounds", TaggedValue::ImageFrame(ImageFrame::empty()), true),
@ -907,7 +1027,30 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNodeDefinition {
name: "Image",
category: "Ignore",
implementation: NodeImplementation::proto("graphene_core::ops::IdentityNode"),
implementation: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![NodeId(0)],
outputs: vec![NodeOutput::new(NodeId(1), 0)],
nodes: vec![
DocumentNode {
name: "Identity".to_string(),
inputs: vec![NodeInput::Network(concrete!(ImageFrame<Color>))],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::ops::IdentityNode")),
..Default::default()
},
DocumentNode {
name: "Cull".to_string(),
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::transform::CullNode<_>")),
manual_composition: Some(concrete!(Footprint)),
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (NodeId(id as u64), node))
.collect(),
..Default::default()
}),
inputs: vec![DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), false)],
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
properties: |_document_node, _node_id, _context| node_properties::string_properties("A bitmap image embedded in this node"),
@ -2013,7 +2156,30 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNodeDefinition {
name: "Circle",
category: "Vector",
implementation: NodeImplementation::proto("graphene_core::vector::generator_nodes::CircleGenerator<_>"),
implementation: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![NodeId(0), NodeId(0)],
outputs: vec![NodeOutput::new(NodeId(1), 0)],
nodes: vec![
DocumentNode {
name: "Circle Generator".to_string(),
inputs: vec![NodeInput::Network(concrete!(())), NodeInput::Network(concrete!(f32))],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::vector::generator_nodes::CircleGenerator<_>")),
..Default::default()
},
DocumentNode {
name: "Cull".to_string(),
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::transform::CullNode<_>")),
manual_composition: Some(concrete!(Footprint)),
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (NodeId(id as u64), node))
.collect(),
..Default::default()
}),
inputs: vec![DocumentInputType::none(), DocumentInputType::value("Radius", TaggedValue::F32(50.), false)],
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
properties: node_properties::circle_properties,
@ -2022,7 +2188,30 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNodeDefinition {
name: "Ellipse",
category: "Vector",
implementation: NodeImplementation::proto("graphene_core::vector::generator_nodes::EllipseGenerator<_, _>"),
implementation: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![NodeId(0), NodeId(0), NodeId(0)],
outputs: vec![NodeOutput::new(NodeId(1), 0)],
nodes: vec![
DocumentNode {
name: "Ellipse Generator".to_string(),
inputs: vec![NodeInput::Network(concrete!(())), NodeInput::Network(concrete!(f32)), NodeInput::Network(concrete!(f32))],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::vector::generator_nodes::EllipseGenerator<_, _>")),
..Default::default()
},
DocumentNode {
name: "Cull".to_string(),
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::transform::CullNode<_>")),
manual_composition: Some(concrete!(Footprint)),
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (NodeId(id as u64), node))
.collect(),
..Default::default()
}),
inputs: vec![
DocumentInputType::none(),
DocumentInputType::value("Radius X", TaggedValue::F32(50.), false),
@ -2035,7 +2224,30 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNodeDefinition {
name: "Rectangle",
category: "Vector",
implementation: NodeImplementation::proto("graphene_core::vector::generator_nodes::RectangleGenerator<_, _>"),
implementation: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![NodeId(0), NodeId(0), NodeId(0)],
outputs: vec![NodeOutput::new(NodeId(1), 0)],
nodes: vec![
DocumentNode {
name: "Rectangle Generator".to_string(),
inputs: vec![NodeInput::Network(concrete!(())), NodeInput::Network(concrete!(f32)), NodeInput::Network(concrete!(f32))],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::vector::generator_nodes::RectangleGenerator<_, _>")),
..Default::default()
},
DocumentNode {
name: "Cull".to_string(),
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::transform::CullNode<_>")),
manual_composition: Some(concrete!(Footprint)),
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (NodeId(id as u64), node))
.collect(),
..Default::default()
}),
inputs: vec![
DocumentInputType::none(),
DocumentInputType::value("Size X", TaggedValue::F32(100.), false),
@ -2048,7 +2260,30 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNodeDefinition {
name: "Regular Polygon",
category: "Vector",
implementation: NodeImplementation::proto("graphene_core::vector::generator_nodes::RegularPolygonGenerator<_, _>"),
implementation: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![NodeId(0), NodeId(0), NodeId(0)],
outputs: vec![NodeOutput::new(NodeId(1), 0)],
nodes: vec![
DocumentNode {
name: "Regular Polygon Generator".to_string(),
inputs: vec![NodeInput::Network(concrete!(())), NodeInput::Network(concrete!(u32)), NodeInput::Network(concrete!(f32))],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::vector::generator_nodes::RegularPolygonGenerator<_, _>")),
..Default::default()
},
DocumentNode {
name: "Cull".to_string(),
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::transform::CullNode<_>")),
manual_composition: Some(concrete!(Footprint)),
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (NodeId(id as u64), node))
.collect(),
..Default::default()
}),
inputs: vec![
DocumentInputType::none(),
DocumentInputType::value("Sides", TaggedValue::U32(6), false),
@ -2061,7 +2296,35 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNodeDefinition {
name: "Star",
category: "Vector",
implementation: NodeImplementation::proto("graphene_core::vector::generator_nodes::StarGenerator<_, _, _>"),
implementation: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![NodeId(0), NodeId(0), NodeId(0), NodeId(0)],
outputs: vec![NodeOutput::new(NodeId(1), 0)],
nodes: vec![
DocumentNode {
name: "Star Generator".to_string(),
inputs: vec![
NodeInput::Network(concrete!(())),
NodeInput::Network(concrete!(u32)),
NodeInput::Network(concrete!(f32)),
NodeInput::Network(concrete!(f32)),
],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::vector::generator_nodes::StarGenerator<_, _, _>")),
..Default::default()
},
DocumentNode {
name: "Cull".to_string(),
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::transform::CullNode<_>")),
manual_composition: Some(concrete!(Footprint)),
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (NodeId(id as u64), node))
.collect(),
..Default::default()
}),
inputs: vec![
DocumentInputType::none(),
DocumentInputType::value("Sides", TaggedValue::U32(5), false),
@ -2075,7 +2338,30 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNodeDefinition {
name: "Line",
category: "Vector",
implementation: NodeImplementation::proto("graphene_core::vector::generator_nodes::LineGenerator<_, _>"),
implementation: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![NodeId(0), NodeId(0), NodeId(0)],
outputs: vec![NodeOutput::new(NodeId(1), 0)],
nodes: vec![
DocumentNode {
name: "Line Generator".to_string(),
inputs: vec![NodeInput::Network(concrete!(())), NodeInput::Network(concrete!(DVec2)), NodeInput::Network(concrete!(DVec2))],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::vector::generator_nodes::LineGenerator<_, _>")),
..Default::default()
},
DocumentNode {
name: "Cull".to_string(),
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::transform::CullNode<_>")),
manual_composition: Some(concrete!(Footprint)),
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (NodeId(id as u64), node))
.collect(),
..Default::default()
}),
inputs: vec![
DocumentInputType::none(),
DocumentInputType::value("Start", TaggedValue::DVec2(DVec2::new(0., -50.)), false),
@ -2088,7 +2374,30 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNodeDefinition {
name: "Spline",
category: "Vector",
implementation: NodeImplementation::proto("graphene_core::vector::generator_nodes::SplineGenerator<_>"),
implementation: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![NodeId(0), NodeId(0)],
outputs: vec![NodeOutput::new(NodeId(1), 0)],
nodes: vec![
DocumentNode {
name: "Spline Generator".to_string(),
inputs: vec![NodeInput::Network(concrete!(())), NodeInput::Network(concrete!(Vec<DVec2>))],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::vector::generator_nodes::SplineGenerator<_>")),
..Default::default()
},
DocumentNode {
name: "Cull".to_string(),
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::transform::CullNode<_>")),
manual_composition: Some(concrete!(Footprint)),
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (NodeId(id as u64), node))
.collect(),
..Default::default()
}),
inputs: vec![
DocumentInputType::none(),
DocumentInputType::value("Points", TaggedValue::VecDVec2(vec![DVec2::new(0., -50.), DVec2::new(25., 0.), DVec2::new(0., 50.)]), false),
@ -2100,10 +2409,35 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNodeDefinition {
name: "Shape",
category: "Vector",
implementation: NodeImplementation::proto("graphene_core::vector::generator_nodes::PathGenerator<_>"),
implementation: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![NodeId(0), NodeId(0)],
outputs: vec![NodeOutput::new(NodeId(1), 0)],
nodes: vec![
DocumentNode {
name: "Path Generator".to_string(),
inputs: vec![
NodeInput::Network(concrete!(Vec<bezier_rs::Subpath<graphene_core::uuid::ManipulatorGroupId>>)),
NodeInput::Network(concrete!(Vec<graphene_core::uuid::ManipulatorGroupId>)),
],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::vector::generator_nodes::PathGenerator<_>")),
..Default::default()
},
DocumentNode {
name: "Cull".to_string(),
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::transform::CullNode<_>")),
manual_composition: Some(concrete!(Footprint)),
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (NodeId(id as u64), node))
.collect(),
..Default::default()
}),
inputs: vec![
DocumentInputType::value("Path Data", TaggedValue::Subpaths(vec![]), false),
// TODO: Keavon asks: what is this for? Is it dead code? It seems to only be set, never read.
DocumentInputType::value("Mirror", TaggedValue::ManipulatorGroupIds(vec![]), false),
],
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
@ -2139,10 +2473,38 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNodeDefinition {
name: "Text",
category: "Vector",
implementation: NodeImplementation::proto("graphene_core::text::TextGeneratorNode<_, _, _>"),
implementation: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![NodeId(0), NodeId(0), NodeId(0), NodeId(0)],
outputs: vec![NodeOutput::new(NodeId(1), 0)],
nodes: vec![
DocumentNode {
name: "Text Generator".to_string(),
inputs: vec![
NodeInput::Network(concrete!(application_io::EditorApi<graphene_std::wasm_application_io::WasmApplicationIo>)),
NodeInput::Network(concrete!(String)),
NodeInput::Network(concrete!(graphene_core::text::Font)),
NodeInput::Network(concrete!(f64)),
],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::text::TextGeneratorNode<_, _, _>")),
..Default::default()
},
DocumentNode {
name: "Cull".to_string(),
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::Unresolved(ProtoNodeIdentifier::new("graphene_core::transform::CullNode<_>")),
manual_composition: Some(concrete!(Footprint)),
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (NodeId(id as u64), node))
.collect(),
..Default::default()
}),
inputs: vec![
DocumentInputType::none(),
DocumentInputType::value("Text", TaggedValue::String("hello world".to_string()), false),
DocumentInputType::value("Text", TaggedValue::String("Lorem ipsum".to_string()), false),
DocumentInputType::value("Font", TaggedValue::Font(Font::new(DEFAULT_FONT_FAMILY.into(), DEFAULT_FONT_STYLE.into())), false),
DocumentInputType::value("Size", TaggedValue::F64(24.), false),
],
@ -2312,6 +2674,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
properties: node_properties::node_no_properties,
..Default::default()
},
// TODO: This needs to work with resolution-aware (raster with footprint, post-Cull node) data.
DocumentNodeDefinition {
name: "Image Segmentation",
category: "Image Adjustments",

View file

@ -782,11 +782,11 @@ fn curves_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
}
pub fn levels_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let input_shadows = number_widget(document_node, node_id, 1, "Shadows", NumberInput::default().min(0.).max(100.).unit("%"), true);
let input_midtones = number_widget(document_node, node_id, 2, "Midtones", NumberInput::default().min(0.).max(100.).unit("%"), true);
let input_highlights = number_widget(document_node, node_id, 3, "Highlights", NumberInput::default().min(0.).max(100.).unit("%"), true);
let output_minimums = number_widget(document_node, node_id, 4, "Output Minimums", NumberInput::default().min(0.).max(100.).unit("%"), true);
let output_maximums = number_widget(document_node, node_id, 5, "Output Maximums", NumberInput::default().min(0.).max(100.).unit("%"), true);
let input_shadows = number_widget(document_node, node_id, 1, "Shadows", NumberInput::default().mode_range().min(0.).max(100.).unit("%"), true);
let input_midtones = number_widget(document_node, node_id, 2, "Midtones", NumberInput::default().mode_range().min(0.).max(100.).unit("%"), true);
let input_highlights = number_widget(document_node, node_id, 3, "Highlights", NumberInput::default().mode_range().min(0.).max(100.).unit("%"), true);
let output_minimums = number_widget(document_node, node_id, 4, "Output Minimums", NumberInput::default().mode_range().min(0.).max(100.).unit("%"), true);
let output_maximums = number_widget(document_node, node_id, 5, "Output Maximums", NumberInput::default().mode_range().min(0.).max(100.).unit("%"), true);
vec![
LayoutGroup::Row { widgets: input_shadows },
@ -802,12 +802,12 @@ pub fn black_and_white_properties(document_node: &DocumentNode, node_id: NodeId,
const MAX: f64 = 300.;
// TODO: Add tint color (blended above using the "Color" blend mode)
let tint = color_widget(document_node, node_id, 1, "Tint", ColorButton::default(), true);
let r_weight = number_widget(document_node, node_id, 2, "Reds", NumberInput::default().min(MIN).max(MAX).unit("%"), true);
let y_weight = number_widget(document_node, node_id, 3, "Yellows", NumberInput::default().min(MIN).max(MAX).unit("%"), true);
let g_weight = number_widget(document_node, node_id, 4, "Greens", NumberInput::default().min(MIN).max(MAX).unit("%"), true);
let c_weight = number_widget(document_node, node_id, 5, "Cyans", NumberInput::default().min(MIN).max(MAX).unit("%"), true);
let b_weight = number_widget(document_node, node_id, 6, "Blues", NumberInput::default().min(MIN).max(MAX).unit("%"), true);
let m_weight = number_widget(document_node, node_id, 7, "Magentas", NumberInput::default().min(MIN).max(MAX).unit("%"), true);
let r_weight = number_widget(document_node, node_id, 2, "Reds", NumberInput::default().mode_range().min(MIN).max(MAX).unit("%"), true);
let y_weight = number_widget(document_node, node_id, 3, "Yellows", NumberInput::default().mode_range().min(MIN).max(MAX).unit("%"), true);
let g_weight = number_widget(document_node, node_id, 4, "Greens", NumberInput::default().mode_range().min(MIN).max(MAX).unit("%"), true);
let c_weight = number_widget(document_node, node_id, 5, "Cyans", NumberInput::default().mode_range().min(MIN).max(MAX).unit("%"), true);
let b_weight = number_widget(document_node, node_id, 6, "Blues", NumberInput::default().mode_range().min(MIN).max(MAX).unit("%"), true);
let m_weight = number_widget(document_node, node_id, 7, "Magentas", NumberInput::default().mode_range().min(MIN).max(MAX).unit("%"), true);
vec![
tint,
@ -823,7 +823,7 @@ pub fn black_and_white_properties(document_node: &DocumentNode, node_id: NodeId,
pub fn blend_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let backdrop = color_widget(document_node, node_id, 1, "Backdrop", ColorButton::default(), true);
let blend_mode = blend_mode(document_node, node_id, 2, "Blend Mode", true);
let opacity = number_widget(document_node, node_id, 3, "Opacity", NumberInput::default().min(0.).max(100.).unit("%"), true);
let opacity = number_widget(document_node, node_id, 3, "Opacity", NumberInput::default().mode_range().min(0.).max(100.).unit("%"), true);
vec![backdrop, blend_mode, LayoutGroup::Row { widgets: opacity }]
}
@ -1043,8 +1043,8 @@ pub fn noise_pattern_properties(document_node: &DocumentNode, node_id: NodeId, _
pub fn adjust_hsl_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let hue_shift = number_widget(document_node, node_id, 1, "Hue Shift", NumberInput::default().min(-180.).max(180.).unit("°"), true);
let saturation_shift = number_widget(document_node, node_id, 2, "Saturation Shift", NumberInput::default().min(-100.).max(100.).unit("%"), true);
let lightness_shift = number_widget(document_node, node_id, 3, "Lightness Shift", NumberInput::default().min(-100.).max(100.).unit("%"), true);
let saturation_shift = number_widget(document_node, node_id, 2, "Saturation Shift", NumberInput::default().mode_range().min(-100.).max(100.).unit("%"), true);
let lightness_shift = number_widget(document_node, node_id, 3, "Lightness Shift", NumberInput::default().mode_range().min(-100.).max(100.).unit("%"), true);
vec![
LayoutGroup::Row { widgets: hue_shift },
@ -1079,15 +1079,15 @@ pub fn _blur_image_properties(document_node: &DocumentNode, node_id: NodeId, _co
}
pub fn adjust_threshold_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let thereshold_min = number_widget(document_node, node_id, 1, "Min Luminance", NumberInput::default().min(0.).max(100.).unit("%"), true);
let thereshold_max = number_widget(document_node, node_id, 2, "Max Luminance", NumberInput::default().min(0.).max(100.).unit("%"), true);
let thereshold_min = number_widget(document_node, node_id, 1, "Min Luminance", NumberInput::default().mode_range().min(0.).max(100.).unit("%"), true);
let thereshold_max = number_widget(document_node, node_id, 2, "Max Luminance", NumberInput::default().mode_range().min(0.).max(100.).unit("%"), true);
let luminance_calc = luminance_calculation(document_node, node_id, 3, "Luminance Calc", true);
vec![LayoutGroup::Row { widgets: thereshold_min }, LayoutGroup::Row { widgets: thereshold_max }, luminance_calc]
}
pub fn adjust_vibrance_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let vibrance = number_widget(document_node, node_id, 1, "Vibrance", NumberInput::default().min(-100.).max(100.).unit("%"), true);
let vibrance = number_widget(document_node, node_id, 1, "Vibrance", NumberInput::default().mode_range().min(-100.).max(100.).unit("%"), true);
vec![LayoutGroup::Row { widgets: vibrance }]
}
@ -1140,10 +1140,38 @@ pub fn adjust_channel_mixer_properties(document_node: &DocumentNode, node_id: No
(false, RedGreenBlue::Green) => ((10, "(Green) Red", 0.), (11, "(Green) Green", 100.), (12, "(Green) Blue", 0.), (13, "(Green) Constant", 0.)),
(false, RedGreenBlue::Blue) => ((14, "(Blue) Red", 0.), (15, "(Blue) Green", 0.), (16, "(Blue) Blue", 100.), (17, "(Blue) Constant", 0.)),
};
let red = number_widget(document_node, node_id, r.0, r.1, NumberInput::default().min(-200.).max(200.).value(Some(r.2)).unit("%"), true);
let green = number_widget(document_node, node_id, g.0, g.1, NumberInput::default().min(-200.).max(200.).value(Some(g.2)).unit("%"), true);
let blue = number_widget(document_node, node_id, b.0, b.1, NumberInput::default().min(-200.).max(200.).value(Some(b.2)).unit("%"), true);
let constant = number_widget(document_node, node_id, c.0, c.1, NumberInput::default().min(-200.).max(200.).value(Some(c.2)).unit("%"), true);
let red = number_widget(
document_node,
node_id,
r.0,
r.1,
NumberInput::default().mode_range().min(-200.).max(200.).value(Some(r.2)).unit("%"),
true,
);
let green = number_widget(
document_node,
node_id,
g.0,
g.1,
NumberInput::default().mode_range().min(-200.).max(200.).value(Some(g.2)).unit("%"),
true,
);
let blue = number_widget(
document_node,
node_id,
b.0,
b.1,
NumberInput::default().mode_range().min(-200.).max(200.).value(Some(b.2)).unit("%"),
true,
);
let constant = number_widget(
document_node,
node_id,
c.0,
c.1,
NumberInput::default().mode_range().min(-200.).max(200.).value(Some(c.2)).unit("%"),
true,
);
// Monochrome
let mut layout = vec![LayoutGroup::Row { widgets: monochrome }];
@ -1206,10 +1234,10 @@ pub fn adjust_selective_color_properties(document_node: &DocumentNode, node_id:
SelectiveColorChoice::Neutrals => ((30, "(Neutrals) Cyan"), (31, "(Neutrals) Magenta"), (32, "(Neutrals) Yellow"), (33, "(Neutrals) Black")),
SelectiveColorChoice::Blacks => ((34, "(Blacks) Cyan"), (35, "(Blacks) Magenta"), (36, "(Blacks) Yellow"), (37, "(Blacks) Black")),
};
let cyan = number_widget(document_node, node_id, c.0, c.1, NumberInput::default().min(-100.).max(100.).unit("%"), true);
let magenta = number_widget(document_node, node_id, m.0, m.1, NumberInput::default().min(-100.).max(100.).unit("%"), true);
let yellow = number_widget(document_node, node_id, y.0, y.1, NumberInput::default().min(-100.).max(100.).unit("%"), true);
let black = number_widget(document_node, node_id, k.0, k.1, NumberInput::default().min(-100.).max(100.).unit("%"), true);
let cyan = number_widget(document_node, node_id, c.0, c.1, NumberInput::default().mode_range().min(-100.).max(100.).unit("%"), true);
let magenta = number_widget(document_node, node_id, m.0, m.1, NumberInput::default().mode_range().min(-100.).max(100.).unit("%"), true);
let yellow = number_widget(document_node, node_id, y.0, y.1, NumberInput::default().mode_range().min(-100.).max(100.).unit("%"), true);
let black = number_widget(document_node, node_id, k.0, k.1, NumberInput::default().mode_range().min(-100.).max(100.).unit("%"), true);
// Mode
let mode_index = 1;

View file

@ -420,14 +420,11 @@ fn new_brush_layer(document: &DocumentMessageHandler, responses: &mut VecDeque<M
let brush_node = resolve_document_node_type("Brush")
.expect("Brush node does not exist")
.to_document_node_default_inputs([], DocumentNodeMetadata::position((-8, 0)));
let cull_node = resolve_document_node_type("Cull")
.expect("Cull node does not exist")
.to_document_node_default_inputs([Some(NodeInput::node(NodeId(1), 0))], DocumentNodeMetadata::default());
let id = NodeId(generate_uuid());
responses.add(GraphOperationMessage::NewCustomLayer {
id,
nodes: HashMap::from([(NodeId(1), brush_node), (NodeId(0), cull_node)]),
nodes: HashMap::from([(NodeId(0), brush_node)]),
parent: document.new_layer_parent(),
insert_index: -1,
});

View file

@ -173,7 +173,7 @@ register_node!(graphene_core::transform::SetTransformNode<_>, input: VectorData,
## Debugging
Debugging inside your node can be done with the `log` macros, for example `info!("The opacity is {opacity_multiplier}");`.
Debugging inside your node can be done with the `log::debug!()` macro, for example `log::debug!("The opacity is {opacity_multiplier}");`.
We need a utility to easily view a graph as the various steps are applied. We also need a way to transparently see which constructors are being run, which nodes are being evaluated, and in what order.

View file

@ -99,7 +99,6 @@ fn spline_generator(_input: (), positions: Vec<DVec2>) -> VectorData {
// TODO(TrueDoctor): I removed the Arc requirement we should think about when it makes sense to use it vs making a generic value node
#[derive(Debug, Clone)]
pub struct PathGenerator<Mirror> {
// TODO: Keavon asks: what is this for? Is it dead code? It seems to only be set, never read.
mirror: Mirror,
}

View file

@ -16,7 +16,8 @@ pub struct VectorData {
pub transform: DAffine2,
pub style: PathStyle,
pub alpha_blending: AlphaBlending,
// TODO: Keavon asks: what is this for? Is it dead code? It seems to only be set, never read.
/// A list of all manipulator groups (referenced in `subpaths`) that have smooth handles (where their handles are colinear, or locked to 180° angles from one another)
/// This gets read in `graph_operation_message_handler.rs` by calling `inputs.as_mut_slice()` (search for the string `"Shape does not have subpath and mirror angle inputs"` to find it).
pub mirror_angle: Vec<ManipulatorGroupId>,
}

View file

@ -998,7 +998,6 @@ impl NodeNetwork {
if let Some(new_output_node) = self.nodes.get_mut(&output.node_id) {
for source in node.original_location.outputs(i) {
info!("{:?} {}", source, output.node_output_index);
new_output_node.original_location.outputs_source.insert(source, output.node_output_index);
}
}

View file

@ -578,16 +578,16 @@ impl core::fmt::Debug for GraphErrorType {
x if x.ends_with('1') && !x.ends_with("11") => format!("{x}st"),
x if x.ends_with('2') && !x.ends_with("12") => format!("{x}nd"),
x if x.ends_with('3') && !x.ends_with("13") => format!("{x}rd"),
x => format!("{x}th parameter"),
x => format!("{x}th"),
};
let format_index = |index: usize| if index == 0 { "primary".to_string() } else { format!("{} parameter", ordinal(index - 1)) };
let format_index = |index: usize| if index == 0 { "primary".to_string() } else { format!("{} parameter", ordinal(index)) };
let format_error = |(index, (real, expected)): &(usize, (Type, Type))| format!("• The {} input expected {} but found {}", format_index(*index), expected, real);
let format_error_list = |errors: &Vec<(usize, (Type, Type))>| errors.iter().map(format_error).collect::<Vec<_>>().join("\n");
let errors = error_inputs.iter().map(format_error_list).collect::<Vec<_>>();
write!(
f,
"Node graph type error! If this just appeared while editing the graph,\n\
consider using undo to go back and trying another way to connect the nodes.\n\
consider using undo to go back and try another way to connect the nodes.\n\
\n\
No node implementation exists for type ({parameters}).\n\
\n\