Skip to content

Commit 64e3d1c

Browse files
committed
Mostly fix the nested transform cage angle (except leaf layers and skew)
1 parent 64e2751 commit 64e3d1c

File tree

5 files changed

+44
-17
lines changed

5 files changed

+44
-17
lines changed

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

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -237,44 +237,51 @@ impl<'a> ModifyInputsContext<'a> {
237237
}
238238
})
239239
}
240+
240241
/// Gets the node id of a node with a specific reference that is upstream from the layer node, and optionally creates it if it does not exist.
241242
/// The returned node is based on the selection dots in the layer. The right most dot will always insert/access the path that flows directly into the layer.
242243
/// Each dot after that represents an existing path node. If there is an existing upstream node, then it will always be returned first.
243-
pub fn existing_node_id(&mut self, reference: &'static str, create_if_nonexistent: bool) -> Option<NodeId> {
244+
pub fn existing_node_id(&mut self, reference_name: &'static str, create_if_nonexistent: bool) -> Option<NodeId> {
244245
// Start from the layer node or export
245246
let output_layer = self.get_output_layer()?;
246247

247-
let existing_node_id = Self::locate_existing_id(output_layer, self.network_interface, reference);
248+
let existing_node_id = Self::locate_node_in_layer_chain(reference_name, output_layer, self.network_interface);
248249

249250
// Create a new node if the node does not exist and update its inputs
250251
if create_if_nonexistent {
251-
return existing_node_id.or_else(|| self.create_node(reference));
252+
return existing_node_id.or_else(|| self.create_node(reference_name));
252253
}
253254

254255
existing_node_id
255256
}
256257

257-
/// Gets the node id of a node with a specific reference that is upstream from the layer node.
258-
pub fn locate_existing_id(output_layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface, reference: &str) -> Option<NodeId> {
259-
let upstream = network_interface.upstream_flow_back_from_nodes(vec![output_layer.to_node()], &[], network_interface::FlowType::HorizontalFlow);
258+
/// Gets the node id of a node with a specific reference (name) that is upstream (leftward) from the layer node, but before reaching another upstream layer stack.
259+
/// For example, if given a group layer, this would find a requested "Transform" or "Boolean Operation" node in its chain, between the group layer and its layer stack child contents.
260+
/// It would also travel up an entire layer that's not fed by a stack until reaching the generator node, such as a "Rectangle" or "Path" layer.
261+
pub fn locate_node_in_layer_chain(reference_name: &str, left_of_layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
262+
let upstream = network_interface.upstream_flow_back_from_nodes(vec![left_of_layer.to_node()], &[], network_interface::FlowType::HorizontalFlow);
260263

261264
// Look at all of the upstream nodes
262265
for upstream_node in upstream {
263266
// Check if this is the node we have been searching for.
264267
if network_interface
265268
.reference(&upstream_node, &[])
266-
.is_some_and(|node_reference| *node_reference == Some(reference.to_string()))
269+
.is_some_and(|node_reference| *node_reference == Some(reference_name.to_string()))
267270
{
271+
if !network_interface.is_visible(&upstream_node, &[]) {
272+
continue;
273+
}
274+
268275
return Some(upstream_node);
269276
}
270277

271-
let is_traversal_start = |node_id: NodeId| output_layer.to_node() == node_id || network_interface.document_network().exports.iter().any(|export| export.as_node() == Some(node_id));
272-
273278
// Take until another layer node is found (but not the first layer node)
279+
let is_traversal_start = |node_id: NodeId| left_of_layer.to_node() == node_id || network_interface.document_network().exports.iter().any(|export| export.as_node() == Some(node_id));
274280
if !is_traversal_start(upstream_node) && (network_interface.is_layer(&upstream_node, &[])) {
275281
return None;
276282
}
277283
}
284+
278285
None
279286
}
280287

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ impl OriginalTransforms {
5757

5858
/// Gets the transform from the most downstream transform node
5959
fn get_layer_transform(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<DAffine2> {
60-
let transform_node_id = ModifyInputsContext::locate_existing_id(layer, network_interface, "Transform")?;
60+
let transform_node_id = ModifyInputsContext::locate_node_in_layer_chain("Transform", layer, network_interface)?;
6161

6262
let document_node = network_interface.document_network().nodes.get(&transform_node_id)?;
6363
Some(transform_utils::get_current_transform(&document_node.inputs))

editor/src/messages/tool/tool_messages/select_tool.rs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::consts::{
66
SELECTION_DRAG_ANGLE, SELECTION_TOLERANCE,
77
};
88
use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition;
9-
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
9+
use crate::messages::portfolio::document::graph_operation::utility_types::{ModifyInputsContext, TransformIn};
1010
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
1111
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
1212
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis, GroupFolderType};
@@ -23,6 +23,7 @@ use crate::messages::tool::common_functionality::transformation_cage::*;
2323
use crate::messages::tool::common_functionality::{auto_panning::AutoPanning, measure};
2424

2525
use bezier_rs::Subpath;
26+
use graph_craft::document::value::TaggedValue;
2627
use graph_craft::document::NodeId;
2728
use graphene_core::renderer::Quad;
2829
use graphene_core::text::load_face;
@@ -523,16 +524,34 @@ impl Fsm for SelectToolFsmState {
523524
}
524525

525526
// Update bounds
526-
let transform = document
527+
let mut transform = document
527528
.network_interface
528529
.selected_nodes()
529530
.selected_visible_and_unlocked_layers(&document.network_interface)
530531
.find(|layer| !document.network_interface.is_artboard(&layer.to_node(), &[]))
531-
.map(|layer| document.metadata().transform_to_viewport(layer));
532+
.map(|layer| {
533+
let transform = document.metadata().transform_to_viewport(layer);
534+
535+
if let Some(node) =
536+
ModifyInputsContext::locate_node_in_layer_chain("Transform", layer, &document.network_interface).and_then(|node_id| document.network_interface.document_node(&node_id, &[]))
537+
{
538+
if let (Some(&TaggedValue::F64(angle)), Some(&TaggedValue::DVec2(skew))) = (node.inputs[2].as_value(), node.inputs[3].as_value()) {
539+
// TODO: Figure out and fix why this incorrectly applies the rotation twice if it's a leaf layer (VectorData, ImageFrame, etc. rather than a group)
540+
return DAffine2::from_angle(angle) * transform;
541+
542+
// TODO: Include skew in the transform cage, since rotation and skew are the two parts that affect the basis for the space in which the bounding box is calculated
543+
// let mut skew_matrix = DAffine2::IDENTITY;
544+
// skew_matrix.matrix2.x_axis[0] = skew.x.tan();
545+
// skew_matrix.matrix2.y_axis[1] = skew.y.tan();
546+
}
547+
};
548+
549+
transform
550+
})
551+
.unwrap_or(DAffine2::IDENTITY);
532552

533-
let mut transform = transform.unwrap_or(DAffine2::IDENTITY);
534-
let mut transform_tampered = false;
535553
// Check if the matrix is not invertible
554+
let mut transform_tampered = false;
536555
if transform.matrix2.determinant() == 0. {
537556
transform.matrix2 += DMat2::IDENTITY * 1e-4; // TODO: Is this the cleanest way to handle this?
538557
transform_tampered = true;
@@ -549,6 +568,7 @@ impl Fsm for SelectToolFsmState {
549568
.bounding_box_with_transform(layer, transform.inverse() * document.metadata().transform_to_viewport(layer))
550569
})
551570
.reduce(graphene_core::renderer::Quad::combine_bounds);
571+
552572
if let Some(bounds) = bounds {
553573
let bounding_box_manager = tool_data.bounding_box_manager.get_or_insert(BoundingBoxManager::default());
554574

node-graph/gcore/src/graphic_element.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,8 @@ async fn layer(_: impl Ctx, mut stack: GraphicGroupTable, element: GraphicElemen
289289
stack
290290
}
291291

292-
#[node_macro::node(category("General"))]
292+
// TODO: Once we have nicely working spreadsheet tables, test this and make it nicely user-facing and move it from "Debug" to "General"
293+
#[node_macro::node(category("Debug"))]
293294
async fn concatenate<T: Clone>(
294295
_: impl Ctx,
295296
#[implementations(

node-graph/gcore/src/graphic_element/renderer.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,6 @@ impl GraphicElementRendered for GraphicGroupTable {
358358
click_target.apply_transform(*instance.transform)
359359
}
360360

361-
log::debug!("new_click_targets for {graphic_group_id:?}: {new_click_targets:?}");
362361
all_upstream_click_targets.extend(new_click_targets);
363362
}
364363

0 commit comments

Comments
 (0)