From a5d363a9f4c59c662f05f81ea88242d255035d0e Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 14 Dec 2020 14:19:35 +0100 Subject: [PATCH 1/5] Do not emit M1 gcode for firmwares other than Marlin (after MM priming) The M1 gcode is apparently only supported on Marlin, others do not support it or use it for something else This should fix #5441 --- src/libslic3r/GCode.cpp | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index fab7a70582..830fff8f98 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -413,14 +413,11 @@ namespace Slic3r { std::string WipeTowerIntegration::prime(GCode& gcodegen) { - assert(m_layer_idx == 0); std::string gcode; - for (const WipeTower::ToolChangeResult& tcr : m_priming) { if (! tcr.extrusions.empty()) gcode += append_tcr(gcodegen, tcr, tcr.new_tool); } - return gcode; } @@ -1243,18 +1240,30 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz)); BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print)); bbox_prime.offset(0.5f); - // Beep for 500ms, tone 800Hz. Yet better, play some Morse. - _write(file, this->retract()); - _write(file, "M300 S800 P500\n"); - if (bbox_prime.overlap(bbox_print)) { - // Wait for the user to remove the priming extrusions, otherwise they would - // get covered by the print. - _write(file, "M1 Remove priming towers and click button.\n"); - } - else { - // Just wait for a bit to let the user check, that the priming succeeded. - //TODO Add a message explaining what the printer is waiting for. This needs a firmware fix. - _write(file, "M1 S10\n"); + bool overlap = bbox_prime.overlap(bbox_print); + + if (print.config().gcode_flavor == gcfMarlin) { + _write(file, this->retract()); + _write(file, "M300 S800 P500\n"); // Beep for 500ms, tone 800Hz. + if (overlap) { + // Wait for the user to remove the priming extrusions. + _write(file, "M1 Remove priming towers and click button.\n"); + } else { + // Just wait for a bit to let the user check, that the priming succeeded. + //TODO Add a message explaining what the printer is waiting for. This needs a firmware fix. + _write(file, "M1 S10\n"); + } + } else { + // This is not Marlin, M1 command is probably not supported. + // (See https://github.com/prusa3d/PrusaSlicer/issues/5441.) + if (overlap) { + print.active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, + L("Your print is very close to the priming regions. " + "Make sure there is no collision.")); + } else { + // Just continue printing, no action necessary. + } + } } print.throw_if_canceled(); From 5553762d9c5c9cfeac0cc9bd1f413235430757d2 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 14 Dec 2020 14:22:43 +0100 Subject: [PATCH 2/5] Do not remove custom supports/seams when converting units to imperial --- src/libslic3r/Model.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 7a1cdf8d38..c25026cc49 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1042,8 +1042,6 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, bool from_imperial int vol_idx = 0; for (ModelVolume* volume : volumes) { - volume->supported_facets.clear(); - volume->seam_facets.clear(); if (!volume->mesh().empty()) { TriangleMesh mesh(volume->mesh()); mesh.require_shared_vertices(); @@ -1060,6 +1058,9 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, bool from_imperial vol->source.object_idx = (int)new_objects.size(); vol->source.volume_idx = vol_idx; + vol->supported_facets.assign(volume->supported_facets); + vol->seam_facets.assign(volume->seam_facets); + // Perform conversion only if the target "imperial" state is different from the current one. // This check supports conversion of "mixed" set of volumes, each with different "imperial" state. if (//vol->source.is_converted_from_inches != from_imperial && From 1249fdb71d775eb61451b44759e99660da00e6eb Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 14 Dec 2020 14:23:05 +0100 Subject: [PATCH 3/5] Remove custom supports/seams after mesh repair The repair can remove some of the triangles, so the custom data would make no sense. This will hopefully fix #5458 Also, show a notification with a hyperlink to undo just before the repair. --- src/slic3r/GUI/NotificationManager.cpp | 10 ++++-- src/slic3r/GUI/NotificationManager.hpp | 7 ++-- src/slic3r/GUI/Plater.cpp | 46 ++++++++++++++++++++++---- src/slic3r/Utils/UndoRedo.cpp | 18 +++++----- 4 files changed, 61 insertions(+), 20 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index b531968440..7706e7c243 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1011,7 +1011,13 @@ void NotificationManager::push_notification(const std::string& text, int timesta { push_notification_data({ NotificationType::CustomNotification, NotificationLevel::RegularNotification, 10, text }, timestamp); } -void NotificationManager::push_notification(const std::string& text, NotificationManager::NotificationLevel level, int timestamp) + +void NotificationManager::push_notification(NotificationType type, + NotificationLevel level, + const std::string& text, + const std::string& hypertext, + std::function callback, + int timestamp) { int duration = 0; switch (level) { @@ -1022,7 +1028,7 @@ void NotificationManager::push_notification(const std::string& text, Notificatio assert(false); return; } - push_notification_data({ NotificationType::CustomNotification, level, duration, text }, timestamp); + push_notification_data({ type, level, duration, text, hypertext, callback }, timestamp); } void NotificationManager::push_slicing_error_notification(const std::string& text) { diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 9452a8da0c..3e278a3908 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -67,7 +67,9 @@ enum class NotificationType // Progress bar instead of text. ProgressBar, // Notification, when Color Change G-code is empty and user try to add color change on DoubleSlider. - EmptyColorChangeCode + EmptyColorChangeCode, + // Notification that custom supports/seams were deleted after mesh repair. + CustomSupportsAndSeamRemovedAfterRepair }; class NotificationManager @@ -97,7 +99,8 @@ public: void push_notification(const std::string& text, int timestamp = 0); // Push a NotificationType::CustomNotification with provided notification level and 10s for RegularNotification. // ErrorNotification and ImportantNotification are never faded out. - void push_notification(const std::string& text, NotificationLevel level, int timestamp = 0); + void push_notification(NotificationType type, NotificationLevel level, const std::string& text, const std::string& hypertext = "", + std::function callback = std::function(), int timestamp = 0); // Creates Slicing Error notification with a custom text and no fade out. void push_slicing_error_notification(const std::string& text); // Creates Slicing Warning notification with a custom text and no fade out. diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 71205f014b..636eb457d1 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2124,11 +2124,15 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) if (evt.data.second) { this->show_action_buttons(this->ready_to_slice); notification_manager->close_notification_of_type(NotificationType::ExportFinished); - notification_manager->push_notification(format(_L("Successfully unmounted. The device %s(%s) can now be safely removed from the computer."), evt.data.first.name, evt.data.first.path), - NotificationManager::NotificationLevel::RegularNotification); + notification_manager->push_notification(NotificationType::CustomNotification, + NotificationManager::NotificationLevel::RegularNotification, + format(_L("Successfully unmounted. The device %s(%s) can now be safely removed from the computer."), evt.data.first.name, evt.data.first.path) + ); } else { - notification_manager->push_notification(format(_L("Ejecting of device %s(%s) has failed."), evt.data.first.name, evt.data.first.path), - NotificationManager::NotificationLevel::ErrorNotification); + notification_manager->push_notification(NotificationType::CustomNotification, + NotificationManager::NotificationLevel::ErrorNotification, + format(_L("Ejecting of device %s(%s) has failed."), evt.data.first.name, evt.data.first.path) + ); } }); this->q->Bind(EVT_REMOVABLE_DRIVES_CHANGED, [this, q](RemovableDrivesChangedEvent &) { @@ -3361,10 +3365,38 @@ void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* = if (obj_idx < 0) return; - Plater::TakeSnapshot snapshot(q, _L("Fix Throught NetFabb")); + size_t snapshot_time = undo_redo_stack().active_snapshot_time(); + Plater::TakeSnapshot snapshot(q, _L("Fix through NetFabb")); - fix_model_by_win10_sdk_gui(*model.objects[obj_idx], vol_idx); - sla::reproject_points_and_holes(model.objects[obj_idx]); + ModelObject* mo = model.objects[obj_idx]; + + // If there are custom supports/seams, remove them. Fixed mesh + // may be different and they would make no sense. + bool paint_removed = false; + for (ModelVolume* mv : mo->volumes) { + paint_removed |= ! mv->supported_facets.empty() || ! mv->seam_facets.empty(); + mv->supported_facets.clear(); + mv->seam_facets.clear(); + } + if (paint_removed) { + // snapshot_time is captured by copy so the lambda knows where to undo/redo to. + notification_manager->push_notification( + NotificationType::CustomSupportsAndSeamRemovedAfterRepair, + NotificationManager::NotificationLevel::RegularNotification, + _u8L("Custom supports and seams were removed after repairing the mesh."), + _u8L("Undo the repair"), + [this, snapshot_time](wxEvtHandler*){ + if (undo_redo_stack().has_undo_snapshot(snapshot_time)) + undo_redo_to(snapshot_time); + else + notification_manager->push_notification( + _u8L("Cannot undo to before the mesh repair!")); + return true; + }); + } + + fix_model_by_win10_sdk_gui(*mo, vol_idx); + sla::reproject_points_and_holes(mo); this->update(); this->object_list_changed(); this->schedule_background_process(); diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index edf6b917a1..d82d9e31db 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -36,6 +36,15 @@ namespace Slic3r { namespace UndoRedo { +#ifdef SLIC3R_UNDOREDO_DEBUG +static inline std::string ptr_to_string(const void* ptr) +{ + char buf[64]; + sprintf(buf, "%p", ptr); + return buf; +} +#endif + SnapshotData::SnapshotData() : printer_technology(ptUnknown), flags(0), layer_range_idx(-1) { } @@ -368,15 +377,6 @@ private: MutableHistoryInterval& operator=(const MutableHistoryInterval &rhs); }; -#ifdef SLIC3R_UNDOREDO_DEBUG -static inline std::string ptr_to_string(const void* ptr) -{ - char buf[64]; - sprintf(buf, "%p", ptr); - return buf; -} -#endif - // Smaller objects (Model, ModelObject, ModelInstance, ModelVolume, DynamicPrintConfig) // are mutable and there is not tracking of the changes, therefore a snapshot needs to be // taken every time and compared to the previous data at the Undo / Redo stack. From b9bdfd26e3c0258ab9c526274174df657b3563eb Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Sat, 12 Dec 2020 13:37:50 +0100 Subject: [PATCH 4/5] Linux locales detection: A little refactoring --- src/slic3r/GUI/GUI_App.cpp | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 406f57de02..44843303ec 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1293,34 +1293,40 @@ static const wxLanguageInfo* linux_get_existing_locale_language(const wxLanguage } // locales now contain all candidates for this language. - // Sort them so ones containing anything about UTF-8 are at the beginning. + // Sort them so ones containing anything about UTF-8 are at the end. std::sort(locales.begin(), locales.end(), [](const std::string& a, const std::string& b) { auto has_utf8 = [](const std::string & s) { - return boost::to_upper_copy(s).find("UTF") != std::string::npos - && s.find("8") != std::string::npos; + auto S = boost::to_upper_copy(s); + return S.find("UTF8") != std::string::npos || S.find("UTF-8") != std::string::npos; }; - return (has_utf8(a) && ! has_utf8(b)); + return ! has_utf8(a) && has_utf8(b); }); // Remove the suffix. for (std::string& s : locales) s = s.substr(0, s.find(".")); - // Is there a candidate matching a country code of a system language? Put it at the beginning - // (duplicates do not matter). Check backwards so the utf8 one ends up first if there are more. + // Is there a candidate matching a country code of a system language? Move it to the end, + // while maintaining the order of matches, so that the best match ends up at the very end. + // We just hope that dear Linux "locale -a" returns country codes in ISO 3166-1 alpha-2 code (two letter) format. + // https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes std::string system_country = "_" + into_u8(system_language->CanonicalName.AfterFirst('_')).substr(0, 2); int cnt = locales.size(); for (int i=0; iLanguage)) - return lang; - } + for (auto it = locales.rbegin(); it != locales.rend(); ++ it) + if (! it->empty()) { + const std::string &locale = *it; + const wxLanguageInfo* lang = wxLocale::FindLanguageInfo(locale.substr(0, locale.find("."))); + if (wxLocale::IsAvailable(lang->Language)) + return lang; + } return language; } #endif From acb7d6657760141cb7b1d3f3f73fa3beed6b4871 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 14 Dec 2020 22:58:28 +0100 Subject: [PATCH 5/5] Linux locales detection: reject unexpected formatting --- src/slic3r/GUI/GUI_App.cpp | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 44843303ec..9bf0a23b53 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -1272,7 +1273,7 @@ bool GUI_App::switch_language() } } -#ifdef __linux +#ifdef __linux__ static const wxLanguageInfo* linux_get_existing_locale_language(const wxLanguageInfo* language, const wxLanguageInfo* system_language) { @@ -1281,6 +1282,9 @@ static const wxLanguageInfo* linux_get_existing_locale_language(const wxLanguage std::vector locales; const std::string lang_prefix = into_u8(language->CanonicalName.BeforeFirst('_')); + // Call locale -a so we can parse the output to get the list of available locales + // We expect lines such as "en_US.utf8". Pick ones starting with the language code + // we are switching to. Lines with different formatting will be removed later. FILE* fp = popen("locale -a", "r"); if (fp != NULL) { while (fgets(path, max_len, fp) != NULL) { @@ -1303,19 +1307,30 @@ static const wxLanguageInfo* linux_get_existing_locale_language(const wxLanguage return ! has_utf8(a) && has_utf8(b); }); - // Remove the suffix. + // Remove the suffix behind a dot, if there is one. for (std::string& s : locales) s = s.substr(0, s.find(".")); + // We just hope that dear Linux "locale -a" returns country codes + // in ISO 3166-1 alpha-2 code (two letter) format. + // https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes + // To be sure, remove anything not looking as expected + // (any number of lowercase letters, underscore, two uppercase letters). + locales.erase(std::remove_if(locales.begin(), + locales.end(), + [](const std::string& s) { + return ! std::regex_match(s, + std::regex("^[a-z]+_[A-Z]{2}$")); + }), + locales.end()); + // Is there a candidate matching a country code of a system language? Move it to the end, // while maintaining the order of matches, so that the best match ends up at the very end. - // We just hope that dear Linux "locale -a" returns country codes in ISO 3166-1 alpha-2 code (two letter) format. - // https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes std::string system_country = "_" + into_u8(system_language->CanonicalName.AfterFirst('_')).substr(0, 2); int cnt = locales.size(); for (int i=0; iempty()) { const std::string &locale = *it; - const wxLanguageInfo* lang = wxLocale::FindLanguageInfo(locale.substr(0, locale.find("."))); + const wxLanguageInfo* lang = wxLocale::FindLanguageInfo(from_u8(locale)); if (wxLocale::IsAvailable(lang->Language)) return lang; }