diff --git a/resources/ui_layout/default/printer_fff.ui b/resources/ui_layout/default/printer_fff.ui index fd995a3de7..f9a4af5d31 100644 --- a/resources/ui_layout/default/printer_fff.ui +++ b/resources/ui_layout/default/printer_fff.ui @@ -31,14 +31,14 @@ group:silent_mode_event:filename_format_event:Firmware setting:gcode_precision_xyz setting:gcode_precision_e end_line + line:Precision limits + setting:gcode_min_length + setting:gcode_min_resolution + end_line line:Processing limit setting:max_gcode_per_second setting:gcode_command_buffer end_line - line:Precision limits - setting:gcode_min_resolution - setting:gcode_min_length - end_line line:G2/G3 generation setting:arc_fitting setting:arc_fitting_resolution diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index fa876ab85b..3fb6f3215d 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -5324,45 +5324,52 @@ std::string GCodeGenerator::extrude_path(const ExtrusionPath &path, const std::s // simplify with gcode_resolution (not used yet). Simplify by jusntion deviation before the g1/sec count, to be able to use that decimation to reduce max_gcode_per_second triggers. // But as it can be visible on cylinders, should only be called if a max_gcode_per_second trigger may come. - const coordf_t scaled_min_length = scale_d(this->config().gcode_min_length.get_abs_value(m_current_perimeter_extrusion_width)); + const coordf_t scaled_min_length = this->config().gcode_min_length.is_enabled() ? + scale_d(this->config().gcode_min_length.get_abs_value(m_current_perimeter_extrusion_width)) : + 0; const coordf_t scaled_min_resolution = scale_d(this->config().gcode_min_resolution.get_abs_value(m_current_perimeter_extrusion_width)); - const int32_t gcode_buffer_window = this->config().gcode_command_buffer.value; - const int32_t max_gcode_per_second = this->config().max_gcode_per_second.value; - coordf_t scaled_mean_length = scaled_min_length * 2; + const int32_t max_gcode_per_second = this->config().max_gcode_per_second.is_enabled() ? + this->config().max_gcode_per_second.value : + 0; double fan_speed; if (max_gcode_per_second > 0) { + const int32_t gcode_buffer_window = this->config().gcode_command_buffer.value; double speed = _compute_speed_mm_per_sec(path, speed_mm_per_sec, fan_speed, nullptr); - scaled_mean_length = scale_d(speed / max_gcode_per_second); - } - if (scaled_mean_length > 0 && !m_last_too_small.empty()) { - //ensure that it's a continous thing of the same type - if (m_last_too_small.last_point().distance_to_square(path.first_point()) < EPSILON * EPSILON * 4 && - (path.role() == m_last_too_small.role() || m_last_too_small.length() < scale_d(m_last_too_small.width()/10))) { - simplifed_path.attributes_mutable().height = float(m_last_too_small.height() * m_last_too_small.length() + simplifed_path.height() * simplifed_path.length()) / float(m_last_too_small.length() + simplifed_path.length()); - simplifed_path.attributes_mutable().mm3_per_mm = (m_last_too_small.mm3_per_mm() * m_last_too_small.length() + simplifed_path.mm3_per_mm() * simplifed_path.length()) / (m_last_too_small.length() + simplifed_path.length()); - m_last_too_small.polyline.append(simplifed_path.polyline); - simplifed_path.polyline.swap(m_last_too_small.polyline); - assert(simplifed_path.height() == simplifed_path.height()); - assert(simplifed_path.mm3_per_mm() == simplifed_path.mm3_per_mm()); - m_last_too_small.polyline.clear(); - } else { - //finish extrude the little thing that was left before us and incompatible with our next extrusion. - ExtrusionPath to_finish = m_last_too_small; - gcode += this->_extrude(m_last_too_small, m_last_description, m_last_speed_mm_per_sec); - // put this very small segment in the buffer, as it's very small - m_last_command_buffer_used++; - m_last_too_small.polyline.clear(); + coordf_t scaled_mean_length = scale_d(speed / max_gcode_per_second); + if (!m_last_too_small.empty()) { + //ensure that it's a continous thing of the same type + if (m_last_too_small.last_point().distance_to_square(path.first_point()) < EPSILON * EPSILON * 4 && + (path.role() == m_last_too_small.role() || m_last_too_small.length() < scale_d(m_last_too_small.width()/10))) { + simplifed_path.attributes_mutable().height = float(m_last_too_small.height() * m_last_too_small.length() + simplifed_path.height() * simplifed_path.length()) / float(m_last_too_small.length() + simplifed_path.length()); + simplifed_path.attributes_mutable().mm3_per_mm = (m_last_too_small.mm3_per_mm() * m_last_too_small.length() + simplifed_path.mm3_per_mm() * simplifed_path.length()) / (m_last_too_small.length() + simplifed_path.length()); + m_last_too_small.polyline.append(simplifed_path.polyline); + simplifed_path.polyline.swap(m_last_too_small.polyline); + assert(simplifed_path.height() == simplifed_path.height()); + assert(simplifed_path.mm3_per_mm() == simplifed_path.mm3_per_mm()); + m_last_too_small.polyline.clear(); + } else { + //finish extrude the little thing that was left before us and incompatible with our next extrusion. + ExtrusionPath to_finish = m_last_too_small; + gcode += this->_extrude(m_last_too_small, m_last_description, m_last_speed_mm_per_sec); + // put this very small segment in the buffer, as it's very small + m_last_command_buffer_used++; + m_last_too_small.polyline.clear(); + } } - } - //set at least 2 buffer space, to not over-erase first lines. - if (gcode_buffer_window > 2 && gcode_buffer_window - m_last_command_buffer_used < 2) { - m_last_command_buffer_used = gcode_buffer_window - 2; - } + //set at least 2 buffer space, to not over-erase first lines. + if (gcode_buffer_window > 2 && gcode_buffer_window - m_last_command_buffer_used < 2) { + m_last_command_buffer_used = gcode_buffer_window - 2; + } - //simplify - m_last_command_buffer_used = simplifed_path.polyline.simplify_straits(scaled_min_resolution, scaled_min_length, scaled_mean_length, gcode_buffer_window, m_last_command_buffer_used); - + //simplify + m_last_command_buffer_used = simplifed_path.polyline.simplify_straits(scaled_min_resolution, + scaled_min_length, scaled_mean_length, + gcode_buffer_window, + m_last_command_buffer_used); + } else if (scaled_min_length > 0) { + simplifed_path.polyline.simplify_straits(scaled_min_resolution, scaled_min_length); + } // if the path is too small to be printed, put in the queue to be merge with the next one. if (scaled_min_length > 0 && simplifed_path.length() < scaled_min_length) { m_last_too_small = simplifed_path; @@ -6984,25 +6991,33 @@ Polyline GCodeGenerator::travel_to(std::string &gcode, const Point &point, Extru this->m_throw_if_canceled(); //if needed, remove points to avoid surcharging the printer. if (this->last_pos_defined()) { - const coordf_t scaled_min_length = scale_d(this->config().gcode_min_length.get_abs_value(m_current_perimeter_extrusion_width)); + const coordf_t scaled_min_length = this->config().gcode_min_length.is_enabled() ? + scale_d(this->config().gcode_min_length.get_abs_value(m_current_perimeter_extrusion_width)) : + 0; coordf_t scaled_min_resolution = scale_d(this->config().gcode_min_resolution.get_abs_value(m_current_perimeter_extrusion_width)); if (config().avoid_crossing_perimeters.value) { // min with peri/2 because it's less a problem for travels. but if travel don't cross, then they must not deviate much. scaled_min_resolution = std::min(scale_d(m_current_perimeter_extrusion_width / 4), scaled_min_resolution); } const int32_t gcode_buffer_window = this->config().gcode_command_buffer.value; - const int32_t max_gcode_per_second = this->config().max_gcode_per_second.value; - coordf_t scaled_mean_length = scaled_min_length * 2; + const int32_t max_gcode_per_second = this->config().max_gcode_per_second.is_enabled() ? + this->config().max_gcode_per_second.value : + 0; + coordf_t scaled_mean_length = 0; if (max_gcode_per_second > 0) { scaled_mean_length = scale_d(m_config.get_computed_value("travel_speed")) / max_gcode_per_second; - } - if (scaled_mean_length > 0) { + if (scaled_mean_length > 0) { + ArcPolyline poly_simplify(travel); + + //TODO: this is done after the simplification of the next extrusion. can't use the 'm_last_command_buffer_used' so it must began & end with 0 + poly_simplify.simplify_straits(scaled_min_resolution, scaled_min_length, scaled_mean_length, gcode_buffer_window, -1); + assert(!poly_simplify.has_arc()); + //TODO: create arc here? + travel = poly_simplify.to_polyline(); + } + } else if(scaled_min_length > 0) { ArcPolyline poly_simplify(travel); - - //TODO: this is done after the simplification of the next extrusion. can't use the 'm_last_command_buffer_used' so it must began & end with 0 - poly_simplify.simplify_straits(scaled_min_resolution, scaled_min_length, scaled_mean_length, gcode_buffer_window, -1); - assert(!poly_simplify.has_arc()); - //TODO: create arc here? + poly_simplify.simplify_straits(scaled_min_resolution, scaled_min_length); travel = poly_simplify.to_polyline(); } } diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index e984664141..21554928a3 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -1194,6 +1194,7 @@ Geometry::ArcWelder::Path ArcPolyline::_from_polyline(const Points &poly) path.emplace_back(std::move(point), 0, Geometry::ArcWelder::Orientation::Unknown); return path; } +#pragma UNOPTIMIZE Geometry::ArcWelder::Path ArcPolyline::_from_polyline(std::initializer_list poly) { Geometry::ArcWelder::Path path; @@ -1336,6 +1337,7 @@ int ArcPolyline::simplify_straits(coordf_t min_tolerance, arc.erase(arc.begin() + worst_idx); buffer_length -= line_length[worst_idx]; line_length.erase(line_length.begin() + worst_idx); + assert(weights[worst_idx] > 0); weights.erase(weights.begin() + worst_idx); --current_buffer_size; // recompute next point things @@ -1361,7 +1363,7 @@ int ArcPolyline::simplify_straits(coordf_t min_tolerance, //check if the previous point has enough dist at both end if (current_buffer_size > 0 && arc.back() == 0 && min_point_distance > line_length.back() && min_point_distance > new_seg_length - // also make sure it's not an importnat point for a ponty tip. + // also make sure it's not an important point for a ponty tip. && new_seg_length < m_path[idxs[idxs.size() - 2]].point.distance_to(new_point) ) { // erase previous point @@ -1370,6 +1372,7 @@ int ArcPolyline::simplify_straits(coordf_t min_tolerance, arc.pop_back(); buffer_length -= line_length.back(); line_length.pop_back(); + assert(weights.back() > 0); weights.pop_back(); --current_buffer_size; new_seg_length = coord_t(m_path[idxs.back()].point.distance_to(new_point)); @@ -1403,12 +1406,13 @@ int ArcPolyline::simplify_straits(coordf_t min_tolerance, for (size_t i = 1; i < idxs.size(); ++i) assert(idxs[i - 1] < idxs[i]); - //remove first point(s) if enough dist + // remove first point(s) if enough dist while (buffer_length > min_buffer_length && current_buffer_size > 1) { idxs.pop_front(); // this erase the idx before the first point. we keep first point idx as a 'previous' arc.pop_front(); buffer_length -= line_length.front(); line_length.pop_front(); + assert(weights.front() > 0); weights.pop_front(); --current_buffer_size; } @@ -1449,6 +1453,37 @@ int ArcPolyline::simplify_straits(coordf_t min_tolerance, return current_buffer_size; } + +void ArcPolyline::simplify_straits(const coordf_t min_tolerance, + const coordf_t min_point_distance) +{ + assert(is_valid()); + + //use a window of buffer size. + const coord_t min_point_distance_sqr = min_point_distance * min_point_distance; + + for (size_t idx_pt = 1; idx_pt < this->m_path.size() - 1; ++idx_pt) { + // only erase point between two strait segment + if (m_path[idx_pt].radius == 0 && m_path[idx_pt + 1].radius != 0) { + // Get previous & next point + Point previous = m_path[idx_pt - 1].point; + Point current = m_path[idx_pt].point; + Point next = m_path[idx_pt + 1].point; + // check deviation + coordf_t deviation = Line::distance_to(current, previous, next); + //if devaition is small enough and the distance is too small + if (deviation < min_tolerance && + (min_point_distance_sqr < previous.distance_to_square(current) || + min_point_distance_sqr < current.distance_to_square(next))) { + m_path.erase(m_path.begin() + idx_pt); + } + } + } + assert(is_valid()); + //at the end, we should have the buffer no more than 1/2 filled. +} + + // douglas_peuker and create arc if with_fitting_arc void ArcPolyline::make_arc(ArcFittingType with_fitting_arc, coordf_t tolerance, double fit_percent_tolerance) { diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index d6c803e224..3ba40ee83f 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -287,7 +287,7 @@ typedef std::vector Polylines3; class ArcPolyline { protected: - // each segment is strait if it's radius ==0 (oriantation should be unknown in this case) + // each segment is strait if it's radius ==0 (orientation should be unknown in this case) // radius is negative if the arc betweent he two point is the longest of the two. it's positive if it's the shortest. // the sign of the radius and the orientation are two different way to get the same information. They MUST be in synch. // note: first Segment in Path is always "strait", as it's the starting point of the following segment. @@ -367,6 +367,7 @@ class ArcPolyline void make_arc(ArcFittingType with_fitting_arc, coordf_t tolerance, double fit_percent_tolerance); // remove strait segemnts that are too near each other, and will overlaod the firmware. Return the buffer lines it still uses a t the end. int simplify_straits(coordf_t min_tolerance, coordf_t min_point_distance, coordf_t mean_dist_per_line, const int buffer_size, const int buffer_init); + void simplify_straits(const coordf_t min_tolerance, const coordf_t min_point_distance); // remove points that are too near each other, and return false if the whole path is too small bool normalize(); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 8c0ada95e4..fd62ceabf3 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3841,16 +3841,17 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionFloats{ 1500., 1250. }); def = this->add("max_gcode_per_second", coFloat); - def->label = L("Maximum G1 per second"); + def->label = L("Maximum G1 per second (Experimental)"); def->category = OptionCategory::speed; def->tooltip = L("If your firmware stops while printing, it may have its gcode queue full." " Set this parameter to merge extrusions into bigger ones to reduce the number of gcode commands the printer has to process each second." "\nOn 8bit controlers, a value of 150 is typical." "\nNote that reducing your printing speed (at least for the external extrusions) will reduce the number of time this will triggger and so increase quality." - "\nSet zero to disable."); + "\nDisabled if set to 0."); def->min = 0; def->mode = comExpert | comSuSi; - def->set_default_value(new ConfigOptionFloat(1500)); + def->can_be_disabled = true; + def->set_default_value(disable_defaultoption(new ConfigOptionFloat(1500), true)); def = this->add("max_fan_speed", coInts); def->label = L("Max"); @@ -4869,12 +4870,13 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("When outputting gcode, this setting ensure that there is almost no commands more than this value apart." " Be sure to also use max_gcode_per_second instead, as it's much better when you have very different speeds for features" " (Too many too small commands may overload the firmware / connection)." - "\nSet zero to disable."); + "\nDisabled if set to 0."); def->sidetext = L("mm or %"); def->min = 0; def->precision = 6; def->mode = comExpert | comSuSi; - def->set_default_value(new ConfigOptionFloatOrPercent(0.02, false)); + def->can_be_disabled = true; + def->set_default_value(disable_defaultoption(new ConfigOptionFloatOrPercent(0.02, false), false)); def->aliases = {"min_length"}; def = this->add("gcode_min_resolution", coFloatOrPercent); @@ -4887,7 +4889,7 @@ void PrintConfigDef::init_fff_params() def->min = 0; def->precision = 6; def->mode = comExpert | comSuSi; - def->set_default_value(new ConfigOptionFloatOrPercent(50, true)); + def->set_default_value(new ConfigOptionFloatOrPercent(10, true)); def = this->add("resolution_internal", coFloat); def->label = L("Internal resolution"); @@ -8657,8 +8659,16 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va value = opt_decoder.serialize(); } } - if ("max_layer_height" == opt_key && "0" == value) { - value = "!75%"; + if ("0" == value) { + if ("max_layer_height" == opt_key) { + value = "!75%"; + } + if ("gcode_min_length" == opt_key) { + value = "!0"; + } + if ("max_gcode_per_second" == opt_key) { + value = "!0"; + } } if (value == "-1") { if ("overhangs_bridge_threshold" == opt_key) {value = "!0";} diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 5391535b1a..cefe2069d6 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -722,9 +722,10 @@ void ConfigManipulation::toggle_printer_fff_options(DynamicPrintConfig *config, bool have_remaining_times = config->opt_bool("remaining_times"); toggle_field("remaining_times_type", have_remaining_times); - bool has_gcode_culling = config->get_float("gcode_min_length") > 0 || config->get_float("max_gcode_per_second") > 0; - toggle_field("gcode_min_resolution", has_gcode_culling); - toggle_field("gcode_command_buffer", has_gcode_culling); + bool gcode_min_length = config->get_float("gcode_min_length") > 0 && config->is_enabled("gcode_min_length"); + bool max_gcode_per_second = config->get_float("max_gcode_per_second") > 0 && config->is_enabled("max_gcode_per_second"); + toggle_field("gcode_min_resolution", gcode_min_length || max_gcode_per_second); + toggle_field("gcode_command_buffer", max_gcode_per_second); auto flavor = config->option>("gcode_flavor")->value; bool is_marlin_flavor = flavor == gcfMarlinLegacy || flavor == gcfMarlinFirmware;