diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index f8ad8b7bb3..b16198d190 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -193,6 +193,106 @@ void ObjectList::create_popup_menus() create_instance_popupmenu(&m_menu_instance); } +void ObjectList::get_selected_item_indexes(int& obj_idx, int& vol_idx, const wxDataViewItem& input_item/* = wxDataViewItem(0)*/) +{ + const wxDataViewItem item = input_item == wxDataViewItem(0) ? GetSelection() : input_item; + + if (!item) + { + obj_idx = vol_idx = -1; + return; + } + + const ItemType type = m_objects_model->GetItemType(item); + + obj_idx = type & itObject ? m_objects_model->GetIdByItem(item) : + type & itVolume ? m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)) : -1; + + vol_idx = type & itVolume ? m_objects_model->GetVolumeIdByItem(item) : -1; +} + +int ObjectList::get_mesh_errors_count(const int obj_idx, const int vol_idx /*= -1*/) const +{ + if (obj_idx < 0) + return 0; + + int errors = 0; + + std::vector volumes; + if (vol_idx == -1) + volumes = (*m_objects)[obj_idx]->volumes; + else + volumes.emplace_back((*m_objects)[obj_idx]->volumes[vol_idx]); + + for (ModelVolume* volume : volumes) + { + const stl_stats& stats = volume->mesh.stl.stats; + + errors += stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + + stats.facets_added + stats.facets_reversed + stats.backwards_edges; + } + + return errors; +} + +wxString ObjectList::get_mesh_errors_list(const int obj_idx, const int vol_idx /*= -1*/) const +{ + const int errors = get_mesh_errors_count(obj_idx, vol_idx); + + if (errors == 0) + return ""; // hide tooltip + + // Create tooltip string, if there are errors + wxString tooltip = wxString::Format(_(L("Auto-repaired (%d errors):\n")), errors); + + std::vector volumes; + if (vol_idx == -1) + volumes = (*m_objects)[obj_idx]->volumes; + else + volumes.emplace_back((*m_objects)[obj_idx]->volumes[vol_idx]); + + std::map error_msg = { + {L("degenerate facets") , 0}, + {L("edges fixed") , 0}, + {L("facets removed") , 0}, + {L("facets added") , 0}, + {L("facets reversed") , 0}, + {L("backwards edges") , 0} + }; + + for (ModelVolume* volume : volumes) + { + const stl_stats& stats = volume->mesh.stl.stats; + + error_msg[L("degenerate facets")] += stats.degenerate_facets; + error_msg[L("edges fixed")] += stats.edges_fixed; + error_msg[L("facets removed")] += stats.facets_removed; + error_msg[L("facets added")] += stats.facets_added; + error_msg[L("facets reversed")] += stats.facets_reversed; + error_msg[L("backwards edges")] += stats.backwards_edges; + } + + for (const auto& error : error_msg) + if (error.second > 0) + tooltip += wxString::Format(_("\t%d %s\n"), error.second, error.first); + + if (is_windows10()) + tooltip += _(L("Right button click the icon to fix STL through Netfabb")); + + return tooltip; +} + +wxString ObjectList::get_mesh_errors_list() +{ + if (!GetSelection()) + return ""; + + int obj_idx, vol_idx; + get_selected_item_indexes(obj_idx, vol_idx); + + return get_mesh_errors_list(obj_idx, vol_idx); +} + void ObjectList::set_tooltip_for_item(const wxPoint& pt) { wxDataViewItem item; @@ -202,38 +302,11 @@ void ObjectList::set_tooltip_for_item(const wxPoint& pt) if (col->GetTitle() == " " && GetSelectedItemsCount()<2) GetMainWindow()->SetToolTip(_(L("Right button click the icon to change the object settings"))); - else if (col->GetTitle() == _("Name") && - m_objects_model->GetBitmap(item).GetRefData() == m_bmp_manifold_warning.GetRefData()) { - int obj_idx = m_objects_model->GetIdByItem(item); - auto& stats = (*m_objects)[obj_idx]->volumes[0]->mesh.stl.stats; - int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + - stats.facets_added + stats.facets_reversed + stats.backwards_edges; - - wxString tooltip = wxString::Format(_(L("Auto-repaired (%d errors):\n")), errors); - - std::map error_msg; - error_msg[L("degenerate facets")] = stats.degenerate_facets; - error_msg[L("edges fixed")] = stats.edges_fixed; - error_msg[L("facets removed")] = stats.facets_removed; - error_msg[L("facets added")] = stats.facets_added; - error_msg[L("facets reversed")] = stats.facets_reversed; - error_msg[L("backwards edges")] = stats.backwards_edges; - - for (auto error : error_msg) - { - if (error.second > 0) - tooltip += wxString::Format(_("\t%d %s\n"), error.second, error.first); - } -// OR -// tooltip += wxString::Format(_(L("%d degenerate facets, %d edges fixed, %d facets removed, " -// "%d facets added, %d facets reversed, %d backwards edges")), -// stats.degenerate_facets, stats.edges_fixed, stats.facets_removed, -// stats.facets_added, stats.facets_reversed, stats.backwards_edges); - - if (is_windows10()) - tooltip += _(L("Right button click the icon to fix STL through Netfabb")); - - GetMainWindow()->SetToolTip(tooltip); + else if (col->GetTitle() == _("Name") ) + { + int obj_idx, vol_idx; + get_selected_item_indexes(obj_idx, vol_idx, item); + GetMainWindow()->SetToolTip(get_mesh_errors_list(obj_idx, vol_idx)); } else GetMainWindow()->SetToolTip(""); // hide tooltip @@ -533,10 +606,12 @@ void ObjectList::OnContextMenu(wxDataViewEvent&) if (title == " ") show_context_menu(); - else if (title == _("Name") && pt.x >15 && - m_objects_model->GetBitmap(item).GetRefData() == m_bmp_manifold_warning.GetRefData()) + else if (title == _("Name") && pt.x > 1.6f*wxGetApp().em_unit() && pt.x < 3.2f*wxGetApp().em_unit()) { - if (is_windows10()) + int obj_idx, vol_idx; + get_selected_item_indexes(obj_idx, vol_idx, item); + + if (is_windows10() && get_mesh_errors_count(obj_idx, vol_idx) > 0) fix_through_netfabb(); } @@ -1729,6 +1804,7 @@ void ObjectList::parts_changed(int obj_idx) void ObjectList::part_selection_changed() { int obj_idx = -1; + int volume_id = -1; m_config = nullptr; wxString og_name = wxEmptyString; @@ -1775,7 +1851,7 @@ void ObjectList::part_selection_changed() } else if (m_objects_model->GetItemType(item) == itVolume) { og_name = _(L("Part manipulation")); - const auto volume_id = m_objects_model->GetVolumeIdByItem(item); + volume_id = m_objects_model->GetVolumeIdByItem(item); m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config; update_and_show_manipulations = true; } @@ -1795,7 +1871,11 @@ void ObjectList::part_selection_changed() if (update_and_show_manipulations) { wxGetApp().obj_manipul()->get_og()->set_name(" " + og_name + " "); - wxGetApp().obj_manipul()->get_og()->set_value("object_name", m_objects_model->GetName(GetSelection())); + + if (item) { + wxGetApp().obj_manipul()->get_og()->set_value("object_name", m_objects_model->GetName(item)); + wxGetApp().obj_manipul()->update_manifold_warning_icon_state(get_mesh_errors_list(obj_idx, volume_id)); + } } if (update_and_show_settings) @@ -1815,16 +1895,13 @@ void ObjectList::part_selection_changed() void ObjectList::add_object_to_list(size_t obj_idx) { auto model_object = (*m_objects)[obj_idx]; - wxString item_name = from_u8(model_object->name); + const wxString& item_name = from_u8(model_object->name); const auto item = m_objects_model->Add(item_name, !model_object->config.has("extruder") ? 0 : model_object->config.option("extruder")->value); // Add error icon if detected auto-repaire - auto stats = model_object->volumes[0]->mesh.stl.stats; - int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + - stats.facets_added + stats.facets_reversed + stats.backwards_edges; - if (errors > 0) { + if (get_mesh_errors_count(obj_idx) > 0) { wxVariant variant; variant << PrusaDataViewBitmapText(item_name, m_bmp_manifold_warning); m_objects_model->SetValue(variant, item, 0); @@ -2641,18 +2718,10 @@ void ObjectList::rename_item() update_name_in_model(item); } -void ObjectList::fix_through_netfabb() const +void ObjectList::fix_through_netfabb() { - const wxDataViewItem item = GetSelection(); - if (!item) - return; - - const ItemType type = m_objects_model->GetItemType(item); - - const int obj_idx = type & itObject ? m_objects_model->GetIdByItem(item) : - type & itVolume ? m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)) : -1; - - const int vol_idx = type & itVolume ? m_objects_model->GetVolumeIdByItem(item) : -1; + int obj_idx, vol_idx; + get_selected_item_indexes(obj_idx, vol_idx); wxGetApp().plater()->fix_through_netfabb(obj_idx, vol_idx); @@ -2666,16 +2735,10 @@ void ObjectList::update_item_error_icon(const int obj_idx, const int vol_idx) co if (!item) return; - auto model_object = (*m_objects)[obj_idx]; - - const stl_stats& stats = model_object->volumes[vol_idx<0 ? 0 : vol_idx]->mesh.stl.stats; - const int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + - stats.facets_added + stats.facets_reversed + stats.backwards_edges; - - if (errors == 0) { + if (get_mesh_errors_count(obj_idx, vol_idx) == 0) { // delete Error_icon if all errors are fixed wxVariant variant; - variant << PrusaDataViewBitmapText(from_u8(model_object->name), wxNullBitmap); + variant << PrusaDataViewBitmapText(from_u8((*m_objects)[obj_idx]->name), wxNullBitmap); m_objects_model->SetValue(variant, item, 0); } } diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index a0343100ab..617b6da8a0 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -177,6 +177,16 @@ public: void init_icons(); + // Get obj_idx and vol_idx values for the selected (by default) or an adjusted item + void get_selected_item_indexes(int& obj_idx, int& vol_idx, const wxDataViewItem& item = wxDataViewItem(0)); + // Get count of errors in the mesh + int get_mesh_errors_count(const int obj_idx, const int vol_idx = -1) const; + /* Get list of errors in the mesh. Return value is a string, used for the tooltip + * Function without parameters is for a call from Manipulation panel, + * when we don't know parameters of selected item + */ + wxString get_mesh_errors_list(const int obj_idx, const int vol_idx = -1) const; + wxString get_mesh_errors_list(); void set_tooltip_for_item(const wxPoint& pt); void selection_changed(); @@ -285,7 +295,7 @@ public: void instances_to_separated_objects(const int obj_idx); void split_instances(); void rename_item(); - void fix_through_netfabb() const; + void fix_through_netfabb(); void update_item_error_icon(const int obj_idx, int vol_idx) const ; void paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index f9284a19b2..e623114e9d 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -10,6 +10,7 @@ #include "Selection.hpp" #include +#include "slic3r/Utils/FixModelByWin10.hpp" namespace Slic3r { @@ -20,6 +21,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : OG_Settings(parent, true) #ifndef __APPLE__ , m_focused_option("") + , m_manifold_warning_bmp(create_scaled_bitmap(parent, "exclamation")) #endif // __APPLE__ { m_og->set_name(_(L("Object Manipulation"))); @@ -42,17 +44,47 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : ConfigOptionDef def; // Objects(sub-objects) name - def.label = L("Name"); +// def.label = L("Name"); +// def.gui_type = "legend"; +// def.tooltip = L("Object name"); +// def.width = 21 * wxGetApp().em_unit(); +// def.default_value = new ConfigOptionString{ " " }; +// m_og->append_single_option_line(Option(def, "object_name")); + + Line line = Line{ "Name", "Object name" }; + + auto manifold_warning_icon = [this](wxWindow* parent) { + m_fix_throught_netfab_bitmap = new wxStaticBitmap(parent, wxID_ANY, wxNullBitmap); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(m_fix_throught_netfab_bitmap); + + if (is_windows10()) + m_fix_throught_netfab_bitmap->Bind(wxEVT_CONTEXT_MENU, [this](wxCommandEvent &e) + { + // if object/sub-object has no errors + if (m_fix_throught_netfab_bitmap->GetBitmap().GetRefData() == wxNullBitmap.GetRefData()) + return; + + wxGetApp().obj_list()->fix_through_netfabb(); + update_manifold_warning_icon_state(wxGetApp().obj_list()->get_mesh_errors_list()); + }); + + return sizer; + }; + + line.append_widget(manifold_warning_icon); + def.label = ""; def.gui_type = "legend"; def.tooltip = L("Object name"); def.width = 21 * wxGetApp().em_unit(); def.default_value = new ConfigOptionString{ " " }; - m_og->append_single_option_line(Option(def, "object_name")); + line.append_option(Option(def, "object_name")); + m_og->append_line(line); const int field_width = 5 * wxGetApp().em_unit()/*50*/; // Legend for object modification - auto line = Line{ "", "" }; + line = Line{ "", "" }; def.label = ""; def.type = coString; def.width = field_width/*50*/; @@ -334,6 +366,14 @@ void ObjectManipulation::emulate_kill_focus() else on_change(option, 0); } + +void ObjectManipulation::update_manifold_warning_icon_state(const wxString& tooltip) +{ + m_fix_throught_netfab_bitmap->SetBitmap(tooltip.IsEmpty() ? wxNullBitmap : m_manifold_warning_bmp); + + m_fix_throught_netfab_bitmap->SetToolTip(tooltip); +} + #endif // __APPLE__ void ObjectManipulation::reset_settings_value() diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index a5a180a56b..071edbed8c 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -78,6 +78,9 @@ class ObjectManipulation : public OG_Settings bool m_uniform_scale {true}; PrusaLockButton* m_lock_bnt{ nullptr }; + wxBitmap m_manifold_warning_bmp; + wxStaticBitmap* m_fix_throught_netfab_bitmap; + #ifndef __APPLE__ // Currently focused option name (empty if none) std::string m_focused_option; @@ -107,6 +110,8 @@ public: void emulate_kill_focus(); #endif // __APPLE__ + void update_manifold_warning_icon_state(const wxString& tooltip); + private: void reset_settings_value();