Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_sinking_objects_collision

This commit is contained in:
enricoturri1966 2021-10-05 15:33:37 +02:00
commit 982172b878
8 changed files with 132 additions and 62 deletions

View File

@ -48,6 +48,7 @@
# enabled_tags = ... # enabled_tags = ...
# disabled_tags = ... # disabled_tags = ...
# supported tags are: simple; advanced; expert; FFF; MMU; SLA; Windows; Linux; OSX; # supported tags are: simple; advanced; expert; FFF; MMU; SLA; Windows; Linux; OSX;
# and all filament types: PLA; PET; ABS; ASA; FLEX; HIPS; EDGE; NGEN; NYLON; PVA; PC; PP; PEI; PEEK; PEKK; POM; PSU; PVDF; SCAFF;
# Tags are case sensitive. # Tags are case sensitive.
# FFF is affirmative for both one or more extruder printers. # FFF is affirmative for both one or more extruder printers.
# Algorithm shows hint only if ALL enabled tags are affirmative. (so never do enabled_tags = FFF; SLA;) # Algorithm shows hint only if ALL enabled tags are affirmative. (so never do enabled_tags = FFF; SLA;)

View File

@ -1639,12 +1639,13 @@ void visit_antipodals (Idx& ia, Idx &ib, Fn &&fn)
// Set current caliper direction to be the lower edge angle from X axis // Set current caliper direction to be the lower edge angle from X axis
int cmp = cmp_angles(ia.prev_dir(), ia.dir(), ib.dir()); int cmp = cmp_angles(ia.prev_dir(), ia.dir(), ib.dir());
Idx *current = cmp <= 0 ? &ia : &ib, *other = cmp <= 0 ? &ib : &ia; Idx *current = cmp <= 0 ? &ia : &ib, *other = cmp <= 0 ? &ib : &ia;
Idx *initial = current;
bool visitor_continue = true; bool visitor_continue = true;
size_t a_start = ia.idx(), b_start = ib.idx(); size_t start = initial->idx();
bool a_finished = false, b_finished = false; bool finished = false;
while (visitor_continue && !(a_finished && b_finished)) { while (visitor_continue && !finished) {
Point current_dir_a = current == &ia ? current->dir() : -current->dir(); Point current_dir_a = current == &ia ? current->dir() : -current->dir();
visitor_continue = fn(ia.idx(), ib.idx(), current_dir_a); visitor_continue = fn(ia.idx(), ib.idx(), current_dir_a);
@ -1664,8 +1665,7 @@ void visit_antipodals (Idx& ia, Idx &ib, Fn &&fn)
std::swap(current, other); std::swap(current, other);
} }
if (ia.idx() == a_start) a_finished = true; if (initial->idx() == start) finished = true;
if (ib.idx() == b_start) b_finished = true;
} }
} }
@ -1702,8 +1702,8 @@ bool intersects(const Polygon &A, const Polygon &B)
BoundingBox bbA{{A[bA.xmin].x(), A[bA.ymin].y()}, {A[bA.xmax].x(), A[bA.ymax].y()}}; BoundingBox bbA{{A[bA.xmin].x(), A[bA.ymin].y()}, {A[bA.xmax].x(), A[bA.ymax].y()}};
BoundingBox bbB{{B[bB.xmin].x(), B[bB.ymin].y()}, {B[bB.xmax].x(), B[bB.ymax].y()}}; BoundingBox bbB{{B[bB.xmin].x(), B[bB.ymin].y()}, {B[bB.xmax].x(), B[bB.ymax].y()}};
if (!bbA.overlap(bbB)) // if (!bbA.overlap(bbB))
return false; // return false;
// Establish starting antipodals as extreme vertex pairs in X or Y direction // Establish starting antipodals as extreme vertex pairs in X or Y direction
// which reside on different polygons. If no such pair is found, the two // which reside on different polygons. If no such pair is found, the two

View File

