From c2815ea0e33604504842b7831f0f31b7fc64c23e Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 16 Jul 2025 18:36:22 -0700 Subject: [PATCH] fix animation --- editor/src/dispatcher.rs | 60 +------- .../messages/animation/animation_message.rs | 17 --- .../animation/animation_message_handler.rs | 134 ------------------ editor/src/messages/animation/mod.rs | 9 -- .../messages/input_mapper/input_mappings.rs | 4 +- .../input_preprocessor_message.rs | 2 +- .../input_preprocessor_message_handler.rs | 2 +- editor/src/messages/message.rs | 9 -- editor/src/messages/mod.rs | 1 - .../portfolio/document/document_message.rs | 2 + .../document/document_message_handler.rs | 88 ++++++++---- .../node_graph/document_node_definitions.rs | 2 - .../node_graph/node_graph_message_handler.rs | 4 +- .../document/properties_panel/mod.rs | 2 +- .../properties_panel_message_handler.rs | 8 +- .../utility_types/network_interface.rs | 2 +- .../messages/portfolio/portfolio_message.rs | 5 +- .../portfolio/portfolio_message_handler.rs | 42 +----- editor/src/messages/prelude.rs | 1 - .../messages/tool/tool_messages/path_tool.rs | 2 +- .../transform_layer_message_handler.rs | 132 ++++++++++++++++- editor/src/node_graph_executor.rs | 21 --- frontend/wasm/src/editor_api.rs | 10 +- node-graph/gcore/src/context.rs | 42 +++--- .../gcore/src/vector/algorithms/instance.rs | 8 +- node-graph/gcore/src/vector/vector_nodes.rs | 23 +-- node-graph/graph-craft/src/document/value.rs | 2 +- node-graph/gstd/src/any.rs | 1 + node-graph/gstd/src/text.rs | 2 +- node-graph/gsvg-renderer/src/renderer.rs | 7 +- 30 files changed, 258 insertions(+), 386 deletions(-) delete mode 100644 editor/src/messages/animation/animation_message.rs delete mode 100644 editor/src/messages/animation/animation_message_handler.rs delete mode 100644 editor/src/messages/animation/mod.rs diff --git a/editor/src/dispatcher.rs b/editor/src/dispatcher.rs index 9ff407843c..871803f8c1 100644 --- a/editor/src/dispatcher.rs +++ b/editor/src/dispatcher.rs @@ -16,7 +16,6 @@ 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, @@ -59,7 +58,7 @@ 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))]; +const DEBUG_MESSAGE_BLOCK_LIST: &[MessageDiscriminant] = &[MessageDiscriminant::InputPreprocessor(InputPreprocessorMessageDiscriminant::CurrentTime)]; // 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", "CurrentTime", "Time"]; @@ -163,27 +162,6 @@ impl Dispatcher { let clear_message = PortfolioMessage::ClearIntrospectedData.into(); Self::schedule_execution(&mut self.message_queues, true, [clear_message]); } - Message::NoOp => {} - Message::Init => { - // Load persistent data from the browser database - queue.add(FrontendMessage::TriggerLoadFirstAutoSaveDocument); - queue.add(FrontendMessage::TriggerLoadPreferences); - - // Display the menu bar at the top of the window - queue.add(MenuBarMessage::SendLayout); - - // Send the information for tooltips and categories for each node/input. - queue.add(FrontendMessage::SendUIMetadata { - node_descriptions: document_node_definitions::collect_node_descriptions(), - node_types: document_node_definitions::collect_node_types(), - }); - - // 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::Broadcast(message) => self.message_handlers.broadcast_message_handler.process_message(message, &mut queue, ()), Message::Debug(message) => { self.message_handlers.debug_message_handler.process_message(message, &mut queue, ()); @@ -238,8 +216,6 @@ impl Dispatcher { 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 reset_node_definitions_on_open = self.message_handlers.portfolio_message_handler.reset_node_definitions_on_open; - let timing_information = self.message_handlers.animation_message_handler.timing_information(); - let animation = &self.message_handlers.animation_message_handler; self.message_handlers.portfolio_message_handler.process_message( message, @@ -250,8 +226,6 @@ impl Dispatcher { current_tool, message_logging_verbosity, reset_node_definitions_on_open, - timing_information, - animation, }, ); } @@ -283,37 +257,6 @@ impl Dispatcher { Message::Batched { messages } => { messages.iter().for_each(|message| self.handle_message(message.to_owned(), false)); } - Message::StartBuffer => { - self.buffered_queue = Some(std::mem::take(&mut self.message_queues)); - } - Message::EndBuffer { render_metadata } => { - // Assign the message queue to the currently buffered queue - if let Some(buffered_queue) = self.buffered_queue.take() { - self.cleanup_queues(false); - assert!(self.message_queues.is_empty(), "message queues are always empty when ending a buffer"); - self.message_queues = buffered_queue; - }; - - let graphene_std::renderer::RenderMetadata { - upstream_footprints: footprints, - local_transforms, - first_instance_source_id, - click_targets, - clip_targets, - } = render_metadata; - - // Run these update state messages immediately - let messages = [ - DocumentMessage::UpdateUpstreamTransforms { - upstream_footprints: footprints, - local_transforms, - first_instance_source_id, - }, - DocumentMessage::UpdateClickTargets { click_targets }, - DocumentMessage::UpdateClipTargets { clip_targets }, - ]; - Self::schedule_execution(&mut self.message_queues, false, messages.map(Message::from)); - } } // If there are child messages, append the queue to the list of queues @@ -329,7 +272,6 @@ 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()); diff --git a/editor/src/messages/animation/animation_message.rs b/editor/src/messages/animation/animation_message.rs deleted file mode 100644 index 128cc70ea8..0000000000 --- a/editor/src/messages/animation/animation_message.rs +++ /dev/null @@ -1,17 +0,0 @@ -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, - RestartAnimation, - SetFrameIndex { frame: f64 }, - SetTime { time: f64 }, - UpdateTime, - IncrementFrameCounter, - SetAnimationTimeMode { animation_time_mode: AnimationTimeMode }, -} diff --git a/editor/src/messages/animation/animation_message_handler.rs b/editor/src/messages/animation/animation_message_handler.rs deleted file mode 100644 index fe4eed5dd9..0000000000 --- a/editor/src/messages/animation/animation_message_handler.rs +++ /dev/null @@ -1,134 +0,0 @@ -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(Default, Debug, Clone, PartialEq)] -enum AnimationState { - #[default] - Stopped, - Playing { - start: f64, - }, - Paused { - start: f64, - pause_time: f64, - }, -} - -#[derive(Default, Debug, Clone, PartialEq, ExtractField)] -pub struct AnimationMessageHandler { - /// Used to re-send the UI on the next frame after playback starts - live_preview_recently_zero: bool, - timestamp: f64, - frame_index: f64, - animation_state: AnimationState, - fps: f64, - animation_time_mode: AnimationTimeMode, -} -impl AnimationMessageHandler { - pub(crate) fn timing_information(&self) -> TimingInformation { - let animation_time = self.timestamp - self.animation_start(); - 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 } - } - - pub(crate) fn animation_start(&self) -> f64 { - match self.animation_state { - AnimationState::Stopped => self.timestamp, - AnimationState::Playing { start } => start, - AnimationState::Paused { start, pause_time } => start + self.timestamp - pause_time, - } - } - - pub fn is_playing(&self) -> bool { - matches!(self.animation_state, AnimationState::Playing { .. }) - } -} - -#[message_handler_data] -impl MessageHandler for AnimationMessageHandler { - fn process_message(&mut self, message: AnimationMessage, responses: &mut VecDeque, _: ()) { - match message { - AnimationMessage::ToggleLivePreview => match self.animation_state { - AnimationState::Stopped => responses.add(AnimationMessage::EnableLivePreview), - AnimationState::Playing { .. } => responses.add(AnimationMessage::DisableLivePreview), - AnimationState::Paused { .. } => responses.add(AnimationMessage::EnableLivePreview), - }, - AnimationMessage::EnableLivePreview => { - self.animation_state = AnimationState::Playing { start: self.animation_start() }; - - // Update the restart and pause/play buttons - responses.add(PortfolioMessage::UpdateDocumentWidgets); - } - AnimationMessage::DisableLivePreview => { - match self.animation_state { - AnimationState::Stopped => (), - AnimationState::Playing { start } => self.animation_state = AnimationState::Paused { start, pause_time: self.timestamp }, - AnimationState::Paused { .. } => (), - } - - // Update the restart and pause/play buttons - responses.add(PortfolioMessage::UpdateDocumentWidgets); - } - AnimationMessage::SetFrameIndex { frame } => { - self.frame_index = frame; - responses.add(PortfolioMessage::EvaluateActiveDocumentWithThumbnails); - // Update the restart and pause/play buttons - responses.add(PortfolioMessage::UpdateDocumentWidgets); - } - AnimationMessage::SetTime { time } => { - self.timestamp = time; - responses.add(AnimationMessage::UpdateTime); - } - AnimationMessage::IncrementFrameCounter => { - if self.is_playing() { - self.frame_index += 1.; - responses.add(AnimationMessage::UpdateTime); - } - } - AnimationMessage::UpdateTime => { - if self.is_playing() { - responses.add(PortfolioMessage::EvaluateActiveDocumentWithThumbnails); - - if self.live_preview_recently_zero { - // Update the restart and pause/play buttons - responses.add(PortfolioMessage::UpdateDocumentWidgets); - self.live_preview_recently_zero = false; - } - } - } - AnimationMessage::RestartAnimation => { - self.frame_index = 0.; - self.animation_state = match self.animation_state { - AnimationState::Playing { .. } => AnimationState::Playing { start: self.timestamp }, - _ => AnimationState::Stopped, - }; - self.live_preview_recently_zero = true; - responses.add(PortfolioMessage::EvaluateActiveDocumentWithThumbnails); - // Update the restart and pause/play buttons - responses.add(PortfolioMessage::UpdateDocumentWidgets); - } - AnimationMessage::SetAnimationTimeMode { animation_time_mode } => { - self.animation_time_mode = animation_time_mode; - } - } - } - - advertise_actions!(AnimationMessageDiscriminant; - ToggleLivePreview, - SetFrameIndex, - RestartAnimation, - ); -} diff --git a/editor/src/messages/animation/mod.rs b/editor/src/messages/animation/mod.rs deleted file mode 100644 index c92c9de5d6..0000000000 --- a/editor/src/messages/animation/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -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_std::application_io::TimingInformation; diff --git a/editor/src/messages/input_mapper/input_mappings.rs b/editor/src/messages/input_mapper/input_mappings.rs index 02dafca170..27acbcfb17 100644 --- a/editor/src/messages/input_mapper/input_mappings.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -437,8 +437,8 @@ pub fn input_mappings() -> Mapping { 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(Home); modifiers=[Shift], action_dispatch=AnimationMessage::RestartAnimation), + entry!(KeyDown(Space); modifiers=[Shift], action_dispatch=DocumentMessage::ToggleAnimation), + entry!(KeyDown(Home); modifiers=[Shift], action_dispatch=DocumentMessage::RestartAnimation), ]; 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; diff --git a/editor/src/messages/input_preprocessor/input_preprocessor_message.rs b/editor/src/messages/input_preprocessor/input_preprocessor_message.rs index 53347542fc..6bbd173f48 100644 --- a/editor/src/messages/input_preprocessor/input_preprocessor_message.rs +++ b/editor/src/messages/input_preprocessor/input_preprocessor_message.rs @@ -12,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 }, - CurrentTime { timestamp: u64 }, + CurrentTime { timestamp: f64 }, WheelScroll { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys }, } diff --git a/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs b/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs index 6b1ecfb808..215da75332 100644 --- a/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs +++ b/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs @@ -97,7 +97,7 @@ impl MessageHandler f self.translate_mouse_event(mouse_state, false, responses); } InputPreprocessorMessage::CurrentTime { timestamp } => { - self.time = timestamp as f64; + self.time = timestamp; } InputPreprocessorMessage::WheelScroll { editor_mouse_state, modifier_keys } => { self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses); diff --git a/editor/src/messages/message.rs b/editor/src/messages/message.rs index 761bec7d4e..52b644b289 100644 --- a/editor/src/messages/message.rs +++ b/editor/src/messages/message.rs @@ -4,9 +4,6 @@ use graphite_proc_macros::*; #[impl_message] #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] pub enum Message { - NoOp, - Init, - Batched(Box<[Message]>), // Adds any subsequent messages to the queue StartEvaluationQueue, // Stop adding messages to the queue. @@ -15,8 +12,6 @@ pub enum Message { #[serde(skip)] ProcessEvaluationQueue(graphene_std::renderer::RenderMetadata, IntrospectionResponse), #[child] - Animation(AnimationMessage), - #[child] Broadcast(BroadcastMessage), #[child] Debug(DebugMessage), @@ -46,10 +41,6 @@ pub enum Message { Batched { messages: Box<[Message]>, }, - StartBuffer, - EndBuffer { - render_metadata: RenderMetadata, - }, } /// Provides an impl of `specta::Type` for `MessageDiscriminant`, the struct created by `impl_message`. diff --git a/editor/src/messages/mod.rs b/editor/src/messages/mod.rs index 7b43a40108..c6f3c23d24 100644 --- a/editor/src/messages/mod.rs +++ b/editor/src/messages/mod.rs @@ -1,6 +1,5 @@ //! 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; diff --git a/editor/src/messages/portfolio/document/document_message.rs b/editor/src/messages/portfolio/document/document_message.rs index dcf16f2075..d52961ddfe 100644 --- a/editor/src/messages/portfolio/document/document_message.rs +++ b/editor/src/messages/portfolio/document/document_message.rs @@ -170,6 +170,8 @@ pub enum DocumentMessage { RepeatedAbortTransaction { undo_count: usize, }, + ToggleAnimation, + RestartAnimation, ToggleLayerExpansion { id: NodeId, recursive: bool, diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 446fd65ee5..66a4da3a79 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -35,17 +35,13 @@ use graphene_std::uuid::NodeId; use graphene_std::vector::PointId; use graphene_std::vector::click_target::{ClickTarget, ClickTargetType}; use graphene_std::vector::style::ViewMode; -use std::time::Duration; #[derive(ExtractField)] -pub struct DocumentMessageData<'a> { +pub struct DocumentMessageContext<'a> { pub ipp: &'a InputPreprocessorMessageHandler, - pub persistent_data: &'a PersistentData, pub current_tool: &'a ToolType, pub preferences: &'a PreferencesMessageHandler, pub device_pixel_ratio: f64, - // pub introspected_inputs: &HashMap>, - // pub downcasted_inputs: &mut HashMap, } #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ExtractField)] @@ -101,6 +97,9 @@ pub struct DocumentMessageHandler { // Fields omitted from the saved document format // ============================================= // + /// Animation state for when the animation button was pressed/paused + #[serde(skip)] + pub animation_state: AnimationState, /// Path to network currently viewed in the node graph overlay. This will eventually be stored in each panel, so that multiple panels can refer to different networks #[serde(skip)] pub breadcrumb_network_path: Vec, @@ -164,16 +163,16 @@ impl Default for DocumentMessageHandler { auto_saved_hash: None, layer_range_selection_reference: None, is_loaded: false, + animation_state: AnimationState::Stopped, } } } #[message_handler_data] -impl MessageHandler> for DocumentMessageHandler { - fn process_message(&mut self, message: DocumentMessage, responses: &mut VecDeque, data: DocumentMessageData) { - let DocumentMessageData { +impl MessageHandler> for DocumentMessageHandler { + fn process_message(&mut self, message: DocumentMessage, responses: &mut VecDeque, data: DocumentMessageContext) { + let DocumentMessageContext { ipp, - persistent_data, current_tool, preferences, device_pixel_ratio, @@ -217,7 +216,7 @@ impl MessageHandler> for DocumentMessag ); } DocumentMessage::PropertiesPanel(message) => { - let properties_panel_message_handler_data = super::properties_panel::PropertiesPanelMessageHandlerData { + let context = super::properties_panel::PropertiesPanelMessageContext { network_interface: &mut self.network_interface, selection_network_path: &self.selection_network_path, document_name: self.name.as_str(), @@ -1284,6 +1283,27 @@ impl MessageHandler> for DocumentMessag responses.add(NodeGraphMessage::SendGraph); } + DocumentMessage::ToggleAnimation => { + match self.animation_state { + AnimationState::Stopped => { + self.animation_state = AnimationState::Playing { start: ipp.time }; + responses.add(PortfolioMessage::EvaluateActiveDocumentWithThumbnails); + } + AnimationState::Playing { start } => self.animation_state = AnimationState::Paused { start, pause_time: ipp.time }, + AnimationState::Paused { start, .. } => { + self.animation_state = AnimationState::Playing { start }; + responses.add(PortfolioMessage::EvaluateActiveDocumentWithThumbnails); + } + } + responses.add(PortfolioMessage::UpdateDocumentWidgets); + } + DocumentMessage::RestartAnimation => { + self.animation_state = match self.animation_state { + AnimationState::Playing { .. } => AnimationState::Playing { start: ipp.time }, + _ => AnimationState::Stopped, + }; + responses.add(PortfolioMessage::UpdateDocumentWidgets); + } DocumentMessage::ToggleSelectedLocked => responses.add(NodeGraphMessage::ToggleSelectedLocked), DocumentMessage::ToggleSelectedVisibility => { responses.add(NodeGraphMessage::ToggleSelectedVisibility); @@ -1302,17 +1322,6 @@ impl MessageHandler> for DocumentMessag self.snapping_state.snapping_enabled = !self.snapping_state.snapping_enabled; responses.add(PortfolioMessage::UpdateDocumentWidgets); } - // DocumentMessage::ToggleAnimation => match self.animation_state { - // AnimationState::Stopped => {self.animation_state = AnimationState::Playing { start: ipp.time }; responses.add(PortfolioMessage::EvaluateActiveDocument)}, - // AnimationState::Playing { start } => self.animation_state = AnimationState::Paused { start , pause_time: ipp.time }, - // AnimationState::Paused { start, .. } => {self.animation_state = AnimationState::Playing { start }; responses.add(PortfolioMessage::EvaluateActiveDocument)}, - // }, - // DocumentMessage::RestartAnimation => { - // self.animation_state = match self.animation_state { - // AnimationState::Playing { .. } => AnimationState::Playing { start: ipp.time }, - // _ => AnimationState::Stopped, - // }; - // } DocumentMessage::UpdateUpstreamTransforms { upstream_footprints, local_transforms, @@ -1320,6 +1329,9 @@ impl MessageHandler> for DocumentMessag } => { self.network_interface.update_transforms(upstream_footprints, local_transforms); self.network_interface.update_first_instance_source_id(first_instance_source_id); + if matches!(self.animation_state, AnimationState::Playing { .. }) { + responses.add(PortfolioMessage::EvaluateActiveDocumentWithThumbnails); + } } DocumentMessage::UpdateClickTargets { click_targets } => { // TODO: Allow non layer nodes to have click targets @@ -1582,6 +1594,14 @@ impl MessageHandler> for DocumentMessag } impl DocumentMessageHandler { + pub fn animation_time(&self, ipp: &InputPreprocessorMessageHandler) -> f64 { + match self.animation_state { + AnimationState::Stopped => 0., + AnimationState::Playing { start } => ipp.time - start, + AnimationState::Paused { start, pause_time } => pause_time - start, + } + } + /// Runs an intersection test with all layers and a viewport space quad pub fn intersect_quad<'a>(&'a self, viewport_quad: graphene_std::renderer::Quad, ipp: &InputPreprocessorMessageHandler) -> impl Iterator + use<'a> { let document_to_viewport = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz); @@ -2113,7 +2133,9 @@ impl DocumentMessageHandler { } } - pub fn update_document_widgets(&self, responses: &mut VecDeque, animation_is_playing: bool, time: Duration) { + pub fn update_document_widgets(&self, responses: &mut VecDeque, ipp: &InputPreprocessorMessageHandler) { + let animation_is_playing = matches!(self.animation_state, AnimationState::Playing { .. }); + // Document mode (dropdown menu at the left of the bar above the viewport, before the tool options) let document_mode_layout = WidgetLayout::new(vec![LayoutGroup::Row { @@ -2153,14 +2175,14 @@ impl DocumentMessageHandler { let mut widgets = vec![ IconButton::new("PlaybackToStart", 24) .tooltip("Restart Animation") - .tooltip_shortcut(action_keys!(AnimationMessageDiscriminant::RestartAnimation)) - .on_update(|_| AnimationMessage::RestartAnimation.into()) - .disabled(time == Duration::ZERO) + .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::RestartAnimation)) + .on_update(|_| DocumentMessage::RestartAnimation.into()) + .disabled(self.animation_time(ipp) == 0.) .widget_holder(), IconButton::new(if animation_is_playing { "PlaybackPause" } else { "PlaybackPlay" }, 24) .tooltip(if animation_is_playing { "Pause Animation" } else { "Play Animation" }) - .tooltip_shortcut(action_keys!(AnimationMessageDiscriminant::ToggleLivePreview)) - .on_update(|_| AnimationMessage::ToggleLivePreview.into()) + .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleAnimation)) + .on_update(|_| DocumentMessage::ToggleAnimation.into()) .widget_holder(), Separator::new(SeparatorType::Unrelated).widget_holder(), CheckboxInput::new(self.overlays_visibility_settings.all) @@ -3140,6 +3162,18 @@ impl Iterator for ClickXRayIter<'_> { } } +#[derive(Default, Debug, Clone, PartialEq)] +pub enum AnimationState { + #[default] + Stopped, + Playing { + start: f64, + }, + Paused { + start: f64, + pause_time: f64, + }, +} // #[cfg(test)] // mod document_message_handler_tests { // use super::*; diff --git a/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs b/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs index a2c08f5c12..3eb27ee150 100644 --- a/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs +++ b/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs @@ -8,7 +8,6 @@ use crate::messages::portfolio::document::utility_types::network_interface::{ DocumentNodeMetadata, DocumentNodePersistentMetadata, InputMetadata, NodeNetworkInterface, NodeNetworkMetadata, NodeNetworkPersistentMetadata, NodeTemplate, NodeTypePersistentMetadata, NumberInputSettings, Vec2InputSettings, WidgetOverride, }; -use crate::messages::portfolio::utility_types::PersistentData; use crate::messages::prelude::Message; use glam::DVec2; use graph_craft::ProtoNodeIdentifier; @@ -28,7 +27,6 @@ use graphene_std::*; use std::collections::{HashMap, HashSet, VecDeque}; pub struct NodePropertiesContext<'a> { - pub persistent_data: &'a PersistentData, pub responses: &'a mut VecDeque, pub network_interface: &'a mut NodeNetworkInterface, pub selection_network_path: &'a [NodeId], diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs index 7a109002c7..0a64c3dd2b 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs @@ -2305,7 +2305,7 @@ impl NodeGraphMessageHandler { .find(|error| match &error.original_location { graph_craft::proto::OriginalLocation::Value(_) => false, graph_craft::proto::OriginalLocation::Node(prefixed_node_path) => { - let (prefix, node_path) = prefixed_node_path.split_first().unwrap(); + let (_, node_path) = prefixed_node_path.split_first().unwrap(); node_path == &node_id_path } }) @@ -2314,7 +2314,7 @@ impl NodeGraphMessageHandler { if self.node_graph_errors.iter().any(|error| match &error.original_location { graph_craft::proto::OriginalLocation::Value(_) => false, graph_craft::proto::OriginalLocation::Node(prefixed_node_path) => { - let (prefix, node_path) = prefixed_node_path.split_first().unwrap(); + let (_, node_path) = prefixed_node_path.split_first().unwrap(); node_path.starts_with(&node_id_path) } }) { diff --git a/editor/src/messages/portfolio/document/properties_panel/mod.rs b/editor/src/messages/portfolio/document/properties_panel/mod.rs index 763d3d52c3..e1521b734b 100644 --- a/editor/src/messages/portfolio/document/properties_panel/mod.rs +++ b/editor/src/messages/portfolio/document/properties_panel/mod.rs @@ -4,4 +4,4 @@ mod properties_panel_message_handler; #[doc(inline)] pub use properties_panel_message::{PropertiesPanelMessage, PropertiesPanelMessageDiscriminant}; #[doc(inline)] -pub use properties_panel_message_handler::{PropertiesPanelMessageHandler, PropertiesPanelMessageHandlerData}; +pub use properties_panel_message_handler::{PropertiesPanelMessageContext, PropertiesPanelMessageHandler}; diff --git a/editor/src/messages/portfolio/document/properties_panel/properties_panel_message_handler.rs b/editor/src/messages/portfolio/document/properties_panel/properties_panel_message_handler.rs index a215bda3d7..968f349341 100644 --- a/editor/src/messages/portfolio/document/properties_panel/properties_panel_message_handler.rs +++ b/editor/src/messages/portfolio/document/properties_panel/properties_panel_message_handler.rs @@ -3,11 +3,10 @@ use graphene_std::uuid::NodeId; use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::portfolio::document::node_graph::document_node_definitions::NodePropertiesContext; use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface; -use crate::messages::portfolio::utility_types::PersistentData; use crate::messages::prelude::*; -use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface; -pub struct PropertiesPanelMessageHandlerData<'a> { +#[derive(ExtractField)] +pub struct PropertiesPanelMessageContext<'a> { pub network_interface: &'a mut NodeNetworkInterface, pub selection_network_path: &'a [NodeId], pub document_name: &'a str, @@ -23,7 +22,7 @@ impl MessageHandler> f network_interface, selection_network_path, document_name, - } = data; + } = context; match message { PropertiesPanelMessage::Clear => { @@ -34,7 +33,6 @@ impl MessageHandler> f } PropertiesPanelMessage::Refresh => { let mut node_properties_context = NodePropertiesContext { - persistent_data, responses, network_interface, selection_network_path, diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface.rs b/editor/src/messages/portfolio/document/utility_types/network_interface.rs index 63ab5efe34..470d562cc0 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface.rs @@ -2535,7 +2535,7 @@ impl NodeNetworkInterface { pub fn newly_loaded_input_wire(&mut self, input: &InputConnector, graph_wire_style: &GraphWireStyle, network_path: &[NodeId]) -> Option { match self.cached_wire(input, network_path) { - Some(loaded) => None, + Some(_) => None, None => { self.load_wire(input, graph_wire_style, network_path); self.cached_wire(input, network_path).cloned() diff --git a/editor/src/messages/portfolio/portfolio_message.rs b/editor/src/messages/portfolio/portfolio_message.rs index e0f1f48e3f..27c9bff3db 100644 --- a/editor/src/messages/portfolio/portfolio_message.rs +++ b/editor/src/messages/portfolio/portfolio_message.rs @@ -22,6 +22,7 @@ pub enum PortfolioMessage { #[child] Spreadsheet(SpreadsheetMessage), + Init, // Sends a request to compile the network. Should occur when any value, preference, or font changes CompileActiveDocument, // Sends a request to evaluate the network. Should occur when any context value changes. @@ -54,10 +55,6 @@ pub enum PortfolioMessage { // Introspected data is cleared after queued messages are complete ClearIntrospectedData, ProcessThumbnails, - DocumentPassMessage { - document_id: DocumentId, - message: DocumentMessage, - }, AutoSaveActiveDocument, AutoSaveAllDocuments, AutoSaveDocument { diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index 3b08020fc4..19da88c3af 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -10,6 +10,7 @@ use crate::messages::frontend::utility_types::{ExportBounds, FrontendDocumentDet use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::portfolio::document::DocumentMessageContext; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; +use crate::messages::portfolio::document::node_graph::document_node_definitions; use crate::messages::portfolio::document::utility_types::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT}; use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use crate::messages::portfolio::document::utility_types::wires::{GraphWireStyle, WireSNIUpdate}; @@ -23,7 +24,6 @@ use glam::{DAffine2, DVec2}; use graph_craft::document::value::EditorMetadata; use graph_craft::document::{InputConnector, NodeInput, OutputConnector}; use graphene_std::EditorContext; -use graphene_std::application_io::TimingInformation; use graphene_std::memo::MonitorIntrospectResult; use graphene_std::renderer::{Quad, RenderMetadata}; use graphene_std::text::Font; @@ -38,8 +38,6 @@ pub struct PortfolioMessageContext<'a> { pub current_tool: &'a ToolType, pub message_logging_verbosity: MessageLoggingVerbosity, pub reset_node_definitions_on_open: bool, - pub timing_information: TimingInformation, - pub animation: &'a AnimationMessageHandler, } #[derive(Debug, Default, ExtractField)] @@ -74,8 +72,6 @@ impl MessageHandler> for Portfolio current_tool, message_logging_verbosity, reset_node_definitions_on_open, - timing_information, - animation, } = context; match message { @@ -127,9 +123,8 @@ impl MessageHandler> for Portfolio PortfolioMessage::Document(message) => { if let Some(document_id) = self.active_document_id { if let Some(document) = self.documents.get_mut(&document_id) { - let document_inputs = DocumentMessageData { + let document_inputs = DocumentMessageContext { ipp, - persistent_data: &self.persistent_data, current_tool, preferences, device_pixel_ratio: self.device_pixel_ratio.unwrap_or(1.), @@ -138,7 +133,6 @@ impl MessageHandler> for Portfolio } } } - // Messages PortfolioMessage::Init => { // Load persistent data from the browser database @@ -157,18 +151,6 @@ impl MessageHandler> for Portfolio // Finish loading persistent data from the browser database responses.add(FrontendMessage::TriggerLoadRestAutoSaveDocuments); } - PortfolioMessage::DocumentPassMessage { document_id, message } => { - if let Some(document) = self.documents.get_mut(&document_id) { - let document_inputs = DocumentMessageData { - ipp, - persistent_data: &self.persistent_data, - current_tool, - preferences, - device_pixel_ratio: self.device_pixel_ratio.unwrap_or(1.), - }; - document.process_message(message, responses, document_inputs) - } - } PortfolioMessage::AutoSaveActiveDocument => { if let Some(document_id) = self.active_document_id { if let Some(document) = self.active_document_mut() { @@ -831,25 +813,17 @@ impl MessageHandler> for Portfolio return; }; - // let animation_time = match animation.timing_information().animation_time { - // AnimationState::Stopped => 0., - // AnimationState::Playing { start } => ipp.time - start, - // AnimationState::Paused { start, pause_time } => pause_time - start, - // }; - let mut context = EditorContext::default(); context.footprint = Some(Footprint { transform: document.metadata().document_to_viewport, resolution: ipp.viewport_bounds.size().as_uvec2(), quality: RenderQuality::Full, }); - context.animation_time = Some(timing_information.animation_time.as_secs_f64()); + context.animation_time = Some(document.animation_time(ipp)); context.real_time = Some(ipp.time); context.downstream_transform = Some(DAffine2::IDENTITY); - let nodes_to_try_render = self.visible_nodes_to_try_render(ipp, &preferences.graph_wire_style); - - self.executor.submit_node_graph_evaluation(context, None, None, nodes_to_try_render); + self.executor.submit_node_graph_evaluation(context, None, None, nodes_to_introspect); } PortfolioMessage::ProcessEvaluationResponse { evaluation_metadata, @@ -876,10 +850,6 @@ impl MessageHandler> for Portfolio responses.add(DocumentMessage::RenderScrollbars); responses.add(DocumentMessage::RenderRulers); responses.add(OverlaysMessage::Draw); - // match document.animation_state { - // AnimationState::Playing { .. } => responses.add(PortfolioMessage::EvaluateActiveDocument), - // _ => {} - // }; } // PortfolioMessage::IntrospectActiveDocument { nodes_to_introspect } => { // self.executor.submit_node_graph_introspection(nodes_to_introspect); @@ -1081,7 +1051,7 @@ impl MessageHandler> for Portfolio } PortfolioMessage::UpdateDocumentWidgets => { if let Some(document) = self.active_document() { - document.update_document_widgets(responses, animation.is_playing(), timing_information.animation_time); + document.update_document_widgets(responses, ipp); } } PortfolioMessage::UpdateOpenDocumentsList => { @@ -1283,8 +1253,6 @@ impl PortfolioMessageHandler { .network_interface .viewport_loaded_thumbnail_position(&input_connector, graph_wire_style, &document.breadcrumb_network_path) { - log::debug!("viewport position: {:?}, input: {:?}", viewport_position, input_connector); - let in_view = viewport_position.x > 0.0 && viewport_position.y > 0.0 && viewport_position.x < ipp.viewport_bounds()[1].x && viewport_position.y < ipp.viewport_bounds()[1].y; if in_view { let Some(protonode) = document.network_interface.protonode_from_input(&input_connector, &document.breadcrumb_network_path) else { diff --git a/editor/src/messages/prelude.rs b/editor/src/messages/prelude.rs index 72a6cb9ba7..9308fa270d 100644 --- a/editor/src/messages/prelude.rs +++ b/editor/src/messages/prelude.rs @@ -2,7 +2,6 @@ pub use crate::utility_traits::{ActionList, AsMessage, HierarchicalTree, MessageHandler, ToDiscriminant, TransitiveChild}; pub use crate::utility_types::{DebugMessageTree, MessageData}; // 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, ExportDialogMessageContext, ExportDialogMessageDiscriminant, ExportDialogMessageHandler}; diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index bfe23604e4..6e06a83d42 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -611,7 +611,7 @@ impl PathToolData { self.last_drill_through_click_position.map_or(true, |last_pos| last_pos.distance(position) > DRILL_THROUGH_THRESHOLD) } - fn set_ghost_outline(&mut self, shape_editor: &ShapeState, document: &DocumentMessageHandler) { + pub fn set_ghost_outline(&mut self, shape_editor: &ShapeState, document: &DocumentMessageHandler) { self.ghost_outline.clear(); for &layer in shape_editor.selected_shape_state.keys() { // We probably need to collect here diff --git a/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs b/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs index 845a56736e..a964b76e63 100644 --- a/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs +++ b/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs @@ -11,9 +11,8 @@ use crate::messages::tool::tool_messages::tool_prelude::Key; use crate::messages::tool::utility_types::{ToolData, ToolType}; use glam::{DAffine2, DVec2}; use graphene_std::renderer::Quad; -use graphene_std::vector::ManipulatorPointId; use graphene_std::vector::click_target::ClickTargetType; -use graphene_std::vector::{VectorData, VectorModificationType}; +use graphene_std::vector::{ManipulatorPointId, VectorData, VectorModificationType}; use std::f64::consts::{PI, TAU}; const TRANSFORM_GRS_OVERLAY_PROVIDER: OverlayProvider = |context| TransformLayerMessage::Overlays(context).into(); @@ -670,6 +669,135 @@ impl MessageHandler> for } } +impl TransformLayerMessageHandler { + pub fn is_transforming(&self) -> bool { + self.transform_operation != TransformOperation::None + } + + pub fn hints(&self, responses: &mut VecDeque) { + self.transform_operation.hints(responses, self.local); + } + + fn set_ghost_outline(ghost_outline: &mut Vec<(Vec, DAffine2)>, shape_editor: &ShapeState, document: &DocumentMessageHandler) { + ghost_outline.clear(); + for &layer in shape_editor.selected_shape_state.keys() { + // We probably need to collect here + let outline = document.metadata().layer_with_free_points_outline(layer).cloned().collect(); + let transform = document.metadata().transform_to_viewport(layer); + ghost_outline.push((outline, transform)); + } + } +} + +fn calculate_pivot( + document: &DocumentMessageHandler, + selected_points: &Vec<&ManipulatorPointId>, + vector_data: &VectorData, + viewspace: DAffine2, + get_location: impl Fn(&ManipulatorPointId) -> Option, + gizmo: &mut PivotGizmo, +) -> (Option<(DVec2, DVec2)>, Option<[DVec2; 2]>) { + let average_position = || { + let mut point_count = 0_usize; + selected_points.iter().filter_map(|p| get_location(p)).inspect(|_| point_count += 1).sum::() / point_count as f64 + }; + let bounds = selected_points.iter().filter_map(|p| get_location(p)).fold(None, |acc: Option<[DVec2; 2]>, point| { + if let Some([mut min, mut max]) = acc { + min.x = min.x.min(point.x); + min.y = min.y.min(point.y); + max.x = max.x.max(point.x); + max.y = max.y.max(point.y); + Some([min, max]) + } else { + Some([point, point]) + } + }); + gizmo.pivot.recalculate_pivot_for_layer(document, bounds); + let position = || { + (if !gizmo.state.disabled { + match gizmo.state.gizmo_type { + PivotGizmoType::Average => None, + PivotGizmoType::Active => gizmo.point.and_then(|p| get_location(&p)), + PivotGizmoType::Pivot => gizmo.pivot.pivot, + } + } else { + None + }) + .unwrap_or_else(average_position) + }; + let [point] = selected_points.as_slice() else { + // Handle the case where there are multiple points + let position = position(); + return (Some((position, position)), bounds); + }; + + match point { + ManipulatorPointId::PrimaryHandle(_) | ManipulatorPointId::EndHandle(_) => { + // Get the anchor position and transform it to the pivot + let (Some(pivot_position), Some(position)) = ( + point.get_anchor_position(vector_data).map(|anchor_position| viewspace.transform_point2(anchor_position)), + point.get_position(vector_data), + ) else { + return (None, None); + }; + let target = viewspace.transform_point2(position); + (Some((pivot_position, target)), None) + } + _ => { + // Calculate the average position of all selected points + let position = position(); + (Some((position, position)), bounds) + } + } +} + +fn project_edge_to_quad(edge: DVec2, quad: &Quad, local: bool, axis_constraint: Axis) -> DVec2 { + match axis_constraint { + Axis::X => { + if local { + edge.project_onto(quad.top_right() - quad.top_left()) + } else { + edge.with_y(0.) + } + } + Axis::Y => { + if local { + edge.project_onto(quad.bottom_left() - quad.top_left()) + } else { + edge.with_x(0.) + } + } + _ => edge, + } +} + +fn update_colinear_handles(selected_layers: &[LayerNodeIdentifier], document: &DocumentMessageHandler, responses: &mut VecDeque) { + for &layer in selected_layers { + let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue }; + + for [handle1, handle2] in &vector_data.colinear_manipulators { + let manipulator1 = handle1.to_manipulator_point(); + let manipulator2 = handle2.to_manipulator_point(); + + let Some(anchor) = manipulator1.get_anchor_position(&vector_data) else { continue }; + let Some(pos1) = manipulator1.get_position(&vector_data).map(|pos| pos - anchor) else { continue }; + let Some(pos2) = manipulator2.get_position(&vector_data).map(|pos| pos - anchor) else { continue }; + + let angle = pos1.angle_to(pos2); + + // Check if handles are not colinear (not approximately equal to +/- PI) + if (angle - PI).abs() > 1e-6 && (angle + PI).abs() > 1e-6 { + let modification_type = VectorModificationType::SetG1Continuous { + handles: [*handle1, *handle2], + enabled: false, + }; + + responses.add(GraphOperationMessage::Vector { layer, modification_type }); + } + } + } +} + // #[cfg(test)] // mod test_transform_layer { // use crate::messages::portfolio::document::graph_operation::transform_utils; diff --git a/editor/src/node_graph_executor.rs b/editor/src/node_graph_executor.rs index d4515d2dad..215c0c8528 100644 --- a/editor/src/node_graph_executor.rs +++ b/editor/src/node_graph_executor.rs @@ -79,15 +79,6 @@ struct EvaluationContext { export_config: Option, } -impl Default for NodeGraphExecutor { - fn default() -> Self { - Self { - futures: Default::default(), - runtime_io: NodeRuntimeIO::new(), - } - } -} - impl NodeGraphExecutor { /// A local runtime is useful on threads since having global state causes flakes // #[cfg(test)] @@ -270,18 +261,6 @@ impl NodeGraphExecutor { } } -// pub enum AnimationState { -// #[default] -// Stopped, -// Playing { -// start: f64, -// }, -// Paused { -// start: f64, -// pause_time: f64, -// }, -// } - // Re-export for usage by tests in other modules // #[cfg(test)] // pub use test::Instrumented; diff --git a/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index a4e539df42..200f242fc0 100644 --- a/frontend/wasm/src/editor_api.rs +++ b/frontend/wasm/src/editor_api.rs @@ -130,18 +130,12 @@ impl EditorHandle { let f = std::rc::Rc::new(RefCell::new(None)); let g = f.clone(); - *g.borrow_mut() = Some(Closure::new(move |_timestamp| { + *g.borrow_mut() = Some(Closure::new(move |timestamp| { wasm_bindgen_futures::spawn_local(poll_node_graph_evaluation()); if !EDITOR_HAS_CRASHED.load(Ordering::SeqCst) { editor_and_handle(|editor, handle| { - for message in editor.handle_message(InputPreprocessorMessage::CurrentTime { - timestamp: js_sys::Date::now() as u64, - }) { - handle.send_frontend_message_to_js(message); - } - - for message in editor.handle_message(AnimationMessage::IncrementFrameCounter) { + for message in editor.handle_message(InputPreprocessorMessage::CurrentTime { timestamp }) { handle.send_frontend_message_to_js(message); } diff --git a/node-graph/gcore/src/context.rs b/node-graph/gcore/src/context.rs index 3fd1ffc34d..5691b69cb3 100644 --- a/node-graph/gcore/src/context.rs +++ b/node-graph/gcore/src/context.rs @@ -41,7 +41,7 @@ pub trait ExtractAnimationTime { } pub trait ExtractIndex { - fn try_index(&self) -> Option>; + fn try_index(&self) -> Option; } // Consider returning a slice or something like that @@ -231,7 +231,7 @@ impl ExtractAnimationTime for Option { } } impl ExtractIndex for Option { - fn try_index(&self) -> Option> { + fn try_index(&self) -> Option { self.as_ref().and_then(|x| x.try_index()) } } @@ -263,7 +263,7 @@ impl ExtractAnimationTime for Arc { } } impl ExtractIndex for Arc { - fn try_index(&self) -> Option> { + fn try_index(&self) -> Option { (**self).try_index() } } @@ -316,7 +316,7 @@ impl ExtractAnimationTime for OwnedContextImpl { } } impl ExtractIndex for OwnedContextImpl { - fn try_index(&self) -> Option> { + fn try_index(&self) -> Option { self.index.clone() } } @@ -630,8 +630,8 @@ fn get_animation_time(ctx: impl Ctx + ExtractAnimationTime) -> Option { } #[node_macro::node(category("Context Getter"))] -fn get_index(ctx: impl Ctx + ExtractIndex) -> Option { - ctx.try_index().map(|index| index as u32) +fn get_index(ctx: impl Ctx + ExtractIndex) -> Option { + ctx.try_index() } // #[node_macro::node(category("Loop"))] @@ -671,21 +671,21 @@ fn get_index(ctx: impl Ctx + ExtractIndex) -> Option { // } // } -#[node_macro::node(category("Loop"))] -async fn set_index( - ctx: impl Ctx + ExtractAll + CloneVarArgs + Sync, - #[expose] - #[implementations( - Context -> u32, - Context -> (), - )] - input: impl Node, Output = T>, - number: u32, -) -> T { - let mut new_context = OwnedContextImpl::from(ctx); - new_context.index = Some(number.try_into().unwrap()); - input.eval(new_context.into_context()).await -} +// #[node_macro::node(category("Loop"))] +// async fn set_index( +// ctx: impl Ctx + ExtractAll + CloneVarArgs + Sync, +// #[expose] +// #[implementations( +// Context -> u32, +// Context -> (), +// )] +// input: impl Node, Output = T>, +// number: u32, +// ) -> T { +// let mut new_context = OwnedContextImpl::from(ctx); +// new_context.index = Some(number.try_into().unwrap()); +// input.eval(new_context.into_context()).await +// } // #[node_macro::node(category("Loop"))] // fn create_arc_mutex(_ctx: impl Ctx) -> Arc>> { diff --git a/node-graph/gcore/src/vector/algorithms/instance.rs b/node-graph/gcore/src/vector/algorithms/instance.rs index eb86287966..cbf8f67e7c 100644 --- a/node-graph/gcore/src/vector/algorithms/instance.rs +++ b/node-graph/gcore/src/vector/algorithms/instance.rs @@ -86,10 +86,10 @@ async fn instance_position(ctx: impl Ctx + ExtractVarArgs) -> DVec2 { // TODO: Make this return a u32 instead of an f64, but we ned to improve math-related compatibility with integer types first. #[node_macro::node(category("Instancing"), path(graphene_core::vector))] -async fn instance_index(ctx: impl Ctx + ExtractIndex, _primary: (), loop_level: u32) -> f64 { - ctx.try_index() - .and_then(|indexes| indexes.get(indexes.len().wrapping_sub(1).wrapping_sub(loop_level as usize)).copied()) - .unwrap_or_default() as f64 +async fn instance_index(ctx: impl Ctx + ExtractIndex, _primary: (), _loop_level: u32) -> f64 { + ctx.try_index().unwrap_or_default() as f64 + // .and_then(|indexes| indexes.get(indexes.len().wrapping_sub(1).wrapping_sub(loop_level as usize)).copied()) + // .unwrap_or_default() as f64 } // #[cfg(test)] diff --git a/node-graph/gcore/src/vector/vector_nodes.rs b/node-graph/gcore/src/vector/vector_nodes.rs index 51c65d7dae..c80fcc365f 100644 --- a/node-graph/gcore/src/vector/vector_nodes.rs +++ b/node-graph/gcore/src/vector/vector_nodes.rs @@ -20,7 +20,8 @@ use glam::{DAffine2, DVec2}; use kurbo::{Affine, BezPath, DEFAULT_ACCURACY, ParamCurve, PathEl, PathSeg, Shape}; use rand::{Rng, SeedableRng}; use std::collections::hash_map::DefaultHasher; -use std::f64::consts::TAU; +use std::f64::consts::{PI, TAU}; +use std::hash::{Hash, Hasher}; /// Implemented for types that can be converted to an iterator of vector data. /// Used for the fill and stroke node so they can be used on VectorData or GraphicGroup @@ -1732,7 +1733,7 @@ async fn morph(_: impl Ctx, source: VectorDataTable, #[expose] target: VectorDat fn bevel_algorithm(mut vector_data: VectorData, vector_data_transform: DAffine2, distance: f64) -> VectorData { // Splits a bézier curve based on a distance measurement fn split_distance(bezier: PathSeg, distance: f64, length: f64) -> PathSeg { - let parametric = eval_pathseg_euclidean(bezier, (distance / length).clamp(0., 1.), DEFAULT_ACCURACY); + let parametric = bezpath_algorithms::eval_pathseg_euclidean(bezier, (distance / length).clamp(0., 1.), DEFAULT_ACCURACY); bezier.subsegment(parametric..1.) } @@ -1773,7 +1774,7 @@ fn bevel_algorithm(mut vector_data: VectorData, vector_data_transform: DAffine2, } fn calculate_distance_to_spilt(bezier1: PathSeg, bezier2: PathSeg, bevel_length: f64) -> f64 { - if is_linear(&bezier1) && is_linear(&bezier2) { + if bezpath_algorithms::is_linear(&bezier1) && bezpath_algorithms::is_linear(&bezier2) { let v1 = (bezier1.end() - bezier1.start()).normalize(); let v2 = (bezier1.end() - bezier2.end()).normalize(); @@ -1798,8 +1799,8 @@ fn bevel_algorithm(mut vector_data: VectorData, vector_data_transform: DAffine2, for i in 0..=INITIAL_SAMPLES { let distance_sample = max_split * (i as f64 / INITIAL_SAMPLES as f64); - let x_point_t = eval_pathseg_euclidean(bezier1, 1. - clamp_and_round(distance_sample / length1), DEFAULT_ACCURACY); - let y_point_t = eval_pathseg_euclidean(bezier2, clamp_and_round(distance_sample / length2), DEFAULT_ACCURACY); + let x_point_t = bezpath_algorithms::eval_pathseg_euclidean(bezier1, 1. - clamp_and_round(distance_sample / length1), DEFAULT_ACCURACY); + let y_point_t = bezpath_algorithms::eval_pathseg_euclidean(bezier2, clamp_and_round(distance_sample / length2), DEFAULT_ACCURACY); let x_point = bezier1.eval(x_point_t); let y_point = bezier2.eval(y_point_t); @@ -1822,8 +1823,8 @@ fn bevel_algorithm(mut vector_data: VectorData, vector_data_transform: DAffine2, for j in 1..=REFINE_STEPS { let refined_sample = prev_sample + (distance_sample - prev_sample) * (j as f64 / REFINE_STEPS as f64); - let x_point_t = eval_pathseg_euclidean(bezier1, 1. - (refined_sample / length1).clamp(0., 1.), DEFAULT_ACCURACY); - let y_point_t = eval_pathseg_euclidean(bezier2, (refined_sample / length2).clamp(0., 1.), DEFAULT_ACCURACY); + let x_point_t = bezpath_algorithms::eval_pathseg_euclidean(bezier1, 1. - (refined_sample / length1).clamp(0., 1.), DEFAULT_ACCURACY); + let y_point_t = bezpath_algorithms::eval_pathseg_euclidean(bezier2, (refined_sample / length2).clamp(0., 1.), DEFAULT_ACCURACY); let x_point = bezier1.eval(x_point_t); let y_point = bezier2.eval(y_point_t); @@ -1911,16 +1912,16 @@ fn bevel_algorithm(mut vector_data: VectorData, vector_data_transform: DAffine2, let spilt_distance = calculate_distance_to_spilt(bezier, next_bezier, distance); - if is_linear(&bezier) { + if bezpath_algorithms::is_linear(&bezier) { let start = point_to_dvec2(bezier.start()); let end = point_to_dvec2(bezier.end()); - bezier = handles_to_segment(start, BezierHandles::Linear, end); + bezier = handles_to_segment(start, bezier_rs::BezierHandles::Linear, end); } - if is_linear(&next_bezier) { + if bezpath_algorithms::is_linear(&next_bezier) { let start = point_to_dvec2(next_bezier.start()); let end = point_to_dvec2(next_bezier.end()); - next_bezier = handles_to_segment(start, BezierHandles::Linear, end); + next_bezier = handles_to_segment(start, bezier_rs::BezierHandles::Linear, end); } let inverse_transform = (vector_data_transform.matrix2.determinant() != 0.).then(|| vector_data_transform.inverse()).unwrap_or_default(); diff --git a/node-graph/graph-craft/src/document/value.rs b/node-graph/graph-craft/src/document/value.rs index a45468e63e..92b17e5ffa 100644 --- a/node-graph/graph-craft/src/document/value.rs +++ b/node-graph/graph-craft/src/document/value.rs @@ -13,7 +13,6 @@ use graphene_core::uuid::NodeId; use graphene_core::vector::style::Fill; use graphene_core::{Color, MemoHash, Node, Type}; use graphene_svg_renderer::{GraphicElementRendered, RenderMetadata}; -use std::cell::Cell; use std::fmt::Display; use std::hash::Hash; use std::marker::PhantomData; @@ -564,6 +563,7 @@ thumbnail_render! { graphene_core::GraphicElement, Option, Vec, + f64, } pub enum ThumbnailRenderResult { diff --git a/node-graph/gstd/src/any.rs b/node-graph/gstd/src/any.rs index 4932687ab6..36890c0a26 100644 --- a/node-graph/gstd/src/any.rs +++ b/node-graph/gstd/src/any.rs @@ -3,6 +3,7 @@ pub use graph_craft::proto::{Any, NodeContainer, TypeErasedBox, TypeErasedNode}; use graph_craft::proto::{DynFuture, FutureAny, SharedNodeContainer}; use graphene_core::Context; use graphene_core::ContextDependencies; +use graphene_core::EditorContext; use graphene_core::NodeIO; use graphene_core::OwnedContextImpl; use graphene_core::WasmNotSend; diff --git a/node-graph/gstd/src/text.rs b/node-graph/gstd/src/text.rs index cd05cb31c2..f1652230b9 100644 --- a/node-graph/gstd/src/text.rs +++ b/node-graph/gstd/src/text.rs @@ -1,4 +1,4 @@ -use crate::vector::{VectorData, VectorDataTable}; +use crate::vector::{VectorDataTable}; use graphene_core::Ctx; pub use graphene_core::text::*; diff --git a/node-graph/gsvg-renderer/src/renderer.rs b/node-graph/gsvg-renderer/src/renderer.rs index 245ae342e4..c472ab425f 100644 --- a/node-graph/gsvg-renderer/src/renderer.rs +++ b/node-graph/gsvg-renderer/src/renderer.rs @@ -213,6 +213,7 @@ pub trait GraphicElementRendered: BoundingBox + RenderComplexity { fn render_thumbnail(&self) -> String { let Some(bounds) = self.bounding_box(DAffine2::IDENTITY, true) else { + log::debug!("Could not get bounds"); return String::new(); }; @@ -228,7 +229,7 @@ pub trait GraphicElementRendered: BoundingBox + RenderComplexity { let mut render = SvgRender::new(); self.render_svg(&mut render, &render_params); - + // let center = (bounds[0] + bounds[1]) / 2.; // let size = bounds[1] - bounds[0]; @@ -1193,13 +1194,13 @@ impl Primitive for f64 {} impl Primitive for DVec2 {} fn text_attributes(attributes: &mut SvgRenderAttrs) { - attributes.push("fill", "white"); - attributes.push("y", "30"); + attributes.push("fill", "black"); attributes.push("font-size", "30"); } impl GraphicElementRendered for P { fn render_svg(&self, render: &mut SvgRender, _render_params: &RenderParams) { + log::debug!("Rendering svg for primative: {}", self); render.parent_tag("text", text_attributes, |render| render.leaf_node(format!("{self}"))); }