mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-20 14:08:20 +00:00
Fix G-code export stalling for hours on layers with thousands of islands
This commit is contained in:
1 parent
c9d897b4d0
commit
beae8aa516
3 files changed
+62
-6
No files matched your search
@@ -6,6 +6,7 @@
|
||||
#include "../ShortestPath.hpp"
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/libslic3r.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
@@ -120,7 +121,7 @@ bool tsp_remove_crossings(std::vector<size_t>& path, const Points& centers)
|
||||
// TSP helpers (2-opt, closing-edge rotation) that operate on the full cycle.
|
||||
size_t n_edges = pn;
|
||||
|
||||
// Scan for first crossing; returns {i, j} or {npos, npos} if none.
|
||||
// Scan for the first pair of intersecting edges worth reversing; returns {i, j} or {npos, npos} if none.
|
||||
auto find_crossing = [&]() -> std::pair<size_t, size_t> {
|
||||
for (size_t i = 0; i < n_edges; ++i) {
|
||||
const Point& ai = centers[path[i]];
|
||||
@@ -133,8 +134,13 @@ bool tsp_remove_crossings(std::vector<size_t>& path, const Points& centers)
|
||||
const Point& aj = centers[path[j]];
|
||||
const Point& bj = centers[path[(j + 1) % pn]];
|
||||
|
||||
if (!bboxes_overlap(ai, bi, aj, bj)) continue;
|
||||
if (Geometry::segments_intersect(ai, bi, aj, bj))
|
||||
if (!bboxes_overlap(ai, bi, aj, bj) || !Geometry::segments_intersect(ai, bi, aj, bj))
|
||||
continue;
|
||||
// The reversal replaces edges (ai, bi) and (aj, bj) with (ai, aj) and (bi, bj). Edges that only touch
|
||||
// or overlap collinearly, as at points sharing a position, may gain nothing from it, and reversing
|
||||
// them would find the same pair again on every scan. A gain within SCALED_EPSILON, which nearly
|
||||
// collinear crossings can have too, is not worth a reversal either.
|
||||
if (ai.distance_to(bi) + aj.distance_to(bj) - ai.distance_to(aj) - bi.distance_to(bj) > SCALED_EPSILON)
|
||||
return {i, j};
|
||||
}
|
||||
}
|
||||
@@ -142,7 +148,8 @@ bool tsp_remove_crossings(std::vector<size_t>& path, const Points& centers)
|
||||
};
|
||||
|
||||
// Process crossings one at a time: find first, reverse it, restart scan.
|
||||
// Cap iterations to prevent infinite loops on collinear/overlapping segments.
|
||||
// Every reversal shortens the cycle, so no tour repeats and the scan ends once no pair is worth
|
||||
// reversing; the cap is a safety net.
|
||||
int max_iters = static_cast<int>(pn * pn);
|
||||
bool improved = false;
|
||||
while (max_iters-- > 0) {
|
||||
|
||||
@@ -25,8 +25,8 @@ namespace Slic3r {
|
||||
// Returns true if any improvement was made.
|
||||
bool tsp_2opt_improve(std::vector<size_t>& path, const Points& centers, int max_passes = 10);
|
||||
|
||||
// Crossing removal: reverse any segment pair whose edges geometrically cross.
|
||||
// Returns true if any crossing was removed.
|
||||
// Crossing removal: reverse each pair of intersecting edges whose reversal shortens the cycle by more
|
||||
// than SCALED_EPSILON. Returns true if any pair was reversed.
|
||||
bool tsp_remove_crossings(std::vector<size_t>& path, const Points& centers);
|
||||
|
||||
// The shortest cycle through the points, starting at point 0, by dynamic programming over subsets; for a few points only.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/generators/catch_generators.hpp>
|
||||
#include <catch2/catch_all.hpp>
|
||||
#include <catch2/matchers/catch_matchers.hpp>
|
||||
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <math.h>
|
||||
@@ -136,6 +138,53 @@ TEST_CASE("tsp_remove_crossings eliminates crossings", "[TSPPostProcessing]") {
|
||||
REQUIRE(is_permutation(path, centers.size()));
|
||||
}
|
||||
|
||||
TEST_CASE("tsp_remove_crossings leaves a tour alone when no reversal shortens it", "[TSPPostProcessing]") {
|
||||
// Three points on a diagonal, the middle one doubled as island centroids snapped to the same 1 mm cell are,
|
||||
// and a corner closing the tour. Edges 0-1 and 2-3 touch at the doubled point, and reversing between them
|
||||
// only swaps the two copies.
|
||||
Points centers;
|
||||
centers.emplace_back(0, 0);
|
||||
centers.emplace_back(3000000, 3000000);
|
||||
centers.emplace_back(3000000, 3000000);
|
||||
centers.emplace_back(6000000, 6000000);
|
||||
centers.emplace_back(6000000, 0);
|
||||
std::vector<size_t> path = {0, 1, 2, 3, 4};
|
||||
REQUIRE(Geometry::segments_intersect(centers[0], centers[1], centers[2], centers[3]));
|
||||
|
||||
CHECK(!tsp_remove_crossings(path, centers));
|
||||
CHECK(path == std::vector<size_t>{0, 1, 2, 3, 4});
|
||||
}
|
||||
|
||||
TEST_CASE("tsp_remove_crossings keeps a crossing whose reversal gains less than SCALED_EPSILON", "[TSPPostProcessing]") {
|
||||
// Two 100 mm edges crossing at a 10 nm offset: uncrossing them saves about 1e-6 scaled units of travel.
|
||||
Points centers;
|
||||
centers.emplace_back(0, 0);
|
||||
centers.emplace_back(100000000, 10);
|
||||
centers.emplace_back(100000000, 0);
|
||||
centers.emplace_back(0, 10);
|
||||
std::vector<size_t> path = {0, 1, 2, 3};
|
||||
REQUIRE(Geometry::segments_intersect(centers[0], centers[1], centers[2], centers[3]));
|
||||
|
||||
CHECK(!tsp_remove_crossings(path, centers));
|
||||
CHECK(path == std::vector<size_t>{0, 1, 2, 3});
|
||||
}
|
||||
|
||||
TEST_CASE("tsp_remove_crossings reverses overlapping collinear edges when that shortens the tour", "[TSPPostProcessing]") {
|
||||
// Points at x = 0, 3, 1 and 2 mm on one line. Tour 0-3-1-2 is 8 mm long, and its edges [0,3] and [1,2] overlap.
|
||||
// The shortest tour through points on a line is twice their 3 mm span.
|
||||
Points centers;
|
||||
centers.emplace_back(0, 0);
|
||||
centers.emplace_back(3000000, 0);
|
||||
centers.emplace_back(1000000, 0);
|
||||
centers.emplace_back(2000000, 0);
|
||||
std::vector<size_t> path = {0, 1, 2, 3};
|
||||
REQUIRE_THAT(tsp_cycle_path_length(path, centers), Catch::Matchers::WithinAbs(8000000., 1.));
|
||||
|
||||
CHECK(tsp_remove_crossings(path, centers));
|
||||
REQUIRE(is_permutation(path, centers.size()));
|
||||
CHECK_THAT(tsp_cycle_path_length(path, centers), Catch::Matchers::WithinAbs(6000000., 1.));
|
||||
}
|
||||
|
||||
|
||||
|
||||
TEST_CASE("tsp_rotate_minimize_closing shortens closing edge", "[TSPPostProcessing]") {
|
||||
|
||||
Reference in new issue
Block a user