diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 2f8eb14c5..9e23112c5 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -349,8 +349,8 @@ if(APPLE) target_link_libraries(XS "-undefined dynamic_lookup") endif() target_link_libraries(XS libslic3r libslic3r_gui admesh clipper nowide polypartition poly2tri) -if(SLIC3R_DEBUG) - target_link_libraries(Shiny) +if(SLIC3R_PROFILE) + target_link_libraries(XS Shiny) endif() # Add the OpenGL and GLU libraries. @@ -393,7 +393,7 @@ endif () if (SLIC3R_PROFILE) message("Slic3r will be built with a Shiny invasive profiler") - target_compile_definitions(XS PRIVATE -DSLIC3R_PROFILE) + add_definitions(-DSLIC3R_PROFILE) endif () if (SLIC3R_HAS_BROKEN_CROAK) @@ -552,7 +552,7 @@ endif() add_executable(slic3r ${PROJECT_SOURCE_DIR}/src/slic3r.cpp) target_include_directories(XS PRIVATE src src/libslic3r) target_link_libraries(slic3r libslic3r libslic3r_gui admesh ${Boost_LIBRARIES} clipper ${EXPAT_LIBRARIES} ${GLEW_LIBRARIES} polypartition poly2tri ${TBB_LIBRARIES} ${wxWidgets_LIBRARIES}) -if(SLIC3R_DEBUG) +if(SLIC3R_PROFILE) target_link_libraries(Shiny) endif() if (APPLE) diff --git a/xs/src/Shiny/ShinyManager.c b/xs/src/Shiny/ShinyManager.c index 2cb0df4c2..6b2811851 100644 --- a/xs/src/Shiny/ShinyManager.c +++ b/xs/src/Shiny/ShinyManager.c @@ -90,8 +90,8 @@ ShinyNode* _ShinyManager_dummyNodeTable[] = { NULL }; /* primary hash function */ SHINY_INLINE uint32_t hash_value(void* a_pParent, void* a_pZone) { -// uint32_t a = (uint32_t) a_pParent + (uint32_t) a_pZone; - uint32_t a = *reinterpret_cast(&a_pParent) + *reinterpret_cast(&a_pZone); + uint32_t a = (uint32_t) a_pParent + (uint32_t) a_pZone; +// uint32_t a = *reinterpret_cast(&a_pParent) + *reinterpret_cast(&a_pZone); a = (a+0x7ed55d16) + (a<<12); a = (a^0xc761c23c) ^ (a>>19); diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 47bd6bc07..f713015f7 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -20,6 +20,8 @@ #include "SVG.hpp" +#include + #if 0 // Enable debugging and asserts, even in the release build. #define DEBUG @@ -348,6 +350,8 @@ std::vector>> GCode::collec void GCode::do_export(Print *print, const char *path) { + PROFILE_CLEAR(); + // Remove the old g-code if it exists. boost::nowide::remove(path); @@ -384,10 +388,16 @@ void GCode::do_export(Print *print, const char *path) throw std::runtime_error( std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' + "Is " + path_tmp + " locked?" + '\n'); + + // Write the profiler measurements to file + PROFILE_UPDATE(); + PROFILE_OUTPUT(debug_out_path("gcode-export-profile.txt").c_str()); } void GCode::_do_export(Print &print, FILE *file) { + PROFILE_FUNC(); + // resets time estimator m_time_estimator.reset(); @@ -1987,44 +1997,41 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill return gcode; } -void GCode::_write(FILE* file, const std::string& what) +void GCode::_write(FILE* file, const char *what, size_t size) { - if (!what.empty()) { + if (size > 0) { // writes string to file - fwrite(what.data(), 1, what.size(), file); + fwrite(what, 1, size, file); // updates time estimator and gcode lines vector - const char endLine = '\n'; - std::string::size_type beginPos = 0; - std::string::size_type endPos = what.find_first_of(endLine, beginPos); - while (endPos != std::string::npos) { - std::string line = what.substr(beginPos, endPos - beginPos + 1); - m_time_estimator.add_gcode_line(line); - beginPos = endPos + 1; - endPos = what.find_first_of(endLine, beginPos); - } + m_time_estimator.add_gcode_block(what); } } -void GCode::_writeln(FILE* file, const std::string& what) +void GCode::_writeln(FILE* file, const std::string &what) { - if (!what.empty()) { - if (what.back() != '\n') - _write_format(file, "%s\n", what.c_str()); - else - _write(file, what); - } + if (! what.empty()) + _write(file, (what.back() == '\n') ? what : (what + '\n')); } void GCode::_write_format(FILE* file, const char* format, ...) { - char buffer[1024]; va_list args; va_start(args, format); - int res = ::vsnprintf(buffer, 1024, format, args); + int buflen = +#ifdef _MSC_VER + _vscprintf(format, args); +#else + vsnprintf(nullptr, 0, format, args); +#endif + char buffer[1024]; + bool buffer_dynamic = buflen > 1024; + char *bufptr = buffer_dynamic ? (char*)malloc(buflen) : buffer; + int res = ::vsnprintf(bufptr, buflen, format, args); + if (res >= 0 && bufptr[0] != 0) + _write(file, bufptr, buflen); va_end(args); - - if (res >= 0) - _writeln(file, buffer); + if (buffer_dynamic) + free(bufptr); } std::string GCode::_extrude(const ExtrusionPath &path, std::string description, double speed) diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index cb8b0027a..5c622ec2d 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -278,7 +278,8 @@ protected: GCodeTimeEstimator m_time_estimator; // Write a string into a file. - void _write(FILE* file, const std::string& what); + void _write(FILE* file, const std::string& what) { this->_write(file, what.c_str(), what.size()); } + void _write(FILE* file, const char *what, size_t size); // Write a string into a file. // Add a newline, if the string does not end with a newline already. diff --git a/xs/src/libslic3r/GCode/SpiralVase.cpp b/xs/src/libslic3r/GCode/SpiralVase.cpp index fd300067f..892d3b4cc 100644 --- a/xs/src/libslic3r/GCode/SpiralVase.cpp +++ b/xs/src/libslic3r/GCode/SpiralVase.cpp @@ -4,17 +4,7 @@ namespace Slic3r { -std::string -_format_z(float z) -{ - std::ostringstream ss; - ss << std::fixed << std::setprecision(3) - << z; - return ss.str(); -} - -std::string -SpiralVase::process_layer(const std::string &gcode) +std::string SpiralVase::process_layer(const std::string &gcode) { /* This post-processor relies on several assumptions: - all layers are processed through it, including those that are not supposed @@ -38,16 +28,17 @@ SpiralVase::process_layer(const std::string &gcode) bool set_z = false; { + //FIXME Performance warning: This copies the GCodeConfig of the reader. GCodeReader r = this->_reader; // clone r.parse(gcode, [&total_layer_length, &layer_height, &z, &set_z] - (GCodeReader &, const GCodeReader::GCodeLine &line) { - if (line.cmd == "G1") { - if (line.extruding()) { - total_layer_length += line.dist_XY(); - } else if (line.has('Z')) { - layer_height += line.dist_Z(); + (GCodeReader &reader, const GCodeReader::GCodeLine &line) { + if (line.cmd_is("G1")) { + if (line.extruding(reader)) { + total_layer_length += line.dist_XY(reader); + } else if (line.has(Z)) { + layer_height += line.dist_Z(reader); if (!set_z) { - z = line.new_Z(); + z = line.new_Z(reader); set_z = true; } } @@ -60,22 +51,22 @@ SpiralVase::process_layer(const std::string &gcode) std::string new_gcode; this->_reader.parse(gcode, [&new_gcode, &z, &layer_height, &total_layer_length] - (GCodeReader &, GCodeReader::GCodeLine line) { - if (line.cmd == "G1") { - if (line.has('Z')) { + (GCodeReader &reader, GCodeReader::GCodeLine line) { + if (line.cmd_is("G1")) { + if (line.has_z()) { // If this is the initial Z move of the layer, replace it with a // (redundant) move to the last Z of previous layer. - line.set('Z', _format_z(z)); - new_gcode += line.raw + '\n'; + line.set(reader, Z, z); + new_gcode += line.raw() + '\n'; return; } else { - float dist_XY = line.dist_XY(); + float dist_XY = line.dist_XY(reader); if (dist_XY > 0) { // horizontal move - if (line.extruding()) { + if (line.extruding(reader)) { z += dist_XY * layer_height / total_layer_length; - line.set('Z', _format_z(z)); - new_gcode += line.raw + '\n'; + line.set(reader, Z, z); + new_gcode += line.raw() + '\n'; } return; @@ -87,7 +78,7 @@ SpiralVase::process_layer(const std::string &gcode) } } } - new_gcode += line.raw + '\n'; + new_gcode += line.raw() + '\n'; }); return new_gcode; diff --git a/xs/src/libslic3r/GCode/SpiralVase.hpp b/xs/src/libslic3r/GCode/SpiralVase.hpp index 234642524..60aa668d8 100644 --- a/xs/src/libslic3r/GCode/SpiralVase.hpp +++ b/xs/src/libslic3r/GCode/SpiralVase.hpp @@ -13,7 +13,7 @@ class SpiralVase { SpiralVase(const PrintConfig &config) : enable(false), _config(&config) { - this->_reader.Z = this->_config->z_offset; + this->_reader.z() = this->_config->z_offset; this->_reader.apply_config(*this->_config); }; std::string process_layer(const std::string &gcode); diff --git a/xs/src/libslic3r/GCodeReader.cpp b/xs/src/libslic3r/GCodeReader.cpp index 453cc9f94..86009fb0f 100644 --- a/xs/src/libslic3r/GCodeReader.cpp +++ b/xs/src/libslic3r/GCodeReader.cpp @@ -4,6 +4,8 @@ #include #include +#include + namespace Slic3r { void GCodeReader::apply_config(const GCodeConfig &config) @@ -26,58 +28,121 @@ void GCodeReader::parse(const std::string &gcode, callback_t callback) this->parse_line(line, callback); } -void GCodeReader::parse_line(std::string line, callback_t callback) +static inline bool is_whitespace(char c) { - GCodeLine gline(this); - gline.raw = line; - if (this->verbose) - std::cout << line << std::endl; - - // strip comment - { - size_t pos = line.find(';'); - if (pos != std::string::npos) { - gline.comment = line.substr(pos+1); - line.erase(pos); - } - } + return c == ' ' || c == '\t'; +} + +static inline bool is_end_of_line(char c) +{ + return c == '\r' || c == '\n' || c == 0; +} + +static inline bool is_end_of_gcode_line(char c) +{ + return c == ';' || is_end_of_line(c); +} + +static inline bool is_end_of_word(char c) +{ + return is_whitespace(c) || is_end_of_gcode_line(c); +} + +static inline const char* skip_whitespaces(const char *c) +{ + for (; is_whitespace(*c); ++ c); + return c; +} + +static inline const char* skip_word(const char *c) +{ + for (; ! is_end_of_word(*c); ++ c); + return c; +} + +const char* GCodeReader::parse_line_internal(const char *ptr, GCodeLine &gline, std::pair &command) +{ + PROFILE_FUNC(); // command and args + const char *c = ptr; { - std::vector args; - boost::split(args, line, boost::is_any_of(" ")); - - // first one is cmd - gline.cmd = args.front(); - args.erase(args.begin()); - - for (std::string &arg : args) { - if (arg.size() < 2) continue; - gline.args.insert(std::make_pair(arg[0], arg.substr(1))); + PROFILE_BLOCK(command_and_args); + // Skip the whitespaces. + command.first = skip_whitespaces(c); + // Skip the command. + c = command.second = skip_word(command.first); + // Up to the end of line or comment. + while (! is_end_of_gcode_line(*c)) { + // Skip whitespaces. + c = skip_whitespaces(c); + if (is_end_of_gcode_line(*c)) + break; + // Check the name of the axis. + Axis axis = NUM_AXES; + switch (*c) { + case 'X': axis = X; break; + case 'Y': axis = Y; break; + case 'Z': axis = Z; break; + case 'F': axis = F; break; + default: + if (*c == m_extrusion_axis) + axis = E; + break; + } + if (axis != NUM_AXES) { + // Try to parse the numeric value. + char *pend = nullptr; + double v = strtod(++ c, &pend); + if (pend != nullptr && is_end_of_word(*pend)) { + // The axis value has been parsed correctly. + gline.m_axis[int(axis)] = float(v); + gline.m_mask |= 1 << int(axis); + c = pend; + } else + // Skip the rest of the word. + c = skip_word(c); + } else + // Skip the rest of the word. + c = skip_word(c); } } - // convert extrusion axis - if (m_extrusion_axis != 'E') { - const auto it = gline.args.find(m_extrusion_axis); - if (it != gline.args.end()) { - std::swap(gline.args['E'], it->second); - gline.args.erase(it); - } + if (gline.has(E) && m_config.use_relative_e_distances) + m_position[E] = 0; + + // Skip the rest of the line. + for (; ! is_end_of_line(*c); ++ c); + + // Copy the raw string including the comment, without the trailing newlines. + if (c > ptr) { + PROFILE_BLOCK(copy_raw_string); + gline.m_raw.assign(ptr, c); } - - if (gline.has('E') && m_config.use_relative_e_distances) - this->E = 0; - - if (callback) callback(*this, gline); - - // update coordinates - if (gline.cmd == "G0" || gline.cmd == "G1" || gline.cmd == "G92") { - this->X = gline.new_X(); - this->Y = gline.new_Y(); - this->Z = gline.new_Z(); - this->E = gline.new_E(); - this->F = gline.new_F(); + + // Skip the trailing newlines. + if (*c == '\r') + ++ c; + if (*c == '\n') + ++ c; + + if (m_verbose) + std::cout << gline.m_raw << std::endl; + + return c; +} + +void GCodeReader::update_coordinates(GCodeLine &gline, std::pair &command) +{ + PROFILE_FUNC(); + if (*command.first == 'G') { + int cmd_len = int(command.second - command.first); + if ((cmd_len == 2 && (command.first[1] == '0' || command.first[1] == '1')) || + (cmd_len == 3 && command.first[1] == '9' && command.first[2] == '2')) { + for (size_t i = 0; i < NUM_AXES; ++ i) + if (gline.has(Axis(i))) + this->m_position[i] = gline.value(Axis(i)); + } } } @@ -89,22 +154,64 @@ void GCodeReader::parse_file(const std::string &file, callback_t callback) this->parse_line(line, callback); } -void GCodeReader::GCodeLine::set(char arg, std::string value) +bool GCodeReader::GCodeLine::has_value(char axis, float &value) const { - const std::string space(" "); - if (this->has(arg)) { - size_t pos = this->raw.find(space + arg)+2; - size_t end = this->raw.find(' ', pos+1); - this->raw = this->raw.replace(pos, end-pos, value); - } else { - size_t pos = this->raw.find(' '); - if (pos == std::string::npos) { - this->raw += space + arg + value; - } else { - this->raw = this->raw.replace(pos, 0, space + arg + value); + const char *c = m_raw.c_str(); + // Skip the whitespaces. + c = skip_whitespaces(c); + // Skip the command. + c = skip_word(c); + // Up to the end of line or comment. + while (! is_end_of_gcode_line(*c)) { + // Skip whitespaces. + c = skip_whitespaces(c); + if (is_end_of_gcode_line(*c)) + break; + // Check the name of the axis. + if (*c == axis) { + // Try to parse the numeric value. + char *pend = nullptr; + double v = strtod(++ c, &pend); + if (pend != nullptr && is_end_of_word(*pend)) { + // The axis value has been parsed correctly. + value = float(v); + return true; + } } + // Skip the rest of the word. + c = skip_word(c); } - this->args[arg] = value; + return false; +} + +void GCodeReader::GCodeLine::set(const GCodeReader &reader, const Axis axis, const float new_value, const int decimal_digits) +{ + std::ostringstream ss; + ss << std::fixed << std::setprecision(decimal_digits) << new_value; + + char match[3] = " X"; + if (int(axis) < 3) + match[1] += int(axis); + else if (axis == F) + match[1] = 'F'; + else { + assert(axis == E); + match[1] = reader.extrusion_axis(); + } + + if (this->has(axis)) { + size_t pos = m_raw.find(match)+2; + size_t end = m_raw.find(' ', pos+1); + m_raw = m_raw.replace(pos, end-pos, ss.str()); + } else { + size_t pos = m_raw.find(' '); + if (pos == std::string::npos) + m_raw += std::string(match) + ss.str(); + else + m_raw = m_raw.replace(pos, 0, std::string(match) + ss.str()); + } + m_axis[axis] = new_value; + m_mask |= 1 << int(axis); } } diff --git a/xs/src/libslic3r/GCodeReader.hpp b/xs/src/libslic3r/GCodeReader.hpp index 1792d6cde..4a7825520 100644 --- a/xs/src/libslic3r/GCodeReader.hpp +++ b/xs/src/libslic3r/GCodeReader.hpp @@ -11,54 +11,104 @@ namespace Slic3r { class GCodeReader { -public: +public: class GCodeLine { public: - GCodeReader* reader; - std::string raw; - std::string cmd; - std::string comment; - std::map args; - - GCodeLine(GCodeReader* _reader) : reader(_reader) {}; - - bool has(char arg) const { return this->args.count(arg) > 0; }; - float get_float(char arg) const { return float(atof(this->args.at(arg).c_str())); }; - float new_X() const { return this->has('X') ? float(atof(this->args.at('X').c_str())) : this->reader->X; }; - float new_Y() const { return this->has('Y') ? float(atof(this->args.at('Y').c_str())) : this->reader->Y; }; - float new_Z() const { return this->has('Z') ? float(atof(this->args.at('Z').c_str())) : this->reader->Z; }; - float new_E() const { return this->has('E') ? float(atof(this->args.at('E').c_str())) : this->reader->E; }; - float new_F() const { return this->has('F') ? float(atof(this->args.at('F').c_str())) : this->reader->F; }; - float dist_X() const { return this->new_X() - this->reader->X; }; - float dist_Y() const { return this->new_Y() - this->reader->Y; }; - float dist_Z() const { return this->new_Z() - this->reader->Z; }; - float dist_E() const { return this->new_E() - this->reader->E; }; - float dist_XY() const { - float x = this->dist_X(); - float y = this->dist_Y(); + GCodeLine() { reset(); } + void reset() { m_mask = 0; memset(m_axis, 0, sizeof(m_axis)); m_raw.clear(); } + + const std::string& raw() const { return m_raw; } + const std::string cmd() const + { size_t pos = m_raw.find_first_of(" \t\n;"); return (pos == std::string::npos) ? m_raw : m_raw.substr(0, pos); } + const std::string comment() const + { size_t pos = m_raw.find(';'); return (pos == std::string::npos) ? "" : m_raw.substr(pos + 1); } + + bool has(Axis axis) const { return (m_mask & (1 << int(axis))) != 0; } + float value(Axis axis) const { return m_axis[axis]; } + bool has_value(char axis, float &value) const; + float new_Z(const GCodeReader &reader) const { return this->has(Z) ? this->z() : reader.z(); } + float new_E(const GCodeReader &reader) const { return this->has(E) ? this->e() : reader.e(); } + float new_F(const GCodeReader &reader) const { return this->has(F) ? this->f() : reader.f(); } + float dist_X(const GCodeReader &reader) const { return this->has(X) ? (this->x() - reader.x()) : 0; } + float dist_Y(const GCodeReader &reader) const { return this->has(Y) ? (this->y() - reader.y()) : 0; } + float dist_Z(const GCodeReader &reader) const { return this->has(Z) ? (this->z() - reader.z()) : 0; } + float dist_E(const GCodeReader &reader) const { return this->has(E) ? (this->e() - reader.e()) : 0; } + float dist_XY(const GCodeReader &reader) const { + float x = this->has(X) ? (this->x() - reader.x()) : 0; + float y = this->has(Y) ? (this->y() - reader.y()) : 0; return sqrt(x*x + y*y); - }; - bool extruding() const { return this->cmd == "G1" && this->dist_E() > 0; }; - bool retracting() const { return this->cmd == "G1" && this->dist_E() < 0; }; - bool travel() const { return this->cmd == "G1" && ! this->has('E'); }; - void set(char arg, std::string value); + } + bool cmd_is(const char *cmd_test) const { + int len = strlen(cmd_test); + if (strncmp(m_raw.c_str(), cmd_test, len)) + return false; + char c = m_raw.c_str()[len]; + return c == 0 || c == ' ' || c == '\t' || c == ';'; + } + bool extruding(const GCodeReader &reader) const { return this->cmd_is("G1") && this->dist_E(reader) > 0; } + bool retracting(const GCodeReader &reader) const { return this->cmd_is("G1") && this->dist_E(reader) < 0; } + bool travel() const { return this->cmd_is("G1") && ! this->has(E); } + void set(const GCodeReader &reader, const Axis axis, const float new_value, const int decimal_digits = 3); + + bool has_x() const { return this->has(X); } + bool has_y() const { return this->has(Y); } + bool has_z() const { return this->has(Z); } + bool has_e() const { return this->has(E); } + bool has_f() const { return this->has(F); } + float x() const { return m_axis[X]; } + float y() const { return m_axis[Y]; } + float z() const { return m_axis[Z]; } + float e() const { return m_axis[E]; } + float f() const { return m_axis[F]; } + + private: + std::string m_raw; + float m_axis[NUM_AXES]; + uint32_t m_mask; + friend class GCodeReader; }; + typedef std::function callback_t; - float X, Y, Z, E, F; - bool verbose; - callback_t callback; - - GCodeReader() : X(0), Y(0), Z(0), E(0), F(0), verbose(false), m_extrusion_axis('E') {}; + GCodeReader() : m_verbose(false), m_extrusion_axis('E') { memset(m_position, 0, sizeof(m_position)); } void apply_config(const GCodeConfig &config); void apply_config(const DynamicPrintConfig &config); void parse(const std::string &gcode, callback_t callback); - void parse_line(std::string line, callback_t callback); + template + const char* parse_line(const char *ptr, GCodeLine &gline, Callback &callback) + { + std::pair cmd; + const char *end = parse_line_internal(ptr, gline, cmd); + callback(*this, gline); + update_coordinates(gline, cmd); + return end; + } + template + void parse_line(const std::string &line, Callback &callback) + { GCodeLine gline; this->parse_line(line.c_str(), gline, callback); } void parse_file(const std::string &file, callback_t callback); - + + float& x() { return m_position[X]; } + float x() const { return m_position[X]; } + float& y() { return m_position[Y]; } + float y() const { return m_position[Y]; } + float& z() { return m_position[Z]; } + float z() const { return m_position[Z]; } + float& e() { return m_position[E]; } + float e() const { return m_position[E]; } + float& f() { return m_position[F]; } + float f() const { return m_position[F]; } + + char extrusion_axis() const { return m_extrusion_axis; } + private: + const char* parse_line_internal(const char *ptr, GCodeLine &gline, std::pair &command); + void update_coordinates(GCodeLine &gline, std::pair &command); + GCodeConfig m_config; - char m_extrusion_axis; + char m_extrusion_axis; + float m_position[NUM_AXES]; + bool m_verbose; }; } /* namespace Slic3r */ diff --git a/xs/src/libslic3r/GCodeTimeEstimator.cpp b/xs/src/libslic3r/GCodeTimeEstimator.cpp index 43b331676..31ee30cc1 100644 --- a/xs/src/libslic3r/GCodeTimeEstimator.cpp +++ b/xs/src/libslic3r/GCodeTimeEstimator.cpp @@ -4,7 +4,6 @@ #include -static const std::string AXIS_STR = "XYZE"; static const float MMMIN_TO_MMSEC = 1.0f / 60.0f; static const float MILLISEC_TO_SEC = 0.001f; static const float INCHES_TO_MM = 25.4f; @@ -159,7 +158,9 @@ namespace Slic3r { { for (const std::string& line : gcode_lines) { - _parser.parse_line(line, boost::bind(&GCodeTimeEstimator::_process_gcode_line, this, _1, _2)); + _parser.parse_line(line, + [this](GCodeReader &reader, const GCodeReader::GCodeLine &line) + { this->_process_gcode_line(reader, line); }); } _calculate_time(); reset(); @@ -168,7 +169,21 @@ namespace Slic3r { void GCodeTimeEstimator::add_gcode_line(const std::string& gcode_line) { PROFILE_FUNC(); - _parser.parse_line(gcode_line, boost::bind(&GCodeTimeEstimator::_process_gcode_line, this, _1, _2)); + _parser.parse_line(gcode_line, + [this](GCodeReader &reader, const GCodeReader::GCodeLine &line) + { this->_process_gcode_line(reader, line); }); + } + + void GCodeTimeEstimator::add_gcode_block(const char *ptr) + { + PROFILE_FUNC(); + GCodeReader::GCodeLine gline; + for (; *ptr != 0;) { + gline.reset(); + ptr = _parser.parse_line(ptr, gline, + [this](GCodeReader &reader, const GCodeReader::GCodeLine &line) + { this->_process_gcode_line(reader, line); }); + } } void GCodeTimeEstimator::calculate_time() @@ -406,13 +421,14 @@ namespace Slic3r { void GCodeTimeEstimator::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLine& line) { PROFILE_FUNC(); - if (line.cmd.length() > 1) + std::string cmd = line.cmd(); + if (cmd.length() > 1) { - switch (::toupper(line.cmd[0])) + switch (::toupper(cmd[0])) { case 'G': { - switch (::atoi(&line.cmd[1])) + switch (::atoi(&cmd[1])) { case 1: // Move { @@ -460,7 +476,7 @@ namespace Slic3r { } case 'M': { - switch (::atoi(&line.cmd[1])) + switch (::atoi(&cmd[1])) { case 82: // Set extruder to absolute mode { @@ -514,9 +530,9 @@ namespace Slic3r { float axis_absolute_position_from_G1_line(GCodeTimeEstimator::EAxis axis, const GCodeReader::GCodeLine& lineG1, GCodeTimeEstimator::EUnits units, GCodeTimeEstimator::EPositioningType type, float current_absolute_position) { float lengthsScaleFactor = (units == GCodeTimeEstimator::Inches) ? INCHES_TO_MM : 1.0f; - if (lineG1.has(AXIS_STR[axis])) + if (lineG1.has(Slic3r::Axis(axis))) { - float ret = lineG1.get_float(AXIS_STR[axis]) * lengthsScaleFactor; + float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor; return (type == GCodeTimeEstimator::Absolute) ? ret : current_absolute_position + ret; } else @@ -534,8 +550,8 @@ namespace Slic3r { } // updates feedrate from line, if present - if (line.has('F')) - set_feedrate(std::max(line.get_float('F') * MMMIN_TO_MMSEC, get_minimum_feedrate())); + if (line.has_f()) + set_feedrate(std::max(line.f() * MMMIN_TO_MMSEC, get_minimum_feedrate())); // fills block data Block block; @@ -682,15 +698,16 @@ namespace Slic3r { } // adds block to blocks list - _blocks.push_back(block); + _blocks.emplace_back(block); } void GCodeTimeEstimator::_processG4(const GCodeReader::GCodeLine& line) { EDialect dialect = get_dialect(); - if (line.has('P')) - add_additional_time(line.get_float('P') * MILLISEC_TO_SEC); + float value; + if (line.has_value('P', value)) + add_additional_time(value * MILLISEC_TO_SEC); // see: http://reprap.org/wiki/G-code#G4:_Dwell if ((dialect == Repetier) || @@ -698,8 +715,8 @@ namespace Slic3r { (dialect == Smoothieware) || (dialect == RepRapFirmware)) { - if (line.has('S')) - add_additional_time(line.get_float('S')); + if (line.has_value('S', value)) + add_additional_time(value); } } @@ -745,27 +762,27 @@ namespace Slic3r { float lengthsScaleFactor = (get_units() == Inches) ? INCHES_TO_MM : 1.0f; bool anyFound = false; - if (line.has('X')) + if (line.has_x()) { - set_axis_position(X, line.get_float('X') * lengthsScaleFactor); + set_axis_position(X, line.x() * lengthsScaleFactor); anyFound = true; } - if (line.has('Y')) + if (line.has_y()) { - set_axis_position(Y, line.get_float('Y') * lengthsScaleFactor); + set_axis_position(Y, line.y() * lengthsScaleFactor); anyFound = true; } - if (line.has('Z')) + if (line.has_z()) { - set_axis_position(Z, line.get_float('Z') * lengthsScaleFactor); + set_axis_position(Z, line.z() * lengthsScaleFactor); anyFound = true; } - if (line.has('E')) + if (line.has_e()) { - set_axis_position(E, line.get_float('E') * lengthsScaleFactor); + set_axis_position(E, line.e() * lengthsScaleFactor); anyFound = true; } @@ -790,17 +807,17 @@ namespace Slic3r { // see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration float factor = ((dialect != RepRapFirmware) && (get_units() == GCodeTimeEstimator::Inches)) ? INCHES_TO_MM : 1.0f; - if (line.has('X')) - set_axis_max_acceleration(X, line.get_float('X') * factor); + if (line.has_x()) + set_axis_max_acceleration(X, line.x() * factor); - if (line.has('Y')) - set_axis_max_acceleration(Y, line.get_float('Y') * factor); + if (line.has_y()) + set_axis_max_acceleration(Y, line.y() * factor); - if (line.has('Z')) - set_axis_max_acceleration(Z, line.get_float('Z') * factor); + if (line.has_z()) + set_axis_max_acceleration(Z, line.z() * factor); - if (line.has('E')) - set_axis_max_acceleration(E, line.get_float('E') * factor); + if (line.has_e()) + set_axis_max_acceleration(E, line.e() * factor); } void GCodeTimeEstimator::_processM203(const GCodeReader::GCodeLine& line) @@ -814,66 +831,68 @@ namespace Slic3r { // see http://reprap.org/wiki/G-code#M203:_Set_maximum_feedrate float factor = (dialect == Marlin) ? 1.0f : MMMIN_TO_MMSEC; - if (line.has('X')) - set_axis_max_feedrate(X, line.get_float('X') * factor); + if (line.has_x()) + set_axis_max_feedrate(X, line.x() * factor); - if (line.has('Y')) - set_axis_max_feedrate(Y, line.get_float('Y') * factor); + if (line.has_y()) + set_axis_max_feedrate(Y, line.y() * factor); - if (line.has('Z')) - set_axis_max_feedrate(Z, line.get_float('Z') * factor); + if (line.has_z()) + set_axis_max_feedrate(Z, line.z() * factor); - if (line.has('E')) - set_axis_max_feedrate(E, line.get_float('E') * factor); + if (line.has_e()) + set_axis_max_feedrate(E, line.e() * factor); } void GCodeTimeEstimator::_processM204(const GCodeReader::GCodeLine& line) { - if (line.has('S')) - set_acceleration(line.get_float('S')); + float value; + if (line.has_value('S', value)) + set_acceleration(value); - if (line.has('T')) - set_retract_acceleration(line.get_float('T')); + if (line.has_value('T', value)) + set_retract_acceleration(value); } void GCodeTimeEstimator::_processM205(const GCodeReader::GCodeLine& line) { - if (line.has('X')) + if (line.has_x()) { - float max_jerk = line.get_float('X'); + float max_jerk = line.x(); set_axis_max_jerk(X, max_jerk); set_axis_max_jerk(Y, max_jerk); } - if (line.has('Y')) - set_axis_max_jerk(Y, line.get_float('Y')); + if (line.has_y()) + set_axis_max_jerk(Y, line.y()); - if (line.has('Z')) - set_axis_max_jerk(Z, line.get_float('Z')); + if (line.has_z()) + set_axis_max_jerk(Z, line.z()); - if (line.has('E')) - set_axis_max_jerk(E, line.get_float('E')); + if (line.has_e()) + set_axis_max_jerk(E, line.e()); - if (line.has('S')) - set_minimum_feedrate(line.get_float('S')); + float value; + if (line.has_value('S', value)) + set_minimum_feedrate(value); - if (line.has('T')) - set_minimum_travel_feedrate(line.get_float('T')); + if (line.has_value('T', value)) + set_minimum_travel_feedrate(value); } void GCodeTimeEstimator::_processM566(const GCodeReader::GCodeLine& line) { - if (line.has('X')) - set_axis_max_jerk(X, line.get_float('X') * MMMIN_TO_MMSEC); + if (line.has_x()) + set_axis_max_jerk(X, line.x() * MMMIN_TO_MMSEC); - if (line.has('Y')) - set_axis_max_jerk(Y, line.get_float('Y') * MMMIN_TO_MMSEC); + if (line.has_y()) + set_axis_max_jerk(Y, line.y() * MMMIN_TO_MMSEC); - if (line.has('Z')) - set_axis_max_jerk(Z, line.get_float('Z') * MMMIN_TO_MMSEC); + if (line.has_z()) + set_axis_max_jerk(Z, line.z() * MMMIN_TO_MMSEC); - if (line.has('E')) - set_axis_max_jerk(E, line.get_float('E') * MMMIN_TO_MMSEC); + if (line.has_e()) + set_axis_max_jerk(E, line.e() * MMMIN_TO_MMSEC); } void GCodeTimeEstimator::_forward_pass() diff --git a/xs/src/libslic3r/GCodeTimeEstimator.hpp b/xs/src/libslic3r/GCodeTimeEstimator.hpp index 894d00ef3..84c6de5fc 100644 --- a/xs/src/libslic3r/GCodeTimeEstimator.hpp +++ b/xs/src/libslic3r/GCodeTimeEstimator.hpp @@ -183,6 +183,9 @@ namespace Slic3r { // Adds the given gcode line void add_gcode_line(const std::string& gcode_line); + void add_gcode_block(const char *ptr); + void add_gcode_block(const std::string &str) { this->add_gcode_block(str.c_str()); } + // Calculates the time estimate from the gcode lines added using add_gcode_line() void calculate_time(); diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index 1a02605e1..7c694b05e 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -102,7 +102,7 @@ inline std::string debug_out_path(const char *name, ...) namespace Slic3r { -enum Axis { X=0, Y, Z }; +enum Axis { X=0, Y, Z, E, F, NUM_AXES }; template inline void append_to(std::vector &dst, const std::vector &src)