From aa85d050fed276665704b0bb71d4be6505579d45 Mon Sep 17 00:00:00 2001 From: Pavel Mikus Date: Tue, 25 Apr 2023 18:30:52 +0200 Subject: [PATCH] Fix path sorting --- src/libslic3r/Algorithm/PathSorting.hpp | 215 ++++++++++-------------- src/libslic3r/Fill/FillEnsuring.cpp | 44 ++--- src/libslic3r/PerimeterGenerator.cpp | 1 + 3 files changed, 118 insertions(+), 142 deletions(-) diff --git a/src/libslic3r/Algorithm/PathSorting.hpp b/src/libslic3r/Algorithm/PathSorting.hpp index a4806db82..5a032a39b 100644 --- a/src/libslic3r/Algorithm/PathSorting.hpp +++ b/src/libslic3r/Algorithm/PathSorting.hpp @@ -2,12 +2,15 @@ #define SRC_LIBSLIC3R_PATH_SORTING_HPP_ #include "AABBTreeLines.hpp" +#include "BoundingBox.hpp" +#include "Line.hpp" #include "ankerl/unordered_dense.h" #include #include #include #include #include +#include #include #include @@ -21,154 +24,122 @@ namespace Algorithm { // to the second, then they touch. // convert_to_lines is a lambda that should accept the path as argument and return it as Lines vector, in correct order. template -void sort_paths(RandomAccessIterator begin, RandomAccessIterator end, RandomAccessIterator last_seed, double touch_limit_distance, ToLines convert_to_lines) +void sort_paths(RandomAccessIterator begin, RandomAccessIterator end, Point start, double touch_limit_distance, ToLines convert_to_lines) { size_t paths_count = std::distance(begin, end); if (paths_count <= 1) return; + auto paths_min_distance = [](const AABBTreeLines::LinesDistancer &left, const AABBTreeLines::LinesDistancer &right) { + double min_distance = std::numeric_limits::max(); + for (const Line &l : left.get_lines()) { + if (double dist = right.distance_from_lines(l.a); dist < min_distance) { + min_distance = dist; + } + } + if (double dist = right.distance_from_lines(left.get_lines().back().b); dist < min_distance) { + min_distance = dist; + } + + for (const Line &l : right.get_lines()) { + if (double dist = left.distance_from_lines(l.a); dist < min_distance) { + min_distance = dist; + } + } + if (double dist = left.distance_from_lines(right.get_lines().back().b); dist < min_distance) { + min_distance = dist; + } + return min_distance; + }; + std::vector> distancers(paths_count); for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { distancers[path_idx] = AABBTreeLines::LinesDistancer{convert_to_lines(*std::next(begin, path_idx))}; } - auto paths_touch = [touch_limit_distance](const AABBTreeLines::LinesDistancer &left, - const AABBTreeLines::LinesDistancer &right) { - for (const Line &l : left.get_lines()) { - if (right.distance_from_lines(l.a) < touch_limit_distance) { - return true; - } - } - if (right.distance_from_lines(left.get_lines().back().b) < touch_limit_distance) { - return true; - } - - for (const Line &l : right.get_lines()) { - if (left.distance_from_lines(l.a) < touch_limit_distance) { - return true; - } - } - if (left.distance_from_lines(right.get_lines().back().b) < touch_limit_distance) { - return true; - } - return false; - }; - std::vector> dependencies(paths_count); for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { - for (size_t prev_path_idx = 0; prev_path_idx < path_idx; prev_path_idx++) { - if (paths_touch(distancers[path_idx], distancers[prev_path_idx])) { - dependencies[path_idx].insert(prev_path_idx); - dependencies[prev_path_idx].insert(path_idx); + for (size_t next_path_idx = path_idx + 1; next_path_idx < paths_count; next_path_idx++) { + double dist = paths_min_distance(distancers[path_idx], distancers[next_path_idx]); + if (dist < touch_limit_distance) { + dependencies[next_path_idx].insert(path_idx); } } } - size_t index_of_last_fixed = std::distance(begin, last_seed); - - std::vector processed(paths_count, false); - for (size_t path_idx = 0; path_idx <= index_of_last_fixed; path_idx++) { - processed[path_idx] = true; + for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { + std::cout << "Dependencies of " << path_idx << " are "; + for (size_t dep : dependencies[path_idx]) + std::cout << dep << ", "; + std::cout << std::endl; } - for (size_t i = index_of_last_fixed + 1; i < paths_count; i++) { - bool change = false; - for (size_t path_idx = index_of_last_fixed + 1; path_idx < paths_count; path_idx++) { - if (processed[path_idx]) + Point current_point = start; + + std::vector> correct_order_and_direction(paths_count); + size_t unsorted_idx = 0; + size_t null_idx = size_t(-1); + size_t next_idx = null_idx; + bool reverse = false; + while (unsorted_idx < paths_count) { + next_idx = null_idx; + double lines_dist = std::numeric_limits::max(); + for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { + if (!dependencies[path_idx].empty()) continue; - auto processed_dep = std::find_if(dependencies[path_idx].begin(), dependencies[path_idx].end(), - [&](size_t dep) { return processed[dep]; }); - if (processed_dep != dependencies[path_idx].end()) { - for (auto it = dependencies[path_idx].begin(); it != dependencies[path_idx].end();) { - if (!processed[*it]) { - dependencies[*it].insert(path_idx); - dependencies[path_idx].erase(it++); - } else { - ++it; - } + + double ldist = distancers[path_idx].distance_from_lines(current_point); + if (ldist < lines_dist) { + const auto &lines = distancers[path_idx].get_lines(); + double dist_a = line_alg::distance_to(lines.front(), current_point); + double dist_b = line_alg::distance_to(lines.back(), current_point); + if (std::abs(dist_a - dist_b) < touch_limit_distance) { + dist_a = (lines.front().a - current_point).squaredNorm(); + dist_b = (lines.back().b - current_point).squaredNorm(); } - processed[path_idx] = true; - change = true; + next_idx = path_idx; + reverse = dist_b < dist_a; } } - if (!change) { + + // we have valid next_idx, sort it, update dependencies, update current point + correct_order_and_direction[next_idx] = {unsorted_idx, reverse}; + unsorted_idx++; + current_point = reverse ? distancers[next_idx].get_lines().front().a : distancers[next_idx].get_lines().back().b; + + dependencies[next_idx].insert(null_idx); // prevent it from being selected again + for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { + dependencies[path_idx].erase(next_idx); + } + } + + std::cout << "Final order is "; + for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { + std::cout << correct_order_and_direction[path_idx].first << ", "; + } + std::cout << std::endl; + + for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { + if (correct_order_and_direction[path_idx].second) { + std::next(begin, path_idx)->reverse(); + } + } + + for (size_t i = 0; i < correct_order_and_direction.size() - 1; i++) { + bool swapped = false; + for (size_t j = 0; j < correct_order_and_direction.size() - i - 1; j++) { + if (correct_order_and_direction[j].first > correct_order_and_direction[j + 1].first) { + std::swap(correct_order_and_direction[j], correct_order_and_direction[j + 1]); + std::iter_swap(std::next(begin, j), std::next(begin, j + 1)); + swapped = true; + } + } + if (swapped == false) { break; } } - - Point current_point = distancers.begin()->get_lines().begin()->a; - - size_t null_idx = size_t(-1); - size_t unsorted_idx = 0; - size_t next_idx = null_idx; - bool reverse = false; - while (true) { - if (next_idx == null_idx) { // find next pidx to print - double dist = std::numeric_limits::max(); - for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { - if (!dependencies[path_idx].empty()) - continue; - const auto& lines = distancers[path_idx].get_lines(); - double dist_a = (lines.front().a - current_point).cast().squaredNorm(); - if (dist_a < dist) { - dist = dist_a; - next_idx = path_idx; - reverse = false; - } - double dist_b = (lines.back().b - current_point).cast().squaredNorm(); - if (dist_b < dist) { - dist = dist_b; - next_idx = path_idx; - reverse = true; - } - } - if (next_idx == null_idx) { - break; - } - } else { - // we have valid next_idx, sort it, update dependencies, update current point and potentialy set new next_idx - std::iter_swap(std::next(begin, unsorted_idx), std::next(begin, next_idx)); // next_path is now at sorted spot - if (reverse) { - std::next(begin, unsorted_idx)->reverse(); - } - unsorted_idx++; - - assert(dependencies[next_idx].empty()); - dependencies[next_idx].insert(null_idx); - current_point = reverse ? distancers[next_idx].get_lines().front().a : distancers[next_idx].get_lines().back().b; - for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { - dependencies[path_idx].erase(next_idx); - } - double dist = std::numeric_limits::max(); - next_idx = null_idx; - - for (size_t path_idx = next_idx + 1; path_idx < paths_count; path_idx++) { - if (!dependencies[path_idx].empty()) { - continue; - } - const auto &lines = distancers[path_idx].get_lines(); - double dist_a = (lines.front().a - current_point).cast().squaredNorm(); - if (dist_a < dist) { - dist = dist_a; - next_idx = path_idx; - reverse = false; - } - double dist_b = (lines.back().b - current_point).cast().squaredNorm(); - if (dist_b < dist) { - dist = dist_b; - next_idx = path_idx; - reverse = true; - } - } - if (dist > scaled(5.0)) { - next_idx = null_idx; - } - } - } } - -} -} +}} // namespace Slic3r::Algorithm #endif /*SRC_LIBSLIC3R_PATH_SORTING_HPP_*/ \ No newline at end of file diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index e97b6c479..b32ca1ed0 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -4,6 +4,7 @@ #include "AABBTreeLines.hpp" #include "Algorithm/PathSorting.hpp" +#include "BoundingBox.hpp" #include "ExPolygon.hpp" #include "FillEnsuring.hpp" #include "KDTreeIndirect.hpp" @@ -322,14 +323,14 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const } // gaps_for_additional_filling = opening_ex(gaps_for_additional_filling, 0.3 * scaled_spacing); - // BoundingBox bbox = get_extents(filled_area); - // bbox.offset(scale_(1.)); - // ::Slic3r::SVG svg(debug_out_path(("surface" + std::to_string(surface->area())).c_str()).c_str(), bbox); - // svg.draw(to_lines(filled_area), "red", scale_(0.4)); - // svg.draw(to_lines(reconstructed_area), "blue", scale_(0.3)); - // svg.draw(to_lines(gaps_for_additional_filling), "green", scale_(0.2)); - // svg.draw(vertical_lines, "black", scale_(0.1)); - // svg.Close(); + BoundingBox bbox = get_extents(filled_area); + bbox.offset(scale_(1.)); + ::Slic3r::SVG svg(debug_out_path(("surface" + std::to_string(surface->area())).c_str()).c_str(), bbox); + svg.draw(to_lines(filled_area), "red", scale_(0.4)); + svg.draw(to_lines(reconstructed_area), "blue", scale_(0.3)); + svg.draw(to_lines(gaps_for_additional_filling), "green", scale_(0.2)); + svg.draw(vertical_lines, "black", scale_(0.1)); + svg.Close(); for (ExPolygon &ex_poly : gaps_for_additional_filling) { Point bbox_size = ex_poly.contour.bounding_box().size(); @@ -447,8 +448,6 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const } } - rotate_thick_polylines(thick_polylines_out, cos(-aligning_angle), sin(-aligning_angle)); - thick_polylines_out.erase(std::remove_if(thick_polylines_out.begin(), thick_polylines_out.end(), [scaled_spacing](const ThickPolyline &tp) { return tp.length() < scaled_spacing && @@ -457,17 +456,22 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const }), thick_polylines_out.end()); - Algorithm::sort_paths(thick_polylines_out.begin(), thick_polylines_out.end(), thick_polylines_out.begin(), scaled_spacing * 2, - [](const ThickPolyline &tp) { - Lines ls; - Point prev = tp.first_point(); - for (size_t i = 1; i < tp.points.size(); i++) { - ls.emplace_back(prev, tp.points[i]); - prev = ls.back().b; - } - return ls; - }); + std::sort(thick_polylines_out.begin(), thick_polylines_out.end(), [](const ThickPolyline &left, const ThickPolyline &right) { + return BoundingBox(left.points).min.x() < BoundingBox(right.points).min.x(); + }); + Algorithm::sort_paths(thick_polylines_out.begin(), thick_polylines_out.end(), bb.min, scaled_spacing * 1.2, [](const ThickPolyline + &tp) { + Lines ls; + Point prev = tp.first_point(); + for (size_t i = 1; i < tp.points.size(); i++) { + ls.emplace_back(prev, tp.points[i]); + prev = ls.back().b; + } + return ls; + }); + + rotate_thick_polylines(thick_polylines_out, cos(-aligning_angle), sin(-aligning_angle)); return thick_polylines_out; } diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 712e8d131..b6c543bd9 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include