Skip to content

Commit 9f9dd71

Browse files
authored
Fix vector drawing tool transform space handling (#3872)
* Fix vector drawing tool transform space handling * Review fixes * Fix test
1 parent 20501ca commit 9f9dd71

File tree

12 files changed

+381
-125
lines changed

12 files changed

+381
-125
lines changed

editor/src/messages/portfolio/document/graph_operation/utility_types.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,8 @@ impl<'a> ModifyInputsContext<'a> {
201201
Some(NodeInput::value(TaggedValue::F64(typesetting.character_spacing), false)),
202202
Some(NodeInput::value(TaggedValue::Bool(typesetting.max_width.is_some()), false)),
203203
Some(NodeInput::value(TaggedValue::F64(typesetting.max_width.unwrap_or(100.)), false)),
204-
Some(NodeInput::value(TaggedValue::Bool(typesetting.max_width.is_some()), false)),
205-
Some(NodeInput::value(TaggedValue::F64(typesetting.max_width.unwrap_or(100.)), false)),
204+
Some(NodeInput::value(TaggedValue::Bool(typesetting.max_height.is_some()), false)),
205+
Some(NodeInput::value(TaggedValue::F64(typesetting.max_height.unwrap_or(100.)), false)),
206206
Some(NodeInput::value(TaggedValue::F64(typesetting.tilt), false)),
207207
Some(NodeInput::value(TaggedValue::TextAlign(typesetting.align), false)),
208208
]);

editor/src/messages/portfolio/document_migration.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1708,6 +1708,132 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
17081708
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[2].clone(), network_path);
17091709
}
17101710

