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:
Adesh Gupta
2025-08-27 23:42:04 +00:00
committed by GitHub
co-authored by Keavon Chambers
parent 57853f755b
commit 34b52bcc54
10 changed files with 254 additions and 154 deletions
@@ -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,
}