From 13f4959e5f5db50937acdf02be283837094a5ebb Mon Sep 17 00:00:00 2001 From: Hanif Koh Date: Fri, 25 Sep 2026 17:03:42 +0800 Subject: [PATCH] Test That Slab Slicing Does Not Depend on the Thread Schedule Projects a dense, tilted sphere with slice_mesh_slabs() on one thread and then three times multi-threaded, and requires the polygons to match exactly, vertex order included. Fails without the canonical line sort, passes with it. --- tests/libslic3r/CMakeLists.txt | 1 + tests/libslic3r/test_trianglemesh_slicer.cpp | 50 ++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 tests/libslic3r/test_trianglemesh_slicer.cpp diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index 185dce37da..78149d4589 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -36,6 +36,7 @@ add_executable(${_TEST_NAME}_tests test_stl.cpp test_triangle_selector.cpp test_meshboolean.cpp + test_trianglemesh_slicer.cpp test_marchingsquares.cpp test_lay_on_face.cpp test_model.cpp diff --git a/tests/libslic3r/test_trianglemesh_slicer.cpp b/tests/libslic3r/test_trianglemesh_slicer.cpp new file mode 100644 index 0000000000..1e4e32be30 --- /dev/null +++ b/tests/libslic3r/test_trianglemesh_slicer.cpp @@ -0,0 +1,50 @@ +#include + +#include + +#include "libslic3r/TriangleMesh.hpp" +#include "libslic3r/TriangleMeshSlicer.hpp" + +using namespace Slic3r; + +// The slab slicer collects each slab's intersection lines from a parallel loop over the facets. +// Its loops, and therefore the projected polygons, are derived from the order of those lines, so +// the order has to be canonical or the same mesh projects to different polygons run to run. +// The single-threaded projection is the reference; every multi-threaded run must reproduce it +// exactly, vertex order included. +TEST_CASE("Slab slicing projects the same polygons whatever the thread schedule", "[TriangleMeshSlicer]") +{ + // A dense sphere, tilted so no facet is axis aligned: thousands of upward and downward + // facing facets spread over every slab. + indexed_triangle_set mesh = its_make_sphere(10., 0.05); + Transform3d trafo = Transform3d::Identity(); + trafo.rotate(Eigen::AngleAxisd(0.37, Vec3d(0.3, 0.5, 1.).normalized())); + trafo.translate(Vec3d(1., 2., 0.)); + std::vector zs; + for (float z = -9.7f; z < 9.7f; z += 0.2f) + zs.emplace_back(z); + + auto project = [&mesh, &trafo, &zs]() { + std::vector top, bottom; + slice_mesh_slabs(mesh, zs, trafo, &top, &bottom, nullptr, []{}); + return std::make_pair(std::move(top), std::move(bottom)); + }; + + std::pair, std::vector> reference; + { + tbb::global_control single_thread(tbb::global_control::max_allowed_parallelism, 1); + reference = project(); + } + REQUIRE(reference.first.size() == zs.size()); + REQUIRE(std::any_of(reference.first.begin(), reference.first.end(), [](const Polygons &p) { return !p.empty(); })); + REQUIRE(std::any_of(reference.second.begin(), reference.second.end(), [](const Polygons &p) { return !p.empty(); })); + + for (int run = 0; run < 3; ++run) { + DYNAMIC_SECTION("multi-threaded run " << run) + { + auto parallel = project(); + CHECK(parallel.first == reference.first); + CHECK(parallel.second == reference.second); + } + } +}