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 <dennis@kobert.dev>
Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
0HyperCube
2023-10-17 11:09:32 -07:00
committed by Keavon Chambers
co-authored by Dennis Kobert Keavon Chambers
parent e1cdb2242d
commit 5827e989dc
46 changed files with 1041 additions and 1215 deletions
@@ -9,15 +9,14 @@ use graphene_core::uuid::ManipulatorGroupId;
use graphene_core::vector::style::{FillType, Gradient};
use graphene_core::Color;
use glam::DAffine2;
use glam::{DAffine2, DVec2};
use std::collections::VecDeque;
/// Create a new vector layer from a vector of [`bezier_rs::Subpath`].
pub fn new_vector_layer(subpaths: Vec<Subpath<ManipulatorGroupId>>, layer_path: Vec<LayerId>, responses: &mut VecDeque<Message>) {
responses.add(GraphOperationMessage::NewVectorLayer {
id: *layer_path.last().unwrap(),
subpaths,
});
let id = *layer_path.last().unwrap();
responses.add(GraphOperationMessage::NewVectorLayer { id, subpaths });
responses.add(NodeGraphMessage::SetSelectNodes { nodes: vec![id] })
}
/// Creat a new bitmap layer from an [`graphene_core::raster::ImageFrame<Color>`]
@@ -62,6 +61,25 @@ pub fn get_subpaths(layer: LayerNodeIdentifier, document: &Document) -> Option<&
}
}
/// Locate the final pivot from the transform (TODO: decide how the pivot should actually work)
pub fn get_pivot(layer: LayerNodeIdentifier, document: &Document) -> Option<DVec2> {
if let TaggedValue::DVec2(pivot) = NodeGraphLayer::new(layer, document)?.find_input("Transform", 5)? {
Some(*pivot)
} else {
None
}
}
pub fn get_document_pivot(layer: LayerNodeIdentifier, document: &Document) -> Option<DVec2> {
let [min, max] = document.metadata.nonzero_bounding_box(layer);
get_pivot(layer, document).map(|pivot| document.metadata.transform_to_document(layer).transform_point2(min + (max - min) * pivot))
}
pub fn get_viewport_pivot(layer: LayerNodeIdentifier, document: &Document) -> Option<DVec2> {
let [min, max] = document.metadata.nonzero_bounding_box(layer);
get_pivot(layer, document).map(|pivot| document.metadata.transform_to_viewport(layer).transform_point2(min + (max - min) * pivot))
}
/// Get the currently mirrored handles for a particular layer from the shape node
pub fn get_mirror_handles(layer: LayerNodeIdentifier, document: &Document) -> Option<&Vec<ManipulatorGroupId>> {
if let TaggedValue::ManipulatorGroupIds(mirror_handles) = NodeGraphLayer::new(layer, document)?.find_input("Shape", 1)? {
@@ -106,6 +124,16 @@ pub fn is_artboard(layer: LayerNodeIdentifier, document: &Document) -> bool {
NodeGraphLayer::new(layer, document).is_some_and(|layer| layer.uses_node("Artboard"))
}
/// Is a specified layer a shape?
pub fn is_shape_layer(layer: LayerNodeIdentifier, document: &Document) -> bool {
NodeGraphLayer::new(layer, document).is_some_and(|layer| layer.uses_node("Shape"))
}
/// Is a specified layer text?
pub fn is_text_layer(layer: LayerNodeIdentifier, document: &Document) -> bool {
NodeGraphLayer::new(layer, document).is_some_and(|layer| layer.uses_node("Text"))
}
/// Convert subpaths to an iterator of manipulator groups
pub fn get_manipulator_groups(subpaths: &[Subpath<ManipulatorGroupId>]) -> impl Iterator<Item = &bezier_rs::ManipulatorGroup<ManipulatorGroupId>> + DoubleEndedIterator {
subpaths.iter().flat_map(|subpath| subpath.manipulator_groups())
@@ -51,7 +51,7 @@ impl OverlayRenderer {
}
pub fn render_subpath_overlays(&mut self, selected_shape_state: &SelectedShapeState, document: &Document, layer: LayerNodeIdentifier, responses: &mut VecDeque<Message>) {
let transform = document.metadata.transform_from_viewport(layer);
let transform = document.metadata.transform_to_viewport(layer);
let Some(subpaths) = get_subpaths(layer, document) else {
return;
@@ -1,19 +1,17 @@
use crate::application::generate_uuid;
use crate::consts::{COLOR_ACCENT, PATH_OUTLINE_WEIGHT, SELECTION_TOLERANCE};
use crate::consts::{COLOR_ACCENT, PATH_OUTLINE_WEIGHT};
use crate::messages::prelude::*;
use document_legacy::intersection::Quad;
use document_legacy::layers::layer_info::LayerDataType;
use document_legacy::document_metadata::LayerNodeIdentifier;
use document_legacy::layers::style::{self, Fill, RenderData, Stroke};
use document_legacy::{LayerId, Operation};
use graphene_std::vector::subpath::Subpath;
use glam::{DAffine2, DVec2};
use glam::DAffine2;
/// Manages the overlay used by the select tool for outlining selected shapes and when hovering over a non selected shape.
#[derive(Clone, Debug, Default)]
pub struct PathOutline {
hovered_layer_path: Option<Vec<LayerId>>,
hovered_layer_path: Option<LayerNodeIdentifier>,
hovered_overlay_path: Option<Vec<LayerId>>,
selected_overlay_paths: Vec<Vec<LayerId>>,
}
@@ -21,29 +19,14 @@ pub struct PathOutline {
impl PathOutline {
/// Creates an outline of a layer either with a pre-existing overlay or by generating a new one
fn try_create_outline(
document_layer_path: Vec<LayerId>,
layer: LayerNodeIdentifier,
overlay_path: Option<Vec<LayerId>>,
document: &DocumentMessageHandler,
responses: &mut VecDeque<Message>,
render_data: &RenderData,
) -> Option<Vec<LayerId>> {
// Get layer data
let document_layer = document.document_legacy.layer(&document_layer_path).ok()?;
// Get the subpath from the shape
let subpath = match &document_layer.data {
LayerDataType::Shape(shape) => Some(shape.shape.clone()),
LayerDataType::Layer(layer) => {
if let Some(vector_data) = layer.as_vector_data() {
// Vector graph output
Some(Subpath::from_bezier_rs(&vector_data.subpaths))
} else {
// Frame graph output
Some(Subpath::new_rect(DVec2::new(0., 0.), DVec2::new(1., 1.)))
}
}
_ => document_layer.aabb_for_transform(DAffine2::IDENTITY, render_data).map(|[p1, p2]| Subpath::new_rect(p1, p2)),
}?;
let subpath = document.metadata().layer_outline(layer);
let transform = document.metadata().transform_to_viewport(layer);
// Generate a new overlay layer if necessary
let overlay = overlay_path.unwrap_or_else(|| {
@@ -70,7 +53,7 @@ impl PathOutline {
responses.add(DocumentMessage::Overlays(
(Operation::SetLayerTransform {
path: overlay.clone(),
transform: document.document_legacy.multiply_transforms(&document_layer_path).unwrap().to_cols_array(),
transform: transform.to_cols_array(),
})
.into(),
));
@@ -82,14 +65,14 @@ impl PathOutline {
///
/// Creates an outline, discarding the overlay on failure.
fn create_outline(
document_layer_path: Vec<LayerId>,
layer: LayerNodeIdentifier,
overlay_path: Option<Vec<LayerId>>,
document: &DocumentMessageHandler,
responses: &mut VecDeque<Message>,
render_data: &RenderData,
) -> Option<Vec<LayerId>> {
let copied_overlay_path = overlay_path.clone();
let result = Self::try_create_outline(document_layer_path, overlay_path, document, responses, render_data);
let result = Self::try_create_outline(layer, overlay_path, document, responses, render_data);
if result.is_none() {
// Discard the overlay layer if it exists
if let Some(overlay_path) = copied_overlay_path {
@@ -112,26 +95,25 @@ impl PathOutline {
/// Performs an intersect test and generates a hovered overlay if necessary
pub fn intersect_test_hovered(&mut self, input: &InputPreprocessorMessageHandler, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>, render_data: &RenderData) {
// Get the layer the user is hovering over
let tolerance = DVec2::splat(SELECTION_TOLERANCE);
let quad = Quad::from_box([input.mouse.position - tolerance, input.mouse.position + tolerance]);
let mut intersection = document.document_legacy.intersects_quad_root(quad, render_data);
let intersection = document.metadata().click(input.mouse.position, &document.document_legacy.document_network);
// If the user is hovering over a layer they have not already selected, then update outline
if let Some(path) = intersection.pop() {
if !document.selected_visible_layers().any(|visible| visible == path.as_slice()) {
// Updates the overlay, generating a new one if necessary
self.hovered_overlay_path = Self::create_outline(path.clone(), self.hovered_overlay_path.take(), document, responses, render_data);
if self.hovered_overlay_path.is_none() {
self.clear_hovered(responses);
}
let Some(hovered_layer) = intersection else {
self.clear_hovered(responses);
return;
};
self.hovered_layer_path = Some(path);
} else {
self.clear_hovered(responses);
}
} else {
if document.metadata().selected_layers_contains(hovered_layer) {
self.clear_hovered(responses);
return;
}
// Updates the overlay, generating a new one if necessary
self.hovered_overlay_path = Self::create_outline(hovered_layer, self.hovered_overlay_path.take(), document, responses, render_data);
if self.hovered_overlay_path.is_none() {
self.clear_hovered(responses);
}
self.hovered_layer_path = Some(hovered_layer);
}
/// Clears overlays for the selected paths and removes references
@@ -143,11 +125,11 @@ impl PathOutline {
}
/// Updates the selected overlays, generating or removing overlays if necessary
pub fn update_selected<'a>(&mut self, selected: impl Iterator<Item = &'a [LayerId]>, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>, render_data: &RenderData) {
pub fn update_selected<'a>(&mut self, selected: impl Iterator<Item = LayerNodeIdentifier>, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>, render_data: &RenderData) {
let mut old_overlay_paths = std::mem::take(&mut self.selected_overlay_paths);
for document_layer_path in selected {
if let Some(overlay_path) = Self::create_outline(document_layer_path.to_vec(), old_overlay_paths.pop(), document, responses, render_data) {
for layer_identifier in selected {
if let Some(overlay_path) = Self::create_outline(layer_identifier, old_overlay_paths.pop(), document, responses, render_data) {
self.selected_overlay_paths.push(overlay_path);
}
}
@@ -5,12 +5,15 @@ use crate::consts::{COLOR_ACCENT, PIVOT_INNER, PIVOT_OUTER, PIVOT_OUTER_OUTLINE_
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::prelude::*;
use document_legacy::document_metadata::LayerNodeIdentifier;
use document_legacy::layers::style::{self, RenderData};
use document_legacy::{LayerId, Operation};
use glam::{DAffine2, DVec2};
use std::collections::VecDeque;
use super::graph_modification_utils;
#[derive(Clone, Debug)]
pub struct Pivot {
/// Pivot between (0,0) and (1,1)
@@ -39,54 +42,49 @@ impl Default for Pivot {
impl Pivot {
/// Calculates the transform that gets from normalized pivot to viewspace.
fn get_layer_pivot_transform(layer_path: &[LayerId], layer: &document_legacy::layers::layer_info::Layer, document: &DocumentMessageHandler, render_data: &RenderData) -> DAffine2 {
let [mut min, max] = layer.aabb_for_transform(DAffine2::IDENTITY, render_data).unwrap_or([DVec2::ZERO, DVec2::ONE]);
fn get_layer_pivot_transform(layer: LayerNodeIdentifier, document: &DocumentMessageHandler) -> DAffine2 {
let [min, max] = document.metadata().nonzero_bounding_box(layer);
// If the layer bounds are 0 in either axis then set them to one (to avoid div 0)
if (max.x - min.x) < f64::EPSILON * 1000. {
min.x = max.x - 1.;
}
if (max.y - min.y) < f64::EPSILON * 1000. {
min.y = max.y - 1.;
}
let bounds_transform = DAffine2::from_translation(min) * DAffine2::from_scale(max - min);
let layer_transform = document.document_legacy.multiply_transforms(layer_path).unwrap_or(DAffine2::IDENTITY);
let layer_transform = document.metadata().transform_to_viewport(layer);
layer_transform * bounds_transform
}
/// Recomputes the pivot position and transform.
fn recalculate_pivot(&mut self, document: &DocumentMessageHandler, render_data: &RenderData) {
let mut layers = document.selected_visible_layers();
if let Some(first) = layers.next() {
// Add one because the first item is consumed above.
let selected_layers_count = layers.count() + 1;
// If just one layer is selected we can use its inner transform
if selected_layers_count == 1 {
if let Ok(layer) = document.document_legacy.layer(first) {
self.normalized_pivot = layer.pivot;
self.transform_from_normalized = Self::get_layer_pivot_transform(first, layer, document, render_data);
self.pivot = Some(self.transform_from_normalized.transform_point2(layer.pivot));
}
} else {
// If more than one layer is selected we use the AABB with the mean of the pivots
let xy_summation = document
.selected_visible_layers()
.filter_map(|path| document.document_legacy.pivot(path, render_data))
.reduce(|a, b| a + b)
.unwrap_or_default();
let pivot = xy_summation / selected_layers_count as f64;
self.pivot = Some(pivot);
let [min, max] = document.selected_visible_layers_bounding_box(render_data).unwrap_or([DVec2::ZERO, DVec2::ONE]);
self.normalized_pivot = (pivot - min) / (max - min);
self.transform_from_normalized = DAffine2::from_translation(min) * DAffine2::from_scale(max - min);
}
} else {
let mut layers = document.metadata().selected_visible_layers();
let Some(first) = layers.next() else {
// If no layers are selected then we revert things back to default
self.normalized_pivot = DVec2::splat(0.5);
self.pivot = None;
return;
};
// Add one because the first item is consumed above.
let selected_layers_count = layers.count() + 1;
// If just one layer is selected we can use its inner transform (as it accounts for rotation)
if selected_layers_count == 1 {
if let Some(normalized_pivot) = graph_modification_utils::get_pivot(first, &document.document_legacy) {
self.normalized_pivot = normalized_pivot;
self.transform_from_normalized = Self::get_layer_pivot_transform(first, document);
self.pivot = Some(self.transform_from_normalized.transform_point2(normalized_pivot));
}
} else {
// If more than one layer is selected we use the AABB with the mean of the pivots
let xy_summation = document
.metadata()
.selected_visible_layers()
.filter_map(|layer| graph_modification_utils::get_viewport_pivot(layer, &document.document_legacy))
.reduce(|a, b| a + b)
.unwrap_or_default();
let pivot = xy_summation / selected_layers_count as f64;
self.pivot = Some(pivot);
let [min, max] = document.metadata().selected_visible_layers_bounding_box_viewport().unwrap_or([DVec2::ZERO, DVec2::ONE]);
self.normalized_pivot = (pivot - min) / (max - min);
self.transform_from_normalized = DAffine2::from_translation(min) * DAffine2::from_scale(max - min);
}
}
@@ -158,23 +156,21 @@ impl Pivot {
}
/// Sets the viewport position of the pivot for all selected layers.
pub fn set_viewport_position(&self, position: DVec2, document: &DocumentMessageHandler, render_data: &RenderData, responses: &mut VecDeque<Message>) {
for layer_path in document.selected_visible_layers() {
if let Ok(layer) = document.document_legacy.layer(layer_path) {
let transform = Self::get_layer_pivot_transform(layer_path, layer, document, render_data);
let pivot = transform.inverse().transform_point2(position);
// Only update the pivot when computed position is finite. Infinite can happen when scale is 0.
if pivot.is_finite() {
let layer = layer_path.to_owned();
responses.add(GraphOperationMessage::TransformSetPivot { layer, pivot });
}
pub fn set_viewport_position(&self, position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
for layer in document.metadata().selected_visible_layers() {
let transform = Self::get_layer_pivot_transform(layer, document);
let pivot = transform.inverse().transform_point2(position);
// Only update the pivot when computed position is finite. Infinite can happen when scale is 0.
if pivot.is_finite() {
let layer = layer.to_path();
responses.add(GraphOperationMessage::TransformSetPivot { layer, pivot });
}
}
}
/// Set the pivot using the normalized transform that is set above.
pub fn set_normalized_position(&self, position: DVec2, document: &DocumentMessageHandler, render_data: &RenderData, responses: &mut VecDeque<Message>) {
self.set_viewport_position(self.transform_from_normalized.transform_point2(position), document, render_data, responses);
pub fn set_normalized_position(&self, position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
self.set_viewport_position(self.transform_from_normalized.transform_point2(position), document, responses);
}
/// Answers if the pointer is currently positioned over the pivot.
@@ -20,7 +20,7 @@ impl Resize {
pub fn start(&mut self, responses: &mut VecDeque<Message>, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, render_data: &RenderData) {
self.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, render_data), true, true);
self.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
let root_transform = document.document_legacy.metadata.document_to_viewport;
let root_transform = document.metadata().document_to_viewport;
self.drag_start = root_transform.inverse().transform_point2(self.snap_manager.snap_position(responses, document, input.mouse.position));
}
@@ -32,7 +32,7 @@ impl Resize {
/// Calculate the drag start position in viewport space.
pub fn viewport_drag_start(&self, document: &DocumentMessageHandler) -> DVec2 {
let root_transform = document.document_legacy.metadata.document_to_viewport;
let root_transform = document.metadata().document_to_viewport;
root_transform.transform_point2(self.drag_start)
}
@@ -95,7 +95,7 @@ impl ShapeState {
selected_shape_state.select_point(manipulator_point_id);
// Offset to snap the selected point to the cursor
let offset = mouse_position - document.metadata.transform_from_viewport(layer).transform_point2(point_position);
let offset = mouse_position - document.metadata.transform_to_viewport(layer).transform_point2(point_position);
let points = self
.selected_shape_state
@@ -116,7 +116,7 @@ impl ShapeState {
pub fn select_all_points(&mut self, document: &Document) {
for (layer, state) in self.selected_shape_state.iter_mut() {
let Some(subpaths) = get_subpaths(*layer, document) else { return };
let Some(subpaths) = get_subpaths(*layer, document) else { return };
for manipulator in get_manipulator_groups(subpaths) {
state.select_point(ManipulatorPointId::new(manipulator.id, SelectedType::Anchor));
for selected_type in &[SelectedType::InHandle, SelectedType::OutHandle] {
@@ -367,7 +367,7 @@ impl ShapeState {
let Some(subpaths) = get_subpaths(layer, document) else { continue };
let Some(mirror_angle) = get_mirror_handles(layer, document) else { continue };
let transform = document.metadata.transform_from_viewport(layer);
let transform = document.metadata.transform_to_viewport(layer);
let delta = transform.inverse().transform_vector2(delta);
for &point in state.selected_points.iter() {
@@ -435,7 +435,7 @@ impl ShapeState {
let opposing_handle_lengths = opposing_handle_lengths.as_ref().and_then(|lengths| lengths.get(&layer));
let transform = document.metadata.transform_from_viewport(layer);
let transform = document.metadata.transform_to_viewport(layer);
for &point in state.selected_points.iter() {
let anchor = ManipulatorPointId::new(point.group, SelectedType::Anchor);
@@ -649,7 +649,7 @@ impl ShapeState {
let mut result = None;
let subpaths = get_subpaths(layer, document)?;
let viewspace = document.metadata.transform_from_viewport(layer);
let viewspace = document.metadata.transform_to_viewport(layer);
for manipulator in get_manipulator_groups(subpaths) {
let (selected, distance_squared) = SelectedType::closest_widget(manipulator, viewspace, pos, crate::consts::HIDE_HANDLE_DISTANCE);
@@ -664,7 +664,7 @@ impl ShapeState {
/// Find the `t` value along the path segment we have clicked upon, together with that segment ID.
fn closest_segment(&self, document: &Document, layer: LayerNodeIdentifier, position: glam::DVec2, tolerance: f64) -> Option<(ManipulatorGroupId, ManipulatorGroupId, Bezier, f64)> {
let transform = document.metadata.transform_from_viewport(layer);
let transform = document.metadata.transform_to_viewport(layer);
let layer_pos = transform.inverse().transform_point2(position);
let projection_options = bezier_rs::ProjectionOptions { lut_size: 5, ..Default::default() };
@@ -735,7 +735,7 @@ impl ShapeState {
let mut process_layer = |layer| {
let subpaths = get_subpaths(layer, document)?;
let transform_to_screenspace = document.metadata.transform_from_viewport(layer);
let transform_to_screenspace = document.metadata.transform_to_viewport(layer);
let mut result = None;
let mut closest_distance_squared = tolerance * tolerance;
@@ -803,7 +803,7 @@ impl ShapeState {
let Some(subpaths) = get_subpaths(layer, document) else { continue };
let transform = document.metadata.transform_from_viewport(layer);
let transform = document.metadata.transform_to_viewport(layer);
for manipulator_group in get_manipulator_groups(subpaths) {
for selected_type in [SelectedType::Anchor, SelectedType::InHandle, SelectedType::OutHandle] {
@@ -103,14 +103,14 @@ struct ArtboardToolData {
impl ArtboardToolData {
fn refresh_overlays(&mut self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
let current_artboard = self.selected_artboard.and_then(|layer| document.document_legacy.metadata.bounding_box_document(layer));
let current_artboard = self.selected_artboard.and_then(|layer| document.metadata().bounding_box_document(layer));
match (current_artboard, self.bounding_box_overlays.take()) {
(None, Some(bounding_box_overlays)) => bounding_box_overlays.delete(responses),
(Some(bounds), paths) => {
let mut bounding_box_overlays = paths.unwrap_or_else(|| BoundingBoxOverlays::new(responses));
bounding_box_overlays.bounds = bounds;
bounding_box_overlays.transform = document.document_legacy.metadata.document_to_viewport;
bounding_box_overlays.transform = document.metadata().document_to_viewport;
bounding_box_overlays.transform(responses);
@@ -265,7 +265,7 @@ impl Fsm for ArtboardToolFsmState {
let mouse_position = input.mouse.position;
let snapped_mouse_position = tool_data.snap_manager.snap_position(responses, document, mouse_position);
let root_transform = document.document_legacy.metadata.document_to_viewport.inverse();
let root_transform = document.metadata().document_to_viewport.inverse();
let mut start = tool_data.drag_start;
let mut size = snapped_mouse_position - start;
@@ -341,7 +341,7 @@ impl Fsm for BrushToolFsmState {
document, global_tool_data, input, ..
} = tool_action_data;
let document_position = document.document_legacy.metadata.document_to_viewport.inverse().transform_point2(input.mouse.position);
let document_position = document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position);
let layer_position = tool_data.transform.inverse().transform_point2(document_position);
let ToolMessage::Brush(event) = event else {
@@ -68,7 +68,7 @@ impl Fsm for FillToolFsmState {
let ToolMessage::Fill(event) = event else {
return self;
};
let Some(layer_identifier) = document.document_legacy.metadata.click(input.mouse.position) else {
let Some(layer_identifier) = document.metadata().click(input.mouse.position, &document.document_legacy.document_network) else {
return self;
};
let layer = layer_identifier.to_path();
@@ -110,7 +110,7 @@ impl Fsm for NodeGraphToolFsmState {
match (self, event) {
(_, FrameToolMessage::DocumentIsDirty | FrameToolMessage::SelectionChanged) => {
tool_data.path_outlines.clear_selected(responses);
tool_data.path_outlines.update_selected(document.selected_visible_layers(), document, responses, render_data);
//tool_data.path_outlines.update_selected(document.selected_visible_layers(), document, responses, render_data);
self
}
@@ -191,7 +191,7 @@ impl Fsm for FreehandToolFsmState {
document, global_tool_data, input, ..
} = tool_action_data;
let transform = document.document_legacy.metadata.document_to_viewport;
let transform = document.metadata().document_to_viewport;
let ToolMessage::Freehand(event) = event else {
return self;
@@ -116,10 +116,10 @@ enum GradientToolFsmState {
/// Computes the transform from gradient space to viewport space (where gradient space is 0..1)
fn gradient_space_transform(layer: LayerNodeIdentifier, document: &DocumentMessageHandler) -> DAffine2 {
let bounds = document.document_legacy.metadata.bounding_box_with_transform(layer, DAffine2::IDENTITY).unwrap();
let bounds = document.metadata().nonzero_bounding_box(layer);
let bound_transform = DAffine2::from_scale_angle_translation(bounds[1] - bounds[0], 0., bounds[0]);
let multiplied = document.document_legacy.metadata.transform_from_viewport(layer);
let multiplied = document.metadata().transform_to_viewport(layer);
multiplied * bound_transform
}
@@ -252,7 +252,7 @@ impl SelectedGradient {
};
// Clear the gradient if layer deleted
if !inner_gradient.layer.exists(&document.document_legacy.metadata) {
if !inner_gradient.layer.exists(&document.metadata()) {
responses.add(ToolMessage::RefreshToolOptions);
*gradient = None;
return;
@@ -391,7 +391,7 @@ impl Fsm for GradientToolFsmState {
SelectedGradient::update(&mut tool_data.selected_gradient, document, responses);
}
for layer in document.document_legacy.metadata.selected_visible_layers() {
for layer in document.metadata().selected_visible_layers() {
if let Some(gradient) = get_gradient(layer, &document.document_legacy) {
let dragging = tool_data
.selected_gradient
@@ -526,7 +526,7 @@ impl Fsm for GradientToolFsmState {
document.backup_nonmut(responses);
GradientToolFsmState::Drawing
} else {
let selected_layer = document.document_legacy.metadata.click(input.mouse.position);
let selected_layer = document.metadata().click(input.mouse.position, &document.document_legacy.document_network);
// Apply the gradient to the selected layer
if let Some(layer) = selected_layer {
@@ -540,7 +540,7 @@ impl Fsm for GradientToolFsmState {
// return self;
// }
if !document.document_legacy.metadata.selected_layers_contains(layer) {
if !document.metadata().selected_layers_contains(layer) {
let replacement_selected_layers = vec![layer.to_path()];
responses.add(DocumentMessage::SetSelectedLayers { replacement_selected_layers });
@@ -118,7 +118,7 @@ impl Fsm for ImaginateToolFsmState {
match (self, event) {
(_, ImaginateToolMessage::DocumentIsDirty | ImaginateToolMessage::SelectionChanged) => {
tool_data.path_outlines.clear_selected(responses);
tool_data.path_outlines.update_selected(document.selected_visible_layers(), document, responses, render_data);
//tool_data.path_outlines.update_selected(document.selected_visible_layers(), document, responses, render_data);
self
}
@@ -181,7 +181,7 @@ impl Fsm for LineToolFsmState {
tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
let viewport_start = tool_data.snap_manager.snap_position(responses, document, input.mouse.position);
tool_data.drag_start = document.document_legacy.metadata.document_to_viewport.inverse().transform_point2(viewport_start);
tool_data.drag_start = document.metadata().document_to_viewport.inverse().transform_point2(viewport_start);
let subpath = bezier_rs::Subpath::new_line(DVec2::ZERO, DVec2::X);
@@ -202,7 +202,7 @@ impl Fsm for LineToolFsmState {
tool_data.drag_current = tool_data.snap_manager.snap_position(responses, document, input.mouse.position);
let keyboard = &input.keyboard;
let transform = document.document_legacy.metadata.document_to_viewport;
let transform = document.metadata().document_to_viewport;
responses.add(generate_transform(tool_data, transform, keyboard.key(lock_angle), keyboard.key(snap_angle), keyboard.key(center)));
LineToolFsmState::Drawing
@@ -210,7 +210,7 @@ struct PathToolData {
impl PathToolData {
fn refresh_overlays(&mut self, document: &DocumentMessageHandler, shape_editor: &mut ShapeState, shape_overlay: &mut OverlayRenderer, responses: &mut VecDeque<Message>) {
// Set the previously selected layers to invisible
for layer in document.document_legacy.metadata.all_layers() {
for layer in document.metadata().all_layers() {
shape_overlay.layer_overlay_visibility(&document.document_legacy, layer, false, responses);
}
@@ -242,15 +242,11 @@ impl PathToolData {
PathToolFsmState::Dragging
}
// We didn't find a point nearby, so consider selecting the nearest shape instead
else if let Some(layer) = document.document_legacy.metadata.click(input.mouse.position) {
// TODO: Actual selection
let layer_list = vec![layer.to_path()];
else if let Some(layer) = document.metadata().click(input.mouse.position, &document.document_legacy.document_network) {
if shift {
responses.add(DocumentMessage::AddSelectedLayers { additional_layers: layer_list });
responses.add(NodeGraphMessage::AddSelectNodes { nodes: vec![layer.to_node()] });
} else {
responses.add(DocumentMessage::SetSelectedLayers {
replacement_selected_layers: layer_list,
});
responses.add(NodeGraphMessage::SetSelectNodes { nodes: vec![layer.to_node()] });
}
self.drag_start_pos = input.mouse.position;
self.previous_mouse_position = input.mouse.position;
@@ -342,7 +338,7 @@ impl Fsm for PathToolFsmState {
match (self, event) {
(_, PathToolMessage::SelectionChanged) => {
// Set the newly targeted layers to visible
let target_layers = document.document_legacy.metadata.selected_layers().collect();
let target_layers = document.metadata().selected_layers().collect();
shape_editor.set_selected_layers(target_layers);
tool_data.refresh_overlays(document, shape_editor, shape_overlay, responses);
@@ -354,7 +350,7 @@ impl Fsm for PathToolFsmState {
(_, PathToolMessage::DocumentIsDirty) => {
// When the document has moved / needs to be redraw, re-render the overlays
// TODO the overlay system should probably receive this message instead of the tool
for layer in document.document_legacy.metadata.selected_layers() {
for layer in document.metadata().selected_layers() {
shape_overlay.render_subpath_overlays(&shape_editor.selected_shape_state, &document.document_legacy, layer, responses);
}
@@ -386,7 +382,7 @@ impl Fsm for PathToolFsmState {
let shift_pressed = input.keyboard.get(add_to_selection as usize);
if tool_data.drag_start_pos == tool_data.previous_mouse_position {
responses.add(DocumentMessage::DeselectAllLayers);
responses.add(NodeGraphMessage::SetSelectNodes { nodes: vec![] });
} else {
shape_editor.select_all_in_quad(&document.document_legacy, [tool_data.drag_start_pos, tool_data.previous_mouse_position], !shift_pressed);
tool_data.refresh_overlays(document, shape_editor, shape_overlay, responses);
@@ -401,7 +397,7 @@ impl Fsm for PathToolFsmState {
let shift_pressed = input.keyboard.get(shift_mirror_distance as usize);
if tool_data.drag_start_pos == tool_data.previous_mouse_position {
responses.add(DocumentMessage::DeselectAllLayers);
responses.add(NodeGraphMessage::SetSelectNodes { nodes: vec![] });
} else {
shape_editor.select_all_in_quad(&document.document_legacy, [tool_data.drag_start_pos, tool_data.previous_mouse_position], !shift_pressed);
tool_data.refresh_overlays(document, shape_editor, shape_overlay, responses);
@@ -247,7 +247,7 @@ impl PenToolData {
let layer_path = document.get_path_for_new_layer();
// Get the position and set properties
let transform = document.document_legacy.metadata.document_to_viewport * document.document_legacy.multiply_transforms(&layer_path[..layer_path.len() - 1]).unwrap_or_default();
let transform = document.metadata().document_to_viewport * document.document_legacy.multiply_transforms(&layer_path[..layer_path.len() - 1]).unwrap_or_default();
let snapped_position = self.snap_manager.snap_position(responses, document, input.mouse.position);
let start_position = transform.inverse().transform_point2(snapped_position);
self.weight = line_weight;
@@ -566,7 +566,7 @@ impl Fsm for PenToolFsmState {
transform = DAffine2::IDENTITY;
}
transform = document.document_legacy.metadata.document_to_viewport * transform;
transform = document.metadata().document_to_viewport * transform;
let ToolMessage::Pen(event) = event else {
return self;
@@ -579,19 +579,19 @@ impl Fsm for PenToolFsmState {
(_, PenToolMessage::DocumentIsDirty) => {
// When the document has moved / needs to be redraw, re-render the overlays
// TODO the overlay system should probably receive this message instead of the tool
for layer in document.document_legacy.metadata.selected_layers() {
for layer in document.metadata().selected_layers() {
shape_overlay.render_subpath_overlays(&shape_editor.selected_shape_state, &document.document_legacy, layer, responses);
}
self
}
(_, PenToolMessage::SelectionChanged) => {
// Set the previously selected layers to invisible
for layer in document.document_legacy.metadata.all_layers() {
for layer in document.metadata().all_layers() {
shape_overlay.layer_overlay_visibility(&document.document_legacy, layer, false, responses);
}
// Redraw the overlays of the newly selected layers
for layer in document.document_legacy.metadata.selected_layers() {
for layer in document.metadata().selected_layers() {
shape_overlay.render_subpath_overlays(&shape_editor.selected_shape_state, &document.document_legacy, layer, responses);
}
self
File diff suppressed because it is too large Load Diff
@@ -202,7 +202,7 @@ impl Fsm for SplineToolFsmState {
..
} = tool_action_data;
let transform = document.document_legacy.metadata.document_to_viewport;
let transform = document.metadata().document_to_viewport;
let ToolMessage::Spline(event) = event else {
return self;
@@ -47,11 +47,12 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
let using_path_tool = tool_data.active_tool_type == ToolType::Path;
let selected_layers = document.layer_metadata.iter().filter_map(|(layer_path, data)| data.selected.then_some(layer_path)).collect::<Vec<_>>();
let selected_layers_n = document.metadata().selected_layers().collect::<Vec<_>>();
let mut selected = Selected::new(
&mut self.original_transforms,
&mut self.pivot,
&selected_layers,
&selected_layers_n,
responses,
&document.document_legacy,
Some(shape_editor),
@@ -228,7 +229,7 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
self.mouse_position = ipp.mouse.position;
}
SelectionChanged => {
let target_layers = document.document_legacy.metadata.selected_layers().collect();
let target_layers = document.metadata().selected_layers().collect();
shape_editor.set_selected_layers(target_layers);
}
TypeBackspace => self.transform_operation.grs_typed(self.typing.type_backspace(), &mut selected, self.snap),