Add temporary pivot for rotating multiple 2D nodes

This commit is contained in:
kobewi 2022-02-21 01:22:11 +01:00
parent 11d3768132
commit f8cd3bbc36
2 changed files with 68 additions and 15 deletions

View File

@ -1344,22 +1344,33 @@ bool CanvasItemEditor::_gui_input_pivot(const Ref<InputEvent> &p_event) {
// Drag the pivot (in pivot mode / with V key)
if (drag_type == DRAG_NONE) {
bool move_temp_pivot = ((b.is_valid() && b->is_shift_pressed()) || (k.is_valid() && k->is_shift_pressed()));
if ((b.is_valid() && b->is_pressed() && b->get_button_index() == MouseButton::LEFT && tool == TOOL_EDIT_PIVOT) ||
(k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == Key::V && tool == TOOL_SELECT && k->get_modifiers_mask().is_empty())) {
(k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == Key::V && tool == TOOL_SELECT && (k->get_modifiers_mask().is_empty() || move_temp_pivot))) {
List<CanvasItem *> selection = _get_edited_canvas_items();
// Filters the selection with nodes that allow setting the pivot
drag_selection = List<CanvasItem *>();
for (CanvasItem *ci : selection) {
if (ci->_edit_use_pivot()) {
if (ci->_edit_use_pivot() || move_temp_pivot) {
drag_selection.push_back(ci);
}
}
// Start dragging if we still have nodes
if (drag_selection.size() > 0) {
Vector2 event_pos = (b.is_valid()) ? b->get_position() : viewport->get_local_mouse_position();
if (move_temp_pivot) {
drag_type = DRAG_TEMP_PIVOT;
temp_pivot = transform.affine_inverse().xform(event_pos);
viewport->queue_redraw();
return true;
}
_save_canvas_item_state(drag_selection);
drag_from = transform.affine_inverse().xform((b.is_valid()) ? b->get_position() : viewport->get_local_mouse_position());
drag_from = transform.affine_inverse().xform(event_pos);
Vector2 new_pos;
if (drag_selection.size() == 1) {
new_pos = snap_point(drag_from, SNAP_NODE_SIDES | SNAP_NODE_CENTER | SNAP_NODE_ANCHORS | SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, 0, drag_selection[0]);
@ -1416,6 +1427,20 @@ bool CanvasItemEditor::_gui_input_pivot(const Ref<InputEvent> &p_event) {
return true;
}
}
if (drag_type == DRAG_TEMP_PIVOT) {
if (m.is_valid()) {
temp_pivot = transform.affine_inverse().xform(m->get_position());
viewport->queue_redraw();
return true;
}
if ((b.is_valid() && !b->is_pressed() && b->get_button_index() == MouseButton::LEFT && tool == TOOL_EDIT_PIVOT) ||
(k.is_valid() && !k->is_pressed() && k->get_keycode() == Key::V)) {
drag_type = DRAG_NONE;
return true;
}
}
return false;
}
@ -1441,7 +1466,9 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) {
drag_type = DRAG_ROTATE;
drag_from = transform.affine_inverse().xform(b->get_position());
CanvasItem *ci = drag_selection[0];
if (ci->_edit_use_pivot()) {
if (!Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y)) {
drag_rotation_center = temp_pivot;
} else if (ci->_edit_use_pivot()) {
drag_rotation_center = ci->get_global_transform_with_canvas().xform(ci->_edit_get_pivot());
} else {
drag_rotation_center = ci->get_global_transform_with_canvas().get_origin();
@ -1461,7 +1488,16 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) {
drag_to = transform.affine_inverse().xform(m->get_position());
//Rotate the opposite way if the canvas item's compounded scale has an uneven number of negative elements
bool opposite = (ci->get_global_transform().get_scale().sign().dot(ci->get_transform().get_scale().sign()) == 0);
ci->_edit_set_rotation(snap_angle(ci->_edit_get_rotation() + (opposite ? -1 : 1) * (drag_from - drag_rotation_center).angle_to(drag_to - drag_rotation_center), ci->_edit_get_rotation()));
real_t prev_rotation = ci->_edit_get_rotation();
real_t new_rotation = snap_angle(ci->_edit_get_rotation() + (opposite ? -1 : 1) * (drag_from - drag_rotation_center).angle_to(drag_to - drag_rotation_center), prev_rotation);
ci->_edit_set_rotation(new_rotation);
if (!Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y)) {
Transform2D xform = ci->get_global_transform_with_canvas() * ci->get_transform().affine_inverse();
Vector2 radius = xform.xform(ci->_edit_get_position()) - temp_pivot;
radius = radius.rotated(new_rotation - prev_rotation);
ci->_edit_set_position(xform.affine_inverse().xform(temp_pivot + radius));
}
viewport->queue_redraw();
}
return true;
@ -3161,7 +3197,7 @@ void CanvasItemEditor::_draw_ruler_tool() {
} else {
if (grid_snap_active) {
Ref<Texture2D> position_icon = get_editor_theme_icon(SNAME("EditorPosition"));
viewport->draw_texture(get_editor_theme_icon(SNAME("EditorPosition")), (ruler_tool_origin - view_offset) * zoom - position_icon->get_size() / 2);
viewport->draw_texture(position_icon, (ruler_tool_origin - view_offset) * zoom - position_icon->get_size() / 2);
}
}
}
@ -3583,6 +3619,10 @@ void CanvasItemEditor::_draw_selection() {
get_theme_color(SNAME("accent_color"), EditorStringName(Editor)) * Color(1, 1, 1, 0.6),
Math::round(2 * EDSCALE));
}
if (!Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y)) {
viewport->draw_texture(pivot_icon, (temp_pivot - view_offset) * zoom - (pivot_icon->get_size() / 2).floor(), get_theme_color(SNAME("accent_color"), SNAME("Editor")));
}
}
void CanvasItemEditor::_draw_straight_line(Point2 p_from, Point2 p_to, Color p_color) {
@ -3931,8 +3971,6 @@ void CanvasItemEditor::_notification(int p_what) {
} break;
case NOTIFICATION_PROCESS: {
int nb_having_pivot = 0;
// Update the viewport if the canvas_item changes
List<CanvasItem *> selection = _get_edited_canvas_items(true);
for (CanvasItem *ci : selection) {
@ -3972,14 +4010,10 @@ void CanvasItemEditor::_notification(int p_what) {
viewport->queue_redraw();
}
}
if (ci->_edit_use_pivot()) {
nb_having_pivot++;
}
}
// Activate / Deactivate the pivot tool
pivot_button->set_disabled(nb_having_pivot == 0);
// Activate / Deactivate the pivot tool.
pivot_button->set_disabled(selection.is_empty());
// Update the viewport if bones changes
for (KeyValue<BoneKey, BoneList> &E : bone_list) {
@ -4048,6 +4082,11 @@ void CanvasItemEditor::_selection_changed() {
_reset_drag();
}
selected_from_canvas = false;
if (temp_pivot != Vector2(INFINITY, INFINITY)) {
temp_pivot = Vector2(INFINITY, INFINITY);
viewport->queue_redraw();
}
}
void CanvasItemEditor::edit(CanvasItem *p_canvas_item) {
@ -4202,6 +4241,18 @@ void CanvasItemEditor::_button_tool_select(int p_index) {
tool = (Tool)p_index;
if (p_index == TOOL_EDIT_PIVOT && Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
// Special action that places temporary rotation pivot in the middle of the selection.
List<CanvasItem *> selection = _get_edited_canvas_items();
if (!selection.is_empty()) {
Vector2 center;
for (const CanvasItem *ci : selection) {
center += ci->_edit_get_position();
}
temp_pivot = center / selection.size();
}
}
viewport->queue_redraw();
_update_cursor();
}
@ -5279,7 +5330,7 @@ CanvasItemEditor::CanvasItemEditor() {
main_menu_hbox->add_child(pivot_button);
pivot_button->set_toggle_mode(true);
pivot_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select).bind(TOOL_EDIT_PIVOT));
pivot_button->set_tooltip_text(TTR("Click to change object's rotation pivot."));
pivot_button->set_tooltip_text(TTR("Click to change object's rotation pivot.") + "\n" + TTR("Shift: Set temporary rotation pivot.") + "\n" + TTR("Click this button while holding Shift to put the rotation pivot in the center of the selected nodes."));
pan_button = memnew(Button);
pan_button->set_theme_type_variation("FlatButton");

View File

@ -174,6 +174,7 @@ private:
DRAG_SCALE_BOTH,
DRAG_ROTATE,
DRAG_PIVOT,
DRAG_TEMP_PIVOT,
DRAG_V_GUIDE,
DRAG_H_GUIDE,
DRAG_DOUBLE_GUIDE,
@ -251,6 +252,7 @@ private:
bool key_scale = false;
bool pan_pressed = false;
Vector2 temp_pivot = Vector2(INFINITY, INFINITY);
bool ruler_tool_active = false;
Point2 ruler_tool_origin;