Merge branch 'repair_functions-old' into repair_functions
This commit is contained in:
@@ -24,16 +24,16 @@
|
||||
|
||||
#include <CGAL/license/Polygon_mesh_processing/miscellaneous.h>
|
||||
|
||||
#include <CGAL/algorithm.h>
|
||||
#include <CGAL/boost/graph/iterator.h>
|
||||
#include <CGAL/Polygon_mesh_processing/internal/named_function_params.h>
|
||||
#include <CGAL/Polygon_mesh_processing/internal/named_params_helper.h>
|
||||
|
||||
#include <boost/graph/graph_traits.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/type_traits/is_same.hpp>
|
||||
#include <boost/unordered_set.hpp>
|
||||
|
||||
#include <CGAL/Polygon_mesh_processing/internal/named_function_params.h>
|
||||
#include <CGAL/Polygon_mesh_processing/internal/named_params_helper.h>
|
||||
#include <CGAL/algorithm.h>
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace CGAL{
|
||||
@@ -257,6 +257,38 @@ namespace Polygon_mesh_processing {
|
||||
|
||||
return border_counter;
|
||||
}
|
||||
|
||||
/// @ingroup PkgPolygonMeshProcessing
|
||||
/// extracts boundary cycles as a list of halfedges, with one halfedge per border.
|
||||
///
|
||||
/// @tparam PolygonMesh a model of `HalfedgeListGraph`
|
||||
/// @tparam OutputIterator a model of `OutputIterator` holding objects of type
|
||||
/// `boost::graph_traits<PolygonMesh>::%halfedge_descriptor`
|
||||
///
|
||||
/// @param pm a polygon mesh
|
||||
/// @param out an output iterator where the border halfedges will be put
|
||||
///
|
||||
/// @todo It could make sense to also return the length of each cycle.
|
||||
/// @todo It should probably go into BGL package (like the rest of this file).
|
||||
template <typename PolygonMesh, typename OutputIterator>
|
||||
OutputIterator extract_boundary_cycles(PolygonMesh& pm,
|
||||
OutputIterator out)
|
||||
{
|
||||
typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
|
||||
|
||||
boost::unordered_set<halfedge_descriptor> hedge_handled;
|
||||
BOOST_FOREACH(halfedge_descriptor h, halfedges(pm))
|
||||
{
|
||||
if(is_border(h, pm) && hedge_handled.insert(h).second)
|
||||
{
|
||||
*out++ = h;
|
||||
BOOST_FOREACH(halfedge_descriptor h2, halfedges_around_face(h, pm))
|
||||
hedge_handled.insert(h2);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
} // end of namespace Polygon_mesh_processing
|
||||
} // end of namespace CGAL
|
||||
|
||||
|
||||
+47
-59
@@ -30,6 +30,7 @@
|
||||
#include <CGAL/Polygon_mesh_processing/repair.h>
|
||||
#include <CGAL/Polygon_mesh_processing/measure.h>
|
||||
#include <CGAL/Polygon_mesh_processing/connected_components.h>
|
||||
#include <CGAL/Polygon_mesh_processing/shape_predicates.h>
|
||||
|
||||
#include <CGAL/AABB_tree.h>
|
||||
#include <CGAL/AABB_traits.h>
|
||||
@@ -90,21 +91,6 @@ namespace internal {
|
||||
};
|
||||
|
||||
// A property map
|
||||
template<typename Descriptor>
|
||||
struct No_constraint_pmap
|
||||
{
|
||||
public:
|
||||
typedef Descriptor key_type;
|
||||
typedef bool value_type;
|
||||
typedef value_type& reference;
|
||||
typedef boost::read_write_property_map_tag category;
|
||||
|
||||
friend bool get(const No_constraint_pmap& , const key_type& ) {
|
||||
return false;
|
||||
}
|
||||
friend void put(No_constraint_pmap& , const key_type& , const bool ) {}
|
||||
};
|
||||
|
||||
template <typename PM, typename FaceIndexMap>
|
||||
struct Border_constraint_pmap
|
||||
{
|
||||
@@ -325,6 +311,7 @@ namespace internal {
|
||||
public:
|
||||
Incremental_remesher(PolygonMesh& pmesh
|
||||
, VertexPointMap& vpmap
|
||||
, const GeomTraits& gt
|
||||
, const bool protect_constraints
|
||||
, EdgeIsConstrainedMap ecmap
|
||||
, VertexIsConstrainedMap vcmap
|
||||
@@ -333,6 +320,7 @@ namespace internal {
|
||||
, const bool build_tree = true)//built by the remesher
|
||||
: mesh_(pmesh)
|
||||
, vpmap_(vpmap)
|
||||
, gt_(gt)
|
||||
, build_tree_(build_tree)
|
||||
, has_border_(false)
|
||||
, input_triangles_()
|
||||
@@ -364,9 +352,10 @@ namespace internal {
|
||||
|
||||
BOOST_FOREACH(face_descriptor f, face_range)
|
||||
{
|
||||
if (is_degenerate_triangle_face(halfedge(f,mesh_),mesh_,vpmap_,GeomTraits())){
|
||||
if(is_degenerate_triangle_face(f, mesh_, parameters::vertex_point_map(vpmap_)
|
||||
.geom_traits(gt_)))
|
||||
continue;
|
||||
}
|
||||
|
||||
Patch_id pid = get_patch_id(f);
|
||||
input_triangles_.push_back(triangle(f));
|
||||
input_patch_ids_.push_back(pid);
|
||||
@@ -817,7 +806,8 @@ namespace internal {
|
||||
debug_status_map();
|
||||
debug_self_intersections();
|
||||
CGAL_assertion(0 == PMP::remove_degenerate_faces(mesh_,
|
||||
PMP::parameters::vertex_point_map(vpmap_).geom_traits(GeomTraits())));
|
||||
parameters::vertex_point_map(vpmap_)
|
||||
.geom_traits(gt_)));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -922,7 +912,7 @@ namespace internal {
|
||||
debug_status_map();
|
||||
CGAL_assertion(0 == PMP::remove_degenerate_faces(mesh_
|
||||
, PMP::parameters::vertex_point_map(vpmap_)
|
||||
.geom_traits(GeomTraits())));
|
||||
.geom_traits(gt_)));
|
||||
debug_self_intersections();
|
||||
#endif
|
||||
|
||||
@@ -964,9 +954,9 @@ namespace internal {
|
||||
|
||||
else if (is_on_patch(v))
|
||||
{
|
||||
Vector_3 vn = PMP::compute_vertex_normal(v, mesh_
|
||||
, PMP::parameters::vertex_point_map(vpmap_)
|
||||
.geom_traits(GeomTraits()));
|
||||
Vector_3 vn = PMP::compute_vertex_normal(v, mesh_,
|
||||
parameters::vertex_point_map(vpmap_)
|
||||
.geom_traits(gt_));
|
||||
put(propmap_normals, v, vn);
|
||||
|
||||
Vector_3 move = CGAL::NULL_VECTOR;
|
||||
@@ -1458,20 +1448,8 @@ private:
|
||||
if (f == boost::graph_traits<PM>::null_face())
|
||||
return CGAL::NULL_VECTOR;
|
||||
|
||||
halfedge_descriptor hd = halfedge(f, mesh_);
|
||||
typename boost::property_traits<VertexPointMap>::reference
|
||||
p = get(vpmap_, target(hd, mesh_));
|
||||
hd = next(hd,mesh_);
|
||||
typename boost::property_traits<VertexPointMap>::reference
|
||||
q = get(vpmap_, target(hd, mesh_));
|
||||
hd = next(hd,mesh_);
|
||||
typename boost::property_traits<VertexPointMap>::reference
|
||||
r =get(vpmap_, target(hd, mesh_));
|
||||
|
||||
if (GeomTraits().collinear_3_object()(p,q,r))
|
||||
return CGAL::NULL_VECTOR;
|
||||
else
|
||||
return PMP::compute_face_normal(f, mesh_, parameters::vertex_point_map(vpmap_));
|
||||
return PMP::compute_face_normal(f, mesh_, parameters::vertex_point_map(vpmap_)
|
||||
.geom_traits(gt_));
|
||||
}
|
||||
|
||||
template<typename FaceRange>
|
||||
@@ -1513,7 +1491,7 @@ private:
|
||||
|
||||
// update status using constrained edge map
|
||||
if (!boost::is_same<EdgeIsConstrainedMap,
|
||||
No_constraint_pmap<edge_descriptor> >::value)
|
||||
Constant_property_map<edge_descriptor, bool> >::value)
|
||||
{
|
||||
BOOST_FOREACH(edge_descriptor e, edges(mesh_))
|
||||
{
|
||||
@@ -1587,27 +1565,31 @@ private:
|
||||
const bool collapse_constraints)
|
||||
{
|
||||
CGAL_assertion_code(std::size_t nb_done = 0);
|
||||
|
||||
boost::unordered_set<halfedge_descriptor> degenerate_faces;
|
||||
BOOST_FOREACH(halfedge_descriptor h,
|
||||
halfedges_around_target(halfedge(v, mesh_), mesh_))
|
||||
{
|
||||
if (is_border(h, mesh_))
|
||||
continue;
|
||||
if (is_degenerate_triangle_face(h, mesh_, vpmap_, GeomTraits()))
|
||||
if(!is_border(h, mesh_) &&
|
||||
is_degenerate_triangle_face(face(h, mesh_), mesh_,
|
||||
parameters::vertex_point_map(vpmap_)
|
||||
.geom_traits(gt_)))
|
||||
degenerate_faces.insert(h);
|
||||
}
|
||||
|
||||
while(!degenerate_faces.empty())
|
||||
{
|
||||
halfedge_descriptor h = *(degenerate_faces.begin());
|
||||
degenerate_faces.erase(degenerate_faces.begin());
|
||||
|
||||
if (!is_degenerate_triangle_face(h, mesh_, vpmap_, GeomTraits()))
|
||||
if (!is_degenerate_triangle_face(face(h, mesh_), mesh_,
|
||||
parameters::vertex_point_map(vpmap_)
|
||||
.geom_traits(gt_)))
|
||||
//this can happen when flipping h has consequences further in the mesh
|
||||
continue;
|
||||
|
||||
//check that opposite is not also degenerate
|
||||
if (degenerate_faces.find(opposite(h, mesh_)) != degenerate_faces.end())
|
||||
degenerate_faces.erase(opposite(h, mesh_));
|
||||
degenerate_faces.erase(opposite(h, mesh_));
|
||||
|
||||
if(is_border(h, mesh_))
|
||||
continue;
|
||||
@@ -1653,11 +1635,15 @@ private:
|
||||
short_edges.insert(typename Bimap::value_type(hf, sqlen));
|
||||
}
|
||||
|
||||
if (!is_border(hf, mesh_)
|
||||
&& is_degenerate_triangle_face(hf, mesh_, vpmap_, GeomTraits()))
|
||||
if(!is_border(hf, mesh_) &&
|
||||
is_degenerate_triangle_face(face(hf, mesh_), mesh_,
|
||||
parameters::vertex_point_map(vpmap_)
|
||||
.geom_traits(gt_)))
|
||||
degenerate_faces.insert(hf);
|
||||
if (!is_border(hfo, mesh_)
|
||||
&& is_degenerate_triangle_face(hfo, mesh_, vpmap_, GeomTraits()))
|
||||
if(!is_border(hfo, mesh_) &&
|
||||
is_degenerate_triangle_face(face(hfo, mesh_), mesh_,
|
||||
parameters::vertex_point_map(vpmap_)
|
||||
.geom_traits(gt_)))
|
||||
degenerate_faces.insert(hfo);
|
||||
|
||||
break;
|
||||
@@ -1674,9 +1660,10 @@ private:
|
||||
BOOST_FOREACH(halfedge_descriptor h,
|
||||
halfedges_around_target(he, mesh_))
|
||||
{
|
||||
if (is_border(h, mesh_))
|
||||
continue;
|
||||
if (is_degenerate_triangle_face(h, mesh_, vpmap_, GeomTraits()))
|
||||
if(!is_border(h, mesh_) &&
|
||||
is_degenerate_triangle_face(face(h, mesh_), mesh_,
|
||||
parameters::vertex_point_map(vpmap_)
|
||||
.geom_traits(gt_)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1817,10 +1804,10 @@ private:
|
||||
{
|
||||
std::cout << "Test self intersections...";
|
||||
std::vector<std::pair<face_descriptor, face_descriptor> > facets;
|
||||
PMP::self_intersections(
|
||||
mesh_,
|
||||
std::back_inserter(facets),
|
||||
PMP::parameters::vertex_point_map(vpmap_));
|
||||
PMP::self_intersections(mesh_,
|
||||
std::back_inserter(facets),
|
||||
PMP::parameters::vertex_point_map(vpmap_)
|
||||
.geom_traits(gt_));
|
||||
//CGAL_assertion(facets.empty());
|
||||
std::cout << "done ("<< facets.size() <<" facets)." << std::endl;
|
||||
}
|
||||
@@ -1829,11 +1816,11 @@ private:
|
||||
{
|
||||
std::cout << "Test self intersections...";
|
||||
std::vector<std::pair<face_descriptor, face_descriptor> > facets;
|
||||
PMP::self_intersections(
|
||||
faces_around_target(halfedge(v, mesh_), mesh_),
|
||||
mesh_,
|
||||
std::back_inserter(facets),
|
||||
PMP::parameters::vertex_point_map(vpmap_));
|
||||
PMP::self_intersections(faces_around_target(halfedge(v, mesh_), mesh_),
|
||||
mesh_,
|
||||
std::back_inserter(facets),
|
||||
PMP::parameters::vertex_point_map(vpmap_)
|
||||
.geom_traits(gt_));
|
||||
//CGAL_assertion(facets.empty());
|
||||
std::cout << "done ("<< facets.size() <<" facets)." << std::endl;
|
||||
}
|
||||
@@ -1916,6 +1903,7 @@ private:
|
||||
private:
|
||||
PolygonMesh& mesh_;
|
||||
VertexPointMap& vpmap_;
|
||||
const GeomTraits& gt_;
|
||||
bool build_tree_;
|
||||
bool has_border_;
|
||||
std::vector<AABB_tree*> trees;
|
||||
|
||||
@@ -0,0 +1,290 @@
|
||||
// Copyright (c) 2018 GeometryFactory (France).
|
||||
// All rights reserved.
|
||||
//
|
||||
// This file is part of CGAL (www.cgal.org).
|
||||
// You can redistribute it and/or modify it under the terms of the GNU
|
||||
// General Public License as published by the Free Software Foundation,
|
||||
// either version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// Licensees holding a valid commercial license may use this file in
|
||||
// accordance with the commercial license agreement provided with the software.
|
||||
//
|
||||
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// $URL$
|
||||
// $Id$
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
//
|
||||
//
|
||||
// Author(s) : Sebastien Loriot
|
||||
|
||||
#ifndef CGAL_POLYGON_MESH_PROCESSING_MERGE_BORDER_VERTICES_H
|
||||
#define CGAL_POLYGON_MESH_PROCESSING_MERGE_BORDER_VERTICES_H
|
||||
|
||||
#include <CGAL/boost/graph/helpers.h>
|
||||
#include <CGAL/boost/graph/properties.h>
|
||||
#include <CGAL/Polygon_mesh_processing/border.h>
|
||||
#include <CGAL/Polygon_mesh_processing/internal/named_function_params.h>
|
||||
#include <CGAL/Polygon_mesh_processing/internal/named_params_helper.h>
|
||||
#include <CGAL/Polygon_mesh_processing/stitch_borders.h>
|
||||
|
||||
#include <boost/unordered_set.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Polygon_mesh_processing {
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename PM, typename VertexPointMap>
|
||||
struct Less_on_point_of_target
|
||||
{
|
||||
typedef typename boost::graph_traits<PM>::halfedge_descriptor
|
||||
halfedge_descriptor;
|
||||
typedef typename boost::property_traits<VertexPointMap>::reference Point;
|
||||
|
||||
Less_on_point_of_target(const PM& pm,
|
||||
const VertexPointMap& vpm)
|
||||
: pm(pm),
|
||||
vpm(vpm)
|
||||
{}
|
||||
|
||||
bool operator()(const std::pair<halfedge_descriptor, std::size_t>& h1,
|
||||
const std::pair<halfedge_descriptor, std::size_t>& h2) const
|
||||
{
|
||||
if ( get(vpm, target(h1.first, pm)) < get(vpm, target(h2.first, pm)) )
|
||||
return true;
|
||||
if ( get(vpm, target(h1.first, pm)) > get(vpm, target(h2.first, pm)) )
|
||||
return false;
|
||||
return h1.second < h2.second;
|
||||
}
|
||||
|
||||
const PM& pm;
|
||||
const VertexPointMap& vpm;
|
||||
};
|
||||
|
||||
|
||||
// warning: cycle_hedges will be altered (sorted)
|
||||
template <class PolygonMesh, class Vpm, class halfedge_descriptor>
|
||||
void detect_identical_mergeable_vertices(
|
||||
std::vector< std::pair<halfedge_descriptor, std::size_t> >& cycle_hedges,
|
||||
std::vector< std::vector<halfedge_descriptor> >& hedges_with_identical_point_target,
|
||||
const PolygonMesh& pm,
|
||||
Vpm vpm)
|
||||
{
|
||||
// sort vertices using their point to ease the detection
|
||||
// of vertices with identical points
|
||||
Less_on_point_of_target<PolygonMesh, Vpm> less(pm, vpm);
|
||||
std::sort( cycle_hedges.begin(), cycle_hedges.end(), less);
|
||||
|
||||
std::size_t nbv=cycle_hedges.size();
|
||||
std::size_t i=1;
|
||||
|
||||
std::set< std::pair<std::size_t, std::size_t> > intervals;
|
||||
|
||||
while(i!=nbv)
|
||||
{
|
||||
if ( get(vpm, target(cycle_hedges[i].first, pm)) ==
|
||||
get(vpm, target(cycle_hedges[i-1].first, pm)) )
|
||||
{
|
||||
hedges_with_identical_point_target.push_back( std::vector<halfedge_descriptor>() );
|
||||
hedges_with_identical_point_target.back().push_back(cycle_hedges[i-1].first);
|
||||
hedges_with_identical_point_target.back().push_back(cycle_hedges[i].first);
|
||||
intervals.insert( std::make_pair(cycle_hedges[i-1].second, cycle_hedges[i].second) );
|
||||
std::size_t previous = cycle_hedges[i].second;
|
||||
while(++i!=nbv)
|
||||
{
|
||||
if ( get(vpm, target(cycle_hedges[i].first, pm)) ==
|
||||
get(vpm, target(cycle_hedges[i-1].first, pm)) )
|
||||
{
|
||||
hedges_with_identical_point_target.back().push_back(cycle_hedges[i].first);
|
||||
intervals.insert( std::make_pair(previous, cycle_hedges[i].second) );
|
||||
previous = cycle_hedges[i].second;
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
++i;
|
||||
}
|
||||
|
||||
// check that intervals are disjoint or strictly nested
|
||||
// if there is only one issue we drop the whole cycle.
|
||||
/// \todo shall we try to be more conservative?
|
||||
if (hedges_with_identical_point_target.empty()) return;
|
||||
std::set< std::pair<std::size_t, std::size_t> >::iterator it1 = intervals.begin(),
|
||||
end2 = intervals.end(),
|
||||
end1 = cpp11::prev(end2),
|
||||
it2;
|
||||
for (; it1!=end1; ++it1)
|
||||
for(it2=cpp11::next(it1); it2!= end2; ++it2 )
|
||||
{
|
||||
CGAL_assertion(it1->first<it2->first);
|
||||
CGAL_assertion(it1->first < it1->second && it2->first < it2->second);
|
||||
if (it1->second > it2->first && it2->second > it1->second)
|
||||
{
|
||||
std::cerr << "Merging is skipt to avoid bad cycle connections\n";
|
||||
hedges_with_identical_point_target.clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // end of internal
|
||||
|
||||
/// \ingroup PMP_repairing_grp
|
||||
/// merges target vertices of a list of halfedges.
|
||||
/// Halfedges must be sorted in the list.
|
||||
///
|
||||
/// @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph`.
|
||||
/// @tparam HalfedgeRange a range of halfedge descriptors of `PolygonMesh`, model of `Range`.
|
||||
///
|
||||
/// @param sorted_hedges a sorted list of halfedges.
|
||||
/// @param pm the polygon mesh which contains the list of halfedges.
|
||||
///
|
||||
template <typename PolygonMesh, class HalfedgeRange>
|
||||
void merge_vertices_in_range(const HalfedgeRange& sorted_hedges,
|
||||
PolygonMesh& pm)
|
||||
{
|
||||
typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
|
||||
typedef typename boost::graph_traits<PolygonMesh>::vertex_descriptor vertex_descriptor;
|
||||
|
||||
halfedge_descriptor in_h_kept = *boost::begin(sorted_hedges);
|
||||
halfedge_descriptor out_h_kept = next(in_h_kept, pm);
|
||||
vertex_descriptor v_kept = target(in_h_kept, pm);
|
||||
|
||||
std::vector<vertex_descriptor> vertices_to_rm;
|
||||
|
||||
BOOST_FOREACH(halfedge_descriptor in_h_rm, sorted_hedges)
|
||||
{
|
||||
vertex_descriptor vd = target(in_h_rm, pm);
|
||||
if (vd==v_kept) continue; // skip identical vertices (in particular this skips the first halfedge)
|
||||
if (edge(vd, v_kept, pm).second) continue; // skip null edges
|
||||
bool shall_continue=false;
|
||||
BOOST_FOREACH(halfedge_descriptor h, halfedges_around_target(v_kept, pm))
|
||||
{
|
||||
if (edge(vd, source(h, pm), pm).second)
|
||||
{
|
||||
shall_continue=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (shall_continue) continue; // skip vertices already incident to the same vertex
|
||||
// update the vertex of the halfedges incident to the vertex to remove
|
||||
internal::update_target_vertex(in_h_rm, v_kept, pm);
|
||||
// update next/prev pointers around the 2 vertices to be merged
|
||||
halfedge_descriptor out_h_rm = next(in_h_rm, pm);
|
||||
set_next(in_h_kept, out_h_rm, pm);
|
||||
set_next(in_h_rm, out_h_kept, pm);
|
||||
vertices_to_rm.push_back(vd);
|
||||
out_h_kept=out_h_rm;
|
||||
}
|
||||
|
||||
BOOST_FOREACH(vertex_descriptor vd, vertices_to_rm)
|
||||
remove_vertex(vd, pm);
|
||||
}
|
||||
|
||||
/// \ingroup PMP_repairing_grp
|
||||
/// merges identical vertices around a cycle of connected edges.
|
||||
///
|
||||
/// @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph`.
|
||||
/// @tparam NamedParameter a sequence of \ref pmp_namedparameters "Named Parameters".
|
||||
///
|
||||
/// @param h a halfedge that belongs to the cycle.
|
||||
/// @param pm the polygon mesh which containts the cycle.
|
||||
/// @param np optional parameter of \ref pmp_namedparameters "Named Parameters" listed below.
|
||||
///
|
||||
/// \cgalNamedParamsBegin
|
||||
/// \cgalParamBegin{vertex_point_map}
|
||||
/// the property map with the points associated to the vertices of `pm`.
|
||||
/// If this parameter is omitted, an internal property map for
|
||||
/// `CGAL::vertex_point_t` should be available in `PolygonMesh`
|
||||
/// \cgalParamEnd
|
||||
/// \cgalNamedParamsEnd
|
||||
template <class PolygonMesh, class NamedParameter>
|
||||
void merge_duplicated_vertices_in_boundary_cycle(
|
||||
typename boost::graph_traits<PolygonMesh>::halfedge_descriptor h,
|
||||
PolygonMesh& pm,
|
||||
const NamedParameter& np)
|
||||
{
|
||||
typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
|
||||
typedef typename GetVertexPointMap<PolygonMesh, NamedParameter>::const_type Vpm;
|
||||
|
||||
Vpm vpm = choose_param(get_param(np, internal_np::vertex_point),
|
||||
get_const_property_map(vertex_point, pm));
|
||||
|
||||
// collect all the halfedges of the cycle
|
||||
std::vector< std::pair<halfedge_descriptor, std::size_t> > cycle_hedges;
|
||||
halfedge_descriptor start=h;
|
||||
std::size_t index=0;
|
||||
do{
|
||||
cycle_hedges.push_back( std::make_pair(h, index) );
|
||||
h=next(h, pm);
|
||||
++index;
|
||||
}while(start!=h);
|
||||
|
||||
std::vector< std::vector<halfedge_descriptor> > hedges_with_identical_point_target;
|
||||
internal::detect_identical_mergeable_vertices(cycle_hedges, hedges_with_identical_point_target, pm, vpm);
|
||||
|
||||
BOOST_FOREACH(const std::vector<halfedge_descriptor>& hedges,
|
||||
hedges_with_identical_point_target)
|
||||
{
|
||||
start=hedges.front();
|
||||
// hedges are sorted along the cycle
|
||||
merge_vertices_in_range(hedges, pm);
|
||||
}
|
||||
}
|
||||
|
||||
/// \ingroup PMP_repairing_grp
|
||||
/// extracts boundary cycles and merges the duplicated
|
||||
/// vertices of each cycle.
|
||||
///
|
||||
/// @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph`.
|
||||
/// @tparam NamedParameter a sequence of \ref pmp_namedparameters "Named Parameters".
|
||||
///
|
||||
/// @param pm the polygon mesh which containts the cycle.
|
||||
/// @param np optional parameter of \ref pmp_namedparameters "Named Parameters" listed below.
|
||||
///
|
||||
/// \cgalNamedParamsBegin
|
||||
/// \cgalParamBegin{vertex_point_map}
|
||||
/// the property map with the points associated to the vertices of `pm`.
|
||||
/// If this parameter is omitted, an internal property map for
|
||||
/// `CGAL::vertex_point_t` should be available in `PolygonMesh`
|
||||
/// \cgalParamEnd
|
||||
/// \cgalNamedParamsEnd
|
||||
template <class PolygonMesh, class NamedParameter>
|
||||
void merge_duplicated_vertices_in_boundary_cycles( PolygonMesh& pm,
|
||||
const NamedParameter& np)
|
||||
{
|
||||
typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
|
||||
|
||||
std::vector<halfedge_descriptor> cycles;
|
||||
extract_boundary_cycles(pm, std::back_inserter(cycles));
|
||||
|
||||
BOOST_FOREACH(halfedge_descriptor h, cycles)
|
||||
merge_duplicated_vertices_in_boundary_cycle(h, pm, np);
|
||||
}
|
||||
|
||||
template <class PolygonMesh>
|
||||
void merge_duplicated_vertices_in_boundary_cycles(PolygonMesh& pm)
|
||||
{
|
||||
merge_duplicated_vertices_in_boundary_cycles(pm, parameters::all_default());
|
||||
}
|
||||
|
||||
template <class PolygonMesh>
|
||||
void merge_duplicated_vertices_in_boundary_cycle(
|
||||
typename boost::graph_traits<PolygonMesh>::halfedge_descriptor h,
|
||||
PolygonMesh& pm)
|
||||
{
|
||||
merge_duplicated_vertices_in_boundary_cycle(h, pm, parameters::all_default());
|
||||
}
|
||||
|
||||
} } // end of CGAL::Polygon_mesh_processing
|
||||
|
||||
#endif //CGAL_POLYGON_MESH_PROCESSING_MERGE_BORDER_VERTICES_H
|
||||
@@ -174,10 +174,10 @@ void random_perturbation(VertexRange vertices
|
||||
typedef typename boost::lookup_named_param_def <
|
||||
internal_np::vertex_is_constrained_t,
|
||||
NamedParameters,
|
||||
internal::No_constraint_pmap<vertex_descriptor>//default
|
||||
Constant_property_map<vertex_descriptor, bool> // default
|
||||
> ::type VCMap;
|
||||
VCMap vcmap = choose_param(get_param(np, internal_np::vertex_is_constrained),
|
||||
internal::No_constraint_pmap<vertex_descriptor>());
|
||||
Constant_property_map<vertex_descriptor, bool>(false));
|
||||
|
||||
unsigned int seed = choose_param(get_param(np, internal_np::random_seed), -1);
|
||||
bool do_project = choose_param(get_param(np, internal_np::do_project), true);
|
||||
|
||||
@@ -163,6 +163,7 @@ void isotropic_remeshing(const FaceRange& faces
|
||||
boost::is_default_param(get_param(np, internal_np::projection_functor));
|
||||
|
||||
typedef typename GetGeomTraits<PM, NamedParameters>::type GT;
|
||||
GT gt = choose_param(get_param(np, internal_np::geom_traits), GT());
|
||||
|
||||
typedef typename GetVertexPointMap<PM, NamedParameters>::type VPMap;
|
||||
VPMap vpmap = choose_param(get_param(np, internal_np::vertex_point),
|
||||
@@ -175,18 +176,18 @@ void isotropic_remeshing(const FaceRange& faces
|
||||
typedef typename boost::lookup_named_param_def <
|
||||
internal_np::edge_is_constrained_t,
|
||||
NamedParameters,
|
||||
internal::No_constraint_pmap<edge_descriptor>//default
|
||||
Constant_property_map<edge_descriptor, bool> // default (no constraint pmap)
|
||||
> ::type ECMap;
|
||||
ECMap ecmap = choose_param(get_param(np, internal_np::edge_is_constrained)
|
||||
, internal::No_constraint_pmap<edge_descriptor>());
|
||||
ECMap ecmap = choose_param(get_param(np, internal_np::edge_is_constrained),
|
||||
Constant_property_map<edge_descriptor, bool>(false));
|
||||
|
||||
typedef typename boost::lookup_named_param_def <
|
||||
internal_np::vertex_is_constrained_t,
|
||||
NamedParameters,
|
||||
internal::No_constraint_pmap<vertex_descriptor>//default
|
||||
Constant_property_map<vertex_descriptor, bool> // default (no constraint pmap)
|
||||
> ::type VCMap;
|
||||
VCMap vcmap = choose_param(get_param(np, internal_np::vertex_is_constrained),
|
||||
internal::No_constraint_pmap<vertex_descriptor>());
|
||||
Constant_property_map<vertex_descriptor, bool>(false));
|
||||
|
||||
bool protect = choose_param(get_param(np, internal_np::protect_constraints), false);
|
||||
typedef typename boost::lookup_named_param_def <
|
||||
@@ -227,7 +228,7 @@ void isotropic_remeshing(const FaceRange& faces
|
||||
#endif
|
||||
|
||||
typename internal::Incremental_remesher<PM, VPMap, GT, ECMap, VCMap, FPMap, FIMap>
|
||||
remesher(pmesh, vpmap, protect, ecmap, vcmap, fpmap, fimap, need_aabb_tree);
|
||||
remesher(pmesh, vpmap, gt, protect, ecmap, vcmap, fpmap, fimap, need_aabb_tree);
|
||||
remesher.init_remeshing(faces);
|
||||
|
||||
#ifdef CGAL_PMP_REMESHING_VERBOSE
|
||||
@@ -340,6 +341,8 @@ void split_long_edges(const EdgeRange& edges
|
||||
using boost::get_param;
|
||||
|
||||
typedef typename GetGeomTraits<PM, NamedParameters>::type GT;
|
||||
GT gt = choose_param(get_param(np, internal_np::geom_traits), GT());
|
||||
|
||||
typedef typename GetVertexPointMap<PM, NamedParameters>::type VPMap;
|
||||
VPMap vpmap = choose_param(get_param(np, internal_np::vertex_point),
|
||||
get_property_map(vertex_point, pmesh));
|
||||
@@ -351,22 +354,21 @@ void split_long_edges(const EdgeRange& edges
|
||||
typedef typename boost::lookup_named_param_def <
|
||||
internal_np::edge_is_constrained_t,
|
||||
NamedParameters,
|
||||
internal::No_constraint_pmap<edge_descriptor>//default
|
||||
Constant_property_map<edge_descriptor, bool> // default (no constraint pmap)
|
||||
> ::type ECMap;
|
||||
ECMap ecmap = choose_param(get_param(np, internal_np::edge_is_constrained),
|
||||
internal::No_constraint_pmap<edge_descriptor>());
|
||||
Constant_property_map<edge_descriptor, bool>(false));
|
||||
|
||||
typename internal::Incremental_remesher<PM, VPMap, GT, ECMap,
|
||||
internal::No_constraint_pmap<vertex_descriptor>,
|
||||
Constant_property_map<vertex_descriptor, bool>, // no constraint pmap
|
||||
internal::Connected_components_pmap<PM, FIMap>,
|
||||
FIMap
|
||||
>
|
||||
remesher(pmesh, vpmap, false/*protect constraints*/
|
||||
, ecmap
|
||||
, internal::No_constraint_pmap<vertex_descriptor>()
|
||||
, internal::Connected_components_pmap<PM, FIMap>(faces(pmesh), pmesh, ecmap, fimap, false)
|
||||
, fimap
|
||||
, false/*need aabb_tree*/);
|
||||
remesher(pmesh, vpmap, gt, false/*protect constraints*/, ecmap,
|
||||
Constant_property_map<vertex_descriptor, bool>(false),
|
||||
internal::Connected_components_pmap<PM, FIMap>(faces(pmesh), pmesh, ecmap, fimap, false),
|
||||
fimap,
|
||||
false/*need aabb_tree*/);
|
||||
|
||||
remesher.split_long_edges(edges, max_length);
|
||||
}
|
||||
|
||||
@@ -24,12 +24,9 @@
|
||||
|
||||
#include <CGAL/license/Polygon_mesh_processing/repair.h>
|
||||
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <boost/algorithm/minmax_element.hpp>
|
||||
#include <CGAL/boost/graph/Euler_operations.h>
|
||||
#include <CGAL/Union_find.h>
|
||||
#include <CGAL/property_map.h>
|
||||
#include <CGAL/algorithm.h>
|
||||
#include <CGAL/array.h>
|
||||
|
||||
@@ -40,21 +37,33 @@
|
||||
|
||||
#include <CGAL/Polygon_mesh_processing/connected_components.h>
|
||||
#include <CGAL/Polygon_mesh_processing/border.h>
|
||||
#include <CGAL/Polygon_mesh_processing/shape_predicates.h>
|
||||
|
||||
#include <CGAL/Polygon_mesh_processing/internal/named_function_params.h>
|
||||
#include <CGAL/Polygon_mesh_processing/internal/named_params_helper.h>
|
||||
|
||||
#include <CGAL/is_iterator.h>
|
||||
|
||||
#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
|
||||
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
|
||||
#include <CGAL/IO/OFF_reader.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#endif
|
||||
|
||||
#include <boost/algorithm/minmax_element.hpp>
|
||||
#include <boost/type_traits/is_same.hpp>
|
||||
#include <boost/utility/enable_if.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace CGAL{
|
||||
namespace Polygon_mesh_processing {
|
||||
|
||||
namespace debug{
|
||||
|
||||
template <class TriangleMesh, class VertexPointMap>
|
||||
std::ostream& dump_edge_neighborhood(
|
||||
typename boost::graph_traits<TriangleMesh>::edge_descriptor ed,
|
||||
@@ -142,8 +151,11 @@ namespace debug{
|
||||
<< vids[ target(next(next(halfedge(f, tm), tm), tm), tm) ] << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
} //end of namespace debug
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <class HalfedgeGraph, class VertexPointMap, class Traits>
|
||||
struct Less_vertex_point{
|
||||
typedef typename boost::graph_traits<HalfedgeGraph>::vertex_descriptor vertex_descriptor;
|
||||
@@ -157,33 +169,139 @@ struct Less_vertex_point{
|
||||
}
|
||||
};
|
||||
|
||||
///\cond SKIP_IN_MANUAL
|
||||
} // end namespace internal
|
||||
|
||||
template <class Traits, class TriangleMesh, class VertexPointMap, class OutputIterator>
|
||||
OutputIterator
|
||||
degenerate_faces(const TriangleMesh& tm,
|
||||
const VertexPointMap& vpmap,
|
||||
const Traits& traits,
|
||||
OutputIterator out)
|
||||
/// \ingroup PMP_repairing_grp
|
||||
/// collects the degenerate edges within a given range of edges.
|
||||
///
|
||||
/// @tparam EdgeRange a model of `Range` with value type `boost::graph_traits<TriangleMesh>::edge_descriptor`
|
||||
/// @tparam TriangleMesh a model of `EdgeListGraph`
|
||||
/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
|
||||
///
|
||||
/// @param edges a subset of edges of `tm`
|
||||
/// @param tm a triangle mesh
|
||||
/// @param np optional \ref pmp_namedparameters "Named Parameters" described below
|
||||
///
|
||||
/// \cgalNamedParamsBegin
|
||||
/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `tm`.
|
||||
/// The type of this map is model of `ReadWritePropertyMap`.
|
||||
/// If this parameter is omitted, an internal property map for
|
||||
/// `CGAL::vertex_point_t` should be available in `TriangleMesh`
|
||||
/// \cgalParamEnd
|
||||
/// \cgalParamBegin{geom_traits} a geometric traits class instance.
|
||||
/// The traits class must provide the nested type `Point_3`,
|
||||
/// and the nested functor `Equal_3` to check whether two points are identical.
|
||||
/// \cgalParamEnd
|
||||
/// \cgalNamedParamsEnd
|
||||
template <class EdgeRange, class TriangleMesh, class OutputIterator, class NamedParameters>
|
||||
OutputIterator degenerate_edges(const EdgeRange& edges,
|
||||
const TriangleMesh& tm,
|
||||
OutputIterator out,
|
||||
const NamedParameters& np)
|
||||
{
|
||||
typedef typename boost::graph_traits<TriangleMesh>::face_descriptor face_descriptor;
|
||||
BOOST_FOREACH(face_descriptor fd, faces(tm))
|
||||
typedef typename boost::graph_traits<TriangleMesh>::edge_descriptor edge_descriptor;
|
||||
|
||||
BOOST_FOREACH(edge_descriptor ed, edges)
|
||||
{
|
||||
if ( is_degenerate_triangle_face(fd, tm, vpmap, traits) )
|
||||
*out++=fd;
|
||||
if(is_degenerate_edge(ed, tm, np))
|
||||
*out++ = ed;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template <class EdgeRange, class TriangleMesh, class OutputIterator>
|
||||
OutputIterator degenerate_edges(const EdgeRange& edges,
|
||||
const TriangleMesh& tm,
|
||||
OutputIterator out,
|
||||
typename boost::disable_if_c<
|
||||
CGAL::is_iterator<TriangleMesh>::value
|
||||
>::type* = 0)
|
||||
{
|
||||
return degenerate_edges(edges, tm, out, CGAL::parameters::all_default());
|
||||
}
|
||||
|
||||
template <class TriangleMesh, class OutputIterator, class NamedParameters>
|
||||
OutputIterator degenerate_edges(const TriangleMesh& tm,
|
||||
OutputIterator out,
|
||||
const NamedParameters& np,
|
||||
typename boost::enable_if_c<
|
||||
CGAL::is_iterator<OutputIterator>::value
|
||||
>::type* = 0)
|
||||
{
|
||||
return degenerate_edges(edges(tm), tm, out, np);
|
||||
}
|
||||
|
||||
template <class TriangleMesh, class OutputIterator>
|
||||
OutputIterator
|
||||
degenerate_faces(const TriangleMesh& tm, OutputIterator out)
|
||||
degenerate_edges(const TriangleMesh& tm, OutputIterator out)
|
||||
{
|
||||
typedef typename boost::property_map<TriangleMesh, CGAL::vertex_point_t>::type Vpm;
|
||||
typedef typename boost::property_traits<Vpm>::value_type Point;
|
||||
typedef typename Kernel_traits<Point>::Kernel Kernel;
|
||||
return degenerate_edges(edges(tm), tm, out, CGAL::parameters::all_default());
|
||||
}
|
||||
|
||||
return degenerate_faces(tm, get(vertex_point, tm), Kernel(), out);
|
||||
/// \ingroup PMP_repairing_grp
|
||||
/// collects the degenerate faces within a given range of faces.
|
||||
///
|
||||
/// @tparam FaceRange a model of `Range` with value type `boost::graph_traits<TriangleMesh>::face_descriptor`
|
||||
/// @tparam TriangleMesh a model of `FaceGraph`
|
||||
/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
|
||||
///
|
||||
/// @param faces a subset of faces of `tm`
|
||||
/// @param tm a triangle mesh
|
||||
/// @param np optional \ref pmp_namedparameters "Named Parameters" described below
|
||||
///
|
||||
/// \cgalNamedParamsBegin
|
||||
/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `tm`.
|
||||
/// The type of this map is model of `ReadWritePropertyMap`.
|
||||
/// If this parameter is omitted, an internal property map for
|
||||
/// `CGAL::vertex_point_t` should be available in `TriangleMesh`
|
||||
/// \cgalParamEnd
|
||||
/// \cgalParamBegin{geom_traits} a geometric traits class instance.
|
||||
/// The traits class must provide the nested functor `Collinear_3`
|
||||
/// to check whether three points are collinear.
|
||||
/// \cgalParamEnd
|
||||
/// \cgalNamedParamsEnd
|
||||
template <class FaceRange, class TriangleMesh, class OutputIterator, class NamedParameters>
|
||||
OutputIterator degenerate_faces(const FaceRange& faces,
|
||||
const TriangleMesh& tm,
|
||||
OutputIterator out,
|
||||
const NamedParameters& np)
|
||||
{
|
||||
typedef typename boost::graph_traits<TriangleMesh>::face_descriptor face_descriptor;
|
||||
|
||||
BOOST_FOREACH(face_descriptor fd, faces)
|
||||
{
|
||||
if(is_degenerate_triangle_face(fd, tm, np))
|
||||
*out++ = fd;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template <class FaceRange, class TriangleMesh, class OutputIterator>
|
||||
OutputIterator degenerate_faces(const FaceRange& faces,
|
||||
const TriangleMesh& tm,
|
||||
OutputIterator out,
|
||||
typename boost::disable_if_c<
|
||||
CGAL::is_iterator<TriangleMesh>::value
|
||||
>::type* = 0)
|
||||
{
|
||||
return degenerate_faces(faces, tm, out, CGAL::parameters::all_default());
|
||||
}
|
||||
|
||||
template <class TriangleMesh, class OutputIterator, class NamedParameters>
|
||||
OutputIterator degenerate_faces(const TriangleMesh& tm,
|
||||
OutputIterator out,
|
||||
const NamedParameters& np,
|
||||
typename boost::enable_if_c<
|
||||
CGAL::is_iterator<OutputIterator>::value
|
||||
>::type* = 0)
|
||||
{
|
||||
return degenerate_faces(faces(tm), tm, out, np);
|
||||
}
|
||||
|
||||
template <class TriangleMesh, class OutputIterator>
|
||||
OutputIterator degenerate_faces(const TriangleMesh& tm, OutputIterator out)
|
||||
{
|
||||
return degenerate_faces(faces(tm), tm, out, CGAL::parameters::all_default());
|
||||
}
|
||||
|
||||
// this function remove a border edge even if it does not satisfy the link condition.
|
||||
@@ -365,10 +483,9 @@ remove_a_border_edge(typename boost::graph_traits<TriangleMesh>::edge_descriptor
|
||||
}
|
||||
|
||||
template <class EdgeRange, class TriangleMesh, class NamedParameters>
|
||||
std::size_t remove_null_edges(
|
||||
const EdgeRange& edge_range,
|
||||
TriangleMesh& tmesh,
|
||||
const NamedParameters& np)
|
||||
std::size_t remove_degenerate_edges(const EdgeRange& edge_range,
|
||||
TriangleMesh& tmesh,
|
||||
const NamedParameters& np)
|
||||
{
|
||||
CGAL_assertion(CGAL::is_triangle_mesh(tmesh));
|
||||
|
||||
@@ -385,27 +502,24 @@ std::size_t remove_null_edges(
|
||||
typedef typename GetVertexPointMap<TM, NamedParameters>::type VertexPointMap;
|
||||
VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point),
|
||||
get_property_map(vertex_point, tmesh));
|
||||
|
||||
typedef typename GetGeomTraits<TM, NamedParameters>::type Traits;
|
||||
Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits());
|
||||
|
||||
std::size_t nb_deg_faces = 0;
|
||||
|
||||
// collect edges of length 0
|
||||
std::set<edge_descriptor> null_edges_to_remove;
|
||||
BOOST_FOREACH(edge_descriptor ed, edge_range)
|
||||
{
|
||||
if ( traits.equal_3_object()(get(vpmap, target(ed, tmesh)), get(vpmap, source(ed, tmesh))) )
|
||||
null_edges_to_remove.insert(ed);
|
||||
}
|
||||
std::set<edge_descriptor> degenerate_edges_to_remove;
|
||||
degenerate_edges(edge_range, tmesh, std::inserter(degenerate_edges_to_remove,
|
||||
degenerate_edges_to_remove.end()));
|
||||
|
||||
#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
|
||||
std::cout << "Found " << null_edges_to_remove.size() << " null edges.\n";
|
||||
#endif
|
||||
#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
|
||||
std::cout << "Found " << degenerate_edges_to_remove.size() << " null edges.\n";
|
||||
#endif
|
||||
|
||||
while (!null_edges_to_remove.empty())
|
||||
while (!degenerate_edges_to_remove.empty())
|
||||
{
|
||||
edge_descriptor ed = *null_edges_to_remove.begin();
|
||||
null_edges_to_remove.erase(null_edges_to_remove.begin());
|
||||
edge_descriptor ed = *degenerate_edges_to_remove.begin();
|
||||
degenerate_edges_to_remove.erase(degenerate_edges_to_remove.begin());
|
||||
|
||||
halfedge_descriptor h = halfedge(ed, tmesh);
|
||||
|
||||
@@ -415,12 +529,12 @@ std::size_t remove_null_edges(
|
||||
if ( face(h, tmesh)!=GT::null_face() )
|
||||
{
|
||||
++nb_deg_faces;
|
||||
null_edges_to_remove.erase(edge(prev(h, tmesh), tmesh));
|
||||
degenerate_edges_to_remove.erase(edge(prev(h, tmesh), tmesh));
|
||||
}
|
||||
if (face(opposite(h, tmesh), tmesh)!=GT::null_face())
|
||||
{
|
||||
++nb_deg_faces;
|
||||
null_edges_to_remove.erase(edge(prev(opposite(h, tmesh), tmesh), tmesh));
|
||||
degenerate_edges_to_remove.erase(edge(prev(opposite(h, tmesh), tmesh), tmesh));
|
||||
}
|
||||
//now remove the edge
|
||||
CGAL::Euler::collapse_edge(ed, tmesh);
|
||||
@@ -435,7 +549,7 @@ std::size_t remove_null_edges(
|
||||
if (is_triangle(hd, tmesh))
|
||||
{
|
||||
Euler::fill_hole(hd, tmesh);
|
||||
null_edges_to_remove.insert(ed);
|
||||
degenerate_edges_to_remove.insert(ed);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -621,7 +735,7 @@ std::size_t remove_null_edges(
|
||||
// remove edges
|
||||
BOOST_FOREACH(edge_descriptor ed, edges_to_remove)
|
||||
{
|
||||
null_edges_to_remove.erase(ed);
|
||||
degenerate_edges_to_remove.erase(ed);
|
||||
remove_edge(ed, tmesh);
|
||||
}
|
||||
|
||||
@@ -641,8 +755,8 @@ std::size_t remove_null_edges(
|
||||
put(vpmap, target(new_hd, tmesh), pt);
|
||||
|
||||
BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_target(new_hd, tmesh))
|
||||
if ( traits.equal_3_object()(get(vpmap, target(hd, tmesh)), get(vpmap, source(hd, tmesh))) )
|
||||
null_edges_to_remove.insert(edge(hd, tmesh));
|
||||
if(is_degenerate_edge(edge(hd, tmesh), tmesh, np))
|
||||
degenerate_edges_to_remove.insert(edge(hd, tmesh));
|
||||
|
||||
CGAL_assertion( is_valid_polygon_mesh(tmesh) );
|
||||
}
|
||||
@@ -652,12 +766,10 @@ std::size_t remove_null_edges(
|
||||
}
|
||||
|
||||
template <class EdgeRange, class TriangleMesh>
|
||||
std::size_t remove_null_edges(
|
||||
const EdgeRange& edge_range,
|
||||
TriangleMesh& tmesh)
|
||||
std::size_t remove_degenerate_edges(const EdgeRange& edge_range,
|
||||
TriangleMesh& tmesh)
|
||||
{
|
||||
return remove_null_edges(edge_range, tmesh,
|
||||
parameters::all_default());
|
||||
return remove_degenerate_edges(edge_range, tmesh, parameters::all_default());
|
||||
}
|
||||
|
||||
/// \ingroup PMP_repairing_grp
|
||||
@@ -674,15 +786,15 @@ std::size_t remove_null_edges(
|
||||
/// @param np optional \ref pmp_namedparameters "Named Parameters" described below
|
||||
///
|
||||
/// \cgalNamedParamsBegin
|
||||
/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`.
|
||||
/// If this parameter is omitted, an internal property map for
|
||||
/// `CGAL::vertex_point_t` must be available in `TriangleMesh`
|
||||
/// \cgalParamEnd
|
||||
/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`.
|
||||
/// The type of this map is model of `ReadWritePropertyMap`.
|
||||
/// If this parameter is omitted, an internal property map for
|
||||
/// `CGAL::vertex_point_t` must be available in `TriangleMesh`
|
||||
/// \cgalParamEnd
|
||||
/// \cgalParamBegin{geom_traits} a geometric traits class instance.
|
||||
/// The traits class must provide the nested type `Point_3`,
|
||||
/// and the nested functors :
|
||||
/// - `Compare_distance_3` to compute the distance between 2 points
|
||||
/// - `Collinear_are_ordered_along_line_3` to check whether 3 collinear points are ordered
|
||||
/// - `Collinear_3` to check whether 3 points are collinear
|
||||
/// - `Less_xyz_3` to compare lexicographically two points
|
||||
/// - `Equal_3` to check whether 2 points are identical
|
||||
@@ -692,6 +804,7 @@ std::size_t remove_null_edges(
|
||||
///
|
||||
/// \todo the function might not be able to remove all degenerate faces.
|
||||
/// We should probably do something with the return type.
|
||||
///
|
||||
/// \return number of removed degenerate faces
|
||||
template <class TriangleMesh, class NamedParameters>
|
||||
std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
|
||||
@@ -717,8 +830,9 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
|
||||
|
||||
typedef typename boost::property_traits<VertexPointMap>::value_type Point_3;
|
||||
typedef typename boost::property_traits<VertexPointMap>::reference Point_ref;
|
||||
|
||||
// First remove edges of length 0
|
||||
std::size_t nb_deg_faces = remove_null_edges(edges(tmesh), tmesh, np);
|
||||
std::size_t nb_deg_faces = remove_degenerate_edges(edges(tmesh), tmesh, np);
|
||||
|
||||
#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
|
||||
{
|
||||
@@ -731,9 +845,8 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
|
||||
|
||||
// Then, remove triangles made of 3 collinear points
|
||||
std::set<face_descriptor> degenerate_face_set;
|
||||
BOOST_FOREACH(face_descriptor fd, faces(tmesh))
|
||||
if ( is_degenerate_triangle_face(fd, tmesh, vpmap, traits) )
|
||||
degenerate_face_set.insert(fd);
|
||||
degenerate_faces(tmesh, std::inserter(degenerate_face_set, degenerate_face_set.begin()), np);
|
||||
|
||||
nb_deg_faces+=degenerate_face_set.size();
|
||||
|
||||
// first remove degree 3 vertices that are part of a cap
|
||||
@@ -763,7 +876,7 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
|
||||
degenerate_face_set.erase( face(hd2, tmesh) );
|
||||
// remove the central vertex and check if the new face is degenerated
|
||||
hd=CGAL::Euler::remove_center_vertex(hd, tmesh);
|
||||
if (is_degenerate_triangle_face(face(hd, tmesh), tmesh, vpmap, traits))
|
||||
if (is_degenerate_triangle_face(face(hd, tmesh), tmesh, np))
|
||||
{
|
||||
degenerate_face_set.insert( face(hd, tmesh) );
|
||||
}
|
||||
@@ -964,7 +1077,7 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
|
||||
// preliminary step to check if the operation is possible
|
||||
// sort the boundary points along the common supporting line
|
||||
// we first need a reference point
|
||||
typedef Less_vertex_point<TriangleMesh, VertexPointMap, Traits> Less_vertex;
|
||||
typedef internal::Less_vertex_point<TriangleMesh, VertexPointMap, Traits> Less_vertex;
|
||||
std::pair<
|
||||
typename std::set<vertex_descriptor>::iterator,
|
||||
typename std::set<vertex_descriptor>::iterator > ref_vertices =
|
||||
@@ -1281,7 +1394,6 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
|
||||
return nb_deg_faces;
|
||||
}
|
||||
|
||||
|
||||
template<class TriangleMesh>
|
||||
std::size_t remove_degenerate_faces(TriangleMesh& tmesh)
|
||||
{
|
||||
@@ -1289,52 +1401,194 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh)
|
||||
CGAL::Polygon_mesh_processing::parameters::all_default());
|
||||
}
|
||||
|
||||
template <class TriangleMesh, class Vpm>
|
||||
std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm, Vpm vpm)
|
||||
namespace internal {
|
||||
|
||||
template <typename G, typename OutputIterator>
|
||||
struct Vertex_collector
|
||||
{
|
||||
typedef typename boost::graph_traits<G>::vertex_descriptor vertex_descriptor;
|
||||
|
||||
void collect_vertices(vertex_descriptor v1, vertex_descriptor v2)
|
||||
{
|
||||
std::vector<vertex_descriptor>& verts = collections[v1];
|
||||
if(verts.empty())
|
||||
verts.push_back(v1);
|
||||
verts.push_back(v2);
|
||||
}
|
||||
|
||||
void dump(OutputIterator out)
|
||||
{
|
||||
typedef std::pair<const vertex_descriptor, std::vector<vertex_descriptor> > Pair_type;
|
||||
BOOST_FOREACH(const Pair_type& p, collections) {
|
||||
*out++ = p.second;
|
||||
}
|
||||
}
|
||||
|
||||
std::map<vertex_descriptor, std::vector<vertex_descriptor> > collections;
|
||||
};
|
||||
|
||||
template <typename G>
|
||||
struct Vertex_collector<G, Emptyset_iterator>
|
||||
{
|
||||
typedef typename boost::graph_traits<G>::vertex_descriptor vertex_descriptor;
|
||||
void collect_vertices(vertex_descriptor, vertex_descriptor)
|
||||
{}
|
||||
|
||||
void dump(Emptyset_iterator)
|
||||
{}
|
||||
};
|
||||
|
||||
} // end namespace internal
|
||||
|
||||
/// \ingroup PMP_repairing_grp
|
||||
/// checks whether a vertex of a triangle mesh is non-manifold.
|
||||
///
|
||||
/// @tparam TriangleMesh a model of `HalfedgeListGraph`
|
||||
///
|
||||
/// @param v a vertex of `tm`
|
||||
/// @param tm a triangle mesh containing `v`
|
||||
///
|
||||
/// \return `true` if the vertex is non-manifold, `false` otherwise.
|
||||
template <typename TriangleMesh>
|
||||
bool is_non_manifold_vertex(typename boost::graph_traits<TriangleMesh>::vertex_descriptor v,
|
||||
const TriangleMesh& tm)
|
||||
{
|
||||
CGAL_assertion(CGAL::is_triangle_mesh(tm));
|
||||
|
||||
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
|
||||
|
||||
boost::unordered_set<halfedge_descriptor> halfedges_handled;
|
||||
BOOST_FOREACH(halfedge_descriptor h, halfedges_around_target(v, tm))
|
||||
halfedges_handled.insert(h);
|
||||
|
||||
BOOST_FOREACH(halfedge_descriptor h, halfedges(tm))
|
||||
{
|
||||
if(v == target(h, tm))
|
||||
{
|
||||
if(halfedges_handled.count(h) == 0)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \ingroup PMP_repairing_grp
|
||||
/// duplicates all the non-manifold vertices of the input mesh.
|
||||
///
|
||||
/// @tparam TriangleMesh a model of `HalfedgeListGraph` and `MutableHalfedgeGraph`
|
||||
/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
|
||||
///
|
||||
/// @param tm the triangulated surface mesh to be repaired
|
||||
/// @param np optional \ref pmp_namedparameters "Named Parameters" described below
|
||||
///
|
||||
/// \cgalNamedParamsBegin
|
||||
/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`.
|
||||
/// The type of this map is model of `ReadWritePropertyMap`.
|
||||
/// If this parameter is omitted, an internal property map for
|
||||
/// `CGAL::vertex_point_t` should be available in `TriangleMesh`
|
||||
/// \cgalParamEnd
|
||||
/// \cgalParamBegin{vertex_is_constrained_map} a writable property map with `vertex_descriptor`
|
||||
/// as key and `bool` as `value_type`. `put(pmap, v, true)` will be called for each duplicated
|
||||
/// vertices and the input one.
|
||||
/// \cgalParamEnd
|
||||
/// \cgalParamBegin{output_iterator} a model of `OutputIterator` with value type
|
||||
/// `std::vector<vertex_descriptor>`. The first vertex of each vector is a non-manifold vertex
|
||||
/// of the input mesh, followed by the new vertices that were created to fix this precise
|
||||
/// non-manifold configuration.
|
||||
/// \cgalParamEnd
|
||||
/// \cgalNamedParamsEnd
|
||||
///
|
||||
/// \return the number of vertices created.
|
||||
template <typename TriangleMesh, typename NamedParameters>
|
||||
std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm,
|
||||
const NamedParameters& np)
|
||||
{
|
||||
CGAL_assertion(CGAL::is_triangle_mesh(tm));
|
||||
|
||||
using boost::get_param;
|
||||
using boost::choose_param;
|
||||
|
||||
typedef boost::graph_traits<TriangleMesh> GT;
|
||||
typedef typename GT::vertex_descriptor vertex_descriptor;
|
||||
typedef typename GT::halfedge_descriptor halfedge_descriptor;
|
||||
|
||||
typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::type VertexPointMap;
|
||||
VertexPointMap vpm = choose_param(get_param(np, internal_np::vertex_point),
|
||||
get_property_map(vertex_point, tm));
|
||||
|
||||
typedef typename boost::lookup_named_param_def <
|
||||
internal_np::vertex_is_constrained_t,
|
||||
NamedParameters,
|
||||
Constant_property_map<vertex_descriptor, bool> // default (no constraint pmap)
|
||||
> ::type VerticesMap;
|
||||
VerticesMap cmap
|
||||
= choose_param(get_param(np, internal_np::vertex_is_constrained),
|
||||
Constant_property_map<vertex_descriptor, bool>(false));
|
||||
|
||||
typedef typename boost::lookup_named_param_def <
|
||||
internal_np::output_iterator_t,
|
||||
NamedParameters,
|
||||
Emptyset_iterator
|
||||
> ::type Output_iterator;
|
||||
Output_iterator out
|
||||
= choose_param(get_param(np, internal_np::output_iterator),
|
||||
Emptyset_iterator());
|
||||
|
||||
internal::Vertex_collector<TriangleMesh, Output_iterator> dmap;
|
||||
boost::unordered_set<vertex_descriptor> vertices_handled;
|
||||
boost::unordered_set<halfedge_descriptor> halfedges_handled;
|
||||
|
||||
std::size_t nb_new_vertices=0;
|
||||
std::size_t nb_new_vertices = 0;
|
||||
|
||||
std::vector<halfedge_descriptor> non_manifold_cones;
|
||||
BOOST_FOREACH(halfedge_descriptor h, halfedges(tm))
|
||||
{
|
||||
if (halfedges_handled.insert(h).second)
|
||||
// If 'h' is not visited yet, we walk around the target of 'h' and mark these
|
||||
// halfedges as visited. Thus, if we are here and the target is already marked as visited,
|
||||
// it means that the vertex is non manifold.
|
||||
if(halfedges_handled.insert(h).second)
|
||||
{
|
||||
vertex_descriptor vd = target(h, tm);
|
||||
if ( !vertices_handled.insert(vd).second )
|
||||
if(!vertices_handled.insert(vd).second)
|
||||
{
|
||||
put(cmap, vd, true); // store the originals
|
||||
non_manifold_cones.push_back(h);
|
||||
}
|
||||
else
|
||||
{
|
||||
set_halfedge(vd, h, tm);
|
||||
halfedge_descriptor start=opposite(next(h, tm), tm);
|
||||
h=start;
|
||||
do{
|
||||
}
|
||||
|
||||
halfedge_descriptor start = opposite(next(h, tm), tm);
|
||||
h = start;
|
||||
do
|
||||
{
|
||||
halfedges_handled.insert(h);
|
||||
h=opposite(next(h, tm), tm);
|
||||
}while(h!=start);
|
||||
h = opposite(next(h, tm), tm);
|
||||
}
|
||||
while(h != start);
|
||||
}
|
||||
}
|
||||
|
||||
if (!non_manifold_cones.empty()) {
|
||||
if(!non_manifold_cones.empty())
|
||||
{
|
||||
BOOST_FOREACH(halfedge_descriptor h, non_manifold_cones)
|
||||
{
|
||||
halfedge_descriptor start = h;
|
||||
vertex_descriptor new_vd = add_vertex(tm);
|
||||
++nb_new_vertices;
|
||||
put(cmap, new_vd, true); // store the duplicates
|
||||
dmap.collect_vertices(target(h, tm), new_vd);
|
||||
put(vpm, new_vd, get(vpm, target(h, tm)));
|
||||
set_halfedge(new_vd, h, tm);
|
||||
do{
|
||||
do
|
||||
{
|
||||
set_target(h, new_vd, tm);
|
||||
h=opposite(next(h, tm), tm);
|
||||
} while(h!=start);
|
||||
h = opposite(next(h, tm), tm);
|
||||
}
|
||||
while(h != start);
|
||||
}
|
||||
dmap.dump(out);
|
||||
}
|
||||
|
||||
return nb_new_vertices;
|
||||
@@ -1343,12 +1597,9 @@ std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm, Vpm vpm)
|
||||
template <class TriangleMesh>
|
||||
std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm)
|
||||
{
|
||||
return duplicate_non_manifold_vertices(tm, get(vertex_point, tm));
|
||||
return duplicate_non_manifold_vertices(tm, parameters::all_default());
|
||||
}
|
||||
|
||||
/// \endcond
|
||||
|
||||
|
||||
/// \ingroup PMP_repairing_grp
|
||||
/// removes the isolated vertices from any polygon mesh.
|
||||
/// A vertex is considered isolated if it is not incident to any simplex
|
||||
|
||||
@@ -0,0 +1,334 @@
|
||||
// Copyright (c) 2015, 2018 GeometryFactory (France).
|
||||
// All rights reserved.
|
||||
//
|
||||
// This file is part of CGAL (www.cgal.org).
|
||||
// You can redistribute it and/or modify it under the terms of the GNU
|
||||
// General Public License as published by the Free Software Foundation,
|
||||
// either version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// Licensees holding a valid commercial license may use this file in
|
||||
// accordance with the commercial license agreement provided with the software.
|
||||
//
|
||||
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||||
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// $URL$
|
||||
// $Id$
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
//
|
||||
//
|
||||
// Author(s) : Konstantinos Katrioplas,
|
||||
// Mael Rouxel-Labbé
|
||||
|
||||
#ifndef CGAL_POLYGON_MESH_PROCESSING_SHAPE_PREDICATES_H
|
||||
#define CGAL_POLYGON_MESH_PROCESSING_SHAPE_PREDICATES_H
|
||||
|
||||
#include <CGAL/Polygon_mesh_processing/internal/named_function_params.h>
|
||||
#include <CGAL/Polygon_mesh_processing/internal/named_params_helper.h>
|
||||
|
||||
#include <CGAL/array.h>
|
||||
#include <CGAL/boost/graph/iterator.h>
|
||||
#include <CGAL/boost/graph/helpers.h>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/graph/graph_traits.hpp>
|
||||
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Polygon_mesh_processing {
|
||||
|
||||
/// \ingroup PMP_repairing_grp
|
||||
/// checks whether an edge is degenerate.
|
||||
/// An edge is considered degenerate if the geometric positions of its two extremities are identical.
|
||||
///
|
||||
/// @tparam PolygonMesh a model of `HalfedgeGraph`
|
||||
/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
|
||||
///
|
||||
/// @param e an edge of `pm`
|
||||
/// @param pm polygon mesh containing `e`
|
||||
/// @param np optional \ref pmp_namedparameters "Named Parameters" described below
|
||||
///
|
||||
/// \cgalNamedParamsBegin
|
||||
/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pm`.
|
||||
/// The type of this map is model of `ReadWritePropertyMap`.
|
||||
/// If this parameter is omitted, an internal property map for
|
||||
/// `CGAL::vertex_point_t` should be available in `PolygonMesh`
|
||||
/// \cgalParamEnd
|
||||
/// \cgalParamBegin{geom_traits} a geometric traits class instance.
|
||||
/// The traits class must provide the nested type `Point_3`,
|
||||
/// and the nested functor `Equal_3` to check whether two points are identical.
|
||||
/// \cgalParamEnd
|
||||
/// \cgalNamedParamsEnd
|
||||
///
|
||||
/// \return `true` if the edge `e` is degenerate, `false` otherwise.
|
||||
template <typename PolygonMesh, typename NamedParameters>
|
||||
bool is_degenerate_edge(typename boost::graph_traits<PolygonMesh>::edge_descriptor e,
|
||||
const PolygonMesh& pm,
|
||||
const NamedParameters& np)
|
||||
{
|
||||
using boost::get_param;
|
||||
using boost::choose_param;
|
||||
|
||||
typedef typename GetVertexPointMap<PolygonMesh, NamedParameters>::const_type VertexPointMap;
|
||||
VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point),
|
||||
get_const_property_map(vertex_point, pm));
|
||||
|
||||
typedef typename GetGeomTraits<PolygonMesh, NamedParameters>::type Traits;
|
||||
Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits());
|
||||
|
||||
return traits.equal_3_object()(get(vpmap, source(e, pm)), get(vpmap, target(e, pm)));
|
||||
}
|
||||
|
||||
template <typename PolygonMesh>
|
||||
bool is_degenerate_edge(typename boost::graph_traits<PolygonMesh>::edge_descriptor e,
|
||||
const PolygonMesh& pm)
|
||||
{
|
||||
return is_degenerate_edge(e, pm, parameters::all_default());
|
||||
}
|
||||
|
||||
/// \ingroup PMP_repairing_grp
|
||||
/// checks whether a triangle face is degenerate.
|
||||
/// A triangle face is considered degenerate if the geometric positions of its vertices are collinear.
|
||||
///
|
||||
/// @tparam TriangleMesh a model of `FaceGraph`
|
||||
/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
|
||||
///
|
||||
/// @param f a triangle face of `tm`
|
||||
/// @param tm a triangle mesh containing `f`
|
||||
/// @param np optional \ref pmp_namedparameters "Named Parameters" described below
|
||||
///
|
||||
/// \cgalNamedParamsBegin
|
||||
/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `tm`.
|
||||
/// The type of this map is model of `ReadWritePropertyMap`.
|
||||
/// If this parameter is omitted, an internal property map for
|
||||
/// `CGAL::vertex_point_t` should be available in `TriangleMesh`
|
||||
/// \cgalParamEnd
|
||||
/// \cgalParamBegin{geom_traits} a geometric traits class instance.
|
||||
/// The traits class must provide the nested functor `Collinear_3`
|
||||
/// to check whether three points are collinear.
|
||||
/// \cgalParamEnd
|
||||
/// \cgalNamedParamsEnd
|
||||
///
|
||||
/// \return `true` if the face `f` is degenerate, `false` otherwise.
|
||||
template <typename TriangleMesh, typename NamedParameters>
|
||||
bool is_degenerate_triangle_face(typename boost::graph_traits<TriangleMesh>::face_descriptor f,
|
||||
const TriangleMesh& tm,
|
||||
const NamedParameters& np)
|
||||
{
|
||||
CGAL_precondition(CGAL::is_triangle_mesh(tm));
|
||||
|
||||
using boost::get_param;
|
||||
using boost::choose_param;
|
||||
|
||||
typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::const_type VertexPointMap;
|
||||
VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point),
|
||||
get_const_property_map(vertex_point, tm));
|
||||
|
||||
typedef typename GetGeomTraits<TriangleMesh, NamedParameters>::type Traits;
|
||||
Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits());
|
||||
|
||||
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor h = halfedge(f, tm);
|
||||
|
||||
return traits.collinear_3_object()(get(vpmap, source(h, tm)),
|
||||
get(vpmap, target(h, tm)),
|
||||
get(vpmap, target(next(h, tm), tm)));
|
||||
}
|
||||
|
||||
template <typename TriangleMesh>
|
||||
bool is_degenerate_triangle_face(typename boost::graph_traits<TriangleMesh>::face_descriptor f,
|
||||
const TriangleMesh& tm)
|
||||
{
|
||||
return CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(f, tm, parameters::all_default());
|
||||
}
|
||||
|
||||
/// \ingroup PMP_repairing_grp
|
||||
/// checks whether a triangle face is needle.
|
||||
/// A triangle is said to be a <i>needle</i> if its longest edge is much longer than its shortest edge.
|
||||
///
|
||||
/// @tparam TriangleMesh a model of `FaceGraph`
|
||||
/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
|
||||
///
|
||||
/// @param f a triangle face of `tm`
|
||||
/// @param tm triangle mesh containing `f`
|
||||
/// @param threshold a bound on the ratio of the longest edge length and the shortest edge length
|
||||
/// @param np optional \ref pmp_namedparameters "Named Parameters" described below
|
||||
///
|
||||
/// \cgalNamedParamsBegin
|
||||
/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `tm`.
|
||||
/// The type of this map is model of `ReadWritePropertyMap`.
|
||||
/// If this parameter is omitted, an internal property map for
|
||||
/// `CGAL::vertex_point_t` should be available in `TriangleMesh`
|
||||
/// \cgalParamEnd
|
||||
/// \cgalParamBegin{geom_traits} a geometric traits class instance.
|
||||
/// The traits class must provide the nested type `FT` and
|
||||
/// the nested functor `Compute_squared_distance_3`.
|
||||
/// \cgalParamEnd
|
||||
/// \cgalNamedParamsEnd
|
||||
///
|
||||
/// \return the shortest halfedge if the triangle face is a needle, and a null halfedge otherwise.
|
||||
template <typename TriangleMesh, typename NamedParameters>
|
||||
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor
|
||||
is_needle_triangle_face(typename boost::graph_traits<TriangleMesh>::face_descriptor f,
|
||||
const TriangleMesh& tm,
|
||||
const double threshold,
|
||||
const NamedParameters& np)
|
||||
{
|
||||
CGAL_precondition(CGAL::is_triangle_mesh(tm));
|
||||
CGAL_precondition(threshold >= 1.);
|
||||
|
||||
using boost::get_param;
|
||||
using boost::choose_param;
|
||||
|
||||
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
|
||||
|
||||
typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::const_type VertexPointMap;
|
||||
VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point),
|
||||
get_const_property_map(vertex_point, tm));
|
||||
|
||||
typedef typename GetGeomTraits<TriangleMesh, NamedParameters>::type Traits;
|
||||
Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits());
|
||||
|
||||
typedef typename Traits::FT FT;
|
||||
|
||||
const halfedge_descriptor h0 = halfedge(f, tm);
|
||||
FT max_sq_length = - std::numeric_limits<FT>::max(),
|
||||
min_sq_length = std::numeric_limits<FT>::max();
|
||||
halfedge_descriptor min_h = boost::graph_traits<TriangleMesh>::null_halfedge();
|
||||
|
||||
BOOST_FOREACH(halfedge_descriptor h, halfedges_around_face(h0, tm))
|
||||
{
|
||||
const FT sq_length = traits.compute_squared_distance_3_object()(get(vpmap, source(h, tm)),
|
||||
get(vpmap, target(h, tm)));
|
||||
|
||||
if(max_sq_length < sq_length)
|
||||
max_sq_length = sq_length;
|
||||
|
||||
if(min_sq_length > sq_length)
|
||||
{
|
||||
min_h = h;
|
||||
min_sq_length = sq_length;
|
||||
}
|
||||
}
|
||||
|
||||
const FT sq_threshold = threshold * threshold;
|
||||
if(max_sq_length / min_sq_length >= sq_threshold)
|
||||
{
|
||||
CGAL_assertion(min_h != boost::graph_traits<TriangleMesh>::null_halfedge());
|
||||
return min_h;
|
||||
}
|
||||
else
|
||||
return boost::graph_traits<TriangleMesh>::null_halfedge();
|
||||
}
|
||||
|
||||
template <typename TriangleMesh>
|
||||
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor
|
||||
is_needle_triangle_face(typename boost::graph_traits<TriangleMesh>::face_descriptor f,
|
||||
const TriangleMesh& tm,
|
||||
const double threshold)
|
||||
{
|
||||
return is_needle_triangle_face(f, tm, threshold, parameters::all_default());
|
||||
}
|
||||
|
||||
/// \ingroup PMP_repairing_grp
|
||||
/// checks whether a triangle face is a cap.
|
||||
/// A triangle is said to be a <i>cap</i> if one of the its angles is close to `180` degrees.
|
||||
///
|
||||
/// @tparam TriangleMesh a model of `FaceGraph`
|
||||
/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
|
||||
///
|
||||
/// @param f a triangle face of `tm`
|
||||
/// @param tm triangle mesh containing `f`
|
||||
/// @param threshold the cosine of a minimum angle such that if `f` has an angle greater than this bound,
|
||||
/// it is a cap. The threshold is in range `[-1 0]` and corresponds to an angle
|
||||
/// between `90` and `180` degrees.
|
||||
/// @param np optional \ref pmp_namedparameters "Named Parameters" described below
|
||||
///
|
||||
/// \cgalNamedParamsBegin
|
||||
/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `tm`.
|
||||
/// The type of this map is model of `ReadWritePropertyMap`.
|
||||
/// If this parameter is omitted, an internal property map for
|
||||
/// `CGAL::vertex_point_t` should be available in `TriangleMesh`
|
||||
/// \cgalParamEnd
|
||||
/// \cgalParamBegin{geom_traits} a geometric traits class instance.
|
||||
/// The traits class must provide the nested type `Point_3` and
|
||||
/// the nested functors `Compute_squared_distance_3`, `Construct_vector_3`,
|
||||
/// and `Compute_scalar_product_3`.
|
||||
/// \cgalParamEnd
|
||||
/// \cgalNamedParamsEnd
|
||||
///
|
||||
/// \return the halfedge opposite of the largest angle if the face is a cap, and a null halfedge otherwise.
|
||||
template <typename TriangleMesh, typename NamedParameters>
|
||||
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor
|
||||
is_cap_triangle_face(typename boost::graph_traits<TriangleMesh>::face_descriptor f,
|
||||
const TriangleMesh& tm,
|
||||
const double threshold,
|
||||
const NamedParameters& np)
|
||||
{
|
||||
CGAL_precondition(CGAL::is_triangle_mesh(tm));
|
||||
CGAL_precondition(threshold >= -1.);
|
||||
CGAL_precondition(threshold <= 0.);
|
||||
|
||||
using boost::get_param;
|
||||
using boost::choose_param;
|
||||
|
||||
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
|
||||
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
|
||||
|
||||
typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::const_type VertexPointMap;
|
||||
VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point),
|
||||
get_const_property_map(vertex_point, tm));
|
||||
|
||||
typedef typename GetGeomTraits<TriangleMesh, NamedParameters>::type Traits;
|
||||
Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits());
|
||||
|
||||
typedef typename Traits::FT FT;
|
||||
typedef typename Traits::Vector_3 Vector_3;
|
||||
|
||||
const FT sq_threshold = threshold * threshold;
|
||||
const halfedge_descriptor h0 = halfedge(f, tm);
|
||||
|
||||
cpp11::array<FT, 3> sq_lengths;
|
||||
int pos = 0;
|
||||
BOOST_FOREACH(halfedge_descriptor h, halfedges_around_face(h0, tm))
|
||||
{
|
||||
sq_lengths[pos++] = traits.compute_squared_distance_3_object()(get(vpmap, source(h, tm)),
|
||||
get(vpmap, target(h, tm)));
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
BOOST_FOREACH(halfedge_descriptor h, halfedges_around_face(h0, tm))
|
||||
{
|
||||
const vertex_descriptor v0 = source(h, tm);
|
||||
const vertex_descriptor v1 = target(h, tm);
|
||||
const vertex_descriptor v2 = target(next(h, tm), tm);
|
||||
const Vector_3 a = traits.construct_vector_3_object()(get(vpmap, v1), get(vpmap, v2));
|
||||
const Vector_3 b = traits.construct_vector_3_object()(get(vpmap, v1), get(vpmap, v0));
|
||||
const FT dot_ab = traits.compute_scalar_product_3_object()(a, b);
|
||||
const bool neg_sp = (dot_ab <= 0);
|
||||
const FT sq_a = sq_lengths[(pos+1)%3];
|
||||
const FT sq_b = sq_lengths[pos];
|
||||
const FT sq_cos = dot_ab * dot_ab / (sq_a * sq_b);
|
||||
|
||||
if(neg_sp && sq_cos >= sq_threshold)
|
||||
return prev(h, tm);
|
||||
}
|
||||
return boost::graph_traits<TriangleMesh>::null_halfedge();
|
||||
}
|
||||
|
||||
template <typename TriangleMesh>
|
||||
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor
|
||||
is_cap_triangle_face(typename boost::graph_traits<TriangleMesh>::face_descriptor f,
|
||||
const TriangleMesh& tm,
|
||||
const double threshold)
|
||||
{
|
||||
return is_cap_triangle_face(f, tm, threshold, parameters::all_default());
|
||||
}
|
||||
|
||||
} } // end namespaces CGAL and PMP
|
||||
|
||||
#endif // CGAL_POLYGON_MESH_PROCESSING_SHAPE_PREDICATES_H
|
||||
Reference in New Issue
Block a user