mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-09 15:58:01 +00:00
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:
parent
92fe0bea50
commit
d710285029
23 changed files with 105 additions and 118 deletions
|
@ -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>,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 },
|
||||||
|
|
|
@ -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,7 +237,7 @@ 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();
|
let transform = transform.to_cols_array();
|
||||||
responses.add(match transform_in {
|
responses.add(match transform_in {
|
||||||
TransformIn::Local => Operation::SetLayerTransform { path: layer, transform },
|
TransformIn::Local => Operation::SetLayerTransform { path: layer, transform },
|
||||||
|
@ -242,7 +248,6 @@ impl MessageHandler<GraphOperationMessage, (&mut Document, &mut NodeGraphMessage
|
||||||
TransformIn::Viewport => Operation::SetLayerTransformInViewport { path: layer, transform },
|
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);
|
||||||
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) {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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());
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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} />
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue