WIP: change API of scale space (separate core/smoothing/meshing)

This commit is contained in:
Simon Giraudot 2017-04-28 18:07:53 +02:00
parent fc29bc3a6a
commit f8fd2a6eaf
3 changed files with 1369 additions and 933 deletions

View File

@ -0,0 +1,914 @@
#ifndef CGAL_SCALE_SPACE_RECONSTRUCTION_3_ALPHA_SHAPE_MESHER_H
#define CGAL_SCALE_SPACE_RECONSTRUCTION_3_ALPHA_SHAPE_MESHER_H
#include <CGAL/Scale_space_reconstruction_3/Shape_construction_3.h>
#include <CGAL/Union_find.h>
namespace CGAL
{
namespace Scale_space_reconstruction_3
{
template <typename Geom_traits, typename FixedSurface = Tag_true>
class Alpha_shape_mesher
{
public:
typedef typename Geom_traits::FT FT;
typedef typename Geom_traits::Point_3 Point; ///< defines the point type.
typedef CGAL::cpp11::array< unsigned int, 3 > Triple; ///< defines a triple of point indices indicating a triangle of the surface.
private:
typedef std::list< Triple > Tripleset; ///< defines a collection of triples.
// Note that this is a list for two reasons: iterator validity for the shell iterators, and memory requirements for the expected huge collections.
public:
#ifdef DOXYGEN_RUNNING
typedef unspecified_type Triple_iterator; ///< defines an iterator over the triples.
typedef const unspecified_type Triple_const_iterator; ///< defines a constant iterator over the triples.
#else // DOXYGEN_RUNNING
typedef Tripleset::iterator Triple_iterator;
typedef Tripleset::const_iterator Triple_const_iterator;
#endif // DOXYGEN_RUNNING
private:
typedef std::vector< Triple_iterator > TripleIterSet;
private:
// Constructing the surface.
typedef CGAL::Shape_construction_3<Geom_traits, FixedSurface> Shape_construction_3;
typedef typename Shape_construction_3::Shape Shape;
typedef typename Shape_construction_3::Triangulation Triangulation;
typedef typename Shape::Vertex_handle Vertex_handle;
typedef typename Shape::Cell_handle Cell_handle;
typedef typename Shape::Facet Facet;
typedef typename Shape::Edge Edge;
typedef std::pair<Vertex_handle, Vertex_handle> VEdge;
typedef typename Shape::Vertex_iterator Vertex_iterator;
typedef typename Shape::Cell_iterator Cell_iterator;
typedef typename Shape::Facet_iterator Facet_iterator;
typedef typename Shape::Edge_iterator Edge_iterator;
typedef typename Shape::Finite_cells_iterator Finite_cells_iterator;
typedef typename Shape::Finite_facets_iterator Finite_facets_iterator;
typedef typename Shape::Finite_edges_iterator Finite_edges_iterator;
typedef typename Shape::Finite_vertices_iterator Finite_vertices_iterator;
typedef typename Shape::Facet_circulator Facet_circulator;
typedef typename Shape::All_cells_iterator All_cells_iterator;
typedef typename Shape::Classification_type Classification_type;
typedef std::map<Facet, unsigned int> Map_facet_to_shell;
typedef typename CGAL::cpp11::array<std::set<Facet>, 2 > Bubble;
bool _separate_shells;
bool _force_manifold;
FT _border_angle;
// The shape must be a pointer, because the alpha of a Fixed_alpha_shape_3
// can only be set at construction and its assignment operator is private.
// We want to be able to set the alpha after constructing the scale-space
// reconstructer object.
Shape* _shape;
// The surface. If the surface is collected per shell, the triples of the
// same shell are stored consecutively.
Tripleset _surface;
// The shells can be accessed through iterators to the surface.
TripleIterSet _shells;
// If the surface is forced to be manifold, removed facets are stored
Tripleset _garbage;
// Map TDS facets to shells
Map_facet_to_shell _map_f2s;
// std::map<Facet, unsigned int> _map_f2s;
unsigned int _index;
std::vector<Bubble> _bubbles;
std::map<Facet, std::size_t> _map_f2b;
FT _squared_radius;
public:
Alpha_shape_mesher (bool separate_shells = false, bool force_manifold = false,
FT border_angle = 45.)
: _separate_shells (separate_shells),
_force_manifold (force_manifold),
_border_angle (border_angle),
_shape (NULL),
_squared_radius (0.)
{
}
template <typename InputIterator, typename OutputIterator>
void operator() (InputIterator begin, InputIterator end, OutputIterator output)
{
clear_surface();
_shape = Shape_construction_3().construct (begin, end, _squared_radius);
// If shells are not separated and no manifold constraint is given,
// then the quick collection of facets can be applied
if (!_separate_shells && !_force_manifold)
{
collect_facets_quick ();
_shells.push_back (_surface.begin ());
}
else
{
// Init shell index
_index = 0;
// Collect all surface meshes from the alpha-shape in a fashion similar to ball-pivoting.
// Reset the facet handled markers.
for( All_cells_iterator cit = _shape->all_cells_begin(); cit != _shape->all_cells_end(); ++cit )
cit->info() = 0;
if (_force_manifold)
{
// If manifold surface is wanted, small volumes (bubbles) are first detected in
// order to know which layer to ignore when collecting facets
detect_bubbles();
}
collect_facets ();
if (_force_manifold)
{
// Even when taking into account facets, some nonmanifold features might remain
fix_nonmanifold_edges();
fix_nonmanifold_vertices();
}
}
for (Triple_iterator it = _surface.begin(); it != _surface.end(); ++ it)
{
Triple t = *it;
*(output ++) = {{ (*it)[0], (*it)[1], (*it)[2] }};
}
// std::copy (_surface.begin(), _surface.end(), output);
}
private:
void deinit_shape()
{
if (_shape != NULL)
{
delete _shape;
_shape = NULL;
}
}
void clear_surface()
{
_shells.clear();
_surface.clear();
_garbage.clear();
deinit_shape();
}
void collect_facets ()
{
// We check each of the facets: if it is not handled and either regular or singular,
// we start collecting the next surface from here.
for (Finite_facets_iterator fit = _shape->finite_facets_begin(); fit != _shape->finite_facets_end(); ++fit)
{
switch(_shape->classify (*fit))
{
case Shape::REGULAR:
// Build a surface from the outer cell.
if (_shape->classify(fit->first) == Shape::EXTERIOR)
collect_shell (*fit);
else
collect_shell (_shape->mirror_facet (*fit));
break;
case Shape::SINGULAR:
// Build a surface from both incident cells.
collect_shell (*fit);
collect_shell (_shape->mirror_facet (*fit));
break;
default:
break;
}
}
}
void collect_facets_quick ()
{
// Collect all facets from the alpha-shape in an unordered fashion.
for (Finite_facets_iterator fit = _shape->finite_facets_begin(); fit != _shape->finite_facets_end(); ++fit)
{
switch (_shape->classify(*fit))
{
case Shape::REGULAR:
// Collect the outer cell.
if (_shape->classify(fit->first) == Shape::EXTERIOR)
_surface.push_back (ordered_facet_indices (*fit));
else
_surface.push_back (ordered_facet_indices (_shape->mirror_facet(*fit)));
break;
case Shape::SINGULAR:
// Collect both incident cells.
_surface.push_back (ordered_facet_indices (*fit));
_surface.push_back (ordered_facet_indices (_shape->mirror_facet (*fit)));
break;
default:
break;
}
}
}
inline bool is_handled( Cell_handle c, unsigned int li ) const
{
switch( li ) {
case 0: return ( c->info()&1 ) != 0;
case 1: return ( c->info()&2 ) != 0;
case 2: return ( c->info()&4 ) != 0;
case 3: return ( c->info()&8 ) != 0;
}
return false;
}
inline bool is_handled( const Facet& f ) const { return is_handled( f.first, f.second ); }
inline void mark_handled( Cell_handle c, unsigned int li )
{
switch( li ) {
case 0: c->info() |= 1; return;
case 1: c->info() |= 2; return;
case 2: c->info() |= 4; return;
case 3: c->info() |= 8; return;
}
}
inline void mark_handled( Facet f ) { mark_handled( f.first, f.second ); }
inline void mark_opposite_handled( Facet f )
{
Classification_type cl = _shape->classify (f);
// If cell is singular, simply mark mirror facet as handled
if (cl == Shape::SINGULAR)
{
Facet mirror = _shape->mirror_facet (f);
mark_handled (mirror);
}
// If cell is regular, get corresponding bubble and mark
// facets of the other layer of the bubble as handled
else if (cl == Shape::REGULAR)
{
Facet fac = (_shape->classify (f.first) == Shape::EXTERIOR)
? f
: _shape->mirror_facet (f);
typename std::map<Facet, std::size_t>::iterator
search = _map_f2b.find (fac);
if (search == _map_f2b.end ())
return;
unsigned int layer = (_bubbles[search->second][0].find (fac) == _bubbles[search->second][0].end ())
? 0 : 1;
typename std::set<Facet>::iterator it = _bubbles[search->second][layer].begin ();
// If bubble has already been handled, no need to do it again
if (is_handled (*it))
return;
for (;it != _bubbles[search->second][layer].end (); ++ it)
{
_garbage.push_back (ordered_facet_indices (*it));
mark_handled (*it);
}
}
}
inline Triple ordered_facet_indices( const Facet& f ) const
{
if( (f.second&1) == 0 )
return make_array<unsigned int>( f.first->vertex( (f.second+2)&3 )->info(),
f.first->vertex( (f.second+1)&3 )->info(),
f.first->vertex( (f.second+3)&3 )->info() );
else
return make_array<unsigned int>( f.first->vertex( (f.second+1)&3 )->info(),
f.first->vertex( (f.second+2)&3 )->info(),
f.first->vertex( (f.second+3)&3 )->info() );
}
void collect_shell( const Facet& f)
{
collect_shell (f.first, f.second);
}
void collect_shell( Cell_handle c, unsigned int li)
{
// Collect one surface mesh from the alpha-shape in a fashion similar to ball-pivoting.
// Invariant: the facet is regular or singular.
// To stop stack overflows: use own stack.
std::stack<Facet> stack;
stack.push( Facet(c, li) );
Facet f;
Cell_handle n, p;
int ni, pi;
Vertex_handle a;
Classification_type cl;
bool processed = false;
while( !stack.empty() ) {
f = stack.top();
stack.pop();
// Check if the cell was already handled.
// Note that this is an extra check that in many cases is not necessary.
if( is_handled(f) )
continue;
// The facet is part of the surface.
CGAL_assertion( !_shape->is_infinite(f) );
mark_handled(f);
// Output the facet as a triple of indices.
_surface.push_back( ordered_facet_indices(f) );
if( !processed ) {
if (_separate_shells || _shells.size () == 0)
{
_shells.push_back( --_surface.end() );
_index ++;
}
processed = true;
}
// Save in which shell the facet is stored
_map_f2s[f] = _index-1;
// If the surface is required to be manifold,
// the opposite layer should be ignored
if (_force_manifold)
mark_opposite_handled (f);
// Pivot over each of the facet's edges and continue the surface at the next regular or singular facet.
for( int i = 0; i < 4; ++i ) {
// Skip the current facet.
if( i == f.second || is_handled(f.first, i) )
continue;
// Rotate around the edge (starting from the shared facet in the current cell) until a regular or singular facet is found.
n = f.first;
ni = i;
a = f.first->vertex( f.second );
cl = _shape->classify( Facet(n, ni) );
while( cl != Shape::REGULAR && cl != Shape::SINGULAR ) {
p = n;
n = n->neighbor(ni);
ni = n->index(a);
pi = n->index(p);
a = n->vertex(pi);
cl = _shape->classify( Facet(n, ni) );
}
// Continue the surface at the next regular or singular facet.
stack.push( Facet(n, ni) );
}
}
}
void detect_bubbles ()
{
std::set<Cell_handle> done;
unsigned int nb_facets_removed = 0;
unsigned int nb_skipped = 0;
for (Cell_iterator cit = _shape->cells_begin (); cit != _shape->cells_end (); ++ cit)
{
if (_shape->is_infinite (cit))
continue;
if (_shape->classify (cit) != Shape::INTERIOR)
continue;
if (done.find (cit) != done.end ())
continue;
std::set<VEdge> borders;
std::vector<Cell_handle> cells;
std::stack<Cell_handle> todo;
todo.push (cit);
// Get all cells of volume and all borders
while (!(todo.empty ()))
{
Cell_handle c = todo.top ();
todo.pop ();
if (!(done.insert (c).second))
continue;
cells.push_back (c);
for (unsigned int i = 0; i < 4; ++ i)
{
if (_shape->classify (c->neighbor (i)) == Shape::INTERIOR)
todo.push (c->neighbor (i));
else
{
// Test if edge is proper border
for (unsigned int j = 0; j < 3; ++ j)
{
unsigned int i0 = (i + j + 1)%4;
unsigned int i1 = (i + (j+1)%3 + 1)%4;
CGAL_assertion (i0 != i && i1 != i);
Edge edge (c, i0, i1);
if (_shape->classify (edge) != Shape::REGULAR)
continue;
VEdge vedge = (c->vertex (i0) < c->vertex (i1))
? std::make_pair (c->vertex (i0), c->vertex (i1))
: std::make_pair (c->vertex (i1), c->vertex (i0));
if (borders.find (vedge) != borders.end ())
continue;
Facet_circulator start = _shape->incident_facets (edge);
Facet_circulator circ = start;
unsigned int cnt = 0;
do
{
if (_shape->classify (*circ) == Shape::SINGULAR
|| _shape->classify (*circ) == Shape::REGULAR)
++ cnt;
++ circ;
}
while (circ != start);
// If edge is non-manifold, use as border
if (cnt > 2)
{
borders.insert (vedge);
continue;
}
// Else, if facets in cell are regular and angle is
// under _border_angle limit, use as border
Facet f0 (c, i);
Facet f1 (c, (i + (j+2)%3 + 1)%4);
if (_shape->classify (f0) != Shape::REGULAR
|| _shape->classify (f1) != Shape::REGULAR)
continue;
double angle = Geom_traits().compute_approximate_dihedral_angle_3_object()(vedge.first->point (),
vedge.second->point (),
c->vertex (i)->point (),
c->vertex ((i + (j+2)%3 + 1)%4)->point ());
if (-_border_angle < angle && angle < _border_angle)
{
borders.insert (vedge);
}
}
}
}
}
int layer = -1;
// Try to generate bubble from the volume found
_bubbles.push_back (Bubble());
std::set<Facet> done;
for (unsigned int c = 0; c < cells.size (); ++ c)
{
for (unsigned int ii = 0; ii < 4; ++ ii)
{
Facet start = _shape->mirror_facet (Facet (cells[c], ii));
if (_shape->classify (start) != Shape::REGULAR)
continue;
if (done.find (start) != done.end ())
continue;
++ layer;
std::stack<Facet> stack;
stack.push (start);
Facet f;
Cell_handle n, p;
int ni, pi;
Vertex_handle a;
Classification_type cl;
// A bubble is well formed is the border contains one loop that
// separates two layers.
// If the number of layers is different than 2, the volume is completely ignored.
while( !stack.empty() )
{
f = stack.top();
stack.pop();
if (!(done.insert (f).second))
continue;
if (_shape->classify (f.first) == Shape::EXTERIOR)
{
if (layer < 2)
{
_bubbles.back ()[layer].insert (f);
_map_f2b[f] = _bubbles.size () - 1;
}
else
{
nb_facets_removed ++;
mark_handled (f);
_garbage.push_back (ordered_facet_indices (f));
}
}
else
{
if (layer < 2)
{
_bubbles.back ()[layer].insert (_shape->mirror_facet (f));
_map_f2b[_shape->mirror_facet (f)] = _bubbles.size () - 1;
}
else
{
nb_facets_removed ++;
mark_handled (_shape->mirror_facet (f));
_garbage.push_back (ordered_facet_indices (_shape->mirror_facet (f)));
}
}
for( int i = 0; i < 4; ++i )
{
// Skip the current facet.
if( i == f.second)
continue;
n = f.first;
ni = i;
a = f.first->vertex( f.second );
cl = _shape->classify( Facet(n, ni) );
int n0 = -1, n1 = -1;
bool n0found = false;
for (int j = 0; j < 4; ++ j)
{
if (j != ni && j != f.second)
{
if (n0found)
{
n1 = j;
break;
}
else
{
n0 = j;
n0found = true;
}
}
}
VEdge vedge = (n->vertex (n0) < n->vertex (n1))
? std::make_pair (n->vertex (n0), n->vertex (n1))
: std::make_pair (n->vertex (n1), n->vertex (n0));
// If the edge is a border, propagation stops in this direction.
if (borders.find (vedge) != borders.end ())
continue;
while( cl != Shape::REGULAR && cl != Shape::SINGULAR ) {
p = n;
n = n->neighbor(ni);
ni = n->index(a);
pi = n->index(p);
a = n->vertex(pi);
cl = _shape->classify( Facet(n, ni) );
}
stack.push (Facet (n, ni));
}
}
}
}
// If number of layers is != 2, ignore volume and discard bubble
if (layer != 1)
{
nb_skipped ++;
for (unsigned int i = 0; i < 2; ++ i)
for (typename std::set<Facet>::iterator fit = _bubbles.back()[i].begin ();
fit != _bubbles.back()[i].end (); ++ fit)
{
mark_handled (*fit);
_map_f2b.erase (*fit);
_garbage.push_back (ordered_facet_indices (*fit));
nb_facets_removed ++;
}
_bubbles.pop_back ();
}
}
}
void fix_nonmanifold_edges()
{
typedef std::map<std::pair<VEdge, unsigned int>, std::set<Triple> > Edge_shell_map_triples;
typedef typename Edge_shell_map_triples::iterator Edge_shell_map_triples_iterator;
unsigned int nb_facets_removed = 0;
unsigned int nb_nm_edges = 0;
// Store for each pair edge/shell the incident facets
Edge_shell_map_triples eshell_triples;
std::map<Triple, Facet> map_t2f;
for (typename Map_facet_to_shell::iterator fit = _map_f2s.begin ();
fit != _map_f2s.end (); ++ fit)
{
Facet f = fit->first;
Triple t = ordered_facet_indices (f);
map_t2f[t] = f;
for (unsigned int k = 0; k < 3; ++ k)
{
Vertex_handle v0 = f.first->vertex ((f.second + k + 1)%4);
Vertex_handle v1 = f.first->vertex ((f.second + (k+1)%3 + 1)%4);
VEdge vedge = (v0 < v1) ? std::make_pair (v0, v1) : std::make_pair (v1, v0);
std::pair<Edge_shell_map_triples_iterator, bool>
search = eshell_triples.insert (std::make_pair (std::make_pair (vedge, fit->second),
std::set<Triple>()));
search.first->second.insert (t);
}
}
for (Edge_shell_map_triples_iterator eit = eshell_triples.begin ();
eit != eshell_triples.end (); ++ eit)
{
// If an edge has more than 2 incident facets for one shell, it is non-manifold
if (eit->second.size () < 3)
continue;
++ nb_nm_edges;
Triple_iterator tit = _shells[eit->first.second];
Triple_iterator end = (eit->first.second == _shells.size () - 1)
? _surface.end () : _shells[eit->first.second + 1];
// Remove facets until the edge is manifold in this shell
while (tit != end && eit->second.size () > 2)
{
Triple_iterator current = tit ++;
typename std::set<Triple>::iterator search = eit->second.find (*current);
if (search != eit->second.end ())
{
if (current == _shells[eit->first.second])
_shells[eit->first.second] = tit;
_garbage.push_back (*current);
_map_f2s.erase (map_t2f[*current]);
_surface.erase (current);
++ nb_facets_removed;
eit->second.erase (search);
}
}
}
}
template <typename T>
struct operator_less
{
bool operator() (const T& a, const T& b) const
{
return &*a < &*b;
}
};
void find_two_other_vertices(const Facet& f, Vertex_handle v,
Vertex_handle& v1, Vertex_handle& v2)
{
Vertex_handle vother = f.first->vertex (f.second);
bool v1found = false;
for (unsigned int i = 0; i < 4; ++ i)
{
Vertex_handle vi = f.first->vertex (i);
if (vi != v && vi != vother)
{
if (v1found)
{
v2 = vi;
return;
}
else
{
v1 = vi;
v1found = true;
}
}
}
}
void fix_nonmanifold_vertices()
{
typedef ::CGAL::Union_find<Facet> UF;
typedef typename UF::handle UF_handle;
typedef std::map<std::pair<Vertex_handle, unsigned int>, std::vector<Facet> > Vertex_shell_map_facets;
typedef typename Vertex_shell_map_facets::iterator Vertex_shell_map_facet_iterator;
// For faster facet removal, we sort the triples of each shell as a preprocessing
for (unsigned int i = 0; i < _shells.size (); ++ i)
{
Triple_iterator begin = _shells[i];
Triple_iterator end = (i+1 == _shells.size ()) ? _surface.end () : _shells[i+1];
Tripleset tmp;
tmp.splice (tmp.end(), _surface, begin, end);
tmp.sort();
_shells[i] = tmp.begin ();
_surface.splice(end, tmp, tmp.begin(), tmp.end());
}
unsigned int nb_facets_removed = 0;
unsigned int nb_nm_vertices = 0;
// Removing facets to fix non-manifold vertices might make some other vertices
// become non-manifold, therefore we iterate until no facet needs to be removed.
do
{
nb_nm_vertices = 0;
nb_facets_removed = 0;
// Store for each pair vertex/shell the incident facets
Vertex_shell_map_facets vshell_facets;
for (typename Map_facet_to_shell::iterator fit = _map_f2s.begin ();
fit != _map_f2s.end (); ++ fit)
{
Facet f = fit->first;
for (unsigned int k = 0; k < 3; ++ k)
{
Vertex_handle v = f.first->vertex ((f.second+k+1)%4);
std::pair<Vertex_shell_map_facet_iterator, bool>
search = vshell_facets.insert (std::make_pair (std::make_pair (v, fit->second),
std::vector<Facet>()));
search.first->second.push_back (f);
}
}
for (Vertex_shell_map_facet_iterator fit = vshell_facets.begin ();
fit != vshell_facets.end (); ++ fit)
{
if (fit->second.size () < 2)
continue;
Vertex_handle vit = fit->first.first;
unsigned int shell = fit->first.second;
UF uf;
std::map<Facet, UF_handle> map_f2h;
for (unsigned int i = 0; i < fit->second.size (); ++ i)
map_f2h.insert (std::make_pair (fit->second[i], uf.make_set (fit->second[i])));
std::map<Vertex_handle, Facet> map_v2f;
for (unsigned int i = 0; i < fit->second.size (); ++ i)
{
Vertex_handle v1, v2;
find_two_other_vertices (fit->second[i], vit, v1, v2);
std::pair<typename std::map<Vertex_handle, Facet>::iterator, bool>
insertion1 = map_v2f.insert (std::make_pair (v1, fit->second[i]));
if (!(insertion1.second))
uf.unify_sets (map_f2h[fit->second[i]], map_f2h[insertion1.first->second]);
std::pair<typename std::map<Vertex_handle, Facet>::iterator, bool>
insertion2 = map_v2f.insert (std::make_pair (v2, fit->second[i]));
if (!(insertion2.second))
uf.unify_sets (map_f2h[fit->second[i]], map_f2h[insertion2.first->second]);
}
if (uf.number_of_sets () > 1)
{
++ nb_nm_vertices;
typedef std::map<UF_handle, std::vector<Facet>, operator_less<UF_handle> > Map_uf_sets;
Map_uf_sets map_h2f;
for (unsigned int i = 0; i < fit->second.size (); ++ i)
{
UF_handle handle = uf.find (map_f2h[fit->second[i]]);
std::pair<typename Map_uf_sets::iterator, bool>
insertion = map_h2f.insert (std::make_pair (handle, std::vector<Facet>()));
insertion.first->second.push_back (fit->second[i]);
}
typename Map_uf_sets::iterator largest = map_h2f.end ();
std::size_t nb_largest = 0;
for (typename Map_uf_sets::iterator ufit = map_h2f.begin (); ufit != map_h2f.end (); ++ ufit)
{
std::size_t size = ufit->second.size ();
if (size > nb_largest)
{
nb_largest = size;
largest = ufit;
}
}
std::vector<Triple> triples;
for (typename Map_uf_sets::iterator ufit = map_h2f.begin (); ufit != map_h2f.end (); ++ ufit)
{
if (ufit == largest)
continue;
for (unsigned int i = 0; i < ufit->second.size (); ++ i)
{
_map_f2s.erase (ufit->second[i]);
triples.push_back (ordered_facet_indices (ufit->second[i]));
}
}
std::sort (triples.begin (), triples.end ());
Triple_iterator tit = _shells[shell];
Triple_iterator end = (shell == _shells.size () - 1)
? _surface.end () : _shells[shell + 1];
unsigned int tindex = 0;
while (tit != end && tindex < triples.size ())
{
Triple_iterator current = tit ++;
if (*current == triples[tindex])
{
if (current == _shells[shell])
_shells[shell] = tit;
_garbage.push_back (*current);
_surface.erase (current);
++ nb_facets_removed;
++ tindex;
}
}
}
}
}
while (nb_nm_vertices != 0);
}
};
} // namespace Scale_space_reconstruction_3
} // namespace CGAL
#endif // CGAL_SCALE_SPACE_RECONSTRUCTION_3_ALPHA_SHAPE_MESHER_H

