From 7aed2dc8bd32595acec36c3e7880f5e2ec5dc0aa Mon Sep 17 00:00:00 2001 From: Clifford Date: Fri, 8 May 2026 15:03:39 -0400 Subject: [PATCH] fix(preset): don't truncate child variant vectors to parent size on load (#13316) Co-authored-by: Claude Opus 4.7 --- src/libslic3r/PrintConfig.cpp | 7 +++++ tests/libslic3r/test_config.cpp | 50 +++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index ac22ab89cc..9a1a794f2a 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -9641,6 +9641,13 @@ void DynamicPrintConfig::update_non_diff_values_to_base_config(DynamicPrintConfi //nothing to do, keep the original one } else { + // Guard: set_with_restore is parent-shaped and would truncate the child's + // vector when the child has more extruders than the parent (e.g. an IDEX + // preset inheriting from a single-nozzle base). The child's saved value is + // authoritative for its own extruder count, so skip the merge for this key. + if (cur_variant_count > target_variant_count) + continue; + int stride = 1; if (key_set2.find(opt) != key_set2.end()) stride = 2; diff --git a/tests/libslic3r/test_config.cpp b/tests/libslic3r/test_config.cpp index 5591842c64..2bee0d7d11 100644 --- a/tests/libslic3r/test_config.cpp +++ b/tests/libslic3r/test_config.cpp @@ -265,6 +265,56 @@ SCENARIO("DynamicPrintConfig serialization", "[Config]") { } } +SCENARIO("update_non_diff_values_to_base_config preserves child vectors when child has more extruders than parent", + "[Config][Variant]") { + GIVEN("A 2-extruder child printer config inheriting from a 1-extruder parent") { + Slic3r::DynamicPrintConfig child; + Slic3r::DynamicPrintConfig parent; + + child.set_key_value("nozzle_diameter", new Slic3r::ConfigOptionFloats({0.4, 0.4})); + child.set_key_value("printer_extruder_id", new Slic3r::ConfigOptionInts({1, 2})); + child.set_key_value("printer_extruder_variant", new Slic3r::ConfigOptionStrings({"Direct Drive Standard", "Direct Drive Standard"})); + child.set_key_value("retraction_length", new Slic3r::ConfigOptionFloats({1.5, 1.5})); + + parent.set_key_value("nozzle_diameter", new Slic3r::ConfigOptionFloats({0.4})); + parent.set_key_value("printer_extruder_id", new Slic3r::ConfigOptionInts({1})); + parent.set_key_value("printer_extruder_variant", new Slic3r::ConfigOptionStrings({"Direct Drive Standard"})); + parent.set_key_value("retraction_length", new Slic3r::ConfigOptionFloats({0.8})); + + const Slic3r::t_config_option_keys keys = { + "retraction_length", "printer_extruder_id", "printer_extruder_variant" + }; + const std::set different_keys = { + "retraction_length", "printer_extruder_id", "printer_extruder_variant" + }; + + WHEN("update_non_diff_values_to_base_config is called") { + std::string id_name = "printer_extruder_id"; + std::string var_name = "printer_extruder_variant"; + child.update_non_diff_values_to_base_config( + parent, keys, different_keys, id_name, var_name, + Slic3r::printer_options_with_variant_1, + Slic3r::printer_options_with_variant_2); + + THEN("printer_extruder_id retains size 2") { + REQUIRE(child.option("printer_extruder_id")->values.size() == 2); + } + THEN("printer_extruder_variant retains size 2") { + REQUIRE(child.option("printer_extruder_variant")->values.size() == 2); + } + THEN("retraction_length retains size 2") { + REQUIRE(child.option("retraction_length")->values.size() == 2); + } + THEN("printer_extruder_id values are preserved for both extruders") { + auto* pe_id = child.option("printer_extruder_id"); + REQUIRE(pe_id->values.size() == 2); + REQUIRE(pe_id->values[0] == 1); + REQUIRE(pe_id->values[1] == 2); + } + } + } +} + // SCENARIO("DynamicPrintConfig JSON serialization", "[Config]") { // WHEN("DynamicPrintConfig is serialized and deserialized") { // auto now = std::chrono::high_resolution_clock::now();