diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt index 1248c37ab68..dba765d7f6a 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt @@ -19,6 +19,10 @@ \example Polygon_mesh_processing/refine_fair_example.cpp \example Polygon_mesh_processing/mesh_slicer_example.cpp \example Polygon_mesh_processing/isotropic_remeshing_example.cpp +\example Polygon_mesh_processing/isotropic_remeshing_of_patch_example.cpp +\example Polygon_mesh_processing/isotropic_remeshing_with_allow_move.cpp +\example Polygon_mesh_processing/isotropic_remeshing_with_custom_sizing_example.cpp +\example Polygon_mesh_processing/isotropic_remeshing_with_sizing_example.cpp \example Polygon_mesh_processing/interpolated_corrected_curvatures_SM.cpp \example Polygon_mesh_processing/interpolated_corrected_curvatures_PH.cpp \example Polygon_mesh_processing/interpolated_corrected_curvatures_vertex.cpp @@ -48,5 +52,4 @@ \example Polygon_mesh_processing/remesh_almost_planar_patches.cpp \example Polygon_mesh_processing/sample_example.cpp \example Polygon_mesh_processing/soup_autorefinement.cpp -\example Polygon_mesh_processing/isotropic_remeshing_with_allow_move.cpp */ diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Face_graph_output_builder.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Face_graph_output_builder.h index 5664e2db18e..44c4756c9ac 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Face_graph_output_builder.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Face_graph_output_builder.h @@ -1091,6 +1091,10 @@ public: boost::dynamic_bitset<> coplanar_patches_of_tm2_for_union_and_intersection(nb_patches_tm2,false); patch_status_not_set_tm1.set(); patch_status_not_set_tm2.set(); + // extra containers used when entire connected components are identical (filled only if needed) + std::vector coplanar_tm1_to_coplanar_tm2; + std::vector extreme_vertex_per_cc_1; + std::vector extreme_vertex_per_cc_2; // first set coplanar status of patches using the coplanar faces collected during the // extra intersection edges collected. This is important in the case of full connected components @@ -1847,8 +1851,61 @@ public: { if (coplanar_patches_of_tm1.test(patch_id)) { - if (is_tm1_inside_out == is_tm2_inside_out) + // Two "identical" coplanar patches that are entire connected components + //we have the correspondance between cpln patches thanks to faces in tm1_coplanar_faces and tm2_coplanar_faces + CGAL_assertion(tm1_coplanar_faces.size()==tm2_coplanar_faces.size()); + if (coplanar_tm1_to_coplanar_tm2.empty()) // fill container only once + { + coplanar_tm1_to_coplanar_tm2.resize(nb_patches_tm1, NID); + for (std::size_t i=0; i::null_vertex(); + extreme_vertex_per_cc_1.assign(nb_patches_tm1, null_v); + for (face_descriptor fd : faces(tm1)) + { + std::size_t patch_id = tm1_patch_ids[get(fids1, fd)]; + if (!coplanar_patches_of_tm1.test(patch_id)) continue; + halfedge_descriptor hd=halfedge(fd, tm1); + for (halfedge_descriptor h : CGAL::halfedges_around_face(hd, tm1)) + { + vertex_descriptor vd = target(h, tm1); + if (extreme_vertex_per_cc_1[patch_id]==null_v || get(vpm1,extreme_vertex_per_cc_1[patch_id]) r1 = internal::is_badly_shaped( face(he, mesh_), mesh_, vpmap_, vcmap_, ecmap_, gt_, - cap_threshold, // bound on the angle: above 160 deg => cap 4, // bound on shortest/longest edge above 4 => needle + cap_threshold, // bound on the angle: above 160 deg => cap 0,// collapse length threshold : not needed here 0); // flip triangle height threshold std::array r2 = internal::is_badly_shaped( face(opposite(he, mesh_), mesh_), - mesh_, vpmap_, vcmap_, ecmap_, gt_, cap_threshold, 4, 0, 0); + mesh_, vpmap_, vcmap_, ecmap_, gt_, 4, cap_threshold, 0, 0); const bool badly_shaped = (r1[0] != boost::graph_traits::null_halfedge()//needle || r1[1] != boost::graph_traits::null_halfedge()//cap diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair_degeneracies.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair_degeneracies.h index a21e4333ec0..25096d0c26f 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair_degeneracies.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair_degeneracies.h @@ -51,17 +51,15 @@ namespace Polygon_mesh_processing { namespace internal { template -std::array::halfedge_descriptor, 2> -is_badly_shaped(const typename boost::graph_traits::face_descriptor f, - TriangleMesh& tmesh, - const VPM& vpm, - const VCM& vcm, - const ECM& ecm, - const Traits& gt, - const double cap_threshold, // angle over 160° ==> cap - const double needle_threshold, // longest edge / shortest edge over this ratio ==> needle - const double collapse_length_threshold, // max length of edges allowed to be collapsed - const double flip_triangle_height_threshold_squared) // max height of triangles allowed to be flipped +typename boost::graph_traits::halfedge_descriptor +is_it_a_needle(const typename boost::graph_traits::face_descriptor f, + TriangleMesh& tmesh, + const VPM& vpm, + const VCM& vcm, + const ECM& /* ecm */, //not used because vcm is filled with end points of edges in ecm + const Traits& gt, + const double needle_threshold, // longest edge / shortest edge over this ratio ==> needle + const double collapse_length_threshold) // max length of edges allowed to be collapsed { namespace PMP = CGAL::Polygon_mesh_processing; @@ -78,23 +76,70 @@ is_badly_shaped(const typename boost::graph_traits::face_descripto if(collapse_length_threshold == 0 || edge_length(res, tmesh, parameters::vertex_point_map(vpm).geom_traits(gt)) <= collapse_length_threshold) { - return make_array(res, null_h); + return res; } } - res = PMP::is_cap_triangle_face(f, tmesh, cap_threshold, parameters::vertex_point_map(vpm).geom_traits(gt)); + return null_h; +} + +template +typename boost::graph_traits::halfedge_descriptor +is_it_a_cap(const typename boost::graph_traits::face_descriptor f, + TriangleMesh& tmesh, + const VPM& vpm, + const VCM& /* vcm */, + const ECM& ecm, + const Traits& gt, + const double cap_threshold, // angle over 160° ==> cap + const double flip_triangle_height_threshold_squared) // max height of triangles allowed to be flipped +{ + namespace PMP = CGAL::Polygon_mesh_processing; + + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + + const halfedge_descriptor null_h = boost::graph_traits::null_halfedge(); + + halfedge_descriptor res = + PMP::is_cap_triangle_face(f, tmesh, cap_threshold, parameters::vertex_point_map(vpm).geom_traits(gt)); if( res != null_h && !get(ecm, edge(res, tmesh) ) && (flip_triangle_height_threshold_squared == 0 || typename Traits::Compare_squared_distance_3()( get(vpm, target(next(res,tmesh), tmesh)), typename Traits::Line_3(get(vpm, source(res,tmesh)), get(vpm, target(res,tmesh))), flip_triangle_height_threshold_squared) != LARGER )) { - return make_array(null_h, res); + return res; } - return make_array(null_h, null_h); + return null_h; } +// This function tests both needle-ness and cap-ness +template +std::array::halfedge_descriptor, 2> +is_badly_shaped(const typename boost::graph_traits::face_descriptor f, + TriangleMesh& tmesh, + const VPM& vpm, + const VCM& vcm, + const ECM& ecm, + const Traits& gt, + const double needle_threshold, // longest edge / shortest edge over this ratio ==> needle + const double cap_threshold, // angle over 160° ==> cap + const double collapse_length_threshold, // max length of edges allowed to be collapsed + const double flip_triangle_height_threshold_squared) // max height of triangles allowed to be flipped +{ + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + + const halfedge_descriptor null_h = boost::graph_traits::null_halfedge(); + std::array retval = make_array(null_h, null_h); + + retval[0] = is_it_a_needle(f, tmesh, vpm, vcm, ecm, gt, needle_threshold, collapse_length_threshold); + retval[1] = is_it_a_cap(f, tmesh, vpm, vcm, ecm, gt, cap_threshold, flip_triangle_height_threshold_squared); + + return retval; +} + +// This function tests both needle-ness and cap-ness and fills both ranges template void collect_badly_shaped_triangles(const typename boost::graph_traits::face_descriptor f, @@ -112,11 +157,13 @@ void collect_badly_shaped_triangles(const typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - std::array res = is_badly_shaped(f, tmesh, vpm, vcm, ecm, gt, cap_threshold, - needle_threshold, + const halfedge_descriptor null_h = boost::graph_traits::null_halfedge(); + + std::array res = is_badly_shaped(f, tmesh, vpm, vcm, ecm, gt, + needle_threshold, cap_threshold, collapse_length_threshold, flip_triangle_height_threshold_squared); - if(res[0] != boost::graph_traits::null_halfedge()) + if(res[0] != null_h) { #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA std::cout << "add new needle: " << edge(res[0], tmesh) << std::endl; @@ -125,17 +172,15 @@ void collect_badly_shaped_triangles(const typename boost::graph_traits::null_halfedge()) - { #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA - std::cout << "add new cap: " << edge(res[1],tmesh) << std::endl; + std::cout << "add new cap: " << edge(res[1],tmesh) << std::endl; #endif - CGAL_assertion(!is_border(res[1], tmesh)); - CGAL_assertion(!get(ecm, edge(res[1], tmesh))); - edges_to_flip.insert(res[1]); - } + CGAL_assertion(!is_border(res[1], tmesh)); + CGAL_assertion(!get(ecm, edge(res[1], tmesh))); + edges_to_flip.insert(res[1]); } } @@ -607,6 +652,8 @@ bool remove_almost_degenerate_faces(const FaceRange& face_range, typedef typename boost::graph_traits::edge_descriptor edge_descriptor; typedef typename boost::graph_traits::face_descriptor face_descriptor; + const halfedge_descriptor null_h = boost::graph_traits::null_halfedge(); + typedef Static_boolean_property_map Default_VCM; typedef typename internal_np::Lookup_named_param_def::null_face()) @@ -673,36 +721,38 @@ bool remove_almost_degenerate_faces(const FaceRange& face_range, } } - // Start the process of removing bad elements - std::set edges_to_collapse; - std::set edges_to_flip; + // @todo maybe using a priority queue handling the more almost degenerate elements first should be used + std::unordered_set edges_to_collapse; + std::unordered_set edges_to_flip; - // @todo could probably do something a bit better by looping edges, consider the incident faces - // f1 / f2 and look at f1 if f1 next_edges_to_collapse; - std::set next_edges_to_flip; + std::unordered_set next_edges_to_collapse; // Treat needles =============================================================================== #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA int kk=0; std::ofstream(std::string("tmp/n-00000.off")) << tmesh; #endif + + auto run_cap_check = [&](halfedge_descriptor h, bool consider_for_collapse=true) + { + halfedge_descriptor cap_h = internal::is_it_a_cap(face(h, tmesh), tmesh, vpm, vcm, ecm, gt, + cap_threshold, flip_triangle_height_threshold_squared); + if(cap_h != null_h) + { +#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA + std::cout << "\t\t But the face is a cap" << std::endl; +#endif + edges_to_flip.insert(cap_h); + } + else + { + if (consider_for_collapse) next_edges_to_collapse.insert(h); + } + }; + while(!edges_to_collapse.empty()) { + // note that on the first iteration, 'h' does not indicate a known needle halfedge_descriptor h = *edges_to_collapse.begin(); edges_to_collapse.erase(edges_to_collapse.begin()); + CGAL_assertion(is_valid_halfedge_descriptor(h, tmesh)); + + // Verify that the element is still badly shaped + halfedge_descriptor needle_h = internal::is_it_a_needle(face(h, tmesh), tmesh, vpm, vcm, ecm, gt, + needle_threshold, collapse_length_threshold); + if(needle_h == null_h) + { +#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA + std::cout << "\t Needle criterion not verified" << std::endl; +#endif + run_cap_check(h, false); + continue; + } + else + { + h = needle_h; + } CGAL_assertion(!is_border(h, tmesh)); const edge_descriptor e = edge(h, tmesh); CGAL_assertion(!get(ecm, edge(h, tmesh))); - - if(get(vcm, source(h, tmesh)) && get(vcm, target(h, tmesh))) - continue; + CGAL_assertion(!get(vcm, source(h, tmesh)) && !get(vcm, target(h, tmesh))); #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA std::cout << " treat needle: " << e << " (" << source(e, tmesh) << " " << tmesh.point(source(h, tmesh)) << " --- " << source(e, tmesh) << " " << tmesh.point(target(h, tmesh)) << ")" << std::endl; #endif + if(CGAL::Euler::does_satisfy_link_condition(e, tmesh)) { - // Verify that the element is still badly shaped - const std::array nc = - internal::is_badly_shaped(face(h, tmesh), tmesh, vpm, vcm, ecm, gt, - cap_threshold, needle_threshold, collapse_length_threshold, flip_triangle_height_threshold_squared); - - if(nc[0] != h) - { -#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA - std::cout << "\t Needle criteria no longer verified" << std::endl; -#endif - continue; - } - // pick the orientation of edge to keep the vertex minimizing the volume variation const halfedge_descriptor best_h = internal::get_best_edge_orientation(e, tmesh, vpm, vcm, gt); - if(best_h == boost::graph_traits::null_halfedge()) + if(best_h == null_h) { #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA std::cout << "\t Geometrically invalid edge collapse!" << std::endl; #endif - next_edges_to_collapse.insert(h); + run_cap_check(h); continue; } - if (!accept_change.collapse(edge(best_h, tmesh))) + if(!accept_change.collapse(edge(best_h, tmesh))) { #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA std::cout << "\t edge collapse prevented by the user functor" << std::endl; #endif - next_edges_to_collapse.insert(h); + run_cap_check(h); continue; } @@ -830,7 +900,7 @@ bool remove_almost_degenerate_faces(const FaceRange& face_range, else v = Euler::collapse_edge(edge(best_h, tmesh), tmesh); - // moving to the midpoint is not a good idea. On a circle for example you might endpoint with + // moving to the midpoint is not a good idea. On a circle for example you might end with // a bad geometry because you iteratively move one point // auto mp = midpoint(tmesh.point(source(h, tmesh)), tmesh.point(target(h, tmesh))); // tmesh.point(v) = mp; @@ -840,10 +910,7 @@ bool remove_almost_degenerate_faces(const FaceRange& face_range, { if(!is_border(hv, tmesh)) { - internal::collect_badly_shaped_triangles(face(hv, tmesh), tmesh, vpm, vcm, ecm, gt, - cap_threshold, needle_threshold, - collapse_length_threshold, flip_triangle_height_threshold_squared, - edges_to_collapse, edges_to_flip); + next_edges_to_collapse.insert(hv); // shape will be tested when popped } } @@ -862,24 +929,40 @@ bool remove_almost_degenerate_faces(const FaceRange& face_range, #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA std::cout << "\t Uncollapsable edge!" << std::endl; #endif - next_edges_to_collapse.insert(h); + run_cap_check(h); } } // Treat caps ================================================================================== - CGAL_assertion(next_edges_to_flip.empty()); #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA kk=0; std::ofstream(std::string("tmp/c-000.off")) << tmesh; #endif + while(!edges_to_flip.empty()) { halfedge_descriptor h = *edges_to_flip.begin(); edges_to_flip.erase(edges_to_flip.begin()); - + CGAL_assertion(is_valid_halfedge_descriptor(h, tmesh)); CGAL_assertion(!is_border(h, tmesh)); + // check if the face is still a cap + halfedge_descriptor cap_h = internal::is_it_a_cap(face(h, tmesh), tmesh, vpm, vcm, ecm, gt, + cap_threshold, flip_triangle_height_threshold_squared); + + if(cap_h == null_h) + { +#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA + std::cout << "\t Cap criterion no longer verified" << std::endl; +#endif + continue; + } + else + { + h = cap_h; + } + const edge_descriptor e = edge(h, tmesh); CGAL_assertion(!get(ecm, e)); @@ -889,23 +972,11 @@ bool remove_almost_degenerate_faces(const FaceRange& face_range, << " --- " << target(e, tmesh) << " " << tmesh.point(target(h, tmesh)) << ")" << std::endl; #endif - std::array nc = internal::is_badly_shaped(face(h, tmesh), tmesh, vpm, vcm, ecm, gt, - cap_threshold, needle_threshold, - collapse_length_threshold, flip_triangle_height_threshold_squared); - // Check the triangle is still a cap - if(nc[1] != h) - { -#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA - std::cout << "\t Cap criteria no longer verified" << std::endl; -#endif - continue; - } - // special case of `edge(h, tmesh)` being a border edge --> remove the face if(is_border(opposite(h, tmesh), tmesh)) { - // check a non-manifold vertex won't be created - bool removal_is_nm=false; + // check that a non-manifold vertex won't be created + bool removal_is_nm = false; for(halfedge_descriptor hh : CGAL::halfedges_around_target(next(h, tmesh), tmesh)) { if (is_border(hh, tmesh)) @@ -914,13 +985,13 @@ bool remove_almost_degenerate_faces(const FaceRange& face_range, break; } } - if (removal_is_nm) continue; + + if(removal_is_nm) + continue; for(halfedge_descriptor hh : CGAL::halfedges_around_face(h, tmesh)) { - // Remove from even 'next_edges_to_flip' because it might have been re-added from a flip edges_to_flip.erase(hh); - next_edges_to_flip.erase(hh); next_edges_to_collapse.erase(hh); } @@ -941,15 +1012,16 @@ bool remove_almost_degenerate_faces(const FaceRange& face_range, #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA std::cout << "\t Flipping prevented: not the best diagonal" << std::endl; #endif - next_edges_to_flip.insert(h); + next_edges_to_collapse.insert(h); continue; } - if (!accept_change.flip(h)) + if(!accept_change.flip(h)) { #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA std::cout << "\t Flipping prevented: rejected by user functor" << std::endl; #endif + next_edges_to_collapse.insert(h); continue; } @@ -963,16 +1035,7 @@ bool remove_almost_degenerate_faces(const FaceRange& face_range, for(int i=0; i<2; ++i) { CGAL_assertion(!is_border(h, tmesh)); - std::array nc = - internal::is_badly_shaped(face(h, tmesh), tmesh, vpm, vcm, ecm, gt, - cap_threshold, needle_threshold, - collapse_length_threshold, flip_triangle_height_threshold_squared); - - if(nc[1] != boost::graph_traits::null_halfedge() && nc[1] != h) - next_edges_to_flip.insert(nc[1]); - else if(nc[0] != boost::graph_traits::null_halfedge()) - next_edges_to_collapse.insert(nc[0]); - + next_edges_to_collapse.insert(h); h = opposite(h, tmesh); } @@ -984,7 +1047,7 @@ bool remove_almost_degenerate_faces(const FaceRange& face_range, std::cout << "\t Unflippable edge!" << std::endl; #endif CGAL_assertion(!is_border(h, tmesh)); - next_edges_to_flip.insert(h); + next_edges_to_collapse.insert(h); } #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA @@ -1001,7 +1064,6 @@ bool remove_almost_degenerate_faces(const FaceRange& face_range, return false; std::swap(edges_to_collapse, next_edges_to_collapse); - std::swap(edges_to_flip, next_edges_to_flip); } return false; diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/smooth_mesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/smooth_mesh.h index 0c06d588abc..361593e8aae 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/smooth_mesh.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/smooth_mesh.h @@ -31,7 +31,7 @@ namespace Polygon_mesh_processing { * \ingroup PMPDeprecated * * \deprecated This function is deprecated since \cgal 5.5, -* `CGAL::angle_and_area_smoothing()` should be used instead. +* `CGAL::Polygon_mesh_processing::angle_and_area_smoothing()` should be used instead. */ template CGAL_DEPRECATED void smooth_mesh(const FaceRange& faces, diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt index 0dd21ab22e1..44b52f55548 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt @@ -67,6 +67,7 @@ create_single_source_cgal_program("test_pmp_np_function.cpp") create_single_source_cgal_program("test_degenerate_pmp_clip_split_corefine.cpp") create_single_source_cgal_program("test_isolevel_refinement.cpp") create_single_source_cgal_program("test_corefinement_nm_bo.cpp") +create_single_source_cgal_program("test_corefinement_cavities.cpp") # create_single_source_cgal_program("test_pmp_repair_self_intersections.cpp") find_package(Eigen3 3.2.0 QUIET) #(requires 3.2.0 or greater) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefinement_cavities.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefinement_cavities.cpp new file mode 100644 index 00000000000..ad5bfe39675 --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefinement_cavities.cpp @@ -0,0 +1,123 @@ +#include +#include +#include + +template +void make_hexa(double x, double y, double z, + double X, double Y, double Z, + Mesh& mesh, int t) +{ + CGAL::make_hexahedron( + Point(x,y,Z), + Point(X,y,Z), + Point(X,y,z), + Point(x,y,z), + Point(x,Y,z), + Point(x,Y,Z), + Point(X,Y,Z), + Point(X,Y,z), + mesh); + + using face_descriptor = typename boost::graph_traits::face_descriptor; + using halfedge_descriptor = typename boost::graph_traits::halfedge_descriptor; + + std::vector fcs(faces(mesh).begin(), faces(mesh).end()); + for (face_descriptor f : fcs) + { + halfedge_descriptor h = halfedge(f, mesh); + if (t==1) h=next(h,mesh); + halfedge_descriptor h2=next(next(h, mesh), mesh); + CGAL::Euler::split_face(h, h2, mesh); + } +} + +using K = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_3 = K::Point_3; +using Mesh = CGAL::Surface_mesh; + +namespace PMP = CGAL::Polygon_mesh_processing; + + +void test_operations(Mesh A, Mesh B, + bool reverse_A, bool reverse_B, + std::string round, + std::size_t union_v, std::size_t inter_v, std::size_t diff1_v, std::size_t diff2_v) +{ +#ifndef VERBOSE + CGAL_USE(round); +#endif + if (reverse_A) PMP::reverse_face_orientations(A); + if (reverse_B) PMP::reverse_face_orientations(B); + + Mesh out_union, out_inter, out_diff1, out_diff2; + std::array, 4> output; + output[PMP::Corefinement::UNION] = &out_union; + output[PMP::Corefinement::INTERSECTION] = &out_inter; + output[PMP::Corefinement::TM1_MINUS_TM2] = &out_diff1; + output[PMP::Corefinement::TM2_MINUS_TM1] = &out_diff2; + + Mesh lA=A, lB=B; + PMP::corefine_and_compute_boolean_operations(lA,lB,output); +#ifdef VERBOSE + std::ofstream("out_union_"+round+".off") << out_union; + std::ofstream("out_inter_"+round+".off") << out_inter; + std::ofstream("out_diff1_"+round+".off") << out_diff1; + std::ofstream("out_diff2_"+round+".off") << out_diff2; +#endif + assert(vertices(out_union).size()==union_v); + assert(vertices(out_inter).size()==inter_v); + assert(vertices(out_diff1).size()==diff1_v); + assert(vertices(out_diff2).size()==diff2_v); +} + +int main() +{ + + Mesh A, mh, B; + make_hexa(0, 0, 0, + 4, 4, 4, + A, 0); + make_hexa(1, 1, 1, + 2, 2, 2, + mh, 0); + make_hexa(1, 1, 1, + 2, 2, 2, + B, 1); + + Mesh A2, mh2, B2; + make_hexa(5, 0, 0, + 9, 4, 4, + A2, 0); + make_hexa(6, 1, 1, + 7, 2, 2, + mh2, 0); + make_hexa(6, 1, 1, + 7, 2, 2, + B2, 1); + + A.join(A2); + mh.join(mh2); + PMP::reverse_face_orientations(mh); + A.join(mh); + B.join(B2); + +#ifdef VERBOSE + std::ofstream("A.off") << A; + std::ofstream("B.off") << B; +#endif + + test_operations(A, B, false, false, "r00", 16, 0, 44, 28); + test_operations(A, B, false, true, "r01", 28, 44, 0, 16); + test_operations(A, B, true, false, "r10", 44, 28, 16, 0); + test_operations(A, B, true, true, "r11", 0, 16, 28, 44); + + test_operations(A, A, false, false, "a00", 32, 32, 0, 0); + test_operations(A, A, false, true, "a01", 0, 0, 32, 32); + test_operations(A, A, true, false, "a10", 0, 0, 32, 32); + test_operations(A, A, true, true, "a11", 32, 32, 0, 0); + + test_operations(B, B, false, false, "b00", 16, 16, 0, 0); + test_operations(B, B, false, true, "b01", 0, 0, 16, 16); + test_operations(B, B, true, false, "b10", 0, 0, 16, 16); + test_operations(B, B, true, true, "b11", 16, 16, 0, 0); +} diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_remove_caps_needles.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_remove_caps_needles.cpp index 505a731ed89..0b90e28fcfa 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_remove_caps_needles.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_remove_caps_needles.cpp @@ -29,7 +29,7 @@ void general_test(std::string filename) std::ifstream input(filename); Mesh mesh; - if (!input || !(input >> mesh) || !CGAL::is_triangle_mesh(mesh)) { + if (!CGAL::IO::read_polygon_mesh(filename, mesh) || !CGAL::is_triangle_mesh(mesh)) { std::cerr << "Not a valid input file." << std::endl; exit(EXIT_FAILURE); } @@ -57,7 +57,8 @@ void test_with_envelope(std::string filename, double eps) std::ifstream input(filename); Mesh mesh, bk; - if (!input || !(input >> mesh) || !CGAL::is_triangle_mesh(mesh)) { + if (!CGAL::IO::read_polygon_mesh(filename, mesh) || !CGAL::is_triangle_mesh(mesh)) + { std::cerr << "Not a valid input file." << std::endl; exit(EXIT_FAILURE); } @@ -132,7 +133,8 @@ void test_parameters_on_pig(std::string filename) std::ifstream input(filename); Mesh mesh, bk; - if (!input || !(input >> mesh) || !CGAL::is_triangle_mesh(mesh)) { + if (!CGAL::IO::read_polygon_mesh(filename, mesh) || !CGAL::is_triangle_mesh(mesh)) + { std::cerr << "Not a valid input file." << std::endl; exit(EXIT_FAILURE); } @@ -165,13 +167,11 @@ void test_parameters_on_pig(std::string filename) int main(int argc, char** argv) { const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/pig.off"); + double eps = (argc > 2) ? atof(argv[2]) : 0.01; general_test(filename); - if (argc==2) - test_with_envelope(filename, 0.01); - else - if (argc==3) - test_with_envelope(filename, atof(argv[2])); + + test_with_envelope(filename, eps); // only run that test with pig.off if (argc==1)