Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/libslic3r/GCode/PressureEqualizer.cpp')
-rw-r--r--src/libslic3r/GCode/PressureEqualizer.cpp621
1 files changed, 621 insertions, 0 deletions
diff --git a/src/libslic3r/GCode/PressureEqualizer.cpp b/src/libslic3r/GCode/PressureEqualizer.cpp
new file mode 100644
index 000000000..3b2a58a88
--- /dev/null
+++ b/src/libslic3r/GCode/PressureEqualizer.cpp
@@ -0,0 +1,621 @@
+#include <memory.h>
+#include <string.h>
+#include <float.h>
+
+#include "../libslic3r.h"
+#include "../PrintConfig.hpp"
+
+#include "PressureEqualizer.hpp"
+
+namespace Slic3r {
+
+PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig *config) :
+ m_config(config)
+{
+ reset();
+}
+
+PressureEqualizer::~PressureEqualizer()
+{
+}
+
+void PressureEqualizer::reset()
+{
+ circular_buffer_pos = 0;
+ circular_buffer_size = 100;
+ circular_buffer_items = 0;
+ circular_buffer.assign(circular_buffer_size, GCodeLine());
+
+ // Preallocate some data, so that output_buffer.data() will return an empty string.
+ output_buffer.assign(32, 0);
+ output_buffer_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;
+ // Expect the first command to fill the nozzle (deretract).
+ m_retracted = true;
+
+ // 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;
+ 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;
+ }
+
+ // 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;
+
+ m_stat.reset();
+ line_idx = 0;
+}
+
+const char* PressureEqualizer::process(const char *szGCode, bool flush)
+{
+ // Reset length of the output_buffer.
+ output_buffer_length = 0;
+
+ if (szGCode != 0) {
+ const char *p = szGCode;
+ while (*p != 0) {
+ // Find end of the line.
+ const char *endl = p;
+ // 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;
+ }
+ p = endl;
+ if (*p == '\n')
+ ++ p;
+ }
+ }
+
+ 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;
+
+#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
+ }
+
+ return output_buffer.data();
+}
+
+// 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 == ';'; };
+// 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); };
+
+// Eat whitespaces.
+static void eatws(const char *&line)
+{
+ while (is_ws(*line))
+ ++ line;
+}
+
+// Parse an int starting at the current position of a line.
+// If succeeded, the line pointer is advanced.
+static inline int parse_int(const char *&line)
+{
+ char *endptr = NULL;
+ long result = strtol(line, &endptr, 10);
+ if (endptr == NULL || !is_ws_or_eol(*endptr))
+ throw std::runtime_error("PressureEqualizer: Error parsing an int");
+ line = endptr;
+ return int(result);
+};
+
+// 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)
+{
+ char *endptr = NULL;
+ float result = strtof(line, &endptr);
+ if (endptr == NULL || !is_ws_or_eol(*endptr))
+ throw std::runtime_error("PressureEqualizer: Error parsing a float");
+ line = endptr;
+ return result;
+};
+
+#define EXTRUSION_ROLE_TAG ";_EXTRUSION_ROLE:"
+bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLine &buf)
+{
+ if (strncmp(line, EXTRUSION_ROLE_TAG, strlen(EXTRUSION_ROLE_TAG)) == 0) {
+ line += strlen(EXTRUSION_ROLE_TAG);
+ int role = atoi(line);
+ m_current_extrusion_role = ExtrusionRole(role);
+ ++ line_idx;
+ return false;
+ }
+
+ // Set the type, copy the line to the buffer.
+ buf.type = GCODELINETYPE_OTHER;
+ buf.modified = false;
+ if (buf.raw.size() < len + 1)
+ buf.raw.assign(line, line + len + 1);
+ else
+ memcpy(buf.raw.data(), line, len);
+ buf.raw[len] = 0;
+ buf.raw_length = len;
+
+ memcpy(buf.pos_start, m_current_pos, sizeof(float)*5);
+ memcpy(buf.pos_end, m_current_pos, sizeof(float)*5);
+ memset(buf.pos_provided, 0, 5);
+
+ buf.volumetric_extrusion_rate = 0.f;
+ buf.volumetric_extrusion_rate_start = 0.f;
+ buf.volumetric_extrusion_rate_end = 0.f;
+ buf.max_volumetric_extrusion_rate_slope_positive = 0.f;
+ buf.max_volumetric_extrusion_rate_slope_negative = 0.f;
+ buf.extrusion_role = m_current_extrusion_role;
+
+ // Parse the G-code line, store the result into the buf.
+ switch (toupper(*line ++)) {
+ case 'G': {
+ int gcode = parse_int(line);
+ eatws(line);
+ switch (gcode) {
+ case 0:
+ case 1:
+ {
+ // G0, G1: A FFF 3D printer does not make a difference between the two.
+ 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++);
+ int i = -1;
+ switch (axis) {
+ case 'X':
+ case 'Y':
+ case 'Z':
+ i = axis - 'X';
+ break;
+ case 'E':
+ i = 3;
+ break;
+ case 'F':
+ i = 4;
+ break;
+ default:
+ assert(false);
+ }
+ if (i == -1)
+ throw std::runtime_error(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.
+ float diff = new_pos[3] - m_current_pos[3];
+ if (diff < 0) {
+ buf.type = GCODELINETYPE_RETRACT;
+ m_retracted = true;
+ } else if (! changed[0] && ! changed[1] && ! changed[2]) {
+ // assert(m_retracted);
+ buf.type = GCODELINETYPE_UNRETRACT;
+ m_retracted = false;
+ } else {
+ assert(changed[0] || changed[1]);
+ // Moving in XY plane.
+ buf.type = GCODELINETYPE_EXTRUDE;
+ // Calculate the volumetric extrusion rate.
+ float diff[4];
+ for (size_t i = 0; i < 4; ++ i)
+ diff[i] = new_pos[i] - m_current_pos[i];
+ // volumetric extrusion rate = A_filament * F_xyz * L_e / L_xyz [mm^3/min]
+ float len2 = diff[0]*diff[0]+diff[1]*diff[1]+diff[2]*diff[2];
+ float rate = m_filament_crossections[m_current_extruder] * new_pos[4] * sqrt((diff[3]*diff[3])/len2);
+ buf.volumetric_extrusion_rate = rate;
+ buf.volumetric_extrusion_rate_start = rate;
+ buf.volumetric_extrusion_rate_end = rate;
+ m_stat.update(rate, sqrt(len2));
+ 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]);
+ }
+ }
+ } else if (changed[0] || changed[1] || changed[2]) {
+ // Moving without extrusion.
+ buf.type = GCODELINETYPE_MOVE;
+ }
+ memcpy(m_current_pos, new_pos, sizeof(float) * 5);
+ break;
+ }
+ case 92:
+ {
+ // 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++);
+ 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;
+ break;
+ case 'E':
+ m_current_pos[3] = (!is_ws_or_eol(*line)) ? parse_float(line) : 0.f;
+ set = true;
+ break;
+ default:
+ throw std::runtime_error(std::string("GCode::PressureEqualizer: Incorrect axis in a G92 G-code: ") + axis);
+ }
+ eatws(line);
+ }
+ assert(set);
+ break;
+ }
+ case 10:
+ case 22:
+ // Firmware retract.
+ buf.type = GCODELINETYPE_RETRACT;
+ m_retracted = true;
+ break;
+ case 11:
+ case 23:
+ // Firmware unretract.
+ buf.type = GCODELINETYPE_UNRETRACT;
+ m_retracted = false;
+ break;
+ default:
+ // Ignore the rest.
+ break;
+ }
+ break;
+ }
+ case 'M': {
+ int mcode = parse_int(line);
+ eatws(line);
+ switch (mcode) {
+ default:
+ // Ignore the rest of the M-codes.
+ break;
+ }
+ break;
+ }
+ case 'T':
+ {
+ // Activate an extruder head.
+ int new_extruder = parse_int(line);
+ if (new_extruder != m_current_extruder) {
+ m_current_extruder = new_extruder;
+ m_retracted = true;
+ buf.type = GCODELINETYPE_TOOL_CHANGE;
+ } else {
+ buf.type = GCODELINETYPE_NOOP;
+ }
+ break;
+ }
+ }
+
+ buf.extruder_id = m_current_extruder;
+ memcpy(buf.pos_end, m_current_pos, sizeof(float)*5);
+
+ adjust_volumetric_rate();
+ ++ line_idx;
+ return true;
+}
+
+void PressureEqualizer::output_gcode_line(GCodeLine &line)
+{
+ if (! line.modified) {
+ push_to_output(line.raw.data(), line.raw_length, true);
+ return;
+ }
+
+ // The line was modified.
+ // Find the comment.
+ const char *comment = line.raw.data();
+ while (*comment != ';' && *comment != 0) ++comment;
+ if (*comment != ';')
+ comment = NULL;
+
+ // 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);
+ } else {
+ bool accelerating = line.volumetric_extrusion_rate_start < line.volumetric_extrusion_rate_end;
+ // Update the initial and final feed rate values.
+ line.pos_start[4] = line.volumetric_extrusion_rate_start * line.pos_end[4] / line.volumetric_extrusion_rate;
+ 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;
+ // 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;
+ // Time of the acceleration / deceleration part of the segment, if accelerating / decelerating
+ // with the maximum volumetric extrusion rate slope.
+ float t_acc = 0.5f * (line.volumetric_extrusion_rate_start + line.volumetric_extrusion_rate_end) / max_volumetric_extrusion_rate_slope;
+ float l_acc = l;
+ 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;
+ if (l_steady < 0.5f * m_max_segment_length) {
+ l_acc = l;
+ l_steady = 0.f;
+ } else
+ 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);
+ if (l_steady > 0.f) {
+ // There will be a steady feed segment emitted.
+ if (accelerating) {
+ // Prepare the final steady feed rate segment.
+ memcpy(pos_end2, pos_end, sizeof(float)*4);
+ float t = l_acc / l;
+ for (int i = 0; i < 4; ++ i) {
+ pos_end[i] = pos_start[i] + (pos_end[i] - pos_start[i]) * t;
+ line.pos_provided[i] = true;
+ }
+ } else {
+ // Emit the steady feed rate segment.
+ float t = l_steady / l;
+ for (int i = 0; i < 4; ++ i) {
+ 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;
+ memcpy(line.pos_start, line.pos_end, sizeof(float)*5);
+ memcpy(pos_start, line.pos_end, sizeof(float)*5);
+ }
+ }
+ // Split the segment into pieces.
+ for (size_t i = 1; i < nSegments; ++ i) {
+ float t = float(i) / float(nSegments);
+ for (size_t j = 0; j < 4; ++ j) {
+ line.pos_end[j] = pos_start[j] + (pos_end[j] - pos_start[j]) * t;
+ 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;
+ memcpy(line.pos_start, line.pos_end, sizeof(float)*5);
+ }
+ if (l_steady > 0.f && accelerating) {
+ for (int i = 0; i < 4; ++ i) {
+ line.pos_end[i] = pos_end2[i];
+ line.pos_provided[i] = true;
+ }
+ push_line_to_output(line, pos_end[4], comment);
+ }
+ }
+}
+
+void PressureEqualizer::adjust_volumetric_rate()
+{
+ if (circular_buffer_items < 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())
+ // 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;
+
+ 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;
+ // Volumetric extrusion rate at the start of the succeding segment.
+ float rate_succ = circular_buffer[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;
+ float rate_end = feedrate_per_extrusion_role[iRole];
+ if (iRole == 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) {
+ 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;
+ }
+ feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_start : 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;
+
+ 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;
+ // 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;
+ float rate_start = feedrate_per_extrusion_role[iRole];
+ if (iRole == 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.modified = true;
+ } else if (iRole == 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;
+ }
+ feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_end : rate_end;
+ }
+ }
+}
+
+void PressureEqualizer::push_axis_to_output(const char axis, const float value, bool add_eol)
+{
+ char buf[2048];
+ int len = sprintf(buf,
+ (axis == 'E') ? " %c%.3f" : " %c%.5f",
+ axis, value);
+ push_to_output(buf, len, add_eol);
+}
+
+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;
+
+ // Resize the output buffer to a power of 2 higher than the required memory.
+ if (output_buffer.size() < len_new) {
+ size_t v = len_new;
+ // Compute the next highest power of 2 of 32-bit v
+ // http://graphics.stanford.edu/~seander/bithacks.html
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v++;
+ output_buffer.resize(v);
+ }
+
+ // Copy the text to the output.
+ if (len != 0) {
+ memcpy(output_buffer.data() + output_buffer_length, text, len);
+ output_buffer_length += len;
+ }
+ if (add_eol)
+ 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)
+{
+ 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);
+}
+
+} // namespace Slic3r