cgal/Polygon_mesh_processing/include/CGAL/orient_polygon_soup.h

287 lines
9.3 KiB
C++

// 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 <CGAL/IO/generic_print_polyhedron.h>
#include <CGAL/Polyhedron_incremental_builder_3.h>
#include <CGAL/Modifier_base.h>
#include <set>
#include <stack>
#include <algorithm>
#include <iostream>
#include <boost/array.hpp>
namespace CGAL {
namespace internal {
template<class Point_3>
class Polygon_soup_orienter
{
typedef std::vector<std::size_t> Polygon_3;
typedef std::vector<Point_3> Points;
typedef std::map<std::pair<std::size_t, std::size_t>, std::set<std::size_t> > Edges_map;
typedef boost::array<std::size_t, 2> Edge;
typedef std::vector<Polygon_3> Polygons;
typedef std::set<Edge> 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<bool> oriented;
std::stack<std::size_t> 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 <class Point_3>
bool orient_polygon_soup(const std::vector<Point_3>& points,
std::vector< std::vector<std::size_t> >& polygons)
{
internal::Polygon_soup_orienter<Point_3> 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 HDS, class Point_3>
class Polygon_soup_to_polyhedron_3: public CGAL::Modifier_base<HDS>
{
typedef std::vector<std::size_t> Polygon_3;
const std::vector<Point_3>& points;
const std::vector<std::vector<std::size_t> >& 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<Point_3>& points,
const std::vector<std::vector<std::size_t> >& polygons)
: points(points), polygons(polygons)
{ }
void operator()(HDS& out_hds)
{
Polyhedron_incremental_builder_3<HDS> 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