1711+
// Migrate old Arrow node from (start, end, shaft_width, head_width, head_length) to (arrow_to, shaft_width, head_width, head_length) with a Transform node for positioning
1712+
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector_nodes::arrow::IDENTIFIER) && inputs_count == 6 {
1713+
// Read old start and end values
1714+
let start = match node.inputs.get(1)? {
1715+
NodeInput::Value { tagged_value, .. } => {
1716+
if let TaggedValue::DVec2(v) = *tagged_value.clone().into_inner() {
1717+
v
1718+
} else {
1719+
DVec2::ZERO
1720+
}
1721+
}
1722+
_ => DVec2::ZERO,
1723+
};
1724+
let end = match node.inputs.get(2)? {
1725+
NodeInput::Value { tagged_value, .. } => {
1726+
if let TaggedValue::DVec2(v) = *tagged_value.clone().into_inner() {
1727+
v
1728+
} else {
1729+
DVec2::new(100., 0.)
1730+
}
1731+
}
1732+
_ => DVec2::new(100., 0.),
1733+
};
1734+
1735+
// Replace inputs with the new node definition (primary + arrow_to + shaft_width + head_width + head_length)
1736+
let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
1737+
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
1738+
1739+
// Preserve primary input connection
1740+
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
1741+
// Set arrow_to = end - start
1742+
document
1743+
.network_interface
1744+
.set_input(&InputConnector::node(*node_id, 1), NodeInput::value(TaggedValue::DVec2(end - start), false), network_path);
1745+
// Preserve shaft_width, head_width, head_length (shifted from indices 3,4,5 to 2,3,4)
1746+
document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[3].clone(), network_path);
1747+
document.network_interface.set_input(&InputConnector::node(*node_id, 3), old_inputs[4].clone(), network_path);
1748+
document.network_interface.set_input(&InputConnector::node(*node_id, 4), old_inputs[5].clone(), network_path);
1749+
1750+
// Find downstream connection to insert Transform node
1751+
let downstream = document
1752+
.network_interface
1753+
.outward_wires(network_path)
1754+
.and_then(|wires| wires.get(&OutputConnector::node(*node_id, 0)))
1755+
.and_then(|connections| connections.first().cloned());
1756+
1757+
if let Some(downstream_input) = downstream {
1758+
// Create a Transform node with translation = start
1759+
let Some(transform_node_type) = resolve_network_node_type("Transform") else {
1760+
log::error!("Transform node definition not found during Arrow migration");
1761+
return None;
1762+
};
1763+
let mut transform_template = transform_node_type.default_node_template();
1764+
transform_template.document_node.inputs[1] = NodeInput::value(TaggedValue::DVec2(start), false);
1765+
1766+
let transform_id = NodeId::new();
1767+
1768+
// Position the Transform node to the right of the Arrow node
1769+
let arrow_position = document.network_interface.position(node_id, network_path).unwrap_or_default();
1770+
document.network_interface.insert_node(transform_id, transform_template, network_path);
1771+
document.network_interface.shift_absolute_node_position(&transform_id, arrow_position + IVec2::new(7, 0), network_path);
1772+
document.network_interface.insert_node_between(&transform_id, &downstream_input, 0, network_path);
1773+
}
1774+
}
1775+
1776+
// Migrate old Line node from (start, end) to (line_to) with a Transform node for positioning
1777+
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::line::IDENTIFIER) && inputs_count == 3 {
1778+
// Read old start and end values
1779+
let start = match node.inputs.get(1)? {
1780+
NodeInput::Value { tagged_value, .. } => {
1781+
if let TaggedValue::DVec2(v) = *tagged_value.clone().into_inner() {
1782+
v
1783+
} else {
1784+
DVec2::ZERO
1785+
}
1786+
}
1787+
_ => DVec2::ZERO,
1788+
};
1789+
let end = match node.inputs.get(2)? {
1790+
NodeInput::Value { tagged_value, .. } => {
1791+
if let TaggedValue::DVec2(v) = *tagged_value.clone().into_inner() {
1792+
v
1793+
} else {
1794+
DVec2::new(100., 100.)
1795+
}
1796+
}
1797+
_ => DVec2::new(100., 100.),
1798+
};
1799+
1800+
// Replace inputs with the new node definition (primary + line_to)
1801+
let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
1802+
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
1803+
1804+
// Preserve primary input connection
1805+
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
1806+
// Set line_to = end - start
1807+
document
1808+
.network_interface
1809+
.set_input(&InputConnector::node(*node_id, 1), NodeInput::value(TaggedValue::DVec2(end - start), false), network_path);
1810+
1811+
// Find downstream connection to insert Transform node
1812+
let downstream = document
1813+
.network_interface
1814+
.outward_wires(network_path)
1815+
.and_then(|wires| wires.get(&OutputConnector::node(*node_id, 0)))
1816+
.and_then(|connections| connections.first().cloned());
1817+
1818+
if let Some(downstream_input) = downstream {
1819+
// Create a Transform node with translation = start
1820+
let Some(transform_node_type) = resolve_network_node_type("Transform") else {
1821+
log::error!("Transform node definition not found during Line migration");
1822+
return None;
1823+
};
1824+
let mut transform_template = transform_node_type.default_node_template();
1825+
transform_template.document_node.inputs[1] = NodeInput::value(TaggedValue::DVec2(start), false);
1826+
1827+
let transform_id = NodeId::new();
1828+
1829+
// Position the Transform node to the right of the Line node
1830+
let line_position = document.network_interface.position(node_id, network_path).unwrap_or_default();
1831+
document.network_interface.insert_node(transform_id, transform_template, network_path);
1832+
document.network_interface.shift_absolute_node_position(&transform_id, line_position + IVec2::new(7, 0), network_path);
1833+
document.network_interface.insert_node_between(&transform_id, &downstream_input, 0, network_path);
1834+
}
1835+
}
1836+
17111837
// Add context features to nodes that don't have them (fine-grained context caching migration)
17121838
if node.context_features == graphene_std::ContextDependencies::default()
17131839
&& let Some(reference) = document.network_interface.reference(node_id, network_path).clone()

editor/src/messages/tool/common_functionality/shapes/arrow_shape.rs

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use super::shape_utility::ShapeToolModifierKey;
22
use super::*;
3-
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_proto_node_type;
3+
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
4+
use crate::messages::portfolio::document::node_graph::document_node_definitions::{DefinitionIdentifier, resolve_document_node_type};
45
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
56
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
67
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
78
use crate::messages::prelude::*;
89
use crate::messages::tool::common_functionality::graph_modification_utils;
9-
use glam::DVec2;
10+
use glam::{DAffine2, DVec2};
1011
use graph_craft::document::NodeInput;
1112
use graph_craft::document::value::TaggedValue;
1213
use std::collections::VecDeque;
@@ -15,16 +16,15 @@ use std::collections::VecDeque;
1516
pub struct Arrow;
1617

