Skip to content

Commit 3d3cea2

Browse files
committed
Implemented all modes for pen and path tool
1 parent 97567bd commit 3d3cea2

File tree

4 files changed

+267
-38
lines changed

4 files changed

+267
-38
lines changed

editor/src/messages/portfolio/document/overlays/utility_functions.rs

Lines changed: 118 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
use super::utility_types::OverlayContext;
1+
use super::utility_types::{DrawHandles, OverlayContext};
22
use crate::consts::HIDE_HANDLE_DISTANCE;
33
use crate::messages::tool::common_functionality::shape_editor::{SelectedLayerState, ShapeState};
44
use crate::messages::tool::tool_messages::tool_prelude::{DocumentMessageHandler, PreferencesMessageHandler};
55

66
use graphene_core::vector::ManipulatorPointId;
77

8-
use glam::DVec2;
8+
use bezier_rs::Bezier;
9+
use glam::{DAffine2, DVec2};
10+
use graphene_std::vector::{PointId, SegmentId};
911
use wasm_bindgen::JsCast;
1012

1113
pub fn overlay_canvas_element() -> Option<web_sys::HtmlCanvasElement> {
@@ -23,7 +25,70 @@ pub fn overlay_canvas_context() -> web_sys::CanvasRenderingContext2d {
2325
create_context().expect("Failed to get canvas context")
2426
}
2527

26-
pub fn path_overlays(document: &DocumentMessageHandler, shape_editor: &mut ShapeState, overlay_context: &mut OverlayContext, draw_handles: bool) {
28+
pub fn get_selected_segments(document: &DocumentMessageHandler, shape_editor: &mut ShapeState) -> Vec<SegmentId> {
29+
let selected_points = shape_editor.selected_points();
30+
let selected_anchors: Vec<PointId> = selected_points
31+
.filter_map(|point_id| if let ManipulatorPointId::Anchor(p) = point_id { Some(*p) } else { None })
32+
.collect();
33+
//Collect the segments whose handles are selected
34+
let mut selected_segments: Vec<SegmentId> = shape_editor
35+
.selected_points()
36+
.filter_map(|point_id| {
37+
if let ManipulatorPointId::EndHandle(segment_id) = point_id {
38+
Some(*segment_id)
39+
} else if let ManipulatorPointId::PrimaryHandle(segment_id) = point_id {
40+
Some(*segment_id)
41+
} else {
42+
None
43+
}
44+
})
45+
.collect();
46+
//TODO: Currently if there are two duplicate layers, both of their segments get overlays
47+
// Segments of which the selected anchors are a part of
48+
for layer in document.network_interface.selected_nodes(&[]).unwrap().selected_layers(document.metadata()) {
49+
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
50+
continue;
51+
};
52+
for (segment_id, _bezier, start, end) in vector_data.segment_bezier_iter() {
53+
if selected_anchors.contains(&start) || selected_anchors.contains(&end) {
54+
selected_segments.push(segment_id);
55+
}
56+
}
57+
}
58+
selected_segments
59+
}
60+
61+
fn overlay_bezier_handles(
62+
segment_id: SegmentId,
63+
bezier: Bezier,
64+
transform: DAffine2,
65+
overlay_context: &mut OverlayContext,
66+
selected: Option<&SelectedLayerState>,
67+
is_selected: impl Fn(Option<&SelectedLayerState>, ManipulatorPointId) -> bool,
68+
) {
69+
let bezier = bezier.apply_transformation(|point| transform.transform_point2(point));
70+
let not_under_anchor = |position: DVec2, anchor: DVec2| position.distance_squared(anchor) >= HIDE_HANDLE_DISTANCE * HIDE_HANDLE_DISTANCE;
71+
match bezier.handles {
72+
bezier_rs::BezierHandles::Quadratic { handle } if not_under_anchor(handle, bezier.start) && not_under_anchor(handle, bezier.end) => {
73+
overlay_context.line(handle, bezier.start, None);
74+
overlay_context.line(handle, bezier.end, None);
75+
overlay_context.manipulator_handle(handle, is_selected(selected, ManipulatorPointId::PrimaryHandle(segment_id)), None);
76+
}
77+
bezier_rs::BezierHandles::Cubic { handle_start, handle_end } => {
78+
if not_under_anchor(handle_start, bezier.start) {
79+
overlay_context.line(handle_start, bezier.start, None);
80+
overlay_context.manipulator_handle(handle_start, is_selected(selected, ManipulatorPointId::PrimaryHandle(segment_id)), None);
81+
}
82+
if not_under_anchor(handle_end, bezier.end) {
83+
overlay_context.line(handle_end, bezier.end, None);
84+
overlay_context.manipulator_handle(handle_end, is_selected(selected, ManipulatorPointId::EndHandle(segment_id)), None);
85+
}
86+
}
87+
_ => {}
88+
}
89+
}
90+
91+
pub fn path_overlays(document: &DocumentMessageHandler, shape_editor: &mut ShapeState, overlay_context: &mut OverlayContext, draw_handles: DrawHandles) {
2792
for layer in document.network_interface.selected_nodes(&[]).unwrap().selected_layers(document.metadata()) {
2893
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
2994
continue;
@@ -35,30 +100,59 @@ pub fn path_overlays(document: &DocumentMessageHandler, shape_editor: &mut Shape
35100
overlay_context.outline_vector(&vector_data, transform);
36101

37102
//TODO: Here define which handles to show and which handles to not, for path tool selection
38-
if draw_handles {
39-
for (segment_id, bezier, _start, _end) in vector_data.segment_bezier_iter() {
40-
let bezier = bezier.apply_transformation(|point| transform.transform_point2(point));
41-
let not_under_anchor = |position: DVec2, anchor: DVec2| position.distance_squared(anchor) >= HIDE_HANDLE_DISTANCE * HIDE_HANDLE_DISTANCE;
42-
match bezier.handles {
43-
bezier_rs::BezierHandles::Quadratic { handle } if not_under_anchor(handle, bezier.start) && not_under_anchor(handle, bezier.end) => {
44-
overlay_context.line(handle, bezier.start, None);
45-
overlay_context.line(handle, bezier.end, None);
46-
overlay_context.manipulator_handle(handle, is_selected(selected, ManipulatorPointId::PrimaryHandle(segment_id)), None);
47-
}
48-
bezier_rs::BezierHandles::Cubic { handle_start, handle_end } => {
49-
if not_under_anchor(handle_start, bezier.start) {
50-
overlay_context.line(handle_start, bezier.start, None);
51-
overlay_context.manipulator_handle(handle_start, is_selected(selected, ManipulatorPointId::PrimaryHandle(segment_id)), None);
52-
}
53-
if not_under_anchor(handle_end, bezier.end) {
54-
overlay_context.line(handle_end, bezier.end, None);
55-
overlay_context.manipulator_handle(handle_end, is_selected(selected, ManipulatorPointId::EndHandle(segment_id)), None);
103+
match draw_handles {
104+
DrawHandles::All => {
105+
vector_data.segment_bezier_iter().for_each(|(segment_id, bezier, _start, _end)| {
106+
overlay_bezier_handles(segment_id, bezier, transform, overlay_context, selected, is_selected);
107+
});
108+
}
109+
DrawHandles::SelectedAnchors(ref selected_segments) => {
110+
vector_data
111+
.segment_bezier_iter()
112+
.filter(|(segment_id, ..)| selected_segments.contains(segment_id))
113+
.for_each(|(segment_id, bezier, _start, _end)| {
114+
overlay_bezier_handles(segment_id, bezier, transform, overlay_context, selected, is_selected);
115+
});
116+
}
117+
118+
DrawHandles::FrontierHandles(ref segment_endpoints) => {
119+
vector_data
120+
.segment_bezier_iter()
121+
.filter(|(segment_id, ..)| segment_endpoints.contains_key(&segment_id))
122+
.for_each(|(segment_id, bezier, start, end)| {
123+
if segment_endpoints.get(&segment_id).unwrap().len() == 1 {
124+
let point_to_render = segment_endpoints.get(&segment_id).unwrap()[0];
125+
let bezier = bezier.apply_transformation(|point| transform.transform_point2(point));
126+
let not_under_anchor = |position: DVec2, anchor: DVec2| position.distance_squared(anchor) >= HIDE_HANDLE_DISTANCE * HIDE_HANDLE_DISTANCE;
127+
match bezier.handles {
128+
bezier_rs::BezierHandles::Quadratic { handle } if not_under_anchor(handle, bezier.start) && not_under_anchor(handle, bezier.end) => {
129+
if start == point_to_render {
130+
overlay_context.line(handle, bezier.start, None);
131+
} else {
132+
overlay_context.line(handle, bezier.end, None);
133+
}
134+
overlay_context.manipulator_handle(handle, is_selected(selected, ManipulatorPointId::PrimaryHandle(segment_id)), None);
135+
}
136+
bezier_rs::BezierHandles::Cubic { handle_start, handle_end } => {
137+
if not_under_anchor(handle_start, bezier.start) && (point_to_render == start) {
138+
overlay_context.line(handle_start, bezier.start, None);
139+
overlay_context.manipulator_handle(handle_start, is_selected(selected, ManipulatorPointId::PrimaryHandle(segment_id)), None);
140+
}
141+
if not_under_anchor(handle_end, bezier.end) && (point_to_render == end) {
142+
overlay_context.line(handle_end, bezier.end, None);
143+
overlay_context.manipulator_handle(handle_end, is_selected(selected, ManipulatorPointId::EndHandle(segment_id)), None);
144+
}
145+
}
146+
_ => {}
147+
}
148+
} else {
149+
overlay_bezier_handles(segment_id, bezier, transform, overlay_context, selected, is_selected);
56150
}
57-
}
58-
_ => {}
59-
}
151+
});
60152
}
153+
DrawHandles::None => {}
61154
}
155+
62156
for (&id, &position) in vector_data.point_domain.ids().iter().zip(vector_data.point_domain.positions()) {
63157
overlay_context.manipulator_anchor(transform.transform_point2(position), is_selected(selected, ManipulatorPointId::Anchor(id)), None);
64158
}

editor/src/messages/portfolio/document/overlays/utility_types.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::collections::HashMap;
2+
13
use super::utility_functions::overlay_canvas_context;
24
use crate::consts::{
35
COLOR_OVERLAY_BLUE, COLOR_OVERLAY_TRANSPARENT, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_YELLOW, MANIPULATOR_GROUP_MARKER_SIZE, PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER,
@@ -6,7 +8,7 @@ use crate::messages::prelude::Message;
68

79
use bezier_rs::{Bezier, Subpath};
810
use graphene_core::renderer::Quad;
9-
use graphene_std::vector::{PointId, VectorData};
11+
use graphene_std::vector::{PointId, SegmentId, VectorData};
1012

1113
use core::borrow::Borrow;
1214
use core::f64::consts::TAU;
@@ -482,3 +484,10 @@ pub enum Pivot {
482484
Middle,
483485
End,
484486
}
487+
488+
pub enum DrawHandles {
489+
All,
490+
SelectedAnchors(Vec<SegmentId>),
491+
FrontierHandles(HashMap<SegmentId, Vec<PointId>>),
492+
None,
493+
}

editor/src/messages/tool/tool_messages/path_tool.rs

Lines changed: 96 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ use super::tool_prelude::*;
33
use crate::consts::{
44
COLOR_OVERLAY_BLUE, DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD, DRAG_THRESHOLD, HANDLE_ROTATE_SNAP_ANGLE, INSERT_POINT_ON_SEGMENT_TOO_FAR_DISTANCE, SELECTION_THRESHOLD, SELECTION_TOLERANCE,
55
};
6-
use crate::messages::portfolio::document::overlays::utility_functions::path_overlays;
7-
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
6+
use crate::messages::portfolio::document::overlays::utility_functions::{get_selected_segments, path_overlays};
7+
use crate::messages::portfolio::document::overlays::utility_types::{DrawHandles, OverlayContext};
88
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
99
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
1010
use crate::messages::preferences::SelectionMode;
@@ -15,15 +15,28 @@ use crate::messages::tool::common_functionality::shape_editor::{
1515
use crate::messages::tool::common_functionality::snapping::{SnapCache, SnapCandidatePoint, SnapConstraint, SnapData, SnapManager};
1616

1717
use graphene_core::renderer::Quad;
18-
use graphene_core::vector::ManipulatorPointId;
19-
use graphene_std::vector::NoHashBuilder;
18+
use graphene_core::vector::{ManipulatorPointId, PointId};
19+
use graphene_std::vector::{NoHashBuilder, SegmentId};
2020

2121
use std::vec;
2222

2323
#[derive(Default)]
2424
pub struct PathTool {
2525
fsm_state: PathToolFsmState,
2626
tool_data: PathToolData,
27+
options: PathToolOptions,
28+
}
29+
30+
pub struct PathToolOptions {
31+
path_overlay_mode: PathOverlayMode,
32+
}
33+
34+
impl Default for PathToolOptions {
35+
fn default() -> Self {
36+
Self {
37+
path_overlay_mode: PathOverlayMode::SelectedPointHandles,
38+
}
39+
}
2740
}
2841

2942
#[impl_message(Message, ToolMessage, Path)]
@@ -89,6 +102,19 @@ pub enum PathToolMessage {
89102
new_y: f64,
90103
},
91104
SwapSelectedHandles,
105+
UpdateOptions(PathOptionsUpdate),
106+
}
107+
108+
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
109+
pub enum PathOverlayMode {
110+
AllHandles = 0,
111+
SelectedPointHandles = 1,
112+
FrontierHandles = 2,
113+
}
114+
115+
#[derive(PartialEq, Eq, Clone, Debug, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
116+
pub enum PathOptionsUpdate {
117+
OverlayModeType(PathOverlayMode),
92118
}
93119

94120
impl ToolMetadata for PathTool {
@@ -103,6 +129,21 @@ impl ToolMetadata for PathTool {
103129
}
104130
}
105131

132+
fn create_path_overlay_mode_widget(path_overlay_mode: PathOverlayMode) -> WidgetHolder {
133+
let entries = vec![
134+
RadioEntryData::new("1")
135+
.label("1")
136+
.on_update(move |_| PathToolMessage::UpdateOptions(PathOptionsUpdate::OverlayModeType(PathOverlayMode::AllHandles)).into()),
137+
RadioEntryData::new("2")
138+
.label("2")
139+
.on_update(move |_| PathToolMessage::UpdateOptions(PathOptionsUpdate::OverlayModeType(PathOverlayMode::SelectedPointHandles)).into()),
140+
RadioEntryData::new("3")
141+
.label("3")
142+
.on_update(move |_| PathToolMessage::UpdateOptions(PathOptionsUpdate::OverlayModeType(PathOverlayMode::FrontierHandles)).into()),
143+
];
144+
RadioInput::new(entries).selected_index(Some(path_overlay_mode as u32)).widget_holder()
145+
}
146+
106147
impl LayoutHolder for PathTool {
107148
fn layout(&self) -> Layout {
108149
let coordinates = self.tool_data.selection_status.as_one().as_ref().map(|point| point.coordinates);
@@ -175,10 +216,12 @@ impl LayoutHolder for PathTool {
175216
x_location,
176217
related_seperator.clone(),
177218
y_location,
178-
unrelated_seperator,
219+
unrelated_seperator.clone(),
179220
colinear_handle_checkbox,
180221
related_seperator,
181222
colinear_handles_label,
223+
unrelated_seperator.clone(),
224+
create_path_overlay_mode_widget(self.options.path_overlay_mode),
182225
],
183226
}]))
184227
}
@@ -189,6 +232,12 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PathToo
189232
let updating_point = message == ToolMessage::Path(PathToolMessage::SelectedPointUpdated);
190233

191234
match message {
235+
ToolMessage::Path(PathToolMessage::UpdateOptions(action)) => match action {
236+
PathOptionsUpdate::OverlayModeType(overlay_mode_type) => {
237+
self.options.path_overlay_mode = overlay_mode_type;
238+
responses.add(OverlaysMessage::Draw);
239+
}
240+
},
192241
ToolMessage::Path(PathToolMessage::ClosePath) => {
193242
responses.add(DocumentMessage::AddTransaction);
194243
tool_data.shape_editor.close_selected_path(tool_data.document, responses);
@@ -204,7 +253,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PathToo
204253
}
205254
}
206255
_ => {
207-
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &(), responses, true);
256+
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &self.options, responses, true);
208257
}
209258
}
210259

@@ -686,9 +735,9 @@ impl PathToolData {
686735

687736
impl Fsm for PathToolFsmState {
688737
type ToolData = PathToolData;
689-
type ToolOptions = ();
738+
type ToolOptions = PathToolOptions;
690739

691-
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionHandlerData, _tool_options: &(), responses: &mut VecDeque<Message>) -> Self {
740+
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionHandlerData, tool_options: &Self::ToolOptions, responses: &mut VecDeque<Message>) -> Self {
692741
let ToolActionHandlerData { document, input, shape_editor, .. } = tool_action_data;
693742
let ToolMessage::Path(event) = event else { return self };
694743
match (self, event) {
@@ -703,7 +752,45 @@ impl Fsm for PathToolFsmState {
703752
self
704753
}
705754
(_, PathToolMessage::Overlays(mut overlay_context)) => {
706-
path_overlays(document, shape_editor, &mut overlay_context, true);
755+
//TODO: find the segment ids of which the selected points are a part of
756+
757+
match tool_options.path_overlay_mode {
758+
PathOverlayMode::AllHandles => {
759+
path_overlays(document, shape_editor, &mut overlay_context, DrawHandles::All);
760+
}
761+
762+
PathOverlayMode::SelectedPointHandles => {
763+
let selected_segments = get_selected_segments(document, shape_editor);
764+
path_overlays(document, shape_editor, &mut overlay_context, DrawHandles::SelectedAnchors(selected_segments));
765+
}
766+
767+
PathOverlayMode::FrontierHandles => {
768+
let selected_segments = get_selected_segments(document, shape_editor);
769+
let mut segment_endpoints: HashMap<SegmentId, Vec<PointId>> = HashMap::new();
770+
771+
for layer in document.network_interface.selected_nodes(&[]).unwrap().selected_layers(document.metadata()) {
772+
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
773+
continue;
774+
};
775+
776+
//The points which are part of only one segment will be rendered
777+
let mut selected_segments_by_point: HashMap<PointId, Vec<SegmentId>> = HashMap::new();
778+
for (segment_id, _bezier, start, end) in vector_data.segment_bezier_iter() {
779+
if selected_segments.contains(&segment_id) {
780+
selected_segments_by_point.entry(start).or_insert_with(Vec::new).push(segment_id);
781+
selected_segments_by_point.entry(end).or_insert_with(Vec::new).push(segment_id);
782+
}
783+
}
784+
for (point, attached_segments) in selected_segments_by_point {
785+
if attached_segments.len() == 1 {
786+
segment_endpoints.entry(attached_segments[0]).or_insert_with(Vec::new).push(point);
787+
}
788+
}
789+
}
790+
//Now frontier anchors can be sent for rendering overlays
791+
path_overlays(document, shape_editor, &mut overlay_context, DrawHandles::FrontierHandles(segment_endpoints));
792+
}
793+
}
707794

708795
match self {
709796
Self::Drawing { selection_shape } => {

0 commit comments

Comments
 (0)