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:
authorVojtech Bubnik <bubnikv@gmail.com>2022-02-07 17:33:34 +0300
committerVojtech Bubnik <bubnikv@gmail.com>2022-02-07 17:33:34 +0300
commit199dc121a55c0aa0723e5f58417bb2fad1dfc690 (patch)
tree8b3b9d9352bedd249c08a4604cd871aaad59e4cb
parent1138c563b2237f753ab79a5d10ad0ff8ac7a8262 (diff)
Quantization of G-code export to achieve more precise extrusionversion_2.5.0-alpha0
width control.
-rw-r--r--src/libslic3r/Extruder.cpp30
-rw-r--r--src/libslic3r/Extruder.hpp42
-rw-r--r--src/libslic3r/GCode.cpp108
-rw-r--r--src/libslic3r/GCode.hpp9
-rw-r--r--src/libslic3r/GCodeWriter.cpp76
-rw-r--r--src/libslic3r/GCodeWriter.hpp10
6 files changed, 139 insertions, 136 deletions
diff --git a/src/libslic3r/Extruder.cpp b/src/libslic3r/Extruder.cpp
index f7a5c5007..d2ff65097 100644
--- a/src/libslic3r/Extruder.cpp
+++ b/src/libslic3r/Extruder.cpp
@@ -1,4 +1,5 @@
#include "Extruder.hpp"
+#include "GCodeWriter.hpp"
#include "PrintConfig.hpp"
namespace Slic3r {
@@ -7,24 +8,24 @@ Extruder::Extruder(unsigned int id, GCodeConfig *config) :
m_id(id),
m_config(config)
{
- reset();
-
// cache values that are going to be called often
m_e_per_mm3 = this->extrusion_multiplier();
if (! m_config->use_volumetric_e)
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
if (m_config->use_relative_e_distances)
m_E = 0.;
+ // Quantize extruder delta to G-code resolution.
+ dE = GCodeFormatter::quantize_e(dE);
m_E += dE;
m_absolute_E += dE;
if (dE < 0.)
m_retracted -= dE;
- return dE;
+ return std::make_pair(dE, m_E);
}
/* 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
unretraction. If we're actually performing a retraction, any restart_extra
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
if (m_config->use_relative_e_distances)
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.) {
m_E -= to_retract;
m_absolute_E -= 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;
- this->extrude(dE);
+ return std::max(0., GCodeFormatter::quantize_e(retract_length - m_retracted));
+}
+
+std::pair<double, double> Extruder::unretract()
+{
+ auto [dE, emitE] = this->extrude(m_retracted + m_restart_extra);
m_retracted = 0.;
m_restart_extra = 0.;
- return dE;
+ return std::make_pair(dE, emitE);
}
// Used filament volume in mm^3.
diff --git a/src/libslic3r/Extruder.hpp b/src/libslic3r/Extruder.hpp
index e9c6927f8..7491b1c8f 100644
--- a/src/libslic3r/Extruder.hpp
+++ b/src/libslic3r/Extruder.hpp
@@ -12,22 +12,24 @@ class Extruder
{
public:
Extruder(unsigned int id, GCodeConfig *config);
- virtual ~Extruder() {}
-
- void reset() {
- m_E = 0;
- m_absolute_E = 0;
- m_retracted = 0;
- m_restart_extra = 0;
- }
+ ~Extruder() = default;
unsigned int id() const { return m_id; }
- double extrude(double dE);
- double retract(double length, double restart_extra);
- double unretract();
- double E() const { return m_E; }
- void reset_E() { m_E = 0.; }
+ // Following three methods emit:
+ // first - extrusion delta
+ // second - number to emit to G-code: This may be delta for relative mode or a distance from last reset_E() for absolute mode.
+ // They also quantize the E axis to G-code resolution.
+ 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_mm3() const { return m_e_per_mm3; }
// Used filament volume in mm^3.
@@ -57,14 +59,16 @@ private:
GCodeConfig *m_config;
// Print-wide global ID of this extruder.
unsigned int m_id;
- // Current state of the extruder axis, may be resetted if use_relative_e_distances.
- double m_E;
+ // Current state of the extruder axis.
+ // 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.
- double m_absolute_E;
+ double m_absolute_E { 0 };
// Current positive amount of retraction.
- double m_retracted;
+ double m_retracted { 0 };
// 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;
};
@@ -76,4 +80,4 @@ inline bool operator> (const Extruder &e1, const Extruder &e2) { return e1.id()
}
-#endif
+#endif // slic3r_Extruder_hpp_
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 13aedab1b..6a53917de 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -27,7 +27,6 @@
#include <boost/foreach.hpp>
#include <boost/filesystem.hpp>
#include <boost/log/trivial.hpp>
-#include <boost/beast/core/detail/base64.hpp>
#include <boost/nowide/iostream.hpp>
#include <boost/nowide/cstdio.hpp>
@@ -155,63 +154,52 @@ namespace Slic3r {
std::string Wipe::wipe(GCode& gcodegen, bool toolchange)
{
- std::string gcode;
-
- /* Reduce feedrate a bit; travel speed is often too high to move on existing material.
- Too fast = ripping of existing material; too slow = short wipe path, thus more blob. */
- double wipe_speed = gcodegen.writer().config.travel_speed.value * 0.8;
-
- // get the retraction length
- double length = toolchange
- ? gcodegen.writer().extruder()->retract_length_toolchange()
- : gcodegen.writer().extruder()->retract_length();
- // Shorten the retraction length by the amount already retracted before wipe.
- length *= (1. - gcodegen.writer().extruder()->retract_before_wipe());
-
- if (length > 0) {
- /* Calculate how long we need to travel in order to consume the required
- amount of retraction. In other words, how far do we move in XY at wipe_speed
- 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
+ std::string gcode;
+ const Extruder &extruder = *gcodegen.writer().extruder();
+
+ // Remaining quantized retraction length.
+ if (double retract_length = extruder.retract_to_go(toolchange ? extruder.retract_length_toolchange() : extruder.retract_length());
+ retract_length > 0 && this->path.size() >= 2) {
+ // Reduce feedrate a bit; travel speed is often too high to move on existing material.
+ // Too fast = ripping of existing material; too slow = short wipe path, thus more blob.
+ const double wipe_speed = gcodegen.writer().config.travel_speed.value * 0.8;
+ // Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one
+ // due to rounding (TODO: test and/or better math for this).
+ const double xy_to_e = 0.95 * extruder.retract_speed() / wipe_speed;
+ // 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());
+ auto it = this->path.points.begin();
+ Vec2d p = gcodegen.point_to_gcode_quantized(*(++ it));
+ if (p != prev) {
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Start) + "\n";
- for (const Line& line : wipe_path.lines()) {
- double segment_length = line.length();
- /* Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one
- due to rounding (TODO: test and/or better math for this) */
- double dE = length * (segment_length / wipe_dist) * 0.95;
+ auto end = this->path.points.end();
+ bool done = false;
+ for (; it != end; ++ it) {
+ p = gcodegen.point_to_gcode_quantized(*it);
+ 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.
// 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().extrude_to_xy(
- gcodegen.point_to_gcode(line.b),
- -dE,
- "wipe and retract"
- );
+ gcode += gcodegen.writer().set_speed(wipe_speed * 60, {}, gcodegen.enable_cooling_markers() ? ";_WIPE" : "");
+ gcode += gcodegen.writer().extrude_to_xy(p, -dE, "wipe and retract");
+ prev = p;
+ retract_length -= dE;
}
// add tag for processor
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
- this->reset_path();
}
+ // Prevent wiping again on the same path.
+ this->reset_path();
return gcode;
}
@@ -3010,13 +2998,15 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
double path_length = 0.;
{
std::string comment = m_config.gcode_comments ? description : "";
- for (const Line &line : path.polyline.lines()) {
- const double line_length = line.length() * SCALING_FACTOR;
+ Vec2d prev = this->point_to_gcode_quantized(path.polyline.points.front());
+ 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;
- gcode += m_writer.extrude_to_xy(
- this->point_to_gcode(line.b),
- e_per_mm * line_length,
- comment);
+ gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, comment);
+ prev = p;
}
}
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 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
diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp
index f46558c35..c0e636c79 100644
--- a/src/libslic3r/GCode.hpp
+++ b/src/libslic3r/GCode.hpp
@@ -55,9 +55,9 @@ public:
Polyline path;
Wipe() : enable(false) {}
- bool has_path() const { return !this->path.points.empty(); }
- void reset_path() { this->path = Polyline(); }
- std::string wipe(GCode &gcodegen, bool toolchange = false);
+ bool has_path() const { return ! this->path.empty(); }
+ void reset_path() { this->path.clear(); }
+ std::string wipe(GCode &gcodegen, bool toolchange);
};
class WipeTowerIntegration {
@@ -151,7 +151,10 @@ public:
void set_origin(const Vec2d &pointf);
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; }
+ // Convert coordinates of the active object to G-code coordinates, possibly adjusted for extruder offset.
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;
const FullPrintConfig &config() const { return m_config; }
const Layer* layer() const { return m_layer; }
diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp
index 233976b19..c5279c0f5 100644
--- a/src/libslic3r/GCodeWriter.cpp
+++ b/src/libslic3r/GCodeWriter.cpp
@@ -79,7 +79,7 @@ std::string GCodeWriter::postamble() const
std::string GCodeWriter::set_temperature(unsigned int temperature, bool wait, int tool) const
{
if (wait && (FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish)))
- return "";
+ return {};
std::string code, comment;
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)
{
- if (FLAVOR_IS(gcfMach3)
- || FLAVOR_IS(gcfMakerWare)
- || FLAVOR_IS(gcfSailfish))
- return "";
-
- if (m_extruder != nullptr) {
- 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 "";
- }
+ return
+ FLAVOR_IS(gcfMach3) || FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish) || this->config.use_relative_e_distances ||
+ (m_extruder != nullptr && ! m_extruder->reset_E() && ! force) ||
+ m_extrusion_axis.empty() ?
+ std::string{} :
+ std::string("G92 ") + m_extrusion_axis + (this->config.gcode_comments ? "0 ; reset extrusion distance\n" : "0\n");
}
std::string GCodeWriter::update_progress(unsigned int num, unsigned int tot, bool allow_100) const
{
if (FLAVOR_IS_NOT(gcfMakerWare) && FLAVOR_IS_NOT(gcfSailfish))
- return "";
+ return {};
unsigned int percent = (unsigned int)floor(100.0 * num / tot + 0.5);
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)
{
- m_pos(0) = point(0);
- m_pos(1) = point(1);
+ m_pos.x() = point.x();
+ m_pos.y() = point.y();
GCodeG1Formatter w;
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
adjust the nominal Z by reducing the lift amount that will be
used for unlift. */
- if (!this->will_move_z(point(2))) {
- double nominal_z = m_pos(2) - m_lifted;
- m_lifted -= (point(2) - nominal_z);
+ if (!this->will_move_z(point.z())) {
+ double nominal_z = m_pos.z() - m_lifted;
+ m_lifted -= (point.z() - nominal_z);
// 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
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
reducing the lift amount that will be used for unlift. */
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);
if (std::abs(m_lifted) < EPSILON)
m_lifted = 0.;
- return "";
+ return {};
}
/* 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)
{
- m_pos(2) = z;
+ m_pos.z() = z;
double speed = this->config.travel_speed_z.value;
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
we don't perform an actual Z move. */
if (m_lifted > 0) {
- double nominal_z = m_pos(2) - m_lifted;
- if (z >= nominal_z && z <= m_pos(2))
+ double nominal_z = m_pos.z() - m_lifted;
+ if (z >= nominal_z && z <= m_pos.z())
return false;
}
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)
{
- m_pos(0) = point(0);
- m_pos(1) = point(1);
- m_extruder->extrude(dE);
+ m_pos.x() = point.x();
+ m_pos.y() = point.y();
GCodeG1Formatter w;
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);
return w.string();
}
+#if 0
std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment)
{
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);
return w.string();
}
+#endif
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;
}
-
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) {
gcode = FLAVOR_IS(gcfMachinekit) ? "G22 ; retract\n" : "G10 ; retract\n";
} else if (! m_extrusion_axis.empty()) {
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_comment(this->config.gcode_comments, comment);
gcode = w.string();
@@ -449,14 +435,14 @@ std::string GCodeWriter::unretract()
if (FLAVOR_IS(gcfMakerWare))
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) {
gcode += FLAVOR_IS(gcfMachinekit) ? "G23 ; unretract\n" : "G11 ; unretract\n";
gcode += this->reset_e();
} else if (! m_extrusion_axis.empty()) {
// use G1 instead of G0 because G0 will blend the restart with the previous travel move
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_comment(this->config.gcode_comments, " ; unretract");
gcode += w.string();
@@ -476,21 +462,21 @@ std::string GCodeWriter::lift()
{
double above = this->config.retract_lift_above.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());
}
if (m_lifted == 0 && target_lift > 0) {
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 gcode;
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;
}
return gcode;
diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp
index e8a54737e..6c36c0d3a 100644
--- a/src/libslic3r/GCodeWriter.hpp
+++ b/src/libslic3r/GCodeWriter.hpp
@@ -61,7 +61,7 @@ public:
std::string travel_to_z(double z, const std::string &comment = std::string());
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_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_for_toolchange(bool before_wipe = false);
std::string unretract();
@@ -121,6 +121,14 @@ public:
// static constexpr const int E_EXPORT_DIGITS = 9;
#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_xy(const Vec2d &point) {