From 234f062ad424081df0e9889135aab185b2bc6438 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 30 Sep 2021 15:49:12 +0200 Subject: [PATCH 1/3] Fast convex polygon intersection test with rotating calipers --- src/libslic3r/Geometry.cpp | 211 +++++++++++++++++++++++++++++- src/libslic3r/Geometry.hpp | 2 + tests/libslic3r/CMakeLists.txt | 1 + tests/libslic3r/test_geometry.cpp | 173 ++++++++++++++++++++++++ 4 files changed, 386 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 321443204..bbb1f0d6b 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -20,6 +20,26 @@ #include #include +#if defined(_MSC_VER) && defined(__clang__) +#define BOOST_NO_CXX17_HDR_STRING_VIEW +#endif + +#include + +#include + +namespace Slic3r { + +#if !defined(HAS_INTRINSIC_128_TYPE) || defined(__APPLE__) +using int128_t = boost::multiprecision::int128_t; +#else +using int128_t = __int128; +#endif + +using int256_t = boost::multiprecision::int256_t; + +} // namespace Slic3r + #ifdef SLIC3R_DEBUG #include "SVG.hpp" #endif @@ -1543,4 +1563,193 @@ double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to) return (axis.z() < 0) ? -angle : angle; } -} } +namespace rotcalip { + +inline int128_t magnsq(const Point &p) +{ + return int128_t(p.x()) * p.x() + int64_t(p.y()) * p.y(); +} + +inline int128_t dot(const Point &a, const Point &b) +{ + return int128_t(a.x()) * b.x() + int64_t(a.y()) * b.y(); +} + +// Compares the angle enclosed by vectors dir and dirA (alpha) with the angle +// enclosed by -dir and dirB (beta). Returns -1 if alpha is less than beta, 0 +// if they are equal and 1 if alpha is greater than beta. Note that dir is +// reversed for beta, because it represents the opposite side of a caliper. +int cmp_angles(const Point &dir, const Point &dirA, const Point &dirB) { + int128_t dotA = dot(dir, dirA); + int128_t dotB = dot(-dir, dirB); + int256_t dcosa = int256_t(magnsq(dirB)) * int256_t(std::abs(dotA)) * dotA; + int256_t dcosb = int256_t(magnsq(dirA)) * int256_t(std::abs(dotB)) * dotB; + int256_t diff = dcosa - dcosb; + + return diff > 0? -1 : (diff < 0 ? 1 : 0); +} + +// A helper class to navigate on a polygon. Given a vertex index, one can +// get the edge belonging to that vertex, the coordinates of the vertex, the +// next and previous edges. Stuff that is needed in the rotating calipers algo. +class Idx +{ + size_t m_idx; + const Polygon *m_poly; +public: + explicit Idx(const Polygon &p): m_idx{0}, m_poly{&p} {} + explicit Idx(size_t idx, const Polygon &p): m_idx{idx}, m_poly{&p} {} + + size_t idx() const { return m_idx; } + void set_idx(size_t i) { m_idx = i; } + size_t next() const { return (m_idx + 1) % m_poly->size(); } + size_t inc() { return m_idx = (m_idx + 1) % m_poly->size(); } + Point prev_dir() const { + return pt() - (*m_poly)[(m_idx + m_poly->size() - 1) % m_poly->size()]; + } + + const Point &pt() const { return (*m_poly)[m_idx]; } + const Point dir() const { return (*m_poly)[next()] - pt(); } + const Point next_dir() const + { + return (*m_poly)[(m_idx + 2) % m_poly->size()] - (*m_poly)[next()]; + } + const Polygon &poly() const { return *m_poly; } +}; + +enum class AntipodalVisitMode { Full, SkipParallelSegments }; + +// Visit all antipodal pairs starting from the initial ia, ib pair which +// has to be a valid antipodal pair (not checked). fn is called for every +// antipodal pair encountered including the initial one. +// The callback Fn has a signiture of bool(size_t i, size_t j, const Point &dir) +// where i,j are the vertex indices of the antipodal pair and dir is the +// direction of the calipers touching the i vertex. +template +void visit_antipodals (Idx& ia, Idx &ib, Fn &&fn) +{ + // Set current caliper direction to be the lower edge angle from X axis + int cmp = cmp_angles(ia.prev_dir(), ia.dir(), ib.dir()); + Idx *current = cmp <= 0 ? &ia : &ib, *other = cmp <= 0 ? &ib : &ia; + bool visitor_continue = true; + + size_t a_start = ia.idx(), b_start = ib.idx(); + bool a_finished = false, b_finished = false; + + while (visitor_continue && !(a_finished && b_finished)) { + Point current_dir_a = current == &ia ? current->dir() : -current->dir(); + visitor_continue = fn(ia.idx(), ib.idx(), current_dir_a); + + if constexpr (mode == AntipodalVisitMode::Full) + if (cmp == 0 && visitor_continue) { + visitor_continue = fn(current == &ia ? ia.idx() : ia.next(), + current == &ib ? ib.idx() : ib.next(), + current_dir_a); + } + + cmp = cmp_angles(current->dir(), current->next_dir(), other->dir()); + + current->inc(); + if (cmp > 0) { + std::swap(current, other); + } + + if (ia.idx() == a_start) a_finished = true; + if (ib.idx() == b_start) b_finished = true; + } +} + +static bool is_left(const Point &a, const Point &b, const Point &c) +{ + Vec<2, int64_t> V = (b - a).cast(); + Vec<2, int64_t> W = (c - a).cast(); + + return V.x() * W.y() - V.y() * W.x() > 0; +} + +} // namespace rotcalip + +bool intersects(const Polygon &A, const Polygon &B) +{ + using namespace rotcalip; + + // Establish starting antipodals as extremes in XY plane. Use the + // easily obtainable bounding boxes to check if A and B is disjoint + // and return false if the are. + + struct BB + { + size_t xmin = 0, xmax = 0, ymin = 0, ymax = 0; + const Polygon &P; + static bool cmpy(const Point &l, const Point &u) + { + return l.y() < u.y() || (l.y() == u.y() && l.x() < u.x()); + } + + BB(const Polygon &poly): P{poly} + { + for (size_t i = 0; i < P.size(); ++i) { + if (P[i] < P[xmin]) xmin = i; + if (P[xmax] < P[i]) xmax = i; + if (cmpy(P[i], P[ymin])) ymin = i; + if (cmpy(P[ymax], P[i])) ymax = i; + } + } + }; + + BB bA{A}, bB{B}; + BoundingBox bbA{{A[bA.xmin].x(), A[bA.ymin].y()}, {A[bA.xmax].x(), A[bA.ymax].y()}}; + BoundingBox bbB{{B[bB.xmin].x(), B[bB.ymin].y()}, {B[bB.xmax].x(), B[bB.ymax].y()}}; + + if (!bbA.overlap(bbB)) + return false; + + // Establish starting antipodals as extreme vertex pairs in X or Y direction + // which reside on different polygons. If no such pair is found, the two + // polygons are certainly not disjoint. + Idx imin{bA.xmin, A}, imax{bB.xmax, B}; + if (B[bB.xmin] < imin.pt()) imin = Idx{bB.xmin, B}; + if (imax.pt() < A[bA.xmax]) imax = Idx{bA.xmax, A}; + if (&imin.poly() == &imax.poly()) { + imin = Idx{bA.ymin, A}; + imax = Idx{bB.ymax, B}; + if (B[bB.ymin] < imin.pt()) imin = Idx{bB.ymin, B}; + if (imax.pt() < A[bA.ymax]) imax = Idx{bA.ymax, A}; + } + + if (&imin.poly() == &imax.poly()) + return true; + + bool found_divisor; + visit_antipodals( + imin, imax, + [&imin, &imax, &found_divisor](size_t ia, size_t ib, const Point &dir) { + // std::cout << "A" << ia << " B" << ib << " dir " << + // dir.x() << " " << dir.y() << std::endl; + const Polygon &A = imin.poly(), &B = imax.poly(); + + Point ref_a = A[(ia + 2) % A.size()], + ref_b = B[(ib + 2) % B.size()]; + Point Anext = A[ia] + dir; + bool is_left_a = is_left(A[ia], Anext, ref_a); + bool is_left_b = is_left(B[ib], B[ib] - dir, ref_b); + + // If both reference points are on the left (or right) of the + // support line and the opposite support line is to the righ (or + // left), the divisor line is found. We only test the reference + // point, as by definition, if that is on one side, all the other + // points must be on the same side of a support line. + if (is_left(A[ia], Anext, B[ib])) { + found_divisor = !is_left_a && !is_left_b; + } else { + found_divisor = is_left_a && is_left_b; + } + + return !found_divisor; + }); + + // Intersects if the divisor was not found + return !found_divisor; +} + +}} // namespace Slic3r::Geometry diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index c6af515c8..bb583c33c 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -532,6 +532,8 @@ inline bool is_rotation_ninety_degrees(const Vec3d &rotation) return is_rotation_ninety_degrees(rotation.x()) && is_rotation_ninety_degrees(rotation.y()) && is_rotation_ninety_degrees(rotation.z()); } +bool intersects(const Polygon &convex_poly1, const Polygon &convex_poly2); + } } // namespace Slicer::Geometry #endif diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index 575878cf2..05898db28 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -23,6 +23,7 @@ add_executable(${_TEST_NAME}_tests test_png_io.cpp test_timeutils.cpp test_indexed_triangle_set.cpp + ../libnest2d/printer_parts.cpp ) if (TARGET OpenVDB::openvdb) diff --git a/tests/libslic3r/test_geometry.cpp b/tests/libslic3r/test_geometry.cpp index 24e0908cc..7373eaa6a 100644 --- a/tests/libslic3r/test_geometry.cpp +++ b/tests/libslic3r/test_geometry.cpp @@ -9,6 +9,14 @@ #include "libslic3r/ClipperUtils.hpp" #include "libslic3r/ShortestPath.hpp" +#include +#include "libnest2d/tools/benchmark.h" +#include "libslic3r/SVG.hpp" + +#include "../libnest2d/printer_parts.hpp" + +#include + using namespace Slic3r; TEST_CASE("Polygon::contains works properly", "[Geometry]"){ @@ -452,3 +460,168 @@ SCENARIO("Ported from xs/t/14_geometry.t", "[Geometry]"){ REQUIRE(! Slic3r::Geometry::directions_parallel(M_PI /2, PI, M_PI /180)); } } + +static Polygon gen_convex_poly(std::mt19937_64 &rg, size_t point_cnt) +{ + std::uniform_int_distribution dist(0, 100); + + Polygon out; + out.points.reserve(point_cnt); + + coord_t tr = dist(rg) * 2 / SCALING_FACTOR; + + for (size_t i = 0; i < point_cnt; ++i) + out.points.emplace_back(tr + dist(rg) / SCALING_FACTOR, + tr + dist(rg) / SCALING_FACTOR); + + return Geometry::convex_hull(out.points); +} + +TEST_CASE("Convex polygon intersection on two disjoint squares", "[Geometry][Rotcalip]") { + Polygon A{{0, 0}, {10, 0}, {10, 10}, {0, 10}}; + A.scale(1. / SCALING_FACTOR); + + Polygon B = A; + B.translate(20 / SCALING_FACTOR, 0); + + bool is_inters = Geometry::intersects(A, B); + + REQUIRE(is_inters != true); +} + +TEST_CASE("Convex polygon intersection on two intersecting squares", "[Geometry][Rotcalip]") { + Polygon A{{0, 0}, {10, 0}, {10, 10}, {0, 10}}; + A.scale(1. / SCALING_FACTOR); + + Polygon B = A; + B.translate(5 / SCALING_FACTOR, 5 / SCALING_FACTOR); + + bool is_inters = Geometry::intersects(A, B); + + REQUIRE(is_inters == true); +} + +TEST_CASE("Convex polygon intersection test on random polygons", "[Geometry]") { + constexpr size_t TEST_CNT = 1000; + constexpr size_t POINT_CNT = 1000; + + std::mt19937_64 rg{std::random_device{}()}; + Benchmark bench; + + auto tests = reserve_vector>(TEST_CNT); + auto results = reserve_vector(TEST_CNT); + auto expects = reserve_vector(TEST_CNT); + + for (size_t i = 0; i < TEST_CNT; ++i) { + tests.emplace_back(gen_convex_poly(rg, POINT_CNT), gen_convex_poly(rg, POINT_CNT)); + } + + bench.start(); + for (const auto &test : tests) + results.emplace_back(Geometry::intersects(test.first, test.second)); + bench.stop(); + + std::cout << "Test time: " << bench.getElapsedSec() << std::endl; + + bench.start(); + for (const auto &test : tests) + expects.emplace_back(!intersection(test.first, test.second).empty()); + bench.stop(); + + std::cout << "Clipper time: " << bench.getElapsedSec() << std::endl; + + REQUIRE(results.size() == expects.size()); + + for (size_t i = 0; i < results.size(); ++i) { + // std::cout << expects[i] << " "; + + if (results[i] != expects[i]) { + SVG svg{std::string("fail") + std::to_string(i) + ".svg"}; + svg.draw(tests[i].first, "blue"); + svg.draw(tests[i].second, "green"); + svg.Close(); + + // std::cout << std::endl; + } + REQUIRE(results[i] == expects[i]); + } + std::cout << std::endl; + +} + +struct Pair +{ + size_t first, second; + bool operator==(const Pair &b) const { return first == b.first && second == b.second; } +}; + +template<> struct std::hash { + size_t operator()(const Pair &c) const + { + return c.first * PRINTER_PART_POLYGONS.size() + c.second; + } +}; + +TEST_CASE("Convex polygon intersection test prusa polygons", "[Geometry][Rotcalip]") { + std::unordered_set combos; + for (size_t i = 0; i < PRINTER_PART_POLYGONS.size(); ++i) { + for (size_t j = 0; j < PRINTER_PART_POLYGONS.size(); ++j) { + if (i != j) { + size_t a = std::min(i, j), b = std::max(i, j); + combos.insert(Pair{a, b}); + } + } + } + + // All disjoint + for (const auto &combo : combos) { + Polygon A = PRINTER_PART_POLYGONS[combo.first], B = PRINTER_PART_POLYGONS[combo.second]; + A = Geometry::convex_hull(A.points); + B = Geometry::convex_hull(B.points); + + auto bba = A.bounding_box(); + auto bbb = B.bounding_box(); + + A.translate(-bba.center()); + B.translate(-bbb.center()); + + B.translate(bba.size() + bbb.size()); + + bool res = Geometry::intersects(A, B); + bool ref = !intersection(A, B).empty(); + + if (res != ref) { + SVG svg{std::string("fail") + std::to_string(combo.first) + "_" + std::to_string(combo.second) + ".svg"}; + svg.draw(A, "blue"); + svg.draw(B, "green"); + svg.Close(); + } + + REQUIRE(res == ref); + } + + // All intersecting + for (const auto &combo : combos) { + Polygon A = PRINTER_PART_POLYGONS[combo.first], B = PRINTER_PART_POLYGONS[combo.second]; + A = Geometry::convex_hull(A.points); + B = Geometry::convex_hull(B.points); + + auto bba = A.bounding_box(); + auto bbb = B.bounding_box(); + + A.translate(-bba.center()); + B.translate(-bbb.center()); + + bool res = Geometry::intersects(A, B); + bool ref = !intersection(A, B).empty(); + + if (res != ref) { + SVG svg{std::string("fail") + std::to_string(combo.first) + "_" + std::to_string(combo.second) + ".svg"}; + svg.draw(A, "blue"); + svg.draw(B, "green"); + svg.Close(); + } + + REQUIRE(res == ref); + } +} From 476b48ed111507c1f63a3c3bcebb3020fb73a771 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 30 Sep 2021 16:49:00 +0200 Subject: [PATCH 2/3] Revert "Merge branch 'tm_convex_intersect_rotcalip'" This reverts commit 627d8bcaefb3c46864324554f663403495d9ef43, reversing changes made to 66d44627248b8e412143298541b5a8397f8a0af5. The change breaks build on mac --- src/libslic3r/Geometry.cpp | 211 +----------------------------- src/libslic3r/Geometry.hpp | 2 - tests/libslic3r/CMakeLists.txt | 1 - tests/libslic3r/test_geometry.cpp | 173 ------------------------ 4 files changed, 1 insertion(+), 386 deletions(-) diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index bbb1f0d6b..321443204 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -20,26 +20,6 @@ #include #include -#if defined(_MSC_VER) && defined(__clang__) -#define BOOST_NO_CXX17_HDR_STRING_VIEW -#endif - -#include - -#include - -namespace Slic3r { - -#if !defined(HAS_INTRINSIC_128_TYPE) || defined(__APPLE__) -using int128_t = boost::multiprecision::int128_t; -#else -using int128_t = __int128; -#endif - -using int256_t = boost::multiprecision::int256_t; - -} // namespace Slic3r - #ifdef SLIC3R_DEBUG #include "SVG.hpp" #endif @@ -1563,193 +1543,4 @@ double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to) return (axis.z() < 0) ? -angle : angle; } -namespace rotcalip { - -inline int128_t magnsq(const Point &p) -{ - return int128_t(p.x()) * p.x() + int64_t(p.y()) * p.y(); -} - -inline int128_t dot(const Point &a, const Point &b) -{ - return int128_t(a.x()) * b.x() + int64_t(a.y()) * b.y(); -} - -// Compares the angle enclosed by vectors dir and dirA (alpha) with the angle -// enclosed by -dir and dirB (beta). Returns -1 if alpha is less than beta, 0 -// if they are equal and 1 if alpha is greater than beta. Note that dir is -// reversed for beta, because it represents the opposite side of a caliper. -int cmp_angles(const Point &dir, const Point &dirA, const Point &dirB) { - int128_t dotA = dot(dir, dirA); - int128_t dotB = dot(-dir, dirB); - int256_t dcosa = int256_t(magnsq(dirB)) * int256_t(std::abs(dotA)) * dotA; - int256_t dcosb = int256_t(magnsq(dirA)) * int256_t(std::abs(dotB)) * dotB; - int256_t diff = dcosa - dcosb; - - return diff > 0? -1 : (diff < 0 ? 1 : 0); -} - -// A helper class to navigate on a polygon. Given a vertex index, one can -// get the edge belonging to that vertex, the coordinates of the vertex, the -// next and previous edges. Stuff that is needed in the rotating calipers algo. -class Idx -{ - size_t m_idx; - const Polygon *m_poly; -public: - explicit Idx(const Polygon &p): m_idx{0}, m_poly{&p} {} - explicit Idx(size_t idx, const Polygon &p): m_idx{idx}, m_poly{&p} {} - - size_t idx() const { return m_idx; } - void set_idx(size_t i) { m_idx = i; } - size_t next() const { return (m_idx + 1) % m_poly->size(); } - size_t inc() { return m_idx = (m_idx + 1) % m_poly->size(); } - Point prev_dir() const { - return pt() - (*m_poly)[(m_idx + m_poly->size() - 1) % m_poly->size()]; - } - - const Point &pt() const { return (*m_poly)[m_idx]; } - const Point dir() const { return (*m_poly)[next()] - pt(); } - const Point next_dir() const - { - return (*m_poly)[(m_idx + 2) % m_poly->size()] - (*m_poly)[next()]; - } - const Polygon &poly() const { return *m_poly; } -}; - -enum class AntipodalVisitMode { Full, SkipParallelSegments }; - -// Visit all antipodal pairs starting from the initial ia, ib pair which -// has to be a valid antipodal pair (not checked). fn is called for every -// antipodal pair encountered including the initial one. -// The callback Fn has a signiture of bool(size_t i, size_t j, const Point &dir) -// where i,j are the vertex indices of the antipodal pair and dir is the -// direction of the calipers touching the i vertex. -template -void visit_antipodals (Idx& ia, Idx &ib, Fn &&fn) -{ - // Set current caliper direction to be the lower edge angle from X axis - int cmp = cmp_angles(ia.prev_dir(), ia.dir(), ib.dir()); - Idx *current = cmp <= 0 ? &ia : &ib, *other = cmp <= 0 ? &ib : &ia; - bool visitor_continue = true; - - size_t a_start = ia.idx(), b_start = ib.idx(); - bool a_finished = false, b_finished = false; - - while (visitor_continue && !(a_finished && b_finished)) { - Point current_dir_a = current == &ia ? current->dir() : -current->dir(); - visitor_continue = fn(ia.idx(), ib.idx(), current_dir_a); - - if constexpr (mode == AntipodalVisitMode::Full) - if (cmp == 0 && visitor_continue) { - visitor_continue = fn(current == &ia ? ia.idx() : ia.next(), - current == &ib ? ib.idx() : ib.next(), - current_dir_a); - } - - cmp = cmp_angles(current->dir(), current->next_dir(), other->dir()); - - current->inc(); - if (cmp > 0) { - std::swap(current, other); - } - - if (ia.idx() == a_start) a_finished = true; - if (ib.idx() == b_start) b_finished = true; - } -} - -static bool is_left(const Point &a, const Point &b, const Point &c) -{ - Vec<2, int64_t> V = (b - a).cast(); - Vec<2, int64_t> W = (c - a).cast(); - - return V.x() * W.y() - V.y() * W.x() > 0; -} - -} // namespace rotcalip - -bool intersects(const Polygon &A, const Polygon &B) -{ - using namespace rotcalip; - - // Establish starting antipodals as extremes in XY plane. Use the - // easily obtainable bounding boxes to check if A and B is disjoint - // and return false if the are. - - struct BB - { - size_t xmin = 0, xmax = 0, ymin = 0, ymax = 0; - const Polygon &P; - static bool cmpy(const Point &l, const Point &u) - { - return l.y() < u.y() || (l.y() == u.y() && l.x() < u.x()); - } - - BB(const Polygon &poly): P{poly} - { - for (size_t i = 0; i < P.size(); ++i) { - if (P[i] < P[xmin]) xmin = i; - if (P[xmax] < P[i]) xmax = i; - if (cmpy(P[i], P[ymin])) ymin = i; - if (cmpy(P[ymax], P[i])) ymax = i; - } - } - }; - - BB bA{A}, bB{B}; - BoundingBox bbA{{A[bA.xmin].x(), A[bA.ymin].y()}, {A[bA.xmax].x(), A[bA.ymax].y()}}; - BoundingBox bbB{{B[bB.xmin].x(), B[bB.ymin].y()}, {B[bB.xmax].x(), B[bB.ymax].y()}}; - - if (!bbA.overlap(bbB)) - return false; - - // Establish starting antipodals as extreme vertex pairs in X or Y direction - // which reside on different polygons. If no such pair is found, the two - // polygons are certainly not disjoint. - Idx imin{bA.xmin, A}, imax{bB.xmax, B}; - if (B[bB.xmin] < imin.pt()) imin = Idx{bB.xmin, B}; - if (imax.pt() < A[bA.xmax]) imax = Idx{bA.xmax, A}; - if (&imin.poly() == &imax.poly()) { - imin = Idx{bA.ymin, A}; - imax = Idx{bB.ymax, B}; - if (B[bB.ymin] < imin.pt()) imin = Idx{bB.ymin, B}; - if (imax.pt() < A[bA.ymax]) imax = Idx{bA.ymax, A}; - } - - if (&imin.poly() == &imax.poly()) - return true; - - bool found_divisor; - visit_antipodals( - imin, imax, - [&imin, &imax, &found_divisor](size_t ia, size_t ib, const Point &dir) { - // std::cout << "A" << ia << " B" << ib << " dir " << - // dir.x() << " " << dir.y() << std::endl; - const Polygon &A = imin.poly(), &B = imax.poly(); - - Point ref_a = A[(ia + 2) % A.size()], - ref_b = B[(ib + 2) % B.size()]; - Point Anext = A[ia] + dir; - bool is_left_a = is_left(A[ia], Anext, ref_a); - bool is_left_b = is_left(B[ib], B[ib] - dir, ref_b); - - // If both reference points are on the left (or right) of the - // support line and the opposite support line is to the righ (or - // left), the divisor line is found. We only test the reference - // point, as by definition, if that is on one side, all the other - // points must be on the same side of a support line. - if (is_left(A[ia], Anext, B[ib])) { - found_divisor = !is_left_a && !is_left_b; - } else { - found_divisor = is_left_a && is_left_b; - } - - return !found_divisor; - }); - - // Intersects if the divisor was not found - return !found_divisor; -} - -}} // namespace Slic3r::Geometry +} } diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index bb583c33c..c6af515c8 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -532,8 +532,6 @@ inline bool is_rotation_ninety_degrees(const Vec3d &rotation) return is_rotation_ninety_degrees(rotation.x()) && is_rotation_ninety_degrees(rotation.y()) && is_rotation_ninety_degrees(rotation.z()); } -bool intersects(const Polygon &convex_poly1, const Polygon &convex_poly2); - } } // namespace Slicer::Geometry #endif diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index 05898db28..575878cf2 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -23,7 +23,6 @@ add_executable(${_TEST_NAME}_tests test_png_io.cpp test_timeutils.cpp test_indexed_triangle_set.cpp - ../libnest2d/printer_parts.cpp ) if (TARGET OpenVDB::openvdb) diff --git a/tests/libslic3r/test_geometry.cpp b/tests/libslic3r/test_geometry.cpp index 7373eaa6a..24e0908cc 100644 --- a/tests/libslic3r/test_geometry.cpp +++ b/tests/libslic3r/test_geometry.cpp @@ -9,14 +9,6 @@ #include "libslic3r/ClipperUtils.hpp" #include "libslic3r/ShortestPath.hpp" -#include -#include "libnest2d/tools/benchmark.h" -#include "libslic3r/SVG.hpp" - -#include "../libnest2d/printer_parts.hpp" - -#include - using namespace Slic3r; TEST_CASE("Polygon::contains works properly", "[Geometry]"){ @@ -460,168 +452,3 @@ SCENARIO("Ported from xs/t/14_geometry.t", "[Geometry]"){ REQUIRE(! Slic3r::Geometry::directions_parallel(M_PI /2, PI, M_PI /180)); } } - -static Polygon gen_convex_poly(std::mt19937_64 &rg, size_t point_cnt) -{ - std::uniform_int_distribution dist(0, 100); - - Polygon out; - out.points.reserve(point_cnt); - - coord_t tr = dist(rg) * 2 / SCALING_FACTOR; - - for (size_t i = 0; i < point_cnt; ++i) - out.points.emplace_back(tr + dist(rg) / SCALING_FACTOR, - tr + dist(rg) / SCALING_FACTOR); - - return Geometry::convex_hull(out.points); -} - -TEST_CASE("Convex polygon intersection on two disjoint squares", "[Geometry][Rotcalip]") { - Polygon A{{0, 0}, {10, 0}, {10, 10}, {0, 10}}; - A.scale(1. / SCALING_FACTOR); - - Polygon B = A; - B.translate(20 / SCALING_FACTOR, 0); - - bool is_inters = Geometry::intersects(A, B); - - REQUIRE(is_inters != true); -} - -TEST_CASE("Convex polygon intersection on two intersecting squares", "[Geometry][Rotcalip]") { - Polygon A{{0, 0}, {10, 0}, {10, 10}, {0, 10}}; - A.scale(1. / SCALING_FACTOR); - - Polygon B = A; - B.translate(5 / SCALING_FACTOR, 5 / SCALING_FACTOR); - - bool is_inters = Geometry::intersects(A, B); - - REQUIRE(is_inters == true); -} - -TEST_CASE("Convex polygon intersection test on random polygons", "[Geometry]") { - constexpr size_t TEST_CNT = 1000; - constexpr size_t POINT_CNT = 1000; - - std::mt19937_64 rg{std::random_device{}()}; - Benchmark bench; - - auto tests = reserve_vector>(TEST_CNT); - auto results = reserve_vector(TEST_CNT); - auto expects = reserve_vector(TEST_CNT); - - for (size_t i = 0; i < TEST_CNT; ++i) { - tests.emplace_back(gen_convex_poly(rg, POINT_CNT), gen_convex_poly(rg, POINT_CNT)); - } - - bench.start(); - for (const auto &test : tests) - results.emplace_back(Geometry::intersects(test.first, test.second)); - bench.stop(); - - std::cout << "Test time: " << bench.getElapsedSec() << std::endl; - - bench.start(); - for (const auto &test : tests) - expects.emplace_back(!intersection(test.first, test.second).empty()); - bench.stop(); - - std::cout << "Clipper time: " << bench.getElapsedSec() << std::endl; - - REQUIRE(results.size() == expects.size()); - - for (size_t i = 0; i < results.size(); ++i) { - // std::cout << expects[i] << " "; - - if (results[i] != expects[i]) { - SVG svg{std::string("fail") + std::to_string(i) + ".svg"}; - svg.draw(tests[i].first, "blue"); - svg.draw(tests[i].second, "green"); - svg.Close(); - - // std::cout << std::endl; - } - REQUIRE(results[i] == expects[i]); - } - std::cout << std::endl; - -} - -struct Pair -{ - size_t first, second; - bool operator==(const Pair &b) const { return first == b.first && second == b.second; } -}; - -template<> struct std::hash { - size_t operator()(const Pair &c) const - { - return c.first * PRINTER_PART_POLYGONS.size() + c.second; - } -}; - -TEST_CASE("Convex polygon intersection test prusa polygons", "[Geometry][Rotcalip]") { - std::unordered_set combos; - for (size_t i = 0; i < PRINTER_PART_POLYGONS.size(); ++i) { - for (size_t j = 0; j < PRINTER_PART_POLYGONS.size(); ++j) { - if (i != j) { - size_t a = std::min(i, j), b = std::max(i, j); - combos.insert(Pair{a, b}); - } - } - } - - // All disjoint - for (const auto &combo : combos) { - Polygon A = PRINTER_PART_POLYGONS[combo.first], B = PRINTER_PART_POLYGONS[combo.second]; - A = Geometry::convex_hull(A.points); - B = Geometry::convex_hull(B.points); - - auto bba = A.bounding_box(); - auto bbb = B.bounding_box(); - - A.translate(-bba.center()); - B.translate(-bbb.center()); - - B.translate(bba.size() + bbb.size()); - - bool res = Geometry::intersects(A, B); - bool ref = !intersection(A, B).empty(); - - if (res != ref) { - SVG svg{std::string("fail") + std::to_string(combo.first) + "_" + std::to_string(combo.second) + ".svg"}; - svg.draw(A, "blue"); - svg.draw(B, "green"); - svg.Close(); - } - - REQUIRE(res == ref); - } - - // All intersecting - for (const auto &combo : combos) { - Polygon A = PRINTER_PART_POLYGONS[combo.first], B = PRINTER_PART_POLYGONS[combo.second]; - A = Geometry::convex_hull(A.points); - B = Geometry::convex_hull(B.points); - - auto bba = A.bounding_box(); - auto bbb = B.bounding_box(); - - A.translate(-bba.center()); - B.translate(-bbb.center()); - - bool res = Geometry::intersects(A, B); - bool ref = !intersection(A, B).empty(); - - if (res != ref) { - SVG svg{std::string("fail") + std::to_string(combo.first) + "_" + std::to_string(combo.second) + ".svg"}; - svg.draw(A, "blue"); - svg.draw(B, "green"); - svg.Close(); - } - - REQUIRE(res == ref); - } -} From 5048dbf8b473f4ee4200a1410e59f4fae78c3ce8 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 30 Sep 2021 16:50:50 +0200 Subject: [PATCH 3/3] Bumped up version number --- version.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.inc b/version.inc index b7e941bb4..fdbc2523f 100644 --- a/version.inc +++ b/version.inc @@ -3,7 +3,7 @@ set(SLIC3R_APP_NAME "PrusaSlicer") set(SLIC3R_APP_KEY "PrusaSlicer") -set(SLIC3R_VERSION "2.4.0-alpha2") +set(SLIC3R_VERSION "2.4.0-alpha3") set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN") set(SLIC3R_RC_VERSION "2,4,0,0") set(SLIC3R_RC_VERSION_DOTS "2.4.0.0")