From 8bff9aaf32c00e01a2830987a9d6cf6488d76a77 Mon Sep 17 00:00:00 2001 From: Kris Austin Date: Fri, 7 Aug 2026 11:31:04 -0500 Subject: [PATCH] fix(GUI): honor "Ignore" when layer height exceeds the configured maximum (#14369) * fix(GUI): honor "Ignore" when layer height exceeds the configured maximum Entering a layer height above the printer's max_layer_height on the Print Settings tab fired two guards in sequence. Tab::on_value_change prompts "...Adjust to the set range automatically?" with an Adjust/Ignore choice, and ConfigManipulation::update_print_fff_config then showed an OK-only "Too large layer height. Reset to X" dialog whose result was never checked, so it always reset the value. The second guard overrode the user's "Ignore", resetting the layer height regardless. Changes: - Extract the shared Adjust/Ignore dialog into ConfigManipulation::layer_height_out_of_range_dialog, reused by the tab (Tab::on_value_change) and the per-object/part settings panels. The dialog now names the value it will clamp to and reads correctly for too-low as well as too-high. - The tab/plate path is already covered by Tab::on_value_change, so the duplicate reset is dropped from update_print_fff_config. - The per-object/part panels have no on_value_change hook, so add ConfigManipulation::check_object_layer_height and call it from the object settings update paths, gated on the edited option (changed_opt_key == "layer_height"). It prompts once per layer-height edit and does not re-prompt when unrelated object settings change after the user chose "Ignore". Fixes #14214 * refactor(GUI): unify the layer-height range check across tab and object panels Copilot review of #14369 noted that the per-object check only guarded the max at extruder 0 and skipped the too-low case, diverging from the tab. Move the whole range check into ConfigManipulation::check_layer_height, used by both Tab::on_value_change and the per-object/part panels. It takes the widest [min, max] window across the printer's extruders, offers Adjust/Ignore in both directions, and resets a near-zero value. The tab's inline block collapses to one call, dropping the duplicated limit logic. * fix(GUI): only enforce layer-height limits that are actually set max_layer_height defaults to 0 (unset), so the unconditional range check offered to clamp any layer height to 0 on presets that don't define it. Guard each branch (near-zero, too-high, too-low) so an unset limit disables that direction; the slice-time nozzle-diameter check still applies. Also run check_layer_height before update_print_fff_config in the object panels so a near-zero per-object value prompts the same way the tab does, with update_print_fff_config's fallback still covering the no-minimum case. --- src/slic3r/GUI/ConfigManipulation.cpp | 69 +++++++++++++++++----- src/slic3r/GUI/ConfigManipulation.hpp | 3 + src/slic3r/GUI/GUI_ObjectSettings.cpp | 8 ++- src/slic3r/GUI/GUI_ObjectSettings.hpp | 2 +- src/slic3r/GUI/GUI_ObjectTableSettings.cpp | 7 ++- src/slic3r/GUI/GUI_ObjectTableSettings.hpp | 2 +- src/slic3r/GUI/Tab.cpp | 38 +----------- 7 files changed, 72 insertions(+), 57 deletions(-) diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index e46faa803a..3885a391b8 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -12,6 +12,7 @@ #include "libslic3r/GCode/AdaptivePAProcessor.hpp" #include "Plater.hpp" +#include #include #include @@ -250,6 +251,59 @@ void ConfigManipulation::check_chamber_minimal_temperature(DynamicPrintConfig* c } } +void ConfigManipulation::layer_height_limits(double& min_layer_height, double& max_layer_height) const +{ + const DynamicPrintConfig& printer_config = GUI::wxGetApp().preset_bundle->printers.get_edited_preset().config; + const std::vector& min_limits = printer_config.option("min_layer_height")->values; + const std::vector& max_limits = printer_config.option("max_layer_height")->values; + min_layer_height = *std::min_element(min_limits.begin(), min_limits.end()); + max_layer_height = *std::max_element(max_limits.begin(), max_limits.end()); +} + +bool ConfigManipulation::check_layer_height(DynamicPrintConfig* config) +{ + double min_layer_height = 0., max_layer_height = 0.; + layer_height_limits(min_layer_height, max_layer_height); + const double layer_height = config->opt_float("layer_height"); + + if (min_layer_height > EPSILON && layer_height < EPSILON) { + const wxString msg_text = wxString::Format(_L("Layer height is too small. It will be set to the minimum (%g mm)."), min_layer_height); + MessageDialog dialog(wxGetApp().plater(), msg_text, "", wxICON_WARNING | wxOK); + dialog.SetButtonLabel(wxID_OK, _L("OK")); + is_msg_dlg_already_exist = true; + dialog.ShowModal(); + is_msg_dlg_already_exist = false; + DynamicPrintConfig new_conf = *config; + new_conf.set_key_value("layer_height", new ConfigOptionFloat(min_layer_height)); + apply(config, &new_conf); + return true; + } + if (max_layer_height > EPSILON && layer_height > max_layer_height + EPSILON) + return layer_height_out_of_range_dialog(config, max_layer_height); + if (min_layer_height > EPSILON && layer_height < min_layer_height - EPSILON) + return layer_height_out_of_range_dialog(config, min_layer_height); + return false; +} + +bool ConfigManipulation::layer_height_out_of_range_dialog(DynamicPrintConfig* config, double clamp_to) +{ + wxString msg_text = _(L("Layer height is outside the limits set in Printer Settings -> Extruder -> Layer height limits, " + "this may cause printing quality issues.")); + msg_text += "\n\n" + wxString::Format(_L("Adjust it to the limit (%g mm) automatically?"), clamp_to); + MessageDialog dialog(wxGetApp().plater(), msg_text, "", wxICON_WARNING | wxYES | wxNO); + dialog.SetButtonLabel(wxID_YES, _L("Adjust")); + dialog.SetButtonLabel(wxID_NO, _L("Ignore")); + is_msg_dlg_already_exist = true; + const bool adjust = dialog.ShowModal() == wxID_YES; + if (adjust) { + DynamicPrintConfig new_conf = *config; + new_conf.set_key_value("layer_height", new ConfigOptionFloat(clamp_to)); + apply(config, &new_conf); + } + is_msg_dlg_already_exist = false; + return adjust; +} + void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, const bool is_global_config, const bool is_plate_config) { // #ys_FIXME_to_delete @@ -264,7 +318,6 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con // layer_height shouldn't be equal to zero auto layer_height = config->opt_float("layer_height"); - auto gpreset = GUI::wxGetApp().preset_bundle->printers.get_edited_preset(); if (layer_height < EPSILON) { const wxString msg_text = _(L("Layer height too small\nIt has been reset to 0.2")); @@ -277,20 +330,6 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con is_msg_dlg_already_exist = false; } - //BBS: limite the max layer_herght - auto max_lh = gpreset.config.opt_float("max_layer_height",0); - if (max_lh > 0.2 && layer_height > max_lh+ EPSILON) - { - const wxString msg_text = wxString::Format(L"Too large layer height.\nReset to %0.3f.", max_lh); - MessageDialog dialog(nullptr, msg_text, "", wxICON_WARNING | wxOK); - DynamicPrintConfig new_conf = *config; - is_msg_dlg_already_exist = true; - dialog.ShowModal(); - new_conf.set_key_value("layer_height", new ConfigOptionFloat(max_lh)); - apply(config, &new_conf); - is_msg_dlg_already_exist = false; - } - //BBS: ironing_spacing shouldn't be too small or equal to zero if (config->opt_float("ironing_spacing") < 0.05) { diff --git a/src/slic3r/GUI/ConfigManipulation.hpp b/src/slic3r/GUI/ConfigManipulation.hpp index d191ef2c4f..ac53ffb4bb 100644 --- a/src/slic3r/GUI/ConfigManipulation.hpp +++ b/src/slic3r/GUI/ConfigManipulation.hpp @@ -86,6 +86,9 @@ public: void check_filament_max_volumetric_speed(DynamicPrintConfig *config); void check_chamber_temperature(DynamicPrintConfig* config); void check_chamber_minimal_temperature(DynamicPrintConfig* config); + bool check_layer_height(DynamicPrintConfig* config); + bool layer_height_out_of_range_dialog(DynamicPrintConfig* config, double clamp_to); + void layer_height_limits(double& min_layer_height, double& max_layer_height) const; void set_is_BBL_Printer(bool is_bbl_printer) { is_BBL_Printer = is_bbl_printer; }; bool get_is_BBL_Printer() { return is_BBL_Printer; }; // SLA print diff --git a/src/slic3r/GUI/GUI_ObjectSettings.cpp b/src/slic3r/GUI/GUI_ObjectSettings.cpp index 65cd99a2fa..25e6204a40 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.cpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.cpp @@ -139,7 +139,7 @@ bool ObjectSettings::update_settings_list() optgroup->sidetext_width = 5; optgroup->m_on_change = [this, config](const t_config_option_key& opt_id, const boost::any& value) { - this->update_config_values(config); + this->update_config_values(config, opt_id); wxGetApp().obj_list()->changed_object(); }; // call back for rescaling of the extracolumn control @@ -325,7 +325,7 @@ bool ObjectSettings::add_missed_options(ModelConfig* config_to, const DynamicPri return is_added; } -void ObjectSettings::update_config_values(ModelConfig* config) +void ObjectSettings::update_config_values(ModelConfig* config, const std::string& changed_opt_key) { const auto objects_model = wxGetApp().obj_list()->GetModel(); const auto item = wxGetApp().obj_list()->GetSelection(); @@ -403,6 +403,10 @@ void ObjectSettings::update_config_values(ModelConfig* config) } main_config.apply(config->get(), true); + + if (printer_technology == ptFFF && changed_opt_key == "layer_height") + config_manipulation.check_layer_height(&main_config); + printer_technology == ptFFF ? config_manipulation.update_print_fff_config(&main_config) : config_manipulation.update_print_sla_config(&main_config) ; diff --git a/src/slic3r/GUI/GUI_ObjectSettings.hpp b/src/slic3r/GUI/GUI_ObjectSettings.hpp index 8903f8748b..21146425cc 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.hpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.hpp @@ -66,7 +66,7 @@ public: * we should add sparse_infill_pattern to avoid endless loop in update */ bool add_missed_options(ModelConfig *config_to, const DynamicPrintConfig &config_from); - void update_config_values(ModelConfig *config); + void update_config_values(ModelConfig *config, const std::string& changed_opt_key = ""); void UpdateAndShow(const bool show); void msw_rescale(); void sys_color_changed(); diff --git a/src/slic3r/GUI/GUI_ObjectTableSettings.cpp b/src/slic3r/GUI/GUI_ObjectTableSettings.cpp index 37ccfb8e41..e72c421a08 100644 --- a/src/slic3r/GUI/GUI_ObjectTableSettings.cpp +++ b/src/slic3r/GUI/GUI_ObjectTableSettings.cpp @@ -223,7 +223,7 @@ bool ObjectTableSettings::update_settings_list(bool is_object, bool is_multiple_ std::weak_ptr weak_optgroup(optgroup); optgroup->m_on_change = [this, is_object, object, config, group_category](const t_config_option_key &opt_id, const boost::any &value) { this->m_parent->Freeze(); - this->update_config_values(is_object, object, config, group_category); + this->update_config_values(is_object, object, config, group_category, opt_id); wxGetApp().obj_list()->changed_object(); this->m_parent->Thaw(); //update_extra_column_visible_status(optgroup.get(), cat.second, config); @@ -369,7 +369,7 @@ int ObjectTableSettings::update_extra_column_visible_status(ConfigOptionsGroup* return count; } -void ObjectTableSettings::update_config_values(bool is_object, ModelObject* object, ModelConfig* config, const std::string& category) +void ObjectTableSettings::update_config_values(bool is_object, ModelObject* object, ModelConfig* config, const std::string& category, const std::string& changed_opt_key) { int different_count = 0; const auto printer_technology = wxGetApp().plater()->printer_technology(); @@ -403,6 +403,9 @@ void ObjectTableSettings::update_config_values(bool is_object, ModelObject* obje config_manipulation.set_is_BBL_Printer(wxGetApp().preset_bundle->is_bbl_vendor()); + if (printer_technology == ptFFF && changed_opt_key == "layer_height") + config_manipulation.check_layer_height(&main_config); + printer_technology == ptFFF ? config_manipulation.update_print_fff_config(&main_config) : config_manipulation.update_print_sla_config(&main_config) ; diff --git a/src/slic3r/GUI/GUI_ObjectTableSettings.hpp b/src/slic3r/GUI/GUI_ObjectTableSettings.hpp index 534426f554..39e7e514e2 100644 --- a/src/slic3r/GUI/GUI_ObjectTableSettings.hpp +++ b/src/slic3r/GUI/GUI_ObjectTableSettings.hpp @@ -70,7 +70,7 @@ public: bool add_missed_options(ModelConfig *config_to, const DynamicPrintConfig &config_from); //return visible count int update_extra_column_visible_status(ConfigOptionsGroup* option_group, const std::vector& option_keys, ModelConfig* config); - void update_config_values(bool is_object, ModelObject* object, ModelConfig* config, const std::string& category); + void update_config_values(bool is_object, ModelObject* object, ModelConfig* config, const std::string& category, const std::string& changed_opt_key = ""); void UpdateAndShow(int row, const bool show, bool is_object, bool is_multiple_selection, ModelObject* object, ModelConfig* config, const std::string& category); void ValueChanged(int row, bool is_object, ModelObject* object, ModelConfig* config, const std::string& category, const std::string& key); void resetAllValues(int row, bool is_object, ModelObject* object, ModelConfig* config, const std::string& category); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 64c2b586c8..1a31355d0e 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2136,43 +2136,9 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) m_last_sparse_infill_rotate_template_value = m_config->opt_string("sparse_infill_rotate_template"); } - if(opt_key=="layer_height"){ - auto min_layer_height_from_nozzle=m_preset_bundle->full_config().option("min_layer_height")->values; - auto max_layer_height_from_nozzle=m_preset_bundle->full_config().option("max_layer_height")->values; - auto layer_height_floor = *std::min_element(min_layer_height_from_nozzle.begin(), min_layer_height_from_nozzle.end()); - auto layer_height_ceil = *std::max_element(max_layer_height_from_nozzle.begin(), max_layer_height_from_nozzle.end()); - const auto lh = m_config->opt_float("layer_height"); - bool exceed_minimum_flag = lh < layer_height_floor; - bool exceed_maximum_flag = lh > layer_height_ceil; - - if (exceed_maximum_flag || exceed_minimum_flag) { - if (lh < EPSILON) { - auto msg_text = _(L("Layer height is too small.\nIt will set to min_layer_height\n")); - MessageDialog dialog(wxGetApp().plater(), msg_text, "", wxICON_WARNING | wxOK); - dialog.SetButtonLabel(wxID_OK, _L("OK")); - dialog.ShowModal(); - auto new_conf = *m_config; - new_conf.set_key_value("layer_height", new ConfigOptionFloat(layer_height_floor)); - m_config_manipulation.apply(m_config, &new_conf); - } else { - wxString msg_text = _(L("Layer height exceeds the limit in Printer Settings -> Extruder -> Layer height limits, " - "this may cause printing quality issues.")); - msg_text += "\n\n" + _(L("Adjust to the set range automatically?\n")); - MessageDialog dialog(wxGetApp().plater(), msg_text, "", wxICON_WARNING | wxYES | wxNO); - dialog.SetButtonLabel(wxID_YES, _L("Adjust")); - dialog.SetButtonLabel(wxID_NO, _L("Ignore")); - auto answer = dialog.ShowModal(); - auto new_conf = *m_config; - if (answer == wxID_YES) { - if (exceed_maximum_flag) - new_conf.set_key_value("layer_height", new ConfigOptionFloat(layer_height_ceil)); - if (exceed_minimum_flag) - new_conf.set_key_value("layer_height", new ConfigOptionFloat(layer_height_floor)); - m_config_manipulation.apply(m_config, &new_conf); - } - } + if (opt_key == "layer_height") { + if (m_config_manipulation.check_layer_height(m_config)) wxGetApp().plater()->update(); - } } string opt_key_without_idx = opt_key.substr(0, opt_key.find('#'));