Skip to content

Commit 523132d

Browse files
0SlowPoke0Keavon
andauthored
Add 'Circle' to the Shape tool and its associated gizmos (#2914)
* merged with circle and impl inner radius gizmo for arc * impl radius-gizmo for arc * fix only one gizmo shown at a time * Code review * make hints update when changing shape,add default behaviour when dragging to make circle earlier fixed to from center * fixed arc-radius hover threshold and show arc-endpoint when hover over arc-radius gizmo --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
1 parent 97bd0eb commit 523132d

File tree

13 files changed

+730
-155
lines changed

13 files changed

+730
-155
lines changed

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

Lines changed: 142 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,147 @@ impl OverlayContext {
294294
self.end_dpi_aware_transform();
295295
}
296296

297+
#[allow(clippy::too_many_arguments)]
298+
pub fn dashed_ellipse(
299+
&mut self,
300+
center: DVec2,
301+
radius_x: f64,
302+
radius_y: f64,
303+
rotation: Option<f64>,
304+
start_angle: Option<f64>,
305+
end_angle: Option<f64>,
306+
counterclockwise: Option<bool>,
307+
color_fill: Option<&str>,
308+
color_stroke: Option<&str>,
309+
dash_width: Option<f64>,
310+
dash_gap_width: Option<f64>,
311+
dash_offset: Option<f64>,
312+
) {
313+
let color_stroke = color_stroke.unwrap_or(COLOR_OVERLAY_BLUE);
314+
let center = center.round();
315+
316+
self.start_dpi_aware_transform();
317+
318+
if let Some(dash_width) = dash_width {
319+
let dash_gap_width = dash_gap_width.unwrap_or(1.);
320+
let array = js_sys::Array::new();
321+
array.push(&JsValue::from(dash_width));
322+
array.push(&JsValue::from(dash_gap_width));
323+
324+
if let Some(dash_offset) = dash_offset {
325+
if dash_offset != 0. {
326+
self.render_context.set_line_dash_offset(dash_offset);
327+
}
328+
}
329+
330+
self.render_context
331+
.set_line_dash(&JsValue::from(array))
332+
.map_err(|error| log::warn!("Error drawing dashed line: {:?}", error))
333+
.ok();
334+
}
335+
336+
self.render_context.begin_path();
337+
self.render_context
338+
.ellipse_with_anticlockwise(
339+
center.x,
340+
center.y,
341+
radius_x,
342+
radius_y,
343+
rotation.unwrap_or_default(),
344+
start_angle.unwrap_or_default(),
345+
end_angle.unwrap_or(TAU),
346+
counterclockwise.unwrap_or_default(),
347+
)
348+
.expect("Failed to draw ellipse");
349+
self.render_context.set_stroke_style_str(color_stroke);
350+
351+
if let Some(fill_color) = color_fill {
352+
self.render_context.set_fill_style_str(fill_color);
353+
self.render_context.fill();
354+
}
355+
self.render_context.stroke();
356+
357+
// Reset the dash pattern back to solid
358+
if dash_width.is_some() {
359+
self.render_context
360+
.set_line_dash(&JsValue::from(js_sys::Array::new()))
361+
.map_err(|error| log::warn!("Error drawing dashed line: {:?}", error))
362+
.ok();
363+
}
364+
if dash_offset.is_some() && dash_offset != Some(0.) {
365+
self.render_context.set_line_dash_offset(0.);
366+
}
367+
368+
self.end_dpi_aware_transform();
369+
}
370+
371+
pub fn dashed_circle(
372+
&mut self,
373+
position: DVec2,
374+
radius: f64,
375+
color_fill: Option<&str>,
376+
color_stroke: Option<&str>,
377+
dash_width: Option<f64>,
378+
dash_gap_width: Option<f64>,
379+
dash_offset: Option<f64>,
380+
transform: Option<DAffine2>,
381+
) {
382+
let color_stroke = color_stroke.unwrap_or(COLOR_OVERLAY_BLUE);
383+
let position = position.round();
384+
385+
self.start_dpi_aware_transform();
386+
387+
if let Some(transform) = transform {
388+
let [a, b, c, d, e, f] = transform.to_cols_array();
389+
self.render_context.transform(a, b, c, d, e, f).expect("Failed to transform circle");
390+
}
391+
392+
if let Some(dash_width) = dash_width {
393+
let dash_gap_width = dash_gap_width.unwrap_or(1.);
394+
let array = js_sys::Array::new();
395+
array.push(&JsValue::from(dash_width));
396+
array.push(&JsValue::from(dash_gap_width));
397+
398+
if let Some(dash_offset) = dash_offset {
399+
if dash_offset != 0. {
400+
self.render_context.set_line_dash_offset(dash_offset);
401+
}
402+
}
403+
404+
self.render_context
405+
.set_line_dash(&JsValue::from(array))
406+
.map_err(|error| log::warn!("Error drawing dashed line: {:?}", error))
407+
.ok();
408+
}
409+
410+
self.render_context.begin_path();
411+
self.render_context.arc(position.x, position.y, radius, 0., TAU).expect("Failed to draw the circle");
412+
self.render_context.set_stroke_style_str(color_stroke);
413+
414+
if let Some(fill_color) = color_fill {
415+
self.render_context.set_fill_style_str(fill_color);
416+
self.render_context.fill();
417+
}
418+
self.render_context.stroke();
419+
420+
// Reset the dash pattern back to solid
421+
if dash_width.is_some() {
422+
self.render_context
423+
.set_line_dash(&JsValue::from(js_sys::Array::new()))
424+
.map_err(|error| log::warn!("Error drawing dashed line: {:?}", error))
425+
.ok();
426+
}
427+
if dash_offset.is_some() && dash_offset != Some(0.) {
428+
self.render_context.set_line_dash_offset(0.);
429+
}
430+
431+
self.end_dpi_aware_transform();
432+
}
433+
434+
pub fn circle(&mut self, position: DVec2, radius: f64, color_fill: Option<&str>, color_stroke: Option<&str>) {
435+
self.dashed_circle(position, radius, color_fill, color_stroke, None, None, None, None);
436+
}
437+
297438
pub fn manipulator_handle(&mut self, position: DVec2, selected: bool, color: Option<&str>) {
298439
self.start_dpi_aware_transform();
299440

@@ -374,23 +515,6 @@ impl OverlayContext {
374515
self.end_dpi_aware_transform();
375516
}
376517

377-
pub fn circle(&mut self, position: DVec2, radius: f64, color_fill: Option<&str>, color_stroke: Option<&str>) {
378-
let color_fill = color_fill.unwrap_or(COLOR_OVERLAY_WHITE);
379-
let color_stroke = color_stroke.unwrap_or(COLOR_OVERLAY_BLUE);
380-
let position = position.round();
381-
382-
self.start_dpi_aware_transform();
383-
384-
self.render_context.begin_path();
385-
self.render_context.arc(position.x, position.y, radius, 0., TAU).expect("Failed to draw the circle");
386-
self.render_context.set_fill_style_str(color_fill);
387-
self.render_context.set_stroke_style_str(color_stroke);
388-
self.render_context.fill();
389-
self.render_context.stroke();
390-
391-
self.end_dpi_aware_transform();
392-
}
393-
394518
pub fn draw_arc(&mut self, center: DVec2, radius: f64, start_from: f64, end_at: f64) {
395519
let segments = ((end_at - start_from).abs() / (std::f64::consts::PI / 4.)).ceil() as usize;
396520
let step = (end_at - start_from) / segments as f64;
@@ -591,7 +715,7 @@ impl OverlayContext {
591715
}
592716

593717
pub fn arc_sweep_angle(&mut self, offset_angle: f64, angle: f64, end_point_position: DVec2, bold_radius: f64, pivot: DVec2, text: &str, transform: DAffine2) {
594-
self.manipulator_handle(end_point_position, true, Some(COLOR_OVERLAY_RED));
718+
self.manipulator_handle(end_point_position, true, None);
595719
self.draw_arc_gizmo_angle(pivot, bold_radius, ARC_SWEEP_GIZMO_RADIUS, offset_angle, angle.to_radians());
596720
self.text(&text, COLOR_OVERLAY_BLUE, None, transform, 16., [Pivot::Middle, Pivot::Middle]);
597721
}

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,23 @@ impl OverlayContext {
345345
self.scene.stroke(&kurbo::Stroke::new(1.0), transform, Self::parse_color(color_stroke), None, &circle);
346346
}
347347

348+
pub fn dashed_ellipse(
349+
&mut self,
350+
_center: DVec2,
351+
_radius_x: f64,
352+
_radius_y: f64,
353+
_rotation: Option<f64>,
354+
_start_angle: Option<f64>,
355+
_end_angle: Option<f64>,
356+
_counterclockwise: Option<bool>,
357+
_color_fill: Option<&str>,
358+
_color_stroke: Option<&str>,
359+
_dash_width: Option<f64>,
360+
_dash_gap_width: Option<f64>,
361+
_dash_offset: Option<f64>,
362+
) {
363+
}
364+
348365
pub fn draw_arc(&mut self, center: DVec2, radius: f64, start_from: f64, end_at: f64) {
349366
let segments = ((end_at - start_from).abs() / (std::f64::consts::PI / 4.)).ceil() as usize;
350367
let step = (end_at - start_from) / segments as f64;
@@ -541,7 +558,7 @@ impl OverlayContext {
541558

542559
#[allow(clippy::too_many_arguments)]
543560
pub fn arc_sweep_angle(&mut self, offset_angle: f64, angle: f64, end_point_position: DVec2, bold_radius: f64, pivot: DVec2, text: &str, transform: DAffine2) {
544-
self.manipulator_handle(end_point_position, true, Some(COLOR_OVERLAY_RED));
561+
self.manipulator_handle(end_point_position, true, None);
545562
self.draw_arc_gizmo_angle(pivot, bold_radius, ARC_SWEEP_GIZMO_RADIUS, offset_angle, angle.to_radians());
546563
self.text(text, COLOR_OVERLAY_BLUE, None, transform, 16., [Pivot::Middle, Pivot::Middle]);
547564
}

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::messages::prelude::{DocumentMessageHandler, InputPreprocessorMessageH
66
use crate::messages::tool::common_functionality::graph_modification_utils;
77
use crate::messages::tool::common_functionality::shape_editor::ShapeState;
88
use crate::messages::tool::common_functionality::shapes::arc_shape::ArcGizmoHandler;
9+
use crate::messages::tool::common_functionality::shapes::circle_shape::CircleGizmoHandler;
910
use crate::messages::tool::common_functionality::shapes::polygon_shape::PolygonGizmoHandler;
1011
use crate::messages::tool::common_functionality::shapes::shape_utility::ShapeGizmoHandler;
1112
use crate::messages::tool::common_functionality::shapes::star_shape::StarGizmoHandler;
@@ -26,6 +27,7 @@ pub enum ShapeGizmoHandlers {
2627
Star(StarGizmoHandler),
2728
Polygon(PolygonGizmoHandler),
2829
Arc(ArcGizmoHandler),
30+
Circle(CircleGizmoHandler),
2931
}
3032

3133
impl ShapeGizmoHandlers {
@@ -36,6 +38,7 @@ impl ShapeGizmoHandlers {
3638
Self::Star(_) => "star",
3739
Self::Polygon(_) => "polygon",
3840
Self::Arc(_) => "arc",
41+
Self::Circle(_) => "circle",
3942
Self::None => "none",
4043
}
4144
}
@@ -46,6 +49,7 @@ impl ShapeGizmoHandlers {
4649
Self::Star(h) => h.handle_state(layer, mouse_position, document, responses),
4750
Self::Polygon(h) => h.handle_state(layer, mouse_position, document, responses),
4851
Self::Arc(h) => h.handle_state(layer, mouse_position, document, responses),
52+
Self::Circle(h) => h.handle_state(layer, mouse_position, document, responses),
4953
Self::None => {}
5054
}
5155
}
@@ -56,6 +60,7 @@ impl ShapeGizmoHandlers {
5660
Self::Star(h) => h.is_any_gizmo_hovered(),
5761
Self::Polygon(h) => h.is_any_gizmo_hovered(),
5862
Self::Arc(h) => h.is_any_gizmo_hovered(),
63+
Self::Circle(h) => h.is_any_gizmo_hovered(),
5964
Self::None => false,
6065
}
6166
}
@@ -66,6 +71,7 @@ impl ShapeGizmoHandlers {
6671
Self::Star(h) => h.handle_click(),
6772
Self::Polygon(h) => h.handle_click(),
6873
Self::Arc(h) => h.handle_click(),
74+
Self::Circle(h) => h.handle_click(),
6975
Self::None => {}
7076
}
7177
}
@@ -76,6 +82,7 @@ impl ShapeGizmoHandlers {
7682
Self::Star(h) => h.handle_update(drag_start, document, input, responses),
7783
Self::Polygon(h) => h.handle_update(drag_start, document, input, responses),
7884
Self::Arc(h) => h.handle_update(drag_start, document, input, responses),
85+
Self::Circle(h) => h.handle_update(drag_start, document, input, responses),
7986
Self::None => {}
8087
}
8188
}
@@ -86,6 +93,7 @@ impl ShapeGizmoHandlers {
8693
Self::Star(h) => h.cleanup(),
8794
Self::Polygon(h) => h.cleanup(),
8895
Self::Arc(h) => h.cleanup(),
96+
Self::Circle(h) => h.cleanup(),
8997
Self::None => {}
9098
}
9199
}
@@ -104,6 +112,7 @@ impl ShapeGizmoHandlers {
104112
Self::Star(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context),
105113
Self::Polygon(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context),
106114
Self::Arc(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context),
115+
Self::Circle(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context),
107116
Self::None => {}
108117
}
109118
}
@@ -121,6 +130,7 @@ impl ShapeGizmoHandlers {
121130
Self::Star(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context),
122131
Self::Polygon(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context),
123132
Self::Arc(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context),
133+
Self::Circle(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context),
124134
Self::None => {}
125135
}
126136
}
@@ -130,6 +140,7 @@ impl ShapeGizmoHandlers {
130140
Self::Star(h) => h.mouse_cursor_icon(),
131141
Self::Polygon(h) => h.mouse_cursor_icon(),
132142
Self::Arc(h) => h.mouse_cursor_icon(),
143+
Self::Circle(h) => h.mouse_cursor_icon(),
133144
Self::None => None,
134145
}
135146
}
@@ -169,6 +180,10 @@ impl GizmoManager {
169180
if graph_modification_utils::get_arc_id(layer, &document.network_interface).is_some() {
170181
return Some(ShapeGizmoHandlers::Arc(ArcGizmoHandler::new()));
171182
}
183+
// Circle
184+
if graph_modification_utils::get_circle_id(layer, &document.network_interface).is_some() {
185+
return Some(ShapeGizmoHandlers::Circle(CircleGizmoHandler::default()));
186+
}
172187

173188
None
174189
}

0 commit comments

Comments
 (0)