Quantization of G-code export to achieve more precise extrusion

width control.
This commit is contained in:
Vojtech Bubnik 2022-02-07 15:33:34 +01:00
parent 1138c563b2
commit 199dc121a5
6 changed files with 138 additions and 135 deletions

View File

@ -1,4 +1,5 @@
#include "Extruder.hpp" #include "Extruder.hpp"
#include "GCodeWriter.hpp"
#include "PrintConfig.hpp" #include "PrintConfig.hpp"
namespace Slic3r { namespace Slic3r {
@ -7,24 +8,24 @@ Extruder::Extruder(unsigned int id, GCodeConfig *config) :
m_id(id), m_id(id),
m_config(config) m_config(config)
{ {
reset();
// cache values that are going to be called often // cache values that are going to be called often
m_e_per_mm3 = this->extrusion_multiplier(); m_e_per_mm3 = this->extrusion_multiplier();
if (! m_config->use_volumetric_e) if (! m_config->use_volumetric_e)
m_e_per_mm3 /= this->filament_crossection(); m_e_per_mm3 /= this->filament_crossection();
} }
double Extruder::extrude(double dE) std::pair<double, double> Extruder::extrude(double dE)
{ {
// in case of relative E distances we always reset to 0 before any output // in case of relative E distances we always reset to 0 before any output
if (m_config->use_relative_e_distances) if (m_config->use_relative_e_distances)
m_E = 0.; m_E = 0.;
// Quantize extruder delta to G-code resolution.
dE = GCodeFormatter::quantize_e(dE);
m_E += dE; m_E += dE;
m_absolute_E += dE; m_absolute_E += dE;
if (dE < 0.) if (dE < 0.)
m_retracted -= dE; m_retracted -= dE;
return dE; return std::make_pair(dE, m_E);
} }
/* This method makes sure the extruder is retracted by the specified amount /* This method makes sure the extruder is retracted by the specified amount
@ -34,28 +35,33 @@ double Extruder::extrude(double dE)
The restart_extra argument sets the extra length to be used for The restart_extra argument sets the extra length to be used for
unretraction. If we're actually performing a retraction, any restart_extra unretraction. If we're actually performing a retraction, any restart_extra
value supplied will overwrite the previous one if any. */ value supplied will overwrite the previous one if any. */
double Extruder::retract(double length, double restart_extra) std::pair<double, double> Extruder::retract(double retract_length, double restart_extra)
{ {
// in case of relative E distances we always reset to 0 before any output // in case of relative E distances we always reset to 0 before any output
if (m_config->use_relative_e_distances) if (m_config->use_relative_e_distances)
m_E = 0.; m_E = 0.;
double to_retract = std::max(0., length - m_retracted); // Quantize extruder delta to G-code resolution.
double to_retract = this->retract_to_go(retract_length);
if (to_retract > 0.) { if (to_retract > 0.) {
m_E -= to_retract; m_E -= to_retract;
m_absolute_E -= to_retract; m_absolute_E -= to_retract;
m_retracted += to_retract; m_retracted += to_retract;
m_restart_extra = restart_extra; m_restart_extra = restart_extra;
} }
return to_retract; return std::make_pair(to_retract, m_E);
} }
double Extruder::unretract() double Extruder::retract_to_go(double retract_length) const
{ {
double dE = m_retracted + m_restart_extra; return std::max(0., GCodeFormatter::quantize_e(retract_length - m_retracted));
this->extrude(dE); }
std::pair<double, double> Extruder::unretract()
{
auto [dE, emitE] = this->extrude(m_retracted + m_restart_extra);
m_retracted = 0.; m_retracted = 0.;
m_restart_extra = 0.; m_restart_extra = 0.;
return dE; return std::make_pair(dE, emitE);
} }
// Used filament volume in mm^3. // Used filament volume in mm^3.

View File

@ -12,22 +12,24 @@ class Extruder
{ {
public: public:
Extruder(unsigned int id, GCodeConfig *config); Extruder(unsigned int id, GCodeConfig *config);
virtual ~Extruder() {} ~Extruder() = default;
void reset() {
m_E = 0;
m_absolute_E = 0;
m_retracted = 0;
m_restart_extra = 0;
}
unsigned int id() const { return m_id; } unsigned int id() const { return m_id; }
double extrude(double dE); // Following three methods emit:
double retract(double length, double restart_extra); // first - extrusion delta
double unretract(); // second - number to emit to G-code: This may be delta for relative mode or a distance from last reset_E() for absolute mode.
double E() const { return m_E; } // They also quantize the E axis to G-code resolution.
void reset_E() { m_E = 0.; } std::pair<double, double> extrude(double dE);
std::pair<double, double> retract(double retract_length, double restart_extra);
std::pair<double, double> unretract();
// How much to retract yet before retract_length is reached?
// The value is quantized to G-code resolution.
double retract_to_go(double retract_length) const;
// Reset the current state of the E axis (this is only needed for relative extruder addressing mode anyways).
// Returns true if the extruder was non-zero before reset.
bool reset_E() { bool modified = m_E != 0; m_E = 0.; return modified; }
double e_per_mm(double mm3_per_mm) const { return mm3_per_mm * m_e_per_mm3; } double e_per_mm(double mm3_per_mm) const { return mm3_per_mm * m_e_per_mm3; }
double e_per_mm3() const { return m_e_per_mm3; } double e_per_mm3() const { return m_e_per_mm3; }
// Used filament volume in mm^3. // Used filament volume in mm^3.
@ -57,14 +59,16 @@ private:
GCodeConfig *m_config; GCodeConfig *m_config;
// Print-wide global ID of this extruder. // Print-wide global ID of this extruder.
unsigned int m_id; unsigned int m_id;
// Current state of the extruder axis, may be resetted if use_relative_e_distances. // Current state of the extruder axis.
double m_E; // For absolute extruder addressing, it is the current state since the last reset (G92 E0) issued at the end of the last retraction.
// For relative extruder addressing, it is the E axis difference emitted into the G-code the last time.
double m_E { 0 };
// Current state of the extruder tachometer, used to output the extruded_volume() and used_filament() statistics. // Current state of the extruder tachometer, used to output the extruded_volume() and used_filament() statistics.
double m_absolute_E; double m_absolute_E { 0 };
// Current positive amount of retraction. // Current positive amount of retraction.
double m_retracted; double m_retracted { 0 };
// When retracted, this value stores the extra amount of priming on deretraction. // When retracted, this value stores the extra amount of priming on deretraction.
double m_restart_extra; double m_restart_extra { 0 };
double m_e_per_mm3; double m_e_per_mm3;
}; };
@ -76,4 +80,4 @@ inline bool operator> (const Extruder &e1, const Extruder &e2) { return e1.id()
} }
#endif #endif // slic3r_Extruder_hpp_

View File

@ -27,7 +27,6 @@
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <boost/beast/core/detail/base64.hpp>
#include <boost/nowide/iostream.hpp> #include <boost/nowide/iostream.hpp>
#include <boost/nowide/cstdio.hpp> #include <boost/nowide/cstdio.hpp>
@ -156,62 +155,51 @@ namespace Slic3r {
std::string Wipe::wipe(GCode& gcodegen, bool toolchange) std::string Wipe::wipe(GCode& gcodegen, bool toolchange)
{ {
std::string gcode; std::string gcode;
const Extruder &extruder = *gcodegen.writer().extruder();
/* Reduce feedrate a bit; travel speed is often too high to move on existing material. // Remaining quantized retraction length.
Too fast = ripping of existing material; too slow = short wipe path, thus more blob. */ if (double retract_length = extruder.retract_to_go(toolchange ? extruder.retract_length_toolchange() : extruder.retract_length());
double wipe_speed = gcodegen.writer().config.travel_speed.value * 0.8; retract_length > 0 && this->path.size() >= 2) {
// Reduce feedrate a bit; travel speed is often too high to move on existing material.
// get the retraction length // Too fast = ripping of existing material; too slow = short wipe path, thus more blob.
double length = toolchange const double wipe_speed = gcodegen.writer().config.travel_speed.value * 0.8;
? gcodegen.writer().extruder()->retract_length_toolchange() // Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one
: gcodegen.writer().extruder()->retract_length(); // due to rounding (TODO: test and/or better math for this).
// Shorten the retraction length by the amount already retracted before wipe. const double xy_to_e = 0.95 * extruder.retract_speed() / wipe_speed;
length *= (1. - gcodegen.writer().extruder()->retract_before_wipe()); // Start with the current position, which may be different from the wipe path start in case of loop clipping.
Vec2d prev = gcodegen.point_to_gcode_quantized(gcodegen.last_pos());
if (length > 0) { auto it = this->path.points.begin();
/* Calculate how long we need to travel in order to consume the required Vec2d p = gcodegen.point_to_gcode_quantized(*(++ it));
amount of retraction. In other words, how far do we move in XY at wipe_speed if (p != prev) {
for the time needed to consume retract_length at retract_speed? */
double wipe_dist = scale_(length / gcodegen.writer().extruder()->retract_speed() * wipe_speed);
/* Take the stored wipe path and replace first point with the current actual position
(they might be different, for example, in case of loop clipping). */
Polyline wipe_path;
wipe_path.append(gcodegen.last_pos());
wipe_path.append(
this->path.points.begin() + 1,
this->path.points.end()
);
wipe_path.clip_end(wipe_path.length() - wipe_dist);
// subdivide the retraction in segments
if (!wipe_path.empty()) {
// add tag for processor
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Start) + "\n"; gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Start) + "\n";
for (const Line& line : wipe_path.lines()) { auto end = this->path.points.end();
double segment_length = line.length(); bool done = false;
/* Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one for (; it != end; ++ it) {
due to rounding (TODO: test and/or better math for this) */ p = gcodegen.point_to_gcode_quantized(*it);
double dE = length * (segment_length / wipe_dist) * 0.95; double segment_length = (p - prev).norm();
double dE = GCodeFormatter::quantize_e(xy_to_e * segment_length);
if (dE > retract_length - EPSILON) {
if (dE > retract_length + EPSILON)
// Shorten the segment.
p = prev + (p - prev) * (retract_length / dE);
dE = retract_length;
done = true;
}
//FIXME one shall not generate the unnecessary G1 Fxxx commands, here wipe_speed is a constant inside this cycle. //FIXME one shall not generate the unnecessary G1 Fxxx commands, here wipe_speed is a constant inside this cycle.
// Is it here for the cooling markers? Or should it be outside of the cycle? // Is it here for the cooling markers? Or should it be outside of the cycle?
gcode += gcodegen.writer().set_speed(wipe_speed * 60, "", gcodegen.enable_cooling_markers() ? ";_WIPE" : ""); gcode += gcodegen.writer().set_speed(wipe_speed * 60, {}, gcodegen.enable_cooling_markers() ? ";_WIPE" : "");
gcode += gcodegen.writer().extrude_to_xy( gcode += gcodegen.writer().extrude_to_xy(p, -dE, "wipe and retract");
gcodegen.point_to_gcode(line.b), prev = p;
-dE, retract_length -= dE;
"wipe and retract"
);
} }
// add tag for processor // add tag for processor
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_End) + "\n"; gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_End) + "\n";
gcodegen.set_last_pos(wipe_path.points.back()); gcodegen.set_last_pos(gcodegen.gcode_to_point(prev));
}
} }
// prevent wiping again on same path // Prevent wiping again on the same path.
this->reset_path(); this->reset_path();
}
return gcode; return gcode;
} }
@ -3010,13 +2998,15 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
double path_length = 0.; double path_length = 0.;
{ {
std::string comment = m_config.gcode_comments ? description : ""; std::string comment = m_config.gcode_comments ? description : "";
for (const Line &line : path.polyline.lines()) { Vec2d prev = this->point_to_gcode_quantized(path.polyline.points.front());
const double line_length = line.length() * SCALING_FACTOR; auto it = path.polyline.points.begin();
auto end = path.polyline.points.end();
for (++ it; it != end; ++ it) {
Vec2d p = this->point_to_gcode_quantized(*it);
const double line_length = (p - prev).norm();
path_length += line_length; path_length += line_length;
gcode += m_writer.extrude_to_xy( gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, comment);
this->point_to_gcode(line.b), prev = p;
e_per_mm * line_length,
comment);
} }
} }
if (m_enable_cooling_markers) if (m_enable_cooling_markers)
@ -3239,7 +3229,13 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
Vec2d GCode::point_to_gcode(const Point &point) const Vec2d GCode::point_to_gcode(const Point &point) const
{ {
Vec2d extruder_offset = EXTRUDER_CONFIG(extruder_offset); Vec2d extruder_offset = EXTRUDER_CONFIG(extruder_offset);
return unscale(point) + m_origin - extruder_offset; return unscaled<double>(point) + m_origin - extruder_offset;
}
Vec2d GCode::point_to_gcode_quantized(const Point &point) const
{
Vec2d p = this->point_to_gcode(point);
return { GCodeFormatter::quantize_xyzf(p.x()), GCodeFormatter::quantize_xyzf(p.y()) };
} }
// convert a model-space scaled point into G-code coordinates // convert a model-space scaled point into G-code coordinates

