From e342698d8ef37ccf0ce1fb095192a81cd5413c53 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Mon, 24 Aug 2026 15:30:02 +0800 Subject: [PATCH] Update sublayer option check. Add validation warning for gradient mixed filament without sublayer mixing --- src/libslic3r/Print.cpp | 13 +++++ src/slic3r/GUI/MixedFilamentDialog.cpp | 24 --------- tests/fff_print/test_mixed_filament.cpp | 69 +++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 24 deletions(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 8be66da2a4..1bc1015477 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1328,6 +1328,19 @@ StringObjectException Print::validate(std::vector *warnin if (extruders.empty()) return { L("No extrusions under current settings.") }; + // Orca: a gradient mixed filament only renders its gradient with "Mixed color sublayer" on; + // without it ToolOrdering::resolve_mixed_filaments prints one whole component per layer and + // the gradient is dropped silently. extruders() already covers painting, height ranges, + // per-feature filament ids and supports, and still lists mixed slots under their own id here. + if (!m_config.enable_mixed_color_sublayer.value) { + const auto &is_mixed = m_config.filament_is_mixed.values; + const auto &gradient = m_config.filament_mixed_gradient.values; + if (std::any_of(extruders.begin(), extruders.end(), [&](unsigned int e) { + return e < is_mixed.size() && is_mixed[e] && e < gradient.size() && gradient[e]; })) + warn(L("A gradient mixed filament is used, but 'Mixed color sublayer' is disabled. The gradient will not be printed."), + "enable_mixed_color_sublayer"); + } + if (nozzles < 2 && extruders.size() > 1) { auto ret = check_multi_filament_valid(*this); if (!ret.string.empty()) diff --git a/src/slic3r/GUI/MixedFilamentDialog.cpp b/src/slic3r/GUI/MixedFilamentDialog.cpp index 46563664cc..a947f0ed6c 100644 --- a/src/slic3r/GUI/MixedFilamentDialog.cpp +++ b/src/slic3r/GUI/MixedFilamentDialog.cpp @@ -23,8 +23,6 @@ #include "GradientCurveEditor.hpp" #include "FilamentBitmapUtils.hpp" #include "wxExtensions.hpp" -#include "Tab.hpp" -#include "libslic3r/Preset.hpp" #include "Widgets/Button.hpp" #include "Widgets/CheckBox.hpp" #include "Widgets/ComboBox.hpp" @@ -1492,28 +1490,6 @@ void MixedFilamentDialog::on_ratio_changed(int new_ratio_a) void MixedFilamentDialog::on_gradient_toggled() { - // Orca: a gradient is only sliced when the print profile's "enable_mixed_color_sublayer" - // option is on; without it ToolOrdering picks a single component per whole layer. Offer to - // turn the option on instead of silently ignoring the gradient the user just enabled. - bool checked = m_chk_gradient->GetValue(); - - if (checked) { - auto& print_config = wxGetApp().preset_bundle->prints.get_edited_preset().config; - if (!print_config.opt_bool("enable_mixed_color_sublayer")) { - wxMessageDialog dlg(this, - _L("Gradient effect requires 'Mixed color sublayer' to be enabled. Enable it now?"), - _L("Mixed Color Sublayer"), - wxYES_NO | wxICON_QUESTION); - if (dlg.ShowModal() == wxID_YES) { - DynamicPrintConfig new_conf; - new_conf.set_key_value("enable_mixed_color_sublayer", new ConfigOptionBool(true)); - wxGetApp().get_tab(Preset::TYPE_PRINT)->load_config(new_conf); - } else { - m_chk_gradient->SetValue(false); - return; - } - } - } m_result.gradient_enabled = m_chk_gradient->GetValue(); diff --git a/tests/fff_print/test_mixed_filament.cpp b/tests/fff_print/test_mixed_filament.cpp index a8f1e2e84c..25b426c210 100644 --- a/tests/fff_print/test_mixed_filament.cpp +++ b/tests/fff_print/test_mixed_filament.cpp @@ -253,3 +253,72 @@ TEST_CASE("Print::validate rejects a mixed filament as the wipe tower filament", CHECK(err.opt_key == "wipe_tower_filament"); } } + +TEST_CASE("Print::validate warns when a gradient mixed filament is used without sublayer mixing", "[MixedFilament]") +{ + // A gradient mixed filament only renders its gradient with the process option enabled; without + // it ToolOrdering prints one whole component per layer and the gradient is dropped silently, + // so validate() warns whenever the slot actually takes part in the print. The layer-change + // reset avoids an unrelated relative-extrusion warning, as in the wipe tower test above. + DynamicPrintConfig config = mixed_config(false); + config.set_deserialize_strict({ + {"filament_mixed_gradient", "0,0,1"}, + {"layer_change_gcode", "G92 E0\n"}, + }); + + auto count_opt = [](Print &print, const char *opt_key) { + std::vector warnings; + print.validate(&warnings); + return std::count_if(warnings.begin(), warnings.end(), + [&](const StringObjectException &w) { return w.opt_key == opt_key; }); + }; + + SECTION("gradient slot used, sublayer mixing off") { + Print print; + Model model; + init_print({cube(20)}, print, model, config); + std::vector warnings; + const StringObjectException err = print.validate(&warnings); + CHECK(err.string.empty()); + const auto it = std::find_if(warnings.begin(), warnings.end(), [](const StringObjectException &w) { + return w.opt_key == "enable_mixed_color_sublayer"; + }); + REQUIRE(it != warnings.end()); + CHECK(it->is_warning); + CHECK(std::count_if(warnings.begin(), warnings.end(), [](const StringObjectException &w) { + return w.opt_key == "enable_mixed_color_sublayer"; + }) == 1); + } + + SECTION("sublayer mixing on") { + config.set_deserialize_strict({{"enable_mixed_color_sublayer", "1"}}); + Print print; + Model model; + init_print({cube(20)}, print, model, config); + CHECK(count_opt(print, "enable_mixed_color_sublayer") == 0); + } + + SECTION("gradient flag off") { + config.set_deserialize_strict({{"filament_mixed_gradient", "0,0,0"}}); + Print print; + Model model; + init_print({cube(20)}, print, model, config); + CHECK(count_opt(print, "enable_mixed_color_sublayer") == 0); + } + + SECTION("mixed slot not used") { + config.set_deserialize_strict({ + {"outer_wall_filament_id", "0"}, + {"inner_wall_filament_id", "0"}, + {"sparse_infill_filament_id", "0"}, + {"internal_solid_filament_id", "0"}, + {"top_surface_filament_id", "0"}, + {"bottom_surface_filament_id", "0"}, + }); + Print print; + Model model; + const std::vector> overrides{{{ "extruder", "1" }}}; + init_print(std::vector{cube(20)}, print, model, config, &overrides); + CHECK(count_opt(print, "enable_mixed_color_sublayer") == 0); + } +}