diff --git a/core/core_bind.cpp b/core/core_bind.cpp index c08ae987b0f3..471f24d016c2 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -1004,6 +1004,90 @@ TypedArray Geometry2D::exclude_polygons(const Vector Geometry2D::merge_polygons_complex(const TypedArray> &p_polygon_a, const TypedArray> &p_polygon_b) { + Vector> polygon_a; + for (int i = 0; i < p_polygon_a.size(); i++) { + polygon_a.push_back(p_polygon_a[i]); + } + + Vector> polygon_b; + for (int i = 0; i < p_polygon_b.size(); i++) { + polygon_b.push_back(p_polygon_b[i]); + } + + Vector> polys = ::Geometry2D::merge_polygons_complex(polygon_a, polygon_b); + + TypedArray ret; + + for (int i = 0; i < polys.size(); ++i) { + ret.push_back(polys[i]); + } + return ret; +} + +TypedArray Geometry2D::clip_polygons_complex(const TypedArray &p_polygon_a, const TypedArray &p_polygon_b) { + Vector> polygon_a; + for (int i = 0; i < p_polygon_a.size(); i++) { + polygon_a.push_back(p_polygon_a[i]); + } + + Vector> polygon_b; + for (int i = 0; i < p_polygon_b.size(); i++) { + polygon_b.push_back(p_polygon_b[i]); + } + + Vector> polys = ::Geometry2D::clip_polygons_complex(polygon_a, polygon_b); + + TypedArray ret; + + for (int i = 0; i < polys.size(); ++i) { + ret.push_back(polys[i]); + } + return ret; +} + +TypedArray Geometry2D::intersect_polygons_complex(const TypedArray &p_polygon_a, const TypedArray &p_polygon_b) { + Vector> polygon_a; + for (int i = 0; i < p_polygon_a.size(); i++) { + polygon_a.push_back(p_polygon_a[i]); + } + + Vector> polygon_b; + for (int i = 0; i < p_polygon_b.size(); i++) { + polygon_b.push_back(p_polygon_b[i]); + } + + Vector> polys = ::Geometry2D::intersect_polygons_complex(polygon_a, polygon_b); + + TypedArray ret; + + for (int i = 0; i < polys.size(); ++i) { + ret.push_back(polys[i]); + } + return ret; +} + +TypedArray Geometry2D::exclude_polygons_complex(const TypedArray &p_polygon_a, const TypedArray &p_polygon_b) { + Vector> polygon_a; + for (int i = 0; i < p_polygon_a.size(); i++) { + polygon_a.push_back(p_polygon_a[i]); + } + + Vector> polygon_b; + for (int i = 0; i < p_polygon_b.size(); i++) { + polygon_b.push_back(p_polygon_b[i]); + } + + Vector> polys = ::Geometry2D::exclude_polygons_complex(polygon_a, polygon_b); + + TypedArray ret; + + for (int i = 0; i < polys.size(); ++i) { + ret.push_back(polys[i]); + } + return ret; +} + TypedArray Geometry2D::clip_polyline_with_polygon(const Vector &p_polyline, const Vector &p_polygon) { Vector> polys = ::Geometry2D::clip_polyline_with_polygon(p_polyline, p_polygon); @@ -1111,6 +1195,11 @@ void Geometry2D::_bind_methods() { ClassDB::bind_method(D_METHOD("intersect_polygons", "polygon_a", "polygon_b"), &Geometry2D::intersect_polygons); ClassDB::bind_method(D_METHOD("exclude_polygons", "polygon_a", "polygon_b"), &Geometry2D::exclude_polygons); + ClassDB::bind_method(D_METHOD("merge_polygons_complex", "polygon_a", "polygon_b"), &Geometry2D::merge_polygons_complex); + ClassDB::bind_method(D_METHOD("clip_polygons_complex", "polygon_a", "polygon_b"), &Geometry2D::clip_polygons_complex); + ClassDB::bind_method(D_METHOD("intersect_polygons_complex", "polygon_a", "polygon_b"), &Geometry2D::intersect_polygons_complex); + ClassDB::bind_method(D_METHOD("exclude_polygons_complex", "polygon_a", "polygon_b"), &Geometry2D::exclude_polygons_complex); + ClassDB::bind_method(D_METHOD("clip_polyline_with_polygon", "polyline", "polygon"), &Geometry2D::clip_polyline_with_polygon); ClassDB::bind_method(D_METHOD("intersect_polyline_with_polygon", "polyline", "polygon"), &Geometry2D::intersect_polyline_with_polygon); diff --git a/core/core_bind.h b/core/core_bind.h index 9d6e3ee5b3bf..57d6f98fbe52 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -359,6 +359,12 @@ class Geometry2D : public Object { TypedArray intersect_polygons(const Vector &p_polygon_a, const Vector &p_polygon_b); // Common area (multiply). TypedArray exclude_polygons(const Vector &p_polygon_a, const Vector &p_polygon_b); // All but common area (xor). + // 2D complex (multiple contours) polygon boolean operations + TypedArray merge_polygons_complex(const TypedArray &p_polygon_a, const TypedArray &p_polygon_b); // Union (add). + TypedArray clip_polygons_complex(const TypedArray &p_polygon_a, const TypedArray &p_polygon_b); // Difference (subtract). + TypedArray intersect_polygons_complex(const TypedArray &p_polygon_a, const TypedArray &p_polygon_b); // Common area (multiply). + TypedArray exclude_polygons_complex(const TypedArray &p_polygon_a, const TypedArray &p_polygon_b); // All but common area (xor). + // 2D polyline vs polygon operations. TypedArray clip_polyline_with_polygon(const Vector &p_polyline, const Vector &p_polygon); // Cut. TypedArray intersect_polyline_with_polygon(const Vector &p_polyline, const Vector &p_polygon); // Chop. diff --git a/core/math/geometry_2d.cpp b/core/math/geometry_2d.cpp index cbf77603949e..734f477f9184 100644 --- a/core/math/geometry_2d.cpp +++ b/core/math/geometry_2d.cpp @@ -304,6 +304,69 @@ Vector> Geometry2D::_polypaths_do_operation(PolyBooleanOperation return polypaths; } +Vector> Geometry2D::_polypaths_do_operation_complex(PolyBooleanOperation p_op, const Vector> &p_polypaths_a, const Vector> &p_polypaths_b) { + using namespace Clipper2Lib; + + ClipType op = ClipType::Union; + + switch (p_op) { + case OPERATION_UNION: + op = ClipType::Union; + break; + case OPERATION_DIFFERENCE: + op = ClipType::Difference; + break; + case OPERATION_INTERSECTION: + op = ClipType::Intersection; + break; + case OPERATION_XOR: + op = ClipType::Xor; + break; + } + + ClipperD clp(clipper_precision); // Scale points up internally to attain the desired precision. + clp.PreserveCollinear(false); // Remove redundant vertices. + + PathsD paths_a; + for (int i = 0; i < p_polypaths_a.size(); i++) { + const Vector &sub_polypath = p_polypaths_a[i]; + + PathD path(sub_polypath.size()); + for (int j = 0; j != sub_polypath.size(); ++j) { + path[j] = PointD(sub_polypath[j].x, sub_polypath[j].y); + } + paths_a.push_back(path); + } + clp.AddSubject(paths_a); + + PathsD paths_b; + for (int i = 0; i < p_polypaths_b.size(); i++) { + const Vector &sub_polypath = p_polypaths_b[i]; + + PathD path(sub_polypath.size()); + for (int j = 0; j != sub_polypath.size(); ++j) { + path[j] = PointD(sub_polypath[j].x, sub_polypath[j].y); + } + paths_b.push_back(path); + } + clp.AddClip(paths_b); + + PathsD out_paths; + clp.Execute(op, FillRule::EvenOdd, out_paths); + + Vector> polypaths; + for (PathsD::size_type i = 0; i < out_paths.size(); ++i) { + const PathD &path = out_paths[i]; + + Vector polypath; + for (PathsD::size_type j = 0; j < path.size(); ++j) { + polypath.push_back(Point2(static_cast(path[j].x), static_cast(path[j].y))); + } + polypaths.push_back(polypath); + } + return polypaths; +} + Vector> Geometry2D::_polypath_offset(const Vector &p_polypath, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { using namespace Clipper2Lib; diff --git a/core/math/geometry_2d.h b/core/math/geometry_2d.h index fa5b0f061bda..60cb60047a26 100644 --- a/core/math/geometry_2d.h +++ b/core/math/geometry_2d.h @@ -320,6 +320,22 @@ class Geometry2D { return _polypaths_do_operation(OPERATION_XOR, p_polygon_a, p_polygon_b); } + static Vector> merge_polygons_complex(const Vector> &p_polygon_a, const Vector> &p_polygon_b) { + return _polypaths_do_operation_complex(OPERATION_UNION, p_polygon_a, p_polygon_b); + } + + static Vector> clip_polygons_complex(const Vector> &p_polygon_a, const Vector> &p_polygon_b) { + return _polypaths_do_operation_complex(OPERATION_DIFFERENCE, p_polygon_a, p_polygon_b); + } + + static Vector> intersect_polygons_complex(const Vector> &p_polygon_a, const Vector> &p_polygon_b) { + return _polypaths_do_operation_complex(OPERATION_INTERSECTION, p_polygon_a, p_polygon_b); + } + + static Vector> exclude_polygons_complex(const Vector> &p_polygon_a, const Vector> &p_polygon_b) { + return _polypaths_do_operation_complex(OPERATION_XOR, p_polygon_a, p_polygon_b); + } + static Vector> clip_polyline_with_polygon(const Vector &p_polyline, const Vector &p_polygon) { return _polypaths_do_operation(OPERATION_DIFFERENCE, p_polyline, p_polygon, true); } @@ -509,5 +525,6 @@ class Geometry2D { private: static Vector> _polypaths_do_operation(PolyBooleanOperation p_op, const Vector &p_polypath_a, const Vector &p_polypath_b, bool is_a_open = false); + static Vector> _polypaths_do_operation_complex(PolyBooleanOperation p_op, const Vector> &p_polypaths_a, const Vector> &p_polypaths_b); static Vector> _polypath_offset(const Vector &p_polypath, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type); }; diff --git a/doc/classes/Geometry2D.xml b/doc/classes/Geometry2D.xml index 774bff6113db..19c6048b5800 100644 --- a/doc/classes/Geometry2D.xml +++ b/doc/classes/Geometry2D.xml @@ -32,6 +32,15 @@ If [param polygon_b] is enclosed by [param polygon_a], returns an outer polygon (boundary) and inner polygon (hole) which could be distinguished by calling [method is_polygon_clockwise]. + + + + + + Clips [param polygon_a] against [param polygon_b] and returns an array of clipped polygons. This operation will work on a polygon with holes. This performs [constant OPERATION_DIFFERENCE] between polygons. Returns an empty array if [param polygon_b] completely overlaps [param polygon_a]. + If [param polygon_b] is enclosed by [param polygon_a], returns an outer polygon (boundary) and inner polygon (hole) which could be distinguished by calling [method is_polygon_clockwise]. + + @@ -63,6 +72,15 @@ The operation may result in an outer polygon (boundary) and inner polygon (hole) produced which could be distinguished by calling [method is_polygon_clockwise]. + + + + + + Mutually excludes common area defined by intersection of [param polygon_a] and [param polygon_b] (see [method intersect_polygons]) and returns an array of excluded polygons. This operation will work on a polygon with holes. This performs [constant OPERATION_XOR] between polygons. In other words, returns all but common area between polygons. + The operation may result in an outer polygon (boundary) and inner polygon (hole) produced which could be distinguished by calling [method is_polygon_clockwise]. + + @@ -100,6 +118,15 @@ The operation may result in an outer polygon (boundary) and inner polygon (hole) produced which could be distinguished by calling [method is_polygon_clockwise]. + + + + + + Intersects [param polygon_a] with [param polygon_b] and returns an array of intersected polygons. This operation will work on a polygon with holes. This performs [constant OPERATION_INTERSECTION] between polygons. In other words, returns common area shared by polygons. Returns an empty array if no intersection occurs. + The operation may result in an outer polygon (boundary) and inner polygon (hole) produced which could be distinguished by calling [method is_polygon_clockwise]. + + @@ -186,6 +213,15 @@ The operation may result in an outer polygon (boundary) and multiple inner polygons (holes) produced which could be distinguished by calling [method is_polygon_clockwise]. + + + + + + Merges (combines) [param polygon_a] and [param polygon_b] and returns an array of merged polygons. This operation will work on a polygon with holes. This performs [constant OPERATION_UNION] between polygons. + The operation may result in an outer polygon (boundary) and multiple inner polygons (holes) produced which could be distinguished by calling [method is_polygon_clockwise]. + +