diff --git a/src/libslic3r/TextureBake/TextureBakeDecimate.cpp b/src/libslic3r/TextureBake/TextureBakeDecimate.cpp index ccb6db811c..ee335cff3c 100644 --- a/src/libslic3r/TextureBake/TextureBakeDecimate.cpp +++ b/src/libslic3r/TextureBake/TextureBakeDecimate.cpp @@ -457,6 +457,16 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h const HeapEntry top = heap_pop(); ++pops; + if (on_progress) { + // Every 16 k pops as well as whenever the fraction moves: this is the only place a cancel + // is seen, and past the target the fraction stops moving. + const double p = std::min(1.0, double(init_faces - active_faces) / double(to_remove)); + if (p - last_progress > 0.005 || (pops & 0x3fff) == 0) { + last_progress = p; + if (!on_progress(p)) + break; + } + } // The popped entry is the cheapest left, so exceeding the tolerance ends the run. if (reached_target && top.cost > harvest_ceil) break; @@ -527,15 +537,6 @@ DecimateResult decimate(const TriSoup &geometry, size_t target_triangles, bool h } } maybe_compact(); - - if (on_progress) { - const double p = std::min(1.0, double(init_faces - active_faces) / double(to_remove)); - if (p - last_progress > 0.005) { - last_progress = p; - if (!on_progress(p)) - break; - } - } } BOOST_LOG_TRIVIAL(info) << "TextureBake decimate: pops=" << pops << " stale=" << stale_pops diff --git a/tests/libslic3r/test_texture_displacement.cpp b/tests/libslic3r/test_texture_displacement.cpp index b49c9ccfa4..95384dae46 100644 --- a/tests/libslic3r/test_texture_displacement.cpp +++ b/tests/libslic3r/test_texture_displacement.cpp @@ -2032,3 +2032,25 @@ TEST_CASE("A budget met only by flattening curvature removes detail", "[TextureD CHECK(res.geometry.triangle_count() <= 100); CHECK(res.target_cost_detail); } + +TEST_CASE("Cancelling while flat faces are harvested past the budget stops the decimation", "[TextureDisplacement]") +{ + // 12 * 4^6 = 49152 triangles: the budget is met almost at once, and the harvest after it runs to + // a dozen triangles over several times the two polling intervals a cancel takes to be seen. + const TextureBake::TriSoup cube = TextureBake::to_soup(subdivide_mesh_uniform(its_make_cube(20., 20., 20.), 0.f, 6)); + const size_t target = cube.triangle_count() - 1000; + const size_t full = TextureBake::decimate(cube, target, true, 0.005).geometry.triangle_count(); + + // The Cancel button pressed once the bar is full: every call after the first to report 1.0 cancels. + bool full_seen = false; + const auto cancel_when_full = [&full_seen](double f) { + if (full_seen) + return false; + full_seen = f >= 1.0; + return true; + }; + const size_t canceled = TextureBake::decimate(cube, target, true, 0.005, {}, cancel_when_full).geometry.triangle_count(); + CHECK(full_seen); + CHECK(canceled <= target); + CHECK(canceled > full); +}