mirror of https://github.com/CGAL/cgal
287 lines
9.3 KiB
C++
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
|