Fix regressions introduced in the vector nodes migration (#1100)

* Fix double click to enter path tool

* Fix error

* Fix transform bug

* Fix squaring scale on images

* Shift node position and refresh graph

* Downscale node seperate

* Fix mirror

* Remove duplicate transform

* Always show node graph

* Correctly set freehand and spline tool positions

* Run cargo format

* Maybe fix the scale

* Downscaled image is always smaller than origional

* Fix one crash

* Don't show node graph on welcome screen

* Reduce default graph panel height

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
0HyperCube 2023-03-31 21:15:49 +01:00 committed by Keavon Chambers
parent 92fe0bea50
commit d710285029
23 changed files with 105 additions and 118 deletions

View file

@ -230,9 +230,6 @@ pub enum FrontendMessage {
UpdateNodeGraphSelection { UpdateNodeGraphSelection {
selected: Vec<NodeId>, selected: Vec<NodeId>,
}, },
UpdateNodeGraphVisibility {
visible: bool,
},
UpdateNodeTypes { UpdateNodeTypes {
#[serde(rename = "nodeTypes")] #[serde(rename = "nodeTypes")]
node_types: Vec<FrontendNodeType>, node_types: Vec<FrontendNodeType>,

View file

@ -589,10 +589,18 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
warn!("Image node should be in registry"); warn!("Image node should be in registry");
return; return;
}; };
let Some(transform_node_type) = crate::messages::portfolio::document::node_graph::resolve_document_node_type("Transform") else {
warn!("Transform node should be in registry");
return;
};
let Some(downscale_node_type) = crate::messages::portfolio::document::node_graph::resolve_document_node_type("Downscale") else {
warn!("Downscale node should be in registry");
return;
};
let path = vec![generate_uuid()]; let path = vec![generate_uuid()];
let image_node_id = 100; let [image_node_id, transform_node_id, downscale_node_id] = [100, 101, 102];
let mut network = crate::messages::portfolio::document::node_graph::new_image_network(32, image_node_id); let mut network = crate::messages::portfolio::document::node_graph::new_image_network(32, downscale_node_id);
// Transform of parent folder // Transform of parent folder
let to_parent_folder = self.document_legacy.generate_transform_across_scope(&path[..path.len() - 1], None).unwrap_or_default(); let to_parent_folder = self.document_legacy.generate_transform_across_scope(&path[..path.len() - 1], None).unwrap_or_default();
@ -612,16 +620,30 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
responses.push_back(DocumentMessage::StartTransaction.into()); responses.push_back(DocumentMessage::StartTransaction.into());
let mut pos = 8;
let mut next_pos = || {
pos += 8;
graph_craft::document::DocumentNodeMetadata::position((pos, 4))
};
network.nodes.insert( network.nodes.insert(
image_node_id, image_node_id,
image_node_type.to_document_node( image_node_type.to_document_node(
[graph_craft::document::NodeInput::value( [graph_craft::document::NodeInput::value(
graph_craft::document::value::TaggedValue::ImageFrame(ImageFrame { image, transform }), graph_craft::document::value::TaggedValue::ImageFrame(ImageFrame { image, transform: DAffine2::IDENTITY }),
false, false,
)], )],
graph_craft::document::DocumentNodeMetadata::position((20, 4)), next_pos(),
), ),
); );
network.nodes.insert(
transform_node_id,
transform_node_type.to_document_node_default_inputs([Some(graph_craft::document::NodeInput::node(image_node_id, 0))], next_pos()),
);
network.nodes.insert(
downscale_node_id,
downscale_node_type.to_document_node_default_inputs([Some(graph_craft::document::NodeInput::node(transform_node_id, 0))], next_pos()),
);
responses.push_back( responses.push_back(
DocumentOperation::AddNodeGraphFrame { DocumentOperation::AddNodeGraphFrame {

View file

@ -22,7 +22,7 @@ pub enum GraphOperationMessage {
Vector { layer: LayerIdentifier, modification: VectorDataModification }, Vector { layer: LayerIdentifier, modification: VectorDataModification },
} }
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)] #[derive(PartialEq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
pub enum TransformIn { pub enum TransformIn {
Local, Local,
Scope { scope: DAffine2 }, Scope { scope: DAffine2 },

View file

@ -49,6 +49,8 @@ impl<'a> ModifyInputsContext<'a> {
let metadata = output_node.metadata.clone(); let metadata = output_node.metadata.clone();
let new_input = output_node.inputs[0].clone(); let new_input = output_node.inputs[0].clone();
let node_id = generate_uuid(); let node_id = generate_uuid();
output_node.metadata.position.x += 8;
output_node.inputs[0] = NodeInput::node(node_id, 0); output_node.inputs[0] = NodeInput::node(node_id, 0);
let Some(node_type) = resolve_document_node_type(name) else { let Some(node_type) = resolve_document_node_type(name) else {
@ -62,8 +64,8 @@ impl<'a> ModifyInputsContext<'a> {
/// Changes the inputs of a specific node /// Changes the inputs of a specific node
fn modify_inputs(&mut self, name: &'static str, update_input: impl FnOnce(&mut Vec<NodeInput>)) { fn modify_inputs(&mut self, name: &'static str, update_input: impl FnOnce(&mut Vec<NodeInput>)) {
let node_id = self.network.primary_flow().find(|(node, _)| node.name == name).map(|(_, id)| id); let existing_node_id = self.network.primary_flow().find(|(node, _)| node.name == name).map(|(_, id)| id);
if let Some(node_id) = node_id { if let Some(node_id) = existing_node_id {
self.modify_existing_node_inputs(node_id, update_input); self.modify_existing_node_inputs(node_id, update_input);
} else { } else {
self.modify_new_node(name, update_input); self.modify_new_node(name, update_input);
@ -73,6 +75,10 @@ impl<'a> ModifyInputsContext<'a> {
self.responses.add(PropertiesPanelMessage::ResendActiveProperties); self.responses.add(PropertiesPanelMessage::ResendActiveProperties);
let layer_path = self.layer.to_vec(); let layer_path = self.layer.to_vec();
self.responses.add(DocumentMessage::NodeGraphFrameGenerate { layer_path }); self.responses.add(DocumentMessage::NodeGraphFrameGenerate { layer_path });
if existing_node_id.is_none() {
self.responses.add(NodeGraphMessage::SendGraph { should_rerender: false });
}
} }
fn fill_set(&mut self, fill: Fill) { fn fill_set(&mut self, fill: Fill) {
@ -117,8 +123,8 @@ impl<'a> ModifyInputsContext<'a> {
TransformIn::Scope { scope } => scope * parent_transform, TransformIn::Scope { scope } => scope * parent_transform,
TransformIn::Viewport => parent_transform, TransformIn::Viewport => parent_transform,
}; };
let pivot = DAffine2::from_translation(bounds.local_pivot(transform_utils::get_current_normalized_pivot(inputs))); let pivot = DAffine2::from_translation(bounds.layerspace_pivot(transform_utils::get_current_normalized_pivot(inputs)));
let transform = to.inverse() * pivot.inverse() * transform * pivot * to * layer_transform; let transform = to.inverse() * transform * to * layer_transform;
transform_utils::update_transform(inputs, transform); transform_utils::update_transform(inputs, transform);
}); });
} }
@ -130,8 +136,8 @@ impl<'a> ModifyInputsContext<'a> {
TransformIn::Scope { scope } => scope * parent_transform, TransformIn::Scope { scope } => scope * parent_transform,
TransformIn::Viewport => parent_transform, TransformIn::Viewport => parent_transform,
}; };
let pivot = DAffine2::from_translation(bounds.local_pivot(transform_utils::get_current_normalized_pivot(inputs))); let pivot = DAffine2::from_translation(bounds.layerspace_pivot(transform_utils::get_current_normalized_pivot(inputs)));
let transform = to.inverse() * pivot.inverse() * transform * pivot; let transform = to.inverse() * transform * pivot;
transform_utils::update_transform(inputs, transform); transform_utils::update_transform(inputs, transform);
}); });
} }
@ -141,7 +147,7 @@ impl<'a> ModifyInputsContext<'a> {
let layer_transform = transform_utils::get_current_transform(inputs); 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 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 new_pivot_transform = DAffine2::from_translation(bounds.local_pivot(new_pivot));
let transform = new_pivot_transform.inverse() * old_pivot_transform * layer_transform * old_pivot_transform.inverse() * new_pivot_transform; let transform = layer_transform * old_pivot_transform.inverse() * new_pivot_transform;
transform_utils::update_transform(inputs, transform); transform_utils::update_transform(inputs, transform);
inputs[5] = NodeInput::value(TaggedValue::DVec2(new_pivot), false); inputs[5] = NodeInput::value(TaggedValue::DVec2(new_pivot), false);
}); });
@ -184,7 +190,7 @@ impl<'a> ModifyInputsContext<'a> {
let new_pivot_transform = DAffine2::from_translation(new_layerspace_pivot); let new_pivot_transform = DAffine2::from_translation(new_layerspace_pivot);
let old_pivot_transform = DAffine2::from_translation(old_layerspace_pivot); let old_pivot_transform = DAffine2::from_translation(old_layerspace_pivot);
let transform = new_pivot_transform.inverse() * old_pivot_transform * layer_transform * old_pivot_transform.inverse() * new_pivot_transform; let transform = layer_transform * old_pivot_transform.inverse() * new_pivot_transform;
transform_utils::update_transform(inputs, transform); transform_utils::update_transform(inputs, transform);
}); });
} }
@ -231,17 +237,16 @@ impl MessageHandler<GraphOperationMessage, (&mut Document, &mut NodeGraphMessage
let bounds = LayerBounds::new(document, &layer); let bounds = LayerBounds::new(document, &layer);
if let Some(mut modify_inputs) = ModifyInputsContext::new(&layer, document, node_graph, responses) { if let Some(mut modify_inputs) = ModifyInputsContext::new(&layer, document, node_graph, responses) {
modify_inputs.transform_set(transform, transform_in, parent_transform, bounds); modify_inputs.transform_set(transform, transform_in, parent_transform, bounds);
} else {
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 },
});
} }
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 } => { GraphOperationMessage::TransformSetPivot { layer, pivot } => {
let bounds = LayerBounds::new(document, &layer); let bounds = LayerBounds::new(document, &layer);

View file

@ -65,9 +65,6 @@ pub enum NodeGraphMessage {
SendGraph { SendGraph {
should_rerender: bool, should_rerender: bool,
}, },
SetDrawing {
new_drawing: bool,
},
SetInputValue { SetInputValue {
node_id: NodeId, node_id: NodeId,
input_index: usize, input_index: usize,

View file

@ -115,9 +115,6 @@ pub struct NodeGraphMessageHandler {
pub selected_nodes: Vec<graph_craft::document::NodeId>, pub selected_nodes: Vec<graph_craft::document::NodeId>,
#[serde(skip)] #[serde(skip)]
pub widgets: [LayoutGroup; 2], pub widgets: [LayoutGroup; 2],
/// Do not allow the node graph window to open or close whilst the user is drawing a node graph frame
#[serde(skip)]
pub is_drawing_node_graph_frame: bool,
} }
impl NodeGraphMessageHandler { impl NodeGraphMessageHandler {
@ -395,6 +392,15 @@ impl NodeGraphMessageHandler {
.filter_map(|(&id, &new)| network.nodes.get(&id).map(|node| (new, node.clone()))) .filter_map(|(&id, &new)| network.nodes.get(&id).map(|node| (new, node.clone())))
.map(move |(new, node)| (new, node.map_ids(Self::default_node_input, new_ids))) .map(move |(new, node)| (new, node.map_ids(Self::default_node_input, new_ids)))
} }
fn clear_graph(responses: &mut VecDeque<Message>) {
let nodes = Vec::new();
let links = Vec::new();
responses.add(FrontendMessage::UpdateNodeGraph { nodes, links });
responses.add(LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(Vec::new())),
layout_target: crate::messages::layout::utility_types::misc::LayoutTarget::NodeGraphBar,
});
}
} }
impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &[LayerId]>)> for NodeGraphMessageHandler { impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &[LayerId]>)> for NodeGraphMessageHandler {
@ -403,13 +409,8 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
#[remain::sorted] #[remain::sorted]
match message { match message {
NodeGraphMessage::CloseNodeGraph => { NodeGraphMessage::CloseNodeGraph => {
// Don't close when drawing a node graph frame
if self.is_drawing_node_graph_frame {
return;
}
if let Some(_old_layer_path) = self.layer_path.take() { if let Some(_old_layer_path) = self.layer_path.take() {
responses.push_back(FrontendMessage::UpdateNodeGraphVisibility { visible: false }.into()); Self::clear_graph(responses);
responses.push_back(PropertiesPanelMessage::ResendActiveProperties.into()); responses.push_back(PropertiesPanelMessage::ResendActiveProperties.into());
} }
} }
@ -622,16 +623,10 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
Self::send_graph(network, responses); Self::send_graph(network, responses);
} }
NodeGraphMessage::OpenNodeGraph { layer_path } => { NodeGraphMessage::OpenNodeGraph { layer_path } => {
// Don't open when drawing a node graph frame
if self.is_drawing_node_graph_frame {
return;
}
self.layer_path = Some(layer_path); self.layer_path = Some(layer_path);
if let Some(network) = self.get_active_network(document) { if let Some(network) = self.get_active_network(document) {
self.selected_nodes.clear(); self.selected_nodes.clear();
responses.push_back(FrontendMessage::UpdateNodeGraphVisibility { visible: true }.into());
Self::send_graph(network, responses); Self::send_graph(network, responses);
@ -700,25 +695,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
} }
} }
} }
NodeGraphMessage::SetDrawing { new_drawing } => {
let selected: Vec<_> = selected.collect();
// Check if we stopped drawing a node graph frame
if self.is_drawing_node_graph_frame && !new_drawing {
// Check if we should open or close the node graph
if selected.len() == 1
&& document
.layer(selected[0])
.ok()
.filter(|layer| LayerDataTypeDiscriminant::from(&layer.data) == LayerDataTypeDiscriminant::NodeGraphFrame)
.is_some()
{
responses.push_back(NodeGraphMessage::OpenNodeGraph { layer_path: selected[0].to_vec() }.into());
} else {
responses.push_back(NodeGraphMessage::CloseNodeGraph.into());
}
}
self.is_drawing_node_graph_frame = new_drawing
}
NodeGraphMessage::SetInputValue { node_id, input_index, value } => { NodeGraphMessage::SetInputValue { node_id, input_index, value } => {
if let Some(network) = self.get_active_network(document) { if let Some(network) = self.get_active_network(document) {
if let Some(node) = network.nodes.get(&node_id) { if let Some(node) = network.nodes.get(&node_id) {

View file

@ -105,7 +105,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
properties: |_document_node, _node_id, _context| node_properties::string_properties("The identity node simply returns the input"), properties: |_document_node, _node_id, _context| node_properties::string_properties("The identity node simply returns the input"),
}, },
DocumentNodeType { DocumentNodeType {
name: "Image", name: "Downscale",
category: "Ignore", category: "Ignore",
identifier: NodeImplementation::DocumentNode(NodeNetwork { identifier: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![0], inputs: vec![0],
@ -138,7 +138,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
}), }),
inputs: vec![DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), false)], inputs: vec![DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), false)],
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
properties: |_document_node, _node_id, _context| node_properties::string_properties("A bitmap image embedded in this node"), properties: |_document_node, _node_id, _context| node_properties::string_properties("Downscale the image to a lower resolution"),
}, },
// DocumentNodeType { // DocumentNodeType {
// name: "Input", // name: "Input",
@ -473,6 +473,14 @@ fn static_nodes() -> Vec<DocumentNodeType> {
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
properties: node_properties::no_properties, properties: node_properties::no_properties,
}, },
DocumentNodeType {
name: "Image",
category: "Ignore",
identifier: NodeImplementation::proto("graphene_core::ops::IdNode"),
inputs: vec![DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), false)],
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
properties: |_document_node, _node_id, _context| node_properties::string_properties("A bitmap image embedded in this node"),
},
#[cfg(feature = "gpu")] #[cfg(feature = "gpu")]
DocumentNodeType { DocumentNodeType {
name: "GpuImage", name: "GpuImage",

View file

@ -941,7 +941,7 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
// Create the input to the graph using an empty image // Create the input to the graph using an empty image
let image_frame = std::borrow::Cow::Owned(graphene_core::raster::ImageFrame { let image_frame = std::borrow::Cow::Owned(graphene_core::raster::ImageFrame {
image: graphene_core::raster::Image::empty(), image: graphene_core::raster::Image::empty(),
transform, transform: glam::DAffine2::IDENTITY,
}); });
// Compute the transform input to the node graph frame // Compute the transform input to the node graph frame
let image_frame: graphene_core::raster::ImageFrame = context.executor.compute_input(context.network, &imaginate_node, 0, image_frame).unwrap_or_default(); let image_frame: graphene_core::raster::ImageFrame = context.executor.compute_input(context.network, &imaginate_node, 0, image_frame).unwrap_or_default();

View file

@ -320,7 +320,7 @@ pub fn register_artwork_layer_properties(
] ]
} }
LayerDataType::NodeGraphFrame(node_graph_frame) => { LayerDataType::NodeGraphFrame(node_graph_frame) => {
let mut properties_sections = vec![node_section_transform(layer, persistent_data)]; let mut properties_sections = Vec::new();
let mut context = crate::messages::portfolio::document::node_graph::NodePropertiesContext { let mut context = crate::messages::portfolio::document::node_graph::NodePropertiesContext {
persistent_data, persistent_data,

View file

@ -130,9 +130,6 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocess
// Notify the frontend about the new active tool to be displayed // Notify the frontend about the new active tool to be displayed
tool_data.register_properties(responses, LayoutTarget::ToolShelf); tool_data.register_properties(responses, LayoutTarget::ToolShelf);
// Ensure the node graph drawing state is reset
responses.push_back(NodeGraphMessage::SetDrawing { new_drawing: false }.into());
} }
ToolMessage::DeactivateTools => { ToolMessage::DeactivateTools => {
let tool_data = &mut self.tool_state.tool_data; let tool_data = &mut self.tool_state.tool_data;

View file

@ -11,7 +11,7 @@ use document_legacy::LayerId;
use document_legacy::Operation; use document_legacy::Operation;
use graphene_core::vector::style::Stroke; use graphene_core::vector::style::Stroke;
use glam::DVec2; use glam::{DAffine2, DVec2};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Default)] #[derive(Default)]
@ -219,10 +219,16 @@ fn remove_preview(data: &FreehandToolData) -> Message {
fn add_polyline(data: &FreehandToolData, tool_data: &DocumentToolData, responses: &mut VecDeque<Message>) { fn add_polyline(data: &FreehandToolData, tool_data: &DocumentToolData, responses: &mut VecDeque<Message>) {
let layer_path = data.path.clone().unwrap(); let layer_path = data.path.clone().unwrap();
let subpath = bezier_rs::Subpath::from_anchors(data.points.iter().copied(), false); let subpath = bezier_rs::Subpath::from_anchors(data.points.iter().copied(), false);
let position = subpath.bounding_box().unwrap_or_default().into_iter().sum::<DVec2>() / 2.;
graph_modification_utils::new_vector_layer(vec![subpath], layer_path.clone(), responses); graph_modification_utils::new_vector_layer(vec![subpath], layer_path.clone(), responses);
responses.add(GraphOperationMessage::StrokeSet { responses.add(GraphOperationMessage::StrokeSet {
layer: layer_path, layer: layer_path.clone(),
stroke: Stroke::new(tool_data.primary_color, data.weight), stroke: Stroke::new(tool_data.primary_color, data.weight),
}); });
responses.add(GraphOperationMessage::TransformSet {
layer: layer_path,
transform: DAffine2::from_translation(position),
transform_in: TransformIn::Local,
});
} }

View file

@ -114,7 +114,6 @@ impl Fsm for ImaginateToolFsmState {
(Ready, DragStart) => { (Ready, DragStart) => {
shape_data.start(responses, document, input, render_data); shape_data.start(responses, document, input, render_data);
responses.push_back(DocumentMessage::StartTransaction.into()); responses.push_back(DocumentMessage::StartTransaction.into());
responses.push_back(NodeGraphMessage::SetDrawing { new_drawing: true }.into());
shape_data.path = Some(document.get_path_for_new_layer()); shape_data.path = Some(document.get_path_for_new_layer());
responses.push_back(DocumentMessage::DeselectAllLayers.into()); responses.push_back(DocumentMessage::DeselectAllLayers.into());
@ -153,7 +152,6 @@ impl Fsm for ImaginateToolFsmState {
} }
(Drawing, DragStop) => { (Drawing, DragStop) => {
input.mouse.finish_transaction(shape_data.viewport_drag_start(document), responses); input.mouse.finish_transaction(shape_data.viewport_drag_start(document), responses);
responses.push_back(NodeGraphMessage::SetDrawing { new_drawing: false }.into());
shape_data.cleanup(responses); shape_data.cleanup(responses);
Ready Ready
@ -161,8 +159,6 @@ impl Fsm for ImaginateToolFsmState {
(Drawing, Abort) => { (Drawing, Abort) => {
responses.push_back(DocumentMessage::AbortTransaction.into()); responses.push_back(DocumentMessage::AbortTransaction.into());
responses.push_back(NodeGraphMessage::SetDrawing { new_drawing: false }.into());
shape_data.cleanup(responses); shape_data.cleanup(responses);
Ready Ready

View file

@ -114,7 +114,6 @@ impl Fsm for NodeGraphToolFsmState {
(Ready, DragStart) => { (Ready, DragStart) => {
shape_data.start(responses, document, input, render_data); shape_data.start(responses, document, input, render_data);
responses.push_back(DocumentMessage::StartTransaction.into()); responses.push_back(DocumentMessage::StartTransaction.into());
responses.push_back(NodeGraphMessage::SetDrawing { new_drawing: true }.into());
shape_data.path = Some(document.get_path_for_new_layer()); shape_data.path = Some(document.get_path_for_new_layer());
responses.push_back(DocumentMessage::DeselectAllLayers.into()); responses.push_back(DocumentMessage::DeselectAllLayers.into());
@ -141,14 +140,12 @@ impl Fsm for NodeGraphToolFsmState {
} }
(Drawing, DragStop) => { (Drawing, DragStop) => {
input.mouse.finish_transaction(shape_data.viewport_drag_start(document), responses); input.mouse.finish_transaction(shape_data.viewport_drag_start(document), responses);
responses.push_back(NodeGraphMessage::SetDrawing { new_drawing: false }.into());
shape_data.cleanup(responses); shape_data.cleanup(responses);
Ready Ready
} }
(Drawing, Abort) => { (Drawing, Abort) => {
responses.push_back(DocumentMessage::AbortTransaction.into()); responses.push_back(DocumentMessage::AbortTransaction.into());
responses.push_back(NodeGraphMessage::SetDrawing { new_drawing: false }.into());
shape_data.cleanup(responses); shape_data.cleanup(responses);

View file

@ -1202,7 +1202,7 @@ fn edit_layer_shallowest_manipulation(document: &DocumentMessageHandler, interse
} }
fn edit_layer_deepest_manipulation(intersect: &Layer, intersect_layer_path: &Vec<u64>, responses: &mut VecDeque<Message>) { fn edit_layer_deepest_manipulation(intersect: &Layer, intersect_layer_path: &Vec<u64>, responses: &mut VecDeque<Message>) {
match intersect.data { match &intersect.data {
LayerDataType::Text(_) => { LayerDataType::Text(_) => {
responses.push_front(ToolMessage::ActivateTool { tool_type: ToolType::Text }.into()); responses.push_front(ToolMessage::ActivateTool { tool_type: ToolType::Text }.into());
responses.push_back(TextToolMessage::Interact.into()); responses.push_back(TextToolMessage::Interact.into());
@ -1210,11 +1210,8 @@ fn edit_layer_deepest_manipulation(intersect: &Layer, intersect_layer_path: &Vec
LayerDataType::Shape(_) => { LayerDataType::Shape(_) => {
responses.push_front(ToolMessage::ActivateTool { tool_type: ToolType::Path }.into()); responses.push_front(ToolMessage::ActivateTool { tool_type: ToolType::Path }.into());
} }
LayerDataType::NodeGraphFrame(_) => { LayerDataType::NodeGraphFrame(frame) if frame.vector_data.is_some() => {
let replacement_selected_layers = vec![intersect_layer_path.clone()]; responses.push_front(ToolMessage::ActivateTool { tool_type: ToolType::Path }.into());
let layer_path = intersect_layer_path.clone();
responses.push_back(DocumentMessage::SetSelectedLayers { replacement_selected_layers }.into());
responses.push_back(NodeGraphMessage::OpenNodeGraph { layer_path }.into());
} }
_ => {} _ => {}
} }

View file

@ -262,6 +262,7 @@ fn add_spline(tool_data: &SplineToolData, global_tool_data: &DocumentToolData, s
} }
let subpath = bezier_rs::Subpath::new_cubic_spline(points); let subpath = bezier_rs::Subpath::new_cubic_spline(points);
let position = subpath.bounding_box().unwrap_or_default().into_iter().sum::<DVec2>() / 2.;
let layer_path = tool_data.path.clone().unwrap(); let layer_path = tool_data.path.clone().unwrap();
graph_modification_utils::new_vector_layer(vec![subpath], layer_path.clone(), responses); graph_modification_utils::new_vector_layer(vec![subpath], layer_path.clone(), responses);
@ -269,4 +270,9 @@ fn add_spline(tool_data: &SplineToolData, global_tool_data: &DocumentToolData, s
layer: layer_path.clone(), layer: layer_path.clone(),
stroke: Stroke::new(global_tool_data.primary_color, tool_data.weight), stroke: Stroke::new(global_tool_data.primary_color, tool_data.weight),
}); });
responses.add(GraphOperationMessage::TransformSet {
layer: layer_path,
transform: glam::DAffine2::from_translation(position),
transform_in: TransformIn::Local,
})
} }

View file

@ -59,6 +59,7 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
*mouse_position = ipp.mouse.position; *mouse_position = ipp.mouse.position;
*start_mouse = ipp.mouse.position; *start_mouse = ipp.mouse.position;
selected.original_transforms.clear();
}; };
#[remain::sorted] #[remain::sorted]
@ -87,7 +88,6 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
self.transform_operation = TransformOperation::Grabbing(Default::default()); self.transform_operation = TransformOperation::Grabbing(Default::default());
responses.push_back(BroadcastEvent::DocumentIsDirty.into()); responses.push_back(BroadcastEvent::DocumentIsDirty.into());
self.original_transforms.clear();
} }
BeginRotate => { BeginRotate => {
if let TransformOperation::Rotating(_) = self.transform_operation { if let TransformOperation::Rotating(_) = self.transform_operation {
@ -104,7 +104,6 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
self.transform_operation = TransformOperation::Rotating(Default::default()); self.transform_operation = TransformOperation::Rotating(Default::default());
responses.push_back(BroadcastEvent::DocumentIsDirty.into()); responses.push_back(BroadcastEvent::DocumentIsDirty.into());
self.original_transforms.clear();
} }
BeginScale => { BeginScale => {
if let TransformOperation::Scaling(_) = self.transform_operation { if let TransformOperation::Scaling(_) = self.transform_operation {
@ -119,10 +118,8 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
begin_operation(self.transform_operation, &mut self.typing, &mut self.mouse_position, &mut self.start_mouse); begin_operation(self.transform_operation, &mut self.typing, &mut self.mouse_position, &mut self.start_mouse);
self.transform_operation = TransformOperation::Scaling(Default::default()); self.transform_operation = TransformOperation::Scaling(Default::default());
self.transform_operation.apply_transform_operation(&mut selected, self.snap, Axis::Both);
responses.push_back(BroadcastEvent::DocumentIsDirty.into()); responses.push_back(BroadcastEvent::DocumentIsDirty.into());
self.original_transforms.clear();
} }
CancelTransformOperation => { CancelTransformOperation => {
selected.revert_operation(); selected.revert_operation();

View file

@ -15,7 +15,6 @@ impl MessageHandler<WorkspaceMessage, ()> for WorkspaceMessageHandler {
// Messages // Messages
NodeGraphToggleVisibility => { NodeGraphToggleVisibility => {
self.node_graph_visible = !self.node_graph_visible; self.node_graph_visible = !self.node_graph_visible;
responses.push_back(FrontendMessage::UpdateNodeGraphVisibility { visible: self.node_graph_visible }.into());
} }
} }
} }

View file

@ -236,7 +236,7 @@ impl NodeGraphExecutor {
let layer = document.document_legacy.layer(&layer_path).map_err(|e| format!("No layer: {e:?}"))?; let layer = document.document_legacy.layer(&layer_path).map_err(|e| format!("No layer: {e:?}"))?;
// Construct the input image frame // Construct the input image frame
let transform = layer.transform; let transform = DAffine2::IDENTITY;
let image_frame = ImageFrame { image, transform }; let image_frame = ImageFrame { image, transform };
let node_graph_frame = match &layer.data { let node_graph_frame = match &layer.data {

View file

@ -14,8 +14,8 @@
const PANEL_SIZES = { const PANEL_SIZES = {
/**/ root: 100, /**/ root: 100,
/* ├── */ content: 80, /* ├── */ content: 80,
/* │ ├── */ document: 60, /* │ ├── */ document: 80,
/* │ └── */ graph: 40, /* │ └── */ graph: 20,
/* └── */ details: 20, /* └── */ details: 20,
/* ├── */ properties: 45, /* ├── */ properties: 45,
/* └── */ layers: 55, /* └── */ layers: 55,
@ -111,7 +111,7 @@
bind:this={documentPanel} bind:this={documentPanel}
/> />
</LayoutRow> </LayoutRow>
{#if $workspace.nodeGraphVisible} {#if $portfolio.documents.length > 0}
<LayoutRow class="workspace-grid-resize-gutter" data-gutter-vertical on:pointerdown={resizePanel} /> <LayoutRow class="workspace-grid-resize-gutter" data-gutter-vertical on:pointerdown={resizePanel} />
<LayoutRow class="workspace-grid-subdivision" styles={{ "flex-grow": panelSizes["graph"] }} data-subdivision-name="graph"> <LayoutRow class="workspace-grid-subdivision" styles={{ "flex-grow": panelSizes["graph"] }} data-subdivision-name="graph">
<Panel panelType="NodeGraph" tabLabels={[{ name: "Node Graph" }]} tabActiveIndex={0} /> <Panel panelType="NodeGraph" tabLabels={[{ name: "Node Graph" }]} tabActiveIndex={0} />

View file

@ -1,28 +1,15 @@
/* eslint-disable max-classes-per-file */ /* eslint-disable max-classes-per-file */
import {tick} from "svelte";
import {writable} from "svelte/store"; import {writable} from "svelte/store";
import { type Editor } from "@graphite/wasm-communication/editor"; import { type Editor } from "@graphite/wasm-communication/editor";
import { UpdateNodeGraphVisibility } from "@graphite/wasm-communication/messages";
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function createWorkspaceState(editor: Editor) { export function createWorkspaceState(editor: Editor) {
const { subscribe, update } = writable({ const { subscribe, update } = writable({});
nodeGraphVisible: false,
});
// Set up message subscriptions on creation // Set up message subscriptions on creation
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphVisibility, async (updateNodeGraphVisibility) => {
update((state) => {
state.nodeGraphVisible = updateNodeGraphVisibility.visible;
return state;
});
// Update the viewport bounds
await tick();
window.dispatchEvent(new Event("resize"));
});
return { return {
subscribe, subscribe,

View file

@ -44,9 +44,6 @@ export class UpdateNodeGraphSelection extends JsMessage {
readonly selected!: bigint[]; readonly selected!: bigint[];
} }
export class UpdateNodeGraphVisibility extends JsMessage {
readonly visible!: boolean;
}
export class UpdateOpenDocumentsList extends JsMessage { export class UpdateOpenDocumentsList extends JsMessage {
@Type(() => FrontendDocumentDetails) @Type(() => FrontendDocumentDetails)
@ -1432,7 +1429,6 @@ export const messageMakers: Record<string, MessageMaker> = {
UpdateNodeGraphBarLayout, UpdateNodeGraphBarLayout,
UpdateNodeGraphSelection, UpdateNodeGraphSelection,
UpdateNodeTypes, UpdateNodeTypes,
UpdateNodeGraphVisibility,
UpdateOpenDocumentsList, UpdateOpenDocumentsList,
UpdatePropertyPanelOptionsLayout, UpdatePropertyPanelOptionsLayout,
UpdatePropertyPanelSectionsLayout, UpdatePropertyPanelSectionsLayout,

View file

@ -19,7 +19,7 @@ pub struct TransformNode<Translation, Rotation, Scale, Shear, Pivot> {
pub(crate) fn transform_vector_data(mut vector_data: VectorData, translate: DVec2, rotate: f64, scale: DVec2, shear: DVec2, pivot: DVec2) -> VectorData { pub(crate) fn transform_vector_data(mut vector_data: VectorData, translate: DVec2, rotate: f64, scale: DVec2, shear: DVec2, pivot: DVec2) -> VectorData {
let pivot = DAffine2::from_translation(vector_data.local_pivot(pivot)); let pivot = DAffine2::from_translation(vector_data.local_pivot(pivot));
let modification = pivot * DAffine2::from_scale_angle_translation(scale, rotate, translate) * DAffine2::from_cols_array(&[1., shear.y, shear.x, 1., 0., 0.]) * pivot.inverse(); let modification = DAffine2::from_scale_angle_translation(scale, rotate, translate) * DAffine2::from_cols_array(&[1., shear.y, shear.x, 1., 0., 0.]) * pivot.inverse();
vector_data.transform = modification * vector_data.transform; vector_data.transform = modification * vector_data.transform;
vector_data vector_data
@ -40,9 +40,12 @@ where
let rotate = self.rotate.eval(()); let rotate = self.rotate.eval(());
let scale = self.scale.eval(()); let scale = self.scale.eval(());
let shear = self.shear.eval(()); let shear = self.shear.eval(());
let pivot = self.pivot.eval(());
let pivot = DAffine2::from_translation(pivot);
let modification = pivot * DAffine2::from_scale_angle_translation(scale, rotate, translate) * DAffine2::from_cols_array(&[1., shear.y, shear.x, 1., 0., 0.]) * pivot.inverse();
image_frame.transform = modification * image_frame.transform;
let transform = generate_transform(shear, &image_frame.transform, scale, rotate, translate);
image_frame.transform = transform * image_frame.transform;
image_frame image_frame
} }
} }

View file

@ -94,8 +94,8 @@ pub struct DownscaleNode;
#[node_macro::node_fn(DownscaleNode)] #[node_macro::node_fn(DownscaleNode)]
fn downscale(image_frame: ImageFrame) -> ImageFrame { fn downscale(image_frame: ImageFrame) -> ImageFrame {
let target_width = image_frame.transform.transform_vector2((1., 0.).into()).length() as usize; let target_width = (image_frame.transform.transform_vector2((1., 0.).into()).length() as usize).min(image_frame.image.width as usize);
let target_height = image_frame.transform.transform_vector2((0., 1.).into()).length() as usize; let target_height = (image_frame.transform.transform_vector2((0., 1.).into()).length() as usize).min(image_frame.image.height as usize);
let mut image = Image { let mut image = Image {
width: target_width as u32, width: target_width as u32,