diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 4e78b31b46..e176ee588e 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1014,7 +1014,7 @@ static std::vector s_Preset_printer_options { "nozzle_height", "master_extruder_id", "default_print_profile", "inherits", "silent_mode", - "scan_first_layer", "enable_power_loss_recovery", "wrapping_detection_layers", "wrapping_exclude_area", "machine_load_filament_time", "machine_unload_filament_time", "machine_tool_change_time", "time_cost", "machine_pause_gcode", "template_custom_gcode", + "scan_first_layer", "enable_power_loss_recovery", "wrapping_detection_layers", "wrapping_exclude_area", "machine_load_filament_time", "machine_unload_filament_time", "machine_tool_change_time", "zero_based_filament_indexing", "time_cost", "machine_pause_gcode", "template_custom_gcode", "nozzle_type", "nozzle_hrc","auxiliary_fan", "nozzle_volume","upward_compatible_machine", "z_hop_types", "travel_slope", "retract_lift_enforce","support_chamber_temp_control","support_air_filtration","printer_structure", "best_object_pos", "head_wrap_detect_zone", "host_type", "print_host", "printhost_apikey", "bbl_use_printhost", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 4b91328b59..7bede9ecdb 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -4447,6 +4447,12 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(-2.)); + def = this->add("zero_based_filament_indexing", coBool); + def->label = L("0-based filament indexing"); + def->tooltip = L("When enabled, 0-based filament indexing is used in OrcaSlicer's interface (0..N-1 instead of 1..N)."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + def = this->add("start_end_points", coPoints); def->label = L("Start end points"); def->tooltip = L("The start and end points which is from cutter area to garbage can."); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 64fc0ddfbf..e747678e1c 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1385,6 +1385,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionBool, purge_in_prime_tower)) ((ConfigOptionBool, enable_filament_ramming)) ((ConfigOptionBool, support_multi_bed_types)) + ((ConfigOptionBool, zero_based_filament_indexing)) // Small Area Infill Flow Compensation ((ConfigOptionStrings, small_area_infill_flow_compensation_model)) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index dc7a2ec9ac..3dff95355e 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -345,11 +345,11 @@ void GCodeViewer::SequentialView::Marker::render_position_window(const libvgcode break; } case libvgcode::EViewType::Tool: { - sprintf(buf, "%s %s%d", buf, _u8L("Tool: ").c_str(), vertex.extruder_id + 1); + sprintf(buf, "%s %s%d", buf, _u8L("Tool: ").c_str(), filament_index_from_zero_based(static_cast(vertex.extruder_id))); break; } case libvgcode::EViewType::ColorPrint: { - sprintf(buf, "%s %s%d", buf, _u8L("Color: ").c_str(), vertex.color_id + 1); + sprintf(buf, "%s %s%d", buf, _u8L("Color: ").c_str(), filament_index_from_zero_based(static_cast(vertex.color_id))); break; } case libvgcode::EViewType::ActualVolumetricFlowRate: { @@ -2480,7 +2480,7 @@ void GCodeViewer::render_all_plates_stats(const std::vector> columns_offsets; - columns_offsets.push_back({ std::to_string(it->first + 1), offsets[_u8L("Filament")]}); + columns_offsets.push_back({ std::to_string(filament_index_from_zero_based(static_cast(it->first))), offsets[_u8L("Filament")]}); char buf[64]; double unit_conver = imperial_units ? GizmoObjectManipulation::oz_to_g : 1.0; @@ -3562,7 +3562,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv const std::vector& used_extruders_ids = m_viewer.get_used_extruders_ids(); for (uint8_t extruder_id : used_extruders_ids) { ::sprintf(buf, imperial_units ? "%.2f in %.2f g" : "%.2f m %.2f g", model_used_filaments_m[i], model_used_filaments_g[i]); - append_item(EItemType::Rect, libvgcode::convert(m_viewer.get_tool_colors()[extruder_id]), { { _u8L("Extruder") + " " + std::to_string(extruder_id + 1), offsets[0]}, {buf, offsets[1]} }); + append_item(EItemType::Rect, libvgcode::convert(m_viewer.get_tool_colors()[extruder_id]), { { _u8L("Extruder") + " " + std::to_string(filament_index_from_zero_based(extruder_id)), offsets[0]}, {buf, offsets[1]} }); // append_item(EItemType::Rect, libvgcode::convert(m_viewer.get_tool_colors()[extruder_id]), _u8L("Extruder") + " " + std::to_string(extruder_id + 1), // true, "", 0.0f, 0.0f, offsets, used_filaments_m[extruder_id], used_filaments_g[extruder_id]); i++; @@ -3614,7 +3614,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv for (auto extruder_idx : used_extruders_ids) { if (i < model_used_filaments_m.size() && i < model_used_filaments_g.size()) { std::vector> columns_offsets; - columns_offsets.push_back({ std::to_string(extruder_idx + 1), color_print_offsets[_u8L("Filament")]}); + columns_offsets.push_back({ std::to_string(filament_index_from_zero_based(extruder_idx)), color_print_offsets[_u8L("Filament")]}); char buf[64]; float column_sum_m = 0.0f; @@ -3990,7 +3990,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv ret = std::max(ret, ImGui::CalcTextSize((_u8L("Print settings") + std::string(":")).c_str()).x); if (!m_settings_ids.filament.empty()) { for (unsigned char i : m_viewer.get_used_extruders_ids()) { - ret = std::max(ret, ImGui::CalcTextSize((_u8L("Filament") + " " + std::to_string(i + 1) + ":").c_str()).x); + ret = std::max(ret, ImGui::CalcTextSize((_u8L("Filament") + " " + std::to_string(filament_index_from_zero_based(static_cast(i))) + ":").c_str()).x); } } if (ret > 0.0f) @@ -4017,7 +4017,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv for (unsigned char i : m_viewer.get_used_extruders_ids()) { if (i < static_cast(m_settings_ids.filament.size()) && !m_settings_ids.filament[i].empty()) { std::string txt = _u8L("Filament"); - txt += (m_viewer.get_used_extruders_count() == 1) ? ":" : " " + std::to_string(i + 1); + txt += (m_viewer.get_used_extruders_count() == 1) ? ":" : " " + std::to_string(filament_index_from_zero_based(static_cast(i))); imgui.text(txt); ImGui::SameLine(offset); imgui.text(m_settings_ids.filament[i]); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index fe9b60ea93..de2381cb3a 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -9492,14 +9492,14 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) if (error_iter != m_gcode_viewer.m_gcode_check_result.print_area_error_infos.begin()) { text += "\n"; } - int extruder_id = error_iter->first + 1; // change extruder id to 1 based + int extruder_index = error_iter->first; std::string filaments; std::vector slice_error_object_idxs; for (size_t i = 0; i < error_iter->second.size(); ++i) { if (i > 0) { filaments += ", "; } - int filament_id = error_iter->second[i].first + 1; // change filament id to 1 based + int filament_id = filament_index_from_zero_based(error_iter->second[i].first); int object_label_id = error_iter->second[i].second; filaments += std::to_string(filament_id); @@ -9522,7 +9522,7 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) } } } - std::string extruder_name = extruder_name_list[extruder_id-1]; + std::string extruder_name = extruder_name_list[extruder_index]; if (error_iter->second.size() == 1) { text += (boost::format(_u8L("Filament %s is placed in the %s, but the generated G-code path exceeds the printable range of the %s.")) %filaments %extruder_name %extruder_name).str(); } @@ -9538,11 +9538,12 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) if (error_iter != m_gcode_viewer.m_gcode_check_result.print_height_error_infos.begin()) { text += "\n"; } - int extruder_id = error_iter->first + 1; // change extruder id to 1 based + int extruder_index = error_iter->first; + int extruder_id = filament_index_from_zero_based(extruder_index); std::set filament_ids; std::vector slice_error_object_idxs; for (size_t i = 0; i < error_iter->second.size(); ++i) { - int filament_id = error_iter->second[i].first + 1; // change filament id to 1 based + int filament_id = filament_index_from_zero_based(error_iter->second[i].first); int object_label_id = error_iter->second[i].second; filament_ids.insert(filament_id); for (int object_idx = 0; object_idx < (int) m_model->objects.size(); ++object_idx) { @@ -9575,7 +9576,7 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) } } } - std::string extruder_name = extruder_name_list[extruder_id-1]; + std::string extruder_name = extruder_name_list[extruder_index]; if (error_iter->second.size() == 1) { text += (boost::format(_u8L("Filament %s is placed in the %s, but the generated G-code path exceeds the printable height of the %s.")) % filaments % extruder_name % extruder_name).str(); } else { @@ -9601,10 +9602,10 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) break; case EWarning::FilamentUnPrintableOnFirstLayer: { std::string warning; - const std::vector &conflict_filament = m_gcode_viewer.filament_printable_reuslt.conflict_filament; + std::vector &conflict_filament = m_gcode_viewer.filament_printable_reuslt.conflict_filament; auto iter = conflict_filament.begin(); for (int filament : conflict_filament) { - warning += std::to_string(filament + 1); + warning += std::to_string(filament_index_from_zero_based(filament)); warning += " "; } text = (boost::format(_u8L("filaments %s cannot be printed directly on the surface of this plate.")) % warning).str(); diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index d133720c7e..b27801b5d3 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -102,6 +102,27 @@ const std::string& shortkey_alt_prefix() return str; } +bool zero_based_filament_indexing() +{ + const auto* preset_bundle = wxGetApp().preset_bundle; + if (!preset_bundle) + return false; + + const auto& config = preset_bundle->printers.get_edited_preset().config; + const auto* opt = config.option("zero_based_filament_indexing"); + return opt != nullptr && opt->value; +} + +int filament_index_from_zero_based(int index) +{ + return index + (zero_based_filament_indexing() ? 0 : 1); +} + +int filament_index_from_one_based(int index) +{ + return zero_based_filament_indexing() ? index - 1 : index; +} + // opt_index = 0, by the reason of zero-index in ConfigOptionVector by default (in case only one element) void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index /*= 0*/) { diff --git a/src/slic3r/GUI/GUI.hpp b/src/slic3r/GUI/GUI.hpp index db8cf06a61..b27e3ca9b6 100644 --- a/src/slic3r/GUI/GUI.hpp +++ b/src/slic3r/GUI/GUI.hpp @@ -32,6 +32,10 @@ void break_to_debugger(); extern const std::string& shortkey_ctrl_prefix(); extern const std::string& shortkey_alt_prefix(); +bool zero_based_filament_indexing(); +int filament_index_from_zero_based(int index); +int filament_index_from_one_based(int index); + extern AppConfig* get_app_config(); extern void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_language_change); diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 44dd7b8f6e..1ed7228cc9 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -923,9 +923,10 @@ void MenuFactory::append_menu_item_change_extruder(wxMenu* menu) wxString item_name = _L("Default"); if (i > 0) { + const int display_index = filament_index_from_one_based(i); auto preset = wxGetApp().preset_bundle->filaments.find_preset(wxGetApp().preset_bundle->filament_presets[i - 1]); if (preset == nullptr) { - item_name = wxString::Format(_L("Filament %d"), i); + item_name = wxString::Format(_L("Filament %d"), display_index); } else { item_name = from_u8(preset->label(false)); } @@ -1526,8 +1527,9 @@ void MenuFactory::create_filament_action_menu(bool init, int active_filament_men if (i == active_filament_menu_id) continue; + const int display_index = filament_index_from_zero_based(i); auto preset = wxGetApp().preset_bundle->filaments.find_preset(wxGetApp().preset_bundle->filament_presets[i]); - wxString item_name = preset ? from_u8(preset->label(false)) : wxString::Format(_L("Filament %d"), i + 1); + wxString item_name = preset ? wxString::Format("%d: %s", display_index, from_u8(preset->label(false))) : wxString::Format(_L("Filament %d"), display_index); append_menu_item(sub_menu, wxID_ANY, item_name, "", [i](wxCommandEvent&) { plater()->sidebar().change_filament(-2, i); }, *icons[i], menu, @@ -2083,9 +2085,10 @@ void MenuFactory::append_menu_item_change_filament(wxMenu* menu) wxString item_name = _L("Default"); if (i > 0) { + const int display_index = filament_index_from_one_based(i); auto preset = wxGetApp().preset_bundle->filaments.find_preset(wxGetApp().preset_bundle->filament_presets[i - 1]); if (preset == nullptr) { - item_name = wxString::Format(_L("Filament %d"), i); + item_name = wxString::Format(_L("Filament %d"), display_index); } else { item_name = from_u8(preset->label(false)); } diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 4429faf1f5..29f092c2ad 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -77,6 +77,14 @@ static int filaments_count() return wxGetApp().filaments_cnt(); } +static wxString format_filament_index_for_display(int extruder_id) +{ + if (extruder_id <= 0) + return wxString::Format("%d", extruder_id); + + return wxString::Format("%d", extruder_id); +} + static void take_snapshot(const std::string& snapshot_name) { Plater* plater = wxGetApp().plater(); @@ -688,11 +696,11 @@ void ObjectList::update_filament_values_for_items(const size_t filaments_count) auto object = (*m_objects)[i]; wxString extruder; if (!object->config.has("extruder") || size_t(object->config.extruder()) > filaments_count) { - extruder = "1"; + extruder = format_filament_index_for_display(1); object->config.set_key_value("extruder", new ConfigOptionInt(1)); } else { - extruder = wxString::Format("%d", object->config.extruder()); + extruder = format_filament_index_for_display(object->config.extruder()); } m_objects_model->SetExtruder(extruder, item); @@ -707,10 +715,10 @@ void ObjectList::update_filament_values_for_items(const size_t filaments_count) if (!item) continue; if (!object->volumes[id]->config.has("extruder") || size_t(object->volumes[id]->config.extruder()) > filaments_count) { - extruder = wxString::Format("%d", object->config.extruder()); + extruder = format_filament_index_for_display(object->config.extruder()); } else { - extruder = wxString::Format("%d", object->volumes[id]->config.extruder()); + extruder = format_filament_index_for_display(object->volumes[id]->config.extruder()); } m_objects_model->SetExtruder(extruder, item); @@ -737,15 +745,15 @@ void ObjectList::update_filament_values_for_items_when_delete_filament(const siz auto object = (*m_objects)[i]; wxString extruder; if (!object->config.has("extruder")) { - extruder = std::to_string(1); + extruder = format_filament_index_for_display(1); object->config.set_key_value("extruder", new ConfigOptionInt(1)); } else if (size_t(object->config.extruder()) == filament_id + 1) { - extruder = std::to_string(replace_filament_id); + extruder = format_filament_index_for_display(replace_filament_id); object->config.set_key_value("extruder", new ConfigOptionInt(replace_filament_id)); } else { int new_extruder = object->config.extruder() > filament_id ? object->config.extruder() - 1 : object->config.extruder(); - extruder = wxString::Format("%d", new_extruder); + extruder = format_filament_index_for_display(new_extruder); object->config.set_key_value("extruder", new ConfigOptionInt(new_extruder)); } m_objects_model->SetExtruder(extruder, item); @@ -787,7 +795,7 @@ void ObjectList::update_filament_values_for_items_when_delete_filament(const siz object->volumes[id]->config.set_key_value("extruder", new ConfigOptionInt(replace_filament_id)); } else { int new_extruder = object->volumes[id]->config.extruder() > filament_id ? object->volumes[id]->config.extruder() - 1 : object->volumes[id]->config.extruder(); - extruder = wxString::Format("%d", new_extruder); + extruder = format_filament_index_for_display(new_extruder); object->volumes[id]->config.set_key_value("extruder", new ConfigOptionInt(new_extruder)); } @@ -815,12 +823,12 @@ void ObjectList::update_filament_values_for_items_when_delete_filament(const siz auto& layer_range_item = *(l_iter); if (layer_range_item.second.has("extruder") && layer_range_item.second.option("extruder")->getInt() == filament_id + 1) { int new_extruder = replace_id == -1 ? 0 : (replace_id + 1); - extruder = wxString::Format("%d", new_extruder); + extruder = format_filament_index_for_display(new_extruder); layer_range_item.second.set("extruder", new_extruder); } else { int layer_filament_id = layer_range_item.second.option("extruder")->getInt(); int new_extruder = layer_filament_id > filament_id ? layer_filament_id - 1 : layer_filament_id; - extruder = wxString::Format("%d", new_extruder); + extruder = format_filament_index_for_display(new_extruder); layer_range_item.second.set("extruder", new_extruder); } m_objects_model->SetExtruder(extruder, layer_item); @@ -1608,7 +1616,7 @@ void ObjectList::extruder_editing() pos.x = GetColumn(colName)->GetWidth() + GetColumn(colPrint)->GetWidth() + GetColumn(colHeight)->GetWidth() + 5; pos.y -= GetTextExtent("m").y; - apply_extruder_selector(&m_extruder_editor, this, "1", pos, size); + apply_extruder_selector(&m_extruder_editor, this, format_filament_index_for_display(1).ToStdString(), pos, size); m_extruder_editor->SetSelection(m_objects_model->GetExtruderNumber(item)); m_extruder_editor->Show(); @@ -2772,7 +2780,7 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con int extruder_id = last_volume->config.opt_int("extruder"); object->config.set("extruder", extruder_id); } - wxString extruder = object->config.has("extruder") ? wxString::Format("%d", object->config.extruder()) : _devL("1"); + wxString extruder = object->config.has("extruder") ? format_filament_index_for_display(object->config.extruder()) : format_filament_index_for_display(1); m_objects_model->SetExtruder(extruder, obj_item); } // add settings to the object, if it has them @@ -4168,7 +4176,7 @@ void ObjectList::delete_from_model_and_list(const std::vector& it if (obj->volumes.size() == 1) { wxDataViewItem parent = m_objects_model->GetItemById(item->obj_idx); if (obj->config.has("extruder")) { - const wxString extruder = wxString::Format("%d", obj->config.extruder()); + const wxString extruder = format_filament_index_for_display(obj->config.extruder()); m_objects_model->SetExtruder(extruder, parent); } // If last volume item with warning was deleted, unmark object item @@ -6184,7 +6192,7 @@ void ObjectList::set_extruder_for_selected_items(const int extruder) } } - const wxString extruder_str = wxString::Format("%d", new_extruder); + const wxString extruder_str = format_filament_index_for_display(new_extruder); m_objects_model->SetExtruder(extruder_str, item); } diff --git a/src/slic3r/GUI/GUI_ObjectTable.cpp b/src/slic3r/GUI/GUI_ObjectTable.cpp index 35df1f460e..450c38373f 100644 --- a/src/slic3r/GUI/GUI_ObjectTable.cpp +++ b/src/slic3r/GUI/GUI_ObjectTable.cpp @@ -2846,7 +2846,8 @@ int ObjectTablePanel::init_filaments_and_colors() } //parse the filaments - m_filaments_name[i] = wxString(std::to_string(i+1) + ": " + filament_presets[i]); + const int display_index = filament_index_from_zero_based(static_cast(i)); + m_filaments_name[i] = wxString(std::to_string(display_index) + ": " + filament_presets[i]); i++; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index d8bc90fdd6..347d4a9a35 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -192,7 +192,7 @@ void GLGizmoMmuSegmentation::data_changed(bool is_serializing) // BBS bool GLGizmoMmuSegmentation::on_number_key_down(int number) { - int extruder_idx = number - 1; + int extruder_idx = zero_based_filament_indexing() ? number : number - 1; if (extruder_idx < m_extruders_colors.size() && extruder_idx >= 0) m_selected_extruder_idx = extruder_idx; @@ -403,7 +403,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott const ColorRGBA &extruder_color = m_extruders_colors[extruder_idx]; ImVec4 color_vec = ImGuiWrapper::to_ImVec4(extruder_color); std::string color_label = std::string("##extruder color ") + std::to_string(extruder_idx); - std::string item_text = std::to_string(extruder_idx + 1); + std::string item_text = std::to_string(filament_index_from_zero_based(extruder_idx)); const ImVec2 label_size = ImGui::CalcTextSize(item_text.c_str(), NULL, true); const ImVec2 button_size(max_label_size.x + m_imgui->scaled(0.5f),0.f); @@ -435,7 +435,8 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott color_button_high = ImGui::GetCursorPos().y - color_button - 2.0; if (color_picked) { m_selected_extruder_idx = extruder_idx; } - if (extruder_idx < 16 && ImGui::IsItemHovered()) m_imgui->tooltip(_L("Shortcut Key ") + std::to_string(extruder_idx + 1), max_tooltip_width); + if (extruder_idx < 16 && ImGui::IsItemHovered()) + m_imgui->tooltip(_L("Shortcut Key ") + std::to_string(filament_index_from_zero_based(extruder_idx)), max_tooltip_width); // draw filament id float gray = 0.299 * extruder_color.r() + 0.587 * extruder_color.g() + 0.114 * extruder_color.b(); @@ -873,7 +874,7 @@ wxString GLGizmoMmuSegmentation::handle_snapshot_action_name(bool shift_down, GL if (shift_down) action_name = _L("Remove painted color"); else { - action_name = GUI::format(_L("Painted using: Filament %1%"), m_selected_extruder_idx); + action_name = GUI::format(_L("Painted using: Filament %1%"), filament_index_from_zero_based(m_selected_extruder_idx)); } return action_name; } @@ -1047,7 +1048,7 @@ void GLGizmoMmuSegmentation::render_filament_remap_ui(float window_width, float #endif // overlay destination number with proper contrast calculation - std::string dst_txt = std::to_string(m_extruder_remap[src] + 1); + std::string dst_txt = std::to_string(filament_index_from_zero_based(static_cast(m_extruder_remap[src]))); float gray = 0.299f * dst_col.r() + 0.587f * dst_col.g() + 0.114f * dst_col.b(); ImVec2 txt_sz = ImGui::CalcTextSize(dst_txt.c_str()); ImVec2 pos = ImGui::GetItemRectMin(); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 8bd2904c49..a1920d4810 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -3274,7 +3274,7 @@ std::tuple ImGuiWrapper::calculate_filament_group_text_size(const void ImGuiWrapper::filament_group(const std::string& filament_type, const char* hex_color, unsigned char filament_id, float align_width) { //ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); - std::string id = std::to_string(static_cast (filament_id + 1)); + std::string id = std::to_string(filament_index_from_zero_based(static_cast(filament_id))); ImDrawList* draw_list = ImGui::GetWindowDrawList(); static ImTextureID transparent; ImVec2 text_size = ImGui::CalcTextSize(filament_type.c_str()); diff --git a/src/slic3r/GUI/ObjColorDialog.cpp b/src/slic3r/GUI/ObjColorDialog.cpp index 7575777507..f237c8c66f 100644 --- a/src/slic3r/GUI/ObjColorDialog.cpp +++ b/src/slic3r/GUI/ObjColorDialog.cpp @@ -409,7 +409,8 @@ ObjColorPanel::~ObjColorPanel() { void ObjColorPanel::msw_rescale() { for (unsigned int i = 0; i < m_extruder_icon_list.size(); ++i) { - auto bitmap = *get_extruder_color_icon(m_colours[i].GetAsString(wxC2S_HTML_SYNTAX).ToStdString(), std::to_string(i + 1), FromDIP(16), FromDIP(16)); + const int display_index = filament_index_from_zero_based(static_cast(i)); + auto bitmap = *get_extruder_color_icon(m_colours[i].GetAsString(wxC2S_HTML_SYNTAX).ToStdString(), std::to_string(display_index), FromDIP(16), FromDIP(16)); m_extruder_icon_list[i]->SetBitmap(bitmap); } /* for (unsigned int i = 0; i < m_color_cluster_icon_list.size(); ++i) { @@ -530,7 +531,8 @@ wxBoxSizer *ObjColorPanel::create_extruder_icon_and_rgba_sizer(wxWindow *parent, { auto icon_sizer = new wxBoxSizer(wxHORIZONTAL); wxButton *icon = new wxButton(parent, wxID_ANY, {}, wxDefaultPosition, ICON_SIZE, wxBORDER_NONE | wxBU_AUTODRAW); - icon->SetBitmap(*get_extruder_color_icon(color.GetAsString(wxC2S_HTML_SYNTAX).ToStdString(), std::to_string(id + 1), FromDIP(16), FromDIP(16))); + const int display_index = filament_index_from_zero_based(id); + icon->SetBitmap(*get_extruder_color_icon(color.GetAsString(wxC2S_HTML_SYNTAX).ToStdString(), std::to_string(display_index), FromDIP(16), FromDIP(16))); icon->SetCanFocus(false); m_extruder_icon_list.emplace_back(icon); icon_sizer->Add(icon, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 0); // wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index a7d2f711bf..8229a1fc80 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -44,6 +44,14 @@ static constexpr char WarningIcon[] = "obj_warning"; static constexpr char WarningManifoldIcon[] = "obj_warning"; static constexpr char LockIcon[] = "cut_"; +static wxString format_filament_index_for_display(int extruder_id) +{ + if (extruder_id <= 0) + return wxString::Format("%d", extruder_id); + + return wxString::Format("%d", extruder_id); +} + ObjectDataViewModelNode::ObjectDataViewModelNode(PartPlate* part_plate, wxString name) : m_parent(nullptr), m_name(name), @@ -611,7 +619,7 @@ wxDataViewItem ObjectDataViewModel::AddObject(ModelObject *model_object, std::st // create object node //const wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder); - const wxString extruder_str = wxString::Format("%d", extruder); + const wxString extruder_str = format_filament_index_for_display(extruder); auto obj_node = new ObjectDataViewModelNode(name, extruder_str, plate_idx, model_object); // Add warning icon if detected auto-repaire UpdateBitmapForNode(obj_node, warning_bitmap, has_lock); @@ -678,7 +686,7 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent extruder_str = root->m_extruder; } else { - extruder_str = wxString::Format("%d", extruder); + extruder_str = format_filament_index_for_display(extruder); } const auto node = new ObjectDataViewModelNode(root, name, volume_type, is_text_volume, is_svg_volume, extruder_str, root->m_volumes_cnt); @@ -905,7 +913,7 @@ wxDataViewItem ObjectDataViewModel::AddLayersChild(const wxDataViewItem &parent_ if (!parent_node) return wxDataViewItem(0); // BBS - wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder); + wxString extruder_str = extruder == 0 ? _(L("default")) : format_filament_index_for_display(extruder); // get LayerRoot node ObjectDataViewModelNode *layer_root_node; @@ -1748,7 +1756,13 @@ int ObjectDataViewModel::GetExtruderNumber(const wxDataViewItem& item) const if (!node) // happens if item.IsOk()==false return 0; - return atoi(node->m_extruder.c_str()); + const std::string extruder = node->m_extruder.ToStdString(); + char* end = nullptr; + const long value = std::strtol(extruder.c_str(), &end, 10); + if (end == extruder.c_str() || *end != '\0') + return 0; + + return static_cast(value); } wxString ObjectDataViewModel::GetColumnType(unsigned int col) const diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 331d32cbff..4529b02f95 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -834,7 +834,7 @@ struct DynamicFilamentList1Based : DynamicFilamentList wxString get_value(int index) override { wxString str; - str << index+1; + str << filament_index_from_zero_based(index); return str; } int index_of(wxString value) override @@ -842,8 +842,9 @@ struct DynamicFilamentList1Based : DynamicFilamentList long n = 0; if(!value.ToLong(&n)) return -1; - --n; - return (n >= 0 && n <= items.size()) ? int(n) : -1; + if (!zero_based_filament_indexing()) + --n; + return (n >= 0 && n < static_cast(items.size())) ? int(n) : -1; } void update(bool force = false) { @@ -2277,7 +2278,7 @@ void Sidebar::init_filament_combo(PlaterPresetComboBox **combo, const int filame if ((filament_idx % 2) == 0) // Dont add right column item. this one create equal spacing on left, right & middle combo_and_btn_sizer->AddSpacer(FromDIP((filament_idx % 2) == 0 ? 12 : 3)); // Content Margin - (*combo)->clr_picker->SetLabel(wxString::Format("%d", filament_idx + 1)); + (*combo)->clr_picker->SetLabel(wxString::Format("%d", filament_index_from_zero_based(filament_idx))); combo_and_btn_sizer->Add((*combo)->clr_picker, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(SidebarProps::ElementSpacing()) - FromDIP(2)); // ElementSpacing - 2 (from combo box)) combo_and_btn_sizer->Add(*combo, 1, wxALL | wxEXPAND, FromDIP(2))->SetMinSize({-1, FromDIP(30)}); diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index 1aef329297..9f1b885a53 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -338,7 +338,7 @@ static Option create_option(const std::string &opt_key, const wxString &label, P wxString category = gc.category; if (type == Preset::TYPE_PRINTER && category.Contains("Extruder ")) { std::string opt_idx = opt_key.substr(opt_key.find("#") + 1); - category = wxString::Format("%s %d", "Extruder", atoi(opt_idx.c_str()) + 1); + category = wxString::Format("%s %d", "Extruder", GUI::filament_index_from_zero_based(std::atoi(opt_idx.c_str()))); } return Option{boost::nowide::widen(get_key(opt_key, type)), diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index d4d31a3573..280f6592b3 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1537,6 +1537,17 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) wxGetApp().get_tab(Preset::TYPE_PRINT)->update(); } + if (opt_key == "zero_based_filament_indexing") { + wxGetApp().sidebar().update_dynamic_filament_list(); + wxGetApp().sidebar().update_all_preset_comboboxes(); + wxGetApp().obj_list()->update_objects_list_filament_column(wxGetApp().filaments_cnt()); + wxGetApp().plater()->update(); + if (m_type == Preset::TYPE_PRINTER) { + if (auto* printer_tab = dynamic_cast(this)) + printer_tab->refresh_extruder_page_titles(boost::any_cast(value)); + } + } + if(opt_key == "purge_in_prime_tower") wxGetApp().get_tab(Preset::TYPE_PRINT)->update(); @@ -4916,12 +4927,13 @@ if (is_marlin_flavor) optgroup->append_single_option_line("machine_load_filament_time", "printer_multimaterial_advanced#filament-load-time"); optgroup->append_single_option_line("machine_unload_filament_time", "printer_multimaterial_advanced#filament-unload-time"); optgroup->append_single_option_line("machine_tool_change_time", "printer_multimaterial_advanced#tool-change-time"); + optgroup->append_single_option_line("zero_based_filament_indexing", "printer_multimaterial_advanced#zero-based-filament-indexing"); m_pages.insert(m_pages.end() - n_after_single_extruder_MM, page); } // Orca: build missed extruder pages for (auto extruder_idx = m_extruders_count_old; extruder_idx < m_extruders_count; ++extruder_idx) { - const wxString& page_name = (m_extruders_count > 1) ? wxString::Format("Extruder %d", int(extruder_idx + 1)) : wxString::Format("Extruder"); + const wxString& page_name = (m_extruders_count > 1) ? wxString::Format("Extruder %d", filament_index_from_zero_based(static_cast(extruder_idx))) : wxString::Format("Extruder"); //# build page //const wxString& page_name = wxString::Format("Extruder %d", int(extruder_idx + 1)); @@ -5044,15 +5056,15 @@ if (is_marlin_flavor) } // BBS. No extra extruder page for single physical extruder machine // # remove extra pages - auto &first_extruder_title = const_cast(m_pages[n_before_extruders]->title()); if (m_extruders_count < m_extruders_count_old) { - m_pages.erase( m_pages.begin() + n_before_extruders + m_extruders_count, - m_pages.begin() + n_before_extruders + m_extruders_count_old); - if (m_extruders_count == 1) - first_extruder_title = wxString::Format("Extruder"); - } else if (m_extruders_count_old == 1) { - first_extruder_title = wxString::Format("Extruder %d", 1); + m_pages.erase(m_pages.begin() + n_before_extruders + m_extruders_count, + m_pages.begin() + n_before_extruders + m_extruders_count_old); } + for (size_t extruder_idx = 0; extruder_idx < m_extruders_count; ++extruder_idx) { + wxString page_title = (m_extruders_count > 1) ? wxString::Format("Extruder %d", filament_index_from_zero_based(static_cast(extruder_idx))) : wxString("Extruder"); + m_pages[n_before_extruders + extruder_idx]->set_title(page_title); + } + const wxString& first_extruder_title = m_pages[n_before_extruders]->title(); auto & searcher = wxGetApp().sidebar().get_searcher(); for (auto &group : m_pages[n_before_extruders]->m_optgroups) { group->set_config_category_and_type(first_extruder_title, m_type); @@ -5076,6 +5088,43 @@ if (is_marlin_flavor) apply_searcher(); } +void TabPrinter::refresh_extruder_page_titles(bool start_at_zero) +{ + if (m_pages.empty() || m_extruders_count == 0) + return; + + size_t first_extruder_page = m_pages.size(); + for (size_t i = 0; i < m_pages.size(); ++i) { + const wxString& title = m_pages[i]->title(); + if (title == "Extruder" || title.StartsWith("Extruder ")) { + first_extruder_page = i; + break; + } + } + if (first_extruder_page >= m_pages.size()) + return; + + for (size_t extruder_idx = 0; extruder_idx < m_extruders_count; ++extruder_idx) { + const size_t page_index = first_extruder_page + extruder_idx; + if (page_index >= m_pages.size()) + break; + + wxString page_title = (m_extruders_count > 1) ? wxString::Format("Extruder %d", static_cast(extruder_idx) + (start_at_zero ? 0 : 1)) : wxString("Extruder"); + m_pages[page_index]->set_title(page_title); + } + + const wxString& first_extruder_title = m_pages[first_extruder_page]->title(); + auto& searcher = wxGetApp().sidebar().get_searcher(); + for (auto& group : m_pages[first_extruder_page]->m_optgroups) { + group->set_config_category_and_type(first_extruder_title, m_type); + for (auto& opt : group->opt_map()) + searcher.add_key(opt.first + "#0", m_type, group->title, first_extruder_title); + } + + rebuild_page_tree(); + apply_searcher(); +} + // this gets executed after preset is loaded and before GUI fields are updated void TabPrinter::on_preset_loaded() { @@ -7075,6 +7124,16 @@ void Page::reload_config() group->reload_config(); } +void Page::set_title(const wxString& title) +{ + if (m_title == title) + return; + + m_title = title; + if (m_page_title) + m_page_title->SetLabel(_(m_title)); +} + void Page::update_visibility(ConfigOptionMode mode, bool update_contolls_visibility) { bool ret_val = false; diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 96b35b26e1..16cd7a25a5 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -86,6 +86,7 @@ public: wxBoxSizer* vsizer() const { return m_vsizer; } wxWindow* parent() const { return m_parent; } const wxString& title() const { return m_title; } + void set_title(const wxString& title); size_t iconID() const { return m_iconID; } void set_config(DynamicPrintConfig* config_in) { m_config = config_in; } void reload_config(); @@ -640,6 +641,7 @@ public: bool supports_printer_technology(const PrinterTechnology /* tech */) const override { return true; } void set_extruder_volume_type(int extruder_id, NozzleVolumeType type); + void refresh_extruder_page_titles(bool start_at_zero); wxSizer* create_bed_shape_widget(wxWindow* parent); void cache_extruder_cnt(const DynamicPrintConfig* config = nullptr); diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 1a6037184d..3af2db9470 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -548,13 +548,15 @@ std::vector get_extruder_color_icons(bool thin_icon/* = false*/) int index = 0; for (const auto &colors : readable_color_info) { - auto label = std::to_string(++index); - bool is_gradient = ctype[index-1] == "0"; + const int display_index = Slic3r::GUI::filament_index_from_zero_based(index); + auto label = std::to_string(display_index); + bool is_gradient = ctype[index] == "0"; if (colors.size() == 1) { bmps.push_back(get_extruder_color_icon(colors[0], label, icon_width, icon_height)); } else { bmps.push_back(get_extruder_color_icon(colors, is_gradient, label, icon_width, icon_height)); } + ++index; } } else { std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); @@ -565,8 +567,10 @@ std::vector get_extruder_color_icons(bool thin_icon/* = false*/) const int icon_height = lround(2 * em); int index = 0; for (const auto &color : colors) { - auto label = std::to_string(++index); + const int display_index = Slic3r::GUI::filament_index_from_zero_based(index); + auto label = std::to_string(display_index); bmps.push_back(get_extruder_color_icon(color, label, icon_width, icon_height)); + ++index; } } return bmps; @@ -790,9 +794,8 @@ void apply_extruder_selector(Slic3r::GUI::BitmapComboBox** ctrl, ++i; } - (*ctrl)->Append(use_full_item_name - ? Slic3r::GUI::from_u8((boost::format("%1% %2%") % str % i).str()) - : wxString::Format("%d", i), *bmp); + const int display_index = Slic3r::GUI::filament_index_from_one_based(i); + (*ctrl)->Append(use_full_item_name ? Slic3r::GUI::from_u8((boost::format("%1% %2%") % str % display_index).str()) : wxString::Format("%d", display_index), *bmp); ++i; } (*ctrl)->SetSelection(0);