diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index f69bace7e6..233a05549c 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1,7 +1,5 @@ #include "libslic3r.h" -#include "ExPolygon.hpp" -#include "Flow.hpp" -#include "GCode/OverhangProcessor.hpp" +#include "GCode/ExtrusionProcessor.hpp" #include "I18N.hpp" #include "GCode.hpp" #include "Exception.hpp" @@ -41,7 +39,6 @@ #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. @@ -2172,12 +2169,12 @@ LayerResult GCode::process_layer( } } - std::vector layer_lines; + std::vector layers_ptrs; + layers_ptrs.reserve(layers.size()); 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()); + layers_ptrs.push_back(layer_to_print.object_layer); } - m_prev_layer_boundary = AABBTreeLines::LinesDistancer{std::move(layer_lines)}; + m_extrusion_quality_estimator.prepare_for_new_layer(layers_ptrs); // Extrude the skirt, brim, support, perimeters, infill ordered by the extruders. for (unsigned int extruder_id : layer_tools.extruders) @@ -2708,6 +2705,7 @@ std::string GCode::extrude_multi_path(ExtrusionMultiPath multipath, const std::s std::string GCode::extrude_entity(const ExtrusionEntity &entity, const std::string_view description, double speed) { + m_extrusion_quality_estimator.reset_for_next_extrusion(); if (const ExtrusionPath* path = dynamic_cast(&entity)) return this->extrude_path(*path, description, speed); else if (const ExtrusionMultiPath* multipath = dynamic_cast(&entity)) @@ -2880,10 +2878,7 @@ 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) { - 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) { + } else if (path.role() == erOverhangPerimeter || path.role() == erBridgeInfill) { speed = m_config.get_abs_value("bridge_speed"); } else if (path.role() == erInternalInfill) { speed = m_config.get_abs_value("infill_speed"); @@ -2919,6 +2914,15 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm ); } + + bool variable_speed = false; + double last_set_speed = 0.0; + std::vector points_quality{}; + if (!this->on_first_layer() && is_perimeter(path.role())) { + points_quality = m_extrusion_quality_estimator.estimate_extrusion_quality(path); + variable_speed = std::any_of(points_quality.begin(), points_quality.end(), [](float q) { return q != 1.0; }); + } + double F = speed * 60; // convert mm/sec to mm/min // extrude arc or line @@ -2981,8 +2985,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de } // F is mm per minute. - gcode += m_writer.set_speed(F, "", comment); - double path_length = 0.; + if (!variable_speed){ + gcode += m_writer.set_speed(F, "", comment); + } + { std::string comment; if (m_config.gcode_comments) { @@ -2992,12 +2998,21 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de Vec2d prev = this->point_to_gcode_quantized(path.polyline.points.front()); auto it = path.polyline.points.begin(); auto end = path.polyline.points.end(); + int i = 0; for (++ it; it != end; ++ it) { + if (variable_speed) { + double new_speed = std::max(5.0, points_quality[i] * speed); + if (last_set_speed != new_speed) { + last_set_speed = new_speed; + gcode += m_writer.set_speed(new_speed * 60.0, "", comment); + } + } + Vec2d p = this->point_to_gcode_quantized(*it); const double line_length = (p - prev).norm(); - path_length += line_length; gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, comment); prev = p; + i++; } } if (m_enable_cooling_markers) diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 1e2f7e7f4c..9d1a2449b6 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -1,7 +1,7 @@ #ifndef slic3r_GCode_hpp_ #define slic3r_GCode_hpp_ -#include "GCode/OverhangProcessor.hpp" +#include "GCode/ExtrusionProcessor.hpp" #include "JumpPointSearch.hpp" #include "libslic3r.h" #include "ExPolygon.hpp" @@ -333,8 +333,8 @@ private: // Cache for custom seam enforcers/blockers for each layer. SeamPlacer m_seam_placer; - - AABBTreeLines::LinesDistancerm_prev_layer_boundary; + + ExtrusionQualityEstimator m_extrusion_quality_estimator; /* 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/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp new file mode 100644 index 0000000000..1b831c4960 --- /dev/null +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -0,0 +1,150 @@ +#ifndef slic3r_ExtrusionProcessor_hpp_ +#define slic3r_ExtrusionProcessor_hpp_ + +#include "../AABBTreeLines.hpp" +#include "../SupportSpotsGenerator.hpp" +#include "../libslic3r.h" +#include "../ExtrusionEntity.hpp" +#include "../Layer.hpp" + +#include +#include +#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(); } + } +}; + +class ExtrusionQualityEstimator +{ + AABBTreeLines::LinesDistancer prev_layer_boundary; + AABBTreeLines::LinesDistancer next_layer_boundary; + CurvatureEstimator cestim{}; + +public: + void prepare_for_new_layer(const std::vector &layers) + { + std::vector layer_lines; + for (const Layer *layer : layers) { + if (layer == nullptr) continue; + std::vector object_lines = to_unscaled_linesf(layer->lslices); + layer_lines.insert(layer_lines.end(), object_lines.begin(), object_lines.end()); + } + prev_layer_boundary = next_layer_boundary; + next_layer_boundary = AABBTreeLines::LinesDistancer{std::move(layer_lines)}; + } + + void reset_for_next_extrusion() { cestim.reset(); } + + std::vector estimate_extrusion_quality(const ExtrusionPath &path) + { + float flow_width = path.width; + float min_malformation_dist = 0.2 * flow_width; + float max_malformation_dist = 1.1 * flow_width; + float worst_malformation_dist = 0.5 * (min_malformation_dist + max_malformation_dist); + + std::vector points; + Polyline pl = path.as_polyline(); + points.reserve(pl.size()); + for (const Point &p : pl) { points.push_back(unscaled(p).cast()); } + + std::vector point_qualities(points.size(), 1.0); + for (size_t point_idx = 0; point_idx < points.size(); ++point_idx) { + Vec2f b = points[point_idx]; + + double dist_from_prev_layer = prev_layer_boundary.signed_distance_from_lines(b.cast()) + flow_width * 0.5f; + if (dist_from_prev_layer < min_malformation_dist) continue; + + Vec2f a = points[point_idx > 0 ? point_idx - 1 : 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); + + float distance_quality = std::min(1.0, std::abs(dist_from_prev_layer - worst_malformation_dist) / + (worst_malformation_dist - min_malformation_dist)); + + // Curvature is 1 / R, where is radius of the touching sphere + // if the radius of the touching sphere is greater than 10 mm, dont lower quality, for sharper corners do lower the quality of the point + float curvature_value = std::abs(cestim.get_curvature()) * 10.0f; + curvature_value = std::max(curvature_value, 1.0f); + distance_quality /= curvature_value; + + point_qualities[point_idx] = distance_quality; + } + + if (points.size() > 1) { point_qualities[0] = point_qualities[1]; } + + return point_qualities; + } +}; + +} // namespace Slic3r + +#endif // slic3r_ExtrusionProcessor_hpp_ diff --git a/src/libslic3r/GCode/OverhangProcessor.hpp b/src/libslic3r/GCode/OverhangProcessor.hpp deleted file mode 100644 index 12a445d299..0000000000 --- a/src/libslic3r/GCode/OverhangProcessor.hpp +++ /dev/null @@ -1,121 +0,0 @@ -#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_