Insert point on segment by clicking once (no more sliding) and Alt+click to delete a segment (#2495)

* segment overlay change

* Segment split and delete

* Cleanup

* graceful handling of edge cases

* Moved constants to conts.rs and tuned the threshold

* Remove going into another state

* Insert point mode cleanup

* Linting fix

* Code review

* Added hints

* Added field for delete segment

* Change controls and fix too far logic

* Fixes

* Code review

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Adesh Gupta
2025-04-30 01:03:24 +00:00
committed by GitHub
co-authored by Keavon Chambers
parent 478ace3349
commit da38f672ae
4 changed files with 138 additions and 127 deletions
@@ -104,6 +104,14 @@ impl ClosestSegment {
self.layer
}
pub fn segment(&self) -> SegmentId {
self.segment
}
pub fn points(&self) -> [PointId; 2] {
self.points
}
pub fn closest_point_to_viewport(&self) -> DVec2 {
self.bezier_point_to_viewport
}
@@ -128,9 +136,7 @@ impl ClosestSegment {
pub fn too_far(&self, mouse_position: DVec2, tolerance: f64, document_metadata: &DocumentMetadata) -> bool {
let dist_sq = self.distance_squared(mouse_position);
let stroke_width = document_metadata.document_to_viewport.decompose_scale().x.max(1.) * self.stroke_width;
let stroke_width_sq = stroke_width * stroke_width;
let tolerance_sq = tolerance * tolerance;
(stroke_width_sq + tolerance_sq) < dist_sq
(stroke_width + tolerance).powi(2) < dist_sq
}
pub fn handle_positions(&self, document_metadata: &DocumentMetadata) -> (Option<DVec2>, Option<DVec2>) {
@@ -199,6 +205,28 @@ impl ClosestSegment {
let id = self.adjusted_insert(responses);
shape_editor.select_anchor_point_by_id(self.layer, id, extend_selection)
}
pub fn calculate_perp(&self, document: &DocumentMessageHandler) -> DVec2 {
let tangent = if let (Some(handle1), Some(handle2)) = self.handle_positions(document.metadata()) {
(handle1 - handle2).try_normalize()
} else {
let [first_point, last_point] = self.points();
if let Some(vector_data) = document.network_interface.compute_modified_vector(self.layer()) {
if let (Some(pos1), Some(pos2)) = (
ManipulatorPointId::Anchor(first_point).get_position(&vector_data),
ManipulatorPointId::Anchor(last_point).get_position(&vector_data),
) {
(pos1 - pos2).try_normalize()
} else {
None
}
} else {
None
}
}
.unwrap_or(DVec2::ZERO);
tangent.perp()
}
}
// TODO Consider keeping a list of selected manipulators to minimize traversals of the layers
@@ -900,6 +928,29 @@ impl ShapeState {
.collect::<HashMap<_, _>>()
}
pub fn dissolve_segment(&self, responses: &mut VecDeque<Message>, layer: LayerNodeIdentifier, vector_data: &VectorData, segment: SegmentId, points: [PointId; 2]) {
// Checking which point is terminal point
let is_point1_terminal = vector_data.connected_count(points[0]) == 1;
let is_point2_terminal = vector_data.connected_count(points[1]) == 1;
// Delete the segment and terminal points
let modification_type = VectorModificationType::RemoveSegment { id: segment };
responses.add(GraphOperationMessage::Vector { layer, modification_type });
for &handles in vector_data.colinear_manipulators.iter().filter(|handles| handles.iter().any(|handle| handle.segment == segment)) {
let modification_type = VectorModificationType::SetG1Continuous { handles, enabled: false };
responses.add(GraphOperationMessage::Vector { layer, modification_type });
}
if is_point1_terminal {
let modification_type = VectorModificationType::RemovePoint { id: points[0] };
responses.add(GraphOperationMessage::Vector { layer, modification_type });
}
if is_point2_terminal {
let modification_type = VectorModificationType::RemovePoint { id: points[1] };
responses.add(GraphOperationMessage::Vector { layer, modification_type });
}
}
fn dissolve_anchor(anchor: PointId, responses: &mut VecDeque<Message>, layer: LayerNodeIdentifier, vector_data: &VectorData) -> Option<[(HandleId, PointId); 2]> {
// Delete point
let modification_type = VectorModificationType::RemovePoint { id: anchor };