mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-21 12:28:12 +08:00
Code cleanup after migrating node graph interaction to the backend (#1790)
* Cleanup transforms and click targets * Improve click target caching * Fix click target bugs * Add auto panning * update_modified_click_targets * Code review --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
Keavon Chambers
parent
6fd5b76430
commit
84ac2e274e
@@ -100,6 +100,7 @@ pub enum DocumentMessage {
|
||||
},
|
||||
RenderRulers,
|
||||
RenderScrollbars,
|
||||
ResetTransform,
|
||||
SaveDocument,
|
||||
SelectAllLayers,
|
||||
SelectedLayersLower,
|
||||
|
||||
@@ -31,8 +31,6 @@ use graphene_std::vector::style::{Fill, FillType, Gradient};
|
||||
|
||||
use glam::{DAffine2, DVec2, IVec2};
|
||||
|
||||
use std::vec;
|
||||
|
||||
pub struct DocumentMessageData<'a> {
|
||||
pub document_id: DocumentId,
|
||||
pub ipp: &'a InputPreprocessorMessageHandler,
|
||||
@@ -74,9 +72,7 @@ pub struct DocumentMessageHandler {
|
||||
/// We save this to provide a hint about which version of the editor was used to create the document.
|
||||
commit_hash: String,
|
||||
/// The current pan, tilt, and zoom state of the viewport's view of the document canvas.
|
||||
pub navigation: PTZ,
|
||||
/// The current pan, and zoom state of the viewport's view of the node graph.
|
||||
node_graph_transform: PTZ,
|
||||
pub document_ptz: PTZ,
|
||||
/// The current mode that the document is in, which starts out as Design Mode. This choice affects the editing behavior of the tools.
|
||||
document_mode: DocumentMode,
|
||||
/// The current view mode that the user has set for rendering the document within the viewport.
|
||||
@@ -119,6 +115,12 @@ pub struct DocumentMessageHandler {
|
||||
/// This is updated frequently, whenever the information it's derived from changes.
|
||||
#[serde(skip)]
|
||||
pub metadata: DocumentMetadata,
|
||||
/// The current pan, and zoom state of the viewport's view of the node graph.
|
||||
#[serde(skip)]
|
||||
node_graph_ptz: HashMap<Vec<NodeId>, PTZ>,
|
||||
/// Transform from node graph space to viewport space.
|
||||
#[serde(skip)]
|
||||
node_graph_to_viewport: HashMap<Vec<NodeId>, DAffine2>,
|
||||
}
|
||||
|
||||
impl Default for DocumentMessageHandler {
|
||||
@@ -139,8 +141,7 @@ impl Default for DocumentMessageHandler {
|
||||
collapsed: CollapsedLayers::default(),
|
||||
name: DEFAULT_DOCUMENT_NAME.to_string(),
|
||||
commit_hash: GRAPHITE_GIT_COMMIT_HASH.to_string(),
|
||||
navigation: PTZ::default(),
|
||||
node_graph_transform: PTZ::default(),
|
||||
document_ptz: PTZ::default(),
|
||||
document_mode: DocumentMode::DesignMode,
|
||||
view_mode: ViewMode::default(),
|
||||
overlays_visible: true,
|
||||
@@ -157,6 +158,8 @@ impl Default for DocumentMessageHandler {
|
||||
undo_in_progress: false,
|
||||
layer_range_selection_reference: None,
|
||||
metadata: Default::default(),
|
||||
node_graph_ptz: HashMap::new(),
|
||||
node_graph_to_viewport: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -181,10 +184,11 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||
} else {
|
||||
self.selected_visible_layers_bounding_box_viewport()
|
||||
},
|
||||
ptz: if self.graph_view_overlay_open { &mut self.node_graph_transform } else { &mut self.navigation },
|
||||
document_ptz: &mut self.document_ptz,
|
||||
node_graph_ptz: &mut self.node_graph_ptz,
|
||||
graph_view_overlay_open: self.graph_view_overlay_open,
|
||||
document_network: &self.network,
|
||||
node_graph_handler: &self.node_graph_handler,
|
||||
node_graph_to_viewport: &self.node_graph_to_viewport.entry(self.node_graph_handler.network.clone()).or_insert(DAffine2::IDENTITY),
|
||||
};
|
||||
|
||||
self.navigation_handler.process_message(message, responses, data);
|
||||
@@ -218,6 +222,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||
collapsed: &mut self.collapsed,
|
||||
ipp,
|
||||
graph_view_overlay_open: self.graph_view_overlay_open,
|
||||
node_graph_to_viewport: &self.node_graph_to_viewport.entry(self.node_graph_handler.network.clone()).or_insert(DAffine2::IDENTITY),
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -378,7 +383,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||
DocumentMessage::GraphViewOverlay { open } => {
|
||||
self.graph_view_overlay_open = open;
|
||||
|
||||
// TODO: Find a better way to update click targets when undoing/redoing
|
||||
// TODO: Update click targets when node graph is closed so that this is not necessary
|
||||
if self.graph_view_overlay_open {
|
||||
self.node_graph_handler.update_all_click_targets(&mut self.network, self.node_graph_handler.network.clone())
|
||||
}
|
||||
@@ -735,16 +740,15 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||
responses.add(NodeGraphMessage::UpdateNewNodeGraph);
|
||||
}
|
||||
DocumentMessage::RenderRulers => {
|
||||
let document_transform_scale = self.navigation_handler.snapped_zoom(self.navigation.zoom);
|
||||
let document_transform_scale = self.navigation_handler.snapped_zoom(self.document_ptz.zoom);
|
||||
|
||||
let ruler_origin = if !self.graph_view_overlay_open {
|
||||
self.metadata().document_to_viewport.transform_point2(DVec2::ZERO)
|
||||
} else {
|
||||
let Some(network) = self.network.nested_network(&self.node_graph_handler.network) else {
|
||||
log::error!("Nested network not found in UpdateDocumentTransform");
|
||||
return;
|
||||
};
|
||||
network.node_graph_to_viewport.transform_point2(DVec2::ZERO)
|
||||
self.node_graph_to_viewport
|
||||
.get(&self.node_graph_handler.network)
|
||||
.unwrap_or(&DAffine2::IDENTITY)
|
||||
.transform_point2(DVec2::ZERO)
|
||||
};
|
||||
let log = document_transform_scale.log2();
|
||||
let ruler_interval: f64 = if log < 0. { 100. * 2_f64.powf(-log.ceil()) } else { 100. / 2_f64.powf(log.ceil()) };
|
||||
@@ -758,7 +762,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||
});
|
||||
}
|
||||
DocumentMessage::RenderScrollbars => {
|
||||
let document_transform_scale = self.navigation_handler.snapped_zoom(self.navigation.zoom);
|
||||
let document_transform_scale = self.navigation_handler.snapped_zoom(self.document_ptz.zoom);
|
||||
|
||||
let scale = 0.5 + ASYMPTOTIC_EFFECT + document_transform_scale * SCALE_EFFECT;
|
||||
|
||||
@@ -767,7 +771,10 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||
let [bounds1, bounds2] = if !self.graph_view_overlay_open {
|
||||
self.metadata().document_bounds_viewport_space().unwrap_or([viewport_mid; 2])
|
||||
} else {
|
||||
self.node_graph_handler.graph_bounds_viewport_space(&self.network).unwrap_or([viewport_mid; 2])
|
||||
self.node_graph_to_viewport
|
||||
.get(&self.node_graph_handler.network)
|
||||
.and_then(|node_graph_to_viewport| self.node_graph_handler.graph_bounds_viewport_space(*node_graph_to_viewport))
|
||||
.unwrap_or([viewport_mid; 2])
|
||||
};
|
||||
let bounds1 = bounds1.min(viewport_mid) - viewport_size * scale;
|
||||
let bounds2 = bounds2.max(viewport_mid) + viewport_size * scale;
|
||||
@@ -1086,6 +1093,14 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||
responses.add(DocumentMessage::DocumentStructureChanged);
|
||||
responses.add(NodeGraphMessage::SendGraph);
|
||||
}
|
||||
DocumentMessage::ResetTransform => {
|
||||
let transform = if self.graph_view_overlay_open {
|
||||
*self.node_graph_to_viewport.entry(self.node_graph_handler.network.clone()).or_insert(DAffine2::IDENTITY)
|
||||
} else {
|
||||
self.metadata.document_to_viewport
|
||||
};
|
||||
responses.add(DocumentMessage::UpdateDocumentTransform { transform });
|
||||
}
|
||||
DocumentMessage::UpdateDocumentTransform { transform } => {
|
||||
responses.add(DocumentMessage::RenderRulers);
|
||||
responses.add(DocumentMessage::RenderScrollbars);
|
||||
@@ -1095,11 +1110,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
} else {
|
||||
let Some(network) = self.network.nested_network_mut(&self.node_graph_handler.network) else {
|
||||
log::error!("Nested network not found in UpdateDocumentTransform");
|
||||
return;
|
||||
};
|
||||
network.node_graph_to_viewport = transform;
|
||||
self.node_graph_to_viewport.insert(self.node_graph_handler.network.clone(), transform);
|
||||
|
||||
responses.add(FrontendMessage::UpdateNodeGraphTransform {
|
||||
transform: Transform {
|
||||
@@ -1120,10 +1131,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||
}
|
||||
DocumentMessage::ZoomCanvasToFitAll => {
|
||||
let bounds = if self.graph_view_overlay_open {
|
||||
self.node_graph_handler
|
||||
.network_metadata
|
||||
.get(&self.node_graph_handler.network)
|
||||
.and_then(|network_metadata| network_metadata.bounding_box_subpath.as_ref().and_then(|subpath| subpath.bounding_box()))
|
||||
self.node_graph_handler.bounding_box_subpath.as_ref().and_then(|subpath| subpath.bounding_box())
|
||||
} else {
|
||||
self.metadata().document_bounds_document_space(true)
|
||||
};
|
||||
@@ -1274,17 +1282,15 @@ impl DocumentMessageHandler {
|
||||
self.selected_nodes
|
||||
.selected_nodes(network)
|
||||
.filter_map(|node| {
|
||||
let mut node_path = self.node_graph_handler.network.clone();
|
||||
node_path.push(*node);
|
||||
let Some(node_metadata) = self.node_graph_handler.node_metadata.get(&node_path) else {
|
||||
let Some(node_metadata) = self.node_graph_handler.node_metadata.get(&node) else {
|
||||
log::debug!("Could not get click target for node {node}");
|
||||
return None;
|
||||
};
|
||||
let Some(network_metadata) = self.node_graph_handler.network_metadata.get(&self.node_graph_handler.network) else {
|
||||
log::debug!("Could not get network_metadata in selected_nodes_bounding_box_viewport");
|
||||
let Some(node_graph_to_viewport) = self.node_graph_to_viewport.get(&self.node_graph_handler.network) else {
|
||||
log::debug!("Could not get node_graph_to_viewport for network: {:?}", self.node_graph_handler.network);
|
||||
return None;
|
||||
};
|
||||
node_metadata.node_click_target.subpath.bounding_box_with_transform(network_metadata.node_graph_to_viewport)
|
||||
node_metadata.node_click_target.subpath.bounding_box_with_transform(*node_graph_to_viewport)
|
||||
})
|
||||
.reduce(graphene_core::renderer::Quad::combine_bounds)
|
||||
}
|
||||
@@ -1500,14 +1506,12 @@ impl DocumentMessageHandler {
|
||||
pub fn undo_with_history(&mut self, responses: &mut VecDeque<Message>) {
|
||||
let Some(previous_network) = self.undo(responses) else { return };
|
||||
|
||||
self.update_modified_click_targets(&previous_network);
|
||||
|
||||
self.document_redo_history.push_back(previous_network);
|
||||
if self.document_redo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
||||
self.document_redo_history.pop_front();
|
||||
}
|
||||
// TODO: Find a better way to update click targets when undoing/redoing
|
||||
if self.graph_view_overlay_open {
|
||||
self.node_graph_handler.update_all_click_targets(&mut self.network, self.node_graph_handler.network.clone())
|
||||
}
|
||||
}
|
||||
pub fn undo(&mut self, responses: &mut VecDeque<Message>) -> Option<NodeNetwork> {
|
||||
// Push the UpdateOpenDocumentsList message to the bus in order to update the save status of the open documents
|
||||
@@ -1520,6 +1524,18 @@ impl DocumentMessageHandler {
|
||||
let previous_network = std::mem::replace(&mut self.network, network);
|
||||
Some(previous_network)
|
||||
}
|
||||
pub fn redo_with_history(&mut self, responses: &mut VecDeque<Message>) {
|
||||
// Push the UpdateOpenDocumentsList message to the queue in order to update the save status of the open documents
|
||||
let Some(previous_network) = self.redo(responses) else { return };
|
||||
|
||||
self.update_modified_click_targets(&previous_network);
|
||||
|
||||
self.document_undo_history.push_back(previous_network);
|
||||
if self.document_undo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
||||
self.document_undo_history.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn redo(&mut self, responses: &mut VecDeque<Message>) -> Option<NodeNetwork> {
|
||||
// Push the UpdateOpenDocumentsList message to the bus in order to update the save status of the open documents
|
||||
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
||||
@@ -1531,18 +1547,36 @@ impl DocumentMessageHandler {
|
||||
let previous_network = std::mem::replace(&mut self.network, network);
|
||||
Some(previous_network)
|
||||
}
|
||||
pub fn redo_with_history(&mut self, responses: &mut VecDeque<Message>) {
|
||||
// Push the UpdateOpenDocumentsList message to the bus in order to update the save status of the open documents
|
||||
let Some(previous_network) = self.redo(responses) else { return };
|
||||
|
||||
self.document_undo_history.push_back(previous_network);
|
||||
if self.document_undo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN {
|
||||
self.document_undo_history.pop_front();
|
||||
}
|
||||
// TODO: Find a better way to update click targets when undoing/redoing
|
||||
if self.graph_view_overlay_open {
|
||||
self.node_graph_handler.update_all_click_targets(&mut self.network, self.node_graph_handler.network.clone())
|
||||
pub fn update_modified_click_targets(&mut self, previous_network: &NodeNetwork) {
|
||||
// TODO: Cache nodes that were changed alongside every network in the undo/redo history, although this is complex since undoing to a previous state may change different nodes than redoing to the same state
|
||||
let Some(previous_nested_network) = previous_network.nested_network(&self.node_graph_handler.network) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(network) = self.network.nested_network(&self.node_graph_handler.network) else {
|
||||
log::error!("Could not get nested network in redo_with_history");
|
||||
return;
|
||||
};
|
||||
|
||||
for (node_id, current_node) in &network.nodes {
|
||||
if let Some(previous_node) = previous_nested_network.nodes.get(&node_id) {
|
||||
if previous_node.alias == current_node.alias
|
||||
&& previous_node.inputs.iter().map(|node_input| node_input.is_exposed()).count() == current_node.inputs.iter().map(|node_input| node_input.is_exposed()).count()
|
||||
&& previous_node.is_layer == current_node.is_layer
|
||||
&& previous_node.metadata.position == current_node.metadata.position
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
self.node_graph_handler.update_click_target(*node_id, &self.network, self.node_graph_handler.network.clone());
|
||||
}
|
||||
|
||||
self.node_graph_handler
|
||||
.update_click_target(network.imports_metadata.0, &self.network, self.node_graph_handler.network.clone());
|
||||
self.node_graph_handler
|
||||
.update_click_target(network.exports_metadata.0, &self.network, self.node_graph_handler.network.clone());
|
||||
}
|
||||
|
||||
pub fn current_hash(&self) -> Option<u64> {
|
||||
@@ -1889,7 +1923,7 @@ impl DocumentMessageHandler {
|
||||
.tooltip("Reset Tilt and Zoom to 100%")
|
||||
.tooltip_shortcut(action_keys!(NavigationMessageDiscriminant::CanvasTiltResetAndZoomTo100Percent))
|
||||
.on_update(|_| NavigationMessage::CanvasTiltResetAndZoomTo100Percent.into())
|
||||
.disabled(self.navigation.tilt.abs() < 1e-4 && (self.navigation.zoom - 1.).abs() < 1e-4)
|
||||
.disabled(self.document_ptz.tilt.abs() < 1e-4 && (self.document_ptz.zoom - 1.).abs() < 1e-4)
|
||||
.widget_holder(),
|
||||
PopoverButton::new()
|
||||
.popover_layout(vec![
|
||||
@@ -1920,7 +1954,7 @@ impl DocumentMessageHandler {
|
||||
])
|
||||
.widget_holder(),
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
NumberInput::new(Some(self.navigation_handler.snapped_zoom(self.navigation.zoom) * 100.))
|
||||
NumberInput::new(Some(self.navigation_handler.snapped_zoom(self.document_ptz.zoom) * 100.))
|
||||
.unit("%")
|
||||
.min(0.000001)
|
||||
.max(1000000.)
|
||||
@@ -1937,7 +1971,7 @@ impl DocumentMessageHandler {
|
||||
.widget_holder(),
|
||||
];
|
||||
|
||||
let tilt_value = self.navigation_handler.snapped_tilt(self.navigation.tilt) / (std::f64::consts::PI / 180.);
|
||||
let tilt_value = self.navigation_handler.snapped_tilt(self.document_ptz.tilt) / (std::f64::consts::PI / 180.);
|
||||
if tilt_value.abs() > 0.00001 {
|
||||
widgets.extend([
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
|
||||
+4
-1
@@ -591,7 +591,6 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
|
||||
if let Some(node) = modify_inputs.document_network.nodes.get_mut(&id) {
|
||||
node.alias = alias.clone();
|
||||
}
|
||||
modify_inputs.node_graph.update_click_target(id, &modify_inputs.document_network, Vec::new());
|
||||
|
||||
let shift = nodes
|
||||
.get(&NodeId(0))
|
||||
@@ -615,6 +614,7 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
|
||||
|
||||
// Insert node into network
|
||||
node_graph.insert_node(node_id, document_node, document_network, &Vec::new());
|
||||
node_graph.update_click_target(node_id, document_network, Vec::new());
|
||||
}
|
||||
|
||||
if let Some(layer_node) = document_network.nodes.get_mut(&layer) {
|
||||
@@ -702,6 +702,9 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
|
||||
GraphOperationMessage::SetNameImpl { layer, name } => {
|
||||
if let Some(node) = document_network.nodes.get_mut(&layer.to_node()) {
|
||||
node.alias = name;
|
||||
if let Some(node_metadata) = node_graph.node_metadata.get_mut(&layer.to_node()) {
|
||||
node_metadata.layer_width = Some(NodeGraphMessageHandler::layer_width_cells(node));
|
||||
};
|
||||
node_graph.update_click_target(layer.to_node(), document_network, Vec::new());
|
||||
responses.add(DocumentMessage::RenderRulers);
|
||||
responses.add(DocumentMessage::RenderScrollbars);
|
||||
|
||||
@@ -11,17 +11,19 @@ use crate::messages::portfolio::document::utility_types::misc::PTZ;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||
|
||||
use graph_craft::document::NodeId;
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graph_craft::document::NodeNetwork;
|
||||
|
||||
pub struct NavigationMessageData<'a> {
|
||||
pub metadata: &'a DocumentMetadata,
|
||||
pub ipp: &'a InputPreprocessorMessageHandler,
|
||||
pub selection_bounds: Option<[DVec2; 2]>,
|
||||
pub ptz: &'a mut PTZ,
|
||||
pub document_ptz: &'a mut PTZ,
|
||||
pub node_graph_ptz: &'a mut HashMap<Vec<NodeId>, PTZ>,
|
||||
pub graph_view_overlay_open: bool,
|
||||
pub document_network: &'a NodeNetwork,
|
||||
pub node_graph_handler: &'a NodeGraphMessageHandler,
|
||||
pub node_graph_to_viewport: &'a DAffine2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
@@ -37,11 +39,17 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||
metadata,
|
||||
ipp,
|
||||
selection_bounds,
|
||||
ptz,
|
||||
document_ptz,
|
||||
node_graph_ptz,
|
||||
graph_view_overlay_open,
|
||||
document_network,
|
||||
node_graph_handler,
|
||||
node_graph_to_viewport,
|
||||
} = data;
|
||||
let ptz = if !graph_view_overlay_open {
|
||||
document_ptz
|
||||
} else {
|
||||
node_graph_ptz.entry(node_graph_handler.network.clone()).or_insert(PTZ::default())
|
||||
};
|
||||
let old_zoom = ptz.zoom;
|
||||
|
||||
match message {
|
||||
@@ -112,10 +120,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||
let transformed_delta = if !graph_view_overlay_open {
|
||||
metadata.document_to_viewport.inverse().transform_vector2(delta)
|
||||
} else {
|
||||
let Some(network) = document_network.nested_network(&node_graph_handler.network) else {
|
||||
return;
|
||||
};
|
||||
network.node_graph_to_viewport.inverse().transform_vector2(delta)
|
||||
node_graph_to_viewport.inverse().transform_vector2(delta)
|
||||
};
|
||||
|
||||
ptz.pan += transformed_delta;
|
||||
@@ -126,10 +131,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||
let transformed_delta = if !graph_view_overlay_open {
|
||||
metadata.document_to_viewport.inverse().transform_vector2(delta * ipp.viewport_bounds.size())
|
||||
} else {
|
||||
let Some(network) = document_network.nested_network(&node_graph_handler.network) else {
|
||||
return;
|
||||
};
|
||||
network.node_graph_to_viewport.inverse().transform_vector2(delta * ipp.viewport_bounds.size())
|
||||
node_graph_to_viewport.inverse().transform_vector2(delta * ipp.viewport_bounds.size())
|
||||
};
|
||||
ptz.pan += transformed_delta;
|
||||
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
||||
@@ -175,7 +177,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||
// TODO: Cache this in node graph coordinates and apply the transform to the rectangle to get viewport coordinates
|
||||
metadata.document_bounds_viewport_space()
|
||||
} else {
|
||||
node_graph_handler.graph_bounds_viewport_space(document_network)
|
||||
node_graph_handler.graph_bounds_viewport_space(*node_graph_to_viewport)
|
||||
};
|
||||
zoom_factor *= Self::clamp_zoom(ptz.zoom * zoom_factor, document_bounds, old_zoom, ipp);
|
||||
|
||||
@@ -187,7 +189,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||
// TODO: Cache this in node graph coordinates and apply the transform to the rectangle to get viewport coordinates
|
||||
metadata.document_bounds_viewport_space()
|
||||
} else {
|
||||
node_graph_handler.graph_bounds_viewport_space(document_network)
|
||||
node_graph_handler.graph_bounds_viewport_space(*node_graph_to_viewport)
|
||||
};
|
||||
ptz.zoom = zoom_factor.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX);
|
||||
ptz.zoom *= Self::clamp_zoom(ptz.zoom, document_bounds, old_zoom, ipp);
|
||||
@@ -239,18 +241,12 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||
let v1 = if !graph_view_overlay_open {
|
||||
metadata.document_to_viewport.inverse().transform_point2(DVec2::ZERO)
|
||||
} else {
|
||||
let Some(network) = document_network.nested_network(&node_graph_handler.network) else {
|
||||
return;
|
||||
};
|
||||
network.node_graph_to_viewport.inverse().transform_point2(DVec2::ZERO)
|
||||
node_graph_to_viewport.inverse().transform_point2(DVec2::ZERO)
|
||||
};
|
||||
let v2 = if !graph_view_overlay_open {
|
||||
metadata.document_to_viewport.inverse().transform_point2(ipp.viewport_bounds.size())
|
||||
} else {
|
||||
let Some(network) = document_network.nested_network(&node_graph_handler.network) else {
|
||||
return;
|
||||
};
|
||||
network.node_graph_to_viewport.inverse().transform_point2(ipp.viewport_bounds.size())
|
||||
node_graph_to_viewport.inverse().transform_point2(ipp.viewport_bounds.size())
|
||||
};
|
||||
|
||||
let center = ((v1 + v2) - (pos1 + pos2)) / 2.;
|
||||
@@ -260,10 +256,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||
let viewport_change = if !graph_view_overlay_open {
|
||||
metadata.document_to_viewport.transform_vector2(center)
|
||||
} else {
|
||||
let Some(network) = document_network.nested_network(&node_graph_handler.network) else {
|
||||
return;
|
||||
};
|
||||
network.node_graph_to_viewport.transform_vector2(center)
|
||||
node_graph_to_viewport.transform_vector2(center)
|
||||
};
|
||||
|
||||
// Only change the pan if the change will be visible in the viewport
|
||||
@@ -287,10 +280,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||
let transform = if !graph_view_overlay_open {
|
||||
metadata.document_to_viewport.inverse()
|
||||
} else {
|
||||
let Some(network) = document_network.nested_network(&node_graph_handler.network) else {
|
||||
return;
|
||||
};
|
||||
network.node_graph_to_viewport.inverse()
|
||||
node_graph_to_viewport.inverse()
|
||||
};
|
||||
responses.add(NavigationMessage::FitViewportToBounds {
|
||||
bounds: [transform.transform_point2(bounds[0]), transform.transform_point2(bounds[1])],
|
||||
@@ -344,7 +334,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||
// TODO: Cache this in node graph coordinates and apply the transform to the rectangle to get viewport coordinates
|
||||
metadata.document_bounds_viewport_space()
|
||||
} else {
|
||||
node_graph_handler.graph_bounds_viewport_space(document_network)
|
||||
node_graph_handler.graph_bounds_viewport_space(*node_graph_to_viewport)
|
||||
};
|
||||
|
||||
updated_zoom * Self::clamp_zoom(updated_zoom, document_bounds, old_zoom, ipp)
|
||||
|
||||
@@ -81,6 +81,9 @@ pub enum NodeGraphMessage {
|
||||
shift: Key,
|
||||
},
|
||||
PointerUp,
|
||||
PointerOutsideViewport {
|
||||
shift: Key,
|
||||
},
|
||||
PrintSelectedNodeCoordinates,
|
||||
RunDocumentGraph,
|
||||
SelectedNodesAdd {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::utility_types::{BoxSelection, ContextMenuInformation, DragStart, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendNodeWire, WirePath};
|
||||
use super::utility_types::{BoxSelection, ContextMenuInformation, DragStart, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendNodeWire, NodeMetadata, WirePath};
|
||||
use super::{document_node_types, node_properties};
|
||||
use crate::application::generate_uuid;
|
||||
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
||||
@@ -8,14 +8,15 @@ use crate::messages::portfolio::document::graph_operation::utility_types::Modify
|
||||
use crate::messages::portfolio::document::node_graph::document_node_types::NodePropertiesContext;
|
||||
use crate::messages::portfolio::document::node_graph::utility_types::{ContextMenuData, FrontendGraphDataType};
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
|
||||
use crate::messages::portfolio::document::utility_types::node_metadata::{NetworkMetadata, NodeMetadata};
|
||||
use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, LayerPanelEntry, SelectedNodes};
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
|
||||
|
||||
use bezier_rs::Subpath;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, FlowType, NodeId, NodeInput, NodeNetwork, Previewing, Source};
|
||||
use graph_craft::proto::GraphErrors;
|
||||
use graphene_core::uuid::ManipulatorGroupId;
|
||||
use graphene_core::*;
|
||||
use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypes;
|
||||
|
||||
@@ -33,6 +34,7 @@ pub struct NodeGraphHandlerData<'a> {
|
||||
pub collapsed: &'a mut CollapsedLayers,
|
||||
pub ipp: &'a InputPreprocessorMessageHandler,
|
||||
pub graph_view_overlay_open: bool,
|
||||
pub node_graph_to_viewport: &'a DAffine2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -43,26 +45,25 @@ pub struct NodeGraphMessageHandler {
|
||||
has_selection: bool,
|
||||
widgets: [LayoutGroup; 2],
|
||||
drag_start: Option<DragStart>,
|
||||
// Used to add a transaction for the first node move when dragging
|
||||
/// Used to add a transaction for the first node move when dragging.
|
||||
begin_dragging: bool,
|
||||
// Stored in pixel coordinates
|
||||
/// Stored in pixel coordinates.
|
||||
box_selection_start: Option<UVec2>,
|
||||
disconnecting: Option<(NodeId, usize)>,
|
||||
initial_disconnecting: bool,
|
||||
// Node to select on pointer up if multiple nodes are selected and they were not dragged
|
||||
/// Node to select on pointer up if multiple nodes are selected and they were not dragged.
|
||||
select_if_not_dragged: Option<NodeId>,
|
||||
// The start of the dragged line that cannot be moved. The bool represents if it is a vertical output.
|
||||
/// The start of the dragged line that cannot be moved. The bool represents if it is a vertical output.
|
||||
wire_in_progress_from_connector: Option<(DVec2, bool)>,
|
||||
// The end point of the dragged line that can be moved. The bool represents if it is a vertical input.
|
||||
/// The end point of the dragged line that can be moved. The bool represents if it is a vertical input.
|
||||
wire_in_progress_to_connector: Option<(DVec2, bool)>,
|
||||
// State for the context menu popups
|
||||
/// State for the context menu popups.
|
||||
context_menu: Option<ContextMenuInformation>,
|
||||
/// Click targets for every node in every network by using the path to that node
|
||||
/// TODO: Only store click targets for nodes in the current network
|
||||
pub node_metadata: HashMap<Vec<NodeId>, NodeMetadata>,
|
||||
// Bounding box around all nodes and the node graph to viewport transform for all networks
|
||||
/// TODO: Only store network metadata for the network that is being viewed
|
||||
pub network_metadata: HashMap<Vec<NodeId>, NetworkMetadata>,
|
||||
/// Click targets for every node in the network by using the path to that node.
|
||||
pub node_metadata: HashMap<NodeId, NodeMetadata>,
|
||||
/// Cache for the bounding box around all nodes in node graph space.
|
||||
pub bounding_box_subpath: Option<Subpath<ManipulatorGroupId>>,
|
||||
auto_panning: AutoPanning,
|
||||
}
|
||||
|
||||
/// NodeGraphMessageHandler always modifies the network which the selected nodes are in. No GraphOperationMessages should be added here, since those messages will always affect the document network.
|
||||
@@ -76,6 +77,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||
collapsed,
|
||||
graph_view_overlay_open,
|
||||
ipp,
|
||||
node_graph_to_viewport,
|
||||
..
|
||||
} = data;
|
||||
|
||||
@@ -221,22 +223,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||
responses.add(NodeGraphMessage::InsertNode { node_id, document_node });
|
||||
|
||||
if let Some(wire_in_progress) = self.wire_in_progress_from_connector {
|
||||
let Some((from_node, output_index)) = NodeGraphMessageHandler::get_key_from_point(
|
||||
&self
|
||||
.node_metadata
|
||||
.iter()
|
||||
.filter(|(path, _)| path.starts_with(&self.network) && path.len() == self.network.len() + 1)
|
||||
.flat_map(|(path, node_metadata)| {
|
||||
// TODO: There should be a way to do this without cloning
|
||||
node_metadata
|
||||
.output_click_targets
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, output_click_target)| ((*path.last().expect("Path should not be empty"), index), output_click_target.clone()))
|
||||
})
|
||||
.collect::<HashMap<(NodeId, usize), ClickTarget>>(),
|
||||
wire_in_progress.0,
|
||||
) else {
|
||||
let Some((from_node, output_index)) = self.get_connector_from_point(wire_in_progress.0, |metadata| &metadata.output_click_targets) else {
|
||||
log::error!("Could not get output form connector start");
|
||||
return;
|
||||
};
|
||||
@@ -322,73 +309,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||
}
|
||||
responses.add(NodeGraphMessage::SendGraph);
|
||||
}
|
||||
NodeGraphMessage::EnterNestedNetwork => {
|
||||
let Some(network) = document_network.nested_network_mut(&self.network) else {
|
||||
return;
|
||||
};
|
||||
let viewport_location = ipp.mouse.position;
|
||||
let point = network.node_graph_to_viewport.inverse().transform_point2(viewport_location);
|
||||
let Some(node_id) = NodeGraphMessageHandler::get_key_from_point(
|
||||
&self
|
||||
.node_metadata
|
||||
.iter()
|
||||
.filter_map(|(path, node_metadata)| {
|
||||
let mut path = path.clone();
|
||||
let node_id = path.pop().expect("Path to node should not be empty");
|
||||
if *path == self.network {
|
||||
// TODO: There should be a way to do this without cloning
|
||||
Some((node_id, node_metadata.node_click_target.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<HashMap<NodeId, ClickTarget>>(),
|
||||
point,
|
||||
) else {
|
||||
return;
|
||||
};
|
||||
if NodeGraphMessageHandler::get_key_from_point(
|
||||
&self
|
||||
.node_metadata
|
||||
.iter()
|
||||
.filter_map(|(path, node_metadata)| {
|
||||
let mut path = path.clone();
|
||||
let node_id = path.pop().expect("Path to node should not be empty");
|
||||
if *path == self.network {
|
||||
// TODO: There should be a way to do this without cloning
|
||||
if let Some(visibility_click_target) = &node_metadata.visibility_click_target {
|
||||
Some((node_id, visibility_click_target.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<HashMap<NodeId, ClickTarget>>(),
|
||||
point,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
return;
|
||||
};
|
||||
if network.imports_metadata.0 == node_id || network.exports_metadata.0 == node_id {
|
||||
return;
|
||||
}
|
||||
let Some(node) = network.nodes.get_mut(&node_id) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let DocumentNodeImplementation::Network(_) = node.implementation {
|
||||
self.network.push(node_id);
|
||||
self.update_all_click_targets(document_network, self.network.clone());
|
||||
responses.add(DocumentMessage::ZoomCanvasToFitAll);
|
||||
}
|
||||
|
||||
self.send_graph(document_network, document_metadata, collapsed, graph_view_overlay_open, responses);
|
||||
|
||||
self.update_selected(document_network, selected_nodes, responses);
|
||||
}
|
||||
NodeGraphMessage::DuplicateSelectedNodes => {
|
||||
if let Some(network) = document_network.nested_network_for_selected_nodes(&self.network, selected_nodes.selected_nodes_ref().iter()) {
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
@@ -426,6 +346,35 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||
responses.add(NodeGraphMessage::SetToNodeOrLayer { node_id: node_id, is_layer: false })
|
||||
}
|
||||
}
|
||||
NodeGraphMessage::EnterNestedNetwork => {
|
||||
let Some(network) = document_network.nested_network_mut(&self.network) else { return };
|
||||
|
||||
let viewport_location = ipp.mouse.position;
|
||||
let point = node_graph_to_viewport.inverse().transform_point2(viewport_location);
|
||||
let Some(node_id) = self.get_node_from_point(point) else { return };
|
||||
|
||||
if self.get_visibility_from_point(point).is_some() {
|
||||
return;
|
||||
};
|
||||
if network.imports_metadata.0 == node_id || network.exports_metadata.0 == node_id {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(node) = network.nodes.get_mut(&node_id) else { return };
|
||||
if let DocumentNodeImplementation::Network(_) = node.implementation {
|
||||
self.network.push(node_id);
|
||||
self.node_metadata.clear();
|
||||
|
||||
self.update_all_click_targets(document_network, self.network.clone());
|
||||
|
||||
responses.add(DocumentMessage::ZoomCanvasToFitAll);
|
||||
}
|
||||
|
||||
responses.add(DocumentMessage::ResetTransform);
|
||||
responses.add(NodeGraphMessage::SendGraph);
|
||||
|
||||
self.update_selected(document_network, selected_nodes, responses);
|
||||
}
|
||||
NodeGraphMessage::ExitNestedNetwork { steps_back } => {
|
||||
selected_nodes.clear_selected_nodes();
|
||||
responses.add(BroadcastEvent::SelectionChanged);
|
||||
@@ -433,15 +382,9 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||
for _ in 0..steps_back {
|
||||
self.network.pop();
|
||||
}
|
||||
// TODO: Find a better way to update click targets when undoing/redoing
|
||||
self.node_metadata.clear();
|
||||
self.update_all_click_targets(document_network, self.network.clone());
|
||||
|
||||
let Some(network) = document_network.nested_network(&self.network) else {
|
||||
return;
|
||||
};
|
||||
responses.add(DocumentMessage::UpdateDocumentTransform {
|
||||
transform: network.node_graph_to_viewport,
|
||||
});
|
||||
responses.add(DocumentMessage::ResetTransform);
|
||||
responses.add(NodeGraphMessage::SendGraph);
|
||||
self.update_selected(document_network, selected_nodes, responses);
|
||||
}
|
||||
@@ -625,81 +568,16 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||
};
|
||||
|
||||
let viewport_location = ipp.mouse.position;
|
||||
let point = network.node_graph_to_viewport.inverse().transform_point2(viewport_location);
|
||||
let point = node_graph_to_viewport.inverse().transform_point2(viewport_location);
|
||||
|
||||
if let Some(clicked_visibility) = NodeGraphMessageHandler::get_key_from_point(
|
||||
&self
|
||||
.node_metadata
|
||||
.iter()
|
||||
.filter_map(|(path, node_metadata)| {
|
||||
let mut path = path.clone();
|
||||
let node_id = path.pop().expect("Path to node should not be empty");
|
||||
if *path == self.network {
|
||||
// TODO: There should be a way to do this without cloning
|
||||
if let Some(visibility_click_target) = &node_metadata.visibility_click_target {
|
||||
Some((node_id, visibility_click_target.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<HashMap<NodeId, ClickTarget>>(),
|
||||
point,
|
||||
) {
|
||||
if let Some(clicked_visibility) = self.get_visibility_from_point(point) {
|
||||
responses.add(NodeGraphMessage::ToggleVisibility { node_id: clicked_visibility });
|
||||
return;
|
||||
}
|
||||
let clicked_id = NodeGraphMessageHandler::get_key_from_point(
|
||||
&self
|
||||
.node_metadata
|
||||
.iter()
|
||||
.filter_map(|(path, node_metadata)| {
|
||||
let mut path = path.clone();
|
||||
let node_id = path.pop().expect("Path to node should not be empty");
|
||||
if *path == self.network {
|
||||
// TODO: There should be a way to do this without cloning
|
||||
Some((node_id, node_metadata.node_click_target.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<HashMap<NodeId, ClickTarget>>(),
|
||||
point,
|
||||
);
|
||||
let clicked_input = NodeGraphMessageHandler::get_key_from_point(
|
||||
&self
|
||||
.node_metadata
|
||||
.iter()
|
||||
.filter(|(path, _)| path.starts_with(&self.network) && path.len() == self.network.len() + 1)
|
||||
.flat_map(|(path, node_metadata)| {
|
||||
// TODO: There should be a way to do this without cloning
|
||||
node_metadata
|
||||
.input_click_targets
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, output_click_target)| ((*path.last().expect("Path should not be empty"), index), output_click_target.clone()))
|
||||
})
|
||||
.collect::<HashMap<(NodeId, usize), ClickTarget>>(),
|
||||
point,
|
||||
);
|
||||
let clicked_output = NodeGraphMessageHandler::get_key_from_point(
|
||||
&self
|
||||
.node_metadata
|
||||
.iter()
|
||||
.filter(|(path, _)| path.starts_with(&self.network) && path.len() == self.network.len() + 1)
|
||||
.flat_map(|(path, node_metadata)| {
|
||||
// TODO: There should be a way to do this without cloning
|
||||
node_metadata
|
||||
.output_click_targets
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, output_click_target)| ((*path.last().expect("Path should not be empty"), index), output_click_target.clone()))
|
||||
})
|
||||
.collect::<HashMap<(NodeId, usize), ClickTarget>>(),
|
||||
point,
|
||||
);
|
||||
|
||||
let clicked_id = self.get_node_from_point(point);
|
||||
let clicked_input = self.get_connector_from_point(point, |metadata| &metadata.input_click_targets);
|
||||
let clicked_output = self.get_connector_from_point(point, |metadata| &metadata.output_click_targets);
|
||||
|
||||
// Create the add node popup on right click, then exit
|
||||
if right_click {
|
||||
@@ -716,11 +594,11 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||
let node_graph_shift = if matches!(context_menu_data, ContextMenuData::CreateNode) {
|
||||
let appear_right_of_mouse = if viewport_location.x > ipp.viewport_bounds.size().x - 180. { -180. } else { 0. };
|
||||
let appear_above_mouse = if viewport_location.y > ipp.viewport_bounds.size().y - 200. { -200. } else { 0. };
|
||||
DVec2::new(appear_right_of_mouse, appear_above_mouse) / network.node_graph_to_viewport.matrix2.x_axis.x
|
||||
DVec2::new(appear_right_of_mouse, appear_above_mouse) / node_graph_to_viewport.matrix2.x_axis.x
|
||||
} else {
|
||||
let appear_right_of_mouse = if viewport_location.x > ipp.viewport_bounds.size().x - 173. { -173. } else { 0. };
|
||||
let appear_above_mouse = if viewport_location.y > ipp.viewport_bounds.size().y - 34. { -34. } else { 0. };
|
||||
DVec2::new(appear_right_of_mouse, appear_above_mouse) / network.node_graph_to_viewport.matrix2.x_axis.x
|
||||
DVec2::new(appear_right_of_mouse, appear_above_mouse) / node_graph_to_viewport.matrix2.x_axis.x
|
||||
};
|
||||
|
||||
let context_menu_coordinates = ((point.x + node_graph_shift.x) as i32, (point.y + node_graph_shift.y) as i32);
|
||||
@@ -739,9 +617,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||
|
||||
// If the user is clicking on the create nodes list or context menu, break here
|
||||
if let Some(context_menu) = &self.context_menu {
|
||||
let context_menu_viewport = network
|
||||
.node_graph_to_viewport
|
||||
.transform_point2(DVec2::new(context_menu.context_menu_coordinates.0 as f64, context_menu.context_menu_coordinates.1 as f64));
|
||||
let context_menu_viewport = node_graph_to_viewport.transform_point2(DVec2::new(context_menu.context_menu_coordinates.0 as f64, context_menu.context_menu_coordinates.1 as f64));
|
||||
let (width, height) = if matches!(context_menu.context_menu_data, ContextMenuData::ToggleLayer { .. }) {
|
||||
// Height and width for toggle layer menu
|
||||
(173., 34.)
|
||||
@@ -937,37 +813,27 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||
let Some(network) = document_network.nested_network(&self.network) else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Auto-panning
|
||||
let messages = [NodeGraphMessage::PointerOutsideViewport { shift }.into(), NodeGraphMessage::PointerMove { shift }.into()];
|
||||
self.auto_panning.setup_by_mouse_position(ipp, &messages, responses);
|
||||
|
||||
let viewport_location = ipp.mouse.position;
|
||||
let point = network.node_graph_to_viewport.inverse().transform_point2(viewport_location);
|
||||
let point = node_graph_to_viewport.inverse().transform_point2(viewport_location);
|
||||
|
||||
if self.wire_in_progress_from_connector.is_some() && self.context_menu.is_none() {
|
||||
if let Some((to_connector_node_position, is_layer, input_index)) = NodeGraphMessageHandler::get_key_from_point(
|
||||
&self
|
||||
.node_metadata
|
||||
.iter()
|
||||
.filter(|(path, _)| path.starts_with(&self.network) && path.len() == self.network.len() + 1)
|
||||
.flat_map(|(path, node_metadata)| {
|
||||
// TODO: There should be a way to do this without cloning
|
||||
node_metadata
|
||||
.input_click_targets
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, output_click_target)| ((*path.last().expect("Path should not be empty"), index), output_click_target.clone()))
|
||||
if let Some((to_connector_node_position, is_layer, input_index)) =
|
||||
self.get_connector_from_point(point, |metadata| &metadata.input_click_targets).and_then(|(node_id, input_index)| {
|
||||
network.nodes.get(&node_id).map(|node| (node.metadata.position, node.is_layer, input_index)).or_else(|| {
|
||||
if node_id == network.exports_metadata.0 {
|
||||
Some((network.exports_metadata.1 + IVec2::new(0, 1), false, input_index))
|
||||
} else if node_id == network.imports_metadata.0 {
|
||||
Some((network.imports_metadata.1 + IVec2::new(0, 1), false, input_index))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<HashMap<(NodeId, usize), ClickTarget>>(),
|
||||
point,
|
||||
)
|
||||
.and_then(|(node_id, input_index)| {
|
||||
network.nodes.get(&node_id).map(|node| (node.metadata.position, node.is_layer, input_index)).or_else(|| {
|
||||
if node_id == network.exports_metadata.0 {
|
||||
Some((network.exports_metadata.1 + IVec2::new(0, 1), false, input_index))
|
||||
} else if node_id == network.imports_metadata.0 {
|
||||
Some((network.imports_metadata.1 + IVec2::new(0, 1), false, input_index))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}) {
|
||||
}) {
|
||||
let to_connector_position = if is_layer {
|
||||
if input_index == 0 {
|
||||
DVec2::new(to_connector_node_position.x as f64 * 24. + 2. * 24., to_connector_node_position.y as f64 * 24. + 2. * 24. + 12.)
|
||||
@@ -981,28 +847,13 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||
)
|
||||
};
|
||||
self.wire_in_progress_to_connector = Some((to_connector_position, input_index == 0 && is_layer));
|
||||
} else if let Some((to_connector_node_position, is_layer, output_index)) = NodeGraphMessageHandler::get_key_from_point(
|
||||
&self
|
||||
.node_metadata
|
||||
.iter()
|
||||
.filter(|(path, _)| path.starts_with(&self.network) && path.len() == self.network.len() + 1)
|
||||
.flat_map(|(path, node_metadata)| {
|
||||
// TODO: There should be a way to do this without cloning
|
||||
node_metadata
|
||||
.output_click_targets
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, output_click_target)| ((*path.last().expect("Path should not be empty"), index), output_click_target.clone()))
|
||||
})
|
||||
.collect::<HashMap<(NodeId, usize), ClickTarget>>(),
|
||||
point,
|
||||
)
|
||||
.and_then(|(node_id, output_index)| {
|
||||
network
|
||||
.nodes
|
||||
.get(&node_id)
|
||||
.map(|node| (node.metadata.position, node.is_layer, output_index + if node.has_primary_output { 0 } else { 1 }))
|
||||
}) {
|
||||
} else if let Some((to_connector_node_position, is_layer, output_index)) =
|
||||
self.get_connector_from_point(point, |metadata| &metadata.output_click_targets).and_then(|(node_id, output_index)| {
|
||||
network
|
||||
.nodes
|
||||
.get(&node_id)
|
||||
.map(|node| (node.metadata.position, node.is_layer, output_index + if node.has_primary_output { 0 } else { 1 }))
|
||||
}) {
|
||||
let to_connector_position = if is_layer {
|
||||
DVec2::new(to_connector_node_position.x as f64 * 24. + 2. * 24., to_connector_node_position.y as f64 * 24. - 12.)
|
||||
} else {
|
||||
@@ -1057,7 +908,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||
drag_start.round_y = graph_delta.y;
|
||||
}
|
||||
} else if let Some(box_selection_start) = self.box_selection_start {
|
||||
// TODO: Is this still an issue? The mouse button was released but we missed the pointer up event
|
||||
// The mouse button was released but we missed the pointer up event
|
||||
// if ((e.buttons & 1) === 0) {
|
||||
// completeBoxSelection();
|
||||
// boxSelection = undefined;
|
||||
@@ -1069,11 +920,11 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||
let box_selection = Some(BoxSelection {
|
||||
start_x: box_selection_start.x,
|
||||
start_y: box_selection_start.y,
|
||||
end_x: viewport_location.x.round().abs() as u32,
|
||||
end_y: viewport_location.y.round().abs() as u32,
|
||||
end_x: viewport_location.x.max(0.) as u32,
|
||||
end_y: viewport_location.y.max(0.) as u32,
|
||||
});
|
||||
|
||||
let graph_start = network.node_graph_to_viewport.inverse().transform_point2(box_selection_start.into());
|
||||
let graph_start = node_graph_to_viewport.inverse().transform_point2(box_selection_start.into());
|
||||
|
||||
// TODO: Only loop through visible nodes
|
||||
let shift = ipp.keyboard.get(shift as usize);
|
||||
@@ -1084,11 +935,9 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||
.map(|(node_id, _)| node_id)
|
||||
.chain(vec![network.exports_metadata.0, network.imports_metadata.0].iter())
|
||||
{
|
||||
let mut node_id_path = self.network.clone();
|
||||
node_id_path.push(*node_id);
|
||||
if self
|
||||
.node_metadata
|
||||
.get(&node_id_path)
|
||||
.get(&node_id)
|
||||
.is_some_and(|node_metadata| node_metadata.node_click_target.intersect_rectangle(Quad::from_box([graph_start, point]), DAffine2::IDENTITY))
|
||||
{
|
||||
nodes.push(*node_id);
|
||||
@@ -1105,42 +954,12 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||
};
|
||||
// Disconnect if the wire was previously connected to an input
|
||||
let viewport_location = ipp.mouse.position;
|
||||
let point = network.node_graph_to_viewport.inverse().transform_point2(viewport_location);
|
||||
let point = node_graph_to_viewport.inverse().transform_point2(viewport_location);
|
||||
|
||||
if let (Some(wire_in_progress_from_connector), Some(wire_in_progress_to_connector)) = (self.wire_in_progress_from_connector, self.wire_in_progress_to_connector) {
|
||||
// Check if dragged connector is reconnected to another input
|
||||
let node_from = NodeGraphMessageHandler::get_key_from_point(
|
||||
&self
|
||||
.node_metadata
|
||||
.iter()
|
||||
.filter(|(path, _)| path.starts_with(&self.network) && path.len() == self.network.len() + 1)
|
||||
.flat_map(|(path, node_metadata)| {
|
||||
// TODO: There should be a way to do this without cloning
|
||||
node_metadata
|
||||
.output_click_targets
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, output_click_target)| ((*path.last().expect("Path should not be empty"), index), output_click_target.clone()))
|
||||
})
|
||||
.collect::<HashMap<(NodeId, usize), ClickTarget>>(),
|
||||
wire_in_progress_from_connector.0,
|
||||
);
|
||||
let node_to = NodeGraphMessageHandler::get_key_from_point(
|
||||
&self
|
||||
.node_metadata
|
||||
.iter()
|
||||
.filter(|(path, _)| path.starts_with(&self.network) && path.len() == self.network.len() + 1)
|
||||
.flat_map(|(path, node_metadata)| {
|
||||
// TODO: There should be a way to do this without cloning
|
||||
node_metadata
|
||||
.input_click_targets
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, input_click_target)| ((*path.last().expect("Path should not be empty"), index), input_click_target.clone()))
|
||||
})
|
||||
.collect::<HashMap<(NodeId, usize), ClickTarget>>(),
|
||||
wire_in_progress_to_connector.0,
|
||||
);
|
||||
let node_from = self.get_connector_from_point(wire_in_progress_from_connector.0, |metadata| &metadata.output_click_targets);
|
||||
let node_to = self.get_connector_from_point(wire_in_progress_to_connector.0, |metadata| &metadata.input_click_targets);
|
||||
|
||||
if let (Some(node_from), Some(node_to)) = (node_from, node_to) {
|
||||
responses.add(NodeGraphMessage::ConnectNodesByWire {
|
||||
@@ -1157,7 +976,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||
|
||||
let appear_right_of_mouse = if viewport_location.x > ipp.viewport_bounds.size().x - 173. { -173. } else { 0. };
|
||||
let appear_above_mouse = if viewport_location.y > ipp.viewport_bounds.size().y - 34. { -34. } else { 0. };
|
||||
let node_graph_shift = DVec2::new(appear_right_of_mouse, appear_above_mouse) / network.node_graph_to_viewport.matrix2.x_axis.x;
|
||||
let node_graph_shift = DVec2::new(appear_right_of_mouse, appear_above_mouse) / node_graph_to_viewport.matrix2.x_axis.x;
|
||||
|
||||
self.context_menu = Some(ContextMenuInformation {
|
||||
context_menu_coordinates: ((point.x + node_graph_shift.x) as i32, (point.y + node_graph_shift.y) as i32),
|
||||
@@ -1210,10 +1029,12 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||
// Check if primary output is disconnected
|
||||
if !has_primary_output_connection {
|
||||
// TODO: Cache all wire locations. This will be difficult since there are many ways for an input to changes, and each change will have to update the cache
|
||||
let mut node_id_path = self.network.clone();
|
||||
node_id_path.push(selected_node_id);
|
||||
let Some(bounding_box) = self.node_metadata.get(&node_id_path).and_then(|node_metadata| node_metadata.node_click_target.subpath.bounding_box()) else {
|
||||
log::error!("Could not get bounding box for node: {node_id_path:?}");
|
||||
let Some(bounding_box) = self
|
||||
.node_metadata
|
||||
.get(&selected_node_id)
|
||||
.and_then(|node_metadata| node_metadata.node_click_target.subpath.bounding_box())
|
||||
else {
|
||||
log::error!("Could not get bounding box for node: {selected_node_id}");
|
||||
return;
|
||||
};
|
||||
let overlapping_wire = Self::collect_wires(network).into_iter().find(|frontend_wire| {
|
||||
@@ -1278,7 +1099,15 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None });
|
||||
responses.add(FrontendMessage::UpdateBox { box_selection: None })
|
||||
}
|
||||
|
||||
NodeGraphMessage::PointerOutsideViewport { shift } => {
|
||||
if self.drag_start.is_some() || self.box_selection_start.is_some() {
|
||||
let _ = self.auto_panning.shift_viewport(ipp, responses);
|
||||
} else {
|
||||
// Auto-panning
|
||||
let messages = [NodeGraphMessage::PointerOutsideViewport { shift }.into(), NodeGraphMessage::PointerMove { shift }.into()];
|
||||
self.auto_panning.stop(&messages, responses);
|
||||
}
|
||||
}
|
||||
NodeGraphMessage::PrintSelectedNodeCoordinates => {
|
||||
let Some(network) = document_network.nested_network_for_selected_nodes(&self.network, selected_nodes.selected_nodes_ref().iter()) else {
|
||||
warn!("No network");
|
||||
@@ -1569,6 +1398,13 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||
};
|
||||
if let Some(node) = network.nodes.get_mut(&node_id) {
|
||||
node.alias = name;
|
||||
if let Some(node_metadata) = self.node_metadata.get_mut(&node_id) {
|
||||
if node.is_layer {
|
||||
node_metadata.layer_width = Some(NodeGraphMessageHandler::layer_width_cells(node));
|
||||
} else {
|
||||
node_metadata.layer_width = None;
|
||||
}
|
||||
};
|
||||
self.update_click_target(node_id, document_network, self.network.clone());
|
||||
responses.add(DocumentMessage::RenderRulers);
|
||||
responses.add(DocumentMessage::RenderScrollbars);
|
||||
@@ -1758,6 +1594,18 @@ impl NodeGraphMessageHandler {
|
||||
|
||||
Some(text_width)
|
||||
}
|
||||
pub fn layer_width_cells(node: &DocumentNode) -> u32 {
|
||||
let half_grid_cell_offset = 24. / 2.;
|
||||
let thumbnail_width = 3. * 24.;
|
||||
let gap_width = 8.;
|
||||
let text_width = Self::get_text_width(node).unwrap_or_default();
|
||||
let icon_width = 24.;
|
||||
let icon_overhang_width = icon_width / 2.;
|
||||
|
||||
let text_right = half_grid_cell_offset + thumbnail_width + gap_width + text_width;
|
||||
let layer_width_pixels = text_right + gap_width + icon_width - icon_overhang_width;
|
||||
((layer_width_pixels / 24.) as u32).max(8)
|
||||
}
|
||||
|
||||
// Inserts a node into the network and updates the click target
|
||||
pub fn insert_node(&mut self, node_id: NodeId, node: DocumentNode, document_network: &mut NodeNetwork, network_path: &Vec<NodeId>) {
|
||||
@@ -1780,26 +1628,16 @@ impl NodeGraphMessageHandler {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut node_id_path = network_path.clone();
|
||||
node_id_path.push(node_id);
|
||||
// Clear all click targets for the node
|
||||
self.node_metadata.remove(&node_id_path);
|
||||
|
||||
let grid_size = 24; // Number of pixels per grid unit at 100% zoom
|
||||
|
||||
if let Some(node) = network.nodes.get(&node_id) {
|
||||
let mut layer_width = None;
|
||||
let width = if node.is_layer {
|
||||
let half_grid_cell_offset = 24. / 2.;
|
||||
let thumbnail_width = 3. * 24.;
|
||||
let gap_width = 8.;
|
||||
let text_width = Self::get_text_width(node).unwrap_or_default();
|
||||
let icon_width = 24.;
|
||||
let icon_overhang_width = icon_width / 2.;
|
||||
|
||||
let text_right = half_grid_cell_offset + thumbnail_width + gap_width + text_width;
|
||||
let layer_width_pixels = text_right + gap_width + icon_width - icon_overhang_width;
|
||||
let layer_width_cells = (((layer_width_pixels) / 24.).ceil() as u32).max(8);
|
||||
let layer_width_cells = self
|
||||
.node_metadata
|
||||
.get(&node_id)
|
||||
.and_then(|node_metadata| node_metadata.layer_width)
|
||||
.unwrap_or_else(|| Self::layer_width_cells(node));
|
||||
|
||||
layer_width = Some(layer_width_cells);
|
||||
|
||||
@@ -1916,7 +1754,7 @@ impl NodeGraphMessageHandler {
|
||||
visibility_click_target,
|
||||
layer_width,
|
||||
};
|
||||
self.node_metadata.insert(node_id_path.clone(), node_metadata);
|
||||
self.node_metadata.insert(node_id, node_metadata);
|
||||
} else if node_id == network.exports_metadata.0 {
|
||||
let width = 5 * grid_size;
|
||||
// 1 is added since the first row is reserved for the "Exports" name
|
||||
@@ -1958,7 +1796,7 @@ impl NodeGraphMessageHandler {
|
||||
layer_width: None,
|
||||
};
|
||||
|
||||
self.node_metadata.insert(node_id_path.clone(), node_metadata);
|
||||
self.node_metadata.insert(node_id, node_metadata);
|
||||
}
|
||||
// The number of imports is from the parent node, which is passed as a parameter. The number of exports is available from self.
|
||||
else if node_id == network.imports_metadata.0 {
|
||||
@@ -2010,51 +1848,17 @@ impl NodeGraphMessageHandler {
|
||||
visibility_click_target,
|
||||
layer_width: None,
|
||||
};
|
||||
self.node_metadata.insert(node_id_path.clone(), node_metadata);
|
||||
}
|
||||
}
|
||||
|
||||
// if node_click_target is outside the current bounding box, update the bounding box
|
||||
if self.network_metadata.get(&network_path).map_or(true, |network_metadata| {
|
||||
network_metadata.bounding_box_subpath.as_ref().map_or(true, |bounding_box| {
|
||||
bounding_box.bounding_box().is_some_and(|bounding_box| {
|
||||
self.node_metadata.get(&node_id_path).map_or(true, |node_metadata| {
|
||||
node_metadata.node_click_target.subpath.bounding_box().is_some_and(|click_target| {
|
||||
// Combine bounds and check if new vec is larger than current bounding box
|
||||
let new_bounds = Quad::combine_bounds(click_target, bounding_box);
|
||||
bounding_box != new_bounds
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}) {
|
||||
let node_id_path_len = node_id_path.len();
|
||||
let bounds = self
|
||||
.node_metadata
|
||||
.iter()
|
||||
.filter_map(|(node_path, node_metadata)| {
|
||||
// Check if node is in same network as the updated node
|
||||
if node_path.len() == node_id_path_len && node_path.starts_with(&network_path) {
|
||||
node_metadata.node_click_target.subpath.bounding_box()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.reduce(Quad::combine_bounds);
|
||||
|
||||
let bounds = bounds.map(|bounds| bezier_rs::Subpath::new_rect(bounds[0], bounds[1]));
|
||||
if let Some(network_metadata) = self.network_metadata.get_mut(&network_path) {
|
||||
network_metadata.bounding_box_subpath = bounds;
|
||||
} else {
|
||||
self.network_metadata.insert(
|
||||
network_path,
|
||||
NetworkMetadata {
|
||||
bounding_box_subpath: bounds,
|
||||
node_graph_to_viewport: DAffine2::IDENTITY,
|
||||
},
|
||||
);
|
||||
self.node_metadata.insert(node_id, node_metadata);
|
||||
}
|
||||
} else {
|
||||
self.node_metadata.remove(&node_id);
|
||||
}
|
||||
let bounds = self
|
||||
.node_metadata
|
||||
.iter()
|
||||
.filter_map(|(_, node_metadata)| node_metadata.node_click_target.subpath.bounding_box())
|
||||
.reduce(Quad::combine_bounds);
|
||||
self.bounding_box_subpath = bounds.map(|bounds| bezier_rs::Subpath::new_rect(bounds[0], bounds[1]));
|
||||
}
|
||||
|
||||
// Updates all click targets in a certain network
|
||||
@@ -2065,7 +1869,7 @@ impl NodeGraphMessageHandler {
|
||||
};
|
||||
let export_id = network.exports_metadata.0;
|
||||
let import_id = network.imports_metadata.0;
|
||||
for node_id in network.nodes.clone().keys() {
|
||||
for (node_id, _) in network.nodes.iter() {
|
||||
self.update_click_target(*node_id, document_network, network_path.clone());
|
||||
}
|
||||
self.update_click_target(export_id, document_network, network_path.clone());
|
||||
@@ -2073,23 +1877,41 @@ impl NodeGraphMessageHandler {
|
||||
}
|
||||
|
||||
/// Gets the bounding box in viewport coordinates for each node in the node graph
|
||||
pub fn graph_bounds_viewport_space(&self, document_network: &NodeNetwork) -> Option<[DVec2; 2]> {
|
||||
self.network_metadata.get(&self.network).and_then(|network_metadata| {
|
||||
network_metadata.bounding_box_subpath.as_ref().and_then(|bounding_box| {
|
||||
document_network
|
||||
.nested_network(&self.network)
|
||||
.and_then(|network| bounding_box.bounding_box_with_transform(network.node_graph_to_viewport))
|
||||
})
|
||||
})
|
||||
pub fn graph_bounds_viewport_space(&self, node_graph_to_viewport: DAffine2) -> Option<[DVec2; 2]> {
|
||||
self.bounding_box_subpath
|
||||
.as_ref()
|
||||
.and_then(|bounding_box| bounding_box.bounding_box_with_transform(node_graph_to_viewport))
|
||||
}
|
||||
|
||||
/// Get the clicked target from a mouse click
|
||||
fn get_key_from_point<T: Clone>(hashmap: &HashMap<T, ClickTarget>, point: DVec2) -> Option<T> {
|
||||
hashmap
|
||||
fn get_node_from_point(&self, point: DVec2) -> Option<NodeId> {
|
||||
self.node_metadata
|
||||
.iter()
|
||||
.filter(move |(_, target)| target.intersect_point(point, DAffine2::IDENTITY))
|
||||
.map(|(data, _)| data.clone())
|
||||
.next()
|
||||
.map(|(node_id, node_metadata)| (node_id, &node_metadata.node_click_target))
|
||||
.find_map(|(node_id, click_target)| if click_target.intersect_point(point, DAffine2::IDENTITY) { Some(*node_id) } else { None })
|
||||
}
|
||||
|
||||
fn get_connector_from_point<F>(&self, point: DVec2, click_target_selector: F) -> Option<(NodeId, usize)>
|
||||
where
|
||||
F: Fn(&NodeMetadata) -> &Vec<ClickTarget>,
|
||||
{
|
||||
self.node_metadata
|
||||
.iter()
|
||||
.map(|(node_id, node_metadata)| (node_id, click_target_selector(node_metadata)))
|
||||
.find_map(|(node_id, click_targets)| {
|
||||
for (index, click_target) in click_targets.iter().enumerate() {
|
||||
if click_target.intersect_point(point, DAffine2::IDENTITY) {
|
||||
return Some((node_id.clone(), index));
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
fn get_visibility_from_point(&self, point: DVec2) -> Option<NodeId> {
|
||||
self.node_metadata
|
||||
.iter()
|
||||
.filter_map(|(node_id, node_metadata)| node_metadata.visibility_click_target.as_ref().map(|click_target| (node_id, click_target)))
|
||||
.find_map(|(node_id, click_target)| if click_target.intersect_point(point, DAffine2::IDENTITY) { Some(*node_id) } else { None })
|
||||
}
|
||||
|
||||
/// Send the cached layout to the frontend for the options bar at the top of the node panel
|
||||
@@ -2510,13 +2332,14 @@ impl NodeGraphMessageHandler {
|
||||
} else {
|
||||
(FrontendGraphDataType::General, None)
|
||||
}
|
||||
}
|
||||
// If type should only be determined from resolved_types then remove this
|
||||
else if let NodeInput::Value { tagged_value, .. } = export {
|
||||
} else if let NodeInput::Value { tagged_value, .. } = export {
|
||||
(FrontendGraphDataType::with_type(&tagged_value.ty()), Some(tagged_value.ty()))
|
||||
} else if let NodeInput::Network { import_type, .. } = export {
|
||||
(FrontendGraphDataType::with_type(import_type), Some(import_type.clone()))
|
||||
} else {
|
||||
}
|
||||
// TODO: Get type from parent node input when <https://github.com/GraphiteEditor/Graphite/issues/1762> is possible
|
||||
// else if let NodeInput::Network { import_type, .. } = export {
|
||||
// (FrontendGraphDataType::with_type(import_type), Some(import_type.clone()))
|
||||
// }
|
||||
else {
|
||||
(FrontendGraphDataType::General, None)
|
||||
};
|
||||
|
||||
@@ -2719,17 +2542,7 @@ impl NodeGraphMessageHandler {
|
||||
let layer_widths = self
|
||||
.node_metadata
|
||||
.iter()
|
||||
.filter_map(|(node_path, node_metadata)| {
|
||||
if node_path.starts_with(&self.network) && node_path.len() == self.network.len() + 1 {
|
||||
if let Some(layer_width) = node_metadata.layer_width {
|
||||
Some((*node_path.last().unwrap(), layer_width))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.filter_map(|(node_id, node_metadata)| node_metadata.layer_width.map(|layer_width| (*node_id, layer_width)))
|
||||
.collect::<HashMap<NodeId, u32>>();
|
||||
responses.add(FrontendMessage::UpdateLayerWidths { layer_widths });
|
||||
}
|
||||
@@ -2767,8 +2580,14 @@ impl NodeGraphMessageHandler {
|
||||
resolved_types.outputs.get(&Source { node: current_path.clone(), index: 0 }).cloned()
|
||||
} else if let NodeInput::Value { tagged_value, .. } = current_export {
|
||||
Some(tagged_value.ty())
|
||||
} else if let NodeInput::Network { import_type, .. } = current_export {
|
||||
Some(import_type.clone())
|
||||
} else if let NodeInput::Network { import_index, .. } = current_export {
|
||||
resolved_types
|
||||
.outputs
|
||||
.get(&Source {
|
||||
node: node_id_path.clone(),
|
||||
index: *import_index,
|
||||
})
|
||||
.cloned()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -2973,7 +2792,8 @@ impl Default for NodeGraphMessageHandler {
|
||||
wire_in_progress_to_connector: None,
|
||||
context_menu: None,
|
||||
node_metadata: HashMap::new(),
|
||||
network_metadata: HashMap::new(),
|
||||
bounding_box_subpath: None,
|
||||
auto_panning: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::NodeId;
|
||||
use graphene_core::Type;
|
||||
use graphene_std::renderer::ClickTarget;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
pub enum FrontendGraphDataType {
|
||||
@@ -166,3 +167,17 @@ pub struct ContextMenuInformation {
|
||||
#[serde(rename = "contextMenuData")]
|
||||
pub context_menu_data: ContextMenuData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NodeMetadata {
|
||||
/// Cache for all node click targets in node graph space. Ensure `update_click_target` is called when modifying a node property that changes its size. Currently this is `alias`, `inputs`, `is_layer`, and `metadata`.
|
||||
pub node_click_target: ClickTarget,
|
||||
/// Cache for all node inputs. Should be automatically updated when `update_click_target` is called.
|
||||
pub input_click_targets: Vec<ClickTarget>,
|
||||
/// Cache for all node outputs. Should be automatically updated when `update_click_target` is called.
|
||||
pub output_click_targets: Vec<ClickTarget>,
|
||||
/// Cache for all visibility buttons. Should be automatically updated when `update_click_target` is called.
|
||||
pub visibility_click_target: Option<ClickTarget>,
|
||||
/// Stores the width in grid cell units for layer nodes from the left edge of the thumbnail (+12px padding since thumbnail ends between grid spaces) to the end of the node.
|
||||
pub layer_width: Option<u32>,
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use graphene_std::vector::style::FillChoice;
|
||||
fn grid_overlay_rectangular(document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, spacing: DVec2) {
|
||||
let origin = document.snapping_state.grid.origin;
|
||||
let grid_color: Color = document.snapping_state.grid.grid_color;
|
||||
let Some(spacing) = GridSnapping::compute_rectangle_spacing(spacing, &document.navigation) else {
|
||||
let Some(spacing) = GridSnapping::compute_rectangle_spacing(spacing, &document.document_ptz) else {
|
||||
return;
|
||||
};
|
||||
let document_to_viewport = document.metadata().document_to_viewport;
|
||||
@@ -55,7 +55,7 @@ fn grid_overlay_rectangular(document: &DocumentMessageHandler, overlay_context:
|
||||
fn grid_overlay_dot(document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, spacing: DVec2) {
|
||||
let origin = document.snapping_state.grid.origin;
|
||||
let grid_color = document.snapping_state.grid.grid_color;
|
||||
let Some(spacing) = GridSnapping::compute_rectangle_spacing(spacing, &document.navigation) else {
|
||||
let Some(spacing) = GridSnapping::compute_rectangle_spacing(spacing, &document.document_ptz) else {
|
||||
return;
|
||||
};
|
||||
let document_to_viewport = document.metadata().document_to_viewport;
|
||||
@@ -98,7 +98,7 @@ fn grid_overlay_isometric(document: &DocumentMessageHandler, overlay_context: &m
|
||||
let tan_a = angle_a.to_radians().tan();
|
||||
let tan_b = angle_b.to_radians().tan();
|
||||
let spacing = DVec2::new(y_axis_spacing / (tan_a + tan_b), y_axis_spacing);
|
||||
let Some(spacing_multiplier) = GridSnapping::compute_isometric_multiplier(y_axis_spacing, tan_a + tan_b, &document.navigation) else {
|
||||
let Some(spacing_multiplier) = GridSnapping::compute_isometric_multiplier(y_axis_spacing, tan_a + tan_b, &document.document_ptz) else {
|
||||
return;
|
||||
};
|
||||
let isometric_spacing = spacing * spacing_multiplier;
|
||||
@@ -150,7 +150,7 @@ fn grid_overlay_isometric_dot(document: &DocumentMessageHandler, overlay_context
|
||||
let tan_a = angle_a.to_radians().tan();
|
||||
let tan_b = angle_b.to_radians().tan();
|
||||
let spacing = DVec2::new(y_axis_spacing / (tan_a + tan_b), y_axis_spacing);
|
||||
let Some(spacing_multiplier) = GridSnapping::compute_isometric_multiplier(y_axis_spacing, tan_a + tan_b, &document.navigation) else {
|
||||
let Some(spacing_multiplier) = GridSnapping::compute_isometric_multiplier(y_axis_spacing, tan_a + tan_b, &document.document_ptz) else {
|
||||
return;
|
||||
};
|
||||
let isometric_spacing = spacing * spacing_multiplier;
|
||||
|
||||
@@ -2,6 +2,5 @@ pub mod clipboards;
|
||||
pub mod document_metadata;
|
||||
pub mod error;
|
||||
pub mod misc;
|
||||
pub mod node_metadata;
|
||||
pub mod nodes;
|
||||
pub mod transformation;
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
use bezier_rs::Subpath;
|
||||
use glam::DAffine2;
|
||||
use graphene_core::renderer::ClickTarget;
|
||||
use graphene_core::uuid::ManipulatorGroupId;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NodeMetadata {
|
||||
/// Cache for all node click targets in node graph space. Ensure update_click_target is called when modifying a node property that changes its size. Currently this is alias, inputs, is_layer, and metadata
|
||||
pub node_click_target: ClickTarget,
|
||||
/// Cache for all node inputs. Should be automatically updated when update_click_target is called
|
||||
pub input_click_targets: Vec<ClickTarget>,
|
||||
/// Cache for all node outputs. Should be automatically updated when update_click_target is called
|
||||
pub output_click_targets: Vec<ClickTarget>,
|
||||
/// Cache for all visibility buttons. Should be automatically updated when update_click_target is called
|
||||
pub visibility_click_target: Option<ClickTarget>,
|
||||
/// Stores the width in grid cell units for layer nodes from the left edge of the thumbnail (+12px padding since thumbnail ends between grid spaces) to the end of the node
|
||||
pub layer_width: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NetworkMetadata {
|
||||
/// Cache for the bounding box around all nodes in node graph space.
|
||||
pub bounding_box_subpath: Option<Subpath<ManipulatorGroupId>>,
|
||||
/// Transform from node graph space to viewport space.
|
||||
pub node_graph_to_viewport: DAffine2,
|
||||
}
|
||||
@@ -61,7 +61,7 @@ impl SnapConstraint {
|
||||
}
|
||||
}
|
||||
pub fn snap_tolerance(document: &DocumentMessageHandler) -> f64 {
|
||||
document.snapping_state.tolerance / document.navigation.zoom
|
||||
document.snapping_state.tolerance / document.document_ptz.zoom
|
||||
}
|
||||
|
||||
fn compare_points(a: &&SnappedPoint, b: &&SnappedPoint) -> Ordering {
|
||||
|
||||
@@ -21,7 +21,7 @@ impl GridSnapper {
|
||||
let document = snap_data.document;
|
||||
let mut lines = Vec::new();
|
||||
|
||||
let Some(spacing) = GridSnapping::compute_rectangle_spacing(spacing, &document.navigation) else {
|
||||
let Some(spacing) = GridSnapping::compute_rectangle_spacing(spacing, &document.document_ptz) else {
|
||||
return lines;
|
||||
};
|
||||
let origin = document.snapping_state.grid.origin;
|
||||
@@ -47,7 +47,7 @@ impl GridSnapper {
|
||||
let tan_a = angle_a.to_radians().tan();
|
||||
let tan_b = angle_b.to_radians().tan();
|
||||
let spacing = DVec2::new(y_axis_spacing / (tan_a + tan_b), y_axis_spacing);
|
||||
let Some(spacing_multiplier) = GridSnapping::compute_isometric_multiplier(y_axis_spacing, tan_a + tan_b, &document.navigation) else {
|
||||
let Some(spacing_multiplier) = GridSnapping::compute_isometric_multiplier(y_axis_spacing, tan_a + tan_b, &document.document_ptz) else {
|
||||
return lines;
|
||||
};
|
||||
let spacing = spacing * spacing_multiplier;
|
||||
|
||||
Reference in New Issue
Block a user