diff --git a/resources/profiles/Jubilee.ini b/resources/profiles/Jubilee.ini index 477b6a1e3..d9ab64bd7 100644 --- a/resources/profiles/Jubilee.ini +++ b/resources/profiles/Jubilee.ini @@ -498,6 +498,7 @@ use_volumetric_e = 0 variable_layer_height = 0 wipe = 0,0 z_offset = 0 +before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0 default_filament_profile = "Generic PLA @Jubilee; Generic PLA @Jubilee" default_print_profile = 0.2mm V6, aesthetic @Jubilee diff --git a/resources/profiles/RatRig.ini b/resources/profiles/RatRig.ini index ff18030d3..69379e781 100644 --- a/resources/profiles/RatRig.ini +++ b/resources/profiles/RatRig.ini @@ -384,7 +384,7 @@ top_solid_infill_speed = 60% # Common printer preset [printer:*common*] -before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;[layer_z]\n;{if layer_num == 2 }SET_FILAMENT_SENSOR SENSOR=my_sensor ENABLE=1{endif}\n\n +before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;[layer_z]\nG92 E0\n;{if layer_num == 2 }SET_FILAMENT_SENSOR SENSOR=my_sensor ENABLE=1{endif}\n\n between_objects_gcode = color_change_gcode = M600 cooling_tube_length = 5 @@ -453,7 +453,7 @@ printer_variant = 0.4 [printer:*vminion-klipper*] inherits = *common* -before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;[layer_z]\n;{if layer_num == 2 }SET_FILAMENT_SENSOR SENSOR=my_sensor ENABLE=1{endif}\n\n +before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;[layer_z]\nG92 E0\n;{if layer_num == 2 }SET_FILAMENT_SENSOR SENSOR=my_sensor ENABLE=1{endif}\n\n default_filament_profile = "Generic PLA @RatRig" default_print_profile = 0.20mm NORMAL V-Minion @RatRig deretract_speed = 40 diff --git a/resources/profiles/Voron.ini b/resources/profiles/Voron.ini index fb024870a..cd0fbd2d9 100644 --- a/resources/profiles/Voron.ini +++ b/resources/profiles/Voron.ini @@ -168,7 +168,7 @@ default_print_profile = 0.4mm 1.2nozzle # Common printer preset [printer:*common*] printer_technology = FFF -before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;[layer_z]\n\n +before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;[layer_z]\nG92 E0\n between_objects_gcode = deretract_speed = 25 end_gcode = print_end ;end script from macro diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index e8078931b..dc47b382d 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -22,6 +22,7 @@ #include #include #include +#include // Mark string for localization and translate. #define L(s) Slic3r::I18N::translate(s) @@ -438,7 +439,8 @@ static inline bool sequential_print_vertical_clearance_valid(const Print &print) return it == print_instances_ordered.end() || (*it)->print_object->height() <= scale_(print.config().extruder_clearance_height.value); } - +// Matches "G92 E0" with various forms of writing the zero and with an optional comment. +boost::regex regex_g92e0 { "^[ \\t]*[gG]92[ \\t]*[eE](0(\\.0*)?|\\.0+)[ \\t]*(;.*)?$" }; // Precondition: Print::validate() requires the Print::apply() to be called its invocation. std::string Print::validate(std::string* warning) const @@ -652,6 +654,18 @@ std::string Print::validate(std::string* warning) const return err_msg; } } + { + bool before_layer_gcode_resets_extruder = boost::regex_search(m_config.before_layer_gcode.value, regex_g92e0); + bool layer_gcode_resets_extruder = boost::regex_search(m_config.layer_gcode.value, regex_g92e0); + if (m_config.use_relative_e_distances) { + // See GH issues #6336 #5073 + if (! before_layer_gcode_resets_extruder && ! layer_gcode_resets_extruder) + return L("Relative extruder addressing requires resetting the extruder position at each layer to prevent loss of floating point accuracy. Add \"G92 E0\" to layer_gcode."); + } else if (before_layer_gcode_resets_extruder) + return L("\"G92 E0\" was found in before_layer_gcode, which is incompatible with absolute extruder addressing."); + else if (layer_gcode_resets_extruder) + return L("\"G92 E0\" was found in layer_gcode, which is incompatible with absolute extruder addressing."); + } return std::string(); } diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 2562d1913..10790ef49 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -78,6 +78,7 @@ extern std::string normalize_utf8_nfc(const char *src); // Returns next utf8 sequence length. =number of bytes in string, that creates together one utf-8 character. // Starting at pos. ASCII characters returns 1. Works also if pos is in the middle of the sequence. extern size_t get_utf8_sequence_length(const std::string& text, size_t pos = 0); +extern size_t get_utf8_sequence_length(const char *seq, size_t size); // Safely rename a file even if the target exists. // On Windows, the file explorer (or anti-virus or whatever else) often locks the file diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index c74e57e99..e00b6e71c 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -866,8 +866,13 @@ std::string normalize_utf8_nfc(const char *src) size_t get_utf8_sequence_length(const std::string& text, size_t pos) { assert(pos < text.size()); + return get_utf8_sequence_length(text.c_str() + pos, text.size() - pos); +} + +size_t get_utf8_sequence_length(const char *seq, size_t size) +{ size_t length = 0; - unsigned char c = text[pos]; + unsigned char c = seq[0]; if (c < 0x80) { // 0x00-0x7F // is ASCII letter length++; @@ -876,8 +881,8 @@ size_t get_utf8_sequence_length(const std::string& text, size_t pos) // pos is in the middle of a utf-8 sequence. Add the utf-8 trailer bytes. else if (c < 0xC0) { // 0x80-0xBF length++; - while (pos + length < text.size()) { - c = text[pos + length]; + while (length < size) { + c = seq[length]; if (c < 0x80 || c >= 0xC0) { break; // prevent overrun } @@ -888,36 +893,36 @@ size_t get_utf8_sequence_length(const std::string& text, size_t pos) // The number of one bits above the topmost zero bit indicates the number of bytes (including this one) in the whole sequence. else if (c < 0xE0) { // 0xC0-0xDF // add a utf-8 sequence (2 bytes) - if (pos + 2 > text.size()) { - return text.size() - pos; // prevent overrun + if (2 > size) { + return size; // prevent overrun } length += 2; } else if (c < 0xF0) { // 0xE0-0xEF // add a utf-8 sequence (3 bytes) - if (pos + 3 > text.size()) { - return text.size() - pos; // prevent overrun + if (3 > size) { + return size; // prevent overrun } length += 3; } else if (c < 0xF8) { // 0xF0-0xF7 // add a utf-8 sequence (4 bytes) - if (pos + 4 > text.size()) { - return text.size() - pos; // prevent overrun + if (4 > size) { + return size; // prevent overrun } length += 4; } else if (c < 0xFC) { // 0xF8-0xFB // add a utf-8 sequence (5 bytes) - if (pos + 5 > text.size()) { - return text.size() - pos; // prevent overrun + if (5 > size) { + return size; // prevent overrun } length += 5; } else if (c < 0xFE) { // 0xFC-0xFD // add a utf-8 sequence (6 bytes) - if (pos + 6 > text.size()) { - return text.size() - pos; // prevent overrun + if (6 > size) { + return size; // prevent overrun } length += 6; } diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index b55ba0d75..a33742d14 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -871,8 +871,8 @@ void GUI_App::init_app_config() { // Profiles for the alpha are stored into the PrusaSlicer-alpha directory to not mix with the current release. // SetAppName(SLIC3R_APP_KEY); - SetAppName(SLIC3R_APP_KEY "-alpha"); -// SetAppName(SLIC3R_APP_KEY "-beta"); +// SetAppName(SLIC3R_APP_KEY "-alpha"); + SetAppName(SLIC3R_APP_KEY "-beta"); // SetAppDisplayName(SLIC3R_APP_NAME); // Set the Slic3r data directory at the Slic3r XS module. diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index a6c2dac81..acdbd249c 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -292,42 +292,58 @@ InfoDialog::InfoDialog(wxWindow* parent, const wxString &title, const wxString& finalize(); } -wxString get_wraped_wxString(const wxString& text_in, size_t line_len /*=80*/) +wxString get_wraped_wxString(const wxString& in, size_t line_len /*=80*/) { -#ifdef __WXMSW__ - char slash = '\\'; -#else - char slash = '/'; -#endif - char space = ' '; - char new_line = '\n'; + wxString out; - wxString text = text_in; - - int idx = -1; - size_t cur_len = 0; - size_t text_len = text.Len(); - - for (size_t i = 0; i < text_len; i++) { - cur_len++; - if (text[i] == space || text[i] == slash) - idx = i; - if (text[i] == new_line) { - idx = -1; - cur_len = 0; - continue; + for (size_t i = 0; i < in.size();) { + // Overwrite the character (space or newline) starting at ibreak? + bool overwrite = false; +#if wxUSE_UNICODE_WCHAR + // On Windows, most likely the internal representation of wxString is wide char. + size_t end = std::min(in.size(), i + line_len); + size_t ibreak = end; + for (size_t j = i; j < end; ++ j) { + if (bool newline = in[j] == '\n'; in[j] == ' ' || in[j] == '\t' || newline) { + ibreak = j; + overwrite = true; + if (newline) + break; + } else if (in[j] == '/' || in[j] == '\\') + ibreak = j + 1; } - if (cur_len >= line_len && idx >= 0) { - if (text[idx] == slash) { - text.insert(static_cast(idx) + 1, 1, new_line); - text_len++; +#else + // UTF8 representation of wxString. + // Where to break the line, index of character at the start of a UTF-8 sequence. + size_t ibreak = size_t(-1); + // Overwrite the character at ibreak (it is a whitespace) or not? + for (size_t cnt = 0, j = i; j < in.size();) { + if (bool newline = in[j] == '\n'; in[j] == ' ' || in[j] == '\t' || newline) { + // Overwrite the whitespace. + ibreak = j ++; + overwrite = true; + if (newline) + break; + } else if (in[j] == '/') { + // Insert after the slash. + ibreak = ++ j; + } else + j += get_utf8_sequence_length(in.c_str() + j, in.size() - j); + if (++ cnt == line_len) { + if (ibreak == size_t(-1)) + ibreak = j; + break; } - else // space - text[idx] = new_line; - cur_len = i - static_cast(idx); } +#endif + out.append(in.begin() + i, in.begin() + ibreak); + out.append('\n'); + i = ibreak; + if (overwrite) + ++ i; } - return text; + + return out; } } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index d1b7c6769..4f19ad806 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1647,18 +1647,6 @@ void TabPrint::build() option.opt.full_width = true; optgroup->append_single_option_line(option); - optgroup = page->new_optgroup(L("Post-processing scripts"), 0); - line = { "", "" }; - line.full_width = 1; - line.widget = [this](wxWindow* parent) { - return description_line_widget(parent, &m_post_process_explanation); - }; - optgroup->append_line(line); - option = optgroup->get_option("post_process"); - option.opt.full_width = true; - option.opt.height = 5;//50; - optgroup->append_single_option_line(option); - optgroup = page->new_optgroup(L("Other")); create_line_with_widget(optgroup.get(), "gcode_substitutions", "g-code-substitutions_301694", [this](wxWindow* parent) { @@ -1671,6 +1659,18 @@ void TabPrint::build() }; optgroup->append_line(line); + optgroup = page->new_optgroup(L("Post-processing scripts"), 0); + line = { "", "" }; + line.full_width = 1; + line.widget = [this](wxWindow* parent) { + return description_line_widget(parent, &m_post_process_explanation); + }; + optgroup->append_line(line); + option = optgroup->get_option("post_process"); + option.opt.full_width = true; + option.opt.height = 5;//50; + optgroup->append_single_option_line(option); + page = add_options_page(L("Notes"), "note.png"); optgroup = page->new_optgroup(L("Notes"), 0); option = optgroup->get_option("notes"); @@ -3967,7 +3967,7 @@ void SubstitutionManager::add_substitution( int substitution_id, editor->SetFont(wxGetApp().normal_font()); wxGetApp().UpdateDarkUI(editor); - top_sizer->Add(editor, proportion, wxALIGN_CENTER_VERTICAL | wxEXPAND| wxRIGHT, m_em); + top_sizer->Add(editor, proportion, wxALIGN_CENTER_VERTICAL | wxRIGHT, m_em); editor->Bind(wxEVT_TEXT_ENTER, [this, editor, substitution_id, opt_pos](wxEvent& e) { #if !defined(__WXGTK__) @@ -4032,7 +4032,7 @@ void SubstitutionManager::add_substitution( int substitution_id, auto v_sizer = new wxBoxSizer(wxVERTICAL); v_sizer->Add(top_sizer, 1, wxEXPAND); v_sizer->Add(params_sizer, 1, wxEXPAND|wxTOP|wxBOTTOM, int(0.5* m_em)); - m_grid_sizer->Add(v_sizer, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND); + m_grid_sizer->Add(v_sizer, 1, wxEXPAND); if (call_after_layout) { m_parent->GetParent()->Layout(); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 808e968b4..d49b4b1d3 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -1158,10 +1158,11 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& } if (opt_key == "gcode_substitutions") { if (!strings->empty()) - for (size_t id = 0; id < strings->size(); id += 3) + for (size_t id = 0; id < strings->size(); id += 4) out += from_u8(strings->get_at(id)) + ";\t" + from_u8(strings->get_at(id + 1)) + ";\t" + - from_u8(strings->get_at(id + 2)) + ";\n"; + from_u8(strings->get_at(id + 2)) + ";\t" + + from_u8(strings->get_at(id + 3)) + ";\n"; return out; } if (!strings->empty() && opt_idx < strings->values.size()) diff --git a/t/gcode.t b/t/gcode.t index a43b5049c..b95505e43 100644 --- a/t/gcode.t +++ b/t/gcode.t @@ -133,6 +133,7 @@ use Slic3r::Test; my $config = Slic3r::Config::new_from_defaults; $config->set('retract_length', [1000000]); $config->set('use_relative_e_distances', 1); + $config->set('layer_gcode', "G92 E0\n"); my $print = Slic3r::Test::init_print('20mm_cube', config => $config); Slic3r::Test::gcode($print); ok $print->print->total_used_filament > 0, 'final retraction is not considered in total used filament'; diff --git a/version.inc b/version.inc index c33dc002e..b976d7d67 100644 --- a/version.inc +++ b/version.inc @@ -3,7 +3,7 @@ set(SLIC3R_APP_NAME "PrusaSlicer") set(SLIC3R_APP_KEY "PrusaSlicer") -set(SLIC3R_VERSION "2.4.1-alpha0") +set(SLIC3R_VERSION "2.4.1-beta1") set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN") set(SLIC3R_RC_VERSION "2,4,1,0") set(SLIC3R_RC_VERSION_DOTS "2.4.1.0")