Merge remote-tracking branch 'cgal/6.0.x-branch' into HEAD

This commit is contained in:
Sébastien Loriot 2024-12-23 09:40:20 +01:00
commit f5f5dc7244
8 changed files with 363 additions and 117 deletions

View File

@ -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
*/

View File

@ -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<std::size_t> coplanar_tm1_to_coplanar_tm2;
std::vector<vertex_descriptor> extreme_vertex_per_cc_1;
std::vector<vertex_descriptor> 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<tm1_coplanar_faces.size(); ++i)
{
std::size_t pid1 = tm1_patch_ids[get(fids1, tm1_coplanar_faces[i])];
std::size_t pid2 = tm2_patch_ids[get(fids2, tm2_coplanar_faces[i])];
coplanar_tm1_to_coplanar_tm2[pid1]=pid2;
}
const vertex_descriptor null_v = boost::graph_traits<TriangleMesh>::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])<get(vpm1,vd))
extreme_vertex_per_cc_1[patch_id]=vd;
}
}
extreme_vertex_per_cc_2.assign(nb_patches_tm2, null_v);
for (face_descriptor fd : faces(tm2))
{
std::size_t patch_id = tm2_patch_ids[get(fids2, fd)];
if (!coplanar_patches_of_tm2.test(patch_id)) continue;
halfedge_descriptor hd=halfedge(fd, tm2);
for (halfedge_descriptor h : CGAL::halfedges_around_face(hd, tm2))
{
vertex_descriptor vd = target(h, tm2);
if (extreme_vertex_per_cc_2[patch_id]==null_v || get(vpm2,extreme_vertex_per_cc_2[patch_id])<get(vpm2,vd))
extreme_vertex_per_cc_2[patch_id]=vd;
}
}
}
const std::size_t patch_id2=coplanar_tm1_to_coplanar_tm2[patch_id];
CGAL_assertion(patch_id2!=NID);
bool is_oo_tm1 = ::CGAL::Polygon_mesh_processing::internal::is_outward_oriented(extreme_vertex_per_cc_1[patch_id], tm1, parameters::vertex_point_map(vpm1)),
is_oo_tm2 = ::CGAL::Polygon_mesh_processing::internal::is_outward_oriented(extreme_vertex_per_cc_2[patch_id2], tm2, parameters::vertex_point_map(vpm2));
if (is_oo_tm1==is_oo_tm2)
{
coplanar_patches_of_tm1_for_union_and_intersection.set(patch_id);
coplanar_patches_of_tm2_for_union_and_intersection.set(patch_id2);
patch_status_not_set_tm2.reset( patch_id2 );
}
}
else
{

View File

@ -879,14 +879,14 @@ namespace internal {
std::array<halfedge_descriptor, 2> 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<halfedge_descriptor, 2> 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<PolygonMesh>::null_halfedge()//needle
|| r1[1] != boost::graph_traits<PolygonMesh>::null_halfedge()//cap

View File

@ -51,17 +51,15 @@ namespace Polygon_mesh_processing {
namespace internal {
template <typename TriangleMesh, typename VPM, typename VCM, typename ECM, typename Traits>
std::array<typename boost::graph_traits<TriangleMesh>::halfedge_descriptor, 2>
is_badly_shaped(const typename boost::graph_traits<TriangleMesh>::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<TriangleMesh>::halfedge_descriptor
is_it_a_needle(const typename boost::graph_traits<TriangleMesh>::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<TriangleMesh>::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 TriangleMesh, typename VPM, typename VCM, typename ECM, typename Traits>
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor
is_it_a_cap(const typename boost::graph_traits<TriangleMesh>::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<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
const halfedge_descriptor null_h = boost::graph_traits<TriangleMesh>::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 <typename TriangleMesh, typename VPM, typename VCM, typename ECM, typename Traits>
std::array<typename boost::graph_traits<TriangleMesh>::halfedge_descriptor, 2>
is_badly_shaped(const typename boost::graph_traits<TriangleMesh>::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<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
const halfedge_descriptor null_h = boost::graph_traits<TriangleMesh>::null_halfedge();
std::array<halfedge_descriptor,2> 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 <typename TriangleMesh, typename HalfedgeContainer,
typename VPM, typename VCM, typename ECM, typename Traits>
void collect_badly_shaped_triangles(const typename boost::graph_traits<TriangleMesh>::face_descriptor f,
@ -112,11 +157,13 @@ void collect_badly_shaped_triangles(const typename boost::graph_traits<TriangleM
{
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
std::array<halfedge_descriptor, 2> res = is_badly_shaped(f, tmesh, vpm, vcm, ecm, gt, cap_threshold,
needle_threshold,
const halfedge_descriptor null_h = boost::graph_traits<TriangleMesh>::null_halfedge();
std::array<halfedge_descriptor, 2> 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<TriangleMesh>::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<TriangleM
CGAL_assertion(!get(ecm, edge(res[0], tmesh)));
edges_to_collapse.insert(res[0]);
}
else // let's not make it possible to have a face be both a cap and a needle (for now)
if(res[1] != null_h)
{
if(res[1] != boost::graph_traits<TriangleMesh>::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<TriangleMesh>::edge_descriptor edge_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::face_descriptor face_descriptor;
const halfedge_descriptor null_h = boost::graph_traits<TriangleMesh>::null_halfedge();
typedef Static_boolean_property_map<vertex_descriptor, false> Default_VCM;
typedef typename internal_np::Lookup_named_param_def<internal_np::vertex_is_constrained_t,
NamedParameters,
@ -654,6 +701,7 @@ bool remove_almost_degenerate_faces(const FaceRange& face_range,
CGAL_precondition(is_valid_polygon_mesh(tmesh));
CGAL_precondition(is_triangle_mesh(tmesh));
// constrain extremities of constrained edges
for(face_descriptor f : face_range)
{
if(f == boost::graph_traits<TriangleMesh>::null_face())
@ -673,36 +721,38 @@ bool remove_almost_degenerate_faces(const FaceRange& face_range,
}
}
// Start the process of removing bad elements
std::set<halfedge_descriptor> edges_to_collapse;
std::set<halfedge_descriptor> edges_to_flip;
// @todo maybe using a priority queue handling the more almost degenerate elements first should be used
std::unordered_set<halfedge_descriptor> edges_to_collapse;
std::unordered_set<halfedge_descriptor> 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<f2, and the edge is smaller than the two other edges...
// initial needless-ness and cap-ness checks
for(face_descriptor f : face_range)
{
internal::collect_badly_shaped_triangles(f, tmesh, vpm, vcm, ecm, gt,
cap_threshold, needle_threshold,
collapse_length_threshold, flip_triangle_height_threshold_squared,
edges_to_collapse, edges_to_flip);
halfedge_descriptor needle_h = internal::is_it_a_needle(f, tmesh, vpm, vcm, ecm, gt,
needle_threshold, collapse_length_threshold);
if(needle_h != null_h)
edges_to_collapse.insert(needle_h);
else
{
halfedge_descriptor cap_h = internal::is_it_a_cap(f, tmesh, vpm, vcm, ecm, gt,
cap_threshold, flip_triangle_height_threshold_squared);
if(cap_h != null_h)
edges_to_flip.insert(cap_h);
}
}
#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES
std::cout << edges_to_collapse.size() << " to collapse" << std::endl;
std::cout << edges_to_flip.size() << " to flip" << std::endl;
#endif
// Start the process of removing bad elements
#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES
int iter = 0;
#endif
for(;;)
{
bool something_was_done = false;
#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES
std::cout << edges_to_collapse.size() << " needles and " << edges_to_flip.size() << " caps" << std::endl;
std::cout << "Iter: " << iter << std::endl;
std::cout << edges_to_collapse.size() << " needles and " << edges_to_flip.size() << " caps" << std::endl;
std::ostringstream oss;
oss << "degen_cleaning_iter_" << iter++ << ".off";
CGAL::IO::write_polygon_mesh(oss.str(), tmesh, CGAL::parameters::stream_precision(17));
@ -711,65 +761,85 @@ bool remove_almost_degenerate_faces(const FaceRange& face_range,
if(edges_to_collapse.empty() && edges_to_flip.empty())
return true;
// @todo maybe using a priority queue handling the more almost degenerate elements should be used
std::set<halfedge_descriptor> next_edges_to_collapse;
std::set<halfedge_descriptor> next_edges_to_flip;
std::unordered_set<halfedge_descriptor> 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<halfedge_descriptor, 2> 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<TriangleMesh>::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<halfedge_descriptor,2> 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<halfedge_descriptor, 2> 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<TriangleMesh>::null_halfedge() && nc[1] != h)
next_edges_to_flip.insert(nc[1]);
else if(nc[0] != boost::graph_traits<TriangleMesh>::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;

View File

@ -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<typename TriangleMesh, typename FaceRange, typename NamedParameters = parameters::Default_named_parameters>
CGAL_DEPRECATED void smooth_mesh(const FaceRange& faces,

View File

@ -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)

View File

@ -0,0 +1,123 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Polygon_mesh_processing/corefinement.h>
template <class Point, class Mesh>
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<Mesh>::face_descriptor;
using halfedge_descriptor = typename boost::graph_traits<Mesh>::halfedge_descriptor;
std::vector<face_descriptor> 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<Point_3>;
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<std::optional<Mesh*>, 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<Point_3>(0, 0, 0,
4, 4, 4,
A, 0);
make_hexa<Point_3>(1, 1, 1,
2, 2, 2,
mh, 0);
make_hexa<Point_3>(1, 1, 1,
2, 2, 2,
B, 1);
Mesh A2, mh2, B2;
make_hexa<Point_3>(5, 0, 0,
9, 4, 4,
A2, 0);
make_hexa<Point_3>(6, 1, 1,
7, 2, 2,
mh2, 0);
make_hexa<Point_3>(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);
}

View File

@ -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)