Skip to content

Commit 3a8c1b6

Browse files
0SlowPoke0Keavon
andauthored
Add Arc drawing mode to the Shape tool and the associated angle gizmos (#2757)
* implement arc gizmo handler * fixed wrapping need to fix snapping and overlays * fixed all the issues * Code review --------- Co-authored-by: Keavon Chambers <[email protected]>
1 parent a1796db commit 3a8c1b6

File tree

14 files changed

+691
-26
lines changed

14 files changed

+691
-26
lines changed

editor/src/consts.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ pub const POINT_RADIUS_HANDLE_SNAP_THRESHOLD: f64 = 8.;
126126
pub const POINT_RADIUS_HANDLE_SEGMENT_THRESHOLD: f64 = 7.9;
127127
pub const NUMBER_OF_POINTS_DIAL_SPOKE_EXTENSION: f64 = 1.2;
128128
pub const NUMBER_OF_POINTS_DIAL_SPOKE_LENGTH: f64 = 10.;
129+
pub const ARC_SNAP_THRESHOLD: f64 = 5.;
130+
pub const ARC_SWEEP_GIZMO_RADIUS: f64 = 14.;
131+
pub const ARC_SWEEP_GIZMO_TEXT_HEIGHT: f64 = 12.;
129132
pub const GIZMO_HIDE_THRESHOLD: f64 = 20.;
130133

131134
// SCROLLBARS

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

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use super::utility_functions::overlay_canvas_context;
22
use crate::consts::{
3-
COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_50, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_YELLOW, COLOR_OVERLAY_YELLOW_DULL, COMPASS_ROSE_ARROW_SIZE,
4-
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,
5-
PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER,
3+
ARC_SWEEP_GIZMO_RADIUS, COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_50, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_YELLOW, COLOR_OVERLAY_YELLOW_DULL,
4+
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,
5+
PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER,
66
};
77
use crate::messages::prelude::Message;
88
use bezier_rs::{Bezier, Subpath};
@@ -423,6 +423,14 @@ impl OverlayContext {
423423
self.render_context.stroke();
424424
}
425425

426+
pub fn draw_arc_gizmo_angle(&mut self, pivot: DVec2, bold_radius: f64, dash_radius: f64, arc_radius: f64, offset_angle: f64, angle: f64) {
427+
let end_point1 = pivot + bold_radius * DVec2::from_angle(angle + offset_angle);
428+
let end_point2 = pivot + dash_radius * DVec2::from_angle(offset_angle);
429+
self.line(pivot, end_point1, None, None);
430+
self.dashed_line(pivot, end_point2, None, None, Some(2.), Some(2.), Some(0.5));
431+
self.draw_arc(pivot, arc_radius, offset_angle, (angle) % TAU + offset_angle);
432+
}
433+
426434
pub fn draw_angle(&mut self, pivot: DVec2, radius: f64, arc_radius: f64, offset_angle: f64, angle: f64) {
427435
let end_point1 = pivot + radius * DVec2::from_angle(angle + offset_angle);
428436
let end_point2 = pivot + radius * DVec2::from_angle(offset_angle);
@@ -584,6 +592,12 @@ impl OverlayContext {
584592
self.end_dpi_aware_transform();
585593
}
586594

595+
pub fn arc_sweep_angle(&mut self, offset_angle: f64, angle: f64, end_point_position: DVec2, bold_radius: f64, dash_radius: f64, pivot: DVec2, text: &str, transform: DAffine2) {
596+
self.manipulator_handle(end_point_position, true, Some(COLOR_OVERLAY_RED));
597+
self.draw_arc_gizmo_angle(pivot, bold_radius, dash_radius, ARC_SWEEP_GIZMO_RADIUS, offset_angle, angle.to_radians());
598+
self.text(&text, COLOR_OVERLAY_BLUE, None, transform, 16., [Pivot::Middle, Pivot::Middle]);
599+
}
600+
587601
/// Used by the Pen and Path tools to outline the path of the shape.
588602
pub fn outline_vector(&mut self, vector_data: &VectorData, transform: DAffine2) {
589603
self.start_dpi_aware_transform();

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -517,8 +517,8 @@ impl<'a> Selected<'a> {
517517
tool_type: &'a ToolType,
518518
pen_handle: Option<&'a mut DVec2>,
519519
) -> Self {
520-
// If user is using the Select tool then use the original layer transforms
521-
if (*tool_type == ToolType::Select) && (*original_transforms == OriginalTransforms::Path(HashMap::new())) {
520+
// If user is using the Select tool or Shape tool then use the original layer transforms
521+
if (*tool_type == ToolType::Select || *tool_type == ToolType::Shape) && (*original_transforms == OriginalTransforms::Path(HashMap::new())) {
522522
*original_transforms = OriginalTransforms::Layer(HashMap::new());
523523
}
524524

editor/src/messages/portfolio/document_migration.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
338338
node: graphene_std::raster_nodes::gradient_map::gradient_map::IDENTIFIER,
339339
aliases: &[
340340
"graphene_raster_nodes::gradient_map::GradientMapNode",
341+
"graphene_raster_nodes::adjustments::GradientMapNode",
341342
"graphene_core::raster::adjustments::GradientMapNode",
342343
"graphene_core::raster::GradientMapNode",
343344
],

editor/src/messages/tool/common_functionality/gizmos/gizmo_manager.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye
44
use crate::messages::prelude::{DocumentMessageHandler, InputPreprocessorMessageHandler};
55
use crate::messages::tool::common_functionality::graph_modification_utils;
66
use crate::messages::tool::common_functionality::shape_editor::ShapeState;
7+
use crate::messages::tool::common_functionality::shapes::arc_shape::ArcGizmoHandler;
78
use crate::messages::tool::common_functionality::shapes::polygon_shape::PolygonGizmoHandler;
89
use crate::messages::tool::common_functionality::shapes::shape_utility::ShapeGizmoHandler;
910
use crate::messages::tool::common_functionality::shapes::star_shape::StarGizmoHandler;
@@ -23,6 +24,7 @@ pub enum ShapeGizmoHandlers {
2324
None,
2425
Star(StarGizmoHandler),
2526
Polygon(PolygonGizmoHandler),
27+
Arc(ArcGizmoHandler),
2628
}
2729

2830
impl ShapeGizmoHandlers {
@@ -32,6 +34,7 @@ impl ShapeGizmoHandlers {
3234
match self {
3335
Self::Star(_) => "star",
3436
Self::Polygon(_) => "polygon",
37+
Self::Arc(_) => "arc",
3538
Self::None => "none",
3639
}
3740
}
@@ -41,6 +44,7 @@ impl ShapeGizmoHandlers {
4144
match self {
4245
Self::Star(h) => h.handle_state(layer, mouse_position, document, responses),
4346
Self::Polygon(h) => h.handle_state(layer, mouse_position, document, responses),
47+
Self::Arc(h) => h.handle_state(layer, mouse_position, document, responses),
4448
Self::None => {}
4549
}
4650
}
@@ -50,6 +54,7 @@ impl ShapeGizmoHandlers {
5054
match self {
5155
Self::Star(h) => h.is_any_gizmo_hovered(),
5256
Self::Polygon(h) => h.is_any_gizmo_hovered(),
57+
Self::Arc(h) => h.is_any_gizmo_hovered(),
5358
Self::None => false,
5459
}
5560
}
@@ -59,6 +64,7 @@ impl ShapeGizmoHandlers {
5964
match self {
6065
Self::Star(h) => h.handle_click(),
6166
Self::Polygon(h) => h.handle_click(),
67+
Self::Arc(h) => h.handle_click(),
6268
Self::None => {}
6369
}
6470
}
@@ -68,6 +74,7 @@ impl ShapeGizmoHandlers {
6874
match self {
6975
Self::Star(h) => h.handle_update(drag_start, document, input, responses),
7076
Self::Polygon(h) => h.handle_update(drag_start, document, input, responses),
77+
Self::Arc(h) => h.handle_update(drag_start, document, input, responses),
7178
Self::None => {}
7279
}
7380
}
@@ -77,6 +84,7 @@ impl ShapeGizmoHandlers {
7784
match self {
7885
Self::Star(h) => h.cleanup(),
7986
Self::Polygon(h) => h.cleanup(),
87+
Self::Arc(h) => h.cleanup(),
8088
Self::None => {}
8189
}
8290
}
@@ -94,6 +102,7 @@ impl ShapeGizmoHandlers {
94102
match self {
95103
Self::Star(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context),
96104
Self::Polygon(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context),
105+
Self::Arc(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context),
97106
Self::None => {}
98107
}
99108
}
@@ -110,6 +119,7 @@ impl ShapeGizmoHandlers {
110119
match self {
111120
Self::Star(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context),
112121
Self::Polygon(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context),
122+
Self::Arc(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context),
113123
Self::None => {}
114124
}
115125
}
@@ -141,11 +151,14 @@ impl GizmoManager {
141151
if graph_modification_utils::get_star_id(layer, &document.network_interface).is_some() {
142152
return Some(ShapeGizmoHandlers::Star(StarGizmoHandler::default()));
143153
}
144-
145154
// Polygon
146155
if graph_modification_utils::get_polygon_id(layer, &document.network_interface).is_some() {
147156
return Some(ShapeGizmoHandlers::Polygon(PolygonGizmoHandler::default()));
148157
}
158+
// Arc
159+
if graph_modification_utils::get_arc_id(layer, &document.network_interface).is_some() {
160+
return Some(ShapeGizmoHandlers::Arc(ArcGizmoHandler::new()));
161+
}
149162

150163
None
151164
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
pub mod number_of_points_dial;
22
pub mod point_radius_handle;
3+
pub mod sweep_angle_gizmo;

editor/src/messages/tool/common_functionality/gizmos/shape_gizmos/point_radius_handle.rs

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,6 @@ impl PointRadiusHandle {
262262
};
263263

264264
let viewport = document.metadata().transform_to_viewport(layer);
265-
let center = viewport.transform_point2(DVec2::ZERO);
266265

267266
match snapping_index {
268267
// Make a triangle with previous two points
@@ -274,41 +273,57 @@ impl PointRadiusHandle {
274273
overlay_context.line(before_outer_position, outer_position, Some(COLOR_OVERLAY_RED), Some(3.));
275274
overlay_context.line(outer_position, point_position, Some(COLOR_OVERLAY_RED), Some(3.));
276275

276+
let before_outer_position = viewport.inverse().transform_point2(before_outer_position);
277+
let outer_position = viewport.inverse().transform_point2(outer_position);
278+
let point_position = viewport.inverse().transform_point2(point_position);
279+
277280
let l1 = (before_outer_position - outer_position).length() * 0.2;
278281
let Some(l1_direction) = (before_outer_position - outer_position).try_normalize() else { return };
279282
let Some(l2_direction) = (point_position - outer_position).try_normalize() else { return };
280-
let Some(direction) = (center - outer_position).try_normalize() else { return };
283+
let Some(direction) = (-outer_position).try_normalize() else { return };
281284

282285
let new_point = SQRT_2 * l1 * direction + outer_position;
283286

284287
let before_outer_position = l1 * l1_direction + outer_position;
285288
let point_position = l1 * l2_direction + outer_position;
286289

287-
overlay_context.line(before_outer_position, new_point, Some(COLOR_OVERLAY_RED), Some(3.));
288-
overlay_context.line(new_point, point_position, Some(COLOR_OVERLAY_RED), Some(3.));
290+
overlay_context.line(
291+
viewport.transform_point2(before_outer_position),
292+
viewport.transform_point2(new_point),
293+
Some(COLOR_OVERLAY_RED),
294+
Some(3.),
295+
);
296+
overlay_context.line(viewport.transform_point2(new_point), viewport.transform_point2(point_position), Some(COLOR_OVERLAY_RED), Some(3.));
289297
}
290298
1 => {
291299
let before_outer_position = star_vertex_position(viewport, (self.point as i32) - 1, sides, radius1, radius2);
292-
293300
let after_point_position = star_vertex_position(viewport, (self.point as i32) + 1, sides, radius1, radius2);
294-
295301
let point_position = star_vertex_position(viewport, self.point as i32, sides, radius1, radius2);
296302

297303
overlay_context.line(before_outer_position, point_position, Some(COLOR_OVERLAY_RED), Some(3.));
298304
overlay_context.line(point_position, after_point_position, Some(COLOR_OVERLAY_RED), Some(3.));
299305

306+
let before_outer_position = viewport.inverse().transform_point2(before_outer_position);
307+
let after_point_position = viewport.inverse().transform_point2(after_point_position);
308+
let point_position = viewport.inverse().transform_point2(point_position);
309+
300310
let l1 = (before_outer_position - point_position).length() * 0.2;
301311
let Some(l1_direction) = (before_outer_position - point_position).try_normalize() else { return };
302312
let Some(l2_direction) = (after_point_position - point_position).try_normalize() else { return };
303-
let Some(direction) = (center - point_position).try_normalize() else { return };
313+
let Some(direction) = (-point_position).try_normalize() else { return };
304314

305315
let new_point = SQRT_2 * l1 * direction + point_position;
306316

307317
let before_outer_position = l1 * l1_direction + point_position;
308318
let after_point_position = l1 * l2_direction + point_position;
309319

310-
overlay_context.line(before_outer_position, new_point, Some(COLOR_OVERLAY_RED), Some(3.));
311-
overlay_context.line(new_point, after_point_position, Some(COLOR_OVERLAY_RED), Some(3.));
320+
overlay_context.line(
321+
viewport.transform_point2(before_outer_position),
322+
viewport.transform_point2(new_point),
323+
Some(COLOR_OVERLAY_RED),
324+
Some(3.),
325+
);
326+
overlay_context.line(viewport.transform_point2(new_point), viewport.transform_point2(after_point_position), Some(COLOR_OVERLAY_RED), Some(3.));
312327
}
313328
i => {
314329
// Use `self.point` as absolute reference as it matches the index of vertices of the star starting from 0

0 commit comments

Comments
 (0)