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

1424 lines
43 KiB
C++

// 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/vertex_perturbation.h $
// $Id: vertex_perturbation.h 315a0b5 2020-01-29T16:27:53+01:00 Laurent Rineau
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s) : Stephane Tayeb
//
//******************************************************************************
// File Description :
//******************************************************************************
#ifndef CGAL_MESH_3_VERTEX_PERTURBATION_H
#define CGAL_MESH_3_VERTEX_PERTURBATION_H
#include <CGAL/license/Mesh_3.h>
#include <CGAL/disable_warnings.h>
#include <CGAL/Mesh_3/config.h>
#include <CGAL/Mesh_3/C3T3_helpers.h>
#include <CGAL/Mesh_3/Triangulation_helpers.h>
#include <CGAL/Time_stamper.h>
#ifdef CGAL_MESH_3_PERTURBER_VERBOSE
#include <CGAL/Timer.h>
#ifdef CGAL_LINKED_WITH_TBB
#include <tbb/enumerable_thread_specific.h>
#include <atomic>
#endif
#endif
#include <boost/optional.hpp>
#include <boost/random/lagged_fibonacci.hpp>
#include <boost/random/uniform_real.hpp>
#include <boost/random/variate_generator.hpp>
#include <string>
#include <ctime>
namespace CGAL {
namespace Mesh_3 {
namespace details
{
/**
* @brief Returns the angle in radian of vectors \c u and \c v
*/
template <typename K>
typename K::FT
angle_in_radian(const typename K::Vector_3& u,
const typename K::Vector_3& v,
K k = K())
{
typedef typename K::FT FT;
typedef typename K::Vector_3 Vector_3;
typename K::Construct_cross_product_vector_3 cross_product =
k.construct_cross_product_vector_3_object();
typename K::Compute_scalar_product_3 scalar_product =
k.compute_scalar_product_3_object();
typename K::Compute_squared_length_3 sq_length =
k.compute_squared_length_3_object();
// -------------------------------------
// Angle between two vectors (in rad)
// uv = |u||v| cos(u,v)
// u^v = w
// |w| = |u||v| |sin(u,v)|
// -------------------------------------
FT product = CGAL::sqrt(sq_length(u) * sq_length(v));
// Check
if ( product == FT(0) )
return FT(0);
// Sine
Vector_3 w = cross_product(u,v);
FT abs_sin = CGAL::sqrt(sq_length(w)) / product;
if ( abs_sin < FT(-1) ) { abs_sin = FT(-1); }
if ( abs_sin > FT(1) ) { abs_sin = FT(1); }
CGAL_assertion(abs_sin >= -1);
CGAL_assertion(abs_sin <= 1);
// We just need cosine sign
FT cosine_sign = scalar_product(u,v);
if ( cosine_sign >= FT(0) )
return FT(std::asin(abs_sin));
else
return FT(CGAL_PI) - FT(std::asin(abs_sin));
}
/**
* @brief Returns the angle in radian of vectors \c u and \c v
*/
template <typename Vector_3>
typename Kernel_traits<Vector_3>::Kernel::FT
angle_in_radian(const Vector_3& u, const Vector_3& v)
{
return angle_in_radian(u,v,typename Kernel_traits<Vector_3>::Kernel());
}
/**
* @brief Returns the squared length of edge \c e
*/
template <typename Tr>
typename Tr::Geom_traits::FT
edge_sq_length(const typename Tr::Edge& e,
const Tr& tr)
{
typedef typename Tr::Geom_traits Gt;
typedef typename Tr::Bare_point Bare_point;
typedef typename Tr::Weighted_point Weighted_point;
typename Gt::Construct_point_3 cp =
tr.geom_traits().construct_point_3_object();
typename Gt::Compute_squared_distance_3 sq_distance =
tr.geom_traits().compute_squared_distance_3_object();
const Weighted_point& wp = tr.point(e.first, e.second);
const Weighted_point& wq = tr.point(e.first, e.third);
const Bare_point& p = cp(wp);
const Bare_point& q = cp(wq);
return sq_distance(p,q);
}
/**
* @brief Returns the minimal incident edge length of \c v
* in triangulation \c tr
*/
template <typename Tr>
typename Tr::Geom_traits::FT
min_incident_edge_sq_length(const typename Tr::Vertex_handle& v,
const Tr& tr)
{
CGAL_precondition(!tr.is_infinite(v));
typedef typename Tr::Edge Edge;
typedef typename Tr::Geom_traits::FT FT;
// Get all incident edges
std::vector<Edge> edges;
tr.finite_incident_edges(v, std::back_inserter(edges));
CGAL_assertion(!edges.empty());
// Get squared min length
typename std::vector<Edge>::iterator eit = edges.begin();
FT min_sq_length = edge_sq_length<Tr>(*eit++, tr);
for ( ; eit != edges.end() ; ++eit )
{
min_sq_length = (std::min)(min_sq_length, edge_sq_length<Tr>(*eit, tr));
}
return min_sq_length;
}
} // end namespace details
/**
* @class Abstract_perturbation
*
* Perturbation interface. It is used by Sliver_perturber class.
*/
template <typename C3T3, typename MeshDomain, typename SliverCriterion>
class Abstract_perturbation
{
protected:
typedef typename C3T3::Vertex_handle Vertex_handle;
typedef typename C3T3::Cell_handle Cell_handle;
typedef typename C3T3::Triangulation::Geom_traits::FT FT;
public:
/**
* @brief constructor
*/
Abstract_perturbation()
: p_next_(nullptr)
, p_previous_(nullptr)
, order_(0)
#ifdef CGAL_MESH_3_PERTURBER_VERBOSE
, counter_(0)
, timer_()
#endif
{
#ifdef CGAL_MESH_3_PERTURBER_VERBOSE
// Initialized here in case it's some std::atomic
total_counter_ = 0;
total_time_ = 0;
#endif
}
/**
* @brief destructor
*
* Note that Abstract_perturbation is not responsible of
* p_next_ nor p_previous_ deletion
*/
virtual ~Abstract_perturbation() {}
/**
* @brief This operator try to move vertex \v using the perturbation.
* @param v the vertex to move
* @param slivers a vector which contains incident slivers of \c v
* @param c3t3 the c3t3
* @param domain the domain
* @param criterion the criterion which is used to evaluate if a cell is
* a sliver.
* @param sliver_bound the bound for the above criterion.
* @param modified_vertices an output vector which contains vertices of c3t3
* which may have been impacted by v relocation.
* @return a pair containing:
* - a bool which is \c true if a move has been done.
* - a Vertex_handle which is always filled and may be the new vertex (if
* the move is a success), or the vertex which lies at \c v's position in
* the new c3t3.
*
* Note that this function is hill_climbing only. The min \c criterion value
* of c3t3 could not decrease.
*/
std::pair<bool,Vertex_handle>
operator()(const Vertex_handle& v,
const std::vector<Cell_handle>& slivers,
C3T3& c3t3,
const MeshDomain& domain,
const SliverCriterion& criterion,
const FT& sliver_bound,
std::vector<Vertex_handle>& modified_vertices,
bool *could_lock_zone = nullptr) const
{
#ifndef CGAL_MESH_3_PERTURBER_VERBOSE
return do_perturb(v, slivers, c3t3, domain, criterion,
sliver_bound, modified_vertices, could_lock_zone);
#else
timer().start();
// Virtual call
std::pair<bool,Vertex_handle> perturb =
do_perturb(v, slivers, c3t3, domain, criterion,
sliver_bound, modified_vertices, could_lock_zone);
if ( perturb.first )
++counter_;
timer().stop();
return perturb;
#endif
}
/**
* @brief Sets next perturbation
*/
void set_next(Abstract_perturbation* next)
{
p_next_ = next;
if ( nullptr != next )
next->p_previous_ = this;
}
/**
* Returns next perturbation
*/
Abstract_perturbation* next() const
{
return p_next_;
}
/**
* Returns previous perturbation
*/
Abstract_perturbation* previous() const
{
return p_previous_;
}
/**
* Returns the order value
*/
int order() const
{
return order_;
}
/**
* Sets the order value
*/
void set_order(const int order)
{
order_ = order;
}
protected:
/**
* Virtual function which must be implemented in children
*/
virtual std::pair<bool,Vertex_handle>
do_perturb(const Vertex_handle& v,
const std::vector<Cell_handle>& slivers,
C3T3& c3t3,
const MeshDomain& domain,
const SliverCriterion& criterion,
const FT& sliver_bound,
std::vector<Vertex_handle>& modified_vertices,
bool *could_lock_zone = nullptr) const = 0;
/**
* @brief a helper function which returns the amplitude of perturbation
*/
FT compute_perturbation_sq_amplitude(const Vertex_handle& v,
const C3T3& c3t3,
const FT& sq_factor) const
{
// We don't care if the shortest edge is inside or outside c3t3
return details::min_incident_edge_sq_length(v,c3t3.triangulation())
* sq_factor;
}
private:
Abstract_perturbation* p_next_;
Abstract_perturbation* p_previous_;
// An int to have an order between Abstract_perturbation
int order_;
#ifdef CGAL_MESH_3_PERTURBER_VERBOSE
public:
void reset_timer() { total_time_+= 1000*time(); timer().reset(); }
void reset_counter() { total_counter_ += counter_; counter_ = 0; }
int counter() const { return counter_; }
double time() const { return timer().time(); }
int total_counter() const { return total_counter_ + counter(); }
std::size_t total_time() const
{ return static_cast<std::size_t>(double(total_time_) + 1000*time()); }
virtual std::string perturbation_name() const = 0;
private:
CGAL::Timer &timer() const
{
#ifdef CGAL_LINKED_WITH_TBB
return timer_.local();
#else
return timer_;
#endif
}
mutable int counter_;
#ifdef CGAL_LINKED_WITH_TBB
mutable tbb::enumerable_thread_specific<CGAL::Timer> timer_;
std::atomic<int> total_counter_;
std::atomic<std::size_t> total_time_;
#else
mutable CGAL::Timer timer_;
int total_counter_;
std::size_t total_time_;
#endif
#endif
};
template <typename C3T3, typename MD, typename SC>
inline
bool
operator<(const Abstract_perturbation<C3T3,MD,SC>& lhs,
const Abstract_perturbation<C3T3,MD,SC>& rhs)
{
return lhs.order() < rhs.order();
}
/**
* @class Gradient_based_perturbation
*
* Base class for gradient based perturbations. The goal of these perturbations
* is to make the sliver flip.
*/
template <typename C3T3, typename MeshDomain, typename SliverCriterion>
class Gradient_based_perturbation
: public Abstract_perturbation<C3T3,MeshDomain,SliverCriterion>
{
protected:
typedef Abstract_perturbation<C3T3, MeshDomain, SliverCriterion> Base;
typedef typename Base::Vertex_handle Vertex_handle;
typedef typename Base::Cell_handle Cell_handle;
typedef typename C3T3::Triangulation Tr;
typedef typename Tr::Geom_traits Gt;
typedef typename Gt::FT FT;
typedef typename Gt::Vector_3 Vector_3;
typedef typename Tr::Bare_point Bare_point;
typedef typename Tr::Weighted_point Weighted_point;
public:
/**
* @brief Constructor
*/
Gradient_based_perturbation(unsigned int max_step_nb,
double step_size)
: max_step_nb_(max_step_nb)
, sq_step_size_(step_size*step_size) { }
/**
* @brief destructor
*/
virtual ~Gradient_based_perturbation() {}
protected:
/**
* Virtual function which must be implemented in children
*/
virtual std::pair<bool,Vertex_handle>
do_perturb(const Vertex_handle& v,
const std::vector<Cell_handle>& slivers,
C3T3& c3t3,
const MeshDomain& domain,
const SliverCriterion& criterion,
const FT& sliver_bound,
std::vector<Vertex_handle>& modified_vertices,
bool *could_lock_zone = nullptr) const = 0;
protected:
// -----------------------------------
// Protected methods
// -----------------------------------
/**
* Tries to apply a gradient perturbation using direction of
* \c gradient_vector
*/
std::pair<bool, Vertex_handle>
apply_perturbation(const Vertex_handle& v,
const Vector_3& gradient_vector,
C3T3& c3t3,
const MeshDomain& domain,
const SliverCriterion& criterion,
std::vector<Vertex_handle>& modified_vertices,
bool *could_lock_zone = nullptr) const
{
typedef Triangulation_helpers<typename C3T3::Triangulation> Th;
const Tr& tr = c3t3.triangulation();
typename Gt::Construct_point_3 cp =
tr.geom_traits().construct_point_3_object();
typename Gt::Construct_weighted_point_3 cwp =
tr.geom_traits().construct_weighted_point_3_object();
typename Gt::Compute_squared_length_3 sq_length =
tr.geom_traits().compute_squared_length_3_object();
typename Gt::Construct_translated_point_3 translate =
tr.geom_traits().construct_translated_point_3_object();
typename Gt::Construct_vector_3 vector =
tr.geom_traits().construct_vector_3_object();
// create a helper
typedef C3T3_helpers<C3T3,MeshDomain> C3T3_helpers;
C3T3_helpers helper(c3t3, domain);
modified_vertices.clear();
// norm depends on the local size of the mesh
FT sq_norm = this->compute_perturbation_sq_amplitude(v, c3t3, sq_step_size_);
FT step_length = CGAL::sqrt(sq_norm / sq_length(gradient_vector));
Vector_3 step_vector = step_length * gradient_vector;
const Weighted_point& weighted_initial_loc = c3t3.triangulation().point(v);
Bare_point initial_loc = cp(weighted_initial_loc);
Bare_point new_loc = translate(initial_loc, step_vector);
Bare_point final_loc = new_loc;
if ( c3t3.in_dimension(v) < 3 )
final_loc = helper.project_on_surface(v, new_loc);
Vector_3 move_vector = vector(initial_loc, final_loc);
unsigned int i = 0;
// Concurrent-safe version
if (could_lock_zone)
{
// as long as no topological change takes place
while(Th().no_topological_change__without_set_point(c3t3.triangulation(),
v, cwp(final_loc)) &&
++i <= max_step_nb_ )
{
new_loc = translate(new_loc, step_vector);
if ( c3t3.in_dimension(v) == 3 )
final_loc = new_loc;
else
final_loc = helper.project_on_surface(v, new_loc);
}
}
else
{
while( Th().no_topological_change(c3t3.triangulation(), v,
move_vector, cwp(final_loc)) &&
++i <= max_step_nb_ )
{
new_loc = translate(new_loc, step_vector);
if ( c3t3.in_dimension(v) == 3 )
final_loc = new_loc;
else
final_loc = helper.project_on_surface(v, new_loc);
move_vector = vector(initial_loc, final_loc);
}
}
// Topology could not change moving this vertex
if ( i > max_step_nb_ ||
Th().inside_protecting_balls(c3t3.triangulation(), v, final_loc) )
return std::make_pair(false, v);
// we know that there will be a combinatorial change
return helper.update_mesh_topo_change(v,
cwp(final_loc),
criterion,
std::back_inserter(modified_vertices),
could_lock_zone);
}
private:
unsigned int max_step_nb_;
double sq_step_size_;
};
/**
* @class Sq_radius_perturbation
*
* Gradient perturbation which tends to maximize cell squared radius.
*/
template <typename C3T3, typename MeshDomain, typename SliverCriterion>
class Sq_radius_perturbation
: public Gradient_based_perturbation<C3T3,MeshDomain,SliverCriterion>
{
protected:
typedef Gradient_based_perturbation<C3T3, MeshDomain, SliverCriterion> Base;
typedef typename C3T3::Triangulation Tr;
typedef typename C3T3::Triangulation Triangulation;
typedef typename Base::Vertex_handle Vertex_handle;
typedef typename Base::Cell_handle Cell_handle;
typedef typename Tr::Geom_traits Gt;
typedef typename Gt::FT FT;
typedef typename Gt::Vector_3 Vector_3;
typedef typename Tr::Bare_point Bare_point;
typedef typename Tr::Weighted_point Weighted_point;
public:
/**
* @brief Constructor
*/
Sq_radius_perturbation(unsigned int max_step_nb,
double step_size)
: Base(max_step_nb,step_size) {}
/**
* @brief destructor
*/
virtual ~Sq_radius_perturbation() {}
#ifdef CGAL_MESH_3_PERTURBER_VERBOSE
virtual std::string perturbation_name() const
{
return std::string("Sq radius gradient perturbation");
}
#endif
protected:
/**
* do_perturb implementation
*/
virtual std::pair<bool,Vertex_handle>
do_perturb(const Vertex_handle& v,
const std::vector<Cell_handle>& slivers,
C3T3& c3t3,
const MeshDomain& domain,
const SliverCriterion& criterion,
const FT&,
std::vector<Vertex_handle>& modified_vertices,
bool *could_lock_zone = nullptr) const
{
CGAL_precondition(!slivers.empty());
Vector_3 grad_vector = compute_gradient_vector(c3t3, v, slivers);
// Exit if grad_vector is not relevant
if ( CGAL::NULL_VECTOR == grad_vector )
return std::make_pair(false,v);
return Base::apply_perturbation(v,
grad_vector,
c3t3,
domain,
criterion,
modified_vertices,
could_lock_zone);
}
private:
// -----------------------------------
// Private methods
// -----------------------------------
/**
* @brief compute the gradient vector
*/
Vector_3
compute_gradient_vector(const C3T3& c3t3,
const Vertex_handle& v,
const std::vector<Cell_handle>& slivers) const
{
switch (slivers.size())
{
case 1:
return compute_gradient_vector(c3t3, slivers.front(), v);
break;
case 2:
{
Vector_3 v1 = compute_gradient_vector(c3t3, slivers.front(), v);
Vector_3 v2 = compute_gradient_vector(c3t3, slivers.back(), v);
if( v1 * v2 > 0 )
// "+0.5" because sq_radius has to go up
return 0.5*(v1 + v2);
break;
}
default:
break;
}
// May happen if sq_radius_gradient is not relevant for this vertex
return CGAL::NULL_VECTOR;
}
/**
* @brief compute the gradient vector
*/
Vector_3 compute_gradient_vector(const C3T3& c3t3,
const Cell_handle& cell,
const Vertex_handle& v) const
{
const Triangulation& tr = c3t3.triangulation();
typename Gt::Construct_point_3 cp =
c3t3.triangulation().geom_traits().construct_point_3_object();
typename Gt::Construct_translated_point_3 translate =
c3t3.triangulation().geom_traits().construct_translated_point_3_object();
unsigned int index = cell->index(v);
const Weighted_point& wvp = tr.point(cell, index);
const Weighted_point& wp2 = tr.point(cell, (index+1)&3);
const Weighted_point& wp3 = tr.point(cell, (index+2)&3);
const Weighted_point& wp4 = tr.point(cell, (index+3)&3);
// translate the tet so that 'wp4' is the origin
Vector_3 translate_to_origin(CGAL::ORIGIN, cp(wp4));
const Bare_point& p1 = translate(cp(wvp), - translate_to_origin);
const Bare_point& p2 = translate(cp(wp2), - translate_to_origin);
const Bare_point& p3 = translate(cp(wp3), - translate_to_origin);
// pre-compute everything
FT sq_p1 = p1.x()*p1.x() + p1.y()*p1.y() + p1.z()*p1.z();
FT sq_p2 = p2.x()*p2.x() + p2.y()*p2.y() + p2.z()*p2.z();
FT sq_p3 = p3.x()*p3.x() + p3.y()*p3.y() + p3.z()*p3.z();
// every derivative is computed w.r.t p1 (x1, y1, z1)
FT da_dx = p2.y()*p3.z() - p3.y()*p2.z();
FT da_dy = p2.z()*p3.x() - p2.x()*p3.z();
FT da_dz = p2.x()*p3.y() - p3.x()*p2.y();
FT dDx_dx = -2*p1.x()*da_dx;
FT dDx_dy = -2*p1.y()*da_dx + sq_p2*p3.z() - sq_p3*p2.z();
FT dDx_dz = -2*p1.z()*da_dx - sq_p2*p3.y() + sq_p3*p2.y();
FT dDy_dx = -2*p1.x()*da_dy - sq_p2*p3.z() + sq_p3*p2.z();
FT dDy_dy = -2*p1.y()*da_dy;
FT dDy_dz = -2*p1.z()*da_dy + sq_p2*p3.x() - sq_p3*p2.x();
FT dDz_dx = -2*p1.x()*da_dz + sq_p2*p3.y() - sq_p3*p2.y();
FT dDz_dy = -2*p1.y()*da_dz - sq_p2*p3.x() + sq_p3*p2.x();
FT dDz_dz = -2*p1.z()*da_dz;
FT a = p1.x()*da_dx + p1.y()*da_dy + p1.z()*da_dz;
if ( CGAL_NTS is_zero(a) )
return CGAL::NULL_VECTOR;
FT Dx = -sq_p1*da_dx + p1.y()*(sq_p2*p3.z() - sq_p3*p2.z()) - p1.z()*(sq_p2*p3.y() - sq_p3*p2.y());
FT Dy = -sq_p1*da_dy - p1.x()*(sq_p2*p3.z() - sq_p3*p2.z()) + p1.z()*(sq_p2*p3.x() - sq_p3*p2.x());
FT Dz = -sq_p1*da_dz + p1.x()*(sq_p2*p3.y() - sq_p3*p2.y()) - p1.y()*(sq_p2*p3.x() - sq_p3*p2.x());
// compute gradient vector
FT sum_sqD = Dx*Dx + Dy*Dy + Dz*Dz;
FT gx = (Dx*dDx_dx + Dy*dDy_dx + Dz*dDz_dx) / (2.0*a*a) - (da_dx * sum_sqD) / (2.0*a*a*a);
FT gy = (Dx*dDx_dy + Dy*dDy_dy + Dz*dDz_dy) / (2.0*a*a) - (da_dy * sum_sqD) / (2.0*a*a*a);
FT gz = (Dx*dDx_dz + Dy*dDy_dz + Dz*dDz_dz) / (2.0*a*a) - (da_dz * sum_sqD) / (2.0*a*a*a);
return Vector_3(gx, gy, gz);
}
};
/**
* @class Volume_perturbation
*
* Gradient perturbation which tends to decrease cell volume.
*/
template <typename C3T3, typename MeshDomain, typename SliverCriterion>
class Volume_perturbation
: public Gradient_based_perturbation<C3T3,MeshDomain,SliverCriterion>
{
protected:
typedef Gradient_based_perturbation<C3T3, MeshDomain, SliverCriterion> Base;
typedef typename Base::Vertex_handle Vertex_handle;
typedef typename Base::Cell_handle Cell_handle;
typedef typename C3T3::Triangulation Tr;
typedef typename Tr::Geom_traits Gt;
typedef typename Gt::FT FT;
typedef typename Gt::Vector_3 Vector_3;
typedef typename Tr::Bare_point Bare_point;
typedef typename Tr::Weighted_point Weighted_point;
public:
/**
* @brief Constructor
*/
Volume_perturbation(unsigned int max_step_nb,
double step_size)
: Base(max_step_nb,step_size) {}
/**
* @brief Destructor
*/
virtual ~Volume_perturbation() {}
#ifdef CGAL_MESH_3_PERTURBER_VERBOSE
virtual std::string perturbation_name() const
{
return std::string("Volume gradient perturbation");
}
#endif
protected:
/**
* @brief do_perturb implementation
*/
virtual std::pair<bool,Vertex_handle>
do_perturb(const Vertex_handle& v,
const std::vector<Cell_handle>& slivers,
C3T3& c3t3,
const MeshDomain& domain,
const SliverCriterion& criterion,
const FT&,
std::vector<Vertex_handle>& modified_vertices,
bool *could_lock_zone = nullptr) const
{
CGAL_precondition(!slivers.empty());
Vector_3 grad_vector = compute_gradient_vector(c3t3, v, slivers);
// Exit if grad_vector is not relevant
if ( CGAL::NULL_VECTOR == grad_vector )
return std::make_pair(false,v);
return Base::apply_perturbation(v,
grad_vector,
c3t3,
domain,
criterion,
modified_vertices,
could_lock_zone);
}
private:
// -----------------------------------
// Private methods
// -----------------------------------
/**
* @brief compute the gradient vector
*/
Vector_3
compute_gradient_vector(const C3T3& c3t3,
const Vertex_handle& v,
const std::vector<Cell_handle>& slivers) const
{
switch (slivers.size())
{
case 1:
return -1*compute_gradient_vector(c3t3, slivers.front(), v);
break;
case 2:
{
Vector_3 v1 = compute_gradient_vector(c3t3, slivers.front(), v);
Vector_3 v2 = compute_gradient_vector(c3t3, slivers.back(), v);
if( v1 * v2 > 0 )
// "-0.5" because volume has to go down
return -0.5 * (v1 + v2);
break;
}
default:
break;
}
// May happen if volume_gradient is not relevant for this vertex
return CGAL::NULL_VECTOR;
}
/**
* @brief compute the gradient vector
*/
Vector_3 compute_gradient_vector(const C3T3& c3t3,
const Cell_handle& cell,
const Vertex_handle& v) const
{
CGAL_precondition(cell->has_vertex(v));
const typename C3T3::Triangulation& tr = c3t3.triangulation();
typename Gt::Construct_point_3 cp = tr.geom_traits().construct_point_3_object();
const int i = cell->index(v);
// fixed vertices: (the ones with index != i)
int k1 = (i+1)&3;
int k2 = (i+2)&3;
int k3 = (i+3)&3;
if ( (i&1) == 0 )
std::swap(k1,k3);
const Weighted_point& wp1 = tr.point(cell, k1);
const Weighted_point& wp2 = tr.point(cell, k2);
const Weighted_point& wp3 = tr.point(cell, k3);
const Bare_point& p1 = cp(wp1);
const Bare_point& p2 = cp(wp2);
const Bare_point& p3 = cp(wp3);
FT gx = p2.y()*p3.z() + p1.y()*(p2.z()-p3.z())
- p3.y()*p2.z() - p1.z()*(p2.y()-p3.y());
FT gy = -p2.x()*p3.z() - p1.x()*(p2.z()-p3.z())
+ p3.x()*p2.z() + p1.z()*(p2.x()-p3.x());
FT gz = p2.x()*p3.y() + p1.x()*(p2.y()-p3.y())
- p3.x()*p2.y() - p1.y()*(p2.x()-p3.x());
return (1.0 / 6.0 * Vector_3(gx, gy, gz));
}
};
/**
* @class Dihedral_angle_perturbation
*
* Gradient perturbation which tends to decrease cell minimal dihedral angle.
*/
template <typename C3T3, typename MeshDomain, typename SliverCriterion>
class Dihedral_angle_perturbation
: public Gradient_based_perturbation<C3T3,MeshDomain,SliverCriterion>
{
protected:
typedef Gradient_based_perturbation<C3T3, MeshDomain, SliverCriterion> Base;
typedef typename Base::Vertex_handle Vertex_handle;
typedef typename Base::Cell_handle Cell_handle;
typedef typename C3T3::Triangulation Tr;
typedef typename Tr::Geom_traits Gt;
typedef typename Gt::FT FT;
typedef typename Gt::Vector_3 Vector_3;
typedef typename Tr::Bare_point Bare_point;
typedef typename Tr::Weighted_point Weighted_point;
public:
/**
* @brief constructor
*/
Dihedral_angle_perturbation(unsigned int max_step_nb,
double step_size)
: Base(max_step_nb,step_size) {}
/**
* @brief destructor
*/
virtual ~Dihedral_angle_perturbation() {}
#ifdef CGAL_MESH_3_PERTURBER_VERBOSE
virtual std::string perturbation_name() const
{
return std::string("Dihedral angle gradient perturbation");
}
#endif
protected:
/**
* @brief do_perturb implementation
*/
virtual std::pair<bool,Vertex_handle>
do_perturb(const Vertex_handle& v,
const std::vector<Cell_handle>& slivers,
C3T3& c3t3,
const MeshDomain& domain,
const SliverCriterion& criterion,
const FT&,
std::vector<Vertex_handle>& modified_vertices,
bool *could_lock_zone = nullptr) const
{
CGAL_precondition(!slivers.empty());
Vector_3 grad_vector = compute_gradient_vector(c3t3, v, slivers);
// Exit if grad_vector is not relevant
if ( CGAL::NULL_VECTOR == grad_vector )
return std::make_pair(false,v);
return Base::apply_perturbation(v,
grad_vector,
c3t3,
domain,
criterion,
modified_vertices,
could_lock_zone);
}
private:
// -----------------------------------
// Private methods
// -----------------------------------
/**
* @brief compute the gradient vector
*/
Vector_3
compute_gradient_vector(const C3T3& c3t3,
const Vertex_handle& v,
const std::vector<Cell_handle>& slivers) const
{
switch (slivers.size())
{
case 1:
return -1*compute_gradient_vector(c3t3, slivers.front(), v);
break;
case 2:
{
Vector_3 v1 = compute_gradient_vector(c3t3, slivers.front(), v);
Vector_3 v2 = compute_gradient_vector(c3t3, slivers.back(), v);
if( v1 * v2 > 0 )
// "-0.5" because angle has to go down
return -0.5*(v1 + v2);
break;
}
default:
break;
}
// May happen if dihedral gradient is not relevant for this vertex
return CGAL::NULL_VECTOR;
}
/**
* @brief compute the gradient vector
*/
Vector_3
compute_gradient_vector(const C3T3& c3t3,
const Cell_handle& cell,
const Vertex_handle& v) const
{
CGAL_assertion(cell->has_vertex(v));
const typename C3T3::Triangulation& tr = c3t3.triangulation();
typename Gt::Construct_point_3 cp =
tr.geom_traits().construct_point_3_object();
typename Gt::Compute_squared_distance_3 sq_distance =
tr.geom_traits().compute_squared_distance_3_object();
const int i = cell->index(v);
const Weighted_point& wp0 = tr.point(cell, i);
const Bare_point& p0 = cp(wp0);
// Other indices
int k1 = (i+1)&3;
int k2 = (i+2)&3;
int k3 = (i+3)&3;
FT angle_k1k2 = abs_dihedral_angle(c3t3, p0, k1, k2, k3, cell);
FT angle_k2k3 = abs_dihedral_angle(c3t3, p0, k2, k3, k1, cell);
FT angle_k3k1 = abs_dihedral_angle(c3t3, p0, k3, k1, k2, cell);
if ( angle_k1k2 > angle_k2k3 )
std::swap(k1,k3);
// Here we know that min_angle_k1k2 <= min_angle_k2k3
if ( angle_k1k2 > angle_k3k1 )
std::swap(k2,k3);
// Here edge k1k2 minimizes dihedral angle
const Weighted_point& wp1 = tr.point(cell, k1);
const Weighted_point& wp2 = tr.point(cell, k2);
const Weighted_point& wp3 = tr.point(cell, k3);
const Bare_point& p1 = cp(wp1);
const Bare_point& p2 = cp(wp2);
const Bare_point& p3 = cp(wp3);
// grad of min dihedral angle (in cell) wrt p0
const Vector_3 p1p0 (p1,p0);
const Vector_3 p1p2 (p1,p2);
const Vector_3 p1p3 (p1,p3);
FT a_02 = CGAL::abs(details::angle_in_radian(p1p0, p1p2));
FT a_03 = CGAL::abs(details::angle_in_radian(p1p0, p1p3));
Vector_3 n0 = normal_estimate(c3t3, cell, k3);
Vector_3 n1 = normal_estimate(c3t3, cell, k2);
const FT d_p0p1 = CGAL::sqrt(sq_distance(p0,p1));
CGAL_assertion(!is_zero(d_p0p1));
return ( (-1./ d_p0p1) * (cotangent(a_02)*n0 + cotangent(a_03)*n1) );
}
/**
* @brief returns the absolute value of dihedral angle of p,p(k1),p(k2),p(k3)
*/
FT abs_dihedral_angle(const C3T3& c3t3,
const Bare_point& p,
const int k1,
const int k2,
const int k3,
const Cell_handle& cell) const
{
const typename C3T3::Triangulation& tr = c3t3.triangulation();
typename Gt::Construct_point_3 cp =
tr.geom_traits().construct_point_3_object();
typename Gt::Compute_approximate_dihedral_angle_3 approx_dihedral_angle =
tr.geom_traits().compute_approximate_dihedral_angle_3_object();
const Weighted_point& wp1 = tr.point(cell, k1);
const Weighted_point& wp2 = tr.point(cell, k2);
const Weighted_point& wp3 = tr.point(cell, k3);
return CGAL::abs(approx_dihedral_angle(cp(p), cp(wp1), cp(wp2), cp(wp3)));
}
/**
* @brief returns the cotangent of \c value
*/
FT cotangent(const FT& value) const
{
return FT(1/std::tan(CGAL::to_double(value)));
}
/**
* @brief returns the normal of facet (ch,i), oriented from inside to outside
* of \c ch
*/
Vector_3 normal_estimate(const C3T3& c3t3, const Cell_handle& ch, const int i) const
{
const typename C3T3::Triangulation& tr = c3t3.triangulation();
typename Gt::Construct_point_3 cp = tr.geom_traits().construct_point_3_object();
int k1 = (i+1)&3;
int k2 = (i+2)&3;
int k3 = (i+3)&3;
// Orient normals to the outside of cell
if ( (i&1) == 1 )
std::swap(k1,k2);
const Weighted_point& wp1 = tr.point(ch, k1);
const Weighted_point& wp2 = tr.point(ch, k2);
const Weighted_point& wp3 = tr.point(ch, k3);
const Bare_point& p1 = cp(wp1);
const Bare_point& p2 = cp(wp2);
const Bare_point& p3 = cp(wp3);
// compute normal and return it
return normal(p1, p2, p3);
}
};
/**
* @class Gradient_based_perturbation
*
* Base class for random based perturbations. The goal of these perturbations
* is to improve the mesh quality (using a flip or not).
*/
template <typename C3T3, typename MeshDomain, typename SliverCriterion>
class Random_based_perturbation
: public Abstract_perturbation<C3T3,MeshDomain,SliverCriterion>
{
protected:
typedef Abstract_perturbation<C3T3, MeshDomain, SliverCriterion> Base;
typedef typename Base::Vertex_handle Vertex_handle;
typedef typename Base::Cell_handle Cell_handle;
typedef typename C3T3::Triangulation Tr;
typedef typename Tr::Geom_traits Gt;
typedef typename Gt::FT FT;
typedef typename Gt::Vector_3 Vector_3;
typedef typename Tr::Bare_point Bare_point;
public:
/**
* @brief constructor
*/
Random_based_perturbation(unsigned int max_try_nb,
const FT& sphere_radius)
: max_try_nb_(max_try_nb)
, sphere_radius_(sphere_radius)
, sphere_sq_radius_(sphere_radius*sphere_radius)
, generator_() // initialize the random generator deterministically
, uni_dist_(0,1)
, random_(generator_, uni_dist_) {}
/**
* @brief destructor
*/
virtual ~Random_based_perturbation() {}
protected:
/**
* Virtual function which must be implemented in children
*/
virtual std::pair<bool,Vertex_handle>
do_perturb(const Vertex_handle& v,
const std::vector<Cell_handle>& slivers,
C3T3& c3t3,
const MeshDomain& domain,
const SliverCriterion& criterion,
const FT& sliver_bound,
std::vector<Vertex_handle>& modified_vertices,
bool *could_lock_zone = nullptr) const = 0;
protected:
// -----------------------------------
// Protected methods (children helpers)
// -----------------------------------
/// Access functions
unsigned int max_try_nb() const { return max_try_nb_; }
FT sphere_radius() const { return sphere_radius_; }
FT sphere_sq_radius() const { return sphere_sq_radius_; }
/**
* @brief returns a FT between \c min and \c max
*/
FT random_ft(const FT& min = FT(0.), const FT& max = FT(1.)) const
{
FT r (random_());
return r*(max-min) + min;
}
/**
* @brief returns a random vector with size \c vector_size
*/
Vector_3 random_vector_fixed_size(const C3T3& c3t3,
const FT& vector_sq_size) const
{
typename Gt::Compute_squared_length_3 sq_length =
c3t3.triangulation().geom_traits().compute_squared_length_3_object();
Vector_3 rnd_vector(random_ft(),random_ft(),random_ft());
FT rnd_sq_size = sq_length(rnd_vector);
if ( ! CGAL_NTS is_zero(rnd_sq_size) )
return CGAL::sqrt(vector_sq_size / rnd_sq_size) * rnd_vector;
else
return CGAL::NULL_VECTOR;
}
/**
* @brief returns a random vector with size between 0 and \c vector_size
*/
Vector_3 random_vector_max_size(const C3T3& c3t3,
const FT& vector_max_sq_size) const
{
return random_ft() * random_vector_fixed_size(c3t3, vector_max_sq_size);
}
/**
* @brief returns a random vector.
*/
Vector_3 random_vector(const C3T3& c3t3,
const FT& vector_sq_size,
bool fixed_size) const
{
if ( fixed_size )
return random_vector_fixed_size(c3t3, vector_sq_size);
else
return random_vector_max_size(c3t3, vector_sq_size);
}
private:
/// The maximum random point which will be tried
unsigned int max_try_nb_;
// The radius of the sphere which will contain random points
FT sphere_radius_;
FT sphere_sq_radius_;
// boost random generator
typedef boost::lagged_fibonacci607 base_generator_type;
base_generator_type generator_;
boost::uniform_real<FT> uni_dist_;
mutable boost::variate_generator<base_generator_type&,
boost::uniform_real<FT> > random_;
};
/**
* @class Li_random_perturbation
*
* Li random based perturbation.
*/
template <typename C3T3, typename MeshDomain, typename SliverCriterion>
class Li_random_perturbation
: public Random_based_perturbation<C3T3,MeshDomain,SliverCriterion>
{
protected:
typedef Random_based_perturbation<C3T3, MeshDomain, SliverCriterion> Base;
typedef typename Base::Vertex_handle Vertex_handle;
typedef typename Base::Cell_handle Cell_handle;
typedef typename C3T3::Triangulation Tr;
typedef typename Tr::Geom_traits Gt;
typedef typename Gt::FT FT;
typedef typename Gt::Vector_3 Vector_3;
typedef typename Tr::Bare_point Bare_point;
typedef typename Tr::Weighted_point Weighted_point;
typedef CGAL::Hash_handles_with_or_without_timestamps Hash_fct;
typedef boost::unordered_set<Vertex_handle, Hash_fct> Vertex_set;
public:
/**
* @brief constructor
*/
Li_random_perturbation(unsigned int max_try_nb,
const FT& sphere_radius,
const bool on_sphere = false)
: Base(max_try_nb,sphere_radius)
, on_sphere_(on_sphere) {}
/**
* @brief destructor
*/
virtual ~Li_random_perturbation() {}
#ifdef CGAL_MESH_3_PERTURBER_VERBOSE
virtual std::string perturbation_name() const
{
std::stringstream name;
name << "Li random perturbation [" << this->max_try_nb() << ", "
<< this->sphere_radius() << ", " << (on_sphere_?"on":"in") << "]";
return name.str();
}
#endif
protected:
/**
* @brief do_perturb implementation
*/
virtual std::pair<bool,Vertex_handle>
do_perturb(const Vertex_handle& v,
const std::vector<Cell_handle>& slivers,
C3T3& c3t3,
const MeshDomain& domain,
const SliverCriterion& criterion,
const FT& sliver_bound,
std::vector<Vertex_handle>& modified_vertices,
bool *could_lock_zone = nullptr) const
{
CGAL_precondition(!slivers.empty());
return apply_perturbation(v, slivers, c3t3, domain, criterion,
sliver_bound, modified_vertices,
could_lock_zone);
}
private:
// -----------------------------------
// Private methods
// -----------------------------------
/**
* @brief try to improve mesh using random moves of \c v
*/
std::pair<bool,Vertex_handle>
apply_perturbation(const Vertex_handle& v,
const std::vector<Cell_handle>& slivers,
C3T3& c3t3,
const MeshDomain& domain,
const SliverCriterion& criterion,
const FT& sliver_bound,
std::vector<Vertex_handle>& modified_vertices,
bool *could_lock_zone = nullptr) const
{
typedef Triangulation_helpers<typename C3T3::Triangulation> Th;
typename Gt::Construct_point_3 cp =
c3t3.triangulation().geom_traits().construct_point_3_object();
typename Gt::Construct_weighted_point_3 cwp =
c3t3.triangulation().geom_traits().construct_weighted_point_3_object();
typename Gt::Construct_translated_point_3 translate =
c3t3.triangulation().geom_traits().construct_translated_point_3_object();
typename Gt::Construct_vector_3 vector =
c3t3.triangulation().geom_traits().construct_vector_3_object();
modified_vertices.clear();
// Create a helper
typedef C3T3_helpers<C3T3,MeshDomain> C3T3_helpers;
C3T3_helpers helper(c3t3, domain);
// norm depends on the local size of the mesh
FT sq_norm = this->compute_perturbation_sq_amplitude(v, c3t3, this->sphere_sq_radius());
const Weighted_point& weighted_initial_location = c3t3.triangulation().point(v);
const Bare_point initial_location = cp(weighted_initial_location);
// Initialize loop variables
bool criterion_improved = false;
Vertex_handle moving_vertex = v;
Vertex_set mod_vertices;
// TODO: use no_topo_change to compute new_angle without moving everything
unsigned int try_nb = 0;
while ( ++try_nb <= Base::max_try_nb() )
{
Vector_3 delta = this->random_vector(c3t3, sq_norm, on_sphere_);
// always from initial_location!
Bare_point new_location = translate(initial_location, delta);
if ( c3t3.in_dimension(moving_vertex) < 3 )
new_location = helper.project_on_surface(moving_vertex, new_location);
// check that we don't insert a vertex inside a protecting ball
if(Th().inside_protecting_balls(c3t3.triangulation(),
moving_vertex, new_location))
continue;
// try to move vertex
std::vector<Vertex_handle> tmp_mod_vertices;
std::pair<bool,Vertex_handle> update =
helper.update_mesh(moving_vertex,
vector(initial_location, new_location),
cwp(new_location),
criterion,
std::back_inserter(tmp_mod_vertices),
could_lock_zone);
if (could_lock_zone && *could_lock_zone == false)
return std::make_pair(false, Vertex_handle());
// get new vertex
moving_vertex = update.second;
if ( update.first )
{
criterion_improved = true;
mod_vertices.insert(tmp_mod_vertices.begin(), tmp_mod_vertices.end());
std::size_t nois;
#ifdef CGAL_NEW_INCIDENT_SLIVERS
nois = helper.number_of_incident_slivers(moving_vertex, criterion, sliver_bound);
#else
std::vector<Cell_handle> new_slivers =
helper.incident_slivers(moving_vertex, criterion, sliver_bound);
nois = new_slivers.size();
#endif
// If sliver number has decreased, we won
if(nois < slivers.size() )
{
std::copy(mod_vertices.begin(),
mod_vertices.end(),
std::back_inserter(modified_vertices));
return std::make_pair(true,moving_vertex);
}
}
}//end while ( ++try_nb <= Base::max_try_nb() )
if ( criterion_improved )
{
std::copy(mod_vertices.begin(),
mod_vertices.end(),
std::back_inserter(modified_vertices));
}
// Moving vertex is located on the best location
return std::make_pair(criterion_improved, moving_vertex);
}
private:
// If set to \c true, then random point will be generated on sphere surface.
bool on_sphere_;
};
} // end namespace Mesh_3
} //namespace CGAL
#include <CGAL/enable_warnings.h>
#endif // CGAL_MESH_3_VERTEX_PERTURBATION_H