Add Layer and Artboard node definitions and underlying data structures (#1204)

* Add basic node defenitions

* Code review

* change widget code

* Artboard node changes

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
0HyperCube 2023-05-09 21:57:22 +01:00 committed by Keavon Chambers
parent 7e1b452757
commit 4e0c673a35
12 changed files with 295 additions and 36 deletions

View file

@ -34,6 +34,10 @@ pub enum FrontendGraphDataType {
Boolean,
#[serde(rename = "vec2")]
Vector,
#[serde(rename = "graphic")]
GraphicGroup,
#[serde(rename = "artboard")]
Artboard,
}
impl FrontendGraphDataType {
pub const fn with_tagged_value(value: &TaggedValue) -> Self {
@ -46,6 +50,8 @@ impl FrontendGraphDataType {
TaggedValue::ImageFrame(_) => Self::Raster,
TaggedValue::Color(_) => Self::Color,
TaggedValue::RcSubpath(_) | TaggedValue::Subpaths(_) | TaggedValue::VectorData(_) => Self::Subpath,
TaggedValue::GraphicGroup(_) => Self::GraphicGroup,
TaggedValue::Artboard(_) => Self::Artboard,
_ => Self::General,
}
}
@ -750,6 +756,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
warn!("No network");
return;
};
debug_assert!(network.is_acyclic(), "Not acyclic. Network: {network:#?}");
let outwards_links = network.collect_outwards_links();
let required_shift = |left: NodeId, right: NodeId, network: &NodeNetwork| {
if let (Some(left), Some(right)) = (network.nodes.get(&left), network.nodes.get(&right)) {

View file

@ -118,6 +118,34 @@ fn static_nodes() -> Vec<DocumentNodeType> {
outputs: vec![DocumentOutputType::new("Out", FrontendGraphDataType::General)],
properties: |_document_node, _node_id, _context| node_properties::string_properties("The Monitor node stores the value of its last evaluation"),
},
DocumentNodeType {
name: "Layer",
category: "General",
identifier: NodeImplementation::proto("graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>"),
inputs: vec![
DocumentInputType::value("Vector Data", TaggedValue::VectorData(graphene_core::vector::VectorData::empty()), true),
DocumentInputType::value("Name", TaggedValue::String(String::new()), false),
DocumentInputType::value("Blend Mode", TaggedValue::BlendMode(BlendMode::Normal), false),
DocumentInputType::value("Opacity", TaggedValue::F32(100.), false),
DocumentInputType::value("Visible", TaggedValue::Bool(true), false),
DocumentInputType::value("Locked", TaggedValue::Bool(false), false),
DocumentInputType::value("Collapsed", TaggedValue::Bool(false), false),
DocumentInputType::value("Stack", TaggedValue::GraphicGroup(GraphicGroup::EMPTY), true),
],
outputs: vec![DocumentOutputType::new("Out", FrontendGraphDataType::GraphicGroup)],
properties: node_properties::layer_properties,
},
DocumentNodeType {
name: "Artboard",
category: "General",
identifier: NodeImplementation::proto("graphene_core::ConstructArtboardNode<_>"),
inputs: vec![
DocumentInputType::value("Graphic Group", TaggedValue::GraphicGroup(GraphicGroup::EMPTY), true),
DocumentInputType::value("Bounds", TaggedValue::Optional2IVec2(None), false),
],
outputs: vec![DocumentOutputType::new("Out", FrontendGraphDataType::Artboard)],
properties: node_properties::artboard_properties,
},
DocumentNodeType {
name: "Downres",
category: "Ignore",

View file

@ -75,7 +75,6 @@ fn start_widgets(document_node: &DocumentNode, node_id: NodeId, index: usize, na
widgets
}
#[cfg(feature = "gpu")]
fn text_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::Text, blank_assist);
@ -203,7 +202,7 @@ fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
WidgetHolder::unrelated_separator(),
number_props
.value(Some(x))
.on_update(update_value(|x: &NumberInput| TaggedValue::F64(x.value.unwrap()), node_id, index))
.on_update(update_value(move |x: &NumberInput| TaggedValue::F64(x.value.unwrap()), node_id, index))
.widget_holder(),
])
} else if let NodeInput::Value {
@ -215,7 +214,19 @@ fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
WidgetHolder::unrelated_separator(),
number_props
.value(Some(x as f64))
.on_update(update_value(|x: &NumberInput| TaggedValue::U32(x.value.unwrap() as u32), node_id, index))
.on_update(update_value(move |x: &NumberInput| TaggedValue::U32((x.value.unwrap()) as u32), node_id, index))
.widget_holder(),
])
} else if let NodeInput::Value {
tagged_value: TaggedValue::F32(x),
exposed: false,
} = document_node.inputs[index]
{
widgets.extend_from_slice(&[
WidgetHolder::unrelated_separator(),
number_props
.value(Some(x as f64))
.on_update(update_value(move |x: &NumberInput| TaggedValue::F32((x.value.unwrap()) as f32), node_id, index))
.widget_holder(),
])
}
@ -788,14 +799,8 @@ pub fn quantize_properties(document_node: &DocumentNode, node_id: NodeId, _conte
pub fn exposure_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let exposure = number_widget(document_node, node_id, 1, "Exposure", NumberInput::default().min(-20.).max(20.), true);
let offset = number_widget(document_node, node_id, 2, "Offset", NumberInput::default().min(-0.5).max(0.5), true);
let gamma_correction = number_widget(
document_node,
node_id,
3,
"Gamma Correction",
NumberInput::default().min(0.01).max(9.99).mode_increment().increment_step(0.1),
true,
);
let gamma_input = NumberInput::default().min(0.01).max(9.99).mode_increment().increment_step(0.1);
let gamma_correction = number_widget(document_node, node_id, 3, "Gamma Correction", gamma_input, true);
vec![
LayoutGroup::Row { widgets: exposure },
@ -1434,7 +1439,8 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
};
let blur_radius = {
let widgets = number_widget(document_node, node_id, mask_blur_index, "Mask Blur", NumberInput::default().unit(" px").min(0.).max(25.).int(), true);
let number_props = NumberInput::default().unit(" px").min(0.).max(25.).int();
let widgets = number_widget(document_node, node_id, mask_blur_index, "Mask Blur", number_props, true);
LayoutGroup::Row { widgets }.with_tooltip("Blur radius for the mask. Useful for softening sharp edges to blend the masked area with the rest of the image.")
};
@ -1590,3 +1596,25 @@ pub fn fill_properties(document_node: &DocumentNode, node_id: NodeId, _context:
widgets
}
pub fn layer_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let name = text_widget(document_node, node_id, 1, "Name", 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 visible = bool_widget(document_node, node_id, 4, "Visible", true);
let locked = bool_widget(document_node, node_id, 5, "Locked", true);
let collapsed = bool_widget(document_node, node_id, 6, "Collapsed", true);
vec![
LayoutGroup::Row { widgets: name },
blend_mode,
LayoutGroup::Row { widgets: opacity },
LayoutGroup::Row { widgets: visible },
LayoutGroup::Row { widgets: locked },
LayoutGroup::Row { widgets: collapsed },
]
}
pub fn artboard_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let label = text_widget(document_node, node_id, 1, "Label", true);
vec![LayoutGroup::Row { widgets: label }]
}

