Porting of Pressure Equalizer feature from Prusa Slicer 2.6.x (#2161)

* Overhang perimeter handling

Updated code to handle overhang perimeters as an overhang and not as a bridge.

* Preparing to add curled extrusions identification

* Porting curling calculations from Prusa Slier 2.6.1

* Prototype 1 - slowdown extended to detect curled edges and further reduce speed

First prototype of the code submitted.

* Working prototype - 2

Code is now finally working - external perimeters are slowed down as needed when there is likelyhood of curling up.

ToDo:
1. Reslicing the model causes the algorithm not to run - need to find where this fails to trigger the call for this.
2. Slowdown of internal perimeters not working yet.

* Updated to use overhang wall speed instead of bridging speed for this algorithm

* Fixed bug in speed calculation and tweaked parameters for high speed printer

Fixed bug in speed calculation and tweaked parameters for high speed printer

* Attempting to fix "set started" not being set

* Parameter tweak after print tests

* Fixed estimation not running when model is re-sliced.

* Removing debug printf statements and fixed threading flag.

* Fixed threading

* Parameter tweaks following print tests

* Made this as an option in the GUI

* Reintroduced handling of bridges as per original design

* UI line toggling when option makes sense to be visible.

* Fixed bug in field visibility & made it default to off

* Code optimisation

* Initial commit of code from Prusa Slicer 2.6.1

* Ported ExtrusionRole from Prusa Slicer 2.6.1

* fix compile errors

* Update GCode.hpp

* code changes to invoke pressure equalizer

* attempting to trigger pressure equalizer

(Not compiling)

* Update Fill.cpp

* Update Fill.cpp

* Pressure equaliser layer result update

* Further commits

* Merged PR https://github.com/prusa3d/PrusaSlicer/pull/9622

* First complete working version

* Update PressureEqualizer.cpp

* Implemented parameter in GUI

* Toggle fields according to compatibility

* Updated UI toggles between extrusion rate slope and arc fitting.

* Updated tooltip

* Introduced parameter smoothing segment length

This parameter influences the number of division a line will undergo in response to the requirement to adhere to the extrusion rate flow adjustment.

* Internal parameter and tool tip tweaking

* Parameter and tool tip tweaking

* Updated parameters and tooltip following testing.

* Sync PressureEq with latest PrusaSlicer

* Revert "Sync PressureEq with latest PrusaSlicer"

This reverts commit 131fb94c6b.

---------

Co-authored-by: MGunlogson <MGunlogson@users.noreply.github.com>
Co-authored-by: Vojtech Bubnik <bubnikv@gmail.com>
This commit is contained in:
Ioannis Giannakas 2023-09-27 14:44:45 +01:00 committed by GitHub
parent 78a8bad6f4
commit cf846195cc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 824 additions and 431 deletions

View file

@ -1,139 +1,218 @@
///|/ Copyright (c) Prusa Research 2016 - 2023 Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas, Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena
///|/ Copyright (c) SuperSlicer 2023 Remi Durand @supermerill
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include <memory.h>
#include <string.h>
#include <float.h>
#include <cstring>
#include <cfloat>
#include <algorithm>
#include "../libslic3r.h"
#include "../PrintConfig.hpp"
#include "../LocalesUtils.hpp"
#include "../GCode.hpp"
#include "PressureEqualizer.hpp"
#include "fast_float/fast_float.h"
#include "GCodeWriter.hpp"
namespace Slic3r {
PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig *config) :
m_config(config)
{
reset();
}
static const std::string EXTRUSION_ROLE_TAG = ";_EXTRUSION_ROLE:";
static const std::string EXTRUDE_END_TAG = ";_EXTRUDE_END";
static const std::string EXTRUDE_SET_SPEED_TAG = ";_EXTRUDE_SET_SPEED";
static const std::string EXTERNAL_PERIMETER_TAG = ";_EXTERNAL_PERIMETER";
PressureEqualizer::~PressureEqualizer()
{
}
// For how many GCode lines back will adjust a flow rate from the latest line.
// Bigger values affect the GCode export speed a lot, and smaller values could
// affect how distant will be propagated a flow rate adjustment.
static constexpr int max_look_back_limit = 128;
void PressureEqualizer::reset()
{
circular_buffer_pos = 0;
circular_buffer_size = 100;
circular_buffer_items = 0;
circular_buffer.assign(circular_buffer_size, GCodeLine());
// Max non-extruding XY distance (travel move) in mm between two continous extrusions where we pretend
// its all one continous extruded line. Above this distance we assume extruder pressure hits 0
// This exists because often there's tiny travel moves between stuff like infill
// lines where some extruder pressure will remain (so we should equalize between these small travels)
static constexpr long max_ignored_gap_between_extruding_segments = 3;
PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig &config) : m_use_relative_e_distances(config.use_relative_e_distances.value)
{
// Preallocate some data, so that output_buffer.data() will return an empty string.
output_buffer.assign(32, 0);
output_buffer_length = 0;
output_buffer_length = 0;
output_buffer_prev_length = 0;
m_current_extruder = 0;
// Zero the position of the XYZE axes + the current feed
memset(m_current_pos, 0, sizeof(float) * 5);
m_current_extrusion_role = erNone;
m_current_extrusion_role = GCodeExtrusionRole::None;
// Expect the first command to fill the nozzle (deretract).
m_retracted = true;
m_max_segment_length = 2.f;
// Calculate filamet crossections for the multiple extruders.
m_filament_crossections.clear();
for (size_t i = 0; i < m_config->filament_diameter.values.size(); ++ i) {
double r = m_config->filament_diameter.values[i];
double a = 0.25f*M_PI*r*r;
for (double r : config.filament_diameter.values) {
double a = 0.25f * M_PI * r * r;
m_filament_crossections.push_back(float(a));
}
m_max_segment_length = 20.f;
// Volumetric rate of a 0.45mm x 0.2mm extrusion at 60mm/s XY movement: 0.45*0.2*60*60=5.4*60 = 324 mm^3/min
// Volumetric rate of a 0.45mm x 0.2mm extrusion at 20mm/s XY movement: 0.45*0.2*20*60=1.8*60 = 108 mm^3/min
// Slope of the volumetric rate, changing from 20mm/s to 60mm/s over 2 seconds: (5.4-1.8)*60*60/2=60*60*1.8 = 6480 mm^3/min^2 = 1.8 mm^3/s^2
m_max_volumetric_extrusion_rate_slope_positive = (m_config == NULL) ? 6480.f :
m_config->max_volumetric_extrusion_rate_slope_positive.value * 60.f * 60.f;
m_max_volumetric_extrusion_rate_slope_negative = (m_config == NULL) ? 6480.f :
m_config->max_volumetric_extrusion_rate_slope_negative.value * 60.f * 60.f;
for (size_t i = 0; i < numExtrusionRoles; ++ i) {
m_max_volumetric_extrusion_rate_slopes[i].negative = m_max_volumetric_extrusion_rate_slope_negative;
m_max_volumetric_extrusion_rate_slopes[i].positive = m_max_volumetric_extrusion_rate_slope_positive;
if(config.max_volumetric_extrusion_rate_slope.value > 0){
m_max_volumetric_extrusion_rate_slope_positive = float(config.max_volumetric_extrusion_rate_slope.value) * 60.f * 60.f;
m_max_volumetric_extrusion_rate_slope_negative = float(config.max_volumetric_extrusion_rate_slope.value) * 60.f * 60.f;
m_max_segment_length = float(config.max_volumetric_extrusion_rate_slope_segment_length.value);
}
// Don't regulate the pressure in infill.
m_max_volumetric_extrusion_rate_slopes[erBridgeInfill].negative = 0;
m_max_volumetric_extrusion_rate_slopes[erBridgeInfill].positive = 0;
// Don't regulate the pressure in gap fill.
m_max_volumetric_extrusion_rate_slopes[erGapFill].negative = 0;
m_max_volumetric_extrusion_rate_slopes[erGapFill].positive = 0;
for (ExtrusionRateSlope &extrusion_rate_slope : m_max_volumetric_extrusion_rate_slopes) {
extrusion_rate_slope.negative = m_max_volumetric_extrusion_rate_slope_negative;
extrusion_rate_slope.positive = m_max_volumetric_extrusion_rate_slope_positive;
}
// Don't regulate the pressure before and after ironing.
for (const GCodeExtrusionRole er : {GCodeExtrusionRole::Ironing}) {
m_max_volumetric_extrusion_rate_slopes[size_t(er)].negative = 0;
m_max_volumetric_extrusion_rate_slopes[size_t(er)].positive = 0;
}
opened_extrude_set_speed_block = false;
#ifdef PRESSURE_EQUALIZER_STATISTIC
m_stat.reset();
#endif
#ifdef PRESSURE_EQUALIZER_DEBUG
line_idx = 0;
#endif
}
const char* PressureEqualizer::process(const char *szGCode, bool flush)
void PressureEqualizer::process_layer(const std::string &gcode)
{
// Reset length of the output_buffer.
output_buffer_length = 0;
if (szGCode != 0) {
const char *p = szGCode;
while (*p != 0) {
if (!gcode.empty()) {
const char *gcode_begin = gcode.c_str();
while (*gcode_begin != 0) {
// Find end of the line.
const char *endl = p;
const char *gcode_end = gcode_begin;
// Slic3r always generates end of lines in a Unix style.
for (; *endl != 0 && *endl != '\n'; ++ endl) ;
if (circular_buffer_items == circular_buffer_size)
// Buffer is full. Push out the oldest line.
output_gcode_line(circular_buffer[circular_buffer_pos]);
else
++ circular_buffer_items;
// Process a G-code line, store it into the provided GCodeLine object.
size_t idx_tail = circular_buffer_pos;
circular_buffer_pos = circular_buffer_idx_next(circular_buffer_pos);
if (! process_line(p, endl - p, circular_buffer[idx_tail])) {
// The line has to be forgotten. It contains comment marks, which shall be
// filtered out of the target g-code.
circular_buffer_pos = idx_tail;
-- circular_buffer_items;
for (; *gcode_end != 0 && *gcode_end != '\n'; ++gcode_end);
m_gcode_lines.emplace_back();
if (!this->process_line(gcode_begin, gcode_end, m_gcode_lines.back())) {
// The line has to be forgotten. It contains comment marks, which shall be filtered out of the target g-code.
m_gcode_lines.pop_back();
}
p = endl;
if (*p == '\n')
++ p;
gcode_begin = gcode_end;
if (*gcode_begin == '\n')
++gcode_begin;
}
assert(!this->opened_extrude_set_speed_block);
}
// at this point, we have an entire layer of gcode lines loaded into m_gcode_lines
// now we will split the mix of travels and extrudes into segments of continous extrusion and process those
// We skip over large travels, and pretend small ones are part of a continous extrusion segment
long idx_end_current_extrusion = 0;
while (idx_end_current_extrusion < m_gcode_lines.size()) {
// find beginning of next extrusion segment from current pos
const long idx_begin_current_extrusion = find_if(m_gcode_lines.begin() + idx_end_current_extrusion, m_gcode_lines.end(),
[](GCodeLine line) { return line.extruding(); }) - m_gcode_lines.begin();
// (extrusion begin idx = extrusion end idx) here because we start with extrusion length of zero
idx_end_current_extrusion = idx_begin_current_extrusion;
// inner loop extends the extrusion segment over small travel moves
while (idx_end_current_extrusion < m_gcode_lines.size()) {
// find end of the current extrusion segment
const auto just_after_end_extrusion = find_if(m_gcode_lines.begin() + idx_end_current_extrusion, m_gcode_lines.end(),
[](GCodeLine line) { return !line.extruding(); });
idx_end_current_extrusion = std::max<long>(0,(just_after_end_extrusion - m_gcode_lines.begin()) - 1);
const long idx_begin_segment_continuation = advance_segment_beyond_small_gap(idx_end_current_extrusion);
if (idx_begin_segment_continuation > idx_end_current_extrusion) {
// extend the continous line over the small gap
idx_end_current_extrusion = idx_begin_segment_continuation;
continue; // keep going, loop again to find new end of extrusion segment
} else {
// gap to next extrude is too big, stop looking forward. We've found end of this segment
break;
}
}
// now run the pressure equalizer across the segment like a streamroller
// it operates on a sliding window that moves forward across gcode line by line
for (int i = idx_begin_current_extrusion; i < idx_end_current_extrusion; ++i) {
// feed pressure equalizer past lines, going back to max_look_back_limit (or start of segment)
const auto start_idx = std::max<long>(idx_begin_current_extrusion, i - max_look_back_limit);
adjust_volumetric_rate(start_idx, i);
}
// current extrusion is all done processing so advance beyond it for next loop
idx_end_current_extrusion++;
}
}
long PressureEqualizer::advance_segment_beyond_small_gap(const long idx_orig)
{
// this should only be run on the last extruding line before a gap
assert(m_gcode_lines[idx_cur_pos].extruding());
double distance_traveled = 0.0;
// start at beginning of gap, advance till extrusion found or gap too big
for (auto idx_cur_pos = idx_orig + 1; idx_cur_pos < m_gcode_lines.size(); idx_cur_pos++) {
// started extruding again! return segment extension
if (m_gcode_lines[idx_cur_pos].extruding()) {
return idx_cur_pos;
}
distance_traveled += m_gcode_lines[idx_cur_pos].dist_xy();
// gap too big, dont extend segment
if (distance_traveled > max_ignored_gap_between_extruding_segments) {
return idx_orig;
}
}
// looped until end of layer and couldn't extend extrusion
return idx_orig;
}
if (flush) {
// Flush the remaining valid lines of the circular buffer.
for (size_t idx = circular_buffer_idx_head(); circular_buffer_items > 0; -- circular_buffer_items) {
output_gcode_line(circular_buffer[idx]);
if (++ idx == circular_buffer_size)
idx = 0;
}
// Reset the index pointer.
assert(circular_buffer_items == 0);
circular_buffer_pos = 0;
LayerResult PressureEqualizer::process_layer(LayerResult &&input)
{
const bool is_first_layer = m_layer_results.empty();
const size_t next_layer_first_idx = m_gcode_lines.size();
#if 1
printf("Statistics: \n");
printf("Minimum volumetric extrusion rate: %f\n", m_stat.volumetric_extrusion_rate_min);
printf("Maximum volumetric extrusion rate: %f\n", m_stat.volumetric_extrusion_rate_max);
if (m_stat.extrusion_length > 0)
m_stat.volumetric_extrusion_rate_avg /= m_stat.extrusion_length;
printf("Average volumetric extrusion rate: %f\n", m_stat.volumetric_extrusion_rate_avg);
m_stat.reset();
#endif
}
if (!input.nop_layer_result) {
this->process_layer(input.gcode);
input.gcode.clear(); // GCode is already processed, so it isn't needed to store it.
m_layer_results.emplace(new LayerResult(input));
}
return output_buffer.data();
if (is_first_layer) // Buffer previous input result and output NOP.
return LayerResult::make_nop_layer_result();
// Export previous layer.
LayerResult *prev_layer_result = m_layer_results.front();
m_layer_results.pop();
output_buffer_length = 0;
output_buffer_prev_length = 0;
for (size_t line_idx = 0; line_idx < next_layer_first_idx; ++line_idx)
output_gcode_line(line_idx);
m_gcode_lines.erase(m_gcode_lines.begin(), m_gcode_lines.begin() + int(next_layer_first_idx));
if (output_buffer_length > 0)
prev_layer_result->gcode = std::string(output_buffer.data());
assert(!input.nop_layer_result || m_layer_results.empty());
LayerResult out = *prev_layer_result;
delete prev_layer_result;
return out;
}
// Is a white space?
static inline bool is_ws(const char c) { return c == ' ' || c == '\t'; }
// Is it an end of line? Consider a comment to be an end of line as well.
static inline bool is_eol(const char c) { return c == 0 || c == '\r' || c == '\n' || c == ';'; };
static inline bool is_eol(const char c) { return c == 0 || c == '\r' || c == '\n' || c == ';'; }
// Is it a white space or end of line?
static inline bool is_ws_or_eol(const char c) { return is_ws(c) || is_eol(c); };
static inline bool is_ws_or_eol(const char c) { return is_ws(c) || is_eol(c); }
// Eat whitespaces.
static void eatws(const char *&line)
@ -146,35 +225,45 @@ static void eatws(const char *&line)
// If succeeded, the line pointer is advanced.
static inline int parse_int(const char *&line)
{
char *endptr = NULL;
char *endptr = nullptr;
long result = strtol(line, &endptr, 10);
if (endptr == NULL || !is_ws_or_eol(*endptr))
throw Slic3r::RuntimeError("PressureEqualizer: Error parsing an int");
if (endptr == nullptr || !is_ws_or_eol(*endptr))
throw Slic3r::InvalidArgument("PressureEqualizer: Error parsing an int");
line = endptr;
return int(result);
};
}
float string_to_float_decimal_point(const char *line, const size_t str_len, size_t* pos)
{
float out;
size_t p = fast_float::from_chars(line, line + str_len, out).ptr - line;
if (pos)
*pos = p;
return out;
}
// Parse an int starting at the current position of a line.
// If succeeded, the line pointer is advanced.
static inline float parse_float(const char *&line)
static inline float parse_float(const char *&line, const size_t line_length)
{
char *endptr = NULL;
float result = string_to_double_decimal_point(line, &endptr);
if (endptr == NULL || !is_ws_or_eol(*endptr))
size_t endptr = 0;
auto result = string_to_float_decimal_point(line, line_length, &endptr);
if (endptr == 0 || !is_ws_or_eol(*(line + endptr)))
throw Slic3r::RuntimeError("PressureEqualizer: Error parsing a float");
line = endptr;
line = line + endptr;
return result;
};
}
bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLine &buf)
bool PressureEqualizer::process_line(const char *line, const char *line_end, GCodeLine &buf)
{
static constexpr const char *EXTRUSION_ROLE_TAG = ";_EXTRUSION_ROLE:";
if (strncmp(line, EXTRUSION_ROLE_TAG, strlen(EXTRUSION_ROLE_TAG)) == 0) {
line += strlen(EXTRUSION_ROLE_TAG);
const size_t len = line_end - line;
if (strncmp(line, EXTRUSION_ROLE_TAG.data(), EXTRUSION_ROLE_TAG.length()) == 0) {
line += EXTRUSION_ROLE_TAG.length();
int role = atoi(line);
m_current_extrusion_role = ExtrusionRole(role);
++ line_idx;
m_current_extrusion_role = GCodeExtrusionRole(role);
#ifdef PRESSURE_EQUALIZER_DEBUG
++line_idx;
#endif
return false;
}
@ -199,21 +288,43 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
buf.max_volumetric_extrusion_rate_slope_negative = 0.f;
buf.extrusion_role = m_current_extrusion_role;
std::string str_line(line, line_end);
const bool found_extrude_set_speed_tag = boost::contains(str_line, EXTRUDE_SET_SPEED_TAG);
const bool found_extrude_end_tag = boost::contains(str_line, EXTRUDE_END_TAG);
assert(!found_extrude_set_speed_tag || !found_extrude_end_tag);
if (found_extrude_set_speed_tag)
this->opened_extrude_set_speed_block = true;
else if (found_extrude_end_tag)
this->opened_extrude_set_speed_block = false;
// Parse the G-code line, store the result into the buf.
switch (toupper(*line ++)) {
case 'G': {
int gcode = parse_int(line);
int gcode = -1;
try {
gcode = parse_int(line);
} catch (Slic3r::InvalidArgument &) {
// Ignore invalid GCodes.
eatws(line);
break;
}
assert(gcode != -1);
eatws(line);
switch (gcode) {
case 0:
case 1:
{
// G0, G1: A FFF 3D printer does not make a difference between the two.
buf.adjustable_flow = this->opened_extrude_set_speed_block;
buf.extrude_set_speed_tag = found_extrude_set_speed_tag;
buf.extrude_end_tag = found_extrude_end_tag;
float new_pos[5];
memcpy(new_pos, m_current_pos, sizeof(float)*5);
bool changed[5] = { false, false, false, false, false };
while (!is_eol(*line)) {
char axis = toupper(*line++);
const char axis = toupper(*line++);
int i = -1;
switch (axis) {
case 'X':
@ -228,16 +339,16 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
i = 4;
break;
default:
assert(false);
break;
}
if (i != -1) {
buf.pos_provided[i] = true;
new_pos[i] = parse_float(line, line_end - line);
if (i == 3 && m_use_relative_e_distances)
new_pos[i] += m_current_pos[i];
changed[i] = new_pos[i] != m_current_pos[i];
eatws(line);
}
if (i == -1)
throw Slic3r::RuntimeError(std::string("GCode::PressureEqualizer: Invalid axis for G0/G1: ") + axis);
buf.pos_provided[i] = true;
new_pos[i] = parse_float(line);
if (i == 3 && m_config->use_relative_e_distances.value)
new_pos[i] += m_current_pos[i];
changed[i] = new_pos[i] != m_current_pos[i];
eatws(line);
}
if (changed[3]) {
// Extrusion, retract or unretract.
@ -263,15 +374,17 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
buf.volumetric_extrusion_rate = rate;
buf.volumetric_extrusion_rate_start = rate;
buf.volumetric_extrusion_rate_end = rate;
#ifdef PRESSURE_EQUALIZER_STATISTIC
m_stat.update(rate, sqrt(len2));
#endif
#ifdef PRESSURE_EQUALIZER_DEBUG
if (rate < 40.f) {
printf("Extremely low flow rate: %f. Line %d, Length: %f, extrusion: %f Old position: (%f, %f, %f), new position: (%f, %f, %f)\n",
rate,
int(line_idx),
sqrt(len2), sqrt((diff[3]*diff[3])/len2),
m_current_pos[0], m_current_pos[1], m_current_pos[2],
new_pos[0], new_pos[1], new_pos[2]);
printf("Extremely low flow rate: %f. Line %d, Length: %f, extrusion: %f Old position: (%f, %f, %f), new position: (%f, %f, %f)\n",
rate, int(line_idx), sqrt(len2), sqrt((diff[3] * diff[3]) / len2), m_current_pos[0], m_current_pos[1], m_current_pos[2],
new_pos[0], new_pos[1], new_pos[2]);
}
#endif
}
} else if (changed[0] || changed[1] || changed[2]) {
// Moving without extrusion.
@ -285,26 +398,22 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
// G92 : Set Position
// Set a logical coordinate position to a new value without actually moving the machine motors.
// Which axes to set?
bool set = false;
while (!is_eol(*line)) {
char axis = toupper(*line++);
const char axis = toupper(*line++);
switch (axis) {
case 'X':
case 'Y':
case 'Z':
m_current_pos[axis - 'X'] = (!is_ws_or_eol(*line)) ? parse_float(line) : 0.f;
set = true;
m_current_pos[axis - 'X'] = (!is_ws_or_eol(*line)) ? parse_float(line, line_end - line) : 0.f;
break;
case 'E':
m_current_pos[3] = (!is_ws_or_eol(*line)) ? parse_float(line) : 0.f;
set = true;
m_current_pos[3] = (!is_ws_or_eol(*line)) ? parse_float(line, line_end - line) : 0.f;
break;
default:
throw Slic3r::RuntimeError(std::string("GCode::PressureEqualizer: Incorrect axis in a G92 G-code: ") + axis);
break;
}
eatws(line);
}
assert(set);
break;
}
case 10:
@ -326,20 +435,24 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
break;
}
case 'M': {
int mcode = parse_int(line);
eatws(line);
switch (mcode) {
default:
// Ignore the rest of the M-codes.
break;
}
// Ignore the rest of the M-codes.
break;
}
case 'T':
{
// Activate an extruder head.
int new_extruder = parse_int(line);
if (new_extruder != m_current_extruder) {
int new_extruder = -1;
try {
new_extruder = parse_int(line);
} catch (Slic3r::InvalidArgument &) {
// Ignore invalid GCodes starting with T.
eatws(line);
break;
}
assert(new_extruder != -1);
if (new_extruder != int(m_current_extruder)) {
m_current_extruder = new_extruder;
m_retracted = true;
buf.type = GCODELINETYPE_TOOL_CHANGE;
@ -352,15 +465,16 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
buf.extruder_id = m_current_extruder;
memcpy(buf.pos_end, m_current_pos, sizeof(float)*5);
adjust_volumetric_rate();
++ line_idx;
return true;
#ifdef PRESSURE_EQUALIZER_DEBUG
++line_idx;
#endif
return true;
}
void PressureEqualizer::output_gcode_line(GCodeLine &line)
void PressureEqualizer::output_gcode_line(const size_t line_idx)
{
if (! line.modified) {
GCodeLine &line = m_gcode_lines[line_idx];
if (!line.modified) {
push_to_output(line.raw.data(), line.raw_length, true);
return;
}
@ -370,15 +484,12 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
const char *comment = line.raw.data();
while (*comment != ';' && *comment != 0) ++comment;
if (*comment != ';')
comment = NULL;
comment = nullptr;
// Emit the line with lowered extrusion rates.
float l2 = line.dist_xyz2();
float l = sqrt(l2);
size_t nSegments = size_t(ceil(l / m_max_segment_length));
if (nSegments == 1) {
// Just update this segment.
push_line_to_output(line, line.feedrate() * line.volumetric_correction_avg(), comment);
float l = line.dist_xyz();
if (auto nSegments = size_t(ceil(l / m_max_segment_length)); nSegments == 1) { // Just update this segment.
push_line_to_output(line_idx, line.feedrate() * line.volumetric_correction_avg(), comment);
} else {
bool accelerating = line.volumetric_extrusion_rate_start < line.volumetric_extrusion_rate_end;
// Update the initial and final feed rate values.
@ -386,8 +497,8 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
line.pos_end [4] = line.volumetric_extrusion_rate_end * line.pos_end[4] / line.volumetric_extrusion_rate;
float feed_avg = 0.5f * (line.pos_start[4] + line.pos_end[4]);
// Limiting volumetric extrusion rate slope for this segment.
float max_volumetric_extrusion_rate_slope = accelerating ?
line.max_volumetric_extrusion_rate_slope_positive : line.max_volumetric_extrusion_rate_slope_negative;
float max_volumetric_extrusion_rate_slope = accelerating ? line.max_volumetric_extrusion_rate_slope_positive :
line.max_volumetric_extrusion_rate_slope_negative;
// Total time for the segment, corrected for the possibly lowered volumetric feed rate,
// if accelerating / decelerating over the complete segment.
float t_total = line.dist_xyz() / feed_avg;
@ -398,8 +509,8 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
float l_steady = 0.f;
if (t_acc < t_total) {
// One may achieve higher print speeds if part of the segment is not speed limited.
float l_acc = t_acc * feed_avg;
float l_steady = l - l_acc;
l_acc = t_acc * feed_avg;
l_steady = l - l_acc;
if (l_steady < 0.5f * m_max_segment_length) {
l_acc = l;
l_steady = 0.f;
@ -407,10 +518,10 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
nSegments = size_t(ceil(l_acc / m_max_segment_length));
}
float pos_start[5];
float pos_end [5];
float pos_end2 [4];
memcpy(pos_start, line.pos_start, sizeof(float)*5);
memcpy(pos_end , line.pos_end , sizeof(float)*5);
float pos_end[5];
float pos_end2[4];
memcpy(pos_start, line.pos_start, sizeof(float) * 5);
memcpy(pos_end, line.pos_end, sizeof(float) * 5);
if (l_steady > 0.f) {
// There will be a steady feed segment emitted.
if (accelerating) {
@ -428,10 +539,16 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
line.pos_end[i] = pos_start[i] + (pos_end[i] - pos_start[i]) * t;
line.pos_provided[i] = true;
}
push_line_to_output(line, pos_start[4], comment);
comment = NULL;
push_line_to_output(line_idx, pos_start[4], comment);
comment = nullptr;
float new_pos_start_feedrate = pos_start[4];
memcpy(line.pos_start, line.pos_end, sizeof(float)*5);
memcpy(pos_start, line.pos_end, sizeof(float)*5);
line.pos_start[4] = new_pos_start_feedrate;
pos_start[4] = new_pos_start_feedrate;
}
}
// Split the segment into pieces.
@ -442,8 +559,8 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
line.pos_provided[j] = true;
}
// Interpolate the feed rate at the center of the segment.
push_line_to_output(line, pos_start[4] + (pos_end[4] - pos_start[4]) * (float(i) - 0.5f) / float(nSegments), comment);
comment = NULL;
push_line_to_output(line_idx, pos_start[4] + (pos_end[4] - pos_start[4]) * (float(i) - 0.5f) / float(nSegments), comment);
comment = nullptr;
memcpy(line.pos_start, line.pos_end, sizeof(float)*5);
}
if (l_steady > 0.f && accelerating) {
@ -451,136 +568,161 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
line.pos_end[i] = pos_end2[i];
line.pos_provided[i] = true;
}
push_line_to_output(line, pos_end[4], comment);
push_line_to_output(line_idx, pos_end[4], comment);
} else {
for (int i = 0; i < 4; ++ i) {
line.pos_end[i] = pos_end[i];
line.pos_provided[i] = true;
}
push_line_to_output(line_idx, pos_end[4], comment);
}
}
}
void PressureEqualizer::adjust_volumetric_rate()
void PressureEqualizer::adjust_volumetric_rate(const size_t fist_line_idx, const size_t last_line_idx)
{
if (circular_buffer_items < 2)
// don't bother adjusting volumetric rate if there's no gcode to adjust
if (last_line_idx-fist_line_idx < 2)
return;
// Go back from the current circular_buffer_pos and lower the feedtrate to decrease the slope of the extrusion rate changes.
const size_t idx_head = circular_buffer_idx_head();
const size_t idx_tail = circular_buffer_idx_prev(circular_buffer_idx_tail());
size_t idx = idx_tail;
if (idx == idx_head || ! circular_buffer[idx].extruding())
size_t line_idx = last_line_idx;
if (line_idx == fist_line_idx || !m_gcode_lines[line_idx].extruding())
// Nothing to do, the last move is not extruding.
return;
float feedrate_per_extrusion_role[numExtrusionRoles];
for (size_t i = 0; i < numExtrusionRoles; ++ i)
feedrate_per_extrusion_role[i] = FLT_MAX;
feedrate_per_extrusion_role[circular_buffer[idx].extrusion_role] = circular_buffer[idx].volumetric_extrusion_rate_start;
std::array<float, size_t(GCodeExtrusionRole::Count)> feedrate_per_extrusion_role{};
feedrate_per_extrusion_role.fill(std::numeric_limits<float>::max());
feedrate_per_extrusion_role[int(m_gcode_lines[line_idx].extrusion_role)] = m_gcode_lines[line_idx].volumetric_extrusion_rate_start;
bool modified = true;
while (modified && idx != idx_head) {
size_t idx_prev = circular_buffer_idx_prev(idx);
for (; ! circular_buffer[idx_prev].extruding() && idx_prev != idx_head; idx_prev = circular_buffer_idx_prev(idx_prev)) ;
if (! circular_buffer[idx_prev].extruding())
break;
while (line_idx != fist_line_idx) {
size_t idx_prev = line_idx - 1;
for (; !m_gcode_lines[idx_prev].extruding() && idx_prev != fist_line_idx; --idx_prev);
if (!m_gcode_lines[idx_prev].extruding())
break;
// Don't decelerate before ironing.
if (m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::Ironing) { line_idx = idx_prev;
continue;
}
// Volumetric extrusion rate at the start of the succeding segment.
float rate_succ = circular_buffer[idx].volumetric_extrusion_rate_start;
float rate_succ = m_gcode_lines[line_idx].volumetric_extrusion_rate_start;
// What is the gradient of the extrusion rate between idx_prev and idx?
idx = idx_prev;
GCodeLine &line = circular_buffer[idx];
for (size_t iRole = 1; iRole < numExtrusionRoles; ++ iRole) {
float rate_slope = m_max_volumetric_extrusion_rate_slopes[iRole].negative;
if (rate_slope == 0)
// The negative rate is unlimited.
continue;
line_idx = idx_prev;
GCodeLine &line = m_gcode_lines[line_idx];
for (size_t iRole = 1; iRole < size_t(GCodeExtrusionRole::Count); ++ iRole) {
const float &rate_slope = m_max_volumetric_extrusion_rate_slopes[iRole].negative;
if (rate_slope == 0 || feedrate_per_extrusion_role[iRole] == std::numeric_limits<float>::max())
continue; // The negative rate is unlimited or the rate for GCodeExtrusionRole iRole is unlimited.
float rate_end = feedrate_per_extrusion_role[iRole];
if (iRole == line.extrusion_role && rate_succ < rate_end)
if (iRole == size_t(line.extrusion_role) && rate_succ < rate_end)
// Limit by the succeeding volumetric flow rate.
rate_end = rate_succ;
if (line.volumetric_extrusion_rate_end > rate_end) {
line.volumetric_extrusion_rate_end = rate_end;
line.modified = true;
} else if (iRole == line.extrusion_role) {
// don't alter the flow rate for these extrusion types
if (!line.adjustable_flow || line.extrusion_role == GCodeExtrusionRole::BridgeInfill || line.extrusion_role == GCodeExtrusionRole::Ironing) {
rate_end = line.volumetric_extrusion_rate_end;
} else if (line.volumetric_extrusion_rate_end > rate_end) {
line.volumetric_extrusion_rate_end = rate_end;
line.max_volumetric_extrusion_rate_slope_negative = rate_slope;
line.modified = true;
} else if (iRole == size_t(line.extrusion_role)) {
rate_end = line.volumetric_extrusion_rate_end;
} else if (rate_end == FLT_MAX) {
// The rate for ExtrusionRole iRole is unlimited.
continue;
} else {
// Use the original, 'floating' extrusion rate as a starting point for the limiter.
}
// modified = false;
float rate_start = rate_end + rate_slope * line.time_corrected();
if (rate_start < line.volumetric_extrusion_rate_start) {
// Limit the volumetric extrusion rate at the start of this segment due to a segment
// of ExtrusionType iRole, which will be extruded in the future.
line.volumetric_extrusion_rate_start = rate_start;
line.max_volumetric_extrusion_rate_slope_negative = rate_slope;
line.modified = true;
// modified = true;
if (line.adjustable_flow) {
float rate_start = rate_end + rate_slope * line.time_corrected();
if (rate_start < line.volumetric_extrusion_rate_start) {
// Limit the volumetric extrusion rate at the start of this segment due to a segment
// of ExtrusionType iRole, which will be extruded in the future.
line.volumetric_extrusion_rate_start = rate_start;
line.max_volumetric_extrusion_rate_slope_negative = rate_slope;
line.modified = true;
}
}
feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_start : rate_start;
// feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_start : rate_start;
// Don't store feed rate for ironing
if (line.extrusion_role != GCodeExtrusionRole::Ironing)
feedrate_per_extrusion_role[iRole] = line.volumetric_extrusion_rate_start;
}
}
// Go forward and adjust the feedrate to decrease the slope of the extrusion rate changes.
for (size_t i = 0; i < numExtrusionRoles; ++ i)
feedrate_per_extrusion_role[i] = FLT_MAX;
feedrate_per_extrusion_role[circular_buffer[idx].extrusion_role] = circular_buffer[idx].volumetric_extrusion_rate_end;
feedrate_per_extrusion_role.fill(std::numeric_limits<float>::max());
feedrate_per_extrusion_role[size_t(m_gcode_lines[line_idx].extrusion_role)] = m_gcode_lines[line_idx].volumetric_extrusion_rate_end;
assert(circular_buffer[idx].extruding());
while (idx != idx_tail) {
size_t idx_next = circular_buffer_idx_next(idx);
for (; ! circular_buffer[idx_next].extruding() && idx_next != idx_tail; idx_next = circular_buffer_idx_next(idx_next)) ;
if (! circular_buffer[idx_next].extruding())
break;
float rate_prec = circular_buffer[idx].volumetric_extrusion_rate_end;
assert(m_gcode_lines[line_idx].extruding());
while (line_idx != last_line_idx) {
size_t idx_next = line_idx + 1;
for (; !m_gcode_lines[idx_next].extruding() && idx_next != last_line_idx; ++idx_next);
if (!m_gcode_lines[idx_next].extruding())
break;
// Don't accelerate after ironing.
if (m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::Ironing) {
line_idx = idx_next;
continue;
}
float rate_prec = m_gcode_lines[line_idx].volumetric_extrusion_rate_end;
// What is the gradient of the extrusion rate between idx_prev and idx?
idx = idx_next;
GCodeLine &line = circular_buffer[idx];
for (size_t iRole = 1; iRole < numExtrusionRoles; ++ iRole) {
float rate_slope = m_max_volumetric_extrusion_rate_slopes[iRole].positive;
if (rate_slope == 0)
// The positive rate is unlimited.
continue;
line_idx = idx_next;
GCodeLine &line = m_gcode_lines[line_idx];
for (size_t iRole = 1; iRole < size_t(GCodeExtrusionRole::Count); ++ iRole) {
const float &rate_slope = m_max_volumetric_extrusion_rate_slopes[iRole].positive;
if (rate_slope == 0 || feedrate_per_extrusion_role[iRole] == std::numeric_limits<float>::max())
continue; // The positive rate is unlimited or the rate for GCodeExtrusionRole iRole is unlimited.
float rate_start = feedrate_per_extrusion_role[iRole];
if (iRole == line.extrusion_role && rate_prec < rate_start)
// don't alter the flow rate for these extrusion types
if (!line.adjustable_flow || line.extrusion_role == GCodeExtrusionRole::BridgeInfill || line.extrusion_role == GCodeExtrusionRole::Ironing) {
rate_start = line.volumetric_extrusion_rate_start;
} else if (iRole == size_t(line.extrusion_role) && rate_prec < rate_start)
rate_start = rate_prec;
if (line.volumetric_extrusion_rate_start > rate_start) {
line.volumetric_extrusion_rate_start = rate_start;
line.max_volumetric_extrusion_rate_slope_positive = rate_slope;
line.modified = true;
} else if (iRole == line.extrusion_role) {
} else if (iRole == size_t(line.extrusion_role)) {
rate_start = line.volumetric_extrusion_rate_start;
} else if (rate_start == FLT_MAX) {
// The rate for ExtrusionRole iRole is unlimited.
continue;
} else {
// Use the original, 'floating' extrusion rate as a starting point for the limiter.
}
float rate_end = (rate_slope == 0) ? FLT_MAX : rate_start + rate_slope * line.time_corrected();
if (rate_end < line.volumetric_extrusion_rate_end) {
// Limit the volumetric extrusion rate at the start of this segment due to a segment
// of ExtrusionType iRole, which was extruded before.
line.volumetric_extrusion_rate_end = rate_end;
line.max_volumetric_extrusion_rate_slope_positive = rate_slope;
line.modified = true;
if (line.adjustable_flow) {
float rate_end = rate_start + rate_slope * line.time_corrected();
if (rate_end < line.volumetric_extrusion_rate_end) {
// Limit the volumetric extrusion rate at the start of this segment due to a segment
// of ExtrusionType iRole, which was extruded before.
line.volumetric_extrusion_rate_end = rate_end;
line.max_volumetric_extrusion_rate_slope_positive = rate_slope;
line.modified = true;
}
}
feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_end : rate_end;
// feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_end : rate_end;
// Don't store feed rate for ironing
if (line.extrusion_role != GCodeExtrusionRole::Ironing)
feedrate_per_extrusion_role[iRole] = line.volumetric_extrusion_rate_end;
}
}
}
void PressureEqualizer::push_axis_to_output(const char axis, const float value, bool add_eol)
inline void PressureEqualizer::push_to_output(GCodeG1Formatter &formatter)
{
char buf[2048];
int len = sprintf(buf,
(axis == 'E') ? " %c%.3f" : " %c%.5f",
axis, value);
push_to_output(buf, len, add_eol);
return this->push_to_output(formatter.string(), false);
}
void PressureEqualizer::push_to_output(const char *text, const size_t len, bool add_eol)
inline void PressureEqualizer::push_to_output(const std::string &text, bool add_eol)
{
return this->push_to_output(text.data(), text.size(), add_eol);
}
inline void PressureEqualizer::push_to_output(const char *text, const size_t len, bool add_eol)
{
// New length of the output buffer content.
size_t len_new = output_buffer_length + len + 1;
if (add_eol)
++ len_new;
++len_new;
// Resize the output buffer to a power of 2 higher than the required memory.
if (output_buffer.size() < len_new) {
@ -600,24 +742,63 @@ void PressureEqualizer::push_to_output(const char *text, const size_t len, bool
// Copy the text to the output.
if (len != 0) {
memcpy(output_buffer.data() + output_buffer_length, text, len);
this->output_buffer_prev_length = this->output_buffer_length;
output_buffer_length += len;
}
if (add_eol)
output_buffer[output_buffer_length ++] = '\n';
output_buffer[output_buffer_length++] = '\n';
output_buffer[output_buffer_length] = 0;
}
void PressureEqualizer::push_line_to_output(const GCodeLine &line, const float new_feedrate, const char *comment)
inline bool is_just_line_with_extrude_set_speed_tag(const std::string &line)
{
push_to_output("G1", 2, false);
for (char i = 0; i < 3; ++ i)
if (line.pos_provided[i])
push_axis_to_output('X'+i, line.pos_end[i]);
push_axis_to_output('E', m_config->use_relative_e_distances.value ? (line.pos_end[3] - line.pos_start[3]) : line.pos_end[3]);
// if (line.pos_provided[4] || fabs(line.feedrate() - new_feedrate) > 1e-5)
push_axis_to_output('F', new_feedrate);
// output comment and EOL
push_to_output(comment, (comment == NULL) ? 0 : strlen(comment), true);
}
if (line.empty() && !boost::starts_with(line, "G1 ") && !boost::ends_with(line, EXTRUDE_SET_SPEED_TAG))
return false;
const char *p_line = line.data() + 3;
const char *const line_end = line.data() + line.length() - 1;
while (!is_eol(*p_line)) {
if (toupper(*p_line++) == 'F')
break;
else
return false;
}
parse_float(p_line, line_end - p_line);
eatws(p_line);
p_line += EXTRUDE_SET_SPEED_TAG.length();
return p_line <= line_end && is_eol(*p_line);
}
void PressureEqualizer::push_line_to_output(const size_t line_idx, const float new_feedrate, const char *comment)
{
const GCodeLine &line = m_gcode_lines[line_idx];
if (line_idx > 0 && output_buffer_length > 0) {
const std::string prev_line_str = std::string(output_buffer.begin() + int(this->output_buffer_prev_length),
output_buffer.begin() + int(this->output_buffer_length) + 1);
if (is_just_line_with_extrude_set_speed_tag(prev_line_str))
this->output_buffer_length = this->output_buffer_prev_length; // Remove the last line because it only sets the speed for an empty block of g-code lines, so it is useless.
else
push_to_output(EXTRUDE_END_TAG.data(), EXTRUDE_END_TAG.length(), true);
} else
push_to_output(EXTRUDE_END_TAG.data(), EXTRUDE_END_TAG.length(), true);
GCodeG1Formatter feedrate_formatter;
feedrate_formatter.emit_f(new_feedrate);
feedrate_formatter.emit_string(std::string(EXTRUDE_SET_SPEED_TAG.data(), EXTRUDE_SET_SPEED_TAG.length()));
if (line.extrusion_role == GCodeExtrusionRole::ExternalPerimeter)
feedrate_formatter.emit_string(std::string(EXTERNAL_PERIMETER_TAG.data(), EXTERNAL_PERIMETER_TAG.length()));
push_to_output(feedrate_formatter);
GCodeG1Formatter extrusion_formatter;
for (size_t axis_idx = 0; axis_idx < 3; ++axis_idx)
if (line.pos_provided[axis_idx])
extrusion_formatter.emit_axis(char('X' + axis_idx), line.pos_end[axis_idx], GCodeFormatter::XYZF_EXPORT_DIGITS);
extrusion_formatter.emit_axis('E', m_use_relative_e_distances ? (line.pos_end[3] - line.pos_start[3]) : line.pos_end[3], GCodeFormatter::E_EXPORT_DIGITS);
if (comment != nullptr)
extrusion_formatter.emit_string(std::string(comment));
push_to_output(extrusion_formatter);
}
} // namespace Slic3r

View file

@ -1,41 +1,59 @@
///|/ Copyright (c) Prusa Research 2016 - 2023 Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas
///|/ Copyright (c) SuperSlicer 2023 Remi Durand @supermerill
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GCode_PressureEqualizer_hpp_
#define slic3r_GCode_PressureEqualizer_hpp_
#include "../libslic3r.h"
#include "../PrintConfig.hpp"
#include "../ExtrusionEntity.hpp"
#include "../ExtrusionRole.hpp"
#include <queue>
namespace Slic3r {
struct LayerResult;
class GCodeG1Formatter;
//#define PRESSURE_EQUALIZER_STATISTIC
//#define PRESSURE_EQUALIZER_DEBUG
// Processes a G-code. Finds changes in the volumetric extrusion speed and adjusts the transitions
// between these paths to limit fast changes in the volumetric extrusion speed.
class PressureEqualizer
{
public:
PressureEqualizer(const Slic3r::GCodeConfig *config);
~PressureEqualizer();
void reset();
// Process a next batch of G-code lines. Flush the internal buffers if asked for.
const char* process(const char *szGCode, bool flush);
size_t get_output_buffer_length() const { return output_buffer_length; }
PressureEqualizer() = delete;
explicit PressureEqualizer(const Slic3r::GCodeConfig &config);
~PressureEqualizer() = default;
// Process a next batch of G-code lines.
// The last LayerResult must be LayerResult::make_nop_layer_result() because it always returns GCode for the previous layer.
// When process_layer is called for the first layer, then LayerResult::make_nop_layer_result() is returned.
LayerResult process_layer(LayerResult &&input);
private:
void process_layer(const std::string &gcode);
#ifdef PRESSURE_EQUALIZER_STATISTIC
struct Statistics
{
void reset() {
volumetric_extrusion_rate_min = std::numeric_limits<float>::max();
void reset()
{
volumetric_extrusion_rate_min = std::numeric_limits<float>::max();
volumetric_extrusion_rate_max = 0.f;
volumetric_extrusion_rate_avg = 0.f;
extrusion_length = 0.f;
extrusion_length = 0.f;
}
void update(float volumetric_extrusion_rate, float length) {
volumetric_extrusion_rate_min = std::min(volumetric_extrusion_rate_min, volumetric_extrusion_rate);
volumetric_extrusion_rate_max = std::max(volumetric_extrusion_rate_max, volumetric_extrusion_rate);
void update(float volumetric_extrusion_rate, float length)
{
volumetric_extrusion_rate_min = std::min(volumetric_extrusion_rate_min, volumetric_extrusion_rate);
volumetric_extrusion_rate_max = std::max(volumetric_extrusion_rate_max, volumetric_extrusion_rate);
volumetric_extrusion_rate_avg += volumetric_extrusion_rate * length;
extrusion_length += length;
extrusion_length += length;
}
float volumetric_extrusion_rate_min;
float volumetric_extrusion_rate_max;
@ -44,9 +62,7 @@ private:
};
struct Statistics m_stat;
// Keeps the reference, does not own the config.
const Slic3r::GCodeConfig *m_config;
#endif
// Private configuration values
// How fast could the volumetric extrusion rate increase / decrase? mm^3/sec^2
@ -54,12 +70,9 @@ private:
float positive;
float negative;
};
enum { numExtrusionRoles = erSupportMaterialInterface + 1 };
ExtrusionRateSlope m_max_volumetric_extrusion_rate_slopes[numExtrusionRoles];
ExtrusionRateSlope m_max_volumetric_extrusion_rate_slopes[size_t(GCodeExtrusionRole::Count)];
float m_max_volumetric_extrusion_rate_slope_positive;
float m_max_volumetric_extrusion_rate_slope_negative;
// Maximum segment length to split a long segment, if the initial and the final flow rate differ.
float m_max_segment_length;
// Configuration extracted from config.
// Area of the crossestion of each filament. Necessary to calculate the volumetric flow rate.
@ -69,11 +82,19 @@ private:
// X,Y,Z,E,F
float m_current_pos[5];
size_t m_current_extruder;
ExtrusionRole m_current_extrusion_role;
GCodeExtrusionRole m_current_extrusion_role;
bool m_retracted;
bool m_use_relative_e_distances;
enum GCodeLineType
{
// Maximum segment length to split a long segment if the initial and the final flow rate differ.
// Smaller value means a smoother transition between two different flow rates.
float m_max_segment_length;
// Indicate if extrude set speed block was opened using the tag ";_EXTRUDE_SET_SPEED"
// or not (not opened, or it was closed using the tag ";_EXTRUDE_END").
bool opened_extrude_set_speed_block = false;
enum GCodeLineType {
GCODELINETYPE_INVALID,
GCODELINETYPE_NOOP,
GCODELINETYPE_OTHER,
@ -128,18 +149,16 @@ private:
// or maybe the line needs to be split into multiple lines.
bool modified;
// float timeStart;
// float timeEnd;
// X,Y,Z,E,F. Storing the state of the currently active extruder only.
float pos_start[5];
float pos_end[5];
// Was the axis found on the G-code line? X,Y,Z,F
// Was the axis found on the G-code line? X,Y,Z,E,F
bool pos_provided[5];
// Index of the active extruder.
size_t extruder_id;
// Extrusion role of this segment.
ExtrusionRole extrusion_role;
GCodeExtrusionRole extrusion_role;
// Current volumetric extrusion rate.
float volumetric_extrusion_rate;
@ -152,59 +171,42 @@ private:
// If set to zero, the slope is unlimited.
float max_volumetric_extrusion_rate_slope_positive;
float max_volumetric_extrusion_rate_slope_negative;
};
// Circular buffer of GCode lines. The circular buffer size will be limited to circular_buffer_size.
std::vector<GCodeLine> circular_buffer;
// Current position of the circular buffer (index, where to write the next line to, the line has to be pushed out before it is overwritten).
size_t circular_buffer_pos;
// Circular buffer size, configuration value.
size_t circular_buffer_size;
// Number of valid lines in the circular buffer. Lower or equal to circular_buffer_size.
size_t circular_buffer_items;
bool adjustable_flow = false;
bool extrude_set_speed_tag = false;
bool extrude_end_tag = false;
};
// Output buffer will only grow. It will not be reallocated over and over.
std::vector<char> output_buffer;
size_t output_buffer_length;
size_t output_buffer_prev_length;
#ifdef PRESSURE_EQUALIZER_DEBUG
// For debugging purposes. Index of the G-code line processed.
size_t line_idx;
#endif
bool process_line(const char *line, const size_t len, GCodeLine &buf);
void output_gcode_line(GCodeLine &buf);
bool process_line(const char *line, const char *line_end, GCodeLine &buf);
long advance_segment_beyond_small_gap(long idx_cur_pos);
void output_gcode_line(size_t line_idx);
// Go back from the current circular_buffer_pos and lower the feedtrate to decrease the slope of the extrusion rate changes.
// Then go forward and adjust the feedrate to decrease the slope of the extrusion rate changes.
void adjust_volumetric_rate();
void adjust_volumetric_rate(size_t first_line_idx, size_t last_line_idx);
// Push the text to the end of the output_buffer.
void push_to_output(const char *text, const size_t len, bool add_eol = true);
// Push an axis assignment to the end of the output buffer.
void push_axis_to_output(const char axis, const float value, bool add_eol = false);
// Push a G-code line to the output,
void push_line_to_output(const GCodeLine &line, const float new_feedrate, const char *comment);
inline void push_to_output(GCodeG1Formatter &formatter);
inline void push_to_output(const std::string &text, bool add_eol);
inline void push_to_output(const char *text, size_t len, bool add_eol = true);
// Push a G-code line to the output.
void push_line_to_output(size_t line_idx, float new_feedrate, const char *comment);
size_t circular_buffer_idx_head() const {
size_t idx = circular_buffer_pos + circular_buffer_size - circular_buffer_items;
if (idx >= circular_buffer_size)
idx -= circular_buffer_size;
return idx;
}
public:
std::queue<LayerResult*> m_layer_results;
size_t circular_buffer_idx_tail() const { return circular_buffer_pos; }
size_t circular_buffer_idx_prev(size_t idx) const {
idx += circular_buffer_size - 1;
if (idx >= circular_buffer_size)
idx -= circular_buffer_size;
return idx;
}
size_t circular_buffer_idx_next(size_t idx) const {
if (++ idx >= circular_buffer_size)
idx -= circular_buffer_size;
return idx;
}
std::vector<GCodeLine> m_gcode_lines;
};
} // namespace Slic3r