mirror of https://github.com/CGAL/cgal
487 lines
21 KiB
C++
487 lines
21 KiB
C++
// Copyright (c) 2011 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$
|
|
//
|
|
//
|
|
// Author(s) : Sebastien Loriot
|
|
|
|
#ifndef CGAL_COREFINEMENT_OPERATIONS_H
|
|
#define CGAL_COREFINEMENT_OPERATIONS_H
|
|
|
|
#include <CGAL/intersection_of_Polyhedra_3.h>
|
|
#include <CGAL/intersection_of_Polyhedra_3_refinement_visitor.h>
|
|
|
|
namespace CGAL{
|
|
/** \cond */
|
|
namespace internal{
|
|
|
|
template <class HDS>
|
|
class Import_volume_as_polyhedron : public CGAL::Modifier_base<HDS> {
|
|
typedef typename HDS::Halfedge_handle Halfedge_handle;
|
|
typedef typename HDS::Vertex_handle Vertex_handle;
|
|
typedef typename HDS::Face_handle Face_handle;
|
|
typedef typename HDS::Vertex Vertex;
|
|
typedef typename HDS::Halfedge Halfedge;
|
|
typedef typename HDS::Face Face;
|
|
|
|
//data members
|
|
Face_handle current_face;
|
|
std::vector< typename Vertex::Point > points;
|
|
std::size_t nb_edges;
|
|
std::vector<CGAL::cpp11::tuple<unsigned,unsigned,unsigned> > faces;
|
|
|
|
typename HDS::Halfedge::Base*
|
|
unlock_halfedge(Halfedge_handle h){
|
|
return static_cast<typename HDS::Halfedge::Base*>(&(*h));
|
|
}
|
|
|
|
typename HDS::Face::Base*
|
|
unlock_face(Face_handle f){
|
|
return static_cast<typename HDS::Face::Base*>(&(*f));
|
|
}
|
|
|
|
public:
|
|
|
|
//to import each piece individually
|
|
template <class Combinatorial_map_3>
|
|
Import_volume_as_polyhedron(const Combinatorial_map_3& map,typename Combinatorial_map_3::Dart_const_handle dart):nb_edges(0)
|
|
{
|
|
typedef Combinatorial_map_3 CMap;
|
|
typedef std::map<const typename Vertex::Point*,unsigned int> Vertex_map;
|
|
Vertex_map vertex_map;
|
|
unsigned int index=0;
|
|
//recover all the vertices in the current volumeiterator over all the point
|
|
//map the vertex to the index of the point in the vector
|
|
for (typename CMap::template One_dart_per_incident_cell_const_range<0,3>::const_iterator
|
|
it=map.template one_dart_per_incident_cell<0,3>(dart).begin(),
|
|
itend=map.template one_dart_per_incident_cell<0,3>(dart).end();
|
|
it!=itend; ++it)
|
|
{
|
|
points.push_back(it->template attribute<0>()->point());
|
|
vertex_map.insert(std::make_pair(&it->template attribute<0>()->point(),index++));
|
|
}
|
|
|
|
//count the number of edges
|
|
nb_edges+=map.template one_dart_per_incident_cell<1,3>(dart).size();
|
|
|
|
//recover one dart per face
|
|
for (typename CMap::template One_dart_per_incident_cell_const_range<2,3>::const_iterator
|
|
it=map.template one_dart_per_incident_cell<2,3>(dart).begin(),
|
|
itend=map.template one_dart_per_incident_cell<2,3>(dart).end();
|
|
it!=itend; ++it)
|
|
{
|
|
//warning: the convention used into a polyhedron is that the normal
|
|
// of a triangle indicates the outside of the object; thus
|
|
// we need to reverse the orientation of the faces of the
|
|
// combinatorial map.
|
|
unsigned int i=vertex_map[&it->template attribute<0>()->point()];
|
|
unsigned int j=vertex_map[&it->beta(0)->template attribute<0>()->point()];
|
|
unsigned int k=vertex_map[&it->beta(1)->template attribute<0>()->point()];
|
|
faces.push_back(CGAL::cpp11::make_tuple(i,j,k));
|
|
}
|
|
}
|
|
|
|
//for intersection and symetric difference
|
|
template <class Combinatorial_map_3,class Iterator>
|
|
Import_volume_as_polyhedron(const Combinatorial_map_3& map,
|
|
Iterator dart_begin,
|
|
Iterator dart_end):nb_edges(0)
|
|
{
|
|
typedef Combinatorial_map_3 CMap;
|
|
typedef std::map<const typename Vertex::Point*,unsigned int> Vertex_map;
|
|
Vertex_map vertex_map;
|
|
unsigned int index=0;
|
|
|
|
for (Iterator it=dart_begin;it!=dart_end;++it)
|
|
{
|
|
typename Combinatorial_map_3::Dart_const_handle dart=*it;
|
|
//recover all the vertices in the current volumeiterator over all the point
|
|
//map the vertex to the index of the point in the vector
|
|
for (typename CMap::template One_dart_per_incident_cell_const_range<0,3>::const_iterator
|
|
it=map.template one_dart_per_incident_cell<0,3>(dart).begin(),
|
|
itend=map.template one_dart_per_incident_cell<0,3>(dart).end();
|
|
it!=itend; ++it)
|
|
{
|
|
if ( vertex_map.insert(std::make_pair(&it->template attribute<0>()->point(),index)).second )
|
|
{
|
|
points.push_back(it->template attribute<0>()->point());
|
|
++index;
|
|
}
|
|
}
|
|
|
|
//count the number of edges
|
|
nb_edges+=map.template one_dart_per_incident_cell<1,3>(dart).size();
|
|
|
|
//recover one dart per face
|
|
for (typename CMap::template One_dart_per_incident_cell_const_range<2,3>::const_iterator
|
|
it=map.template one_dart_per_incident_cell<2,3>(dart).begin(),
|
|
itend=map.template one_dart_per_incident_cell<2,3>(dart).end();
|
|
it!=itend; ++it)
|
|
{
|
|
//warning: the convention used into a polyhedron is that the normal
|
|
// of a triangle indicates the outside of the object; thus
|
|
// we need to reverse the orientation of the faces of the
|
|
// combinatorial map.
|
|
unsigned int i=vertex_map[&it->template attribute<0>()->point()];
|
|
unsigned int j=vertex_map[&it->beta(0)->template attribute<0>()->point()];
|
|
unsigned int k=vertex_map[&it->beta(1)->template attribute<0>()->point()];
|
|
faces.push_back(CGAL::cpp11::make_tuple(i,j,k));
|
|
}
|
|
}
|
|
}
|
|
|
|
//for union : use the inverse of the complementary
|
|
template <class Combinatorial_map_3,class Iterator>
|
|
Import_volume_as_polyhedron(const Combinatorial_map_3& map,
|
|
Iterator dart_begin,
|
|
Iterator dart_end,bool):nb_edges(0)
|
|
{
|
|
typedef Combinatorial_map_3 CMap;
|
|
typedef std::map<const typename Vertex::Point*,unsigned int> Vertex_map;
|
|
Vertex_map vertex_map;
|
|
unsigned int index=0;
|
|
|
|
for (Iterator it=dart_begin;it!=dart_end;++it)
|
|
{
|
|
typename Combinatorial_map_3::Dart_const_handle dart=*it;
|
|
//recover all the vertices in the current volumeiterator over all the point
|
|
//map the vertex to the index of the point in the vector
|
|
for (typename CMap::template One_dart_per_incident_cell_const_range<0,3>::const_iterator
|
|
it=map.template one_dart_per_incident_cell<0,3>(dart).begin(),
|
|
itend=map.template one_dart_per_incident_cell<0,3>(dart).end();
|
|
it!=itend; ++it)
|
|
{
|
|
if (vertex_map.insert(std::make_pair(&it->template attribute<0>()->point(),index)).second )
|
|
{
|
|
points.push_back(it->template attribute<0>()->point());
|
|
++index;
|
|
}
|
|
}
|
|
|
|
//count the number of edges
|
|
nb_edges+=map.template one_dart_per_incident_cell<1,3>(dart).size();
|
|
|
|
//recover one dart per face
|
|
for (typename CMap::template One_dart_per_incident_cell_const_range<2,3>::const_iterator
|
|
it=map.template one_dart_per_incident_cell<2,3>(dart).begin(),
|
|
itend=map.template one_dart_per_incident_cell<2,3>(dart).end();
|
|
it!=itend; ++it)
|
|
{
|
|
//warning: the convention used into a polyhedron is that the normal
|
|
// of a triangle indicates the outside of the object; thus
|
|
// we need to reverse the orientation of the faces of the
|
|
// combinatorial map. Since to get the complementary we
|
|
// also need to reverse the orientation, we finally do
|
|
// not change it.
|
|
unsigned int i=vertex_map[&it->template attribute<0>()->point()];
|
|
unsigned int j=vertex_map[&it->beta(1)->template attribute<0>()->point()];
|
|
unsigned int k=vertex_map[&it->beta(0)->template attribute<0>()->point()];
|
|
faces.push_back(CGAL::cpp11::make_tuple(i,j,k));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void operator()( HDS& hds)
|
|
{
|
|
CGAL::Polyhedron_incremental_builder_3<HDS> B(hds, true);
|
|
B.begin_surface( points.size(), faces.size(),2*nb_edges);
|
|
|
|
//insert vertices
|
|
for (typename std::vector<typename Vertex::Point>::iterator it=points.begin();it!=points.end();++it)
|
|
B.add_vertex(*it);
|
|
|
|
//create faces
|
|
for (std::vector<CGAL::cpp11::tuple<unsigned,unsigned,unsigned> >::iterator it=faces.begin();it!=faces.end();++it)
|
|
{
|
|
B.begin_facet();
|
|
B.add_vertex_to_facet(CGAL::cpp11::get<0>(*it));
|
|
B.add_vertex_to_facet(CGAL::cpp11::get<1>(*it));
|
|
B.add_vertex_to_facet(CGAL::cpp11::get<2>(*it));
|
|
B.end_facet();
|
|
}
|
|
B.end_surface();
|
|
}
|
|
};
|
|
|
|
}
|
|
/** \endcond */
|
|
|
|
/*! \class Polyhedron_corefinement corefinement_operations.h CGAL/corefinement_operations.h
|
|
* Function object to compute the decomposition of the space induced by two polyhedra.
|
|
* @tparam Polyhedron must be an instantiation of CGAL::Polyhedron_3<Traits>.
|
|
* @tparam Kernel must be a CGAL Kernel compatible with the underlying kernel of Polyhedron.
|
|
*/
|
|
template <class Polyhedron,class Kernel=typename Polyhedron::Traits::Kernel>
|
|
class Polyhedron_corefinement
|
|
{
|
|
typedef internal::Import_volume_as_polyhedron<typename Polyhedron::HalfedgeDS> Volume_import_modifier;
|
|
|
|
public:
|
|
/** Enumeration of the different feature tags, listing all kind of space decomposition the functor can compute given two input polyhedra P and Q.*/
|
|
enum Boolean_operation_tag
|
|
{
|
|
Join_tag=1, /*!< the union of P and Q. */
|
|
Intersection_tag=2, /*!< the intersection of P and Q. */
|
|
P_minus_Q_tag=4, /*!< P minus Q. */
|
|
Q_minus_P_tag=8, /*!< Q minus P. */
|
|
Parts_of_P_tag=32, /*!< decomposition of the volume bounded by P induced by Q. */
|
|
Parts_of_Q_tag=64, /*!< decomposition of the volume bounded by Q induced by P. */
|
|
Decomposition_tag=16 /*!< Both decompositions of P and Q. */
|
|
};
|
|
|
|
/**
|
|
* This computes different polyhedra according to the value of features.
|
|
* Each input polyhedron is expected to bound a volume: the volume is bounded by the surface polyhedron with facet
|
|
* normals pointing outside the volume. Each facet is supposed to be counterclockwise oriented, that is its vertex
|
|
* sequence (induced by halfedges) is seen counterclockwise from the side of the facet pointed by its normal. If one or
|
|
* both polyhedra are not closed, the algorithm will end up correctly if each intersection polyline separates each surface
|
|
* in two components. In that case for each triangle of each surface path boundary, the interior of the volume is considered
|
|
* to be on the side opposite of that pointed by it normals (but the orientation must be consistent on each patch).
|
|
* the surface the volume bounded by an open surface is considered to be an infinite volume above
|
|
* or below the surface (the side not pointed by normal vectors).
|
|
* @tparam Polyline_output_iterator must be an output iterator of std::vector<Kernel::Point_3>.
|
|
* @tparam Polyhedron_ptr_and_type_output_iterator an output iterator of std::pair<Polyhedron*,int>.
|
|
* @param P is the first input triangulated polyhedron. Note that a reference is taken and P will be updated to contain the 1D intersection between the two surfaces P and Q.
|
|
* @param Q is the first input triangulated polyhedron. Note that a reference is taken and Q will be updated to contain the 1D intersection between the two surfaces P and Q.
|
|
* @param polyline_output is an output iterator that collects intersection polylines between P and Q.
|
|
* @param poly_output is an output iterator that collects output polyhedra. Note that each polyhedron is allocated within this function (thus must be explicitly deleted when no longer used). The integer is a feature tag corresponding to the volume represented by the polyhedron of the pair.
|
|
* @param features is an integer indicating what polyhedra the function must compute. If several kind of polyhedra are expected, feature tags must be combined by |. For example if features = Polyhedron_corefinement<Polyhedron,Kernel>::Join_tag | Polyhedron_corefinement<Polyhedron,Kernel>::Intersection_tag, then poly_output will collect two polyhedra, the union and the intersection of P and Q.
|
|
*/
|
|
template <class Polyline_output_iterator,class Polyhedron_ptr_and_type_output_iterator>
|
|
void operator()( Polyhedron& P, Polyhedron& Q,
|
|
Polyline_output_iterator polyline_output,
|
|
Polyhedron_ptr_and_type_output_iterator poly_output,
|
|
int features) const
|
|
{
|
|
typedef CGAL::Node_visitor_refine_polyhedra<Polyhedron> Split_visitor;
|
|
Split_visitor visitor;
|
|
CGAL::Intersection_of_Polyhedra_3<Polyhedron,
|
|
Kernel,
|
|
Split_visitor> polyline_intersections(visitor);
|
|
|
|
polyline_intersections(P, Q, polyline_output);
|
|
|
|
typedef typename Split_visitor::Combinatorial_map_3 Combinatorial_map_3;
|
|
typedef typename Split_visitor::Volume_info Volume_info;
|
|
typedef typename Combinatorial_map_3::Dart_const_handle Dart_const_handle;
|
|
|
|
const typename Split_visitor::Combinatorial_map_3& final_map=visitor.combinatorial_map();
|
|
|
|
typename Combinatorial_map_3::template One_dart_per_cell_const_range<3> cell_range=final_map.template one_dart_per_cell<3>();
|
|
|
|
std::list<Dart_const_handle> intersection;
|
|
std::list<Dart_const_handle> union_;
|
|
std::list<Dart_const_handle> P_minus_Q;
|
|
std::list<Dart_const_handle> Q_minus_P;
|
|
|
|
for (typename Combinatorial_map_3::template One_dart_per_cell_const_range<3>::const_iterator
|
|
it = cell_range.begin(), it_end= cell_range.end();
|
|
it!= it_end;
|
|
++it )
|
|
{
|
|
|
|
const Volume_info& info=it->template attribute<3>()->info();
|
|
std::size_t inside_size=info.inside.size();
|
|
std::size_t outside_size=info.outside.size();
|
|
|
|
if ( inside_size + outside_size != 2){
|
|
std::cerr << "Error: One volume cannot be represented using a polyhedron. Aborted.\n";
|
|
break;
|
|
}
|
|
|
|
switch (outside_size)
|
|
{
|
|
case 2:
|
|
if (features & Join_tag) union_.push_back(it);
|
|
break;
|
|
case 0:
|
|
if (features & Intersection_tag) intersection.push_back(it);
|
|
break;
|
|
default:
|
|
if ( *info.inside.begin() == &P )
|
|
{ if (features & P_minus_Q_tag) P_minus_Q.push_back(it); }
|
|
else
|
|
{ if (features & Q_minus_P_tag) Q_minus_P.push_back(it); }
|
|
}
|
|
|
|
if ( features&Decomposition_tag )
|
|
{
|
|
Volume_import_modifier modifier(final_map,it);
|
|
Polyhedron* new_poly=new Polyhedron();
|
|
new_poly->delegate(modifier);
|
|
*poly_output++ = std::make_pair( new_poly,static_cast<int>(Decomposition_tag) );
|
|
}
|
|
|
|
if ( features&Parts_of_P_tag && info.inside.find(&P)!=info.inside.end() )
|
|
{
|
|
Volume_import_modifier modifier(final_map,it);
|
|
Polyhedron* new_poly=new Polyhedron();
|
|
new_poly->delegate(modifier);
|
|
*poly_output++ = std::make_pair( new_poly,static_cast<int>(Parts_of_P_tag) );
|
|
}
|
|
|
|
if ( features&Parts_of_Q_tag && info.inside.find(&Q)!=info.inside.end() )
|
|
{
|
|
Volume_import_modifier modifier(final_map,it);
|
|
Polyhedron* new_poly=new Polyhedron();
|
|
new_poly->delegate(modifier);
|
|
*poly_output++ = std::make_pair( new_poly,static_cast<int>(Parts_of_Q_tag) );
|
|
}
|
|
}
|
|
|
|
if (!intersection.empty())
|
|
{
|
|
Volume_import_modifier modifier(final_map,intersection.begin(),intersection.end());
|
|
Polyhedron* new_poly=new Polyhedron();
|
|
new_poly->delegate(modifier);
|
|
*poly_output++=std::make_pair( new_poly,static_cast<int>(Intersection_tag) );
|
|
}
|
|
|
|
if (!P_minus_Q.empty())
|
|
{
|
|
Volume_import_modifier modifier(final_map,P_minus_Q.begin(),P_minus_Q.end());
|
|
Polyhedron* new_poly=new Polyhedron();
|
|
new_poly->delegate(modifier);
|
|
*poly_output++=std::make_pair( new_poly,static_cast<int>(P_minus_Q_tag) );
|
|
}
|
|
|
|
if (!Q_minus_P.empty())
|
|
{
|
|
Volume_import_modifier modifier(final_map,Q_minus_P.begin(),Q_minus_P.end());
|
|
Polyhedron* new_poly=new Polyhedron();
|
|
new_poly->delegate(modifier);
|
|
*poly_output++=std::make_pair( new_poly,static_cast<int>(Q_minus_P_tag) );
|
|
}
|
|
|
|
if (!union_.empty())
|
|
{
|
|
Volume_import_modifier modifier(final_map,union_.begin(),union_.end(),true);
|
|
Polyhedron* new_poly=new Polyhedron();
|
|
new_poly->delegate(modifier);
|
|
*poly_output++=std::make_pair( new_poly,static_cast<int>(Join_tag) );
|
|
}
|
|
}
|
|
/**
|
|
* This updates P according to the value of features.
|
|
* Each input polyhedron is expected to bound a volume: the volume is bounded by the surface polyhedron with facet
|
|
* normals pointing outside the volume. Each facet is supposed to be counterclockwise oriented, that is its vertex
|
|
* sequence (induced by halfedges) is seen counterclockwise from the side of the facet pointed by its normal. If one or
|
|
* both polyhedra are not closed, the algorithm will end up correctly if each intersection polyline separates each surface
|
|
* in two components. In that case for each triangle of each surface path boundary, the interior of the volume is considered
|
|
* to be on the side opposite of that pointed by it normals (but the orientation must be consistent on each patch).
|
|
* the surface the volume bounded by an open surface is considered to be an infinite volume above
|
|
* or below the surface (the side not pointed by normal vectors).
|
|
* @tparam Polyline_output_iterator must be an output iterator of std::vector<Kernel::Point_3>.
|
|
* @param P is the first input triangulated polyhedron. Note that a reference is taken and P will be updated to contain the 1D intersection between the two surfaces P and Q.
|
|
* @param Q is the first input triangulated polyhedron. Note that a reference is taken and Q will be updated to contain the 1D intersection between the two surfaces P and Q.
|
|
* @param polyline_output is an output iterator that collects intersection polylines between P and Q.
|
|
* @param features is either Join_tag, Intersection_tag, P_minus_Q_tag or Q_minus_P_tag
|
|
*/
|
|
template <class Polyline_output_iterator>
|
|
void operator()( Polyhedron& P, Polyhedron& Q,
|
|
Polyline_output_iterator polyline_output,
|
|
Boolean_operation_tag features) const
|
|
{
|
|
typedef CGAL::Node_visitor_refine_polyhedra<Polyhedron> Split_visitor;
|
|
Split_visitor visitor;
|
|
CGAL::Intersection_of_Polyhedra_3<Polyhedron,
|
|
Kernel,
|
|
Split_visitor> polyline_intersections(visitor);
|
|
|
|
polyline_intersections(P, Q, polyline_output);
|
|
|
|
typedef typename Split_visitor::Combinatorial_map_3 Combinatorial_map_3;
|
|
typedef typename Split_visitor::Volume_info Volume_info;
|
|
typedef typename Combinatorial_map_3::Dart_const_handle Dart_const_handle;
|
|
|
|
const typename Split_visitor::Combinatorial_map_3& final_map=visitor.combinatorial_map();
|
|
|
|
typename Combinatorial_map_3::template One_dart_per_cell_const_range<3> cell_range=final_map.template one_dart_per_cell<3>();
|
|
|
|
std::list<Dart_const_handle> darts;
|
|
|
|
for (typename Combinatorial_map_3::template One_dart_per_cell_const_range<3>::const_iterator
|
|
it = cell_range.begin(), it_end= cell_range.end();
|
|
it!= it_end;
|
|
++it )
|
|
{
|
|
|
|
const Volume_info& info=it->template attribute<3>()->info();
|
|
std::size_t inside_size=info.inside.size();
|
|
std::size_t outside_size=info.outside.size();
|
|
|
|
if ( inside_size + outside_size != 2){
|
|
std::cerr << "Error: One volume cannot be represented using a polyhedron. Aborted.\n";
|
|
break;
|
|
}
|
|
|
|
switch (outside_size)
|
|
{
|
|
case 2:
|
|
if ( features==Join_tag ) darts.push_back(it);
|
|
break;
|
|
case 0:
|
|
if ( features==Intersection_tag ) darts.push_back(it);
|
|
break;
|
|
default:
|
|
if ( *info.inside.begin() == &P )
|
|
{ if (features == P_minus_Q_tag) darts.push_back(it); }
|
|
else
|
|
{ if (features == Q_minus_P_tag) darts.push_back(it); }
|
|
}
|
|
}
|
|
|
|
P.clear();
|
|
|
|
if (!darts.empty())
|
|
{
|
|
Volume_import_modifier modifier=
|
|
features==Join_tag?
|
|
Volume_import_modifier(final_map,darts.begin(),darts.end(),true)
|
|
: Volume_import_modifier(final_map,darts.begin(),darts.end());
|
|
P.delegate(modifier);
|
|
}
|
|
}
|
|
|
|
/** \cond */
|
|
static std::string get_type_str(const std::string& Pname,const std::string& Qname,int i)
|
|
{
|
|
switch (i)
|
|
{
|
|
case Join_tag:
|
|
return Pname+std::string("_union_")+Qname;
|
|
case P_minus_Q_tag:
|
|
return Pname+std::string("_minus_")+Qname;
|
|
case Q_minus_P_tag:
|
|
return Qname+std::string("_minus_")+Pname;
|
|
case Intersection_tag:
|
|
return Pname+std::string("_inter_")+Qname;
|
|
case Decomposition_tag:
|
|
return std::string("Decomposition");
|
|
}
|
|
return std::string("Unknow");
|
|
}
|
|
/** \endcond */
|
|
};
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
#endif // CGAL_COREFINEMENT_OPERATIONS_H
|