feat(config): keep per-(extruder x volume-type) slots in variant expansion

- get_extruder_nozzle_volume_count derives per-extruder volume-type slot
  lists from extruder_nozzle_stats (absent stats = one slot per extruder)
- update_values_to_printer_extruders learns the slot layout: when any
  extruder mixes volume types, option arrays keep one slot per
  (extruder x volume type), extruder-ascending then volume-ascending;
  single-slot resolution takes the filament's volume type on mixed
  extruders
- update_values_to_printer_extruders_for_multiple_filaments applies a
  per-filament nozzle_volume_type override from filament_volume_map
  (when sized to the filament count) and remaps filament_self_index
  through the same pipeline as every other filament key
- get_config_index_base + is_auto_filament_map_mode helpers (consumers
  land with the per-filament config-index resolvers)
- callers updated: PresetBundle composition paths, PrintApply (counts
  hoisted above the extruder_applied guard), Print write-back
- new tests: slot counting, Hybrid slot expansion incl. stride 2,
  per-filament override, non-Hybrid degeneracy

Non-Hybrid printers keep their variant layout and values (proven by a
19-fixture byte gate; the only header delta is filament_self_index now
flowing through the same variant pipeline as its sibling filament
keys). Hybrid slices grow the config-block variant arrays to one entry
per sub-nozzle volume type; motion g-code is unchanged until the
g-code writer consumes the new slots.
This commit is contained in:
SoftFever
2026-07-11 00:51:10 +08:00
parent 7c8d086d9e
commit 13cd8d4864
7 changed files with 631 additions and 197 deletions

View File

