Merge branch 'fs_QuadricEdgeCollapse'

This commit is contained in:
Lukas Matena 2021-08-06 18:23:49 +02:00
commit 0ccc791750
23 changed files with 1472 additions and 55 deletions

View file

@ -57,6 +57,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Gizmos/GLGizmoPainterBase.hpp
GUI/Gizmos/GLGizmoSeam.cpp
GUI/Gizmos/GLGizmoSeam.hpp
GUI/Gizmos/GLGizmoSimplify.cpp
GUI/Gizmos/GLGizmoSimplify.hpp
GUI/Gizmos/GLGizmoMmuSegmentation.cpp
GUI/Gizmos/GLGizmoMmuSegmentation.hpp
GUI/GLSelectionRectangle.cpp

View file

@ -657,6 +657,15 @@ wxMenuItem* MenuFactory::append_menu_item_fix_through_netfabb(wxMenu* menu)
wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _L("Fix through the Netfabb"), "",
[](wxCommandEvent&) { obj_list()->fix_through_netfabb(); }, "", menu,
[]() {return plater()->can_fix_through_netfabb(); }, plater());
return menu_item;
}
wxMenuItem* MenuFactory::append_menu_item_simplify(wxMenu* menu)
{
wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _L("Simplify model"), "",
[](wxCommandEvent&) { obj_list()->simplify(); }, "", menu,
[]() {return plater()->can_simplify(); }, plater());
menu->AppendSeparator();
return menu_item;
@ -874,6 +883,7 @@ void MenuFactory::create_common_object_menu(wxMenu* menu)
append_menu_item_scale_selection_to_fit_print_volume(menu);
append_menu_item_fix_through_netfabb(menu);
append_menu_item_simplify(menu);
append_menu_items_mirror(menu);
}
@ -923,6 +933,7 @@ void MenuFactory::create_part_menu()
append_menu_item_replace_with_stl(menu);
append_menu_item_export_stl(menu);
append_menu_item_fix_through_netfabb(menu);
append_menu_item_simplify(menu);
append_menu_items_mirror(menu);
append_menu_item(menu, wxID_ANY, _L("Split"), _L("Split the selected object into individual parts"),

View file

@ -92,6 +92,7 @@ private:
wxMenuItem* append_menu_item_printable(wxMenu* menu);
void append_menu_items_osx(wxMenu* menu);
wxMenuItem* append_menu_item_fix_through_netfabb(wxMenu* menu);
wxMenuItem* append_menu_item_simplify(wxMenu* menu);
void append_menu_item_export_stl(wxMenu* menu);
void append_menu_item_reload_from_disk(wxMenu* menu);
void append_menu_item_replace_with_stl(wxMenu* menu);

View file

@ -3955,6 +3955,12 @@ void ObjectList::fix_through_netfabb()
update_item_error_icon(obj_idx, vol_idx);
}
void ObjectList::simplify()
{
GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager();
gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify);
}
void ObjectList::update_item_error_icon(const int obj_idx, const int vol_idx) const
{
const wxDataViewItem item = vol_idx <0 ? m_objects_model->GetItemById(obj_idx) :
@ -3972,6 +3978,8 @@ void ObjectList::update_item_error_icon(const int obj_idx, const int vol_idx) co
// unmark fixed item only
m_objects_model->DeleteWarningIcon(item);
}
else
m_objects_model->AddWarningIcon(item);
}
void ObjectList::msw_rescale()

View file

@ -357,6 +357,7 @@ public:
void split_instances();
void rename_item();
void fix_through_netfabb();
void simplify();
void update_item_error_icon(const int obj_idx, int vol_idx) const ;
void copy_layers_to_clipboard();

View file

@ -51,6 +51,11 @@ bool GLGizmoMmuSegmentation::on_is_selectable() const
&& wxGetApp().get_mode() != comSimple && wxGetApp().extruders_edited_cnt() > 1);
}
bool GLGizmoMmuSegmentation::on_is_activable() const
{
return GLGizmoPainterBase::on_is_activable() && wxGetApp().extruders_edited_cnt() > 1;
}
static std::vector<std::array<float, 4>> get_extruders_colors()
{
unsigned char rgb_color[3] = {};

View file

@ -109,6 +109,7 @@ protected:
std::string on_get_name() const override;
bool on_is_selectable() const override;
bool on_is_activable() const override;
wxString handle_snapshot_action_name(bool shift_down, Button button_down) const override;

View file

@ -520,7 +520,7 @@ bool GLGizmoPainterBase::on_is_activable() const
const Selection& selection = m_parent.get_selection();
if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptFFF
|| !selection.is_single_full_instance())
|| !selection.is_single_full_instance() || wxGetApp().get_mode() == comSimple)
return false;
// Check that none of the selected volumes is outside. Only SLA auxiliaries (supports) are allowed outside.

View file

@ -0,0 +1,322 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmoSimplify.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/GUI_ObjectManipulation.hpp"
#include "slic3r/GUI/GUI_ObjectList.hpp"
#include "libslic3r/AppConfig.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/QuadricEdgeCollapse.hpp"
#include <chrono>
#include <thread>
namespace Slic3r::GUI {
GLGizmoSimplify::GLGizmoSimplify(GLCanvas3D & parent,
const std::string &icon_filename,
unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, -1)
, state(State::settings)
, is_valid_result(false)
, progress(0)
, volume(nullptr)
, obj_index(0)
, need_reload(false)
{}
GLGizmoSimplify::~GLGizmoSimplify() {
state = State::canceling;
if (worker.joinable()) worker.join();
}
bool GLGizmoSimplify::on_init()
{
//m_grabbers.emplace_back();
//m_shortcut_key = WXK_CONTROL_C;
return true;
}
std::string GLGizmoSimplify::on_get_name() const
{
return (_L("Simplify")).ToUTF8().data();
}
void GLGizmoSimplify::on_render() {}
void GLGizmoSimplify::on_render_for_picking() {}
void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limit)
{
const int min_triangle_count = 4; // tetrahedron
const int max_char_in_name = 25;
create_gui_cfg();
int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoCollapse;
m_imgui->begin(on_get_name(), flag);
const Selection &selection = m_parent.get_selection();
int object_idx = selection.get_object_idx();
ModelObject *obj = wxGetApp().plater()->model().objects[object_idx];
ModelVolume *act_volume = obj->volumes.front();
// Check selection of new volume
// Do not reselect object when processing
if (act_volume != volume && state == State::settings) {
obj_index = object_idx; // to remember correct object
volume = act_volume;
original_its = {};
const TriangleMesh &tm = volume->mesh();
c.wanted_percent = 50.; // default value
c.update_percent(tm.its.indices.size());
is_valid_result = false;
// set window position
ImVec2 pos = ImGui::GetMousePos();
pos.x -= gui_cfg->window_offset;
pos.y -= gui_cfg->window_offset;
ImGui::SetWindowPos(pos, ImGuiCond_Always);
}
size_t triangle_count = volume->mesh().its.indices.size();
// already reduced mesh
if (original_its.has_value())
triangle_count = original_its->indices.size();
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Mesh name") + ":");
ImGui::SameLine(gui_cfg->top_left_width);
std::string name = volume->name;
if (name.length() > max_char_in_name)
name = name.substr(0, max_char_in_name-3) + "...";
m_imgui->text(name);
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Triangles") + ":");
ImGui::SameLine(gui_cfg->top_left_width);
m_imgui->text(std::to_string(triangle_count));
ImGui::Separator();
ImGui::Text(_L("Limit by triangles").c_str());
ImGui::SameLine(gui_cfg->bottom_left_width);
// First initialization + fix triangle count
if (m_imgui->checkbox("##UseCount", c.use_count)) {
if (!c.use_count) c.use_error = true;
is_valid_result = false;
}
m_imgui->disabled_begin(!c.use_count);
ImGui::Text(_L("Triangle count").c_str());
ImGui::SameLine(gui_cfg->bottom_left_width);
int wanted_count = c.wanted_count;
ImGui::SetNextItemWidth(gui_cfg->input_width);
if (ImGui::SliderInt("##triangle_count", &wanted_count, min_triangle_count, triangle_count, "%d")) {
c.wanted_count = static_cast<uint32_t>(wanted_count);
if (c.wanted_count < min_triangle_count)
c.wanted_count = min_triangle_count;
if (c.wanted_count > triangle_count)
c.wanted_count = triangle_count;
c.update_count(triangle_count);
is_valid_result = false;
}
ImGui::Text(_L("Ratio").c_str());
ImGui::SameLine(gui_cfg->bottom_left_width);
ImGui::SetNextItemWidth(gui_cfg->input_small_width);
const char * precision = (c.wanted_percent > 10)? "%.0f": ((c.wanted_percent > 1)? "%.1f":"%.2f");
float step = (c.wanted_percent > 10)? 1.f: ((c.wanted_percent > 1)? 0.1f : 0.01f);
if (ImGui::InputFloat("%", &c.wanted_percent, step, 10*step, precision)) {
if (c.wanted_percent > 100.f) c.wanted_percent = 100.f;
c.update_percent(triangle_count);
if (c.wanted_count < min_triangle_count) {
c.wanted_count = min_triangle_count;
c.update_count(triangle_count);
}
is_valid_result = false;
}
m_imgui->disabled_end(); // use_count
ImGui::NewLine();
ImGui::Text(_L("Limit by error").c_str());
ImGui::SameLine(gui_cfg->bottom_left_width);
if (m_imgui->checkbox("##UseError", c.use_error)) {
if (!c.use_error) c.use_count = true;
is_valid_result = false;
}
m_imgui->disabled_begin(!c.use_error);
ImGui::Text(_L("Max. error").c_str());
ImGui::SameLine(gui_cfg->bottom_left_width);
ImGui::SetNextItemWidth(gui_cfg->input_small_width);
if (ImGui::InputFloat("##maxError", &c.max_error, 0.01f, .1f, "%.2f")) {
if (c.max_error < 0.f) c.max_error = 0.f;
is_valid_result = false;
}
m_imgui->disabled_end(); // use_error
if (state == State::settings) {
if (m_imgui->button(_L("Cancel"))) {
if (original_its.has_value()) {
set_its(*original_its);
state = State::close_on_end;
} else {
close();
}
}
ImGui::SameLine(gui_cfg->bottom_left_width);
if (m_imgui->button(_L("Preview"))) {
state = State::simplifying;
// simplify but not aply on mesh
process();
}
ImGui::SameLine();
if (m_imgui->button(_L("Apply"))) {
if (!is_valid_result) {
state = State::close_on_end;
process();
} else {
// use preview and close
close();
}
}
} else {
m_imgui->disabled_begin(state == State::canceling);
if (m_imgui->button(_L("Cancel"))) state = State::canceling;
m_imgui->disabled_end();
ImGui::SameLine(gui_cfg->bottom_left_width);
// draw progress bar
char buf[32];
sprintf(buf, L("Process %d / 100"), progress);
ImGui::ProgressBar(progress / 100., ImVec2(gui_cfg->input_width, 0.f), buf);
}
m_imgui->end();
if (need_reload) {
need_reload = false;
// Reload visualization of mesh - change VBO, FBO on GPU
m_parent.reload_scene(true); // deactivate gizmo??
GLGizmosManager &gizmos_mgr = m_parent.get_gizmos_manager();
gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify);
if (state == State::close_on_end) {
// fix hollowing, sla support points, modifiers, ...
auto plater = wxGetApp().plater();
plater->changed_mesh(obj_index); // deactivate gizmo??
// changed_mesh cause close();
//close();
}
// change from simplifying | aply
state = State::settings;
// Fix warning icon in object list
wxGetApp().obj_list()->update_item_error_icon(obj_index, -1);
}
}
void GLGizmoSimplify::close() {
volume = nullptr;
// close gizmo == open it again
GLGizmosManager &gizmos_mgr = m_parent.get_gizmos_manager();
gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify);
}
void GLGizmoSimplify::process()
{
class SimplifyCanceledException : public std::exception {
public:
const char* what() const throw() { return L("Model simplification has been canceled"); }
};
if (!original_its.has_value())
original_its = volume->mesh().its; // copy
auto plater = wxGetApp().plater();
plater->take_snapshot(_L("Simplify ") + volume->name);
plater->clear_before_change_mesh(obj_index);
progress = 0;
if (worker.joinable()) worker.join();
worker = std::thread([&]() {
// store original triangles
uint32_t triangle_count = (c.use_count) ? c.wanted_count : 0;
float max_error = (c.use_error) ? c.max_error : std::numeric_limits<float>::max();
std::function<void(void)> throw_on_cancel = [&]() {
if (state == State::canceling) {
throw SimplifyCanceledException();
}
};
std::function<void(int)> statusfn = [&](int percent) {
progress = percent;
m_parent.schedule_extra_frame(0);
};
indexed_triangle_set collapsed;
if (last_error.has_value()) {
// is chance to continue with last reduction
const indexed_triangle_set &its = volume->mesh().its;
uint32_t last_triangle_count = static_cast<uint32_t>(its.indices.size());
if ((!c.use_count || triangle_count <= last_triangle_count) &&
(!c.use_error || c.max_error <= *last_error)) {
collapsed = its; // small copy
} else {
collapsed = *original_its; // copy
}
} else {
collapsed = *original_its; // copy
}
try {
its_quadric_edge_collapse(collapsed, triangle_count, &max_error, throw_on_cancel, statusfn);
set_its(collapsed);
is_valid_result = true;
last_error = max_error;
} catch (SimplifyCanceledException &) {
state = State::settings;
}
// need to render last status fn
// without sleep it freezes until mouse move
std::this_thread::sleep_for(std::chrono::milliseconds(50));
m_parent.schedule_extra_frame(0);
});
}
void GLGizmoSimplify::set_its(indexed_triangle_set &its) {
auto tm = std::make_unique<TriangleMesh>(its);
tm->repair();
volume->set_mesh(std::move(tm));
volume->set_new_unique_id();
volume->get_object()->invalidate_bounding_box();
need_reload = true;
}
bool GLGizmoSimplify::on_is_activable() const
{
return !m_parent.get_selection().is_empty();
}
void GLGizmoSimplify::create_gui_cfg() {
if (gui_cfg.has_value()) return;
int space_size = m_imgui->calc_text_size(":MM").x;
GuiCfg cfg;
cfg.top_left_width = std::max(m_imgui->calc_text_size(_L("Mesh name")).x,
m_imgui->calc_text_size(_L("Triangles")).x)
+ space_size;
cfg.bottom_left_width =
std::max(
std::max(m_imgui->calc_text_size(_L("Limit by triangles")).x,
std::max(m_imgui->calc_text_size(_L("Triangle count")).x,
m_imgui->calc_text_size(_L("Ratio")).x)),
std::max(m_imgui->calc_text_size(_L("Limit by error")).x,
m_imgui->calc_text_size(_L("Max. error")).x)) + space_size;
cfg.input_width = cfg.bottom_left_width;
cfg.input_small_width = cfg.input_width * 0.8;
cfg.window_offset = cfg.input_width;
gui_cfg = cfg;
}
} // namespace Slic3r::GUI

View file

@ -0,0 +1,89 @@
#ifndef slic3r_GLGizmoSimplify_hpp_
#define slic3r_GLGizmoSimplify_hpp_
#include "GLGizmoBase.hpp"
#include "libslic3r/Model.hpp"
#include <thread>
#include <optional>
namespace Slic3r {
namespace GUI {
class GLGizmoSimplify : public GLGizmoBase
{
enum class State {
settings,
simplifying, // start processing
canceling, // canceled
successfull, // successful simplified
close_on_end
} state;
bool is_valid_result; // differ what to do in apply
int progress;
ModelVolume *volume;
size_t obj_index;
std::optional<indexed_triangle_set> original_its;
std::optional<float> last_error; // for use previous reduction
bool need_reload; // after simplify, glReload must be on main thread
std::thread worker;
public:
GLGizmoSimplify(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
virtual ~GLGizmoSimplify();
protected:
virtual bool on_init() override;
virtual std::string on_get_name() const override;
virtual void on_render() override;
virtual void on_render_for_picking() override;
virtual void on_render_input_window(float x, float y, float bottom_limit) override;
virtual bool on_is_activable() const override;
virtual bool on_is_selectable() const override { return false; }
private:
void close();
void process();
void set_its(indexed_triangle_set &its);
struct Configuration
{
bool use_count = true;
// minimal triangle count
float wanted_percent = 50.f;
uint32_t wanted_count = 0; // initialize by percents
bool use_error = false;
// maximal quadric error
float max_error = 1.;
void update_count(size_t triangle_count)
{
wanted_percent = (float) wanted_count / triangle_count * 100.f;
}
void update_percent(size_t triangle_count)
{
wanted_count = static_cast<uint32_t>(
std::round(triangle_count * wanted_percent / 100.f));
}
} c;
// This configs holds GUI layout size given by translated texts.
// etc. When language changes, GUI is recreated and this class constructed again,
// so the change takes effect. (info by GLGizmoFdmSupports.hpp)
struct GuiCfg
{
int top_left_width = 100;
int bottom_left_width = 100;
int input_width = 100;
int input_small_width = 80;
int window_offset = 100;
};
std::optional<GuiCfg> gui_cfg;
void create_gui_cfg();
};
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_GLGizmoSimplify_hpp_

View file

@ -20,6 +20,7 @@
#include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/PresetBundle.hpp"
@ -50,14 +51,7 @@ std::vector<size_t> GLGizmosManager::get_selectable_idxs() const
return out;
}
std::vector<size_t> GLGizmosManager::get_activable_idxs() const
{
std::vector<size_t> out;
for (size_t i=0; i<m_gizmos.size(); ++i)
if (m_gizmos[i]->is_activable())
out.push_back(i);
return out;
}
size_t GLGizmosManager::get_gizmo_idx_from_mouse(const Vec2d& mouse_pos) const
{
@ -110,6 +104,7 @@ bool GLGizmosManager::init()
m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, "fdm_supports.svg", 7));
m_gizmos.emplace_back(new GLGizmoSeam(m_parent, "seam.svg", 8));
m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, "fdm_supports.svg", 9));
m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "cut.svg", 10));
m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent));
@ -169,7 +164,7 @@ void GLGizmosManager::refresh_on_off_state()
return;
if (m_current != Undefined
&& (! m_gizmos[m_current]->is_activable() || ! m_gizmos[m_current]->is_selectable())) {
&& ! m_gizmos[m_current]->is_activable()) {
activate_gizmo(Undefined);
update_data();
}
@ -187,7 +182,7 @@ void GLGizmosManager::reset_all_states()
bool GLGizmosManager::open_gizmo(EType type)
{
int idx = int(type);
if (m_gizmos[idx]->is_selectable() && m_gizmos[idx]->is_activable()) {
if (m_gizmos[idx]->is_activable()) {
activate_gizmo(m_current == idx ? Undefined : (EType)idx);
update_data();
return true;
@ -304,7 +299,7 @@ bool GLGizmosManager::handle_shortcut(int key)
auto it = std::find_if(m_gizmos.begin(), m_gizmos.end(),
[key](const std::unique_ptr<GLGizmoBase>& gizmo) {
int gizmo_key = gizmo->get_shortcut_key();
return gizmo->is_selectable()
return gizmo->is_activable()
&& ((gizmo_key == key - 64) || (gizmo_key == key - 96));
});
@ -1077,6 +1072,7 @@ void GLGizmosManager::do_render_overlay() const
float u_offset = 1.0f / (float)tex_width;
float v_offset = 1.0f / (float)tex_height;
float current_y = FLT_MAX;
for (size_t idx : selectable_idxs)
{
GLGizmoBase* gizmo = m_gizmos[idx].get();
@ -1090,12 +1086,18 @@ void GLGizmosManager::do_render_overlay() const
float u_right = u_left + du - u_offset;
GLTexture::render_sub_texture(icons_texture_id, zoomed_top_x, zoomed_top_x + zoomed_icons_size, zoomed_top_y - zoomed_icons_size, zoomed_top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } });
if (idx == m_current) {
float toolbar_top = cnv_h - wxGetApp().plater()->get_view_toolbar().get_height();
gizmo->render_input_window(width, 0.5f * cnv_h - zoomed_top_y * zoom, toolbar_top);
if (idx == m_current || current_y == FLT_MAX) {
// The FLT_MAX trick is here so that even non-selectable but activable
// gizmos are passed some meaningful value.
current_y = 0.5f * cnv_h - zoomed_top_y * zoom;
}
zoomed_top_y -= zoomed_stride_y;
}
if (m_current != Undefined) {
float toolbar_top = cnv_h - wxGetApp().plater()->get_view_toolbar().get_height();
m_gizmos[m_current]->render_input_window(width, current_y, toolbar_top);
}
}
float GLGizmosManager::get_scaled_total_height() const

View file

@ -69,6 +69,7 @@ public:
FdmSupports,
Seam,
MmuSegmentation,
Simplify,
Undefined
};
@ -101,7 +102,6 @@ private:
std::pair<EType, bool> m_highlight; // bool true = higlightedShown, false = highlightedHidden
std::vector<size_t> get_selectable_idxs() const;
std::vector<size_t> get_activable_idxs() const;
size_t get_gizmo_idx_from_mouse(const Vec2d& mouse_pos) const;
void activate_gizmo(EType type);

View file

@ -1697,6 +1697,24 @@ wxBitmap ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_ty
return *bmp;
}
void ObjectDataViewModel::AddWarningIcon(const wxDataViewItem& item)
{
if (!item.IsOk())
return;
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
if (node->GetType() & itObject) {
node->SetBitmap(m_warning_bmp);
return;
}
if (node->GetType() & itVolume) {
node->SetBitmap(GetVolumeIcon(node->GetVolumeType(), true));
node->GetParent()->SetBitmap(m_warning_bmp);
return;
}
}
void ObjectDataViewModel::DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object/* = false*/)
{
if (!item.IsOk())

View file

@ -376,6 +376,7 @@ public:
wxBitmap GetVolumeIcon(const Slic3r::ModelVolumeType vol_type,
const bool is_marked = false);
void AddWarningIcon(const wxDataViewItem& item);
void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false);
t_layer_height_range GetLayerRangeByItem(const wxDataViewItem& item) const;

View file

@ -1771,6 +1771,7 @@ struct Plater::priv
bool can_arrange() const;
bool can_layers_editing() const;
bool can_fix_through_netfabb() const;
bool can_simplify() const;
bool can_set_instance_to_object() const;
bool can_mirror() const;
bool can_reload_from_disk() const;
@ -3495,44 +3496,10 @@ void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* =
// size_t snapshot_time = undo_redo_stack().active_snapshot_time();
Plater::TakeSnapshot snapshot(q, _L("Fix through NetFabb"));
q->clear_before_change_mesh(obj_idx);
ModelObject* mo = model.objects[obj_idx];
// If there are custom supports/seams/mmu segmentation, remove them. Fixed mesh
// may be different and they would make no sense.
bool paint_removed = false;
for (ModelVolume* mv : mo->volumes) {
paint_removed |= ! mv->supported_facets.empty() || ! mv->seam_facets.empty() || ! mv->mmu_segmentation_facets.empty();
mv->supported_facets.clear();
mv->seam_facets.clear();
mv->mmu_segmentation_facets.clear();
}
if (paint_removed) {
// snapshot_time is captured by copy so the lambda knows where to undo/redo to.
notification_manager->push_notification(
NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
NotificationManager::NotificationLevel::RegularNotification,
_u8L("Custom supports and seams were removed after repairing the mesh."));
// _u8L("Undo the repair"),
// [this, snapshot_time](wxEvtHandler*){
// // Make sure the snapshot is still available and that
// // we are in the main stack and not in a gizmo-stack.
// if (undo_redo_stack().has_undo_snapshot(snapshot_time)
// && q->canvas3D()->get_gizmos_manager().get_current() == nullptr)
// undo_redo_to(snapshot_time);
// else
// notification_manager->push_notification(
// NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
// NotificationManager::NotificationLevel::RegularNotification,
// _u8L("Cannot undo to before the mesh repair!"));
// return true;
// });
}
fix_model_by_win10_sdk_gui(*mo, vol_idx);
sla::reproject_points_and_holes(mo);
this->update();
this->object_list_changed();
this->schedule_background_process();
q->changed_mesh(obj_idx);
}
void Plater::priv::set_current_panel(wxPanel* panel)
@ -4304,6 +4271,12 @@ bool Plater::priv::can_fix_through_netfabb() const
return model.objects[obj_idx]->get_mesh_errors_count() > 0;
}
bool Plater::priv::can_simplify() const
{
return true;
}
bool Plater::priv::can_increase_instances() const
{
if (m_ui_jobs.is_any_running()
@ -6135,6 +6108,51 @@ bool Plater::set_printer_technology(PrinterTechnology printer_technology)
return ret;
}
void Plater::clear_before_change_mesh(int obj_idx)
{
ModelObject* mo = model().objects[obj_idx];
// If there are custom supports/seams/mmu segmentation, remove them. Fixed mesh
// may be different and they would make no sense.
bool paint_removed = false;
for (ModelVolume* mv : mo->volumes) {
paint_removed |= ! mv->supported_facets.empty() || ! mv->seam_facets.empty() || ! mv->mmu_segmentation_facets.empty();
mv->supported_facets.clear();
mv->seam_facets.clear();
mv->mmu_segmentation_facets.clear();
}
if (paint_removed) {
// snapshot_time is captured by copy so the lambda knows where to undo/redo to.
get_notification_manager()->push_notification(
NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
NotificationManager::NotificationLevel::RegularNotification,
_u8L("Custom supports and seams were removed after repairing the mesh."));
// _u8L("Undo the repair"),
// [this, snapshot_time](wxEvtHandler*){
// // Make sure the snapshot is still available and that
// // we are in the main stack and not in a gizmo-stack.
// if (undo_redo_stack().has_undo_snapshot(snapshot_time)
// && q->canvas3D()->get_gizmos_manager().get_current() == nullptr)
// undo_redo_to(snapshot_time);
// else
// notification_manager->push_notification(
// NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
// NotificationManager::NotificationLevel::RegularNotification,
// _u8L("Cannot undo to before the mesh repair!"));
// return true;
// });
}
}
void Plater::changed_mesh(int obj_idx)
{
ModelObject* mo = model().objects[obj_idx];
sla::reproject_points_and_holes(mo);
update();
p->object_list_changed();
p->schedule_background_process();
}
void Plater::changed_object(int obj_idx)
{
if (obj_idx < 0)
@ -6402,6 +6420,7 @@ bool Plater::can_increase_instances() const { return p->can_increase_instances()
bool Plater::can_decrease_instances() const { return p->can_decrease_instances(); }
bool Plater::can_set_instance_to_object() const { return p->can_set_instance_to_object(); }
bool Plater::can_fix_through_netfabb() const { return p->can_fix_through_netfabb(); }
bool Plater::can_simplify() const { return p->can_simplify(); }
bool Plater::can_split_to_objects() const { return p->can_split_to_objects(); }
bool Plater::can_split_to_volumes() const { return p->can_split_to_volumes(); }
bool Plater::can_arrange() const { return p->can_arrange(); }

View file

@ -226,6 +226,10 @@ public:
void reslice_SLA_supports(const ModelObject &object, bool postpone_error_messages = false);
void reslice_SLA_hollowing(const ModelObject &object, bool postpone_error_messages = false);
void reslice_SLA_until_step(SLAPrintObjectStep step, const ModelObject &object, bool postpone_error_messages = false);
void clear_before_change_mesh(int obj_idx);
void changed_mesh(int obj_idx);
void changed_object(int obj_idx);
void changed_objects(const std::vector<size_t>& object_idxs);
void schedule_background_process(bool schedule = true);
@ -306,6 +310,7 @@ public:
bool can_decrease_instances() const;
bool can_set_instance_to_object() const;
bool can_fix_through_netfabb() const;
bool can_simplify() const;
bool can_split_to_objects() const;
bool can_split_to_volumes() const;
bool can_arrange() const;