mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-21 00:48:11 +08:00
Add layer node chains, import/export edge connectors, and refactor graph editing to go thru a NodeNetworkInterface (#1794)
* WIP: NodeNetworkInterface * Organize ModifyInputsContext to use network interface * Improve ClickTarget and Position state * Rework ClickTarget state * Continue fixing NodeGraphMessageHandler * Restructure network_metadata * Final(?) NodeNetworkInterface struct * Final(??) NodeNetworkInterface * Final(???) NodeNetworkInterface. Separated persistent and transient data * Final NodeNetworkInterface data structure. Implemented all basic getters * Continue migrating functionality to network interface * Migrate all NodeGraphMessage's to use network interface * Fix all helper functions in NodeGraphMessageHandler * Move document metadata to network interface, remove various cached fields * Move all editor only NodeNetwork implementations to NodeNetworkInterface * Fix all DocumentNodeDefinitions * Rework and migrate GraphOperationMessages to network interface * Continue migration to NodeNetworkInterface * Save point before merging master * Fix all errors in network_interface * 850 -> 160 errors * Fix all errors :D * Render default document * Visualize click targets * merge conflicts * Cache transient metadata separately, store entire interface in document history * Start migration to storing selected nodes for each network * Remove selected nodes from document message handler * Move outward wires and all nodes bounding box to transient metadata * Fix connecting/disconnecting nodes * Layer stack organization for disconnecting/connecting nodes * Basic chain locking * Improve chain positioning * Add copy/pasting * Move upstream nodes on shift+drag * merge conflict fixes * Improve Graph.svelte code quality * Final improvements to Graph.svelte * Fix layer panel * Performance optimizations * Bug fixes and derived PTZ * Chain organization improvement and bug fixes * Bug fixes, remove all warnings * Automatic file upgrade * Final code review * Fix editor tests * Fix compile errors * Remove select tool intersection check when panning * WIP: Import/Exports * Fix JS issues * Finish simplified import/export UI * Import/Export viewport edge UI * Remove minimum y bound on import/export ports * Improve performance while panning graph * cargo fmt * Fix CI code build * Format the demo artwork graph with chains * Code review --------- Co-authored-by: Keavon Chambers <keavon@keavon.com> Co-authored-by: dennis@kobert.dev <dennis@kobert.dev>
This commit is contained in:
co-authored by
Keavon Chambers
dennis@kobert.dev
parent
ea44d1440a
commit
0dbbabe73e
@@ -70,22 +70,25 @@ impl MessageHandler<DialogMessage, DialogMessageData<'_>> for DialogMessageHandl
|
||||
DialogMessage::RequestExportDialog => {
|
||||
if let Some(document) = portfolio.active_document() {
|
||||
let artboards = document
|
||||
.metadata
|
||||
.metadata()
|
||||
.all_layers()
|
||||
.filter(|&layer| document.metadata.is_artboard(layer))
|
||||
.filter(|&layer| document.network_interface.is_artboard(&layer.to_node(), &[]))
|
||||
.map(|layer| {
|
||||
let name = document
|
||||
.network
|
||||
.nodes
|
||||
.get(&layer.to_node())
|
||||
.and_then(|node| if node.alias.is_empty() { None } else { Some(node.alias.clone()) })
|
||||
.network_interface
|
||||
.node_metadata(&layer.to_node(), &[])
|
||||
.map(|node| node.persistent_metadata.display_name.clone())
|
||||
.and_then(|name| if name.is_empty() { None } else { Some(name) })
|
||||
.unwrap_or_else(|| "Artboard".to_string());
|
||||
(layer, name)
|
||||
})
|
||||
.collect();
|
||||
|
||||
self.export_dialog.artboards = artboards;
|
||||
self.export_dialog.has_selection = document.selected_nodes.selected_layers(document.metadata()).next().is_some();
|
||||
self.export_dialog.has_selection = document
|
||||
.network_interface
|
||||
.selected_nodes(&[])
|
||||
.is_some_and(|selected_nodes| selected_nodes.selected_layers(document.metadata()).next().is_some());
|
||||
self.export_dialog.send_dialog_to_frontend(responses);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use super::utility_types::{FrontendDocumentDetails, MouseCursorIcon};
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::portfolio::document::node_graph::utility_types::{BoxSelection, ContextMenuInformation, FrontendNode, FrontendNodeType, FrontendNodeWire, Transform, WirePath};
|
||||
use crate::messages::portfolio::document::node_graph::utility_types::{
|
||||
BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendNodeType, FrontendNodeWire, Transform, WirePath,
|
||||
};
|
||||
use crate::messages::portfolio::document::utility_types::nodes::{JsRawBuffer, LayerPanelEntry, RawBuffer};
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::utility_types::HintData;
|
||||
@@ -121,6 +123,14 @@ pub enum FrontendMessage {
|
||||
#[serde(rename = "documentId")]
|
||||
document_id: DocumentId,
|
||||
},
|
||||
UpdateImportsExports {
|
||||
imports: Vec<(FrontendGraphOutput, i32, i32)>,
|
||||
exports: Vec<(FrontendGraphInput, i32, i32)>,
|
||||
},
|
||||
UpdateInSelectedNetwork {
|
||||
#[serde(rename = "inSelectedNetwork")]
|
||||
in_selected_network: bool,
|
||||
},
|
||||
UpdateBox {
|
||||
#[serde(rename = "box")]
|
||||
box_selection: Option<BoxSelection>,
|
||||
@@ -129,9 +139,15 @@ pub enum FrontendMessage {
|
||||
#[serde(rename = "contextMenuInformation")]
|
||||
context_menu_information: Option<ContextMenuInformation>,
|
||||
},
|
||||
UpdateClickTargets {
|
||||
#[serde(rename = "clickTargets")]
|
||||
click_targets: Option<FrontendClickTargets>,
|
||||
},
|
||||
UpdateLayerWidths {
|
||||
#[serde(rename = "layerWidths")]
|
||||
layer_widths: HashMap<NodeId, u32>,
|
||||
#[serde(rename = "chainWidths")]
|
||||
chain_widths: HashMap<NodeId, u32>,
|
||||
},
|
||||
UpdateDialogButtons {
|
||||
#[serde(rename = "layoutTarget")]
|
||||
|
||||
@@ -69,10 +69,12 @@ pub fn input_mappings() -> Mapping {
|
||||
entry!(KeyDown(KeyX); modifiers=[Accel], action_dispatch=NodeGraphMessage::Cut),
|
||||
entry!(KeyDown(KeyC); modifiers=[Accel], action_dispatch=NodeGraphMessage::Copy),
|
||||
entry!(KeyDown(KeyD); modifiers=[Accel], action_dispatch=NodeGraphMessage::DuplicateSelectedNodes),
|
||||
entry!(KeyDown(KeyH); modifiers=[Accel], action_dispatch=GraphOperationMessage::ToggleSelectedVisibility),
|
||||
entry!(KeyDown(KeyL); modifiers=[Accel], action_dispatch=GraphOperationMessage::ToggleSelectedLocked),
|
||||
entry!(KeyDown(KeyH); modifiers=[Accel], action_dispatch=NodeGraphMessage::ToggleSelectedVisibility),
|
||||
entry!(KeyDown(KeyL); modifiers=[Accel], action_dispatch=NodeGraphMessage::ToggleSelectedLocked),
|
||||
entry!(KeyDown(KeyL); modifiers=[Alt], action_dispatch=NodeGraphMessage::ToggleSelectedAsLayersOrNodes),
|
||||
entry!(KeyDown(KeyC); modifiers=[Shift], action_dispatch=NodeGraphMessage::PrintSelectedNodeCoordinates),
|
||||
entry!(KeyDown(KeyC); modifiers=[Alt], action_dispatch=NodeGraphMessage::SendClickTargets),
|
||||
entry!(KeyUp(KeyC); action_dispatch=NodeGraphMessage::EndSendClickTargets),
|
||||
//
|
||||
// TransformLayerMessage
|
||||
entry!(KeyDown(Enter); action_dispatch=TransformLayerMessage::ApplyTransformOperation),
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
use super::utility_types::misc::{OptionBoundsSnapping, OptionPointSnapping};
|
||||
use super::utility_types::network_interface::NodeNetworkInterface;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::Key;
|
||||
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis, GridSnapping};
|
||||
use crate::messages::portfolio::utility_types::PanelType;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use graph_craft::document::{NodeId, NodeNetwork};
|
||||
use graph_craft::document::NodeId;
|
||||
use graphene_core::raster::BlendMode;
|
||||
use graphene_core::raster::Image;
|
||||
use graphene_core::vector::style::ViewMode;
|
||||
@@ -12,8 +15,6 @@ use graphene_core::Color;
|
||||
|
||||
use glam::DAffine2;
|
||||
|
||||
use super::utility_types::misc::{OptionBoundsSnapping, OptionPointSnapping};
|
||||
|
||||
#[impl_message(Message, PortfolioMessage, Document)]
|
||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum DocumentMessage {
|
||||
@@ -37,7 +38,7 @@ pub enum DocumentMessage {
|
||||
aggregate: AlignAggregate,
|
||||
},
|
||||
BackupDocument {
|
||||
network: NodeNetwork,
|
||||
network_interface: NodeNetworkInterface,
|
||||
},
|
||||
ClearArtboards,
|
||||
ClearLayersPanel,
|
||||
@@ -47,15 +48,18 @@ pub enum DocumentMessage {
|
||||
},
|
||||
CreateEmptyFolder,
|
||||
DebugPrintDocument,
|
||||
DeleteLayer {
|
||||
layer: LayerNodeIdentifier,
|
||||
},
|
||||
DeleteSelectedLayers,
|
||||
DeselectAllLayers,
|
||||
DocumentHistoryBackward,
|
||||
DocumentHistoryForward,
|
||||
DocumentStructureChanged,
|
||||
DuplicateSelectedLayers,
|
||||
EnterNestedNetwork {
|
||||
node_id: NodeId,
|
||||
},
|
||||
ExitNestedNetwork {
|
||||
steps_back: usize,
|
||||
},
|
||||
FlipSelectedLayers {
|
||||
flip_axis: FlipAxis,
|
||||
},
|
||||
@@ -77,11 +81,14 @@ pub enum DocumentMessage {
|
||||
svg: String,
|
||||
transform: DAffine2,
|
||||
parent: LayerNodeIdentifier,
|
||||
insert_index: isize,
|
||||
insert_index: usize,
|
||||
},
|
||||
MoveSelectedLayersTo {
|
||||
parent: LayerNodeIdentifier,
|
||||
insert_index: isize,
|
||||
insert_index: usize,
|
||||
},
|
||||
MoveSelectedLayersToGroup {
|
||||
parent: LayerNodeIdentifier,
|
||||
},
|
||||
NudgeSelectedLayers {
|
||||
delta_x: f64,
|
||||
@@ -103,7 +110,6 @@ pub enum DocumentMessage {
|
||||
},
|
||||
RenderRulers,
|
||||
RenderScrollbars,
|
||||
ResetTransform,
|
||||
SaveDocument,
|
||||
SelectAllLayers,
|
||||
SelectedLayersLower,
|
||||
@@ -118,6 +124,9 @@ pub enum DocumentMessage {
|
||||
ctrl: bool,
|
||||
shift: bool,
|
||||
},
|
||||
SetActivePanel {
|
||||
active_panel: PanelType,
|
||||
},
|
||||
SetBlendModeForSelectedLayers {
|
||||
blend_mode: BlendMode,
|
||||
},
|
||||
@@ -148,9 +157,10 @@ pub enum DocumentMessage {
|
||||
Undo,
|
||||
UndoFinished,
|
||||
UngroupSelectedLayers,
|
||||
UpdateDocumentTransform {
|
||||
transform: glam::DAffine2,
|
||||
UngroupLayer {
|
||||
layer: LayerNodeIdentifier,
|
||||
},
|
||||
PTZUpdate,
|
||||
ZoomCanvasTo100Percent,
|
||||
ZoomCanvasTo200Percent,
|
||||
ZoomCanvasToFitAll,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,10 @@
|
||||
use super::utility_types::TransformIn;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::NodeTemplate;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use bezier_rs::Subpath;
|
||||
use graph_craft::document::{DocumentNode, NodeId, NodeInput};
|
||||
use graph_craft::document::NodeId;
|
||||
use graphene_core::raster::{BlendMode, ImageFrame};
|
||||
use graphene_core::text::Font;
|
||||
use graphene_core::vector::brush_stroke::BrushStroke;
|
||||
@@ -11,59 +12,16 @@ use graphene_core::vector::style::{Fill, Stroke};
|
||||
use graphene_core::vector::PointId;
|
||||
use graphene_core::vector::VectorModificationType;
|
||||
use graphene_core::{Artboard, Color};
|
||||
use graphene_std::vector::misc::BooleanOperation;
|
||||
|
||||
use glam::{DAffine2, DVec2, IVec2};
|
||||
|
||||
#[impl_message(Message, DocumentMessage, GraphOperation)]
|
||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum GraphOperationMessage {
|
||||
AddNodesAsChild {
|
||||
nodes: HashMap<NodeId, DocumentNode>,
|
||||
new_ids: HashMap<NodeId, NodeId>,
|
||||
parent: LayerNodeIdentifier,
|
||||
insert_index: isize,
|
||||
},
|
||||
CreateBooleanOperationNode {
|
||||
node_id: NodeId,
|
||||
operation: BooleanOperation,
|
||||
},
|
||||
DeleteLayer {
|
||||
layer: LayerNodeIdentifier,
|
||||
reconnect: bool,
|
||||
},
|
||||
DisconnectInput {
|
||||
node_id: NodeId,
|
||||
input_index: usize,
|
||||
},
|
||||
DisconnectNodeFromStack {
|
||||
node_id: NodeId,
|
||||
reconnect_to_sibling: bool,
|
||||
},
|
||||
FillSet {
|
||||
layer: LayerNodeIdentifier,
|
||||
fill: Fill,
|
||||
},
|
||||
InsertNodeAtStackIndex {
|
||||
node_id: NodeId,
|
||||
parent: LayerNodeIdentifier,
|
||||
insert_index: usize,
|
||||
},
|
||||
InsertNodeBetween {
|
||||
// Post node
|
||||
post_node_id: NodeId,
|
||||
post_node_input_index: usize,
|
||||
// Inserted node
|
||||
insert_node_id: NodeId,
|
||||
insert_node_output_index: usize,
|
||||
insert_node_input_index: usize,
|
||||
// Pre node
|
||||
pre_node_id: NodeId,
|
||||
pre_node_output_index: usize,
|
||||
},
|
||||
MoveSelectedSiblingsToChild {
|
||||
new_parent: LayerNodeIdentifier,
|
||||
},
|
||||
OpacitySet {
|
||||
layer: LayerNodeIdentifier,
|
||||
opacity: f64,
|
||||
@@ -100,6 +58,9 @@ pub enum GraphOperationMessage {
|
||||
layer: LayerNodeIdentifier,
|
||||
strokes: Vec<BrushStroke>,
|
||||
},
|
||||
SetUpstreamToChain {
|
||||
layer: LayerNodeIdentifier,
|
||||
},
|
||||
NewArtboard {
|
||||
id: NodeId,
|
||||
artboard: Artboard,
|
||||
@@ -108,20 +69,19 @@ pub enum GraphOperationMessage {
|
||||
id: NodeId,
|
||||
image_frame: ImageFrame<Color>,
|
||||
parent: LayerNodeIdentifier,
|
||||
insert_index: isize,
|
||||
insert_index: usize,
|
||||
},
|
||||
NewCustomLayer {
|
||||
id: NodeId,
|
||||
nodes: HashMap<NodeId, DocumentNode>,
|
||||
nodes: Vec<(NodeId, NodeTemplate)>,
|
||||
parent: LayerNodeIdentifier,
|
||||
insert_index: isize,
|
||||
alias: String,
|
||||
insert_index: usize,
|
||||
},
|
||||
NewVectorLayer {
|
||||
id: NodeId,
|
||||
subpaths: Vec<Subpath<PointId>>,
|
||||
parent: LayerNodeIdentifier,
|
||||
insert_index: isize,
|
||||
insert_index: usize,
|
||||
},
|
||||
NewTextLayer {
|
||||
id: NodeId,
|
||||
@@ -129,10 +89,10 @@ pub enum GraphOperationMessage {
|
||||
font: Font,
|
||||
size: f64,
|
||||
parent: LayerNodeIdentifier,
|
||||
insert_index: isize,
|
||||
insert_index: usize,
|
||||
},
|
||||
ResizeArtboard {
|
||||
id: NodeId,
|
||||
layer: LayerNodeIdentifier,
|
||||
location: IVec2,
|
||||
dimensions: IVec2,
|
||||
},
|
||||
@@ -142,45 +102,6 @@ pub enum GraphOperationMessage {
|
||||
svg: String,
|
||||
transform: DAffine2,
|
||||
parent: LayerNodeIdentifier,
|
||||
insert_index: isize,
|
||||
},
|
||||
ShiftUpstream {
|
||||
node_id: NodeId,
|
||||
shift: IVec2,
|
||||
shift_self: bool,
|
||||
},
|
||||
SetNodePosition {
|
||||
node_id: NodeId,
|
||||
position: IVec2,
|
||||
},
|
||||
SetName {
|
||||
layer: LayerNodeIdentifier,
|
||||
name: String,
|
||||
},
|
||||
SetNameImpl {
|
||||
layer: LayerNodeIdentifier,
|
||||
name: String,
|
||||
},
|
||||
SetNodeInput {
|
||||
node_id: NodeId,
|
||||
input_index: usize,
|
||||
input: NodeInput,
|
||||
},
|
||||
ToggleSelectedVisibility,
|
||||
ToggleVisibility {
|
||||
node_id: NodeId,
|
||||
},
|
||||
SetVisibility {
|
||||
node_id: NodeId,
|
||||
visible: bool,
|
||||
},
|
||||
StartPreviewingWithoutRestore,
|
||||
ToggleSelectedLocked,
|
||||
ToggleLocked {
|
||||
node_id: NodeId,
|
||||
},
|
||||
SetLocked {
|
||||
node_id: NodeId,
|
||||
locked: bool,
|
||||
insert_index: usize,
|
||||
},
|
||||
}
|
||||
|
||||
+98
-567
@@ -1,24 +1,21 @@
|
||||
use super::transform_utils;
|
||||
use super::utility_types::ModifyInputsContext;
|
||||
use crate::messages::portfolio::document::node_graph::document_node_types::resolve_document_node_type;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
|
||||
use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, SelectedNodes};
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeNetworkInterface};
|
||||
use crate::messages::portfolio::document::utility_types::nodes::CollapsedLayers;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{generate_uuid, NodeId, NodeInput, NodeNetwork, Previewing};
|
||||
use graph_craft::document::{generate_uuid, NodeId, NodeInput};
|
||||
use graphene_core::renderer::Quad;
|
||||
use graphene_core::text::Font;
|
||||
use graphene_core::vector::style::{Fill, Gradient, GradientStops, GradientType, LineCap, LineJoin, Stroke};
|
||||
use graphene_core::Color;
|
||||
use graphene_std::vector::convert_usvg_path;
|
||||
|
||||
use glam::{DAffine2, DVec2, IVec2};
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
pub struct GraphOperationMessageData<'a> {
|
||||
pub document_network: &'a mut NodeNetwork,
|
||||
pub document_metadata: &'a mut DocumentMetadata,
|
||||
pub selected_nodes: &'a mut SelectedNodes,
|
||||
pub network_interface: &'a mut NodeNetworkInterface,
|
||||
pub collapsed: &'a mut CollapsedLayers,
|
||||
pub node_graph: &'a mut NodeGraphMessageHandler,
|
||||
}
|
||||
@@ -30,301 +27,26 @@ pub struct GraphOperationMessageHandler {}
|
||||
// For changes to the selected network, use NodeGraphMessageHandler. No NodeGraphMessage's should be added here, since they will affect the selected nested network.
|
||||
impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for GraphOperationMessageHandler {
|
||||
fn process_message(&mut self, message: GraphOperationMessage, responses: &mut VecDeque<Message>, data: GraphOperationMessageData) {
|
||||
let GraphOperationMessageData {
|
||||
document_network,
|
||||
document_metadata,
|
||||
selected_nodes,
|
||||
collapsed,
|
||||
node_graph,
|
||||
} = data;
|
||||
let network_interface = data.network_interface;
|
||||
|
||||
match message {
|
||||
GraphOperationMessage::AddNodesAsChild { nodes, new_ids, parent, insert_index } => {
|
||||
let shift = document_network
|
||||
.get_root_node()
|
||||
.and_then(|root_node| {
|
||||
nodes.get(&root_node.id).and_then(|node| {
|
||||
if parent == LayerNodeIdentifier::ROOT_PARENT {
|
||||
return None;
|
||||
};
|
||||
let parent_node_id = parent.to_node();
|
||||
document_network
|
||||
.nodes
|
||||
.get(&parent_node_id)
|
||||
.map(|layer| layer.metadata.position - node.metadata.position + IVec2::new(-8, 0))
|
||||
})
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
for (old_id, mut document_node) in nodes {
|
||||
// Shift copied node
|
||||
document_node.metadata.position += shift;
|
||||
|
||||
// Get the new, non-conflicting id
|
||||
let node_id = *new_ids.get(&old_id).unwrap();
|
||||
let default_inputs = NodeGraphMessageHandler::get_default_inputs(document_network, &Vec::new(), node_id, &node_graph.resolved_types, &document_node);
|
||||
document_node = document_node.map_ids(default_inputs, &new_ids);
|
||||
|
||||
// Insert node into network
|
||||
node_graph.insert_node(node_id, document_node, document_network, &Vec::new());
|
||||
}
|
||||
|
||||
let Some(new_layer_id) = new_ids.get(&NodeId(0)) else {
|
||||
error!("Could not get layer node when adding as child");
|
||||
return;
|
||||
};
|
||||
|
||||
let insert_index = if insert_index < 0 { 0 } else { insert_index as usize };
|
||||
let (downstream_node, upstream_node, input_index) = ModifyInputsContext::get_post_node_with_index(document_network, parent, insert_index);
|
||||
|
||||
responses.add(NodeGraphMessage::SelectedNodesAdd { nodes: vec![*new_layer_id] });
|
||||
|
||||
match (downstream_node, upstream_node) {
|
||||
(Some(downstream_node), Some(upstream_node)) => responses.add(GraphOperationMessage::InsertNodeBetween {
|
||||
post_node_id: downstream_node,
|
||||
post_node_input_index: input_index,
|
||||
insert_node_output_index: 0,
|
||||
insert_node_id: *new_layer_id,
|
||||
insert_node_input_index: 0,
|
||||
pre_node_output_index: 0,
|
||||
pre_node_id: upstream_node,
|
||||
}),
|
||||
(Some(downstream_node), None) => responses.add(GraphOperationMessage::SetNodeInput {
|
||||
node_id: downstream_node,
|
||||
input_index,
|
||||
input: NodeInput::node(*new_layer_id, 0),
|
||||
}),
|
||||
(None, Some(upstream_node)) => responses.add(GraphOperationMessage::InsertNodeBetween {
|
||||
post_node_id: document_network.exports_metadata.0,
|
||||
post_node_input_index: 0,
|
||||
insert_node_output_index: 0,
|
||||
insert_node_id: *new_layer_id,
|
||||
insert_node_input_index: 0,
|
||||
pre_node_output_index: 0,
|
||||
pre_node_id: upstream_node,
|
||||
}),
|
||||
(None, None) => {
|
||||
if let Some(primary_export) = document_network.exports.get_mut(0) {
|
||||
*primary_export = NodeInput::node(*new_layer_id, 0)
|
||||
}
|
||||
}
|
||||
};
|
||||
responses.add(GraphOperationMessage::ShiftUpstream {
|
||||
node_id: *new_layer_id,
|
||||
shift: IVec2::new(0, 3),
|
||||
shift_self: true,
|
||||
});
|
||||
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
GraphOperationMessage::CreateBooleanOperationNode { node_id, operation } => {
|
||||
let new_boolean_operation_node = resolve_document_node_type("Boolean Operation")
|
||||
.expect("Failed to create a Boolean Operation node")
|
||||
.to_document_node_default_inputs(
|
||||
[
|
||||
Some(NodeInput::value(TaggedValue::VectorData(graphene_std::vector::VectorData::empty()), true)),
|
||||
Some(NodeInput::value(TaggedValue::VectorData(graphene_std::vector::VectorData::empty()), true)),
|
||||
Some(NodeInput::value(TaggedValue::BooleanOperation(operation), false)),
|
||||
],
|
||||
Default::default(),
|
||||
);
|
||||
|
||||
node_graph.insert_node(node_id, new_boolean_operation_node, document_network, &Vec::new());
|
||||
}
|
||||
GraphOperationMessage::DeleteLayer { layer, reconnect } => {
|
||||
if layer == LayerNodeIdentifier::ROOT_PARENT {
|
||||
log::error!("Cannot delete ROOT_PARENT");
|
||||
return;
|
||||
}
|
||||
ModifyInputsContext::delete_nodes(node_graph, document_network, selected_nodes, vec![layer.to_node()], reconnect, responses, Vec::new());
|
||||
|
||||
load_network_structure(document_network, document_metadata, collapsed);
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
// Make sure to also update NodeGraphMessage::DisconnectInput when changing this
|
||||
GraphOperationMessage::DisconnectInput { node_id, input_index } => {
|
||||
let Some(existing_input) = document_network
|
||||
.nodes
|
||||
.get(&node_id)
|
||||
.map_or_else(|| document_network.exports.get(input_index), |node| node.inputs.get(input_index))
|
||||
else {
|
||||
warn!("Could not find input for {node_id} at index {input_index} when disconnecting");
|
||||
return;
|
||||
};
|
||||
|
||||
let tagged_value = TaggedValue::from_type(&ModifyInputsContext::get_input_type(document_network, &Vec::new(), node_id, &node_graph.resolved_types, input_index));
|
||||
|
||||
let mut input = NodeInput::value(tagged_value, true);
|
||||
if let NodeInput::Value { exposed, .. } = &mut input {
|
||||
*exposed = existing_input.is_exposed();
|
||||
}
|
||||
if node_id == document_network.exports_metadata.0 {
|
||||
// Since it is only possible to drag the solid line, there must be a root_node_to_restore
|
||||
if let Previewing::Yes { .. } = document_network.previewing {
|
||||
responses.add(GraphOperationMessage::StartPreviewingWithoutRestore);
|
||||
}
|
||||
// If there is no preview, then disconnect
|
||||
else {
|
||||
responses.add(GraphOperationMessage::SetNodeInput { node_id, input_index, input });
|
||||
}
|
||||
} else {
|
||||
responses.add(GraphOperationMessage::SetNodeInput { node_id, input_index, input });
|
||||
}
|
||||
if document_network.connected_to_output(node_id) {
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
responses.add(NodeGraphMessage::SendGraph);
|
||||
}
|
||||
GraphOperationMessage::DisconnectNodeFromStack { node_id, reconnect_to_sibling } => {
|
||||
ModifyInputsContext::remove_references_from_network(node_graph, document_network, node_id, reconnect_to_sibling, &Vec::new());
|
||||
responses.add(GraphOperationMessage::DisconnectInput { node_id, input_index: 0 });
|
||||
}
|
||||
GraphOperationMessage::FillSet { layer, fill } => {
|
||||
if layer == LayerNodeIdentifier::ROOT_PARENT {
|
||||
log::error!("Cannot run FillSet on ROOT_PARENT");
|
||||
return;
|
||||
}
|
||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
|
||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) {
|
||||
modify_inputs.fill_set(fill);
|
||||
}
|
||||
}
|
||||
GraphOperationMessage::InsertNodeAtStackIndex { node_id, parent, insert_index } => {
|
||||
let (post_node_id, pre_node_id, post_node_input_index) = ModifyInputsContext::get_post_node_with_index(document_network, parent, insert_index);
|
||||
|
||||
// `layer_to_move` should always correspond to a node.
|
||||
let Some(layer_to_move_node) = document_network.nodes.get(&node_id) else {
|
||||
log::error!("Layer node not found when inserting node {} at index {}", node_id, insert_index);
|
||||
return;
|
||||
};
|
||||
|
||||
// Move current layer to post node.
|
||||
let current_position = layer_to_move_node.metadata.position;
|
||||
let new_position = if let Some(post_node_id) = post_node_id {
|
||||
document_network.nodes.get(&post_node_id).expect("Post node id should always refer to a node").metadata.position
|
||||
} else if let Some(root_node) = document_network.get_root_node() {
|
||||
document_network.nodes.get(&root_node.id).expect("Root node id should always refer to a node").metadata.position + IVec2::new(8, -3)
|
||||
} else {
|
||||
document_network.exports_metadata.1
|
||||
};
|
||||
|
||||
// If moved to top of a layer stack, move to the left of the post node. If moved within a stack, move directly on the post node. The stack will be shifted down later.
|
||||
let offset_to_post_node = if insert_index == 0 {
|
||||
new_position - current_position - IVec2::new(8, 0)
|
||||
} else {
|
||||
new_position - current_position
|
||||
};
|
||||
|
||||
responses.add(GraphOperationMessage::ShiftUpstream {
|
||||
node_id,
|
||||
shift: offset_to_post_node,
|
||||
shift_self: true,
|
||||
});
|
||||
|
||||
match (post_node_id, pre_node_id) {
|
||||
(Some(post_node_id), Some(pre_node_id)) => responses.add(GraphOperationMessage::InsertNodeBetween {
|
||||
post_node_id,
|
||||
post_node_input_index,
|
||||
insert_node_output_index: 0,
|
||||
insert_node_id: node_id,
|
||||
insert_node_input_index: 0,
|
||||
pre_node_output_index: 0,
|
||||
pre_node_id,
|
||||
}),
|
||||
(None, Some(pre_node_id)) => responses.add(GraphOperationMessage::InsertNodeBetween {
|
||||
post_node_id: document_network.exports_metadata.0,
|
||||
post_node_input_index: 0,
|
||||
insert_node_output_index: 0,
|
||||
insert_node_id: node_id,
|
||||
insert_node_input_index: 0,
|
||||
pre_node_output_index: 0,
|
||||
pre_node_id,
|
||||
}),
|
||||
(Some(post_node_id), None) => responses.add(GraphOperationMessage::SetNodeInput {
|
||||
node_id: post_node_id,
|
||||
input_index: post_node_input_index,
|
||||
input: NodeInput::node(node_id, 0),
|
||||
}),
|
||||
(None, None) => {
|
||||
if let Some(primary_export) = document_network.exports.get_mut(0) {
|
||||
*primary_export = NodeInput::node(node_id, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shift stack down, starting at the moved node.
|
||||
responses.add(GraphOperationMessage::ShiftUpstream {
|
||||
node_id,
|
||||
shift: IVec2::new(0, 3),
|
||||
shift_self: true,
|
||||
});
|
||||
}
|
||||
GraphOperationMessage::InsertNodeBetween {
|
||||
post_node_id,
|
||||
post_node_input_index,
|
||||
insert_node_output_index,
|
||||
insert_node_id,
|
||||
insert_node_input_index,
|
||||
pre_node_output_index,
|
||||
pre_node_id,
|
||||
} => {
|
||||
let post_node = document_network.nodes.get(&post_node_id);
|
||||
let Some((post_node_input_index, _)) = post_node
|
||||
.map_or(&document_network.exports, |post_node| &post_node.inputs)
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|input| input.1.is_exposed())
|
||||
.nth(post_node_input_index)
|
||||
else {
|
||||
error!("Failed to find input index {post_node_input_index} on node {post_node_id:#?}");
|
||||
return;
|
||||
};
|
||||
let Some(insert_node) = document_network.nodes.get(&insert_node_id) else {
|
||||
error!("Insert node not found");
|
||||
return;
|
||||
};
|
||||
let Some((insert_node_input_index, _)) = insert_node.inputs.iter().enumerate().filter(|input| input.1.is_exposed()).nth(insert_node_input_index) else {
|
||||
error!("Failed to find input index {insert_node_input_index} on node {insert_node_id:#?}");
|
||||
return;
|
||||
};
|
||||
|
||||
let post_input = NodeInput::node(insert_node_id, insert_node_output_index);
|
||||
responses.add(GraphOperationMessage::SetNodeInput {
|
||||
node_id: post_node_id,
|
||||
input_index: post_node_input_index,
|
||||
input: post_input,
|
||||
});
|
||||
|
||||
let insert_input = NodeInput::node(pre_node_id, pre_node_output_index);
|
||||
responses.add(GraphOperationMessage::SetNodeInput {
|
||||
node_id: insert_node_id,
|
||||
input_index: insert_node_input_index,
|
||||
input: insert_input,
|
||||
});
|
||||
}
|
||||
GraphOperationMessage::OpacitySet { layer, opacity } => {
|
||||
if layer == LayerNodeIdentifier::ROOT_PARENT {
|
||||
log::error!("Cannot run OpacitySet on ROOT_PARENT");
|
||||
return;
|
||||
}
|
||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
|
||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) {
|
||||
modify_inputs.opacity_set(opacity);
|
||||
}
|
||||
}
|
||||
GraphOperationMessage::BlendModeSet { layer, blend_mode } => {
|
||||
if layer == LayerNodeIdentifier::ROOT_PARENT {
|
||||
log::error!("Cannot run BlendModeSet on ROOT_PARENT");
|
||||
return;
|
||||
}
|
||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
|
||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) {
|
||||
modify_inputs.blend_mode_set(blend_mode);
|
||||
}
|
||||
}
|
||||
GraphOperationMessage::StrokeSet { layer, stroke } => {
|
||||
if layer == LayerNodeIdentifier::ROOT_PARENT {
|
||||
log::error!("Cannot run StrokeSet on ROOT_PARENT");
|
||||
return;
|
||||
}
|
||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
|
||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) {
|
||||
modify_inputs.stroke_set(stroke);
|
||||
}
|
||||
}
|
||||
@@ -334,12 +56,8 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
|
||||
transform_in,
|
||||
skip_rerender,
|
||||
} => {
|
||||
if layer == LayerNodeIdentifier::ROOT_PARENT {
|
||||
log::error!("Cannot run TransformChange on ROOT_PARENT");
|
||||
return;
|
||||
}
|
||||
let parent_transform = document_metadata.downstream_transform_to_viewport(layer);
|
||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
|
||||
let parent_transform = network_interface.document_metadata().downstream_transform_to_viewport(layer);
|
||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) {
|
||||
modify_inputs.transform_change(transform, transform_in, parent_transform, skip_rerender);
|
||||
}
|
||||
}
|
||||
@@ -349,14 +67,9 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
|
||||
transform_in,
|
||||
skip_rerender,
|
||||
} => {
|
||||
if layer == LayerNodeIdentifier::ROOT_PARENT {
|
||||
log::error!("Cannot run TransformSet on ROOT_PARENT");
|
||||
return;
|
||||
}
|
||||
let parent_transform = document_metadata.downstream_transform_to_viewport(layer);
|
||||
|
||||
let current_transform = Some(document_metadata.transform_to_viewport(layer));
|
||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
|
||||
let parent_transform = network_interface.document_metadata().downstream_transform_to_viewport(layer);
|
||||
let current_transform = Some(network_interface.document_metadata().transform_to_viewport(layer));
|
||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) {
|
||||
modify_inputs.transform_set(transform, transform_in, parent_transform, current_transform, skip_rerender);
|
||||
}
|
||||
}
|
||||
@@ -365,7 +78,7 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
|
||||
log::error!("Cannot run TransformSetPivot on ROOT_PARENT");
|
||||
return;
|
||||
}
|
||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
|
||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) {
|
||||
modify_inputs.pivot_set(pivot);
|
||||
}
|
||||
}
|
||||
@@ -374,96 +87,51 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
|
||||
log::error!("Cannot run Vector on ROOT_PARENT");
|
||||
return;
|
||||
}
|
||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
|
||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) {
|
||||
modify_inputs.vector_modify(modification_type);
|
||||
}
|
||||
}
|
||||
GraphOperationMessage::Brush { layer, strokes } => {
|
||||
if layer == LayerNodeIdentifier::ROOT_PARENT {
|
||||
log::error!("Cannot run Brush on ROOT_PARENT");
|
||||
return;
|
||||
}
|
||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
|
||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) {
|
||||
modify_inputs.brush_modify(strokes);
|
||||
}
|
||||
}
|
||||
GraphOperationMessage::MoveSelectedSiblingsToChild { new_parent } => {
|
||||
let Some(group_parent) = new_parent.parent(document_metadata) else {
|
||||
log::error!("Could not find parent for layer {:?}", new_parent);
|
||||
GraphOperationMessage::SetUpstreamToChain { layer } => {
|
||||
let Some(first_chain_node) = network_interface
|
||||
.upstream_flow_back_from_nodes(
|
||||
vec![layer.to_node()],
|
||||
&[],
|
||||
crate::messages::portfolio::document::utility_types::network_interface::FlowType::HorizontalFlow,
|
||||
)
|
||||
.nth(1)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Create a vec of nodes to move with all selected layers in the parent layer child stack, as well as each non layer sibling directly upstream of the selected layer
|
||||
let mut selected_siblings = Vec::new();
|
||||
|
||||
// Skip over horizontal non layer node chain that feeds into parent
|
||||
let Some(mut current_stack_node_id) = group_parent.first_child(document_metadata).map(|current_stack_node| current_stack_node.to_node()) else {
|
||||
log::error!("Folder should always have child");
|
||||
return;
|
||||
};
|
||||
let current_stack_node_id = &mut current_stack_node_id;
|
||||
|
||||
loop {
|
||||
let mut current_stack_node = document_network.nodes.get(current_stack_node_id).expect("Current stack node id should always be a node");
|
||||
|
||||
// Check if the current stack node is a selected layer
|
||||
if selected_nodes
|
||||
.selected_layers(document_metadata)
|
||||
.any(|selected_node_id| selected_node_id.to_node() == *current_stack_node_id)
|
||||
{
|
||||
selected_siblings.push(*current_stack_node_id);
|
||||
|
||||
// Push all non layer sibling nodes directly upstream of the selected layer
|
||||
loop {
|
||||
let Some(NodeInput::Node { node_id, .. }) = current_stack_node.inputs.first() else { break };
|
||||
|
||||
let next_node = document_network.nodes.get(node_id).expect("Stack node id should always be a node");
|
||||
|
||||
// If the next node is a layer, immediately break and leave current stack node as the non layer node
|
||||
if next_node.is_layer {
|
||||
break;
|
||||
}
|
||||
|
||||
*current_stack_node_id = *node_id;
|
||||
current_stack_node = next_node;
|
||||
|
||||
selected_siblings.push(*current_stack_node_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Get next node
|
||||
let Some(NodeInput::Node { node_id, .. }) = current_stack_node.inputs.first() else { break };
|
||||
*current_stack_node_id = *node_id;
|
||||
}
|
||||
|
||||
// Start with the furthest upstream node, move it as a child of the new folder, and continue downstream for each layer in vec
|
||||
for node_to_move in selected_siblings.iter().rev() {
|
||||
// Disconnect node, then reconnect as new child
|
||||
responses.add(GraphOperationMessage::DisconnectNodeFromStack {
|
||||
node_id: *node_to_move,
|
||||
reconnect_to_sibling: true,
|
||||
});
|
||||
|
||||
responses.add(GraphOperationMessage::InsertNodeAtStackIndex {
|
||||
node_id: *node_to_move,
|
||||
parent: new_parent,
|
||||
insert_index: 0,
|
||||
});
|
||||
}
|
||||
|
||||
let Some(most_upstream_sibling) = selected_siblings.last() else {
|
||||
return;
|
||||
};
|
||||
responses.add(GraphOperationMessage::DisconnectInput {
|
||||
node_id: *most_upstream_sibling,
|
||||
input_index: 0,
|
||||
});
|
||||
network_interface.force_set_upstream_to_chain(&first_chain_node, &[]);
|
||||
}
|
||||
GraphOperationMessage::NewArtboard { id, artboard } => {
|
||||
if let Some(artboard_id) = ModifyInputsContext::create_artboard(node_graph, document_network, id, artboard) {
|
||||
responses.add_front(NodeGraphMessage::SelectedNodesSet { nodes: vec![artboard_id] });
|
||||
let mut modify_inputs = ModifyInputsContext::new(network_interface, responses);
|
||||
|
||||
let artboard_layer = modify_inputs.create_artboard(id, artboard);
|
||||
network_interface.move_layer_to_stack(artboard_layer, LayerNodeIdentifier::ROOT_PARENT, 0, &[]);
|
||||
|
||||
// If there is a non artboard feeding into the primary input of the artboard, move it to the secondary input
|
||||
let Some(artboard) = network_interface.network(&[]).unwrap().nodes.get(&id) else {
|
||||
log::error!("Artboard not created");
|
||||
return;
|
||||
};
|
||||
let primary_input = artboard.inputs.first().expect("Artboard should have a primary input").clone();
|
||||
if let NodeInput::Node { node_id, .. } = &primary_input {
|
||||
if network_interface.is_layer(node_id, &[]) && !network_interface.is_artboard(node_id, &[]) {
|
||||
network_interface.move_layer_to_stack(LayerNodeIdentifier::new(*node_id, network_interface), artboard_layer, 0, &[]);
|
||||
} else {
|
||||
network_interface.disconnect_input(&InputConnector::node(artboard_layer.to_node(), 0), &[]);
|
||||
network_interface.set_input(&InputConnector::node(id, 0), primary_input, &[]);
|
||||
}
|
||||
}
|
||||
load_network_structure(document_network, document_metadata, collapsed);
|
||||
responses.add_front(NodeGraphMessage::SelectedNodesSet { nodes: vec![id] });
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
GraphOperationMessage::NewBitmapLayer {
|
||||
id,
|
||||
@@ -471,71 +139,38 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
|
||||
parent,
|
||||
insert_index,
|
||||
} => {
|
||||
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
|
||||
if let Some(layer) = modify_inputs.create_layer(id, parent, insert_index) {
|
||||
ModifyInputsContext::insert_image_data(node_graph, document_network, image_frame, layer, responses);
|
||||
}
|
||||
let mut modify_inputs = ModifyInputsContext::new(network_interface, responses);
|
||||
let layer = modify_inputs.create_layer(id);
|
||||
modify_inputs.insert_image_data(image_frame, layer);
|
||||
network_interface.move_layer_to_stack(layer, parent, insert_index, &[]);
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
GraphOperationMessage::NewCustomLayer {
|
||||
id,
|
||||
nodes,
|
||||
parent,
|
||||
insert_index,
|
||||
alias,
|
||||
} => {
|
||||
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
|
||||
GraphOperationMessage::NewCustomLayer { id, nodes, parent, insert_index } => {
|
||||
let mut modify_inputs = ModifyInputsContext::new(network_interface, responses);
|
||||
let layer = modify_inputs.create_layer(id);
|
||||
|
||||
if let Some(layer) = modify_inputs.create_layer(id, parent, insert_index) {
|
||||
let new_ids: HashMap<_, _> = nodes.iter().map(|(&id, _)| (id, NodeId(generate_uuid()))).collect();
|
||||
if !nodes.is_empty() {
|
||||
// Add the nodes to the network
|
||||
let new_ids: HashMap<_, _> = nodes.iter().map(|(id, _)| (*id, NodeId(generate_uuid()))).collect();
|
||||
// Since all the new nodes are already connected, just connect the input of the layer to first new node
|
||||
let first_new_node_id = new_ids[&NodeId(0)];
|
||||
responses.add(NodeGraphMessage::AddNodes { nodes, new_ids });
|
||||
|
||||
if let Some(node) = modify_inputs.document_network.nodes.get_mut(&id) {
|
||||
node.alias.clone_from(&alias);
|
||||
}
|
||||
|
||||
let shift = nodes
|
||||
.get(&NodeId(0))
|
||||
.and_then(|node| {
|
||||
modify_inputs
|
||||
.document_network
|
||||
.nodes
|
||||
.get(&layer)
|
||||
.map(|layer| layer.metadata.position - node.metadata.position + IVec2::new(-8, 0))
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
for (old_id, mut document_node) in nodes {
|
||||
// Shift copied node
|
||||
document_node.metadata.position += shift;
|
||||
|
||||
// Get the new, non-conflicting id
|
||||
let node_id = *new_ids.get(&old_id).unwrap();
|
||||
let default_inputs = NodeGraphMessageHandler::get_default_inputs(document_network, &Vec::new(), node_id, &node_graph.resolved_types, &document_node);
|
||||
document_node = document_node.map_ids(default_inputs, &new_ids);
|
||||
|
||||
// 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) {
|
||||
if let Some(&input) = new_ids.get(&NodeId(0)) {
|
||||
layer_node.inputs[1] = NodeInput::node(input, 0);
|
||||
}
|
||||
}
|
||||
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
} else {
|
||||
error!("Creating new custom layer failed");
|
||||
responses.add(NodeGraphMessage::SetInput {
|
||||
input_connector: InputConnector::node(layer.to_node(), 1),
|
||||
input: NodeInput::node(first_new_node_id, 0),
|
||||
});
|
||||
}
|
||||
|
||||
load_network_structure(document_network, document_metadata, collapsed);
|
||||
// Move the layer and all nodes to the correct position in the network
|
||||
responses.add(NodeGraphMessage::MoveLayerToStack { layer, parent, insert_index });
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
GraphOperationMessage::NewVectorLayer { id, subpaths, parent, insert_index } => {
|
||||
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
|
||||
if let Some(layer) = modify_inputs.create_layer(id, parent, insert_index) {
|
||||
modify_inputs.insert_vector_data(subpaths, layer);
|
||||
}
|
||||
load_network_structure(document_network, document_metadata, collapsed);
|
||||
let mut modify_inputs = ModifyInputsContext::new(network_interface, responses);
|
||||
let layer = modify_inputs.create_layer(id);
|
||||
modify_inputs.insert_vector_data(subpaths, layer);
|
||||
network_interface.move_layer_to_stack(layer, parent, insert_index, &[]);
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
GraphOperationMessage::NewTextLayer {
|
||||
id,
|
||||
@@ -545,22 +180,25 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
|
||||
parent,
|
||||
insert_index,
|
||||
} => {
|
||||
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
|
||||
if let Some(layer) = modify_inputs.create_layer(id, parent, insert_index) {
|
||||
modify_inputs.insert_text(text, font, size, layer);
|
||||
}
|
||||
load_network_structure(document_network, document_metadata, collapsed);
|
||||
let mut modify_inputs = ModifyInputsContext::new(network_interface, responses);
|
||||
let layer = modify_inputs.create_layer(id);
|
||||
modify_inputs.insert_text(text, font, size, layer);
|
||||
network_interface.move_layer_to_stack(layer, parent, insert_index, &[]);
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
GraphOperationMessage::ResizeArtboard { id, location, dimensions } => {
|
||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(id, document_network, document_metadata, node_graph, responses) {
|
||||
GraphOperationMessage::ResizeArtboard { layer, location, dimensions } => {
|
||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) {
|
||||
modify_inputs.resize_artboard(location, dimensions);
|
||||
}
|
||||
}
|
||||
GraphOperationMessage::ClearArtboards => {
|
||||
for &artboard in document_metadata.all_artboards() {
|
||||
responses.add(GraphOperationMessage::DeleteLayer { layer: artboard, reconnect: true });
|
||||
for artboard in network_interface.all_artboards() {
|
||||
responses.add(NodeGraphMessage::DeleteNodes {
|
||||
node_ids: vec![artboard.to_node()],
|
||||
reconnect: false,
|
||||
});
|
||||
}
|
||||
load_network_structure(document_network, document_metadata, collapsed);
|
||||
// TODO: Replace deleted artboards with merge nodes
|
||||
}
|
||||
GraphOperationMessage::NewSvg {
|
||||
id,
|
||||
@@ -580,112 +218,9 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
|
||||
return;
|
||||
}
|
||||
};
|
||||
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
|
||||
let mut modify_inputs = ModifyInputsContext::new(network_interface, responses);
|
||||
|
||||
import_usvg_node(&mut modify_inputs, &usvg::Node::Group(Box::new(tree.root().clone())), transform, id, parent, insert_index);
|
||||
load_network_structure(document_network, document_metadata, collapsed);
|
||||
}
|
||||
GraphOperationMessage::SetNodePosition { node_id, position } => {
|
||||
let Some(node) = document_network.nodes.get_mut(&node_id) else {
|
||||
log::error!("Failed to find node {node_id} when setting position");
|
||||
return;
|
||||
};
|
||||
node.metadata.position = position;
|
||||
node_graph.update_click_target(node_id, document_network, Vec::new());
|
||||
responses.add(DocumentMessage::RenderRulers);
|
||||
responses.add(DocumentMessage::RenderScrollbars);
|
||||
}
|
||||
GraphOperationMessage::SetName { layer, name } => {
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
responses.add(GraphOperationMessage::SetNameImpl { layer, name });
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
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);
|
||||
responses.add(NodeGraphMessage::SendGraph);
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
}
|
||||
GraphOperationMessage::SetNodeInput { node_id, input_index, input } => {
|
||||
if ModifyInputsContext::set_input(node_graph, document_network, &Vec::new(), node_id, input_index, input, true) {
|
||||
load_network_structure(document_network, document_metadata, collapsed);
|
||||
}
|
||||
}
|
||||
GraphOperationMessage::ShiftUpstream { node_id, shift, shift_self } => {
|
||||
ModifyInputsContext::shift_upstream(node_graph, document_network, &Vec::new(), node_id, shift, shift_self);
|
||||
}
|
||||
GraphOperationMessage::ToggleSelectedVisibility => {
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
|
||||
// If any of the selected nodes are hidden, show them all. Otherwise, hide them all.
|
||||
let visible = !selected_nodes.selected_layers(document_metadata).all(|layer| document_metadata.node_is_visible(layer.to_node()));
|
||||
|
||||
for layer in selected_nodes.selected_layers(document_metadata) {
|
||||
responses.add(GraphOperationMessage::SetVisibility { node_id: layer.to_node(), visible });
|
||||
}
|
||||
}
|
||||
GraphOperationMessage::ToggleVisibility { node_id } => {
|
||||
let visible = !document_metadata.node_is_visible(node_id);
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
responses.add(GraphOperationMessage::SetVisibility { node_id, visible });
|
||||
}
|
||||
GraphOperationMessage::SetVisibility { node_id, visible } => {
|
||||
// Set what we determined shall be the visibility of the node
|
||||
let Some(node) = document_network.nodes.get_mut(&node_id) else {
|
||||
log::error!("Could not get node {:?} in GraphOperationMessage::SetVisibility", node_id);
|
||||
return;
|
||||
};
|
||||
node.visible = visible;
|
||||
|
||||
// Only generate node graph if one of the selected nodes is connected to the output
|
||||
if document_network.connected_to_output(node_id) {
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
|
||||
document_metadata.load_structure(document_network);
|
||||
responses.add(NodeGraphMessage::SelectedNodesUpdated);
|
||||
responses.add(PropertiesPanelMessage::Refresh);
|
||||
}
|
||||
GraphOperationMessage::StartPreviewingWithoutRestore => {
|
||||
document_network.start_previewing_without_restore();
|
||||
}
|
||||
GraphOperationMessage::ToggleSelectedLocked => {
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
|
||||
// If any of the selected nodes are locked, show them all. Otherwise, hide them all.
|
||||
let locked = !selected_nodes.selected_layers(document_metadata).all(|layer| document_metadata.node_is_locked(layer.to_node()));
|
||||
|
||||
for layer in selected_nodes.selected_layers(document_metadata) {
|
||||
responses.add(GraphOperationMessage::SetLocked { node_id: layer.to_node(), locked });
|
||||
}
|
||||
}
|
||||
GraphOperationMessage::ToggleLocked { node_id } => {
|
||||
let Some(node) = document_network.nodes.get(&node_id) else {
|
||||
log::error!("Cannot get node {:?} in GraphOperationMessage::ToggleLocked", node_id);
|
||||
return;
|
||||
};
|
||||
|
||||
let locked = !node.locked;
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
responses.add(GraphOperationMessage::SetLocked { node_id, locked });
|
||||
}
|
||||
GraphOperationMessage::SetLocked { node_id, locked } => {
|
||||
let Some(node) = document_network.nodes.get_mut(&node_id) else { return };
|
||||
node.locked = locked;
|
||||
|
||||
if document_network.connected_to_output(node_id) {
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
|
||||
document_metadata.load_structure(document_network);
|
||||
responses.add(NodeGraphMessage::SelectedNodesUpdated)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -695,11 +230,6 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_network_structure(document_network: &NodeNetwork, document_metadata: &mut DocumentMetadata, collapsed: &mut CollapsedLayers) {
|
||||
document_metadata.load_structure(document_network);
|
||||
collapsed.0.retain(|&layer| document_metadata.layer_exists(layer));
|
||||
}
|
||||
|
||||
fn usvg_color(c: usvg::Color, a: f32) -> Color {
|
||||
Color::from_rgbaf32_unchecked(c.red as f32 / 255., c.green as f32 / 255., c.blue as f32 / 255., a)
|
||||
}
|
||||
@@ -708,15 +238,13 @@ fn usvg_transform(c: usvg::Transform) -> DAffine2 {
|
||||
DAffine2::from_cols_array(&[c.sx as f64, c.ky as f64, c.kx as f64, c.sy as f64, c.tx as f64, c.ty as f64])
|
||||
}
|
||||
|
||||
fn import_usvg_node(modify_inputs: &mut ModifyInputsContext, node: &usvg::Node, transform: DAffine2, id: NodeId, parent: LayerNodeIdentifier, insert_index: isize) {
|
||||
let Some(layer) = modify_inputs.create_layer(id, parent, insert_index) else {
|
||||
return;
|
||||
};
|
||||
fn import_usvg_node(modify_inputs: &mut ModifyInputsContext, node: &usvg::Node, transform: DAffine2, id: NodeId, parent: LayerNodeIdentifier, insert_index: usize) {
|
||||
let layer = modify_inputs.create_layer(id);
|
||||
modify_inputs.layer_node = Some(layer);
|
||||
match node {
|
||||
usvg::Node::Group(group) => {
|
||||
for child in group.children() {
|
||||
import_usvg_node(modify_inputs, child, transform, NodeId(generate_uuid()), LayerNodeIdentifier::new_unchecked(layer), -1);
|
||||
import_usvg_node(modify_inputs, child, transform, NodeId(generate_uuid()), layer, 0);
|
||||
}
|
||||
modify_inputs.layer_node = Some(layer);
|
||||
}
|
||||
@@ -725,9 +253,12 @@ fn import_usvg_node(modify_inputs: &mut ModifyInputsContext, node: &usvg::Node,
|
||||
let bounds = subpaths.iter().filter_map(|subpath| subpath.bounding_box()).reduce(Quad::combine_bounds).unwrap_or_default();
|
||||
modify_inputs.insert_vector_data(subpaths, layer);
|
||||
|
||||
modify_inputs.modify_inputs("Transform", true, |inputs, _node_id, _metadata| {
|
||||
transform_utils::update_transform(inputs, transform * usvg_transform(node.abs_transform()));
|
||||
});
|
||||
modify_inputs.network_interface.move_layer_to_stack(layer, parent, insert_index, &[]);
|
||||
|
||||
if let Some(transform_node_id) = modify_inputs.get_existing_node_id("Transform") {
|
||||
transform_utils::update_transform(modify_inputs.network_interface, &transform_node_id, transform * usvg_transform(node.abs_transform()));
|
||||
}
|
||||
|
||||
let bounds_transform = DAffine2::from_scale_angle_translation(bounds[1] - bounds[0], 0., bounds[0]);
|
||||
apply_usvg_fill(path.fill(), modify_inputs, transform * usvg_transform(node.abs_transform()), bounds_transform);
|
||||
apply_usvg_stroke(path.stroke(), modify_inputs);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeNetworkInterface};
|
||||
|
||||
use bezier_rs::Subpath;
|
||||
use graph_craft::document::{value::TaggedValue, NodeInput};
|
||||
use graph_craft::document::{value::TaggedValue, NodeId, NodeInput};
|
||||
use graphene_core::vector::PointId;
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
@@ -30,13 +32,13 @@ pub fn compute_scale_angle_translation_shear(transform: DAffine2) -> (DVec2, f64
|
||||
}
|
||||
|
||||
/// Update the inputs of the transform node to match a new transform
|
||||
pub fn update_transform(inputs: &mut [NodeInput], transform: DAffine2) {
|
||||
pub fn update_transform(network_interface: &mut NodeNetworkInterface, node_id: &NodeId, transform: DAffine2) {
|
||||
let (scale, angle, translation, shear) = compute_scale_angle_translation_shear(transform);
|
||||
|
||||
inputs[1] = NodeInput::value(TaggedValue::DVec2(translation), false);
|
||||
inputs[2] = NodeInput::value(TaggedValue::F64(angle), false);
|
||||
inputs[3] = NodeInput::value(TaggedValue::DVec2(scale), false);
|
||||
inputs[4] = NodeInput::value(TaggedValue::DVec2(shear), false);
|
||||
network_interface.set_input(&InputConnector::node(*node_id, 1), NodeInput::value(TaggedValue::DVec2(translation), false), &[]);
|
||||
network_interface.set_input(&InputConnector::node(*node_id, 2), NodeInput::value(TaggedValue::F64(angle), false), &[]);
|
||||
network_interface.set_input(&InputConnector::node(*node_id, 3), NodeInput::value(TaggedValue::DVec2(scale), false), &[]);
|
||||
network_interface.set_input(&InputConnector::node(*node_id, 4), NodeInput::value(TaggedValue::DVec2(shear), false), &[]);
|
||||
}
|
||||
|
||||
// TODO: This should be extracted from the graph at the location of the transform node.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,8 +6,8 @@ use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion};
|
||||
use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition;
|
||||
use crate::messages::portfolio::document::navigation::utility_types::NavigationOperation;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::DocumentMetadata;
|
||||
use crate::messages::portfolio::document::utility_types::misc::PTZ;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||
|
||||
@@ -16,14 +16,12 @@ use graph_craft::document::NodeId;
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
pub struct NavigationMessageData<'a> {
|
||||
pub metadata: &'a DocumentMetadata,
|
||||
pub network_interface: &'a mut NodeNetworkInterface,
|
||||
pub breadcrumb_network_path: &'a [NodeId],
|
||||
pub ipp: &'a InputPreprocessorMessageHandler,
|
||||
pub selection_bounds: Option<[DVec2; 2]>,
|
||||
pub document_ptz: &'a mut PTZ,
|
||||
pub node_graph_ptz: &'a mut HashMap<Vec<NodeId>, PTZ>,
|
||||
pub graph_view_overlay_open: bool,
|
||||
pub node_graph_handler: &'a NodeGraphMessageHandler,
|
||||
pub node_graph_to_viewport: &'a DAffine2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
@@ -36,24 +34,46 @@ pub struct NavigationMessageHandler {
|
||||
impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for NavigationMessageHandler {
|
||||
fn process_message(&mut self, message: NavigationMessage, responses: &mut VecDeque<Message>, data: NavigationMessageData) {
|
||||
let NavigationMessageData {
|
||||
metadata,
|
||||
network_interface,
|
||||
breadcrumb_network_path,
|
||||
ipp,
|
||||
selection_bounds,
|
||||
document_ptz,
|
||||
node_graph_ptz,
|
||||
graph_view_overlay_open,
|
||||
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())
|
||||
|
||||
fn get_ptz<'a>(document_ptz: &'a PTZ, network_interface: &'a NodeNetworkInterface, graph_view_overlay_open: bool, breadcrumb_network_path: &[NodeId]) -> Option<&'a PTZ> {
|
||||
if !graph_view_overlay_open {
|
||||
Some(document_ptz)
|
||||
} else {
|
||||
let network_metadata = network_interface.network_metadata(breadcrumb_network_path)?;
|
||||
Some(&network_metadata.persistent_metadata.navigation_metadata.node_graph_ptz)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_ptz_mut<'a>(document_ptz: &'a mut PTZ, network_interface: &'a mut NodeNetworkInterface, graph_view_overlay_open: bool, breadcrumb_network_path: &[NodeId]) -> Option<&'a mut PTZ> {
|
||||
if !graph_view_overlay_open {
|
||||
Some(document_ptz)
|
||||
} else {
|
||||
let Some(node_graph_ptz) = network_interface.node_graph_ptz_mut(breadcrumb_network_path) else {
|
||||
log::error!("Could not get node graph PTZ in NavigationMessageHandler process_message");
|
||||
return None;
|
||||
};
|
||||
Some(node_graph_ptz)
|
||||
}
|
||||
}
|
||||
|
||||
let Some(ptz) = get_ptz(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
||||
log::error!("Could not get PTZ in NavigationMessageHandler process_message");
|
||||
return;
|
||||
};
|
||||
let old_zoom = ptz.zoom();
|
||||
|
||||
match message {
|
||||
NavigationMessage::BeginCanvasPan => {
|
||||
let Some(ptz) = get_ptz(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
||||
return;
|
||||
};
|
||||
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Grabbing });
|
||||
|
||||
responses.add(FrontendMessage::UpdateInputHints {
|
||||
@@ -64,6 +84,9 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||
self.navigation_operation = NavigationOperation::Pan { pan_original_for_abort: ptz.pan };
|
||||
}
|
||||
NavigationMessage::BeginCanvasTilt { was_dispatched_from_menu } => {
|
||||
let Some(ptz) = get_ptz(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
||||
return;
|
||||
};
|
||||
// If the node graph is open, prevent tilt and instead start panning
|
||||
if graph_view_overlay_open {
|
||||
responses.add(NavigationMessage::BeginCanvasPan);
|
||||
@@ -94,6 +117,10 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||
}
|
||||
}
|
||||
NavigationMessage::BeginCanvasZoom => {
|
||||
let Some(ptz) = get_ptz(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
||||
return;
|
||||
};
|
||||
|
||||
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::ZoomIn });
|
||||
responses.add(FrontendMessage::UpdateInputHints {
|
||||
hint_data: HintData(vec![
|
||||
@@ -117,24 +144,27 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||
self.mouse_position = ipp.mouse.position;
|
||||
}
|
||||
NavigationMessage::CanvasPan { delta } => {
|
||||
let transformed_delta = if !graph_view_overlay_open {
|
||||
metadata.document_to_viewport.inverse().transform_vector2(delta)
|
||||
} else {
|
||||
node_graph_to_viewport.inverse().transform_vector2(delta)
|
||||
let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
||||
log::error!("Could not get PTZ in CanvasPan");
|
||||
return;
|
||||
};
|
||||
let document_to_viewport = self.calculate_offset_transform(ipp.viewport_bounds.center(), ptz);
|
||||
let transformed_delta = document_to_viewport.inverse().transform_vector2(delta);
|
||||
|
||||
ptz.pan += transformed_delta;
|
||||
responses.add(BroadcastEvent::CanvasTransformed);
|
||||
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
||||
responses.add(DocumentMessage::PTZUpdate);
|
||||
}
|
||||
NavigationMessage::CanvasPanByViewportFraction { delta } => {
|
||||
let transformed_delta = if !graph_view_overlay_open {
|
||||
metadata.document_to_viewport.inverse().transform_vector2(delta * ipp.viewport_bounds.size())
|
||||
} else {
|
||||
node_graph_to_viewport.inverse().transform_vector2(delta * ipp.viewport_bounds.size())
|
||||
let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
||||
log::error!("Could not get node graph PTZ in CanvasPanByViewportFraction");
|
||||
return;
|
||||
};
|
||||
let document_to_viewport = self.calculate_offset_transform(ipp.viewport_bounds.center(), ptz);
|
||||
let transformed_delta = document_to_viewport.inverse().transform_vector2(delta * ipp.viewport_bounds.size());
|
||||
|
||||
ptz.pan += transformed_delta;
|
||||
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
||||
responses.add(DocumentMessage::PTZUpdate);
|
||||
}
|
||||
NavigationMessage::CanvasPanMouseWheel { use_y_as_x } => {
|
||||
let delta = match use_y_as_x {
|
||||
@@ -144,16 +174,28 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||
responses.add(NavigationMessage::CanvasPan { delta });
|
||||
}
|
||||
NavigationMessage::CanvasTiltResetAndZoomTo100Percent => {
|
||||
let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
||||
log::error!("Could not get mutable PTZ in CanvasTiltResetAndZoomTo100Percent");
|
||||
return;
|
||||
};
|
||||
ptz.tilt = 0.;
|
||||
ptz.set_zoom(1.);
|
||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
||||
responses.add(DocumentMessage::PTZUpdate);
|
||||
}
|
||||
NavigationMessage::CanvasTiltSet { angle_radians } => {
|
||||
let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
||||
log::error!("Could not get mutable PTZ in CanvasTiltSet");
|
||||
return;
|
||||
};
|
||||
ptz.tilt = angle_radians;
|
||||
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
||||
responses.add(DocumentMessage::PTZUpdate);
|
||||
}
|
||||
NavigationMessage::CanvasZoomDecrease { center_on_mouse } => {
|
||||
let Some(ptz) = get_ptz(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().rev().find(|scale| **scale < ptz.zoom()).unwrap_or(&ptz.zoom());
|
||||
if center_on_mouse {
|
||||
responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom(), ipp.mouse.position));
|
||||
@@ -161,6 +203,10 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||
responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: new_scale });
|
||||
}
|
||||
NavigationMessage::CanvasZoomIncrease { center_on_mouse } => {
|
||||
let Some(ptz) = get_ptz(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().find(|scale| **scale > ptz.zoom()).unwrap_or(&ptz.zoom());
|
||||
if center_on_mouse {
|
||||
responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom(), ipp.mouse.position));
|
||||
@@ -175,10 +221,14 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||
}
|
||||
let document_bounds = if !graph_view_overlay_open {
|
||||
// TODO: Cache this in node graph coordinates and apply the transform to the rectangle to get viewport coordinates
|
||||
metadata.document_bounds_viewport_space()
|
||||
network_interface.document_metadata().document_bounds_viewport_space()
|
||||
} else {
|
||||
node_graph_handler.graph_bounds_viewport_space(*node_graph_to_viewport)
|
||||
network_interface.graph_bounds_viewport_space(breadcrumb_network_path)
|
||||
};
|
||||
let Some(ptz) = get_ptz(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
||||
return;
|
||||
};
|
||||
|
||||
zoom_factor *= Self::clamp_zoom(ptz.zoom() * zoom_factor, document_bounds, old_zoom, ipp);
|
||||
|
||||
responses.add(self.center_zoom(ipp.viewport_bounds.size(), zoom_factor, ipp.mouse.position));
|
||||
@@ -189,17 +239,25 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||
NavigationMessage::CanvasZoomSet { zoom_factor } => {
|
||||
let document_bounds = if !graph_view_overlay_open {
|
||||
// TODO: Cache this in node graph coordinates and apply the transform to the rectangle to get viewport coordinates
|
||||
metadata.document_bounds_viewport_space()
|
||||
network_interface.document_metadata().document_bounds_viewport_space()
|
||||
} else {
|
||||
node_graph_handler.graph_bounds_viewport_space(*node_graph_to_viewport)
|
||||
network_interface.graph_bounds_viewport_space(breadcrumb_network_path)
|
||||
};
|
||||
let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
||||
log::error!("Could not get mutable PTZ in CanvasZoomSet");
|
||||
return;
|
||||
};
|
||||
let zoom = zoom_factor.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX);
|
||||
let zoom = zoom * Self::clamp_zoom(zoom, document_bounds, old_zoom, ipp);
|
||||
ptz.set_zoom(zoom);
|
||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
||||
responses.add(DocumentMessage::PTZUpdate);
|
||||
}
|
||||
NavigationMessage::EndCanvasPTZ { abort_transform } => {
|
||||
let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
||||
log::error!("Could not get mutable PTZ in EndCanvasPTZ");
|
||||
return;
|
||||
};
|
||||
// If an abort was requested, reset the active PTZ value to its original state
|
||||
if abort_transform && self.navigation_operation != NavigationOperation::None {
|
||||
match self.navigation_operation {
|
||||
@@ -215,7 +273,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||
}
|
||||
}
|
||||
|
||||
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
||||
responses.add(DocumentMessage::PTZUpdate);
|
||||
}
|
||||
|
||||
// Final chance to apply snapping if the key was pressed during this final frame
|
||||
@@ -248,18 +306,20 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||
return;
|
||||
}
|
||||
|
||||
let transform = (if graph_view_overlay_open { *node_graph_to_viewport } else { metadata.document_to_viewport }).inverse();
|
||||
let (v1, v2) = (transform.transform_point2(DVec2::ZERO), transform.transform_point2(ipp.viewport_bounds.size()));
|
||||
let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
||||
log::error!("Could not get node graph PTZ in CanvasPanByViewportFraction");
|
||||
return;
|
||||
};
|
||||
let document_to_viewport = self.calculate_offset_transform(ipp.viewport_bounds.center(), ptz);
|
||||
|
||||
let v1 = document_to_viewport.inverse().transform_point2(DVec2::ZERO);
|
||||
let v2 = document_to_viewport.inverse().transform_point2(ipp.viewport_bounds.size());
|
||||
|
||||
let center = ((v2 + v1) - (pos2 + pos1)) / 2.;
|
||||
let size = (v2 - v1) / diagonal;
|
||||
let new_scale = size.min_element();
|
||||
|
||||
let viewport_change = if !graph_view_overlay_open {
|
||||
metadata.document_to_viewport.transform_vector2(center)
|
||||
} else {
|
||||
node_graph_to_viewport.transform_vector2(center)
|
||||
};
|
||||
let viewport_change = document_to_viewport.transform_vector2(center);
|
||||
|
||||
// Only change the pan if the change will be visible in the viewport
|
||||
if viewport_change.x.abs() > 0.5 || viewport_change.y.abs() > 0.5 {
|
||||
@@ -275,17 +335,17 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||
}
|
||||
|
||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
||||
responses.add(DocumentMessage::PTZUpdate);
|
||||
}
|
||||
NavigationMessage::FitViewportToSelection => {
|
||||
if let Some(bounds) = selection_bounds {
|
||||
let transform = if !graph_view_overlay_open {
|
||||
metadata.document_to_viewport.inverse()
|
||||
} else {
|
||||
node_graph_to_viewport.inverse()
|
||||
let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
||||
log::error!("Could not get node graph PTZ in FitViewportToSelection");
|
||||
return;
|
||||
};
|
||||
let document_to_viewport = self.calculate_offset_transform(ipp.viewport_bounds.center(), ptz);
|
||||
responses.add(NavigationMessage::FitViewportToBounds {
|
||||
bounds: [transform.transform_point2(bounds[0]), transform.transform_point2(bounds[1])],
|
||||
bounds: [document_to_viewport.inverse().transform_point2(bounds[0]), document_to_viewport.inverse().transform_point2(bounds[1])],
|
||||
prevent_zoom_past_100: false,
|
||||
})
|
||||
}
|
||||
@@ -310,6 +370,10 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||
|
||||
tilt_raw_not_snapped + angle
|
||||
};
|
||||
let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
||||
log::error!("Could not get mutable PTZ in Tilt");
|
||||
return;
|
||||
};
|
||||
ptz.tilt = self.snapped_tilt(tilt_raw_not_snapped);
|
||||
|
||||
let snap = ipp.keyboard.get(snap as usize);
|
||||
@@ -334,13 +398,17 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||
|
||||
let document_bounds = if !graph_view_overlay_open {
|
||||
// TODO: Cache this in node graph coordinates and apply the transform to the rectangle to get viewport coordinates
|
||||
metadata.document_bounds_viewport_space()
|
||||
network_interface.document_metadata().document_bounds_viewport_space()
|
||||
} else {
|
||||
node_graph_handler.graph_bounds_viewport_space(*node_graph_to_viewport)
|
||||
network_interface.graph_bounds_viewport_space(breadcrumb_network_path)
|
||||
};
|
||||
|
||||
updated_zoom * Self::clamp_zoom(updated_zoom, document_bounds, old_zoom, ipp)
|
||||
};
|
||||
let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
||||
log::error!("Could not get mutable PTZ in Zoom");
|
||||
return;
|
||||
};
|
||||
ptz.set_zoom(self.snapped_zoom(zoom_raw_not_snapped));
|
||||
|
||||
let snap = ipp.keyboard.get(snap as usize);
|
||||
@@ -413,7 +481,11 @@ impl NavigationMessageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calculate_offset_transform(&self, viewport_center: DVec2, pan: DVec2, tilt: f64, zoom: f64) -> DAffine2 {
|
||||
pub fn calculate_offset_transform(&self, viewport_center: DVec2, ptz: &PTZ) -> DAffine2 {
|
||||
let pan = ptz.pan;
|
||||
let tilt = ptz.tilt;
|
||||
let zoom = ptz.zoom();
|
||||
|
||||
let scaled_center = viewport_center / self.snapped_zoom(zoom);
|
||||
|
||||
// Try to avoid fractional coordinates to reduce anti aliasing.
|
||||
@@ -428,11 +500,6 @@ impl NavigationMessageHandler {
|
||||
scale_transform * offset_transform * angle_transform * translation_transform
|
||||
}
|
||||
|
||||
fn create_document_transform(&self, viewport_center: DVec2, ptz: &PTZ, responses: &mut VecDeque<Message>) {
|
||||
let transform = self.calculate_offset_transform(viewport_center, ptz.pan, ptz.tilt, ptz.zoom());
|
||||
responses.add(DocumentMessage::UpdateDocumentTransform { transform });
|
||||
}
|
||||
|
||||
pub fn center_zoom(&self, viewport_bounds: DVec2, zoom_factor: f64, mouse: DVec2) -> Message {
|
||||
let new_viewport_bounds = viewport_bounds / zoom_factor;
|
||||
let delta_size = viewport_bounds - new_viewport_bounds;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,31 +1,34 @@
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::Key;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate, OutputConnector};
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{DocumentNode, NodeId, NodeInput};
|
||||
use graph_craft::document::{NodeId, NodeInput};
|
||||
use graph_craft::proto::GraphErrors;
|
||||
use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypes;
|
||||
|
||||
#[impl_message(Message, DocumentMessage, NodeGraph)]
|
||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum NodeGraphMessage {
|
||||
// Messages
|
||||
AddNodes {
|
||||
nodes: Vec<(NodeId, NodeTemplate)>,
|
||||
new_ids: HashMap<NodeId, NodeId>,
|
||||
},
|
||||
Init,
|
||||
SelectedNodesUpdated,
|
||||
ConnectNodesByWire {
|
||||
output_node: NodeId,
|
||||
output_node_connector_index: usize,
|
||||
input_node: NodeId,
|
||||
input_node_connector_index: usize,
|
||||
},
|
||||
Copy,
|
||||
CloseCreateNodeMenu,
|
||||
CreateNode {
|
||||
CreateNodeFromContextMenu {
|
||||
node_id: Option<NodeId>,
|
||||
node_type: String,
|
||||
x: i32,
|
||||
y: i32,
|
||||
},
|
||||
CreateWire {
|
||||
output_connector: OutputConnector,
|
||||
input_connector: InputConnector,
|
||||
},
|
||||
Cut,
|
||||
DeleteNodes {
|
||||
node_ids: Vec<NodeId>,
|
||||
@@ -35,17 +38,10 @@ pub enum NodeGraphMessage {
|
||||
reconnect: bool,
|
||||
},
|
||||
DisconnectInput {
|
||||
node_id: NodeId,
|
||||
input_index: usize,
|
||||
input_connector: InputConnector,
|
||||
},
|
||||
EnterNestedNetwork,
|
||||
DuplicateSelectedNodes,
|
||||
EnforceLayerHasNoMultiParams {
|
||||
node_id: NodeId,
|
||||
},
|
||||
ExitNestedNetwork {
|
||||
steps_back: usize,
|
||||
},
|
||||
ExposeInput {
|
||||
node_id: NodeId,
|
||||
input_index: usize,
|
||||
@@ -53,21 +49,17 @@ pub enum NodeGraphMessage {
|
||||
},
|
||||
InsertNode {
|
||||
node_id: NodeId,
|
||||
document_node: DocumentNode,
|
||||
node_template: NodeTemplate,
|
||||
},
|
||||
InsertNodeBetween {
|
||||
post_node_id: NodeId,
|
||||
post_node_input_index: usize,
|
||||
insert_node_output_index: usize,
|
||||
insert_node_id: NodeId,
|
||||
node_id: NodeId,
|
||||
input_connector: InputConnector,
|
||||
insert_node_input_index: usize,
|
||||
pre_node_output_index: usize,
|
||||
pre_node_id: NodeId,
|
||||
},
|
||||
MoveSelectedNodes {
|
||||
displacement_x: i32,
|
||||
displacement_y: i32,
|
||||
move_upstream: bool,
|
||||
MoveLayerToStack {
|
||||
layer: LayerNodeIdentifier,
|
||||
parent: LayerNodeIdentifier,
|
||||
insert_index: usize,
|
||||
},
|
||||
PasteNodes {
|
||||
serialized_nodes: String,
|
||||
@@ -87,6 +79,7 @@ pub enum NodeGraphMessage {
|
||||
},
|
||||
PrintSelectedNodeCoordinates,
|
||||
RunDocumentGraph,
|
||||
ForceRunDocumentGraph,
|
||||
SelectedNodesAdd {
|
||||
nodes: Vec<NodeId>,
|
||||
},
|
||||
@@ -96,48 +89,35 @@ pub enum NodeGraphMessage {
|
||||
SelectedNodesSet {
|
||||
nodes: Vec<NodeId>,
|
||||
},
|
||||
SendClickTargets,
|
||||
EndSendClickTargets,
|
||||
SendGraph,
|
||||
SetInputValue {
|
||||
node_id: NodeId,
|
||||
input_index: usize,
|
||||
value: TaggedValue,
|
||||
},
|
||||
SetNodeInput {
|
||||
node_id: NodeId,
|
||||
input_index: usize,
|
||||
SetInput {
|
||||
input_connector: InputConnector,
|
||||
input: NodeInput,
|
||||
},
|
||||
SetQualifiedInputValue {
|
||||
SetDisplayName {
|
||||
node_id: NodeId,
|
||||
input_index: usize,
|
||||
value: TaggedValue,
|
||||
alias: String,
|
||||
},
|
||||
/// Move all the downstream nodes to the right in the graph to allow space for a newly inserted node
|
||||
ShiftNode {
|
||||
SetDisplayNameImpl {
|
||||
node_id: NodeId,
|
||||
},
|
||||
SetVisibility {
|
||||
node_id: NodeId,
|
||||
visible: bool,
|
||||
},
|
||||
SetLocked {
|
||||
node_id: NodeId,
|
||||
locked: bool,
|
||||
},
|
||||
SetName {
|
||||
node_id: NodeId,
|
||||
name: String,
|
||||
},
|
||||
SetNameImpl {
|
||||
node_id: NodeId,
|
||||
name: String,
|
||||
alias: String,
|
||||
},
|
||||
SetToNodeOrLayer {
|
||||
node_id: NodeId,
|
||||
is_layer: bool,
|
||||
},
|
||||
StartPreviewingWithoutRestore {
|
||||
node_id: NodeId,
|
||||
ShiftNodes {
|
||||
node_ids: Vec<NodeId>,
|
||||
displacement_x: i32,
|
||||
displacement_y: i32,
|
||||
move_upstream: bool,
|
||||
},
|
||||
TogglePreview {
|
||||
node_id: NodeId,
|
||||
@@ -146,10 +126,28 @@ pub enum NodeGraphMessage {
|
||||
node_id: NodeId,
|
||||
},
|
||||
ToggleSelectedAsLayersOrNodes,
|
||||
ToggleSelectedLocked,
|
||||
ToggleLocked {
|
||||
node_id: NodeId,
|
||||
},
|
||||
SetLocked {
|
||||
node_id: NodeId,
|
||||
locked: bool,
|
||||
},
|
||||
ToggleSelectedVisibility,
|
||||
ToggleVisibility {
|
||||
node_id: NodeId,
|
||||
},
|
||||
SetVisibility {
|
||||
node_id: NodeId,
|
||||
visible: bool,
|
||||
},
|
||||
SetLockedOrVisibilitySideEffects {
|
||||
node_ids: Vec<NodeId>,
|
||||
},
|
||||
UpdateEdges,
|
||||
UpdateBoxSelection,
|
||||
UpdateLayerPanel,
|
||||
UpdateNewNodeGraph,
|
||||
UpdateTypes {
|
||||
#[serde(skip)]
|
||||
@@ -157,4 +155,7 @@ pub enum NodeGraphMessage {
|
||||
#[serde(skip)]
|
||||
node_graph_errors: GraphErrors,
|
||||
},
|
||||
UpdateActionButtons,
|
||||
UpdateInSelectedNetwork,
|
||||
SendSelectedNodes,
|
||||
}
|
||||
|
||||
+920
-1880
File diff suppressed because it is too large
Load Diff
@@ -73,11 +73,7 @@ fn add_blank_assist(widgets: &mut Vec<WidgetHolder>) {
|
||||
|
||||
fn start_widgets(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, data_type: FrontendGraphDataType, blank_assist: bool) -> Vec<WidgetHolder> {
|
||||
let Some(input) = document_node.inputs.get(index) else {
|
||||
log::warn!(
|
||||
"A widget named '{name}' for node {} (alias '{}') failed to be built because its node's input index {index} is invalid.",
|
||||
document_node.name,
|
||||
document_node.alias
|
||||
);
|
||||
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||
return vec![];
|
||||
};
|
||||
let mut widgets = vec![expose_widget(node_id, index, data_type, input.is_exposed()), TextLabel::new(name).widget_holder()];
|
||||
@@ -1632,9 +1628,17 @@ pub fn text_properties(document_node: &DocumentNode, node_id: NodeId, _context:
|
||||
}
|
||||
|
||||
pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
let imaginate_node = [context.nested_path, &[node_id]].concat();
|
||||
let imaginate_node = [context.selection_network_path, &[node_id]].concat();
|
||||
|
||||
let resolve_input = |name: &str| IMAGINATE_NODE.inputs.iter().position(|input| input.name == name).unwrap_or_else(|| panic!("Input {name} not found"));
|
||||
let resolve_input = |name: &str| {
|
||||
IMAGINATE_NODE
|
||||
.default_node_template()
|
||||
.persistent_node_metadata
|
||||
.input_names
|
||||
.iter()
|
||||
.position(|input| input == name)
|
||||
.unwrap_or_else(|| panic!("Input {name} not found"))
|
||||
};
|
||||
let seed_index = resolve_input("Seed");
|
||||
let resolution_index = resolve_input("Resolution");
|
||||
let samples_index = resolve_input("Samples");
|
||||
@@ -1830,7 +1834,7 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
||||
let image_size = context
|
||||
.executor
|
||||
.introspect_node_in_network(
|
||||
context.document_network,
|
||||
context.network_interface.network(&[]).unwrap(),
|
||||
&imaginate_node,
|
||||
|network| {
|
||||
network
|
||||
@@ -2093,12 +2097,16 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
||||
layout
|
||||
}
|
||||
|
||||
fn unknown_node_properties(document_node: &DocumentNode) -> Vec<LayoutGroup> {
|
||||
string_properties(format!("Node '{}' cannot be found in library", document_node.name))
|
||||
fn unknown_node_properties(reference: &String) -> Vec<LayoutGroup> {
|
||||
string_properties(format!("Node '{}' cannot be found in library", reference))
|
||||
}
|
||||
|
||||
pub fn node_no_properties(document_node: &DocumentNode, _node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
string_properties(if document_node.is_layer { "Layer has no properties" } else { "Node has no properties" })
|
||||
pub fn node_no_properties(_document_node: &DocumentNode, node_id: NodeId, context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
string_properties(if context.network_interface.is_layer(&node_id, context.selection_network_path) {
|
||||
"Layer has no properties"
|
||||
} else {
|
||||
"Node has no properties"
|
||||
})
|
||||
}
|
||||
|
||||
pub fn index_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
@@ -2108,13 +2116,18 @@ pub fn index_properties(document_node: &DocumentNode, node_id: NodeId, _context:
|
||||
}
|
||||
|
||||
pub fn generate_node_properties(document_node: &DocumentNode, node_id: NodeId, context: &mut NodePropertiesContext) -> LayoutGroup {
|
||||
let name = document_node.name.clone();
|
||||
let layout = match super::document_node_types::resolve_document_node_type(&name) {
|
||||
Some(document_node_type) => (document_node_type.properties)(document_node, node_id, context),
|
||||
None => unknown_node_properties(document_node),
|
||||
let reference = context.network_interface.reference(&node_id, context.selection_network_path).clone();
|
||||
let layout = if let Some(ref reference) = reference {
|
||||
match super::document_node_types::resolve_document_node_type(reference) {
|
||||
Some(document_node_type) => (document_node_type.properties)(document_node, node_id, context),
|
||||
None => unknown_node_properties(reference),
|
||||
}
|
||||
} else {
|
||||
node_no_properties(document_node, node_id, context)
|
||||
};
|
||||
|
||||
LayoutGroup::Section {
|
||||
name,
|
||||
name: reference.unwrap_or_default(),
|
||||
visible: document_node.visible,
|
||||
id: node_id.0,
|
||||
layout,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::NodeId;
|
||||
use graphene_core::Type;
|
||||
use graphene_std::renderer::ClickTarget;
|
||||
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, OutputConnector};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
pub enum FrontendGraphDataType {
|
||||
@@ -43,7 +44,8 @@ pub struct FrontendGraphInput {
|
||||
pub name: String,
|
||||
#[serde(rename = "resolvedType")]
|
||||
pub resolved_type: Option<String>,
|
||||
pub connected: Option<NodeId>,
|
||||
#[serde(rename = "connectedTo")]
|
||||
pub connected_to: Option<OutputConnector>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
@@ -53,9 +55,8 @@ pub struct FrontendGraphOutput {
|
||||
pub name: String,
|
||||
#[serde(rename = "resolvedType")]
|
||||
pub resolved_type: Option<String>,
|
||||
pub connected: Vec<NodeId>,
|
||||
#[serde(rename = "connectedIndex")]
|
||||
pub connected_index: Vec<usize>,
|
||||
#[serde(rename = "connectedTo")]
|
||||
pub connected_to: Vec<InputConnector>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
@@ -65,8 +66,9 @@ pub struct FrontendNode {
|
||||
pub is_layer: bool,
|
||||
#[serde(rename = "canBeLayer")]
|
||||
pub can_be_layer: bool,
|
||||
pub alias: String,
|
||||
pub name: String,
|
||||
pub reference: Option<String>,
|
||||
#[serde(rename = "displayName")]
|
||||
pub display_name: String,
|
||||
#[serde(rename = "primaryInput")]
|
||||
pub primary_input: Option<FrontendGraphInput>,
|
||||
#[serde(rename = "exposedInputs")]
|
||||
@@ -87,13 +89,9 @@ pub struct FrontendNode {
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
pub struct FrontendNodeWire {
|
||||
#[serde(rename = "wireStart")]
|
||||
pub wire_start: NodeId,
|
||||
#[serde(rename = "wireStartOutputIndex")]
|
||||
pub wire_start_output_index: usize,
|
||||
pub wire_start: OutputConnector,
|
||||
#[serde(rename = "wireEnd")]
|
||||
pub wire_end: NodeId,
|
||||
#[serde(rename = "wireEndInputIndex")]
|
||||
pub wire_end_input_index: usize,
|
||||
pub wire_end: InputConnector,
|
||||
pub dashed: bool,
|
||||
}
|
||||
|
||||
@@ -168,16 +166,16 @@ pub struct ContextMenuInformation {
|
||||
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>,
|
||||
#[derive(Clone, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
pub struct FrontendClickTargets {
|
||||
#[serde(rename = "nodeClickTargets")]
|
||||
pub node_click_targets: Vec<String>,
|
||||
#[serde(rename = "layerClickTargets")]
|
||||
pub layer_click_targets: Vec<String>,
|
||||
#[serde(rename = "portClickTargets")]
|
||||
pub port_click_targets: Vec<String>,
|
||||
#[serde(rename = "visibilityClickTargets")]
|
||||
pub visibility_click_targets: Vec<String>,
|
||||
#[serde(rename = "allNodesBoundingBox")]
|
||||
pub all_nodes_bounding_box: String,
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ fn grid_overlay_rectangular(document: &DocumentMessageHandler, overlay_context:
|
||||
let Some(spacing) = GridSnapping::compute_rectangle_spacing(spacing, &document.document_ptz) else {
|
||||
return;
|
||||
};
|
||||
let document_to_viewport = document.metadata().document_to_viewport;
|
||||
let document_to_viewport = document.navigation_handler.calculate_offset_transform(overlay_context.size / 2., &document.document_ptz);
|
||||
|
||||
let bounds = document_to_viewport.inverse() * Quad::from_box([DVec2::ZERO, overlay_context.size]);
|
||||
|
||||
for primary in 0..2 {
|
||||
@@ -57,7 +58,8 @@ fn grid_overlay_rectangular_dot(document: &DocumentMessageHandler, overlay_conte
|
||||
let Some(spacing) = GridSnapping::compute_rectangle_spacing(spacing, &document.document_ptz) else {
|
||||
return;
|
||||
};
|
||||
let document_to_viewport = document.metadata().document_to_viewport;
|
||||
let document_to_viewport = document.navigation_handler.calculate_offset_transform(overlay_context.size / 2., &document.document_ptz);
|
||||
|
||||
let bounds = document_to_viewport.inverse() * Quad::from_box([DVec2::ZERO, overlay_context.size]);
|
||||
|
||||
let min = bounds.0.iter().map(|corner| corner.y).min_by(|a, b| a.partial_cmp(b).unwrap()).unwrap_or_default();
|
||||
@@ -92,7 +94,8 @@ fn grid_overlay_isometric(document: &DocumentMessageHandler, overlay_context: &m
|
||||
let grid_color = document.snapping_state.grid.grid_color;
|
||||
let cmp = |a: &f64, b: &f64| a.partial_cmp(b).unwrap();
|
||||
let origin = document.snapping_state.grid.origin;
|
||||
let document_to_viewport = document.metadata().document_to_viewport;
|
||||
let document_to_viewport = document.navigation_handler.calculate_offset_transform(overlay_context.size / 2., &document.document_ptz);
|
||||
|
||||
let bounds = document_to_viewport.inverse() * Quad::from_box([DVec2::ZERO, overlay_context.size]);
|
||||
let tan_a = angle_a.to_radians().tan();
|
||||
let tan_b = angle_b.to_radians().tan();
|
||||
@@ -142,7 +145,8 @@ fn grid_overlay_isometric_dot(document: &DocumentMessageHandler, overlay_context
|
||||
let grid_color = document.snapping_state.grid.grid_color;
|
||||
let cmp = |a: &f64, b: &f64| a.partial_cmp(b).unwrap();
|
||||
let origin = document.snapping_state.grid.origin;
|
||||
let document_to_viewport = document.metadata().document_to_viewport;
|
||||
let document_to_viewport = document.navigation_handler.calculate_offset_transform(overlay_context.size / 2., &document.document_ptz);
|
||||
|
||||
let bounds = document_to_viewport.inverse() * Quad::from_box([DVec2::ZERO, overlay_context.size]);
|
||||
let tan_a = angle_a.to_radians().tan();
|
||||
let tan_b = angle_b.to_radians().tan();
|
||||
|
||||
@@ -24,10 +24,11 @@ pub fn overlay_canvas_context() -> web_sys::CanvasRenderingContext2d {
|
||||
}
|
||||
|
||||
pub fn path_overlays(document: &DocumentMessageHandler, shape_editor: &mut ShapeState, overlay_context: &mut OverlayContext) {
|
||||
for layer in document.selected_nodes.selected_layers(document.metadata()) {
|
||||
let Some(vector_data) = document.metadata.compute_modified_vector(layer, &document.network) else {
|
||||
for layer in document.network_interface.selected_nodes(&[]).unwrap().selected_layers(document.metadata()) {
|
||||
let Some(vector_data) = document.metadata().compute_modified_vector(layer, &document.network_interface) else {
|
||||
continue;
|
||||
};
|
||||
//let document_to_viewport = document.navigation_handler.calculate_offset_transform(overlay_context.size / 2., &document.document_ptz);
|
||||
let transform = document.metadata().transform_to_viewport(layer);
|
||||
let selected = shape_editor.selected_shape_state.get(&layer);
|
||||
let is_selected = |selected: Option<&SelectedLayerState>, point: ManipulatorPointId| selected.is_some_and(|selected| selected.is_selected(point));
|
||||
@@ -62,10 +63,11 @@ pub fn path_overlays(document: &DocumentMessageHandler, shape_editor: &mut Shape
|
||||
}
|
||||
|
||||
pub fn path_endpoint_overlays(document: &DocumentMessageHandler, shape_editor: &mut ShapeState, overlay_context: &mut OverlayContext) {
|
||||
for layer in document.selected_nodes.selected_layers(document.metadata()) {
|
||||
let Some(vector_data) = document.metadata.compute_modified_vector(layer, &document.network) else {
|
||||
for layer in document.network_interface.selected_nodes(&[]).unwrap().selected_layers(document.metadata()) {
|
||||
let Some(vector_data) = document.metadata().compute_modified_vector(layer, &document.network_interface) else {
|
||||
continue;
|
||||
};
|
||||
//let document_to_viewport = document.navigation_handler.calculate_offset_transform(overlay_context.size / 2., &document.document_ptz);
|
||||
let transform = document.metadata().transform_to_viewport(layer);
|
||||
let selected = shape_editor.selected_shape_state.get(&layer);
|
||||
let is_selected = |selected: Option<&SelectedLayerState>, point: ManipulatorPointId| selected.is_some_and(|selected| selected.is_selected(point));
|
||||
|
||||
+6
-9
@@ -10,12 +10,10 @@ pub struct PropertiesPanelMessageHandler {}
|
||||
impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPanelMessageHandlerData<'a>)> for PropertiesPanelMessageHandler {
|
||||
fn process_message(&mut self, message: PropertiesPanelMessage, responses: &mut VecDeque<Message>, (persistent_data, data): (&PersistentData, PropertiesPanelMessageHandlerData)) {
|
||||
let PropertiesPanelMessageHandlerData {
|
||||
node_graph_message_handler,
|
||||
executor,
|
||||
document_network: network,
|
||||
document_metadata: metadata,
|
||||
selected_nodes,
|
||||
network_interface,
|
||||
selection_path,
|
||||
document_name,
|
||||
executor,
|
||||
} = data;
|
||||
|
||||
match message {
|
||||
@@ -33,13 +31,12 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
|
||||
let mut context = NodePropertiesContext {
|
||||
persistent_data,
|
||||
responses,
|
||||
nested_path: &node_graph_message_handler.network,
|
||||
executor,
|
||||
document_network: network,
|
||||
metadata,
|
||||
network_interface,
|
||||
selection_network_path: selection_path,
|
||||
};
|
||||
|
||||
let properties_sections = node_graph_message_handler.collate_properties(&mut context, selected_nodes);
|
||||
let properties_sections = NodeGraphMessageHandler::collate_properties(&mut context);
|
||||
|
||||
let options_bar = vec![LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::DocumentMetadata;
|
||||
use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes;
|
||||
use crate::messages::prelude::NodeGraphMessageHandler;
|
||||
use graph_craft::document::NodeId;
|
||||
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
|
||||
use crate::node_graph_executor::NodeGraphExecutor;
|
||||
|
||||
use graph_craft::document::NodeNetwork;
|
||||
|
||||
pub struct PropertiesPanelMessageHandlerData<'a> {
|
||||
pub network_interface: &'a NodeNetworkInterface,
|
||||
pub selection_path: &'a [NodeId],
|
||||
pub document_name: &'a str,
|
||||
pub document_network: &'a NodeNetwork,
|
||||
pub document_metadata: &'a mut DocumentMetadata,
|
||||
pub selected_nodes: &'a SelectedNodes,
|
||||
pub node_graph_message_handler: &'a NodeGraphMessageHandler,
|
||||
pub executor: &'a mut NodeGraphExecutor,
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use graph_craft::document::DocumentNode;
|
||||
use graph_craft::document::NodeId;
|
||||
use super::network_interface::NodeTemplate;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use graph_craft::document::NodeId;
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, PartialEq, Eq, Debug, specta::Type)]
|
||||
@@ -17,10 +16,9 @@ pub const INTERNAL_CLIPBOARD_COUNT: u8 = Clipboard::_InternalClipboardCount as u
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CopyBufferEntry {
|
||||
pub nodes: HashMap<NodeId, DocumentNode>,
|
||||
pub nodes: Vec<(NodeId, NodeTemplate)>,
|
||||
pub selected: bool,
|
||||
pub visible: bool,
|
||||
pub locked: bool,
|
||||
pub collapsed: bool,
|
||||
pub alias: String,
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use super::nodes::SelectedNodes;
|
||||
use super::network_interface::NodeNetworkInterface;
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::FlowType;
|
||||
use graph_craft::document::{NodeId, NodeNetwork};
|
||||
use graph_craft::document::NodeId;
|
||||
use graphene_core::renderer::ClickTarget;
|
||||
use graphene_core::renderer::Quad;
|
||||
use graphene_core::transform::Footprint;
|
||||
@@ -11,7 +10,7 @@ use graphene_std::vector::PointId;
|
||||
use graphene_std::vector::VectorData;
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::HashMap;
|
||||
use std::num::NonZeroU64;
|
||||
|
||||
// ================
|
||||
@@ -22,14 +21,11 @@ use std::num::NonZeroU64;
|
||||
// TODO: it might be better to have a system that can query the state of the node network on demand.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DocumentMetadata {
|
||||
upstream_transforms: HashMap<NodeId, (Footprint, DAffine2)>,
|
||||
structure: HashMap<LayerNodeIdentifier, NodeRelations>,
|
||||
artboards: HashSet<LayerNodeIdentifier>,
|
||||
folders: HashSet<LayerNodeIdentifier>,
|
||||
hidden: HashSet<NodeId>,
|
||||
locked: HashSet<NodeId>,
|
||||
click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>,
|
||||
vector_modify: HashMap<NodeId, VectorData>,
|
||||
pub upstream_transforms: HashMap<NodeId, (Footprint, DAffine2)>,
|
||||
pub structure: HashMap<LayerNodeIdentifier, NodeRelations>,
|
||||
pub click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>,
|
||||
pub vector_modify: HashMap<NodeId, VectorData>,
|
||||
// TODO: Remove and derive from document_ptz in document message handler
|
||||
/// Transform from document space to viewport space.
|
||||
pub document_to_viewport: DAffine2,
|
||||
}
|
||||
@@ -39,10 +35,6 @@ impl Default for DocumentMetadata {
|
||||
Self {
|
||||
upstream_transforms: HashMap::new(),
|
||||
structure: HashMap::new(),
|
||||
artboards: HashSet::new(),
|
||||
folders: HashSet::new(),
|
||||
hidden: HashSet::new(),
|
||||
locked: HashSet::new(),
|
||||
vector_modify: HashMap::new(),
|
||||
click_targets: HashMap::new(),
|
||||
document_to_viewport: DAffine2::IDENTITY,
|
||||
@@ -67,9 +59,10 @@ impl DocumentMetadata {
|
||||
self.click_targets.get(&layer)
|
||||
}
|
||||
|
||||
/// Get vector data after the modification is appled
|
||||
pub fn compute_modified_vector(&self, layer: LayerNodeIdentifier, network: &NodeNetwork) -> Option<VectorData> {
|
||||
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, network);
|
||||
// TODO: Move into network interface so that it does not have to be passed as an argument
|
||||
/// Get vector data after the modification is applied
|
||||
pub fn compute_modified_vector(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<VectorData> {
|
||||
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, network_interface);
|
||||
|
||||
if let Some(vector_data) = graph_layer.upstream_node_id_from_name("Path").and_then(|node| self.vector_modify.get(&node)) {
|
||||
let mut modified = vector_data.clone();
|
||||
@@ -93,164 +86,7 @@ impl DocumentMetadata {
|
||||
fn get_structure_mut(&mut self, node_identifier: LayerNodeIdentifier) -> &mut NodeRelations {
|
||||
self.structure.entry(node_identifier).or_default()
|
||||
}
|
||||
|
||||
/// Layers excluding ones that are children of other layers in the list.
|
||||
pub fn shallowest_unique_layers(&self, layers: impl Iterator<Item = LayerNodeIdentifier>) -> Vec<Vec<LayerNodeIdentifier>> {
|
||||
let mut sorted_layers = layers
|
||||
.map(|layer| {
|
||||
let mut layer_path = layer.ancestors(self).collect::<Vec<_>>();
|
||||
layer_path.reverse();
|
||||
layer_path
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Sorting here creates groups of similar UUID paths
|
||||
sorted_layers.sort();
|
||||
sorted_layers.dedup_by(|a, b| a.starts_with(b));
|
||||
sorted_layers
|
||||
}
|
||||
|
||||
/// Ancestor that is shared by all layers and that is deepest (more nested). Default may be the root. Skips selected non-folder, non-artboard layers
|
||||
pub fn deepest_common_ancestor(&self, layers: impl Iterator<Item = LayerNodeIdentifier>, include_self: bool) -> Option<LayerNodeIdentifier> {
|
||||
layers
|
||||
.map(|layer| {
|
||||
let mut layer_path = layer.ancestors(self).collect::<Vec<_>>();
|
||||
layer_path.reverse();
|
||||
|
||||
if !include_self || !self.is_artboard(layer) {
|
||||
layer_path.pop();
|
||||
}
|
||||
|
||||
layer_path
|
||||
})
|
||||
.reduce(|mut a, b| {
|
||||
a.truncate(a.iter().zip(b.iter()).position(|(&a, &b)| a != b).unwrap_or_else(|| a.len().min(b.len())));
|
||||
a
|
||||
})
|
||||
.and_then(|layer| layer.last().copied())
|
||||
}
|
||||
|
||||
pub fn active_artboard(&self) -> LayerNodeIdentifier {
|
||||
self.artboards.iter().next().copied().unwrap_or(LayerNodeIdentifier::ROOT_PARENT)
|
||||
}
|
||||
|
||||
pub fn all_artboards(&self) -> &HashSet<LayerNodeIdentifier> {
|
||||
&self.artboards
|
||||
}
|
||||
|
||||
pub fn is_folder(&self, layer: LayerNodeIdentifier) -> bool {
|
||||
self.folders.contains(&layer)
|
||||
}
|
||||
|
||||
pub fn is_artboard(&self, layer: LayerNodeIdentifier) -> bool {
|
||||
self.artboards.contains(&layer)
|
||||
}
|
||||
|
||||
pub fn node_is_visible(&self, layer: NodeId) -> bool {
|
||||
!self.hidden.contains(&layer)
|
||||
}
|
||||
|
||||
pub fn node_is_locked(&self, layer: NodeId) -> bool {
|
||||
self.locked.contains(&layer)
|
||||
}
|
||||
|
||||
/// Folders sorted from most nested to least nested
|
||||
pub fn folders_sorted_by_most_nested(&self, layers: impl Iterator<Item = LayerNodeIdentifier>) -> Vec<LayerNodeIdentifier> {
|
||||
let mut folders: Vec<_> = layers.filter(|layer| self.folders.contains(layer)).collect();
|
||||
folders.sort_by_cached_key(|a| std::cmp::Reverse(a.ancestors(self).count()));
|
||||
folders
|
||||
}
|
||||
}
|
||||
|
||||
// ==============================================
|
||||
// DocumentMetadata: Selected layer modifications
|
||||
// ==============================================
|
||||
|
||||
impl DocumentMetadata {
|
||||
/// Loads the structure of layer nodes from a node graph.
|
||||
pub fn load_structure(&mut self, graph: &NodeNetwork) {
|
||||
self.structure = HashMap::from_iter([(LayerNodeIdentifier::ROOT_PARENT, NodeRelations::default())]);
|
||||
self.artboards = HashSet::new();
|
||||
self.folders = HashSet::new();
|
||||
self.hidden = HashSet::new();
|
||||
self.locked = HashSet::new();
|
||||
|
||||
// Should refer to output node
|
||||
|
||||
let mut awaiting_horizontal_flow = vec![(NodeId(u64::MAX), LayerNodeIdentifier::ROOT_PARENT)];
|
||||
let mut awaiting_primary_flow = vec![];
|
||||
|
||||
while let Some((horizontal_root_node_id, mut parent_layer_node)) = awaiting_horizontal_flow.pop() {
|
||||
let horizontal_flow_iter = graph.upstream_flow_back_from_nodes(vec![horizontal_root_node_id], FlowType::HorizontalFlow);
|
||||
// Skip the horizontal_root_node_id node
|
||||
for (current_node, current_node_id) in horizontal_flow_iter.skip(if horizontal_root_node_id == NodeId(u64::MAX) { 0 } else { 1 }) {
|
||||
if !current_node.visible {
|
||||
self.hidden.insert(current_node_id);
|
||||
}
|
||||
|
||||
if current_node.locked {
|
||||
self.locked.insert(current_node_id);
|
||||
}
|
||||
|
||||
if current_node.is_layer {
|
||||
let current_layer_node = LayerNodeIdentifier::new(current_node_id, graph);
|
||||
if !self.structure.contains_key(¤t_layer_node) {
|
||||
awaiting_primary_flow.push((current_node_id, parent_layer_node));
|
||||
|
||||
parent_layer_node.push_child(self, current_layer_node);
|
||||
parent_layer_node = current_layer_node;
|
||||
|
||||
if is_artboard(current_layer_node, graph) {
|
||||
self.artboards.insert(current_layer_node);
|
||||
}
|
||||
|
||||
if graph.nodes.get(¤t_layer_node.to_node()).map(|node| node.layer_has_child_layers(graph)).unwrap_or_default() {
|
||||
self.folders.insert(current_layer_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while let Some((primary_root_node_id, parent_layer_node)) = awaiting_primary_flow.pop() {
|
||||
let primary_flow_iter = graph.upstream_flow_back_from_nodes(vec![primary_root_node_id], FlowType::PrimaryFlow);
|
||||
// Skip the primary_root_node_id node
|
||||
for (current_node, current_node_id) in primary_flow_iter.skip(1) {
|
||||
if !current_node.visible {
|
||||
self.hidden.insert(current_node_id);
|
||||
}
|
||||
|
||||
if current_node.locked {
|
||||
self.locked.insert(current_node_id);
|
||||
}
|
||||
|
||||
if current_node.is_layer {
|
||||
// Create a new layer for the top of each stack, and add it as a child to the previous parent
|
||||
let current_layer_node = LayerNodeIdentifier::new(current_node_id, graph);
|
||||
if !self.structure.contains_key(¤t_layer_node) {
|
||||
parent_layer_node.push_child(self, current_layer_node);
|
||||
|
||||
// The layer nodes for the horizontal flow is itself
|
||||
awaiting_horizontal_flow.push((current_node_id, current_layer_node));
|
||||
|
||||
if is_artboard(current_layer_node, graph) {
|
||||
self.artboards.insert(current_layer_node);
|
||||
}
|
||||
|
||||
if graph.nodes.get(¤t_layer_node.to_node()).map(|node| node.layer_has_child_layers(graph)).unwrap_or_default() {
|
||||
self.folders.insert(current_layer_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.upstream_transforms.retain(|node, _| graph.nodes.contains_key(node));
|
||||
self.click_targets.retain(|layer, _| self.structure.contains_key(layer));
|
||||
self.vector_modify.retain(|node, _| graph.nodes.contains_key(node));
|
||||
}
|
||||
}
|
||||
|
||||
// ============================
|
||||
// DocumentMetadata: Transforms
|
||||
// ============================
|
||||
@@ -351,23 +187,6 @@ impl DocumentMetadata {
|
||||
self.all_layers().filter_map(|layer| self.bounding_box_viewport(layer)).reduce(Quad::combine_bounds)
|
||||
}
|
||||
|
||||
/// Calculates the document bounds in document space
|
||||
pub fn document_bounds_document_space(&self, include_artboards: bool) -> Option<[DVec2; 2]> {
|
||||
self.all_layers()
|
||||
.filter(|&layer| include_artboards || !self.is_artboard(layer))
|
||||
.filter_map(|layer| self.bounding_box_document(layer))
|
||||
.reduce(Quad::combine_bounds)
|
||||
}
|
||||
|
||||
/// Calculates the selected layer bounds in document space
|
||||
pub fn selected_bounds_document_space(&self, include_artboards: bool, selected_nodes: &SelectedNodes) -> Option<[DVec2; 2]> {
|
||||
selected_nodes
|
||||
.selected_layers(self)
|
||||
.filter(|&layer| include_artboards || !self.is_artboard(layer))
|
||||
.filter_map(|layer| self.bounding_box_document(layer))
|
||||
.reduce(Quad::combine_bounds)
|
||||
}
|
||||
|
||||
pub fn layer_outline(&self, layer: LayerNodeIdentifier) -> impl Iterator<Item = &bezier_rs::Subpath<PointId>> {
|
||||
static EMPTY: Vec<ClickTarget> = Vec::new();
|
||||
let click_targets = self.click_targets.get(&layer).unwrap_or(&EMPTY);
|
||||
@@ -398,7 +217,7 @@ impl Default for LayerNodeIdentifier {
|
||||
}
|
||||
|
||||
impl LayerNodeIdentifier {
|
||||
/// A conceptual node used to represent the UI-only "Export" node
|
||||
/// A conceptual layer used to represent the parent of layers that feed into the export
|
||||
pub const ROOT_PARENT: Self = LayerNodeIdentifier::new_unchecked(NodeId(0));
|
||||
|
||||
/// Construct a [`LayerNodeIdentifier`] without checking if it is a layer node
|
||||
@@ -407,13 +226,13 @@ impl LayerNodeIdentifier {
|
||||
Self(unsafe { NonZeroU64::new_unchecked(node_id.0 + 1) })
|
||||
}
|
||||
|
||||
/// Construct a [`LayerNodeIdentifier`], debug asserting that it is a layer node
|
||||
/// Construct a [`LayerNodeIdentifier`], debug asserting that it is a layer node in the document network
|
||||
#[track_caller]
|
||||
pub fn new(node_id: NodeId, network: &NodeNetwork) -> Self {
|
||||
pub fn new(node_id: NodeId, network_interface: &NodeNetworkInterface) -> Self {
|
||||
debug_assert!(
|
||||
network.nodes.get(&node_id).is_some_and(|node| node.is_layer),
|
||||
network_interface.is_layer(&node_id, &Vec::new()),
|
||||
"Layer identifier constructed from non-layer node {node_id}: {:#?}",
|
||||
network.nodes.get(&node_id)
|
||||
network_interface.network(&[]).unwrap().nodes.get(&node_id)
|
||||
);
|
||||
Self::new_unchecked(node_id)
|
||||
}
|
||||
@@ -421,6 +240,7 @@ impl LayerNodeIdentifier {
|
||||
/// Access the node id of this layer
|
||||
pub fn to_node(self) -> NodeId {
|
||||
let id = NodeId(u64::from(self.0) - 1);
|
||||
|
||||
debug_assert!(id != NodeId(0), "LayerNodeIdentifier::ROOT_PARENT cannot be converted to NodeId");
|
||||
id
|
||||
}
|
||||
@@ -450,7 +270,7 @@ impl LayerNodeIdentifier {
|
||||
metadata.get_relations(self).and_then(|relations| relations.last_child)
|
||||
}
|
||||
|
||||
/// Does the layer have children?
|
||||
/// Does the layer have children? If so, then it is a folder
|
||||
pub fn has_children(self, metadata: &DocumentMetadata) -> bool {
|
||||
self.first_child(metadata).is_some()
|
||||
}
|
||||
@@ -672,7 +492,7 @@ impl<'a> DoubleEndedIterator for DescendantsIter<'a> {
|
||||
// =============
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
struct NodeRelations {
|
||||
pub struct NodeRelations {
|
||||
parent: Option<LayerNodeIdentifier>,
|
||||
previous_sibling: Option<LayerNodeIdentifier>,
|
||||
next_sibling: Option<LayerNodeIdentifier>,
|
||||
@@ -684,14 +504,6 @@ struct NodeRelations {
|
||||
// Helper functions
|
||||
// ================
|
||||
|
||||
pub fn is_artboard(layer: LayerNodeIdentifier, network: &NodeNetwork) -> bool {
|
||||
if layer == LayerNodeIdentifier::ROOT_PARENT {
|
||||
return false;
|
||||
}
|
||||
let Some(node) = network.nodes.get(&layer.to_node()) else { return false };
|
||||
node.is_artboard()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tree() {
|
||||
let mut metadata = DocumentMetadata::default();
|
||||
|
||||
@@ -404,7 +404,7 @@ impl fmt::Display for SnappingOptions {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct PTZ {
|
||||
pub pan: DVec2,
|
||||
|
||||
@@ -2,5 +2,6 @@ pub mod clipboards;
|
||||
pub mod document_metadata;
|
||||
pub mod error;
|
||||
pub mod misc;
|
||||
pub mod network_interface;
|
||||
pub mod nodes;
|
||||
pub mod transformation;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
||||
use super::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
|
||||
use super::network_interface::NodeNetworkInterface;
|
||||
|
||||
use graph_craft::document::{NodeId, NodeNetwork};
|
||||
|
||||
@@ -33,7 +34,6 @@ impl serde::Serialize for JsRawBuffer {
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
|
||||
pub struct LayerPanelEntry {
|
||||
pub id: NodeId,
|
||||
pub name: String,
|
||||
pub alias: String,
|
||||
pub tooltip: String,
|
||||
#[serde(rename = "childrenAllowed")]
|
||||
@@ -50,68 +50,75 @@ pub struct LayerPanelEntry {
|
||||
pub parents_unlocked: bool,
|
||||
#[serde(rename = "parentId")]
|
||||
pub parent_id: Option<NodeId>,
|
||||
pub selected: bool,
|
||||
#[serde(rename = "inSelectedNetwork")]
|
||||
pub in_selected_network: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
|
||||
pub struct SelectedNodes(pub Vec<NodeId>);
|
||||
|
||||
impl SelectedNodes {
|
||||
pub fn layer_visible(&self, layer: LayerNodeIdentifier, metadata: &DocumentMetadata) -> bool {
|
||||
layer.ancestors(metadata).all(|layer| {
|
||||
pub fn layer_visible(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> bool {
|
||||
layer.ancestors(network_interface.document_metadata()).all(|layer| {
|
||||
if layer != LayerNodeIdentifier::ROOT_PARENT {
|
||||
metadata.node_is_visible(layer.to_node())
|
||||
network_interface.is_visible(&layer.to_node(), &[])
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn selected_visible_layers<'a>(&'a self, metadata: &'a DocumentMetadata) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
|
||||
self.selected_layers(metadata).filter(move |&layer| self.layer_visible(layer, metadata))
|
||||
pub fn selected_visible_layers<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
|
||||
self.selected_layers(network_interface.document_metadata())
|
||||
.filter(move |&layer| self.layer_visible(layer, network_interface))
|
||||
}
|
||||
|
||||
pub fn layer_locked(&self, layer: LayerNodeIdentifier, metadata: &DocumentMetadata) -> bool {
|
||||
layer.ancestors(metadata).any(|layer| {
|
||||
pub fn layer_locked(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> bool {
|
||||
layer.ancestors(network_interface.document_metadata()).any(|layer| {
|
||||
if layer != LayerNodeIdentifier::ROOT_PARENT {
|
||||
metadata.node_is_locked(layer.to_node())
|
||||
network_interface.is_locked(&layer.to_node(), &[])
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn selected_unlocked_layers<'a>(&'a self, metadata: &'a DocumentMetadata) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
|
||||
self.selected_layers(metadata).filter(move |&layer| !self.layer_locked(layer, metadata))
|
||||
pub fn selected_unlocked_layers<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
|
||||
self.selected_layers(network_interface.document_metadata())
|
||||
.filter(move |&layer| !self.layer_locked(layer, network_interface))
|
||||
}
|
||||
|
||||
pub fn selected_visible_and_unlocked_layers<'a>(&'a self, metadata: &'a DocumentMetadata) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
|
||||
self.selected_layers(metadata)
|
||||
.filter(move |&layer| self.layer_visible(layer, metadata) && !self.layer_locked(layer, metadata))
|
||||
pub fn selected_visible_and_unlocked_layers<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
|
||||
self.selected_layers(network_interface.document_metadata())
|
||||
.filter(move |&layer| self.layer_visible(layer, network_interface) && !self.layer_locked(layer, network_interface))
|
||||
}
|
||||
|
||||
pub fn selected_layers<'a>(&'a self, metadata: &'a DocumentMetadata) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
|
||||
metadata.all_layers().filter(|layer| self.0.contains(&layer.to_node()))
|
||||
}
|
||||
|
||||
pub fn selected_layers_except_artboards<'a>(&'a self, metadata: &'a DocumentMetadata) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
|
||||
self.selected_layers(metadata).filter(move |&layer| !metadata.is_artboard(layer))
|
||||
pub fn selected_layers_except_artboards<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
|
||||
self.selected_layers(network_interface.document_metadata())
|
||||
.filter(move |&layer| !network_interface.is_artboard(&layer.to_node(), &[]))
|
||||
}
|
||||
|
||||
pub fn selected_layers_contains(&self, layer: LayerNodeIdentifier, metadata: &DocumentMetadata) -> bool {
|
||||
self.selected_layers(metadata).any(|selected| selected == layer)
|
||||
}
|
||||
|
||||
// All selected nodes must be in the same network
|
||||
pub fn selected_nodes<'a>(&'a self, network: &'a NodeNetwork) -> impl Iterator<Item = &NodeId> + '_ {
|
||||
self.0
|
||||
.iter()
|
||||
.filter(|node_id| network.nodes.contains_key(*node_id) || **node_id == network.imports_metadata.0 || **node_id == network.exports_metadata.0)
|
||||
pub fn selected_nodes(&self) -> impl Iterator<Item = &NodeId> + '_ {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
pub fn selected_nodes_ref(&self) -> &Vec<NodeId> {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub fn network_has_selected_nodes(&self, network: &NodeNetwork) -> bool {
|
||||
self.0.iter().any(|node_id| network.nodes.contains_key(node_id))
|
||||
}
|
||||
|
||||
pub fn has_selected_nodes(&self) -> bool {
|
||||
!self.0.is_empty()
|
||||
}
|
||||
@@ -120,38 +127,11 @@ impl SelectedNodes {
|
||||
self.0.retain(f);
|
||||
}
|
||||
|
||||
// TODO: This function is run when a node in the layer panel is currently selected, and a new node is selected in the graph, as well as when a node is currently selected in the graph and a node in the layer panel is selected. These are fundamentally different operations, since different nodes should be selected in each case, but cannot be distinguished. Currently it is not possible to shift+click a node in the node graph while a layer is selected. Instead of set_selected_nodes, add_selected_nodes should be used.
|
||||
pub fn set_selected_nodes(&mut self, new: Vec<NodeId>, document_network: &NodeNetwork, network_path: &[NodeId]) {
|
||||
let Some(network) = document_network.nested_network(network_path) else { return };
|
||||
|
||||
let mut new_nodes = new;
|
||||
|
||||
// If any nodes to add are in the document network, clear selected nodes in the current network
|
||||
if new_nodes.iter().any(|node_to_add| document_network.nodes.contains_key(node_to_add)) {
|
||||
new_nodes.retain(|selected_node| {
|
||||
document_network.nodes.contains_key(selected_node) || document_network.imports_metadata.0 == *selected_node || document_network.exports_metadata.0 == *selected_node
|
||||
});
|
||||
}
|
||||
// If not, then clear any nodes that are not in the current network
|
||||
else {
|
||||
new_nodes.retain(|selected_node| network.nodes.contains_key(selected_node) || network.imports_metadata.0 == *selected_node || network.exports_metadata.0 == *selected_node);
|
||||
}
|
||||
|
||||
self.0 = new_nodes;
|
||||
pub fn set_selected_nodes(&mut self, new: Vec<NodeId>) {
|
||||
self.0 = new;
|
||||
}
|
||||
|
||||
pub fn add_selected_nodes(&mut self, new: Vec<NodeId>, document_network: &NodeNetwork, network_path: &[NodeId]) {
|
||||
let Some(network) = document_network.nested_network(network_path) else { return };
|
||||
|
||||
// If the nodes to add are in the document network, clear selected nodes in the current network
|
||||
if new.iter().any(|node_to_add| document_network.nodes.contains_key(node_to_add)) {
|
||||
self.retain_selected_nodes(|selected_node| {
|
||||
document_network.nodes.contains_key(selected_node) || document_network.imports_metadata.0 == *selected_node || document_network.exports_metadata.0 == *selected_node
|
||||
});
|
||||
} else {
|
||||
self.retain_selected_nodes(|selected_node| network.nodes.contains_key(selected_node) || network.imports_metadata.0 == *selected_node || network.exports_metadata.0 == *selected_node);
|
||||
}
|
||||
|
||||
pub fn add_selected_nodes(&mut self, new: Vec<NodeId>) {
|
||||
self.0.extend(new);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use super::network_interface::NodeNetworkInterface;
|
||||
use crate::consts::{ROTATE_SNAP_ANGLE, SCALE_SNAP_INTERVAL};
|
||||
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
|
||||
@@ -6,7 +7,6 @@ use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
use crate::messages::tool::common_functionality::shape_editor::ShapeState;
|
||||
use crate::messages::tool::utility_types::ToolType;
|
||||
|
||||
use graph_craft::document::NodeNetwork;
|
||||
use graphene_core::renderer::Quad;
|
||||
use graphene_core::vector::ManipulatorPointId;
|
||||
use graphene_core::vector::VectorModificationType;
|
||||
@@ -53,7 +53,9 @@ impl OriginalTransforms {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update<'a>(&mut self, selected: &'a [LayerNodeIdentifier], document_network: &NodeNetwork, document_metadata: &DocumentMetadata, shape_editor: Option<&'a ShapeState>) {
|
||||
pub fn update<'a>(&mut self, selected: &'a [LayerNodeIdentifier], network_interface: &NodeNetworkInterface, shape_editor: Option<&'a ShapeState>) {
|
||||
let document_metadata = network_interface.document_metadata();
|
||||
|
||||
match self {
|
||||
OriginalTransforms::Layer(layer_map) => {
|
||||
layer_map.retain(|layer, _| selected.contains(layer));
|
||||
@@ -73,7 +75,7 @@ impl OriginalTransforms {
|
||||
if path_map.contains_key(&layer) {
|
||||
continue;
|
||||
}
|
||||
let Some(vector_data) = document_metadata.compute_modified_vector(layer, document_network) else {
|
||||
let Some(vector_data) = network_interface.document_metadata().compute_modified_vector(layer, network_interface) else {
|
||||
continue;
|
||||
};
|
||||
let Some(selected_points) = shape_editor.selected_points_in_layer(layer) else {
|
||||
@@ -343,8 +345,7 @@ impl TransformOperation {
|
||||
pub struct Selected<'a> {
|
||||
pub selected: &'a [LayerNodeIdentifier],
|
||||
pub responses: &'a mut VecDeque<Message>,
|
||||
pub document_network: &'a NodeNetwork,
|
||||
pub document_metadata: &'a DocumentMetadata,
|
||||
pub network_interface: &'a NodeNetworkInterface,
|
||||
pub original_transforms: &'a mut OriginalTransforms,
|
||||
pub pivot: &'a mut DVec2,
|
||||
pub shape_editor: Option<&'a ShapeState>,
|
||||
@@ -358,8 +359,7 @@ impl<'a> Selected<'a> {
|
||||
pivot: &'a mut DVec2,
|
||||
selected: &'a [LayerNodeIdentifier],
|
||||
responses: &'a mut VecDeque<Message>,
|
||||
document_network: &'a NodeNetwork,
|
||||
document_metadata: &'a DocumentMetadata,
|
||||
network_interface: &'a NodeNetworkInterface,
|
||||
shape_editor: Option<&'a ShapeState>,
|
||||
tool_type: &'a ToolType,
|
||||
) -> Self {
|
||||
@@ -368,13 +368,12 @@ impl<'a> Selected<'a> {
|
||||
*original_transforms = OriginalTransforms::Layer(HashMap::new());
|
||||
}
|
||||
|
||||
original_transforms.update(selected, document_network, document_metadata, shape_editor);
|
||||
original_transforms.update(selected, network_interface, shape_editor);
|
||||
|
||||
Self {
|
||||
selected,
|
||||
responses,
|
||||
document_network,
|
||||
document_metadata,
|
||||
network_interface,
|
||||
original_transforms,
|
||||
pivot,
|
||||
shape_editor,
|
||||
@@ -386,7 +385,7 @@ impl<'a> Selected<'a> {
|
||||
let xy_summation = self
|
||||
.selected
|
||||
.iter()
|
||||
.map(|&layer| graph_modification_utils::get_viewport_pivot(layer, self.document_network, self.document_metadata))
|
||||
.map(|&layer| graph_modification_utils::get_viewport_pivot(layer, self.network_interface))
|
||||
.reduce(|a, b| a + b)
|
||||
.unwrap_or_default();
|
||||
|
||||
@@ -397,7 +396,7 @@ impl<'a> Selected<'a> {
|
||||
let [min, max] = self
|
||||
.selected
|
||||
.iter()
|
||||
.filter_map(|&layer| self.document_metadata.bounding_box_viewport(layer))
|
||||
.filter_map(|&layer| self.network_interface.document_metadata().bounding_box_viewport(layer))
|
||||
.reduce(Quad::combine_bounds)
|
||||
.unwrap_or_default();
|
||||
(min + max) / 2.
|
||||
@@ -446,14 +445,14 @@ impl<'a> Selected<'a> {
|
||||
pub fn apply_transformation(&mut self, transformation: DAffine2) {
|
||||
if !self.selected.is_empty() {
|
||||
// TODO: Cache the result of `shallowest_unique_layers` to avoid this heavy computation every frame of movement, see https://github.com/GraphiteEditor/Graphite/pull/481
|
||||
for layer_ancestors in self.document_metadata.shallowest_unique_layers(self.selected.iter().copied()) {
|
||||
let layer = *layer_ancestors.last().unwrap();
|
||||
|
||||
for layer in self.network_interface.shallowest_unique_layers(&[]) {
|
||||
match &mut self.original_transforms {
|
||||
OriginalTransforms::Layer(layer_transforms) => Self::transform_layer(self.document_metadata, layer, layer_transforms.get(&layer), transformation, self.responses),
|
||||
OriginalTransforms::Layer(layer_transforms) => {
|
||||
Self::transform_layer(self.network_interface.document_metadata(), layer, layer_transforms.get(&layer), transformation, self.responses)
|
||||
}
|
||||
OriginalTransforms::Path(path_transforms) => {
|
||||
if let Some(initial_points) = path_transforms.get_mut(&layer) {
|
||||
Self::transform_path(self.document_metadata, layer, initial_points, transformation, self.responses)
|
||||
Self::transform_path(self.network_interface.document_metadata(), layer, initial_points, transformation, self.responses)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use super::utility_types::PanelType;
|
||||
use crate::messages::frontend::utility_types::{ExportBounds, FileType};
|
||||
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
|
||||
use crate::messages::prelude::*;
|
||||
@@ -81,12 +82,15 @@ pub enum PortfolioMessage {
|
||||
PasteIntoFolder {
|
||||
clipboard: Clipboard,
|
||||
parent: LayerNodeIdentifier,
|
||||
insert_index: isize,
|
||||
insert_index: usize,
|
||||
},
|
||||
PasteSerializedData {
|
||||
data: String,
|
||||
},
|
||||
PrevDocument,
|
||||
SetActivePanel {
|
||||
panel: PanelType,
|
||||
},
|
||||
SelectDocument {
|
||||
document_id: DocumentId,
|
||||
},
|
||||
@@ -99,6 +103,7 @@ pub enum PortfolioMessage {
|
||||
},
|
||||
SubmitGraphRender {
|
||||
document_id: DocumentId,
|
||||
ignore_hash: bool,
|
||||
},
|
||||
ToggleRulers,
|
||||
UpdateDocumentWidgets,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use super::utility_types::PersistentData;
|
||||
use super::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use super::document::utility_types::network_interface::{self, InputConnector};
|
||||
use super::utility_types::{PanelType, PersistentData};
|
||||
use crate::application::generate_uuid;
|
||||
use crate::consts::DEFAULT_DOCUMENT_NAME;
|
||||
use crate::messages::dialog::simple_dialogs;
|
||||
use crate::messages::frontend::utility_types::FrontendDocumentDetails;
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
|
||||
use crate::messages::portfolio::document::utility_types::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT};
|
||||
use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes;
|
||||
use crate::messages::portfolio::document::DocumentMessageData;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::utility_types::{HintData, HintGroup};
|
||||
@@ -18,6 +18,7 @@ use graphene_core::text::Font;
|
||||
use graphene_std::vector::style::{Fill, FillType, Gradient};
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::vec;
|
||||
|
||||
pub struct PortfolioMessageData<'a> {
|
||||
pub ipp: &'a InputPreprocessorMessageHandler,
|
||||
@@ -29,6 +30,7 @@ pub struct PortfolioMessageHandler {
|
||||
menu_bar_message_handler: MenuBarMessageHandler,
|
||||
pub documents: HashMap<DocumentId, DocumentMessageHandler>,
|
||||
document_ids: Vec<DocumentId>,
|
||||
active_panel: PanelType,
|
||||
pub(crate) active_document_id: Option<DocumentId>,
|
||||
copy_buffer: [Vec<CopyBufferEntry>; INTERNAL_CLIPBOARD_COUNT as usize],
|
||||
pub persistent_data: PersistentData,
|
||||
@@ -181,54 +183,37 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
||||
}
|
||||
PortfolioMessage::Copy { clipboard } => {
|
||||
// We can't use `self.active_document()` because it counts as an immutable borrow of the entirety of `self`
|
||||
let Some(active_document) = self.active_document_id.and_then(|id| self.documents.get(&id)) else {
|
||||
let Some(active_document) = self.active_document_id.and_then(|id| self.documents.get_mut(&id)) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let copy_val = |buffer: &mut Vec<CopyBufferEntry>| {
|
||||
let binding = active_document
|
||||
.metadata()
|
||||
.shallowest_unique_layers(active_document.selected_nodes.selected_layers(active_document.metadata()));
|
||||
|
||||
let get_last_elements: Vec<_> = binding.iter().map(|x| x.last().expect("empty path")).collect();
|
||||
|
||||
let ordered_last_elements: Vec<_> = active_document.metadata.all_layers().filter(|layer| get_last_elements.contains(&layer)).collect();
|
||||
let mut copy_val = |buffer: &mut Vec<CopyBufferEntry>| {
|
||||
let ordered_last_elements = active_document.network_interface.shallowest_unique_layers(&[]);
|
||||
|
||||
for layer in ordered_last_elements {
|
||||
let layer_node_id = layer.to_node();
|
||||
let previous_alias = active_document.network().nodes.get(&layer_node_id).map(|node| node.alias.clone()).unwrap_or_default();
|
||||
|
||||
let mut copy_ids = HashMap::new();
|
||||
copy_ids.insert(layer_node_id, NodeId(0_u64));
|
||||
if let Some(input_node) = active_document
|
||||
.network()
|
||||
.nodes
|
||||
.get(&layer_node_id)
|
||||
.and_then(|node| if node.is_layer { node.inputs.get(1) } else { node.inputs.first() })
|
||||
.and_then(|input| input.as_node())
|
||||
{
|
||||
active_document
|
||||
.network()
|
||||
.upstream_flow_back_from_nodes(vec![input_node], graph_craft::document::FlowType::UpstreamFlow)
|
||||
.enumerate()
|
||||
.for_each(|(index, (_, node_id))| {
|
||||
copy_ids.insert(node_id, NodeId((index + 1) as u64));
|
||||
});
|
||||
};
|
||||
copy_ids.insert(layer_node_id, NodeId(0));
|
||||
|
||||
active_document
|
||||
.network_interface
|
||||
.upstream_flow_back_from_nodes(vec![layer_node_id], &[], network_interface::FlowType::LayerChildrenUpstreamFlow)
|
||||
.enumerate()
|
||||
.for_each(|(index, node_id)| {
|
||||
copy_ids.insert(node_id, NodeId((index + 1) as u64));
|
||||
});
|
||||
|
||||
buffer.push(CopyBufferEntry {
|
||||
nodes: NodeGraphMessageHandler::copy_nodes(
|
||||
active_document.network(),
|
||||
&active_document.node_graph_handler.network,
|
||||
&active_document.node_graph_handler.resolved_types,
|
||||
©_ids,
|
||||
)
|
||||
.collect(),
|
||||
selected: active_document.selected_nodes.selected_layers_contains(layer, active_document.metadata()),
|
||||
visible: active_document.selected_nodes.layer_visible(layer, active_document.metadata()),
|
||||
locked: active_document.selected_nodes.layer_locked(layer, active_document.metadata()),
|
||||
nodes: active_document.network_interface.copy_nodes(©_ids, &[]).collect(),
|
||||
selected: active_document
|
||||
.network_interface
|
||||
.selected_nodes(&[])
|
||||
.unwrap()
|
||||
.selected_layers_contains(layer, active_document.metadata()),
|
||||
visible: active_document.network_interface.selected_nodes(&[]).unwrap().layer_visible(layer, &active_document.network_interface),
|
||||
locked: active_document.network_interface.selected_nodes(&[]).unwrap().layer_locked(layer, &active_document.network_interface),
|
||||
collapsed: false,
|
||||
alias: previous_alias.to_string(),
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -345,7 +330,10 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
||||
}
|
||||
}
|
||||
PortfolioMessage::NewDocumentWithName { name } => {
|
||||
let new_document = DocumentMessageHandler::with_name(name, ipp, responses);
|
||||
let mut new_document = DocumentMessageHandler::default();
|
||||
new_document.name = name;
|
||||
responses.add(DocumentMessage::PTZUpdate);
|
||||
|
||||
let document_id = DocumentId(generate_uuid());
|
||||
if self.active_document().is_some() {
|
||||
responses.add(BroadcastEvent::ToolAbort);
|
||||
@@ -409,36 +397,70 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
||||
// TODO: Eventually remove this (probably starting late 2024)
|
||||
// Upgrade all old nodes to support editable subgraphs introduced in #1750
|
||||
if upgrade_from_before_editable_subgraphs {
|
||||
for node in document.network.nodes.values_mut() {
|
||||
let node_definition = crate::messages::portfolio::document::node_graph::document_node_types::resolve_document_node_type(&node.name).unwrap();
|
||||
let default_definition_node = node_definition.default_document_node();
|
||||
|
||||
node.implementation = default_definition_node.implementation.clone();
|
||||
// This can be used, if uncommented, to upgrade demo artwork with outdated document node internals from their definitions. Delete when it's no longer needed.
|
||||
// Used for upgrading old internal networks for demo artwork nodes. Will reset all node internals for any opened file
|
||||
for node_id in &document
|
||||
.network_interface
|
||||
.network_metadata(&[])
|
||||
.unwrap()
|
||||
.persistent_metadata
|
||||
.node_metadata
|
||||
.keys()
|
||||
.cloned()
|
||||
.collect::<Vec<NodeId>>()
|
||||
{
|
||||
if let Some(reference) = document
|
||||
.network_interface
|
||||
.network_metadata(&[])
|
||||
.unwrap()
|
||||
.persistent_metadata
|
||||
.node_metadata
|
||||
.get(node_id)
|
||||
.and_then(|node| node.persistent_metadata.reference.as_ref())
|
||||
{
|
||||
let node_definition = crate::messages::portfolio::document::node_graph::document_node_types::resolve_document_node_type(reference).unwrap();
|
||||
let default_definition_node = node_definition.default_node_template();
|
||||
document.network_interface.set_implementation(node_id, &[], default_definition_node.document_node.implementation);
|
||||
}
|
||||
}
|
||||
}
|
||||
if document.network.nodes.iter().any(|(node_id, node)| node.name == "Output" && *node_id == NodeId(0)) {
|
||||
ModifyInputsContext::delete_nodes(
|
||||
&mut document.node_graph_handler,
|
||||
&mut document.network,
|
||||
&mut SelectedNodes(vec![]),
|
||||
vec![NodeId(0)],
|
||||
true,
|
||||
responses,
|
||||
Vec::new(),
|
||||
);
|
||||
|
||||
if document
|
||||
.network_interface
|
||||
.network_metadata(&[])
|
||||
.unwrap()
|
||||
.persistent_metadata
|
||||
.node_metadata
|
||||
.iter()
|
||||
.any(|(node_id, node)| node.persistent_metadata.reference.as_ref().is_some_and(|reference| reference == "Output") && *node_id == NodeId(0))
|
||||
{
|
||||
document.network_interface.delete_nodes(vec![NodeId(0)], true, &[]);
|
||||
}
|
||||
|
||||
// TODO: Eventually remove this (probably starting late 2024)
|
||||
// Upgrade Fill nodes to the format change in #1778
|
||||
for node in document.network.nodes.values_mut() {
|
||||
if node.name == "Fill" && node.inputs.len() == 8 {
|
||||
let node_definition = crate::messages::portfolio::document::node_graph::document_node_types::resolve_document_node_type(&node.name).unwrap();
|
||||
let default_definition_node = node_definition.default_document_node();
|
||||
let node_ids = document.network_interface.network(&[]).unwrap().nodes.keys().cloned().collect::<Vec<_>>();
|
||||
for node_id in &node_ids {
|
||||
let Some(node) = document.network_interface.network(&[]).unwrap().nodes.get(node_id) else {
|
||||
log::error!("could not get node in deserialize_document");
|
||||
continue;
|
||||
};
|
||||
let Some(node_metadata) = document.network_interface.network_metadata(&[]).unwrap().persistent_metadata.node_metadata.get(node_id) else {
|
||||
log::error!("could not get node metadata for node {node_id} in deserialize_document");
|
||||
continue;
|
||||
};
|
||||
|
||||
node.implementation = default_definition_node.implementation.clone();
|
||||
let old_inputs = std::mem::replace(&mut node.inputs, default_definition_node.inputs.clone());
|
||||
// Upgrade Fill nodes to the format change in #1778
|
||||
// TODO: Eventually remove this (probably starting late 2024)
|
||||
let Some(ref reference) = node_metadata.persistent_metadata.reference.clone() else {
|
||||
continue;
|
||||
};
|
||||
if reference == "Fill" && node.inputs.len() == 8 {
|
||||
let node_definition = crate::messages::portfolio::document::node_graph::document_node_types::resolve_document_node_type(reference).unwrap();
|
||||
let document_node = node_definition.default_node_template().document_node;
|
||||
document.network_interface.set_implementation(node_id, &[], document_node.implementation.clone());
|
||||
|
||||
node.inputs[0] = old_inputs[0].clone();
|
||||
let old_inputs = document.network_interface.replace_inputs(node_id, document_node.inputs.clone(), &[]);
|
||||
|
||||
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), &[]);
|
||||
|
||||
let Some(fill_type) = old_inputs[1].as_value().cloned() else { continue };
|
||||
let TaggedValue::FillType(fill_type) = fill_type else { continue };
|
||||
@@ -466,19 +488,35 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
||||
transform,
|
||||
}),
|
||||
};
|
||||
node.inputs[1] = NodeInput::value(TaggedValue::Fill(fill.clone()), false);
|
||||
document
|
||||
.network_interface
|
||||
.set_input(&InputConnector::node(*node_id, 1), NodeInput::value(TaggedValue::Fill(fill.clone()), false), &[]);
|
||||
match fill {
|
||||
Fill::None => {
|
||||
node.inputs[2] = NodeInput::value(TaggedValue::OptionalColor(None), false);
|
||||
document
|
||||
.network_interface
|
||||
.set_input(&InputConnector::node(*node_id, 2), NodeInput::value(TaggedValue::OptionalColor(None), false), &[]);
|
||||
}
|
||||
Fill::Solid(color) => {
|
||||
node.inputs[2] = NodeInput::value(TaggedValue::OptionalColor(Some(color)), false);
|
||||
document
|
||||
.network_interface
|
||||
.set_input(&InputConnector::node(*node_id, 2), NodeInput::value(TaggedValue::OptionalColor(Some(color)), false), &[]);
|
||||
}
|
||||
Fill::Gradient(gradient) => {
|
||||
node.inputs[3] = NodeInput::value(TaggedValue::Gradient(gradient), false);
|
||||
document
|
||||
.network_interface
|
||||
.set_input(&InputConnector::node(*node_id, 3), NodeInput::value(TaggedValue::Gradient(gradient), false), &[]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Upgrade artboard name being passed as hidden value input to "To Artboard"
|
||||
if reference == "Artboard" {
|
||||
let label = document.network_interface.display_name(node_id, &[]);
|
||||
document
|
||||
.network_interface
|
||||
.set_input(&InputConnector::node(NodeId(0), 1), NodeInput::value(TaggedValue::String(label), false), &[*node_id]);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Eventually remove this (probably starting late 2024)
|
||||
@@ -505,8 +543,10 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
||||
if self.active_document().is_some() {
|
||||
trace!("Pasting into folder {parent:?} as index: {insert_index}");
|
||||
let nodes = entry.clone().nodes;
|
||||
let new_ids: HashMap<_, _> = nodes.iter().map(|(&id, _)| (id, NodeId(generate_uuid()))).collect();
|
||||
responses.add(GraphOperationMessage::AddNodesAsChild { nodes, new_ids, parent, insert_index });
|
||||
let new_ids: HashMap<_, _> = nodes.iter().map(|(id, _)| (*id, NodeId(generate_uuid()))).collect();
|
||||
let layer = LayerNodeIdentifier::new_unchecked(new_ids[&NodeId(0)]);
|
||||
responses.add(NodeGraphMessage::AddNodes { nodes, new_ids });
|
||||
responses.add(NodeGraphMessage::MoveLayerToStack { layer, parent, insert_index });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -515,6 +555,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
||||
for entry in self.copy_buffer[clipboard as usize].iter().rev() {
|
||||
paste(entry, responses)
|
||||
}
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
PortfolioMessage::PasteSerializedData { data } => {
|
||||
if let Some(document) = self.active_document() {
|
||||
@@ -526,14 +567,12 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
||||
|
||||
for entry in data.into_iter().rev() {
|
||||
document.load_layer_resources(responses);
|
||||
let new_ids: HashMap<_, _> = entry.nodes.iter().map(|(&id, _)| (id, NodeId(generate_uuid()))).collect();
|
||||
responses.add(GraphOperationMessage::AddNodesAsChild {
|
||||
nodes: entry.nodes,
|
||||
new_ids,
|
||||
parent,
|
||||
insert_index: -1,
|
||||
});
|
||||
let new_ids: HashMap<_, _> = entry.nodes.iter().map(|(id, _)| (*id, NodeId(generate_uuid()))).collect();
|
||||
let layer = LayerNodeIdentifier::new_unchecked(new_ids[&NodeId(0)]);
|
||||
responses.add(NodeGraphMessage::AddNodes { nodes: entry.nodes, new_ids });
|
||||
responses.add(NodeGraphMessage::MoveLayerToStack { layer, parent, insert_index: 0 });
|
||||
}
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -546,6 +585,10 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
||||
responses.add(PortfolioMessage::SelectDocument { document_id: prev_id });
|
||||
}
|
||||
}
|
||||
PortfolioMessage::SetActivePanel { panel } => {
|
||||
self.active_panel = panel;
|
||||
responses.add(DocumentMessage::SetActivePanel { active_panel: self.active_panel });
|
||||
}
|
||||
PortfolioMessage::SelectDocument { document_id } => {
|
||||
// Auto-save the document we are leaving
|
||||
let mut node_graph_open = false;
|
||||
@@ -598,11 +641,11 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
||||
});
|
||||
}
|
||||
}
|
||||
PortfolioMessage::SubmitGraphRender { document_id } => {
|
||||
PortfolioMessage::SubmitGraphRender { document_id, ignore_hash } => {
|
||||
let result = self.executor.submit_node_graph_evaluation(
|
||||
self.documents.get_mut(&document_id).expect("Tried to render non-existent document"),
|
||||
ipp.viewport_bounds.size().as_uvec2(),
|
||||
false,
|
||||
ignore_hash,
|
||||
);
|
||||
|
||||
if let Err(description) = result {
|
||||
@@ -666,7 +709,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
||||
common.extend(document.actions());
|
||||
|
||||
// Extend with actions that must have a selected layer
|
||||
if document.selected_nodes.selected_layers(document.metadata()).next().is_some() {
|
||||
if document.network_interface.selected_nodes(&[]).unwrap().selected_layers(document.metadata()).next().is_some() {
|
||||
common.extend(actions!(PortfolioMessageDiscriminant;
|
||||
Copy,
|
||||
Cut,
|
||||
@@ -730,12 +773,10 @@ impl PortfolioMessageHandler {
|
||||
|
||||
// TODO: Fix how this doesn't preserve tab order upon loading new document from *File > Open*
|
||||
fn load_document(&mut self, new_document: DocumentMessageHandler, document_id: DocumentId, responses: &mut VecDeque<Message>) {
|
||||
let mut new_document = new_document;
|
||||
let new_document = new_document;
|
||||
self.document_ids.push(document_id);
|
||||
new_document.update_layers_panel_options_bar_widgets(responses);
|
||||
|
||||
new_document.node_graph_handler.update_all_click_targets(&new_document.network, Vec::new());
|
||||
|
||||
self.documents.insert(document_id, new_document);
|
||||
|
||||
if self.active_document().is_some() {
|
||||
@@ -743,7 +784,7 @@ impl PortfolioMessageHandler {
|
||||
responses.add(ToolMessage::DeactivateTools);
|
||||
}
|
||||
|
||||
//TODO: Remove this and find a way to fix the issue where creating a new document when the node graph is open causes the transform in the new document to be incorrect
|
||||
// TODO: Remove this and find a way to fix the issue where creating a new document when the node graph is open causes the transform in the new document to be incorrect
|
||||
responses.add(DocumentMessage::GraphViewOverlay { open: false });
|
||||
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
||||
responses.add(PortfolioMessage::SelectDocument { document_id });
|
||||
|
||||
@@ -37,3 +37,22 @@ pub enum KeyboardPlatformLayout {
|
||||
/// Keyboard mapping used by Macs where Command is sometimes used in favor of Control
|
||||
Mac,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub enum PanelType {
|
||||
#[default]
|
||||
Document,
|
||||
Layers,
|
||||
Properties,
|
||||
}
|
||||
|
||||
impl From<String> for PanelType {
|
||||
fn from(value: String) -> Self {
|
||||
match value.as_str() {
|
||||
"Document" => PanelType::Document,
|
||||
"Layers" => PanelType::Layers,
|
||||
"Properties" => PanelType::Properties,
|
||||
_ => panic!("Unknown panel type: {}", value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, NodeNetworkInterface, NodeTemplate};
|
||||
use crate::messages::prelude::*;
|
||||
use bezier_rs::Subpath;
|
||||
use graph_craft::document::{value::TaggedValue, DocumentNode, NodeId, NodeInput, NodeNetwork};
|
||||
use graph_craft::document::{value::TaggedValue, NodeId, NodeInput};
|
||||
use graphene_core::raster::{BlendMode, ImageFrame};
|
||||
use graphene_core::text::Font;
|
||||
use graphene_core::vector::style::Gradient;
|
||||
@@ -13,7 +14,7 @@ use std::collections::VecDeque;
|
||||
|
||||
/// Create a new vector layer from a vector of [`bezier_rs::Subpath`].
|
||||
pub fn new_vector_layer(subpaths: Vec<Subpath<PointId>>, id: NodeId, parent: LayerNodeIdentifier, responses: &mut VecDeque<Message>) -> LayerNodeIdentifier {
|
||||
let insert_index = -1;
|
||||
let insert_index = 0;
|
||||
responses.add(GraphOperationMessage::NewVectorLayer { id, subpaths, parent, insert_index });
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![id] });
|
||||
|
||||
@@ -22,7 +23,7 @@ pub fn new_vector_layer(subpaths: Vec<Subpath<PointId>>, id: NodeId, parent: Lay
|
||||
|
||||
/// Create a new bitmap layer from an [`graphene_core::raster::ImageFrame<Color>`]
|
||||
pub fn new_image_layer(image_frame: ImageFrame<Color>, id: NodeId, parent: LayerNodeIdentifier, responses: &mut VecDeque<Message>) -> LayerNodeIdentifier {
|
||||
let insert_index = -1;
|
||||
let insert_index = 0;
|
||||
responses.add(GraphOperationMessage::NewBitmapLayer {
|
||||
id,
|
||||
image_frame,
|
||||
@@ -34,7 +35,7 @@ pub fn new_image_layer(image_frame: ImageFrame<Color>, id: NodeId, parent: Layer
|
||||
|
||||
/// Create a new group layer from an svg
|
||||
pub fn new_svg_layer(svg: String, transform: glam::DAffine2, id: NodeId, parent: LayerNodeIdentifier, responses: &mut VecDeque<Message>) -> LayerNodeIdentifier {
|
||||
let insert_index = -1;
|
||||
let insert_index = 0;
|
||||
responses.add(DocumentMessage::ImportSvg {
|
||||
id,
|
||||
svg,
|
||||
@@ -44,39 +45,37 @@ pub fn new_svg_layer(svg: String, transform: glam::DAffine2, id: NodeId, parent:
|
||||
});
|
||||
LayerNodeIdentifier::new_unchecked(id)
|
||||
}
|
||||
pub fn new_custom(id: NodeId, nodes: HashMap<NodeId, DocumentNode>, parent: LayerNodeIdentifier, responses: &mut VecDeque<Message>) -> LayerNodeIdentifier {
|
||||
responses.add(GraphOperationMessage::NewCustomLayer {
|
||||
id,
|
||||
nodes,
|
||||
parent,
|
||||
insert_index: -1,
|
||||
alias: String::new(),
|
||||
|
||||
pub fn new_custom(id: NodeId, nodes: Vec<(NodeId, NodeTemplate)>, parent: LayerNodeIdentifier, responses: &mut VecDeque<Message>) -> LayerNodeIdentifier {
|
||||
responses.add(GraphOperationMessage::NewCustomLayer { id, nodes, parent, insert_index: 0 });
|
||||
responses.add(GraphOperationMessage::SetUpstreamToChain {
|
||||
layer: LayerNodeIdentifier::new_unchecked(id),
|
||||
});
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![id] });
|
||||
LayerNodeIdentifier::new_unchecked(id)
|
||||
}
|
||||
|
||||
/// Locate the final pivot from the transform (TODO: decide how the pivot should actually work)
|
||||
pub fn get_pivot(layer: LayerNodeIdentifier, network: &NodeNetwork) -> Option<DVec2> {
|
||||
pub fn get_pivot(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<DVec2> {
|
||||
let pivot_node_input_index = 5;
|
||||
if let TaggedValue::DVec2(pivot) = NodeGraphLayer::new(layer, network).find_input("Transform", pivot_node_input_index)? {
|
||||
if let TaggedValue::DVec2(pivot) = NodeGraphLayer::new(layer, network_interface).find_input("Transform", pivot_node_input_index)? {
|
||||
Some(*pivot)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_viewport_pivot(layer: LayerNodeIdentifier, document_network: &NodeNetwork, document_metadata: &DocumentMetadata) -> DVec2 {
|
||||
let [min, max] = document_metadata.nonzero_bounding_box(layer);
|
||||
let pivot = get_pivot(layer, document_network).unwrap_or(DVec2::splat(0.5));
|
||||
document_metadata.transform_to_viewport(layer).transform_point2(min + (max - min) * pivot)
|
||||
pub fn get_viewport_pivot(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> DVec2 {
|
||||
let [min, max] = network_interface.document_metadata().nonzero_bounding_box(layer);
|
||||
let pivot = get_pivot(layer, network_interface).unwrap_or(DVec2::splat(0.5));
|
||||
network_interface.document_metadata().transform_to_viewport(layer).transform_point2(min + (max - min) * pivot)
|
||||
}
|
||||
|
||||
/// Get the current gradient of a layer from the closest Fill node
|
||||
pub fn get_gradient(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -> Option<Gradient> {
|
||||
pub fn get_gradient(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<Gradient> {
|
||||
let fill_index = 1;
|
||||
|
||||
let inputs = NodeGraphLayer::new(layer, document_network).find_node_inputs("Fill")?;
|
||||
let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Fill")?;
|
||||
let TaggedValue::Fill(graphene_std::vector::style::Fill::Gradient(gradient)) = inputs.get(fill_index)?.as_value()? else {
|
||||
return None;
|
||||
};
|
||||
@@ -84,10 +83,10 @@ pub fn get_gradient(layer: LayerNodeIdentifier, document_network: &NodeNetwork)
|
||||
}
|
||||
|
||||
/// Get the current fill of a layer from the closest Fill node
|
||||
pub fn get_fill_color(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -> Option<Color> {
|
||||
pub fn get_fill_color(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<Color> {
|
||||
let fill_index = 1;
|
||||
|
||||
let inputs = NodeGraphLayer::new(layer, document_network).find_node_inputs("Fill")?;
|
||||
let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Fill")?;
|
||||
let TaggedValue::Fill(graphene_std::vector::style::Fill::Solid(color)) = inputs.get(fill_index)?.as_value()? else {
|
||||
return None;
|
||||
};
|
||||
@@ -95,8 +94,8 @@ pub fn get_fill_color(layer: LayerNodeIdentifier, document_network: &NodeNetwork
|
||||
}
|
||||
|
||||
/// Get the current blend mode of a layer from the closest Blend Mode node
|
||||
pub fn get_blend_mode(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -> Option<BlendMode> {
|
||||
let inputs = NodeGraphLayer::new(layer, document_network).find_node_inputs("Blend Mode")?;
|
||||
pub fn get_blend_mode(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<BlendMode> {
|
||||
let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Blend Mode")?;
|
||||
let TaggedValue::BlendMode(blend_mode) = inputs.get(1)?.as_value()? else {
|
||||
return None;
|
||||
};
|
||||
@@ -110,25 +109,25 @@ pub fn get_blend_mode(layer: LayerNodeIdentifier, document_network: &NodeNetwork
|
||||
/// - Already factored into the pixel alpha channel of an image
|
||||
/// - The default value of 100% if no Opacity node is present, but this function returns None in that case
|
||||
/// With those limitations in mind, the intention of this function is to show just the value already present in an upstream Opacity node so that value can be directly edited.
|
||||
pub fn get_opacity(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -> Option<f64> {
|
||||
let inputs = NodeGraphLayer::new(layer, document_network).find_node_inputs("Opacity")?;
|
||||
pub fn get_opacity(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<f64> {
|
||||
let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Opacity")?;
|
||||
let TaggedValue::F64(opacity) = inputs.get(1)?.as_value()? else {
|
||||
return None;
|
||||
};
|
||||
Some(*opacity)
|
||||
}
|
||||
|
||||
pub fn get_fill_id(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -> Option<NodeId> {
|
||||
NodeGraphLayer::new(layer, document_network).upstream_node_id_from_name("Fill")
|
||||
pub fn get_fill_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
|
||||
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Fill")
|
||||
}
|
||||
|
||||
pub fn get_text_id(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -> Option<NodeId> {
|
||||
NodeGraphLayer::new(layer, document_network).upstream_node_id_from_name("Text")
|
||||
pub fn get_text_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
|
||||
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Text")
|
||||
}
|
||||
|
||||
/// Gets properties from the Text node
|
||||
pub fn get_text(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -> Option<(&String, &Font, f64)> {
|
||||
let inputs = NodeGraphLayer::new(layer, document_network).find_node_inputs("Text")?;
|
||||
pub fn get_text(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<(&String, &Font, f64)> {
|
||||
let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Text")?;
|
||||
|
||||
let Some(TaggedValue::String(text)) = &inputs[1].as_value() else { return None };
|
||||
let Some(TaggedValue::Font(font)) = &inputs[2].as_value() else { return None };
|
||||
@@ -137,9 +136,9 @@ pub fn get_text(layer: LayerNodeIdentifier, document_network: &NodeNetwork) -> O
|
||||
Some((text, font, font_size))
|
||||
}
|
||||
|
||||
pub fn get_stroke_width(layer: LayerNodeIdentifier, network: &NodeNetwork) -> Option<f64> {
|
||||
pub fn get_stroke_width(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<f64> {
|
||||
let weight_node_input_index = 2;
|
||||
if let TaggedValue::F64(width) = NodeGraphLayer::new(layer, network).find_input("Stroke", weight_node_input_index)? {
|
||||
if let TaggedValue::F64(width) = NodeGraphLayer::new(layer, network_interface).find_input("Stroke", weight_node_input_index)? {
|
||||
Some(*width)
|
||||
} else {
|
||||
None
|
||||
@@ -147,43 +146,44 @@ pub fn get_stroke_width(layer: LayerNodeIdentifier, network: &NodeNetwork) -> Op
|
||||
}
|
||||
|
||||
/// Checks if a specified layer uses an upstream node matching the given name.
|
||||
pub fn is_layer_fed_by_node_of_name(layer: LayerNodeIdentifier, document_network: &NodeNetwork, node_name: &str) -> bool {
|
||||
NodeGraphLayer::new(layer, document_network).find_node_inputs(node_name).is_some()
|
||||
pub fn is_layer_fed_by_node_of_name(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface, node_name: &str) -> bool {
|
||||
NodeGraphLayer::new(layer, network_interface).find_node_inputs(node_name).is_some()
|
||||
}
|
||||
|
||||
/// An immutable reference to a layer within the document node graph for easy access.
|
||||
pub struct NodeGraphLayer<'a> {
|
||||
node_graph: &'a NodeNetwork,
|
||||
network_interface: &'a NodeNetworkInterface,
|
||||
layer_node: NodeId,
|
||||
}
|
||||
|
||||
impl<'a> NodeGraphLayer<'a> {
|
||||
/// Get the layer node from the document
|
||||
pub fn new(layer: LayerNodeIdentifier, network: &'a NodeNetwork) -> Self {
|
||||
pub fn new(layer: LayerNodeIdentifier, network_interface: &'a NodeNetworkInterface) -> Self {
|
||||
debug_assert!(layer != LayerNodeIdentifier::ROOT_PARENT, "Cannot create new NodeGraphLayer from ROOT_PARENT");
|
||||
Self {
|
||||
node_graph: network,
|
||||
network_interface,
|
||||
layer_node: layer.to_node(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an iterator up the horizontal flow of the layer
|
||||
pub fn horizontal_layer_flow(&self) -> impl Iterator<Item = (&'a DocumentNode, NodeId)> {
|
||||
self.node_graph.upstream_flow_back_from_nodes(vec![self.layer_node], graph_craft::document::FlowType::HorizontalFlow)
|
||||
pub fn horizontal_layer_flow(&self) -> impl Iterator<Item = NodeId> + 'a {
|
||||
self.network_interface.upstream_flow_back_from_nodes(vec![self.layer_node], &[], FlowType::HorizontalFlow)
|
||||
}
|
||||
|
||||
/// Node id of a node if it exists in the layer's primary flow
|
||||
pub fn upstream_node_id_from_name(&self, node_name: &str) -> Option<NodeId> {
|
||||
self.horizontal_layer_flow().find(|(node, _)| node.name == node_name).map(|(_, id)| id)
|
||||
self.horizontal_layer_flow()
|
||||
.find(|node_id| self.network_interface.reference(node_id, &[]).is_some_and(|reference| reference == node_name))
|
||||
}
|
||||
|
||||
/// Find all of the inputs of a specific node within the layer's primary flow, up until the next layer is reached.
|
||||
pub fn find_node_inputs(&self, node_name: &str) -> Option<&'a Vec<NodeInput>> {
|
||||
self.horizontal_layer_flow()
|
||||
.skip(1)// Skip self
|
||||
.take_while(|(node, _)| !node.is_layer)
|
||||
.find(|(node, _)| node.name == node_name)
|
||||
.map(|(node, _id)| &node.inputs)
|
||||
.take_while(|node_id| !self.network_interface.is_layer(node_id,&[]))
|
||||
.find(|node_id| self.network_interface.reference(node_id,&[]).is_some_and(|reference| reference == node_name))
|
||||
.and_then(|node_id| self.network_interface.network(&[]).unwrap().nodes.get(&node_id).map(|node| &node.inputs))
|
||||
}
|
||||
|
||||
/// Find a specific input of a node within the layer's primary flow
|
||||
|
||||
@@ -45,7 +45,11 @@ impl Pivot {
|
||||
|
||||
/// Recomputes the pivot position and transform.
|
||||
fn recalculate_pivot(&mut self, document: &DocumentMessageHandler) {
|
||||
let mut layers = document.selected_nodes.selected_visible_and_unlocked_layers(document.metadata());
|
||||
let mut layers = document
|
||||
.network_interface
|
||||
.selected_nodes(&[])
|
||||
.unwrap()
|
||||
.selected_visible_and_unlocked_layers(&document.network_interface);
|
||||
let Some(first) = layers.next() else {
|
||||
// If no layers are selected then we revert things back to default
|
||||
self.normalized_pivot = DVec2::splat(0.5);
|
||||
@@ -58,16 +62,18 @@ impl Pivot {
|
||||
|
||||
// If just one layer is selected we can use its inner transform (as it accounts for rotation)
|
||||
if selected_layers_count == 1 {
|
||||
let normalized_pivot = graph_modification_utils::get_pivot(first, &document.network).unwrap_or(DVec2::splat(0.5));
|
||||
let normalized_pivot = graph_modification_utils::get_pivot(first, &document.network_interface).unwrap_or(DVec2::splat(0.5));
|
||||
self.normalized_pivot = normalized_pivot;
|
||||
self.transform_from_normalized = Self::get_layer_pivot_transform(first, document);
|
||||
self.pivot = Some(self.transform_from_normalized.transform_point2(normalized_pivot));
|
||||
} else {
|
||||
// If more than one layer is selected we use the AABB with the mean of the pivots
|
||||
let xy_summation = document
|
||||
.selected_nodes
|
||||
.selected_visible_and_unlocked_layers(document.metadata())
|
||||
.map(|layer| graph_modification_utils::get_viewport_pivot(layer, &document.network, &document.metadata))
|
||||
.network_interface
|
||||
.selected_nodes(&[])
|
||||
.unwrap()
|
||||
.selected_visible_and_unlocked_layers(&document.network_interface)
|
||||
.map(|layer| graph_modification_utils::get_viewport_pivot(layer, &document.network_interface))
|
||||
.reduce(|a, b| a + b)
|
||||
.unwrap_or_default();
|
||||
|
||||
@@ -101,7 +107,12 @@ impl Pivot {
|
||||
|
||||
/// Sets the viewport position of the pivot for all selected layers.
|
||||
pub fn set_viewport_position(&self, position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
||||
for layer in document.selected_nodes.selected_visible_and_unlocked_layers(document.metadata()) {
|
||||
for layer in document
|
||||
.network_interface
|
||||
.selected_nodes(&[])
|
||||
.unwrap()
|
||||
.selected_visible_and_unlocked_layers(&document.network_interface)
|
||||
{
|
||||
let transform = Self::get_layer_pivot_transform(layer, document);
|
||||
let pivot = transform.inverse().transform_point2(position);
|
||||
// Only update the pivot when computed position is finite. Infinite can happen when scale is 0.
|
||||
|
||||
@@ -37,15 +37,15 @@ impl Resize {
|
||||
return None;
|
||||
}
|
||||
|
||||
if !document.network().nodes.contains_key(&layer.to_node()) {
|
||||
if !document.network_interface.network(&[]).unwrap().nodes.contains_key(&layer.to_node()) {
|
||||
self.layer.take();
|
||||
return None;
|
||||
}
|
||||
|
||||
let start = self.viewport_drag_start(document);
|
||||
let mouse = input.mouse.position;
|
||||
let to_viewport = document.metadata().document_to_viewport;
|
||||
let document_mouse = to_viewport.inverse().transform_point2(mouse);
|
||||
let document_to_viewport = document.navigation_handler.calculate_offset_transform(input.viewport_bounds.center(), &document.document_ptz);
|
||||
let document_mouse = document_to_viewport.inverse().transform_point2(mouse);
|
||||
let mut points_viewport = [start, mouse];
|
||||
let ignore = if let Some(layer) = self.layer { vec![layer] } else { vec![] };
|
||||
let ratio = input.keyboard.get(lock_ratio as usize);
|
||||
@@ -55,7 +55,7 @@ impl Resize {
|
||||
let size = points_viewport[1] - points_viewport[0];
|
||||
let size = size.abs().max(size.abs().yx()) * size.signum();
|
||||
points_viewport[1] = points_viewport[0] + size;
|
||||
let end_document = to_viewport.inverse().transform_point2(points_viewport[1]);
|
||||
let end_document = document_to_viewport.inverse().transform_point2(points_viewport[1]);
|
||||
let constraint = SnapConstraint::Line {
|
||||
origin: self.drag_start,
|
||||
direction: end_document - self.drag_start,
|
||||
@@ -65,24 +65,24 @@ impl Resize {
|
||||
let far = SnapCandidatePoint::handle(2. * self.drag_start - end_document);
|
||||
let snapped_far = self.snap_manager.constrained_snap(&snap_data, &far, constraint, None);
|
||||
let best = if snapped_far.other_snap_better(&snapped) { snapped } else { snapped_far };
|
||||
points_viewport[0] = to_viewport.transform_point2(best.snapped_point_document);
|
||||
points_viewport[1] = to_viewport.transform_point2(self.drag_start * 2. - best.snapped_point_document);
|
||||
points_viewport[0] = document_to_viewport.transform_point2(best.snapped_point_document);
|
||||
points_viewport[1] = document_to_viewport.transform_point2(self.drag_start * 2. - best.snapped_point_document);
|
||||
self.snap_manager.update_indicator(best);
|
||||
} else {
|
||||
let snapped = self.snap_manager.constrained_snap(&snap_data, &SnapCandidatePoint::handle(end_document), constraint, None);
|
||||
points_viewport[1] = to_viewport.transform_point2(snapped.snapped_point_document);
|
||||
points_viewport[1] = document_to_viewport.transform_point2(snapped.snapped_point_document);
|
||||
self.snap_manager.update_indicator(snapped);
|
||||
}
|
||||
} else if center {
|
||||
let snapped = self.snap_manager.free_snap(&snap_data, &SnapCandidatePoint::handle(document_mouse), None, false);
|
||||
let snapped_far = self.snap_manager.free_snap(&snap_data, &SnapCandidatePoint::handle(2. * self.drag_start - document_mouse), None, false);
|
||||
let best = if snapped_far.other_snap_better(&snapped) { snapped } else { snapped_far };
|
||||
points_viewport[0] = to_viewport.transform_point2(best.snapped_point_document);
|
||||
points_viewport[1] = to_viewport.transform_point2(self.drag_start * 2. - best.snapped_point_document);
|
||||
points_viewport[0] = document_to_viewport.transform_point2(best.snapped_point_document);
|
||||
points_viewport[1] = document_to_viewport.transform_point2(self.drag_start * 2. - best.snapped_point_document);
|
||||
self.snap_manager.update_indicator(best);
|
||||
} else {
|
||||
let snapped = self.snap_manager.free_snap(&snap_data, &SnapCandidatePoint::handle(document_mouse), None, false);
|
||||
points_viewport[1] = to_viewport.transform_point2(snapped.snapped_point_document);
|
||||
points_viewport[1] = document_to_viewport.transform_point2(snapped.snapped_point_document);
|
||||
self.snap_manager.update_indicator(snapped);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@ use super::graph_modification_utils;
|
||||
use super::snapping::{SnapCandidatePoint, SnapData, SnapManager, SnappedPoint};
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
|
||||
use crate::messages::portfolio::document::utility_types::misc::{GeometrySnapSource, SnapSource};
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use bezier_rs::{Bezier, BezierHandles, TValue};
|
||||
use graph_craft::document::NodeNetwork;
|
||||
use graphene_core::transform::Transform;
|
||||
use graphene_core::vector::{ManipulatorPointId, PointId, VectorData, VectorModificationType};
|
||||
|
||||
@@ -171,7 +171,7 @@ impl ShapeState {
|
||||
let mut snap_data = SnapData::new(document, input);
|
||||
|
||||
for (layer, state) in &self.selected_shape_state {
|
||||
let Some(vector_data) = document.metadata.compute_modified_vector(*layer, &document.network) else {
|
||||
let Some(vector_data) = document.metadata().compute_modified_vector(*layer, &document.network_interface) else {
|
||||
continue;
|
||||
};
|
||||
for point in &state.selected_points {
|
||||
@@ -180,15 +180,20 @@ impl ShapeState {
|
||||
}
|
||||
}
|
||||
|
||||
let mouse_delta = document.metadata.document_to_viewport.inverse().transform_vector2(input.mouse.position - previous_mouse);
|
||||
let mouse_delta = document
|
||||
.network_interface
|
||||
.document_metadata()
|
||||
.document_to_viewport
|
||||
.inverse()
|
||||
.transform_vector2(input.mouse.position - previous_mouse);
|
||||
let mut offset = mouse_delta;
|
||||
let mut best_snapped = SnappedPoint::infinite_snap(document.metadata.document_to_viewport.inverse().transform_point2(input.mouse.position));
|
||||
let mut best_snapped = SnappedPoint::infinite_snap(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
|
||||
for (layer, state) in &self.selected_shape_state {
|
||||
let Some(vector_data) = document.metadata.compute_modified_vector(*layer, &document.network) else {
|
||||
let Some(vector_data) = document.metadata().compute_modified_vector(*layer, &document.network_interface) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let to_document = document.metadata.transform_to_document(*layer);
|
||||
let to_document = document.metadata().transform_to_document(*layer);
|
||||
|
||||
for &selected in &state.selected_points {
|
||||
let source = match selected {
|
||||
@@ -218,25 +223,18 @@ impl ShapeState {
|
||||
}
|
||||
}
|
||||
snap_manager.update_indicator(best_snapped);
|
||||
document.metadata.document_to_viewport.transform_vector2(offset)
|
||||
document.metadata().document_to_viewport.transform_vector2(offset)
|
||||
}
|
||||
|
||||
/// Select/deselect the first point within the selection threshold.
|
||||
/// Returns a tuple of the points if found and the offset, or `None` otherwise.
|
||||
pub fn change_point_selection(
|
||||
&mut self,
|
||||
document_network: &NodeNetwork,
|
||||
document_metadata: &DocumentMetadata,
|
||||
mouse_position: DVec2,
|
||||
select_threshold: f64,
|
||||
add_to_selection: bool,
|
||||
) -> Option<Option<SelectedPointsInfo>> {
|
||||
pub fn change_point_selection(&mut self, network_interface: &NodeNetworkInterface, mouse_position: DVec2, select_threshold: f64, add_to_selection: bool) -> Option<Option<SelectedPointsInfo>> {
|
||||
if self.selected_shape_state.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some((layer, manipulator_point_id)) = self.find_nearest_point_indices(document_network, document_metadata, mouse_position, select_threshold) {
|
||||
let vector_data = document_metadata.compute_modified_vector(layer, document_network)?;
|
||||
if let Some((layer, manipulator_point_id)) = self.find_nearest_point_indices(network_interface, mouse_position, select_threshold) {
|
||||
let vector_data = network_interface.document_metadata().compute_modified_vector(layer, network_interface)?;
|
||||
let point_position = manipulator_point_id.get_position(&vector_data)?;
|
||||
|
||||
let selected_shape_state = self.selected_shape_state.get(&layer)?;
|
||||
@@ -246,7 +244,7 @@ impl ShapeState {
|
||||
let new_selected = if already_selected { !add_to_selection } else { true };
|
||||
|
||||
// Offset to snap the selected point to the cursor
|
||||
let offset = mouse_position - document_metadata.transform_to_viewport(layer).transform_point2(point_position);
|
||||
let offset = mouse_position - network_interface.document_metadata().transform_to_viewport(layer).transform_point2(point_position);
|
||||
|
||||
// This is selecting the manipulator only for now, next to generalize to points
|
||||
if new_selected {
|
||||
@@ -287,10 +285,10 @@ impl ShapeState {
|
||||
|
||||
/// Selects all anchors connected to the selected subpath, and deselects all handles, for the given layer.
|
||||
pub fn select_connected_anchors(&mut self, document: &DocumentMessageHandler, layer: LayerNodeIdentifier, mouse: DVec2) {
|
||||
let Some(vector_data) = document.metadata.compute_modified_vector(layer, &document.network) else {
|
||||
let Some(vector_data) = document.metadata().compute_modified_vector(layer, &document.network_interface) else {
|
||||
return;
|
||||
};
|
||||
let to_viewport = document.metadata.transform_to_viewport(layer);
|
||||
let to_viewport = document.metadata().transform_to_viewport(layer);
|
||||
let layer_mouse = to_viewport.inverse().transform_point2(mouse);
|
||||
let state = self.selected_shape_state.entry(layer).or_default();
|
||||
|
||||
@@ -335,7 +333,7 @@ impl ShapeState {
|
||||
|
||||
/// Internal helper function that selects all anchors, and deselects all handles, for a layer given its [`LayerNodeIdentifier`] and [`SelectedLayerState`].
|
||||
fn select_all_anchors_in_layer_with_state(document: &DocumentMessageHandler, layer: LayerNodeIdentifier, state: &mut SelectedLayerState) {
|
||||
let Some(vector_data) = document.metadata.compute_modified_vector(layer, &document.network) else {
|
||||
let Some(vector_data) = document.metadata().compute_modified_vector(layer, &document.network_interface) else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -429,14 +427,13 @@ impl ShapeState {
|
||||
pub fn reposition_control_point(
|
||||
&self,
|
||||
point: &ManipulatorPointId,
|
||||
network: &NodeNetwork,
|
||||
metadata: &DocumentMetadata,
|
||||
network_interface: &NodeNetworkInterface,
|
||||
new_position: DVec2,
|
||||
layer: LayerNodeIdentifier,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) -> Option<()> {
|
||||
let vector_data = metadata.compute_modified_vector(layer, network)?;
|
||||
let transform = metadata.transform_to_document(layer).inverse();
|
||||
let vector_data = network_interface.document_metadata().compute_modified_vector(layer, network_interface)?;
|
||||
let transform = network_interface.document_metadata().transform_to_document(layer).inverse();
|
||||
let position = transform.transform_point2(new_position);
|
||||
let current_position = point.get_position(&vector_data)?;
|
||||
let delta = position - current_position;
|
||||
@@ -464,12 +461,12 @@ impl ShapeState {
|
||||
|
||||
/// Iterates over the selected manipulator groups, returning whether their handles have mixed, colinear, or free angles.
|
||||
/// If there are no points selected this function returns mixed.
|
||||
pub fn selected_manipulator_angles(&self, document_network: &NodeNetwork, document_metadata: &DocumentMetadata) -> ManipulatorAngle {
|
||||
pub fn selected_manipulator_angles(&self, network_interface: &NodeNetworkInterface) -> ManipulatorAngle {
|
||||
// This iterator contains a bool indicating whether or not selected points' manipulator groups have colinear handles.
|
||||
let mut points_colinear_status = self
|
||||
.selected_shape_state
|
||||
.iter()
|
||||
.map(|(&layer, selection_state)| (document_metadata.compute_modified_vector(layer, document_network), selection_state))
|
||||
.map(|(&layer, selection_state)| (network_interface.document_metadata().compute_modified_vector(layer, network_interface), selection_state))
|
||||
.flat_map(|(data, selection_state)| selection_state.selected_points.iter().map(move |&point| data.as_ref().map_or(false, |data| data.colinear(point))));
|
||||
|
||||
let Some(first_is_colinear) = points_colinear_status.next() else { return ManipulatorAngle::Mixed };
|
||||
@@ -547,10 +544,10 @@ impl ShapeState {
|
||||
let mut skip_set = HashSet::new();
|
||||
|
||||
for (&layer, layer_state) in self.selected_shape_state.iter() {
|
||||
let Some(vector_data) = document.metadata.compute_modified_vector(layer, &document.network) else {
|
||||
let Some(vector_data) = document.metadata().compute_modified_vector(layer, &document.network_interface) else {
|
||||
continue;
|
||||
};
|
||||
let transform = document.metadata.transform_to_document(layer);
|
||||
let transform = document.metadata().transform_to_document(layer);
|
||||
|
||||
for &point in layer_state.selected_points.iter() {
|
||||
let Some(handles) = point.get_handle_pair(&vector_data) else { continue };
|
||||
@@ -622,12 +619,12 @@ impl ShapeState {
|
||||
/// Move the selected points by dragging the mouse.
|
||||
pub fn move_selected_points(&self, handle_lengths: Option<OpposingHandleLengths>, document: &DocumentMessageHandler, delta: DVec2, equidistant: bool, responses: &mut VecDeque<Message>) {
|
||||
for (&layer, state) in &self.selected_shape_state {
|
||||
let Some(vector_data) = document.metadata.compute_modified_vector(layer, &document.network) else {
|
||||
let Some(vector_data) = document.metadata().compute_modified_vector(layer, &document.network_interface) else {
|
||||
continue;
|
||||
};
|
||||
let opposing_handles = handle_lengths.as_ref().and_then(|handle_lengths| handle_lengths.get(&layer));
|
||||
|
||||
let transform = document.metadata.transform_to_viewport(layer);
|
||||
let transform = document.metadata().transform_to_viewport(layer);
|
||||
let delta = transform.inverse().transform_vector2(delta);
|
||||
|
||||
for &point in state.selected_points.iter() {
|
||||
@@ -662,7 +659,7 @@ impl ShapeState {
|
||||
let new_relative = if equidistant {
|
||||
-(handle_position - anchor_position)
|
||||
} else {
|
||||
let transform = document.metadata.document_to_viewport.inverse() * transform;
|
||||
let transform = document.metadata().document_to_viewport.inverse() * transform;
|
||||
let Some(other_position) = other.to_manipulator_point().get_position(&vector_data) else {
|
||||
continue;
|
||||
};
|
||||
@@ -683,8 +680,8 @@ impl ShapeState {
|
||||
self.selected_shape_state
|
||||
.iter()
|
||||
.filter_map(|(&layer, state)| {
|
||||
let vector_data = document.metadata.compute_modified_vector(layer, &document.network)?;
|
||||
let transform = document.metadata.transform_to_document(layer);
|
||||
let vector_data = document.metadata().compute_modified_vector(layer, &document.network_interface)?;
|
||||
let transform = document.metadata().transform_to_document(layer);
|
||||
let opposing_handle_lengths = vector_data
|
||||
.colinear_manipulators
|
||||
.iter()
|
||||
@@ -749,7 +746,7 @@ impl ShapeState {
|
||||
pub fn delete_selected_points(&self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
||||
for (&layer, state) in &self.selected_shape_state {
|
||||
let mut missing_anchors = HashMap::new();
|
||||
let Some(vector_data) = document.metadata.compute_modified_vector(layer, &document.network) else {
|
||||
let Some(vector_data) = document.metadata().compute_modified_vector(layer, &document.network_interface) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
@@ -840,7 +837,7 @@ impl ShapeState {
|
||||
|
||||
pub fn break_path_at_selected_point(&self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
||||
for (&layer, state) in &self.selected_shape_state {
|
||||
let Some(vector_data) = document.metadata.compute_modified_vector(layer, &document.network) else {
|
||||
let Some(vector_data) = document.metadata().compute_modified_vector(layer, &document.network_interface) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
@@ -886,7 +883,7 @@ impl ShapeState {
|
||||
/// Delete point(s) and adjacent segments.
|
||||
pub fn delete_point_and_break_path(&self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
||||
for (&layer, state) in &self.selected_shape_state {
|
||||
let Some(vector_data) = document.metadata.compute_modified_vector(layer, &document.network) else {
|
||||
let Some(vector_data) = document.metadata().compute_modified_vector(layer, &document.network_interface) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
@@ -907,9 +904,11 @@ impl ShapeState {
|
||||
}
|
||||
|
||||
/// Disable colinear handles colinear.
|
||||
pub fn disable_colinear_handles_state_on_selected(&self, metadata: &DocumentMetadata, network: &NodeNetwork, responses: &mut VecDeque<Message>) {
|
||||
pub fn disable_colinear_handles_state_on_selected(&self, network_interface: &NodeNetworkInterface, responses: &mut VecDeque<Message>) {
|
||||
for (&layer, state) in &self.selected_shape_state {
|
||||
let Some(vector_data) = metadata.compute_modified_vector(layer, network) else { continue };
|
||||
let Some(vector_data) = network_interface.document_metadata().compute_modified_vector(layer, network_interface) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
for &point in &state.selected_points {
|
||||
if let ManipulatorPointId::Anchor(point) = point {
|
||||
@@ -928,13 +927,7 @@ impl ShapeState {
|
||||
}
|
||||
|
||||
/// Find a [ManipulatorPoint] that is within the selection threshold and return the layer path, an index to the [ManipulatorGroup], and an enum index for [ManipulatorPoint].
|
||||
pub fn find_nearest_point_indices(
|
||||
&mut self,
|
||||
document_network: &NodeNetwork,
|
||||
document_metadata: &DocumentMetadata,
|
||||
mouse_position: DVec2,
|
||||
select_threshold: f64,
|
||||
) -> Option<(LayerNodeIdentifier, ManipulatorPointId)> {
|
||||
pub fn find_nearest_point_indices(&mut self, network_interface: &NodeNetworkInterface, mouse_position: DVec2, select_threshold: f64) -> Option<(LayerNodeIdentifier, ManipulatorPointId)> {
|
||||
if self.selected_shape_state.is_empty() {
|
||||
return None;
|
||||
}
|
||||
@@ -943,7 +936,7 @@ impl ShapeState {
|
||||
|
||||
// Find the closest control point among all elements of shapes_to_modify
|
||||
for &layer in self.selected_shape_state.keys() {
|
||||
if let Some((manipulator_point_id, distance_squared)) = Self::closest_point_in_layer(document_network, document_metadata, layer, mouse_position) {
|
||||
if let Some((manipulator_point_id, distance_squared)) = Self::closest_point_in_layer(network_interface, layer, mouse_position) {
|
||||
// Choose the first point under the threshold
|
||||
if distance_squared < select_threshold_squared {
|
||||
trace!("Selecting... manipulator point: {manipulator_point_id:?}");
|
||||
@@ -959,12 +952,12 @@ impl ShapeState {
|
||||
/// Find the closest manipulator, manipulator point, and distance so we can select path elements.
|
||||
/// Brute force comparison to determine which manipulator (handle or anchor) we want to select taking O(n) time.
|
||||
/// Return value is an `Option` of the tuple representing `(ManipulatorPointId, distance squared)`.
|
||||
fn closest_point_in_layer(document_network: &NodeNetwork, document_metadata: &DocumentMetadata, layer: LayerNodeIdentifier, pos: glam::DVec2) -> Option<(ManipulatorPointId, f64)> {
|
||||
fn closest_point_in_layer(network_interface: &NodeNetworkInterface, layer: LayerNodeIdentifier, pos: glam::DVec2) -> Option<(ManipulatorPointId, f64)> {
|
||||
let mut closest_distance_squared: f64 = f64::MAX;
|
||||
let mut manipulator_point = None;
|
||||
|
||||
let vector_data = document_metadata.compute_modified_vector(layer, document_network)?;
|
||||
let viewspace = document_metadata.transform_to_viewport(layer);
|
||||
let vector_data = network_interface.document_metadata().compute_modified_vector(layer, network_interface)?;
|
||||
let viewspace = network_interface.document_metadata().transform_to_viewport(layer);
|
||||
|
||||
// Handles
|
||||
for (segment_id, bezier, _, _) in vector_data.segment_bezier_iter() {
|
||||
@@ -999,8 +992,8 @@ impl ShapeState {
|
||||
}
|
||||
|
||||
/// Find the `t` value along the path segment we have clicked upon, together with that segment ID.
|
||||
fn closest_segment(&self, document_network: &NodeNetwork, document_metadata: &DocumentMetadata, layer: LayerNodeIdentifier, position: glam::DVec2, tolerance: f64) -> Option<ClosestSegment> {
|
||||
let transform = document_metadata.transform_to_viewport(layer);
|
||||
fn closest_segment(&self, network_interface: &NodeNetworkInterface, layer: LayerNodeIdentifier, position: glam::DVec2, tolerance: f64) -> Option<ClosestSegment> {
|
||||
let transform = network_interface.document_metadata().transform_to_viewport(layer);
|
||||
let layer_pos = transform.inverse().transform_point2(position);
|
||||
|
||||
let tolerance = tolerance + 0.5;
|
||||
@@ -1008,7 +1001,7 @@ impl ShapeState {
|
||||
let mut closest = None;
|
||||
let mut closest_distance_squared: f64 = tolerance * tolerance;
|
||||
|
||||
let vector_data = document_metadata.compute_modified_vector(layer, document_network)?;
|
||||
let vector_data = network_interface.document_metadata().compute_modified_vector(layer, network_interface)?;
|
||||
|
||||
for (segment, mut bezier, start, end) in vector_data.segment_bezier_iter() {
|
||||
let t = bezier.project(layer_pos);
|
||||
@@ -1023,7 +1016,7 @@ impl ShapeState {
|
||||
// 0.5 is half the line (center to side) but it's convenient to allow targeting slightly more than half the line width
|
||||
const STROKE_WIDTH_PERCENT: f64 = 0.7;
|
||||
|
||||
let stroke_width = graph_modification_utils::get_stroke_width(layer, document_network).unwrap_or(1.) as f64 * STROKE_WIDTH_PERCENT;
|
||||
let stroke_width = graph_modification_utils::get_stroke_width(layer, network_interface).unwrap_or(1.) as f64 * STROKE_WIDTH_PERCENT;
|
||||
|
||||
// Convert to linear if handes are on top of control points
|
||||
if let bezier_rs::BezierHandles::Cubic { handle_start, handle_end } = bezier.handles {
|
||||
@@ -1054,22 +1047,22 @@ impl ShapeState {
|
||||
}
|
||||
|
||||
/// find closest to the position segment on selected layers. If there is more than one layers with close enough segment it return upper from them
|
||||
pub fn upper_closest_segment(&self, document_network: &NodeNetwork, document_metadata: &DocumentMetadata, position: glam::DVec2, tolerance: f64) -> Option<ClosestSegment> {
|
||||
let closest_seg = |layer| self.closest_segment(document_network, document_metadata, layer, position, tolerance);
|
||||
pub fn upper_closest_segment(&self, network_interface: &NodeNetworkInterface, position: glam::DVec2, tolerance: f64) -> Option<ClosestSegment> {
|
||||
let closest_seg = |layer| self.closest_segment(network_interface, layer, position, tolerance);
|
||||
match self.selected_shape_state.len() {
|
||||
0 => None,
|
||||
1 => self.selected_layers().next().copied().and_then(closest_seg),
|
||||
_ => self.sorted_selected_layers(document_metadata).find_map(closest_seg),
|
||||
_ => self.sorted_selected_layers(network_interface.document_metadata()).find_map(closest_seg),
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a nearby clicked anchor point's handles between sharp (zero-length handles) and smooth (pulled-apart handle(s)).
|
||||
/// If both handles aren't zero-length, they are set that. If both are zero-length, they are stretched apart by a reasonable amount.
|
||||
/// This can can be activated by double clicking on an anchor with the Path tool.
|
||||
pub fn flip_smooth_sharp(&self, document_network: &NodeNetwork, document_metadata: &DocumentMetadata, target: glam::DVec2, tolerance: f64, responses: &mut VecDeque<Message>) -> bool {
|
||||
pub fn flip_smooth_sharp(&self, network_interface: &NodeNetworkInterface, target: glam::DVec2, tolerance: f64, responses: &mut VecDeque<Message>) -> bool {
|
||||
let mut process_layer = |layer| {
|
||||
let vector_data = document_metadata.compute_modified_vector(layer, document_network)?;
|
||||
let transform_to_screenspace = document_metadata.transform_to_viewport(layer);
|
||||
let vector_data = network_interface.document_metadata().compute_modified_vector(layer, network_interface)?;
|
||||
let transform_to_screenspace = network_interface.document_metadata().transform_to_viewport(layer);
|
||||
|
||||
let mut result = None;
|
||||
let mut closest_distance_squared = tolerance * tolerance;
|
||||
@@ -1145,15 +1138,15 @@ impl ShapeState {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn select_all_in_quad(&mut self, document_network: &NodeNetwork, document_metadata: &DocumentMetadata, quad: [DVec2; 2], clear_selection: bool) {
|
||||
pub fn select_all_in_quad(&mut self, network_interface: &NodeNetworkInterface, quad: [DVec2; 2], clear_selection: bool) {
|
||||
for (&layer, state) in &mut self.selected_shape_state {
|
||||
if clear_selection {
|
||||
state.clear_points()
|
||||
}
|
||||
|
||||
let vector_data = document_metadata.compute_modified_vector(layer, document_network);
|
||||
let vector_data = network_interface.document_metadata().compute_modified_vector(layer, network_interface);
|
||||
let Some(vector_data) = vector_data else { continue };
|
||||
let transform = document_metadata.transform_to_viewport(layer);
|
||||
let transform = network_interface.document_metadata().transform_to_viewport(layer);
|
||||
|
||||
assert_eq!(vector_data.segment_domain.ids().len(), vector_data.segment_domain.start_point().len());
|
||||
assert_eq!(vector_data.segment_domain.ids().len(), vector_data.segment_domain.end_point().len());
|
||||
|
||||
@@ -187,7 +187,7 @@ impl SnapManager {
|
||||
self.indicator = None;
|
||||
}
|
||||
pub fn preview_draw(&mut self, snap_data: &SnapData, mouse: DVec2) {
|
||||
let point = SnapCandidatePoint::handle(snap_data.document.metadata.document_to_viewport.inverse().transform_point2(mouse));
|
||||
let point = SnapCandidatePoint::handle(snap_data.document.metadata().document_to_viewport.inverse().transform_point2(mouse));
|
||||
let snapped = self.free_snap(snap_data, &point, None, false);
|
||||
self.update_indicator(snapped);
|
||||
}
|
||||
@@ -230,7 +230,7 @@ impl SnapManager {
|
||||
let mut best_point = None;
|
||||
|
||||
for point in snapped_points {
|
||||
let viewport_point = document.metadata.document_to_viewport.transform_point2(point.snapped_point_document);
|
||||
let viewport_point = document.metadata().document_to_viewport.transform_point2(point.snapped_point_document);
|
||||
let on_screen = viewport_point.cmpgt(DVec2::ZERO).all() && viewport_point.cmplt(snap_data.input.viewport_bounds.size()).all();
|
||||
if !on_screen && !off_screen {
|
||||
continue;
|
||||
@@ -258,29 +258,29 @@ impl SnapManager {
|
||||
if candidates.len() > 10 {
|
||||
return;
|
||||
}
|
||||
if !document.selected_nodes.layer_visible(layer, &document.metadata) {
|
||||
if !document.network_interface.selected_nodes(&[]).unwrap().layer_visible(layer, &document.network_interface) {
|
||||
return;
|
||||
}
|
||||
if snap_data.ignore.contains(&layer) {
|
||||
return;
|
||||
}
|
||||
if document.metadata.is_folder(layer) {
|
||||
for layer in layer.children(&document.metadata) {
|
||||
if layer.has_children(document.metadata()) {
|
||||
for layer in layer.children(document.metadata()) {
|
||||
add_candidates(layer, snap_data, quad, candidates);
|
||||
}
|
||||
return;
|
||||
}
|
||||
let Some(bounds) = document.metadata.bounding_box_with_transform(layer, DAffine2::IDENTITY) else {
|
||||
let Some(bounds) = document.metadata().bounding_box_with_transform(layer, DAffine2::IDENTITY) else {
|
||||
return;
|
||||
};
|
||||
let layer_bounds = document.metadata.transform_to_document(layer) * Quad::from_box(bounds);
|
||||
let screen_bounds = document.metadata.document_to_viewport.inverse() * Quad::from_box([DVec2::ZERO, snap_data.input.viewport_bounds.size()]);
|
||||
let layer_bounds = document.metadata().transform_to_document(layer) * Quad::from_box(bounds);
|
||||
let screen_bounds = document.metadata().document_to_viewport.inverse() * Quad::from_box([DVec2::ZERO, snap_data.input.viewport_bounds.size()]);
|
||||
if quad.intersects(layer_bounds) && screen_bounds.intersects(layer_bounds) {
|
||||
candidates.push(layer);
|
||||
}
|
||||
}
|
||||
|
||||
for layer in LayerNodeIdentifier::ROOT_PARENT.children(&document.metadata) {
|
||||
for layer in LayerNodeIdentifier::ROOT_PARENT.children(document.metadata()) {
|
||||
add_candidates(layer, snap_data, quad, &mut candidates);
|
||||
}
|
||||
|
||||
@@ -330,7 +330,7 @@ impl SnapManager {
|
||||
}
|
||||
|
||||
pub fn draw_overlays(&mut self, snap_data: SnapData, overlay_context: &mut OverlayContext) {
|
||||
let to_viewport = snap_data.document.metadata.document_to_viewport;
|
||||
let to_viewport = snap_data.document.metadata().document_to_viewport;
|
||||
if let Some(ind) = &self.indicator {
|
||||
for curve in &ind.curves {
|
||||
let Some(curve) = curve else { continue };
|
||||
|
||||
@@ -22,13 +22,16 @@ impl LayerSnapper {
|
||||
return;
|
||||
}
|
||||
|
||||
let bounds = if document.metadata.is_artboard(layer) {
|
||||
document.metadata.bounding_box_with_transform(layer, document.metadata.transform_to_document(layer)).map(Quad::from_box)
|
||||
let bounds = if document.network_interface.is_artboard(&layer.to_node(), &[]) {
|
||||
document
|
||||
.metadata()
|
||||
.bounding_box_with_transform(layer, document.metadata().transform_to_document(layer))
|
||||
.map(Quad::from_box)
|
||||
} else {
|
||||
document
|
||||
.metadata
|
||||
.metadata()
|
||||
.bounding_box_with_transform(layer, DAffine2::IDENTITY)
|
||||
.map(|bounds| document.metadata.transform_to_document(layer) * Quad::from_box(bounds))
|
||||
.map(|bounds| document.metadata().transform_to_document(layer) * Quad::from_box(bounds))
|
||||
};
|
||||
let Some(bounds) = bounds else { return };
|
||||
|
||||
@@ -53,21 +56,21 @@ impl LayerSnapper {
|
||||
let document = snap_data.document;
|
||||
self.paths_to_snap.clear();
|
||||
|
||||
for layer in document.metadata.all_layers() {
|
||||
if !document.metadata.is_artboard(layer) || snap_data.ignore.contains(&layer) {
|
||||
for layer in document.metadata().all_layers() {
|
||||
if !document.network_interface.is_artboard(&layer.to_node(), &[]) || snap_data.ignore.contains(&layer) {
|
||||
continue;
|
||||
}
|
||||
self.add_layer_bounds(document, layer, SnapTarget::Board(BoardSnapTarget::Edge));
|
||||
}
|
||||
for &layer in snap_data.get_candidates() {
|
||||
let transform = document.metadata.transform_to_document(layer);
|
||||
let transform = document.metadata().transform_to_document(layer);
|
||||
if !transform.is_finite() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if document.snapping_state.target_enabled(SnapTarget::Geometry(GeometrySnapTarget::Intersection)) || document.snapping_state.target_enabled(SnapTarget::Geometry(GeometrySnapTarget::Path))
|
||||
{
|
||||
for subpath in document.metadata.layer_outline(layer) {
|
||||
for subpath in document.metadata().layer_outline(layer) {
|
||||
for (start_index, curve) in subpath.iter().enumerate() {
|
||||
let document_curve = curve.apply_transformation(|p| transform.transform_point2(p));
|
||||
let start = subpath.manipulator_groups()[start_index].id;
|
||||
@@ -175,13 +178,17 @@ impl LayerSnapper {
|
||||
let document = snap_data.document;
|
||||
self.points_to_snap.clear();
|
||||
|
||||
for layer in document.metadata.all_layers() {
|
||||
if !document.metadata.is_artboard(layer) || snap_data.ignore.contains(&layer) {
|
||||
for layer in document.metadata().all_layers() {
|
||||
if !document.network_interface.is_artboard(&layer.to_node(), &[]) || snap_data.ignore.contains(&layer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if document.snapping_state.target_enabled(SnapTarget::Board(BoardSnapTarget::Corner)) {
|
||||
let Some(bounds) = document.metadata.bounding_box_with_transform(layer, document.metadata.transform_to_document(layer)) else {
|
||||
let Some(bounds) = document
|
||||
.network_interface
|
||||
.document_metadata()
|
||||
.bounding_box_with_transform(layer, document.metadata().transform_to_document(layer))
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
@@ -194,10 +201,10 @@ impl LayerSnapper {
|
||||
if snap_data.ignore_bounds(layer) {
|
||||
continue;
|
||||
}
|
||||
let Some(bounds) = document.metadata.bounding_box_with_transform(layer, DAffine2::IDENTITY) else {
|
||||
let Some(bounds) = document.metadata().bounding_box_with_transform(layer, DAffine2::IDENTITY) else {
|
||||
continue;
|
||||
};
|
||||
let quad = document.metadata.transform_to_document(layer) * Quad::from_box(bounds);
|
||||
let quad = document.metadata().transform_to_document(layer) * Quad::from_box(bounds);
|
||||
let values = BBoxSnapValues::BOUNDING_BOX;
|
||||
get_bbox_points(quad, &mut self.points_to_snap, values, document);
|
||||
}
|
||||
@@ -441,17 +448,17 @@ pub fn are_manipulator_handles_colinear(group: &bezier_rs::ManipulatorGroup<Poin
|
||||
pub fn get_layer_snap_points(layer: LayerNodeIdentifier, snap_data: &SnapData, points: &mut Vec<SnapCandidatePoint>) {
|
||||
let document = snap_data.document;
|
||||
|
||||
if document.metadata().is_artboard(layer) {
|
||||
if document.network_interface.is_artboard(&layer.to_node(), &[]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if document.metadata().is_folder(layer) {
|
||||
if layer.has_children(document.metadata()) {
|
||||
for child in layer.descendants(document.metadata()) {
|
||||
get_layer_snap_points(child, snap_data, points);
|
||||
}
|
||||
} else if document.metadata.layer_outline(layer).next().is_some() {
|
||||
let to_document = document.metadata.transform_to_document(layer);
|
||||
for subpath in document.metadata.layer_outline(layer) {
|
||||
} else if document.metadata().layer_outline(layer).next().is_some() {
|
||||
let to_document = document.metadata().transform_to_document(layer);
|
||||
for subpath in document.metadata().layer_outline(layer) {
|
||||
subpath_anchor_snap_points(layer, subpath, snap_data, points, to_document);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ impl SelectedEdges {
|
||||
}
|
||||
|
||||
if let Some(SizeSnapData { manager, points, snap_data }) = snap {
|
||||
let view_to_doc = snap_data.document.metadata.document_to_viewport.inverse();
|
||||
let view_to_doc = snap_data.document.metadata().document_to_viewport.inverse();
|
||||
let bounds_to_doc = view_to_doc * transform;
|
||||
let mut best_snap = SnappedPoint::infinite_snap(pivot);
|
||||
let mut best_scale_factor = DVec2::ONE;
|
||||
@@ -220,10 +220,10 @@ pub fn axis_align_drag(axis_align: bool, position: DVec2, start: DVec2) -> DVec2
|
||||
pub fn snap_drag(start: DVec2, current: DVec2, axis_align: bool, snap_data: SnapData, snap_manager: &mut SnapManager, candidates: &Vec<SnapCandidatePoint>) -> DVec2 {
|
||||
let mouse_position = axis_align_drag(axis_align, snap_data.input.mouse.position, start);
|
||||
let document = snap_data.document;
|
||||
let total_mouse_delta_document = document.metadata.document_to_viewport.inverse().transform_vector2(mouse_position - start);
|
||||
let mouse_delta_document = document.metadata.document_to_viewport.inverse().transform_vector2(mouse_position - current);
|
||||
let total_mouse_delta_document = document.metadata().document_to_viewport.inverse().transform_vector2(mouse_position - start);
|
||||
let mouse_delta_document = document.metadata().document_to_viewport.inverse().transform_vector2(mouse_position - current);
|
||||
let mut offset = mouse_delta_document;
|
||||
let mut best_snap = SnappedPoint::infinite_snap(document.metadata.document_to_viewport.inverse().transform_point2(mouse_position));
|
||||
let mut best_snap = SnappedPoint::infinite_snap(document.metadata().document_to_viewport.inverse().transform_point2(mouse_position));
|
||||
|
||||
for point in candidates {
|
||||
let mut point = point.clone();
|
||||
@@ -251,7 +251,7 @@ pub fn snap_drag(start: DVec2, current: DVec2, axis_align: bool, snap_data: Snap
|
||||
|
||||
snap_manager.update_indicator(best_snap);
|
||||
|
||||
document.metadata.document_to_viewport.transform_vector2(offset)
|
||||
document.metadata().document_to_viewport.transform_vector2(offset)
|
||||
}
|
||||
|
||||
/// Contains info on the overlays for the bounding box and transform handles
|
||||
|
||||
@@ -10,10 +10,10 @@ pub fn should_extend(document: &DocumentMessageHandler, goal: DVec2, tolerance:
|
||||
let mut best = None;
|
||||
let mut best_distance_squared = tolerance * tolerance;
|
||||
|
||||
for layer in document.selected_nodes.selected_layers(document.metadata()) {
|
||||
for layer in document.network_interface.selected_nodes(&[]).unwrap().selected_layers(document.metadata()) {
|
||||
let viewspace = document.metadata().transform_to_viewport(layer);
|
||||
|
||||
let vector_data = document.metadata.compute_modified_vector(layer, document.network())?;
|
||||
let vector_data = document.metadata().compute_modified_vector(layer, &document.network_interface)?;
|
||||
for id in vector_data.single_connected_points() {
|
||||
let Some(point) = vector_data.point_domain.position_from_id(id) else { continue };
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ use graphene_core::raster::color::Color;
|
||||
|
||||
pub struct ToolMessageData<'a> {
|
||||
pub document_id: DocumentId,
|
||||
pub document: &'a DocumentMessageHandler,
|
||||
pub document: &'a mut DocumentMessageHandler,
|
||||
pub input: &'a InputPreprocessorMessageHandler,
|
||||
pub persistent_data: &'a PersistentData,
|
||||
pub node_graph: &'a NodeGraphExecutor,
|
||||
|
||||
@@ -119,7 +119,7 @@ impl ArtboardToolData {
|
||||
|
||||
let Some(layer) = self.selected_artboard else { return };
|
||||
|
||||
if let Some(bounds) = document.metadata.bounding_box_with_transform(layer, document.metadata.transform_to_document(layer)) {
|
||||
if let Some(bounds) = document.metadata().bounding_box_with_transform(layer, document.metadata().transform_to_document(layer)) {
|
||||
snapping::get_bbox_points(Quad::from_box(bounds), &mut self.snap_candidates, snapping::BBoxSnapValues::ARTBOARD, document);
|
||||
}
|
||||
}
|
||||
@@ -142,9 +142,7 @@ impl ArtboardToolData {
|
||||
}
|
||||
|
||||
fn hovered_artboard(document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler) -> Option<LayerNodeIdentifier> {
|
||||
document
|
||||
.click_xray(input.mouse.position)
|
||||
.find(|&layer| document.network.nodes.get(&layer.to_node()).map_or(false, |document_node| document_node.is_artboard()))
|
||||
document.click_xray(input).find(|&layer| document.network_interface.is_artboard(&layer.to_node(), &[]))
|
||||
}
|
||||
|
||||
fn select_artboard(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) -> bool {
|
||||
@@ -196,7 +194,7 @@ impl ArtboardToolData {
|
||||
let size = (max - min).abs();
|
||||
|
||||
responses.add(GraphOperationMessage::ResizeArtboard {
|
||||
id: self.selected_artboard.unwrap().to_node(),
|
||||
layer: self.selected_artboard.unwrap(),
|
||||
location: position.round().as_ivec2(),
|
||||
dimensions: size.round().as_ivec2(),
|
||||
});
|
||||
@@ -294,7 +292,7 @@ impl Fsm for ArtboardToolFsmState {
|
||||
return ArtboardToolFsmState::Ready { hovered };
|
||||
}
|
||||
responses.add(GraphOperationMessage::ResizeArtboard {
|
||||
id: tool_data.selected_artboard.unwrap().to_node(),
|
||||
layer: tool_data.selected_artboard.unwrap(),
|
||||
location: position.round().as_ivec2(),
|
||||
dimensions: size.round().as_ivec2(),
|
||||
});
|
||||
@@ -350,7 +348,7 @@ impl Fsm for ArtboardToolFsmState {
|
||||
log::error!("Selected artboard cannot be ROOT_PARENT");
|
||||
} else {
|
||||
responses.add(GraphOperationMessage::ResizeArtboard {
|
||||
id: artboard.to_node(),
|
||||
layer: artboard,
|
||||
location: start.min(end).round().as_ivec2(),
|
||||
dimensions: (start.round() - end.round()).abs().as_ivec2(),
|
||||
});
|
||||
@@ -460,12 +458,17 @@ impl Fsm for ArtboardToolFsmState {
|
||||
ArtboardToolFsmState::Ready { hovered }
|
||||
}
|
||||
(_, ArtboardToolMessage::UpdateSelectedArtboard) => {
|
||||
tool_data.selected_artboard = document.selected_nodes.selected_layers(document.metadata()).find(|layer| document.metadata().is_artboard(*layer));
|
||||
tool_data.selected_artboard = document
|
||||
.network_interface
|
||||
.selected_nodes(&[])
|
||||
.unwrap()
|
||||
.selected_layers(document.metadata())
|
||||
.find(|layer| document.network_interface.is_artboard(&layer.to_node(), &[]));
|
||||
self
|
||||
}
|
||||
(_, ArtboardToolMessage::DeleteSelected) => {
|
||||
tool_data.selected_artboard.take();
|
||||
responses.add(NodeGraphMessage::DeleteSelectedNodes { reconnect: true });
|
||||
responses.add(DocumentMessage::DeleteSelectedLayers);
|
||||
|
||||
ArtboardToolFsmState::Ready { hovered }
|
||||
}
|
||||
@@ -475,7 +478,7 @@ impl Fsm for ArtboardToolFsmState {
|
||||
log::error!("Selected artboard cannot be ROOT_PARENT");
|
||||
} else {
|
||||
responses.add(GraphOperationMessage::ResizeArtboard {
|
||||
id: tool_data.selected_artboard.unwrap().to_node(),
|
||||
layer: tool_data.selected_artboard.unwrap(),
|
||||
location: DVec2::new(bounds.bounds[0].x + delta_x, bounds.bounds[0].y + delta_y).round().as_ivec2(),
|
||||
dimensions: (bounds.bounds[1] - bounds.bounds[0]).round().as_ivec2(),
|
||||
});
|
||||
|
||||
@@ -2,10 +2,11 @@ use super::tool_prelude::*;
|
||||
use crate::messages::portfolio::document::graph_operation::transform_utils::{get_current_normalized_pivot, get_current_transform};
|
||||
use crate::messages::portfolio::document::node_graph::document_node_types::resolve_document_node_type;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::FlowType;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{DocumentNodeMetadata, NodeId};
|
||||
use graph_craft::document::NodeId;
|
||||
use graphene_core::raster::BlendMode;
|
||||
use graphene_core::uuid::generate_uuid;
|
||||
use graphene_core::vector::brush_stroke::{BrushInputSample, BrushStroke, BrushStyle};
|
||||
@@ -259,14 +260,20 @@ impl BrushToolData {
|
||||
fn load_existing_strokes(&mut self, document: &DocumentMessageHandler) -> Option<LayerNodeIdentifier> {
|
||||
self.transform = DAffine2::IDENTITY;
|
||||
|
||||
if document.selected_nodes.selected_layers(document.metadata()).count() != 1 {
|
||||
if document.network_interface.selected_nodes(&[]).unwrap().selected_layers(document.metadata()).count() != 1 {
|
||||
return None;
|
||||
}
|
||||
let layer = document.selected_nodes.selected_layers(document.metadata()).next()?;
|
||||
let layer = document.network_interface.selected_nodes(&[]).unwrap().selected_layers(document.metadata()).next()?;
|
||||
|
||||
self.layer = Some(layer);
|
||||
for (node, node_id) in document.network().upstream_flow_back_from_nodes(vec![layer.to_node()], graph_craft::document::FlowType::HorizontalFlow) {
|
||||
if node.name == "Brush" && node_id != layer.to_node() {
|
||||
for node_id in document.network_interface.upstream_flow_back_from_nodes(vec![layer.to_node()], &[], FlowType::HorizontalFlow) {
|
||||
let Some(node) = document.network_interface.network(&[]).unwrap().nodes.get(&node_id) else {
|
||||
continue;
|
||||
};
|
||||
let Some(reference) = document.network_interface.reference(&node_id, &[]) else {
|
||||
continue;
|
||||
};
|
||||
if reference == "Brush" && node_id != layer.to_node() {
|
||||
let points_input = node.inputs.get(2)?;
|
||||
let Some(TaggedValue::BrushStrokes(strokes)) = points_input.as_value() else {
|
||||
continue;
|
||||
@@ -274,7 +281,7 @@ impl BrushToolData {
|
||||
self.strokes.clone_from(strokes);
|
||||
|
||||
return Some(layer);
|
||||
} else if node.name == "Transform" {
|
||||
} else if reference == "Transform" {
|
||||
let upstream = document.metadata().upstream_transform(node_id);
|
||||
let pivot = DAffine2::from_translation(upstream.transform_point2(get_current_normalized_pivot(&node.inputs)));
|
||||
self.transform = pivot * get_current_transform(&node.inputs) * pivot.inverse() * self.transform;
|
||||
@@ -313,7 +320,12 @@ impl Fsm for BrushToolFsmState {
|
||||
tool_data.layer = Some(layer);
|
||||
|
||||
let parent = layer.parent(document.metadata()).unwrap_or_else(|| document.new_layer_parent(true));
|
||||
let parent_transform = document.metadata().transform_to_viewport(parent).inverse().transform_point2(input.mouse.position);
|
||||
let parent_transform = document
|
||||
.network_interface
|
||||
.document_metadata()
|
||||
.transform_to_viewport(parent)
|
||||
.inverse()
|
||||
.transform_point2(input.mouse.position);
|
||||
let layer_position = tool_data.transform.inverse().transform_point2(parent_transform);
|
||||
|
||||
let layer_document_scale = document.metadata().transform_to_document(parent) * tool_data.transform;
|
||||
@@ -351,7 +363,12 @@ impl Fsm for BrushToolFsmState {
|
||||
if let Some(layer) = tool_data.layer {
|
||||
if let Some(stroke) = tool_data.strokes.last_mut() {
|
||||
let parent = layer.parent(document.metadata()).unwrap_or(LayerNodeIdentifier::ROOT_PARENT);
|
||||
let parent_position = document.metadata().transform_to_viewport(parent).inverse().transform_point2(input.mouse.position);
|
||||
let parent_position = document
|
||||
.network_interface
|
||||
.document_metadata()
|
||||
.transform_to_viewport(parent)
|
||||
.inverse()
|
||||
.transform_point2(input.mouse.position);
|
||||
let layer_position = tool_data.transform.inverse().transform_point2(parent_position);
|
||||
|
||||
stroke.trace.push(BrushInputSample { position: layer_position })
|
||||
@@ -407,17 +424,14 @@ impl Fsm for BrushToolFsmState {
|
||||
fn new_brush_layer(document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) -> LayerNodeIdentifier {
|
||||
responses.add(DocumentMessage::DeselectAllLayers);
|
||||
|
||||
let brush_node = resolve_document_node_type("Brush")
|
||||
.expect("Brush node does not exist")
|
||||
.to_document_node_default_inputs([], DocumentNodeMetadata::position((-6, 0)));
|
||||
let brush_node = resolve_document_node_type("Brush").expect("Brush node does not exist").default_node_template();
|
||||
|
||||
let id = NodeId(generate_uuid());
|
||||
responses.add(GraphOperationMessage::NewCustomLayer {
|
||||
id,
|
||||
nodes: HashMap::from([(NodeId(0), brush_node)]),
|
||||
nodes: vec![(NodeId(0), brush_node)],
|
||||
parent: document.new_layer_parent(true),
|
||||
insert_index: -1,
|
||||
alias: String::new(),
|
||||
insert_index: 0,
|
||||
});
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![id] });
|
||||
|
||||
|
||||
@@ -201,15 +201,10 @@ impl Fsm for EllipseToolFsmState {
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
|
||||
// Create a new ellipse vector shape
|
||||
let nodes = {
|
||||
let node_type = resolve_document_node_type("Ellipse").expect("Ellipse node does not exist");
|
||||
let node = node_type.to_document_node_default_inputs(
|
||||
[None, Some(NodeInput::value(TaggedValue::F64(0.5), false)), Some(NodeInput::value(TaggedValue::F64(0.5), false))],
|
||||
Default::default(),
|
||||
);
|
||||
let node_type = resolve_document_node_type("Ellipse").expect("Ellipse node does not exist");
|
||||
let node = node_type.node_template_input_override([None, Some(NodeInput::value(TaggedValue::F64(0.5), false)), Some(NodeInput::value(TaggedValue::F64(0.5), false))]);
|
||||
let nodes = vec![(NodeId(0), node)];
|
||||
|
||||
HashMap::from([(NodeId(0), node)])
|
||||
};
|
||||
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, document.new_layer_parent(true), responses);
|
||||
tool_options.fill.apply_fill(layer, responses);
|
||||
tool_options.stroke.apply_stroke(tool_options.line_weight, layer, responses);
|
||||
|
||||
@@ -87,7 +87,7 @@ impl Fsm for FillToolFsmState {
|
||||
|
||||
match (self, event) {
|
||||
(FillToolFsmState::Ready, color_event) => {
|
||||
let Some(layer_identifier) = document.click(input.mouse.position, &document.network) else {
|
||||
let Some(layer_identifier) = document.click(input) else {
|
||||
return self;
|
||||
};
|
||||
let fill = match color_event {
|
||||
|
||||
@@ -224,12 +224,9 @@ impl Fsm for FreehandToolFsmState {
|
||||
|
||||
let parent = document.new_layer_parent(true);
|
||||
|
||||
let nodes = {
|
||||
let node_type = resolve_document_node_type("Path").expect("Path node does not exist");
|
||||
let node = node_type.to_document_node_default_inputs([], Default::default());
|
||||
|
||||
HashMap::from([(NodeId(0), node)])
|
||||
};
|
||||
let node_type = resolve_document_node_type("Path").expect("Path node does not exist");
|
||||
let node = node_type.default_node_template();
|
||||
let nodes = vec![(NodeId(0), node)];
|
||||
|
||||
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, parent, responses);
|
||||
tool_options.fill.apply_fill(layer, responses);
|
||||
|
||||
@@ -249,8 +249,8 @@ impl Fsm for GradientToolFsmState {
|
||||
(_, GradientToolMessage::Overlays(mut overlay_context)) => {
|
||||
let selected = tool_data.selected_gradient.as_ref();
|
||||
|
||||
for layer in document.selected_nodes.selected_visible_layers(document.metadata()) {
|
||||
let Some(gradient) = get_gradient(layer, &document.network) else { continue };
|
||||
for layer in document.network_interface.selected_nodes(&[]).unwrap().selected_visible_layers(&document.network_interface) {
|
||||
let Some(gradient) = get_gradient(layer, &document.network_interface) else { continue };
|
||||
let transform = gradient_space_transform(layer, document);
|
||||
let dragging = selected
|
||||
.filter(|selected| selected.layer.map_or(false, |selected_layer| selected_layer == layer))
|
||||
@@ -324,8 +324,8 @@ impl Fsm for GradientToolFsmState {
|
||||
self
|
||||
}
|
||||
(_, GradientToolMessage::InsertStop) => {
|
||||
for layer in document.selected_nodes.selected_visible_layers(document.metadata()) {
|
||||
let Some(mut gradient) = get_gradient(layer, &document.network) else { continue };
|
||||
for layer in document.network_interface.selected_nodes(&[]).unwrap().selected_visible_layers(&document.network_interface) {
|
||||
let Some(mut gradient) = get_gradient(layer, &document.network_interface) else { continue };
|
||||
let transform = gradient_space_transform(layer, document);
|
||||
|
||||
let mouse = input.mouse.position;
|
||||
@@ -363,8 +363,8 @@ impl Fsm for GradientToolFsmState {
|
||||
let tolerance = (MANIPULATOR_GROUP_MARKER_SIZE * 2.).powi(2);
|
||||
|
||||
let mut dragging = false;
|
||||
for layer in document.selected_nodes.selected_visible_layers(document.metadata()) {
|
||||
let Some(gradient) = get_gradient(layer, &document.network) else { continue };
|
||||
for layer in document.network_interface.selected_nodes(&[]).unwrap().selected_visible_layers(&document.network_interface) {
|
||||
let Some(gradient) = get_gradient(layer, &document.network_interface) else { continue };
|
||||
let transform = gradient_space_transform(layer, document);
|
||||
|
||||
// Check for dragging step
|
||||
@@ -399,11 +399,11 @@ impl Fsm for GradientToolFsmState {
|
||||
document.backup_nonmut(responses);
|
||||
GradientToolFsmState::Drawing
|
||||
} else {
|
||||
let selected_layer = document.click(input.mouse.position, &document.network);
|
||||
let selected_layer = document.click(input);
|
||||
|
||||
// Apply the gradient to the selected layer
|
||||
if let Some(layer) = selected_layer {
|
||||
if !document.selected_nodes.selected_layers_contains(layer, document.metadata()) {
|
||||
if !document.network_interface.selected_nodes(&[]).unwrap().selected_layers_contains(layer, document.metadata()) {
|
||||
let nodes = vec![layer.to_node()];
|
||||
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes });
|
||||
@@ -412,7 +412,7 @@ impl Fsm for GradientToolFsmState {
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
|
||||
// Use the already existing gradient if it exists
|
||||
let gradient = if let Some(gradient) = get_gradient(layer, &document.network) {
|
||||
let gradient = if let Some(gradient) = get_gradient(layer, &document.network_interface) {
|
||||
gradient.clone()
|
||||
} else {
|
||||
// Generate a new gradient
|
||||
|
||||
@@ -101,7 +101,7 @@ impl Fsm for ImaginateToolFsmState {
|
||||
(ImaginateToolFsmState::Ready, ImaginateToolMessage::DragStart) => {
|
||||
shape_data.start(document, input);
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
shape_data.layer = Some(LayerNodeIdentifier::new(NodeId(generate_uuid()), document.network()));
|
||||
shape_data.layer = Some(LayerNodeIdentifier::new(NodeId(generate_uuid()), &document.network_interface));
|
||||
responses.add(DocumentMessage::DeselectAllLayers);
|
||||
|
||||
// // Utility function to offset the position of each consecutive node
|
||||
@@ -120,7 +120,7 @@ impl Fsm for ImaginateToolFsmState {
|
||||
|
||||
// // Give them a unique ID
|
||||
// let transform_node_id = NodeId(100);
|
||||
let imaginate_node_id = NodeId(101);
|
||||
//let imaginate_node_id = NodeId(101);
|
||||
|
||||
// Create the network based on the Input -> Output passthrough default network
|
||||
// let mut network = new_image_network(16, imaginate_node_id);
|
||||
@@ -134,7 +134,7 @@ impl Fsm for ImaginateToolFsmState {
|
||||
// imaginate_node_id,
|
||||
// imaginate_node_type.to_document_node_default_inputs([Some(NodeInput::node(transform_node_id, 0))], next_pos()),
|
||||
// );
|
||||
responses.add(NodeGraphMessage::ShiftNode { node_id: imaginate_node_id });
|
||||
// responses.add(NodeGraphMessage::ShiftNode { node_id: imaginate_node_id });
|
||||
|
||||
// // Add a layer with a frame to the document
|
||||
// responses.add(Operation::AddFrame {
|
||||
|
||||
@@ -173,25 +173,20 @@ impl Fsm for LineToolFsmState {
|
||||
self
|
||||
}
|
||||
(LineToolFsmState::Ready, LineToolMessage::DragStart) => {
|
||||
let point = SnapCandidatePoint::handle(document.metadata.document_to_viewport.inverse().transform_point2(input.mouse.position));
|
||||
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
|
||||
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, None, false);
|
||||
tool_data.drag_start = snapped.snapped_point_document;
|
||||
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
|
||||
let nodes = {
|
||||
let node_type = resolve_document_node_type("Line").expect("Line node does not exist");
|
||||
let node = node_type.to_document_node_default_inputs(
|
||||
[
|
||||
None,
|
||||
Some(NodeInput::value(TaggedValue::DVec2(DVec2::ZERO), false)),
|
||||
Some(NodeInput::value(TaggedValue::DVec2(DVec2::X), false)),
|
||||
],
|
||||
Default::default(),
|
||||
);
|
||||
let node_type = resolve_document_node_type("Line").expect("Line node does not exist");
|
||||
let node = node_type.node_template_input_override([
|
||||
None,
|
||||
Some(NodeInput::value(TaggedValue::DVec2(DVec2::ZERO), false)),
|
||||
Some(NodeInput::value(TaggedValue::DVec2(DVec2::X), false)),
|
||||
]);
|
||||
let nodes = vec![(NodeId(0), node)];
|
||||
|
||||
HashMap::from([(NodeId(0), node)])
|
||||
};
|
||||
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, document.new_layer_parent(false), responses);
|
||||
tool_options.stroke.apply_stroke(tool_options.line_weight, layer, responses);
|
||||
tool_data.layer = Some(layer);
|
||||
@@ -296,7 +291,7 @@ impl Fsm for LineToolFsmState {
|
||||
}
|
||||
|
||||
fn generate_transform(tool_data: &mut LineToolData, snap_data: SnapData, lock_angle: bool, snap_angle: bool, center: bool) -> Message {
|
||||
let document_to_viewport = snap_data.document.metadata.document_to_viewport;
|
||||
let document_to_viewport = snap_data.document.metadata().document_to_viewport;
|
||||
let mut document_points = [tool_data.drag_start, document_to_viewport.inverse().transform_point2(tool_data.drag_current)];
|
||||
|
||||
let mut angle = -(document_points[1] - document_points[0]).angle_to(DVec2::X);
|
||||
|
||||
@@ -2,12 +2,12 @@ use super::tool_prelude::*;
|
||||
use crate::consts::{COLOR_OVERLAY_YELLOW, DRAG_THRESHOLD, INSERT_POINT_ON_SEGMENT_TOO_FAR_DISTANCE, SELECTION_THRESHOLD, SELECTION_TOLERANCE};
|
||||
use crate::messages::portfolio::document::overlays::utility_functions::path_overlays;
|
||||
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
|
||||
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
|
||||
use crate::messages::tool::common_functionality::shape_editor::{ClosestSegment, ManipulatorAngle, ManipulatorPointInfo, OpposingHandleLengths, SelectedPointsInfo, ShapeState};
|
||||
use crate::messages::tool::common_functionality::snapping::{SnapData, SnapManager};
|
||||
|
||||
use graph_craft::document::NodeNetwork;
|
||||
use graphene_core::renderer::Quad;
|
||||
use graphene_core::vector::ManipulatorPointId;
|
||||
|
||||
@@ -272,10 +272,10 @@ impl PathToolData {
|
||||
PathToolFsmState::InsertPoint
|
||||
}
|
||||
|
||||
fn update_insertion(&mut self, shape_editor: &mut ShapeState, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>, mouse_position: DVec2) -> PathToolFsmState {
|
||||
fn update_insertion(&mut self, shape_editor: &mut ShapeState, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>, input: &InputPreprocessorMessageHandler) -> PathToolFsmState {
|
||||
if let Some(closed_segment) = &mut self.segment {
|
||||
closed_segment.update_closest_point(&document.metadata, mouse_position);
|
||||
if closed_segment.too_far(mouse_position, INSERT_POINT_ON_SEGMENT_TOO_FAR_DISTANCE, &document.metadata) {
|
||||
closed_segment.update_closest_point(document.metadata(), input.mouse.position);
|
||||
if closed_segment.too_far(input.mouse.position, INSERT_POINT_ON_SEGMENT_TOO_FAR_DISTANCE, document.metadata()) {
|
||||
self.end_insertion(shape_editor, responses, InsertEndKind::Abort)
|
||||
} else {
|
||||
PathToolFsmState::InsertPoint
|
||||
@@ -317,13 +317,10 @@ impl PathToolData {
|
||||
self.double_click_handled = false;
|
||||
self.opposing_handle_lengths = None;
|
||||
|
||||
let document_network = document.network();
|
||||
let document_metadata = document.metadata();
|
||||
|
||||
self.drag_start_pos = input.mouse.position;
|
||||
|
||||
// Select the first point within the threshold (in pixels)
|
||||
if let Some(selected_points) = shape_editor.change_point_selection(document_network, document_metadata, input.mouse.position, SELECTION_THRESHOLD, add_to_selection) {
|
||||
if let Some(selected_points) = shape_editor.change_point_selection(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD, add_to_selection) {
|
||||
if let Some(selected_points) = selected_points {
|
||||
self.drag_start_pos = input.mouse.position;
|
||||
self.start_dragging_point(selected_points, input, document, responses);
|
||||
@@ -332,7 +329,7 @@ impl PathToolData {
|
||||
PathToolFsmState::Dragging
|
||||
}
|
||||
// We didn't find a point nearby, so now we'll try to add a point into the closest path segment
|
||||
else if let Some(closed_segment) = shape_editor.upper_closest_segment(document_network, document_metadata, input.mouse.position, SELECTION_TOLERANCE) {
|
||||
else if let Some(closed_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, SELECTION_TOLERANCE) {
|
||||
if direct_insert_without_sliding {
|
||||
self.start_insertion(responses, closed_segment);
|
||||
self.end_insertion(shape_editor, responses, InsertEndKind::Add { shift: add_to_selection })
|
||||
@@ -341,14 +338,14 @@ impl PathToolData {
|
||||
}
|
||||
}
|
||||
// We didn't find a segment path, so consider selecting the nearest shape instead
|
||||
else if let Some(layer) = document.click(input.mouse.position, &document.network) {
|
||||
else if let Some(layer) = document.click(input) {
|
||||
if add_to_selection {
|
||||
responses.add(NodeGraphMessage::SelectedNodesAdd { nodes: vec![layer.to_node()] });
|
||||
} else {
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] });
|
||||
}
|
||||
self.drag_start_pos = input.mouse.position;
|
||||
self.previous_mouse_position = document.metadata.document_to_viewport.inverse().transform_point2(input.mouse.position);
|
||||
self.previous_mouse_position = document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position);
|
||||
shape_editor.select_connected_anchors(document, layer, input.mouse.position);
|
||||
|
||||
PathToolFsmState::Dragging
|
||||
@@ -356,7 +353,7 @@ impl PathToolData {
|
||||
// Start drawing a box
|
||||
else {
|
||||
self.drag_start_pos = input.mouse.position;
|
||||
self.previous_mouse_position = document.metadata.document_to_viewport.inverse().transform_point2(input.mouse.position);
|
||||
self.previous_mouse_position = document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position);
|
||||
|
||||
PathToolFsmState::DrawingBox
|
||||
}
|
||||
@@ -383,7 +380,7 @@ impl PathToolData {
|
||||
}
|
||||
selected_points.points.extend(additional_selected_points);
|
||||
|
||||
let viewport_to_document = document.metadata.document_to_viewport.inverse();
|
||||
let viewport_to_document = document.metadata().document_to_viewport.inverse();
|
||||
self.previous_mouse_position = viewport_to_document.transform_point2(input.mouse.position - selected_points.offset);
|
||||
}
|
||||
|
||||
@@ -397,7 +394,7 @@ impl PathToolData {
|
||||
ManipulatorAngle::Mixed => false,
|
||||
});
|
||||
if colinear {
|
||||
shape_editor.disable_colinear_handles_state_on_selected(&document.metadata, &document.network, responses);
|
||||
shape_editor.disable_colinear_handles_state_on_selected(&document.network_interface, responses);
|
||||
} else {
|
||||
shape_editor.convert_selected_manipulators_to_colinear_handles(responses, document);
|
||||
}
|
||||
@@ -414,11 +411,11 @@ impl PathToolData {
|
||||
|
||||
fn drag(&mut self, equidistant: bool, shape_editor: &mut ShapeState, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
|
||||
// Move the selected points with the mouse
|
||||
let previous_mouse = document.metadata.document_to_viewport.transform_point2(self.previous_mouse_position);
|
||||
let previous_mouse = document.metadata().document_to_viewport.transform_point2(self.previous_mouse_position);
|
||||
let snapped_delta = shape_editor.snap(&mut self.snap_manager, document, input, previous_mouse);
|
||||
let handle_lengths = if equidistant { None } else { self.opposing_handle_lengths.take() };
|
||||
shape_editor.move_selected_points(handle_lengths, document, snapped_delta, equidistant, responses);
|
||||
self.previous_mouse_position += document.metadata.document_to_viewport.inverse().transform_vector2(snapped_delta);
|
||||
self.previous_mouse_position += document.metadata().document_to_viewport.inverse().transform_vector2(snapped_delta);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -435,7 +432,7 @@ impl Fsm for PathToolFsmState {
|
||||
match (self, event) {
|
||||
(_, PathToolMessage::SelectionChanged) => {
|
||||
// Set the newly targeted layers to visible
|
||||
let target_layers = document.selected_nodes.selected_layers(document.metadata()).collect();
|
||||
let target_layers = document.network_interface.selected_nodes(&[]).unwrap().selected_layers(document.metadata()).collect();
|
||||
shape_editor.set_selected_layers(target_layers);
|
||||
|
||||
responses.add(OverlaysMessage::Draw);
|
||||
@@ -454,7 +451,7 @@ impl Fsm for PathToolFsmState {
|
||||
tool_data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context);
|
||||
}
|
||||
Self::InsertPoint => {
|
||||
let state = tool_data.update_insertion(shape_editor, document, responses, input.mouse.position);
|
||||
let state = tool_data.update_insertion(shape_editor, document, responses, input);
|
||||
|
||||
if let Some(closest_segment) = &tool_data.segment {
|
||||
overlay_context.manipulator_anchor(closest_segment.closest_point_to_viewport(), false, Some(COLOR_OVERLAY_YELLOW));
|
||||
@@ -552,7 +549,7 @@ impl Fsm for PathToolFsmState {
|
||||
if tool_data.drag_start_pos == tool_data.previous_mouse_position {
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![] });
|
||||
} else {
|
||||
shape_editor.select_all_in_quad(&document.network, &document.metadata, [tool_data.drag_start_pos, tool_data.previous_mouse_position], !shift_pressed);
|
||||
shape_editor.select_all_in_quad(&document.network_interface, [tool_data.drag_start_pos, tool_data.previous_mouse_position], !shift_pressed);
|
||||
}
|
||||
responses.add(OverlaysMessage::Draw);
|
||||
|
||||
@@ -576,7 +573,7 @@ impl Fsm for PathToolFsmState {
|
||||
if tool_data.drag_start_pos == tool_data.previous_mouse_position {
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![] });
|
||||
} else {
|
||||
shape_editor.select_all_in_quad(&document.network, &document.metadata, [tool_data.drag_start_pos, tool_data.previous_mouse_position], !equidistant);
|
||||
shape_editor.select_all_in_quad(&document.network_interface, [tool_data.drag_start_pos, tool_data.previous_mouse_position], !equidistant);
|
||||
}
|
||||
responses.add(OverlaysMessage::Draw);
|
||||
responses.add(PathToolMessage::SelectedPointUpdated);
|
||||
@@ -586,7 +583,7 @@ impl Fsm for PathToolFsmState {
|
||||
(_, PathToolMessage::DragStop { equidistant }) => {
|
||||
let equidistant = input.keyboard.get(equidistant as usize);
|
||||
|
||||
let nearest_point = shape_editor.find_nearest_point_indices(&document.network, &document.metadata, input.mouse.position, SELECTION_THRESHOLD);
|
||||
let nearest_point = shape_editor.find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD);
|
||||
|
||||
if let Some((layer, nearest_point)) = nearest_point {
|
||||
if tool_data.drag_start_pos.distance(input.mouse.position) <= DRAG_THRESHOLD && !equidistant {
|
||||
@@ -623,7 +620,7 @@ impl Fsm for PathToolFsmState {
|
||||
}
|
||||
(_, PathToolMessage::FlipSmoothSharp) => {
|
||||
if !tool_data.double_click_handled {
|
||||
shape_editor.flip_smooth_sharp(&document.network, &document.metadata, input.mouse.position, SELECTION_TOLERANCE, responses);
|
||||
shape_editor.flip_smooth_sharp(&document.network_interface, input.mouse.position, SELECTION_TOLERANCE, responses);
|
||||
responses.add(PathToolMessage::SelectedPointUpdated);
|
||||
}
|
||||
self
|
||||
@@ -650,18 +647,18 @@ impl Fsm for PathToolFsmState {
|
||||
}
|
||||
(_, PathToolMessage::SelectedPointXChanged { new_x }) => {
|
||||
if let Some(&SingleSelectedPoint { coordinates, id, layer, .. }) = tool_data.selection_status.as_one() {
|
||||
shape_editor.reposition_control_point(&id, &document.network, &document.metadata, DVec2::new(new_x, coordinates.y), layer, responses);
|
||||
shape_editor.reposition_control_point(&id, &document.network_interface, DVec2::new(new_x, coordinates.y), layer, responses);
|
||||
}
|
||||
PathToolFsmState::Ready
|
||||
}
|
||||
(_, PathToolMessage::SelectedPointYChanged { new_y }) => {
|
||||
if let Some(&SingleSelectedPoint { coordinates, id, layer, .. }) = tool_data.selection_status.as_one() {
|
||||
shape_editor.reposition_control_point(&id, &document.network, &document.metadata, DVec2::new(coordinates.x, new_y), layer, responses);
|
||||
shape_editor.reposition_control_point(&id, &document.network_interface, DVec2::new(coordinates.x, new_y), layer, responses);
|
||||
}
|
||||
PathToolFsmState::Ready
|
||||
}
|
||||
(_, PathToolMessage::SelectedPointUpdated) => {
|
||||
tool_data.selection_status = get_selection_status(&document.network, &document.metadata, shape_editor);
|
||||
tool_data.selection_status = get_selection_status(&document.network_interface, shape_editor);
|
||||
self
|
||||
}
|
||||
(_, PathToolMessage::ManipulatorMakeHandlesColinear) => {
|
||||
@@ -673,7 +670,7 @@ impl Fsm for PathToolFsmState {
|
||||
}
|
||||
(_, PathToolMessage::ManipulatorMakeHandlesFree) => {
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
shape_editor.disable_colinear_handles_state_on_selected(&document.metadata, &document.network, responses);
|
||||
shape_editor.disable_colinear_handles_state_on_selected(&document.network_interface, responses);
|
||||
responses.add(DocumentMessage::CommitTransaction);
|
||||
PathToolFsmState::Ready
|
||||
}
|
||||
@@ -776,7 +773,7 @@ struct SingleSelectedPoint {
|
||||
|
||||
/// Sets the cumulative description of the selected points: if `None` are selected, if `One` is selected, or if `Multiple` are selected.
|
||||
/// Applies to any selected points, whether they are anchors or handles; and whether they are from a single shape or across multiple shapes.
|
||||
fn get_selection_status(document_network: &NodeNetwork, document_metadata: &DocumentMetadata, shape_state: &mut ShapeState) -> SelectionStatus {
|
||||
fn get_selection_status(network_interface: &NodeNetworkInterface, shape_state: &mut ShapeState) -> SelectionStatus {
|
||||
let mut selection_layers = shape_state.selected_shape_state.iter().map(|(k, v)| (*k, v.selected_points_count()));
|
||||
let total_selected_points = selection_layers.clone().map(|(_, v)| v).sum::<usize>();
|
||||
|
||||
@@ -785,7 +782,7 @@ fn get_selection_status(document_network: &NodeNetwork, document_metadata: &Docu
|
||||
let Some(layer) = selection_layers.find(|(_, v)| *v > 0).map(|(k, _)| k) else {
|
||||
return SelectionStatus::None;
|
||||
};
|
||||
let Some(vector_data) = document_metadata.compute_modified_vector(layer, document_network) else {
|
||||
let Some(vector_data) = network_interface.document_metadata().compute_modified_vector(layer, network_interface) else {
|
||||
return SelectionStatus::None;
|
||||
};
|
||||
let Some(&point) = shape_state.selected_points().next() else {
|
||||
@@ -795,7 +792,7 @@ fn get_selection_status(document_network: &NodeNetwork, document_metadata: &Docu
|
||||
return SelectionStatus::None;
|
||||
};
|
||||
|
||||
let coordinates = document_metadata.transform_to_document(layer).transform_point2(local_position);
|
||||
let coordinates = network_interface.document_metadata().transform_to_document(layer).transform_point2(local_position);
|
||||
let manipulator_angle = if vector_data.colinear(point) { ManipulatorAngle::Colinear } else { ManipulatorAngle::Free };
|
||||
|
||||
return SelectionStatus::One(SingleSelectedPoint {
|
||||
@@ -809,7 +806,7 @@ fn get_selection_status(document_network: &NodeNetwork, document_metadata: &Docu
|
||||
// Check to see if multiple manipulator groups are selected
|
||||
if total_selected_points > 1 {
|
||||
return SelectionStatus::Multiple(MultipleSelectedPoints {
|
||||
manipulator_angle: shape_state.selected_manipulator_angles(document_network, document_metadata),
|
||||
manipulator_angle: shape_state.selected_manipulator_angles(network_interface),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -241,7 +241,7 @@ impl PenToolData {
|
||||
|
||||
// Break the control
|
||||
let Some(last_pos) = self.latest_point().map(|point| point.pos) else { return };
|
||||
let transform = document.metadata.document_to_viewport * transform;
|
||||
let transform = document.metadata().document_to_viewport * transform;
|
||||
let on_top = transform.transform_point2(self.next_point).distance_squared(transform.transform_point2(last_pos)) < crate::consts::SNAP_POINT_TOLERANCE.powi(2);
|
||||
if on_top {
|
||||
if let Some(point) = self.latest_point_mut() {
|
||||
@@ -269,9 +269,9 @@ impl PenToolData {
|
||||
// Get close path
|
||||
let mut end = None;
|
||||
let layer = self.layer?;
|
||||
let vector_data = document.metadata.compute_modified_vector(layer, &document.network)?;
|
||||
let vector_data = document.metadata().compute_modified_vector(layer, &document.network_interface)?;
|
||||
let start = self.latest_point()?.id;
|
||||
let transform = document.metadata.document_to_viewport * transform;
|
||||
let transform = document.metadata().document_to_viewport * transform;
|
||||
for id in vector_data.single_connected_points().filter(|&point| point != start) {
|
||||
let Some(pos) = vector_data.point_domain.position_from_id(id) else { continue };
|
||||
let transformed_distance_between_squared = transform.transform_point2(pos).distance_squared(transform.transform_point2(next_point));
|
||||
@@ -348,7 +348,7 @@ impl PenToolData {
|
||||
fn compute_snapped_angle(&mut self, snap_data: SnapData, transform: DAffine2, colinear: bool, mouse: DVec2, relative: Option<DVec2>, neighbor: bool) -> DVec2 {
|
||||
let ModifierState { snap_angle, lock_angle, .. } = self.modifiers;
|
||||
let document = snap_data.document;
|
||||
let mut document_pos = document.metadata.document_to_viewport.inverse().transform_point2(mouse);
|
||||
let mut document_pos = document.metadata().document_to_viewport.inverse().transform_point2(mouse);
|
||||
let snap = &mut self.snap_manager;
|
||||
|
||||
let neighbors = relative.filter(|_| neighbor).map_or(Vec::new(), |neighbor| vec![neighbor]);
|
||||
@@ -455,7 +455,7 @@ impl Fsm for PenToolFsmState {
|
||||
self
|
||||
}
|
||||
(_, PenToolMessage::Overlays(mut overlay_context)) => {
|
||||
let transform = document.metadata.document_to_viewport * transform;
|
||||
let transform = document.metadata().document_to_viewport * transform;
|
||||
if let (Some((start, handle_start)), Some(handle_end)) = (tool_data.latest_point().map(|point| (point.pos, point.handle_start)), tool_data.handle_end) {
|
||||
let handles = BezierHandles::Cubic { handle_start, handle_end };
|
||||
let bezier = Bezier {
|
||||
@@ -508,9 +508,9 @@ impl Fsm for PenToolFsmState {
|
||||
(PenToolFsmState::Ready, PenToolMessage::DragStart) => {
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
|
||||
let point = SnapCandidatePoint::handle(document.metadata.document_to_viewport.inverse().transform_point2(input.mouse.position));
|
||||
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
|
||||
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, None, false);
|
||||
let viewport = document.metadata.document_to_viewport.transform_point2(snapped.snapped_point_document);
|
||||
let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
|
||||
|
||||
// Perform extension of an existing path
|
||||
if let Some((layer, point, position)) = should_extend(document, viewport, crate::consts::SNAP_POINT_TOLERANCE) {
|
||||
@@ -525,10 +525,8 @@ impl Fsm for PenToolFsmState {
|
||||
tool_data.next_handle_start = position;
|
||||
} else {
|
||||
// New path layer
|
||||
let nodes = {
|
||||
let node_type = resolve_document_node_type("Path").expect("Path node does not exist");
|
||||
HashMap::from([(NodeId(0), node_type.to_document_node_default_inputs([], Default::default()))])
|
||||
};
|
||||
let node_type = resolve_document_node_type("Path").expect("Path node does not exist");
|
||||
let nodes = vec![(NodeId(0), node_type.default_node_template())];
|
||||
|
||||
let parent = document.new_layer_parent(true);
|
||||
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, parent, responses);
|
||||
|
||||
@@ -244,31 +244,24 @@ impl Fsm for PolygonToolFsmState {
|
||||
polygon_data.start(document, input);
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
|
||||
let nodes = {
|
||||
let node = match tool_options.polygon_type {
|
||||
PolygonType::Convex => resolve_document_node_type("Regular Polygon")
|
||||
.expect("Regular Polygon node does not exist")
|
||||
.to_document_node_default_inputs(
|
||||
[
|
||||
None,
|
||||
Some(NodeInput::value(TaggedValue::U32(tool_options.vertices), false)),
|
||||
Some(NodeInput::value(TaggedValue::F64(0.5), false)),
|
||||
],
|
||||
Default::default(),
|
||||
),
|
||||
PolygonType::Star => resolve_document_node_type("Star").expect("Star node does not exist").to_document_node_default_inputs(
|
||||
[
|
||||
None,
|
||||
Some(NodeInput::value(TaggedValue::U32(tool_options.vertices), false)),
|
||||
Some(NodeInput::value(TaggedValue::F64(0.5), false)),
|
||||
Some(NodeInput::value(TaggedValue::F64(0.25), false)),
|
||||
],
|
||||
Default::default(),
|
||||
),
|
||||
};
|
||||
|
||||
HashMap::from([(NodeId(0), node)])
|
||||
let node = match tool_options.polygon_type {
|
||||
PolygonType::Convex => resolve_document_node_type("Regular Polygon")
|
||||
.expect("Regular Polygon node does not exist")
|
||||
.node_template_input_override([
|
||||
None,
|
||||
Some(NodeInput::value(TaggedValue::U32(tool_options.vertices), false)),
|
||||
Some(NodeInput::value(TaggedValue::F64(0.5), false)),
|
||||
]),
|
||||
PolygonType::Star => resolve_document_node_type("Star").expect("Star node does not exist").node_template_input_override([
|
||||
None,
|
||||
Some(NodeInput::value(TaggedValue::U32(tool_options.vertices), false)),
|
||||
Some(NodeInput::value(TaggedValue::F64(0.5), false)),
|
||||
Some(NodeInput::value(TaggedValue::F64(0.25), false)),
|
||||
]),
|
||||
};
|
||||
|
||||
let nodes = vec![(NodeId(0), node)];
|
||||
|
||||
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, document.new_layer_parent(false), responses);
|
||||
tool_options.fill.apply_fill(layer, responses);
|
||||
tool_options.stroke.apply_stroke(tool_options.line_weight, layer, responses);
|
||||
|
||||
@@ -207,15 +207,10 @@ impl Fsm for RectangleToolFsmState {
|
||||
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
|
||||
let nodes = {
|
||||
let node_type = resolve_document_node_type("Rectangle").expect("Rectangle node does not exist");
|
||||
let node = node_type.to_document_node_default_inputs(
|
||||
[None, Some(NodeInput::value(TaggedValue::F64(1.), false)), Some(NodeInput::value(TaggedValue::F64(1.), false))],
|
||||
Default::default(),
|
||||
);
|
||||
let node_type = resolve_document_node_type("Rectangle").expect("Rectangle node does not exist");
|
||||
let node = node_type.node_template_input_override([None, Some(NodeInput::value(TaggedValue::F64(1.), false)), Some(NodeInput::value(TaggedValue::F64(1.), false))]);
|
||||
let nodes = vec![(NodeId(0), node)];
|
||||
|
||||
HashMap::from([(NodeId(0), node)])
|
||||
};
|
||||
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, document.new_layer_parent(true), responses);
|
||||
tool_options.fill.apply_fill(layer, responses);
|
||||
tool_options.stroke.apply_stroke(tool_options.line_weight, layer, responses);
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::messages::portfolio::document::graph_operation::utility_types::Transf
|
||||
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis};
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, NodeNetworkInterface, NodeTemplate};
|
||||
use crate::messages::portfolio::document::utility_types::transformation::Selected;
|
||||
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils::is_layer_fed_by_node_of_name;
|
||||
@@ -15,7 +16,7 @@ use crate::messages::tool::common_functionality::pivot::Pivot;
|
||||
use crate::messages::tool::common_functionality::snapping::{self, SnapCandidatePoint, SnapData, SnapManager};
|
||||
use crate::messages::tool::common_functionality::transformation_cage::*;
|
||||
|
||||
use graph_craft::document::{DocumentNode, NodeId, NodeNetwork};
|
||||
use graph_craft::document::NodeId;
|
||||
use graphene_core::renderer::Quad;
|
||||
use graphene_std::vector::misc::BooleanOperation;
|
||||
|
||||
@@ -288,8 +289,8 @@ impl SelectToolData {
|
||||
if (self.snap_candidates.len() as f64) < document.snapping_state.tolerance {
|
||||
snapping::get_layer_snap_points(layer, &SnapData::new(document, input), &mut self.snap_candidates);
|
||||
}
|
||||
if let Some(bounds) = document.metadata.bounding_box_with_transform(layer, DAffine2::IDENTITY) {
|
||||
let quad = document.metadata.transform_to_document(layer) * Quad::from_box(bounds);
|
||||
if let Some(bounds) = document.metadata().bounding_box_with_transform(layer, DAffine2::IDENTITY) {
|
||||
let quad = document.metadata().transform_to_document(layer) * Quad::from_box(bounds);
|
||||
snapping::get_bbox_points(quad, &mut self.snap_candidates, snapping::BBoxSnapValues::BOUNDING_BOX, document);
|
||||
}
|
||||
}
|
||||
@@ -310,20 +311,11 @@ impl SelectToolData {
|
||||
}
|
||||
|
||||
/// Duplicates the currently dragging layers. Called when Alt is pressed and the layers have not yet been duplicated.
|
||||
fn start_duplicates(&mut self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
||||
fn start_duplicates(&mut self, document: &mut DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
||||
self.non_duplicated_layers = Some(self.layers_dragging.clone());
|
||||
let mut new_dragging = Vec::new();
|
||||
for layer_ancestors in document.metadata().shallowest_unique_layers(self.layers_dragging.iter().copied().rev()) {
|
||||
let Some(layer) = layer_ancestors.last().copied() else { continue };
|
||||
|
||||
// `layer` cannot be `ROOT_PARENT`, since `ROOT_PARENT` cannot be part of `layers_dragging`
|
||||
if layer == LayerNodeIdentifier::ROOT_PARENT {
|
||||
log::error!("ROOT_PARENT cannot be in layers_dragging");
|
||||
continue;
|
||||
}
|
||||
|
||||
// `parent` can be `ROOT_PARENT`
|
||||
let Some(parent) = layer.parent(&document.metadata) else { continue };
|
||||
for layer in document.network_interface.shallowest_unique_layers(&[]) {
|
||||
let Some(parent) = layer.parent(document.metadata()) else { continue };
|
||||
|
||||
// Moves the layer back to its starting position.
|
||||
responses.add(GraphOperationMessage::TransformChange {
|
||||
@@ -335,36 +327,32 @@ impl SelectToolData {
|
||||
|
||||
// Copy the layer
|
||||
let mut copy_ids = HashMap::new();
|
||||
let node = layer.to_node();
|
||||
copy_ids.insert(node, NodeId(0_u64));
|
||||
if let Some(input_node) = document
|
||||
.network()
|
||||
.nodes
|
||||
.get(&node)
|
||||
.and_then(|node| if node.is_layer { node.inputs.get(1) } else { node.inputs.first() })
|
||||
.and_then(|input| input.as_node())
|
||||
{
|
||||
document
|
||||
.network()
|
||||
.upstream_flow_back_from_nodes(vec![input_node], graph_craft::document::FlowType::UpstreamFlow)
|
||||
.enumerate()
|
||||
.for_each(|(index, (_, node_id))| {
|
||||
copy_ids.insert(node_id, NodeId((index + 1) as u64));
|
||||
});
|
||||
};
|
||||
let nodes: HashMap<NodeId, DocumentNode> =
|
||||
NodeGraphMessageHandler::copy_nodes(document.network(), &document.node_graph_handler.network, &document.node_graph_handler.resolved_types, ©_ids).collect();
|
||||
let node_id = layer.to_node();
|
||||
copy_ids.insert(node_id, NodeId(0));
|
||||
|
||||
let insert_index = DocumentMessageHandler::get_calculated_insert_index(&document.metadata, &document.selected_nodes, parent);
|
||||
document
|
||||
.network_interface
|
||||
.upstream_flow_back_from_nodes(vec![layer.to_node()], &[], FlowType::LayerChildrenUpstreamFlow)
|
||||
.enumerate()
|
||||
.for_each(|(index, node_id)| {
|
||||
copy_ids.insert(node_id, NodeId((index + 1) as u64));
|
||||
});
|
||||
|
||||
let new_ids: HashMap<_, _> = nodes.iter().map(|(&id, _)| (id, NodeId(generate_uuid()))).collect();
|
||||
let nodes = document.network_interface.copy_nodes(©_ids, &[]).collect::<Vec<(NodeId, NodeTemplate)>>();
|
||||
|
||||
let insert_index = DocumentMessageHandler::get_calculated_insert_index(document.metadata(), document.network_interface.selected_nodes(&[]).unwrap(), parent);
|
||||
|
||||
let new_ids: HashMap<_, _> = nodes.iter().map(|(id, _)| (*id, NodeId(generate_uuid()))).collect();
|
||||
|
||||
let layer_id = *new_ids.get(&NodeId(0)).expect("Node Id 0 should be a layer");
|
||||
responses.add(GraphOperationMessage::AddNodesAsChild { nodes, new_ids, parent, insert_index });
|
||||
new_dragging.push(LayerNodeIdentifier::new_unchecked(layer_id));
|
||||
let layer = LayerNodeIdentifier::new_unchecked(layer_id);
|
||||
new_dragging.push(layer);
|
||||
responses.add(NodeGraphMessage::AddNodes { nodes, new_ids });
|
||||
responses.add(NodeGraphMessage::MoveLayerToStack { layer, parent, insert_index });
|
||||
}
|
||||
let nodes = new_dragging.iter().map(|layer| layer.to_node()).collect();
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes });
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
self.layers_dragging = new_dragging;
|
||||
}
|
||||
|
||||
@@ -375,12 +363,7 @@ impl SelectToolData {
|
||||
};
|
||||
|
||||
// Delete the duplicated layers
|
||||
for layer_ancestors in document.metadata().shallowest_unique_layers(self.layers_dragging.iter().copied()) {
|
||||
let layer = layer_ancestors.last().unwrap();
|
||||
if *layer == LayerNodeIdentifier::ROOT_PARENT {
|
||||
log::error!("ROOT_PARENT cannot be in layers_dragging");
|
||||
continue;
|
||||
}
|
||||
for layer in document.network_interface.shallowest_unique_layers(&[]) {
|
||||
responses.add(NodeGraphMessage::DeleteNodes {
|
||||
node_ids: vec![layer.to_node()],
|
||||
reconnect: true,
|
||||
@@ -425,19 +408,26 @@ impl Fsm for SelectToolFsmState {
|
||||
(_, SelectToolMessage::Overlays(mut overlay_context)) => {
|
||||
tool_data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context);
|
||||
|
||||
let selected_layers_count = document.selected_nodes.selected_unlocked_layers(document.metadata()).count();
|
||||
let selected_layers_count = document.network_interface.selected_nodes(&[]).unwrap().selected_unlocked_layers(&document.network_interface).count();
|
||||
tool_data.selected_layers_changed = selected_layers_count != tool_data.selected_layers_count;
|
||||
tool_data.selected_layers_count = selected_layers_count;
|
||||
|
||||
// Outline selected layers
|
||||
for layer in document.selected_nodes.selected_visible_and_unlocked_layers(document.metadata()) {
|
||||
for layer in document
|
||||
.network_interface
|
||||
.selected_nodes(&[])
|
||||
.unwrap()
|
||||
.selected_visible_and_unlocked_layers(&document.network_interface)
|
||||
{
|
||||
overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer));
|
||||
}
|
||||
|
||||
// Update bounds
|
||||
let transform = document
|
||||
.selected_nodes
|
||||
.selected_visible_and_unlocked_layers(document.metadata())
|
||||
.network_interface
|
||||
.selected_nodes(&[])
|
||||
.unwrap()
|
||||
.selected_visible_and_unlocked_layers(&document.network_interface)
|
||||
.next()
|
||||
.map(|layer| document.metadata().transform_to_viewport(layer));
|
||||
let transform = transform.unwrap_or(DAffine2::IDENTITY);
|
||||
@@ -445,8 +435,10 @@ impl Fsm for SelectToolFsmState {
|
||||
return self;
|
||||
}
|
||||
let bounds = document
|
||||
.selected_nodes
|
||||
.selected_visible_and_unlocked_layers(document.metadata())
|
||||
.network_interface
|
||||
.selected_nodes(&[])
|
||||
.unwrap()
|
||||
.selected_visible_and_unlocked_layers(&document.network_interface)
|
||||
.filter_map(|layer| {
|
||||
document
|
||||
.metadata()
|
||||
@@ -473,16 +465,17 @@ impl Fsm for SelectToolFsmState {
|
||||
let quad = Quad::from_box([tool_data.drag_start, tool_data.drag_current]);
|
||||
|
||||
// Draw outline visualizations on the layers to be selected
|
||||
for layer in document.intersect_quad(quad, &document.network) {
|
||||
for layer in document.intersect_quad(quad, input) {
|
||||
overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer));
|
||||
}
|
||||
|
||||
// Update the selection box
|
||||
overlay_context.quad(quad);
|
||||
} else {
|
||||
// Only highlight layers if the viewport is not being panned (middle mouse button is pressed)
|
||||
} else if !input.keyboard.get(Key::Mmb as usize) {
|
||||
// Get the layer the user is hovering over
|
||||
let click = document.click(input.mouse.position, &document.network);
|
||||
let not_selected_click = click.filter(|&hovered_layer| !document.selected_nodes.selected_layers_contains(hovered_layer, document.metadata()));
|
||||
let click = document.click(input);
|
||||
let not_selected_click = click.filter(|&hovered_layer| !document.network_interface.selected_nodes(&[]).unwrap().selected_layers_contains(hovered_layer, document.metadata()));
|
||||
if let Some(layer) = not_selected_click {
|
||||
overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer));
|
||||
}
|
||||
@@ -492,10 +485,10 @@ impl Fsm for SelectToolFsmState {
|
||||
}
|
||||
(_, SelectToolMessage::EditLayer) => {
|
||||
// Edit the clicked layer
|
||||
if let Some(intersect) = document.click(input.mouse.position, &document.network) {
|
||||
if let Some(intersect) = document.click(input) {
|
||||
match tool_data.nested_selection_behavior {
|
||||
NestedSelectionBehavior::Shallowest => edit_layer_shallowest_manipulation(document, intersect, responses),
|
||||
NestedSelectionBehavior::Deepest => edit_layer_deepest_manipulation(intersect, &document.network, responses),
|
||||
NestedSelectionBehavior::Deepest => edit_layer_deepest_manipulation(intersect, &document.network_interface, responses),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,9 +516,14 @@ impl Fsm for SelectToolFsmState {
|
||||
.map(|bounding_box| bounding_box.check_rotate(input.mouse.position))
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut selected: Vec<_> = document.selected_nodes.selected_visible_and_unlocked_layers(document.metadata()).collect();
|
||||
let intersection_list = document.click_list(input.mouse.position, &document.network).collect::<Vec<_>>();
|
||||
let intersection = document.find_deepest(&intersection_list, &document.network);
|
||||
let mut selected: Vec<_> = document
|
||||
.network_interface
|
||||
.selected_nodes(&[])
|
||||
.unwrap()
|
||||
.selected_visible_and_unlocked_layers(&document.network_interface)
|
||||
.collect();
|
||||
let intersection_list = document.click_list(input).collect::<Vec<_>>();
|
||||
let intersection = document.find_deepest(&intersection_list);
|
||||
|
||||
// If the user is dragging the bounding box bounds, go into ResizingBounds mode.
|
||||
// If the user is dragging the rotate trigger, go into RotatingBounds mode.
|
||||
@@ -554,7 +552,7 @@ impl Fsm for SelectToolFsmState {
|
||||
|
||||
tool_data.layers_dragging.retain(|layer| {
|
||||
if *layer != LayerNodeIdentifier::ROOT_PARENT {
|
||||
document.network.nodes.contains_key(&layer.to_node())
|
||||
document.network_interface.network(&[]).unwrap().nodes.contains_key(&layer.to_node())
|
||||
} else {
|
||||
log::error!("ROOT_PARENT should not be part of layers_dragging");
|
||||
false
|
||||
@@ -566,8 +564,7 @@ impl Fsm for SelectToolFsmState {
|
||||
&mut bounds.center_of_transformation,
|
||||
&tool_data.layers_dragging,
|
||||
responses,
|
||||
&document.network,
|
||||
&document.metadata,
|
||||
&document.network_interface,
|
||||
None,
|
||||
&ToolType::Select,
|
||||
);
|
||||
@@ -584,7 +581,7 @@ impl Fsm for SelectToolFsmState {
|
||||
if let Some(bounds) = &mut tool_data.bounding_box_manager {
|
||||
tool_data.layers_dragging.retain(|layer| {
|
||||
if *layer != LayerNodeIdentifier::ROOT_PARENT {
|
||||
document.network.nodes.contains_key(&layer.to_node())
|
||||
document.network_interface.network(&[]).unwrap().nodes.contains_key(&layer.to_node())
|
||||
} else {
|
||||
log::error!("ROOT_PARENT should not be part of layers_dragging");
|
||||
false
|
||||
@@ -595,8 +592,7 @@ impl Fsm for SelectToolFsmState {
|
||||
&mut bounds.center_of_transformation,
|
||||
&selected,
|
||||
responses,
|
||||
&document.network,
|
||||
&document.metadata,
|
||||
&document.network_interface,
|
||||
None,
|
||||
&ToolType::Select,
|
||||
);
|
||||
@@ -615,7 +611,7 @@ impl Fsm for SelectToolFsmState {
|
||||
if tool_data.nested_selection_behavior == NestedSelectionBehavior::Deepest {
|
||||
tool_data.select_single_layer = intersection;
|
||||
} else {
|
||||
tool_data.select_single_layer = intersection.and_then(|intersection| intersection.ancestors(&document.metadata).find(|ancestor| selected.contains(ancestor)));
|
||||
tool_data.select_single_layer = intersection.and_then(|intersection| intersection.ancestors(document.metadata()).find(|ancestor| selected.contains(ancestor)));
|
||||
}
|
||||
|
||||
tool_data.layers_dragging = selected;
|
||||
@@ -686,9 +682,9 @@ impl Fsm for SelectToolFsmState {
|
||||
let mouse_delta = snap_drag(start, current, axis_align, snap_data, &mut tool_data.snap_manager, &tool_data.snap_candidates);
|
||||
|
||||
// TODO: Cache the result of `shallowest_unique_layers` to avoid this heavy computation every frame of movement, see https://github.com/GraphiteEditor/Graphite/pull/481
|
||||
for layer_ancestors in document.metadata().shallowest_unique_layers(tool_data.layers_dragging.iter().copied()) {
|
||||
for layer in document.network_interface.shallowest_unique_layers(&[]) {
|
||||
responses.add_front(GraphOperationMessage::TransformChange {
|
||||
layer: *layer_ancestors.last().unwrap(),
|
||||
layer,
|
||||
transform: DAffine2::from_translation(mouse_delta),
|
||||
transform_in: TransformIn::Viewport,
|
||||
skip_rerender: false,
|
||||
@@ -724,23 +720,14 @@ impl Fsm for SelectToolFsmState {
|
||||
|
||||
tool_data.layers_dragging.retain(|layer| {
|
||||
if *layer != LayerNodeIdentifier::ROOT_PARENT {
|
||||
document.network.nodes.contains_key(&layer.to_node())
|
||||
document.network_interface.network(&[]).unwrap().nodes.contains_key(&layer.to_node())
|
||||
} else {
|
||||
log::error!("ROOT_PARENT should not be part of layers_dragging");
|
||||
false
|
||||
}
|
||||
});
|
||||
let selected = &tool_data.layers_dragging;
|
||||
let mut selected = Selected::new(
|
||||
&mut bounds.original_transforms,
|
||||
&mut pivot,
|
||||
selected,
|
||||
responses,
|
||||
&document.network,
|
||||
&document.metadata,
|
||||
None,
|
||||
&ToolType::Select,
|
||||
);
|
||||
let mut selected = Selected::new(&mut bounds.original_transforms, &mut pivot, selected, responses, &document.network_interface, None, &ToolType::Select);
|
||||
|
||||
selected.apply_transformation(bounds.original_bound_transform * transformation * bounds.original_bound_transform.inverse());
|
||||
|
||||
@@ -774,7 +761,7 @@ impl Fsm for SelectToolFsmState {
|
||||
|
||||
tool_data.layers_dragging.retain(|layer| {
|
||||
if *layer != LayerNodeIdentifier::ROOT_PARENT {
|
||||
document.network().nodes.contains_key(&layer.to_node())
|
||||
document.network_interface.network(&[]).unwrap().nodes.contains_key(&layer.to_node())
|
||||
} else {
|
||||
log::error!("ROOT_PARENT should not be part of replacement_selected_layers");
|
||||
false
|
||||
@@ -785,8 +772,7 @@ impl Fsm for SelectToolFsmState {
|
||||
&mut bounds.center_of_transformation,
|
||||
&tool_data.layers_dragging,
|
||||
responses,
|
||||
&document.network,
|
||||
&document.metadata,
|
||||
&document.network_interface,
|
||||
None,
|
||||
&ToolType::Select,
|
||||
);
|
||||
@@ -902,11 +888,13 @@ impl Fsm for SelectToolFsmState {
|
||||
// Deselect layer if not snap dragging
|
||||
if !tool_data.has_dragged && input.keyboard.key(remove_from_selection) && tool_data.layer_selected_on_start.is_none() {
|
||||
let quad = tool_data.selection_quad();
|
||||
let intersection = document.intersect_quad(quad, &document.network);
|
||||
let intersection = document.intersect_quad(quad, input);
|
||||
|
||||
if let Some(path) = intersection.last() {
|
||||
let replacement_selected_layers: Vec<_> = document
|
||||
.selected_nodes
|
||||
.network_interface
|
||||
.selected_nodes(&[])
|
||||
.unwrap()
|
||||
.selected_layers(document.metadata())
|
||||
.filter(|&layer| !path.starts_with(layer, document.metadata()))
|
||||
.collect();
|
||||
@@ -994,8 +982,8 @@ impl Fsm for SelectToolFsmState {
|
||||
}
|
||||
(SelectToolFsmState::DrawingBox { .. }, SelectToolMessage::DragStop { .. } | SelectToolMessage::Enter) => {
|
||||
let quad = tool_data.selection_quad();
|
||||
let new_selected: HashSet<_> = document.intersect_quad(quad, &document.network).collect();
|
||||
let current_selected: HashSet<_> = document.selected_nodes.selected_layers(document.metadata()).collect();
|
||||
let new_selected: HashSet<_> = document.intersect_quad(quad, input).collect();
|
||||
let current_selected: HashSet<_> = document.network_interface.selected_nodes(&[]).unwrap().selected_layers(document.metadata()).collect();
|
||||
if new_selected != current_selected {
|
||||
tool_data.layers_dragging = new_selected.into_iter().collect();
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
@@ -1020,11 +1008,11 @@ impl Fsm for SelectToolFsmState {
|
||||
SelectToolFsmState::Ready { selection }
|
||||
}
|
||||
(SelectToolFsmState::Ready { .. }, SelectToolMessage::Enter) => {
|
||||
let mut selected_layers = document.selected_nodes.selected_layers(document.metadata());
|
||||
let mut selected_layers = document.network_interface.selected_nodes(&[]).unwrap().selected_layers(document.metadata());
|
||||
|
||||
if let Some(layer) = selected_layers.next() {
|
||||
// Check that only one layer is selected
|
||||
if selected_layers.next().is_none() && is_layer_fed_by_node_of_name(layer, &document.network, "Text") {
|
||||
if selected_layers.next().is_none() && is_layer_fed_by_node_of_name(layer, &document.network_interface, "Text") {
|
||||
responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Text });
|
||||
responses.add(TextToolMessage::EditSelected);
|
||||
}
|
||||
@@ -1044,7 +1032,7 @@ impl Fsm for SelectToolFsmState {
|
||||
(_, SelectToolMessage::Abort) => {
|
||||
tool_data.layers_dragging.retain(|layer| {
|
||||
if *layer != LayerNodeIdentifier::ROOT_PARENT {
|
||||
document.network().nodes.contains_key(&layer.to_node())
|
||||
document.network_interface.network(&[]).unwrap().nodes.contains_key(&layer.to_node())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@@ -1055,8 +1043,7 @@ impl Fsm for SelectToolFsmState {
|
||||
&mut bounding_box_overlays.opposite_pivot,
|
||||
&tool_data.layers_dragging,
|
||||
responses,
|
||||
&document.network,
|
||||
&document.metadata,
|
||||
&document.network_interface,
|
||||
None,
|
||||
&ToolType::Select,
|
||||
);
|
||||
@@ -1155,7 +1142,7 @@ impl Fsm for SelectToolFsmState {
|
||||
}
|
||||
|
||||
fn not_artboard(document: &DocumentMessageHandler) -> impl Fn(&LayerNodeIdentifier) -> bool + '_ {
|
||||
|&layer| !document.metadata.is_artboard(layer)
|
||||
|&layer| !document.network_interface.is_artboard(&layer.to_node(), &[])
|
||||
}
|
||||
|
||||
fn drag_shallowest_manipulation(responses: &mut VecDeque<Message>, selected: Vec<LayerNodeIdentifier>, tool_data: &mut SelectToolData, document: &DocumentMessageHandler) {
|
||||
@@ -1163,7 +1150,7 @@ fn drag_shallowest_manipulation(responses: &mut VecDeque<Message>, selected: Vec
|
||||
let ancestor = layer
|
||||
.ancestors(document.metadata())
|
||||
.filter(not_artboard(document))
|
||||
.find(|&ancestor| document.selected_nodes.selected_layers_contains(ancestor, document.metadata()));
|
||||
.find(|&ancestor| document.network_interface.selected_nodes(&[]).unwrap().selected_layers_contains(ancestor, document.metadata()));
|
||||
|
||||
let new_selected = ancestor.unwrap_or_else(|| {
|
||||
layer
|
||||
@@ -1194,12 +1181,10 @@ fn drag_shallowest_manipulation(responses: &mut VecDeque<Message>, selected: Vec
|
||||
}
|
||||
|
||||
fn drag_deepest_manipulation(responses: &mut VecDeque<Message>, selected: Vec<LayerNodeIdentifier>, tool_data: &mut SelectToolData, document: &DocumentMessageHandler) {
|
||||
tool_data
|
||||
.layers_dragging
|
||||
.append(&mut vec![document.find_deepest(&selected, &document.network).unwrap_or(LayerNodeIdentifier::new(
|
||||
document.network.get_root_node().expect("Root node should exist when dragging layers").id,
|
||||
&document.network,
|
||||
))]);
|
||||
tool_data.layers_dragging.append(&mut vec![document.find_deepest(&selected).unwrap_or(LayerNodeIdentifier::new(
|
||||
document.network_interface.root_node(&[]).expect("Root node should exist when dragging layers").node_id,
|
||||
&document.network_interface,
|
||||
))]);
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet {
|
||||
nodes: tool_data
|
||||
.layers_dragging
|
||||
@@ -1217,7 +1202,7 @@ fn drag_deepest_manipulation(responses: &mut VecDeque<Message>, selected: Vec<La
|
||||
}
|
||||
|
||||
fn edit_layer_shallowest_manipulation(document: &DocumentMessageHandler, layer: LayerNodeIdentifier, responses: &mut VecDeque<Message>) {
|
||||
if document.selected_nodes.selected_layers_contains(layer, document.metadata()) {
|
||||
if document.network_interface.selected_nodes(&[]).unwrap().selected_layers_contains(layer, document.metadata()) {
|
||||
responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Path });
|
||||
return;
|
||||
}
|
||||
@@ -1225,7 +1210,7 @@ fn edit_layer_shallowest_manipulation(document: &DocumentMessageHandler, layer:
|
||||
let Some(new_selected) = layer.ancestors(document.metadata()).filter(not_artboard(document)).find(|ancestor| {
|
||||
ancestor
|
||||
.parent(document.metadata())
|
||||
.is_some_and(|parent| document.selected_nodes.selected_layers_contains(parent, document.metadata()))
|
||||
.is_some_and(|parent| document.network_interface.selected_nodes(&[]).unwrap().selected_layers_contains(parent, document.metadata()))
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
@@ -1238,11 +1223,11 @@ fn edit_layer_shallowest_manipulation(document: &DocumentMessageHandler, layer:
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![new_selected.to_node()] });
|
||||
}
|
||||
|
||||
fn edit_layer_deepest_manipulation(layer: LayerNodeIdentifier, document_network: &NodeNetwork, responses: &mut VecDeque<Message>) {
|
||||
if is_layer_fed_by_node_of_name(layer, document_network, "Text") {
|
||||
fn edit_layer_deepest_manipulation(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface, responses: &mut VecDeque<Message>) {
|
||||
if is_layer_fed_by_node_of_name(layer, network_interface, "Text") {
|
||||
responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Text });
|
||||
responses.add(TextToolMessage::EditSelected);
|
||||
} else if is_layer_fed_by_node_of_name(layer, document_network, "Path") {
|
||||
} else if is_layer_fed_by_node_of_name(layer, network_interface, "Path") {
|
||||
responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Path });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,12 +216,10 @@ impl Fsm for SplineToolFsmState {
|
||||
|
||||
tool_data.weight = tool_options.line_weight;
|
||||
|
||||
let nodes = {
|
||||
let node_type = resolve_document_node_type("Spline").expect("Spline node does not exist");
|
||||
let node = node_type.to_document_node_default_inputs([None, Some(NodeInput::value(TaggedValue::VecDVec2(Vec::new()), false))], Default::default());
|
||||
let node_type = resolve_document_node_type("Spline").expect("Spline node does not exist");
|
||||
let node = node_type.node_template_input_override([None, Some(NodeInput::value(TaggedValue::VecDVec2(Vec::new()), false))]);
|
||||
let nodes = vec![(NodeId(0), node)];
|
||||
|
||||
HashMap::from([(NodeId(0), node)])
|
||||
};
|
||||
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, parent, responses);
|
||||
tool_options.fill.apply_fill(layer, responses);
|
||||
tool_options.stroke.apply_stroke(tool_data.weight, layer, responses);
|
||||
@@ -330,7 +328,7 @@ fn update_spline(document: &DocumentMessageHandler, tool_data: &SplineToolData,
|
||||
|
||||
let Some(layer) = tool_data.layer else { return };
|
||||
|
||||
let Some(node_id) = graph_modification_utils::NodeGraphLayer::new(layer, document.network()).upstream_node_id_from_name("Spline") else {
|
||||
let Some(node_id) = graph_modification_utils::NodeGraphLayer::new(layer, &document.network_interface).upstream_node_id_from_name("Spline") else {
|
||||
return;
|
||||
};
|
||||
responses.add_front(NodeGraphMessage::SetInputValue { node_id, input_index: 1, value });
|
||||
|
||||
@@ -5,11 +5,12 @@ use crate::application::generate_uuid;
|
||||
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
||||
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::InputConnector;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils::{self, is_layer_fed_by_node_of_name};
|
||||
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::NodeId;
|
||||
use graph_craft::document::{NodeId, NodeInput};
|
||||
use graphene_core::renderer::Quad;
|
||||
use graphene_core::text::{load_face, Font, FontCache};
|
||||
use graphene_core::vector::style::Fill;
|
||||
@@ -213,9 +214,8 @@ struct TextToolData {
|
||||
impl TextToolData {
|
||||
/// Set the editing state of the currently modifying layer
|
||||
fn set_editing(&self, editable: bool, font_cache: &FontCache, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
||||
// TODO: Should always set visibility for document network, but `node_id` is not a layer so it crashes
|
||||
if let Some(node_id) = graph_modification_utils::get_fill_id(self.layer, &document.network) {
|
||||
responses.add(GraphOperationMessage::SetVisibility { node_id, visible: !editable });
|
||||
if let Some(node_id) = graph_modification_utils::get_fill_id(self.layer, &document.network_interface) {
|
||||
responses.add(NodeGraphMessage::SetVisibility { node_id, visible: !editable });
|
||||
}
|
||||
|
||||
if let Some(editing_text) = self.editing_text.as_ref().filter(|_| editable) {
|
||||
@@ -234,8 +234,8 @@ impl TextToolData {
|
||||
|
||||
fn load_layer_text_node(&mut self, document: &DocumentMessageHandler) -> Option<()> {
|
||||
let transform = document.metadata().transform_to_viewport(self.layer);
|
||||
let color = graph_modification_utils::get_fill_color(self.layer, &document.network).unwrap_or(Color::BLACK);
|
||||
let (text, font, font_size) = graph_modification_utils::get_text(self.layer, &document.network)?;
|
||||
let color = graph_modification_utils::get_fill_color(self.layer, &document.network_interface).unwrap_or(Color::BLACK);
|
||||
let (text, font, font_size) = graph_modification_utils::get_text(self.layer, &document.network_interface)?;
|
||||
self.editing_text = Some(EditingText {
|
||||
text: text.clone(),
|
||||
font: font.clone(),
|
||||
@@ -266,12 +266,16 @@ impl TextToolData {
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![self.layer.to_node()] });
|
||||
}
|
||||
|
||||
fn interact(&mut self, state: TextToolFsmState, mouse: DVec2, document: &DocumentMessageHandler, font_cache: &FontCache, responses: &mut VecDeque<Message>) -> TextToolFsmState {
|
||||
fn interact(
|
||||
&mut self,
|
||||
state: TextToolFsmState,
|
||||
input: &InputPreprocessorMessageHandler,
|
||||
document: &DocumentMessageHandler,
|
||||
font_cache: &FontCache,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) -> TextToolFsmState {
|
||||
// Check if the user has selected an existing text layer
|
||||
if let Some(clicked_text_layer_path) = document
|
||||
.click(mouse, document.network())
|
||||
.filter(|&layer| is_layer_fed_by_node_of_name(layer, &document.network, "Text"))
|
||||
{
|
||||
if let Some(clicked_text_layer_path) = document.click(input).filter(|&layer| is_layer_fed_by_node_of_name(layer, &document.network_interface, "Text")) {
|
||||
self.start_editing_layer(clicked_text_layer_path, state, document, font_cache, responses);
|
||||
|
||||
TextToolFsmState::Editing
|
||||
@@ -288,7 +292,7 @@ impl TextToolData {
|
||||
font: editing_text.font.clone(),
|
||||
size: editing_text.font_size,
|
||||
parent: document.new_layer_parent(true),
|
||||
insert_index: -1,
|
||||
insert_index: 0,
|
||||
});
|
||||
responses.add(GraphOperationMessage::FillSet {
|
||||
layer: self.layer,
|
||||
@@ -316,7 +320,7 @@ impl TextToolData {
|
||||
}
|
||||
|
||||
fn can_edit_selected(document: &DocumentMessageHandler) -> Option<LayerNodeIdentifier> {
|
||||
let mut selected_layers = document.selected_nodes.selected_layers(document.metadata());
|
||||
let mut selected_layers = document.network_interface.selected_nodes(&[]).unwrap().selected_layers(document.metadata());
|
||||
let layer = selected_layers.next()?;
|
||||
|
||||
// Check that only one layer is selected
|
||||
@@ -324,7 +328,7 @@ fn can_edit_selected(document: &DocumentMessageHandler) -> Option<LayerNodeIdent
|
||||
return None;
|
||||
}
|
||||
|
||||
if !is_layer_fed_by_node_of_name(layer, &document.network, "Text") {
|
||||
if !is_layer_fed_by_node_of_name(layer, &document.network_interface, "Text") {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -364,8 +368,8 @@ impl Fsm for TextToolFsmState {
|
||||
TextToolFsmState::Editing
|
||||
}
|
||||
(_, TextToolMessage::Overlays(mut overlay_context)) => {
|
||||
for layer in document.selected_nodes.selected_layers(document.metadata()) {
|
||||
let Some((text, font, font_size)) = graph_modification_utils::get_text(layer, &document.network) else {
|
||||
for layer in document.network_interface.selected_nodes(&[]).unwrap().selected_layers(document.metadata()) {
|
||||
let Some((text, font, font_size)) = graph_modification_utils::get_text(layer, &document.network_interface) else {
|
||||
continue;
|
||||
};
|
||||
let buzz_face = font_cache.get(font).map(|data| load_face(data));
|
||||
@@ -387,7 +391,7 @@ impl Fsm for TextToolFsmState {
|
||||
});
|
||||
tool_data.new_text = String::new();
|
||||
|
||||
tool_data.interact(state, input.mouse.position, document, font_cache, responses)
|
||||
tool_data.interact(state, input, document, font_cache, responses)
|
||||
}
|
||||
(state, TextToolMessage::EditSelected) => {
|
||||
if let Some(layer) = can_edit_selected(document) {
|
||||
@@ -410,10 +414,9 @@ impl Fsm for TextToolFsmState {
|
||||
TextToolFsmState::Editing
|
||||
}
|
||||
(TextToolFsmState::Editing, TextToolMessage::TextChange { new_text }) => {
|
||||
responses.add(NodeGraphMessage::SetQualifiedInputValue {
|
||||
node_id: graph_modification_utils::get_text_id(tool_data.layer, &document.network).unwrap(),
|
||||
input_index: 1,
|
||||
value: TaggedValue::String(new_text),
|
||||
responses.add(NodeGraphMessage::SetInput {
|
||||
input_connector: InputConnector::node(graph_modification_utils::get_text_id(tool_data.layer, &document.network_interface).unwrap(), 1),
|
||||
input: NodeInput::value(TaggedValue::String(new_text), false),
|
||||
});
|
||||
|
||||
tool_data.set_editing(false, font_cache, document, responses);
|
||||
|
||||
@@ -44,10 +44,13 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
|
||||
fn process_message(&mut self, message: TransformLayerMessage, responses: &mut VecDeque<Message>, (document, input, tool_data, shape_editor): TransformData) {
|
||||
let using_path_tool = tool_data.active_tool_type == ToolType::Path;
|
||||
|
||||
// TODO: Add support for transforming layer not in the document network
|
||||
let selected_layers = document
|
||||
.selected_nodes
|
||||
.network_interface
|
||||
.selected_nodes(&[])
|
||||
.unwrap()
|
||||
.selected_layers(document.metadata())
|
||||
.filter(|&layer| document.metadata().node_is_visible(layer.to_node()) && !document.metadata().node_is_locked(layer.to_node()))
|
||||
.filter(|&layer| document.network_interface.is_visible(&layer.to_node(), &[]) && !document.network_interface.is_locked(&layer.to_node(), &[]))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut selected = Selected::new(
|
||||
@@ -55,8 +58,7 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
|
||||
&mut self.pivot,
|
||||
&selected_layers,
|
||||
responses,
|
||||
&document.network,
|
||||
&document.metadata,
|
||||
&document.network_interface,
|
||||
Some(shape_editor),
|
||||
&tool_data.active_tool_type,
|
||||
);
|
||||
@@ -68,7 +70,10 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
|
||||
}
|
||||
|
||||
if using_path_tool {
|
||||
if let Some(vector_data) = selected_layers.first().and_then(|&layer| document.metadata.compute_modified_vector(layer, &document.network)) {
|
||||
if let Some(vector_data) = selected_layers
|
||||
.first()
|
||||
.and_then(|&layer| document.metadata().compute_modified_vector(layer, &document.network_interface))
|
||||
{
|
||||
*selected.original_transforms = OriginalTransforms::default();
|
||||
let viewspace = document.metadata().transform_to_viewport(selected_layers[0]);
|
||||
|
||||
@@ -212,7 +217,7 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
|
||||
self.mouse_position = input.mouse.position;
|
||||
}
|
||||
TransformLayerMessage::SelectionChanged => {
|
||||
let target_layers = document.selected_nodes.selected_layers(document.metadata()).collect();
|
||||
let target_layers = document.network_interface.selected_nodes(&[]).unwrap().selected_layers(document.metadata()).collect();
|
||||
shape_editor.set_selected_layers(target_layers);
|
||||
}
|
||||
TransformLayerMessage::TypeBackspace => self.transform_operation.grs_typed(self.typing.type_backspace(), &mut selected, self.snap),
|
||||
|
||||
@@ -18,7 +18,7 @@ use graphene_std::text::FontCache;
|
||||
use std::fmt::{self, Debug};
|
||||
|
||||
pub struct ToolActionHandlerData<'a> {
|
||||
pub document: &'a DocumentMessageHandler,
|
||||
pub document: &'a mut DocumentMessageHandler,
|
||||
pub document_id: DocumentId,
|
||||
pub global_tool_data: &'a DocumentToolData,
|
||||
pub input: &'a InputPreprocessorMessageHandler,
|
||||
@@ -28,7 +28,7 @@ pub struct ToolActionHandlerData<'a> {
|
||||
}
|
||||
impl<'a> ToolActionHandlerData<'a> {
|
||||
pub fn new(
|
||||
document: &'a DocumentMessageHandler,
|
||||
document: &'a mut DocumentMessageHandler,
|
||||
document_id: DocumentId,
|
||||
global_tool_data: &'a DocumentToolData,
|
||||
input: &'a InputPreprocessorMessageHandler,
|
||||
|
||||
Reference in New Issue
Block a user