Fix regressions from network interface PR (#1906)

* Fix regressions

* Remove unnecessary graph render in box selection

* Improve stack positioning

* Store layer stacks as absolute position

* Improve automatically creating chains

* Improve group selection

* Increase horizontal layer spacing by 1

* Fix export crash and demo artwork

* Improve inserting node onto wire, fix spline crash

* Remove comment
This commit is contained in:
adamgerhant 2024-08-07 01:03:12 -07:00 committed by GitHub
parent e46af89708
commit 8041b1237c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 436 additions and 190 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -593,11 +593,18 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
let all_layers_to_group = self.network_interface.shallowest_unique_layers(&self.selection_network_path).collect::<Vec<_>>();
// Ensure nodes are grouped in the correct order
for (insert_index, layer_to_group) in all_layers_to_group.into_iter().rev().enumerate() {
let mut all_layers_to_group_sorted = Vec::new();
for descendant in LayerNodeIdentifier::ROOT_PARENT.descendants(self.metadata()) {
if all_layers_to_group.contains(&descendant) {
all_layers_to_group_sorted.push(descendant);
};
}
for layer_to_group in all_layers_to_group_sorted.into_iter().rev() {
responses.add(NodeGraphMessage::MoveLayerToStack {
layer: layer_to_group,
parent,
insert_index,
insert_index: 0,
});
}

View File

@ -255,7 +255,7 @@ fn import_usvg_node(modify_inputs: &mut ModifyInputsContext, node: &usvg::Node,
modify_inputs.network_interface.move_layer_to_stack(layer, parent, insert_index, &[]);
if let Some(transform_node_id) = modify_inputs.get_existing_node_id("Transform") {
if let Some(transform_node_id) = modify_inputs.existing_node_id("Transform") {
transform_utils::update_transform(modify_inputs.network_interface, &transform_node_id, transform * usvg_transform(node.abs_transform()));
}

View File

@ -217,17 +217,26 @@ impl<'a> ModifyInputsContext<'a> {
})
}
// Gets the node id of a node with a specific reference that is upstream from the layer node, and creates it if it does not exist
pub fn get_existing_node_id(&mut self, reference: &'static str) -> Option<NodeId> {
let existing_node_id = self
.network_interface
.upstream_flow_back_from_nodes(self.get_output_layer().map_or(vec![], |layer| vec![layer.to_node()]), &[], network_interface::FlowType::HorizontalFlow)
pub fn existing_node_id(&mut self, reference: &'static str) -> Option<NodeId> {
// Start from the layer node or export
let node_id = self.get_output_layer().map_or(Vec::new(), |output_layer| vec![output_layer.to_node()]);
let upstream = self.network_interface.upstream_flow_back_from_nodes(node_id, &[], network_interface::FlowType::HorizontalFlow);
let is_traversal_start = |node_id: NodeId| {
self.layer_node.map(|layer| layer.to_node()) == Some(node_id) || self.network_interface.network(&[]).unwrap().exports.iter().any(|export| export.as_node() == Some(node_id))
};
// Take until another layer node is found (but not the first layer node)
let existing_node_id = upstream
.take_while(|node_id| is_traversal_start(*node_id) || !self.network_interface.is_layer(node_id, &[]))
.find(|node_id| self.network_interface.reference(node_id, &[]).is_some_and(|node_reference| node_reference == reference));
// Create a new node if the node does not exist and update its inputs
existing_node_id.or_else(|| {
let output_layer = self.get_output_layer()?;
let Some(node_definition) = resolve_document_node_type(reference) else {
log::error!("Node type {} does not exist in ModifyInputsContext::get_existing_node_id", reference);
log::error!("Node type {} does not exist in ModifyInputsContext::existing_node_id", reference);
return None;
};
let node_id = NodeId(generate_uuid());
@ -242,7 +251,7 @@ impl<'a> ModifyInputsContext<'a> {
let backup_color_index = 2;
let backup_gradient_index = 3;
let Some(fill_node_id) = self.get_existing_node_id("Fill") else { return };
let Some(fill_node_id) = self.existing_node_id("Fill") else { return };
match &fill {
Fill::None => {
let input_connector = InputConnector::node(fill_node_id, backup_color_index);
@ -262,19 +271,21 @@ impl<'a> ModifyInputsContext<'a> {
}
pub fn opacity_set(&mut self, opacity: f64) {
let Some(opacity_node_id) = self.get_existing_node_id("Opacity") else { return };
let Some(opacity_node_id) = self.existing_node_id("Opacity") else { return };
let input_connector = InputConnector::node(opacity_node_id, 1);
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(opacity * 100.), false), false);
}
pub fn blend_mode_set(&mut self, blend_mode: BlendMode) {
let Some(blend_mode_node_id) = self.get_existing_node_id("Blend Mode") else { return };
let Some(blend_mode_node_id) = self.existing_node_id("Blend Mode") else {
return;
};
let input_connector = InputConnector::node(blend_mode_node_id, 1);
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::BlendMode(blend_mode), false), false);
}
pub fn stroke_set(&mut self, stroke: Stroke) {
let Some(stroke_node_id) = self.get_existing_node_id("Stroke") else { return };
let Some(stroke_node_id) = self.existing_node_id("Stroke") else { return };
let input_connector = InputConnector::node(stroke_node_id, 1);
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::OptionalColor(stroke.color), false), true);
@ -293,7 +304,7 @@ impl<'a> ModifyInputsContext<'a> {
}
pub fn transform_change(&mut self, transform: DAffine2, transform_in: TransformIn, parent_transform: DAffine2, skip_rerender: bool) {
let Some(transform_node_id) = self.get_existing_node_id("Transform") else { return };
let Some(transform_node_id) = self.existing_node_id("Transform") else { return };
let document_node = self.network_interface.network(&[]).unwrap().nodes.get(&transform_node_id).unwrap();
let layer_transform = transform_utils::get_current_transform(&document_node.inputs);
let to = match transform_in {
@ -312,7 +323,7 @@ impl<'a> ModifyInputsContext<'a> {
}
pub fn transform_set(&mut self, mut transform: DAffine2, transform_in: TransformIn, parent_transform: DAffine2, current_transform: Option<DAffine2>, skip_rerender: bool) {
let Some(transform_node_id) = self.get_existing_node_id("Transform") else { return };
let Some(transform_node_id) = self.existing_node_id("Transform") else { return };
let upstream_transform = self.network_interface.document_metadata().upstream_transform(transform_node_id);
let to = match transform_in {
TransformIn::Local => DAffine2::IDENTITY,
@ -336,25 +347,27 @@ impl<'a> ModifyInputsContext<'a> {
}
pub fn pivot_set(&mut self, new_pivot: DVec2) {
let Some(transform_node_id) = self.get_existing_node_id("Transform") else { return };
let Some(transform_node_id) = self.existing_node_id("Transform") else { return };
self.set_input_with_refresh(InputConnector::node(transform_node_id, 5), NodeInput::value(TaggedValue::DVec2(new_pivot), false), false);
}
pub fn vector_modify(&mut self, modification_type: VectorModificationType) {
let Some(path_node_id) = self.get_existing_node_id("Path") else { return };
let Some(path_node_id) = self.existing_node_id("Path") else { return };
self.network_interface.vector_modify(&path_node_id, modification_type);
self.responses.add(PropertiesPanelMessage::Refresh);
self.responses.add(NodeGraphMessage::RunDocumentGraph);
}
pub fn brush_modify(&mut self, strokes: Vec<BrushStroke>) {
let Some(brush_node_id) = self.get_existing_node_id("Brush") else { return };
let Some(brush_node_id) = self.existing_node_id("Brush") else { return };
self.set_input_with_refresh(InputConnector::node(brush_node_id, 2), NodeInput::value(TaggedValue::BrushStrokes(strokes), false), false);
}
pub fn resize_artboard(&mut self, location: IVec2, dimensions: IVec2) {
let Some(artboard_node_id) = self.get_existing_node_id("Artboard") else { return };
let Some(artboard_node_id) = self.existing_node_id("Artboard") else {
return;
};
let mut dimensions = dimensions;
let mut location = location;

View File

@ -74,8 +74,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
match message {
// TODO: automatically remove broadcast messages.
NodeGraphMessage::AddNodes { nodes, new_ids } => {
let Some(new_layer_id) = new_ids.get(&NodeId(0)).cloned() else {
error!("Could not get layer node when adding as child");
let Some(new_layer_id) = new_ids.get(&NodeId(0)).cloned().or_else(|| nodes.get(0).map(|(node_id, _)| *node_id)) else {
log::error!("No nodes to add in AddNodes");
return;
};
network_interface.insert_node_group(nodes, new_ids, selection_network_path);
@ -232,11 +232,12 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
responses.add(DocumentMessage::StartTransaction);
let new_ids = all_selected_nodes.iter().map(|&id| (id, NodeId(generate_uuid()))).collect::<HashMap<NodeId, NodeId>>();
let copy_ids = all_selected_nodes.iter().enumerate().map(|(new, id)| (*id, NodeId(new as u64))).collect::<HashMap<NodeId, NodeId>>();
// Copy the selected nodes
let nodes = network_interface.copy_nodes(&new_ids, selection_network_path).collect::<Vec<_>>();
let nodes = network_interface.copy_nodes(&copy_ids, selection_network_path).collect::<Vec<_>>();
let new_ids = nodes.iter().map(|(id, _)| (*id, NodeId(generate_uuid()))).collect::<HashMap<_, _>>();
responses.add(NodeGraphMessage::AddNodes { nodes, new_ids });
}
NodeGraphMessage::EnterNestedNetwork => {
@ -519,7 +520,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
}
NodeGraphMessage::PointerMove { shift } => {
if selection_network_path != breadcrumb_network_path {
log::error!("Selection network path does not match breadcrumb network path in PointerUp");
log::error!("Selection network path does not match breadcrumb network path in PointerMove");
return;
}
let Some(network_metadata) = network_interface.network_metadata(selection_network_path) else {
@ -722,58 +723,92 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
return;
};
// TODO: Cache all wire locations if this is a performance issue
let mut overlapping_wires = Self::collect_wires(network_interface, selection_network_path).into_iter().filter(|frontend_wire| {
// Prevent inserting on a link that is connected upstream to the selected node
if network_interface
.upstream_flow_back_from_nodes(vec![selected_node_id], selection_network_path, network_interface::FlowType::UpstreamFlow)
.any(|upstream_id| {
frontend_wire.wire_end.node_id().is_some_and(|wire_end_id| wire_end_id == upstream_id)
|| frontend_wire.wire_start.node_id().is_some_and(|wire_start_id| wire_start_id == upstream_id)
}) {
return false;
}
let mut overlapping_wires = Self::collect_wires(network_interface, selection_network_path)
.into_iter()
.filter(|frontend_wire| {
// Prevent inserting on a link that is connected upstream to the selected node
if network_interface
.upstream_flow_back_from_nodes(vec![selected_node_id], selection_network_path, network_interface::FlowType::UpstreamFlow)
.any(|upstream_id| {
frontend_wire.wire_end.node_id().is_some_and(|wire_end_id| wire_end_id == upstream_id)
|| frontend_wire.wire_start.node_id().is_some_and(|wire_start_id| wire_start_id == upstream_id)
}) {
return false;
}
// Prevent inserting a layer into a chain
if network_interface.is_layer(&selected_node_id, selection_network_path)
&& frontend_wire
.wire_start
// Prevent inserting a layer into a chain
if network_interface.is_layer(&selected_node_id, selection_network_path)
&& frontend_wire
.wire_start
.node_id()
.is_some_and(|wire_start_id| network_interface.is_chain(&wire_start_id, selection_network_path))
{
return false;
}
let Some(input_position) = network_interface.input_position(&frontend_wire.wire_end, selection_network_path) else {
log::error!("Could not get input port position for {:?}", frontend_wire.wire_end);
return false;
};
let Some(output_position) = network_interface.output_position(&frontend_wire.wire_start, selection_network_path) else {
log::error!("Could not get output port position for {:?}", frontend_wire.wire_start);
return false;
};
let start_node_is_layer = frontend_wire
.wire_end
.node_id()
.is_some_and(|wire_start_id| network_interface.is_chain(&wire_start_id, selection_network_path))
{
return false;
.is_some_and(|wire_start_id| network_interface.is_layer(&wire_start_id, selection_network_path));
let end_node_is_layer = frontend_wire
.wire_end
.node_id()
.is_some_and(|wire_end_id| network_interface.is_layer(&wire_end_id, selection_network_path));
let locations = Self::build_wire_path_locations(output_position, input_position, start_node_is_layer, end_node_is_layer);
let bezier = bezier_rs::Bezier::from_cubic_dvec2(
(locations[0].x, locations[0].y).into(),
(locations[1].x, locations[1].y).into(),
(locations[2].x, locations[2].y).into(),
(locations[3].x, locations[3].y).into(),
);
!bezier.rectangle_intersections(bounding_box[0], bounding_box[1]).is_empty() || bezier.is_contained_within(bounding_box[0], bounding_box[1])
})
.collect::<Vec<_>>();
let is_stack_wire = |wire: &FrontendNodeWire| match (wire.wire_start.node_id(), wire.wire_end.node_id(), wire.wire_end.input_index()) {
(Some(start_id), Some(end_id), input_index) => {
input_index == 0 && network_interface.is_layer(&start_id, selection_network_path) && network_interface.is_layer(&end_id, selection_network_path)
}
_ => false,
};
let Some(input_position) = network_interface.input_position(&frontend_wire.wire_end, selection_network_path) else {
log::error!("Could not get input port position for {:?}", frontend_wire.wire_end);
return false;
};
// Prioritize vertical thick lines and cancel if there are multiple potential wires
let mut node_wires = Vec::new();
let mut stack_wires = Vec::new();
for wire in overlapping_wires {
if is_stack_wire(&wire) {
stack_wires.push(wire)
} else {
node_wires.push(wire)
}
}
let Some(output_position) = network_interface.output_position(&frontend_wire.wire_start, selection_network_path) else {
log::error!("Could not get output port position for {:?}", frontend_wire.wire_start);
return false;
};
let start_node_is_layer = frontend_wire
.wire_end
.node_id()
.is_some_and(|wire_start_id| network_interface.is_layer(&wire_start_id, selection_network_path));
let end_node_is_layer = frontend_wire
.wire_end
.node_id()
.is_some_and(|wire_end_id| network_interface.is_layer(&wire_end_id, selection_network_path));
let locations = Self::build_wire_path_locations(output_position, input_position, start_node_is_layer, end_node_is_layer);
let bezier = bezier_rs::Bezier::from_cubic_dvec2(
(locations[0].x, locations[0].y).into(),
(locations[1].x, locations[1].y).into(),
(locations[2].x, locations[2].y).into(),
(locations[3].x, locations[3].y).into(),
);
!bezier.rectangle_intersections(bounding_box[0], bounding_box[1]).is_empty() || bezier.is_contained_within(bounding_box[0], bounding_box[1])
});
if let Some(overlapping_wire) = overlapping_wires.next() {
let overlapping_wire = if network_interface.is_layer(&selected_node_id, selection_network_path) {
if stack_wires.len() == 1 {
stack_wires.first()
} else if stack_wires.len() == 0 && node_wires.len() == 1 {
node_wires.first()
} else {
None
}
} else if node_wires.len() == 1 {
node_wires.first()
} else {
None
};
if let Some(overlapping_wire) = overlapping_wire {
let Some(network) = network_interface.network(selection_network_path) else {
return;
};
@ -941,19 +976,56 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
}
}
}
for node_id in node_ids {
network_interface.shift_node(&node_id, IVec2::new(displacement_x, displacement_y), selection_network_path);
if let Some(outward_wires) = network_interface
.outward_wires(selection_network_path)
.and_then(|outward_wires| outward_wires.get(&OutputConnector::node(node_id, 0)))
.cloned()
let mut filtered_node_ids = Vec::new();
for selected_node in &node_ids {
// Deselect chain nodes upstream from a selected layer
if network_interface.is_chain(selected_node, selection_network_path)
&& network_interface
.downstream_layer(selected_node, selection_network_path)
.is_some_and(|downstream_layer| node_ids.contains(&downstream_layer.to_node()))
{
if outward_wires.len() == 1 {
network_interface.try_set_upstream_to_chain(&outward_wires[0], selection_network_path)
}
// Deselect stack nodes upstream from a selected layer
continue;
}
let mut is_downstream_from_selected_absolute_layer = false;
let mut current_node = *selected_node;
loop {
let Some(outward_wires) = network_interface.outward_wires(selection_network_path) else {
break;
};
let Some(outward_wires) = outward_wires.get(&OutputConnector::node(current_node, 0)) else {
break;
};
if outward_wires.is_empty() {
break;
}
let Some(downstream_node) = outward_wires[0].node_id() else {
break;
};
if outward_wires[0].input_index() != 0 {
break;
}
if !network_interface.is_layer(&downstream_node, selection_network_path) {
break;
}
if network_interface.is_absolute(&downstream_node, selection_network_path) {
is_downstream_from_selected_absolute_layer = node_ids.contains(&downstream_node);
break;
}
current_node = downstream_node;
}
if is_downstream_from_selected_absolute_layer {
continue;
}
filtered_node_ids.push(*selected_node)
}
for node_id in filtered_node_ids {
network_interface.shift_node(&node_id, IVec2::new(displacement_x, displacement_y), selection_network_path);
}
if graph_view_overlay_open {
responses.add(NodeGraphMessage::SendGraph);
responses.add(DocumentMessage::RenderRulers);
@ -1015,13 +1087,13 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
log::error!("Could not get selected nodes in NodeGraphMessage::ToggleSelectedLocked");
return;
};
let node_ids = selected_nodes.selected_nodes().cloned().collect::<Vec<_>>();
// If any of the selected layers are locked, show them all. Otherwise, hide them all.
let locked = !node_ids.iter().all(|node_id| network_interface.is_locked(node_id, selection_network_path));
responses.add(DocumentMessage::StartTransaction);
for node_id in &node_ids {
responses.add(NodeGraphMessage::SetLocked { node_id: *node_id, locked });
}
@ -1127,7 +1199,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
log::error!("Could not get selected nodes in PointerMove");
return;
};
let mut nodes = if shift { selected_nodes.selected_nodes_ref().clone() } else { Vec::new() };
let previous_selection = selected_nodes.selected_nodes_ref().clone();
let mut nodes = if shift { previous_selection.clone() } else { Vec::new() };
let all_nodes = network_metadata.persistent_metadata.node_metadata.keys().cloned().collect::<Vec<_>>();
for node_id in all_nodes {
let Some(click_targets) = network_interface.node_click_targets(&node_id, selection_network_path) else {
@ -1141,7 +1214,9 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
nodes.push(node_id);
}
}
responses.add(NodeGraphMessage::SelectedNodesSet { nodes });
if nodes != previous_selection {
responses.add(NodeGraphMessage::SelectedNodesSet { nodes });
}
responses.add(FrontendMessage::UpdateBox { box_selection })
}
}
@ -1221,6 +1296,8 @@ impl NodeGraphMessageHandler {
DeleteSelectedNodes,
DuplicateSelectedNodes,
ToggleSelectedAsLayersOrNodes,
ToggleSelectedLocked,
ToggleSelectedVisibility,
PrintSelectedNodeCoordinates,
));
}

View File

@ -767,17 +767,6 @@ impl NodeNetworkInterface {
}
}
pub fn is_stack(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
let Some(node_metadata) = self.node_metadata(node_id, network_path) else {
log::error!("Could not get node_metadata in is_chain");
return false;
};
match &node_metadata.persistent_metadata.node_type_metadata {
NodeTypePersistentMetadata::Layer(layer_metadata) => matches!(layer_metadata.position, LayerPosition::Stack(_)),
_ => false,
}
}
pub fn is_artboard(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
self.reference(node_id, network_path)
.as_ref()
@ -1390,7 +1379,7 @@ impl NodeNetworkInterface {
let icon_overhang_width = icon_width / 2.;
let text_right = thumbnail_width + gap_width + text_width;
let layer_width_pixels = text_right + gap_width + icon_width - icon_overhang_width;
let layer_width_pixels = text_right + gap_width + icon_width + icon_overhang_width;
let layer_width = ((layer_width_pixels / 24.) as u32).max(8);
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else {
@ -1549,12 +1538,11 @@ impl NodeNetworkInterface {
log::error!("Could not get nested node_metadata in new DocumentNodeTransientMetadata");
return None;
};
match &node_metadata.persistent_metadata.node_type_metadata {
match &node_metadata.persistent_metadata.node_type_metadata.clone() {
NodeTypePersistentMetadata::Layer(layer_metadata) => {
match layer_metadata.position {
LayerPosition::Absolute(position) => Some(position),
LayerPosition::Stack(y_offset) => {
// TODO: Use root node to restore if previewing
let Some(downstream_node_connectors) = self
.outward_wires(network_path)
.and_then(|outward_wires| outward_wires.get(&OutputConnector::node(*node_id, 0)))
@ -1563,48 +1551,21 @@ impl NodeNetworkInterface {
log::error!("Could not get downstream node in position_from_downstream_node");
return None;
};
let Some((downstream_node_id, input_index)) = downstream_node_connectors
let downstream_connector = downstream_node_connectors
.iter()
.find_map(|input_connector| input_connector.node_id().map(|node_id| (node_id, input_connector.input_index())))
else {
log::error!("Could not get downstream node input connector with input index 0 for node {node_id}");
.find_map(|input_connector| input_connector.node_id().map(|node_id| (node_id, input_connector.input_index())));
let Some((downstream_node_id, _)) = downstream_connector else {
log::error!("Could not get downstream node input connector for node {node_id}");
return None;
};
// Ensure that the right edge of the layer is aligned with the vertical connection line if the input index is greater than 0
let network = self.network(network_path)?;
let downstream_node = network.nodes.get(&downstream_node_id)?;
let inputs_len = downstream_node.inputs.len();
let mut right_stack_nodes = Vec::new();
for post_input_index in input_index..inputs_len {
// Do not offset nodes directly below in the layer stack
if input_index == 0 && self.is_layer(&downstream_node_id, network_path) {
break;
}
let Some(stack_node) = downstream_node.inputs.get(post_input_index).and_then(|input| input.as_node()) else {
continue;
};
if self.is_layer(&stack_node, network_path) {
right_stack_nodes.push(stack_node);
}
}
let mut right_offset = 0;
for right_stack_node in right_stack_nodes {
let Some(layer_width) = self.layer_width(&right_stack_node, network_path) else {
log::error!("Could not get layer width in position_from_downstream_node");
return None;
};
right_offset -= layer_width as i32;
}
// Get the height of the node to ensure nodes do not overlap
let Some(node_height) = self.height_from_click_target(&downstream_node_id, network_path) else {
let Some(downstream_node_height) = self.height_from_click_target(&downstream_node_id, network_path) else {
log::error!("Could not get click target height in position_from_downstream_node");
return None;
};
self.position(&downstream_node_id, network_path)
.map(|position| position + IVec2::new(right_offset, (node_height + 1 + y_offset) as i32))
.map(|position| position + IVec2::new(0, 1 + downstream_node_height as i32 + y_offset as i32))
}
}
}
@ -2306,6 +2267,13 @@ impl NodeNetworkInterface {
return;
};
// When changing a NodeInput::Node to a NodeInput::Node, the input should first be disconnected to ensure proper side effects
if (matches!(previous_input, NodeInput::Node { .. }) && matches!(new_input, NodeInput::Node { .. })) {
self.disconnect_input(input_connector, network_path);
self.set_input(input_connector, new_input, network_path);
return;
}
// If the previous input is connected to a chain node, then set all upstream chain nodes to absolute position
if let NodeInput::Node { node_id: previous_upstream_id, .. } = &previous_input {
if self.is_chain(previous_upstream_id, network_path) {
@ -2402,7 +2370,10 @@ impl NodeNetworkInterface {
// If a layer is connected to the exports, it should be set to absolute position without being moved.
self.set_absolute_position(upstream_node_id, network_path, current_node_position)
}
InputConnector::Node { node_id: downstream_node_id, .. } => {
InputConnector::Node {
node_id: downstream_node_id,
input_index,
} => {
// If a layer is connected to another node, it should be set to stack positioning
let Some(downstream_node_metadata) = self.node_metadata(downstream_node_id, network_path) else {
log::error!("Could not get downstream node_metadata in set_input");
@ -2410,15 +2381,21 @@ impl NodeNetworkInterface {
};
match &downstream_node_metadata.persistent_metadata.node_type_metadata {
// TODO: Layout system
NodeTypePersistentMetadata::Layer(downstream_layer_metadata) => match downstream_layer_metadata.position {
// If the layer feeds into an absolute positioned layer, set its y offset to 0
LayerPosition::Absolute(_) => self.set_stack_position(upstream_node_id, network_path, 0),
// If the layer is added to a stack, set its y offset based on the downstream node
LayerPosition::Stack(_) => self.set_stack_position(upstream_node_id, network_path, 0),
},
NodeTypePersistentMetadata::Layer(_) => {
// If the layer feeds into the bottom input of layer, set its position to stack at its previous y position
if *input_index == 0 {
let Some(downstream_node_position) = self.position(downstream_node_id, network_path) else {
log::error!("Could not get downstream node position in set_input for node {downstream_node_id}");
return;
};
self.set_stack_position(upstream_node_id, (current_node_position.y - downstream_node_position.y - 3).max(0) as u32, network_path);
} else {
self.set_absolute_position(upstream_node_id, network_path, current_node_position);
}
}
NodeTypePersistentMetadata::Node(_) => {
// If the layer feeds into a node, set its y offset to 0
self.set_stack_position(upstream_node_id, network_path, 0);
self.set_absolute_position(upstream_node_id, network_path, current_node_position);
}
}
}
@ -2500,14 +2477,17 @@ impl NodeNetworkInterface {
if matches!(input_connector, InputConnector::Node { .. }) {
self.set_input(input_connector, value_input, network_path);
} else {
// TODO: Allow the user to drag the solid line when previewing
// This causes a crash since when ending preview the NodeInput::Node to the previewed node needs to be disconnected.
// Since it is only possible to drag the solid line, if previewing then there must be a dashed connection, which becomes the new export
if matches!(self.previewing(network_path), Previewing::Yes { .. }) {
self.start_previewing_without_restore(network_path);
}
// If there is no preview, then disconnect
else {
self.set_input(input_connector, value_input, network_path);
}
// if matches!(self.previewing(network_path), Previewing::Yes { .. }) {
// self.start_previewing_without_restore(network_path);
// }
// // If there is no preview, then disconnect
// else {
// self.set_input(input_connector, value_input, network_path);
// }
self.set_input(input_connector, value_input, network_path);
}
}
@ -2641,6 +2621,12 @@ impl NodeNetworkInterface {
}
for delete_node_id in &delete_nodes {
let upstream_chain_nodes = self
.upstream_flow_back_from_nodes(vec![*delete_node_id], network_path, FlowType::PrimaryFlow)
.skip(1)
.take_while(|upstream_node| self.is_chain(upstream_node, network_path))
.collect::<Vec<_>>();
if !self.remove_references_from_network(delete_node_id, reconnect, network_path) {
log::error!("could not remove references from network");
continue;
@ -2667,6 +2653,9 @@ impl NodeNetworkInterface {
continue;
};
network_metadata.persistent_metadata.node_metadata.remove(delete_node_id);
for previous_chain_node in upstream_chain_nodes {
self.set_chain_position(&previous_chain_node, network_path);
}
}
self.unload_all_nodes_bounding_box(network_path);
// Instead of unloaded all node click targets, just unload the nodes upstream from the deleted nodes. unload_upstream_node_click_targets will not work since the nodes have been deleted.
@ -2833,35 +2822,87 @@ impl NodeNetworkInterface {
}
pub fn set_to_node_or_layer(&mut self, node_id: &NodeId, network_path: &[NodeId], is_layer: bool) {
// If a node is set to a layer, or a layer is set to a node, set upstream nodes to absolute position
if !self.is_layer(node_id, network_path) && is_layer || self.is_layer(node_id, network_path) && !is_layer {
self.set_upstream_chain_to_absolute(node_id, network_path);
}
// If a layer is set to a node, set upstream nodes to absolute position, and upstream siblings to absolute position
let child_id = { self.upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::HorizontalFlow).skip(1).next() };
let upstream_sibling_id = { self.upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::PrimaryFlow).skip(1).next() };
match (self.is_layer(node_id, network_path), is_layer) {
(true, false) => {
if let Some(child_id) = child_id {
self.set_upstream_chain_to_absolute(&child_id, network_path);
}
if let Some(upstream_sibling_id) = upstream_sibling_id {
let Some(upstream_sibling_position) = self.position(&upstream_sibling_id, network_path) else {
log::error!("Could not get upstream sibling position in set_to_node_or_layer");
return;
};
self.set_absolute_position(&upstream_sibling_id, network_path, upstream_sibling_position);
}
}
(false, true) => {
// If a node is set to a layer
if let Some(upstream_sibling_id) = upstream_sibling_id {
if self.is_layer(&upstream_sibling_id, network_path) {
let (Some(toggled_node_position), Some(upstream_node_position)) = (self.position(node_id, network_path), self.position(&upstream_sibling_id, network_path)) else {
log::error!("Could not get downstream node position in set_to_node_or_layer");
return;
};
self.set_stack_position(&upstream_sibling_id, (upstream_node_position.y - toggled_node_position.y - 3).max(0) as u32, network_path);
} else {
self.set_upstream_chain_to_absolute(&upstream_sibling_id, network_path);
}
}
}
_ => return,
};
let Some(position) = self.position(node_id, network_path) else {
log::error!("Could not get position in set_to_node_or_layer");
return;
};
let downstream_is_layer = self
.outward_wires(network_path)
.and_then(|outward_wires| {
outward_wires
.get(&OutputConnector::node(*node_id, 0))
.and_then(|outward_wires| outward_wires.get(0))
.and_then(|downstream_connector| if downstream_connector.input_index() == 0 { downstream_connector.node_id() } else { None })
})
.is_some_and(|downstream_node_id| self.is_layer(&downstream_node_id, network_path));
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else {
log::error!("Could not get node_metadata for node {node_id}");
return;
};
// TODO: Set to LayerPosition::Stack if it has a downstream node
node_metadata.persistent_metadata.node_type_metadata = if is_layer {
NodeTypePersistentMetadata::Layer(LayerPersistentMetadata {
position: LayerPosition::Absolute(position),
})
if downstream_is_layer {
NodeTypePersistentMetadata::Layer(LayerPersistentMetadata { position: LayerPosition::Stack(0) })
} else {
NodeTypePersistentMetadata::Layer(LayerPersistentMetadata {
position: LayerPosition::Absolute(position),
})
}
} else {
NodeTypePersistentMetadata::Node(NodePersistentMetadata {
position: NodePosition::Absolute(position),
})
};
if is_layer {
node_metadata.transient_metadata.node_type_metadata = NodeTypeTransientMetadata::Layer(LayerTransientMetadata::default());
} else {
node_metadata.transient_metadata.node_type_metadata = NodeTypeTransientMetadata::Node;
}
if is_layer {
self.try_set_upstream_to_chain(&InputConnector::node(*node_id, 1), network_path);
// Reload click target of the layer which used to encapsulate the node
if let Some(downstream_layer) = self.downstream_layer(node_id, network_path) {
self.unload_node_click_targets(&downstream_layer.to_node(), network_path);
}
}
self.unload_upstream_node_click_targets(vec![*node_id], network_path);
self.unload_all_nodes_bounding_box(network_path);
self.load_structure();
@ -2963,7 +3004,7 @@ impl NodeNetworkInterface {
}
/// Sets the position of a layer to a stack position
fn set_stack_position(&mut self, node_id: &NodeId, network_path: &[NodeId], y_offset: u32) {
pub fn set_stack_position(&mut self, node_id: &NodeId, y_offset: u32, network_path: &[NodeId]) {
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else {
log::error!("Could not get node_metadata for node {node_id}");
return;
@ -2998,7 +3039,7 @@ impl NodeNetworkInterface {
}
pub fn try_set_upstream_to_chain(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) {
// If the new input is to a non layer node on the same y position as the input connector, and the input connector is the side input of a layer, then set it to chain position
// If the new input is to a non layer node on the same y position as the input connector, or the input connector is the side input of a layer, then set it to chain position
if let InputConnector::Node {
node_id: input_connector_node_id,
input_index,
@ -3056,6 +3097,7 @@ impl NodeNetworkInterface {
}
}
/// node_id is the first chain node, not the layer
fn set_upstream_chain_to_absolute(&mut self, node_id: &NodeId, network_path: &[NodeId]) {
for upstream_id in self.upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::HorizontalFlow).collect::<Vec<_>>().iter() {
let Some(previous_position) = self.position(upstream_id, network_path) else {
@ -3090,10 +3132,35 @@ impl NodeNetworkInterface {
let shifted_y_offset = *y_offset as i32 + shift.y;
// A layer can only be shifted to a positive y_offset
*y_offset = shifted_y_offset.max(0) as u32;
let Some(downstream_node) = self
.outward_wires(network_path)
.and_then(|outward_wires| outward_wires.get(&OutputConnector::node(*node_id, 0)))
.and_then(|downstream_nodes| downstream_nodes.first())
.and_then(|downstream_node| downstream_node.node_id())
else {
log::error!("Could not get downstream node in shift_node");
return;
};
self.shift_node(&downstream_node, IVec2::new(shift.x, 0), network_path);
// Unload click targets for all upstream nodes, since they may have been derived from the node that was shifted
}
self.unload_upstream_node_click_targets(vec![*node_id], network_path);
self.try_set_upstream_to_chain(&InputConnector::node(*node_id, 1), network_path);
} else if let NodeTypePersistentMetadata::Node(node_metadata) = &mut node_metadata.persistent_metadata.node_type_metadata {
if let NodePosition::Absolute(node_metadata) = &mut node_metadata.position {
*node_metadata += shift;
// Unload click targets for all upstream nodes, since they may have been derived from the node that was shifted
self.unload_upstream_node_click_targets(vec![*node_id], network_path);
if let Some(outward_wires) = self
.outward_wires(network_path)
.and_then(|outward_wires| outward_wires.get(&OutputConnector::node(*node_id, 0)))
.cloned()
{
if outward_wires.len() == 1 {
self.try_set_upstream_to_chain(&outward_wires[0], network_path)
}
}
} else if let NodePosition::Chain = node_metadata.position {
// TODO: Don't break the chain when shifting a node left or right. Instead, shift the entire chain (?).
// TODO: Instead of outward wires to the export being based on the export (which changes when previewing), it should be based on the root node.
@ -3101,25 +3168,11 @@ impl NodeNetworkInterface {
self.shift_node(node_id, shift, network_path);
}
}
// TODO: Update transient metadata based on the movement. Unloading it means it will be recalculated next time it is needed, which is a simple solution.
// Unload click targets for all nodes, since they may have been derived from the node that was shifted
// Unload click targets for all upstream nodes, since they may have been derived from the node that was shifted
self.unload_upstream_node_click_targets(vec![*node_id], network_path);
self.unload_all_nodes_bounding_box(network_path);
}
fn reconstruct_chain(&self, input_connector: &InputConnector, network_path: &[NodeId]) -> bool {
let Some(previous_input) = self.input_from_connector(input_connector, network_path).cloned() else {
log::error!("Could not get previous input in reconstruct_chain");
return false;
};
//Reconstruct the chain if the upstream node is in a chain.
if let NodeInput::Node { node_id: previous_node_id, .. } = &previous_input {
self.is_chain(previous_node_id, network_path)
} else {
false
}
}
// TODO: Run the auto layout system to make space for the new nodes
// Disconnect the layers primary output and the input to the last non layer node feeding into it through primary flow, reconnects, then moves the layer to the new layer and stack index
pub fn move_layer_to_stack(&mut self, layer: LayerNodeIdentifier, mut parent: LayerNodeIdentifier, mut insert_index: usize, network_path: &[NodeId]) {
@ -3142,10 +3195,31 @@ impl NodeNetworkInterface {
}
}
let previous_upstream_layer = self
.upstream_flow_back_from_nodes(vec![layer.to_node()], network_path, FlowType::PrimaryFlow)
.skip(1)
.find(|node_id| self.is_layer(node_id, network_path));
let Some(layer_to_move_position) = self.position(&layer.to_node(), network_path) else {
log::error!("Could not get layer to move position in move_layer_to_stack");
return;
};
// Get the distance between the layer to move and its upstream layer sibling
// Disconnect layer to move
self.remove_references_from_network(&layer.to_node(), true, network_path);
self.disconnect_input(&InputConnector::node(layer.to_node(), 0), network_path);
// Shift the previous upstream layer to the moved layers position
if let Some(previous_upstream_layer) = previous_upstream_layer {
let Some(previous_upstream_layer_position) = self.position(&previous_upstream_layer, network_path) else {
log::error!("Could not get previous upstream layer position in move_layer_to_stack");
return;
};
let y_offset = layer_to_move_position.y - previous_upstream_layer_position.y;
self.shift_node(&previous_upstream_layer, IVec2::new(0, y_offset), network_path);
}
let post_node = ModifyInputsContext::get_post_node_with_index(self, parent, insert_index);
// // Get the previous input to the post node before inserting the layer
@ -3154,11 +3228,52 @@ impl NodeNetworkInterface {
return;
};
// Connect the layer to the post node
if matches!(post_node_input, NodeInput::Value { .. }) {
self.create_wire(&OutputConnector::node(layer.to_node(), 0), &post_node, network_path);
// Connect the layer to a parent layer/node at the top of the stack
if post_node.input_index() == 1 || matches!(post_node, InputConnector::Export(_)) {
match post_node_input {
// Create a new stack
NodeInput::Value { .. } | NodeInput::Scope(_) | NodeInput::Inline(_) => {
self.create_wire(&OutputConnector::node(layer.to_node(), 0), &post_node, network_path);
let post_node_position = if let Some(post_node_id) = post_node.node_id() {
self.position(&post_node_id, network_path)
} else {
Some(IVec2::ZERO)
};
let Some(post_node_position) = post_node_position else {
log::error!("Could not get post node position in move_layer_to_stack");
return;
};
let offset = IVec2::new(-8, 3);
self.set_absolute_position(&layer.to_node(), network_path, post_node_position + offset);
}
// Move to the top of a stack
NodeInput::Node { node_id, .. } => {
let Some(top_of_stack_position) = self.position(&node_id, network_path) else {
log::error!("Could not get top of stack position in move_layer_to_stack");
return;
};
self.set_absolute_position(&layer.to_node(), network_path, top_of_stack_position - IVec2::new(0, 3));
self.shift_node(&layer.to_node(), IVec2::new(0, 3), network_path);
self.insert_node_between(&layer.to_node(), &post_node, 0, network_path);
}
NodeInput::Network { .. } => {
log::error!("Cannot move post node to parent which connects to the imports")
}
}
} else {
self.insert_node_between(&layer.to_node(), &post_node, 0, network_path);
// Create a new stack
match post_node_input {
NodeInput::Value { .. } | NodeInput::Scope(_) | NodeInput::Inline(_) => {
self.create_wire(&OutputConnector::node(layer.to_node(), 0), &post_node, network_path);
}
NodeInput::Node { .. } => {
self.insert_node_between(&layer.to_node(), &post_node, 0, network_path);
}
NodeInput::Network { .. } => {
log::error!("Cannot move post node to parent which connects to the imports")
}
}
self.set_stack_position(&layer.to_node(), 0, network_path);
}
}
@ -3168,7 +3283,7 @@ impl NodeNetworkInterface {
log::error!("Cannot insert a node onto a wire with no exposed inputs");
return;
}
let reconstruct_chain = self.reconstruct_chain(input_connector, network_path);
let Some(upstream_output) = self.upstream_output_connector(input_connector, network_path) else {
log::error!("Could not get upstream output in insert_node_between");
return;
@ -3182,10 +3297,6 @@ impl NodeNetworkInterface {
// Connect the new node to the previous node
self.create_wire(&upstream_output, &InputConnector::node(*node_id, insert_node_input_index), network_path);
if reconstruct_chain {
self.force_set_upstream_to_chain(node_id, network_path);
}
}
// Moves a node and to the start of a layer chain (feeding into the secondary input of the layer)
@ -3199,6 +3310,7 @@ impl NodeNetworkInterface {
self.set_chain_position(node_id, network_path);
} else {
self.insert_node_between(node_id, &InputConnector::node(parent.to_node(), 1), 0, network_path);
self.force_set_upstream_to_chain(node_id, network_path);
}
}
}
@ -3650,8 +3762,7 @@ pub struct LayerPersistentMetadata {
pub enum LayerPosition {
// Position of the node in grid spaces
Absolute(IVec2),
// A layer is in a Stack when it feeds into the secondary input of a layer input. The Y position stores the vertical distance between the layer and its parent.
// TODO: Store x offset
// A layer is in a Stack when it feeds into the bottom input of a layer. The Y position stores the vertical distance between the layer and its upstream sibling/parent.
Stack(u32),
}

View File

@ -1,5 +1,5 @@
use super::document::utility_types::document_metadata::LayerNodeIdentifier;
use super::document::utility_types::network_interface::{self, InputConnector};
use super::document::utility_types::network_interface::{self, InputConnector, OutputConnector};
use super::utility_types::{PanelType, PersistentData};
use crate::application::generate_uuid;
use crate::consts::DEFAULT_DOCUMENT_NAME;
@ -533,6 +533,33 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
return;
}
// Ensure layers are positioned as stacks if they upstream siblings of another layer
document.network_interface.load_structure();
let all_layers = LayerNodeIdentifier::ROOT_PARENT.descendants(document.network_interface.document_metadata()).collect::<Vec<_>>();
for layer in all_layers {
let Some((downstream_node, input_index)) = document
.network_interface
.outward_wires(&[])
.and_then(|outward_wires| outward_wires.get(&OutputConnector::node(layer.to_node(), 0)))
.and_then(|outward_wires| outward_wires.first())
.and_then(|input_connector| input_connector.node_id().map(|node_id| (node_id, input_connector.input_index())))
else {
continue;
};
// If the downstream node is a layer and the input is the first input and the current layer is not in a stack
if input_index == 0 && document.network_interface.is_layer(&downstream_node, &[]) && document.network_interface.is_absolute(&layer.to_node(), &[]) {
let (Some(layer_position), Some(downstream_position)) =
(document.network_interface.position(&layer.to_node(), &[]), document.network_interface.position(&downstream_node, &[]))
else {
log::error!("Could not get downstream node position in PortfolioMessage::OpenDocumentFileWithId");
return;
};
document
.network_interface
.set_stack_position(&layer.to_node(), (layer_position.y - downstream_position.y - 3).max(0) as u32, &[]);
}
}
document.set_auto_save_state(document_is_auto_saved);
document.set_save_state(document_is_saved);

View File

@ -783,8 +783,10 @@ impl EditorHandle {
let mut shape = None;
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, &mut document.network_interface, &mut responses) {
let Some(transform_node_id) = modify_inputs.get_existing_node_id("Transform") else { return };
if !updated_nodes.insert(transform_node_id) {
let Some(transform_node_id) = modify_inputs.existing_node_id("Transform") else {
return;
};
if !updated_nodes.insert(transform_node_id.clone()) {
return;
}
let Some(inputs) = modify_inputs.network_interface.network(&[]).unwrap().nodes.get(&transform_node_id).map(|node| &node.inputs) else {
@ -798,7 +800,9 @@ impl EditorHandle {
update_transform(&mut document.network_interface, &transform_node_id, pivot_transform * transform * pivot_transform.inverse());
}
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, &mut document.network_interface, &mut responses) {
let Some(shape_node_id) = modify_inputs.get_existing_node_id("Shape") else { return };
let Some(shape_node_id) = modify_inputs.existing_node_id("Shape") else {
return;
};
if !updated_nodes.insert(shape_node_id) {
return;
}

View File

@ -368,6 +368,9 @@ impl<PointId: crate::Identifier> Subpath<PointId> {
pub fn solve_spline_first_handle(points: &[DVec2]) -> Vec<DVec2> {
let len_points = points.len();
if len_points == 0 {
return Vec::new();
}
// Matrix coefficients a, b and c (see https://mathworld.wolfram.com/CubicSpline.html).
// Because the 'a' coefficients are all 1, they need not be stored.

View File

@ -396,6 +396,10 @@ fn splines_from_points(mut vector_data: VectorData) -> VectorData {
vector_data.segment_domain.clear();
if points.positions().is_empty() {
return vector_data;
}
let first_handles = bezier_rs::solve_spline_first_handle(points.positions());
let stroke_id = StrokeId::ZERO;