diff --git a/src/libslic3r/AABBTreeLines.hpp b/src/libslic3r/AABBTreeLines.hpp index 6426b7d121..06cd082c11 100644 --- a/src/libslic3r/AABBTreeLines.hpp +++ b/src/libslic3r/AABBTreeLines.hpp @@ -137,6 +137,13 @@ public: tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(this->lines); } + explicit LinesDistancer(std::vector &&lines) : lines(lines) + { + tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(this->lines); + } + + LinesDistancer() = default; + // negative sign means inside std::tuple> signed_distance_from_lines_extra(const Vec<2, Scalar> &point) const { diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index e1e8a559ec..f69bace7e6 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1,4 +1,7 @@ #include "libslic3r.h" +#include "ExPolygon.hpp" +#include "Flow.hpp" +#include "GCode/OverhangProcessor.hpp" #include "I18N.hpp" #include "GCode.hpp" #include "Exception.hpp" @@ -38,6 +41,7 @@ #include "SVG.hpp" #include +#include // Intel redesigned some TBB interface considerably when merging TBB with their oneAPI set of libraries, see GH #7332. // We are using quite an old TBB 2017 U7. Before we update our build servers, let's use the old API, which is deprecated in up to date TBB. @@ -2168,6 +2172,13 @@ LayerResult GCode::process_layer( } } + std::vector layer_lines; + for (const LayerToPrint &layer_to_print : layers) { + std::vector object_lines = to_unscaled_linesf(layer_to_print.object_layer->lslices); + layer_lines.insert(layer_lines.end() ,object_lines.begin(), object_lines.end()); + } + m_prev_layer_boundary = AABBTreeLines::LinesDistancer{std::move(layer_lines)}; + // Extrude the skirt, brim, support, perimeters, infill ordered by the extruders. for (unsigned int extruder_id : layer_tools.extruders) { @@ -2845,12 +2856,12 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de acceleration = m_config.first_layer_acceleration.value; } else if (this->object_layer_over_raft() && m_config.first_layer_acceleration_over_raft.value > 0) { acceleration = m_config.first_layer_acceleration_over_raft.value; - } else if (m_config.perimeter_acceleration.value > 0 && is_perimeter(path.role())) { - acceleration = m_config.perimeter_acceleration.value; } else if (m_config.bridge_acceleration.value > 0 && is_bridge(path.role())) { acceleration = m_config.bridge_acceleration.value; } else if (m_config.infill_acceleration.value > 0 && is_infill(path.role())) { acceleration = m_config.infill_acceleration.value; + } else if (m_config.perimeter_acceleration.value > 0 && is_perimeter(path.role())) { + acceleration = m_config.perimeter_acceleration.value; } else { acceleration = m_config.default_acceleration.value; } @@ -2869,7 +2880,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de speed = m_config.get_abs_value("perimeter_speed"); } else if (path.role() == erExternalPerimeter) { speed = m_config.get_abs_value("external_perimeter_speed"); - } else if (path.role() == erOverhangPerimeter || path.role() == erBridgeInfill) { + } else if (path.role() == erOverhangPerimeter) { + float quality = estimate_overhang_quality(path, path.width, this->m_prev_layer_boundary); + speed = std::max(10.0, quality * m_config.get_abs_value("bridge_speed")); + } else if (path.role() == erBridgeInfill) { speed = m_config.get_abs_value("bridge_speed"); } else if (path.role() == erInternalInfill) { speed = m_config.get_abs_value("infill_speed"); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index e37d11a2e9..1e2f7e7f4c 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -1,6 +1,7 @@ #ifndef slic3r_GCode_hpp_ #define slic3r_GCode_hpp_ +#include "GCode/OverhangProcessor.hpp" #include "JumpPointSearch.hpp" #include "libslic3r.h" #include "ExPolygon.hpp" @@ -332,6 +333,8 @@ private: // Cache for custom seam enforcers/blockers for each layer. SeamPlacer m_seam_placer; + + AABBTreeLines::LinesDistancerm_prev_layer_boundary; /* Origin of print coordinates expressed in unscaled G-code coordinates. This affects the input arguments supplied to the extrude*() and travel_to() diff --git a/src/libslic3r/GCode/OverhangProcessor.hpp b/src/libslic3r/GCode/OverhangProcessor.hpp new file mode 100644 index 0000000000..12a445d299 --- /dev/null +++ b/src/libslic3r/GCode/OverhangProcessor.hpp @@ -0,0 +1,121 @@ +#ifndef slic3r_OverhangProcessor_hpp_ +#define slic3r_OverhangProcessor_hpp_ + +#include "../AABBTreeLines.hpp" +#include "../SupportSpotsGenerator.hpp" +#include "../libslic3r.h" + +#include +#include + +namespace Slic3r { + +class SlidingWindowCurvatureAccumulator +{ + float window_size; + float total_distance = 0; // accumulated distance + float total_curvature = 0; // accumulated signed ccw angles + deque distances; + deque angles; + +public: + SlidingWindowCurvatureAccumulator(float window_size) : window_size(window_size) {} + + void add_point(float distance, float angle) + { + total_distance += distance; + total_curvature += angle; + distances.push_back(distance); + angles.push_back(angle); + + while (distances.size() > 1 && total_distance > window_size) { + total_distance -= distances.front(); + total_curvature -= angles.front(); + distances.pop_front(); + angles.pop_front(); + } + } + + float get_curvature() const + { + if (total_distance <= 0.0) { return 0.0; } + + return total_curvature / std::min(total_distance, window_size); + } + + void reset() + { + total_curvature = 0; + total_distance = 0; + distances.clear(); + angles.clear(); + } +}; + +class CurvatureEstimator +{ + static const size_t sliders_count = 4; + SlidingWindowCurvatureAccumulator sliders[sliders_count] = {{2.0}, {4.0}, {8.0}, {16.0}}; + +public: + void add_point(float distance, float angle) + { + for (SlidingWindowCurvatureAccumulator &slider : sliders) { slider.add_point(distance, angle); } + } + float get_curvature() + { + float max_curvature = std::numeric_limits::min(); + for (const SlidingWindowCurvatureAccumulator &slider : sliders) { max_curvature = std::max(max_curvature, slider.get_curvature()); } + return max_curvature; + } + void reset() + { + for (SlidingWindowCurvatureAccumulator &slider : sliders) { slider.reset(); } + } +}; + +inline float estimate_overhang_quality(const ExtrusionPath &entity, + float flow_width, + AABBTreeLines::LinesDistancer &prev_layer_boundary) +{ + // value of 1 is for nice straigth lines that are either in air or mostly lying on the prev layer. + float quality = 1.0; + + float min_malformation_dist = 0.0f; + float max_malformation_dist = 0.7 * flow_width; + + CurvatureEstimator cestim{}; + std::vector points; + Polyline pl = entity.as_polyline(); + points.reserve(pl.size()); + for (const Point &p : pl) { points.push_back(unscaled(p).cast()); } + + for (size_t point_idx = 0; point_idx < points.size(); ++point_idx) { + Vec2f a = points[point_idx > 0 ? point_idx - 1 : point_idx]; + Vec2f b = points[point_idx]; + Vec2f c = points[point_idx < points.size() - 1 ? point_idx + 1 : point_idx]; + + const Vec2f v1 = b - a; + const Vec2f v2 = c - b; + float curr_angle = angle(v1, v2); + + cestim.add_point(v1.norm(), curr_angle); + // malformation in concave angles does not happen + if (curr_angle < -20.0 * PI / 180.0) { cestim.reset(); } + + double dist_from_prev_layer = prev_layer_boundary.signed_distance_from_lines(b.cast()); + + float distance_quality = std::abs(dist_from_prev_layer - (max_malformation_dist + min_malformation_dist) * 0.5); + float curvature_quality = std::abs(cestim.get_curvature()) * 10.0f; + curvature_quality = std::max(curvature_quality, 1.0f); + distance_quality /= curvature_quality; + + if (distance_quality < quality) { quality = 0.8 * quality + 0.2 * distance_quality; } + } + + return quality; +} + +}; // namespace Slic3r + +#endif // slic3r_OverhangProcessor_hpp_ diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 1f9cd7a176..0aaae73154 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -881,7 +881,7 @@ SupportPoints full_search(const PrintObject *po, const Params ¶ms) struct LayerCurlingEstimator { - LD prev_layer_lines = LD({}); + LD prev_layer_lines; Params params; std::function flow_width_getter;