Skip to content

Commit

Permalink
Thumbnails for the layer node (#1210)
Browse files Browse the repository at this point in the history
* 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

* Code review changes

* Code review pass with minor changes

---------

Co-authored-by: Keavon Chambers <[email protected]>
  • Loading branch information
0HyperCube and Keavon authored May 18, 2023
1 parent d76bd36 commit 88c4713
Show file tree
Hide file tree
Showing 24 changed files with 609 additions and 131 deletions.
4 changes: 2 additions & 2 deletions document-legacy/src/layers/layer_layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,15 @@ impl LayerData for LayerLayer {
match &self.cached_output_data {
CachedOutputData::VectorPath(vector_data) => {
let layer_bounds = vector_data.bounding_box().unwrap_or_default();
let transfomed_bounds = vector_data.bounding_box_with_transform(transform).unwrap_or_default();
let transformed_bounds = vector_data.bounding_box_with_transform(transform).unwrap_or_default();

let _ = write!(svg, "<path d=\"");
for subpath in &vector_data.subpaths {
let _ = subpath.subpath_to_svg(svg, transform);
}
svg.push('"');

svg.push_str(&vector_data.style.render(render_data.view_mode, svg_defs, transform, layer_bounds, transfomed_bounds));
svg.push_str(&vector_data.style.render(render_data.view_mode, svg_defs, transform, layer_bounds, transformed_bounds));
let _ = write!(svg, "/>");
}
CachedOutputData::BlobURL(blob_url) => {
Expand Down
2 changes: 2 additions & 0 deletions editor/src/messages/frontend/utility_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub struct FrontendImageData {
#[serde(skip)]
pub image_data: std::sync::Arc<Vec<u8>>,
pub transform: Option<[f64; 6]>,
#[serde(rename = "nodeId")]
pub node_id: Option<graph_craft::document::NodeId>,
}

#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize, specta::Type)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
}
#[remain::unsorted]
NodeGraph(message) => {
let selected_layers = &mut self.layer_metadata.iter().filter_map(|(path, data)| data.selected.then_some(path.as_slice()));
self.node_graph_handler.process_message(message, responses, (&mut self.document_legacy, selected_layers));
self.node_graph_handler.process_message(message, responses, (&mut self.document_legacy, executor));
}
#[remain::unsorted]
GraphOperation(message) => GraphOperationMessageHandler.process_message(message, responses, (&mut self.document_legacy, &mut self.node_graph_handler)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ 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::prelude::*;
use crate::node_graph_executor::NodeGraphExecutor;

use document_legacy::document::Document;
use document_legacy::layers::layer_layer::LayerLayer;
Expand Down Expand Up @@ -84,6 +85,8 @@ pub struct FrontendNode {
pub position: (i32, i32),
pub disabled: bool,
pub previewed: bool,
#[serde(rename = "thumbnailSvg")]
pub thumbnail_svg: Option<String>,
}

// (link_start, link_end, link_end_input_index)
Expand Down Expand Up @@ -254,9 +257,11 @@ impl NodeGraphMessageHandler {
}
}

fn send_graph(network: &NodeNetwork, responses: &mut VecDeque<Message>) {
fn send_graph(network: &NodeNetwork, executor: &NodeGraphExecutor, layer_path: &Option<Vec<LayerId>>, responses: &mut VecDeque<Message>) {
responses.add(PropertiesPanelMessage::ResendActiveProperties);

let layer_id = layer_path.as_ref().and_then(|path| path.last().copied());

// List of links in format (link_start, link_end, link_end_input_index)
let links = network
.nodes
Expand Down Expand Up @@ -288,37 +293,49 @@ impl NodeGraphMessageHandler {
warn!("Node '{}' does not exist in library", node.name);
continue;
};

let primary_input = node
.inputs
.first()
.filter(|input| input.is_exposed())
.and_then(|_| node_type.inputs.get(0))
.map(|input_type| input_type.data_type);
let exposed_inputs = node
.inputs
.iter()
.zip(node_type.inputs.iter())
.skip(1)
.filter(|(input, _)| input.is_exposed())
.map(|(_, input_type)| NodeGraphInput {
data_type: input_type.data_type,
name: input_type.name.to_string(),
})
.collect();

let outputs = node_type
.outputs
.iter()
.map(|output_type| NodeGraphOutput {
data_type: output_type.data_type,
name: output_type.name.to_string(),
})
.collect();

let thumbnail_svg = layer_id
.and_then(|layer_id| executor.thumbnails.get(&layer_id))
.and_then(|layer| layer.get(id))
.map(|svg| svg.to_string());

nodes.push(FrontendNode {
id: *id,
display_name: node.name.clone(),
primary_input: node
.inputs
.first()
.filter(|input| input.is_exposed())
.and_then(|_| node_type.inputs.get(0))
.map(|input_type| input_type.data_type),
exposed_inputs: node
.inputs
.iter()
.zip(node_type.inputs.iter())
.skip(1)
.filter(|(input, _)| input.is_exposed())
.map(|(_, input_type)| NodeGraphInput {
data_type: input_type.data_type,
name: input_type.name.to_string(),
})
.collect(),
outputs: node_type
.outputs
.iter()
.map(|output_type| NodeGraphOutput {
data_type: output_type.data_type,
name: output_type.name.to_string(),
})
.collect(),
primary_input,
exposed_inputs,
outputs,
position: node.metadata.position.into(),
previewed: network.outputs_contain(*id),
disabled: network.disabled.contains(id),
thumbnail_svg,
})
}
responses.add(FrontendMessage::UpdateNodeGraph { nodes, links });
Expand Down Expand Up @@ -405,9 +422,9 @@ impl NodeGraphMessageHandler {
}
}

impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &[LayerId]>)> for NodeGraphMessageHandler {
impl MessageHandler<NodeGraphMessage, (&mut Document, &NodeGraphExecutor)> for NodeGraphMessageHandler {
#[remain::check]
fn process_message(&mut self, message: NodeGraphMessage, responses: &mut VecDeque<Message>, (document, _selected): (&mut Document, &mut dyn Iterator<Item = &[LayerId]>)) {
fn process_message(&mut self, message: NodeGraphMessage, responses: &mut VecDeque<Message>, (document, executor): (&mut Document, &NodeGraphExecutor)) {
#[remain::sorted]
match message {
NodeGraphMessage::CloseNodeGraph => {
Expand Down Expand Up @@ -536,7 +553,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
}
}
if let Some(network) = self.get_active_network(document) {
Self::send_graph(network, responses);
Self::send_graph(network, executor, &self.layer_path, responses);
}
self.collect_nested_addresses(document, responses);
self.update_selected(document, responses);
Expand All @@ -561,7 +578,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
responses.add(NodeGraphMessage::InsertNode { node_id, document_node });
}

Self::send_graph(network, responses);
Self::send_graph(network, executor, &self.layer_path, responses);
self.update_selected(document, responses);
}
}
Expand All @@ -571,7 +588,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
self.nested_path.pop();
}
if let Some(network) = self.get_active_network(document) {
Self::send_graph(network, responses);
Self::send_graph(network, executor, &self.layer_path, responses);
}
self.collect_nested_addresses(document, responses);
self.update_selected(document, responses);
Expand Down Expand Up @@ -622,15 +639,15 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
node.metadata.position += IVec2::new(displacement_x, displacement_y)
}
}
Self::send_graph(network, responses);
Self::send_graph(network, executor, &self.layer_path, responses);
}
NodeGraphMessage::OpenNodeGraph { layer_path } => {
self.layer_path = Some(layer_path);

if let Some(network) = self.get_active_network(document) {
self.selected_nodes.clear();

Self::send_graph(network, responses);
Self::send_graph(network, executor, &self.layer_path, responses);

let node_types = document_node_types::collect_node_types();
responses.add(FrontendMessage::UpdateNodeTypes { node_types });
Expand Down Expand Up @@ -689,7 +706,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
}
NodeGraphMessage::SendGraph { should_rerender } => {
if let Some(network) = self.get_active_network(document) {
Self::send_graph(network, responses);
Self::send_graph(network, executor, &self.layer_path, responses);
if should_rerender {
if let Some(layer_path) = self.layer_path.clone() {
responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path });
Expand Down Expand Up @@ -816,7 +833,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
.disabled
.extend(self.selected_nodes.iter().filter(|&id| !network.inputs.contains(id) && !original_outputs.contains(id)));
}
Self::send_graph(network, responses);
Self::send_graph(network, executor, &self.layer_path, responses);

// Only generate node graph if one of the selected nodes is connected to the output
if self.selected_nodes.iter().any(|&node_id| network.connected_to_output(node_id, true)) {
Expand All @@ -842,7 +859,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
} else {
return;
}
Self::send_graph(network, responses);
Self::send_graph(network, executor, &self.layer_path, responses);
}
self.update_selection_action_buttons(document, responses);
if let Some(layer_path) = self.layer_path.clone() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,40 @@ fn static_nodes() -> Vec<DocumentNodeType> {
DocumentNodeType {
name: "Layer",
category: "General",
identifier: NodeImplementation::proto("graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>"),
identifier: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![0; 8],
outputs: vec![NodeOutput::new(1, 0)],
nodes: [
(
0,
DocumentNode {
inputs: vec![
NodeInput::Network(concrete!(graphene_core::vector::VectorData)),
NodeInput::Network(concrete!(String)),
NodeInput::Network(concrete!(BlendMode)),
NodeInput::Network(concrete!(f32)),
NodeInput::Network(concrete!(bool)),
NodeInput::Network(concrete!(bool)),
NodeInput::Network(concrete!(bool)),
NodeInput::Network(concrete!(graphene_core::GraphicGroup)),
],
implementation: DocumentNodeImplementation::proto("graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>"),
..Default::default()
},
),
// The monitor node is used to display a thumbnail in the UI.
(
1,
DocumentNode {
inputs: vec![NodeInput::node(0, 0)],
implementation: DocumentNodeImplementation::proto("graphene_std::memo::MonitorNode<_>"),
..Default::default()
},
),
]
.into(),
..Default::default()
}),
inputs: vec![
DocumentInputType::value("Vector Data", TaggedValue::VectorData(graphene_core::vector::VectorData::empty()), true),
DocumentInputType::value("Name", TaggedValue::String(String::new()), false),
Expand Down Expand Up @@ -528,7 +561,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
identifier: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![0],
outputs: vec![NodeOutput::new(1, 0)],
nodes: vec![
nodes: [
(
0,
DocumentNode {
Expand All @@ -548,8 +581,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
},
),
]
.into_iter()
.collect(),
.into(),
..Default::default()
}),
inputs: vec![DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true)],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -727,12 +727,12 @@ pub fn adjust_selective_color_properties(document_node: &DocumentNode, node_id:
} = &document_node.inputs[colors_index]
{
use SelectiveColorChoice::*;
let entries = [vec![Reds, Yellows, Greens, Cyans, Blues, Magentas], vec![Whites, Neutrals, Blacks]]
let entries = [[Reds, Yellows, Greens, Cyans, Blues, Magentas].as_slice(), [Whites, Neutrals, Blacks].as_slice()]
.into_iter()
.map(|section| {
section
.into_iter()
.map(|choice| DropdownEntryData::new(choice.to_string()).on_update(update_value(move |_| TaggedValue::SelectiveColorChoice(choice), node_id, colors_index)))
.map(|choice| DropdownEntryData::new(choice.to_string()).on_update(update_value(move |_| TaggedValue::SelectiveColorChoice(*choice), node_id, colors_index)))
.collect()
})
.collect();
Expand Down
1 change: 1 addition & 0 deletions editor/src/messages/portfolio/portfolio_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ pub enum PortfolioMessage {
SetImageBlobUrl {
document_id: u64,
layer_path: Vec<LayerId>,
node_id: Option<NodeId>,
blob_url: String,
resolution: (f64, f64),
},
Expand Down
6 changes: 6 additions & 0 deletions editor/src/messages/portfolio/portfolio_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -503,9 +503,15 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
PortfolioMessage::SetImageBlobUrl {
document_id,
layer_path,
node_id,
blob_url,
resolution,
} => {
if let (Some(layer_id), Some(node_id)) = (layer_path.last().copied(), node_id) {
self.executor.insert_thumbnail_blob_url(blob_url, layer_id, node_id, responses);
return;
}

let message = DocumentMessage::SetImageBlobUrl {
layer_path,
blob_url,
Expand Down
Loading

0 comments on commit 88c4713

Please sign in to comment.