dust3d/thirdparty/cgal/CGAL-4.13/include/CGAL/Mesh_3/Sliver_perturber.h

1651 lines
45 KiB
C++
Executable File

// Copyright (c) 2009 INRIA Sophia-Antipolis (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$
// SPDX-License-Identifier: GPL-3.0+
//
//
// Author(s) : Stephane Tayeb
//
//******************************************************************************
// File Description :
//******************************************************************************
#ifndef CGAL_MESH_3_SLIVER_PERTURBER_H
#define CGAL_MESH_3_SLIVER_PERTURBER_H
#include <CGAL/license/Mesh_3.h>
#include <CGAL/disable_warnings.h>
#include <CGAL/Mesh_3/config.h>
#ifdef CGAL_MESH_3_VERBOSE
#define CGAL_MESH_3_PERTURBER_VERBOSE
#endif
#ifdef CGAL_MESH_3_PERTURBER_VERBOSE
#ifndef CGAL_MESH_3_PERTURBER_HIGH_VERBOSITY
#define CGAL_MESH_3_PERTURBER_LOW_VERBOSITY
#else
#undef CGAL_MESH_3_PERTURBER_LOW_VERBOSITY
#endif
#endif
#include <CGAL/Mesh_3/vertex_perturbation.h>
#include <CGAL/Mesh_3/C3T3_helpers.h>
#include <CGAL/Mesh_optimization_return_code.h>
#include <CGAL/Real_timer.h>
#include <CGAL/Mesh_3/Null_perturber_visitor.h>
#include <CGAL/Mesh_3/sliver_criteria.h>
#include <CGAL/Has_timestamp.h>
#include <CGAL/Hash_handles_with_or_without_timestamps.h>
#include <CGAL/Mesh_3/Concurrent_mesher_config.h>
#include <CGAL/Mesh_3/Worksharing_data_structures.h>
#ifdef CGAL_CONCURRENT_MESH_3_PROFILING
# define CGAL_PROFILE
# include <CGAL/Profile_counter.h>
#endif
#ifdef CGAL_LINKED_WITH_TBB
# include <tbb/task.h>
#endif
#include <boost/format.hpp>
#ifdef CGAL_MESH_3_USE_RELAXED_HEAP
# error This option CGAL_MESH_3_USE_RELAXED_HEAP is no longer supported
// The reason is that the Boost relaxed heap does not ensure a strict order
// of the priority queue.
#include <boost/pending/relaxed_heap.hpp>
#else
#include <CGAL/Modifiable_priority_queue.h>
#endif //CGAL_MESH_3_USE_RELAXED_HEAP
#include <boost/ptr_container/ptr_vector.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <boost/unordered_map.hpp>
#include <memory>
namespace CGAL {
namespace Mesh_3 {
/**
* @class PVertex
* Vertex with associated perturbation datas
*/
// Sequential
template< typename FT
, typename Vertex_handle
, typename Point_3
, typename SliverCriterion
, typename Perturbation
, typename Concurrency_tag>
class PVertex_
{
public:
typedef PVertex_<FT,
Vertex_handle,
Point_3,
SliverCriterion,
Perturbation,
Concurrency_tag> Self;
typedef std::size_t id_type;
/// Constructor
PVertex_()
: vertex_handle_()
, incident_sliver_nb_(0)
, min_value_((std::numeric_limits<double>::max)())
, try_nb_(0)
, p_perturbation_(NULL)
, id_()
{ }
PVertex_(const Vertex_handle& vh, id_type id)
: vertex_handle_(vh)
, incident_sliver_nb_(0)
, min_value_((std::numeric_limits<double>::max)())
, try_nb_(0)
, p_perturbation_(NULL)
, id_(id)
{ }
/// Associated vertex
const Vertex_handle& vertex() const { return vertex_handle_; }
void set_vertex(const Vertex_handle& vh) { vertex_handle_ = vh; }
/// Incident slivers number
unsigned int sliver_nb() const { return incident_sliver_nb_; }
void set_sliver_nb(const unsigned int n) { incident_sliver_nb_ = n; }
/// Current perturbation
const Perturbation* perturbation() const { return p_perturbation_; }
void set_perturbation(const Perturbation* p) { p_perturbation_ = p; }
/// Is perturbable
bool is_perturbable() const
{
return ( (vertex_handle_->in_dimension() > 1)
&& (NULL != perturbation())
&& (sliver_nb() != 0) );
}
/// Min sliver value
const FT& min_value() const { return min_value_; }
void set_min_value(const FT& min_value){ min_value_ = min_value; }
/// Try nb
const unsigned int& try_nb() const { return try_nb_; }
void set_try_nb(const unsigned int& try_nb) { try_nb_ = try_nb; }
void increment_try_nb() { ++try_nb_; }
/// Id
void set_id(const id_type& id) { id_ = id; }
id_type id() const { return id_; }
/// Operators
bool operator==(const Self& pv) const { return ( id() == pv.id() ); }
bool operator<(const Self& pv) const
{
// vertex type (smallest-interior first)
if ( vertex()->in_dimension() != pv.vertex()->in_dimension() )
return vertex()->in_dimension() > pv.vertex()->in_dimension();
// nb incident slivers (smallest first)
else if ( sliver_nb() != pv.sliver_nb() )
return sliver_nb() < pv.sliver_nb();
// min angle (smallest first)
else if ( min_value() != pv.min_value() )
return min_value() < pv.min_value();
// try nb (smallest first)
else if ( try_nb() != pv.try_nb() )
return try_nb() < pv.try_nb();
// perturbation type (smallest first)
else if ( perturbation() != pv.perturbation() )
return *perturbation() < *pv.perturbation();
return ( id() < pv.id() ); // all characteristics are the same!
}
/// Dummy functions
void update_saved_erase_counter() {}
bool is_zombie() { return false; }
private:
/// Private datas
Vertex_handle vertex_handle_;
unsigned int incident_sliver_nb_;
FT min_value_;
unsigned int try_nb_;
const Perturbation* p_perturbation_;
id_type id_;
};
#ifdef CGAL_LINKED_WITH_TBB
// Parallel
template< typename FT
, typename Vertex_handle
, typename Point_3
, typename SliverCriterion
, typename Perturbation>
class PVertex_<FT, Vertex_handle, Point_3,
SliverCriterion, Perturbation, Parallel_tag>
{
public:
typedef PVertex_<FT,
Vertex_handle,
Point_3,
SliverCriterion,
Perturbation,
Parallel_tag> Self;
typedef std::size_t id_type;
/// Constructor
PVertex_()
: vertex_handle_()
, in_dimension_(-1)
, incident_sliver_nb_(0)
, min_value_((std::numeric_limits<double>::max)())
, try_nb_(0)
, p_perturbation_(NULL)
, id_()
{ }
PVertex_(const Vertex_handle& vh, id_type id)
: vertex_handle_(vh)
, vh_erase_counter_when_added_(vh->erase_counter())
, in_dimension_(vh->in_dimension())
, incident_sliver_nb_(0)
, min_value_((std::numeric_limits<double>::max)())
, try_nb_(0)
, p_perturbation_(NULL)
, id_(id)
{ }
/// Associated vertex
const Vertex_handle& vertex() const { return vertex_handle_; }
void set_vertex(const Vertex_handle& vh)
{
vertex_handle_ = vh;
update_saved_erase_counter();
}
void update_saved_erase_counter()
{
vh_erase_counter_when_added_ = vertex_handle_->erase_counter();
}
int in_dimension() const { return in_dimension_; }
/// Incident slivers number
unsigned int sliver_nb() const { return incident_sliver_nb_; }
void set_sliver_nb(const unsigned int n) { incident_sliver_nb_ = n; }
/// Current perturbation
const Perturbation* perturbation() const { return p_perturbation_; }
void set_perturbation(const Perturbation* p) { p_perturbation_ = p; }
/// Is perturbable
bool is_perturbable() const
{
return ( (vertex_handle_->in_dimension() > 1)
&& (NULL != perturbation())
&& (sliver_nb() != 0) );
}
/// Min sliver value
const FT& min_value() const { return min_value_; }
void set_min_value(const FT& min_value){ min_value_ = min_value; }
/// Try nb
const unsigned int& try_nb() const { return try_nb_; }
void set_try_nb(const unsigned int& try_nb) { try_nb_ = try_nb; }
void increment_try_nb() { ++try_nb_; }
/// Id
void set_id(const id_type& id) { id_ = id; }
id_type id() const { return id_; }
/// Zombie
bool is_zombie() const
{
return vertex_handle_->erase_counter() != vh_erase_counter_when_added_;
}
/// Operators
bool operator==(const Self& pv) const { return ( id() == pv.id() ); }
bool operator<(const Self& pv) const
{
// vertex type (smallest-interior first)
if ( in_dimension() != pv.in_dimension() )
return in_dimension() > pv.in_dimension();
// nb incident slivers (smallest first)
else if ( sliver_nb() != pv.sliver_nb() )
return sliver_nb() < pv.sliver_nb();
// min angle (smallest first)
else if ( min_value() != pv.min_value() )
return min_value() < pv.min_value();
// try nb (smallest first)
else if ( try_nb() != pv.try_nb() )
return try_nb() < pv.try_nb();
// perturbation type (smallest first)
else if ( perturbation() != pv.perturbation() )
return *perturbation() < *pv.perturbation();
return ( id() < pv.id() ); // all characteristics are the same!
}
private:
/// Private datas
Vertex_handle vertex_handle_;
unsigned int vh_erase_counter_when_added_;
int in_dimension_;
unsigned int incident_sliver_nb_;
FT min_value_;
unsigned int try_nb_;
const Perturbation* p_perturbation_;
id_type id_;
};
#endif
/************************************************
// Class Sliver_perturber_base
// Two versions: sequential / parallel
************************************************/
// Sequential
template <typename Tr, typename Concurrency_tag>
class Sliver_perturber_base
{
protected:
typedef typename Tr::Vertex_handle Vertex_handle;
typedef typename Tr::Geom_traits Gt;
typedef typename Gt::FT FT;
typedef typename std::vector<Vertex_handle> Bad_vertices_vector;
typedef typename Tr::Lock_data_structure Lock_data_structure;
Sliver_perturber_base(const Bbox_3 &, int) {}
Lock_data_structure *
get_lock_data_structure() const { return 0; }
void unlock_all_elements() const {}
void create_root_task() const {}
bool flush_work_buffers() const { return true; }
void wait_for_all() const {}
void destroy_root_task() const {}
template <typename Func, typename PVertex>
void enqueue_work(Func, const PVertex &) const {}
void increment_erase_counter(const Vertex_handle &) const {}
};
#ifdef CGAL_LINKED_WITH_TBB
// Parallel
template <typename Tr>
class Sliver_perturber_base<Tr, Parallel_tag>
{
protected:
typedef typename Tr::Vertex_handle Vertex_handle;
typedef typename Tr::Geom_traits Gt;
typedef typename Gt::FT FT;
typedef typename tbb::concurrent_vector<Vertex_handle> Bad_vertices_vector;
typedef typename Tr::Lock_data_structure Lock_data_structure;
Sliver_perturber_base(const Bbox_3 &bbox, int num_grid_cells_per_axis)
: m_lock_ds(bbox, num_grid_cells_per_axis)
, m_worksharing_ds(bbox)
{
}
Lock_data_structure *get_lock_data_structure() const
{
return &m_lock_ds;
}
void unlock_all_elements() const
{
m_lock_ds.unlock_all_points_locked_by_this_thread();
}
void create_root_task() const
{
m_empty_root_task = new( tbb::task::allocate_root() ) tbb::empty_task;
m_empty_root_task->set_ref_count(1);
}
bool flush_work_buffers() const
{
m_empty_root_task->set_ref_count(1);
bool keep_flushing = m_worksharing_ds.flush_work_buffers(*m_empty_root_task);
wait_for_all();
return keep_flushing;
}
void wait_for_all() const
{
m_empty_root_task->wait_for_all();
}
void destroy_root_task() const
{
tbb::task::destroy(*m_empty_root_task);
m_empty_root_task = 0;
}
template <typename Func, typename PVertex>
void enqueue_work(Func f, const PVertex &pv) const
{
CGAL_assertion(m_empty_root_task != 0);
m_worksharing_ds.enqueue_work(f, pv, *m_empty_root_task);
}
void increment_erase_counter(const Vertex_handle &vh) const
{
vh->increment_erase_counter();
}
public:
protected:
mutable Lock_data_structure m_lock_ds;
mutable Mesh_3::Auto_worksharing_ds m_worksharing_ds;
mutable tbb::task *m_empty_root_task;
};
#endif // CGAL_LINKED_WITH_TBB
/************************************************
// Class Sliver_perturber
************************************************/
template < typename C3T3,
typename MeshDomain,
typename SliverCriterion = Mesh_3::Min_dihedral_angle_criterion
<typename C3T3::Triangulation::Geom_traits>,
typename Visitor_ = Null_perturber_visitor<C3T3> >
class Sliver_perturber
: public Sliver_perturber_base<typename C3T3::Triangulation,
typename C3T3::Concurrency_tag>
{
// Types
typedef typename C3T3::Concurrency_tag Concurrency_tag;
typedef Sliver_perturber<C3T3, MeshDomain, SliverCriterion, Visitor_> Self;
typedef Sliver_perturber_base<
typename C3T3::Triangulation, Concurrency_tag> Base;
typedef typename C3T3::Triangulation Tr;
typedef typename Tr::Geom_traits Gt;
typedef typename Tr::Cell_handle Cell_handle;
typedef typename Base::Vertex_handle Vertex_handle;
typedef typename Tr::Vertex Vertex;
typedef typename Tr::Bare_point Bare_point;
typedef typename Tr::Weighted_point Weighted_point;
typedef typename std::vector<Cell_handle> Cell_vector;
typedef typename std::vector<Vertex_handle> Vertex_vector;
typedef typename Base::Bad_vertices_vector Bad_vertices_vector;
typedef typename Gt::FT FT;
// Helper
typedef class C3T3_helpers<C3T3,MeshDomain> C3T3_helpers;
using Base::get_lock_data_structure;
// Visitor
// Should define
// - bound_reached(FT bound)
// - end_of_perturbation_iteration(std::size_t vertices_left)
typedef Visitor_ Visitor;
// perturbations
public:
typedef Abstract_perturbation<C3T3,MeshDomain,SliverCriterion> Perturbation;
typedef boost::ptr_vector<Perturbation> Perturbation_vector;
private:
// Relaxed heap
typedef PVertex_<FT,
Vertex_handle,
Bare_point,
SliverCriterion,
Perturbation,
Concurrency_tag> PVertex;
/**
* @class PVertex_id
* relaxed heap
*/
class PVertex_id :
public boost::put_get_helper<typename PVertex::id_type, PVertex_id>
{
public:
typedef boost::readable_property_map_tag category;
typedef typename PVertex::id_type value_type;
typedef typename PVertex::id_type reference;
typedef PVertex key_type;
value_type operator[] (const key_type& pv) const { return pv.id(); }
};
typedef std::less<PVertex> less_PVertex;
#ifdef CGAL_MESH_3_USE_RELAXED_HEAP
typedef boost::relaxed_heap<PVertex, less_PVertex, PVertex_id> PQueue;
#else
typedef ::CGAL::internal::mutable_queue_with_remove<PVertex,std::vector<PVertex>, less_PVertex, PVertex_id> PQueue;
#endif //CGAL_MESH_3_USE_RELAXED_HEAP
public:
/**
* Constructor
*/
Sliver_perturber(C3T3& c3t3,
const MeshDomain& domain,
const SliverCriterion& criterion);
/**
* @brief Launch perturbation
* @param sliver_bound the bound the perturber will try to achieve
* @param delta the size of the step used by the perturber
*
* Runs explicit perturbation. The goal is that for each tet of the mesh,
* SliverCriterion(tet) > sliver_bound.
* The perturber runs step by step, using delta as step size.
*/
Mesh_optimization_return_code
operator()(Visitor visitor = Visitor());
/**
* Adds a perturbation at the end of the perturbation queue
*/
void add_perturbation(Perturbation* perturbation);
/// Time accessors
void set_time_limit(double time) { time_limit_ = time; }
double time_limit() const { return time_limit_; }
private:
// -----------------------------------
// Private methods
// -----------------------------------
/**
* One step perturbation: tries to achieve sliver_bound quality in the mesh
*/
bool perturb(const FT& sliver_bound, PQueue& pqueue, Visitor& v) const;
/**
* Builds priority queue. It will contain all vertices that have quality below
* sliver_bound.
* Returns priority queue size.
*
* precondition: pqueue.empty()
*/
int build_priority_queue(const FT& sliver_bound, PQueue& pqueue) const;
/**
* Updates priority queue for all vertices of \c vertices
*/
// Sequential
int update_priority_queue(const Vertex_vector& vertices,
const FT& sliver_bound,
PQueue& pqueue) const;
#ifdef CGAL_LINKED_WITH_TBB
// Parallel
int update_priority_queue( const Vertex_vector& vertices
, const FT& sliver_bound
, Visitor& visitor
, Bad_vertices_vector &bad_vertices) const;
#endif
/**
* Updates \c pv in priority queue
*/
int update_priority_queue(const PVertex& pv, PQueue& pqueue) const;
// For parallel version
void
perturb_vertex( PVertex pv
, const FT& sliver_bound
, Visitor& visitor
, Bad_vertices_vector &bad_vertices
, bool *could_lock_zone
) const;
/**
* Returns a pvertex from a vertex handle \c vh, using id \c pv_id
*/
PVertex
make_pvertex(const Vertex_handle& vh,
const FT& sliver_bound,
const typename PVertex::id_type& pv_id) const;
PVertex
make_pvertex__concurrent(
const Vertex_handle& vh,
const FT& sliver_bound,
const typename PVertex::id_type& pv_id) const;
/**
* Updates a pvertex \c pv
*/
void update_pvertex(PVertex& pv, const FT& sliver_bound) const;
void update_pvertex__concurrent(PVertex& pv, const FT& sliver_bound) const;
/**
* Returns \c vh pvertex id
*/
typename PVertex::id_type get_pvertex_id(const Vertex_handle& vh) const
{
return static_cast<typename PVertex::id_type>(vh->meshing_info());
}
/**
* Update bad vertices vector, wrt \c sliver_bound
*/
// Sequential
void update_bad_vertices(std::vector<Vertex_handle> &bad_vertices,
const FT& sliver_bound) const;
#ifdef CGAL_LINKED_WITH_TBB
// Parallel
void update_bad_vertices(tbb::concurrent_vector<Vertex_handle> &bad_vertices,
const FT& sliver_bound) const;
#endif
/**
* Initializes vertices ids
*/
void initialize_vertices_id() const;
/**
* Returns true if time_limit is reached
*/
bool is_time_limit_reached() const
{
return ( (time_limit() > 0) && (running_time_.time() > time_limit()) );
}
#ifdef CGAL_MESH_3_PERTURBER_VERBOSE
/// Verbose mode methods
void print_perturbations_statistics() const;
void print_final_perturbations_statistics() const;
void reset_perturbation_counters();
#endif
#ifdef CGAL_LINKED_WITH_TBB
// For parallel version
void
enqueue_task(const PVertex &pv,
const FT& sliver_bound,
Visitor& visitor,
Bad_vertices_vector &bad_vertices
) const;
#endif
private:
#ifdef CGAL_LINKED_WITH_TBB
// Functor for enqueue_task function
template <typename SP, typename Bad_vertices_vector_>
class Perturb_vertex
{
const SP & m_sliver_perturber;
PVertex m_pv;
FT m_sliver_bound;
Visitor & m_visitor;
Bad_vertices_vector_ & m_bad_vertices;
public:
// Constructor
Perturb_vertex(const SP &sp,
const PVertex &pv,
FT sliver_bound,
Visitor& visitor,
Bad_vertices_vector_ &bad_vertices)
: m_sliver_perturber(sp),
m_pv(pv),
m_sliver_bound(sliver_bound),
m_visitor(visitor),
m_bad_vertices(bad_vertices)
{
}
// Constructor
Perturb_vertex(const Perturb_vertex &pvx)
: m_sliver_perturber(pvx.m_sliver_perturber),
m_pv(pvx.m_pv),
m_sliver_bound(pvx.m_sliver_bound),
m_visitor(pvx.m_visitor),
m_bad_vertices(pvx.m_bad_vertices)
{}
// operator()
void operator()() const
{
bool could_lock_zone;
do
{
m_sliver_perturber.perturb_vertex(
m_pv, m_sliver_bound, m_visitor, m_bad_vertices, &could_lock_zone);
m_sliver_perturber.unlock_all_elements();
} while (!could_lock_zone);
if ( m_sliver_perturber.is_time_limit_reached() )
tbb::task::self().cancel_group_execution();
}
};
#endif
// -----------------------------------
// Private data
// -----------------------------------
C3T3& c3t3_;
Tr& tr_;
const MeshDomain& domain_;
SliverCriterion sliver_criterion_;
Perturbation_vector perturbation_vector_;
C3T3_helpers helper_;
// Internal perturbation ordering
int next_perturbation_order_;
// Timer
double time_limit_;
CGAL::Real_timer running_time_;
};
template <typename C3T3, typename Md, typename Sc, typename V_>
Sliver_perturber<C3T3,Md,Sc,V_>::
Sliver_perturber(C3T3& c3t3,
const Md& domain,
const Sc& criterion)
: Base(c3t3.bbox(),
Concurrent_mesher_config::get().locking_grid_num_cells_per_axis)
, c3t3_(c3t3)
, tr_(c3t3_.triangulation())
, domain_(domain)
, sliver_criterion_(criterion)
, helper_(c3t3_,domain_,get_lock_data_structure())
, next_perturbation_order_(0)
, time_limit_(-1)
, running_time_()
{
// If we're multi-thread
tr_.set_lock_data_structure(get_lock_data_structure());
}
template <typename C3T3, typename Md, typename Sc, typename V_>
Mesh_optimization_return_code
Sliver_perturber<C3T3,Md,Sc,V_>::
operator()(Visitor visitor)
{
//check criterion bound
if ( sliver_criterion_.sliver_bound() == 0 )
sliver_criterion_.set_sliver_bound(sliver_criterion_.get_default_value());
// Reset sliver value cache
helper_.reset_cache();
// Init time counter
if (running_time_.is_running())
running_time_.stop();
running_time_.reset();
running_time_.start();
#ifdef CGAL_MESH_3_PROFILING
WallClockTimer t;
#endif
// Build priority queue (we use one queue for all steps)
PQueue pqueue(tr_.number_of_vertices());
// Initialize vertices ids
initialize_vertices_id();
#if defined(CGAL_MESH_3_PERTURBER_VERBOSE) \
|| defined(CGAL_MESH_3_PROFILING)
std::cerr << "Running sliver perturbation..." << std::endl;
#endif
#ifdef CGAL_MESH_3_PERTURBER_LOW_VERBOSITY
std::cerr << "Legend of the following line: "
<< "(#vertices in pqueue, #iterations, #fails)" << std::endl;
#endif
const FT& delta = sliver_criterion_.get_perturbation_unit();
FT current_bound = delta;
bool perturbation_ok = true;
while(current_bound <= sliver_criterion_.sliver_bound() && perturbation_ok)
{
#ifdef CGAL_MESH_3_PERTURBER_HIGH_VERBOSITY
// reset_perturbation_counters is not const
reset_perturbation_counters();
#endif
perturbation_ok = perturb(current_bound, pqueue, visitor);
visitor.bound_reached(current_bound);
current_bound += delta;
if ( (current_bound >= sliver_criterion_.sliver_bound())
&& (current_bound < sliver_criterion_.sliver_bound() + delta) )
{
current_bound = sliver_criterion_.sliver_bound();
}
}
#ifdef CGAL_MESH_3_PROFILING
double perturbation_time = t.elapsed();
#endif
running_time_.stop();
helper_.reset_cache();//in case we re-use caches in another operation
// after this perturbation
#ifdef CGAL_MESH_3_PERTURBER_VERBOSE
std::cerr << std::endl
<< "Total perturbation time: " << running_time_.time() << "s";
std::cerr << std::endl << "Perturbation statistics:" << std::endl;
print_final_perturbations_statistics();
#endif
#ifdef CGAL_MESH_3_PROFILING
std::cerr << std::endl << "Total perturbation 'wall-clock' time: "
<< perturbation_time << "s" << std::endl;
#endif
Mesh_optimization_return_code ret;
if ( is_time_limit_reached() ) {
#if defined(CGAL_MESH_3_PERTURBER_VERBOSE) || defined(CGAL_MESH_3_PROFILING)
std::cerr << "Perturbation return code: TIME_LIMIT_REACHED\n\n";
#endif // CGAL_MESH_3_PERTURBER_VERBOSE
ret = TIME_LIMIT_REACHED;
}
else if ( !perturbation_ok ) {
#if defined(CGAL_MESH_3_PERTURBER_VERBOSE) || defined(CGAL_MESH_3_PROFILING)
std::cerr << "Perturbation return code: CANT_IMPROVE_ANYMORE\n\n";
#endif // CGAL_MESH_3_PERTURBER_VERBOSE
ret = CANT_IMPROVE_ANYMORE;
}
else
{
#if defined(CGAL_MESH_3_PERTURBER_VERBOSE) || defined(CGAL_MESH_3_PROFILING)
std::cerr << "Perturbation return code: BOUND_REACHED\n\n";
#endif // CGAL_MESH_3_PERTURBER_VERBOSE
ret = BOUND_REACHED;
}
#if defined(CGAL_MESH_3_EXPORT_PERFORMANCE_DATA) \
&& defined(CGAL_MESH_3_PROFILING)
if (ret == BOUND_REACHED)
{
CGAL_MESH_3_SET_PERFORMANCE_DATA("Perturber_optim_time", perturbation_time);
}
else
{
CGAL_MESH_3_SET_PERFORMANCE_DATA("Perturber_optim_time",
(ret == CANT_IMPROVE_ANYMORE ?
"CANT_IMPROVE_ANYMORE" : "TIME_LIMIT_REACHED"));
}
#endif
return ret;
}
template <typename C3T3, typename Md, typename Sc, typename V_>
void
Sliver_perturber<C3T3,Md,Sc,V_>::
add_perturbation(Perturbation* perturbation)
{
if ( !perturbation_vector_.empty() )
perturbation_vector_.back().set_next(perturbation);
if ( NULL != perturbation )
{
// Set order
perturbation->set_order(next_perturbation_order_++);
// Add perturbation
perturbation_vector_.push_back(perturbation);
}
}
// -----------------------------------
// Private methods
// -----------------------------------
template <typename C3T3, typename Md, typename Sc, typename V_>
bool
Sliver_perturber<C3T3,Md,Sc,V_>::
perturb(const FT& sliver_bound, PQueue& pqueue, Visitor& visitor) const
{
#ifdef CGAL_MESH_3_PERTURBER_HIGH_VERBOSITY
CGAL::Real_timer timer;
timer.start();
std::streamsize prec = std::cerr.precision(4);
std::cerr << "Perturb sliver vertices (bound: " << sliver_bound
<< ") ..." << std::endl;
std::cerr.precision(prec);
#endif
// build priority queue
int pqueue_size = build_priority_queue(sliver_bound, pqueue);
#ifdef CGAL_MESH_3_PERTURBER_HIGH_VERBOSITY
std::cerr << "Legend of the following line: "
<< "(#vertices in pqueue, #iterations, #fails)" << std::endl;
// Store construction time
timer.stop();
double construction_time = timer.time();
timer.reset();
timer.start();
#endif
// Stores the vertices for which perturbation has failed
Bad_vertices_vector bad_vertices;
#ifdef CGAL_LINKED_WITH_TBB
// Parallel
if (boost::is_convertible<Concurrency_tag, Parallel_tag>::value)
{
this->create_root_task();
while (pqueue.size() > 0)
{
PVertex pv = pqueue.top();
pqueue.pop();
enqueue_task(pv, sliver_bound,
visitor, bad_vertices);
}
this->wait_for_all();
# if defined(CGAL_MESH_3_PERTURBER_VERBOSE) || defined(CGAL_MESH_3_PROFILING)
std::cerr << " Flushing";
# endif
bool keep_flushing = true;
while (keep_flushing)
{
keep_flushing = this->flush_work_buffers();
# if defined(CGAL_MESH_3_PERTURBER_VERBOSE) || defined(CGAL_MESH_3_PROFILING)
std::cerr << ".";
# endif
}
this->destroy_root_task();
}
// Sequential
else
#endif // CGAL_LINKED_WITH_TBB
{
# ifdef CGAL_MESH_3_PERTURBER_VERBOSE
int iteration_nb = 0;
# endif
while ( !is_time_limit_reached() && !pqueue.empty() )
{
// Get pqueue head
PVertex pv = pqueue.top();
pqueue.pop();
--pqueue_size;
CGAL_assertion(pv.is_perturbable());
// Get pvertex slivers list
# ifdef CGAL_NEW_INCIDENT_SLIVERS
Cell_vector slivers;
helper_.new_incident_slivers(pv.vertex(), sliver_criterion_, sliver_bound,
std::back_inserter(slivers));
# else
Cell_vector slivers =
helper_.incident_slivers(pv.vertex(), sliver_criterion_, sliver_bound);
# endif
CGAL_assertion(slivers.size() == pv.sliver_nb());
// Perturb vertex
Vertex_vector modified_vertices;
// pv.perturbation() should not be NULL if pv is in pqueue
CGAL_assertion(pv.perturbation() != NULL);
std::pair<bool,Vertex_handle> perturbation_ok =
pv.perturbation()->operator()(pv.vertex(),
slivers,
c3t3_,
domain_,
sliver_criterion_,
sliver_bound,
modified_vertices);
// If vertex has changed - may happen in two cases: vertex has been moved
// or vertex has been reverted to the same location -
if ( perturbation_ok.second != pv.vertex() )
{
// Update pvertex vertex
pv.set_vertex(perturbation_ok.second);
}
// If v has been moved
if ( perturbation_ok.first )
{
// Update pvertex
update_pvertex(pv,sliver_bound);
// If pv needs to be modified again, try first perturbation
pv.set_perturbation(&perturbation_vector_.front());
pv.increment_try_nb();
// update modified vertices
pqueue_size += update_priority_queue(modified_vertices,
sliver_bound,
pqueue);
}
else
{
// If perturbation fails, try next one
pv.set_perturbation(pv.perturbation()->next());
if ( NULL == pv.perturbation() )
{
bad_vertices.push_back(pv.vertex());
}
}
// Update pqueue in every cases, because pv was poped
pqueue_size += update_priority_queue(pv, pqueue);
visitor.end_of_perturbation_iteration(pqueue_size);
# ifdef CGAL_MESH_3_PERTURBER_HIGH_VERBOSITY
++iteration_nb;
std::cerr << boost::format("\r \r"
"(%1%,%2%,%4%) (%|3$.1f| iteration/s)")
% pqueue_size
% iteration_nb
% (iteration_nb / timer.time())
% bad_vertices.size();
# endif
# ifdef CGAL_MESH_3_PERTURBER_LOW_VERBOSITY
++iteration_nb;
std::cerr << boost::format("\r \r"
"bound %5%: (%1%,%2%,%4%) (%|3$.1f| iteration/s)")
% pqueue_size
% iteration_nb
% (iteration_nb / running_time_.time())
% bad_vertices.size()
% sliver_bound;
# endif
}
}
#ifdef CGAL_MESH_3_PERTURBER_HIGH_VERBOSITY
std::cerr << std::endl;
print_perturbations_statistics();
std::cerr << "Step perturbation time: " << timer.time() + construction_time
<< "s" << std::endl << std::endl;
#endif
if ( is_time_limit_reached() )
return false;
// update bad vertices list (remove those which are not bad anymore)
update_bad_vertices(bad_vertices,sliver_bound);
return bad_vertices.empty();
}
#ifdef CGAL_FASTER_BUILD_QUEUE
template <typename C3T3, typename Md, typename Sc, typename V_>
int
Sliver_perturber<C3T3,Md,Sc,V_>::
build_priority_queue(const FT& sliver_bound, PQueue& pqueue) const
{
CGAL_precondition(pqueue.empty());
#ifdef CGAL_MESH_3_PERTURBER_HIGH_VERBOSITY
CGAL::Real_timer timer;
timer.start();
std::cerr << "Build pqueue...";
#endif
int pqueue_size = 0;
typedef CGAL::Hash_handles_with_or_without_timestamps Hash_fct;
typedef boost::unordered_map<Vertex_handle, PVertex, Hash_fct> M;
M vpm;
for ( typename Tr::Finite_cells_iterator cit = tr_.finite_cells_begin();
cit != tr_.finite_cells_end() ;
++cit )
{
if(helper_.is_sliver(cit, sliver_criterion_, sliver_bound))
{
double d = cit->sliver_value();
for(int i=0; i< 4; i++){
Vertex_handle vh = cit->vertex(i);
PVertex& pv = vpm[vh];
if(pv.sliver_nb() ==0)
{
pv.set_vertex(vh);
pv.set_id( get_pvertex_id(vh));
pv.set_sliver_nb(1);
pv.set_min_value(d);
pv.set_perturbation(&perturbation_vector_.front());
}
else
{
pv.set_sliver_nb(pv.sliver_nb()+1);
if(d < pv.min_value())
pv.set_min_value(d);
}
}
}
}
for( typename M::iterator vit = vpm.begin();
vit != vpm.end() ;
++vit )
pqueue_size += update_priority_queue(vit->second, pqueue);
#ifdef CGAL_MESH_3_PERTURBER_HIGH_VERBOSITY
std::cerr << "done (" << pqueue_size << " vertices inserted in "
<< timer.time() << "s)\n";
#endif
return pqueue_size;
}
#else // not CGAL_FASTER_BUILD_QUEUE
template <typename C3T3, typename Md, typename Sc, typename V_>
int
Sliver_perturber<C3T3,Md,Sc,V_>::
build_priority_queue(const FT& sliver_bound, PQueue& pqueue) const
{
CGAL_precondition(pqueue.empty());
#ifdef CGAL_MESH_3_PERTURBER_HIGH_VERBOSITY
CGAL::Real_timer timer;
timer.start();
std::cerr << "Build pqueue...";
#endif
int pqueue_size = 0;
for ( typename Tr::Finite_vertices_iterator vit = tr_.finite_vertices_begin();
vit != tr_.finite_vertices_end() ;
++vit )
{
PVertex pv = make_pvertex(vit, sliver_bound, get_pvertex_id(vit));
pqueue_size += update_priority_queue(pv, pqueue);
}
#ifdef CGAL_MESH_3_PERTURBER_HIGH_VERBOSITY
std::cerr << "done (" << pqueue_size << " vertices inserted in "
<< timer.time() << "s)\n";
#endif
return pqueue_size;
}
#endif // not CGAL_FASTER_BUILD_QUEUE
template <typename C3T3, typename Md, typename Sc, typename V_>
int
Sliver_perturber<C3T3,Md,Sc,V_>::
update_priority_queue(const Vertex_vector& vertices,
const FT& sliver_bound,
PQueue& pqueue) const
{
int modified_pv_nb = 0;
for ( typename Vertex_vector::const_iterator vit = vertices.begin() ;
vit != vertices.end() ;
++vit )
{
PVertex pv = make_pvertex(*vit,sliver_bound,get_pvertex_id(*vit));
modified_pv_nb += update_priority_queue(pv, pqueue);
}
return modified_pv_nb;
}
#ifdef CGAL_LINKED_WITH_TBB
// For parallel version
template <typename C3T3, typename Md, typename Sc, typename V_>
int
Sliver_perturber<C3T3,Md,Sc,V_>::
update_priority_queue( const Vertex_vector& vertices
, const FT& sliver_bound
, Visitor& visitor
, Bad_vertices_vector &bad_vertices) const
{
int modified_pv_nb = 0;
for ( typename Vertex_vector::const_iterator vit = vertices.begin() ;
vit != vertices.end() ;
++vit )
{
PVertex pv = make_pvertex__concurrent(*vit,sliver_bound,get_pvertex_id(*vit));
if (pv.is_perturbable())
{
enqueue_task(pv, sliver_bound, visitor, bad_vertices);
++modified_pv_nb;
}
}
return modified_pv_nb;
}
#endif
template <typename C3T3, typename Md, typename Sc, typename V_>
int
Sliver_perturber<C3T3,Md,Sc,V_>::
update_priority_queue(const PVertex& pv, PQueue& pqueue) const
{
if ( pqueue.contains(pv) )
{
if ( pv.is_perturbable() )
{
pqueue.update(pv);
return 0;
}
else
{
pqueue.remove(pv);
return -1;
}
}
else
{
if ( pv.is_perturbable() )
{
pqueue.push(pv);
return 1;
}
}
return 0;
}
#ifdef CGAL_LINKED_WITH_TBB
// For parallel version
template <typename C3T3, typename Md, typename Sc, typename V_>
void
Sliver_perturber<C3T3,Md,Sc,V_>::
perturb_vertex( PVertex pv
, const FT& sliver_bound
, Visitor& visitor
, Bad_vertices_vector &bad_vertices
, bool *could_lock_zone
) const
{
typename Gt::Construct_point_3 cp = tr_.geom_traits().construct_point_3_object();
#ifdef CGAL_CONCURRENT_MESH_3_PROFILING
static Profile_branch_counter_3 bcounter(
"early withdrawals / late withdrawals / successes [Perturber]");
#endif
*could_lock_zone = true;
// Zombie?
if (pv.is_zombie())
{
return;
}
Bare_point p = cp(pv.vertex()->point());
if (!helper_.try_lock_point_no_spin(pv.vertex()->point()) ||
! tr_.geom_traits().equal_3_object()(p, cp(pv.vertex()->point())))
{
#ifdef CGAL_CONCURRENT_MESH_3_PROFILING
bcounter.increment_branch_2(); // THIS is an early withdrawal!
#endif
*could_lock_zone = false;
return;
}
// Zombie? (in case the vertex has changed in the meantime)
if (pv.is_zombie())
{
return;
}
CGAL_assertion(pv.is_perturbable());
int num_new_vertices_to_treat = 0;
Cell_vector slivers;
slivers.reserve(8);
if (!helper_.try_lock_and_get_incident_slivers(
pv.vertex(), sliver_criterion_, sliver_bound, slivers))
{
*could_lock_zone = false;
#ifdef CGAL_CONCURRENT_MESH_3_PROFILING
bcounter.increment_branch_1(); // THIS is a late withdrawal!
#endif
}
else
{
// Slivers may be empty if the vertex has been modified by another thread in the meatime
if (slivers.empty())
{
return;
}
// Perturb vertex
Vertex_vector modified_vertices;
// pv.perturbation() should not be NULL if pv is in pqueue
CGAL_assertion(pv.perturbation() != NULL);
std::pair<bool,Vertex_handle> perturbation_ok =
pv.perturbation()->operator()(pv.vertex(),
slivers,
c3t3_,
domain_,
sliver_criterion_,
sliver_bound,
modified_vertices,
could_lock_zone);
if (*could_lock_zone)
{
// If vertex has changed - may happen in two cases: vertex has been moved
// or vertex has been reverted to the same location -
if ( perturbation_ok.second != pv.vertex() )
{
// Update pvertex vertex
pv.set_vertex(perturbation_ok.second);
}
// If the vertex hasn't changed, we still need to "virtually" increment
// the erase counter, because we need to invalidate the PVertex that
// may be in other threads' queues
else
{
this->increment_erase_counter(pv.vertex());
}
// If v has been moved
if ( perturbation_ok.first )
{
// Update pvertex
update_pvertex__concurrent(pv,sliver_bound);
// If pv needs to be modified again, try first perturbation
pv.set_perturbation(&perturbation_vector_.front());
pv.increment_try_nb();
// update modified vertices
num_new_vertices_to_treat +=
update_priority_queue(modified_vertices, sliver_bound, visitor, bad_vertices);
}
else
{
// If perturbation fails, try next one
pv.set_perturbation(pv.perturbation()->next());
pv.update_saved_erase_counter();
if ( NULL == pv.perturbation() )
{
bad_vertices.push_back(pv.vertex());
}
}
#ifdef CGAL_CONCURRENT_MESH_3_PROFILING
++bcounter;
#endif
// Update pqueue in every cases, because pv was poped
if (pv.is_perturbable())
{
enqueue_task(pv, sliver_bound, visitor, bad_vertices);
++num_new_vertices_to_treat;
}
}
else
{
#ifdef CGAL_CONCURRENT_MESH_3_PROFILING
bcounter.increment_branch_1(); // THIS is a late withdrawal!
#endif
}
}
visitor.end_of_perturbation_iteration(0);
}
#endif
// Sequential
template <typename C3T3, typename Md, typename Sc, typename V_>
typename Sliver_perturber<C3T3,Md,Sc,V_>::PVertex
Sliver_perturber<C3T3,Md,Sc,V_>::
make_pvertex(const Vertex_handle& vh,
const FT& sliver_bound,
const typename PVertex::id_type& pv_id) const
{
CGAL_assertion(!tr_.is_infinite(vh));
// Make pvertex in all cases
PVertex pv(vh,pv_id);
pv.set_perturbation(&perturbation_vector_.front());
update_pvertex(pv, sliver_bound);
return pv;
}
// Parallel
template <typename C3T3, typename Md, typename Sc, typename V_>
typename Sliver_perturber<C3T3,Md,Sc,V_>::PVertex
Sliver_perturber<C3T3,Md,Sc,V_>::
make_pvertex__concurrent(
const Vertex_handle& vh,
const FT& sliver_bound,
const typename PVertex::id_type& pv_id) const
{
// Make pvertex in all cases
PVertex pv(vh,pv_id);
pv.set_perturbation(&perturbation_vector_.front());
update_pvertex__concurrent(pv, sliver_bound);
return pv;
}
// Sequential
template <typename C3T3, typename Md, typename Sc, typename V_>
void
Sliver_perturber<C3T3,Md,Sc,V_>::
update_pvertex(PVertex& pv, const FT& sliver_bound) const
{
#ifdef CGAL_NEW_INCIDENT_SLIVERS
Cell_vector slivers;
helper_.new_incident_slivers(pv.vertex(), sliver_criterion_, sliver_bound, std::back_inserter(slivers));
#else
Cell_vector slivers =
helper_.incident_slivers(pv.vertex(), sliver_criterion_, sliver_bound);
#endif
pv.set_sliver_nb(static_cast<unsigned int>(slivers.size()));
pv.set_min_value(helper_.min_sliver_value(slivers, sliver_criterion_));
}
#ifdef CGAL_LINKED_WITH_TBB
// Parallel
template <typename C3T3, typename Md, typename Sc, typename V_>
void
Sliver_perturber<C3T3,Md,Sc,V_>::
update_pvertex__concurrent(PVertex& pv, const FT& sliver_bound) const
{
Cell_vector slivers;
helper_.get_incident_slivers_without_using_tds_data(
pv.vertex(), sliver_criterion_, sliver_bound, slivers);
pv.set_sliver_nb(static_cast<unsigned int>(slivers.size()));
pv.set_min_value(helper_.min_sliver_value(slivers, sliver_criterion_));
}
#endif
// Sequential
template <typename C3T3, typename Md, typename Sc, typename V_>
void
Sliver_perturber<C3T3,Md,Sc,V_>::
update_bad_vertices(std::vector<Vertex_handle> &bad_vertices,
const FT& sliver_bound) const
{
typename std::vector<Vertex_handle>::iterator vit = bad_vertices.begin();
while ( vit != bad_vertices.end() )
{
if ( tr_.is_vertex(*vit)
&& helper_.min_incident_value(*vit,sliver_criterion_) <= sliver_bound )
{
++vit;
}
else
{
vit = bad_vertices.erase(vit);
}
}
}
#ifdef CGAL_LINKED_WITH_TBB
// Parallel
template <typename C3T3, typename Md, typename Sc, typename V_>
void
Sliver_perturber<C3T3,Md,Sc,V_>::
update_bad_vertices(tbb::concurrent_vector<Vertex_handle> &bad_vertices,
const FT& sliver_bound) const
{
tbb::concurrent_vector<Vertex_handle> tmpv;
typename tbb::concurrent_vector<Vertex_handle>::iterator vit
= bad_vertices.begin();
while ( vit != bad_vertices.end() )
{
if ( tr_.is_vertex(*vit)
&& helper_.min_incident_value(*vit,sliver_criterion_) <= sliver_bound )
{
tmpv.push_back(*vit);
}
++vit;
}
bad_vertices.swap(tmpv);
}
#endif // CGAL_LINKED_WITH_TBB
template <typename C3T3, typename Md, typename Sc, typename V_>
void
Sliver_perturber<C3T3,Md,Sc,V_>::
initialize_vertices_id() const
{
int cur_id = 0;
for(typename Tr::Finite_vertices_iterator it = tr_.finite_vertices_begin();
it != tr_.finite_vertices_end(); ++it) {
it->set_meshing_info(cur_id++);
}
}
#ifdef CGAL_MESH_3_PERTURBER_VERBOSE
template <typename C3T3, typename Md, typename Sc, typename V_>
void
Sliver_perturber<C3T3,Md,Sc,V_>::
print_perturbations_statistics() const
{
int total_perturbation_nb = 0;
typename Perturbation_vector::const_iterator it = perturbation_vector_.begin();
for ( ; it != perturbation_vector_.end() ; ++it )
{
total_perturbation_nb += it->counter();
}
if ( 0 == total_perturbation_nb )
{
std::cerr << "No perturbation done at this step" << std::endl;
return;
}
for ( it = perturbation_vector_.begin() ;
it != perturbation_vector_.end() ;
++it )
{
std::cerr << it->perturbation_name() << ": "
<< (double)it->counter() / (double)total_perturbation_nb * 100.
<< "% (" << it->counter() << " in " << it->time() << "s)"
<< std::endl;
}
}
template <typename C3T3, typename Md, typename Sc, typename V_>
void
Sliver_perturber<C3T3,Md,Sc,V_>::
print_final_perturbations_statistics() const
{
int total_perturbation_nb = 0;
typename Perturbation_vector::const_iterator it = perturbation_vector_.begin();
for ( ; it != perturbation_vector_.end() ; ++it )
{
total_perturbation_nb += it->total_counter();
}
if ( 0 == total_perturbation_nb )
{
std::cerr << "No perturbation done" << std::endl;
return;
}
for ( it = perturbation_vector_.begin() ;
it != perturbation_vector_.end() ;
++it )
{
std::cerr << it->perturbation_name() << ": "
<< (double)it->total_counter() / (double)total_perturbation_nb * 100.
<< "% (" << it->total_counter() << " in " << it->total_time() << "ms)"
<< std::endl;
}
}
template <typename C3T3, typename Md, typename Sc, typename V_>
void
Sliver_perturber<C3T3,Md,Sc,V_>::
reset_perturbation_counters()
{
typename Perturbation_vector::iterator it = perturbation_vector_.begin();
for ( ; it != perturbation_vector_.end() ; ++it )
{
it->reset_counter();
it->reset_timer();
}
}
#endif
#ifdef CGAL_LINKED_WITH_TBB
// For parallel version
template <typename C3T3, typename Md, typename Sc, typename V_>
void
Sliver_perturber<C3T3,Md,Sc,V_>::
enqueue_task(const PVertex &pv,
const FT& sliver_bound,
Visitor& visitor,
Bad_vertices_vector &bad_vertices
) const
{
this->enqueue_work(
Perturb_vertex<Self, Bad_vertices_vector>(
*this, pv, sliver_bound, visitor, bad_vertices),
pv);
}
#endif
} // end namespace Mesh_3
} //namespace CGAL
#include <CGAL/enable_warnings.h>
#endif // CGAL_MESH_3_SLIVERS_PERTURBER_H