Add the document graph (#1217)

* Thumbnails for the layer node

* Raster node graph frames

* Downscale to a random resolution

* Cleanup and bug fixes

* Generate paths before duplicating outputs

* Fix stable id test

* Add a document node graph

* Fix merge conflict

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
0HyperCube
2023-05-26 17:22:58 +01:00
committed by GitHub
co-authored by Keavon Chambers
parent d45e7cb755
commit 44d16c42f5
18 changed files with 264 additions and 152 deletions
+15
View File
@@ -26,6 +26,8 @@ pub struct Document {
/// This identifier is not a hash and is not guaranteed to be equal for equivalent documents. /// This identifier is not a hash and is not guaranteed to be equal for equivalent documents.
#[serde(skip)] #[serde(skip)]
pub state_identifier: DefaultHasher, pub state_identifier: DefaultHasher,
#[serde(default)]
pub document_network: graph_craft::document::NodeNetwork,
} }
impl PartialEq for Document { impl PartialEq for Document {
@@ -39,6 +41,19 @@ impl Default for Document {
Self { Self {
root: Layer::new(LayerDataType::Folder(FolderLayer::default()), DAffine2::IDENTITY.to_cols_array()), root: Layer::new(LayerDataType::Folder(FolderLayer::default()), DAffine2::IDENTITY.to_cols_array()),
state_identifier: DefaultHasher::new(), state_identifier: DefaultHasher::new(),
document_network: {
use graph_craft::document::{value::TaggedValue, NodeInput, NodeNetwork};
let mut network = NodeNetwork::default();
let node = graph_craft::document::DocumentNode {
name: "Output".into(),
inputs: vec![NodeInput::value(TaggedValue::GraphicGroup(Default::default()), true)],
implementation: graph_craft::document::DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into()),
metadata: graph_craft::document::DocumentNodeMetadata::position((8, 4)),
..Default::default()
};
network.push_node(node, false);
network
},
} }
} }
} }
+18
View File
@@ -253,6 +253,24 @@ impl Layer {
} }
} }
/// Gets a child layer of this layer, by a path. If the layer with id 1 is inside a folder with id 0, the path will be [0, 1].
pub fn child(&self, path: &[LayerId]) -> Option<&Layer> {
let mut layer = self;
for id in path {
layer = layer.as_folder().ok()?.layer(*id)?;
}
Some(layer)
}
/// Gets a child layer of this layer, by a path. If the layer with id 1 is inside a folder with id 0, the path will be [0, 1].
pub fn child_mut(&mut self, path: &[LayerId]) -> Option<&mut Layer> {
let mut layer = self;
for id in path {
layer = layer.as_folder_mut().ok()?.layer_mut(*id)?;
}
Some(layer)
}
/// Iterate over the layers encapsulated by this layer. /// Iterate over the layers encapsulated by this layer.
/// If the [Layer type](Layer::data) is not a folder, the only item in the iterator will be the layer itself. /// If the [Layer type](Layer::data) is not a folder, the only item in the iterator will be the layer itself.
/// If the [Layer type](Layer::data) wraps a [Folder](LayerDataType::Folder), the iterator will recursively yield all the layers contained in the folder as well as potential sub-folders. /// If the [Layer type](Layer::data) wraps a [Folder](LayerDataType::Folder), the iterator will recursively yield all the layers contained in the folder as well as potential sub-folders.
@@ -3,7 +3,7 @@ pub mod misc;
pub mod widgets; pub mod widgets;
pub mod widget_prelude { pub mod widget_prelude {
pub use super::layout_widget::{LayoutGroup, Widget, WidgetHolder, WidgetLayout}; pub use super::layout_widget::*;
pub use super::widgets::assist_widgets::*; pub use super::widgets::assist_widgets::*;
pub use super::widgets::button_widgets::*; pub use super::widgets::button_widgets::*;
pub use super::widgets::input_widgets::*; pub use super::widgets::input_widgets::*;
@@ -202,7 +202,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
} }
#[remain::unsorted] #[remain::unsorted]
NodeGraph(message) => { NodeGraph(message) => {
self.node_graph_handler.process_message(message, responses, (&mut self.document_legacy, executor)); self.node_graph_handler.process_message(message, responses, (&mut self.document_legacy, executor, document_id));
} }
#[remain::unsorted] #[remain::unsorted]
GraphOperation(message) => GraphOperationMessageHandler.process_message(message, responses, (&mut self.document_legacy, &mut self.node_graph_handler)), GraphOperation(message) => GraphOperationMessageHandler.process_message(message, responses, (&mut self.document_legacy, &mut self.node_graph_handler)),
@@ -502,7 +502,9 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
}); });
} }
InputFrameRasterizeRegionBelowLayer { layer_path } => { InputFrameRasterizeRegionBelowLayer { layer_path } => {
if let Some(message) = self.rasterize_region_below_layer(document_id, layer_path, preferences, persistent_data, None) { if layer_path.is_empty() {
responses.add(NodeGraphMessage::RunDocumentGraph);
} else if let Some(message) = self.rasterize_region_below_layer(document_id, layer_path, preferences, persistent_data, None) {
responses.add(message); responses.add(message);
} }
} }
@@ -70,7 +70,7 @@ impl<'a> ModifyInputsContext<'a> {
} else { } else {
self.modify_new_node(name, update_input); self.modify_new_node(name, update_input);
} }
self.node_graph.layer_path = Some(self.layer.to_vec()); self.node_graph.update_layer_path(Some(self.layer.to_vec()), self.responses);
self.node_graph.nested_path.clear(); self.node_graph.nested_path.clear();
self.responses.add(PropertiesPanelMessage::ResendActiveProperties); self.responses.add(PropertiesPanelMessage::ResendActiveProperties);
let layer_path = self.layer.to_vec(); let layer_path = self.layer.to_vec();
@@ -59,6 +59,7 @@ pub enum NodeGraphMessage {
PasteNodes { PasteNodes {
serialized_nodes: String, serialized_nodes: String,
}, },
RunDocumentGraph,
SelectNodes { SelectNodes {
nodes: Vec<NodeId>, nodes: Vec<NodeId>,
}, },
@@ -92,4 +93,5 @@ pub enum NodeGraphMessage {
TogglePreviewImpl { TogglePreviewImpl {
node_id: NodeId, node_id: NodeId,
}, },
UpdateNewNodeGraph,
} }
@@ -1,12 +1,10 @@
pub use self::document_node_types::*; pub use self::document_node_types::*;
use crate::messages::input_mapper::utility_types::macros::action_keys; use crate::messages::input_mapper::utility_types::macros::action_keys;
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, Widget, WidgetCallback, WidgetHolder, WidgetLayout}; use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::layout::utility_types::widgets::button_widgets::TextButton;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use crate::node_graph_executor::NodeGraphExecutor; use crate::node_graph_executor::NodeGraphExecutor;
use document_legacy::document::Document; use document_legacy::document::Document;
use document_legacy::layers::layer_layer::LayerLayer;
use document_legacy::LayerId; use document_legacy::LayerId;
use graph_craft::document::value::TaggedValue; use graph_craft::document::value::TaggedValue;
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput, NodeNetwork, NodeOutput}; use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput, NodeNetwork, NodeOutput};
@@ -33,6 +31,7 @@ pub enum FrontendGraphDataType {
Number, Number,
#[serde(rename = "number")] #[serde(rename = "number")]
Boolean, Boolean,
/// Refers to the mathematical vector, with direction and magnitude.
#[serde(rename = "vec2")] #[serde(rename = "vec2")]
Vector, Vector,
#[serde(rename = "graphic")] #[serde(rename = "graphic")]
@@ -46,7 +45,7 @@ impl FrontendGraphDataType {
TaggedValue::String(_) => Self::Text, TaggedValue::String(_) => Self::Text,
TaggedValue::F32(_) | TaggedValue::F64(_) | TaggedValue::U32(_) | TaggedValue::DAffine2(_) => Self::Number, TaggedValue::F32(_) | TaggedValue::F64(_) | TaggedValue::U32(_) | TaggedValue::DAffine2(_) => Self::Number,
TaggedValue::Bool(_) => Self::Boolean, TaggedValue::Bool(_) => Self::Boolean,
TaggedValue::DVec2(_) => Self::Vector, TaggedValue::DVec2(_) | TaggedValue::IVec2(_) => Self::Vector,
TaggedValue::Image(_) => Self::Raster, TaggedValue::Image(_) => Self::Raster,
TaggedValue::ImageFrame(_) => Self::Raster, TaggedValue::ImageFrame(_) => Self::Raster,
TaggedValue::Color(_) => Self::Color, TaggedValue::Color(_) => Self::Color,
@@ -118,33 +117,43 @@ impl FrontendNodeType {
#[derive(Debug, Clone, PartialEq, Default, serde::Serialize, serde::Deserialize)] #[derive(Debug, Clone, PartialEq, Default, serde::Serialize, serde::Deserialize)]
pub struct NodeGraphMessageHandler { pub struct NodeGraphMessageHandler {
pub layer_path: Option<Vec<document_legacy::LayerId>>, pub layer_path: Option<Vec<LayerId>>,
pub nested_path: Vec<graph_craft::document::NodeId>, pub nested_path: Vec<NodeId>,
pub selected_nodes: Vec<graph_craft::document::NodeId>, pub selected_nodes: Vec<NodeId>,
#[serde(skip)] #[serde(skip)]
pub widgets: [LayoutGroup; 2], pub widgets: [LayoutGroup; 2],
} }
impl NodeGraphMessageHandler { impl NodeGraphMessageHandler {
fn get_root_network<'a>(&self, document: &'a Document) -> Option<&'a graph_craft::document::NodeNetwork> { pub fn update_layer_path(&mut self, layer_path: Option<Vec<LayerId>>, responses: &mut VecDeque<Message>) {
self.layer_path.as_ref().and_then(|path| document.layer(path).ok()).and_then(|layer| layer.as_layer_network().ok()) self.layer_path = layer_path;
responses.add(NodeGraphMessage::UpdateNewNodeGraph);
} }
fn get_root_network_mut<'a>(&self, document: &'a mut Document) -> Option<&'a mut graph_craft::document::NodeNetwork> { fn get_root_network<'a>(&self, document: &'a Document) -> &'a graph_craft::document::NodeNetwork {
self.layer_path self.layer_path
.as_ref() .as_ref()
.and_then(|path| document.layer_mut(path).ok()) .and_then(|path| document.root.child(path))
.and_then(|layer| layer.as_layer_network().ok())
.unwrap_or(&document.document_network)
}
fn get_root_network_mut<'a>(&self, document: &'a mut Document) -> &'a mut graph_craft::document::NodeNetwork {
self.layer_path
.as_ref()
.and_then(|path| document.root.child_mut(path))
.and_then(|layer| layer.as_layer_network_mut().ok()) .and_then(|layer| layer.as_layer_network_mut().ok())
.unwrap_or(&mut document.document_network)
} }
/// Get the active graph_craft NodeNetwork struct /// Get the active graph_craft NodeNetwork struct
fn get_active_network<'a>(&self, document: &'a Document) -> Option<&'a graph_craft::document::NodeNetwork> { fn get_active_network<'a>(&self, document: &'a Document) -> Option<&'a graph_craft::document::NodeNetwork> {
self.get_root_network(document).and_then(|network| network.nested_network(&self.nested_path)) self.get_root_network(document).nested_network(&self.nested_path)
} }
/// Get the active graph_craft NodeNetwork struct /// Get the active graph_craft NodeNetwork struct
fn get_active_network_mut<'a>(&self, document: &'a mut Document) -> Option<&'a mut graph_craft::document::NodeNetwork> { fn get_active_network_mut<'a>(&self, document: &'a mut Document) -> Option<&'a mut graph_craft::document::NodeNetwork> {
self.get_root_network_mut(document).and_then(|network| network.nested_network_mut(&self.nested_path)) self.get_root_network_mut(document).nested_network_mut(&self.nested_path)
} }
/// Send the cached layout for the bar at the top of the node panel to the frontend /// Send the cached layout for the bar at the top of the node panel to the frontend
@@ -156,38 +165,41 @@ impl NodeGraphMessageHandler {
} }
/// Collect the addresses of the currently viewed nested node e.g. Root -> MyFunFilter -> Exposure /// Collect the addresses of the currently viewed nested node e.g. Root -> MyFunFilter -> Exposure
fn collect_nested_addresses(&mut self, _document: &Document, _responses: &mut VecDeque<Message>) { fn collect_nested_addresses(&mut self, document: &Document, responses: &mut VecDeque<Message>) {
// // Build path list // // Build path list
// let mut path = vec!["Root".to_string()]; let mut path = vec![];
// let mut network = self.get_root_network(document); if let Some(layer) = self.layer_path.as_ref().and_then(|path| document.layer(&path).ok()) {
// for node_id in &self.nested_path { path.push(format!("Layer: {}", layer.name.as_ref().map(|name| name.as_str()).unwrap_or("Untitled Layer")));
// let node = network.and_then(|network| network.nodes.get(node_id)); } else {
// if let Some(DocumentNode { name, .. }) = node { path.push("Document Network".to_string());
// path.push(name.clone()); }
// } let mut network = Some(self.get_root_network(document));
// network = node.and_then(|node| node.implementation.get_network()); for node_id in &self.nested_path {
// } let node = network.and_then(|network| network.nodes.get(node_id));
// let nesting = path.len(); if let Some(DocumentNode { name, .. }) = node {
path.push(name.clone());
}
network = node.and_then(|node| node.implementation.get_network());
}
let nesting = path.len();
// // Update UI // Update UI
// self.widgets[0] = LayoutGroup::Row { self.widgets[0] = LayoutGroup::Row {
// widgets: vec![WidgetHolder::new(Widget::BreadcrumbTrailButtons(BreadcrumbTrailButtons { widgets: vec![BreadcrumbTrailButtons::new(path.clone())
// labels: path.clone(), .on_update(move |input: &u64| {
// on_update: WidgetCallback::new(move |input: &u64| { NodeGraphMessage::ExitNestedNetwork {
// NodeGraphMessage::ExitNestedNetwork { depth_of_nesting: nesting - (*input as usize) - 1,
// depth_of_nesting: nesting - (*input as usize) - 1, }
// } .into()
// .into() })
// }), .widget_holder()],
// ..Default::default() };
// }))],
// };
// self.send_node_bar_layout(responses); self.send_node_bar_layout(responses);
} }
/// Updates the buttons for disable and preview /// Updates the buttons for disable and preview
fn update_selection_action_buttons(&mut self, document: &mut Document, responses: &mut VecDeque<Message>) { fn update_selection_action_buttons(&mut self, document: &Document, responses: &mut VecDeque<Message>) {
if let Some(network) = self.get_active_network(document) { if let Some(network) = self.get_active_network(document) {
let mut widgets = Vec::new(); let mut widgets = Vec::new();
@@ -235,8 +247,8 @@ impl NodeGraphMessageHandler {
} }
/// Collate the properties panel sections for a node graph /// Collate the properties panel sections for a node graph
pub fn collate_properties(&self, graph: &LayerLayer, context: &mut NodePropertiesContext, sections: &mut Vec<LayoutGroup>) { pub fn collate_properties(&self, context: &mut NodePropertiesContext, sections: &mut Vec<LayoutGroup>) {
let mut network = &graph.network; let mut network = context.network;
for segment in &self.nested_path { for segment in &self.nested_path {
network = network.nodes.get(segment).and_then(|node| node.implementation.get_network()).unwrap(); network = network.nodes.get(segment).and_then(|node| node.implementation.get_network()).unwrap();
} }
@@ -411,27 +423,15 @@ impl NodeGraphMessageHandler {
.filter_map(|(&id, &new)| network.nodes.get(&id).map(|node| (new, node.clone()))) .filter_map(|(&id, &new)| network.nodes.get(&id).map(|node| (new, node.clone())))
.map(move |(new, node)| (new, node.map_ids(Self::default_node_input, new_ids))) .map(move |(new, node)| (new, node.map_ids(Self::default_node_input, new_ids)))
} }
fn clear_graph(responses: &mut VecDeque<Message>) {
let nodes = Vec::new();
let links = Vec::new();
responses.add(FrontendMessage::UpdateNodeGraph { nodes, links });
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(Vec::new())),
layout_target: crate::messages::layout::utility_types::misc::LayoutTarget::NodeGraphBar,
});
}
} }
impl MessageHandler<NodeGraphMessage, (&mut Document, &NodeGraphExecutor)> for NodeGraphMessageHandler { impl MessageHandler<NodeGraphMessage, (&mut Document, &NodeGraphExecutor, u64)> for NodeGraphMessageHandler {
#[remain::check] #[remain::check]
fn process_message(&mut self, message: NodeGraphMessage, responses: &mut VecDeque<Message>, (document, executor): (&mut Document, &NodeGraphExecutor)) { fn process_message(&mut self, message: NodeGraphMessage, responses: &mut VecDeque<Message>, (document, executor, document_id): (&mut Document, &NodeGraphExecutor, u64)) {
#[remain::sorted] #[remain::sorted]
match message { match message {
NodeGraphMessage::CloseNodeGraph => { NodeGraphMessage::CloseNodeGraph => {
if let Some(_old_layer_path) = self.layer_path.take() { self.update_layer_path(None, responses);
Self::clear_graph(responses);
responses.add(PropertiesPanelMessage::ResendActiveProperties);
}
} }
NodeGraphMessage::ConnectNodesByLink { NodeGraphMessage::ConnectNodesByLink {
output_node, output_node,
@@ -703,6 +703,13 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &NodeGraphExecutor)> for N
responses.add(NodeGraphMessage::SendGraph { should_rerender: false }); responses.add(NodeGraphMessage::SendGraph { should_rerender: false });
} }
NodeGraphMessage::RunDocumentGraph => responses.add(PortfolioMessage::RenderGraphUsingRasterizedRegionBelowLayer {
document_id,
layer_path: Vec::new(),
input_image_data: vec![],
size: (0, 0),
imaginate_node_path: None,
}),
NodeGraphMessage::SelectNodes { nodes } => { NodeGraphMessage::SelectNodes { nodes } => {
self.selected_nodes = nodes; self.selected_nodes = nodes;
self.update_selection_action_buttons(document, responses); self.update_selection_action_buttons(document, responses);
@@ -715,6 +722,8 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &NodeGraphExecutor)> for N
if should_rerender { if should_rerender {
if let Some(layer_path) = self.layer_path.clone() { if let Some(layer_path) = self.layer_path.clone() {
responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path }); responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path });
} else {
responses.add(NodeGraphMessage::RunDocumentGraph);
} }
} }
} }
@@ -731,6 +740,8 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &NodeGraphExecutor)> for N
if (node.name != "Imaginate" || input_index == 0) && network.connected_to_output(node_id, true) { if (node.name != "Imaginate" || input_index == 0) && network.connected_to_output(node_id, true) {
if let Some(layer_path) = self.layer_path.clone() { if let Some(layer_path) = self.layer_path.clone() {
responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path }); responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path });
} else {
responses.add(NodeGraphMessage::RunDocumentGraph);
} }
} }
} }
@@ -754,11 +765,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &NodeGraphExecutor)> for N
return; return;
}; };
let network = document let network = self.get_root_network_mut(document).nested_network_mut(node_path);
.layer_mut(&layer_path)
.ok()
.and_then(|layer| layer.as_layer_network_mut().ok())
.and_then(|network| network.nested_network_mut(node_path));
if let Some(network) = network { if let Some(network) = network {
if let Some(node) = network.nodes.get_mut(node_id) { if let Some(node) = network.nodes.get_mut(node_id) {
@@ -871,11 +878,23 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &NodeGraphExecutor)> for N
responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path }); responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path });
} }
} }
NodeGraphMessage::UpdateNewNodeGraph => {
if let Some(network) = self.get_active_network(document) {
self.selected_nodes.clear();
Self::send_graph(network, executor, &self.layer_path, responses);
let node_types = document_node_types::collect_node_types();
responses.add(FrontendMessage::UpdateNodeTypes { node_types });
}
self.collect_nested_addresses(document, responses);
self.update_selected(document, responses);
}
} }
} }
fn actions(&self) -> ActionList { fn actions(&self) -> ActionList {
if self.layer_path.is_some() && !self.selected_nodes.is_empty() { if !self.selected_nodes.is_empty() {
actions!(NodeGraphMessageDiscriminant; DeleteSelectedNodes, Cut, Copy, DuplicateSelectedNodes, ToggleHidden) actions!(NodeGraphMessageDiscriminant; DeleteSelectedNodes, Cut, Copy, DuplicateSelectedNodes, ToggleHidden)
} else { } else {
actions!(NodeGraphMessageDiscriminant;) actions!(NodeGraphMessageDiscriminant;)
@@ -171,10 +171,12 @@ fn static_nodes() -> Vec<DocumentNodeType> {
DocumentNodeType { DocumentNodeType {
name: "Artboard", name: "Artboard",
category: "General", category: "General",
identifier: NodeImplementation::proto("graphene_core::ConstructArtboardNode<_>"), identifier: NodeImplementation::proto("graphene_core::ConstructArtboardNode<_, _, _>"),
inputs: vec![ inputs: vec![
DocumentInputType::value("Graphic Group", TaggedValue::GraphicGroup(GraphicGroup::EMPTY), true), DocumentInputType::value("Graphic Group", TaggedValue::GraphicGroup(GraphicGroup::EMPTY), true),
DocumentInputType::value("Bounds", TaggedValue::Optional2IVec2(None), false), DocumentInputType::value("Location", TaggedValue::IVec2(glam::IVec2::ZERO), false),
DocumentInputType::value("Dimensions", TaggedValue::IVec2(glam::IVec2::new(1920, 1080)), false),
DocumentInputType::value("Background", TaggedValue::Color(Color::WHITE), false),
], ],
outputs: vec![DocumentOutputType::new("Out", FrontendGraphDataType::Artboard)], outputs: vec![DocumentOutputType::new("Out", FrontendGraphDataType::Artboard)],
properties: node_properties::artboard_properties, properties: node_properties::artboard_properties,
@@ -2,7 +2,9 @@ use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use glam::DVec2; use document_legacy::layers::layer_info::LayerDataTypeDiscriminant;
use document_legacy::Operation;
use glam::{DVec2, IVec2};
use graph_craft::concrete; use graph_craft::concrete;
use graph_craft::document::value::TaggedValue; use graph_craft::document::value::TaggedValue;
use graph_craft::document::{DocumentNode, NodeId, NodeInput}; use graph_craft::document::{DocumentNode, NodeId, NodeInput};
@@ -127,6 +129,58 @@ fn bool_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
widgets widgets
} }
fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, x: &str, y: &str, mut assist: impl FnMut(&mut Vec<WidgetHolder>)) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::Vector, false);
assist(&mut widgets);
if let NodeInput::Value {
tagged_value: TaggedValue::DVec2(vec2),
exposed: false,
} = document_node.inputs[index]
{
widgets.extend_from_slice(&[
WidgetHolder::unrelated_separator(),
NumberInput::new(Some(vec2.x))
.label(x)
.unit(" px")
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(input.value.unwrap(), vec2.y)), node_id, index))
.widget_holder(),
WidgetHolder::related_separator(),
NumberInput::new(Some(vec2.y))
.label(y)
.unit(" px")
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(vec2.x, input.value.unwrap())), node_id, index))
.widget_holder(),
]);
} else if let NodeInput::Value {
tagged_value: TaggedValue::IVec2(vec2),
exposed: false,
} = document_node.inputs[index]
{
let update_x = move |input: &NumberInput| TaggedValue::IVec2(IVec2::new(input.value.unwrap() as i32, vec2.y));
let update_y = move |input: &NumberInput| TaggedValue::IVec2(IVec2::new(vec2.x, input.value.unwrap() as i32));
widgets.extend_from_slice(&[
WidgetHolder::unrelated_separator(),
NumberInput::new(Some(vec2.x as f64))
.int()
.label(x)
.unit(" px")
.on_update(update_value(update_x, node_id, index))
.widget_holder(),
WidgetHolder::related_separator(),
NumberInput::new(Some(vec2.y as f64))
.int()
.label(y)
.unit(" px")
.on_update(update_value(update_y, node_id, index))
.widget_holder(),
]);
}
LayoutGroup::Row { widgets }
}
fn vec_f32_input(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, text_props: TextInput, blank_assist: bool) -> Vec<WidgetHolder> { fn vec_f32_input(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, text_props: TextInput, blank_assist: bool) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::Color, blank_assist); let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::Color, blank_assist);
@@ -874,11 +928,7 @@ pub fn add_properties(document_node: &DocumentNode, node_id: NodeId, _context: &
} }
pub fn transform_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> { pub fn transform_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let translation = { let translation_assist = |widgets: &mut Vec<WidgetHolder>| {
let index = 1;
let mut widgets = start_widgets(document_node, node_id, index, "Translation", FrontendGraphDataType::Vector, false);
let pivot_index = 5; let pivot_index = 5;
if let NodeInput::Value { if let NodeInput::Value {
tagged_value: TaggedValue::DVec2(pivot), tagged_value: TaggedValue::DVec2(pivot),
@@ -892,32 +942,10 @@ pub fn transform_properties(document_node: &DocumentNode, node_id: NodeId, _cont
.widget_holder(), .widget_holder(),
); );
} else { } else {
add_blank_assist(&mut widgets); add_blank_assist(widgets);
} }
if let NodeInput::Value {
tagged_value: TaggedValue::DVec2(vec2),
exposed: false,
} = document_node.inputs[index]
{
widgets.extend_from_slice(&[
WidgetHolder::unrelated_separator(),
NumberInput::new(Some(vec2.x))
.label("X")
.unit(" px")
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(input.value.unwrap(), vec2.y)), node_id, index))
.widget_holder(),
WidgetHolder::related_separator(),
NumberInput::new(Some(vec2.y))
.label("Y")
.unit(" px")
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(vec2.x, input.value.unwrap())), node_id, index))
.widget_holder(),
]);
}
LayoutGroup::Row { widgets }
}; };
let translation = vec2_widget(document_node, node_id, 1, "Translation", "X", "Y", translation_assist);
let rotation = { let rotation = {
let index = 2; let index = 2;
@@ -944,32 +972,7 @@ pub fn transform_properties(document_node: &DocumentNode, node_id: NodeId, _cont
LayoutGroup::Row { widgets } LayoutGroup::Row { widgets }
}; };
let scale = { let scale = vec2_widget(document_node, node_id, 3, "Scale", "X", "Y", add_blank_assist);
let index = 3;
let mut widgets = start_widgets(document_node, node_id, index, "Scale", FrontendGraphDataType::Vector, true);
if let NodeInput::Value {
tagged_value: TaggedValue::DVec2(vec2),
exposed: false,
} = document_node.inputs[index]
{
widgets.extend_from_slice(&[
WidgetHolder::unrelated_separator(),
NumberInput::new(Some(vec2.x))
.label("X")
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(input.value.unwrap(), vec2.y)), node_id, index))
.widget_holder(),
WidgetHolder::related_separator(),
NumberInput::new(Some(vec2.y))
.label("Y")
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(vec2.x, input.value.unwrap())), node_id, index))
.widget_holder(),
]);
}
LayoutGroup::Row { widgets }
};
vec![translation, rotation, scale] vec![translation, rotation, scale]
} }
@@ -1655,6 +1658,8 @@ pub fn layer_properties(document_node: &DocumentNode, node_id: NodeId, _context:
] ]
} }
pub fn artboard_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> { 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); let location = vec2_widget(document_node, node_id, 1, "Location", "X", "Y", add_blank_assist);
vec![LayoutGroup::Row { widgets: label }] let dimensions = vec2_widget(document_node, node_id, 2, "Dimensions", "W", "H", add_blank_assist);
let background = color_widget(document_node, node_id, 3, "Background", ColorInput::default().allow_none(false), true);
vec![location, dimensions, background]
} }
@@ -1,4 +1,4 @@
use super::utility_functions::{register_artboard_layer_properties, register_artwork_layer_properties}; use super::utility_functions::{register_artboard_layer_properties, register_artwork_layer_properties, register_document_graph_properties};
use super::utility_types::PropertiesPanelMessageHandlerData; use super::utility_types::PropertiesPanelMessageHandlerData;
use crate::messages::layout::utility_types::layout_widget::{Layout, WidgetLayout}; use crate::messages::layout::utility_types::layout_widget::{Layout, WidgetLayout};
use crate::messages::layout::utility_types::misc::LayoutTarget; use crate::messages::layout::utility_types::misc::LayoutTarget;
@@ -141,6 +141,17 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
TargetDocument::Artboard => register_artboard_layer_properties(layer, responses, persistent_data), TargetDocument::Artboard => register_artboard_layer_properties(layer, responses, persistent_data),
TargetDocument::Artwork => register_artwork_layer_properties(document, path, layer, responses, persistent_data, node_graph_message_handler, executor), TargetDocument::Artwork => register_artwork_layer_properties(document, path, layer, responses, persistent_data, node_graph_message_handler, executor),
} }
} else {
let context = crate::messages::portfolio::document::node_graph::NodePropertiesContext {
persistent_data,
document: artwork_document,
responses,
nested_path: &node_graph_message_handler.nested_path,
layer_path: &[],
executor,
network: &artwork_document.document_network,
};
register_document_graph_properties(context, node_graph_message_handler);
} }
} }
UpdateSelectedDocumentProperties => responses.add(PropertiesPanelMessage::SetActiveLayers { UpdateSelectedDocumentProperties => responses.add(PropertiesPanelMessage::SetActiveLayers {
@@ -6,6 +6,7 @@ use crate::messages::layout::utility_types::widgets::assist_widgets::PivotAssist
use crate::messages::layout::utility_types::widgets::button_widgets::{IconButton, PopoverButton, TextButton}; use crate::messages::layout::utility_types::widgets::button_widgets::{IconButton, PopoverButton, TextButton};
use crate::messages::layout::utility_types::widgets::input_widgets::{CheckboxInput, ColorInput, NumberInput, NumberInputMode, RadioEntryData, RadioInput, TextInput}; use crate::messages::layout::utility_types::widgets::input_widgets::{CheckboxInput, ColorInput, NumberInput, NumberInputMode, RadioEntryData, RadioInput, TextInput};
use crate::messages::layout::utility_types::widgets::label_widgets::{IconLabel, TextLabel}; use crate::messages::layout::utility_types::widgets::label_widgets::{IconLabel, TextLabel};
use crate::messages::portfolio::document::node_graph::NodePropertiesContext;
use crate::messages::portfolio::utility_types::PersistentData; use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use crate::node_graph_executor::NodeGraphExecutor; use crate::node_graph_executor::NodeGraphExecutor;
@@ -301,7 +302,7 @@ pub fn register_artwork_layer_properties(
LayerDataType::Layer(layer) => { LayerDataType::Layer(layer) => {
let mut properties_sections = Vec::new(); let mut properties_sections = Vec::new();
let mut context = crate::messages::portfolio::document::node_graph::NodePropertiesContext { let mut context = NodePropertiesContext {
persistent_data, persistent_data,
document, document,
responses, responses,
@@ -310,7 +311,7 @@ pub fn register_artwork_layer_properties(
executor, executor,
network: &layer.network, network: &layer.network,
}; };
node_graph_message_handler.collate_properties(layer, &mut context, &mut properties_sections); node_graph_message_handler.collate_properties(&mut context, &mut properties_sections);
properties_sections properties_sections
} }
@@ -329,6 +330,31 @@ pub fn register_artwork_layer_properties(
}); });
} }
pub fn register_document_graph_properties(mut context: NodePropertiesContext, node_graph_message_handler: &NodeGraphMessageHandler) {
let mut properties_sections = Vec::new();
node_graph_message_handler.collate_properties(&mut context, &mut properties_sections);
let options_bar = vec![LayoutGroup::Row {
widgets: vec![
IconLabel::new("File").widget_holder(),
WidgetHolder::unrelated_separator(),
TextLabel::new("Document graph").widget_holder(),
WidgetHolder::unrelated_separator(),
TextInput::new("No layer selected").disabled(true).widget_holder(),
WidgetHolder::related_separator(),
PopoverButton::new("Options Bar", "Coming soon").widget_holder(),
],
}];
context.responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(options_bar)),
layout_target: LayoutTarget::PropertiesOptions,
});
context.responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(properties_sections)),
layout_target: LayoutTarget::PropertiesSections,
});
}
fn node_section_transform(layer: &Layer, persistent_data: &PersistentData) -> LayoutGroup { fn node_section_transform(layer: &Layer, persistent_data: &PersistentData) -> LayoutGroup {
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::default(), None); let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::default(), None);
let pivot = layer.transform.transform_vector2(layer.layerspace_pivot(&render_data)); let pivot = layer.transform.transform_vector2(layer.layerspace_pivot(&render_data));
@@ -514,7 +514,6 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
self.executor.insert_thumbnail_blob_url(blob_url, layer_id, node_id, responses); self.executor.insert_thumbnail_blob_url(blob_url, layer_id, node_id, responses);
return; return;
} }
let message = DocumentMessage::SetImageBlobUrl { let message = DocumentMessage::SetImageBlobUrl {
layer_path, layer_path,
blob_url, blob_url,
+11 -7
View File
@@ -367,18 +367,22 @@ impl NodeGraphExecutor {
// Get the node graph layer // Get the node graph layer
let document = documents.get_mut(&document_id).ok_or_else(|| "Invalid document".to_string())?; let document = documents.get_mut(&document_id).ok_or_else(|| "Invalid document".to_string())?;
let layer = document.document_legacy.layer(&layer_path).map_err(|e| format!("No layer: {e:?}"))?; let network = if layer_path.is_empty() {
document.document_legacy.document_network.clone()
} else {
let layer = document.document_legacy.layer(&layer_path).map_err(|e| format!("No layer: {e:?}"))?;
let layer_layer = match &layer.data {
LayerDataType::Layer(layer) => Ok(layer),
_ => Err("Invalid layer type".to_string()),
}?;
layer_layer.network.clone()
};
// Construct the input image frame // Construct the input image frame
let transform = DAffine2::IDENTITY; let transform = DAffine2::IDENTITY;
let image_frame = ImageFrame { image, transform }; let image_frame = ImageFrame { image, transform };
let layer_layer = match &layer.data {
LayerDataType::Layer(layer) => Ok(layer),
_ => Err("Invalid layer type".to_string()),
}?;
let network = layer_layer.network.clone();
// Special execution path for generating Imaginate (as generation requires IO from outside node graph) // Special execution path for generating Imaginate (as generation requires IO from outside node graph)
/*if let Some(imaginate_node) = imaginate_node { /*if let Some(imaginate_node) = imaginate_node {
responses.add(self.generate_imaginate(network, imaginate_node, (document, document_id), layer_path, editor_api, persistent_data)?); responses.add(self.generate_imaginate(network, imaginate_node, (document, document_id), layer_path, editor_api, persistent_data)?);
+2 -2
View File
@@ -171,8 +171,8 @@ impl JsEditorHandle {
} }
#[cfg(feature = "tauri")] #[cfg(feature = "tauri")]
{ {
let identifier = format!("http://localhost:3001/image/{:?}_{}", image.path, document_id); let identifier = format!("http://localhost:3001/image/{:?}_{}", &image.path, document_id);
fetchImage(image.path, image.node_id, image.mime, document_id, identifier); fetchImage(image.path.clone(), image.node_id, image.mime, document_id, identifier);
} }
} }
return; return;
+14 -5
View File
@@ -46,7 +46,9 @@ pub struct GraphicElement {
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Artboard { pub struct Artboard {
pub graphic_group: GraphicGroup, pub graphic_group: GraphicGroup,
pub bounds: Option<[IVec2; 2]>, pub location: IVec2,
pub dimensions: IVec2,
pub background: Color,
} }
pub struct ConstructLayerNode<Name, BlendMode, Opacity, Visible, Locked, Collapsed, Stack> { pub struct ConstructLayerNode<Name, BlendMode, Opacity, Visible, Locked, Collapsed, Stack> {
@@ -82,13 +84,20 @@ fn construct_layer<Data: Into<GraphicElementData>>(
stack stack
} }
pub struct ConstructArtboardNode<Bounds> { pub struct ConstructArtboardNode<Location, Dimensions, Background> {
bounds: Bounds, location: Location,
dimensions: Dimensions,
background: Background,
} }
#[node_fn(ConstructArtboardNode)] #[node_fn(ConstructArtboardNode)]
fn construct_artboard(graphic_group: GraphicGroup, bounds: Option<[IVec2; 2]>) -> Artboard { fn construct_artboard(graphic_group: GraphicGroup, location: IVec2, dimensions: IVec2, background: Color) -> Artboard {
Artboard { graphic_group, bounds } Artboard {
graphic_group,
location,
dimensions,
background,
}
} }
impl From<ImageFrame<Color>> for GraphicElementData { impl From<ImageFrame<Color>> for GraphicElementData {
@@ -99,8 +99,8 @@ impl GraphicElementRendered for Artboard {
self.graphic_group.render_svg(render, render_params) self.graphic_group.render_svg(render, render_params)
} }
fn bounding_box(&self, transform: DAffine2) -> Option<[DVec2; 2]> { fn bounding_box(&self, transform: DAffine2) -> Option<[DVec2; 2]> {
let artboard_bounds = self.bounds.map(|[a, b]| (transform * Quad::from_box([a.as_dvec2(), b.as_dvec2()])).bounding_box()); let artboard_bounds = (transform * Quad::from_box([self.location.as_dvec2(), self.location.as_dvec2() + self.dimensions.as_dvec2()])).bounding_box();
[self.graphic_group.bounding_box(transform), artboard_bounds].into_iter().flatten().reduce(Quad::combine_bounds) [self.graphic_group.bounding_box(transform), Some(artboard_bounds)].into_iter().flatten().reduce(Quad::combine_bounds)
} }
} }
+5 -5
View File
@@ -59,7 +59,7 @@ pub enum TaggedValue {
DocumentNode(DocumentNode), DocumentNode(DocumentNode),
GraphicGroup(graphene_core::GraphicGroup), GraphicGroup(graphene_core::GraphicGroup),
Artboard(graphene_core::Artboard), Artboard(graphene_core::Artboard),
Optional2IVec2(Option<[glam::IVec2; 2]>), IVec2(glam::IVec2),
} }
#[allow(clippy::derived_hash_with_manual_eq)] #[allow(clippy::derived_hash_with_manual_eq)]
@@ -129,7 +129,7 @@ impl Hash for TaggedValue {
Self::DocumentNode(document_node) => document_node.hash(state), Self::DocumentNode(document_node) => document_node.hash(state),
Self::GraphicGroup(graphic_group) => graphic_group.hash(state), Self::GraphicGroup(graphic_group) => graphic_group.hash(state),
Self::Artboard(artboard) => artboard.hash(state), Self::Artboard(artboard) => artboard.hash(state),
Self::Optional2IVec2(v) => v.hash(state), Self::IVec2(v) => v.hash(state),
} }
} }
} }
@@ -181,7 +181,7 @@ impl<'a> TaggedValue {
TaggedValue::DocumentNode(x) => Box::new(x), TaggedValue::DocumentNode(x) => Box::new(x),
TaggedValue::GraphicGroup(x) => Box::new(x), TaggedValue::GraphicGroup(x) => Box::new(x),
TaggedValue::Artboard(x) => Box::new(x), TaggedValue::Artboard(x) => Box::new(x),
TaggedValue::Optional2IVec2(x) => Box::new(x), TaggedValue::IVec2(x) => Box::new(x),
} }
} }
@@ -244,7 +244,7 @@ impl<'a> TaggedValue {
TaggedValue::DocumentNode(_) => concrete!(crate::document::DocumentNode), TaggedValue::DocumentNode(_) => concrete!(crate::document::DocumentNode),
TaggedValue::GraphicGroup(_) => concrete!(graphene_core::GraphicGroup), TaggedValue::GraphicGroup(_) => concrete!(graphene_core::GraphicGroup),
TaggedValue::Artboard(_) => concrete!(graphene_core::Artboard), TaggedValue::Artboard(_) => concrete!(graphene_core::Artboard),
TaggedValue::Optional2IVec2(_) => concrete!(Option<[glam::IVec2; 2]>), TaggedValue::IVec2(_) => concrete!(glam::IVec2),
} }
} }
@@ -296,7 +296,7 @@ impl<'a> TaggedValue {
x if x == TypeId::of::<crate::document::DocumentNode>() => Some(TaggedValue::DocumentNode(*downcast(input).unwrap())), x if x == TypeId::of::<crate::document::DocumentNode>() => Some(TaggedValue::DocumentNode(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::GraphicGroup>() => Some(TaggedValue::GraphicGroup(*downcast(input).unwrap())), x if x == TypeId::of::<graphene_core::GraphicGroup>() => Some(TaggedValue::GraphicGroup(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::Artboard>() => Some(TaggedValue::Artboard(*downcast(input).unwrap())), x if x == TypeId::of::<graphene_core::Artboard>() => Some(TaggedValue::Artboard(*downcast(input).unwrap())),
x if x == TypeId::of::<Option<[glam::IVec2; 2]>>() => Some(TaggedValue::Optional2IVec2(*downcast(input).unwrap())), x if x == TypeId::of::<glam::IVec2>() => Some(TaggedValue::IVec2(*downcast(input).unwrap())),
_ => None, _ => None,
} }
} }
@@ -602,7 +602,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
register_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>, input: ImageFrame<Color>, params: [String, BlendMode, f32, bool, bool, bool, graphene_core::GraphicGroup]), register_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>, input: ImageFrame<Color>, params: [String, BlendMode, f32, bool, bool, bool, graphene_core::GraphicGroup]),
register_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>, input: graphene_core::GraphicGroup, params: [String, BlendMode, f32, bool, bool, bool, graphene_core::GraphicGroup]), register_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>, input: graphene_core::GraphicGroup, params: [String, BlendMode, f32, bool, bool, bool, graphene_core::GraphicGroup]),
register_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>, input: graphene_core::Artboard, params: [String, BlendMode, f32, bool, bool, bool, graphene_core::GraphicGroup]), register_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>, input: graphene_core::Artboard, params: [String, BlendMode, f32, bool, bool, bool, graphene_core::GraphicGroup]),
register_node!(graphene_core::ConstructArtboardNode<_>, input: graphene_core::GraphicGroup, params: [Option<[glam::IVec2; 2]>]), register_node!(graphene_core::ConstructArtboardNode<_, _, _>, input: graphene_core::GraphicGroup, params: [glam::IVec2, glam::IVec2, Color]),
]; ];
let mut map: HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>> = HashMap::new(); let mut map: HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>> = HashMap::new();
for (id, c, types) in node_types.into_iter().flatten() { for (id, c, types) in node_types.into_iter().flatten() {