diff --git a/src/libslic3r/AABBTreeLines.hpp b/src/libslic3r/AABBTreeLines.hpp index 5678a7b8c0..55cf328f57 100644 --- a/src/libslic3r/AABBTreeLines.hpp +++ b/src/libslic3r/AABBTreeLines.hpp @@ -233,6 +233,7 @@ namespace AABBTreeLines { AABBTreeIndirect::detail::indexed_primitives_within_distance_squared_recurisve(distancer, size_t(0), max_distance_squared, found_lines); return found_lines; } + // return 1 if true, -1 if false, 0 for point on contour (or if cannot be determined) template @@ -350,10 +351,10 @@ namespace AABBTreeLines { return dist; } - std::vector all_lines_in_radius(const Vec<2, typename LineType::Scalar>& point, Floating radius) - { - return all_lines_in_radius(this->lines, this->tree, point, radius * radius); - } + std::vector all_lines_in_radius(const Vec<2, Scalar> &point, Floating radius) + { + return AABBTreeLines::all_lines_in_radius(this->lines, this->tree, point.template cast(), radius * radius); + } template std::vector, size_t>> intersections_with_line(const LineType& line) const diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp index 413834db20..7d6f92a528 100644 --- a/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -46,6 +46,13 @@ public: ~ExtrusionEntityCollection() { clear(); } explicit operator ExtrusionPaths() const; + ExtrusionEntitiesPtr::const_iterator cbegin() const { return this->entities.cbegin(); } + ExtrusionEntitiesPtr::const_iterator cend() const { return this->entities.cend(); } + ExtrusionEntitiesPtr::const_iterator begin() const { return this->entities.cbegin(); } + ExtrusionEntitiesPtr::const_iterator end() const { return this->entities.cend(); } + ExtrusionEntitiesPtr::iterator begin() { return this->entities.begin(); } + ExtrusionEntitiesPtr::iterator end() { return this->entities.end(); } + bool is_collection() const override { return true; } ExtrusionRole role() const override { ExtrusionRole out = erNone; @@ -112,6 +119,7 @@ public: Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const { Polygons out; this->polygons_covered_by_spacing(out, scaled_epsilon); return out; } size_t items_count() const; + size_t size() const { return entities.size(); } /// Returns a flattened copy of this ExtrusionEntityCollection. That is, all of the items in its entities vector are not collections. /// You should be iterating over flatten().entities if you are interested in the underlying ExtrusionEntities (and don't care about hierarchy). /// \param preserve_ordering Flag to method that will flatten if and only if the underlying collection is sortable when True (default: False). diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 9f6181f713..db46eb534f 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -4530,35 +4530,68 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, if (m_config.enable_overhang_speed && !m_config.overhang_speed_classic && !this->on_first_layer() && (is_bridge(path.role()) || is_perimeter(path.role()))) { - double out_wall_ref_speed = m_config.get_abs_value("outer_wall_speed"); - ConfigOptionPercents overhang_overlap_levels({75, 50, 25, 13, 12.99, 0}); - ConfigOptionFloatsOrPercents dynamic_overhang_speeds( - {(m_config.get_abs_value("overhang_1_4_speed") < 0.5) ? - FloatOrPercent{100, true} : - FloatOrPercent{m_config.get_abs_value("overhang_1_4_speed") * 100 / out_wall_ref_speed, true}, - (m_config.get_abs_value("overhang_2_4_speed") < 0.5) ? - FloatOrPercent{100, true} : - FloatOrPercent{m_config.get_abs_value("overhang_2_4_speed") * 100 / out_wall_ref_speed, true}, - (m_config.get_abs_value("overhang_3_4_speed") < 0.5) ? - FloatOrPercent{100, true} : - FloatOrPercent{m_config.get_abs_value("overhang_3_4_speed") * 100 / out_wall_ref_speed, true}, - (m_config.get_abs_value("overhang_4_4_speed") < 0.5) ? - FloatOrPercent{100, true} : - FloatOrPercent{m_config.get_abs_value("overhang_4_4_speed") * 100 / out_wall_ref_speed, true}, - FloatOrPercent{m_config.get_abs_value("bridge_speed") * 100 / out_wall_ref_speed, true}, - FloatOrPercent{m_config.get_abs_value("bridge_speed") * 100 / out_wall_ref_speed, true}}); + + double out_wall_ref_speed = m_config.get_abs_value("outer_wall_speed"); + ConfigOptionPercents overhang_overlap_levels({75, 50, 25, 13, 12.99, 0}); - if (out_wall_ref_speed == 0) - out_wall_ref_speed = EXTRUDER_CONFIG(filament_max_volumetric_speed) / _mm3_per_mm; + if (m_config.slowdown_for_curled_perimeters){ + ConfigOptionFloatsOrPercents dynamic_overhang_speeds( + {(m_config.get_abs_value("overhang_1_4_speed") < 0.5) ? + FloatOrPercent{100, true} : + FloatOrPercent{m_config.get_abs_value("overhang_1_4_speed") * 100 / out_wall_ref_speed, true}, + (m_config.get_abs_value("overhang_2_4_speed") < 0.5) ? + FloatOrPercent{100, true} : + FloatOrPercent{m_config.get_abs_value("overhang_2_4_speed") * 100 / out_wall_ref_speed, true}, + (m_config.get_abs_value("overhang_3_4_speed") < 0.5) ? + FloatOrPercent{100, true} : + FloatOrPercent{m_config.get_abs_value("overhang_3_4_speed") * 100 / out_wall_ref_speed, true}, + (m_config.get_abs_value("overhang_4_4_speed") < 0.5) ? + FloatOrPercent{100, true} : + FloatOrPercent{m_config.get_abs_value("overhang_4_4_speed") * 100 / out_wall_ref_speed, true}, + (m_config.get_abs_value("overhang_4_4_speed") < 0.5) ? + FloatOrPercent{100, true} : + FloatOrPercent{m_config.get_abs_value("overhang_4_4_speed") * 100 / out_wall_ref_speed, true}, + (m_config.get_abs_value("overhang_4_4_speed") < 0.5) ? + FloatOrPercent{100, true} : + FloatOrPercent{m_config.get_abs_value("overhang_4_4_speed") * 100 / out_wall_ref_speed, true}}); + if (out_wall_ref_speed == 0) + out_wall_ref_speed = EXTRUDER_CONFIG(filament_max_volumetric_speed) / _mm3_per_mm; - if (EXTRUDER_CONFIG(filament_max_volumetric_speed) > 0) { - out_wall_ref_speed = std::min(out_wall_ref_speed, EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm); - } + if (EXTRUDER_CONFIG(filament_max_volumetric_speed) > 0) { + out_wall_ref_speed = std::min(out_wall_ref_speed, EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm); + } - new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, overhang_overlap_levels, dynamic_overhang_speeds, - out_wall_ref_speed, speed); + new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, overhang_overlap_levels, dynamic_overhang_speeds, + out_wall_ref_speed, speed, m_config.slowdown_for_curled_perimeters); + }else{ + ConfigOptionFloatsOrPercents dynamic_overhang_speeds( + {(m_config.get_abs_value("overhang_1_4_speed") < 0.5) ? + FloatOrPercent{100, true} : + FloatOrPercent{m_config.get_abs_value("overhang_1_4_speed") * 100 / out_wall_ref_speed, true}, + (m_config.get_abs_value("overhang_2_4_speed") < 0.5) ? + FloatOrPercent{100, true} : + FloatOrPercent{m_config.get_abs_value("overhang_2_4_speed") * 100 / out_wall_ref_speed, true}, + (m_config.get_abs_value("overhang_3_4_speed") < 0.5) ? + FloatOrPercent{100, true} : + FloatOrPercent{m_config.get_abs_value("overhang_3_4_speed") * 100 / out_wall_ref_speed, true}, + (m_config.get_abs_value("overhang_4_4_speed") < 0.5) ? + FloatOrPercent{100, true} : + FloatOrPercent{m_config.get_abs_value("overhang_4_4_speed") * 100 / out_wall_ref_speed, true}, + FloatOrPercent{m_config.get_abs_value("bridge_speed") * 100 / out_wall_ref_speed, true}, + FloatOrPercent{m_config.get_abs_value("bridge_speed") * 100 / out_wall_ref_speed, true}}); + + if (out_wall_ref_speed == 0) + out_wall_ref_speed = EXTRUDER_CONFIG(filament_max_volumetric_speed) / _mm3_per_mm; + + if (EXTRUDER_CONFIG(filament_max_volumetric_speed) > 0) { + out_wall_ref_speed = std::min(out_wall_ref_speed, EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm); + } + + new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, overhang_overlap_levels, dynamic_overhang_speeds, + out_wall_ref_speed, speed, m_config.slowdown_for_curled_perimeters); + } + variable_speed = std::any_of(new_points.begin(), new_points.end(), [speed](const ProcessedPoint &p) { return p.speed != speed; }); - variable_speed = std::any_of(new_points.begin(), new_points.end(), [speed](const ProcessedPoint &p) { return p.speed != speed; }); } double F = speed * 60; // convert mm/sec to mm/min diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index 967620b407..08a0694e3e 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -247,6 +247,8 @@ class ExtrusionQualityEstimator { 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; public: @@ -258,16 +260,22 @@ public: const PrintObject *object = obj; prev_layer_boundaries[object] = next_layer_boundaries[object]; next_layer_boundaries[object] = AABBTreeLines::LinesDistancer{to_unscaled_linesf(layer->lslices)}; + prev_curled_extrusions[object] = next_curled_extrusions[object]; + next_curled_extrusions[object] = AABBTreeLines::LinesDistancer{layer->curled_lines}; } std::vector estimate_extrusion_quality(const ExtrusionPath &path, const ConfigOptionPercents &overlaps, const ConfigOptionFloatsOrPercents &speeds, float ext_perimeter_speed, - float original_speed) + float original_speed, + bool slowdown_for_curled_edges) { size_t speed_sections_count = std::min(overlaps.values.size(), speeds.values.size()); std::vector> speed_sections; + + + for (size_t i = 0; i < speed_sections_count; i++) { float distance = path.width * (1.0 - (overlaps.get_at(i) / 100.0)); float speed = speeds.get_at(i).percent ? (ext_perimeter_speed * speeds.get_at(i).value / 100.0) : speeds.get_at(i).value; @@ -297,6 +305,56 @@ public: 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]; + + float artificial_distance_to_curled_lines = 0.0; + if(slowdown_for_curled_edges) { + // The following code artifically increases the distance to provide slowdown for extrusions that are over curled lines + const double dist_limit = 10.0 * path.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. + // 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_curled_extrusions[current_object].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_curled_extrusions[current_object].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 calculate_speed = [&speed_sections, &original_speed](float distance) { float final_speed; @@ -316,10 +374,15 @@ public: } return final_speed; }; - + float extrusion_speed = std::min(calculate_speed(curr.distance), calculate_speed(next.distance)); + if(slowdown_for_curled_edges) { + float curled_speed = calculate_speed(artificial_distance_to_curled_lines); + extrusion_speed = std::min(curled_speed, extrusion_speed); // adjust extrusion speed based on what is smallest - the calculated overhang speed or the artificial curled speed + } + float overlap = std::min(1 - curr.distance * width_inv, 1 - next.distance * width_inv); - + processed_points.push_back({ scaled(curr.position), extrusion_speed, overlap }); } return processed_points; diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 3bb10bad7d..a72d427692 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -132,6 +132,9 @@ public: coordf_t height; // layer height in unscaled coordinates coordf_t bottom_z() const { return this->print_z - this->height; } + //Extrusions estimated to be seriously malformed, estimated during "Estimating curled extrusions" step. These lines should be avoided during fast travels. + CurledLines curled_lines; + // BBS mutable ExPolygons sharp_tails; mutable ExPolygons cantilevers; diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp index dbb5ff8ee0..5c39f25faa 100644 --- a/src/libslic3r/Line.hpp +++ b/src/libslic3r/Line.hpp @@ -209,6 +209,18 @@ public: double a_width, b_width; }; +class CurledLine : public Line +{ +public: + CurledLine() : curled_height(0.0f) {} + CurledLine(const Point& a, const Point& b) : Line(a, b), curled_height(0.0f) {} + CurledLine(const Point& a, const Point& b, float curled_height) : Line(a, b), curled_height(curled_height) {} + + float curled_height; +}; + +using CurledLines = std::vector; + class Line3 { public: diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index c48782f93e..f640524ad9 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -752,7 +752,7 @@ static std::vector s_Preset_print_options { "tree_support_branch_diameter", "tree_support_branch_diameter_angle", "tree_support_branch_diameter_double_wall", "detect_narrow_internal_solid_infill", "gcode_add_line_number", "enable_arc_fitting", "infill_combination", /*"adaptive_layer_height",*/ - "support_bottom_interface_spacing", "enable_overhang_speed", "overhang_1_4_speed", "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed", + "support_bottom_interface_spacing", "enable_overhang_speed", "slowdown_for_curled_perimeters", "overhang_1_4_speed", "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed", "initial_layer_infill_speed", "only_one_wall_top", "timelapse_type", "internal_bridge_support_thickness", "wall_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 4634f19264..4656e318a1 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -282,6 +282,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n || opt_key == "enable_arc_fitting" || opt_key == "wall_infill_order") { osteps.emplace_back(posPerimeters); + osteps.emplace_back(posEstimateCurledExtrusions); osteps.emplace_back(posInfill); osteps.emplace_back(posSupportMaterial); osteps.emplace_back(posSimplifyPath); @@ -1666,6 +1667,15 @@ void Print::process(bool use_cache) obj->set_done(posPerimeters); } } + for (PrintObject *obj : m_objects) { + if (need_slicing_objects.count(obj) != 0) { + obj->estimate_curled_extrusions(); + } + else { + if (obj->set_started(posEstimateCurledExtrusions)) + obj->set_done(posEstimateCurledExtrusions); + } + } for (PrintObject *obj : m_objects) { if (need_slicing_objects.count(obj) != 0) { obj->infill(); @@ -1723,6 +1733,7 @@ void Print::process(bool use_cache) obj->infill(); obj->ironing(); obj->generate_support_material(); + obj->estimate_curled_extrusions(); } } } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index f688e4ec3b..00ad8de36a 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -87,7 +87,7 @@ enum PrintStep { }; enum PrintObjectStep { - posSlice, posPerimeters, posPrepareInfill, + posSlice, posPerimeters,posEstimateCurledExtrusions, posPrepareInfill, posInfill, posIroning, posSupportMaterial, posSimplifyPath, posSimplifySupportPath, // BBS posSimplifyInfill, @@ -468,6 +468,7 @@ private: void infill(); void ironing(); void generate_support_material(); + void estimate_curled_extrusions(); void simplify_extrusion_path(); void slice_volumes(); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 039cecaa77..2b2c8ba9bf 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -800,6 +800,13 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Enable this option to slow printing down for different overhang degree"); def->mode = comAdvanced; def->set_default_value(new ConfigOptionBool{ true }); + + def = this->add("slowdown_for_curled_perimeters", coBool); + def->label = L("Slow down for curled perimeters"); + def->category = L("Speed"); + def->tooltip = L("Enable this option to slow printing down in areas where potential curled perimeters may exist"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool{ false }); def = this->add("overhang_1_4_speed", coFloatOrPercent); def->label = "(10%, 25%)"; diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 7480eeff41..3d518f625f 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -816,6 +816,7 @@ PRINT_CONFIG_CLASS_DEFINE( // Orca ((ConfigOptionBool, make_overhang_printable)) ((ConfigOptionBool, extra_perimeters_on_overhangs)) + ((ConfigOptionBool, slowdown_for_curled_perimeters)) ) PRINT_CONFIG_CLASS_DEFINE( diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 9cea53e4e8..f39732b173 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -9,6 +9,7 @@ #include "MutablePolygon.hpp" #include "PrintConfig.hpp" #include "SupportMaterial.hpp" +#include "SupportSpotsGenerator.hpp" #include "Support/TreeSupport.hpp" #include "Surface.hpp" #include "Slicing.hpp" @@ -499,6 +500,25 @@ void PrintObject::generate_support_material() } } +void PrintObject::estimate_curled_extrusions() +{ + if (this->set_started(posEstimateCurledExtrusions)) { + if ( std::any_of(this->print()->m_print_regions.begin(), this->print()->m_print_regions.end(), + [](const PrintRegion *region) { return region->config().enable_overhang_speed.getBool(); })) { + + // Estimate curling of support material and add it to the malformaition lines of each layer + float support_flow_width = support_material_flow(this, this->config().layer_height).width(); + SupportSpotsGenerator::Params params{this->print()->m_config.filament_type.values, + float(this->print()->m_config.inner_wall_acceleration.getFloat()), + this->config().raft_layers.getInt(), this->config().brim_type.value, + float(this->config().brim_width.getFloat())}; + SupportSpotsGenerator::estimate_malformations(this->layers(), params); + m_print->throw_if_canceled(); + } + //this->set_done(posEstimateCurledExtrusions); + } +} + void PrintObject::simplify_extrusion_path() { if (this->set_started(posSimplifyPath)) { @@ -756,6 +776,7 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "raft_contact_distance" || opt_key == "slice_closing_radius" || opt_key == "slicing_mode" + || opt_key == "slowdown_for_curled_perimeters" || opt_key == "make_overhang_printable" || opt_key == "make_overhang_printable_angle" || opt_key == "make_overhang_printable_hole_size") { diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 0b10a6b755..52f3424d35 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -1,5 +1,5 @@ #include "SupportSpotsGenerator.hpp" -/* + #include "BoundingBox.hpp" #include "ExPolygon.hpp" #include "ExtrusionEntity.hpp" @@ -45,6 +45,7 @@ #include "libslic3r/Color.hpp" #endif + namespace Slic3r { class ExtrusionLine @@ -85,6 +86,113 @@ namespace SupportSpotsGenerator { using LD = AABBTreeLines::LinesDistancer; +float get_flow_width(const LayerRegion *region, ExtrusionRole role) +{ + if (role == ExtrusionRole::erBridgeInfill) return region->flow(FlowRole::frExternalPerimeter).width(); + if (role == ExtrusionRole::erExternalPerimeter) return region->flow(FlowRole::frExternalPerimeter).width(); + if (role == ExtrusionRole::erGapFill) return region->flow(FlowRole::frInfill).width(); + if (role == ExtrusionRole::erPerimeter) return region->flow(FlowRole::frPerimeter).width(); + if (role == ExtrusionRole::erSolidInfill) return region->flow(FlowRole::frSolidInfill).width(); + if (role == ExtrusionRole::erInternalInfill) return region->flow(FlowRole::frInfill).width(); + if (role == ExtrusionRole::erTopSolidInfill) return region->flow(FlowRole::frTopSolidInfill).width(); + // default + return region->flow(FlowRole::frPerimeter).width(); +} + +float estimate_curled_up_height( + float distance, float curvature, float layer_height, float flow_width, float prev_line_curled_height, Params params) +{ + float curled_up_height = 0; + if (fabs(distance) < 3.0 * flow_width) { + curled_up_height = std::max(prev_line_curled_height - layer_height * 0.75f, 0.0f); + //printf("If 1 %d\n",curled_up_height); + } + + //printf("distance %f, params.malformation_distance_factors.first %f, params.malformation_distance_factors.second %f, flow_width %f\n", distance, params.malformation_distance_factors.first, params.malformation_distance_factors.second, flow_width); + //printf("distance %f,params.malformation_distance_factors.first * flow_width %f, params.malformation_distance_factors.second * flow_width %f\n", distance, params.malformation_distance_factors.first * flow_width, params.malformation_distance_factors.second * flow_width); + + if (distance > params.malformation_distance_factors.first * flow_width && + distance < params.malformation_distance_factors.second * flow_width) { + + // imagine the extrusion profile. The part that has been glued (melted) with the previous layer will be called anchored section + // and the rest will be called curling section + // float anchored_section = flow_width - point.distance; + float curling_section = distance; + + // after extruding, the curling (floating) part of the extrusion starts to shrink back to the rounded shape of the nozzle + // The anchored part not, because the melted material holds to the previous layer well. + // We can assume for simplicity perfect equalization of layer height and raising part width, from which: + float swelling_radius = (layer_height + curling_section) / 2.0f; + curled_up_height += std::max(0.f, (swelling_radius - layer_height) / 2.0f); + + // On convex turns, there is larger tension on the floating edge of the extrusion then on the middle section. + // The tension is caused by the shrinking tendency of the filament, and on outer edge of convex trun, the expansion is greater and + // thus shrinking force is greater. This tension will cause the curling section to curle up + if (curvature > 0.01) { + float radius = (1.0 / curvature); + float curling_t = sqrt(radius / 100); + float b = curling_t * flow_width; + float a = curling_section; + float c = sqrt(std::max(0.0f, a * a - b * b)); + + curled_up_height += c; + } + curled_up_height = std::min(curled_up_height, params.max_curled_height_factor * layer_height); + } + + return curled_up_height; +} + +void estimate_malformations(LayerPtrs &layers, const Params ¶ms) +{ + LD prev_layer_lines{}; + for (Layer *l : layers) { + l->curled_lines.clear(); + std::vector boundary_lines = l->lower_layer != nullptr ? to_unscaled_linesf(l->lower_layer->lslices) : std::vector(); + AABBTreeLines::LinesDistancer prev_layer_boundary{std::move(boundary_lines)}; + std::vector current_layer_lines; + for (const LayerRegion *layer_region : l->regions()) { + for (const ExtrusionEntity *extrusion : layer_region->perimeters.flatten().entities) { + if (extrusion->role() != Slic3r::erExternalPerimeter) + continue; + Points extrusion_pts; + extrusion->collect_points(extrusion_pts); + float flow_width = get_flow_width(layer_region, extrusion->role()); + auto annotated_points = estimate_points_properties(extrusion_pts, prev_layer_lines, flow_width, + params.bridge_distance); + for (size_t i = 0; i < annotated_points.size(); ++i) { + const ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i]; + const ExtendedPoint &b = annotated_points[i]; + ExtrusionLine line_out{a.position.cast(), b.position.cast(), float((a.position - b.position).norm()), + extrusion}; + Vec2f middle = 0.5 * (line_out.a + line_out.b); + auto [middle_distance, bottom_line_idx, x] = prev_layer_lines.distance_from_lines_extra(middle); + ExtrusionLine bottom_line = prev_layer_lines.get_lines().empty() ? ExtrusionLine{} : + prev_layer_lines.get_line(bottom_line_idx); + + // correctify the distance sign using slice polygons + float sign = (prev_layer_boundary.distance_from_lines(middle.cast()) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f; + + line_out.curled_up_height = estimate_curled_up_height(middle_distance * sign, 0.5 * (a.curvature + b.curvature), + l->height, flow_width, bottom_line.curled_up_height, params); + + current_layer_lines.push_back(line_out); + } + } + } + for (const ExtrusionLine &line : current_layer_lines) { + if (line.curled_up_height > params.curling_tolerance_limit) { + l->curled_lines.push_back(CurledLine{Point::new_scale(line.a), Point::new_scale(line.b), line.curled_up_height}); + } + } + + prev_layer_lines = LD{current_layer_lines}; + } +} + +/* + + struct SupportGridFilter { private: @@ -169,18 +277,7 @@ struct SliceConnection } }; -float get_flow_width(const LayerRegion *region, ExtrusionRole role) -{ - if (role == ExtrusionRole::erBridgeInfill) return region->flow(FlowRole::frExternalPerimeter).width(); - if (role == ExtrusionRole::erExternalPerimeter) return region->flow(FlowRole::frExternalPerimeter).width(); - if (role == ExtrusionRole::erGapFill) return region->flow(FlowRole::frInfill).width(); - if (role == ExtrusionRole::erPerimeter) return region->flow(FlowRole::frPerimeter).width(); - if (role == ExtrusionRole::erSolidInfill) return region->flow(FlowRole::frSolidInfill).width(); - if (role == ExtrusionRole::erInternalInfill) return region->flow(FlowRole::frInfill).width(); - if (role == ExtrusionRole::erTopSolidInfill) return region->flow(FlowRole::frTopSolidInfill).width(); - // default - return region->flow(FlowRole::frPerimeter).width(); -} + std::vector to_short_lines(const ExtrusionEntity *e, float length_limit) { @@ -205,24 +302,7 @@ std::vector to_short_lines(const ExtrusionEntity *e, float length return lines; } -float estimate_curled_up_height( - const ExtendedPoint &point, float layer_height, float flow_width, float prev_line_curled_height, Params params) -{ - float curled_up_height = 0.0f; - if (fabs(point.distance) < 1.5 * flow_width) { - curled_up_height = 0.85 * prev_line_curled_height; - } - if (point.distance > params.malformation_distance_factors.first * flow_width && - point.distance < params.malformation_distance_factors.second * flow_width && point.curvature > -0.1f) { - float dist_factor = std::max(point.distance - params.malformation_distance_factors.first * flow_width, 0.01f) / - ((params.malformation_distance_factors.second - params.malformation_distance_factors.first) * flow_width); - curled_up_height = layer_height * sqrt(sqrt(dist_factor)) * std::clamp(3.0f * point.curvature, 1.0f, 3.0f); - curled_up_height = std::min(curled_up_height, params.max_curled_height_factor * layer_height); - } - - return curled_up_height; -} std::vector check_extrusion_entity_stability(const ExtrusionEntity *entity, const LayerRegion *layer_region, @@ -1104,78 +1184,7 @@ void estimate_supports_malformations(SupportLayerPtrs &layers, float flow_width, #endif } -void estimate_malformations(LayerPtrs &layers, const Params ¶ms) -{ -#ifdef DEBUG_FILES - FILE *debug_file = boost::nowide::fopen(debug_out_path("object_malformations.obj").c_str(), "w"); - FILE *full_file = boost::nowide::fopen(debug_out_path("object_full.obj").c_str(), "w"); -#endif - LD prev_layer_lines{}; - - for (Layer *l : layers) { - std::vector boundary_lines = l->lower_layer != nullptr ? to_unscaled_linesf(l->lower_layer->lslices) : std::vector(); - AABBTreeLines::LinesDistancer prev_layer_boundary{std::move(boundary_lines)}; - std::vector current_layer_lines; - for (const LayerRegion *layer_region : l->regions()) { - for (const ExtrusionEntity *extrusion : layer_region->perimeters().flatten().entities) { - - if (!extrusion->role().is_external_perimeter()) continue; - - Points extrusion_pts; - extrusion->collect_points(extrusion_pts); - float flow_width = get_flow_width(layer_region, extrusion->role()); - auto annotated_points = estimate_points_properties(extrusion_pts, prev_layer_lines, flow_width, - params.bridge_distance); - for (size_t i = 0; i < annotated_points.size(); ++i) { - ExtendedPoint &curr_point = annotated_points[i]; - float line_len = i > 0 ? ((annotated_points[i - 1].position - curr_point.position).norm()) : 0.0f; - ExtrusionLine line_out{i > 0 ? annotated_points[i - 1].position.cast() : curr_point.position.cast(), - curr_point.position.cast(), line_len, extrusion}; - - const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ? - prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) : - ExtrusionLine{}; - - float sign = (prev_layer_boundary.distance_from_lines(curr_point.position) + 0.5f * flow_width) < 0.0f ? -1.0f : - 1.0f; - curr_point.distance *= sign; - - line_out.curled_up_height = estimate_curled_up_height(curr_point, layer_region->layer()->height, flow_width, - nearest_prev_layer_line.curled_up_height, params); - - current_layer_lines.push_back(line_out); - } - } - } - - for (const ExtrusionLine &line : current_layer_lines) { - if (line.curled_up_height > 0.3f) { - l->malformed_lines.push_back(Line{Point::new_scale(line.a), Point::new_scale(line.b)}); - } - } - -#ifdef DEBUG_FILES - for (const ExtrusionLine &line : current_layer_lines) { - if (line.curled_up_height > 0.3f) { - Vec3f color = value_to_rgbf(-EPSILON, l->height * params.max_curled_height_factor, line.curled_up_height); - fprintf(debug_file, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], l->print_z, color[0], color[1], color[2]); - } - } - for (const ExtrusionLine &line : current_layer_lines) { - Vec3f color = value_to_rgbf(-EPSILON, l->height * params.max_curled_height_factor, line.curled_up_height); - fprintf(full_file, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], l->print_z, color[0], color[1], color[2]); - } -#endif - - prev_layer_lines = LD{current_layer_lines}; - } - -#ifdef DEBUG_FILES - fclose(debug_file); - fclose(full_file); -#endif -} std::vector> gather_issues(const SupportPoints &support_points, PartialObjects &partial_objects) { @@ -1262,6 +1271,7 @@ std::vector> gather_issues(const SupportPoint return result; } +*/ + } // namespace SupportSpotsGenerator } // namespace Slic3r -*/ \ No newline at end of file diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index c3fde4d6ae..eed2351f82 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -1,6 +1,6 @@ #ifndef SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_ #define SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_ -/* + #include "Layer.hpp" #include "Line.hpp" #include "PrintBase.hpp" @@ -9,6 +9,7 @@ #include #include + namespace Slic3r { namespace SupportSpotsGenerator { @@ -42,8 +43,9 @@ struct Params BrimType brim_type; const float brim_width; - const std::pair malformation_distance_factors = std::pair { 0.5, 1.1 }; + const std::pair malformation_distance_factors = std::pair { 0.33, 0.7 }; const float max_curled_height_factor = 10.0f; + const float curling_tolerance_limit = 0.1f; const float min_distance_between_support_points = 3.0f; //mm const float support_points_interface_radius = 1.5f; // mm @@ -77,6 +79,9 @@ struct Params } }; +void estimate_malformations(std::vector &layers, const Params ¶ms); + + enum class SupportPointCause { LongBridge, // point generated on bridge and straight perimeter extrusion longer than the allowed length FloatingBridgeAnchor, // point generated on unsupported bridge endpoint @@ -97,7 +102,7 @@ enum class SupportPointCause { // between forces that destabilize the object (extruder conflicts with curled filament, weight if instable center of mass, bed movements etc) // and forces that stabilize the object (bed adhesion, other support spots adhesion, weight if stable center of mass). // Note that the force is only the difference - the amount needed to stabilize the object again. -struct SupportPoint +/*struct SupportPoint { SupportPoint(SupportPointCause cause, const Vec3f &position, float force, float spot_radius, const Vec2f &direction) : cause(cause), position(position), force(force), spot_radius(spot_radius), direction(direction) @@ -147,13 +152,12 @@ using PartialObjects = std::vector; std::tuple full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params ¶ms); void estimate_supports_malformations(std::vector &layers, float supports_flow_width, const Params ¶ms); -void estimate_malformations(std::vector &layers, const Params ¶ms); // NOTE: the boolean marks if the issue is critical or not for now. std::vector> gather_issues(const SupportPoints &support_points, PartialObjects &partial_objects); - -}} // namespace Slic3r::SupportSpotsGenerator */ +}} // namespace Slic3r::SupportSpotsGenerator + #endif /* SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_ */ diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index f190e67a53..2f62eadf76 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -671,12 +671,15 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co bool have_avoid_crossing_perimeters = config->opt_bool("reduce_crossing_wall"); toggle_line("max_travel_detour_distance", have_avoid_crossing_perimeters); - + bool has_overhang_speed = config->opt_bool("enable_overhang_speed"); for (auto el : {"overhang_speed_classic", "overhang_1_4_speed", "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed"}) toggle_line(el, has_overhang_speed); + + bool has_overhang_speed_classic = config->opt_bool("overhang_speed_classic"); + toggle_line("slowdown_for_curled_perimeters",!has_overhang_speed_classic && has_overhang_speed); toggle_line("flush_into_objects", !is_global_config); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index cb0c761bed..7d369a3fae 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1957,6 +1957,7 @@ void TabPrint::build() optgroup = page->new_optgroup(L("Overhang speed"), L"param_speed", 15); optgroup->append_single_option_line("enable_overhang_speed", "slow-down-for-overhang"); optgroup->append_single_option_line("overhang_speed_classic", "slow-down-for-overhang"); + optgroup->append_single_option_line("slowdown_for_curled_perimeters"); Line line = { L("Overhang speed"), L("This is the speed for various overhang degrees. Overhang degrees are expressed as a percentage of line width. 0 speed means no slowing down for the overhang degree range and wall speed is used") }; line.label_path = "slow-down-for-overhang"; line.append_option(optgroup->get_option("overhang_1_4_speed"));