GCodeWriter: published GCodeFormatter, made it more generic, so

it could be used outside of GCodeWriter.
Ported the GCodeWriter::retract/deretract to GCodeFormatter.
This commit is contained in:
Vojtech Bubnik 2021-09-13 11:55:38 +02:00
parent fda8ef6fce
commit e7591e6aa6
2 changed files with 150 additions and 147 deletions

View File

@ -1,7 +1,6 @@
#include "GCodeWriter.hpp"
#include "CustomGCode.hpp"
#include <algorithm>
#include <charconv>
#include <iomanip>
#include <iostream>
#include <map>
@ -11,15 +10,8 @@
#include <boost/spirit/include/karma.hpp>
#endif
#define XYZF_EXPORT_DIGITS 3
#define E_EXPORT_DIGITS 5
#define FLAVOR_IS(val) this->config.gcode_flavor == val
#define FLAVOR_IS_NOT(val) this->config.gcode_flavor != val
#define COMMENT(comment) if (this->config.gcode_comments && !comment.empty()) gcode << " ; " << comment;
#define PRECISION(val, precision) std::fixed << std::setprecision(precision) << (val)
#define XYZF_NUM(val) PRECISION(val, XYZF_EXPORT_DIGITS)
#define E_NUM(val) PRECISION(val, E_EXPORT_DIGITS)
namespace Slic3r {
@ -261,115 +253,12 @@ std::string GCodeWriter::toolchange(unsigned int extruder_id)
return gcode.str();
}
class G1Writer {
private:
static constexpr const size_t buflen = 256;
char buf[buflen];
char *buf_end;
std::to_chars_result ptr_err;
public:
G1Writer() {
this->buf[0] = 'G';
this->buf[1] = '1';
this->buf_end = this->buf + buflen;
this->ptr_err.ptr = this->buf + 2;
}
void emit_axis(const char axis, const double v, size_t digits) {
assert(digits <= 6);
static constexpr const std::array<int, 7> pow_10{1, 10, 100, 1000, 10000, 100000, 1000000};
*ptr_err.ptr++ = ' '; *ptr_err.ptr++ = axis;
char *base_ptr = this->ptr_err.ptr;
auto v_int = int64_t(std::round(v * pow_10[digits]));
// Older stdlib on macOS doesn't support std::from_chars at all, so it is used boost::spirit::karma::generate instead of it.
// That is a little bit slower than std::to_chars but not much.
#ifdef __APPLE__
boost::spirit::karma::generate(this->ptr_err.ptr, boost::spirit::karma::int_generator<int64_t>(), v_int);
#else
// this->buf_end minus 1 because we need space for adding the extra decimal point.
this->ptr_err = std::to_chars(this->ptr_err.ptr, this->buf_end - 1, v_int);
#endif
size_t writen_digits = (this->ptr_err.ptr - base_ptr) - (v_int < 0 ? 1 : 0);
if (writen_digits < digits) {
// Number is smaller than 10^digits, so that we will pad it with zeros.
size_t remaining_digits = digits - writen_digits;
// Move all newly inserted chars by remaining_digits to allocate space for padding with zeros.
for (char *from_ptr = this->ptr_err.ptr - 1, *to_ptr = from_ptr + remaining_digits; from_ptr >= this->ptr_err.ptr - writen_digits; --to_ptr, --from_ptr)
*to_ptr = *from_ptr;
memset(this->ptr_err.ptr - writen_digits, '0', remaining_digits);
this->ptr_err.ptr += remaining_digits;
}
// Move all newly inserted chars by one to allocate space for a decimal point.
for (char *to_ptr = this->ptr_err.ptr, *from_ptr = to_ptr - 1; from_ptr >= this->ptr_err.ptr - digits; --to_ptr, --from_ptr)
*to_ptr = *from_ptr;
*(this->ptr_err.ptr - digits) = '.';
for (size_t i = 0; i < digits; ++i) {
if (*this->ptr_err.ptr != '0')
break;
this->ptr_err.ptr--;
}
if (*this->ptr_err.ptr == '.')
this->ptr_err.ptr--;
if ((this->ptr_err.ptr + 1) == base_ptr || *this->ptr_err.ptr == '-')
*(++this->ptr_err.ptr) = '0';
this->ptr_err.ptr++;
}
void emit_xy(const Vec2d &point) {
this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS);
this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS);
}
void emit_xyz(const Vec3d &point) {
this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS);
this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS);
this->emit_z(point.z());
}
void emit_z(const double z) {
this->emit_axis('Z', z, XYZF_EXPORT_DIGITS);
}
void emit_e(const std::string &axis, double v) {
if (! axis.empty()) {
// not gcfNoExtrusion
this->emit_axis(axis[0], v, E_EXPORT_DIGITS);
}
}
void emit_f(double speed) {
this->emit_axis('F', speed, XYZF_EXPORT_DIGITS);
}
void emit_string(const std::string &s) {
strncpy(ptr_err.ptr, s.c_str(), s.size());
ptr_err.ptr += s.size();
}
void emit_comment(bool allow_comments, const std::string &comment) {
if (allow_comments && ! comment.empty()) {
*ptr_err.ptr ++ = ' '; *ptr_err.ptr ++ = ';'; *ptr_err.ptr ++ = ' ';
this->emit_string(comment);
}
}
std::string string() {
*ptr_err.ptr ++ = '\n';
return std::string(this->buf, ptr_err.ptr - buf);
}
};
std::string GCodeWriter::set_speed(double F, const std::string &comment, const std::string &cooling_marker) const
{
assert(F > 0.);
assert(F < 100000.);
G1Writer w;
auto w = GCodeFormatter::G1();
w.emit_f(F);
w.emit_comment(this->config.gcode_comments, comment);
w.emit_string(cooling_marker);
@ -381,7 +270,7 @@ std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &com
m_pos(0) = point(0);
m_pos(1) = point(1);
G1Writer w;
auto w = GCodeFormatter::G1();
w.emit_xy(point);
w.emit_f(this->config.travel_speed.value * 60.0);
w.emit_comment(this->config.gcode_comments, comment);
@ -414,7 +303,7 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
m_lifted = 0;
m_pos = point;
G1Writer w;
auto w = GCodeFormatter::G1();
w.emit_xyz(point);
w.emit_f(this->config.travel_speed.value * 60.0);
w.emit_comment(this->config.gcode_comments, comment);
@ -448,7 +337,7 @@ std::string GCodeWriter::_travel_to_z(double z, const std::string &comment)
if (speed == 0.)
speed = this->config.travel_speed.value;
G1Writer w;
auto w = GCodeFormatter::G1();
w.emit_z(z);
w.emit_f(speed * 60.0);
w.emit_comment(this->config.gcode_comments, comment);
@ -473,7 +362,7 @@ std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std:
m_pos(1) = point(1);
m_extruder->extrude(dE);
G1Writer w;
auto w = GCodeFormatter::G1();
w.emit_xy(point);
w.emit_e(m_extrusion_axis, m_extruder->E());
w.emit_comment(this->config.gcode_comments, comment);
@ -486,7 +375,7 @@ std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std
m_lifted = 0;
m_extruder->extrude(dE);
G1Writer w;
auto w = GCodeFormatter::G1();
w.emit_xyz(point);
w.emit_e(m_extrusion_axis, m_extruder->E());
w.emit_comment(this->config.gcode_comments, comment);
@ -517,12 +406,11 @@ std::string GCodeWriter::retract_for_toolchange(bool before_wipe)
std::string GCodeWriter::_retract(double length, double restart_extra, const std::string &comment)
{
std::ostringstream gcode;
/* If firmware retraction is enabled, we use a fake value of 1
since we ignore the actual configured retract_length which
might be 0, in which case the retraction logic gets skipped. */
if (this->config.use_firmware_retraction) length = 1;
if (this->config.use_firmware_retraction)
length = 1;
// If we use volumetric E values we turn lengths into volumes */
if (this->config.use_volumetric_e) {
@ -532,52 +420,48 @@ std::string GCodeWriter::_retract(double length, double restart_extra, const std
restart_extra = restart_extra * area;
}
double dE = m_extruder->retract(length, restart_extra);
if (dE != 0) {
std::string gcode;
if (double dE = m_extruder->retract(length, restart_extra); dE != 0) {
if (this->config.use_firmware_retraction) {
if (FLAVOR_IS(gcfMachinekit))
gcode << "G22 ; retract\n";
else
gcode << "G10 ; retract\n";
gcode = FLAVOR_IS(gcfMachinekit) ? "G22 ; retract\n" : "G10 ; retract\n";
} else if (! m_extrusion_axis.empty()) {
gcode << "G1 " << m_extrusion_axis << E_NUM(m_extruder->E())
<< " F" << XYZF_NUM(m_extruder->retract_speed() * 60.);
COMMENT(comment);
gcode << "\n";
auto w = GCodeFormatter::G1();
w.emit_e(m_extrusion_axis, m_extruder->E());
w.emit_f(m_extruder->retract_speed() * 60.);
w.emit_comment(this->config.gcode_comments, comment);
gcode = w.string();
}
}
if (FLAVOR_IS(gcfMakerWare))
gcode << "M103 ; extruder off\n";
return gcode.str();
gcode += "M103 ; extruder off\n";
return gcode;
}
std::string GCodeWriter::unretract()
{
std::ostringstream gcode;
std::string gcode;
if (FLAVOR_IS(gcfMakerWare))
gcode << "M101 ; extruder on\n";
gcode = "M101 ; extruder on\n";
double dE = m_extruder->unretract();
if (dE != 0) {
if (double dE = m_extruder->unretract(); dE != 0) {
if (this->config.use_firmware_retraction) {
if (FLAVOR_IS(gcfMachinekit))
gcode << "G23 ; unretract\n";
else
gcode << "G11 ; unretract\n";
gcode << this->reset_e();
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
gcode << "G1 " << m_extrusion_axis << E_NUM(m_extruder->E())
<< " F" << XYZF_NUM(m_extruder->deretract_speed() * 60.);
if (this->config.gcode_comments) gcode << " ; unretract";
gcode << "\n";
auto w = GCodeFormatter::G1();
w.emit_e(m_extrusion_axis, m_extruder->E());
w.emit_f(m_extruder->deretract_speed() * 60.);
w.emit_comment(this->config.gcode_comments, " ; unretract");
gcode += w.string();
}
}
return gcode.str();
return gcode;
}
/* If this method is called more than once before calling unlift(),
@ -649,4 +533,49 @@ std::string GCodeWriter::set_fan(unsigned int speed) const
return GCodeWriter::set_fan(this->config.gcode_flavor, this->config.gcode_comments, speed);
}
void GCodeFormatter::emit_axis(const char axis, const double v, size_t digits) {
assert(digits <= 6);
static constexpr const std::array<int, 7> pow_10{1, 10, 100, 1000, 10000, 100000, 1000000};
*ptr_err.ptr++ = ' '; *ptr_err.ptr++ = axis;
char *base_ptr = this->ptr_err.ptr;
auto v_int = int64_t(std::round(v * pow_10[digits]));
// Older stdlib on macOS doesn't support std::from_chars at all, so it is used boost::spirit::karma::generate instead of it.
// That is a little bit slower than std::to_chars but not much.
#ifdef __APPLE__
boost::spirit::karma::generate(this->ptr_err.ptr, boost::spirit::karma::int_generator<int64_t>(), v_int);
#else
// this->buf_end minus 1 because we need space for adding the extra decimal point.
this->ptr_err = std::to_chars(this->ptr_err.ptr, this->buf_end - 1, v_int);
#endif
size_t writen_digits = (this->ptr_err.ptr - base_ptr) - (v_int < 0 ? 1 : 0);
if (writen_digits < digits) {
// Number is smaller than 10^digits, so that we will pad it with zeros.
size_t remaining_digits = digits - writen_digits;
// Move all newly inserted chars by remaining_digits to allocate space for padding with zeros.
for (char *from_ptr = this->ptr_err.ptr - 1, *to_ptr = from_ptr + remaining_digits; from_ptr >= this->ptr_err.ptr - writen_digits; --to_ptr, --from_ptr)
*to_ptr = *from_ptr;
memset(this->ptr_err.ptr - writen_digits, '0', remaining_digits);
this->ptr_err.ptr += remaining_digits;
}
// Move all newly inserted chars by one to allocate space for a decimal point.
for (char *to_ptr = this->ptr_err.ptr, *from_ptr = to_ptr - 1; from_ptr >= this->ptr_err.ptr - digits; --to_ptr, --from_ptr)
*to_ptr = *from_ptr;
*(this->ptr_err.ptr - digits) = '.';
for (size_t i = 0; i < digits; ++i) {
if (*this->ptr_err.ptr != '0')
break;
this->ptr_err.ptr--;
}
if (*this->ptr_err.ptr == '.')
this->ptr_err.ptr--;
if ((this->ptr_err.ptr + 1) == base_ptr || *this->ptr_err.ptr == '-')
*(++this->ptr_err.ptr) = '0';
this->ptr_err.ptr++;
}
} // namespace Slic3r

View File

@ -3,6 +3,7 @@
#include "libslic3r.h"
#include <string>
#include <charconv>
#include "Extruder.hpp"
#include "Point.hpp"
#include "PrintConfig.hpp"
@ -93,6 +94,79 @@ private:
std::string _retract(double length, double restart_extra, const std::string &comment);
};
class GCodeFormatter {
private:
static constexpr const size_t buflen = 256;
char buf[buflen];
char *buf_end;
std::to_chars_result ptr_err;
GCodeFormatter() {
this->buf_end = buf + buflen;
this->ptr_err.ptr = this->buf;
}
public:
static constexpr const int XYZF_EXPORT_DIGITS = 3;
static constexpr const int E_EXPORT_DIGITS = 5;
static GCodeFormatter empty() {
return {};
}
static GCodeFormatter G1() {
GCodeFormatter out;
out.buf[0] = 'G';
out.buf[1] = '1';
out.ptr_err.ptr += 2;
return out;
}
void emit_axis(const char axis, const double v, size_t digits);
void emit_xy(const Vec2d &point) {
this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS);
this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS);
}
void emit_xyz(const Vec3d &point) {
this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS);
this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS);
this->emit_z(point.z());
}
void emit_z(const double z) {
this->emit_axis('Z', z, XYZF_EXPORT_DIGITS);
}
void emit_e(const std::string &axis, double v) {
if (! axis.empty()) {
// not gcfNoExtrusion
this->emit_axis(axis[0], v, E_EXPORT_DIGITS);
}
}
void emit_f(double speed) {
this->emit_axis('F', speed, XYZF_EXPORT_DIGITS);
}
void emit_string(const std::string &s) {
strncpy(ptr_err.ptr, s.c_str(), s.size());
ptr_err.ptr += s.size();
}
void emit_comment(bool allow_comments, const std::string &comment) {
if (allow_comments && ! comment.empty()) {
*ptr_err.ptr ++ = ' '; *ptr_err.ptr ++ = ';'; *ptr_err.ptr ++ = ' ';
this->emit_string(comment);
}
}
std::string string() {
*ptr_err.ptr ++ = '\n';
return std::string(this->buf, ptr_err.ptr - buf);
}
};
} /* namespace Slic3r */
#endif /* slic3r_GCodeWriter_hpp_ */