From bf850189ddba59e6afc6c669118a1ee6d6b502b7 Mon Sep 17 00:00:00 2001 From: mtvare6 Date: Tue, 18 Feb 2025 18:00:18 +0530 Subject: [PATCH 01/11] Transfer pasted layers to viewport center when not in viewport Fixes #2301 --- .../portfolio/portfolio_message_handler.rs | 50 +++++++++++++------ .../src/graphic_element/renderer/quad.rs | 5 ++ 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index 6afd3683f7..ac0a07afa6 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -7,6 +7,7 @@ use crate::messages::debug::utility_types::MessageLoggingVerbosity; use crate::messages::dialog::simple_dialogs; use crate::messages::frontend::utility_types::FrontendDocumentDetails; use crate::messages::layout::utility_types::widget_prelude::*; +use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::utility_types::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT}; use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; @@ -15,6 +16,7 @@ use crate::messages::preferences::SelectionMode; use crate::messages::prelude::*; use crate::messages::tool::utility_types::{HintData, HintGroup, ToolType}; use crate::node_graph_executor::{ExportConfig, NodeGraphExecutor}; +use graphene_core::renderer::Quad; use bezier_rs::Subpath; use glam::IVec2; @@ -888,22 +890,42 @@ impl MessageHandler> for PortfolioMes if let Some(document) = self.active_document() { if let Ok(data) = serde_json::from_str::>(&data) { let parent = document.new_layer_parent(false); - - let mut added_nodes = false; - - for entry in data.into_iter().rev() { - if !added_nodes { - responses.add(DocumentMessage::DeselectAllLayers); - responses.add(DocumentMessage::AddTransaction); - added_nodes = true; + let mut transform = document.metadata().document_to_viewport; + + // Check parent bounds first since children will be pasted at same position + if let Some(parent_bounds) = document.metadata().bounding_box_document(parent) { + let viewport_bounds = Quad::from_box_at_zero(ipp.viewport_bounds.size()); + let quad = transform * Quad::from_box(parent_bounds); + + transform.translation = (viewport_bounds.center() - quad.center()).round() + quad.center(); + let centering_transform = quad.0.into_iter().all(|point| !viewport_bounds.contains(point)).then_some(transform); + + let mut added_nodes = false; + + for entry in data.into_iter().rev() { + if !added_nodes { + responses.add(DocumentMessage::DeselectAllLayers); + responses.add(DocumentMessage::AddTransaction); + added_nodes = true; + } + + document.load_layer_resources(responses); + let new_ids: HashMap<_, _> = entry.nodes.iter().map(|(id, _)| (*id, NodeId::new())).collect(); + let layer = LayerNodeIdentifier::new_unchecked(new_ids[&NodeId(0)]); + responses.add(NodeGraphMessage::AddNodes { nodes: entry.nodes, new_ids }); + responses.add(NodeGraphMessage::MoveLayerToStack { layer, parent, insert_index: 0 }); + + if let Some(transform) = centering_transform { + responses.add(GraphOperationMessage::TransformSet { + layer, + transform, + transform_in: TransformIn::Viewport, + skip_rerender: false, + }); + } } - document.load_layer_resources(responses); - let new_ids: HashMap<_, _> = entry.nodes.iter().map(|(id, _)| (*id, NodeId::new())).collect(); - let layer = LayerNodeIdentifier::new_unchecked(new_ids[&NodeId(0)]); - responses.add(NodeGraphMessage::AddNodes { nodes: entry.nodes, new_ids }); - responses.add(NodeGraphMessage::MoveLayerToStack { layer, parent, insert_index: 0 }); + responses.add(NodeGraphMessage::RunDocumentGraph); } - responses.add(NodeGraphMessage::RunDocumentGraph); } } } diff --git a/node-graph/gcore/src/graphic_element/renderer/quad.rs b/node-graph/gcore/src/graphic_element/renderer/quad.rs index 9d5cd8f200..3029394cf4 100644 --- a/node-graph/gcore/src/graphic_element/renderer/quad.rs +++ b/node-graph/gcore/src/graphic_element/renderer/quad.rs @@ -38,6 +38,11 @@ impl Quad { Self([bbox[0], bbox[0] + size * DVec2::X, bbox[1], bbox[0] + size * DVec2::Y]) } + /// Create a box starting at (0, 0) upto [`point`] + pub fn from_box_at_zero(point: DVec2) -> Self{ + Self::from_box([DVec2::ZERO, point]) + } + /// Create a quad from the center and offset (distance from center to middle of an edge) pub fn from_square(center: DVec2, offset: f64) -> Self { Self::from_box([center - offset, center + offset]) From 68ad92ff7ac85866000373317497046475725382 Mon Sep 17 00:00:00 2001 From: mtvare6 Date: Tue, 18 Feb 2025 22:32:16 +0530 Subject: [PATCH 02/11] Move a layer as a whole and use change --- .../portfolio/portfolio_message_handler.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index ac0a07afa6..4263a9ca4c 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -890,15 +890,20 @@ impl MessageHandler> for PortfolioMes if let Some(document) = self.active_document() { if let Ok(data) = serde_json::from_str::>(&data) { let parent = document.new_layer_parent(false); - let mut transform = document.metadata().document_to_viewport; + let transform = document.metadata().document_to_viewport; // Check parent bounds first since children will be pasted at same position if let Some(parent_bounds) = document.metadata().bounding_box_document(parent) { let viewport_bounds = Quad::from_box_at_zero(ipp.viewport_bounds.size()); let quad = transform * Quad::from_box(parent_bounds); - transform.translation = (viewport_bounds.center() - quad.center()).round() + quad.center(); - let centering_transform = quad.0.into_iter().all(|point| !viewport_bounds.contains(point)).then_some(transform); + // Calculate the translation needed to center the parent + let translation = viewport_bounds.center() - quad.center(); + let centering_transform = quad + .0 + .into_iter() + .all(|point| !viewport_bounds.contains(point)) + .then_some(glam::DAffine2::from_translation(translation.round())); let mut added_nodes = false; @@ -915,8 +920,9 @@ impl MessageHandler> for PortfolioMes responses.add(NodeGraphMessage::AddNodes { nodes: entry.nodes, new_ids }); responses.add(NodeGraphMessage::MoveLayerToStack { layer, parent, insert_index: 0 }); + // Apply the same translation to all layers if let Some(transform) = centering_transform { - responses.add(GraphOperationMessage::TransformSet { + responses.add(GraphOperationMessage::TransformChange { layer, transform, transform_in: TransformIn::Viewport, From bf09d6b0fc97f94563970ddc26cd0e31ff372ae5 Mon Sep 17 00:00:00 2001 From: mtvare6 Date: Wed, 19 Feb 2025 00:10:35 +0530 Subject: [PATCH 03/11] Make a bbox and move content according to that --- .../messages/portfolio/portfolio_message.rs | 3 + .../portfolio/portfolio_message_handler.rs | 99 +++++++++++-------- .../src/graphic_element/renderer/quad.rs | 2 +- 3 files changed, 64 insertions(+), 40 deletions(-) diff --git a/editor/src/messages/portfolio/portfolio_message.rs b/editor/src/messages/portfolio/portfolio_message.rs index f60f98d7c1..6364dc171b 100644 --- a/editor/src/messages/portfolio/portfolio_message.rs +++ b/editor/src/messages/portfolio/portfolio_message.rs @@ -88,6 +88,9 @@ pub enum PortfolioMessage { PasteSerializedData { data: String, }, + CenterPastedLayers { + layers: Vec, + }, PasteImage { name: Option, image: Image, diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index 4263a9ca4c..bfd4cb8e09 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -7,6 +7,7 @@ use crate::messages::debug::utility_types::MessageLoggingVerbosity; use crate::messages::dialog::simple_dialogs; use crate::messages::frontend::utility_types::FrontendDocumentDetails; use crate::messages::layout::utility_types::widget_prelude::*; +use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::utility_types::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT}; @@ -19,7 +20,7 @@ use crate::node_graph_executor::{ExportConfig, NodeGraphExecutor}; use graphene_core::renderer::Quad; use bezier_rs::Subpath; -use glam::IVec2; +use glam::{DAffine2, DVec2, IVec2}; use graph_craft::document::value::TaggedValue; use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput}; use graphene_core::text::{Font, TypesettingConfig}; @@ -890,49 +891,69 @@ impl MessageHandler> for PortfolioMes if let Some(document) = self.active_document() { if let Ok(data) = serde_json::from_str::>(&data) { let parent = document.new_layer_parent(false); - let transform = document.metadata().document_to_viewport; - - // Check parent bounds first since children will be pasted at same position - if let Some(parent_bounds) = document.metadata().bounding_box_document(parent) { - let viewport_bounds = Quad::from_box_at_zero(ipp.viewport_bounds.size()); - let quad = transform * Quad::from_box(parent_bounds); - - // Calculate the translation needed to center the parent - let translation = viewport_bounds.center() - quad.center(); - let centering_transform = quad - .0 - .into_iter() - .all(|point| !viewport_bounds.contains(point)) - .then_some(glam::DAffine2::from_translation(translation.round())); - - let mut added_nodes = false; - - for entry in data.into_iter().rev() { - if !added_nodes { - responses.add(DocumentMessage::DeselectAllLayers); - responses.add(DocumentMessage::AddTransaction); - added_nodes = true; - } + let mut layers = Vec::new(); + + let mut added_nodes = false; + for entry in data.into_iter().rev() { + if !added_nodes { + responses.add(DocumentMessage::DeselectAllLayers); + responses.add(DocumentMessage::AddTransaction); + added_nodes = true; + } + + document.load_layer_resources(responses); + let new_ids: HashMap<_, _> = entry.nodes.iter().map(|(id, _)| (*id, NodeId::new())).collect(); + let layer = LayerNodeIdentifier::new_unchecked(new_ids[&NodeId(0)]); + responses.add(NodeGraphMessage::AddNodes { nodes: entry.nodes, new_ids }); + responses.add(NodeGraphMessage::MoveLayerToStack { layer, parent, insert_index: 0 }); + layers.push(layer); + } - document.load_layer_resources(responses); - let new_ids: HashMap<_, _> = entry.nodes.iter().map(|(id, _)| (*id, NodeId::new())).collect(); - let layer = LayerNodeIdentifier::new_unchecked(new_ids[&NodeId(0)]); - responses.add(NodeGraphMessage::AddNodes { nodes: entry.nodes, new_ids }); - responses.add(NodeGraphMessage::MoveLayerToStack { layer, parent, insert_index: 0 }); - - // Apply the same translation to all layers - if let Some(transform) = centering_transform { - responses.add(GraphOperationMessage::TransformChange { - layer, - transform, - transform_in: TransformIn::Viewport, - skip_rerender: false, - }); + responses.add(NodeGraphMessage::RunDocumentGraph); + responses.add(PortfolioMessage::CenterPastedLayers { layers }); + } + } + } + PortfolioMessage::CenterPastedLayers { layers } => { + use crate::messages::portfolio::document::graph_operation::transform_utils; + if let Some(document) = self.active_document_mut() { + let viewport_bounds = Quad::from_box_at_zero(ipp.viewport_bounds.size()); + let viewport_center = viewport_bounds.center(); + + let mut positions = Vec::new(); + + for layer in &layers { + if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(*layer, &mut document.network_interface, responses) { + if let Some(transform_node_id) = modify_inputs.existing_node_id("Transform", true) { + if let Some(network) = modify_inputs.network_interface.network(&[]) { + if let Some(node) = network.nodes.get(&transform_node_id) { + let current_transform = transform_utils::get_current_transform(&node.inputs); + positions.push((*layer, current_transform.translation)); + } } } - responses.add(NodeGraphMessage::RunDocumentGraph); } } + + if !positions.is_empty() { + let mean_pos = positions.iter().fold(glam::DVec2::ZERO, |acc, (_, pos)| acc + *pos) / positions.len() as f64; + let mut transform = document.metadata().document_to_viewport; + + // Center each layer maintaining relative positions + for (layer, pos) in positions { + let offset_from_center = pos - mean_pos; + let new_pos = viewport_center + transform.transform_vector2(offset_from_center); + transform.translation = new_pos; + + responses.add(GraphOperationMessage::TransformSet { + layer, + transform, + transform_in: TransformIn::Viewport, + skip_rerender: false, + }); + } + responses.add(NodeGraphMessage::RunDocumentGraph); + } } } PortfolioMessage::PasteImage { diff --git a/node-graph/gcore/src/graphic_element/renderer/quad.rs b/node-graph/gcore/src/graphic_element/renderer/quad.rs index 3029394cf4..75efb4a434 100644 --- a/node-graph/gcore/src/graphic_element/renderer/quad.rs +++ b/node-graph/gcore/src/graphic_element/renderer/quad.rs @@ -39,7 +39,7 @@ impl Quad { } /// Create a box starting at (0, 0) upto [`point`] - pub fn from_box_at_zero(point: DVec2) -> Self{ + pub fn from_box_at_zero(point: DVec2) -> Self { Self::from_box([DVec2::ZERO, point]) } From 134e191dac591cf075eac177acd77530942e4dae Mon Sep 17 00:00:00 2001 From: mtvare6 Date: Tue, 4 Mar 2025 18:52:42 +0530 Subject: [PATCH 04/11] partial fix --- .../portfolio/portfolio_message_handler.rs | 54 ++++++++++++------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index bfd4cb8e09..91502eb07c 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -922,13 +922,19 @@ impl MessageHandler> for PortfolioMes let mut positions = Vec::new(); - for layer in &layers { - if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(*layer, &mut document.network_interface, responses) { - if let Some(transform_node_id) = modify_inputs.existing_node_id("Transform", true) { - if let Some(network) = modify_inputs.network_interface.network(&[]) { - if let Some(node) = network.nodes.get(&transform_node_id) { - let current_transform = transform_utils::get_current_transform(&node.inputs); - positions.push((*layer, current_transform.translation)); + for &layer in &layers { + if document.network_interface.is_artboard(&layer.to_node(), &[]) { + if let Some(bounds) = document.metadata().bounding_box_document(layer) { + positions.push((layer, bounds[0], true)); + } + } else { + if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, &mut document.network_interface, responses) { + if let Some(transform_node_id) = modify_inputs.existing_node_id("Transform", true) { + if let Some(network) = modify_inputs.network_interface.network(&[]) { + if let Some(node) = network.nodes.get(&transform_node_id) { + let current_transform = transform_utils::get_current_transform(&node.inputs); + positions.push((layer, current_transform.translation, false)); + } } } } @@ -936,22 +942,34 @@ impl MessageHandler> for PortfolioMes } if !positions.is_empty() { - let mean_pos = positions.iter().fold(glam::DVec2::ZERO, |acc, (_, pos)| acc + *pos) / positions.len() as f64; - let mut transform = document.metadata().document_to_viewport; + let mean_pos = positions.iter().fold(glam::DVec2::ZERO, |acc, (_, pos, _)| acc + *pos) / positions.len() as f64; + let transform = document.metadata().document_to_viewport; - // Center each layer maintaining relative positions - for (layer, pos) in positions { + for (layer, pos, is_artboard) in positions { let offset_from_center = pos - mean_pos; let new_pos = viewport_center + transform.transform_vector2(offset_from_center); - transform.translation = new_pos; - responses.add(GraphOperationMessage::TransformSet { - layer, - transform, - transform_in: TransformIn::Viewport, - skip_rerender: false, - }); + if is_artboard { + if let Some(bounds) = document.metadata().bounding_box_document(layer) { + let dimensions = (bounds[1] - bounds[0]).round().as_ivec2(); + responses.add(GraphOperationMessage::ResizeArtboard { + layer, + location: new_pos.round().as_ivec2(), + dimensions, + }); + } + } else { + let mut new_transform = transform; + new_transform.translation = new_pos; + responses.add(GraphOperationMessage::TransformSet { + layer, + transform: new_transform, + transform_in: TransformIn::Viewport, + skip_rerender: false, + }); + } } + responses.add(NodeGraphMessage::RunDocumentGraph); } } From 69b42cc407426ea182ad01a70128feca639da841 Mon Sep 17 00:00:00 2001 From: mtvare6 Date: Tue, 4 Mar 2025 22:56:54 +0530 Subject: [PATCH 05/11] Only move if none within viewport --- .../portfolio/portfolio_message_handler.rs | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index 91502eb07c..0537e8667f 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -20,7 +20,7 @@ use crate::node_graph_executor::{ExportConfig, NodeGraphExecutor}; use graphene_core::renderer::Quad; use bezier_rs::Subpath; -use glam::{DAffine2, DVec2, IVec2}; +use glam::{DVec2, IVec2}; use graph_craft::document::value::TaggedValue; use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput}; use graphene_core::text::{Font, TypesettingConfig}; @@ -915,16 +915,29 @@ impl MessageHandler> for PortfolioMes } } PortfolioMessage::CenterPastedLayers { layers } => { - use crate::messages::portfolio::document::graph_operation::transform_utils; if let Some(document) = self.active_document_mut() { let viewport_bounds = Quad::from_box_at_zero(ipp.viewport_bounds.size()); let viewport_center = viewport_bounds.center(); + let transform = document.metadata().document_to_viewport; + + let viewport_points = Quad::from_box_at_zero(ipp.viewport_bounds.size()).0; + + let inv_transform = transform.inverse(); + let doc_viewport_points = viewport_points.map(|p| inv_transform.transform_point2(p)); + let viewport_in_doc_space = Quad(doc_viewport_points); let mut positions = Vec::new(); for &layer in &layers { if document.network_interface.is_artboard(&layer.to_node(), &[]) { if let Some(bounds) = document.metadata().bounding_box_document(layer) { + let artboard_quad = Quad::from_box(bounds); + + // Only center if nothing is in the viewport + if artboard_quad.intersects(viewport_in_doc_space) { + return; + } + positions.push((layer, bounds[0], true)); } } else { @@ -933,6 +946,12 @@ impl MessageHandler> for PortfolioMes if let Some(network) = modify_inputs.network_interface.network(&[]) { if let Some(node) = network.nodes.get(&transform_node_id) { let current_transform = transform_utils::get_current_transform(&node.inputs); + + // Only center if nothing is in the viewport + if viewport_in_doc_space.contains(current_transform.translation) { + return; + } + positions.push((layer, current_transform.translation, false)); } } @@ -943,7 +962,6 @@ impl MessageHandler> for PortfolioMes if !positions.is_empty() { let mean_pos = positions.iter().fold(glam::DVec2::ZERO, |acc, (_, pos, _)| acc + *pos) / positions.len() as f64; - let transform = document.metadata().document_to_viewport; for (layer, pos, is_artboard) in positions { let offset_from_center = pos - mean_pos; @@ -974,6 +992,7 @@ impl MessageHandler> for PortfolioMes } } } + PortfolioMessage::PasteImage { name, image, From e7240509fac7f9e5753f02b876d1df7687bf079b Mon Sep 17 00:00:00 2001 From: mtvare6 Date: Tue, 4 Mar 2025 23:01:33 +0530 Subject: [PATCH 06/11] Fix import error --- editor/src/messages/portfolio/portfolio_message_handler.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index 0537e8667f..a63d85cc1b 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -1,3 +1,4 @@ +use super::document::graph_operation::transform_utils; use super::document::utility_types::document_metadata::LayerNodeIdentifier; use super::document::utility_types::network_interface::{self, InputConnector, OutputConnector}; use super::utility_types::{PanelType, PersistentData}; @@ -20,7 +21,7 @@ use crate::node_graph_executor::{ExportConfig, NodeGraphExecutor}; use graphene_core::renderer::Quad; use bezier_rs::Subpath; -use glam::{DVec2, IVec2}; +use glam::IVec2; use graph_craft::document::value::TaggedValue; use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput}; use graphene_core::text::{Font, TypesettingConfig}; From b178ed434efb5378b20d67fd39d8045e94e286b2 Mon Sep 17 00:00:00 2001 From: mtvare6 Date: Tue, 4 Mar 2025 23:49:28 +0530 Subject: [PATCH 07/11] Fix artboard --- .../portfolio/portfolio_message_handler.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index a63d85cc1b..7623bbe60e 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -911,6 +911,7 @@ impl MessageHandler> for PortfolioMes } responses.add(NodeGraphMessage::RunDocumentGraph); + responses.add(Message::StartBuffer); responses.add(PortfolioMessage::CenterPastedLayers { layers }); } } @@ -921,11 +922,8 @@ impl MessageHandler> for PortfolioMes let viewport_center = viewport_bounds.center(); let transform = document.metadata().document_to_viewport; - let viewport_points = Quad::from_box_at_zero(ipp.viewport_bounds.size()).0; - - let inv_transform = transform.inverse(); - let doc_viewport_points = viewport_points.map(|p| inv_transform.transform_point2(p)); - let viewport_in_doc_space = Quad(doc_viewport_points); + let viewport_in_doc_space = transform.inverse() * viewport_bounds; + let viewport_center_in_doc_space = transform.inverse().transform_point2(viewport_center); let mut positions = Vec::new(); @@ -963,21 +961,24 @@ impl MessageHandler> for PortfolioMes if !positions.is_empty() { let mean_pos = positions.iter().fold(glam::DVec2::ZERO, |acc, (_, pos, _)| acc + *pos) / positions.len() as f64; + let doc_space_translation = viewport_center_in_doc_space - mean_pos; for (layer, pos, is_artboard) in positions { - let offset_from_center = pos - mean_pos; - let new_pos = viewport_center + transform.transform_vector2(offset_from_center); - if is_artboard { if let Some(bounds) = document.metadata().bounding_box_document(layer) { let dimensions = (bounds[1] - bounds[0]).round().as_ivec2(); + let new_artboard_pos = pos + doc_space_translation - bounds[1].midpoint(bounds[0]); + responses.add(GraphOperationMessage::ResizeArtboard { layer, - location: new_pos.round().as_ivec2(), + location: new_artboard_pos.round().as_ivec2(), dimensions, }); } } else { + let offset_from_center = pos - mean_pos; + let new_pos = viewport_center + transform.transform_vector2(offset_from_center); + let mut new_transform = transform; new_transform.translation = new_pos; responses.add(GraphOperationMessage::TransformSet { From 7cd5abc2d49a20df9e5b081860c03cbc9b2c933f Mon Sep 17 00:00:00 2001 From: mtvare6 Date: Wed, 5 Mar 2025 13:17:14 +0530 Subject: [PATCH 08/11] tmp, skip ones being added to ab --- .../portfolio/portfolio_message_handler.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index 7623bbe60e..e6f9455ad6 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -926,6 +926,7 @@ impl MessageHandler> for PortfolioMes let viewport_center_in_doc_space = transform.inverse().transform_point2(viewport_center); let mut positions = Vec::new(); + let mut artboards = Vec::new(); // Track artboards separately for &layer in &layers { if document.network_interface.is_artboard(&layer.to_node(), &[]) { @@ -938,8 +939,20 @@ impl MessageHandler> for PortfolioMes } positions.push((layer, bounds[0], true)); + artboards.push(layer); // Add to artboards list } } else { + // Skip layers that are children of artboards we're already moving + let is_child_of_moving_artboard = artboards.iter().any(|&artboard| { + layer.ancestors(document.metadata()) + .skip(1) // Skip self + .any(|ancestor| ancestor == artboard) + }); + + if is_child_of_moving_artboard { + continue; // Skip this layer as its parent artboard will be moved + } + if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, &mut document.network_interface, responses) { if let Some(transform_node_id) = modify_inputs.existing_node_id("Transform", true) { if let Some(network) = modify_inputs.network_interface.network(&[]) { @@ -994,7 +1007,6 @@ impl MessageHandler> for PortfolioMes } } } - PortfolioMessage::PasteImage { name, image, From f0fcd1b983c9558e65edc62ee76695a771b6ee11 Mon Sep 17 00:00:00 2001 From: mtvare6 Date: Mon, 19 May 2025 16:43:28 +0530 Subject: [PATCH 09/11] fix stuff --- .../portfolio/portfolio_message_handler.rs | 158 ++++++++++-------- 1 file changed, 85 insertions(+), 73 deletions(-) diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index 5181368f33..7a7750a0ef 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -1,4 +1,3 @@ -use super::document::graph_operation::transform_utils; use super::document::utility_types::document_metadata::LayerNodeIdentifier; use super::document::utility_types::network_interface::{self, InputConnector, OutputConnector}; use super::spreadsheet::SpreadsheetMessageHandler; @@ -11,7 +10,6 @@ use crate::messages::dialog::simple_dialogs; use crate::messages::frontend::utility_types::FrontendDocumentDetails; use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::portfolio::document::DocumentMessageData; -use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; use crate::messages::portfolio::document::utility_types::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT}; @@ -21,7 +19,7 @@ use crate::messages::prelude::*; use crate::messages::tool::utility_types::{HintData, HintGroup, ToolType}; use crate::node_graph_executor::{ExportConfig, NodeGraphExecutor}; use bezier_rs::Subpath; -use glam::IVec2; +use glam::{DVec2, IVec2, DAffine2}; use graph_craft::document::value::TaggedValue; use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput}; use graphene_core::renderer::Quad; @@ -1027,93 +1025,107 @@ impl MessageHandler> for PortfolioMes } PortfolioMessage::CenterPastedLayers { layers } => { if let Some(document) = self.active_document_mut() { - let viewport_bounds = Quad::from_box_at_zero(ipp.viewport_bounds.size()); - let viewport_center = viewport_bounds.center(); - let transform = document.metadata().document_to_viewport; + let viewport_bounds_quad_pixels = Quad::from_box_at_zero(ipp.viewport_bounds.size()); + let viewport_center_pixels = viewport_bounds_quad_pixels.center(); // In viewport pixel coordinates - let viewport_in_doc_space = transform.inverse() * viewport_bounds; - let viewport_center_in_doc_space = transform.inverse().transform_point2(viewport_center); + let doc_to_viewport_transform = document.metadata().document_to_viewport; + let viewport_to_doc_transform = doc_to_viewport_transform.inverse(); - let mut positions = Vec::new(); - let mut artboards = Vec::new(); // Track artboards separately + let viewport_quad_doc_space = viewport_to_doc_transform * viewport_bounds_quad_pixels; - for &layer in &layers { - if document.network_interface.is_artboard(&layer.to_node(), &[]) { - if let Some(bounds) = document.metadata().bounding_box_document(layer) { - let artboard_quad = Quad::from_box(bounds); + let mut top_level_items_to_center: Vec = Vec::new(); + let mut artboards_in_selection: Vec = Vec::new(); - // Only center if nothing is in the viewport - if artboard_quad.intersects(viewport_in_doc_space) { - return; - } - - positions.push((layer, bounds[0], true)); - artboards.push(layer); // Add to artboards list - } - } else { - // Skip layers that are children of artboards we're already moving - let is_child_of_moving_artboard = artboards.iter().any(|&artboard| { - layer.ancestors(document.metadata()) - .skip(1) // Skip self - .any(|ancestor| ancestor == artboard) - }); + for &layer_id in &layers { + if document.network_interface.is_artboard(&layer_id.to_node(), &document.node_graph_handler.network) { + artboards_in_selection.push(layer_id); + } + } - if is_child_of_moving_artboard { - continue; // Skip this layer as its parent artboard will be moved + for &layer_id in &layers { + let is_child_of_selected_artboard = artboards_in_selection.iter().any(|&artboard_id| { + if layer_id == artboard_id { + return false; } + layer_id.ancestors(document.metadata()).any(|ancestor| ancestor == artboard_id) + }); - if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, &mut document.network_interface, responses) { - if let Some(transform_node_id) = modify_inputs.existing_node_id("Transform", true) { - let network = modify_inputs.network_interface.document_network(); + if !is_child_of_selected_artboard { + top_level_items_to_center.push(layer_id); + } + } - if let Some(node) = network.nodes.get(&transform_node_id) { - let current_transform = transform_utils::get_current_transform(&node.inputs); + if top_level_items_to_center.is_empty() { + return; + } - // Only center if nothing is in the viewport - if viewport_in_doc_space.contains(current_transform.translation) { - return; - } + let mut combined_min_doc = DVec2::MAX; + let mut combined_max_doc = DVec2::MIN; + let mut has_any_bounds = false; - positions.push((layer, current_transform.translation, false)); - } - } - } + for &item_id in &top_level_items_to_center { + if let Some(bounds_doc) = document.metadata().bounding_box_document(item_id) { + combined_min_doc = combined_min_doc.min(bounds_doc[0]); + combined_max_doc = combined_max_doc.max(bounds_doc[1]); + has_any_bounds = true; } } - if !positions.is_empty() { - let mean_pos = positions.iter().fold(glam::DVec2::ZERO, |acc, (_, pos, _)| acc + *pos) / positions.len() as f64; - let doc_space_translation = viewport_center_in_doc_space - mean_pos; - - for (layer, pos, is_artboard) in positions { - if is_artboard { - if let Some(bounds) = document.metadata().bounding_box_document(layer) { - let dimensions = (bounds[1] - bounds[0]).round().as_ivec2(); - let new_artboard_pos = pos + doc_space_translation - bounds[1].midpoint(bounds[0]); - - responses.add(GraphOperationMessage::ResizeArtboard { - layer, - location: new_artboard_pos.round().as_ivec2(), - dimensions, - }); - } - } else { - let offset_from_center = pos - mean_pos; - let new_pos = viewport_center + transform.transform_vector2(offset_from_center); - - let mut new_transform = transform; - new_transform.translation = new_pos; - responses.add(GraphOperationMessage::TransformSet { - layer, - transform: new_transform, - transform_in: TransformIn::Viewport, - skip_rerender: false, + if !has_any_bounds { + return; + } + + let combined_bounds_doc_quad = Quad::from_box([combined_min_doc, combined_max_doc]); + + if combined_bounds_doc_quad.intersects(viewport_quad_doc_space) { + return; + } + + let combined_center_doc = combined_bounds_doc_quad.center(); + let combined_center_viewport_pixels = doc_to_viewport_transform.transform_point2(combined_center_doc); + let translation_viewport_pixels_rounded = (viewport_center_pixels - combined_center_viewport_pixels).round(); + + let final_translation_offset_doc = viewport_to_doc_transform.transform_vector2(translation_viewport_pixels_rounded); + + if final_translation_offset_doc.abs_diff_eq(glam::DVec2::ZERO, 1e-9) { + return; + } + + responses.add(DocumentMessage::AddTransaction); + + for &item_id in &top_level_items_to_center { + if document.network_interface.is_artboard(&item_id.to_node(), &document.node_graph_handler.network) { + if let Some(bounds_doc) = document.metadata().bounding_box_document(item_id) { + let current_artboard_origin_doc = bounds_doc[0]; + let dimensions_doc = bounds_doc[1] - bounds_doc[0]; + let new_artboard_origin_doc = current_artboard_origin_doc + final_translation_offset_doc; + + responses.add(GraphOperationMessage::ResizeArtboard { + layer: item_id, + location: new_artboard_origin_doc.round().as_ivec2(), + dimensions: dimensions_doc.round().as_ivec2(), }); } - } + } else { + let current_abs_doc_transform = document.metadata().transform_to_document(item_id); - responses.add(NodeGraphMessage::RunDocumentGraph); + let new_abs_doc_transform = DAffine2 { + matrix2: current_abs_doc_transform.matrix2, + translation: current_abs_doc_transform.translation + final_translation_offset_doc, + }; + + let transform = doc_to_viewport_transform * new_abs_doc_transform; + + responses.add(GraphOperationMessage::TransformSet { + layer: item_id, + transform, + transform_in: TransformIn::Viewport, + skip_rerender: false, + }); + } } + + responses.add(NodeGraphMessage::RunDocumentGraph); } } PortfolioMessage::PasteImage { From 58effb83b3a1564b47d972d9bcc3103f985aac7c Mon Sep 17 00:00:00 2001 From: mtvare6 Date: Mon, 19 May 2025 20:36:19 +0530 Subject: [PATCH 10/11] fix formatting --- editor/src/messages/portfolio/portfolio_message_handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index 2815763516..45b19a1eba 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -19,7 +19,7 @@ use crate::messages::prelude::*; use crate::messages::tool::utility_types::{HintData, HintGroup, ToolType}; use crate::node_graph_executor::{ExportConfig, NodeGraphExecutor}; use bezier_rs::Subpath; -use glam::{DVec2, IVec2, DAffine2}; +use glam::{DAffine2, DVec2, IVec2}; use graph_craft::document::value::TaggedValue; use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput}; use graphene_core::renderer::Quad; From 791267366000ed4c90c78e887209103e38643e8a Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Mon, 19 May 2025 18:00:56 -0700 Subject: [PATCH 11/11] Code review --- editor/src/messages/portfolio/portfolio_message_handler.rs | 3 ++- node-graph/gcore/src/graphic_element/renderer/quad.rs | 5 ----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index 45b19a1eba..1b6ab69355 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -1008,6 +1008,7 @@ impl MessageHandler> for PortfolioMes let mut layers = Vec::new(); let mut added_nodes = false; + for entry in data.into_iter().rev() { if !added_nodes { responses.add(DocumentMessage::DeselectAllLayers); @@ -1031,7 +1032,7 @@ impl MessageHandler> for PortfolioMes } PortfolioMessage::CenterPastedLayers { layers } => { if let Some(document) = self.active_document_mut() { - let viewport_bounds_quad_pixels = Quad::from_box_at_zero(ipp.viewport_bounds.size()); + let viewport_bounds_quad_pixels = Quad::from_box([DVec2::ZERO, ipp.viewport_bounds.size()]); let viewport_center_pixels = viewport_bounds_quad_pixels.center(); // In viewport pixel coordinates let doc_to_viewport_transform = document.metadata().document_to_viewport; diff --git a/node-graph/gcore/src/graphic_element/renderer/quad.rs b/node-graph/gcore/src/graphic_element/renderer/quad.rs index 48cabaae66..d0c3f2d5ad 100644 --- a/node-graph/gcore/src/graphic_element/renderer/quad.rs +++ b/node-graph/gcore/src/graphic_element/renderer/quad.rs @@ -38,11 +38,6 @@ impl Quad { Self([bbox[0], bbox[0] + size * DVec2::X, bbox[1], bbox[0] + size * DVec2::Y]) } - /// Create a box starting at (0, 0) upto [`point`] - pub fn from_box_at_zero(point: DVec2) -> Self { - Self::from_box([DVec2::ZERO, point]) - } - /// Create a quad from the center and offset (distance from center to middle of an edge) pub fn from_square(center: DVec2, offset: f64) -> Self { Self::from_box([center - offset, center + offset])