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:
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(),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -507,7 +507,7 @@
|
|||
size={24}
|
||||
icon={node.visible ? "EyeVisible" : "EyeHidden"}
|
||||
action={() => {
|
||||
/*Button is purely visual, clicking is handled in NodeGraphMessage::PointerDown*/
|
||||
/* Button is purely visual, clicking is handled in NodeGraphMessage::PointerDown */
|
||||
}}
|
||||
tooltip={node.visible ? "Visible" : "Hidden"}
|
||||
/>
|
||||
|
|
@ -658,7 +658,8 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Box select widget -->
|
||||
<!-- Box selection widget -->
|
||||
<!-- TODO: Make its initial corner stay put (in graph space) when panning around -->
|
||||
{#if $nodeGraph.box}
|
||||
<div
|
||||
class="box-selection"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use dyn_any::{DynAny, StaticType};
|
|||
pub use graphene_core::uuid::generate_uuid;
|
||||
use graphene_core::{ProtoNodeIdentifier, Type};
|
||||
|
||||
use glam::{DAffine2, IVec2};
|
||||
use glam::IVec2;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
|
@ -665,9 +665,6 @@ pub struct NodeNetwork {
|
|||
pub imports_metadata: (NodeId, IVec2),
|
||||
#[serde(default = "default_export_metadata")]
|
||||
pub exports_metadata: (NodeId, IVec2),
|
||||
/// Transform from node graph space to viewport space.
|
||||
#[serde(default)]
|
||||
pub node_graph_to_viewport: DAffine2,
|
||||
}
|
||||
|
||||
impl std::hash::Hash for NodeNetwork {
|
||||
|
|
@ -690,7 +687,6 @@ impl Default for NodeNetwork {
|
|||
previewing: Default::default(),
|
||||
imports_metadata: default_import_metadata(),
|
||||
exports_metadata: default_export_metadata(),
|
||||
node_graph_to_viewport: DAffine2::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,6 +133,7 @@ impl BorrowTree {
|
|||
/// Pushes new nodes into the tree and return orphaned nodes
|
||||
pub async fn update(&mut self, proto_network: ProtoNetwork, typing_context: &TypingContext) -> Result<Vec<NodeId>, GraphErrors> {
|
||||
let mut old_nodes: HashSet<_> = self.nodes.keys().copied().collect();
|
||||
// TODO: Problem: When an identity node is connected directly to an export the first input to identity node is not added to the proto network, while the second input is. This means the primary input does not have a type.
|
||||
for (id, node) in proto_network.nodes {
|
||||
if !self.nodes.contains_key(&id) {
|
||||
self.push_node(id, node, typing_context).await?;
|
||||
|
|
|
|||
Loading…
Reference in New Issue