mirror of https://github.com/CGAL/cgal
Merge branch 'maxGimeno/PMP_orient_connected_components-GF' into cgal/master
This commit is contained in:
commit
d1597f2fb5
|
|
@ -56,6 +56,7 @@ CGAL_add_named_parameter(number_of_points_per_edge_t, number_of_points_per_edge,
|
|||
CGAL_add_named_parameter(number_of_points_on_edges_t, number_of_points_on_edges, number_of_points_on_edges)
|
||||
CGAL_add_named_parameter(nb_points_per_area_unit_t, nb_points_per_area_unit, number_of_points_per_area_unit)
|
||||
CGAL_add_named_parameter(nb_points_per_distance_unit_t, nb_points_per_distance_unit, number_of_points_per_distance_unit)
|
||||
CGAL_add_named_parameter(outward_orientation_t, outward_orientation, outward_orientation)
|
||||
|
||||
// List of named parameters that we use in the package 'Surface Mesh Simplification'
|
||||
CGAL_add_named_parameter(get_cost_policy_t, get_cost_policy, get_cost)
|
||||
|
|
|
|||
|
|
@ -69,6 +69,10 @@ Release date: April 2018
|
|||
|
||||
### Polygon Mesh Processing
|
||||
|
||||
- Added two functions for orienting connected components :
|
||||
- `CGAL::Polygon_mesh_processing::orient()`
|
||||
- `CGAL::Polygon_mesh_processing::orient_to_bound_a_volume()`
|
||||
|
||||
- Added new functions for feature detection and feature-guided
|
||||
segmentation:
|
||||
- `CGAL::Polygon_mesh_processing::detect_sharp_edges()`
|
||||
|
|
|
|||
|
|
@ -295,6 +295,14 @@ If this parameter is not provided, the perturbation is not deterministic
|
|||
<b>Type:</b> `unsigned int` \n
|
||||
<b>Default:</b> the random number generator is initialized with `CGAL::Random()`
|
||||
\cgalNPEnd
|
||||
|
||||
\cgalNPBegin{outward_orientation} \anchor PMP_outward_orientation
|
||||
Parameter used in orientation functions to choose between an outward or inward orientation.
|
||||
\n
|
||||
\b Type : `bool` \n
|
||||
\b Default value is `true`
|
||||
\cgalNPEnd
|
||||
|
||||
\cgalNPTableEnd
|
||||
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -109,6 +109,8 @@ and provides a list of the parameters that are used in this package.
|
|||
- `CGAL::Polygon_mesh_processing::is_outward_oriented()`
|
||||
- `CGAL::Polygon_mesh_processing::reverse_face_orientations()`
|
||||
- `CGAL::Polygon_mesh_processing::orient_polygon_soup()`
|
||||
- `CGAL::Polygon_mesh_processing::orient()`
|
||||
- `CGAL::Polygon_mesh_processing::orient_to_bound_a_volume()`
|
||||
|
||||
## Combinatorial Repairing Functions ##
|
||||
- \link PMP_repairing_grp `CGAL::Polygon_mesh_processing::stitch_borders()` \endlink
|
||||
|
|
|
|||
|
|
@ -430,6 +430,13 @@ As a consequence, the normal computed for each face (see Section
|
|||
|
||||
The \ref PolygonSoupExample puts these functions at work on a polygon soup.
|
||||
|
||||
The function `CGAL::Polygon_mesh_processing::orient()` makes each connected component
|
||||
of a closed polygon mesh outward or inward oriented.
|
||||
|
||||
The function `CGAL::Polygon_mesh_processing::orient_to_bound_a_volume()` orients
|
||||
the connected components of a closed polygon mesh so that it bounds a volume
|
||||
(see \ref coref_def_subsec for the precise definition).
|
||||
|
||||
|
||||
****************************************
|
||||
\section PMPRepairing Combinatorial Repairing
|
||||
|
|
|
|||
|
|
@ -150,6 +150,7 @@ bool recursive_does_bound_a_volume(const TriangleMesh& tm,
|
|||
* \cgalParamEnd
|
||||
* \cgalNamedParamsEnd
|
||||
*
|
||||
* \see `CGAL::Polygon_mesh_processing::orient_to_bound_a_volume()`
|
||||
*/
|
||||
template <class TriangleMesh, class NamedParameters>
|
||||
bool does_bound_a_volume(const TriangleMesh& tm, const NamedParameters& np)
|
||||
|
|
|
|||
|
|
@ -27,16 +27,18 @@
|
|||
|
||||
|
||||
#include <algorithm>
|
||||
#include <CGAL/Polygon_mesh_processing/connected_components.h>
|
||||
#include <CGAL/Polygon_mesh_processing/compute_normal.h>
|
||||
#include <CGAL/Polygon_mesh_processing/internal/named_function_params.h>
|
||||
#include <CGAL/Polygon_mesh_processing/internal/named_params_helper.h>
|
||||
#include <CGAL/Side_of_triangle_mesh.h>
|
||||
#include <CGAL/Projection_traits_xy_3.h>
|
||||
#include <CGAL/boost/graph/helpers.h>
|
||||
#include <CGAL/boost/graph/iterator.h>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/unordered_set.hpp>
|
||||
|
||||
#include <boost/dynamic_bitset.hpp>
|
||||
namespace CGAL {
|
||||
|
||||
namespace Polygon_mesh_processing {
|
||||
|
|
@ -253,7 +255,7 @@ void reverse_face_orientations(PolygonMesh& pmesh)
|
|||
typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
|
||||
BOOST_FOREACH(face_descriptor fd, faces(pmesh)){
|
||||
reverse_orientation(halfedge(fd,pmesh),pmesh);
|
||||
}
|
||||
}
|
||||
// Note: A border edge is now parallel to its opposite edge.
|
||||
// We scan all border edges for this property. If it holds, we
|
||||
// reorient the associated hole and search again until no border
|
||||
|
|
@ -334,7 +336,313 @@ void reverse_face_orientations(const FaceRange& face_range, PolygonMesh& pmesh)
|
|||
}
|
||||
}
|
||||
}
|
||||
namespace internal {
|
||||
|
||||
template <class Kernel, class TriangleMesh, class VD, class Fid_map, class Vpm>
|
||||
void recursive_orient_volume_ccs( TriangleMesh& tm,
|
||||
Vpm& vpm,
|
||||
Fid_map& fid_map,
|
||||
const std::vector<VD>& xtrm_vertices,
|
||||
boost::dynamic_bitset<>& cc_handled,
|
||||
const std::vector<std::size_t>& face_cc,
|
||||
std::size_t xtrm_cc_id,
|
||||
bool is_parent_outward_oriented)
|
||||
{
|
||||
typedef boost::graph_traits<TriangleMesh> Graph_traits;
|
||||
typedef typename Graph_traits::face_descriptor face_descriptor;
|
||||
typedef Side_of_triangle_mesh<TriangleMesh, Kernel, Vpm> Side_of_tm;
|
||||
std::vector<face_descriptor> cc_faces;
|
||||
BOOST_FOREACH(face_descriptor fd, faces(tm))
|
||||
{
|
||||
if(face_cc[get(fid_map, fd)]==xtrm_cc_id)
|
||||
cc_faces.push_back(fd);
|
||||
}
|
||||
// first check that the orientation of the current cc is consistant with its
|
||||
// parent cc containing it
|
||||
bool new_is_parent_outward_oriented = internal::is_outward_oriented(
|
||||
xtrm_vertices[xtrm_cc_id], tm, parameters::vertex_point_map(vpm));
|
||||
if (new_is_parent_outward_oriented==is_parent_outward_oriented)
|
||||
{
|
||||
Polygon_mesh_processing::reverse_face_orientations(cc_faces, tm);
|
||||
new_is_parent_outward_oriented = !new_is_parent_outward_oriented;
|
||||
}
|
||||
cc_handled.set(xtrm_cc_id);
|
||||
|
||||
std::size_t nb_cc = cc_handled.size();
|
||||
|
||||
// get all cc that are inside xtrm_cc_id
|
||||
|
||||
typename Side_of_tm::AABB_tree aabb_tree(cc_faces.begin(), cc_faces.end(),
|
||||
tm, vpm);
|
||||
Side_of_tm side_of_cc(aabb_tree);
|
||||
|
||||
std::vector<std::size_t> cc_inside;
|
||||
for(std::size_t id=0; id<nb_cc; ++id)
|
||||
{
|
||||
if (cc_handled.test(id)) continue;
|
||||
if (side_of_cc(get(vpm,xtrm_vertices[id]))==ON_BOUNDED_SIDE)
|
||||
cc_inside.push_back(id);
|
||||
}
|
||||
|
||||
// check whether we need another recursion for cc inside xtrm_cc_id
|
||||
if (!cc_inside.empty())
|
||||
{
|
||||
std::size_t new_xtrm_cc_id = cc_inside.front();
|
||||
boost::dynamic_bitset<> new_cc_handled(nb_cc,0);
|
||||
new_cc_handled.set();
|
||||
new_cc_handled.reset(new_xtrm_cc_id);
|
||||
cc_handled.set(new_xtrm_cc_id);
|
||||
|
||||
std::size_t nb_candidates = cc_inside.size();
|
||||
for (std::size_t i=1;i<nb_candidates;++i)
|
||||
{
|
||||
std::size_t candidate = cc_inside[i];
|
||||
if(get(vpm,xtrm_vertices[candidate]).z() >
|
||||
get(vpm,xtrm_vertices[new_xtrm_cc_id]).z()) new_xtrm_cc_id=candidate;
|
||||
new_cc_handled.reset(candidate);
|
||||
cc_handled.set(candidate);
|
||||
}
|
||||
|
||||
internal::recursive_orient_volume_ccs<Kernel>(
|
||||
tm, vpm, fid_map, xtrm_vertices, new_cc_handled, face_cc,
|
||||
new_xtrm_cc_id, new_is_parent_outward_oriented);
|
||||
}
|
||||
|
||||
// now explore remaining cc included in the same cc as xtrm_cc_id
|
||||
boost::dynamic_bitset<> cc_not_handled = ~cc_handled;
|
||||
std::size_t new_xtrm_cc_id = cc_not_handled.find_first();
|
||||
if (new_xtrm_cc_id == cc_not_handled.npos) return ;
|
||||
|
||||
for (std::size_t candidate = cc_not_handled.find_next(new_xtrm_cc_id);
|
||||
candidate < cc_not_handled.npos;
|
||||
candidate = cc_not_handled.find_next(candidate))
|
||||
{
|
||||
if(get(vpm,xtrm_vertices[candidate]).z() > get(vpm,xtrm_vertices[new_xtrm_cc_id]).z())
|
||||
new_xtrm_cc_id = candidate;
|
||||
}
|
||||
|
||||
internal::recursive_orient_volume_ccs<Kernel>(
|
||||
tm, vpm, fid_map, xtrm_vertices, cc_handled, face_cc,
|
||||
new_xtrm_cc_id, is_parent_outward_oriented);
|
||||
}
|
||||
|
||||
}//end internal
|
||||
|
||||
/**
|
||||
* \ingroup PMP_orientation_grp
|
||||
* makes each connected component of a closed triangulated surface mesh
|
||||
* inward or outward oriented.
|
||||
*
|
||||
* @tparam TriangleMesh a model of `FaceListGraph` and `MutableFaceGraph` .
|
||||
* If `TriangleMesh` has an internal property map for `CGAL::face_index_t`,
|
||||
* as a named parameter, then it must be initialized.
|
||||
* @tparam NamedParameters a sequence of \ref namedparameters
|
||||
*
|
||||
* @param tm a closed triangulated surface mesh
|
||||
* @param np optional sequence of \ref namedparameters among the ones listed below
|
||||
*
|
||||
* \cgalNamedParamsBegin
|
||||
* \cgalParamBegin{vertex_point_map}
|
||||
* the property map with the points associated to the vertices of `tm`.
|
||||
* If this parameter is omitted, an internal property map for
|
||||
* `CGAL::vertex_point_t` should be available in `TriangleMesh`
|
||||
* \cgalParamEnd
|
||||
* \cgalParamBegin{face_index_map}
|
||||
* a property map containing the index of each face of `tm`.
|
||||
* \cgalParamEnd
|
||||
* \cgalParamBegin{outward_orientation}
|
||||
* if set to `true` (default) indicates that each connected component will be outward oriented,
|
||||
* (inward oriented if `false`).
|
||||
* \cgalParamEnd
|
||||
* \cgalNamedParamsEnd
|
||||
*/
|
||||
template<class TriangleMesh, class NamedParameters>
|
||||
void orient(TriangleMesh& tm, const NamedParameters& np)
|
||||
{
|
||||
typedef boost::graph_traits<TriangleMesh> Graph_traits;
|
||||
typedef typename Graph_traits::vertex_descriptor vertex_descriptor;
|
||||
typedef typename Graph_traits::face_descriptor face_descriptor;
|
||||
typedef typename Graph_traits::halfedge_descriptor halfedge_descriptor;
|
||||
typedef typename GetVertexPointMap<TriangleMesh,
|
||||
NamedParameters>::const_type Vpm;
|
||||
typedef typename GetFaceIndexMap<TriangleMesh,
|
||||
NamedParameters>::const_type Fid_map;
|
||||
|
||||
CGAL_assertion(is_triangle_mesh(tm));
|
||||
CGAL_assertion(is_valid(tm));
|
||||
CGAL_assertion(is_closed(tm));
|
||||
|
||||
using boost::choose_param;
|
||||
using boost::get_param;
|
||||
|
||||
bool orient_outward = choose_param(
|
||||
get_param(np, internal_np::outward_orientation),true);
|
||||
|
||||
Vpm vpm = choose_param(get_param(np, internal_np::vertex_point),
|
||||
get_const_property_map(boost::vertex_point, tm));
|
||||
|
||||
Fid_map fid_map = choose_param(get_param(np, internal_np::face_index),
|
||||
get_const_property_map(boost::face_index, tm));
|
||||
|
||||
std::vector<std::size_t> face_cc(num_faces(tm), std::size_t(-1));
|
||||
|
||||
// set the connected component id of each face
|
||||
std::size_t nb_cc = connected_components(tm,
|
||||
bind_property_maps(fid_map,make_property_map(face_cc)),
|
||||
parameters::face_index_map(fid_map));
|
||||
|
||||
// extract a vertex with max z coordinate for each connected component
|
||||
std::vector<vertex_descriptor> xtrm_vertices(nb_cc, Graph_traits::null_vertex());
|
||||
BOOST_FOREACH(vertex_descriptor vd, vertices(tm))
|
||||
{
|
||||
halfedge_descriptor test_hd = halfedge(vd, tm);
|
||||
if(test_hd == Graph_traits::null_halfedge())
|
||||
continue;
|
||||
face_descriptor test_face = face(halfedge(vd, tm), tm);
|
||||
if(test_face == Graph_traits::null_face())
|
||||
test_face = face(opposite(halfedge(vd, tm), tm), tm);
|
||||
CGAL_assertion(test_face != Graph_traits::null_face());
|
||||
std::size_t cc_id = face_cc[get(fid_map,test_face )];
|
||||
if (xtrm_vertices[cc_id]==Graph_traits::null_vertex())
|
||||
xtrm_vertices[cc_id]=vd;
|
||||
else
|
||||
if (get(vpm, vd).z()>get(vpm,xtrm_vertices[cc_id]).z())
|
||||
xtrm_vertices[cc_id]=vd;
|
||||
}
|
||||
std::vector<std::vector<face_descriptor> > ccs(nb_cc);
|
||||
BOOST_FOREACH(face_descriptor fd, faces(tm))
|
||||
{
|
||||
ccs[face_cc[get(fid_map,fd)]].push_back(fd);
|
||||
}
|
||||
|
||||
//orient ccs outward
|
||||
for(std::size_t id=0; id<nb_cc; ++id)
|
||||
{
|
||||
if(internal::is_outward_oriented(xtrm_vertices[id], tm, np)
|
||||
!= orient_outward)
|
||||
{
|
||||
reverse_face_orientations(ccs[id], tm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class TriangleMesh>
|
||||
void orient(TriangleMesh& tm)
|
||||
{
|
||||
orient(tm, parameters::all_default());
|
||||
}
|
||||
|
||||
|
||||
/** \ingroup PMP_orientation_grp
|
||||
*
|
||||
* orients the connected components of `tm` to make it bound a volume.
|
||||
* See \ref coref_def_subsec for a precise definition.
|
||||
*
|
||||
* @tparam TriangleMesh a model of `MutableFaceGraph`, `HalfedgeListGraph` and `FaceListGraph`.
|
||||
* If `TriangleMesh` has an internal property map for `CGAL::face_index_t`,
|
||||
* as a named parameter, then it must be initialized.
|
||||
* @tparam NamedParameters a sequence of \ref namedparameters
|
||||
*
|
||||
* @param tm a closed triangulated surface mesh
|
||||
* @param np optional sequence of \ref namedparameters among the ones listed below
|
||||
*
|
||||
* \cgalNamedParamsBegin
|
||||
* \cgalParamBegin{vertex_point_map}
|
||||
* the property map with the points associated to the vertices of `tm`.
|
||||
* If this parameter is omitted, an internal property map for
|
||||
* `CGAL::vertex_point_t` should be available in `TriangleMesh`
|
||||
* \cgalParamEnd
|
||||
* \cgalParamBegin{face_index_map}
|
||||
* a property map containing the index of each face of `tm`.
|
||||
* \cgalParamEnd
|
||||
* \cgalParamBegin{outward_orientation}
|
||||
* if set to `true` (default) the outer connected components will be outward oriented (inward oriented if set to `false`).
|
||||
* If the outer connected components are inward oriented, it means that the infinity will be considered
|
||||
* as part of the volume bounded by `tm`.
|
||||
* \cgalParamEnd
|
||||
* \cgalNamedParamsEnd
|
||||
*
|
||||
* \see `CGAL::Polygon_mesh_processing::does_bound_a_volume()`
|
||||
*/
|
||||
template <class TriangleMesh, class NamedParameters>
|
||||
void orient_to_bound_a_volume(TriangleMesh& tm,
|
||||
const NamedParameters& np)
|
||||
{
|
||||
typedef boost::graph_traits<TriangleMesh> Graph_traits;
|
||||
typedef typename Graph_traits::vertex_descriptor vertex_descriptor;
|
||||
typedef typename GetVertexPointMap<TriangleMesh,
|
||||
NamedParameters>::const_type Vpm;
|
||||
typedef typename GetFaceIndexMap<TriangleMesh,
|
||||
NamedParameters>::const_type Fid_map;
|
||||
typedef typename Kernel_traits<
|
||||
typename boost::property_traits<Vpm>::value_type >::Kernel Kernel;
|
||||
if (!is_closed(tm)) return;
|
||||
if (!is_triangle_mesh(tm)) return;
|
||||
|
||||
using boost::choose_param;
|
||||
using boost::get_param;
|
||||
|
||||
bool orient_outward = choose_param(
|
||||
get_param(np, internal_np::outward_orientation),true);
|
||||
|
||||
Vpm vpm = choose_param(get_param(np, internal_np::vertex_point),
|
||||
get_const_property_map(boost::vertex_point, tm));
|
||||
|
||||
Fid_map fid_map = choose_param(get_param(np, internal_np::face_index),
|
||||
get_const_property_map(boost::face_index, tm));
|
||||
|
||||
std::vector<std::size_t> face_cc(num_faces(tm), std::size_t(-1));
|
||||
|
||||
|
||||
// set the connected component id of each face
|
||||
std::size_t nb_cc = connected_components(tm,
|
||||
bind_property_maps(fid_map,make_property_map(face_cc)),
|
||||
parameters::face_index_map(fid_map));
|
||||
|
||||
if (nb_cc == 1)
|
||||
{
|
||||
if( orient_outward != is_outward_oriented(tm))
|
||||
reverse_face_orientations(faces(tm), tm);
|
||||
return ;
|
||||
}
|
||||
|
||||
|
||||
boost::dynamic_bitset<> cc_handled(nb_cc, 0);
|
||||
|
||||
// extract a vertex with max z coordinate for each connected component
|
||||
std::vector<vertex_descriptor> xtrm_vertices(nb_cc, Graph_traits::null_vertex());
|
||||
BOOST_FOREACH(vertex_descriptor vd, vertices(tm))
|
||||
{
|
||||
std::size_t cc_id = face_cc[get(fid_map, face(halfedge(vd, tm), tm))];
|
||||
if (xtrm_vertices[cc_id]==Graph_traits::null_vertex())
|
||||
xtrm_vertices[cc_id]=vd;
|
||||
else
|
||||
if (get(vpm, vd).z()>get(vpm,xtrm_vertices[cc_id]).z())
|
||||
xtrm_vertices[cc_id]=vd;
|
||||
}
|
||||
|
||||
//extract a vertex with max z amongst all components
|
||||
std::size_t xtrm_cc_id=0;
|
||||
for(std::size_t id=1; id<nb_cc; ++id)
|
||||
if (get(vpm, xtrm_vertices[id]).z()>get(vpm,xtrm_vertices[xtrm_cc_id]).z())
|
||||
xtrm_cc_id=id;
|
||||
|
||||
bool is_parent_outward_oriented =
|
||||
! orient_outward;
|
||||
|
||||
internal::recursive_orient_volume_ccs<Kernel>(tm, vpm, fid_map,
|
||||
xtrm_vertices,
|
||||
cc_handled,
|
||||
face_cc,
|
||||
xtrm_cc_id,
|
||||
is_parent_outward_oriented);
|
||||
}
|
||||
|
||||
template <class TriangleMesh>
|
||||
void orient_to_bound_a_volume(TriangleMesh& tm)
|
||||
{
|
||||
orient_to_bound_a_volume(tm, parameters::all_default());
|
||||
}
|
||||
} // namespace Polygon_mesh_processing
|
||||
} // namespace CGAL
|
||||
#endif // CGAL_ORIENT_POLYGON_MESH_H
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ endif()
|
|||
create_single_source_cgal_program("autorefinement_sm.cpp")
|
||||
create_single_source_cgal_program("triangulate_hole_polyline_test.cpp")
|
||||
create_single_source_cgal_program("surface_intersection_sm_poly.cpp" )
|
||||
create_single_source_cgal_program("test_orient_cc.cpp")
|
||||
|
||||
if( TBB_FOUND )
|
||||
CGAL_target_use_TBB(test_pmp_distance)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,130 @@
|
|||
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||
#include <CGAL/Surface_mesh.h>
|
||||
#include <CGAL/Polygon_mesh_processing/orientation.h>
|
||||
#include <CGAL/Polygon_mesh_processing/corefinement.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
namespace PMP = CGAL::Polygon_mesh_processing;
|
||||
|
||||
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
|
||||
typedef CGAL::Surface_mesh<Kernel::Point_3> SMesh;
|
||||
|
||||
template<class TriangleMesh, class NamedParameters>
|
||||
bool test_orientation(TriangleMesh& tm, bool is_positive, const NamedParameters& np)
|
||||
{
|
||||
typedef boost::graph_traits<TriangleMesh> Graph_traits;
|
||||
typedef typename Graph_traits::vertex_descriptor vertex_descriptor;
|
||||
typedef typename Graph_traits::face_descriptor face_descriptor;
|
||||
typedef typename CGAL::GetVertexPointMap<TriangleMesh,
|
||||
NamedParameters>::const_type Vpm;
|
||||
typedef typename CGAL::GetFaceIndexMap<TriangleMesh,
|
||||
NamedParameters>::const_type Fid_map;
|
||||
|
||||
Vpm vpm = boost::choose_param(get_param(np, CGAL::internal_np::vertex_point),
|
||||
CGAL::get_const_property_map(boost::vertex_point, tm));
|
||||
|
||||
Fid_map fid_map = boost::choose_param(get_param(np, CGAL::internal_np::face_index),
|
||||
CGAL::get_const_property_map(boost::face_index, tm));
|
||||
|
||||
std::vector<std::size_t> face_cc(num_faces(tm), std::size_t(-1));
|
||||
|
||||
// set the connected component id of each face
|
||||
std::size_t nb_cc = PMP::connected_components(tm,
|
||||
CGAL::bind_property_maps(fid_map,CGAL::make_property_map(face_cc)),
|
||||
PMP::parameters::face_index_map(fid_map));
|
||||
|
||||
// extract a vertex with max z coordinate for each connected component
|
||||
std::vector<vertex_descriptor> xtrm_vertices(nb_cc, Graph_traits::null_vertex());
|
||||
BOOST_FOREACH(vertex_descriptor vd, vertices(tm))
|
||||
{
|
||||
face_descriptor test_face = face(halfedge(vd, tm), tm);
|
||||
if(test_face == Graph_traits::null_face())
|
||||
test_face = face(opposite(halfedge(vd, tm), tm), tm);
|
||||
std::size_t cc_id = face_cc[get(fid_map,test_face )];
|
||||
if (xtrm_vertices[cc_id]==Graph_traits::null_vertex())
|
||||
xtrm_vertices[cc_id]=vd;
|
||||
else
|
||||
if (get(vpm, vd).z()>get(vpm,xtrm_vertices[cc_id]).z())
|
||||
xtrm_vertices[cc_id]=vd;
|
||||
}
|
||||
std::vector<std::vector<face_descriptor> > ccs(nb_cc);
|
||||
BOOST_FOREACH(face_descriptor fd, faces(tm))
|
||||
{
|
||||
ccs[face_cc[get(fid_map,fd)]].push_back(fd);
|
||||
}
|
||||
|
||||
//test ccs orientation
|
||||
for(std::size_t id=0; id<nb_cc; ++id)
|
||||
{
|
||||
if((!PMP::internal::is_outward_oriented(xtrm_vertices[id], tm, np)
|
||||
&& is_positive)
|
||||
|| (PMP::internal::is_outward_oriented(xtrm_vertices[id], tm, np)
|
||||
&& !is_positive))
|
||||
{
|
||||
std::cerr<<" the orientation failed"<<std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
|
||||
std::ifstream input("data-coref/nested_cubes_invalid_volume.off");
|
||||
assert(input);
|
||||
SMesh sm1, sm2, sm3, sm4, volume;
|
||||
input >> sm1;
|
||||
sm2 = sm1;
|
||||
sm3 = sm1;
|
||||
sm4 = sm1;
|
||||
volume = sm1;
|
||||
PMP::orient(sm1);
|
||||
if(!test_orientation(sm1, true, PMP::parameters::all_default()))
|
||||
return 1;
|
||||
typedef boost::property_map<SMesh, CGAL::vertex_point_t>::type Ppmap;
|
||||
typedef boost::property_map<SMesh, CGAL::face_index_t>::type Fidmap;
|
||||
Ppmap vpmap2 = get(CGAL::vertex_point, sm2);
|
||||
Fidmap fidmap2 = get(CGAL::face_index, sm2);
|
||||
|
||||
PMP::orient(sm2, PMP::parameters::vertex_point_map(vpmap2)
|
||||
.face_index_map(fidmap2));
|
||||
if(!test_orientation(sm2, true, PMP::parameters::vertex_point_map(vpmap2)
|
||||
.face_index_map(fidmap2)))
|
||||
{
|
||||
std::cerr << "ERROR for test1\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
PMP::orient(sm3, PMP::parameters::outward_orientation(false));
|
||||
if(!test_orientation(sm3, false, PMP::parameters::all_default()))
|
||||
{
|
||||
std::cerr << "ERROR for test2\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
Ppmap vpmap4 = get(CGAL::vertex_point, sm4);
|
||||
Fidmap fidmap4 = get(CGAL::face_index, sm4);
|
||||
|
||||
PMP::orient(sm4, PMP::parameters::vertex_point_map(vpmap4)
|
||||
.face_index_map(fidmap4)
|
||||
.outward_orientation(false));
|
||||
if(!test_orientation(sm4, false, PMP::parameters::vertex_point_map(vpmap4)
|
||||
.face_index_map(fidmap4)))
|
||||
{
|
||||
std::cerr << "ERROR for test3\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
PMP::orient_to_bound_a_volume(volume);
|
||||
if( !PMP::does_bound_a_volume(volume))
|
||||
{
|
||||
std::cerr << "ERROR for test4\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
#include <QAction>
|
||||
#include <QStringList>
|
||||
#include <QMainWindow>
|
||||
#include <QInputDialog>
|
||||
#include "Scene_polyhedron_item.h"
|
||||
#include "Scene_polygon_soup_item.h"
|
||||
#include "Scene_surface_mesh_item.h"
|
||||
|
|
@ -29,26 +30,43 @@ public:
|
|||
Messages_interface*)
|
||||
{
|
||||
scene = scene_interface;
|
||||
QAction* actionInsideOut = new QAction(tr("Inside Out"), mw);
|
||||
this->mw = mw;
|
||||
actionInsideOut = new QAction(tr("Inside Out"), mw);
|
||||
|
||||
actionInsideOut->setProperty("subMenuName", "Polygon Mesh Processing");
|
||||
connect(actionInsideOut, SIGNAL(triggered()), this, SLOT(on_actionInsideOut_triggered()));
|
||||
_actions << actionInsideOut;
|
||||
|
||||
actionOrientCC = new QAction(tr("Orient Connected Components"), mw);
|
||||
|
||||
actionOrientCC->setProperty("subMenuName", "Polygon Mesh Processing");
|
||||
connect(actionOrientCC, SIGNAL(triggered()), this, SLOT(on_actionOrientCC_triggered()));
|
||||
_actions << actionOrientCC;
|
||||
|
||||
|
||||
}
|
||||
bool applicable(QAction*) const {
|
||||
bool applicable(QAction* action) const {
|
||||
const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();
|
||||
return qobject_cast<Scene_polyhedron_item*>(scene->item(index))
|
||||
|| qobject_cast<Scene_polygon_soup_item*>(scene->item(index))
|
||||
|| qobject_cast<Scene_surface_mesh_item*>(scene->item(index));
|
||||
if(action == actionInsideOut)
|
||||
return qobject_cast<Scene_polyhedron_item*>(scene->item(index))
|
||||
|| qobject_cast<Scene_polygon_soup_item*>(scene->item(index))
|
||||
|| qobject_cast<Scene_surface_mesh_item*>(scene->item(index));
|
||||
else if(action == actionOrientCC)
|
||||
return qobject_cast<Scene_polyhedron_item*>(scene->item(index))
|
||||
|| qobject_cast<Scene_surface_mesh_item*>(scene->item(index));
|
||||
return false;
|
||||
}
|
||||
|
||||
public Q_SLOTS:
|
||||
void on_actionInsideOut_triggered();
|
||||
void on_actionOrientCC_triggered();
|
||||
|
||||
private:
|
||||
QAction* actionInsideOut;
|
||||
QAction* actionOrientCC;
|
||||
QList<QAction*> _actions;
|
||||
Scene_interface *scene;
|
||||
QMainWindow* mw;
|
||||
}; // end Polyhedron_demo_inside_out_plugin
|
||||
|
||||
void Polyhedron_demo_inside_out_plugin::on_actionInsideOut_triggered()
|
||||
|
|
@ -94,4 +112,54 @@ void Polyhedron_demo_inside_out_plugin::on_actionInsideOut_triggered()
|
|||
}
|
||||
}
|
||||
|
||||
void Polyhedron_demo_inside_out_plugin::on_actionOrientCC_triggered()
|
||||
{
|
||||
const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex();
|
||||
|
||||
Scene_polyhedron_item* poly_item =
|
||||
qobject_cast<Scene_polyhedron_item*>(scene->item(index));
|
||||
|
||||
Scene_surface_mesh_item* sm_item =
|
||||
qobject_cast<Scene_surface_mesh_item*>(scene->item(index));
|
||||
|
||||
if(poly_item || sm_item)
|
||||
{
|
||||
QStringList items;
|
||||
items << tr("Outward") << tr("Inward");
|
||||
|
||||
bool ok;
|
||||
QString item = QInputDialog::getItem(mw, tr("QInputDialog::getItem()"),
|
||||
tr("The connected components should be oriented:"), items, 0, false, &ok);
|
||||
if (!ok )
|
||||
return;
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
if(poly_item) {
|
||||
Polyhedron* pMesh = poly_item->polyhedron();
|
||||
if(pMesh){
|
||||
if(is_closed(*pMesh))
|
||||
CGAL::Polygon_mesh_processing::orient_to_bound_a_volume(*pMesh, item==items.first());
|
||||
else
|
||||
CGAL::Polygon_mesh_processing::orient(*pMesh, item==items.first());
|
||||
poly_item->invalidateOpenGLBuffers();
|
||||
}
|
||||
}
|
||||
else if(sm_item) {
|
||||
SMesh* pMesh = sm_item->polyhedron();
|
||||
if(pMesh){
|
||||
if(is_closed(*pMesh))
|
||||
CGAL::Polygon_mesh_processing::orient_to_bound_a_volume(*pMesh, item==items.first());
|
||||
else
|
||||
CGAL::Polygon_mesh_processing::orient(*pMesh, item==items.first());
|
||||
sm_item->invalidateOpenGLBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
// update scene
|
||||
scene->itemChanged(index);
|
||||
|
||||
// default cursor
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
}
|
||||
|
||||
#include "Inside_out_plugin.moc"
|
||||
|
|
|
|||
Loading…
Reference in New Issue