Fix Shape tools bugs with stars/polygons with negative radii and circle radius click detection when viewport is zoomed (#2986)
* fixed spacing,click detection in radius gizmo fix drag in start point radiius gizmo * Code review --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
5637f01845
commit
fd66f29853
|
|
@ -118,16 +118,19 @@ impl RadiusHandle {
|
||||||
let viewport = document.metadata().transform_to_viewport(layer);
|
let viewport = document.metadata().transform_to_viewport(layer);
|
||||||
let center = viewport.transform_point2(DVec2::ZERO);
|
let center = viewport.transform_point2(DVec2::ZERO);
|
||||||
|
|
||||||
let start_point = viewport.transform_point2(calculate_circle_point_position(0., radius)).distance(center);
|
let x_point = viewport.transform_point2(calculate_circle_point_position(0., radius));
|
||||||
let end_point = viewport.transform_point2(calculate_circle_point_position(FRAC_PI_2, radius)).distance(center);
|
let y_point = viewport.transform_point2(calculate_circle_point_position(FRAC_PI_2, radius));
|
||||||
|
|
||||||
|
let direction_x = viewport.transform_vector2(DVec2::X);
|
||||||
|
let direction_y = viewport.transform_vector2(-DVec2::Y);
|
||||||
|
|
||||||
if let Some(stroke_width) = get_stroke_width(layer, &document.network_interface) {
|
if let Some(stroke_width) = get_stroke_width(layer, &document.network_interface) {
|
||||||
let spacing = Self::calculate_extra_spacing(viewport, radius, center, stroke_width, 15.);
|
let spacing = Self::calculate_extra_spacing(viewport, radius, center, stroke_width, 15.);
|
||||||
let smaller_radius_x = (start_point - spacing).abs();
|
let smaller_radius_x = (x_point - direction_x * spacing).distance(center);
|
||||||
let smaller_radius_y = (end_point - spacing).abs();
|
let smaller_radius_y = (y_point - direction_y * spacing).distance(center);
|
||||||
|
|
||||||
let larger_radius_x = (start_point + spacing).abs();
|
let larger_radius_x = (x_point + direction_x * spacing).distance(center);
|
||||||
let larger_radius_y = (end_point + spacing).abs();
|
let larger_radius_y = (y_point + direction_y * spacing).distance(center);
|
||||||
|
|
||||||
overlay_context.dashed_ellipse(center, smaller_radius_x, smaller_radius_y, None, None, None, None, None, None, Some(4.), Some(4.), Some(0.5));
|
overlay_context.dashed_ellipse(center, smaller_radius_x, smaller_radius_y, None, None, None, None, None, None, Some(4.), Some(4.), Some(0.5));
|
||||||
overlay_context.dashed_ellipse(center, larger_radius_x, larger_radius_y, None, None, None, None, None, None, Some(4.), Some(4.), Some(0.5));
|
overlay_context.dashed_ellipse(center, larger_radius_x, larger_radius_y, None, None, None, None, None, None, Some(4.), Some(4.), Some(0.5));
|
||||||
|
|
@ -135,7 +138,9 @@ impl RadiusHandle {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
overlay_context.dashed_ellipse(center, start_point, end_point, None, None, None, None, None, None, Some(4.), Some(4.), Some(0.5));
|
let radius_x = x_point.distance(center);
|
||||||
|
let radius_y = y_point.distance(center);
|
||||||
|
overlay_context.dashed_ellipse(center, radius_x, radius_y, None, None, None, None, None, None, Some(4.), Some(4.), Some(0.5));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -153,7 +158,7 @@ impl RadiusHandle {
|
||||||
let center = viewport_transform.transform_point2(DVec2::ZERO);
|
let center = viewport_transform.transform_point2(DVec2::ZERO);
|
||||||
|
|
||||||
let delta_vector = viewport_transform.inverse().transform_point2(input.mouse.position) - viewport_transform.inverse().transform_point2(self.previous_mouse_position);
|
let delta_vector = viewport_transform.inverse().transform_point2(input.mouse.position) - viewport_transform.inverse().transform_point2(self.previous_mouse_position);
|
||||||
let radius = document.metadata().document_to_viewport.transform_point2(drag_start) - center;
|
let radius = drag_start - center;
|
||||||
let sign = radius.dot(delta_vector).signum();
|
let sign = radius.dot(delta_vector).signum();
|
||||||
|
|
||||||
let net_delta = delta_vector.length() * sign * self.initial_radius.signum();
|
let net_delta = delta_vector.length() * sign * self.initial_radius.signum();
|
||||||
|
|
|
||||||
|
|
@ -189,8 +189,8 @@ impl NumberOfPointsDial {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_number_of_sides(&self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>, drag_start: DVec2) {
|
pub fn update_number_of_sides(&self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>, drag_start: DVec2) {
|
||||||
let delta = input.mouse.position - document.metadata().document_to_viewport.transform_point2(drag_start);
|
let delta = input.mouse.position - drag_start;
|
||||||
let sign = (input.mouse.position.x - document.metadata().document_to_viewport.transform_point2(drag_start).x).signum();
|
let sign = (input.mouse.position.x - drag_start.x).signum();
|
||||||
let net_delta = (delta.length() / 25.).round() * sign;
|
let net_delta = (delta.length() / 25.).round() * sign;
|
||||||
|
|
||||||
let Some(layer) = self.layer else { return };
|
let Some(layer) = self.layer else { return };
|
||||||
|
|
|
||||||
|
|
@ -142,14 +142,7 @@ impl PointRadiusHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn overlays(
|
pub fn overlays(&self, selected_star_layer: Option<LayerNodeIdentifier>, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, overlay_context: &mut OverlayContext) {
|
||||||
&self,
|
|
||||||
selected_star_layer: Option<LayerNodeIdentifier>,
|
|
||||||
document: &DocumentMessageHandler,
|
|
||||||
input: &InputPreprocessorMessageHandler,
|
|
||||||
mouse_position: DVec2,
|
|
||||||
overlay_context: &mut OverlayContext,
|
|
||||||
) {
|
|
||||||
match &self.handle_state {
|
match &self.handle_state {
|
||||||
PointRadiusHandleState::Inactive => {
|
PointRadiusHandleState::Inactive => {
|
||||||
let Some(layer) = selected_star_layer else { return };
|
let Some(layer) = selected_star_layer else { return };
|
||||||
|
|
@ -161,25 +154,12 @@ impl PointRadiusHandle {
|
||||||
for i in 0..(2 * sides) {
|
for i in 0..(2 * sides) {
|
||||||
let point = star_vertex_position(viewport, i as i32, sides, radius1, radius2);
|
let point = star_vertex_position(viewport, i as i32, sides, radius1, radius2);
|
||||||
let center = viewport.transform_point2(DVec2::ZERO);
|
let center = viewport.transform_point2(DVec2::ZERO);
|
||||||
let viewport_diagonal = input.viewport_bounds.size().length();
|
|
||||||
|
|
||||||
// If the user zooms out such that shape is very small hide the gizmo
|
// If the user zooms out such that shape is very small hide the gizmo
|
||||||
if point.distance(center) < GIZMO_HIDE_THRESHOLD {
|
if point.distance(center) < GIZMO_HIDE_THRESHOLD {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if point.distance(mouse_position) < 5. {
|
|
||||||
let Some(direction) = (point - center).try_normalize() else { continue };
|
|
||||||
|
|
||||||
overlay_context.manipulator_handle(point, true, None);
|
|
||||||
let angle = ((i as f64) * PI) / (sides as f64);
|
|
||||||
overlay_context.line(center, center + direction * viewport_diagonal, None, None);
|
|
||||||
|
|
||||||
draw_snapping_ticks(&self.snap_radii, direction, viewport, angle, overlay_context);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
overlay_context.manipulator_handle(point, false, None);
|
overlay_context.manipulator_handle(point, false, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -191,22 +171,12 @@ impl PointRadiusHandle {
|
||||||
for i in 0..sides {
|
for i in 0..sides {
|
||||||
let point = polygon_vertex_position(viewport, i as i32, sides, radius);
|
let point = polygon_vertex_position(viewport, i as i32, sides, radius);
|
||||||
let center = viewport.transform_point2(DVec2::ZERO);
|
let center = viewport.transform_point2(DVec2::ZERO);
|
||||||
let viewport_diagonal = input.viewport_bounds.size().length();
|
|
||||||
|
|
||||||
// If the user zooms out such that shape is very small hide the gizmo
|
// If the user zooms out such that shape is very small hide the gizmo
|
||||||
if point.distance(center) < GIZMO_HIDE_THRESHOLD {
|
if point.distance(center) < GIZMO_HIDE_THRESHOLD {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if point.distance(mouse_position) < 5. {
|
|
||||||
let Some(direction) = (point - center).try_normalize() else { continue };
|
|
||||||
|
|
||||||
overlay_context.manipulator_handle(point, true, None);
|
|
||||||
overlay_context.line(center, center + direction * viewport_diagonal, None, None);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
overlay_context.manipulator_handle(point, false, None);
|
overlay_context.manipulator_handle(point, false, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -232,12 +202,9 @@ impl PointRadiusHandle {
|
||||||
star_outline(Some(layer), document, overlay_context);
|
star_outline(Some(layer), document, overlay_context);
|
||||||
|
|
||||||
// Make the ticks for snapping
|
// Make the ticks for snapping
|
||||||
|
if (radius1.signum() * radius2.signum()).is_sign_positive() {
|
||||||
// If dragging to make radius negative don't show the
|
draw_snapping_ticks(&self.snap_radii, direction, viewport, angle, overlay_context);
|
||||||
if (mouse_position - center).dot(direction) < 0. {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
draw_snapping_ticks(&self.snap_radii, direction, viewport, angle, overlay_context);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -368,25 +335,36 @@ impl PointRadiusHandle {
|
||||||
return snap_radii;
|
return snap_radii;
|
||||||
};
|
};
|
||||||
|
|
||||||
let other_index = if radius_index == 3 { 2 } else { 3 };
|
let (Some(&TaggedValue::F64(radius_1)), Some(&TaggedValue::F64(radius_2))) = (node_inputs[2].as_value(), node_inputs[3].as_value()) else {
|
||||||
|
|
||||||
let Some(&TaggedValue::F64(other_radius)) = node_inputs[other_index].as_value() else {
|
|
||||||
return snap_radii;
|
return snap_radii;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let other_radius = if radius_index == 3 { radius_1 } else { radius_2 };
|
||||||
|
|
||||||
let Some(&TaggedValue::U32(sides)) = node_inputs[1].as_value() else {
|
let Some(&TaggedValue::U32(sides)) = node_inputs[1].as_value() else {
|
||||||
return snap_radii;
|
return snap_radii;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let both_radii_negative = radius_1.is_sign_negative() && radius_2.is_sign_negative();
|
||||||
|
let both_radii_same_sign = (radius_1.signum() * radius_2.signum()).is_sign_positive();
|
||||||
|
|
||||||
|
// When only one of the radii is negative, no need for snapping
|
||||||
|
if !both_radii_same_sign {
|
||||||
|
return snap_radii;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sign = if both_radii_negative { -1. } else { 1. };
|
||||||
|
|
||||||
// Inner radius for 90°
|
// Inner radius for 90°
|
||||||
let b = FRAC_PI_4 * 3. - PI / (sides as f64);
|
let b = FRAC_PI_4 * 3. - PI / (sides as f64);
|
||||||
let angle = b.sin();
|
let angle = b.sin();
|
||||||
let required_radius = (other_radius / angle) * FRAC_1_SQRT_2;
|
let required_radius = (other_radius.abs() * sign / angle) * FRAC_1_SQRT_2;
|
||||||
|
|
||||||
snap_radii.push(required_radius);
|
snap_radii.push(required_radius);
|
||||||
|
|
||||||
// Also push the case when the when it length increases more than the other
|
// Also push the case when the when it length increases more than the other
|
||||||
|
|
||||||
let flipped = other_radius * angle * SQRT_2;
|
let flipped = other_radius.abs() * sign * angle * SQRT_2;
|
||||||
|
|
||||||
snap_radii.push(flipped);
|
snap_radii.push(flipped);
|
||||||
|
|
||||||
|
|
@ -401,11 +379,11 @@ impl PointRadiusHandle {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if other_radius * factor > 1e-6 {
|
if other_radius.abs() * factor > 1e-6 {
|
||||||
snap_radii.push(other_radius * factor);
|
snap_radii.push(other_radius.abs() * sign * factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
snap_radii.push((other_radius * 1.) / factor);
|
snap_radii.push((other_radius.abs() * sign) / factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
snap_radii
|
snap_radii
|
||||||
|
|
@ -441,21 +419,23 @@ impl PointRadiusHandle {
|
||||||
};
|
};
|
||||||
|
|
||||||
let viewport_transform = document.network_interface.document_metadata().transform_to_viewport(layer);
|
let viewport_transform = document.network_interface.document_metadata().transform_to_viewport(layer);
|
||||||
let document_transform = document.network_interface.document_metadata().transform_to_document(layer);
|
|
||||||
let center = viewport_transform.transform_point2(DVec2::ZERO);
|
let center = viewport_transform.transform_point2(DVec2::ZERO);
|
||||||
let radius_index = self.radius_index;
|
let radius_index = self.radius_index;
|
||||||
|
|
||||||
let original_radius = self.initial_radius;
|
let original_radius = self.initial_radius;
|
||||||
|
|
||||||
let delta = viewport_transform.inverse().transform_point2(input.mouse.position) - document_transform.inverse().transform_point2(drag_start);
|
let delta = viewport_transform.inverse().transform_point2(input.mouse.position) - viewport_transform.inverse().transform_point2(drag_start);
|
||||||
let radius = document.metadata().document_to_viewport.transform_point2(drag_start) - center;
|
let radius = drag_start - center;
|
||||||
let projection = delta.project_onto(radius);
|
let projection = delta.project_onto(radius);
|
||||||
let sign = radius.dot(delta).signum();
|
let sign = radius.dot(delta).signum();
|
||||||
|
|
||||||
let mut net_delta = projection.length() * sign;
|
let mut net_delta = projection.length() * sign * original_radius.signum();
|
||||||
let new_radius = original_radius + net_delta;
|
let new_radius = original_radius + net_delta;
|
||||||
|
|
||||||
self.update_state(PointRadiusHandleState::Dragging);
|
self.update_state(PointRadiusHandleState::Dragging);
|
||||||
|
|
||||||
|
self.check_if_radius_flipped(original_radius, new_radius, document, layer, radius_index);
|
||||||
|
|
||||||
if let Some((index, snapped_delta)) = self.check_snapping(new_radius, original_radius) {
|
if let Some((index, snapped_delta)) = self.check_snapping(new_radius, original_radius) {
|
||||||
net_delta = snapped_delta;
|
net_delta = snapped_delta;
|
||||||
self.update_state(PointRadiusHandleState::Snapped(index));
|
self.update_state(PointRadiusHandleState::Snapped(index));
|
||||||
|
|
@ -467,4 +447,23 @@ impl PointRadiusHandle {
|
||||||
});
|
});
|
||||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_if_radius_flipped(&mut self, original_radius: f64, new_radius: f64, document: &DocumentMessageHandler, layer: LayerNodeIdentifier, radius_index: usize) {
|
||||||
|
let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Star") else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let (Some(&TaggedValue::F64(radius_1)), Some(&TaggedValue::F64(radius_2))) = (node_inputs[2].as_value(), node_inputs[3].as_value()) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let other_radius = if radius_index == 3 { radius_1 } else { radius_2 };
|
||||||
|
|
||||||
|
let flipped = (other_radius.is_sign_positive() && original_radius.is_sign_negative() && new_radius.is_sign_positive())
|
||||||
|
|| (other_radius.is_sign_negative() && original_radius.is_sign_positive() && new_radius.is_sign_negative());
|
||||||
|
|
||||||
|
if flipped {
|
||||||
|
self.snap_radii = Self::calculate_snap_radii(document, layer, radius_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ impl ShapeGizmoHandler for PolygonGizmoHandler {
|
||||||
overlay_context: &mut OverlayContext,
|
overlay_context: &mut OverlayContext,
|
||||||
) {
|
) {
|
||||||
self.number_of_points_dial.overlays(document, selected_polygon_layer, shape_editor, mouse_position, overlay_context);
|
self.number_of_points_dial.overlays(document, selected_polygon_layer, shape_editor, mouse_position, overlay_context);
|
||||||
self.point_radius_handle.overlays(selected_polygon_layer, document, input, mouse_position, overlay_context);
|
self.point_radius_handle.overlays(selected_polygon_layer, document, input, overlay_context);
|
||||||
|
|
||||||
polygon_outline(selected_polygon_layer, document, overlay_context);
|
polygon_outline(selected_polygon_layer, document, overlay_context);
|
||||||
}
|
}
|
||||||
|
|
@ -85,7 +85,7 @@ impl ShapeGizmoHandler for PolygonGizmoHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.point_radius_handle.is_dragging_or_snapped() {
|
if self.point_radius_handle.is_dragging_or_snapped() {
|
||||||
self.point_radius_handle.overlays(None, document, input, mouse_position, overlay_context);
|
self.point_radius_handle.overlays(None, document, input, overlay_context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ impl ShapeGizmoHandler for StarGizmoHandler {
|
||||||
overlay_context: &mut OverlayContext,
|
overlay_context: &mut OverlayContext,
|
||||||
) {
|
) {
|
||||||
self.number_of_points_dial.overlays(document, selected_star_layer, shape_editor, mouse_position, overlay_context);
|
self.number_of_points_dial.overlays(document, selected_star_layer, shape_editor, mouse_position, overlay_context);
|
||||||
self.point_radius_handle.overlays(selected_star_layer, document, input, mouse_position, overlay_context);
|
self.point_radius_handle.overlays(selected_star_layer, document, input, overlay_context);
|
||||||
|
|
||||||
star_outline(selected_star_layer, document, overlay_context);
|
star_outline(selected_star_layer, document, overlay_context);
|
||||||
}
|
}
|
||||||
|
|
@ -82,7 +82,7 @@ impl ShapeGizmoHandler for StarGizmoHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.point_radius_handle.is_dragging_or_snapped() {
|
if self.point_radius_handle.is_dragging_or_snapped() {
|
||||||
self.point_radius_handle.overlays(None, document, input, mouse_position, overlay_context);
|
self.point_radius_handle.overlays(None, document, input, overlay_context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -743,7 +743,7 @@ impl Fsm for ShapeToolFsmState {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
(ShapeToolFsmState::ModifyingGizmo, ShapeToolMessage::PointerMove(..)) => {
|
(ShapeToolFsmState::ModifyingGizmo, ShapeToolMessage::PointerMove(..)) => {
|
||||||
tool_data.gizmo_manager.handle_update(tool_data.data.drag_start, document, input, responses);
|
tool_data.gizmo_manager.handle_update(tool_data.data.viewport_drag_start(document), document, input, responses);
|
||||||
|
|
||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue