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:
Dennis Kobert
2025-03-19 01:19:49 -07:00
committed by GitHub
co-authored by Keavon Chambers
parent b98711dbdb
commit 44694ff8d6
31 changed files with 428 additions and 62 deletions
@@ -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,
);