From 6ec918c2a2d2b27d8fa73cf34f108745453869b9 Mon Sep 17 00:00:00 2001 From: Pavel Date: Wed, 19 Jul 2023 16:13:23 +0200 Subject: [PATCH] Compuation of overhang distance of the perimeters is a new step in the slicing pipeline, the result is stored as and ExtrusionPath attribute. Perimeters are split as necessary based on the differing distances. --- src/libslic3r/AABBTreeLines.hpp | 2 +- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/ExtrusionEntity.hpp | 9 ++ src/libslic3r/GCode/ExtrusionProcessor.cpp | 130 +++++++++++++++++++++ src/libslic3r/GCode/ExtrusionProcessor.hpp | 126 +++++--------------- src/libslic3r/PrintObject.cpp | 12 +- 6 files changed, 178 insertions(+), 103 deletions(-) create mode 100644 src/libslic3r/GCode/ExtrusionProcessor.cpp diff --git a/src/libslic3r/AABBTreeLines.hpp b/src/libslic3r/AABBTreeLines.hpp index 990b3197fd..fd5e7cd967 100644 --- a/src/libslic3r/AABBTreeLines.hpp +++ b/src/libslic3r/AABBTreeLines.hpp @@ -345,7 +345,7 @@ public: return dist; } - std::vector all_lines_in_radius(const Vec<2, Scalar> &point, Floating radius) + std::vector all_lines_in_radius(const Vec<2, Scalar> &point, Floating radius) const { return AABBTreeLines::all_lines_in_radius(this->lines, this->tree, point.template cast(), radius * radius); } diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 10aa7bd38a..696c4b8bb5 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -142,6 +142,8 @@ set(SLIC3R_SOURCES GCode/ConflictChecker.hpp GCode/CoolingBuffer.cpp GCode/CoolingBuffer.hpp + GCode/ExtrusionProcessor.cpp + GCode/ExtrusionProcessor.hpp GCode/FindReplace.cpp GCode/FindReplace.hpp GCode/GCodeWriter.cpp diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index d7c9367ef5..5893853364 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -8,6 +8,7 @@ #include "Polyline.hpp" #include +#include #include #include @@ -101,6 +102,11 @@ inline bool operator==(const ExtrusionFlow &lhs, const ExtrusionFlow &rhs) return lhs.mm3_per_mm == rhs.mm3_per_mm && lhs.width == rhs.width && lhs.height == rhs.height; } +struct OverhangAttributes { + float max_distance_from_prev_layer; + float proximity_to_curled_lines; //value between 0 and 1 +}; + struct ExtrusionAttributes : ExtrusionFlow { ExtrusionAttributes() = default; @@ -110,6 +116,9 @@ struct ExtrusionAttributes : ExtrusionFlow // What is the role / purpose of this extrusion? ExtrusionRole role{ ExtrusionRole::None }; + // OVerhangAttributes are currently computed for perimeters if dynamic overhangs are enabled. + // They are used to control fan and print speed in export. + std::optional overhang_attributes; }; inline bool operator==(const ExtrusionAttributes &lhs, const ExtrusionAttributes &rhs) diff --git a/src/libslic3r/GCode/ExtrusionProcessor.cpp b/src/libslic3r/GCode/ExtrusionProcessor.cpp new file mode 100644 index 0000000000..3f5d8f1d54 --- /dev/null +++ b/src/libslic3r/GCode/ExtrusionProcessor.cpp @@ -0,0 +1,130 @@ +#include "ExtrusionProcessor.hpp" + +namespace Slic3r { + +ExtrusionPaths calculate_and_split_overhanging_extrusions(const ExtrusionPath &path, + const AABBTreeLines::LinesDistancer &unscaled_prev_layer, + const AABBTreeLines::LinesDistancer &prev_layer_curled_lines) +{ + std::vector extended_points = estimate_points_properties(path.polyline.points, + unscaled_prev_layer, path.width()); + std::vector> calculated_distances(extended_points.size()); + + for (size_t i = 0; i < extended_points.size(); i++) { + const ExtendedPoint &curr = extended_points[i]; + const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i]; + + // The following code artifically increases the distance to provide slowdown for extrusions that are over curled lines + float proximity_to_curled_lines = 0.0; + const double dist_limit = 10.0 * path.width(); + { + Vec2d middle = 0.5 * (curr.position + next.position); + auto line_indices = prev_layer_curled_lines.all_lines_in_radius(Point::new_scale(middle), scale_(dist_limit)); + if (!line_indices.empty()) { + double len = (next.position - curr.position).norm(); + // For long lines, there is a problem with the additional slowdown. If by accident, there is small curled line near the middle + // of this long line + // The whole segment gets slower unnecesarily. For these long lines, we do additional check whether it is worth slowing down. + // NOTE that this is still quite rough approximation, e.g. we are still checking lines only near the middle point + // TODO maybe split the lines into smaller segments before running this alg? but can be demanding, and GCode will be huge + if (len > 8) { + Vec2d dir = Vec2d(next.position - curr.position) / len; + Vec2d right = Vec2d(-dir.y(), dir.x()); + + Polygon box_of_influence = { + scaled(Vec2d(curr.position + right * dist_limit)), + scaled(Vec2d(next.position + right * dist_limit)), + scaled(Vec2d(next.position - right * dist_limit)), + scaled(Vec2d(curr.position - right * dist_limit)), + }; + + double projected_lengths_sum = 0; + for (size_t idx : line_indices) { + const CurledLine &line = prev_layer_curled_lines.get_line(idx); + Lines inside = intersection_ln({{line.a, line.b}}, {box_of_influence}); + if (inside.empty()) + continue; + double projected_length = abs(dir.dot(unscaled(Vec2d((inside.back().b - inside.back().a).cast())))); + projected_lengths_sum += projected_length; + } + if (projected_lengths_sum < 0.4 * len) { + line_indices.clear(); + } + } + + for (size_t idx : line_indices) { + const CurledLine &line = prev_layer_curled_lines.get_line(idx); + float distance_from_curled = unscaled(line_alg::distance_to(line, Point::new_scale(middle))); + float proximity = (1.0 - (distance_from_curled / dist_limit)) * (1.0 - (distance_from_curled / dist_limit)) * + (line.curled_height / (path.height() * 10.0f)); // max_curled_height_factor from SupportSpotGenerator + proximity_to_curled_lines = std::max(proximity_to_curled_lines, proximity); + } + } + } + calculated_distances[i].first = std::max(curr.distance, next.distance); + calculated_distances[i].second = proximity_to_curled_lines; + } + + ExtrusionPaths result; + ExtrusionAttributes new_attrs = path.attributes(); + new_attrs.overhang_attributes = std::optional({calculated_distances[0].first, calculated_distances[0].second}); + result.emplace_back(new_attrs); + result.back().polyline.append(Point::new_scale(extended_points[0].position)); + for (size_t i = 1; i < extended_points.size(); i++) { + if (std::abs(calculated_distances[i].first - calculated_distances[i - 1].first) < path.width() * 0.1 && + std::abs(calculated_distances[i].second - calculated_distances[i - 1].second) < 0.1) { + // do not start new path, the attributes are similar enough + } else { + result.back().polyline.append(Point::new_scale(extended_points[i].position)); + new_attrs.overhang_attributes->max_distance_from_prev_layer = calculated_distances[i].first; + new_attrs.overhang_attributes->proximity_to_curled_lines = calculated_distances[i].second; + result.emplace_back(new_attrs); + } + result.back().polyline.append(Point::new_scale(extended_points[i].position)); + } + + return result; +}; + +ExtrusionEntityCollection calculate_and_split_overhanging_extrusions(const ExtrusionEntityCollection &ecc, + const AABBTreeLines::LinesDistancer &unscaled_prev_layer, + const AABBTreeLines::LinesDistancer &prev_layer_curled_lines) +{ + ExtrusionEntityCollection result; + result.no_sort = ecc.no_sort; + for (const auto *e : ecc.entities) { + if (auto *col = static_cast(e)) { + auto new_col = calculate_and_split_overhanging_extrusions(*col, unscaled_prev_layer, prev_layer_curled_lines); + result.append(new_col); + } else if (auto *loop = static_cast(e)) { + ExtrusionLoop new_loop = *loop; + new_loop.paths.clear(); + for (const ExtrusionPath &p : loop->paths) { + auto paths = calculate_and_split_overhanging_extrusions(p, unscaled_prev_layer, prev_layer_curled_lines); + new_loop.paths.insert(new_loop.paths.end(), paths.begin(), paths.end()); + } + result.append(new_loop); + } else if (auto *mp = static_cast(e)) { + ExtrusionMultiPath new_mp = *mp; + new_mp.paths.clear(); + for (const ExtrusionPath &p : mp->paths) { + auto paths = calculate_and_split_overhanging_extrusions(p, unscaled_prev_layer, prev_layer_curled_lines); + new_mp.paths.insert(new_mp.paths.end(), paths.begin(), paths.end()); + } + result.append(new_mp); + } else if (auto *op = static_cast(e)) { + auto paths = calculate_and_split_overhanging_extrusions(*op, unscaled_prev_layer, prev_layer_curled_lines); + for (const ExtrusionPath &p : paths) { + result.append(ExtrusionPathOriented(p.polyline, p.attributes())); + } + } else if (auto *p = static_cast(e)) { + auto paths = calculate_and_split_overhanging_extrusions(*p, unscaled_prev_layer, prev_layer_curled_lines); + result.append(paths); + } else { + throw Slic3r::InvalidArgument("Unknown extrusion entity type"); + } + } + return result; +}; + +} // namespace Slic3r \ No newline at end of file diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index 1deb1cd067..0d60ad9851 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -14,6 +14,7 @@ #include "../Flow.hpp" #include "../Config.hpp" #include "../Line.hpp" +#include "../Exception.hpp" #include #include @@ -21,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -211,106 +213,29 @@ std::vector estimate_points_properties(const POINTS return points; } -void calculate_and_split_overhanging_extrusions(ExtrusionPath &path, - AABBTreeLines::LinesDistancer prev_layer_polygon, - AABBTreeLines::LinesDistancer prev_layer_curled_lines) -{ - std::vector extended_points = estimate_points_properties(path.polyline.points, - prev_layer_polygon, path.width()); +ExtrusionPaths calculate_and_split_overhanging_extrusions(const ExtrusionPath &path, + const AABBTreeLines::LinesDistancer &unscaled_prev_layer, + const AABBTreeLines::LinesDistancer &prev_layer_curled_lines); - for (size_t i = 0; i < extended_points.size(); i++) { - const ExtendedPoint &curr = extended_points[i]; - const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i]; - - // The following code artifically increases the distance to provide slowdown for extrusions that are over curled lines - float artificial_distance_to_curled_lines = 0.0; - const double dist_limit = 10.0 * path.width(); - { - Vec2d middle = 0.5 * (curr.position + next.position); - auto line_indices = prev_layer_curled_lines.all_lines_in_radius(Point::new_scale(middle), scale_(dist_limit)); - if (!line_indices.empty()) { - double len = (next.position - curr.position).norm(); - // For long lines, there is a problem with the additional slowdown. If by accident, there is small curled line near the middle - // of this long line - // The whole segment gets slower unnecesarily. For these long lines, we do additional check whether it is worth slowing down. - // NOTE that this is still quite rough approximation, e.g. we are still checking lines only near the middle point - // TODO maybe split the lines into smaller segments before running this alg? but can be demanding, and GCode will be huge - if (len > 8) { - Vec2d dir = Vec2d(next.position - curr.position) / len; - Vec2d right = Vec2d(-dir.y(), dir.x()); - - Polygon box_of_influence = { - scaled(Vec2d(curr.position + right * dist_limit)), - scaled(Vec2d(next.position + right * dist_limit)), - scaled(Vec2d(next.position - right * dist_limit)), - scaled(Vec2d(curr.position - right * dist_limit)), - }; - - double projected_lengths_sum = 0; - for (size_t idx : line_indices) { - const CurledLine &line = prev_layer_curled_lines.get_line(idx); - Lines inside = intersection_ln({{line.a, line.b}}, {box_of_influence}); - if (inside.empty()) - continue; - double projected_length = abs(dir.dot(unscaled(Vec2d((inside.back().b - inside.back().a).cast())))); - projected_lengths_sum += projected_length; - } - if (projected_lengths_sum < 0.4 * len) { - line_indices.clear(); - } - } - - for (size_t idx : line_indices) { - const CurledLine &line = prev_layer_curled_lines.get_line(idx); - float distance_from_curled = unscaled(line_alg::distance_to(line, Point::new_scale(middle))); - float dist = path.width() * (1.0 - (distance_from_curled / dist_limit)) * (1.0 - (distance_from_curled / dist_limit)) * - (line.curled_height / (path.height() * 10.0f)); // max_curled_height_factor from SupportSpotGenerator - artificial_distance_to_curled_lines = std::max(artificial_distance_to_curled_lines, dist); - } - } - } - - auto interpolate_speed = [](const std::map &values, float distance) { - auto upper_dist = values.lower_bound(distance); - if (upper_dist == values.end()) { - return values.rbegin()->second; - } - if (upper_dist == values.begin()) { - return upper_dist->second; - } - - auto lower_dist = std::prev(upper_dist); - float t = (distance - lower_dist->first) / (upper_dist->first - lower_dist->first); - return (1.0f - t) * lower_dist->second + t * upper_dist->second; - }; - - float extrusion_speed = std::min(interpolate_speed(speed_sections, curr.distance), interpolate_speed(speed_sections, next.distance)); - float curled_base_speed = interpolate_speed(speed_sections, artificial_distance_to_curled_lines); - float final_speed = std::min(curled_base_speed, extrusion_speed); - float fan_speed = std::min(interpolate_speed(fan_speed_sections, curr.distance), - interpolate_speed(fan_speed_sections, next.distance)); - - processed_points.push_back({scaled(curr.position), final_speed, int(fan_speed)}); - } - return processed_points; -} - +ExtrusionEntityCollection calculate_and_split_overhanging_extrusions(const ExtrusionEntityCollection &ecc, + const AABBTreeLines::LinesDistancer &unscaled_prev_layer, + const AABBTreeLines::LinesDistancer &prev_layer_curled_lines); struct ProcessedPoint { Point p; - float speed = 1.0f; - int fan_speed = 0; + float speed = 1.0f; + int fan_speed = 0; }; class ExtrusionQualityEstimator { - std::unordered_map> prev_layer_boundaries; - std::unordered_map> next_layer_boundaries; + std::unordered_map> prev_layer_boundaries; + std::unordered_map> next_layer_boundaries; std::unordered_map> prev_curled_extrusions; std::unordered_map> next_curled_extrusions; - const PrintObject *current_object; + const PrintObject *current_object; public: void set_current_object(const PrintObject *object) { current_object = object; } @@ -338,17 +263,18 @@ public: float speed_base = ext_perimeter_speed > 0 ? ext_perimeter_speed : original_speed; std::map speed_sections; for (size_t i = 0; i < overhangs_w_speeds.size(); i++) { - float distance = flow.width * (1.0 - (overhangs_w_speeds[i].first / 100.0)); - float speed = overhangs_w_speeds[i].second.percent ? (speed_base * overhangs_w_speeds[i].second.value / 100.0) : - overhangs_w_speeds[i].second.value; - if (speed < EPSILON) speed = speed_base; + float distance = flow.width * (1.0 - (overhangs_w_speeds[i].first / 100.0)); + float speed = overhangs_w_speeds[i].second.percent ? (speed_base * overhangs_w_speeds[i].second.value / 100.0) : + overhangs_w_speeds[i].second.value; + if (speed < EPSILON) + speed = speed_base; speed_sections[distance] = speed; } std::map fan_speed_sections; for (size_t i = 0; i < overhangs_w_fan_speeds.size(); i++) { - float distance = flow.width * (1.0 - (overhangs_w_fan_speeds[i].first / 100.0)); - float fan_speed = overhangs_w_fan_speeds[i].second.get_at(extruder_id); + float distance = flow.width * (1.0 - (overhangs_w_fan_speeds[i].first / 100.0)); + float fan_speed = overhangs_w_fan_speeds[i].second.get_at(extruder_id); fan_speed_sections[distance] = fan_speed; } @@ -362,15 +288,17 @@ public: const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i]; // The following code artifically increases the distance to provide slowdown for extrusions that are over curled lines - float artificial_distance_to_curled_lines = 0.0; - const double dist_limit = 10.0 * flow.width; + float artificial_distance_to_curled_lines = 0.0; + const double dist_limit = 10.0 * flow.width; { Vec2d middle = 0.5 * (curr.position + next.position); auto line_indices = prev_curled_extrusions[current_object].all_lines_in_radius(Point::new_scale(middle), scale_(dist_limit)); if (!line_indices.empty()) { - double len = (next.position - curr.position).norm(); - // For long lines, there is a problem with the additional slowdown. If by accident, there is small curled line near the middle of this long line - // The whole segment gets slower unnecesarily. For these long lines, we do additional check whether it is worth slowing down. + double len = (next.position - curr.position).norm(); + // For long lines, there is a problem with the additional slowdown. If by accident, there is small curled line near + // the middle of this long line + // The whole segment gets slower unnecesarily. For these long lines, we do additional check whether it is worth slowing + // down. // NOTE that this is still quite rough approximation, e.g. we are still checking lines only near the middle point // TODO maybe split the lines into smaller segments before running this alg? but can be demanding, and GCode will be huge if (len > 8) { diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 522a61375e..fd457e1691 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -3,6 +3,7 @@ #include "ExPolygon.hpp" #include "Exception.hpp" #include "Flow.hpp" +#include "GCode/ExtrusionProcessor.hpp" #include "KDTreeIndirect.hpp" #include "Line.hpp" #include "Point.hpp" @@ -548,16 +549,21 @@ void PrintObject::calculate_overhanging_perimeters() m_print->set_status(89, _u8L("Calculating overhanging perimeters")); std::unordered_map> curled_lines; + std::unordered_map> unscaled_polygons_lines; for (const Layer *l : this->layers()) { - curled_lines[l->id()] = AABBTreeLines::LinesDistancer{l->curled_lines}; + curled_lines[l->id()] = AABBTreeLines::LinesDistancer{l->curled_lines}; + unscaled_polygons_lines[l->id()] = AABBTreeLines::LinesDistancer{to_unscaled_linesf(l->lslices)}; } for (Layer *l : this->layers()) { - for (const LayerRegion *layer_region : l->regions()) { + for (LayerRegion *layer_region : l->regions()) { if (regions_with_dynamic_overhangs.find(layer_region->m_region) == regions_with_dynamic_overhangs.end()) { continue; } - + ExPolygons prev_layer_polygon = l->lower_layer == nullptr ? ExPolygons() : l->lower_layer->lslices; + layer_region->m_perimeters = calculate_and_split_overhanging_extrusions(layer_region->m_perimeters, + unscaled_polygons_lines[l->id()], + curled_lines[l->id()]); } }