Migrate the Select tool to the document graph (#1433)

* function for accessing document metadata

* Better select tool

* Fix render

* Fix transforms

* Fix loading saved documents

* Populate graph UI when loading autosave

* Multiple transform nodes

* Fix deep select

* Graph tooltips

* Fix flip axis icon

* Show disabled widgets

* Stop select tool from selecting artboards

* Disable (not hide) the pivot widget; remove Deep/Shallow select for now

* Code review changes

* Fix pivot position with select tool

* Fix incorrectly selected layers when shift clicking

---------

Co-authored-by: Dennis Kobert <dennis@kobert.dev>
Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
0HyperCube
2023-10-17 11:09:32 -07:00
committed by Keavon Chambers
co-authored by Dennis Kobert Keavon Chambers
parent e1cdb2242d
commit 5827e989dc
46 changed files with 1041 additions and 1215 deletions
@@ -28,7 +28,6 @@ use document_legacy::{DocumentError, DocumentResponse, LayerId, Operation as Doc
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeInput, NodeNetwork};
use graphene_core::raster::ImageFrame;
use graphene_core::renderer::Quad;
use graphene_core::text::Font;
use glam::{DAffine2, DVec2};
@@ -123,9 +122,6 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
for response in document_responses {
match &response {
DocumentResponse::FolderChanged { path } => responses.add(FolderChanged { affected_folder_path: path.clone() }),
DocumentResponse::AddSelectedLayer { additional_layers } => responses.add(AddSelectedLayers {
additional_layers: additional_layers.clone(),
}),
DocumentResponse::DeletedLayer { path } => {
self.layer_metadata.remove(path);
}
@@ -139,20 +135,8 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
insert_index: *insert_index,
reverse_index: *reverse_index,
}),
DocumentResponse::CreatedLayer { path, is_selected } => {
if self.layer_metadata.contains_key(path) {
warn!("CreatedLayer overrides existing layer metadata.");
}
self.layer_metadata.insert(path.clone(), LayerMetadata::new(false));
responses.add(LayerChanged { affected_layer_path: path.clone() });
self.layer_range_selection_reference = path.clone();
if *is_selected {
responses.add(AddSelectedLayers {
additional_layers: vec![path.clone()],
});
}
DocumentResponse::CreatedLayer { .. } => {
unimplemented!("We should no longer be creating layers in the document and should instead be using the node graph.")
}
DocumentResponse::DocumentChanged => responses.add(RenderDocument),
DocumentResponse::DeletedSelectedManipulatorPoints => {
@@ -176,11 +160,11 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
}
#[remain::unsorted]
Navigation(message) => {
let document_bounds = self.document_bounds();
let document_bounds = self.metadata().document_bounds();
self.navigation_handler.process_message(
message,
responses,
(&self.document_legacy, document_bounds, ipp, self.selected_visible_layers_bounding_box(&render_data)),
(&self.document_legacy, document_bounds, ipp, self.metadata().selected_visible_layers_bounding_box_viewport()),
);
}
#[remain::unsorted]
@@ -235,39 +219,38 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
}
AlignSelectedLayers { axis, aggregate } => {
self.backup(responses);
let (paths, boxes): (Vec<_>, Vec<_>) = self
.selected_layers()
.filter_map(|path| self.document_legacy.viewport_bounding_box(path, &render_data).ok()?.map(|b| (path, b)))
.unzip();
let axis = match axis {
AlignAxis::X => DVec2::X,
AlignAxis::Y => DVec2::Y,
};
let lerp = |bbox: &[DVec2; 2]| bbox[0].lerp(bbox[1], 0.5);
if let Some(combined_box) = self.document_legacy.combined_viewport_bounding_box(self.selected_layers(), &render_data) {
let aggregated = match aggregate {
AlignAggregate::Min => combined_box[0],
AlignAggregate::Max => combined_box[1],
AlignAggregate::Center => lerp(&combined_box),
AlignAggregate::Average => boxes.iter().map(|b| lerp(b)).reduce(|a, b| a + b).map(|b| b / boxes.len() as f64).unwrap(),
let Some(combined_box) = self.metadata().selected_visible_layers_bounding_box_viewport() else {
return;
};
let aggregated = match aggregate {
AlignAggregate::Min => combined_box[0],
AlignAggregate::Max => combined_box[1],
AlignAggregate::Center => (combined_box[0] + combined_box[1]) / 2.,
};
for layer in self.metadata().selected_layers() {
let Some(bbox) = self.metadata().bounding_box_viewport(layer) else {
continue;
};
for (path, bbox) in paths.into_iter().zip(boxes) {
let center = match aggregate {
AlignAggregate::Min => bbox[0],
AlignAggregate::Max => bbox[1],
_ => lerp(&bbox),
};
let translation = (aggregated - center) * axis;
responses.add(GraphOperationMessage::TransformChange {
layer: path.to_vec(),
transform: DAffine2::from_translation(translation),
transform_in: TransformIn::Viewport,
skip_rerender: false,
});
}
responses.add(BroadcastEvent::DocumentIsDirty);
let center = match aggregate {
AlignAggregate::Min => bbox[0],
AlignAggregate::Max => bbox[1],
_ => (bbox[0] + bbox[1]) / 2.,
};
let translation = (aggregated - center) * axis;
responses.add(GraphOperationMessage::TransformChange {
layer: layer.to_path(),
transform: DAffine2::from_translation(translation),
transform_in: TransformIn::Viewport,
skip_rerender: false,
});
}
responses.add(BroadcastEvent::DocumentIsDirty);
}
BackupDocument { document, layer_metadata } => self.backup_with_document(document, layer_metadata, responses),
ClearLayerTree => {
@@ -374,8 +357,8 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
// Calculate the bounding box of the region to be exported
let bounds = match bounds {
ExportBounds::AllArtwork => self.all_layer_bounds(&render_data),
ExportBounds::Selection => self.selected_visible_layers_bounding_box(&render_data),
ExportBounds::Artboard(id) => self.document_legacy.metadata.bounding_box_document(id),
ExportBounds::Selection => self.metadata().selected_visible_layers_bounding_box_viewport(),
ExportBounds::Artboard(id) => self.metadata().bounding_box_document(id),
}
.unwrap_or_default();
let size = bounds[1] - bounds[0];
@@ -405,12 +388,12 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
FlipAxis::X => DVec2::new(-1., 1.),
FlipAxis::Y => DVec2::new(1., -1.),
};
if let Some([min, max]) = self.document_legacy.combined_viewport_bounding_box(self.selected_layers(), &render_data) {
if let Some([min, max]) = self.metadata().selected_visible_layers_bounding_box_viewport() {
let center = (max + min) / 2.;
let bbox_trans = DAffine2::from_translation(-center);
for path in self.selected_layers() {
for layer in self.metadata().selected_layers() {
responses.add(GraphOperationMessage::TransformChange {
layer: path.to_vec(),
layer: layer.to_path(),
transform: DAffine2::from_scale(scale),
transform_in: TransformIn::Scope { scope: bbox_trans },
skip_rerender: false,
@@ -636,7 +619,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
RenderRulers => {
let document_transform_scale = self.navigation_handler.snapped_scale();
let ruler_origin = self.document_legacy.metadata.document_to_viewport.transform_point2(DVec2::ZERO);
let ruler_origin = self.metadata().document_to_viewport.transform_point2(DVec2::ZERO);
let log = document_transform_scale.log2();
let ruler_interval = if log < 0. { 100. * 2_f64.powf(-log.ceil()) } else { 100. / 2_f64.powf(log.ceil()) };
let ruler_spacing = ruler_interval * document_transform_scale;
@@ -654,7 +637,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
let viewport_size = ipp.viewport_bounds.size();
let viewport_mid = ipp.viewport_bounds.center();
let [bounds1, bounds2] = self.document_bounds().unwrap_or([viewport_mid; 2]);
let [bounds1, bounds2] = self.metadata().document_bounds().unwrap_or([viewport_mid; 2]);
let bounds1 = bounds1.min(viewport_mid) - viewport_size * scale;
let bounds2 = bounds2.max(viewport_mid) + viewport_size * scale;
let bounds_length = (bounds2 - bounds1) * (1. + SCROLLBAR_SPACING);
@@ -912,7 +895,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
responses.add_front(NavigationMessage::SetCanvasZoom { zoom_factor: 2. });
}
ZoomCanvasToFitAll => {
if let Some(bounds) = self.document_bounds() {
if let Some(bounds) = self.metadata().document_bounds() {
responses.add(NavigationMessage::FitViewportToBounds {
bounds,
padding_scale_factor: Some(VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR),
@@ -964,10 +947,13 @@ impl DocumentMessageHandler {
pub fn network(&self) -> &NodeNetwork {
&self.document_legacy.document_network
}
pub fn metadata(&self) -> &document_legacy::document_metadata::DocumentMetadata {
&self.document_legacy.metadata
}
/// Remove the artwork and artboard pan/tilt/zoom to render it without the user's viewport navigation, and save it to be restored at the end
pub(crate) fn remove_document_transform(&mut self) -> DAffine2 {
let old_artwork_transform = self.document_legacy.metadata.document_to_viewport;
let old_artwork_transform = self.metadata().document_to_viewport;
self.document_legacy.metadata.document_to_viewport = DAffine2::IDENTITY;
DocumentLegacy::mark_children_as_dirty(&mut self.document_legacy.root);
@@ -1074,11 +1060,6 @@ impl DocumentMessageHandler {
}
}
pub fn selected_visible_layers_bounding_box(&self, render_data: &RenderData) -> Option<[DVec2; 2]> {
let paths = self.selected_visible_layers();
self.document_legacy.combined_viewport_bounding_box(paths, render_data)
}
pub fn selected_layers(&self) -> impl Iterator<Item = &[LayerId]> {
self.layer_metadata.iter().filter_map(|(path, data)| data.selected.then_some(path.as_slice()))
}
@@ -1130,17 +1111,18 @@ impl DocumentMessageHandler {
fn serialize_structure(&self, folder: LayerNodeIdentifier, structure: &mut Vec<u64>, data: &mut Vec<LayerId>, path: &mut Vec<LayerId>) {
let mut space = 0;
for layer_node in folder.children(&self.document_legacy.metadata) {
for layer_node in folder.children(&self.metadata()) {
data.push(layer_node.to_node());
info!("Pushed child");
space += 1;
if layer_node.has_children(&self.document_legacy.metadata) {
if layer_node.has_children(&self.metadata()) {
path.push(layer_node.to_node());
if self.layer_metadata(path).expanded {
structure.push(space);
self.serialize_structure(folder, structure, data, path);
space = 0;
}
// TODO: Skip if folder is not expanded.
structure.push(space);
self.serialize_structure(layer_node, structure, data, path);
space = 0;
path.pop();
}
}
@@ -1181,7 +1163,7 @@ impl DocumentMessageHandler {
/// ```
pub fn serialize_root(&self) -> Vec<u64> {
let (mut structure, mut data) = (vec![0], Vec::new());
self.serialize_structure(self.document_legacy.metadata.root(), &mut structure, &mut data, &mut vec![]);
self.serialize_structure(self.metadata().root(), &mut structure, &mut data, &mut vec![]);
structure[0] = structure.len() as u64 - 1;
structure.extend(data);
@@ -1279,7 +1261,7 @@ impl DocumentMessageHandler {
/// Replace the document with a new document save, returning the document save.
pub fn replace_document(&mut self, DocumentSave { document, layer_metadata }: DocumentSave) -> DocumentSave {
// Keeping the root is required if the bounds of the viewport have changed during the operation
let old_root = self.document_legacy.metadata.document_to_viewport;
let old_root = self.metadata().document_to_viewport;
let document = std::mem::replace(&mut self.document_legacy, document);
self.document_legacy.metadata.document_to_viewport = old_root;
self.document_legacy.root.cache_dirty = true;
@@ -1414,10 +1396,7 @@ impl DocumentMessageHandler {
pub fn layer_panel_entry_from_path(&self, path: &[LayerId], render_data: &RenderData) -> Option<LayerPanelEntry> {
let layer_metadata = self.layer_metadata(path);
let transform = self
.document_legacy
.generate_transform_across_scope(path, Some(self.document_legacy.metadata.document_to_viewport.inverse()))
.ok()?;
let transform = self.document_legacy.generate_transform_across_scope(path, Some(self.metadata().document_to_viewport.inverse())).ok()?;
let layer = self.document_legacy.layer(path).ok()?;
Some(LayerPanelEntry::new(layer_metadata, transform, layer, path.to_vec(), render_data))
@@ -1439,15 +1418,6 @@ impl DocumentMessageHandler {
self.document_legacy.viewport_bounding_box(&[], render_data).ok().flatten()
}
/// Calculates the document bounds used for scrolling and centring (the layer bounds or the artboard (if applicable))
pub fn document_bounds(&self) -> Option<[DVec2; 2]> {
self.document_legacy
.metadata
.all_layers()
.filter_map(|layer| self.document_legacy.metadata.bounding_box_viewport(layer))
.reduce(Quad::combine_bounds)
}
/// Calculate the path that new layers should be inserted to.
/// Depends on the selected layers as well as their types (Folder/Non-Folder)
pub fn get_path_for_new_layer(&self) -> Vec<u64> {
@@ -59,9 +59,9 @@ impl<'a> ModifyInputsContext<'a> {
}
/// Updates the input of an existing node
fn modify_existing_node_inputs(&mut self, node_id: NodeId, update_input: impl FnOnce(&mut Vec<NodeInput>)) {
fn modify_existing_node_inputs(&mut self, node_id: NodeId, update_input: impl FnOnce(&mut Vec<NodeInput>, NodeId, &DocumentMetadata)) {
let document_node = self.network.nodes.get_mut(&node_id).unwrap();
update_input(&mut document_node.inputs);
update_input(&mut document_node.inputs, node_id, &self.document_metadata);
}
pub fn insert_between(&mut self, id: NodeId, pre: NodeOutput, post: NodeOutput, mut node: DocumentNode, input: usize, output: usize, shift_upstream: IVec2) -> Option<NodeId> {
@@ -216,7 +216,7 @@ impl<'a> ModifyInputsContext<'a> {
}
/// Inserts a new node and modifies the inputs
fn modify_new_node(&mut self, name: &'static str, update_input: impl FnOnce(&mut Vec<NodeInput>)) {
fn modify_new_node(&mut self, name: &'static str, update_input: impl FnOnce(&mut Vec<NodeInput>, NodeId, &DocumentMetadata)) {
let output_node_id = self.layer_node.unwrap_or(self.network.outputs[0].node_id);
let Some(output_node) = self.network.nodes.get_mut(&output_node_id) else {
warn!("Output node doesn't exist");
@@ -235,19 +235,20 @@ impl<'a> ModifyInputsContext<'a> {
return;
};
let mut new_document_node = node_type.to_document_node_default_inputs([Some(new_input)], metadata);
update_input(&mut new_document_node.inputs);
update_input(&mut new_document_node.inputs, node_id, &self.document_metadata);
self.network.nodes.insert(node_id, new_document_node);
}
/// Changes the inputs of a specific node
fn modify_inputs(&mut self, name: &'static str, skip_rerender: bool, update_input: impl FnOnce(&mut Vec<NodeInput>)) {
fn modify_inputs(&mut self, name: &'static str, skip_rerender: bool, update_input: impl FnOnce(&mut Vec<NodeInput>, NodeId, &DocumentMetadata)) {
let existing_node_id = self.network.primary_flow_from_opt(self.layer_node).find(|(node, _)| node.name == name).map(|(_, id)| id);
if let Some(node_id) = existing_node_id {
self.modify_existing_node_inputs(node_id, update_input);
} else {
self.modify_new_node(name, update_input);
}
self.node_graph.nested_path.clear();
self.node_graph.network.clear();
self.responses.add(PropertiesPanelMessage::ResendActiveProperties);
let layer_path = self.layer.to_vec();
@@ -261,8 +262,25 @@ impl<'a> ModifyInputsContext<'a> {
}
}
/// Changes the inputs of a all of the existing instances of a node name
fn modify_all_node_inputs(&mut self, name: &'static str, skip_rerender: bool, mut update_input: impl FnMut(&mut Vec<NodeInput>, NodeId, &DocumentMetadata)) {
let existing_nodes: Vec<_> = self.network.primary_flow_from_opt(self.layer_node).filter(|(node, _)| node.name == name).map(|(_, id)| id).collect();
for existing_node_id in existing_nodes {
self.modify_existing_node_inputs(existing_node_id, &mut update_input);
}
self.responses.add(PropertiesPanelMessage::ResendActiveProperties);
let layer_path = self.layer.to_vec();
if !skip_rerender {
self.responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path });
} else {
self.responses.add(DocumentMessage::FrameClear);
}
}
fn fill_set(&mut self, fill: Fill) {
self.modify_inputs("Fill", false, |inputs| {
self.modify_inputs("Fill", false, |inputs, _node_id, _metadata| {
let fill_type = match fill {
Fill::None => FillType::None,
Fill::Solid(_) => FillType::Solid,
@@ -284,7 +302,7 @@ impl<'a> ModifyInputsContext<'a> {
}
fn stroke_set(&mut self, stroke: Stroke) {
self.modify_inputs("Stroke", false, |inputs| {
self.modify_inputs("Stroke", false, |inputs, _node_id, _metadata| {
inputs[1] = NodeInput::value(TaggedValue::OptionalColor(stroke.color), false);
inputs[2] = NodeInput::value(TaggedValue::F32(stroke.weight as f32), false);
inputs[3] = NodeInput::value(TaggedValue::VecF32(stroke.dash_lengths), false);
@@ -296,49 +314,45 @@ impl<'a> ModifyInputsContext<'a> {
}
fn transform_change(&mut self, transform: DAffine2, transform_in: TransformIn, parent_transform: DAffine2, bounds: LayerBounds, skip_rerender: bool) {
self.modify_inputs("Transform", skip_rerender, |inputs| {
self.modify_inputs("Transform", skip_rerender, |inputs, node_id, metadata| {
let layer_transform = transform_utils::get_current_transform(inputs);
let upstream_transform = metadata.upstream_transform(node_id);
let to = match transform_in {
TransformIn::Local => DAffine2::IDENTITY,
TransformIn::Scope { scope } => scope * parent_transform,
TransformIn::Viewport => parent_transform,
};
let pivot = DAffine2::from_translation(bounds.layerspace_pivot(transform_utils::get_current_normalized_pivot(inputs)));
let pivot = DAffine2::from_translation(upstream_transform.transform_point2(bounds.layerspace_pivot(transform_utils::get_current_normalized_pivot(inputs))));
let transform = pivot.inverse() * to.inverse() * transform * to * pivot * layer_transform;
transform_utils::update_transform(inputs, transform);
});
}
fn transform_set(&mut self, mut transform: DAffine2, transform_in: TransformIn, parent_transform: DAffine2, current_transform: Option<DAffine2>, bounds: LayerBounds, skip_rerender: bool) {
self.modify_inputs("Transform", skip_rerender, |inputs| {
let current_transform_node = transform_utils::get_current_transform(inputs);
self.modify_inputs("Transform", skip_rerender, |inputs, node_id, metadata| {
let upstream_transform = metadata.upstream_transform(node_id);
let to = match transform_in {
TransformIn::Local => DAffine2::IDENTITY,
TransformIn::Scope { scope } => scope * parent_transform,
TransformIn::Viewport => parent_transform,
};
let pivot = DAffine2::from_translation(bounds.layerspace_pivot(transform_utils::get_current_normalized_pivot(inputs)));
let pivot = DAffine2::from_translation(upstream_transform.transform_point2(bounds.layerspace_pivot(transform_utils::get_current_normalized_pivot(inputs))));
if let Some(current_transform) = current_transform.filter(|transform| transform.inverse().is_finite() && current_transform_node.inverse().is_finite()) {
// this_transform * upstream_transforms = current_transform
// So this_transform.inverse() * current_transform = upstream_transforms
let upstream_transform = (pivot * current_transform_node * pivot.inverse()).inverse() * current_transform;
// desired_final_transform = this_transform * upstream_transform
// So this_transform = desired_final_transform * upstream_transform.inverse()
if let Some(current_transform) = current_transform.filter(|transform| transform.matrix2.determinant() != 0. && upstream_transform.matrix2.determinant() != 0.) {
transform = transform * upstream_transform.inverse();
}
let transform = pivot.inverse() * to.inverse() * transform * pivot;
transform_utils::update_transform(inputs, transform);
let final_transform = pivot.inverse() * to.inverse() * transform * pivot;
transform_utils::update_transform(inputs, final_transform);
});
}
fn pivot_set(&mut self, new_pivot: DVec2, bounds: LayerBounds) {
self.modify_inputs("Transform", false, |inputs| {
self.modify_inputs("Transform", false, |inputs, node_id, metadata| {
let layer_transform = transform_utils::get_current_transform(inputs);
let old_pivot_transform = DAffine2::from_translation(bounds.local_pivot(transform_utils::get_current_normalized_pivot(inputs)));
let new_pivot_transform = DAffine2::from_translation(bounds.local_pivot(new_pivot));
let upstream_transform = metadata.upstream_transform(node_id);
let old_pivot_transform = DAffine2::from_translation(upstream_transform.transform_point2(bounds.local_pivot(transform_utils::get_current_normalized_pivot(inputs))));
let new_pivot_transform = DAffine2::from_translation(upstream_transform.transform_point2(bounds.local_pivot(new_pivot)));
let transform = new_pivot_transform.inverse() * old_pivot_transform * layer_transform * old_pivot_transform.inverse() * new_pivot_transform;
transform_utils::update_transform(inputs, transform);
inputs[5] = NodeInput::value(TaggedValue::DVec2(new_pivot), false);
@@ -346,14 +360,15 @@ impl<'a> ModifyInputsContext<'a> {
}
fn update_bounds(&mut self, [old_bounds_min, old_bounds_max]: [DVec2; 2], [new_bounds_min, new_bounds_max]: [DVec2; 2]) {
self.modify_inputs("Transform", false, |inputs| {
self.modify_all_node_inputs("Transform", false, |inputs, node_id, metadata| {
let upstream_transform = metadata.upstream_transform(node_id);
let layer_transform = transform_utils::get_current_transform(inputs);
let normalized_pivot = transform_utils::get_current_normalized_pivot(inputs);
let old_layerspace_pivot = (old_bounds_max - old_bounds_min) * normalized_pivot + old_bounds_min;
let new_layerspace_pivot = (new_bounds_max - new_bounds_min) * normalized_pivot + new_bounds_min;
let new_pivot_transform = DAffine2::from_translation(new_layerspace_pivot);
let old_pivot_transform = DAffine2::from_translation(old_layerspace_pivot);
let new_pivot_transform = DAffine2::from_translation(upstream_transform.transform_point2(new_layerspace_pivot));
let old_pivot_transform = DAffine2::from_translation(upstream_transform.transform_point2(old_layerspace_pivot));
let transform = new_pivot_transform.inverse() * old_pivot_transform * layer_transform * old_pivot_transform.inverse() * new_pivot_transform;
transform_utils::update_transform(inputs, transform);
@@ -369,7 +384,7 @@ impl<'a> ModifyInputsContext<'a> {
let [mut old_bounds_min, mut old_bounds_max] = [DVec2::ZERO, DVec2::ONE];
let [mut new_bounds_min, mut new_bounds_max] = [DVec2::ZERO, DVec2::ONE];
self.modify_inputs("Shape", false, |inputs| {
self.modify_inputs("Shape", false, |inputs, _node_id, _metadata| {
let [subpaths, mirror_angle_groups] = inputs.as_mut_slice() else {
panic!("Shape does not have subpath and mirror angle inputs");
};
@@ -400,13 +415,13 @@ impl<'a> ModifyInputsContext<'a> {
}
fn brush_modify(&mut self, strokes: Vec<BrushStroke>) {
self.modify_inputs("Brush", false, |inputs| {
self.modify_inputs("Brush", false, |inputs, _node_id, _metadata| {
inputs[2] = NodeInput::value(TaggedValue::BrushStrokes(strokes), false);
});
}
fn resize_artboard(&mut self, location: IVec2, dimensions: IVec2) {
self.modify_inputs("Artboard", false, |inputs| {
self.modify_inputs("Artboard", false, |inputs, _node_id, _metadata| {
inputs[1] = NodeInput::value(TaggedValue::IVec2(location), false);
inputs[2] = NodeInput::value(TaggedValue::IVec2(dimensions), false);
});
@@ -443,9 +458,10 @@ impl<'a> ModifyInputsContext<'a> {
}
}
for node_id in delete_nodes {
self.network.nodes.remove(&node_id);
for node_id in &delete_nodes {
self.network.nodes.remove(node_id);
}
self.responses.add(self.document_metadata.retain_selected_nodes(|id| !delete_nodes.contains(id)));
self.responses.add(DocumentMessage::DocumentStructureChanged);
self.responses.add(NodeGraphMessage::SendGraph { should_rerender: true });
@@ -503,29 +519,18 @@ impl MessageHandler<GraphOperationMessage, (&mut Document, &mut NodeGraphMessage
skip_rerender,
} => {
let parent_transform = document.metadata.document_to_viewport * document.multiply_transforms(&layer[..layer.len() - 1]).unwrap_or_default();
let current_transform = document.layer(&layer).ok().map(|layer| layer.transform);
let current_transform = Some(document.metadata.transform_to_viewport(LayerNodeIdentifier::new(*layer.last().unwrap(), &document.document_network)));
let bounds = LayerBounds::new(document, &layer);
if let Some(mut modify_inputs) = ModifyInputsContext::new_layer(&layer, document, node_graph, responses) {
modify_inputs.transform_set(transform, transform_in, parent_transform, current_transform, bounds, skip_rerender);
}
let transform = transform.to_cols_array();
responses.add(match transform_in {
TransformIn::Local => Operation::SetLayerTransform { path: layer, transform },
TransformIn::Scope { scope } => {
let scope = scope.to_cols_array();
Operation::SetLayerTransformInScope { path: layer, transform, scope }
}
TransformIn::Viewport => Operation::SetLayerTransformInViewport { path: layer, transform },
});
}
GraphOperationMessage::TransformSetPivot { layer, pivot } => {
let bounds = LayerBounds::new(document, &layer);
if let Some(mut modify_inputs) = ModifyInputsContext::new_layer(&layer, document, node_graph, responses) {
modify_inputs.pivot_set(pivot, bounds);
}
let pivot = pivot.into();
responses.add(Operation::SetPivot { layer_path: layer, pivot });
}
GraphOperationMessage::Vector { layer, modification } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_layer(&layer, document, node_graph, responses) {
@@ -2,6 +2,7 @@ use crate::messages::portfolio::document::node_graph::VectorDataModification;
use bezier_rs::{ManipulatorGroup, Subpath};
use document_legacy::document::Document;
use document_legacy::document_metadata::LayerNodeIdentifier;
use graph_craft::document::{value::TaggedValue, NodeInput};
use graphene_core::uuid::ManipulatorGroupId;
use graphene_core::vector::{ManipulatorPointId, SelectedType};
@@ -52,18 +53,12 @@ pub struct LayerBounds {
impl LayerBounds {
/// Extract the layer bounds and their transform for a layer.
pub fn new(document: &Document, layer_path: &[u64]) -> Self {
let layer = document.layer(layer_path).ok();
let bounds = layer
.and_then(|layer| layer.as_layer().ok())
.and_then(|frame| frame.as_vector_data().as_ref().map(|vector| vector.nonzero_bounding_box()))
.unwrap_or([DVec2::ZERO, DVec2::ONE]);
let bounds_transform = DAffine2::IDENTITY;
let layer_transform = document.multiply_transforms(layer_path).unwrap_or_default();
pub fn new(document: &Document, layer: &[u64]) -> Self {
let layer = LayerNodeIdentifier::new(*layer.last().unwrap(), &document.document_network);
Self {
bounds,
bounds_transform,
layer_transform,
bounds: document.metadata.nonzero_bounding_box(layer),
bounds_transform: DAffine2::IDENTITY,
layer_transform: document.metadata.transform_to_document(layer),
}
}
@@ -126,7 +121,7 @@ pub fn get_current_normalized_pivot(inputs: &[NodeInput]) -> DVec2 {
if let NodeInput::Value {
tagged_value: TaggedValue::DVec2(pivot),
..
} = inputs[4]
} = inputs[5]
{
pivot
} else {
@@ -4,11 +4,15 @@ use document_legacy::LayerId;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{DocumentNode, NodeId, NodeInput};
#[remain::sorted]
#[impl_message(Message, DocumentMessage, NodeGraph)]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum NodeGraphMessage {
// Messages
Init,
AddSelectNodes {
nodes: Vec<NodeId>,
},
SelectedNodesUpdated,
CloseNodeGraph,
ConnectNodesByLink {
output_node: u64,
@@ -63,9 +67,6 @@ pub enum NodeGraphMessage {
serialized_nodes: String,
},
RunDocumentGraph,
SelectNodes {
nodes: Vec<NodeId>,
},
SendGraph {
should_rerender: bool,
},
@@ -85,6 +86,9 @@ pub enum NodeGraphMessage {
input_index: usize,
value: TaggedValue,
},
SetSelectNodes {
nodes: Vec<NodeId>,
},
ShiftNode {
node_id: NodeId,
},
@@ -119,39 +119,19 @@ impl FrontendNodeType {
#[derive(Debug, Clone, PartialEq, Default, serde::Serialize, serde::Deserialize)]
pub struct NodeGraphMessageHandler {
pub layer_path: Option<Vec<LayerId>>,
pub nested_path: Vec<NodeId>,
pub selected_nodes: Vec<NodeId>,
pub network: Vec<NodeId>,
has_selection: bool,
#[serde(skip)]
pub widgets: [LayoutGroup; 2],
}
impl Into<Message> for document_legacy::document_metadata::SelectionChanged {
fn into(self) -> Message {
BroadcastMessage::TriggerEvent(BroadcastEvent::SelectionChanged).into()
}
}
impl NodeGraphMessageHandler {
fn get_root_network<'a>(&self, document: &'a Document) -> &'a graph_craft::document::NodeNetwork {
self.layer_path
.as_ref()
.and_then(|path| document.root.child(path))
.and_then(|layer| layer.as_layer_network().ok())
.unwrap_or(&document.document_network)
}
fn get_root_network_mut<'a>(&self, document: &'a mut Document) -> &'a mut graph_craft::document::NodeNetwork {
self.layer_path
.as_ref()
.and_then(|path| document.root.child_mut(path))
.and_then(|layer| layer.as_layer_network_mut().ok())
.unwrap_or(&mut document.document_network)
}
/// Get the active graph_craft NodeNetwork struct
fn get_active_network<'a>(&self, document: &'a Document) -> Option<&'a graph_craft::document::NodeNetwork> {
self.get_root_network(document).nested_network(&self.nested_path)
}
/// Get the active graph_craft NodeNetwork struct
fn get_active_network_mut<'a>(&self, document: &'a mut Document) -> Option<&'a mut graph_craft::document::NodeNetwork> {
self.get_root_network_mut(document).nested_network_mut(&self.nested_path)
}
/// Send the cached layout to the frontend for the options bar at the top of the node panel
fn send_node_bar_layout(&self, responses: &mut VecDeque<Message>) {
responses.add(LayoutMessage::SendLayout {
@@ -176,8 +156,8 @@ impl NodeGraphMessageHandler {
None => ("File", "Document"),
};
let mut network = Some(self.get_root_network(document));
for node_id in &self.nested_path {
let mut network = Some(&document.document_network);
for node_id in &self.network {
let node = network.and_then(|network| network.nodes.get(node_id));
if let Some(DocumentNode { name, .. }) = node {
@@ -210,16 +190,16 @@ impl NodeGraphMessageHandler {
/// Updates the buttons for disable and preview
fn update_selection_action_buttons(&mut self, document: &Document, responses: &mut VecDeque<Message>) {
if let Some(network) = self.get_active_network(document) {
if let Some(network) = document.document_network.nested_network(&self.network) {
let mut widgets = Vec::new();
// Don't allow disabling input or output nodes
let mut selected_nodes = self.selected_nodes.iter().filter(|&&id| !network.inputs.contains(&id) && !network.original_outputs_contain(id));
let mut selected_nodes = document.metadata.selected_nodes().filter(|&&id| !network.inputs.contains(&id) && !network.original_outputs_contain(id));
// If there is at least one other selected node then show the hide or show button
if selected_nodes.next().is_some() {
// Check if any of the selected nodes are disabled
let is_hidden = self.selected_nodes.iter().any(|id| network.disabled.contains(id));
let is_hidden = document.metadata.selected_nodes().any(|id| network.disabled.contains(id));
// Check if multiple nodes are selected
let multiple_nodes = selected_nodes.next().is_some();
@@ -234,8 +214,8 @@ impl NodeGraphMessageHandler {
}
// If only one node is selected then show the preview or stop previewing button
if self.selected_nodes.len() == 1 {
let node_id = self.selected_nodes[0];
let mut selected_nodes = document.metadata.selected_nodes();
if let (Some(&node_id), None) = (selected_nodes.next(), selected_nodes.next()) {
// Is this node the current output
let is_output = network.outputs_contain(node_id);
@@ -257,18 +237,20 @@ impl NodeGraphMessageHandler {
/// Collate the properties panel sections for a node graph
pub fn collate_properties(&self, context: &mut NodePropertiesContext, sections: &mut Vec<LayoutGroup>) {
let mut network = context.network;
for segment in &self.nested_path {
let document = context.document;
for segment in &self.network {
network = network.nodes.get(segment).and_then(|node| node.implementation.get_network()).unwrap();
}
// If empty, show all nodes in the network starting with the output
if self.selected_nodes.is_empty() {
if !document.metadata.has_selected_nodes() {
for (document_node, node_id) in network.primary_flow().collect::<Vec<_>>().into_iter().rev() {
sections.push(node_properties::generate_node_properties(document_node, node_id, context));
}
}
// Show properties for all selected nodes
for node_id in &self.selected_nodes {
for node_id in document.metadata.selected_nodes() {
let Some(document_node) = network.nodes.get(node_id) else {
continue;
};
@@ -359,7 +341,7 @@ impl NodeGraphMessageHandler {
fn update_selected(&mut self, document: &mut Document, responses: &mut VecDeque<Message>) {
self.update_selection_action_buttons(document, responses);
responses.add(FrontendMessage::UpdateNodeGraphSelection {
selected: self.selected_nodes.clone(),
selected: document.metadata.selected_nodes_ref().clone(),
});
}
@@ -428,14 +410,16 @@ impl NodeGraphMessageHandler {
}
/// Tries to remove a node from the network, returning true on success.
fn remove_node(&mut self, network: &mut NodeNetwork, node_id: NodeId, reconnect: bool) -> bool {
if Self::remove_references_from_network(network, node_id, reconnect) {
network.nodes.remove(&node_id);
self.selected_nodes.retain(|&id| id != node_id);
true
} else {
false
fn remove_node(&mut self, document: &mut Document, node_id: NodeId, responses: &mut VecDeque<Message>, reconnect: bool) -> bool {
let Some(network) = document.document_network.nested_network_mut(&self.network) else {
return false;
};
if !Self::remove_references_from_network(network, node_id, reconnect) {
return false;
}
network.nodes.remove(&node_id);
responses.add(document.metadata.retain_selected_nodes(|&id| id != node_id));
true
}
/// Gets the default node input based on the node name and the input index
@@ -464,10 +448,26 @@ pub struct NodeGraphHandlerData<'a> {
}
impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGraphMessageHandler {
#[remain::check]
fn process_message(&mut self, message: NodeGraphMessage, responses: &mut VecDeque<Message>, data: NodeGraphHandlerData<'a>) {
#[remain::sorted]
let document = data.document;
let document_id = data.document_id;
match message {
// TODO: automatically remove broadcast messages.
NodeGraphMessage::Init => {
responses.add(BroadcastMessage::SubscribeEvent {
on: BroadcastEvent::SelectionChanged,
send: Box::new(NodeGraphMessage::SelectedNodesUpdated.into()),
});
document.metadata.load_structure(&document.document_network);
responses.add(DocumentMessage::DocumentStructureChanged);
}
NodeGraphMessage::AddSelectNodes { nodes } => {
responses.add(document.metadata.add_selected_nodes(nodes));
}
NodeGraphMessage::SelectedNodesUpdated => {
self.update_selection_action_buttons(document, responses);
self.update_selected(document, responses);
}
NodeGraphMessage::CloseNodeGraph => {}
NodeGraphMessage::ConnectNodesByLink {
output_node,
@@ -477,7 +477,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
} => {
let node_id = input_node;
let Some(network) = self.get_active_network(data.document) else {
let Some(network) = document.document_network.nested_network(&self.network) else {
error!("No network");
return;
};
@@ -489,6 +489,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
error!("Failed to find actual index of connector index {input_node_connector_index} on node {input_node:#?}");
return;
};
document.metadata.load_structure(&document.document_network);
responses.add(DocumentMessage::DocumentStructureChanged);
responses.add(DocumentMessage::StartTransaction);
@@ -499,13 +501,13 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
responses.add(NodeGraphMessage::SendGraph { should_rerender });
}
NodeGraphMessage::Copy => {
let Some(network) = self.get_active_network(data.document) else {
let Some(network) = document.document_network.nested_network(&self.network) else {
error!("No network");
return;
};
// Collect the selected nodes
let new_ids = &self.selected_nodes.iter().copied().enumerate().map(|(new, old)| (old, new as NodeId)).collect();
let new_ids = &document.metadata.selected_nodes().copied().enumerate().map(|(new, old)| (old, new as NodeId)).collect();
let copied_nodes: Vec<_> = Self::copy_nodes(network, new_ids).collect();
// Prefix to show that this is nodes
@@ -540,23 +542,20 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
responses.add(NodeGraphMessage::DeleteSelectedNodes { reconnect: true });
}
NodeGraphMessage::DeleteNode { node_id, reconnect } => {
if let Some(network) = self.get_active_network_mut(data.document) {
self.remove_node(network, node_id, reconnect);
}
self.update_selected(data.document, responses);
self.remove_node(document, node_id, responses, reconnect);
}
NodeGraphMessage::DeleteSelectedNodes { reconnect } => {
responses.add(DocumentMessage::StartTransaction);
for node_id in self.selected_nodes.clone() {
for node_id in document.metadata.selected_nodes().copied() {
responses.add(NodeGraphMessage::DeleteNode { node_id, reconnect });
}
responses.add(NodeGraphMessage::SendGraph { should_rerender: false });
if let Some(network) = self.get_active_network(data.document) {
if let Some(network) = document.document_network.nested_network(&self.network) {
// Only generate node graph if one of the selected nodes is connected to the output
if self.selected_nodes.iter().any(|&node_id| network.connected_to_output(node_id)) {
if document.metadata.selected_nodes().any(|&node_id| network.connected_to_output(node_id)) {
if let Some(layer_path) = self.layer_path.clone() {
responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path });
} else {
@@ -566,7 +565,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
}
}
NodeGraphMessage::DisconnectNodes { node_id, input_index } => {
let Some(network) = self.get_active_network(data.document) else {
let Some(network) = document.document_network.nested_network(&self.network) else {
warn!("No network");
return;
};
@@ -594,55 +593,56 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
responses.add(NodeGraphMessage::SendGraph { should_rerender });
}
NodeGraphMessage::DoubleClickNode { node } => {
if let Some(network) = self.get_active_network(data.document) {
if let Some(network) = document.document_network.nested_network(&self.network) {
if network.nodes.get(&node).and_then(|node| node.implementation.get_network()).is_some() {
self.nested_path.push(node);
self.network.push(node);
}
}
if let Some(network) = self.get_active_network(data.document) {
if let Some(network) = document.document_network.nested_network(&self.network) {
Self::send_graph(network, &self.layer_path, responses);
}
self.collect_nested_addresses(data.document, data.document_name, responses);
self.update_selected(data.document, responses);
self.collect_nested_addresses(document, data.document_name, responses);
self.update_selected(document, responses);
}
NodeGraphMessage::DuplicateSelectedNodes => {
if let Some(network) = self.get_active_network(data.document) {
if let Some(network) = document.document_network.nested_network(&self.network) {
responses.add(DocumentMessage::StartTransaction);
let new_ids = &self.selected_nodes.iter().map(|&id| (id, crate::application::generate_uuid())).collect();
self.selected_nodes.clear();
let new_ids = &document.metadata.selected_nodes().map(|&id| (id, crate::application::generate_uuid())).collect();
responses.add(document.metadata.clear_selected_nodes());
// Copy the selected nodes
let copied_nodes = Self::copy_nodes(network, new_ids).collect::<Vec<_>>();
// Select the new nodes
responses.add(document.metadata.add_selected_nodes(copied_nodes.iter().map(|(node_id, _)| *node_id)));
for (node_id, mut document_node) in copied_nodes {
// Shift duplicated node
document_node.metadata.position += IVec2::splat(2);
// Add new node to the list
self.selected_nodes.push(node_id);
// Insert new node into graph
responses.add(NodeGraphMessage::InsertNode { node_id, document_node });
}
Self::send_graph(network, &self.layer_path, responses);
self.update_selected(data.document, responses);
self.update_selected(document, responses);
responses.add(NodeGraphMessage::SendGraph { should_rerender: false });
}
}
NodeGraphMessage::ExitNestedNetwork { depth_of_nesting } => {
self.selected_nodes.clear();
responses.add(document.metadata.clear_selected_nodes());
for _ in 0..depth_of_nesting {
self.nested_path.pop();
self.network.pop();
}
if let Some(network) = self.get_active_network(data.document) {
if let Some(network) = document.document_network.nested_network(&self.network) {
Self::send_graph(network, &self.layer_path, responses);
}
self.collect_nested_addresses(data.document, data.document_name, responses);
self.update_selected(data.document, responses);
self.collect_nested_addresses(document, data.document_name, responses);
self.update_selected(document, responses);
}
NodeGraphMessage::ExposeInput { node_id, input_index, new_exposed } => {
let Some(network) = self.get_active_network(data.document) else {
let Some(network) = document.document_network.nested_network(&self.network) else {
warn!("No network");
return;
};
@@ -672,17 +672,17 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
responses.add(PropertiesPanelMessage::ResendActiveProperties);
}
NodeGraphMessage::InsertNode { node_id, document_node } => {
if let Some(network) = self.get_active_network_mut(data.document) {
if let Some(network) = document.document_network.nested_network_mut(&self.network) {
network.nodes.insert(node_id, document_node);
}
}
NodeGraphMessage::MoveSelectedNodes { displacement_x, displacement_y } => {
let Some(network) = self.get_active_network_mut(data.document) else {
let Some(network) = document.document_network.nested_network_mut(&self.network) else {
warn!("No network");
return;
};
for node_id in &self.selected_nodes {
for node_id in document.metadata.selected_nodes() {
if let Some(node) = network.nodes.get_mut(node_id) {
node.metadata.position += IVec2::new(displacement_x, displacement_y)
}
@@ -692,19 +692,19 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
NodeGraphMessage::OpenNodeGraph { layer_path } => {
self.layer_path = Some(layer_path);
if let Some(network) = self.get_active_network(data.document) {
self.selected_nodes.clear();
if let Some(network) = document.document_network.nested_network(&self.network) {
responses.add(document.metadata.clear_selected_nodes());
Self::send_graph(network, &self.layer_path, responses);
let node_types = document_node_types::collect_node_types();
responses.add(FrontendMessage::UpdateNodeTypes { node_types });
}
self.collect_nested_addresses(data.document, data.document_name, responses);
self.update_selected(data.document, responses);
self.collect_nested_addresses(document, data.document_name, responses);
self.update_selected(document, responses);
}
NodeGraphMessage::PasteNodes { serialized_nodes } => {
let Some(network) = self.get_active_network(data.document) else {
let Some(network) = document.document_network.nested_network(&self.network) else {
warn!("No network");
return;
};
@@ -746,22 +746,16 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
}
let nodes = new_ids.values().copied().collect();
responses.add(NodeGraphMessage::SelectNodes { nodes });
responses.add(NodeGraphMessage::SetSelectNodes { nodes });
responses.add(NodeGraphMessage::SendGraph { should_rerender: false });
}
NodeGraphMessage::RunDocumentGraph => responses.add(PortfolioMessage::SubmitGraphRender {
document_id: data.document_id,
document_id: document_id,
layer_path: Vec::new(),
}),
NodeGraphMessage::SelectNodes { nodes } => {
self.selected_nodes = nodes;
self.update_selection_action_buttons(data.document, responses);
self.update_selected(data.document, responses);
responses.add(PropertiesPanelMessage::ResendActiveProperties);
}
NodeGraphMessage::SendGraph { should_rerender } => {
if let Some(network) = self.get_active_network(data.document) {
if let Some(network) = document.document_network.nested_network(&self.network) {
Self::send_graph(network, &self.layer_path, responses);
if should_rerender {
if let Some(layer_path) = self.layer_path.clone() {
@@ -772,9 +766,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
}
}
}
NodeGraphMessage::SetInputValue { node_id, input_index, value } => {
if let Some(network) = self.get_active_network(data.document) {
if let Some(network) = document.document_network.nested_network(&self.network) {
if let Some(node) = network.nodes.get(&node_id) {
responses.add(DocumentMessage::StartTransaction);
@@ -792,7 +785,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
}
}
NodeGraphMessage::SetNodeInput { node_id, input_index, input } => {
if let Some(network) = self.get_active_network_mut(data.document) {
if let Some(network) = document.document_network.nested_network_mut(&self.network) {
if let Some(node) = network.nodes.get_mut(&node_id) {
node.inputs[input_index] = input
}
@@ -809,7 +802,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
return;
};
let network = self.get_root_network_mut(data.document).nested_network_mut(node_path);
let network = document.document_network.nested_network_mut(node_path);
if let Some(network) = network {
if let Some(node) = network.nodes.get_mut(node_id) {
@@ -824,8 +817,12 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
}
}
}
NodeGraphMessage::SetSelectNodes { nodes } => {
responses.add(document.metadata.set_selected_nodes(nodes));
responses.add(PropertiesPanelMessage::ResendActiveProperties);
}
NodeGraphMessage::ShiftNode { node_id } => {
let Some(network) = self.get_active_network_mut(data.document) else {
let Some(network) = document.document_network.nested_network_mut(&self.network) else {
warn!("No network");
return;
};
@@ -877,22 +874,22 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
responses.add(NodeGraphMessage::ToggleHiddenImpl);
}
NodeGraphMessage::ToggleHiddenImpl => {
if let Some(network) = self.get_active_network_mut(data.document) {
if let Some(network) = document.document_network.nested_network_mut(&self.network) {
// Check if any of the selected nodes are hidden
if self.selected_nodes.iter().any(|id| network.disabled.contains(id)) {
if document.metadata.selected_nodes().any(|id| network.disabled.contains(id)) {
// Remove all selected nodes from the disabled list
network.disabled.retain(|id| !self.selected_nodes.contains(id));
network.disabled.retain(|id| !document.metadata.selected_nodes_ref().contains(id));
} else {
let original_outputs = network.original_outputs().iter().map(|output| output.node_id).collect::<Vec<_>>();
// Add all selected nodes to the disabled list (excluding input or output nodes)
network
.disabled
.extend(self.selected_nodes.iter().filter(|&id| !network.inputs.contains(id) && !original_outputs.contains(id)));
.extend(document.metadata.selected_nodes().filter(|&id| !network.inputs.contains(id) && !original_outputs.contains(id)));
}
Self::send_graph(network, &self.layer_path, responses);
// Only generate node graph if one of the selected nodes is connected to the output
if self.selected_nodes.iter().any(|&node_id| network.connected_to_output(node_id)) {
if document.metadata.selected_nodes().any(|&node_id| network.connected_to_output(node_id)) {
if let Some(layer_path) = self.layer_path.clone() {
responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path });
} else {
@@ -900,14 +897,14 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
}
}
}
self.update_selection_action_buttons(data.document, responses);
self.update_selection_action_buttons(document, responses);
}
NodeGraphMessage::TogglePreview { node_id } => {
responses.add(DocumentMessage::StartTransaction);
responses.add(NodeGraphMessage::TogglePreviewImpl { node_id });
}
NodeGraphMessage::TogglePreviewImpl { node_id } => {
if let Some(network) = self.get_active_network_mut(data.document) {
if let Some(network) = document.document_network.nested_network_mut(&self.network) {
// Check if the node is not already being previewed
if !network.outputs_contain(node_id) {
network.previous_outputs = Some(network.previous_outputs.to_owned().unwrap_or_else(|| network.outputs.clone()));
@@ -919,7 +916,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
}
Self::send_graph(network, &self.layer_path, responses);
}
self.update_selection_action_buttons(data.document, responses);
self.update_selection_action_buttons(document, responses);
if let Some(layer_path) = self.layer_path.clone() {
responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path });
} else {
@@ -927,22 +924,23 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
}
}
NodeGraphMessage::UpdateNewNodeGraph => {
if let Some(network) = self.get_active_network(data.document) {
self.selected_nodes.clear();
if let Some(network) = document.document_network.nested_network(&self.network) {
responses.add(document.metadata.clear_selected_nodes());
Self::send_graph(network, &self.layer_path, responses);
let node_types = document_node_types::collect_node_types();
responses.add(FrontendMessage::UpdateNodeTypes { node_types });
}
self.collect_nested_addresses(data.document, data.document_name, responses);
self.update_selected(data.document, responses);
self.collect_nested_addresses(document, data.document_name, responses);
self.update_selected(document, responses);
}
}
self.has_selection = document.metadata.has_selected_nodes();
}
fn actions(&self) -> ActionList {
if !self.selected_nodes.is_empty() {
if self.has_selection {
actions!(NodeGraphMessageDiscriminant; DeleteSelectedNodes, Cut, Copy, DuplicateSelectedNodes, ToggleHidden)
} else {
actions!(NodeGraphMessageDiscriminant;)
@@ -2127,7 +2127,38 @@ fn static_nodes() -> Vec<DocumentNodeType> {
DocumentNodeType {
name: "Transform",
category: "Transform",
identifier: NodeImplementation::proto("graphene_core::transform::TransformNode<_, _, _, _, _, _>"),
identifier: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![0, 1, 1, 1, 1, 1],
outputs: vec![NodeOutput::new(1, 0)],
nodes: [
DocumentNode {
name: "Monitor".to_string(),
inputs: vec![NodeInput::Network(concrete!(VectorData))],
implementation: DocumentNodeImplementation::proto("graphene_core::memo::MonitorNode<_>"),
skip_deduplication: true,
..Default::default()
},
DocumentNode {
name: "Transform".to_string(),
inputs: vec![
NodeInput::node(0, 0),
NodeInput::Network(concrete!(DVec2)),
NodeInput::Network(concrete!(f32)),
NodeInput::Network(concrete!(DVec2)),
NodeInput::Network(concrete!(DVec2)),
NodeInput::Network(concrete!(DVec2)),
],
manual_composition: Some(concrete!(Footprint)),
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::transform::TransformNode<_, _, _, _, _, _>")),
..Default::default()
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (id as NodeId, node))
.collect(),
..Default::default()
}),
manual_composition: Some(concrete!(Footprint)),
inputs: vec![
DocumentInputType::value("Vector Data", TaggedValue::VectorData(VectorData::empty()), true),
@@ -1171,7 +1171,7 @@ pub fn logic_operator_properties(document_node: &DocumentNode, node_id: NodeId,
vec![LayoutGroup::Row { widgets }]
}
pub fn transform_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
pub fn transform_properties(document_node: &DocumentNode, node_id: NodeId, context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let translation_assist = |widgets: &mut Vec<WidgetHolder>| {
let pivot_index = 5;
if let NodeInput::Value {
@@ -1182,7 +1182,7 @@ pub fn transform_properties(document_node: &DocumentNode, node_id: NodeId, _cont
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
widgets.push(
PivotAssist::new(pivot.into())
.on_update(|pivot_assist: &PivotAssist| PropertiesPanelMessage::SetPivot { new_position: pivot_assist.position }.into())
.on_update(update_value(|pivot: &PivotAssist| TaggedValue::DVec2(Into::<Option<DVec2>>::into(pivot.position).unwrap()), node_id, 5))
.widget_holder(),
);
} else {
@@ -138,7 +138,7 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
persistent_data,
document: artwork_document,
responses,
nested_path: &node_graph_message_handler.nested_path,
nested_path: &node_graph_message_handler.network,
layer_path: &[],
executor,
network: &artwork_document.document_network,
@@ -108,7 +108,7 @@ pub fn register_artwork_layer_properties(
persistent_data,
document,
responses,
nested_path: &node_graph_message_handler.nested_path,
nested_path: &node_graph_message_handler.network,
layer_path: &layer_path,
executor,
network: &layer.network,
@@ -13,24 +13,23 @@ pub struct DocumentSave {
pub layer_metadata: HashMap<Vec<LayerId>, LayerMetadata>,
}
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize, Hash)]
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize, Hash)]
pub enum FlipAxis {
X,
Y,
}
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize, Hash, specta::Type)]
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize, Hash, specta::Type)]
pub enum AlignAxis {
X,
Y,
}
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize, Hash, specta::Type)]
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize, Hash, specta::Type)]
pub enum AlignAggregate {
Min,
Max,
Center,
Average,
}
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
@@ -1,11 +1,13 @@
use crate::consts::{ROTATE_SNAP_ANGLE, SCALE_SNAP_INTERVAL};
use crate::messages::portfolio::document::node_graph::VectorDataModification;
use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::graph_modification_utils;
use crate::messages::tool::common_functionality::shape_editor::ShapeState;
use crate::messages::tool::utility_types::ToolType;
use document_legacy::document::Document;
use document_legacy::document_metadata::LayerNodeIdentifier;
use document_legacy::layers::style::RenderData;
use document_legacy::LayerId;
use graphene_core::renderer::Quad;
use graphene_core::vector::{ManipulatorPointId, SelectedType};
use glam::{DAffine2, DVec2};
@@ -13,8 +15,8 @@ use std::collections::{HashMap, VecDeque};
#[derive(Debug, PartialEq, Clone)]
pub enum OriginalTransforms {
Layer(HashMap<Vec<LayerId>, DAffine2>),
Path(HashMap<Vec<LayerId>, Vec<(ManipulatorPointId, DVec2)>>),
Layer(HashMap<LayerNodeIdentifier, DAffine2>),
Path(HashMap<LayerNodeIdentifier, Vec<(ManipulatorPointId, DVec2)>>),
}
impl Default for OriginalTransforms {
fn default() -> Self {
@@ -28,6 +30,51 @@ impl OriginalTransforms {
OriginalTransforms::Path(path_map) => path_map.clear(),
}
}
pub fn update<'a>(&mut self, selected: &'a [LayerNodeIdentifier], responses: &'a mut VecDeque<Message>, document: &'a Document, shape_editor: Option<&'a ShapeState>, tool_type: &'a ToolType) {
match self {
OriginalTransforms::Layer(layer_map) => {
for &layer in selected {
if !layer_map.contains_key(&layer) {
layer_map.insert(layer, document.metadata.transform_to_document(layer));
}
}
}
OriginalTransforms::Path(path_map) => {
for &layer in selected {
let Some(shape_editor) = shape_editor else {
warn!("No shape editor structure found, which only happens in select tool, which cannot reach this point as we check for ToolType");
continue;
};
// Anchors also move their handles
let expand_anchors = |&point: &ManipulatorPointId| {
if point.manipulator_type.is_handle() {
[Some(point), None, None]
} else {
[
Some(point),
Some(ManipulatorPointId::new(point.group, SelectedType::InHandle)),
Some(ManipulatorPointId::new(point.group, SelectedType::OutHandle)),
]
}
};
let points = shape_editor.selected_points().flat_map(expand_anchors).flatten();
if path_map.contains_key(&layer) {
continue;
}
let Some(vector_data) = graph_modification_utils::get_subpaths(layer, document) else {
continue;
};
let get_manipulator_point_position = |point_id: ManipulatorPointId| {
graph_modification_utils::get_manipulator_from_id(vector_data, point_id.group)
.and_then(|manipulator_group| point_id.manipulator_type.get_position(manipulator_group))
.map(|position| (point_id, position))
};
path_map.insert(layer, points.filter_map(get_manipulator_point_position).collect());
}
}
}
}
}
#[derive(Default, Debug, Clone, PartialEq, Eq, Copy)]
@@ -264,7 +311,7 @@ impl TransformOperation {
}
pub struct Selected<'a> {
pub selected: &'a [&'a Vec<LayerId>],
pub selected: &'a [LayerNodeIdentifier],
pub responses: &'a mut VecDeque<Message>,
pub document: &'a Document,
pub original_transforms: &'a mut OriginalTransforms,
@@ -277,7 +324,7 @@ impl<'a> Selected<'a> {
pub fn new(
original_transforms: &'a mut OriginalTransforms,
pivot: &'a mut DVec2,
selected: &'a [&'a Vec<LayerId>],
selected: &'a [LayerNodeIdentifier],
responses: &'a mut VecDeque<Message>,
document: &'a Document,
shape_editor: Option<&'a ShapeState>,
@@ -288,57 +335,8 @@ impl<'a> Selected<'a> {
*original_transforms = OriginalTransforms::Layer(HashMap::new());
}
match original_transforms {
OriginalTransforms::Layer(layer_map) => {
for layer_path in selected {
if !layer_map.contains_key(*layer_path) {
if let Ok(layer) = document.layer(layer_path) {
layer_map.insert(layer_path.to_vec(), layer.transform);
} else {
warn!("Didn't find a layer for {:?}", layer_path);
}
}
}
}
OriginalTransforms::Path(path_map) => {
for path in selected {
let Some(shape_editor) = shape_editor else {
warn!("No shape editor structure found, which only happens in select tool, which cannot reach this point as we check for ToolType");
continue;
};
// Anchors also move their handles
let expand_anchors = |&point: &ManipulatorPointId| {
if point.manipulator_type.is_handle() {
[Some(point), None, None]
} else {
[
Some(point),
Some(ManipulatorPointId::new(point.group, SelectedType::InHandle)),
Some(ManipulatorPointId::new(point.group, SelectedType::OutHandle)),
]
}
};
let points = shape_editor.selected_points().flat_map(expand_anchors).flatten();
if path_map.contains_key(*path) {
continue;
}
let Ok(layer) = document.layer(path) else {
warn!("Didn't find a layer for {:?}", path);
continue;
};
let Some(vector_data) = layer.as_vector_data() else {
continue;
};
let get_manipulator_point_position = |point_id: ManipulatorPointId| {
vector_data
.manipulator_from_id(point_id.group)
.and_then(|manipulator_group| point_id.manipulator_type.get_position(manipulator_group))
.map(|position| (point_id, position))
};
path_map.insert(path.to_vec(), points.filter_map(get_manipulator_point_position).collect());
}
}
}
original_transforms.update(selected, responses, document, shape_editor, tool_type);
Self {
selected,
responses,
@@ -351,7 +349,12 @@ impl<'a> Selected<'a> {
}
pub fn mean_average_of_pivots(&mut self, render_data: &RenderData) -> DVec2 {
let xy_summation = self.selected.iter().filter_map(|path| self.document.pivot(path, render_data)).reduce(|a, b| a + b).unwrap_or_default();
let xy_summation = self
.selected
.iter()
.filter_map(|&layer| graph_modification_utils::get_viewport_pivot(layer, self.document))
.reduce(|a, b| a + b)
.unwrap_or_default();
xy_summation / self.selected.len() as f64
}
@@ -360,12 +363,8 @@ impl<'a> Selected<'a> {
let [min, max] = self
.selected
.iter()
.filter_map(|path| {
let multiplied_transform = self.document.multiply_transforms(path).unwrap();
self.document.layer(path).unwrap().aabb_for_transform(multiplied_transform, render_data)
})
.reduce(|a, b| [a[0].min(b[0]), a[1].max(b[1])])
.filter_map(|&layer| self.document.metadata.bounding_box_viewport(layer))
.reduce(Quad::combine_bounds)
.unwrap_or_default();
(min + max) / 2.
}
@@ -376,27 +375,30 @@ impl<'a> Selected<'a> {
let transformation = pivot * delta * pivot.inverse();
// TODO: Cache the result of `shallowest_unique_layers` to avoid this heavy computation every frame of movement, see https://github.com/GraphiteEditor/Graphite/pull/481
for layer_path in Document::shallowest_unique_layers(self.selected.iter()) {
let parent_folder_path = &layer_path[..layer_path.len() - 1];
for layer_ancestors in self.document.metadata.shallowest_unique_layers(self.selected.iter()) {
let layer = *layer_ancestors.last().unwrap();
let parent = layer.parent(&self.document.metadata);
if *self.tool_type == ToolType::Select {
let original_layer_transforms = match self.original_transforms {
OriginalTransforms::Layer(layer_map) => *layer_map.get(*layer_path).unwrap(),
OriginalTransforms::Layer(layer_map) => *layer_map.get(&layer).unwrap(),
OriginalTransforms::Path(_path_map) => {
warn!("Found Path variant in original_transforms, returning identity transform for layer {:?}", layer_path);
warn!("Found Path variant in original_transforms, returning identity transform for layer {:?}", layer);
DAffine2::IDENTITY
}
};
let to = self.document.generate_transform_across_scope(parent_folder_path, None).unwrap();
let to = parent
.map(|parent| self.document.metadata.transform_to_viewport(parent))
.unwrap_or(self.document.metadata.document_to_viewport);
let new = to.inverse() * transformation * to * original_layer_transforms;
self.responses.add(GraphOperationMessage::TransformSet {
layer: layer_path.to_vec(),
layer: layer.to_path(),
transform: new,
transform_in: TransformIn::Local,
skip_rerender: true,
skip_rerender: false,
});
}
if *self.tool_type == ToolType::Path {
let viewspace = self.document.generate_transform_relative_to_viewport(layer_path).ok().unwrap_or_default();
let viewspace = self.document.metadata.transform_to_viewport(layer);
let layerspace_rotation = viewspace.inverse() * transformation;
let initial_points = match self.original_transforms {
@@ -404,7 +406,7 @@ impl<'a> Selected<'a> {
warn!("Found Layer variant in original_transforms when Path wanted, returning identity transform for layer");
None
}
OriginalTransforms::Path(path_map) => path_map.get(*layer_path),
OriginalTransforms::Path(path_map) => path_map.get(&layer),
};
let Some(original) = initial_points else {
@@ -418,7 +420,7 @@ impl<'a> Selected<'a> {
let position = new_pos_viewport;
self.responses.add(GraphOperationMessage::Vector {
layer: (*layer_path).to_vec(),
layer: layer.to_path(),
modification: VectorDataModification::SetManipulatorPosition { point, position },
});
}
@@ -429,23 +431,23 @@ impl<'a> Selected<'a> {
}
pub fn revert_operation(&mut self) {
for path in self.selected.iter().copied() {
for layer in self.selected.iter().copied() {
let original_transform = &self.original_transforms;
match original_transform {
OriginalTransforms::Layer(hash) => {
let Some(matrix) = hash.get(path) else { continue };
let Some(matrix) = hash.get(&layer) else { continue };
self.responses.add(GraphOperationMessage::TransformSet {
layer: path.to_vec(),
layer: layer.to_path(),
transform: *matrix,
transform_in: TransformIn::Local,
skip_rerender: false,
});
}
OriginalTransforms::Path(path) => {
for (layer_path, points) in path {
for (layer, points) in path {
for &(point, position) in points {
self.responses.add(GraphOperationMessage::Vector {
layer: (*layer_path).clone(),
layer: layer.to_path(),
modification: VectorDataModification::SetManipulatorPosition { point, position },
});
}