Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to perform boolean operations on array of polypaths #29881

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 95 additions & 58 deletions core/bind/core_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1593,100 +1593,137 @@ Vector<Vector3> _Geometry::clip_polygon(const Vector<Vector3> &p_points, const P
return Geometry::clip_polygon(p_points, p_plane);
}

Array _Geometry::merge_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
Array _Geometry::merge_polygons_2d(const Variant &p_subject) {

Vector<Vector<Point2> > 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<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
Array _Geometry::intersect_polygons_2d(const Variant &p_subject, const Variant &p_clip) {

Vector<Vector<Point2> > 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<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
Array _Geometry::clip_polylines_with_polygons_2d(const Variant &p_polylines, const Variant &p_polygons) {

Vector<Vector<Point2> > 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<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
Array _Geometry::offset_polygons_2d(const Variant &p_polygons, real_t p_delta, PolyJoinType p_join_type) {

Vector<Vector<Point2> > polys = Geometry::exclude_polygons_2d(p_polygon_a, p_polygon_b);
Vector<Vector<Point2> > polygons;
_convert_polypaths(p_polygons, polygons);

Array ret;
Vector<Vector<Point2> > 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<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) {
Array _Geometry::offset_polylines_2d(const Variant &p_polylines, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) {

Vector<Vector<Point2> > polys = Geometry::clip_polyline_with_polygon_2d(p_polyline, p_polygon);
Vector<Vector<Point2> > polylines;
_convert_polypaths(p_polylines, polylines);

Array ret;
Vector<Vector<Point2> > 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<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) {
Array _Geometry::_polypaths_do_operation(PolyBooleanOperation p_op, const Variant &p_subject, const Variant &p_clip, bool is_subject_open) {

Vector<Vector<Point2> > polys = Geometry::intersect_polyline_with_polygon_2d(p_polyline, p_polygon);
Vector<Vector<Point2> > solution;

Array ret;
Vector<Vector<Point2> > 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<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type) {
solution = Geometry::merge_polygons_2d(subject);
} else {

Vector<Vector<Point2> > polys = Geometry::offset_polygon_2d(p_polygon, p_delta, Geometry::PolyJoinType(p_join_type));
Vector<Vector<Point2> > 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<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) {
void _Geometry::_convert_polypaths(const Variant &p_input, Vector<Vector<Point2> > &r_output) {

Vector<Vector<Point2> > 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<Point2> _Geometry::transform_points_2d(const Vector<Point2> &p_points, const Transform2D &p_mat) {
Expand Down Expand Up @@ -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);

Expand Down
20 changes: 12 additions & 8 deletions core/bind/core_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -409,14 +409,14 @@ class _Geometry : public Object {
OPERATION_XOR
};
// 2D polygon boolean operations.
Array merge_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // Union (add).
Array clip_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // Difference (subtract).
Array intersect_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // Common area (multiply).
Array exclude_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &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<Vector2> &p_polyline, const Vector<Vector2> &p_polygon); // Cut.
Array intersect_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &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 {
Expand All @@ -431,14 +431,18 @@ class _Geometry : public Object {
END_SQUARE,
END_ROUND
};
Array offset_polygon_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type = JOIN_SQUARE);
Array offset_polyline_2d(const Vector<Vector2> &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<Point2> transform_points_2d(const Vector<Point2> &p_points, const Transform2D &p_mat);

Dictionary make_atlas(const Vector<Size2> &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<Vector<Point2> > &r_output);
};

VARIANT_ENUM_CAST(_Geometry::PolyBooleanOperation);
Expand Down
Loading