@@ -120,17 +120,34 @@ DynamicPrintConfig PresetBundle::construct_full_config(
inherits.emplace_back(print_inherits);
// BBS: update printer config related with variants
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
int extruder_count = 1, extruder_volume_type_count = 1;
bool different_extruder = false;
if (apply_extruder) {
out.update_values_to_printer_extruders(out, printer_options_with_variant_1, "printer_extruder_id", "printer_extruder_variant");
out.update_values_to_printer_extruders(out, printer_options_with_variant_2, "printer_extruder_id", "printer_extruder_variant", 2);
// update print config related with variants
out.update_values_to_printer_extruders(out, print_options_with_variant, "print_extruder_id", "print_extruder_variant");
different_extruder = out.support_different_extruders(extruder_count);
extruder_volume_type_count = out.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
if ((extruder_count > 1) || different_extruder) {
// Orca: keep processing variant_1 before variant_2 here; variant_2 slots are resolved
// against the printer id/variant lists as rewritten by the variant_1 pass, and the
// composed values depend on that order. Note the order is load-bearing, not correct
// in general: the variant_2 pass reads the original full-width arrays through indices
// resolved on the shrunk lists, which mis-reads presets whose variant_2 columns differ
// per variant (e.g. X2D machine_max_speed_e/machine_max_acceleration_e). The slicing
// path composes variant_2 first and is unaffected; changing the order here would alter
// long-standing composed values, so any fix must re-baseline them.
out.update_values_to_printer_extruders(out, extruder_count, extruder_volume_type_count, nozzle_volume_types, printer_options_with_variant_1, "printer_extruder_id", "printer_extruder_variant");
out.update_values_to_printer_extruders(out, extruder_count, extruder_volume_type_count, nozzle_volume_types, printer_options_with_variant_2, "printer_extruder_id", "printer_extruder_variant", 2);
// update print config related with variants
out.update_values_to_printer_extruders(out, extruder_count, extruder_volume_type_count, nozzle_volume_types, print_options_with_variant, "print_extruder_id", "print_extruder_variant");
}
}
if (num_filaments <= 1) {
// BBS: update filament config related with variants
DynamicPrintConfig filament_config = in_filament_presets[0].config;
if (apply_extruder) filament_config.update_values_to_printer_extruders(out, filament_options_with_variant, "", "filament_extruder_variant", 1, filament_maps[0]);
if (apply_extruder && ((extruder_count > 1) || different_extruder))
filament_config.update_values_to_printer_extruders(out, extruder_count, extruder_volume_type_count, nozzle_volume_types, filament_options_with_variant, "", "filament_extruder_variant", 1, filament_maps[0]);
out.apply(filament_config);
compatible_printers_condition.emplace_back(in_filament_presets[0].compatible_printers_condition());
compatible_prints_condition.emplace_back(in_filament_presets[0].compatible_prints_condition());
@@ -153,8 +170,8 @@ DynamicPrintConfig PresetBundle::construct_full_config(
filament_temp_configs.resize(num_filaments);
for (size_t i = 0; i < num_filaments; ++i) {
filament_temp_configs[i] = *(filament_configs[i]);
if (apply_extruder)
filament_temp_configs[i].update_values_to_printer_extruders(out, filament_options_with_variant, "", "filament_extruder_variant", 1, filament_maps[i]);
if (apply_extruder && ((extruder_count > 1) || different_extruder))
filament_temp_configs[i].update_values_to_printer_extruders(out, extruder_count, extruder_volume_type_count, nozzle_volume_types, filament_options_with_variant, "", "filament_extruder_variant", 1, filament_maps[i]);
}
// loop through options and apply them to the resulting config.
@@ -3979,18 +3996,34 @@ DynamicPrintConfig PresetBundle::full_fff_config(bool apply_extruder, std::optio
different_settings.emplace_back(different_print_settings);
//BBS: update printer config related with variants
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
int extruder_count = 1, extruder_volume_type_count = 1;
bool different_extruder = false;
if (apply_extruder) {
out.update_values_to_printer_extruders(out, printer_options_with_variant_1, "printer_extruder_id", "printer_extruder_variant");
out.update_values_to_printer_extruders(out, printer_options_with_variant_2, "printer_extruder_id", "printer_extruder_variant", 2);
//update print config related with variants
out.update_values_to_printer_extruders(out, print_options_with_variant, "print_extruder_id", "print_extruder_variant");
different_extruder = out.support_different_extruders(extruder_count);
extruder_volume_type_count = out.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
if ((extruder_count > 1) || different_extruder) {
// Orca: keep processing variant_1 before variant_2 here; variant_2 slots are resolved
// against the printer id/variant lists as rewritten by the variant_1 pass, and the
// composed values depend on that order. Note the order is load-bearing, not correct
// in general: the variant_2 pass reads the original full-width arrays through indices
// resolved on the shrunk lists, which mis-reads presets whose variant_2 columns differ
// per variant (e.g. X2D machine_max_speed_e/machine_max_acceleration_e). The slicing
// path composes variant_2 first and is unaffected; changing the order here would alter
// long-standing composed values, so any fix must re-baseline them.
out.update_values_to_printer_extruders(out, extruder_count, extruder_volume_type_count, nozzle_volume_types, printer_options_with_variant_1, "printer_extruder_id", "printer_extruder_variant");
out.update_values_to_printer_extruders(out, extruder_count, extruder_volume_type_count, nozzle_volume_types, printer_options_with_variant_2, "printer_extruder_id", "printer_extruder_variant", 2);
//update print config related with variants
out.update_values_to_printer_extruders(out, extruder_count, extruder_volume_type_count, nozzle_volume_types, print_options_with_variant, "print_extruder_id", "print_extruder_variant");
}
}
if (num_filaments <= 1) {
//BBS: update filament config related with variants
DynamicPrintConfig filament_config = this->filaments.get_edited_preset().config;
if (apply_extruder)
filament_config.update_values_to_printer_extruders(out, filament_options_with_variant, "", "filament_extruder_variant", 1, filament_maps[0]);
if (apply_extruder && ((extruder_count > 1) || different_extruder))
filament_config.update_values_to_printer_extruders(out, extruder_count, extruder_volume_type_count, nozzle_volume_types, filament_options_with_variant, "", "filament_extruder_variant", 1, filament_maps[0]);
out.apply(filament_config);
compatible_printers_condition.emplace_back(this->filaments.get_edited_preset().compatible_printers_condition());
compatible_prints_condition .emplace_back(this->filaments.get_edited_preset().compatible_prints_condition());
@@ -4083,8 +4116,8 @@ DynamicPrintConfig PresetBundle::full_fff_config(bool apply_extruder, std::optio
filament_temp_configs.resize(num_filaments);
for (size_t i = 0; i < num_filaments; ++i) {
filament_temp_configs[i] = *(filament_configs[i]);
if (apply_extruder)
filament_temp_configs[i].update_values_to_printer_extruders(out, filament_options_with_variant, "", "filament_extruder_variant", 1, filament_maps[i]);
if (apply_extruder && ((extruder_count > 1) || different_extruder))
filament_temp_configs[i].update_values_to_printer_extruders(out, extruder_count, extruder_volume_type_count, nozzle_volume_types, filament_options_with_variant, "", "filament_extruder_variant", 1, filament_maps[i]);
}
// loop through options and apply them to the resulting config.

View File

@@ -3206,8 +3206,16 @@ void Print::update_filament_maps_to_config(std::vector<int> f_maps)
m_ori_full_print_config.option<ConfigOptionInts>("filament_map", true)->values = f_maps;
m_config.filament_map.values = f_maps;
int extruder_count = 1, extruder_volume_type_count = 1;
bool support_multi = m_ori_full_print_config.support_different_extruders(extruder_count);
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
extruder_volume_type_count = m_ori_full_print_config.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
m_full_print_config = m_ori_full_print_config;
m_full_print_config.update_values_to_printer_extruders_for_multiple_filaments(m_full_print_config, filament_options_with_variant, "filament_self_index", "filament_extruder_variant");
std::set<std::string> filament_keys = filament_options_with_variant;
filament_keys.insert("filament_self_index");
if ((extruder_count > 1) || support_multi)
m_full_print_config.update_values_to_printer_extruders_for_multiple_filaments(m_full_print_config, extruder_count, extruder_volume_type_count, filament_keys, "filament_self_index", "filament_extruder_variant");
const std::vector<std::string> &extruder_retract_keys = print_config_def.extruder_retract_keys();
const std::string filament_prefix = "filament_";

View File

@@ -1162,15 +1162,28 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
}
//apply extruder related values
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
int extruder_count = 1, extruder_volume_type_count = 1;
bool different_extruder = false;
different_extruder = new_full_config.support_different_extruders(extruder_count);
extruder_volume_type_count = new_full_config.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
if (!extruder_applied) {
// variant_2 must be processed first, because variant_1 will make `printer_extruder_id` and `printer_extruder_variant` half of the size that makes `get_index_for_extruder` no longer work properly
new_full_config.update_values_to_printer_extruders(new_full_config, printer_options_with_variant_2, "printer_extruder_id", "printer_extruder_variant", 2);
new_full_config.update_values_to_printer_extruders(new_full_config, printer_options_with_variant_1, "printer_extruder_id", "printer_extruder_variant");
//update print config related with variants
new_full_config.update_values_to_printer_extruders(new_full_config, print_options_with_variant, "print_extruder_id", "print_extruder_variant");
if ((extruder_count > 1) || different_extruder) {
// variant_2 must be processed first, because variant_1 will make `printer_extruder_id` and `printer_extruder_variant` half of the size that makes `get_index_for_extruder` no longer work properly
new_full_config.update_values_to_printer_extruders(new_full_config, extruder_count, extruder_volume_type_count, nozzle_volume_types, printer_options_with_variant_2, "printer_extruder_id", "printer_extruder_variant", 2);
new_full_config.update_values_to_printer_extruders(new_full_config, extruder_count, extruder_volume_type_count, nozzle_volume_types, printer_options_with_variant_1, "printer_extruder_id", "printer_extruder_variant");
//update print config related with variants
new_full_config.update_values_to_printer_extruders(new_full_config, extruder_count, extruder_volume_type_count, nozzle_volume_types, print_options_with_variant, "print_extruder_id", "print_extruder_variant");
}
m_ori_full_print_config = new_full_config;
new_full_config.update_values_to_printer_extruders_for_multiple_filaments(new_full_config, filament_options_with_variant, "filament_self_index", "filament_extruder_variant");
std::set<std::string> filament_keys = filament_options_with_variant;
filament_keys.insert("filament_self_index");
if ((extruder_count > 1) || different_extruder)
new_full_config.update_values_to_printer_extruders_for_multiple_filaments(m_ori_full_print_config, extruder_count, extruder_volume_type_count, filament_keys,
"filament_self_index", "filament_extruder_variant");
}
// else {
// int extruder_count;
@@ -1199,7 +1212,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
if (print_diff_set.find("filament_map_mode") == print_diff_set.end())
{
FilamentMapMode map_mode = new_full_config.option<ConfigOptionEnum<FilamentMapMode>>("filament_map_mode", true)->value;
if (map_mode < fmmManual) {
if (is_auto_filament_map_mode(map_mode)) {
if (print_diff_set.find("filament_map") != print_diff_set.end()) {
print_diff_set.erase("filament_map");
//full_config_diff.erase("filament_map");

View File

@@ -650,6 +650,16 @@ std::string get_extruder_variant_string(ExtruderType extruder_type, NozzleVolume
return variant_string;
}
int get_config_index_base(NozzleVolumeType volume_type, ExtruderType extruder_type, int variant_id_1based, const std::vector<std::string>& variant_list, const std::vector<int>& variant_ids_1based)
{
assert(variant_list.size() == variant_ids_1based.size());
std::string extruder_variant = get_extruder_variant_string(extruder_type, volume_type);
for (int index = 0; index < int(variant_list.size()); ++index) {
if (extruder_variant == variant_list[index] && variant_ids_1based[index] == variant_id_1based) { return index; }
}
return 0;
}
std::string get_nozzle_volume_type_string(NozzleVolumeType nozzle_volume_type)
{
if (nozzle_volume_type > nvtMaxNozzleVolumeType) {
@@ -10283,184 +10293,229 @@ DynamicPrintConfig::get_filament_type() const
return std::string();
}
void DynamicPrintConfig::update_values_to_printer_extruders(DynamicPrintConfig& printer_config, std::set<std::string>& key_set, std::string id_name, std::string variant_name, unsigned int stride, unsigned int extruder_id)
int DynamicPrintConfig::get_extruder_nozzle_volume_count(int extruder_count, std::vector<std::vector<NozzleVolumeType>>& nozzle_volume_types) const
{
int extruder_count;
bool different_extruder = printer_config.support_different_extruders(extruder_count);
if ((extruder_count > 1) || different_extruder)
{
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", Line %1%: different extruders processing")%__LINE__;
//apply process settings
//auto opt_nozzle_diameters = this->option<ConfigOptionFloats>("nozzle_diameter");
//int extruder_count = opt_nozzle_diameters->size();
auto opt_extruder_type = dynamic_cast<const ConfigOptionEnumsGeneric*>(printer_config.option("extruder_type"));
auto opt_nozzle_volume_type = dynamic_cast<const ConfigOptionEnumsGeneric*>(printer_config.option("nozzle_volume_type"));
if (!opt_extruder_type || !opt_nozzle_volume_type) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: extruder_type or nozzle_volume_type option not found, skipping")%__LINE__;
return;
}
std::vector<int> variant_index;
if (extruder_id > 0 && extruder_id <= static_cast<unsigned> (extruder_count)) {
variant_index.resize(1);
ExtruderType extruder_type = (ExtruderType)(opt_extruder_type->get_at(extruder_id - 1));
NozzleVolumeType nozzle_volume_type = (NozzleVolumeType)(opt_nozzle_volume_type->get_at(extruder_id - 1));
//variant index
variant_index[0] = get_index_for_extruder(extruder_id, id_name, extruder_type, nozzle_volume_type, variant_name);
if (variant_index[0] < 0) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", Line %1%: could not found extruder_type %2%, nozzle_volume_type %3%, for filament")
% __LINE__ % s_keys_names_ExtruderType[extruder_type] % s_keys_names_NozzleVolumeType[nozzle_volume_type];
assert(false);
}
extruder_count = 1;
}
else {
variant_index.resize(extruder_count);
for (int e_index = 0; e_index < extruder_count; e_index++)
{
ExtruderType extruder_type = (ExtruderType)(opt_extruder_type->get_at(e_index));
NozzleVolumeType nozzle_volume_type = (NozzleVolumeType)(opt_nozzle_volume_type->get_at(e_index));
//variant index
variant_index[e_index] = get_index_for_extruder(e_index+1, id_name, extruder_type, nozzle_volume_type, variant_name);
if (variant_index[e_index] < 0) {
// Orca: This is expected during transient UI states (e.g. popup windows),
// fall back to 0 silently.
variant_index[e_index] = 0;
}
}
}
const ConfigDef *config_def = this->def();
if (!config_def) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", Line %1%: can not find config define")%__LINE__;
return;
}
for (auto& key: key_set)
int count = extruder_count;
auto opt_extruder_nozzle_stats = dynamic_cast<const ConfigOptionStrings*>(this->option("extruder_nozzle_stats"));
nozzle_volume_types.resize(extruder_count, std::vector<NozzleVolumeType>{});
if (opt_extruder_nozzle_stats && (int(opt_extruder_nozzle_stats->values.size()) == extruder_count)) {
std::vector<std::map<NozzleVolumeType, int>> extruder_nozzle_counts = get_extruder_nozzle_stats(opt_extruder_nozzle_stats->values);
count = 0;
for (int i = 0; i < extruder_count; i++)
{
const ConfigOptionDef *optdef = config_def->get(key);
if (!optdef) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: can not find opt define for %2%")%__LINE__%key;
continue;
}
switch (optdef->type) {
case coStrings:
{
ConfigOptionStrings * opt = this->option<ConfigOptionStrings>(key);
std::vector<std::string> new_values;
new_values.resize(extruder_count * stride);
for (int e_index = 0; e_index < extruder_count; e_index++)
{
for (unsigned int i = 0; i < stride; i++)
new_values[e_index*stride + i] = opt->get_at(variant_index[e_index]*stride + i);
}
opt->values = new_values;
break;
}
case coInts:
{
ConfigOptionInts * opt = this->option<ConfigOptionInts>(key);
std::vector<int> new_values;
new_values.resize(extruder_count * stride);
for (int e_index = 0; e_index < extruder_count; e_index++)
{
for (unsigned int i = 0; i < stride; i++)
new_values[e_index*stride + i] = opt->get_at(variant_index[e_index]*stride + i);
}
opt->values = new_values;
break;
}
case coFloats:
{
ConfigOptionFloats * opt = this->option<ConfigOptionFloats>(key);
std::vector<double> new_values;
new_values.resize(extruder_count * stride);
for (int e_index = 0; e_index < extruder_count; e_index++)
{
for (unsigned int i = 0; i < stride; i++)
new_values[e_index*stride + i] = opt->get_at(variant_index[e_index]*stride + i);
}
opt->values = new_values;
break;
}
case coPercents:
{
ConfigOptionPercents * opt = this->option<ConfigOptionPercents>(key);
std::vector<double> new_values;
new_values.resize(extruder_count * stride);
for (int e_index = 0; e_index < extruder_count; e_index++)
{
for (unsigned int i = 0; i < stride; i++)
new_values[e_index*stride + i] = opt->get_at(variant_index[e_index]*stride + i);
}
opt->values = new_values;
break;
}
case coFloatsOrPercents:
{
ConfigOptionFloatsOrPercents * opt = this->option<ConfigOptionFloatsOrPercents>(key);
std::vector<FloatOrPercent> new_values;
new_values.resize(extruder_count * stride);
for (int e_index = 0; e_index < extruder_count; e_index++)
{
for (unsigned int i = 0; i < stride; i++)
new_values[e_index*stride + i] = opt->get_at(variant_index[e_index]*stride + i);
}
opt->values = new_values;
break;
}
case coBools:
{
ConfigOptionBools * opt = this->option<ConfigOptionBools>(key);
std::vector<unsigned char> new_values;
new_values.resize(extruder_count * stride);
for (int e_index = 0; e_index < extruder_count; e_index++)
{
for (unsigned int i = 0; i < stride; i++)
new_values[e_index*stride + i] = opt->get_at(variant_index[e_index]*stride + i);
}
opt->values = new_values;
break;
}
case coEnums:
{
ConfigOptionEnumsGeneric * opt = this->option<ConfigOptionEnumsGeneric>(key);
std::vector<int> new_values;
new_values.resize(extruder_count * stride);
for (int e_index = 0; e_index < extruder_count; e_index++)
{
for (unsigned int i = 0; i < stride; i++)
new_values[e_index*stride + i] = opt->get_at(variant_index[e_index]*stride + i);
}
opt->values = new_values;
break;
}
default:
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: unsupported option type for %2%")%__LINE__%key;
break;
}
count += extruder_nozzle_counts[i].size();
// std::map iteration order gives the canonical slot order: ascending NozzleVolumeType
for (auto& iter : extruder_nozzle_counts[i])
nozzle_volume_types[i].push_back(iter.first);
}
}
return count;
}
void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filaments(DynamicPrintConfig& printer_config, std::set<std::string>& key_set, std::string id_name, std::string variant_name)
std::vector<int> DynamicPrintConfig::update_values_to_printer_extruders(DynamicPrintConfig& printer_config, int extruder_count, int extruder_nozzle_volume_count, std::vector<std::vector<NozzleVolumeType>>& nv_types,
std::set<std::string>& key_set, std::string id_name, std::string variant_name, unsigned int stride, unsigned int extruder_id, NozzleVolumeType filament_nvt)
{
int extruder_count;
bool different_extruder = printer_config.support_different_extruders(extruder_count);
if ((extruder_count > 1) || different_extruder)
std::vector<int> variant_index;
int variant_count = extruder_count;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", Line %1%: extruder_count %2%, extruder_nozzle_volume_count %3%")%__LINE__ %extruder_count %extruder_nozzle_volume_count;
auto opt_extruder_type = dynamic_cast<const ConfigOptionEnumsGeneric*>(printer_config.option("extruder_type"));
auto opt_nozzle_volume_type = dynamic_cast<const ConfigOptionEnumsGeneric*>(printer_config.option("nozzle_volume_type"));
if (!opt_extruder_type || !opt_nozzle_volume_type) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: extruder_type or nozzle_volume_type option not found, skipping")%__LINE__;
return variant_index;
}
if (extruder_id > 0 && extruder_id <= static_cast<unsigned> (extruder_count)) {
variant_index.resize(1);
ExtruderType extruder_type = (ExtruderType)(opt_extruder_type->get_at(extruder_id - 1));
NozzleVolumeType nozzle_volume_type = (NozzleVolumeType)(opt_nozzle_volume_type->get_at(extruder_id - 1));
if (nozzle_volume_type == nvtHybrid) {
// a mixed-nozzle extruder has no preset column of its own: the filament's concrete
// volume type selects the slot
nozzle_volume_type = filament_nvt;
}
else if (nozzle_volume_type != filament_nvt) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__
<< boost::format(", Line %1%: nozzle_volume_type is %2%, not equal to filament_nvt %3%") % __LINE__ % nozzle_volume_type % filament_nvt;
}
//variant index
variant_index[0] = get_index_for_extruder(extruder_id, id_name, extruder_type, nozzle_volume_type, variant_name);
if (variant_index[0] < 0) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", Line %1%: could not found extruder_type %2%, nozzle_volume_type %3%, for filament")
% __LINE__ % s_keys_names_ExtruderType[extruder_type] % s_keys_names_NozzleVolumeType[nozzle_volume_type];
}
variant_count = 1;
}
else {
// Orca: emit the slots first, then size variant_count from what was actually
// emitted. extruder_nozzle_volume_count only equals the emitted total when every
// extruder carries per-type stats; an extruder with an empty stats entry combined
// with a Hybrid extruder would otherwise overrun a table pre-sized from that count.
for (int e_index = 0; e_index < extruder_count; e_index++)
{
ExtruderType extruder_type = (ExtruderType)(opt_extruder_type->get_at(e_index));
NozzleVolumeType nozzle_volume_type = (NozzleVolumeType)(opt_nozzle_volume_type->get_at(e_index));
// Orca: latch the decision before the inner loop reassigns nozzle_volume_type;
// re-testing the reassigned value would make every slot after the first reuse
// the first slot's volume type.
const bool per_type_slots = extruder_nozzle_volume_count > extruder_count || nozzle_volume_type == nvtHybrid;
const int nvt_count = per_type_slots ? int(nv_types[e_index].size()) : 1;
for (int nvt_index = 0; nvt_index < nvt_count; nvt_index++)
{
if (per_type_slots)
nozzle_volume_type = nv_types[e_index][nvt_index];
//variant index
int slot_index = get_index_for_extruder(e_index+1, id_name, extruder_type, nozzle_volume_type, variant_name);
if (slot_index < 0) {
// Orca: This is expected during transient UI states (e.g. popup windows),
// fall back to 0 silently.
slot_index = 0;
}
variant_index.push_back(slot_index);
}
}
variant_count = int(variant_index.size());
}
const ConfigDef *config_def = this->def();
if (!config_def) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", Line %1%: can not find config define")%__LINE__;
return variant_index;
}
for (auto& key: key_set)
{
const ConfigOptionDef *optdef = config_def->get(key);
if (!optdef) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: can not find opt define for %2%")%__LINE__%key;
continue;
}
switch (optdef->type) {
case coStrings:
{
ConfigOptionStrings * opt = this->option<ConfigOptionStrings>(key);
if (!opt) continue;
std::vector<std::string> new_values;
new_values.resize(variant_count * stride);
for (int e_index = 0; e_index < variant_count; e_index++)
{
for (unsigned int i = 0; i < stride; i++)
new_values[e_index*stride + i] = opt->get_at(variant_index[e_index]*stride + i);
}
opt->values = new_values;
break;
}
case coInts:
{
ConfigOptionInts * opt = this->option<ConfigOptionInts>(key);
if (!opt) continue;
std::vector<int> new_values;
new_values.resize(variant_count * stride);
for (int e_index = 0; e_index < variant_count; e_index++)
{
for (unsigned int i = 0; i < stride; i++)
new_values[e_index*stride + i] = opt->get_at(variant_index[e_index]*stride + i);
}
opt->values = new_values;
break;
}
case coFloats:
{
ConfigOptionFloats * opt = this->option<ConfigOptionFloats>(key);
if (!opt) continue;
std::vector<double> new_values;
new_values.resize(variant_count * stride);
for (int e_index = 0; e_index < variant_count; e_index++)
{
for (unsigned int i = 0; i < stride; i++)
new_values[e_index*stride + i] = opt->get_at(variant_index[e_index]*stride + i);
}
opt->values = new_values;
break;
}
case coPercents:
{
ConfigOptionPercents * opt = this->option<ConfigOptionPercents>(key);
if (!opt) continue;
std::vector<double> new_values;
new_values.resize(variant_count * stride);
for (int e_index = 0; e_index < variant_count; e_index++)
{
for (unsigned int i = 0; i < stride; i++)
new_values[e_index*stride + i] = opt->get_at(variant_index[e_index]*stride + i);
}
opt->values = new_values;
break;
}
case coFloatsOrPercents:
{
ConfigOptionFloatsOrPercents * opt = this->option<ConfigOptionFloatsOrPercents>(key);
if (!opt) continue;
std::vector<FloatOrPercent> new_values;
new_values.resize(variant_count * stride);
for (int e_index = 0; e_index < variant_count; e_index++)
{
for (unsigned int i = 0; i < stride; i++)
new_values[e_index*stride + i] = opt->get_at(variant_index[e_index]*stride + i);
}
opt->values = new_values;
break;
}
case coBools:
{
ConfigOptionBools * opt = this->option<ConfigOptionBools>(key);
if (!opt) continue;
std::vector<unsigned char> new_values;
new_values.resize(variant_count * stride);
for (int e_index = 0; e_index < variant_count; e_index++)
{
for (unsigned int i = 0; i < stride; i++)
new_values[e_index*stride + i] = opt->get_at(variant_index[e_index]*stride + i);
}
opt->values = new_values;
break;
}
case coEnums:
{
ConfigOptionEnumsGeneric * opt = this->option<ConfigOptionEnumsGeneric>(key);
if (!opt) continue;
std::vector<int> new_values;
new_values.resize(variant_count * stride);
for (int e_index = 0; e_index < variant_count; e_index++)
{
for (unsigned int i = 0; i < stride; i++)
new_values[e_index*stride + i] = opt->get_at(variant_index[e_index]*stride + i);
}
opt->values = new_values;
break;
}
default:
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: unsupported option type for %2%")%__LINE__%key;
break;
}
}
return variant_index;
}
void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filaments(DynamicPrintConfig& printer_config, int extruder_count, int extruder_nozzle_volume_count, std::set<std::string>& key_set, std::string id_name, std::string variant_name)
{
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", Line %1%: extruder_count %2%, extruder_nozzle_volume_count %3%")%__LINE__ %extruder_count %extruder_nozzle_volume_count;
{
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", Line %1%: extruder_count=%2%, different_extruder=%3%")%__LINE__ %extruder_count %different_extruder;
auto opt_filament_map = printer_config.option<ConfigOptionInts>("filament_map");
if (!opt_filament_map) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: filament_map option not found, skipping")%__LINE__;
@@ -10469,14 +10524,22 @@ void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filamen
std::vector<int> filament_maps = opt_filament_map->values;
size_t filament_count = filament_maps.size();
//apply process settings
//auto opt_nozzle_diameters = this->option<ConfigOptionFloats>("nozzle_diameter");
//int extruder_count = opt_nozzle_diameters->size();
auto opt_extruder_type = dynamic_cast<const ConfigOptionEnumsGeneric*>(printer_config.option("extruder_type"));
auto opt_nozzle_volume_type = dynamic_cast<const ConfigOptionEnumsGeneric*>(printer_config.option("nozzle_volume_type"));
if (!opt_extruder_type || !opt_nozzle_volume_type) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: extruder_type or nozzle_volume_type option not found, skipping")%__LINE__;
return;
}
auto opt_filament_volume_maps = dynamic_cast<const ConfigOptionInts*>(printer_config.option("filament_volume_map"));
std::vector<int> filament_volume_maps;
// Orca: the per-filament volume map is only trustworthy when a producer explicitly
// sized it to the filament count; the registered default is a 1-element vector and
// stale project values may be mis-sized. A single-filament config cannot be told
// apart from that default by size, so it is ignored too until the pipeline that
// seeds the map lands; anything untrusted must not distort slot resolution.
if (opt_filament_volume_maps && filament_count > 1 && opt_filament_volume_maps->values.size() == filament_count)
filament_volume_maps = opt_filament_volume_maps->values;
auto opt_ids = id_name.empty()? nullptr: dynamic_cast<const ConfigOptionInts*>(this->option(id_name));
std::vector<int> variant_index;
@@ -10487,6 +10550,10 @@ void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filamen
ExtruderType extruder_type = (ExtruderType)(opt_extruder_type->get_at(filament_maps[f_index] - 1));
NozzleVolumeType nozzle_volume_type = (NozzleVolumeType)(opt_nozzle_volume_type->get_at(filament_maps[f_index] - 1));
if ((extruder_nozzle_volume_count > extruder_count || nozzle_volume_type == nvtHybrid) && (!filament_volume_maps.empty())) {
nozzle_volume_type = (NozzleVolumeType)(filament_volume_maps[f_index]);
}
//variant index
variant_index[f_index] = get_index_for_extruder(f_index+1, id_name, extruder_type, nozzle_volume_type, variant_name);
if (variant_index[f_index] < 0) {
@@ -10511,8 +10578,11 @@ void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filamen
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", Line %1%: can not find config define")%__LINE__;
return;
}
bool has_id_key = !id_name.empty() && key_set.count(id_name) > 0;
for (auto& key: key_set)
{
if (has_id_key && key == id_name)
continue;
const ConfigOptionDef *optdef = config_def->get(key);
if (!optdef) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: can not find opt define for %2%")%__LINE__%key;
@@ -10672,6 +10742,16 @@ void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filamen
break;
}
}
// Orca: also require a non-empty id list; get_at on an empty vector is undefined.
if (has_id_key && opt_ids && !opt_ids->values.empty()) {
// remap the id list itself last: the lookups above still need the original ids
std::vector<int> new_values;
new_values.resize(filament_count);
for (int f_index = 0; f_index < filament_count; f_index++) {
new_values[f_index] = opt_ids->get_at(variant_index[f_index]);
}
const_cast<ConfigOptionInts*>(opt_ids)->values = new_values;
}
}
}

