Skip to content

Commit 8daf183

Browse files
0HyperCubeTrueDoctorKeavon
authored
Migrate the Select tool to the document graph (#1433)
* function for accessing document metadata * Better select tool * Fix render * Fix transforms * Fix loading saved documents * Populate graph UI when loading autosave * Multiple transform nodes * Fix deep select * Graph tooltips * Fix flip axis icon * Show disabled widgets * Stop select tool from selecting artboards * Disable (not hide) the pivot widget; remove Deep/Shallow select for now * Code review changes * Fix pivot position with select tool * Fix incorrectly selected layers when shift clicking --------- Co-authored-by: Dennis Kobert <[email protected]> Co-authored-by: Keavon Chambers <[email protected]>
1 parent 8dc12ad commit 8daf183

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1041
-1215
lines changed

document-legacy/src/document_metadata.rs

Lines changed: 177 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@ use graphene_core::renderer::ClickTarget;
33
use std::collections::HashMap;
44
use std::num::NonZeroU64;
55

6-
use graph_craft::document::{NodeId, NodeNetwork};
6+
use graph_craft::document::{DocumentNode, NodeId, NodeNetwork};
77

88
use graphene_core::renderer::Quad;
99

1010
#[derive(Debug, Clone)]
1111
pub struct DocumentMetadata {
1212
transforms: HashMap<LayerNodeIdentifier, DAffine2>,
13+
upstream_transforms: HashMap<NodeId, DAffine2>,
1314
structure: HashMap<LayerNodeIdentifier, NodeRelations>,
1415
click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>,
16+
selected_nodes: Vec<NodeId>,
1517
/// Transform from document space to viewport space.
1618
pub document_to_viewport: DAffine2,
1719
}
@@ -20,13 +22,17 @@ impl Default for DocumentMetadata {
2022
fn default() -> Self {
2123
Self {
2224
transforms: HashMap::new(),
25+
upstream_transforms: HashMap::new(),
2326
click_targets: HashMap::new(),
2427
structure: HashMap::from_iter([(LayerNodeIdentifier::ROOT, NodeRelations::default())]),
28+
selected_nodes: Vec::new(),
2529
document_to_viewport: DAffine2::IDENTITY,
2630
}
2731
}
2832
}
33+
pub struct SelectionChanged;
2934

35+
// layer iters
3036
impl DocumentMetadata {
3137
/// Get the root layer from the document
3238
pub const fn root(&self) -> LayerNodeIdentifier {
@@ -38,15 +44,27 @@ impl DocumentMetadata {
3844
}
3945

4046
pub fn selected_layers(&self) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
41-
self.all_layers()
47+
self.all_layers().filter(|layer| self.selected_nodes.contains(&layer.to_node()))
4248
}
4349

4450
pub fn selected_layers_contains(&self, layer: LayerNodeIdentifier) -> bool {
4551
self.selected_layers().any(|selected| selected == layer)
4652
}
4753

4854
pub fn selected_visible_layers(&self) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
49-
self.all_layers()
55+
self.selected_layers()
56+
}
57+
58+
pub fn selected_nodes(&self) -> core::slice::Iter<'_, NodeId> {
59+
self.selected_nodes.iter()
60+
}
61+
62+
pub fn selected_nodes_ref(&self) -> &Vec<NodeId> {
63+
&self.selected_nodes
64+
}
65+
66+
pub fn has_selected_nodes(&self) -> bool {
67+
!self.selected_nodes.is_empty()
5068
}
5169

5270
/// Access the [`NodeRelations`] of a layer
@@ -59,35 +77,125 @@ impl DocumentMetadata {
5977
self.structure.entry(node_identifier).or_default()
6078
}
6179

