diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 583a44846ff2..6e5dcc1c695b 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -1680,9 +1680,9 @@ Array _Geometry::intersect_polyline_with_polygon_2d(const Vector &p_pol return ret; } -Array _Geometry::offset_polygon_2d(const Vector &p_polygon, real_t p_delta, PolyJoinType p_join_type) { +Array _Geometry::offset_polygon_2d(const Vector &p_polygon, real_t p_delta, PolyConnection p_con_type, real_t p_miter_limit, real_t p_arc_tolerance) { - Vector > polys = Geometry::offset_polygon_2d(p_polygon, p_delta, Geometry::PolyJoinType(p_join_type)); + Vector > polys = Geometry::offset_polygon_2d(p_polygon, p_delta, Geometry::PolyConnection(p_con_type), p_miter_limit, p_arc_tolerance); Array ret; @@ -1692,9 +1692,9 @@ Array _Geometry::offset_polygon_2d(const Vector &p_polygon, real_t p_de return ret; } -Array _Geometry::offset_polyline_2d(const Vector &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { +Array _Geometry::offset_polyline_2d(const Vector &p_polygon, real_t p_delta, PolyConnection p_con_type, real_t p_miter_limit, real_t p_arc_tolerance) { - Vector > polys = Geometry::offset_polyline_2d(p_polygon, p_delta, Geometry::PolyJoinType(p_join_type), Geometry::PolyEndType(p_end_type)); + Vector > polys = Geometry::offset_polyline_2d(p_polygon, p_delta, Geometry::PolyConnection(p_con_type), p_miter_limit, p_arc_tolerance); Array ret; @@ -1780,8 +1780,8 @@ void _Geometry::_bind_methods() { 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("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_polygon_2d", "polygon", "delta", "connection_type", "miter_limit", "arc_tolerance"), &_Geometry::offset_polygon_2d, DEFVAL(JOIN_SQUARE), DEFVAL(2.0), DEFVAL(0.25)); + ClassDB::bind_method(D_METHOD("offset_polyline_2d", "polyline", "delta", "connection_type", "miter_limit", "arc_tolerance"), &_Geometry::offset_polyline_2d, DEFVAL(JOIN_SQUARE | END_SQUARE), DEFVAL(2.0), DEFVAL(0.25)); ClassDB::bind_method(D_METHOD("make_atlas", "sizes"), &_Geometry::make_atlas); @@ -1793,7 +1793,6 @@ void _Geometry::_bind_methods() { BIND_ENUM_CONSTANT(JOIN_SQUARE); BIND_ENUM_CONSTANT(JOIN_ROUND); BIND_ENUM_CONSTANT(JOIN_MITER); - BIND_ENUM_CONSTANT(END_POLYGON); BIND_ENUM_CONSTANT(END_JOINED); BIND_ENUM_CONSTANT(END_BUTT); diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 342c43802ef0..c3509da51806 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -411,20 +411,18 @@ class _Geometry : public Object { Array intersect_polyline_with_polygon_2d(const Vector &p_polyline, const Vector &p_polygon); // Chop. // 2D offset polygons/polylines. - enum PolyJoinType { - JOIN_SQUARE, - JOIN_ROUND, - JOIN_MITER + enum PolyConnection { + JOIN_SQUARE = 1 << 0, + JOIN_ROUND = 1 << 1, + JOIN_MITER = 1 << 2, + END_POLYGON = 1 << 3, + END_JOINED = 1 << 4, + END_BUTT = 1 << 5, + END_SQUARE = 1 << 6, + END_ROUND = 1 << 7 }; - enum PolyEndType { - END_POLYGON, - END_JOINED, - END_BUTT, - 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_polygon_2d(const Vector &p_polygon, real_t p_delta, PolyConnection p_con_type = JOIN_SQUARE, real_t p_miter_limit = 2.0, real_t p_arc_tolerance = 0.25); + Array offset_polyline_2d(const Vector &p_polygon, real_t p_delta, PolyConnection p_con_type = PolyConnection(JOIN_SQUARE | END_SQUARE), real_t p_miter_limit = 2.0, real_t p_arc_tolerance = 0.25); Dictionary make_atlas(const Vector &p_rects); @@ -432,8 +430,7 @@ class _Geometry : public Object { }; VARIANT_ENUM_CAST(_Geometry::PolyBooleanOperation); -VARIANT_ENUM_CAST(_Geometry::PolyJoinType); -VARIANT_ENUM_CAST(_Geometry::PolyEndType); +VARIANT_ENUM_CAST(_Geometry::PolyConnection); class _File : public Reference { diff --git a/core/math/geometry.cpp b/core/math/geometry.cpp index 69c7abfd303e..05f89098d971 100644 --- a/core/math/geometry.cpp +++ b/core/math/geometry.cpp @@ -1128,28 +1128,35 @@ Vector > Geometry::_polypaths_do_operation(PolyBooleanOperation p return polypaths; } -Vector > Geometry::_polypath_offset(const Vector &p_polypath, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { +Vector > Geometry::_polypath_offset(const Vector &p_polypath, real_t p_delta, PolyConnection p_con_type, real_t p_miter_limit, real_t p_arc_tolerance) { using namespace ClipperLib; JoinType jt = jtSquare; - switch (p_join_type) { - case JOIN_SQUARE: jt = jtSquare; break; - case JOIN_ROUND: jt = jtRound; break; - case JOIN_MITER: jt = jtMiter; break; + if (p_con_type & JOIN_SQUARE) { + jt = jtSquare; + } else if (p_con_type & JOIN_ROUND) { + jt = jtRound; + } else if (p_con_type & JOIN_MITER) { + jt = jtMiter; } EndType et = etClosedPolygon; - switch (p_end_type) { - case END_POLYGON: et = etClosedPolygon; break; - case END_JOINED: et = etClosedLine; break; - case END_BUTT: et = etOpenButt; break; - case END_SQUARE: et = etOpenSquare; break; - case END_ROUND: et = etOpenRound; break; + if (p_con_type & END_POLYGON) { + et = etClosedPolygon; + } else if (p_con_type & END_JOINED) { + et = etClosedLine; + } else if (p_con_type & END_BUTT) { + et = etOpenButt; + } else if (p_con_type & END_SQUARE) { + et = etOpenSquare; + } else if (p_con_type & END_ROUND) { + et = etOpenRound; } - ClipperOffset co(2.0, 0.25 * SCALE_FACTOR); // Defaults from ClipperOffset. + + ClipperOffset co(p_miter_limit, p_arc_tolerance * SCALE_FACTOR); Path path; // Need to scale points (Clipper's requirement for robust computation). diff --git a/core/math/geometry.h b/core/math/geometry.h index a94d00bf77a4..2396adbdb49a 100644 --- a/core/math/geometry.h +++ b/core/math/geometry.h @@ -777,17 +777,15 @@ class Geometry { OPERATION_INTERSECTION, OPERATION_XOR }; - enum PolyJoinType { - JOIN_SQUARE, - JOIN_ROUND, - JOIN_MITER - }; - enum PolyEndType { - END_POLYGON, - END_JOINED, - END_BUTT, - END_SQUARE, - END_ROUND + enum PolyConnection { + JOIN_SQUARE = 1 << 0, + JOIN_ROUND = 1 << 1, + JOIN_MITER = 1 << 2, + END_POLYGON = 1 << 3, + END_JOINED = 1 << 4, + END_BUTT = 1 << 5, + END_SQUARE = 1 << 6, + END_ROUND = 1 << 7 }; static Vector > merge_polygons_2d(const Vector &p_polygon_a, const Vector &p_polygon_b) { @@ -820,16 +818,16 @@ class Geometry { return _polypaths_do_operation(OPERATION_INTERSECTION, p_polyline, p_polygon, true); } - static Vector > offset_polygon_2d(const Vector &p_polygon, real_t p_delta, PolyJoinType p_join_type) { + static Vector > offset_polygon_2d(const Vector &p_polygon, real_t p_delta, PolyConnection p_con_type, real_t p_miter_limit, real_t p_arc_tolerance) { - return _polypath_offset(p_polygon, p_delta, p_join_type, END_POLYGON); + return _polypath_offset(p_polygon, p_delta, PolyConnection(p_con_type | END_POLYGON), p_miter_limit, p_arc_tolerance); } - static Vector > offset_polyline_2d(const Vector &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { + static Vector > offset_polyline_2d(const Vector &p_polygon, real_t p_delta, PolyConnection p_con_type, real_t p_miter_limit, real_t p_arc_tolerance) { - ERR_FAIL_COND_V_MSG(p_end_type == END_POLYGON, Vector >(), "Attempt to offset a polyline like a polygon (use offset_polygon_2d instead)."); + ERR_FAIL_COND_V_MSG(p_con_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 _polypath_offset(p_polygon, p_delta, p_con_type, p_miter_limit, p_arc_tolerance); } static Vector triangulate_delaunay_2d(const Vector &p_points) { @@ -1016,7 +1014,7 @@ class Geometry { 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 > _polypath_offset(const Vector &p_polypath, real_t p_delta, PolyConnection p_con_type, real_t p_miter_limit = 2.0, real_t p_arc_tolerance = 0.25); // Defaults from ClipperOffset. }; #endif diff --git a/doc/classes/Geometry.xml b/doc/classes/Geometry.xml index 9d4e0d03886b..f71fa0dec836 100644 --- a/doc/classes/Geometry.xml +++ b/doc/classes/Geometry.xml @@ -296,11 +296,17 @@ - + + + + + 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. - Each polygon's vertices will be rounded as determined by [code]join_type[/code], see [enum PolyJoinType]. + Each polygon's vertices will be rounded as determined by [code]connection_type[/code] from one of the [code]JOIN_*[/code] types, see [enum PolyConnection]. + [code]miter_limit[/code] sets the maximum distance in multiples of [code]delta[/code] that vertices can be offset from their original positions before squaring is applied. The default value is 2.0 (twice delta) which is the smallest value that's allowed to avoid generating unacceptably long 'spikes' at very acute angles. + [code]arc_tolerance[/code] sets the maximum distance the flattened path will deviate from the mathematical representation of an arc. Smaller values will increase smoothness at a cost of performance. Only relevant when [code]connection_type[/code] is set to [code]JOIN_ROUND[/code]. The operation may result in an outer polygon (boundary) and inner polygon (hole) produced which could be distinguished by calling [method is_polygon_clockwise]. @@ -311,14 +317,22 @@ - + + + - + 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. - 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]. + Each polygon's intermediate vertices and endpoints will be rounded as determined by [code]connection_type[/code] from a combination of [code]JOIN_*[/code] and [code]END_*[/code] types respectively, see [enum PolyConnection]. For instance: + [codeblock] + # Produces a thin corridor with round corner passages and dead-ends. + var polyline = [Vector2(0, 0), Vector2(100, 0), Vector2(100, 100), Vector2(0, 100)] + var polygons = Geometry.offset_polyline_2d(polyline, 20.0, Geometry.JOIN_ROUND | Geometry.END_BUTT) + [/codeblock] + [code]miter_limit[/code] sets the maximum distance in multiples of [code]delta[/code] that vertices can be offset from their original positions before squaring is applied. The default value is 2.0 (twice delta) which is the smallest value that's allowed to avoid generating unacceptably long 'spikes' at very acute angles. + [code]arc_tolerance[/code] sets the maximum distance the flattened path will deviate from the mathematical representation of an arc. Smaller values will increase smoothness at a cost of performance. Only relevant when [code]connection_type[/code] is set to [code]JOIN_ROUND[/code]. The operation may result in an outer polygon (boundary) and inner polygon (hole) produced which could be distinguished by calling [method is_polygon_clockwise]. @@ -476,28 +490,28 @@ 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. - + There's a necessary limit to mitered joins since offsetting edges that join at very acute angles will produce excessively long and narrow "spikes". For any given edge join, when miter offsetting would exceed that maximum distance, "square" joining is applied. - - Endpoints are joined using the [enum PolyJoinType] value and the path filled as a polygon. + + Endpoints are joined using one of the [code]JOIN_*[/code] values and the path filled as a polygon. - - Endpoints are joined using the [enum PolyJoinType] value and the path filled as a polyline. + + Endpoints are joined using one of the [code]JOIN_*[/code] values and the path filled as a polyline. - + Endpoints are squared off with no extension. - + Endpoints are squared off and extended by [code]delta[/code] units. - + Endpoints are rounded off and extended by [code]delta[/code] units.