mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-02 20:42:16 +00:00
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:
parent
8d778e4848
commit
15eb4df8d4
18 changed files with 264 additions and 152 deletions
|
@ -26,6 +26,8 @@ pub struct Document {
|
|||
/// This identifier is not a hash and is not guaranteed to be equal for equivalent documents.
|
||||
#[serde(skip)]
|
||||
pub state_identifier: DefaultHasher,
|
||||
#[serde(default)]
|
||||
pub document_network: graph_craft::document::NodeNetwork,
|
||||
}
|
||||
|
||||
impl PartialEq for Document {
|
||||
|
@ -39,6 +41,19 @@ impl Default for Document {
|
|||
Self {
|
||||
root: Layer::new(LayerDataType::Folder(FolderLayer::default()), DAffine2::IDENTITY.to_cols_array()),
|
||||
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
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
/// 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.
|
||||
|
|
|
@ -3,7 +3,7 @@ pub mod misc;
|
|||
pub mod widgets;
|
||||
|
||||
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::button_widgets::*;
|
||||
pub use super::widgets::input_widgets::*;
|
||||
|
|
|
@ -202,7 +202,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
|||
}
|
||||
#[remain::unsorted]
|
||||
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]
|
||||
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 } => {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ impl<'a> ModifyInputsContext<'a> {
|
|||
} else {
|
||||
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.responses.add(PropertiesPanelMessage::ResendActiveProperties);
|
||||
let layer_path = self.layer.to_vec();
|
||||
|
|
|
@ -59,6 +59,7 @@ pub enum NodeGraphMessage {
|
|||
PasteNodes {
|
||||
serialized_nodes: String,
|
||||
},
|
||||
RunDocumentGraph,
|
||||
SelectNodes {
|
||||
nodes: Vec<NodeId>,
|
||||
},
|
||||
|
@ -92,4 +93,5 @@ pub enum NodeGraphMessage {
|
|||
TogglePreviewImpl {
|
||||
node_id: NodeId,
|
||||
},
|
||||
UpdateNewNodeGraph,
|
||||
}
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
pub use self::document_node_types::*;
|
||||
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::widgets::button_widgets::TextButton;
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::node_graph_executor::NodeGraphExecutor;
|
||||
|
||||
use document_legacy::document::Document;
|
||||
use document_legacy::layers::layer_layer::LayerLayer;
|
||||
use document_legacy::LayerId;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput, NodeNetwork, NodeOutput};
|
||||
|
@ -33,6 +31,7 @@ pub enum FrontendGraphDataType {
|
|||
Number,
|
||||
#[serde(rename = "number")]
|
||||
Boolean,
|
||||
/// Refers to the mathematical vector, with direction and magnitude.
|
||||
#[serde(rename = "vec2")]
|
||||
Vector,
|
||||
#[serde(rename = "graphic")]
|
||||
|
@ -46,7 +45,7 @@ impl FrontendGraphDataType {
|
|||
TaggedValue::String(_) => Self::Text,
|
||||
TaggedValue::F32(_) | TaggedValue::F64(_) | TaggedValue::U32(_) | TaggedValue::DAffine2(_) => Self::Number,
|
||||
TaggedValue::Bool(_) => Self::Boolean,
|
||||
TaggedValue::DVec2(_) => Self::Vector,
|
||||
TaggedValue::DVec2(_) | TaggedValue::IVec2(_) => Self::Vector,
|
||||
TaggedValue::Image(_) => Self::Raster,
|
||||
TaggedValue::ImageFrame(_) => Self::Raster,
|
||||
TaggedValue::Color(_) => Self::Color,
|
||||
|
@ -118,33 +117,43 @@ impl FrontendNodeType {
|
|||
|
||||
#[derive(Debug, Clone, PartialEq, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub struct NodeGraphMessageHandler {
|
||||
pub layer_path: Option<Vec<document_legacy::LayerId>>,
|
||||
pub nested_path: Vec<graph_craft::document::NodeId>,
|
||||
pub selected_nodes: Vec<graph_craft::document::NodeId>,
|
||||
pub layer_path: Option<Vec<LayerId>>,
|
||||
pub nested_path: Vec<NodeId>,
|
||||
pub selected_nodes: Vec<NodeId>,
|
||||
#[serde(skip)]
|
||||
pub widgets: [LayoutGroup; 2],
|
||||
}
|
||||
|
||||
impl NodeGraphMessageHandler {
|
||||
fn get_root_network<'a>(&self, document: &'a Document) -> Option<&'a graph_craft::document::NodeNetwork> {
|
||||
self.layer_path.as_ref().and_then(|path| document.layer(path).ok()).and_then(|layer| layer.as_layer_network().ok())
|
||||
pub fn update_layer_path(&mut self, layer_path: Option<Vec<LayerId>>, responses: &mut VecDeque<Message>) {
|
||||
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
|
||||
.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())
|
||||
.unwrap_or(&mut document.document_network)
|
||||
}
|
||||
|
||||
/// Get the active graph_craft NodeNetwork struct
|
||||
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
|
||||
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
|
||||
|
@ -156,38 +165,41 @@ impl NodeGraphMessageHandler {
|
|||
}
|
||||
|
||||
/// 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
|
||||
// let mut path = vec!["Root".to_string()];
|
||||
// let mut network = self.get_root_network(document);
|
||||
// for node_id in &self.nested_path {
|
||||
// let node = network.and_then(|network| network.nodes.get(node_id));
|
||||
// if let Some(DocumentNode { name, .. }) = node {
|
||||
// path.push(name.clone());
|
||||
// }
|
||||
// network = node.and_then(|node| node.implementation.get_network());
|
||||
// }
|
||||
// let nesting = path.len();
|
||||
let mut path = vec![];
|
||||
if let Some(layer) = self.layer_path.as_ref().and_then(|path| document.layer(&path).ok()) {
|
||||
path.push(format!("Layer: {}", layer.name.as_ref().map(|name| name.as_str()).unwrap_or("Untitled Layer")));
|
||||
} else {
|
||||
path.push("Document Network".to_string());
|
||||
}
|
||||
let mut network = Some(self.get_root_network(document));
|
||||
for node_id in &self.nested_path {
|
||||
let node = network.and_then(|network| network.nodes.get(node_id));
|
||||
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
|
||||
// self.widgets[0] = LayoutGroup::Row {
|
||||
// widgets: vec![WidgetHolder::new(Widget::BreadcrumbTrailButtons(BreadcrumbTrailButtons {
|
||||
// labels: path.clone(),
|
||||
// on_update: WidgetCallback::new(move |input: &u64| {
|
||||
// NodeGraphMessage::ExitNestedNetwork {
|
||||
// depth_of_nesting: nesting - (*input as usize) - 1,
|
||||
// }
|
||||
// .into()
|
||||
// }),
|
||||
// ..Default::default()
|
||||
// }))],
|
||||
// };
|
||||
// Update UI
|
||||
self.widgets[0] = LayoutGroup::Row {
|
||||
widgets: vec![BreadcrumbTrailButtons::new(path.clone())
|
||||
.on_update(move |input: &u64| {
|
||||
NodeGraphMessage::ExitNestedNetwork {
|
||||
depth_of_nesting: nesting - (*input as usize) - 1,
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_holder()],
|
||||
};
|
||||
|
||||
// self.send_node_bar_layout(responses);
|
||||
self.send_node_bar_layout(responses);
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
let mut widgets = Vec::new();
|
||||
|
||||
|
@ -235,8 +247,8 @@ impl NodeGraphMessageHandler {
|
|||
}
|
||||
|
||||
/// Collate the properties panel sections for a node graph
|
||||
pub fn collate_properties(&self, graph: &LayerLayer, context: &mut NodePropertiesContext, sections: &mut Vec<LayoutGroup>) {
|
||||
let mut network = &graph.network;
|
||||
pub fn collate_properties(&self, context: &mut NodePropertiesContext, sections: &mut Vec<LayoutGroup>) {
|
||||
let mut network = context.network;
|
||||
for segment in &self.nested_path {
|
||||
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())))
|
||||
.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]
|
||||
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]
|
||||
match message {
|
||||
NodeGraphMessage::CloseNodeGraph => {
|
||||
if let Some(_old_layer_path) = self.layer_path.take() {
|
||||
Self::clear_graph(responses);
|
||||
responses.add(PropertiesPanelMessage::ResendActiveProperties);
|
||||
}
|
||||
self.update_layer_path(None, responses);
|
||||
}
|
||||
NodeGraphMessage::ConnectNodesByLink {
|
||||
output_node,
|
||||
|
@ -703,6 +703,13 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &NodeGraphExecutor)> for N
|
|||
|
||||
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 } => {
|
||||
self.selected_nodes = nodes;
|
||||
self.update_selection_action_buttons(document, responses);
|
||||
|
@ -715,6 +722,8 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &NodeGraphExecutor)> for N
|
|||
if should_rerender {
|
||||
if let Some(layer_path) = self.layer_path.clone() {
|
||||
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 let Some(layer_path) = self.layer_path.clone() {
|
||||
responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path });
|
||||
} else {
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -754,11 +765,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &NodeGraphExecutor)> for N
|
|||
return;
|
||||
};
|
||||
|
||||
let network = document
|
||||
.layer_mut(&layer_path)
|
||||
.ok()
|
||||
.and_then(|layer| layer.as_layer_network_mut().ok())
|
||||
.and_then(|network| network.nested_network_mut(node_path));
|
||||
let network = self.get_root_network_mut(document).nested_network_mut(node_path);
|
||||
|
||||
if let Some(network) = network {
|
||||
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 });
|
||||
}
|
||||
}
|
||||
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 {
|
||||
if self.layer_path.is_some() && !self.selected_nodes.is_empty() {
|
||||
if !self.selected_nodes.is_empty() {
|
||||
actions!(NodeGraphMessageDiscriminant; DeleteSelectedNodes, Cut, Copy, DuplicateSelectedNodes, ToggleHidden)
|
||||
} else {
|
||||
actions!(NodeGraphMessageDiscriminant;)
|
||||
|
|
|
@ -171,10 +171,12 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||
DocumentNodeType {
|
||||
name: "Artboard",
|
||||
category: "General",
|
||||
identifier: NodeImplementation::proto("graphene_core::ConstructArtboardNode<_>"),
|
||||
identifier: NodeImplementation::proto("graphene_core::ConstructArtboardNode<_, _, _>"),
|
||||
inputs: vec![
|
||||
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)],
|
||||
properties: node_properties::artboard_properties,
|
||||
|
|
|
@ -2,7 +2,9 @@ use crate::messages::layout::utility_types::widget_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::document::value::TaggedValue;
|
||||
use graph_craft::document::{DocumentNode, NodeId, NodeInput};
|
||||
|
@ -127,6 +129,58 @@ fn bool_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
|||
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> {
|
||||
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> {
|
||||
let translation = {
|
||||
let index = 1;
|
||||
|
||||
let mut widgets = start_widgets(document_node, node_id, index, "Translation", FrontendGraphDataType::Vector, false);
|
||||
|
||||
let translation_assist = |widgets: &mut Vec<WidgetHolder>| {
|
||||
let pivot_index = 5;
|
||||
if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::DVec2(pivot),
|
||||
|
@ -892,32 +942,10 @@ pub fn transform_properties(document_node: &DocumentNode, node_id: NodeId, _cont
|
|||
.widget_holder(),
|
||||
);
|
||||
} 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 index = 2;
|
||||
|
@ -944,32 +972,7 @@ pub fn transform_properties(document_node: &DocumentNode, node_id: NodeId, _cont
|
|||
LayoutGroup::Row { widgets }
|
||||
};
|
||||
|
||||
let scale = {
|
||||
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 }
|
||||
};
|
||||
let scale = vec2_widget(document_node, node_id, 3, "Scale", "X", "Y", add_blank_assist);
|
||||
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> {
|
||||
let label = text_widget(document_node, node_id, 1, "Label", true);
|
||||
vec![LayoutGroup::Row { widgets: label }]
|
||||
let location = vec2_widget(document_node, node_id, 1, "Location", "X", "Y", add_blank_assist);
|
||||
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 crate::messages::layout::utility_types::layout_widget::{Layout, WidgetLayout};
|
||||
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::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 {
|
||||
|
|
|
@ -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::input_widgets::{CheckboxInput, ColorInput, NumberInput, NumberInputMode, RadioEntryData, RadioInput, TextInput};
|
||||
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::prelude::*;
|
||||
use crate::node_graph_executor::NodeGraphExecutor;
|
||||
|
@ -301,7 +302,7 @@ pub fn register_artwork_layer_properties(
|
|||
LayerDataType::Layer(layer) => {
|
||||
let mut properties_sections = Vec::new();
|
||||
|
||||
let mut context = crate::messages::portfolio::document::node_graph::NodePropertiesContext {
|
||||
let mut context = NodePropertiesContext {
|
||||
persistent_data,
|
||||
document,
|
||||
responses,
|
||||
|
@ -310,7 +311,7 @@ pub fn register_artwork_layer_properties(
|
|||
executor,
|
||||
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
|
||||
}
|
||||
|
@ -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 {
|
||||
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::default(), None);
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
let message = DocumentMessage::SetImageBlobUrl {
|
||||
layer_path,
|
||||
blob_url,
|
||||
|
|
|
@ -367,17 +367,21 @@ impl NodeGraphExecutor {
|
|||
|
||||
// Get the node graph layer
|
||||
let document = documents.get_mut(&document_id).ok_or_else(|| "Invalid document".to_string())?;
|
||||
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:?}"))?;
|
||||
|
||||
// Construct the input image frame
|
||||
let transform = DAffine2::IDENTITY;
|
||||
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();
|
||||
layer_layer.network.clone()
|
||||
};
|
||||
|
||||
// Construct the input image frame
|
||||
let transform = DAffine2::IDENTITY;
|
||||
let image_frame = ImageFrame { image, transform };
|
||||
|
||||
// Special execution path for generating Imaginate (as generation requires IO from outside node graph)
|
||||
/*if let Some(imaginate_node) = imaginate_node {
|
||||
|
|
|
@ -171,8 +171,8 @@ impl JsEditorHandle {
|
|||
}
|
||||
#[cfg(feature = "tauri")]
|
||||
{
|
||||
let identifier = format!("http://localhost:3001/image/{:?}_{}", image.path, document_id);
|
||||
fetchImage(image.path, image.node_id, image.mime, document_id, identifier);
|
||||
let identifier = format!("http://localhost:3001/image/{:?}_{}", &image.path, document_id);
|
||||
fetchImage(image.path.clone(), image.node_id, image.mime, document_id, identifier);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
|
|
@ -46,7 +46,9 @@ pub struct GraphicElement {
|
|||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct Artboard {
|
||||
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> {
|
||||
|
@ -82,13 +84,20 @@ fn construct_layer<Data: Into<GraphicElementData>>(
|
|||
stack
|
||||
}
|
||||
|
||||
pub struct ConstructArtboardNode<Bounds> {
|
||||
bounds: Bounds,
|
||||
pub struct ConstructArtboardNode<Location, Dimensions, Background> {
|
||||
location: Location,
|
||||
dimensions: Dimensions,
|
||||
background: Background,
|
||||
}
|
||||
|
||||
#[node_fn(ConstructArtboardNode)]
|
||||
fn construct_artboard(graphic_group: GraphicGroup, bounds: Option<[IVec2; 2]>) -> Artboard {
|
||||
Artboard { graphic_group, bounds }
|
||||
fn construct_artboard(graphic_group: GraphicGroup, location: IVec2, dimensions: IVec2, background: Color) -> Artboard {
|
||||
Artboard {
|
||||
graphic_group,
|
||||
location,
|
||||
dimensions,
|
||||
background,
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ImageFrame<Color>> for GraphicElementData {
|
||||
|
|
|
@ -99,8 +99,8 @@ impl GraphicElementRendered for Artboard {
|
|||
self.graphic_group.render_svg(render, render_params)
|
||||
}
|
||||
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());
|
||||
[self.graphic_group.bounding_box(transform), artboard_bounds].into_iter().flatten().reduce(Quad::combine_bounds)
|
||||
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), Some(artboard_bounds)].into_iter().flatten().reduce(Quad::combine_bounds)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ pub enum TaggedValue {
|
|||
DocumentNode(DocumentNode),
|
||||
GraphicGroup(graphene_core::GraphicGroup),
|
||||
Artboard(graphene_core::Artboard),
|
||||
Optional2IVec2(Option<[glam::IVec2; 2]>),
|
||||
IVec2(glam::IVec2),
|
||||
}
|
||||
|
||||
#[allow(clippy::derived_hash_with_manual_eq)]
|
||||
|
@ -129,7 +129,7 @@ impl Hash for TaggedValue {
|
|||
Self::DocumentNode(document_node) => document_node.hash(state),
|
||||
Self::GraphicGroup(graphic_group) => graphic_group.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::GraphicGroup(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::GraphicGroup(_) => concrete!(graphene_core::GraphicGroup),
|
||||
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::<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::<Option<[glam::IVec2; 2]>>() => Some(TaggedValue::Optional2IVec2(*downcast(input).unwrap())),
|
||||
x if x == TypeId::of::<glam::IVec2>() => Some(TaggedValue::IVec2(*downcast(input).unwrap())),
|
||||
_ => 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: 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::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();
|
||||
for (id, c, types) in node_types.into_iter().flatten() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue