cgal/HDVF/include/CGAL/HDVF/Geometric_chain_complex_too...

675 lines
32 KiB
C++

// Copyright (c) 2025 LIS Marseille (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s) : Alexandra Bac <alexandra.bac@univ-amu.fr>
#ifndef CGAL_HDVF_GEOMETRIC_CHAIN_COMPLEX_TOOLS_H
#define CGAL_HDVF_GEOMETRIC_CHAIN_COMPLEX_TOOLS_H
#include <CGAL/license/HDVF.h>
#include <vector>
#include <map>
#include <stdexcept>
#include <unordered_set>
#include <CGAL/boost/graph/generators.h>
#include <CGAL/Subdivision_method_3/subdivision_methods_3.h>
#include <CGAL/make_conforming_constrained_Delaunay_triangulation_3.h>
#include <CGAL/HDVF/Simplicial_chain_complex.h>
#include <CGAL/HDVF/Cubical_chain_complex.h>
#include <CGAL/HDVF/Hdvf_core.h>
#include <CGAL/HDVF/Hdvf_persistence.h>
#include <CGAL/HDVF/Hdvf_duality.h>
#include <CGAL/HDVF/Sub_chain_complex_mask.h>
#include <CGAL/HDVF/Mesh_object_io.h>
#include <CGAL/HDVF/Surface_mesh_io.h>
#include <CGAL/HDVF/Triangulation_3_io.h>
#include <CGAL/HDVF/Cub_object_io.h>
#include <CGAL/HDVF/Tet_object_io.h>
#include <CGAL/HDVF/Icosphere_object_io.h>
#include <CGAL/OSM/OSM.h>
/**
* \class SimpComplexTools
* \brief Provides tools for SimpComplexes ((co)homology, duality, persistent homology).
* \brief Friend class of SimpComplex
*/
namespace CGAL {
namespace IO {
// GeometricChainComplex IO
// Hdvf vtk export
/*!
* \brief Exports all the `Hdvf` information of a geometric chain complex to vtk files.
*
* Export PSC labels and homology/cohomology generators (depending on HDVF options) associated to each critical cell to vtk files.
*
* \param hdvf Reference to the HDVF exported.
* \param complex Underlying geometric chain complex.
* \param filename Prefix of all generated files.
* \param co_faces Export the cohomology generator or its co-faces (sometimes more convenient for visualisation).
*
* Below, a sample mesh with, (left) homology generators, (right) two examples of cohomology generators (corresponding generators/co-generators bear similar colours):
*
* <img src="HDVF_dtorus_homs.png" align="center" width=25%/>
* <img src="HDVF_dtorus_cohom1.png" align="center" width=25%/>
* <img src="HDVF_dtorus_cohom2.png" align="center" width=25%/>
*
* The same generators displayed through their co-faces:
*
* <img src="HDVF_dtorus_cohom1_co.png" align="center" width=25%/>
* <img src="HDVF_dtorus_cohom2_co.png" align="center" width=25%/>
*
* All homology / cohomology generators:
*
*<img src="HDVF_dtorus_all.png" align="center" width=30%/>
*/
template <typename ChainComplex, template <typename, int> typename ChainType = OSM::Sparse_chain, template <typename, int> typename SparseMatrixType = OSM::Sparse_matrix, typename VertexIdType = size_t>
void write_VTK (Homological_discrete_vector_field::Hdvf_core<ChainComplex, ChainType, SparseMatrixType>& hdvf, ChainComplex &complex, std::string filename = "test", bool co_faces = false)
{
typedef typename ChainComplex::Coefficient_ring Coefficient_ring;
typedef Homological_discrete_vector_field::Hdvf_core<ChainComplex, ChainType, SparseMatrixType> HDVF_parent;
// Export PSC labelling
std::string outfile(filename+"_PSC.vtk") ;
std::vector<std::vector<int> > labels = hdvf.psc_labels() ;
ChainComplex::chain_complex_to_vtk(complex, outfile, &labels) ;
if (hdvf.hdvf_opts() != Homological_discrete_vector_field::OPT_BND)
{
// Export generators of all critical cells
std::vector<std::vector<size_t> > criticals(hdvf.psc_flags(Homological_discrete_vector_field::CRITICAL)) ;
for (int q = 0; q <= complex.dimension(); ++q)
{
for (size_t c : criticals.at(q))
{
// Homology generators
if (hdvf.hdvf_opts() & (Homological_discrete_vector_field::OPT_FULL | Homological_discrete_vector_field::OPT_G))
{
std::string outfile_g(filename+"_hom_"+std::to_string(c)+"_dim_"+std::to_string(q)+".vtk") ;
// std::vector<std::vector<size_t> > labels = hdvf.export_label(G,c,q) ;
OSM::Sparse_chain<Coefficient_ring,OSM::COLUMN> chain(hdvf.homology_chain(c,q)) ;
ChainComplex::chain_to_vtk(complex, outfile_g, chain, q, c) ;
}
// Cohomology generators
if (hdvf.hdvf_opts() & (Homological_discrete_vector_field::OPT_FULL | Homological_discrete_vector_field::OPT_F))
{
std::string outfile_f(filename+"_cohom_"+std::to_string(c)+"_dim_"+std::to_string(q)+".vtk") ;
OSM::Sparse_chain<Coefficient_ring,OSM::COLUMN> chain(hdvf.cohomology_chain(c, q)) ;
if (!co_faces)
{
ChainComplex::chain_to_vtk(complex, outfile_f, chain, q, c) ;
}
else
{
if (q < complex.dimension())
{
ChainComplex::chain_to_vtk(complex, outfile_f, complex.cofaces_chain(chain, q), q+1, c) ;
}
}
}
}
}
}
}
// Hdvf_persistence vtk export
/** \brief Exports all the `HDVF_persistence` information of a geometric chain complex to vtk files.
*
* Export PSC labels and homology/cohomology generators (depending on HDVF options) associated to each persistent intervals to vtk files.
*
* \param per_hdvf Reference to the persistent HDVF exported.
* \param complex Underlying geometric chain complex.
* \param filename Prefix of all generated files.
* \param co_faces Export the cohomology generator or its co-faces (sometimes more convenient for visualisation).
*/
template <typename ChainComplex, typename Degree, typename FiltrationType>
void write_VTK (Homological_discrete_vector_field::Hdvf_persistence<ChainComplex, Degree, FiltrationType> &per_hdvf, ChainComplex &complex, std::string filename = "per", bool co_faces = false)
{
typedef typename ChainComplex::Coefficient_ring Coefficient_ring;
if (!per_hdvf.with_export())
throw("Cannot export persistent generators to vtk: with_export is off!") ;
using HDVF_type = Homological_discrete_vector_field::Hdvf_persistence<ChainComplex, Degree, FiltrationType>;
using Persistence_interval = HDVF_type::Persistence_interval;
// Export the filtration
std::string out_file_filtration = filename+"_filtration.vtk" ;
std::vector<std::vector<size_t> > filtr_labels = per_hdvf.filtration().export_filtration();
ChainComplex::template chain_complex_to_vtk<size_t>(complex, out_file_filtration, &filtr_labels, "unsigned_long") ;
// Iterate over persistence diagram (iterator over non zero intervals)
// Batch informations are stored in file filename_infos.txt
std::ofstream info_file(filename+"_infos.txt") ;
size_t i = 0 ;
for (typename HDVF_type::iterator it = per_hdvf.begin(); it != per_hdvf.end(); ++it)
{
typename HDVF_type::Persistent_hole hole_data(*it) ;
const Persistence_interval hole(hole_data.hole) ;
// Export informations of this hole
/* V1 */
info_file << i << " -> " << " --- duration : " << hole.duration() << " -- " ;
hole.insert(info_file);
info_file << std::endl ;
/* V2 */
// info_file << i << " -> " << " --- duration : " << hole.duration() << " -- " << hole << std::endl ;
if (hole.duration()>0) // Export vtk of "finite" holes
{
// Build name associated to the ith hole : filename_i
std::string out_file = filename+"_"+std::to_string(i) ;
// Export PSC labels to vtk
ChainComplex::chain_complex_to_vtk(complex, out_file+"_PSC.vtk", &hole_data.labelsPSC) ;
// Export homology generators (g)
if (per_hdvf.hdvf_opts() & (Homological_discrete_vector_field::OPT_FULL | Homological_discrete_vector_field::OPT_G))
{
// First generator : filename_i_g_sigma_q.vtk
{
const size_t id(hole_data.hole.cell_birth.first) ;
const int dim(hole_data.hole.cell_birth.second) ;
std::string tmp(out_file+"_hom_"+std::to_string(id)+"_"+std::to_string(dim)+".vtk") ;
ChainComplex::chain_to_vtk(complex, tmp, hole_data.homology_chain_birth, dim);
}
// Second generator : filename_i_g_tau_q+1.vtk
{
const size_t id(hole_data.hole.cell_death.first) ;
const int dim(hole_data.hole.cell_death.second) ;
std::string tmp(out_file+"_hom_"+std::to_string(id)+"_"+std::to_string(dim)+".vtk") ;
ChainComplex::chain_to_vtk(complex, tmp, hole_data.homology_chain_death, dim);
}
}
// Export cohomology generators (fstar)
if ((per_hdvf.hdvf_opts() == Homological_discrete_vector_field::OPT_FULL) || (per_hdvf.hdvf_opts() == Homological_discrete_vector_field::OPT_F))
{
// First generator : filename_i_fstar_sigma_q.vtk
{
const size_t id(hole_data.hole.cell_birth.first) ;
const int dim(hole_data.hole.cell_birth.second) ;
std::string tmp(out_file+"_cohom_"+std::to_string(id)+"_"+std::to_string(dim)+".vtk") ;
if (co_faces)
{
ChainComplex::chain_to_vtk(complex, tmp, complex.cofaces_chain(hole_data.cohomology_chain_birth, dim), dim+1);
}
else
ChainComplex::chain_to_vtk(complex, tmp, hole_data.cohomology_chain_birth, dim);
}
// Second generator : filename_i_fstar_tau_q+1.vtk
{
const size_t id(hole_data.hole.cell_death.first) ;
const int dim(hole_data.hole.cell_death.second) ;
std::string tmp(out_file+"_cohom_"+std::to_string(id)+"_"+std::to_string(dim)+".vtk") ;
if (co_faces)
{
ChainComplex::chain_to_vtk(complex, tmp, complex.cofaces_chain(hole_data.cohomology_chain_death, dim), dim+1);
}
else
ChainComplex::chain_to_vtk(complex, tmp, hole_data.cohomology_chain_death, dim);
}
}
}
++i ;
}
info_file.close() ;
// Export infinite holes by calling Hdvf_core write_VTK
(static_cast<void (*) (Homological_discrete_vector_field::Hdvf_core<ChainComplex, CGAL::OSM::Sparse_chain,CGAL::OSM::Sub_sparse_matrix>&, ChainComplex&, std::string, bool)>(CGAL::IO::write_VTK<ChainComplex,CGAL::OSM::Sparse_chain,CGAL::OSM::Sub_sparse_matrix, size_t>))(per_hdvf, complex, filename+"_inf", co_faces);
}
// Hdvf_duality vtk export
/** \brief Exports all the `HDVF_duality` information of a geometric chain complex to vtk files.
Export PSC labels and homology/cohomology generators (depending on HDVF options) associated to each persistent intervals to vtk files.
\param hdvf Reference to the HDVF exported.
\param complex Underlying geometric chain complex.
\param filename Prefix of all generated files.
\param co_faces Export the cohomology generator or its co-faces (sometimes more convenient for visualisation).
*/
template <typename ChainComplex, typename VertexIdType = size_t>
void write_VTK (Homological_discrete_vector_field::Hdvf_duality<ChainComplex> &hdvf, ChainComplex &complex, std::string filename = "test", bool co_faces = false)
{
typedef typename ChainComplex::Coefficient_ring Coefficient_ring;
typedef Homological_discrete_vector_field::Hdvf_duality<ChainComplex> HDVF_parent;
// Export PSC labelling
std::string outfile(filename+"_PSC.vtk") ;
std::vector<std::vector<int> > labels = hdvf.psc_labels() ;
ChainComplex::chain_complex_to_vtk(complex, outfile, &labels) ;
if (hdvf.hdvf_opts() != Homological_discrete_vector_field::OPT_BND)
{
// Export generators of all critical cells
std::vector<std::vector<size_t> > criticals(hdvf.psc_flags(Homological_discrete_vector_field::CRITICAL)) ;
for (int q = 0; q <= complex.dimension(); ++q)
{
for (size_t c : criticals.at(q))
{
// Homology generators
if (hdvf.hdvf_opts() & (Homological_discrete_vector_field::OPT_FULL | Homological_discrete_vector_field::OPT_G))
{
std::string outfile_g(filename+"_hom_"+std::to_string(c)+"_dim_"+std::to_string(q)+".vtk") ;
// std::vector<std::vector<size_t> > labels = hdvf.export_label(G,c,q) ;
OSM::Sparse_chain<Coefficient_ring,OSM::COLUMN> chain(hdvf.homology_chain(c,q)) ;
ChainComplex::chain_to_vtk(complex, outfile_g, chain, q, c) ;
}
// Cohomology generators
if (hdvf.hdvf_opts() & (Homological_discrete_vector_field::OPT_FULL | Homological_discrete_vector_field::OPT_F))
{
std::string outfile_f(filename+"_cohom_"+std::to_string(c)+"_dim_"+std::to_string(q)+".vtk") ;
OSM::Sparse_chain<Coefficient_ring,OSM::COLUMN> chain(hdvf.cohomology_chain(c, q)) ;
if (!co_faces)
{
ChainComplex::chain_to_vtk(complex, outfile_f, chain, q, c) ;
}
else // Compute co-faces
{
if (q < complex.dimension())
{
// Restrict the cofaces of the cohomology generator to the current sub chain complex
OSM::Sparse_chain<Coefficient_ring,OSM::COLUMN> cofaces_chain(complex.cofaces_chain(chain, q)) ;
Homological_discrete_vector_field::Sub_chain_complex_mask<ChainComplex> sub(hdvf.get_current_mask());
sub.screen_chain(cofaces_chain, q+1);
// Display
ChainComplex::chain_to_vtk(complex, outfile_f, cofaces_chain, q+1, c) ;
}
}
}
}
}
}
}
} /* end namespace IO */
namespace Homological_discrete_vector_field {
// Duality tools
/** \brief Type returned by `dualize_complex()`.
*
* \tparam ChainComplex Type of the underlying chain complex.
*
* The structure contains a pair:
* - `L_complex`: a pointer over simplicial chain complex (homeomorphic to \f$\mathbb B^3\f$).
* - `K_complex`: a sub chain complex mask (encoding the initial mesh inside `*L_complex`)
*/
template <typename ChainComplex>
class Complex_duality_data_t {
public:
/** \brief Type of the underlying chain complex. */
typedef ChainComplex Chain_complex;
/** \brief Type of sub chain complex mask encoding the sub complex `*K_complex`. */
typedef Sub_chain_complex_mask<Chain_complex> Sub_chain_complex ;
/** \brief Pointer over a complex homeomorphic to \f$\mathbb B^3\f$. */
std::shared_ptr<Chain_complex> L_complex ;
/** \brief Pointer over a sub chain complex mask encoding a sub complex of `*L_complex`. */
std::shared_ptr<Sub_chain_complex> K_complex ;
/** \brief Default constructor*/
Complex_duality_data_t() : L_complex(nullptr), K_complex(nullptr) {}
/** \brief Constructor from a pointer over a chain complex and a sub chain complex mask.
*
* \warning The `Complex_duality_data_t` destructor deletes its underlying complex and sub chain complex mask.
*/
Complex_duality_data_t(std::shared_ptr<Chain_complex> L, std::shared_ptr<Sub_chain_complex> K) : L_complex(L), K_complex(K) {}
// Complex_duality_data_t(const Complex_duality_data_t&) = delete;
} ;
// =========== Simplicial chain complex tools
// Build K sub-chain complex of L (homeomorphic to B^n)
/*!
\ingroup PkgHDVFAlgorithmClasses
The class `Duality_simplicial_complex_tools` is dedicated to Alexander duality for 3D surface meshes. Starting from a simplicial chain complex (encoding a 3D surface mesh), it provides methods to embed the complex into a larger icosphere and generate a 3D constrained Delaunay triangulation.
Technically, starting from a Simplicial_chain_complex `K_init`, the method `dualize_complex()` builds a `Simplicial_chain_complex` `L` and a `Sub_chain_complex_mask` `K`.
- L : complex built out of K_init together with a closing icosphere, meshed by tetgen (constrained Delaunay triangulation)
- K (Sub_chain_complex_mask) : Sub_chain_complex_mask identifying K_init inside L
\tparam CoefficientRing a model of the `IntegralDomainWithoutDivision` concept providing the ring used to compute homology.
\tparam Traits a geometric traits class model of the `HDVFTraits` concept.
*/
template<typename CoefficientRing, typename Traits>
class Duality_simplicial_complex_tools {
public:
typedef typename Traits::Point Point ;
/** \brief Type of simplicial chain complex encoding `L`. */
typedef Simplicial_chain_complex<CoefficientRing,Traits> Chain_complex ;
/** \brief Type of sub chain complex mask encoding the sub complex `K`. */
typedef Sub_chain_complex_mask<Chain_complex> Sub_chain_complex ;
/** \brief Type of 3D surface meshes. */
typedef CGAL::Surface_mesh<typename Traits::Kernel::Point_3> Triangle_mesh;
/** \brief Type returned by `dualize_complex()`. */
typedef Complex_duality_data_t<Chain_complex> Complex_duality_data;
/** \brief Default constructor. */
Duality_simplicial_complex_tools() {}
private:
static std::shared_ptr<Sub_chain_complex> compute_sub_chain_complex(const Chain_complex& _K, const Chain_complex &L){
// Build the Sub_chain_complex_mask encoding K_init inside L
std::shared_ptr<Sub_chain_complex> K(std::make_shared<Sub_chain_complex>(L, false));
// Visit all cells of K_init and activate the corresponding bit in K
for (int q=0; q<=_K.dimension(); ++q)
{
for (size_t i=0; i<_K.number_of_cells(q); ++i)
{
const Simplex& simplex(_K.index_to_cell(i,q)) ;
const size_t id(L.cell_to_index(simplex));
K->set_bit_on(q, id) ;
}
}
return K;
}
public:
/** \brief Generates a subcomplex \f$K\f$K and a complex \f$L\f$ with \f$K\subseteq L\f$ from a set of simplices `mesh_io`.
*
* On the one hand, a `Simplicial_chain_complex` is built from `mesh_io` (let us call if `_K`); on the other hand, this complex is embedded into a larger icosphere and a 3D constrained Delaunay tetraedrization is generated.
* Then \f$K\f$, \f$L\f$ and vertex coordinates are extracted and stored in a `Complex_duality_data` structure.
*
* After the function, `mesh_object_io` has been extended with the simplices of the closing icosphere.
*
* The following figures shows the resulting complex with an initial `Twirl` mesh (right - sectional view):
* <img src="HDVF_twirl_view1.png" align="center" width=35%/>
* <img src="HDVF_twirl_view2.png" align="center" width=30%/>
*
* \param mesh_io `Mesh_object_io` containing the initial set of simplicies (working mesh).
* \param BB_ratio Ratio of the "closing" icosphere diameter with respect to the diameter of the object's bounding box.
* \param out_file_prefix Prefix of tetgen intermediate files (default: "file_K_closed.off").
* \param subdiv Subdivision level of the bounding icosphere.
*/
static Complex_duality_data dualize_complex (Mesh_object_io<Traits>& mesh_io, double BB_ratio=1.5, const std::string& out_file_prefix = "file_K_closed.off", unsigned int subdiv = 2)
{
std::cerr << "-- Starting dualize_complex" << std::endl;
std::cerr << "Imported set of simplices" << std::endl;
std::cout << mesh_io;
// Create a temporary associated complex (to identify simplices of mesh_io)
Chain_complex* _K = new Chain_complex(mesh_io);
// Closing mesh_object_io by adding the icosphere
// Compute a bounding icosphere
Point center = mesh_io.centroid() ;
double r = mesh_io.radius(center) ;
Icosphere_object_io<Traits> ico(subdiv,center, BB_ratio*r) ;
std::cerr << "Icosphere generated" << std::endl;
ico.print_infos() ;
// Add it to the mesh
mesh_io.push_back(ico) ;
std::cerr << "Mesh concatenation" << std::endl;
mesh_io.print_infos() ;
// Write this mesh to an off file for Tetgen
mesh_io.write_off(out_file_prefix) ;
// const std::string tetgen_path(CMAKE_TETGEN_PATH) ;
// WARNING: use CGAL 3D triangulation to get rid of tetgen...
const std::string tetgen_path("/Users/umenohana/Dropbox/G-Mod/TopAlg/code/tetgen1.6.0/build/") ;
std::string tetgen_command = tetgen_path+"tetgen -pqkcYfe "+out_file_prefix+".off" ;
system(tetgen_command.c_str()) ;
// Read the mesh built by tetgen for L
Tet_object_io<Traits> tetL(out_file_prefix) ;
// Build the output data structure
Complex_duality_data dualized_complex;
// Build the associated SimpComplex
dualized_complex.L_complex = std::make_shared<Chain_complex>(tetL) ;
std::shared_ptr<Chain_complex> L(dualized_complex.L_complex);
std::cout << "------ L:" << *L;
// Build the Sub_chain_complex_mask encoding K_init inside L
dualized_complex.K_complex = compute_sub_chain_complex(*_K, *L);
// Remove the temporary complex
delete _K;
return dualized_complex;
}
/** \brief Generates a subcomplex \f$K\f$K and a complex \f$L\f$ with \f$K\subseteq L\f$ from a `Surface_mesh`.
*
* On the one hand, a `Simplicial_chain_complex` is built from the `Surface_mesh` (let us call it `_K`); on the other hand, this complex is embedded into a larger icosphere and a 3D constrained Delaunay tetraedrization is generated.
* Then \f$K\f$, \f$L\f$ and vertex coordinates are built and extracted (from both previous computation) and stored in a `Complex_duality_data` structure.
*
* After the function, `mesh_object_io` has been extended with the simplices of the closing icosphere.
*
* The following figures shows the resulting complex with an initial `Twirl` mesh (right - sectional view):
* <img src="HDVF_twirl_view1.png" align="center" width=35%/>
* <img src="HDVF_twirl_view2.png" align="center" width=30%/>
*
* \param mesh `Surface_mesh` containing the initial mesh.
* \param BB_ratio Ratio of the "closing" icosphere diameter with respect to the diameter of the object's bounding box.
* \param subdiv Subdivision level of the bounding icosphere.
*/
static Complex_duality_data dualize_complex (Triangle_mesh& mesh, double BB_ratio=1.5, unsigned int subdiv = 2)
{
typedef CGAL::Triangulation_data_structure_3<CGAL::Conforming_constrained_Delaunay_triangulation_vertex_base_3<typename Traits::Kernel>, CGAL::Conforming_constrained_Delaunay_triangulation_cell_base_3<typename Traits::Kernel>> TDS;
typedef CGAL::Triangulation_3<typename Traits::Kernel, TDS> Triangulation;
typedef CGAL::Conforming_constrained_Delaunay_triangulation_3<typename Traits::Kernel, Triangulation> CCDT3;
std::cerr << "-- Starting dualize_complex" << std::endl;
std::cerr << "Imported mesh" << std::endl;
// Create a temporary Surfaces_mesh_io (to load the mesh)
Surface_mesh_io<Triangle_mesh, Traits> K_init_mesh_io(mesh);
// Create a temporary associated complex (to identify simplices of mesh_io)
Chain_complex* _K = new Chain_complex(K_init_mesh_io);
std::cout << "------ _K:" << *_K;
// Closing mesh_object_io by adding the icosphere
// Compute a bounding icosphere
Point center = K_init_mesh_io.centroid() ;
double r = K_init_mesh_io.radius(center) ;
Triangle_mesh ico;
CGAL::make_icosahedron<Triangle_mesh, Point>(ico, center, r*BB_ratio);
CGAL::Subdivision_method_3::Loop_subdivision(
ico, CGAL::parameters::number_of_iterations(subdiv)
);
std::cerr << "Icosphere generated" << std::endl;
// Add it to the mesh
mesh += ico ;
std::cerr << "Mesh concatenation" << std::endl;
std::string off_file("tmp/test_bounded.off");
std::ofstream out (off_file , std::ios::out | std::ios::trunc);
if ( ! out . good () ) {
std::cerr << "Cannot output dualized mesh to:\n " << off_file << " - not found.\n";
throw std::runtime_error("File Parsing Error: File not found");
}
out << mesh ;
out.close();
// Generate plc_facet_map
auto plc_facet_map = get(CGAL::face_patch_id_t<int>(), mesh);
int cpt(0);
for (typename Triangle_mesh::Face_iterator it = mesh.faces_begin(); it!= mesh.faces_end(); ++it){
plc_facet_map[*it] = cpt++;
}
// Constrained Delaunay Tetraedrisation preserving plc_facet_map
auto ccdt = CGAL::make_conforming_constrained_Delaunay_triangulation_3(mesh, CGAL::parameters::plc_face_id(plc_facet_map));
// Detect refined constrained faces
std::vector<std::vector<typename Triangulation::Facet > > faces_constr(cpt);
for (typename Triangulation::Facet f : ccdt.constrained_facets()) {
if (ccdt.is_facet_constrained(f))
{
const int i(ccdt.face_constraint_index(f));
faces_constr.at(i).push_back(f);
}
}
for (int i=0; i<faces_constr.size(); ++i) {
if (faces_constr.at(i).size()>1)
std::cout << i << ": " << faces_constr.at(i).size() << std::endl;
}
Triangulation tri_L = std::move(ccdt).triangulation();
// Build the output object
Complex_duality_data dualized_complex;
// Build the associated Triangulation_3_io
Triangulation_3_io<Triangulation, Traits> L_tri_io(tri_L);
// Build the associated SimpComplex
dualized_complex.L_complex = std::make_shared<Chain_complex>(L_tri_io) ;
std::shared_ptr<Chain_complex> L(dualized_complex.L_complex);
std::cout << "------ L:" << *L;
std::vector<std::vector<size_t> > indices(L->dimension()+1);
for (int q=0; q<=L->dimension(); ++q){
for (int i=0; i<L->number_of_cells(q); ++i){
indices.at(q).push_back(i);
}
}
std::string vtk_file("tmp/test_CDT3.vtk");
Chain_complex::chain_complex_to_vtk(*L, vtk_file, &indices, "int");
// Build the Sub_chain_complex_mask encoding K_init inside L
dualized_complex.K_complex = compute_sub_chain_complex(*_K, *L);
// Remove the temporary complex
delete _K;
return dualized_complex;
}
/** \brief Exports a chain complex to a Mesh_object_io */
static Mesh_object_io<Traits>& export_mesh_object(const Chain_complex& _CC)
{
std::vector<Io_cell_type> vcells ;
for (int q = 0; q <= _CC.dimension(); ++q)
for (size_t i = 0; i<_CC.number_of_cells(q); ++i)
{
const Simplex& s(_CC.index_to_cell(i,q)) ;
vcells.push_back(s.vertices()) ;
}
std::vector<Point> coords;
for (auto it = _CC.points().begin(); it != _CC.points().end(); ++it)
coords.push_back(Point(*it));
Mesh_object_io<Traits> &m = *(new Mesh_object_io<Traits>(-3, coords, vcells)) ;
return m ;
}
} ;
// =========== Cubical chain complex tools
// Build K sub-chain complex of L (homeomorphic to B^n)
// Adjust L size
/*!
\ingroup PkgHDVFAlgorithmClasses
The class `Duality_cubical_complex_tools` is dedicated to Alexander duality for 3D binary volumes.
Starting from a Cubical_chain_complex `K_init`, the method `dualize_complex()` builds a `Cubical_chain_complex` `L` and `Sub_chain_complex_mask` `K`.
- L : complex built of the "full" bounding box of `K_init`
- K (Sub_chain_complex_mask) : Sub_chain_complex_mask identifying `K_init` inside `L`
Use the `frame()` method from the `Cub_object_io` class to enlarge the bounding box (via a 1 pixel dilatation) if necessary.
\tparam CoefficientRing a model of the `IntegralDomainWithoutDivision` concept providing the ring used to compute homology.
\tparam Traits a geometric traits class model of the `HDVFTraits` concept.
*/
template<typename CoefficientRing, typename Traits>
class Duality_cubical_complex_tools {
public:
/** \brief Type of cubical complexes used for the initial complex and \f$L\f$. */
typedef Cubical_chain_complex<CoefficientRing, Traits> Chain_complex ;
/** \brief Type of sub chain complex mask used to encode the sub-complex \f$K\f$. */
typedef Sub_chain_complex_mask<Chain_complex> Sub_chain_complex ;
/** \brief Type returned by `dualize_complex()`. */
typedef Complex_duality_data_t<Chain_complex> Complex_duality_data;
// Constructor
Duality_cubical_complex_tools() {}
/** \brief Generates a subcomplex \f$K\f$K and a complex \f$L\f$ with \f$K\subseteq L\f$ from a cubical complex `K_init`.
*
* `L` is the bounding box of `K_init` (homeomorphic to a ball) and \f$K\f$ is a sub chain complex mask encoding `K_init`.
*
* The following figures shows the resulting complex with an initial simple cubical complex (right - sectional view):
* <img src="HDVF_eight_view1.png" align="center" width=35%/>
* <img src="HDVF_eight_view2.png" align="center" width=30%/>
*
* \param K_init Initial cubical chain complex (working mesh).
*/
static Complex_duality_data dualize_complex (const Chain_complex& K_init)
{
Cub_object_io<Traits> tmp_L ;
tmp_L.dim = K_init.dimension() ;
tmp_L.N = K_init.size_bb() ;
tmp_L.ncubs.resize(tmp_L.dim+1) ;
// Visit all boolean indices in the BB of _CC and insert corresponding cells in the Cub_object_io of L
for (size_t i=0; i<K_init.size(); ++i)
{
const std::vector<size_t> tmpkhal(K_init.bindex_to_cell(i)) ;
const int dtmp(K_init.dimension(tmpkhal)) ;
(tmp_L.ncubs)[dtmp] += 1 ;
tmp_L.cubs.push_back(tmpkhal) ;
}
// Build the output object
Complex_duality_data dualized_complex;
dualized_complex.L_complex = std::make_shared<Chain_complex>(tmp_L, Chain_complex::PRIMAL);
std::shared_ptr<Chain_complex> L(dualized_complex.L_complex) ;
// Build the Sub_chain_complex_mask corresponding to _CC
dualized_complex.K_complex = std::make_shared<Sub_chain_complex> (Sub_chain_complex(*L, false)) ;
std::shared_ptr<Sub_chain_complex> K(dualized_complex.K_complex) ;
// Visit all cells of _CC and activate the corresponding bit in K
for (int q=0; q<=K_init.dimension(); ++q)
{
for (size_t i=0; i<K_init.number_of_cells(q); ++i)
{
const std::vector<size_t> khal(K_init.index_to_cell(i, q)) ;
const size_t j = L->cell_to_index(khal);
K->set_bit_on(q,j) ;
}
}
return dualized_complex;
}
/** \brief Generates a subcomplex \f$K\f$K and a complex \f$L\f$ with \f$K\subseteq L\f$ from a `Cub_object_io` `K_init`.
*
* `L` is the bounding box of `K_init` (homeomorphic to a ball) and \f$K\f$ is a sub chain complex mask encoding `K_init`.
*
* The following figures shows the resulting complex with an initial simple cubical complex (right - sectional view):
* <img src="HDVF_eight_view1.png" align="center" width=35%/>
* <img src="HDVF_eight_view2.png" align="center" width=30%/>
*
* \param K_init Initial `Cub_object_io` (cubical object).
*/
static Complex_duality_data dualize_complex (const Cub_object_io<Traits>& K_init, typename Chain_complex::Cubical_complex_primal_dual primal_dual) {
Chain_complex* _K = new Chain_complex(K_init, primal_dual);
Complex_duality_data res(dualize_complex(*_K));
delete _K;
return res;
}
} ;
} /* end namespace Homological_discrete_vector_field */
} /* end namespace CGAL */
#endif // CGAL_HDVF_GEOMETRIC_CHAIN_COMPLEX_TOOLS_H