dust3d/thirdparty/cgal/CGAL-5.1/include/CGAL/Mesh_3/Slivers_exuder.h

1873 lines
59 KiB
C++

// Copyright (c) 2004-2007 INRIA Sophia-Antipolis (France).
// Copyright (c) 2008 GeometryFactory, Sophia Antipolis (France)
// Copyright (c) 2009 INRIA Sophia-Antipolis (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL: https://github.com/CGAL/cgal/blob/v5.1/Mesh_3/include/CGAL/Mesh_3/Slivers_exuder.h $
// $Id: Slivers_exuder.h 254d60f 2019-10-19T15:23:19+02:00 Sébastien Loriot
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s) : Laurent Rineau, Stephane Tayeb
#ifndef CGAL_MESH_3_SLIVERS_EXUDER_H
#define CGAL_MESH_3_SLIVERS_EXUDER_H
#include <CGAL/license/Mesh_3.h>
#include <CGAL/disable_warnings.h>
#include <CGAL/Mesh_3/config.h>
#include <CGAL/Mesh_3/Concurrent_mesher_config.h>
#include <CGAL/Mesh_3/sliver_criteria.h>
#include <CGAL/Mesh_optimization_return_code.h>
#include <CGAL/Mesh_3/Null_exuder_visitor.h>
#include <CGAL/Mesh_3/Triangulation_helpers.h>
#include <CGAL/Bbox_3.h>
#include <CGAL/Double_map.h>
#include <CGAL/enum.h>
#include <CGAL/functional.h>
#include <CGAL/internal/Has_member_visited.h>
#include <CGAL/iterator.h>
#include <CGAL/Real_timer.h>
#include <CGAL/boost/iterator/transform_iterator.hpp>
#include <boost/bind.hpp>
#include <boost/format.hpp>
#include <boost/function_output_iterator.hpp>
#include <boost/optional.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <algorithm>
#include <iomanip> // std::setprecision
#include <iostream> // std::cerr/cout
#include <map>
#include <set>
#include <vector>
#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
#ifdef CGAL_MESH_3_VERBOSE
#define CGAL_MESH_3_EXUDER_VERBOSE
#endif
namespace CGAL {
namespace Mesh_3 {
namespace details { // various function objects
// That functor Second_of takes a pair as input (the value type of a
// map), and returns the ".second" member of that pair. It is used in
// Slivers_exuder, to constructor a transform iterator.
// It should be doable using STL bind operators, but i am not sure how
// to use them. -- Laurent Rineau, 2007/07/27
template <typename Map>
struct Second_of
: public CGAL::cpp98::unary_function<typename Map::value_type,
const typename Map::mapped_type&>
{
typedef CGAL::cpp98::unary_function<typename Map::value_type,
const typename Map::mapped_type&> Base;
typedef typename Base::result_type result_type;
typedef typename Base::argument_type argument_type;
const typename Map::mapped_type&
operator()(const typename Map::value_type& p) const
{
return p.second;
}
}; // end class Second_of
} // end namespace details
/************************************************
// Class Slivers_exuder_base
// Two versions: sequential / parallel
************************************************/
// Sequential
template <typename Tr, typename Concurrency_tag>
class Slivers_exuder_base
{
protected:
typedef typename Tr::Vertex_handle Vertex_handle;
typedef typename Tr::Cell_handle Cell_handle;
typedef std::vector<Cell_handle> Cell_vector;
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;
// A priority queue ordered on Tet quality (SliverCriteria)
typedef CGAL::Double_map<Cell_handle, double> Tet_priority_queue;
typedef typename Tet_priority_queue::reverse_iterator Queue_iterator;
typedef typename Tet_priority_queue::Reverse_entry Queue_value_type;
Slivers_exuder_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>
void enqueue_work(Func, FT) const {}
protected:
Cell_handle extract_cell_handle_from_queue_value(const Queue_value_type &qv) const
{
return qv.second;
}
FT extract_cell_quality_from_queue_value(const Queue_value_type &qv) const
{
return qv.first;
}
unsigned int extract_erase_counter_from_queue_value(const Queue_value_type &) const
{
return 0;
}
// Dummy
unsigned int erase_counter(const Cell_handle &) const { return 0; }
std::size_t cells_queue_size() const { return cells_queue_.size(); }
bool cells_queue_empty() const { return cells_queue_.empty(); }
Queue_iterator
cells_queue_front() { return cells_queue_.front(); }
void cells_queue_pop_front() { cells_queue_.pop_front(); }
void cells_queue_clear() { cells_queue_.clear(); }
void cells_queue_insert(const Cell_handle &ch, FT quality_value)
{
cells_queue_.insert(ch, quality_value);
}
/**
* A functor to remove one \c Cell_handle from a priority queue
*/
class Erase_from_queue
{
public:
Erase_from_queue(Tet_priority_queue& queue)
: r_queue_(queue) { }
void operator()(const Cell_handle& cell) { r_queue_.erase(cell); }
private:
Tet_priority_queue& r_queue_;
};
/**
* Delete cells of \c cells from \c cells_queue
*/
void delete_cells_from_queue(const Cell_vector& cells)
{
std::for_each(cells.begin(), cells.end(),
Erase_from_queue(cells_queue_));
}
private:
Tet_priority_queue cells_queue_;
};
// The debug version of the VC++ testsuite gets a timeout when using TBB, so let's disable it
#if defined( CGAL_LINKED_WITH_TBB ) && ( !defined (BOOST_MSVC) || !defined( _DEBUG ) || !defined (CGAL_TEST_SUITE) )
// Parallel
template <typename Tr>
class Slivers_exuder_base<Tr, Parallel_tag>
{
protected:
typedef typename Tr::Vertex_handle Vertex_handle;
typedef typename Tr::Cell_handle Cell_handle;
typedef std::vector<Cell_handle> Cell_vector;
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;
// A priority queue ordered on Tet quality (SliverCriteria)
typedef std::multimap<
FT, std::pair<Cell_handle, unsigned int> > Tet_priority_queue;
typedef typename Tet_priority_queue::iterator Queue_iterator;
typedef typename Tet_priority_queue::value_type Queue_value_type;
Slivers_exuder_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>
void enqueue_work(Func f, FT value) const
{
CGAL_assertion(m_empty_root_task != 0);
m_worksharing_ds.enqueue_work(f, value, *m_empty_root_task);
}
public:
protected:
Cell_handle extract_cell_handle_from_queue_value(const Queue_value_type &qv) const
{
return qv.second.first;
}
FT extract_cell_quality_from_queue_value(const Queue_value_type &qv) const
{
return qv.first;
}
unsigned int extract_erase_counter_from_queue_value(const Queue_value_type &qv) const
{
return qv.second.second;
}
unsigned int erase_counter(const Cell_handle &ch) const
{
return ch->erase_counter();
}
std::size_t cells_queue_size() const { return cells_queue_.size(); }
bool cells_queue_empty() const { return cells_queue_.empty(); }
Queue_iterator cells_queue_front() { return cells_queue_.begin(); }
void cells_queue_pop_front() { cells_queue_.erase(cells_queue_front()); }
void cells_queue_clear() { cells_queue_.clear(); }
void cells_queue_insert(const Cell_handle &ch, FT quality_value)
{
cells_queue_.insert(std::make_pair(
quality_value,
std::make_pair(ch, ch->erase_counter())));
}
/**
* A functor to remove one \c Cell_handle from a priority queue
*/
class Erase_from_queue
{
public:
Erase_from_queue(Tet_priority_queue&) {}
void operator()(const Cell_handle& cell)
{ cell->increment_erase_counter(); }
};
/**
* Delete cells of \c cells from \c cells_queue
*/
void delete_cells_from_queue(const Cell_vector& cells)
{
std::for_each(cells.begin(), cells.end(),
Erase_from_queue(cells_queue_));
}
mutable Lock_data_structure m_lock_ds;
mutable Mesh_3::Auto_worksharing_ds m_worksharing_ds;
mutable tbb::task *m_empty_root_task;
private:
Tet_priority_queue cells_queue_;
};
#endif // CGAL_LINKED_WITH_TBB
/************************************************
// Class Slivers_exuder
************************************************/
template <
typename C3T3,
typename SliverCriteria,
typename Visitor_ = Null_exuder_visitor<C3T3>
>
class Slivers_exuder
: public Slivers_exuder_base<typename C3T3::Triangulation,
typename C3T3::Concurrency_tag>
{
public: // Types
typedef typename C3T3::Concurrency_tag Concurrency_tag;
typedef Slivers_exuder_base<
typename C3T3::Triangulation, Concurrency_tag> Base;
private: // Types
typedef Slivers_exuder<C3T3, SliverCriteria, Visitor_> Self;
typedef typename C3T3::Triangulation Tr;
typedef typename Tr::Weighted_point Weighted_point;
typedef typename Tr::Bare_point Bare_point;
typedef typename Tr::Cell_handle Cell_handle;
typedef typename Tr::Facet Facet;
typedef typename Tr::Vertex_handle Vertex_handle;
typedef typename Weighted_point::Weight Weight;
typedef typename Base::Queue_value_type Queue_value_type;
typedef typename Base::Cell_vector Cell_vector;
typedef typename Tr::Geom_traits Gt;
typedef typename Base::FT FT;
typedef typename Gt::Tetrahedron_3 Tetrahedron_3;
typedef typename C3T3::Cells_in_complex_iterator Cell_iterator;
typedef std::vector<Facet> Facet_vector;
typedef typename C3T3::Surface_patch_index Surface_patch_index;
typedef typename C3T3::Subdomain_index Subdomain_index;
typedef typename C3T3::Index Index;
// Umbrella will store the surface_index of internal facets of a new
// weighted point conflict zone. Such facets are represented by their edge
// which do not contain the pumped vertex
typedef std::pair<Vertex_handle,Vertex_handle> Ordered_edge;
typedef std::pair<Surface_patch_index, std::size_t> Patch_and_counter;
typedef std::map<Ordered_edge, Patch_and_counter> Umbrella;
// Boundary_facets_from_outside represents the facet of the conflict zone
// seen from outside of it. It stores Surface_patch_index of the facet, and
// Subdomain_index of the cell which is inside the conflict zone.
typedef std::map<Facet,
std::pair<Surface_patch_index, Subdomain_index> > Boundary_facets_from_outside;
/** Pre_star will represent the pre-star of a point. It is a (double)-map
* of Facet (viewed from cells inside the star), ordered by the
* critial_radius of the point with the cell that lies on the facet, at
* the exterior of the pre-star. */
typedef CGAL::Double_map<Facet, FT> Pre_star;
// Stores the value of facet for the sliver criterion functor
typedef std::map<Facet, FT> Sliver_values;
// Visitor class
// Should define
// - after_cell_pumped(std::size_t cells_left_number)
typedef Visitor_ Visitor;
// Helper (for 'get_sq_distance_to_closest_vertex')
typedef Triangulation_helpers<Tr> Tr_helpers;
using Base::get_lock_data_structure;
public: // methods
/**
* @brief Constructor
* @param c3t3 The mesh to exude
* @param criteria The criterion which will be used to evaluate tet quality
* @param d defines the maximal weight we will try:
* max_weight(v) < d*dist(v,nearest_vertice(v))
*/
Slivers_exuder(C3T3& c3t3,
const SliverCriteria& criterion,
const FT d = 0.45);
/**
* @brief pumps vertices
* @param criterion_value_limit All vertices of tetrahedra that have a
* quality below this bound will be pumped
*/
Mesh_optimization_return_code operator()(Visitor visitor = Visitor())
{
#ifdef CGAL_MESH_3_PROFILING
WallClockTimer t;
#endif
Mesh_optimization_return_code ret =
pump_vertices<true>(sliver_criteria_.sliver_bound(), visitor);
#ifdef CGAL_MESH_3_PROFILING
double exudation_time = t.elapsed();
std::cerr << std::endl << "==== Total exudation 'wall-clock' time: "
<< exudation_time << "s ====" << std::endl;
#endif
#ifdef CGAL_MESH_3_EXPORT_PERFORMANCE_DATA
if (ret == BOUND_REACHED)
{
CGAL_MESH_3_SET_PERFORMANCE_DATA("Exuder_optim_time", exudation_time);
}
else
{
CGAL_MESH_3_SET_PERFORMANCE_DATA("Exuder_optim_time",
(ret == CANT_IMPROVE_ANYMORE ?
"CANT_IMPROVE_ANYMORE" : "TIME_LIMIT_REACHED"));
}
#endif
return ret;
}
/// Time accessors
void set_time_limit(double time) { time_limit_ = time; }
double time_limit() const { return time_limit_; }
private:
// -----------------------------------
// Private Methods
// -----------------------------------
/**
* Pumps vertices
*/
template <bool pump_vertices_on_surfaces>
Mesh_optimization_return_code
pump_vertices(FT criterion_value_limit, Visitor& v);
/**
* Pump one vertex
*/
template <bool pump_vertices_on_surfaces>
bool pump_vertex(const Vertex_handle& v,
bool *could_lock_zone = nullptr);
/**
* Returns the best_weight of v
*/
FT get_best_weight(const Vertex_handle& v,
bool *could_lock_zone = nullptr) const;
/**
* Initializes pre_star and criterion_values
*/
void
initialize_prestar_and_criterion_values(const Vertex_handle& v,
const Cell_vector& incident_cells,
Pre_star& pre_star,
Sliver_values& criterion_values) const;
/**
* Expand pre_star with cell_to_add
*/
bool expand_prestar(const Cell_handle& cell_to_add,
const Vertex_handle& pumped_vertex,
Pre_star& pre_star,
Sliver_values& criterion_values) const;
/**
* Returns Ordered_edge of facet which do not contains vertex
*/
Ordered_edge get_opposite_ordered_edge(const Facet& facet,
const Vertex_handle& vertex) const;
/**
* Returns the umbrella of internal_facets vector
*/
boost::optional<Umbrella>
get_umbrella(const Facet_vector& internal_facets,
const Vertex_handle& v) const;
/**
* Updates the mesh with new_point
*/
template <bool pump_vertices_on_surfaces>
bool update_mesh(const Weighted_point& new_point,
const Vertex_handle& old_vertex,
bool *could_lock_zone = nullptr);
/**
* Restores cells and boundary facets of conflict zone of new_vertex in c3t3_
*/
template <bool pump_vertices_on_surfaces>
void restore_cells_and_boundary_facets(
const Boundary_facets_from_outside& boundary_facets_from_outside,
const Vertex_handle& new_vertex);
/**
* Restore internal facets of conflict zone of new_vertex in c3t3_
*/
void restore_internal_facets(const Umbrella& umbrella,
const Vertex_handle& new_vertex);
/**
* Orders handles \c h1 & \c h2
*/
template <typename Handle>
static
void order_two_handles(Handle& h1, Handle& h2)
{
if( h2 < h1 )
std::swap(h1, h2);
}
template <typename Handle>
static
void order_three_handles(Handle& h1, Handle& h2, Handle& h3)
{
if(h1 > h2) std::swap(h1, h2);
if(h2 > h3) std::swap(h2, h3);
if(h1 > h2) std::swap(h1, h2);
}
/**
* Initialization
*/
void init(double limit_value)
{
if ( 0 < limit_value )
sliver_criteria_.set_sliver_bound(limit_value);
else
sliver_criteria_.set_sliver_bound(sliver_criteria_.get_max_value());
this->cells_queue_clear();
initialize_cells_priority_queue();
initialized_ = true;
}
/**
* Initialize cells_queue w.r.t sliver_bound_
*/
void initialize_cells_priority_queue()
{
for( Cell_iterator cit = c3t3_.cells_in_complex_begin() ;
cit != c3t3_.cells_in_complex_end() ;
++cit)
{
const FT value = sliver_criteria_(cit);
if( value < sliver_criteria_.sliver_bound() )
this->cells_queue_insert(cit, value);
}
}
/**
* Returns the min value of second element of Ratio
*/
FT get_min_value(const Sliver_values& criterion_values) const
{
using boost::make_transform_iterator;
typedef details::Second_of<Sliver_values> Second_of;
return *(std::min_element(
make_transform_iterator(criterion_values.begin(), Second_of()),
make_transform_iterator(criterion_values.end(), Second_of())));
}
/**
* Returns the \c Boundary_facets_from_outside object containing mirror facets
* of \c facets
*/
Boundary_facets_from_outside
get_boundary_facets_from_outside(const Facet_vector& facets) const
{
Boundary_facets_from_outside boundary_facets_from_outside;
for(typename Facet_vector::const_iterator fit = facets.begin();
fit != facets.end();
++fit)
{
boundary_facets_from_outside.insert(std::make_pair(
tr_.mirror_facet(*fit),
std::make_pair(c3t3_.surface_patch_index(*fit),
c3t3_.subdomain_index(fit->first))));
}
return boundary_facets_from_outside;
}
/**
* Add a cell \c ch to \c cells_queue
*/
template <bool pump_vertices_on_surfaces>
void add_cell_to_queue(Cell_handle ch, FT criterion_value)
{
#if defined( CGAL_LINKED_WITH_TBB ) && ( !defined (BOOST_MSVC) || !defined( _DEBUG ) || !defined (CGAL_TEST_SUITE) )
// Parallel
if (boost::is_convertible<Concurrency_tag, Parallel_tag>::value)
enqueue_task<pump_vertices_on_surfaces>(
ch, this->erase_counter(ch), criterion_value);
// Sequential
else
#endif
this->cells_queue_insert(ch, criterion_value);
}
/**
* A functor to remove one handle (Cell_handle/Facet_handle) from complex
*/
class Remove_from_complex
{
public:
Remove_from_complex(C3T3& c3t3)
: c3t3_(c3t3) { }
template <typename Handle_>
void operator()(const Handle_& handle)
{ c3t3_.remove_from_complex(handle); }
private:
C3T3& c3t3_;
};
/**
* Removes objects of [begin,end[ range from \c c3t3_
*/
template<typename ForwardIterator>
void remove_from_c3t3(ForwardIterator begin, ForwardIterator end)
{
std::for_each(begin, end, Remove_from_complex(c3t3_));
}
/**
* Returns true if time_limit is reached
*/
bool is_time_limit_reached() const
{
return ( (time_limit() > 0) && (running_time_.time() > time_limit()) );
}
/**
* Returns `true` if all cells of mesh have a sliver_criteria_ value greater
* than `sliver_bound_`
*/
bool check_sliver_bound() const
{
for( Cell_iterator cit = c3t3_.cells_in_complex_begin() ;
cit != c3t3_.cells_in_complex_end() ;
++cit)
{
const FT value = sliver_criteria_(cit);
if( value < sliver_criteria_.sliver_bound() )
return false;
}
return true;
}
#if defined( CGAL_LINKED_WITH_TBB ) && ( !defined (BOOST_MSVC) || !defined( _DEBUG ) || !defined (CGAL_TEST_SUITE) )
// For parallel version
template <bool pump_vertices_on_surfaces>
void
enqueue_task(Cell_handle ch, unsigned int erase_counter, FT value);
#endif
private:
#if defined( CGAL_LINKED_WITH_TBB ) && ( !defined (BOOST_MSVC) || !defined( _DEBUG ) || !defined (CGAL_TEST_SUITE) )
// Functor for enqueue_task function
template <typename SE, bool pump_vertices_on_surfaces>
class Pump_vertex
{
SE & m_sliver_exuder;
const C3T3 & m_c3t3;
Cell_handle m_cell_handle;
unsigned int m_erase_counter;
public:
// Constructor
Pump_vertex(SE &sliver_exuder,
const C3T3 &c3t3,
Cell_handle cell_handle,
unsigned int erase_counter)
: m_sliver_exuder(sliver_exuder),
m_c3t3(c3t3),
m_cell_handle(cell_handle),
m_erase_counter(erase_counter)
{
}
// Constructor
Pump_vertex(const Pump_vertex &pvx)
: m_sliver_exuder(pvx.m_sliver_exuder),
m_c3t3(pvx.m_c3t3),
m_cell_handle(pvx.m_cell_handle),
m_erase_counter(pvx.m_erase_counter)
{}
// operator()
void operator()() const
{
#ifdef CGAL_CONCURRENT_MESH_3_PROFILING
static Profile_branch_counter_3 bcounter(
"early withdrawals / late withdrawals / successes [Exuder]");
#endif
for( int i = 0; i < 4; ++i )
{
bool could_lock_zone;
do
{
could_lock_zone = true;
if (m_sliver_exuder.erase_counter(m_cell_handle) != m_erase_counter)
break;
if (!m_c3t3.triangulation().try_lock_cell(m_cell_handle))
{
#ifdef CGAL_CONCURRENT_MESH_3_PROFILING
bcounter.increment_branch_2(); // THIS is an early withdrawal!
#endif
could_lock_zone = false;
m_sliver_exuder.unlock_all_elements();
continue;
}
if (m_sliver_exuder.erase_counter(m_cell_handle) != m_erase_counter)
{
m_sliver_exuder.unlock_all_elements();
break;
}
// pump_vertices_on_surfaces is a boolean template parameter. The
// following condition is pruned at compiled time, if
// pump_vertices_on_surfaces==false.
if (pump_vertices_on_surfaces
|| m_c3t3.in_dimension(m_cell_handle->vertex(i)) > 2)
{
m_sliver_exuder.template pump_vertex<pump_vertices_on_surfaces>(
m_cell_handle->vertex(i), &could_lock_zone);
#ifdef CGAL_CONCURRENT_MESH_3_PROFILING
if (!could_lock_zone)
bcounter.increment_branch_1(); // THIS is a late withdrawal!
else
++bcounter; // Success!
#endif
}
m_sliver_exuder.unlock_all_elements();
} while (!could_lock_zone);
}
if ( m_sliver_exuder.is_time_limit_reached() )
tbb::task::self().cancel_group_execution();
}
};
#endif
// -----------------------------------
// Private data
// -----------------------------------
C3T3& c3t3_;
Tr& tr_;
const FT sq_delta_;
int num_of_pumped_vertices_;
int num_of_ignored_vertices_;
int num_of_treated_vertices_;
bool initialized_;
SliverCriteria sliver_criteria_;
// Timer
double time_limit_;
CGAL::Real_timer running_time_;
#ifdef CGAL_MESH_3_DEBUG_SLIVERS_EXUDER
// -----------------------------------
// Debug Helpers
// -----------------------------------
private:
/**
* Verifies that two 'FT' are near equal
*/
static bool near_equal(const FT d1, const FT d2)
{
const FT epsilon = 1e-8;
return ( ((d1-d2) >= -1*epsilon) && ((d1-d2) <= epsilon) );
}
/**
* Prints a value
*/
static void print_FT(const FT d)
{
std::cerr << d << " ; ";
}
/** This function verifies that the pre_star contains exactly the set of
facets given by the sequence [begin, end[.
If v!=0, it also fills another Pre_star object, from the sequence [begin,
end[, and checks that is in the same order as pre_star.
*/
template <class Input_facet_it>
bool check_pre_star(const Pre_star& pre_star,
Input_facet_it begin,
Input_facet_it end,
const Vertex_handle v = Vertex_handle()) const;
/** This function verifies that the pre_star contains exactly the set of
facets on the boundary of the conflict zone of the weighted point wp.
The vertex handle vh is a hint for the location of wp.
It also fills another Pre_star object, and checks that is in the same
order as pre_star.
*/
bool check_pre_star(const Pre_star& pre_star,
const Weighted_point& wp,
const Vertex_handle& vh) const;
/**
* Checks if the sliver criterion values from \c criterion_values are the same as
* those that will be found if wp is inserted in the triangulation
*/
bool check_ratios(const Sliver_values& criterion_values,
const Weighted_point& wp,
const Vertex_handle& vh) const;
#endif // CGAL_MESH_3_DEBUG_SLIVERS_EXUDER
}; // end class Slivers_exuder
template <typename C3T3, typename SC, typename V_>
Slivers_exuder<C3T3,SC,V_>::
Slivers_exuder(C3T3& c3t3, const SC& criteria, const FT d)
: Base(c3t3.bbox(),
Concurrent_mesher_config::get().locking_grid_num_cells_per_axis)
, c3t3_(c3t3)
, tr_(c3t3_.triangulation())
, sq_delta_(d*d)
, num_of_pumped_vertices_(0)
, num_of_ignored_vertices_(0)
, num_of_treated_vertices_(0)
, initialized_(false)
, sliver_criteria_(criteria)
, time_limit_(-1)
, running_time_()
{
// If we're multi-thread
tr_.set_lock_data_structure(get_lock_data_structure());
}
template <typename C3T3, typename SC, typename V_>
template <bool pump_vertices_on_surfaces>
Mesh_optimization_return_code
Slivers_exuder<C3T3,SC,V_>::
pump_vertices(FT sliver_criterion_limit,
Visitor& visitor)
{
#ifdef CGAL_MESH_3_PROFILING
WallClockTimer t;
#endif
init(sliver_criterion_limit);
#ifdef CGAL_MESH_3_PROFILING
std::cerr << std::endl << "==== Init time: "
<< t.elapsed() << "s ====" << std::endl;
#endif
#ifdef CGAL_MESH_3_EXUDER_VERBOSE
std::cerr << "Exuding...\n";
std::cerr << "Legend of the following line: "
<< "(#cells left,#vertices pumped,#vertices ignored)" << std::endl;
std::cerr << "(" << this->cells_queue_size() << ",0,0)";
#endif // CGAL_MESH_3_EXUDER_VERBOSE
running_time_.reset();
running_time_.start();
#ifdef CGAL_MESH_3_PROFILING
t.reset();
#endif
#if defined( CGAL_LINKED_WITH_TBB ) && ( !defined (BOOST_MSVC) || !defined( _DEBUG ) || !defined (CGAL_TEST_SUITE) )
// Parallel
if (boost::is_convertible<Concurrency_tag, Parallel_tag>::value)
{
this->create_root_task();
while (!this->cells_queue_empty())
{
Queue_value_type front = *(this->cells_queue_front());
this->cells_queue_pop_front();
Cell_handle c = this->extract_cell_handle_from_queue_value(front);
FT q = this->extract_cell_quality_from_queue_value(front);
unsigned int ec = this->extract_erase_counter_from_queue_value(front);
// Low quality first (i.e. low value of q)
enqueue_task<pump_vertices_on_surfaces>(c, ec, q);
}
this->wait_for_all();
# if defined(CGAL_MESH_3_EXUDER_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_EXUDER_VERBOSE) || defined(CGAL_MESH_3_PROFILING)
std::cerr << ".";
# endif
}
this->destroy_root_task();
}
// Sequential
else
#endif // CGAL_LINKED_WITH_TBB
{
while( !this->cells_queue_empty() && !is_time_limit_reached() )
{
Queue_value_type front = *(this->cells_queue_front());
Cell_handle c = this->extract_cell_handle_from_queue_value(front);
// Low quality first (i.e. low value of cell quality)
bool vertex_pumped = false;
for( int i = 0; i < 4; ++i )
{
// pump_vertices_on_surfaces is a boolean template parameter. The
// following condition is pruned at compiled time, if
// pump_vertices_on_surfaces==false.
if( pump_vertices_on_surfaces || c3t3_.in_dimension(c->vertex(i)) > 2 )
{
if( pump_vertex<pump_vertices_on_surfaces>(c->vertex(i)) )
{
vertex_pumped = true;
++num_of_pumped_vertices_;
break;
}
else
++num_of_ignored_vertices_;
++num_of_treated_vertices_;
}
}
// if the tet could not be deleted
if ( ! vertex_pumped )
this->cells_queue_pop_front();
visitor.after_cell_pumped(this->cells_queue_size());
#ifdef CGAL_MESH_3_EXUDER_VERBOSE
std::cerr << boost::format("\r \r"
"(%1%,%2%,%3%) (%|4$.1f| vertices/s)")
% this->cells_queue_size()
% num_of_pumped_vertices_
% num_of_ignored_vertices_
% (num_of_treated_vertices_ / running_time_.time());
#endif // CGAL_MESH_3_EXUDER_VERBOSE
}
}
running_time_.stop();
#ifdef CGAL_MESH_3_PROFILING
std::cerr << std::endl << "==== Iterations time: "
<< t.elapsed() << "s ====" << std::endl;
#endif
#ifdef CGAL_MESH_3_EXUDER_VERBOSE
std::cerr << std::endl;
std::cerr << "Total exuding time: " << running_time_.time() << "s";
std::cerr << std::endl;
#endif // CGAL_MESH_3_EXUDER_VERBOSE
if ( is_time_limit_reached() ) {
#ifdef CGAL_MESH_3_EXUDER_VERBOSE
std::cerr << "Exuding return code: TIME_LIMIT_REACHED\n\n";
#endif // CGAL_MESH_3_EXUDER_VERBOSE
return TIME_LIMIT_REACHED;
}
if ( check_sliver_bound() ) {
#ifdef CGAL_MESH_3_EXUDER_VERBOSE
std::cerr << "Exuding return code: BOUND_REACHED\n\n";
#endif // CGAL_MESH_3_EXUDER_VERBOSE
return BOUND_REACHED;
}
#ifdef CGAL_MESH_3_EXUDER_VERBOSE
std::cerr << "Exuding return code: CANT_IMPROVE_ANYMORE\n\n";
#endif // CGAL_MESH_3_EXUDER_VERBOSE
return CANT_IMPROVE_ANYMORE;
} // end function pump_vertices
template <typename C3T3, typename SC, typename V_>
template <bool pump_vertices_on_surfaces>
bool
Slivers_exuder<C3T3,SC,V_>::
pump_vertex(const Vertex_handle& pumped_vertex,
bool *could_lock_zone)
{
// Get best_weight
FT best_weight = get_best_weight(pumped_vertex, could_lock_zone);
if (could_lock_zone && *could_lock_zone == false)
return false;
typename Gt::Compare_weighted_squared_radius_3 compare_sq_radius =
tr_.geom_traits().compare_weighted_squared_radius_3_object();
// If best_weight <= pumped_vertex weight, nothing to do
const Weighted_point& pumped_vertex_wp = tr_.point(pumped_vertex);
if ( compare_sq_radius(pumped_vertex_wp, - best_weight) == CGAL::LARGER ) // best_weight > v's weight
{
typename Gt::Construct_point_3 cp = tr_.geom_traits().construct_point_3_object();
const Weighted_point& pwp = tr_.point(pumped_vertex);
Weighted_point wp(cp(pwp), best_weight);
// Insert weighted point into mesh
// note it can fail if the mesh is non-manifold at pumped_vertex
return update_mesh<pump_vertices_on_surfaces>(wp,
pumped_vertex,
could_lock_zone);
}
return false;
}
template <typename C3T3, typename SC, typename V_>
void
Slivers_exuder<C3T3,SC,V_>::
initialize_prestar_and_criterion_values(const Vertex_handle& v,
const Cell_vector& incident_cells,
Pre_star& pre_star,
Sliver_values& criterion_values) const
{
for ( typename Cell_vector::const_iterator cit = incident_cells.begin() ;
cit != incident_cells.end() ;
++cit )
{
const Cell_handle c = *cit;
const int index = c->index(v);
const Facet f = Facet(c, index);
const Facet opposite_facet = tr_.mirror_facet(f);
// Sliver criterion values initialization
if( c3t3_.is_in_complex(c) )
{
criterion_values[f] = sliver_criteria_(c);
}
// Pre_star initialization
// If facet is adjacent to an infinite cell, no need to put it in prestar
// (infinite power distance radius)
if ( tr_.is_infinite(opposite_facet.first) )
continue;
// Insert facet in prestar (even if it is not in complex)
const Cell_handle opposite_cell = opposite_facet.first;
const int index_in_opposite = opposite_cell->index(c);
FT power_distance_to_power_sphere =
tr_.compute_power_distance_to_power_sphere(opposite_facet.first, index_in_opposite);
pre_star.insert(f, power_distance_to_power_sphere);
}
}
template <typename C3T3, typename SC, typename V_>
bool
Slivers_exuder<C3T3,SC,V_>::
expand_prestar(const Cell_handle& cell_to_add,
const Vertex_handle& pumped_vertex,
Pre_star& pre_star,
Sliver_values& criterion_values) const
{
typename Gt::Compute_weight_3 cw = tr_.geom_traits().compute_weight_3_object();
typename Gt::Construct_point_3 cp = tr_.geom_traits().construct_point_3_object();
// Delete first facet of pre_star
Facet start_facet = pre_star.front()->second;
CGAL_assertion(tr_.mirror_facet(start_facet).first == cell_to_add);
#ifdef CGAL_MESH_3_DEBUG_SLIVERS_EXUDER
FT power_distance_to_power_sphere = pre_star.front()->first;
#endif
pre_star.pop_front();
if ( c3t3_.is_in_complex(cell_to_add) )
{
criterion_values.erase(start_facet);
}
int start_mirror_facet_index = tr_.mirror_facet(start_facet).second;
// For each facet of cell_to_add
for(int i = 0; i<4 ; ++i)
{
// We have already treated start_facet
if ( i == start_mirror_facet_index )
continue;
const Facet current_facet(cell_to_add, i);
const Facet current_mirror_facet(tr_.mirror_facet(current_facet));
FT new_power_distance_to_power_sphere = std::numeric_limits<FT>::infinity();
// If current_facet_mirror is in prestar, delete it
// (it may happen that pre_star contains two facets of the same cell)
if ( pre_star.erase(current_mirror_facet) )
{
// If it is a boundary facet, stop pre_star expansion
if ( c3t3_.is_in_complex(current_mirror_facet) )
{
return false;
}
// Update criterion_values
if ( c3t3_.is_in_complex(cell_to_add) )
{
criterion_values.erase(current_mirror_facet);
}
}
// If current_mirror_facet is not in prestar:
// expand prestar & update criterion_values
else
{
const Cell_handle& current_mirror_cell = current_mirror_facet.first;
CGAL_assertion(current_mirror_cell != start_facet.first);
CGAL_assertion(pumped_vertex != current_mirror_facet.first->vertex(0));
CGAL_assertion(pumped_vertex != current_mirror_facet.first->vertex(1));
CGAL_assertion(pumped_vertex != current_mirror_facet.first->vertex(2));
CGAL_assertion(pumped_vertex != current_mirror_facet.first->vertex(3));
// Update pre_star (we do not insert facets with infinite power distance)
// We do insert facet of cells which are outside the complex (we just
// don't use their sliver criterion value to get best weight)
if ( ! tr_.is_infinite(current_mirror_cell) )
{
new_power_distance_to_power_sphere =
tr_.compute_power_distance_to_power_sphere(current_mirror_cell, pumped_vertex);
pre_star.insert(current_facet, new_power_distance_to_power_sphere);
#ifdef CGAL_MESH_3_DEBUG_SLIVERS_EXUDER
if ( new_power_distance_to_power_sphere < power_distance_to_power_sphere )
std::cerr << "new power distance:" << new_power_distance_to_power_sphere
<< " / current power distance:" << power_distance_to_power_sphere
<< std::endl;
#endif // CGAL_MESH_3_DEBUG_SLIVERS_EXUDER
}
// Update ratio (ratio is needed for cells of complex only)
if ( c3t3_.is_in_complex(cell_to_add) )
{
const Weighted_point& pwp = tr_.point(pumped_vertex);
// Adding the power distance to the pumped vertex's weight is a way
// to artificially make the cell 'cell_to_add' be in conflict
// with the pumped vertex. This is done for periodic triangulations,
// where the function 'tetrahedron(Facet, Weighted_point)' requires
// the cell of the facet to be in conflict with the point to determine
// the correct offset of the weighted point (to get a correct tetrahedron).
FT new_weight = cw(pwp);
Facet curr_f;
if(! tr_.is_infinite(current_mirror_cell))
{
// if current_mirror_cell is finite, we can re-use the value
// 'new_power_distance_to_power_sphere'
// Ensure that 'new_power_distance_to_power_sphere' has been initialized
CGAL_assertion(new_power_distance_to_power_sphere != std::numeric_limits<FT>::infinity());
new_weight += new_power_distance_to_power_sphere;
curr_f = current_mirror_facet;
}
else // tr_.is_infinite(current_mirror_cell)
{
// We can't use 'new_power_distance_to_power_sphere' because 'current_mirror_cell'
// is infinite. So we compute the power distance to 'cell_to_add' instead.
CGAL_assertion(!tr_.is_infinite(cell_to_add));
new_weight += tr_.compute_power_distance_to_power_sphere(cell_to_add, pumped_vertex);
curr_f = current_facet;
}
// With 'new_weight' only, the point is only orthogonal to the power sphere,
// so we add a little bit more weight to make sure that 'pwp' is in conflict.
new_weight += 1e5 * std::numeric_limits<FT>::epsilon(); // 'epsilon' alone is not enough
Weighted_point ncr_pwp(cp(pwp), new_weight);
// The 'tetrahedron' function returns positions for which the cell is in conflict
// with the point, which is not trivial in a periodic setting.
Tetrahedron_3 tet = tr_.tetrahedron(curr_f, ncr_pwp);
FT new_value = sliver_criteria_(tet);
criterion_values.insert(std::make_pair(current_facet, new_value));
}
}
}
return true;
}
template <typename C3T3, typename SC, typename V_>
typename Slivers_exuder<C3T3,SC,V_>::FT
Slivers_exuder<C3T3,SC,V_>::
get_best_weight(const Vertex_handle& v, bool *could_lock_zone) const
{
// incident_cells will be used both in 'initialize_prestar_and_criterion_values'
// and to get the smallest distance between 'v' and a neighbor.
Cell_vector incident_cells;
incident_cells.reserve(64);
// Parallel
if (could_lock_zone)
{
if (!tr_.try_lock_and_get_incident_cells(v, incident_cells))
{
this->unlock_all_elements();
*could_lock_zone = false;
return 0.;
}
}
// Sequential
else
{
tr_.incident_cells(v, std::back_inserter(incident_cells));
}
// Get pre_star and criterion_values
Pre_star pre_star;
Sliver_values criterion_values;
initialize_prestar_and_criterion_values(v, incident_cells, pre_star, criterion_values);
#ifdef CGAL_MESH_3_DEBUG_SLIVERS_EXUDER
Pre_star pre_star_copy;
Sliver_values ratios_copy;
#endif
FT worst_criterion_value = get_min_value(criterion_values);
FT best_weight = 0;
FT sq_d_v = Tr_helpers().template get_sq_distance_to_closest_vertex
<CGAL_NTS internal::Has_member_visited<typename Tr::Vertex> >(
c3t3_.triangulation(), v, incident_cells);
// If that boolean is set to false, it means that a facet in the complex
// is about to be flipped. In that case, the pumping is stopped.
bool can_flip = true;
// Main loop: find the weight which maximizes the minimum value of ratio
while( can_flip
&& ! pre_star.empty()
&& pre_star.front()->first < (sq_delta_ * sq_d_v)
&& ! c3t3_.is_in_complex(pre_star.front()->second) )
{
// Store critial radius (pre_star will be modified in expand_prestar)
FT power_distance_to_power_sphere = pre_star.front()->first;
// expand prestar (insert opposite_cell facets in pre_star)
Facet link = pre_star.front()->second;
const Cell_handle& opposite_cell = tr_.mirror_facet(link).first;
if (could_lock_zone && !tr_.try_lock_cell(opposite_cell))
{
*could_lock_zone = false;
return 0.;
}
can_flip = expand_prestar(opposite_cell, v, pre_star, criterion_values);
// Update best_weight if needed
if(can_flip)
{
FT min_of_pre_star = get_min_value(criterion_values);
if( min_of_pre_star > worst_criterion_value )
{
// Update worst_criterion_value
worst_criterion_value = min_of_pre_star;
// Update best_weight
CGAL_assertion(!pre_star.empty());
FT next_r = pre_star.front()->first;
best_weight = (power_distance_to_power_sphere + next_r) / 2;
#ifdef CGAL_MESH_3_DEBUG_SLIVERS_EXUDER
pre_star_copy = pre_star;
ratios_copy = criterion_values;
#endif // CGAL_MESH_3_DEBUG_SLIVERS_EXUDER
}
}
} // end while(... can pump...)
#ifdef CGAL_MESH_3_DEBUG_SLIVERS_EXUDER
typename Gt::Compare_weighted_squared_radius_3 compare_sq_radius =
tr_.geom_traits().compare_weighted_squared_radius_3_object();
const Weighted_point& vwp = tr_.point(v);
if ( compare_sq_radius(vwp, - best_weight) == CGAL::LARGER ) // best_weight > v's weight
{
typename Gt::Construct_point_3 cp = tr_.geom_traits().construct_point_3_object();
const Weighted_point& wpv = tr_.point(v);
Weighted_point wp(cp(wpv), best_weight);
check_pre_star(pre_star_copy, wp, v);
check_ratios(ratios_copy, wp, v);
}
#endif // CGAL_MESH_3_DEBUG_SLIVERS_EXUDER
return best_weight;
}
template <typename C3T3, typename SC, typename V_>
boost::optional<typename Slivers_exuder<C3T3,SC,V_>::Umbrella >
Slivers_exuder<C3T3,SC,V_>::
get_umbrella(const Facet_vector& facets, // internal_facets of conflict zone
const Vertex_handle& /* v, no longer used */) const
{
Umbrella umbrella; //std::map<Ordered_edge, Patch_and_counter>
// Insert into umbrella surface_index of facets which are on the surface
typename Facet_vector::const_iterator fit = facets.begin();
for ( ; fit != facets.end() ; ++fit )
{
if ( c3t3_.is_in_complex(*fit) )
{
Facet f = *fit;
Vertex_handle v1 = f.first->vertex((f.second+1)%4);
Vertex_handle v2 = f.first->vertex((f.second+2)%4);
Vertex_handle v3 = f.first->vertex((f.second+3)%4);
order_three_handles(v1, v2, v3);
std::vector<Ordered_edge> edges;
edges.push_back(Ordered_edge(v1, v2));
edges.push_back(Ordered_edge(v2, v3));
edges.push_back(Ordered_edge(v1, v3));
for(std::size_t i = 0; i < 3; ++i)
{
Ordered_edge oe = edges[i];
typename Umbrella::iterator uit = umbrella.find(oe);
if(uit == umbrella.end()) //umbrella does not contain oe yet
{
umbrella.insert(std::make_pair(oe,
std::make_pair(c3t3_.surface_patch_index(f), 1)));
}
else //umbrella already contains oe. Increment counter or return
{
std::size_t count = (*uit).second.second;
if(count == 2) //there will be more than 3 after insertion
return boost::none; //non-manifold configuration
umbrella.insert(uit,
std::make_pair(oe,
std::make_pair(c3t3_.surface_patch_index(f), count + 1)));
}
}
}
}
// Erase edges that have been counted twice.
// Each Oriented_edge should appear only once.
// Twice means that it belongs to two internal facets that are restricted.
// Three or more corresponds to a non-manifold geometry.
typename Umbrella::iterator uit = umbrella.begin();
while(uit != umbrella.end())
{
if((*uit).second.second == 2)
{
typename Umbrella::iterator to_be_erased = uit++;
umbrella.erase(to_be_erased);
}
else
{
++uit;
}
}
return umbrella;
}
template <typename C3T3, typename SC, typename V_>
template <bool pump_vertices_on_surfaces>
void
Slivers_exuder<C3T3,SC,V_>::
restore_cells_and_boundary_facets(
const Boundary_facets_from_outside& boundary_facets_from_outside,
const Vertex_handle& new_vertex)
{
Cell_vector new_cells;
new_cells.reserve(64);
tr_.incident_cells(new_vertex, std::back_inserter(new_cells));
// Each cell must have a facet on the boundary of the conflict zone
CGAL_assertion(boundary_facets_from_outside.size() == new_cells.size());
// Restore attributes of each cell
for(typename Cell_vector::iterator cit = new_cells.begin();
cit != new_cells.end();
++cit)
{
(*cit)->invalidate_weighted_circumcenter_cache();
const int index = (*cit)->index(new_vertex);
const Facet new_facet = std::make_pair(*cit, index);
const Facet new_facet_from_outside = tr_.mirror_facet(new_facet);
// Search new_facet_from_outside in boundary_facets_from_outside.
// That search cannot fail.
typename Boundary_facets_from_outside::const_iterator it =
boundary_facets_from_outside.find(new_facet_from_outside);
CGAL_assertion(it != boundary_facets_from_outside.end());
// Restore facet attributes
if ( !( it->second.first == Surface_patch_index() ) )
c3t3_.add_to_complex(new_facet, it->second.first);
// Restore cell attributes
if ( !( it->second.second == Subdomain_index() ) )
c3t3_.add_to_complex(*cit, it->second.second);
// if the new cell is in the domain, and it criterion value is less that
// the maximum, push it in the cells queue.
if( c3t3_.is_in_complex(*cit) )
{
FT criterion_value = sliver_criteria_(*cit);
if( criterion_value < sliver_criteria_.sliver_bound() )
add_cell_to_queue<pump_vertices_on_surfaces>(*cit, criterion_value);
}
}
}
template <typename C3T3, typename SC, typename V_>
typename Slivers_exuder<C3T3,SC,V_>::Ordered_edge
Slivers_exuder<C3T3,SC,V_>::get_opposite_ordered_edge(
const Facet& facet,
const Vertex_handle& vertex) const
{
CGAL_assertion(tr_.has_vertex(facet, vertex));
Vertex_handle v1;
Vertex_handle v2;
// Get the two vertex of *fit which are not new_vertex
for ( int i = 0 ; i < 4 ; ++i )
{
const Vertex_handle current_vertex = facet.first->vertex(i);
if ( current_vertex != vertex && tr_.has_vertex(facet, current_vertex) )
{
if ( v1 == Vertex_handle() )
v1 = current_vertex;
else
v2 = current_vertex;
}
}
CGAL_assertion(v1 != Vertex_handle() && v2 != Vertex_handle());
order_two_handles(v1,v2);
return Ordered_edge(v1,v2);
}
template <typename C3T3, typename SC, typename V_>
void
Slivers_exuder<C3T3,SC,V_>::
restore_internal_facets(const Umbrella& umbrella,
const Vertex_handle& new_vertex)
{
Facet_vector new_internal_facets;
new_internal_facets.reserve(64);
tr_.incident_facets(new_vertex, std::back_inserter(new_internal_facets));
// Restore attributes of each facet
for(typename Facet_vector::iterator fit = new_internal_facets.begin();
fit != new_internal_facets.end();
++fit)
{
Ordered_edge edge = get_opposite_ordered_edge(*fit, new_vertex);
// Search edge in umbrella.
// If it is found, restore facet surface index from umbrella
const typename Umbrella::const_iterator um_it = umbrella.find(edge);
if( um_it != umbrella.end() )
{
c3t3_.add_to_complex(*fit, um_it->second.first);
}
}
}
template <typename C3T3, typename SC, typename V_>
template <bool pump_vertices_on_surfaces>
bool
Slivers_exuder<C3T3,SC,V_>::
update_mesh(const Weighted_point& new_point,
const Vertex_handle& old_vertex,
bool *could_lock_zone)
{
CGAL_assertion_code(std::size_t nb_vert = tr_.number_of_vertices();)
Cell_vector deleted_cells;
Facet_vector internal_facets;
Facet_vector boundary_facets;
deleted_cells.reserve(64);
internal_facets.reserve(64);
boundary_facets.reserve(64);
tr_.find_conflicts(new_point,
old_vertex->cell(),
std::back_inserter(boundary_facets),
std::back_inserter(deleted_cells),
std::back_inserter(internal_facets),
could_lock_zone);
if (could_lock_zone && *could_lock_zone == false)
return false;
// Get some datas to restore mesh
Boundary_facets_from_outside boundary_facets_from_outside =
get_boundary_facets_from_outside(boundary_facets);
boost::optional<Umbrella> umbrella = get_umbrella(internal_facets, old_vertex);
if(umbrella == boost::none)
return false; //abort pumping this vertex
// Delete old cells from queue (they aren't in the triangulation anymore)
this->delete_cells_from_queue(deleted_cells);
// Delete old cells & facets from c3t3
remove_from_c3t3(deleted_cells.begin(),deleted_cells.end());
remove_from_c3t3(boundary_facets.begin(),boundary_facets.end());
remove_from_c3t3(internal_facets.begin(),internal_facets.end());
// Insert new point (v will be updated using a wp)
int dimension = c3t3_.in_dimension(old_vertex);
Index vertice_index = c3t3_.index(old_vertex);
Vertex_handle new_vertex = tr_.insert(new_point, old_vertex->cell());
c3t3_.set_dimension(new_vertex,dimension);
c3t3_.set_index(new_vertex,vertice_index);
// Only true for sequential version
CGAL_assertion(could_lock_zone || nb_vert == tr_.number_of_vertices());
// Restore mesh
restore_cells_and_boundary_facets<pump_vertices_on_surfaces>(
boundary_facets_from_outside, new_vertex);
restore_internal_facets(*umbrella, new_vertex);
// Only true for sequential version
CGAL_assertion(could_lock_zone || nb_vert == tr_.number_of_vertices());
return true; // pump was done successfully
}
#if defined( CGAL_LINKED_WITH_TBB ) && ( !defined (BOOST_MSVC) || !defined( _DEBUG ) || !defined (CGAL_TEST_SUITE) )
// For parallel version
template <typename C3T3, typename SC, typename V_>
template <bool pump_vertices_on_surfaces>
void
Slivers_exuder<C3T3,SC,V_>::
enqueue_task(Cell_handle ch, unsigned int erase_counter, FT value)
{
this->enqueue_work(
Pump_vertex<Self, pump_vertices_on_surfaces>(
*this, c3t3_, ch, erase_counter),
value);
}
#endif
#ifdef CGAL_MESH_3_DEBUG_SLIVERS_EXUDER
template <typename Pre_star>
void print_pre_stars(Pre_star pre_star_1, Pre_star pre_star_2)
{
std::cout << "Stars: " << std::endl;
while(!pre_star_1.empty() && !pre_star_2.empty())
{
std::cout << "F: " << &*((pre_star_1.front()->second).first)
<< " " << pre_star_1.front()->second.second
<< " S: " << pre_star_1.front()->first
<< " |||||| "
<< "F: " << &*((pre_star_2.front()->second).first)
<< " " << pre_star_2.front()->second.second
<< " S: " << pre_star_2.front()->first
<< std::endl;
if(pre_star_1.front()->second != pre_star_2.front()->second ||
pre_star_1.front()->first != pre_star_2.front()->first)
std::cout << "Warning!" << std::endl;
pre_star_1.pop_front();
pre_star_2.pop_front();
}
}
template <typename C3T3, typename SC, typename V_>
template <class Input_facet_it>
bool
Slivers_exuder<C3T3,SC,V_>::
check_pre_star(const Pre_star& pre_star,
Input_facet_it begin,
Input_facet_it end,
const Vertex_handle v) const
{
Pre_star pre_star_copy = pre_star;
if(v != Vertex_handle())
{
Pre_star pre_star2;
// fill pre_star2
for(Input_facet_it fit = begin; fit != end; ++fit)
{
const Facet opposite_facet = tr_.mirror_facet(*fit);
if(! tr_.is_infinite(opposite_facet.first) )
{
pre_star2.insert(
*fit, tr_.compute_power_distance_to_power_sphere(opposite_facet.first, v));
}
}
while(!pre_star_copy.empty() && !pre_star2.empty())
{
if(pre_star_copy.front()->first != pre_star2.front()->first) {
std::cerr << "bad order\n";
std::cerr << boost::format("pre_star.front()->first=%1%, should be %2%\n")
% pre_star_copy.front()->first % pre_star2.front()->first;
return false;
}
if ( pre_star_copy.front()->second != pre_star2.front()->second )
{
Facet f1 = pre_star_copy.front()->second;
Facet f2 = pre_star2.front()->second;
pre_star2.pop_front();
pre_star_copy.pop_front();
if (!pre_star_copy.empty() && !pre_star2.empty() &&
pre_star_copy.front()->second == f2 && pre_star2.front()->second == f1 )
{
// It's ok
pre_star2.pop_front();
pre_star_copy.pop_front();
}
else
{
Facet f1 = tr_.mirror_facet(pre_star_copy.front()->second);
Facet f2 = tr_.mirror_facet(pre_star2.front()->second);
std::cerr << "Bad facet:" << f1.second << "/" << f2.second
<< " - " << &*f1.first << "/" << &*f2.first << std::endl;
}
}
else
{
pre_star2.pop_front();
pre_star_copy.pop_front();
}
}
if(pre_star2.empty() && ! pre_star_copy.empty()) {
std::cerr << "pre_star is too big!\n";
while(!pre_star_copy.empty())
{
const Facet f = pre_star_copy.front()->second;
const FT r = pre_star_copy.front()->first;
pre_star_copy.pop_front();
std::cerr << boost::format("extra facet (%1%,%2%) (infinite: %3%, opposite infinite: %4%), power distance: %5%\n")
% &*f.first % f.second % tr_.is_infinite(f.first) % tr_.is_infinite(f.first->neighbor(f.second))
% r;
}
return false;
}
if( pre_star_copy.empty() && ! pre_star2.empty() ) {
std::cerr << "pre_star is too small!\n";
while(!pre_star2.empty())
{
const Facet f = pre_star2.front()->second;
pre_star2.pop_front();
std::cerr << boost::format("missing facet (%1%,%2%) (infinite: %3%, opposite infinite: %4%)\n")
% &*f.first % f.second % tr_.is_infinite(f.first) % tr_.is_infinite(f.first->neighbor(f.second));
}
return false;
}
}
pre_star_copy = pre_star;
for(Input_facet_it fit = begin;
fit != end;
++fit)
{
const Facet opposite_facet = tr_.mirror_facet(*fit);
if(!tr_.is_infinite(opposite_facet.first) && !pre_star_copy.erase(*fit))
return false;
}
if( !pre_star_copy.empty() )
return false;
return true;
}
template <typename C3T3, typename SC, typename V_>
bool
Slivers_exuder<C3T3,SC,V_>::
check_pre_star(const Pre_star& pre_star,
const Weighted_point& wp,
const Vertex_handle& vh) const
{
std::vector<Facet> boundary_facets;
boundary_facets.reserve(64);
tr_.find_conflicts(wp,
vh->cell(),
std::back_inserter(boundary_facets),
CGAL::Emptyset_iterator(),
CGAL::Emptyset_iterator());
const bool result = check_pre_star(pre_star,
boundary_facets.begin(),
boundary_facets.end(),
vh);
if( ! result )
{
std::cerr << "tested wp=" << wp << '\n';
std::cerr << "boundary_facets.size()=" << boundary_facets.size() << std::endl;
typename std::vector<Facet>::const_iterator bfit = boundary_facets.begin(),
bfend = boundary_facets.end();
for(; bfit != bfend; ++bfit)
std::cerr << "Cell: " << &*(bfit->first) << " second: " << bfit->second << '\n';
std::cerr << "\npre_star.size()=" << pre_star.size() << std::endl;
typename Pre_star::const_iterator psit = pre_star.begin(), psend = pre_star.end();
for(; psit != psend; ++psit)
std::cerr << "Cell: " << &*(psit->first.first) << " second: " << psit->first.second << '\n';
std::cerr << std::endl;
}
return result;
}
template <typename C3T3, typename SC, typename V_>
bool
Slivers_exuder<C3T3,SC,V_>::
check_ratios(const Sliver_values& criterion_values,
const Weighted_point& wp,
const Vertex_handle& vh) const
{
Cell_vector deleted_cells;
Facet_vector internal_facets;
Facet_vector boundary_facets;
tr_.find_conflicts(wp,
vh->cell(),
std::back_inserter(boundary_facets),
std::back_inserter(deleted_cells),
std::back_inserter(internal_facets));
bool result = true;
std::vector<FT> expected_ratios;
std::vector<FT> ratio_vector;
for ( typename Sliver_values::const_iterator rit = criterion_values.begin() ;
rit != criterion_values.end() ;
++rit )
{
ratio_vector.push_back(rit->second);
}
for ( typename Facet_vector::const_iterator it = boundary_facets.begin() ;
it != boundary_facets.end() ;
++ it )
{
if ( !c3t3_.is_in_complex((it->first)) )
continue;
Tetrahedron_3 tet = tr_.tetrahedron(*it, wp);
FT ratio = sliver_criteria_(tet);
expected_ratios.push_back(ratio);
bool found = false;
for ( typename Sliver_values::const_iterator rit = criterion_values.begin() ;
rit != criterion_values.end() ;
++rit )
{
if ( near_equal(rit->second,ratio) )
{
found = true;
break;
}
}
if ( ! found )
{
result = false;
}
}
if (expected_ratios.size() != criterion_values.size())
result = false;
if ( !result )
{
std::sort(expected_ratios.begin(),expected_ratios.end());
std::sort(ratio_vector.begin(),ratio_vector.end());
std::vector<FT> diff;
std::set_difference(expected_ratios.begin(),expected_ratios.end(),
ratio_vector.begin(),ratio_vector.end(),
std::back_inserter(diff));
std::cerr << "\nExpected criterion_values (size: "
<< expected_ratios.size() << ") [";
std::for_each(expected_ratios.begin(), expected_ratios.end(), print_FT);
std::cerr << "]\nRatios (size: " << ratio_vector.size() << ") [";
std::for_each(ratio_vector.begin(), ratio_vector.end(), print_FT);
std::cerr << "]\nDiff:[";
std::for_each(diff.begin(),diff.end(), print_FT);
std::cerr << "]\n";
}
return result;
}
#endif // CGAL_MESH_3_DEBUG_SLIVERS_EXUDER
} // end namespace Mesh_3
} // end namespace CGAL
#include <CGAL/enable_warnings.h>
#endif // end CGAL_MESH_3_SLIVERS_EXUDER_H