mirror of https://github.com/CGAL/cgal
817 lines
31 KiB
C++
817 lines
31 KiB
C++
// Copyright (c) 1997 ETH Zurich (Switzerland).
|
|
// 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) : Lutz Kettner <kettner@mpi-sb.mpg.de>)
|
|
|
|
#ifndef CGAL_SURFACE_MESH_INCREMENTAL_BUILDER_H
|
|
#define CGAL_SURFACE_MESH_INCREMENTAL_BUILDER_H 1
|
|
|
|
#include <CGAL/basic.h>
|
|
#include <CGAL/Random_access_adaptor.h>
|
|
#include <CGAL/Unique_hash_map.h>
|
|
#include <CGAL/IO/Verbose_ostream.h>
|
|
#include <CGAL/boost/graph/properties.h>
|
|
#include <CGAL/boost/graph/helpers.h>
|
|
#include <CGAL/boost/graph/graph_traits_Surface_mesh.h>
|
|
|
|
#include <vector>
|
|
#include <cstddef>
|
|
|
|
namespace CGAL {
|
|
|
|
template < class HalfedgeDS_>
|
|
class Surface_mesh_incremental_builder {
|
|
public:
|
|
typedef HalfedgeDS_ HDS; // internal
|
|
typedef HalfedgeDS_ HalfedgeDS;
|
|
typedef typename boost::graph_traits<HDS>::vertex_descriptor Vertex_descriptor;
|
|
typedef typename boost::graph_traits<HDS>::halfedge_descriptor Halfedge_descriptor;
|
|
typedef typename boost::graph_traits<HDS>::face_descriptor Face_descriptor;
|
|
|
|
typedef typename HDS::Point Point_3;
|
|
typedef typename HDS::size_type size_type;
|
|
|
|
protected:
|
|
typedef typename HDS::Vertex_iterator Vertex_iterator;
|
|
typedef typename HDS::Halfedge_iterator Halfedge_iterator;
|
|
typedef Random_access_value_adaptor<Vertex_iterator,Vertex_descriptor> Random_access_index;
|
|
|
|
bool m_error;
|
|
bool m_verbose;
|
|
HDS& hds;
|
|
size_type rollback_v;
|
|
size_type rollback_f;
|
|
size_type rollback_h;
|
|
size_type new_vertices;
|
|
size_type new_faces;
|
|
size_type new_halfedges;
|
|
Face_descriptor current_face;
|
|
Random_access_index index_to_vertex_map;
|
|
std::vector< Halfedge_descriptor> vertex_to_edge_map;
|
|
|
|
Halfedge_descriptor g1; // first halfedge, 0 denotes none.
|
|
Halfedge_descriptor gprime;
|
|
Halfedge_descriptor h1; // current halfedge
|
|
size_type w1; // first vertex.
|
|
size_type w2; // second vertex.
|
|
size_type v1; // current vertex
|
|
bool first_vertex;
|
|
bool last_vertex;
|
|
|
|
CGAL_assertion_code( int check_protocol;) // use to check protocol.
|
|
// states for checking: 0 = created, 1 = constructing, 2 = make face.
|
|
|
|
// Implement the vertex_to_edge_map either with
|
|
// the halfedge pointer in the vertices
|
|
// ----------------------------------------------------
|
|
void initialize_vertex_to_edge_map( size_type n, bool mode) {
|
|
vertex_to_edge_map.clear();
|
|
vertex_to_edge_map.resize(n);
|
|
if ( mode) {
|
|
// go through all halfedges and keep a halfedge for each
|
|
// vertex found in a hashmap.
|
|
size_type i = 0;
|
|
for ( Vertex_iterator vi = hds.vertices_begin();
|
|
vi != hds.vertices_end();
|
|
++vi) {
|
|
set_vertex_to_edge_map( i, halfedge(*vi,hds));
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
|
|
void push_back_vertex_to_edge_map( Halfedge_descriptor h) {
|
|
vertex_to_edge_map.push_back(h);
|
|
}
|
|
Halfedge_descriptor get_vertex_to_edge_map( size_type i) {
|
|
// Use the halfedge pointer within the vertex.
|
|
return halfedge(index_to_vertex_map[i], hds);
|
|
}
|
|
|
|
void set_vertex_to_edge_map( size_type i, Halfedge_descriptor h) {
|
|
// Use the self-managed array vertex_to_edge_map.
|
|
CGAL_assertion(i < vertex_to_edge_map.size());
|
|
vertex_to_edge_map[i] = h;
|
|
// Use the halfedge pointer within the vertex.
|
|
set_halfedge(index_to_vertex_map[i], h, hds);
|
|
}
|
|
|
|
// An Incremental Builder for Polyhedral Surfaces
|
|
// ----------------------------------------------
|
|
// DEFINITION
|
|
//
|
|
// Surface_mesh_incremental_builder<HDS> is an auxiliary class that
|
|
// supports the incremental construction of polyhedral surfaces. This is
|
|
// for example convinient when constructing polyhedral surfaces from
|
|
// files. The incremental construction starts with a list of all point
|
|
// coordinates and concludes with a list of all facet polygons. Edges are
|
|
// not explicitly specified. They are derived from the incidence
|
|
// information provided from the facet polygons. These are given as a
|
|
// sequence of vertex indices. The correct protocol of method calls to
|
|
// build a polyhedral surface can be stated as regular expression:
|
|
//
|
|
// `begin_surface (add_vertex | (begin_facet add_vertex_to_facet*
|
|
// end_facet))* end_surface '
|
|
//
|
|
// PARAMETERS
|
|
//
|
|
// `HDS' is the halfedge data structure used to represent the
|
|
// polyhedral surface that is to be constructed.
|
|
//
|
|
// CREATION
|
|
public:
|
|
bool error() const { return m_error; }
|
|
|
|
Surface_mesh_incremental_builder( HDS& h, bool verbose = false)
|
|
// stores a reference to the halfedge data structure `h' in the
|
|
// internal state. The previous polyhedral surface in `h'
|
|
// remains unchanged. The incremental builder adds the new
|
|
// polyhedral surface to the old one.
|
|
: m_error( false), m_verbose( verbose), hds(h) {
|
|
CGAL_assertion_code(check_protocol = 0;)
|
|
}
|
|
|
|
~Surface_mesh_incremental_builder() {
|
|
CGAL_assertion( check_protocol == 0);
|
|
}
|
|
|
|
// OPERATIONS
|
|
enum { RELATIVE_INDEXING = 0, ABSOLUTE_INDEXING = 1};
|
|
|
|
|
|
void begin_surface( std::size_t v, std::size_t f, std::size_t h = 0,
|
|
int mode = RELATIVE_INDEXING);
|
|
// starts the construction. v is the number of new
|
|
// vertices to expect, f the number of new facets, and h the number of
|
|
// new halfedges. If h is unspecified (`== 0') it is estimated using
|
|
// Euler equations (plus 5% for the so far unkown holes and genus
|
|
// of the object). These values are used to reserve space in the
|
|
// surface_mesh representation `HDS'. If the representation
|
|
// supports insertion these values do not restrict the class of
|
|
// readable surface_meshs. If the representation does not support
|
|
// insertion the object must fit in the reserved sizes.
|
|
// If `mode' is set to ABSOLUTE_INDEXING the incremental builder
|
|
// uses absolute indexing and the vertices of the old polyhedral
|
|
// surface can be used in new facets. Otherwise relative indexing is
|
|
// used starting with new indices for the new construction.
|
|
|
|
|
|
Vertex_descriptor add_vertex( const Point_3& p) {
|
|
// adds p to the vertex list.
|
|
CGAL_assertion( check_protocol == 1);
|
|
Vertex_descriptor v = CGAL::add_vertex(hds);
|
|
set_halfedge( v, Halfedge_descriptor(),hds);
|
|
put(vertex_point, hds, v, p);
|
|
index_to_vertex_map.push_back(Vertex_iterator(v, &hds));
|
|
push_back_vertex_to_edge_map( Halfedge_descriptor());
|
|
++new_vertices;
|
|
return v;
|
|
}
|
|
|
|
|
|
// returns handle for the vertex of index i
|
|
Vertex_descriptor vertex( std::size_t i) {
|
|
if ( i < new_vertices)
|
|
return index_to_vertex_map[i];
|
|
return Vertex_descriptor();
|
|
}
|
|
|
|
Face_descriptor begin_facet() {
|
|
// starts a facet.
|
|
if ( m_error)
|
|
return Face_descriptor();
|
|
CGAL_assertion( check_protocol == 1);
|
|
CGAL_assertion_code( check_protocol = 2;)
|
|
|
|
// initialize all status variables.
|
|
first_vertex = true; // denotes 'no vertex yet'
|
|
g1 = Halfedge_descriptor(); // denotes 'no halfedge yet'
|
|
last_vertex = false;
|
|
|
|
return add_face(hds);
|
|
}
|
|
|
|
void add_vertex_to_facet( std::size_t i);
|
|
// adds a vertex with index i to the current facet. The first
|
|
// point added with `add_vertex()' has the index 0.
|
|
|
|
Halfedge_descriptor end_facet() {
|
|
// ends a facet.
|
|
if ( m_error)
|
|
return Halfedge_descriptor();
|
|
CGAL_assertion( check_protocol == 2);
|
|
CGAL_assertion( ! first_vertex);
|
|
// cleanup all static status variables
|
|
add_vertex_to_facet( w1);
|
|
if ( m_error)
|
|
return Halfedge_descriptor();
|
|
last_vertex = true;
|
|
add_vertex_to_facet( w2);
|
|
if ( m_error)
|
|
return Halfedge_descriptor();
|
|
CGAL_assertion( check_protocol == 2);
|
|
CGAL_assertion_code( check_protocol = 1;)
|
|
Halfedge_descriptor h = get_vertex_to_edge_map(w1);
|
|
set_halfedge(current_face, h, hds);
|
|
++new_faces;
|
|
return h;
|
|
}
|
|
|
|
template <class InputIterator>
|
|
Halfedge_descriptor add_facet( InputIterator first, InputIterator beyond) {
|
|
// synonym for begin_facet(), a call to add_vertex_to_facet() for each iterator
|
|
// value type, and end_facet().
|
|
begin_facet();
|
|
for ( ; ! m_error && first != beyond; ++first)
|
|
add_vertex_to_facet( *first);
|
|
if ( m_error)
|
|
return Halfedge_descriptor();
|
|
return end_facet();
|
|
}
|
|
|
|
template <class InputIterator>
|
|
bool test_facet( InputIterator first, InputIterator beyond) {
|
|
// tests if the facet described by the vertex indices in the
|
|
// range [first,beyond) can be inserted without creating a
|
|
// a non-manifold (and therefore invalid) situation.
|
|
// First, create a copy of the indices and close it cyclically
|
|
std::vector< std::size_t> indices( first, beyond);
|
|
if ( indices.size() < 3)
|
|
return false;
|
|
indices.push_back( indices[0]);
|
|
return test_facet_indices( indices);
|
|
}
|
|
|
|
bool test_facet_indices( std::vector< std::size_t> indices);
|
|
|
|
void end_surface();
|
|
// ends the construction.
|
|
|
|
bool check_unconnected_vertices();
|
|
// returns `true' if unconnected vertices are detected. If `verb'
|
|
// is set to `true' debug information about the unconnected
|
|
// vertices is printed.
|
|
|
|
bool remove_unconnected_vertices();
|
|
|
|
|
|
void rollback();
|
|
|
|
protected:
|
|
Halfedge_descriptor lookup_hole( std::size_t w) {
|
|
CGAL_assertion( w < new_vertices);
|
|
return lookup_hole( get_vertex_to_edge_map( w));
|
|
}
|
|
|
|
size_type find_vertex( Vertex_descriptor v) {
|
|
// Returns 0 if v == NULL.
|
|
if ( v == Vertex_descriptor() )
|
|
return 0;
|
|
size_type n = 0;
|
|
typename HDS::Vertex_iterator it, end;
|
|
boost::tie(it,end) = hds.vertices();
|
|
while ( *it != v) {
|
|
CGAL_assertion( it != end);
|
|
++n;
|
|
++it;
|
|
}
|
|
n = n - rollback_v;
|
|
return n;
|
|
}
|
|
|
|
size_type find_facet( Face_descriptor f) {
|
|
// Returns 0 if f == NULL.
|
|
if ( f == Face_descriptor())
|
|
return 0;
|
|
size_type n = 0;
|
|
typename HDS::Face_iterator it,end;
|
|
boost::tie(it,end) = hds.faces();
|
|
while ( *it != f) {
|
|
CGAL_assertion( it != end);
|
|
++n;
|
|
++it;
|
|
}
|
|
n = n - rollback_f;
|
|
return n;
|
|
}
|
|
|
|
Halfedge_descriptor lookup_halfedge( size_type w, size_type v) {
|
|
// Pre: 0 <= w,v < new_vertices
|
|
// Case a: It exists an halfedge g from w to v:
|
|
// g must be a border halfedge and the facet of g->opposite()
|
|
// must be set and different from the current facet.
|
|
// Set the facet of g to the current facet. Return the
|
|
// halfedge pointing to g.
|
|
// Case b: It exists no halfedge from w to v:
|
|
// Create a new pair of halfedges g and g->opposite().
|
|
// Set the facet of g to the current facet and g->opposite()
|
|
// to a border halfedge. Assign the vertex references.
|
|
// Set g->opposite()->next() to g. Return g->opposite().
|
|
|
|
CGAL_assertion( w < new_vertices);
|
|
CGAL_assertion( v < new_vertices);
|
|
CGAL_assertion( ! last_vertex);
|
|
|
|
Halfedge_descriptor e = get_vertex_to_edge_map( w);
|
|
if ( e != Halfedge_descriptor()) {
|
|
CGAL_assertion( target(e,hds) == index_to_vertex_map[w]);
|
|
// check that the facet has no self intersections
|
|
if ( current_face != Face_descriptor()
|
|
&& current_face == face(e,hds)) {
|
|
Verbose_ostream verr( m_verbose);
|
|
verr << " " << std::endl;
|
|
verr << "CGAL::Surface_mesh_incremental_builder<HDS>::"
|
|
<< std::endl;
|
|
verr << "lookup_halfedge(): input error: facet "
|
|
<< new_faces << " has a self intersection at vertex "
|
|
<< w << "." << std::endl;
|
|
m_error = true;
|
|
return Halfedge_descriptor();
|
|
}
|
|
Halfedge_descriptor start_edge(e);
|
|
do {
|
|
if ( target(next(e,hds),hds) == index_to_vertex_map[v]) {
|
|
if ( ! is_border(next(e,hds),hds)) {
|
|
Verbose_ostream verr( m_verbose);
|
|
verr << " " << std::endl;
|
|
verr << "CGAL::Surface_mesh_incremental_builder"
|
|
"<HDS>::" << std::endl;
|
|
verr << "lookup_halfedge(): input error: facet "
|
|
<< new_faces << " shares a halfedge from "
|
|
"vertex " << w << " to vertex " << v
|
|
<< " with";
|
|
if ( m_verbose && current_face != Face_descriptor())
|
|
verr << " facet "
|
|
<< find_facet( face(next(e,hds), hds))
|
|
<< '.' << std::endl;
|
|
else
|
|
verr << " another facet." << std::endl;
|
|
m_error = true;
|
|
return Halfedge_descriptor();
|
|
}
|
|
CGAL_assertion( ! is_border(opposite(next(e,hds),hds), hds) );
|
|
if ( current_face != Face_descriptor() && current_face ==
|
|
face(opposite(next(e,hds),hds),hds)) {
|
|
Verbose_ostream verr( m_verbose);
|
|
verr << " " << std::endl;
|
|
verr << "CGAL::Surface_mesh_incremental_builder"
|
|
"<HDS>::" << std::endl;
|
|
verr << "lookup_halfedge(): input error: facet "
|
|
<< new_faces << " has a self intersection "
|
|
"at the halfedge from vertex " << w
|
|
<< " to vertex " << v << "." << std::endl;
|
|
m_error = true;
|
|
return Halfedge_descriptor();
|
|
}
|
|
set_face( next(e,hds), current_face, hds);
|
|
// The following line prevents e->next() to be picked
|
|
// by get_vertex_to_edge_map(v) in an upcoming call
|
|
// of lookup_halfedge(v, *)
|
|
set_vertex_to_edge_map( v, opposite(next(next(e,hds),hds),hds));
|
|
return e;
|
|
}
|
|
e = opposite(next(e,hds),hds);
|
|
} while ( e != start_edge);
|
|
}
|
|
// create a new halfedge
|
|
|
|
e = halfedge(add_edge(hds),hds);
|
|
new_halfedges++;
|
|
new_halfedges++;
|
|
set_face(e, current_face,hds);
|
|
set_target(e, index_to_vertex_map[v], hds);
|
|
set_next(e, Halfedge_descriptor(),hds);
|
|
e = opposite(e,hds);
|
|
set_target(e, index_to_vertex_map[w], hds);
|
|
set_next(e, opposite(e,hds),hds);
|
|
return e;
|
|
}
|
|
|
|
Halfedge_descriptor lookup_hole( Halfedge_descriptor e) {
|
|
// Halfedge e points to a vertex w. Walk around w to find a hole
|
|
// in the facet structure. Report an error if none exist. Return
|
|
// the halfedge at this hole that points to the vertex w.
|
|
CGAL_assertion( e != Halfedge_descriptor());
|
|
|
|
Halfedge_descriptor start_edge( e);
|
|
do {
|
|
if ( is_border(next(e,hds),hds)) {
|
|
return e;
|
|
}
|
|
e = opposite(next(e,hds),hds);
|
|
} while ( e != start_edge);
|
|
|
|
Verbose_ostream verr( m_verbose);
|
|
verr << " " << std::endl;
|
|
verr << "CGAL::Surface_mesh_incremental_builder<HDS>::" << std::endl;
|
|
verr << "lookup_hole(): input error: at vertex "
|
|
<< find_vertex( target(e,hds))
|
|
<< " a closed surface already exists and facet "
|
|
<< new_faces << " is nonetheless adjacent." << std::endl;
|
|
if ( m_verbose && current_face != Face_descriptor()) {
|
|
verr << " The closed cycle of facets is:";
|
|
do {
|
|
if ( ! is_border(e,hds))
|
|
verr << " " << find_facet( face(e,hds));
|
|
e = opposite(next(e,hds),hds);
|
|
} while ( e != start_edge);
|
|
verr << '.' << std::endl;
|
|
}
|
|
m_error = true;
|
|
return Halfedge_descriptor();
|
|
}
|
|
};
|
|
|
|
template < class HDS>
|
|
void
|
|
Surface_mesh_incremental_builder<HDS>::
|
|
rollback() {
|
|
CGAL_assertion( rollback_v <= num_vertices(hds));
|
|
CGAL_assertion( rollback_h <= num_halfedges(hds));
|
|
CGAL_assertion( rollback_f <= num_faces(hds));
|
|
if ( rollback_v == 0 && rollback_h == 0 && rollback_f == 0) {
|
|
hds.clear();
|
|
} else {
|
|
while ( rollback_v != (hds.num_vertices() - hds.num_removed_vertices()))
|
|
hds.vertices_pop_back();
|
|
|
|
while ( rollback_h != (hds.num_halfedges() - hds.num_removed_halfedges()))
|
|
hds.edges_pop_back();
|
|
while ( rollback_f != (hds.num_faces() - - hds.num_removed_faces()))
|
|
hds.faces_pop_back();
|
|
}
|
|
m_error = false;
|
|
CGAL_assertion_code( check_protocol = 0;)
|
|
}
|
|
|
|
template < class HDS> CGAL_MEDIUM_INLINE
|
|
void
|
|
Surface_mesh_incremental_builder<HDS>::
|
|
begin_surface( std::size_t v, std::size_t f, std::size_t h, int mode) {
|
|
CGAL_assertion( check_protocol == 0);
|
|
CGAL_assertion_code( check_protocol = 1;)
|
|
CGAL_assertion( ! m_error);
|
|
if ( mode == RELATIVE_INDEXING) {
|
|
new_vertices = 0;
|
|
new_faces = 0;
|
|
new_halfedges = 0;
|
|
rollback_v = hds.num_vertices();
|
|
rollback_f = hds.num_faces();
|
|
rollback_h = hds.num_halfedges();
|
|
} else {
|
|
new_vertices = hds.num_vertices();
|
|
new_faces = hds.num_faces();
|
|
new_halfedges = hds.num_halfedges();
|
|
rollback_v = 0;
|
|
rollback_f = 0;
|
|
rollback_h = 0;
|
|
}
|
|
if ( h == 0) {
|
|
// Use the Eulerian equation for connected planar graphs. We do
|
|
// not know the number of facets that are holes and we do not
|
|
// know the genus of the surface. So we add 12 and a factor of
|
|
// 5 percent.
|
|
h = (std::size_t)((double)(v + f - 2 + 12) * 2.1);
|
|
}
|
|
hds.reserve( hds.num_vertices() + v,
|
|
hds.num_halfedges() + h,
|
|
hds.num_faces() + f);
|
|
if ( mode == RELATIVE_INDEXING) {
|
|
index_to_vertex_map = Random_access_index( hds.vertices().second);
|
|
index_to_vertex_map.reserve(v);
|
|
initialize_vertex_to_edge_map( v, false);
|
|
} else {
|
|
Vertex_iterator b,e;
|
|
boost::tie(b,e) = vertices(hds);
|
|
index_to_vertex_map = Random_access_index(b,e);
|
|
index_to_vertex_map.reserve( hds.num_vertices() + v);
|
|
initialize_vertex_to_edge_map( hds.num_vertices() + v, true);
|
|
}
|
|
}
|
|
|
|
template < class HDS>
|
|
void
|
|
Surface_mesh_incremental_builder<HDS>::
|
|
add_vertex_to_facet( std::size_t v2) {
|
|
if ( m_error)
|
|
return;
|
|
CGAL_assertion( check_protocol == 2);
|
|
if ( v2 >= new_vertices) {
|
|
Verbose_ostream verr( m_verbose);
|
|
verr << " " << std::endl;
|
|
verr << "CGAL::Surface_mesh_incremental_builder<HDS>::"
|
|
<< std::endl;
|
|
verr << "add_vertex_to_facet(): vertex index " << v2
|
|
<< " is out-of-range [0," << new_vertices-1 << "]."
|
|
<< std::endl;
|
|
m_error = true;
|
|
return;
|
|
}
|
|
|
|
if ( first_vertex) {
|
|
CGAL_assertion( ! last_vertex);
|
|
w1 = v2;
|
|
first_vertex = false;
|
|
return;
|
|
}
|
|
if ( g1 == Halfedge_descriptor()) {
|
|
CGAL_assertion( ! last_vertex);
|
|
gprime = lookup_halfedge( w1, v2);
|
|
if ( m_error)
|
|
return;
|
|
h1 = g1 = next(gprime,hds);
|
|
v1 = w2 = v2;
|
|
return;
|
|
}
|
|
// g1, h1, v1, w1, w2 are set. Insert halfedge.
|
|
// Lookup v1-->v2
|
|
Halfedge_descriptor hprime;
|
|
if ( last_vertex)
|
|
hprime = gprime;
|
|
else {
|
|
hprime = lookup_halfedge( v1, v2);
|
|
if ( m_error)
|
|
return;
|
|
}
|
|
Halfedge_descriptor h2 = next(hprime,hds);
|
|
CGAL_assertion( ! last_vertex || h2 == g1);
|
|
Halfedge_descriptor prev = next(h1,hds);
|
|
set_next(h1, h2, hds);
|
|
|
|
if ( get_vertex_to_edge_map( v1) == Halfedge_descriptor()) { // case 1:
|
|
set_next(opposite(h2,hds), opposite(h1,hds),hds);
|
|
|
|
} else { // case 2:
|
|
bool b1 = is_border(opposite(h1,hds), hds);
|
|
bool b2 = is_border(opposite(h2,hds), hds);
|
|
if ( b1 && b2) {
|
|
Halfedge_descriptor hole = lookup_hole( v1);
|
|
if ( m_error)
|
|
return;
|
|
CGAL_assertion( hole != Halfedge_descriptor());
|
|
set_next(opposite(h2,hds),next(hole,hds), hds);
|
|
set_next(hole, opposite(h1,hds), hds);
|
|
} else if ( b2) { // case 2.b:
|
|
CGAL_assertion( is_border(prev,hds));
|
|
set_next(opposite(h2,hds), prev, hds);
|
|
} else if ( b1) { // case 2.c:
|
|
CGAL_assertion( is_border(hprime,hds));
|
|
set_next(hprime, opposite(h1,hds),hds);
|
|
|
|
} else if ( next(opposite(h2,hds),hds) == opposite(h1,hds)) {// case 2.d:
|
|
// f1 == f2
|
|
CGAL_assertion( face(opposite(h1,hds),hds) ==
|
|
face(opposite(h2,hds),hds) );
|
|
} else { // case 2.e:
|
|
if ( prev == h2) { // case _i:
|
|
// nothing to be done, hole is closed.
|
|
} else { // case _ii:
|
|
CGAL_assertion( is_border(prev,hds));
|
|
CGAL_assertion( is_border(hprime,hds));
|
|
set_next(hprime, prev, hds);
|
|
// Check whether the halfedges around v1 are connected.
|
|
// It is sufficient to check it for h1 to prev.
|
|
// Assert loop termination:
|
|
CGAL_assertion_code( std::size_t k = 0;)
|
|
// Look for a hole in the facet complex starting at h1.
|
|
Halfedge_descriptor hole;
|
|
Halfedge_descriptor e = h1;
|
|
do {
|
|
if ( is_border(e,hds))
|
|
hole = e;
|
|
e = opposite(next(e,hds),hds);
|
|
CGAL_assertion( k++ < hds.num_halfedges());
|
|
} while ( next(e,hds) != prev && e != h1);
|
|
if ( e == h1) {
|
|
// disconnected facet complexes
|
|
if ( hole != Halfedge_descriptor()) {
|
|
// The complex can be connected with
|
|
// the hole at hprime.
|
|
set_next(hprime, next(hole,hds),hds);
|
|
set_next(hole, prev,hds);
|
|
} else {
|
|
Verbose_ostream verr( m_verbose);
|
|
verr << " " << std::endl;
|
|
verr << "CGAL::Surface_mesh_incremental_builder<"
|
|
"HDS>::" << std::endl;
|
|
verr << "add_vertex_to_facet(): input error: "
|
|
"disconnected facet complexes at vertex "
|
|
<< v1 << ":" << std::endl;
|
|
|
|
if ( m_verbose && current_face != Face_descriptor()) {
|
|
verr << " involved facets are:";
|
|
do {
|
|
if ( ! is_border(e,hds))
|
|
verr << " " << find_facet(face(e,hds));
|
|
e = opposite(next(e,hds),hds);
|
|
} while ( e != h1);
|
|
verr << " (closed cycle) and";
|
|
e = hprime;
|
|
do {
|
|
if ( ! is_border(e,hds))
|
|
verr << " " << find_facet(face(e,hds));
|
|
} while ( e != hprime);
|
|
verr << "." << std::endl;
|
|
}
|
|
m_error = true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ( target(h1,hds) == index_to_vertex_map[v1])
|
|
set_vertex_to_edge_map( v1, h1);
|
|
CGAL_assertion( target(h1,hds) == index_to_vertex_map[v1]);
|
|
h1 = h2;
|
|
v1 = v2;
|
|
}
|
|
|
|
template < class HDS>
|
|
bool
|
|
Surface_mesh_incremental_builder<HDS>::
|
|
test_facet_indices( std::vector< std::size_t> indices) {
|
|
|
|
// tests if the facet described by the vertex indices can be inserted
|
|
// without creating a non-manifold (and therefore invalid) situation.
|
|
// indices are cyclically closed once.
|
|
std::size_t n = indices.size() - 1;
|
|
// Test if a vertex is not twice in the indices
|
|
for ( std::size_t i = 0; i < n; ++i) {
|
|
CGAL_precondition( indices[i] < new_vertices);
|
|
// check if vertex indices[i] is already in the sequence [0..i-1]
|
|
for ( std::size_t k = 0; k < i; ++k) {
|
|
if ( indices[k] == indices[i])
|
|
return false;
|
|
}
|
|
}
|
|
// Test non-manifold halfedges
|
|
for ( std::size_t i = 0; i < n; ++i) {
|
|
// halfedge goes from vertex indices[i] to indices[i+1]
|
|
// we know already that the halfedge is only once in the sequence
|
|
// (otherwise the end-vertices would be twice in the sequence too)
|
|
// check if halfedge is already in the HDS and is not border halfedge
|
|
Halfedge_descriptor v = get_vertex_to_edge_map(indices[i]);
|
|
Vertex_descriptor w = index_to_vertex_map[indices[i+1]];
|
|
if ( v != Halfedge_descriptor()
|
|
&& get_vertex_to_edge_map(indices[i+1]) != Halfedge_descriptor()) {
|
|
// cycle through halfedge-loop and find edge to indices[i+1]
|
|
Halfedge_descriptor vstart = v;
|
|
do {
|
|
v = opposite(next(vstart,hds),hds);
|
|
} while ( target(next(v,hds),hds) != w && v != vstart);
|
|
if ( target(next(v,hds),hds) == w && ! is_border(next(v,hds),hds))
|
|
return false;
|
|
}
|
|
}
|
|
// test non-manifold vertices
|
|
for ( std::size_t i = 0; i < n; ++i) {
|
|
// since we don't allow duplicates in indices[..] and we
|
|
// tested for non-manifold halfedges already, we just need to check
|
|
// if the vertex indices[i] is not a closed manifold yet.
|
|
Halfedge_descriptor v = get_vertex_to_edge_map(indices[i]);
|
|
if ( v != Halfedge_descriptor()) {
|
|
Halfedge_descriptor vstart = v;
|
|
do {
|
|
v = opposite(next(v,hds),hds);
|
|
} while ( ! is_border(v,hds) && v != vstart);
|
|
if ( ! is_border(v,hds))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//Test if all halfedges of the new face
|
|
//are possibly consecutive border halfedges in the HDS.
|
|
//Possibly because it may be not directly encoded in the HDS
|
|
//(using next() function ). This situation can occur when one or
|
|
//more facets share only a vertex: For example, the new facet we try to add
|
|
//would make the vertex indices[i] a manifold but this should be forbidden
|
|
//if a facet only incident to that vertex has already been inserted.
|
|
//We check this for each vertex of the sequence.
|
|
for ( std::size_t i = 0; i < n; ++i) {
|
|
std::size_t prev_index=indices[ (i-1+n)%n];
|
|
std::size_t next_index=indices[ (i+1)%n];
|
|
Vertex_descriptor previous_vertex = index_to_vertex_map[ prev_index ];
|
|
Vertex_descriptor next_vertex = index_to_vertex_map[ next_index ];
|
|
|
|
Halfedge_descriptor v = get_vertex_to_edge_map(indices[i]);
|
|
|
|
if ( v == Halfedge_descriptor() ||
|
|
get_vertex_to_edge_map(prev_index) == Halfedge_descriptor() ||
|
|
get_vertex_to_edge_map(next_index) == Halfedge_descriptor()
|
|
) continue;
|
|
|
|
Halfedge_descriptor start=v;
|
|
//halfedges pointing to/running out from vertex indices[i]
|
|
//and that need to be possibly consecutive
|
|
Halfedge_descriptor previous=Halfedge_descriptor(),next=Halfedge_descriptor();
|
|
|
|
//look for a halfedge incident to vertex indices[i]
|
|
//and which opposite is incident to previous_vertex
|
|
do{
|
|
if (target(opposite(v,hds),hds)==previous_vertex){
|
|
previous=v;
|
|
CGAL_precondition(is_border(previous,hds));
|
|
break;
|
|
}
|
|
v = opposite(next(v,hds),hds);
|
|
}
|
|
while (v!=start);
|
|
|
|
if (previous!=Halfedge_descriptor()){
|
|
v = opposite(next(v,hds),hds);
|
|
//previous and next are already consecutive in the HDS
|
|
if (target(opposite(v,hds),hds)==next_vertex) continue;
|
|
|
|
//look for a border halfedge which opposite is
|
|
//incident to next_vertex: set next halfedge
|
|
do
|
|
{
|
|
if (target(opposite(v,hds),hds)==next_vertex){
|
|
next = opposite(v,hds);
|
|
break;
|
|
}
|
|
v= opposite(next(v,hds));
|
|
}
|
|
while(v!=previous);
|
|
if (next==Halfedge_descriptor()) continue;
|
|
|
|
//check if no constraint prevents
|
|
//previous and next to be adjacent:
|
|
do{
|
|
v = opposite(next(v,hds),hds);
|
|
if ( is_border(opposite(v,hds),hds) ) break;
|
|
}
|
|
while (v!=previous);
|
|
if (v==previous) return false;
|
|
start=v;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
template < class HDS> CGAL_MEDIUM_INLINE
|
|
void
|
|
Surface_mesh_incremental_builder<HDS>::
|
|
end_surface() {
|
|
if ( m_error)
|
|
return;
|
|
CGAL_assertion( check_protocol == 1);
|
|
CGAL_assertion_code( check_protocol = 0;)
|
|
}
|
|
|
|
template < class HDS>
|
|
bool
|
|
Surface_mesh_incremental_builder<HDS>::
|
|
check_unconnected_vertices() {
|
|
if ( m_error)
|
|
return false;
|
|
bool unconnected = false;
|
|
Verbose_ostream verr( m_verbose);
|
|
for ( std::size_t i = 0; i < new_vertices; i++) {
|
|
if ( get_vertex_to_edge_map( i) == Halfedge_descriptor()) {
|
|
verr << "CGAL::Surface_mesh_incremental_builder<HDS>::\n"
|
|
<< "check_unconnected_vertices( verb = true): "
|
|
<< "vertex " << i << " is unconnected." << std::endl;
|
|
unconnected = true;
|
|
}
|
|
}
|
|
return unconnected;
|
|
}
|
|
|
|
template < class HDS>
|
|
bool
|
|
Surface_mesh_incremental_builder<HDS>::
|
|
remove_unconnected_vertices() {
|
|
if ( m_error)
|
|
return true;
|
|
for( std::size_t i = 0; i < new_vertices; i++) {
|
|
if( get_vertex_to_edge_map( i) == Halfedge_descriptor()) {
|
|
remove_vertex( index_to_vertex_map[i], hds);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} //namespace CGAL
|
|
|
|
#endif // CGAL_SURFACE_MESH_INCREMENTAL_BUILDER_H //
|
|
// EOF //
|