From 6a8c34dad9adfb29bf6198df289cd07f8d85065b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 2 Jan 2020 13:41:49 +0100 Subject: [PATCH 01/33] Checking of icons for dark mode is removed to BitmapCache --- src/slic3r/GUI/BitmapCache.cpp | 29 +++++++++++++++++++++++------ src/slic3r/GUI/BitmapCache.hpp | 2 +- src/slic3r/GUI/wxExtensions.cpp | 14 +++++++------- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/BitmapCache.cpp b/src/slic3r/GUI/BitmapCache.cpp index f7cefe72b2..ae4c21ccb4 100644 --- a/src/slic3r/GUI/BitmapCache.cpp +++ b/src/slic3r/GUI/BitmapCache.cpp @@ -1,6 +1,7 @@ #include "BitmapCache.hpp" #include "libslic3r/Utils.hpp" +#include #if ! defined(WIN32) && ! defined(__APPLE__) #define BROKEN_ALPHA @@ -15,7 +16,7 @@ #include "nanosvg/nanosvg.h" #define NANOSVGRAST_IMPLEMENTATION #include "nanosvg/nanosvgrast.h" -#include "GUI_App.hpp" +//#include "GUI_App.hpp" namespace Slic3r { namespace GUI { @@ -226,24 +227,40 @@ wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned width, } wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_width, unsigned target_height, - float scale /* = 1.0f */, const bool grayscale/* = false*/) + float scale /* = 1.0f */, const bool grayscale/* = false*/, const bool dark_mode/* = false*/) { - std::string bitmap_key = bitmap_name + ( target_height !=0 ? + /* For the Dark mode of any platform, we should draw icons in respect to OS background + * Note: All standard(regular) icons are collected in "icons" folder, + * SVG-icons, which have "Dark mode" variant, are collected in "icons/white" folder + */ + std::string folder; + if (dark_mode) + { +#ifdef __WXMSW__ + folder = "white\\"; +#else + folder = "white/"; +#endif + if (!boost::filesystem::exists(Slic3r::var(folder + bitmap_name + ".svg"))) + folder.clear(); + } + + std::string bitmap_key = folder + bitmap_name + ( target_height !=0 ? "-h" + std::to_string(target_height) : "-w" + std::to_string(target_width)) + (scale != 1.0f ? "-s" + std::to_string(scale) : "") + (grayscale ? "-gs" : ""); - target_height != 0 ? target_height *= scale : target_width *= scale; - auto it = m_map.find(bitmap_key); if (it != m_map.end()) return it->second; - NSVGimage *image = ::nsvgParseFromFile(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f); + NSVGimage *image = ::nsvgParseFromFile(Slic3r::var(folder + bitmap_name + ".svg").c_str(), "px", 96.0f); if (image == nullptr) return nullptr; + target_height != 0 ? target_height *= scale : target_width *= scale; + float svg_scale = target_height != 0 ? (float)target_height / image->height : target_width != 0 ? (float)target_width / image->width : 1; diff --git a/src/slic3r/GUI/BitmapCache.hpp b/src/slic3r/GUI/BitmapCache.hpp index ce77057c82..041e7d8920 100644 --- a/src/slic3r/GUI/BitmapCache.hpp +++ b/src/slic3r/GUI/BitmapCache.hpp @@ -34,7 +34,7 @@ public: // Load png from resources/icons. bitmap_key is given without the .png suffix. Bitmap will be rescaled to provided height/width if nonzero. wxBitmap* load_png(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false); // Load svg from resources/icons. bitmap_key is given without the .svg suffix. SVG will be rasterized to provided height/width. - wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, float scale = 1.0f, const bool grayscale = false); + wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, float scale = 1.0f, const bool grayscale = false, const bool dark_mode = false); static wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency); static wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3]) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE); } diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 7b36207f52..78950e0291 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -13,9 +13,8 @@ #include #include -#include +//#include #include -#include #include "BitmapCache.hpp" #include "GUI.hpp" @@ -427,6 +426,7 @@ static float get_svg_scale_factor(wxWindow *win) } // in the Dark mode of any platform, we should draw icons in respect to OS background +/* static std::string icon_name_respected_to_mode(const std::string& bmp_name_in) { #ifdef __WXMSW__ @@ -447,7 +447,7 @@ static std::string icon_name_respected_to_mode(const std::string& bmp_name_in) } return bmp_name; } - +*/ // If an icon has horizontal orientation (width > height) call this function with is_horizontal = true wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name_in, const int px_cnt/* = 16*/, const bool is_horizontal /* = false*/, const bool grayscale/* = false*/) @@ -474,13 +474,13 @@ wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name_in, scale_base = (unsigned int)(em_unit(win) * px_cnt * 0.1f + 0.5f); -// std::string bmp_name = bmp_name_in; -// boost::replace_last(bmp_name, ".png", ""); + std::string bmp_name = bmp_name_in; + boost::replace_last(bmp_name, ".png", ""); - std::string bmp_name = icon_name_respected_to_mode(bmp_name_in); +// std::string bmp_name = icon_name_respected_to_mode(bmp_name_in); // Try loading an SVG first, then PNG if SVG is not found: - wxBitmap *bmp = cache.load_svg(bmp_name, width, height, scale_factor, grayscale); + wxBitmap *bmp = cache.load_svg(bmp_name, width, height, scale_factor, grayscale, Slic3r::GUI::wxGetApp().dark_mode()); if (bmp == nullptr) { bmp = cache.load_png(bmp_name, width, height, grayscale); } From e531b0319f8425f56a9360d71e25ad25b803b369 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 7 Jan 2020 16:17:20 +0100 Subject: [PATCH 02/33] Code cleaning and refactoring for https://github.com/prusa3d/PrusaSlicer/commit/6a8c34dad9adfb29bf6198df289cd07f8d85065b --- src/slic3r/GUI/BitmapCache.cpp | 33 +++++++++++++++++++++++---------- src/slic3r/GUI/wxExtensions.cpp | 24 ------------------------ 2 files changed, 23 insertions(+), 34 deletions(-) diff --git a/src/slic3r/GUI/BitmapCache.cpp b/src/slic3r/GUI/BitmapCache.cpp index ae4c21ccb4..7322a88d1c 100644 --- a/src/slic3r/GUI/BitmapCache.cpp +++ b/src/slic3r/GUI/BitmapCache.cpp @@ -229,6 +229,12 @@ wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned width, wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_width, unsigned target_height, float scale /* = 1.0f */, const bool grayscale/* = false*/, const bool dark_mode/* = false*/) { + std::string bitmap_key = bitmap_name + ( target_height !=0 ? + "-h" + std::to_string(target_height) : + "-w" + std::to_string(target_width)) + + (scale != 1.0f ? "-s" + std::to_string(scale) : "") + + (grayscale ? "-gs" : ""); + /* For the Dark mode of any platform, we should draw icons in respect to OS background * Note: All standard(regular) icons are collected in "icons" folder, * SVG-icons, which have "Dark mode" variant, are collected in "icons/white" folder @@ -241,19 +247,26 @@ wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_ #else folder = "white/"; #endif + auto it = m_map.find(folder + bitmap_key); + if (it != m_map.end()) + return it->second; + else { + it = m_map.find(bitmap_key); + if (it != m_map.end()) + return it->second; + } + if (!boost::filesystem::exists(Slic3r::var(folder + bitmap_name + ".svg"))) folder.clear(); + else + bitmap_key = folder + bitmap_key; + } + else + { + auto it = m_map.find(bitmap_key); + if (it != m_map.end()) + return it->second; } - - std::string bitmap_key = folder + bitmap_name + ( target_height !=0 ? - "-h" + std::to_string(target_height) : - "-w" + std::to_string(target_width)) - + (scale != 1.0f ? "-s" + std::to_string(scale) : "") - + (grayscale ? "-gs" : ""); - - auto it = m_map.find(bitmap_key); - if (it != m_map.end()) - return it->second; NSVGimage *image = ::nsvgParseFromFile(Slic3r::var(folder + bitmap_name + ".svg").c_str(), "px", 96.0f); if (image == nullptr) diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 78950e0291..ae27f6d03d 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -13,7 +13,6 @@ #include #include -//#include #include #include "BitmapCache.hpp" @@ -425,29 +424,6 @@ static float get_svg_scale_factor(wxWindow *win) #endif } -// in the Dark mode of any platform, we should draw icons in respect to OS background -/* -static std::string icon_name_respected_to_mode(const std::string& bmp_name_in) -{ -#ifdef __WXMSW__ - const std::string folder = "white\\"; -#else - const std::string folder = "white/"; -#endif - std::string bmp_name; - if (Slic3r::GUI::wxGetApp().dark_mode()) { - bmp_name = folder + bmp_name_in; - boost::replace_last(bmp_name, ".png", ""); - if (! boost::filesystem::exists(Slic3r::var(bmp_name + ".svg"))) - bmp_name.clear(); - } - if (bmp_name.empty()) { - bmp_name = bmp_name_in; - boost::replace_last(bmp_name, ".png", ""); - } - return bmp_name; -} -*/ // If an icon has horizontal orientation (width > height) call this function with is_horizontal = true wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name_in, const int px_cnt/* = 16*/, const bool is_horizontal /* = false*/, const bool grayscale/* = false*/) From 5a63b9a6a0e5ead36ec5f8943e211ecf2bd6f513 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 8 Jan 2020 10:04:39 +0100 Subject: [PATCH 03/33] Follow-up of 5b2e2fe7b3956fc13b8ce8e0e35db4d3278c50a8 -> Fixed uv mapping on gizmobar --- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 959d7d3edb..abcb39c14d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -886,7 +886,7 @@ void GLGizmosManager::do_render_overlay() const #else float du = (float)(tex_width - 1) / (3.0f * (float)tex_width); // 3 is the number of possible states if the icons #endif // ENABLE_GIZMO_ICONS_NON_ACTIVABLE_STATE - float dv = (float)(tex_height - 1) / (float)(selectable_idxs.size() * tex_height); + float dv = (float)(tex_height - 1) / (float)(m_gizmos.size() * tex_height); // tiles in the texture are spaced by 1 pixel float u_offset = 1.0f / (float)tex_width; From abd432e7a8f91bba876a29c857844c93dab4b762 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 8 Jan 2020 11:11:38 +0100 Subject: [PATCH 04/33] Configurable paths export (fullpath or not) to 3mf and amf --- src/PrusaSlicer.cpp | 8 ++++ src/libslic3r/Format/3mf.cpp | 50 +++++++++++++++++++++++++ src/libslic3r/Format/3mf.hpp | 8 ++++ src/libslic3r/Format/AMF.cpp | 9 +++++ src/libslic3r/Format/AMF.hpp | 4 ++ src/libslic3r/Technologies.hpp | 3 ++ src/slic3r/GUI/AppConfig.cpp | 5 +++ src/slic3r/GUI/ConfigWizard.cpp | 28 ++++++++++++++ src/slic3r/GUI/ConfigWizard_private.hpp | 16 ++++++++ src/slic3r/GUI/Plater.cpp | 16 ++++++++ src/slic3r/GUI/Preferences.cpp | 10 +++++ src/slic3r/Utils/FixModelByWin10.cpp | 7 ++++ tests/libslic3r/test_3mf.cpp | 4 ++ 13 files changed, 168 insertions(+) diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index d755f0b2e7..28aadc0458 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -641,10 +641,18 @@ bool CLI::export_models(IO::ExportFormat format) const std::string path = this->output_filepath(model, format); bool success = false; switch (format) { +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF + case IO::AMF: success = Slic3r::store_amf(path.c_str(), &model, nullptr, false); break; +#else case IO::AMF: success = Slic3r::store_amf(path.c_str(), &model, nullptr); break; +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF case IO::OBJ: success = Slic3r::store_obj(path.c_str(), &model); break; case IO::STL: success = Slic3r::store_stl(path.c_str(), &model, true); break; +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF + case IO::TMF: success = Slic3r::store_3mf(path.c_str(), &model, nullptr, false); break; +#else case IO::TMF: success = Slic3r::store_3mf(path.c_str(), &model, nullptr); break; +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF default: assert(false); break; } if (success) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 44e12db865..cca90b4633 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -1876,12 +1876,24 @@ namespace Slic3r { typedef std::vector BuildItemsList; typedef std::map IdToObjectDataMap; +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF + bool m_fullpath_sources{ true }; +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF + public: +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF +#if ENABLE_THUMBNAIL_GENERATOR + bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data = nullptr); +#else + bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources); +#endif // ENABLE_THUMBNAIL_GENERATOR +#else #if ENABLE_THUMBNAIL_GENERATOR bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data = nullptr); #else bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config); #endif // ENABLE_THUMBNAIL_GENERATOR +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF private: #if ENABLE_THUMBNAIL_GENERATOR @@ -1906,6 +1918,22 @@ namespace Slic3r { bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model); }; +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF +#if ENABLE_THUMBNAIL_GENERATOR + bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data) + { + clear_errors(); + m_fullpath_sources = fullpath_sources; + return _save_model_to_file(filename, model, config, thumbnail_data); + } +#else + bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources) + { + clear_errors(); + return _save_model_to_file(filename, model, config); + } +#endif // ENABLE_THUMBNAIL_GENERATOR +#else #if ENABLE_THUMBNAIL_GENERATOR bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data) { @@ -1919,6 +1947,7 @@ namespace Slic3r { return _save_model_to_file(filename, model, config); } #endif // ENABLE_THUMBNAIL_GENERATOR +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF #if ENABLE_THUMBNAIL_GENERATOR bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data) @@ -2557,7 +2586,12 @@ namespace Slic3r { // stores volume's source data if (!volume->source.input_file.empty()) { +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF + std::string input_file = xml_escape(m_fullpath_sources ? volume->source.input_file : boost::filesystem::path(volume->source.input_file).filename().string()); + stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << input_file << "\"/>\n"; +#else stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->source.input_file) << "\"/>\n"; +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OBJECT_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.object_idx << "\"/>\n"; stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_VOLUME_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.volume_idx << "\"/>\n"; stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_X_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(0) << "\"/>\n"; @@ -2646,21 +2680,37 @@ bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model, bool c return res; } +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF +#if ENABLE_THUMBNAIL_GENERATOR +bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data) +#else +bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources) +#endif // ENABLE_THUMBNAIL_GENERATOR +#else #if ENABLE_THUMBNAIL_GENERATOR bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data) #else bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config) #endif // ENABLE_THUMBNAIL_GENERATOR +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF { if ((path == nullptr) || (model == nullptr)) return false; _3MF_Exporter exporter; +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF +#if ENABLE_THUMBNAIL_GENERATOR + bool res = exporter.save_model_to_file(path, *model, config, fullpath_sources, thumbnail_data); +#else + bool res = exporter.save_model_to_file(path, *model, config, fullpath_sources); +#endif // ENABLE_THUMBNAIL_GENERATOR +#else #if ENABLE_THUMBNAIL_GENERATOR bool res = exporter.save_model_to_file(path, *model, config, thumbnail_data); #else bool res = exporter.save_model_to_file(path, *model, config); #endif // ENABLE_THUMBNAIL_GENERATOR +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF if (!res) exporter.log_errors(); diff --git a/src/libslic3r/Format/3mf.hpp b/src/libslic3r/Format/3mf.hpp index 2e85b7f59e..c28716fffc 100644 --- a/src/libslic3r/Format/3mf.hpp +++ b/src/libslic3r/Format/3mf.hpp @@ -31,11 +31,19 @@ namespace Slic3r { // Save the given model and the config data contained in the given Print into a 3mf file. // The model could be modified during the export process if meshes are not repaired or have no shared vertices +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF +#if ENABLE_THUMBNAIL_GENERATOR + extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data = nullptr); +#else + extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources); +#endif // ENABLE_THUMBNAIL_GENERATOR +#else #if ENABLE_THUMBNAIL_GENERATOR extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data = nullptr); #else extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config); #endif // ENABLE_THUMBNAIL_GENERATOR +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF }; // namespace Slic3r diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index 5b65d8fa2d..947c044581 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -1019,7 +1019,11 @@ bool load_amf(const char* path, DynamicPrintConfig* config, Model* model, bool c return false; } +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF +bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources) +#else bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF { if ((path == nullptr) || (model == nullptr)) return false; @@ -1177,7 +1181,12 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) stream << "\n"; if (!volume->source.input_file.empty()) { +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF + std::string input_file = xml_escape(fullpath_sources ? volume->source.input_file : boost::filesystem::path(volume->source.input_file).filename().string()); + stream << " " << input_file << "\n"; +#else stream << " " << xml_escape(volume->source.input_file) << "\n"; +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF stream << " " << volume->source.object_idx << "\n"; stream << " " << volume->source.volume_idx << "\n"; stream << " " << volume->source.mesh_offset(0) << "\n"; diff --git a/src/libslic3r/Format/AMF.hpp b/src/libslic3r/Format/AMF.hpp index 1206d60d07..7404e1e901 100644 --- a/src/libslic3r/Format/AMF.hpp +++ b/src/libslic3r/Format/AMF.hpp @@ -11,7 +11,11 @@ extern bool load_amf(const char* path, DynamicPrintConfig* config, Model* model, // Save the given model and the config data into an amf file. // The model could be modified during the export process if meshes are not repaired or have no shared vertices +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF +extern bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources); +#else extern bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config); +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF }; // namespace Slic3r diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index bc3ec6c447..23c516a348 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -71,5 +71,8 @@ // Enable a modified version of the toolbar textures where all the icons are separated by 1 pixel #define ENABLE_MODIFIED_TOOLBAR_TEXTURES (1 && ENABLE_2_2_0_BETA1) +// Enable configurable paths export (fullpath or not) to 3mf and amf +#define ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF (1 && ENABLE_2_2_0_BETA1) + #endif // _technologies_h_ diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp index eb0a7fca69..bcd79274f9 100644 --- a/src/slic3r/GUI/AppConfig.cpp +++ b/src/slic3r/GUI/AppConfig.cpp @@ -61,6 +61,11 @@ void AppConfig::set_defaults() if (get("preset_update").empty()) set("preset_update", "1"); +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF + if (get("export_sources_full_pathnames").empty()) + set("export_sources_full_pathnames", "1"); +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF + // remove old 'use_legacy_opengl' parameter from this config, if present if (!get("use_legacy_opengl").empty()) erase("", "use_legacy_opengl"); diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 265e7f203a..75d170b03d 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -765,6 +765,23 @@ PageUpdate::PageUpdate(ConfigWizard *parent) box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); }); } +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF +PageReloadFromDisk::PageReloadFromDisk(ConfigWizard* parent) + : ConfigWizardPage(parent, _(L("Reload from disk")), _(L("Reload from disk"))) + , full_pathnames(true) +{ + auto* box_pathnames = new wxCheckBox(this, wxID_ANY, _(L("Export full pathnames of models and parts sources into 3mf and amf files"))); + box_pathnames->SetValue(wxGetApp().app_config->get("export_sources_full_pathnames") == "1"); + append(box_pathnames); + append_text(_(L( + "If enabled, allows the Reload from disk command to automatically find and load the files when invoked.\n" + "If not enabled, the Reload from disk command will ask to select each file using an open file dialog." + ))); + + box_pathnames->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& event) { this->full_pathnames = event.IsChecked(); }); +} +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF + PageMode::PageMode(ConfigWizard *parent) : ConfigWizardPage(parent, _(L("View mode")), _(L("View mode"))) { @@ -1356,6 +1373,9 @@ void ConfigWizard::priv::load_pages() btn_finish->Enable(any_fff_selected || any_sla_selected); index->add_page(page_update); +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF + index->add_page(page_reload_from_disk); +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF index->add_page(page_mode); index->go_to(former_active); // Will restore the active item/page if possible @@ -1730,6 +1750,11 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese } app_config->set("version_check", page_update->version_check ? "1" : "0"); app_config->set("preset_update", page_update->preset_update ? "1" : "0"); + +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF + app_config->set("export_sources_full_pathnames", page_reload_from_disk->full_pathnames ? "1" : "0"); +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF + page_mode->serialize_mode(app_config); std::string preferred_model; @@ -1885,6 +1910,9 @@ ConfigWizard::ConfigWizard(wxWindow *parent) p->add_page(p->page_custom = new PageCustom(this)); p->add_page(p->page_update = new PageUpdate(this)); +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF + p->add_page(p->page_reload_from_disk = new PageReloadFromDisk(this)); +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF p->add_page(p->page_mode = new PageMode(this)); p->add_page(p->page_firmware = new PageFirmware(this)); p->add_page(p->page_bed = new PageBedShape(this)); diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index a90832a3fc..3ea5321f84 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -301,6 +301,17 @@ struct PageUpdate: ConfigWizardPage PageUpdate(ConfigWizard *parent); }; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF +struct PageReloadFromDisk : ConfigWizardPage +{ + bool full_pathnames; + + PageReloadFromDisk(ConfigWizard* parent); +}; +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + struct PageMode: ConfigWizardPage { wxRadioButton *radio_simple; @@ -455,6 +466,11 @@ struct ConfigWizard::priv PageMaterials *page_sla_materials = nullptr; PageCustom *page_custom = nullptr; PageUpdate *page_update = nullptr; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF + PageReloadFromDisk *page_reload_from_disk = nullptr; +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ PageMode *page_mode = nullptr; PageVendors *page_vendors = nullptr; Pages3rdparty pages_3rdparty; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b0fb3f0e68..fe50466be4 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4909,7 +4909,12 @@ void Plater::export_amf() wxBusyCursor wait; bool export_config = true; DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure(); +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF + bool full_pathnames = wxGetApp().app_config->get("export_sources_full_pathnames") == "1"; + if (Slic3r::store_amf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames)) { +#else if (Slic3r::store_amf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr)) { +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF // Success p->statusbar()->set_status_text(wxString::Format(_(L("AMF file exported to %s")), path)); } else { @@ -4938,6 +4943,16 @@ void Plater::export_3mf(const boost::filesystem::path& output_path) DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure(); const std::string path_u8 = into_u8(path); wxBusyCursor wait; +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF + bool full_pathnames = wxGetApp().app_config->get("export_sources_full_pathnames") == "1"; +#if ENABLE_THUMBNAIL_GENERATOR + ThumbnailData thumbnail_data; + p->generate_thumbnail(thumbnail_data, THUMBNAIL_SIZE_3MF.first, THUMBNAIL_SIZE_3MF.second, false, true, true, true); + if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames, &thumbnail_data)) { +#else + if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames)) { +#endif // ENABLE_THUMBNAIL_GENERATOR +#else #if ENABLE_THUMBNAIL_GENERATOR ThumbnailData thumbnail_data; p->generate_thumbnail(thumbnail_data, THUMBNAIL_SIZE_3MF.first, THUMBNAIL_SIZE_3MF.second, false, true, true, true); @@ -4945,6 +4960,7 @@ void Plater::export_3mf(const boost::filesystem::path& output_path) #else if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr)) { #endif // ENABLE_THUMBNAIL_GENERATOR +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF // Success p->statusbar()->set_status_text(wxString::Format(_(L("3MF file exported to %s")), path)); p->set_project_filename(path); diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 827d1f4e05..295c1a6ecc 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -73,6 +73,16 @@ void PreferencesDialog::build() option = Option (def, "version_check"); m_optgroup->append_single_option_line(option); +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF + // Please keep in sync with ConfigWizard + def.label = L("Export sources full pathnames to 3mf and amf"); + def.type = coBool; + def.tooltip = L("If enabled, allows the Reload from disk command to automatically find and load the files when invoked."); + def.set_default_value(new ConfigOptionBool(app_config->get("export_sources_full_pathnames") == "1")); + option = Option(def, "export_sources_full_pathnames"); + m_optgroup->append_single_option_line(option); +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF + // Please keep in sync with ConfigWizard def.label = L("Update built-in Presets automatically"); def.type = coBool; diff --git a/src/slic3r/Utils/FixModelByWin10.cpp b/src/slic3r/Utils/FixModelByWin10.cpp index b7eb12e020..28f8b54d5e 100644 --- a/src/slic3r/Utils/FixModelByWin10.cpp +++ b/src/slic3r/Utils/FixModelByWin10.cpp @@ -363,10 +363,17 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx) ModelObject *model_object = model.add_object(); model_object->add_volume(*volumes[ivolume]); model_object->add_instance(); +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF + if (!Slic3r::store_3mf(path_src.string().c_str(), &model, nullptr, false)) { + boost::filesystem::remove(path_src); + throw std::runtime_error(L("Export of a temporary 3mf file failed")); + } +#else if (! Slic3r::store_3mf(path_src.string().c_str(), &model, nullptr)) { boost::filesystem::remove(path_src); throw std::runtime_error(L("Export of a temporary 3mf file failed")); } +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF model.clear_objects(); model.clear_materials(); boost::filesystem::path path_dst = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); diff --git a/tests/libslic3r/test_3mf.cpp b/tests/libslic3r/test_3mf.cpp index 5215e2ebd2..5f1c74c365 100644 --- a/tests/libslic3r/test_3mf.cpp +++ b/tests/libslic3r/test_3mf.cpp @@ -51,7 +51,11 @@ SCENARIO("Export+Import geometry to/from 3mf file cycle", "[3mf]") { WHEN("model is saved+loaded to/from 3mf file") { // save the model to 3mf file std::string test_file = std::string(TEST_DATA_DIR) + "/test_3mf/prusa.3mf"; +#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF + store_3mf(test_file.c_str(), &src_model, nullptr, false); +#else store_3mf(test_file.c_str(), &src_model, nullptr); +#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF // load back the model from the 3mf file Model dst_model; From 7a8251b62677b47baa0c9d0e9eb29ded3de0a95f Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 8 Jan 2020 12:30:42 +0100 Subject: [PATCH 05/33] Attempt to fix #3479 --- src/slic3r/GUI/Mouse3DController.cpp | 15 +++++++++------ src/slic3r/GUI/Mouse3DController.hpp | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index 38432d0d34..8485c4b276 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -631,13 +631,16 @@ bool Mouse3DController::connect_device() if (m_device != nullptr) { - std::vector manufacturer(1024, 0); - hid_get_manufacturer_string(m_device, manufacturer.data(), 1024); - m_device_str = boost::nowide::narrow(manufacturer.data()); + wchar_t buffer[1024]; + hid_get_manufacturer_string(m_device, buffer, 1024); + m_device_str = boost::nowide::narrow(buffer); + // #3479 seems to show that sometimes an extra whitespace is added, so we remove it + boost::algorithm::trim(m_device_str); - std::vector product(1024, 0); - hid_get_product_string(m_device, product.data(), 1024); - m_device_str += "/" + boost::nowide::narrow(product.data()); + hid_get_product_string(m_device, buffer, 1024); + m_device_str += "/" + boost::nowide::narrow(buffer); + // #3479 seems to show that sometimes an extra whitespace is added, so we remove it + boost::algorithm::trim(m_device_str); BOOST_LOG_TRIVIAL(info) << "Connected 3DConnexion device:"; BOOST_LOG_TRIVIAL(info) << "Manufacturer/product: " << m_device_str; diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 3598d7df7a..1cddf254bd 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -68,7 +68,7 @@ class Mouse3DController CustomParameters m_translation_params; CustomParameters m_rotation_params; #if ENABLE_3DCONNEXION_Y_AS_ZOOM - CustomParameters m_zoom_params; + CustomParameters m_zoom_params; #endif // ENABLE_3DCONNEXION_Y_AS_ZOOM // When the 3Dconnexion driver is running the system gets, by default, mouse wheel events when rotations around the X axis are detected. From e0811e4aa580983cc5316427414c46b9af7867d6 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 8 Jan 2020 14:58:12 +0100 Subject: [PATCH 06/33] Optimization of G-code export: 1) Don't allocate ExtruderOverrides if not necessary 2) Use boost::container::small_vectorfills.entities : layerm->perimeters.entities; - - for (const ExtrusionEntity *ee : source_entities) { - // fill represents infill extrusions of a single island. - const auto *fill = dynamic_cast(ee); - if (fill->entities.empty()) // This shouldn't happen but first_point() would fail. + std::vector printing_extruders; + for (const ObjectByExtruder::Island::Region::Type entity_type : { ObjectByExtruder::Island::Region::INFILL, ObjectByExtruder::Island::Region::PERIMETERS }) { + for (const ExtrusionEntity *ee : (entity_type == ObjectByExtruder::Island::Region::INFILL) ? layerm->fills.entities : layerm->perimeters.entities) { + // extrusions represents infill or perimeter extrusions of a single island. + assert(dynamic_cast(ee) != nullptr); + const auto *extrusions = static_cast(ee); + if (extrusions->entities.empty()) // This shouldn't happen but first_point() would fail. continue; // This extrusion is part of certain Region, which tells us which extruder should be used for it: - int correct_extruder_id = Print::get_extruder(*fill, region); + int correct_extruder_id = Print::get_extruder(*extrusions, region); // Let's recover vector of extruder overrides: - const ExtruderPerCopy* entity_overrides = const_cast(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->copies().size()); + const WipingExtrusions::ExtruderPerCopy *entity_overrides = const_cast(layer_tools).wiping_extrusions().get_extruder_overrides(extrusions, correct_extruder_id, layer_to_print.object()->copies().size()); + printing_extruders.clear(); + if (entity_overrides == nullptr) { + printing_extruders.emplace_back(correct_extruder_id); + } else { + printing_extruders.reserve(entity_overrides->size() + 1); + for (int extruder : *entity_overrides) + printing_extruders.emplace_back(extruder >= 0 ? + // at least one copy is overridden to use this extruder + extruder : + // at least one copy would normally be printed with this extruder (see get_extruder_overrides function for explanation) + static_cast(- extruder - 1)); + } + if (! layer_tools.has_extruder(correct_extruder_id)) { + // this entity is not overridden, but its extruder is not in layer_tools - we'll print it + // by last extruder on this layer (could happen e.g. when a wiping object is taller than others - dontcare extruders are eradicated from layer_tools) + printing_extruders.emplace_back(layer_tools.extruders.back()); + } + Slic3r::sort_remove_duplicates(printing_extruders); + if (printing_extruders.size() == 1 && printing_extruders.front() == correct_extruder_id) + entity_overrides = nullptr; // Now we must add this extrusion into the by_extruder map, once for each extruder that will print it: - for (unsigned int extruder : layer_tools.extruders) + for (unsigned int extruder : printing_extruders) { - // Init by_extruder item only if we actually use the extruder: - if (std::find(entity_overrides->begin(), entity_overrides->end(), extruder) != entity_overrides->end() || // at least one copy is overridden to use this extruder - std::find(entity_overrides->begin(), entity_overrides->end(), -extruder-1) != entity_overrides->end() || // at least one copy would normally be printed with this extruder (see get_extruder_overrides function for explanation) - (std::find(layer_tools.extruders.begin(), layer_tools.extruders.end(), correct_extruder_id) == layer_tools.extruders.end() && extruder == layer_tools.extruders.back())) // this entity is not overridden, but its extruder is not in layer_tools - we'll print it - //by last extruder on this layer (could happen e.g. when a wiping object is taller than others - dontcare extruders are eradicated from layer_tools) - { - std::vector &islands = object_islands_by_extruder( - by_extruder, - extruder, - &layer_to_print - layers.data(), - layers.size(), n_slices+1); - for (size_t i = 0; i <= n_slices; ++ i) { - bool last = i == n_slices; - size_t island_idx = last ? n_slices : slices_test_order[i]; - if (// fill->first_point does not fit inside any slice - last || - // fill->first_point fits inside ith slice - point_inside_surface(island_idx, fill->first_point())) { - if (islands[island_idx].by_region.empty()) - islands[island_idx].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region()); - islands[island_idx].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->copies().size()); - break; - } + std::vector &islands = object_islands_by_extruder( + by_extruder, + extruder, + &layer_to_print - layers.data(), + layers.size(), n_slices+1); + for (size_t i = 0; i <= n_slices; ++ i) { + bool last = i == n_slices; + size_t island_idx = last ? n_slices : slices_test_order[i]; + if (// extrusions->first_point does not fit inside any slice + last || + // extrusions->first_point fits inside ith slice + point_inside_surface(island_idx, extrusions->first_point())) { + if (islands[island_idx].by_region.empty()) + islands[island_idx].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region()); + islands[island_idx].by_region[region_id].append(entity_type, extrusions, entity_overrides); + break; } } } @@ -2147,6 +2158,7 @@ void GCode::process_layer( // We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature): bool is_anything_overridden = const_cast(layer_tools).wiping_extrusions().is_anything_overridden(); + std::vector by_region_per_copy_cache; for (int print_wipe_extrusions = is_anything_overridden; print_wipe_extrusions>=0; --print_wipe_extrusions) { if (is_anything_overridden && print_wipe_extrusions == 0) gcode+="; PURGING FINISHED\n"; @@ -2174,8 +2186,8 @@ void GCode::process_layer( m_layer = layers[instance_to_print.layer_id].layer(); } for (ObjectByExtruder::Island &island : instance_to_print.object_by_extruder.islands) { - const auto& by_region_specific = is_anything_overridden ? island.by_region_per_copy(instance_to_print.instance_id, extruder_id, print_wipe_extrusions) : island.by_region; - + const auto& by_region_specific = is_anything_overridden ? island.by_region_per_copy(by_region_per_copy_cache, instance_to_print.instance_id, extruder_id, print_wipe_extrusions) : island.by_region; + //FIXME the following code prints regions in the order they are defined, the path is not optimized in any way. if (print.config().infill_first) { gcode += this->extrude_infill(print, by_region_specific); gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[instance_to_print.layer_id]); @@ -3260,59 +3272,97 @@ Point GCode::gcode_to_point(const Vec2d &point) const // Goes through by_region std::vector and returns reference to a subvector of entities, that are to be printed // during infill/perimeter wiping, or normally (depends on wiping_entities parameter) -// Returns a reference to member to avoid copying. -const std::vector& GCode::ObjectByExtruder::Island::by_region_per_copy(unsigned int copy, int extruder, bool wiping_entities) +// Fills in by_region_per_copy_cache and returns its reference. +const std::vector& GCode::ObjectByExtruder::Island::by_region_per_copy(std::vector &by_region_per_copy_cache, unsigned int copy, int extruder, bool wiping_entities) const { + bool has_overrides = false; + for (const auto& reg : by_region) + if (! reg.infills_overrides.empty() || ! reg.perimeters_overrides.empty()) { + has_overrides = true; + break; + } + if (! has_overrides) + // Simple case. No need to copy the regions. + return this->by_region; + + // Complex case. Some of the extrusions of some object instances are to be printed first - those are the wiping extrusions. + // Some of the extrusions of some object instances are printed later - those are the clean print extrusions. + // Filter out the extrusions based on the infill_overrides / perimeter_overrides: + + // Data is cleared, but the memory is not. by_region_per_copy_cache.clear(); for (const auto& reg : by_region) { - by_region_per_copy_cache.push_back(ObjectByExtruder::Island::Region()); // creates a region in the newly created Island + by_region_per_copy_cache.emplace_back(); // creates a region in the newly created Island // Now we are going to iterate through perimeters and infills and pick ones that are supposed to be printed // References are used so that we don't have to repeat the same code for (int iter = 0; iter < 2; ++iter) { - const ExtrusionEntitiesPtr& entities = (iter ? reg.infills.entities : reg.perimeters.entities); - ExtrusionEntityCollection& target_eec = (iter ? by_region_per_copy_cache.back().infills : by_region_per_copy_cache.back().perimeters); - const std::vector& overrides = (iter ? reg.infills_overrides : reg.perimeters_overrides); + const ExtrusionEntitiesPtr& entities = (iter ? reg.infills.entities : reg.perimeters.entities); + ExtrusionEntityCollection& target_eec = (iter ? by_region_per_copy_cache.back().infills : by_region_per_copy_cache.back().perimeters); + const std::vector& overrides = (iter ? reg.infills_overrides : reg.perimeters_overrides); // Now the most important thing - which extrusion should we print. // See function ToolOrdering::get_extruder_overrides for details about the negative numbers hack. - int this_extruder_mark = wiping_entities ? extruder : -extruder-1; - - for (unsigned int i=0;iat(copy) == this_extruder_mark) // this copy should be printed with this extruder - target_eec.append((*entities[i])); + if (wiping_entities) { + // Apply overrides for this region. + for (unsigned int i = 0; i < overrides.size(); ++ i) { + const WipingExtrusions::ExtruderPerCopy *this_override = overrides[i]; + // This copy (aka object instance) should be printed with this extruder, which overrides the default one. + if (this_override != nullptr && (*this_override)[copy] == extruder) + // Clone the ExtrusionEntity. This is quite expensive. + target_eec.append((*entities[i])); + } + } else { + // Apply normal extrusions (non-overrides) for this region. + unsigned int i = 0; + for (; i < overrides.size(); ++ i) { + const WipingExtrusions::ExtruderPerCopy *this_override = overrides[i]; + // This copy (aka object instance) should be printed with this extruder, which shall be equal to the default one. + if (this_override == nullptr || (*this_override)[copy] == -extruder-1) + // Clone the ExtrusionEntity. This is quite expensive. + target_eec.append((*entities[i])); + } + for (; i < overrides.size(); ++ i) + // Clone the ExtrusionEntity. This is quite expensive. + target_eec.append(*entities[i]); + } } } return by_region_per_copy_cache; } - - // This function takes the eec and appends its entities to either perimeters or infills of this Region (depending on the first parameter) // It also saves pointer to ExtruderPerCopy struct (for each entity), that holds information about which extruders should be used for which copy. -void GCode::ObjectByExtruder::Island::Region::append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copies_extruder, size_t object_copies_num) +void GCode::ObjectByExtruder::Island::Region::append(const Type type, const ExtrusionEntityCollection* eec, const WipingExtrusions::ExtruderPerCopy* copies_extruder) { // We are going to manipulate either perimeters or infills, exactly in the same way. Let's create pointers to the proper structure to not repeat ourselves: - ExtrusionEntityCollection* perimeters_or_infills = &infills; - std::vector* perimeters_or_infills_overrides = &infills_overrides; + ExtrusionEntityCollection* perimeters_or_infills; + std::vector* perimeters_or_infills_overrides; - if (type == "perimeters") { - perimeters_or_infills = &perimeters; - perimeters_or_infills_overrides = &perimeters_overrides; + switch (type) { + case PERIMETERS: + perimeters_or_infills = &perimeters; + perimeters_or_infills_overrides = &perimeters_overrides; + break; + case INFILL: + perimeters_or_infills = &infills; + perimeters_or_infills_overrides = &infills_overrides; + break; + default: + throw std::invalid_argument("Unknown parameter!"); } - else - if (type != "infills") { - throw std::invalid_argument("Unknown parameter!"); - return; - } - // First we append the entities, there are eec->entities.size() of them: + size_t old_size = perimeters_or_infills->entities.size(); perimeters_or_infills->append(eec->entities); - for (unsigned int i=0;ientities.size();++i) - perimeters_or_infills_overrides->push_back(copies_extruder); + if (copies_extruder != nullptr) { + perimeters_or_infills_overrides->reserve(old_size + eec->entities.size()); + perimeters_or_infills_overrides->resize(old_size, nullptr); + for (unsigned int i = 0; i < eec->entities.size(); ++ i) + perimeters_or_infills_overrides->emplace_back(copies_extruder); + } } } // namespace Slic3r diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 497dc8c718..934dcaeced 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -239,7 +239,6 @@ protected: std::string extrude_multi_path(ExtrusionMultiPath multipath, std::string description = "", double speed = -1.); std::string extrude_path(ExtrusionPath path, std::string description = "", double speed = -1.); - typedef std::vector ExtruderPerCopy; // Extruding multiple objects with soluble / non-soluble / combined supports // on a multi-material printer, trying to minimize tool switches. // Following structures sort extrusions by the extruder ID, by an order of objects and object islands. @@ -256,18 +255,23 @@ protected: ExtrusionEntityCollection perimeters; ExtrusionEntityCollection infills; - std::vector infills_overrides; - std::vector perimeters_overrides; + std::vector infills_overrides; + std::vector perimeters_overrides; + + enum Type { + PERIMETERS, + INFILL, + }; // Appends perimeter/infill entities and writes don't indices of those that are not to be extruder as part of perimeter/infill wiping - void append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copy_extruders, size_t object_copies_num); + void append(const Type type, const ExtrusionEntityCollection* eec, const WipingExtrusions::ExtruderPerCopy* copy_extruders); }; - std::vector by_region; // all extrusions for this island, grouped by regions - const std::vector& by_region_per_copy(unsigned int copy, int extruder, bool wiping_entities = false); // returns reference to subvector of by_region - private: - std::vector by_region_per_copy_cache; // caches vector generated by function above to avoid copying and recalculating + std::vector by_region; // all extrusions for this island, grouped by regions + + // Fills in by_region_per_copy_cache and returns its reference. + const std::vector& by_region_per_copy(std::vector &by_region_per_copy_cache, unsigned int copy, int extruder, bool wiping_entities = false) const; }; std::vector islands; }; @@ -277,7 +281,9 @@ protected: InstanceToPrint(ObjectByExtruder &object_by_extruder, size_t layer_id, const PrintObject &print_object, size_t instance_id) : object_by_extruder(object_by_extruder), layer_id(layer_id), print_object(print_object), instance_id(instance_id) {} - ObjectByExtruder &object_by_extruder; + // Repository + ObjectByExtruder &object_by_extruder; + // Index into std::vector, which contains Object and Support layers for the current print_z, collected for a single object, or for possibly multiple objects with multiple instances. const size_t layer_id; const PrintObject &print_object; // Instance idx of the copy of a print object. @@ -285,7 +291,8 @@ protected: }; std::vector sort_print_object_instances( - std::vector &objects_by_extruder, + std::vector &objects_by_extruder, + // Object and Support layers for the current print_z, collected for a single object, or for possibly multiple objects with multiple instances. const std::vector &layers, // Ordering must be defined for normal (non-sequential print). const std::vector> *ordering, diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index bcb65554d2..123656e435 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -19,7 +19,7 @@ namespace Slic3r { // Returns true in case that extruder a comes before b (b does not have to be present). False otherwise. bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const { - if (a==b) + if (a == b) return false; for (auto extruder : extruders) { @@ -415,10 +415,9 @@ void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsi { something_overridden = true; - auto entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector()))).first; // (add and) return iterator - auto& copies_vector = entity_map_it->second; - if (copies_vector.size() < num_of_copies) - copies_vector.resize(num_of_copies, -1); + auto entity_map_it = (entity_map.emplace(entity, ExtruderPerCopy())).first; // (add and) return iterator + ExtruderPerCopy& copies_vector = entity_map_it->second; + copies_vector.resize(num_of_copies, -1); if (copies_vector[copy_id] != -1) std::cout << "ERROR: Entity extruder overriden multiple times!!!\n"; // A debugging message - this must never happen. @@ -426,7 +425,6 @@ void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsi copies_vector[copy_id] = extruder; } - // Finds first non-soluble extruder on the layer int WipingExtrusions::first_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const { @@ -622,31 +620,23 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) } } - - - - - - -// Following function is called from process_layer and returns pointer to vector with information about which extruders should be used for given copy of this entity. -// It first makes sure the pointer is valid (creates the vector if it does not exist) and contains a record for each copy -// It also modifies the vector in place and changes all -1 to correct_extruder_id (at the time the overrides were created, correct extruders were not known, -// so -1 was used as "print as usual". -// The resulting vector has to keep track of which extrusions are the ones that were overridden and which were not. In the extruder is used as overridden, -// its number is saved as it is (zero-based index). Usual extrusions are saved as -number-1 (unfortunately there is no negative zero). -const std::vector* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies) +// Following function is called from GCode::process_layer and returns pointer to vector with information about which extruders should be used for given copy of this entity. +// If this extrusion does not have any override, nullptr is returned. +// Otherwise it modifies the vector in place and changes all -1 to correct_extruder_id (at the time the overrides were created, correct extruders were not known, +// so -1 was used as "print as usual"). +// The resulting vector therefore keeps track of which extrusions are the ones that were overridden and which were not. If the extruder used is overridden, +// its number is saved as is (zero-based index). Regular extrusions are saved as -number-1 (unfortunately there is no negative zero). +const WipingExtrusions::ExtruderPerCopy* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies) { + ExtruderPerCopy *overrides = nullptr; auto entity_map_it = entity_map.find(entity); - if (entity_map_it == entity_map.end()) - entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector()))).first; - - // Now the entity_map_it should be valid, let's make sure the vector is long enough: - entity_map_it->second.resize(num_of_copies, -1); - - // Each -1 now means "print as usual" - we will replace it with actual extruder id (shifted it so we don't lose that information): - std::replace(entity_map_it->second.begin(), entity_map_it->second.end(), -1, -correct_extruder_id-1); - - return &(entity_map_it->second); + if (entity_map_it != entity_map.end()) { + overrides = &entity_map_it->second; + overrides->resize(num_of_copies, -1); + // Each -1 now means "print as usual" - we will replace it with actual extruder id (shifted it so we don't lose that information): + std::replace(overrides->begin(), overrides->end(), -1, -correct_extruder_id-1); + } + return overrides; } diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp index c37c908239..dcb93c0211 100644 --- a/src/libslic3r/GCode/ToolOrdering.hpp +++ b/src/libslic3r/GCode/ToolOrdering.hpp @@ -7,6 +7,8 @@ #include +#include + namespace Slic3r { class Print; @@ -25,8 +27,19 @@ public: return something_overridden; } + // When allocating extruder overrides of an object's ExtrusionEntity, overrides for maximum 3 copies are allocated in place. + typedef boost::container::small_vector ExtruderPerCopy; + + class ExtruderOverrides + { + public: + ExtruderOverrides(const ExtruderPerCopy *overrides, const int correct_extruder_id) : m_overrides(overrides) {} + private: + const ExtruderPerCopy *m_overrides; + }; + // This is called from GCode::process_layer - see implementation for further comments: - const std::vector* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies); + const ExtruderPerCopy* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies); // This function goes through all infill entities, decides which ones will be used for wiping and // marks them by the extruder id. Returns volume that remains to be wiped on the wipe tower: @@ -50,7 +63,7 @@ private: return (entity_map.find(entity) == entity_map.end() ? false : entity_map.at(entity).at(copy_id) != -1); } - std::map> entity_map; // to keep track of who prints what + std::map entity_map; // to keep track of who prints what bool something_overridden = false; const LayerTools* m_layer_tools; // so we know which LayerTools object this belongs to }; @@ -74,6 +87,7 @@ public: bool operator==(const LayerTools &rhs) const { return print_z == rhs.print_z; } bool is_extruder_order(unsigned int a, unsigned int b) const; + bool has_extruder(unsigned int extruder) const { return std::find(this->extruders.begin(), this->extruders.end(), extruder) != this->extruders.end(); } coordf_t print_z; bool has_object; diff --git a/src/libslic3r/pchheader.hpp b/src/libslic3r/pchheader.hpp index 67b3d3a214..1339368bd4 100644 --- a/src/libslic3r/pchheader.hpp +++ b/src/libslic3r/pchheader.hpp @@ -63,6 +63,7 @@ #include #include #include +#include #include #include #include From 3e0690b37bff4af00015a3b393c39a129ab0d1cc Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 8 Jan 2020 15:16:31 +0100 Subject: [PATCH 07/33] Further optimizations of G-Code generator when Wipe into object / infill: Don't do unnecessary tests if it is known that there is no Wipe into object or infill active. --- src/libslic3r/GCode.cpp | 46 +++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 1e7fae3b53..f5e075a364 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1947,6 +1947,7 @@ void GCode::process_layer( // Group extrusions by an extruder, then by an object, an island and a region. std::map> by_extruder; + bool is_anything_overridden = const_cast(layer_tools).wiping_extrusions().is_anything_overridden(); for (const LayerToPrint &layer_to_print : layers) { if (layer_to_print.support_layer != nullptr) { const SupportLayer &support_layer = *layer_to_print.support_layer; @@ -2048,27 +2049,29 @@ void GCode::process_layer( int correct_extruder_id = Print::get_extruder(*extrusions, region); // Let's recover vector of extruder overrides: - const WipingExtrusions::ExtruderPerCopy *entity_overrides = const_cast(layer_tools).wiping_extrusions().get_extruder_overrides(extrusions, correct_extruder_id, layer_to_print.object()->copies().size()); - printing_extruders.clear(); - if (entity_overrides == nullptr) { - printing_extruders.emplace_back(correct_extruder_id); - } else { - printing_extruders.reserve(entity_overrides->size() + 1); - for (int extruder : *entity_overrides) - printing_extruders.emplace_back(extruder >= 0 ? - // at least one copy is overridden to use this extruder - extruder : - // at least one copy would normally be printed with this extruder (see get_extruder_overrides function for explanation) - static_cast(- extruder - 1)); - } - if (! layer_tools.has_extruder(correct_extruder_id)) { - // this entity is not overridden, but its extruder is not in layer_tools - we'll print it - // by last extruder on this layer (could happen e.g. when a wiping object is taller than others - dontcare extruders are eradicated from layer_tools) - printing_extruders.emplace_back(layer_tools.extruders.back()); - } - Slic3r::sort_remove_duplicates(printing_extruders); - if (printing_extruders.size() == 1 && printing_extruders.front() == correct_extruder_id) - entity_overrides = nullptr; + const WipingExtrusions::ExtruderPerCopy *entity_overrides = nullptr; + if (is_anything_overridden) { + printing_extruders.clear(); + if (! layer_tools.has_extruder(correct_extruder_id)) { + // this entity is not overridden, but its extruder is not in layer_tools - we'll print it + // by last extruder on this layer (could happen e.g. when a wiping object is taller than others - dontcare extruders are eradicated from layer_tools) + correct_extruder_id = layer_tools.extruders.back(); + } + entity_overrides = const_cast(layer_tools).wiping_extrusions().get_extruder_overrides(extrusions, correct_extruder_id, layer_to_print.object()->copies().size()); + if (entity_overrides == nullptr) { + printing_extruders.emplace_back(correct_extruder_id); + } else { + printing_extruders.reserve(entity_overrides->size()); + for (int extruder : *entity_overrides) + printing_extruders.emplace_back(extruder >= 0 ? + // at least one copy is overridden to use this extruder + extruder : + // at least one copy would normally be printed with this extruder (see get_extruder_overrides function for explanation) + static_cast(- extruder - 1)); + } + Slic3r::sort_remove_duplicates(printing_extruders); + } else + printing_extruders = { correct_extruder_id }; // Now we must add this extrusion into the by_extruder map, once for each extruder that will print it: for (unsigned int extruder : printing_extruders) @@ -2157,7 +2160,6 @@ void GCode::process_layer( std::vector instances_to_print = sort_print_object_instances(objects_by_extruder_it->second, layers, ordering, single_object_instance_idx); // We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature): - bool is_anything_overridden = const_cast(layer_tools).wiping_extrusions().is_anything_overridden(); std::vector by_region_per_copy_cache; for (int print_wipe_extrusions = is_anything_overridden; print_wipe_extrusions>=0; --print_wipe_extrusions) { if (is_anything_overridden && print_wipe_extrusions == 0) From 3d4cda8ace890fcd0cc63951d7b0274dab50662f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 8 Jan 2020 15:23:46 +0100 Subject: [PATCH 08/33] Fixed update of "Cost" field in "Sliced Info" box after a change of any material's options ("bottle_cost", "bottle_volume", "bottle_weight", "material_density") --- src/slic3r/GUI/Plater.cpp | 4 ++-- src/slic3r/GUI/Tab.cpp | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index fe50466be4..011d7e7ce3 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1192,9 +1192,9 @@ void Sidebar::update_sliced_info_sizer() { double material_cost = cfg->option("bottle_cost")->getFloat() / cfg->option("bottle_volume")->getFloat(); - str_total_cost = wxString::Format("%.2f", material_cost*(ps.objects_used_material + ps.support_used_material) / 1000); + str_total_cost = wxString::Format("%.3f", material_cost*(ps.objects_used_material + ps.support_used_material) / 1000); } - p->sliced_info->SetTextAndShow(siCost, str_total_cost); + p->sliced_info->SetTextAndShow(siCost, str_total_cost, "Cost"); wxString t_est = std::isnan(ps.estimated_print_time) ? "N/A" : get_time_dhms(float(ps.estimated_print_time)); p->sliced_info->SetTextAndShow(siEstimatedTime, t_est, _(L("Estimated printing time")) + " :"); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 353e822e3d..c518ede80d 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3448,7 +3448,7 @@ void TabSLAMaterial::build() new_conf.set_key_value("bottle_weight", new ConfigOptionFloat(new_bottle_weight)); } if (opt_key == "bottle_weight") { - double new_bottle_volume = boost::any_cast(value)/(new_conf.option("material_density")->getFloat() * 1000); + double new_bottle_volume = boost::any_cast(value)/new_conf.option("material_density")->getFloat() * 1000; new_conf.set_key_value("bottle_volume", new ConfigOptionFloat(new_bottle_volume)); } if (opt_key == "material_density") { @@ -3459,12 +3459,10 @@ void TabSLAMaterial::build() load_config(new_conf); update_dirty(); - on_value_change(opt_key, value); - if (opt_key == "bottle_volume" || opt_key == "bottle_cost") { - wxGetApp().sidebar().update_sliced_info_sizer(); - wxGetApp().sidebar().Layout(); - } + // Change of any from those options influences for an update of "Sliced Info" + wxGetApp().sidebar().update_sliced_info_sizer(); + wxGetApp().sidebar().Layout(); }; optgroup = page->new_optgroup(_(L("Layers"))); From 0aaa7dd076fba27ca7e4f24575e288f92b90662c Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 8 Jan 2020 15:44:39 +0100 Subject: [PATCH 09/33] Small fix for https://github.com/prusa3d/PrusaSlicer/commit/3e0690b37bff4af00015a3b393c39a129ab0d1cc --- src/libslic3r/GCode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index f5e075a364..3b2d2ca74c 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2071,7 +2071,7 @@ void GCode::process_layer( } Slic3r::sort_remove_duplicates(printing_extruders); } else - printing_extruders = { correct_extruder_id }; + printing_extruders = { (unsigned int)correct_extruder_id }; // Now we must add this extrusion into the by_extruder map, once for each extruder that will print it: for (unsigned int extruder : printing_extruders) From de70adca9cd70f7243eeb5c8f186f88b481453a8 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 9 Jan 2020 10:00:38 +0100 Subject: [PATCH 10/33] Optimization of G-code export: Don't make copies of ExtrusionEntities when sorting them into Extruders / Islands / Regions. --- src/libslic3r/ExtrusionEntityCollection.cpp | 32 ++++++++------------- src/libslic3r/ExtrusionEntityCollection.hpp | 4 ++- src/libslic3r/GCode.cpp | 27 ++++++++--------- src/libslic3r/GCode.hpp | 7 +++-- 4 files changed, 34 insertions(+), 36 deletions(-) diff --git a/src/libslic3r/ExtrusionEntityCollection.cpp b/src/libslic3r/ExtrusionEntityCollection.cpp index e1a9709d1a..7e0e1fc9c9 100644 --- a/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/src/libslic3r/ExtrusionEntityCollection.cpp @@ -74,31 +74,23 @@ void ExtrusionEntityCollection::remove(size_t i) this->entities.erase(this->entities.begin() + i); } -ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(const Point &start_near, ExtrusionRole role) const +ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(const ExtrusionEntitiesPtr& extrusion_entities, const Point &start_near, ExtrusionRole role) { ExtrusionEntityCollection out; - if (this->no_sort) { - out = *this; - } else { - if (role == erMixed) - out = *this; - else { - for (const ExtrusionEntity *ee : this->entities) { - if (role != erMixed) { - // The caller wants only paths with a specific extrusion role. - auto role2 = ee->role(); - if (role != role2) { - // This extrusion entity does not match the role asked. - assert(role2 != erMixed); - continue; - } - } - out.entities.emplace_back(ee->clone()); + for (const ExtrusionEntity *ee : extrusion_entities) { + if (role != erMixed) { + // The caller wants only paths with a specific extrusion role. + auto role2 = ee->role(); + if (role != role2) { + // This extrusion entity does not match the role asked. + assert(role2 != erMixed); + continue; } } - chain_and_reorder_extrusion_entities(out.entities, &start_near); + out.entities.emplace_back(ee->clone()); } - return out; + chain_and_reorder_extrusion_entities(out.entities, &start_near); + return out; } void ExtrusionEntityCollection::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp index 3084e5741a..e529bea02d 100644 --- a/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -65,7 +65,9 @@ public: } void replace(size_t i, const ExtrusionEntity &entity); void remove(size_t i); - ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = erMixed) const; + static ExtrusionEntityCollection chained_path_from(const ExtrusionEntitiesPtr &extrusion_entities, const Point &start_near, ExtrusionRole role = erMixed); + ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = erMixed) const + { return (this->no_sort || role == erMixed) ? *this : chained_path_from(this->entities, start_near, role); } void reverse(); const Point& first_point() const { return this->entities.front()->first_point(); } const Point& last_point() const { return this->entities.back()->last_point(); } diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 3b2d2ca74c..f6396e8f73 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2797,7 +2797,7 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vectorconfig()); - for (ExtrusionEntity *ee : region.perimeters.entities) + for (const ExtrusionEntity *ee : region.perimeters) gcode += this->extrude_entity(*ee, "perimeter", -1., &lower_layer_edge_grid); } return gcode; @@ -2809,7 +2809,9 @@ std::string GCode::extrude_infill(const Print &print, const std::vectorconfig()); - for (ExtrusionEntity *fill : region.infills.chained_path_from(m_last_pos).entities) { +// for (ExtrusionEntity *fill : ExtrusionEntityCollection::chained_path_from(region.infills, m_last_pos).entities) { + // Don't sort the infills, they contain gap fill, which shall be extruded after normal fills. + for (const ExtrusionEntity *fill : region.infills) { auto *eec = dynamic_cast(fill); if (eec) { for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos).entities) @@ -3300,8 +3302,8 @@ const std::vector& GCode::ObjectByExtru // Now we are going to iterate through perimeters and infills and pick ones that are supposed to be printed // References are used so that we don't have to repeat the same code for (int iter = 0; iter < 2; ++iter) { - const ExtrusionEntitiesPtr& entities = (iter ? reg.infills.entities : reg.perimeters.entities); - ExtrusionEntityCollection& target_eec = (iter ? by_region_per_copy_cache.back().infills : by_region_per_copy_cache.back().perimeters); + const ExtrusionEntitiesPtr& entities = (iter ? reg.infills : reg.perimeters); + ExtrusionEntitiesPtr& target_eec = (iter ? by_region_per_copy_cache.back().infills : by_region_per_copy_cache.back().perimeters); const std::vector& overrides = (iter ? reg.infills_overrides : reg.perimeters_overrides); // Now the most important thing - which extrusion should we print. @@ -3312,8 +3314,7 @@ const std::vector& GCode::ObjectByExtru const WipingExtrusions::ExtruderPerCopy *this_override = overrides[i]; // This copy (aka object instance) should be printed with this extruder, which overrides the default one. if (this_override != nullptr && (*this_override)[copy] == extruder) - // Clone the ExtrusionEntity. This is quite expensive. - target_eec.append((*entities[i])); + target_eec.emplace_back(entities[i]); } } else { // Apply normal extrusions (non-overrides) for this region. @@ -3322,12 +3323,10 @@ const std::vector& GCode::ObjectByExtru const WipingExtrusions::ExtruderPerCopy *this_override = overrides[i]; // This copy (aka object instance) should be printed with this extruder, which shall be equal to the default one. if (this_override == nullptr || (*this_override)[copy] == -extruder-1) - // Clone the ExtrusionEntity. This is quite expensive. - target_eec.append((*entities[i])); + target_eec.emplace_back(entities[i]); } for (; i < overrides.size(); ++ i) - // Clone the ExtrusionEntity. This is quite expensive. - target_eec.append(*entities[i]); + target_eec.emplace_back(entities[i]); } } } @@ -3339,7 +3338,7 @@ const std::vector& GCode::ObjectByExtru void GCode::ObjectByExtruder::Island::Region::append(const Type type, const ExtrusionEntityCollection* eec, const WipingExtrusions::ExtruderPerCopy* copies_extruder) { // We are going to manipulate either perimeters or infills, exactly in the same way. Let's create pointers to the proper structure to not repeat ourselves: - ExtrusionEntityCollection* perimeters_or_infills; + ExtrusionEntitiesPtr* perimeters_or_infills; std::vector* perimeters_or_infills_overrides; switch (type) { @@ -3356,8 +3355,10 @@ void GCode::ObjectByExtruder::Island::Region::append(const Type type, const Extr } // First we append the entities, there are eec->entities.size() of them: - size_t old_size = perimeters_or_infills->entities.size(); - perimeters_or_infills->append(eec->entities); + size_t old_size = perimeters_or_infills->size(); + perimeters_or_infills->reserve(perimeters_or_infills->size() + eec->entities.size()); + for (auto* ee : eec->entities) + perimeters_or_infills->emplace_back(ee); if (copies_extruder != nullptr) { perimeters_or_infills_overrides->reserve(old_size + eec->entities.size()); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 934dcaeced..e8bc88a297 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -252,8 +252,11 @@ protected: struct Island { struct Region { - ExtrusionEntityCollection perimeters; - ExtrusionEntityCollection infills; + // Non-owned references to LayerRegion::perimeters::entities + // std::vector would be better here, but there is no way in C++ to convert from std::vector std::vector without copying. + ExtrusionEntitiesPtr perimeters; + // Non-owned references to LayerRegion::fills::entities + ExtrusionEntitiesPtr infills; std::vector infills_overrides; std::vector perimeters_overrides; From 5bb9ba64c2760fc5832b0f733e40ee8de051015d Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 9 Jan 2020 10:14:50 +0100 Subject: [PATCH 11/33] Fix of previous commit --- src/libslic3r/GCode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index f6396e8f73..abfa87a75f 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2812,7 +2812,7 @@ std::string GCode::extrude_infill(const Print &print, const std::vector(fill); + auto *eec = dynamic_cast(fill); if (eec) { for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos).entities) gcode += this->extrude_entity(*ee, "infill"); From 63f8fda61d7222ca2e93aad8e47d32025e701ac5 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 9 Jan 2020 12:23:17 +0100 Subject: [PATCH 12/33] #3492 - Fix of export of toolpaths to obj --- src/slic3r/GUI/3DScene.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index bbfcabd36f..f6b1719dbf 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -877,13 +877,10 @@ bool can_export_to_obj(const GLVolume& volume) if (!volume.is_active || !volume.is_extrusion_path) return false; - if (volume.indexed_vertex_array.triangle_indices.empty() && (std::min(volume.indexed_vertex_array.triangle_indices_size, volume.tverts_range.second - volume.tverts_range.first) == 0)) - return false; + bool has_triangles = !volume.indexed_vertex_array.triangle_indices.empty() || (std::min(volume.indexed_vertex_array.triangle_indices_size, volume.tverts_range.second - volume.tverts_range.first) > 0); + bool has_quads = !volume.indexed_vertex_array.quad_indices.empty() || (std::min(volume.indexed_vertex_array.quad_indices_size, volume.qverts_range.second - volume.qverts_range.first) > 0); - if (volume.indexed_vertex_array.quad_indices.empty() && (std::min(volume.indexed_vertex_array.quad_indices_size, volume.qverts_range.second - volume.qverts_range.first) == 0)) - return false; - - return true; + return has_triangles || has_quads; } bool GLVolumeCollection::has_toolpaths_to_export() const From 7fd4800e7cbee720f735e22848fdc05960e5435f Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 9 Jan 2020 14:21:01 +0100 Subject: [PATCH 13/33] Fix wrong bounding box calculation for initial arrange item. --- src/libnest2d/include/libnest2d/placers/nfpplacer.hpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp index 0030287586..e44e1dae1e 100644 --- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp @@ -1116,12 +1116,8 @@ private: for(Item& item : items_) item.translate(d); } - void setInitialPosition(Item& item) { - auto sh = item.rawShape(); - sl::translate(sh, item.translation()); - sl::rotate(sh, item.rotation()); - - Box bb = sl::boundingBox(sh); + void setInitialPosition(Item& item) { + Box bb = item.boundingBox(); Vertex ci, cb; auto bbin = sl::boundingBox(bin_); From c0b90f68ab54bb327227ad0e0aed947ca46c8837 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 9 Jan 2020 17:12:40 +0100 Subject: [PATCH 14/33] force creating static library for libnest2d --- src/libnest2d/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libnest2d/CMakeLists.txt b/src/libnest2d/CMakeLists.txt index 6484da3d06..592ab069c3 100644 --- a/src/libnest2d/CMakeLists.txt +++ b/src/libnest2d/CMakeLists.txt @@ -24,7 +24,7 @@ set(LIBNEST2D_SRCFILES src/libnest2d.cpp ) -add_library(libnest2d ${LIBNEST2D_SRCFILES}) +add_library(libnest2d STATIC ${LIBNEST2D_SRCFILES}) target_include_directories(libnest2d PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) target_link_libraries(libnest2d PUBLIC clipper NLopt::nlopt TBB::tbb Boost::boost) From cc2b9b88495532415e9a688d3fe85839944b6bc4 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 9 Jan 2020 10:32:52 +0100 Subject: [PATCH 15/33] ToolOrdering: Removed unused parameter. --- src/libslic3r/GCode/ToolOrdering.cpp | 2 +- src/libslic3r/GCode/ToolOrdering.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 123656e435..15d7d368f2 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -111,7 +111,7 @@ void ToolOrdering::initialize_layers(std::vector &zs) coordf_t zmax = zs[i] + EPSILON; for (; j < zs.size() && zs[j] <= zmax; ++ j) ; // Assign an average print_z to the set of layers with nearly equal print_z. - m_layer_tools.emplace_back(LayerTools(0.5 * (zs[i] + zs[j-1]), m_print_config_ptr)); + m_layer_tools.emplace_back(LayerTools(0.5 * (zs[i] + zs[j-1]))); i = j; } } diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp index dcb93c0211..0597fcf7f0 100644 --- a/src/libslic3r/GCode/ToolOrdering.hpp +++ b/src/libslic3r/GCode/ToolOrdering.hpp @@ -73,7 +73,7 @@ private: class LayerTools { public: - LayerTools(const coordf_t z, const PrintConfig* print_config_ptr = nullptr) : + LayerTools(const coordf_t z) : print_z(z), has_object(false), has_support(false), From 15eedef74b5cd57f9a4189e5da54eac35336993c Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 10 Jan 2020 11:26:52 +0100 Subject: [PATCH 16/33] Refactoring of ToolOrdering (wipe into infill / object) Refactoring of GCode::_do_export() Helper lower_bound and search functions similar to std, but without needing the value object explicitely. --- src/libslic3r/GCode.cpp | 597 ++++++++++++++------------- src/libslic3r/GCode.hpp | 4 +- src/libslic3r/GCode/ToolOrdering.cpp | 73 ++-- src/libslic3r/GCode/ToolOrdering.hpp | 9 +- src/libslic3r/Print.hpp | 20 +- src/libslic3r/libslic3r.h | 47 +++ 6 files changed, 428 insertions(+), 322 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index abfa87a75f..c43d35fb49 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -792,6 +792,297 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ PROFILE_OUTPUT(debug_out_path("gcode-export-profile.txt").c_str()); } +// free functions called by GCode::_do_export() +namespace DoExport { + static void init_time_estimators(const PrintConfig &config, GCodeTimeEstimator &normal_time_estimator, GCodeTimeEstimator &silent_time_estimator, bool &silent_time_estimator_enabled) + { + // resets time estimators + normal_time_estimator.reset(); + normal_time_estimator.set_dialect(config.gcode_flavor); + normal_time_estimator.set_extrusion_axis(config.get_extrusion_axis()[0]); + silent_time_estimator_enabled = (config.gcode_flavor == gcfMarlin) && config.silent_mode; + + // Until we have a UI support for the other firmwares than the Marlin, use the hardcoded default values + // and let the user to enter the G-code limits into the start G-code. + // If the following block is enabled for other firmwares than the Marlin, then the function + // this->print_machine_envelope(file, print); + // shall be adjusted as well to produce a G-code block compatible with the particular firmware flavor. + if (config.gcode_flavor.value == gcfMarlin) { + normal_time_estimator.set_max_acceleration((float)config.machine_max_acceleration_extruding.values[0]); + normal_time_estimator.set_retract_acceleration((float)config.machine_max_acceleration_retracting.values[0]); + normal_time_estimator.set_minimum_feedrate((float)config.machine_min_extruding_rate.values[0]); + normal_time_estimator.set_minimum_travel_feedrate((float)config.machine_min_travel_rate.values[0]); + normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)config.machine_max_acceleration_x.values[0]); + normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)config.machine_max_acceleration_y.values[0]); + normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)config.machine_max_acceleration_z.values[0]); + normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)config.machine_max_acceleration_e.values[0]); + normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)config.machine_max_feedrate_x.values[0]); + normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)config.machine_max_feedrate_y.values[0]); + normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)config.machine_max_feedrate_z.values[0]); + normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)config.machine_max_feedrate_e.values[0]); + normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)config.machine_max_jerk_x.values[0]); + normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)config.machine_max_jerk_y.values[0]); + normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)config.machine_max_jerk_z.values[0]); + normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)config.machine_max_jerk_e.values[0]); + + if (silent_time_estimator_enabled) + { + silent_time_estimator.reset(); + silent_time_estimator.set_dialect(config.gcode_flavor); + silent_time_estimator.set_extrusion_axis(config.get_extrusion_axis()[0]); + /* "Stealth mode" values can be just a copy of "normal mode" values + * (when they aren't input for a printer preset). + * Thus, use back value from values, instead of second one, which could be absent + */ + silent_time_estimator.set_max_acceleration((float)config.machine_max_acceleration_extruding.values.back()); + silent_time_estimator.set_retract_acceleration((float)config.machine_max_acceleration_retracting.values.back()); + silent_time_estimator.set_minimum_feedrate((float)config.machine_min_extruding_rate.values.back()); + silent_time_estimator.set_minimum_travel_feedrate((float)config.machine_min_travel_rate.values.back()); + silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)config.machine_max_acceleration_x.values.back()); + silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)config.machine_max_acceleration_y.values.back()); + silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)config.machine_max_acceleration_z.values.back()); + silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)config.machine_max_acceleration_e.values.back()); + silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)config.machine_max_feedrate_x.values.back()); + silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)config.machine_max_feedrate_y.values.back()); + silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)config.machine_max_feedrate_z.values.back()); + silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)config.machine_max_feedrate_e.values.back()); + silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)config.machine_max_jerk_x.values.back()); + silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)config.machine_max_jerk_y.values.back()); + silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)config.machine_max_jerk_z.values.back()); + silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)config.machine_max_jerk_e.values.back()); + if (config.single_extruder_multi_material) { + // As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they + // are considered to be active for the single extruder multi-material printers only. + silent_time_estimator.set_filament_load_times(config.filament_load_time.values); + silent_time_estimator.set_filament_unload_times(config.filament_unload_time.values); + } + } + } + // Filament load / unload times are not specific to a firmware flavor. Let anybody use it if they find it useful. + if (config.single_extruder_multi_material) { + // As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they + // are considered to be active for the single extruder multi-material printers only. + normal_time_estimator.set_filament_load_times(config.filament_load_time.values); + normal_time_estimator.set_filament_unload_times(config.filament_unload_time.values); + } + } + + static void init_gcode_analyzer(const PrintConfig &config, GCodeAnalyzer &analyzer) + { + // resets analyzer + analyzer.reset(); + + // send extruder offset data to analyzer + GCodeAnalyzer::ExtruderOffsetsMap extruder_offsets; + unsigned int num_extruders = static_cast(config.nozzle_diameter.values.size()); + for (unsigned int extruder_id = 0; extruder_id < num_extruders; ++ extruder_id) + { + Vec2d offset = config.extruder_offset.get_at(extruder_id); + if (!offset.isApprox(Vec2d::Zero())) + extruder_offsets[extruder_id] = offset; + } + analyzer.set_extruder_offsets(extruder_offsets); + + // tell analyzer about the extrusion axis + analyzer.set_extrusion_axis(config.get_extrusion_axis()[0]); + + // send extruders count to analyzer to allow it to detect invalid extruder idxs + analyzer.set_extruders_count(num_extruders); + + // tell analyzer about the gcode flavor + analyzer.set_gcode_flavor(config.gcode_flavor); + } + + static double autospeed_volumetric_limit(const Print &print) + { + // get the minimum cross-section used in the print + std::vector mm3_per_mm; + for (auto object : print.objects()) { + for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { + const PrintRegion* region = print.regions()[region_id]; + for (auto layer : object->layers()) { + const LayerRegion* layerm = layer->regions()[region_id]; + if (region->config().get_abs_value("perimeter_speed") == 0 || + region->config().get_abs_value("small_perimeter_speed") == 0 || + region->config().get_abs_value("external_perimeter_speed") == 0 || + region->config().get_abs_value("bridge_speed") == 0) + mm3_per_mm.push_back(layerm->perimeters.min_mm3_per_mm()); + if (region->config().get_abs_value("infill_speed") == 0 || + region->config().get_abs_value("solid_infill_speed") == 0 || + region->config().get_abs_value("top_solid_infill_speed") == 0 || + region->config().get_abs_value("bridge_speed") == 0) + mm3_per_mm.push_back(layerm->fills.min_mm3_per_mm()); + } + } + if (object->config().get_abs_value("support_material_speed") == 0 || + object->config().get_abs_value("support_material_interface_speed") == 0) + for (auto layer : object->support_layers()) + mm3_per_mm.push_back(layer->support_fills.min_mm3_per_mm()); + } + // filter out 0-width segments + mm3_per_mm.erase(std::remove_if(mm3_per_mm.begin(), mm3_per_mm.end(), [](double v) { return v < 0.000001; }), mm3_per_mm.end()); + double volumetric_speed = 0.; + if (! mm3_per_mm.empty()) { + // In order to honor max_print_speed we need to find a target volumetric + // speed that we can use throughout the print. So we define this target + // volumetric speed as the volumetric speed produced by printing the + // smallest cross-section at the maximum speed: any larger cross-section + // will need slower feedrates. + volumetric_speed = *std::min_element(mm3_per_mm.begin(), mm3_per_mm.end()) * print.config().max_print_speed.value; + // limit such volumetric speed with max_volumetric_speed if set + if (print.config().max_volumetric_speed.value > 0) + volumetric_speed = std::min(volumetric_speed, print.config().max_volumetric_speed.value); + } + return volumetric_speed; + } + + static void init_ooze_prevention(const Print &print, OozePrevention &ooze_prevention) + { + // Calculate wiping points if needed + if (print.config().ooze_prevention.value && ! print.config().single_extruder_multi_material) { + Points skirt_points; + for (const ExtrusionEntity *ee : print.skirt().entities) + for (const ExtrusionPath &path : dynamic_cast(ee)->paths) + append(skirt_points, path.polyline.points); + if (! skirt_points.empty()) { + Polygon outer_skirt = Slic3r::Geometry::convex_hull(skirt_points); + Polygons skirts; + for (unsigned int extruder_id : print.extruders()) { + const Vec2d &extruder_offset = print.config().extruder_offset.get_at(extruder_id); + Polygon s(outer_skirt); + s.translate(Point::new_scale(-extruder_offset(0), -extruder_offset(1))); + skirts.emplace_back(std::move(s)); + } + ooze_prevention.enable = true; + ooze_prevention.standby_points = offset(Slic3r::Geometry::convex_hull(skirts), scale_(3.f)).front().equally_spaced_points(scale_(10.)); + #if 0 + require "Slic3r/SVG.pm"; + Slic3r::SVG::output( + "ooze_prevention.svg", + red_polygons => \@skirts, + polygons => [$outer_skirt], + points => $gcodegen->ooze_prevention->standby_points, + ); + #endif + } + } + } + + #if ENABLE_THUMBNAIL_GENERATOR + template + static void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, const std::vector &sizes, WriteToOutput &output, ThrowIfCanceledCallback throw_if_canceled) + { + // Write thumbnails using base64 encoding + if (thumbnail_cb != nullptr) + { + const size_t max_row_length = 78; + ThumbnailsList thumbnails; + thumbnail_cb(thumbnails, sizes, true, true, true, true); + for (const ThumbnailData& data : thumbnails) + { + if (data.is_valid()) + { + size_t png_size = 0; + void* png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)data.pixels.data(), data.width, data.height, 4, &png_size, MZ_DEFAULT_LEVEL, 1); + if (png_data != nullptr) + { + std::string encoded; + encoded.resize(boost::beast::detail::base64::encoded_size(png_size)); + encoded.resize(boost::beast::detail::base64::encode((void*)&encoded[0], (const void*)png_data, png_size)); + + output((boost::format("\n;\n; thumbnail begin %dx%d %d\n") % data.width % data.height % encoded.size()).str().c_str()); + + unsigned int row_count = 0; + while (encoded.size() > max_row_length) + { + output((boost::format("; %s\n") % encoded.substr(0, max_row_length)).str().c_str()); + encoded = encoded.substr(max_row_length); + ++row_count; + } + + if (encoded.size() > 0) + output((boost::format("; %s\n") % encoded).str().c_str()); + + output("; thumbnail end\n;\n"); + + mz_free(png_data); + } + } + throw_if_canceled(); + } + } + } + #endif // ENABLE_THUMBNAIL_GENERATOR + + // Fill in print_statistics and return formatted string containing filament statistics to be inserted into G-code comment section. + static std::string update_print_stats_and_format_filament_stats( + const GCodeTimeEstimator &normal_time_estimator, + const GCodeTimeEstimator &silent_time_estimator, + const bool silent_time_estimator_enabled, + const bool has_wipe_tower, + const WipeTowerData &wipe_tower_data, + const std::vector &extruders, + PrintStatistics &print_statistics) + { + std::string filament_stats_string_out; + + print_statistics.clear(); + print_statistics.estimated_normal_print_time = normal_time_estimator.get_time_dhms(); + print_statistics.estimated_silent_print_time = silent_time_estimator_enabled ? silent_time_estimator.get_time_dhms() : "N/A"; + print_statistics.estimated_normal_color_print_times = normal_time_estimator.get_color_times_dhms(true); + if (silent_time_estimator_enabled) + print_statistics.estimated_silent_color_print_times = silent_time_estimator.get_color_times_dhms(true); + print_statistics.total_toolchanges = std::max(0, wipe_tower_data.number_of_toolchanges); + if (! extruders.empty()) { + std::pair out_filament_used_mm ("; filament used [mm] = ", 0); + std::pair out_filament_used_cm3("; filament used [cm3] = ", 0); + std::pair out_filament_used_g ("; filament used [g] = ", 0); + std::pair out_filament_cost ("; filament cost = ", 0); + for (const Extruder &extruder : extruders) { + double used_filament = extruder.used_filament() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] : 0.f); + double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter + double filament_weight = extruded_volume * extruder.filament_density() * 0.001; + double filament_cost = filament_weight * extruder.filament_cost() * 0.001; + auto append = [&extruder, &extruders](std::pair &dst, const char *tmpl, double value) { + while (dst.second < extruder.id()) { + // Fill in the non-printing extruders with zeros. + dst.first += (dst.second > 0) ? ", 0" : "0"; + ++ dst.second; + } + if (dst.second > 0) + dst.first += ", "; + char buf[64]; + sprintf(buf, tmpl, value); + dst.first += buf; + ++ dst.second; + }; + print_statistics.filament_stats.insert(std::pair{extruder.id(), (float)used_filament}); + append(out_filament_used_mm, "%.1lf", used_filament); + append(out_filament_used_cm3, "%.1lf", extruded_volume * 0.001); + if (filament_weight > 0.) { + print_statistics.total_weight = print_statistics.total_weight + filament_weight; + append(out_filament_used_g, "%.1lf", filament_weight); + if (filament_cost > 0.) { + print_statistics.total_cost = print_statistics.total_cost + filament_cost; + append(out_filament_cost, "%.1lf", filament_cost); + } + } + print_statistics.total_used_filament += used_filament; + print_statistics.total_extruded_volume += extruded_volume; + print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.; + print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.; + } + filament_stats_string_out += out_filament_used_mm.first; + filament_stats_string_out += out_filament_used_cm3.first; + if (out_filament_used_g.second) + filament_stats_string_out += out_filament_used_g.first; + if (out_filament_cost.second) + filament_stats_string_out += out_filament_cost.first; + } + return filament_stats_string_out; + } +} + #if ENABLE_THUMBNAIL_GENERATOR void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb) #else @@ -800,99 +1091,10 @@ void GCode::_do_export(Print& print, FILE* file) { PROFILE_FUNC(); - // resets time estimators - m_normal_time_estimator.reset(); - m_normal_time_estimator.set_dialect(print.config().gcode_flavor); - m_normal_time_estimator.set_extrusion_axis(print.config().get_extrusion_axis()[0]); - m_silent_time_estimator_enabled = (print.config().gcode_flavor == gcfMarlin) && print.config().silent_mode; - - // Until we have a UI support for the other firmwares than the Marlin, use the hardcoded default values - // and let the user to enter the G-code limits into the start G-code. - // If the following block is enabled for other firmwares than the Marlin, then the function - // this->print_machine_envelope(file, print); - // shall be adjusted as well to produce a G-code block compatible with the particular firmware flavor. - if (print.config().gcode_flavor.value == gcfMarlin) { - m_normal_time_estimator.set_max_acceleration((float)print.config().machine_max_acceleration_extruding.values[0]); - m_normal_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values[0]); - m_normal_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values[0]); - m_normal_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values[0]); - m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values[0]); - m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values[0]); - m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values[0]); - m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values[0]); - m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values[0]); - m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values[0]); - m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values[0]); - m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values[0]); - m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values[0]); - m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values[0]); - m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values[0]); - m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values[0]); - - if (m_silent_time_estimator_enabled) - { - m_silent_time_estimator.reset(); - m_silent_time_estimator.set_dialect(print.config().gcode_flavor); - m_silent_time_estimator.set_extrusion_axis(print.config().get_extrusion_axis()[0]); - /* "Stealth mode" values can be just a copy of "normal mode" values - * (when they aren't input for a printer preset). - * Thus, use back value from values, instead of second one, which could be absent - */ - m_silent_time_estimator.set_max_acceleration((float)print.config().machine_max_acceleration_extruding.values.back()); - m_silent_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values.back()); - m_silent_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values.back()); - m_silent_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values.back()); - m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values.back()); - m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values.back()); - m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values.back()); - m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values.back()); - m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values.back()); - m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values.back()); - m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values.back()); - m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values.back()); - m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values.back()); - m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values.back()); - m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values.back()); - m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values.back()); - if (print.config().single_extruder_multi_material) { - // As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they - // are considered to be active for the single extruder multi-material printers only. - m_silent_time_estimator.set_filament_load_times(print.config().filament_load_time.values); - m_silent_time_estimator.set_filament_unload_times(print.config().filament_unload_time.values); - } - } - } - // Filament load / unload times are not specific to a firmware flavor. Let anybody use it if they find it useful. - if (print.config().single_extruder_multi_material) { - // As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they - // are considered to be active for the single extruder multi-material printers only. - m_normal_time_estimator.set_filament_load_times(print.config().filament_load_time.values); - m_normal_time_estimator.set_filament_unload_times(print.config().filament_unload_time.values); - } - - // resets analyzer - m_analyzer.reset(); - - // send extruder offset data to analyzer - GCodeAnalyzer::ExtruderOffsetsMap extruder_offsets; - for (unsigned int extruder_id : print.extruders()) - { - Vec2d offset = print.config().extruder_offset.get_at(extruder_id); - if (!offset.isApprox(Vec2d::Zero())) - extruder_offsets[extruder_id] = offset; - } - m_analyzer.set_extruder_offsets(extruder_offsets); - - // tell analyzer about the extrusion axis - m_analyzer.set_extrusion_axis(print.config().get_extrusion_axis()[0]); - - // send extruders count to analyzer to allow it to detect invalid extruder idxs - const ConfigOptionStrings* extruders_opt = dynamic_cast(print.config().option("extruder_colour")); - const ConfigOptionStrings* filamemts_opt = dynamic_cast(print.config().option("filament_colour")); - m_analyzer.set_extruders_count(std::max((unsigned int)extruders_opt->values.size(), (unsigned int)filamemts_opt->values.size())); - - // tell analyzer about the gcode flavor - m_analyzer.set_gcode_flavor(print.config().gcode_flavor); + DoExport::init_time_estimators(print.config(), + // modifies the following: + m_normal_time_estimator, m_silent_time_estimator, m_silent_time_estimator_enabled); + DoExport::init_gcode_analyzer(print.config(), m_analyzer); // resets analyzer's tracking data m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm; @@ -914,8 +1116,7 @@ void GCode::_do_export(Print& print, FILE* file) std::sort(zs.begin(), zs.end()); m_layer_count += (unsigned int)(object->copies().size() * (std::unique(zs.begin(), zs.end()) - zs.begin())); } - } - else { + } else { // Print all objects with the same print_z together. std::vector zs; for (auto object : print.objects()) { @@ -938,47 +1139,7 @@ void GCode::_do_export(Print& print, FILE* file) Model* model = print.get_object(0)->model_object()->get_model(); m_custom_gcode_per_print_z = model->custom_gcode_per_print_z; - // Initialize autospeed. - { - // get the minimum cross-section used in the print - std::vector mm3_per_mm; - for (auto object : print.objects()) { - for (size_t region_id = 0; region_id < object->region_volumes.size(); ++region_id) { - const PrintRegion* region = print.regions()[region_id]; - for (auto layer : object->layers()) { - const LayerRegion* layerm = layer->regions()[region_id]; - if (region->config().get_abs_value("perimeter_speed") == 0 || - region->config().get_abs_value("small_perimeter_speed") == 0 || - region->config().get_abs_value("external_perimeter_speed") == 0 || - region->config().get_abs_value("bridge_speed") == 0) - mm3_per_mm.push_back(layerm->perimeters.min_mm3_per_mm()); - if (region->config().get_abs_value("infill_speed") == 0 || - region->config().get_abs_value("solid_infill_speed") == 0 || - region->config().get_abs_value("top_solid_infill_speed") == 0 || - region->config().get_abs_value("bridge_speed") == 0) - mm3_per_mm.push_back(layerm->fills.min_mm3_per_mm()); - } - } - if (object->config().get_abs_value("support_material_speed") == 0 || - object->config().get_abs_value("support_material_interface_speed") == 0) - for (auto layer : object->support_layers()) - mm3_per_mm.push_back(layer->support_fills.min_mm3_per_mm()); - } - print.throw_if_canceled(); - // filter out 0-width segments - mm3_per_mm.erase(std::remove_if(mm3_per_mm.begin(), mm3_per_mm.end(), [](double v) { return v < 0.000001; }), mm3_per_mm.end()); - if (!mm3_per_mm.empty()) { - // In order to honor max_print_speed we need to find a target volumetric - // speed that we can use throughout the print. So we define this target - // volumetric speed as the volumetric speed produced by printing the - // smallest cross-section at the maximum speed: any larger cross-section - // will need slower feedrates. - m_volumetric_speed = *std::min_element(mm3_per_mm.begin(), mm3_per_mm.end()) * print.config().max_print_speed.value; - // limit such volumetric speed with max_volumetric_speed if set - if (print.config().max_volumetric_speed.value > 0) - m_volumetric_speed = std::min(m_volumetric_speed, print.config().max_volumetric_speed.value); - } - } + m_volumetric_speed = DoExport::autospeed_volumetric_limit(print); print.throw_if_canceled(); m_cooling_buffer = make_unique(*this); @@ -996,47 +1157,9 @@ void GCode::_do_export(Print& print, FILE* file) // Write information on the generator. _write_format(file, "; %s\n\n", Slic3r::header_slic3r_generated().c_str()); -#if ENABLE_THUMBNAIL_GENERATOR - // Write thumbnails using base64 encoding - if (thumbnail_cb != nullptr) - { - const size_t max_row_length = 78; - ThumbnailsList thumbnails; - thumbnail_cb(thumbnails, print.full_print_config().option("thumbnails")->values, true, true, true, true); - for (const ThumbnailData& data : thumbnails) - { - if (data.is_valid()) - { - size_t png_size = 0; - void* png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)data.pixels.data(), data.width, data.height, 4, &png_size, MZ_DEFAULT_LEVEL, 1); - if (png_data != nullptr) - { - std::string encoded; - encoded.resize(boost::beast::detail::base64::encoded_size(png_size)); - encoded.resize(boost::beast::detail::base64::encode((void*)&encoded[0], (const void*)png_data, png_size)); - - _write_format(file, "\n;\n; thumbnail begin %dx%d %d\n", data.width, data.height, encoded.size()); - - unsigned int row_count = 0; - while (encoded.size() > max_row_length) - { - _write_format(file, "; %s\n", encoded.substr(0, max_row_length).c_str()); - encoded = encoded.substr(max_row_length); - ++row_count; - } - - if (encoded.size() > 0) - _write_format(file, "; %s\n", encoded.c_str()); - - _write(file, "; thumbnail end\n;\n"); - - mz_free(png_data); - } - } - print.throw_if_canceled(); - } - } -#endif // ENABLE_THUMBNAIL_GENERATOR + DoExport::export_thumbnails_to_file(thumbnail_cb, print.full_print_config().option("thumbnails")->values, + [this, file](const char* sz) { this->_write(file, sz); }, + [&print]() { print.throw_if_canceled(); }); // Write notes (content of the Print Settings tab -> Notes) { @@ -1079,9 +1202,6 @@ void GCode::_do_export(Print& print, FILE* file) _writeln(file, GCodeTimeEstimator::Silent_First_M73_Output_Placeholder_Tag); } - // Hold total number of print toolchanges. Check for negative toolchanges (single extruder mode) and set to 0 (no tool change). - int total_toolchanges = std::max(0, print.wipe_tower_data().number_of_toolchanges); - // Prepare the helper object for replacing placeholders in custom G-code and output filename. m_placeholder_parser = print.placeholder_parser(); m_placeholder_parser.update_timestamp(); @@ -1187,7 +1307,8 @@ void GCode::_do_export(Print& print, FILE* file) // For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided. m_placeholder_parser.set("has_wipe_tower", has_wipe_tower); m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming); - m_placeholder_parser.set("total_toolchanges", total_toolchanges); + m_placeholder_parser.set("total_toolchanges", std::max(0, print.wipe_tower_data().number_of_toolchanges)); // Check for negative toolchanges (single extruder mode) and set to 0 (no tool change). + std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config().start_gcode.value, initial_extruder_id); // Set bed temperature if the start G-code does not contain any bed temp control G-codes. this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true); @@ -1195,12 +1316,8 @@ void GCode::_do_export(Print& print, FILE* file) this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, false); if (m_enable_analyzer) - { // adds tag for analyzer - char buf[32]; - sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erCustom); - _writeln(file, buf); - } + _write_format(file, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erCustom); // Write the custom start G-code _writeln(file, start_gcode); @@ -1228,35 +1345,8 @@ void GCode::_do_export(Print& print, FILE* file) } // Calculate wiping points if needed - if (print.config().ooze_prevention.value && ! print.config().single_extruder_multi_material) { - Points skirt_points; - for (const ExtrusionEntity *ee : print.skirt().entities) - for (const ExtrusionPath &path : dynamic_cast(ee)->paths) - append(skirt_points, path.polyline.points); - if (! skirt_points.empty()) { - Polygon outer_skirt = Slic3r::Geometry::convex_hull(skirt_points); - Polygons skirts; - for (unsigned int extruder_id : print.extruders()) { - const Vec2d &extruder_offset = print.config().extruder_offset.get_at(extruder_id); - Polygon s(outer_skirt); - s.translate(Point::new_scale(-extruder_offset(0), -extruder_offset(1))); - skirts.emplace_back(std::move(s)); - } - m_ooze_prevention.enable = true; - m_ooze_prevention.standby_points = - offset(Slic3r::Geometry::convex_hull(skirts), scale_(3.f)).front().equally_spaced_points(scale_(10.)); -#if 0 - require "Slic3r/SVG.pm"; - Slic3r::SVG::output( - "ooze_prevention.svg", - red_polygons => \@skirts, - polygons => [$outer_skirt], - points => $gcodegen->ooze_prevention->standby_points, - ); -#endif - } - print.throw_if_canceled(); - } + DoExport::init_ooze_prevention(print, m_ooze_prevention); + print.throw_if_canceled(); if (! (has_wipe_tower && print.config().single_extruder_multi_material_priming)) { // Set initial extruder only after custom start G-code. @@ -1387,12 +1477,8 @@ void GCode::_do_export(Print& print, FILE* file) _write(file, m_writer.set_fan(false)); if (m_enable_analyzer) - { // adds tag for analyzer - char buf[32]; - sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erCustom); - _writeln(file, buf); - } + _write_format(file, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erCustom); // Process filament-specific gcode in extruder order. { @@ -1432,60 +1518,13 @@ void GCode::_do_export(Print& print, FILE* file) m_silent_time_estimator.calculate_time(false); // Get filament stats. - print.m_print_statistics.clear(); - print.m_print_statistics.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms(); - print.m_print_statistics.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A"; - print.m_print_statistics.estimated_normal_color_print_times = m_normal_time_estimator.get_color_times_dhms(true); - if (m_silent_time_estimator_enabled) - print.m_print_statistics.estimated_silent_color_print_times = m_silent_time_estimator.get_color_times_dhms(true); - print.m_print_statistics.total_toolchanges = total_toolchanges; - std::vector extruders = m_writer.extruders(); - if (! extruders.empty()) { - std::pair out_filament_used_mm ("; filament used [mm] = ", 0); - std::pair out_filament_used_cm3("; filament used [cm3] = ", 0); - std::pair out_filament_used_g ("; filament used [g] = ", 0); - std::pair out_filament_cost ("; filament cost = ", 0); - for (const Extruder &extruder : extruders) { - double used_filament = extruder.used_filament() + (has_wipe_tower ? print.wipe_tower_data().used_filament[extruder.id()] : 0.f); - double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? print.wipe_tower_data().used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter - double filament_weight = extruded_volume * extruder.filament_density() * 0.001; - double filament_cost = filament_weight * extruder.filament_cost() * 0.001; - auto append = [&extruder, &extruders](std::pair &dst, const char *tmpl, double value) { - while (dst.second < extruder.id()) { - // Fill in the non-printing extruders with zeros. - dst.first += (dst.second > 0) ? ", 0" : "0"; - ++ dst.second; - } - if (dst.second > 0) - dst.first += ", "; - char buf[64]; - sprintf(buf, tmpl, value); - dst.first += buf; - ++ dst.second; - }; - print.m_print_statistics.filament_stats.insert(std::pair{extruder.id(), (float)used_filament}); - append(out_filament_used_mm, "%.1lf", used_filament); - append(out_filament_used_cm3, "%.1lf", extruded_volume * 0.001); - if (filament_weight > 0.) { - print.m_print_statistics.total_weight = print.m_print_statistics.total_weight + filament_weight; - append(out_filament_used_g, "%.1lf", filament_weight); - if (filament_cost > 0.) { - print.m_print_statistics.total_cost = print.m_print_statistics.total_cost + filament_cost; - append(out_filament_cost, "%.1lf", filament_cost); - } - } - print.m_print_statistics.total_used_filament += used_filament; - print.m_print_statistics.total_extruded_volume += extruded_volume; - print.m_print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.; - print.m_print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.; - } - _writeln(file, out_filament_used_mm.first); - _writeln(file, out_filament_used_cm3.first); - if (out_filament_used_g.second) - _writeln(file, out_filament_used_g.first); - if (out_filament_cost.second) - _writeln(file, out_filament_cost.first); - } + _write(file, DoExport::update_print_stats_and_format_filament_stats( + // Const inputs + m_normal_time_estimator, m_silent_time_estimator, m_silent_time_estimator_enabled, + has_wipe_tower, print.wipe_tower_data(), + m_writer.extruders(), + // Modifies + print.m_print_statistics)); _write_format(file, "; total filament used [g] = %.1lf\n", print.m_print_statistics.total_weight); _write_format(file, "; total filament cost = %.1lf\n", print.m_print_statistics.total_cost); if (print.m_print_statistics.total_toolchanges > 0) @@ -2015,7 +2054,7 @@ void GCode::process_layer( slices_test_order.reserve(n_slices); for (size_t i = 0; i < n_slices; ++ i) slices_test_order.emplace_back(i); - std::sort(slices_test_order.begin(), slices_test_order.end(), [&layer_surface_bboxes](int i, int j) { + std::sort(slices_test_order.begin(), slices_test_order.end(), [&layer_surface_bboxes](size_t i, size_t j) { const Vec2d s1 = layer_surface_bboxes[i].size().cast(); const Vec2d s2 = layer_surface_bboxes[j].size().cast(); return s1.x() * s1.y() < s2.x() * s2.y(); @@ -2188,7 +2227,7 @@ void GCode::process_layer( m_layer = layers[instance_to_print.layer_id].layer(); } for (ObjectByExtruder::Island &island : instance_to_print.object_by_extruder.islands) { - const auto& by_region_specific = is_anything_overridden ? island.by_region_per_copy(by_region_per_copy_cache, instance_to_print.instance_id, extruder_id, print_wipe_extrusions) : island.by_region; + const auto& by_region_specific = is_anything_overridden ? island.by_region_per_copy(by_region_per_copy_cache, static_cast(instance_to_print.instance_id), extruder_id, print_wipe_extrusions != 0) : island.by_region; //FIXME the following code prints regions in the order they are defined, the path is not optimized in any way. if (print.config().infill_first) { gcode += this->extrude_infill(print, by_region_specific); @@ -3277,7 +3316,7 @@ Point GCode::gcode_to_point(const Vec2d &point) const // Goes through by_region std::vector and returns reference to a subvector of entities, that are to be printed // during infill/perimeter wiping, or normally (depends on wiping_entities parameter) // Fills in by_region_per_copy_cache and returns its reference. -const std::vector& GCode::ObjectByExtruder::Island::by_region_per_copy(std::vector &by_region_per_copy_cache, unsigned int copy, int extruder, bool wiping_entities) const +const std::vector& GCode::ObjectByExtruder::Island::by_region_per_copy(std::vector &by_region_per_copy_cache, unsigned int copy, unsigned int extruder, bool wiping_entities) const { bool has_overrides = false; for (const auto& reg : by_region) @@ -3313,7 +3352,7 @@ const std::vector& GCode::ObjectByExtru for (unsigned int i = 0; i < overrides.size(); ++ i) { const WipingExtrusions::ExtruderPerCopy *this_override = overrides[i]; // This copy (aka object instance) should be printed with this extruder, which overrides the default one. - if (this_override != nullptr && (*this_override)[copy] == extruder) + if (this_override != nullptr && (*this_override)[copy] == int(extruder)) target_eec.emplace_back(entities[i]); } } else { @@ -3322,7 +3361,7 @@ const std::vector& GCode::ObjectByExtru for (; i < overrides.size(); ++ i) { const WipingExtrusions::ExtruderPerCopy *this_override = overrides[i]; // This copy (aka object instance) should be printed with this extruder, which shall be equal to the default one. - if (this_override == nullptr || (*this_override)[copy] == -extruder-1) + if (this_override == nullptr || (*this_override)[copy] == -int(extruder)-1) target_eec.emplace_back(entities[i]); } for (; i < overrides.size(); ++ i) diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index e8bc88a297..d5213d00ba 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -197,7 +197,7 @@ public: // append full config to the given string static void append_full_config(const Print& print, std::string& str); -protected: +private: #if ENABLE_THUMBNAIL_GENERATOR void _do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb); #else @@ -274,7 +274,7 @@ protected: std::vector by_region; // all extrusions for this island, grouped by regions // Fills in by_region_per_copy_cache and returns its reference. - const std::vector& by_region_per_copy(std::vector &by_region_per_copy_cache, unsigned int copy, int extruder, bool wiping_entities = false) const; + const std::vector& by_region_per_copy(std::vector &by_region_per_copy_cache, unsigned int copy, unsigned int extruder, bool wiping_entities = false) const; }; std::vector islands; }; diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 15d7d368f2..8aed420cbc 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -13,6 +13,8 @@ #include #include +#include + namespace Slic3r { @@ -150,14 +152,14 @@ void ToolOrdering::collect_extruders(const PrintObject &object) if (m_print_config_ptr) { // in this case complete_objects is false (see ToolOrdering constructors) something_nonoverriddable = false; for (const auto& eec : layerm->perimeters.entities) // let's check if there are nonoverriddable entities - if (!layer_tools.wiping_extrusions().is_overriddable(dynamic_cast(*eec), *m_print_config_ptr, object, region)) { + if (!layer_tools.wiping_extrusions().is_overriddable_and_mark(dynamic_cast(*eec), *m_print_config_ptr, object, region)) { something_nonoverriddable = true; break; } } if (something_nonoverriddable) - layer_tools.extruders.push_back(region.config().perimeter_extruder.value); + layer_tools.extruders.push_back(region.config().perimeter_extruder.value); layer_tools.has_object = true; } @@ -176,7 +178,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object) has_infill = true; if (m_print_config_ptr) { - if (!something_nonoverriddable && !layer_tools.wiping_extrusions().is_overriddable(*fill, *m_print_config_ptr, object, region)) + if (!something_nonoverriddable && !layer_tools.wiping_extrusions().is_overriddable_and_mark(*fill, *m_print_config_ptr, object, region)) something_nonoverriddable = true; } } @@ -463,7 +465,6 @@ bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, con return true; } - // Following function iterates through all extrusions on the layer, remembers those that could be used for wiping after toolchange // and returns volume that is left to be wiped on the wipe tower. float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int old_extruder, unsigned int new_extruder, float volume_to_wipe) @@ -471,7 +472,8 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int const LayerTools& lt = *m_layer_tools; const float min_infill_volume = 0.f; // ignore infill with smaller volume than this - if (print.config().filament_soluble.get_at(old_extruder) || print.config().filament_soluble.get_at(new_extruder)) + assert(volume_to_wipe >= 0.); + if (! this->something_overridable || volume_to_wipe == 0. || print.config().filament_soluble.get_at(old_extruder) || print.config().filament_soluble.get_at(new_extruder)) return volume_to_wipe; // Soluble filament cannot be wiped in a random infill, neither the filament after it // we will sort objects so that dedicated for wiping are at the beginning: @@ -495,13 +497,13 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int const PrintObject* object = object_list[i]; // Finds this layer: - auto this_layer_it = std::find_if(object->layers().begin(), object->layers().end(), [<](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)layers().end()) - continue; - const Layer* this_layer = *this_layer_it; + const Layer* this_layer = object->get_layer_at_printz(lt.print_z, EPSILON); + if (this_layer == nullptr) + continue; size_t num_of_copies = object->copies().size(); - for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves + // iterate through copies (aka PrintObject instances) first, so that we mark neighbouring infills to minimize travel moves + for (unsigned int copy = 0; copy < num_of_copies; ++copy) { for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { const auto& region = *object->print()->regions()[region_id]; @@ -509,18 +511,15 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int if (!region.config().wipe_into_infill && !object->config().wipe_into_objects) continue; - - if ((!print.config().infill_first ? perimeters_done : !perimeters_done) || (!object->config().wipe_into_objects && region.config().wipe_into_infill)) { + bool wipe_into_infill_only = ! object->config().wipe_into_objects && region.config().wipe_into_infill; + if (print.config().infill_first != perimeters_done || wipe_into_infill_only) { for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->fills.entities) { // iterate through all infill Collections auto* fill = dynamic_cast(ee); if (!is_overriddable(*fill, print.config(), *object, region)) continue; - if (volume_to_wipe<=0) - continue; - - if (!object->config().wipe_into_objects && !print.config().infill_first && region.config().wipe_into_infill) + if (wipe_into_infill_only && ! print.config().infill_first) // In this case we must check that the original extruder is used on this layer before the one we are overridding // (and the perimeters will be finished before the infill is printed): if (!lt.is_extruder_order(region.config().perimeter_extruder - 1, new_extruder)) @@ -528,32 +527,32 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) { // this infill will be used to wipe this extruder set_extruder_override(fill, copy, new_extruder, num_of_copies); - volume_to_wipe -= float(fill->total_volume()); + if ((volume_to_wipe -= float(fill->total_volume())) <= 0.f) + // More material was purged already than asked for. + return 0.f; } } } // Now the same for perimeters - see comments above for explanation: - if (object->config().wipe_into_objects && (print.config().infill_first ? perimeters_done : !perimeters_done)) + if (object->config().wipe_into_objects && print.config().infill_first == perimeters_done) { for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) { auto* fill = dynamic_cast(ee); - if (!is_overriddable(*fill, print.config(), *object, region)) - continue; - - if (volume_to_wipe<=0) - continue; - - if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) { + if (is_overriddable(*fill, print.config(), *object, region) && !is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume) { set_extruder_override(fill, copy, new_extruder, num_of_copies); - volume_to_wipe -= float(fill->total_volume()); + if ((volume_to_wipe -= float(fill->total_volume())) <= 0.f) + // More material was purged already than asked for. + return 0.f; } } } } } } - return std::max(0.f, volume_to_wipe); + // Some purge remains to be done on the Wipe Tower. + assert(volume_to_wipe > 0.); + return volume_to_wipe; } @@ -564,16 +563,18 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int // them again and make sure we override it. void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) { + if (! this->something_overridable) + return; + const LayerTools& lt = *m_layer_tools; unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config()); unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config()); for (const PrintObject* object : print.objects()) { // Finds this layer: - auto this_layer_it = std::find_if(object->layers().begin(), object->layers().end(), [<](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)layers().end()) - continue; - const Layer* this_layer = *this_layer_it; + const Layer* this_layer = object->get_layer_at_printz(lt.print_z, EPSILON); + if (this_layer == nullptr) + continue; size_t num_of_copies = object->copies().size(); for (size_t copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves @@ -597,8 +598,7 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) if (print.config().infill_first || object->config().wipe_into_objects // in this case the perimeter is overridden, so we can override by the last one safely || lt.is_extruder_order(region.config().perimeter_extruder - 1, last_nonsoluble_extruder // !infill_first, but perimeter is already printed when last extruder prints - || std::find(lt.extruders.begin(), lt.extruders.end(), region.config().infill_extruder - 1) == lt.extruders.end()) // we have to force override - this could violate infill_first (FIXME) - ) + || ! lt.has_extruder(region.config().infill_extruder - 1))) // we have to force override - this could violate infill_first (FIXME) set_extruder_override(fill, copy, (print.config().infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies); else { // In this case we can (and should) leave it to be printed normally. @@ -609,11 +609,8 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) // Now the same for perimeters - see comments above for explanation: for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) { // iterate through all perimeter Collections auto* fill = dynamic_cast(ee); - if (!is_overriddable(*fill, print.config(), *object, region) - || is_entity_overridden(fill, copy) ) - continue; - - set_extruder_override(fill, copy, (print.config().infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies); + if (is_overriddable(*fill, print.config(), *object, region) && ! is_entity_overridden(fill, copy)) + set_extruder_override(fill, copy, (print.config().infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies); } } } diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp index 0597fcf7f0..ed7aaab436 100644 --- a/src/libslic3r/GCode/ToolOrdering.hpp +++ b/src/libslic3r/GCode/ToolOrdering.hpp @@ -48,6 +48,11 @@ public: void ensure_perimeters_infills_order(const Print& print); bool is_overriddable(const ExtrusionEntityCollection& ee, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const; + bool is_overriddable_and_mark(const ExtrusionEntityCollection& ee, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) { + bool out = this->is_overriddable(ee, print_config, object, region); + this->something_overridable |= out; + return out; + } void set_layer_tools_ptr(const LayerTools* lt) { m_layer_tools = lt; } @@ -60,10 +65,12 @@ private: // Returns true in case that entity is not printed with its usual extruder for a given copy: bool is_entity_overridden(const ExtrusionEntity* entity, size_t copy_id) const { - return (entity_map.find(entity) == entity_map.end() ? false : entity_map.at(entity).at(copy_id) != -1); + auto it = entity_map.find(entity); + return it == entity_map.end() ? false : it->second[copy_id] != -1; } std::map entity_map; // to keep track of who prints what + bool something_overridable = false; bool something_overridden = false; const LayerTools* m_layer_tools; // so we know which LayerTools object this belongs to }; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 95c7b656a4..c434eec438 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -15,6 +15,8 @@ #include "GCode/ThumbnailData.hpp" #endif // ENABLE_THUMBNAIL_GENERATOR +#include "libslic3r.h" + namespace Slic3r { class Print; @@ -116,8 +118,21 @@ public: size_t total_layer_count() const { return this->layer_count() + this->support_layer_count(); } size_t layer_count() const { return m_layers.size(); } void clear_layers(); - Layer* get_layer(int idx) { return m_layers[idx]; } - const Layer* get_layer(int idx) const { return m_layers[idx]; } + const Layer* get_layer(int idx) const { return m_layers[idx]; } + Layer* get_layer(int idx) { return m_layers[idx]; } + // Get a layer exactly at print_z. + const Layer* get_layer_at_printz(coordf_t print_z) const { + auto it = Slic3r::lower_bound_by_predicate(m_layers.begin(), m_layers.end(), [print_z](const Layer *layer) { return layer->print_z < print_z; }); + return (it == m_layers.end() || (*it)->print_z != print_z) ? nullptr : *it; + } + Layer* get_layer_at_printz(coordf_t print_z) { return const_cast(std::as_const(*this).get_layer_at_printz(print_z)); } + // Get a layer approximately at print_z. + const Layer* get_layer_at_printz(coordf_t print_z, coordf_t epsilon) const { + coordf_t limit = print_z + epsilon; + auto it = Slic3r::lower_bound_by_predicate(m_layers.begin(), m_layers.end(), [limit](const Layer *layer) { return layer->print_z < limit; }); + return (it == m_layers.end() || (*it)->print_z < print_z - epsilon) ? nullptr : *it; + } + Layer* get_layer_at_printz(coordf_t print_z, coordf_t epsilon) { return const_cast(std::as_const(*this).get_layer_at_printz(print_z, epsilon)); } // print_z: top of the layer; slice_z: center of the layer. Layer* add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z); @@ -345,6 +360,7 @@ public: const PrintConfig& config() const { return m_config; } const PrintObjectConfig& default_object_config() const { return m_default_object_config; } const PrintRegionConfig& default_region_config() const { return m_default_region_config; } + //FIXME returning const vector to non-const PrintObject*, caller could modify PrintObjects! const PrintObjectPtrs& objects() const { return m_objects; } PrintObject* get_object(size_t idx) { return m_objects[idx]; } const PrintObject* get_object(size_t idx) const { return m_objects[idx]; } diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index afbf94fa39..d3e4992ce8 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -158,6 +158,53 @@ inline std::unique_ptr make_unique(Args&&... args) { return std::unique_ptr(new T(std::forward(args)...)); } +// Variant of std::lower_bound() with compare predicate, but without the key. +// This variant is very useful in case that the T type is large or it does not even have a public constructor. +template +ForwardIt lower_bound_by_predicate(ForwardIt first, ForwardIt last, LowerThanKeyPredicate lower_thank_key) +{ + ForwardIt it; + typename std::iterator_traits::difference_type count, step; + count = std::distance(first, last); + + while (count > 0) { + it = first; + step = count / 2; + std::advance(it, step); + if (lower_thank_key(*it)) { + first = ++it; + count -= step + 1; + } + else + count = step; + } + return first; +} + +// from https://en.cppreference.com/w/cpp/algorithm/lower_bound +template> +ForwardIt binary_find(ForwardIt first, ForwardIt last, const T& value, Compare comp={}) +{ + // Note: BOTH type T and the type after ForwardIt is dereferenced + // must be implicitly convertible to BOTH Type1 and Type2, used in Compare. + // This is stricter than lower_bound requirement (see above) + + first = std::lower_bound(first, last, value, comp); + return first != last && !comp(value, *first) ? first : last; +} + +// from https://en.cppreference.com/w/cpp/algorithm/lower_bound +template +ForwardIt binary_find_by_predicate(ForwardIt first, ForwardIt last, LowerThanKeyPredicate lower_thank_key, EqualToKeyPredicate equal_to_key) +{ + // Note: BOTH type T and the type after ForwardIt is dereferenced + // must be implicitly convertible to BOTH Type1 and Type2, used in Compare. + // This is stricter than lower_bound requirement (see above) + + first = lower_bound_by_predicate(first, last, lower_thank_key); + return first != last && equal_to_key(*first) ? first : last; +} + template static inline T sqr(T x) { From 702a489b038bcde81c3eb79393586e460e8b8660 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 10 Jan 2020 11:53:39 +0100 Subject: [PATCH 17/33] Fixing compilation isue on non-MS compilers. --- src/libslic3r/GCode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index c43d35fb49..7bd3525373 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -970,7 +970,7 @@ namespace DoExport { #if ENABLE_THUMBNAIL_GENERATOR template - static void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, const std::vector &sizes, WriteToOutput &output, ThrowIfCanceledCallback throw_if_canceled) + static void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, const std::vector &sizes, WriteToOutput output, ThrowIfCanceledCallback throw_if_canceled) { // Write thumbnails using base64 encoding if (thumbnail_cb != nullptr) From 9038dc21e85d1aa956fcb0429659884d028dee66 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 10 Jan 2020 15:51:35 +0100 Subject: [PATCH 18/33] Fixing regression issue after G-code export refactoring. --- src/libslic3r/ExtrusionEntityCollection.cpp | 39 ++++++++++++++------- src/libslic3r/ExtrusionEntityCollection.hpp | 19 ++++++++-- src/libslic3r/GCode.cpp | 6 ++-- 3 files changed, 46 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/ExtrusionEntityCollection.cpp b/src/libslic3r/ExtrusionEntityCollection.cpp index 7e0e1fc9c9..147a60d95a 100644 --- a/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/src/libslic3r/ExtrusionEntityCollection.cpp @@ -6,6 +6,26 @@ namespace Slic3r { +void filter_by_extrusion_role_in_place(ExtrusionEntitiesPtr &extrusion_entities, ExtrusionRole role) +{ + if (role != erMixed) { + auto first = extrusion_entities.begin(); + auto last = extrusion_entities.end(); + auto result = first; + while (first != last) { + // The caller wants only paths with a specific extrusion role. + auto role2 = (*first)->role(); + if (role != role2) { + // This extrusion entity does not match the role asked. + assert(role2 != erMixed); + *result = *first; + ++ result; + } + ++ first; + } + } +} + ExtrusionEntityCollection::ExtrusionEntityCollection(const ExtrusionPaths &paths) : no_sort(false) { @@ -76,19 +96,12 @@ void ExtrusionEntityCollection::remove(size_t i) ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(const ExtrusionEntitiesPtr& extrusion_entities, const Point &start_near, ExtrusionRole role) { - ExtrusionEntityCollection out; - for (const ExtrusionEntity *ee : extrusion_entities) { - if (role != erMixed) { - // The caller wants only paths with a specific extrusion role. - auto role2 = ee->role(); - if (role != role2) { - // This extrusion entity does not match the role asked. - assert(role2 != erMixed); - continue; - } - } - out.entities.emplace_back(ee->clone()); - } + // Return a filtered copy of the collection. + ExtrusionEntityCollection out; + out.entities = filter_by_extrusion_role(extrusion_entities, role); + // Clone the extrusion entities. + for (auto &ptr : out.entities) + ptr = ptr->clone(); chain_and_reorder_extrusion_entities(out.entities, &start_near); return out; } diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp index e529bea02d..8aacb4d46f 100644 --- a/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -6,6 +6,21 @@ namespace Slic3r { +// Remove those items from extrusion_entities, that do not match role. +// Do nothing if role is mixed. +// Removed elements are NOT being deleted. +void filter_by_extrusion_role_in_place(ExtrusionEntitiesPtr &extrusion_entities, ExtrusionRole role); + +// Return new vector of ExtrusionEntities* with only those items from input extrusion_entities, that match role. +// Return all extrusion entities if role is mixed. +// Returned extrusion entities are shared with the source vector, they are NOT cloned, they are considered to be owned by extrusion_entities. +inline ExtrusionEntitiesPtr filter_by_extrusion_role(const ExtrusionEntitiesPtr &extrusion_entities, ExtrusionRole role) +{ + ExtrusionEntitiesPtr out { extrusion_entities }; + filter_by_extrusion_role_in_place(out, role); + return out; +} + class ExtrusionEntityCollection : public ExtrusionEntity { public: @@ -67,7 +82,7 @@ public: void remove(size_t i); static ExtrusionEntityCollection chained_path_from(const ExtrusionEntitiesPtr &extrusion_entities, const Point &start_near, ExtrusionRole role = erMixed); ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = erMixed) const - { return (this->no_sort || role == erMixed) ? *this : chained_path_from(this->entities, start_near, role); } + { return this->no_sort ? *this : chained_path_from(this->entities, start_near, role); } void reverse(); const Point& first_point() const { return this->entities.front()->first_point(); } const Point& last_point() const { return this->entities.back()->last_point(); } @@ -107,6 +122,6 @@ public: } }; -} +} // namespace Slic3r #endif diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 7bd3525373..942558f272 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2848,9 +2848,9 @@ std::string GCode::extrude_infill(const Print &print, const std::vectorconfig()); -// for (ExtrusionEntity *fill : ExtrusionEntityCollection::chained_path_from(region.infills, m_last_pos).entities) { - // Don't sort the infills, they contain gap fill, which shall be extruded after normal fills. - for (const ExtrusionEntity *fill : region.infills) { + ExtrusionEntitiesPtr extrusions { region.infills }; + chain_and_reorder_extrusion_entities(extrusions, &m_last_pos); + for (const ExtrusionEntity *fill : extrusions) { auto *eec = dynamic_cast(fill); if (eec) { for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos).entities) From fdb1d2c9dec923d3b2862a44ee0519348de4bba9 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 13 Jan 2020 08:47:11 +0100 Subject: [PATCH 19/33] Follow - up of abd432e7a8f91bba876a29c857844c93dab4b762->Set default as non fullpath export --- src/slic3r/GUI/AppConfig.cpp | 2 +- src/slic3r/GUI/ConfigWizard.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp index bcd79274f9..d277fc095a 100644 --- a/src/slic3r/GUI/AppConfig.cpp +++ b/src/slic3r/GUI/AppConfig.cpp @@ -63,7 +63,7 @@ void AppConfig::set_defaults() #if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF if (get("export_sources_full_pathnames").empty()) - set("export_sources_full_pathnames", "1"); + set("export_sources_full_pathnames", "0"); #endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF // remove old 'use_legacy_opengl' parameter from this config, if present diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 75d170b03d..8a4b28d3a4 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -768,7 +768,7 @@ PageUpdate::PageUpdate(ConfigWizard *parent) #if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF PageReloadFromDisk::PageReloadFromDisk(ConfigWizard* parent) : ConfigWizardPage(parent, _(L("Reload from disk")), _(L("Reload from disk"))) - , full_pathnames(true) + , full_pathnames(false) { auto* box_pathnames = new wxCheckBox(this, wxID_ANY, _(L("Export full pathnames of models and parts sources into 3mf and amf files"))); box_pathnames->SetValue(wxGetApp().app_config->get("export_sources_full_pathnames") == "1"); From 91acbd01eddbe78c072216bf7f93f5045842e060 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 13 Jan 2020 15:27:37 +0100 Subject: [PATCH 20/33] Fixed GCodeAnalyzer and GCodeTimeEstimator to avoid artifacts while rendering toolpaths due to numerical issues on extruder coordinate --- src/libslic3r/GCode/Analyzer.cpp | 16 ++++++++-------- src/libslic3r/GCodeTimeEstimator.cpp | 4 +++- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp index 7b8004ab05..c70abbbbdd 100644 --- a/src/libslic3r/GCode/Analyzer.cpp +++ b/src/libslic3r/GCode/Analyzer.cpp @@ -311,24 +311,22 @@ void GCodeAnalyzer::_processG1(const GCodeReader::GCodeLine& line) { auto axis_absolute_position = [this](GCodeAnalyzer::EAxis axis, const GCodeReader::GCodeLine& lineG1) -> float { - float current_absolute_position = _get_axis_position(axis); - float current_origin = _get_axis_origin(axis); - float lengthsScaleFactor = (_get_units() == GCodeAnalyzer::Inches) ? INCHES_TO_MM : 1.0f; - bool is_relative = (_get_global_positioning_type() == Relative); if (axis == E) is_relative |= (_get_e_local_positioning_type() == Relative); if (lineG1.has(Slic3r::Axis(axis))) { + float lengthsScaleFactor = (_get_units() == GCodeAnalyzer::Inches) ? INCHES_TO_MM : 1.0f; float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor; - return is_relative ? current_absolute_position + ret : ret + current_origin; + return is_relative ? _get_axis_position(axis) + ret : _get_axis_origin(axis) + ret; } else - return current_absolute_position; + return _get_axis_position(axis); }; // updates axes positions from line + float new_pos[Num_Axis]; for (unsigned char a = X; a < Num_Axis; ++a) { @@ -352,7 +350,7 @@ void GCodeAnalyzer::_processG1(const GCodeReader::GCodeLine& line) if (delta_pos[E] < 0.0f) { if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f) || (delta_pos[Z] != 0.0f)) - type = GCodeMove::Move; + type = GCodeMove::Move; else type = GCodeMove::Retract; } @@ -440,7 +438,9 @@ void GCodeAnalyzer::_processG92(const GCodeReader::GCodeLine& line) if (line.has_e()) { - _set_axis_origin(E, _get_axis_position(E) - line.e() * lengthsScaleFactor); + // extruder coordinate can grow to the point where its float representation does not allow for proper addition with small increments, + // we set the value taken from the G92 line as the new current position for it + _set_axis_position(E, line.e() * lengthsScaleFactor); anyFound = true; } diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp index c624c0fce8..36127cad77 100644 --- a/src/libslic3r/GCodeTimeEstimator.cpp +++ b/src/libslic3r/GCodeTimeEstimator.cpp @@ -1261,7 +1261,9 @@ namespace Slic3r { if (line.has_e()) { - set_axis_origin(E, get_axis_position(E) - line.e() * lengthsScaleFactor); + // extruder coordinate can grow to the point where its float representation does not allow for proper addition with small increments, + // we set the value taken from the G92 line as the new current position for it + set_axis_position(E, line.e() * lengthsScaleFactor); anyFound = true; } else From 19f0f50e9816bbef8f328a4e6d48324c3b44cfd5 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 13 Jan 2020 15:57:31 +0100 Subject: [PATCH 21/33] MSVC specific: Disable STL4007: Many result_type typedefs and all argument_type, first_argument_type, and second_argument_type typedefs are deprecated in C++17. Remove this warning disable after eigen library adapts to the new C++17 adaptor rules. --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bca5a71aa..f31c0eac1e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,9 @@ if (MSVC) # error C3859: virtual memory range for PCH exceeded; please recompile with a command line option of '-Zm90' or greater # Generate symbols at every build target, even for the release. add_compile_options(-bigobj -Zm520 /Zi) + # Disable STL4007: Many result_type typedefs and all argument_type, first_argument_type, and second_argument_type typedefs are deprecated in C++17. + #FIXME Remove this line after eigen library adapts to the new C++17 adaptor rules. + add_compile_options(-D_SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING) endif () if (MINGW) From 9a3901e159b618e3e708f40b38cb9a883d592902 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 13 Jan 2020 16:01:01 +0100 Subject: [PATCH 22/33] Support for new "default_materials" property at PrinterModel of system profiles. The "default_materials" key accepts "default_filaments" as well. --- src/slic3r/GUI/3DBed.cpp | 18 ++---------------- src/slic3r/GUI/Preset.cpp | 24 ++++++++++++++++++++++++ src/slic3r/GUI/Preset.hpp | 6 ++++++ 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index ecbca42286..c66c8efdd3 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -341,24 +341,10 @@ void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox) printf("Unable to create bed grid lines\n"); } -static const VendorProfile::PrinterModel* system_printer_model(const Preset &preset) -{ - const VendorProfile::PrinterModel *out = nullptr; - if (preset.vendor != nullptr) { - auto *printer_model = preset.config.opt("printer_model"); - if (printer_model != nullptr && ! printer_model->value.empty()) { - auto it = std::find_if(preset.vendor->models.begin(), preset.vendor->models.end(), [printer_model](const VendorProfile::PrinterModel &pm) { return pm.id == printer_model->value; }); - if (it != preset.vendor->models.end()) - out = &(*it); - } - } - return out; -} - static std::string system_print_bed_model(const Preset &preset) { std::string out; - const VendorProfile::PrinterModel *pm = system_printer_model(preset); + const VendorProfile::PrinterModel *pm = PresetUtils::system_printer_model(preset); if (pm != nullptr && ! pm->bed_model.empty()) out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_model; return out; @@ -367,7 +353,7 @@ static std::string system_print_bed_model(const Preset &preset) static std::string system_print_bed_texture(const Preset &preset) { std::string out; - const VendorProfile::PrinterModel *pm = system_printer_model(preset); + const VendorProfile::PrinterModel *pm = PresetUtils::system_printer_model(preset); if (pm != nullptr && ! pm->bed_texture.empty()) out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_texture; return out; diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index c20a5bb281..b89000891d 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -184,6 +184,14 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem } else { BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Malformed variants field: `%2%`") % id % variants_field; } + auto default_materials_field = section.second.get("default_materials", ""); + if (default_materials_field.empty()) + default_materials_field = section.second.get("default_filaments", ""); + if (Slic3r::unescape_strings_cstyle(default_materials_field, model.default_materials)) { + Slic3r::sort_remove_duplicates(model.default_materials); + } else { + BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Malformed default_materials field: `%2%`") % id % default_materials_field; + } model.bed_model = section.second.get("bed_model", ""); model.bed_texture = section.second.get("bed_texture", ""); if (! model.id.empty() && ! model.variants.empty()) @@ -1502,4 +1510,20 @@ const Preset* PrinterPresetCollection::find_by_model_id(const std::string &model return it != cend() ? &*it : nullptr; } +namespace PresetUtils { + const VendorProfile::PrinterModel* system_printer_model(const Preset &preset) + { + const VendorProfile::PrinterModel *out = nullptr; + if (preset.vendor != nullptr) { + auto *printer_model = preset.config.opt("printer_model"); + if (printer_model != nullptr && ! printer_model->value.empty()) { + auto it = std::find_if(preset.vendor->models.begin(), preset.vendor->models.end(), [printer_model](const VendorProfile::PrinterModel &pm) { return pm.id == printer_model->value; }); + if (it != preset.vendor->models.end()) + out = &(*it); + } + } + return out; + } +} // namespace PresetUtils + } // namespace Slic3r diff --git a/src/slic3r/GUI/Preset.hpp b/src/slic3r/GUI/Preset.hpp index 19d07d0077..0a26f90ccf 100644 --- a/src/slic3r/GUI/Preset.hpp +++ b/src/slic3r/GUI/Preset.hpp @@ -61,6 +61,7 @@ public: PrinterTechnology technology; std::string family; std::vector variants; + std::vector default_materials; // Vendor & Printer Model specific print bed model & texture. std::string bed_model; std::string bed_texture; @@ -563,6 +564,11 @@ public: const Preset* find_by_model_id(const std::string &model_id) const; }; +namespace PresetUtils { + // PrinterModel of a system profile, from which this preset is derived, or null if it is not derived from a system profile. + const VendorProfile::PrinterModel* system_printer_model(const Preset &preset); +} // namespace PresetUtils + } // namespace Slic3r #endif /* slic3r_Preset_hpp_ */ From 79d7a0130f687a3814d5061cafdf8501338909c0 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 13 Jan 2020 17:41:40 +0100 Subject: [PATCH 23/33] Fixing some missing throw statements. Adding noexcept to move constructors / operators. --- src/libslic3r/Config.hpp | 18 +++++++++--------- src/libslic3r/ExPolygon.hpp | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index e7ecc29778..9b04ae0269 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -500,7 +500,7 @@ public: if (NULLABLE) this->values.push_back(nil_value()); else - std::runtime_error("Deserializing nil into a non-nullable object"); + throw std::runtime_error("Deserializing nil into a non-nullable object"); } else { std::istringstream iss(item_str); double value; @@ -525,9 +525,9 @@ protected: if (NULLABLE) ss << "nil"; else - std::runtime_error("Serializing NaN"); + throw std::runtime_error("Serializing NaN"); } else - std::runtime_error("Serializing invalid number"); + throw std::runtime_error("Serializing invalid number"); } static bool vectors_equal(const std::vector &v1, const std::vector &v2) { if (NULLABLE) { @@ -646,7 +646,7 @@ public: if (NULLABLE) this->values.push_back(nil_value()); else - std::runtime_error("Deserializing nil into a non-nullable object"); + throw std::runtime_error("Deserializing nil into a non-nullable object"); } else { std::istringstream iss(item_str); int value; @@ -663,7 +663,7 @@ private: if (NULLABLE) ss << "nil"; else - std::runtime_error("Serializing NaN"); + throw std::runtime_error("Serializing NaN"); } else ss << v; } @@ -1126,7 +1126,7 @@ public: if (NULLABLE) this->values.push_back(nil_value()); else - std::runtime_error("Deserializing nil into a non-nullable object"); + throw std::runtime_error("Deserializing nil into a non-nullable object"); } else this->values.push_back(item_str.compare("1") == 0); } @@ -1139,7 +1139,7 @@ protected: if (NULLABLE) ss << "nil"; else - std::runtime_error("Serializing NaN"); + throw std::runtime_error("Serializing NaN"); } else ss << (v ? "1" : "0"); } @@ -1638,7 +1638,7 @@ class DynamicConfig : public virtual ConfigBase public: DynamicConfig() {} DynamicConfig(const DynamicConfig &rhs) { *this = rhs; } - DynamicConfig(DynamicConfig &&rhs) : options(std::move(rhs.options)) { rhs.options.clear(); } + DynamicConfig(DynamicConfig &&rhs) noexcept : options(std::move(rhs.options)) { rhs.options.clear(); } explicit DynamicConfig(const ConfigBase &rhs, const t_config_option_keys &keys); explicit DynamicConfig(const ConfigBase& rhs) : DynamicConfig(rhs, rhs.keys()) {} virtual ~DynamicConfig() override { clear(); } @@ -1656,7 +1656,7 @@ public: // Move a content of one DynamicConfig to another DynamicConfig. // If rhs.def() is not null, then it has to be equal to this->def(). - DynamicConfig& operator=(DynamicConfig &&rhs) + DynamicConfig& operator=(DynamicConfig &&rhs) noexcept { assert(this->def() == nullptr || this->def() == rhs.def()); this->clear(); diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp index 4ee8974f47..7c0dfcce5b 100644 --- a/src/libslic3r/ExPolygon.hpp +++ b/src/libslic3r/ExPolygon.hpp @@ -19,7 +19,7 @@ class ExPolygon public: ExPolygon() {} ExPolygon(const ExPolygon &other) : contour(other.contour), holes(other.holes) {} - ExPolygon(ExPolygon &&other) : contour(std::move(other.contour)), holes(std::move(other.holes)) {} + ExPolygon(ExPolygon &&other) noexcept : contour(std::move(other.contour)), holes(std::move(other.holes)) {} explicit ExPolygon(const Polygon &contour) : contour(contour) {} explicit ExPolygon(Polygon &&contour) : contour(std::move(contour)) {} explicit ExPolygon(const Points &contour) : contour(contour) {} @@ -32,7 +32,7 @@ public: ExPolygon(std::initializer_list contour, std::initializer_list hole) : contour(contour), holes({ hole }) {} ExPolygon& operator=(const ExPolygon &other) { contour = other.contour; holes = other.holes; return *this; } - ExPolygon& operator=(ExPolygon &&other) { contour = std::move(other.contour); holes = std::move(other.holes); return *this; } + ExPolygon& operator=(ExPolygon &&other) noexcept { contour = std::move(other.contour); holes = std::move(other.holes); return *this; } Polygon contour; Polygons holes; From 8bfc986fa7da5dc5676580a4191b0550f3d9e2ab Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 14 Jan 2020 10:31:18 +0100 Subject: [PATCH 24/33] Refactoring of GCode::process_layer(). Refactoring of GCode export of color changes, extruder switches etc, so that the "color change" like extruder switches are applied first at the Wipe Tower / G-code export, so that adding / removing an extruder switch at the G-code preview slider does not invalidate slicing. --- src/libslic3r/Extruder.hpp | 3 - src/libslic3r/GCode.cpp | 365 ++++++++++++-------- src/libslic3r/GCode.hpp | 32 +- src/libslic3r/GCode/ToolOrdering.cpp | 91 +++-- src/libslic3r/GCode/ToolOrdering.hpp | 19 +- src/libslic3r/GCodeWriter.cpp | 10 +- src/libslic3r/GCodeWriter.hpp | 3 +- src/libslic3r/Model.cpp | 28 +- src/libslic3r/Model.hpp | 7 +- src/libslic3r/Print.cpp | 74 +--- src/libslic3r/Print.hpp | 9 - src/slic3r/GUI/BackgroundSlicingProcess.hpp | 5 - src/slic3r/GUI/Plater.cpp | 1 - 13 files changed, 352 insertions(+), 295 deletions(-) diff --git a/src/libslic3r/Extruder.hpp b/src/libslic3r/Extruder.hpp index df92bf84b3..066a5f56d2 100644 --- a/src/libslic3r/Extruder.hpp +++ b/src/libslic3r/Extruder.hpp @@ -48,9 +48,6 @@ public: double retract_length_toolchange() const; double retract_restart_extra_toolchange() const; - // Constructor for a key object, to be used by the stdlib search functions. - static Extruder key(unsigned int id) { return Extruder(id); } - private: // Private constructor to create a key for a search in std::set. Extruder(unsigned int id) : m_id(id) {} diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 942558f272..5d0a0d4d95 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1133,11 +1133,9 @@ void GCode::_do_export(Print& print, FILE* file) m_enable_cooling_markers = true; this->apply_print_config(print.config()); - this->set_extruders(print.extruders()); - // Initialize custom gcode - Model* model = print.get_object(0)->model_object()->get_model(); - m_custom_gcode_per_print_z = model->custom_gcode_per_print_z; + // Initialize custom gcode iterator. + m_custom_gcode_per_print_z_it = print.model().custom_gcode_per_print_z.cbegin(); m_volumetric_speed = DoExport::autospeed_volumetric_limit(print); print.throw_if_canceled(); @@ -1221,18 +1219,27 @@ void GCode::_do_export(Print& print, FILE* file) if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1) break; } + // We don't allow switching of extruders per layer by Model::custom_gcode_per_print_z in sequential mode. + // Use the extruder IDs collected from Regions. + this->set_extruders(print.extruders()); } else { // Find tool ordering for all the objects at once, and the initial extruder ID. // If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it. tool_ordering = print.wipe_tower_data().tool_ordering.empty() ? - ToolOrdering(print, initial_extruder_id) : + ToolOrdering(print, initial_extruder_id, false, + // Use the extruder switches from Model::custom_gcode_per_print_z to override the extruder to print the object. + // Do it only if all the objects were configured to be printed with a single extruder. + (print.object_extruders().size() == 1) ? &custom_tool_changes(print.model(), (unsigned int)m_config.nozzle_diameter.size()) : nullptr) : print.wipe_tower_data().tool_ordering; has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower(); - initial_extruder_id = (has_wipe_tower && ! print.config().single_extruder_multi_material_priming) ? + initial_extruder_id = (has_wipe_tower && ! print.config().single_extruder_multi_material_priming) ? // The priming towers will be skipped. tool_ordering.all_extruders().back() : - // Don't skip the priming towers. + // Don't skip the priming towers. tool_ordering.first_extruder(); + // In non-sequential print, the printing extruders may have been modified by the extruder switches stored in Model::custom_gcode_per_print_z. + // Therefore initialize the printing extruders from there. + this->set_extruders(tool_ordering.all_extruders()); } if (initial_extruder_id == (unsigned int)-1) { // Nothing to print! @@ -1247,7 +1254,7 @@ void GCode::_do_export(Print& print, FILE* file) // #ys_FIXME_no_exported_codes /* /* To avoid change filament for non-used extruder for Multi-material, - * check model->custom_gcode_per_print_z using tool_ordering values + * check print.model().custom_gcode_per_print_z using tool_ordering values * / if (!m_custom_gcode_per_print_z. empty()) { @@ -1283,7 +1290,7 @@ void GCode::_do_export(Print& print, FILE* file) } if (delete_executed) - model->custom_gcode_per_print_z = m_custom_gcode_per_print_z; + print.model().custom_gcode_per_print_z = m_custom_gcode_per_print_z; } */ @@ -1768,6 +1775,174 @@ std::vector GCode::sort_print_object_instances( return out; } +namespace ProcessLayer +{ + + std::string emit_custom_gcode_per_print_z( + // Last processed CustomGCode. + std::vector::const_iterator &custom_gcode_per_print_z_it, + const std::vector::const_iterator custom_gcode_per_print_z_end, + // This layer's print_z. + coordf_t current_print_z, + // ID of the first extruder printing this layer. + unsigned int first_extruder_id, + size_t num_extruders) + { + // Let's issue a filament change command if requested at this layer. + // In case there are more toolchange requests that weren't done yet and should happen simultaneously, erase them all. + // (Layers can be close to each other, model could have been resliced with bigger layer height, ...). + bool has_colorchange = false; + + std::string custom_code; + std::string pause_print_msg; + int m600_before_extruder = -1; + while (custom_gcode_per_print_z_it != custom_gcode_per_print_z_end) { + auto it_next = custom_gcode_per_print_z_it; + if ((++ it_next)->print_z >= current_print_z + EPSILON) + break; + custom_gcode_per_print_z_it = it_next; + } + if (custom_gcode_per_print_z_it != custom_gcode_per_print_z_end && custom_gcode_per_print_z_it->print_z < current_print_z + EPSILON) { + custom_code = custom_gcode_per_print_z_it->gcode; + + if (custom_code == ColorChangeCode && custom_gcode_per_print_z_it->extruder > 0) + m600_before_extruder = custom_gcode_per_print_z_it->extruder - 1; + if (custom_code == PausePrintCode) + pause_print_msg = custom_gcode_per_print_z_it->color; + + // This color change is consumed, don't use it again. + ++ custom_gcode_per_print_z_it; + has_colorchange = true; + } + + // we should add or not colorprint_change in respect to nozzle_diameter count instead of really used extruders count + + // don't save "tool_change"(ExtruderChangeCode) code to GCode + std::string gcode; + if (has_colorchange && custom_code != ExtruderChangeCode) { + const bool single_material_print = num_extruders == 1; + + if (custom_code == ColorChangeCode) // color change + { + // add tag for analyzer + gcode += "; " + GCodeAnalyzer::Color_Change_Tag + ",T" + std::to_string(m600_before_extruder) + "\n"; + // add tag for time estimator + gcode += "; " + GCodeTimeEstimator::Color_Change_Tag + "\n"; + + if (!single_material_print && m600_before_extruder >= 0 && first_extruder_id != m600_before_extruder + // && !MMU1 + ) { + //! FIXME_in_fw show message during print pause + gcode += "M601\n"; // pause print + gcode += "M117 Change filament for Extruder " + std::to_string(m600_before_extruder) + "\n"; + } + else + gcode += custom_code + "\n"; + } + else + { + if (custom_code == PausePrintCode) // Pause print + { + // add tag for analyzer + gcode += "; " + GCodeAnalyzer::Pause_Print_Tag + "\n"; + //! FIXME_in_fw show message during print pause + if (!pause_print_msg.empty()) + gcode += "M117 " + pause_print_msg + "\n"; + // add tag for time estimator + //gcode += "; " + GCodeTimeEstimator::Pause_Print_Tag + "\n"; + } + else // custom Gcode + { + // add tag for analyzer + gcode += "; " + GCodeAnalyzer::Custom_Code_Tag + "\n"; + // add tag for time estimator + //gcode += "; " + GCodeTimeEstimator::Custom_Code_Tag + "\n"; + } + gcode += custom_code + "\n"; + } + } + + return gcode; + } +} // namespace ProcessLayer + +namespace Skirt { + std::map> make_skirt_loops_per_extruder_1st_layer( + const Print &print, + const std::vector &layers, + const LayerTools &layer_tools, + std::vector extruder_ids, + // Heights at which the skirt has already been extruded. + std::vector &skirt_done) + { + // Extrude skirt at the print_z of the raft layers and normal object layers + // not at the print_z of the interlaced support material layers. + std::map> skirt_loops_per_extruder_out; + assert(skirt_done.empty()); + if (print.has_skirt() && ! print.skirt().entities.empty()) { + // Prime all the printing extruders over the skirt lines. + // Reorder the extruders, so that the last used extruder is at the front. + unsigned int first_extruder_id = layer_tools.extruders.front(); + for (size_t i = 1; i < extruder_ids.size(); ++ i) + if (extruder_ids[i] == first_extruder_id) { + // Move the last extruder to the front. + memmove(extruder_ids.data() + 1, extruder_ids.data(), i * sizeof(unsigned int)); + extruder_ids.front() = first_extruder_id; + break; + } + size_t n_loops = print.skirt().entities.size(); + if (n_loops <= extruder_ids.size()) { + for (size_t i = 0; i < n_loops; ++i) + skirt_loops_per_extruder_out[extruder_ids[i]] = std::pair(i, i + 1); + } else { + // Assign skirt loops to the extruders. + std::vector extruder_loops(extruder_ids.size(), 1); + n_loops -= extruder_loops.size(); + while (n_loops > 0) { + for (size_t i = 0; i < extruder_ids.size() && n_loops > 0; ++i, --n_loops) + ++extruder_loops[i]; + } + for (size_t i = 0; i < extruder_ids.size(); ++i) + skirt_loops_per_extruder_out[extruder_ids[i]] = std::make_pair( + (i == 0) ? 0 : extruder_loops[i - 1], + ((i == 0) ? 0 : extruder_loops[i - 1]) + extruder_loops[i]); + } + skirt_done.emplace_back(layer_tools.print_z - (skirt_done.empty() ? 0. : skirt_done.back())); + + } + return skirt_loops_per_extruder_out; + } + + std::map> make_skirt_loops_per_extruder_other_layers( + const Print &print, + const std::vector &layers, + const LayerTools &layer_tools, + // Heights at which the skirt has already been extruded. + std::vector &skirt_done) + { + // Extrude skirt at the print_z of the raft layers and normal object layers + // not at the print_z of the interlaced support material layers. + std::map> skirt_loops_per_extruder_out; + if (print.has_skirt() && ! print.skirt().entities.empty() && + // Not enough skirt layers printed yet. + //FIXME infinite or high skirt does not make sense for sequential print! + (skirt_done.size() < (size_t)print.config().skirt_height.value || print.has_infinite_skirt()) && + // This print_z has not been extruded yet + skirt_done.back() < layer_tools.print_z - EPSILON && + // and this layer is an object layer, or it is a raft layer. + //FIXME one uses the number of raft layers from the 1st object! + (layer_tools.has_object || layers.front().support_layer->id() < (size_t)layers.front().support_layer->object()->config().raft_layers.value)) { + // Extrude all skirts with the current extruder. + unsigned int first_extruder_id = layer_tools.extruders.front(); + skirt_loops_per_extruder_out[first_extruder_id] = std::pair(0, print.config().skirts.value); + assert(!skirt_done.empty()); + skirt_done.emplace_back(layer_tools.print_z - skirt_done.back()); + } + return skirt_loops_per_extruder_out; + } + +} // namespace Skirt + // In sequential mode, process_layer is called once per each object and its copy, // therefore layers will contain a single entry and single_object_instance_idx will point to the copy of the object. // In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated. @@ -1804,7 +1979,7 @@ void GCode::process_layer( if (l.support_layer != nullptr && support_layer == nullptr) support_layer = l.support_layer; } - const Layer &layer = (object_layer != nullptr) ? *object_layer : *support_layer; + const Layer &layer = (object_layer != nullptr) ? *object_layer : *support_layer; coordf_t print_z = layer.print_z; bool first_layer = layer.id() == 0; unsigned int first_extruder_id = layer_tools.extruders.front(); @@ -1868,120 +2043,21 @@ void GCode::process_layer( m_second_layer_things_done = true; } - // Let's issue a filament change command if requested at this layer. - // In case there are more toolchange requests that weren't done yet and should happen simultaneously, erase them all. - // (Layers can be close to each other, model could have been resliced with bigger layer height, ...). - bool colorprint_change = false; - - std::string custom_code = ""; - std::string pause_print_msg = ""; - int m600_before_extruder = -1; - while (!m_custom_gcode_per_print_z.empty() && m_custom_gcode_per_print_z.front().print_z - EPSILON < layer.print_z) { - custom_code = m_custom_gcode_per_print_z.front().gcode; - - if (custom_code == ColorChangeCode && m_custom_gcode_per_print_z.front().extruder > 0) - m600_before_extruder = m_custom_gcode_per_print_z.front().extruder - 1; - if (custom_code == PausePrintCode) - pause_print_msg = m_custom_gcode_per_print_z.front().color; - - m_custom_gcode_per_print_z.erase(m_custom_gcode_per_print_z.begin()); - colorprint_change = true; - } - - // we should add or not colorprint_change in respect to nozzle_diameter count instead of really used extruders count - - // don't save "tool_change"(ExtruderChangeCode) code to GCode - if (colorprint_change && custom_code != ExtruderChangeCode) { - const bool single_material_print = print.config().nozzle_diameter.size() == 1; - - if (custom_code == ColorChangeCode) // color change - { - // add tag for analyzer - gcode += "; " + GCodeAnalyzer::Color_Change_Tag + ",T" + std::to_string(m600_before_extruder) + "\n"; - // add tag for time estimator - gcode += "; " + GCodeTimeEstimator::Color_Change_Tag + "\n"; - - if (!single_material_print && m600_before_extruder >= 0 && first_extruder_id != m600_before_extruder - // && !MMU1 - ) { - //! FIXME_in_fw show message during print pause - gcode += "M601\n"; // pause print - gcode += "M117 Change filament for Extruder " + std::to_string(m600_before_extruder) + "\n"; - } - else - gcode += custom_code + "\n"; - } - else - { - if (custom_code == PausePrintCode) // Pause print - { - // add tag for analyzer - gcode += "; " + GCodeAnalyzer::Pause_Print_Tag + "\n"; - //! FIXME_in_fw show message during print pause - if (!pause_print_msg.empty()) - gcode += "M117 " + pause_print_msg + "\n"; - // add tag for time estimator - //gcode += "; " + GCodeTimeEstimator::Pause_Print_Tag + "\n"; - } - else // custom Gcode - { - // add tag for analyzer - gcode += "; " + GCodeAnalyzer::Custom_Code_Tag + "\n"; - // add tag for time estimator - //gcode += "; " + GCodeTimeEstimator::Custom_Code_Tag + "\n"; - } - gcode += custom_code + "\n"; - } - } - - - // Extrude skirt at the print_z of the raft layers and normal object layers - // not at the print_z of the interlaced support material layers. - bool extrude_skirt = - ! print.skirt().entities.empty() && - // Not enough skirt layers printed yet. - (m_skirt_done.size() < (size_t)print.config().skirt_height.value || print.has_infinite_skirt()) && - // This print_z has not been extruded yet - (m_skirt_done.empty() ? 0. : m_skirt_done.back()) < print_z - EPSILON && - // and this layer is the 1st layer, or it is an object layer, or it is a raft layer. - (first_layer || object_layer != nullptr || support_layer->id() < (size_t)m_config.raft_layers.value); + // Map from extruder ID to index of skirt loops to be extruded with that extruder. std::map> skirt_loops_per_extruder; - coordf_t skirt_height = 0.; - if (extrude_skirt) { - // Fill in skirt_loops_per_extruder. - skirt_height = print_z - (m_skirt_done.empty() ? 0. : m_skirt_done.back()); - m_skirt_done.push_back(print_z); - if (first_layer) { - // Prime the extruders over the skirt lines. - std::vector extruder_ids = m_writer.extruder_ids(); - // Reorder the extruders, so that the last used extruder is at the front. - for (size_t i = 1; i < extruder_ids.size(); ++ i) - if (extruder_ids[i] == first_extruder_id) { - // Move the last extruder to the front. - memmove(extruder_ids.data() + 1, extruder_ids.data(), i * sizeof(unsigned int)); - extruder_ids.front() = first_extruder_id; - break; - } - size_t n_loops = print.skirt().entities.size(); - if (n_loops <= extruder_ids.size()) { - for (size_t i = 0; i < n_loops; ++i) - skirt_loops_per_extruder[extruder_ids[i]] = std::pair(i, i + 1); - } else { - // Assign skirt loops to the extruders. - std::vector extruder_loops(extruder_ids.size(), 1); - n_loops -= extruder_loops.size(); - while (n_loops > 0) { - for (size_t i = 0; i < extruder_ids.size() && n_loops > 0; ++ i, -- n_loops) - ++ extruder_loops[i]; - } - for (size_t i = 0; i < extruder_ids.size(); ++ i) - skirt_loops_per_extruder[extruder_ids[i]] = std::make_pair( - (i == 0) ? 0 : extruder_loops[i - 1], - ((i == 0) ? 0 : extruder_loops[i - 1]) + extruder_loops[i]); - } - } else - // Extrude all skirts with the current extruder. - skirt_loops_per_extruder[first_extruder_id] = std::pair(0, print.config().skirts.value); + + if (single_object_instance_idx == size_t(-1) && object_layer != nullptr) { + // Normal (non-sequential) print. + gcode += ProcessLayer::emit_custom_gcode_per_print_z( + // input / output + m_custom_gcode_per_print_z_it, + // inputs + print.model().custom_gcode_per_print_z.cend(), layer.print_z, first_extruder_id, print.config().nozzle_diameter.size()); + // Extrude skirt at the print_z of the raft layers and normal object layers + // not at the print_z of the interlaced support material layers. + skirt_loops_per_extruder = first_layer ? + Skirt::make_skirt_loops_per_extruder_1st_layer(print, layers, layer_tools, m_writer.extruder_ids(), m_skirt_done) : + Skirt::make_skirt_loops_per_extruder_other_layers(print, layers, layer_tools, m_skirt_done); } // Group extrusions by an extruder, then by an object, an island and a region. @@ -2085,7 +2161,7 @@ void GCode::process_layer( continue; // This extrusion is part of certain Region, which tells us which extruder should be used for it: - int correct_extruder_id = Print::get_extruder(*extrusions, region); + int correct_extruder_id = layer_tools.extruder(*extrusions, region); // Let's recover vector of extruder overrides: const WipingExtrusions::ExtruderPerCopy *entity_overrides = nullptr; @@ -2152,30 +2228,27 @@ void GCode::process_layer( if (m_enable_analyzer && layer_tools.has_wipe_tower && m_wipe_tower) m_last_analyzer_extrusion_role = erWipeTower; - if (extrude_skirt) { - auto loops_it = skirt_loops_per_extruder.find(extruder_id); - if (loops_it != skirt_loops_per_extruder.end()) { - const std::pair loops = loops_it->second; - this->set_origin(0.,0.); - m_avoid_crossing_perimeters.use_external_mp = true; - Flow skirt_flow = print.skirt_flow(); - for (size_t i = loops.first; i < loops.second; ++ i) { - // Adjust flow according to this layer's layer height. - ExtrusionLoop loop = *dynamic_cast(print.skirt().entities[i]); - Flow layer_skirt_flow(skirt_flow); - layer_skirt_flow.height = (float)skirt_height; - double mm3_per_mm = layer_skirt_flow.mm3_per_mm(); - for (ExtrusionPath &path : loop.paths) { - path.height = (float)layer.height; - path.mm3_per_mm = mm3_per_mm; - } - gcode += this->extrude_loop(loop, "skirt", m_config.support_material_speed.value); + if (auto loops_it = skirt_loops_per_extruder.find(extruder_id); loops_it != skirt_loops_per_extruder.end()) { + const std::pair loops = loops_it->second; + this->set_origin(0., 0.); + m_avoid_crossing_perimeters.use_external_mp = true; + Flow layer_skirt_flow(print.skirt_flow()); + layer_skirt_flow.height = (float)(m_skirt_done.back() - ((m_skirt_done.size() == 1) ? 0. : m_skirt_done[m_skirt_done.size() - 2])); + double mm3_per_mm = layer_skirt_flow.mm3_per_mm(); + for (size_t i = loops.first; i < loops.second; ++i) { + // Adjust flow according to this layer's layer height. + ExtrusionLoop loop = *dynamic_cast(print.skirt().entities[i]); + for (ExtrusionPath &path : loop.paths) { + path.height = layer_skirt_flow.height; + path.mm3_per_mm = mm3_per_mm; } - m_avoid_crossing_perimeters.use_external_mp = false; - // Allow a straight travel move to the first object point if this is the first layer (but don't in next layers). - if (first_layer && loops.first == 0) - m_avoid_crossing_perimeters.disable_once = true; + //FIXME using the support_material_speed of the 1st object printed. + gcode += this->extrude_loop(loop, "skirt", m_config.support_material_speed.value); } + m_avoid_crossing_perimeters.use_external_mp = false; + // Allow a straight travel move to the first object point if this is the first layer (but don't in next layers). + if (first_layer && loops.first == 0) + m_avoid_crossing_perimeters.disable_once = true; } // Extrude brim with the extruder of the 1st region. diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index d5213d00ba..10644a60b4 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -197,23 +197,25 @@ public: // append full config to the given string static void append_full_config(const Print& print, std::string& str); + // Object and support extrusions of the same PrintObject at the same print_z. + // public, so that it could be accessed by free helper functions from GCode.cpp + struct LayerToPrint + { + LayerToPrint() : object_layer(nullptr), support_layer(nullptr) {} + const Layer* object_layer; + const SupportLayer* support_layer; + const Layer* layer() const { return (object_layer != nullptr) ? object_layer : support_layer; } + const PrintObject* object() const { return (this->layer() != nullptr) ? this->layer()->object() : nullptr; } + coordf_t print_z() const { return (object_layer != nullptr && support_layer != nullptr) ? 0.5 * (object_layer->print_z + support_layer->print_z) : this->layer()->print_z; } + }; + private: #if ENABLE_THUMBNAIL_GENERATOR - void _do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb); + void _do_export(Print &print, FILE *file, ThumbnailsGeneratorCallback thumbnail_cb); #else void _do_export(Print &print, FILE *file); #endif //ENABLE_THUMBNAIL_GENERATOR - // Object and support extrusions of the same PrintObject at the same print_z. - struct LayerToPrint - { - LayerToPrint() : object_layer(nullptr), support_layer(nullptr) {} - const Layer *object_layer; - const SupportLayer *support_layer; - const Layer* layer() const { return (object_layer != nullptr) ? object_layer : support_layer; } - const PrintObject* object() const { return (this->layer() != nullptr) ? this->layer()->object() : nullptr; } - coordf_t print_z() const { return (object_layer != nullptr && support_layer != nullptr) ? 0.5 * (object_layer->print_z + support_layer->print_z) : this->layer()->print_z; } - }; static std::vector collect_layers_to_print(const PrintObject &object); static std::vector>> collect_layers_to_print(const Print &print); void process_layer( @@ -372,11 +374,9 @@ private: bool m_second_layer_things_done; // Index of a last object copy extruded. std::pair m_last_obj_copy; - // Extensions for colorprint - now it's not a just color_print_heights, - // there can be some custom gcode. - // Updated before the export and erased during the process, - // so no toolchange occurs twice. - std::vector m_custom_gcode_per_print_z; + // Iterator to Model::custom_gcode_per_print_z, which is being increased by process_layer. + // The Model::custom_gcode_per_print_z may contain color changes, extruder switches, pauses and custom G-codes. + std::vector::const_iterator m_custom_gcode_per_print_z_it; // Time estimators GCodeTimeEstimator m_normal_time_estimator; diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 8aed420cbc..3edb7cf174 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -34,6 +34,39 @@ bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const return false; } +// Return a zero based extruder from the region, or extruder_override if overriden. +unsigned int LayerTools::perimeter_extruder(const PrintRegion ®ion) const +{ + assert(region.config().perimeter_extruder.value > 0); + return ((this->extruder_override == 0) ? region.config().perimeter_extruder.value : this->extruder_override) - 1; +} + +unsigned int LayerTools::infill_extruder(const PrintRegion ®ion) const +{ + assert(region.config().infill_extruder.value > 0); + return ((this->extruder_override == 0) ? region.config().infill_extruder.value : this->extruder_override) - 1; +} + +unsigned int LayerTools::solid_infill_extruder(const PrintRegion ®ion) const +{ + assert(region.config().solid_infill_extruder.value > 0); + return ((this->extruder_override == 0) ? region.config().solid_infill_extruder.value : this->extruder_override) - 1; +} + +// Returns a zero based extruder this eec should be printed with, according to PrintRegion config or extruder_override if overriden. +unsigned int LayerTools::extruder(const ExtrusionEntityCollection &extrusions, const PrintRegion ®ion) const +{ + assert(region.config().perimeter_extruder.value > 0); + assert(region.config().infill_extruder.value > 0); + assert(region.config().solid_infill_extruder.value > 0); + // 1 based extruder ID. + unsigned int extruder = ((this->extruder_override == 0) ? + (is_infill(extrusions.role()) ? + (is_solid_infill(extrusions.entities.front()->role()) ? region.config().solid_infill_extruder : region.config().infill_extruder) : + region.config().perimeter_extruder.value) : + this->extruder_override); + return (extruder == 0) ? 0 : extruder - 1; +} // For the use case when each object is printed separately // (print.config().complete_objects is true). @@ -54,7 +87,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude } // Collect extruders reuqired to print the layers. - this->collect_extruders(object); + this->collect_extruders(object, nullptr); // Reorder the extruders to minimize tool switches. this->reorder_extruders(first_extruder); @@ -66,7 +99,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude // For the use case when all objects are printed at once. // (print.config().complete_objects is false). -ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material) +ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material, const std::vector> *per_layer_extruder_switches) { m_print_config_ptr = &print.config(); @@ -93,7 +126,7 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool // Collect extruders reuqired to print the layers. for (auto object : print.objects()) - this->collect_extruders(*object); + this->collect_extruders(*object, (per_layer_extruder_switches != nullptr && ! per_layer_extruder_switches->empty()) ? per_layer_extruder_switches : nullptr); // Reorder the extruders to minimize tool switches. this->reorder_extruders(first_extruder); @@ -119,7 +152,7 @@ void ToolOrdering::initialize_layers(std::vector &zs) } // Collect extruders reuqired to print layers. -void ToolOrdering::collect_extruders(const PrintObject &object) +void ToolOrdering::collect_extruders(const PrintObject &object, const std::vector> *per_layer_extruder_switches) { // Collect the support extruders. for (auto support_layer : object.support_layers()) { @@ -136,9 +169,25 @@ void ToolOrdering::collect_extruders(const PrintObject &object) if (has_support || has_interface) layer_tools.has_support = true; } + + // Extruder overrides are ordered by print_z. + std::vector>::const_iterator it_per_layer_extruder_override; + if (per_layer_extruder_switches != nullptr) + it_per_layer_extruder_override = per_layer_extruder_switches->begin(); + unsigned int extruder_override = 0; + // Collect the object extruders. for (auto layer : object.layers()) { LayerTools &layer_tools = this->tools_for_layer(layer->print_z); + + // Override extruder with the next + if (per_layer_extruder_switches != nullptr) + for (; it_per_layer_extruder_override != per_layer_extruder_switches->end() && it_per_layer_extruder_override->first < layer->print_z + EPSILON; ++ it_per_layer_extruder_override) + extruder_override = (int)it_per_layer_extruder_override->second; + + // Store the current extruder override (set to zero if no overriden), so that layer_tools.wiping_extrusions().is_overridable_and_mark() will use it. + layer_tools.extruder_override = extruder_override; + // What extruders are required to print this object layer? for (size_t region_id = 0; region_id < object.region_volumes.size(); ++ region_id) { const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr; @@ -159,12 +208,11 @@ void ToolOrdering::collect_extruders(const PrintObject &object) } if (something_nonoverriddable) - layer_tools.extruders.push_back(region.config().perimeter_extruder.value); + layer_tools.extruders.emplace_back((extruder_override == 0) ? region.config().perimeter_extruder.value : extruder_override); layer_tools.has_object = true; } - bool has_infill = false; bool has_solid_infill = false; bool something_nonoverriddable = false; @@ -183,12 +231,14 @@ void ToolOrdering::collect_extruders(const PrintObject &object) } } - if (something_nonoverriddable || !m_print_config_ptr) - { - if (has_solid_infill) - layer_tools.extruders.push_back(region.config().solid_infill_extruder); - if (has_infill) - layer_tools.extruders.push_back(region.config().infill_extruder); + if (something_nonoverriddable || !m_print_config_ptr) { + if (extruder_override == 0) { + if (has_solid_infill) + layer_tools.extruders.emplace_back(region.config().solid_infill_extruder); + if (has_infill) + layer_tools.extruders.emplace_back(region.config().infill_extruder); + } else if (has_solid_infill || has_infill) + layer_tools.extruders.emplace_back(extruder_override); } if (has_solid_infill || has_infill) layer_tools.has_object = true; @@ -201,7 +251,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object) // make sure that there are some tools for each object layer (e.g. tall wiping object will result in empty extruders vector) if (layer.extruders.empty() && layer.has_object) - layer.extruders.push_back(0); // 0="dontcare" extruder - it will be taken care of in reorder_extruders + layer.extruders.emplace_back(0); // 0="dontcare" extruder - it will be taken care of in reorder_extruders } } @@ -256,11 +306,9 @@ void ToolOrdering::reorder_extruders(unsigned int last_extruder_id) for (unsigned int &extruder_id : lt.extruders) { assert(extruder_id > 0); -- extruder_id; - } + } } - - void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z) { if (m_layer_tools.empty()) @@ -413,7 +461,7 @@ const LayerTools& ToolOrdering::tools_for_layer(coordf_t print_z) const } // This function is called from Print::mark_wiping_extrusions and sets extruder this entity should be printed with (-1 .. as usual) -void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies) +void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, size_t copy_id, int extruder, size_t num_of_copies) { something_overridden = true; @@ -449,11 +497,10 @@ int WipingExtrusions::last_nonsoluble_extruder_on_layer(const PrintConfig& print return (-1); } - // Decides whether this entity could be overridden bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const { - if (print_config.filament_soluble.get_at(Print::get_extruder(eec, region))) + if (print_config.filament_soluble.get_at(m_layer_tools->extruder(eec, region))) return false; if (object.config().wipe_into_objects) @@ -522,7 +569,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int if (wipe_into_infill_only && ! print.config().infill_first) // In this case we must check that the original extruder is used on this layer before the one we are overridding // (and the perimeters will be finished before the infill is printed): - if (!lt.is_extruder_order(region.config().perimeter_extruder - 1, new_extruder)) + if (!lt.is_extruder_order(lt.perimeter_extruder(region), new_extruder)) continue; if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) { // this infill will be used to wipe this extruder @@ -597,8 +644,8 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) // Either way, we will now force-override it with something suitable: if (print.config().infill_first || object->config().wipe_into_objects // in this case the perimeter is overridden, so we can override by the last one safely - || lt.is_extruder_order(region.config().perimeter_extruder - 1, last_nonsoluble_extruder // !infill_first, but perimeter is already printed when last extruder prints - || ! lt.has_extruder(region.config().infill_extruder - 1))) // we have to force override - this could violate infill_first (FIXME) + || lt.is_extruder_order(lt.perimeter_extruder(region), last_nonsoluble_extruder // !infill_first, but perimeter is already printed when last extruder prints + || ! lt.has_extruder(lt.infill_extruder(region)))) // we have to force override - this could violate infill_first (FIXME) set_extruder_override(fill, copy, (print.config().infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies); else { // In this case we can (and should) leave it to be printed normally. diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp index ed7aaab436..1f043d94f4 100644 --- a/src/libslic3r/GCode/ToolOrdering.hpp +++ b/src/libslic3r/GCode/ToolOrdering.hpp @@ -61,7 +61,7 @@ private: int last_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const; // This function is called from mark_wiping_extrusions and sets extruder that it should be printed with (-1 .. as usual) - void set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies); + void set_extruder_override(const ExtrusionEntity* entity, size_t copy_id, int extruder, size_t num_of_copies); // Returns true in case that entity is not printed with its usual extruder for a given copy: bool is_entity_overridden(const ExtrusionEntity* entity, size_t copy_id) const { @@ -84,6 +84,7 @@ public: print_z(z), has_object(false), has_support(false), + extruder_override(0), has_wipe_tower(false), wipe_tower_partitions(0), wipe_tower_layer_height(0.) {} @@ -96,11 +97,21 @@ public: bool is_extruder_order(unsigned int a, unsigned int b) const; bool has_extruder(unsigned int extruder) const { return std::find(this->extruders.begin(), this->extruders.end(), extruder) != this->extruders.end(); } + // Return a zero based extruder from the region, or extruder_override if overriden. + unsigned int perimeter_extruder(const PrintRegion ®ion) const; + unsigned int infill_extruder(const PrintRegion ®ion) const; + unsigned int solid_infill_extruder(const PrintRegion ®ion) const; + // Returns a zero based extruder this eec should be printed with, according to PrintRegion config or extruder_override if overriden. + unsigned int extruder(const ExtrusionEntityCollection &extrusions, const PrintRegion ®ion) const; + coordf_t print_z; bool has_object; bool has_support; // Zero based extruder IDs, ordered to minimize tool switches. std::vector extruders; + // If per layer extruder switches are inserted by the G-code preview slider, this value contains the new (1 based) extruder, with which the whole object layer is being printed with. + // If not overriden, it is set to 0. + unsigned int extruder_override; // Will there be anything extruded on this layer for the wipe tower? // Due to the support layers possibly interleaving the object layers, // wipe tower will be disabled for some support only layers. @@ -129,11 +140,11 @@ public: // For the use case when each object is printed separately // (print.config.complete_objects is true). - ToolOrdering(const PrintObject &object, unsigned int first_extruder = (unsigned int)-1, bool prime_multi_material = false); + ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material = false); // For the use case when all objects are printed at once. // (print.config.complete_objects is false). - ToolOrdering(const Print &print, unsigned int first_extruder = (unsigned int)-1, bool prime_multi_material = false); + ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material = false, const std::vector> *per_layer_extruder_switches = nullptr); void clear() { m_layer_tools.clear(); } @@ -160,7 +171,7 @@ public: private: void initialize_layers(std::vector &zs); - void collect_extruders(const PrintObject &object); + void collect_extruders(const PrintObject &object, const std::vector> *per_layer_extruder_switches); void reorder_extruders(unsigned int last_extruder_id); void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z); void collect_extruder_statistics(bool prime_multi_material); diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index 364ba12ae1..0266eee39e 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -19,8 +19,8 @@ void GCodeWriter::apply_print_config(const PrintConfig &print_config) this->config.apply(print_config, true); m_extrusion_axis = this->config.get_extrusion_axis(); m_single_extruder_multi_material = print_config.single_extruder_multi_material.value; - m_max_acceleration = (print_config.gcode_flavor.value == gcfMarlin) ? - print_config.machine_max_acceleration_extruding.values.front() : 0; + m_max_acceleration = std::lrint((print_config.gcode_flavor.value == gcfMarlin) ? + print_config.machine_max_acceleration_extruding.values.front() : 0); } void GCodeWriter::set_extruders(const std::vector &extruder_ids) @@ -247,9 +247,9 @@ std::string GCodeWriter::toolchange_prefix() const std::string GCodeWriter::toolchange(unsigned int extruder_id) { // set the new extruder - auto it_extruder = std::lower_bound(m_extruders.begin(), m_extruders.end(), Extruder::key(extruder_id)); - assert(it_extruder != m_extruders.end()); - m_extruder = const_cast(&*it_extruder); + auto it_extruder = Slic3r::lower_bound_by_predicate(m_extruders.begin(), m_extruders.end(), [extruder_id](const Extruder &e) { return e.id() < extruder_id; }); + assert(it_extruder != m_extruders.end() && it_extruder->id() == extruder_id); + m_extruder = &*it_extruder; // return the toolchange command // if we are running a single-extruder setup, just set the extruder and return nothing diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index 98abdda280..1631d8590e 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -74,7 +74,8 @@ public: Vec3d get_position() const { return m_pos; } private: - std::vector m_extruders; + // Extruders are sorted by their ID, so that binary search is possible. + std::vector m_extruders; std::string m_extrusion_axis; bool m_single_extruder_multi_material; Extruder* m_extruder; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 96a32680e6..812180641b 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -598,21 +598,6 @@ std::string Model::propose_export_file_name_and_path(const std::string &new_exte return boost::filesystem::path(this->propose_export_file_name_and_path()).replace_extension(new_extension).string(); } -std::vector> Model::get_custom_tool_changes(double default_layer_height, size_t num_extruders) const -{ - std::vector> custom_tool_changes; - for (const CustomGCode& custom_gcode : custom_gcode_per_print_z) - if (custom_gcode.gcode == ExtruderChangeCode) { - DynamicPrintConfig config; - // If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders - config.set_key_value("extruder", new ConfigOptionInt(custom_gcode.extruder > num_extruders ? 0 : custom_gcode.extruder)); - // For correct extruders(tools) changing, we should decrease custom_gcode.height value by one default layer height - custom_tool_changes.push_back({ custom_gcode.print_z - default_layer_height, config }); - } - - return custom_tool_changes; -} - ModelObject::~ModelObject() { this->clear_volumes(); @@ -1856,6 +1841,19 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const return ret; } +// Return pairs of sorted by increasing print_z from custom_gcode_per_print_z. +// print_z corresponds to the first layer printed with the new extruder. +std::vector> custom_tool_changes(const Model &model, size_t num_extruders) +{ + std::vector> custom_tool_changes; + for (const Model::CustomGCode &custom_gcode : model.custom_gcode_per_print_z) + if (custom_gcode.gcode == ExtruderChangeCode) { + // If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders + custom_tool_changes.emplace_back(custom_gcode.print_z, static_cast(custom_gcode.extruder > num_extruders ? 1 : custom_gcode.extruder)); + } + return custom_tool_changes; +} + // Test whether the two models contain the same number of ModelObjects with the same set of IDs // ordered in the same order. In that case it is not necessary to kill the background processing. bool model_object_list_equal(const Model &model_old, const Model &model_new) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 44f5049c9f..26bb4cb92f 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -838,9 +838,6 @@ public: // Propose an output path, replace extension. The new_extension shall contain the initial dot. std::string propose_export_file_name_and_path(const std::string &new_extension) const; - // from custom_gcode_per_print_z get just tool_change codes - std::vector> get_custom_tool_changes(double default_layer_height, size_t num_extruders) const; - private: explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); }; void assign_new_unique_ids_recursive(); @@ -857,6 +854,10 @@ private: #undef OBJECTBASE_DERIVED_COPY_MOVE_CLONE #undef OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE +// Return pairs of sorted by increasing print_z from custom_gcode_per_print_z. +// print_z corresponds to the first layer printed with the new extruder. +extern std::vector> custom_tool_changes(const Model &model, size_t num_extruders); + // Test whether the two models contain the same number of ModelObjects with the same set of IDs // ordered in the same order. In that case it is not necessary to kill the background processing. extern bool model_object_list_equal(const Model &model_old, const Model &model_new); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 02d9da7848..2d327c70b9 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -638,48 +638,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr); } - // Convert input config ranges into continuous non-overlapping sorted vector of intervals and their configs, - // considering custom_tool_change values - void assign(const t_layer_config_ranges &in, const std::vector> &custom_tool_changes) { - m_ranges.clear(); - m_ranges.reserve(in.size()); - // Input ranges are sorted lexicographically. First range trims the other ranges. - coordf_t last_z = 0; - for (const std::pair &range : in) - if (range.first.second > last_z) { - coordf_t min_z = std::max(range.first.first, 0.); - if (min_z > last_z + EPSILON) { - m_ranges.emplace_back(t_layer_height_range(last_z, min_z), nullptr); - last_z = min_z; - } - if (range.first.second > last_z + EPSILON) { - const DynamicPrintConfig* cfg = &range.second; - m_ranges.emplace_back(t_layer_height_range(last_z, range.first.second), cfg); - last_z = range.first.second; - } - } - - // add ranges for extruder changes from custom_tool_changes - for (size_t i = 0; i < custom_tool_changes.size(); i++) { - const DynamicPrintConfig* cfg = &custom_tool_changes[i].second; - coordf_t cur_Z = custom_tool_changes[i].first; - coordf_t next_Z = i == custom_tool_changes.size()-1 ? DBL_MAX : custom_tool_changes[i+1].first; - if (cur_Z > last_z + EPSILON) { - if (i==0) - m_ranges.emplace_back(t_layer_height_range(last_z, cur_Z), nullptr); - m_ranges.emplace_back(t_layer_height_range(cur_Z, next_Z), cfg); - } - else if (next_Z > last_z + EPSILON) - m_ranges.emplace_back(t_layer_height_range(last_z, next_Z), cfg); - } - - if (m_ranges.empty()) - m_ranges.emplace_back(t_layer_height_range(0, DBL_MAX), nullptr); - else if (m_ranges.back().second == nullptr) - m_ranges.back().first.second = DBL_MAX; - else if (m_ranges.back().first.second != DBL_MAX) - m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr); - } const DynamicPrintConfig* config(const t_layer_height_range &range) const { auto it = std::lower_bound(m_ranges.begin(), m_ranges.end(), std::make_pair< t_layer_height_range, const DynamicPrintConfig*>(t_layer_height_range(range.first - EPSILON, range.second - EPSILON), nullptr)); // #ys_FIXME_COLOR @@ -733,17 +691,15 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ for (const ModelObject *model_object : m_model.objects) model_object_status.emplace(model_object->id(), ModelObjectStatus::New); } else { + if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) { + // If custom gcode per layer height was changed, we should stop background processing. + update_apply_status(this->invalidate_steps({ psWipeTower, psGCodeExport })); + m_model.custom_gcode_per_print_z = model.custom_gcode_per_print_z; + } if (model_object_list_equal(m_model, model)) { // The object list did not change. for (const ModelObject *model_object : m_model.objects) model_object_status.emplace(model_object->id(), ModelObjectStatus::Old); - - // But if custom gcode per layer height was changed - if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) { - // we should stop background processing - update_apply_status(this->invalidate_step(psGCodeExport)); - m_model.custom_gcode_per_print_z = model.custom_gcode_per_print_z; - } } else if (model_object_list_extended(m_model, model)) { // Add new objects. Their volumes and configs will be synchronized later. update_apply_status(this->invalidate_step(psGCodeExport)); @@ -835,9 +791,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ for (PrintObject *print_object : m_objects) print_object_status.emplace(PrintObjectStatus(print_object)); - std::vector> custom_tool_changes = - m_model.get_custom_tool_changes(m_default_object_config.layer_height, num_extruders); - // 3) Synchronize ModelObjects & PrintObjects. for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) { ModelObject &model_object = *m_model.objects[idx_model_object]; @@ -845,9 +798,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ assert(it_status != model_object_status.end()); assert(it_status->status != ModelObjectStatus::Deleted); const ModelObject& model_object_new = *model.objects[idx_model_object]; - // ys_FIXME_COLOR - // const_cast(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges); - const_cast(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges, custom_tool_changes); + const_cast(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges); if (it_status->status == ModelObjectStatus::New) // PrintObject instances will be added in the next loop. continue; @@ -1015,8 +966,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ PrintRegionConfig this_region_config; bool this_region_config_set = false; for (PrintObject *print_object : m_objects) { - if(m_force_update_print_regions && !custom_tool_changes.empty()) - goto print_object_end; const LayerRanges *layer_ranges; { auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id())); @@ -1989,7 +1938,9 @@ void Print::_make_wipe_tower() wipe_volumes.push_back(std::vector(wiping_matrix.begin()+i*number_of_extruders, wiping_matrix.begin()+(i+1)*number_of_extruders)); // Let the ToolOrdering class know there will be initial priming extrusions at the start of the print. - m_wipe_tower_data.tool_ordering = ToolOrdering(*this, (unsigned int)-1, true); + m_wipe_tower_data.tool_ordering = ToolOrdering(*this, (unsigned int)-1, true, + (this->object_extruders().size() == 1) ? &custom_tool_changes(this->model(), (unsigned int)m_config.nozzle_diameter.size()) : nullptr); + if (! m_wipe_tower_data.tool_ordering.has_wipe_tower()) // Don't generate any wipe tower. return; @@ -2107,13 +2058,6 @@ void Print::_make_wipe_tower() m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges(); } -// Returns extruder this eec should be printed with, according to PrintRegion config -int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion ®ion) -{ - return is_infill(fill.role()) ? std::max(0, (is_solid_infill(fill.entities.front()->role()) ? region.config().solid_infill_extruder : region.config().infill_extruder) - 1) : - std::max(region.config().perimeter_extruder.value - 1, 0); -} - // Generate a recommended G-code output file name based on the format template, default extension, and template parameters // (timestamps, object placeholders derived from the model, current placeholder prameters and print statistics. // Use the final print statistics if available, or just keep the print statistics placeholders if not available yet (before G-code is finalized). diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index c434eec438..89d27e3cb8 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -369,9 +369,6 @@ public: // If zero, then the print is empty and the print shall not be executed. unsigned int num_object_instances() const; - // Returns extruder this eec should be printed with, according to PrintRegion config: - static int get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion ®ion); - const ExtrusionEntityCollection& skirt() const { return m_skirt; } const ExtrusionEntityCollection& brim() const { return m_brim; } @@ -386,9 +383,6 @@ public: // Accessed by SupportMaterial const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; } - // force update of PrintRegions, when custom_tool_change is not empty and (Re)Slicing is started - void set_force_update_print_regions(bool force_update_print_regions) { m_force_update_print_regions = force_update_print_regions; } - protected: // methods for handling regions PrintRegion* get_region(size_t idx) { return m_regions[idx]; } @@ -431,9 +425,6 @@ private: // Estimated print time, filament consumed. PrintStatistics m_print_statistics; - // flag used - bool m_force_update_print_regions = false; - // To allow GCode to set the Print's GCodeExport step status. friend class GCode; // Allow PrintObject to access m_mutex and m_cancel_callback. diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index a66dcf39c3..984686e355 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -132,11 +132,6 @@ public: // This "finished" flag does not account for the final export of the output file (.gcode or zipped PNGs), // and it does not account for the OctoPrint scheduling. bool finished() const { return m_print->finished(); } - - void set_force_update_print_regions(bool force_update_print_regions) { - if (m_fff_print) - m_fff_print->set_force_update_print_regions(force_update_print_regions); - } private: void thread_proc(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 011d7e7ce3..dc720ddaee 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3022,7 +3022,6 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool this->update_print_volume_state(); // Apply new config to the possibly running background task. bool was_running = this->background_process.running(); - this->background_process.set_force_update_print_regions(force_validation); Print::ApplyStatus invalidated = this->background_process.apply(this->q->model(), wxGetApp().preset_bundle->full_config()); // Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile. From 98e49772ed7aaec45dacfc4dd75a06ec41cd2add Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 14 Jan 2020 11:54:09 +0100 Subject: [PATCH 25/33] Custom G-code references are now being assigned to ToolOrdering::LayerTools() and the superfluous M600 (color change) events are filtered out there. Fixed a handful of compiler warnings. --- src/libslic3r/Flow.cpp | 4 +- src/libslic3r/GCode.cpp | 118 +++++--------------------- src/libslic3r/GCode.hpp | 3 - src/libslic3r/GCode/Analyzer.cpp | 2 +- src/libslic3r/GCode/CoolingBuffer.cpp | 4 +- src/libslic3r/GCode/PrintExtents.cpp | 6 +- src/libslic3r/GCode/ToolOrdering.cpp | 58 ++++++++++++- src/libslic3r/GCode/ToolOrdering.hpp | 29 +++---- src/libslic3r/Print.cpp | 7 +- 9 files changed, 100 insertions(+), 131 deletions(-) diff --git a/src/libslic3r/Flow.cpp b/src/libslic3r/Flow.cpp index 6069677a1b..2c5c64fe73 100644 --- a/src/libslic3r/Flow.cpp +++ b/src/libslic3r/Flow.cpp @@ -107,9 +107,9 @@ double Flow::mm3_per_mm() const { float res = this->bridge ? // Area of a circle with dmr of this->width. - (this->width * this->width) * 0.25 * PI : + float((this->width * this->width) * 0.25 * PI) : // Rectangle with semicircles at the ends. ~ h (w - 0.215 h) - this->height * (this->width - this->height * (1. - 0.25 * PI)); + float(this->height * (this->width - this->height * (1. - 0.25 * PI))); //assert(res > 0.); if (res <= 0.) throw std::runtime_error("Flow::mm3_per_mm() produced negative flow. Did you set some extrusion width too small?"); diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 5d0a0d4d95..6fa145bf63 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1134,9 +1134,6 @@ void GCode::_do_export(Print& print, FILE* file) m_enable_cooling_markers = true; this->apply_print_config(print.config()); - // Initialize custom gcode iterator. - m_custom_gcode_per_print_z_it = print.model().custom_gcode_per_print_z.cbegin(); - m_volumetric_speed = DoExport::autospeed_volumetric_limit(print); print.throw_if_canceled(); @@ -1226,10 +1223,7 @@ void GCode::_do_export(Print& print, FILE* file) // Find tool ordering for all the objects at once, and the initial extruder ID. // If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it. tool_ordering = print.wipe_tower_data().tool_ordering.empty() ? - ToolOrdering(print, initial_extruder_id, false, - // Use the extruder switches from Model::custom_gcode_per_print_z to override the extruder to print the object. - // Do it only if all the objects were configured to be printed with a single extruder. - (print.object_extruders().size() == 1) ? &custom_tool_changes(print.model(), (unsigned int)m_config.nozzle_diameter.size()) : nullptr) : + ToolOrdering(print, initial_extruder_id, false) : print.wipe_tower_data().tool_ordering; has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower(); initial_extruder_id = (has_wipe_tower && ! print.config().single_extruder_multi_material_priming) ? @@ -1251,49 +1245,6 @@ void GCode::_do_export(Print& print, FILE* file) } print.throw_if_canceled(); -// #ys_FIXME_no_exported_codes - /* - /* To avoid change filament for non-used extruder for Multi-material, - * check print.model().custom_gcode_per_print_z using tool_ordering values - * / - if (!m_custom_gcode_per_print_z. empty()) - { - bool delete_executed = false; - auto it = m_custom_gcode_per_print_z.end(); - while (it != m_custom_gcode_per_print_z.begin()) - { - --it; - if (it->gcode != ColorChangeCode) - continue; - - auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), LayerTools(it->print_z)); - - bool used_extruder = false; - for (; it_layer_tools != tool_ordering.end(); it_layer_tools++) - { - const std::vector& extruders = it_layer_tools->extruders; - if (std::find(extruders.begin(), extruders.end(), (unsigned)(it->extruder-1)) != extruders.end()) - { - used_extruder = true; - break; - } - } - if (used_extruder) - continue; - - /* If we are there, current extruder wouldn't be used, - * so this color change is a redundant move. - * Delete this item from m_custom_gcode_per_print_z - * / - it = m_custom_gcode_per_print_z.erase(it); - delete_executed = true; - } - - if (delete_executed) - print.model().custom_gcode_per_print_z = m_custom_gcode_per_print_z; - } -*/ - m_cooling_buffer->set_current_extruder(initial_extruder_id); // Emit machine envelope limits for the Marlin firmware. @@ -1779,62 +1730,39 @@ namespace ProcessLayer { std::string emit_custom_gcode_per_print_z( - // Last processed CustomGCode. - std::vector::const_iterator &custom_gcode_per_print_z_it, - const std::vector::const_iterator custom_gcode_per_print_z_end, - // This layer's print_z. - coordf_t current_print_z, + const Model::CustomGCode *custom_gcode, // ID of the first extruder printing this layer. unsigned int first_extruder_id, - size_t num_extruders) + bool single_material_print) { - // Let's issue a filament change command if requested at this layer. - // In case there are more toolchange requests that weren't done yet and should happen simultaneously, erase them all. - // (Layers can be close to each other, model could have been resliced with bigger layer height, ...). - bool has_colorchange = false; + std::string gcode; + + if (custom_gcode != nullptr) { + // Extruder switches are processed by LayerTools, they should be filtered out. + assert(custom_gcode->gcode != ExtruderChangeCode); - std::string custom_code; - std::string pause_print_msg; - int m600_before_extruder = -1; - while (custom_gcode_per_print_z_it != custom_gcode_per_print_z_end) { - auto it_next = custom_gcode_per_print_z_it; - if ((++ it_next)->print_z >= current_print_z + EPSILON) - break; - custom_gcode_per_print_z_it = it_next; - } - if (custom_gcode_per_print_z_it != custom_gcode_per_print_z_end && custom_gcode_per_print_z_it->print_z < current_print_z + EPSILON) { - custom_code = custom_gcode_per_print_z_it->gcode; + const std::string &custom_code = custom_gcode->gcode; + std::string pause_print_msg; + int m600_extruder_before_layer = -1; + if (custom_code == ColorChangeCode && custom_gcode->extruder > 0) + m600_extruder_before_layer = custom_gcode->extruder - 1; + else if (custom_code == PausePrintCode) + pause_print_msg = custom_gcode->color; - if (custom_code == ColorChangeCode && custom_gcode_per_print_z_it->extruder > 0) - m600_before_extruder = custom_gcode_per_print_z_it->extruder - 1; - if (custom_code == PausePrintCode) - pause_print_msg = custom_gcode_per_print_z_it->color; - - // This color change is consumed, don't use it again. - ++ custom_gcode_per_print_z_it; - has_colorchange = true; - } - - // we should add or not colorprint_change in respect to nozzle_diameter count instead of really used extruders count - - // don't save "tool_change"(ExtruderChangeCode) code to GCode - std::string gcode; - if (has_colorchange && custom_code != ExtruderChangeCode) { - const bool single_material_print = num_extruders == 1; - + // we should add or not colorprint_change in respect to nozzle_diameter count instead of really used extruders count if (custom_code == ColorChangeCode) // color change { // add tag for analyzer - gcode += "; " + GCodeAnalyzer::Color_Change_Tag + ",T" + std::to_string(m600_before_extruder) + "\n"; + gcode += "; " + GCodeAnalyzer::Color_Change_Tag + ",T" + std::to_string(m600_extruder_before_layer) + "\n"; // add tag for time estimator gcode += "; " + GCodeTimeEstimator::Color_Change_Tag + "\n"; - if (!single_material_print && m600_before_extruder >= 0 && first_extruder_id != m600_before_extruder + if (!single_material_print && m600_extruder_before_layer >= 0 && first_extruder_id != m600_extruder_before_layer // && !MMU1 ) { //! FIXME_in_fw show message during print pause gcode += "M601\n"; // pause print - gcode += "M117 Change filament for Extruder " + std::to_string(m600_before_extruder) + "\n"; + gcode += "M117 Change filament for Extruder " + std::to_string(m600_extruder_before_layer) + "\n"; } else gcode += custom_code + "\n"; @@ -1860,7 +1788,7 @@ namespace ProcessLayer } gcode += custom_code + "\n"; } - } + } return gcode; } @@ -2048,11 +1976,7 @@ void GCode::process_layer( if (single_object_instance_idx == size_t(-1) && object_layer != nullptr) { // Normal (non-sequential) print. - gcode += ProcessLayer::emit_custom_gcode_per_print_z( - // input / output - m_custom_gcode_per_print_z_it, - // inputs - print.model().custom_gcode_per_print_z.cend(), layer.print_z, first_extruder_id, print.config().nozzle_diameter.size()); + gcode += ProcessLayer::emit_custom_gcode_per_print_z(layer_tools.custom_gcode, first_extruder_id, print.config().nozzle_diameter.size() == 1); // Extrude skirt at the print_z of the raft layers and normal object layers // not at the print_z of the interlaced support material layers. skirt_loops_per_extruder = first_layer ? diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 10644a60b4..aee8ee2e25 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -374,9 +374,6 @@ private: bool m_second_layer_things_done; // Index of a last object copy extruded. std::pair m_last_obj_copy; - // Iterator to Model::custom_gcode_per_print_z, which is being increased by process_layer. - // The Model::custom_gcode_per_print_z may contain color changes, extruder switches, pauses and custom G-codes. - std::vector::const_iterator m_custom_gcode_per_print_z_it; // Time estimators GCodeTimeEstimator m_normal_time_estimator; diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp index c70abbbbdd..26e125bbcf 100644 --- a/src/libslic3r/GCode/Analyzer.cpp +++ b/src/libslic3r/GCode/Analyzer.cpp @@ -956,7 +956,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ GCodePreviewData::Extrusion::Path &path = paths.back(); path.polyline = polyline; path.extrusion_role = data.extrusion_role; - path.mm3_per_mm = data.mm3_per_mm; + path.mm3_per_mm = float(data.mm3_per_mm); path.width = data.width; path.height = data.height; path.feedrate = data.feedrate; diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index b00bc73eb7..6815ea73a0 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -303,8 +303,8 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: unsigned int extruder_id = extruders[i].id(); adj.extruder_id = extruder_id; adj.cooling_slow_down_enabled = config.cooling.get_at(extruder_id); - adj.slowdown_below_layer_time = config.slowdown_below_layer_time.get_at(extruder_id); - adj.min_print_speed = config.min_print_speed.get_at(extruder_id); + adj.slowdown_below_layer_time = float(config.slowdown_below_layer_time.get_at(extruder_id)); + adj.min_print_speed = float(config.min_print_speed.get_at(extruder_id)); map_extruder_to_per_extruder_adjustment[extruder_id] = i; } diff --git a/src/libslic3r/GCode/PrintExtents.cpp b/src/libslic3r/GCode/PrintExtents.cpp index d44ef1aad5..1fedcf3f03 100644 --- a/src/libslic3r/GCode/PrintExtents.cpp +++ b/src/libslic3r/GCode/PrintExtents.cpp @@ -29,7 +29,7 @@ static inline BoundingBox extrusion_polyline_extents(const Polyline &polyline, c static inline BoundingBoxf extrusionentity_extents(const ExtrusionPath &extrusion_path) { - BoundingBox bbox = extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width)); + BoundingBox bbox = extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width))); BoundingBoxf bboxf; if (! empty(bbox)) { bboxf.min = unscale(bbox.min); @@ -43,7 +43,7 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionLoop &extrusio { BoundingBox bbox; for (const ExtrusionPath &extrusion_path : extrusion_loop.paths) - bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width))); + bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width)))); BoundingBoxf bboxf; if (! empty(bbox)) { bboxf.min = unscale(bbox.min); @@ -57,7 +57,7 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionMultiPath &ext { BoundingBox bbox; for (const ExtrusionPath &extrusion_path : extrusion_multi_path.paths) - bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width))); + bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width)))); BoundingBoxf bboxf; if (! empty(bbox)) { bboxf.min = unscale(bbox.min); diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 3edb7cf174..c2febbafb6 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -15,6 +15,8 @@ #include +#include "../GCodeWriter.hpp" + namespace Slic3r { @@ -99,7 +101,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude // For the use case when all objects are printed at once. // (print.config().complete_objects is false). -ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material, const std::vector> *per_layer_extruder_switches) +ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material) { m_print_config_ptr = &print.config(); @@ -124,9 +126,16 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool this->initialize_layers(zs); } + // Use the extruder switches from Model::custom_gcode_per_print_z to override the extruder to print the object. + // Do it only if all the objects were configured to be printed with a single extruder. + const std::vector> *per_layer_extruder_switches = (print.object_extruders().size() == 1) ? + &custom_tool_changes(print.model(), (unsigned int)print.config().nozzle_diameter.size()) : nullptr; + if (per_layer_extruder_switches != nullptr && per_layer_extruder_switches->empty()) + per_layer_extruder_switches = nullptr; + // Collect extruders reuqired to print the layers. for (auto object : print.objects()) - this->collect_extruders(*object, (per_layer_extruder_switches != nullptr && ! per_layer_extruder_switches->empty()) ? per_layer_extruder_switches : nullptr); + this->collect_extruders(*object, per_layer_extruder_switches); // Reorder the extruders to minimize tool switches. this->reorder_extruders(first_extruder); @@ -134,6 +143,11 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool this->fill_wipe_tower_partitions(print.config(), object_bottom_z); this->collect_extruder_statistics(prime_multi_material); + + // Assign custom G-code actions from Model::custom_gcode_per_print_z to their respecive layers, + // ignoring the extruder switches, which were processed above, and ignoring color changes for extruders, + // that do not print above their respective print_z. + this->assign_custom_gcodes(print); } void ToolOrdering::initialize_layers(std::vector &zs) @@ -444,6 +458,46 @@ void ToolOrdering::collect_extruder_statistics(bool prime_multi_material) } } +// Assign a pointer to a custom G-code to the respective ToolOrdering::LayerTools. +// Ignore color changes, which are performed on a layer and for such an extruder, that the extruder will not be printing above that layer. +// If multiple events are planned over a span of a single layer, use the last one. +void ToolOrdering::assign_custom_gcodes(const Print &print) +{ + const std::vector &custom_gcode_per_print_z = print.model().custom_gcode_per_print_z; + if (custom_gcode_per_print_z.empty()) + return; + + unsigned int num_extruders = *std::max_element(m_all_printing_extruders.begin(), m_all_printing_extruders.end()) + 1; + std::vector extruder_printing_above(num_extruders, false); + auto custom_gcode_it = custom_gcode_per_print_z.rbegin(); + // From the last layer to the first one: + for (auto it_lt = m_layer_tools.rbegin(); it_lt != m_layer_tools.rend(); ++ it_lt) { + LayerTools < = *it_lt; + // Add the extruders of the current layer to the set of extruders printing at and above this print_z. + for (unsigned int i : lt.extruders) + extruder_printing_above[i] = true; + // Skip all custom G-codes above this layer and skip all extruder switches. + for (; custom_gcode_it != custom_gcode_per_print_z.rend() && (custom_gcode_it->print_z > lt.print_z + EPSILON || custom_gcode_it->gcode == ExtruderChangeCode); ++ custom_gcode_it); + if (custom_gcode_it == custom_gcode_per_print_z.rend()) + // Custom G-codes were processed. + break; + // Some custom G-code is configured for this layer or a layer below. + const Model::CustomGCode &custom_gcode = *custom_gcode_it; + // print_z of the layer below the current layer. + coordf_t print_z_below = 0.; + if (auto it_lt_below = it_lt; -- it_lt_below != m_layer_tools.rend()) + print_z_below = it_lt_below->print_z; + if (custom_gcode.print_z > print_z_below - EPSILON) { + // The custom G-code applies to the current layer. + if (custom_gcode.gcode != ColorChangeCode || extruder_printing_above[unsigned(custom_gcode.extruder - 1)]) + // If it is color change, it will actually be useful as the exturder above will print. + lt.custom_gcode = &custom_gcode; + // Consume that custom G-code event. + -- custom_gcode_it; + } + } +} + const LayerTools& ToolOrdering::tools_for_layer(coordf_t print_z) const { auto it_layer_tools = std::lower_bound(m_layer_tools.begin(), m_layer_tools.end(), LayerTools(print_z - EPSILON)); diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp index 1f043d94f4..54024fc72f 100644 --- a/src/libslic3r/GCode/ToolOrdering.hpp +++ b/src/libslic3r/GCode/ToolOrdering.hpp @@ -80,14 +80,7 @@ private: class LayerTools { public: - LayerTools(const coordf_t z) : - print_z(z), - has_object(false), - has_support(false), - extruder_override(0), - has_wipe_tower(false), - wipe_tower_partitions(0), - wipe_tower_layer_height(0.) {} + LayerTools(const coordf_t z) : print_z(z) {} // Changing these operators to epsilon version can make a problem in cases where support and object layers get close to each other. // In case someone tries to do it, make sure you know what you're doing and test it properly (slice multiple objects at once with supports). @@ -104,22 +97,24 @@ public: // Returns a zero based extruder this eec should be printed with, according to PrintRegion config or extruder_override if overriden. unsigned int extruder(const ExtrusionEntityCollection &extrusions, const PrintRegion ®ion) const; - coordf_t print_z; - bool has_object; - bool has_support; + coordf_t print_z = 0.; + bool has_object = false; + bool has_support = false; // Zero based extruder IDs, ordered to minimize tool switches. std::vector extruders; // If per layer extruder switches are inserted by the G-code preview slider, this value contains the new (1 based) extruder, with which the whole object layer is being printed with. // If not overriden, it is set to 0. - unsigned int extruder_override; + unsigned int extruder_override = 0; // Will there be anything extruded on this layer for the wipe tower? // Due to the support layers possibly interleaving the object layers, // wipe tower will be disabled for some support only layers. - bool has_wipe_tower; + bool has_wipe_tower = false; // Number of wipe tower partitions to support the required number of tool switches // and to support the wipe tower partitions above this one. - size_t wipe_tower_partitions; - coordf_t wipe_tower_layer_height; + size_t wipe_tower_partitions = 0; + coordf_t wipe_tower_layer_height = 0.; + // Custom G-code (color change, extruder switch, pause) to be performed before this layer starts to print. + const Model::CustomGCode *custom_gcode = nullptr; WipingExtrusions& wiping_extrusions() { m_wiping_extrusions.set_layer_tools_ptr(this); @@ -144,7 +139,7 @@ public: // For the use case when all objects are printed at once. // (print.config.complete_objects is false). - ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material = false, const std::vector> *per_layer_extruder_switches = nullptr); + ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material = false); void clear() { m_layer_tools.clear(); } @@ -175,6 +170,7 @@ private: void reorder_extruders(unsigned int last_extruder_id); void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z); void collect_extruder_statistics(bool prime_multi_material); + void assign_custom_gcodes(const Print &print); std::vector m_layer_tools; // First printing extruder, including the multi-material priming sequence. @@ -184,7 +180,6 @@ private: // All extruders, which extrude some material over m_layer_tools. std::vector m_all_printing_extruders; - const PrintConfig* m_print_config_ptr = nullptr; }; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 2d327c70b9..ed88178825 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1912,8 +1912,8 @@ const WipeTowerData& Print::wipe_tower_data(size_t extruders_cnt, double first_l // If the wipe tower wasn't created yet, make sure the depth and brim_width members are set to default. if (! is_step_done(psWipeTower) && extruders_cnt !=0) { - float width = m_config.wipe_tower_width; - float brim_spacing = nozzle_diameter * 1.25f - first_layer_height * (1. - M_PI_4); + float width = float(m_config.wipe_tower_width); + float brim_spacing = float(nozzle_diameter * 1.25f - first_layer_height * (1. - M_PI_4)); const_cast(this)->m_wipe_tower_data.depth = (900.f/width) * float(extruders_cnt - 1); const_cast(this)->m_wipe_tower_data.brim_width = 4.5f * brim_spacing; @@ -1938,8 +1938,7 @@ void Print::_make_wipe_tower() wipe_volumes.push_back(std::vector(wiping_matrix.begin()+i*number_of_extruders, wiping_matrix.begin()+(i+1)*number_of_extruders)); // Let the ToolOrdering class know there will be initial priming extrusions at the start of the print. - m_wipe_tower_data.tool_ordering = ToolOrdering(*this, (unsigned int)-1, true, - (this->object_extruders().size() == 1) ? &custom_tool_changes(this->model(), (unsigned int)m_config.nozzle_diameter.size()) : nullptr); + m_wipe_tower_data.tool_ordering = ToolOrdering(*this, (unsigned int)-1, true); if (! m_wipe_tower_data.tool_ordering.has_wipe_tower()) // Don't generate any wipe tower. From c96fc5eb513c2f09ac561cb8893bad72b2692a50 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 14 Jan 2020 12:10:01 +0100 Subject: [PATCH 26/33] gcc & clang do not like taking reference of const temporaries. --- src/libslic3r/GCode/ToolOrdering.cpp | 19 ++++++++----------- src/libslic3r/GCode/ToolOrdering.hpp | 2 +- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index c2febbafb6..628379aaf9 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -89,7 +89,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude } // Collect extruders reuqired to print the layers. - this->collect_extruders(object, nullptr); + this->collect_extruders(object, std::vector>()); // Reorder the extruders to minimize tool switches. this->reorder_extruders(first_extruder); @@ -128,10 +128,9 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool // Use the extruder switches from Model::custom_gcode_per_print_z to override the extruder to print the object. // Do it only if all the objects were configured to be printed with a single extruder. - const std::vector> *per_layer_extruder_switches = (print.object_extruders().size() == 1) ? - &custom_tool_changes(print.model(), (unsigned int)print.config().nozzle_diameter.size()) : nullptr; - if (per_layer_extruder_switches != nullptr && per_layer_extruder_switches->empty()) - per_layer_extruder_switches = nullptr; + std::vector> per_layer_extruder_switches; + if (print.object_extruders().size() == 1) + per_layer_extruder_switches = custom_tool_changes(print.model(), (unsigned int)print.config().nozzle_diameter.size()); // Collect extruders reuqired to print the layers. for (auto object : print.objects()) @@ -166,7 +165,7 @@ void ToolOrdering::initialize_layers(std::vector &zs) } // Collect extruders reuqired to print layers. -void ToolOrdering::collect_extruders(const PrintObject &object, const std::vector> *per_layer_extruder_switches) +void ToolOrdering::collect_extruders(const PrintObject &object, const std::vector> &per_layer_extruder_switches) { // Collect the support extruders. for (auto support_layer : object.support_layers()) { @@ -186,8 +185,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto // Extruder overrides are ordered by print_z. std::vector>::const_iterator it_per_layer_extruder_override; - if (per_layer_extruder_switches != nullptr) - it_per_layer_extruder_override = per_layer_extruder_switches->begin(); + it_per_layer_extruder_override = per_layer_extruder_switches.begin(); unsigned int extruder_override = 0; // Collect the object extruders. @@ -195,9 +193,8 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto LayerTools &layer_tools = this->tools_for_layer(layer->print_z); // Override extruder with the next - if (per_layer_extruder_switches != nullptr) - for (; it_per_layer_extruder_override != per_layer_extruder_switches->end() && it_per_layer_extruder_override->first < layer->print_z + EPSILON; ++ it_per_layer_extruder_override) - extruder_override = (int)it_per_layer_extruder_override->second; + for (; it_per_layer_extruder_override != per_layer_extruder_switches.end() && it_per_layer_extruder_override->first < layer->print_z + EPSILON; ++ it_per_layer_extruder_override) + extruder_override = (int)it_per_layer_extruder_override->second; // Store the current extruder override (set to zero if no overriden), so that layer_tools.wiping_extrusions().is_overridable_and_mark() will use it. layer_tools.extruder_override = extruder_override; diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp index 54024fc72f..a9d5a98e70 100644 --- a/src/libslic3r/GCode/ToolOrdering.hpp +++ b/src/libslic3r/GCode/ToolOrdering.hpp @@ -166,7 +166,7 @@ public: private: void initialize_layers(std::vector &zs); - void collect_extruders(const PrintObject &object, const std::vector> *per_layer_extruder_switches); + void collect_extruders(const PrintObject &object, const std::vector> &per_layer_extruder_switches); void reorder_extruders(unsigned int last_extruder_id); void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z); void collect_extruder_statistics(bool prime_multi_material); From b54ce0d468d0d842861f93f875c42d26cbc16f92 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 14 Jan 2020 14:24:38 +0100 Subject: [PATCH 27/33] Fixing GCode Skirt generator after refactoring. --- src/libslic3r/GCode.cpp | 36 +++++++++++++++++------------------- src/libslic3r/GCode.hpp | 2 +- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 6fa145bf63..c42c1c35a2 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1800,14 +1800,13 @@ namespace Skirt { const std::vector &layers, const LayerTools &layer_tools, std::vector extruder_ids, - // Heights at which the skirt has already been extruded. - std::vector &skirt_done) + // Heights (print_z) at which the skirt has already been extruded. + std::vector &skirt_done) { // Extrude skirt at the print_z of the raft layers and normal object layers // not at the print_z of the interlaced support material layers. std::map> skirt_loops_per_extruder_out; - assert(skirt_done.empty()); - if (print.has_skirt() && ! print.skirt().entities.empty()) { + if (skirt_done.empty() && print.has_skirt() && ! print.skirt().entities.empty()) { // Prime all the printing extruders over the skirt lines. // Reorder the extruders, so that the last used extruder is at the front. unsigned int first_extruder_id = layer_tools.extruders.front(); @@ -1835,7 +1834,7 @@ namespace Skirt { (i == 0) ? 0 : extruder_loops[i - 1], ((i == 0) ? 0 : extruder_loops[i - 1]) + extruder_loops[i]); } - skirt_done.emplace_back(layer_tools.print_z - (skirt_done.empty() ? 0. : skirt_done.back())); + skirt_done.emplace_back(layer_tools.print_z); } return skirt_loops_per_extruder_out; @@ -1845,8 +1844,8 @@ namespace Skirt { const Print &print, const std::vector &layers, const LayerTools &layer_tools, - // Heights at which the skirt has already been extruded. - std::vector &skirt_done) + // Heights (print_z) at which the skirt has already been extruded. + std::vector &skirt_done) { // Extrude skirt at the print_z of the raft layers and normal object layers // not at the print_z of the interlaced support material layers. @@ -1855,16 +1854,15 @@ namespace Skirt { // Not enough skirt layers printed yet. //FIXME infinite or high skirt does not make sense for sequential print! (skirt_done.size() < (size_t)print.config().skirt_height.value || print.has_infinite_skirt()) && - // This print_z has not been extruded yet + // This print_z has not been extruded yet (sequential print) skirt_done.back() < layer_tools.print_z - EPSILON && // and this layer is an object layer, or it is a raft layer. - //FIXME one uses the number of raft layers from the 1st object! (layer_tools.has_object || layers.front().support_layer->id() < (size_t)layers.front().support_layer->object()->config().raft_layers.value)) { // Extrude all skirts with the current extruder. unsigned int first_extruder_id = layer_tools.extruders.front(); skirt_loops_per_extruder_out[first_extruder_id] = std::pair(0, print.config().skirts.value); assert(!skirt_done.empty()); - skirt_done.emplace_back(layer_tools.print_z - skirt_done.back()); + skirt_done.emplace_back(layer_tools.print_z); } return skirt_loops_per_extruder_out; } @@ -1974,15 +1972,15 @@ void GCode::process_layer( // Map from extruder ID to index of skirt loops to be extruded with that extruder. std::map> skirt_loops_per_extruder; - if (single_object_instance_idx == size_t(-1) && object_layer != nullptr) { - // Normal (non-sequential) print. - gcode += ProcessLayer::emit_custom_gcode_per_print_z(layer_tools.custom_gcode, first_extruder_id, print.config().nozzle_diameter.size() == 1); - // Extrude skirt at the print_z of the raft layers and normal object layers - // not at the print_z of the interlaced support material layers. - skirt_loops_per_extruder = first_layer ? - Skirt::make_skirt_loops_per_extruder_1st_layer(print, layers, layer_tools, m_writer.extruder_ids(), m_skirt_done) : - Skirt::make_skirt_loops_per_extruder_other_layers(print, layers, layer_tools, m_skirt_done); + if (single_object_instance_idx == size_t(-1)) { + // Normal (non-sequential) print. + gcode += ProcessLayer::emit_custom_gcode_per_print_z(layer_tools.custom_gcode, first_extruder_id, print.config().nozzle_diameter.size() == 1); } + // Extrude skirt at the print_z of the raft layers and normal object layers + // not at the print_z of the interlaced support material layers. + skirt_loops_per_extruder = first_layer ? + Skirt::make_skirt_loops_per_extruder_1st_layer(print, layers, layer_tools, m_writer.extruder_ids(), m_skirt_done) : + Skirt::make_skirt_loops_per_extruder_other_layers(print, layers, layer_tools, m_skirt_done); // Group extrusions by an extruder, then by an object, an island and a region. std::map> by_extruder; @@ -2157,7 +2155,7 @@ void GCode::process_layer( this->set_origin(0., 0.); m_avoid_crossing_perimeters.use_external_mp = true; Flow layer_skirt_flow(print.skirt_flow()); - layer_skirt_flow.height = (float)(m_skirt_done.back() - ((m_skirt_done.size() == 1) ? 0. : m_skirt_done[m_skirt_done.size() - 2])); + layer_skirt_flow.height = float(m_skirt_done.back() - (m_skirt_done.size() == 1 ? 0. : m_skirt_done[m_skirt_done.size() - 2])); double mm3_per_mm = layer_skirt_flow.mm3_per_mm(); for (size_t i = loops.first; i < loops.second; ++i) { // Adjust flow according to this layer's layer height. diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index aee8ee2e25..0344924a13 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -366,7 +366,7 @@ private: #endif /* HAS_PRESSURE_EQUALIZER */ std::unique_ptr m_wipe_tower; - // Heights at which the skirt has already been extruded. + // Heights (print_z) at which the skirt has already been extruded. std::vector m_skirt_done; // Has the brim been extruded already? Brim is being extruded only for the first object of a multi-object print. bool m_brim_done; From ab6a2b71e8d40df4956626d758063f956229e218 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 14 Jan 2020 15:12:45 +0100 Subject: [PATCH 28/33] Another bugfixes of GCode export after refactoring. --- src/libslic3r/GCode/ToolOrdering.cpp | 5 ++--- src/libslic3r/GCodeWriter.cpp | 3 ++- src/libslic3r/GCodeWriter.hpp | 2 +- src/libslic3r/Print.hpp | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 628379aaf9..0b8fa11321 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -570,9 +570,8 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int const LayerTools& lt = *m_layer_tools; const float min_infill_volume = 0.f; // ignore infill with smaller volume than this - assert(volume_to_wipe >= 0.); - if (! this->something_overridable || volume_to_wipe == 0. || print.config().filament_soluble.get_at(old_extruder) || print.config().filament_soluble.get_at(new_extruder)) - return volume_to_wipe; // Soluble filament cannot be wiped in a random infill, neither the filament after it + if (! this->something_overridable || volume_to_wipe <= 0. || print.config().filament_soluble.get_at(old_extruder) || print.config().filament_soluble.get_at(new_extruder)) + return 0.f; // Soluble filament cannot be wiped in a random infill, neither the filament after it // we will sort objects so that dedicated for wiping are at the beginning: PrintObjectPtrs object_list = print.objects(); diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index 0266eee39e..4c53048dc9 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -23,8 +23,9 @@ void GCodeWriter::apply_print_config(const PrintConfig &print_config) print_config.machine_max_acceleration_extruding.values.front() : 0); } -void GCodeWriter::set_extruders(const std::vector &extruder_ids) +void GCodeWriter::set_extruders(std::vector extruder_ids) { + std::sort(extruder_ids.begin(), extruder_ids.end()); m_extruders.clear(); m_extruders.reserve(extruder_ids.size()); for (unsigned int extruder_id : extruder_ids) diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index 1631d8590e..667c1ef95a 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -33,7 +33,7 @@ public: std::string extrusion_axis() const { return m_extrusion_axis; } void apply_print_config(const PrintConfig &print_config); // Extruders are expected to be sorted in an increasing order. - void set_extruders(const std::vector &extruder_ids); + void set_extruders(std::vector extruder_ids); const std::vector& extruders() const { return m_extruders; } std::vector extruder_ids() const { std::vector out; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 89d27e3cb8..42f8d761e1 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -52,7 +52,7 @@ public: // Average diameter of nozzles participating on extruding this region. coordf_t bridging_height_avg(const PrintConfig &print_config) const; - // Collect extruder indices used to print this region's object. + // Collect 0-based extruder indices used to print this region's object. void collect_object_printing_extruders(std::vector &object_extruders) const; static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig ®ion_config, std::vector &object_extruders); From fd6c9582d52f05abdd9851ff02894bfc8127c2ef Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 14 Jan 2020 15:43:43 +0100 Subject: [PATCH 29/33] Refactoring fix: making sure that mark_wiping_extrusions doesn't report it wiped something it didn't --- src/libslic3r/GCode/ToolOrdering.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 0b8fa11321..d937d4eed3 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -571,7 +571,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int const float min_infill_volume = 0.f; // ignore infill with smaller volume than this if (! this->something_overridable || volume_to_wipe <= 0. || print.config().filament_soluble.get_at(old_extruder) || print.config().filament_soluble.get_at(new_extruder)) - return 0.f; // Soluble filament cannot be wiped in a random infill, neither the filament after it + return std::max(0.f, volume_to_wipe); // Soluble filament cannot be wiped in a random infill, neither the filament after it // we will sort objects so that dedicated for wiping are at the beginning: PrintObjectPtrs object_list = print.objects(); From 390376daae8a60b7fb7bf856c83865eb04e23c2e Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 14 Jan 2020 16:10:53 +0100 Subject: [PATCH 30/33] Fix of recent refactoring of color print. --- src/libslic3r/GCode/ToolOrdering.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index d937d4eed3..4374d5be06 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -482,7 +482,7 @@ void ToolOrdering::assign_custom_gcodes(const Print &print) const Model::CustomGCode &custom_gcode = *custom_gcode_it; // print_z of the layer below the current layer. coordf_t print_z_below = 0.; - if (auto it_lt_below = it_lt; -- it_lt_below != m_layer_tools.rend()) + if (auto it_lt_below = it_lt; ++ it_lt_below != m_layer_tools.rend()) print_z_below = it_lt_below->print_z; if (custom_gcode.print_z > print_z_below - EPSILON) { // The custom G-code applies to the current layer. @@ -490,7 +490,7 @@ void ToolOrdering::assign_custom_gcodes(const Print &print) // If it is color change, it will actually be useful as the exturder above will print. lt.custom_gcode = &custom_gcode; // Consume that custom G-code event. - -- custom_gcode_it; + ++ custom_gcode_it; } } } From 10d1b459bb305683a6c919ced40f98d1d6553928 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 14 Jan 2020 16:38:34 +0100 Subject: [PATCH 31/33] Fix of Skirt loops not being honored, only producing single loop #2193 First layer skirt only has half the loops when using multiple extruders #469 The skirt generator used to prime all printing extruders at the 1st layer if enough skirt lines were configured, while at the other layers the skirt used to be printed with the 1st extruder printing the layer. There was a bug introduced quite a long time ago, where not enough skirt lines were extruded if some extruders were not needed to print the 1st layer. Newly the skirt generator works the same way on all layers: All the extruders planned to print a layer are primed over the skirt if enough skirt lines are configured. --- src/libslic3r/GCode.cpp | 61 ++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 37 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index c42c1c35a2..870749867e 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1729,7 +1729,7 @@ std::vector GCode::sort_print_object_instances( namespace ProcessLayer { - std::string emit_custom_gcode_per_print_z( + static std::string emit_custom_gcode_per_print_z( const Model::CustomGCode *custom_gcode, // ID of the first extruder printing this layer. unsigned int first_extruder_id, @@ -1795,11 +1795,20 @@ namespace ProcessLayer } // namespace ProcessLayer namespace Skirt { - std::map> make_skirt_loops_per_extruder_1st_layer( + static void skirt_loops_per_extruder_all_printing(const Print &print, const LayerTools &layer_tools, std::map> &skirt_loops_per_extruder_out) + { + // Prime all extruders printing over the 1st layer over the skirt lines. + size_t n_loops = print.skirt().entities.size(); + size_t n_tools = layer_tools.extruders.size(); + size_t lines_per_extruder = (n_loops + n_tools - 1) / n_tools; + for (size_t i = 0; i < n_loops; i += lines_per_extruder) + skirt_loops_per_extruder_out[layer_tools.extruders[i / lines_per_extruder]] = std::pair(i, std::min(i + lines_per_extruder, n_loops)); + } + + static std::map> make_skirt_loops_per_extruder_1st_layer( const Print &print, - const std::vector &layers, + const std::vector & /*layers */, const LayerTools &layer_tools, - std::vector extruder_ids, // Heights (print_z) at which the skirt has already been extruded. std::vector &skirt_done) { @@ -1807,40 +1816,13 @@ namespace Skirt { // not at the print_z of the interlaced support material layers. std::map> skirt_loops_per_extruder_out; if (skirt_done.empty() && print.has_skirt() && ! print.skirt().entities.empty()) { - // Prime all the printing extruders over the skirt lines. - // Reorder the extruders, so that the last used extruder is at the front. - unsigned int first_extruder_id = layer_tools.extruders.front(); - for (size_t i = 1; i < extruder_ids.size(); ++ i) - if (extruder_ids[i] == first_extruder_id) { - // Move the last extruder to the front. - memmove(extruder_ids.data() + 1, extruder_ids.data(), i * sizeof(unsigned int)); - extruder_ids.front() = first_extruder_id; - break; - } - size_t n_loops = print.skirt().entities.size(); - if (n_loops <= extruder_ids.size()) { - for (size_t i = 0; i < n_loops; ++i) - skirt_loops_per_extruder_out[extruder_ids[i]] = std::pair(i, i + 1); - } else { - // Assign skirt loops to the extruders. - std::vector extruder_loops(extruder_ids.size(), 1); - n_loops -= extruder_loops.size(); - while (n_loops > 0) { - for (size_t i = 0; i < extruder_ids.size() && n_loops > 0; ++i, --n_loops) - ++extruder_loops[i]; - } - for (size_t i = 0; i < extruder_ids.size(); ++i) - skirt_loops_per_extruder_out[extruder_ids[i]] = std::make_pair( - (i == 0) ? 0 : extruder_loops[i - 1], - ((i == 0) ? 0 : extruder_loops[i - 1]) + extruder_loops[i]); - } + skirt_loops_per_extruder_all_printing(print, layer_tools, skirt_loops_per_extruder_out); skirt_done.emplace_back(layer_tools.print_z); - } return skirt_loops_per_extruder_out; } - std::map> make_skirt_loops_per_extruder_other_layers( + static std::map> make_skirt_loops_per_extruder_other_layers( const Print &print, const std::vector &layers, const LayerTools &layer_tools, @@ -1858,9 +1840,14 @@ namespace Skirt { skirt_done.back() < layer_tools.print_z - EPSILON && // and this layer is an object layer, or it is a raft layer. (layer_tools.has_object || layers.front().support_layer->id() < (size_t)layers.front().support_layer->object()->config().raft_layers.value)) { - // Extrude all skirts with the current extruder. - unsigned int first_extruder_id = layer_tools.extruders.front(); - skirt_loops_per_extruder_out[first_extruder_id] = std::pair(0, print.config().skirts.value); +#if 0 + // Prime just the first printing extruder. This is original Slic3r's implementation. + skirt_loops_per_extruder_out[layer_tools.extruders.front()] = std::pair(0, print.config().skirts.value); +#else + // Prime all extruders planned for this layer, see + // https://github.com/prusa3d/PrusaSlicer/issues/469#issuecomment-322450619 + skirt_loops_per_extruder_all_printing(print, layer_tools, skirt_loops_per_extruder_out); +#endif assert(!skirt_done.empty()); skirt_done.emplace_back(layer_tools.print_z); } @@ -1979,7 +1966,7 @@ void GCode::process_layer( // Extrude skirt at the print_z of the raft layers and normal object layers // not at the print_z of the interlaced support material layers. skirt_loops_per_extruder = first_layer ? - Skirt::make_skirt_loops_per_extruder_1st_layer(print, layers, layer_tools, m_writer.extruder_ids(), m_skirt_done) : + Skirt::make_skirt_loops_per_extruder_1st_layer(print, layers, layer_tools, m_skirt_done) : Skirt::make_skirt_loops_per_extruder_other_layers(print, layers, layer_tools, m_skirt_done); // Group extrusions by an extruder, then by an object, an island and a region. From 2c958c021dfec353fe598d3c818e83edadceb7d4 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 14 Jan 2020 17:35:42 +0100 Subject: [PATCH 32/33] Fixed regression in placement of Color Change event at the correct layer. --- src/libslic3r/GCode/ToolOrdering.cpp | 2 +- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 4374d5be06..a76f0fafbf 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -484,7 +484,7 @@ void ToolOrdering::assign_custom_gcodes(const Print &print) coordf_t print_z_below = 0.; if (auto it_lt_below = it_lt; ++ it_lt_below != m_layer_tools.rend()) print_z_below = it_lt_below->print_z; - if (custom_gcode.print_z > print_z_below - EPSILON) { + if (custom_gcode.print_z > print_z_below + 0.5 * EPSILON) { // The custom G-code applies to the current layer. if (custom_gcode.gcode != ColorChangeCode || extruder_printing_above[unsigned(custom_gcode.extruder - 1)]) // If it is color change, it will actually be useful as the exturder above will print. diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 9d6a2a5ec7..8af721f9da 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -95,14 +95,6 @@ void BackgroundSlicingProcess::process_fff() m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data); #endif // ENABLE_THUMBNAIL_GENERATOR - /* #ys_FIXME_no_exported_codes - if (m_fff_print->model().custom_gcode_per_print_z != GUI::wxGetApp().model().custom_gcode_per_print_z) { - GUI::wxGetApp().model().custom_gcode_per_print_z = m_fff_print->model().custom_gcode_per_print_z; - GUI::show_info(nullptr, _(L("To except of redundant tool manipulation, \n" - "Color change(s) for unused extruder(s) was(were) deleted")), _(L("Info"))); - } - */ - if (this->set_step_started(bspsGCodeFinalize)) { if (! m_export_path.empty()) { //FIXME localize the messages From 536514ff03fadec4fa92776f3fb81e0e9cec17ef Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 15 Jan 2020 09:33:19 +0100 Subject: [PATCH 33/33] Added default filament(resin) in wizard for selected printers Added default_materials field to "Vendor".ini --- resources/profiles/Creality.ini | 1 + resources/profiles/PrusaResearch.ini | 12 +++ src/slic3r/GUI/ConfigWizard.cpp | 106 +++++++++++++++++++++++- src/slic3r/GUI/ConfigWizard_private.hpp | 13 ++- 4 files changed, 128 insertions(+), 4 deletions(-) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index 4ebf99d328..eba85fef17 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -18,6 +18,7 @@ config_update_url = http://files.prusa3d.com/wp-content/uploads/repository/Prusa name = Creality Ender-3 variants = 0.4 technology = FFF +default_materials = Creality PLA @ENDER3; Prusament PLA @ENDER3 # All presets starting with asterisk, for example *common*, are intermediate and they will # not make it into the user interface. diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 746aaf9668..a2dfc49139 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -22,72 +22,84 @@ name = Original Prusa MINI variants = 0.4; 0.25; 0.6 technology = FFF family = MINI +default_materials = Prusament PLA; Prusament PETG @MINI [printer_model:MK3S] name = Original Prusa i3 MK3S variants = 0.4; 0.25; 0.6 technology = FFF family = MK3 +default_materials = Prusament PLA; Prusament PETG [printer_model:MK3] name = Original Prusa i3 MK3 variants = 0.4; 0.25; 0.6 technology = FFF family = MK3 +default_materials = Prusament PLA; Prusament PETG [printer_model:MK3SMMU2S] name = Original Prusa i3 MK3S MMU2S variants = 0.4; 0.25; 0.6 technology = FFF family = MK3 +default_materials = Prusament PLA @MMU2; Prusament PETG @MMU2 [printer_model:MK3MMU2] name = Original Prusa i3 MK3 MMU2 variants = 0.4; 0.25; 0.6 technology = FFF family = MK3 +default_materials = Prusament PLA @MMU2; Prusament PETG @MMU2 [printer_model:MK2.5S] name = Original Prusa i3 MK2.5S variants = 0.4; 0.25; 0.6 technology = FFF family = MK2.5 +default_materials = Prusament PLA; Prusament PETG [printer_model:MK2.5] name = Original Prusa i3 MK2.5 variants = 0.4; 0.25; 0.6 technology = FFF family = MK2.5 +default_materials = Prusament PLA; Prusament PETG [printer_model:MK2.5SMMU2S] name = Original Prusa i3 MK2.5S MMU2S variants = 0.4; 0.25; 0.6 technology = FFF family = MK2.5 +default_materials = Prusament PLA @MMU2; Prusament PETG @MMU2 [printer_model:MK2.5MMU2] name = Original Prusa i3 MK2.5 MMU2 variants = 0.4; 0.25; 0.6 technology = FFF family = MK2.5 +default_materials = Prusament PLA @MMU2; Prusament PETG @MMU2 [printer_model:MK2S] name = Original Prusa i3 MK2S variants = 0.4; 0.25; 0.6 technology = FFF family = MK2 +default_materials = Prusament PLA; Prusament PETG [printer_model:MK2SMM] name = Original Prusa i3 MK2S MMU1 variants = 0.4; 0.6 technology = FFF family = MK2 +default_materials = Prusament PLA; Prusament PETG @MMU1 [printer_model:SL1] name = Original Prusa SL1 variants = default technology = SLA family = SL1 +default_materials = Prusa Transparent Tough @0.05 # All presets starting with asterisk, for example *common*, are intermediate and they will # not make it into the user interface. diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 8a4b28d3a4..add680a40f 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -350,6 +350,21 @@ bool PrinterPicker::any_selected() const return false; } +std::set PrinterPicker::get_selected_models() const +{ + std::set ret_set; + + for (const auto& cb : cboxes) + if (cb->GetValue()) + ret_set.emplace(cb->model); + + for (const auto& cb : cboxes_alt) + if (cb->GetValue()) + ret_set.emplace(cb->model); + + return ret_set; +} + void PrinterPicker::on_checkbox(const Checkbox *cbox, bool checked) { PrinterPickerEvent evt(EVT_PRINTER_PICK, GetId(), vendor_id, cbox->model, cbox->variant, checked); @@ -500,6 +515,19 @@ bool PagePrinters::any_selected() const return false; } +std::set PagePrinters::get_selected_models() +{ + std::set ret_set; + + for (const auto *picker : printer_pickers) + { + std::set tmp_models = picker->get_selected_models(); + ret_set.insert(tmp_models.begin(), tmp_models.end()); + } + + return ret_set; +} + void PagePrinters::set_run_reason(ConfigWizard::RunReason run_reason) { if (technology == T_FFF @@ -1600,6 +1628,10 @@ void ConfigWizard::priv::on_printer_pick(PagePrinters *page, const PrinterPicker preset.is_visible = evt.enable; } } + + // if at list one printer is selected but there in no one selected material, + // select materials which is default for selected printer(s) + select_default_materials_if_needed(pair.second.vendor_profile, page->technology, evt.model_id); } if (page->technology & T_FFF) { @@ -1609,6 +1641,57 @@ void ConfigWizard::priv::on_printer_pick(PagePrinters *page, const PrinterPicker } } +void ConfigWizard::priv::select_default_materials_for_printer_model(const std::vector& models, Technology technology, const std::string& model_id) +{ + PageMaterials* page_materials = technology & T_FFF ? page_filaments : page_sla_materials; + + auto it = std::find_if(models.begin(), models.end(), [model_id](VendorProfile::PrinterModel model) {return model_id == model.id; }); + if (it != models.end()) + for (const std::string& material : it->default_materials) + appconfig_new.set(page_materials->materials->appconfig_section(), material, "1"); +} + +void ConfigWizard::priv::select_default_materials_if_needed(VendorProfile* vendor_profile, Technology technology, const std::string& model_id) +{ + if ((technology & T_FFF && !any_fff_selected) || + (technology & T_SLA && !any_sla_selected) || + check_materials_in_config(technology, false)) + return; + + select_default_materials_for_printer_model(vendor_profile->models, technology, model_id); +} + +void ConfigWizard::priv::selected_default_materials(Technology technology) +{ + auto select_default_materials_for_printer_page = [this](PagePrinters * page_printers, Technology technology) + { + std::set selected_models = page_printers->get_selected_models(); + const std::string vendor_id = page_printers->get_vendor_id(); + + for (auto& pair : bundles) + { + if (pair.first != vendor_id) + continue; + + for (const std::string& model_id : selected_models) + select_default_materials_for_printer_model(pair.second.vendor_profile->models, technology, model_id); + } + }; + + PagePrinters* page_printers = technology & T_FFF ? page_fff : page_msla; + select_default_materials_for_printer_page(page_printers, technology); + + for (const auto& printer : pages_3rdparty) + { + page_printers = technology & T_FFF ? printer.second.first : printer.second.second; + if (page_printers) + select_default_materials_for_printer_page(page_printers, technology); + } + + update_materials(technology); + (technology& T_FFF ? page_filaments : page_sla_materials)->reload_presets(); +} + void ConfigWizard::priv::on_3rdparty_install(const VendorProfile *vendor, bool install) { auto it = pages_3rdparty.find(vendor->id); @@ -1645,7 +1728,7 @@ bool ConfigWizard::priv::on_bnt_finish() return check_materials_in_config(T_ANY); } -bool ConfigWizard::priv::check_materials_in_config(Technology technology) +bool ConfigWizard::priv::check_materials_in_config(Technology technology, bool show_info_msg) { const auto exist_preset = [this](const std::string& section, const Materials& materials) { @@ -1660,15 +1743,32 @@ bool ConfigWizard::priv::check_materials_in_config(Technology technology) return false; }; + const auto ask_and_selected_default_materials = [this](wxString message, Technology technology) + { + wxMessageDialog msg(q, message, _(L("Notice")), wxYES_NO); + if (msg.ShowModal() == wxID_YES) + selected_default_materials(technology); + }; + if (any_fff_selected && technology & T_FFF && !exist_preset(AppConfig::SECTION_FILAMENTS, filaments)) { - show_info(q, _(L("You have to select at least one filament for selected printers")), ""); + if (show_info_msg) + { + wxString message = _(L("You have to select at least one filament for selected printers")) + "\n\n\t" + + _(L("Do you want to automatic select default filaments?")); + ask_and_selected_default_materials(message, T_FFF); + } return false; } if (any_sla_selected && technology & T_SLA && !exist_preset(AppConfig::SECTION_MATERIALS, sla_materials)) { - show_info(q, _(L("You have to select at least one material for selected printers")), ""); + if (show_info_msg) + { + wxString message = _(L("You have to select at least one material for selected printers")) + "\n\n\t" + + _(L("Do you want to automatic select default materials?")); + ask_and_selected_default_materials(message, T_SLA); + } return false; } diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index 3ea5321f84..9c14633c9f 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -149,6 +149,7 @@ struct PrinterPicker: wxPanel void select_all(bool select, bool alternates = false); void select_one(size_t i, bool select); bool any_selected() const; + std::set get_selected_models() const ; int get_width() const { return width; } const std::vector& get_button_indexes() { return m_button_indexes; } @@ -215,6 +216,9 @@ struct PagePrinters: ConfigWizardPage void select_all(bool select, bool alternates = false); int get_width() const; bool any_selected() const; + std::set get_selected_models(); + + std::string get_vendor_id() const { return printer_pickers.empty() ? "" : printer_pickers[0]->vendor_id; } virtual void set_run_reason(ConfigWizard::RunReason run_reason) override; }; @@ -503,10 +507,17 @@ struct ConfigWizard::priv void on_custom_setup(); void on_printer_pick(PagePrinters *page, const PrinterPickerEvent &evt); + void select_default_materials_for_printer_model(const std::vector &models, + Technology technology, + const std::string & model_id); + void select_default_materials_if_needed(VendorProfile* vendor_profile, + Technology technology, + const std::string &model_id); + void selected_default_materials(Technology technology); void on_3rdparty_install(const VendorProfile *vendor, bool install); bool on_bnt_finish(); - bool check_materials_in_config(Technology technology); + bool check_materials_in_config(Technology technology, bool show_info_msg = true); void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater); // #ys_FIXME_alise void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add);