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 };