mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Add Table<Color> as a graphical type (#3033)
* Reduce code duplication in bounding box impls on Table * Working Table<Color> rendering in the graph * Implement color and fix other rendering with Vello and polish
This commit is contained in:
@@ -8,11 +8,13 @@ use graph_craft::proto::GraphErrors;
|
||||
use graph_craft::wasm_application_io::EditorPreferences;
|
||||
use graph_craft::{ProtoNodeIdentifier, concrete};
|
||||
use graphene_std::application_io::{ImageTexture, NodeGraphUpdateMessage, NodeGraphUpdateSender, RenderConfig};
|
||||
use graphene_std::bounds::RenderBoundingBox;
|
||||
use graphene_std::memo::IORecord;
|
||||
use graphene_std::renderer::{Render, RenderParams, SvgRender};
|
||||
use graphene_std::renderer::{RenderSvgSegmentList, SvgSegment};
|
||||
use graphene_std::table::{Table, TableRow};
|
||||
use graphene_std::text::FontCache;
|
||||
use graphene_std::transform::RenderQuality;
|
||||
use graphene_std::vector::Vector;
|
||||
use graphene_std::vector::style::ViewMode;
|
||||
use graphene_std::wasm_application_io::{RenderOutputType, WasmApplicationIo, WasmEditorApi};
|
||||
@@ -202,8 +204,6 @@ impl NodeRuntime {
|
||||
});
|
||||
}
|
||||
GraphRuntimeRequest::ExecutionRequest(ExecutionRequest { execution_id, render_config, .. }) => {
|
||||
let transform = render_config.viewport.transform;
|
||||
|
||||
let result = self.execute_network(render_config).await;
|
||||
let mut responses = VecDeque::new();
|
||||
// TODO: Only process monitor nodes if the graph has changed, not when only the Footprint changes
|
||||
@@ -227,7 +227,7 @@ impl NodeRuntime {
|
||||
execution_id,
|
||||
result,
|
||||
responses,
|
||||
transform,
|
||||
footprint: render_config.viewport,
|
||||
vector_modify: self.vector_modify.clone(),
|
||||
inspect_result,
|
||||
});
|
||||
@@ -292,51 +292,49 @@ impl NodeRuntime {
|
||||
if self.inspect_state.is_some_and(|inspect_state| monitor_node_path.last().copied() == Some(inspect_state.monitor_node)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The monitor nodes are located within a document node, and are thus children in that network, so this gets the parent document node's ID
|
||||
let Some(parent_network_node_id) = monitor_node_path.len().checked_sub(2).and_then(|index| monitor_node_path.get(index)).copied() else {
|
||||
warn!("Monitor node has invalid node id");
|
||||
|
||||
continue;
|
||||
};
|
||||
|
||||
// Extract the monitor node's stored `Graphic` data.
|
||||
// Extract the monitor node's stored `Graphic` data
|
||||
let Ok(introspected_data) = self.executor.introspect(monitor_node_path) else {
|
||||
// TODO: Fix the root of the issue causing the spam of this warning (this at least temporarily disables it in release builds)
|
||||
#[cfg(debug_assertions)]
|
||||
warn!("Failed to introspect monitor node {}", self.executor.introspect(monitor_node_path).unwrap_err());
|
||||
|
||||
continue;
|
||||
};
|
||||
|
||||
// Graphic table: thumbnail
|
||||
if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, Table<Graphic>>>() {
|
||||
Self::process_graphic(&mut self.thumbnail_renders, parent_network_node_id, &io.output, responses, update_thumbnails)
|
||||
} else if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, Table<Artboard>>>() {
|
||||
Self::process_graphic(&mut self.thumbnail_renders, parent_network_node_id, &io.output, responses, update_thumbnails)
|
||||
// Insert the vector modify if we are dealing with vector data
|
||||
} else if let Some(record) = introspected_data.downcast_ref::<IORecord<Context, Table<Vector>>>() {
|
||||
if update_thumbnails {
|
||||
Self::render_thumbnail(&mut self.thumbnail_renders, parent_network_node_id, &io.output, responses)
|
||||
}
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
// Vector table: vector modifications
|
||||
else if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, Table<Vector>>>() {
|
||||
// Insert the vector modify
|
||||
let default = TableRow::default();
|
||||
self.vector_modify
|
||||
.insert(parent_network_node_id, record.output.iter().next().unwrap_or_else(|| default.as_ref()).element.clone());
|
||||
} else {
|
||||
.insert(parent_network_node_id, io.output.iter().next().unwrap_or_else(|| default.as_ref()).element.clone());
|
||||
}
|
||||
// Other
|
||||
else {
|
||||
log::warn!("Failed to downcast monitor node output {parent_network_node_id:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If this is `Graphic` data, regenerate click targets and thumbnails for the layers in the graph, modifying the state and updating the UI.
|
||||
fn process_graphic(
|
||||
thumbnail_renders: &mut HashMap<NodeId, Vec<SvgSegment>>,
|
||||
parent_network_node_id: NodeId,
|
||||
graphic: &impl Render,
|
||||
responses: &mut VecDeque<FrontendMessage>,
|
||||
update_thumbnails: bool,
|
||||
) {
|
||||
// RENDER THUMBNAIL
|
||||
|
||||
if !update_thumbnails {
|
||||
return;
|
||||
}
|
||||
|
||||
/// 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>) {
|
||||
// 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());
|
||||
@@ -349,12 +347,21 @@ impl NodeRuntime {
|
||||
return;
|
||||
}
|
||||
|
||||
let bounds = graphic.bounding_box(DAffine2::IDENTITY, true);
|
||||
let bounds = match graphic.bounding_box(DAffine2::IDENTITY, true) {
|
||||
RenderBoundingBox::None => return,
|
||||
RenderBoundingBox::Infinite => [DVec2::ZERO, DVec2::new(300., 200.)],
|
||||
RenderBoundingBox::Rectangle(bounds) => bounds,
|
||||
};
|
||||
let footprint = Footprint {
|
||||
transform: DAffine2::from_translation(DVec2::new(bounds[0].x, bounds[0].y)),
|
||||
resolution: UVec2::new((bounds[1].x - bounds[0].x).abs() as u32, (bounds[1].y - bounds[0].y).abs() as u32),
|
||||
quality: RenderQuality::Full,
|
||||
};
|
||||
|
||||
// Render the thumbnail from a `Graphic` into an SVG string
|
||||
let render_params = RenderParams {
|
||||
view_mode: ViewMode::Normal,
|
||||
culling_bounds: bounds,
|
||||
footprint,
|
||||
thumbnail: true,
|
||||
hide_artboards: false,
|
||||
for_export: false,
|
||||
@@ -365,8 +372,7 @@ impl NodeRuntime {
|
||||
graphic.render_svg(&mut render, &render_params);
|
||||
|
||||
// And give the SVG a viewbox and outer <svg>...</svg> wrapper tag
|
||||
let [min, max] = bounds.unwrap_or_default();
|
||||
render.format_svg(min, max);
|
||||
render.format_svg(bounds[0], bounds[1]);
|
||||
|
||||
// UPDATE FRONTEND THUMBNAIL
|
||||
|
||||
|
||||
Reference in New Issue
Block a user