Răsfoiți Sursa

Merge pull request #15093 from poke1024/canvas-editor-select

More exact picking for canvas editor
Rémi Verschelde 7 ani în urmă
părinte
comite
459ee51338

+ 11 - 0
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<Vector2> &p_polygon) {
+
+	Vector<int> 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<int, int> vtx_remap;

+ 3 - 0
core/math/geometry.h

@@ -512,6 +512,9 @@ public:
 
 		return true;
 	}
+
+	static bool is_point_in_polygon(const Vector2 &p_point, const Vector<Vector2> &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];

+ 6 - 2
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<Viewport>(p_node))
 		return;
 
+	const real_t grab_distance = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8);
 	CanvasItem *c = Object::cast_to<CanvasItem>(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<CanvasLayer>(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<Node2D>(c);
 
 			_SelectResult res;

+ 1 - 0
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; };
 

+ 5 - 0
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<CollisionObject2D>(get_parent())) {

+ 1 - 0
scene/2d/collision_polygon_2d.h

@@ -70,6 +70,7 @@ public:
 	Vector<Point2> 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;
 

+ 8 - 0
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<CollisionObject2D>(get_parent())) {

+ 1 - 0
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<Shape2D> &p_shape);
 	Ref<Shape2D> get_shape() const;

+ 26 - 0
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<Vector2>::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<Vector2> &p_points) {
 	_points = p_points;
 	update();

+ 3 - 0
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<Vector2> &p_points);
 	PoolVector<Vector2> get_points() const;
 

+ 59 - 1
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<Vector2> &outline = outlines[i];
+			const int outline_size = outline.size();
+			if (outline_size < 3)
+				continue;
+			PoolVector<Vector2>::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<Vector2> &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<Vector2> &p_vertices) {
 
 	vertices = p_vertices;
+	rect_cache_dirty = true;
 }
 
 PoolVector<Vector2> 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<int> &p_polygon) {
 void NavigationPolygon::add_outline_at_index(const PoolVector<Vector2> &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<Vector2> &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<Vector2> &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<Vector2> NavigationPolygon::get_outline(int p_idx) const {
@@ -138,6 +184,7 @@ PoolVector<Vector2> 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) {

+ 9 - 0
scene/2d/navigation_polygon.h

@@ -43,6 +43,9 @@ class NavigationPolygon : public Resource {
 	Vector<Polygon> polygons;
 	Vector<PoolVector<Vector2> > 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<Vector2> &p_vertices);
 	PoolVector<Vector2> 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;
 

+ 53 - 1
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;
 			}
 		}

+ 3 - 0
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<Curve2D> &p_curve);
 	Ref<Curve2D> get_curve() const;
 

+ 6 - 1
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);

+ 1 - 0
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();
 };

+ 61 - 32
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> 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())

+ 3 - 0
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<Texture> &p_texture);

+ 20 - 9
scene/resources/capsule_shape_2d.cpp

@@ -32,6 +32,25 @@
 #include "servers/physics_2d_server.h"
 #include "servers/visual_server.h"
 
+Vector<Vector2> CapsuleShape2D::_get_points() const {
+
+	Vector<Vector2> 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<Vector2> 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<Vector2> points = _get_points();
 	Vector<Color> col;
 	col.push_back(p_color);
 	VisualServer::get_singleton()->canvas_item_add_polygon(p_to_rid, points, col);

+ 3 - 0
scene/resources/capsule_shape_2d.h

@@ -39,11 +39,14 @@ class CapsuleShape2D : public Shape2D {
 	real_t radius;
 
 	void _update_shape();
+	Vector<Vector2> _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;
 

+ 6 - 0
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);

+ 2 - 0
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;
 

+ 17 - 0
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<Vector2> s = get_segments();
+	int len = s.size();
+	if (len == 0 || (len % 2) == 1)
+		return false;
+
+	PoolVector<Vector2>::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<Vector2> &p_segments) {
 
 	Physics2DServer::get_singleton()->shape_set_data(get_rid(), p_segments);

+ 2 - 0
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<Vector2> &p_segments);
 	PoolVector<Vector2> get_segments() const;
 

+ 5 - 0
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);

+ 2 - 0
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<Vector2> &p_points);
 	void set_points(const Vector<Vector2> &p_points);
 	Vector<Vector2> get_points() const;

+ 7 - 0
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;

+ 2 - 0
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);
 

+ 2 - 0
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;
 

+ 15 - 0
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;

+ 2 - 0
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);