mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-30 04:11:00 +00:00
Add multi-variant support to the app
This commit is contained in:
@@ -715,3 +715,56 @@ TEST_CASE("Multi-extruder slice stays in bounds with a short max_layer_height",
|
||||
REQUIRE_FALSE(print.objects().front()->layers().empty());
|
||||
}
|
||||
|
||||
|
||||
// A filament can define several variants (Standard, High Flow). Each filament prints with its
|
||||
// variant of the extruder's variant string, or with its own first variant when it defines none, on a
|
||||
// printer listing a single variant as on one listing several.
|
||||
TEST_CASE("Each filament prints with its variant of the extruder's variant string", "[MultiFilament]")
|
||||
{
|
||||
auto [variant_list, nozzle_volume_type, filament, temperature, resolved] = GENERATE(table<std::string, NozzleVolumeType, int, int, std::string>({
|
||||
{ "Direct Drive Standard", nvtStandard, 1, 211, "211,223" },
|
||||
{ "Direct Drive Standard", nvtStandard, 2, 223, "211,223" },
|
||||
{ "Direct Drive High Flow", nvtHighFlow, 1, 239, "239,223" },
|
||||
{ "Direct Drive High Flow", nvtHighFlow, 2, 223, "239,223" }, // filament 2 defines no High Flow variant
|
||||
{ "Direct Drive Standard,Direct Drive High Flow", nvtHighFlow, 1, 239, "239,223" },
|
||||
{ "Direct Drive Standard,Direct Drive High Flow", nvtHighFlow, 2, 223, "239,223" },
|
||||
}));
|
||||
DYNAMIC_SECTION(variant_list << " printer, " << get_nozzle_volume_type_string(nozzle_volume_type) << " nozzle, filament " << filament) {
|
||||
DynamicPrintConfig config = multifilament_config(2, {
|
||||
{ "extruder_variant_list", variant_list },
|
||||
// filament 1 defines Standard (211) and High Flow (239), filament 2 Standard (223)
|
||||
{ "filament_extruder_variant", "Direct Drive Standard;Direct Drive High Flow;Direct Drive Standard" },
|
||||
{ "filament_self_index", "1,1,2" },
|
||||
{ "nozzle_temperature", "211,239,223" },
|
||||
{ "nozzle_temperature_initial_layer", "211,239,223" },
|
||||
{ "sparse_infill_filament_id", filament },
|
||||
{ "internal_solid_filament_id", filament },
|
||||
{ "top_surface_filament_id", filament },
|
||||
{ "bottom_surface_filament_id", filament },
|
||||
{ "outer_wall_filament_id", filament },
|
||||
{ "inner_wall_filament_id", filament },
|
||||
{ "enable_prime_tower", 0 },
|
||||
{ "skirt_loops", 0 },
|
||||
{ "brim_type", "no_brim" },
|
||||
// custom G-code indexes the per-filament arrays by filament
|
||||
{ "machine_start_gcode", "; start temperature {nozzle_temperature_initial_layer[initial_extruder]}" },
|
||||
});
|
||||
config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type", true)->values = { nozzle_volume_type };
|
||||
const std::string gcode = slice({ cube(20) }, config);
|
||||
|
||||
std::set<int> temperatures;
|
||||
std::istringstream stream(gcode);
|
||||
for (std::string line; std::getline(stream, line);) {
|
||||
if (line.rfind("M104 ", 0) != 0 && line.rfind("M109 ", 0) != 0)
|
||||
continue;
|
||||
const size_t s = line.find(" S");
|
||||
if (s != std::string::npos && std::stoi(line.substr(s + 2)) > 0)
|
||||
temperatures.insert(std::stoi(line.substr(s + 2)));
|
||||
}
|
||||
CHECK(temperatures == std::set<int>{ temperature });
|
||||
CHECK(gcode.find("; start temperature " + std::to_string(temperature) + "\n") != std::string::npos);
|
||||
// The config the slice ran with holds one value per filament, as the readers that index
|
||||
// it by filament (the wipe tower, the filament compatibility check) expect.
|
||||
CHECK(gcode.find("; nozzle_temperature = " + resolved + "\n") != std::string::npos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,13 +120,25 @@ TEST_CASE("get_config_index_base resolves (volume type, extruder type, id) to a
|
||||
REQUIRE(get_config_index_base(nvtHighFlow, etDirectDrive, 2, variant_list, variant_ids) == 3);
|
||||
}
|
||||
|
||||
SECTION("no matching column falls back to slot 0") {
|
||||
REQUIRE(get_config_index_base(nvtStandard, etDirectDrive, 3, variant_list, variant_ids) == 0);
|
||||
SECTION("no matching variant falls back to the id's first variant") {
|
||||
REQUIRE(get_config_index_base(nvtStandard, etBowden, 1, variant_list, variant_ids) == 0);
|
||||
REQUIRE(get_config_index_base(nvtStandard, etBowden, 2, variant_list, variant_ids) == 2);
|
||||
}
|
||||
|
||||
SECTION("Hybrid is not a preset variant string and falls back to slot 0") {
|
||||
REQUIRE(get_config_index_base(nvtHybrid, etDirectDrive, 2, variant_list, variant_ids) == 0);
|
||||
SECTION("an id without any variant falls back to variant index 0") {
|
||||
REQUIRE(get_config_index_base(nvtStandard, etDirectDrive, 3, variant_list, variant_ids) == 0);
|
||||
}
|
||||
|
||||
SECTION("Hybrid is not a preset variant string and falls back to the id's first variant") {
|
||||
REQUIRE(get_config_index_base(nvtHybrid, etDirectDrive, 2, variant_list, variant_ids) == 2);
|
||||
}
|
||||
|
||||
SECTION("a filament without a High Flow variant keeps its own variant, not the first filament's") {
|
||||
// filament 1 defines Standard and High Flow, filament 2 only Standard
|
||||
const std::vector<std::string> mixed_list = {"Direct Drive Standard", "Direct Drive High Flow", "Direct Drive Standard"};
|
||||
const std::vector<int> mixed_ids = {1, 1, 2};
|
||||
REQUIRE(get_config_index_base(nvtHighFlow, etDirectDrive, 1, mixed_list, mixed_ids) == 1);
|
||||
REQUIRE(get_config_index_base(nvtHighFlow, etDirectDrive, 2, mixed_list, mixed_ids) == 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -512,6 +524,73 @@ TEST_CASE("update_values_to_printer_extruders_for_multiple_filaments resolves pe
|
||||
Catch::Matchers::Approx(std::vector<double>({-1., -1.})));
|
||||
REQUIRE(config.option<ConfigOptionFloats>("filament_max_volumetric_speed")->values == std::vector<double>({12., 21.}));
|
||||
}
|
||||
|
||||
// A single extruder whose only variant is Standard or High Flow; filament 1 defines both
|
||||
// variants, filament 2 only Standard.
|
||||
auto make_single_variant_config = [](NozzleVolumeType nozzle_volume_type, const std::string &variant) {
|
||||
DynamicPrintConfig config;
|
||||
config.option<ConfigOptionEnumsGeneric>("extruder_type", true)->values = {etDirectDrive};
|
||||
config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type", true)->values = {nozzle_volume_type};
|
||||
config.option<ConfigOptionStrings>("extruder_variant_list", true)->values = {variant};
|
||||
config.option<ConfigOptionFloats>("filament_diameter", true)->values = {1.75, 1.75};
|
||||
config.option<ConfigOptionInts>("filament_self_index", true)->values = {1, 1, 2};
|
||||
config.option<ConfigOptionStrings>("filament_extruder_variant", true)->values = {"Direct Drive Standard", "Direct Drive High Flow",
|
||||
"Direct Drive Standard"};
|
||||
config.option<ConfigOptionFloats>("filament_max_volumetric_speed", true)->values = {12., 20., 13.};
|
||||
config.option<ConfigOptionInts>("filament_map", true)->values = {1, 1};
|
||||
return config;
|
||||
};
|
||||
|
||||
SECTION("a single-variant printer picks each filament's variant of that variant string") {
|
||||
auto [nozzle_volume_type, variant, speeds] = GENERATE(table<NozzleVolumeType, std::string, std::vector<double>>({
|
||||
{nvtStandard, "Direct Drive Standard", {12., 13.}},
|
||||
// filament 2 defines no High Flow variant and keeps its first
|
||||
{nvtHighFlow, "Direct Drive High Flow", {20., 13.}},
|
||||
}));
|
||||
DynamicPrintConfig config = make_single_variant_config(nozzle_volume_type, variant);
|
||||
int extruder_count = 1;
|
||||
REQUIRE_FALSE(config.support_different_extruders(extruder_count));
|
||||
REQUIRE(config.has_multi_variant_filament());
|
||||
|
||||
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
|
||||
int count = config.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
|
||||
config.update_values_to_printer_extruders_for_multiple_filaments(config, extruder_count, count, filament_keys,
|
||||
"filament_self_index", "filament_extruder_variant");
|
||||
|
||||
REQUIRE(config.option<ConfigOptionFloats>("filament_max_volumetric_speed")->values == speeds);
|
||||
REQUIRE(config.option<ConfigOptionInts>("filament_self_index")->values == std::vector<int>({1, 2}));
|
||||
}
|
||||
|
||||
SECTION("a filament map shorter than the filament count keeps every filament") {
|
||||
DynamicPrintConfig config = make_single_variant_config(nvtStandard, "Direct Drive Standard");
|
||||
config.option<ConfigOptionInts>("filament_map", true)->values = {1};
|
||||
|
||||
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
|
||||
int extruder_count = 1;
|
||||
int count = config.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
|
||||
config.update_values_to_printer_extruders_for_multiple_filaments(config, extruder_count, count, filament_keys,
|
||||
"filament_self_index", "filament_extruder_variant");
|
||||
|
||||
REQUIRE(config.option<ConfigOptionFloats>("filament_max_volumetric_speed")->values == std::vector<double>({12., 13.}));
|
||||
REQUIRE(config.option<ConfigOptionInts>("filament_self_index")->values == std::vector<int>({1, 2}));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("has_multi_variant_filament is true only when a filament defines more than one variant", "[Config]")
|
||||
{
|
||||
DynamicPrintConfig config;
|
||||
config.option<ConfigOptionFloats>("filament_diameter", true)->values = {1.75, 1.75};
|
||||
|
||||
SECTION("one variant per filament reports false") {
|
||||
config.option<ConfigOptionStrings>("filament_extruder_variant", true)->values = {"Direct Drive Standard", "Bowden Standard"};
|
||||
REQUIRE_FALSE(config.has_multi_variant_filament());
|
||||
}
|
||||
|
||||
SECTION("a filament with Standard and High Flow variants reports true") {
|
||||
config.option<ConfigOptionStrings>("filament_extruder_variant", true)->values = {"Direct Drive Standard", "Direct Drive High Flow",
|
||||
"Direct Drive Standard"};
|
||||
REQUIRE(config.has_multi_variant_filament());
|
||||
}
|
||||
}
|
||||
|
||||
// update_values_from_multi_to_multi_2 walks the DESTINATION PRINTER's variant list while writing
|
||||
|
||||
@@ -10,11 +10,13 @@ TEST_CASE("convert_to_nvt_type maps extruder variant strings to nozzle volume ty
|
||||
REQUIRE(convert_to_nvt_type("Direct Drive Standard") == nvtStandard);
|
||||
REQUIRE(convert_to_nvt_type("Direct Drive High Flow") == nvtHighFlow);
|
||||
REQUIRE(convert_to_nvt_type("Direct Drive TPU High Flow") == nvtTPUHighFlow);
|
||||
REQUIRE(convert_to_nvt_type("Direct Drive E3D High Flow") == nvtE3DHighFlow);
|
||||
}
|
||||
|
||||
SECTION("Bowden variants") {
|
||||
REQUIRE(convert_to_nvt_type("Bowden Standard") == nvtStandard);
|
||||
REQUIRE(convert_to_nvt_type("Bowden High Flow") == nvtHighFlow);
|
||||
REQUIRE(convert_to_nvt_type("Bowden E3D High Flow") == nvtE3DHighFlow);
|
||||
}
|
||||
|
||||
SECTION("Unparsable strings fall back to hybrid") {
|
||||
@@ -29,3 +31,49 @@ TEST_CASE("convert_to_nvt_type maps extruder variant strings to nozzle volume ty
|
||||
REQUIRE(convert_to_nvt_type(" Bowden Standard") == nvtStandard);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("E3D High Flow is nozzle volume type 5, after the reserved 4", "[Config]")
|
||||
{
|
||||
REQUIRE(int(nvtE3DHighFlow) == 5);
|
||||
REQUIRE(get_valid_nozzle_volume_type() == std::set<NozzleVolumeType>{nvtStandard, nvtHighFlow, nvtTPUHighFlow, nvtE3DHighFlow});
|
||||
REQUIRE(get_nozzle_volume_type_string(nvtE3DHighFlow) == "E3D High Flow");
|
||||
REQUIRE(get_extruder_variant_string(etDirectDrive, nvtE3DHighFlow) == "Direct Drive E3D High Flow");
|
||||
REQUIRE(get_extruder_variant_string(etBowden, nvtE3DHighFlow) == "Bowden E3D High Flow");
|
||||
|
||||
SECTION("nozzle_volume_type round-trips it by name") {
|
||||
DynamicPrintConfig config;
|
||||
config.set_deserialize_strict("nozzle_volume_type", "Standard,E3D High Flow");
|
||||
REQUIRE(config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type")->values == std::vector<int>{nvtStandard, nvtE3DHighFlow});
|
||||
REQUIRE(config.opt_serialize("nozzle_volume_type") == "Standard,E3D High Flow");
|
||||
}
|
||||
|
||||
SECTION("its variant is found by value, not mistaken for High Flow") {
|
||||
const std::vector<std::string> variant_list = {"Direct Drive Standard", "Direct Drive High Flow", "Direct Drive E3D High Flow"};
|
||||
const std::vector<int> variant_ids = {1, 1, 1};
|
||||
REQUIRE(get_config_index_base(nvtE3DHighFlow, etDirectDrive, 1, variant_list, variant_ids) == 2);
|
||||
REQUIRE(get_config_index_base(nvtHighFlow, etDirectDrive, 1, variant_list, variant_ids) == 1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("get_extruder_supported_nozzle_volume_types reads the extruder's variant list", "[Config]")
|
||||
{
|
||||
DynamicPrintConfig config;
|
||||
// H2C: the left extruder offers E3D High Flow, the right (nozzle rack) extruder does not.
|
||||
config.set_deserialize_strict("extruder_type", "Direct Drive,Direct Drive");
|
||||
config.option<ConfigOptionStrings>("extruder_variant_list", true)->values = {
|
||||
"Direct Drive Standard,Direct Drive High Flow,Direct Drive E3D High Flow",
|
||||
"Direct Drive Standard,Direct Drive High Flow"};
|
||||
|
||||
REQUIRE(get_extruder_supported_nozzle_volume_types(config, 0) == std::set<NozzleVolumeType>{nvtStandard, nvtHighFlow, nvtE3DHighFlow});
|
||||
REQUIRE(get_extruder_supported_nozzle_volume_types(config, 1) == std::set<NozzleVolumeType>{nvtStandard, nvtHighFlow});
|
||||
|
||||
SECTION("an E3D-only list does not report plain High Flow") {
|
||||
config.option<ConfigOptionStrings>("extruder_variant_list")->values[1] = "Direct Drive Standard,Direct Drive E3D High Flow";
|
||||
REQUIRE(get_extruder_supported_nozzle_volume_types(config, 1) == std::set<NozzleVolumeType>{nvtStandard, nvtE3DHighFlow});
|
||||
}
|
||||
|
||||
SECTION("an extruder past the profile's lists gives the empty, unknown set") {
|
||||
REQUIRE(get_extruder_supported_nozzle_volume_types(config, 2).empty());
|
||||
REQUIRE(get_extruder_supported_nozzle_volume_types(config, -1).empty());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user