62-
/// Update the cached transforms of the layers
63-
pub fn update_transforms(&mut self, new_transforms: HashMap<LayerNodeIdentifier, DAffine2>) {
64-
self.transforms = new_transforms;
80+
pub fn shallowest_unique_layers<'a>(&self, layers: impl Iterator<Item = &'a LayerNodeIdentifier>) -> Vec<Vec<LayerNodeIdentifier>> {
81+
let mut sorted_layers = layers
82+
.map(|layer| {
83+
let mut layer_path = layer.ancestors(self).collect::<Vec<_>>();
84+
layer_path.reverse();
85+
layer_path
86+
})
87+
.collect::<Vec<_>>();
88+
sorted_layers.sort();
89+
// Sorting here creates groups of similar UUID paths
90+
sorted_layers.dedup_by(|a, b| a.starts_with(b));
91+
sorted_layers
6592
}
93+
}
6694

67-
/// Update the cached click targets of the layers
68-
pub fn update_click_targets(&mut self, new_click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>) {
69-
self.click_targets = new_click_targets;
95+
// selected layer modifications
96+
impl DocumentMetadata {
97+
#[must_use]
98+
pub fn retain_selected_nodes(&mut self, f: impl FnMut(&NodeId) -> bool) -> SelectionChanged {
99+
self.selected_nodes.retain(f);
100+
SelectionChanged
101+
}
102+
#[must_use]
103+
pub fn set_selected_nodes(&mut self, new: Vec<NodeId>) -> SelectionChanged {
104+
self.selected_nodes = new;
105+
SelectionChanged
106+
}
107+
#[must_use]
108+
pub fn add_selected_nodes(&mut self, iter: impl IntoIterator<Item = NodeId>) -> SelectionChanged {
109+
self.selected_nodes.extend(iter);
110+
SelectionChanged
111+
}
112+
#[must_use]
113+
pub fn clear_selected_nodes(&mut self) -> SelectionChanged {
114+
self.set_selected_nodes(Vec::new())
115+
}
116+
117+
/// Loads the structure of layer nodes from a node graph.
118+
pub fn load_structure(&mut self, graph: &NodeNetwork) {
119+
self.structure = HashMap::from_iter([(LayerNodeIdentifier::ROOT, NodeRelations::default())]);
120+
121+
let id = graph.outputs[0].node_id;
122+
let Some(output_node) = graph.nodes.get(&id) else {
123+
return;
124+
};
125+
let Some((layer_node, node_id)) = first_child_layer(graph, output_node, id) else {
126+
return;
127+
};
128+
let parent = LayerNodeIdentifier::ROOT;
129+
let mut stack = vec![(layer_node, node_id, parent)];
130+
while let Some((node, id, parent)) = stack.pop() {
131+
let mut current = Some((node, id));
132+
while let Some(&(current_node, current_id)) = current.as_ref() {
133+
let current_identifier = LayerNodeIdentifier::new_unchecked(current_id);
134+
if !self.structure.contains_key(&current_identifier) {
135+
parent.push_child(self, current_identifier);
136+
137+
if let Some((child_node, child_id)) = first_child_layer(graph, current_node, current_id) {
138+
stack.push((child_node, child_id, current_identifier));
139+
}
140+
}
141+
142+
current = sibling_below(graph, current_node, current_id);
143+
}
144+
}
70145
}
146+
}
71147

72-
/// Access the cached transformation from document space to layer space
73-
pub fn transform_from_document(&self, layer: LayerNodeIdentifier) -> DAffine2 {
148+
fn first_child_layer<'a>(graph: &'a NodeNetwork, node: &DocumentNode, id: NodeId) -> Option<(&'a DocumentNode, NodeId)> {
149+
graph.primary_flow_from_opt(Some(node.inputs[0].as_node()?)).find(|(node, _)| node.name == "Layer")
150+
}
151+
fn sibling_below<'a>(graph: &'a NodeNetwork, node: &DocumentNode, id: NodeId) -> Option<(&'a DocumentNode, NodeId)> {
152+
node.inputs[7].as_node().and_then(|id| graph.nodes.get(&id).filter(|node| node.name == "Layer").map(|node| (node, id)))
153+
}
154+
155+
// transforms
156+
impl DocumentMetadata {
157+
/// Update the cached transforms of the layers
158+
pub fn update_transforms(&mut self, new_transforms: HashMap<LayerNodeIdentifier, DAffine2>, new_upstream_transforms: HashMap<NodeId, DAffine2>) {
159+
self.transforms = new_transforms;
160+
self.upstream_transforms = new_upstream_transforms;
161+
}
162+
163+
/// Access the cached transformation to document space from layer space
164+
pub fn transform_to_document(&self, layer: LayerNodeIdentifier) -> DAffine2 {
74165
self.transforms.get(&layer).copied().unwrap_or_else(|| {
75-
warn!("Tried to access transform of bad layer");
166+
warn!("Tried to access transform of bad layer {layer:?}");
76167
DAffine2::IDENTITY
77168
})
78169
}
79170

80-
pub fn transform_from_viewport(&self, layer: LayerNodeIdentifier) -> DAffine2 {
81-
self.document_to_viewport * self.transform_from_document(layer)
171+
pub fn transform_to_viewport(&self, layer: LayerNodeIdentifier) -> DAffine2 {
172+
self.document_to_viewport * self.transform_to_document(layer)
173+
}
174+
175+
pub fn upstream_transform(&self, node_id: NodeId) -> DAffine2 {
176+
self.upstream_transforms.get(&node_id).copied().unwrap_or(DAffine2::IDENTITY)
177+
}
178+
}
179+
180+
fn is_artboard(layer: LayerNodeIdentifier, network: &NodeNetwork) -> bool {
181+
network.primary_flow_from_opt(Some(layer.to_node())).any(|(node, _)| node.name == "Artboard")
182+
}
183+
184+
// click targets
185+
impl DocumentMetadata {
186+
/// Update the cached click targets of the layers
187+
pub fn update_click_targets(&mut self, new_click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>) {
188+
self.click_targets = new_click_targets;
82189
}
83190

84191
/// Runs an intersection test with all layers and a viewport space quad
85-
pub fn intersect_quad(&self, viewport_quad: Quad) -> Option<LayerNodeIdentifier> {
192+
pub fn intersect_quad<'a>(&'a self, viewport_quad: Quad, network: &'a NodeNetwork) -> impl Iterator<Item = LayerNodeIdentifier> + 'a {
86193
let document_quad = self.document_to_viewport.inverse() * viewport_quad;
87194
self.root()
88195
.decendants(self)
196+
.filter(|&layer| !is_artboard(layer, network))
89197
.filter_map(|layer| self.click_targets.get(&layer).map(|targets| (layer, targets)))
90-
.find(|(layer, target)| target.iter().any(|target| target.intersect_rectangle(document_quad, self.transform_from_document(*layer))))
198+
.filter(move |(layer, target)| target.iter().any(move |target| target.intersect_rectangle(document_quad, self.transform_to_document(*layer))))
91199
.map(|(layer, _)| layer)
92200
}
93201

@@ -97,13 +205,13 @@ impl DocumentMetadata {
97205
self.root()
98206
.decendants(self)
99207
.filter_map(|layer| self.click_targets.get(&layer).map(|targets| (layer, targets)))
100-
.filter(move |(layer, target)| target.iter().any(|target: &ClickTarget| target.intersect_point(point, self.transform_from_document(*layer))))
208+
.filter(move |(layer, target)| target.iter().any(|target: &ClickTarget| target.intersect_point(point, self.transform_to_document(*layer))))
101209
.map(|(layer, _)| layer)
102210
}
103211

104212
/// Find the layer that has been clicked on from a viewport space location
105-
pub fn click(&self, viewport_location: DVec2) -> Option<LayerNodeIdentifier> {
106-
self.click_xray(viewport_location).next()
213+
pub fn click(&self, viewport_location: DVec2, network: &NodeNetwork) -> Option<LayerNodeIdentifier> {
214+
self.click_xray(viewport_location).filter(|&layer| !is_artboard(layer, network)).next()
107215
}
108216

109217
/// Get the bounding box of the click target of the specified layer in the specified transform space
@@ -115,14 +223,47 @@ impl DocumentMetadata {
115223
.reduce(Quad::combine_bounds)
116224
}
117225

226+
/// Calculate the corners of the bounding box but with a nonzero size.
227+
///
228+
/// If the layer bounds are `0` in either axis then they are changed to be `1`.
229+
pub fn nonzero_bounding_box(&self, layer: LayerNodeIdentifier) -> [DVec2; 2] {
230+
let [bounds_min, mut bounds_max] = self.bounding_box_with_transform(layer, DAffine2::IDENTITY).unwrap_or_default();
231+
232+
let bounds_size = bounds_max - bounds_min;
233+
if bounds_size.x < 1e-10 {
234+
bounds_max.x = bounds_min.x + 1.;
235+
}
236+
if bounds_size.y < 1e-10 {
237+
bounds_max.y = bounds_min.y + 1.;
238+
}
239+
240+
[bounds_min, bounds_max]
241+
}
242+
118243
/// Get the bounding box of the click target of the specified layer in document space
119244
pub fn bounding_box_document(&self, layer: LayerNodeIdentifier) -> Option<[DVec2; 2]> {
120-
self.bounding_box_with_transform(layer, self.transform_from_document(layer))
245+
self.bounding_box_with_transform(layer, self.transform_to_document(layer))
121246
}
122247

123248
/// Get the bounding box of the click target of the specified layer in viewport space
124249
pub fn bounding_box_viewport(&self, layer: LayerNodeIdentifier) -> Option<[DVec2; 2]> {
125-
self.bounding_box_with_transform(layer, self.transform_from_viewport(layer))
250+
self.bounding_box_with_transform(layer, self.transform_to_viewport(layer))
251+
}
252+
253+
pub fn selected_visible_layers_bounding_box_viewport(&self) -> Option<[DVec2; 2]> {
254+
self.selected_layers().filter_map(|layer| self.bounding_box_viewport(layer)).reduce(Quad::combine_bounds)
255+
}
256+
257+
/// Calculates the document bounds used for scrolling and centring (the layer bounds or the artboard (if applicable))
258+
pub fn document_bounds(&self) -> Option<[DVec2; 2]> {
259+
self.all_layers().filter_map(|layer| self.bounding_box_viewport(layer)).reduce(Quad::combine_bounds)
260+
}
261+
262+
pub fn layer_outline(&self, layer: LayerNodeIdentifier) -> graphene_core::vector::Subpath {
263+
let Some(click_targets) = self.click_targets.get(&layer) else {
264+
return graphene_core::vector::Subpath::new();
265+
};
266+
graphene_core::vector::Subpath::from_bezier_rs(click_targets.iter().map(|click_target| &click_target.subpath))
126267
}
127268
}
128269

@@ -242,7 +383,7 @@ impl LayerNodeIdentifier {
242383
pub fn decendants(self, document_metadata: &DocumentMetadata) -> DecendantsIter {
243384
DecendantsIter {
244385
front: self.first_child(document_metadata),
245-
back: self.last_child(document_metadata),
386+
back: self.last_child(document_metadata).and_then(|child| child.last_children(document_metadata).last()),
246387
document_metadata,
247388
}
248389
}
@@ -339,6 +480,17 @@ impl LayerNodeIdentifier {
339480
pub fn exists(&self, document_metadata: &DocumentMetadata) -> bool {
340481
document_metadata.get_relations(*self).is_some()
341482
}
483+
484+
pub fn starts_with(&self, other: Self, document_metadata: &DocumentMetadata) -> bool {
485+
self.ancestors(document_metadata).any(|parent| parent == other)
486+
}
487+
488+
pub fn child_of_root(&self, document_metadata: &DocumentMetadata) -> Self {
489+
self.ancestors(document_metadata)
490+
.filter(|&layer| layer != LayerNodeIdentifier::ROOT)
491+
.last()
492+
.expect("There should be a layer before the root")
493+
}
342494
}
343495

344496
impl From<NodeId> for LayerNodeIdentifier {
@@ -457,7 +609,8 @@ fn test_tree() {
457609
assert!(root.children(document_metadata).all(|child| child.parent(document_metadata) == Some(root)));
458610
LayerNodeIdentifier::new_unchecked(6).delete(document_metadata);
459611
LayerNodeIdentifier::new_unchecked(1).delete(document_metadata);
612+
LayerNodeIdentifier::new_unchecked(9).push_child(document_metadata, LayerNodeIdentifier::new_unchecked(10));
460613
assert_eq!(root.children(document_metadata).map(LayerNodeIdentifier::to_node).collect::<Vec<_>>(), vec![2, 3, 4, 5, 9]);
461-
assert_eq!(root.decendants(document_metadata).map(LayerNodeIdentifier::to_node).collect::<Vec<_>>(), vec![2, 3, 4, 5, 9]);
462-
assert_eq!(root.decendants(document_metadata).map(LayerNodeIdentifier::to_node).rev().collect::<Vec<_>>(), vec![9, 5, 4, 3, 2]);
614+
assert_eq!(root.decendants(document_metadata).map(LayerNodeIdentifier::to_node).collect::<Vec<_>>(), vec![2, 3, 4, 5, 9, 10]);
615+
assert_eq!(root.decendants(document_metadata).map(LayerNodeIdentifier::to_node).rev().collect::<Vec<_>>(), vec![10, 9, 5, 4, 3, 2]);
463616
}

document-legacy/src/layers/shape_layer.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,12 @@ impl LayerData for ShapeLayer {
3232
let layer_bounds = subpath.bounding_box().unwrap_or_default();
3333

3434
let transform = self.transform(transforms, render_data.view_mode);
35-
let inverse = transform.inverse();
36-
if !inverse.is_finite() {
35+
if !transform.is_finite() || transform.matrix2.determinant() == 0. {
3736
let _ = write!(svg, "<!-- SVG shape has an invalid transform -->");
3837
return false;
3938
}
39+
let inverse = transform.inverse();
40+
4041
subpath.apply_affine(transform);
4142

4243
let transformed_bounds = subpath.bounding_box().unwrap_or_default();

document-legacy/src/lib.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,10 @@
33
extern crate log;
44

55
pub mod boolean_ops;
6-
/// Contains constant values used by this crate.
76
pub mod consts;
87
pub mod document;
98
pub mod document_metadata;
10-
/// Defines errors that can occur when using this crate.
119
pub mod error;
12-
/// Utilities for computing intersections.
1310
pub mod intersection;
1411
pub mod layers;
1512
pub mod operation;

document-legacy/src/response.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@ pub enum DocumentResponse {
1111
FolderChanged {
1212
path: Vec<LayerId>,
1313
},
14-
AddSelectedLayer {
15-
additional_layers: Vec<Vec<LayerId>>,
16-
},
1714
CreatedLayer {
1815
path: Vec<LayerId>,
1916
is_selected: bool,
@@ -38,7 +35,6 @@ impl fmt::Display for DocumentResponse {
3835
match self {
3936
DocumentResponse::DocumentChanged { .. } => write!(f, "DocumentChanged"),
4037
DocumentResponse::FolderChanged { .. } => write!(f, "FolderChanged"),
41-
DocumentResponse::AddSelectedLayer { .. } => write!(f, "AddSelectedLayer"),
4238
DocumentResponse::CreatedLayer { .. } => write!(f, "CreatedLayer"),
4339
DocumentResponse::LayerChanged { .. } => write!(f, "LayerChanged"),
4440
DocumentResponse::DeletedLayer { .. } => write!(f, "DeleteLayer"),

editor/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ serde = { version = "1.0", features = ["derive"] }
2727
serde_json = { version = "1.0" }
2828
graphite-proc-macros = { path = "../proc-macros" }
2929
bezier-rs = { path = "../libraries/bezier-rs" }
30-
glam = { version = "0.24", features = ["serde"] }
30+
glam = { version = "0.24", features = ["serde", "debug-glam-assert"] }
3131
remain = "0.2.2"
3232
derivative = "2.2.0"
3333
once_cell = "1.13.0" # Remove when `core::cell::LazyCell` is stabilized (<https://doc.rust-lang.org/core/cell/struct.LazyCell.html>)

editor/src/messages/layout/utility_types/widgets/assist_widgets.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::messages::layout::utility_types::widget_prelude::*;
2+
use crate::messages::prelude::*;
23
use graphite_proc_macros::WidgetBuilder;
34

45
use derivative::*;

0 commit comments

Comments
 (0)