diff --git a/src/libslic3r/MixedFilament.cpp b/src/libslic3r/MixedFilament.cpp index 4931199ab4..d53c720124 100644 --- a/src/libslic3r/MixedFilament.cpp +++ b/src/libslic3r/MixedFilament.cpp @@ -10,10 +10,18 @@ #include #include #include -#include +#include +#include namespace Slic3r { +static uint64_t canonical_pair_key(unsigned int a, unsigned int b) +{ + const unsigned int lo = std::min(a, b); + const unsigned int hi = std::max(a, b); + return (uint64_t(lo) << 32) | uint64_t(hi); +} + // --------------------------------------------------------------------------- // Colour helpers (internal) // --------------------------------------------------------------------------- @@ -738,9 +746,13 @@ void MixedFilamentManager::auto_generate(const std::vector &filamen std::vector custom_rows; custom_rows.reserve(old.size()); + std::unordered_map old_auto_rows; + old_auto_rows.reserve(old.size()); for (const MixedFilament &prev : old) { - if (!prev.custom) + if (!prev.custom) { + old_auto_rows.emplace(canonical_pair_key(prev.component_a, prev.component_b), &prev); continue; + } if (prev.component_a == 0 || prev.component_b == 0 || prev.component_a > n || prev.component_b > n || prev.component_a == prev.component_b) continue; MixedFilament custom = prev; @@ -762,18 +774,14 @@ void MixedFilamentManager::auto_generate(const std::vector &filamen mf.custom = false; mf.origin_auto = true; - // Try to preserve previous settings. - for (const auto &prev : old) { - if (!prev.custom && - prev.component_a == mf.component_a && - prev.component_b == mf.component_b) { - mf.enabled = prev.enabled; - mf.deleted = prev.deleted; - mf.stable_id = prev.stable_id; - if (mf.deleted) - mf.enabled = false; - break; - } + const auto it_prev = old_auto_rows.find(canonical_pair_key(mf.component_a, mf.component_b)); + if (it_prev != old_auto_rows.end()) { + const MixedFilament &prev = *it_prev->second; + mf.enabled = prev.enabled; + mf.deleted = prev.deleted; + mf.stable_id = prev.stable_id; + if (mf.deleted) + mf.enabled = false; } mf.stable_id = normalize_stable_id(mf.stable_id); m_mixed.push_back(mf); @@ -931,21 +939,23 @@ void MixedFilamentManager::load_custom_entries(const std::string &serialized, co size_t appended_auto = 0; size_t skipped_rows = 0; - auto canonical_pair = [](unsigned int a, unsigned int b) { - return std::make_pair(std::min(a, b), std::max(a, b)); - }; - - std::vector auto_rows; - auto_rows.reserve(m_mixed.size()); + std::vector auto_rows_in_order; + auto_rows_in_order.reserve(m_mixed.size()); + std::unordered_map auto_rows_by_pair; + auto_rows_by_pair.reserve(m_mixed.size()); for (const MixedFilament &mf : m_mixed) { - if (!mf.custom) - auto_rows.push_back(mf); + if (!mf.custom) { + auto_rows_in_order.push_back(&mf); + auto_rows_by_pair.emplace(canonical_pair_key(mf.component_a, mf.component_b), &mf); + } } std::vector rebuilt; rebuilt.reserve(m_mixed.size() + 8); - std::set> consumed_auto_pairs; - std::set used_stable_ids; + std::unordered_set consumed_auto_pairs; + consumed_auto_pairs.reserve(auto_rows_by_pair.size()); + std::unordered_set used_stable_ids; + used_stable_ids.reserve(m_mixed.size() + 8); auto dedupe_stable_id = [this, &used_stable_ids](uint64_t stable_id) { stable_id = normalize_stable_id(stable_id); if (used_stable_ids.insert(stable_id).second) @@ -991,31 +1001,29 @@ void MixedFilamentManager::load_custom_entries(const std::string &serialized, co } if (!custom) { - const auto key = canonical_pair(a, b); + const uint64_t key = canonical_pair_key(a, b); if (consumed_auto_pairs.count(key) != 0) { ++skipped_rows; BOOST_LOG_TRIVIAL(warning) << "MixedFilamentManager::load_custom_entries duplicate auto row" << ", row=" << row - << ", a=" << key.first - << ", b=" << key.second; + << ", a=" << std::min(a, b) + << ", b=" << std::max(a, b); continue; } - auto it_auto = std::find_if(auto_rows.begin(), auto_rows.end(), [key, canonical_pair](const MixedFilament &mf) { - return canonical_pair(mf.component_a, mf.component_b) == key; - }); - if (it_auto == auto_rows.end()) { + auto it_auto = auto_rows_by_pair.find(key); + if (it_auto == auto_rows_by_pair.end()) { ++skipped_rows; BOOST_LOG_TRIVIAL(warning) << "MixedFilamentManager::load_custom_entries auto row missing after regenerate" << ", row=" << row - << ", a=" << key.first - << ", b=" << key.second; + << ", a=" << std::min(a, b) + << ", b=" << std::max(a, b); continue; } - MixedFilament mf = *it_auto; - mf.component_a = key.first; - mf.component_b = key.second; + MixedFilament mf = *it_auto->second; + mf.component_a = std::min(a, b); + mf.component_b = std::max(a, b); mf.stable_id = dedupe_stable_id(stable_id != 0 ? stable_id : mf.stable_id); mf.enabled = enabled; mf.pointillism_all_filaments = pointillism_all_filaments; @@ -1064,13 +1072,17 @@ void MixedFilamentManager::load_custom_entries(const std::string &serialized, co // Keep any newly generated auto rows that were not present in serialized // definitions and append them at the end to preserve existing virtual IDs. - for (const MixedFilament &auto_mf : auto_rows) { - const auto key = canonical_pair(auto_mf.component_a, auto_mf.component_b); + for (const MixedFilament *auto_mf_ptr : auto_rows_in_order) { + if (auto_mf_ptr == nullptr) + continue; + const uint64_t key = canonical_pair_key(auto_mf_ptr->component_a, auto_mf_ptr->component_b); if (consumed_auto_pairs.count(key) != 0) continue; - MixedFilament mf = auto_mf; - mf.component_a = key.first; - mf.component_b = key.second; + MixedFilament mf = *auto_mf_ptr; + const unsigned int lo = std::min(mf.component_a, mf.component_b); + const unsigned int hi = std::max(mf.component_a, mf.component_b); + mf.component_a = lo; + mf.component_b = hi; mf.stable_id = dedupe_stable_id(mf.stable_id); mf.custom = false; mf.origin_auto = true; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 32b4c07aff..a1aa1776e9 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -658,6 +658,7 @@ struct Sidebar::priv Button* m_btn_add_pattern = nullptr; // Add pattern button Button* m_btn_toggle_mixed_filaments = nullptr; // Collapse/expand toggle button bool m_mixed_filaments_collapsed = false; // Collapse state + bool m_skip_mixed_filament_sync_once = false; // Local edits already mutated manager in place. std::unordered_set m_expanded_mixed_filament_rows; // Expanded row editors wxStaticLine* m_staticline2; wxPanel* m_panel_project_title; @@ -1471,7 +1472,7 @@ Sidebar::Sidebar(Plater *parent) // Persist the custom entries so they survive the clear/load cycle in update_mixed_filament_panel if (ConfigOptionString *opt = wxGetApp().preset_bundle->project_config.option("mixed_filament_definitions")) opt->value = mgr.serialize_custom_entries(); - update_mixed_filament_panel(); + update_mixed_filament_panel(false); m_scrolled_sizer->Layout(); } }); @@ -1497,7 +1498,7 @@ Sidebar::Sidebar(Plater *parent) // Persist the custom entries so they survive the clear/load cycle in update_mixed_filament_panel if (ConfigOptionString *opt = wxGetApp().preset_bundle->project_config.option("mixed_filament_definitions")) opt->value = mgr.serialize_custom_entries(); - update_mixed_filament_panel(); + update_mixed_filament_panel(false); m_scrolled_sizer->Layout(); } }); @@ -2266,12 +2267,16 @@ void Sidebar::on_filaments_change(size_t num_filaments) if (num_filaments == choices.size()) { // Project load may keep the same physical filament count while mixed // definitions changed. Refresh mixed panel even without count changes. + const bool sync_manager = !p->m_skip_mixed_filament_sync_once; + p->m_skip_mixed_filament_sync_once = false; update_ui_from_settings(); update_dynamic_filament_list(); - update_mixed_filament_panel(); + update_mixed_filament_panel(sync_manager); return; } + p->m_skip_mixed_filament_sync_once = false; + if (choices.size() == 1 || num_filaments == 1) choices[0]->GetDropDown().Invalidate(); @@ -3867,12 +3872,15 @@ void MixedFilamentConfigPanel::update_preview() } // namespace -void Sidebar::update_mixed_filament_panel() +void Sidebar::update_mixed_filament_panel(bool sync_manager) { // Check for new collapsible structure if (!p->m_panel_mixed_filaments_title || !p->m_panel_mixed_filaments_content) return; + wxWindowUpdateLocker noUpdates_sidebar(this); + wxWindowUpdateLocker noUpdates_mixed_panel(p->m_panel_mixed_filaments_content); + auto refresh_model_canvas_colors = []() { Plater *plater = wxGetApp().plater(); if (plater == nullptr) @@ -4292,10 +4300,12 @@ void Sidebar::update_mixed_filament_panel() const std::string mixed_definitions = get_mixed_string("mixed_filament_definitions"); auto &mixed_mgr = preset_bundle->mixed_filaments; - mixed_mgr.auto_generate(physical_colors); - mixed_mgr.clear_custom_entries(); - mixed_mgr.load_custom_entries(mixed_definitions, physical_colors); - mixed_mgr.apply_gradient_settings(gradient_mode, lower_bound, upper_bound, advanced_dithering); + if (sync_manager) { + mixed_mgr.auto_generate(physical_colors); + mixed_mgr.clear_custom_entries(); + mixed_mgr.load_custom_entries(mixed_definitions, physical_colors); + mixed_mgr.apply_gradient_settings(gradient_mode, lower_bound, upper_bound, advanced_dithering); + } // During project load, sidebar may refresh before physical filament combos // finish syncing. Avoid overwriting persisted mixed definitions while the @@ -4477,8 +4487,10 @@ void Sidebar::update_mixed_filament_panel() mgr.apply_gradient_settings(mode, lo, hi, advanced); update_dynamic_filament_list(); - if (rebuild_virtual_id_remap && wxGetApp().plater()) + if (rebuild_virtual_id_remap && wxGetApp().plater()) { + p->m_skip_mixed_filament_sync_once = true; wxGetApp().plater()->on_filaments_change(num_physical); + } }; size_t visible_mixed_idx = 0; @@ -4603,8 +4615,10 @@ void Sidebar::update_mixed_filament_panel() notify_mixed_change(); if (wxGetApp().plater()) wxGetApp().plater()->update_project_dirty_from_presets(); - if (wxGetApp().plater()) + if (wxGetApp().plater()) { + p->m_skip_mixed_filament_sync_once = true; wxGetApp().plater()->on_filaments_change(num_physical); + } } } }); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 25f188c99a..2db6c573e5 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -156,7 +156,7 @@ public: void edit_filament(); void on_filaments_delete(size_t filament_id); - void update_mixed_filament_panel(); + void update_mixed_filament_panel(bool sync_manager = true); // BBS void on_bed_type_change(BedType bed_type); void load_ams_list(std::string const & device, MachineObject* obj); @@ -862,4 +862,4 @@ std::vector get_min_flush_volumes(const DynamicPrintConfig& full_config); } // namespace GUI } // namespace Slic3r -#endif \ No newline at end of file +#endif