diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp index da409ff58..e17fbe9c8 100644 --- a/src/libslic3r/QuadricEdgeCollapse.cpp +++ b/src/libslic3r/QuadricEdgeCollapse.cpp @@ -126,21 +126,29 @@ bool check_new_vertex(const Vec3f& nv, const Vec3f& v0, const Vec3f& v1) { #endif // NDEBUG -void Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, - uint32_t triangle_count, - float * max_error) +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) { + // constants --> may be move to config + const int status_init_size = 10; // in percents + const int check_cancel_period = 16; // how many edge to reduce before call throw_on_cancel + + // check input + if (triangle_count >= its.indices.size()) return; + float maximal_error = (max_error == nullptr)? std::numeric_limits::max() : *max_error; + if (maximal_error <= 0.f) return; + 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); - - float max_float = std::numeric_limits::max(); - float last_collapsed_error = 0.f; - if (max_error == nullptr) { - max_error = &max_float; - } + throw_on_cancel(); + statusfn(status_init_size); // convert from triangle index to mutable priority queue index std::vector ti_2_mpqi(its.indices.size(), {0}); @@ -159,10 +167,26 @@ void Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, changed_triangle_indices.reserve(2 * max_triangle_count_for_one_vertex); uint32_t actual_triangle_count = its.indices.size(); + uint32_t count_triangle_to_reduce = actual_triangle_count - triangle_count; + auto increase_status = [&]() { + double reduced = (actual_triangle_count - triangle_count) / + (double) count_triangle_to_reduce; + double status = (100 - status_init_size) * (1. - reduced); + statusfn(static_cast(std::round(status))); + }; + // modulo for update status + uint32_t status_mod = std::max(uint32_t(16), count_triangle_to_reduce / 100); + + uint32_t iteration_number = 0; + float last_collapsed_error = 0.f; while (actual_triangle_count > triangle_count && !mpq.empty()) { + ++iteration_number; + if (iteration_number % status_mod == 0) increase_status(); + if (iteration_number % check_cancel_period == 0) throw_on_cancel(); + // triangle index 0 Error e = mpq.top(); // copy - if (e.value >= *max_error) break; // Too big error + if (e.value >= maximal_error) break; // Too big error mpq.pop(); uint32_t ti0 = e.triangle_index; TriangleInfo &t_info0 = t_infos[ti0]; @@ -258,7 +282,7 @@ void Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its, // compact triangle compact(v_infos, t_infos, e_infos, its); - *max_error = last_collapsed_error; + if (max_error != nullptr) *max_error = last_collapsed_error; } Vec3f QuadricEdgeCollapse::create_normal(const Triangle &triangle, diff --git a/src/libslic3r/QuadricEdgeCollapse.hpp b/src/libslic3r/QuadricEdgeCollapse.hpp index 422227d28..3fcf61a08 100644 --- a/src/libslic3r/QuadricEdgeCollapse.hpp +++ b/src/libslic3r/QuadricEdgeCollapse.hpp @@ -3,6 +3,7 @@ // inspiration: https://github.com/sp4cerat/Fast-Quadric-Mesh-Simplification #include +#include #include "TriangleMesh.hpp" namespace Slic3r { @@ -14,10 +15,14 @@ namespace Slic3r { /// Wanted triangle count. /// Maximal Quadric for reduce. /// When nullptr then max float is used -/// Output: Last used ErrorValue to collapse edge -/// -void its_quadric_edge_collapse(indexed_triangle_set &its, - uint32_t triangle_count = 0, - float * max_error = nullptr); +/// Output: Last used ErrorValue to collapse edge +/// Could stop process of calculation. +/// Give a feed back to user about progress. +void its_quadric_edge_collapse( + indexed_triangle_set & its, + uint32_t triangle_count = 0, + float * max_error = nullptr, + std::function throw_on_cancel = nullptr, + std::function statusfn = nullptr); } // namespace Slic3r diff --git a/tests/libslic3r/test_indexed_triangle_set.cpp b/tests/libslic3r/test_indexed_triangle_set.cpp index 094c17fd6..087636edf 100644 --- a/tests/libslic3r/test_indexed_triangle_set.cpp +++ b/tests/libslic3r/test_indexed_triangle_set.cpp @@ -167,8 +167,8 @@ std::vector its_sample_surface(const indexed_triangle_set &its, // return Average abs distance to original float compare(const indexed_triangle_set &original, - const indexed_triangle_set &simplified, - double sample_per_mm2) + const indexed_triangle_set &simplified, + double sample_per_mm2) { // create ABBTree auto tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set( @@ -203,8 +203,8 @@ TEST_CASE("Reduce one edge by Quadric Edge Collapse", "[its]") indexed_triangle_set its_ = its; // copy // its_write_obj(its, "tetrhedron_in.obj"); - size_t wanted_count = its.indices.size() - 1; - CHECK(its_quadric_edge_collapse(its, wanted_count)); + uint32_t wanted_count = its.indices.size() - 1; + its_quadric_edge_collapse(its, wanted_count); // its_write_obj(its, "tetrhedron_out.obj"); CHECK(its.indices.size() == 4); CHECK(its.vertices.size() == 4); @@ -235,11 +235,12 @@ TEST_CASE("Symplify mesh by Quadric edge collapse to 5%", "[its]") { TriangleMesh mesh = load_model("frog_legs.obj"); double original_volume = its_volume(mesh.its); - size_t wanted_count = mesh.its.indices.size() * 0.05; + uint32_t wanted_count = mesh.its.indices.size() * 0.05; REQUIRE_FALSE(mesh.empty()); indexed_triangle_set its = mesh.its; // copy - its_quadric_edge_collapse(its, wanted_count); // its_write_obj(its, "frog_legs_qec.obj"); + float max_error = std::numeric_limits::max(); + its_quadric_edge_collapse(its, wanted_count, &max_error); CHECK(its.indices.size() <= wanted_count); double volume = its_volume(its); CHECK(fabs(original_volume - volume) < 30.);