mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-27 03:28:11 +08:00
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:
committed by
Keavon Chambers
co-authored by
Dennis Kobert
Keavon Chambers
parent
e1cdb2242d
commit
5827e989dc
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user