Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Insert point on segment by clicking once (no more sliding); Ctrl+click to remove segment #2495

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion editor/src/messages/input_mapper/input_mappings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ pub fn input_mappings() -> Mapping {
entry!(KeyDown(Delete); modifiers=[Shift], action_dispatch=PathToolMessage::BreakPath),
entry!(KeyDown(Backspace); modifiers=[Shift], action_dispatch=PathToolMessage::BreakPath),
entry!(KeyDownNoRepeat(Tab); action_dispatch=PathToolMessage::SwapSelectedHandles),
entry!(KeyDown(MouseLeft); action_dispatch=PathToolMessage::MouseDown { direct_insert_without_sliding: Control, extend_selection: Shift, lasso_select: Control }),
entry!(KeyDown(MouseLeft); action_dispatch=PathToolMessage::MouseDown { delete_segment: Control, extend_selection: Shift, lasso_select: Control }),
entry!(KeyDown(MouseRight); action_dispatch=PathToolMessage::RightClick),
entry!(KeyDown(Escape); action_dispatch=PathToolMessage::Escape),
entry!(KeyDown(KeyG); action_dispatch=PathToolMessage::GRS { key: KeyG }),
Expand Down
31 changes: 31 additions & 0 deletions editor/src/messages/tool/common_functionality/shape_editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -858,6 +866,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 };
Expand Down
124 changes: 91 additions & 33 deletions editor/src/messages/tool/tool_messages/path_tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ pub enum PathToolMessage {
ManipulatorMakeHandlesFree,
ManipulatorMakeHandlesColinear,
MouseDown {
direct_insert_without_sliding: Key,
delete_segment: Key,
extend_selection: Key,
lasso_select: Key,
},
Expand Down Expand Up @@ -271,6 +271,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PathToo
BreakPath,
DeleteAndBreakPath,
ClosePath,
PointerMove,
),
PathToolFsmState::Dragging(_) => actions!(PathToolMessageDiscriminant;
Escape,
Expand Down Expand Up @@ -365,6 +366,7 @@ struct PathToolData {
segment: Option<ClosestSegment>,
snap_cache: SnapCache,
double_click_handled: bool,
delete_segment_pressed: bool,
auto_panning: AutoPanning,
saved_points_before_anchor_select_toggle: Vec<ManipulatorPointId>,
select_anchor_toggled: bool,
Expand Down Expand Up @@ -457,6 +459,7 @@ impl PathToolData {

fn end_insertion(&mut self, shape_editor: &mut ShapeState, responses: &mut VecDeque<Message>, kind: InsertEndKind) -> PathToolFsmState {
let mut commit_transaction = false;
self.delete_segment_pressed = false;
match self.segment.as_mut() {
None => {
warn!("Segment was `None` before `end_insertion`")
Expand Down Expand Up @@ -487,7 +490,6 @@ impl PathToolData {
input: &InputPreprocessorMessageHandler,
responses: &mut VecDeque<Message>,
extend_selection: bool,
direct_insert_without_sliding: bool,
lasso_select: bool,
) -> PathToolFsmState {
self.double_click_handled = false;
Expand Down Expand Up @@ -521,17 +523,7 @@ impl PathToolData {
}
PathToolFsmState::Dragging(self.dragging_state)
}
// We didn't find a point nearby, so now we'll try to add a point into the closest path segment
else if let Some(closed_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, SELECTION_TOLERANCE) {
responses.add(DocumentMessage::StartTransaction);
if direct_insert_without_sliding {
self.start_insertion(responses, closed_segment);
self.end_insertion(shape_editor, responses, InsertEndKind::Add { extend_selection })
} else {
self.start_insertion(responses, closed_segment)
}
}
// We didn't find a segment path, so consider selecting the nearest shape instead
// We didn't find a point, so consider selecting the nearest shape instead
else if let Some(layer) = document.click(input) {
shape_editor.deselect_all_points();
if extend_selection {
Expand Down Expand Up @@ -984,12 +976,36 @@ impl Fsm for PathToolFsmState {
let state = tool_data.update_insertion(shape_editor, document, responses, input);

if let Some(closest_segment) = &tool_data.segment {
overlay_context.manipulator_anchor(closest_segment.closest_point_to_viewport(), false, Some(COLOR_OVERLAY_BLUE));
if let (Some(handle1), Some(handle2)) = closest_segment.handle_positions(document.metadata()) {
overlay_context.line(closest_segment.closest_point_to_viewport(), handle1, Some(COLOR_OVERLAY_BLUE), None);
overlay_context.line(closest_segment.closest_point_to_viewport(), handle2, Some(COLOR_OVERLAY_BLUE), None);
overlay_context.manipulator_handle(handle1, false, Some(COLOR_OVERLAY_BLUE));
overlay_context.manipulator_handle(handle2, false, Some(COLOR_OVERLAY_BLUE));
// Perpendicular line when inserting a point, and a cross when deleting a segment
let tangent = if let (Some(handle1), Some(handle2)) = closest_segment.handle_positions(document.metadata()) {
(handle1 - handle2).try_normalize()
} else {
let layer = closest_segment.layer();
let points = closest_segment.points();
if let Some(vector_data) = document.network_interface.compute_modified_vector(layer) {
if let (Some(pos1), Some(pos2)) = (
ManipulatorPointId::Anchor(points[0]).get_position(&vector_data),
ManipulatorPointId::Anchor(points[1]).get_position(&vector_data),
) {
(pos1 - pos2).try_normalize()
} else {
None
}
} else {
None
}
}
.unwrap_or(DVec2::ZERO);
let perp = tangent.perp();
let point = closest_segment.closest_point_to_viewport();
if tool_data.delete_segment_pressed {
let degrees: f64 = 45.0;
let tilted_line = DVec2::from_angle(degrees.to_radians()).rotate(tangent);
let tilted_perp = tilted_line.perp();
overlay_context.line(point - tilted_line * 10., point + tilted_line * 10., Some(COLOR_OVERLAY_BLUE), None);
overlay_context.line(point - tilted_perp * 10., point + tilted_perp * 10., Some(COLOR_OVERLAY_BLUE), None);
} else {
overlay_context.line(point - perp * 10., point + perp * 10., Some(COLOR_OVERLAY_BLUE), None);
}
}

Expand All @@ -1004,36 +1020,59 @@ impl Fsm for PathToolFsmState {
}

// `Self::InsertPoint` case:
(Self::InsertPoint, PathToolMessage::MouseDown { extend_selection, .. } | PathToolMessage::Enter { extend_selection, .. }) => {
(Self::InsertPoint, PathToolMessage::MouseDown { extend_selection, delete_segment, .. }) => {
//| PathToolMessage::Enter { extend_selection, .. } consider adding support for ctrl key in here
tool_data.double_click_handled = true;
let extend_selection = input.keyboard.get(extend_selection as usize);
tool_data.end_insertion(shape_editor, responses, InsertEndKind::Add { extend_selection })
let delete_segment = input.keyboard.get(delete_segment as usize);

if delete_segment {
if let Some(closest_segment) = &tool_data.segment {
let segment = closest_segment.segment();
let layer = closest_segment.layer();
let points = closest_segment.points();
if let Some(vector_data) = document.network_interface.compute_modified_vector(layer) {
shape_editor.dissolve_segment(responses, layer, &vector_data, segment, points);
responses.add(DocumentMessage::EndTransaction);
}
}
return PathToolFsmState::Ready;
} else {
tool_data.end_insertion(shape_editor, responses, InsertEndKind::Add { extend_selection })
}
}
(Self::InsertPoint, PathToolMessage::PointerMove { .. }) => {
(Self::InsertPoint, PathToolMessage::PointerMove { lock_angle, .. }) => {
let lock_angle_state = input.keyboard.get(lock_angle as usize);
if lock_angle_state {
tool_data.delete_segment_pressed = true;
} else {
tool_data.delete_segment_pressed = false;
}

responses.add(OverlaysMessage::Draw);
// `tool_data.update_insertion` would be called on `OverlaysMessage::Draw`
// we anyway should to call it on `::Draw` because we can change scale by ctrl+scroll without `::PointerMove`

// If there is an anchor point very close to the current point then get out the InsertPoint mode
if shape_editor
.find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD)
.is_some()
{
return PathToolFsmState::Ready;
}
self
}
(Self::InsertPoint, PathToolMessage::Escape | PathToolMessage::Delete | PathToolMessage::RightClick) => tool_data.end_insertion(shape_editor, responses, InsertEndKind::Abort),
(Self::InsertPoint, PathToolMessage::GRS { key: _ }) => PathToolFsmState::InsertPoint,
// Mouse down
(
_,
PathToolMessage::MouseDown {
direct_insert_without_sliding,
extend_selection,
lasso_select,
},
) => {
(_, PathToolMessage::MouseDown { extend_selection, lasso_select, .. }) => {
let extend_selection = input.keyboard.get(extend_selection as usize);
let lasso_select = input.keyboard.get(lasso_select as usize);
let direct_insert_without_sliding = input.keyboard.get(direct_insert_without_sliding as usize);

tool_data.selection_mode = None;
tool_data.lasso_polygon.clear();

tool_data.mouse_down(shape_editor, document, input, responses, extend_selection, direct_insert_without_sliding, lasso_select)
tool_data.mouse_down(shape_editor, document, input, responses, extend_selection, lasso_select)
}
(
PathToolFsmState::Drawing { selection_shape },
Expand Down Expand Up @@ -1164,6 +1203,26 @@ impl Fsm for PathToolFsmState {

PathToolFsmState::Dragging(tool_data.dragging_state)
}
(PathToolFsmState::Ready, PathToolMessage::PointerMove { lock_angle, .. }) => {
// Check for a point in Selection threshold if it is there then don't change mode
if shape_editor
.find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD)
.is_some()
{
self
}
// Check for a segment nearby, if present then enter into insert point mode else go for ready
else if let Some(closed_segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, SELECTION_TOLERANCE) {
let lock_angle_state = input.keyboard.get(lock_angle as usize);
if lock_angle_state {
tool_data.delete_segment_pressed = true;
}
responses.add(DocumentMessage::StartTransaction);
tool_data.start_insertion(responses, closed_segment)
} else {
self
}
}
(PathToolFsmState::Drawing { selection_shape: selection_type }, PathToolMessage::PointerOutsideViewport { .. }) => {
// Auto-panning
if let Some(offset) = tool_data.auto_panning.shift_viewport(input, responses) {
Expand Down Expand Up @@ -1377,7 +1436,6 @@ impl Fsm for PathToolFsmState {
responses.add(OverlaysMessage::Draw);
PathToolFsmState::Ready
}
(_, PathToolMessage::PointerMove { .. }) => self,
(_, PathToolMessage::NudgeSelectedPoints { delta_x, delta_y }) => {
shape_editor.move_selected_points(
tool_data.opposing_handle_lengths.take(),
Expand Down
Loading