View File

@@ -468,6 +468,11 @@ enum FilamentMapMode {
fmmDefault
};
// All auto modes are ordered before fmmManual (see the enum ordering note above).
inline bool is_auto_filament_map_mode(FilamentMapMode mode) {
return mode < fmmManual;
}
// Dual-extruder purge control. Default reproduces the current
// per-extruder flush_multiplier + filament_prime_volume behaviour, so absent/default is inert.
// Saving -> reduce prime volume to 15 mm3; Fast -> use flush_multiplier_fast + filament_flush_temp_fast.
@@ -479,6 +484,10 @@ enum PrimeVolumeMode {
extern std::string get_extruder_variant_string(ExtruderType extruder_type, NozzleVolumeType nozzle_volume_type);
// Base slot lookup: scans a variant list (paired with its 1-based extruder/filament ids) for the
// entry matching the given extruder/volume type and id. Returns 0 when no entry matches.
extern int get_config_index_base(NozzleVolumeType volume_type, ExtruderType extruder_type, int variant_id_1based, const std::vector<std::string>& variant_list, const std::vector<int>& variant_ids_1based);
static std::set<NozzleVolumeType> get_valid_nozzle_volume_type() {
std::set<NozzleVolumeType> type;
for (int i = 0; i <= nvtMaxNozzleVolumeType; ++i) {
@@ -731,9 +740,14 @@ public:
//BBS
bool is_using_different_extruders();
bool support_different_extruders(int& extruder_count) const;
// Counts the config slots of a printer: one per (extruder x nozzle volume type) as described by
// extruder_nozzle_stats, or simply one per extruder when the stats are absent/mismatched.
// Fills nozzle_volume_types with each extruder's volume types in ascending enum order.
int get_extruder_nozzle_volume_count(int extruder_count, std::vector<std::vector<NozzleVolumeType>>& nozzle_volume_types) const;
int get_index_for_extruder(int extruder_or_filament_id, std::string id_name, ExtruderType extruder_type, NozzleVolumeType nozzle_volume_type, std::string variant_name, unsigned int stride = 1) const;
void update_values_to_printer_extruders(DynamicPrintConfig& printer_config, std::set<std::string>& key_set, std::string id_name, std::string variant_name, unsigned int stride = 1, unsigned int extruder_id = 0);
void update_values_to_printer_extruders_for_multiple_filaments(DynamicPrintConfig& printer_config, std::set<std::string>& key_set, std::string id_name, std::string variant_name);
std::vector<int> update_values_to_printer_extruders(DynamicPrintConfig& printer_config, int extruder_count, int extruder_nozzle_volume_count, std::vector<std::vector<NozzleVolumeType>>& nv_types,
std::set<std::string>& key_set, std::string id_name, std::string variant_name, unsigned int stride = 1, unsigned int extruder_id = 0, NozzleVolumeType filament_nvt = nvtStandard);
void update_values_to_printer_extruders_for_multiple_filaments(DynamicPrintConfig& printer_config, int extruder_count, int extruder_nozzle_volume_count, std::set<std::string>& key_set, std::string id_name, std::string variant_name);
void update_non_diff_values_to_base_config(DynamicPrintConfig& new_config, const t_config_option_keys& keys, const std::set<std::string>& different_keys, std::string extruder_id_name, std::string extruder_variant_name,
std::set<std::string>& key_set1, std::set<std::string>& key_set2);