View File

@ -0,0 +1,280 @@
#ifndef CGAL_SCALE_SPACE_RECONSTRUCTION_3_WEIGHTED_PCA_SMOOTHER_H
#define CGAL_SCALE_SPACE_RECONSTRUCTION_3_WEIGHTED_PCA_SMOOTHER_H
#include <CGAL/Search_traits_3.h>
#include <CGAL/Search_traits_adapter.h>
#include <CGAL/Orthogonal_incremental_neighbor_search.h>
#include <CGAL/Orthogonal_k_neighbor_search.h>
#include <CGAL/Fuzzy_sphere.h>
#include <CGAL/Random.h>
#include <CGAL/Default_diagonalize_traits.h>
#include <boost/function_output_iterator.hpp>
#ifdef CGAL_LINKED_WITH_TBB
#include "tbb/blocked_range.h"
#include "tbb/parallel_for.h"
#endif // CGAL_LINKED_WITH_TBB
namespace CGAL
{
namespace Scale_space_reconstruction_3
{
template <typename Geom_traits,
typename DiagonalizeTraits = CGAL::Default_diagonalize_traits<typename Geom_traits::FT, 3>,
#ifdef CGAL_LINKED_WITH_TBB
typename ConcurrencyTag = CGAL::Parallel_tag>
#else
typename ConcurrencyTag = CGAL::Sequential_tag>
#endif
class Weighted_PCA_smoother
{
public:
typedef typename Geom_traits::FT FT; ///< defines the point type.
typedef typename Geom_traits::Point_3 Point; ///< defines the point typ.e
typedef typename Geom_traits::Vector_3 Vector; ///< defines the vector type.
private:
typedef boost::tuple<Point, std::size_t> Point_and_size_t;
typedef std::vector<unsigned int> CountVec;
typedef std::vector<Point> Pointset;
typedef Search_traits_3<Geom_traits> Traits_base;
typedef CGAL::Search_traits_adapter<Point_and_size_t,
CGAL::Nth_of_tuple_property_map<0, Point_and_size_t>,
Traits_base> Search_traits;
typedef Orthogonal_k_neighbor_search<Search_traits> Static_search;
typedef Orthogonal_incremental_neighbor_search<Search_traits> Dynamic_search;
typedef typename Dynamic_search::Tree Search_tree;
typedef Fuzzy_sphere< Search_traits > Sphere;
Random _generator;
unsigned int _mean_neighbors;
unsigned int _samples;
FT _squared_radius;
Search_tree _tree; // To quickly search for nearest neighbors.
Pointset _points;
public:
Weighted_PCA_smoother (unsigned int neighbors = 12, unsigned int samples = 300)
: _generator(0), _mean_neighbors (neighbors), _samples (samples),_squared_radius (-1.) { }
Weighted_PCA_smoother (FT squared_radius)
: _generator(0), _mean_neighbors (0), _samples (0),_squared_radius (squared_radius) { }
template <typename InputIterator>
void operator() (InputIterator begin, InputIterator end)
{
_tree.clear();
_points.clear();
std::size_t i = 0;
std::size_t size = std::size_t(end - begin);
_tree.reserve(size);
_points.reserve(size);
while(begin!=end)
{
_points.push_back (*begin);
_tree.insert( boost::make_tuple(*(begin ++),i ++) );
}
_tree.build();
if (_squared_radius == -1)
estimate_neighborhood_squared_radius();
// Collect the number of neighbors of each point.
// This can be done concurrently.
CountVec neighbors (_tree.size(), 0);
try_parallel (ComputeNN (_points, _tree, _squared_radius, neighbors), 0, _tree.size());
// Compute the transformed point locations.
// This can be done concurrently.
try_parallel (AdvanceSS (_tree, neighbors, _points), 0, _tree.size());
}
private:
void estimate_neighborhood_squared_radius ()
{
typename Geom_traits::Compute_squared_distance_3 squared_distance
= Geom_traits().compute_squared_distance_3_object();
unsigned int handled = 0;
unsigned int checked = 0;
FT radius = 0.;
for (typename Search_tree::const_iterator it = _tree.begin(); it != _tree.end(); ++it )
{
unsigned int left = (unsigned int)(_tree.size() - handled);
if (_samples >= left || _generator.get_double() < double(_samples - checked) / left)
{
// The neighborhood should contain the point itself as well.
Static_search search (_tree, boost::get<0>(*it), _mean_neighbors + 1);
radius += std::sqrt (to_double (squared_distance( boost::get<0>(*it), boost::get<0>((search.end()-1)->first))));
++checked;
}
++handled;
}
radius /= double(checked);
_squared_radius = radius * radius;
}
template <typename F>
void try_parallel (const F& func, std::size_t begin, std::size_t end)
{
#ifndef CGAL_LINKED_WITH_TBB
CGAL_static_assertion_msg (!(boost::is_convertible<ConcurrencyTag, Parallel_tag>::value),
"Parallel_tag is enabled but TBB is unavailable.");
#else
if (boost::is_convertible<ConcurrencyTag,Parallel_tag>::value)
tbb::parallel_for(tbb::blocked_range<std::size_t>(begin, end), func);
else
#endif
for (std::size_t i = begin; i < end; ++i)
func(i);
}
struct Inc
{
unsigned int * i;
Inc(unsigned int& i)
: i(&i)
{}
template <typename T>
void operator()(const T&) const
{
++(*i);
}
};
// Compute the number of neighbors of a point that lie within a fixed radius.
class ComputeNN
{
private:
typename Geom_traits::Compare_squared_distance_3 compare;
const Pointset& _pts;
const Search_tree& _tree;
const FT _sq_rd;
CountVec& _nn;
public:
ComputeNN(const Pointset& points, const Search_tree& tree,
const FT& sq_radius, CountVec& nn)
: _pts(points), _tree(tree), _sq_rd(sqrt(sq_radius)), _nn(nn) {}
#ifdef CGAL_LINKED_WITH_TBB
void operator()( const tbb::blocked_range< std::size_t >& range ) const {
for( std::size_t i = range.begin(); i != range.end(); ++i )
(*this)( i );
}
#endif // CGAL_LINKED_WITH_TBB
void operator()( const std::size_t& i ) const {
Sphere sp(_pts[i], _sq_rd);
Inc inc(_nn[i]);
_tree.search(boost::make_function_output_iterator(inc),sp);
}
}; // class ComputeNN
// Advance a point to a coarser scale.
class AdvanceSS
{
private:
const Search_tree& _tree;
const CountVec& _nn;
Pointset& _pts;
public:
AdvanceSS(const Search_tree& tree, const CountVec& nn, Pointset& points)
: _tree(tree), _nn(nn),_pts(points) {}
#ifdef CGAL_LINKED_WITH_TBB
void operator()( const tbb::blocked_range< std::size_t >& range ) const {
for( std::size_t i = range.begin(); i != range.end(); ++i )
(*this)( i );
}
#endif // CGAL_LINKED_WITH_TBB
void operator()( const std::size_t& i ) const {
// If the neighborhood is too small, the vertex is not moved.
if( _nn[i] < 4 )
return;
Static_search search(_tree, _pts[i], _nn[i]);
Point barycenter (0., 0., 0.);
FT weight_sum = 0.;
unsigned int column = 0;
// Compute total weight
for( typename Static_search::iterator nit = search.begin();
nit != search.end() && column < _nn[i];
++nit, ++column )
weight_sum += (1.0 / _nn[boost::get<1>(nit->first)]);
column = 0;
// Compute barycenter
for( typename Static_search::iterator nit = search.begin();
nit != search.end() && column < _nn[i];
++nit, ++column )
{
Vector v (CGAL::ORIGIN, boost::get<0>(nit->first));
barycenter = barycenter + ((1.0 / _nn[boost::get<1>(nit->first)]) / weight_sum) * v;
}
CGAL::cpp11::array<FT, 6> covariance = {{ 0., 0., 0., 0., 0., 0. }};
column = 0;
// Compute covariance matrix of Weighted PCA
for( typename Static_search::iterator nit = search.begin();
nit != search.end() && column < _nn[i];
++nit, ++column )
{
Vector v (barycenter, boost::get<0>(nit->first));
FT w = (1.0 / _nn[boost::get<1>(nit->first)]);
v = w*v;
covariance[0] += w * v.x () * v.x ();
covariance[1] += w * v.x () * v.y ();
covariance[2] += w * v.x () * v.z ();
covariance[3] += w * v.y () * v.y ();
covariance[4] += w * v.y () * v.z ();
covariance[5] += w * v.z () * v.z ();
}
// Compute the weighted least-squares planar approximation of the point set.
CGAL::cpp11::array<FT, 9> eigenvectors = {{ 0., 0., 0.,
0., 0., 0.,
0., 0., 0. }};
CGAL::cpp11::array<FT, 3> eigenvalues = {{ 0., 0., 0. }};
DiagonalizeTraits::diagonalize_selfadjoint_covariance_matrix
(covariance, eigenvalues, eigenvectors);
// The vertex is moved by projecting it onto the plane
// through the barycenter and orthogonal to the Eigen vector with smallest Eigen value.
Vector norm (eigenvectors[0], eigenvectors[1], eigenvectors[2]);
Vector b2p (barycenter, _pts[i]);
_pts[i] = barycenter + b2p - ((norm * b2p) * norm);
}
}; // class AdvanceSS
};
} // namespace Scale_space_reconstruction_3
} // namespace CGAL
#endif // CGAL_SCALE_SPACE_RECONSTRUCTION_3_WEIGHTED_PCA_SMOOTHER_H