diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index ce0f47de9c4c..9834b4dc0a3e 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -1593,100 +1593,137 @@ Vector _Geometry::clip_polygon(const Vector &p_points, const P return Geometry::clip_polygon(p_points, p_plane); } -Array _Geometry::merge_polygons_2d(const Vector &p_polygon_a, const Vector &p_polygon_b) { +Array _Geometry::merge_polygons_2d(const Variant &p_subject) { - Vector > polys = Geometry::merge_polygons_2d(p_polygon_a, p_polygon_b); + return _polypaths_do_operation(OPERATION_UNION, p_subject); +} - Array ret; +Array _Geometry::clip_polygons_2d(const Variant &p_subject, const Variant &p_clip) { - for (int i = 0; i < polys.size(); ++i) { - ret.push_back(polys[i]); - } - return ret; + return _polypaths_do_operation(OPERATION_DIFFERENCE, p_subject, p_clip); } -Array _Geometry::clip_polygons_2d(const Vector &p_polygon_a, const Vector &p_polygon_b) { +Array _Geometry::intersect_polygons_2d(const Variant &p_subject, const Variant &p_clip) { - Vector > polys = Geometry::clip_polygons_2d(p_polygon_a, p_polygon_b); + return _polypaths_do_operation(OPERATION_INTERSECTION, p_subject, p_clip); +} - Array ret; +Array _Geometry::exclude_polygons_2d(const Variant &p_subject, const Variant &p_clip) { - for (int i = 0; i < polys.size(); ++i) { - ret.push_back(polys[i]); - } - return ret; + return _polypaths_do_operation(OPERATION_XOR, p_subject, p_clip); } -Array _Geometry::intersect_polygons_2d(const Vector &p_polygon_a, const Vector &p_polygon_b) { +Array _Geometry::clip_polylines_with_polygons_2d(const Variant &p_polylines, const Variant &p_polygons) { - Vector > polys = Geometry::intersect_polygons_2d(p_polygon_a, p_polygon_b); + return _polypaths_do_operation(OPERATION_DIFFERENCE, p_polylines, p_polygons, true); +} - Array ret; +Array _Geometry::intersect_polylines_with_polygons_2d(const Variant &p_polylines, const Variant &p_polygons) { - for (int i = 0; i < polys.size(); ++i) { - ret.push_back(polys[i]); - } - return ret; + return _polypaths_do_operation(OPERATION_INTERSECTION, p_polylines, p_polygons, true); } -Array _Geometry::exclude_polygons_2d(const Vector &p_polygon_a, const Vector &p_polygon_b) { +Array _Geometry::offset_polygons_2d(const Variant &p_polygons, real_t p_delta, PolyJoinType p_join_type) { - Vector > polys = Geometry::exclude_polygons_2d(p_polygon_a, p_polygon_b); + Vector > polygons; + _convert_polypaths(p_polygons, polygons); - Array ret; + Vector > solution; + solution = Geometry::offset_polygons_2d(polygons, p_delta, Geometry::PolyJoinType(p_join_type)); - for (int i = 0; i < polys.size(); ++i) { - ret.push_back(polys[i]); + Array ret; + for (int i = 0; i < solution.size(); ++i) { + ret.push_back(solution[i]); } return ret; } -Array _Geometry::clip_polyline_with_polygon_2d(const Vector &p_polyline, const Vector &p_polygon) { +Array _Geometry::offset_polylines_2d(const Variant &p_polylines, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { - Vector > polys = Geometry::clip_polyline_with_polygon_2d(p_polyline, p_polygon); + Vector > polylines; + _convert_polypaths(p_polylines, polylines); - Array ret; + Vector > solution; + solution = Geometry::offset_polylines_2d(polylines, p_delta, Geometry::PolyJoinType(p_join_type), Geometry::PolyEndType(p_end_type)); - for (int i = 0; i < polys.size(); ++i) { - ret.push_back(polys[i]); + Array ret; + for (int i = 0; i < solution.size(); ++i) { + ret.push_back(solution[i]); } return ret; } -Array _Geometry::intersect_polyline_with_polygon_2d(const Vector &p_polyline, const Vector &p_polygon) { +Array _Geometry::_polypaths_do_operation(PolyBooleanOperation p_op, const Variant &p_subject, const Variant &p_clip, bool is_subject_open) { - Vector > polys = Geometry::intersect_polyline_with_polygon_2d(p_polyline, p_polygon); + Vector > solution; - Array ret; + Vector > subject; + _convert_polypaths(p_subject, subject); - for (int i = 0; i < polys.size(); ++i) { - ret.push_back(polys[i]); - } - return ret; -} + if (p_clip.get_type() == Variant::NIL) { + // Assume we operate in OPERATION_UNION mode as it's + // the only operation accepting subject polygons only + ERR_FAIL_COND_V(p_op != OPERATION_UNION, Array()); -Array _Geometry::offset_polygon_2d(const Vector &p_polygon, real_t p_delta, PolyJoinType p_join_type) { + solution = Geometry::merge_polygons_2d(subject); + } else { - Vector > polys = Geometry::offset_polygon_2d(p_polygon, p_delta, Geometry::PolyJoinType(p_join_type)); + Vector > clip; + _convert_polypaths(p_clip, clip); - Array ret; + switch (p_op) { + case OPERATION_UNION: { + ERR_FAIL_V(Array()); // merge operation should be handled above + } break; - for (int i = 0; i < polys.size(); ++i) { - ret.push_back(polys[i]); + case OPERATION_DIFFERENCE: { + if (is_subject_open) { + solution = Geometry::clip_polylines_with_polygons_2d(subject, clip); + } else { + solution = Geometry::clip_polygons_2d(subject, clip); + } + } break; + + case OPERATION_INTERSECTION: { + if (is_subject_open) { + solution = Geometry::intersect_polylines_with_polygons_2d(subject, clip); + } else { + solution = Geometry::intersect_polygons_2d(subject, clip); + } + } break; + + case OPERATION_XOR: { + solution = Geometry::exclude_polygons_2d(subject, clip); + } break; + } + } + Array ret; + for (int i = 0; i < solution.size(); ++i) { + ret.push_back(solution[i]); } return ret; } -Array _Geometry::offset_polyline_2d(const Vector &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { +void _Geometry::_convert_polypaths(const Variant &p_input, Vector > &r_output) { - Vector > polys = Geometry::offset_polyline_2d(p_polygon, p_delta, Geometry::PolyJoinType(p_join_type), Geometry::PolyEndType(p_end_type)); + if (p_input.get_type() == Variant::ARRAY) { + Array input = p_input; - Array ret; + for (int i = 0; i < input.size(); ++i) { +#if DEBUG_ENABLED + ERR_EXPLAIN("Expected an Array of PoolVector2Array's as polygons.") + ERR_FAIL_COND(input[i].get_type() != Variant::POOL_VECTOR2_ARRAY); +#endif + r_output.push_back(input[i]); + } + } else if (p_input.get_type() == Variant::POOL_VECTOR2_ARRAY) { + + r_output.push_back(p_input); - for (int i = 0; i < polys.size(); ++i) { - ret.push_back(polys[i]); + } else { + ERR_EXPLAIN("Expected a polygon or an array of polygons as input.") + ERR_FAIL(); } - return ret; } Vector _Geometry::transform_points_2d(const Vector &p_points, const Transform2D &p_mat) { @@ -1762,16 +1799,16 @@ void _Geometry::_bind_methods() { ClassDB::bind_method(D_METHOD("convex_hull_2d", "points"), &_Geometry::convex_hull_2d); ClassDB::bind_method(D_METHOD("clip_polygon", "points", "plane"), &_Geometry::clip_polygon); - ClassDB::bind_method(D_METHOD("merge_polygons_2d", "polygon_a", "polygon_b"), &_Geometry::merge_polygons_2d); - ClassDB::bind_method(D_METHOD("clip_polygons_2d", "polygon_a", "polygon_b"), &_Geometry::clip_polygons_2d); - ClassDB::bind_method(D_METHOD("intersect_polygons_2d", "polygon_a", "polygon_b"), &_Geometry::intersect_polygons_2d); - ClassDB::bind_method(D_METHOD("exclude_polygons_2d", "polygon_a", "polygon_b"), &_Geometry::exclude_polygons_2d); + ClassDB::bind_method(D_METHOD("merge_polygons_2d", "polygons"), &_Geometry::merge_polygons_2d); + ClassDB::bind_method(D_METHOD("clip_polygons_2d", "polygons_a", "polygons_b"), &_Geometry::clip_polygons_2d); + ClassDB::bind_method(D_METHOD("intersect_polygons_2d", "polygons_a", "polygons_b"), &_Geometry::intersect_polygons_2d); + ClassDB::bind_method(D_METHOD("exclude_polygons_2d", "polygons_a", "polygons_b"), &_Geometry::exclude_polygons_2d); - ClassDB::bind_method(D_METHOD("clip_polyline_with_polygon_2d", "polyline", "polygon"), &_Geometry::clip_polyline_with_polygon_2d); - ClassDB::bind_method(D_METHOD("intersect_polyline_with_polygon_2d", "polyline", "polygon"), &_Geometry::intersect_polyline_with_polygon_2d); + ClassDB::bind_method(D_METHOD("clip_polylines_with_polygons_2d", "polylines", "polygons"), &_Geometry::clip_polylines_with_polygons_2d); + ClassDB::bind_method(D_METHOD("intersect_polylines_with_polygons_2d", "polylines", "polygons"), &_Geometry::intersect_polylines_with_polygons_2d); - ClassDB::bind_method(D_METHOD("offset_polygon_2d", "polygon", "delta", "join_type"), &_Geometry::offset_polygon_2d, DEFVAL(JOIN_SQUARE)); - ClassDB::bind_method(D_METHOD("offset_polyline_2d", "polyline", "delta", "join_type", "end_type"), &_Geometry::offset_polyline_2d, DEFVAL(JOIN_SQUARE), DEFVAL(END_SQUARE)); + ClassDB::bind_method(D_METHOD("offset_polygons_2d", "polygons", "delta", "join_type"), &_Geometry::offset_polygons_2d, DEFVAL(JOIN_SQUARE)); + ClassDB::bind_method(D_METHOD("offset_polylines_2d", "polylines", "delta", "join_type", "end_type"), &_Geometry::offset_polylines_2d, DEFVAL(JOIN_SQUARE), DEFVAL(END_SQUARE)); ClassDB::bind_method(D_METHOD("transform_points_2d", "points", "transform"), &_Geometry::transform_points_2d); diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 561bba53c975..69e8f30a2aa0 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -409,14 +409,14 @@ class _Geometry : public Object { OPERATION_XOR }; // 2D polygon boolean operations. - Array merge_polygons_2d(const Vector &p_polygon_a, const Vector &p_polygon_b); // Union (add). - Array clip_polygons_2d(const Vector &p_polygon_a, const Vector &p_polygon_b); // Difference (subtract). - Array intersect_polygons_2d(const Vector &p_polygon_a, const Vector &p_polygon_b); // Common area (multiply). - Array exclude_polygons_2d(const Vector &p_polygon_a, const Vector &p_polygon_b); // All but common area (xor). + Array merge_polygons_2d(const Variant &p_polygons); // Union (add). + Array clip_polygons_2d(const Variant &p_polygons_a, const Variant &p_polygons_b); // Difference (subtract). + Array intersect_polygons_2d(const Variant &p_polygons_a, const Variant &p_polygons_b); // Common area (multiply). + Array exclude_polygons_2d(const Variant &p_polygons_a, const Variant &p_polygons_b); // All but common area (xor). // 2D polyline vs polygon operations. - Array clip_polyline_with_polygon_2d(const Vector &p_polyline, const Vector &p_polygon); // Cut. - Array intersect_polyline_with_polygon_2d(const Vector &p_polyline, const Vector &p_polygon); // Chop. + Array clip_polylines_with_polygons_2d(const Variant &p_polylines, const Variant &p_polygons); // Cut. + Array intersect_polylines_with_polygons_2d(const Variant &p_polylines, const Variant &p_polygons); // Chop. // 2D offset polygons/polylines. enum PolyJoinType { @@ -431,14 +431,18 @@ class _Geometry : public Object { END_SQUARE, END_ROUND }; - Array offset_polygon_2d(const Vector &p_polygon, real_t p_delta, PolyJoinType p_join_type = JOIN_SQUARE); - Array offset_polyline_2d(const Vector &p_polygon, real_t p_delta, PolyJoinType p_join_type = JOIN_SQUARE, PolyEndType p_end_type = END_SQUARE); + Array offset_polygons_2d(const Variant &p_polygons, real_t p_delta, PolyJoinType p_join_type = JOIN_SQUARE); + Array offset_polylines_2d(const Variant &p_polylines, real_t p_delta, PolyJoinType p_join_type = JOIN_SQUARE, PolyEndType p_end_type = END_SQUARE); Vector transform_points_2d(const Vector &p_points, const Transform2D &p_mat); Dictionary make_atlas(const Vector &p_rects); _Geometry(); + +private: + Array _polypaths_do_operation(PolyBooleanOperation p_op, const Variant &p_polypaths_subject, const Variant &p_polypaths_clip = Variant(), bool is_subject_open = false); + void _convert_polypaths(const Variant &p_input, Vector > &r_output); }; VARIANT_ENUM_CAST(_Geometry::PolyBooleanOperation); diff --git a/core/math/geometry.cpp b/core/math/geometry.cpp index f37db9092949..1d54300ddda3 100644 --- a/core/math/geometry.cpp +++ b/core/math/geometry.cpp @@ -1088,7 +1088,49 @@ void Geometry::make_atlas(const Vector &p_rects, Vector &r_resu r_size = Size2(results[best].max_w, results[best].max_h); } -Vector > Geometry::_polypaths_do_operation(PolyBooleanOperation p_op, const Vector &p_polypath_a, const Vector &p_polypath_b, bool is_a_open) { +// Polygon and polyline boolean operations + +namespace GodotClipperUtils { + +static void scale_up_polypaths(const Vector > &p_polypaths_in, ClipperLib::Paths &p_polypaths_out) { + + p_polypaths_out.clear(); + p_polypaths_out.resize(p_polypaths_in.size()); + + for (int i = 0; i < p_polypaths_in.size(); ++i) { + + const Vector &polypath_in = p_polypaths_in[i]; + ClipperLib::Path &polypath_out = p_polypaths_out[i]; + + for (int j = 0; j < polypath_in.size(); ++j) { + polypath_out << ClipperLib::IntPoint( + polypath_in[j].x * SCALE_FACTOR, + polypath_in[j].y * SCALE_FACTOR); + } + } +} + +static void scale_down_polypaths(const ClipperLib::Paths &p_polypaths_in, Vector > &p_polypaths_out) { + + p_polypaths_out.clear(); + + for (ClipperLib::Paths::size_type i = 0; i < p_polypaths_in.size(); ++i) { + + const ClipperLib::Path &polypath_in = p_polypaths_in[i]; + Vector polypath_out; + + for (ClipperLib::Paths::size_type j = 0; j < polypath_in.size(); ++j) { + polypath_out.push_back(Point2( + static_cast(polypath_in[j].X) / SCALE_FACTOR, + static_cast(polypath_in[j].Y) / SCALE_FACTOR)); + } + p_polypaths_out.push_back(polypath_out); + } +} + +} // namespace GodotClipperUtils + +Vector > Geometry::_polypaths_do_operation(PolyBooleanOperation p_op, const Vector > &p_polypaths_subject, const Vector > &p_polypaths_clip, bool is_a_open) { using namespace ClipperLib; @@ -1100,47 +1142,33 @@ Vector > Geometry::_polypaths_do_operation(PolyBooleanOperation p case OPERATION_INTERSECTION: op = ctIntersection; break; case OPERATION_XOR: op = ctXor; break; } - Path path_a, path_b; + Paths paths_subject, paths_clip; // Need to scale points (Clipper's requirement for robust computation). - for (int i = 0; i != p_polypath_a.size(); ++i) { - path_a << IntPoint(p_polypath_a[i].x * SCALE_FACTOR, p_polypath_a[i].y * SCALE_FACTOR); - } - for (int i = 0; i != p_polypath_b.size(); ++i) { - path_b << IntPoint(p_polypath_b[i].x * SCALE_FACTOR, p_polypath_b[i].y * SCALE_FACTOR); - } + GodotClipperUtils::scale_up_polypaths(p_polypaths_subject, paths_subject); + GodotClipperUtils::scale_up_polypaths(p_polypaths_clip, paths_clip); + Clipper clp; - clp.AddPath(path_a, ptSubject, !is_a_open); // Forward compatible with Clipper 10.0.0. - clp.AddPath(path_b, ptClip, true); // Polylines cannot be set as clip. + clp.AddPaths(paths_subject, ptSubject, !is_a_open); // Forward compatible with Clipper 10.0.0. + clp.AddPaths(paths_clip, ptClip, true); // Polylines cannot be set as clip. - Paths paths; + Paths solution; if (is_a_open) { PolyTree tree; // Needed to populate polylines. clp.Execute(op, tree); - OpenPathsFromPolyTree(tree, paths); + OpenPathsFromPolyTree(tree, solution); } else { - clp.Execute(op, paths); // Works on closed polygons only. + clp.Execute(op, solution, pftNonZero); // Works on closed polygons only. } // Have to scale points down now. Vector > polypaths; + GodotClipperUtils::scale_down_polypaths(solution, polypaths); - for (Paths::size_type i = 0; i < paths.size(); ++i) { - Vector polypath; - - const Path &scaled_path = paths[i]; - - for (Paths::size_type j = 0; j < scaled_path.size(); ++j) { - polypath.push_back(Point2( - static_cast(scaled_path[j].X) / SCALE_FACTOR, - static_cast(scaled_path[j].Y) / SCALE_FACTOR)); - } - polypaths.push_back(polypath); - } return polypaths; } -Vector > Geometry::_polypath_offset(const Vector &p_polypath, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { +Vector > Geometry::_polypaths_offset(const Vector > &p_polypaths, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { using namespace ClipperLib; @@ -1162,31 +1190,19 @@ Vector > Geometry::_polypath_offset(const Vector &p_polyp case END_ROUND: et = etOpenRound; break; } ClipperOffset co; - Path path; + Paths paths; // Need to scale points (Clipper's requirement for robust computation). - for (int i = 0; i != p_polypath.size(); ++i) { - path << IntPoint(p_polypath[i].x * SCALE_FACTOR, p_polypath[i].y * SCALE_FACTOR); - } - co.AddPath(path, jt, et); + GodotClipperUtils::scale_up_polypaths(p_polypaths, paths); - Paths paths; - co.Execute(paths, p_delta * SCALE_FACTOR); // Inflate/deflate. + co.AddPaths(paths, jt, et); + + Paths solution; + co.Execute(solution, p_delta * SCALE_FACTOR); // Inflate/deflate. // Have to scale points down now. Vector > polypaths; + GodotClipperUtils::scale_down_polypaths(solution, polypaths); - for (Paths::size_type i = 0; i < paths.size(); ++i) { - Vector polypath; - - const Path &scaled_path = paths[i]; - - for (Paths::size_type j = 0; j < scaled_path.size(); ++j) { - polypath.push_back(Point2( - static_cast(scaled_path[j].X) / SCALE_FACTOR, - static_cast(scaled_path[j].Y) / SCALE_FACTOR)); - } - polypaths.push_back(polypath); - } return polypaths; } diff --git a/core/math/geometry.h b/core/math/geometry.h index b512cfce8041..741f11de2013 100644 --- a/core/math/geometry.h +++ b/core/math/geometry.h @@ -790,46 +790,46 @@ class Geometry { END_ROUND }; - static Vector > merge_polygons_2d(const Vector &p_polygon_a, const Vector &p_polygon_b) { - - return _polypaths_do_operation(OPERATION_UNION, p_polygon_a, p_polygon_b); + static Vector > merge_polygons_2d(const Vector > &p_subject) { + // no need to distinguish between Subject/Clip polygons + return _polypaths_do_operation(OPERATION_UNION, p_subject); } - static Vector > clip_polygons_2d(const Vector &p_polygon_a, const Vector &p_polygon_b) { + static Vector > clip_polygons_2d(const Vector > &p_subject, const Vector > &p_clip) { - return _polypaths_do_operation(OPERATION_DIFFERENCE, p_polygon_a, p_polygon_b); + return _polypaths_do_operation(OPERATION_DIFFERENCE, p_subject, p_clip); } - static Vector > intersect_polygons_2d(const Vector &p_polygon_a, const Vector &p_polygon_b) { + static Vector > intersect_polygons_2d(const Vector > &p_subject, const Vector > &p_clip) { - return _polypaths_do_operation(OPERATION_INTERSECTION, p_polygon_a, p_polygon_b); + return _polypaths_do_operation(OPERATION_INTERSECTION, p_subject, p_clip); } - static Vector > exclude_polygons_2d(const Vector &p_polygon_a, const Vector &p_polygon_b) { + static Vector > exclude_polygons_2d(const Vector > &p_subject, const Vector > &p_clip) { - return _polypaths_do_operation(OPERATION_XOR, p_polygon_a, p_polygon_b); + return _polypaths_do_operation(OPERATION_XOR, p_subject, p_clip); } - static Vector > clip_polyline_with_polygon_2d(const Vector &p_polyline, const Vector &p_polygon) { + static Vector > clip_polylines_with_polygons_2d(const Vector > &p_polylines, const Vector > &p_polygons) { - return _polypaths_do_operation(OPERATION_DIFFERENCE, p_polyline, p_polygon, true); + return _polypaths_do_operation(OPERATION_DIFFERENCE, p_polylines, p_polygons, true); } - static Vector > intersect_polyline_with_polygon_2d(const Vector &p_polyline, const Vector &p_polygon) { + static Vector > intersect_polylines_with_polygons_2d(const Vector > &p_polylines, const Vector > &p_polygons) { - return _polypaths_do_operation(OPERATION_INTERSECTION, p_polyline, p_polygon, true); + return _polypaths_do_operation(OPERATION_INTERSECTION, p_polylines, p_polygons, true); } - static Vector > offset_polygon_2d(const Vector &p_polygon, real_t p_delta, PolyJoinType p_join_type) { + static Vector > offset_polygons_2d(const Vector > &p_polygons, real_t p_delta, PolyJoinType p_join_type) { - return _polypath_offset(p_polygon, p_delta, p_join_type, END_POLYGON); + return _polypaths_offset(p_polygons, p_delta, p_join_type, END_POLYGON); } - static Vector > offset_polyline_2d(const Vector &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { + static Vector > offset_polylines_2d(const Vector > &p_polylines, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { ERR_FAIL_COND_V_MSG(p_end_type == END_POLYGON, Vector >(), "Attempt to offset a polyline like a polygon (use offset_polygon_2d instead)."); - return _polypath_offset(p_polygon, p_delta, p_join_type, p_end_type); + return _polypaths_offset(p_polylines, p_delta, p_join_type, p_end_type); } static Vector transform_points_2d(const Vector &p_points, const Transform2D &p_mat) { @@ -1034,8 +1034,8 @@ class Geometry { static void make_atlas(const Vector &p_rects, Vector &r_result, Size2i &r_size); 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 > _polypath_offset(const Vector &p_polypath, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type); + static Vector > _polypaths_do_operation(PolyBooleanOperation p_op, const Vector > &p_polypaths_subject, const Vector > &p_polypaths_clip = Vector >(), bool is_a_open = false); + static Vector > _polypaths_offset(const Vector > &p_polypaths, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type); }; #endif diff --git a/doc/classes/Geometry.xml b/doc/classes/Geometry.xml index 5c5be6c44fa1..e4fca78b8171 100644 --- a/doc/classes/Geometry.xml +++ b/doc/classes/Geometry.xml @@ -62,24 +62,26 @@ - + - + - Clips [code]polygon_a[/code] against [code]polygon_b[/code] and returns an array of clipped polygons. This performs [constant OPERATION_DIFFERENCE] between polygons. Returns an empty array if [code]polygon_b[/code] completely overlaps [code]polygon_a[/code]. - If [code]polygon_b[/code] is enclosed by [code]polygon_a[/code], returns an outer polygon (boundary) and inner polygon (hole) which could be distiguished by calling [method is_polygon_clockwise]. + Clips [code]polygons_a[/code] against [code]polygons_b[/code] and returns an array of clipped polygons. This performs [constant OPERATION_DIFFERENCE] between polygons. Returns an empty array if [code]polygons_b[/code] completely overlap [code]polygons_a[/code]. + If [code]polygons_b[/code] are enclosed by [code]polygons_a[/code], returns outer polygons (boundary) and inner polygons (hole) which could be distinguished by calling [method is_polygon_clockwise]. + Both [code]polygons_a[/code] and [code]polygons_b[/code] parameters can either accept [PoolVector2Array] to be interpreted as a single polygon or [Array] of [PoolVector2Array]'s to accept a list of polygons. - + - + - + - Clips [code]polyline[/code] against [code]polygon[/code] and returns an array of clipped polylines. This performs [constant OPERATION_DIFFERENCE] between the polyline and the polygon. This operation can be thought of as cutting a line with a closed shape. + Clips [code]polylines[/code] against [code]polygons[/code] and returns an array of clipped polylines. This performs [constant OPERATION_DIFFERENCE between polylines and polygons. This operation can be thought of as cutting a line in two with a closed shape. + Both [code]polylines[/code] and [code]polygons[/code] parameters can either accept [PoolVector2Array] to be interpreted as a single polypath or [Array] of [PoolVector2Array]'s to accept a list of polypaths. @@ -94,13 +96,14 @@ - + - + - Mutually excludes common area defined by intersection of [code]polygon_a[/code] and [code]polygon_b[/code] (see [method intersect_polygons_2d]) and returns an array of excluded polygons. 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 distiguished by calling [method is_polygon_clockwise]. + Mutually excludes common area defined by intersection of [code]polygons_a[/code] and [code]polygons_b[/code] (see [method intersect_polygons_2d]) and returns an array of excluded polygons. This performs [constant OPERATION_XOR] between polygons. In other words, returns all but common area defined by polygons. + The operation may result in outer polygons (boundary) and inner polygons (hole) produced which could be distinguished by calling [method is_polygon_clockwise]. + Both [code]polygons_a[/code] and [code]polygons_b[/code] parameters can either accept [PoolVector2Array] to be interpreted as a single polygon or [Array] of [PoolVector2Array]'s to accept a list of polygons. @@ -196,24 +199,26 @@ - + - + - Intersects [code]polygon_a[/code] with [code]polygon_b[/code] and returns an array of intersected polygons. 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]. + Intersects [code]polygons_a[/code] with [code]polygons_b[/code] and returns an array of intersected polygons. 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 outer polygons (boundary) and inner polygons (hole) produced which could be distinguished by calling [method is_polygon_clockwise]. + Both [code]polygons_a[/code] and [code]polygons_b[/code] parameters can either accept [PoolVector2Array] to be interpreted as a single polygon or [Array] of [PoolVector2Array]'s to accept a list of polygons. - + - + - + - Intersects [code]polyline[/code] with [code]polygon[/code] and returns an array of intersected polylines. This performs [constant OPERATION_INTERSECTION] between the polyline and the polygon. This operation can be thought of as chopping a line with a closed shape. + Intersects [code]polylines[/code] with [code]polygons[/code] and returns an array of intersected polylines. This performs [constant OPERATION_INTERSECTION] between the polyline and the polygon. This operation can be thought of as chopping a line with a closed shape. + Both [code]polylines[/code] and [code]polygons[/code] parameters can either accept [PoolVector2Array] to be interpreted as a single polypath or [Array] of [PoolVector2Array]'s to accept a list of polypaths. @@ -277,34 +282,35 @@ - - - + - Merges (combines) [code]polygon_a[/code] and [code]polygon_b[/code] and returns an array of merged polygons. This performs [constant OPERATION_UNION] 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]. + Merges (combines) all [code]polygons[/code] and returns an array of merged polygons. This performs [constant OPERATION_UNION] between polygons. + Not all polygons may be merged but only those which overlap geometrically. If not, consider using [method offset_polygons_2d] with positive delta before merging, also see [method convex_hull_2d], [method triangulate_delaunay_2d]. Returns original polygons if neither polygon overlap. The method may also attempt to turn self-intersecting polygons into strictly simple even if no merging occurs. + The operation may result in outer polygons (boundary) and inner polygons (hole) produced which could be distinguished by calling [method is_polygon_clockwise]. + [code]polygons[/code] parameter accepts [Array] of [PoolVector2Array]'s to represent a list of polygons. Passing a single [PoolVector2Array] polygon is possible and can be used to eliminate self-intersections. - + - + - Inflates or deflates [code]polygon[/code] by [code]delta[/code] units (pixels). If [code]delta[/code] is positive, makes the polygon grow outward. If [code]delta[/code] is negative, shrinks the polygon inward. Returns an array of polygons because inflating/deflating may result in multiple discrete polygons. Returns an empty array if [code]delta[/code] is negative and the absolute value of it approximately exceeds the minimum bounding rectangle dimensions of the polygon. + Inflates or deflates [code]polygons[/code] by [code]delta[/code] units (pixels). If [code]delta[/code] is positive, makes the polygons grow outward. If [code]delta[/code] is negative, shrinks the polygons inward. Returns an array of polygons as inflating/deflating may result in multiple discrete polygons. Returns an empty array if [code]delta[/code] is negative and the absolute value of it approximately exceeds the minimum bounding rectangle dimensions of each polygon. Each polygon's vertices will be rounded as determined by [code]join_type[/code], see [enum PolyJoinType]. - The operation may result in an outer polygon (boundary) and inner polygon (hole) produced which could be distinguished by calling [method is_polygon_clockwise]. + The operation may result in outer polygons (boundary) and inner polygons (hole) produced which could be distinguished by calling [method is_polygon_clockwise]. + [code]polygons[/code] parameter can either accept [PoolVector2Array] to be interpreted as a single polygon or [Array] of [PoolVector2Array]'s to accept a list of polygons. - + - + @@ -313,10 +319,11 @@ - Inflates or deflates [code]polyline[/code] by [code]delta[/code] units (pixels), producing polygons. If [code]delta[/code] is positive, makes the polyline grow outward. Returns an array of polygons because inflating/deflating may result in multiple discrete polygons. If [code]delta[/code] is negative, returns an empty array. + Inflates or deflates [code]polylines[/code] by [code]delta[/code] units (pixels), producing polygons. If [code]delta[/code] is positive, makes the polylines grow outward. Returns an array of polygons as inflating/deflating may result in multiple discrete polygons. If [code]delta[/code] is negative, returns an empty array. Each polygon's vertices will be rounded as determined by [code]join_type[/code], see [enum PolyJoinType]. Each polygon's endpoints will be rounded as determined by [code]end_type[/code], see [enum PolyEndType]. - The operation may result in an outer polygon (boundary) and inner polygon (hole) produced which could be distinguished by calling [method is_polygon_clockwise]. + The operation may result in outer polygons (boundary) and inner polygons (hole) produced which could be distinguished by calling [method is_polygon_clockwise]. + [code]polylines[/code] parameter can either accept [PoolVector2Array] to be interpreted as a single polyline or [Array] of [PoolVector2Array]'s to accept a list of polylines. @@ -486,7 +493,7 @@ Create regions where either subject or clip polygons are filled but not where both are filled. - Squaring is applied uniformally at all convex edge joins at [code]1 * delta[/code]. + Squaring is applied uniformly at all convex edge joins at [code]1 * delta[/code]. While flattened paths can never perfectly trace an arc, they are approximated by a series of arc chords.