diff --git a/src/libslic3r/Algorithm/PathSorting.hpp b/src/libslic3r/Algorithm/PathSorting.hpp new file mode 100644 index 0000000000..a4806db82d --- /dev/null +++ b/src/libslic3r/Algorithm/PathSorting.hpp @@ -0,0 +1,174 @@ +#ifndef SRC_LIBSLIC3R_PATH_SORTING_HPP_ +#define SRC_LIBSLIC3R_PATH_SORTING_HPP_ + +#include "AABBTreeLines.hpp" +#include "ankerl/unordered_dense.h" +#include +#include +#include +#include +#include +#include +#include + +namespace Slic3r { +namespace Algorithm { + +//Sorts the paths such that all paths between begin and last_seed are printed first, in some order. The rest of the paths is sorted +// such that the paths that are touching some of the already printed are printed first, sorted secondary by the distance to the last point of the last +// printed path. +// begin, end, and last_seed are random access iterators. touch_limit_distance is used to check if the paths are touching - if any part of the path gets this close +// 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) +{ + size_t paths_count = std::distance(begin, end); + if (paths_count <= 1) + return; + + 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); + } + } + } + + 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 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]) + 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; + } + } + processed[path_idx] = true; + change = true; + } + } + if (!change) { + 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; + } + } + } +} + + +} +} + +#endif /*SRC_LIBSLIC3R_PATH_SORTING_HPP_*/ \ No newline at end of file diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 297d2e3ff5..b94f94d9b7 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -22,8 +22,9 @@ set(SLIC3R_SOURCES AABBTreeLines.hpp AABBMesh.hpp AABBMesh.cpp - Algorithm/RegionExpansion.cpp + Algorithm/PathSorting.hpp Algorithm/RegionExpansion.hpp + Algorithm/RegionExpansion.cpp AnyPtr.hpp BoundingBox.cpp BoundingBox.hpp diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index 3b4dd6b549..e97b6c4790 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -3,6 +3,7 @@ #include "../Arachne/WallToolPaths.hpp" #include "AABBTreeLines.hpp" +#include "Algorithm/PathSorting.hpp" #include "ExPolygon.hpp" #include "FillEnsuring.hpp" #include "KDTreeIndirect.hpp" @@ -242,8 +243,8 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const { struct TracedPoly { - std::vector lows; - std::vector highs; + Points lows; + Points highs; }; std::vector current_traced_polys; @@ -321,14 +322,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(); @@ -456,15 +457,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; + }); + return thick_polylines_out; } } // namespace Slic3r - - - - // const size_t n_vlines = (bb.max.x() - bb.min.x() + scaled_spacing - 1) / scaled_spacing; // std::vector vertical_lines(2 * n_vlines + 1); // coord_t y_min = bb.min.y(); diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index e1068d7636..712e8d131f 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -728,7 +728,7 @@ Polylines reconnect_polylines(const Polylines &polylines, double limit_distance) return result; } -ExtrusionPaths sort_extra_perimeters(ExtrusionPaths extra_perims, int index_of_first_unanchored, double extrusion_spacing) +ExtrusionPaths sort_extra_perimeters(const ExtrusionPaths& extra_perims, int index_of_first_unanchored, double extrusion_spacing) { if (extra_perims.empty()) return {};