mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-07 15:51:08 +00:00
The device drying options hold several values per filament, as many as the filament preset gives, and a project stores them as the filaments' values one after another. The CLI filament merge wrote them like an option with one value per filament, putting each preset's first value at the filament's own index, so a project with three filaments whose preset gives "1", "0" was exported with 1;1;1;0;1;0 where the GUI writes 1;0;1;0;1;0. The merge now leaves these options out of the per-filament pass and rebuilds them afterwards from every filament's values in slot order. Without a fixed number of values per filament one slot cannot be replaced in place, so the stored values are kept when any slot has no config to rebuild from.
829 lines
49 KiB
C++
829 lines
49 KiB
C++
#include <catch2/catch_all.hpp>
|
|
#include "libslic3r/Config.hpp"
|
|
#include <string>
|
|
#include <vector>
|
|
#include <set>
|
|
#include <cmath>
|
|
|
|
#include <catch2/catch_test_macros.hpp>
|
|
#include <catch2/matchers/catch_matchers.hpp>
|
|
#include <catch2/matchers/catch_matchers_vector.hpp>
|
|
#include <catch2/generators/catch_generators.hpp>
|
|
#include <catch2/catch_approx.hpp>
|
|
#include "libslic3r/PrintConfig.hpp"
|
|
#include "libslic3r/Preset.hpp"
|
|
|
|
using namespace Slic3r;
|
|
|
|
namespace {
|
|
|
|
// A 2-extruder printer whose second extruder holds both a Standard and a High Flow nozzle
|
|
// (nozzle_volume_type Hybrid), described by extruder_nozzle_stats. The variant lists carry one
|
|
// column per (extruder x volume type) as composed from the presets.
|
|
DynamicPrintConfig make_hybrid_printer_config()
|
|
{
|
|
DynamicPrintConfig config;
|
|
config.option<ConfigOptionStrings>("extruder_nozzle_stats", true)->values = {"Standard#1", "Standard#3|High Flow#2"};
|
|
config.option<ConfigOptionEnumsGeneric>("extruder_type", true)->values = {etDirectDrive, etDirectDrive};
|
|
config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type", true)->values = {nvtStandard, nvtHybrid};
|
|
config.option<ConfigOptionStrings>("extruder_variant_list", true)->values = {"Direct Drive Standard,Direct Drive High Flow",
|
|
"Direct Drive Standard,Direct Drive High Flow"};
|
|
return config;
|
|
}
|
|
|
|
void add_print_variant_columns(DynamicPrintConfig &config)
|
|
{
|
|
config.option<ConfigOptionInts>("print_extruder_id", true)->values = {1, 1, 2, 2};
|
|
config.option<ConfigOptionStrings>("print_extruder_variant", true)->values = {"Direct Drive Standard", "Direct Drive High Flow",
|
|
"Direct Drive Standard", "Direct Drive High Flow"};
|
|
config.option<ConfigOptionFloats>("outer_wall_speed", true)->values = {30., 200., 50., 500.};
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST_CASE("Filament cooling and pressure settings follow the selected nozzle variant", "[Config][FilamentVariants]")
|
|
{
|
|
struct Setting {
|
|
const char *key;
|
|
const char *standard;
|
|
const char *high_flow;
|
|
};
|
|
const Setting settings[] = {
|
|
{"enable_pressure_advance", "0", "1"},
|
|
{"pressure_advance", "0.04", "0.02"},
|
|
{"fan_min_speed", "15", "10"},
|
|
{"fan_max_speed", "15", "60"},
|
|
{"additional_cooling_fan_speed", "70", "0"},
|
|
{"filament_minimal_purge_on_wipe_tower", "0", "15"},
|
|
{"filament_multitool_ramming", "0", "1"},
|
|
{"filament_multitool_ramming_volume", "0", "5"},
|
|
{"filament_multitool_ramming_flow", "10", "30"},
|
|
{"nozzle_temperature_range_low", "190", "210"},
|
|
{"nozzle_temperature_range_high", "255", "280"},
|
|
};
|
|
const int variant_index = GENERATE(0, 1, 2, 3);
|
|
const bool load_preset = GENERATE(false, true);
|
|
const bool high_flow = variant_index % 2 != 0;
|
|
for (const Setting &setting : settings) {
|
|
DYNAMIC_SECTION(setting.key << " variant=" << variant_index << " loaded=" << load_preset) {
|
|
DynamicPrintConfig printer = make_hybrid_printer_config();
|
|
if (variant_index >= 2)
|
|
printer.option<ConfigOptionEnumsGeneric>("extruder_type")->values[1] = etBowden;
|
|
DynamicPrintConfig filament;
|
|
filament.option<ConfigOptionStrings>("filament_extruder_variant", true)->values =
|
|
{"Direct Drive Standard", "Direct Drive High Flow", "Bowden Standard", "Bowden High Flow"};
|
|
// one value per variant of filament_extruder_variant
|
|
const std::string values = std::string(setting.standard) + "," + setting.high_flow + "," + setting.standard + "," + setting.high_flow;
|
|
REQUIRE(filament.option(setting.key, true)->deserialize(values));
|
|
|
|
if (load_preset) {
|
|
extend_default_config_length(filament, false, DynamicPrintConfig::full_print_config());
|
|
DynamicPrintConfig expanded;
|
|
REQUIRE(expanded.option(setting.key, true)->deserialize(values));
|
|
REQUIRE(*filament.option(setting.key) == *expanded.option(setting.key));
|
|
}
|
|
|
|
std::vector<std::vector<NozzleVolumeType>> nozzle_types;
|
|
const int count = printer.get_extruder_nozzle_volume_count(2, nozzle_types);
|
|
filament.update_values_to_printer_extruders(printer, 2, count, nozzle_types,
|
|
filament_options_with_variant, "", "filament_extruder_variant", 1, 2,
|
|
high_flow ? nvtHighFlow : nvtStandard);
|
|
|
|
DynamicPrintConfig expected;
|
|
REQUIRE(expected.option(setting.key, true)->deserialize(high_flow ? setting.high_flow : setting.standard));
|
|
REQUIRE(*filament.option(setting.key) == *expected.option(setting.key));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_CASE("apply_override fills nil entries from the 0-based default index", "[Config]")
|
|
{
|
|
ConfigOptionFloats machine({10., 20., 30.});
|
|
ConfigOptionFloatsNullable filament;
|
|
filament.values = {ConfigOptionFloatsNullable::nil_value(), 42.};
|
|
|
|
SECTION("a nil entry picks the slot addressed by its 0-based index") {
|
|
std::vector<int> slot_index{2, 0};
|
|
ConfigOptionFloats resolved(machine);
|
|
REQUIRE(resolved.apply_override(&filament, slot_index));
|
|
REQUIRE(resolved.values == std::vector<double>({30., 42.}));
|
|
}
|
|
|
|
SECTION("an index past the machine slots keeps the slot's own value") {
|
|
std::vector<int> slot_index{5, 0};
|
|
ConfigOptionFloats resolved(machine);
|
|
REQUIRE(resolved.apply_override(&filament, slot_index));
|
|
REQUIRE(resolved.values == std::vector<double>({10., 42.}));
|
|
}
|
|
|
|
SECTION("a negative index (unresolved slot) keeps the slot's own value") {
|
|
ConfigOptionFloatsNullable all_nil;
|
|
all_nil.values = {ConfigOptionFloatsNullable::nil_value(), ConfigOptionFloatsNullable::nil_value(),
|
|
ConfigOptionFloatsNullable::nil_value()};
|
|
std::vector<int> slot_index{2, -1, 0};
|
|
ConfigOptionFloats resolved(machine);
|
|
REQUIRE(!resolved.apply_override(&all_nil, slot_index));
|
|
REQUIRE(resolved.values == std::vector<double>({30., 20., 10.}));
|
|
}
|
|
|
|
SECTION("all-nil overrides keyed by unresolved slots leave the machine values intact") {
|
|
// The failed-lookup map a degenerate print_extruder_id used to produce; the negative
|
|
// slots must not collapse the machine array to its first value.
|
|
ConfigOptionFloats per_extruder({100., 70., 70., 70., 100.});
|
|
ConfigOptionFloatsNullable all_nil;
|
|
all_nil.values.assign(5, ConfigOptionFloatsNullable::nil_value());
|
|
std::vector<int> slot_index{0, -1, -1, -1, 0};
|
|
ConfigOptionFloats resolved(per_extruder);
|
|
REQUIRE(!resolved.apply_override(&all_nil, slot_index));
|
|
REQUIRE(resolved.values == std::vector<double>({100., 70., 70., 70., 100.}));
|
|
}
|
|
}
|
|
|
|
TEST_CASE("support_different_extruders is true only when the printer defines more than one variant column", "[Config]")
|
|
{
|
|
int extruder_count = 0;
|
|
|
|
SECTION("a non-Bambu dual-nozzle printer with one variant column reports false") {
|
|
DynamicPrintConfig config;
|
|
config.option<ConfigOptionFloats>("nozzle_diameter", true)->values = {0.4, 0.4};
|
|
// Both extruders resolve to the same default variant, so there is only one column.
|
|
config.option<ConfigOptionStrings>("extruder_variant_list", true)->values = {"Direct Drive Standard",
|
|
"Direct Drive Standard"};
|
|
REQUIRE(config.support_different_extruders(extruder_count) == false);
|
|
REQUIRE(extruder_count == 2);
|
|
}
|
|
|
|
SECTION("a Bambu H2D-style printer with distinct variants reports true") {
|
|
DynamicPrintConfig config;
|
|
config.option<ConfigOptionFloats>("nozzle_diameter", true)->values = {0.4, 0.4};
|
|
config.option<ConfigOptionStrings>("extruder_variant_list", true)->values = {
|
|
"Direct Drive Standard,Direct Drive High Flow",
|
|
"Direct Drive Standard,Direct Drive High Flow,Direct Drive TPU High Flow"};
|
|
REQUIRE(config.support_different_extruders(extruder_count) == true);
|
|
REQUIRE(extruder_count == 2);
|
|
}
|
|
|
|
SECTION("a many-toolhead printer that never opts into variants reports false") {
|
|
// A Snapmaker U1 has four identical toolheads and never defines extruder_variant_list,
|
|
// so the config falls back to a single default variant token.
|
|
DynamicPrintConfig config;
|
|
config.option<ConfigOptionFloats>("nozzle_diameter", true)->values = {0.4, 0.4, 0.4, 0.4};
|
|
REQUIRE(config.support_different_extruders(extruder_count) == false);
|
|
REQUIRE(extruder_count == 4);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("get_config_index_base resolves (volume type, extruder type, id) to a slot", "[Config]")
|
|
{
|
|
const std::vector<std::string> variant_list = {"Direct Drive Standard", "Direct Drive High Flow",
|
|
"Direct Drive Standard", "Direct Drive High Flow"};
|
|
const std::vector<int> variant_ids = {1, 1, 2, 2};
|
|
|
|
SECTION("a matching (variant, id) pair yields its slot") {
|
|
REQUIRE(get_config_index_base(nvtStandard, etDirectDrive, 1, variant_list, variant_ids) == 0);
|
|
REQUIRE(get_config_index_base(nvtHighFlow, etDirectDrive, 1, variant_list, variant_ids) == 1);
|
|
REQUIRE(get_config_index_base(nvtStandard, etDirectDrive, 2, variant_list, variant_ids) == 2);
|
|
REQUIRE(get_config_index_base(nvtHighFlow, etDirectDrive, 2, variant_list, variant_ids) == 3);
|
|
}
|
|
|
|
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("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);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("support interface pattern registry includes spiral inset", "[Config]")
|
|
{
|
|
const auto &values = ConfigOptionEnum<SupportMaterialInterfacePattern>::get_enum_values();
|
|
REQUIRE(values.at("spiralinset") == SupportMaterialInterfacePattern::smipSpiralInset);
|
|
}
|
|
|
|
TEST_CASE("get_extruder_nozzle_volume_count reads the per-extruder volume-type layout", "[Config]")
|
|
{
|
|
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
|
|
|
|
SECTION("absent stats fall back to one slot per extruder") {
|
|
DynamicPrintConfig config;
|
|
REQUIRE(config.get_extruder_nozzle_volume_count(2, nozzle_volume_types) == 2);
|
|
REQUIRE(nozzle_volume_types.size() == 2);
|
|
REQUIRE(nozzle_volume_types[0].empty());
|
|
REQUIRE(nozzle_volume_types[1].empty());
|
|
}
|
|
|
|
SECTION("stats sized differently from the extruder count are ignored") {
|
|
DynamicPrintConfig config;
|
|
config.option<ConfigOptionStrings>("extruder_nozzle_stats", true)->values = {"Standard#1"};
|
|
REQUIRE(config.get_extruder_nozzle_volume_count(2, nozzle_volume_types) == 2);
|
|
REQUIRE(nozzle_volume_types[0].empty());
|
|
REQUIRE(nozzle_volume_types[1].empty());
|
|
}
|
|
|
|
SECTION("single volume type per extruder counts one slot each") {
|
|
DynamicPrintConfig config;
|
|
config.option<ConfigOptionStrings>("extruder_nozzle_stats", true)->values = {"Standard#1", "High Flow#1"};
|
|
REQUIRE(config.get_extruder_nozzle_volume_count(2, nozzle_volume_types) == 2);
|
|
REQUIRE(nozzle_volume_types[0] == std::vector<NozzleVolumeType>{nvtStandard});
|
|
REQUIRE(nozzle_volume_types[1] == std::vector<NozzleVolumeType>{nvtHighFlow});
|
|
}
|
|
|
|
SECTION("a mixed-nozzle extruder contributes one slot per volume type, ascending enum order") {
|
|
DynamicPrintConfig config;
|
|
// list High Flow first in the token string: parsing must still order Standard before High Flow
|
|
config.option<ConfigOptionStrings>("extruder_nozzle_stats", true)->values = {"Standard#3", "High Flow#3|Standard#3"};
|
|
REQUIRE(config.get_extruder_nozzle_volume_count(2, nozzle_volume_types) == 3);
|
|
REQUIRE(nozzle_volume_types[0] == std::vector<NozzleVolumeType>{nvtStandard});
|
|
REQUIRE(nozzle_volume_types[1] == std::vector<NozzleVolumeType>({nvtStandard, nvtHighFlow}));
|
|
}
|
|
}
|
|
|
|
TEST_CASE("update_values_to_printer_extruders expands one slot per (extruder x volume type)", "[Config]")
|
|
{
|
|
SECTION("Hybrid extruder yields three slots, extruder-ascending then volume-ascending") {
|
|
DynamicPrintConfig config = make_hybrid_printer_config();
|
|
add_print_variant_columns(config);
|
|
|
|
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
|
|
int extruder_count = 2;
|
|
int count = config.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
|
|
REQUIRE(count == 3);
|
|
|
|
std::vector<int> variant_index = config.update_values_to_printer_extruders(config, extruder_count, count, nozzle_volume_types,
|
|
print_options_with_variant, "print_extruder_id", "print_extruder_variant");
|
|
|
|
REQUIRE(variant_index == std::vector<int>({0, 2, 3}));
|
|
REQUIRE(config.option<ConfigOptionFloats>("outer_wall_speed")->values == std::vector<double>({30., 50., 500.}));
|
|
REQUIRE(config.option<ConfigOptionInts>("print_extruder_id")->values == std::vector<int>({1, 2, 2}));
|
|
REQUIRE(config.option<ConfigOptionStrings>("print_extruder_variant")->values ==
|
|
std::vector<std::string>({"Direct Drive Standard", "Direct Drive Standard", "Direct Drive High Flow"}));
|
|
}
|
|
|
|
SECTION("stride-2 options keep (normal, silent) pairs together per slot") {
|
|
DynamicPrintConfig config = make_hybrid_printer_config();
|
|
config.option<ConfigOptionInts>("printer_extruder_id", true)->values = {1, 1, 2, 2};
|
|
config.option<ConfigOptionStrings>("printer_extruder_variant", true)->values = {"Direct Drive Standard", "Direct Drive High Flow",
|
|
"Direct Drive Standard", "Direct Drive High Flow"};
|
|
config.option<ConfigOptionFloats>("machine_max_speed_x", true)->values = {100., 50., 110., 55., 120., 60., 130., 65.};
|
|
|
|
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
|
|
int extruder_count = 2;
|
|
int count = config.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
|
|
|
|
std::vector<int> variant_index = config.update_values_to_printer_extruders(config, extruder_count, count, nozzle_volume_types,
|
|
printer_options_with_variant_2, "printer_extruder_id", "printer_extruder_variant", 2);
|
|
|
|
REQUIRE(variant_index == std::vector<int>({0, 2, 3}));
|
|
REQUIRE(config.option<ConfigOptionFloats>("machine_max_speed_x")->values ==
|
|
std::vector<double>({100., 50., 120., 60., 130., 65.}));
|
|
}
|
|
|
|
SECTION("single-slot expansion on a Hybrid extruder resolves via the filament volume type") {
|
|
DynamicPrintConfig printer_config = make_hybrid_printer_config();
|
|
|
|
DynamicPrintConfig filament_config;
|
|
filament_config.option<ConfigOptionStrings>("filament_extruder_variant", true)->values = {"Direct Drive Standard", "Direct Drive High Flow"};
|
|
filament_config.option<ConfigOptionFloats>("filament_max_volumetric_speed", true)->values = {12., 20.};
|
|
|
|
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
|
|
int extruder_count = 2;
|
|
int count = printer_config.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
|
|
|
|
SECTION("default filament volume type selects the Standard column") {
|
|
std::vector<int> variant_index = filament_config.update_values_to_printer_extruders(printer_config, extruder_count, count,
|
|
nozzle_volume_types, filament_options_with_variant, "", "filament_extruder_variant", 1, 2);
|
|
REQUIRE(variant_index == std::vector<int>({0}));
|
|
REQUIRE(filament_config.option<ConfigOptionFloats>("filament_max_volumetric_speed")->values == std::vector<double>({12.}));
|
|
}
|
|
|
|
SECTION("a High Flow filament volume type selects the High Flow column") {
|
|
std::vector<int> variant_index = filament_config.update_values_to_printer_extruders(printer_config, extruder_count, count,
|
|
nozzle_volume_types, filament_options_with_variant, "", "filament_extruder_variant", 1, 2, nvtHighFlow);
|
|
REQUIRE(variant_index == std::vector<int>({1}));
|
|
REQUIRE(filament_config.option<ConfigOptionFloats>("filament_max_volumetric_speed")->values == std::vector<double>({20.}));
|
|
}
|
|
}
|
|
|
|
SECTION("an extruder without per-type stats does not overrun the slot table when another is Hybrid") {
|
|
DynamicPrintConfig config;
|
|
// e0 carries no per-type stats (empty entry), so the summed volume-type count (2) does
|
|
// not exceed the extruder count even though the Hybrid e1 emits one slot per volume type.
|
|
config.option<ConfigOptionStrings>("extruder_nozzle_stats", true)->values = {"", "Standard#3|High Flow#3"};
|
|
config.option<ConfigOptionEnumsGeneric>("extruder_type", true)->values = {etDirectDrive, etDirectDrive};
|
|
config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type", true)->values = {nvtStandard, nvtHybrid};
|
|
config.option<ConfigOptionStrings>("extruder_variant_list", true)->values = {"Direct Drive Standard,Direct Drive High Flow",
|
|
"Direct Drive Standard,Direct Drive High Flow"};
|
|
add_print_variant_columns(config);
|
|
|
|
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
|
|
int extruder_count = 2;
|
|
int count = config.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
|
|
REQUIRE(count == 2);
|
|
REQUIRE(nozzle_volume_types[0].empty());
|
|
|
|
std::vector<int> variant_index = config.update_values_to_printer_extruders(config, extruder_count, count, nozzle_volume_types,
|
|
print_options_with_variant, "print_extruder_id", "print_extruder_variant");
|
|
|
|
// e0 resolves by its configured type; the Hybrid e1 emits one slot per stats volume type
|
|
REQUIRE(variant_index == std::vector<int>({0, 2, 3}));
|
|
REQUIRE(config.option<ConfigOptionFloats>("outer_wall_speed")->values == std::vector<double>({30., 50., 500.}));
|
|
}
|
|
|
|
SECTION("without Hybrid or extra slots the expansion matches the per-extruder resolution") {
|
|
DynamicPrintConfig config;
|
|
config.option<ConfigOptionEnumsGeneric>("extruder_type", true)->values = {etDirectDrive, etDirectDrive};
|
|
config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type", true)->values = {nvtStandard, nvtHighFlow};
|
|
config.option<ConfigOptionStrings>("extruder_variant_list", true)->values = {"Direct Drive Standard,Direct Drive High Flow",
|
|
"Direct Drive Standard,Direct Drive High Flow"};
|
|
add_print_variant_columns(config);
|
|
|
|
// compute what the per-extruder loop resolves directly, before the arrays are rewritten
|
|
std::vector<int> expected_index;
|
|
for (int e_index = 0; e_index < 2; e_index++)
|
|
expected_index.push_back(config.get_index_for_extruder(e_index + 1, "print_extruder_id", etDirectDrive,
|
|
e_index == 0 ? nvtStandard : nvtHighFlow, "print_extruder_variant"));
|
|
REQUIRE(expected_index == std::vector<int>({0, 3}));
|
|
|
|
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
|
|
int extruder_count = 2;
|
|
int count = config.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
|
|
REQUIRE(count == 2);
|
|
|
|
std::vector<int> variant_index = config.update_values_to_printer_extruders(config, extruder_count, count, nozzle_volume_types,
|
|
print_options_with_variant, "print_extruder_id", "print_extruder_variant");
|
|
|
|
REQUIRE(variant_index == expected_index);
|
|
REQUIRE(config.option<ConfigOptionFloats>("outer_wall_speed")->values == std::vector<double>({30., 500.}));
|
|
}
|
|
}
|
|
|
|
TEST_CASE("update_values_to_printer_extruders synthesizes degenerate process variant columns", "[Config]")
|
|
{
|
|
// Non-BBL process presets and 3mf project configs keep the length-1 defaults for
|
|
// print_extruder_id/print_extruder_variant; only BBL system presets ship full-width columns.
|
|
auto add_degenerate_print_columns = [](DynamicPrintConfig &config) {
|
|
config.option<ConfigOptionInts>("print_extruder_id", true)->values = {1};
|
|
config.option<ConfigOptionStrings>("print_extruder_variant", true)->values = {"Direct Drive Standard"};
|
|
config.option<ConfigOptionFloats>("outer_wall_speed", true)->values = {30.};
|
|
};
|
|
|
|
SECTION("a single-column pair on a multi-extruder machine expands to one column per extruder") {
|
|
DynamicPrintConfig config;
|
|
config.option<ConfigOptionEnumsGeneric>("extruder_type", true)->values = {etDirectDrive, etDirectDrive};
|
|
config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type", true)->values = {nvtStandard, nvtStandard};
|
|
config.option<ConfigOptionStrings>("extruder_variant_list", true)->values = {"Direct Drive Standard", "Direct Drive Standard"};
|
|
add_degenerate_print_columns(config);
|
|
|
|
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
|
|
int extruder_count = 2;
|
|
int count = config.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
|
|
|
|
std::vector<int> variant_index = config.update_values_to_printer_extruders(config, extruder_count, count, nozzle_volume_types,
|
|
print_options_with_variant, "print_extruder_id", "print_extruder_variant");
|
|
|
|
REQUIRE(variant_index == std::vector<int>({0, 1}));
|
|
REQUIRE(config.option<ConfigOptionInts>("print_extruder_id")->values == std::vector<int>({1, 2}));
|
|
REQUIRE(config.option<ConfigOptionStrings>("print_extruder_variant")->values ==
|
|
std::vector<std::string>({"Direct Drive Standard", "Direct Drive Standard"}));
|
|
// width-1 data arrays replicate their only column into every slot
|
|
REQUIRE(config.option<ConfigOptionFloats>("outer_wall_speed")->values == std::vector<double>({30., 30.}));
|
|
}
|
|
|
|
SECTION("a multi-variant list synthesizes one column per (extruder x variant)") {
|
|
DynamicPrintConfig config = make_hybrid_printer_config();
|
|
add_degenerate_print_columns(config);
|
|
|
|
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
|
|
int extruder_count = 2;
|
|
int count = config.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
|
|
REQUIRE(count == 3);
|
|
|
|
std::vector<int> variant_index = config.update_values_to_printer_extruders(config, extruder_count, count, nozzle_volume_types,
|
|
print_options_with_variant, "print_extruder_id", "print_extruder_variant");
|
|
|
|
// same slot resolution as the explicit BBL-style 4-column layout
|
|
REQUIRE(variant_index == std::vector<int>({0, 2, 3}));
|
|
REQUIRE(config.option<ConfigOptionInts>("print_extruder_id")->values == std::vector<int>({1, 2, 2}));
|
|
REQUIRE(config.option<ConfigOptionStrings>("print_extruder_variant")->values ==
|
|
std::vector<std::string>({"Direct Drive Standard", "Direct Drive Standard", "Direct Drive High Flow"}));
|
|
REQUIRE(config.option<ConfigOptionFloats>("outer_wall_speed")->values == std::vector<double>({30., 30., 30.}));
|
|
}
|
|
|
|
SECTION("a single-extruder single-column layout is not treated as degenerate") {
|
|
DynamicPrintConfig config;
|
|
config.option<ConfigOptionEnumsGeneric>("extruder_type", true)->values = {etDirectDrive};
|
|
config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type", true)->values = {nvtStandard};
|
|
config.option<ConfigOptionStrings>("extruder_variant_list", true)->values = {"Direct Drive Standard"};
|
|
add_degenerate_print_columns(config);
|
|
|
|
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(config, extruder_count, count, nozzle_volume_types,
|
|
print_options_with_variant, "print_extruder_id", "print_extruder_variant");
|
|
|
|
REQUIRE(config.option<ConfigOptionInts>("print_extruder_id")->values == std::vector<int>({1}));
|
|
REQUIRE(config.option<ConfigOptionFloats>("outer_wall_speed")->values == std::vector<double>({30.}));
|
|
}
|
|
|
|
SECTION("a second expansion leaves the synthesized layout unchanged") {
|
|
DynamicPrintConfig config;
|
|
config.option<ConfigOptionEnumsGeneric>("extruder_type", true)->values = {etDirectDrive, etDirectDrive};
|
|
config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type", true)->values = {nvtStandard, nvtStandard};
|
|
config.option<ConfigOptionStrings>("extruder_variant_list", true)->values = {"Direct Drive Standard", "Direct Drive Standard"};
|
|
add_degenerate_print_columns(config);
|
|
|
|
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
|
|
int extruder_count = 2;
|
|
int count = config.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
|
|
|
|
config.update_values_to_printer_extruders(config, extruder_count, count, nozzle_volume_types,
|
|
print_options_with_variant, "print_extruder_id", "print_extruder_variant");
|
|
DynamicPrintConfig once = config;
|
|
config.update_values_to_printer_extruders(config, extruder_count, count, nozzle_volume_types,
|
|
print_options_with_variant, "print_extruder_id", "print_extruder_variant");
|
|
|
|
REQUIRE(config.option<ConfigOptionInts>("print_extruder_id")->values ==
|
|
once.option<ConfigOptionInts>("print_extruder_id")->values);
|
|
REQUIRE(config.option<ConfigOptionStrings>("print_extruder_variant")->values ==
|
|
once.option<ConfigOptionStrings>("print_extruder_variant")->values);
|
|
REQUIRE(config.option<ConfigOptionFloats>("outer_wall_speed")->values ==
|
|
once.option<ConfigOptionFloats>("outer_wall_speed")->values);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("update_values_to_printer_extruders_for_multiple_filaments resolves per-filament slots", "[Config]")
|
|
{
|
|
auto make_filament_arrays = [](DynamicPrintConfig &config) {
|
|
config.option<ConfigOptionInts>("filament_self_index", true)->values = {1, 1, 2, 2};
|
|
config.option<ConfigOptionStrings>("filament_extruder_variant", true)->values = {"Direct Drive Standard", "Direct Drive High Flow",
|
|
"Direct Drive Standard", "Direct Drive High Flow"};
|
|
config.option<ConfigOptionFloats>("filament_max_volumetric_speed", true)->values = {12., 20., 13., 21.};
|
|
config.option<ConfigOptionFloats>("fan_max_speed", true)->values = {15., 60., 20., 80.};
|
|
config.option<ConfigOptionBools>("filament_multitool_ramming", true)->values = {false, true, false, true};
|
|
};
|
|
|
|
std::set<std::string> filament_keys = filament_options_with_variant;
|
|
filament_keys.insert("filament_self_index");
|
|
|
|
SECTION("filament_volume_map picks the concrete volume type on a Hybrid extruder") {
|
|
DynamicPrintConfig config = make_hybrid_printer_config();
|
|
make_filament_arrays(config);
|
|
config.option<ConfigOptionInts>("filament_map", true)->values = {2, 2};
|
|
config.option<ConfigOptionInts>("filament_volume_map", true)->values = {nvtStandard, nvtHighFlow};
|
|
|
|
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
|
|
int extruder_count = 2;
|
|
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., 21.}));
|
|
REQUIRE(config.option<ConfigOptionFloats>("fan_max_speed")->values == std::vector<double>({15., 80.}));
|
|
REQUIRE(config.option<ConfigOptionBools>("filament_multitool_ramming")->values == std::vector<unsigned char>({0, 1}));
|
|
REQUIRE(config.option<ConfigOptionStrings>("filament_extruder_variant")->values ==
|
|
std::vector<std::string>({"Direct Drive Standard", "Direct Drive High Flow"}));
|
|
REQUIRE(config.option<ConfigOptionInts>("filament_self_index")->values == std::vector<int>({1, 2}));
|
|
}
|
|
|
|
SECTION("a volume map not sized to the filament count is ignored") {
|
|
DynamicPrintConfig config = make_hybrid_printer_config();
|
|
make_filament_arrays(config);
|
|
config.option<ConfigOptionInts>("filament_map", true)->values = {2, 2};
|
|
// the registered default is a single-element vector; it must not override slot resolution
|
|
config.option<ConfigOptionInts>("filament_volume_map", true)->values = {nvtStandard};
|
|
|
|
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
|
|
int extruder_count = 2;
|
|
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");
|
|
|
|
// Hybrid resolves as Standard when no usable per-filament map exists
|
|
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}));
|
|
}
|
|
|
|
SECTION("a single-filament explicit assignment on a Hybrid extruder is honored") {
|
|
DynamicPrintConfig config = make_hybrid_printer_config();
|
|
config.option<ConfigOptionInts>("filament_self_index", true)->values = {1, 1};
|
|
config.option<ConfigOptionStrings>("filament_extruder_variant", true)->values = {"Direct Drive Standard", "Direct Drive High Flow"};
|
|
config.option<ConfigOptionFloats>("filament_max_volumetric_speed", true)->values = {12., 20.};
|
|
config.option<ConfigOptionInts>("filament_map", true)->values = {2};
|
|
// sized to the (single) filament count: the producers guarantee sizing, so a
|
|
// single-filament map is as trustworthy as any other and the explicit High Flow
|
|
// request must win over the Hybrid->Standard fallback
|
|
config.option<ConfigOptionInts>("filament_volume_map", true)->values = {nvtHighFlow};
|
|
|
|
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
|
|
int extruder_count = 2;
|
|
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>({20.}));
|
|
REQUIRE(config.option<ConfigOptionInts>("filament_self_index")->values == std::vector<int>({1}));
|
|
}
|
|
|
|
SECTION("without Hybrid or extra slots the volume map is not consulted") {
|
|
DynamicPrintConfig config;
|
|
config.option<ConfigOptionEnumsGeneric>("extruder_type", true)->values = {etDirectDrive, etDirectDrive};
|
|
config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type", true)->values = {nvtStandard, nvtHighFlow};
|
|
config.option<ConfigOptionStrings>("extruder_variant_list", true)->values = {"Direct Drive Standard,Direct Drive High Flow",
|
|
"Direct Drive Standard,Direct Drive High Flow"};
|
|
make_filament_arrays(config);
|
|
config.option<ConfigOptionInts>("filament_map", true)->values = {1, 2};
|
|
// sized to the filament count, but inert because no extruder exposes multiple volume types
|
|
config.option<ConfigOptionInts>("filament_volume_map", true)->values = {nvtHighFlow, nvtStandard};
|
|
|
|
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
|
|
int extruder_count = 2;
|
|
int count = config.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
|
|
REQUIRE(count == 2);
|
|
|
|
config.update_values_to_printer_extruders_for_multiple_filaments(config, extruder_count, count, filament_keys,
|
|
"filament_self_index", "filament_extruder_variant");
|
|
|
|
// filament 1 keeps its extruder's Standard column, filament 2 its extruder's High Flow column
|
|
REQUIRE(config.option<ConfigOptionFloats>("filament_max_volumetric_speed")->values == std::vector<double>({12., 21.}));
|
|
REQUIRE(config.option<ConfigOptionInts>("filament_self_index")->values == std::vector<int>({1, 2}));
|
|
}
|
|
|
|
SECTION("a variant option shorter than the filament slots keeps its first value instead of zero") {
|
|
DynamicPrintConfig config;
|
|
config.option<ConfigOptionEnumsGeneric>("extruder_type", true)->values = {etDirectDrive, etDirectDrive};
|
|
config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type", true)->values = {nvtStandard, nvtHighFlow};
|
|
config.option<ConfigOptionStrings>("extruder_variant_list", true)->values = {"Direct Drive Standard,Direct Drive High Flow",
|
|
"Direct Drive Standard,Direct Drive High Flow"};
|
|
make_filament_arrays(config);
|
|
config.option<ConfigOptionInts>("filament_map", true)->values = {1, 2};
|
|
// no loaded preset carries the key, so only its single registered default is present
|
|
config.option<ConfigOptionFloatsNullable>("filament_cooling_before_tower", true)->values = {10.};
|
|
// only the first filament's two variant columns were loaded
|
|
config.option<ConfigOptionFloatsNullable>("filament_ramming_volumetric_speed", true)->values = {-1., -2.};
|
|
|
|
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
|
|
int extruder_count = 2;
|
|
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");
|
|
|
|
// filament 2 resolves to column 3 (its extruder's High Flow column), past the end of both vectors
|
|
REQUIRE_THAT(config.option<ConfigOptionFloatsNullable>("filament_cooling_before_tower")->values,
|
|
Catch::Matchers::Approx(std::vector<double>({10., 10.})));
|
|
REQUIRE_THAT(config.option<ConfigOptionFloatsNullable>("filament_ramming_volumetric_speed")->values,
|
|
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
|
|
// into a row taken from the destination PRINT preset, whose arrays are sized to its own
|
|
// print_extruder_variant. Those two widths disagree until the print preset is re-selected for the
|
|
// new printer -- Tab::load_current_preset() runs this migration first -- so a project authored on
|
|
// a single-variant printer, opened and switched to a wider one, wrote past the end of the row.
|
|
TEST_CASE("update_values_from_multi_to_multi_2 sizes the destination row to the variant count",
|
|
"[Config][VariantExpansion]")
|
|
{
|
|
const std::vector<std::string> src_variants{"Direct Drive Standard"};
|
|
const std::vector<std::string> dst_variants{"Direct Drive Standard", "Direct Drive High Flow",
|
|
"Direct Drive Standard", "Direct Drive High Flow"};
|
|
const std::set<std::string> keys{"outer_wall_speed"};
|
|
|
|
// The per-object override as authored on the single-variant printer.
|
|
const auto object_override = [] {
|
|
DynamicPrintConfig c;
|
|
c.option<ConfigOptionFloatsNullable>("outer_wall_speed", true)->values = {42.};
|
|
return c;
|
|
};
|
|
|
|
SECTION("a row narrower than the variant list is grown, not overrun") {
|
|
DynamicPrintConfig object_config = object_override();
|
|
DynamicPrintConfig dst;
|
|
dst.option<ConfigOptionFloatsNullable>("outer_wall_speed", true)->values = {200.};
|
|
|
|
REQUIRE(object_config.update_values_from_multi_to_multi_2(src_variants, dst_variants, dst, keys) == 0);
|
|
|
|
const auto& out = object_config.option<ConfigOptionFloatsNullable>("outer_wall_speed")->values;
|
|
REQUIRE(out.size() == dst_variants.size());
|
|
// Both "Direct Drive Standard" columns match the source variant, so they take the override.
|
|
CHECK(out[0] == Catch::Approx(42.));
|
|
CHECK(out[2] == Catch::Approx(42.));
|
|
// The High Flow columns have no matching source variant: nil, so the destination keeps
|
|
// tracking the print preset rather than being pinned to another variant's value.
|
|
CHECK(std::isnan(out[1]));
|
|
CHECK(std::isnan(out[3]));
|
|
}
|
|
|
|
// The regression guard: where the row already matches the variant list -- every case that was
|
|
// not corrupting the heap -- the resize is a no-op and the output is unchanged.
|
|
SECTION("a correctly sized row is untouched") {
|
|
DynamicPrintConfig object_config = object_override();
|
|
DynamicPrintConfig dst;
|
|
dst.option<ConfigOptionFloatsNullable>("outer_wall_speed", true)->values = {200., 500., 210., 510.};
|
|
|
|
REQUIRE(object_config.update_values_from_multi_to_multi_2(src_variants, dst_variants, dst, keys) == 0);
|
|
|
|
const auto& out = object_config.option<ConfigOptionFloatsNullable>("outer_wall_speed")->values;
|
|
REQUIRE(out.size() == 4);
|
|
CHECK(out[0] == Catch::Approx(42.)); // matched -> override
|
|
CHECK(out[1] == Catch::Approx(500.)); // unmatched -> preset value preserved
|
|
CHECK(out[2] == Catch::Approx(42.));
|
|
CHECK(out[3] == Catch::Approx(510.));
|
|
}
|
|
|
|
// is_nil(idx) indexes values[idx] with no bounds check, so a source shorter than its own
|
|
// variant list read out of range before the guard was added.
|
|
SECTION("a source shorter than its variant list is read in range") {
|
|
DynamicPrintConfig object_config = object_override(); // one value...
|
|
DynamicPrintConfig dst;
|
|
dst.option<ConfigOptionFloatsNullable>("outer_wall_speed", true)->values = {200., 500.};
|
|
|
|
REQUIRE(object_config.update_values_from_multi_to_multi_2(
|
|
{"Direct Drive Standard", "Direct Drive Standard"}, // ...but two source variants
|
|
{"Direct Drive Standard", "Direct Drive High Flow"}, dst, keys) == 0);
|
|
|
|
const auto& out = object_config.option<ConfigOptionFloatsNullable>("outer_wall_speed")->values;
|
|
REQUIRE(out.size() == 2);
|
|
CHECK(out[0] == Catch::Approx(42.));
|
|
CHECK(out[1] == Catch::Approx(500.));
|
|
}
|
|
|
|
SECTION("an empty destination variant list is refused") {
|
|
DynamicPrintConfig object_config = object_override();
|
|
DynamicPrintConfig dst;
|
|
dst.option<ConfigOptionFloatsNullable>("outer_wall_speed", true)->values = {200.};
|
|
|
|
CHECK(object_config.update_values_from_multi_to_multi_2(src_variants, {}, dst, keys) == -1);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("get_index_for_extruder returns -1 when no variant column matches the extruder", "[Config]")
|
|
{
|
|
DynamicPrintConfig config;
|
|
config.option<ConfigOptionInts>("print_extruder_id", true)->values = {1};
|
|
config.option<ConfigOptionStrings>("print_extruder_variant", true)->values = {"Direct Drive Standard"};
|
|
|
|
SECTION("the extruder that owns the column resolves to its slot") {
|
|
REQUIRE(config.get_index_for_extruder(1, "print_extruder_id", etDirectDrive, nvtStandard, "print_extruder_variant") == 0);
|
|
}
|
|
|
|
SECTION("an extruder with no matching column resolves to -1") {
|
|
REQUIRE(config.get_index_for_extruder(2, "print_extruder_id", etDirectDrive, nvtStandard, "print_extruder_variant") == -1);
|
|
}
|
|
}
|
|
|
|
// stride scales the returned slot so callers can address stride-2 options (machine_max_*, a
|
|
// Normal/Silent pair per column) by their pair's base slot. The printer Tab's extruder sync
|
|
// relies on this to copy the right slots on the Motion ability page.
|
|
TEST_CASE("get_index_for_extruder scales the variant column by the requested stride", "[Config]")
|
|
{
|
|
DynamicPrintConfig config;
|
|
config.option<ConfigOptionInts>("printer_extruder_id", true)->values = {1, 2};
|
|
config.option<ConfigOptionStrings>("printer_extruder_variant", true)->values = {"Direct Drive Standard",
|
|
"Direct Drive High Flow"};
|
|
|
|
// extruder 1 resolves to column 0, extruder 2 to column 1
|
|
const int col0_stride1 = config.get_index_for_extruder(1, "printer_extruder_id", etDirectDrive, nvtStandard,
|
|
"printer_extruder_variant", 1);
|
|
const int col1_stride1 = config.get_index_for_extruder(2, "printer_extruder_id", etDirectDrive, nvtHighFlow,
|
|
"printer_extruder_variant", 1);
|
|
REQUIRE(col0_stride1 == 0);
|
|
REQUIRE(col1_stride1 == 1);
|
|
|
|
// stride 2 returns exactly twice the stride-1 index (the pair's base slot)
|
|
const int col0_stride2 = config.get_index_for_extruder(1, "printer_extruder_id", etDirectDrive, nvtStandard,
|
|
"printer_extruder_variant", 2);
|
|
const int col1_stride2 = config.get_index_for_extruder(2, "printer_extruder_id", etDirectDrive, nvtHighFlow,
|
|
"printer_extruder_variant", 2);
|
|
REQUIRE(col0_stride2 == col0_stride1 * 2);
|
|
REQUIRE(col1_stride2 == col1_stride1 * 2);
|
|
REQUIRE(col0_stride2 == 0);
|
|
REQUIRE(col1_stride2 == 2);
|
|
}
|
|
|
|
// A per-variant filament option read with a single value gives it to every filament variant. A project
|
|
// exported by an older CLI holds a single value for an option no loaded preset defined, such as
|
|
// filament_ironing_flow.
|
|
TEST_CASE("A per-variant filament option read with a single value gives it to every filament variant", "[Config]")
|
|
{
|
|
// filament 1 defines Standard and High Flow, filament 2 Standard
|
|
DynamicPrintConfig config;
|
|
config.option<ConfigOptionInts>("filament_self_index", true)->values = {1, 1, 2};
|
|
config.load_from_ini_string("pressure_advance = 0.021", ForwardCompatibilitySubstitutionRule::Disable);
|
|
REQUIRE(config.option<ConfigOptionFloats>("pressure_advance")->values == std::vector<double>({0.021, 0.021, 0.021}));
|
|
}
|
|
|
|
// The device drying options hold several values per filament, as many as each filament preset gives.
|
|
TEST_CASE("The device drying options are rebuilt as each filament's values in slot order", "[Config]")
|
|
{
|
|
DynamicPrintConfig two_values, one_value, no_value;
|
|
two_values.option<ConfigOptionStrings>("filament_dev_ams_drying_ams_limitations", true)->values = {"1", "0"};
|
|
two_values.option<ConfigOptionFloats>("filament_dev_ams_drying_temperature", true)->values = {45., 45., 55., 55.};
|
|
one_value.option<ConfigOptionStrings>("filament_dev_ams_drying_ams_limitations", true)->values = {"1"};
|
|
one_value.option<ConfigOptionFloats>("filament_dev_ams_drying_temperature", true)->values = {65., 65., 75., 75.};
|
|
|
|
// values a project stored for three other filaments
|
|
DynamicPrintConfig config;
|
|
config.option<ConfigOptionStrings>("filament_dev_ams_drying_ams_limitations", true)->values = {"0", "0", "0"};
|
|
config.option<ConfigOptionFloats>("filament_dev_chamber_drying_time", true)->values = {12., 8., 12.};
|
|
|
|
set_filament_dev_options(config, {&two_values, &one_value, &two_values});
|
|
REQUIRE(config.option<ConfigOptionStrings>("filament_dev_ams_drying_ams_limitations")->values ==
|
|
std::vector<std::string>({"1", "0", "1", "1", "0"}));
|
|
REQUIRE(config.option<ConfigOptionFloats>("filament_dev_ams_drying_temperature")->values ==
|
|
std::vector<double>({45., 45., 55., 55., 65., 65., 75., 75., 45., 45., 55., 55.}));
|
|
// an option no filament defines keeps the stored values
|
|
REQUIRE(config.option<ConfigOptionFloats>("filament_dev_chamber_drying_time")->values == std::vector<double>({12., 8., 12.}));
|
|
|
|
// a filament without the option takes the option's default
|
|
set_filament_dev_options(config, {&two_values, &no_value});
|
|
REQUIRE(config.option<ConfigOptionStrings>("filament_dev_ams_drying_ams_limitations")->values == std::vector<std::string>({"1", "0", ""}));
|
|
}
|