diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 832ff9b3b4..6c3caafdc4 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -10032,6 +10032,48 @@ static void extend_extruder_variant(DynamicPrintConfig& config, const unsigned i } } +// Options in printer_options_with_variant_2 are stored as (normal,silent) pairs per printer variant. +// Some legacy presets/projects carry a variant list but still store only one pair; normalize to avoid crashes. +static void normalize_stride2_floats(ConfigOptionFloats &opt, size_t expected_size) +{ + auto &v = opt.values; + if (expected_size == 0) { + v.clear(); + return; + } + if (v.empty()) { + // Fallback: keep behavior predictable instead of crashing. This should be rare. + v.resize(expected_size, 0.0); + return; + } + + const double first = v[0]; + const double second = (v.size() >= 2) ? v[1] : first; + + // Ensure we have at least one (normal,silent) pair to replicate. + if (v.size() < 2) { + v.resize(2, first); + v[1] = second; + } + // Keep pair alignment if some legacy preset produced odd length. + if (v.size() % 2 != 0) + v.push_back(second); + + if (v.size() > expected_size) { + v.resize(expected_size); + return; + } + + const size_t have_variants = v.size() / 2; + const size_t want_variants = expected_size / 2; + v.resize(expected_size); + for (size_t vi = have_variants; vi < want_variants; ++vi) { + v[vi * 2] = first; + if (vi * 2 + 1 < v.size()) + v[vi * 2 + 1] = second; + } +} + void DynamicPrintConfig::set_num_extruders(unsigned int num_extruders) { extend_extruder_variant(*this, num_extruders); @@ -10049,6 +10091,11 @@ void DynamicPrintConfig::set_num_extruders(unsigned int num_extruders) static_cast(opt)->resize(get_parameter_size(key, num_extruders), defaults.option(key)); } } + // Orca: machine limits are not extruder option keys and need a pair-aware resize, so that every + // printer variant has its own (normal, silent) pair to edit. + for (const std::string &key : printer_options_with_variant_2) + if (auto *opt = this->option(key)) + normalize_stride2_floats(*opt, get_parameter_size(key, num_extruders)); } // BBS @@ -11446,48 +11493,6 @@ void DynamicPrintConfig::update_filament_config_values_for_multiple_extruders(Dy } namespace { -// Options in printer_options_with_variant_2 are stored as (normal,silent) pairs per printer variant. -// Some legacy presets/projects carry a variant list but still store only one pair; normalize to avoid crashes. -static void normalize_stride2_floats(ConfigOptionFloats &opt, size_t expected_size) -{ - auto &v = opt.values; - if (expected_size == 0) { - v.clear(); - return; - } - if (v.empty()) { - // Fallback: keep behavior predictable instead of crashing. This should be rare. - v.resize(expected_size, 0.0); - return; - } - - const double first = v[0]; - const double second = (v.size() >= 2) ? v[1] : first; - - // Ensure we have at least one (normal,silent) pair to replicate. - if (v.size() < 2) { - v.resize(2, first); - v[1] = second; - } - // Keep pair alignment if some legacy preset produced odd length. - if (v.size() % 2 != 0) - v.push_back(second); - - if (v.size() > expected_size) { - v.resize(expected_size); - return; - } - - const size_t have_variants = v.size() / 2; - const size_t want_variants = expected_size / 2; - v.resize(expected_size); - for (size_t vi = have_variants; vi < want_variants; ++vi) { - v[vi * 2] = first; - if (vi * 2 + 1 < v.size()) - v[vi * 2 + 1] = second; - } -} - static void log_normalize_legacy_vector_size(const char *fn, const std::string &key, int stride, size_t src_size, size_t dest_size, size_t expected_size, size_t restore_n, int cur_variant_count, int target_variant_count, size_t cur_ids, size_t target_ids, const ConfigOption *opt_src, const ConfigOption *opt_target) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 80b930c35e..e064043eba 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1161,8 +1161,9 @@ std::string Tab::options_list_storage_key(const std::string& opt_key) const // Orca: deep_diff() flags every vector entry at or past the reference vector's length as changed, // whatever its value (e.g. the values of an extruder added by raising the extruder count). A vector -// grows by copying its first entry (ConfigOptionVector::resize), so such an entry only counts as -// changed when it differs from the reference's first entry, as before Orca's deep_diff() change. +// grows by copying its first entry (ConfigOptionVector::resize), and machine limits by copying their +// first (normal, silent) pair (set_num_extruders), so such an entry only counts as changed when it +// differs from the reference entry it was copied from, as before Orca's deep_diff() change. // The change of the count itself shows on "extruders_count". static void drop_unchanged_added_entries(std::vector &options, const DynamicPrintConfig ¤t, const Preset *reference) { @@ -1171,7 +1172,7 @@ static void drop_unchanged_added_entries(std::vector &options, cons // deep_diff() lists a key's entries one after another, so serialize each key's vectors once. std::string serialized_key; std::vector cur_values; - std::string ref_first; + std::vector ref_values; options.erase(std::remove_if(options.begin(), options.end(), [&](const std::string &opt) { const auto pos = opt.find('#'); if (pos == std::string::npos) @@ -1185,9 +1186,10 @@ static void drop_unchanged_added_entries(std::vector &options, cons if (key != serialized_key) { serialized_key = key; cur_values = cur->vserialize(); - ref_first = ref->vserialize().front(); + ref_values = ref->vserialize(); } - return cur_values[idx] == ref_first; + const size_t copied_from = printer_options_with_variant_2.count(key) > 0 && idx % 2 < ref_values.size() ? idx % 2 : 0; + return cur_values[idx] == ref_values[copied_from]; }), options.end()); } diff --git a/tests/libslic3r/test_config_variant_expansion.cpp b/tests/libslic3r/test_config_variant_expansion.cpp index 240a6e25fa..5880e21645 100644 --- a/tests/libslic3r/test_config_variant_expansion.cpp +++ b/tests/libslic3r/test_config_variant_expansion.cpp @@ -799,3 +799,40 @@ TEST_CASE("A per-variant filament option read with a single value gives it to ev config.load_from_ini_string("pressure_advance = 0.021", ForwardCompatibilitySubstitutionRule::Disable); REQUIRE(config.option("pressure_advance")->values == std::vector({0.021, 0.021, 0.021})); } + +// Machine limits (printer_options_with_variant_2) hold a (normal, silent) pair per printer variant, so the +// printer Tab's Motion ability page can edit each extruder's own limits. +TEST_CASE("set_num_extruders gives every printer variant its own pair of machine limits", "[Config]") +{ + DynamicPrintConfig config = DynamicPrintConfig::full_print_config(); + auto speed_x = [&config]() { return config.option("machine_max_speed_x")->values; }; + + SECTION("a single pair is copied to every extruder") { + config.option("machine_max_speed_x")->values = {500., 200.}; + config.set_num_extruders(3); + REQUIRE(config.option("printer_extruder_variant")->size() == 3); + REQUIRE(speed_x() == std::vector({500., 200., 500., 200., 500., 200.})); + } + + SECTION("per-extruder pairs are kept, and removing an extruder removes its pair") { + config.option("machine_max_speed_x")->values = {500., 200., 400., 150., 300., 100.}; + config.set_num_extruders(3); + REQUIRE(speed_x() == std::vector({500., 200., 400., 150., 300., 100.})); + config.set_num_extruders(2); + REQUIRE(speed_x() == std::vector({500., 200., 400., 150.})); + } + + SECTION("a printer with nozzle variants gets a pair per variant column") { + // 2 extruders x 2 variants = 4 columns + config.option("extruder_variant_list", true)->values = {"Direct Drive Standard,Direct Drive High Flow", + "Direct Drive Standard,Direct Drive High Flow"}; + const std::vector per_variant = {500., 200., 510., 210., 520., 220., 530., 230.}; + config.option("machine_max_speed_x")->values = per_variant; + config.set_num_extruders(2); + REQUIRE(speed_x() == per_variant); + + config.option("machine_max_speed_x")->values = {500., 200.}; + config.set_num_extruders(2); + REQUIRE(speed_x() == std::vector({500., 200., 500., 200., 500., 200., 500., 200.})); + } +}