mirror of https://github.com/CGAL/cgal
update orient_polygon_soup to always produce a polyhedron
with self-intersection at singular vertices/edges
This commit is contained in:
parent
b4bd141980
commit
24fcc4a2fe
|
|
@ -16,19 +16,20 @@
|
||||||
// $Id$
|
// $Id$
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Author(s) : Laurent Rineau and Ilker O. Yaz
|
// Author(s) : Laurent Rineau and Sebastien Loriot
|
||||||
|
|
||||||
|
|
||||||
#ifndef CGAL_ORIENT_POLYGON_SOUP
|
#ifndef CGAL_ORIENT_POLYGON_SOUP
|
||||||
#define CGAL_ORIENT_POLYGON_SOUP
|
#define CGAL_ORIENT_POLYGON_SOUP
|
||||||
|
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <map>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <CGAL/array.h>
|
|
||||||
|
|
||||||
namespace CGAL {
|
namespace CGAL {
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
@ -36,193 +37,368 @@ namespace internal {
|
||||||
template<class Point_3, class Polygon_3>
|
template<class Point_3, class Polygon_3>
|
||||||
class Polygon_soup_orienter
|
class Polygon_soup_orienter
|
||||||
{
|
{
|
||||||
typedef typename std::iterator_traits<typename Polygon_3::iterator>::value_type Index;
|
/// Index types
|
||||||
typedef std::vector<Point_3> Points;
|
typedef typename std::iterator_traits<
|
||||||
typedef std::map<std::pair<Index, Index>, std::set<std::size_t> > Edges_map;
|
typename Polygon_3::iterator >::value_type V_ID;
|
||||||
typedef cpp11::array<Index, 2> Edge;
|
typedef typename std::vector<Polygon_3>::size_type P_ID;
|
||||||
typedef std::vector<Polygon_3> Polygons;
|
// typedef int CC_ID;
|
||||||
typedef std::set<Edge> Edges;
|
typedef std::pair<V_ID, V_ID> V_ID_pair;
|
||||||
typedef typename Polygons::size_type size_type;
|
/// Container types
|
||||||
|
typedef std::vector<Point_3> Points;
|
||||||
|
typedef std::vector<Polygon_3> Polygons;
|
||||||
|
typedef std::map<V_ID_pair, std::set<P_ID> > Edge_map;
|
||||||
|
typedef typename Edge_map::iterator Edge_map_iterator;
|
||||||
|
typedef std::set<V_ID_pair> Marked_edges;
|
||||||
|
|
||||||
const Points& points;
|
/// Data members
|
||||||
Polygons& polygons;
|
Points& points; //< the set of input points
|
||||||
|
Polygons& polygons; //< the set of input polygons
|
||||||
|
Edge_map edges; //< the set of edges of the input polygons
|
||||||
|
Marked_edges marked_edges; //< the set of singular edges or edges incident
|
||||||
|
//< to non-compatible orientation polygons
|
||||||
|
|
||||||
Edges_map edges;
|
/// for each polygon referenced by its position in `polygons`, indicates
|
||||||
Edges non_manifold_edges;
|
/// the connected component it belongs too after orientation.
|
||||||
|
// std::vector< CC_ID > polygon_cc_id;
|
||||||
|
/// for each vertex, indicates the list of polygon containing it
|
||||||
|
std::vector< std::vector<P_ID> > incident_polygons_per_vertex;
|
||||||
|
|
||||||
private:
|
/// Utility functions
|
||||||
Edge canonical_edge(Index i, Index j)
|
V_ID_pair canonical_edge(V_ID i, V_ID j)
|
||||||
{
|
{
|
||||||
return i<j ? CGAL::make_array(i,j):CGAL::make_array(j,i);
|
return i<j ? V_ID_pair(i,j):V_ID_pair(j,i);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fill_edges() {
|
bool is_edge_marked(V_ID i, V_ID j)
|
||||||
// Fill edges
|
{
|
||||||
edges.clear();
|
return marked_edges.count(canonical_edge(i,j));
|
||||||
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 Index& i0 = polygons[i][j];
|
|
||||||
const Index& i1 = polygons[i][ j+1 < size ? j+1: 0];
|
|
||||||
edges[std::make_pair(i0, i1)].insert(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill non-manifold edges
|
void set_edge_marked(V_ID i, V_ID j)
|
||||||
non_manifold_edges.clear();
|
{
|
||||||
for(size_type i = 0; i < polygons.size(); ++i)
|
marked_edges.insert(canonical_edge(i,j));
|
||||||
{
|
}
|
||||||
const size_type size = polygons[i].size();
|
|
||||||
for(size_type j = 0; j < size; ++j) {
|
|
||||||
const Index& i0 = polygons[i][j];
|
|
||||||
const Index& i1 = polygons[i][ j+1 < size ? j+1: 0];
|
|
||||||
|
|
||||||
if( edges[std::make_pair(i0, i1)].size() +
|
cpp11::array<V_ID,3>
|
||||||
edges[std::make_pair(i1, i0)].size() > 2 )
|
get_neighbor_vertices(V_ID v_id, P_ID polygon_index)
|
||||||
{
|
{
|
||||||
non_manifold_edges.insert(canonical_edge(i0,i1));
|
std::size_t nbv = polygons[polygon_index].size(), pvid=0;
|
||||||
}
|
for (; pvid!=nbv; ++pvid)
|
||||||
}
|
if (v_id==polygons[polygon_index][pvid]) break;
|
||||||
}
|
CGAL_assertion( pvid!=nbv );
|
||||||
|
V_ID prev = polygons[polygon_index][ (pvid+nbv-1)%nbv ];
|
||||||
|
V_ID next = polygons[polygon_index][ (pvid+1)%nbv ];
|
||||||
|
return make_array(prev,v_id,next);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<V_ID,P_ID>
|
||||||
|
next_cw_vertex_around_source(V_ID src, V_ID tgt)
|
||||||
|
{
|
||||||
|
typedef std::pair<V_ID,P_ID> VID_and_PID;
|
||||||
|
if ( is_edge_marked(src,tgt) ) return VID_and_PID(src,300612);
|
||||||
|
Edge_map_iterator em_it=edges.find(V_ID_pair(tgt, src));
|
||||||
|
if ( em_it==edges.end() ) return VID_and_PID(src,300612);// the vertex is on the border
|
||||||
|
CGAL_assertion(em_it->second.size()==1);
|
||||||
|
P_ID p_id = *(em_it->second.begin());
|
||||||
|
return VID_and_PID(get_neighbor_vertices(src, p_id)[2], p_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<V_ID,P_ID>
|
||||||
|
next_ccw_vertex_around_target(V_ID src, V_ID tgt)
|
||||||
|
{
|
||||||
|
typedef std::pair<V_ID,P_ID> VID_and_PID;
|
||||||
|
if ( is_edge_marked(src,tgt) ) return VID_and_PID(tgt,300612);
|
||||||
|
Edge_map_iterator em_it=edges.find(V_ID_pair(tgt, src));
|
||||||
|
if ( em_it==edges.end() ) return VID_and_PID(tgt,300612);// the vertex is on the border
|
||||||
|
CGAL_assertion(em_it->second.size()==1);
|
||||||
|
P_ID p_id = *(em_it->second.begin());
|
||||||
|
return VID_and_PID(get_neighbor_vertices(tgt, p_id)[0], p_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void inverse_orientation(const std::size_t index) {
|
void inverse_orientation(const std::size_t index) {
|
||||||
std::reverse(polygons[index].begin(), polygons[index].end());
|
std::reverse(polygons[index].begin(), polygons[index].end());
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
void replace_vertex_index_in_polygon(
|
||||||
|
std::size_t polygon_id,
|
||||||
Polygon_soup_orienter(const Points& points, Polygons& polygons)
|
V_ID old_index,
|
||||||
: points(points), polygons(polygons)
|
V_ID new_index)
|
||||||
{
|
{
|
||||||
fill_edges();
|
BOOST_FOREACH(V_ID& i, polygons[polygon_id])
|
||||||
|
if( i==old_index )
|
||||||
|
i=new_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool orient()
|
/// Functions filling containers
|
||||||
|
void fill_edge_map() {
|
||||||
|
// Fill edges
|
||||||
|
edges.clear();
|
||||||
|
for(P_ID i = 0; i < polygons.size(); ++i)
|
||||||
|
{
|
||||||
|
const P_ID size = polygons[i].size();
|
||||||
|
for(P_ID j = 0; j < size; ++j) {
|
||||||
|
V_ID i0 = polygons[i][j];
|
||||||
|
V_ID i1 = polygons[i][ (j+1)%size];
|
||||||
|
edges[V_ID_pair(i0, i1)].insert(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill non-manifold edges
|
||||||
|
marked_edges.clear();
|
||||||
|
for(P_ID i = 0; i < polygons.size(); ++i)
|
||||||
|
{
|
||||||
|
const P_ID size = polygons[i].size();
|
||||||
|
for(P_ID j = 0; j < size; ++j) {
|
||||||
|
V_ID i0 = polygons[i][j];
|
||||||
|
V_ID i1 = polygons[i][ (j+1)%size ];
|
||||||
|
|
||||||
|
std::size_t nb_edges = 0;
|
||||||
|
Edge_map_iterator em_it = edges.find( V_ID_pair(i0, i1) );
|
||||||
|
if ( em_it!=edges.end() ) nb_edges += em_it->second.size();
|
||||||
|
em_it = edges.find( V_ID_pair(i1, i0) );
|
||||||
|
if ( em_it!=edges.end() ) nb_edges += em_it->second.size();
|
||||||
|
|
||||||
|
if( nb_edges > 2 ) set_edge_marked(i0,i1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill_incident_polygons_per_vertex()
|
||||||
|
{
|
||||||
|
incident_polygons_per_vertex.resize(points.size());
|
||||||
|
|
||||||
|
P_ID nb_polygons=polygons.size();
|
||||||
|
for(P_ID ip=0; ip<nb_polygons; ++ip)
|
||||||
|
{
|
||||||
|
BOOST_FOREACH(V_ID iv, polygons[ip])
|
||||||
|
incident_polygons_per_vertex[iv].push_back(ip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Polygon_soup_orienter(Points& points, Polygons& polygons)
|
||||||
|
: points(points), polygons(polygons)
|
||||||
|
{
|
||||||
|
fill_edge_map();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// We try to orient polygon consistently by walking in the dual graph, from
|
||||||
|
/// a not yet re-oriented polygon.
|
||||||
|
/// We have an edge between two polygons if they share an edge, and this edge
|
||||||
|
/// is shared by exactly two polygons. While walking along an edge, we reorient
|
||||||
|
/// the polygon we walked in if its orientation is not compatible with the one
|
||||||
|
/// we come from.
|
||||||
|
/// If the polygon was already marked as oriented, then we cut the dual edge
|
||||||
|
/// in the graph and the primal edge is marked.
|
||||||
|
/// At the same time, we assign an id to each polygon in the same connected
|
||||||
|
/// componenet of the dual graph.
|
||||||
|
void orient()
|
||||||
{
|
{
|
||||||
std::vector<bool> oriented;
|
std::vector<bool> oriented;
|
||||||
std::stack<std::size_t> stack;
|
std::stack<std::size_t> stack;
|
||||||
using std::make_pair;
|
// polygon_cc_id.resize(polygons.size(), -1);
|
||||||
|
|
||||||
// no polygon is oriented
|
// We first consider all polygons as non-oriented
|
||||||
oriented.resize(polygons.size());
|
oriented.resize(polygons.size());
|
||||||
|
|
||||||
size_type polygon_index = 0;
|
P_ID polygon_index = 0;
|
||||||
bool success = true;
|
|
||||||
|
|
||||||
|
// CC_ID current_cc_index=-1;
|
||||||
while (polygon_index != polygons.size())
|
while (polygon_index != polygons.size())
|
||||||
{
|
{
|
||||||
|
// We look for the first polygon not already oriented
|
||||||
while ( polygon_index != polygons.size() && oriented[polygon_index] ) {
|
while ( polygon_index != polygons.size() && oriented[polygon_index] ) {
|
||||||
++polygon_index;
|
++polygon_index;
|
||||||
}
|
}
|
||||||
if(polygon_index == polygons.size()) break;
|
if(polygon_index == polygons.size()) break;
|
||||||
|
|
||||||
|
// ++ current_cc_index; // visit a new connected component
|
||||||
|
|
||||||
|
// we visit the connected component by crossing edges manifold edges
|
||||||
oriented[polygon_index] = true;
|
oriented[polygon_index] = true;
|
||||||
stack.push(polygon_index);
|
stack.push(polygon_index);
|
||||||
while(! stack.empty() )
|
while(! stack.empty() )
|
||||||
{
|
{
|
||||||
const size_type to_be_oriented_index = stack.top();
|
const P_ID to_be_oriented_index = stack.top();
|
||||||
stack.pop();
|
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 Index& i1 = polygons[to_be_oriented_index][ih];
|
|
||||||
const Index& i2 = polygons[to_be_oriented_index][ihp1];
|
|
||||||
|
|
||||||
if(non_manifold_edges.count(canonical_edge(i1,i2)) > 0) {
|
// CGAL_assertion(polygon_cc_id[to_be_oriented_index]==-1);
|
||||||
continue;
|
// polygon_cc_id[to_be_oriented_index]=current_cc_index;
|
||||||
}
|
|
||||||
|
const P_ID size = polygons[to_be_oriented_index].size();
|
||||||
|
for(P_ID ih = 0 ; ih < size ; ++ih) {
|
||||||
|
P_ID ihp1 = (ih+1)%size;
|
||||||
|
const V_ID i1 = polygons[to_be_oriented_index][ih];
|
||||||
|
const V_ID i2 = polygons[to_be_oriented_index][ihp1];
|
||||||
|
|
||||||
|
if( is_edge_marked(i1,i2) ) continue;
|
||||||
|
|
||||||
// edge (i1,i2)
|
// edge (i1,i2)
|
||||||
typename Edges_map::iterator it_same_orient = edges.find(make_pair(i1, i2));
|
Edge_map_iterator it_same_orient = edges.find(V_ID_pair(i1, i2));
|
||||||
// edges (i2,i1)
|
// edges (i2,i1)
|
||||||
typename Edges_map::iterator it_other_orient = edges.find(make_pair(i2, i1));
|
Edge_map_iterator it_other_orient = edges.find(V_ID_pair(i2, i1));
|
||||||
|
|
||||||
CGAL_assertion(it_same_orient != edges.end());
|
CGAL_assertion(it_same_orient != edges.end());
|
||||||
if(it_same_orient->second.size() > 1) {
|
CGAL_assertion(it_other_orient == edges.end() ||
|
||||||
if((it_other_orient != edges.end() && it_other_orient->second.size() > 0) ||
|
it_other_orient->second.size()==1);
|
||||||
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
|
if (it_same_orient->second.size() > 1)
|
||||||
const size_type size = polygons[index].size();
|
{
|
||||||
for(size_type j = 0; j < size; ++j) {
|
CGAL_assertion(it_other_orient == edges.end());
|
||||||
const Index& i0 = polygons[index][j];
|
// one neighbor but with the same orientation
|
||||||
const Index& i1 = polygons[index][ j+1 < size ? j+1: 0];
|
P_ID index = *(it_same_orient->second.begin());
|
||||||
CGAL_assertion_code(const bool r = )
|
if(index == to_be_oriented_index)
|
||||||
edges[std::make_pair(i0, i1)].erase(index)
|
index = *(++it_same_orient->second.begin());
|
||||||
CGAL_assertion_code(!= 0);
|
|
||||||
CGAL_assertion(r);
|
|
||||||
}
|
|
||||||
inverse_orientation(index);
|
|
||||||
for(size_type j = 0; j < size; ++j) {
|
|
||||||
const Index& i0 = polygons[index][j];
|
|
||||||
const Index& 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])
|
if(oriented[index])
|
||||||
continue;
|
{
|
||||||
|
// polygon already oriented but its orientation is not compatible ---> mark the edge and continue
|
||||||
|
set_edge_marked(i1,i2);
|
||||||
|
continue; // next edge
|
||||||
|
}
|
||||||
|
|
||||||
|
// reverse the orientation
|
||||||
|
const P_ID size = polygons[index].size();
|
||||||
|
for(P_ID j = 0; j < size; ++j) {
|
||||||
|
V_ID i0 = polygons[index][j];
|
||||||
|
V_ID i1 = polygons[index][(j+1)%size];
|
||||||
|
Edge_map_iterator em_it = edges.find(V_ID_pair(i0, i1));
|
||||||
|
CGAL_assertion_code(const bool r = )
|
||||||
|
em_it->second.erase(index)
|
||||||
|
CGAL_assertion_code(!= 0);
|
||||||
|
CGAL_assertion(r);
|
||||||
|
if ( em_it->second.empty() ) edges.erase(em_it);
|
||||||
|
}
|
||||||
|
inverse_orientation(index);
|
||||||
|
for(P_ID j = 0; j < size; ++j) {
|
||||||
|
V_ID i0 = polygons[index][j];
|
||||||
|
V_ID i1 = polygons[index][(j+1)%size];
|
||||||
|
edges[V_ID_pair(i0, i1)].insert(index);
|
||||||
|
}
|
||||||
|
// "inverse the orientation of polygon #index
|
||||||
oriented[index] = true;
|
oriented[index] = true;
|
||||||
// "keep the orientation of polygon #%1\n").arg(index);
|
|
||||||
stack.push(index);
|
stack.push(index);
|
||||||
}
|
}
|
||||||
else {
|
else{
|
||||||
if(it_same_orient->second.size() != 1 ||
|
if( it_other_orient != edges.end() ){
|
||||||
(it_other_orient != edges.end() && it_other_orient->second.size() > 0))
|
CGAL_assertion(it_same_orient->second.size() == 1);
|
||||||
{
|
CGAL_assertion(it_other_orient->second.size() == 1);
|
||||||
success = false; // non-orientable
|
// one polygon, same orientation
|
||||||
|
const P_ID index = *(it_other_orient->second.begin());
|
||||||
|
if(oriented[index]) continue; //nothing todo already processed and correctly oriented
|
||||||
|
oriented[index] = true;
|
||||||
|
// "keep the orientation of polygon #index
|
||||||
|
stack.push(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // end for on all edges of one
|
} // end for on all edges of one
|
||||||
} // end while loop on the polygons of the connected component
|
} // end while loop on the polygons of the connected component
|
||||||
} // end while loop on all non-oriented polygons remaining
|
} // end while loop on all non-oriented polygons remaining
|
||||||
|
}
|
||||||
|
|
||||||
return success;
|
/// A vertex is said to be singular if its link is neither a cycle nor a chain,
|
||||||
|
/// but several cycles and chains.
|
||||||
|
/// For each such vertex v, we consider each set of polygons incident to v
|
||||||
|
/// and sharing a non-marked edge incident to v. A copy of v is assigned to
|
||||||
|
/// each but one set of incident polygons.
|
||||||
|
void duplicate_singular_vertices()
|
||||||
|
{
|
||||||
|
fill_incident_polygons_per_vertex();
|
||||||
|
std::vector< std::pair<V_ID, std::vector<P_ID> > > vertices_to_duplicate;
|
||||||
|
|
||||||
|
V_ID nbv = static_cast<V_ID>( points.size() );
|
||||||
|
for (V_ID v_id = 0; v_id < nbv; ++v_id)
|
||||||
|
{
|
||||||
|
const std::vector< P_ID >& incident_polygons = incident_polygons_per_vertex[v_id];
|
||||||
|
|
||||||
|
if ( incident_polygons.empty() ) continue; //isolated vertex
|
||||||
|
std::set<P_ID> visited_polygons;
|
||||||
|
|
||||||
|
bool first_pass = true;
|
||||||
|
BOOST_FOREACH(P_ID p_id, incident_polygons)
|
||||||
|
{
|
||||||
|
if ( !visited_polygons.insert(p_id).second ) continue; // already visited
|
||||||
|
|
||||||
|
if (!first_pass)
|
||||||
|
{
|
||||||
|
vertices_to_duplicate.push_back(std::pair<V_ID, std::vector<P_ID> >());
|
||||||
|
vertices_to_duplicate.back().first=v_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cpp11::array<V_ID,3>& neighbors = get_neighbor_vertices(v_id,p_id);
|
||||||
|
|
||||||
|
V_ID next = neighbors[2];
|
||||||
|
|
||||||
|
if( !first_pass)
|
||||||
|
vertices_to_duplicate.back().second.push_back(p_id);
|
||||||
|
|
||||||
|
do{
|
||||||
|
P_ID other_p_id;
|
||||||
|
cpp11::tie(next, other_p_id) = next_cw_vertex_around_source(v_id, next);
|
||||||
|
if (next==v_id) break;
|
||||||
|
visited_polygons.insert(other_p_id);
|
||||||
|
if( !first_pass)
|
||||||
|
vertices_to_duplicate.back().second.push_back(other_p_id);
|
||||||
|
}
|
||||||
|
while(next!=neighbors[0]);
|
||||||
|
|
||||||
|
if (next==v_id){
|
||||||
|
/// turn the otherway round
|
||||||
|
next = neighbors[0];
|
||||||
|
do{
|
||||||
|
P_ID other_p_id;
|
||||||
|
cpp11::tie(next, other_p_id) = next_ccw_vertex_around_target(next, v_id);
|
||||||
|
if (next==v_id) break;
|
||||||
|
visited_polygons.insert(other_p_id);
|
||||||
|
if( !first_pass)
|
||||||
|
vertices_to_duplicate.back().second.push_back(other_p_id);
|
||||||
|
}
|
||||||
|
while(true);
|
||||||
|
}
|
||||||
|
first_pass=false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// now duplicate the vertices
|
||||||
|
typedef std::pair<V_ID, std::vector<P_ID> > V_ID_and_Polygon_ids;
|
||||||
|
BOOST_FOREACH(const V_ID_and_Polygon_ids& vid_and_pids, vertices_to_duplicate)
|
||||||
|
{
|
||||||
|
V_ID new_index = static_cast<V_ID>(points.size());
|
||||||
|
points.push_back( points[vid_and_pids.first] );
|
||||||
|
BOOST_FOREACH(P_ID polygon_id, vid_and_pids.second)
|
||||||
|
replace_vertex_index_in_polygon(polygon_id, vid_and_pids.first, new_index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to consistently orient a soup of polygons in 3D space.
|
* Tries to consistently orient a soup of polygons in 3D space.
|
||||||
* If a consistent orientation has been found, `true` is returned.
|
* If it is not possible to produce a combinatorial manifold surface, some points will be
|
||||||
* In any case `polygons` is updated.
|
* duplicated. These points are either an endpoint of edges incident to more than
|
||||||
|
* two polygons, or an endpoint of an edge between two polygons with incompatible orientations
|
||||||
|
* (during the re-orientation process), or a point shared by at least two polygons that do not
|
||||||
|
* share an edge this point is incident to.
|
||||||
* @tparam Point_3 the point type
|
* @tparam Point_3 the point type
|
||||||
|
* @tparam Polygon_3 the Polygon type, being a container of indices
|
||||||
*
|
*
|
||||||
* @param points points of the soup of polygons.
|
* @param[in,out] points points of the soup of polygons. Some points might be pushed back to resolve
|
||||||
* @param[in, out] polygons each element in the vector describes a polygon using the index of the points in the vector.
|
* non-manifold or non-orientability issues.
|
||||||
|
* @param[in, out] polygons each element in the vector describes a polygon using the index of the points in `points`.
|
||||||
*
|
*
|
||||||
* @return true if a consistent orientation has been found
|
* @return return false if some points where duplicated, thus producing a self-intersecting polyhedron
|
||||||
*
|
*
|
||||||
* \TODO code: there is no check for duplicate points, yet it can be implemented as separate filter function
|
|
||||||
*/
|
*/
|
||||||
template <class Point_3, class Polygon_3>
|
template <class Point_3, class Polygon_3>
|
||||||
bool orient_polygon_soup(const std::vector<Point_3>& points,
|
bool orient_polygon_soup(std::vector<Point_3>& points,
|
||||||
std::vector< Polygon_3 >& polygons)
|
std::vector< Polygon_3 >& polygons)
|
||||||
{
|
{
|
||||||
|
std::size_t inital_nb_pts = points.size();
|
||||||
internal::Polygon_soup_orienter<Point_3, Polygon_3> orienter(points, polygons);
|
internal::Polygon_soup_orienter<Point_3, Polygon_3> orienter(points, polygons);
|
||||||
return orienter.orient();
|
orienter.orient();
|
||||||
|
orienter.duplicate_singular_vertices();
|
||||||
|
|
||||||
|
return inital_nb_pts==points.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
}// namespace CGAL
|
}// namespace CGAL
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
OFF
|
||||||
|
8 13 0
|
||||||
|
-1 -1 -1
|
||||||
|
-1 1 -1
|
||||||
|
1 1 -1
|
||||||
|
1 -1 -1
|
||||||
|
-1 -1 1
|
||||||
|
-1 1 1
|
||||||
|
1 1 1
|
||||||
|
1 -1 1
|
||||||
|
3 0 1 3
|
||||||
|
3 3 1 2
|
||||||
|
3 0 4 1
|
||||||
|
3 1 4 5
|
||||||
|
3 3 2 7
|
||||||
|
3 7 2 6
|
||||||
|
3 4 0 3
|
||||||
|
3 7 4 3
|
||||||
|
3 6 4 7
|
||||||
|
3 6 5 4
|
||||||
|
3 1 5 6
|
||||||
|
3 2 1 6
|
||||||
|
3 0 1 2
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
OFF
|
||||||
|
14 9 0
|
||||||
|
|
||||||
|
0 0 0
|
||||||
|
1 0 0
|
||||||
|
1 1 0
|
||||||
|
0 1 0
|
||||||
|
2 1 0
|
||||||
|
0 2 0
|
||||||
|
1 2 0
|
||||||
|
2 2 0
|
||||||
|
0 0 0.5
|
||||||
|
0.8 0 0.5
|
||||||
|
1 1 1
|
||||||
|
2 1 1
|
||||||
|
2 2 1
|
||||||
|
1 2 1
|
||||||
|
|
||||||
|
4 0 1 2 3
|
||||||
|
4 2 4 7 6
|
||||||
|
4 3 2 6 5
|
||||||
|
4 2 4 11 10
|
||||||
|
4 6 7 12 13
|
||||||
|
4 10 11 12 13
|
||||||
|
4 0 1 9 8
|
||||||
|
3 13 10 8
|
||||||
|
3 8 9 13
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
OFF
|
||||||
|
9 12 0
|
||||||
|
|
||||||
|
0 0 0
|
||||||
|
1 0 0
|
||||||
|
0 1 0
|
||||||
|
1 1 0
|
||||||
|
0.5 0.5 0
|
||||||
|
0 0 1
|
||||||
|
1 0 1
|
||||||
|
1 1 1
|
||||||
|
0 1 1
|
||||||
|
|
||||||
|
3 1 3 4
|
||||||
|
3 0 1 4
|
||||||
|
3 4 3 2
|
||||||
|
3 0 4 2
|
||||||
|
|
||||||
|
3 5 6 4
|
||||||
|
3 6 7 4
|
||||||
|
3 4 7 8
|
||||||
|
3 5 4 8
|
||||||
|
|
||||||
|
4 0 1 6 5
|
||||||
|
4 0 2 8 5
|
||||||
|
|
||||||
|
4 3 2 8 7
|
||||||
|
4 1 3 7 6
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
OFF
|
||||||
|
5 2 0
|
||||||
|
0 0 0
|
||||||
|
0 1 0
|
||||||
|
1 0 0
|
||||||
|
0 0 1
|
||||||
|
0 1 1
|
||||||
|
3 0 1 2
|
||||||
|
3 0 3 4
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
OFF
|
||||||
|
9 13 0
|
||||||
|
0 1 0
|
||||||
|
1 0 0
|
||||||
|
1 2 0
|
||||||
|
0.5 1 1
|
||||||
|
0 1 2
|
||||||
|
1 0 2
|
||||||
|
1 2 2
|
||||||
|
1.5 1 0
|
||||||
|
1.5 1 0.5
|
||||||
|
3 0 1 2
|
||||||
|
3 0 1 3
|
||||||
|
3 1 2 3
|
||||||
|
3 0 2 3
|
||||||
|
3 3 5 4
|
||||||
|
3 3 5 6
|
||||||
|
3 3 6 4
|
||||||
|
3 4 6 5
|
||||||
|
3 1 2 5
|
||||||
|
3 1 2 7
|
||||||
|
3 1 2 8
|
||||||
|
3 2 5 8
|
||||||
|
3 1 8 5
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
OFF
|
||||||
|
6 4 0
|
||||||
|
0 0 0
|
||||||
|
0 1 0
|
||||||
|
1 0.5 0
|
||||||
|
0 0.5 1
|
||||||
|
0 0.5 -1
|
||||||
|
-1 0.5 0
|
||||||
|
3 0 1 2
|
||||||
|
3 0 1 3
|
||||||
|
3 0 1 4
|
||||||
|
3 0 1 5
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
OFF
|
||||||
|
10 6 0
|
||||||
|
0 0 0
|
||||||
|
0 1 0
|
||||||
|
1 0.5 0
|
||||||
|
0 0.5 1
|
||||||
|
0 0.5 -1
|
||||||
|
-1 0.5 0
|
||||||
|
-1 -1 0
|
||||||
|
1 -1 0
|
||||||
|
-1 -1 1
|
||||||
|
1 -1 1
|
||||||
|
3 0 1 2
|
||||||
|
3 0 1 3
|
||||||
|
3 0 1 4
|
||||||
|
3 0 1 5
|
||||||
|
3 0 6 7
|
||||||
|
3 0 8 9
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
OFF
|
||||||
|
16 8 0
|
||||||
|
|
||||||
|
0 0 0
|
||||||
|
1 0 0
|
||||||
|
2 0 0
|
||||||
|
0 1 0
|
||||||
|
1 1 0
|
||||||
|
2 1 0
|
||||||
|
0 2 0
|
||||||
|
1 2 0
|
||||||
|
2 2 0
|
||||||
|
|
||||||
|
1 1 -1
|
||||||
|
2 1 -1
|
||||||
|
3 1 -1
|
||||||
|
3 1 0
|
||||||
|
1 1 1
|
||||||
|
2 1 1
|
||||||
|
3 1 1
|
||||||
|
|
||||||
|
4 0 1 4 3
|
||||||
|
4 1 2 5 4
|
||||||
|
4 4 5 8 7
|
||||||
|
4 3 4 7 6
|
||||||
|
|
||||||
|
4 9 10 5 4
|
||||||
|
4 10 11 12 5
|
||||||
|
4 5 12 15 14
|
||||||
|
4 4 5 14 13
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
#include <CGAL/Polyhedron_3.h>
|
||||||
|
#include <CGAL/Simple_cartesian.h>
|
||||||
|
|
||||||
|
#include <CGAL/orient_polygon_soup.h>
|
||||||
|
#include <CGAL/polygon_soup_to_polyhedron_3.h>
|
||||||
|
#include <CGAL/IO/OFF_reader.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
typedef CGAL::Simple_cartesian<double> K;
|
||||||
|
typedef CGAL::Polyhedron_3<K> Polyhedron;
|
||||||
|
|
||||||
|
void test(std::string fname, std::size_t expected_duplicated_vertices)
|
||||||
|
{
|
||||||
|
std::vector<K::Point_3> points;
|
||||||
|
std::vector< std::vector<std::size_t> > polygons;
|
||||||
|
std::ifstream input(fname.c_str());
|
||||||
|
if (!input)
|
||||||
|
{
|
||||||
|
std::cerr << "Cannot open file " << fname << "\n";
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CGAL::read_OFF(input, points, polygons))
|
||||||
|
{
|
||||||
|
std::cerr << "Error parsing the OFF file " << fname << "\n";
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t initial_nb_points = points.size();
|
||||||
|
CGAL::orient_polygon_soup(points, polygons);
|
||||||
|
|
||||||
|
assert(expected_duplicated_vertices == points.size()-initial_nb_points);
|
||||||
|
|
||||||
|
Polyhedron P;
|
||||||
|
CGAL::polygon_soup_to_polyhedron_3(P, points, polygons);
|
||||||
|
assert(P.is_valid());
|
||||||
|
std::cout << fname << " OK\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
test("data_polygon_soup/bad_cube.off", 4);
|
||||||
|
test("data_polygon_soup/isolated_singular_vertex_one_cc.off", 1);
|
||||||
|
test("data_polygon_soup/isolated_vertices.off", 1);
|
||||||
|
test("data_polygon_soup/nm_vertex_and_edge.off", 6);
|
||||||
|
test("data_polygon_soup/one_duplicated_edge.off", 6);
|
||||||
|
test("data_polygon_soup/one_duplicated_edge_sharing_vertex.off", 8);
|
||||||
|
test("data_polygon_soup/partial_overlap.off", 4);
|
||||||
|
test("data_polygon_soup/incompatible_orientation.off", 2);
|
||||||
|
}
|
||||||
|
|
@ -88,28 +88,27 @@ void Polyhedron_demo_orient_soup_plugin::orient()
|
||||||
// qDebug() << tr("I have the item %1\n").arg(item->name());
|
// qDebug() << tr("I have the item %1\n").arg(item->name());
|
||||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||||
if(!item->orient()) {
|
if(!item->orient()) {
|
||||||
messages->warning(tr("The polygon soup \"%1\" is not orientable.")
|
QMessageBox::information(mw, tr("Not orientable without self-intersections"),
|
||||||
.arg(item->name()));
|
tr("The polygon soup \"%1\" is not directly orientable."
|
||||||
// QMessageBox::information(mw, tr("Not orientable"),
|
" Some vertices have been duplicated and some self-intersections"
|
||||||
// tr("The polygon soup \"%1\" is not orientable.")
|
" have been created.")
|
||||||
// .arg(item->name()));
|
.arg(item->name()));
|
||||||
scene->itemChanged(item);
|
|
||||||
} else {
|
|
||||||
|
|
||||||
Scene_polyhedron_item* poly_item = new Scene_polyhedron_item();
|
|
||||||
if(item->exportAsPolyhedron(poly_item->polyhedron())) {
|
|
||||||
poly_item->setName(item->name());
|
|
||||||
poly_item->setColor(item->color());
|
|
||||||
poly_item->setRenderingMode(item->renderingMode());
|
|
||||||
poly_item->setVisible(item->visible());
|
|
||||||
poly_item->changed();
|
|
||||||
poly_item->setProperty("source filename", item->property("source filename"));
|
|
||||||
scene->replaceItem(index, poly_item);
|
|
||||||
delete item;
|
|
||||||
} else {
|
|
||||||
scene->itemChanged(item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Scene_polyhedron_item* poly_item = new Scene_polyhedron_item();
|
||||||
|
if(item->exportAsPolyhedron(poly_item->polyhedron())) {
|
||||||
|
poly_item->setName(item->name());
|
||||||
|
poly_item->setColor(item->color());
|
||||||
|
poly_item->setRenderingMode(item->renderingMode());
|
||||||
|
poly_item->setVisible(item->visible());
|
||||||
|
poly_item->changed();
|
||||||
|
poly_item->setProperty("source filename", item->property("source filename"));
|
||||||
|
scene->replaceItem(index, poly_item);
|
||||||
|
delete item;
|
||||||
|
} else {
|
||||||
|
scene->itemChanged(item);
|
||||||
|
}
|
||||||
|
|
||||||
QApplication::restoreOverrideCursor();
|
QApplication::restoreOverrideCursor();
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
|
|
|
||||||
|
|
@ -155,11 +155,10 @@ void Scene_polygon_soup_item::inside_out()
|
||||||
bool
|
bool
|
||||||
Scene_polygon_soup_item::orient()
|
Scene_polygon_soup_item::orient()
|
||||||
{
|
{
|
||||||
if(isEmpty() || this->oriented)
|
if(isEmpty() || oriented)
|
||||||
return true; // nothing to do
|
return true; // nothing to do
|
||||||
|
oriented=true;
|
||||||
oriented = CGAL::orient_polygon_soup(soup->points, soup->polygons);
|
return CGAL::orient_polygon_soup(soup->points, soup->polygons);
|
||||||
return oriented;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue