1 /*************************************************************************/
2 /* abstract_polygon_2d_editor.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
10 /* */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the */
13 /* "Software"), to deal in the Software without restriction, including */
14 /* without limitation the rights to use, copy, modify, merge, publish, */
15 /* distribute, sublicense, and/or sell copies of the Software, and to */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions: */
18 /* */
19 /* The above copyright notice and this permission notice shall be */
20 /* included in all copies or substantial portions of the Software. */
21 /* */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29 /*************************************************************************/
30
31 #include "abstract_polygon_2d_editor.h"
32
33 #include "canvas_item_editor_plugin.h"
34 #include "core/os/keyboard.h"
35 #include "editor/editor_scale.h"
36
Vertex()37 AbstractPolygon2DEditor::Vertex::Vertex() :
38 polygon(-1),
39 vertex(-1) {
40 // invalid vertex
41 }
42
Vertex(int p_vertex)43 AbstractPolygon2DEditor::Vertex::Vertex(int p_vertex) :
44 polygon(-1),
45 vertex(p_vertex) {
46 // vertex p_vertex of current wip polygon
47 }
48
Vertex(int p_polygon,int p_vertex)49 AbstractPolygon2DEditor::Vertex::Vertex(int p_polygon, int p_vertex) :
50 polygon(p_polygon),
51 vertex(p_vertex) {
52 // vertex p_vertex of polygon p_polygon
53 }
54
operator ==(const AbstractPolygon2DEditor::Vertex & p_vertex) const55 bool AbstractPolygon2DEditor::Vertex::operator==(const AbstractPolygon2DEditor::Vertex &p_vertex) const {
56
57 return polygon == p_vertex.polygon && vertex == p_vertex.vertex;
58 }
59
operator !=(const AbstractPolygon2DEditor::Vertex & p_vertex) const60 bool AbstractPolygon2DEditor::Vertex::operator!=(const AbstractPolygon2DEditor::Vertex &p_vertex) const {
61
62 return !(*this == p_vertex);
63 }
64
valid() const65 bool AbstractPolygon2DEditor::Vertex::valid() const {
66
67 return vertex >= 0;
68 }
69
PosVertex()70 AbstractPolygon2DEditor::PosVertex::PosVertex() {
71 // invalid vertex
72 }
73
PosVertex(const Vertex & p_vertex,const Vector2 & p_pos)74 AbstractPolygon2DEditor::PosVertex::PosVertex(const Vertex &p_vertex, const Vector2 &p_pos) :
75 Vertex(p_vertex.polygon, p_vertex.vertex),
76 pos(p_pos) {
77 }
78
PosVertex(int p_polygon,int p_vertex,const Vector2 & p_pos)79 AbstractPolygon2DEditor::PosVertex::PosVertex(int p_polygon, int p_vertex, const Vector2 &p_pos) :
80 Vertex(p_polygon, p_vertex),
81 pos(p_pos) {
82 }
83
_is_empty() const84 bool AbstractPolygon2DEditor::_is_empty() const {
85
86 if (!_get_node())
87 return true;
88
89 const int n = _get_polygon_count();
90
91 for (int i = 0; i < n; i++) {
92
93 Vector<Vector2> vertices = _get_polygon(i);
94
95 if (vertices.size() != 0)
96 return false;
97 }
98
99 return true;
100 }
101
_is_line() const102 bool AbstractPolygon2DEditor::_is_line() const {
103
104 return false;
105 }
106
_has_uv() const107 bool AbstractPolygon2DEditor::_has_uv() const {
108
109 return false;
110 }
111
_get_polygon_count() const112 int AbstractPolygon2DEditor::_get_polygon_count() const {
113
114 return 1;
115 }
116
_get_polygon(int p_idx) const117 Variant AbstractPolygon2DEditor::_get_polygon(int p_idx) const {
118
119 return _get_node()->get("polygon");
120 }
121
_set_polygon(int p_idx,const Variant & p_polygon) const122 void AbstractPolygon2DEditor::_set_polygon(int p_idx, const Variant &p_polygon) const {
123
124 _get_node()->set("polygon", p_polygon);
125 }
126
_action_set_polygon(int p_idx,const Variant & p_previous,const Variant & p_polygon)127 void AbstractPolygon2DEditor::_action_set_polygon(int p_idx, const Variant &p_previous, const Variant &p_polygon) {
128
129 Node2D *node = _get_node();
130 undo_redo->add_do_method(node, "set_polygon", p_polygon);
131 undo_redo->add_undo_method(node, "set_polygon", p_previous);
132 }
133
_get_offset(int p_idx) const134 Vector2 AbstractPolygon2DEditor::_get_offset(int p_idx) const {
135
136 return Vector2(0, 0);
137 }
138
_commit_action()139 void AbstractPolygon2DEditor::_commit_action() {
140
141 undo_redo->add_do_method(canvas_item_editor, "update_viewport");
142 undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
143 undo_redo->commit_action();
144 }
145
_action_add_polygon(const Variant & p_polygon)146 void AbstractPolygon2DEditor::_action_add_polygon(const Variant &p_polygon) {
147
148 _action_set_polygon(0, p_polygon);
149 }
150
_action_remove_polygon(int p_idx)151 void AbstractPolygon2DEditor::_action_remove_polygon(int p_idx) {
152
153 _action_set_polygon(p_idx, _get_polygon(p_idx), PoolVector<Vector2>());
154 }
155
_action_set_polygon(int p_idx,const Variant & p_polygon)156 void AbstractPolygon2DEditor::_action_set_polygon(int p_idx, const Variant &p_polygon) {
157
158 _action_set_polygon(p_idx, _get_polygon(p_idx), p_polygon);
159 }
160
_has_resource() const161 bool AbstractPolygon2DEditor::_has_resource() const {
162
163 return true;
164 }
165
_create_resource()166 void AbstractPolygon2DEditor::_create_resource() {
167 }
168
_menu_option(int p_option)169 void AbstractPolygon2DEditor::_menu_option(int p_option) {
170
171 switch (p_option) {
172
173 case MODE_CREATE: {
174
175 mode = MODE_CREATE;
176 button_create->set_pressed(true);
177 button_edit->set_pressed(false);
178 button_delete->set_pressed(false);
179 } break;
180 case MODE_EDIT: {
181
182 _wip_close();
183 mode = MODE_EDIT;
184 button_create->set_pressed(false);
185 button_edit->set_pressed(true);
186 button_delete->set_pressed(false);
187 } break;
188 case MODE_DELETE: {
189
190 _wip_close();
191 mode = MODE_DELETE;
192 button_create->set_pressed(false);
193 button_edit->set_pressed(false);
194 button_delete->set_pressed(true);
195 } break;
196 }
197 }
198
_notification(int p_what)199 void AbstractPolygon2DEditor::_notification(int p_what) {
200
201 switch (p_what) {
202
203 case NOTIFICATION_READY: {
204
205 disable_polygon_editing(false, String());
206
207 button_create->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveCreate", "EditorIcons"));
208 button_edit->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveEdit", "EditorIcons"));
209 button_delete->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveDelete", "EditorIcons"));
210 button_edit->set_pressed(true);
211
212 get_tree()->connect("node_removed", this, "_node_removed");
213 create_resource->connect("confirmed", this, "_create_resource");
214 } break;
215 }
216 }
217
_node_removed(Node * p_node)218 void AbstractPolygon2DEditor::_node_removed(Node *p_node) {
219
220 if (p_node == _get_node()) {
221 edit(NULL);
222 hide();
223
224 canvas_item_editor->update_viewport();
225 }
226 }
227
_wip_changed()228 void AbstractPolygon2DEditor::_wip_changed() {
229
230 if (wip_active && _is_line()) {
231 _set_polygon(0, wip);
232 }
233 }
234
_wip_cancel()235 void AbstractPolygon2DEditor::_wip_cancel() {
236
237 wip.clear();
238 wip_active = false;
239
240 edited_point = PosVertex();
241 hover_point = Vertex();
242 selected_point = Vertex();
243
244 canvas_item_editor->update_viewport();
245 }
246
_wip_close()247 void AbstractPolygon2DEditor::_wip_close() {
248 if (!wip_active)
249 return;
250
251 if (_is_line()) {
252
253 _set_polygon(0, wip);
254 } else if (wip.size() >= (_is_line() ? 2 : 3)) {
255
256 undo_redo->create_action(TTR("Create Polygon"));
257 _action_add_polygon(wip);
258 if (_has_uv()) {
259 undo_redo->add_do_method(_get_node(), "set_uv", PoolVector<Vector2>());
260 undo_redo->add_undo_method(_get_node(), "set_uv", _get_node()->get("uv"));
261 }
262 _commit_action();
263 } else {
264
265 return;
266 }
267
268 mode = MODE_EDIT;
269 button_edit->set_pressed(true);
270 button_create->set_pressed(false);
271 button_delete->set_pressed(false);
272
273 wip.clear();
274 wip_active = false;
275
276 edited_point = PosVertex();
277 hover_point = Vertex();
278 selected_point = Vertex();
279 }
280
disable_polygon_editing(bool p_disable,String p_reason)281 void AbstractPolygon2DEditor::disable_polygon_editing(bool p_disable, String p_reason) {
282
283 _polygon_editing_enabled = !p_disable;
284
285 button_create->set_disabled(p_disable);
286 button_edit->set_disabled(p_disable);
287 button_delete->set_disabled(p_disable);
288
289 if (p_disable) {
290
291 button_create->set_tooltip(p_reason);
292 button_edit->set_tooltip(p_reason);
293 button_delete->set_tooltip(p_reason);
294 } else {
295
296 button_create->set_tooltip(TTR("Create points."));
297 button_edit->set_tooltip(TTR("Edit points.\nLMB: Move Point\nRMB: Erase Point"));
298 button_delete->set_tooltip(TTR("Erase points."));
299 }
300 }
301
forward_gui_input(const Ref<InputEvent> & p_event)302 bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
303
304 if (!_get_node() || !_polygon_editing_enabled)
305 return false;
306
307 Ref<InputEventMouseButton> mb = p_event;
308
309 if (!_has_resource()) {
310
311 if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed()) {
312 create_resource->set_text(String("No polygon resource on this node.\nCreate and assign one?"));
313 create_resource->popup_centered_minsize();
314 }
315 return (mb.is_valid() && mb->get_button_index() == 1);
316 }
317
318 CanvasItemEditor::Tool tool = CanvasItemEditor::get_singleton()->get_current_tool();
319 if (tool != CanvasItemEditor::TOOL_SELECT)
320 return false;
321
322 if (mb.is_valid()) {
323
324 Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_global_transform();
325
326 Vector2 gpoint = mb->get_position();
327 Vector2 cpoint = _get_node()->get_global_transform().affine_inverse().xform(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mb->get_position())));
328
329 if (mode == MODE_EDIT || (_is_line() && mode == MODE_CREATE)) {
330 if (mb->get_button_index() == BUTTON_LEFT) {
331 if (mb->is_pressed()) {
332 if (mb->get_control() || mb->get_shift() || mb->get_alt())
333 return false;
334
335 const PosVertex insert = closest_edge_point(gpoint);
336
337 if (insert.valid()) {
338
339 Vector<Vector2> vertices = _get_polygon(insert.polygon);
340
341 if (vertices.size() < (_is_line() ? 2 : 3)) {
342
343 vertices.push_back(cpoint);
344 undo_redo->create_action(TTR("Edit Polygon"));
345 selected_point = Vertex(insert.polygon, vertices.size());
346 _action_set_polygon(insert.polygon, vertices);
347 _commit_action();
348 return true;
349 } else {
350
351 Vector<Vector2> vertices2 = _get_polygon(insert.polygon);
352 pre_move_edit = vertices2;
353 edited_point = PosVertex(insert.polygon, insert.vertex + 1, xform.affine_inverse().xform(insert.pos));
354 vertices2.insert(edited_point.vertex, edited_point.pos);
355 selected_point = edited_point;
356 edge_point = PosVertex();
357
358 undo_redo->create_action(TTR("Insert Point"));
359 _action_set_polygon(insert.polygon, vertices2);
360 _commit_action();
361 return true;
362 }
363 } else {
364
365 //look for points to move
366 const PosVertex closest = closest_point(gpoint);
367
368 if (closest.valid()) {
369
370 pre_move_edit = _get_polygon(closest.polygon);
371 edited_point = PosVertex(closest, xform.affine_inverse().xform(closest.pos));
372 selected_point = closest;
373 edge_point = PosVertex();
374 canvas_item_editor->update_viewport();
375 return true;
376 } else {
377
378 selected_point = Vertex();
379 }
380 }
381 } else {
382
383 if (edited_point.valid()) {
384
385 //apply
386
387 Vector<Vector2> vertices = _get_polygon(edited_point.polygon);
388 ERR_FAIL_INDEX_V(edited_point.vertex, vertices.size(), false);
389 vertices.write[edited_point.vertex] = edited_point.pos - _get_offset(edited_point.polygon);
390
391 undo_redo->create_action(TTR("Edit Polygon"));
392 _action_set_polygon(edited_point.polygon, pre_move_edit, vertices);
393 _commit_action();
394
395 edited_point = PosVertex();
396 return true;
397 }
398 }
399 } else if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed() && !edited_point.valid()) {
400
401 const PosVertex closest = closest_point(gpoint);
402
403 if (closest.valid()) {
404
405 remove_point(closest);
406 return true;
407 }
408 }
409 } else if (mode == MODE_DELETE) {
410
411 if (mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) {
412
413 const PosVertex closest = closest_point(gpoint);
414
415 if (closest.valid()) {
416
417 remove_point(closest);
418 return true;
419 }
420 }
421 }
422
423 if (mode == MODE_CREATE) {
424
425 if (mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) {
426
427 if (_is_line()) {
428
429 // for lines, we don't have a wip mode, and we can undo each single add point.
430 Vector<Vector2> vertices = _get_polygon(0);
431 vertices.push_back(cpoint);
432 undo_redo->create_action(TTR("Insert Point"));
433 _action_set_polygon(0, vertices);
434 _commit_action();
435 return true;
436 } else if (!wip_active) {
437
438 wip.clear();
439 wip.push_back(cpoint);
440 wip_active = true;
441 _wip_changed();
442 edited_point = PosVertex(-1, 1, cpoint);
443 canvas_item_editor->update_viewport();
444 hover_point = Vertex();
445 selected_point = Vertex(0);
446 edge_point = PosVertex();
447 return true;
448 } else {
449
450 const real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius");
451
452 if (!_is_line() && wip.size() > 1 && xform.xform(wip[0]).distance_to(xform.xform(cpoint)) < grab_threshold) {
453 //wip closed
454 _wip_close();
455
456 return true;
457 } else {
458
459 //add wip point
460 wip.push_back(cpoint);
461 _wip_changed();
462 edited_point = PosVertex(-1, wip.size(), cpoint);
463 selected_point = Vertex(wip.size() - 1);
464 canvas_item_editor->update_viewport();
465 return true;
466 }
467 }
468 } else if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed() && wip_active) {
469 _wip_cancel();
470 }
471 }
472 }
473
474 Ref<InputEventMouseMotion> mm = p_event;
475
476 if (mm.is_valid()) {
477
478 Vector2 gpoint = mm->get_position();
479
480 if (edited_point.valid() && (wip_active || (mm->get_button_mask() & BUTTON_MASK_LEFT))) {
481
482 Vector2 cpoint = _get_node()->get_global_transform().affine_inverse().xform(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint)));
483
484 //Move the point in a single axis. Should only work when editing a polygon and while holding shift.
485 if (mode == MODE_EDIT && mm->get_shift()) {
486 Vector2 old_point = pre_move_edit.get(selected_point.vertex);
487 if (ABS(cpoint.x - old_point.x) > ABS(cpoint.y - old_point.y)) {
488 cpoint.y = old_point.y;
489 } else {
490 cpoint.x = old_point.x;
491 }
492 }
493
494 edited_point = PosVertex(edited_point, cpoint);
495
496 if (!wip_active) {
497
498 Vector<Vector2> vertices = _get_polygon(edited_point.polygon);
499 ERR_FAIL_INDEX_V(edited_point.vertex, vertices.size(), false);
500 vertices.write[edited_point.vertex] = cpoint - _get_offset(edited_point.polygon);
501 _set_polygon(edited_point.polygon, vertices);
502 }
503
504 canvas_item_editor->update_viewport();
505 } else if (mode == MODE_EDIT || (_is_line() && mode == MODE_CREATE)) {
506
507 const PosVertex onEdgeVertex = closest_edge_point(gpoint);
508
509 if (onEdgeVertex.valid()) {
510
511 hover_point = Vertex();
512 edge_point = onEdgeVertex;
513 canvas_item_editor->update_viewport();
514 } else {
515
516 if (edge_point.valid()) {
517
518 edge_point = PosVertex();
519 canvas_item_editor->update_viewport();
520 }
521
522 const PosVertex new_hover_point = closest_point(gpoint);
523 if (hover_point != new_hover_point) {
524
525 hover_point = new_hover_point;
526 canvas_item_editor->update_viewport();
527 }
528 }
529 }
530 }
531
532 Ref<InputEventKey> k = p_event;
533
534 if (k.is_valid() && k->is_pressed()) {
535
536 if (k->get_scancode() == KEY_DELETE || k->get_scancode() == KEY_BACKSPACE) {
537
538 if (wip_active && selected_point.polygon == -1) {
539
540 if (wip.size() > selected_point.vertex) {
541
542 wip.remove(selected_point.vertex);
543 _wip_changed();
544 selected_point = wip.size() - 1;
545 canvas_item_editor->update_viewport();
546 return true;
547 }
548 } else {
549
550 const Vertex active_point = get_active_point();
551
552 if (active_point.valid()) {
553
554 remove_point(active_point);
555 return true;
556 }
557 }
558 } else if (wip_active && k->get_scancode() == KEY_ENTER) {
559
560 _wip_close();
561 } else if (wip_active && k->get_scancode() == KEY_ESCAPE) {
562 _wip_cancel();
563 }
564 }
565
566 return false;
567 }
568
forward_canvas_draw_over_viewport(Control * p_overlay)569 void AbstractPolygon2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
570
571 if (!_get_node())
572 return;
573
574 Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_global_transform();
575 // All polygon points are sharp, so use the sharp handle icon
576 const Ref<Texture> handle = get_icon("EditorPathSharpHandle", "EditorIcons");
577
578 const Vertex active_point = get_active_point();
579 const int n_polygons = _get_polygon_count();
580 const bool is_closed = !_is_line();
581
582 for (int j = -1; j < n_polygons; j++) {
583
584 if (wip_active && wip_destructive && j != -1)
585 continue;
586
587 PoolVector<Vector2> points;
588 Vector2 offset;
589
590 if (wip_active && j == edited_point.polygon) {
591
592 points = Variant(wip);
593 offset = Vector2(0, 0);
594 } else {
595
596 if (j == -1)
597 continue;
598 points = _get_polygon(j);
599 offset = _get_offset(j);
600 }
601
602 if (!wip_active && j == edited_point.polygon && EDITOR_GET("editors/poly_editor/show_previous_outline")) {
603
604 const Color col = Color(0.5, 0.5, 0.5); // FIXME polygon->get_outline_color();
605 const int n = pre_move_edit.size();
606 for (int i = 0; i < n - (is_closed ? 0 : 1); i++) {
607
608 Vector2 p, p2;
609 p = pre_move_edit[i] + offset;
610 p2 = pre_move_edit[(i + 1) % n] + offset;
611
612 Vector2 point = xform.xform(p);
613 Vector2 next_point = xform.xform(p2);
614
615 p_overlay->draw_line(point, next_point, col, Math::round(2 * EDSCALE), true);
616 }
617 }
618
619 const int n_points = points.size();
620 const Color col = Color(1, 0.3, 0.1, 0.8);
621
622 for (int i = 0; i < n_points; i++) {
623
624 const Vertex vertex(j, i);
625
626 const Vector2 p = (vertex == edited_point) ? edited_point.pos : (points[i] + offset);
627 const Vector2 point = xform.xform(p);
628
629 if (is_closed || i < n_points - 1) {
630
631 Vector2 p2;
632 if (j == edited_point.polygon &&
633 ((wip_active && i == n_points - 1) || (((i + 1) % n_points) == edited_point.vertex)))
634 p2 = edited_point.pos;
635 else
636 p2 = points[(i + 1) % n_points] + offset;
637
638 const Vector2 next_point = xform.xform(p2);
639 p_overlay->draw_line(point, next_point, col, Math::round(2 * EDSCALE), true);
640 }
641 }
642
643 for (int i = 0; i < n_points; i++) {
644
645 const Vertex vertex(j, i);
646
647 const Vector2 p = (vertex == edited_point) ? edited_point.pos : (points[i] + offset);
648 const Vector2 point = xform.xform(p);
649
650 const Color modulate = vertex == active_point ? Color(0.5, 1, 2) : Color(1, 1, 1);
651 p_overlay->draw_texture(handle, point - handle->get_size() * 0.5, modulate);
652
653 if (vertex == hover_point) {
654 Ref<Font> font = get_font("font", "Label");
655 String num = String::num(vertex.vertex);
656 Size2 num_size = font->get_string_size(num);
657 p_overlay->draw_string(font, point - num_size * 0.5, num, Color(1.0, 1.0, 1.0, 0.5));
658 }
659 }
660 }
661
662 if (edge_point.valid()) {
663
664 Ref<Texture> add_handle = get_icon("EditorHandleAdd", "EditorIcons");
665 p_overlay->draw_texture(add_handle, edge_point.pos - add_handle->get_size() * 0.5);
666 }
667 }
668
edit(Node * p_polygon)669 void AbstractPolygon2DEditor::edit(Node *p_polygon) {
670
671 if (!canvas_item_editor)
672 canvas_item_editor = CanvasItemEditor::get_singleton();
673
674 if (p_polygon) {
675
676 _set_node(p_polygon);
677
678 // Enable the pencil tool if the polygon is empty.
679 if (_is_empty())
680 _menu_option(MODE_CREATE);
681 else
682 _menu_option(MODE_EDIT);
683
684 wip.clear();
685 wip_active = false;
686 edited_point = PosVertex();
687 hover_point = Vertex();
688 selected_point = Vertex();
689
690 canvas_item_editor->update_viewport();
691 } else {
692
693 _set_node(NULL);
694 }
695 }
696
_bind_methods()697 void AbstractPolygon2DEditor::_bind_methods() {
698
699 ClassDB::bind_method(D_METHOD("_node_removed"), &AbstractPolygon2DEditor::_node_removed);
700 ClassDB::bind_method(D_METHOD("_menu_option"), &AbstractPolygon2DEditor::_menu_option);
701 ClassDB::bind_method(D_METHOD("_create_resource"), &AbstractPolygon2DEditor::_create_resource);
702 }
703
remove_point(const Vertex & p_vertex)704 void AbstractPolygon2DEditor::remove_point(const Vertex &p_vertex) {
705
706 PoolVector<Vector2> vertices = _get_polygon(p_vertex.polygon);
707
708 if (vertices.size() > (_is_line() ? 2 : 3)) {
709
710 vertices.remove(p_vertex.vertex);
711
712 undo_redo->create_action(TTR("Edit Polygon (Remove Point)"));
713 _action_set_polygon(p_vertex.polygon, vertices);
714 _commit_action();
715 } else {
716
717 undo_redo->create_action(TTR("Remove Polygon And Point"));
718 _action_remove_polygon(p_vertex.polygon);
719 _commit_action();
720 }
721
722 if (_is_empty())
723 _menu_option(MODE_CREATE);
724
725 hover_point = Vertex();
726 if (selected_point == p_vertex)
727 selected_point = Vertex();
728 }
729
get_active_point() const730 AbstractPolygon2DEditor::Vertex AbstractPolygon2DEditor::get_active_point() const {
731
732 return hover_point.valid() ? hover_point : selected_point;
733 }
734
closest_point(const Vector2 & p_pos) const735 AbstractPolygon2DEditor::PosVertex AbstractPolygon2DEditor::closest_point(const Vector2 &p_pos) const {
736
737 const real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius");
738
739 const int n_polygons = _get_polygon_count();
740 const Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_global_transform();
741
742 PosVertex closest;
743 real_t closest_dist = 1e10;
744
745 for (int j = 0; j < n_polygons; j++) {
746
747 PoolVector<Vector2> points = _get_polygon(j);
748 const Vector2 offset = _get_offset(j);
749 const int n_points = points.size();
750
751 for (int i = 0; i < n_points; i++) {
752
753 Vector2 cp = xform.xform(points[i] + offset);
754
755 real_t d = cp.distance_to(p_pos);
756 if (d < closest_dist && d < grab_threshold) {
757 closest_dist = d;
758 closest = PosVertex(j, i, cp);
759 }
760 }
761 }
762
763 return closest;
764 }
765
closest_edge_point(const Vector2 & p_pos) const766 AbstractPolygon2DEditor::PosVertex AbstractPolygon2DEditor::closest_edge_point(const Vector2 &p_pos) const {
767
768 const real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius");
769 const real_t eps = grab_threshold * 2;
770 const real_t eps2 = eps * eps;
771
772 const int n_polygons = _get_polygon_count();
773 const Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_global_transform();
774
775 PosVertex closest;
776 real_t closest_dist = 1e10;
777
778 for (int j = 0; j < n_polygons; j++) {
779
780 PoolVector<Vector2> points = _get_polygon(j);
781 const Vector2 offset = _get_offset(j);
782 const int n_points = points.size();
783 const int n_segments = n_points - (_is_line() ? 1 : 0);
784
785 for (int i = 0; i < n_segments; i++) {
786
787 Vector2 segment[2] = { xform.xform(points[i] + offset),
788 xform.xform(points[(i + 1) % n_points] + offset) };
789
790 Vector2 cp = Geometry::get_closest_point_to_segment_2d(p_pos, segment);
791
792 if (cp.distance_squared_to(segment[0]) < eps2 || cp.distance_squared_to(segment[1]) < eps2)
793 continue; //not valid to reuse point
794
795 real_t d = cp.distance_to(p_pos);
796 if (d < closest_dist && d < grab_threshold) {
797 closest_dist = d;
798 closest = PosVertex(j, i, cp);
799 }
800 }
801 }
802
803 return closest;
804 }
805
AbstractPolygon2DEditor(EditorNode * p_editor,bool p_wip_destructive)806 AbstractPolygon2DEditor::AbstractPolygon2DEditor(EditorNode *p_editor, bool p_wip_destructive) {
807
808 canvas_item_editor = NULL;
809 editor = p_editor;
810 undo_redo = EditorNode::get_undo_redo();
811
812 wip_active = false;
813 edited_point = PosVertex();
814 wip_destructive = p_wip_destructive;
815
816 hover_point = Vertex();
817 selected_point = Vertex();
818 edge_point = PosVertex();
819
820 add_child(memnew(VSeparator));
821 button_create = memnew(ToolButton);
822 add_child(button_create);
823 button_create->connect("pressed", this, "_menu_option", varray(MODE_CREATE));
824 button_create->set_toggle_mode(true);
825
826 button_edit = memnew(ToolButton);
827 add_child(button_edit);
828 button_edit->connect("pressed", this, "_menu_option", varray(MODE_EDIT));
829 button_edit->set_toggle_mode(true);
830
831 button_delete = memnew(ToolButton);
832 add_child(button_delete);
833 button_delete->connect("pressed", this, "_menu_option", varray(MODE_DELETE));
834 button_delete->set_toggle_mode(true);
835
836 create_resource = memnew(ConfirmationDialog);
837 add_child(create_resource);
838 create_resource->get_ok()->set_text(TTR("Create"));
839
840 mode = MODE_EDIT;
841 }
842
edit(Object * p_object)843 void AbstractPolygon2DEditorPlugin::edit(Object *p_object) {
844
845 polygon_editor->edit(Object::cast_to<Node>(p_object));
846 }
847
handles(Object * p_object) const848 bool AbstractPolygon2DEditorPlugin::handles(Object *p_object) const {
849
850 return p_object->is_class(klass);
851 }
852
make_visible(bool p_visible)853 void AbstractPolygon2DEditorPlugin::make_visible(bool p_visible) {
854
855 if (p_visible) {
856
857 polygon_editor->show();
858 } else {
859
860 polygon_editor->hide();
861 polygon_editor->edit(NULL);
862 }
863 }
864
AbstractPolygon2DEditorPlugin(EditorNode * p_node,AbstractPolygon2DEditor * p_polygon_editor,String p_class)865 AbstractPolygon2DEditorPlugin::AbstractPolygon2DEditorPlugin(EditorNode *p_node, AbstractPolygon2DEditor *p_polygon_editor, String p_class) :
866 polygon_editor(p_polygon_editor),
867 editor(p_node),
868 klass(p_class) {
869 CanvasItemEditor::get_singleton()->add_control_to_menu_panel(polygon_editor);
870 polygon_editor->hide();
871 }
872
~AbstractPolygon2DEditorPlugin()873 AbstractPolygon2DEditorPlugin::~AbstractPolygon2DEditorPlugin() {
874 }
875