mirror of https://github.com/CGAL/cgal
359 lines
12 KiB
C++
359 lines
12 KiB
C++
// Copyright (c) 2017 CNRS and LIRIS' Establishments (France).
|
|
// All rights reserved.
|
|
//
|
|
// This file is part of CGAL (www.cgal.org); you can redistribute it and/or
|
|
// modify it under the terms of the GNU Lesser General Public License as
|
|
// published by the Free Software Foundation; either version 3 of the License,
|
|
// or (at your option) any later version.
|
|
//
|
|
// Licensees holding a valid commercial license may use this file in
|
|
// accordance with the commercial license agreement provided with the software.
|
|
//
|
|
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
|
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
//
|
|
// $URL$
|
|
// $Id$
|
|
// SPDX-License-Identifier: LGPL-3.0+
|
|
//
|
|
// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr>
|
|
//
|
|
#ifndef CGAL_COMBINATORIAL_MAP_FUNCTIONALITIES_H
|
|
#define CGAL_COMBINATORIAL_MAP_FUNCTIONALITIES_H 1
|
|
|
|
#include <stack>
|
|
#include <CGAL/Union_find.h>
|
|
#include <boost/unordered_map.hpp>
|
|
#include<CGAL/Random.h>
|
|
|
|
namespace CGAL {
|
|
|
|
template<typename Map>
|
|
class Path_on_surface
|
|
{
|
|
public:
|
|
typedef typename Map::Dart_handle Dart_handle;
|
|
typedef typename Map::Dart_const_handle Dart_const_handle;
|
|
|
|
Path_on_surface(const Map& amap) : m_map(amap)
|
|
{}
|
|
|
|
// @return true iff the path is valid; i.e. a sequence of edges two by
|
|
// two adjacent.
|
|
bool is_valid() const
|
|
{
|
|
for (unsigned int i=1; i<m_path.size(); ++i)
|
|
{
|
|
Dart_const_handle pend=m_map.other_extremity(m_path[i-1]);
|
|
if (pend==Map::null_handle) { return false; }
|
|
|
|
if (!CGAL::template belong_to_same_cell<Map,0>(m_map, m_path[i], pend))
|
|
{ return false; }
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// @return true iff the path is empty
|
|
bool is_empty() const
|
|
{ return m_path.empty(); }
|
|
|
|
// @return true iff the path is closed (i.e. the second extremity of the
|
|
// last dart of the path is the same vertex than the one of the
|
|
// first dart of the path.
|
|
bool is_closed() const
|
|
{
|
|
if (is_empty()) { return false; } // or true by vacuity ?
|
|
if (!is_valid()) { return false; } // Interest ??
|
|
|
|
Dart_const_handle pend=m_map.other_extremity(m_path.back());
|
|
if (pend==Map::null_handle) { return false; }
|
|
|
|
return CGAL::belong_to_same_cell<Map,0>(m_map, m_path[0], pend);
|
|
}
|
|
|
|
// @return true iff the path does not pass twice through a same edge
|
|
// or a same vertex.
|
|
bool is_simple() const
|
|
{
|
|
typename Map::size_type markvertex=m_map.get_new_mark();
|
|
typename Map::size_type markedge=m_map.get_new_mark();
|
|
|
|
bool res=true;
|
|
unsigned int i=0;
|
|
for (i=0; res && i<m_path.size(); ++i)
|
|
{
|
|
if (m_map.is_marked(m_path[i], markvertex)) res=false;
|
|
if (m_map.is_marked(m_path[i], markedge)) res=false;
|
|
|
|
CGAL::mark_cell<Map, 0>(m_path[i], markvertex);
|
|
CGAL::mark_cell<Map, 1>(m_path[i], markedge);
|
|
}
|
|
|
|
i=0;
|
|
while(m_map.number_of_marked_darts(markedge)>0)
|
|
{
|
|
assert(i<m_path.size());
|
|
CGAL::unmark_cell<Map, 0>(m_path[i], markvertex);
|
|
CGAL::unmark_cell<Map, 1>(m_path[i], markedge);
|
|
++i;
|
|
}
|
|
|
|
m_map.free_mark(markvertex);
|
|
m_map.free_mark(markedge);
|
|
|
|
return res;
|
|
}
|
|
|
|
bool extend_randomly(bool allow_half_turn=false)
|
|
{
|
|
CGAL::Random random;
|
|
if (m_path.empty())
|
|
{
|
|
unsigned int index=random.get_int(0, m_map.darts().capacity());
|
|
while (!m_map.darts().is_used(index))
|
|
{
|
|
++index;
|
|
if (index==m_map.darts().capacity()) index=0;
|
|
}
|
|
m_path.push_back(m_map.darts().iterator_to(m_map.darts()[index]));
|
|
return true;
|
|
}
|
|
|
|
Dart_const_handle pend=m_map.other_extremity(m_path.back());
|
|
if (pend==Map::null_handle) { return false; }
|
|
|
|
typename Map::template Dart_of_cell_range<0>::iterator
|
|
it=m_map.template darts_of_cell<0>(pend).begin();
|
|
|
|
unsigned int index=random.get_int((allow_half_turn?0:1), m_map.template darts_of_cell<0>
|
|
(pend).size());
|
|
for(unsigned int i=0; i<index; ++i)
|
|
{ ++it; }
|
|
|
|
m_path.push_back(it);
|
|
return true;
|
|
}
|
|
|
|
protected:
|
|
const Map& m_map;
|
|
std::vector<Dart_const_handle> m_path;
|
|
};
|
|
|
|
|
|
template<typename Map>
|
|
class Combinatorial_map_tools
|
|
{
|
|
public:
|
|
typedef typename Map::Dart_handle Dart_handle;
|
|
typedef typename Map::Dart_const_handle Dart_const_handle;
|
|
typedef CGAL::Union_find<Dart_handle> UFTree;
|
|
typedef typename UFTree::handle UFTree_handle;
|
|
|
|
typedef boost::unordered_map<Dart_const_handle,
|
|
std::pair<Dart_const_handle, Dart_const_handle> > TPaths;
|
|
|
|
Combinatorial_map_tools(Map& amap) : m_map(amap)
|
|
{
|
|
if (!m_map.is_without_boundary(1))
|
|
{
|
|
std::cerr<<"ERROR: the given amap has 1-boundaries; such a surface is not possible to process here."
|
|
<<std::endl;
|
|
}
|
|
if (!m_map.is_without_boundary(2))
|
|
{
|
|
std::cerr<<"ERROR: the given amap has 2-boundaries; which are not yet considered (but this will be done later)."
|
|
<<std::endl;
|
|
}
|
|
}
|
|
|
|
void initialize_vertices(UFTree& uftrees,
|
|
boost::unordered_map<Dart_const_handle, UFTree_handle>& vertices)
|
|
{
|
|
uftrees.clear();
|
|
vertices.clear();
|
|
|
|
typename Map::size_type treated=m_map.get_new_mark();
|
|
for (typename Map::Dart_range::iterator it=m_map.darts().begin(),
|
|
itend=m_map.darts().end(); it!=itend; ++it)
|
|
{
|
|
if (!m_map.is_marked(it, treated))
|
|
{
|
|
UFTree_handle newuf=uftrees.make_set(it);
|
|
for (typename Map::template Dart_of_cell_basic_range<0>::iterator
|
|
itv=m_map.template darts_of_cell_basic<0>(it, treated).begin(),
|
|
itvend=m_map.template darts_of_cell_basic<0>(it, treated).end();
|
|
itv!=itvend; ++itv)
|
|
{
|
|
vertices[itv]=newuf;
|
|
m_map.mark(itv, treated);
|
|
}
|
|
}
|
|
}
|
|
m_map.free_mark(treated);
|
|
}
|
|
|
|
void initialize_faces(UFTree& uftrees,
|
|
boost::unordered_map<Dart_const_handle, UFTree_handle>& faces)
|
|
{
|
|
uftrees.clear();
|
|
faces.clear();
|
|
|
|
typename Map::size_type treated=m_map.get_new_mark();
|
|
for (typename Map::Dart_range::iterator it=m_map.darts().begin(), itend=m_map.darts().end();
|
|
it!=itend; ++it)
|
|
{
|
|
if (!m_map.is_marked(it, treated))
|
|
{
|
|
UFTree_handle newuf=uftrees.make_set(it);
|
|
Dart_handle cur=it;
|
|
do
|
|
{
|
|
faces[cur]=newuf;
|
|
m_map.mark(cur, treated);
|
|
cur=m_map.template beta<1>(cur);
|
|
}
|
|
while (cur!=it);
|
|
}
|
|
}
|
|
m_map.free_mark(treated);
|
|
}
|
|
|
|
UFTree_handle get_uftree(const UFTree& uftrees,
|
|
const boost::unordered_map<Dart_const_handle,
|
|
UFTree_handle>& mapdhtouf,
|
|
Dart_const_handle dh)
|
|
{
|
|
assert(dh!=NULL);
|
|
assert(mapdhtouf.find(dh)!=mapdhtouf.end());
|
|
return uftrees.find(mapdhtouf.find(dh)->second);
|
|
}
|
|
|
|
void surface_simplification_in_one_face()
|
|
{
|
|
UFTree uftrees; // uftree of faces; one tree for each face, contains one dart of the face
|
|
boost::unordered_map<Dart_const_handle, UFTree_handle> faces;
|
|
initialize_faces(uftrees, faces);
|
|
|
|
m_map.set_automatic_attributes_management(false);
|
|
|
|
typename Map::size_type treated=m_map.get_new_mark();
|
|
Dart_handle currentdart=NULL;
|
|
|
|
for (typename Map::Dart_range::iterator it=m_map.darts().begin(),
|
|
itend=m_map.darts().end(); it!=itend; ++it)
|
|
{
|
|
currentdart=it;
|
|
if (!m_map.is_marked(currentdart, treated))
|
|
{
|
|
if (m_map.template is_free<2>(currentdart))
|
|
{ m_map.mark(currentdart, treated); }
|
|
else
|
|
{
|
|
m_map.mark(currentdart, treated);
|
|
m_map.mark(m_map.template beta<2>(currentdart), treated);
|
|
|
|
// We remove dangling edges and degree two edges.
|
|
// The two first tests allow to keep isolated edges (case of spheres)
|
|
if ((m_map.template beta<0>(currentdart)!=m_map.template beta<2>(currentdart) ||
|
|
m_map.template beta<1>(currentdart)!=m_map.template beta<2>(currentdart)) &&
|
|
get_uftree(uftrees, faces, currentdart)!=
|
|
get_uftree(uftrees, faces, m_map.template beta<2>(currentdart)))
|
|
{
|
|
uftrees.unify_sets(get_uftree(uftrees, faces, currentdart),
|
|
get_uftree(uftrees, faces,
|
|
m_map.template beta<2>(currentdart)));
|
|
|
|
// TODO LATER (?) OPTIMIZE AND REPLACE THE REMOVE_CELL CALL BY THE MODIFICATION BY HAND
|
|
// OR DEVELOP A SPECIALIZED VERSION OF REMOVE_CELL
|
|
m_map.remove_cell<1>(currentdart);
|
|
if (!m_map.is_dart_used(it))
|
|
{ ++it; }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
m_map.set_automatic_attributes_management(true);
|
|
m_map.free_mark(treated);
|
|
}
|
|
|
|
void surface_simplification_in_one_vertex()
|
|
{
|
|
UFTree uftrees; // uftree of vertices; one tree for each vertex, contains one dart of the vertex
|
|
boost::unordered_map<Dart_const_handle, UFTree_handle> vertices;
|
|
initialize_vertices(uftrees, vertices);
|
|
|
|
m_map.set_automatic_attributes_management(false);
|
|
|
|
for (typename Map::Dart_range::iterator it=m_map.darts().begin(),
|
|
itend=m_map.darts().end(); it!=itend; ++it)
|
|
{
|
|
if (get_uftree(uftrees, vertices, it)!=
|
|
get_uftree(uftrees, vertices, m_map.template beta<2>(it)))
|
|
{
|
|
uftrees.unify_sets(get_uftree(uftrees, vertices, it),
|
|
get_uftree(uftrees, vertices, m_map.template beta<2>(it)));
|
|
m_map.contract_cell<1>(it);
|
|
}
|
|
}
|
|
|
|
m_map.set_automatic_attributes_management(true);
|
|
}
|
|
|
|
void surface_quadrangulate()
|
|
{
|
|
// Here the map has only one face and one vertex.
|
|
typename Map::size_type oldedges=m_map.get_new_mark();
|
|
m_map.negate_mark(oldedges); // now all edges are marked
|
|
|
|
// 1) We insert a vertex in the face (note that all points have the same geometry).
|
|
// New edges created by the operation are not marked.
|
|
m_map.insert_point_in_cell_2(m_map.darts().begin(), m_map.point(m_map.darts().begin()));
|
|
|
|
// 2) We update the pair of darts
|
|
for (typename TPaths::iterator itp=paths.begin(), itpend=paths.end(); itp!=itpend; ++itp)
|
|
{
|
|
std::pair<Dart_const_handle, Dart_const_handle>& p=itp->second;
|
|
p.first=m_map.template beta<1>(p.first);
|
|
p.second=m_map.template beta<1>(p.second);
|
|
}
|
|
|
|
// 3) We remove all the old edges.
|
|
for (typename Map::Dart_range::iterator it=m_map.darts().begin(), itend=m_map.darts().end();
|
|
it!=itend; ++it)
|
|
{
|
|
if (m_map.is_marked(it, oldedges))
|
|
{ m_map.remove_cell<1>(it); }
|
|
}
|
|
|
|
m_map.free_mark(oldedges);
|
|
}
|
|
|
|
// Compute, for each edge not in the spanning tree, the pair of darts adjacent
|
|
// to it
|
|
void compute_length_two_paths()
|
|
{
|
|
paths.clear();
|
|
|
|
for (typename Map::Dart_range::iterator it=m_map.darts().begin(),
|
|
itend=m_map.darts().end(); it!=itend; ++it)
|
|
{
|
|
if (m_map.template is_free<2>(it) ||
|
|
it<m_map.template beta<2>(it))
|
|
{
|
|
paths[it]=std::make_pair(m_map.template beta<0>(it),
|
|
m_map.template beta<2,0>(it));
|
|
}
|
|
}
|
|
}
|
|
|
|
protected:
|
|
Map& m_map;
|
|
TPaths paths;
|
|
};
|
|
|
|
} // namespace CGAL
|
|
|
|
#endif // CGAL_COMBINATORIAL_MAP_FUNCTIONALITIES_H //
|
|
// EOF //
|