From 31bbe80c16a45f9d8bbe86f8b6546cfde4a47ee8 Mon Sep 17 00:00:00 2001 From: Christos Karampeazis-Papadakis Date: Sat, 22 Jun 2024 15:17:15 +0200 Subject: [PATCH] Implement Small Area Infill Flow Compensation * Use graph option type * Disable small area flow compensation on first layer Reasoning: We do not want to affect the first layer's extrusion in any way, especially by underextruding it. Doing so might cause user confusion and/or adhesion issues. inspired by OrcaSlicer Credits: - Graph adaptation: @supermerill - OrcaSlicer port: @mjonuschat - Original implementation: @Alexander-T-Moss supermerill/SuperSlicer#4329 --- resources/ui_layout/default/print.ui | 4 +++ resources/ui_layout/example/print.ui | 4 +++ src/libslic3r/Config.cpp | 19 ++++++++++++++ src/libslic3r/Fill/Fill.cpp | 6 ++++- src/libslic3r/GCode.cpp | 37 +++++++++++++++++++++----- src/libslic3r/GCode.hpp | 2 +- src/libslic3r/Preset.cpp | 3 ++- src/libslic3r/PrintConfig.cpp | 38 +++++++++++++++++++++++++++ src/libslic3r/PrintConfig.hpp | 2 ++ src/libslic3r/PrintObject.cpp | 2 ++ src/slic3r/GUI/ConfigManipulation.cpp | 5 ++++ 11 files changed, 113 insertions(+), 9 deletions(-) diff --git a/resources/ui_layout/default/print.ui b/resources/ui_layout/default/print.ui index ea3a565f88d..669b74a8464 100644 --- a/resources/ui_layout/default/print.ui +++ b/resources/ui_layout/default/print.ui @@ -238,6 +238,10 @@ group:Advanced Infill options setting:label_width$8:width$5:fill_smooth_distribution setting:label_width$26:label$Spacing between ironing lines:width$5:sidetext_width$7:fill_smooth_width end_line + line:Small Area Infill Flow Compensation (beta) + setting:label$_:sidetext_width$0:small_area_infill_flow_compensation + setting:label$_:small_area_infill_flow_compensation_model + end_line group:title_width$19:Ironing post-process (This will go on top of infills and perimeters) line:Enable ironing post-process setting:label$_:sidetext_width$0:ironing diff --git a/resources/ui_layout/example/print.ui b/resources/ui_layout/example/print.ui index 5d90c54bdaf..228c58d59e8 100644 --- a/resources/ui_layout/example/print.ui +++ b/resources/ui_layout/example/print.ui @@ -214,6 +214,10 @@ group:Advanced Infill options setting:label_width$8:width$5:fill_smooth_distribution setting:label_width$26:label$Spacing between ironing lines:width$5:sidetext_width$7:fill_smooth_width end_line + line:Small Area Infill Flow Compensation (beta) + setting:label$_:sidetext_width$0:small_area_infill_flow_compensation + setting:label_:small_area_infill_flow_compensation_model + end_line group:title_width$19:Ironing post-process (This will go on top of infills and perimeters) line:Enable ironing post-process setting:label$_:sidetext_width$0:ironing diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 1915fba68b2..2ee14d3b07f 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -453,6 +453,25 @@ bool GraphData::deserialize(const std::string &str) this->begin_idx = 0; this->end_idx = this->graph_points.size(); this->type = GraphType::SPLINE; + } else if (size_t pos = str.find(','); pos != std::string::npos) { + //maybe a coStrings with 0,0 values inside, like a coPoints but worse (used by orca's small_area_infill_flow_compensation_model) + std::vector args; + boost::split(args, str, boost::is_any_of(",")); + if (args.size() % 2 == 0) { + for (size_t i = 0; i < args.size(); i += 2) { + this->graph_points.emplace_back(); + Vec2d &data_point = this->graph_points.back(); + args[i].erase(std::remove(args[i].begin(), args[i].end(), '\n'), args[i].end()); + args[i].erase(std::remove(args[i].begin(), args[i].end(), '"'), args[i].end()); + data_point.x() = std::stod(args[i]); + args[i+1].erase(std::remove(args[i+1].begin(), args[i+1].end(), '\n'), args[i+1].end()); + args[i+1].erase(std::remove(args[i+1].begin(), args[i+1].end(), '"'), args[i+1].end()); + data_point.y() = std::stod(args[i+1]); + } + } + this->begin_idx = 0; + this->end_idx = this->graph_points.size(); + this->type = GraphType::SPLINE; } else { std::istringstream iss(str); std::string item; diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index bbf16cbe141..4bcf2e1a1a9 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -86,7 +86,9 @@ struct SurfaceFillParams : FillParams RETURN_COMPARE_NON_EQUAL(config->gap_fill_acceleration); RETURN_COMPARE_NON_EQUAL(config->gap_fill_speed); RETURN_COMPARE_NON_EQUAL(config->print_extrusion_multiplier); - RETURN_COMPARE_NON_EQUAL(config->region_gcode.value); + RETURN_COMPARE_NON_EQUAL(config->region_gcode.value) + RETURN_COMPARE_NON_EQUAL(config->small_area_infill_flow_compensation.value) + RETURN_COMPARE_NON_EQUAL(config->small_area_infill_flow_compensation_model.value); } if (config == nullptr || rhs.config == nullptr || max_sparse_infill_spacing == 0) RETURN_COMPARE_NON_EQUAL(flow.width()); @@ -115,6 +117,8 @@ struct SurfaceFillParams : FillParams || config->gap_fill_speed != rhs.config->gap_fill_speed || config->print_extrusion_multiplier != rhs.config->print_extrusion_multiplier || config->region_gcode != rhs.config->region_gcode + || config->small_area_infill_flow_compensation != rhs.config->small_area_infill_flow_compensation + || config->small_area_infill_flow_compensation_model != rhs.config->small_area_infill_flow_compensation_model )) return false; // then check params diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 5d481d8bde6..cb6a86fa744 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -5595,16 +5595,41 @@ std::vector cut_corner_cache = { 0.252510726678311,0.262777267777188,0.27352986689699,0.284799648665007,0.296620441746888,0.309029079319231,0.322065740515038,0.335774339512048,0.350202970204428,0.365404415947691, 0.381436735764648,0.398363940736199,0.416256777189962,0.435193636891737,0.455261618934834 }; - -void GCode::_extrude_line(std::string& gcode_str, const Line& line, const double e_per_mm, const std::string& comment) { +void GCode::_extrude_line(std::string& gcode_str, const Line& line, const double e_per_mm, const std::string& comment, + ExtrusionRole role) { if (line.a.coincides_with_epsilon(line.b)) { assert(false); // todo: investigate if it happens (it happens in perimeters) return; } + std::string comment_copy = comment; + double unscaled_line_length = unscaled(line.length()); + double extrusion_value = e_per_mm * unscaled_line_length; + // small_area_infill_flow_compensation + // this is only done in _extrude_line and not in _extrude_line_cut_corner because _extrude_line_cut_corner doesn't apply to solid infill, but only for external perimeters. + if (!this->on_first_layer() && (role == ExtrusionRole::erSolidInfill || role == ExtrusionRole::erTopSolidInfill) && + m_config.small_area_infill_flow_compensation.value && + m_config.small_area_infill_flow_compensation_model.value.data_size() > 1) { + GraphData graph = m_config.small_area_infill_flow_compensation_model.value; + assert(graph.begin_idx >= 0 && graph.begin_idx + 1 < graph.end_idx && graph.end_idx <= graph.graph_points.size()); + // ensure it start at length = 0, and ensure it ends with a compensation of 1. + graph.graph_points[graph.begin_idx].x() = 0; + graph.graph_points[graph.end_idx - 1].y() = 1; + //interpolate and verify + double new_extrusion_value = extrusion_value * graph.interpolate(unscaled_line_length); + assert(new_extrusion_value > 0.0); + if (new_extrusion_value != extrusion_value) { + extrusion_value = (new_extrusion_value > 0.0) ? new_extrusion_value : 0.0; + if (m_config.gcode_comments) { + comment_copy += Slic3r::format(_(L(" | Old Flow Value: %0.5f Length: %0.5f")), extrusion_value, + unscaled_line_length); + } + } + } + // end small_area_infill_flow_compensation gcode_str += m_writer.extrude_to_xy( this->point_to_gcode(line.b), - e_per_mm * unscaled(line.length()), - comment); + extrusion_value, + comment_copy); } void GCode::_extrude_line_cut_corner(std::string& gcode_str, const Line& line, const double e_per_mm, const std::string& comment, Point& last_pos, const double path_width) { @@ -5745,7 +5770,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string &descri for (const Line& line : path.polyline.lines()) { if (path.role() != erExternalPerimeter || config().external_perimeter_cut_corners.value == 0) { // normal & legacy pathcode - _extrude_line(gcode, line, e_per_mm, comment); + _extrude_line(gcode, line, e_per_mm, comment, path.role()); } else { _extrude_line_cut_corner(gcode, line, e_per_mm, comment, last_pos, path.width); } @@ -5763,7 +5788,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string &descri const Line line = Line(path.polyline.get_points()[point_index - 1], path.polyline.get_points()[point_index]); if (path.role() != erExternalPerimeter || config().external_perimeter_cut_corners.value == 0) { // normal & legacy pathcode - _extrude_line(gcode, line, e_per_mm, comment); + _extrude_line(gcode, line, e_per_mm, comment, path.role()); } else { _extrude_line_cut_corner(gcode, line, e_per_mm, comment, last_pos, path.width); } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 193d18371c1..09d30fa6269 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -519,7 +519,7 @@ class GCode : ExtrusionVisitorConst { double compute_e_per_mm(double path_mm3_per_mm); std::string _extrude(const ExtrusionPath &path, const std::string &description, double speed = -1); - void _extrude_line(std::string& gcode_str, const Line& line, const double e_per_mm, const std::string& comment); + void _extrude_line(std::string& gcode_str, const Line& line, const double e_per_mm, const std::string& comment, ExtrusionRole role); void _extrude_line_cut_corner(std::string& gcode_str, const Line& line, const double e_per_mm, const std::string& comment, Point& last_pos, const double path_width); std::string _before_extrude(const ExtrusionPath &path, const std::string &description, double speed = -1); double_t _compute_speed_mm_per_sec(const ExtrusionPath &path, double speed, std::string *comment); diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index c653b0012ba..57e85994df3 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -679,6 +679,7 @@ static std::vector s_Preset_print_options { "bridge_overlap_min", "first_layer_flow_ratio", "clip_multipart_objects", "enforce_full_fill_volume", "external_infill_margin", "bridged_infill_margin", + "small_area_infill_flow_compensation", "small_area_infill_flow_compensation_model", // compensation "first_layer_size_compensation", "first_layer_size_compensation_layers", @@ -737,7 +738,7 @@ static std::vector s_Preset_print_options { "milling_speed", //Arachne "perimeter_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", - "wall_distribution_count", "min_feature_size", "min_bead_width" + "wall_distribution_count", "min_feature_size", "min_bead_width", }; static std::vector s_Preset_filament_options { diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 15196b3989d..17c3232a9a0 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2510,6 +2510,42 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("%"); def->set_default_value(new ConfigOptionPercent(10)); + def = this->add("small_area_infill_flow_compensation", coBool); + def->label = L("Enable small area flow compensation"); + def->category = OptionCategory::infill; + def->tooltip = L("Enable flow compensation for small infill areas." + "\nFirst layer is always disabled, to not compromise adhesion."); + def->mode = comExpert | comSuSi; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("small_area_infill_flow_compensation_model", coGraph); + def->label = L("Flow Compensation Model"); + def->category = OptionCategory::infill; + def->tooltip = L("Flow Compensation Model, used to adjust the flow for small solid infill " + "lines. The model is a graph of flow correction factors (between 0 and 1) per extrusion length (in mm)." + "\nThe first point length has to be 0mm. the last point need to have a flow correction of 1."); + def->mode = comExpert | comSuSi; + def->set_default_value(new ConfigOptionGraph(GraphData(0,10, GraphData::GraphType::SPLINE, + {{0,0},{0.2,0.44},{0.4,0.61},{0.6,0.7},{0.8,0.76},{1.5,0.86},{2,0.89},{3,0.92},{5,0.95},{10,1}} + ))); + def->graph_settings = std::make_shared(); + def->graph_settings->title = L("Flow Compensation Model"); + def->graph_settings->description = def->tooltip; + def->graph_settings->x_label = L("Length of an extrusion (mm)"); + def->graph_settings->y_label = L("Flow correction (ratio between 0 and 1)"); + def->graph_settings->null_label = L("No values"); + def->graph_settings->label_min_x = ""; + def->graph_settings->label_max_x = L("Maximum length"); + def->graph_settings->label_min_y = L("Minimum ratio"); + def->graph_settings->label_max_y = L("Maximum ratio"); + def->graph_settings->min_x = 0; + def->graph_settings->max_x = 100; + def->graph_settings->step_x = 0.1; + def->graph_settings->min_y = 0; + def->graph_settings->max_y = 1; + def->graph_settings->step_y = 0.01; + def->graph_settings->allowed_types = {GraphData::GraphType::LINEAR, GraphData::GraphType::SPLINE, GraphData::GraphType::SQUARE}; + def = this->add("first_layer_acceleration", coFloatOrPercent); def->label = L("Max"); def->full_label = L("First layer acceleration"); @@ -8467,6 +8503,8 @@ std::unordered_set prusa_export_to_remove_keys = { "skirt_brim", "skirt_distance_from_brim", "skirt_extrusion_width", +"small_area_infill_flow_compensation", +"small_area_infill_flow_compensation_model", "small_perimeter_max_length", "small_perimeter_min_length", "solid_fill_pattern", diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index b7b88778410..e24e1e3359f 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -952,6 +952,8 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, print_retract_length)) ((ConfigOptionFloat, print_retract_lift)) ((ConfigOptionString, region_gcode)) + ((ConfigOptionBool, small_area_infill_flow_compensation)) + ((ConfigOptionGraph, small_area_infill_flow_compensation_model)) ((ConfigOptionFloatOrPercent, small_perimeter_speed)) ((ConfigOptionFloatOrPercent, small_perimeter_min_length)) ((ConfigOptionFloatOrPercent, small_perimeter_max_length)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 84bba807261..20fc7fc317a 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1031,6 +1031,8 @@ FillLightning::GeneratorPtr PrintObject::prepare_lightning_infill_data() || opt_key == "seam_notch_outer" || opt_key == "seam_travel_cost" || opt_key == "seam_visibility" + || opt_key == "small_area_infill_flow_compensation" + || opt_key == "small_area_infill_flow_compensation_model" || opt_key == "small_perimeter_speed" || opt_key == "small_perimeter_min_length" || opt_key == "small_perimeter_max_length" diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index e5d7d5273ad..8b0cbe11f67 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -434,6 +434,11 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field("fill_angle", (have_infill || has_solid_infill) && ((ConfigOptionVectorBase*)config->option("fill_angle_template"))->size() == 0); + + toggle_field("small_area_infill_flow_compensation", has_solid_infill); + bool have_small_area_infill_flow_compensation = has_solid_infill && config->opt_bool("small_area_infill_flow_compensation"); + toggle_field("small_area_infill_flow_compensation_model", have_small_area_infill_flow_compensation); + toggle_field("top_solid_min_thickness", ! has_spiral_vase && has_top_solid_infill); toggle_field("bottom_solid_min_thickness", ! has_spiral_vase && has_bottom_solid_infill);