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

Replace Clipper1 library by Clipper2 library #90153

Merged
merged 1 commit into from
Apr 22, 2024
Merged
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
6 changes: 0 additions & 6 deletions COPYRIGHT.txt
Original file line number Diff line number Diff line change
Expand Up @@ -360,12 +360,6 @@ Copyright: 1998-2010, Gilles Vollant
2009-2010, Mathias Svensson
License: Zlib

Files: ./thirdparty/misc/clipper.cpp
./thirdparty/misc/clipper.hpp
Comment: Clipper
Copyright: 2010-2017, Angus Johnson
License: BSL-1.0

Files: ./thirdparty/misc/cubemap_coeffs.h
Comment: Fast Filtering of Reflection Probes
Copyright: 2016, Activision Publishing, Inc.
Expand Down
1 change: 0 additions & 1 deletion core/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ thirdparty_misc_sources = [
# C++ sources
"pcg.cpp",
"polypartition.cpp",
"clipper.cpp",
"smolv.cpp",
]
thirdparty_misc_sources = [thirdparty_misc_dir + file for file in thirdparty_misc_sources]
Expand Down
111 changes: 53 additions & 58 deletions core/math/geometry_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@

#include "geometry_2d.h"

#include "thirdparty/misc/clipper.hpp"
#include "thirdparty/clipper2/include/clipper2/clipper.h"
#include "thirdparty/misc/polypartition.h"
#define STB_RECT_PACK_IMPLEMENTATION
#include "thirdparty/misc/stb_rect_pack.h"

#define SCALE_FACTOR 100000.0 // Based on CMP_EPSILON.
#define PRECISION 5 // Based on CMP_EPSILON.

Vector<Vector<Vector2>> Geometry2D::decompose_polygon_in_convex(const Vector<Point2> &polygon) {
Vector<Vector<Vector2>> decomp;
Expand Down Expand Up @@ -196,126 +196,121 @@ void Geometry2D::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_re
}

