thumbnails

This commit is contained in:
Adam
2025-09-06 21:27:24 -07:00
parent a25a23e61e
commit 8caf9317a5
8 changed files with 182 additions and 43 deletions

View File

@@ -7,6 +7,7 @@ use glam::IVec2;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeInput};
use graph_craft::proto::GraphErrors;
use graphene_std::Graphic;
use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypesDelta;
#[impl_message(Message, DocumentMessage, NodeGraph)]
@@ -218,6 +219,10 @@ pub enum NodeGraphMessage {
UpdateImportsExports,
UpdateLayerPanel,
UpdateNewNodeGraph,
UpdateThumbnail {
node_id: NodeId,
graphic: Graphic,
},
UpdateTypes {
#[serde(skip)]
resolved_types: ResolvedDocumentNodeTypesDelta,

View File

@@ -92,6 +92,8 @@ pub struct NodeGraphMessageHandler {
reordering_export: Option<usize>,
/// The end index of the moved connector
end_index: Option<usize>,
// The rendered string for each thumbnail
pub thumbnails: HashMap<NodeId, Graphic>,
}
/// NodeGraphMessageHandler always modifies the network which the selected nodes are in. No GraphOperationMessages should be added here, since those messages will always affect the document network.
@@ -1924,6 +1926,9 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
responses.add(NodeGraphMessage::SendGraph);
}
NodeGraphMessage::UpdateThumbnail { node_id, graphic } => {
self.thumbnails.insert(node_id, graphic);
}
NodeGraphMessage::UpdateTypes { resolved_types, node_graph_errors } => {
network_interface.resolved_types.update(resolved_types);
self.node_graph_errors = node_graph_errors;
@@ -2559,6 +2564,7 @@ impl Default for NodeGraphMessageHandler {
reordering_export: None,
reordering_import: None,
end_index: None,
thumbnails: HashMap::new(),
}
}
}

View File