@ -3203,6 +3203,14 @@ void GCodeViewer::render_shells()
if (shader == nullptr) if (shader == nullptr)
return; return;
// when the background processing is enabled, it may happen that the shells data have been loaded
// before opengl has been initialized for the preview canvas.
// when this happens, the volumes' data have not been sent to gpu yet.
for (GLVolume* v : m_shells.volumes.volumes) {
if (!v->indexed_vertex_array.has_VBOs())
v->finalize_geometry(true);
}
// glsafe(::glDepthMask(GL_FALSE)); // glsafe(::glDepthMask(GL_FALSE));
shader->start_using(); shader->start_using();

View File

@ -382,46 +382,57 @@ int ObjectList::get_mesh_errors_count(const int obj_idx, const int vol_idx /*= -
static std::string get_warning_icon_name(const TriangleMeshStats& stats) static std::string get_warning_icon_name(const TriangleMeshStats& stats)
{ {
return stats.repaired() ? (stats.manifold() ? "exclamation_manifold" : "exclamation") : ""; return stats.manifold() ? (stats.repaired() ? "exclamation_manifold" : "") : "exclamation";
} }
std::pair<wxString, std::string> ObjectList::get_mesh_errors(const int obj_idx, const int vol_idx /*= -1*/, bool from_plater /*= false*/) const std::pair<wxString, std::string> ObjectList::get_mesh_errors(const int obj_idx, const int vol_idx /*= -1*/, wxString* sidebar_info /*= nullptr*/) 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 = format_wxstr(_L_PLURAL("Auto-repaired %1$d error", "Auto-repaired %1$d errors", errors), errors) + ":\n";
const TriangleMeshStats& stats = vol_idx == -1 ? const TriangleMeshStats& stats = vol_idx == -1 ?
(*m_objects)[obj_idx]->get_object_stl_stats() : (*m_objects)[obj_idx]->get_object_stl_stats() :
(*m_objects)[obj_idx]->volumes[vol_idx]->mesh().stats(); (*m_objects)[obj_idx]->volumes[vol_idx]->mesh().stats();
if (stats.degenerate_facets > 0) if (!stats.repaired() && stats.manifold()) {
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d degenerate facet", "%1$d degenerate facets", stats.degenerate_facets), stats.degenerate_facets) + "\n"; if (sidebar_info)
if (stats.edges_fixed > 0) *sidebar_info = _L("No errors detected");
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d edge fixed", "%1$d edges fixed", stats.edges_fixed), stats.edges_fixed) + "\n"; return { {}, {} }; // hide tooltip
if (stats.facets_removed > 0) }
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d facet removed", "%1$d facets removed", stats.facets_removed), stats.facets_removed) + "\n";
if (stats.facets_reversed > 0)
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d facet reversed", "%1$d facets reversed", stats.facets_reversed), stats.facets_reversed) + "\n";
if (stats.backwards_edges > 0)
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d backwards edge", "%1$d backwards edges", stats.backwards_edges), stats.backwards_edges) + "\n";
wxString tooltip, auto_repaired_info, remaining_info;
// Create tooltip string, if there are errors
if (stats.repaired()) {
const int errors = get_mesh_errors_count(obj_idx, vol_idx);
auto_repaired_info = format_wxstr(_L_PLURAL("Auto-repaired %1$d error", "Auto-repaired %1$d errors", errors), errors);
tooltip += auto_repaired_info +":\n";
if (stats.degenerate_facets > 0)
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d degenerate facet", "%1$d degenerate facets", stats.degenerate_facets), stats.degenerate_facets) + "\n";
if (stats.edges_fixed > 0)
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d edge fixed", "%1$d edges fixed", stats.edges_fixed), stats.edges_fixed) + "\n";
if (stats.facets_removed > 0)
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d facet removed", "%1$d facets removed", stats.facets_removed), stats.facets_removed) + "\n";
if (stats.facets_reversed > 0)
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d facet reversed", "%1$d facets reversed", stats.facets_reversed), stats.facets_reversed) + "\n";
if (stats.backwards_edges > 0)
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d backwards edge", "%1$d backwards edges", stats.backwards_edges), stats.backwards_edges) + "\n";
}
if (!stats.manifold()) { if (!stats.manifold()) {
remaining_info = format_wxstr(_L_PLURAL("Remaining %1$d open edge", "Remaining %1$d open edges", stats.open_edges), stats.open_edges);
tooltip += _L("Remaning errors") + ":\n"; tooltip += _L("Remaning errors") + ":\n";
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d open edge", "%1$d open edges", stats.open_edges), stats.open_edges) + "\n"; tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d open edge", "%1$d open edges", stats.open_edges), stats.open_edges) + "\n";
} }
if (is_windows10() && !from_plater) if (sidebar_info)
*sidebar_info = stats.manifold() ? auto_repaired_info : (remaining_info + (stats.repaired() ? ("\n" + auto_repaired_info) : ""));
if (is_windows10() && !sidebar_info)
tooltip += "\n" + _L("Right button click the icon to fix STL through Netfabb"); tooltip += "\n" + _L("Right button click the icon to fix STL through Netfabb");
return { tooltip, get_warning_icon_name(stats) }; return { tooltip, get_warning_icon_name(stats) };
} }
std::pair<wxString, std::string> ObjectList::get_mesh_errors(bool from_plater /*= false*/) std::pair<wxString, std::string> ObjectList::get_mesh_errors(wxString* sidebar_info /*= nullptr*/)
{ {
if (!GetSelection()) if (!GetSelection())
return { "", "" }; return { "", "" };
@ -429,7 +440,7 @@ std::pair<wxString, std::string> ObjectList::get_mesh_errors(bool from_plater /*
int obj_idx, vol_idx; int obj_idx, vol_idx;
get_selected_item_indexes(obj_idx, vol_idx); get_selected_item_indexes(obj_idx, vol_idx);
return get_mesh_errors(obj_idx, vol_idx, from_plater); return get_mesh_errors(obj_idx, vol_idx, sidebar_info);
} }
void ObjectList::set_tooltip_for_item(const wxPoint& pt) void ObjectList::set_tooltip_for_item(const wxPoint& pt)

View File

@ -217,8 +217,8 @@ public:
// Return value is a pair <Tooltip, warning_icon_name>, used for the tooltip and related warning icon // Return value is a pair <Tooltip, warning_icon_name>, used for the tooltip and related warning icon
// Function without parameters is for a call from Manipulation panel, // Function without parameters is for a call from Manipulation panel,
// when we don't know parameters of selected item // when we don't know parameters of selected item
std::pair<wxString, std::string> get_mesh_errors(const int obj_idx, const int vol_idx = -1, bool from_plater = false) const; std::pair<wxString, std::string> get_mesh_errors(const int obj_idx, const int vol_idx = -1, wxString* sidebar_info = nullptr) const;
std::pair<wxString, std::string> get_mesh_errors(bool from_plater = false); std::pair<wxString, std::string> get_mesh_errors(wxString* sidebar_info = nullptr);
void set_tooltip_for_item(const wxPoint& pt); void set_tooltip_for_item(const wxPoint& pt);
void selection_changed(); void selection_changed();

View File

@ -962,10 +962,9 @@ void Preview::load_print_as_fff(bool keep_z_range)
if (0 <= type && type < static_cast<int>(GCodeViewer::EViewType::Count)) { if (0 <= type && type < static_cast<int>(GCodeViewer::EViewType::Count)) {
m_choice_view_type->SetSelection(type); m_choice_view_type->SetSelection(type);
m_canvas->set_gcode_view_preview_type(static_cast<GCodeViewer::EViewType>(type)); m_canvas->set_gcode_view_preview_type(static_cast<GCodeViewer::EViewType>(type));
if (wxGetApp().is_gcode_viewer()) { if (wxGetApp().is_gcode_viewer())
m_keep_current_preview_type = true; m_keep_current_preview_type = true;
refresh_print(); refresh_print();
}
} }
} }
} }

View File

@ -5,10 +5,14 @@
#include "GUI_ObjectList.hpp" #include "GUI_ObjectList.hpp"
#include "GLCanvas3D.hpp" #include "GLCanvas3D.hpp"
#include "MainFrame.hpp" #include "MainFrame.hpp"
#include "Tab.hpp"
#include "libslic3r/AppConfig.hpp" #include "libslic3r/AppConfig.hpp"
#include "libslic3r/Utils.hpp" #include "libslic3r/Utils.hpp"
#include "libslic3r/Config.hpp" #include "libslic3r/Config.hpp"
#include "libslic3r/PresetBundle.hpp" #include "libslic3r/PresetBundle.hpp"
#include "libslic3r/Preset.hpp"
#include "libslic3r/Config.hpp"
#include "libslic3r/PrintConfig.hpp"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/nowide/fstream.hpp> #include <boost/nowide/fstream.hpp>
@ -159,6 +163,33 @@ TagCheckResult tag_check_system(const std::string& tag)
return TagCheckNotCompatible; return TagCheckNotCompatible;
} }
TagCheckResult tag_check_material(const std::string& tag)
{
if (const GUI::Tab* tab = wxGetApp().get_tab(Preset::Type::TYPE_FILAMENT)) {
// search PrintConfig filament_type to find if allowed tag
if (wxGetApp().app_config->get("filament_type").find(tag)) {
const Preset& preset = tab->m_presets->get_edited_preset();
const auto* opt = preset.config.opt<ConfigOptionStrings>("filament_type");
if (opt->values[0] == tag)
return TagCheckAffirmative;
return TagCheckNegative;
}
return TagCheckNotCompatible;
}
/* TODO: SLA materials
else if (const GUI::Tab* tab = wxGetApp().get_tab(Preset::Type::TYPE_SLA_MATERIAL)) {
//if (wxGetApp().app_config->get("material_type").find(tag)) {
const Preset& preset = tab->m_presets->get_edited_preset();
const auto* opt = preset.config.opt<ConfigOptionStrings>("material_type");
if (opt->values[0] == tag)
return TagCheckAffirmative;
return TagCheckNegative;
//}
return TagCheckNotCompatible;
}*/
return TagCheckNotCompatible;
}
// return true if NOT in disabled mode. // return true if NOT in disabled mode.
bool tags_check(const std::string& disabled_tags, const std::string& enabled_tags) bool tags_check(const std::string& disabled_tags, const std::string& enabled_tags)
{ {
@ -189,6 +220,11 @@ bool tags_check(const std::string& disabled_tags, const std::string& enabled_tag
if (result == TagCheckResult::TagCheckAffirmative) if (result == TagCheckResult::TagCheckAffirmative)
continue; continue;
result = tag_check_system(tag); result = tag_check_system(tag);
if (result == TagCheckResult::TagCheckNegative)
return false;
if (result == TagCheckResult::TagCheckAffirmative)
continue;
result = tag_check_material(tag);
if (result == TagCheckResult::TagCheckNegative) if (result == TagCheckResult::TagCheckNegative)
return false; return false;
if (result == TagCheckResult::TagCheckAffirmative) if (result == TagCheckResult::TagCheckAffirmative)
@ -225,6 +261,11 @@ bool tags_check(const std::string& disabled_tags, const std::string& enabled_tag
if (result == TagCheckResult::TagCheckAffirmative) if (result == TagCheckResult::TagCheckAffirmative)
return false; return false;
result = tag_check_system(tag); result = tag_check_system(tag);
if (result == TagCheckResult::TagCheckAffirmative)
return false;
if (result == TagCheckResult::TagCheckNegative)
continue;
result = tag_check_material(tag);
if (result == TagCheckResult::TagCheckAffirmative) if (result == TagCheckResult::TagCheckAffirmative)
return false; return false;
if (result == TagCheckResult::TagCheckNegative) if (result == TagCheckResult::TagCheckNegative)

View File

@ -27,6 +27,7 @@
#include <wx/numdlg.h> #include <wx/numdlg.h>
#include <wx/debug.h> #include <wx/debug.h>
#include <wx/busyinfo.h> #include <wx/busyinfo.h>
#include <wx/richtooltip.h>
#include "libslic3r/libslic3r.h" #include "libslic3r/libslic3r.h"
#include "libslic3r/Format/STL.hpp" #include "libslic3r/Format/STL.hpp"
@ -173,13 +174,10 @@ ObjectInfo::ObjectInfo(wxWindow *parent) :
label_materials = init_info_label(&info_materials, _L("Materials")); label_materials = init_info_label(&info_materials, _L("Materials"));
Add(grid_sizer, 0, wxEXPAND); Add(grid_sizer, 0, wxEXPAND);
auto *info_manifold_text = new wxStaticText(parent, wxID_ANY, _L("Manifold") + ":");
info_manifold_text->SetFont(wxGetApp().small_font());
info_manifold = new wxStaticText(parent, wxID_ANY, ""); info_manifold = new wxStaticText(parent, wxID_ANY, "");
info_manifold->SetFont(wxGetApp().small_font()); info_manifold->SetFont(wxGetApp().small_font());
manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, create_scaled_bitmap(m_warning_icon_name)); manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, create_scaled_bitmap(m_warning_icon_name));
auto *sizer_manifold = new wxBoxSizer(wxHORIZONTAL); auto *sizer_manifold = new wxBoxSizer(wxHORIZONTAL);
sizer_manifold->Add(info_manifold_text, 0);
sizer_manifold->Add(manifold_warning_icon, 0, wxLEFT, 2); sizer_manifold->Add(manifold_warning_icon, 0, wxLEFT, 2);
sizer_manifold->Add(info_manifold, 0, wxLEFT, 2); sizer_manifold->Add(info_manifold, 0, wxLEFT, 2);
Add(sizer_manifold, 0, wxEXPAND | wxTOP, 4); Add(sizer_manifold, 0, wxEXPAND | wxTOP, 4);
@ -201,10 +199,10 @@ void ObjectInfo::msw_rescale()
void ObjectInfo::update_warning_icon(const std::string& warning_icon_name) void ObjectInfo::update_warning_icon(const std::string& warning_icon_name)
{ {
if (warning_icon_name.empty()) if (showing_manifold_warning_icon = !warning_icon_name.empty()) {
return; m_warning_icon_name = warning_icon_name;
m_warning_icon_name = warning_icon_name; manifold_warning_icon->SetBitmap(create_scaled_bitmap(m_warning_icon_name));
manifold_warning_icon->SetBitmap(create_scaled_bitmap(m_warning_icon_name)); }
} }
enum SlicedInfoIdx enum SlicedInfoIdx
@ -611,6 +609,7 @@ struct Sidebar::priv
wxButton *btn_export_gcode; wxButton *btn_export_gcode;
wxButton *btn_reslice; wxButton *btn_reslice;
wxString btn_reslice_tip;
ScalableButton *btn_send_gcode; ScalableButton *btn_send_gcode;
//ScalableButton *btn_eject_device; //ScalableButton *btn_eject_device;
ScalableButton* btn_export_gcode_removable; //exports to removable drives (appears only if removable drive is connected) ScalableButton* btn_export_gcode_removable; //exports to removable drives (appears only if removable drive is connected)
@ -622,6 +621,7 @@ struct Sidebar::priv
~priv(); ~priv();
void show_preset_comboboxes(); void show_preset_comboboxes();
void show_rich_tip(const wxString& tooltip, wxButton* btn);
}; };
Sidebar::priv::~priv() Sidebar::priv::~priv()
@ -650,6 +650,18 @@ void Sidebar::priv::show_preset_comboboxes()
scrolled->Refresh(); scrolled->Refresh();
} }
void Sidebar::priv::show_rich_tip(const wxString& tooltip, wxButton* btn)
{
if (tooltip.IsEmpty())
return;
wxRichToolTip tip(tooltip, "");
tip.SetIcon(wxICON_NONE);
tip.SetTipKind(wxTipKind_BottomRight);
tip.SetTitleFont(wxGetApp().normal_font());
tip.SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
tip.SetTimeout(1200);
tip.ShowFor(btn);
}
// Sidebar / public // Sidebar / public
@ -784,7 +796,11 @@ Sidebar::Sidebar(Plater *parent)
#endif //__APPLE__ #endif //__APPLE__
ScalableBitmap bmp = ScalableBitmap(this, icon_name, bmp_px_cnt); ScalableBitmap bmp = ScalableBitmap(this, icon_name, bmp_px_cnt);
*btn = new ScalableButton(this, wxID_ANY, bmp, "", wxBU_EXACTFIT); *btn = new ScalableButton(this, wxID_ANY, bmp, "", wxBU_EXACTFIT);
(*btn)->SetToolTip(tooltip);
(*btn)->Bind(wxEVT_ENTER_WINDOW, [tooltip, btn, this](wxMouseEvent& event) {
p->show_rich_tip(tooltip, *btn);
event.Skip();
});
(*btn)->Hide(); (*btn)->Hide();
}; };
@ -843,6 +859,11 @@ Sidebar::Sidebar(Plater *parent)
p->plater->reslice(); p->plater->reslice();
p->plater->select_view_3D("Preview"); p->plater->select_view_3D("Preview");
}); });
p->btn_reslice->Bind(wxEVT_ENTER_WINDOW, [this](wxMouseEvent& event) {
p->show_rich_tip(p->btn_reslice_tip, p->btn_reslice);
event.Skip();
});
p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); }); p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); });
// p->btn_eject_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); }); // p->btn_eject_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); });
p->btn_export_gcode_removable->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(true); }); p->btn_export_gcode_removable->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(true); });
@ -970,7 +991,7 @@ void Sidebar::update_reslice_btn_tooltip() const
wxString tooltip = wxString("Slice") + " [" + GUI::shortkey_ctrl_prefix() + "R]"; wxString tooltip = wxString("Slice") + " [" + GUI::shortkey_ctrl_prefix() + "R]";
if (m_mode != comSimple) if (m_mode != comSimple)
tooltip += wxString("\n") + _L("Hold Shift to Slice & Export G-code"); tooltip += wxString("\n") + _L("Hold Shift to Slice & Export G-code");
p->btn_reslice->SetToolTip(tooltip); p->btn_reslice_tip = tooltip;
} }
void Sidebar::msw_rescale() void Sidebar::msw_rescale()
@ -1144,24 +1165,13 @@ void Sidebar::show_info_sizer()
p->object_info->info_facets->SetLabel(format_wxstr(_L_PLURAL("%1% (%2$d shell)", "%1% (%2$d shells)", stats.number_of_parts), p->object_info->info_facets->SetLabel(format_wxstr(_L_PLURAL("%1% (%2$d shell)", "%1% (%2$d shells)", stats.number_of_parts),
static_cast<int>(model_object->facets_count()), stats.number_of_parts)); static_cast<int>(model_object->facets_count()), stats.number_of_parts));
if (stats.repaired()) { wxString info_manifold_label;
int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + stats.facets_reversed + stats.backwards_edges; auto mesh_errors = obj_list()->get_mesh_errors(&info_manifold_label);
p->object_info->info_manifold->SetLabel(format_wxstr(_L_PLURAL("Auto-repaired %1$d error", "Auto-repaired %1$d errors", errors), errors)); wxString tooltip = mesh_errors.first;
p->object_info->update_warning_icon(mesh_errors.second);
auto mesh_errors = obj_list()->get_mesh_errors(true); p->object_info->info_manifold->SetLabel(info_manifold_label);
wxString tooltip = mesh_errors.first; p->object_info->info_manifold->SetToolTip(tooltip);
p->object_info->manifold_warning_icon->SetToolTip(tooltip);
p->object_info->update_warning_icon(mesh_errors.second);
p->object_info->showing_manifold_warning_icon = true;
p->object_info->info_manifold->SetToolTip(tooltip);
p->object_info->manifold_warning_icon->SetToolTip(tooltip);
}
else {
p->object_info->info_manifold->SetLabel(_L("Yes"));
p->object_info->showing_manifold_warning_icon = false;
p->object_info->info_manifold->SetToolTip("");
p->object_info->manifold_warning_icon->SetToolTip("");
}
p->object_info->show_sizer(true); p->object_info->show_sizer(true);