dust3d/thirdparty/cgal/CGAL-5.1/include/CGAL/Mesh_2/Clusters.h

585 lines
16 KiB
C++

// Copyright (c) 2004-2005 INRIA Sophia-Antipolis (France).
// Copyright (c) 2010 GeometryFactory Sarl (France)
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL: https://github.com/CGAL/cgal/blob/v5.1/Mesh_2/include/CGAL/Mesh_2/Clusters.h $
// $Id: Clusters.h 8bb22d5 2020-03-26T14:23:37+01:00 Sébastien Loriot
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s) : Laurent Rineau
#ifndef CGAL_MESH_2_CLUSTERS_H
#define CGAL_MESH_2_CLUSTERS_H
#include <CGAL/license/Mesh_2.h>
#include <CGAL/Filter_circulator.h>
#include <CGAL/Unique_hash_map.h>
#include <utility>
#include <CGAL/boost/iterator/transform_iterator.hpp>
namespace CGAL {
namespace Mesh_2
{
namespace details
{
template <class Tr>
class Is_edge_constrained {
const Tr* tr_;
public:
typedef Is_edge_constrained<Tr> Self;
typedef typename Tr::Edge_circulator Edge_circulator;
Is_edge_constrained(const Tr& tr) : tr_(&tr)
{}
bool operator()(const Edge_circulator& ec) const
{
return tr_->is_constrained(*ec);
}
};
} // end namespace details
template <class Tr>
class Clusters
{
typedef typename Tr::Vertex_handle Vertex_handle;
typedef typename Tr::Point Point;
typedef typename Tr::Geom_traits Geom_traits;
typedef typename Geom_traits::FT FT;
typedef FT Squared_length; /**<This typedef is used to remind that
the length is squared. */
typedef typename Tr::Edge_circulator Edge_circulator;
/**
* Special type: filtered circulator that returns only constrained
* edges.
*/
typedef Filter_circulator<Edge_circulator,
details::Is_edge_constrained<Tr> >
Constrained_edge_circulator;
public:
/** \name Clusters public types */
/**
* \c Cluster register several informations about clusters.
* A cluster is a set of vertices v_i incident to one vertice
* v_0, so that angles between segments [v_0, v_i] is less than 60
* degres.
*/
struct Cluster {
bool reduced ; /**< Is the cluster reduced? */
/**
* Smallest_angle gives the two vertices defining the
* smallest angle in the cluster.
*/
std::pair<Vertex_handle, Vertex_handle> smallest_angle;
FT rmin; // @fixme: rmin has no meaning if reduced=false!!!
Squared_length minimum_squared_length;
/**
* The following map tells what vertices are in the cluster and if
* the corresponding segment has been split once.
*/
typedef std::map<Vertex_handle, bool> Vertices_map;
Vertices_map vertices;
bool is_reduced() const {
return reduced;
}
bool is_reduced(const Vertex_handle v) {
return vertices[v];
}
};
private:
/** \name Clusters associated types */
typedef std::multimap<Vertex_handle, Cluster> Cluster_map;
typedef typename Cluster_map::value_type Cluster_map_value_type;
template <class Pair>
struct Pair_get_first
: public CGAL::cpp98::unary_function<Pair, typename Pair::first_type>
{
typedef typename Pair::first_type result;
const result& operator()(const Pair& p) const
{
return p.first;
}
};
typedef typename Cluster::Vertices_map Cluster_vertices_map;
private:
/* --- protected datas --- */
Tr& tr; /**< The triangulation itself. */
/**
* Multimap \c Vertex_handle -> \c Cluster
* Each vertex can have several clusters.
*/
Cluster_map cluster_map;
public:
typedef typename Cluster_map::const_iterator const_iterator;
typedef typename Cluster_map::iterator iterator;
Clusters(Tr& tr_) : tr(tr_)
{
}
/** For all vertices, calls create_clusters_of_vertex(). */
void create_clusters() {
create_clusters(typename Tr::Constraint_hierarchy_tag());
}
// function that depends of Tr::Constraint_hierarchy_tag
template <typename Constraint_hierarchy_tag>
void create_clusters(Constraint_hierarchy_tag) {
cluster_map.clear();
for(typename Tr::Finite_vertices_iterator vit = tr.finite_vertices_begin();
vit != tr.finite_vertices_end();
vit++)
{
create_clusters_of_vertex(vit);
}
}
void create_clusters(Tag_true) {
cluster_map.clear();
Unique_hash_map<Vertex_handle,bool> created(false);
for(typename Tr::Subconstraint_iterator it = tr.subconstraints_begin();
it != tr.subconstraints_end(); ++it) {
Vertex_handle vh = it->first.first;
if(!created[vh]){
created[vh] = true;
create_clusters_of_vertex(vh);
}
vh = it->first.second;
if(!created[vh]){
created[vh] = true;
create_clusters_of_vertex(vh);
}
}
}
private:
/**
* Computes clusters of the vertex \c v, using the auxiliary function
* construct_cluster().
*/
void create_clusters_of_vertex(const Vertex_handle v);
/**
* Adds the sequence [\c begin, \c end] to the cluster \c c and adds it
* to the clusters of the vertex \c v.
*/
void construct_cluster(const Vertex_handle v,
const Constrained_edge_circulator& begin,
const Constrained_edge_circulator& end,
Cluster c = Cluster());
public:
/** \name Functions to manage clusters during the refinement process. */
/**
* Update the cluster of [\c va,\c vb], putting \c vm instead of \c vb.
* If reduction=false, the edge [va,vm] is not set reduced.
*/
void update_cluster(Cluster& c, iterator it,
const Vertex_handle va, const Vertex_handle vb,
const Vertex_handle vm,
bool reduction = true);
/**
* Returns the cluster of [\c va,\c vb] in \c c and return true
* if it is in a cluster. Returns also a const_iterator in \c it.
*/
bool get_cluster(const Vertex_handle va, const Vertex_handle vb,
Cluster& c, iterator& it);
/** Const version of get_cluster(). */
bool get_cluster(const Vertex_handle va, const Vertex_handle vb,
Cluster& c, const_iterator& it) const;
/** \name Auxiliary functions that return a boolean. */
/**
* Tells if the angle <pleft, pmiddle, pright> is less than 60 degres.
* Uses squared_cosine_of_angle_times_4() and used by
* create_clusters_of_vertex().
*/
bool is_small_angle(const Point& pleft,
const Point& pmiddle,
const Point& pright) const;
private:
/** \name Helping computing functions */
/** Returns the squared cosine of the angle <pleft, pmiddle, pright>
times 4. */
FT squared_cosine_of_angle_times_4(const Point& pleft,
const Point& pmiddle,
const Point& pright) const;
/** Helper functions to access the two vertices of an Edge
source is the vertex around which the circulator turns. */
//@{
Vertex_handle source(const Edge_circulator& ec) const
{
return ec->first->vertex(tr.cw(ec->second));
}
Vertex_handle target(const Edge_circulator& ec) const
{
return ec->first->vertex(tr.ccw(ec->second));
}
//@}
public:
/** \name CONST ACCESS FUNCTIONS */
typedef typename boost::transform_iterator<
Pair_get_first<typename Cluster_map::value_type>,
typename Cluster_map::const_iterator>
Cluster_vertices_iterator;
typedef typename boost::transform_iterator<
Pair_get_first<typename Cluster_vertices_map::value_type>,
typename Cluster_vertices_map::const_iterator>
Vertices_in_cluster_iterator;
int size() const
{
return cluster_map.size();
}
Cluster_vertices_iterator clusters_vertices_begin() const
{
return Cluster_vertices_iterator(cluster_map.begin());
}
Cluster_vertices_iterator clusters_vertices_end() const
{
return Cluster_vertices_iterator(cluster_map.end());
}
unsigned int number_of_clusters_at_vertex(const Vertex_handle& vh) const
{
typedef typename Cluster_map::const_iterator Iterator;
typedef std::pair<Iterator, Iterator> Range;
Range range = cluster_map.equal_range(vh);
return std::distance(range.first, range.second);
}
// returns the sequence of vertices bellonging to the n-th cluster of vh
std::pair<Vertices_in_cluster_iterator, Vertices_in_cluster_iterator>
vertices_in_cluster_sequence(const Vertex_handle& vh,
const unsigned int n) const
{
typedef typename Cluster_map::const_iterator Iterator;
typedef std::pair<Iterator, Iterator> Range;
Range range = cluster_map.equal_range(vh);
Iterator first = range.first;
std::advance(first, n);
const Cluster& c = first->second;
return
std::make_pair(Vertices_in_cluster_iterator(c.vertices.begin()),
Vertices_in_cluster_iterator(c.vertices.end()));
}
}; // end class Clusters
template <typename Tr>
void Clusters<Tr>::
update_cluster(Cluster& c, iterator it, Vertex_handle va,
Vertex_handle vb, Vertex_handle vm, bool reduction)
{
typename Geom_traits::Compute_squared_distance_2 squared_distance =
tr.geom_traits().compute_squared_distance_2_object();
cluster_map.erase(it);
c.vertices.erase(vb);
c.vertices[vm] = reduction;
if(false == reduction) {
for(typename Cluster::Vertices_map::iterator
it = c.vertices.begin(),
end = c.vertices.end();
it != end; ++it)
{
it->second = false;
}
}
if(vb==c.smallest_angle.first)
c.smallest_angle.first = vm;
if(vb==c.smallest_angle.second)
c.smallest_angle.second = vm;
FT l = squared_distance(va->point(),vm->point());
if(l<c.minimum_squared_length)
c.minimum_squared_length = l;
if(!c.is_reduced())
{
typename Cluster::Vertices_map::iterator it = c.vertices.begin();
while(it!=c.vertices.end() && c.is_reduced(it->first))
++it; // @todo: use std::find and an object class
if(it==c.vertices.end())
c.reduced = true;
}
if(c.is_reduced())
c.rmin = squared_distance(c.smallest_angle.first->point(),
c.smallest_angle.second->point())/FT(4);
cluster_map.insert(Cluster_map_value_type(va,c));
#ifdef CGAL_MESH_2_DEBUG_CLUSTERS
std::cerr << "Cluster at " << va->point() << " is updated. "
<< "\n vm: " << vm->point()
<< "\n reduction: " << reduction
<< "\n min_sq_len: " << c.minimum_squared_length
<< "\n";
#endif // CGAL_MESH_2_DEBUG_CLUSTERS
}
template <typename Tr>
bool Clusters<Tr>::
get_cluster(Vertex_handle va, Vertex_handle vb, Cluster& c,
const_iterator& it) const
{
typedef std::pair<const_iterator, const_iterator> Range;
Range range = cluster_map.equal_range(va);
for(it = range.first; it != range.second; it++)
{
const Cluster &cl = it->second;
if(cl.vertices.find(vb)!=cl.vertices.end()) {
c = it->second;
return true;
}
}
return false;
}
template <typename Tr>
bool Clusters<Tr>::
get_cluster(Vertex_handle va, Vertex_handle vb, Cluster& c,
iterator& it)
{
typedef std::pair<iterator, iterator> Range;
Range range = cluster_map.equal_range(va);
for(it = range.first; it != range.second; it++)
{
const Cluster &cl = it->second;
if(cl.vertices.find(vb)!=cl.vertices.end()) {
c = it->second;
return true;
}
}
return false;
}
template <typename Tr>
void Clusters<Tr>::
create_clusters_of_vertex(const Vertex_handle v)
{
details::Is_edge_constrained<Tr> test(tr);
Constrained_edge_circulator begin(tr.incident_edges(v),test);
// This circulator represents all constrained edges around the
// vertex v. An edge [v,v'] is represented by the vertex v'.
if(begin == 0) return; // if there is only one vertex
Constrained_edge_circulator
current(begin), next(begin), cluster_begin(begin);
++next; // next is always just after current.
if(current == next) return;
bool in_a_cluster = false;
do
{
if(is_small_angle(target(current)->point(), v->point(),
target(next)->point()))
{
if(!in_a_cluster)
{
// at this point, current is the beginning of a cluster
in_a_cluster = true;
cluster_begin = current;
}
}
else {
if(in_a_cluster)
{
// at this point, current is the end of a cluster and
// cluster_begin is its beginning
construct_cluster(v, cluster_begin, current);
in_a_cluster = false;
}
}
current = next;
++next;
} while( current!=begin );
if(in_a_cluster)
{
Cluster c;
iterator it;
if(get_cluster(v, target(begin), c, it))
{
// get the cluster and erase it from the clusters map
cluster_map.erase(it);
construct_cluster(v, cluster_begin, begin, c);
}
else
construct_cluster(v, cluster_begin, current);
}
}
template <typename Tr>
void Clusters<Tr>::
construct_cluster(Vertex_handle v,
const Constrained_edge_circulator& begin,
const Constrained_edge_circulator& end,
Cluster c)
{
typename Geom_traits::Compute_squared_distance_2 squared_distance =
tr.geom_traits().compute_squared_distance_2_object();
if(c.vertices.empty())
{
c.reduced = false;
// c.rmin is not initialized because
// reduced=false!
c.minimum_squared_length =
squared_distance(v->point(), target(begin)->point());
Constrained_edge_circulator second(begin);
++second;
c.smallest_angle.first = target(begin);
c.smallest_angle.second = target(second);
}
const bool all_edges_in_cluster = (begin == end); // tell if all incident edges
// are in the cluster
const Point& vp = v->point();
FT greatest_cosine =
squared_cosine_of_angle_times_4(c.smallest_angle.first->point(),
v->point(),
c.smallest_angle.second->point());
bool one_full_loop_is_needed = all_edges_in_cluster;
bool stop = false;
Constrained_edge_circulator circ(begin);
Constrained_edge_circulator next(begin);
while(!stop)
{
c.vertices[target(circ)] = false;
Squared_length l = squared_distance(vp,
target(circ)->point());
c.minimum_squared_length =
(std::min)(l,c.minimum_squared_length);
if(circ!=end || one_full_loop_is_needed)
{
FT cosine =
squared_cosine_of_angle_times_4(target(circ)->point(),
v->point(),
target(next)->point());
if(cosine>greatest_cosine)
{
greatest_cosine = cosine;
c.smallest_angle.first = target(circ);
c.smallest_angle.second = target(next);
}
}
if(one_full_loop_is_needed) {
one_full_loop_is_needed = false;
} else {
stop = (circ == end);
}
++circ;
++next;
}
typedef typename Cluster_map::value_type Value_key_pair;
cluster_map.insert(Value_key_pair(v,c));
}
template <typename Tr>
bool Clusters<Tr>::
is_small_angle(const Point& pleft,
const Point& pmiddle,
const Point& pright) const
{
typename Geom_traits::Angle_2 angle =
tr.geom_traits().angle_2_object();
typename Geom_traits::Orientation_2 orient =
tr.geom_traits().orientation_2_object();
if( angle(pleft, pmiddle, pright)==OBTUSE )
return false;
if( orient(pmiddle,pleft,pright)==RIGHT_TURN)
return false;
FT cos_alpha = squared_cosine_of_angle_times_4(pleft, pmiddle,
pright);
if(cos_alpha > 1)
{
return true; //the same cluster
}
else
{
return false; //another cluster
}
}
template <typename Tr>
typename Clusters<Tr>::FT
Clusters<Tr>::
squared_cosine_of_angle_times_4(const Point& pb, const Point& pa,
const Point& pc) const
{
typename Geom_traits::Compute_squared_distance_2 squared_distance =
tr.geom_traits().compute_squared_distance_2_object();
const FT
a = squared_distance(pb, pc),
b = squared_distance(pa, pb),
c = squared_distance(pa, pc);
const FT num = a-(b+c);
return (num*num)/(b*c);
}
} // end namespace Mesh_2
} // end namespace CGAL
#endif // CGAL_MESH_2_CLUSTERS_H