@@ -29,10 +29,16 @@ pub struct ExecutionRequest {
pub struct ExecutionResponse {
execution_id: u64,
result: Result<TaggedValue, String>,
responses: VecDeque<FrontendMessage>,
execution_responses: Vec<ExecutionResponseMessage>,
vector_modify: HashMap<NodeId, Vector>,
}
pub enum ExecutionResponseMessage {
/// The resulting value from the temporary inspected during execution
inspect_result: Option<InspectResult>,
InspectResult(Option<InspectResult>),
UpdateNodeGraphThumbnail(NodeId, Graphic),
UpdateFrontendThumbnail(NodeId, String),
SendGraph,
}
#[derive(serde::Serialize, serde::Deserialize)]
@@ -260,11 +266,24 @@ impl NodeGraphExecutor {
let ExecutionResponse {
execution_id,
result,
responses: existing_responses,
execution_responses,
vector_modify,
inspect_result,
} = execution_response;
for execution_response in execution_responses {
match execution_response {
ExecutionResponseMessage::InspectResult(inspect_result) => {
// Update the Data panel on the frontend using the value of the inspect result.
if let Some(inspect_result) = (self.previous_node_to_inspect.is_some()).then_some(inspect_result).flatten() {
responses.add(DataPanelMessage::UpdateLayout { inspect_result });
} else {
responses.add(DataPanelMessage::ClearLayout);
}
}
ExecutionResponseMessage::UpdateNodeGraphThumbnail(node_id, graphic) => responses.add(NodeGraphMessage::UpdateThumbnail { node_id, graphic }),
ExecutionResponseMessage::UpdateFrontendThumbnail(node_id, string) => responses.add(FrontendMessage::UpdateNodeThumbnail { id: node_id, value: string }),
ExecutionResponseMessage::SendGraph => responses.add(NodeGraphMessage::SendGraph),
}
}
responses.add(OverlaysMessage::Draw);
let node_graph_output = match result {
@@ -277,7 +296,6 @@ impl NodeGraphExecutor {
}
};
responses.extend(existing_responses.into_iter().map(Into::into));
document.network_interface.update_vector_modify(vector_modify);
let execution_context = self.futures.remove(&execution_id).ok_or_else(|| "Invalid generation ID".to_string())?;
@@ -291,13 +309,6 @@ impl NodeGraphExecutor {
execution_id,
document_id: execution_context.document_id,
});
// Update the Data panel on the frontend using the value of the inspect result.
if let Some(inspect_result) = (self.previous_node_to_inspect.is_some()).then_some(inspect_result).flatten() {
responses.add(DataPanelMessage::UpdateLayout { inspect_result });
} else {
responses.add(DataPanelMessage::ClearLayout);
}
}
NodeGraphUpdate::CompilationResponse(execution_response) => {
let CompilationResponse { node_graph_errors, result } = execution_response;

View File

@@ -212,14 +212,14 @@ impl NodeRuntime {
}
GraphRuntimeRequest::ExecutionRequest(ExecutionRequest { execution_id, render_config, .. }) => {
let result = self.execute_network(render_config).await;
let mut responses = VecDeque::new();
let mut execution_responses = Vec::new();
// TODO: Only process monitor nodes if the graph has changed, not when only the Footprint changes
self.process_monitor_nodes(&mut responses, self.update_thumbnails);
self.process_monitor_nodes(&mut execution_responses, self.update_thumbnails);
self.update_thumbnails = false;
// Resolve the result from the inspection by accessing the monitor node
let inspect_result = self.inspect_state.and_then(|state| state.access(&self.executor));
execution_responses.push(ExecutionResponseMessage::InspectResult(inspect_result));
let texture = if let Ok(TaggedValue::RenderOutput(RenderOutput {
data: RenderOutputType::Texture(texture),
..
@@ -233,9 +233,8 @@ impl NodeRuntime {
self.sender.send_execution_response(ExecutionResponse {
execution_id,
result,
responses,
execution_responses,
vector_modify: self.vector_modify.clone(),
inspect_result,
});
return texture;
}
@@ -289,10 +288,10 @@ impl NodeRuntime {
}
/// Updates state data
pub fn process_monitor_nodes(&mut self, responses: &mut VecDeque<FrontendMessage>, update_thumbnails: bool) {
pub fn process_monitor_nodes(&mut self, responses: &mut Vec<ExecutionResponseMessage>, update_thumbnails: bool) {
// TODO: Consider optimizing this since it's currently O(m*n^2), with a sort it could be made O(m * n*log(n))
self.thumbnail_renders.retain(|id, _| self.monitor_nodes.iter().any(|monitor_node_path| monitor_node_path.contains(id)));
let mut updated_thumbnails = false;
for monitor_node_path in &self.monitor_nodes {
// Skip the inspect monitor node
if self.inspect_state.is_some_and(|inspect_state| monitor_node_path.last().copied() == Some(inspect_state.monitor_node)) {
@@ -316,13 +315,17 @@ impl NodeRuntime {
// Graphic table: thumbnail
if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, Table<Graphic>>>() {
if update_thumbnails {
Self::render_thumbnail(&mut self.thumbnail_renders, parent_network_node_id, &io.output, responses)
Self::render_thumbnail(&mut self.thumbnail_renders, parent_network_node_id, &io.output, responses);
responses.push(ExecutionResponseMessage::UpdateNodeGraphThumbnail(parent_network_node_id, io.output.clone().to_graphic()));
updated_thumbnails = true;
}
}
// Artboard table: thumbnail
else if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, Table<Artboard>>>() {
if update_thumbnails {
Self::render_thumbnail(&mut self.thumbnail_renders, parent_network_node_id, &io.output, responses)
Self::render_thumbnail(&mut self.thumbnail_renders, parent_network_node_id, &io.output, responses);
responses.push(ExecutionResponseMessage::UpdateNodeGraphThumbnail(parent_network_node_id, io.output.clone().to_graphic()));
updated_thumbnails = true;
}
}
// Vector table: vector modifications
@@ -337,18 +340,21 @@ impl NodeRuntime {
log::warn!("Failed to downcast monitor node output {parent_network_node_id:?}");
}
}
if updated_thumbnails {
responses.push(ExecutionResponseMessage::SendGraph);
}
}
/// If this is `Graphic` data, regenerate click targets and thumbnails for the layers in the graph, modifying the state and updating the UI.
fn render_thumbnail(thumbnail_renders: &mut HashMap<NodeId, Vec<SvgSegment>>, parent_network_node_id: NodeId, graphic: &impl Render, responses: &mut VecDeque<FrontendMessage>) {
fn render_thumbnail(thumbnail_renders: &mut HashMap<NodeId, Vec<SvgSegment>>, parent_network_node_id: NodeId, graphic: &impl Render, responses: &mut Vec<ExecutionResponseMessage>) {
// Skip thumbnails if the layer is too complex (for performance)
if graphic.render_complexity() > 1000 {
let old = thumbnail_renders.insert(parent_network_node_id, Vec::new());
if old.is_none_or(|v| !v.is_empty()) {
responses.push_back(FrontendMessage::UpdateNodeThumbnail {
id: parent_network_node_id,
value: "<svg viewBox=\"0 0 10 10\"><title>Dense thumbnail omitted for performance</title><line x1=\"0\" y1=\"10\" x2=\"10\" y2=\"0\" stroke=\"red\" /></svg>".to_string(),
});
responses.push(ExecutionResponseMessage::UpdateFrontendThumbnail(
parent_network_node_id,
"<svg viewBox=\"0 0 10 10\"><title>Dense thumbnail omitted for performance</title><line x1=\"0\" y1=\"10\" x2=\"10\" y2=\"0\" stroke=\"red\" /></svg>".to_string(),
));
}
return;
}
@@ -382,10 +388,7 @@ impl NodeRuntime {
let old_thumbnail_svg = thumbnail_renders.entry(parent_network_node_id).or_default();
if old_thumbnail_svg != &new_thumbnail_svg {
responses.push_back(FrontendMessage::UpdateNodeThumbnail {
id: parent_network_node_id,
value: new_thumbnail_svg.to_svg_string(),
});
responses.push(ExecutionResponseMessage::UpdateFrontendThumbnail(parent_network_node_id, new_thumbnail_svg.to_svg_string()));
*old_thumbnail_svg = new_thumbnail_svg;
}
}