View File

@ -55,9 +55,9 @@ public:
Polyline path; Polyline path;
Wipe() : enable(false) {} Wipe() : enable(false) {}
bool has_path() const { return !this->path.points.empty(); } bool has_path() const { return ! this->path.empty(); }
void reset_path() { this->path = Polyline(); } void reset_path() { this->path.clear(); }
std::string wipe(GCode &gcodegen, bool toolchange = false); std::string wipe(GCode &gcodegen, bool toolchange);
}; };
class WipeTowerIntegration { class WipeTowerIntegration {
@ -151,7 +151,10 @@ public:
void set_origin(const Vec2d &pointf); void set_origin(const Vec2d &pointf);
void set_origin(const coordf_t x, const coordf_t y) { this->set_origin(Vec2d(x, y)); } void set_origin(const coordf_t x, const coordf_t y) { this->set_origin(Vec2d(x, y)); }
const Point& last_pos() const { return m_last_pos; } const Point& last_pos() const { return m_last_pos; }
// Convert coordinates of the active object to G-code coordinates, possibly adjusted for extruder offset.
Vec2d point_to_gcode(const Point &point) const; Vec2d point_to_gcode(const Point &point) const;
// Convert coordinates of the active object to G-code coordinates, possibly adjusted for extruder offset and quantized to G-code resolution.
Vec2d point_to_gcode_quantized(const Point &point) const;
Point gcode_to_point(const Vec2d &point) const; Point gcode_to_point(const Vec2d &point) const;
const FullPrintConfig &config() const { return m_config; } const FullPrintConfig &config() const { return m_config; }
const Layer* layer() const { return m_layer; } const Layer* layer() const { return m_layer; }

View File

@ -79,7 +79,7 @@ std::string GCodeWriter::postamble() const
std::string GCodeWriter::set_temperature(unsigned int temperature, bool wait, int tool) const std::string GCodeWriter::set_temperature(unsigned int temperature, bool wait, int tool) const
{ {
if (wait && (FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish))) if (wait && (FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish)))
return ""; return {};
std::string code, comment; std::string code, comment;
if (wait && FLAVOR_IS_NOT(gcfTeacup) && FLAVOR_IS_NOT(gcfRepRapFirmware)) { if (wait && FLAVOR_IS_NOT(gcfTeacup) && FLAVOR_IS_NOT(gcfRepRapFirmware)) {
@ -192,32 +192,18 @@ std::string GCodeWriter::set_acceleration(unsigned int acceleration)
std::string GCodeWriter::reset_e(bool force) std::string GCodeWriter::reset_e(bool force)
{ {
if (FLAVOR_IS(gcfMach3) return
|| FLAVOR_IS(gcfMakerWare) FLAVOR_IS(gcfMach3) || FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish) || this->config.use_relative_e_distances ||
|| FLAVOR_IS(gcfSailfish)) (m_extruder != nullptr && ! m_extruder->reset_E() && ! force) ||
return ""; m_extrusion_axis.empty() ?
std::string{} :
if (m_extruder != nullptr) { std::string("G92 ") + m_extrusion_axis + (this->config.gcode_comments ? "0 ; reset extrusion distance\n" : "0\n");
if (m_extruder->E() == 0. && ! force)
return "";
m_extruder->reset_E();
}
if (! m_extrusion_axis.empty() && ! this->config.use_relative_e_distances) {
std::ostringstream gcode;
gcode << "G92 " << m_extrusion_axis << "0";
if (this->config.gcode_comments) gcode << " ; reset extrusion distance";
gcode << "\n";
return gcode.str();
} else {
return "";
}
} }
std::string GCodeWriter::update_progress(unsigned int num, unsigned int tot, bool allow_100) const std::string GCodeWriter::update_progress(unsigned int num, unsigned int tot, bool allow_100) const
{ {
if (FLAVOR_IS_NOT(gcfMakerWare) && FLAVOR_IS_NOT(gcfSailfish)) if (FLAVOR_IS_NOT(gcfMakerWare) && FLAVOR_IS_NOT(gcfSailfish))
return ""; return {};
unsigned int percent = (unsigned int)floor(100.0 * num / tot + 0.5); unsigned int percent = (unsigned int)floor(100.0 * num / tot + 0.5);
if (!allow_100) percent = std::min(percent, (unsigned int)99); if (!allow_100) percent = std::min(percent, (unsigned int)99);
@ -269,8 +255,8 @@ std::string GCodeWriter::set_speed(double F, const std::string &comment, const s
std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &comment) std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &comment)
{ {
m_pos(0) = point(0); m_pos.x() = point.x();
m_pos(1) = point(1); m_pos.y() = point.y();
GCodeG1Formatter w; GCodeG1Formatter w;
w.emit_xy(point); w.emit_xy(point);
@ -290,9 +276,9 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
don't perform the Z move but we only move in the XY plane and don't perform the Z move but we only move in the XY plane and
adjust the nominal Z by reducing the lift amount that will be adjust the nominal Z by reducing the lift amount that will be
used for unlift. */ used for unlift. */
if (!this->will_move_z(point(2))) { if (!this->will_move_z(point.z())) {
double nominal_z = m_pos(2) - m_lifted; double nominal_z = m_pos.z() - m_lifted;
m_lifted -= (point(2) - nominal_z); m_lifted -= (point.z() - nominal_z);
// In case that retract_lift == layer_height we could end up with almost zero in_m_lifted // In case that retract_lift == layer_height we could end up with almost zero in_m_lifted
// and a retract could be skipped (https://github.com/prusa3d/PrusaSlicer/issues/2154 // and a retract could be skipped (https://github.com/prusa3d/PrusaSlicer/issues/2154
if (std::abs(m_lifted) < EPSILON) if (std::abs(m_lifted) < EPSILON)
@ -318,11 +304,11 @@ std::string GCodeWriter::travel_to_z(double z, const std::string &comment)
we don't perform the move but we only adjust the nominal Z by we don't perform the move but we only adjust the nominal Z by
reducing the lift amount that will be used for unlift. */ reducing the lift amount that will be used for unlift. */
if (!this->will_move_z(z)) { if (!this->will_move_z(z)) {
double nominal_z = m_pos(2) - m_lifted; double nominal_z = m_pos.z() - m_lifted;
m_lifted -= (z - nominal_z); m_lifted -= (z - nominal_z);
if (std::abs(m_lifted) < EPSILON) if (std::abs(m_lifted) < EPSILON)
m_lifted = 0.; m_lifted = 0.;
return ""; return {};
} }
/* In all the other cases, we perform an actual Z move and cancel /* In all the other cases, we perform an actual Z move and cancel
@ -333,7 +319,7 @@ std::string GCodeWriter::travel_to_z(double z, const std::string &comment)
std::string GCodeWriter::_travel_to_z(double z, const std::string &comment) std::string GCodeWriter::_travel_to_z(double z, const std::string &comment)
{ {
m_pos(2) = z; m_pos.z() = z;
double speed = this->config.travel_speed_z.value; double speed = this->config.travel_speed_z.value;
if (speed == 0.) if (speed == 0.)
@ -351,8 +337,8 @@ bool GCodeWriter::will_move_z(double z) const
/* If target Z is lower than current Z but higher than nominal Z /* If target Z is lower than current Z but higher than nominal Z
we don't perform an actual Z move. */ we don't perform an actual Z move. */
if (m_lifted > 0) { if (m_lifted > 0) {
double nominal_z = m_pos(2) - m_lifted; double nominal_z = m_pos.z() - m_lifted;
if (z >= nominal_z && z <= m_pos(2)) if (z >= nominal_z && z <= m_pos.z())
return false; return false;
} }
return true; return true;
@ -360,17 +346,17 @@ bool GCodeWriter::will_move_z(double z) const
std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std::string &comment) std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std::string &comment)
{ {
m_pos(0) = point(0); m_pos.x() = point.x();
m_pos(1) = point(1); m_pos.y() = point.y();
m_extruder->extrude(dE);
GCodeG1Formatter w; GCodeG1Formatter w;
w.emit_xy(point); w.emit_xy(point);
w.emit_e(m_extrusion_axis, m_extruder->E()); w.emit_e(m_extrusion_axis, m_extruder->extrude(dE).second);
w.emit_comment(this->config.gcode_comments, comment); w.emit_comment(this->config.gcode_comments, comment);
return w.string(); return w.string();
} }
#if 0
std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment) std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment)
{ {
m_pos = point; m_pos = point;
@ -383,6 +369,7 @@ std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std
w.emit_comment(this->config.gcode_comments, comment); w.emit_comment(this->config.gcode_comments, comment);
return w.string(); return w.string();
} }
#endif
std::string GCodeWriter::retract(bool before_wipe) std::string GCodeWriter::retract(bool before_wipe)
{ {
@ -422,14 +409,13 @@ std::string GCodeWriter::_retract(double length, double restart_extra, const std
restart_extra = restart_extra * area; restart_extra = restart_extra * area;
} }
std::string gcode; std::string gcode;
if (double dE = m_extruder->retract(length, restart_extra); dE != 0) { if (auto [dE, emitE] = m_extruder->retract(length, restart_extra); dE != 0) {
if (this->config.use_firmware_retraction) { if (this->config.use_firmware_retraction) {
gcode = FLAVOR_IS(gcfMachinekit) ? "G22 ; retract\n" : "G10 ; retract\n"; gcode = FLAVOR_IS(gcfMachinekit) ? "G22 ; retract\n" : "G10 ; retract\n";
} else if (! m_extrusion_axis.empty()) { } else if (! m_extrusion_axis.empty()) {
GCodeG1Formatter w; GCodeG1Formatter w;
w.emit_e(m_extrusion_axis, m_extruder->E()); w.emit_e(m_extrusion_axis, emitE);
w.emit_f(m_extruder->retract_speed() * 60.); w.emit_f(m_extruder->retract_speed() * 60.);
w.emit_comment(this->config.gcode_comments, comment); w.emit_comment(this->config.gcode_comments, comment);
gcode = w.string(); gcode = w.string();
@ -449,14 +435,14 @@ std::string GCodeWriter::unretract()
if (FLAVOR_IS(gcfMakerWare)) if (FLAVOR_IS(gcfMakerWare))
gcode = "M101 ; extruder on\n"; gcode = "M101 ; extruder on\n";
if (double dE = m_extruder->unretract(); dE != 0) { if (auto [dE, emitE] = m_extruder->unretract(); dE != 0) {
if (this->config.use_firmware_retraction) { if (this->config.use_firmware_retraction) {
gcode += FLAVOR_IS(gcfMachinekit) ? "G23 ; unretract\n" : "G11 ; unretract\n"; gcode += FLAVOR_IS(gcfMachinekit) ? "G23 ; unretract\n" : "G11 ; unretract\n";
gcode += this->reset_e(); gcode += this->reset_e();
} else if (! m_extrusion_axis.empty()) { } else if (! m_extrusion_axis.empty()) {
// use G1 instead of G0 because G0 will blend the restart with the previous travel move // use G1 instead of G0 because G0 will blend the restart with the previous travel move
GCodeG1Formatter w; GCodeG1Formatter w;
w.emit_e(m_extrusion_axis, m_extruder->E()); w.emit_e(m_extrusion_axis, emitE);
w.emit_f(m_extruder->deretract_speed() * 60.); w.emit_f(m_extruder->deretract_speed() * 60.);
w.emit_comment(this->config.gcode_comments, " ; unretract"); w.emit_comment(this->config.gcode_comments, " ; unretract");
gcode += w.string(); gcode += w.string();
@ -476,21 +462,21 @@ std::string GCodeWriter::lift()
{ {
double above = this->config.retract_lift_above.get_at(m_extruder->id()); double above = this->config.retract_lift_above.get_at(m_extruder->id());
double below = this->config.retract_lift_below.get_at(m_extruder->id()); double below = this->config.retract_lift_below.get_at(m_extruder->id());
if (m_pos(2) >= above && (below == 0 || m_pos(2) <= below)) if (m_pos.z() >= above && (below == 0 || m_pos.z() <= below))
target_lift = this->config.retract_lift.get_at(m_extruder->id()); target_lift = this->config.retract_lift.get_at(m_extruder->id());
} }
if (m_lifted == 0 && target_lift > 0) { if (m_lifted == 0 && target_lift > 0) {
m_lifted = target_lift; m_lifted = target_lift;
return this->_travel_to_z(m_pos(2) + target_lift, "lift Z"); return this->_travel_to_z(m_pos.z() + target_lift, "lift Z");
} }
return ""; return {};
} }
std::string GCodeWriter::unlift() std::string GCodeWriter::unlift()
{ {
std::string gcode; std::string gcode;
if (m_lifted > 0) { if (m_lifted > 0) {
gcode += this->_travel_to_z(m_pos(2) - m_lifted, "restore layer Z"); gcode += this->_travel_to_z(m_pos.z() - m_lifted, "restore layer Z");
m_lifted = 0; m_lifted = 0;
} }
return gcode; return gcode;

View File

@ -61,7 +61,7 @@ public:
std::string travel_to_z(double z, const std::string &comment = std::string()); std::string travel_to_z(double z, const std::string &comment = std::string());
bool will_move_z(double z) const; bool will_move_z(double z) const;
std::string extrude_to_xy(const Vec2d &point, double dE, const std::string &comment = std::string()); std::string extrude_to_xy(const Vec2d &point, double dE, const std::string &comment = std::string());
std::string extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment = std::string()); // std::string extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment = std::string());
std::string retract(bool before_wipe = false); std::string retract(bool before_wipe = false);
std::string retract_for_toolchange(bool before_wipe = false); std::string retract_for_toolchange(bool before_wipe = false);
std::string unretract(); std::string unretract();
@ -121,6 +121,14 @@ public:
// static constexpr const int E_EXPORT_DIGITS = 9; // static constexpr const int E_EXPORT_DIGITS = 9;
#endif #endif
static constexpr const std::array<double, 10> pow_10 { 1., 10., 100., 1000., 10000., 100000., 1000000., 10000000., 100000000., 1000000000.};
static constexpr const std::array<double, 10> pow_10_inv{1./1., 1./10., 1./100., 1./1000., 1./10000., 1./100000., 1./1000000., 1./10000000., 1./100000000., 1./1000000000.};
// Quantize doubles to a resolution of the G-code.
static double quantize(double v, size_t ndigits) { return std::round(v * pow_10[ndigits]) * pow_10_inv[ndigits]; }
static double quantize_xyzf(double v) { return quantize(v, XYZF_EXPORT_DIGITS); }
static double quantize_e(double v) { return quantize(v, E_EXPORT_DIGITS); }
void emit_axis(const char axis, const double v, size_t digits); void emit_axis(const char axis, const double v, size_t digits);
void emit_xy(const Vec2d &point) { void emit_xy(const Vec2d &point) {