diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index a78720807..ed7d8a92b 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -26,6 +26,7 @@ add_executable(${_TEST_NAME}_tests test_voronoi.cpp test_optimizers.cpp test_png_io.cpp + test_surface_mesh.cpp test_timeutils.cpp test_indexed_triangle_set.cpp test_astar.cpp diff --git a/tests/libslic3r/test_surface_mesh.cpp b/tests/libslic3r/test_surface_mesh.cpp new file mode 100644 index 000000000..34ff35667 --- /dev/null +++ b/tests/libslic3r/test_surface_mesh.cpp @@ -0,0 +1,122 @@ +#include +#include + + +#include + +using namespace Slic3r; + + +// Generate a broken cube mesh. Face 8 is inverted, face 11 is missing. +indexed_triangle_set its_make_cube_broken(double xd, double yd, double zd) +{ + auto x = float(xd), y = float(yd), z = float(zd); + return { + { {0, 1, 2}, {0, 2, 3}, {4, 5, 6}, {4, 6, 7}, + {0, 4, 7}, {0, 7, 1}, {1, 7, 6}, {1, 6, 2}, + {2, 5, 6}, {2, 5, 3}, {4, 0, 3} /*missing face*/ }, + { {x, y, 0}, {x, 0, 0}, {0, 0, 0}, {0, y, 0}, + {x, y, z}, {0, y, z}, {0, 0, z}, {x, 0, z} } + }; +} + + + +TEST_CASE("SurfaceMesh on a cube", "[SurfaceMesh]") { + indexed_triangle_set cube = its_make_cube(1., 1., 1.); + SurfaceMesh sm(cube); + const Halfedge_index hi_first = sm.halfedge(Face_index(0)); + Halfedge_index hi = hi_first; + + REQUIRE(! hi_first.is_invalid()); + + SECTION("next / prev halfedge") { + hi = sm.next(hi); + REQUIRE(hi != hi_first); + hi = sm.next(hi); + hi = sm.next(hi); + REQUIRE(hi == hi_first); + hi = sm.prev(hi); + REQUIRE(hi != hi_first); + hi = sm.prev(hi); + hi = sm.prev(hi); + REQUIRE(hi == hi_first); + } + + SECTION("next_around_target") { + // Check that we get to the same halfedge after applying next_around_target + // four times. + const Vertex_index target_vert = sm.target(hi_first); + for (int i=0; i<4;++i) { + hi = sm.next_around_target(hi); + REQUIRE((hi == hi_first) == (i == 3)); + REQUIRE(sm.is_same_vertex(sm.target(hi), target_vert)); + REQUIRE(! sm.is_border(hi)); + } + } + + SECTION("iterate around target and source") { + hi = sm.next_around_target(hi); + hi = sm.prev_around_target(hi); + hi = sm.prev_around_source(hi); + hi = sm.next_around_source(hi); + REQUIRE(hi == hi_first); + } + + SECTION("opposite") { + const Vertex_index target = sm.target(hi); + const Vertex_index source = sm.source(hi); + hi = sm.opposite(hi); + REQUIRE(sm.is_same_vertex(target, sm.source(hi))); + REQUIRE(sm.is_same_vertex(source, sm.target(hi))); + hi = sm.opposite(hi); + REQUIRE(hi == hi_first); + } + + SECTION("halfedges walk") { + for (int i=0; i<4; ++i) { + hi = sm.next(hi); + hi = sm.opposite(hi); + } + REQUIRE(hi == hi_first); + } + + SECTION("point accessor") { + Halfedge_index hi = sm.halfedge(Face_index(0)); + hi = sm.opposite(hi); + hi = sm.prev(hi); + hi = sm.opposite(hi); + REQUIRE(hi.face() == Face_index(6)); + REQUIRE(sm.point(sm.target(hi)).isApprox(cube.vertices[7])); + } +} + + + + +TEST_CASE("SurfaceMesh on a broken cube", "[SurfaceMesh]") { + indexed_triangle_set cube = its_make_cube_broken(1., 1., 1.); + SurfaceMesh sm(cube); + + SECTION("Check inverted face") { + Halfedge_index hi = sm.halfedge(Face_index(8)); + for (int i=0; i<3; ++i) { + REQUIRE(! hi.is_invalid()); + REQUIRE(sm.is_border(hi)); + } + REQUIRE(hi == sm.halfedge(Face_index(8))); + hi = sm.opposite(hi); + REQUIRE(hi.is_invalid()); + } + + SECTION("missing face") { + Halfedge_index hi = sm.halfedge(Face_index(0)); + for (int i=0; i<3; ++i) + hi = sm.next_around_source(hi); + hi = sm.next(hi); + REQUIRE(sm.is_border(hi)); + REQUIRE(! hi.is_invalid()); + hi = sm.opposite(hi); + REQUIRE(hi.is_invalid()); + } +}