mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-29 20:01:26 +00:00
Add multi-variant support to the app
This commit is contained in:
@@ -199,7 +199,8 @@ DynamicPrintConfig PresetBundle::construct_full_config(
|
||||
if (num_filaments <= 1) {
|
||||
// BBS: update filament config related with variants
|
||||
DynamicPrintConfig filament_config = in_filament_presets[0].config;
|
||||
if (apply_extruder && ((extruder_count > 1) || different_extruder))
|
||||
// Orca: a multi-variant filament resolves its variants on a single-variant printer too.
|
||||
if (apply_extruder && ((extruder_count > 1) || different_extruder || filament_config.has_multi_variant_filament()))
|
||||
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], (NozzleVolumeType)filament_volume_maps[0]);
|
||||
out.apply(filament_config);
|
||||
compatible_printers_condition.emplace_back(in_filament_presets[0].compatible_printers_condition());
|
||||
@@ -223,7 +224,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 && ((extruder_count > 1) || different_extruder))
|
||||
// Orca: a multi-variant filament resolves its variants on a single-variant printer too.
|
||||
if (apply_extruder && ((extruder_count > 1) || different_extruder || filament_temp_configs[i].has_multi_variant_filament()))
|
||||
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], (NozzleVolumeType)filament_volume_maps[i]);
|
||||
}
|
||||
|
||||
@@ -4664,7 +4666,8 @@ DynamicPrintConfig PresetBundle::full_fff_config(bool apply_extruder, std::optio
|
||||
if (num_filaments <= 1) {
|
||||
//BBS: update filament config related with variants
|
||||
DynamicPrintConfig filament_config = this->filaments.get_edited_preset().config;
|
||||
if (apply_extruder && ((extruder_count > 1) || different_extruder))
|
||||
// Orca: a multi-variant filament resolves its variants on a single-variant printer too.
|
||||
if (apply_extruder && ((extruder_count > 1) || different_extruder || filament_config.has_multi_variant_filament()))
|
||||
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], (NozzleVolumeType)filament_volume_maps[0]);
|
||||
out.apply(filament_config);
|
||||
compatible_printers_condition.emplace_back(this->filaments.get_edited_preset().compatible_printers_condition());
|
||||
@@ -4758,7 +4761,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 && ((extruder_count > 1) || different_extruder))
|
||||
// Orca: a multi-variant filament resolves its variants on a single-variant printer too.
|
||||
if (apply_extruder && ((extruder_count > 1) || different_extruder || filament_temp_configs[i].has_multi_variant_filament()))
|
||||
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], (NozzleVolumeType)filament_volume_maps[i]);
|
||||
}
|
||||
|
||||
|
||||
@@ -3938,6 +3938,9 @@ void Print::update_filament_maps_to_config(std::vector<int> f_maps, std::vector<
|
||||
{
|
||||
int extruder_count = 1, extruder_volume_type_count = 1;
|
||||
bool support_multi = m_ori_full_print_config.support_different_extruders(extruder_count);
|
||||
// Orca: resolve the filament variants wherever Print::apply does, a multi-variant filament
|
||||
// on a single-variant printer included.
|
||||
const bool expand_filaments = (extruder_count > 1) || support_multi || m_ori_full_print_config.has_multi_variant_filament();
|
||||
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);
|
||||
|
||||
@@ -3978,7 +3981,7 @@ void Print::update_filament_maps_to_config(std::vector<int> f_maps, std::vector<
|
||||
m_full_print_config = m_ori_full_print_config;
|
||||
std::set<std::string> filament_keys = filament_options_with_variant;
|
||||
filament_keys.insert("filament_self_index");
|
||||
if ((extruder_count > 1) || support_multi)
|
||||
if (expand_filaments)
|
||||
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();
|
||||
@@ -3995,7 +3998,7 @@ void Print::update_filament_maps_to_config(std::vector<int> f_maps, std::vector<
|
||||
compute_filament_override_value(opt_key, opt_old_machine, opt_new_machine, opt_new_filament, m_full_print_config, print_diff, filament_overrides, m_config.filament_map_2.values);
|
||||
}
|
||||
|
||||
if ((extruder_count > 1) || support_multi) {
|
||||
if (expand_filaments) {
|
||||
t_config_option_keys keys(filament_options_with_variant.begin(), filament_options_with_variant.end());
|
||||
keys.push_back("filament_self_index");
|
||||
m_config.apply_only(m_full_print_config, keys, true);
|
||||
|
||||
@@ -1296,7 +1296,9 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||
extruder_count, extruder_volume_type_count, filament_keys,
|
||||
"filament_self_index", "filament_extruder_variant",
|
||||
&dynamic_slot_indices);
|
||||
else if ((extruder_count > 1) || different_extruder)
|
||||
// Orca: also on a printer with a single extruder variant once a filament defines several
|
||||
// (e.g. Standard and High Flow), so each filament takes its variant for that extruder.
|
||||
else if ((extruder_count > 1) || different_extruder || new_full_config.has_multi_variant_filament())
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -626,7 +626,8 @@ static const t_config_enum_values s_keys_map_NozzleVolumeType = {
|
||||
{ "Standard", nvtStandard },
|
||||
{ "High Flow", nvtHighFlow },
|
||||
{ "TPU High Flow", nvtTPUHighFlow },
|
||||
{ "Hybrid", nvtHybrid }
|
||||
{ "Hybrid", nvtHybrid },
|
||||
{ "E3D High Flow", nvtE3DHighFlow }
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(NozzleVolumeType)
|
||||
|
||||
@@ -676,9 +677,34 @@ int get_config_index_base(NozzleVolumeType volume_type, ExtruderType extruder_ty
|
||||
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; }
|
||||
}
|
||||
// Without this variant, use the id's own first variant (usually Standard), not variant index 0,
|
||||
// which belongs to the first filament or extruder.
|
||||
for (int index = 0; index < int(variant_list.size()); ++index) {
|
||||
if (variant_ids_1based[index] == variant_id_1based) { return index; }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::set<NozzleVolumeType> get_extruder_supported_nozzle_volume_types(const DynamicPrintConfig &printer_config, int extruder_id)
|
||||
{
|
||||
std::set<NozzleVolumeType> supported_types;
|
||||
|
||||
auto *variant_list = printer_config.option<ConfigOptionStrings>("extruder_variant_list");
|
||||
auto *extruder_types = printer_config.option<ConfigOptionEnumsGeneric>("extruder_type");
|
||||
if (!variant_list || !extruder_types || extruder_id < 0 ||
|
||||
extruder_id >= (int) variant_list->values.size() || extruder_id >= (int) extruder_types->values.size())
|
||||
return supported_types;
|
||||
|
||||
const ExtruderType extruder_type = ExtruderType(extruder_types->values[extruder_id]);
|
||||
for (NozzleVolumeType volume_type : get_valid_nozzle_volume_type()) {
|
||||
// An unsupported extruder type yields an empty name, which would match any list.
|
||||
const std::string variant = get_extruder_variant_string(extruder_type, volume_type);
|
||||
if (!variant.empty() && variant_list->values[extruder_id].find(variant) != std::string::npos)
|
||||
supported_types.insert(volume_type);
|
||||
}
|
||||
return supported_types;
|
||||
}
|
||||
|
||||
std::string get_nozzle_volume_type_string(NozzleVolumeType nozzle_volume_type)
|
||||
{
|
||||
if (nozzle_volume_type > nvtMaxNozzleVolumeType) {
|
||||
@@ -5923,15 +5949,18 @@ void PrintConfigDef::init_fff_params()
|
||||
def->label = "Nozzle Volume Type";
|
||||
def->tooltip = "Nozzle volume type for extruders.";
|
||||
def->enum_keys_map = &ConfigOptionEnum<NozzleVolumeType>::get_enum_values();
|
||||
// Order must match the NozzleVolumeType enum values (Standard=0, High Flow=1, Hybrid=2, TPU High Flow=3).
|
||||
// Listed in display order. A position is not the enum value (E3D High Flow is 5, after the reserved 4),
|
||||
// so map a position to its NozzleVolumeType through enum_keys_map.
|
||||
def->enum_values.push_back(L("Standard"));
|
||||
def->enum_values.push_back(L("High Flow"));
|
||||
def->enum_values.push_back(L("Hybrid"));
|
||||
def->enum_values.push_back(L("TPU High Flow"));
|
||||
def->enum_values.push_back(L("E3D High Flow"));
|
||||
def->enum_labels.push_back(L("Standard"));
|
||||
def->enum_labels.push_back(L("High Flow"));
|
||||
def->enum_labels.push_back(L("Hybrid"));
|
||||
def->enum_labels.push_back(L("TPU High Flow"));
|
||||
def->enum_labels.push_back(L("E3D High Flow"));
|
||||
def->mode = comSimple;
|
||||
def->set_default_value(new ConfigOptionEnumsGeneric{ NozzleVolumeType::nvtStandard });
|
||||
|
||||
@@ -5944,10 +5973,12 @@ void PrintConfigDef::init_fff_params()
|
||||
def->enum_values.push_back(L("High Flow"));
|
||||
def->enum_values.push_back(L("Hybrid"));
|
||||
def->enum_values.push_back(L("TPU High Flow"));
|
||||
def->enum_values.push_back(L("E3D High Flow"));
|
||||
def->enum_labels.push_back(L("Standard"));
|
||||
def->enum_labels.push_back(L("High Flow"));
|
||||
def->enum_labels.push_back(L("Hybrid"));
|
||||
def->enum_labels.push_back(L("TPU High Flow"));
|
||||
def->enum_labels.push_back(L("E3D High Flow"));
|
||||
def->mode = comDevelop;
|
||||
def->set_default_value(new ConfigOptionEnumsGeneric{ NozzleVolumeType::nvtStandard });
|
||||
|
||||
@@ -5986,7 +6017,7 @@ void PrintConfigDef::init_fff_params()
|
||||
// Per-nozzle volume type. Forward-compat-only registration with no slicing consumer — nothing in
|
||||
// src/ reads it; the engine resolves per-nozzle volume types from `extruder_nozzle_stats` tokens
|
||||
// instead. Kept registered so a project/config carrying it loads without an unknown-option
|
||||
// substitution warning. Registers Standard/High Flow/TPU High Flow only (no Hybrid).
|
||||
// substitution warning. Registers the physical types only (no Hybrid).
|
||||
// Internal use only, no translation.
|
||||
def = this->add("extruder_nozzle_volume_type", coEnums);
|
||||
def->label = "Extruder nozzle volume type";
|
||||
@@ -5995,9 +6026,11 @@ void PrintConfigDef::init_fff_params()
|
||||
def->enum_values.push_back("Standard");
|
||||
def->enum_values.push_back("High Flow");
|
||||
def->enum_values.push_back("TPU High Flow");
|
||||
def->enum_values.push_back("E3D High Flow");
|
||||
def->enum_labels.push_back("Standard");
|
||||
def->enum_labels.push_back("High Flow");
|
||||
def->enum_labels.push_back("TPU High Flow");
|
||||
def->enum_labels.push_back("E3D High Flow");
|
||||
def->mode = comDevelop;
|
||||
def->set_default_value(new ConfigOptionEnumsGeneric{ NozzleVolumeType::nvtStandard });
|
||||
|
||||
@@ -10070,6 +10103,13 @@ bool DynamicPrintConfig::is_using_different_extruders()
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool DynamicPrintConfig::has_multi_variant_filament() const
|
||||
{
|
||||
auto variants = dynamic_cast<const ConfigOptionStrings*>(this->option("filament_extruder_variant"));
|
||||
auto diameters = dynamic_cast<const ConfigOptionFloats*>(this->option("filament_diameter"));
|
||||
return variants && diameters && variants->size() > diameters->size();
|
||||
}
|
||||
|
||||
bool DynamicPrintConfig::support_different_extruders(int& extruder_count) const
|
||||
{
|
||||
std::set<std::string> variant_set;
|
||||
@@ -11070,6 +11110,11 @@ void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filamen
|
||||
return;
|
||||
}
|
||||
std::vector<int> filament_maps = opt_filament_map->values;
|
||||
auto opt_ids = id_name.empty()? nullptr: dynamic_cast<const ConfigOptionInts*>(this->option(id_name));
|
||||
// Orca: a map shorter than the filament count must not drop the filaments past its end;
|
||||
// they take the first extruder.
|
||||
if (opt_ids && !opt_ids->values.empty())
|
||||
filament_maps.resize(std::max<size_t>(filament_maps.size(), *std::max_element(opt_ids->values.begin(), opt_ids->values.end())), 1);
|
||||
size_t filament_count = filament_maps.size();
|
||||
//apply process settings
|
||||
auto opt_extruder_type = dynamic_cast<const ConfigOptionEnumsGeneric*>(printer_config.option("extruder_type"));
|
||||
@@ -11088,7 +11133,6 @@ void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filamen
|
||||
// indexed out of bounds.
|
||||
if (opt_filament_volume_maps && 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;
|
||||
|
||||
variant_index.resize(filament_count, -1);
|
||||
@@ -11105,9 +11149,10 @@ void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filamen
|
||||
//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) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", Line %1%: could not found extruder_type %2%, nozzle_volume_type %3%, filament_index %4%, extruder index %5%")
|
||||
// Orca: a filament need not define every extruder variant (a Direct Drive filament on a
|
||||
// Bowden printer), so this is not an invalid state: the filament's first variant is used.
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", Line %1%: could not found extruder_type %2%, nozzle_volume_type %3%, filament_index %4%, extruder index %5%")
|
||||
%__LINE__ %s_keys_names_ExtruderType[extruder_type] % s_keys_names_NozzleVolumeType[nozzle_volume_type] % (f_index+1) %filament_maps[f_index];
|
||||
assert(false);
|
||||
//for some updates happens in a invalid state(caused by popup window)
|
||||
//we need to avoid crash
|
||||
variant_index[f_index] = 0;
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class DynamicPrintConfig;
|
||||
|
||||
enum GCodeFlavor : unsigned char {
|
||||
gcfMarlinLegacy,
|
||||
gcfKlipper,
|
||||
@@ -519,8 +521,10 @@ enum NozzleVolumeType {
|
||||
// with more than one sub-nozzle (extruder_max_nozzle_count > 1); matched as Standard for
|
||||
// preset lookup and never emitted in profile variant strings
|
||||
nvtTPUHighFlow, // physical variant, used on H2D/H2DP 0.4 nozzles only
|
||||
// 4 is reserved: E3D High Flow is 5 in BambuStudio's slice_info and device numbering.
|
||||
nvtE3DHighFlow = 5, // physical variant, E3D high-flow hotend on 0.4/0.6 nozzles
|
||||
// Integer values are serialized as raw ints in 3mf plate metadata and device MQTT, so they MUST stay stable.
|
||||
nvtMaxNozzleVolumeType = nvtTPUHighFlow
|
||||
nvtMaxNozzleVolumeType = nvtE3DHighFlow
|
||||
};
|
||||
|
||||
enum FilamentMapMode {
|
||||
@@ -558,11 +562,19 @@ static std::set<NozzleVolumeType> get_valid_nozzle_volume_type() {
|
||||
// Hybrid is not a physical nozzle variant: presets never define it, so it must not
|
||||
// produce a variant string.
|
||||
if (t == nvtHybrid) continue;
|
||||
// Skip the reserved gap between nvtTPUHighFlow (3) and nvtE3DHighFlow (5).
|
||||
if (i > nvtTPUHighFlow && i < nvtE3DHighFlow) continue;
|
||||
type.insert(t);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
// The nozzle volume types the given extruder physically provides, as declared by the printer
|
||||
// profile's extruder_variant_list. An empty set means the profile could not be read and must be
|
||||
// treated as "unknown", not as "none". nvtHybrid is never reported: it describes an extruder
|
||||
// holding a mix of nozzles, not a nozzle the profile can offer.
|
||||
extern std::set<NozzleVolumeType> get_extruder_supported_nozzle_volume_types(const DynamicPrintConfig &printer_config, int extruder_id);
|
||||
|
||||
std::string get_nozzle_volume_type_string(NozzleVolumeType nozzle_volume_type);
|
||||
|
||||
static std::string bed_type_to_gcode_string(const BedType type)
|
||||
@@ -699,8 +711,6 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SurfaceFillOrder)
|
||||
|
||||
#undef CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS
|
||||
|
||||
class DynamicPrintConfig;
|
||||
|
||||
// Defines each and every configuration option of Slic3r, including the properties of the GUI dialogs.
|
||||
// Does not store the actual values, but defines default values.
|
||||
class PrintConfigDef : public ConfigDef
|
||||
@@ -821,6 +831,10 @@ public:
|
||||
//BBS
|
||||
bool is_using_different_extruders();
|
||||
bool support_different_extruders(int& extruder_count) const;
|
||||
// Whether any filament defines more than one variant (filament_extruder_variant longer than
|
||||
// filament_diameter). Its variants then have to be resolved even on a printer with a single
|
||||
// extruder variant, which picks the filament's variant of the same variant string.
|
||||
bool has_multi_variant_filament() 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.
|
||||
|
||||
@@ -818,12 +818,14 @@ NewCalibrationHistoryDialog::NewCalibrationHistoryDialog(wxWindow *parent, const
|
||||
if (support_nozzle_volume(curr_obj)) {
|
||||
Label *nozzle_name_title = new Label(top_panel, _L("Nozzle"));
|
||||
m_comboBox_nozzle_type = new ::ComboBox(top_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, NEW_HISTORY_DIALOG_INPUT_SIZE, 0, nullptr, wxCB_READONLY);
|
||||
wxArrayString nozzle_items;
|
||||
// The labels are in display order, not enum order (E3D High Flow is 5 but the fifth label), so each
|
||||
// item carries its NozzleVolumeType as client data.
|
||||
const ConfigOptionDef *nozzle_volume_type_def = print_config_def.get("nozzle_volume_type");
|
||||
if (nozzle_volume_type_def && nozzle_volume_type_def->enum_keys_map) {
|
||||
for (auto item : nozzle_volume_type_def->enum_labels) { nozzle_items.push_back(_L(item)); }
|
||||
for (size_t i = 0; i < nozzle_volume_type_def->enum_labels.size(); ++i)
|
||||
m_comboBox_nozzle_type->Append(_L(nozzle_volume_type_def->enum_labels[i]), wxNullBitmap,
|
||||
(void *) (intptr_t) nozzle_volume_type_def->enum_keys_map->at(nozzle_volume_type_def->enum_values[i]));
|
||||
}
|
||||
m_comboBox_nozzle_type->Set(nozzle_items);
|
||||
m_comboBox_nozzle_type->SetSelection(-1);
|
||||
flex_sizer->Add(nozzle_name_title);
|
||||
flex_sizer->Add(m_comboBox_nozzle_type);
|
||||
@@ -891,7 +893,7 @@ int NewCalibrationHistoryDialog::get_nozzle_combo_id_code() const
|
||||
|
||||
void NewCalibrationHistoryDialog::on_select_nozzle_pos(wxCommandEvent &event)
|
||||
{
|
||||
// Mirror the picked hotend's flow onto the (Orca index-based) nozzle-type combo.
|
||||
// Mirror the picked hotend's flow onto the nozzle-type combo.
|
||||
if (!curr_obj || !m_comboBox_nozzle_id || !m_comboBox_nozzle_type || !curr_obj->GetNozzleSystem())
|
||||
return;
|
||||
|
||||
@@ -902,7 +904,9 @@ void NewCalibrationHistoryDialog::on_select_nozzle_pos(wxCommandEvent &event)
|
||||
DevNozzle nozzle = curr_obj->GetNozzleSystem()->GetNozzleByPosId(pos);
|
||||
if (nozzle.IsNormal()) {
|
||||
NozzleVolumeType volume_type = DevNozzle::ToNozzleVolumeType(nozzle.GetNozzleFlowType());
|
||||
m_comboBox_nozzle_type->SetSelection(static_cast<int>(volume_type));
|
||||
for (unsigned int i = 0; i < m_comboBox_nozzle_type->GetCount(); ++i)
|
||||
if (NozzleVolumeType(intptr_t(m_comboBox_nozzle_type->GetClientData(i))) == volume_type)
|
||||
m_comboBox_nozzle_type->SetSelection(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -949,7 +953,7 @@ void NewCalibrationHistoryDialog::on_ok(wxCommandEvent &event)
|
||||
msg_dlg.ShowModal();
|
||||
return;
|
||||
}
|
||||
m_new_result.nozzle_volume_type = NozzleVolumeType(m_comboBox_nozzle_type->GetSelection());
|
||||
m_new_result.nozzle_volume_type = NozzleVolumeType(intptr_t(m_comboBox_nozzle_type->GetClientData(m_comboBox_nozzle_type->GetSelection())));
|
||||
}
|
||||
|
||||
auto filament_item = map_filament_items[m_comboBox_filament->GetValue().ToStdString()];
|
||||
|
||||
@@ -803,6 +803,29 @@ void CalibrationPresetPage::create_selection_panel(wxWindow* parent)
|
||||
#define NOZZLE_LIST_DEFAULT 1
|
||||
float nozzle_diameter_list[NOZZLE_LIST_COUNT] = {0.2, 0.4, 0.6, 0.8 };
|
||||
|
||||
// The nozzle_volume_type labels are in display order, not enum order (E3D High Flow is 5 but the fifth
|
||||
// label), so each item carries its NozzleVolumeType as client data and is selected by that value.
|
||||
static void select_nozzle_volume(ComboBox *combo, NozzleVolumeType volume_type)
|
||||
{
|
||||
for (unsigned int i = 0; i < combo->GetCount(); ++i)
|
||||
if (NozzleVolumeType(intptr_t(combo->GetClientData(i))) == volume_type) {
|
||||
combo->SetSelection(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void fill_nozzle_volume_combo(ComboBox *combo)
|
||||
{
|
||||
combo->Clear();
|
||||
const ConfigOptionDef *nozzle_volume_type_def = print_config_def.get("nozzle_volume_type");
|
||||
if (nozzle_volume_type_def && nozzle_volume_type_def->enum_keys_map) {
|
||||
for (size_t i = 0; i < nozzle_volume_type_def->enum_labels.size(); ++i)
|
||||
combo->Append(_L(nozzle_volume_type_def->enum_labels[i]), wxNullBitmap,
|
||||
(void *) (intptr_t) nozzle_volume_type_def->enum_keys_map->at(nozzle_volume_type_def->enum_values[i]));
|
||||
}
|
||||
select_nozzle_volume(combo, NozzleVolumeType::nvtStandard);
|
||||
}
|
||||
|
||||
void CalibrationPresetPage::init_selection_values()
|
||||
{
|
||||
// init nozzle diameter and nozzle volume
|
||||
@@ -813,15 +836,7 @@ void CalibrationPresetPage::init_selection_values()
|
||||
}
|
||||
m_comboBox_nozzle_dia->SetSelection(NOZZLE_LIST_DEFAULT);
|
||||
|
||||
m_comboBox_nozzle_volume->Clear();
|
||||
const ConfigOptionDef *nozzle_volume_type_def = print_config_def.get("nozzle_volume_type");
|
||||
if (nozzle_volume_type_def && nozzle_volume_type_def->enum_keys_map) {
|
||||
for (auto item : nozzle_volume_type_def->enum_labels) {
|
||||
m_comboBox_nozzle_volume->AppendString(_L(item));
|
||||
}
|
||||
}
|
||||
|
||||
m_comboBox_nozzle_volume->SetSelection(int(NozzleVolumeType::nvtStandard));
|
||||
fill_nozzle_volume_combo(m_comboBox_nozzle_volume);
|
||||
}
|
||||
|
||||
Preset* cur_printer_preset = get_printer_preset(curr_obj, 0.4);
|
||||
@@ -866,15 +881,7 @@ void CalibrationPresetPage::init_selection_values()
|
||||
}
|
||||
m_left_comboBox_nozzle_dia->SetSelection(NOZZLE_LIST_DEFAULT);
|
||||
|
||||
m_left_comboBox_nozzle_volume->Clear();
|
||||
const ConfigOptionDef *nozzle_volume_type_def = print_config_def.get("nozzle_volume_type");
|
||||
if (nozzle_volume_type_def && nozzle_volume_type_def->enum_keys_map) {
|
||||
for (auto item : nozzle_volume_type_def->enum_labels) {
|
||||
m_left_comboBox_nozzle_volume->AppendString(_L(item));
|
||||
}
|
||||
}
|
||||
|
||||
m_left_comboBox_nozzle_volume->SetSelection(int(NozzleVolumeType::nvtStandard));
|
||||
fill_nozzle_volume_combo(m_left_comboBox_nozzle_volume);
|
||||
}
|
||||
|
||||
// right
|
||||
@@ -885,15 +892,7 @@ void CalibrationPresetPage::init_selection_values()
|
||||
}
|
||||
m_right_comboBox_nozzle_dia->SetSelection(NOZZLE_LIST_DEFAULT);
|
||||
|
||||
m_right_comboBox_nozzle_volume->Clear();
|
||||
const ConfigOptionDef *nozzle_volume_type_def = print_config_def.get("nozzle_volume_type");
|
||||
if (nozzle_volume_type_def && nozzle_volume_type_def->enum_keys_map) {
|
||||
for (auto item : nozzle_volume_type_def->enum_labels) {
|
||||
m_right_comboBox_nozzle_volume->AppendString(_L(item));
|
||||
}
|
||||
}
|
||||
|
||||
m_right_comboBox_nozzle_volume->SetSelection(int(NozzleVolumeType::nvtStandard));
|
||||
fill_nozzle_volume_combo(m_right_comboBox_nozzle_volume);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -980,13 +979,13 @@ NozzleVolumeType CalibrationPresetPage::get_nozzle_volume_type(int extruder_id)
|
||||
if (curr_obj) {
|
||||
if (curr_obj->is_multi_extruders()) {
|
||||
if (extruder_id == LEFT_EXTRUDER_ID) {
|
||||
return NozzleVolumeType(m_left_comboBox_nozzle_volume->GetSelection());
|
||||
return NozzleVolumeType(intptr_t(m_left_comboBox_nozzle_volume->GetClientData(m_left_comboBox_nozzle_volume->GetSelection())));
|
||||
} else if (extruder_id == RIGHT_EXTRUDER_ID) {
|
||||
return NozzleVolumeType(m_right_comboBox_nozzle_volume->GetSelection());
|
||||
return NozzleVolumeType(intptr_t(m_right_comboBox_nozzle_volume->GetClientData(m_right_comboBox_nozzle_volume->GetSelection())));
|
||||
}
|
||||
}
|
||||
else
|
||||
return NozzleVolumeType(m_comboBox_nozzle_volume->GetSelection());
|
||||
return NozzleVolumeType(intptr_t(m_comboBox_nozzle_volume->GetClientData(m_comboBox_nozzle_volume->GetSelection())));
|
||||
}
|
||||
return NozzleVolumeType::nvtStandard;
|
||||
}
|
||||
@@ -2139,7 +2138,7 @@ void CalibrationPresetPage::init_with_machine(MachineObject* obj)
|
||||
}
|
||||
|
||||
if (obj->GetExtderSystem()->GetNozzleFlowType(i) != NozzleFlowType::NONE_FLOWTYPE) {
|
||||
m_left_comboBox_nozzle_volume->SetSelection(int(DevNozzle::ToNozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(i))));
|
||||
select_nozzle_volume(m_left_comboBox_nozzle_volume, DevNozzle::ToNozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(i)));
|
||||
} else {
|
||||
m_left_comboBox_nozzle_volume->SetSelection(0);
|
||||
}
|
||||
@@ -2159,7 +2158,7 @@ void CalibrationPresetPage::init_with_machine(MachineObject* obj)
|
||||
}
|
||||
|
||||
if (obj->GetExtderSystem()->GetNozzleFlowType(i) != NozzleFlowType::NONE_FLOWTYPE) {
|
||||
m_right_comboBox_nozzle_volume->SetSelection(int(DevNozzle::ToNozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(i))));
|
||||
select_nozzle_volume(m_right_comboBox_nozzle_volume, DevNozzle::ToNozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(i)));
|
||||
} else {
|
||||
m_right_comboBox_nozzle_volume->SetSelection(0);
|
||||
}
|
||||
@@ -2197,7 +2196,7 @@ void CalibrationPresetPage::init_with_machine(MachineObject* obj)
|
||||
else {
|
||||
if ((obj->GetExtderSystem()->GetTotalExtderCount() > 0) && (obj->GetExtderSystem()->GetNozzleFlowType(0) != NozzleFlowType::NONE_FLOWTYPE))
|
||||
{
|
||||
m_comboBox_nozzle_volume->SetSelection(int(DevNozzle::ToNozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(0))));
|
||||
select_nozzle_volume(m_comboBox_nozzle_volume, DevNozzle::ToNozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(0)));
|
||||
} else {
|
||||
m_comboBox_nozzle_volume->SetSelection(0);
|
||||
}
|
||||
|
||||
@@ -732,6 +732,7 @@ void CaliPASaveAutoPanel::sync_cali_result_for_multi_extruder(const std::vector<
|
||||
case NozzleVolumeType::nvtStandard: nozzle_id_str += _L("Standard Flow"); break;
|
||||
case NozzleVolumeType::nvtHighFlow: nozzle_id_str += _L("High Flow"); break;
|
||||
case NozzleVolumeType::nvtTPUHighFlow: nozzle_id_str += _L("TPU High Flow"); break;
|
||||
case NozzleVolumeType::nvtE3DHighFlow: nozzle_id_str += _L("E3D High Flow"); break;
|
||||
default: break;
|
||||
}
|
||||
nozzle_id_value->SetLabel(nozzle_id_str);
|
||||
|
||||
@@ -36,7 +36,8 @@ static NozzleVolumeType convert_to_nozzle_type(const std::string &str)
|
||||
return NozzleVolumeType::nvtHighFlow;
|
||||
else if (str[1] == 'U')
|
||||
return NozzleVolumeType::nvtTPUHighFlow;
|
||||
// Orca: no nvtE3DHighFlow in Orca's NozzleVolumeType; map 'B' to Standard
|
||||
else if (str[1] == 'B')
|
||||
return NozzleVolumeType::nvtE3DHighFlow;
|
||||
else
|
||||
return NozzleVolumeType::nvtStandard;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
// ---- DevNozzle: flow/volume conversions (Standard / High Flow / TPU High Flow) ----------------------
|
||||
// ---- DevNozzle: flow/volume conversions (Standard / High Flow / TPU High Flow / E3D High Flow) ------
|
||||
// Device-reported U_FLOW nozzles map to nvtTPUHighFlow so a synced TPU-HF rack activates the H2C
|
||||
// change_filament_gcode TPU-kit branch. nvtHybrid is a slicer-only sentinel with no device
|
||||
// representation, so ToNozzleFlowType(nvtHybrid) falls through to NONE_FLOWTYPE.
|
||||
@@ -24,6 +24,7 @@ NozzleFlowType DevNozzle::ToNozzleFlowType(const NozzleVolumeType& type)
|
||||
case NozzleVolumeType::nvtStandard: return NozzleFlowType::S_FLOW;
|
||||
case NozzleVolumeType::nvtHighFlow: return NozzleFlowType::H_FLOW;
|
||||
case NozzleVolumeType::nvtTPUHighFlow: return NozzleFlowType::U_FLOW;
|
||||
case NozzleVolumeType::nvtE3DHighFlow: return NozzleFlowType::E_FLOW;
|
||||
default: return NozzleFlowType::NONE_FLOWTYPE;
|
||||
}
|
||||
}
|
||||
@@ -34,6 +35,7 @@ NozzleVolumeType DevNozzle::ToNozzleVolumeType(const NozzleFlowType& type)
|
||||
case NozzleFlowType::S_FLOW: return NozzleVolumeType::nvtStandard;
|
||||
case NozzleFlowType::H_FLOW: return NozzleVolumeType::nvtHighFlow;
|
||||
case NozzleFlowType::U_FLOW: return NozzleVolumeType::nvtTPUHighFlow;
|
||||
case NozzleFlowType::E_FLOW: return NozzleVolumeType::nvtE3DHighFlow;
|
||||
default: {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << "nozzle flow type None convert to nozzle volume type Standard";
|
||||
return NozzleVolumeType::nvtStandard;
|
||||
@@ -47,6 +49,7 @@ wxString DevNozzle::GetNozzleFlowTypeStr(NozzleFlowType type)
|
||||
case NozzleFlowType::H_FLOW: return _L("High Flow");
|
||||
case NozzleFlowType::S_FLOW: return _L("Standard");
|
||||
case NozzleFlowType::U_FLOW: return _L("TPU High Flow");
|
||||
case NozzleFlowType::E_FLOW: return _L("E3D High Flow");
|
||||
default: break;
|
||||
}
|
||||
|
||||
@@ -61,6 +64,7 @@ std::string DevNozzle::GetNozzleFlowTypeString(NozzleFlowType type)
|
||||
case NozzleFlowType::H_FLOW: return "High Flow";
|
||||
case NozzleFlowType::S_FLOW: return "Standard";
|
||||
case NozzleFlowType::U_FLOW: return "TPU High Flow";
|
||||
case NozzleFlowType::E_FLOW: return "E3D High Flow";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
@@ -73,6 +77,7 @@ std::string DevNozzle::ToNozzleFlowString(const NozzleFlowType& type)
|
||||
case NozzleFlowType::S_FLOW: return "Standard";
|
||||
case NozzleFlowType::H_FLOW: return "High Flow";
|
||||
case NozzleFlowType::U_FLOW: return "TPU High Flow";
|
||||
case NozzleFlowType::E_FLOW: return "E3D High Flow";
|
||||
default: return std::string();
|
||||
}
|
||||
}
|
||||
@@ -408,6 +413,7 @@ static unordered_map<string, NozzleFlowType> _str2_nozzle_flow_type = {
|
||||
{"X", NozzleFlowType::S_FLOW},
|
||||
{"E", NozzleFlowType::H_FLOW}, // E3D high-flow
|
||||
{"U", NozzleFlowType::U_FLOW}, // TPU 1.75 high-flow -> nvtTPUHighFlow
|
||||
{"B", NozzleFlowType::E_FLOW}, // E3D High Flow -> nvtE3DHighFlow
|
||||
};
|
||||
|
||||
static unordered_map<string, NozzleType> _str2_nozzle_type = {
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
|
||||
// Device flow-type mapping note (nozzle rack): the device U_FLOW value maps to nvtTPUHighFlow
|
||||
// (TPU High Flow), so a device-synced TPU-HF rack resolves to nvtTPUHighFlow and the H2C
|
||||
// change_filament_gcode TPU-kit branch can activate. nvtHybrid is a slicer-only sentinel with no
|
||||
// device representation, so it stays out of these device flow-type conversions.
|
||||
// change_filament_gcode TPU-kit branch can activate. E_FLOW maps to nvtE3DHighFlow. nvtHybrid is a
|
||||
// slicer-only sentinel with no device representation, so it stays out of these device flow-type
|
||||
// conversions.
|
||||
//
|
||||
// GetExtruderNozzleInfo (and its ExtruderNozzleInfos aggregate) is intentionally omitted here;
|
||||
// the SelectMachine slicing-vs-installed nozzle comparison collects its per-extruder NozzleDef
|
||||
@@ -36,13 +37,13 @@ namespace Slic3r
|
||||
float m_diameter = 0.0f;// unknown until reported by the printer
|
||||
|
||||
public:
|
||||
// flow/volume conversions (Standard / High Flow / TPU High Flow)
|
||||
// flow/volume conversions (Standard / High Flow / TPU High Flow / E3D High Flow)
|
||||
static NozzleFlowType ToNozzleFlowType(const NozzleVolumeType& type);
|
||||
static NozzleVolumeType ToNozzleVolumeType(const NozzleFlowType& type);
|
||||
|
||||
static wxString GetNozzleFlowTypeStr(NozzleFlowType type);
|
||||
static std::string GetNozzleFlowTypeString(NozzleFlowType type);// untranslated literal ("High Flow"/"Standard"/"TPU High Flow") — the filament blacklist JSON matches these raw strings, so it must NOT use the translated GetNozzleFlowTypeStr (would break non-English locales)
|
||||
static std::string ToNozzleFlowString(const NozzleFlowType& type);// untranslated "Standard"/"High Flow"/"TPU High Flow" ("" for none) — the raw literal serialized into the get_auto_nozzle_mapping payload
|
||||
static std::string GetNozzleFlowTypeString(NozzleFlowType type);// untranslated literal ("High Flow"/"Standard"/"TPU High Flow"/"E3D High Flow") — the filament blacklist JSON matches these raw strings, so it must NOT use the translated GetNozzleFlowTypeStr (would break non-English locales)
|
||||
static std::string ToNozzleFlowString(const NozzleFlowType& type);// untranslated "Standard"/"High Flow"/"TPU High Flow"/"E3D High Flow" ("" for none) — the raw literal serialized into the get_auto_nozzle_mapping payload
|
||||
static wxString GetNozzleTypeStr(NozzleType type);
|
||||
|
||||
public:
|
||||
|
||||
@@ -347,6 +347,10 @@ static wxString _generate_nozzle_id(NozzleVolumeType nozzle_type, const std::str
|
||||
nozzle_id += "H";
|
||||
break;
|
||||
}
|
||||
case NozzleVolumeType::nvtE3DHighFlow: {
|
||||
nozzle_id += "B";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
nozzle_id += "H";
|
||||
break;
|
||||
@@ -368,6 +372,8 @@ NozzleVolumeType convert_to_nozzle_type(const std::string &str)
|
||||
res = NozzleVolumeType::nvtStandard;
|
||||
else if (str[1] == 'H')
|
||||
res = NozzleVolumeType::nvtHighFlow;
|
||||
else if (str[1] == 'B')
|
||||
res = NozzleVolumeType::nvtE3DHighFlow;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -566,7 +566,8 @@ std::vector<int> SeparatedDragDropPanel::GetHighFlowFilaments() const
|
||||
const int right_eid = 1;
|
||||
if (nozzle_volumes->values.size() > right_eid) {
|
||||
int volume_type = nozzle_volumes->values[right_eid];
|
||||
if (volume_type == static_cast<int>(NozzleVolumeType::nvtHighFlow)) {
|
||||
if (volume_type == static_cast<int>(NozzleVolumeType::nvtHighFlow) ||
|
||||
volume_type == static_cast<int>(NozzleVolumeType::nvtE3DHighFlow)) {
|
||||
return m_unified_panel->GetAllFilaments();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,6 +185,12 @@ std::vector<int> FilamentMapManualPanel::GetFilamentVolumeMaps() const
|
||||
auto preset_bundle = wxGetApp().preset_bundle;
|
||||
auto proj_config = preset_bundle->project_config;
|
||||
auto nozzle_volume_values = proj_config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type")->values;
|
||||
// The high-flow panel of the right extruder also holds an E3D High Flow nozzle; keep that type
|
||||
// instead of writing plain High Flow, which the extruder does not have.
|
||||
const int right_extruder_id = 1;
|
||||
const bool right_e3d_high_flow = nozzle_volume_values.size() > right_extruder_id &&
|
||||
nozzle_volume_values[right_extruder_id] == static_cast<int>(NozzleVolumeType::nvtE3DHighFlow);
|
||||
const int right_high_flow_volume = static_cast<int>(right_e3d_high_flow ? NozzleVolumeType::nvtE3DHighFlow : NozzleVolumeType::nvtHighFlow);
|
||||
|
||||
for (int i = 0; i < (int) volume_map.size(); ++i) {
|
||||
int filament_id = i + 1;
|
||||
@@ -199,7 +205,7 @@ std::vector<int> FilamentMapManualPanel::GetFilamentVolumeMaps() const
|
||||
}
|
||||
}
|
||||
else if (std::find(right_high_flow_filaments.begin(), right_high_flow_filaments.end(), filament_id) != right_high_flow_filaments.end()) {
|
||||
volume_map[i] = static_cast<int>(NozzleVolumeType::nvtHighFlow);
|
||||
volume_map[i] = right_high_flow_volume;
|
||||
}
|
||||
else if (std::find(right_standard_filaments.begin(), right_standard_filaments.end(), filament_id) != right_standard_filaments.end()) {
|
||||
volume_map[i] = static_cast<int>(NozzleVolumeType::nvtStandard);
|
||||
|
||||
+274
-54
@@ -605,15 +605,50 @@ private:
|
||||
bool m_enabled{false};
|
||||
};
|
||||
|
||||
// The nozzle rows are set in the small body font their "T1" prefix and diameter readout use, so the
|
||||
// combos of a row drop from the Body_14 they are created with to match, instead of towering over
|
||||
// the labels next to them.
|
||||
static void use_nozzle_row_font(ComboBox *combo)
|
||||
{
|
||||
// A read-only combo draws its value with the font of the TextInput it is built on, and its
|
||||
// dropped list carries a font of its own.
|
||||
combo->SetFont(Label::Body_10);
|
||||
combo->GetDropDown().SetFont(Label::Body_10);
|
||||
|
||||
// A row is as tall as the tags of the variant switch: a line of Body_10 text plus the 3 points of
|
||||
// padding they put above and below it. TextInput::messureSize() instead sizes a combo as its
|
||||
// (hidden) text control plus 8, and on macOS that control's best height is a fixed 16 points
|
||||
// whatever font it carries, so the box would keep the height of the Body_14 it was created with.
|
||||
// Handing the control the height the row asks for, less those 8 points, keeps messureSize()'s own
|
||||
// arithmetic landing on the small box however often it re-measures.
|
||||
const int row_height = combo->GetTextExtent("0.4 mm").y + 2 * combo->FromDIP(3);
|
||||
auto *text_ctrl = combo->GetTextCtrl();
|
||||
text_ctrl->SetFont(Label::Body_10);
|
||||
text_ctrl->SetInitialSize(wxSize(text_ctrl->GetBestSize().x, row_height - 8));
|
||||
|
||||
combo->SetMinSize(wxSize(combo->GetMinSize().x, row_height));
|
||||
combo->SetSize(wxSize(combo->GetSize().x, row_height));
|
||||
}
|
||||
|
||||
struct ExtruderGroup : StaticBox
|
||||
{
|
||||
// One nozzle row: the tool prefix ("T1"…"Tn", shown only when the group lists several
|
||||
// extruders) plus the nozzle readout and flow combo of a single extruder. Rows are appended on
|
||||
// demand and never destroyed, so an event handler may capture its row index for the lifetime
|
||||
// of the group.
|
||||
struct NozzleRow
|
||||
{
|
||||
Label * prefix = nullptr;
|
||||
Label * diameter_label = nullptr; // read-only nozzle diameter of this extruder
|
||||
ComboBox * flow = nullptr;
|
||||
};
|
||||
|
||||
ExtruderGroup(wxWindow * parent, int index, wxString const &title);
|
||||
wxBoxSizer * sizer = nullptr;
|
||||
HoverLabel * hover_label = nullptr;
|
||||
wxStaticText* ams_label{nullptr};
|
||||
ScalableButton * btn_edit = nullptr;
|
||||
ComboBox * combo_diameter = nullptr;
|
||||
ComboBox * combo_flow = nullptr;
|
||||
AMSPreview * ams[4] = {nullptr};
|
||||
wxStaticText *ams_not_installed_msg{nullptr};
|
||||
ScalableButton * btn_up{nullptr};
|
||||
@@ -627,6 +662,23 @@ struct ExtruderGroup : StaticBox
|
||||
std::vector<AMSinfo> ams_1;
|
||||
wxString diameter;
|
||||
|
||||
std::vector<NozzleRow> rows; // rows[0] always exists, one row per extruder when multi
|
||||
size_t nozzle_row_count = 0; // rows currently shown
|
||||
wxBoxSizer * row_columns[2] = {}; // row i is dealt into column i % 2
|
||||
int group_index = -1; // ctor index, the extruder a single-row group edits
|
||||
|
||||
// Show `count` nozzle rows, one per extruder, each prefixed with T<index> when count > 1.
|
||||
void SetNozzleRowCount(size_t count);
|
||||
size_t NozzleRowCount() const { return nozzle_row_count; }
|
||||
// Nozzle diameter readout of the shown rows, one value per extruder. A listing card cannot edit
|
||||
// it: the diameter belongs to the machine variant the printer preset selects, not to the extruder.
|
||||
void SetRowDiameters(const std::vector<double> &diameters);
|
||||
|
||||
NozzleRow create_nozzle_row();
|
||||
// Widths the prefix and diameter labels of the visible rows so that the flow combos of a column
|
||||
// line up.
|
||||
void update_row_widths();
|
||||
|
||||
void set_ams_count(int n4, int n1)
|
||||
{
|
||||
if (n4 == ams_n4 && n1 == ams_n1)
|
||||
@@ -657,7 +709,12 @@ struct ExtruderGroup : StaticBox
|
||||
btn_up->msw_rescale();
|
||||
btn_down->msw_rescale();
|
||||
combo_diameter->Rescale();
|
||||
combo_flow->Rescale();
|
||||
for (const NozzleRow &row : rows) {
|
||||
row.prefix->SetFont(Label::Body_10.Bold());
|
||||
row.diameter_label->SetFont(Label::Body_10);
|
||||
row.flow->Rescale();
|
||||
}
|
||||
update_row_widths();
|
||||
for (int i = 0; i < 4; ++i)
|
||||
ams[i]->msw_rescale();
|
||||
}
|
||||
@@ -668,6 +725,10 @@ struct ExtruderGroup : StaticBox
|
||||
hover_label->sys_color_changed();
|
||||
if (btn_edit)
|
||||
btn_edit->SetBackgroundColour(extruder_group_chip_bg());
|
||||
for (const NozzleRow &row : rows) {
|
||||
row.prefix->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636")));
|
||||
row.diameter_label->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636")));
|
||||
}
|
||||
Refresh();
|
||||
}
|
||||
};
|
||||
@@ -691,11 +752,12 @@ struct Sidebar::priv
|
||||
ScalableButton * btn_edit_printer = nullptr;
|
||||
ScalableButton * btn_connect_printer = nullptr;
|
||||
|
||||
// Nozzle diameter
|
||||
// Printer variant switch, titled "Variant": the combo lists the variants (nozzle sizes) of the
|
||||
// current machine model and picking one switches the printer preset.
|
||||
StaticBox * panel_nozzle_dia = nullptr;
|
||||
Label * label_nozzle_title= nullptr;
|
||||
ComboBox * combo_nozzle_dia = nullptr;
|
||||
Label * label_nozzle_type = nullptr;
|
||||
Label * label_nozzle_type = nullptr; // read-only nozzle material, one-row layouts only
|
||||
|
||||
// Printer - bed
|
||||
StaticBox * panel_printer_bed = nullptr;
|
||||
@@ -809,6 +871,8 @@ struct Sidebar::priv
|
||||
// otherwise reuses the app_config-cached option when the machine's nozzle config is unchanged.
|
||||
std::optional<NozzleOption> get_nozzle_options(MachineObject* obj, int extruder_count, bool support_multi_nozzle, bool is_manual);
|
||||
bool switch_diameter(bool single);
|
||||
// Switch to the printer preset offering `diameter`.
|
||||
bool switch_diameter_to(const wxString &diameter);
|
||||
void update_sync_status(const MachineObject* obj);
|
||||
|
||||
// Filament Track Switch (H2-family accessory): true only when the connected printer is the
|
||||
@@ -918,11 +982,19 @@ void Sidebar::priv::layout_printer(bool isBBL, bool isDual)
|
||||
int extruder_count = 0;
|
||||
const bool has_flow_variant = cfg.support_different_extruders(extruder_count);
|
||||
|
||||
panel_nozzle_dia->Show(!has_flow_variant);
|
||||
extruder_single_sizer->Show(has_flow_variant);
|
||||
// ORCA: a non-Bambu printer with several extruders lists one nozzle row per tool; Bambu's own
|
||||
// multi-extruder printers keep the left/right pair above.
|
||||
const bool multi_extruder_rows = !preset_bundle.is_bbl_vendor() && extruder_count > 1;
|
||||
|
||||
// The variant box switches the machine variant. The card below lists the nozzles when there
|
||||
// are flow variants to choose or several extruders to show; otherwise the box says it all.
|
||||
panel_nozzle_dia->Show(true);
|
||||
extruder_single_sizer->Show(has_flow_variant || multi_extruder_rows);
|
||||
single_extruder->SetNozzleRowCount(multi_extruder_rows ? size_t(extruder_count) : 1);
|
||||
} else {
|
||||
panel_nozzle_dia->Show(false);
|
||||
extruder_single_sizer->Show(false);
|
||||
single_extruder->SetNozzleRowCount(1);
|
||||
}
|
||||
|
||||
// ORCA ensure printer section is visible after changing printer from printer selection dialog
|
||||
@@ -1319,7 +1391,7 @@ public:
|
||||
};
|
||||
|
||||
ExtruderGroup::ExtruderGroup(wxWindow * parent, int index, wxString const &title)
|
||||
: StaticBox(parent)
|
||||
: StaticBox(parent), group_index(index)
|
||||
{
|
||||
SetFont(Label::Body_10);
|
||||
SetForegroundColour(wxColour("#CECECE"));
|
||||
@@ -1332,29 +1404,13 @@ ExtruderGroup::ExtruderGroup(wxWindow * parent, int index, wxString const &title
|
||||
hover_label = new HoverLabel(this, title);
|
||||
hover_label->SetPosition(wxPoint(FromDIP(PRINTER_PANEL_RADIUS), 0)); // position it without putting in a sizer so it will look like title
|
||||
|
||||
// Nozzle
|
||||
// Nozzle. Only the dual left/right cards show this combo: it is how they ask which nozzle the
|
||||
// project should print with. Every other card reads the diameter out as text.
|
||||
auto combo_diameter = new ComboBox(this, wxID_ANY, wxString(""), wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_READONLY);
|
||||
this->combo_diameter = combo_diameter;
|
||||
combo_diameter->SetToolTip(_L("Diameter"));
|
||||
|
||||
// Flow
|
||||
auto combo_flow = new ComboBox(this, wxID_ANY, wxString(""), wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_READONLY);
|
||||
combo_flow->GetDropDown().SetUseContentWidth(true);
|
||||
combo_flow->Bind(wxEVT_COMBOBOX, [index, combo_flow](wxCommandEvent &evt) {
|
||||
auto printer_tab = dynamic_cast<TabPrinter *>(wxGetApp().get_tab(Preset::TYPE_PRINTER));
|
||||
NozzleVolumeType volume_type = NozzleVolumeType(intptr_t(combo_flow->GetClientData(evt.GetInt())));
|
||||
printer_tab->set_extruder_volume_type(index, volume_type);
|
||||
auto plater = GUI::wxGetApp().plater();
|
||||
if (plater) {
|
||||
// A new Flow type invalidates the per-filament volume choices stored on the
|
||||
// plates for this extruder; rewrite them so the next apply/grouping sees the
|
||||
// selected volume instead of a stale one.
|
||||
plater->update_filament_volume_map(index, static_cast<int>(volume_type));
|
||||
plater->update_machine_sync_status();
|
||||
}
|
||||
});
|
||||
this->combo_flow = combo_flow;
|
||||
combo_flow->SetToolTip(_L("Flow"));
|
||||
combo_diameter->Show(index >= 0);
|
||||
use_nozzle_row_font(combo_diameter);
|
||||
|
||||
// AMS
|
||||
auto ams_panel = new wxPanel(this, wxID_ANY);
|
||||
@@ -1440,10 +1496,6 @@ ExtruderGroup::ExtruderGroup(wxWindow * parent, int index, wxString const &title
|
||||
btn_down->Hide();
|
||||
|
||||
wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL);
|
||||
wxBoxSizer *hsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
hsizer->Add(combo_diameter, 1, wxRIGHT, FromDIP(5));
|
||||
hsizer->Add(combo_flow , 1);
|
||||
|
||||
vsizer->AddSpacer(FromDIP(16)); // spacing for title and control
|
||||
if (index < 0) {
|
||||
@@ -1452,14 +1504,137 @@ ExtruderGroup::ExtruderGroup(wxWindow * parent, int index, wxString const &title
|
||||
vsizer->Add(ams_panel, 0, wxEXPAND | wxLEFT | wxRIGHT , FromDIP(5));
|
||||
vsizer->AddSpacer(FromDIP(2));
|
||||
}
|
||||
vsizer->Add(hsizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5));
|
||||
|
||||
// Nozzle rows. The first one is the only one for a printer whose nozzles are not listed
|
||||
// individually; a multi-extruder printer grows one row per tool (see SetNozzleRowCount).
|
||||
// The rows are dealt into two columns, which halves the height of a long tool list; the second
|
||||
// column empties itself when there is a single row, so a one-row card still spans the width.
|
||||
auto rows_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
for (wxBoxSizer *&column : row_columns) {
|
||||
column = new wxBoxSizer(wxVERTICAL);
|
||||
rows_sizer->Add(column, 1, wxEXPAND);
|
||||
}
|
||||
vsizer->Add(rows_sizer, 0, wxEXPAND);
|
||||
sizer = vsizer; // the floating filament-switch icon positions itself against this card
|
||||
SetSizer(vsizer);
|
||||
SetNozzleRowCount(1);
|
||||
|
||||
Layout();
|
||||
|
||||
AMSCountPopupWindow::UpdateAMSCount(index < 0 ? 0 : index, this);
|
||||
}
|
||||
|
||||
ExtruderGroup::NozzleRow ExtruderGroup::create_nozzle_row()
|
||||
{
|
||||
const size_t row_index = rows.size();
|
||||
NozzleRow row;
|
||||
|
||||
row.prefix = new Label(this, Label::Body_10.Bold());
|
||||
row.prefix->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636")));
|
||||
row.prefix->SetLabelText(wxString::Format("T%d", int(row_index) + 1));
|
||||
row.prefix->Hide();
|
||||
|
||||
row.diameter_label = new Label(this, Label::Body_10);
|
||||
row.diameter_label->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636")));
|
||||
row.diameter_label->Hide();
|
||||
|
||||
auto combo_flow = new ComboBox(this, wxID_ANY, wxString(""), wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_READONLY);
|
||||
combo_flow->GetDropDown().SetUseContentWidth(true);
|
||||
combo_flow->Bind(wxEVT_COMBOBOX, [this, row_index, combo_flow](wxCommandEvent &evt) {
|
||||
// A one-row card edits the extruder it was created for, a multi-row card one extruder per row.
|
||||
const int index = nozzle_row_count > 1 ? int(row_index) : group_index;
|
||||
auto printer_tab = dynamic_cast<TabPrinter *>(wxGetApp().get_tab(Preset::TYPE_PRINTER));
|
||||
NozzleVolumeType volume_type = NozzleVolumeType(intptr_t(combo_flow->GetClientData(evt.GetInt())));
|
||||
printer_tab->set_extruder_volume_type(index, volume_type);
|
||||
auto plater = GUI::wxGetApp().plater();
|
||||
if (plater) {
|
||||
// A new Flow type invalidates the per-filament volume choices stored on the
|
||||
// plates for this extruder; rewrite them so the next apply/grouping sees the
|
||||
// selected volume instead of a stale one.
|
||||
plater->update_filament_volume_map(index, static_cast<int>(volume_type));
|
||||
plater->update_machine_sync_status();
|
||||
}
|
||||
});
|
||||
combo_flow->SetToolTip(_L("Flow"));
|
||||
row.flow = combo_flow;
|
||||
use_nozzle_row_font(combo_flow);
|
||||
|
||||
auto row_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
row_sizer->Add(row.prefix, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(3));
|
||||
if (row_index == 0)
|
||||
row_sizer->Add(combo_diameter, 1, wxRIGHT, FromDIP(5));
|
||||
row_sizer->Add(row.diameter_label, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(5));
|
||||
row_sizer->Add(row.flow, 1);
|
||||
// Row-major: T1 T2 on the first line, T3 T4 on the second, and so on.
|
||||
row_columns[row_index % 2]->Add(row_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5));
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
void ExtruderGroup::update_row_widths()
|
||||
{
|
||||
// Measured from the rows on screen rather than fixed, so no gap is reserved for a "T10" or a
|
||||
// "0.25 mm" that no row carries while the flow combos of a column still line up.
|
||||
wxSize prefix_width(0, -1);
|
||||
wxSize diameter_width(0, -1);
|
||||
for (const NozzleRow &row : rows) {
|
||||
if (row.prefix->IsShown())
|
||||
prefix_width.x = std::max(prefix_width.x, row.prefix->GetTextExtent(row.prefix->GetLabelText()).x);
|
||||
if (row.diameter_label->IsShown())
|
||||
diameter_width.x = std::max(diameter_width.x, row.diameter_label->GetTextExtent(row.diameter_label->GetLabelText()).x);
|
||||
}
|
||||
for (const NozzleRow &row : rows) {
|
||||
row.prefix->SetMinSize(prefix_width);
|
||||
row.diameter_label->SetMinSize(diameter_width);
|
||||
}
|
||||
}
|
||||
|
||||
void ExtruderGroup::SetRowDiameters(const std::vector<double> &diameters)
|
||||
{
|
||||
for (size_t i = 0; i < std::min(nozzle_row_count, diameters.size()); ++i) {
|
||||
// Spelled like the calibration dialogs spell a nozzle diameter, "0.4 mm".
|
||||
const wxString text = from_u8(get_diameter_string(diameters[i])) + " mm";
|
||||
rows[i].diameter_label->SetLabelText(text);
|
||||
rows[i].diameter_label->SetToolTip(text);
|
||||
}
|
||||
update_row_widths();
|
||||
Layout();
|
||||
}
|
||||
|
||||
void ExtruderGroup::SetNozzleRowCount(size_t count)
|
||||
{
|
||||
const size_t row_count = std::min(count, size_t(MAXIMUM_EXTRUDER_NUMBER));
|
||||
if (row_count == nozzle_row_count)
|
||||
return;
|
||||
nozzle_row_count = row_count;
|
||||
|
||||
// Every row names its tool as soon as there is more than one, so a single-nozzle card keeps the
|
||||
// plain "Nozzle" look. A row is shown by showing its widgets: a sizer item holding a sub-sizer
|
||||
// reports itself as shown while any of its windows is, so hiding the sub-sizer alone would
|
||||
// leave the row in the layout.
|
||||
for (size_t i = 0; i < row_count; ++i) {
|
||||
if (i >= rows.size())
|
||||
rows.push_back(create_nozzle_row());
|
||||
rows[i].prefix->Show(row_count > 1);
|
||||
rows[i].diameter_label->Show(group_index < 0); // the dual left/right cards show combo_diameter
|
||||
// The flow combo's own visibility is decided with its choices, see update_extruder_variant.
|
||||
}
|
||||
for (size_t i = row_count; i < rows.size(); ++i) {
|
||||
rows[i].prefix->Hide();
|
||||
rows[i].diameter_label->Hide();
|
||||
rows[i].flow->Hide();
|
||||
}
|
||||
|
||||
// The rows just changed: re-measure them, then let the card and its containers give it the new room.
|
||||
update_row_widths();
|
||||
Layout();
|
||||
if (wxWindow *parent = GetParent()) {
|
||||
parent->Layout();
|
||||
if (wxWindow *grand_parent = parent->GetParent())
|
||||
grand_parent->Layout();
|
||||
}
|
||||
}
|
||||
|
||||
void ExtruderGroup::update_ams()
|
||||
{
|
||||
static AMSinfo info4;
|
||||
@@ -1588,9 +1763,19 @@ bool Sidebar::priv::switch_diameter(bool single)
|
||||
diameter = diameter_left;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return switch_diameter_to(diameter);
|
||||
}
|
||||
|
||||
bool Sidebar::priv::switch_diameter_to(const wxString &diameter)
|
||||
{
|
||||
// ORCA: Check if the selected diameter matches the current nozzle diameter in the config
|
||||
Preset& printer_preset = wxGetApp().preset_bundle->printers.get_edited_preset();
|
||||
// The combo lists printer variants, and the variant of a mixed-nozzle machine ("0.4+0.6") is no
|
||||
// single extruder's diameter, so the preset's own variant answers first.
|
||||
if (printer_preset.config.opt_string("printer_variant") == diameter.ToStdString()) {
|
||||
return true;
|
||||
}
|
||||
auto* nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(printer_preset.config.option("nozzle_diameter"));
|
||||
if (nozzle_diameter && nozzle_diameter->size() > 0) {
|
||||
auto current_nozzle_dia = get_diameter_string(nozzle_diameter->values[0]);
|
||||
@@ -2679,7 +2864,8 @@ Sidebar::Sidebar(Plater *parent)
|
||||
p->combo_nozzle_dia->wxEvtHandler::ProcessEvent(evt);
|
||||
});
|
||||
|
||||
p->label_nozzle_title = new Label(p->panel_nozzle_dia, _L("Nozzle"), LB_PROPAGATE_MOUSE_EVENT);
|
||||
// "Variant", not "Nozzle": picking one switches the printer preset (see panel_nozzle_dia).
|
||||
p->label_nozzle_title = new Label(p->panel_nozzle_dia, _L("Variant"), LB_PROPAGATE_MOUSE_EVENT);
|
||||
p->label_nozzle_title->SetFont(Label::Body_10);
|
||||
|
||||
p->combo_nozzle_dia = new ComboBox(p->panel_nozzle_dia, wxID_ANY, wxString(""), wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_READONLY);
|
||||
@@ -2688,12 +2874,15 @@ Sidebar::Sidebar(Plater *parent)
|
||||
p->combo_nozzle_dia->SetMinSize(FromDIP(wxSize(PRINTER_PANEL_SIZE.GetWidth() - 4, 26))); // requires a static value in here
|
||||
p->combo_nozzle_dia->SetMaxSize(FromDIP(wxSize(PRINTER_PANEL_SIZE.GetWidth() - 4, 26))); // using -1 with wxEXPAND has issues
|
||||
p->combo_nozzle_dia->Bind(wxEVT_COMBOBOX, [this](auto &e) {
|
||||
auto evt_combo = (*p->single_extruder).combo_diameter;
|
||||
evt_combo->SetSelection(e.GetSelection());
|
||||
wxCommandEvent evt(wxEVT_COMBOBOX, evt_combo->GetId());
|
||||
evt.SetEventObject(evt_combo);
|
||||
evt.SetInt(e.GetSelection());
|
||||
wxPostEvent(evt_combo, evt);
|
||||
// This box is the machine-variant selector, so it switches the printer preset directly
|
||||
// instead of feeding an extruder card's combo. Deferred, because switching preset
|
||||
// rebuilds this very combo.
|
||||
const wxString diameter = p->combo_nozzle_dia->GetValue();
|
||||
p->combo_nozzle_dia->CallAfter([this, diameter]() {
|
||||
p->is_switching_diameter = true;
|
||||
p->switch_diameter_to(diameter);
|
||||
p->is_switching_diameter = false;
|
||||
});
|
||||
e.Skip();
|
||||
});
|
||||
// ORCA paint whole combobox on focus
|
||||
@@ -3673,8 +3862,16 @@ void Sidebar::update_presets(Preset::Type preset_type)
|
||||
auto diameters = wxGetApp().preset_bundle->printers.diameters_of_selected_printer();
|
||||
auto diameter = printer_preset.config.opt_string("printer_variant");
|
||||
auto extruder_max_nozzle_count = printer_preset.config.option<ConfigOptionIntsNullable>("extruder_max_nozzle_count");
|
||||
auto update_extruder_variant = [printer_model, extruders_def, extruders, nozzle_volumes_def, nozzle_volumes, extruder_variants,diameter,extruder_max_nozzle_count](ExtruderGroup & extruder, int index) {
|
||||
extruder.combo_flow->Clear();
|
||||
auto update_extruder_variant = [printer_model, extruders_def, extruders, nozzle_volumes_def, nozzle_volumes, extruder_variants,diameter,extruder_max_nozzle_count](ExtruderGroup & extruder, size_t row, int index) {
|
||||
ComboBox *combo_flow = extruder.rows[row].flow;
|
||||
combo_flow->Clear();
|
||||
// A profile may leave the per-extruder lists shorter than the nozzle count (they are
|
||||
// padded when the printer is loaded, but a hand-written preset need not be).
|
||||
if (index >= int(extruder_variants->values.size()) || index >= int(extruders->values.size()) ||
|
||||
index >= int(nozzle_volumes->values.size())) {
|
||||
combo_flow->Hide();
|
||||
return;
|
||||
}
|
||||
auto type = extruders_def->enum_labels[extruders->values[index]];
|
||||
int select = -1;
|
||||
for (size_t i = 0; i < nozzle_volumes_def->enum_labels.size(); ++i) {
|
||||
@@ -3684,26 +3881,36 @@ void Sidebar::update_presets(Preset::Type preset_type)
|
||||
if (boost::algorithm::contains(extruder_variants->values[index], type + " " + nozzle_volumes_def->enum_labels[i]) ||
|
||||
(extruder_max_nozzle_count->get_at(index) > 1 && extruder_max_nozzle_count->get_at(index) != ConfigOptionIntsNullable::nil_value() &&
|
||||
nozzle_volumes_def->enum_keys_map->at(nozzle_volumes_def->enum_values[i]) == nvtHybrid)) {
|
||||
if (nozzle_volumes_def->enum_keys_map->at(nozzle_volumes_def->enum_values[i]) == NozzleVolumeType::nvtHighFlow &&(diameter == "0.2" ||
|
||||
auto cur_volume_type = nozzle_volumes_def->enum_keys_map->at(nozzle_volumes_def->enum_values[i]);
|
||||
// Defensive: profiles restrict E3D to 0.4 / 0.6; keep it out elsewhere.
|
||||
if (cur_volume_type == NozzleVolumeType::nvtE3DHighFlow && diameter != "0.4" && diameter != "0.6")
|
||||
continue;
|
||||
if (cur_volume_type == NozzleVolumeType::nvtHighFlow && (diameter == "0.2" ||
|
||||
is_skip_high_flow_printer(printer_model)))
|
||||
continue;
|
||||
if (nozzle_volumes->values[index] == i)
|
||||
select = extruder.combo_flow->GetCount();
|
||||
extruder.combo_flow->Append(_L(nozzle_volumes_def->enum_labels[i]), {}, (void*)i);
|
||||
// The client data is the enum value, not the label position: E3D High Flow is 5
|
||||
// but sits at position 4, and a position would write an invalid type.
|
||||
if (nozzle_volumes->values[index] == cur_volume_type)
|
||||
select = combo_flow->GetCount();
|
||||
combo_flow->Append(_L(nozzle_volumes_def->enum_labels[i]), {}, (void*)(intptr_t)cur_volume_type);
|
||||
}
|
||||
}
|
||||
if (select == -1)
|
||||
select = extruder.combo_flow->GetCount() - 1;
|
||||
extruder.combo_flow->SetSelection(select);
|
||||
select = combo_flow->GetCount() - 1;
|
||||
combo_flow->SetSelection(select);
|
||||
// No flow variant for this extruder: the diameter keeps the whole row.
|
||||
combo_flow->Show(combo_flow->GetCount() > 0);
|
||||
};
|
||||
|
||||
auto update_extruder_diameter = [&diameters, &nozzle_diameter](int extruder_index,ExtruderGroup & extruder) {
|
||||
extruder.combo_diameter->Clear();
|
||||
if (extruder_index >= int(nozzle_diameter->values.size()))
|
||||
return;
|
||||
int select = -1;
|
||||
// ORCA get the actual nozzle diameter from printer config
|
||||
auto nozzle_dia = get_diameter_string(nozzle_diameter->values[extruder_index]);
|
||||
// ORCA try to add nozzle diameter from config if list is empty. fixes blank nozzle combo box when preset has no alias
|
||||
if(diameters[0].empty() && !nozzle_dia.empty()){
|
||||
if(!diameters.empty() && diameters[0].empty() && !nozzle_dia.empty()){
|
||||
diameters[0] = nozzle_dia;
|
||||
}
|
||||
// Orca: Check if the actual nozzle diameter exists in the list, if not add it as a custom option
|
||||
@@ -3719,14 +3926,15 @@ void Sidebar::update_presets(Preset::Type preset_type)
|
||||
extruder.diameter = nozzle_dia;
|
||||
};
|
||||
auto image_path = get_cur_select_bed_image();
|
||||
if (is_dual_extruder) {
|
||||
const bool multi_extruder_rows = p->single_extruder->NozzleRowCount() > 1;
|
||||
if (is_dual_extruder && !multi_extruder_rows) {
|
||||
std::string printer_type = printer_preset.get_printer_type(wxGetApp().preset_bundle);
|
||||
p->left_extruder->SetTitle(_L(DevPrinterConfigUtil::get_toolhead_display_name(printer_type, DEPUTY_EXTRUDER_ID, ToolHeadComponent::Nozzle, ToolHeadNameCase::TitleCase)));
|
||||
p->right_extruder->SetTitle(_L(DevPrinterConfigUtil::get_toolhead_display_name(printer_type, MAIN_EXTRUDER_ID, ToolHeadComponent::Nozzle, ToolHeadNameCase::TitleCase)));
|
||||
AMSCountPopupWindow::UpdateAMSCount(0, p->left_extruder);
|
||||
AMSCountPopupWindow::UpdateAMSCount(1, p->right_extruder);
|
||||
update_extruder_variant(*p->left_extruder, 0);
|
||||
update_extruder_variant(*p->right_extruder, 1);
|
||||
update_extruder_variant(*p->left_extruder, 0, 0);
|
||||
update_extruder_variant(*p->right_extruder, 0, 1);
|
||||
//if (!p->is_switching_diameter) {
|
||||
update_extruder_diameter(0, *p->left_extruder);
|
||||
update_extruder_diameter(1, *p->right_extruder);
|
||||
@@ -3734,15 +3942,27 @@ void Sidebar::update_presets(Preset::Type preset_type)
|
||||
p->image_printer_bed->SetBitmap(create_scaled_bitmap(image_path, this, PRINTER_THUMBNAIL_SIZE.GetHeight()));
|
||||
} else {
|
||||
AMSCountPopupWindow::UpdateAMSCount(0, p->single_extruder);
|
||||
update_extruder_variant(*p->single_extruder, 0);
|
||||
for (size_t row = 0; row < p->single_extruder->NozzleRowCount(); ++row)
|
||||
update_extruder_variant(*p->single_extruder, row, int(row));
|
||||
//if (!p->is_switching_diameter)
|
||||
update_extruder_diameter(0, *p->single_extruder);
|
||||
// The card reads the nozzles out, the variant box above is what switches the machine.
|
||||
p->single_extruder->SetRowDiameters(nozzle_diameter->values);
|
||||
if (multi_extruder_rows) {
|
||||
// The hidden left/right cards keep mirroring the first extruders' diameters: the
|
||||
// device sync path (a connected Bambu printer or a printer agent) still reads them.
|
||||
update_extruder_diameter(0, *p->left_extruder);
|
||||
update_extruder_diameter(1, *p->right_extruder);
|
||||
}
|
||||
|
||||
// ORCA sync unified nozzle combo box
|
||||
p->combo_nozzle_dia->Clear();
|
||||
for (size_t i = 0; i < diameters.size(); ++i)
|
||||
p->combo_nozzle_dia->Append(diameters[i], {});
|
||||
p->combo_nozzle_dia->SetSelection((*p->single_extruder).combo_diameter->GetSelection());
|
||||
// Prefer the variant the preset names: a mixed-nozzle machine reads "0.4+0.6", which is
|
||||
// no single extruder's diameter.
|
||||
const int variant = p->combo_nozzle_dia->FindString(diameter);
|
||||
p->combo_nozzle_dia->SetSelection(variant != wxNOT_FOUND ? variant : (*p->single_extruder).combo_diameter->GetSelection());
|
||||
|
||||
// ORCA update nozzle type
|
||||
const auto& full_config = wxGetApp().preset_bundle->full_config();
|
||||
|
||||
@@ -1793,6 +1793,7 @@ wxString PrinterPartsDialog::GetString(NozzleFlowType nozzle_flow_type) const {
|
||||
case Slic3r::S_FLOW: return _L("Standard");
|
||||
case Slic3r::H_FLOW: return _L("High flow");
|
||||
case Slic3r::U_FLOW: return _L("TPU High flow");
|
||||
case Slic3r::E_FLOW: return _L("E3D High Flow");
|
||||
default: break;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,6 +75,9 @@ std::string get_nozzle_volume_type_cloud_string(NozzleVolumeType nozzle_volume_t
|
||||
else if (nozzle_volume_type == NozzleVolumeType::nvtTPUHighFlow) {
|
||||
return "tpu_high_flow";
|
||||
}
|
||||
else if (nozzle_volume_type == NozzleVolumeType::nvtE3DHighFlow) {
|
||||
return "e3d_high_flow";
|
||||
}
|
||||
else if (nozzle_volume_type == NozzleVolumeType::nvtHybrid) {
|
||||
// to be supported
|
||||
return "hybrid_flow";
|
||||
|
||||
@@ -1516,6 +1516,8 @@ bool SyncAmsInfoDialog::is_nozzle_type_match(DevExtderSystem data, wxString &err
|
||||
used_extruders_flow[used_extruders[i]] = "Standard";
|
||||
} else if (nozzle_volume_type == NozzleVolumeType::nvtTPUHighFlow) {
|
||||
used_extruders_flow[used_extruders[i]] = "TPU High Flow";
|
||||
} else if (nozzle_volume_type == NozzleVolumeType::nvtE3DHighFlow) {
|
||||
used_extruders_flow[used_extruders[i]] = "E3D High Flow";
|
||||
} else {
|
||||
used_extruders_flow[used_extruders[i]] = "High Flow";
|
||||
}
|
||||
@@ -1533,6 +1535,8 @@ bool SyncAmsInfoDialog::is_nozzle_type_match(DevExtderSystem data, wxString &err
|
||||
flow_type_of_machine.push_back("Standard");
|
||||
} else if (it->GetNozzleFlowType() == NozzleFlowType::U_FLOW) {
|
||||
flow_type_of_machine.push_back("TPU High Flow");
|
||||
} else if (it->GetNozzleFlowType() == NozzleFlowType::E_FLOW) {
|
||||
flow_type_of_machine.push_back("E3D High Flow");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+41
-12
@@ -506,7 +506,7 @@ void Tab::create_preset_tab()
|
||||
|
||||
if (dynamic_cast<TabPrinter *>(this) || dynamic_cast<TabPrint *>(this)) {
|
||||
m_extruder_switch = new MultiSwitchButton(panel);
|
||||
m_extruder_switch->SetMaxSize({em_unit(this) * 40, -1});
|
||||
m_extruder_switch->SetFitToOptions();
|
||||
m_extruder_switch->Bind(wxCUSTOMEVT_MULTISWITCH_SELECTION, [this](auto &evt) {
|
||||
evt.Skip();
|
||||
int selection = evt.GetInt();
|
||||
@@ -539,7 +539,8 @@ void Tab::create_preset_tab()
|
||||
auto right_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
m_variant_sizer->AddStretchSpacer(1);
|
||||
m_variant_sizer->Add(m_extruder_switch, 0, wxALIGN_CENTER, 0);
|
||||
// Orca: proportion 1 lets a narrow row squeeze the switch, which then scrolls its buttons.
|
||||
m_variant_sizer->Add(m_extruder_switch, 1, wxALIGN_CENTER, 0);
|
||||
m_variant_sizer->Add(right_sizer, 1, wxALIGN_CENTER);
|
||||
right_sizer->AddStretchSpacer(1);
|
||||
right_sizer->Add(m_extruder_sync_box, 0, wxALIGN_CENTER | wxRIGHT, m_em_unit);
|
||||
@@ -8172,7 +8173,7 @@ void Tab::update_extruder_variants(int extruder_id, bool reload)
|
||||
|
||||
// Orca: a non-Bambu dual-nozzle printer has two extruders but a single variant column, so
|
||||
// the nozzle switch and sync button have nothing to act on. Only enable with real variants.
|
||||
if (extruder_nums == 2 && m_preset_bundle->support_different_extruders()) {
|
||||
if (extruder_nums >= 2 && m_preset_bundle->support_different_extruders()) {
|
||||
auto options = generate_extruder_options();
|
||||
m_extruder_switch->SetOptions(options);
|
||||
|
||||
@@ -8219,6 +8220,23 @@ void Tab::update_extruder_variants(int extruder_id, bool reload)
|
||||
}
|
||||
}
|
||||
|
||||
// The variant switch tags are the narrowest place a volume type is named, so they abbreviate it;
|
||||
// the flow combo boxes and the Printer tab's parameter labels keep the full names.
|
||||
static wxString short_nozzle_volume_name(const std::string &volume_name)
|
||||
{
|
||||
// Hybrid has no entry on purpose: it is never a variant string, and the switch lists its two
|
||||
// sub-nozzle types as separate tags (see generate_extruder_options).
|
||||
if (volume_name == "Standard")
|
||||
return "SF";
|
||||
if (volume_name == "High Flow")
|
||||
return "HF";
|
||||
if (volume_name == "TPU High Flow")
|
||||
return "TPU HF";
|
||||
if (volume_name == "E3D High Flow")
|
||||
return "E3D HF";
|
||||
return from_u8(volume_name);
|
||||
}
|
||||
|
||||
std::vector<wxString> Tab::generate_extruder_options()
|
||||
{
|
||||
std::vector<wxString> options;
|
||||
@@ -8259,7 +8277,7 @@ std::vector<wxString> Tab::generate_extruder_options()
|
||||
nozzle = "";
|
||||
}
|
||||
}
|
||||
options.push_back(wxString::Format(_L("%s: %s"), _L(drive), _L(nozzle)));
|
||||
options.push_back(wxString::Format(_L("%s: %s"), _L(drive), short_nozzle_volume_name(nozzle)));
|
||||
}
|
||||
return options;
|
||||
}
|
||||
@@ -8273,18 +8291,22 @@ std::vector<wxString> Tab::generate_extruder_options()
|
||||
}
|
||||
|
||||
std::string pt = m_preset_bundle->printers.get_edited_preset().get_printer_type(m_preset_bundle);
|
||||
// Orca: the main/deputy toolhead names describe a dual-nozzle printer, where extruder 0 is the
|
||||
// left (deputy) and extruder 1 the right (main) nozzle. From three extruders on the tools are
|
||||
// interchangeable, so name them by index instead of repeating one side.
|
||||
for (int i = 0; i < extruder_nums; ++i) {
|
||||
int ext_id = (i == 0) ? DEPUTY_EXTRUDER_ID : MAIN_EXTRUDER_ID;
|
||||
wxString extruder_name = _L(DevPrinterConfigUtil::get_toolhead_display_name(
|
||||
pt, ext_id, ToolHeadComponent::Nozzle, ToolHeadNameCase::TitleCase, true));
|
||||
wxString extruder_name = extruder_nums > 2 ? wxString::Format("T%d", i + 1) :
|
||||
_L(DevPrinterConfigUtil::get_toolhead_display_name(
|
||||
pt, (i == 0) ? DEPUTY_EXTRUDER_ID : MAIN_EXTRUDER_ID,
|
||||
ToolHeadComponent::Nozzle, ToolHeadNameCase::TitleCase, true));
|
||||
NozzleVolumeType volume_type = NozzleVolumeType(nozzle_volumes->values[i]);
|
||||
|
||||
if (volume_type == NozzleVolumeType::nvtHybrid) {
|
||||
options.push_back(wxString::Format(_L("%s: %s"), extruder_name, _L("Standard")));
|
||||
options.push_back(wxString::Format(_L("%s: %s"), extruder_name, _L("High Flow")));
|
||||
options.push_back(wxString::Format(_L("%s: %s"), extruder_name, short_nozzle_volume_name(get_nozzle_volume_type_string(NozzleVolumeType::nvtStandard))));
|
||||
options.push_back(wxString::Format(_L("%s: %s"), extruder_name, short_nozzle_volume_name(get_nozzle_volume_type_string(NozzleVolumeType::nvtHighFlow))));
|
||||
} else {
|
||||
wxString volume_name = get_nozzle_volume_type_name(volume_type);
|
||||
options.push_back(wxString::Format(_L("%s: %s"), extruder_name, volume_name));
|
||||
options.push_back(wxString::Format(_L("%s: %s"), extruder_name,
|
||||
short_nozzle_volume_name(get_nozzle_volume_type_string(volume_type))));
|
||||
}
|
||||
}
|
||||
return options;
|
||||
@@ -8315,7 +8337,10 @@ bool Tab::get_extruder_sync_enable_state(int extruder_id)
|
||||
Preset& printer_preset = m_preset_bundle->printers.get_edited_preset();
|
||||
auto nozzle_volumes = m_preset_bundle->project_config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type");
|
||||
auto extruders = printer_preset.config.option<ConfigOptionEnumsGeneric>("extruder_type");
|
||||
if (nozzle_volumes->values.size() < 2 || extruders->values.size() < 2) {
|
||||
// Orca: every rule below describes the two toolheads of a dual-nozzle printer as left/right.
|
||||
// A printer with any other extruder count has no single counterpart to copy to, so it gets no
|
||||
// sync button (a toolchanger would need a target to be chosen).
|
||||
if (nozzle_volumes->values.size() != 2 || extruders->values.size() != 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -8456,6 +8481,10 @@ void Tab::sync_excluder()
|
||||
ExtruderType(extruders->values[extruder_id]), nozzle_type, variant_keys.second, stride);
|
||||
};
|
||||
int active_index = get_current_active_extruder();
|
||||
// The button copies to the other toolhead, so it is only offered when that other one exists;
|
||||
// without this the `1 - active_index` below would index an extruder that is not there.
|
||||
if (!get_extruder_sync_enable_state(active_index))
|
||||
return;
|
||||
auto active_nozzle = get_actual_nozzle_volume_type(active_index);
|
||||
int from_index = get_index_for_extruder(active_index, active_nozzle);
|
||||
int dest_index = get_index_for_extruder(1 - active_index, active_nozzle);
|
||||
|
||||
@@ -70,8 +70,8 @@ int getExtruderNozzleCountTotal(PresetBundle *preset_bundle, int extruder_id)
|
||||
|
||||
// ---- ManualNozzleCountDialog ----------------------------------------------------------------------------------
|
||||
|
||||
ManualNozzleCountDialog::ManualNozzleCountDialog(
|
||||
wxWindow *parent, NozzleVolumeType volume_type, int standard_count, int highflow_count, int max_nozzle_count, bool force_no_zero)
|
||||
ManualNozzleCountDialog::ManualNozzleCountDialog(wxWindow *parent, NozzleVolumeType volume_type, int standard_count, int highflow_count, int e3d_count,
|
||||
int max_nozzle_count, bool force_no_zero, const std::set<NozzleVolumeType> &supported_types)
|
||||
: DPIDialog(parent, wxID_ANY, _L("Set nozzle count"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
@@ -93,43 +93,39 @@ ManualNozzleCountDialog::ManualNozzleCountDialog(
|
||||
for (int i = 0; i <= max_nozzle_count; ++i)
|
||||
nozzle_choices.Add(wxString::Format("%d", i));
|
||||
|
||||
// A Hybrid extruder mixes Standard and High Flow nozzles, so it gets both count choices; the concrete
|
||||
// types get exactly one.
|
||||
if (volume_type == nvtStandard || volume_type == nvtHybrid) {
|
||||
wxBoxSizer *standard_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto standard_label = new wxStaticText(content, wxID_ANY, _L(get_nozzle_volume_type_string(nvtStandard)), wxDefaultPosition, wxSize(FromDIP(100), -1));
|
||||
standard_label->SetFont(Label::Body_14);
|
||||
standard_label->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636")));
|
||||
standard_sizer->Add(standard_label, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_standard_choice = new ComboBox(content, wxID_ANY, "", wxDefaultPosition, wxSize(FromDIP(80), -1), 0, nullptr, wxCB_READONLY);
|
||||
std::vector<wxString>::iterator iter;
|
||||
for (iter = nozzle_choices.begin(); iter != nozzle_choices.end(); iter++)
|
||||
m_standard_choice->Append(*iter);
|
||||
m_standard_choice->SetSelection(standard_count);
|
||||
standard_sizer->Add(m_standard_choice, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(5));
|
||||
choice_sizer->Add(standard_sizer, 0, wxTOP | wxRIGHT, FromDIP(15));
|
||||
}
|
||||
if (volume_type == nvtHighFlow || volume_type == nvtHybrid) {
|
||||
wxBoxSizer *highflow_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto highflow_label = new wxStaticText(content, wxID_ANY, _L(get_nozzle_volume_type_string(nvtHighFlow)), wxDefaultPosition, wxSize(FromDIP(100), -1));
|
||||
highflow_label->SetFont(Label::Body_14);
|
||||
highflow_label->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636")));
|
||||
highflow_sizer->Add(highflow_label, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_highflow_choice = new ComboBox(content, wxID_ANY, "", wxDefaultPosition, wxSize(FromDIP(80), -1), 0, nullptr, wxCB_READONLY);
|
||||
std::vector<wxString>::iterator iter;
|
||||
for (iter = nozzle_choices.begin(); iter != nozzle_choices.end(); iter++)
|
||||
m_highflow_choice->Append(*iter);
|
||||
m_highflow_choice->SetSelection(highflow_count);
|
||||
highflow_sizer->Add(m_highflow_choice, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(5));
|
||||
choice_sizer->Add(highflow_sizer, 0, wxTOP | wxRIGHT, FromDIP(15));
|
||||
}
|
||||
// A Hybrid extruder mixes several nozzle volume types, so it gets a count choice for each type the
|
||||
// extruder actually has; a concrete type gets exactly its own, which it supports by definition.
|
||||
auto shows = [volume_type, &supported_types](NozzleVolumeType type) {
|
||||
return volume_type == type || (volume_type == nvtHybrid && (supported_types.empty() || supported_types.count(type) > 0));
|
||||
};
|
||||
auto add_count_choice = [this, content, choice_sizer, &nozzle_choices](NozzleVolumeType type, int count) {
|
||||
wxBoxSizer *row_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto label = new wxStaticText(content, wxID_ANY, _L(get_nozzle_volume_type_string(type)), wxDefaultPosition, wxSize(FromDIP(100), -1));
|
||||
label->SetFont(Label::Body_14);
|
||||
label->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636")));
|
||||
row_sizer->Add(label, 0, wxALIGN_CENTER_VERTICAL);
|
||||
auto choice = new ComboBox(content, wxID_ANY, "", wxDefaultPosition, wxSize(FromDIP(80), -1), 0, nullptr, wxCB_READONLY);
|
||||
for (const wxString &item : nozzle_choices)
|
||||
choice->Append(item);
|
||||
choice->SetSelection(count);
|
||||
row_sizer->Add(choice, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(5));
|
||||
choice_sizer->Add(row_sizer, 0, wxTOP | wxRIGHT, FromDIP(15));
|
||||
return choice;
|
||||
};
|
||||
if (shows(nvtStandard))
|
||||
m_standard_choice = add_count_choice(nvtStandard, standard_count);
|
||||
if (shows(nvtHighFlow))
|
||||
m_highflow_choice = add_count_choice(nvtHighFlow, highflow_count);
|
||||
if (shows(nvtE3DHighFlow))
|
||||
m_e3d_choice = add_count_choice(nvtE3DHighFlow, e3d_count);
|
||||
|
||||
m_error_label = new wxStaticText(this, wxID_ANY, "");
|
||||
m_error_label->SetForegroundColour(wxColour("#E14747"));
|
||||
m_error_label->Hide();
|
||||
|
||||
auto update_nozzle_error = [this, force_no_zero, content, max_nozzle_count](int standard_count, int highflow_count) {
|
||||
const int total_count = standard_count + highflow_count;
|
||||
auto update_nozzle_error = [this, force_no_zero, content, max_nozzle_count](wxCommandEvent &e) {
|
||||
// The Hybrid count is the sum over every choice on screen, whatever the extruder's own type.
|
||||
const int total_count = GetNozzleCount(nvtHybrid);
|
||||
if (0 < total_count && total_count <= max_nozzle_count && m_error_label->IsShown()) {
|
||||
m_error_label->Hide();
|
||||
m_confirm_btn->Enable();
|
||||
@@ -145,18 +141,12 @@ ManualNozzleCountDialog::ManualNozzleCountDialog(
|
||||
Layout();
|
||||
Fit();
|
||||
}
|
||||
e.Skip();
|
||||
};
|
||||
|
||||
if (m_standard_choice)
|
||||
m_standard_choice->Bind(wxEVT_COMBOBOX, [this, update_nozzle_error](wxCommandEvent &e) {
|
||||
update_nozzle_error(m_standard_choice->GetSelection(), m_highflow_choice ? m_highflow_choice->GetSelection() : 0);
|
||||
e.Skip();
|
||||
});
|
||||
if (m_highflow_choice)
|
||||
m_highflow_choice->Bind(wxEVT_COMBOBOX, [this, update_nozzle_error](wxCommandEvent &e) {
|
||||
update_nozzle_error(m_standard_choice ? m_standard_choice->GetSelection() : 0, m_highflow_choice->GetSelection());
|
||||
e.Skip();
|
||||
});
|
||||
for (ComboBox *choice : {m_standard_choice, m_highflow_choice, m_e3d_choice})
|
||||
if (choice)
|
||||
choice->Bind(wxEVT_COMBOBOX, update_nozzle_error);
|
||||
|
||||
content_sizer->Add(nozzle_icon, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(15));
|
||||
content_sizer->Add(choice_sizer, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
@@ -181,9 +171,10 @@ int ManualNozzleCountDialog::GetNozzleCount(NozzleVolumeType volume_type) const
|
||||
return m_standard_choice ? m_standard_choice->GetSelection() : 0;
|
||||
if (volume_type == nvtHighFlow)
|
||||
return m_highflow_choice ? m_highflow_choice->GetSelection() : 0;
|
||||
if (volume_type == nvtE3DHighFlow)
|
||||
return m_e3d_choice ? m_e3d_choice->GetSelection() : 0;
|
||||
if (volume_type == nvtHybrid)
|
||||
return (m_standard_choice ? m_standard_choice->GetSelection() : 0) +
|
||||
(m_highflow_choice ? m_highflow_choice->GetSelection() : 0);
|
||||
return GetNozzleCount(nvtStandard) + GetNozzleCount(nvtHighFlow) + GetNozzleCount(nvtE3DHighFlow);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -318,6 +309,7 @@ void manuallySetNozzleCount(int extruder_id)
|
||||
const NozzleVolumeType volume_type = NozzleVolumeType(nozzle_volume_type_opt->values[extruder_id]);
|
||||
const int standard_count = getExtruderNozzleCount(preset_bundle, extruder_id, nvtStandard);
|
||||
const int highflow_count = getExtruderNozzleCount(preset_bundle, extruder_id, nvtHighFlow);
|
||||
const int e3d_count = getExtruderNozzleCount(preset_bundle, extruder_id, nvtE3DHighFlow);
|
||||
|
||||
// Require at least one nozzle for a Hybrid extruder (an empty mix is meaningless) and when the other
|
||||
// extruder currently has none.
|
||||
@@ -325,11 +317,19 @@ void manuallySetNozzleCount(int extruder_id)
|
||||
if (nozzle_volume_type_opt->values.size() > 1)
|
||||
force_no_zero |= getExtruderNozzleCountTotal(preset_bundle, 1 - extruder_id) == 0;
|
||||
|
||||
ManualNozzleCountDialog dialog(wxGetApp().plater(), volume_type, standard_count, highflow_count, max_nozzle_count->values[extruder_id], force_no_zero);
|
||||
// The printer profile's variant list is the authority on which volume types an extruder provides. An
|
||||
// empty set means the profile could not be read, and the dialog then skips filtering instead of hiding
|
||||
// everything.
|
||||
const std::set<NozzleVolumeType> supported_types =
|
||||
get_extruder_supported_nozzle_volume_types(preset_bundle->printers.get_edited_preset().config, extruder_id);
|
||||
|
||||
ManualNozzleCountDialog dialog(wxGetApp().plater(), volume_type, standard_count, highflow_count, e3d_count,
|
||||
max_nozzle_count->values[extruder_id], force_no_zero, supported_types);
|
||||
if (dialog.ShowModal() == wxID_OK) {
|
||||
if (volume_type == nvtHybrid) {
|
||||
setExtruderNozzleCount(preset_bundle, extruder_id, nvtStandard, dialog.GetNozzleCount(nvtStandard), true);
|
||||
setExtruderNozzleCount(preset_bundle, extruder_id, nvtHighFlow, dialog.GetNozzleCount(nvtHighFlow), false);
|
||||
setExtruderNozzleCount(preset_bundle, extruder_id, nvtE3DHighFlow, dialog.GetNozzleCount(nvtE3DHighFlow), false);
|
||||
} else {
|
||||
setExtruderNozzleCount(preset_bundle, extruder_id, volume_type, dialog.GetNozzleCount(volume_type), true);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
@@ -64,7 +65,9 @@ struct NozzleOption
|
||||
class ManualNozzleCountDialog : public DPIDialog
|
||||
{
|
||||
public:
|
||||
ManualNozzleCountDialog(wxWindow *parent, NozzleVolumeType volume_type, int standard_count, int highflow_count, int max_nozzle_count, bool force_no_zero);
|
||||
// supported_types lists the volume types the extruder physically provides; an empty set means "unknown", in which case no filtering is applied.
|
||||
ManualNozzleCountDialog(wxWindow *parent, NozzleVolumeType volume_type, int standard_count, int highflow_count, int e3d_count, int max_nozzle_count, bool force_no_zero,
|
||||
const std::set<NozzleVolumeType> &supported_types);
|
||||
~ManualNozzleCountDialog() override = default;
|
||||
void on_dpi_changed(const wxRect &suggested_rect) override {}
|
||||
int GetNozzleCount(NozzleVolumeType volume_type) const;
|
||||
@@ -72,6 +75,7 @@ public:
|
||||
private:
|
||||
ComboBox *m_standard_choice{nullptr};
|
||||
ComboBox *m_highflow_choice{nullptr};
|
||||
ComboBox *m_e3d_choice{nullptr};
|
||||
Button *m_confirm_btn{nullptr};
|
||||
wxStaticText *m_error_label{nullptr};
|
||||
};
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "StaticBox.hpp"
|
||||
|
||||
#include "../wxExtensions.hpp"
|
||||
|
||||
#include <wx/settings.h>
|
||||
#include "../GUI_App.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "../Utils/MacDarkMode.hpp"
|
||||
@@ -620,19 +622,34 @@ MultiSwitchButton::MultiSwitchButton(wxWindow *parent, wxWindowID id, const wxPo
|
||||
std::make_pair(0x6B6B6B, (int) StateColor::NotChecked),
|
||||
std::make_pair(0xFFFFFE, (int) StateColor::Normal)))
|
||||
, m_button_radius(10.0)
|
||||
, m_button_padding(10, 6)
|
||||
, m_button_padding(FromDIP(wxSize(11, 3)))
|
||||
{
|
||||
SetCornerRadius(m_button_radius);
|
||||
SetBorderWidth(0);
|
||||
|
||||
sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto *hsizer = new wxBoxSizer(wxVERTICAL);
|
||||
hsizer->Add(sizer, 1, wxEXPAND);
|
||||
SetSizer(hsizer);
|
||||
SetMinSize(wxSize(-1, 20));
|
||||
// Orca: a switch can hold more buttons than the layout has room for (a toolchanger lists one per
|
||||
// tool), so they live in a scrolled area: the caller caps the switch at its natural width and
|
||||
// this scrolls horizontally instead of clipping the last buttons.
|
||||
m_scroll = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL | wxBORDER_NONE);
|
||||
m_scroll->SetBackgroundColour(GetBackgroundColour());
|
||||
// The buttons are a single row, so only the horizontal bar may ever appear: a vertical one would
|
||||
// eat into the row's height.
|
||||
m_scroll->ShowScrollbars(wxSHOW_SB_DEFAULT, wxSHOW_SB_NEVER);
|
||||
m_scroll->EnableScrolling(true, false);
|
||||
m_scroll->SetScrollRate(FromDIP(10), 0);
|
||||
|
||||
sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_scroll->SetSizer(sizer);
|
||||
|
||||
auto *hsizer = new wxBoxSizer(wxVERTICAL);
|
||||
hsizer->Add(m_scroll, 1, wxEXPAND);
|
||||
SetSizer(hsizer);
|
||||
SetMinSize(wxSize(-1, options_height()));
|
||||
|
||||
Bind(wxEVT_SIZE, &MultiSwitchButton::on_size, this);
|
||||
Bind(wxEVT_COMMAND_BUTTON_CLICKED, &MultiSwitchButton::button_clicked, this);
|
||||
SetFont(Label::Body_12);
|
||||
// The tags name a tool and its volume type only, so they stay compact.
|
||||
SetFont(Label::Body_10);
|
||||
}
|
||||
|
||||
MultiSwitchButton::~MultiSwitchButton()
|
||||
@@ -640,10 +657,88 @@ MultiSwitchButton::~MultiSwitchButton()
|
||||
DeleteAllOptions();
|
||||
}
|
||||
|
||||
int MultiSwitchButton::options_height() const
|
||||
{
|
||||
// With no button to measure yet, keep the placeholder height the switch starts with.
|
||||
return btns.empty() ? FromDIP(20) : btns.front()->GetMinSize().y;
|
||||
}
|
||||
|
||||
void MultiSwitchButton::update_scroll_range()
|
||||
{
|
||||
// The scrollbar range is measured against the virtual size, so it has to follow the buttons
|
||||
// whenever their labels or count change.
|
||||
m_scroll->InvalidateBestSize();
|
||||
m_scroll->FitInside();
|
||||
|
||||
// A scrolled window reports its min size plus a scrollbar as its best size, never the width of
|
||||
// the buttons it holds, so the layout has to be given that width explicitly. It is also the
|
||||
// widest this switch wants to be: a row with less room squeezes it below this and it scrolls.
|
||||
const wxSize content = sizer->CalcMin();
|
||||
SetMinSize(wxSize(content.x, options_height() + scrollbar_height(content.x)));
|
||||
SetMaxSize(m_fit_to_options ? wxSize(content.x, -1) : wxDefaultSize);
|
||||
InvalidateBestSize();
|
||||
}
|
||||
|
||||
int MultiSwitchButton::scrollbar_height(int options_width) const
|
||||
{
|
||||
// The bar is drawn inside the switch, so while the buttons need more width than the row gave us
|
||||
// the switch has to be taller by the bar's height, or the bar sits on top of the buttons.
|
||||
const int width = GetClientSize().x;
|
||||
if (width <= 0 || options_width <= width)
|
||||
return 0;
|
||||
|
||||
const int bar = wxSystemSettings::GetMetric(wxSYS_HSCROLL_Y, this);
|
||||
return bar > 0 ? bar : 0;
|
||||
}
|
||||
|
||||
void MultiSwitchButton::on_size(wxSizeEvent &evt)
|
||||
{
|
||||
evt.Skip();
|
||||
|
||||
// The row resized, so the buttons may now overflow it (or no longer fit in it) and the room the
|
||||
// bar needs changed with that. Width does not depend on height, so this settles in one pass.
|
||||
const int height = options_height() + scrollbar_height(sizer->CalcMin().x);
|
||||
if (GetMinSize().y != height) {
|
||||
SetMinSize(wxSize(GetMinSize().x, height));
|
||||
// The switch sits in a row of this tab, and the tab in a panel that shares its height
|
||||
// with the page view, so both have to lay out again for the taller row to get its room.
|
||||
if (wxWindow *tab = GetParent()) {
|
||||
tab->Layout();
|
||||
if (wxWindow *panel = tab->GetParent())
|
||||
panel->Layout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MultiSwitchButton::scroll_option_into_view(Button *btn)
|
||||
{
|
||||
const int width = m_scroll->GetClientSize().x;
|
||||
const int view_x = m_scroll->GetViewStartPixels().x;
|
||||
if (width <= 0) // not laid out yet: there is no view to scroll
|
||||
return;
|
||||
|
||||
const wxRect rect = btn->GetRect();
|
||||
const int right = rect.GetRight() + 1;
|
||||
int target = view_x;
|
||||
if (rect.x < view_x)
|
||||
target = rect.x;
|
||||
else if (right > view_x + width)
|
||||
target = right - width;
|
||||
|
||||
if (target == view_x)
|
||||
return;
|
||||
|
||||
// Scroll() counts scroll units; round up so the whole button ends up inside the view rather
|
||||
// than a few pixels short of it.
|
||||
int step = 1;
|
||||
m_scroll->GetScrollPixelsPerUnit(&step, nullptr);
|
||||
m_scroll->Scroll((target + step - 1) / step, -1);
|
||||
}
|
||||
|
||||
int MultiSwitchButton::AppendOption(const wxString &option, void *clientData)
|
||||
{
|
||||
Button *btn = new Button();
|
||||
btn->Create(this, option, "", wxBORDER_NONE);
|
||||
btn->Create(m_scroll, option, "", wxBORDER_NONE);
|
||||
btn->SetFont(GetFont());
|
||||
btn->SetBackgroundColor(m_bg_color);
|
||||
btn->SetTextColor(m_text_color);
|
||||
@@ -654,9 +749,6 @@ int MultiSwitchButton::AppendOption(const wxString &option, void *clientData)
|
||||
btns.push_back(btn);
|
||||
sizer->Add(btn, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
wxSize text_size = btn->GetTextExtent(option);
|
||||
btn->SetMinSize(wxSize(text_size.x + m_button_padding.x * 2 + 6, -1));
|
||||
|
||||
return int(btns.size()) - 1;
|
||||
}
|
||||
|
||||
@@ -666,6 +758,7 @@ void MultiSwitchButton::SetOptions(const std::vector<wxString> &options)
|
||||
for (const auto &option : options)
|
||||
AppendOption(option);
|
||||
|
||||
update_scroll_range();
|
||||
Layout();
|
||||
Refresh();
|
||||
}
|
||||
@@ -700,6 +793,8 @@ void MultiSwitchButton::SetSelection(int index)
|
||||
sel = index;
|
||||
update_button_styles();
|
||||
send_selection_event();
|
||||
// The selected button may be scrolled out of sight, e.g. when the tab restores the active tool.
|
||||
scroll_option_into_view(btns[sel]);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
@@ -718,6 +813,7 @@ void MultiSwitchButton::SetOptionText(unsigned int index, const wxString &text)
|
||||
if (index >= btns.size())
|
||||
return;
|
||||
btns[index]->SetLabel(text);
|
||||
update_scroll_range();
|
||||
}
|
||||
|
||||
void *MultiSwitchButton::GetOptionData(unsigned int index) const
|
||||
@@ -769,6 +865,7 @@ void MultiSwitchButton::SetButtonPadding(const wxSize &padding)
|
||||
m_button_padding = padding;
|
||||
for (auto *btn : btns)
|
||||
btn->SetPaddingSize(padding);
|
||||
update_scroll_range();
|
||||
Layout();
|
||||
Refresh();
|
||||
}
|
||||
@@ -777,6 +874,8 @@ void MultiSwitchButton::Rescale()
|
||||
{
|
||||
for (auto *btn : btns)
|
||||
btn->Rescale();
|
||||
// Rescaling can change how the labels measure, and the scrollbar range follows the buttons.
|
||||
update_scroll_range();
|
||||
}
|
||||
|
||||
void MultiSwitchButton::button_clicked(wxCommandEvent &event)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/scrolwin.h>
|
||||
#include <wx/tglbtn.h>
|
||||
#include "Button.hpp"
|
||||
|
||||
@@ -164,6 +165,10 @@ public:
|
||||
void SetButtonCornerRadius(double radius);
|
||||
void SetButtonPadding(const wxSize &padding);
|
||||
|
||||
// Keep the switch exactly as wide as the buttons need instead of letting the layout stretch it.
|
||||
// A layout with less room than that still squeezes it, and it scrolls its buttons then.
|
||||
void SetFitToOptions(bool fit = true) { m_fit_to_options = fit; update_scroll_range(); }
|
||||
|
||||
void Rescale();
|
||||
|
||||
protected:
|
||||
@@ -173,9 +178,20 @@ protected:
|
||||
bool send_selection_event();
|
||||
|
||||
private:
|
||||
// Height of the single button row, measured from the buttons themselves.
|
||||
int options_height() const;
|
||||
void update_scroll_range();
|
||||
int scrollbar_height(int options_width) const;
|
||||
void scroll_option_into_view(Button *btn);
|
||||
void on_size(wxSizeEvent &evt);
|
||||
|
||||
std::vector<Button *> btns;
|
||||
wxBoxSizer *sizer = nullptr;
|
||||
int sel = -1;
|
||||
// The buttons are laid out inside this scrolled area so that a switch holding more options than
|
||||
// the layout has room for scrolls instead of clipping its tail.
|
||||
wxScrolledWindow *m_scroll = nullptr;
|
||||
wxBoxSizer *sizer = nullptr;
|
||||
bool m_fit_to_options = false;
|
||||
int sel = -1;
|
||||
|
||||
StateColor m_bg_color;
|
||||
StateColor m_text_color;
|
||||
|
||||
@@ -106,6 +106,8 @@ wxString get_nozzle_volume_type_name(NozzleVolumeType type)
|
||||
return _L("Hybrid");
|
||||
} else if (NozzleVolumeType::nvtTPUHighFlow == type) {
|
||||
return _L("TPU High Flow");
|
||||
} else if (NozzleVolumeType::nvtE3DHighFlow == type) {
|
||||
return _L("E3D High Flow");
|
||||
}
|
||||
return wxString();
|
||||
}
|
||||
|
||||
@@ -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