View file

@ -286,7 +286,7 @@ impl NodeGraphExecutor {
self.last_output_type.insert(layer_path.clone(), Some(concrete!(VectorData)));
responses.add(Operation::SetLayerTransform { path: layer_path.clone(), transform });
responses.add(Operation::SetVectorData { path: layer_path, vector_data });
} else {
} else if core::any::TypeId::of::<ImageFrame<Color>>() == DynAny::type_id(boxed_node_graph_output.as_ref()) {
// Attempt to downcast to an image frame
let ImageFrame { image, transform } = dyn_any::downcast(boxed_node_graph_output).map(|image_frame| *image_frame)?;
self.last_output_type.insert(layer_path.clone(), Some(concrete!(ImageFrame<Color>)));
@ -316,6 +316,14 @@ impl NodeGraphExecutor {
}];
responses.add(FrontendMessage::UpdateImageData { document_id, image_data });
}
} else if core::any::TypeId::of::<graphene_core::Artboard>() == DynAny::type_id(boxed_node_graph_output.as_ref()) {
let artboard: graphene_core::Artboard = dyn_any::downcast(boxed_node_graph_output).map(|artboard| *artboard)?;
info!("{artboard:#?}");
return Err(format!("Artboard (see console)"));
} else if core::any::TypeId::of::<graphene_core::GraphicGroup>() == DynAny::type_id(boxed_node_graph_output.as_ref()) {
let graphic_group: graphene_core::GraphicGroup = dyn_any::downcast(boxed_node_graph_output).map(|graphic| *graphic)?;
info!("{graphic_group:#?}");
return Err(format!("Graphic group (see console)"));
}
Ok(())