// 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 #include namespace CGAL{ /** \cond */ namespace internal{ template class Import_volume_as_polyhedron : public CGAL::Modifier_base { 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 > faces; public: //to import each piece individually template 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 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 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 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 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 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 B(hds, true); B.begin_surface( points.size(), faces.size(),2*nb_edges); //insert vertices for (typename std::vector::iterator it=points.begin();it!=points.end();++it) B.add_vertex(*it); //create faces for (std::vector >::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. * @tparam Kernel must be a CGAL Kernel compatible with the underlying kernel of Polyhedron. * @tparam Output_polyhedron is a polyhedron type used as output. `Kernel` must be compatible with the underlying kernel of `Output_polyhedron`. */ template class Polyhedron_corefinement { typedef internal::Import_volume_as_polyhedron 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. * @tparam Polyhedron_ptr_and_type_output_iterator an output iterator of std::pair. * @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 second 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::Join_tag | Polyhedron_corefinement::Intersection_tag, then poly_output will collect two polyhedra, the union and the intersection of P and Q. */ template 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 Split_visitor; Split_visitor visitor; CGAL::Intersection_of_Polyhedra_3 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 intersection; std::list union_; std::list P_minus_Q; std::list 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); Output_polyhedron* new_poly=new Output_polyhedron(); new_poly->delegate(modifier); *poly_output++ = std::make_pair( new_poly,static_cast(Decomposition_tag) ); } if ( features&Parts_of_P_tag && info.inside.find(&P)!=info.inside.end() ) { Volume_import_modifier modifier(final_map,it); Output_polyhedron* new_poly=new Output_polyhedron(); new_poly->delegate(modifier); *poly_output++ = std::make_pair( new_poly,static_cast(Parts_of_P_tag) ); } if ( features&Parts_of_Q_tag && info.inside.find(&Q)!=info.inside.end() ) { Volume_import_modifier modifier(final_map,it); Output_polyhedron* new_poly=new Output_polyhedron(); new_poly->delegate(modifier); *poly_output++ = std::make_pair( new_poly,static_cast(Parts_of_Q_tag) ); } } if (!intersection.empty()) { Volume_import_modifier modifier(final_map,intersection.begin(),intersection.end()); Output_polyhedron* new_poly=new Output_polyhedron(); new_poly->delegate(modifier); *poly_output++=std::make_pair( new_poly,static_cast(Intersection_tag) ); } if (!P_minus_Q.empty()) { Volume_import_modifier modifier(final_map,P_minus_Q.begin(),P_minus_Q.end()); Output_polyhedron* new_poly=new Output_polyhedron(); new_poly->delegate(modifier); *poly_output++=std::make_pair( new_poly,static_cast(P_minus_Q_tag) ); } if (!Q_minus_P.empty()) { Volume_import_modifier modifier(final_map,Q_minus_P.begin(),Q_minus_P.end()); Output_polyhedron* new_poly=new Output_polyhedron(); new_poly->delegate(modifier); *poly_output++=std::make_pair( new_poly,static_cast(Q_minus_P_tag) ); } if (!union_.empty()) { Volume_import_modifier modifier(final_map,union_.begin(),union_.end(),true); Output_polyhedron* new_poly=new Output_polyhedron(); new_poly->delegate(modifier); *poly_output++=std::make_pair( new_poly,static_cast(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. * @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 void operator()( Polyhedron& P, Polyhedron& Q, Polyline_output_iterator polyline_output, Boolean_operation_tag features) const { typedef CGAL::Node_visitor_refine_polyhedra Split_visitor; Split_visitor visitor; CGAL::Intersection_of_Polyhedra_3 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 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