Skip to content

Commit d2e71a2

Browse files
committed
implemented the gizmo for polygon, added tests , brackets to increase sides
1 parent 15388c6 commit d2e71a2

File tree

16 files changed

+1578
-822
lines changed

16 files changed

+1578
-822
lines changed

editor/src/messages/input_mapper/input_mappings.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,35 @@ pub fn input_mappings() -> Mapping {
176176
entry!(KeyUp(MouseLeft); action_dispatch=ShapeToolMessage::DragStop),
177177
entry!(KeyDown(MouseRight); action_dispatch=ShapeToolMessage::Abort),
178178
entry!(KeyDown(Escape); action_dispatch=ShapeToolMessage::Abort),
179+
entry!(KeyDown(BracketLeft); action_dispatch=ShapeToolMessage::DecreaseSides),
180+
entry!(KeyDown(BracketRight); action_dispatch=ShapeToolMessage::IncreaseSides),
179181
entry!(PointerMove; refresh_keys=[Alt, Shift, Control], action_dispatch=ShapeToolMessage::PointerMove ([Alt, Shift, Control, Shift])),
182+
entry!(KeyDown(ArrowUp); modifiers=[Shift, ArrowLeft], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: -BIG_NUDGE_AMOUNT, delta_y: -BIG_NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
183+
entry!(KeyDown(ArrowUp); modifiers=[Shift, ArrowRight], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: BIG_NUDGE_AMOUNT, delta_y: -BIG_NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
184+
entry!(KeyDown(ArrowUp); modifiers=[Shift], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: 0., delta_y: -BIG_NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
185+
entry!(KeyDown(ArrowDown); modifiers=[Shift, ArrowLeft], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: -BIG_NUDGE_AMOUNT, delta_y: BIG_NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
186+
entry!(KeyDown(ArrowDown); modifiers=[Shift, ArrowRight], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: BIG_NUDGE_AMOUNT, delta_y: BIG_NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
187+
entry!(KeyDown(ArrowDown); modifiers=[Shift], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: 0., delta_y: BIG_NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
188+
entry!(KeyDown(ArrowLeft); modifiers=[Shift, ArrowUp], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: -BIG_NUDGE_AMOUNT, delta_y: -BIG_NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
189+
entry!(KeyDown(ArrowLeft); modifiers=[Shift, ArrowDown], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: -BIG_NUDGE_AMOUNT, delta_y: BIG_NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
190+
entry!(KeyDown(ArrowLeft); modifiers=[Shift], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: -BIG_NUDGE_AMOUNT, delta_y: 0., resize: Alt, resize_opposite_corner: Control }),
191+
entry!(KeyDown(ArrowRight); modifiers=[Shift, ArrowUp], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: BIG_NUDGE_AMOUNT, delta_y: -BIG_NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
192+
entry!(KeyDown(ArrowRight); modifiers=[Shift, ArrowDown], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: BIG_NUDGE_AMOUNT, delta_y: BIG_NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
193+
entry!(KeyDown(ArrowRight); modifiers=[Shift], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: BIG_NUDGE_AMOUNT, delta_y: 0., resize: Alt, resize_opposite_corner: Control }),
194+
entry!(KeyDown(ArrowUp); modifiers=[ArrowLeft], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: -NUDGE_AMOUNT, delta_y: -NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
195+
entry!(KeyDown(ArrowUp); modifiers=[ArrowRight], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: NUDGE_AMOUNT, delta_y: -NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
196+
entry!(KeyDown(ArrowUp); action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: 0., delta_y: -NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
197+
entry!(KeyDown(ArrowDown); modifiers=[ArrowLeft], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: -NUDGE_AMOUNT, delta_y: NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
198+
entry!(KeyDown(ArrowDown); modifiers=[ArrowRight], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: NUDGE_AMOUNT, delta_y: NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
199+
entry!(KeyDown(ArrowDown); action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: 0., delta_y: NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
200+
entry!(KeyDown(ArrowLeft); modifiers=[ArrowUp], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: -NUDGE_AMOUNT, delta_y: -NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
201+
entry!(KeyDown(ArrowLeft); modifiers=[ArrowDown], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: -NUDGE_AMOUNT, delta_y: NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
202+
entry!(KeyDown(ArrowLeft); action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: -NUDGE_AMOUNT, delta_y: 0., resize: Alt, resize_opposite_corner: Control }),
203+
entry!(KeyDown(ArrowRight); modifiers=[ArrowUp], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: NUDGE_AMOUNT, delta_y: -NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
204+
entry!(KeyDown(ArrowRight); modifiers=[ArrowDown], action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: NUDGE_AMOUNT, delta_y: NUDGE_AMOUNT, resize: Alt, resize_opposite_corner: Control }),
205+
entry!(KeyDown(ArrowRight); action_dispatch=ShapeToolMessage::NudgeSelectedLayers { delta_x: NUDGE_AMOUNT, delta_y: 0., resize: Alt, resize_opposite_corner: Control }),
206+
entry!(KeyDown(ArrowUp); action_dispatch=ShapeToolMessage::IncreaseSides),
207+
entry!(KeyDown(ArrowDown); action_dispatch=ShapeToolMessage::DecreaseSides),
180208
//
181209
// ImaginateToolMessage
182210
// entry!(KeyDown(MouseLeft); action_dispatch=ImaginateToolMessage::DragStart),
@@ -290,7 +318,7 @@ pub fn input_mappings() -> Mapping {
290318
entry!(KeyDown(KeyL); action_dispatch=ToolMessage::ActivateShapeLine),
291319
entry!(KeyDown(KeyM); action_dispatch=ToolMessage::ActivateShapeRectangle),
292320
entry!(KeyDown(KeyE); action_dispatch=ToolMessage::ActivateShapeEllipse),
293-
entry!(KeyDown(KeyY); action_dispatch=ToolMessage::ActivateToolPolygon),
321+
entry!(KeyDown(KeyY); action_dispatch=ToolMessage::ActivateToolShape),
294322
entry!(KeyDown(KeyB); action_dispatch=ToolMessage::ActivateToolBrush),
295323
entry!(KeyDown(KeyX); modifiers=[Accel, Shift], action_dispatch=ToolMessage::ResetColors),
296324
entry!(KeyDown(KeyX); modifiers=[Shift], action_dispatch=ToolMessage::SwapColors),

editor/src/messages/tool/common_functionality/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub mod measure;
66
pub mod pivot;
77
pub mod resize;
88
pub mod shape_editor;
9+
pub mod shape_gizmos;
910
pub mod shapes;
1011
pub mod snapping;
1112
pub mod transformation_cage;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pub mod number_of_points_handle;
2+
pub mod point_radius_handle;
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
use crate::messages::tool::{
2+
common_functionality::shapes::shape_utility::{calculate_polygon_vertex_position, extract_polygon_parameters, inside_polygon, inside_star},
3+
tool_messages::tool_prelude::Key,
4+
};
5+
use std::{collections::VecDeque, f64::consts::TAU};
6+
7+
use crate::messages::{portfolio::document::utility_types::document_metadata::LayerNodeIdentifier, prelude::Responses};
8+
use glam::{DAffine2, DVec2};
9+
use graph_craft::document::{NodeInput, value::TaggedValue};
10+
11+
use crate::{
12+
consts::{GIZMO_HIDE_THRESHOLD, NUMBER_OF_POINTS_HANDLE_SPOKE_EXTENSION, NUMBER_OF_POINTS_HANDLE_SPOKE_LENGTH, POINT_RADIUS_HANDLE_SEGMENT_THRESHOLD},
13+
messages::{
14+
frontend::utility_types::MouseCursorIcon,
15+
message::Message,
16+
portfolio::document::{overlays::utility_types::OverlayContext, utility_types::network_interface::InputConnector},
17+
prelude::{DocumentMessageHandler, FrontendMessage, InputPreprocessorMessageHandler, NodeGraphMessage},
18+
tool::common_functionality::{
19+
graph_modification_utils,
20+
shape_editor::ShapeState,
21+
shapes::shape_utility::{calculate_star_vertex_position, extract_star_parameters},
22+
},
23+
},
24+
};
25+
26+
#[derive(Clone, Debug, Default, PartialEq)]
27+
pub enum NumberOfPointsHandleState {
28+
#[default]
29+
Inactive,
30+
Hover,
31+
Dragging,
32+
}
33+
34+
#[derive(Clone, Debug, Default)]
35+
pub struct NumberOfPointsHandle {
36+
pub layer: Option<LayerNodeIdentifier>,
37+
pub initial_points: u32,
38+
pub handle_state: NumberOfPointsHandleState,
39+
}
40+
41+
impl NumberOfPointsHandle {
42+
pub fn cleanup(&mut self) {
43+
self.handle_state = NumberOfPointsHandleState::Inactive;
44+
self.layer = None;
45+
}
46+
pub fn update_state(&mut self, state: NumberOfPointsHandleState) {
47+
self.handle_state = state;
48+
}
49+
50+
pub fn is_hovering(&self) -> bool {
51+
self.handle_state == NumberOfPointsHandleState::Hover
52+
}
53+
54+
pub fn is_dragging(&self) -> bool {
55+
self.handle_state == NumberOfPointsHandleState::Dragging
56+
}
57+
58+
pub fn handle_actions(
59+
&mut self,
60+
document: &DocumentMessageHandler,
61+
input: &InputPreprocessorMessageHandler,
62+
mouse_position: DVec2,
63+
overlay_context: &mut OverlayContext,
64+
responses: &mut VecDeque<Message>,
65+
) {
66+
if input.keyboard.key(Key::Control) {
67+
return;
68+
}
69+
70+
match &self.handle_state {
71+
NumberOfPointsHandleState::Inactive => {
72+
for layer in document
73+
.network_interface
74+
.selected_nodes()
75+
.selected_visible_and_unlocked_layers(&document.network_interface)
76+
.filter(|layer| {
77+
graph_modification_utils::get_star_id(*layer, &document.network_interface).is_some() || graph_modification_utils::get_polygon_id(*layer, &document.network_interface).is_some()
78+
}) {
79+
if let Some((n, radius1, radius2)) = extract_star_parameters(Some(layer), document) {
80+
let viewport = document.metadata().transform_to_viewport(layer);
81+
let center = viewport.transform_point2(DVec2::ZERO);
82+
83+
let point_on_max_radius = calculate_star_vertex_position(viewport, 0, n, radius1, radius2);
84+
85+
if mouse_position.distance(center) < NUMBER_OF_POINTS_HANDLE_SPOKE_LENGTH && point_on_max_radius.distance(center) > GIZMO_HIDE_THRESHOLD {
86+
self.layer = Some(layer);
87+
self.initial_points = n;
88+
self.update_state(NumberOfPointsHandleState::Hover);
89+
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::EWResize });
90+
}
91+
}
92+
93+
if let Some((n, radius)) = extract_polygon_parameters(Some(layer), document) {
94+
let viewport = document.metadata().transform_to_viewport(layer);
95+
let center = viewport.transform_point2(DVec2::ZERO);
96+
97+
let point_on_max_radius = calculate_polygon_vertex_position(viewport, 0, n, radius);
98+
99+
if mouse_position.distance(center) < NUMBER_OF_POINTS_HANDLE_SPOKE_LENGTH && point_on_max_radius.distance(center) > GIZMO_HIDE_THRESHOLD {
100+
self.layer = Some(layer);
101+
self.initial_points = n;
102+
self.update_state(NumberOfPointsHandleState::Hover);
103+
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::EWResize });
104+
}
105+
}
106+
}
107+
}
108+
NumberOfPointsHandleState::Hover | NumberOfPointsHandleState::Dragging => {
109+
let Some(layer) = self.layer else {
110+
return;
111+
};
112+
113+
let Some((n, radius)) = extract_star_parameters(Some(layer), document)
114+
.map(|(n, r1, r2)| (n, r1.max(r2)))
115+
.or_else(|| extract_polygon_parameters(Some(layer), document).map(|(n, r)| (n, r)))
116+
else {
117+
return;
118+
};
119+
120+
let viewport = document.metadata().transform_to_viewport(layer);
121+
let center = viewport.transform_point2(DVec2::ZERO);
122+
123+
if mouse_position.distance(center) > NUMBER_OF_POINTS_HANDLE_SPOKE_LENGTH && matches!(&self.handle_state, NumberOfPointsHandleState::Hover) {
124+
self.update_state(NumberOfPointsHandleState::Inactive);
125+
self.layer = None;
126+
self.draw_spokes(center, viewport, n, radius, overlay_context);
127+
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default });
128+
129+
return;
130+
}
131+
}
132+
}
133+
}
134+
135+
pub fn overlays(
136+
&mut self,
137+
document: &DocumentMessageHandler,
138+
input: &InputPreprocessorMessageHandler,
139+
shape_editor: &mut &mut ShapeState,
140+
mouse_position: DVec2,
141+
overlay_context: &mut OverlayContext,
142+
) {
143+
if input.keyboard.key(Key::Control) {
144+
return;
145+
}
146+
147+
match &self.handle_state {
148+
NumberOfPointsHandleState::Inactive => {
149+
for layer in document
150+
.network_interface
151+
.selected_nodes()
152+
.selected_visible_and_unlocked_layers(&document.network_interface)
153+
.filter(|layer| {
154+
graph_modification_utils::get_star_id(*layer, &document.network_interface).is_some() || graph_modification_utils::get_polygon_id(*layer, &document.network_interface).is_some()
155+
}) {
156+
if let Some((n, radius1, radius2)) = extract_star_parameters(Some(layer), document) {
157+
let radius = radius1.max(radius2);
158+
let viewport = document.metadata().transform_to_viewport(layer);
159+
let center = viewport.transform_point2(DVec2::ZERO);
160+
161+
if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, mouse_position, POINT_RADIUS_HANDLE_SEGMENT_THRESHOLD) {
162+
if closest_segment.layer() == layer {
163+
return;
164+
}
165+
}
166+
let point_on_max_radius = calculate_star_vertex_position(viewport, 0, n, radius1, radius2);
167+
168+
if inside_star(viewport, n, radius1, radius2, mouse_position) && point_on_max_radius.distance(center) > GIZMO_HIDE_THRESHOLD {
169+
self.draw_spokes(center, viewport, n, radius, overlay_context);
170+
return;
171+
}
172+
}
173+
174+
if let Some((n, radius)) = extract_polygon_parameters(Some(layer), document) {
175+
let viewport = document.metadata().transform_to_viewport(layer);
176+
let center = viewport.transform_point2(DVec2::ZERO);
177+
178+
if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, mouse_position, POINT_RADIUS_HANDLE_SEGMENT_THRESHOLD) {
179+
if closest_segment.layer() == layer {
180+
return;
181+
}
182+
}
183+
let point_on_max_radius = calculate_polygon_vertex_position(viewport, 0, n, radius);
184+
185+
if inside_polygon(viewport, n, radius, mouse_position) && point_on_max_radius.distance(center) > GIZMO_HIDE_THRESHOLD {
186+
self.draw_spokes(center, viewport, n, radius, overlay_context);
187+
return;
188+
}
189+
}
190+
}
191+
}
192+
NumberOfPointsHandleState::Hover | NumberOfPointsHandleState::Dragging => {
193+
let Some(layer) = self.layer else {
194+
return;
195+
};
196+
197+
let Some((n, radius)) = extract_star_parameters(Some(layer), document)
198+
.map(|(n, r1, r2)| (n, r1.max(r2)))
199+
.or_else(|| extract_polygon_parameters(Some(layer), document).map(|(n, r)| (n, r)))
200+
else {
201+
return;
202+
};
203+
204+
let viewport = document.metadata().transform_to_viewport(layer);
205+
let center = viewport.transform_point2(DVec2::ZERO);
206+
207+
self.draw_spokes(center, viewport, n, radius, overlay_context);
208+
}
209+
}
210+
}
211+
212+
fn draw_spokes(&self, center: DVec2, viewport: DAffine2, n: u32, radius: f64, overlay_context: &mut OverlayContext) {
213+
for i in 0..n {
214+
let angle = ((i as f64) * TAU) / (n as f64);
215+
216+
let point = viewport.transform_point2(DVec2 {
217+
x: radius * angle.sin(),
218+
y: -radius * angle.cos(),
219+
});
220+
221+
let Some(direction) = (point - center).try_normalize() else {
222+
continue;
223+
};
224+
225+
// If the user zooms out such that shape is very small hide the gizmo
226+
if point.distance(center) < GIZMO_HIDE_THRESHOLD {
227+
return;
228+
}
229+
230+
let end_point = direction * NUMBER_OF_POINTS_HANDLE_SPOKE_LENGTH;
231+
if matches!(self.handle_state, NumberOfPointsHandleState::Hover | NumberOfPointsHandleState::Dragging) {
232+
overlay_context.line(center, end_point * NUMBER_OF_POINTS_HANDLE_SPOKE_EXTENSION + center, None, None);
233+
} else {
234+
overlay_context.line(center, end_point + center, None, None);
235+
}
236+
}
237+
}
238+
239+
pub fn update_no_of_sides(&self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>, drag_start: DVec2) {
240+
let delta = input.mouse.position - document.metadata().document_to_viewport.transform_point2(drag_start);
241+
let sign = (input.mouse.position.x - document.metadata().document_to_viewport.transform_point2(drag_start).x).signum();
242+
let net_delta = (delta.length() / 25.0).round() * sign;
243+
244+
let Some(layer) = self.layer else {
245+
return;
246+
};
247+
248+
let Some(node_id) = graph_modification_utils::get_star_id(layer, &document.network_interface).or(graph_modification_utils::get_polygon_id(layer, &document.network_interface)) else {
249+
return;
250+
};
251+
252+
let new_point_count = ((self.initial_points as i32) + (net_delta as i32)).max(3);
253+
254+
responses.add(NodeGraphMessage::SetInput {
255+
input_connector: InputConnector::node(node_id, 1),
256+
input: NodeInput::value(TaggedValue::U32(new_point_count as u32), false),
257+
});
258+
responses.add(NodeGraphMessage::RunDocumentGraph);
259+
}
260+
}

0 commit comments

Comments
 (0)