mirror of https://github.com/CGAL/cgal
Various improvements/fixes to degenerate/needle/cap functions
This commit is contained in:
parent
fc41d58bfd
commit
49a971e9c2
|
|
@ -337,7 +337,7 @@ of a mesh independently.\n
|
|||
Parameter used to pass a visitor class to a function. Its type and behavior depend on the visited function.
|
||||
\n
|
||||
\b Type : `A class` \n
|
||||
\b Default Specific to the function visited
|
||||
\b Default : Specific to the function visited
|
||||
\cgalNPEnd
|
||||
|
||||
\cgalNPBegin{throw_on_self_intersection} \anchor PMP_throw_on_self_intersection
|
||||
|
|
@ -364,18 +364,13 @@ should be considered as part of the clipping volume or not.
|
|||
\b Default value is `true`
|
||||
\cgalNPEnd
|
||||
|
||||
|
||||
\cgalNPBegin{output_iterator} \anchor PMP_output_iterator
|
||||
Iterator where `std::vector<vertex_descriptor>` can be put.
|
||||
The first vertex of the vector is an input vertex that was non-manifold,
|
||||
the other vertices in the vertex are the new vertices created to fix
|
||||
the non-manifoldness.
|
||||
Parameter to pass an output iterator.
|
||||
\n
|
||||
\b Type : `iterator` \n
|
||||
\b Default `Emptyset_iterator`
|
||||
\b Type : a model of `OutputIterator` \n
|
||||
\b Default : `Emptyset_iterator`
|
||||
\cgalNPEnd
|
||||
|
||||
|
||||
\cgalNPTableEnd
|
||||
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2015 GeometryFactory (France).
|
||||
// Copyright (c) 2015, 2018 GeometryFactory (France).
|
||||
// All rights reserved.
|
||||
//
|
||||
// This file is part of CGAL (www.cgal.org).
|
||||
|
|
@ -17,7 +17,8 @@
|
|||
// SPDX-License-Identifier: GPL-3.0+
|
||||
//
|
||||
//
|
||||
// Author(s) : Konstantinos Katrioplas
|
||||
// Author(s) : Konstantinos Katrioplas,
|
||||
// Mael Rouxel-Labbé
|
||||
|
||||
#ifndef CGAL_POLYGON_MESH_PROCESSING_HELPERS_H
|
||||
#define CGAL_POLYGON_MESH_PROCESSING_HELPERS_H
|
||||
|
|
@ -25,86 +26,40 @@
|
|||
#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 {
|
||||
|
||||
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)
|
||||
{}
|
||||
};
|
||||
|
||||
// used only for testing
|
||||
template <typename PolygonMesh>
|
||||
void merge_identical_points(PolygonMesh& mesh,
|
||||
typename boost::graph_traits<PolygonMesh>::vertex_descriptor v_keep,
|
||||
typename boost::graph_traits<PolygonMesh>::vertex_descriptor v_rm)
|
||||
{
|
||||
typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
|
||||
halfedge_descriptor h = halfedge(v_rm, mesh);
|
||||
halfedge_descriptor start = h;
|
||||
|
||||
do{
|
||||
set_target(h, v_keep, mesh);
|
||||
h = opposite(next(h, mesh), mesh);
|
||||
} while( h != start );
|
||||
|
||||
remove_vertex(v_rm, mesh);
|
||||
}
|
||||
} // end internal
|
||||
|
||||
/// \ingroup PMP_repairing_grp
|
||||
/// checks whether a vertex is non-manifold.
|
||||
/// checks whether a vertex of a triangle mesh is non-manifold.
|
||||
///
|
||||
/// @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph`
|
||||
/// @tparam TriangleMesh a model of `HalfedgeListGraph`
|
||||
///
|
||||
/// @param v the vertex
|
||||
/// @param tm triangle mesh containing v
|
||||
/// @param v a vertex of `tm`
|
||||
/// @param tm a triangle mesh containing `v`
|
||||
///
|
||||
/// \return true if the vertrex is non-manifold
|
||||
template <typename PolygonMesh>
|
||||
bool is_non_manifold_vertex(typename boost::graph_traits<PolygonMesh>::vertex_descriptor v,
|
||||
const PolygonMesh& tm)
|
||||
/// \return `true` if the vertrex 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 boost::graph_traits<PolygonMesh> GT;
|
||||
typedef typename GT::halfedge_descriptor halfedge_descriptor;
|
||||
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);
|
||||
|
||||
|
|
@ -121,28 +76,28 @@ bool is_non_manifold_vertex(typename boost::graph_traits<PolygonMesh>::vertex_de
|
|||
|
||||
/// \ingroup PMP_repairing_grp
|
||||
/// checks whether an edge is degenerate.
|
||||
/// An edge is considered degenerate if the points of its vertices are identical.
|
||||
/// 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 the edge
|
||||
/// @param pm polygon mesh containing e
|
||||
/// @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 `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 `PolygonMesh`
|
||||
/// \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 2 points are identical
|
||||
/// \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 is degenerate
|
||||
/// \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,
|
||||
|
|
@ -154,12 +109,11 @@ bool is_degenerate_edge(typename boost::graph_traits<PolygonMesh>::edge_descript
|
|||
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());
|
||||
|
||||
if ( traits.equal_3_object()(get(vpmap, target(e, pm)), get(vpmap, source(e, pm))) )
|
||||
return true;
|
||||
return false;
|
||||
return traits.equal_3_object()(get(vpmap, source(e, pm)), get(vpmap, target(e, pm)));
|
||||
}
|
||||
|
||||
template <typename PolygonMesh>
|
||||
|
|
@ -171,34 +125,34 @@ bool is_degenerate_edge(typename boost::graph_traits<PolygonMesh>::edge_descript
|
|||
|
||||
/// \ingroup PMP_repairing_grp
|
||||
/// checks whether a triangle face is degenerate.
|
||||
/// A triangle face is degenerate if its points are collinear.
|
||||
/// 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 the triangle face
|
||||
/// @param tm triangle mesh containing f
|
||||
/// @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 `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`
|
||||
/// \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 :
|
||||
/// - `Collinear_3` to check whether 3 points are collinear
|
||||
/// The traits class must provide the nested functor `Collinear_3`
|
||||
/// to check whether three points are collinear.
|
||||
/// \cgalParamEnd
|
||||
/// \cgalNamedParamsEnd
|
||||
///
|
||||
/// \return true if the triangle face is degenerate
|
||||
/// \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_assertion(CGAL::is_triangle_mesh(tm));
|
||||
CGAL_precondition(CGAL::is_triangle_mesh(tm));
|
||||
|
||||
using boost::get_param;
|
||||
using boost::choose_param;
|
||||
|
|
@ -206,15 +160,15 @@ bool is_degenerate_triangle_face(typename boost::graph_traits<TriangleMesh>::fac
|
|||
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 hd = halfedge(f,tm);
|
||||
const typename Traits::Point_3& p1 = get(vpmap, target( hd, tm) );
|
||||
const typename Traits::Point_3& p2 = get(vpmap, target(next(hd, tm), tm) );
|
||||
const typename Traits::Point_3& p3 = get(vpmap, source( hd, tm) );
|
||||
return traits.collinear_3_object()(p1, p2, p3);
|
||||
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>
|
||||
|
|
@ -226,159 +180,185 @@ bool is_degenerate_triangle_face(typename boost::graph_traits<TriangleMesh>::fac
|
|||
|
||||
/// \ingroup PMP_repairing_grp
|
||||
/// checks whether a triangle face is needle.
|
||||
/// A triangle is needle if its longest edge is much longer than the shortest one.
|
||||
/// 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 the triangle face
|
||||
/// @param tm triangle mesh containing f
|
||||
/// @param threshold the cosine of an angle of f.
|
||||
/// The threshold is in range [0 1] and corresponds to
|
||||
/// angles between 0 and 90 degrees.
|
||||
/// @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 `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`
|
||||
/// \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`.
|
||||
/// The traits class must provide the nested type `FT` and
|
||||
/// the nested functor `Compute_squared_distance_3`.
|
||||
/// \cgalParamEnd
|
||||
/// \cgalNamedParamsEnd
|
||||
///
|
||||
/// \return true if the triangle face is a needle
|
||||
/// \return the smallest halfedge if the triangle face is a needle, and a null halfedge otherwise.
|
||||
template <typename TriangleMesh, typename NamedParameters>
|
||||
bool is_needle_triangle_face(typename boost::graph_traits<TriangleMesh>::face_descriptor f,
|
||||
const TriangleMesh& tm,
|
||||
const double threshold,
|
||||
const NamedParameters& np)
|
||||
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_assertion(CGAL::is_triangle_mesh(tm));
|
||||
CGAL_assertion(threshold >= 0);
|
||||
CGAL_assertion(threshold <= 1);
|
||||
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;
|
||||
typedef typename Traits::FT FT;
|
||||
typedef boost::graph_traits<TriangleMesh> GT;
|
||||
typedef typename GT::halfedge_descriptor halfedge_descriptor;
|
||||
typedef typename GT::vertex_descriptor vertex_descriptor;
|
||||
typedef typename boost::property_traits<VertexPointMap>::value_type Point_type;
|
||||
typedef typename Kernel_traits<Point_type>::Kernel::Vector_3 Vector;
|
||||
|
||||
BOOST_FOREACH(halfedge_descriptor h, halfedges_around_face(halfedge(f, tm), 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))
|
||||
{
|
||||
vertex_descriptor v0 = source(h, tm);
|
||||
vertex_descriptor v1 = target(h, tm);
|
||||
vertex_descriptor v2 = target(next(h, tm), tm);
|
||||
Vector a = get(vpmap, v0) - get (vpmap, v1);
|
||||
Vector b = get(vpmap, v2) - get(vpmap, v1);
|
||||
FT aa = a.squared_length();
|
||||
FT bb = b.squared_length();
|
||||
FT squared_dot_ab = ((a*b)*(a*b)) / (aa * bb);
|
||||
const FT sq_length = traits.compute_squared_distance_3_object()(get(vpmap, source(h, tm)),
|
||||
get(vpmap, target(h, tm)));
|
||||
|
||||
if(squared_dot_ab > threshold * threshold)
|
||||
return true;
|
||||
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;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
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>
|
||||
bool is_needle_triangle_face(typename boost::graph_traits<TriangleMesh>::face_descriptor f,
|
||||
const TriangleMesh& tm,
|
||||
const double threshold)
|
||||
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 a cap if it has an angle very close to 180 degrees.
|
||||
/// 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 the triangle face
|
||||
/// @param tm triangle mesh containing f
|
||||
/// @param threshold the cosine of an angle of f.
|
||||
/// The threshold is in range [-1 0] and corresponds to
|
||||
/// angles between 90 and 180 degrees.
|
||||
/// @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 `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`
|
||||
/// \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`
|
||||
/// The traits class must provide the nested type `Point_3`
|
||||
/// \cgalParamEnd
|
||||
/// \cgalNamedParamsEnd
|
||||
///
|
||||
/// \return true if the triangle face is a cap
|
||||
/// \return `true` if the triangle face is a cap
|
||||
template <typename TriangleMesh, typename NamedParameters>
|
||||
bool is_cap_triangle_face(typename boost::graph_traits<TriangleMesh>::face_descriptor f,
|
||||
const TriangleMesh& tm,
|
||||
const double threshold,
|
||||
const NamedParameters& np)
|
||||
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_assertion(CGAL::is_triangle_mesh(tm));
|
||||
CGAL_assertion(threshold >= -1);
|
||||
CGAL_assertion(threshold <= 0);
|
||||
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;
|
||||
typedef typename Traits::FT FT;
|
||||
typedef boost::graph_traits<TriangleMesh> GT;
|
||||
typedef typename GT::halfedge_descriptor halfedge_descriptor;
|
||||
typedef typename GT::vertex_descriptor vertex_descriptor;
|
||||
typedef typename boost::property_traits<VertexPointMap>::value_type Point_type;
|
||||
typedef typename Kernel_traits<Point_type>::Kernel::Vector_3 Vector;
|
||||
|
||||
BOOST_FOREACH(halfedge_descriptor h, halfedges_around_face(halfedge(f, tm), 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))
|
||||
{
|
||||
vertex_descriptor v0 = source(h, tm);
|
||||
vertex_descriptor v1 = target(h, tm);
|
||||
vertex_descriptor v2 = target(next(h, tm), tm);
|
||||
Vector a = get(vpmap, v0) - get (vpmap, v1);
|
||||
Vector b = get(vpmap, v2) - get(vpmap, v1);
|
||||
FT aa = a.squared_length();
|
||||
FT bb = b.squared_length();
|
||||
FT squared_dot_ab = ((a*b)*(a*b)) / (aa * bb);
|
||||
|
||||
if(squared_dot_ab > threshold * threshold)
|
||||
return true;
|
||||
sq_lengths[pos++] = traits.compute_squared_distance_3_object()(get(vpmap, source(h, tm)),
|
||||
get(vpmap, target(h, tm)));
|
||||
}
|
||||
return false;
|
||||
|
||||
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>
|
||||
bool is_cap_triangle_face(typename boost::graph_traits<TriangleMesh>::face_descriptor f,
|
||||
const TriangleMesh& tm,
|
||||
const double threshold)
|
||||
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_HELPERS_H
|
||||
|
||||
|
|
|
|||
|
|
@ -311,6 +311,7 @@ namespace internal {
|
|||
public:
|
||||
Incremental_remesher(PolygonMesh& pmesh
|
||||
, VertexPointMap& vpmap
|
||||
, const GeomTraits& gt
|
||||
, const bool protect_constraints
|
||||
, EdgeIsConstrainedMap ecmap
|
||||
, VertexIsConstrainedMap vcmap
|
||||
|
|
@ -319,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_()
|
||||
|
|
@ -350,9 +352,10 @@ namespace internal {
|
|||
|
||||
BOOST_FOREACH(face_descriptor f, face_range)
|
||||
{
|
||||
if (is_degenerate_triangle_face(f, mesh_)){
|
||||
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);
|
||||
|
|
@ -803,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
|
||||
}
|
||||
|
||||
|
|
@ -908,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
|
||||
|
||||
|
|
@ -950,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;
|
||||
|
|
@ -1444,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>
|
||||
|
|
@ -1573,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(face(h), mesh_))
|
||||
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(face(h), mesh_))
|
||||
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;
|
||||
|
|
@ -1639,11 +1635,15 @@ private:
|
|||
short_edges.insert(typename Bimap::value_type(hf, sqlen));
|
||||
}
|
||||
|
||||
if (!is_border(hf, mesh_)
|
||||
&& is_degenerate_triangle_face(face(h), mesh_))
|
||||
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(face(h), mesh_))
|
||||
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;
|
||||
|
|
@ -1660,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(face(h), mesh_))
|
||||
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;
|
||||
|
|
@ -1803,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;
|
||||
}
|
||||
|
|
@ -1815,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;
|
||||
}
|
||||
|
|
@ -1902,6 +1903,7 @@ private:
|
|||
private:
|
||||
PolygonMesh& mesh_;
|
||||
VertexPointMap& vpmap_;
|
||||
const GeomTraits& gt_;
|
||||
bool build_tree_;
|
||||
bool has_border_;
|
||||
std::vector<AABB_tree*> trees;
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
@ -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));
|
||||
|
|
@ -361,7 +364,7 @@ void split_long_edges(const EdgeRange& edges
|
|||
internal::Connected_components_pmap<PM, FIMap>,
|
||||
FIMap
|
||||
>
|
||||
remesher(pmesh, vpmap, false/*protect constraints*/, ecmap,
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -159,17 +159,27 @@ struct Less_vertex_point{
|
|||
}
|
||||
};
|
||||
|
||||
template <class TriangleMesh, class OutputIterator, class NamedParameters>
|
||||
OutputIterator
|
||||
degenerate_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(tm))
|
||||
{
|
||||
if(is_degenerate_triangle_face(fd, tm, np))
|
||||
*out++ = fd;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template <class TriangleMesh, class OutputIterator>
|
||||
OutputIterator
|
||||
degenerate_faces(const TriangleMesh& tm, OutputIterator out)
|
||||
{
|
||||
typedef typename boost::graph_traits<TriangleMesh>::face_descriptor face_descriptor;
|
||||
BOOST_FOREACH(face_descriptor fd, faces(tm))
|
||||
{
|
||||
if ( is_degenerate_triangle_face(fd, tm) )
|
||||
*out++=fd;
|
||||
}
|
||||
return out;
|
||||
return degenerate_faces(tm, out, CGAL::parameters::all_default());
|
||||
}
|
||||
|
||||
// this function remove a border edge even if it does not satisfy the link condition.
|
||||
|
|
@ -717,9 +727,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, np))
|
||||
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
|
||||
|
|
@ -1274,6 +1283,45 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh)
|
|||
CGAL::Polygon_mesh_processing::parameters::all_default());
|
||||
}
|
||||
|
||||
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
|
||||
/// duplicates all non-manifold vertices of the input mesh.
|
||||
///
|
||||
|
|
@ -1284,22 +1332,22 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh)
|
|||
/// @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`.
|
||||
/// \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 `PolygonMesh`
|
||||
/// `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} an output iterator where `std::vector<vertex_descriptor>` can be put.
|
||||
/// The first vertex of the vector is an input vertex that was non-manifold,
|
||||
/// the other vertices in the vertex are the new vertices created to fix
|
||||
/// the non-manifoldness.
|
||||
/// \cgalParamBegin{output_iterator} a model of `OutputIterator` with value type
|
||||
/// `std::vector<vertex_descriptor>`. The first vertex of the vector is a non-manifold vertex
|
||||
/// of the input mesh, followed by the new vertices that were created to fix the non-manifoldness.
|
||||
/// \cgalParamEnd
|
||||
/// \cgalNamedParamsEnd
|
||||
///
|
||||
/// \return the number of vertices created
|
||||
/// \return the number of vertices created.
|
||||
template <typename TriangleMesh, typename NamedParameters>
|
||||
std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm,
|
||||
const NamedParameters& np)
|
||||
|
|
@ -1315,7 +1363,7 @@ std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm,
|
|||
|
||||
typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::type VertexPointMap;
|
||||
VertexPointMap vpm = choose_param(get_param(np, internal_np::vertex_point),
|
||||
get_property_map(vertex_point, tm));
|
||||
get_property_map(vertex_point, tm));
|
||||
|
||||
typedef typename boost::lookup_named_param_def <
|
||||
internal_np::vertex_is_constrained_t,
|
||||
|
|
@ -1333,37 +1381,46 @@ std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm,
|
|||
> ::type Output_iterator;
|
||||
Output_iterator out
|
||||
= choose_param(get_param(np, internal_np::output_iterator),
|
||||
Emptyset_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;
|
||||
|
|
@ -1373,13 +1430,16 @@ std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm,
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
OFF
|
||||
9 3 0
|
||||
0 0 0
|
||||
1 0 0
|
||||
1 1 0
|
||||
0 0 1
|
||||
1 0 1
|
||||
10 10 1
|
||||
0 0 2
|
||||
1 0 2
|
||||
-0.99619469809 0.08715574274 2
|
||||
3 0 1 2
|
||||
3 3 4 5
|
||||
3 6 7 8
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,142 +1,248 @@
|
|||
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||
|
||||
#include <CGAL/Surface_mesh.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include <CGAL/Polygon_mesh_processing/helpers.h>
|
||||
#include <CGAL/Polygon_mesh_processing/repair.h>
|
||||
|
||||
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
|
||||
typedef CGAL::Surface_mesh<K::Point_3> Surface_mesh;
|
||||
#include <boost/math/constants/constants.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
|
||||
typedef K::FT FT;
|
||||
typedef K::Point_3 Point_3;
|
||||
typedef CGAL::Surface_mesh<Point_3> Surface_mesh;
|
||||
|
||||
void check_edge_degeneracy(const char* fname)
|
||||
{
|
||||
std::ifstream input(fname);
|
||||
std::cout << "test edge degeneracy...";
|
||||
|
||||
typedef typename boost::graph_traits<Surface_mesh>::edge_descriptor edge_descriptor;
|
||||
|
||||
std::ifstream input(fname);
|
||||
Surface_mesh mesh;
|
||||
if (!input || !(input >> mesh) || mesh.is_empty()) {
|
||||
std::cerr << fname << " is not a valid off file.\n";
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
}
|
||||
typedef typename boost::graph_traits<Surface_mesh>::edge_descriptor edge_descriptor;
|
||||
std::vector<edge_descriptor> all_edges(edges(mesh).begin(), edges(mesh).end());
|
||||
|
||||
CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[0], mesh));
|
||||
CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[1], mesh));
|
||||
CGAL_assertion(CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[2], mesh));
|
||||
assert(!CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[0], mesh));
|
||||
assert(!CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[1], mesh));
|
||||
assert(CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[2], mesh));
|
||||
std::cout << "done" << std::endl;
|
||||
}
|
||||
|
||||
void check_triangle_face_degeneracy(const char* fname)
|
||||
{
|
||||
std::ifstream input(fname);
|
||||
std::cout << "test face degeneracy...";
|
||||
|
||||
typedef typename boost::graph_traits<Surface_mesh>::face_descriptor face_descriptor;
|
||||
|
||||
std::ifstream input(fname);
|
||||
Surface_mesh mesh;
|
||||
if (!input || !(input >> mesh) || mesh.is_empty()) {
|
||||
std::cerr << fname << " is not a valid off file.\n";
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
typedef typename boost::graph_traits<Surface_mesh>::face_descriptor face_descriptor;
|
||||
std::vector<face_descriptor> all_faces(faces(mesh).begin(), faces(mesh).end());
|
||||
CGAL_assertion(CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[0], mesh));
|
||||
CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[1], mesh));
|
||||
CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[2], mesh));
|
||||
CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[3], mesh));
|
||||
assert(CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[0], mesh));
|
||||
assert(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[1], mesh));
|
||||
assert(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[2], mesh));
|
||||
assert(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[3], mesh));
|
||||
|
||||
std::cout << "done" << std::endl;
|
||||
}
|
||||
|
||||
// tests repair.h
|
||||
void test_vertices_merge_and_duplication(const char* fname)
|
||||
// tests merge_and_duplication
|
||||
template <typename PolygonMesh>
|
||||
void merge_identical_points(typename boost::graph_traits<PolygonMesh>::vertex_descriptor v_keep,
|
||||
typename boost::graph_traits<PolygonMesh>::vertex_descriptor v_rm,
|
||||
PolygonMesh& mesh)
|
||||
{
|
||||
std::ifstream input(fname);
|
||||
Surface_mesh mesh;
|
||||
if (!input || !(input >> mesh) || mesh.is_empty()) {
|
||||
std::cerr << fname << " is not a valid off file.\n";
|
||||
exit(1);
|
||||
typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
|
||||
|
||||
halfedge_descriptor h = halfedge(v_rm, mesh);
|
||||
halfedge_descriptor start = h;
|
||||
|
||||
do
|
||||
{
|
||||
set_target(h, v_keep, mesh);
|
||||
h = opposite(next(h, mesh), mesh);
|
||||
}
|
||||
const std::size_t initial_vertices = vertices(mesh).size();
|
||||
while( h != start );
|
||||
|
||||
// create non-manifold vertex
|
||||
typedef typename boost::graph_traits<Surface_mesh>::vertex_descriptor vertex_descriptor;
|
||||
std::vector<vertex_descriptor> all_vertices(vertices(mesh).begin(), vertices(mesh).end());
|
||||
CGAL::Polygon_mesh_processing::internal::merge_identical_points(mesh, all_vertices[1], all_vertices[7]);
|
||||
|
||||
const std::size_t vertices_after_merge = vertices(mesh).size();
|
||||
CGAL_assertion(vertices_after_merge == initial_vertices - 1);
|
||||
|
||||
std::vector< std::vector<vertex_descriptor> > duplicated_vertices;
|
||||
CGAL::Polygon_mesh_processing::duplicate_non_manifold_vertices(mesh,
|
||||
CGAL::parameters::output_iterator(std::back_inserter(duplicated_vertices)));
|
||||
const std::size_t final_vertices_size = vertices(mesh).size();
|
||||
CGAL_assertion(final_vertices_size == vertices_after_merge + 1);
|
||||
CGAL_assertion(final_vertices_size == initial_vertices);
|
||||
CGAL_assertion(duplicated_vertices.size() == 2);
|
||||
remove_vertex(v_rm, mesh);
|
||||
}
|
||||
|
||||
void test_vertex_non_manifoldness(const char* fname)
|
||||
{
|
||||
std::cout << "test vertex non manifoldness...";
|
||||
|
||||
typedef typename boost::graph_traits<Surface_mesh>::vertex_descriptor vertex_descriptor;
|
||||
typedef typename boost::graph_traits<Surface_mesh>::vertices_size_type size_type;
|
||||
|
||||
std::ifstream input(fname);
|
||||
Surface_mesh mesh;
|
||||
if (!input || !(input >> mesh) || mesh.is_empty()) {
|
||||
std::cerr << fname << " is not a valid off file.\n";
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
size_type ini_nv = num_vertices(mesh);
|
||||
|
||||
// create non-manifold vertex
|
||||
typedef typename boost::graph_traits<Surface_mesh>::vertex_descriptor vertex_descriptor;
|
||||
std::vector<vertex_descriptor> all_vertices(vertices(mesh).begin(), vertices(mesh).end());
|
||||
CGAL::Polygon_mesh_processing::internal::merge_identical_points(mesh, all_vertices[1], all_vertices[7]);
|
||||
std::vector<vertex_descriptor> vertices_with_non_manifold(vertices(mesh).begin(), vertices(mesh).end());
|
||||
CGAL_assertion(vertices_with_non_manifold.size() == all_vertices.size() - 1);
|
||||
Surface_mesh::Vertex_index vertex_to_merge_onto(1);
|
||||
Surface_mesh::Vertex_index vertex_to_merge(7);
|
||||
merge_identical_points(vertex_to_merge_onto, vertex_to_merge, mesh);
|
||||
mesh.collect_garbage();
|
||||
|
||||
BOOST_FOREACH(std::size_t iv, vertices(mesh))
|
||||
assert(num_vertices(mesh) == ini_nv - 1);
|
||||
|
||||
BOOST_FOREACH(vertex_descriptor v, vertices(mesh))
|
||||
{
|
||||
vertex_descriptor v = vertices_with_non_manifold[iv];
|
||||
if(iv == 1)
|
||||
CGAL_assertion(CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, mesh));
|
||||
if(v == vertex_to_merge_onto)
|
||||
assert(CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, mesh));
|
||||
else
|
||||
CGAL_assertion(!CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, mesh));
|
||||
assert(!CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, mesh));
|
||||
}
|
||||
|
||||
std::cout << "done" << std::endl;
|
||||
}
|
||||
|
||||
void test_needle(const char* fname)
|
||||
void test_vertices_merge_and_duplication(const char* fname)
|
||||
{
|
||||
std::cout << "test non manifold vertex duplication...";
|
||||
|
||||
typedef typename boost::graph_traits<Surface_mesh>::vertex_descriptor vertex_descriptor;
|
||||
|
||||
std::ifstream input(fname);
|
||||
Surface_mesh mesh;
|
||||
if (!input || !(input >> mesh) || mesh.is_empty()) {
|
||||
std::cerr << fname << " is not a valid off file.\n";
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
}
|
||||
const std::size_t initial_vertices = num_vertices(mesh);
|
||||
|
||||
const double threshold = 0.8;
|
||||
BOOST_FOREACH(typename boost::graph_traits<Surface_mesh>::face_descriptor f, faces(mesh))
|
||||
{
|
||||
CGAL_assertion(CGAL::Polygon_mesh_processing::is_needle_triangle_face(f, mesh, threshold));
|
||||
}
|
||||
// create non-manifold vertex
|
||||
Surface_mesh::Vertex_index vertex_to_merge_onto(1);
|
||||
Surface_mesh::Vertex_index vertex_to_merge(7);
|
||||
Surface_mesh::Vertex_index vertex_to_merge_2(14);
|
||||
Surface_mesh::Vertex_index vertex_to_merge_3(21);
|
||||
|
||||
Surface_mesh::Vertex_index vertex_to_merge_onto_2(2);
|
||||
Surface_mesh::Vertex_index vertex_to_merge_4(8);
|
||||
|
||||
merge_identical_points(vertex_to_merge_onto, vertex_to_merge, mesh);
|
||||
merge_identical_points(vertex_to_merge_onto, vertex_to_merge_2, mesh);
|
||||
merge_identical_points(vertex_to_merge_onto, vertex_to_merge_3, mesh);
|
||||
merge_identical_points(vertex_to_merge_onto_2, vertex_to_merge_4, mesh);
|
||||
mesh.collect_garbage();
|
||||
|
||||
const std::size_t vertices_after_merge = num_vertices(mesh);
|
||||
assert(vertices_after_merge == initial_vertices - 4);
|
||||
|
||||
std::vector<std::vector<vertex_descriptor> > duplicated_vertices;
|
||||
CGAL::Polygon_mesh_processing::duplicate_non_manifold_vertices(mesh,
|
||||
CGAL::parameters::output_iterator(std::back_inserter(duplicated_vertices)));
|
||||
|
||||
const std::size_t final_vertices_size = vertices(mesh).size();
|
||||
assert(final_vertices_size == initial_vertices);
|
||||
assert(duplicated_vertices.size() == 2); // two non-manifold vertex
|
||||
assert(duplicated_vertices.front().size() == 4);
|
||||
assert(duplicated_vertices.back().size() == 2);
|
||||
|
||||
std::cout << "done" << std::endl;
|
||||
}
|
||||
|
||||
void test_cap(const char* fname)
|
||||
void test_needles_and_caps(const char* fname)
|
||||
{
|
||||
std::cout << "test needles&caps...";
|
||||
|
||||
namespace PMP = CGAL::Polygon_mesh_processing;
|
||||
|
||||
typedef typename boost::graph_traits<Surface_mesh>::halfedge_descriptor halfedge_descriptor;
|
||||
typedef typename boost::graph_traits<Surface_mesh>::face_iterator face_iterator;
|
||||
typedef typename boost::graph_traits<Surface_mesh>::face_descriptor face_descriptor;
|
||||
|
||||
std::ifstream input(fname);
|
||||
Surface_mesh mesh;
|
||||
if (!input || !(input >> mesh) || mesh.is_empty()) {
|
||||
std::cerr << fname << " is not a valid off file.\n";
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
const double threshold = -0.8;
|
||||
BOOST_FOREACH(typename boost::graph_traits<Surface_mesh>::face_descriptor f, faces(mesh))
|
||||
{
|
||||
CGAL_assertion(CGAL::Polygon_mesh_processing::is_cap_triangle_face(f, mesh, threshold));
|
||||
}
|
||||
const FT eps = std::numeric_limits<FT>::epsilon();
|
||||
|
||||
face_iterator fit, fend;
|
||||
boost::tie(fit, fend) = faces(mesh);
|
||||
|
||||
// (0 0 0) -- (1 0 0) -- (1 1 0) (90° cap angle)
|
||||
face_descriptor f = *fit;
|
||||
halfedge_descriptor res = PMP::is_needle_triangle_face(f, mesh, 2/*needle_threshold*/);
|
||||
assert(res == boost::graph_traits<Surface_mesh>::null_halfedge()); // not a needle
|
||||
res = PMP::is_needle_triangle_face(f, mesh, CGAL::sqrt(FT(2) - eps)/*needle_threshold*/);
|
||||
assert(res != boost::graph_traits<Surface_mesh>::null_halfedge()); // is a needle
|
||||
|
||||
res = PMP::is_cap_triangle_face(f, mesh, 0./*cos(pi/2)*/);
|
||||
assert(mesh.point(target(res, mesh)) == CGAL::ORIGIN);
|
||||
res = PMP::is_cap_triangle_face(f, mesh, std::cos(91 * CGAL_PI / 180));
|
||||
assert(res == boost::graph_traits<Surface_mesh>::null_halfedge()); res = PMP::is_cap_triangle_face(f, mesh, std::cos(boost::math::constants::two_thirds_pi<FT>()));
|
||||
assert(res == boost::graph_traits<Surface_mesh>::null_halfedge());
|
||||
++ fit;
|
||||
|
||||
// (0 0 1) -- (1 0 1) -- (10 10 1)
|
||||
f = *fit;
|
||||
res = PMP::is_needle_triangle_face(f, mesh, 20);
|
||||
assert(res == boost::graph_traits<Surface_mesh>::null_halfedge());
|
||||
res = PMP::is_needle_triangle_face(f, mesh, 10 * CGAL::sqrt(FT(2) - eps));
|
||||
assert(mesh.point(target(res, mesh)) == Point_3(1,0,1));
|
||||
res = PMP::is_needle_triangle_face(f, mesh, 1);
|
||||
assert(mesh.point(target(res, mesh)) == Point_3(1,0,1));
|
||||
|
||||
res = PMP::is_cap_triangle_face(f, mesh, 0./*cos(pi/2)*/);
|
||||
assert(mesh.point(target(res, mesh)) == Point_3(0,0,1));
|
||||
res = PMP::is_cap_triangle_face(f, mesh, std::cos(boost::math::constants::two_thirds_pi<FT>()));
|
||||
assert(mesh.point(target(res, mesh)) == Point_3(0,0,1));
|
||||
res = PMP::is_cap_triangle_face(f, mesh, std::cos(boost::math::constants::three_quarters_pi<FT>()));
|
||||
assert(res == boost::graph_traits<Surface_mesh>::null_halfedge());
|
||||
++ fit;
|
||||
|
||||
// (0 0 2) -- (1 0 2) -- (-0.99619469809 0.08715574274 2) (175° cap angle)
|
||||
f = *fit;
|
||||
res = PMP::is_needle_triangle_face(f, mesh, 2);
|
||||
assert(res == boost::graph_traits<Surface_mesh>::null_halfedge());
|
||||
res = PMP::is_needle_triangle_face(f, mesh, 1.9);
|
||||
assert(mesh.point(target(res, mesh)) == Point_3(0,0,2) ||
|
||||
mesh.point(target(res, mesh)) == Point_3(1,0,2));
|
||||
res = PMP::is_needle_triangle_face(f, mesh, 1);
|
||||
assert(mesh.point(target(res, mesh)) == Point_3(0,0,2) ||
|
||||
mesh.point(target(res, mesh)) == Point_3(1,0,2));
|
||||
|
||||
res = PMP::is_cap_triangle_face(f, mesh, 0./*cos(pi/2)*/);
|
||||
assert(res != boost::graph_traits<Surface_mesh>::null_halfedge() &&
|
||||
mesh.point(target(res, mesh)) != Point_3(0,0,2) &&
|
||||
mesh.point(target(res, mesh)) != Point_3(1,0,2));
|
||||
res = PMP::is_cap_triangle_face(f, mesh, std::cos(boost::math::constants::two_thirds_pi<FT>()));
|
||||
assert(res != boost::graph_traits<Surface_mesh>::null_halfedge());
|
||||
res = PMP::is_cap_triangle_face(f, mesh, std::cos(175 * CGAL_PI / 180));
|
||||
assert(res != boost::graph_traits<Surface_mesh>::null_halfedge());
|
||||
res = PMP::is_cap_triangle_face(f, mesh, std::cos(176 * CGAL_PI / 180));
|
||||
assert(res == boost::graph_traits<Surface_mesh>::null_halfedge());
|
||||
|
||||
std::cout << "done" << std::endl;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
check_edge_degeneracy("data_degeneracies/degtri_edge.off");
|
||||
check_triangle_face_degeneracy("data_degeneracies/degtri_four.off");
|
||||
test_vertices_merge_and_duplication("data_degeneracies/non_manifold_vertex_duplicated.off");
|
||||
test_vertex_non_manifoldness("data_degeneracies/non_manifold_vertex_duplicated.off");
|
||||
test_needle("data_degeneracies/needle.off");
|
||||
test_cap("data_degeneracies/cap.off");
|
||||
test_vertex_non_manifoldness("data/blobby.off");
|
||||
test_vertices_merge_and_duplication("data/blobby.off");
|
||||
test_needles_and_caps("data_degeneracies/caps_and_needles.off");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -742,12 +742,10 @@ public Q_SLOTS:
|
|||
//Edition mode
|
||||
case 1:
|
||||
{
|
||||
VPmap vpmap = get(CGAL::vertex_point, *selection_item->polyhedron());
|
||||
bool is_valid = true;
|
||||
BOOST_FOREACH(boost::graph_traits<Face_graph>::face_descriptor fd, faces(*selection_item->polyhedron()))
|
||||
{
|
||||
if (CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(fd,
|
||||
*selection_item->polyhedron()))
|
||||
if (CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(fd, *selection_item->polyhedron()))
|
||||
{
|
||||
is_valid = false;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1682,7 +1682,7 @@ QString Scene_polyhedron_item::computeStats(int type)
|
|||
if (d->poly->is_pure_triangle())
|
||||
{
|
||||
if (d->number_of_degenerated_faces == (unsigned int)(-1))
|
||||
d->number_of_degenerated_faces = nb_degenerate_faces(d->poly, get(CGAL::vertex_point, *(d->poly)));
|
||||
d->number_of_degenerated_faces = nb_degenerate_faces(d->poly);
|
||||
return QString::number(d->number_of_degenerated_faces);
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -2032,8 +2032,9 @@ bool Scene_polyhedron_selection_item_priv::canAddFace(fg_halfedge_descriptor hc,
|
|||
found = true;
|
||||
fg_halfedge_descriptor res =
|
||||
CGAL::Euler::add_face_to_border(t,hc, *item->polyhedron());
|
||||
fg_face_descriptor resf = face(res, *item->polyhedron());
|
||||
|
||||
if(CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(res, *item->polyhedron()))
|
||||
if(CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(resf, *item->polyhedron()))
|
||||
{
|
||||
CGAL::Euler::remove_face(res, *item->polyhedron());
|
||||
tempInstructions("Edge not selected : resulting facet is degenerated.",
|
||||
|
|
|
|||
|
|
@ -1502,7 +1502,7 @@ QString Scene_surface_mesh_item::computeStats(int type)
|
|||
if(is_triangle_mesh(*d->smesh_))
|
||||
{
|
||||
if (d->number_of_degenerated_faces == (unsigned int)(-1))
|
||||
d->number_of_degenerated_faces = nb_degenerate_faces(d->smesh_, get(CGAL::vertex_point, *(d->smesh_)));
|
||||
d->number_of_degenerated_faces = nb_degenerate_faces(d->smesh_);
|
||||
return QString::number(d->number_of_degenerated_faces);
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
#ifndef POLYHEDRON_DEMO_STATISTICS_HELPERS_H
|
||||
#define POLYHEDRON_DEMO_STATISTICS_HELPERS_H
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <boost/accumulators/accumulators.hpp>
|
||||
#include <boost/accumulators/statistics/stats.hpp>
|
||||
#include <boost/accumulators/statistics/mean.hpp>
|
||||
|
|
@ -10,12 +8,15 @@
|
|||
#include <boost/accumulators/statistics/max.hpp>
|
||||
#include <boost/accumulators/statistics/median.hpp>
|
||||
#include <CGAL/squared_distance_3_0.h>
|
||||
#include <map>
|
||||
#include <boost/property_map/property_map.hpp>
|
||||
|
||||
#include <CGAL/Polygon_mesh_processing/repair.h>
|
||||
#include <CGAL/Polygon_mesh_processing/helpers.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
template<typename Mesh>
|
||||
void angles(Mesh* poly, double& mini, double& maxi, double& ave)
|
||||
|
|
@ -84,19 +85,15 @@ void edges_length(Mesh* poly,
|
|||
mid = extract_result< tag::median >(acc);
|
||||
}
|
||||
|
||||
template<typename Mesh, typename VPmap>
|
||||
unsigned int nb_degenerate_faces(Mesh* poly, VPmap vpmap)
|
||||
template<typename Mesh>
|
||||
unsigned int nb_degenerate_faces(Mesh* poly)
|
||||
{
|
||||
typedef typename boost::graph_traits<Mesh>::face_descriptor face_descriptor;
|
||||
typedef typename CGAL::Kernel_traits< typename boost::property_traits<VPmap>::value_type >::Kernel Traits;
|
||||
|
||||
unsigned int nb = 0;
|
||||
BOOST_FOREACH(face_descriptor f, faces(*poly))
|
||||
{
|
||||
if (CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(f, *poly))
|
||||
++nb;
|
||||
}
|
||||
return nb;
|
||||
std::vector<face_descriptor> degenerate_faces;
|
||||
CGAL::Polygon_mesh_processing::degenerate_faces(*poly, std::back_inserter(degenerate_faces));
|
||||
|
||||
return static_cast<unsigned int>(degenerate_faces.size());
|
||||
}
|
||||
|
||||
template<typename Mesh>
|
||||
|
|
|
|||
Loading…
Reference in New Issue