// Copyright (c) 2009-2013 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) : Laurent Rineau and Ilker O. Yaz #ifndef CGAL_ORIENT_POLYGON_SOUP #define CGAL_ORIENT_POLYGON_SOUP #include #include #include #include #include #include #include #include namespace CGAL { namespace internal { template class Polygon_soup_orienter { typedef std::vector Polygon_3; typedef std::vector Points; typedef std::map, std::set > Edges_map; typedef boost::array Edge; typedef std::vector Polygons; typedef std::set Edges; typedef Polygons::size_type size_type; const Points& points; Polygons& polygons; Edges_map edges; Edges non_manifold_edges; public: Polygon_soup_orienter(const Points& points, Polygons& polygons) : points(points), polygons(polygons) { fill_edges(); } private: void fill_edges() { // Fill edges edges.clear(); for(size_type i = 0; i < polygons.size(); ++i) { const size_type size = polygons[i].size(); for(size_type j = 0; j < size; ++j) { const std::size_t& i0 = polygons[i][j]; const std::size_t& i1 = polygons[i][ j+1 < size ? j+1: 0]; edges[std::make_pair(i0, i1)].insert(i); } } // Fill non-manifold edges non_manifold_edges.clear(); for(size_type i = 0; i < polygons.size(); ++i) { const size_type size = polygons[i].size(); for(size_type j = 0; j < size; ++j) { const std::size_t& i0 = polygons[i][j]; const std::size_t& i1 = polygons[i][ j+1 < size ? j+1: 0]; if( (i0 < i1) && (edges[std::make_pair(i0, i1)].size() + edges[std::make_pair(i1, i0)].size() > 2) ) { Edge edge; edge[0] = i0; edge[1] = i1; if(i0 > i1) std::swap(edge[0], edge[1]); non_manifold_edges.insert(edge); } } } } void inverse_orientation(const std::size_t index) { std::reverse(polygons[index].begin(), polygons[index].end()); } public: bool orient() { std::vector oriented; std::stack stack; using std::make_pair; // no polygon is oriented oriented.resize(polygons.size()); size_type polygon_index = 0; bool success = true; while (polygon_index != polygons.size()) { while ( polygon_index != polygons.size() && oriented[polygon_index] ) { ++polygon_index; } if(polygon_index == polygons.size()) break; oriented[polygon_index] = true; stack.push(polygon_index); while(! stack.empty() ) { const size_type to_be_oriented_index = stack.top(); stack.pop(); const size_type size = polygons[to_be_oriented_index].size(); for(size_type ih = 0 ; ih < size ; ++ih) { size_type ihp1 = ih+1; if(ihp1>=size) ihp1 = 0; const std::size_t& i1 = polygons[to_be_oriented_index][ih]; const std::size_t& i2 = polygons[to_be_oriented_index][ihp1]; Edge edge; edge[0] = i1; edge[1] = i2; if(i1 > i2) std::swap(edge[0], edge[1]); if(non_manifold_edges.count(edge) > 0) { continue; } // edge (i1,i2) Edges_map::iterator it_same_orient = edges.find(make_pair(i1, i2)); // edges (i2,i1) Edges_map::iterator it_other_orient = edges.find(make_pair(i2, i1)); CGAL_assertion(it_same_orient != edges.end()); if(it_same_orient->second.size() > 1) { if((it_other_orient != edges.end() && it_other_orient->second.size() > 0) || it_same_orient->second.size() > 2) { // three polygons at the edge success = false; // non-orientable } { // one neighbor polyhedron, opposite orientation size_type index = *(it_same_orient->second.begin()); if(index == to_be_oriented_index) index = *(++it_same_orient->second.begin()); if(oriented[index]) { // "neighbor polygon #%1 is already oriented, but in opposite orientation").arg(index); success = false; // non-orientable continue; // next edge } // reverse the orientation const size_type size = polygons[index].size(); for(size_type j = 0; j < size; ++j) { const std::size_t& i0 = polygons[index][j]; const std::size_t& i1 = polygons[index][ j+1 < size ? j+1: 0]; CGAL_assertion_code(const bool r = ) edges[std::make_pair(i0, i1)].erase(index) CGAL_assertion_code(!= 0); CGAL_assertion(r); } inverse_orientation(index); for(size_type j = 0; j < size; ++j) { const std::size_t& i0 = polygons[index][j]; const std::size_t& i1 = polygons[index][ j+1 < size ? j+1: 0]; edges[std::make_pair(i0, i1)].insert(index); } // "inverse the orientation of polygon #%1\n").arg(index); oriented[index] = true; stack.push(index); } } else if(it_other_orient != edges.end() && it_other_orient->second.size() == 1) { // one polygon, same orientation const size_type index = *(it_other_orient->second.begin()); if(oriented[index]) continue; oriented[index] = true; // "keep the orientation of polygon #%1\n").arg(index); stack.push(index); } else { if(it_same_orient->second.size() != 1 || (it_other_orient != edges.end() && it_other_orient->second.size() > 0)) { success = false; // non-orientable } } } // end for on all edges of one } // end while loop on the polygons of the connected component } // end while loop on all non-oriented polygons remaining return success; } }; } // namespace internal /** * \ingroup PkgPolygonMeshProcessing * Tries to consistently orient a soup of polygons in 3D space. * If a consistent orientation has been found, `true` is returned. * In any case `polygons` is updated. * @tparam Point_3 the point type * * @param points points of the soup of polygons. * @param[in, out] polygons each element in the vector describes a polygon using the index of the points in the vector. * * @return true if a consistent orientation has been found * * \todo code: there is no check for duplicate points, yet it can be implemented as separate filter function * \todo code: support fixed size arrays for polygons, or creating a concept which provides .size and .operator[] */ template bool orient_polygon_soup(const std::vector& points, std::vector< std::vector >& polygons) { internal::Polygon_soup_orienter orienter(points, polygons); return orienter.orient(); } /** * \ingroup PkgPolygonMeshProcessing * Polyhedron modifier to build a polyhedron from a soup of polygons. * \todo move me to a separate header file */ template class Polygon_soup_to_polyhedron_3: public CGAL::Modifier_base { typedef std::vector Polygon_3; const std::vector& points; const std::vector >& polygons; public: /** * The constructor for modifier object. * @param points points of the soup of polygons. * @param polygons each element in the vector describes a polygon using the index of the points in the vector. */ Polygon_soup_to_polyhedron_3(const std::vector& points, const std::vector >& polygons) : points(points), polygons(polygons) { } void operator()(HDS& out_hds) { Polyhedron_incremental_builder_3 builder(out_hds); builder.begin_surface(points.size(), polygons.size()); for(std::size_t i = 0, end = points.size(); i < end; ++i) { builder.add_vertex(points[i]); } for(std::size_t i = 0, end = polygons.size(); i < end; ++i) { const Polygon_3& polygon = polygons[i]; const std::size_t size = polygon.size(); builder.begin_facet(); for(std::size_t j = 0; j < size; ++j) { builder.add_vertex_to_facet(polygon[j]); } builder.end_facet(); } builder.end_surface(); } }; }// namespace CGAL #endif // CGAL_ORIENT_POLYGON_SOUP