From d336dbe1a5b351f8fac9175c8a9f251d9e450d59 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 18 Nov 2021 16:39:07 +0100 Subject: [PATCH 01/16] Fix broken hollowing preview with defined holes and unchecked hollowing fixes SPE-1121 --- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index cf4b18a86b..d2a7e0d73e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -271,11 +271,9 @@ void HollowedMesh::on_update() m_old_hollowing_timestamp = timestamp; indexed_triangle_set interior = print_object->hollowed_interior_mesh(); - if (!interior.empty()) { - its_flip_triangles(interior); - m_hollowed_interior_transformed = std::make_unique(std::move(interior)); - m_hollowed_interior_transformed->transform(trafo_inv); - } + its_flip_triangles(interior); + m_hollowed_interior_transformed = std::make_unique(std::move(interior)); + m_hollowed_interior_transformed->transform(trafo_inv); } else { m_hollowed_mesh_transformed.reset(nullptr); From a7260e7257109e5e577b4c80a676212759004345 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 10 Nov 2021 13:44:47 +0100 Subject: [PATCH 02/16] Add material print speed parameter for sla printers except sl1 --- src/libslic3r/Format/SL1.cpp | 1 + src/libslic3r/Preset.cpp | 1 + src/libslic3r/PrintConfig.cpp | 17 +++++++++++++++++ src/libslic3r/PrintConfig.hpp | 3 +++ src/slic3r/GUI/Tab.cpp | 12 ++++++++++++ src/slic3r/GUI/Tab.hpp | 2 +- 6 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Format/SL1.cpp b/src/libslic3r/Format/SL1.cpp index 9e271238ee..6ed8c5ebed 100644 --- a/src/libslic3r/Format/SL1.cpp +++ b/src/libslic3r/Format/SL1.cpp @@ -382,6 +382,7 @@ void fill_iniconf(ConfMap &m, const SLAPrint &print) m["layerHeight"] = get_cfg_value(cfg, "layer_height"); m["expTime"] = get_cfg_value(cfg, "exposure_time"); m["expTimeFirst"] = get_cfg_value(cfg, "initial_exposure_time"); + m["expUserProfile"] = get_cfg_value(cfg, "material_print_speed") == "slow" ? "1" : "0"; m["materialName"] = get_cfg_value(cfg, "sla_material_settings_id"); m["printerModel"] = get_cfg_value(cfg, "printer_model"); m["printerVariant"] = get_cfg_value(cfg, "printer_variant"); diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index a19eb4d52e..7950d38de9 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -551,6 +551,7 @@ static std::vector s_Preset_sla_material_options { "material_correction_z", "material_notes", "material_vendor", + "material_print_speed", "default_sla_material_profile", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits" diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 14d48368f7..18defae2c0 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -165,6 +165,12 @@ static const t_config_enum_values s_keys_map_SLAPillarConnectionMode = { }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SLAPillarConnectionMode) +static const t_config_enum_values s_keys_map_SLAMaterialSpeed = { + {"slow", slamsSlow}, + {"fast", slamsFast} +}; +CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SLAMaterialSpeed); + static const t_config_enum_values s_keys_map_BrimType = { {"no_brim", btNoBrim}, {"outer_only", btOuterOnly}, @@ -3730,6 +3736,17 @@ void PrintConfigDef::init_sla_params() def->max = 10; def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(2.0)); + + def = this->add("material_print_speed", coEnum); + def->label = L("Print speed"); + def->tooltip = L("lorem ipsum"); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("slow"); + def->enum_values.push_back("fast"); + def->enum_labels.push_back(L("Slow")); + def->enum_labels.push_back(L("Fast")); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionEnum(slamsSlow)); } void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value) diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 8297f9d2fe..68dbd68d35 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -915,6 +915,8 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, hollowing_closing_distance)) ) +enum SLAMaterialSpeed { slamsSlow, slamsFast }; + PRINT_CONFIG_CLASS_DEFINE( SLAMaterialConfig, @@ -929,6 +931,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, material_correction_x)) ((ConfigOptionFloat, material_correction_y)) ((ConfigOptionFloat, material_correction_z)) + ((ConfigOptionEnum, material_print_speed)) ) PRINT_CONFIG_CLASS_DEFINE( diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 6c820cff3d..d96e7e3e1b 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -4239,6 +4239,11 @@ void TabSLAMaterial::build() optgroup->append_single_option_line(option); build_preset_description_line(optgroup.get()); + + page = add_options_page(L("Material printing profile"), "note.png"); + optgroup = page->new_optgroup(L("Material printing profile")); + option = optgroup->get_option("material_print_speed"); + optgroup->append_single_option_line(option); } // Reload current config (aka presets->edited_preset->config) into the UI fields. @@ -4249,6 +4254,13 @@ void TabSLAMaterial::reload_config() Tab::reload_config(); } +void TabSLAMaterial::toggle_options() +{ + const Preset ¤t_printer = wxGetApp().preset_bundle->printers.get_edited_preset(); + std::string model = current_printer.config.opt_string("printer_model"); + m_config_manipulation.toggle_field("material_print_speed", model != "SL1"); +} + void TabSLAMaterial::update() { if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptFFF) diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 6a7b56fe40..8a87c60c54 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -480,7 +480,7 @@ public: void build() override; void reload_config() override; - void toggle_options() override {}; + void toggle_options() override; void update() override; void init_options_list() override; bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptSLA; } From 6ebee079c9394110896c7f56d452b3d47a8a9928 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 19 Nov 2021 15:48:03 +0100 Subject: [PATCH 03/16] Follow-up to 9994e0bcbc14fdfbd46f57d3b04d5aad087f8c36 Start updater during start of prusaslicer. Don't search the directory if only verifying that a file exists is sufficient. --- src/slic3r/GUI/GUI_App.cpp | 73 +++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 41 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 94f0995670..80c3709cff 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -431,52 +431,46 @@ bool static check_old_linux_datadir(const wxString& app_name) { static bool run_updater_win() { // find updater exe - boost::filesystem::path path_to_binary = boost::dll::program_location(); - for (const auto& dir_entry : boost::filesystem::directory_iterator(path_to_binary.parent_path())) { - if (dir_entry.path().filename() == "prusaslicer-updater.exe") { - // run updater. Original args: /silent -restartapp prusa-slicer.exe -startappfirst + boost::filesystem::path path_updater = boost::dll::program_location().parent_path() / "prusaslicer-updater.exe"; + if (boost::filesystem::exists(path_updater)) { + // run updater. Original args: /silent -restartapp prusa-slicer.exe -startappfirst - // Using quoted string as mentioned in CreateProcessW docs. - std::wstring wcmd = L"\"" + dir_entry.path().wstring() + L"\""; - wcmd += L" /silent"; + // Using quoted string as mentioned in CreateProcessW docs, silent execution parameter. + std::wstring wcmd = L"\"" + path_updater.wstring() + L"\" /silent"; - // additional information - STARTUPINFOW si; - PROCESS_INFORMATION pi; + // additional information + STARTUPINFOW si; + PROCESS_INFORMATION pi; - // set the size of the structures - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); - ZeroMemory(&pi, sizeof(pi)); + // set the size of the structures + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); - // start the program up - if (CreateProcessW(NULL, // the path - wcmd.data(), // Command line - NULL, // Process handle not inheritable - NULL, // Thread handle not inheritable - FALSE, // Set handle inheritance to FALSE - 0, // No creation flags - NULL, // Use parent's environment block - NULL, // Use parent's starting directory - &si, // Pointer to STARTUPINFO structure - &pi // Pointer to PROCESS_INFORMATION structure (removed extra parentheses) - )) { - // Close process and thread handles. - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - return true; - } else { - BOOST_LOG_TRIVIAL(error) << "Failed to start prusaslicer-updater.exe with command " << wcmd; - } - break; + // start the program up + if (CreateProcessW(NULL, // the path + wcmd.data(), // Command line + NULL, // Process handle not inheritable + NULL, // Thread handle not inheritable + FALSE, // Set handle inheritance to FALSE + 0, // No creation flags + NULL, // Use parent's environment block + NULL, // Use parent's starting directory + &si, // Pointer to STARTUPINFO structure + &pi // Pointer to PROCESS_INFORMATION structure (removed extra parentheses) + )) { + // Close process and thread handles. + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return true; + } else { + BOOST_LOG_TRIVIAL(error) << "Failed to start prusaslicer-updater.exe with command " << wcmd; } } return false; } #endif //_WIN32 - - wxString file_wildcards(FileType file_type, const std::string &custom_extension) { static const std::string defaults[FT_SIZE] = { @@ -738,14 +732,11 @@ void GUI_App::post_init() // sees something else than "we want something" on the first start. show_send_system_info_dialog_if_needed(); } - bool updater_running = #ifdef _WIN32 // Run external updater on Windows. - run_updater_win(); - #else - false; + if (! run_updater_win()) + // "prusaslicer-updater.exe" was not started, run our own update check. #endif // _WIN32 - if (!updater_running) this->preset_updater->slic3r_update_notify(); }); } From 55a555c848b5bad5cbec22394b0dc5b10722120d Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 19 Nov 2021 16:30:37 +0100 Subject: [PATCH 04/16] Tooltip for the new SLA material parameter "material_print_speed" --- src/libslic3r/PrintConfig.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 18defae2c0..c637804495 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3739,7 +3739,9 @@ void PrintConfigDef::init_sla_params() def = this->add("material_print_speed", coEnum); def->label = L("Print speed"); - def->tooltip = L("lorem ipsum"); + def->tooltip = L( + "A slower printing profile might be necessary when using materials with higher viscosity " + "or with some hollowed parts. It slows down the tilt movement and adds a delay before exposure."); def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); def->enum_values.push_back("slow"); def->enum_values.push_back("fast"); From 244b66649c1f60ef42d973971f4d148fd2b49299 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 19 Nov 2021 16:33:15 +0100 Subject: [PATCH 05/16] Check unsaved preset changes only when presets have been changed in the project --- src/slic3r/GUI/MainFrame.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 2 ++ src/slic3r/GUI/Plater.hpp | 1 + src/slic3r/GUI/ProjectDirtyStateManager.hpp | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 6002e1d524..9c5754a42d 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -229,7 +229,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S } // check unsaved changes only if project wasn't saved else if (plater()->is_project_dirty() && saved_project == wxID_NO && event.CanVeto() && - !wxGetApp().check_and_save_current_preset_changes(_L("PrusaSlicer is closing"), _L("Closing PrusaSlicer while some presets are modified."))) { + (plater()->is_presets_dirty() && !wxGetApp().check_and_save_current_preset_changes(_L("PrusaSlicer is closing"), _L("Closing PrusaSlicer while some presets are modified.")))) { event.Veto(); return; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index bd8cc4d460..7e563eb953 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1675,6 +1675,7 @@ struct Plater::priv ~priv(); bool is_project_dirty() const { return dirty_state.is_dirty(); } + bool is_presets_dirty() const { return dirty_state.is_presets_dirty(); } void update_project_dirty_from_presets() { dirty_state.update_from_presets(); } int save_project_if_dirty(const wxString& reason) { int res = wxID_NO; @@ -5016,6 +5017,7 @@ Plater::Plater(wxWindow *parent, MainFrame *main_frame) } bool Plater::is_project_dirty() const { return p->is_project_dirty(); } +bool Plater::is_presets_dirty() const { return p->is_presets_dirty(); } void Plater::update_project_dirty_from_presets() { p->update_project_dirty_from_presets(); } int Plater::save_project_if_dirty(const wxString& reason) { return p->save_project_if_dirty(reason); } void Plater::reset_project_dirty_after_save() { p->reset_project_dirty_after_save(); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 4c9bfc763c..ef30170724 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -140,6 +140,7 @@ public: ~Plater() = default; bool is_project_dirty() const; + bool is_presets_dirty() const; void update_project_dirty_from_presets(); int save_project_if_dirty(const wxString& reason); void reset_project_dirty_after_save(); diff --git a/src/slic3r/GUI/ProjectDirtyStateManager.hpp b/src/slic3r/GUI/ProjectDirtyStateManager.hpp index 29ba4eec0a..6841c89c69 100644 --- a/src/slic3r/GUI/ProjectDirtyStateManager.hpp +++ b/src/slic3r/GUI/ProjectDirtyStateManager.hpp @@ -15,6 +15,7 @@ public: void reset_initial_presets(); bool is_dirty() const { return m_plater_dirty || m_project_config_dirty || m_presets_dirty; } + bool is_presets_dirty() const { return m_presets_dirty; } #if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW void render_debug_window() const; From 21c8f373ff3f6c4d79cb7da835e5a986500e3aba Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 19 Nov 2021 17:00:38 +0100 Subject: [PATCH 06/16] Code refactoring for 69731b1d => nsvgParseFromFileWithReplace() is extracted from nanosvg.h to BitmapCache --- src/nanosvg/nanosvg.h | 20 +-------------- src/slic3r/GUI/BitmapCache.cpp | 43 ++++++++++++++++++++++++++++++--- src/slic3r/GUI/BitmapCache.hpp | 7 ++++++ src/slic3r/GUI/ImGuiWrapper.cpp | 3 ++- 4 files changed, 50 insertions(+), 23 deletions(-) diff --git a/src/nanosvg/nanosvg.h b/src/nanosvg/nanosvg.h index 4eaff9dadc..57bcb7c2c1 100644 --- a/src/nanosvg/nanosvg.h +++ b/src/nanosvg/nanosvg.h @@ -165,11 +165,6 @@ typedef struct NSVGimage // Parses SVG file from a file, returns SVG image as paths. NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi); -// Parses SVG file from a file, returns SVG image as paths. -// And makes replases befor parsing -// replace_map containes old_value->new_value -NSVGimage* nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map& replace_map); - // Parses SVG file from a null terminated string, returns SVG image as paths. // Important note: changes the string. NSVGimage* nsvgParse(char* input, const char* units, float dpi); @@ -2908,12 +2903,6 @@ NSVGimage* nsvgParse(char* input, const char* units, float dpi) NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi) { - return nsvgParseFromFileWithReplace(filename, units, dpi, { {} }); -} - -NSVGimage* nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map& replaces) -{ - std::string str; FILE* fp = NULL; size_t size; char* data = NULL; @@ -2930,14 +2919,7 @@ NSVGimage* nsvgParseFromFileWithReplace(const char* filename, const char* units, data[size] = '\0'; // Must be null terminated. fclose(fp); - if(replaces.empty()) - image = nsvgParse(data, units, dpi); - else { - str.assign(data); - for (auto val : replaces) - boost::replace_all(str, val.first, val.second); - image = nsvgParse(str.data(), units, dpi); - } + image = nsvgParse(data, units, dpi); free(data); return image; diff --git a/src/slic3r/GUI/BitmapCache.cpp b/src/slic3r/GUI/BitmapCache.cpp index 749fc5ef28..39ba849d33 100644 --- a/src/slic3r/GUI/BitmapCache.cpp +++ b/src/slic3r/GUI/BitmapCache.cpp @@ -260,6 +260,43 @@ wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned width, return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image))); } +NSVGimage* BitmapCache::nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map& replaces) +{ + std::string str; + FILE* fp = NULL; + size_t size; + char* data = NULL; + NSVGimage* image = NULL; + + fp = boost::nowide::fopen(filename, "rb"); + if (!fp) goto error; + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, 0, SEEK_SET); + data = (char*)malloc(size + 1); + if (data == NULL) goto error; + if (fread(data, 1, size, fp) != size) goto error; + data[size] = '\0'; // Must be null terminated. + fclose(fp); + + if (replaces.empty()) + image = nsvgParse(data, units, dpi); + else { + str.assign(data); + for (auto val : replaces) + boost::replace_all(str, val.first, val.second); + image = nsvgParse(str.data(), units, dpi); + } + free(data); + return image; + +error: + if (fp) fclose(fp); + if (data) free(data); + if (image) nsvgDelete(image); + return NULL; +} + wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_width, unsigned target_height, const bool grayscale/* = false*/, const bool dark_mode/* = false*/, const std::string& new_color /*= ""*/) { @@ -278,11 +315,11 @@ wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_ // map of color replaces std::map replaces; if (dark_mode) - replaces["#808080"] = "#FFFFFF"; + replaces["\"#808080\""] = "\"#FFFFFF\""; if (!new_color.empty()) - replaces["#ED6B21"] = new_color; + replaces["\"#ED6B21\""] = "\"" + new_color + "\""; - NSVGimage *image = ::nsvgParseFromFileWithReplace(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f, replaces); + NSVGimage *image = nsvgParseFromFileWithReplace(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f, replaces); if (image == nullptr) return nullptr; diff --git a/src/slic3r/GUI/BitmapCache.hpp b/src/slic3r/GUI/BitmapCache.hpp index 5fa8643b5a..4d1d383c41 100644 --- a/src/slic3r/GUI/BitmapCache.hpp +++ b/src/slic3r/GUI/BitmapCache.hpp @@ -9,6 +9,8 @@ #include #endif +struct NSVGimage; + namespace Slic3r { namespace GUI { class BitmapCache @@ -32,6 +34,11 @@ 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); + + // Parses SVG file from a file, returns SVG image as paths. + // And makes replases befor parsing + // replace_map containes old_value->new_value + static NSVGimage* nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map& replaces); // 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, const bool grayscale = false, const bool dark_mode = false, const std::string& new_color = ""); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 894cff3a2f..cac2740b0a 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -31,6 +31,7 @@ #include "GUI.hpp" #include "I18N.hpp" #include "Search.hpp" +#include "BitmapCache.hpp" #include "../Utils/MacDarkMode.hpp" #include "nanosvg/nanosvg.h" @@ -1030,7 +1031,7 @@ std::vector ImGuiWrapper::load_svg(const std::string& bitmap_name { std::vector empty_vector; - NSVGimage* image = ::nsvgParseFromFileWithReplace(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f, { { "#808080", "#FFFFFF" } }); + NSVGimage* image = BitmapCache::nsvgParseFromFileWithReplace(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f, { { "\"#808080\"", "\"#FFFFFF\"" } }); if (image == nullptr) return empty_vector; From 1b7975639e328c7c9b67e99ef157eab3b3bbb324 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Sun, 21 Nov 2021 18:15:59 +0100 Subject: [PATCH 07/16] Follow-up https://github.com/prusa3d/PrusaSlicer/commit/172b97cc2a3ed1290ee0c02b164379d0e38bc39e - Show context menu only on right mouse click --- src/slic3r/GUI/GUI_ObjectList.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 020026e4d9..cd8e8e6a02 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -916,7 +916,7 @@ void ObjectList::list_manipulation(const wxPoint& mouse_pos, bool evt_context_me if (is_windows10() && m_objects_model->HasWarningIcon(item) && mouse_pos.x > 2 * wxGetApp().em_unit() && mouse_pos.x < 4 * wxGetApp().em_unit()) fix_through_netfabb(); - else + else if (evt_context_menu) show_context_menu(evt_context_menu); // show context menu for "Name" column too } // workaround for extruder editing under OSX From 93bd5ee08bae1e57a1bebbf9ac31324ad89fd219 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 22 Nov 2021 09:52:02 +0100 Subject: [PATCH 08/16] Fixed a typo in 02c18dbc521ef33c2e23a3996c50ee60eecf8d2a. Id of the WipeTower is equal to 1000 --- src/slic3r/GUI/Plater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7e563eb953..32aca2d635 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1214,7 +1214,7 @@ void Sidebar::show_info_sizer() ModelObjectPtrs objects = p->plater->model().objects; int obj_idx = selection.get_object_idx(); - if (m_mode < comExpert || objects.empty() || obj_idx < 0 || obj_idx > 1000 || + if (m_mode < comExpert || objects.empty() || obj_idx < 0 || obj_idx == 1000 || objects[obj_idx]->volumes.empty() || // hack to avoid crash when deleting the last object on the bed (selection.is_single_full_object() && objects[obj_idx]->instances.size()> 1) || !(selection.is_single_full_instance() || selection.is_single_volume())) { From a4baecb3401432fa62e5a98b62bda49efe2c496d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 22 Nov 2021 10:57:38 +0100 Subject: [PATCH 09/16] #7326 - Fixed crash when starting GCodeViewer --- src/slic3r/GUI/3DBed.cpp | 10 +++++++--- src/slic3r/GUI/GLCanvas3D.cpp | 2 ++ src/slic3r/GUI/MainFrame.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 20 +++++++++++--------- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 9a21079118..a8d7dbdf25 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -252,12 +252,16 @@ void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor, BoundingBoxf3 Bed3D::calc_extended_bounding_box() const { BoundingBoxf3 out { m_build_volume.bounding_volume() }; + const Vec3d size = out.size(); + // ensures that the bounding box is set as defined or the following calls to merge() will not work as intented + if (size.x() > 0.0 && size.y() > 0.0 && !out.defined) + out.defined = true; // Reset the build volume Z, we don't want to zoom to the top of the build volume if it is empty. - out.min.z() = 0; - out.max.z() = 0; + out.min.z() = 0.0; + out.max.z() = 0.0; // extend to contain axes out.merge(m_axes.get_origin() + m_axes.get_total_length() * Vec3d::Ones()); - out.merge(out.min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, out.max(2))); + out.merge(out.min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, out.max.z())); // extend to contain model, if any BoundingBoxf3 model_bb = m_model.get_bounding_box(); if (model_bb.defined) { diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 407d57030d..e14b4e6004 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1119,6 +1119,8 @@ void GLCanvas3D::reset_volumes() ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state() const { + assert(m_initialized); + ModelInstanceEPrintVolumeState state; m_volumes.check_outside_state(m_bed.build_volume(), &state); return state; diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 9c5754a42d..bfb2537813 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -486,7 +486,7 @@ void MainFrame::update_layout() case ESettingsLayout::GCodeViewer: { m_main_sizer->Add(m_plater, 1, wxEXPAND); - m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, 0, {}, {}, true); + m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, 0.0, {}, {}, true); m_plater->get_collapse_toolbar().set_enabled(false); m_plater->collapse_sidebar(true); m_plater->Show(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7e563eb953..b1d6076b72 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3867,18 +3867,20 @@ void Plater::priv::set_current_panel(wxPanel* panel) preview->get_canvas3d()->bind_event_handlers(); - // see: Plater::priv::object_list_changed() - // FIXME: it may be better to have a single function making this check and let it be called wherever needed - bool export_in_progress = this->background_process.is_export_scheduled(); - bool model_fits = view3D->get_canvas3d()->check_volumes_outside_state() != ModelInstancePVS_Partly_Outside; - if (!model.objects.empty() && !export_in_progress && model_fits) { + if (wxGetApp().is_editor()) { + // see: Plater::priv::object_list_changed() + // FIXME: it may be better to have a single function making this check and let it be called wherever needed + bool export_in_progress = this->background_process.is_export_scheduled(); + bool model_fits = view3D->get_canvas3d()->check_volumes_outside_state() != ModelInstancePVS_Partly_Outside; + if (!model.objects.empty() && !export_in_progress && model_fits) { #if ENABLE_SEAMS_USING_MODELS - preview->get_canvas3d()->init_gcode_viewer(); + preview->get_canvas3d()->init_gcode_viewer(); #endif // ENABLE_SEAMS_USING_MODELS - q->reslice(); + q->reslice(); + } + // keeps current gcode preview, if any + preview->reload_print(true); } - // keeps current gcode preview, if any - preview->reload_print(true); preview->set_as_dirty(); // reset cached size to force a resize on next call to render() to keep imgui in synch with canvas size From aae8c7a844de7656b6d8f7a2feff0fb759288554 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 16 Nov 2021 11:49:41 +0100 Subject: [PATCH 10/16] Fixed the empty layer warning (again), it did not work after top/bottom support contact z was separated --- src/libslic3r/GCode.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 751e1e5ac8..f9f116f8e2 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -475,6 +475,7 @@ std::vector GCode::collect_layers_to_print(const PrintObjec std::vector layers_to_print; layers_to_print.reserve(object.layers().size() + object.support_layers().size()); + /* // Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um. // This is the same logic as in support generator. //FIXME should we use the printing extruders instead? @@ -488,7 +489,7 @@ std::vector GCode::collect_layers_to_print(const PrintObjec for (auto lh : object.print()->config().min_layer_height.values) support_layer_height_min = std::min(support_layer_height_min, std::max(0.01, lh)); gap_over_supports += support_layer_height_min; - } + }*/ std::vector> warning_ranges; @@ -528,22 +529,23 @@ std::vector GCode::collect_layers_to_print(const PrintObjec if ((layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions()) // Allow empty support layers, as the support generator may produce no extrusions for non-empty support regions. || (layer_to_print.support_layer /* && layer_to_print.support_layer->has_extrusions() */)) { - double support_contact_z = (last_extrusion_layer && last_extrusion_layer->support_layer) - ? gap_over_supports - : 0.; + double top_cd = object.config().support_material_contact_distance; + double bottom_cd = object.config().support_material_bottom_contact_distance == 0. ? top_cd : object.config().support_material_bottom_contact_distance; + + double extra_gap = (layer_to_print.support_layer ? bottom_cd : top_cd); + double maximal_print_z = (last_extrusion_layer ? last_extrusion_layer->print_z() : 0.) + layer_to_print.layer()->height - + support_contact_z; + + std::max(0., extra_gap); // Negative support_contact_z is not taken into account, it can result in false positives in cases // where previous layer has object extrusions too (https://github.com/prusa3d/PrusaSlicer/issues/2752) if (has_extrusions && layer_to_print.print_z() > maximal_print_z + 2. * EPSILON) warning_ranges.emplace_back(std::make_pair((last_extrusion_layer ? last_extrusion_layer->print_z() : 0.), layers_to_print.back().print_z())); - - // Remember last layer with extrusions. - if (has_extrusions) - last_extrusion_layer = &layers_to_print.back(); } + // Remember last layer with extrusions. + if (has_extrusions) + last_extrusion_layer = &layers_to_print.back(); } if (! warning_ranges.empty()) { From 6180e3a89ef271eda5bea69723719497a74e2167 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 18 Nov 2021 13:16:34 +0100 Subject: [PATCH 11/16] Do not open simplify dialog on top of the main toolbar --- src/slic3r/GUI/GLCanvas3D.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 4591da29f0..92819136a7 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -812,6 +812,7 @@ public: void schedule_extra_frame(int miliseconds); + float get_main_toolbar_height() { return m_main_toolbar.get_height(); } int get_main_toolbar_item_id(const std::string& name) const { return m_main_toolbar.get_item_id(name); } void force_main_toolbar_left_action(int item_id) { m_main_toolbar.force_left_action(item_id, *this); } void force_main_toolbar_right_action(int item_id) { m_main_toolbar.force_right_action(item_id, *this); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 90b601a9dd..133803441f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -200,7 +200,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi pos.x -= m_gui_cfg->window_offset_x; pos.y -= m_gui_cfg->window_offset_y; // minimal top left value - ImVec2 tl(m_gui_cfg->window_padding, m_gui_cfg->window_padding); + ImVec2 tl(m_gui_cfg->window_padding, m_gui_cfg->window_padding + m_parent.get_main_toolbar_height()); if (pos.x < tl.x) pos.x = tl.x; if (pos.y < tl.y) pos.y = tl.y; // maximal bottom right value From a22bc7f7c8c2823a8c3bf2284bbf284376314da3 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 18 Nov 2021 13:47:00 +0100 Subject: [PATCH 12/16] Show an error dialog when opening simplification on incompatible selection --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 133803441f..2b38b104f7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -3,6 +3,7 @@ #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/GUI_ObjectManipulation.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" +#include "slic3r/GUI/MsgDialog.hpp" #include "slic3r/GUI/NotificationManager.hpp" #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/format.hpp" @@ -150,6 +151,12 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi if (act_volume == nullptr) { stop_worker_thread_request(); close(); + if (! m_parent.get_selection().is_single_volume()) { + MessageDialog msg((wxWindow*)wxGetApp().mainframe, + _L("Simplification is currently only allowed when a single part is selected"), + _L("Error")); + msg.ShowModal(); + } return; } From 83fb066c645ce25db1720314fd82ed0e62dfa57f Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 7 Dec 2020 18:19:53 +0100 Subject: [PATCH 13/16] Check for required CGAL version fixes #4912 --- src/libslic3r/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index e1a39b8e81..f0283f1cc6 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -288,7 +288,7 @@ set(CGAL_DO_NOT_WARN_ABOUT_CMAKE_BUILD_TYPE ON CACHE BOOL "" FORCE) cmake_policy(PUSH) cmake_policy(SET CMP0011 NEW) -find_package(CGAL REQUIRED) +find_package(CGAL 4.13.2 REQUIRED) cmake_policy(POP) add_library(libslic3r_cgal STATIC MeshBoolean.cpp MeshBoolean.hpp TryCatchSignal.hpp From 4c89a9ed43253277798c69419888646e8cadf5a7 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 22 Nov 2021 14:41:23 +0100 Subject: [PATCH 14/16] Increased 3D connexion translation speed maximum to 30 (#3385) --- src/slic3r/GUI/Mouse3DController.cpp | 4 ++-- src/slic3r/GUI/Mouse3DController.hpp | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index b89bb454b4..ff2fa13978 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -375,7 +375,7 @@ void Mouse3DController::load_config(const AppConfig &appconfig) appconfig.get_mouse_device_swap_yz(device_name, swap_yz); // clamp to valid values Params params; - params.translation.scale = Params::DefaultTranslationScale * std::clamp(translation_speed, 0.1, 10.0); + params.translation.scale = Params::DefaultTranslationScale * std::clamp(translation_speed, Params::MinTranslationScale, Params::MaxTranslationScale); params.translation.deadzone = std::clamp(translation_deadzone, 0.0, Params::MaxTranslationDeadzone); params.rotation.scale = Params::DefaultRotationScale * std::clamp(rotation_speed, 0.1f, 10.0f); params.rotation.deadzone = std::clamp(rotation_deadzone, 0.0f, Params::MaxRotationDeadzone); @@ -469,7 +469,7 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const imgui.text_colored(color, _L("Speed:")); float translation_scale = (float)params_copy.translation.scale / Params::DefaultTranslationScale; - if (imgui.slider_float(_L("Translation") + "##1", &translation_scale, 0.1f, 10.0f, "%.1f")) { + if (imgui.slider_float(_L("Translation") + "##1", &translation_scale, Params::MinTranslationScale, Params::MaxTranslationScale, "%.1f")) { params_copy.translation.scale = Params::DefaultTranslationScale * (double)translation_scale; params_changed = true; } diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 12e9b7dc9e..2b2d3aff9c 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -33,6 +33,8 @@ class Mouse3DController // to copy the parameters. struct Params { + static constexpr double MinTranslationScale = 0.1; + static constexpr double MaxTranslationScale = 30.; static constexpr double DefaultTranslationScale = 2.5; static constexpr double MaxTranslationDeadzone = 0.2; static constexpr double DefaultTranslationDeadzone = 0.0; From f30a018c5aecbded0adfb609c5bf180473149387 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 22 Nov 2021 15:22:23 +0100 Subject: [PATCH 15/16] Clamp radius in variable layer height dialog, negative values led to a crash --- src/slic3r/GUI/GLCanvas3D.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e14b4e6004..1c7fc4a61e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -271,8 +271,10 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const ImGui::SetCursorPosX(widget_align); ImGui::PushItemWidth(imgui.get_style_scaling() * 120.0f); int radius = (int)m_smooth_params.radius; - if (ImGui::SliderInt("##1", &radius, 1, 10)) + if (ImGui::SliderInt("##1", &radius, 1, 10)) { + radius = std::clamp(radius, 1, 10); m_smooth_params.radius = (unsigned int)radius; + } ImGui::SetCursorPosX(text_align); ImGui::AlignTextToFramePadding(); From a996f33579280e6047e77d81ee84c2616a2796d5 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 22 Nov 2021 15:33:05 +0100 Subject: [PATCH 16/16] Follow-up to a0ee41770d390efb7c298c397172e1198c85f08d see pull request #7323 for discussion what went wrong. The original issue Fix of Print::apply() creating hierarchy of regions for complex scenarios: Unnecessary regions were created for a modifier over a volume or a modifier, where the modifier did not modify any of its parent's properties. This lead to an explosion of regions for this particular 3MF. Fixes Non Responsive & Memory Leak when opening or changing this 3MF project #7220 After a0ee41770d390efb7c298c397172e1198c85f08d quite often regions were not re-regenerated. This shoul now be fixed together with the original slowness & memory leak issue. --- src/libslic3r/PrintApply.cpp | 79 +++++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index c10e96edea..84ff555988 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -604,12 +604,24 @@ void print_objects_regions_invalidate_keep_some_volumes(PrintObjectRegions &prin print_object_regions.cached_volume_ids.erase(print_object_regions.cached_volume_ids.begin() + last_cached_volume, print_object_regions.cached_volume_ids.end()); } +// Find a bounding box of a volume's part intersecting layer_range. Such a bounding box will likely be smaller in XY than the full bounding box, +// thus it will intersect with lower number of other volumes. const PrintObjectRegions::BoundingBox* find_volume_extents(const PrintObjectRegions::LayerRangeRegions &layer_range, const ModelVolume &volume) { auto it = lower_bound_by_predicate(layer_range.volumes.begin(), layer_range.volumes.end(), [&volume](const PrintObjectRegions::VolumeExtents &l){ return l.volume_id < volume.id(); }); return it != layer_range.volumes.end() && it->volume_id == volume.id() ? &it->bbox : nullptr; } +// Find a bounding box of a topmost printable volume referenced by this modifier given this_region_id. +const PrintObjectRegions::BoundingBox* find_modifier_volume_extents(const PrintObjectRegions::LayerRangeRegions &layer_range, const int this_region_id) +{ + // Find the top-most printable volume of this modifier, or the printable volume itself. + int parent_region_id = this_region_id; + for (; ! layer_range.volume_regions[parent_region_id].model_volume->is_model_part(); parent_region_id = layer_range.volume_regions[parent_region_id].parent) + assert(parent_region_id >= 0); + return find_volume_extents(layer_range, *layer_range.volume_regions[parent_region_id].model_volume); +} + PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_or_parent_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders); void print_region_ref_inc(PrintRegion &r) { ++ r.m_ref_cnt; } @@ -634,11 +646,54 @@ bool verify_update_print_object_regions( print_region_ref_reset(*region); // Verify and / or update PrintRegions produced by ModelVolumes, layer range modifiers, modifier volumes. - for (PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges) + for (PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges) { + // Each modifier ModelVolume intersecting this layer_range shall be referenced here at least once if it intersects some + // printable ModelVolume at this layer_range even if it does not modify its overlapping printable ModelVolume configuration yet. + // VolumeRegions reference ModelVolumes in layer_range.volume_regions the order they are stored in ModelObject volumes. + // Remember whether a given modifier ModelVolume was visited already. + auto it_model_volume_modifier_last = model_volumes.end(); for (PrintObjectRegions::VolumeRegion ®ion : layer_range.volume_regions) if (region.model_volume->is_model_part() || region.model_volume->is_modifier()) { auto it_model_volume = lower_bound_by_predicate(model_volumes.begin(), model_volumes.end(), [®ion](const ModelVolume *l){ return l->id() < region.model_volume->id(); }); assert(it_model_volume != model_volumes.end() && (*it_model_volume)->id() == region.model_volume->id()); + if (region.model_volume->is_modifier() && it_model_volume != it_model_volume_modifier_last) { + // A modifier ModelVolume is visited for the first time. + // A visited modifier may not have had parent volume_regions created overlapping with some model parts or modifiers, + // if the visited modifier did not modify their properties. Now the visited modifier's configuration may have changed, + // which may require new regions to be created. + it_model_volume_modifier_last = it_model_volume; + int this_region_id = int(®ion - layer_range.volume_regions.data()); + int next_region_id = this_region_id + 1; + const PrintObjectRegions::BoundingBox *bbox = find_volume_extents(layer_range, *region.model_volume); + assert(bbox); + for (int parent_region_id = this_region_id - 1; parent_region_id >= 0; -- parent_region_id) { + const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[parent_region_id]; + assert(parent_region.model_volume != region.model_volume); + if (parent_region.model_volume->is_model_part() || parent_region.model_volume->is_modifier()) { + // volume_regions are produced in decreasing order of parent volume_regions ids. + // Some regions may not have been generated the last time by generate_print_object_regions(). + assert(next_region_id == int(layer_range.volume_regions.size()) || + layer_range.volume_regions[next_region_id].model_volume != region.model_volume || + layer_range.volume_regions[next_region_id].parent <= parent_region_id); + if (next_region_id < int(layer_range.volume_regions.size()) && + layer_range.volume_regions[next_region_id].model_volume == region.model_volume && + layer_range.volume_regions[next_region_id].parent == parent_region_id) { + // A parent region is already overridden. + ++ next_region_id; + } else { + // Such parent region does not exist. If it is needed, then we need to reslice. + const PrintObjectRegions::BoundingBox *parent_bbox = find_modifier_volume_extents(layer_range, parent_region_id); + assert(parent_bbox != nullptr); + if (parent_bbox->intersects(*bbox)) + // Only create new region for a modifier, which actually modifies config of it's parent. + if (PrintRegionConfig config = region_config_from_model_volume(parent_region.region->config(), nullptr, **it_model_volume, num_extruders); + config != parent_region.region->config()) + // This modifier newly overrides a region, which it did not before. We need to reslice. + return false; + } + } + } + } PrintRegionConfig cfg = region.parent == -1 ? region_config_from_model_volume(default_region_config, layer_range.config, **it_model_volume, num_extruders) : region_config_from_model_volume(layer_range.volume_regions[region.parent].region->config(), nullptr, **it_model_volume, num_extruders); @@ -657,6 +712,7 @@ bool verify_update_print_object_regions( } print_region_ref_inc(*region.region); } + } // Verify and / or update PrintRegions produced by color painting. for (const PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges) @@ -851,17 +907,28 @@ static PrintObjectRegions* generate_print_object_regions( } else { assert(volume.is_modifier()); // Modifiers may be chained one over the other. Check for overlap, merge DynamicPrintConfigs. - for (int parent_region_id = int(layer_range.volume_regions.size()) - 1; parent_region_id >= 0; -- parent_region_id) - if (const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[parent_region_id]; - parent_region.model_volume->is_model_part() || parent_region.model_volume->is_modifier()) { - const PrintObjectRegions::BoundingBox *parent_bbox = find_volume_extents(layer_range, *parent_region.model_volume); + bool added = false; + int parent_model_part_id = -1; + for (int parent_region_id = int(layer_range.volume_regions.size()) - 1; parent_region_id >= 0; -- parent_region_id) { + const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[parent_region_id]; + const ModelVolume &parent_volume = *parent_region.model_volume; + if (parent_volume.is_model_part() || parent_volume.is_modifier()) { + const PrintObjectRegions::BoundingBox *parent_bbox = find_modifier_volume_extents(layer_range, parent_region_id); assert(parent_bbox != nullptr); if (parent_bbox->intersects(*bbox)) // Only create new region for a modifier, which actually modifies config of it's parent. if (PrintRegionConfig config = region_config_from_model_volume(parent_region.region->config(), nullptr, volume, num_extruders); - config != parent_region.region->config()) + config != parent_region.region->config()) { + added = true; layer_range.volume_regions.push_back({ &volume, parent_region_id, get_create_region(std::move(config)), bbox }); + } else if (parent_model_part_id == -1 && parent_volume.is_model_part()) + parent_model_part_id = parent_region_id; + } } + if (! added && parent_model_part_id >= 0) + // This modifier does not override any printable volume's configuration, however it may in the future. + // Store it so that verify_update_print_object_regions() will handle this modifier correctly if its configuration changes. + layer_range.volume_regions.push_back({ &volume, parent_model_part_id, layer_range.volume_regions[parent_model_part_id].region, bbox }); } } }