Skip to content

Commit d1f2a35

Browse files
committed
Merge pull request #104015 from bruvzg/poly_center
[Polygon2DEditor] Add tool to automatically move center of gravity to origin.
2 parents 7b9e7b9 + df321eb commit d1f2a35

File tree

6 files changed

+196
-9
lines changed

6 files changed

+196
-9
lines changed

editor/icons/CurveCenter.svg

Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading

editor/plugins/abstract_polygon_2d_editor.cpp

Lines changed: 175 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,43 @@ bool AbstractPolygon2DEditor::_has_resource() const {
128128
void AbstractPolygon2DEditor::_create_resource() {
129129
}
130130

131+
Vector2 AbstractPolygon2DEditor::_get_geometric_center() const {
132+
int n_polygons = _get_polygon_count();
133+
134+
double cx = 0.0;
135+
double cy = 0.0;
136+
int n_subs = 0;
137+
for (int i = 0; i < n_polygons; i++) {
138+
const Vector<Vector2> &vertices = _get_polygon(i);
139+
Vector<Vector<Point2>> decomp = ::Geometry2D::decompose_polygon_in_convex(vertices);
140+
if (decomp.is_empty()) {
141+
continue;
142+
}
143+
for (const Vector<Vector2> &sub : decomp) {
144+
int sub_n_points = sub.size();
145+
double sub_area2x = 0.0;
146+
double sub_cx = 0.0;
147+
double sub_cy = 0.0;
148+
for (int n = 0; n < sub_n_points; n++) {
149+
int next = (n + 1 < sub_n_points) ? n + 1 : 0;
150+
sub_area2x += (sub[n].x * sub[next].y) - (sub[next].x * sub[n].y);
151+
sub_cx += (sub[n].x + sub[next].x) * (sub[n].x * sub[next].y - sub[next].x * sub[n].y);
152+
sub_cy += (sub[n].y + sub[next].y) * (sub[n].x * sub[next].y - sub[next].x * sub[n].y);
153+
}
154+
sub_cx /= (sub_area2x * 3);
155+
sub_cy /= (sub_area2x * 3);
156+
157+
cx += sub_cx;
158+
cy += sub_cy;
159+
}
160+
n_subs += decomp.size();
161+
}
162+
cx /= n_subs;
163+
cy /= n_subs;
164+
165+
return Vector2(cx, cy);
166+
}
167+
131168
void AbstractPolygon2DEditor::_menu_option(int p_option) {
132169
switch (p_option) {
133170
case MODE_CREATE: {
@@ -150,6 +187,33 @@ void AbstractPolygon2DEditor::_menu_option(int p_option) {
150187
button_edit->set_pressed(false);
151188
button_delete->set_pressed(true);
152189
} break;
190+
case CENTER_POLY: {
191+
_wip_close();
192+
193+
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
194+
undo_redo->create_action(TTR("Move Origin to Geometric Center"));
195+
196+
Vector2 center = _get_geometric_center();
197+
198+
int n_polygons = _get_polygon_count();
199+
for (int i = 0; i < n_polygons; i++) {
200+
const Vector<Vector2> &vertices = _get_polygon(i);
201+
int n_points = vertices.size();
202+
203+
Vector<Vector2> new_vertices;
204+
new_vertices.resize(n_points);
205+
for (int n = 0; n < n_points; n++) {
206+
new_vertices.write[n] = vertices[n] - center;
207+
}
208+
_action_set_polygon(i, vertices, new_vertices);
209+
}
210+
Node2D *node = _get_node();
211+
Vector2 node_pos = node->get_position();
212+
undo_redo->add_do_method(node, "set_position", node_pos + node->get_transform().basis_xform(center));
213+
undo_redo->add_undo_method(node, "set_position", node_pos);
214+
215+
_commit_action();
216+
} break;
153217
}
154218
}
155219

@@ -159,6 +223,7 @@ void AbstractPolygon2DEditor::_notification(int p_what) {
159223
button_create->set_button_icon(get_editor_theme_icon(SNAME("CurveCreate")));
160224
button_edit->set_button_icon(get_editor_theme_icon(SNAME("CurveEdit")));
161225
button_delete->set_button_icon(get_editor_theme_icon(SNAME("CurveDelete")));
226+
button_center->set_button_icon(get_editor_theme_icon(SNAME("CurveCenter")));
162227
} break;
163228

164229
case NOTIFICATION_READY: {
@@ -194,6 +259,7 @@ void AbstractPolygon2DEditor::_wip_cancel() {
194259
edited_point = PosVertex();
195260
hover_point = Vertex();
196261
selected_point = Vertex();
262+
center_drag = false;
197263

198264
canvas_item_editor->update_viewport();
199265
}
@@ -229,6 +295,7 @@ void AbstractPolygon2DEditor::_wip_close() {
229295
edited_point = PosVertex();
230296
hover_point = Vertex();
231297
selected_point = Vertex();
298+
center_drag = false;
232299
}
233300

234301
void AbstractPolygon2DEditor::disable_polygon_editing(bool p_disable, const String &p_reason) {
@@ -237,16 +304,34 @@ void AbstractPolygon2DEditor::disable_polygon_editing(bool p_disable, const Stri
237304
button_create->set_disabled(p_disable);
238305
button_edit->set_disabled(p_disable);
239306
button_delete->set_disabled(p_disable);
307+
button_center->set_disabled(p_disable);
240308

241309
if (p_disable) {
242310
button_create->set_tooltip_text(p_reason);
243311
button_edit->set_tooltip_text(p_reason);
244312
button_delete->set_tooltip_text(p_reason);
313+
button_center->set_tooltip_text(p_reason);
245314
} else {
246-
button_create->set_tooltip_text(TTR("Create points."));
247-
button_edit->set_tooltip_text(TTR("Edit points.\nLMB: Move Point\nRMB: Erase Point"));
248-
button_delete->set_tooltip_text(TTR("Erase points."));
315+
button_create->set_tooltip_text(TTRC("Create points."));
316+
button_edit->set_tooltip_text(TTRC("Edit points.\nLMB: Move Point\nRMB: Erase Point"));
317+
button_delete->set_tooltip_text(TTRC("Erase points."));
318+
button_center->set_tooltip_text(TTRC("Move center of gravity to geometric center."));
319+
}
320+
}
321+
322+
bool AbstractPolygon2DEditor::_commit_drag() {
323+
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
324+
325+
center_drag = false;
326+
int n_polygons = _get_polygon_count();
327+
ERR_FAIL_COND_V(pre_center_move_edit.size() != n_polygons, false);
328+
undo_redo->create_action(TTR("Move Geometric Center"));
329+
for (int i = 0; i < n_polygons; i++) {
330+
_action_set_polygon(i, pre_center_move_edit[i], _get_polygon(i));
249331
}
332+
pre_center_move_edit.clear();
333+
_commit_action();
334+
return true;
250335
}
251336

252337
bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
@@ -408,14 +493,60 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
408493
_wip_cancel();
409494
}
410495
}
496+
497+
// Center drag.
498+
if (edit_origin_and_center) {
499+
real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
500+
501+
if (mb->get_button_index() == MouseButton::LEFT) {
502+
if (mb->is_meta_pressed() || mb->is_ctrl_pressed() || mb->is_shift_pressed() || mb->is_alt_pressed()) {
503+
return false;
504+
}
505+
if (mb->is_pressed() && !center_drag) {
506+
Vector2 center_point = xform.xform(_get_geometric_center());
507+
if ((gpoint - center_point).length() < grab_threshold) {
508+
pre_center_move_edit.clear();
509+
int n_polygons = _get_polygon_count();
510+
for (int i = 0; i < n_polygons; i++) {
511+
pre_center_move_edit.push_back(_get_polygon(i));
512+
}
513+
center_drag_origin = cpoint;
514+
center_drag = true;
515+
return true;
516+
}
517+
} else if (center_drag) {
518+
return _commit_drag();
519+
}
520+
} else if (mb->get_button_index() == MouseButton::RIGHT && center_drag) {
521+
_commit_drag();
522+
}
523+
}
411524
}
412525

413526
Ref<InputEventMouseMotion> mm = p_event;
414527

415528
if (mm.is_valid()) {
416529
Vector2 gpoint = mm->get_position();
417530

418-
if (edited_point.valid() && (wip_active || mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
531+
if (center_drag) {
532+
Vector2 cpoint = canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint));
533+
cpoint = _get_node()->get_screen_transform().affine_inverse().xform(cpoint);
534+
Vector2 delta = center_drag_origin - cpoint;
535+
536+
int n_polygons = _get_polygon_count();
537+
for (int i = 0; i < n_polygons; i++) {
538+
const Vector<Vector2> &vertices = _get_polygon(i);
539+
int n_points = vertices.size();
540+
541+
Vector<Vector2> new_vertices;
542+
new_vertices.resize(n_points);
543+
for (int n = 0; n < n_points; n++) {
544+
new_vertices.write[n] = vertices[n] - delta;
545+
}
546+
_set_polygon(i, new_vertices);
547+
}
548+
center_drag_origin = cpoint;
549+
} else if (edited_point.valid() && (wip_active || mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
419550
Vector2 cpoint = canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint));
420551
cpoint = _get_node()->get_screen_transform().affine_inverse().xform(cpoint);
421552

@@ -512,11 +643,36 @@ void AbstractPolygon2DEditor::forward_canvas_draw_over_viewport(Control *p_overl
512643
Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_screen_transform();
513644
// All polygon points are sharp, so use the sharp handle icon
514645
const Ref<Texture2D> handle = get_editor_theme_icon(SNAME("EditorPathSharpHandle"));
646+
const Ref<Texture2D> nhandle = get_editor_theme_icon(SNAME("EditorPathNullHandle"));
647+
648+
Ref<Font> font = get_theme_font(SNAME("bold"), EditorStringName(EditorFonts));
649+
int font_size = 1.3 * get_theme_font_size(SNAME("bold_size"), EditorStringName(EditorFonts));
650+
const float outline_size = 4 * EDSCALE;
651+
Color font_color = get_theme_color(SceneStringName(font_color), EditorStringName(Editor));
652+
Color outline_color = font_color.inverted();
515653

516654
const Vertex active_point = get_active_point();
517655
const int n_polygons = _get_polygon_count();
518656
const bool is_closed = !_is_line();
519657

658+
if (edit_origin_and_center) {
659+
const Vector2 &center = _get_geometric_center();
660+
if (!center.is_zero_approx()) {
661+
const Vector2 point = xform.xform(center);
662+
p_overlay->draw_texture(nhandle, point - nhandle->get_size() * 0.5, Color(1, 1, 0.4));
663+
Size2 lbl_size = font->get_string_size("c", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size);
664+
p_overlay->draw_string_outline(font, point - lbl_size * 0.5, "c", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color);
665+
p_overlay->draw_string(font, point - lbl_size * 0.5, "c", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color);
666+
}
667+
{
668+
const Vector2 point = xform.xform(Vector2());
669+
p_overlay->draw_texture(nhandle, point - nhandle->get_size() * 0.5, center.is_equal_approx(Vector2()) ? Color(1, 1, 0.4) : Color(1, 0.4, 1));
670+
Size2 lbl_size = font->get_string_size("o", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size);
671+
p_overlay->draw_string_outline(font, point - lbl_size * 0.5, "o", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color);
672+
p_overlay->draw_string(font, point - lbl_size * 0.5, "o", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color);
673+
}
674+
}
675+
520676
for (int j = -1; j < n_polygons; j++) {
521677
if (wip_active && wip_destructive && j != -1) {
522678
continue;
@@ -584,13 +740,8 @@ void AbstractPolygon2DEditor::forward_canvas_draw_over_viewport(Control *p_overl
584740
p_overlay->draw_texture(handle, point - handle->get_size() * 0.5, overlay_modulate);
585741

586742
if (vertex == hover_point) {
587-
Ref<Font> font = get_theme_font(SNAME("bold"), EditorStringName(EditorFonts));
588-
int font_size = 1.3 * get_theme_font_size(SNAME("bold_size"), EditorStringName(EditorFonts));
589743
String num = String::num_int64(vertex.vertex);
590744
Size2 num_size = font->get_string_size(num, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size);
591-
const float outline_size = 4;
592-
Color font_color = get_theme_color(SceneStringName(font_color), EditorStringName(Editor));
593-
Color outline_color = font_color.inverted();
594745
p_overlay->draw_string_outline(font, point - num_size * 0.5, num, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color);
595746
p_overlay->draw_string(font, point - num_size * 0.5, num, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color);
596747
}
@@ -603,6 +754,13 @@ void AbstractPolygon2DEditor::forward_canvas_draw_over_viewport(Control *p_overl
603754
}
604755
}
605756

757+
void AbstractPolygon2DEditor::set_edit_origin_and_center(bool p_enabled) {
758+
edit_origin_and_center = p_enabled;
759+
if (button_center) {
760+
button_center->set_visible(edit_origin_and_center);
761+
}
762+
}
763+
606764
void AbstractPolygon2DEditor::edit(Node *p_polygon) {
607765
if (!canvas_item_editor) {
608766
canvas_item_editor = CanvasItemEditor::get_singleton();
@@ -623,6 +781,7 @@ void AbstractPolygon2DEditor::edit(Node *p_polygon) {
623781
edited_point = PosVertex();
624782
hover_point = Vertex();
625783
selected_point = Vertex();
784+
center_drag = false;
626785
} else {
627786
_set_node(nullptr);
628787
}
@@ -728,6 +887,7 @@ AbstractPolygon2DEditor::PosVertex AbstractPolygon2DEditor::closest_edge_point(c
728887

729888
AbstractPolygon2DEditor::AbstractPolygon2DEditor(bool p_wip_destructive) {
730889
edited_point = PosVertex();
890+
center_drag = false;
731891
wip_destructive = p_wip_destructive;
732892

733893
hover_point = Vertex();
@@ -755,6 +915,12 @@ AbstractPolygon2DEditor::AbstractPolygon2DEditor(bool p_wip_destructive) {
755915
button_delete->connect(SceneStringName(pressed), callable_mp(this, &AbstractPolygon2DEditor::_menu_option).bind(MODE_DELETE));
756916
button_delete->set_toggle_mode(true);
757917

918+
button_center = memnew(Button);
919+
button_center->set_theme_type_variation(SceneStringName(FlatButton));
920+
add_child(button_center);
921+
button_center->connect(SceneStringName(pressed), callable_mp(this, &AbstractPolygon2DEditor::_menu_option).bind(CENTER_POLY));
922+
button_center->set_visible(edit_origin_and_center);
923+
758924
create_resource = memnew(ConfirmationDialog);
759925
add_child(create_resource);
760926
create_resource->set_ok_button_text(TTR("Create"));

editor/plugins/abstract_polygon_2d_editor.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class AbstractPolygon2DEditor : public HBoxContainer {
4444
Button *button_create = nullptr;
4545
Button *button_edit = nullptr;
4646
Button *button_delete = nullptr;
47+
Button *button_center = nullptr;
4748

4849
struct Vertex {
4950
Vertex() {}
@@ -85,6 +86,11 @@ class AbstractPolygon2DEditor : public HBoxContainer {
8586
bool wip_active = false;
8687
bool wip_destructive = false;
8788

89+
Vector<Vector<Vector2>> pre_center_move_edit;
90+
bool center_drag = false;
91+
Vector2 center_drag_origin;
92+
bool edit_origin_and_center = false;
93+
8894
bool _polygon_editing_enabled = false;
8995

9096
CanvasItemEditor *canvas_item_editor = nullptr;
@@ -97,6 +103,7 @@ class AbstractPolygon2DEditor : public HBoxContainer {
97103
MODE_EDIT,
98104
MODE_DELETE,
99105
MODE_CONT,
106+
CENTER_POLY,
100107
};
101108

102109
int mode = MODE_EDIT;
@@ -109,6 +116,8 @@ class AbstractPolygon2DEditor : public HBoxContainer {
109116
void _notification(int p_what);
110117
void _node_removed(Node *p_node);
111118

119+
bool _commit_drag();
120+
112121
void remove_point(const Vertex &p_vertex);
113122
Vertex get_active_point() const;
114123
PosVertex closest_point(const Vector2 &p_pos) const;
@@ -132,6 +141,8 @@ class AbstractPolygon2DEditor : public HBoxContainer {
132141
virtual void _action_set_polygon(int p_idx, const Variant &p_previous, const Variant &p_polygon);
133142
virtual void _commit_action();
134143

144+
virtual Vector2 _get_geometric_center() const;
145+
135146
virtual bool _has_resource() const;
136147
virtual void _create_resource();
137148

@@ -140,6 +151,7 @@ class AbstractPolygon2DEditor : public HBoxContainer {
140151

141152
bool forward_gui_input(const Ref<InputEvent> &p_event);
142153
void forward_canvas_draw_over_viewport(Control *p_overlay);
154+
void set_edit_origin_and_center(bool p_enabled);
143155

144156
void edit(Node *p_polygon);
145157
AbstractPolygon2DEditor(bool p_wip_destructive = true);

editor/plugins/collision_polygon_2d_editor_plugin.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ void CollisionPolygon2DEditor::_set_node(Node *p_polygon) {
3838
node = Object::cast_to<CollisionPolygon2D>(p_polygon);
3939
}
4040

41+
CollisionPolygon2DEditor::CollisionPolygon2DEditor() {
42+
set_edit_origin_and_center(true);
43+
}
44+
4145
CollisionPolygon2DEditorPlugin::CollisionPolygon2DEditorPlugin() :
4246
AbstractPolygon2DEditorPlugin(memnew(CollisionPolygon2DEditor), "CollisionPolygon2D") {
4347
}

editor/plugins/collision_polygon_2d_editor_plugin.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ class CollisionPolygon2DEditor : public AbstractPolygon2DEditor {
4141
protected:
4242
virtual Node2D *_get_node() const override;
4343
virtual void _set_node(Node *p_polygon) override;
44+
45+
public:
46+
CollisionPolygon2DEditor();
4447
};
4548

4649
class CollisionPolygon2DEditorPlugin : public AbstractPolygon2DEditorPlugin {

0 commit comments

Comments
 (0)