Clean up Path tool related code and fix bugs in several cases (#3070)
* Fix regressions related to path tool * Code review --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
57853f755b
commit
34b52bcc54
|
|
@ -1777,6 +1777,7 @@ version = "0.29.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"libm",
|
||||
"serde",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -223,9 +223,9 @@ pub fn input_mappings() -> Mapping {
|
|||
entry!(KeyDown(KeyS); action_dispatch=PathToolMessage::GRS { key: KeyS }),
|
||||
entry!(PointerMove; refresh_keys=[KeyC, Space, Control, Shift, Alt], action_dispatch=PathToolMessage::PointerMove { toggle_colinear: KeyC, equidistant: Alt, move_anchor_with_handles: Space, snap_angle: Shift, lock_angle: Control, delete_segment: Alt, break_colinear_molding: Alt, segment_editing_modifier: Control }),
|
||||
entry!(KeyDown(Delete); action_dispatch=PathToolMessage::Delete),
|
||||
entry!(KeyDown(KeyA); modifiers=[Accel], action_dispatch=PathToolMessage::SelectAllAnchors),
|
||||
entry!(KeyDown(KeyA); modifiers=[Accel, Shift], canonical, action_dispatch=PathToolMessage::DeselectAllPoints),
|
||||
entry!(KeyDown(KeyA); modifiers=[Alt], action_dispatch=PathToolMessage::DeselectAllPoints),
|
||||
entry!(KeyDown(KeyA); modifiers=[Accel], action_dispatch=PathToolMessage::SelectAll),
|
||||
entry!(KeyDown(KeyA); modifiers=[Accel, Shift], canonical, action_dispatch=PathToolMessage::DeselectAllSelected),
|
||||
entry!(KeyDown(KeyA); modifiers=[Alt], action_dispatch=PathToolMessage::DeselectAllSelected),
|
||||
entry!(KeyDown(Backspace); action_dispatch=PathToolMessage::Delete),
|
||||
entry!(KeyUp(MouseLeft); action_dispatch=PathToolMessage::DragStop { extend_selection: Shift, shrink_selection: Alt }),
|
||||
entry!(KeyDown(Enter); action_dispatch=PathToolMessage::Enter { extend_selection: Shift, shrink_selection: Alt }),
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
use super::utility_types::{DrawHandles, OverlayContext};
|
||||
use crate::consts::HIDE_HANDLE_DISTANCE;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
|
||||
use crate::messages::tool::common_functionality::shape_editor::{SelectedLayerState, ShapeState};
|
||||
use crate::messages::tool::tool_messages::tool_prelude::{DocumentMessageHandler, PreferencesMessageHandler};
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graphene_std::subpath::{Bezier, BezierHandles};
|
||||
use graphene_std::vector::misc::ManipulatorPointId;
|
||||
use graphene_std::vector::{PointId, SegmentId};
|
||||
use graphene_std::vector::{PointId, SegmentId, Vector};
|
||||
use std::collections::HashMap;
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
pub fn overlay_canvas_element() -> Option<web_sys::HtmlCanvasElement> {
|
||||
|
|
@ -24,33 +26,40 @@ pub fn overlay_canvas_context() -> web_sys::CanvasRenderingContext2d {
|
|||
create_context().expect("Failed to get canvas context")
|
||||
}
|
||||
|
||||
pub fn selected_segments(network_interface: &NodeNetworkInterface, shape_editor: &ShapeState) -> Vec<SegmentId> {
|
||||
let selected_points = shape_editor.selected_points();
|
||||
let selected_anchors = selected_points
|
||||
.filter_map(|point_id| if let ManipulatorPointId::Anchor(p) = point_id { Some(*p) } else { None })
|
||||
pub fn selected_segments(network_interface: &NodeNetworkInterface, shape_editor: &ShapeState) -> HashMap<LayerNodeIdentifier, Vec<SegmentId>> {
|
||||
let mut map = HashMap::new();
|
||||
|
||||
for (layer, state) in &shape_editor.selected_shape_state {
|
||||
let Some(vector) = network_interface.compute_modified_vector(*layer) else { continue };
|
||||
let selected_segments = selected_segments_for_layer(&vector, state);
|
||||
|
||||
map.insert(*layer, selected_segments);
|
||||
}
|
||||
|
||||
map
|
||||
}
|
||||
|
||||
pub fn selected_segments_for_layer(vector: &Vector, state: &SelectedLayerState) -> Vec<SegmentId> {
|
||||
let selected_anchors = state
|
||||
.selected_points()
|
||||
.filter_map(|point| if let ManipulatorPointId::Anchor(p) = point { Some(p) } else { None })
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Collect the segments whose handles are selected
|
||||
let mut selected_segments = shape_editor
|
||||
let mut selected_segments = state
|
||||
.selected_points()
|
||||
.filter_map(|point_id| match point_id {
|
||||
ManipulatorPointId::PrimaryHandle(segment_id) | ManipulatorPointId::EndHandle(segment_id) => Some(*segment_id),
|
||||
ManipulatorPointId::PrimaryHandle(segment_id) | ManipulatorPointId::EndHandle(segment_id) => Some(segment_id),
|
||||
ManipulatorPointId::Anchor(_) => None,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// TODO: Currently if there are two duplicate layers, both of their segments get overlays
|
||||
// Adding segments which are are connected to selected anchors
|
||||
for layer in network_interface.selected_nodes().selected_layers(network_interface.document_metadata()) {
|
||||
let Some(vector) = network_interface.compute_modified_vector(layer) else { continue };
|
||||
|
||||
for (segment_id, _bezier, start, end) in vector.segment_bezier_iter() {
|
||||
if selected_anchors.contains(&start) || selected_anchors.contains(&end) {
|
||||
selected_segments.push(segment_id);
|
||||
}
|
||||
for (segment_id, _bezier, start, end) in vector.segment_bezier_iter() {
|
||||
if selected_anchors.contains(&start) || selected_anchors.contains(&end) {
|
||||
selected_segments.push(segment_id);
|
||||
}
|
||||
}
|
||||
|
||||
selected_segments
|
||||
}
|
||||
|
||||
|
|
@ -124,22 +133,21 @@ pub fn path_overlays(document: &DocumentMessageHandler, draw_handles: DrawHandle
|
|||
overlay_context.outline_vector(&vector, transform);
|
||||
}
|
||||
|
||||
let Some(selected_shape_state) = shape_editor.selected_shape_state.get_mut(&layer) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// Get the selected segments and then add a bold line overlay on them
|
||||
for (segment_id, bezier, _, _) in vector.segment_iter() {
|
||||
let Some(selected_shape_state) = shape_editor.selected_shape_state.get_mut(&layer) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if selected_shape_state.is_segment_selected(segment_id) {
|
||||
overlay_context.outline_select_bezier(bezier, transform);
|
||||
}
|
||||
}
|
||||
|
||||
let selected = shape_editor.selected_shape_state.get(&layer);
|
||||
let is_selected = |point: ManipulatorPointId| selected.is_some_and(|selected| selected.is_point_selected(point));
|
||||
let is_selected = |point: ManipulatorPointId| selected_shape_state.is_point_selected(point);
|
||||
|
||||
if display_handles {
|
||||
let opposite_handles_data: Vec<(PointId, SegmentId)> = shape_editor.selected_points().filter_map(|point_id| vector.adjacent_segment(point_id)).collect();
|
||||
let opposite_handles_data = selected_shape_state.selected_points().filter_map(|point_id| vector.adjacent_segment(&point_id)).collect::<Vec<_>>();
|
||||
|
||||
match draw_handles {
|
||||
DrawHandles::All => {
|
||||
|
|
@ -148,9 +156,11 @@ pub fn path_overlays(document: &DocumentMessageHandler, draw_handles: DrawHandle
|
|||
});
|
||||
}
|
||||
DrawHandles::SelectedAnchors(ref selected_segments) => {
|
||||
let Some(focused_segments) = selected_segments.get(&layer) else { continue };
|
||||
|
||||
vector
|
||||
.segment_bezier_iter()
|
||||
.filter(|(segment_id, ..)| selected_segments.contains(segment_id))
|
||||
.filter(|(segment_id, ..)| focused_segments.contains(segment_id))
|
||||
.for_each(|(segment_id, bezier, _start, _end)| {
|
||||
overlay_bezier_handles(bezier, segment_id, transform, is_selected, overlay_context);
|
||||
});
|
||||
|
|
@ -161,7 +171,9 @@ pub fn path_overlays(document: &DocumentMessageHandler, draw_handles: DrawHandle
|
|||
}
|
||||
}
|
||||
}
|
||||
DrawHandles::FrontierHandles(ref segment_endpoints) => {
|
||||
DrawHandles::FrontierHandles(ref segment_endpoints_by_layer) => {
|
||||
let Some(segment_endpoints) = segment_endpoints_by_layer.get(&layer) else { continue };
|
||||
|
||||
vector
|
||||
.segment_bezier_iter()
|
||||
.filter(|(segment_id, ..)| segment_endpoints.contains_key(segment_id))
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use crate::consts::{
|
|||
COMPASS_ROSE_ARROW_SIZE, COMPASS_ROSE_HOVER_RING_DIAMETER, COMPASS_ROSE_MAIN_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER, DOWEL_PIN_RADIUS, MANIPULATOR_GROUP_MARKER_SIZE,
|
||||
PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER, SEGMENT_SELECTED_THICKNESS,
|
||||
};
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::prelude::Message;
|
||||
use core::borrow::Borrow;
|
||||
use core::f64::consts::{FRAC_PI_2, PI, TAU};
|
||||
|
|
@ -1024,7 +1025,7 @@ pub enum Pivot {
|
|||
|
||||
pub enum DrawHandles {
|
||||
All,
|
||||
SelectedAnchors(Vec<SegmentId>),
|
||||
FrontierHandles(HashMap<SegmentId, Vec<PointId>>),
|
||||
SelectedAnchors(HashMap<LayerNodeIdentifier, Vec<SegmentId>>),
|
||||
FrontierHandles(HashMap<LayerNodeIdentifier, HashMap<SegmentId, Vec<PointId>>>),
|
||||
None,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use crate::consts::{
|
|||
COMPASS_ROSE_ARROW_SIZE, COMPASS_ROSE_HOVER_RING_DIAMETER, COMPASS_ROSE_MAIN_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER, DOWEL_PIN_RADIUS, MANIPULATOR_GROUP_MARKER_SIZE,
|
||||
PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER,
|
||||
};
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::prelude::Message;
|
||||
use core::borrow::Borrow;
|
||||
use core::f64::consts::{FRAC_PI_2, PI, TAU};
|
||||
|
|
@ -400,8 +401,8 @@ pub enum Pivot {
|
|||
|
||||
pub enum DrawHandles {
|
||||
All,
|
||||
SelectedAnchors(Vec<SegmentId>),
|
||||
FrontierHandles(HashMap<SegmentId, Vec<PointId>>),
|
||||
SelectedAnchors(HashMap<LayerNodeIdentifier, Vec<SegmentId>>),
|
||||
FrontierHandles(HashMap<LayerNodeIdentifier, HashMap<SegmentId, Vec<PointId>>>),
|
||||
None,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use super::graph_modification_utils::merge_layers;
|
|||
use super::snapping::{SnapCache, SnapCandidatePoint, SnapData, SnapManager, SnappedPoint};
|
||||
use super::utility_functions::{adjust_handle_colinearity, calculate_segment_angle, restore_g1_continuity, restore_previous_handle_position};
|
||||
use crate::consts::HANDLE_LENGTH_FACTOR;
|
||||
use crate::messages::portfolio::document::overlays::utility_functions::selected_segments;
|
||||
use crate::messages::portfolio::document::overlays::utility_functions::selected_segments_for_layer;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
|
||||
use crate::messages::portfolio::document::utility_types::misc::{PathSnapSource, SnapSource};
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
|
||||
|
|
@ -421,7 +421,7 @@ impl ShapeState {
|
|||
(point.as_handle().is_some() && self.ignore_handles) || (point.as_anchor().is_some() && self.ignore_anchors)
|
||||
}
|
||||
|
||||
pub fn close_selected_path(&self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
||||
pub fn close_selected_path(&self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>, vector_meshes: bool) {
|
||||
// First collect all selected anchor points across all layers
|
||||
let all_selected_points: Vec<(LayerNodeIdentifier, PointId)> = self
|
||||
.selected_shape_state
|
||||
|
|
@ -449,7 +449,8 @@ impl ShapeState {
|
|||
let Some(vector1) = document.network_interface.compute_modified_vector(layer1) else { return };
|
||||
let Some(vector2) = document.network_interface.compute_modified_vector(layer2) else { return };
|
||||
|
||||
if vector1.all_connected(start_point).count() != 1 || vector2.all_connected(end_point).count() != 1 {
|
||||
// If vector meshes is not selected then only for endpoints, otherwise normally applicable
|
||||
if !vector_meshes && (vector1.all_connected(start_point).count() != 1 || vector2.all_connected(end_point).count() != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -559,7 +560,7 @@ impl ShapeState {
|
|||
select_threshold: f64,
|
||||
extend_selection: bool,
|
||||
path_overlay_mode: PathOverlayMode,
|
||||
frontier_handles_info: &Option<HashMap<SegmentId, Vec<PointId>>>,
|
||||
frontier_handles_info: Option<&HashMap<LayerNodeIdentifier, HashMap<SegmentId, Vec<PointId>>>>,
|
||||
) -> Option<Option<SelectedPointsInfo>> {
|
||||
if self.selected_shape_state.is_empty() {
|
||||
return None;
|
||||
|
|
@ -608,7 +609,7 @@ impl ShapeState {
|
|||
mouse_position: DVec2,
|
||||
select_threshold: f64,
|
||||
path_overlay_mode: PathOverlayMode,
|
||||
frontier_handles_info: &Option<HashMap<SegmentId, Vec<PointId>>>,
|
||||
frontier_handles_info: Option<&HashMap<LayerNodeIdentifier, HashMap<SegmentId, Vec<PointId>>>>,
|
||||
point_editing_mode: bool,
|
||||
) -> Option<(bool, Option<SelectedPointsInfo>)> {
|
||||
if self.selected_shape_state.is_empty() {
|
||||
|
|
@ -622,15 +623,22 @@ impl ShapeState {
|
|||
}
|
||||
let vector = network_interface.compute_modified_vector(layer)?;
|
||||
let point_position = manipulator_point_id.get_position(&vector)?;
|
||||
|
||||
let selected_shape_state = self.selected_shape_state.get(&layer)?;
|
||||
// Check if point is visible under current overlay mode or not
|
||||
let selected_segments = selected_segments(network_interface, self);
|
||||
let selected_segments_for_layer = selected_segments_for_layer(&vector, selected_shape_state);
|
||||
let selected_points = self.selected_points().cloned().collect::<HashSet<_>>();
|
||||
if !is_visible_point(manipulator_point_id, &vector, path_overlay_mode, frontier_handles_info, selected_segments, &selected_points) {
|
||||
let frontier_handles_for_layer = frontier_handles_info.and_then(|frontier_handles| frontier_handles.get(&layer));
|
||||
if !is_visible_point(
|
||||
manipulator_point_id,
|
||||
&vector,
|
||||
path_overlay_mode,
|
||||
frontier_handles_for_layer,
|
||||
&selected_segments_for_layer,
|
||||
&selected_points,
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let selected_shape_state = self.selected_shape_state.get(&layer)?;
|
||||
let already_selected = selected_shape_state.is_point_selected(manipulator_point_id);
|
||||
|
||||
// Offset to snap the selected point to the cursor
|
||||
|
|
@ -733,6 +741,13 @@ impl ShapeState {
|
|||
}
|
||||
}
|
||||
|
||||
/// Selects all segments for the selected layers.
|
||||
pub fn select_all_segments_in_selected_layers(&mut self, document: &DocumentMessageHandler) {
|
||||
for (&layer, state) in self.selected_shape_state.iter_mut() {
|
||||
Self::select_all_segments_in_layer_with_state(document, layer, state);
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal helper function that selects all anchors, and deselects all handles, for a layer given its [`LayerNodeIdentifier`] and [`SelectedLayerState`].
|
||||
fn select_all_anchors_in_layer_with_state(document: &DocumentMessageHandler, layer: LayerNodeIdentifier, state: &mut SelectedLayerState) {
|
||||
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { return };
|
||||
|
|
@ -744,6 +759,15 @@ impl ShapeState {
|
|||
}
|
||||
}
|
||||
|
||||
/// Internal helper function that selects all segments, for a layer given its [`LayerNodeIdentifier`] and [`SelectedLayerState`].
|
||||
fn select_all_segments_in_layer_with_state(document: &DocumentMessageHandler, layer: LayerNodeIdentifier, state: &mut SelectedLayerState) {
|
||||
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { return };
|
||||
|
||||
for &segment in vector.segment_domain.ids() {
|
||||
state.select_segment(segment);
|
||||
}
|
||||
}
|
||||
|
||||
/// Deselects all points (anchors and handles) across every selected layer.
|
||||
pub fn deselect_all_points(&mut self) {
|
||||
for state in self.selected_shape_state.values_mut() {
|
||||
|
|
@ -1606,7 +1630,7 @@ impl ShapeState {
|
|||
mouse_position: DVec2,
|
||||
select_threshold: f64,
|
||||
path_overlay_mode: PathOverlayMode,
|
||||
frontier_handles_info: &Option<HashMap<SegmentId, Vec<PointId>>>,
|
||||
frontier_handles_info: Option<&HashMap<LayerNodeIdentifier, HashMap<SegmentId, Vec<PointId>>>>,
|
||||
) -> Option<(LayerNodeIdentifier, ManipulatorPointId)> {
|
||||
if self.selected_shape_state.is_empty() {
|
||||
return None;
|
||||
|
|
@ -1621,10 +1645,11 @@ impl ShapeState {
|
|||
if distance_squared < select_threshold_squared {
|
||||
// Check if point is visible in current PathOverlayMode
|
||||
let vector = network_interface.compute_modified_vector(layer)?;
|
||||
let selected_segments = selected_segments(network_interface, self);
|
||||
let Some(state) = self.selected_shape_state.get(&layer) else { continue };
|
||||
let selected_segments = selected_segments_for_layer(&vector, state);
|
||||
let selected_points = self.selected_points().cloned().collect::<HashSet<_>>();
|
||||
|
||||
if !is_visible_point(manipulator_point_id, &vector, path_overlay_mode, frontier_handles_info, selected_segments, &selected_points) {
|
||||
let frontier_handles_for_layer = frontier_handles_info.and_then(|handles_info| handles_info.get(&layer));
|
||||
if !is_visible_point(manipulator_point_id, &vector, path_overlay_mode, frontier_handles_for_layer, &selected_segments, &selected_points) {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
@ -1868,53 +1893,64 @@ impl ShapeState {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn select_points_by_manipulator_id(&mut self, points: &Vec<ManipulatorPointId>) {
|
||||
let layers_to_modify: Vec<_> = self.selected_shape_state.keys().cloned().collect();
|
||||
pub fn select_anchor_and_connected_handles(&mut self, network_interface: &NodeNetworkInterface) {
|
||||
let mut non_empty_layers = self.selected_shape_state.iter_mut().filter(|(_, state)| !state.is_empty());
|
||||
|
||||
for layer in layers_to_modify {
|
||||
if let Some(state) = self.selected_shape_state.get_mut(&layer) {
|
||||
for point in points {
|
||||
state.select_point(*point);
|
||||
}
|
||||
let Some((layer, state)) = non_empty_layers.next() else { return };
|
||||
if non_empty_layers.next().is_some() {
|
||||
return;
|
||||
}
|
||||
let Some(vector) = network_interface.compute_modified_vector(*layer) else { return };
|
||||
|
||||
// Get the current point and its connected handles
|
||||
let selected_points = state.selected_points.clone();
|
||||
if let Some(point) = selected_points.iter().next() {
|
||||
if let Some(anchor) = point.get_anchor(&vector) {
|
||||
state.select_point(ManipulatorPointId::Anchor(anchor));
|
||||
}
|
||||
if let Some(handles) = point.get_handle_pair(&vector) {
|
||||
state.select_point(handles[0].to_manipulator_point());
|
||||
state.select_point(handles[1].to_manipulator_point());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a nearby clicked anchor point's handles between sharp (zero-length handles) and smooth (pulled-apart handle(s)).
|
||||
/// If both handles aren't zero-length, they are set that. If both are zero-length, they are stretched apart by a reasonable amount.
|
||||
/// This can can be activated by double clicking on an anchor with the Path tool.
|
||||
pub fn flip_smooth_sharp(&self, network_interface: &NodeNetworkInterface, target: glam::DVec2, tolerance: f64, responses: &mut VecDeque<Message>) -> bool {
|
||||
let mut process_layer = |layer| {
|
||||
let vector = network_interface.compute_modified_vector(layer)?;
|
||||
let transform_to_screenspace = network_interface.document_metadata().transform_to_viewport_if_feeds(layer, network_interface);
|
||||
|
||||
let mut result = None;
|
||||
let mut closest_distance_squared = tolerance * tolerance;
|
||||
|
||||
// Find the closest anchor point on the current layer
|
||||
for (&id, &anchor) in vector.point_domain.ids().iter().zip(vector.point_domain.positions()) {
|
||||
let screenspace = transform_to_screenspace.transform_point2(anchor);
|
||||
let distance_squared = screenspace.distance_squared(target);
|
||||
|
||||
if distance_squared < closest_distance_squared {
|
||||
closest_distance_squared = distance_squared;
|
||||
result = Some((id, anchor));
|
||||
}
|
||||
pub fn select_points_by_layer_and_id(&mut self, points: &HashMap<LayerNodeIdentifier, Vec<ManipulatorPointId>>) {
|
||||
for (layer, points) in points {
|
||||
if let Some(state) = self.selected_shape_state.get_mut(layer) {
|
||||
points.iter().for_each(|point| state.select_point(*point));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (id, anchor) = result?;
|
||||
let handles = vector.all_connected(id);
|
||||
let positions = handles
|
||||
.filter_map(|handle| handle.to_manipulator_point().get_position(&vector))
|
||||
.filter(|&handle| anchor.abs_diff_eq(handle, 1e-5))
|
||||
.count();
|
||||
pub fn select_point_by_layer_and_id(&mut self, point: ManipulatorPointId, layer: LayerNodeIdentifier) {
|
||||
if let Some(state) = self.selected_shape_state.get_mut(&layer) {
|
||||
state.select_point(point);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the anchor is connected to linear segments.
|
||||
let one_or_more_segment_linear = vector.connected_linear_segments(id) != 0;
|
||||
/// Converts all selected anchor points' handles between sharp (zero-length handles) and smooth (pulled-apart colinear handle(s)).
|
||||
/// If both handles aren't zero-length, they are set to that. If both are zero-length, they are stretched apart by a reasonable amount.
|
||||
/// This can can be activated by double clicking on an anchor with the Path tool.
|
||||
pub fn flip_smooth_sharp(&self, network_interface: &NodeNetworkInterface, responses: &mut VecDeque<Message>) {
|
||||
let mut process_layer = |layer: LayerNodeIdentifier, selected_points: &HashSet<ManipulatorPointId>| {
|
||||
let vector = network_interface.compute_modified_vector(layer)?;
|
||||
|
||||
// Check by comparing the handle positions to the anchor if this manipulator group is a point
|
||||
for point in self.selected_points() {
|
||||
for point in selected_points {
|
||||
let Some(point_id) = point.as_anchor() else { continue };
|
||||
let anchor = point.get_position(&vector)?;
|
||||
let handles = vector.all_connected(point_id);
|
||||
|
||||
// TODO: Check if this method of finding non-colinear is really required
|
||||
let positions = handles
|
||||
.filter_map(|handle| handle.to_manipulator_point().get_position(&vector))
|
||||
.filter(|&handle| anchor.abs_diff_eq(handle, 1e-5))
|
||||
.count();
|
||||
|
||||
// Check if the anchor is connected to linear segments.
|
||||
let one_or_more_segment_linear = vector.connected_linear_segments(point_id) != 0;
|
||||
|
||||
if positions != 0 || one_or_more_segment_linear {
|
||||
self.convert_manipulator_handles_to_colinear(&vector, point_id, responses, layer);
|
||||
} else {
|
||||
|
|
@ -1958,13 +1994,10 @@ impl ShapeState {
|
|||
Some(true)
|
||||
};
|
||||
|
||||
for &layer in self.selected_shape_state.keys() {
|
||||
if let Some(result) = process_layer(layer) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
self.selected_shape_state.iter().for_each(|(layer, state)| {
|
||||
let selected_points = &state.selected_points;
|
||||
process_layer(*layer, selected_points);
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
|
@ -1974,7 +2007,7 @@ impl ShapeState {
|
|||
selection_shape: SelectionShape,
|
||||
selection_change: SelectionChange,
|
||||
path_overlay_mode: PathOverlayMode,
|
||||
frontier_handles_info: &Option<HashMap<SegmentId, Vec<PointId>>>,
|
||||
frontier_handles_info: Option<&HashMap<LayerNodeIdentifier, HashMap<SegmentId, Vec<PointId>>>>,
|
||||
select_segments: bool,
|
||||
select_points: bool,
|
||||
// Here, "selection mode" represents touched or enclosed, not to be confused with editing modes
|
||||
|
|
@ -2046,14 +2079,13 @@ impl ShapeState {
|
|||
network_interface: &NodeNetworkInterface,
|
||||
selection_shape: SelectionShape,
|
||||
path_overlay_mode: PathOverlayMode,
|
||||
frontier_handles_info: &Option<HashMap<SegmentId, Vec<PointId>>>,
|
||||
frontier_handles_info: Option<&HashMap<LayerNodeIdentifier, HashMap<SegmentId, Vec<PointId>>>>,
|
||||
select_segments: bool,
|
||||
select_points: bool,
|
||||
// Represents if the box/lasso selection touches or encloses the targets (not to be confused with editing modes).
|
||||
selection_mode: SelectionMode,
|
||||
) -> (HashMap<LayerNodeIdentifier, HashSet<ManipulatorPointId>>, HashMap<LayerNodeIdentifier, HashSet<SegmentId>>) {
|
||||
let selected_points = self.selected_points().cloned().collect::<HashSet<_>>();
|
||||
let selected_segments = selected_segments(network_interface, self);
|
||||
|
||||
let mut points_inside: HashMap<LayerNodeIdentifier, HashSet<ManipulatorPointId>> = HashMap::new();
|
||||
let mut segments_inside: HashMap<LayerNodeIdentifier, HashSet<SegmentId>> = HashMap::new();
|
||||
|
|
@ -2137,7 +2169,10 @@ impl ShapeState {
|
|||
};
|
||||
|
||||
if select && select_points {
|
||||
let is_visible_handle = is_visible_point(id, &vector, path_overlay_mode, frontier_handles_info, selected_segments.clone(), &selected_points);
|
||||
let frontier_handles_for_layer = frontier_handles_info.and_then(|frontier_handles| frontier_handles.get(&layer));
|
||||
let state = self.selected_shape_state.get(&layer).expect("Cannot find state for layer");
|
||||
let selected_segments_for_layer = selected_segments_for_layer(&vector, state);
|
||||
let is_visible_handle = is_visible_point(id, &vector, path_overlay_mode, frontier_handles_for_layer, &selected_segments_for_layer, &selected_points);
|
||||
|
||||
if is_visible_handle {
|
||||
points_inside.entry(layer).or_default().insert(id);
|
||||
|
|
|
|||
|
|
@ -171,8 +171,8 @@ pub fn is_visible_point(
|
|||
manipulator_point_id: ManipulatorPointId,
|
||||
vector: &Vector,
|
||||
path_overlay_mode: PathOverlayMode,
|
||||
frontier_handles_info: &Option<HashMap<SegmentId, Vec<PointId>>>,
|
||||
selected_segments: Vec<SegmentId>,
|
||||
frontier_handles_for_layer: Option<&HashMap<SegmentId, Vec<PointId>>>,
|
||||
selected_segments: &[SegmentId],
|
||||
selected_points: &HashSet<ManipulatorPointId>,
|
||||
) -> bool {
|
||||
match manipulator_point_id {
|
||||
|
|
@ -197,7 +197,7 @@ pub fn is_visible_point(
|
|||
warn!("No anchor for selected handle");
|
||||
return false;
|
||||
};
|
||||
let Some(frontier_handles) = frontier_handles_info else {
|
||||
let Some(frontier_handles) = frontier_handles_for_layer else {
|
||||
warn!("No frontier handles info provided");
|
||||
return false;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ impl ArtboardToolData {
|
|||
let Some(movement) = &bounds.selected_edges else {
|
||||
return;
|
||||
};
|
||||
if self.selected_artboard.unwrap() == LayerNodeIdentifier::ROOT_PARENT {
|
||||
if self.selected_artboard == Some(LayerNodeIdentifier::ROOT_PARENT) {
|
||||
log::error!("Selected artboard cannot be ROOT_PARENT");
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ pub enum PathToolMessage {
|
|||
|
||||
// Tool-specific messages
|
||||
BreakPath,
|
||||
DeselectAllPoints,
|
||||
DeselectAllSelected,
|
||||
Delete,
|
||||
DeleteAndBreakPath,
|
||||
DragStop {
|
||||
|
|
@ -113,7 +113,7 @@ pub enum PathToolMessage {
|
|||
segment_editing_modifier: Key,
|
||||
},
|
||||
RightClick,
|
||||
SelectAllAnchors,
|
||||
SelectAll,
|
||||
SelectedPointUpdated,
|
||||
SelectedPointXChanged {
|
||||
new_x: f64,
|
||||
|
|
@ -401,12 +401,6 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Path
|
|||
self.send_layout(responses, LayoutTarget::ToolOptions);
|
||||
}
|
||||
},
|
||||
ToolMessage::Path(PathToolMessage::ClosePath) => {
|
||||
responses.add(DocumentMessage::AddTransaction);
|
||||
context.shape_editor.close_selected_path(context.document, responses);
|
||||
responses.add(DocumentMessage::EndTransaction);
|
||||
responses.add(OverlaysMessage::Draw);
|
||||
}
|
||||
ToolMessage::Path(PathToolMessage::SwapSelectedHandles) => {
|
||||
if context.shape_editor.handle_with_pair_selected(&context.document.network_interface) {
|
||||
context.shape_editor.alternate_selected_handles(&context.document.network_interface);
|
||||
|
|
@ -434,8 +428,8 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Path
|
|||
Delete,
|
||||
NudgeSelectedPoints,
|
||||
Enter,
|
||||
SelectAllAnchors,
|
||||
DeselectAllPoints,
|
||||
SelectAll,
|
||||
DeselectAllSelected,
|
||||
BreakPath,
|
||||
DeleteAndBreakPath,
|
||||
ClosePath,
|
||||
|
|
@ -563,11 +557,11 @@ struct PathToolData {
|
|||
segment_editing_modifier: bool,
|
||||
multiple_toggle_pressed: bool,
|
||||
auto_panning: AutoPanning,
|
||||
saved_points_before_anchor_select_toggle: Vec<ManipulatorPointId>,
|
||||
saved_points_before_anchor_select_toggle: HashMap<LayerNodeIdentifier, Vec<ManipulatorPointId>>,
|
||||
select_anchor_toggled: bool,
|
||||
saved_selection_before_handle_drag: HashMap<LayerNodeIdentifier, (HashSet<ManipulatorPointId>, HashSet<SegmentId>)>,
|
||||
handle_drag_toggle: bool,
|
||||
saved_points_before_anchor_convert_smooth_sharp: HashSet<ManipulatorPointId>,
|
||||
saved_points_before_anchor_convert_smooth_sharp: HashMap<LayerNodeIdentifier, Vec<ManipulatorPointId>>,
|
||||
last_click_time: u64,
|
||||
dragging_state: DraggingState,
|
||||
angle: f64,
|
||||
|
|
@ -584,7 +578,7 @@ struct PathToolData {
|
|||
molding_info: Option<(DVec2, DVec2)>,
|
||||
molding_segment: bool,
|
||||
temporary_adjacent_handles_while_molding: Option<[Option<HandleId>; 2]>,
|
||||
frontier_handles_info: Option<HashMap<SegmentId, Vec<PointId>>>,
|
||||
frontier_handles_info: Option<HashMap<LayerNodeIdentifier, HashMap<SegmentId, Vec<PointId>>>>,
|
||||
adjacent_anchor_offset: Option<DVec2>,
|
||||
sliding_point_info: Option<SlidingPointInfo>,
|
||||
started_drawing_from_inside: bool,
|
||||
|
|
@ -599,7 +593,7 @@ struct PathToolData {
|
|||
}
|
||||
|
||||
impl PathToolData {
|
||||
fn save_points_before_anchor_toggle(&mut self, points: Vec<ManipulatorPointId>) -> PathToolFsmState {
|
||||
fn save_points_before_anchor_toggle(&mut self, points: HashMap<LayerNodeIdentifier, Vec<ManipulatorPointId>>) -> PathToolFsmState {
|
||||
self.saved_points_before_anchor_select_toggle = points;
|
||||
PathToolFsmState::Dragging(self.dragging_state)
|
||||
}
|
||||
|
|
@ -744,7 +738,7 @@ impl PathToolData {
|
|||
input.mouse.position,
|
||||
SELECTION_THRESHOLD,
|
||||
path_overlay_mode,
|
||||
&self.frontier_handles_info,
|
||||
self.frontier_handles_info.as_ref(),
|
||||
point_editing_mode,
|
||||
) {
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
|
|
@ -762,7 +756,7 @@ impl PathToolData {
|
|||
SELECTION_THRESHOLD,
|
||||
extend_selection,
|
||||
path_overlay_mode,
|
||||
&self.frontier_handles_info,
|
||||
self.frontier_handles_info.as_ref(),
|
||||
) {
|
||||
selection_info = updated_selection_info;
|
||||
}
|
||||
|
|
@ -802,15 +796,15 @@ impl PathToolData {
|
|||
|
||||
let manipulator_point_id = handles[0].to_manipulator_point();
|
||||
shape_editor.deselect_all_points();
|
||||
shape_editor.select_points_by_manipulator_id(&vec![manipulator_point_id]);
|
||||
shape_editor.select_point_by_layer_and_id(manipulator_point_id, layer);
|
||||
responses.add(PathToolMessage::SelectedPointUpdated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((Some(point), Some(vector))) = shape_editor
|
||||
if let Some((Some(point), Some(vector), layer)) = shape_editor
|
||||
.find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD)
|
||||
.map(|(layer, point)| (point.as_anchor(), document.network_interface.compute_modified_vector(layer)))
|
||||
.map(|(layer, point)| (point.as_anchor(), document.network_interface.compute_modified_vector(layer), layer))
|
||||
{
|
||||
let handles = vector
|
||||
.all_connected(point)
|
||||
|
|
@ -821,7 +815,7 @@ impl PathToolData {
|
|||
|
||||
if drag_zero_handle && (handles.len() == 1 && !endpoint) {
|
||||
shape_editor.deselect_all_points();
|
||||
shape_editor.select_points_by_manipulator_id(&handles);
|
||||
shape_editor.select_points_by_layer_and_id(&HashMap::from([(layer, handles)]));
|
||||
shape_editor.convert_selected_manipulators_to_colinear_handles(responses, document);
|
||||
}
|
||||
}
|
||||
|
|
@ -1201,7 +1195,7 @@ impl PathToolData {
|
|||
// Check if there is no point nearby
|
||||
// If the point mode is deactivated then don't override closest segment even if there is a closer point
|
||||
if shape_editor
|
||||
.find_nearest_visible_point_indices(&document.network_interface, position, SELECTION_THRESHOLD, path_overlay_mode, &self.frontier_handles_info)
|
||||
.find_nearest_visible_point_indices(&document.network_interface, position, SELECTION_THRESHOLD, path_overlay_mode, self.frontier_handles_info.as_ref())
|
||||
.is_some()
|
||||
&& point_editing_mode
|
||||
{
|
||||
|
|
@ -1211,9 +1205,19 @@ impl PathToolData {
|
|||
else if let Some(closest_segment) = &mut self.segment {
|
||||
closest_segment.update_closest_point(document.metadata(), &document.network_interface, position);
|
||||
|
||||
let layer = closest_segment.layer();
|
||||
let segment_id = closest_segment.segment();
|
||||
|
||||
if closest_segment.too_far(position, SEGMENT_INSERTION_DISTANCE) {
|
||||
self.segment = None;
|
||||
}
|
||||
|
||||
// Check if that segment exists or it has been removed
|
||||
if let Some(vector_data) = document.network_interface.compute_modified_vector(layer)
|
||||
&& !(vector_data.segment_domain.ids().iter().any(|segment| *segment == segment_id))
|
||||
{
|
||||
self.segment = None;
|
||||
}
|
||||
}
|
||||
// If not, check that if there is some closest segment or not
|
||||
else if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, position, SEGMENT_INSERTION_DISTANCE) {
|
||||
|
|
@ -1468,7 +1472,8 @@ impl PathToolData {
|
|||
|
||||
// Now change the selection to this handle
|
||||
shape_editor.deselect_all_points();
|
||||
shape_editor.select_points_by_manipulator_id(&vec![handle]);
|
||||
shape_editor.select_point_by_layer_and_id(handle, layer);
|
||||
|
||||
responses.add(PathToolMessage::SelectionChanged);
|
||||
}
|
||||
}
|
||||
|
|
@ -1691,25 +1696,31 @@ impl Fsm for PathToolFsmState {
|
|||
}
|
||||
PathOverlayMode::FrontierHandles => {
|
||||
let selected_segments = selected_segments(&document.network_interface, shape_editor);
|
||||
let selected_points = shape_editor.selected_points();
|
||||
let selected_anchors = selected_points
|
||||
.filter_map(|point_id| if let ManipulatorPointId::Anchor(p) = point_id { Some(*p) } else { None })
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Match the behavior of `PathOverlayMode::SelectedPointHandles` when only one point is selected
|
||||
if shape_editor.selected_points().count() == 1 {
|
||||
path_overlays(document, DrawHandles::SelectedAnchors(selected_segments), shape_editor, &mut overlay_context);
|
||||
} else {
|
||||
let mut segment_endpoints: HashMap<SegmentId, Vec<PointId>> = HashMap::new();
|
||||
let mut segment_endpoints_by_layer = HashMap::new();
|
||||
|
||||
for layer in document.network_interface.selected_nodes().selected_layers(document.metadata()) {
|
||||
let mut segment_endpoints: HashMap<SegmentId, Vec<PointId>> = HashMap::new();
|
||||
|
||||
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
|
||||
let Some(state) = shape_editor.selected_shape_state.get_mut(&layer) else { continue };
|
||||
|
||||
let selected_points = state.selected_points();
|
||||
let selected_anchors = selected_points
|
||||
.filter_map(|point_id| if let ManipulatorPointId::Anchor(p) = point_id { Some(p) } else { None })
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let Some(focused_segments) = selected_segments.get(&layer) else { continue };
|
||||
|
||||
// The points which are part of only one segment will be rendered
|
||||
let mut selected_segments_by_point: HashMap<PointId, Vec<SegmentId>> = HashMap::new();
|
||||
|
||||
for (segment_id, _bezier, start, end) in vector.segment_bezier_iter() {
|
||||
if selected_segments.contains(&segment_id) {
|
||||
if focused_segments.contains(&segment_id) {
|
||||
selected_segments_by_point.entry(start).or_default().push(segment_id);
|
||||
selected_segments_by_point.entry(end).or_default().push(segment_id);
|
||||
}
|
||||
|
|
@ -1725,13 +1736,15 @@ impl Fsm for PathToolFsmState {
|
|||
segment_endpoints.entry(attached_segments[1]).or_default().push(point);
|
||||
}
|
||||
}
|
||||
|
||||
segment_endpoints_by_layer.insert(layer, segment_endpoints);
|
||||
}
|
||||
|
||||
// Caching segment endpoints for use in point selection logic
|
||||
tool_data.frontier_handles_info = Some(segment_endpoints.clone());
|
||||
tool_data.frontier_handles_info = Some(segment_endpoints_by_layer.clone());
|
||||
|
||||
// Now frontier anchors can be sent for rendering overlays
|
||||
path_overlays(document, DrawHandles::FrontierHandles(segment_endpoints), shape_editor, &mut overlay_context);
|
||||
path_overlays(document, DrawHandles::FrontierHandles(segment_endpoints_by_layer), shape_editor, &mut overlay_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1757,7 +1770,7 @@ impl Fsm for PathToolFsmState {
|
|||
input.mouse.position,
|
||||
SELECTION_THRESHOLD,
|
||||
tool_options.path_overlay_mode,
|
||||
&tool_data.frontier_handles_info,
|
||||
tool_data.frontier_handles_info.as_ref(),
|
||||
);
|
||||
|
||||
let Some((layer, manipulator_point_id)) = nearest_visible_point_indices else { return };
|
||||
|
|
@ -1869,7 +1882,7 @@ impl Fsm for PathToolFsmState {
|
|||
&document.network_interface,
|
||||
SelectionShape::Box(bbox),
|
||||
tool_options.path_overlay_mode,
|
||||
&tool_data.frontier_handles_info,
|
||||
tool_data.frontier_handles_info.as_ref(),
|
||||
select_segments,
|
||||
select_points,
|
||||
selection_mode,
|
||||
|
|
@ -1879,7 +1892,7 @@ impl Fsm for PathToolFsmState {
|
|||
&document.network_interface,
|
||||
SelectionShape::Lasso(&tool_data.lasso_polygon),
|
||||
tool_options.path_overlay_mode,
|
||||
&tool_data.frontier_handles_info,
|
||||
tool_data.frontier_handles_info.as_ref(),
|
||||
select_segments,
|
||||
select_points,
|
||||
selection_mode,
|
||||
|
|
@ -2096,13 +2109,19 @@ impl Fsm for PathToolFsmState {
|
|||
if initial_press {
|
||||
responses.add(PathToolMessage::SelectedPointUpdated);
|
||||
tool_data.select_anchor_toggled = true;
|
||||
tool_data.save_points_before_anchor_toggle(shape_editor.selected_points().cloned().collect());
|
||||
shape_editor.select_handles_and_anchor_connected_to_current_handle(&document.network_interface);
|
||||
|
||||
let mut points_to_save = HashMap::new();
|
||||
for (layer, state) in &shape_editor.selected_shape_state {
|
||||
points_to_save.insert(*layer, state.selected_points().collect::<Vec<_>>());
|
||||
}
|
||||
tool_data.save_points_before_anchor_toggle(points_to_save);
|
||||
|
||||
shape_editor.select_anchor_and_connected_handles(&document.network_interface);
|
||||
} else if released_from_toggle {
|
||||
responses.add(PathToolMessage::SelectedPointUpdated);
|
||||
tool_data.select_anchor_toggled = false;
|
||||
shape_editor.deselect_all_points();
|
||||
shape_editor.select_points_by_manipulator_id(&tool_data.saved_points_before_anchor_select_toggle);
|
||||
shape_editor.select_points_by_layer_and_id(&tool_data.saved_points_before_anchor_select_toggle);
|
||||
tool_data.remove_saved_points();
|
||||
}
|
||||
|
||||
|
|
@ -2288,7 +2307,7 @@ impl Fsm for PathToolFsmState {
|
|||
SelectionShape::Box(bbox),
|
||||
selection_change,
|
||||
tool_options.path_overlay_mode,
|
||||
&tool_data.frontier_handles_info,
|
||||
tool_data.frontier_handles_info.as_ref(),
|
||||
tool_options.path_editing_mode.segment_editing_mode,
|
||||
tool_options.path_editing_mode.point_editing_mode,
|
||||
selection_mode,
|
||||
|
|
@ -2299,7 +2318,7 @@ impl Fsm for PathToolFsmState {
|
|||
SelectionShape::Lasso(&tool_data.lasso_polygon),
|
||||
selection_change,
|
||||
tool_options.path_overlay_mode,
|
||||
&tool_data.frontier_handles_info,
|
||||
tool_data.frontier_handles_info.as_ref(),
|
||||
tool_options.path_editing_mode.segment_editing_mode,
|
||||
tool_options.path_editing_mode.point_editing_mode,
|
||||
selection_mode,
|
||||
|
|
@ -2385,7 +2404,7 @@ impl Fsm for PathToolFsmState {
|
|||
SelectionShape::Box(bbox),
|
||||
select_kind,
|
||||
tool_options.path_overlay_mode,
|
||||
&tool_data.frontier_handles_info,
|
||||
tool_data.frontier_handles_info.as_ref(),
|
||||
tool_options.path_editing_mode.segment_editing_mode,
|
||||
tool_options.path_editing_mode.point_editing_mode,
|
||||
selection_mode,
|
||||
|
|
@ -2396,7 +2415,7 @@ impl Fsm for PathToolFsmState {
|
|||
SelectionShape::Lasso(&tool_data.lasso_polygon),
|
||||
select_kind,
|
||||
tool_options.path_overlay_mode,
|
||||
&tool_data.frontier_handles_info,
|
||||
tool_data.frontier_handles_info.as_ref(),
|
||||
tool_options.path_editing_mode.segment_editing_mode,
|
||||
tool_options.path_editing_mode.point_editing_mode,
|
||||
selection_mode,
|
||||
|
|
@ -2420,7 +2439,7 @@ impl Fsm for PathToolFsmState {
|
|||
input.mouse.position,
|
||||
SELECTION_THRESHOLD,
|
||||
tool_options.path_overlay_mode,
|
||||
&tool_data.frontier_handles_info,
|
||||
tool_data.frontier_handles_info.as_ref(),
|
||||
);
|
||||
|
||||
let nearest_segment = tool_data.segment.clone();
|
||||
|
|
@ -2473,7 +2492,11 @@ impl Fsm for PathToolFsmState {
|
|||
}
|
||||
if !drag_occurred && !extend_selection && clicked_selected {
|
||||
if tool_data.saved_points_before_anchor_convert_smooth_sharp.is_empty() {
|
||||
tool_data.saved_points_before_anchor_convert_smooth_sharp = shape_editor.selected_points().copied().collect::<HashSet<_>>();
|
||||
let mut saved_points = HashMap::new();
|
||||
for (layer, state) in &shape_editor.selected_shape_state {
|
||||
saved_points.insert(*layer, state.selected_points().collect::<Vec<_>>());
|
||||
}
|
||||
tool_data.saved_points_before_anchor_convert_smooth_sharp = saved_points;
|
||||
}
|
||||
|
||||
shape_editor.deselect_all_points();
|
||||
|
|
@ -2567,7 +2590,7 @@ impl Fsm for PathToolFsmState {
|
|||
|
||||
if tool_data.select_anchor_toggled {
|
||||
shape_editor.deselect_all_points();
|
||||
shape_editor.select_points_by_manipulator_id(&tool_data.saved_points_before_anchor_select_toggle);
|
||||
shape_editor.select_points_by_layer_and_id(&tool_data.saved_points_before_anchor_select_toggle);
|
||||
tool_data.remove_saved_points();
|
||||
tool_data.select_anchor_toggled = false;
|
||||
}
|
||||
|
|
@ -2612,6 +2635,15 @@ impl Fsm for PathToolFsmState {
|
|||
shape_editor.delete_point_and_break_path(document, responses);
|
||||
PathToolFsmState::Ready
|
||||
}
|
||||
(_, PathToolMessage::ClosePath) => {
|
||||
responses.add(DocumentMessage::AddTransaction);
|
||||
shape_editor.close_selected_path(document, responses, tool_action_data.preferences.vector_meshes);
|
||||
responses.add(DocumentMessage::EndTransaction);
|
||||
|
||||
responses.add(OverlaysMessage::Draw);
|
||||
|
||||
self
|
||||
}
|
||||
(_, PathToolMessage::StartSlidingPoint) => {
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
if tool_data.start_sliding_point(shape_editor, document) {
|
||||
|
|
@ -2928,8 +2960,8 @@ impl Fsm for PathToolFsmState {
|
|||
if !tool_data.double_click_handled && tool_data.drag_start_pos.distance(input.mouse.position) <= DRAG_THRESHOLD {
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
|
||||
shape_editor.select_points_by_manipulator_id(&tool_data.saved_points_before_anchor_convert_smooth_sharp.iter().copied().collect::<Vec<_>>());
|
||||
shape_editor.flip_smooth_sharp(&document.network_interface, input.mouse.position, SELECTION_TOLERANCE, responses);
|
||||
shape_editor.select_points_by_layer_and_id(&tool_data.saved_points_before_anchor_convert_smooth_sharp);
|
||||
shape_editor.flip_smooth_sharp(&document.network_interface, responses);
|
||||
tool_data.saved_points_before_anchor_convert_smooth_sharp.clear();
|
||||
|
||||
responses.add(DocumentMessage::EndTransaction);
|
||||
|
|
@ -3026,14 +3058,28 @@ impl Fsm for PathToolFsmState {
|
|||
|
||||
PathToolFsmState::Ready
|
||||
}
|
||||
(_, PathToolMessage::SelectAllAnchors) => {
|
||||
(_, PathToolMessage::SelectAll) => {
|
||||
shape_editor.select_all_anchors_in_selected_layers(document);
|
||||
|
||||
let point_editing_mode = tool_options.path_editing_mode.point_editing_mode;
|
||||
let segment_editing_mode = tool_options.path_editing_mode.segment_editing_mode;
|
||||
|
||||
if point_editing_mode {
|
||||
shape_editor.select_all_anchors_in_selected_layers(document);
|
||||
}
|
||||
if segment_editing_mode {
|
||||
shape_editor.select_all_segments_in_selected_layers(document);
|
||||
}
|
||||
|
||||
responses.add(OverlaysMessage::Draw);
|
||||
PathToolFsmState::Ready
|
||||
}
|
||||
(_, PathToolMessage::DeselectAllPoints) => {
|
||||
(_, PathToolMessage::DeselectAllSelected) => {
|
||||
shape_editor.deselect_all_points();
|
||||
shape_editor.deselect_all_segments();
|
||||
|
||||
responses.add(OverlaysMessage::Draw);
|
||||
|
||||
PathToolFsmState::Ready
|
||||
}
|
||||
(_, PathToolMessage::SelectedPointXChanged { new_x }) => {
|
||||
|
|
@ -3357,7 +3403,7 @@ fn update_dynamic_hints(
|
|||
position,
|
||||
SELECTION_THRESHOLD,
|
||||
tool_options.path_overlay_mode,
|
||||
&tool_data.frontier_handles_info,
|
||||
tool_data.frontier_handles_info.as_ref(),
|
||||
)
|
||||
.is_some();
|
||||
|
||||
|
|
|
|||
|
|
@ -1667,17 +1667,21 @@ impl Fsm for PenToolFsmState {
|
|||
path_overlays(document, DrawHandles::All, shape_editor, &mut overlay_context);
|
||||
}
|
||||
PenOverlayMode::FrontierHandles => {
|
||||
if let Some(latest_segment) = tool_data.prior_segment {
|
||||
path_overlays(document, DrawHandles::SelectedAnchors(vec![latest_segment]), shape_editor, &mut overlay_context);
|
||||
}
|
||||
// If a vector mesh then there can be more than one prior segments
|
||||
else if let Some(segments) = tool_data.prior_segments.clone() {
|
||||
if preferences.vector_meshes {
|
||||
path_overlays(document, DrawHandles::SelectedAnchors(segments), shape_editor, &mut overlay_context);
|
||||
if let Some(layer) = tool_data.current_layer {
|
||||
if let Some(latest_segment) = tool_data.prior_segment {
|
||||
let selected_anchors_data = HashMap::from([(layer, vec![latest_segment])]);
|
||||
path_overlays(document, DrawHandles::SelectedAnchors(selected_anchors_data), shape_editor, &mut overlay_context);
|
||||
}
|
||||
} else {
|
||||
path_overlays(document, DrawHandles::None, shape_editor, &mut overlay_context);
|
||||
};
|
||||
// If a vector mesh then there can be more than one prior segments
|
||||
else if let Some(segments) = tool_data.prior_segments.clone() {
|
||||
if preferences.vector_meshes {
|
||||
let selected_anchors_data = HashMap::from([(layer, segments)]);
|
||||
path_overlays(document, DrawHandles::SelectedAnchors(selected_anchors_data), shape_editor, &mut overlay_context);
|
||||
}
|
||||
} else {
|
||||
path_overlays(document, DrawHandles::None, shape_editor, &mut overlay_context);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue