Cyclic ordering (#13578)

Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
This commit is contained in:
Maksym Pyrozhok
2026-07-28 01:52:29 +03:00
committed by GitHub
parent 47ccca7f72
commit ef7bfeda9c
11 changed files with 897 additions and 11 deletions

View File

@@ -10,6 +10,7 @@
#include "KDTreeIndirect.hpp"
#include "MutablePriorityQueue.hpp"
#include "Print.hpp"
#include "GCode/OrderingStrategies.hpp"
#include <cmath>
#include <cassert>
@@ -1103,7 +1104,7 @@ std::vector<size_t> chain_expolygons(const ExPolygons &input_exploy) {
return chain_points(points);
}
std::vector<size_t> chain_points(const Points &points, Point *start_near)
std::vector<size_t> chain_points(const Points &points, const Point *start_near)
{
auto segment_end_point = [&points](size_t idx, bool /* first_point */) -> const Point& { return points[idx]; };
std::vector<std::pair<size_t, bool>> ordered = chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, points.size(), start_near);
@@ -1111,9 +1112,26 @@ std::vector<size_t> chain_points(const Points &points, Point *start_near)
out.reserve(ordered.size());
for (auto &segment_and_reversal : ordered)
out.emplace_back(segment_and_reversal.first);
return out;
}
std::vector<size_t> chain_points_with_postprocessing(const Points &points, const Point *start_near)
{
std::vector<size_t> path = chain_points(points, start_near);
// Alternate 2-opt and crossing removal until convergence.
// 2-opt can create new crossings, and crossing removal can create new
// opportunities for 2-opt improvement. Break early if neither improves.
for (int iter = 0; iter < 3; ++iter) {
bool improved = tsp_2opt_improve(path, points);
improved |= tsp_remove_crossings(path, points);
if (!improved) break;
}
if (start_near == nullptr)
tsp_rotate_minimize_closing(path, points);
return path;
}
#ifndef NDEBUG
// #define DEBUG_SVG_OUTPUT
#endif /* NDEBUG */
@@ -2025,12 +2043,13 @@ std::vector<const PrintInstance*> chain_print_object_instances(const std::vector
instances.emplace_back(i, j);
}
}
auto segment_end_point = [&object_reference_points](size_t idx, bool /* first_point */) -> const Point& { return object_reference_points[idx]; };
std::vector<std::pair<size_t, bool>> ordered = chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, instances.size(), start_near);
// Order objects using nearest neighbor + post-processing (crossing removal + 2-opt).
std::vector<size_t> path = chain_points_with_postprocessing(object_reference_points, start_near);
std::vector<const PrintInstance*> out;
out.reserve(instances.size());
for (auto& segment_and_reversal : ordered) {
const std::pair<size_t, size_t>& inst = instances[segment_and_reversal.first];
out.reserve(path.size());
for (size_t idx : path) {
const std::pair<size_t, size_t>& inst = instances[idx];
out.emplace_back(&print_objects[inst.first]->instances()[inst.second]);
}
return out;