// #define CGAL_PMP_STITCHING_DEBUG_PP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace PMP = CGAL::Polygon_mesh_processing; namespace params = CGAL::parameters; //////////////////////////////////////////////////////////////////////////////////////////////////// template void test_stitch_boundary_cycles(const std::string fname, const std::size_t expected_n) { typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; std::cout << "Testing stitch_boundary_cycles(); file: " << fname << "..." << std::flush; std::ifstream input(fname); Mesh mesh; if (!input || !(input >> mesh)){ std::cerr << "Error: can not read file."; return; } // stitching a single cycle should work from any border halfedge for(halfedge_descriptor h : halfedges(mesh)) { if(!is_border(h, mesh)) continue; std::unordered_map h2h; Mesh mesh_cpy; CGAL::copy_face_graph(mesh, mesh_cpy, CGAL::parameters::halfedge_to_halfedge_output_iterator(std::inserter(h2h, h2h.end()))); assert(is_border(h2h.at(h), mesh_cpy)); PMP::stitch_boundary_cycle(h2h.at(h), mesh_cpy); assert(is_valid_polygon_mesh(mesh_cpy)); } std::size_t res = PMP::stitch_boundary_cycles(mesh); std::cout << "res: " << res << " (expected: " << expected_n << ")" << std::endl; assert(res == expected_n); assert(is_valid_polygon_mesh(mesh)); // Just to test the API PMP::stitch_boundary_cycles(mesh, params::vertex_point_map(get(CGAL::vertex_point, mesh))); } template void test_stitch_boundary_cycles() { test_stitch_boundary_cycles("data_stitching/boundary_cycle.off", 4); test_stitch_boundary_cycles("data_stitching/boundary_cycle_2.off", 2); test_stitch_boundary_cycles("data_stitching/complex_hole.off", 3); test_stitch_boundary_cycles("data_stitching/folded_cycle.off", 2); } //////////////////////////////////////////////////////////////////////////////////////////////////// template typename boost::graph_traits::halfedge_descriptor get_border_halfedge(const int edge_id, const Mesh& mesh) { assert(edge_id < static_cast(num_edges(mesh))); // id is of the edge because it's easier to typename boost::graph_traits::edge_iterator eit = edges(mesh).begin(); std::advance(eit, edge_id); typename boost::graph_traits::halfedge_descriptor h = halfedge(*eit, mesh); if(!is_border(h, mesh)) h = opposite(h, mesh); assert(is_border(h, mesh)); return h; } template void test_stitch_borders(const std::string fname, const std::size_t expected_n, const bool per_cc = false, std::set unconstrained_edge_ids = { }, // constrained edges must appear in the output std::set cycle_rep_ids = { }) // restrict stitching to cycles containing these edges { std::cout << "Testing stitch_borders(); file: " << fname << "..." << std::flush; std::ifstream input(fname); Mesh mesh; if (!input || !(input >> mesh)) { std::cerr << "Error: can not read file."; return; } // Mark some edges as UNCONSTRAINED, the stitched mesh must not contain these edges typedef typename boost::graph_traits::edge_descriptor edge_descriptor; typedef CGAL::dynamic_edge_property_t Edge_property_tag; typedef typename boost::property_map::type Marked_edges; typedef PMP::internal::Halfedges_keeper_with_marked_edge_priority Keeper; Marked_edges marks = get(Edge_property_tag(), mesh); int eid = 0; for(edge_descriptor e : edges(mesh)) put(marks, e, (unconstrained_edge_ids.count(eid++) == 0)); Keeper kpr(marks, mesh); // Restrict to given cycles typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; std::set cycle_reps; for(const int id : cycle_rep_ids) cycle_reps.insert(get_border_halfedge(id, mesh)); std::size_t res = -1; if(cycle_reps.empty()) { res = PMP::stitch_borders(mesh, params::apply_per_connected_component(per_cc) .halfedges_keeper(kpr)); } else { res = PMP::stitch_borders(cycle_reps, mesh, params::apply_per_connected_component(per_cc) .halfedges_keeper(kpr)); } std::cout << "res: " << res << " (expected: " << expected_n << ")" << std::endl; for(edge_descriptor e : edges(mesh)) { assert(get(marks, e)); // must not be marked as unconstrained } assert(res == expected_n); assert(is_valid_polygon_mesh(mesh)); // Just to test the API Mesh dummy_mesh; std::deque empty_deque; PMP::stitch_borders(empty_deque, dummy_mesh); PMP::stitch_borders(dummy_mesh, params::apply_per_connected_component(true)); PMP::stitch_borders(dummy_mesh); std::deque> empty_deque_pair; PMP::stitch_borders(dummy_mesh, empty_deque_pair); } template void test_stitch_borders() { test_stitch_borders("data_stitching/complex_hole.off", 3, false, {}, {83}); test_stitch_borders("data_stitching/pinched.off", 2, false, {130, 94}); test_stitch_borders("data_stitching/pinched.off", 2, false, {130, 94}, {94}); test_stitch_borders("data_stitching/pinched.off", 0, false, {}, {140}); // outer border, nothing to stitch test_stitch_borders("data_stitching/full_border.off", 4); test_stitch_borders(CGAL::data_file_path("meshes/quads_to_stitch.off"), 4); test_stitch_borders("data_stitching/half_border.off", 2, false, {23, 15}); test_stitch_borders("data_stitching/incidence_3.off", 3); test_stitch_borders("data_stitching/incoherent_patch_orientation.off", 1); test_stitch_borders("data_stitching/mid_border.off", 2); test_stitch_borders("data_stitching/multiple_incidence.off", 10, false, {3, 5, 16, 24, 31, 45, 53, 65}); test_stitch_borders("data_stitching/non_stitchable.off", 0); test_stitch_borders("data_stitching/non_manifold.off", 0); test_stitch_borders("data_stitching/non_manifold2.off", 0); test_stitch_borders("data_stitching/two_patches.off", 3); test_stitch_borders("data_stitching/nm_cubes.off", 4, true /*per cc*/); } template void test_local_stitch_borders() { test_stitch_borders("data_stitching/pinched.off", 2, false, {130, 94}); } //////////////////////////////////////////////////////////////////////////////////////////////////// template void test_degenerate() { std::cout << "Testing degenerate cases" << std::endl; typedef typename boost::property_traits< typename CGAL::GetVertexPointMap::const_type>::value_type Point; Mesh tm; CGAL::make_triangle(Point(0,0,0), Point(1,0,0), Point(0,1,0), tm); CGAL::make_triangle(Point(0,0,0), Point(1,0,0), Point(0,1,0), tm); CGAL::make_triangle(Point(0,0,0), Point(1,0,0), Point(0,1,0), tm); CGAL::make_triangle(Point(0,0,0), Point(1,0,0), Point(0,1,0), tm); std::size_t res = CGAL::Polygon_mesh_processing::stitch_borders(tm); assert(res == 0); } //////////////////////////////////////////////////////////////////////////////////////////////////// template void test() { test_stitch_boundary_cycles(); test_stitch_borders(); test_degenerate(); } int main() { typedef CGAL::Exact_predicates_inexact_constructions_kernel EPICK; std::cout << "--- Test EPICK SM" << std::endl; test >(); std::cout << "--- Test EPICK Polyhedron" << std::endl; test >(); // Disabled because it takes too long for some test plateforms // typedef CGAL::Exact_predicates_exact_constructions_kernel EPECK; // std::cout << "--- Test EPECK SM" << std::endl; // test >(); // std::cout << "--- Test EPECK Polyhedron" << std::endl; // test >(); std::cout << "Done!" << std::endl; return EXIT_SUCCESS; }