mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-22 19:18:12 +08:00
Initial work migrating vector layers to document graph
* Fix pen tool (except overlays) * Thumbnail of only the layer and not the composite * Fix occasional transform breakages * Constrain size of thumbnail * Insert new layers at the top * Broken layer tree * Fix crash when drawing * Reduce calls to send graph * Reduce calls to updating properties * Store cached transforms upon the document * Fix missing node UI updates * Fix fill tool and clean up imports and indentation * Error on overide existing layer * Fix pen tool (partially) * Fix some lints
This commit is contained in:
committed by
Keavon Chambers
parent
fc6cee372a
commit
4cd72edb64
@@ -1,10 +1,9 @@
|
||||
use crate::messages::portfolio::document::node_graph;
|
||||
use crate::messages::portfolio::document::node_graph::VectorDataModification;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use bezier_rs::{ManipulatorGroup, Subpath};
|
||||
use document_legacy::{LayerId, Operation};
|
||||
use graph_craft::document::NodeNetwork;
|
||||
use document_legacy::{document_metadata::LayerNodeIdentifier, LayerId, Operation};
|
||||
use graph_craft::document::{value::TaggedValue, DocumentNode, NodeId, NodeInput, NodeNetwork};
|
||||
use graphene_core::uuid::ManipulatorGroupId;
|
||||
|
||||
use glam::DAffine2;
|
||||
@@ -12,8 +11,10 @@ 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>) {
|
||||
let network = node_graph::new_vector_network(subpaths);
|
||||
new_custom_layer(network, layer_path, responses);
|
||||
responses.add(GraphOperationMessage::NewVectorLayer {
|
||||
id: *layer_path.last().unwrap(),
|
||||
subpaths,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn new_custom_layer(network: NodeNetwork, layer_path: Vec<LayerId>, responses: &mut VecDeque<Message>) {
|
||||
@@ -38,3 +39,63 @@ pub fn set_manipulator_mirror_angle(manipulator_groups: &Vec<ManipulatorGroup<Ma
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// An immutable reference to a layer within the document node graph for easy access.
|
||||
pub struct NodeGraphLayer<'a> {
|
||||
node_graph: &'a NodeNetwork,
|
||||
outwards_links: HashMap<NodeId, Vec<NodeId>>,
|
||||
layer_node: NodeId,
|
||||
}
|
||||
|
||||
impl<'a> NodeGraphLayer<'a> {
|
||||
/// Get the layer node from the document
|
||||
pub fn new(layer: LayerNodeIdentifier, document: &'a document_legacy::document::Document) -> Option<Self> {
|
||||
let node_graph = &document.document_network;
|
||||
let outwards_links = document.document_network.collect_outwards_links();
|
||||
|
||||
Some(Self {
|
||||
node_graph,
|
||||
outwards_links,
|
||||
layer_node: layer.to_node(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the nearest layer node from the path and the document
|
||||
pub fn new_from_path(layer: &[LayerId], document: &'a document_legacy::document::Document) -> Option<Self> {
|
||||
let node_graph = &document.document_network;
|
||||
let outwards_links = document.document_network.collect_outwards_links();
|
||||
|
||||
let Some(mut layer_node) = layer.last().copied() else {
|
||||
error!("Tried to modify root layer");
|
||||
return None;
|
||||
};
|
||||
while node_graph.nodes.get(&layer_node)?.name != "Layer" {
|
||||
layer_node = outwards_links.get(&layer_node)?.first().copied()?;
|
||||
}
|
||||
Some(Self {
|
||||
node_graph,
|
||||
outwards_links,
|
||||
layer_node,
|
||||
})
|
||||
}
|
||||
|
||||
/// Return an iterator up the primary flow of the layer
|
||||
pub fn primary_layer_flow(&self) -> impl Iterator<Item = (&'a DocumentNode, u64)> {
|
||||
self.node_graph.primary_flow_from_opt(Some(self.layer_node))
|
||||
}
|
||||
|
||||
/// Find a specific input of a node within the layer's primary flow
|
||||
pub fn find_input(&self, node_name: &str, index: usize) -> Option<&'a TaggedValue> {
|
||||
for (node, _id) in self.primary_layer_flow() {
|
||||
if node.name == node_name {
|
||||
let subpaths_input = node.inputs.get(index)?;
|
||||
let NodeInput::Value { tagged_value, .. } = subpaths_input else {
|
||||
continue;
|
||||
};
|
||||
|
||||
return Some(tagged_value);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@ use crate::application::generate_uuid;
|
||||
use crate::consts::VIEWPORT_GRID_ROUNDING_BIAS;
|
||||
use crate::consts::{COLOR_ACCENT, HIDE_HANDLE_DISTANCE, MANIPULATOR_GROUP_MARKER_SIZE, PATH_OUTLINE_WEIGHT};
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::tool_messages::pen_tool::{get_manipulator_groups, get_subpaths};
|
||||
|
||||
use bezier_rs::ManipulatorGroup;
|
||||
use document_legacy::document::Document;
|
||||
use document_legacy::document_metadata::LayerNodeIdentifier;
|
||||
use document_legacy::layers::style::{self, Fill, Stroke};
|
||||
use document_legacy::{LayerId, Operation};
|
||||
use graphene_core::raster::color::Color;
|
||||
@@ -35,8 +37,8 @@ const POINT_STROKE_WEIGHT: f64 = 2.;
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct OverlayRenderer {
|
||||
shape_overlay_cache: HashMap<LayerId, Vec<LayerId>>,
|
||||
manipulator_group_overlay_cache: HashMap<LayerId, HashMap<ManipulatorGroupId, ManipulatorGroupOverlays>>,
|
||||
shape_overlay_cache: HashMap<LayerNodeIdentifier, Vec<LayerId>>,
|
||||
manipulator_group_overlay_cache: HashMap<LayerNodeIdentifier, HashMap<ManipulatorGroupId, ManipulatorGroupOverlays>>,
|
||||
}
|
||||
|
||||
impl OverlayRenderer {
|
||||
@@ -44,116 +46,116 @@ impl OverlayRenderer {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn render_subpath_overlays(&mut self, selected_shape_state: &SelectedShapeState, document: &Document, layer_path: Vec<LayerId>, responses: &mut VecDeque<Message>) {
|
||||
let transform = document.generate_transform_relative_to_viewport(&layer_path).ok().unwrap();
|
||||
if let Ok(layer) = document.layer(&layer_path) {
|
||||
let layer_id = layer_path.last().unwrap();
|
||||
self.layer_overlay_visibility(document, layer_path.clone(), true, responses);
|
||||
pub fn query_cache(&self, layer: &LayerNodeIdentifier) -> Option<&Vec<LayerId>> {
|
||||
self.shape_overlay_cache.get(layer)
|
||||
}
|
||||
|
||||
if let Some(vector_data) = layer.as_vector_data() {
|
||||
let outline_cache = self.shape_overlay_cache.get(layer_id);
|
||||
trace!("Overlay: Outline cache {:?}", &outline_cache);
|
||||
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);
|
||||
|
||||
// Create an outline if we do not have a cached one
|
||||
if outline_cache.is_none() {
|
||||
let outline_path = self.create_shape_outline_overlay(graphene_core::vector::Subpath::from_bezier_rs(&vector_data.subpaths), responses);
|
||||
self.shape_overlay_cache.insert(*layer_id, outline_path.clone());
|
||||
Self::place_outline_overlays(outline_path.clone(), &transform, responses);
|
||||
trace!("Overlay: Creating new outline {:?}", &outline_path);
|
||||
} else if let Some(outline_path) = outline_cache {
|
||||
trace!("Overlay: Updating overlays for {:?} owning layer: {:?}", outline_path, layer_id);
|
||||
Self::modify_outline_overlays(outline_path.clone(), graphene_core::vector::Subpath::from_bezier_rs(&vector_data.subpaths), responses);
|
||||
Self::place_outline_overlays(outline_path.clone(), &transform, responses);
|
||||
}
|
||||
let Some(subpaths) = get_subpaths(layer, document) else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Create, place, and style the manipulator overlays
|
||||
for manipulator_group in vector_data.manipulator_groups() {
|
||||
let manipulator_group_cache = self.manipulator_group_overlay_cache.entry(*layer_id).or_default().entry(manipulator_group.id).or_default();
|
||||
self.layer_overlay_visibility(document, layer, true, responses);
|
||||
|
||||
// Only view in and out handles if they are not on top of the anchor
|
||||
let [in_handle, out_handle] = {
|
||||
let anchor = manipulator_group.anchor;
|
||||
let outline_cache = self.shape_overlay_cache.get(&layer);
|
||||
trace!("Overlay: Outline cache {:?}", &outline_cache);
|
||||
|
||||
let anchor_position = transform.transform_point2(anchor);
|
||||
let not_under_anchor = |&position: &DVec2| transform.transform_point2(position).distance_squared(anchor_position) >= HIDE_HANDLE_DISTANCE * HIDE_HANDLE_DISTANCE;
|
||||
let filter_handle = |manipulator: Option<DVec2>| manipulator.filter(not_under_anchor);
|
||||
[filter_handle(manipulator_group.in_handle), filter_handle(manipulator_group.out_handle)]
|
||||
};
|
||||
// Create an outline if we do not have a cached one
|
||||
if outline_cache.is_none() {
|
||||
let outline_path = self.create_shape_outline_overlay(graphene_core::vector::Subpath::from_bezier_rs(subpaths), responses);
|
||||
self.shape_overlay_cache.insert(layer, outline_path.clone());
|
||||
Self::place_outline_overlays(outline_path.clone(), &transform, responses);
|
||||
trace!("Overlay: Creating new outline {:?}", &outline_path);
|
||||
} else if let Some(outline_path) = outline_cache {
|
||||
trace!("Overlay: Updating overlays for {:?} owning layer: {:?}", outline_path, layer);
|
||||
Self::modify_outline_overlays(outline_path.clone(), graphene_core::vector::Subpath::from_bezier_rs(subpaths), responses);
|
||||
Self::place_outline_overlays(outline_path.clone(), &transform, responses);
|
||||
}
|
||||
|
||||
// Create anchor
|
||||
manipulator_group_cache.anchor = manipulator_group_cache.anchor.take().or_else(|| Some(Self::create_anchor_overlay(responses)));
|
||||
// Create or delete in handle
|
||||
if in_handle.is_none() {
|
||||
Self::remove_overlay(manipulator_group_cache.in_handle.take(), responses);
|
||||
Self::remove_overlay(manipulator_group_cache.in_line.take(), responses);
|
||||
// Create, place, and style the manipulator overlays
|
||||
for manipulator_group in get_manipulator_groups(subpaths) {
|
||||
let manipulator_group_cache = self.manipulator_group_overlay_cache.entry(layer).or_default().entry(manipulator_group.id).or_default();
|
||||
|
||||
// Only view in and out handles if they are not on top of the anchor
|
||||
let [in_handle, out_handle] = {
|
||||
let anchor = manipulator_group.anchor;
|
||||
|
||||
let anchor_position = transform.transform_point2(anchor);
|
||||
let not_under_anchor = |&position: &DVec2| transform.transform_point2(position).distance_squared(anchor_position) >= HIDE_HANDLE_DISTANCE * HIDE_HANDLE_DISTANCE;
|
||||
let filter_handle = |manipulator: Option<DVec2>| manipulator.filter(not_under_anchor);
|
||||
[filter_handle(manipulator_group.in_handle), filter_handle(manipulator_group.out_handle)]
|
||||
};
|
||||
|
||||
// Create anchor
|
||||
manipulator_group_cache.anchor = manipulator_group_cache.anchor.take().or_else(|| Some(Self::create_anchor_overlay(responses)));
|
||||
// Create or delete in handle
|
||||
if in_handle.is_none() {
|
||||
Self::remove_overlay(manipulator_group_cache.in_handle.take(), responses);
|
||||
Self::remove_overlay(manipulator_group_cache.in_line.take(), responses);
|
||||
} else {
|
||||
manipulator_group_cache.in_handle = manipulator_group_cache.in_handle.take().or_else(|| Self::create_handle_overlay_if_exists(in_handle, responses));
|
||||
manipulator_group_cache.in_line = manipulator_group_cache.in_line.take().or_else(|| Self::create_handle_line_overlay_if_exists(in_handle, responses));
|
||||
}
|
||||
// Create or delete out handle
|
||||
if out_handle.is_none() {
|
||||
Self::remove_overlay(manipulator_group_cache.out_handle.take(), responses);
|
||||
Self::remove_overlay(manipulator_group_cache.out_line.take(), responses);
|
||||
} else {
|
||||
manipulator_group_cache.out_handle = manipulator_group_cache.out_handle.take().or_else(|| Self::create_handle_overlay_if_exists(out_handle, responses));
|
||||
manipulator_group_cache.out_line = manipulator_group_cache.out_line.take().or_else(|| Self::create_handle_line_overlay_if_exists(out_handle, responses));
|
||||
}
|
||||
|
||||
// Update placement and style
|
||||
Self::place_manipulator_group_overlays(manipulator_group, manipulator_group_cache, &transform, responses);
|
||||
Self::style_overlays(selected_shape_state, layer, manipulator_group, manipulator_group_cache, responses);
|
||||
}
|
||||
|
||||
if let Some(layer_overlays) = self.manipulator_group_overlay_cache.get_mut(&layer) {
|
||||
if layer_overlays.len() > subpaths.iter().map(|subpath| subpath.len()).sum() {
|
||||
layer_overlays.retain(|manipulator, manipulator_group_overlays| {
|
||||
if get_manipulator_groups(subpaths).any(|current_manipulator| current_manipulator.id == *manipulator) {
|
||||
true
|
||||
} else {
|
||||
manipulator_group_cache.in_handle = manipulator_group_cache.in_handle.take().or_else(|| Self::create_handle_overlay_if_exists(in_handle, responses));
|
||||
manipulator_group_cache.in_line = manipulator_group_cache.in_line.take().or_else(|| Self::create_handle_line_overlay_if_exists(in_handle, responses));
|
||||
Self::remove_manipulator_group_overlays(manipulator_group_overlays, responses);
|
||||
false
|
||||
}
|
||||
// Create or delete out handle
|
||||
if out_handle.is_none() {
|
||||
Self::remove_overlay(manipulator_group_cache.out_handle.take(), responses);
|
||||
Self::remove_overlay(manipulator_group_cache.out_line.take(), responses);
|
||||
} else {
|
||||
manipulator_group_cache.out_handle = manipulator_group_cache.out_handle.take().or_else(|| Self::create_handle_overlay_if_exists(out_handle, responses));
|
||||
manipulator_group_cache.out_line = manipulator_group_cache.out_line.take().or_else(|| Self::create_handle_line_overlay_if_exists(out_handle, responses));
|
||||
}
|
||||
|
||||
// Update placement and style
|
||||
Self::place_manipulator_group_overlays(manipulator_group, manipulator_group_cache, &transform, responses);
|
||||
Self::style_overlays(selected_shape_state, &layer_path, manipulator_group, manipulator_group_cache, responses);
|
||||
}
|
||||
|
||||
if let Some(layer_overlays) = self.manipulator_group_overlay_cache.get_mut(layer_id) {
|
||||
if layer_overlays.len() > vector_data.manipulator_groups().count() {
|
||||
layer_overlays.retain(|manipulator, manipulator_group_overlays| {
|
||||
if vector_data.manipulator_groups().any(|current_manipulator| current_manipulator.id == *manipulator) {
|
||||
true
|
||||
} else {
|
||||
Self::remove_manipulator_group_overlays(manipulator_group_overlays, responses);
|
||||
false
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
// TODO Handle removing shapes from cache so we don't memory leak
|
||||
// Eventually will get replaced with am immediate mode renderer for overlays
|
||||
});
|
||||
}
|
||||
}
|
||||
// TODO Handle removing shapes from cache so we don't memory leak
|
||||
// Eventually will get replaced with am immediate mode renderer for overlays
|
||||
|
||||
responses.add(OverlaysMessage::Rerender);
|
||||
}
|
||||
|
||||
pub fn clear_subpath_overlays(&mut self, _document: &Document, layer_path: Vec<LayerId>, responses: &mut VecDeque<Message>) {
|
||||
let layer_id = layer_path.last().unwrap();
|
||||
|
||||
pub fn clear_subpath_overlays(&mut self, _document: &Document, layer: LayerNodeIdentifier, responses: &mut VecDeque<Message>) {
|
||||
// Remove the shape outline overlays
|
||||
if let Some(overlay_path) = self.shape_overlay_cache.get(layer_id) {
|
||||
if let Some(overlay_path) = self.shape_overlay_cache.get(&layer) {
|
||||
Self::remove_outline_overlays(overlay_path.clone(), responses)
|
||||
}
|
||||
self.shape_overlay_cache.remove(layer_id);
|
||||
self.shape_overlay_cache.remove(&layer);
|
||||
|
||||
// Remove the ManipulatorGroup overlays
|
||||
let Some(layer_cache) = self.manipulator_group_overlay_cache.remove(layer_id) else { return };
|
||||
let Some(layer_cache) = self.manipulator_group_overlay_cache.remove(&layer) else { return };
|
||||
|
||||
for manipulator_group_overlays in layer_cache.values() {
|
||||
Self::remove_manipulator_group_overlays(manipulator_group_overlays, responses);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn layer_overlay_visibility(&mut self, document: &Document, layer_path: Vec<LayerId>, visibility: bool, responses: &mut VecDeque<Message>) {
|
||||
let layer_id = layer_path.last().unwrap();
|
||||
|
||||
pub fn layer_overlay_visibility(&mut self, document: &Document, layer: LayerNodeIdentifier, visibility: bool, responses: &mut VecDeque<Message>) {
|
||||
// Hide the shape outline overlays
|
||||
if let Some(overlay_path) = self.shape_overlay_cache.get(layer_id) {
|
||||
if let Some(overlay_path) = self.shape_overlay_cache.get(&layer) {
|
||||
Self::set_outline_overlay_visibility(overlay_path.clone(), visibility, responses);
|
||||
}
|
||||
|
||||
// Hide the manipulator group overlays
|
||||
let Some(manipulator_groups) = self.manipulator_group_overlay_cache.get(layer_id) else { return };
|
||||
let Some(manipulator_groups) = self.manipulator_group_overlay_cache.get(&layer) else { return };
|
||||
if visibility {
|
||||
let Ok(layer) = document.layer(&layer_path) else { return };
|
||||
let Some(vector_data) = layer.as_vector_data() else { return };
|
||||
for manipulator_group in vector_data.manipulator_groups() {
|
||||
let Some(subpaths) = get_subpaths(layer, document) else { return };
|
||||
for manipulator_group in get_manipulator_groups(subpaths) {
|
||||
let id = manipulator_group.id;
|
||||
if let Some(manipulator_group_overlays) = manipulator_groups.get(&id) {
|
||||
Self::set_manipulator_group_overlay_visibility(manipulator_group_overlays, visibility, responses);
|
||||
@@ -338,11 +340,11 @@ impl OverlayRenderer {
|
||||
}
|
||||
|
||||
/// Sets the overlay style for this point.
|
||||
fn style_overlays(state: &SelectedShapeState, layer_path: &[LayerId], manipulator_group: &GraphiteManipulatorGroup, overlays: &ManipulatorGroupOverlays, responses: &mut VecDeque<Message>) {
|
||||
fn style_overlays(state: &SelectedShapeState, layer: LayerNodeIdentifier, manipulator_group: &GraphiteManipulatorGroup, overlays: &ManipulatorGroupOverlays, responses: &mut VecDeque<Message>) {
|
||||
// TODO Move the style definitions out of the Subpath, should be looked up from a stylesheet or similar
|
||||
let selected_style = style::PathStyle::new(Some(Stroke::new(Some(COLOR_ACCENT), POINT_STROKE_WEIGHT + 1.0)), Fill::solid(COLOR_ACCENT));
|
||||
let deselected_style = style::PathStyle::new(Some(Stroke::new(Some(COLOR_ACCENT), POINT_STROKE_WEIGHT)), Fill::solid(Color::WHITE));
|
||||
let selected_shape_state = state.get(layer_path);
|
||||
let selected_shape_state = state.get(&layer);
|
||||
// Update if the manipulator points are shown as selected
|
||||
// Here the index is important, even though overlays[..] has five elements we only care about the first three
|
||||
for (index, overlay) in [&overlays.in_handle, &overlays.out_handle, &overlays.anchor].into_iter().enumerate() {
|
||||
|
||||
@@ -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.root.transform;
|
||||
let root_transform = document.document_legacy.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.root.transform;
|
||||
let root_transform = document.document_legacy.metadata.document_to_viewport;
|
||||
root_transform.transform_point2(self.drag_start)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use crate::consts::DRAG_THRESHOLD;
|
||||
use crate::messages::portfolio::document::node_graph::VectorDataModification;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::tool_messages::pen_tool::{get_manipulator_from_id, get_manipulator_groups, get_mirror_handles, get_subpaths};
|
||||
|
||||
use bezier_rs::{Bezier, ManipulatorGroup, TValue};
|
||||
use document_legacy::document::Document;
|
||||
use document_legacy::LayerId;
|
||||
use document_legacy::document_metadata::LayerNodeIdentifier;
|
||||
use graphene_core::uuid::ManipulatorGroupId;
|
||||
use graphene_core::vector::{ManipulatorPointId, SelectedType, VectorData};
|
||||
use graphene_core::vector::{ManipulatorPointId, SelectedType};
|
||||
|
||||
use glam::DVec2;
|
||||
|
||||
@@ -40,25 +41,25 @@ impl SelectedLayerState {
|
||||
}
|
||||
}
|
||||
|
||||
pub type SelectedShapeState = HashMap<Vec<LayerId>, SelectedLayerState>;
|
||||
pub type SelectedShapeState = HashMap<LayerNodeIdentifier, SelectedLayerState>;
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ShapeState {
|
||||
// The layers we can select and edit manipulators (anchors and handles) from
|
||||
pub selected_shape_state: SelectedShapeState,
|
||||
}
|
||||
|
||||
pub struct SelectedPointsInfo<'a> {
|
||||
pub points: Vec<ManipulatorPointInfo<'a>>,
|
||||
pub struct SelectedPointsInfo {
|
||||
pub points: Vec<ManipulatorPointInfo>,
|
||||
pub offset: DVec2,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub struct ManipulatorPointInfo<'a> {
|
||||
pub shape_layer_path: &'a [LayerId],
|
||||
pub struct ManipulatorPointInfo {
|
||||
pub layer: LayerNodeIdentifier,
|
||||
pub point_id: ManipulatorPointId,
|
||||
}
|
||||
|
||||
pub type OpposingHandleLengths = HashMap<Vec<LayerId>, HashMap<ManipulatorGroupId, Option<f64>>>;
|
||||
pub type OpposingHandleLengths = HashMap<LayerNodeIdentifier, HashMap<ManipulatorGroupId, Option<f64>>>;
|
||||
|
||||
// TODO Consider keeping a list of selected manipulators to minimize traversals of the layers
|
||||
impl ShapeState {
|
||||
@@ -69,14 +70,14 @@ impl ShapeState {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some((shape_layer_path, manipulator_point_id)) = self.find_nearest_point_indices(document, mouse_position, select_threshold) {
|
||||
if let Some((layer, manipulator_point_id)) = self.find_nearest_point_indices(document, mouse_position, select_threshold) {
|
||||
trace!("Selecting... manipulator point: {:?}", manipulator_point_id);
|
||||
|
||||
let vector_data = document.layer(&shape_layer_path).ok()?.as_vector_data()?;
|
||||
let manipulator_group = vector_data.manipulator_groups().find(|group| group.id == manipulator_point_id.group)?;
|
||||
let subpaths = get_subpaths(layer, document)?;
|
||||
let manipulator_group = get_manipulator_groups(subpaths).find(|group| group.id == manipulator_point_id.group)?;
|
||||
let point_position = manipulator_point_id.manipulator_type.get_position(manipulator_group)?;
|
||||
|
||||
let selected_shape_state = self.selected_shape_state.get(&shape_layer_path)?;
|
||||
let selected_shape_state = self.selected_shape_state.get(&layer)?;
|
||||
let already_selected = selected_shape_state.is_selected(manipulator_point_id);
|
||||
|
||||
// Should we select or deselect the point?
|
||||
@@ -90,24 +91,21 @@ impl ShapeState {
|
||||
}
|
||||
|
||||
// Add to the selected points
|
||||
let selected_shape_state = self.selected_shape_state.get_mut(&shape_layer_path)?;
|
||||
let selected_shape_state = self.selected_shape_state.get_mut(&layer)?;
|
||||
selected_shape_state.select_point(manipulator_point_id);
|
||||
|
||||
// Offset to snap the selected point to the cursor
|
||||
let offset = document
|
||||
.generate_transform_relative_to_viewport(&shape_layer_path)
|
||||
.map(|viewspace| mouse_position - viewspace.transform_point2(point_position))
|
||||
.unwrap_or_default();
|
||||
let offset = mouse_position - document.metadata.transform_from_viewport(layer).transform_point2(point_position);
|
||||
|
||||
let points = self
|
||||
.selected_shape_state
|
||||
.iter()
|
||||
.flat_map(|(shape_layer_path, state)| state.selected_points.iter().map(|&point_id| ManipulatorPointInfo { shape_layer_path, point_id }))
|
||||
.flat_map(|(layer, state)| state.selected_points.iter().map(|&point_id| ManipulatorPointInfo { layer: *layer, point_id }))
|
||||
.collect();
|
||||
|
||||
return Some(SelectedPointsInfo { points, offset });
|
||||
} else {
|
||||
let selected_shape_state = self.selected_shape_state.get_mut(&shape_layer_path)?;
|
||||
let selected_shape_state = self.selected_shape_state.get_mut(&layer)?;
|
||||
selected_shape_state.deselect_point(manipulator_point_id);
|
||||
|
||||
return None;
|
||||
@@ -117,14 +115,12 @@ impl ShapeState {
|
||||
}
|
||||
|
||||
pub fn select_all_points(&mut self, document: &Document) {
|
||||
for (layer_path, selected_layer_state) in self.selected_shape_state.iter_mut() {
|
||||
let Ok(layer) = document.layer(layer_path) else { continue };
|
||||
let Some(vector_data) = layer.as_vector_data() else { continue };
|
||||
|
||||
for group in vector_data.manipulator_groups() {
|
||||
selected_layer_state.select_point(ManipulatorPointId::new(group.id, SelectedType::Anchor));
|
||||
for (layer, state) in self.selected_shape_state.iter_mut() {
|
||||
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] {
|
||||
selected_layer_state.deselect_point(ManipulatorPointId::new(group.id, *selected_type));
|
||||
state.deselect_point(ManipulatorPointId::new(manipulator.id, *selected_type));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,14 +131,14 @@ impl ShapeState {
|
||||
}
|
||||
|
||||
/// Set the shapes we consider for selection, we will choose draggable manipulators from these shapes.
|
||||
pub fn set_selected_layers(&mut self, target_layers: Vec<Vec<LayerId>>) {
|
||||
pub fn set_selected_layers(&mut self, target_layers: Vec<LayerNodeIdentifier>) {
|
||||
self.selected_shape_state.retain(|layer_path, _| target_layers.contains(layer_path));
|
||||
for layer in target_layers {
|
||||
self.selected_shape_state.entry(layer).or_insert_with(SelectedLayerState::default);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn selected_layers(&self) -> impl Iterator<Item = &Vec<LayerId>> {
|
||||
pub fn selected_layers(&self) -> impl Iterator<Item = &LayerNodeIdentifier> {
|
||||
self.selected_shape_state.keys()
|
||||
}
|
||||
|
||||
@@ -157,15 +153,14 @@ impl ShapeState {
|
||||
|
||||
/// A mutable iterator of all the manipulators, regardless of selection.
|
||||
pub fn manipulator_groups<'a>(&'a self, document: &'a Document) -> impl Iterator<Item = &'a ManipulatorGroup<ManipulatorGroupId>> {
|
||||
self.iter(document).flat_map(|shape| shape.manipulator_groups())
|
||||
self.iter(document).flat_map(|subpaths| get_manipulator_groups(subpaths))
|
||||
}
|
||||
|
||||
// Sets the selected points to all points for the corresponding intersection
|
||||
pub fn select_all_anchors(&mut self, document: &Document, layer_path: &[LayerId]) {
|
||||
let Ok(layer) = document.layer(layer_path) else { return };
|
||||
let Some(vector_data) = layer.as_vector_data() else { return };
|
||||
let Some(state) = self.selected_shape_state.get_mut(layer_path) else { return };
|
||||
for manipulator in vector_data.manipulator_groups() {
|
||||
pub fn select_all_anchors(&mut self, document: &Document, layer: LayerNodeIdentifier) {
|
||||
let Some(subpaths) = get_subpaths(layer, document) else { return };
|
||||
let Some(state) = self.selected_shape_state.get_mut(&layer) else { return };
|
||||
for manipulator in get_manipulator_groups(subpaths) {
|
||||
state.select_point(ManipulatorPointId::new(manipulator.id, SelectedType::Anchor))
|
||||
}
|
||||
}
|
||||
@@ -219,7 +214,7 @@ impl ShapeState {
|
||||
.selected_shape_state
|
||||
.iter()
|
||||
.filter_map(|(layer_id, selection_state)| {
|
||||
let layer = document.layer(layer_id).ok()?;
|
||||
let layer = document.layer(&layer_id.to_path()).ok()?;
|
||||
let vector_data = layer.as_vector_data()?;
|
||||
Some((vector_data, selection_state))
|
||||
})
|
||||
@@ -236,7 +231,7 @@ impl ShapeState {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn smooth_manipulator_group(&self, subpath: &bezier_rs::Subpath<ManipulatorGroupId>, index: usize, responses: &mut VecDeque<Message>, layer_path: &[u64]) {
|
||||
pub fn smooth_manipulator_group(&self, subpath: &bezier_rs::Subpath<ManipulatorGroupId>, index: usize, responses: &mut VecDeque<Message>, layer: &LayerNodeIdentifier) {
|
||||
let manipulator_groups = subpath.manipulator_groups();
|
||||
let manipulator = manipulator_groups[index];
|
||||
|
||||
@@ -269,7 +264,7 @@ impl ShapeState {
|
||||
|
||||
// Mirror the angle but not the distance
|
||||
responses.add(GraphOperationMessage::Vector {
|
||||
layer: layer_path.to_vec(),
|
||||
layer: layer.to_path(),
|
||||
modification: VectorDataModification::SetManipulatorHandleMirroring {
|
||||
id: manipulator.id,
|
||||
mirror_angle: true,
|
||||
@@ -290,7 +285,7 @@ impl ShapeState {
|
||||
if let Some(in_handle) = length_previous.map(|length| anchor_position + handle_vector * length) {
|
||||
let point = ManipulatorPointId::new(manipulator.id, SelectedType::InHandle);
|
||||
responses.add(GraphOperationMessage::Vector {
|
||||
layer: layer_path.to_vec(),
|
||||
layer: layer.to_path(),
|
||||
modification: VectorDataModification::SetManipulatorPosition { point, position: in_handle },
|
||||
});
|
||||
}
|
||||
@@ -298,7 +293,7 @@ impl ShapeState {
|
||||
if let Some(out_handle) = length_next.map(|length| anchor_position - handle_vector * length) {
|
||||
let point = ManipulatorPointId::new(manipulator.id, SelectedType::OutHandle);
|
||||
responses.add(GraphOperationMessage::Vector {
|
||||
layer: layer_path.to_vec(),
|
||||
layer: layer.to_path(),
|
||||
modification: VectorDataModification::SetManipulatorPosition { point, position: out_handle },
|
||||
});
|
||||
}
|
||||
@@ -309,7 +304,7 @@ impl ShapeState {
|
||||
let mut skip_set = HashSet::new();
|
||||
|
||||
for (layer_id, layer_state) in self.selected_shape_state.iter() {
|
||||
let layer = document.layer(layer_id).ok()?;
|
||||
let layer = document.layer(&layer_id.to_path()).ok()?;
|
||||
let vector_data = layer.as_vector_data()?;
|
||||
|
||||
for point in layer_state.selected_points.iter() {
|
||||
@@ -338,7 +333,7 @@ impl ShapeState {
|
||||
let out_handle = ManipulatorPointId::new(point.group, SelectedType::OutHandle);
|
||||
if let Some(position) = group.out_handle {
|
||||
responses.add(GraphOperationMessage::Vector {
|
||||
layer: layer_id.to_vec(),
|
||||
layer: layer_id.to_path(),
|
||||
modification: VectorDataModification::SetManipulatorPosition { point: out_handle, position },
|
||||
});
|
||||
}
|
||||
@@ -347,7 +342,7 @@ impl ShapeState {
|
||||
let in_handle = ManipulatorPointId::new(point.group, SelectedType::InHandle);
|
||||
if let Some(position) = group.in_handle {
|
||||
responses.add(GraphOperationMessage::Vector {
|
||||
layer: layer_id.to_vec(),
|
||||
layer: layer_id.to_path(),
|
||||
modification: VectorDataModification::SetManipulatorPosition { point: in_handle, position },
|
||||
});
|
||||
}
|
||||
@@ -373,11 +368,11 @@ impl ShapeState {
|
||||
|
||||
/// Move the selected points by dragging the mouse.
|
||||
pub fn move_selected_points(&self, document: &Document, delta: DVec2, mirror_distance: bool, responses: &mut VecDeque<Message>) {
|
||||
for (layer_path, state) in &self.selected_shape_state {
|
||||
let Ok(layer) = document.layer(layer_path) else { continue };
|
||||
let Some(vector_data) = layer.as_vector_data() else { continue };
|
||||
for (&layer, state) in &self.selected_shape_state {
|
||||
let Some(subpaths) = get_subpaths(layer, document) else { continue };
|
||||
let Some(mirror_angle) = get_mirror_handles(layer, document) else { continue };
|
||||
|
||||
let transform = document.multiply_transforms(layer_path).unwrap_or_default();
|
||||
let transform = document.metadata.transform_from_viewport(layer);
|
||||
let delta = transform.inverse().transform_vector2(delta);
|
||||
|
||||
for &point in state.selected_points.iter() {
|
||||
@@ -385,13 +380,13 @@ impl ShapeState {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(group) = vector_data.manipulator_from_id(point.group) else { continue };
|
||||
let Some(group) =get_manipulator_from_id(subpaths,point.group) else { continue };
|
||||
|
||||
let mut move_point = |point: ManipulatorPointId| {
|
||||
let Some(previous_position) = point.manipulator_type.get_position(group) else { return };
|
||||
let position = previous_position + delta;
|
||||
responses.add(GraphOperationMessage::Vector {
|
||||
layer: layer_path.clone(),
|
||||
layer: layer.to_path(),
|
||||
modification: VectorDataModification::SetManipulatorPosition { point, position },
|
||||
});
|
||||
};
|
||||
@@ -404,13 +399,13 @@ impl ShapeState {
|
||||
}
|
||||
|
||||
if mirror_distance && point.manipulator_type != SelectedType::Anchor {
|
||||
let mut mirror = vector_data.mirror_angle.contains(&point.group);
|
||||
let mut mirror = mirror_angle.contains(&point.group);
|
||||
|
||||
// If there is no opposing handle, we mirror even if mirror_angle doesn't contain the group
|
||||
// and set angle mirroring to true.
|
||||
if !mirror && point.manipulator_type.opposite().get_position(group).is_none() {
|
||||
responses.add(GraphOperationMessage::Vector {
|
||||
layer: layer_path.clone(),
|
||||
layer: layer.to_path(),
|
||||
modification: VectorDataModification::SetManipulatorHandleMirroring { id: group.id, mirror_angle: true },
|
||||
});
|
||||
mirror = true;
|
||||
@@ -428,7 +423,7 @@ impl ShapeState {
|
||||
}
|
||||
let position = group.anchor - (original_handle_position - group.anchor);
|
||||
responses.add(GraphOperationMessage::Vector {
|
||||
layer: layer_path.clone(),
|
||||
layer: layer.to_path(),
|
||||
modification: VectorDataModification::SetManipulatorPosition { point, position },
|
||||
});
|
||||
}
|
||||
@@ -439,13 +434,13 @@ impl ShapeState {
|
||||
|
||||
/// Delete selected and mirrored handles with zero length when the drag stops.
|
||||
pub fn delete_selected_handles_with_zero_length(&self, document: &Document, opposing_handle_lengths: &Option<OpposingHandleLengths>, responses: &mut VecDeque<Message>) {
|
||||
for (layer_path, state) in &self.selected_shape_state {
|
||||
let Ok(layer) = document.layer(layer_path) else { continue };
|
||||
let Some(vector_data) = layer.as_vector_data() else { continue };
|
||||
for (&layer, state) in &self.selected_shape_state {
|
||||
let Some(subpaths) = get_subpaths(layer, document) else { continue };
|
||||
let Some(mirror_angle) = get_mirror_handles(layer, document) else { continue };
|
||||
|
||||
let opposing_handle_lengths = opposing_handle_lengths.as_ref().and_then(|lengths| lengths.get(layer_path));
|
||||
let opposing_handle_lengths = opposing_handle_lengths.as_ref().and_then(|lengths| lengths.get(&layer));
|
||||
|
||||
let transform = document.multiply_transforms(layer_path).unwrap_or(glam::DAffine2::IDENTITY);
|
||||
let transform = document.metadata.transform_from_viewport(layer);
|
||||
|
||||
for &point in state.selected_points.iter() {
|
||||
let anchor = ManipulatorPointId::new(point.group, SelectedType::Anchor);
|
||||
@@ -453,7 +448,7 @@ impl ShapeState {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(group) = vector_data.manipulator_from_id(point.group) else { continue };
|
||||
let Some(group) = get_manipulator_from_id(subpaths,point.group) else { continue };
|
||||
|
||||
let anchor_position = transform.transform_point2(group.anchor);
|
||||
|
||||
@@ -465,17 +460,17 @@ impl ShapeState {
|
||||
|
||||
if (anchor_position - point_position).length() < DRAG_THRESHOLD {
|
||||
responses.add(GraphOperationMessage::Vector {
|
||||
layer: layer_path.clone(),
|
||||
layer: layer.to_path(),
|
||||
modification: VectorDataModification::RemoveManipulatorPoint { point },
|
||||
});
|
||||
|
||||
// Remove opposing handle if it is not selected and is mirrored.
|
||||
let opposite_point = ManipulatorPointId::new(point.group, point.manipulator_type.opposite());
|
||||
if !state.is_selected(opposite_point) && vector_data.mirror_angle.contains(&point.group) {
|
||||
if !state.is_selected(opposite_point) && mirror_angle.contains(&point.group) {
|
||||
if let Some(lengths) = opposing_handle_lengths {
|
||||
if lengths.contains_key(&point.group) {
|
||||
responses.add(GraphOperationMessage::Vector {
|
||||
layer: layer_path.clone(),
|
||||
layer: layer.to_path(),
|
||||
modification: VectorDataModification::RemoveManipulatorPoint { point: opposite_point },
|
||||
});
|
||||
}
|
||||
@@ -490,11 +485,9 @@ impl ShapeState {
|
||||
pub fn opposing_handle_lengths(&self, document: &Document) -> OpposingHandleLengths {
|
||||
self.selected_shape_state
|
||||
.iter()
|
||||
.filter_map(|(path, state)| {
|
||||
let layer = document.layer(path).ok()?;
|
||||
let vector_data = layer.as_vector_data()?;
|
||||
let opposing_handle_lengths = vector_data
|
||||
.subpaths
|
||||
.filter_map(|(&layer, state)| {
|
||||
let subpaths = get_subpaths(layer, document)?;
|
||||
let opposing_handle_lengths = subpaths
|
||||
.iter()
|
||||
.flat_map(|subpath| {
|
||||
subpath.manipulator_groups().iter().filter_map(|manipulator_group| {
|
||||
@@ -525,21 +518,21 @@ impl ShapeState {
|
||||
})
|
||||
})
|
||||
.collect::<HashMap<_, _>>();
|
||||
Some((path.clone(), opposing_handle_lengths))
|
||||
Some((layer, opposing_handle_lengths))
|
||||
})
|
||||
.collect::<HashMap<_, _>>()
|
||||
}
|
||||
|
||||
/// Reset the opposing handle lengths.
|
||||
pub fn reset_opposing_handle_lengths(&self, document: &Document, opposing_handle_lengths: &OpposingHandleLengths, responses: &mut VecDeque<Message>) {
|
||||
for (path, state) in &self.selected_shape_state {
|
||||
let Ok(layer) = document.layer(path) else { continue };
|
||||
let Some(vector_data) = layer.as_vector_data() else { continue };
|
||||
let Some(opposing_handle_lengths) = opposing_handle_lengths.get(path) else { continue };
|
||||
for (&layer, state) in &self.selected_shape_state {
|
||||
let Some(subpaths) = get_subpaths(layer, document) else { continue };
|
||||
let Some(mirror_angle) = get_mirror_handles(layer, document) else { continue };
|
||||
let Some(opposing_handle_lengths) = opposing_handle_lengths.get(&layer) else { continue };
|
||||
|
||||
for subpath in &vector_data.subpaths {
|
||||
for subpath in subpaths {
|
||||
for manipulator_group in subpath.manipulator_groups() {
|
||||
if !vector_data.mirror_angle.contains(&manipulator_group.id) {
|
||||
if !mirror_angle.contains(&manipulator_group.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -563,7 +556,7 @@ impl ShapeState {
|
||||
|
||||
let Some(opposing_handle_length) = opposing_handle_length else {
|
||||
responses.add(GraphOperationMessage::Vector {
|
||||
layer: path.to_vec(),
|
||||
layer: layer.to_path(),
|
||||
modification: VectorDataModification::RemoveManipulatorPoint {
|
||||
point: ManipulatorPointId::new(manipulator_group.id, single_selected_handle.opposite()),
|
||||
},
|
||||
@@ -582,7 +575,7 @@ impl ShapeState {
|
||||
assert!(position.is_finite(), "Opposing handle not finite!");
|
||||
|
||||
responses.add(GraphOperationMessage::Vector {
|
||||
layer: path.to_vec(),
|
||||
layer: layer.to_path(),
|
||||
modification: VectorDataModification::SetManipulatorPosition { point, position },
|
||||
});
|
||||
}
|
||||
@@ -595,7 +588,7 @@ impl ShapeState {
|
||||
for (layer, state) in &self.selected_shape_state {
|
||||
for &point in &state.selected_points {
|
||||
responses.add(GraphOperationMessage::Vector {
|
||||
layer: layer.to_vec(),
|
||||
layer: layer.to_path(),
|
||||
modification: VectorDataModification::RemoveManipulatorPoint { point },
|
||||
})
|
||||
}
|
||||
@@ -607,7 +600,7 @@ impl ShapeState {
|
||||
for (layer, state) in &self.selected_shape_state {
|
||||
for point in &state.selected_points {
|
||||
responses.add(GraphOperationMessage::Vector {
|
||||
layer: layer.to_vec(),
|
||||
layer: layer.to_path(),
|
||||
modification: VectorDataModification::ToggleManipulatorHandleMirroring { id: point.group },
|
||||
})
|
||||
}
|
||||
@@ -619,7 +612,7 @@ impl ShapeState {
|
||||
for (layer, state) in &self.selected_shape_state {
|
||||
for point in &state.selected_points {
|
||||
responses.add(GraphOperationMessage::Vector {
|
||||
layer: layer.to_vec(),
|
||||
layer: layer.to_path(),
|
||||
modification: VectorDataModification::SetManipulatorHandleMirroring { id: point.group, mirror_angle },
|
||||
});
|
||||
}
|
||||
@@ -627,27 +620,24 @@ impl ShapeState {
|
||||
}
|
||||
|
||||
/// Iterate over the shapes.
|
||||
pub fn iter<'a>(&'a self, document: &'a Document) -> impl Iterator<Item = &'a VectorData> + 'a {
|
||||
self.selected_shape_state
|
||||
.keys()
|
||||
.flat_map(|layer_id| document.layer(layer_id))
|
||||
.filter_map(|shape| shape.as_vector_data())
|
||||
pub fn iter<'a>(&'a self, document: &'a Document) -> impl Iterator<Item = &'a Vec<bezier_rs::Subpath<ManipulatorGroupId>>> + 'a {
|
||||
self.selected_shape_state.keys().filter_map(|&layer| get_subpaths(layer, document))
|
||||
}
|
||||
|
||||
/// Find a [ManipulatorPoint] that is within the selection threshold and return the layer path, an index to the [ManipulatorGroup], and an enum index for [ManipulatorPoint].
|
||||
pub fn find_nearest_point_indices(&mut self, document: &Document, mouse_position: DVec2, select_threshold: f64) -> Option<(Vec<LayerId>, ManipulatorPointId)> {
|
||||
pub fn find_nearest_point_indices(&mut self, document: &Document, mouse_position: DVec2, select_threshold: f64) -> Option<(LayerNodeIdentifier, ManipulatorPointId)> {
|
||||
if self.selected_shape_state.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let select_threshold_squared = select_threshold * select_threshold;
|
||||
// Find the closest control point among all elements of shapes_to_modify
|
||||
for layer in self.selected_shape_state.keys() {
|
||||
for &layer in self.selected_shape_state.keys() {
|
||||
if let Some((manipulator_point_id, distance_squared)) = Self::closest_point_in_layer(document, layer, mouse_position) {
|
||||
// Choose the first point under the threshold
|
||||
if distance_squared < select_threshold_squared {
|
||||
trace!("Selecting... manipulator point: {:?}", manipulator_point_id);
|
||||
return Some((layer.clone(), manipulator_point_id));
|
||||
return Some((layer, manipulator_point_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -659,20 +649,18 @@ impl ShapeState {
|
||||
/// Find the closest manipulator, manipulator point, and distance so we can select path elements.
|
||||
/// Brute force comparison to determine which manipulator (handle or anchor) we want to select taking O(n) time.
|
||||
/// Return value is an `Option` of the tuple representing `(ManipulatorPointId, distance squared)`.
|
||||
fn closest_point_in_layer(document: &Document, layer_path: &[LayerId], pos: glam::DVec2) -> Option<(ManipulatorPointId, f64)> {
|
||||
fn closest_point_in_layer(document: &Document, layer: LayerNodeIdentifier, pos: glam::DVec2) -> Option<(ManipulatorPointId, f64)> {
|
||||
let mut closest_distance_squared: f64 = f64::MAX;
|
||||
let mut result = None;
|
||||
|
||||
let vector_data = document.layer(layer_path).ok()?.as_vector_data()?;
|
||||
let viewspace = document.generate_transform_relative_to_viewport(layer_path).ok()?;
|
||||
for subpath in &vector_data.subpaths {
|
||||
for manipulator in subpath.manipulator_groups() {
|
||||
let (selected, distance_squared) = SelectedType::closest_widget(manipulator, viewspace, pos, crate::consts::HIDE_HANDLE_DISTANCE);
|
||||
let subpaths = get_subpaths(layer, document)?;
|
||||
let viewspace = document.metadata.transform_from_viewport(layer);
|
||||
for manipulator in get_manipulator_groups(subpaths) {
|
||||
let (selected, distance_squared) = SelectedType::closest_widget(manipulator, viewspace, pos, crate::consts::HIDE_HANDLE_DISTANCE);
|
||||
|
||||
if distance_squared < closest_distance_squared {
|
||||
closest_distance_squared = distance_squared;
|
||||
result = Some((ManipulatorPointId::new(manipulator.id, selected), distance_squared));
|
||||
}
|
||||
if distance_squared < closest_distance_squared {
|
||||
closest_distance_squared = distance_squared;
|
||||
result = Some((ManipulatorPointId::new(manipulator.id, selected), distance_squared));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -680,17 +668,17 @@ 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_path: &[LayerId], position: glam::DVec2, tolerance: f64) -> Option<(ManipulatorGroupId, ManipulatorGroupId, Bezier, f64)> {
|
||||
let transform = document.generate_transform_relative_to_viewport(layer_path).ok()?;
|
||||
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 layer_pos = transform.inverse().transform_point2(position);
|
||||
let projection_options = bezier_rs::ProjectionOptions { lut_size: 5, ..Default::default() };
|
||||
|
||||
let mut result = None;
|
||||
let mut closest_distance_squared: f64 = tolerance * tolerance;
|
||||
|
||||
let vector_data = document.layer(layer_path).ok()?.as_vector_data()?;
|
||||
let subpaths = get_subpaths(layer, document)?;
|
||||
|
||||
for subpath in &vector_data.subpaths {
|
||||
for subpath in subpaths {
|
||||
for (manipulator_index, bezier) in subpath.iter().enumerate() {
|
||||
let t = bezier.project(layer_pos, Some(projection_options));
|
||||
let layerspace = bezier.evaluate(TValue::Parametric(t));
|
||||
@@ -712,15 +700,15 @@ impl ShapeState {
|
||||
|
||||
/// Handles the splitting of a curve to insert new points (which can be activated by double clicking on a curve with the Path tool).
|
||||
pub fn split(&self, document: &Document, position: glam::DVec2, tolerance: f64, responses: &mut VecDeque<Message>) {
|
||||
for layer_path in self.selected_layers() {
|
||||
if let Some((start, end, bezier, t)) = self.closest_segment(document, layer_path, position, tolerance) {
|
||||
for &layer in self.selected_layers() {
|
||||
if let Some((start, end, bezier, t)) = self.closest_segment(document, layer, position, tolerance) {
|
||||
let [first, second] = bezier.split(TValue::Parametric(t));
|
||||
|
||||
// Adjust the first manipulator group's out handle
|
||||
let point = ManipulatorPointId::new(start, SelectedType::OutHandle);
|
||||
let position = first.handle_start().unwrap_or(first.start());
|
||||
let out_handle = GraphOperationMessage::Vector {
|
||||
layer: layer_path.clone(),
|
||||
layer: layer.to_path(),
|
||||
modification: VectorDataModification::SetManipulatorPosition { point, position },
|
||||
};
|
||||
responses.add(out_handle);
|
||||
@@ -728,7 +716,7 @@ impl ShapeState {
|
||||
// Insert a new manipulator group between the existing ones
|
||||
let manipulator_group = ManipulatorGroup::new(first.end(), first.handle_end(), second.handle_start());
|
||||
let insert = GraphOperationMessage::Vector {
|
||||
layer: layer_path.clone(),
|
||||
layer: layer.to_path(),
|
||||
modification: VectorDataModification::AddManipulatorGroup { manipulator_group, after_id: start },
|
||||
};
|
||||
responses.add(insert);
|
||||
@@ -737,7 +725,7 @@ impl ShapeState {
|
||||
let point = ManipulatorPointId::new(end, SelectedType::InHandle);
|
||||
let position = second.handle_end().unwrap_or(second.end());
|
||||
let in_handle = GraphOperationMessage::Vector {
|
||||
layer: layer_path.clone(),
|
||||
layer: layer.to_path(),
|
||||
modification: VectorDataModification::SetManipulatorPosition { point, position },
|
||||
};
|
||||
responses.add(in_handle);
|
||||
@@ -749,15 +737,15 @@ impl ShapeState {
|
||||
|
||||
/// Handles the flipping between sharp corner and smooth (which can be activated by double clicking on an anchor with the Path tool).
|
||||
pub fn flip_sharp(&self, document: &Document, position: glam::DVec2, tolerance: f64, responses: &mut VecDeque<Message>) -> bool {
|
||||
let mut process_layer = |layer_path| {
|
||||
let vector_data = document.layer(layer_path).ok()?.as_vector_data()?;
|
||||
let mut process_layer = |layer| {
|
||||
let subpaths = get_subpaths(layer, document)?;
|
||||
|
||||
let transform_to_screenspace = document.generate_transform_relative_to_viewport(layer_path).ok()?;
|
||||
let transform_to_screenspace = document.metadata.transform_from_viewport(layer);
|
||||
let mut result = None;
|
||||
let mut closest_distance_squared = tolerance * tolerance;
|
||||
|
||||
// Find the closest anchor point on the current layer
|
||||
for (subpath_index, subpath) in vector_data.subpaths.iter().enumerate() {
|
||||
for (subpath_index, subpath) in subpaths.iter().enumerate() {
|
||||
for (manipulator_index, manipulator) in subpath.manipulator_groups().iter().enumerate() {
|
||||
let screenspace = transform_to_screenspace.transform_point2(manipulator.anchor);
|
||||
let distance_squared = screenspace.distance_squared(position);
|
||||
@@ -771,7 +759,7 @@ impl ShapeState {
|
||||
let (subpath_index, index, manipulator) = result?;
|
||||
let anchor_position = manipulator.anchor;
|
||||
|
||||
let subpath = &vector_data.subpaths[subpath_index];
|
||||
let subpath = &subpaths[subpath_index];
|
||||
|
||||
// Check by comparing the handle positions to the anchor if this maniuplator group is a point
|
||||
let already_sharp = match (manipulator.in_handle, manipulator.out_handle) {
|
||||
@@ -781,20 +769,20 @@ impl ShapeState {
|
||||
};
|
||||
|
||||
if already_sharp {
|
||||
self.smooth_manipulator_group(subpath, index, responses, layer_path);
|
||||
self.smooth_manipulator_group(subpath, index, responses, &layer);
|
||||
} else {
|
||||
let point = ManipulatorPointId::new(manipulator.id, SelectedType::InHandle);
|
||||
responses.add(GraphOperationMessage::Vector {
|
||||
layer: layer_path.to_vec(),
|
||||
layer: layer.to_path(),
|
||||
modification: VectorDataModification::SetManipulatorPosition { point, position: anchor_position },
|
||||
});
|
||||
let point = ManipulatorPointId::new(manipulator.id, SelectedType::OutHandle);
|
||||
responses.add(GraphOperationMessage::Vector {
|
||||
layer: layer_path.to_vec(),
|
||||
layer: layer.to_path(),
|
||||
modification: VectorDataModification::SetManipulatorPosition { point, position: anchor_position },
|
||||
});
|
||||
responses.add(GraphOperationMessage::Vector {
|
||||
layer: layer_path.to_vec(),
|
||||
layer: layer.to_path(),
|
||||
modification: VectorDataModification::SetManipulatorHandleMirroring {
|
||||
id: manipulator.id,
|
||||
mirror_angle: false,
|
||||
@@ -804,8 +792,8 @@ impl ShapeState {
|
||||
|
||||
Some(true)
|
||||
};
|
||||
for layer_path in self.selected_shape_state.keys() {
|
||||
if let Some(result) = process_layer(layer_path) {
|
||||
for &layer in self.selected_shape_state.keys() {
|
||||
if let Some(result) = process_layer(layer) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -813,17 +801,16 @@ impl ShapeState {
|
||||
}
|
||||
|
||||
pub fn select_all_in_quad(&mut self, document: &Document, quad: [DVec2; 2], clear_selection: bool) {
|
||||
for (layer_path, state) in &mut self.selected_shape_state {
|
||||
for (&layer, state) in &mut self.selected_shape_state {
|
||||
if clear_selection {
|
||||
state.clear_points()
|
||||
}
|
||||
|
||||
let Ok(layer) = document.layer(layer_path) else { continue };
|
||||
let Some(vector_data) = layer.as_vector_data() else { continue };
|
||||
let Some(subpaths) = get_subpaths(layer, document) else { continue };
|
||||
|
||||
let transform = document.multiply_transforms(layer_path).unwrap_or_default();
|
||||
let transform = document.metadata.transform_from_viewport(layer);
|
||||
|
||||
for manipulator_group in vector_data.manipulator_groups() {
|
||||
for manipulator_group in get_manipulator_groups(subpaths) {
|
||||
for selected_type in [SelectedType::Anchor, SelectedType::InHandle, SelectedType::OutHandle] {
|
||||
let Some(position) = selected_type.get_position(manipulator_group) else { continue };
|
||||
let transformed_position = transform.transform_point2(position);
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::consts::{
|
||||
};
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use document_legacy::document_metadata::LayerNodeIdentifier;
|
||||
use document_legacy::layers::layer_info::Layer;
|
||||
use document_legacy::layers::style::{self, Stroke};
|
||||
use document_legacy::{LayerId, Operation};
|
||||
@@ -287,7 +288,12 @@ impl SnapManager {
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.filter(|&(point_id, _)| !ignore_points.contains(&ManipulatorPointInfo { shape_layer_path: path, point_id }))
|
||||
.filter(|&(point_id, _)| {
|
||||
!ignore_points.contains(&ManipulatorPointInfo {
|
||||
layer: LayerNodeIdentifier::from_path(path, document_message_handler.network()),
|
||||
point_id,
|
||||
})
|
||||
})
|
||||
.map(|(_, pos)| transform.transform_point2(pos));
|
||||
self.add_snap_points(document_message_handler, input, snap_points);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user