Skip to content

Commit

Permalink
Update to gcode_min_length & max_gcode_per_second:
Browse files Browse the repository at this point in the history
 * now can be disabled via checkbox
 * fixed when using gcode_min_length and not max_gcode_per_second
 * max_gcode_per_second still broken.
  • Loading branch information
supermerill committed Nov 9, 2024
1 parent 90d865c commit bef6220
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 60 deletions.
8 changes: 4 additions & 4 deletions resources/ui_layout/default/printer_fff.ui
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
99 changes: 57 additions & 42 deletions src/libslic3r/GCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}
}
Expand Down
39 changes: 37 additions & 2 deletions src/libslic3r/Polyline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Point> poly)
{
Geometry::ArcWelder::Path path;
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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));
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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)
{
Expand Down
3 changes: 2 additions & 1 deletion src/libslic3r/Polyline.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ typedef std::vector<Polyline3> 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.
Expand Down Expand Up @@ -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();
Expand Down
26 changes: 18 additions & 8 deletions src/libslic3r/PrintConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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);
Expand All @@ -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");
Expand Down Expand Up @@ -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";}
Expand Down
7 changes: 4 additions & 3 deletions src/slic3r/GUI/ConfigManipulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value;
bool is_marlin_flavor = flavor == gcfMarlinLegacy || flavor == gcfMarlinFirmware;
Expand Down

0 comments on commit bef6220

Please sign in to comment.