From ef5c94f90adec4f77ec56e3dd5f32b564dde8bc7 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Fri, 6 Aug 2021 15:11:20 +0200 Subject: [PATCH 01/23] Fix: prevent degeneration of model during simplification --- src/libslic3r/QuadricEdgeCollapse.cpp | 27 ++++++++++- tests/data/simplification.obj | 46 +++++++++++++++++++ tests/libslic3r/test_indexed_triangle_set.cpp | 12 ++++- 3 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 tests/data/simplification.obj diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp index e42ed5deb..ffe1e5ca7 100644 --- a/src/libslic3r/QuadricEdgeCollapse.cpp +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -77,7 +77,8 @@ namespace QuadricEdgeCollapse { uint32_t ti, const EdgeInfos& e_infos, const Indices& indices); bool is_flipped(const Vec3f &new_vertex, uint32_t ti0, uint32_t ti1, const VertexInfo& v_info, const TriangleInfos &t_infos, const EdgeInfos &e_infos, const indexed_triangle_set &its); - + bool degenerate(uint32_t vi, uint32_t ti0, uint32_t ti1, const VertexInfo &v_info, + const EdgeInfos &e_infos, const Indices &indices); // find edge with smallest error in triangle Vec3d calculate_3errors(const Triangle &t, const Vertices &vertices, const VertexInfos &v_infos); Error calculate_error(uint32_t ti, const Triangle& t,const Vertices &vertices, const VertexInfos& v_infos, unsigned char& min_index); @@ -179,6 +180,8 @@ void Slic3r::its_quadric_edge_collapse( find_triangle_index1(vi1, v_info0, ti0, e_infos, its.indices) : find_triangle_index1(vi0, v_info1, ti0, e_infos, its.indices) ; if (!ti1_opt.has_value() || // edge has only one triangle + degenerate(vi0, ti0, *ti1_opt, v_info1, e_infos, its.indices) || + degenerate(vi1, ti0, *ti1_opt, v_info0, e_infos, its.indices) || is_flipped(new_vertex0, ti0, *ti1_opt, v_info0, t_infos, e_infos, its) || is_flipped(new_vertex0, ti0, *ti1_opt, v_info1, t_infos, e_infos, its)) { // try other triangle's edge @@ -487,6 +490,28 @@ bool QuadricEdgeCollapse::is_flipped(const Vec3f & new_vertex, return false; } +bool QuadricEdgeCollapse::degenerate(uint32_t vi, + uint32_t ti0, + uint32_t ti1, + const VertexInfo &v_info, + const EdgeInfos & e_infos, + const Indices & indices) +{ + // check surround triangle do not contain vertex index + // protect from creation of triangle with two same vertices inside + size_t v_info_end = v_info.start + v_info.count; + for (size_t ei = v_info.start; ei < v_info_end; ++ei) { + assert(ei < e_infos.size()); + const EdgeInfo &e_info = e_infos[ei]; + if (e_info.t_index == ti0) continue; // ti0 will be deleted + if (e_info.t_index == ti1) continue; // ti1 will be deleted + const Triangle &t = indices[e_info.t_index]; + for (size_t i = 0; i < 3; ++i) + if (t[i] == vi) return true; + } + return false; +} + Vec3d QuadricEdgeCollapse::calculate_3errors(const Triangle & t, const Vertices & vertices, const VertexInfos &v_infos) diff --git a/tests/data/simplification.obj b/tests/data/simplification.obj new file mode 100644 index 000000000..f509e695c --- /dev/null +++ b/tests/data/simplification.obj @@ -0,0 +1,46 @@ +v 39.349007 -54.069000 -199.819000 +v 39.489006 -54.029007 -199.815002 +v 39.419006 -53.993011 -199.769012 +v 39.629005 -53.975006 -199.815002 +v 39.639008 -53.947006 -199.805023 +v 39.651001 -53.919006 -199.795013 +v 39.807007 -53.863007 -199.796997 +v 39.729004 -53.891006 -199.796997 +v 39.727005 -53.935013 -199.813019 +v 39.767006 -53.899002 -199.805023 +v 39.871002 -53.835007 -199.801025 +v 39.443001 -53.829010 -199.878998 +v 39.523003 -53.965012 -199.827026 +v 39.807007 -53.863007 -199.796997 +v 39.833008 -53.723007 -199.723022 +v 39.759003 -53.822998 -199.822998 +v 39.867004 -53.845001 -199.805023 +v 39.937004 -53.805008 -199.805023 +f 1 2 3 +f 4 5 2 +f 2 6 3 +f 7 8 4 +f 9 10 4 +f 10 9 11 +f 12 2 1 +f 13 6 4 +f 13 4 2 +f 8 7 9 +f 6 9 4 +f 6 14 15 +f 16 14 6 +f 17 18 9 +f 3 6 15 +f 12 16 6 +f 12 6 13 +f 12 13 2 +f 5 4 8 +f 6 8 9 +f 5 6 2 +f 6 5 8 +f 17 9 7 +f 7 11 17 +f 18 11 9 +f 11 18 17 +f 10 7 4 +f 7 10 11 diff --git a/tests/libslic3r/test_indexed_triangle_set.cpp b/tests/libslic3r/test_indexed_triangle_set.cpp index b640d8410..0e08749bf 100644 --- a/tests/libslic3r/test_indexed_triangle_set.cpp +++ b/tests/libslic3r/test_indexed_triangle_set.cpp @@ -246,4 +246,14 @@ TEST_CASE("Simplify mesh by Quadric edge collapse to 5%", "[its]") CHECK(fabs(original_volume - volume) < 33.); float avg_distance = compare(mesh.its, its, 10); CHECK(avg_distance < 0.022f); // 0.02022 | 0.0199614074 -} \ No newline at end of file +} + +TEST_CASE("Simplify trouble case", "[its]") +{ + TriangleMesh tm = load_model("simplification.obj"); + REQUIRE_FALSE(tm.empty()); + float max_error = std::numeric_limits::max(); + uint32_t wanted_count = 8; + its_quadric_edge_collapse(tm.its, wanted_count, &max_error); + CHECK(tm.its.indices.size() <= 8); +} From 62f8ab1cbe500b20a2a0ca5957d101d27a98be9c Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Mon, 16 Aug 2021 11:53:37 +0200 Subject: [PATCH 02/23] Add check of neighbors Add store triangle for debug purpose --- src/libslic3r/QuadricEdgeCollapse.cpp | 120 +++++++++++++++++++++++++- src/libslic3r/TriangleMesh.cpp | 42 +++++++++ src/libslic3r/TriangleMesh.hpp | 4 + 3 files changed, 164 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp index ffe1e5ca7..52f68ee8d 100644 --- a/src/libslic3r/QuadricEdgeCollapse.cpp +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -91,6 +91,119 @@ namespace QuadricEdgeCollapse { using namespace QuadricEdgeCollapse; +// store triangle surrounding to file +void store_surround(const char * obj_filename, + size_t triangle_index, + int depth, + const indexed_triangle_set &its, + const VertexInfos & v_infos, + const EdgeInfos & e_infos) +{ + std::set triangles; + // triangle index, depth + using Item = std::pair; + std::queue process; + process.push({triangle_index, depth}); + + while (!process.empty()) { + Item item = process.front(); + process.pop(); + size_t ti = item.first; + auto it = triangles.find(ti); + if (it != triangles.end()) continue; + triangles.insert(ti); + if (item.second == 0) continue; + + const Vec3i &t = its.indices[ti]; + for (size_t i = 0; i < 3; ++i) { + const auto &v_info = v_infos[t[i]]; + for (size_t d = 0; d < v_info.count; ++d) { + size_t ei = v_info.start + d; + const auto & e_info = e_infos[ei]; + auto it = triangles.find(e_info.t_index); + if (it != triangles.end()) continue; + process.push({e_info.t_index, item.second - 1}); + } + } + } + + std::vector trs; + trs.reserve(triangles.size()); + for (size_t ti : triangles) trs.push_back(ti); + its_store_triangles(its, obj_filename, trs); + //its_write_obj(its,"original.obj"); +} + +bool check_neighbors(const indexed_triangle_set &its, + const TriangleInfos & t_infos, + const VertexInfos & v_infos, + const EdgeInfos & e_infos) +{ + VertexInfos v_infos2(v_infos.size()); + size_t count_indices = 0; + + for (size_t ti = 0; ti < its.indices.size(); ti++) { + if (t_infos[ti].is_deleted()) continue; + ++count_indices; + const Triangle &t = its.indices[ti]; + for (size_t e = 0; e < 3; e++) { + VertexInfo &v_info = v_infos2[t[e]]; + ++v_info.count; // triangle count + } + } + + uint32_t triangle_start = 0; + for (VertexInfo &v_info : v_infos2) { + v_info.start = triangle_start; + triangle_start += v_info.count; + // set filled vertex to zero + v_info.count = 0; + } + + // create reference + EdgeInfos e_infos2(count_indices * 3); + for (size_t ti = 0; ti < its.indices.size(); ti++) { + if (t_infos[ti].is_deleted()) continue; + const Triangle &t = its.indices[ti]; + for (size_t j = 0; j < 3; ++j) { + VertexInfo &v_info = v_infos2[t[j]]; + size_t ei = v_info.start + v_info.count; + assert(ei < e_infos2.size()); + EdgeInfo &e_info = e_infos2[ei]; + e_info.t_index = ti; + e_info.edge = j; + ++v_info.count; + } + } + + for (size_t vi = 0; vi < its.vertices.size(); vi++) { + const VertexInfo &v_info = v_infos[vi]; + if (v_info.is_deleted()) continue; + const VertexInfo &v_info2 = v_infos2[vi]; + if (v_info.count != v_info2.count) { + return false; + } + EdgeInfos eis; + eis.reserve(v_info.count); + std::copy(e_infos.begin() + v_info.start, + e_infos.begin() + v_info.start + v_info.count, + std::back_inserter(eis)); + auto compare = [](const EdgeInfo &ei1, const EdgeInfo &ei2) { + return ei1.t_index < ei2.t_index; + }; + std::sort(eis.begin(), eis.end(), compare); + std::sort(e_infos2.begin() + v_info2.start, + e_infos2.begin() + v_info2.start + v_info2.count, + compare); + for (size_t ei = 0; ei < v_info.count; ++ei) { + if (eis[ei].t_index != e_infos2[ei + v_info2.start].t_index) { + return false; + } + } + } + return true; +} + void Slic3r::its_quadric_edge_collapse( indexed_triangle_set & its, uint32_t triangle_count, @@ -117,6 +230,9 @@ void Slic3r::its_quadric_edge_collapse( throw_on_cancel(); statusfn(status_init_size); + //its_store_triangle(its, "triangle.obj", 1182); + //store_surround("triangle_surround1.obj", 1182, 1, its, v_infos, e_infos); + // convert from triangle index to mutable priority queue index std::vector ti_2_mpqi(its.indices.size(), {0}); auto setter = [&ti_2_mpqi](const Error &e, size_t index) { ti_2_mpqi[e.triangle_index] = index; }; @@ -237,8 +353,7 @@ void Slic3r::its_quadric_edge_collapse( } v_info0.q = q; - // fix neighbors - + // fix neighbors // vertex index of triangle 0 which is not vi0 nor vi1 uint32_t vi_top0 = t0[(t_info0.min_index + 2) % 3]; const Triangle &t1 = its.indices[ti1]; @@ -264,6 +379,7 @@ void Slic3r::its_quadric_edge_collapse( t_info1.set_deleted(); // triangle counter decrementation actual_triangle_count-=2; + assert(check_neighbors(its, t_infos, v_infos, e_infos)); } // compact triangle diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 360a8b14e..d4baabc97 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -957,6 +957,48 @@ int its_compactify_vertices(indexed_triangle_set &its, bool shrink_to_fit) return removed; } +bool its_store_triangle(const indexed_triangle_set &its, + const char * obj_filename, + size_t triangle_index) +{ + if (its.indices.size() <= triangle_index) return false; + Vec3i t = its.indices[triangle_index]; + indexed_triangle_set its2; + its2.indices = {{0, 1, 2}}; + its2.vertices = {its.vertices[t[0]], its.vertices[t[1]], + its.vertices[t[2]]}; + return its_write_obj(its2, obj_filename); +} + +bool its_store_triangles(const indexed_triangle_set &its, + const char * obj_filename, + const std::vector & triangles) +{ + indexed_triangle_set its2; + its2.vertices.reserve(triangles.size() * 3); + its2.indices.reserve(triangles.size()); + std::map vertex_map; + for (auto ti : triangles) { + if (its.indices.size() <= ti) return false; + Vec3i t = its.indices[ti]; + Vec3i new_t; + for (size_t i = 0; i < 3; ++i) { + size_t vi = t[i]; + auto it = vertex_map.find(vi); + if (it != vertex_map.end()) { + new_t[i] = it->second; + continue; + } + size_t new_vi = its2.vertices.size(); + its2.vertices.push_back(its.vertices[vi]); + vertex_map[vi] = new_vi; + new_t[i] = new_vi; + } + its2.indices.push_back(new_t); + } + return its_write_obj(its2, obj_filename); +} + void its_shrink_to_fit(indexed_triangle_set &its) { its.indices.shrink_to_fit(); diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index c8c7e0dd7..b7a1bebb1 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -140,6 +140,10 @@ int its_remove_degenerate_faces(indexed_triangle_set &its, bool shrink_to_fit = // Remove vertices, which none of the faces references. Return number of freed vertices. int its_compactify_vertices(indexed_triangle_set &its, bool shrink_to_fit = true); +// store part of index triangle set +bool its_store_triangle(const indexed_triangle_set &its, const char *obj_filename, size_t triangle_index); +bool its_store_triangles(const indexed_triangle_set &its, const char *obj_filename, const std::vector& triangles); + std::vector its_split(const indexed_triangle_set &its); bool its_is_splittable(const indexed_triangle_set &its); From 21fd35d243ebb91e3c2a632b5d14d8e5cd521a36 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Mon, 16 Aug 2021 13:04:39 +0200 Subject: [PATCH 03/23] Fix: Do not close dialog after preview --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 694eeadd4..cdcf9ce1f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -194,19 +194,15 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi 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); - + m_parent.reload_scene(true); 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(); + plater->changed_mesh(obj_index); + close(); } - // change from simplifying | aply + // change from simplifying | apply state = State::settings; // Fix warning icon in object list From 268b06bdbb7eaa433c663413d9c9387c7fb15d67 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Mon, 16 Aug 2021 15:30:33 +0200 Subject: [PATCH 04/23] fix position of window --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 47 +++++++++++++++++------ src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 2 + 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index cdcf9ce1f..fa0b94200 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -53,10 +53,6 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi 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]; @@ -65,6 +61,12 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi // Check selection of new volume // Do not reselect object when processing if (act_volume != volume && state == State::settings) { + bool change_window_position = (volume == nullptr); + // select different model + if (volume != nullptr && original_its.has_value()) { + set_its(*original_its); + } + obj_index = object_idx; // to remember correct object volume = act_volume; original_its = {}; @@ -72,13 +74,30 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi 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); + + if (change_window_position) { + ImVec2 pos = ImGui::GetMousePos(); + pos.x -= gui_cfg->window_offset; + pos.y -= gui_cfg->window_offset; + // minimal top left value + ImVec2 tl(gui_cfg->window_padding, gui_cfg->window_padding); + if (pos.x < tl.x) pos.x = tl.x; + if (pos.y < tl.y) pos.y = tl.y; + // maximal bottom right value + auto parent_size = m_parent.get_canvas_size(); + ImVec2 br( + parent_size.get_width() - (2 * gui_cfg->window_offset + gui_cfg->window_padding), + parent_size.get_height() - (2 * gui_cfg->window_offset + gui_cfg->window_padding)); + if (pos.x > br.x) pos.x = br.x; + if (pos.y > br.y) pos.y = br.y; + ImGui::SetNextWindowPos(pos, ImGuiCond_Always); + } } + int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoCollapse; + m_imgui->begin(on_get_name(), flag); + size_t triangle_count = volume->mesh().its.indices.size(); // already reduced mesh if (original_its.has_value()) @@ -211,8 +230,6 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi } 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); @@ -293,6 +310,14 @@ bool GLGizmoSimplify::on_is_activable() const return !m_parent.get_selection().is_empty(); } +void GLGizmoSimplify::on_set_state() +{ + // Closing gizmo. e.g. selecting another one + if (m_state == GLGizmoBase::Off) { + volume = nullptr; + } +} + void GLGizmoSimplify::create_gui_cfg() { if (gui_cfg.has_value()) return; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index bd3637360..cefd1abf0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -41,6 +41,7 @@ protected: 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; } + virtual void on_set_state() override; private: void close(); @@ -78,6 +79,7 @@ private: int input_width = 100; int input_small_width = 80; int window_offset = 100; + int window_padding = 0; }; std::optional gui_cfg; void create_gui_cfg(); From c04856e049221312a87761df0e194488ba95b5c6 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Mon, 16 Aug 2021 18:04:38 +0200 Subject: [PATCH 05/23] Extend simplify test to chack distance of each triangle center and each vertices. Both simplify to origin and vice versa --- tests/libslic3r/test_indexed_triangle_set.cpp | 71 +++++++++++++------ 1 file changed, 51 insertions(+), 20 deletions(-) diff --git a/tests/libslic3r/test_indexed_triangle_set.cpp b/tests/libslic3r/test_indexed_triangle_set.cpp index 0e08749bf..a3996e651 100644 --- a/tests/libslic3r/test_indexed_triangle_set.cpp +++ b/tests/libslic3r/test_indexed_triangle_set.cpp @@ -165,29 +165,51 @@ std::vector its_sample_surface(const indexed_triangle_set &its, #include "libslic3r/AABBTreeIndirect.hpp" -// return Average abs distance to original -float compare(const indexed_triangle_set &original, - const indexed_triangle_set &simplified, - double sample_per_mm2) +struct CompareConfig +{ + float max_distance = 3.f; + float max_average_distance = 2.f; +}; + +bool is_similar(const indexed_triangle_set &from, + const indexed_triangle_set &to, + const CompareConfig &cfg) { // create ABBTree auto tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set( - original.vertices, original.indices); + from.vertices, from.indices); + float sum_distance = 0.f; + float max_distance = 0.f; - unsigned int init = 0; - std::mt19937 rnd(init); - auto samples = its_sample_surface(simplified, sample_per_mm2, rnd); - - float sumDistance = 0; - for (const Vec3f &sample : samples) { + auto collect_distances = [&](const Vec3f &surface_point) { size_t hit_idx; Vec3f hit_point; - float distance2 = AABBTreeIndirect::squared_distance_to_indexed_triangle_set( - original.vertices, original.indices, tree, sample, hit_idx, - hit_point); - sumDistance += sqrt(distance2); + float distance2 = + AABBTreeIndirect::squared_distance_to_indexed_triangle_set( + from.vertices, from.indices, tree, surface_point, hit_idx, hit_point); + float distance = sqrt(distance2); + if (max_distance < distance) max_distance = distance; + sum_distance += distance; + }; + + for (const Vec3f &vertex : to.vertices) { + collect_distances(vertex); } - return sumDistance / samples.size(); + + for (const Vec3i &t : to.indices) { + Vec3f center(0,0,0); + for (size_t i = 0; i < 3; ++i) { + center += to.vertices[t[i]] / 3; + } + collect_distances(center); + } + + size_t count = to.vertices.size() + to.indices.size(); + float avg_distance = sum_distance / count; + if (avg_distance > cfg.max_average_distance || + max_distance > cfg.max_distance) + return false; + return true; } TEST_CASE("Reduce one edge by Quadric Edge Collapse", "[its]") @@ -226,8 +248,12 @@ TEST_CASE("Reduce one edge by Quadric Edge Collapse", "[its]") (v[i] > v4[i] && v[i] < v2[i]); CHECK(is_between); } - float avg_distance = compare(its_, its, 10); - CHECK(avg_distance < 8e-3f); + CompareConfig cfg; + cfg.max_average_distance = 0.014f; + cfg.max_distance = 0.75f; + + CHECK(is_similar(its, its_, cfg)); + CHECK(is_similar(its_, its, cfg)); } #include "test_utils.hpp" @@ -244,8 +270,13 @@ TEST_CASE("Simplify mesh by Quadric edge collapse to 5%", "[its]") CHECK(its.indices.size() <= wanted_count); double volume = its_volume(its); CHECK(fabs(original_volume - volume) < 33.); - float avg_distance = compare(mesh.its, its, 10); - CHECK(avg_distance < 0.022f); // 0.02022 | 0.0199614074 + + CompareConfig cfg; + cfg.max_average_distance = 0.043f; + cfg.max_distance = 0.32f; + + CHECK(is_similar(mesh.its, its, cfg)); + CHECK(is_similar(its, mesh.its, cfg)); } TEST_CASE("Simplify trouble case", "[its]") From 090728b9d5c1279650bc585f9be7c1b5fb4608fd Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Tue, 17 Aug 2021 08:58:45 +0200 Subject: [PATCH 06/23] Add private member prefix m_ --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 230 +++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 48 ++--- 2 files changed, 142 insertions(+), 136 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index fa0b94200..23ab4ef13 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -18,17 +18,17 @@ 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) + , m_state(State::settings) + , m_is_valid_result(false) + , m_progress(0) + , m_volume(nullptr) + , m_obj_index(0) + , m_need_reload(false) {} GLGizmoSimplify::~GLGizmoSimplify() { - state = State::canceling; - if (worker.joinable()) worker.join(); + m_state = State::canceling; + if (m_worker.joinable()) m_worker.join(); } bool GLGizmoSimplify::on_init() @@ -60,34 +60,34 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi // Check selection of new volume // Do not reselect object when processing - if (act_volume != volume && state == State::settings) { - bool change_window_position = (volume == nullptr); + if (act_volume != m_volume && m_state == State::settings) { + bool change_window_position = (m_volume == nullptr); // select different model - if (volume != nullptr && original_its.has_value()) { - set_its(*original_its); + if (m_volume != nullptr && m_original_its.has_value()) { + set_its(*m_original_its); } - 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; + m_obj_index = object_idx; // to remember correct object + m_volume = act_volume; + m_original_its = {}; + const TriangleMesh &tm = m_volume->mesh(); + m_configuration.wanted_percent = 50.; // default value + m_configuration.update_percent(tm.its.indices.size()); + m_is_valid_result = false; if (change_window_position) { ImVec2 pos = ImGui::GetMousePos(); - pos.x -= gui_cfg->window_offset; - pos.y -= gui_cfg->window_offset; + pos.x -= m_gui_cfg->window_offset; + pos.y -= m_gui_cfg->window_offset; // minimal top left value - ImVec2 tl(gui_cfg->window_padding, gui_cfg->window_padding); + ImVec2 tl(m_gui_cfg->window_padding, m_gui_cfg->window_padding); if (pos.x < tl.x) pos.x = tl.x; if (pos.y < tl.y) pos.y = tl.y; // maximal bottom right value auto parent_size = m_parent.get_canvas_size(); ImVec2 br( - parent_size.get_width() - (2 * gui_cfg->window_offset + gui_cfg->window_padding), - parent_size.get_height() - (2 * gui_cfg->window_offset + gui_cfg->window_padding)); + parent_size.get_width() - (2 * m_gui_cfg->window_offset + m_gui_cfg->window_padding), + parent_size.get_height() - (2 * m_gui_cfg->window_offset + m_gui_cfg->window_padding)); if (pos.x > br.x) pos.x = br.x; if (pos.y > br.y) pos.y = br.y; ImGui::SetNextWindowPos(pos, ImGuiCond_Always); @@ -98,98 +98,100 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi ImGuiWindowFlags_NoCollapse; m_imgui->begin(on_get_name(), flag); - size_t triangle_count = volume->mesh().its.indices.size(); + size_t triangle_count = m_volume->mesh().its.indices.size(); // already reduced mesh - if (original_its.has_value()) - triangle_count = original_its->indices.size(); + if (m_original_its.has_value()) + triangle_count = m_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; + ImGui::SameLine(m_gui_cfg->top_left_width); + std::string name = m_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); + ImGui::SameLine(m_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); + ImGui::SameLine(m_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; + if (m_imgui->checkbox("##UseCount", m_configuration.use_count)) { + if (!m_configuration.use_count) m_configuration.use_error = true; + m_is_valid_result = false; } - m_imgui->disabled_begin(!c.use_count); + m_imgui->disabled_begin(!m_configuration.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); + ImGui::SameLine(m_gui_cfg->bottom_left_width); + int wanted_count = m_configuration.wanted_count; + ImGui::SetNextItemWidth(m_gui_cfg->input_width); if (ImGui::SliderInt("##triangle_count", &wanted_count, min_triangle_count, triangle_count, "%d")) { - c.wanted_count = static_cast(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; + m_configuration.wanted_count = static_cast(wanted_count); + if (m_configuration.wanted_count < min_triangle_count) + m_configuration.wanted_count = min_triangle_count; + if (m_configuration.wanted_count > triangle_count) + m_configuration.wanted_count = triangle_count; + m_configuration.update_count(triangle_count); + m_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); + ImGui::SameLine(m_gui_cfg->bottom_left_width); + ImGui::SetNextItemWidth(m_gui_cfg->input_small_width); + const char * precision = (m_configuration.wanted_percent > 10)? "%.0f": + ((m_configuration.wanted_percent > 1)? "%.1f":"%.2f"); + float step = (m_configuration.wanted_percent > 10)? 1.f: + ((m_configuration.wanted_percent > 1)? 0.1f : 0.01f); + if (ImGui::InputFloat("%", &m_configuration.wanted_percent, step, 10*step, precision)) { + if (m_configuration.wanted_percent > 100.f) m_configuration.wanted_percent = 100.f; + m_configuration.update_percent(triangle_count); + if (m_configuration.wanted_count < min_triangle_count) { + m_configuration.wanted_count = min_triangle_count; + m_configuration.update_count(triangle_count); } - is_valid_result = false; + m_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; + ImGui::SameLine(m_gui_cfg->bottom_left_width); + if (m_imgui->checkbox("##UseError", m_configuration.use_error)) { + if (!m_configuration.use_error) m_configuration.use_count = true; + m_is_valid_result = false; } - m_imgui->disabled_begin(!c.use_error); + m_imgui->disabled_begin(!m_configuration.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; + ImGui::SameLine(m_gui_cfg->bottom_left_width); + ImGui::SetNextItemWidth(m_gui_cfg->input_small_width); + if (ImGui::InputFloat("##maxError", &m_configuration.max_error, 0.01f, .1f, "%.2f")) { + if (m_configuration.max_error < 0.f) m_configuration.max_error = 0.f; + m_is_valid_result = false; } m_imgui->disabled_end(); // use_error - if (state == State::settings) { + if (m_state == State::settings) { if (m_imgui->button(_L("Cancel"))) { - if (original_its.has_value()) { - set_its(*original_its); - state = State::close_on_end; + if (m_original_its.has_value()) { + set_its(*m_original_its); + m_state = State::close_on_end; } else { close(); } } - ImGui::SameLine(gui_cfg->bottom_left_width); + ImGui::SameLine(m_gui_cfg->bottom_left_width); if (m_imgui->button(_L("Preview"))) { - state = State::simplifying; + m_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; + if (!m_is_valid_result) { + m_state = State::close_on_end; process(); } else { // use preview and close @@ -197,35 +199,35 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi } } } else { - m_imgui->disabled_begin(state == State::canceling); - if (m_imgui->button(_L("Cancel"))) state = State::canceling; + m_imgui->disabled_begin(m_state == State::canceling); + if (m_imgui->button(_L("Cancel"))) m_state = State::canceling; m_imgui->disabled_end(); - ImGui::SameLine(gui_cfg->bottom_left_width); + ImGui::SameLine(m_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); + sprintf(buf, L("Process %d / 100"), m_progress); + ImGui::ProgressBar(m_progress / 100., ImVec2(m_gui_cfg->input_width, 0.f), buf); } m_imgui->end(); - if (need_reload) { - need_reload = false; + if (m_need_reload) { + m_need_reload = false; // Reload visualization of mesh - change VBO, FBO on GPU m_parent.reload_scene(true); - if (state == State::close_on_end) { + if (m_state == State::close_on_end) { // fix hollowing, sla support points, modifiers, ... auto plater = wxGetApp().plater(); - plater->changed_mesh(obj_index); + plater->changed_mesh(m_obj_index); close(); } // change from simplifying | apply - state = State::settings; + m_state = State::settings; // Fix warning icon in object list - wxGetApp().obj_list()->update_item_error_icon(obj_index, -1); + wxGetApp().obj_list()->update_item_error_icon(m_obj_index, -1); } } @@ -243,51 +245,53 @@ void GLGizmoSimplify::process() const char* what() const throw() { return L("Model simplification has been canceled"); } }; - if (!original_its.has_value()) - original_its = volume->mesh().its; // copy + if (!m_original_its.has_value()) + m_original_its = m_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([&]() { + plater->take_snapshot(_L("Simplify ") + m_volume->name); + plater->clear_before_change_mesh(m_obj_index); + m_progress = 0; + if (m_worker.joinable()) m_worker.join(); + m_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::max(); + uint32_t triangle_count = (m_configuration.use_count) ? m_configuration.wanted_count : 0; + float max_error = (m_configuration.use_error) ? + m_configuration.max_error : std::numeric_limits::max(); std::function throw_on_cancel = [&]() { - if (state == State::canceling) { + if (m_state == State::canceling) { throw SimplifyCanceledException(); } }; std::function statusfn = [&](int percent) { - progress = percent; + m_progress = percent; m_parent.schedule_extra_frame(0); }; indexed_triangle_set collapsed; - if (last_error.has_value()) { + if (m_last_error.has_value()) { // is chance to continue with last reduction - const indexed_triangle_set &its = volume->mesh().its; + const indexed_triangle_set &its = m_volume->mesh().its; uint32_t last_triangle_count = static_cast(its.indices.size()); - if ((!c.use_count || triangle_count <= last_triangle_count) && - (!c.use_error || c.max_error <= *last_error)) { + if ((!m_configuration.use_count || triangle_count <= last_triangle_count) && + (!m_configuration.use_error || m_configuration.max_error <= *m_last_error)) { collapsed = its; // small copy } else { - collapsed = *original_its; // copy + collapsed = *m_original_its; // copy } } else { - collapsed = *original_its; // copy + collapsed = *m_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; + m_is_valid_result = true; + m_last_error = max_error; } catch (SimplifyCanceledException &) { - state = State::settings; + // set state out of main thread + m_state = State::settings; } // need to render last status fn // without sleep it freezes until mouse move @@ -299,10 +303,10 @@ void GLGizmoSimplify::process() void GLGizmoSimplify::set_its(indexed_triangle_set &its) { auto tm = std::make_unique(its); tm->repair(); - volume->set_mesh(std::move(tm)); - volume->set_new_unique_id(); - volume->get_object()->invalidate_bounding_box(); - need_reload = true; + m_volume->set_mesh(std::move(tm)); + m_volume->set_new_unique_id(); + m_volume->get_object()->invalidate_bounding_box(); + m_need_reload = true; } bool GLGizmoSimplify::on_is_activable() const @@ -313,13 +317,13 @@ bool GLGizmoSimplify::on_is_activable() const void GLGizmoSimplify::on_set_state() { // Closing gizmo. e.g. selecting another one - if (m_state == GLGizmoBase::Off) { - volume = nullptr; + if (GLGizmoBase::m_state == GLGizmoBase::Off) { + m_volume = nullptr; } } void GLGizmoSimplify::create_gui_cfg() { - if (gui_cfg.has_value()) return; + if (m_gui_cfg.has_value()) return; int space_size = m_imgui->calc_text_size(":MM").x; GuiCfg cfg; @@ -337,7 +341,7 @@ void GLGizmoSimplify::create_gui_cfg() { 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; + m_gui_cfg = cfg; } } // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index cefd1abf0..57174b7c1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -10,26 +10,7 @@ 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 original_its; - - std::optional 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(); @@ -47,6 +28,28 @@ private: void close(); void process(); void set_its(indexed_triangle_set &its); + void create_gui_cfg(); + + bool m_is_valid_result; // differ what to do in apply + volatile int m_progress; // percent of done work + ModelVolume *m_volume; // + size_t m_obj_index; + + std::optional m_original_its; + std::optional m_last_error; // for use previous reduction + + volatile bool m_need_reload; // after simplify, glReload must be on main thread + std::thread m_worker; + + enum class State { + settings, + simplifying, // start processing + canceling, // canceled + successfull, // successful simplified + close_on_end + }; + volatile State m_state; + struct Configuration { bool use_count = true; @@ -67,7 +70,7 @@ private: wanted_count = static_cast( std::round(triangle_count * wanted_percent / 100.f)); } - } c; + } m_configuration; // This configs holds GUI layout size given by translated texts. // etc. When language changes, GUI is recreated and this class constructed again, @@ -81,8 +84,7 @@ private: int window_offset = 100; int window_padding = 0; }; - std::optional gui_cfg; - void create_gui_cfg(); + std::optional m_gui_cfg; }; } // namespace GUI From 11c91d781e4c56c5047a692f743d5f65a938c561 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Tue, 17 Aug 2021 15:28:08 +0200 Subject: [PATCH 07/23] FIX: extra frame request Do not freeze bargraph in Siplify dialog when no mouse move. --- src/slic3r/GUI/GLCanvas3D.cpp | 19 ++++++----------- src/slic3r/GUI/GLCanvas3D.hpp | 4 +--- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 25 +++++++++-------------- src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 9 ++++---- 4 files changed, 22 insertions(+), 35 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 529056e99..96cf015ea 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2780,11 +2780,10 @@ void GLCanvas3D::on_timer(wxTimerEvent& evt) void GLCanvas3D::on_render_timer(wxTimerEvent& evt) { - // no need to do anything here - // right after this event is recieved, idle event is fired - - //m_dirty = true; - //wxWakeUpIdle(); + m_dirty = true; + // wxWakeUpIdle(); + // no need to wake up idle + // right after this event, idle event is fired } @@ -2802,21 +2801,15 @@ void GLCanvas3D::schedule_extra_frame(int miliseconds) return; } } - // Start timer - int64_t now = timestamp_now(); + int remaining_time = m_render_timer.GetInterval(); // Timer is not running - if (! m_render_timer.IsRunning()) { - m_extra_frame_requested_delayed = miliseconds; + if (!m_render_timer.IsRunning()) { m_render_timer.StartOnce(miliseconds); - m_render_timer_start = now; // Timer is running - restart only if new period is shorter than remaning period } else { - const int64_t remaining_time = (m_render_timer_start + m_extra_frame_requested_delayed) - now; if (miliseconds + 20 < remaining_time) { m_render_timer.Stop(); - m_extra_frame_requested_delayed = miliseconds; m_render_timer.StartOnce(miliseconds); - m_render_timer_start = now; } } } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index d9cd55e35..ba8430c07 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -463,15 +463,13 @@ private: std::string m_sidebar_field; // when true renders an extra frame by not resetting m_dirty to false // see request_extra_frame() - bool m_extra_frame_requested; - int m_extra_frame_requested_delayed { std::numeric_limits::max() }; + bool m_extra_frame_requested; bool m_event_handlers_bound{ false }; GLVolumeCollection m_volumes; GCodeViewer m_gcode_viewer; RenderTimer m_render_timer; - int64_t m_render_timer_start; Selection m_selection; const DynamicPrintConfig* m_config; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 23ab4ef13..06a999cad 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -184,7 +184,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi } ImGui::SameLine(m_gui_cfg->bottom_left_width); if (m_imgui->button(_L("Preview"))) { - m_state = State::simplifying; + m_state = State::preview; // simplify but not aply on mesh process(); } @@ -263,23 +263,18 @@ void GLGizmoSimplify::process() if (m_state == State::canceling) { throw SimplifyCanceledException(); } - }; + }; std::function statusfn = [&](int percent) { m_progress = percent; m_parent.schedule_extra_frame(0); }; indexed_triangle_set collapsed; - if (m_last_error.has_value()) { - // is chance to continue with last reduction - const indexed_triangle_set &its = m_volume->mesh().its; - uint32_t last_triangle_count = static_cast(its.indices.size()); - if ((!m_configuration.use_count || triangle_count <= last_triangle_count) && - (!m_configuration.use_error || m_configuration.max_error <= *m_last_error)) { - collapsed = its; // small copy - } else { - collapsed = *m_original_its; // copy - } + if (m_last_error.has_value() && m_last_count.has_value() && + (!m_configuration.use_count || triangle_count <= *m_last_count) && + (!m_configuration.use_error || m_configuration.max_error <= *m_last_error)) { + // continue from last reduction - speed up + collapsed = m_volume->mesh().its; // small copy } else { collapsed = *m_original_its; // copy } @@ -288,14 +283,14 @@ void GLGizmoSimplify::process() its_quadric_edge_collapse(collapsed, triangle_count, &max_error, throw_on_cancel, statusfn); set_its(collapsed); m_is_valid_result = true; + m_last_count = triangle_count; // need to store last requirement, collapsed count could be count-1 m_last_error = max_error; } catch (SimplifyCanceledException &) { // set state out of main thread + m_last_error = {}; m_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)); + // need to render last status fn to change bar graph to buttons m_parent.schedule_extra_frame(0); }); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index 57174b7c1..5a84cabad 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -36,17 +36,18 @@ private: size_t m_obj_index; std::optional m_original_its; + std::optional m_last_error; // for use previous reduction + std::optional m_last_count; volatile bool m_need_reload; // after simplify, glReload must be on main thread std::thread m_worker; enum class State { settings, - simplifying, // start processing - canceling, // canceled - successfull, // successful simplified - close_on_end + preview, // simplify to show preview + close_on_end, // simplify with close on end + canceling // after button click, before canceled }; volatile State m_state; From 1e863cc03181aeff0989c3c9974d0850a0ad7600 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Wed, 18 Aug 2021 10:37:08 +0200 Subject: [PATCH 08/23] Add restriction for simplification Refuse outgoing during simlification. Refuse start simplification when other Gizmo is active Fix close after preview to revert changes Allow change model for simplification --- src/slic3r/GUI/GUI_ObjectList.cpp | 18 ++++++++- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 47 ++++++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 8 +++- src/slic3r/GUI/Plater.cpp | 6 +++ 4 files changed, 63 insertions(+), 16 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 07119b8de..decd55dd2 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -3961,7 +3961,23 @@ void ObjectList::fix_through_netfabb() void ObjectList::simplify() { - GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager(); + auto plater = wxGetApp().plater(); + GLGizmosManager& gizmos_mgr = plater->canvas3D()->get_gizmos_manager(); + + // Do not simplify when a gizmo is open. There might be issues with updates + // and what is worse, the snapshot time would refer to the internal stack. + auto current_type = gizmos_mgr.get_current_type(); + if (current_type == GLGizmosManager::Simplify) { + // close first + gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify); + }else if (current_type != GLGizmosManager::Undefined) { + plater->get_notification_manager()->push_notification( + NotificationType::CustomSupportsAndSeamRemovedAfterRepair, + NotificationManager::NotificationLevel::RegularNotification, + _u8L("ERROR: Please close all manipulators available from " + "the left toolbar before start simplify the mesh.")); + return; + } gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 06a999cad..a921a121e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -1,17 +1,14 @@ -// 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 "slic3r/GUI/NotificationManager.hpp" +#include "slic3r/GUI/Plater.hpp" #include "libslic3r/AppConfig.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/QuadricEdgeCollapse.hpp" -#include -#include - namespace Slic3r::GUI { GLGizmoSimplify::GLGizmoSimplify(GLCanvas3D & parent, @@ -38,7 +35,6 @@ bool GLGizmoSimplify::on_init() return true; } - std::string GLGizmoSimplify::on_get_name() const { return (_L("Simplify")).ToUTF8().data(); @@ -195,6 +191,11 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi process(); } else { // use preview and close + if (m_original_its.has_value()) { + // fix hollowing, sla support points, modifiers, ... + auto plater = wxGetApp().plater(); + plater->changed_mesh(m_obj_index); + } close(); } } @@ -213,18 +214,17 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi if (m_need_reload) { m_need_reload = false; - + bool close_on_end = (m_state == State::close_on_end); // Reload visualization of mesh - change VBO, FBO on GPU m_parent.reload_scene(true); - if (m_state == State::close_on_end) { + // set m_state must be before close() !!! + m_state = State::settings; + if (close_on_end) { // fix hollowing, sla support points, modifiers, ... auto plater = wxGetApp().plater(); plater->changed_mesh(m_obj_index); close(); } - - // change from simplifying | apply - m_state = State::settings; // Fix warning icon in object list wxGetApp().obj_list()->update_item_error_icon(m_obj_index, -1); @@ -287,7 +287,6 @@ void GLGizmoSimplify::process() m_last_error = max_error; } catch (SimplifyCanceledException &) { // set state out of main thread - m_last_error = {}; m_state = State::settings; } // need to render last status fn to change bar graph to buttons @@ -310,10 +309,30 @@ bool GLGizmoSimplify::on_is_activable() const } void GLGizmoSimplify::on_set_state() -{ +{ // Closing gizmo. e.g. selecting another one if (GLGizmoBase::m_state == GLGizmoBase::Off) { - m_volume = nullptr; + + // refuse outgoing during simlification + if (m_state != State::settings) { + GLGizmoBase::m_state = GLGizmoBase::On; + auto notification_manager = wxGetApp().plater()->get_notification_manager(); + notification_manager->push_notification( + NotificationType::CustomNotification, + NotificationManager::NotificationLevel::RegularNotification, + _u8L("ERROR: Wait until Simplification ends or Cancel process.")); + return; + } + + // revert preview + if (m_original_its.has_value()) { + set_its(*m_original_its); + m_parent.reload_scene(true); + m_need_reload = false; + } + + // invalidate selected model + m_volume = nullptr; } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index 5a84cabad..4539fef01 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -1,14 +1,20 @@ #ifndef slic3r_GLGizmoSimplify_hpp_ #define slic3r_GLGizmoSimplify_hpp_ +// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, +// which overrides our localization "L" macro. #include "GLGizmoBase.hpp" -#include "libslic3r/Model.hpp" +#include "admesh/stl.h" // indexed_triangle_set #include #include namespace Slic3r { + +class ModelVolume; + namespace GUI { + class GLGizmoSimplify : public GLGizmoBase { public: diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 22a1b36ae..e037a0ac8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4283,6 +4283,12 @@ bool Plater::priv::can_fix_through_netfabb() const bool Plater::priv::can_simplify() const { + // is object for simplification selected + if (get_selected_object_idx() < 0) return false; + // is already opened? + if (q->canvas3D()->get_gizmos_manager().get_current_type() == + GLGizmosManager::EType::Simplify) + return false; return true; } From 27fcf55eaa61dd9b7fa793801099ce110e7f08f0 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Wed, 18 Aug 2021 12:07:46 +0200 Subject: [PATCH 09/23] Add cancel and statusFn into init phase of simplification Move debug functions into NDEBUG macro --- src/libslic3r/QuadricEdgeCollapse.cpp | 293 +++++++++++++++----------- 1 file changed, 171 insertions(+), 122 deletions(-) diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp index b5568a171..c4e874c65 100644 --- a/src/libslic3r/QuadricEdgeCollapse.cpp +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -7,13 +7,14 @@ using namespace Slic3r; -// only private namespace not neccessary be in hpp +// only private namespace not neccessary be in .hpp namespace QuadricEdgeCollapse { using Vertices = std::vector; using Triangle = stl_triangle_vertex_indices; using Indices = std::vector; using SymMat = SimplifyMesh::implementation::SymetricMatrix; - + using ThrowOnCancel = std::function; + using StatusFn = std::function; // smallest error caused by edges, identify smallest edge in triangle struct Error { @@ -74,7 +75,8 @@ namespace QuadricEdgeCollapse { // calculate error for vertex and quadrics, triangle quadrics and triangle vertex give zero, only pozitive number double vertex_error(const SymMat &q, const Vec3d &vertex); SymMat create_quadric(const Triangle &t, const Vec3d& n, const Vertices &vertices); - std::tuple init(const indexed_triangle_set &its); + std::tuple + init(const indexed_triangle_set &its, ThrowOnCancel& throw_on_cancel, StatusFn& status_fn); std::optional find_triangle_index1(uint32_t vi, const VertexInfo& v_info, uint32_t ti, const EdgeInfos& e_infos, const Indices& indices); bool is_flipped(const Vec3f &new_vertex, uint32_t ti0, uint32_t ti1, const VertexInfo& v_info, @@ -89,129 +91,24 @@ namespace QuadricEdgeCollapse { uint32_t vi0, uint32_t vi1, uint32_t vi_top0, const Triangle &t1, CopyEdgeInfos& infos, EdgeInfos &e_infos1); void compact(const VertexInfos &v_infos, const TriangleInfos &t_infos, const EdgeInfos &e_infos, indexed_triangle_set &its); + +#ifndef NDEBUG + void store_surround(const char *obj_filename, size_t triangle_index, int depth, const indexed_triangle_set &its, + const VertexInfos &v_infos, const EdgeInfos &e_infos); + bool check_neighbors(const indexed_triangle_set &its, const TriangleInfos &t_infos, + const VertexInfos &v_infos, const EdgeInfos &e_infos); +#endif /* NDEBUG */ + } // namespace QuadricEdgeCollapse using namespace QuadricEdgeCollapse; -// store triangle surrounding to file -void store_surround(const char * obj_filename, - size_t triangle_index, - int depth, - const indexed_triangle_set &its, - const VertexInfos & v_infos, - const EdgeInfos & e_infos) -{ - std::set triangles; - // triangle index, depth - using Item = std::pair; - std::queue process; - process.push({triangle_index, depth}); - - while (!process.empty()) { - Item item = process.front(); - process.pop(); - size_t ti = item.first; - auto it = triangles.find(ti); - if (it != triangles.end()) continue; - triangles.insert(ti); - if (item.second == 0) continue; - - const Vec3i &t = its.indices[ti]; - for (size_t i = 0; i < 3; ++i) { - const auto &v_info = v_infos[t[i]]; - for (size_t d = 0; d < v_info.count; ++d) { - size_t ei = v_info.start + d; - const auto & e_info = e_infos[ei]; - auto it = triangles.find(e_info.t_index); - if (it != triangles.end()) continue; - process.push({e_info.t_index, item.second - 1}); - } - } - } - - std::vector trs; - trs.reserve(triangles.size()); - for (size_t ti : triangles) trs.push_back(ti); - its_store_triangles(its, obj_filename, trs); - //its_write_obj(its,"original.obj"); -} - -bool check_neighbors(const indexed_triangle_set &its, - const TriangleInfos & t_infos, - const VertexInfos & v_infos, - const EdgeInfos & e_infos) -{ - VertexInfos v_infos2(v_infos.size()); - size_t count_indices = 0; - - for (size_t ti = 0; ti < its.indices.size(); ti++) { - if (t_infos[ti].is_deleted()) continue; - ++count_indices; - const Triangle &t = its.indices[ti]; - for (size_t e = 0; e < 3; e++) { - VertexInfo &v_info = v_infos2[t[e]]; - ++v_info.count; // triangle count - } - } - - uint32_t triangle_start = 0; - for (VertexInfo &v_info : v_infos2) { - v_info.start = triangle_start; - triangle_start += v_info.count; - // set filled vertex to zero - v_info.count = 0; - } - - // create reference - EdgeInfos e_infos2(count_indices * 3); - for (size_t ti = 0; ti < its.indices.size(); ti++) { - if (t_infos[ti].is_deleted()) continue; - const Triangle &t = its.indices[ti]; - for (size_t j = 0; j < 3; ++j) { - VertexInfo &v_info = v_infos2[t[j]]; - size_t ei = v_info.start + v_info.count; - assert(ei < e_infos2.size()); - EdgeInfo &e_info = e_infos2[ei]; - e_info.t_index = ti; - e_info.edge = j; - ++v_info.count; - } - } - - for (size_t vi = 0; vi < its.vertices.size(); vi++) { - const VertexInfo &v_info = v_infos[vi]; - if (v_info.is_deleted()) continue; - const VertexInfo &v_info2 = v_infos2[vi]; - if (v_info.count != v_info2.count) { - return false; - } - EdgeInfos eis; - eis.reserve(v_info.count); - std::copy(e_infos.begin() + v_info.start, - e_infos.begin() + v_info.start + v_info.count, - std::back_inserter(eis)); - auto compare = [](const EdgeInfo &ei1, const EdgeInfo &ei2) { - return ei1.t_index < ei2.t_index; - }; - std::sort(eis.begin(), eis.end(), compare); - std::sort(e_infos2.begin() + v_info2.start, - e_infos2.begin() + v_info2.start + v_info2.count, - compare); - for (size_t ei = 0; ei < v_info.count; ++ei) { - if (eis[ei].t_index != e_infos2[ei + v_info2.start].t_index) { - return false; - } - } - } - return true; -} - void Slic3r::its_quadric_edge_collapse( indexed_triangle_set & its, uint32_t triangle_count, float * max_error, std::function throw_on_cancel, - std::function statusfn) + std::function status_fn) { // constants --> may be move to config const int status_init_size = 10; // in percents @@ -222,15 +119,19 @@ void Slic3r::its_quadric_edge_collapse( float maximal_error = (max_error == nullptr)? std::numeric_limits::max() : *max_error; if (maximal_error <= 0.f) return; if (throw_on_cancel == nullptr) throw_on_cancel = []() {}; - if (statusfn == nullptr) statusfn = [](int) {}; + if (status_fn == nullptr) status_fn = [](int) {}; + + StatusFn init_status_fn = [&](int percent) { + status_fn(std::round((percent * status_init_size) / 100.)); + }; TriangleInfos t_infos; // only normals with information about deleted triangle VertexInfos v_infos; EdgeInfos e_infos; Errors errors; - std::tie(t_infos, v_infos, e_infos, errors) = init(its); + std::tie(t_infos, v_infos, e_infos, errors) = init(its, throw_on_cancel, init_status_fn); throw_on_cancel(); - statusfn(status_init_size); + status_fn(status_init_size); //its_store_triangle(its, "triangle.obj", 1182); //store_surround("triangle_surround1.obj", 1182, 1, its, v_infos, e_infos); @@ -259,7 +160,7 @@ void Slic3r::its_quadric_edge_collapse( (double) count_triangle_to_reduce; double status = status_init_size + (100 - status_init_size) * (1. - reduced); - statusfn(static_cast(std::round(status))); + status_fn(static_cast(std::round(status))); }; // modulo for update status uint32_t status_mod = std::max(uint32_t(16), count_triangle_to_reduce / 100); @@ -481,8 +382,16 @@ SymMat QuadricEdgeCollapse::create_quadric(const Triangle &t, } std::tuple -QuadricEdgeCollapse::init(const indexed_triangle_set &its) +QuadricEdgeCollapse::init(const indexed_triangle_set &its, ThrowOnCancel& throw_on_cancel, StatusFn& status_fn) { + // change speed of progress bargraph + const int status_normal_size = 25; + const int status_sum_quadric = 25; + const int status_set_offsets = 10; + const int status_calc_errors = 30; + const int status_create_refs = 10; + + int status_offset = 0; TriangleInfos t_infos(its.indices.size()); VertexInfos v_infos(its.vertices.size()); { @@ -496,8 +405,13 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its) Vec3d normal = create_normal(t, its.vertices); t_info.n = normal.cast(); triangle_quadrics[i] = create_quadric(t, normal, its.vertices); + if (i % 1000000 == 0) { + throw_on_cancel(); + status_fn(status_offset + (i * status_normal_size) / its.indices.size()); + } } }); // END parallel for + status_offset += status_normal_size; // sum quadrics for (size_t i = 0; i < its.indices.size(); i++) { @@ -508,7 +422,12 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its) v_info.q += q; ++v_info.count; // triangle count } + if (i % 1000000 == 0) { + throw_on_cancel(); + status_fn(status_offset + (i * status_sum_quadric) / its.indices.size()); + } } + status_offset += status_sum_quadric; } // remove triangle quadrics // set offseted starts @@ -521,6 +440,10 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its) } assert(its.indices.size() * 3 == triangle_start); + status_offset += status_set_offsets; + throw_on_cancel(); + status_fn(status_offset); + // calc error Errors errors(its.indices.size()); tbb::parallel_for(tbb::blocked_range(0, its.indices.size()), @@ -529,8 +452,15 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its) const Triangle &t = its.indices[i]; TriangleInfo & t_info = t_infos[i]; errors[i] = calculate_error(i, t, its.vertices, v_infos, t_info.min_index); + if (i % 1000000 == 0) { + throw_on_cancel(); + status_fn(status_offset + (i * status_calc_errors) / its.indices.size()); + } + if (i % 1000000 == 0) throw_on_cancel(); } }); // END parallel for + + status_offset += status_calc_errors; // create reference EdgeInfos e_infos(its.indices.size() * 3); @@ -545,7 +475,14 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its) e_info.edge = j; ++v_info.count; } + if (i % 1000000 == 0) { + throw_on_cancel(); + status_fn(status_offset + (i * status_create_refs) / its.indices.size()); + } } + + throw_on_cancel(); + status_fn(100); return {t_infos, v_infos, e_infos, errors}; } @@ -794,3 +731,115 @@ void QuadricEdgeCollapse::compact(const VertexInfos & v_infos, } its.indices.erase(its.indices.begin() + ti_new, its.indices.end()); } + +#ifndef NDEBUG +// store triangle surrounding to file +void QuadricEdgeCollapse::store_surround(const char *obj_filename, + size_t triangle_index, + int depth, + const indexed_triangle_set &its, + const VertexInfos & v_infos, + const EdgeInfos & e_infos) +{ + std::set triangles; + // triangle index, depth + using Item = std::pair; + std::queue process; + process.push({triangle_index, depth}); + + while (!process.empty()) { + Item item = process.front(); + process.pop(); + size_t ti = item.first; + auto it = triangles.find(ti); + if (it != triangles.end()) continue; + triangles.insert(ti); + if (item.second == 0) continue; + + const Vec3i &t = its.indices[ti]; + for (size_t i = 0; i < 3; ++i) { + const auto &v_info = v_infos[t[i]]; + for (size_t d = 0; d < v_info.count; ++d) { + size_t ei = v_info.start + d; + const auto &e_info = e_infos[ei]; + auto it = triangles.find(e_info.t_index); + if (it != triangles.end()) continue; + process.push({e_info.t_index, item.second - 1}); + } + } + } + + std::vector trs; + trs.reserve(triangles.size()); + for (size_t ti : triangles) trs.push_back(ti); + its_store_triangles(its, obj_filename, trs); + // its_write_obj(its,"original.obj"); +} + +bool QuadricEdgeCollapse::check_neighbors(const indexed_triangle_set &its, + const TriangleInfos & t_infos, + const VertexInfos & v_infos, + const EdgeInfos & e_infos) +{ + VertexInfos v_infos2(v_infos.size()); + size_t count_indices = 0; + + for (size_t ti = 0; ti < its.indices.size(); ti++) { + if (t_infos[ti].is_deleted()) continue; + ++count_indices; + const Triangle &t = its.indices[ti]; + for (size_t e = 0; e < 3; e++) { + VertexInfo &v_info = v_infos2[t[e]]; + ++v_info.count; // triangle count + } + } + + uint32_t triangle_start = 0; + for (VertexInfo &v_info : v_infos2) { + v_info.start = triangle_start; + triangle_start += v_info.count; + // set filled vertex to zero + v_info.count = 0; + } + + // create reference + EdgeInfos e_infos2(count_indices * 3); + for (size_t ti = 0; ti < its.indices.size(); ti++) { + if (t_infos[ti].is_deleted()) continue; + const Triangle &t = its.indices[ti]; + for (size_t j = 0; j < 3; ++j) { + VertexInfo &v_info = v_infos2[t[j]]; + size_t ei = v_info.start + v_info.count; + assert(ei < e_infos2.size()); + EdgeInfo &e_info = e_infos2[ei]; + e_info.t_index = ti; + e_info.edge = j; + ++v_info.count; + } + } + + for (size_t vi = 0; vi < its.vertices.size(); vi++) { + const VertexInfo &v_info = v_infos[vi]; + if (v_info.is_deleted()) continue; + const VertexInfo &v_info2 = v_infos2[vi]; + if (v_info.count != v_info2.count) { return false; } + EdgeInfos eis; + eis.reserve(v_info.count); + std::copy(e_infos.begin() + v_info.start, + e_infos.begin() + v_info.start + v_info.count, + std::back_inserter(eis)); + auto compare = [](const EdgeInfo &ei1, const EdgeInfo &ei2) { + return ei1.t_index < ei2.t_index; + }; + std::sort(eis.begin(), eis.end(), compare); + std::sort(e_infos2.begin() + v_info2.start, + e_infos2.begin() + v_info2.start + v_info2.count, compare); + for (size_t ei = 0; ei < v_info.count; ++ei) { + if (eis[ei].t_index != e_infos2[ei + v_info2.start].t_index) { + return false; + } + } + } + return true; +} +#endif /* NDEBUG */ From cce3041a95239e08afc8a61bab8159bd3d5074b1 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Fri, 20 Aug 2021 07:53:33 +0200 Subject: [PATCH 10/23] Add warning as tip for simplify big model. --- src/slic3r/GUI/Plater.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e037a0ac8..4af8637e4 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2408,6 +2408,28 @@ std::vector Plater::priv::load_files(const std::vector& input_ return obj_idxs; } + // detection of simplification suggestion + const uint32_t triangles_to_suggest_simplify = 1000000; + for (const ModelObject *model_object : model.objects) { + const indexed_triangle_set &its = + model_object->volumes.front()->mesh().its; + uint32_t triangle_count = its.indices.size(); + if (triangle_count > triangles_to_suggest_simplify) { + std::string text = _u8L("Processing models with more than 1M triangles" + " could be slow. It is highly recommend to reduce amount of triangles.") + "\n"; + std::string hypertext = "Simplify mesh."; + std::function action_fn = [&](wxEvtHandler *) { + auto &manager = q->canvas3D()->get_gizmos_manager(); + manager.open_gizmo(GLGizmosManager::EType::Simplify); + return true; + }; + notification_manager->push_notification( + NotificationType::CustomNotification, + NotificationManager::NotificationLevel::WarningNotification, + _u8L("WARNING:") + "\n" + text, hypertext, action_fn); + } + } + for (ModelObject* model_object : model.objects) { if (!type_3mf && !type_zip_amf) model_object->center_around_origin(false); From 8fab4885c7c0825844281794f5b2ddc566ab9e0e Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Fri, 20 Aug 2021 09:13:09 +0200 Subject: [PATCH 11/23] Add dirty state into Gizmo (hint by @DavidKocik) --- src/slic3r/GUI/GLCanvas3D.cpp | 6 ++++-- src/slic3r/GUI/Gizmos/GLGizmoBase.cpp | 14 ++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoBase.hpp | 8 ++++++++ src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 2 ++ 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 053f7fb32..cae2dfc23 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2239,6 +2239,8 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(wxGetApp().plater()->get_camera()); m_dirty |= mouse3d_controller_applied; m_dirty |= wxGetApp().plater()->get_notification_manager()->update_notifications(*this); + auto gizmo = wxGetApp().plater()->canvas3D()->get_gizmos_manager().get_current(); + if (gizmo != nullptr) m_dirty |= gizmo->update_items_state(); if (!m_dirty) return; @@ -2780,10 +2782,10 @@ void GLCanvas3D::on_timer(wxTimerEvent& evt) void GLCanvas3D::on_render_timer(wxTimerEvent& evt) { - m_dirty = true; - // wxWakeUpIdle(); // no need to wake up idle // right after this event, idle event is fired + // m_dirty = true; + // wxWakeUpIdle(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index 92c03cb75..2952c5e81 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -85,6 +85,7 @@ GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, u , m_dragging(false) , m_imgui(wxGetApp().imgui()) , m_first_input_window_render(true) + , m_dirty(false) { m_base_color = DEFAULT_BASE_COLOR; m_drag_color = DEFAULT_DRAG_COLOR; @@ -154,6 +155,14 @@ void GLGizmoBase::update(const UpdateData& data) on_update(data); } +bool GLGizmoBase::update_items_state() +{ + std::lock_guard g(m_dirty_access); + bool res = m_dirty; + m_dirty = false; + return res; +}; + std::array GLGizmoBase::picking_color_component(unsigned int id) const { static const float INV_255 = 1.0f / 255.0f; @@ -209,6 +218,11 @@ std::string GLGizmoBase::format(float value, unsigned int decimals) const return Slic3r::string_printf("%.*f", decimals, value); } +void GLGizmoBase::set_dirty() { + std::lock_guard g(m_dirty_access); + m_dirty = true; +} + void GLGizmoBase::render_input_window(float x, float y, float bottom_limit) { on_render_input_window(x, y, bottom_limit); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index 2fb9c296a..af1cc6bab 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -1,6 +1,7 @@ #ifndef slic3r_GLGizmoBase_hpp_ #define slic3r_GLGizmoBase_hpp_ +#include #include "libslic3r/Point.hpp" #include "slic3r/GUI/I18N.hpp" @@ -153,6 +154,7 @@ public: bool is_dragging() const { return m_dragging; } void update(const UpdateData& data); + bool update_items_state(); void render() { m_tooltip.clear(); on_render(); } void render_for_picking() { on_render_for_picking(); } @@ -187,6 +189,12 @@ protected: void render_grabbers_for_picking(const BoundingBoxf3& box) const; std::string format(float value, unsigned int decimals) const; + + void set_dirty(); + +private: + std::mutex m_dirty_access; + bool m_dirty; }; // Produce an alpha channel checksum for the red green blue components. The alpha channel may then be used to verify, whether the rgb components diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index a921a121e..eae79b8ed 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -266,6 +266,7 @@ void GLGizmoSimplify::process() }; std::function statusfn = [&](int percent) { m_progress = percent; + set_dirty(); m_parent.schedule_extra_frame(0); }; @@ -290,6 +291,7 @@ void GLGizmoSimplify::process() m_state = State::settings; } // need to render last status fn to change bar graph to buttons + set_dirty(); m_parent.schedule_extra_frame(0); }); } From 6d895872b0cf3db20c1b04ee1bf5dc2d5d77f860 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Fri, 20 Aug 2021 14:29:52 +0200 Subject: [PATCH 12/23] call render in main thread by function callAfter(hint by @Vojtech) --- src/slic3r/GUI/Gizmos/GLGizmoBase.cpp | 6 ++--- src/slic3r/GUI/Gizmos/GLGizmoBase.hpp | 8 ++++--- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 29 +++++++++++++++++------ src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 1 + 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index 2952c5e81..9cda6d75b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -157,9 +157,8 @@ void GLGizmoBase::update(const UpdateData& data) bool GLGizmoBase::update_items_state() { - std::lock_guard g(m_dirty_access); bool res = m_dirty; - m_dirty = false; + m_dirty = false; return res; }; @@ -218,8 +217,7 @@ std::string GLGizmoBase::format(float value, unsigned int decimals) const return Slic3r::string_printf("%.*f", decimals, value); } -void GLGizmoBase::set_dirty() { - std::lock_guard g(m_dirty_access); +void GLGizmoBase::set_dirty() { m_dirty = true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index af1cc6bab..8b033ce73 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -1,7 +1,6 @@ #ifndef slic3r_GLGizmoBase_hpp_ #define slic3r_GLGizmoBase_hpp_ -#include #include "libslic3r/Point.hpp" #include "slic3r/GUI/I18N.hpp" @@ -154,6 +153,8 @@ public: bool is_dragging() const { return m_dragging; } void update(const UpdateData& data); + + // returns True when Gizmo changed its state bool update_items_state(); void render() { m_tooltip.clear(); on_render(); } @@ -190,10 +191,11 @@ protected: std::string format(float value, unsigned int decimals) const; + // Mark gizmo as dirty to Re-Render when idle() void set_dirty(); - private: - std::mutex m_dirty_access; + // Flag for dirty visible state of Gizmo + // When True then need new rendering bool m_dirty; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index eae79b8ed..6d3031dce 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -253,7 +253,7 @@ void GLGizmoSimplify::process() plater->clear_before_change_mesh(m_obj_index); m_progress = 0; if (m_worker.joinable()) m_worker.join(); - m_worker = std::thread([&]() { + m_worker = std::thread([this]() { // store original triangles uint32_t triangle_count = (m_configuration.use_count) ? m_configuration.wanted_count : 0; float max_error = (m_configuration.use_error) ? @@ -264,10 +264,16 @@ void GLGizmoSimplify::process() throw SimplifyCanceledException(); } }; - std::function statusfn = [&](int percent) { + + std::function statusfn = [this](int percent) { m_progress = percent; - set_dirty(); - m_parent.schedule_extra_frame(0); + + // check max 4fps + static int64_t last = 0; + int64_t now = m_parent.timestamp_now(); + if ((now - last) < 250) return; + + request_rerender(); }; indexed_triangle_set collapsed; @@ -290,9 +296,8 @@ void GLGizmoSimplify::process() // set state out of main thread m_state = State::settings; } - // need to render last status fn to change bar graph to buttons - set_dirty(); - m_parent.schedule_extra_frame(0); + // need to render last status fn to change bar graph to buttons + request_rerender(); }); } @@ -335,6 +340,9 @@ void GLGizmoSimplify::on_set_state() // invalidate selected model m_volume = nullptr; + } else if (GLGizmoBase::m_state == GLGizmoBase::On) { + // when open by hyperlink it needs to show up + request_rerender(); } } @@ -360,4 +368,11 @@ void GLGizmoSimplify::create_gui_cfg() { m_gui_cfg = cfg; } +void GLGizmoSimplify::request_rerender() { + wxGetApp().plater()->CallAfter([this]() { + set_dirty(); + m_parent.schedule_extra_frame(0); + }); +} + } // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index 4539fef01..88e60a9fc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -35,6 +35,7 @@ private: void process(); void set_its(indexed_triangle_set &its); void create_gui_cfg(); + void request_rerender(); bool m_is_valid_result; // differ what to do in apply volatile int m_progress; // percent of done work From 1aa4b32fa704b35c3e46b17745f25389a43c9e4c Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Mon, 23 Aug 2021 11:31:38 +0200 Subject: [PATCH 13/23] Create multiple simplify notification --- src/slic3r/GUI/NotificationManager.hpp | 13 ++++- src/slic3r/GUI/Plater.cpp | 73 ++++++++++++++++++-------- 2 files changed, 61 insertions(+), 25 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index b347c9dfe..8c82a550b 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -96,7 +96,9 @@ enum class NotificationType DidYouKnowHint, // Shows when ObjectList::update_info_items finds information that should be stressed to the user // Might contain logo taken from gizmos - UpdatedItemsInfo + UpdatedItemsInfo, + // Give user advice to simplify object with big amount of triangles + SimplifySuggestion }; class NotificationManager @@ -537,7 +539,14 @@ private: // Timestamp of last rendering int64_t m_last_render { 0LL }; // Notification types that can be shown multiple types at once (compared by text) - const std::vector m_multiple_types = { NotificationType::CustomNotification, NotificationType::PlaterWarning, NotificationType::ProgressBar, NotificationType::PrintHostUpload, NotificationType::UpdatedItemsInfo }; + const std::vector m_multiple_types = { + NotificationType::CustomNotification, + NotificationType::PlaterWarning, + NotificationType::ProgressBar, + NotificationType::PrintHostUpload, + NotificationType::UpdatedItemsInfo, + NotificationType::SimplifySuggestion + }; //prepared (basic) notifications static const NotificationData basic_notifications[]; }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 4af8637e4..df0d8ee7d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1720,7 +1720,7 @@ struct Plater::priv void replace_with_stl(); void reload_all_from_disk(); void fix_through_netfabb(const int obj_idx, const int vol_idx = -1); - + void create_simplify_notification(const std::vector& obj_ids); void set_current_panel(wxPanel* panel); void on_select_preset(wxCommandEvent&); @@ -2408,28 +2408,6 @@ std::vector Plater::priv::load_files(const std::vector& input_ return obj_idxs; } - // detection of simplification suggestion - const uint32_t triangles_to_suggest_simplify = 1000000; - for (const ModelObject *model_object : model.objects) { - const indexed_triangle_set &its = - model_object->volumes.front()->mesh().its; - uint32_t triangle_count = its.indices.size(); - if (triangle_count > triangles_to_suggest_simplify) { - std::string text = _u8L("Processing models with more than 1M triangles" - " could be slow. It is highly recommend to reduce amount of triangles.") + "\n"; - std::string hypertext = "Simplify mesh."; - std::function action_fn = [&](wxEvtHandler *) { - auto &manager = q->canvas3D()->get_gizmos_manager(); - manager.open_gizmo(GLGizmosManager::EType::Simplify); - return true; - }; - notification_manager->push_notification( - NotificationType::CustomNotification, - NotificationManager::NotificationLevel::WarningNotification, - _u8L("WARNING:") + "\n" + text, hypertext, action_fn); - } - } - for (ModelObject* model_object : model.objects) { if (!type_3mf && !type_zip_amf) model_object->center_around_origin(false); @@ -2496,6 +2474,8 @@ std::vector Plater::priv::load_files(const std::vector& input_ view3D->get_canvas3d()->update_gizmos_on_off_state(); } + create_simplify_notification(obj_idxs); + return obj_idxs; } @@ -3529,6 +3509,53 @@ void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* = q->SetFocus(); } +void Plater::priv::create_simplify_notification(const std::vector& obj_ids) { + const uint32_t triangles_to_suggest_simplify = 1000000; + + std::vector big_ids; + big_ids.reserve(obj_ids.size()); + std::copy_if(obj_ids.begin(), obj_ids.end(), std::back_inserter(big_ids), + [this, triangles_to_suggest_simplify](size_t object_id) { + if (object_id >= model.objects.size()) return false; // out of object index + ModelVolumePtrs& volumes = model.objects[object_id]->volumes; + if (volumes.size() != 1) return false; // not only one volume + size_t triangle_count = volumes.front()->mesh().its.indices.size(); + if (triangle_count < triangles_to_suggest_simplify) return false; // small volume + return true; + }); + + if (big_ids.empty()) return; + + for (size_t object_id : big_ids) { + std::string t = _u8L( + "Processing model '@object_name' with more than 1M triangles " + "could be slow. It is highly recommend to reduce " + "amount of triangles."); + t.replace(t.find("@object_name"), sizeof("@object_name") - 1, + model.objects[object_id]->name); + std::stringstream text; + text << _u8L("WARNING:") << "\n" << t << "\n"; + std::string hypertext = _u8L("Simplify model"); + + std::function open_simplify = [object_id](wxEvtHandler *) { + auto plater = wxGetApp().plater(); + if (object_id >= plater->model().objects.size()) return true; + + Selection &selection = plater->canvas3D()->get_selection(); + selection.clear(); + selection.add_object((unsigned int) object_id); + + auto &manager = plater->canvas3D()->get_gizmos_manager(); + manager.open_gizmo(GLGizmosManager::EType::Simplify); + return true; + }; + notification_manager->push_notification( + NotificationType::SimplifySuggestion, + NotificationManager::NotificationLevel::WarningNotification, + text.str(), hypertext, open_simplify); + } +} + void Plater::priv::set_current_panel(wxPanel* panel) { if (std::find(panels.begin(), panels.end(), panel) == panels.end()) From b555910185599faa7df15e4f474fab62fa80daac Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Mon, 23 Aug 2021 15:57:09 +0200 Subject: [PATCH 14/23] Simplify dialog, remove fast reduction - create mistake --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 120 ++++++++++------------ src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 6 +- 2 files changed, 58 insertions(+), 68 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 6d3031dce..65e152aab 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -111,63 +111,69 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi ImGui::Separator(); - ImGui::Text(_L("Limit by triangles").c_str()); + if(ImGui::RadioButton("##use_error", !m_configuration.use_count)) { + m_is_valid_result = false; + m_configuration.use_count = !m_configuration.use_count; + } + ImGui::SameLine(); + m_imgui->disabled_begin(m_configuration.use_count); + ImGui::Text(_L("Detail level").c_str()); + std::vector reduce_captions = { + _u8L("Extra high"), + _u8L("High"), + _u8L("Medium"), + _u8L("Low"), + _u8L("Extra low") + }; ImGui::SameLine(m_gui_cfg->bottom_left_width); - // First initialization + fix triangle count - if (m_imgui->checkbox("##UseCount", m_configuration.use_count)) { - if (!m_configuration.use_count) m_configuration.use_error = true; + ImGui::SetNextItemWidth(m_gui_cfg->input_width); + static int reduction = 3; + if(ImGui::SliderInt("##ReductionLevel", &reduction, 1, 5, reduce_captions[reduction-1].c_str())) { m_is_valid_result = false; + if (reduction < 1) reduction = 1; + if (reduction > 5) reduction = 5; + switch (reduction) { + case 1: m_configuration.max_error = 1e-3f; break; + case 2: m_configuration.max_error = 1e-2f; break; + case 3: m_configuration.max_error = 0.1f; break; + case 4: m_configuration.max_error = 0.5f; break; + case 5: m_configuration.max_error = 1.f; break; + } + } + m_imgui->disabled_end(); // !use_count + + if (ImGui::RadioButton("##use_count", m_configuration.use_count)) { + m_is_valid_result = false; + m_configuration.use_count = !m_configuration.use_count; + } + ImGui::SameLine(); + + // show preview result triangle count (percent) + if (m_need_reload && !m_configuration.use_count) { + m_configuration.wanted_count = static_cast(m_volume->mesh().its.indices.size()); + m_configuration.update_count(triangle_count); } m_imgui->disabled_begin(!m_configuration.use_count); - ImGui::Text(_L("Triangle count").c_str()); + ImGui::Text(_L("Ratio").c_str()); ImGui::SameLine(m_gui_cfg->bottom_left_width); int wanted_count = m_configuration.wanted_count; ImGui::SetNextItemWidth(m_gui_cfg->input_width); - if (ImGui::SliderInt("##triangle_count", &wanted_count, min_triangle_count, triangle_count, "%d")) { - m_configuration.wanted_count = static_cast(wanted_count); - if (m_configuration.wanted_count < min_triangle_count) - m_configuration.wanted_count = min_triangle_count; - if (m_configuration.wanted_count > triangle_count) - m_configuration.wanted_count = triangle_count; - m_configuration.update_count(triangle_count); + const char * format = (m_configuration.wanted_percent > 10)? "%.0f %%": + ((m_configuration.wanted_percent > 1)? "%.1f %%":"%.2f %%"); + if (ImGui::SliderFloat("##triangle_ratio", &m_configuration.wanted_percent, 0.f, 100.f, format)) { m_is_valid_result = false; - } - ImGui::Text(_L("Ratio").c_str()); - ImGui::SameLine(m_gui_cfg->bottom_left_width); - ImGui::SetNextItemWidth(m_gui_cfg->input_small_width); - const char * precision = (m_configuration.wanted_percent > 10)? "%.0f": - ((m_configuration.wanted_percent > 1)? "%.1f":"%.2f"); - float step = (m_configuration.wanted_percent > 10)? 1.f: - ((m_configuration.wanted_percent > 1)? 0.1f : 0.01f); - if (ImGui::InputFloat("%", &m_configuration.wanted_percent, step, 10*step, precision)) { - if (m_configuration.wanted_percent > 100.f) m_configuration.wanted_percent = 100.f; + if (m_configuration.wanted_percent < 0.f) + m_configuration.wanted_percent = 0.01; + if (m_configuration.wanted_percent > 100.f) + m_configuration.wanted_percent = 100.f; m_configuration.update_percent(triangle_count); - if (m_configuration.wanted_count < min_triangle_count) { - m_configuration.wanted_count = min_triangle_count; - m_configuration.update_count(triangle_count); - } - m_is_valid_result = false; } - m_imgui->disabled_end(); // use_count ImGui::NewLine(); - ImGui::Text(_L("Limit by error").c_str()); ImGui::SameLine(m_gui_cfg->bottom_left_width); - if (m_imgui->checkbox("##UseError", m_configuration.use_error)) { - if (!m_configuration.use_error) m_configuration.use_count = true; - m_is_valid_result = false; - } - - m_imgui->disabled_begin(!m_configuration.use_error); - ImGui::Text(_L("Max. error").c_str()); - ImGui::SameLine(m_gui_cfg->bottom_left_width); - ImGui::SetNextItemWidth(m_gui_cfg->input_small_width); - if (ImGui::InputFloat("##maxError", &m_configuration.max_error, 0.01f, .1f, "%.2f")) { - if (m_configuration.max_error < 0.f) m_configuration.max_error = 0.f; - m_is_valid_result = false; - } - m_imgui->disabled_end(); // use_error + ImGui::Text(_L("%d triangles").c_str(), m_configuration.wanted_count); + m_imgui->disabled_end(); // use_count if (m_state == State::settings) { if (m_imgui->button(_L("Cancel"))) { @@ -256,8 +262,7 @@ void GLGizmoSimplify::process() m_worker = std::thread([this]() { // store original triangles uint32_t triangle_count = (m_configuration.use_count) ? m_configuration.wanted_count : 0; - float max_error = (m_configuration.use_error) ? - m_configuration.max_error : std::numeric_limits::max(); + float max_error = (!m_configuration.use_count) ? m_configuration.max_error : std::numeric_limits::max(); std::function throw_on_cancel = [&]() { if (m_state == State::canceling) { @@ -272,26 +277,17 @@ void GLGizmoSimplify::process() static int64_t last = 0; int64_t now = m_parent.timestamp_now(); if ((now - last) < 250) return; + last = now; request_rerender(); }; - indexed_triangle_set collapsed; - if (m_last_error.has_value() && m_last_count.has_value() && - (!m_configuration.use_count || triangle_count <= *m_last_count) && - (!m_configuration.use_error || m_configuration.max_error <= *m_last_error)) { - // continue from last reduction - speed up - collapsed = m_volume->mesh().its; // small copy - } else { - collapsed = *m_original_its; // copy - } + indexed_triangle_set collapsed = *m_original_its; // copy try { its_quadric_edge_collapse(collapsed, triangle_count, &max_error, throw_on_cancel, statusfn); set_its(collapsed); m_is_valid_result = true; - m_last_count = triangle_count; // need to store last requirement, collapsed count could be count-1 - m_last_error = max_error; } catch (SimplifyCanceledException &) { // set state out of main thread m_state = State::settings; @@ -348,20 +344,18 @@ void GLGizmoSimplify::on_set_state() void GLGizmoSimplify::create_gui_cfg() { if (m_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; + const float radio_size = ImGui::GetFrameHeight(); 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; + std::max(m_imgui->calc_text_size(_L("Detail level")).x, + m_imgui->calc_text_size(_L("Ratio")).x) + + space_size + radio_size; + cfg.input_width = cfg.bottom_left_width; cfg.input_small_width = cfg.input_width * 0.8; cfg.window_offset = cfg.input_width; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index 88e60a9fc..4e356112a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -44,9 +44,6 @@ private: std::optional m_original_its; - std::optional m_last_error; // for use previous reduction - std::optional m_last_count; - volatile bool m_need_reload; // after simplify, glReload must be on main thread std::thread m_worker; @@ -60,12 +57,11 @@ private: struct Configuration { - bool use_count = true; + bool use_count = false; // 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.; From db6c984b36b61c1063ea9c1c35fefbc654353586 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Mon, 23 Aug 2021 16:26:21 +0200 Subject: [PATCH 15/23] remove unused variable --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 3 +-- src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 65e152aab..a6f4f97a6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -46,7 +46,7 @@ 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; + const int max_char_in_name = 20; create_gui_cfg(); const Selection &selection = m_parent.get_selection(); @@ -357,7 +357,6 @@ void GLGizmoSimplify::create_gui_cfg() { space_size + radio_size; cfg.input_width = cfg.bottom_left_width; - cfg.input_small_width = cfg.input_width * 0.8; cfg.window_offset = cfg.input_width; m_gui_cfg = cfg; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index 4e356112a..3d667d691 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -84,7 +84,6 @@ private: int top_left_width = 100; int bottom_left_width = 100; int input_width = 100; - int input_small_width = 80; int window_offset = 100; int window_padding = 0; }; From d7d4d528439d03c43ec4b4fe90721a01b122b2c5 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Tue, 24 Aug 2021 08:21:14 +0200 Subject: [PATCH 16/23] Fix ../src/libslic3r/QuadricEdgeCollapse.cpp:565:22: warning: comparison of integer expressions of different signedness: 'const int' and 'uint32_t' {aka 'unsigned int'} [-Wsign-compare] ../src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp:48:15: warning: unused variable 'min_triangle_count' [-Wunused-variable] ../src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp:160:9: warning: unused variable 'wanted_count' [-Wunused-variable] ..\src\slic3r\GUI\Gizmos\GLGizmoSimplify.cpp(167): warning C4305: '=': truncation from 'double' to 'float' --- src/libslic3r/QuadricEdgeCollapse.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 14 ++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp index c4e874c65..6efc5f4a9 100644 --- a/src/libslic3r/QuadricEdgeCollapse.cpp +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -562,7 +562,7 @@ bool QuadricEdgeCollapse::degenerate(uint32_t vi, if (e_info.t_index == ti1) continue; // ti1 will be deleted const Triangle &t = indices[e_info.t_index]; for (size_t i = 0; i < 3; ++i) - if (t[i] == vi) return true; + if (static_cast(t[i]) == vi) return true; } return false; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index a6f4f97a6..f0336f729 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -45,7 +45,6 @@ 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 = 20; create_gui_cfg(); @@ -119,11 +118,11 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi m_imgui->disabled_begin(m_configuration.use_count); ImGui::Text(_L("Detail level").c_str()); std::vector reduce_captions = { - _u8L("Extra high"), - _u8L("High"), - _u8L("Medium"), - _u8L("Low"), - _u8L("Extra low") + static_cast(_u8L("Extra high")), + static_cast(_u8L("High")), + static_cast(_u8L("Medium")), + static_cast(_u8L("Low")), + static_cast(_u8L("Extra low")) }; ImGui::SameLine(m_gui_cfg->bottom_left_width); ImGui::SetNextItemWidth(m_gui_cfg->input_width); @@ -157,14 +156,13 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi m_imgui->disabled_begin(!m_configuration.use_count); ImGui::Text(_L("Ratio").c_str()); ImGui::SameLine(m_gui_cfg->bottom_left_width); - int wanted_count = m_configuration.wanted_count; ImGui::SetNextItemWidth(m_gui_cfg->input_width); const char * format = (m_configuration.wanted_percent > 10)? "%.0f %%": ((m_configuration.wanted_percent > 1)? "%.1f %%":"%.2f %%"); if (ImGui::SliderFloat("##triangle_ratio", &m_configuration.wanted_percent, 0.f, 100.f, format)) { m_is_valid_result = false; if (m_configuration.wanted_percent < 0.f) - m_configuration.wanted_percent = 0.01; + m_configuration.wanted_percent = 0.01f; if (m_configuration.wanted_percent > 100.f) m_configuration.wanted_percent = 100.f; m_configuration.update_percent(triangle_count); From debf4981a6a20f67545c399b3144014fe40c2a10 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Wed, 25 Aug 2021 09:44:50 +0200 Subject: [PATCH 17/23] Fix clang for VisualStudio usage --- .clang-format | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clang-format b/.clang-format index 6b68254ec..440c89ec5 100644 --- a/.clang-format +++ b/.clang-format @@ -77,7 +77,7 @@ IndentWidth: 4 IndentWrappedFunctionNames: false JavaScriptQuotes: Leave JavaScriptWrapImports: true -KeepLineBreaksForNonEmptyLines: false +#KeepLineBreaksForNonEmptyLines: false KeepEmptyLinesAtTheStartOfBlocks: false MacroBlockBegin: '' MacroBlockEnd: '' From beb38d09ae16c7d8308806be59893a7d0413548c Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Wed, 25 Aug 2021 10:51:11 +0200 Subject: [PATCH 18/23] Add reserve to mesh boolean operation --- src/libslic3r/MeshBoolean.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp index 3dd5da307..a59165946 100644 --- a/src/libslic3r/MeshBoolean.cpp +++ b/src/libslic3r/MeshBoolean.cpp @@ -118,6 +118,11 @@ void triangle_mesh_to_cgal(const std::vector & V, { if (F.empty()) return; + size_t vertices_count = V.size(); + size_t edges_count = (F.size()* 3) / 2; + size_t faces_count = F.size(); + out.reserve(vertices_count, edges_count, faces_count); + for (auto &v : V) out.add_vertex(typename _Mesh::Point{v.x(), v.y(), v.z()}); From 9896a2190466d1c0d6a4c76e7ec0541f6bef3072 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Wed, 25 Aug 2021 13:09:37 +0200 Subject: [PATCH 19/23] @Vojta request --> change ratio to decimation ratio --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 80 ++++++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 27 +++++--- 2 files changed, 66 insertions(+), 41 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index f0336f729..3530b4240 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -17,10 +17,17 @@ GLGizmoSimplify::GLGizmoSimplify(GLCanvas3D & parent, : GLGizmoBase(parent, icon_filename, -1) , m_state(State::settings) , m_is_valid_result(false) + , m_exist_preview(false) , m_progress(0) , m_volume(nullptr) , m_obj_index(0) , m_need_reload(false) + + , tr_mesh_name(_u8L("Mesh name")) + , tr_triangles(_u8L("Triangles")) + , tr_preview(_u8L("Preview")) + , tr_detail_level(_u8L("Detail level")) + , tr_decimate_ratio(_u8L("Decimate ratio")) {} GLGizmoSimplify::~GLGizmoSimplify() { @@ -45,9 +52,7 @@ void GLGizmoSimplify::on_render_for_picking() {} void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limit) { - const int max_char_in_name = 20; create_gui_cfg(); - const Selection &selection = m_parent.get_selection(); int object_idx = selection.get_object_idx(); ModelObject *obj = wxGetApp().plater()->model().objects[object_idx]; @@ -65,15 +70,15 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi m_obj_index = object_idx; // to remember correct object m_volume = act_volume; m_original_its = {}; - const TriangleMesh &tm = m_volume->mesh(); - m_configuration.wanted_percent = 50.; // default value - m_configuration.update_percent(tm.its.indices.size()); + m_configuration.decimate_ratio = 50.; // default value + m_configuration.fix_count_by_ratio(m_volume->mesh().its.indices.size()); m_is_valid_result = false; + m_exist_preview = false; if (change_window_position) { ImVec2 pos = ImGui::GetMousePos(); - pos.x -= m_gui_cfg->window_offset; - pos.y -= m_gui_cfg->window_offset; + pos.x -= m_gui_cfg->window_offset_x; + pos.y -= m_gui_cfg->window_offset_y; // minimal top left value ImVec2 tl(m_gui_cfg->window_padding, m_gui_cfg->window_padding); if (pos.x < tl.x) pos.x = tl.x; @@ -81,8 +86,8 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi // maximal bottom right value auto parent_size = m_parent.get_canvas_size(); ImVec2 br( - parent_size.get_width() - (2 * m_gui_cfg->window_offset + m_gui_cfg->window_padding), - parent_size.get_height() - (2 * m_gui_cfg->window_offset + m_gui_cfg->window_padding)); + parent_size.get_width() - (2 * m_gui_cfg->window_offset_x + m_gui_cfg->window_padding), + parent_size.get_height() - (2 * m_gui_cfg->window_offset_y + m_gui_cfg->window_padding)); if (pos.x > br.x) pos.x = br.x; if (pos.y > br.y) pos.y = br.y; ImGui::SetNextWindowPos(pos, ImGuiCond_Always); @@ -98,15 +103,23 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi if (m_original_its.has_value()) triangle_count = m_original_its->indices.size(); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Mesh name") + ":"); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr_mesh_name + ":"); ImGui::SameLine(m_gui_cfg->top_left_width); std::string name = m_volume->name; - if (name.length() > max_char_in_name) - name = name.substr(0, max_char_in_name-3) + "..."; + if (name.length() > m_gui_cfg->max_char_in_name) + name = name.substr(0, m_gui_cfg->max_char_in_name - 3) + "..."; m_imgui->text(name); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Triangles") + ":"); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr_triangles + ":"); ImGui::SameLine(m_gui_cfg->top_left_width); m_imgui->text(std::to_string(triangle_count)); + /* + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr_preview + ":"); + ImGui::SameLine(m_gui_cfg->top_left_width); + if (m_exist_preview) { + m_imgui->text(std::to_string(m_volume->mesh().its.indices.size())); + } else { + m_imgui->text("---"); + }*/ ImGui::Separator(); @@ -116,7 +129,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi } ImGui::SameLine(); m_imgui->disabled_begin(m_configuration.use_count); - ImGui::Text(_L("Detail level").c_str()); + ImGui::Text(tr_detail_level.c_str()); std::vector reduce_captions = { static_cast(_u8L("Extra high")), static_cast(_u8L("High")), @@ -150,22 +163,23 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi // show preview result triangle count (percent) if (m_need_reload && !m_configuration.use_count) { m_configuration.wanted_count = static_cast(m_volume->mesh().its.indices.size()); - m_configuration.update_count(triangle_count); + m_configuration.decimate_ratio = + (1.0f - (m_configuration.wanted_count / (float) triangle_count)) * 100.f; } m_imgui->disabled_begin(!m_configuration.use_count); - ImGui::Text(_L("Ratio").c_str()); + ImGui::Text(tr_decimate_ratio.c_str()); ImGui::SameLine(m_gui_cfg->bottom_left_width); ImGui::SetNextItemWidth(m_gui_cfg->input_width); - const char * format = (m_configuration.wanted_percent > 10)? "%.0f %%": - ((m_configuration.wanted_percent > 1)? "%.1f %%":"%.2f %%"); - if (ImGui::SliderFloat("##triangle_ratio", &m_configuration.wanted_percent, 0.f, 100.f, format)) { + const char * format = (m_configuration.decimate_ratio > 10)? "%.0f %%": + ((m_configuration.decimate_ratio > 1)? "%.1f %%":"%.2f %%"); + if (ImGui::SliderFloat("##decimate_ratio", &m_configuration.decimate_ratio, 0.f, 100.f, format)) { m_is_valid_result = false; - if (m_configuration.wanted_percent < 0.f) - m_configuration.wanted_percent = 0.01f; - if (m_configuration.wanted_percent > 100.f) - m_configuration.wanted_percent = 100.f; - m_configuration.update_percent(triangle_count); + if (m_configuration.decimate_ratio < 0.f) + m_configuration.decimate_ratio = 0.01f; + if (m_configuration.decimate_ratio > 100.f) + m_configuration.decimate_ratio = 100.f; + m_configuration.fix_count_by_ratio(triangle_count); } ImGui::NewLine(); @@ -195,7 +209,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi process(); } else { // use preview and close - if (m_original_its.has_value()) { + if (m_exist_preview) { // fix hollowing, sla support points, modifiers, ... auto plater = wxGetApp().plater(); plater->changed_mesh(m_obj_index); @@ -286,6 +300,7 @@ void GLGizmoSimplify::process() its_quadric_edge_collapse(collapsed, triangle_count, &max_error, throw_on_cancel, statusfn); set_its(collapsed); m_is_valid_result = true; + m_exist_preview = true; } catch (SimplifyCanceledException &) { // set state out of main thread m_state = State::settings; @@ -326,7 +341,7 @@ void GLGizmoSimplify::on_set_state() } // revert preview - if (m_original_its.has_value()) { + if (m_exist_preview) { set_its(*m_original_its); m_parent.reload_scene(true); m_need_reload = false; @@ -344,18 +359,19 @@ void GLGizmoSimplify::create_gui_cfg() { if (m_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) + cfg.top_left_width = std::max(m_imgui->calc_text_size(tr_mesh_name).x, + m_imgui->calc_text_size(tr_triangles).x) + space_size; const float radio_size = ImGui::GetFrameHeight(); cfg.bottom_left_width = - std::max(m_imgui->calc_text_size(_L("Detail level")).x, - m_imgui->calc_text_size(_L("Ratio")).x) + + std::max(m_imgui->calc_text_size(tr_detail_level).x, + m_imgui->calc_text_size(tr_decimate_ratio).x) + space_size + radio_size; - cfg.input_width = cfg.bottom_left_width; - cfg.window_offset = cfg.input_width; + cfg.input_width = cfg.bottom_left_width * 1.5; + cfg.window_offset_x = (cfg.bottom_left_width + cfg.input_width)/2; + cfg.window_offset_y = ImGui::GetTextLineHeightWithSpacing() * 5; m_gui_cfg = cfg; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index 3d667d691..9af6686bd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -37,7 +37,9 @@ private: void create_gui_cfg(); void request_rerender(); - bool m_is_valid_result; // differ what to do in apply + std::atomic_bool m_is_valid_result; // differ what to do in apply + std::atomic_bool m_exist_preview; // set when process end + volatile int m_progress; // percent of done work ModelVolume *m_volume; // size_t m_obj_index; @@ -59,20 +61,16 @@ private: { bool use_count = false; // minimal triangle count - float wanted_percent = 50.f; + float decimate_ratio = 50.f; // in percent uint32_t wanted_count = 0; // initialize by percents // 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) + void fix_count_by_ratio(size_t triangle_count) { wanted_count = static_cast( - std::round(triangle_count * wanted_percent / 100.f)); + std::round(triangle_count * (100.f-decimate_ratio) / 100.f)); } } m_configuration; @@ -84,10 +82,21 @@ private: int top_left_width = 100; int bottom_left_width = 100; int input_width = 100; - int window_offset = 100; + int window_offset_x = 100; + int window_offset_y = 100; int window_padding = 0; + + // trunc model name when longer + int max_char_in_name = 30; }; std::optional m_gui_cfg; + + // translations used for calc window size + const std::string tr_mesh_name; + const std::string tr_triangles; + const std::string tr_preview; + const std::string tr_detail_level; + const std::string tr_decimate_ratio; }; } // namespace GUI From 0e4a987bed809a6933b89bfc400316440051580d Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Wed, 25 Aug 2021 13:17:36 +0200 Subject: [PATCH 20/23] extend git ignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index efeaa91b5..63004bab2 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,9 @@ MANIFEST.bak xs/MANIFEST.bak xs/assertlib* .init_bundle.ini +.vs/* local-lib /src/TAGS /.vscode/ +build-linux/* +deps/build-linux/* From e6bae065571c0d8c95ec7ba6100a50fb094f7f80 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 25 Aug 2021 15:07:30 +0200 Subject: [PATCH 21/23] Notice dialog when opening any link outside PrusaSlicer --- src/slic3r/GUI/AboutDialog.cpp | 4 ++-- src/slic3r/GUI/GUI_App.cpp | 19 ++++++++++++++++++- src/slic3r/GUI/GUI_App.hpp | 3 ++- src/slic3r/GUI/HintNotification.cpp | 3 +-- src/slic3r/GUI/MainFrame.cpp | 6 +++--- src/slic3r/GUI/NotificationManager.cpp | 2 +- 6 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/AboutDialog.cpp b/src/slic3r/GUI/AboutDialog.cpp index fdbc1a6d3..72f1421f6 100644 --- a/src/slic3r/GUI/AboutDialog.cpp +++ b/src/slic3r/GUI/AboutDialog.cpp @@ -195,7 +195,7 @@ void CopyrightsDialog::on_dpi_changed(const wxRect &suggested_rect) void CopyrightsDialog::onLinkClicked(wxHtmlLinkEvent &event) { - wxLaunchDefaultBrowser(event.GetLinkInfo().GetHref()); + wxGetApp().open_browser_with_warning_dialog(event.GetLinkInfo().GetHref()); event.Skip(false); } @@ -344,7 +344,7 @@ void AboutDialog::on_dpi_changed(const wxRect &suggested_rect) void AboutDialog::onLinkClicked(wxHtmlLinkEvent &event) { - wxLaunchDefaultBrowser(event.GetLinkInfo().GetHref()); + wxGetApp().open_browser_with_warning_dialog(event.GetLinkInfo().GetHref()); event.Skip(false); } diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 58ce12ae4..102026a0c 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -2327,7 +2327,7 @@ wxString GUI_App::current_language_code_safe() const void GUI_App::open_web_page_localized(const std::string &http_address) { - wxLaunchDefaultBrowser(http_address + "&lng=" + this->current_language_code_safe()); + open_browser_with_warning_dialog(http_address + "&lng=" + this->current_language_code_safe()); } bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage start_page) @@ -2525,6 +2525,23 @@ void GUI_App::check_updates(const bool verbose) } } +bool GUI_App::open_browser_with_warning_dialog(const wxString& url, int flags/* = 0*/) +{ + bool launch = true; + + if (get_app_config()->get("suppress_hyperlinks").empty()) { + wxRichMessageDialog dialog(nullptr, _L("Should we open this hyperlink in your default browser?"), _L("PrusaSlicer: Open hyperlink"), wxICON_QUESTION | wxYES_NO); + dialog.ShowCheckBox(_L("Remember my choice")); + int answer = dialog.ShowModal(); + launch = answer == wxID_YES; + get_app_config()->set("suppress_hyperlinks", dialog.IsCheckBoxChecked() ? (answer == wxID_NO ? "1" : "0") : ""); + } + if (launch) + launch = get_app_config()->get("suppress_hyperlinks") != "1"; + + return launch && wxLaunchDefaultBrowser(url, flags); +} + // static method accepting a wxWindow object as first parameter // void warning_catcher{ // my($self, $message_dialog) = @_; diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index a140247e0..1d8c2fcf7 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -261,7 +261,8 @@ public: void open_preferences(size_t open_on_tab = 0); virtual bool OnExceptionInMainLoop() override; - + // Calls wxLaunchDefaultBrowser if user confirms in dialog. + bool open_browser_with_warning_dialog(const wxString& url, int flags = 0); #ifdef __APPLE__ void OSXStoreOpenFiles(const wxArrayString &files) override; // wxWidgets override to get an event on open files. diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index 1d5931c48..ead7a8f29 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -236,8 +236,7 @@ bool tags_check(const std::string& disabled_tags, const std::string& enabled_tag } void launch_browser_if_allowed(const std::string& url) { - if (wxGetApp().app_config->get("suppress_hyperlinks") != "1") - wxLaunchDefaultBrowser(url); + wxGetApp().open_browser_with_warning_dialog(url); } } //namespace HintDatabase::~HintDatabase() diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 498b02605..329a9a62a 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1057,7 +1057,7 @@ static wxMenu* generate_help_menu() append_menu_item(helpMenu, wxID_ANY, _L("Prusa 3D &Drivers"), _L("Open the Prusa3D drivers download page in your browser"), [](wxCommandEvent&) { wxGetApp().open_web_page_localized("https://www.prusa3d.com/downloads"); }); append_menu_item(helpMenu, wxID_ANY, _L("Software &Releases"), _L("Open the software releases page in your browser"), - [](wxCommandEvent&) { wxLaunchDefaultBrowser("https://github.com/prusa3d/PrusaSlicer/releases"); }); + [](wxCommandEvent&) { wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); }); //# my $versioncheck = $self->_append_menu_item($helpMenu, "Check for &Updates...", "Check for new Slic3r versions", sub{ //# wxTheApp->check_version(1); //# }); @@ -1067,14 +1067,14 @@ static wxMenu* generate_help_menu() [](wxCommandEvent&) { wxGetApp().open_web_page_localized("https://www.prusa3d.com/slicerweb"); }); // append_menu_item(helpMenu, wxID_ANY, wxString::Format(_L("%s &Manual"), SLIC3R_APP_NAME), // wxString::Format(_L("Open the %s manual in your browser"), SLIC3R_APP_NAME), -// [this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://manual.slic3r.org/"); }); +// [this](wxCommandEvent&) { wxGetApp().open_browser_with_warning_dialog("http://manual.slic3r.org/"); }); helpMenu->AppendSeparator(); append_menu_item(helpMenu, wxID_ANY, _L("System &Info"), _L("Show system information"), [](wxCommandEvent&) { wxGetApp().system_info(); }); append_menu_item(helpMenu, wxID_ANY, _L("Show &Configuration Folder"), _L("Show user configuration folder (datadir)"), [](wxCommandEvent&) { Slic3r::GUI::desktop_open_datadir_folder(); }); append_menu_item(helpMenu, wxID_ANY, _L("Report an I&ssue"), wxString::Format(_L("Report an issue on %s"), SLIC3R_APP_NAME), - [](wxCommandEvent&) { wxLaunchDefaultBrowser("https://github.com/prusa3d/slic3r/issues/new"); }); + [](wxCommandEvent&) { wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/slic3r/issues/new"); }); if (wxGetApp().is_editor()) append_menu_item(helpMenu, wxID_ANY, wxString::Format(_L("&About %s"), SLIC3R_APP_NAME), _L("Show about dialog"), [](wxCommandEvent&) { Slic3r::GUI::about(); }); diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 4c1c404da..3278a5a6e 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -42,7 +42,7 @@ const NotificationManager::NotificationData NotificationManager::basic_notificat } }, {NotificationType::NewAppAvailable, NotificationLevel::ImportantNotification, 20, _u8L("New version is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr) { - wxLaunchDefaultBrowser("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }}, + wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }}, {NotificationType::EmptyColorChangeCode, NotificationLevel::RegularNotification, 10, _u8L("You have just added a G-code for color change, but its value is empty.\n" "To export the G-code correctly, check the \"Color Change G-code\" in \"Printer Settings > Custom G-code\"") }, From 02dd1b5f7d5cca8a9aafde182409261dc06f5cc3 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 25 Aug 2021 16:31:23 +0200 Subject: [PATCH 22/23] rendering highlight arrow from svg --- resources/icons/toolbar_arrow.png | Bin 3996 -> 0 bytes resources/icons/toolbar_arrow.svg | 94 +++++----------------- src/slic3r/GUI/GLCanvas3D.cpp | 8 +- src/slic3r/GUI/GLToolbar.cpp | 24 +++--- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 5 +- 5 files changed, 36 insertions(+), 95 deletions(-) delete mode 100644 resources/icons/toolbar_arrow.png diff --git a/resources/icons/toolbar_arrow.png b/resources/icons/toolbar_arrow.png deleted file mode 100644 index a370442ff0e933b85d436f3abac584b2e5ac3f37..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3996 zcmeHK_cI&-)4qrn9EskEe$hM8+i6j94yQ&ZN{F5l5vPR^33Ac7aD;G3^p+e)ln}ig z=afW?UZcLg-@gCheP?#(+1+_|W@mqRb~e%6R3AviP6Yq}fQAOTkZTMF0B(>|l3zy{ zm4}npa3ct!uMNNqa;{w`q@G$PS^z*zD)qTD*>!#^z`!O50HE#sKfm$A|C8Hwku6vc z7Hr|~5gg_m=ne=A3zK;K#3#trIlx`QKhQI0OO^ea{nJoa3mWdYnRhF|u8ZwQSb^Az zda&S)G#G>=dPLhUUVDv9ycUht)FFw^Xo-c@-{p)0O*U?7uDvw`;AMbh@f`G&XmPbw zt&-qE9aJrixsz{yC?tCdnE^1gH-_W=58H=0m1@xM4-S5x0T(x;ZoWa!N1PqCHMFE5 zq#_Sx&5mG%P?15zhxb)uBGMva|5`PaBHDc&k8ez*#TGl&nI{{^bySIhiXtLnc07%y zC-yWWR^(xNw1EiA{auFii@6I(PYtXt40>5m8ncUAgXtt)Dcb28&>jS*z%b;YPeEOe z*GhVTFdZt0{*LCwY-4TUA*+4@ec#{=MV!^)IfajbCO9A}(knkF#q_6XQ*<<*p}PwXzHA>u1a6ACDonU{#b5_<{5XMZu<4WZ`;Jx z(h^cMuHk1Qctiw^6rY+1fBi zjs1Hlnh1@8&1al6ryC_h6ie7YmO7Q+FW=(K^?j8~nlsPc`W;@B*DJoBqXyosl*Y zFcRh9fO4To4qx@{Vj~o@pzE-x6qL;JDQ*15+$b?^6(mA?b%s3RY?ZaKD^J(1oZFCU zVV|VlY7v813zuG!cY#a?q%zE^OUz*TMI_=}98xXJ){4U$Rhi1P%b55@!f#~3;~}1K zZTSJNnCZGk+np2CjFPYVn!x1K2YvP1-@o`gtt3Llo`Y&zSe$}E>mzQi%Puq+-1`+` zu*I?go>@}+X|9DVNnZplAxjIr%o4LJF=rOQg<;5bWX|{YToyG?zAw*T9EgzL@Ij5r zm!XSzzWM}MG63Hc=hbY*Ev_zG<=g0l9!ZZse-8ARB;LcW!47PP&i#@tOgcPZFl#y*50p3YCwH0Ds4Z7BB zx8dlVJZQSC=T2@Tud0>dt^ngek9-^>NGz)Z(gzO@y7-&qFlnv3vz)kRipD);O(`s1 z$GJ$Kb2v(>=((N*B0V=-jA=Pjn<_FK0?MV~v~r~%oaDOBAc-3__DWPo>Fm$QYmZ9& zmAP#;xV}>LyQ3=*7RD>j?Li@mU<;AEzvFhcxqs>{d@K6QGUKhL2wEA-ywObYnu47V z$l4E{Cf#6r+2^{UqFRzu&J;Z%5vi^SuCCljt)67%*`ZCdF3IM5^c}|BCp5+1Fh`No znd?R=c)3oO@{KnAA&H+uLx)F7dh&v0Jxr}S_>Wemv8fH|k^zp)R0dq|MV8t~-iG6i z!h4G)NyJDF%sq*1WXYi@n@+Mfw|oxNM0AThf!%$t&i=rqb2#G34hGmfX(v}OS@$X z)XjF@mltL$n#?Ivd!(Sl;NU@meG-{hDcqLjR^dKv*50uEme*q`L-$%G4n&8@v$p0u zh+{2Yd*xx|QXiPwEXKz?n00FU%cQ{$B|`gNWkT5;EV;=8S%PsRD-K29s-b~4g_ZiL zwH2%uR!g`ibutlq%|FH!2i|9^+5H}*f&arDkU}&f8jNByz>`%_d zkn2lRfX?v3U5i{t-vHg*+0j|1gAOl4Bj+jGFITxP0Z?UWnn{qbs{}hg;*8^C!uPJS z-BQ7TN*IsVntY^iFJ<7kXjZPdr}nNxX*cV>+h98nq2=8IwI20EKRc|$ky_6+B9DA9 zs1jR`i(z%>tqng0qblOe<=`!hm?kqbc`&8}#;8Y);o_vr<9ViZJ@ImS{^y#%-Xyz& zESkM_tOF=qWiM>`b0$@Z2(}C+N4_$*FO|)=`R<5sdSv&}PmJ);M5K!ER#=*}>X3oP zm!=vCL=!I+v+C{F+yHGatburM*+YndTKK#`bcLT;S` zg1rV_7!Buy-|+BkG=3T+sL#vXS@E8uUT4MN(GQ_Kc3I6w%N7jrtr}fQ-e7Uq@s@9Y z5f6c7Joqo|--}=W6a`Ai9CIkq!0j1!4~~MlEw7gmlT_)V_7Zko@5ux7s_vX!T#qfJeTA_Qr`6g_IQ8TE)r#Y~hYw zy4+Epwro!TCRKjQS~f?vZTIf{Np*u^;p9#!&{Un_zYjt{TeP*IYa6Q<NcO!uj)j5QMu3gu)-?NqKv+nkp6bI@=5(`%IfE%YPXe^o$D3r^aaI~XCo z9NN42D^fC@L?7eT$HgU+^#@ds=Zl2-U~BD!xWdlWLS?0f^FEeszC*o!wD$b#UuC-i zHsueh{GNHu4#Rz`4C&=Vw^T1)yLudHz4+K99&)v!c0F0&{eB39Gi8`GrruAy z;%Ixek`#4>!y9Gid12-{?o=d4TINE%Z9cVGNSgI9YF5{st&DhiTE_)5_<7tbhNdn$ zu$NFiAEj5sqCCCb)~u-6V;iIx-!N8rc0W;UgU`K4%=g-QINfE$YLmSyZVcNCW2O2X zsUpn$6W0D%)T6{8n5Wwnq_$GlNFfVR_qk<9xY?yuTEg4@S%nhjL4%^u6%jGPjLZ^YIMLs*r;LnkX-Y-tn2a zSPf{{`^yUHHyOf1WkHg*1ydK1iF!^Z^9uM$dcA;Wg0>`VSI^ZL$u=_SQ5s9c54i7=H=JT-0BQ2IlWpN}{F8m)dh!-wVU|X;ab~K3G&TNQITJ-qEkyf2`)}I- z8`j8HS&TU*#ZKn!9s8vk2uv(q;7PA0cqc}e}5b~E!C+u%q znU@S$Ax1BLOpff|^MHIeHKwaG4~L+7LX46&7JzTC>#2)es0!+v*QZ=6J*Jq*MI)B? z>}^F^o32QR46c(Xf6I}hI89OqU)Rl&+h(g!)xUm)6w*GY>-ooGU>LoCzkYrJhI*#D z7;Ps+6=7j~FS^c&1a3MjoIcZN)f7O|jt_g$#yOV-zq{hGqIm2NpZ&ZT54ob~>jBv( LTMD)O|Nj2~#I)V2 diff --git a/resources/icons/toolbar_arrow.svg b/resources/icons/toolbar_arrow.svg index 998646a24..b49645190 100644 --- a/resources/icons/toolbar_arrow.svg +++ b/resources/icons/toolbar_arrow.svg @@ -1,76 +1,22 @@ - -image/svg+xml - \ No newline at end of file diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 227e5ae4e..1dd9f1d23 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4438,13 +4438,7 @@ bool GLCanvas3D::_init_main_toolbar() } // init arrow BackgroundTexture::Metadata arrow_data; - arrow_data.filename = "toolbar_arrow.png"; -// arrow_data.filename = "toolbar_arrow.svg"; - //arrow_data.left = 16; - //arrow_data.top = 16; - //arrow_data.right = 16; - //arrow_data.bottom = 16; - + arrow_data.filename = "toolbar_arrow.svg"; arrow_data.left = 0; arrow_data.top = 0; arrow_data.right = 0; diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 243031847..515b6ed3a 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -193,10 +193,9 @@ bool GLToolbar::init_arrow(const BackgroundTexture::Metadata& arrow_texture) std::string path = resources_dir() + "/icons/"; bool res = false; - if (!arrow_texture.filename.empty()) - res = m_arrow_texture.texture.load_from_file(path + arrow_texture.filename, false, GLTexture::SingleThreaded, false); -// res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, true, false, 100); - + if (!arrow_texture.filename.empty()) { + res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, false, false, 1000); + } if (res) m_arrow_texture.metadata = arrow_texture; @@ -1176,19 +1175,22 @@ void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighte float right = left + scaled_icons_size; unsigned int tex_id = m_arrow_texture.texture.get_id(); + // width and height of icon arrow is pointing to float tex_width = (float)m_icons_texture.get_width(); float tex_height = (float)m_icons_texture.get_height(); + // arrow width and height + float arr_tex_width = (float)m_arrow_texture.texture.get_width(); + float arr_tex_height = (float)m_arrow_texture.texture.get_height(); + if ((tex_id != 0) && (arr_tex_width > 0) && (arr_tex_height > 0)) { + float inv_tex_width = (arr_tex_width != 0.0f) ? 1.0f / arr_tex_width : 0.0f; + float inv_tex_height = (arr_tex_height != 0.0f) ? 1.0f / arr_tex_height : 0.0f; - if ((tex_id != 0) && (tex_width > 0) && (tex_height > 0)) { - float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f; - float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f; - - float internal_left = left + border - scaled_icons_size / 2; // add half scaled_icons_size for huge arrow - float internal_right = right - border + scaled_icons_size / 2; + float internal_left = left + border - scaled_icons_size * 1.5f; // add scaled_icons_size for huge arrow + float internal_right = right - border + scaled_icons_size * 1.5f; float internal_top = top - border; // bottom is not moving and should be calculated from arrow texture sides ratio float arrow_sides_ratio = (float)m_arrow_texture.texture.get_height() / (float)m_arrow_texture.texture.get_width(); - float internal_bottom = internal_top - (internal_right - internal_left) * arrow_sides_ratio; + float internal_bottom = internal_top - (internal_right - internal_left) * arrow_sides_ratio ; float internal_left_uv = (float)m_arrow_texture.metadata.left * inv_tex_width; float internal_right_uv = 1.0f - (float)m_arrow_texture.metadata.right * inv_tex_width; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 5617b5ed8..6d9a03977 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -132,8 +132,7 @@ bool GLGizmosManager::init_arrow(const BackgroundTexture::Metadata& arrow_textur bool res = false; if (!arrow_texture.filename.empty()) - res = m_arrow_texture.texture.load_from_file(path + arrow_texture.filename, false, GLTexture::SingleThreaded, false); -// res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, true, false, 100); + res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, false, false, 1000); if (res) m_arrow_texture.metadata = arrow_texture; @@ -1019,7 +1018,7 @@ void GLGizmosManager::render_arrow(const GLCanvas3D& parent, EType highlighted_t float arrow_sides_ratio = (float)m_arrow_texture.texture.get_height() / (float)m_arrow_texture.texture.get_width(); - GLTexture::render_sub_texture(tex_id, zoomed_top_x + zoomed_icons_size * 1.2f, zoomed_top_x + zoomed_icons_size * 1.2f + zoomed_icons_size * arrow_sides_ratio, zoomed_top_y - zoomed_icons_size, zoomed_top_y, { { internal_left_uv, internal_bottom_uv }, { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, internal_bottom_uv } }); + GLTexture::render_sub_texture(tex_id, zoomed_top_x + zoomed_icons_size * 1.2f, zoomed_top_x + zoomed_icons_size * 1.2f + zoomed_icons_size * 2.2f * arrow_sides_ratio, zoomed_top_y - zoomed_icons_size * 1.6f , zoomed_top_y + zoomed_icons_size * 0.6f, { { internal_left_uv, internal_bottom_uv }, { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, internal_bottom_uv } }); break; } zoomed_top_y -= zoomed_stride_y; From 673a2bdac8e9874f3fbec15d2568b5cf69018c45 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Wed, 25 Aug 2021 18:25:37 +0200 Subject: [PATCH 23/23] Fix: ../src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp:109:23: warning: comparison of integer expressions of different signedness: 'std::basic_string::size_type' {aka 'long unsigned int'} and 'int' [-Wsign-compare] ../src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp:132:17: warning: format string is not a string literal (potentially insecure) [-Wformat-security] ../src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp:171:17: warning: format string is not a string literal (potentially insecure) [-Wformat-security] Severity Code Description Project File Line Suppression State Warning C26451 Arithmetic overflow: Using operator '-' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator '-' to avoid overflow (io.2). libslic3r_gui C:\GIT\slic3r\src\slic3r\GUI\Gizmos\GLGizmoSimplify.cpp 143 --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 22 +++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 3530b4240..5ce0064db 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -129,7 +129,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi } ImGui::SameLine(); m_imgui->disabled_begin(m_configuration.use_count); - ImGui::Text(tr_detail_level.c_str()); + ImGui::Text("%s", tr_detail_level.c_str()); std::vector reduce_captions = { static_cast(_u8L("Extra high")), static_cast(_u8L("High")), @@ -139,17 +139,17 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi }; ImGui::SameLine(m_gui_cfg->bottom_left_width); ImGui::SetNextItemWidth(m_gui_cfg->input_width); - static int reduction = 3; - if(ImGui::SliderInt("##ReductionLevel", &reduction, 1, 5, reduce_captions[reduction-1].c_str())) { + static int reduction = 2; + if(ImGui::SliderInt("##ReductionLevel", &reduction, 0, 4, reduce_captions[reduction].c_str())) { m_is_valid_result = false; - if (reduction < 1) reduction = 1; - if (reduction > 5) reduction = 5; + if (reduction < 0) reduction = 0; + if (reduction > 4) reduction = 4; switch (reduction) { - case 1: m_configuration.max_error = 1e-3f; break; - case 2: m_configuration.max_error = 1e-2f; break; - case 3: m_configuration.max_error = 0.1f; break; - case 4: m_configuration.max_error = 0.5f; break; - case 5: m_configuration.max_error = 1.f; break; + case 0: m_configuration.max_error = 1e-3f; break; + case 1: m_configuration.max_error = 1e-2f; break; + case 2: m_configuration.max_error = 0.1f; break; + case 3: m_configuration.max_error = 0.5f; break; + case 4: m_configuration.max_error = 1.f; break; } } m_imgui->disabled_end(); // !use_count @@ -168,7 +168,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi } m_imgui->disabled_begin(!m_configuration.use_count); - ImGui::Text(tr_decimate_ratio.c_str()); + ImGui::Text("%s", tr_decimate_ratio.c_str()); ImGui::SameLine(m_gui_cfg->bottom_left_width); ImGui::SetNextItemWidth(m_gui_cfg->input_width); const char * format = (m_configuration.decimate_ratio > 10)? "%.0f %%": diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index 9af6686bd..04d4b99ab 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -87,7 +87,7 @@ private: int window_padding = 0; // trunc model name when longer - int max_char_in_name = 30; + size_t max_char_in_name = 30; }; std::optional m_gui_cfg;