mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-19 10:58:04 +08:00
Experimental animation support (#2443)
* Implement experimental time routing to the node graph * Allow toggling live preview with SHIFT + SPACE * Add animation message handler * Fix hotkeys * Fix milisecond node * Adevertize set frame index action * Fix frame index * Fix year calculation * Add comment for why month and day are not exposed * Combine animation nodes and fix animation time implementation * Fix animation time interaction with playback * Add set animation time mode message * Captalize UTC * Fix compiling * Fix crash and add text nodes --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
@@ -13,6 +13,7 @@ pub struct Dispatcher {
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct DispatcherMessageHandlers {
|
||||
animation_message_handler: AnimationMessageHandler,
|
||||
broadcast_message_handler: BroadcastMessageHandler,
|
||||
debug_message_handler: DebugMessageHandler,
|
||||
dialog_message_handler: DialogMessageHandler,
|
||||
@@ -50,12 +51,9 @@ const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
|
||||
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateDocumentLayerStructure),
|
||||
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::TriggerFontLoad),
|
||||
];
|
||||
const DEBUG_MESSAGE_BLOCK_LIST: &[MessageDiscriminant] = &[
|
||||
MessageDiscriminant::Broadcast(BroadcastMessageDiscriminant::TriggerEvent(BroadcastEventDiscriminant::AnimationFrame)),
|
||||
MessageDiscriminant::InputPreprocessor(InputPreprocessorMessageDiscriminant::FrameTimeAdvance),
|
||||
];
|
||||
const DEBUG_MESSAGE_BLOCK_LIST: &[MessageDiscriminant] = &[MessageDiscriminant::Broadcast(BroadcastMessageDiscriminant::TriggerEvent(BroadcastEventDiscriminant::AnimationFrame))];
|
||||
// TODO: Find a way to combine these with the list above. We use strings for now since these are the standard variant names used by multiple messages. But having these also type-checked would be best.
|
||||
const DEBUG_MESSAGE_ENDING_BLOCK_LIST: &[&str] = &["PointerMove", "PointerOutsideViewport", "Overlays", "Draw"];
|
||||
const DEBUG_MESSAGE_ENDING_BLOCK_LIST: &[&str] = &["PointerMove", "PointerOutsideViewport", "Overlays", "Draw", "CurrentTime", "Time"];
|
||||
|
||||
impl Dispatcher {
|
||||
pub fn new() -> Self {
|
||||
@@ -177,6 +175,9 @@ impl Dispatcher {
|
||||
// Finish loading persistent data from the browser database
|
||||
queue.add(FrontendMessage::TriggerLoadRestAutoSaveDocuments);
|
||||
}
|
||||
Message::Animation(message) => {
|
||||
self.message_handlers.animation_message_handler.process_message(message, &mut queue, ());
|
||||
}
|
||||
Message::Batched(messages) => {
|
||||
messages.iter().for_each(|message| self.handle_message(message.to_owned(), false));
|
||||
}
|
||||
@@ -232,6 +233,7 @@ impl Dispatcher {
|
||||
let preferences = &self.message_handlers.preferences_message_handler;
|
||||
let current_tool = &self.message_handlers.tool_message_handler.tool_state.tool_data.active_tool_type;
|
||||
let message_logging_verbosity = self.message_handlers.debug_message_handler.message_logging_verbosity;
|
||||
let timing_information = self.message_handlers.animation_message_handler.timing_information();
|
||||
|
||||
self.message_handlers.portfolio_message_handler.process_message(
|
||||
message,
|
||||
@@ -241,6 +243,7 @@ impl Dispatcher {
|
||||
preferences,
|
||||
current_tool,
|
||||
message_logging_verbosity,
|
||||
timing_information,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -283,6 +286,7 @@ impl Dispatcher {
|
||||
// TODO: Reduce the number of heap allocations
|
||||
let mut list = Vec::new();
|
||||
list.extend(self.message_handlers.dialog_message_handler.actions());
|
||||
list.extend(self.message_handlers.animation_message_handler.actions());
|
||||
list.extend(self.message_handlers.input_preprocessor_message_handler.actions());
|
||||
list.extend(self.message_handlers.key_mapping_message_handler.actions());
|
||||
list.extend(self.message_handlers.debug_message_handler.actions());
|
||||
|
||||
17
editor/src/messages/animation/animation_message.rs
Normal file
17
editor/src/messages/animation/animation_message.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use super::animation_message_handler::AnimationTimeMode;
|
||||
|
||||
#[impl_message(Message, Animation)]
|
||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum AnimationMessage {
|
||||
ToggleLivePreview,
|
||||
EnableLivePreview,
|
||||
DisableLivePreview,
|
||||
ResetAnimation,
|
||||
SetFrameIndex(f64),
|
||||
SetTime(f64),
|
||||
UpdateTime,
|
||||
IncrementFrameCounter,
|
||||
SetAnimationTimeMode(AnimationTimeMode),
|
||||
}
|
||||
84
editor/src/messages/animation/animation_message_handler.rs
Normal file
84
editor/src/messages/animation/animation_message_handler.rs
Normal file
@@ -0,0 +1,84 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use super::TimingInformation;
|
||||
|
||||
#[derive(PartialEq, Clone, Default, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum AnimationTimeMode {
|
||||
#[default]
|
||||
TimeBased,
|
||||
FrameBased,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct AnimationMessageHandler {
|
||||
live_preview: bool,
|
||||
timestamp: f64,
|
||||
frame_index: f64,
|
||||
animation_start: Option<f64>,
|
||||
fps: f64,
|
||||
animation_time_mode: AnimationTimeMode,
|
||||
}
|
||||
impl AnimationMessageHandler {
|
||||
pub(crate) fn timing_information(&self) -> TimingInformation {
|
||||
let animation_time = self.timestamp - self.animation_start.unwrap_or(self.timestamp);
|
||||
let animation_time = match self.animation_time_mode {
|
||||
AnimationTimeMode::TimeBased => Duration::from_millis(animation_time as u64),
|
||||
AnimationTimeMode::FrameBased => Duration::from_secs((self.frame_index / self.fps) as u64),
|
||||
};
|
||||
TimingInformation { time: self.timestamp, animation_time }
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageHandler<AnimationMessage, ()> for AnimationMessageHandler {
|
||||
fn process_message(&mut self, message: AnimationMessage, responses: &mut VecDeque<Message>, _data: ()) {
|
||||
match message {
|
||||
AnimationMessage::ToggleLivePreview => {
|
||||
if self.animation_start.is_none() {
|
||||
self.animation_start = Some(self.timestamp);
|
||||
}
|
||||
self.live_preview = !self.live_preview
|
||||
}
|
||||
AnimationMessage::EnableLivePreview => {
|
||||
if self.animation_start.is_none() {
|
||||
self.animation_start = Some(self.timestamp);
|
||||
}
|
||||
self.live_preview = true
|
||||
}
|
||||
AnimationMessage::DisableLivePreview => self.live_preview = false,
|
||||
AnimationMessage::SetFrameIndex(frame) => {
|
||||
self.frame_index = frame;
|
||||
log::debug!("set frame index to {}", frame);
|
||||
responses.add(PortfolioMessage::SubmitActiveGraphRender)
|
||||
}
|
||||
AnimationMessage::SetTime(time) => {
|
||||
self.timestamp = time;
|
||||
responses.add(AnimationMessage::UpdateTime);
|
||||
}
|
||||
AnimationMessage::IncrementFrameCounter => {
|
||||
if self.live_preview {
|
||||
self.frame_index += 1.;
|
||||
responses.add(AnimationMessage::UpdateTime);
|
||||
}
|
||||
}
|
||||
AnimationMessage::UpdateTime => {
|
||||
if self.live_preview {
|
||||
responses.add(PortfolioMessage::SubmitActiveGraphRender)
|
||||
}
|
||||
}
|
||||
AnimationMessage::ResetAnimation => {
|
||||
self.frame_index = 0.;
|
||||
self.animation_start = None;
|
||||
responses.add(PortfolioMessage::SubmitActiveGraphRender)
|
||||
}
|
||||
AnimationMessage::SetAnimationTimeMode(animation_time_mode) => self.animation_time_mode = animation_time_mode,
|
||||
}
|
||||
}
|
||||
|
||||
advertise_actions!(AnimationMessageDiscriminant;
|
||||
ToggleLivePreview,
|
||||
SetFrameIndex,
|
||||
ResetAnimation,
|
||||
);
|
||||
}
|
||||
9
editor/src/messages/animation/mod.rs
Normal file
9
editor/src/messages/animation/mod.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
mod animation_message;
|
||||
mod animation_message_handler;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use animation_message::{AnimationMessage, AnimationMessageDiscriminant};
|
||||
#[doc(inline)]
|
||||
pub use animation_message_handler::AnimationMessageHandler;
|
||||
|
||||
pub use graphene_core::application_io::TimingInformation;
|
||||
@@ -430,6 +430,9 @@ pub fn input_mappings() -> Mapping {
|
||||
entry!(KeyDown(Digit0); modifiers=[Alt], action_dispatch=DebugMessage::MessageOff),
|
||||
entry!(KeyDown(Digit1); modifiers=[Alt], action_dispatch=DebugMessage::MessageNames),
|
||||
entry!(KeyDown(Digit2); modifiers=[Alt], action_dispatch=DebugMessage::MessageContents),
|
||||
// AnimationMessage
|
||||
entry!(KeyDown(Space); modifiers=[Shift], action_dispatch=AnimationMessage::ToggleLivePreview),
|
||||
entry!(KeyDown(ArrowLeft); modifiers=[Control], action_dispatch=AnimationMessage::ResetAnimation),
|
||||
];
|
||||
let (mut key_up, mut key_down, mut key_up_no_repeat, mut key_down_no_repeat, mut double_click, mut wheel_scroll, mut pointer_move) = mappings;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, ModifierKeys};
|
||||
use crate::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, ViewportBounds};
|
||||
use crate::messages::prelude::*;
|
||||
use core::time::Duration;
|
||||
|
||||
#[impl_message(Message, InputPreprocessor)]
|
||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
@@ -13,6 +12,6 @@ pub enum InputPreprocessorMessage {
|
||||
PointerDown { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys },
|
||||
PointerMove { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys },
|
||||
PointerUp { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys },
|
||||
FrameTimeAdvance { timestamp: Duration },
|
||||
CurrentTime { timestamp: u64 },
|
||||
WheelScroll { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys },
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ pub struct InputPreprocessorMessageData {
|
||||
#[derive(Debug, Default)]
|
||||
pub struct InputPreprocessorMessageHandler {
|
||||
pub frame_time: FrameTimeInfo,
|
||||
pub time: u64,
|
||||
pub keyboard: KeyStates,
|
||||
pub mouse: MouseState,
|
||||
pub viewport_bounds: ViewportBounds,
|
||||
@@ -93,8 +94,9 @@ impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageData> for
|
||||
|
||||
self.translate_mouse_event(mouse_state, false, responses);
|
||||
}
|
||||
InputPreprocessorMessage::FrameTimeAdvance { timestamp } => {
|
||||
self.frame_time.advance_timestamp(timestamp);
|
||||
InputPreprocessorMessage::CurrentTime { timestamp } => {
|
||||
responses.add(AnimationMessage::SetTime(timestamp as f64));
|
||||
self.time = timestamp;
|
||||
}
|
||||
InputPreprocessorMessage::WheelScroll { editor_mouse_state, modifier_keys } => {
|
||||
self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
|
||||
|
||||
@@ -10,6 +10,8 @@ pub enum Message {
|
||||
StartBuffer,
|
||||
EndBuffer(graphene_std::renderer::RenderMetadata),
|
||||
|
||||
#[child]
|
||||
Animation(AnimationMessage),
|
||||
#[child]
|
||||
Broadcast(BroadcastMessage),
|
||||
#[child]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! The root-level messages forming the first layer of the message system architecture.
|
||||
|
||||
pub mod animation;
|
||||
pub mod broadcast;
|
||||
pub mod debug;
|
||||
pub mod dialog;
|
||||
|
||||
@@ -2086,7 +2086,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
identifier: "Text",
|
||||
category: "Vector",
|
||||
category: "Text",
|
||||
node_template: NodeTemplate {
|
||||
document_node: DocumentNode {
|
||||
implementation: DocumentNodeImplementation::proto("graphene_std::text::TextNode"),
|
||||
|
||||
@@ -20,6 +20,7 @@ use graphene_core::raster::{
|
||||
use graphene_core::text::Font;
|
||||
use graphene_core::vector::misc::CentroidType;
|
||||
use graphene_core::vector::style::{GradientType, LineCap, LineJoin};
|
||||
use graphene_std::animation::RealTimeMode;
|
||||
use graphene_std::application_io::TextureFrameTable;
|
||||
use graphene_std::transform::Footprint;
|
||||
use graphene_std::vector::VectorDataTable;
|
||||
@@ -165,6 +166,7 @@ pub(crate) fn property_from_type(
|
||||
last.clone()
|
||||
}
|
||||
Some(x) if x == TypeId::of::<BlendMode>() => blend_mode(document_node, node_id, index, name, true),
|
||||
Some(x) if x == TypeId::of::<RealTimeMode>() => real_time_mode(document_node, node_id, index, name, true),
|
||||
Some(x) if x == TypeId::of::<RedGreenBlue>() => color_channel(document_node, node_id, index, name, true),
|
||||
Some(x) if x == TypeId::of::<RedGreenBlueAlpha>() => rgba_channel(document_node, node_id, index, name, true),
|
||||
Some(x) if x == TypeId::of::<NoiseType>() => noise_type(document_node, node_id, index, name, true),
|
||||
@@ -778,6 +780,40 @@ pub fn color_channel(document_node: &DocumentNode, node_id: NodeId, index: usize
|
||||
LayoutGroup::Row { widgets }.with_tooltip("Color Channel")
|
||||
}
|
||||
|
||||
pub fn real_time_mode(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||
let Some(input) = document_node.inputs.get(index) else {
|
||||
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||
return LayoutGroup::Row { widgets: vec![] };
|
||||
};
|
||||
if let Some(&TaggedValue::RealTimeMode(mode)) = input.as_non_exposed_value() {
|
||||
let calculation_modes = [
|
||||
RealTimeMode::Utc,
|
||||
RealTimeMode::Year,
|
||||
RealTimeMode::Hour,
|
||||
RealTimeMode::Minute,
|
||||
RealTimeMode::Second,
|
||||
RealTimeMode::Millisecond,
|
||||
];
|
||||
let mut entries = Vec::with_capacity(calculation_modes.len());
|
||||
for method in calculation_modes {
|
||||
entries.push(
|
||||
MenuListEntry::new(format!("{method:?}"))
|
||||
.label(method.to_string())
|
||||
.on_update(update_value(move |_| TaggedValue::RealTimeMode(method), node_id, index))
|
||||
.on_commit(commit_value),
|
||||
);
|
||||
}
|
||||
let entries = vec![entries];
|
||||
|
||||
widgets.extend_from_slice(&[
|
||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
DropdownInput::new(entries).selected_index(Some(mode as u32)).widget_holder(),
|
||||
]);
|
||||
}
|
||||
LayoutGroup::Row { widgets }.with_tooltip("Real Time Mode")
|
||||
}
|
||||
|
||||
pub fn rgba_channel(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||
let Some(input) = document_node.inputs.get(index) else {
|
||||
|
||||
@@ -115,6 +115,7 @@ pub enum PortfolioMessage {
|
||||
bounds: ExportBounds,
|
||||
transparent_background: bool,
|
||||
},
|
||||
SubmitActiveGraphRender,
|
||||
SubmitGraphRender {
|
||||
document_id: DocumentId,
|
||||
ignore_hash: bool,
|
||||
|
||||
@@ -4,6 +4,7 @@ use super::spreadsheet::SpreadsheetMessageHandler;
|
||||
use super::utility_types::{PanelType, PersistentData};
|
||||
use crate::application::generate_uuid;
|
||||
use crate::consts::DEFAULT_DOCUMENT_NAME;
|
||||
use crate::messages::animation::TimingInformation;
|
||||
use crate::messages::debug::utility_types::MessageLoggingVerbosity;
|
||||
use crate::messages::dialog::simple_dialogs;
|
||||
use crate::messages::frontend::utility_types::FrontendDocumentDetails;
|
||||
@@ -32,6 +33,7 @@ pub struct PortfolioMessageData<'a> {
|
||||
pub preferences: &'a PreferencesMessageHandler,
|
||||
pub current_tool: &'a ToolType,
|
||||
pub message_logging_verbosity: MessageLoggingVerbosity,
|
||||
pub timing_information: TimingInformation,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@@ -56,6 +58,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
||||
preferences,
|
||||
current_tool,
|
||||
message_logging_verbosity,
|
||||
timing_information,
|
||||
} = data;
|
||||
|
||||
match message {
|
||||
@@ -306,6 +309,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
||||
let _ = self.executor.submit_node_graph_evaluation(
|
||||
self.documents.get_mut(document_id).expect("Tried to render non-existent document"),
|
||||
ipp.viewport_bounds.size().as_uvec2(),
|
||||
timing_information,
|
||||
inspect_node,
|
||||
true,
|
||||
);
|
||||
@@ -1072,11 +1076,17 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
||||
});
|
||||
}
|
||||
}
|
||||
PortfolioMessage::SubmitActiveGraphRender => {
|
||||
if let Some(document_id) = self.active_document_id {
|
||||
responses.add(PortfolioMessage::SubmitGraphRender { document_id, ignore_hash: false });
|
||||
}
|
||||
}
|
||||
PortfolioMessage::SubmitGraphRender { document_id, ignore_hash } => {
|
||||
let inspect_node = self.inspect_node_id();
|
||||
let result = self.executor.submit_node_graph_evaluation(
|
||||
self.documents.get_mut(&document_id).expect("Tried to render non-existent document"),
|
||||
ipp.viewport_bounds.size().as_uvec2(),
|
||||
timing_information,
|
||||
inspect_node,
|
||||
ignore_hash,
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
pub use crate::utility_traits::{ActionList, AsMessage, MessageHandler, ToDiscriminant, TransitiveChild};
|
||||
|
||||
// Message, MessageData, MessageDiscriminant, MessageHandler
|
||||
pub use crate::messages::animation::{AnimationMessage, AnimationMessageDiscriminant, AnimationMessageHandler};
|
||||
pub use crate::messages::broadcast::{BroadcastMessage, BroadcastMessageDiscriminant, BroadcastMessageHandler};
|
||||
pub use crate::messages::debug::{DebugMessage, DebugMessageDiscriminant, DebugMessageHandler};
|
||||
pub use crate::messages::dialog::export_dialog::{ExportDialogMessage, ExportDialogMessageData, ExportDialogMessageDiscriminant, ExportDialogMessageHandler};
|
||||
|
||||
@@ -57,7 +57,9 @@ where
|
||||
|
||||
/// Calculates the bounding box of the layer's text, based on the settings for max width and height specified in the typesetting config.
|
||||
pub fn text_bounding_box(layer: LayerNodeIdentifier, document: &DocumentMessageHandler, font_cache: &FontCache) -> Quad {
|
||||
let (text, font, typesetting) = get_text(layer, &document.network_interface).expect("Text layer should have text when interacting with the Text tool");
|
||||
let Some((text, font, typesetting)) = get_text(layer, &document.network_interface) else {
|
||||
return Quad::from_box([DVec2::ZERO, DVec2::ZERO]);
|
||||
};
|
||||
|
||||
let buzz_face = font_cache.get(font).map(|data| load_face(data));
|
||||
let far = graphene_core::text::bounding_box(text, buzz_face.as_ref(), typesetting, false);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::consts::FILE_SAVE_SUFFIX;
|
||||
use crate::messages::animation::TimingInformation;
|
||||
use crate::messages::frontend::utility_types::{ExportBounds, FileType};
|
||||
use crate::messages::prelude::*;
|
||||
use glam::{DAffine2, DVec2, UVec2};
|
||||
@@ -572,13 +573,14 @@ impl NodeGraphExecutor {
|
||||
}
|
||||
|
||||
/// Adds an evaluate request for whatever current network is cached.
|
||||
pub(crate) fn submit_current_node_graph_evaluation(&mut self, document: &mut DocumentMessageHandler, viewport_resolution: UVec2) -> Result<(), String> {
|
||||
pub(crate) fn submit_current_node_graph_evaluation(&mut self, document: &mut DocumentMessageHandler, viewport_resolution: UVec2, time: TimingInformation) -> Result<(), String> {
|
||||
let render_config = RenderConfig {
|
||||
viewport: Footprint {
|
||||
transform: document.metadata().document_to_viewport,
|
||||
resolution: viewport_resolution,
|
||||
..Default::default()
|
||||
},
|
||||
time,
|
||||
#[cfg(any(feature = "resvg", feature = "vello"))]
|
||||
export_format: graphene_core::application_io::ExportFormat::Canvas,
|
||||
#[cfg(not(any(feature = "resvg", feature = "vello")))]
|
||||
@@ -596,9 +598,16 @@ impl NodeGraphExecutor {
|
||||
}
|
||||
|
||||
/// Evaluates a node graph, computing the entire graph
|
||||
pub fn submit_node_graph_evaluation(&mut self, document: &mut DocumentMessageHandler, viewport_resolution: UVec2, inspect_node: Option<NodeId>, ignore_hash: bool) -> Result<(), String> {
|
||||
pub fn submit_node_graph_evaluation(
|
||||
&mut self,
|
||||
document: &mut DocumentMessageHandler,
|
||||
viewport_resolution: UVec2,
|
||||
time: TimingInformation,
|
||||
inspect_node: Option<NodeId>,
|
||||
ignore_hash: bool,
|
||||
) -> Result<(), String> {
|
||||
self.update_node_graph(document, inspect_node, ignore_hash)?;
|
||||
self.submit_current_node_graph_evaluation(document, viewport_resolution)?;
|
||||
self.submit_current_node_graph_evaluation(document, viewport_resolution, time)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -623,6 +632,7 @@ impl NodeGraphExecutor {
|
||||
resolution: (size * export_config.scale_factor).as_uvec2(),
|
||||
..Default::default()
|
||||
},
|
||||
time: Default::default(),
|
||||
export_format: graphene_core::application_io::ExportFormat::Svg,
|
||||
view_mode: document.view_mode,
|
||||
hide_artboards: export_config.transparent_background,
|
||||
|
||||
@@ -46,7 +46,7 @@ impl EditorTestUtils {
|
||||
|
||||
let viewport_resolution = glam::UVec2::ONE;
|
||||
exector
|
||||
.submit_current_node_graph_evaluation(document, viewport_resolution)
|
||||
.submit_current_node_graph_evaluation(document, viewport_resolution, Default::default())
|
||||
.expect("submit_current_node_graph_evaluation failed");
|
||||
runtime.run().await;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user