From 8505871a87b59b27acc0912a6dd815231c3b78b1 Mon Sep 17 00:00:00 2001 From: Bernhard Liebl Date: Wed, 27 Dec 2017 09:28:02 +0100 Subject: [PATCH] More exact picking for canvas editor --- core/math/geometry.cpp | 11 +++ core/math/geometry.h | 3 + editor/plugins/canvas_item_editor_plugin.cpp | 8 +- scene/2d/canvas_item.h | 1 + scene/2d/collision_polygon_2d.cpp | 5 ++ scene/2d/collision_polygon_2d.h | 1 + scene/2d/collision_shape_2d.cpp | 8 ++ scene/2d/collision_shape_2d.h | 1 + scene/2d/line_2d.cpp | 26 ++++++ scene/2d/line_2d.h | 3 + scene/2d/navigation_polygon.cpp | 60 ++++++++++++- scene/2d/navigation_polygon.h | 9 ++ scene/2d/path_2d.cpp | 54 +++++++++++- scene/2d/path_2d.h | 3 + scene/2d/polygon_2d.cpp | 7 +- scene/2d/polygon_2d.h | 1 + scene/2d/sprite.cpp | 93 +++++++++++++------- scene/2d/sprite.h | 3 + scene/resources/capsule_shape_2d.cpp | 29 ++++-- scene/resources/capsule_shape_2d.h | 3 + scene/resources/circle_shape_2d.cpp | 6 ++ scene/resources/circle_shape_2d.h | 2 + scene/resources/concave_polygon_shape_2d.cpp | 17 ++++ scene/resources/concave_polygon_shape_2d.h | 2 + scene/resources/convex_polygon_shape_2d.cpp | 5 ++ scene/resources/convex_polygon_shape_2d.h | 2 + scene/resources/segment_shape_2d.cpp | 7 ++ scene/resources/segment_shape_2d.h | 2 + scene/resources/shape_2d.h | 2 + scene/resources/shape_line_2d.cpp | 15 ++++ scene/resources/shape_line_2d.h | 2 + 31 files changed, 345 insertions(+), 46 deletions(-) diff --git a/core/math/geometry.cpp b/core/math/geometry.cpp index 39bd34f03c4..e1f2f91ebd1 100644 --- a/core/math/geometry.cpp +++ b/core/math/geometry.cpp @@ -30,6 +30,17 @@ #include "geometry.h" #include "print_string.h" +bool Geometry::is_point_in_polygon(const Vector2 &p_point, const Vector &p_polygon) { + + Vector indices = Geometry::triangulate_polygon(p_polygon); + for (int j = 0; j + 3 <= indices.size(); j += 3) { + int i1 = indices[j], i2 = indices[j + 1], i3 = indices[j + 2]; + if (Geometry::is_point_in_triangle(p_point, p_polygon[i1], p_polygon[i2], p_polygon[i3])) + return true; + } + return false; +} + void Geometry::MeshData::optimize_vertices() { Map vtx_remap; diff --git a/core/math/geometry.h b/core/math/geometry.h index cd069bd7a3d..6a29a53f8fd 100644 --- a/core/math/geometry.h +++ b/core/math/geometry.h @@ -512,6 +512,9 @@ public: return true; } + + static bool is_point_in_polygon(const Vector2 &p_point, const Vector &p_polygon); + static Vector2 get_closest_point_to_segment_uncapped_2d(const Vector2 &p_point, const Vector2 *p_segment) { Vector2 p = p_point - p_segment[0]; diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index ceb1ec09fcd..dffbf7440e2 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -612,6 +612,7 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no if (Object::cast_to(p_node)) return; + const real_t grab_distance = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8); CanvasItem *c = Object::cast_to(p_node); for (int i = p_node->get_child_count() - 1; i >= 0; i--) { @@ -630,9 +631,12 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no if (c && c->is_visible_in_tree() && !c->has_meta("_edit_lock_") && !Object::cast_to(c)) { Rect2 rect = c->_edit_get_rect(); - Point2 local_pos = (p_parent_xform * p_canvas_xform * c->get_transform()).affine_inverse().xform(p_pos); + Transform2D to_local = (p_parent_xform * p_canvas_xform * c->get_transform()).affine_inverse(); + Point2 local_pos = to_local.xform(p_pos); + const real_t local_grab_distance = (to_local.xform(p_pos + Vector2(grab_distance, 0)) - local_pos).length(); + Rect2 local_pos_rect = Rect2(local_pos, Vector2(0, 0)).grow(local_grab_distance); - if (rect.has_point(local_pos)) { + if (rect.intersects(local_pos_rect) && c->_edit_is_selected_on_click(local_pos, local_grab_distance)) { Node2D *node = Object::cast_to(c); _SelectResult res; diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h index 2384c0f370d..db4ddb445c6 100644 --- a/scene/2d/canvas_item.h +++ b/scene/2d/canvas_item.h @@ -229,6 +229,7 @@ public: // Used to resize/move/select the node virtual void _edit_set_rect(const Rect2 &p_rect){}; virtual Rect2 _edit_get_rect() const { return Rect2(-32, -32, 64, 64); }; + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { return true; } Rect2 _edit_get_item_and_children_rect() const; virtual bool _edit_use_rect() const { return false; }; diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp index 92855299ae6..cf352f0464a 100644 --- a/scene/2d/collision_polygon_2d.cpp +++ b/scene/2d/collision_polygon_2d.cpp @@ -248,6 +248,11 @@ Rect2 CollisionPolygon2D::_edit_get_rect() const { return aabb; } +bool CollisionPolygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + return Geometry::is_point_in_polygon(p_point, Variant(polygon)); +} + String CollisionPolygon2D::get_configuration_warning() const { if (!Object::cast_to(get_parent())) { diff --git a/scene/2d/collision_polygon_2d.h b/scene/2d/collision_polygon_2d.h index 2fb08a4599b..c9e825fffb5 100644 --- a/scene/2d/collision_polygon_2d.h +++ b/scene/2d/collision_polygon_2d.h @@ -70,6 +70,7 @@ public: Vector get_polygon() const; virtual Rect2 _edit_get_rect() const; + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; virtual String get_configuration_warning() const; diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp index f7cb5473e3f..6dcaaea543e 100644 --- a/scene/2d/collision_shape_2d.cpp +++ b/scene/2d/collision_shape_2d.cpp @@ -163,6 +163,14 @@ Rect2 CollisionShape2D::_edit_get_rect() const { return rect; } +bool CollisionShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + if (!shape.is_valid()) + return false; + + return shape->_edit_is_selected_on_click(p_point, p_tolerance); +} + String CollisionShape2D::get_configuration_warning() const { if (!Object::cast_to(get_parent())) { diff --git a/scene/2d/collision_shape_2d.h b/scene/2d/collision_shape_2d.h index 4745c659c86..68c83c615e0 100644 --- a/scene/2d/collision_shape_2d.h +++ b/scene/2d/collision_shape_2d.h @@ -52,6 +52,7 @@ protected: public: virtual Rect2 _edit_get_rect() const; + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; void set_shape(const Ref &p_shape); Ref get_shape() const; diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp index 57a0e154478..d7688acec2f 100644 --- a/scene/2d/line_2d.cpp +++ b/scene/2d/line_2d.cpp @@ -48,6 +48,32 @@ Line2D::Line2D() : _round_precision = 8; } +Rect2 Line2D::_edit_get_rect() const { + + if (_points.size() == 0) + return Rect2(0, 0, 0, 0); + Vector2 d = Vector2(_width, _width); + Rect2 aabb = Rect2(_points[0] - d, 2 * d); + for (int i = 1; i < _points.size(); i++) { + aabb.expand_to(_points[i] - d); + aabb.expand_to(_points[i] + d); + } + return aabb; +} + +bool Line2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + const real_t d = _width / 2 + p_tolerance; + PoolVector::Read points = _points.read(); + for (int i = 0; i < _points.size() - 1; i++) { + Vector2 p = Geometry::get_closest_point_to_segment_2d(p_point, &points[i]); + if (p.distance_to(p_point) <= d) + return true; + } + + return false; +} + void Line2D::set_points(const PoolVector &p_points) { _points = p_points; update(); diff --git a/scene/2d/line_2d.h b/scene/2d/line_2d.h index 6426484f027..d38f1258135 100644 --- a/scene/2d/line_2d.h +++ b/scene/2d/line_2d.h @@ -57,6 +57,9 @@ public: Line2D(); + virtual Rect2 _edit_get_rect() const; + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_points(const PoolVector &p_points); PoolVector get_points() const; diff --git a/scene/2d/navigation_polygon.cpp b/scene/2d/navigation_polygon.cpp index 5a6a5128e66..dc77a74119c 100644 --- a/scene/2d/navigation_polygon.cpp +++ b/scene/2d/navigation_polygon.cpp @@ -35,9 +35,50 @@ #include "thirdparty/misc/triangulator.h" +Rect2 NavigationPolygon::_edit_get_rect() const { + + if (rect_cache_dirty) { + item_rect = Rect2(); + bool first = true; + + for (int i = 0; i < outlines.size(); i++) { + const PoolVector &outline = outlines[i]; + const int outline_size = outline.size(); + if (outline_size < 3) + continue; + PoolVector::Read p = outline.read(); + for (int j = 0; j < outline_size; j++) { + if (first) { + item_rect = Rect2(p[j], Vector2(0, 0)); + first = false; + } else { + item_rect.expand_to(p[j]); + } + } + } + + rect_cache_dirty = false; + } + return item_rect; +} + +bool NavigationPolygon::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + for (int i = 0; i < outlines.size(); i++) { + const PoolVector &outline = outlines[i]; + const int outline_size = outline.size(); + if (outline_size < 3) + continue; + if (Geometry::is_point_in_polygon(p_point, Variant(outline))) + return true; + } + return false; +} + void NavigationPolygon::set_vertices(const PoolVector &p_vertices) { vertices = p_vertices; + rect_cache_dirty = true; } PoolVector NavigationPolygon::get_vertices() const { @@ -70,6 +111,7 @@ void NavigationPolygon::_set_outlines(const Array &p_array) { for (int i = 0; i < p_array.size(); i++) { outlines[i] = p_array[i]; } + rect_cache_dirty = true; } Array NavigationPolygon::_get_outlines() const { @@ -93,6 +135,7 @@ void NavigationPolygon::add_polygon(const Vector &p_polygon) { void NavigationPolygon::add_outline_at_index(const PoolVector &p_outline, int p_index) { outlines.insert(p_index, p_outline); + rect_cache_dirty = true; } int NavigationPolygon::get_polygon_count() const { @@ -112,6 +155,7 @@ void NavigationPolygon::clear_polygons() { void NavigationPolygon::add_outline(const PoolVector &p_outline) { outlines.push_back(p_outline); + rect_cache_dirty = true; } int NavigationPolygon::get_outline_count() const { @@ -122,12 +166,14 @@ int NavigationPolygon::get_outline_count() const { void NavigationPolygon::set_outline(int p_idx, const PoolVector &p_outline) { ERR_FAIL_INDEX(p_idx, outlines.size()); outlines[p_idx] = p_outline; + rect_cache_dirty = true; } void NavigationPolygon::remove_outline(int p_idx) { ERR_FAIL_INDEX(p_idx, outlines.size()); outlines.remove(p_idx); + rect_cache_dirty = true; } PoolVector NavigationPolygon::get_outline(int p_idx) const { @@ -138,6 +184,7 @@ PoolVector NavigationPolygon::get_outline(int p_idx) const { void NavigationPolygon::clear_outlines() { outlines.clear(); + rect_cache_dirty = true; } void NavigationPolygon::make_polygons_from_outlines() { @@ -269,7 +316,8 @@ void NavigationPolygon::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_outlines", "_get_outlines"); } -NavigationPolygon::NavigationPolygon() { +NavigationPolygon::NavigationPolygon() : + rect_cache_dirty(true) { } void NavigationPolygonInstance::set_enabled(bool p_enabled) { @@ -311,6 +359,16 @@ bool NavigationPolygonInstance::is_enabled() const { ///////////////////////////// +Rect2 NavigationPolygonInstance::_edit_get_rect() const { + + return navpoly.is_valid() ? navpoly->_edit_get_rect() : Rect2(); +} + +bool NavigationPolygonInstance::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + return navpoly.is_valid() ? navpoly->_edit_is_selected_on_click(p_point, p_tolerance) : false; +} + void NavigationPolygonInstance::_notification(int p_what) { switch (p_what) { diff --git a/scene/2d/navigation_polygon.h b/scene/2d/navigation_polygon.h index 5576a3abd26..3a7298c3018 100644 --- a/scene/2d/navigation_polygon.h +++ b/scene/2d/navigation_polygon.h @@ -43,6 +43,9 @@ class NavigationPolygon : public Resource { Vector polygons; Vector > outlines; + mutable Rect2 item_rect; + mutable bool rect_cache_dirty; + protected: static void _bind_methods(); @@ -53,6 +56,9 @@ protected: Array _get_outlines() const; public: + Rect2 _edit_get_rect() const; + bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_vertices(const PoolVector &p_vertices); PoolVector get_vertices() const; @@ -93,6 +99,9 @@ protected: static void _bind_methods(); public: + virtual Rect2 _edit_get_rect() const; + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_enabled(bool p_enabled); bool is_enabled() const; diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index 4029ef137b0..9b659a1eb49 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -32,6 +32,51 @@ #include "engine.h" #include "scene/scene_string_names.h" +#ifdef TOOLS_ENABLED +#include "editor/editor_scale.h" +#endif + +Rect2 Path2D::_edit_get_rect() const { + + if (curve->get_point_count() == 0) + return Rect2(0, 0, 0, 0); + + Rect2 aabb = Rect2(curve->get_point_position(0), Vector2(0, 0)); + + for (int i = 0; i < curve->get_point_count(); i++) { + + for (int j = 0; j <= 8; j++) { + + real_t frac = j / 8.0; + Vector2 p = curve->interpolate(i, frac); + aabb.expand_to(p); + } + } + + return aabb; +} + +bool Path2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + for (int i = 0; i < curve->get_point_count(); i++) { + Vector2 s[2]; + s[0] = curve->get_point_position(i); + + for (int j = 1; j <= 8; j++) { + real_t frac = j / 8.0; + s[1] = curve->interpolate(i, frac); + + Vector2 p = Geometry::get_closest_point_to_segment_2d(p_point, s); + if (p.distance_to(p_point) <= p_tolerance) + return true; + + s[0] = s[1]; + } + } + + return false; +} + void Path2D::_notification(int p_what) { if (p_what == NOTIFICATION_DRAW && curve.is_valid()) { @@ -41,6 +86,13 @@ void Path2D::_notification(int p_what) { return; } +#if TOOLS_ENABLED + const float line_width = 2 * EDSCALE; +#else + const float line_width = 2; +#endif + const Color color = Color(0.5, 0.6, 1.0, 0.7); + for (int i = 0; i < curve->get_point_count(); i++) { Vector2 prev_p = curve->get_point_position(i); @@ -49,7 +101,7 @@ void Path2D::_notification(int p_what) { real_t frac = j / 8.0; Vector2 p = curve->interpolate(i, frac); - draw_line(prev_p, p, Color(0.5, 0.6, 1.0, 0.7), 2); + draw_line(prev_p, p, color, line_width); prev_p = p; } } diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h index 88a0abdea99..acc43e48be9 100644 --- a/scene/2d/path_2d.h +++ b/scene/2d/path_2d.h @@ -46,6 +46,9 @@ protected: static void _bind_methods(); public: + virtual Rect2 _edit_get_rect() const; + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_curve(const Ref &p_curve); Ref get_curve() const; diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp index 3f2ad19e515..a8f28078621 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -28,6 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "polygon_2d.h" +#include "core/math/geometry.h" Rect2 Polygon2D::_edit_get_rect() const { @@ -42,13 +43,17 @@ Rect2 Polygon2D::_edit_get_rect() const { else item_rect.expand_to(pos); } - item_rect = item_rect.grow(20); rect_cache_dirty = false; } return item_rect; } +bool Polygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + return Geometry::is_point_in_polygon(p_point, Variant(polygon)); +} + void Polygon2D::_edit_set_pivot(const Point2 &p_pivot) { set_offset(p_pivot); diff --git a/scene/2d/polygon_2d.h b/scene/2d/polygon_2d.h index d09e22f5ffc..12db113191f 100644 --- a/scene/2d/polygon_2d.h +++ b/scene/2d/polygon_2d.h @@ -104,6 +104,7 @@ public: virtual bool _edit_use_pivot() const; virtual Rect2 _edit_get_rect() const; + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; Polygon2D(); }; diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp index df2265aae97..414b726759e 100644 --- a/scene/2d/sprite.cpp +++ b/scene/2d/sprite.cpp @@ -47,6 +47,40 @@ bool Sprite::_edit_use_pivot() const { return true; } +void Sprite::_get_rects(Rect2 &r_src_rect, Rect2 &r_dst_rect, bool &r_filter_clip) const { + + Size2 s; + r_filter_clip = false; + + if (region) { + + s = region_rect.size; + r_src_rect = region_rect; + r_filter_clip = region_filter_clip; + } else { + s = Size2(texture->get_size()); + s = s / Size2(hframes, vframes); + + r_src_rect.size = s; + r_src_rect.position.x += float(frame % hframes) * s.x; + r_src_rect.position.y += float(frame / hframes) * s.y; + } + + Point2 ofs = offset; + if (centered) + ofs -= s / 2; + if (Engine::get_singleton()->get_use_pixel_snap()) { + ofs = ofs.floor(); + } + + r_dst_rect = Rect2(ofs, s); + + if (hflip) + r_dst_rect.size.x = -r_dst_rect.size.x; + if (vflip) + r_dst_rect.size.y = -r_dst_rect.size.y; +} + void Sprite::_notification(int p_what) { switch (p_what) { @@ -63,38 +97,9 @@ void Sprite::_notification(int p_what) { break; */ - Size2 s; - Rect2 src_rect; - bool filter_clip = false; - - if (region) { - - s = region_rect.size; - src_rect = region_rect; - filter_clip = region_filter_clip; - } else { - s = Size2(texture->get_size()); - s = s / Size2(hframes, vframes); - - src_rect.size = s; - src_rect.position.x += float(frame % hframes) * s.x; - src_rect.position.y += float(frame / hframes) * s.y; - } - - Point2 ofs = offset; - if (centered) - ofs -= s / 2; - if (Engine::get_singleton()->get_use_pixel_snap()) { - ofs = ofs.floor(); - } - - Rect2 dst_rect(ofs, s); - - if (hflip) - dst_rect.size.x = -dst_rect.size.x; - if (vflip) - dst_rect.size.y = -dst_rect.size.y; - + Rect2 src_rect, dst_rect; + bool filter_clip; + _get_rects(src_rect, dst_rect, filter_clip); texture->draw_rect_region(ci, dst_rect, src_rect, Color(1, 1, 1), false, normal_map, filter_clip); } break; @@ -257,6 +262,30 @@ int Sprite::get_hframes() const { return hframes; } +bool Sprite::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + if (texture.is_null()) + return false; + + Rect2 src_rect, dst_rect; + bool filter_clip; + _get_rects(src_rect, dst_rect, filter_clip); + + if (!dst_rect.has_point(p_point)) + return false; + + Vector2 q = ((p_point - dst_rect.position) / dst_rect.size) * src_rect.size + src_rect.position; + + Ref image = texture->get_data(); + ERR_FAIL_COND_V(image.is_null(), false); + + image->lock(); + const Color c = image->get_pixel((int)q.x, (int)q.y); + image->unlock(); + + return c.a > 0.01; +} + Rect2 Sprite::_edit_get_rect() const { if (texture.is_null()) diff --git a/scene/2d/sprite.h b/scene/2d/sprite.h index 1bef73c0a59..0e816691a28 100644 --- a/scene/2d/sprite.h +++ b/scene/2d/sprite.h @@ -54,6 +54,8 @@ class Sprite : public Node2D { int vframes; int hframes; + void _get_rects(Rect2 &r_src_rect, Rect2 &r_dst_rect, bool &r_filter_clip) const; + protected: void _notification(int p_what); @@ -65,6 +67,7 @@ public: virtual void _edit_set_pivot(const Point2 &p_pivot); virtual Point2 _edit_get_pivot() const; virtual bool _edit_use_pivot() const; + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; virtual Rect2 _edit_get_rect() const; void set_texture(const Ref &p_texture); diff --git a/scene/resources/capsule_shape_2d.cpp b/scene/resources/capsule_shape_2d.cpp index 3caf12feb88..60c75bca644 100644 --- a/scene/resources/capsule_shape_2d.cpp +++ b/scene/resources/capsule_shape_2d.cpp @@ -32,6 +32,25 @@ #include "servers/physics_2d_server.h" #include "servers/visual_server.h" +Vector CapsuleShape2D::_get_points() const { + + Vector points; + for (int i = 0; i < 24; i++) { + Vector2 ofs = Vector2(0, (i > 6 && i <= 18) ? -get_height() * 0.5 : get_height() * 0.5); + + points.push_back(Vector2(Math::sin(i * Math_PI * 2 / 24.0), Math::cos(i * Math_PI * 2 / 24.0)) * get_radius() + ofs); + if (i == 6 || i == 18) + points.push_back(Vector2(Math::sin(i * Math_PI * 2 / 24.0), Math::cos(i * Math_PI * 2 / 24.0)) * get_radius() - ofs); + } + + return points; +} + +bool CapsuleShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + return Geometry::is_point_in_polygon(p_point, _get_points()); +} + void CapsuleShape2D::_update_shape() { Physics2DServer::get_singleton()->shape_set_data(get_rid(), Vector2(radius, height)); @@ -62,15 +81,7 @@ real_t CapsuleShape2D::get_height() const { void CapsuleShape2D::draw(const RID &p_to_rid, const Color &p_color) { - Vector points; - for (int i = 0; i < 24; i++) { - Vector2 ofs = Vector2(0, (i > 6 && i <= 18) ? -get_height() * 0.5 : get_height() * 0.5); - - points.push_back(Vector2(Math::sin(i * Math_PI * 2 / 24.0), Math::cos(i * Math_PI * 2 / 24.0)) * get_radius() + ofs); - if (i == 6 || i == 18) - points.push_back(Vector2(Math::sin(i * Math_PI * 2 / 24.0), Math::cos(i * Math_PI * 2 / 24.0)) * get_radius() - ofs); - } - + Vector points = _get_points(); Vector col; col.push_back(p_color); VisualServer::get_singleton()->canvas_item_add_polygon(p_to_rid, points, col); diff --git a/scene/resources/capsule_shape_2d.h b/scene/resources/capsule_shape_2d.h index 610f9a4cf99..bdbebd2f80e 100644 --- a/scene/resources/capsule_shape_2d.h +++ b/scene/resources/capsule_shape_2d.h @@ -39,11 +39,14 @@ class CapsuleShape2D : public Shape2D { real_t radius; void _update_shape(); + Vector _get_points() const; protected: static void _bind_methods(); public: + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_height(real_t p_height); real_t get_height() const; diff --git a/scene/resources/circle_shape_2d.cpp b/scene/resources/circle_shape_2d.cpp index bff3ed4d677..381ca07bc18 100644 --- a/scene/resources/circle_shape_2d.cpp +++ b/scene/resources/circle_shape_2d.cpp @@ -31,6 +31,12 @@ #include "servers/physics_2d_server.h" #include "servers/visual_server.h" + +bool CircleShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + return p_point.length() < get_radius() + p_tolerance; +} + void CircleShape2D::_update_shape() { Physics2DServer::get_singleton()->shape_set_data(get_rid(), radius); diff --git a/scene/resources/circle_shape_2d.h b/scene/resources/circle_shape_2d.h index 3ba5ec949c9..2373bd78fee 100644 --- a/scene/resources/circle_shape_2d.h +++ b/scene/resources/circle_shape_2d.h @@ -42,6 +42,8 @@ protected: static void _bind_methods(); public: + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_radius(real_t p_radius); real_t get_radius() const; diff --git a/scene/resources/concave_polygon_shape_2d.cpp b/scene/resources/concave_polygon_shape_2d.cpp index e90046fd287..a6822b19150 100644 --- a/scene/resources/concave_polygon_shape_2d.cpp +++ b/scene/resources/concave_polygon_shape_2d.cpp @@ -32,6 +32,23 @@ #include "servers/physics_2d_server.h" #include "servers/visual_server.h" +bool ConcavePolygonShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + PoolVector s = get_segments(); + int len = s.size(); + if (len == 0 || (len % 2) == 1) + return false; + + PoolVector::Read r = s.read(); + for (int i = 0; i < len; i += 2) { + Vector2 closest = Geometry::get_closest_point_to_segment_2d(p_point, &r[i]); + if (p_point.distance_to(closest) < p_tolerance) + return true; + } + + return false; +} + void ConcavePolygonShape2D::set_segments(const PoolVector &p_segments) { Physics2DServer::get_singleton()->shape_set_data(get_rid(), p_segments); diff --git a/scene/resources/concave_polygon_shape_2d.h b/scene/resources/concave_polygon_shape_2d.h index e766e88a4bb..6d9ee3919c1 100644 --- a/scene/resources/concave_polygon_shape_2d.h +++ b/scene/resources/concave_polygon_shape_2d.h @@ -39,6 +39,8 @@ protected: static void _bind_methods(); public: + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_segments(const PoolVector &p_segments); PoolVector get_segments() const; diff --git a/scene/resources/convex_polygon_shape_2d.cpp b/scene/resources/convex_polygon_shape_2d.cpp index 9c25b6b467b..bf8183d0c7b 100644 --- a/scene/resources/convex_polygon_shape_2d.cpp +++ b/scene/resources/convex_polygon_shape_2d.cpp @@ -33,6 +33,11 @@ #include "servers/physics_2d_server.h" #include "servers/visual_server.h" +bool ConvexPolygonShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + return Geometry::is_point_in_polygon(p_point, points); +} + void ConvexPolygonShape2D::_update_shape() { Physics2DServer::get_singleton()->shape_set_data(get_rid(), points); diff --git a/scene/resources/convex_polygon_shape_2d.h b/scene/resources/convex_polygon_shape_2d.h index 3354199e057..e21ab31d1d2 100644 --- a/scene/resources/convex_polygon_shape_2d.h +++ b/scene/resources/convex_polygon_shape_2d.h @@ -42,6 +42,8 @@ protected: static void _bind_methods(); public: + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_point_cloud(const Vector &p_points); void set_points(const Vector &p_points); Vector get_points() const; diff --git a/scene/resources/segment_shape_2d.cpp b/scene/resources/segment_shape_2d.cpp index e8ef448e238..4022ab17ee5 100644 --- a/scene/resources/segment_shape_2d.cpp +++ b/scene/resources/segment_shape_2d.cpp @@ -32,6 +32,13 @@ #include "servers/physics_2d_server.h" #include "servers/visual_server.h" +bool SegmentShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + Vector2 l[2] = { a, b }; + Vector2 closest = Geometry::get_closest_point_to_segment_2d(p_point, l); + return p_point.distance_to(closest) < p_tolerance; +} + void SegmentShape2D::_update_shape() { Rect2 r; diff --git a/scene/resources/segment_shape_2d.h b/scene/resources/segment_shape_2d.h index eb9c228c835..5942459703b 100644 --- a/scene/resources/segment_shape_2d.h +++ b/scene/resources/segment_shape_2d.h @@ -44,6 +44,8 @@ protected: static void _bind_methods(); public: + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_a(const Vector2 &p_a); void set_b(const Vector2 &p_b); diff --git a/scene/resources/shape_2d.h b/scene/resources/shape_2d.h index fffc7011f15..f624993db20 100644 --- a/scene/resources/shape_2d.h +++ b/scene/resources/shape_2d.h @@ -44,6 +44,8 @@ protected: Shape2D(const RID &p_rid); public: + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { return true; } + void set_custom_solver_bias(real_t p_bias); real_t get_custom_solver_bias() const; diff --git a/scene/resources/shape_line_2d.cpp b/scene/resources/shape_line_2d.cpp index 512ff3bc569..3476b73dffc 100644 --- a/scene/resources/shape_line_2d.cpp +++ b/scene/resources/shape_line_2d.cpp @@ -30,6 +30,21 @@ #include "shape_line_2d.h" #include "servers/physics_2d_server.h" #include "servers/visual_server.h" + +bool LineShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + Vector2 point = get_d() * get_normal(); + Vector2 l[2][2] = { { point - get_normal().tangent() * 100, point + get_normal().tangent() * 100 }, { point, point + get_normal() * 30 } }; + + for (int i = 0; i < 2; i++) { + Vector2 closest = Geometry::get_closest_point_to_segment_2d(p_point, l[i]); + if (p_point.distance_to(closest) < p_tolerance) + return true; + } + + return false; +} + void LineShape2D::_update_shape() { Array arr; diff --git a/scene/resources/shape_line_2d.h b/scene/resources/shape_line_2d.h index 68298b63bea..b74e3b752c2 100644 --- a/scene/resources/shape_line_2d.h +++ b/scene/resources/shape_line_2d.h @@ -44,6 +44,8 @@ protected: static void _bind_methods(); public: + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_normal(const Vector2 &p_normal); void set_d(real_t p_d);