mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	Merge branch 'tm_convex_intersect_rotcalip'
This commit is contained in:
		
						commit
						627d8bcaef
					
				
					 4 changed files with 386 additions and 1 deletions
				
			
		| 
						 | 
				
			
			@ -20,6 +20,26 @@
 | 
			
		|||
#include <boost/algorithm/string/split.hpp>
 | 
			
		||||
#include <boost/log/trivial.hpp>
 | 
			
		||||
 | 
			
		||||
#if defined(_MSC_VER) && defined(__clang__)
 | 
			
		||||
#define BOOST_NO_CXX17_HDR_STRING_VIEW
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <libslic3r/Int128.hpp>
 | 
			
		||||
 | 
			
		||||
#include <boost/multiprecision/integer.hpp>
 | 
			
		||||
 | 
			
		||||
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<AntipodalVisitMode mode = AntipodalVisitMode::Full, class Fn>
 | 
			
		||||
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<int64_t>();
 | 
			
		||||
    Vec<2, int64_t> W = (c - a).cast<int64_t>();
 | 
			
		||||
 | 
			
		||||
    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<AntipodalVisitMode::SkipParallelSegments>(
 | 
			
		||||
        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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,6 +9,14 @@
 | 
			
		|||
#include "libslic3r/ClipperUtils.hpp"
 | 
			
		||||
#include "libslic3r/ShortestPath.hpp"
 | 
			
		||||
 | 
			
		||||
#include <random>
 | 
			
		||||
#include "libnest2d/tools/benchmark.h"
 | 
			
		||||
#include "libslic3r/SVG.hpp"
 | 
			
		||||
 | 
			
		||||
#include "../libnest2d/printer_parts.hpp"
 | 
			
		||||
 | 
			
		||||
#include <unordered_set>
 | 
			
		||||
 | 
			
		||||
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<coord_t> 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<std::pair<Polygon, Polygon>>(TEST_CNT);
 | 
			
		||||
    auto results = reserve_vector<bool>(TEST_CNT);
 | 
			
		||||
    auto expects = reserve_vector<bool>(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<Pair> {
 | 
			
		||||
    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<Pair> 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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue