Add simplification GUI
This commit is contained in:
parent
756d2694eb
commit
af526c54f4
@ -57,6 +57,8 @@ set(SLIC3R_GUI_SOURCES
|
|||||||
GUI/Gizmos/GLGizmoPainterBase.hpp
|
GUI/Gizmos/GLGizmoPainterBase.hpp
|
||||||
GUI/Gizmos/GLGizmoSeam.cpp
|
GUI/Gizmos/GLGizmoSeam.cpp
|
||||||
GUI/Gizmos/GLGizmoSeam.hpp
|
GUI/Gizmos/GLGizmoSeam.hpp
|
||||||
|
GUI/Gizmos/GLGizmoSimplify.cpp
|
||||||
|
GUI/Gizmos/GLGizmoSimplify.hpp
|
||||||
GUI/Gizmos/GLGizmoMmuSegmentation.cpp
|
GUI/Gizmos/GLGizmoMmuSegmentation.cpp
|
||||||
GUI/Gizmos/GLGizmoMmuSegmentation.hpp
|
GUI/Gizmos/GLGizmoMmuSegmentation.hpp
|
||||||
GUI/GLSelectionRectangle.cpp
|
GUI/GLSelectionRectangle.cpp
|
||||||
@ -97,8 +99,6 @@ set(SLIC3R_GUI_SOURCES
|
|||||||
GUI/SavePresetDialog.cpp
|
GUI/SavePresetDialog.cpp
|
||||||
GUI/PhysicalPrinterDialog.hpp
|
GUI/PhysicalPrinterDialog.hpp
|
||||||
GUI/PhysicalPrinterDialog.cpp
|
GUI/PhysicalPrinterDialog.cpp
|
||||||
GUI/SimplificationDialog.cpp
|
|
||||||
GUI/SimplificationDialog.hpp
|
|
||||||
GUI/GUI_Factories.cpp
|
GUI/GUI_Factories.cpp
|
||||||
GUI/GUI_Factories.hpp
|
GUI/GUI_Factories.hpp
|
||||||
GUI/GUI_ObjectList.cpp
|
GUI/GUI_ObjectList.cpp
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
#include "GUI_Factories.hpp"
|
#include "GUI_Factories.hpp"
|
||||||
#include "GUI_ObjectManipulation.hpp"
|
#include "GUI_ObjectManipulation.hpp"
|
||||||
#include "GUI_ObjectLayers.hpp"
|
#include "GUI_ObjectLayers.hpp"
|
||||||
#include "SimplificationDialog.hpp"
|
|
||||||
#include "GUI_App.hpp"
|
#include "GUI_App.hpp"
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
#include "Plater.hpp"
|
#include "Plater.hpp"
|
||||||
@ -3762,13 +3761,8 @@ void ObjectList::fix_through_netfabb()
|
|||||||
|
|
||||||
void ObjectList::simplify()
|
void ObjectList::simplify()
|
||||||
{
|
{
|
||||||
int obj_idx, vol_idx;
|
GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager();
|
||||||
get_selected_item_indexes(obj_idx, vol_idx);
|
gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify);
|
||||||
|
|
||||||
SimplificationDialog dlg(this);
|
|
||||||
dlg.ShowModal();
|
|
||||||
|
|
||||||
wxGetApp().plater()->simplify(obj_idx, vol_idx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::update_item_error_icon(const int obj_idx, const int vol_idx) const
|
void ObjectList::update_item_error_icon(const int obj_idx, const int vol_idx) const
|
||||||
|
231
src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp
Normal file
231
src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
// 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 "libslic3r/AppConfig.hpp"
|
||||||
|
#include "libslic3r/Model.hpp"
|
||||||
|
#include "libslic3r/QuadricEdgeCollapse.hpp"
|
||||||
|
|
||||||
|
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() const{}
|
||||||
|
void GLGizmoSimplify::on_render_for_picking() const{}
|
||||||
|
|
||||||
|
void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limit)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t triangle_count = volume->mesh().its.indices.size();
|
||||||
|
// already reduced mesh
|
||||||
|
if (original_its.has_value())
|
||||||
|
triangle_count = original_its->indices.size();
|
||||||
|
|
||||||
|
int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize |
|
||||||
|
ImGuiWindowFlags_NoCollapse;
|
||||||
|
m_imgui->begin(_L("Simplify mesh ") + volume->name, flag);
|
||||||
|
std::string description = "Reduce amout of triangle in mesh( " +
|
||||||
|
volume->name + " has " +
|
||||||
|
std::to_string(triangle_count) + " triangles)";
|
||||||
|
ImGui::Text(description.c_str());
|
||||||
|
// First initialization + fix triangle count
|
||||||
|
if (c.wanted_count > triangle_count) c.update_percent(triangle_count);
|
||||||
|
if (m_imgui->checkbox(_L("Until triangle count is less than "), c.use_count)) {
|
||||||
|
if (!c.use_count) c.use_error = true;
|
||||||
|
is_valid_result = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_imgui->disabled_begin(!c.use_count);
|
||||||
|
ImGui::SameLine();
|
||||||
|
int wanted_count = c.wanted_count;
|
||||||
|
if (ImGui::SliderInt("triangles", &wanted_count, 0,
|
||||||
|
triangle_count, "%d")) {
|
||||||
|
c.wanted_count = static_cast<uint32_t>(wanted_count);
|
||||||
|
c.update_count(triangle_count);
|
||||||
|
is_valid_result = false;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(80);
|
||||||
|
int precision = (c.wanted_percent > 10)? 0: ((c.wanted_percent > 1)? 1:2);
|
||||||
|
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 < 0.f) c.wanted_percent = 0.f;
|
||||||
|
if (c.wanted_percent > 100.f) c.wanted_percent = 100.f;
|
||||||
|
c.update_percent(triangle_count);
|
||||||
|
is_valid_result = false;
|
||||||
|
}
|
||||||
|
m_imgui->disabled_end(); // use_count
|
||||||
|
|
||||||
|
if (m_imgui->checkbox(_L("Until error"), c.use_error)) {
|
||||||
|
if (!c.use_error) c.use_count = true;
|
||||||
|
is_valid_result = false;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
m_imgui->disabled_begin(!c.use_error);
|
||||||
|
if (ImGui::InputFloat("(maximal quadric error)", &c.max_error, 0.01f, .1f, 2)) {
|
||||||
|
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();
|
||||||
|
if (m_imgui->button(_L("Preview"))) {
|
||||||
|
state = State::simplifying;
|
||||||
|
// simplify but not aply on mesh
|
||||||
|
process();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (m_imgui->button(_L("Aply"))) {
|
||||||
|
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();
|
||||||
|
// draw progress bar
|
||||||
|
ImGui::Text("Processing %c \t %d / 100",
|
||||||
|
"|/-\\"[(int) (ImGui::GetTime() / 0.05f) & 3], progress);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 : nullptr;
|
||||||
|
std::function<void(void)> throw_on_cancel = [&]() { if ( state == State::canceling) throw SimplifyCanceledException(); };
|
||||||
|
std::function<void(int)> statusfn = [&](int percent) { progress = percent; };
|
||||||
|
indexed_triangle_set collapsed = original_its.value(); // copy
|
||||||
|
try {
|
||||||
|
its_quadric_edge_collapse(collapsed, triangle_count, max_error, throw_on_cancel, statusfn);
|
||||||
|
set_its(collapsed);
|
||||||
|
is_valid_result = true;
|
||||||
|
} catch (SimplifyCanceledException &) {
|
||||||
|
is_valid_result = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Slic3r::GUI
|
76
src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp
Normal file
76
src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
#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;
|
||||||
|
|
||||||
|
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();
|
||||||
|
void set_selectable(bool value);
|
||||||
|
protected:
|
||||||
|
virtual bool on_init() override;
|
||||||
|
virtual std::string on_get_name() const override;
|
||||||
|
virtual void on_render() const override;
|
||||||
|
virtual void on_render_for_picking() const 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));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Configuration c;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace GUI
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
#endif // slic3r_GLGizmoSimplify_hpp_
|
@ -20,6 +20,7 @@
|
|||||||
#include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp"
|
#include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp"
|
||||||
#include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp"
|
#include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp"
|
||||||
#include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp"
|
#include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp"
|
||||||
|
#include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp"
|
||||||
|
|
||||||
#include "libslic3r/Model.hpp"
|
#include "libslic3r/Model.hpp"
|
||||||
#include "libslic3r/PresetBundle.hpp"
|
#include "libslic3r/PresetBundle.hpp"
|
||||||
@ -110,6 +111,7 @@ bool GLGizmosManager::init()
|
|||||||
m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, "fdm_supports.svg", 7));
|
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 GLGizmoSeam(m_parent, "seam.svg", 8));
|
||||||
m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, "fdm_supports.svg", 9));
|
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));
|
m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent));
|
||||||
|
|
||||||
@ -169,7 +171,7 @@ void GLGizmosManager::reset_all_states()
|
|||||||
bool GLGizmosManager::open_gizmo(EType type)
|
bool GLGizmosManager::open_gizmo(EType type)
|
||||||
{
|
{
|
||||||
int idx = int(type);
|
int idx = int(type);
|
||||||
if (m_gizmos[idx]->is_selectable() && m_gizmos[idx]->is_activable()) {
|
if (/*m_gizmos[idx]->is_selectable() &&*/ m_gizmos[idx]->is_activable()) {
|
||||||
activate_gizmo(m_current == idx ? Undefined : (EType)idx);
|
activate_gizmo(m_current == idx ? Undefined : (EType)idx);
|
||||||
update_data();
|
update_data();
|
||||||
return true;
|
return true;
|
||||||
@ -1021,6 +1023,8 @@ void GLGizmosManager::do_render_overlay() const
|
|||||||
float u_offset = 1.0f / (float)tex_width;
|
float u_offset = 1.0f / (float)tex_width;
|
||||||
float v_offset = 1.0f / (float)tex_height;
|
float v_offset = 1.0f / (float)tex_height;
|
||||||
|
|
||||||
|
float toolbar_top = 0.f;
|
||||||
|
float current_y = 0.f;
|
||||||
for (size_t idx : selectable_idxs)
|
for (size_t idx : selectable_idxs)
|
||||||
{
|
{
|
||||||
GLGizmoBase* gizmo = m_gizmos[idx].get();
|
GLGizmoBase* gizmo = m_gizmos[idx].get();
|
||||||
@ -1035,11 +1039,14 @@ void GLGizmosManager::do_render_overlay() const
|
|||||||
|
|
||||||
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 } });
|
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) {
|
if (idx == m_current) {
|
||||||
float toolbar_top = cnv_h - wxGetApp().plater()->get_view_toolbar().get_height();
|
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);
|
current_y = 0.5f * cnv_h - zoomed_top_y * zoom;
|
||||||
}
|
}
|
||||||
zoomed_top_y -= zoomed_stride_y;
|
zoomed_top_y -= zoomed_stride_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_current != Undefined)
|
||||||
|
m_gizmos[m_current]->render_input_window(width, current_y, toolbar_top);
|
||||||
}
|
}
|
||||||
|
|
||||||
float GLGizmosManager::get_scaled_total_height() const
|
float GLGizmosManager::get_scaled_total_height() const
|
||||||
|
@ -69,6 +69,7 @@ public:
|
|||||||
FdmSupports,
|
FdmSupports,
|
||||||
Seam,
|
Seam,
|
||||||
MmuSegmentation,
|
MmuSegmentation,
|
||||||
|
Simplify,
|
||||||
Undefined
|
Undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1730,7 +1730,6 @@ struct Plater::priv
|
|||||||
void replace_with_stl();
|
void replace_with_stl();
|
||||||
void reload_all_from_disk();
|
void reload_all_from_disk();
|
||||||
void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
|
void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
|
||||||
void simplify(const int obj_idx, const int vol_idx = -1);
|
|
||||||
|
|
||||||
void set_current_panel(wxPanel* panel);
|
void set_current_panel(wxPanel* panel);
|
||||||
|
|
||||||
@ -3555,70 +3554,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();
|
// size_t snapshot_time = undo_redo_stack().active_snapshot_time();
|
||||||
Plater::TakeSnapshot snapshot(q, _L("Fix through NetFabb"));
|
Plater::TakeSnapshot snapshot(q, _L("Fix through NetFabb"));
|
||||||
|
|
||||||
|
q->clear_before_change_mesh(obj_idx);
|
||||||
ModelObject* mo = model.objects[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);
|
fix_model_by_win10_sdk_gui(*mo, vol_idx);
|
||||||
sla::reproject_points_and_holes(mo);
|
q->changed_mesh(obj_idx);
|
||||||
this->update();
|
|
||||||
this->object_list_changed();
|
|
||||||
this->schedule_background_process();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Plater::priv::simplify(const int obj_idx, const int vol_idx/* = -1*/)
|
|
||||||
{
|
|
||||||
if (obj_idx < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Do not fix anything when a gizmo is open. There might be issues with updates
|
|
||||||
// and what is worse, the snapshot time would refer to the internal stack.
|
|
||||||
if (q->canvas3D()->get_gizmos_manager().get_current_type() != GLGizmosManager::Undefined) {
|
|
||||||
notification_manager->push_notification(
|
|
||||||
NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
|
|
||||||
NotificationManager::NotificationLevel::RegularNotification,
|
|
||||||
_u8L("ERROR: Please close all manipulators available from "
|
|
||||||
"the left toolbar before fixing the mesh."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// size_t snapshot_time = undo_redo_stack().active_snapshot_time();
|
|
||||||
Plater::TakeSnapshot snapshot(q, _L("Symplify model"));
|
|
||||||
|
|
||||||
// ToDo
|
|
||||||
|
|
||||||
this->update();
|
|
||||||
this->object_list_changed();
|
|
||||||
this->schedule_background_process();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::priv::set_current_panel(wxPanel* panel)
|
void Plater::priv::set_current_panel(wxPanel* panel)
|
||||||
@ -6262,6 +6201,51 @@ bool Plater::set_printer_technology(PrinterTechnology printer_technology)
|
|||||||
return ret;
|
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)
|
void Plater::changed_object(int obj_idx)
|
||||||
{
|
{
|
||||||
if (obj_idx < 0)
|
if (obj_idx < 0)
|
||||||
@ -6343,7 +6327,6 @@ void Plater::suppress_background_process(const bool stop_background_process)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Plater::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/) { p->fix_through_netfabb(obj_idx, vol_idx); }
|
void Plater::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/) { p->fix_through_netfabb(obj_idx, vol_idx); }
|
||||||
void Plater::simplify(const int obj_idx, const int vol_idx/* = -1*/) { p->simplify(obj_idx, vol_idx); }
|
|
||||||
void Plater::mirror(Axis axis) { p->mirror(axis); }
|
void Plater::mirror(Axis axis) { p->mirror(axis); }
|
||||||
void Plater::split_object() { p->split_object(); }
|
void Plater::split_object() { p->split_object(); }
|
||||||
void Plater::split_volume() { p->split_volume(); }
|
void Plater::split_volume() { p->split_volume(); }
|
||||||
|
@ -230,13 +230,16 @@ public:
|
|||||||
void reslice_SLA_supports(const ModelObject &object, bool postpone_error_messages = false);
|
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_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 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_object(int obj_idx);
|
||||||
void changed_objects(const std::vector<size_t>& object_idxs);
|
void changed_objects(const std::vector<size_t>& object_idxs);
|
||||||
void schedule_background_process(bool schedule = true);
|
void schedule_background_process(bool schedule = true);
|
||||||
bool is_background_process_update_scheduled() const;
|
bool is_background_process_update_scheduled() const;
|
||||||
void suppress_background_process(const bool stop_background_process) ;
|
void suppress_background_process(const bool stop_background_process) ;
|
||||||
void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
|
void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
|
||||||
void simplify(const int obj_idx, const int vol_idx = -1);
|
|
||||||
void send_gcode();
|
void send_gcode();
|
||||||
void eject_drive();
|
void eject_drive();
|
||||||
|
|
||||||
|
@ -1,91 +0,0 @@
|
|||||||
#include "SimplificationDialog.hpp"
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <boost/algorithm/string.hpp>
|
|
||||||
|
|
||||||
#include <wx/sizer.h>
|
|
||||||
#include <wx/stattext.h>
|
|
||||||
#include <wx/textctrl.h>
|
|
||||||
#include <wx/button.h>
|
|
||||||
#include <wx/statbox.h>
|
|
||||||
#include <wx/wupdlock.h>
|
|
||||||
|
|
||||||
#include "GUI.hpp"
|
|
||||||
#include "GUI_App.hpp"
|
|
||||||
#include "format.hpp"
|
|
||||||
#include "wxExtensions.hpp"
|
|
||||||
#include "I18N.hpp"
|
|
||||||
#include "libslic3r/Utils.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
namespace Slic3r {
|
|
||||||
namespace GUI {
|
|
||||||
|
|
||||||
#define BORDER_W 10
|
|
||||||
|
|
||||||
SimplificationDialog::SimplificationDialog(wxWindow* parent) :
|
|
||||||
DPIDialog(parent, wxID_ANY, _L("Name of Dialog"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), -1), wxDEFAULT_DIALOG_STYLE/* | wxRESIZE_BORDER*/)
|
|
||||||
{
|
|
||||||
SetFont(wxGetApp().normal_font());
|
|
||||||
|
|
||||||
wxStaticText* label_top = new wxStaticText(this, wxID_ANY, _L("Some text") + ":");
|
|
||||||
|
|
||||||
wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(3, 2, 1, 2);
|
|
||||||
grid_sizer->AddGrowableCol(1, 1);
|
|
||||||
grid_sizer->SetFlexibleDirection(wxBOTH);
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
auto* text = new wxStaticText(this, wxID_ANY, _L("Text") + " " + std::to_string(i) + " :");
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
long style = wxBORDER_SIMPLE;
|
|
||||||
#else
|
|
||||||
long style = 0
|
|
||||||
#endif
|
|
||||||
auto value = new wxTextCtrl(this, wxID_ANY, "Some Value", wxDefaultPosition, wxDefaultSize, style);
|
|
||||||
|
|
||||||
grid_sizer->Add(text, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxRIGHT, 4);
|
|
||||||
grid_sizer->Add(value, 1, wxEXPAND | wxBOTTOM, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL);
|
|
||||||
this->Bind(wxEVT_BUTTON, &SimplificationDialog::OnOK, this, wxID_OK);
|
|
||||||
|
|
||||||
wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
|
|
||||||
|
|
||||||
topSizer->Add(label_top , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W);
|
|
||||||
topSizer->Add(grid_sizer, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, BORDER_W);
|
|
||||||
topSizer->Add(btns , 0, wxEXPAND | wxALL, BORDER_W);
|
|
||||||
|
|
||||||
SetSizer(topSizer);
|
|
||||||
topSizer->SetSizeHints(this);
|
|
||||||
|
|
||||||
wxGetApp().UpdateDlgDarkUI(this);
|
|
||||||
|
|
||||||
this->CenterOnScreen();
|
|
||||||
}
|
|
||||||
|
|
||||||
SimplificationDialog::~SimplificationDialog()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void SimplificationDialog::on_dpi_changed(const wxRect& suggested_rect)
|
|
||||||
{
|
|
||||||
const int& em = em_unit();
|
|
||||||
msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL });
|
|
||||||
|
|
||||||
const wxSize& size = wxSize(45 * em, 35 * em);
|
|
||||||
SetMinSize(size);
|
|
||||||
|
|
||||||
Fit();
|
|
||||||
Refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SimplificationDialog::OnOK(wxEvent& event)
|
|
||||||
{
|
|
||||||
event.Skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
}} // namespace Slic3r::GUI
|
|
@ -1,25 +0,0 @@
|
|||||||
#ifndef slic3r_SimplificationDialog_hpp_
|
|
||||||
#define slic3r_SimplificationDialog_hpp_
|
|
||||||
|
|
||||||
#include "GUI_Utils.hpp"
|
|
||||||
|
|
||||||
namespace Slic3r {
|
|
||||||
namespace GUI {
|
|
||||||
|
|
||||||
class SimplificationDialog : public DPIDialog
|
|
||||||
{
|
|
||||||
void OnOK(wxEvent& event);
|
|
||||||
|
|
||||||
public:
|
|
||||||
SimplificationDialog(wxWindow* parent);
|
|
||||||
~SimplificationDialog();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace GUI
|
|
||||||
} // namespace Slic3r
|
|
||||||
|
|
||||||
#endif //slic3r_SimplificationDialog_hpp_
|
|
@ -231,9 +231,10 @@ TEST_CASE("Reduce one edge by Quadric Edge Collapse", "[its]")
|
|||||||
}
|
}
|
||||||
|
|
||||||
#include "test_utils.hpp"
|
#include "test_utils.hpp"
|
||||||
TEST_CASE("Symplify mesh by Quadric edge collapse to 5%", "[its]")
|
TEST_CASE("Simplify mesh by Quadric edge collapse to 5%", "[its]")
|
||||||
{
|
{
|
||||||
TriangleMesh mesh = load_model("frog_legs.obj");
|
TriangleMesh mesh = load_model("frog_legs.obj");
|
||||||
|
//TriangleMesh mesh; load_obj("C:/Users/filip/Documents/models/scarecrow_torso.obj", &mesh);
|
||||||
double original_volume = its_volume(mesh.its);
|
double original_volume = its_volume(mesh.its);
|
||||||
uint32_t wanted_count = mesh.its.indices.size() * 0.05;
|
uint32_t wanted_count = mesh.its.indices.size() * 0.05;
|
||||||
REQUIRE_FALSE(mesh.empty());
|
REQUIRE_FALSE(mesh.empty());
|
||||||
|
@ -339,3 +339,104 @@ TEST_CASE("Mutable priority queue - reshedule first", "[MutableSkipHeapPriorityQ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Mutable priority queue - first pop", "[MutableSkipHeapPriorityQueue]")
|
||||||
|
{
|
||||||
|
struct MyValue{
|
||||||
|
int id;
|
||||||
|
float val;
|
||||||
|
};
|
||||||
|
size_t count = 50000;
|
||||||
|
std::vector<size_t> idxs(count, {0});
|
||||||
|
std::vector<bool> dels(count, false);
|
||||||
|
auto q = make_miniheap_mutable_priority_queue<MyValue, 16, true>(
|
||||||
|
[&](MyValue &v, size_t idx) {
|
||||||
|
idxs[v.id] = idx;
|
||||||
|
},
|
||||||
|
[](MyValue &l, MyValue &r) { return l.val < r.val; });
|
||||||
|
q.reserve(count);
|
||||||
|
for (size_t id = 0; id < count; id++) {
|
||||||
|
MyValue mv;
|
||||||
|
mv.id = id;
|
||||||
|
mv.val = rand();
|
||||||
|
q.push(mv);
|
||||||
|
}
|
||||||
|
MyValue it = q.top(); // copy
|
||||||
|
q.pop();
|
||||||
|
bool valid = (it.id != 0) && (idxs[0] < 3 * count);
|
||||||
|
CHECK(valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Mutable priority queue complex", "[MutableSkipHeapPriorityQueue]")
|
||||||
|
{
|
||||||
|
struct MyValue {
|
||||||
|
int id;
|
||||||
|
float val;
|
||||||
|
};
|
||||||
|
size_t count = 5000;
|
||||||
|
std::vector<size_t> idxs(count, {0});
|
||||||
|
std::vector<bool> dels(count, false);
|
||||||
|
auto q = make_miniheap_mutable_priority_queue<MyValue, 16, true>(
|
||||||
|
[&](MyValue &v, size_t idx) { idxs[v.id] = idx; },
|
||||||
|
[](MyValue &l, MyValue &r) { return l.val < r.val; });
|
||||||
|
q.reserve(count);
|
||||||
|
|
||||||
|
auto rand_val = [&]()->float { return (rand() % 53) / 10.f; };
|
||||||
|
int ord = 0;
|
||||||
|
for (size_t id = 0; id < count; id++) {
|
||||||
|
MyValue mv;
|
||||||
|
mv.id = ord++;
|
||||||
|
mv.val = rand_val();
|
||||||
|
q.push(mv);
|
||||||
|
}
|
||||||
|
auto check = [&]()->bool{
|
||||||
|
for (size_t i = 0; i < idxs.size(); ++i) {
|
||||||
|
if (dels[i]) continue;
|
||||||
|
size_t qid = idxs[i];
|
||||||
|
if (qid > 3*count) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
MyValue &mv = q[qid];
|
||||||
|
if (mv.id != i) {
|
||||||
|
return false; // ERROR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
CHECK(check()); // initial check
|
||||||
|
|
||||||
|
auto get_valid_id = [&]()->int {
|
||||||
|
int id = 0;
|
||||||
|
do {
|
||||||
|
id = rand() % count;
|
||||||
|
} while (dels[id]);
|
||||||
|
return id;
|
||||||
|
};
|
||||||
|
for (size_t i = 0; i < 100; i++) {
|
||||||
|
MyValue it = q.top(); // copy
|
||||||
|
q.pop();
|
||||||
|
dels[it.id] = true;
|
||||||
|
CHECK(check());
|
||||||
|
if (i % 20 == 0) {
|
||||||
|
it.val = rand_val();
|
||||||
|
q.push(it);
|
||||||
|
dels[it.id] = false;
|
||||||
|
CHECK(check());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int id = get_valid_id();
|
||||||
|
q.remove(idxs[id]);
|
||||||
|
dels[id] = true;
|
||||||
|
CHECK(check());
|
||||||
|
for (size_t j = 0; j < 5; j++) {
|
||||||
|
int id = get_valid_id();
|
||||||
|
size_t qid = idxs[id];
|
||||||
|
MyValue &mv = q[qid];
|
||||||
|
mv.val = rand_val();
|
||||||
|
q.update(qid);
|
||||||
|
CHECK(check());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user