mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-06 15:21:09 +00:00
Fix adding filaments with incomplete mixed metadata (#15728)
This commit is contained in:
@@ -1617,7 +1617,7 @@ const char *kMixedKeys[] = {
|
||||
} // namespace
|
||||
|
||||
// Mixed-color filament metadata lives in project_config as parallel per-filament arrays.
|
||||
// set_num_filaments() is the single place that grows them alongside filament_colour; if it
|
||||
// set_num_filaments() grows them alongside filament_colour; if it
|
||||
// misses them, creating a mixed slot writes past the end of the short arrays.
|
||||
TEST_CASE("set_num_filaments keeps mixed-color arrays in step with the filament count", "[Preset][Bundle][FilamentMixer]")
|
||||
{
|
||||
@@ -4541,6 +4541,153 @@ TEST_CASE("Published 3MF overrides each extruder slot on a similar multi-extrude
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Loading incomplete mixed metadata normalizes slots before adding a filament", "[Preset][Bundle][FilamentMixer]")
|
||||
{
|
||||
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
||||
const std::vector<std::string> colors = { "#000000", "#FFFFFF", "#5E5C64" };
|
||||
config.option<ConfigOptionStrings>("filament_colour")->values = colors;
|
||||
config.option<ConfigOptionBool>("single_extruder_multi_material")->value = true;
|
||||
config.option<ConfigOptionFloats>("filament_diameter")->values = { 1.75, 1.75, 1.75 };
|
||||
config.option<ConfigOptionStrings>("filament_settings_id", true)->values = { "Test PETG", "Test PLA", "Test TPU" };
|
||||
const std::vector<std::string> bool_keys = {
|
||||
"filament_is_mixed", "filament_mixed_gradient", "filament_mixed_gradient_per_part"
|
||||
};
|
||||
const std::vector<std::string> string_keys = {
|
||||
"filament_mixed_components", "filament_mixed_sublayer_ratios",
|
||||
"filament_mixed_gradient_range", "filament_mixed_gradient_curve"
|
||||
};
|
||||
const size_t metadata_size = GENERATE(0u, 1u, 4u);
|
||||
for (const auto &key : bool_keys) {
|
||||
if (metadata_size == 0)
|
||||
config.erase(key);
|
||||
else {
|
||||
auto &values = config.option<ConfigOptionBools>(key)->values;
|
||||
values.assign(metadata_size, false);
|
||||
if (metadata_size > colors.size())
|
||||
values.back() = true;
|
||||
}
|
||||
}
|
||||
for (const auto &key : string_keys) {
|
||||
if (metadata_size == 0)
|
||||
config.erase(key);
|
||||
else {
|
||||
auto &values = config.option<ConfigOptionStrings>(key)->values;
|
||||
values.assign(metadata_size, "");
|
||||
if (metadata_size > colors.size())
|
||||
values.back() = "stale";
|
||||
}
|
||||
}
|
||||
Preset::normalize(config);
|
||||
|
||||
PresetBundle bundle;
|
||||
bundle.load_config_model("test.3mf", std::move(config), Semver());
|
||||
const auto presets = bundle.filament_presets;
|
||||
REQUIRE(presets.size() == colors.size());
|
||||
REQUIRE(presets[0] != presets[1]);
|
||||
REQUIRE(presets[1] != presets[2]);
|
||||
REQUIRE(presets[0] != presets[2]);
|
||||
for (const auto &key : bool_keys) {
|
||||
CAPTURE(key, metadata_size);
|
||||
CHECK(bundle.project_config.option<ConfigOptionBools>(key)->values ==
|
||||
std::vector<unsigned char>(colors.size(), false));
|
||||
}
|
||||
for (const auto &key : string_keys) {
|
||||
CAPTURE(key, metadata_size);
|
||||
CHECK(bundle.project_config.option<ConfigOptionStrings>(key)->values ==
|
||||
std::vector<std::string>(colors.size(), ""));
|
||||
}
|
||||
REQUIRE(bundle.num_physical_filaments() == colors.size());
|
||||
REQUIRE(bundle.num_mixed_filaments() == 0);
|
||||
|
||||
bundle.set_num_filaments(bundle.num_physical_filaments() + bundle.num_mixed_filaments() + 1, "#FF0000");
|
||||
REQUIRE(bundle.filament_presets.size() == presets.size() + 1);
|
||||
const auto &actual_colors = bundle.project_config.option<ConfigOptionStrings>("filament_colour")->values;
|
||||
REQUIRE(actual_colors.size() == colors.size() + 1);
|
||||
for (size_t i = 0; i < presets.size(); ++i) {
|
||||
CHECK(bundle.filament_presets[i] == presets[i]);
|
||||
CHECK(actual_colors[i] == colors[i]);
|
||||
}
|
||||
CHECK(bundle.num_physical_filaments() == colors.size() + 1);
|
||||
CHECK_FALSE(bundle.is_mixed_filament(colors.size()));
|
||||
}
|
||||
|
||||
TEST_CASE("Loading a project preserves existing mixed filament definitions", "[Preset][Bundle][FilamentMixer]")
|
||||
{
|
||||
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
||||
config.option<ConfigOptionBool>("single_extruder_multi_material")->value = true;
|
||||
config.option<ConfigOptionFloats>("filament_diameter")->values = { 1.75, 1.75, 1.75 };
|
||||
config.option<ConfigOptionStrings>("filament_colour")->values = { "#000000", "#FFFFFF", "#808080" };
|
||||
config.option<ConfigOptionBools>("filament_is_mixed")->values = { false, false, true };
|
||||
config.option<ConfigOptionStrings>("filament_mixed_components")->values = { "", "", "1,2" };
|
||||
config.option<ConfigOptionStrings>("filament_mixed_sublayer_ratios")->values = { "", "", "1,2" };
|
||||
config.option<ConfigOptionBools>("filament_mixed_gradient")->values = { false, false, true };
|
||||
config.option<ConfigOptionStrings>("filament_mixed_gradient_range")->values = { "", "", "0,100" };
|
||||
config.option<ConfigOptionStrings>("filament_mixed_gradient_curve")->values = { "", "", "0,0.1|1,0.9" };
|
||||
config.option<ConfigOptionBools>("filament_mixed_gradient_per_part")->values = { false, false, true };
|
||||
Preset::normalize(config);
|
||||
const auto original = config;
|
||||
|
||||
PresetBundle bundle;
|
||||
bundle.load_config_model("test.3mf", std::move(config), Semver());
|
||||
for (const auto *key : kMixedKeys) {
|
||||
CAPTURE(key);
|
||||
CHECK(*bundle.project_config.option(key) == *original.option(key));
|
||||
}
|
||||
CHECK(bundle.num_physical_filaments() == 2);
|
||||
CHECK(bundle.num_mixed_filaments() == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("Adding a filament preserves slots with incomplete mixed metadata", "[Preset][Bundle][FilamentMixer]")
|
||||
{
|
||||
PresetBundle bundle;
|
||||
bundle.set_num_filaments(3u, std::string("#000000"));
|
||||
auto *colors = bundle.project_config.option<ConfigOptionStrings>("filament_colour");
|
||||
colors->values = { "#000000", "#FFFFFF", "#5E5C64" };
|
||||
bundle.filament_presets = { "Test PETG", "Test PLA", "Test TPU" };
|
||||
const auto original_presets = bundle.filament_presets;
|
||||
const auto original_colors = colors->values;
|
||||
auto *flags = bundle.project_config.option<ConfigOptionBools>("filament_is_mixed");
|
||||
flags->values = GENERATE(std::vector<unsigned char>{}, std::vector<unsigned char>{ false },
|
||||
std::vector<unsigned char>{ false, false, false, true });
|
||||
const std::vector<std::string> string_keys = {
|
||||
"filament_mixed_components", "filament_mixed_sublayer_ratios",
|
||||
"filament_mixed_gradient_range", "filament_mixed_gradient_curve"
|
||||
};
|
||||
const std::vector<std::string> bool_keys = {
|
||||
"filament_mixed_gradient", "filament_mixed_gradient_per_part"
|
||||
};
|
||||
for (const auto &key : string_keys)
|
||||
bundle.project_config.option<ConfigOptionStrings>(key)->values = { "", "", "", "stale" };
|
||||
for (const auto &key : bool_keys)
|
||||
bundle.project_config.option<ConfigOptionBools>(key)->values = { false, false, false, true };
|
||||
|
||||
REQUIRE(bundle.num_physical_filaments() == original_colors.size());
|
||||
REQUIRE(bundle.num_mixed_filaments() == 0);
|
||||
bundle.set_num_filaments(bundle.num_physical_filaments() + bundle.num_mixed_filaments() + 1, "#FF0000");
|
||||
|
||||
REQUIRE(bundle.filament_presets.size() == original_presets.size() + 1);
|
||||
REQUIRE(colors->values.size() == original_colors.size() + 1);
|
||||
for (size_t i = 0; i < original_presets.size(); ++i) {
|
||||
CHECK(bundle.filament_presets[i] == original_presets[i]);
|
||||
CHECK(colors->values[i] == original_colors[i]);
|
||||
}
|
||||
CHECK(colors->values.back() == "#FF0000");
|
||||
CHECK(bundle.num_physical_filaments() == 4);
|
||||
CHECK(bundle.num_mixed_filaments() == 0);
|
||||
REQUIRE(flags->values.size() == 4);
|
||||
CHECK_FALSE(bundle.is_mixed_filament(3));
|
||||
for (const auto &key : string_keys) {
|
||||
CAPTURE(key);
|
||||
CHECK(bundle.project_config.option<ConfigOptionStrings>(key)->values ==
|
||||
std::vector<std::string>{ "", "", "", "" });
|
||||
}
|
||||
for (const auto &key : bool_keys) {
|
||||
CAPTURE(key);
|
||||
CHECK(bundle.project_config.option<ConfigOptionBools>(key)->values ==
|
||||
std::vector<unsigned char>{ false, false, false, false });
|
||||
}
|
||||
}
|
||||
|
||||
// The nozzle-count top-up in update_multi_material_filament_presets() grows filament_presets on
|
||||
// its own, so a physical count derived from that list reports a slot no per-filament array has
|
||||
// yet. That is what made the extruder-count handler conclude there was nothing to add and leave
|
||||
|
||||
Reference in New Issue
Block a user