1718
impl Arrow {
18-
pub fn create_node(document: &DocumentMessageHandler, drag_start: DVec2, shaft_width: f64, head_width: f64, head_length: f64) -> NodeTemplate {
19-
let node_type = resolve_proto_node_type(graphene_std::vector_nodes::arrow::IDENTIFIER).expect("Arrow node does not exist");
20-
let viewport_pos = document.metadata().document_to_viewport.transform_point2(drag_start);
19+
pub fn create_node(shaft_width: f64, head_width: f64, head_length: f64) -> NodeTemplate {
20+
let identifier = DefinitionIdentifier::ProtoNode(graphene_std::vector_nodes::arrow::IDENTIFIER);
21+
let node_type = resolve_document_node_type(&identifier).expect("Arrow node can't be found");
2122
node_type.node_template_input_override([
2223
None,
23-
Some(NodeInput::value(TaggedValue::DVec2(viewport_pos), false)), // start
24-
Some(NodeInput::value(TaggedValue::DVec2(viewport_pos), false)), // end
25-
Some(NodeInput::value(TaggedValue::F64(shaft_width), false)), // shaft_width
26-
Some(NodeInput::value(TaggedValue::F64(head_width), false)), // head_width
27-
Some(NodeInput::value(TaggedValue::F64(head_length), false)), // head_length
24+
Some(NodeInput::value(TaggedValue::DVec2(DVec2::ZERO), false)), // arrow_to
25+
Some(NodeInput::value(TaggedValue::F64(shaft_width), false)), // shaft_width
26+
Some(NodeInput::value(TaggedValue::F64(head_width), false)), // head_width
27+
Some(NodeInput::value(TaggedValue::F64(head_length), false)), // head_length
2828
])
2929
}
3030

@@ -40,30 +40,32 @@ impl Arrow {
4040
// Track current mouse position in viewport space
4141
tool_data.line_data.drag_current = input.mouse.position;
4242

43-
// Convert both points to document space (matching Line tool pattern)
43+
// Compute arrow_to in document space
4444
let document_to_viewport = document.metadata().document_to_viewport;
4545
let start_document = tool_data.data.drag_start;
46-
let end_document = document_to_viewport.inverse().transform_point2(tool_data.line_data.drag_current);
46+
let end_document = document_to_viewport.inverse().transform_point2(input.mouse.position);
47+
let arrow_to = end_document - start_document;
4748

48-
// Calculate length in document space for validation
49-
let delta = end_document - start_document;
50-
let length_document = delta.length();
51-
if length_document < 1e-6 {
49+
if arrow_to.length() < 1e-6 {
5250
return;
5351
}
5452

5553
let Some(node_id) = graph_modification_utils::get_arrow_id(layer, &document.network_interface) else {
5654
return;
5755
};
5856

59-
// Update Arrow node start and end points with document space coordinates
57+
// Update Arrow node arrow_to in document space
6058
responses.add(NodeGraphMessage::SetInput {
6159
input_connector: InputConnector::node(node_id, 1),
62-
input: NodeInput::value(TaggedValue::DVec2(start_document), false),
60+
input: NodeInput::value(TaggedValue::DVec2(arrow_to), false),
6361
});
64-
responses.add(NodeGraphMessage::SetInput {
65-
input_connector: InputConnector::node(node_id, 2),
66-
input: NodeInput::value(TaggedValue::DVec2(end_document), false),
62+
let downstream = document.metadata().downstream_transform_to_viewport(layer);
63+
let scope = downstream.inverse() * document_to_viewport;
64+
responses.add(GraphOperationMessage::TransformSet {
65+
layer,
66+
transform: DAffine2::from_translation(start_document),
67+
transform_in: TransformIn::Scope { scope },
68+
skip_rerender: false,
6769
});
6870

6971
responses.add(NodeGraphMessage::RunDocumentGraph);

0 commit comments

Comments
 (0)