Vector<Vector<Point2>> Geometry2D::_polypaths_do_operation(PolyBooleanOperation p_op, const Vector<Point2> &p_polypath_a, const Vector<Point2> &p_polypath_b, bool is_a_open) {
using namespace ClipperLib;
using namespace Clipper2Lib;

ClipType op = ctUnion;
ClipType op = ClipType::Union;

switch (p_op) {
case OPERATION_UNION:
op = ctUnion;
op = ClipType::Union;
break;
case OPERATION_DIFFERENCE:
op = ctDifference;
op = ClipType::Difference;
break;
case OPERATION_INTERSECTION:
op = ctIntersection;
op = ClipType::Intersection;
break;
case OPERATION_XOR:
op = ctXor;
op = ClipType::Xor;
break;
}
Path path_a, path_b;

// Need to scale points (Clipper's requirement for robust computation).
PathD path_a(p_polypath_a.size());
for (int i = 0; i != p_polypath_a.size(); ++i) {
path_a << IntPoint(p_polypath_a[i].x * (real_t)SCALE_FACTOR, p_polypath_a[i].y * (real_t)SCALE_FACTOR);
path_a[i] = PointD(p_polypath_a[i].x, p_polypath_a[i].y);
}
PathD path_b(p_polypath_b.size());
for (int i = 0; i != p_polypath_b.size(); ++i) {
path_b << IntPoint(p_polypath_b[i].x * (real_t)SCALE_FACTOR, p_polypath_b[i].y * (real_t)SCALE_FACTOR);
path_b[i] = PointD(p_polypath_b[i].x, p_polypath_b[i].y);
}
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.

Paths paths;
ClipperD clp(PRECISION); // Scale points up internally to attain the desired precision.
clp.PreserveCollinear(false); // Remove redundant vertices.
if (is_a_open) {
clp.AddOpenSubject({ path_a });
} else {
clp.AddSubject({ path_a });
}
clp.AddClip({ path_b });

PathsD paths;

if (is_a_open) {
PolyTree tree; // Needed to populate polylines.
clp.Execute(op, tree);
OpenPathsFromPolyTree(tree, paths);
PolyTreeD tree; // Needed to populate polylines.
clp.Execute(op, FillRule::EvenOdd, tree, paths);
} else {
clp.Execute(op, paths); // Works on closed polygons only.
clp.Execute(op, FillRule::EvenOdd, paths); // Works on closed polygons only.
}
// Have to scale points down now.

Vector<Vector<Point2>> polypaths;
for (PathsD::size_type i = 0; i < paths.size(); ++i) {
const PathD &path = paths[i];

for (Paths::size_type i = 0; i < paths.size(); ++i) {
Vector<Vector2> polypath;

const Path &scaled_path = paths[i];

for (Paths::size_type j = 0; j < scaled_path.size(); ++j) {
polypath.push_back(Point2(
static_cast<real_t>(scaled_path[j].X) / (real_t)SCALE_FACTOR,
static_cast<real_t>(scaled_path[j].Y) / (real_t)SCALE_FACTOR));
for (PathsD::size_type j = 0; j < path.size(); ++j) {
polypath.push_back(Point2(static_cast<real_t>(path[j].x), static_cast<real_t>(path[j].y)));
}
polypaths.push_back(polypath);
}
return polypaths;
}

Vector<Vector<Point2>> Geometry2D::_polypath_offset(const Vector<Point2> &p_polypath, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) {
using namespace ClipperLib;
using namespace Clipper2Lib;

JoinType jt = jtSquare;
JoinType jt = JoinType::Square;

switch (p_join_type) {
case JOIN_SQUARE:
jt = jtSquare;
jt = JoinType::Square;
break;
case JOIN_ROUND:
jt = jtRound;
jt = JoinType::Round;
break;
case JOIN_MITER:
jt = jtMiter;
jt = JoinType::Miter;
break;
}

EndType et = etClosedPolygon;
EndType et = EndType::Polygon;

switch (p_end_type) {
case END_POLYGON:
et = etClosedPolygon;
et = EndType::Polygon;
break;
case END_JOINED:
et = etClosedLine;
et = EndType::Joined;
break;
case END_BUTT:
et = etOpenButt;
et = EndType::Butt;
break;
case END_SQUARE:
et = etOpenSquare;
et = EndType::Square;
break;
case END_ROUND:
et = etOpenRound;
et = EndType::Round;
break;
}
ClipperOffset co(2.0, 0.25f * (real_t)SCALE_FACTOR); // Defaults from ClipperOffset.
Path path;

// Need to scale points (Clipper's requirement for robust computation).
PathD polypath(p_polypath.size());
for (int i = 0; i != p_polypath.size(); ++i) {
path << IntPoint(p_polypath[i].x * (real_t)SCALE_FACTOR, p_polypath[i].y * (real_t)SCALE_FACTOR);
polypath[i] = PointD(p_polypath[i].x, p_polypath[i].y);
}
co.AddPath(path, jt, et);

Paths paths;
co.Execute(paths, p_delta * (real_t)SCALE_FACTOR); // Inflate/deflate.
// Inflate/deflate.
PathsD paths = InflatePaths({ polypath }, p_delta, jt, et, 2.0, PRECISION, 0.0);
// Here the miter_limit = 2.0 and arc_tolerance = 0.0 are Clipper2 defaults,
// and the PRECISION is used to scale points up internally, to attain the desired precision.

// Have to scale points down now.
Vector<Vector<Point2>> polypaths;
for (PathsD::size_type i = 0; i < paths.size(); ++i) {
const PathD &path = paths[i];

for (Paths::size_type i = 0; i < paths.size(); ++i) {
Vector<Vector2> polypath;

const Path &scaled_path = paths[i];

for (Paths::size_type j = 0; j < scaled_path.size(); ++j) {
polypath.push_back(Point2(
static_cast<real_t>(scaled_path[j].X) / (real_t)SCALE_FACTOR,
static_cast<real_t>(scaled_path[j].Y) / (real_t)SCALE_FACTOR));
Vector<Vector2> polypath2;
for (PathsD::size_type j = 0; j < path.size(); ++j) {
polypath2.push_back(Point2(static_cast<real_t>(path[j].x), static_cast<real_t>(path[j].y)));
}
polypaths.push_back(polypath);
polypaths.push_back(polypath2);
}
return polypaths;
}
Expand Down
63 changes: 23 additions & 40 deletions editor/plugins/sprite_2d_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@
#include "scene/gui/menu_button.h"
#include "scene/gui/panel.h"
#include "scene/gui/view_panner.h"
#include "thirdparty/misc/clipper.hpp"
#include "thirdparty/clipper2/include/clipper2/clipper.h"

#define PRECISION 1

void Sprite2DEditor::_node_removed(Node *p_node) {
if (p_node == node) {
Expand All @@ -59,58 +61,39 @@ void Sprite2DEditor::edit(Sprite2D *p_sprite) {
node = p_sprite;
}

#define PRECISION 10.0

Vector<Vector2> expand(const Vector<Vector2> &points, const Rect2i &rect, float epsilon = 2.0) {
int size = points.size();
ERR_FAIL_COND_V(size < 2, Vector<Vector2>());

ClipperLib::Path subj;
ClipperLib::PolyTree solution;
ClipperLib::PolyTree out;

Clipper2Lib::PathD subj(points.size());
for (int i = 0; i < points.size(); i++) {
subj << ClipperLib::IntPoint(points[i].x * PRECISION, points[i].y * PRECISION);
subj[i] = Clipper2Lib::PointD(points[i].x, points[i].y);
}
ClipperLib::ClipperOffset co;
co.AddPath(subj, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
co.Execute(solution, epsilon * PRECISION);

ClipperLib::PolyNode *p = solution.GetFirst();
Clipper2Lib::PathsD solution = Clipper2Lib::InflatePaths({ subj }, epsilon, Clipper2Lib::JoinType::Miter, Clipper2Lib::EndType::Polygon, 2.0, PRECISION, 0.0);
// Here the miter_limit = 2.0 and arc_tolerance = 0.0 are Clipper2 defaults,
// and PRECISION is used to scale points up internally, to attain the desired precision.

ERR_FAIL_NULL_V(p, points);
ERR_FAIL_COND_V(solution.size() == 0, points);

while (p->IsHole()) {
p = p->GetNext();
}
// Clamp into the specified rect.
Clipper2Lib::RectD clamp(rect.position.x,
rect.position.y,
rect.position.x + rect.size.width,
rect.position.y + rect.size.height);
Clipper2Lib::PathsD out = Clipper2Lib::RectClip(clamp, solution[0], PRECISION);
// Here PRECISION is used to scale points up internally, to attain the desired precision.

//turn the result into simply polygon (AKA, fix overlap)

//clamp into the specified rect
ClipperLib::Clipper cl;
cl.StrictlySimple(true);
cl.AddPath(p->Contour, ClipperLib::ptSubject, true);
//create the clipping rect
ClipperLib::Path clamp;
clamp.push_back(ClipperLib::IntPoint(0, 0));
clamp.push_back(ClipperLib::IntPoint(rect.size.width * PRECISION, 0));
clamp.push_back(ClipperLib::IntPoint(rect.size.width * PRECISION, rect.size.height * PRECISION));
clamp.push_back(ClipperLib::IntPoint(0, rect.size.height * PRECISION));
cl.AddPath(clamp, ClipperLib::ptClip, true);
cl.Execute(ClipperLib::ctIntersection, out);
ERR_FAIL_COND_V(out.size() == 0, points);

Vector<Vector2> outPoints;
ClipperLib::PolyNode *p2 = out.GetFirst();
ERR_FAIL_NULL_V(p2, points);
const Clipper2Lib::PathD &p2 = out[0];

while (p2->IsHole()) {
p2 = p2->GetNext();
}
Vector<Vector2> outPoints;

int lasti = p2->Contour.size() - 1;
Vector2 prev = Vector2(p2->Contour[lasti].X / PRECISION, p2->Contour[lasti].Y / PRECISION);
for (uint64_t i = 0; i < p2->Contour.size(); i++) {
Vector2 cur = Vector2(p2->Contour[i].X / PRECISION, p2->Contour[i].Y / PRECISION);
int lasti = p2.size() - 1;
Vector2 prev = Vector2(p2[lasti].x, p2[lasti].y);
for (uint64_t i = 0; i < p2.size(); i++) {
Vector2 cur = Vector2(p2[i].x, p2[i].y);
if (cur.distance_to(prev) > 0.5) {
outPoints.push_back(cur);
prev = cur;
Expand Down
8 changes: 4 additions & 4 deletions tests/core/math/test_geometry_2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -711,12 +711,12 @@ TEST_CASE("[Geometry2D] Clip polyline with polygon") {
r = Geometry2D::clip_polyline_with_polygon(l, p);
REQUIRE_MESSAGE(r.size() == 2, "There should be 2 resulting clipped lines.");
REQUIRE_MESSAGE(r[0].size() == 3, "The resulting clipped line should have 3 vertices.");
CHECK(r[0][0].is_equal_approx(Vector2(160, 320)));
CHECK(r[0][0].is_equal_approx(Vector2(121.412682, 225.038757)));
CHECK(r[0][1].is_equal_approx(Vector2(122, 250)));
CHECK(r[0][2].is_equal_approx(Vector2(121.412682, 225.038757)));
CHECK(r[0][2].is_equal_approx(Vector2(160, 320)));
REQUIRE_MESSAGE(r[1].size() == 2, "The resulting clipped line should have 2 vertices.");
CHECK(r[1][0].is_equal_approx(Vector2(53.07737, 116.143021)));
CHECK(r[1][1].is_equal_approx(Vector2(55, 70)));
CHECK(r[1][0].is_equal_approx(Vector2(55, 70)));
CHECK(r[1][1].is_equal_approx(Vector2(53.07737, 116.143021)));
rburing marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down
Loading
Loading