More exact picking for canvas editor

This commit is contained in:
Bernhard Liebl 2017-12-27 09:28:02 +01:00
parent 32d8b99bc3
commit 8505871a87
31 changed files with 345 additions and 46 deletions

View file

@ -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;

View file

@ -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];

View file

@ -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;

View file

@ -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; };

View file

@ -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())) {

View file

@ -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;

View file

@ -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())) {

View file

@ -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;

View file

@ -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();

View file

@ -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;

View file

@ -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) {

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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);

View file

@ -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();
};

View file

@ -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())

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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);