From 2cba980a8be141747ac78d7ccbebf64f952162b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 8 Jul 2024 14:20:36 +0200 Subject: [PATCH 1/4] Add conversion into Lines for ThickPolyline. --- src/libslic3r/Line.hpp | 2 +- src/libslic3r/Polyline.cpp | 38 ++++++++++++++++++++++++++++++++ src/libslic3r/Polyline.hpp | 45 +++++++++++++++++++++++++------------- 3 files changed, 69 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp index b593d2f7b7..f53479a9f2 100644 --- a/src/libslic3r/Line.hpp +++ b/src/libslic3r/Line.hpp @@ -194,7 +194,7 @@ template bool intersection(const L &l1, const L &l2, Vec, Scalar class Line { public: - Line() {} + Line() = default; Line(const Point& _a, const Point& _b) : a(_a), b(_b) {} explicit operator Lines() const { Lines lines; lines.emplace_back(*this); return lines; } void scale(double factor) { this->a *= factor; this->b *= factor; } diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index 3635b93faa..92878c34ac 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -297,6 +297,44 @@ std::pair foot_pt(const Points &polyline, const Point &pt) return std::make_pair(int(it_proj - polyline.begin()) - 1, foot_pt_min); } +size_t total_lines_count(const ThickPolylines &thick_polylines) { + size_t lines_cnt = 0; + for (const ThickPolyline &thick_polyline : thick_polylines) { + if (thick_polyline.points.size() > 1) { + lines_cnt += thick_polyline.points.size() - 1; + } + } + + return lines_cnt; +} + +Lines to_lines(const ThickPolyline &thick_polyline) { + Lines lines; + if (thick_polyline.points.size() >= 2) { + lines.reserve(thick_polyline.points.size() - 1); + + for (Points::const_iterator it = thick_polyline.points.begin(); it != thick_polyline.points.end() - 1; ++it) { + lines.emplace_back(*it, *(it + 1)); + } + } + + return lines; +} + +Lines to_lines(const ThickPolylines &thick_polylines) { + const size_t lines_cnt = total_lines_count(thick_polylines); + + Lines lines; + lines.reserve(lines_cnt); + for (const ThickPolyline &thick_polyline : thick_polylines) { + for (Points::const_iterator it = thick_polyline.points.begin(); it != thick_polyline.points.end() - 1; ++it) { + lines.emplace_back(*it, *(it + 1)); + } + } + + return lines; +} + ThickLines ThickPolyline::thicklines() const { ThickLines lines; diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index 0357ebb534..0f928328a7 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -119,30 +119,40 @@ inline double total_length(const Polylines &polylines) { return total; } -inline Lines to_lines(const Polyline &poly) -{ +inline size_t total_lines_count(const Polylines &polylines) { + size_t lines_cnt = 0; + for (const Polyline &polyline : polylines) { + if (polyline.points.size() > 1) { + lines_cnt += polyline.points.size() - 1; + } + } + + return lines_cnt; +} + +inline Lines to_lines(const Polyline &poly) { Lines lines; if (poly.points.size() >= 2) { lines.reserve(poly.points.size() - 1); - for (Points::const_iterator it = poly.points.begin(); it != poly.points.end()-1; ++it) - lines.push_back(Line(*it, *(it + 1))); + for (Points::const_iterator it = poly.points.begin(); it != poly.points.end() - 1; ++it) { + lines.emplace_back(*it, *(it + 1)); + } } + return lines; } -inline Lines to_lines(const Polylines &polys) -{ - size_t n_lines = 0; - for (size_t i = 0; i < polys.size(); ++ i) - if (polys[i].points.size() > 1) - n_lines += polys[i].points.size() - 1; +inline Lines to_lines(const Polylines &polylines) { + const size_t lines_cnt = total_lines_count(polylines); + Lines lines; - lines.reserve(n_lines); - for (size_t i = 0; i < polys.size(); ++ i) { - const Polyline &poly = polys[i]; - for (Points::const_iterator it = poly.points.begin(); it != poly.points.end()-1; ++it) - lines.push_back(Line(*it, *(it + 1))); + lines.reserve(lines_cnt); + for (const Polyline &polyline : polylines) { + for (Points::const_iterator it = polyline.points.begin(); it != polyline.points.end() - 1; ++it) { + lines.emplace_back(*it, *(it + 1)); + } } + return lines; } @@ -251,6 +261,11 @@ inline ThickPolylines to_thick_polylines(Polylines &&polylines, const coordf_t w return out; } +size_t total_lines_count(const ThickPolylines &thick_polylines); + +Lines to_lines(const ThickPolyline &thick_polyline); +Lines to_lines(const ThickPolylines &thick_polylines); + class Polyline3 : public MultiPoint3 { public: From b3510ac808695df6331da2a05b939fa067ba5bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 8 Jul 2024 14:20:53 +0200 Subject: [PATCH 2/4] SPE-1950: Reimplement algorithm for filtering vibrating extractions inside the ensuring infill to make it less computation complex. --- src/libslic3r/Fill/FillEnsuring.cpp | 340 ++++++++++++++++++++-------- 1 file changed, 247 insertions(+), 93 deletions(-) diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index ea3fecc1c3..c2f479ac9b 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -33,6 +33,244 @@ namespace Slic3r { +const constexpr coord_t MAX_LINE_LENGTH_TO_FILTER = scaled(4.); // 4 mm. +const constexpr size_t MAX_SKIPS_ALLOWED = 2; // Skip means propagation through long line. +const constexpr size_t MIN_DEPTH_FOR_LINE_REMOVING = 5; + +struct LineNode +{ + struct State + { + // The total number of long lines visited before this node was reached. + // We just need the minimum number of all possible paths to decide whether we can remove the line or not. + int min_skips_taken = 0; + // The total number of short lines visited before this node was reached. + int total_short_lines = 0; + // Some initial line is touching some long line. This information is propagated to neighbors. + bool initial_touches_long_lines = false; + bool initialized = false; + + void reset() { + this->min_skips_taken = 0; + this->total_short_lines = 0; + this->initial_touches_long_lines = false; + this->initialized = false; + } + }; + + explicit LineNode(const Line &line) : line(line) {} + + Line line; + // Pointers to line nodes in the previous and the next section that overlap with this line. + std::vector next_section_overlapping_lines; + std::vector prev_section_overlapping_lines; + + bool is_removed = false; + + State state; + + // Return true if some initial line is touching some long line and this information was propagated into the current line. + bool is_initial_line_touching_long_lines() const { + if (prev_section_overlapping_lines.empty()) + return false; + + for (LineNode *line_node : prev_section_overlapping_lines) { + if (line_node->state.initial_touches_long_lines) + return true; + } + + return false; + } + + // Return true if the current line overlaps with some long line in the previous section. + bool is_touching_long_lines_in_previous_layer() const { + if (prev_section_overlapping_lines.empty()) + return false; + + for (LineNode *line_node : prev_section_overlapping_lines) { + if (!line_node->is_removed && line_node->line.length() >= MAX_LINE_LENGTH_TO_FILTER) + return true; + } + + return false; + } + + // Return true if the current line overlaps with some line in the next section. + bool has_next_layer_neighbours() const { + if (next_section_overlapping_lines.empty()) + return false; + + for (LineNode *line_node : next_section_overlapping_lines) { + if (!line_node->is_removed) + return true; + } + + return false; + } +}; + +using LineNodes = std::vector; + +inline bool are_lines_overlapping_in_y_axes(const Line &first_line, const Line &second_line) { + return (second_line.a.y() <= first_line.a.y() && first_line.a.y() <= second_line.b.y()) + || (second_line.a.y() <= first_line.b.y() && first_line.b.y() <= second_line.b.y()) + || (first_line.a.y() <= second_line.a.y() && second_line.a.y() <= first_line.b.y()) + || (first_line.a.y() <= second_line.b.y() && second_line.b.y() <= first_line.b.y()); +} + +bool can_line_note_be_removed(const LineNode &line_node) { + return (line_node.line.length() < MAX_LINE_LENGTH_TO_FILTER) + && (line_node.state.total_short_lines > int(MIN_DEPTH_FOR_LINE_REMOVING) + || (!line_node.is_initial_line_touching_long_lines() && !line_node.has_next_layer_neighbours())); +} + +// Remove the node and propagate its removal to the previous sections. +void propagate_line_node_remove(const LineNode &line_node) { + std::queue line_node_queue; + for (LineNode *prev_line : line_node.prev_section_overlapping_lines) { + if (prev_line->is_removed) + continue; + + line_node_queue.emplace(prev_line); + } + + for (; !line_node_queue.empty(); line_node_queue.pop()) { + LineNode &line_to_check = *line_node_queue.front(); + + if (can_line_note_be_removed(line_to_check)) { + line_to_check.is_removed = true; + + for (LineNode *prev_line : line_to_check.prev_section_overlapping_lines) { + if (prev_line->is_removed) + continue; + + line_node_queue.emplace(prev_line); + } + } + } +} + +// Filter out short extrusions that could create vibrations. +static std::vector filter_vibrating_extrusions(const std::vector &lines_sections) { + // Initialize all line nodes. + std::vector line_nodes_sections(lines_sections.size()); + for (const Lines &lines_section : lines_sections) { + const size_t section_idx = &lines_section - lines_sections.data(); + + line_nodes_sections[section_idx].reserve(lines_section.size()); + for (const Line &line : lines_section) { + line_nodes_sections[section_idx].emplace_back(line); + } + } + + // Precalculate for each line node which line nodes in the previous and next section this line node overlaps. + for (auto curr_lines_section_it = line_nodes_sections.begin(); curr_lines_section_it != line_nodes_sections.end(); ++curr_lines_section_it) { + if (curr_lines_section_it != line_nodes_sections.begin()) { + const auto prev_lines_section_it = std::prev(curr_lines_section_it); + for (LineNode &curr_line : *curr_lines_section_it) { + for (LineNode &prev_line : *prev_lines_section_it) { + if (are_lines_overlapping_in_y_axes(curr_line.line, prev_line.line)) { + curr_line.prev_section_overlapping_lines.emplace_back(&prev_line); + } + } + } + } + + if (std::next(curr_lines_section_it) != line_nodes_sections.end()) { + const auto next_lines_section_it = std::next(curr_lines_section_it); + for (LineNode &curr_line : *curr_lines_section_it) { + for (LineNode &next_line : *next_lines_section_it) { + if (are_lines_overlapping_in_y_axes(curr_line.line, next_line.line)) { + curr_line.next_section_overlapping_lines.emplace_back(&next_line); + } + } + } + } + } + + // Select each section as the initial lines section and propagate line node states from this initial lines section to the last lines section. + // During this propagation, we remove those lines that meet the conditions for its removal. + // When some line is removed, we propagate this removal to previous layers. + for (size_t initial_line_section_idx = 0; initial_line_section_idx < line_nodes_sections.size(); ++initial_line_section_idx) { + // Stars from non-removed short lines. + for (LineNode &initial_line : line_nodes_sections[initial_line_section_idx]) { + if (initial_line.is_removed || initial_line.line.length() >= MAX_LINE_LENGTH_TO_FILTER) + continue; + + initial_line.state.reset(); + initial_line.state.total_short_lines = 1; + initial_line.state.initial_touches_long_lines = initial_line.is_touching_long_lines_in_previous_layer(); + initial_line.state.initialized = true; + } + + // Iterate from the initial lines section until the last lines section. + for (size_t propagation_line_section_idx = initial_line_section_idx; propagation_line_section_idx < line_nodes_sections.size(); ++propagation_line_section_idx) { + // Before we propagate node states into next lines sections, we reset the state of all line nodes in the next line section. + if (propagation_line_section_idx + 1 < line_nodes_sections.size()) { + for (LineNode &propagation_line : line_nodes_sections[propagation_line_section_idx + 1]) { + propagation_line.state.reset(); + } + } + + for (LineNode &propagation_line : line_nodes_sections[propagation_line_section_idx]) { + if (propagation_line.is_removed || !propagation_line.state.initialized) + continue; + + for (LineNode *neighbour_line : propagation_line.next_section_overlapping_lines) { + if (neighbour_line->is_removed) + continue; + + const bool is_short_line = neighbour_line->line.length() < MAX_LINE_LENGTH_TO_FILTER; + const bool is_skip_allowed = propagation_line.state.min_skips_taken < int(MAX_SKIPS_ALLOWED); + + if (!is_short_line && !is_skip_allowed) + continue; + + const int neighbour_total_short_lines = propagation_line.state.total_short_lines + int(is_short_line); + const int neighbour_min_skips_taken = propagation_line.state.min_skips_taken + int(!is_short_line); + + if (neighbour_line->state.initialized) { + // When the state of the node was previously filled, then we need to update data in such a way + // that will maximize the possibility of removing this node. + neighbour_line->state.min_skips_taken = std::max(neighbour_line->state.min_skips_taken, neighbour_total_short_lines); + neighbour_line->state.min_skips_taken = std::min(neighbour_line->state.min_skips_taken, neighbour_min_skips_taken); + + // We will keep updating neighbor initial_touches_long_lines until it is equal to false. + if (neighbour_line->state.initial_touches_long_lines) { + neighbour_line->state.initial_touches_long_lines = propagation_line.state.initial_touches_long_lines; + } + } else { + neighbour_line->state.total_short_lines = neighbour_total_short_lines; + neighbour_line->state.min_skips_taken = neighbour_min_skips_taken; + neighbour_line->state.initial_touches_long_lines = propagation_line.state.initial_touches_long_lines; + neighbour_line->state.initialized = true; + } + } + + if (can_line_note_be_removed(propagation_line)) { + // Remove the current node and propagate its removal to the previous sections. + propagation_line.is_removed = true; + propagate_line_node_remove(propagation_line); + } + } + } + } + + // Create lines sections without filtered-out lines. + std::vector lines_sections_out(line_nodes_sections.size()); + for (const std::vector &line_nodes_section : line_nodes_sections) { + const size_t section_idx = &line_nodes_section - line_nodes_sections.data(); + + for (const LineNode &line_node : line_nodes_section) { + if (!line_node.is_removed) { + lines_sections_out[section_idx].emplace_back(line_node.line); + } + } + } + + return lines_sections_out; +} + ThickPolylines make_fill_polylines( const Fill *fill, const Surface *surface, const FillParams ¶ms, bool stop_vibrations, bool fill_gaps, bool connect_extrusions) { @@ -49,11 +287,6 @@ ThickPolylines make_fill_polylines( } }; - auto segments_overlap = [](coord_t alow, coord_t ahigh, coord_t blow, coord_t bhigh) { - return (alow >= blow && alow <= bhigh) || (ahigh >= blow && ahigh <= bhigh) || (blow >= alow && blow <= ahigh) || - (bhigh >= alow && bhigh <= ahigh); - }; - const coord_t scaled_spacing = scaled(fill->spacing); double distance_limit_reconnection = 2.0 * double(scaled_spacing); double squared_distance_limit_reconnection = distance_limit_reconnection * distance_limit_reconnection; @@ -70,23 +303,24 @@ ThickPolylines make_fill_polylines( AABBTreeLines::LinesDistancer area_walls{to_lines(inner_area)}; - const size_t n_vlines = (bb.max.x() - bb.min.x() + scaled_spacing - 1) / scaled_spacing; - std::vector vertical_lines(n_vlines); - coord_t y_min = bb.min.y(); - coord_t y_max = bb.max.y(); + const size_t n_vlines = (bb.max.x() - bb.min.x() + scaled_spacing - 1) / scaled_spacing; + const coord_t y_min = bb.min.y(); + const coord_t y_max = bb.max.y(); + Lines vertical_lines(n_vlines); + for (size_t i = 0; i < n_vlines; i++) { coord_t x = bb.min.x() + i * double(scaled_spacing); vertical_lines[i].a = Point{x, y_min}; vertical_lines[i].b = Point{x, y_max}; } - if (vertical_lines.size() > 0) { + + if (!vertical_lines.empty()) { vertical_lines.push_back(vertical_lines.back()); vertical_lines.back().a = Point{coord_t(bb.min.x() + n_vlines * double(scaled_spacing) + scaled_spacing * 0.5), y_min}; vertical_lines.back().b = Point{vertical_lines.back().a.x(), y_max}; } - std::vector> polygon_sections(n_vlines); - + std::vector polygon_sections(n_vlines); for (size_t i = 0; i < n_vlines; i++) { const auto intersections = area_walls.intersections_with_line(vertical_lines[i]); @@ -102,87 +336,7 @@ ThickPolylines make_fill_polylines( } if (stop_vibrations) { - struct Node - { - int section_idx; - int line_idx; - int skips_taken = 0; - bool neighbours_explored = false; - std::vector> neighbours{}; - }; - - coord_t length_filter = scale_(4); - size_t skips_allowed = 2; - size_t min_removal_conut = 5; - for (int section_idx = 0; section_idx < int(polygon_sections.size()); ++ section_idx) { - for (int line_idx = 0; line_idx < int(polygon_sections[section_idx].size()); ++ line_idx) { - if (const Line &line = polygon_sections[section_idx][line_idx]; line.a != line.b && line.length() < length_filter) { - std::set> to_remove{{section_idx, line_idx}}; - std::vector to_visit{{section_idx, line_idx}}; - - bool initial_touches_long_lines = false; - if (section_idx > 0) { - for (int prev_line_idx = 0; prev_line_idx < int(polygon_sections[section_idx - 1].size()); ++ prev_line_idx) { - if (const Line &nl = polygon_sections[section_idx - 1][prev_line_idx]; - nl.a != nl.b && segments_overlap(line.a.y(), line.b.y(), nl.a.y(), nl.b.y())) { - initial_touches_long_lines = true; - } - } - } - - while (!to_visit.empty()) { - Node curr = to_visit.back(); - const Line &curr_l = polygon_sections[curr.section_idx][curr.line_idx]; - if (curr.neighbours_explored) { - bool is_valid_for_removal = (curr_l.length() < length_filter) && - ((int(to_remove.size()) - curr.skips_taken > int(min_removal_conut)) || - (curr.neighbours.empty() && !initial_touches_long_lines)); - if (!is_valid_for_removal) { - for (const auto &n : curr.neighbours) { - if (to_remove.find(n) != to_remove.end()) { - is_valid_for_removal = true; - break; - } - } - } - if (!is_valid_for_removal) { - to_remove.erase({curr.section_idx, curr.line_idx}); - } - to_visit.pop_back(); - } else { - to_visit.back().neighbours_explored = true; - int curr_index = to_visit.size() - 1; - bool can_use_skip = curr_l.length() <= length_filter && curr.skips_taken < int(skips_allowed); - if (curr.section_idx + 1 < int(polygon_sections.size())) { - for (int lidx = 0; lidx < int(polygon_sections[curr.section_idx + 1].size()); ++ lidx) { - if (const Line &nl = polygon_sections[curr.section_idx + 1][lidx]; - nl.a != nl.b && segments_overlap(curr_l.a.y(), curr_l.b.y(), nl.a.y(), nl.b.y()) && - (nl.length() < length_filter || can_use_skip)) { - to_visit[curr_index].neighbours.push_back({curr.section_idx + 1, lidx}); - to_remove.insert({curr.section_idx + 1, lidx}); - Node next_node{curr.section_idx + 1, lidx, curr.skips_taken + (nl.length() >= length_filter)}; - to_visit.push_back(next_node); - } - } - } - } - } - - for (const auto &pair : to_remove) { - Line &l = polygon_sections[pair.first][pair.second]; - l.a = l.b; - } - } - } - } - } - - for (size_t section_idx = 0; section_idx < polygon_sections.size(); section_idx++) { - polygon_sections[section_idx].erase(std::remove_if(polygon_sections[section_idx].begin(), polygon_sections[section_idx].end(), - [](const Line &s) { return s.a == s.b; }), - polygon_sections[section_idx].end()); - std::sort(polygon_sections[section_idx].begin(), polygon_sections[section_idx].end(), - [](const Line &a, const Line &b) { return a.a.y() < b.b.y(); }); + polygon_sections = filter_vibrating_extrusions(polygon_sections); } ThickPolylines thick_polylines; From 82fb648cb7257cd8329e5cd7bbb3eb9f0870095a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 8 Jul 2024 14:21:07 +0200 Subject: [PATCH 3/4] SPE-1950: Optimization of extrusion sorting for the ensuring infill. --- src/libslic3r/Algorithm/PathSorting.hpp | 83 ++++++++++++++++--------- src/libslic3r/BoundingBox.hpp | 17 +++++ src/libslic3r/Polyline.cpp | 20 ++++++ src/libslic3r/Polyline.hpp | 5 ++ 4 files changed, 94 insertions(+), 31 deletions(-) diff --git a/src/libslic3r/Algorithm/PathSorting.hpp b/src/libslic3r/Algorithm/PathSorting.hpp index 7d47eb43aa..758430c679 100644 --- a/src/libslic3r/Algorithm/PathSorting.hpp +++ b/src/libslic3r/Algorithm/PathSorting.hpp @@ -18,8 +18,39 @@ #include #include -namespace Slic3r { -namespace Algorithm { +namespace Slic3r::Algorithm { + +bool is_first_path_touching_second_path(const AABBTreeLines::LinesDistancer &first_distancer, + const AABBTreeLines::LinesDistancer &second_distancer, + const BoundingBox &second_distancer_bbox, + const double touch_distance_threshold) +{ + for (const Line &line : first_distancer.get_lines()) { + if (bbox_point_distance(second_distancer_bbox, line.a) < touch_distance_threshold && second_distancer.distance_from_lines(line.a) < touch_distance_threshold) { + return true; + } + } + + const Point first_distancer_last_pt = first_distancer.get_lines().back().b; + if (bbox_point_distance(second_distancer_bbox, first_distancer_last_pt) && second_distancer.distance_from_lines(first_distancer_last_pt) < touch_distance_threshold) { + return true; + } + + return false; +} + +bool are_paths_touching(const AABBTreeLines::LinesDistancer &first_distancer, const BoundingBox &first_distancer_bbox, + const AABBTreeLines::LinesDistancer &second_distancer, const BoundingBox &second_distancer_bbox, + const double touch_distance_threshold) +{ + if (is_first_path_touching_second_path(first_distancer, second_distancer, second_distancer_bbox, touch_distance_threshold)) { + return true; + } else if (is_first_path_touching_second_path(second_distancer, first_distancer, first_distancer_bbox, touch_distance_threshold)) { + return true; + } + + return false; +} //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 @@ -28,44 +59,34 @@ 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, Point start, double touch_limit_distance, ToLines convert_to_lines) +void sort_paths(RandomAccessIterator begin, RandomAccessIterator end, Point start, const double touch_distance_threshold, ToLines convert_to_lines) { - size_t paths_count = std::distance(begin, end); + const size_t paths_count = std::distance(begin, end); if (paths_count <= 1) return; - 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> 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))}; } + BoundingBoxes bboxes; + bboxes.reserve(paths_count); + for (auto tp_it = begin; tp_it != end; ++tp_it) { + bboxes.emplace_back(tp_it->bounding_box()); + } + std::vector> dependencies(paths_count); - for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { - for (size_t next_path_idx = path_idx + 1; next_path_idx < paths_count; next_path_idx++) { - if (paths_touch(distancers[path_idx], distancers[next_path_idx])) { - dependencies[next_path_idx].insert(path_idx); + for (size_t curr_path_idx = 0; curr_path_idx < paths_count; ++curr_path_idx) { + for (size_t next_path_idx = curr_path_idx + 1; next_path_idx < paths_count; ++next_path_idx) { + const BoundingBox &curr_path_bbox = bboxes[curr_path_idx]; + const BoundingBox &next_path_bbox = bboxes[next_path_idx]; + + if (bbox_bbox_distance(curr_path_bbox, next_path_bbox) >= touch_distance_threshold) + continue; + + if (are_paths_touching(distancers[curr_path_idx], curr_path_bbox, distancers[next_path_idx], next_path_bbox, touch_distance_threshold)) { + dependencies[next_path_idx].insert(curr_path_idx); } } } @@ -127,6 +148,6 @@ void sort_paths(RandomAccessIterator begin, RandomAccessIterator end, Point star } } -}} // namespace Slic3r::Algorithm +} // namespace Slic3r::Algorithm #endif /*SRC_LIBSLIC3R_PATH_SORTING_HPP_*/ diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp index 0b52e28738..a8e27b99d5 100644 --- a/src/libslic3r/BoundingBox.hpp +++ b/src/libslic3r/BoundingBox.hpp @@ -328,6 +328,23 @@ inline double bbox_point_distance_squared(const BoundingBox &bbox, const Point & coord_t(0)); } +// Minimum distance between two Bounding boxes. +// Returns zero when Bounding boxes overlap. +inline double bbox_bbox_distance(const BoundingBox &first_bbox, const BoundingBox &second_bbox) { + if (first_bbox.overlap(second_bbox)) + return 0.; + + double bboxes_distance_squared = 0.; + + for (size_t axis = 0; axis < 2; ++axis) { + const coord_t axis_distance = std::max(std::max(0, first_bbox.min[axis] - second_bbox.max[axis]), + second_bbox.min[axis] - first_bbox.max[axis]); + bboxes_distance_squared += Slic3r::sqr(static_cast(axis_distance)); + } + + return std::sqrt(bboxes_distance_squared); +} + template BoundingBoxBase> to_2d(const BoundingBox3Base> &bb) { diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index 92878c34ac..dbf4838f78 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -201,6 +201,10 @@ bool Polyline::is_straight() const return true; } +BoundingBox ThickPolyline::bounding_box() const { + return BoundingBox(this->points); +} + BoundingBox get_extents(const Polyline &polyline) { return polyline.bounding_box(); @@ -335,6 +339,22 @@ Lines to_lines(const ThickPolylines &thick_polylines) { return lines; } +BoundingBox get_extents(const ThickPolyline &thick_polyline) { + return thick_polyline.bounding_box(); +} + +BoundingBox get_extents(const ThickPolylines &thick_polylines) { + BoundingBox bbox; + if (!thick_polylines.empty()) { + bbox = thick_polylines.front().bounding_box(); + for (size_t i = 1; i < thick_polylines.size(); ++i) { + bbox.merge(thick_polylines[i].points); + } + } + + return bbox; +} + ThickLines ThickPolyline::thicklines() const { ThickLines lines; diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index 0f928328a7..ec13b8e2c3 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -241,6 +241,8 @@ struct ThickPolyline { // On open ThickPolyline make no effect. void start_at_index(int index); + BoundingBox bounding_box() const; + Points points; // vector of startpoint width and endpoint width of each line segment. The size should be always (points.size()-1) * 2 // e.g. let four be points a,b,c,d. that are three lines ab, bc, cd. for each line, there should be start width, so the width vector is: @@ -266,6 +268,9 @@ size_t total_lines_count(const ThickPolylines &thick_polylines); Lines to_lines(const ThickPolyline &thick_polyline); Lines to_lines(const ThickPolylines &thick_polylines); +BoundingBox get_extents(const ThickPolyline &thick_polyline); +BoundingBox get_extents(const ThickPolylines &thick_polylines); + class Polyline3 : public MultiPoint3 { public: From 86309ba939c17ae6e35f0cecb5db6f3032c71fe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 8 Jul 2024 14:21:15 +0200 Subject: [PATCH 4/4] SPE-1950: Optimization of computation complexity of perimeter ordering for Arachne generator. The previous implementation during the grouping of perimeters using depth-first searches unnecessarily searched nodes that had no impact on grouping, which significantly increased the search space. --- src/libslic3r/Arachne/PerimeterOrder.cpp | 47 ++++++++++++------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/src/libslic3r/Arachne/PerimeterOrder.cpp b/src/libslic3r/Arachne/PerimeterOrder.cpp index dcc2a3fcbb..554b4098dd 100644 --- a/src/libslic3r/Arachne/PerimeterOrder.cpp +++ b/src/libslic3r/Arachne/PerimeterOrder.cpp @@ -134,7 +134,7 @@ static std::vector ordered_perimeter_extrusions_to_m const Point &extrusion_start_position = extrusion_line.junctions.front().p; const double distance_sqr = (current_position - extrusion_start_position).cast().squaredNorm(); if (distance_sqr < nearest_distance_sqr) { - if (extrusion_line.is_closed || (!extrusion_line.is_closed && nearest_distance_sqr != std::numeric_limits::max()) || (!extrusion_line.is_closed && !is_nearest_closed)) { + if (extrusion_line.is_closed || (!extrusion_line.is_closed && nearest_distance_sqr == std::numeric_limits::max()) || (!extrusion_line.is_closed && !is_nearest_closed)) { nearest_extrusion_idx = extrusion_idx; nearest_distance_sqr = distance_sqr; is_nearest_closed = extrusion_line.is_closed; @@ -190,7 +190,7 @@ static std::vector order_of_grouped_perimeter_extrusions_to_minimize_dis const Point &extrusion_start_position = external_perimeter_extrusion_line.junctions.front().p; const double distance_sqr = (current_position - extrusion_start_position).cast().squaredNorm(); if (distance_sqr < nearest_distance_sqr) { - if (external_perimeter_extrusion_line.is_closed || (!external_perimeter_extrusion_line.is_closed && nearest_distance_sqr != std::numeric_limits::max()) || (!external_perimeter_extrusion_line.is_closed && !is_nearest_closed)) { + if (external_perimeter_extrusion_line.is_closed || (!external_perimeter_extrusion_line.is_closed && nearest_distance_sqr == std::numeric_limits::max()) || (!external_perimeter_extrusion_line.is_closed && !is_nearest_closed)) { nearest_grouped_extrusions_idx = grouped_extrusion_idx; nearest_distance_sqr = distance_sqr; is_nearest_closed = external_perimeter_extrusion_line.is_closed; @@ -225,33 +225,32 @@ static PerimeterExtrusions extract_ordered_perimeter_extrusions(const PerimeterE while (!stack.empty()) { const PerimeterExtrusion *current_extrusion = stack.top(); const size_t current_extrusion_idx = current_extrusion - sorted_perimeter_extrusions.data(); + stack.pop(); + visited[current_extrusion_idx] = true; - if (visited[current_extrusion_idx]) - continue; - - if (current_extrusion->nearest_external_perimeter == &perimeter_extrusion) + if (current_extrusion->nearest_external_perimeter == &perimeter_extrusion) { grouped_extrusions.back().extrusions.emplace_back(current_extrusion); - - if (current_extrusion->adjacent_perimeter_extrusions.size() == 1) { - const PerimeterExtrusion *adjacent_extrusion = current_extrusion->adjacent_perimeter_extrusions.front(); - stack.push(adjacent_extrusion); - } else if (current_extrusion->adjacent_perimeter_extrusions.size() > 1) { - // When there is more than one available candidate, then order candidates to minimize distances between - // candidates and also to minimize the distance from the current_position. - std::vector available_candidates; - for (const PerimeterExtrusion *adjacent_extrusion : current_extrusion->adjacent_perimeter_extrusions) { - if (const size_t adjacent_extrusion_idx = adjacent_extrusion - sorted_perimeter_extrusions.data(); !visited[adjacent_extrusion_idx]) - available_candidates.emplace_back(adjacent_extrusion); - } - - std::vector adjacent_extrusions = ordered_perimeter_extrusions_to_minimize_distances(Point::Zero(), available_candidates); - std::reverse(adjacent_extrusions.begin(), adjacent_extrusions.end()); - for (const PerimeterExtrusion *adjacent_extrusion : adjacent_extrusions) - stack.push(adjacent_extrusion); } - visited[current_extrusion_idx] = true; + std::vector available_candidates; + for (const PerimeterExtrusion *adjacent_extrusion : current_extrusion->adjacent_perimeter_extrusions) { + const size_t adjacent_extrusion_idx = adjacent_extrusion - sorted_perimeter_extrusions.data(); + if (!visited[adjacent_extrusion_idx] && !adjacent_extrusion->is_external_perimeter() && adjacent_extrusion->nearest_external_perimeter == &perimeter_extrusion) { + available_candidates.emplace_back(adjacent_extrusion); + } + } + + if (available_candidates.size() == 1) { + stack.push(available_candidates.front()); + } else if (available_candidates.size() > 1) { + // When there is more than one available candidate, then order candidates to minimize distances between + // candidates and also to minimize the distance from the current_position. + std::vector adjacent_extrusions = ordered_perimeter_extrusions_to_minimize_distances(Point::Zero(), available_candidates); + for (auto extrusion_it = adjacent_extrusions.rbegin(); extrusion_it != adjacent_extrusions.rend(); ++extrusion_it) { + stack.push(*extrusion_it); + } + } } if (!external_perimeters_first)