mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-26 18:21:18 -06:00
Add simplification GUI
This commit is contained in:
parent
756d2694eb
commit
af526c54f4
12 changed files with 476 additions and 195 deletions
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/GLGizmoSeam.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp"
|
||||
|
||||
#include "libslic3r/Model.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 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 +171,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_selectable() &&*/ m_gizmos[idx]->is_activable()) {
|
||||
activate_gizmo(m_current == idx ? Undefined : (EType)idx);
|
||||
update_data();
|
||||
return true;
|
||||
|
|
@ -1021,6 +1023,8 @@ void GLGizmosManager::do_render_overlay() const
|
|||
float u_offset = 1.0f / (float)tex_width;
|
||||
float v_offset = 1.0f / (float)tex_height;
|
||||
|
||||
float toolbar_top = 0.f;
|
||||
float current_y = 0.f;
|
||||
for (size_t idx : selectable_idxs)
|
||||
{
|
||||
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 } });
|
||||
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);
|
||||
toolbar_top = cnv_h - wxGetApp().plater()->get_view_toolbar().get_height();
|
||||
current_y = 0.5f * cnv_h - zoomed_top_y * zoom;
|
||||
}
|
||||
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
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ public:
|
|||
FdmSupports,
|
||||
Seam,
|
||||
MmuSegmentation,
|
||||
Simplify,
|
||||
Undefined
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue