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:
adamgerhant
2024-08-04 06:47:13 -07:00
committed by GitHub
co-authored by Keavon Chambers dennis@kobert.dev
parent ea44d1440a
commit 0dbbabe73e
77 changed files with 11361 additions and 8011 deletions
@@ -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, &copy_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(&copy_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),
+2 -2
View File
@@ -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,