mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-24 23:23:59 -06:00
Fast convex polygon intersection test with rotating calipers
This commit is contained in:
parent
66d4462724
commit
234f062ad4
4 changed files with 386 additions and 1 deletions
|
@ -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