diff --git a/CMakeLists.txt b/CMakeLists.txt index bac90b2f401e47057d9d9be876ba1e1e65b689a6..2b70fa85e1f592ab7614696e6976269a540da98a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,6 +81,7 @@ opt(PLUGINS "Enable post-processing plugins" ${DEFAULT}) opt(POST "Enable post-processing module (required by GUI)" ${DEFAULT}) opt(POPPLER "Enable Poppler for displaying PDF documents (experimental)" OFF) opt(QT "Enable dummy QT graphical interface proof-of-concept (experimental)" OFF) +opt(REVOROPT "Enable Revoropt (used for CVT remeshing)" OFF) opt(SALOME "Enable Salome routines for CAD healing" ${DEFAULT}) opt(SGEOM "Enable SGEOM interface to OCC (experimental)" OFF) opt(SLEPC "Enable SLEPc eigensolvers (required for conformal compounds)" ${DEFAULT}) @@ -1297,6 +1298,27 @@ if(NOOPT) endforeach(FILE) endif(NOOPT) +#enable Revoropt and set compile flags for the corresponding plugin +if(ENABLE_REVOROPT) + find_path(EIGEN3_INC "eigen3/Eigen/Dense") + if(EIGEN3_INC AND HAVE_MESH AND HAVE_PLUGINS AND HAVE_ANN AND HAVE_BFGS) + list(APPEND EXTERNAL_INCLUDES ${EIGEN3_INC} contrib/Revoropt/include) + message(STATUS "using contrib/Revoropt") + set_config_option(HAVE_REVOROPT "Revoropt") + add_definitions(-DUSE_ANN) + get_source_file_property(PROP Plugin/CVTRemesh.cpp COMPILE_FLAGS) + if(PROP) + set_source_files_properties(Plugin/CVTRemesh.cpp PROPERTIES + COMPILE_FLAGS "${PROP} -std=c++11") + else(PROP) + set_source_files_properties(Plugin/CVTRemesh.cpp PROPERTIES + COMPILE_FLAGS "-std=c++11") + endif(PROP) + else(EIGEN3_INC AND HAVE_MESH AND HAVE_PLUGINS AND HAVE_ANN AND HAVE_BFGS) + message(WARNING "Revoropt requires Eigen3, Mesh, Plugins, Ann and BFGS") + endif(EIGEN3_INC AND HAVE_MESH AND HAVE_PLUGINS AND HAVE_ANN AND HAVE_BFGS) +endif(ENABLE_REVOROPT) + list(SORT CONFIG_OPTIONS) set(GMSH_CONFIG_OPTIONS "") foreach(OPT ${CONFIG_OPTIONS}) diff --git a/Common/GmshConfig.h.in b/Common/GmshConfig.h.in index 10de2ab6b57309f3116853ca88b196abca375504..325b01a5a02908e024aa86352b384b7296e0a5b8 100644 --- a/Common/GmshConfig.h.in +++ b/Common/GmshConfig.h.in @@ -60,6 +60,7 @@ #cmakedefine HAVE_POST #cmakedefine HAVE_POPPLER #cmakedefine HAVE_QT +#cmakedefine HAVE_REVOROPT #cmakedefine HAVE_SALOME #cmakedefine HAVE_SGEOM #cmakedefine HAVE_SLEPC diff --git a/Fltk/onelabGroup.cpp b/Fltk/onelabGroup.cpp index d3c1c0957907de5b0ba212f1f4ba73a6bcc4ac2b..2f5e92b5f99cd9eba8ad8227fa02e56fd5e5860e 100644 --- a/Fltk/onelabGroup.cpp +++ b/Fltk/onelabGroup.cpp @@ -59,7 +59,7 @@ void onelab_cb(Fl_Widget *w, void *data) if(action == "refresh"){ updateGraphs(); - FlGui::instance()->rebuildTree(false); // FIXME: true would be better + FlGui::instance()->rebuildTree(true); // FIXME: true would be better return; } diff --git a/Plugin/CMakeLists.txt b/Plugin/CMakeLists.txt index fbace74ab943be9e5fe5e554ccbf08c72e4ddc08..4f3eb01bdd3903e28f7b0d0cd0a5deaed7b44553 100644 --- a/Plugin/CMakeLists.txt +++ b/Plugin/CMakeLists.txt @@ -35,6 +35,7 @@ set(SRC SimplePartition.cpp Crack.cpp DuplicateBoundaries.cpp FaultZone.cpp MeshSubEntities.cpp + CVTRemesh.cpp ) file(GLOB HDR RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.h) diff --git a/Plugin/CVTRemesh.cpp b/Plugin/CVTRemesh.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e09877c565f886fee3fda80a8c93934d4ec4daef --- /dev/null +++ b/Plugin/CVTRemesh.cpp @@ -0,0 +1,326 @@ +// Gmsh - Copyright (C) 1997-2014 C. Geuzaine, J.-F. Remacle +// +// See the LICENSE.txt file for license information. Please report all +// bugs and problems to the public mailing list <gmsh@geuz.org>. + +#include "GmshConfig.h" + +#if defined(HAVE_REVOROPT) + +#include "CVTRemesh.h" + +#include "Revoropt/Mesh/builder_def.hpp" +#include "Revoropt/Mesh/sampling_def.hpp" +#include "Revoropt/Mesh/normals_def.hpp" + +#include "Revoropt/RVD/rvd.hpp" +#include "Revoropt/RVD/rdt.hpp" + +#include "Revoropt/CVT/minimizer.hpp" + +#include "Revoropt/Solver/alglib_lbfgs.hpp" + +#include "GModel.h" +#include "MTriangle.h" +#include "discreteFace.h" + +#include <vector> +#include <iostream> + +StringXNumber CVTRemeshOptions_Number[] = { + {GMSH_FULLRC, "Sites", NULL, 20000.}, + {GMSH_FULLRC, "Iterations", NULL, 20.}, + {GMSH_FULLRC, "Anisotropy", NULL, 0.03}, + {GMSH_FULLRC, "Variable density", NULL, 0.3}, + {GMSH_FULLRC, "Feature sensitivity", NULL, 5.}, + {GMSH_FULLRC, "Normal computation radius", NULL, 0.005} +}; + +extern "C" +{ + GMSH_Plugin *GMSH_RegisterCVTRemeshPlugin() + { + return new GMSH_CVTRemeshPlugin(); + } +} + +std::string GMSH_CVTRemeshPlugin::getHelp() const +{ + return "Plugin(CVTRemesh) triangulates an input geometry using" + "centroïdal Voronoï tesselations. The STL mesh of the geometry" + "is generated and randomly sampled. An objective function derived" + "from centroïdal Voronoï tesselations is then defined on the" + "generated sampling, and optimized through LBFGS to obtain a" + "regular sampling of the surface. The triangulation is extracted" + "as the restricted Delaunay triangulation of the samples and the" + "STL mesh.\n\n" + "If `View' < 0, the plugin is run on the current view.\n\n" + "Plugin(Triangulate) creates one new view."; +} + +int GMSH_CVTRemeshPlugin::getNbOptions() const +{ + return sizeof(CVTRemeshOptions_Number) / sizeof(StringXNumber); +} + +StringXNumber *GMSH_CVTRemeshPlugin::getOption(int iopt) +{ + return &CVTRemeshOptions_Number[iopt]; +} + +//solver callback +class SolverCallback { + + public : + + template<typename Data> + void operator()( Data* data ) { + //Output current iteration data + Msg::Info("[CVTRemesh] : iteration %d, objective function value is %f", data->k, data->fx) ; + } + +} ; + +PView *GMSH_CVTRemeshPlugin::execute(PView *v) +{ + //TODO normalization + + GModel* m = GModel::current() ; + + std::vector<double> vertices ; + std::vector<unsigned int> faces ; + + unsigned int offset = 0 ; + for(GModel::fiter it = m->firstFace(); it != m->lastFace(); ++it) { + (*it)->buildSTLTriangulation() ; + for(unsigned int i = 0; i < (*it)->stl_vertices.size(); ++i) { + GPoint p = (*it)->point((*it)->stl_vertices[i]) ; + vertices.push_back(p.x()) ; + vertices.push_back(p.y()) ; + vertices.push_back(p.z()) ; + } + for(unsigned int i = 0; i < (*it)->stl_triangles.size(); ++i) { + faces.push_back((*it)->stl_triangles[i]+offset) ; + } + offset += (*it)->stl_vertices.size() ; + } + + Revoropt::MeshBuilder<3> mesh ; + mesh.swap_vertices(vertices) ; + mesh.swap_faces(faces) ; + + double mesh_center[3] ; + double mesh_scale ; + Revoropt::normalize_mesh(&mesh, mesh_center, &mesh_scale) ; + + double nradius = (double)CVTRemeshOptions_Number[5].def ; + + //normals + std::vector<double> normals(3*mesh.vertices_size()) ; + Revoropt::full_robust_vertex_normals(&mesh,nradius,normals.data()) ; + + //lifted vertices + std::vector<double> lifted_vertices(6*mesh.vertices_size(), 0) ; + for(unsigned int vertex = 0; vertex < mesh.vertices_size(); ++vertex) { + std::copy( mesh.vertex(vertex), + mesh.vertex(vertex)+3, + lifted_vertices.data()+6*vertex + ) ; + std::copy( normals.data()+3*vertex, + normals.data()+3*vertex+3, + lifted_vertices.data()+6*vertex+3 + ) ; + } + + //setup lifted mesh + Revoropt::ROMeshWrapper<3,6> lifted_mesh( + lifted_vertices.data(), + lifted_vertices.size()/6, + &mesh + ) ; + + //triangle weight factor + double twfactor = (double)CVTRemeshOptions_Number[3].def ; + + //face ratios + std::vector<double> triangle_weights(lifted_mesh.faces_size()) ; + if(twfactor > 0) { + for(unsigned int f = 0; f < lifted_mesh.faces_size(); ++f) { + //vertices of the initial triangle + const unsigned int* fverts = mesh.face(f) ; + + //positions + const double* x[3] ; + for(int i=0; i<3; ++i) { + x[i] = lifted_mesh.vertex(fverts[i]) ; + } + + //ratio + double ratio = 1 ; + + //vectors + typedef Eigen::Matrix<double,3,1> Vector3 ; + + Eigen::Map<const Vector3> v0(x[0]) ; + Eigen::Map<const Vector3> v1(x[1]) ; + Eigen::Map<const Vector3> v2(x[2]) ; + + //triangle frame + Vector3 U = (v1-v0) ; + const double U_len = U.norm() ; + if(U_len > 0) { + U /= U_len ; + Vector3 H = (v2-v0) ; + H = H - H.dot(U)*U ; + const double H_len = H.norm() ; + if(H_len > 0) { + //we know that the triangle is not flat + H /= H_len ; + + //gradient of the barycentric weights in the triangle + Eigen::Matrix<double,3,2> bar_grads ; + bar_grads(2,0) = 0 ; + bar_grads(2,1) = 1/H_len ; + + //gradient norms of every normal component + for(int i = 0; i < 2; ++i) { + //reference frame for the vertex + Eigen::Map<const Vector3> vi0(x[(i+1)%3]) ; + Eigen::Map<const Vector3> vi1(x[(i+2)%3]) ; + Eigen::Map<const Vector3> vi2(x[ i ]) ; + + Vector3 Ui = (vi1-vi0) ; + Ui /= Ui.norm() ; + Vector3 Hi = (vi2-vi0) ; + Hi = Hi - Hi.dot(Ui)*Ui ; + const double Hi_invlen = 1/Hi.norm() ; + Hi *= Hi_invlen ; + bar_grads(i,0) = Hi.dot(U)*Hi_invlen ; + bar_grads(i,1) = Hi.dot(H)*Hi_invlen ; + } + + //gradient of each component of the normal + Eigen::Map<const Vector3> n0(x[0]+3) ; + Eigen::Map<const Vector3> n1(x[1]+3) ; + Eigen::Map<const Vector3> n2(x[2]+3) ; + + Eigen::Matrix<double,3,2> n_grads = Eigen::Matrix<double,3,2>::Zero() ; + + n_grads = n0*bar_grads.row(0) ; + n_grads += n1*bar_grads.row(1) ; + n_grads += n2*bar_grads.row(2) ; + + //maximal gradient norm + double g_max = n_grads.row(0).dot(n_grads.row(0)) ; + double g_other = n_grads.row(1).dot(n_grads.row(1)) ; + g_max = g_max > g_other ? g_max : g_other ; + g_other = n_grads.row(2).dot(n_grads.row(2)) ; + g_max = g_max > g_other ? g_max : g_other ; + + if(g_max == g_max) { //prevent nan + ratio += g_max ; + } + } + } + triangle_weights[f] = pow(ratio,twfactor) ; + } + } + + //normal factor + double nfactor = (double)CVTRemeshOptions_Number[2].def ; ; + + //weight the normal component by the provided factor + for(unsigned int i = 0; i<lifted_mesh.vertices_size(); ++i) { + double* v = lifted_vertices.data() + 6*i ; + v[3]*= nfactor ; + v[4]*= nfactor ; + v[5]*= nfactor ; + } + + //number of sites + unsigned int nsites = (unsigned int)CVTRemeshOptions_Number[0].def ; + + //lifted sites + std::vector<double> lifted_sites(6*nsites) ; + if(twfactor > 0) { + Revoropt::generate_random_sites< Revoropt::ROMesh<3,6> >( + &lifted_mesh, nsites, lifted_sites.data(), triangle_weights.data() + ) ; + } else { + Revoropt::generate_random_sites< Revoropt::ROMesh<3,6> >( + &lifted_mesh, nsites, lifted_sites.data() + ) ; + } + + //setup the cvt minimizer + Revoropt::CVT::DirectMinimizer< Revoropt::ROMesh<3,6> > cvt ; + cvt.set_sites(lifted_sites.data(), nsites) ; + cvt.set_mesh(&lifted_mesh) ; + if(twfactor > 0) { + cvt.set_triangle_weights(triangle_weights.data()) ; + } + + //setup the callback + SolverCallback callback ; + + //number of iterations + unsigned int niter = (unsigned int)CVTRemeshOptions_Number[1].def ; ; + unsigned int aniso_niter = std::min<unsigned int>(10,niter) ; + + //solver status + int status = 0 ; + + //isotropic iterations + if(niter > 10) { + aniso_niter = std::max(aniso_niter,niter*10/100) ; + cvt.set_anisotropy(1) ; + status = cvt.minimize<Revoropt::Solver::AlgLBFGS>(niter-aniso_niter, &callback) ; + } + + //anisotropic iterations + if(niter > 0) { + //tangent space anisotropy + double tanisotropy = (double)CVTRemeshOptions_Number[4].def ; ; + + //anisotropic iterations + cvt.set_anisotropy(tanisotropy) ; + status = cvt.minimize<Revoropt::Solver::AlgLBFGS>(aniso_niter, &callback) ; + } + + //rdt + std::vector<unsigned int> rdt_triangles ; + Revoropt::RDTBuilder< Revoropt::ROMesh<3,6> > build_rdt(rdt_triangles) ; + Revoropt::RVD< Revoropt::ROMesh<3,6> > rvd ; + rvd.set_sites(lifted_sites.data(), nsites) ; + rvd.set_mesh(&lifted_mesh) ; + rvd.compute(build_rdt) ; + + GFace* res_face = new discreteFace(m, m->getMaxElementaryNumber(2)+1) ; + m->add(res_face) ; + + //scale back and transfer to gmsh + std::vector<MVertex*> m_verts(nsites) ; + for(unsigned int i = 0; i < nsites; ++i) { + m_verts[i] = new MVertex( + lifted_sites[6*i ]*mesh_scale + mesh_center[0], + lifted_sites[6*i+1]*mesh_scale + mesh_center[1], + lifted_sites[6*i+2]*mesh_scale + mesh_center[2] + ) ; + res_face->addMeshVertex(m_verts[i]) ; + } + for(unsigned int i = 0; i < rdt_triangles.size()/3; ++i) { + res_face->addTriangle( + new MTriangle( + m_verts[rdt_triangles[3*i ]], + m_verts[rdt_triangles[3*i+1]], + m_verts[rdt_triangles[3*i+2]] + ) + ) ; + } + + res_face->setAllElementsVisible(true) ; + + return v ; +} + +#endif diff --git a/Plugin/CVTRemesh.h b/Plugin/CVTRemesh.h new file mode 100644 index 0000000000000000000000000000000000000000..41bfd1894cc1c6e0f0e65c8531995a30329b391b --- /dev/null +++ b/Plugin/CVTRemesh.h @@ -0,0 +1,31 @@ +// Gmsh - Copyright (C) 1997-2014 C. Geuzaine, J.-F. Remacle +// +// See the LICENSE.txt file for license information. Please report all +// bugs and problems to the public mailing list <gmsh@geuz.org>. + +#ifndef _CVT_REMESH_H_ +#define _CVT_REMESH_H_ + +#include "Plugin.h" + +extern "C" +{ + GMSH_Plugin *GMSH_RegisterCVTRemeshPlugin(); +} + +class GMSH_CVTRemeshPlugin : public GMSH_PostPlugin +{ + public: + GMSH_CVTRemeshPlugin(){} + std::string getName() const { return "CVTRemesh"; } + std::string getShortHelp() const + { + return "Remesh an object using Centroïdal Voronoï Tesslation"; + } + std::string getHelp() const; + int getNbOptions() const; + StringXNumber* getOption(int iopt); + PView *execute(PView *); +}; + +#endif diff --git a/Plugin/PluginManager.cpp b/Plugin/PluginManager.cpp index b9e2508e291245e416a412350c789f40e17e9ef2..9cd3a9e0c5d2091837f2b24cd4a9dfe1e26591c9 100644 --- a/Plugin/PluginManager.cpp +++ b/Plugin/PluginManager.cpp @@ -63,6 +63,7 @@ #include "NewView.h" #include "FaultZone.h" #include "MeshSubEntities.h" +#include "CVTRemesh.h" // for testing purposes only :-) #undef HAVE_DLOPEN @@ -261,6 +262,10 @@ void PluginManager::registerDefaultPlugins() ("DuplicateBoundaries", GMSH_RegisterDuplicateBoundariesPlugin())); allPlugins.insert(std::pair<std::string, GMSH_Plugin*> ("MeshSubEntities", GMSH_RegisterMeshSubEntitiesPlugin())); +#if defined(HAVE_REVOROPT) + allPlugins.insert(std::pair<std::string, GMSH_Plugin*> + ("CVTRemesh", GMSH_RegisterCVTRemeshPlugin())); +#endif #if defined(HAVE_TETGEN) allPlugins.insert(std::pair<std::string, GMSH_Plugin*> ("Tetrahedralize", GMSH_RegisterTetrahedralizePlugin())); @@ -336,4 +341,3 @@ void PluginManager::addPlugin(std::string fileName) Msg::Info("Loaded Plugin '%s' (%s)", p->getName().c_str(), p->getAuthor().c_str()); #endif } - diff --git a/contrib/Revoropt/include/Revoropt/CVT/.gitignore b/contrib/Revoropt/include/Revoropt/CVT/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..fd37e1c2dd9d4edc729987fa2887958785f5728c --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/CVT/.gitignore @@ -0,0 +1,7 @@ +main.o +objectview.o +viewer.o +utils.os +cvt +.qglviewer.xml +.sconsign.dblite diff --git a/contrib/Revoropt/include/Revoropt/CVT/computer.hpp b/contrib/Revoropt/include/Revoropt/CVT/computer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2fc9fffd6d13f72d48f4604cd48695f25996631f --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/CVT/computer.hpp @@ -0,0 +1,560 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_CVT_COMPUTER_H_ +#define _REVOROPT_CVT_COMPUTER_H_ + +#include <stddef.h> +#include <eigen3/Eigen/Dense> + +#include <Revoropt/RVD/rvd.hpp> +#include <Revoropt/RVD/polygon.hpp> +#include <Revoropt/Mesh/all_def.hpp> +#include <Revoropt/Mesh/measure_def.hpp> + +namespace Revoropt { +namespace CVT { + +template<typename _Triangulation> +class BaseComputer {// : public RVD::Action<_Triangulation> { + + public : + + /* Typedefs and template arguments */ + typedef _Triangulation Triangulation ; + enum { Dim = Triangulation::VertexDim } ; + typedef Eigen::Matrix<double,Dim,1> Vector ; + + BaseComputer() : g_(NULL), fx_(0), + mesh_(NULL), sites_(NULL), + triangle_weights_(NULL), + mesh_area_(0), mesh_boundary_length_(0), + factor_(1), anisotropy_(1), + border_anisotropy_(1), border_weight_(0), + tolerance_(0) { + } + + /* LBFGS variables to compute */ + void set_g( double* g ) { g_ = g ; } + void set_fx( double val ) { fx_ = val ; } + double get_fx() { return fx_ ; } + + /* Mesh and sites */ + void set_mesh( const Triangulation* mesh ) { + mesh_ = mesh ; + mesh_area_ = mesh_area(mesh) ; + //FIXME + //mesh_boundary_length_ = mesh->boundary_length() ; + } + + const Triangulation* get_mesh() { + return mesh_ ; + } + void set_sites( const double* sites ) { + sites_ = sites ; + } + void set_triangle_weights( const double* triangle_weights ) { + triangle_weights_ = triangle_weights ; + } + + /* Parameter setting */ + void set_factor( double factor ) { + factor_ = factor ; + } + + void set_anisotropy( double anisotropy ) { + anisotropy_ = anisotropy ; + } + + void set_border_anisotropy( double border_anisotropy ) { + border_anisotropy_ = border_anisotropy ; + } + + void set_border_weight( double border_weight ) { + border_weight_ = border_weight ; + } + + void set_tolerance( double tolerance ) { + tolerance_ = tolerance ; + } + + protected : + + /* LBFGS variables to compute */ + double* g_ ; + double fx_ ; + + /* Mesh and sites */ + const Triangulation* mesh_ ; + const double* sites_ ; + const double* triangle_weights_ ; + + double mesh_area_ ; + double mesh_boundary_length_ ; + + /* Parameter variables */ + + double factor_ ; + double anisotropy_ ; + double border_anisotropy_ ; + double border_weight_ ; + double tolerance_ ; +} ; + +/** Direct computer **/ + +/* Compute the value and gradient of the direct CVT objective function when + * passed to the corresponding RVD computer. The direct CVT objective function + * optimizes a set of samples to sample uniformly a mesh. */ + +template<typename _Triangulation> +class DirectComputer : public BaseComputer<_Triangulation> { + + public : + + /* Typedefs and template arguments */ + typedef _Triangulation Triangulation ; + enum { Dim = Triangulation::VertexDim } ; + typedef Eigen::Matrix<double,Dim,1> Vector ; + typedef BaseComputer<Triangulation> Base ; + + typedef RVDEdge<Triangulation> Edge ; + + DirectComputer() : BaseComputer<Triangulation>() {} + + void operator()( unsigned int site, + unsigned int face, + const RVDPolygon<Triangulation>& polygon + ) { + //number of vertices of the polygon + const unsigned int size = polygon.size() ; + + if(size < 3) return ; + + //send triangles to the triangle backend + for(unsigned int i=1; i<size-1; ++i) { + triangle_compute( site, + face, + polygon[0], + polygon[i], + polygon[i+1] + ) ; + } + + //std::cout << "after triangle " << fx_ << std::endl ; + + //compute a point inside the polygon for boundary normal computations + Vector bar ; + bar.setZero() ; + for(unsigned int i=0; i<size; ++i) { + bar += polygon[i].vertex ; + } + bar /= size ; + + //send bisector edges to the edge backend + for(unsigned int i=0; i<size; ++i) { + if(polygon[i].config == Edge::BISECTOR_EDGE) { + edge_compute( site, + face, + polygon[i], + polygon[(i+1)%size], + bar + ) ; + } + +/* FIXME + if(border_weight_ != 0 && mesh_boundary_length_ != 0) { + //one dimensional CVT of the boundary of the mesh + if(polygon[i].config == RVD::FACE_EDGE) { + //get edge index + const unsigned int edge_index = polygon[i].combinatorics ; + //get the neighbours of the triangle + const unsigned int* triangle_neighbours + = mesh_->face_neighbours(triangle) ; + //check whether the edge is a boundary edge + if( triangle_neighbours[edge_index] + == Triangulation::NO_NEIGHBOUR + ) { + border_compute(site, polygon[i], polygon[(i+1)%size]) ; + } + } + } +*/ + } + + //std::cout << "after edges " << fx_ << std::endl ; + } + + private : + + using Base::factor_ ; + using Base::anisotropy_ ; + using Base::tolerance_ ; + using Base::mesh_area_ ; + using Base::fx_ ; + using Base::g_ ; + using Base::sites_ ; + using Base::mesh_ ; + using Base::triangle_weights_ ; + + void triangle_compute( const unsigned int site, + const unsigned int triangle, + const RVDEdge<Triangulation>& e1, + const RVDEdge<Triangulation>& e2, + const RVDEdge<Triangulation>& e3 + ) { + //vertices of the triangle + const Vector& c1 = e1.vertex ; + const Vector& c2 = e2.vertex ; + const Vector& c3 = e3.vertex ; + + //Triangle basis and area + const Vector base = (c2-c1) ; + const double base_len = base.norm() ; + if(base_len <= tolerance_) return ; + const Vector tb1 = base/base_len ; + + Vector height = (c3-c1) ; + height -= height.dot(tb1)*tb1 ; + const double height_len = height.norm() ; + if(height_len <= tolerance_) return ; + const Vector tb2 = height/height_len ; + + //triangle weighting + const double tweight = triangle_weights_ == NULL ? + 1 : + triangle_weights_[triangle] ; + const double area = 0.5*base_len*height_len*tweight ; + + if(area <= tolerance_) return ; + + //site position + Eigen::Map<const Vector> v(sites_ + Dim*site) ; + + //function value + + //site vectors + Vector sv[3] ; + sv[0] = v-c1 ; + sv[1] = v-c2 ; + sv[2] = v-c3 ; + + //compute value + double local_fx = 0; + for (int i = 0; i < 3; ++i) { + for (int j = 0; j <= i; ++j) { + local_fx += sv[i].dot(sv[j]) ; + } + } + local_fx /= 6 ; + + //compute gradient + + //centroid vector + Vector g_vect = (c1+c2+c3)/3. ; + g_vect = (v-g_vect) ; + + //anisotropy + if(anisotropy_ > 1) { + //normal component of the face-site vectors + Vector normal_component = v-c1 ; + normal_component -= normal_component.dot(tb1)*tb1 ; + normal_component -= normal_component.dot(tb2)*tb2 ; + ////FIXME keep component in normal space ? + //for(int i = 3; i < Dim; ++i) { + // normal_component[i] = 0 ; + //} + + //value modification + local_fx += (anisotropy_*anisotropy_-1) + * normal_component.dot(normal_component) ; + + //gradient modification + g_vect += (anisotropy_*anisotropy_-1) + * normal_component ; + } + + //store result, normalize by mesh area + fx_ += (factor_/mesh_area_)*area*local_fx ; + Eigen::Map<Vector> grad(g_ + Dim*site) ; + grad += factor_*area*2*g_vect/mesh_area_ ; + } + + void edge_compute( const unsigned int site, + const unsigned int face, + const RVDEdge<Triangulation>& e1, + const RVDEdge<Triangulation>& e2, + const Vector& inner_point + ) { + if(anisotropy_<=1) return ; + + //bisector containing the edge + const unsigned int neighbour = e1.combinatorics ; + + //handling edges only once + if (neighbour<site) return ; + + //sites + Eigen::Map<const Vector> v1(sites_ + Dim*site) ; + Eigen::Map<const Vector> v2(sites_ + Dim*neighbour) ; + + //vertices of the edge + const Vector& c1 = e1.vertex ; + const Vector& c2 = e2.vertex ; + + //edge vector + Vector edge = c2-c1 ; + double edge_len = edge.norm() ; + if(edge_len <= tolerance_) return ; + edge /= edge_len ; + + //triangle weighting + const double tweight = triangle_weights_ == NULL ? + 1 : + triangle_weights_[face] ; + edge_len *= tweight ; + + //edge normal + Vector edge_normal = c1-inner_point ; + edge_normal -= edge_normal.dot(edge)*edge ; + const double edge_normal_len = edge_normal.norm() ; + if(edge_normal_len <= tolerance_) return ; + edge_normal /= edge_normal_len ; + + const double boundary_speed_denom = edge_normal.dot(v1-v2) ; + const double abs_bsd = boundary_speed_denom < 0 ? + - boundary_speed_denom : + boundary_speed_denom ; + if(abs_bsd <= tolerance_) return ; + + //site vector normal component + Vector nc1 = v1-c1 ; + nc1 -= nc1.dot(edge)*edge ; + nc1 -= nc1.dot(edge_normal)*edge_normal ; + Vector nc2 = v2-c1 ; + nc2 -= nc2.dot(edge)*edge ; + nc2 -= nc2.dot(edge_normal)*edge_normal ; + ////FIXME keep component in normal space ? + //for(int i = 3; i < Dim; ++i) { + // nc1[i] = 0 ; + // nc2[i] = 0 ; + //} + + //gradient common part + const double tmp = (anisotropy_*anisotropy_-1) + * (nc1.dot(nc1)-nc2.dot(nc2)) + / boundary_speed_denom + * edge_len + //normalize by the mesh area FIXME for triangle weights + * factor_/mesh_area_ ; + + //edge center + const Vector edge_center = 0.5*(c1+c2) ; + + //gradient for v1 + Eigen::Map<Vector> grad1(g_ + Dim*site) ; + grad1 += tmp*(v1-edge_center) ; + + //gradient for v2 + Eigen::Map<Vector> grad2(g_ + Dim*neighbour) ; + grad2 -= tmp*(v2-edge_center) ; + } + +/* FIXME + void border_compute( const unsigned int site, + const RVD::RVDEdge& e1, + const RVD::RVDEdge& e2 + ) ; + + void border_vertex_compute( const unsigned int site, + const RVD::RVDVertex& c, + const Vector3d& edge + ) ; +*/ +} ; + +/** Inverse computer **/ + +/* Compute the value and gradient of the inverse CVT objective function when + * passed to the corresponding RVD computer. The inverse CVT objective function + * optimizes a mesh such that a given sampling regularly samples it.*/ + +template<typename _Triangulation> +class InverseComputer : public BaseComputer<_Triangulation> { + + public : + + /* Typedefs and template arguments */ + typedef _Triangulation Triangulation ; + enum { Dim = Triangulation::VertexDim } ; + typedef Eigen::Matrix<double,Dim,1> Vector ; + typedef Eigen::Matrix<double,Dim,Dim> Matrix ; + + typedef RVDVertex<Triangulation> Vertex ; + + typedef BaseComputer<Triangulation> Base ; + + InverseComputer() : BaseComputer<Triangulation>() {} + + void operator()( unsigned int site, + unsigned int face, + const RVDPolygon<Triangulation>& polygon + ) { + //number of vertices of the polygon + const unsigned int size = polygon.size() ; + + if(size < 3) return ; + + //send triangles to the triangle backend + for(unsigned int i=1; i<size-1; ++i) { + triangle_compute(site, face, polygon[0], polygon[i], polygon[i+1]) ; + } + } + + void add_mesh_area_gradient() { + //add the contribution of the mesh area gradient + mesh_area_gradient(mesh_, g_, -fx_ / mesh_area_) ; + } + + void finalize() { + add_mesh_area_gradient() ; + } + + private : + + using Base::factor_ ; + using Base::anisotropy_ ; + using Base::tolerance_ ; + using Base::mesh_area_ ; + using Base::fx_ ; + using Base::g_ ; + using Base::sites_ ; + using Base::mesh_ ; + + std::vector<double> area_gradient_ ; + + void triangle_compute( const unsigned int site, + const unsigned int face, + const RVDEdge<Triangulation>& e1, + const RVDEdge<Triangulation>& e2, + const RVDEdge<Triangulation>& e3 + ) { + //vertices of the triangle + const RVDVertex<Triangulation>* c[3] ; + c[0] = &e1.vertex ; + c[1] = &e2.vertex ; + c[2] = &e3.vertex ; + + //normalized edges of the triangle + Vector ed[3] ; + double ed_len[3] ; + for(int i=0; i<3; ++i) { + ed[i] = *(c[(i+2)%3]) - *(c[(i+1)%3]) ; + ed_len[i] = ed[i].norm() ; + if(ed_len[i] <= tolerance_) return ; + ed[i] /= ed_len[i] ; + } + + //normalized heights of the triangle + Vector h[3] ; + double h_len[3] ; + for(int i=0; i<3; ++i) { + h[i] = (ed[(i+1)%3] - (ed[(i+1)%3].dot(ed[i])*ed[i]))*ed_len[(i+1)%3] ; + h_len[i] = h[i].norm() ; + if(h_len[i] <= tolerance_) return ; + h[i] /= h_len[i] ; + } + + //area of the triangle + double area = 0.5*h_len[0]*ed_len[0] ; + if(area <= tolerance_) return ; + + //site + Eigen::Map<const Vector> v(sites_ + Dim*site) ; + + //site vectors + Vector sv[3] ; + for(int i = 0; i < 3; ++i) { + sv[i] = v-*(c[i]) ; + } + + //unweighted objective function value + double local_fx = 0 ; + for(int i=0; i<3; ++i) { + for(int j=0; j<=i; ++j) { + local_fx += sv[i].dot(sv[j]) ; + } + } + local_fx /= 6. ; + + //normal component of the site vector + const Vector nv = *(c[0]) - v - v.dot(ed[0])*ed[0] - v.dot(h[0])*h[0] ; + + if(anisotropy_ > 1) { + local_fx += (anisotropy_*anisotropy_ - 1)*nv.dot(nv) ; + } + + //weighted should add local_fx *= area which is done later to use this + //intermediate result for gradient computations. + + //gradient of the objective function + Vector vertex_gradient ; + const Vector g = *(c[0]) + *(c[1]) + *(c[2]) - 4*v ; + for(int i=0; i<3; ++i) { + //gradient wrt. the vertex c[i] of the triangle + + //variations of the integrand wrt. c[i] + vertex_gradient = *(c[i]) + g ; + + if(anisotropy_ > 1) { + vertex_gradient -= + 12*(anisotropy_*anisotropy_-1)/h_len[i]*(*(c[(i+1)%3])-v).dot(h[i])*nv ; + } + + vertex_gradient *= area/6. ; + + //variation of the triangle area wrt. c[i] + vertex_gradient += 0.5*ed_len[i]*h[i]*local_fx ; + + //composing to get the gradient wrt. the mesh vertices + //number of mesh vertices involved in this RVD vertex + if(c[i]->config() == Vertex::ORIGINAL) { + //original vertex of the mesh + //grab the portion of the gradient wrt. the mesh vertex + Eigen::Map<Vector> vert_g(g_ + Dim*c[i]->combinatorics()[0]) ; + vert_g += factor_ * vertex_gradient / mesh_area_ ; + } else { + //get the number of mesh vertices involved + int comb_size = (c[i]->config() == Vertex::FACE_VERTEX) ? 3 : 2 ; + + //for each vertex in the combinatorics, get the derivative of the RVD + //Vertex wrt. the mesh vertex, and compose the derivatives. + Matrix diff_compose ; + for(int j=0; j<comb_size; ++j) { + //get the derivative + c[i]->derivative(j, mesh_, sites_, diff_compose.data()) ; + //grab the portion of the gradient wrt. the mesh vertex + Eigen::Map<Vector> vert_g(g_ + Dim*c[i]->combinatorics()[j]) ; + //compose the gradient + vert_g += factor_*diff_compose.transpose()*vertex_gradient / mesh_area_ ; + } + } + } + + //finishing the objective function value computation + fx_ += factor_ * area * local_fx / mesh_area_ ; + } +} ; + +} //end of namespace CVT +} //end of namespace Revoropt + +#endif + diff --git a/contrib/Revoropt/include/Revoropt/CVT/minimizer.hpp b/contrib/Revoropt/include/Revoropt/CVT/minimizer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..88d2754874043e5352ddf75caee3b6a194a3ba2a --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/CVT/minimizer.hpp @@ -0,0 +1,292 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_CVT_MINIMIZER_H_ +#define _REVOROPT_CVT_MINIMIZER_H_ + +#include "computer.hpp" + +#include <Revoropt/RVD/rvd.hpp> +#include <Revoropt/Mesh/wrapper_def.hpp> + +namespace Revoropt { +namespace CVT { + +/* Minimization of the direct CVT objective function */ + +template<typename _Mesh> +class DirectObjFun { + + public : + //typedefs and template information + typedef _Mesh Mesh ; + typedef typename Mesh::Scalar Scalar ; + enum { Dim = Mesh::VertexDim } ; + typedef ROTriangulationWrapper< + Dim, + typename Mesh::Scalar, + Mesh::VertexOffset + > Triangulation ; + + /* Initialization */ + + DirectObjFun( bool reset_gradient = true ) : + reset_gradient_(reset_gradient) { + } + + void set_vertices( const Scalar* vertices, unsigned int vertices_size ) { + triangulation_.set_vertices(vertices, vertices_size) ; + rvd_.set_mesh(&triangulation_) ; + computer_.set_mesh(&triangulation_) ; + } + + void set_mesh( const Mesh* mesh ) { + triangulation_.wrap(mesh) ; + rvd_.set_mesh(&triangulation_) ; + computer_.set_mesh(&triangulation_) ; + } + + void set_sites(const double* sites, unsigned int sites_size ) { + sites_ = sites ; + sites_size_ = sites_size ; + set_inner_sites(sites, sites_size) ; + } + + /* Computing the gradient and value of the CVT objective function with respect + * to the site positions */ + + /* given the mesh and the set of sites */ + + double operator() ( + const Mesh* mesh, + const double* sites, + unsigned int site_size, + double* grad + ) { + set_mesh(mesh) ; + set_sites(sites, site_size) ; + + return operator()(grad) ; + } + + /* using the sites and mesh provided using set_mesh and set_sites */ + + double operator() ( double* grad ) { + if(reset_gradient_) { + std::fill(grad, grad+Dim*sites_size_,0) ; + } + computer_.set_g(grad) ; + computer_.set_fx(0) ; + + rvd_.compute(computer_) ; + + return computer_.get_fx() ; + } + + /* Solver callback, using the provided variables as site positions */ + template<typename Data> + double operator()( Data* data ) { + set_inner_sites(data->x, data->n/Dim) ; + return operator()(data->g) ; + } + + /* Parameters */ + + void set_factor( double factor ) { + computer_.set_factor(factor) ; + } + void set_anisotropy( double anisotropy ) { + computer_.set_anisotropy(anisotropy) ; + } + void set_border_anisotropy( double border_anisotropy ) { + computer_.set_border_anisotropy(border_anisotropy) ; + } + void set_tolerance( double tolerance ) { + computer_.set_tolerance(tolerance) ; + } + void set_border_weight( double border_weight ) { + computer_.set_border_weight(border_weight) ; + } + void set_triangle_weights( const double* triangle_weights) { + computer_.set_triangle_weights(triangle_weights) ; + } + + protected : + + void set_inner_sites( const double* sites, unsigned int sites_size ) { + rvd_.set_sites(sites, sites_size) ; + computer_.set_sites(sites) ; + } + + //sites + const double* sites_ ; + unsigned int sites_size_ ; + + //objective function computation + DirectComputer<Triangulation> computer_ ; + bool reset_gradient_ ; + + //Restricted Voronoï diagram + RVD<Triangulation> rvd_ ; + + //Triangulation wrapper + Triangulation triangulation_ ; + +} ; + +template<typename _Mesh> +class DirectMinimizer : public DirectObjFun<_Mesh> { + + public : + enum { Dim = _Mesh::VertexDim } ; + + DirectMinimizer() : DirectObjFun<_Mesh>(true) {} + + void set_sites(double* sites, unsigned int sites_size ) { + var_sites_ = sites ; + DirectObjFun<_Mesh>::set_sites(sites, sites_size) ; + } + + /* minimization */ + template<typename Solver> + int minimize( unsigned int iterations ) { + Solver solver ; + return solver.solve(var_sites_, Dim*sites_size_, this, iterations) ; + } + + template<typename Solver, typename IterCallback> + int minimize( unsigned int iterations, IterCallback* iter_callback ) { + Solver solver ; + return solver.solve(var_sites_, Dim*sites_size_, this, iterations, iter_callback) ; + } + + private : + + using DirectObjFun<_Mesh>::sites_size_ ; + double* var_sites_ ; +} ; + +/* LBFGS callback for the minimization of the inverse CVT objective function */ + +template<typename _Mesh> +class InverseObjFun { + + public : + //typedefs and template information + typedef _Mesh Mesh ; + enum { Dim = Mesh::VertexDim } ; + typedef typename Mesh::Scalar Scalar ; + typedef ROTriangulationWrapper< + Dim, + typename Mesh::Scalar, + Mesh::VertexOffset + > Triangulation ; + + /* Initialization */ + + InverseObjFun( bool reset_gradient = true ) : + reset_gradient_(reset_gradient) { + } + + void set_vertices( const Scalar* vertices, unsigned int vertices_size ) { + triangulation_.set_vertices(vertices, vertices_size) ; + rvd_.set_mesh(&triangulation_) ; + computer_.set_mesh(&triangulation_) ; + } + + void set_mesh( Mesh* mesh ) { + triangulation_.wrap(mesh) ; + rvd_.set_mesh(&triangulation_) ; + computer_.set_mesh(&triangulation_) ; + } + + void set_sites( const Scalar* sites, unsigned int sites_size ) { + rvd_.set_sites(sites, sites_size) ; + computer_.set_sites(sites) ; + } + + /* Computing the gradient and value of the CVT objective function with respect + * to the mesh vertex positions */ + + /* given the mesh and the set of sites */ + + double operator() ( + const Mesh* mesh, + const Scalar* sites, + unsigned int site_size, + double* grad + ) { + set_mesh(mesh) ; + set_sites(sites, site_size) ; + + return operator()(grad) ; + } + + /* using the sites and mesh provided using set_mesh, set_sites or set_rvd */ + + double operator() ( double* grad ) { + if(reset_gradient_) { + std::fill(grad, grad+Dim*rvd_.mesh()->vertices_size(),0) ; + } + computer_.set_g(grad) ; + computer_.set_fx(0) ; + + rvd_.compute(computer_) ; + + return computer_.get_fx() ; + } + + /* Solver callback using the provided variables as mesh vertex positions */ + template<typename Data> + double operator()( Data* data ) { + set_vertices(data->x, data->n/Dim) ; + return operator()(data->g) ; + } + + /* Parameters */ + + void set_factor( double factor ) { + computer_.set_factor(factor) ; + } + void set_anisotropy( double anisotropy ) { + computer_.set_anisotropy(anisotropy) ; + } + void set_border_anisotropy( double border_anisotropy ) { + computer_.set_border_anisotropy(border_anisotropy) ; + } + void set_tolerance( double tolerance ) { + computer_.set_tolerance(tolerance) ; + } + void set_border_weight( double border_weight ) { + computer_.set_border_weight(border_weight) ; + } + void set_triangle_weights( const double* triangle_weights) { + computer_.set_triangle_weights(triangle_weights) ; + } + + private : + + //triangulation wrapper + Triangulation triangulation_ ; + + //objective function computation + InverseComputer<Triangulation> computer_ ; + bool reset_gradient_ ; + + //restricted Voronoï diagram + RVD<Triangulation> rvd_ ; + +} ; + +} //end of namespace CVT +} //end of namespace Revoropt + +#endif + diff --git a/contrib/Revoropt/include/Revoropt/Mesh/all_def.hpp b/contrib/Revoropt/include/Revoropt/Mesh/all_def.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3d6fc2bf4d076578f60880491f26b014be82ff89 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/all_def.hpp @@ -0,0 +1,21 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt + +#ifndef _REVOROPT_MESH_ALL_DEF_HPP_ +#define _REVOROPT_MESH_ALL_DEF_HPP_ + +#include "base_def.hpp" +#include "wrapper_def.hpp" +#include "assembler_def.hpp" +#include "builder_def.hpp" +#include "connectivity_def.hpp" + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/all_fwd.hpp b/contrib/Revoropt/include/Revoropt/Mesh/all_fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f9ec6cdc80afb382984e0a20cd5ba7498d03e00e --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/all_fwd.hpp @@ -0,0 +1,21 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt + +#ifndef _REVOROPT_MESH_ALL_FWD_HPP_ +#define _REVOROPT_MESH_ALL_FWD_HPP_ + +#include "base_fwd.hpp" +#include "wrapper_fwd.hpp" +#include "assembler_fwd.hpp" +#include "builder_fwd.hpp" +#include "connectivity_fwd.hpp" + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/assembler_def.hpp b/contrib/Revoropt/include/Revoropt/Mesh/assembler_def.hpp new file mode 100644 index 0000000000000000000000000000000000000000..0f0e45b5d08b0e9300955a82c182f1a25b3ee0bc --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/assembler_def.hpp @@ -0,0 +1,16 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_ASSEMBLER_DEF_HPP_ +#define _REVOROPT_MESH_ASSEMBLER_DEF_HPP_ + +#include "assembler_fwd.hpp" + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/assembler_fwd.hpp b/contrib/Revoropt/include/Revoropt/Mesh/assembler_fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..964aa0bc708ae66ee3a7f0945ac1d09368e77b92 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/assembler_fwd.hpp @@ -0,0 +1,305 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_ASSEMBLER_H_ +#define _REVOROPT_MESH_ASSEMBLER_H_ + +#include "base_def.hpp" +#include "connectivity_def.hpp" + +namespace Revoropt { + +/*** Mesh assembler, computes and internally holds face neighbourhoods ***/ + +/** Read only version **/ + +/** Fixed face size case **/ + +template< + int _FaceSize = 3, + int _VertexDim = 3, + typename _Scalar = double, + int _VertexOffset = 0 +> +class ROMeshAssembler : + public ROMesh<_FaceSize,_VertexDim,_Scalar,_VertexOffset> +{ + public: + + typedef ROMesh<_FaceSize,_VertexDim,_Scalar,_VertexOffset> Base ; + + typedef _Scalar Scalar ; + + enum { + FaceSize = _FaceSize, + VertexDim = _VertexDim, + VertexOffset = _VertexOffset + } ; + + using Base::NO_NEIGHBOUR ; + + /** Construction **/ + ROMeshAssembler() : Base(), c_computer_(this) {} ; + + /* From raw arrays */ + + ROMeshAssembler( const Scalar* vertices, + unsigned int vertices_size, + const unsigned int* faces, + unsigned int faces_size + ) : Base( vertices, vertices_size, + faces, NULL, faces_size + ), + c_computer_(this) { + c_computer_.compute_connectivity() ; + Base::face_neighbourhoods_ = c_computer_.data() ; + }; + + /** Changing pointers **/ + + /* Vertex pointer */ + void set_vertices( const Scalar* vertices, + unsigned int vertices_size + ) { + Base::vertices_ = vertices ; + Base::vertices_size_ = vertices_size ; + Base::set_dirty() ; + } + + /* Face pointers */ + void set_faces( const unsigned int* faces, + unsigned int faces_size + ) { + Base::faces_ = faces ; + Base::faces_size_ = faces_size ; + c_computer_.compute_connectivity() ; + Base::face_neighbourhoods_ = c_computer_.data() ; + Base::set_dirty() ; + } + private: + + ConnectivityComputer<Base> c_computer_ ; + +} ; + +/** Variable face size case **/ + +template< + int _VertexDim, + typename _Scalar, + int _VertexOffset +> +class ROMeshAssembler<Variable,_VertexDim,_Scalar,_VertexOffset> : + public ROMesh<Variable,_VertexDim,_Scalar,_VertexOffset> +{ + public: + + typedef ROMesh<Variable,_VertexDim,_Scalar,_VertexOffset> Base ; + + typedef _Scalar Scalar ; + + enum { + FaceSize = Variable, + VertexDim = _VertexDim, + VertexOffset = _VertexOffset + } ; + + using Base::NO_NEIGHBOUR ; + + /** Construction **/ + ROMeshAssembler() : Base(), c_computer_(this) {} ; + + /* From raw arrays */ + + ROMeshAssembler( const Scalar* vertices, + unsigned int vertices_size, + const unsigned int* faces, + const unsigned int* face_ends, + unsigned int faces_size + ) : Base( vertices, vertices_size, + faces, face_ends, NULL, faces_size + ), + c_computer_(this) { + c_computer_.compute_connectivity() ; + Base::face_neighbourhoods_ = c_computer_.data() ; + }; + + /** Changing pointers **/ + + /* Vertex pointer */ + void set_vertices( const Scalar* vertices, + unsigned int vertices_size + ) { + Base::vertices_ = vertices ; + Base::vertices_size_ = vertices_size ; + Base::set_dirty() ; + } + + /* Face pointers */ + void set_faces( const unsigned int* faces, + const unsigned int* face_ends, + unsigned int faces_size + ) { + Base::faces_ = faces ; + Base::face_ends_ = face_ends ; + Base::faces_size_ = faces_size ; + c_computer_.compute_connectivity() ; + Base::face_neighbourhoods_ = c_computer_.data() ; + Base::set_dirty() ; + } + private: + + ConnectivityComputer<Base> c_computer_ ; + +} ; + +/** Read write version **/ + +/** Fixed face size case **/ + +template< + int _FaceSize = 3, + int _VertexDim = 3, + typename _Scalar = double, + int _VertexOffset = 0 +> +class MeshAssembler : public Mesh<_FaceSize,_VertexDim,_Scalar,_VertexOffset> +{ + public: + + typedef Mesh<_FaceSize,_VertexDim,_Scalar,_VertexOffset> Base ; + + typedef _Scalar Scalar ; + + enum { + FaceSize = _FaceSize, + VertexDim = _VertexDim, + VertexOffset = _VertexOffset + } ; + + using Base::NO_NEIGHBOUR ; + + /** Construction **/ + MeshAssembler() : Base(), c_computer_(this) {} ; + + /* From raw arrays */ + + MeshAssembler( Scalar* vertices, + unsigned int vertices_size, + unsigned int* faces, + unsigned int faces_size + ) : Base( vertices, vertices_size, + faces, NULL, faces_size + ), + c_computer_(this) { + c_computer_.compute_connectivity() ; + Base::face_neighbourhoods_ = c_computer_.data() ; + }; + + /** Changing pointers **/ + + /* Vertex pointer */ + void set_vertices( Scalar* vertices, + unsigned int vertices_size + ) { + Base::vertices_ = vertices ; + Base::vertices_size_ = vertices_size ; + Base::set_dirty() ; + } + + /* Face pointers */ + void set_faces( unsigned int* faces, + unsigned int faces_size + ) { + Base::faces_ = faces ; + Base::faces_size_ = faces_size ; + c_computer_.compute_connectivity() ; + Base::face_neighbourhoods_ = c_computer_.data() ; + Base::set_dirty() ; + } + private: + + ConnectivityComputer<Base> c_computer_ ; + +} ; + +/** Variable face size case **/ + +template< + int _VertexDim, + typename _Scalar, + int _VertexOffset +> +class MeshAssembler<Variable,_VertexDim,_Scalar,_VertexOffset> : + public Mesh<Variable,_VertexDim,_Scalar,_VertexOffset> +{ + public: + + typedef Mesh<Variable,_VertexDim,_Scalar,_VertexOffset> Base ; + + typedef _Scalar Scalar ; + + enum { + FaceSize = Variable, + VertexDim = _VertexDim, + VertexOffset = _VertexOffset + } ; + + using Base::NO_NEIGHBOUR ; + + /** Construction **/ + MeshAssembler() : Base(), c_computer_(this) {} ; + + /* From raw arrays */ + MeshAssembler( Scalar* vertices, + unsigned int vertices_size, + unsigned int* faces, + unsigned int* face_ends, + unsigned int faces_size + ) : Base( vertices, vertices_size, + faces, face_ends, NULL, faces_size + ), + c_computer_(this) { + c_computer_.compute_connectivity() ; + Base::face_neighbourhoods_ = c_computer_.data() ; + }; + + /** Changing pointers **/ + + /* Vertex pointer */ + void set_vertices( Scalar* vertices, + unsigned int vertices_size + ) { + Base::vertices_ = vertices ; + Base::vertices_size_ = vertices_size ; + Base::set_dirty() ; + } + + /* Face pointers */ + void set_faces( unsigned int* faces, + unsigned int* face_ends, + unsigned int faces_size + ) { + Base::faces_ = faces ; + Base::face_ends_ = face_ends ; + Base::faces_size_ = faces_size ; + c_computer_.compute_connectivity() ; + Base::face_neighbourhoods_ = c_computer_.data() ; + Base::set_dirty() ; + } + private: + + ConnectivityComputer<Base> c_computer_ ; + +} ; + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/base_def.hpp b/contrib/Revoropt/include/Revoropt/Mesh/base_def.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e83551721d85fbe8e99eb429c132459bcf4e29cd --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/base_def.hpp @@ -0,0 +1,66 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_BASE_DEF_HPP_ +#define _REVOROPT_MESH_BASE_DEF_HPP_ + +#include "base_fwd.hpp" + +namespace Revoropt { + +/* Basic mesh with constant face size */ + +template< int FaceSize, int VertexDim, typename Scalar, int VertexOffset > +unsigned int ROMesh<FaceSize, VertexDim, Scalar, VertexOffset + >::edge_face_index( unsigned int face_index, + unsigned int v1, + unsigned int v2 + ) const { + //vertices of the face + const unsigned int* verts = face(face_index) ; + //loop over the vertices + for(unsigned int i = 0; i < FaceSize; ++i) { + unsigned int vi = verts[ i ] ; + unsigned int vj = verts[(i+1)%FaceSize] ; + if(((vi==v1)&&(vj==v2))||((vi==v2)&&(vj==v1))) { + return i ; + } + } + //if no return was issued before, the edge was not found + return NO_INDEX ; +} + +/* Basic mesh with variable face size */ + +template< int VertexDim, typename Scalar, int VertexOffset > +unsigned int ROMesh<Variable, VertexDim, Scalar, VertexOffset + >::edge_face_index( unsigned int face_index, + unsigned int v1, + unsigned int v2 + ) const { + //vertices of the face + const unsigned int* verts = face(face_index) ; + //size of the face + unsigned int fsize = face_size(face_index) ; + //loop over the vertices + for(unsigned int i = 0; i < fsize; ++i) { + unsigned int vi = verts[ i ] ; + unsigned int vj = verts[(i+1)%fsize] ; + if(((vi==v1)&&(vj==v2))||((vi==v2)&&(vj==v1))) { + return i ; + } + } + //if no return was issued before, the edge was not found + return NO_INDEX ; +} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/base_fwd.hpp b/contrib/Revoropt/include/Revoropt/Mesh/base_fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f1687ea8b1468833c27b96d2d5eab1cd84d086b6 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/base_fwd.hpp @@ -0,0 +1,677 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_BASE_FWD_HPP_ +#define _REVOROPT_MESH_BASE_FWD_HPP_ + +#include <eigen3/Eigen/Dense> + +#include <type_traits> + +namespace Revoropt { + +const int Variable = -1 ; + +/***{{{ Forward declarations for friendship ***/ +template< + int _VertexDim = 3, + typename _Scalar = double, + int _VertexOffset = 0 +> +class ROTriangulationWrapper ; + +template< + int _FaceSize = 3, + int _VertexDim = 3, + typename _Scalar = double, + int _VertexOffset = 0 +> +class ROMeshWrapper ; + +template< + int _FaceSize = 3, + int _VertexDim = 3, + typename _Scalar = double, + int _VertexOffset = 0 +> +class MeshWrapper ; + +template< + int _FaceSize = 3, + int _VertexDim = 3, + typename _Scalar = double +> +class MeshBuilder ; + +/* +template< + int _VertexDim = 3, + typename _Scalar = double, + int _VertexOffset = 0 +> +class TriangulationWrapper ; +*/ + +//}}} + +/***{{{ Base for all meshes, not for classical use ***/ + +template< + int _VertexDim = 3, /* Dimension of the vertices */ + typename _Scalar = double, /* Type of coordinates */ + int _VertexOffset = 0 /* Offset between vertices for easy projection */ +> +class ROMeshBase +{ + + public: + + friend class ROTriangulationWrapper<_VertexDim,_Scalar,_VertexOffset> ; + + typedef _Scalar Scalar ; + + enum { + VertexDim = _VertexDim, + VertexOffset = _VertexOffset + } ; + + typedef Eigen::Matrix<Scalar,VertexDim,1> Vector ; + + static const unsigned int NO_NEIGHBOUR ; + + virtual ~ROMeshBase() {} ; + + /** Protected construction this class is only meant to be derived **/ + protected: + ROMeshBase() : + vertices_(NULL), + vertices_size_(0), + faces_(NULL), + face_neighbourhoods_(NULL), + faces_size_(0), + dirty_(false) + {} + + ROMeshBase( const Scalar* vertices, + unsigned int vertices_size, + const unsigned int* faces, + const unsigned int* face_neighbourhoods, + unsigned int faces_size + ) : + vertices_(vertices), + vertices_size_(vertices_size), + faces_(faces), + face_neighbourhoods_(face_neighbourhoods), + faces_size_(faces_size), + dirty_(false) + {} + + public: + + /** Vertices **/ + /* coordinate array of a vertex */ + const Scalar* vertex( unsigned int vertex_index ) const { + return vertices_ + (_VertexDim + _VertexOffset) * vertex_index ; + } + + /* number of vertices */ + unsigned int vertices_size() const { + return vertices_size_ ; + } + + /* vertex access when no offset */ + template< typename T = const Scalar* > + typename std::enable_if< + _VertexOffset == 0 && std::is_same<T,const Scalar*>::value, + T + >::type vertices() const { + return vertices_ ; + } + + /** Data status **/ + bool is_dirty() const { return dirty_ ; } + void set_dirty() { dirty_ = true ; } + void set_clean() { dirty_ = false ; } + + protected: + + /** Data pointers **/ + /* Note : these pointers do not really point to const data, and writable + * meshes cast these to non const data. */ + + /* vertex array */ + const Scalar* vertices_ ; + + unsigned int vertices_size_ ; + + /* face array */ + const unsigned int* faces_ ; + + /* face neighbourhoods */ + const unsigned int* face_neighbourhoods_ ; + + unsigned int faces_size_ ; + + /** Notification of data change **/ + bool dirty_ ; + +} ; + +template< + int _VertexDim, + typename _Scalar, + int _VertexOffset +> +const unsigned int ROMeshBase<_VertexDim, _Scalar, _VertexOffset>::NO_NEIGHBOUR = -1 ; + +//}}} + +/***{{{ Read only mesh, a mesh that cannot be modified ***/ + +/*{{{ Default template parameters is triangulated 3D mesh. */ + +template< + int _FaceSize = 3, /* Vertices per face, use Variable if non constant */ + int _VertexDim = 3, /* Dimension of the vertices */ + typename _Scalar = double, /* Type of coordinates */ + int _VertexOffset = 0 /* Offset between vertices for easy projection */ +> +class ROMesh : public ROMeshBase<_VertexDim,_Scalar,_VertexOffset> +{ + public: + + friend class ROTriangulationWrapper<_VertexDim,_Scalar,_VertexOffset> ; + + template< + int _OtherSize, + int _OtherDim, + typename _OtherScalar, + int _OtherOffset + > + friend class ROMeshWrapper ; + + template< + int _OtherSize, + int _OtherDim, + typename _OtherScalar, + int _OtherOffset + > + friend class MeshWrapper ; + + template< + int _OtherSize, + int _OtherDim, + typename _OtherScalar + > + friend class MeshBuilder ; + + typedef ROMeshBase<_VertexDim,_Scalar,_VertexOffset> Base ; + + typedef _Scalar Scalar ; + + enum { + FaceSize = _FaceSize, + VertexDim = _VertexDim, + VertexOffset = _VertexOffset + } ; + + using Base::NO_NEIGHBOUR ; + static const unsigned int NO_INDEX = -1 ; + + /** Protected construction this class is only meant to be derived **/ + protected: + ROMesh() : Base() {} + + ROMesh( const Scalar* vertices, + unsigned int vertices_size, + const unsigned int* faces, + const unsigned int* face_neighbourhoods, + unsigned int faces_size + ) : Base( vertices, vertices_size, + faces, face_neighbourhoods, faces_size + ) { + } + + public: + + /** Faces **/ + + /* vertex arrays of all faces */ + const unsigned int* faces() const { + return Base::faces_ ; + } + + /* position of a face in the face array */ + unsigned int face_offset( unsigned int face_index ) const { + return FaceSize*face_index ; + } + + /* vertex index array of a face */ + const unsigned int* face( unsigned int face_index ) const { + return Base::faces_+face_offset(face_index) ; + } + + /* number of vertices of a face */ + unsigned int face_size( unsigned int face_index ) const { + return FaceSize ; + } + + /* number of faces */ + unsigned int faces_size() const { + return Base::faces_size_ ; + } + + /* number of faces */ + unsigned int face_vertices_size() const { + return FaceSize*Base::faces_size_ ; + } + + /** Edges **/ + + /* vertices of an edge */ + void face_edge_vertices( unsigned int face_index, + unsigned int edge, + unsigned int* v0, + unsigned int* v1 + ) const { + //size of the face + const unsigned int fsize = face_size(face_index) ; + //face vertices + const unsigned int* fverts = face(face_index) ; + //edge vertices + *v0 = fverts[ edge ] ; + *v1 = fverts[(edge+1)%fsize] ; + } + + unsigned int face_edge_end_vertex( unsigned int face_index, + unsigned int edge + ) const { + //edge vertices + return face(face_index)[(edge+1)%face_size(face_index)] ; + } + + unsigned int face_edge_start_vertex( unsigned int face_index, + unsigned int edge + ) const { + //face vertices + const unsigned int* fverts = face(face_index) ; + return fverts[edge] ; + } + + /** Neighbourhoods **/ + + /* neighbouring face index array of a face */ + /* the neighbouring face of index i is aside the given face along the edge + * starting at the vertex with index i within the face. */ + const unsigned int* face_neighbours( unsigned int face_index ) const { + return Base::face_neighbourhoods_ + face_offset(face_index) ; + } + + /* index of an edge given by its vertices in a face (NO_INDEX if none) */ + unsigned int edge_face_index( unsigned int face_index, + unsigned int v1, + unsigned int v2 + ) const ; + +} ; + +//}}} + +/*{{{ Specialization for meshes with irregular face sizes */ + +template< + int _VertexDim, + typename _Scalar, + int _VertexOffset +> +class ROMesh<Variable,_VertexDim,_Scalar,_VertexOffset> : + public ROMeshBase<_VertexDim,_Scalar,_VertexOffset> +{ + public: + + friend class ROTriangulationWrapper<_VertexDim,_Scalar,_VertexOffset> ; + + template< + int _OtherSize, + int _OtherDim, + typename _OtherScalar, + int _OtherOffset + > + friend class ROMeshWrapper ; + + template< + int _OtherSize, + int _OtherDim, + typename _OtherScalar, + int _OtherOffset + > + friend class MeshWrapper ; + + template< + int _OtherSize, + int _OtherDim, + typename _OtherScalar + > + friend class MeshBuilder ; + + typedef ROMeshBase<_VertexDim,_Scalar,_VertexOffset> Base ; + + typedef _Scalar Scalar ; + + enum { + FaceSize = Variable, + VertexDim = _VertexDim, + VertexOffset = _VertexOffset + } ; + + using Base::NO_NEIGHBOUR ; + static const unsigned int NO_INDEX = -1 ; + + /** Protected construction this class is only meant to be derived **/ + protected: + ROMesh() : Base() {} + + ROMesh( const Scalar* vertices, + unsigned int vertices_size, + const unsigned int* faces, + const unsigned int* face_ends, + const unsigned int* face_neighbourhoods, + unsigned int faces_size + ) : Base( vertices, vertices_size, + faces, face_neighbourhoods, faces_size + ), + face_ends_(face_ends) { + } + + public: + + /** Faces **/ + + /* vertex arrays of all faces */ + const unsigned int* faces() const { + return Base::faces_ ; + } + const unsigned int* face_ends() const { + return face_ends_ ; + } + + /* position of a face in the face array */ + unsigned int face_offset( unsigned int face_index ) const { + return face_index == 0 ? + 0 : + face_ends_[face_index-1] ; + } + + /* vertex index array of a face */ + const unsigned int* face( unsigned int face_index ) const { + return Base::faces_+face_offset(face_index) ; + } + + /* number of vertices of a face */ + unsigned int face_size( unsigned int face_index ) const { + return face_index == 0 ? + face_ends_[face_index] : + face_ends_[face_index] - face_ends_[face_index-1] ; + } + + /* number of faces */ + unsigned int faces_size() const { + return Base::faces_size_ ; + } + + /* number of faces */ + unsigned int face_vertices_size() const { + return Base::faces_size_ > 0 ? + face_ends_[Base::faces_size_-1] : + 0 ; + } + + /** Edges **/ + + /* vertices of an edge */ + void face_edge_vertices( unsigned int face_index, + unsigned int edge, + unsigned int* v0, + unsigned int* v1 + ) const { + //size of the face + const unsigned int fsize = face_size(face_index) ; + //face vertices + const unsigned int* fverts = face(face_index) ; + //edge vertices + *v0 = fverts[ edge ] ; + *v1 = fverts[(edge+1)%fsize] ; + } + + unsigned int face_edge_end_vertex( unsigned int face_index, + unsigned int edge + ) const { + //size of the face + const unsigned int fsize = face_size(face_index) ; + //face vertices + const unsigned int* fverts = face(face_index) ; + //edge vertices + return fverts[(edge+1)%fsize] ; + } + + unsigned int face_edge_start_vertex( unsigned int face_index, + unsigned int edge + ) const { + //face vertices + const unsigned int* fverts = face(face_index) ; + return fverts[edge] ; + } + + /** Neighbourhoods **/ + + /* neighbouring face index array of a face */ + /* the neighbouring face of index i is aside the given face along the edge + * starting at the vertex with index i within the face. */ + const unsigned int* face_neighbours( unsigned int face_index ) const { + return Base::face_neighbourhoods_ + face_offset(face_index) ; + } + + /* index of an edge given by its vertices in a face (NO_INDEX if none) */ + unsigned int edge_face_index( unsigned int face_index, + unsigned int v1, + unsigned int v2 + ) const ; + + protected: + + /* face positions in face array, null unless variable case */ + const unsigned int* face_ends_ ; + +} ; + +//}}} + +//}}} + +/***{{{ Read Write mesh, allowing the vertices and faces to be modified ***/ + +/**{{{ Fixed size faces case **/ + +template< + int _FaceSize = 3, + int _VertexDim = 3, + typename _Scalar = double, + int _VertexOffset = 0 +> +class Mesh : public ROMesh<_FaceSize,_VertexDim,_Scalar,_VertexOffset> +{ + public: + + typedef ROMesh<_FaceSize,_VertexDim,_Scalar,_VertexOffset> Base ; + + typedef _Scalar Scalar ; + + enum { + FaceSize = _FaceSize, + VertexDim = _VertexDim, + VertexOffset = _VertexOffset + } ; + + using Base::NO_NEIGHBOUR ; + + /** Protected construction this class is only meant to be derived **/ + /* Initialization from const is disabled */ + protected: + Mesh() : Base() {} + + Mesh( Scalar* vertices, + unsigned int vertices_size, + unsigned int* faces, + unsigned int* face_neighbourhoods, + unsigned int faces_size + ) : Base( vertices, vertices_size, + faces, face_neighbourhoods, faces_size + ) { + } + + public: + + /** Vertices **/ + + using Base::vertices ; + /* editable full vertex array when offset is 0 */ + template< typename T = Scalar* > + typename std::enable_if< + _VertexOffset == 0 && std::is_same<T,Scalar*>::value, + T + >::type vertices() { + return const_cast<T>(Base::vertices()) ; + } + + using Base::vertex ; + /* editable coordinate array of a vertex */ + Scalar* vertex( unsigned int vertex_index ) { + return const_cast<_Scalar*>(Base::vertex(vertex_index)) ; + } + + /** Faces **/ + using Base::faces ; + /* editable faces array */ + unsigned int* faces() { + return const_cast<unsigned int*>(Base::faces()) ; + } + + using Base::face ; + /* editable vertex index array of a face */ + unsigned int* face( unsigned int face_index ) { + return const_cast<unsigned int*>(Base::face(face_index)) ; + } + + /** Neighbourhoods **/ + + using Base::face_neighbours ; + /* editable neighbouring face index array of a face */ + unsigned int* face_neighbours( unsigned int face_index ) { + return const_cast<unsigned int*>(Base::face_neighbours(face_index)) ; + } +} ; + +//}}} + +/**{{{ Variable size faces case **/ + +template< + int _VertexDim, + typename _Scalar, + int _VertexOffset +> +class Mesh<Variable,_VertexDim,_Scalar,_VertexOffset> : + public ROMesh<Variable,_VertexDim,_Scalar,_VertexOffset> +{ + public: + + typedef ROMesh<Variable,_VertexDim,_Scalar,_VertexOffset> Base ; + + typedef _Scalar Scalar ; + + enum { + FaceSize = Variable, + VertexDim = _VertexDim, + VertexOffset = _VertexOffset + } ; + + using Base::NO_NEIGHBOUR ; + + /** Protected construction this class is only meant to be derived **/ + /* Initialization from const is disabled */ + protected: + Mesh() : Base() {} + + Mesh( Scalar* vertices, + unsigned int vertices_size, + unsigned int* faces, + unsigned int* face_ends, + unsigned int* face_neighbourhoods, + unsigned int faces_size + ) : Base( vertices, vertices_size, + faces, face_ends, face_neighbourhoods, faces_size + ) { + } + + public: + + /** Vertices **/ + + using Base::vertices ; + /* editable full vertex array when offset is 0 */ + template< typename T = Scalar* > + typename std::enable_if< + _VertexOffset == 0 && std::is_same<T,Scalar*>::value, + T + >::type vertices() { + return const_cast<T>(Base::vertices()) ; + } + + using Base::vertex ; + /* editable coordinate array of a vertex */ + Scalar* vertex( unsigned int vertex_index ) { + return const_cast<_Scalar*>(Base::vertex(vertex_index)) ; + } + + /** Faces **/ + using Base::faces ; + unsigned int* faces() { + return const_cast<unsigned int*>(Base::faces()) ; + } + + using Base::face_ends ; + unsigned int* face_ends() { + return const_cast<unsigned int*>(Base::face_ends()) ; + } + + using Base::face ; + + /* editable vertex index array of a face */ + unsigned int* face( unsigned int face_index ) { + return const_cast<unsigned int*>(Base::face(face_index)) ; + } + + /** Neighbourhoods **/ + + using Base::face_neighbours ; + + /* editable neighbouring face index array of a face */ + unsigned int* face_neighbours( unsigned int face_index ) { + return const_cast<unsigned int*>(Base::face_neighbours(face_index)) ; + } +} ; + +//}}} + +//}}} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/builder_def.hpp b/contrib/Revoropt/include/Revoropt/Mesh/builder_def.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b64869cbda464e41623d890dc471aee4f00fd842 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/builder_def.hpp @@ -0,0 +1,373 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_BUILDER_DEF_HPP_ +#define _REVOROPT_MESH_BUILDER_DEF_HPP_ + +#include "builder_fwd.hpp" +#include "connectivity_def.hpp" + +namespace Revoropt { + +/*** {{{ constant face size case ***/ + +template< int _FaceSize, int _VertexDim, typename _Scalar > +template< int OtherDim, int OtherOffset > +void MeshBuilder<_FaceSize,_VertexDim,_Scalar>::init( const ROMesh< _FaceSize, + OtherDim, + _Scalar, + OtherOffset + >* rhs) { + //vertex copy + //automatic projection if dimensions do not match + if(OtherDim + OtherOffset == VertexDim) { + //linear copy + v_vector_.assign( rhs->vertices(), + rhs->vertices()+VertexDim*rhs->vertices_size() + ) ; + //reset vertex pointer and size + Base::vertices_ = v_vector_.data() ; + Base::vertices_size_ = rhs->vertices_size() ; + } else { + //number of coordinates that can be copied + const int copy_size = std::min((int)VertexDim,OtherDim+OtherOffset) ; + //resize vertex vector + v_vector_.resize(VertexDim*rhs->vertices_size()) ; + //reset vertex pointer and size + Base::vertices_ = v_vector_.data() ; + Base::vertices_size_ = rhs->vertices_size() ; + for(unsigned int vertex = 0; vertex < rhs->vertices_size(); ++vertex) { + //copy vertex positions + std::copy( rhs->vertex(vertex), + rhs->vertex(vertex)+copy_size, + Base::vertex(vertex) + ) ; + //add zeros if necessary + std::fill( Base::vertex(vertex)+copy_size, + Base::vertex(vertex)+VertexDim, + 0 + ) ; + } + } + //face copy + f_vector_.assign(rhs->faces(),rhs->faces() + rhs->face_vertices_size()) ; + //reset face pointer and size + Base::faces_ = f_vector_.data() ; + Base::faces_size_ = rhs->faces_size() ; + //recompute neighbourhoods + c_computer_.compute_connectivity() ; + Base::face_neighbourhoods_ = c_computer_.data() ; + Base::set_dirty() ; +} + +template< int FaceSize, int VertexDim, typename Scalar > +unsigned int MeshBuilder<FaceSize,VertexDim,Scalar>::push_vertices( + const Scalar* vertices, unsigned int size +) { + //resize the vertex container + const unsigned int start = v_vector_.size() ; + v_vector_.resize(start + VertexDim*size) ; + //copy he provided vertices + std::copy(vertices, vertices+VertexDim*size, v_vector_.begin() + start) ; + //update pointer and size + Base::vertices_ = v_vector_.data() ; + Base::vertices_size_ += size ; + Base::set_dirty() ; + //return a pointer to the first vertex pushed + return start/VertexDim ; +} + +template< int FaceSize, int VertexDim, typename Scalar > +unsigned int MeshBuilder<FaceSize,VertexDim,Scalar>::extend_vertices( + unsigned int size +) { + //resize the vertex container + const unsigned int start = v_vector_.size() ; + v_vector_.resize(start + VertexDim*size) ; + //update pointer and size + Base::vertices_ = v_vector_.data() ; + Base::vertices_size_ += size ; + Base::set_dirty() ; + //return a pointer to the first vertex pushed + return start/VertexDim ; +} + +template< int FaceSize, int VertexDim, typename Scalar > +void MeshBuilder<FaceSize,VertexDim,Scalar>::erase_vertices( unsigned int start, + unsigned int end + ) { + if(end > start) { + v_vector_.erase( v_vector_.begin() + VertexDim*start, + v_vector_.begin() + VertexDim*end + ) ; + Base::vertices_ = v_vector_.data() ; + Base::vertices_size_ -= end - start ; + Base::set_dirty() ; + } +} + +template< int FaceSize, int VertexDim, typename Scalar > +unsigned int MeshBuilder<FaceSize,VertexDim,Scalar>::push_faces( + const unsigned int* faces, unsigned int size +) { + //starting index of the new faces + const unsigned int start = Base::faces_size_ ; + //original face vertices size + const unsigned int fv_start = FaceSize*start ; + //append the faces + f_vector_.resize(fv_start + FaceSize*size) ; + std::copy(faces, faces + FaceSize*size, f_vector_.begin() + fv_start) ; + //reset face pointer and size + Base::faces_ = f_vector_.data() ; + Base::faces_size_ += size ; + //glue the new faces + c_computer_.resize(fv_start + FaceSize*size) ; + glue_faces(start,start+size) ; + Base::set_dirty() ; + //return pointer to the first face added + return start ; +} + +template< int _FaceSize, int _VertexDim, typename _Scalar > +unsigned int MeshBuilder<_FaceSize,_VertexDim,_Scalar>::extend_faces( + unsigned int size, unsigned int fsize +) { + assert( (fsize == FaceSize) && + "Trying to extend a fixed face size mesh " + "with a face of the wrong size." + ) ; + //starting index of the new faces + const unsigned int start = f_vector_.size() ; + //append the faces + f_vector_.resize(start + FaceSize*size) ; + //reset face pointer and size + Base::faces_ = f_vector_.data() ; + Base::faces_size_ += size ; + //extend neighbourhoods for the new faces + c_computer_.resize(start + FaceSize*size) ; + Base::face_neighbourhoods_ = c_computer_.data() ; + Base::set_dirty() ; + //return pointer to the first face added + return start/FaceSize ; +} + +template< int _FaceSize, int _VertexDim, typename _Scalar > +void MeshBuilder<_FaceSize,_VertexDim,_Scalar>::erase_faces( unsigned int start, + unsigned int end + ) { + if(end > start) { + c_computer_.erase_neighbourhoods(start,end) ; + Base::face_neighbourhoods_ = c_computer_.data() ; + f_vector_.erase( f_vector_.begin() + FaceSize*start, + f_vector_.begin() + FaceSize*end + ) ; + Base::faces_ = f_vector_.data() ; + Base::faces_size_ -= end - start ; + Base::set_dirty() ; + } +} + +//}}} + +/*** {{{ variable face size case ***/ + +template< int _VertexDim, typename _Scalar > +template< int OtherDim, int OtherOffset > +void MeshBuilder<Variable,_VertexDim,_Scalar>::init( const ROMesh< Variable, + OtherDim, + _Scalar, + OtherOffset + >* rhs) { + //vertex copy + //automatic projection if dimensions do not match + if(OtherDim + OtherOffset == VertexDim) { + //linear copy + v_vector_.assign( rhs->vertices(), + rhs->vertices() + VertexDim*rhs->vertices_size()) ; + //reset vertex pointer and size + Base::vertices_ = v_vector_.data() ; + Base::vertices_size_ = rhs->vertices_size() ; + } else { + //number of coordinates that can be copied + const int copy_size = std::min((int)VertexDim,OtherDim+OtherOffset) ; + //resize vertex vector + v_vector_.resize(VertexDim*rhs->vertices_size()) ; + //reset vertex pointer and size + Base::vertices_ = v_vector_.data() ; + Base::vertices_size_ = rhs->vertices_size() ; + for(unsigned int vertex = 0; vertex < rhs->vertices_size(); ++vertex) { + Scalar* v = Base::vertex(vertex) ; + //copy vertex positions + std::copy( rhs->vertex(vertex), + rhs->vertex(vertex)+copy_size, + v + ) ; + //add zeros if necessary + std::fill( v+copy_size, + v+VertexDim, + 0 + ) ; + } + } + //face copy + f_vector_.assign(rhs->faces_,rhs->faces_ + rhs->face_vertices_size()) ; + fe_vector_.assign(rhs->face_ends_,rhs->face_ends_ + rhs->faces_size_) ; + //reset face pointer and size + Base::faces_ = f_vector_.data() ; + Base::face_ends_ = fe_vector_.data() ; + Base::faces_size_ = rhs->faces_size_ ; + //recompute neighbourhoods + c_computer_.compute_connectivity() ; + Base::face_neighbourhoods_ = c_computer_.data() ; + Base::set_dirty() ; +} + +template< int _VertexDim, typename _Scalar > +unsigned int MeshBuilder<Variable,_VertexDim,_Scalar>::push_vertices( + const Scalar* vertices, unsigned int size +) { + //resize the vertex container + const unsigned int start = v_vector_.size() ; + v_vector_.resize(start + VertexDim*size) ; + //copy he provided vertices + std::copy(vertices, vertices+VertexDim*size, v_vector_.begin() + start) ; + //update pointer and size + Base::vertices_ = v_vector_.data() ; + Base::vertices_size_ += size ; + Base::set_dirty() ; + //return a pointer to the first vertex pushed + return start/VertexDim ; +} + +template< int _VertexDim, typename _Scalar > +unsigned int MeshBuilder<Variable,_VertexDim,_Scalar>::extend_vertices( + unsigned int size +) { + //resize the vertex container + const unsigned int start = v_vector_.size() ; + v_vector_.resize(start + VertexDim*size) ; + //update pointer and size + Base::vertices_ = v_vector_.data() ; + Base::vertices_size_ += size ; + Base::set_dirty() ; + //return a pointer to the first vertex pushed + return start/VertexDim ; +} + +template< int _VertexDim, typename _Scalar > +void MeshBuilder<Variable,_VertexDim,_Scalar>::erase_vertices( + unsigned int start, unsigned int end +) { + if(end > start) { + v_vector_.erase( v_vector_.begin() + VertexDim*start, + v_vector_.begin() + VertexDim*end + ) ; + Base::vertices_ = v_vector_.data() ; + Base::vertices_size_ -= end - start ; + Base::set_dirty() ; + } +} + +template< int _VertexDim, typename _Scalar > +unsigned int MeshBuilder<Variable,_VertexDim,_Scalar>::push_faces( + const unsigned int* faces, const unsigned int* face_ends, unsigned int size +) { + //starting index of the new faces + const unsigned int f_start = f_vector_.size() ; + const unsigned int fe_start = fe_vector_.size() ; + //number of vertex indices to add to the faces + const unsigned int fv_size = face_ends[size-1] ; + //append the faces and face_ends + f_vector_.resize(f_start + fv_size) ; + fe_vector_.resize(fe_start + size) ; + std::copy(faces, faces + fv_size, f_vector_.begin() + f_start) ; + std::copy(face_ends, face_ends + size, fe_vector_.begin() + fe_start) ; + //shift the face ends to account for the previous faces of the mesh + for(unsigned int f = fe_start; f < fe_start + size; ++f) { + fe_vector_[f] += f_start ; + } + //reset face pointers and size + Base::faces_ = f_vector_.data() ; + Base::face_ends_ = fe_vector_.data() ; + Base::faces_size_ += size ; + //glue the new faces + c_computer_.resize(f_start + fv_size) ; + glue_faces(fe_start,fe_start+size) ; + Base::face_neighbourhoods_ = c_computer_.data() ; + Base::set_dirty() ; + //return the index of the first face + return fe_start ; +} + +template< int _VertexDim, typename _Scalar > +unsigned int MeshBuilder<Variable,_VertexDim,_Scalar>::extend_faces( + unsigned int size, unsigned int face_size +) { + //starting index of the new faces + const unsigned int f_start = f_vector_.size() ; + const unsigned int fe_start = fe_vector_.size() ; + //append the faces and face_ends + f_vector_.resize(f_start + size*face_size) ; + fe_vector_.resize(fe_start + size) ; + //set the face ends of the new faces + for(unsigned int face = fe_start; face < fe_vector_.size(); ++face) { + fe_vector_[face] = face==0 ? face_size : fe_vector_[face-1]+face_size ; + ; + } + //reset face pointers and size + Base::faces_ = f_vector_.data() ; + Base::face_ends_ = fe_vector_.data() ; + Base::faces_size_ += size ; + //allocate neighbours for the new faces + c_computer_.resize(f_start + size*face_size) ; + Base::face_neighbourhoods_ = c_computer_.data() ; + Base::set_dirty() ; + //return the index of the first face + return fe_start ; +} + +template< int _VertexDim, typename _Scalar > +void MeshBuilder<Variable,_VertexDim,_Scalar>::erase_faces( unsigned int start, + unsigned int end + ) { + if(end > start) { + //update neighbourhoods + c_computer_.erase_neighbourhoods(start,end) ; + Base::face_neighbourhoods_ = c_computer_.data() ; + //number of face vertices to remove + const unsigned int fv_start = Base::face_offset(start) ; + const unsigned int fv_end = end < Base::faces_size_ ? + Base::face_offset(end) : + Base::face_vertices_size() + ; + //remove faces and face_ends + f_vector_.erase( f_vector_.begin() + fv_start, + f_vector_.begin() + fv_end + ) ; + fe_vector_.erase( fe_vector_.begin() + start, + fe_vector_.begin() + end + ) ; + //shift the face_ends after the end + const unsigned int offset = fv_end - fv_start ; + for(unsigned int f = start; f < fe_vector_.size(); ++f) { + fe_vector_[f] -= offset ; + } + Base::faces_ = f_vector_.data() ; + Base::face_ends_ = fe_vector_.data() ; + Base::faces_size_ -= end - start ; + Base::set_dirty() ; + } +} + +//}}} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/builder_fwd.hpp b/contrib/Revoropt/include/Revoropt/Mesh/builder_fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..40d1e2a4985507e196f508e597a40c896f476067 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/builder_fwd.hpp @@ -0,0 +1,348 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_BUILDER_H_ +#define _REVOROPT_MESH_BUILDER_H_ + +#include "base_fwd.hpp" +#include "connectivity_fwd.hpp" + +namespace Revoropt { + +/*** Mesh builder, managing the vertices and faces arrays ***/ + +/** Fixed face size case **/ + +template< + int _FaceSize, + int _VertexDim, + typename _Scalar +> +class MeshBuilder : public Mesh<_FaceSize,_VertexDim,_Scalar,0> +{ + public: + + typedef Mesh<_FaceSize,_VertexDim,_Scalar,0> Base ; + + typedef _Scalar Scalar ; + + enum { + FaceSize = _FaceSize, + VertexDim = _VertexDim + } ; + + using Base::NO_NEIGHBOUR ; + + /** Construction **/ + MeshBuilder() : Base(), c_computer_(this) {} ; + + void clear() { + //vertices + v_vector_.resize(0) ; + Base::vertices_size_ = 0 ; + Base::vertices_ = NULL ; + f_vector_.resize(0) ; + Base::faces_size_ = 0 ; + Base::faces_ = NULL ; + Base::face_neighbourhoods_ = NULL ; + Base::set_dirty() ; + } + + /* init from another mesh */ + template<int _OtherDim, int _OtherOffset> + void init( const ROMesh< _FaceSize, + _OtherDim, + _Scalar, + _OtherOffset + >* rhs + ) ; + + template<int _OtherDim, int _OtherOffset> + void operator=( const ROMesh< _FaceSize, + _OtherDim, + _Scalar, + _OtherOffset + >& rhs + ) { + init(&rhs) ; + Base::set_dirty() ; + } + + /** Edition **/ + + + /* Adding vertices, returns the index of the first vertex */ + unsigned int push_vertices( const Scalar* vertices, unsigned int size ) ; + + unsigned int push_vertex( const Scalar* vertex ) { + return push_vertices(vertex,1) ; + } + + /* Adding unitialized vertices */ + unsigned int extend_vertices( unsigned int size ) ; + + /* Removing vertices */ + void erase_vertices( unsigned int start, unsigned int end ) ; + + /* Adding faces, returns the index of the first face added */ + unsigned int push_faces( const unsigned int* faces, unsigned int size ) ; + + + unsigned int push_face( const unsigned int* face, unsigned int size = 3 ) { + assert(size == 3 && "trying to push a face with wrong size.") ; + return push_face(face,1) ; + } + + /* Adding unitialized faces */ + //second argument is provided for compatibility with the variable face + //size case, and should always be the face size of the mesh + unsigned int extend_faces( unsigned int size, + unsigned int fsize = FaceSize //compatibility + ) ; + + /* Removing faces */ + void erase_faces( unsigned int start, unsigned int end ) ; + + /* Connecting faces (connecting twice leads to inconsistancy) */ + + void glue_faces( unsigned int start, unsigned int end ) { + c_computer_.glue_faces(start,end) ; + Base::face_neighbourhoods_ = c_computer_.data() ; + Base::set_dirty() ; + } + + void glue_face( unsigned int face ) { + glue_faces(face, face+1) ; + } + + /* Disconnecting faces (disconnecting twice leads to inconsistancy) */ + + void unglue_faces( unsigned int start, unsigned int end ) { + c_computer_.unglue_faces(start,end) ; + Base::face_neighbourhoods_ = c_computer_.data() ; + Base::set_dirty() ; + } + + void unglue_face( unsigned int face ) { + unglue_faces(face, face+1) ; + } + + /* Swaps */ + void swap( MeshBuilder& rhs ) { + std::swap(Base::vertices_, rhs.vertices_) ; + std::swap(Base::vertices_size_, rhs.vertices_size_) ; + v_vector_.swap(rhs.v_vector_) ; + std::swap(Base::faces_, rhs.faces_) ; + std::swap(Base::face_neighbourhoods_, rhs.face_neighbourhoods_) ; + std::swap(Base::faces_size_, rhs.faces_size_) ; + f_vector_.swap(rhs.f_vector_) ; + c_computer_.swap(rhs.c_computer_) ; + Base::set_dirty() ; + } + + void swap_vertices( std::vector<Scalar>& other ) { + unsigned int size = other.size() / VertexDim ; + v_vector_.swap(other) ; + Base::vertices_ = v_vector_.data() ; + Base::vertices_size_ = size ; + Base::set_dirty() ; + } + + void swap_faces( std::vector<unsigned int>& other ) { + unsigned int size = other.size() / FaceSize ; + f_vector_.swap(other) ; + Base::faces_ = f_vector_.data() ; + Base::faces_size_ = size ; + c_computer_.compute_connectivity() ; + Base::face_neighbourhoods_ = c_computer_.data() ; + Base::set_dirty() ; + } + + private: + + /* Vertices */ + std::vector<Scalar> v_vector_ ; + + /* Faces */ + std::vector<unsigned int> f_vector_ ; + + /* Neighbourhoods */ + ConnectivityComputer<Base> c_computer_ ; + +} ; + +/** Variable face size case **/ + +template< + int _VertexDim, + typename _Scalar +> +class MeshBuilder<Variable,_VertexDim,_Scalar> : + public Mesh<Variable,_VertexDim,_Scalar,0> +{ + public: + + typedef Mesh<Variable,_VertexDim,_Scalar,0> Base ; + + typedef _Scalar Scalar ; + + enum { + FaceSize = Variable, + VertexDim = _VertexDim + } ; + + using Base::NO_NEIGHBOUR ; + + /** Construction **/ + MeshBuilder() : Base(), c_computer_(this) {} ; + + void clear() { + //vertices + v_vector_.resize(0) ; + Base::vertices_size_ = 0 ; + Base::vertices_ = NULL ; + f_vector_.resize(0) ; + fe_vector_.resize(0) ; + Base::faces_size_ = 0 ; + Base::faces_ = NULL ; + Base::face_neighbourhoods_ = NULL ; + Base::face_ends_ = NULL ; + Base::set_dirty() ; + } + + /* init from another mesh */ + template<int _OtherDim, int _OtherOffset> + void init( const ROMesh< Variable, + _OtherDim, + Scalar, + _OtherOffset + >* rhs + ) ; + + template<int _OtherDim, int _OtherOffset> + void operator=( const ROMesh< Variable, + _OtherDim, + _Scalar, + _OtherOffset + >& rhs + ) { + init(&rhs) ; + Base::set_dirty() ; + } + + /** Edition **/ + + /* Adding vertices, returns the index of the first vertex */ + unsigned int push_vertices( const Scalar* vertices, unsigned int size ) ; + + unsigned int push_vertex( const Scalar* vertex ) { + return push_vertices(vertex,1) ; + } + + /* Adding unitialized vertices */ + unsigned int extend_vertices( unsigned int size ) ; + + /* Removing vertices */ + void erase_vertices( unsigned int start, unsigned int end ) ; + + /* Adding faces, returns the index of the first face created */ + unsigned int push_faces( const unsigned int* faces, + const unsigned int* face_ends, + unsigned int size + ) ; + + unsigned int push_face( const unsigned int* face, unsigned int face_size ) { + return push_faces(face,&face_size,1) ; + } + + /* Adding unitialized faces, all the faces must have the same size */ + unsigned int extend_faces( unsigned int size, unsigned int face_size ) ; + + /* Removing faces */ + void erase_faces( unsigned int start, unsigned int end ) ; + + /* Connecting faces (connecting twice leads to inconsistancy) */ + + void glue_faces( unsigned int start, unsigned int end ) { + c_computer_.glue_faces(start,end) ; + Base::face_neighbourhoods_ = c_computer_.data() ; + Base::set_dirty() ; + } + + void glue_face( unsigned int face ) { + glue_faces(face, face+1) ; + } + + /* Disconnecting faces (disconnecting twice leads to inconsistancy) */ + + void unglue_faces( unsigned int start, unsigned int end ) { + c_computer_.unglue_faces(start,end) ; + Base::face_neighbourhoods_ = c_computer_.data() ; + Base::set_dirty() ; + } + + void unglue_face( unsigned int face ) { + unglue_faces(face, face+1) ; + } + + /* Swaps */ + void swap( MeshBuilder& rhs ) { + std::swap(Base::vertices_, rhs.vertices_) ; + std::swap(Base::vertices_size_, rhs.vertices_size_) ; + v_vector_.swap(rhs.v_vector_) ; + std::swap(Base::faces_, rhs.faces_) ; + std::swap(Base::face_ends_, rhs.face_ends_) ; + std::swap(Base::face_neighbourhoods_, rhs.face_neighbourhoods_) ; + std::swap(Base::faces_size_, rhs.faces_size_) ; + f_vector_.swap(rhs.f_vector_) ; + fe_vector_.swap(rhs.fe_vector_) ; + c_computer_.swap(rhs.c_computer_) ; + Base::set_dirty() ; + } + + void swap_vertices( std::vector<Scalar>& other ) { + unsigned int size = other.size() / VertexDim ; + v_vector_.swap(other) ; + Base::vertices_ = v_vector_.data() ; + Base::vertices_size_ = size ; + Base::set_dirty() ; + } + + void swap_faces( std::vector<unsigned int>& other_faces, + std::vector<unsigned int>& other_face_ends + ) { + unsigned int size = other_face_ends.size() ; + f_vector_.swap(other_faces) ; + fe_vector_.swap(other_face_ends) ; + Base::faces_ = f_vector_.data() ; + Base::face_ends_ = fe_vector_.data() ; + Base::faces_size_ = size ; + c_computer_.compute_connectivity() ; + Base::face_neighbourhoods_ = c_computer_.data() ; + Base::set_dirty() ; + } + + private: + + /* Vertices */ + std::vector<Scalar> v_vector_ ; + + /* Faces */ + std::vector<unsigned int> f_vector_ ; + std::vector<unsigned int> fe_vector_ ; + + /* Neighbourhoods */ + ConnectivityComputer<Base> c_computer_ ; + +} ; + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/connectivity_def.hpp b/contrib/Revoropt/include/Revoropt/Mesh/connectivity_def.hpp new file mode 100644 index 0000000000000000000000000000000000000000..342d4c7c98896f8bd7fa45ab6b5f33afd1581bbc --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/connectivity_def.hpp @@ -0,0 +1,268 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_CONNECTIVITY_DEF_HPP_ +#define _REVOROPT_MESH_CONNECTIVITY_DEF_HPP_ + +#include "connectivity_fwd.hpp" + +namespace Revoropt { + +/***{{{ Connectivity computer ***/ + +template<typename MeshType> +void ConnectivityComputer<MeshType>::glue_face( unsigned int face_index ) { + //number of edges of the face + const unsigned int face_size = mesh_->face_size(face_index) ; + //vertex indices of the face + const unsigned int* face_vertices = mesh_->face(face_index) ; + + //iterate on edges + for(unsigned int edge_index = 0; edge_index<face_size; ++edge_index) { + //vertices of the edge + const unsigned int v0 = face_vertices[ edge_index ] ; + const unsigned int v1 = face_vertices[(edge_index+1)%face_size] ; + + //build the edge + EdgeKey edge(v0, v1) ; + + //info relative to this edge + EdgeInfo edge_info(face_index,edge_index) ; + + //try to insert the edge in the border edges + bedge_iterator it = border_edges_.find(edge) ; + + //connect the triangles if an other edge was already present + if(it != border_edges_.end()) { + //the edge is a border edge, connect the triangles + //index of the neighbouring face + const unsigned int n_face_index = it->second.first ; + //edge index of the current edge in the neighbouring face + const unsigned int n_edge_index = it->second.second ; + //assign the neighbour of the current face + face_neighbours(face_index)[edge_index] = n_face_index ; + //assign the neighbour of the neighbouring face + face_neighbours(n_face_index)[n_edge_index] = face_index ; + //delete the edge from border edges + border_edges_.erase(it) ; + } else { + border_edges_[edge] = edge_info ; + //assign the edge as a border edge + face_neighbours(face_index)[edge_index] = NO_NEIGHBOUR ; + } + } +} + +template<typename MeshType> +void ConnectivityComputer<MeshType>::glue_faces( unsigned int face_start, + unsigned int face_end + ) { + //iterate over the faces to glue + for( unsigned int face_index = face_start; + face_index < face_end; + ++face_index + ) { + //glue the face + glue_face(face_index) ; + } +} + +template<typename MeshType> +void ConnectivityComputer<MeshType>::unglue_face( unsigned int face_index ) { + //number of edges of the face + const unsigned int face_size = mesh_->face_size(face_index) ; + //vertex indices of the face + const unsigned int* face_vertices = mesh_->face(face_index) ; + + //iterate on edges + for(unsigned int edge_index = 0; edge_index<face_size; ++edge_index) { + //vertices of the edge + const unsigned int v0 = face_vertices[ edge_index ] ; + const unsigned int v1 = face_vertices[(edge_index+1)%face_size] ; + + //build the edge + EdgeKey edge(v0, v1) ; + + //get the neighbouring face information + const unsigned int n_face_index = + face_neighbours(face_index)[edge_index] ; + + if(n_face_index == NO_NEIGHBOUR) { + //if the edge is a border edge, remove it from the border edges + border_edges_.erase(edge) ; + } else { + //corresponding edge index in the neighbouring face + unsigned int n_edge_index = + mesh_->edge_face_index(n_face_index,v0,v1) ; + assert( n_edge_index!=MeshType::NO_INDEX && + "error : connectivity mismatch." + ) ; + //info relative to the neighbouring edge + EdgeInfo edge_info(n_face_index,n_edge_index) ; + + //try to insert the edge in the border edges + bedge_iterator it = border_edges_.find(edge) ; + + //connect the triangles if an other edge was already present + if(it != border_edges_.end()) { + //the edge is a border edge, connect the triangles + //index of the neighbouring face + const unsigned int nn_face_index = it->second.first ; + //edge index of the current edge in the neighbouring face + const unsigned int nn_edge_index = it->second.second ; + //assign the neighbour of the current face + face_neighbours(n_face_index)[n_edge_index] = nn_face_index ; + //assign the neighbour of the neighbouring face + face_neighbours(nn_face_index)[nn_edge_index] = n_face_index ; + //delete the edge from border edges + border_edges_.erase(it) ; + } else { + border_edges_[edge] = edge_info ; + //assign the edge as a border edge + face_neighbours(n_face_index)[n_edge_index] = NO_NEIGHBOUR ; + } + } + } +} + +template<typename MeshType> +void ConnectivityComputer<MeshType>::unglue_faces( unsigned int face_start, + unsigned int face_end + ) { + //iterate over the faces to glue + for( unsigned int face_index = face_start; + face_index < face_end; + ++face_index + ) { + //glue the face + unglue_face(face_index) ; + } +} + +template<typename MeshType> +void ConnectivityComputer<MeshType>::erase_neighbourhoods( + unsigned int face_start, unsigned int face_end +) { + //unglue faces + unglue_faces(face_start, face_end) ; + //remove the corresponding neighbourhoods + if(face_end >= mesh_->faces_size()) { + //remove all the neighbourhoods up to the end of the array + const unsigned int begin_index = mesh_->face_offset(face_start) ; + face_neighbourhoods_.erase( face_neighbourhoods_.begin() + begin_index, + face_neighbourhoods_.end() + ) ; + } else { + //remove the neighbourhoods within the range + const unsigned int begin_index = mesh_->face_offset(face_start) ; + const unsigned int end_index = mesh_->face_offset(face_end) ; + face_neighbourhoods_.erase( face_neighbourhoods_.begin() + begin_index, + face_neighbourhoods_.begin() + end_index + ) ; + //shift the indices of the faces after the range + const unsigned int offset = face_end - face_start ; + for( unsigned int neighbour = 0 ; + neighbour < face_neighbourhoods_.size() ; + ++neighbour + ) { + unsigned int& fneigh = face_neighbourhoods_[neighbour] ; + if((fneigh >= face_end) && (fneigh != NO_NEIGHBOUR)) { + face_neighbourhoods_[neighbour] -= offset ; + } + } + } +} + +template<typename MeshType> +void ConnectivityComputer<MeshType>::swap( ConnectivityComputer& rhs ) { + std::swap(mesh_, rhs.mesh_) ; + face_neighbourhoods_.swap(rhs.face_neighbourhoods_) ; + border_edges_.swap(rhs.border_edges_) ; +} + +//}}} + +/***{{{ Edge layer : building edges over a mesh ***/ + +template< typename Mesh > +void EdgeLayer<Mesh>::update() { + //one half edge per face vertex + edges_.resize(mesh_->face_vertices_size()) ; + //one edge per vertex + vertex_edges_.assign(mesh_->vertices_size(),NO_EDGE) ; + //assign (vertex_)edge data + for(unsigned int face = 0; face < mesh_->faces_size(); ++face) { + //size of the face + const unsigned int fsize = mesh_->face_size(face) ; + //offset of the face + const unsigned int foffset = mesh_->face_offset(face) ; + //vertices of the face + const unsigned int* fverts = mesh_->face(face) ; + //neighbouring faces of the face + const unsigned int* fneigh = mesh_->face_neighbours(face) ; + //edges of the face + MeshEdge* fedges = face_edges(face) ; + for(unsigned int i = 0; i < fsize; ++i) { + //vertices of the edge + const unsigned int v1 = fverts[ i ] ; + const unsigned int v2 = fverts[(i+1)%fsize] ; + //assign the face of the edge + fedges[i].face = face ; + //(re)assign the edge of the vertex + vertex_edges_[v1] = foffset + i ; + //test if the edge is a border edge + const unsigned int n_face = fneigh[i] ; + if(n_face == Mesh::NO_NEIGHBOUR) { + //assign the opposite edge as no edge + fedges[i].opposite = NO_EDGE ; + } else if(n_face > face) { //to handle ony once pairs of opposite edges + //neighbouring face offset + const unsigned int n_foffset = mesh_->face_offset(n_face) ; + //neighbouring face edges + MeshEdge* n_fedges = face_edges(n_face) ; + //edge index in the neighbouring face + const unsigned int j = mesh_->edge_face_index(n_face, v1, v2) ; + assert( (j!=Mesh::NO_INDEX) && + "The neighbouring face has no such edge" + ) ; + fedges[i].opposite = n_foffset + j ; + n_fedges[j].opposite = foffset + i ; + } + } + } + //handle border vertices + for(unsigned int vertex = 0; vertex < mesh_->vertices_size(); ++vertex) { + //assign the vertex edge to the first border found circulating backwards + vertex_edges_[vertex] = first_border_vertex_edge(vertex) ; + } +} + +template< typename Mesh > +unsigned int EdgeLayer<Mesh>::first_border_vertex_edge( unsigned int vertex ) { + //initial edge + unsigned int edge = vertex_edge(vertex) ; + //rotate back until a border edge is found + unsigned int prev_edge = edge ; + if(edge != NO_EDGE) { + do { + prev_edge = edge ; + edge = prev_around_vertex(vertex, edge) ; + } while((edge != NO_EDGE) && (edge != vertex_edge(vertex))) ; + } + //assign the edge vertex to the last non NO_EDGE edge + return prev_edge ; +} + +//}}} + +} //end of namespace Revoropt + +#endif + diff --git a/contrib/Revoropt/include/Revoropt/Mesh/connectivity_fwd.hpp b/contrib/Revoropt/include/Revoropt/Mesh/connectivity_fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a68c972a20584ab612d4d395c6c9a6b7e043661f --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/connectivity_fwd.hpp @@ -0,0 +1,446 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_CONNECTIVITY_FWD_HPP_ +#define _REVOROPT_MESH_CONNECTIVITY_FWD_HPP_ + +#include <Revoropt/Tools/combinatorics.hpp> + +#include <assert.h> +#include <vector> +#include <unordered_map> + +namespace Revoropt { + +/***{{{ Connectivity computer ***/ + +template<typename MeshType> +class ConnectivityComputer { + public: + + static const unsigned int NO_NEIGHBOUR ; + + ConnectivityComputer() : mesh_(NULL) {} ; + ConnectivityComputer( const MeshType* mesh ) : mesh_(mesh) { + //reset face neighbourhoods + face_neighbourhoods_.assign(mesh_->face_vertices_size(),NO_NEIGHBOUR) ; + } ; + + /* Resizing */ + void resize( unsigned int size ) { + face_neighbourhoods_.resize(size,NO_NEIGHBOUR) ; + } + + /* Provide a pointer to the neighbourhood array */ + unsigned int* data() { + return face_neighbourhoods_.data() ; + } + + const unsigned int* data() const { + return face_neighbourhoods_.data() ; + } + + /* Setting the mesh */ + void set_mesh(const MeshType* mesh) { + mesh_ = mesh ; + + //reset face neighbourhoods + face_neighbourhoods_.assign(mesh_->face_vertices_size(),NO_NEIGHBOUR) ; + + //Reset border edges + border_edges_.clear() ; + } + + /* Incrementally gluing faces */ + void glue_face( unsigned int face_index ) ; + + void glue_faces( unsigned int face_start, unsigned int face_end ) ; + + /* Ungluing faces */ + + void unglue_face( unsigned int face_index ) ; + + void unglue_faces( unsigned int face_start, unsigned int face_end ) ; + + /* Removing faces */ + + void erase_neighbourhoods( unsigned int face_start, unsigned int face_end ) ; + + /* Reset neighbourhoods and glue all the faces from scratch */ + void compute_connectivity() { + //reset border edges + border_edges_.clear() ; + + //resize according to the mesh + resize(mesh_->face_vertices_size()) ; + + //glue all the faces + glue_faces(0,mesh_->faces_size()) ; + } + + /* Swapping */ + void swap( ConnectivityComputer<MeshType>& rhs ) ; + + private: + + /** Mesh holding the face information **/ + const MeshType* mesh_ ; + + /** Computing the face neighbourhoods **/ + + /* Neighbourhood storage */ + std::vector<unsigned int> face_neighbourhoods_ ; + + /* Face neighbours from an index */ + unsigned int* face_neighbours( unsigned int face_index ) { + const unsigned int offset = mesh_->face_offset(face_index) ; + return data() + offset ; + } + + /* Border edges */ + typedef tuplekey<2> EdgeKey ; + typedef std::pair<unsigned int, unsigned int> EdgeInfo ; + typedef tuple_hash<2> EdgeHash ; + typedef std::unordered_map<EdgeKey, EdgeInfo, EdgeHash>::iterator bedge_iterator ; + std::unordered_map<EdgeKey, EdgeInfo, EdgeHash> border_edges_ ; +} ; + +template<typename MeshType> +const unsigned int ConnectivityComputer<MeshType>::NO_NEIGHBOUR = MeshType::NO_NEIGHBOUR ; + +//}}} + +/***{{{ Edge layer : building edges over a mesh ***/ + +class MeshEdge { + + public : + /* other classical half edge data like next, prev, vertex + * are available via the mesh on which these edges are built*/ + + /* face corresponding to this edge */ + unsigned int face ; + + /* opposite edge index */ + unsigned int opposite ; +} ; + +template< typename Mesh > +class EdgeLayer { + + public : + + static const unsigned int NO_EDGE ; + + //TODO boundary + + /** Construction **/ + + EdgeLayer( const Mesh* mesh ) : mesh_(mesh) { + update() ; + } + + void update() ; + + /* Resize edges according to the faces without initializing them */ + void fit_to_faces() { + //resize + edges_.resize(mesh_->face_vertices_size()) ; + } + + /* Commented since the complexity was nearly that of an update */ + /* Erase edges corresponding to faces. Do it BEFORE erasing faces *//* + void erase_face_edges( unsigned int start, unsigned int end ) { + //first edge to remove + const unsigned int start_edge = mesh_->face_offset(start) ; + //first edge to keep above range + const unsigned int end_edge = end >= mesh_->faces_size() ? + mesh_->face_vertices_size() : + mesh_->face_offset(end) ; + //disconnect opposite edges + for(unsigned int face = start; face < end; ++face) { + //face size + const unsigned int fsize = mesh_->face_size(face) ; + //face edges + MeshEdge* fedges = face_edges(face) ; + //iterate over opposite edges + for(unsigned int edge = 0; edge < fsize; ++edge) { + //assign opposite edge opposite to NO_EDGE + if(fedges[edge].opposite != NO_EDGE) { + edges_[fedges[edge].opposite].opposite = NO_EDGE ; + } + } + } + //erase edges + edges_.erase(edges_.begin() + start_edge, edges_.begin() + end_edge) ; + //number of edges removed + const unsigned int erase_size = end_edge - start_edge ; + //number of faces removed + const unsigned int face_erase_size = end - start ; + //shift edge indices + for(unsigned int edge = 0; edge < edges_.size(); ++edge) { + unsigned int& opp_edge = edges_[edge].opposite ; + if((opp_edge != NO_EDGE) && (opp_edge > start_edge)) { + opp_edge -= erase_size ; + } + unsigned int& face = edges_[edge].face ; + if(face >= start) { + face -= face_erase_size ; + } + } + //reset vertex edges + for(unsigned int face = 0; face < mesh_->faces_size(); ++face) { + if((face < start) || (face >= end)) { + //size of the face + const unsigned int fsize = mesh_->face_size(face) ; + //offset of the face + const unsigned int foffset = mesh_->face_offset(face) ; + //vertices of the face + const unsigned int* fverts = mesh_->face(face) ; + for(unsigned int i = 0; i < fsize; ++i) { + //vertices of the edge + const unsigned int v1 = fverts[i] ; + //(re)assign the edge of the vertex + vertex_edges_[v1] = foffset + i ; + } + } + } + for(unsigned int vertex = 0; vertex < mesh_->vertices_size(); ++vertex) { + //assign the vertex edge to the first border found circulating backwards + vertex_edges_[vertex] = first_border_vertex_edge(vertex) ; + //shift if necessary + unsigned int& vedge = vertex_edges_[vertex] ; + if((vedge != NO_EDGE) && (vedge > start_edge)) { + vedge -= erase_size ; + } + } + } + */ + + /** Access **/ + + /* edge from its index */ + MeshEdge* edge_info( unsigned int index ) { + return edges_.data() + index ; + } + + /* edge array of a face */ + MeshEdge* face_edges( unsigned int index ) { + return edges_.data() + mesh_->face_offset(index) ; + } + + /* edge of a vertex */ + unsigned int vertex_edge( unsigned int vertex ) { + if(vertex >= mesh_->vertices_size()) return NO_EDGE ; + return vertex_edges_[vertex] ; + } + + /** Tools **/ + + /* opposite edge */ + unsigned int edge_opposite( unsigned int edge ) { + if(edge == NO_EDGE) return NO_EDGE ; + return edge_info(edge)->opposite ; + } + + /* face of an edge */ + unsigned int edge_face( unsigned int edge ) { + if(edge == NO_EDGE) return Mesh::NO_NEIGHBOUR ; + return edge_info(edge)->face ; + } + + /* index of an edge from its pointer */ + unsigned int edge_index( MeshEdge* edge ) { + if((edge >= edges_.data()) && (edge < edges_.data() + edges_.size())) { + return (unsigned int) (edge - edges_.data()) ; + } else { + return NO_EDGE ; + } + } + + /* index of an edge within its face */ + unsigned int edge_face_index( unsigned int edge ) { + return edge - mesh_->face_offset(edges_[edge].face) ; + } + + /* vertex at the start of the edge */ + unsigned int edge_start_vertex( unsigned int edge ) { + //face of the edge + const unsigned int face = edge_face(edge) ; + //offset of the face + const unsigned int foffset = mesh_->face_offset(face) ; + //edge index within the face + const unsigned int efindex = edge - foffset ; + //face vertices + const unsigned int* fverts = mesh_->face(face) ; + //output + return fverts[efindex] ; + } + + /* vertex at the start of the edge */ + unsigned int edge_end_vertex( unsigned int edge ) { + //face of the edge + const unsigned int face = edge_face(edge) ; + //size of the face + const unsigned int fsize = mesh_->face_size(face) ; + //offset of the face + const unsigned int foffset = mesh_->face_offset(face) ; + //edge index within the face + const unsigned int efindex = edge - foffset ; + //face vertices + const unsigned int* fverts = mesh_->face(face) ; + //output + return fverts[(efindex+1)%fsize] ; + } + + /* vertex of the edge different from the provided one */ + unsigned int edge_other_vertex( unsigned int edge, unsigned int vertex ) { + //face of the edge + const unsigned int face = edge_face(edge) ; + //size of the face + const unsigned int fsize = mesh_->face_size(face) ; + //offset of the face + const unsigned int foffset = mesh_->face_offset(face) ; + //edge index within the face + const unsigned int efindex = edge - foffset ; + //face vertices + const unsigned int* fverts = mesh_->face(face) ; + //output + if(fverts[efindex] == vertex) { + return fverts[(efindex+1)%fsize] ; + } else { + return fverts[ efindex ] ; + } + } + + /* test if the opposite edge has a lower index */ + bool is_resp( unsigned int edge ) { + unsigned int opp_edge = edge_opposite(edge) ; + //implicitly works for NO_EDGE since it is the biggest unsigned int + return (edge < opp_edge) ; + } + + /* test if the edge is on the boundary */ + bool is_border( unsigned int edge ) { + unsigned int opp_edge = edge_opposite(edge) ; + //implicitly works for NO_EDGE since it is the biggest unsigned int + return (opp_edge == NO_EDGE) ; + } + + /** Circulation **/ + + /* Around faces */ + + /* next edge around a face */ + unsigned int next_around_face( unsigned int edge ) { + //face of the edge + const unsigned int face = edge_face(edge) ; + //size of the face + const unsigned int fsize = mesh_->face_size(face) ; + //offset of the face + const unsigned int foffset = mesh_->face_offset(face) ; + //edge index within the face + const unsigned int efindex = edge - foffset ; + //next edge index within the face + const unsigned int next_efindex = (efindex+1)%fsize ; + //output + return foffset + next_efindex ; + } + + /* previous edge around a face */ + unsigned int prev_around_face( unsigned int edge ) { + //face of the edge + const unsigned int face = edge_face(edge) ; + //size of the face + const unsigned int fsize = mesh_->face_size(face) ; + //offset of the face + const unsigned int foffset = mesh_->face_offset(face) ; + //edge index within the face + const unsigned int efindex = edge - foffset ; + //previous edge index within the face + const unsigned int prev_efindex = (efindex+fsize-1)%fsize ; + //output + return foffset + prev_efindex ; + } + + /* Around vertices */ + + /* circulation direction : given an edge, next searches the other edge sharing + * the vertex in the same face, and returns the edge opposite to this other + * edge. This ensures circulation. If the mesh is consistently oriented, the + * circulation direction is positive if the provided edge starts at the + * vertex, and negative otherwise. */ + + /* next edge around a vertex */ + unsigned int next_around_vertex( unsigned int vertex, unsigned int edge ) { + //if NO_EDGE is provided, circulation is finished + if(edge == NO_EDGE) { + return NO_EDGE ; + } + //find the other edge in the face pointing at this edge + unsigned int other_edge = other_face_edge_for_vertex(vertex, edge) ; + //return its opposite + return edge_opposite(other_edge) ; + } + + /* previous edge around a vertex */ + unsigned int prev_around_vertex( unsigned int vertex, unsigned int edge ) { + //if NO_EDGE is provided, circulation is finished + if(edge == NO_EDGE) { + return NO_EDGE ; + } + //opposite edge + const unsigned int opp_edge = edge_opposite(edge) ; + //if no opposite edge exists, circulation is finished + if(opp_edge == NO_EDGE) { + return NO_EDGE ; + } + //find the other edge pointing at this vertex + return other_face_edge_for_vertex(vertex,opp_edge) ; + } + + /* other edge in the same face using a vertex */ + unsigned int other_face_edge_for_vertex( unsigned int vertex, + unsigned int edge + ) { + if(edge_start_vertex(edge) == vertex) { + return prev_around_face(edge) ; + } else { + if(edge_end_vertex(edge) == vertex) { + return next_around_face(edge) ; + } else { + return NO_EDGE ; + } + } + } + + private : + + /* Rotate around a vertex to set the vertex edge to a border edge if any */ + unsigned int first_border_vertex_edge( unsigned int vertex ) ; + + /* Mesh */ + const Mesh* mesh_ ; + + /* Half edges, stored per face with the same layout as the vertices */ + std::vector<MeshEdge> edges_ ; + + /* One edge per vertex */ + std::vector<unsigned int> vertex_edges_ ; +} ; + +template<typename Mesh> +const unsigned int EdgeLayer<Mesh>::NO_EDGE = -1 ; + +//}}} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/debug_def.hpp b/contrib/Revoropt/include/Revoropt/Mesh/debug_def.hpp new file mode 100644 index 0000000000000000000000000000000000000000..496431ab45077421d32e911514a2fead8cbac553 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/debug_def.hpp @@ -0,0 +1,42 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_DEBUG_DEF_HPP_ +#define _REVOROPT_MESH_DEBUG_DEF_HPP_ + +#include <iostream> +#include "debug_fwd.hpp" + +namespace Revoropt { + +/* Printing the information relative to a face */ +template< typename Mesh > +void print_face( const Mesh* mesh, unsigned int face ) { + std::cout << "=== face " << face << " ===" << std::endl ; + if(face == Mesh::NO_NEIGHBOUR) return ; + //face size + const unsigned int fsize = mesh->face_size(face) ; + //face vertices + const unsigned int* fverts = mesh->face(face) ; + //face neighbours + const unsigned int* fneigh = mesh->face_neighbours(face) ; + //display edge information + for(unsigned int edge = 0; edge < fsize; ++edge) { + //vertices of the edge + const unsigned int v0 = fverts[ edge ] ; + const unsigned int v1 = fverts[(edge+1)%fsize] ; + //display the edge vertices and neighbours + std::cout << " * (" << v0 << "," << v1 << ") : " << fneigh[edge] << std::endl ; + } +} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/debug_fwd.hpp b/contrib/Revoropt/include/Revoropt/Mesh/debug_fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8d31e91502825d4c7c756fa7d215074f71238b02 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/debug_fwd.hpp @@ -0,0 +1,22 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_DEBUG_H_ +#define _REVOROPT_MESH_DEBUG_H_ + +namespace Revoropt { + +/* Printing the information relative to a face */ +template< typename Mesh > +void print_face( const Mesh* mesh, unsigned int face ) ; + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/export_def.hpp b/contrib/Revoropt/include/Revoropt/Mesh/export_def.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1ef669dfa02279ca710af9ddc2fad18188121a0b --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/export_def.hpp @@ -0,0 +1,139 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_EXPORT_DEF_HPP_ +#define _REVOROPT_MESH_EXPORT_DEF_HPP_ + +#include "export_fwd.hpp" + +#include <fstream> +#include <sstream> +#include <vector> +#include <map> + +namespace Revoropt { + +template<typename Mesh> +void export_obj( const Mesh* mesh, const std::string& filename ) { + //typedefs + enum { Dim = Mesh::VertexDim } ; + typedef typename Mesh::Scalar Scalar ; + + //open file + std::ofstream file ; + file.open(filename) ; + + //output vertices + for(unsigned int v = 0; v < mesh->vertices_size(); ++v) { + file << "v " ; + const Scalar* vertex = mesh->vertex(v) ; + for(int d = 0; d < Dim; ++d) { + file << vertex[d] << " " ; + } + file << std::endl ; + } + + //output faces + for(unsigned int f = 0; f < mesh->faces_size(); ++f) { + file << "f " ; + const unsigned int* face = mesh->face(f) ; + for(unsigned int v = 0; v < mesh->face_size(f); ++v) { + file << face[v]+1 << " " ; + } + file << std::endl ; + } + + //close file + file.close() ; +} + +template<typename Mesh> +void export_colored_obj( const Mesh* mesh, + const unsigned int* color_indices, + const double* colormap, + const std::string& basename + ) { + //typedefs + enum { Dim = Mesh::VertexDim } ; + typedef typename Mesh::Scalar Scalar ; + + //file names + std::stringstream objfilename ; + objfilename << basename << ".obj" ; + std::stringstream mtlfilename ; + mtlfilename << basename << ".mtl" ; + + //open obj file + std::ofstream objfile ; + objfile.open(objfilename.str().c_str()) ; + + //provide material file name + objfile << "mtllib " << mtlfilename.str() << std::endl ; + + //output vertices + for(unsigned int v = 0; v < mesh->vertices_size(); ++v) { + objfile << "v " ; + const Scalar* vertex = mesh->vertex(v) ; + for(int d = 0; d < Dim; ++d) { + objfile << vertex[d] << " " ; + } + objfile << std::endl ; + } + + //sorting faces by color index + typedef std::map< unsigned int, std::vector<unsigned int> > FaceMap ; + FaceMap groups ; + + for(unsigned int f = 0; f < mesh->faces_size(); ++f) { + groups[color_indices[f]].push_back(f) ; + } + + //output faces by groups + for(FaceMap::iterator it = groups.begin(); it != groups.end(); ++it) { + objfile << "g " << basename << "_material_" << it->first << std::endl ; + objfile << "usemtl material_" << it->first << std::endl ; + std::vector<unsigned int>& f_indices = it->second ; + for(unsigned int f = 0; f < f_indices.size(); ++f) { + objfile << "f " ; + const unsigned int* face = mesh->face(f_indices[f]) ; + for(unsigned int v = 0; v < mesh->face_size(f_indices[f]); ++v) { + objfile << face[v]+1 << " " ; + } + objfile << std::endl ; + } + } + + //close obj file + objfile.close() ; + + //open mtl file + std::ofstream mtlfile ; + mtlfile.open(mtlfilename.str().c_str()) ; + + //output materials + for(FaceMap::iterator it = groups.begin(); it != groups.end(); ++it) { + mtlfile << "newmtl material_" << it->first << std::endl ; + mtlfile << "Ns 100" << std::endl ; + mtlfile << "Ka 0.000000 0.000000 0.000000" << std::endl ; + //color + mtlfile << "Kd " + << colormap[3*it->first ] << " " + << colormap[3*it->first+1] << " " + << colormap[3*it->first+2] << std::endl ; + mtlfile << "Ks 0.000000 0.000000 0.000000" << std::endl ; + mtlfile << "Ni 1.000000" << std::endl ; + //mtlfile << "d 1.000000" << std::endl ; + mtlfile << "illum 1" << std::endl ; + } +} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/export_fwd.hpp b/contrib/Revoropt/include/Revoropt/Mesh/export_fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c3a6b98c51b31e2e5eb3de1d03db2cdcc4afdc05 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/export_fwd.hpp @@ -0,0 +1,32 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_EXPORT_H_ +#define _REVOROPT_MESH_EXPORT_H_ + +#include <string> + +namespace Revoropt { + +/* write an obj file to describe the mesh */ +template<typename Mesh> +void export_obj( const Mesh* mesh, const std::string& filename ) ; + +/* write an obj file to describe the mesh and a mtl file for colors*/ +template<typename Mesh> +void export_colored_obj( const Mesh* mesh, + const unsigned int* color_indices, + const double* colormap, + const std::string& basename + ) ; + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/import_def.hpp b/contrib/Revoropt/include/Revoropt/Mesh/import_def.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f746fcd44699c9e170aeb49434c52c264b44082f --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/import_def.hpp @@ -0,0 +1,85 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_IMPORT_DEF_HPP_ +#define _REVOROPT_MESH_IMPORT_DEF_HPP_ + +#include "import_fwd.hpp" + +#include <vector> +#include <fstream> +#include <iostream> +#include <sstream> + +namespace Revoropt { + +//loading obj meshes +template< typename MeshBuilder > +void import_obj(const std::string& filename, MeshBuilder* builder) { + //vertex dimension + enum { Dim = MeshBuilder::VertexDim } ; + + //open the file + std::ifstream file(filename) ; + if(!file.is_open()) { + std::cerr << "Mesh file could not be opened" << std::endl ; + return ; + } + + //array used to store face indices for each face + std::vector<unsigned int> face_indices ; + + //read the vertices and faces + unsigned int fcount = 0 ; + std::string line ; + while(file.good()) { + //get a new line + std::getline(file,line) ; + + //get the line type + std::stringstream line_stream(line) ; + std::string elemtype ; + std::getline(line_stream,elemtype,' ') ; + if(elemtype == "v") { + //line contains a vertex + //append the coordinates to the vertex array + double coord[Dim] ; + for(int i=0; i<Dim; ++i) { + line_stream >> coord[i] ; + } + builder->push_vertex(coord) ; + } else if (elemtype == "f") { + //line contains a face + ++fcount ; + //append the vertex indices to the array + face_indices.resize(0) ; + while(true) { + std::string indices ; + std::getline(line_stream,indices,' ') ; + std::stringstream indices_stream(indices) ; + std::string vertex_index ; + std::getline(indices_stream,vertex_index,'/') ; + std::stringstream vertex_index_stream(vertex_index) ; + unsigned int index ; + vertex_index_stream >> index ; + if(line_stream.fail()) break ; + face_indices.push_back(index-1) ; + } + builder->push_face(face_indices.data(), face_indices.size()) ; + } + } + + //close the file + file.close() ; +} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/import_fwd.hpp b/contrib/Revoropt/include/Revoropt/Mesh/import_fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fd48dcacc0a6cfd434c6d20d54506da2731b0a0e --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/import_fwd.hpp @@ -0,0 +1,24 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_IMPORT_FWD_HPP_ +#define _REVOROPT_MESH_IMPORT_FWD_HPP_ + +#include <string> + +namespace Revoropt { + +//loading obj meshes +template< typename MeshBuilder > +void import_obj(const std::string& filename, MeshBuilder* builder) ; + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/measure_def.hpp b/contrib/Revoropt/include/Revoropt/Mesh/measure_def.hpp new file mode 100644 index 0000000000000000000000000000000000000000..77050f8741dbec950ff218ade5deb381e8236246 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/measure_def.hpp @@ -0,0 +1,402 @@ +#ifndef _REVOROPT_MESH_MEASURE_DEF_HPP_ +#define _REVOROPT_MESH_MEASURE_DEF_HPP_ + +#include "measure_fwd.hpp" + +#include <Revoropt/Tools/measure_def.hpp> +#include <Revoropt/Neighbours/neighbourhood_def.hpp> + +#include <eigen3/Eigen/Dense> + +namespace Revoropt { + +/*{{{ Area */ + +/* Area of a face of a mesh */ +template<typename Mesh> +typename Mesh::Scalar mesh_face_area(const Mesh* mesh, unsigned int face) { + //scalar type + typedef typename Mesh::Scalar Scalar ; + + //result + double area = 0 ; + + //iterate over the faces + unsigned int fsize = mesh->face_size(face) ; + const unsigned int* fverts = mesh->face(face) ; + for(unsigned int v = 1; v < fsize-1; ++v) { + area += triangle_area<Mesh::VertexDim,Scalar>( + mesh->vertex(fverts[0]), + mesh->vertex(fverts[v]), + mesh->vertex(fverts[v+1]) + ) ; + } + + return area ; +} + +/* Area of a mesh */ +template<typename Mesh> +typename Mesh::Scalar mesh_area(const Mesh* mesh) { + //scalar type + typedef typename Mesh::Scalar Scalar ; + + //the area will be stored here + Scalar area = 0 ; + + //iterate over the faces + for(unsigned int face = 0; face < mesh->faces_size(); ++face) { + area += mesh_face_area(mesh, face) ; + } + + return area ; +} + +//}}} + +/*{{{ Edge length */ + +template< typename Mesh > +typename Mesh::Scalar edge_length_from_vertices( const Mesh* mesh, + unsigned int v0, + unsigned int v1 + ) { + //scalar type + typedef typename Mesh::Scalar Scalar ; + //vector type + typedef Eigen::Matrix<Scalar,Mesh::VertexDim,1> Vector ; + //vertex positions + Eigen::Map<const Vector> v0_pos(mesh->vertex(v0)) ; + Eigen::Map<const Vector> v1_pos(mesh->vertex(v1)) ; + //length + return (v1_pos-v0_pos).norm() ; +} + +template< typename Mesh > +typename Mesh::Scalar edge_length_from_face_index( const Mesh* mesh, + unsigned int face, + unsigned int edge + ) { + //edge vertex indices + const unsigned int v0 = mesh->face_edge_start_vertex(face, edge) ; + const unsigned int v1 = mesh->face_edge_end_vertex(face, edge) ; + return edge_length_from_vertices(mesh,v0,v1) ; +} + +/* Longest edge of a face */ + +template< typename Mesh > +unsigned int face_longest_edge( const Mesh* mesh, + unsigned int face, + typename Mesh::Scalar* output_length + ) { + //scalar type + typedef typename Mesh::Scalar Scalar ; + //size of the face + const unsigned int face_size = mesh->face_size(face) ; + //initialization + unsigned int longest_edge = 0 ; + Scalar longest_length = 0 ; + //iterate over the edges + for(unsigned int edge = 0; edge < face_size; ++edge) { + //length + Scalar length = edge_length_from_face_index(mesh, face, edge) ; + //update + if(length == longest_length) { + //decide depending on edge vertices in lexicographic order + const unsigned int v0 = mesh->face_edge_start_vertex(face, edge) ; + const unsigned int v1 = mesh->face_edge_end_vertex(face, edge) ; + const unsigned int v2 = mesh->face_edge_start_vertex(face, longest_edge) ; + const unsigned int v3 = mesh->face_edge_end_vertex(face, longest_edge) ; + const unsigned int v0_min = v0 < v1 ? v0 : v1 ; + const unsigned int v0_max = v0 < v1 ? v1 : v0 ; + const unsigned int v1_min = v2 < v3 ? v2 : v3 ; + const unsigned int v1_max = v2 < v3 ? v3 : v2 ; + if(v0_max == v1_max) { + if(v0_min > v1_min) { + longest_edge = edge ; + longest_length = length ; + } + } else if(v0_max > v1_max){ + longest_edge = edge ; + longest_length = length ; + } + + } else if(length > longest_length) { + longest_edge = edge ; + longest_length = length ; + } + } + //result + if(output_length != NULL) { + *output_length = longest_length ; + } + return longest_edge ; +} + +/* Longest edge of a mesh */ +template< typename Mesh > +void mesh_longest_edge_vertices( const Mesh* mesh, + unsigned int* v0, + unsigned int* v1, + typename Mesh::Scalar* output_length + ) { + //scalar type + typedef typename Mesh::Scalar Scalar ; + //initialization + unsigned int longest_face = 0 ; + unsigned int longest_edge = 0 ; + Scalar longest_length = 0 ; + //variables to store edges and lengths + unsigned int edge ; + Scalar length ; + for(unsigned int face = 0; face < mesh->faces_size(); ++face) { + //longest edge of the face + edge = face_longest_edge(mesh, face, &length) ; + if(length > longest_length) { + longest_face = face ; + longest_edge = edge ; + longest_length = length ; + } + } + //result + if(output_length != NULL) { + *output_length = longest_length ; + } + return mesh->face_edge_vertices(longest_face,longest_edge,v0,v1) ; +} + +//}}} + +/*{{{ Angles */ + +template<typename Mesh> +typename Mesh::Scalar face_angle_cos( const Mesh* mesh, + unsigned int face, + unsigned int vertex + ) { + //size of the face + const unsigned int fsize = mesh->face_size(face) ; + //vertices of the face + const unsigned int* fverts = mesh->face(face) ; + //previous and next vertex + const unsigned int prev = fverts[(vertex+fsize-1) % fsize] ; + const unsigned int next = fverts[(vertex +1) % fsize] ; + //angle cosine + return angle_cos<Mesh::VertexDim>( mesh->vertex(vertex), + mesh->vertex(prev), + mesh->vertex(next) + ) ; +} + +//}}} + +/*{{{ Normalization */ + +template<typename Mesh> +void normalize_mesh( Mesh* mesh, double* out_center, double* out_scale ) { + //typedefs + typedef typename Mesh::Scalar Scalar ; + typedef Eigen::Matrix<Scalar,Mesh::VertexDim,1> Vector ; + //compute the new center + Vector center = Vector::Zero() ; + for(unsigned int i=0; i<mesh->vertices_size(); ++i) { + Eigen::Map<const Vector> x(mesh->vertex(i)) ; + center += x ; + } + center /= mesh->vertices_size() ; + + if(out_center) { + for(int i = 0; i < 3; ++i) { + out_center[i] = center[i] ; + } + } + + //center the mesh + for(unsigned int i=0; i<mesh->vertices_size(); ++i) { + Eigen::Map<Vector> x(mesh->vertex(i)) ; + x -= center ; + } + + //compute the scene radius + Scalar radius = 0 ; + for(unsigned int i=0; i<mesh->vertices_size(); ++i) { + Eigen::Map<const Vector> x(mesh->vertex(i)) ; + radius = std::max(radius,(center - x).norm()) ; + } + + //scale the mesh + for(unsigned int i=0; i<mesh->vertices_size(); ++i) { + Eigen::Map<Vector> x(mesh->vertex(i)) ; + x /= radius ; + } + + if(out_scale) { + *out_scale = radius ; + } + + mesh->set_dirty() ; +} + +//}}} + +/*{{{ Distance to a mesh */ + +/* Action passed to a neighbourhood computer to compute normals */ +template<typename Triangulation> +class NeighDistanceCompute { + + public : + + //scalar type, dimension and vector + typedef typename Triangulation::Scalar Scalar ; + enum { Dim = Triangulation::VertexDim } ; + typedef Eigen::Matrix<Scalar,Dim,1> Vector ; + + NeighDistanceCompute( const Triangulation* mesh, + const Scalar* queries, + const unsigned int* indices, + Scalar* output, + bool translate_queries = true + ) : mesh_(mesh), + queries_(queries), + indices_(indices), + output_(output), + translate_queries_(translate_queries) {} ; + + NeighDistanceCompute( const Triangulation* mesh, + const Scalar* queries, + Scalar* output + ) : mesh_(mesh), + queries_(queries), + indices_(NULL), + output_(output), + translate_queries_(false) {} ; + + void operator()( unsigned int query, + unsigned int triangle + ) { + //position of the query + const unsigned int q_in_index = + translate_queries_ ? indices_[query] : query ; + const Scalar* qp = queries_ +Dim*q_in_index ; + Eigen::Map<const Vector> qv(qp) ; + + //vertices of the triangle + const unsigned int* tverts = mesh_->face(triangle) ; + Eigen::Map<const Vector> x0(mesh_->vertex(tverts[0])) ; + Eigen::Map<const Vector> x1(mesh_->vertex(tverts[1])) ; + Eigen::Map<const Vector> x2(mesh_->vertex(tverts[2])) ; + + //distance of the query to the triangle + Scalar uv[2] ; + point_in_triangle<Dim, Scalar>( qp, x0.data(), x1.data(), x2.data(), uv ) ; + const Scalar distance = (uv[0]*x0+uv[1]*x1+(1-uv[0]-uv[1])*x2 - qv).norm() ; + + //assign the minimum distance + const unsigned int q_out_index = + indices_ == NULL ? query : indices_[query] ; + output_[q_out_index] = + distance < output_[q_out_index] ? distance : output_[q_out_index] ; + } + + private : + + const Triangulation* mesh_ ; + const Scalar* queries_ ; + const unsigned int* indices_ ; + bool translate_queries_ ; + Scalar* output_ ; + +} ; + +template<typename Mesh> +void mesh_distances( const Mesh* mesh, + typename Mesh::Scalar* queries, + unsigned int queries_size, + typename Mesh::Scalar radius, + typename Mesh::Scalar* output + ) { + //typedefs + enum { Dim = Mesh::VertexDim, Offset = Mesh::VertexOffset } ; + typedef typename Mesh::Scalar Scalar ; + typedef ROMesh<3, Dim, Scalar, Offset> Triangulation ; + + //initialize the distance array + for(unsigned int i = 0; i < queries_size; ++i) { + output[i] = 2*radius ; + } + + //wrap the mesh as a triangle mesh + ROTriangulationWrapper<Dim, Scalar, Offset> trimesh(mesh) ; + + //use the neighbourhoods of the queries + NeighbourhoodComputer<Triangulation> n_computer( + &trimesh, queries, queries_size + ) ; + NeighDistanceCompute<Triangulation> action(&trimesh, queries, output) ; + n_computer.compute(radius, action) ; +} + +//}}} + +/**{{{ Gradient of the mesh area wrt. its vertices **/ + +template<typename Triangulation> +void mesh_area_gradient( + const Triangulation* mesh, + double* output, + double factor + ) { + typedef typename Triangulation::Scalar Scalar ; + enum { Dim = Triangulation::VertexDim } ; + typedef Eigen::Matrix<Scalar, Dim, 1> Vector ; + + for(unsigned int f = 0; f < mesh->faces_size(); ++f) { + //flag for degenerate triangle + bool degenerate = false ; + //vertices of the triangle + const unsigned int* fverts = mesh->face(f) ; + + //edges of the triangle + Vector edges[3] ; + Scalar e_len[3] ; + for(unsigned int v = 0; v < 3; ++v) { + Eigen::Map<const Vector> x0(mesh->vertex(fverts[v])) ; + Eigen::Map<const Vector> x1(mesh->vertex(fverts[(v+1)%3])) ; + edges[v] = x1 - x0 ; + e_len[v] = edges[v].norm() ; + degenerate |= (e_len[v] == 0) ; + if(degenerate) break ; + } + if(degenerate) continue ; + + //heights of the triangle + Vector heights[3] ; + Scalar h_len[3] ; + for(unsigned int v = 0; v < 3; ++v) { + const Scalar& base_len = e_len[(v+1)%3] ; + const Vector& base = edges[(v+1)%3] ; + heights[v] = edges[(v+2)%3] + - edges[(v+2)%3].dot(base) * base / (base_len*base_len) ; + h_len[v] = heights[v].norm() ; + degenerate |= (h_len[v] == 0) ; + if(degenerate) break ; + } + if(degenerate) continue ; + + //fill the gradient for the appropriate variables + for(unsigned int v = 0; v < 3; ++v) { + Eigen::Map<Vector> g(output + Dim*fverts[v]) ; + g += factor * 0.5 * e_len[(v+1)%3] * heights[v] / h_len[v] ; + } + + } + +} + +//}}} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/measure_fwd.hpp b/contrib/Revoropt/include/Revoropt/Mesh/measure_fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fc616d21cba046a5d5f8736ae337b62aa5ebbf17 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/measure_fwd.hpp @@ -0,0 +1,94 @@ +#ifndef _REVOROPT_MESH_MEASURE_FWD_HPP_ +#define _REVOROPT_MESH_MEASURE_FWD_HPP_ + +#include <cstddef> + +namespace Revoropt { + +/*{{{ Area */ + +/* Area of a face of a mesh */ +template<typename Mesh> +typename Mesh::Scalar mesh_face_area(const Mesh* mesh, unsigned int face) ; + +/* Area of a mesh */ +template<typename Mesh> +typename Mesh::Scalar mesh_area(const Mesh* mesh) ; + +//}}} + +/*{{{ Edge length */ + +template< typename Mesh > +typename Mesh::Scalar edge_length_from_vertices( const Mesh* mesh, + unsigned int v0, + unsigned int v1 + ) ; + +template< typename Mesh > +typename Mesh::Scalar edge_length_from_face_index( const Mesh* mesh, + unsigned int face, + unsigned int edge + ) ; + +/* Longest edge of a face */ + +template< typename Mesh > +unsigned int face_longest_edge( const Mesh* mesh, + unsigned int face, + typename Mesh::Scalar* output_length = NULL + ) ; + +/* Longest edge of a mesh */ +template< typename Mesh > +void mesh_longest_edge_vertices( const Mesh* mesh, + unsigned int* v0, + unsigned int* v1, + typename Mesh::Scalar* output_length = NULL + ) ; + +//}}} + +/*{{{ Angles */ + +template<typename Mesh> +typename Mesh::Scalar face_angle_cos( const Mesh* mesh, + unsigned int face, + unsigned int vertex + ) ; + +//}}} + +/*{{{ Normalization */ + +template<typename Mesh> +void normalize_mesh( Mesh* mesh, double* out_center = NULL, double* out_scale = NULL ) ; + +//}}} + +/**{{{ Distance to a mesh **/ + +template<typename Mesh> +void mesh_distances( const Mesh* mesh, + typename Mesh::Scalar* queries, + unsigned int queries_size, + typename Mesh::Scalar radius, + typename Mesh::Scalar* output + ) ; + +//}}} + +/**{{{ Gradient of the mesh area wrt. its vertices **/ + +template<typename Triangulation> +void mesh_area_gradient( + const Triangulation* mesh, + double* output, + double factor = 1 + ) ; + +//}}} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/normals_def.hpp b/contrib/Revoropt/include/Revoropt/Mesh/normals_def.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9dff330da337be0246700275f2055096f69e4b2d --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/normals_def.hpp @@ -0,0 +1,439 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_NORMALS_FWD_HPP_ +#define _REVOROPT_MESH_NORMALS_FWD_HPP_ + +#include <Revoropt/Tools/normals_def.hpp> +#include <Revoropt/Neighbours/neighbourhood_def.hpp> + +#include <eigen3/Eigen/Dense> +#include <cassert> +#include <cmath> +#include <vector> + +namespace Revoropt { + +/* Face normal */ +template<typename Mesh> +void face_normal ( const Mesh* mesh, + unsigned int index, + typename Mesh::Scalar* output + ) { + //scalar type + typedef typename Mesh::Scalar Scalar ; + //face_size + const unsigned int fsize = mesh->face_size(index) ; + //face vertex indices + const unsigned int* fverts = mesh->face(index) ; + + //call triangle normal + for(unsigned int i = 1; i<fsize-1; ++i) { + const Scalar n_len = triangle_normal( mesh->vertex(fverts[0]), + mesh->vertex(fverts[i]), + mesh->vertex(fverts[i+1]), + output + ) ; + if(n_len !=0) return ; + } +} + +/* Connected normals (based on the faces connected to the vertex) */ +template<typename Mesh> +void connected_vertex_normals( const Mesh* mesh, + typename Mesh::Scalar* output + ) { + assert((Mesh::VertexDim == 3) && "normals are only defined for 3D meshes") ; + //scalar type + typedef typename Mesh::Scalar Scalar ; + //vector type + typedef Eigen::Matrix<Scalar,Mesh::VertexDim,1> Vector ; + //average normals on vertices + for(unsigned int face; face < mesh->faces_size(); ++face) { + //number of vertices for this face + const unsigned int fsize = mesh->face_size(face) ; + if(fsize < 3) continue ; + //get the normal of the face + Vector fnormal ; + face_normal(mesh,face,fnormal.data()) ; + if(fnormal.dot(fnormal) == 0) continue ; + //vertex indices of the face + const unsigned int* fverts = mesh->face(face) ; + //add the weighted normal for each vertex + for(unsigned int vertex = 0; vertex < fsize; ++vertex) { + const unsigned int previndex = fverts[(vertex+fsize-1)%fsize] ; + const unsigned int currindex = fverts[ vertex ] ; + const unsigned int nextindex = fverts[(vertex +1)%fsize] ; + Eigen::Map<const Vector> vprev(mesh->vertex(previndex)) ; + Eigen::Map<const Vector> vcurr(mesh->vertex(currindex)) ; + Eigen::Map<const Vector> vnext(mesh->vertex(nextindex)) ; + //normal weight for this vertex + const Vector e1 = (vprev-vcurr) ; + const Scalar e1_len = e1.norm() ; + const Vector e2 = (vcurr-vnext) ; + const Scalar e2_len = e2.norm() ; + if(e1_len*e2_len != 0) { + Scalar angle_cos = -e1.dot(e2)/(e1_len*e2_len) ; + angle_cos = std::min(angle_cos,1.) ; + angle_cos = std::max(angle_cos,-1.) ; + const Scalar angle = acos(angle_cos) ; + //add the face contribution to the vertex + Eigen::Map<Vector> vnormal(output + 3*currindex) ; + vnormal += angle*fnormal ; + } + } + } + //normalize vertex normals + for(unsigned int vertex = 0; vertex < mesh->vertices_size(); ++vertex) { + Eigen::Map<Vector> vnormal(output + 3*vertex) ; + Scalar vnormal_len = vnormal.norm() ; + if(vnormal_len != 0) { + vnormal /= vnormal_len ; + } + } +} + +/* Action passed to a neighbourhood computer to compute robust normals */ +template<typename Triangulation> +class NeighNormalCompute { + + public : + + //normals are only for dimension 3 + typedef char normals_only_meaningful_for_dimension_3[ + Triangulation::VertexDim==3 ? 1 : -1 + ] ; + + //scalar and vector + typedef typename Triangulation::Scalar Scalar ; + typedef Eigen::Matrix<Scalar,3,1> Vector ; + + NeighNormalCompute( const Triangulation* mesh, + const Scalar* queries, + const unsigned int* indices, + Scalar radius, + Scalar* output, + bool translate_queries = true + ) : mesh_(mesh), + queries_(queries), + indices_(indices), + translate_queries_(translate_queries), + radius_(radius), + output_(output) {} ; + + NeighNormalCompute( const Triangulation* mesh, + const Scalar* queries, + Scalar radius, + Scalar* output + ) : mesh_(mesh), + queries_(queries), + indices_(NULL), + translate_queries_(false), + radius_(radius), + output_(output) {} ; + + void operator()( unsigned int query, + unsigned int triangle + ) { + //position of the query + const unsigned int q_in_index = translate_queries_ ? indices_[query] : query ; + const Scalar* q = queries_ +3*q_in_index ; + + //vertices of the triangle + const unsigned int* tverts = mesh_->face(triangle) ; + + //area of the triangle portion within a ball + //of the provided radius around the query + const Scalar clip_area = triangle_sphere_intersection_area<3>( + mesh_->vertex(tverts[0]), + mesh_->vertex(tverts[1]), + mesh_->vertex(tverts[2]), + q, + radius_ + ) ; + + if(clip_area == 0) return ; + + //normal of the mesh triangle + Vector normal ; + face_normal(mesh_, triangle, normal.data()) ; + + //add the contribution to the normalif the normal is not 0 + if(normal.dot(normal) != 0) { + const unsigned int q_out_index = indices_ == NULL ? query : indices_[query] ; + Eigen::Map<Vector> n_out(output_ + 3*q_out_index) ; + n_out += clip_area*normal ; + } + } + + private : + + const Triangulation* mesh_ ; + const Scalar* queries_ ; + const unsigned int* indices_ ; + bool translate_queries_ ; + Scalar radius_ ; + Scalar* output_ ; + +} ; + +/* Robust normals on boundary vertices (handling disconnected meshes) */ +template<typename Mesh> +void robust_vertex_normals( const Mesh* mesh, + typename Mesh::Scalar radius, + typename Mesh::Scalar* output + ) { + assert((Mesh::VertexDim == 3) && "normals are only defined for 3D meshes") ; + //scalar type + typedef typename Mesh::Scalar Scalar ; + //vector type + typedef Eigen::Matrix<Scalar,3,1> Vector ; + + //compute normals based on connectivity + connected_vertex_normals(mesh,output) ; + + //for boundary normals, use the geometric neighbourhood instead + //get boundary vertices + std::vector<unsigned int> boundary_vertices ; + std::vector<bool> boundary_flag(mesh->vertices_size(),false) ; + //iterate over faces + for(unsigned int face = 0; face < mesh->faces_size(); ++face) { + //size of the face + const unsigned int fsize = mesh->face_size(face) ; + //vertices of the face + const unsigned int* vertices = mesh->face(face) ; + //neighbours of the face + const unsigned int* neighbours = mesh->face_neighbours(face) ; + //circulate around the face to find border vertices + for(unsigned int edge = 0; edge < fsize; ++edge) { + //test if the edge is a boundary edge + if(neighbours[edge] == Mesh::NO_NEIGHBOUR) { + //boundary edge get its vertices + const unsigned int v1 = vertices[ edge ] ; + const unsigned int v2 = vertices[(edge+1)%fsize] ; + //mark vertices as handled, and add them to the list + if(!boundary_flag[v1]) { + boundary_vertices.push_back(v1) ; + } + if(!boundary_flag[v2]) { + boundary_vertices.push_back(v2) ; + } + } + } + } + //cleanup + std::vector<bool>().swap(boundary_flag) ; + + //check if any vertex is on the boundary + const unsigned int bvsize = boundary_vertices.size() ; + if(bvsize > 0) { + //get the boundary vertices coordinates + std::vector<Scalar> queries(3*bvsize) ; + for(unsigned int vertex = 0; vertex < bvsize; ++vertex) { + const unsigned int v = boundary_vertices[vertex] ; + std::copy(mesh->vertex(v), mesh->vertex(v)+3, queries.data() + 3*vertex) ; + for(int i = 0;i < 3; ++i) { + output[3*v+i] = 0 ; + } + } + + //compute normals on geometric neighbourhoods + NeighbourhoodComputer<Mesh> n_computer(mesh,queries.data(),bvsize) ; + NeighNormalCompute<Mesh> action( mesh, + queries.data(), + radius, + output + ) ; + n_computer.compute(radius,action) ; + + //normalize the boundary vertex normals + for(unsigned int vertex = 0; vertex < bvsize; ++vertex) { + const unsigned int v = boundary_vertices[vertex] ; + Eigen::Map<Vector> normal(output + 3*v) ; + const Scalar n_len = normal.norm() ; + if(n_len != 0) { + normal /= n_len ; + } + } + } +} + +/* Robust normals on every vertex */ +template<typename Mesh> +void full_robust_vertex_normals( const Mesh* mesh, + typename Mesh::Scalar radius, + typename Mesh::Scalar* output + ) { + assert((Mesh::VertexDim == 3) && "normals are only defined for 3D meshes") ; + //scalar type + typedef typename Mesh::Scalar Scalar ; + //vector type + typedef Eigen::Matrix<Scalar,3,1> Vector ; + + const Scalar* queries ; + std::vector<Scalar> vertex_copy ; + if(Mesh::VertexOffset == 0) { + queries = mesh->vertices() ; + } else { + //the vertex array is has an offset, copy the vertices + for(unsigned int vertex = 0; vertex < mesh->vertices_size() ; ++vertex) { + const Scalar* v = mesh->vertex(vertex) ; + vertex_copy.insert(vertex_copy.end(), v, v+3) ; + } + queries = vertex_copy.data() ; + } + //compute normals on geometric neighbourhoods + NeighbourhoodComputer<Mesh> n_computer( mesh, + queries, + mesh->vertices_size() + ) ; + NeighNormalCompute<Mesh> action( mesh, + queries, + radius, + output + ) ; + n_computer.compute(radius,action) ; + + //normalize the vertex normals + for(unsigned int vertex = 0; vertex < mesh->vertices_size(); ++vertex) { + Eigen::Map<Vector> normal(output + 3*vertex) ; + const Scalar n_len = normal.norm() ; + if(n_len != 0) { + normal /= n_len ; + } + } +} + +/* Connected edge normals based on the mesh connectivty */ +template< typename Mesh > +void connected_edge_normals( const Mesh* mesh, + typename Mesh::Scalar* output + ) { + assert((Mesh::VertexDim == 3) && "normals are only defined for 3D meshes") ; + //scalar type + typedef typename Mesh::Scalar Scalar ; + //vector type + typedef Eigen::Matrix<Scalar,Mesh::VertexDim,1> Vector ; + + //face normals + std::vector<Scalar> face_normals(3*mesh->faces_size(), 0) ; + for(unsigned int face = 0; face < mesh->faces_size(); ++face) { + //number of vertices/edges for this face + const unsigned int fsize = mesh->face_size(face) ; + if(fsize < 3) continue ; + face_normal(mesh,face,face_normals.data() + 3*face) ; + } + + //edge layer + EdgeLayer<Mesh> edge_layer(mesh) ; + + //edge normals + for(unsigned int edge = 0; edge < mesh->face_vertices_size(); ++edge) { + Eigen::Map<Vector> enormal(output + 3*edge) ; + //face of the edge + const unsigned int eface = edge_layer.edge_face(edge) ; + //add the face contribution + Eigen::Map<const Vector> fnormal(face_normals.data() + 3*eface) ; + enormal += fnormal ; + //test if the edge is on the boundary + if(!edge_layer.is_border(edge)) { + //not on boundary, opposite edge and face + const unsigned int oedge = edge_layer.edge_opposite(edge) ; + const unsigned int oface = edge_layer.edge_face(oedge) ; + //add the opposite face contribution + Eigen::Map<const Vector> ofnormal(face_normals.data() + 3*oface) ; + enormal += ofnormal ; + //normalize + const Scalar n_len = enormal.norm() ; + if(n_len != 0) { + enormal /= n_len ; + } + } + } +} + +/* Robust edge normals */ +template<typename Mesh> +void robust_edge_normals( const Mesh* mesh, + typename Mesh::Scalar radius, + typename Mesh::Scalar* output + ) { + assert((Mesh::VertexDim == 3) && "normals are only defined for 3D meshes") ; + //scalar type + typedef typename Mesh::Scalar Scalar ; + //vector type + typedef Eigen::Matrix<Scalar,3,1> Vector ; + + //compute normals based on connectivity + connected_edge_normals(mesh,output) ; + + //for boundary edge normals, use the geometric neighbourhood instead + //collect boundary edges, and their midpoint + std::vector<unsigned int> boundary_edges ; + std::vector<Scalar> edge_midpoints ; + //edge layer + EdgeLayer<Mesh> edge_layer(mesh) ; + for(unsigned int edge = 0; edge < mesh->face_vertices_size(); ++edge) { + if(edge_layer.is_border(edge)) { + //add the edge to the boundary edges + boundary_edges.push_back(edge) ; + //vertices of the edge + const unsigned int v0 = edge_layer.edge_start_vertex(edge) ; + const unsigned int v1 = edge_layer.edge_end_vertex(edge) ; + Eigen::Map<const Vector> v0_pos(mesh->vertex(v0)) ; + Eigen::Map<const Vector> v1_pos(mesh->vertex(v1)) ; + //add its midpoint + const unsigned int midpos = edge_midpoints.size() ; + edge_midpoints.resize(midpos+3) ; + Eigen::Map<Vector> midpoint(edge_midpoints.data() + midpos) ; + midpoint = 0.5*(v0_pos + v1_pos) ; + } + } + + //check if any edge is on the boundary + const unsigned int besize = boundary_edges.size() ; + if(besize > 0) { + //reset the edge normals of the boundary edges + for(unsigned int e_index = 0; e_index < boundary_edges.size(); ++e_index) { + const unsigned int edge = boundary_edges[e_index] ; + for(int i = 0; i < 3; ++i) { + output[3*edge+i] = 0 ; + } + } + + //compute normals on geometric neighbourhoods + NeighbourhoodComputer<Mesh> n_computer( mesh, + edge_midpoints.data(), + besize + ) ; + NeighNormalCompute<Mesh> action( mesh, + edge_midpoints.data(), + boundary_edges.data(), + radius, + output, + false + ) ; + n_computer.compute(radius,action) ; + + //normalize the boundary edge normals + for(unsigned int e_index = 0; e_index < boundary_edges.size(); ++e_index) { + const unsigned int edge = boundary_edges[e_index] ; + Eigen::Map<Vector> normal(output + 3*edge) ; + const Scalar n_len = normal.norm() ; + if(n_len != 0) { + normal /= n_len ; + } + } + } +} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/normals_fwd.hpp b/contrib/Revoropt/include/Revoropt/Mesh/normals_fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a8807bc34a22f8f0e9a43ada34022daf2549d89f --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/normals_fwd.hpp @@ -0,0 +1,58 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_NORMALS_FWD_HPP_ +#define _REVOROPT_MESH_NORMALS_FWD_HPP_ + +namespace Revoropt { + +/* Face normal */ +template<typename Mesh> +void face_normal ( const Mesh* mesh, + unsigned int index, + typename Mesh::Scalar* output + ) ; + +/* Connected normals (based on the faces connected to the vertex) */ +template<typename Mesh> +void connected_vertex_normals( const Mesh* mesh, + typename Mesh::Scalar* output + ) ; + +/* Robust normals on boundary vertices (handling disconnected meshes) */ +template<typename Mesh> +void robust_vertex_normals( const Mesh* mesh, + typename Mesh::Scalar radius, + typename Mesh::Scalar* output + ) ; + +/* Robust normals on every vertex */ +template<typename Mesh> +void full_robust_vertex_normals( const Mesh* mesh, + typename Mesh::Scalar radius, + typename Mesh::Scalar* output + ) ; + +/* Connected edge normals baseg on the mesh connectivty */ +template< typename Mesh > +void connected_edge_normals( const Mesh* mesh, + typename Mesh::Scalar* output + ) ; + +/* Robust edge normals */ +template<typename Mesh> +void robust_edge_normals( const Mesh* mesh, + typename Mesh::Scalar radius, + typename Mesh::Scalar* output + ) ; + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/sampling_def.hpp b/contrib/Revoropt/include/Revoropt/Mesh/sampling_def.hpp new file mode 100644 index 0000000000000000000000000000000000000000..42aeee578eadf6cfdb4da58d9c8251a0138d6058 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/sampling_def.hpp @@ -0,0 +1,97 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_SAMPLING_HPP_ +#define _REVOROPT_MESH_SAMPLING_HPP_ + +#include <Revoropt/Tools/measure_def.hpp> + +#include "sampling_fwd.hpp" + +namespace Revoropt { + +/* Generate a random set of sites on a mesh */ +template<typename Triangulation> +void generate_random_sites( const Triangulation* mesh, + unsigned int sites_size, + typename Triangulation::Scalar* output_sites, + typename Triangulation::Scalar* triangle_weights, + bool reset_random + ) { + //typedefs + enum { Dim = Triangulation::VertexDim } ; + typedef typename Triangulation::Scalar Scalar ; + typedef Eigen::Matrix<Scalar,Dim,1> Vector ; + + //initialize random generator if requested + if(reset_random) { + srand(0) ; + } + + //sizes + const unsigned int fsize = mesh->faces_size() ; + + //compute the area range of the triangles (without cross products) + Scalar area_ranges[fsize] ; + for(unsigned int i=0; i<fsize; ++i) { + const unsigned int* fverts = mesh->face(i) ; + const Scalar* v0 = mesh->vertex(fverts[0]) ; + const Scalar* v1 = mesh->vertex(fverts[1]) ; + const Scalar* v2 = mesh->vertex(fverts[2]) ; + + const Scalar area = triangle_weights == NULL ? + triangle_area<Dim>(v0,v1,v2) : + triangle_weights[i]*triangle_area<Dim>(v0,v1,v2) ; + + area_ranges[i] = i==0 ? area : area_ranges[i-1]+area ; + if(area_ranges[i] != area_ranges[i]) { + std::cout << "nan area range for face " << i << ", area " << area_ranges[i] << std::endl ; + for(int j = 0; j < Dim; ++j) { std::cout << v0[j] << " " ; } ; + std::cout << std::endl ; + for(int j = 0; j < Dim; ++j) { std::cout << v1[j] << " " ; } ; + std::cout << std::endl ; + for(int j = 0; j < Dim; ++j) { std::cout << v2[j] << " " ; } ; + std::cout << std::endl ; + if(triangle_weights != NULL) { + std::cout << triangle_weights[i] << std::endl ; + } + assert(false) ; + } + } + const Scalar max_area = area_ranges[fsize-1] ; + + //generate random sites + for(unsigned int i=0; i<sites_size; ++i) { + //get a random triangle by area + Scalar rand_area = ((Scalar) rand() / (Scalar) RAND_MAX) * max_area ; + Scalar* pos = std::upper_bound(area_ranges,area_ranges+fsize,rand_area) ; + unsigned int tri_index = pos - area_ranges ; + + //get random barycentric coordinates + Scalar u = sqrt((Scalar) rand() / (Scalar) RAND_MAX) ; + Scalar v = (Scalar) rand() / (Scalar) RAND_MAX ; + v *= u ; + u = 1-u ; + Scalar w = 1-u-v ; + + const unsigned int* fverts = mesh->face(tri_index) ; + Eigen::Map<const Vector> v0(mesh->vertex(fverts[0])) ; + Eigen::Map<const Vector> v1(mesh->vertex(fverts[1])) ; + Eigen::Map<const Vector> v2(mesh->vertex(fverts[2])) ; + + //add the new site + Eigen::Map<Vector> site(output_sites + Dim*i) ; + site = u*v0 + v*v1 + w*v2 ; + } +} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/sampling_fwd.hpp b/contrib/Revoropt/include/Revoropt/Mesh/sampling_fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..227caebba7905f6fd21742547862c29cf0445239 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/sampling_fwd.hpp @@ -0,0 +1,27 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_SAMPLING_FWD_HPP_ +#define _REVOROPT_MESH_SAMPLING_FWD_HPP_ + +namespace Revoropt { + +/* Generate a random set of sites on a mesh */ +template<typename Triangulation> +void generate_random_sites( const Triangulation* mesh, + unsigned int sites_size, + typename Triangulation::Scalar* output_sites, + typename Triangulation::Scalar* triangle_weights = NULL, + bool reset_random = false + ) ; + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/splitting_def.hpp b/contrib/Revoropt/include/Revoropt/Mesh/splitting_def.hpp new file mode 100644 index 0000000000000000000000000000000000000000..025b7d45205cd4ff6a4797407ec9ea70e10af017 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/splitting_def.hpp @@ -0,0 +1,1076 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_SPLITTING_DEF_HPP_ +#define _REVOROPT_MESH_SPLITTING_DEF_HPP_ + +#include "splitting_fwd.hpp" +#include "all_def.hpp" +#include "debug_def.hpp" +#include "connectivity_def.hpp" +#include "measure_def.hpp" +#include "normals_def.hpp" + +#include <eigen3/Eigen/Dense> +#include <queue> + +namespace Revoropt { + +/**{{{ Splitting edges in halves (decimation) **/ + +/* Split a face of the mesh in two given an edge index and the index of its + * center vertex. The neighbourhoods along the split edge are set to none. + * Returns the indices of the two split faces and their sizes. within these + * faces, the layout of the vertices is such that : + * - the first face uses the mid vertex and the next one along the edge + * - the second face uses the first vertex of the edge and the mid one + * - the mid vertex is the last vertex in both faces */ +template<typename MeshBuilder> +void face_split( MeshBuilder* mesh, + unsigned int face, + unsigned int edge_index, + unsigned int mid_vertex_index, + unsigned int* split_face_indices //output + ) { + //size of the face + const unsigned int fsize = mesh->face_size(face) ; + //vertices of the face + const unsigned int* fverts = mesh->face(face) ; + //neighbours of the face + const unsigned int* fneigh = mesh->face_neighbours(face) ; + + //copy the face vertices + std::vector<unsigned int> fverts_copy(fverts, fverts+fsize) ; + //copy the face neighbours + std::vector<unsigned int> fneigh_copy(fneigh, fneigh+fsize) ; + + //split face sizes + unsigned int split_face_sizes[2] ; + split_face_sizes[0] = fsize/2 + 2 ; + split_face_sizes[1] = fsize - split_face_sizes[0] + 3 ; + + //allocation + if(fsize < 5) { + //the first new face exactly has the size of the split face + //indices of the new fces + split_face_indices[0] = face ; + //compatibility of the mesh face size is checked here + split_face_indices[1] = mesh->extend_faces(1,split_face_sizes[1]) ; + } else { + //translate face vector to remove one face + mesh->erase_faces(face, face+1) ; + //allocate space for the new faces + //indices of the new faces + //compatibility of the mesh face size is checked here + split_face_indices[0] = mesh->extend_faces(1,split_face_sizes[0]) ; + split_face_indices[1] = mesh->extend_faces(1,split_face_sizes[1]) ; + } + + //index of the vertex to connect to the mid vertex of the split edge + const unsigned int vopp = (edge_index + fsize/2 + 1) % fsize ; + + //fill the first face vertices and neighbours + unsigned int* split_verts0 = mesh->face(split_face_indices[0]) ; + unsigned int* split_neigh0 = mesh->face_neighbours(split_face_indices[0]) ; + for(unsigned int k = 0 ; k < split_face_sizes[0]-1 ; ++k) { + //vertex + const unsigned int v0 = fverts_copy[(edge_index+1+k)%fsize] ; + //set a vertex of the new face + split_verts0[k] = v0 ; + if(k != split_face_sizes[0]-2) { + //neighbouring face + const unsigned int n_face = fneigh_copy[(edge_index+1+k)%fsize] ; + //next vertex + const unsigned int v1 = fverts_copy[(edge_index+2+k)%fsize] ; + //set a neighbour of the new face + split_neigh0[k] = n_face ; + //set the neighbour of the neighbour face + if(n_face != MeshBuilder::NO_NEIGHBOUR) { + const unsigned int n_edge_index = mesh->edge_face_index(n_face,v0,v1) ; + assert( n_edge_index != MeshBuilder::NO_INDEX && + "error : connectivity mismatch" + ) ; + mesh->face_neighbours(n_face)[n_edge_index] = split_face_indices[0] ; + } + } + } + //add the new vertex to the first face + split_verts0[split_face_sizes[0]-1] = mid_vertex_index ; + + //fill the second face vertices + unsigned int* split_verts1 = mesh->face(split_face_indices[1]) ; + unsigned int* split_neigh1 = mesh->face_neighbours(split_face_indices[1]) ; + for(unsigned int k = 0 ; k < split_face_sizes[1]-1 ; ++k) { + //vertex + const unsigned int v0 = fverts_copy[(vopp+k)%fsize] ; + //set a vertex of the new face + split_verts1[k] = v0 ; + if(k != split_face_sizes[1]-2) { + //neighbouring face + const unsigned int n_face = fneigh_copy[(vopp+k)%fsize] ; + //next vertex + const unsigned int v1 = fverts_copy[(vopp+k+1)%fsize] ; + //set a neighbour of the new face + split_neigh1[k] = n_face ; + if(n_face != MeshBuilder::NO_NEIGHBOUR) { + //set the neighbour of the neighbour face + const unsigned int n_edge_index = mesh->edge_face_index(n_face,v0,v1) ; + assert( n_edge_index != MeshBuilder::NO_INDEX && + "error : connectivity mismatch" + ) ; + mesh->face_neighbours(n_face)[n_edge_index] = split_face_indices[1] ; + } + } + } + //add the new vertex to the second face + split_verts1[split_face_sizes[1]-1] = mid_vertex_index ; + + //set the neighbourhoods along the split edge + split_neigh0[split_face_sizes[0]-2] = split_face_indices[1] ; + split_neigh1[split_face_sizes[1]-1] = split_face_indices[0] ; + + //set the neighbourhoods along the pieces of the split edge to NO_NEIGHBOUR + split_neigh0[split_face_sizes[0]-1] = MeshBuilder::NO_NEIGHBOUR ; + split_neigh1[split_face_sizes[1]-2] = MeshBuilder::NO_NEIGHBOUR ; + +} + +/* Split an edge of a mesh builder. If the provided arrays are not NULL, returns + * the indices and sizes of the new faces. The size of the arrays to provide is + * two in case of a border edge, and 4 otherwise.*/ +template<typename MeshBuilder> +void edge_split( MeshBuilder* mesh, + unsigned int face, + unsigned int edge_index, + unsigned int* split_face_indices = NULL //output + ) { + //types + typedef Eigen::Matrix< typename MeshBuilder::Scalar, + MeshBuilder::VertexDim, + 1 + > Vector ; + + //handle NULL output + bool handle_null_input = split_face_indices == NULL ; + if(handle_null_input){ + split_face_indices = new unsigned int[4] ; + } + + //size of the face + const unsigned int fsize = mesh->face_size(face) ; + //vertices of the face + const unsigned int* fverts = mesh->face(face) ; + //neighbours of the face + const unsigned int* fneigh = mesh->face_neighbours(face) ; + + //vertices of the edge + const unsigned int v1 = fverts[ edge_index ] ; + const unsigned int v2 = fverts[(edge_index+1)%fsize] ; + + //reserve space for a new vertex + const unsigned int vmid = mesh->extend_vertices(1) ; + + //add the edge center to the vertices + Eigen::Map<const Vector> v1pos(mesh->vertex(v1)) ; + Eigen::Map<const Vector> v2pos(mesh->vertex(v2)) ; + Eigen::Map<Vector> vmidpos(mesh->vertex(vmid)) ; + vmidpos = 0.5*(v1pos+v2pos) ; + + //neighbouring face along the edge + const unsigned int n_face = fneigh[edge_index] ; + + //check if the face edge is on the buondary + if(n_face == MeshBuilder::NO_NEIGHBOUR) { + //the edge is on the boundary, no neighbouring face to handle + //split the face + face_split( mesh, + face, edge_index, vmid, + split_face_indices + ) ; + } else { + //a neighbouring face exists and is to be handled + //index of the edge within the neighbouring face + unsigned int n_edge_index = mesh->edge_face_index(n_face,v1,v2) ; + assert( n_edge_index != MeshBuilder::NO_INDEX && + "error : connectivity mismatch." + ) ; + //determine orientation + const unsigned int shift = mesh->face(n_face)[n_edge_index]==v2 ? 0 : 1 ; + //split the face + face_split( mesh, + face, edge_index, vmid, + split_face_indices + ) ; + //split the neighbouring face + face_split( mesh, + n_face, n_edge_index, vmid, + split_face_indices + 2 + ) ; + + //reconnect the split faces along the split edge + //neighbourhoods of the split faces + unsigned int* split_fneigh[4] ; + unsigned int split_fsize[4] ; + for(int i=0; i<4; ++i) { + split_fneigh[i] = mesh->face_neighbours(split_face_indices[i]) ; + split_fsize[i] = mesh->face_size(split_face_indices[i]) ; + } + //fisrt face from the split of face + const unsigned int s_face0 = split_face_indices[0] ; + //second face from the split of face + const unsigned int s_face1 = split_face_indices[1] ; + //face from the split of n_face next to the first split face of face + const unsigned int ns_face0 = split_face_indices[3-shift] ; + //face from the split of n_face next to the second split face of face + const unsigned int ns_face1 = split_face_indices[2+shift] ; + //connect + split_fneigh[0][split_fsize[0]-1] = ns_face0 ; + split_fneigh[3-shift][split_fsize[3-shift]-2+shift] = s_face0 ; + split_fneigh[1][split_fsize[1]-2] = ns_face1 ; + split_fneigh[2+shift][split_fsize[2+shift]-1-shift] = s_face1 ; + } + + //cleanup if necessary + if(handle_null_input){ + delete[] split_face_indices ; + } +} + +template<typename Mesh, typename MeshBuilder> +void split_longest_edge( const Mesh* input, + unsigned int niter, + MeshBuilder* mesh + ) { + //dimension + enum { Dim = Mesh::VertexDim } ; + //scalar type + typedef typename Mesh::Scalar Scalar ; + //vertex pair + typedef std::pair<unsigned int, unsigned int> VertexPair ; + //heap edge + typedef Decimate::HeapEdge<Scalar> HeapEdge ; + + //initialize the output mesh + mesh->init(input) ; + + //the longest edge for each face + std::vector<VertexPair> longest_edge(mesh->faces_size()) ; + + //a heap to track the longest edge + std::vector<HeapEdge> edge_heap ; + + //initialize the heap and longest edges + for(unsigned int face = 0; face < mesh->faces_size(); ++face) { + //size of the face + const unsigned int fsize = mesh->face_size(face) ; + //vertices of the face + const unsigned int* fverts = mesh->face(face) ; + //neighbours of the face + const unsigned int* fneigh = mesh->face_neighbours(face) ; + //longest edge of the face + Scalar length ; + unsigned int edge = face_longest_edge(mesh, face, &length) ; + //vertices of the edge + const unsigned int v0 = fverts[ edge ] ; + const unsigned int v1 = fverts[(edge+1)%fsize] ; + //sort indices + const unsigned int vmin = v0 < v1 ? v0 : v1 ; + const unsigned int vmax = v0 < v1 ? v1 : v0 ; + //add the longest edge for the current face + longest_edge[face] = VertexPair(vmin,vmax) ; + //check if that edge was already added into the heap + const unsigned int n_face = fneigh[edge] ; + if((n_face > face) || (longest_edge[n_face] != VertexPair(vmin,vmax))) { + //the edge has not been added, add it + edge_heap.resize(edge_heap.size()+1) ; + HeapEdge& he = edge_heap.back() ; + he.face = face ; + he.edge = edge ; + he.vmin = vmin ; + he.vmax = vmax ; + he.length = length ; + } + } + + //sort the heap + std::make_heap(edge_heap.begin(), edge_heap.end()) ; + + //TODO erase from longest edge if original face not reused + + //iteratively split the longest edge + unsigned int new_faces[4] ; + for(unsigned int iter = 0; iter < niter; ++iter) { + //get the longest edge + const HeapEdge& he = edge_heap.front() ; + Scalar ref_len ; + //number of faces before the split + const unsigned int mesh_size = mesh->faces_size() ; + //opposite face split + const unsigned int split_nface = mesh->face_neighbours(he.face)[he.edge] ; + + //split the edge + edge_split(mesh, he.face, he.edge, new_faces) ; + + //number of faces split (1 or 2 depending on whether the edge is a boundary) + const unsigned int nsplits = mesh->faces_size() - mesh_size ; + + //update longest edge array in case of face erase + if(mesh->face_size(he.face) > 4) { + longest_edge.erase(longest_edge.begin()+he.face) ; + } + if( (split_nface != Mesh::NO_NEIGHBOUR) && + (mesh->face_size(split_nface) > 4) ) { + longest_edge.erase(longest_edge.begin()+split_nface) ; + } + + //remove the edge from the heap + std::pop_heap(edge_heap.begin(), edge_heap.end()); + edge_heap.pop_back() ; + + //resize longest edge array + longest_edge.resize(longest_edge.size()+nsplits) ; + + //add (if necessary) the longest face edges of the new faces + for(unsigned int i = 0; i < 2*nsplits; ++i ) { + //new face + const unsigned int face = new_faces[i] ; + //size of the face + const unsigned int fsize = mesh->face_size(face) ; + //vertices of the face + const unsigned int* fverts = mesh->face(face) ; + //neighbours of the face + const unsigned int* fneigh = mesh->face_neighbours(face) ; + //longest edge of the face + Scalar length ; + unsigned int edge = face_longest_edge(mesh, face, &length) ; + //vertices of the edge + const unsigned int v0 = fverts[ edge ] ; + const unsigned int v1 = fverts[(edge+1)%fsize] ; + //sort indices + const unsigned int vmin = v0 < v1 ? v0 : v1 ; + const unsigned int vmax = v0 < v1 ? v1 : v0 ; + //add the longest edge for the current face + longest_edge[face] = VertexPair(vmin,vmax) ; + //check if that edge was already added into the heap + const unsigned int n_face = fneigh[edge] ; + bool n_face_is_new_and_after = false ; + for(unsigned int new_face = i+1; new_face < 2*nsplits; ++new_face) { + if(new_face < 4 && n_face == new_faces[new_face]) { + n_face_is_new_and_after = true ; + break ; + } + } + if( n_face_is_new_and_after || (n_face == Mesh::NO_NEIGHBOUR) || + (longest_edge[n_face] != VertexPair(vmin,vmax)) + ) { + //the edge has not been added, add it + edge_heap.resize(edge_heap.size()+1) ; + HeapEdge& new_he = edge_heap.back() ; + new_he.face = face ; + new_he.edge = edge ; + new_he.vmin = vmin ; + new_he.vmax = vmax ; + new_he.length = length ; + //preserve the heap property + std::push_heap(edge_heap.begin(), edge_heap.end()) ; + } else { + } + } + } +} + +//}}} + +/**{{{ Normal bevel : split triangles to improve normal interpolation **/ + +namespace Bevel { + +/* criterion based on the normal to decide whether an edge needs splitting */ +template<typename Mesh> +EdgeSplitType NormalSplitCriterion<Mesh>::edge_type( unsigned int face, + unsigned int edge + ) const { + //edge index + const unsigned int edge_index = mesh_->face_offset(face) + edge ; + //face size + const unsigned int fsize = mesh_->face_size(face) ; + //face vertices + const unsigned int* fverts = mesh_->face(face) ; + //edge vertices + const unsigned int v0 = fverts[edge] ; + const unsigned int v1 = fverts[(edge+1)%fsize] ; + //in case of boundary restriction, do not split inner edges + if(restrict_to_boundary_) { + if(!boundary_vertices_[v0] && !boundary_vertices_[v1]) { + return SPLIT_NONE ; + } + } + ////other vertices centroid + //Vector g = Vector::Zero() ; + //for(unsigned int v = 2; v < fsize; ++v) { + // g += Eigen::Map<const Vector>(mesh_->vertex(fverts[(edge+v)%fsize])) ; + //} + //g /= fsize-2 ; + ////opposite angle criterion + //const Vector gv0 = g - Eigen::Map<const Vector>(mesh_->vertex(v0)) ; + //const Vector gv1 = g - Eigen::Map<const Vector>(mesh_->vertex(v1)) ; + //if(gv0.dot(gv1)/(gv0.norm()*gv1.norm()) > cosine_threshold_) { + // return SPLIT_NONE ; + //} + //normals + Eigen::Map<const Vector> enormal(edge_normals_.data() + 3*edge_index) ; + Eigen::Map<const Vector> vnormal0(vertex_normals_.data() + 3*v0) ; + Eigen::Map<const Vector> vnormal1(vertex_normals_.data() + 3*v1) ; + //test + int split_type = 0 ; + if(enormal.dot(vnormal0) < cosine_threshold_) { + split_type += 1 ; + } else { + //std::cout << enormal.dot(vnormal0) << std::endl ; + } + if(enormal.dot(vnormal1) < cosine_threshold_) { + split_type += 2 ; + } else { + //std::cout << enormal.dot(vnormal1) << std::endl ; + } + return (EdgeSplitType) split_type ; +} + +template<typename Mesh> +void NormalSplitCriterion<Mesh>::init() { + //allocate space for normals + edge_normals_.resize(3*mesh_->face_vertices_size()) ; + vertex_normals_.resize(3*mesh_->vertices_size()) ; + boundary_vertices_.assign(mesh_->vertices_size(), false) ; + //compute vertex normals + full_robust_vertex_normals(mesh_,normal_radius_,vertex_normals_.data()) ; + //compute edge normals + robust_edge_normals(mesh_,normal_radius_,edge_normals_.data()) ; + + //FIXME + EdgeLayer<Mesh> edge_layer(mesh_) ; + edge_midpoints_.assign(3*mesh_->face_vertices_size(),0) ; + for(unsigned int edge = 0; edge < mesh_->face_vertices_size(); ++edge) { + //vertices of the edge + const unsigned int v0 = edge_layer.edge_start_vertex(edge) ; + const unsigned int v1 = edge_layer.edge_end_vertex(edge) ; + if(edge_layer.is_border(edge)) { + boundary_vertices_[v0] = true ; + boundary_vertices_[v1] = true ; + } + Eigen::Map<const Vector> v0_pos(mesh_->vertex(v0)) ; + Eigen::Map<const Vector> v1_pos(mesh_->vertex(v1)) ; + //add its midpoint + Eigen::Map<Vector> midpoint(edge_midpoints_.data() + 3*edge) ; + midpoint = 0.5*(v0_pos + v1_pos) ; + } +} + +/* triangle classification, depending on its edges split types */ +void normalize_triangle_split( const EdgeSplitType edge_types[3], + FaceSplitType* split_type //output + ) { + /* precomputed triangle split type translation */ + static const unsigned char triangle_split_translate[64] = { + 0 , 1 , 1 , 2 , 1 , 3 , 4 , 5 , + 1 , 6 , 3 , 7 , 2 , 7 , 5 , 8 , + 1 , 3 , 6 , 7 , 3 , 9 , 10, 11, + 4 , 10, 10, 12, 5 , 11, 13, 14, + 1 , 4 , 3 , 5 , 6 , 10, 10, 13, + 3 , 10, 9 , 11, 7 , 12, 11, 14, + 2 , 5 , 7 , 8 , 7 , 11, 12, 14, + 5 , 13, 11, 14, 8 , 14, 14, 15 + } ; + + //initialize minimal index + split_type->index = 64 ; + //initialize rotation and symetry + split_type->rotation = 0 ; + split_type->symetry = false ; + //loop over the transformations + unsigned int cur_index = 0 ; + for(int sym = -1; sym < 2; sym += 2) { + for(int rot = 0; rot < 3; ++rot) { + //reinitialize index + cur_index = 0 ; + //compute index + for(int edge = 0; edge < 3; ++edge) { + cur_index *= 4 ; + cur_index += sym < 0 ? + edge_types[(5 + sym*(rot+edge))%3] : + edge_split_type_sym(edge_types[(5 + sym*(rot+edge))%3]) ; + } + //check if this index is minimal for this configuration +// std::cout << "score is " << cur_index << " for rotation " << rot << " and symmetry " << (sym==1) << std::endl ; + if(cur_index < split_type->index) { + //store the index, rotation and symetry + split_type->index = cur_index ; + split_type->rotation = rot ; + split_type->symetry = (sym == 1) ; + } + } + } + //translate type index + split_type->index = triangle_split_translate[split_type->index] ; +} + +/* compute the split points of an edge given its type, a radius and length */ +/* returns the split really performed given the radius and edge length */ + +template<typename Mesh> +EdgeSplitType edge_split( const Mesh* mesh, + unsigned int face, + unsigned int edge, + typename Mesh::Scalar radius, + EdgeSplitType split_type, + std::vector<typename Mesh::Scalar>& coords, //i/o + std::vector<unsigned int>& edge_split_verts //output + ) { + //dimension + enum { Dim = Mesh::VertexDim } ; + //scalar type + typedef typename Mesh::Scalar Scalar ; + //vector type + typedef Eigen::Matrix<Scalar,Mesh::VertexDim,1> Vector ; + + if(split_type == SPLIT_NONE) { + //edge is not split + edge_split_verts.push_back(NO_VERTEX) ; + edge_split_verts.push_back(NO_VERTEX) ; + } else { + //size of the face + const unsigned int fsize = mesh->face_size(face) ; + //face vertices + const unsigned int* fverts = mesh->face(face) ; + //vertices of the edge + const unsigned int v0 = fverts[ edge ] ; + const unsigned int v1 = fverts[(edge+1)%fsize] ; + //cosines of the angles at the extremities + const Scalar v0cos = 0 ;//face_angle_cos(mesh, face, edge ) ; + const Scalar v1cos = 0 ;//face_angle_cos(mesh, face, (edge+1)%fsize) ; + //vertex positions + Eigen::Map<const Vector> v0_pos(mesh->vertex(v0)) ; + Eigen::Map<const Vector> v1_pos(mesh->vertex(v1)) ; + //edge vector + const Vector edge_vect = (v1_pos - v0_pos) ; + //length of the edge + const Scalar e_len = edge_vect.norm() ; + //lehgths of the cuts at each extremity + const Scalar v0len = v0cos == 1 ? e_len : radius/sqrt(1-v0cos*v0cos) ; + const Scalar v1len = v1cos == 1 ? e_len : radius/sqrt(1-v1cos*v1cos) ; + if(split_type == SPLIT_BOTH) { + //edge is split at both ends + //handle merging + if(e_len > (v0len + v1len + radius)) { + //no merging + //split vertex indices + const unsigned int vsplit0 = coords.size()/Dim ; + const unsigned int vsplit1 = vsplit0+1 ; + //allocate space for new vertices + coords.resize(coords.size() + 2*Dim) ; + //split vertex positions + Eigen::Map<Vector> vsplit0_pos(coords.data() + Dim*vsplit0) ; + Eigen::Map<Vector> vsplit1_pos(coords.data() + Dim*vsplit1) ; + vsplit0_pos = v0_pos + v0len/e_len*edge_vect ; + vsplit1_pos = v1_pos - v1len/e_len*edge_vect ; + //append split vertex indices + edge_split_verts.push_back(vsplit0) ; + edge_split_verts.push_back(vsplit1) ; +// std::cout << "split both at " << vsplit0 << " " << vsplit1 << std::endl ; + return SPLIT_BOTH ; + } else if(e_len > (v0len + v1len)) { + //merging at the edge barycenter with weights v0len and v1len + //split vertex index + const unsigned int vsplit = coords.size()/Dim ; + //allocate space for new vertices + coords.resize(coords.size() + Dim) ; + //split vertex positions + Eigen::Map<Vector> vsplit_pos(coords.data() + Dim*vsplit) ; + vsplit_pos = (v1len*v0_pos + v0len*v1_pos) / (v0len + v1len) ; + //append split vertex indices, duplicated here because of the merge + edge_split_verts.push_back(vsplit) ; + edge_split_verts.push_back(vsplit) ; + return SPLIT_BOTH ; + } else { + //splitting canceled + edge_split_verts.push_back(NO_VERTEX) ; + edge_split_verts.push_back(NO_VERTEX) ; + return SPLIT_NONE ; + } + } else { + //one single end is split + //handle merging + const Scalar cutlen = split_type == SPLIT_FIRST ? v0len : v1len ; + if(e_len > (cutlen + radius)) { + //no merging + //split vertex index + const unsigned int vsplit = coords.size()/Dim ; + //allocate space for new vertices + coords.resize(coords.size() + Dim) ; + //split vertex positions + Eigen::Map<Vector> vsplit_pos(coords.data() + Dim*vsplit) ; + if(split_type == SPLIT_FIRST) { + vsplit_pos = v0_pos + cutlen/e_len*edge_vect ; + //append split vertex indices + edge_split_verts.push_back(vsplit) ; + edge_split_verts.push_back(NO_VERTEX) ; + return SPLIT_FIRST ; + } else { + vsplit_pos = v1_pos - cutlen/e_len*edge_vect ; + //append split vertex indices + edge_split_verts.push_back(NO_VERTEX) ; + edge_split_verts.push_back(vsplit) ; + return SPLIT_SECOND ; + } + } else { + //splitting canceled + edge_split_verts.push_back(NO_VERTEX) ; + edge_split_verts.push_back(NO_VERTEX) ; + return SPLIT_NONE ; + } + } + } + return SPLIT_NONE ; +} + +/* split a face given its split type and the edge split vertices */ +template<typename Mesh> +void face_split( const Mesh* mesh, + unsigned int face, + typename Mesh::Scalar radius, + const EdgeSplitType edge_types[3], + const unsigned int edge_split_verts[6], + std::vector<typename Mesh::Scalar>& coords, //output + std::vector<unsigned int>& split_faces //output + ) { + /* predifined triangle split patterns Ref edge types */ + static const unsigned char triangle_split_0[3] = { //0 0 0 + 0,1,2 + } ; + static const unsigned char triangle_split_1[6] = { //1 0 0 + 0,1,3,1,2,3 + } ; + static const unsigned char triangle_split_2[9] = { //3 0 0 + 0,1,4,1,2,4,2,3,4 + } ; + static const unsigned char triangle_split_3[9] = { //1 1 0 + 0,1,4,1,2,3,1,3,4 + } ; + static const unsigned char triangle_split_4[9] = { //2 1 0 + 0,1,3,1,2,3,0,3,4 + } ; + static const unsigned char triangle_split_5[12] = { //3 1 0 + 0,1,5,1,2,4,1,4,5,2,3,4 + } ; + static const unsigned char triangle_split_6[9] = { //1 2 0 + 0,1,3,0,3,4,1,2,3 + } ; + static const unsigned char triangle_split_7[12] = { //3 2 0 + 0,4,5,0,1,4,1,2,4,2,3,4 + } ; + static const unsigned char triangle_split_8[15] = { //3 3 0 + 0,5,6,0,1,5,1,4,5,1,2,4,2,3,4 + } ; + static const unsigned char triangle_split_9[12] = { //1 1 1 + 0,1,5,1,3,5,1,2,3,5,3,4 + } ; + static const unsigned char triangle_split_10[12] = { //2 1 1 + 0,1,5,1,3,5,1,2,3,5,3,4 + } ; + static const unsigned char triangle_split_11[15] = { //3 1 1 + 0,1,6,1,2,6,2,3,4,2,4,6,4,5,6 + } ; + static const unsigned char triangle_split_12[21] = { //3 2 1 + 0,1,7,0,7,6,1,2,7,2,3,7,7,3,4,6,7,4,6,4,5 + } ; + static const unsigned char triangle_split_13[15] = { //2 3 1 + 0,1,6,1,2,3,1,3,6,6,3,4,6,4,5 + } ; + static const unsigned char triangle_split_14[24] = { //3 3 1 + 0,1,8,0,8,7,1,2,8,2,4,8,2,3,4,8,5,7,8,4,5,7,5,6 + } ; + static const unsigned char triangle_split_15[39] = { //3 3 3 + 0,1,8,1,9,8,1,2,9,2,10,9,2,4,10,2,3,4,9,10,11, + 8,9,11,8,11,7,10,4,5,10,5,11,11,5,7,6,7,5 + } ; + static const unsigned char triangle_split_sizes[16] = { + 1,2,3,3,3,4,3,4,5,4,4,5,7,5,8,13 + } ; + static const unsigned char* triangle_patterns[16] = { + triangle_split_0, + triangle_split_1, + triangle_split_2, + triangle_split_3, + triangle_split_4, + triangle_split_5, + triangle_split_6, + triangle_split_7, + triangle_split_8, + triangle_split_9, + triangle_split_10, + triangle_split_11, + triangle_split_12, + triangle_split_13, + triangle_split_14, + triangle_split_15 + } ; + + //dimension + enum { Dim = Mesh::VertexDim } ; + //scalar type + typedef typename Mesh::Scalar Scalar ; + //vector type + typedef Eigen::Matrix<Scalar,Mesh::VertexDim,1> Vector ; + + //normalize the triangle split + FaceSplitType face_type ; + normalize_triangle_split(edge_types, &face_type) ; + + //collect the triangle boundary vertices + std::vector<unsigned int> triangle_vertices ; +// std::cout << "face rotation " << face_type.rotation << std::endl ; +// std::cout << "face symetry " << std::boolalpha << face_type.symetry << std::endl ; + for(int edge = 0; edge < 3; ++edge) { + //compute the edge index given the normalization + const int edge_face_index = face_type.symetry ? + (4+face_type.rotation-edge)%3 : + (3-face_type.rotation+edge)%3 ; + + //add the edge start vertex + const unsigned int edge_start_vertex = face_type.symetry ? + mesh->face_edge_end_vertex(face, edge_face_index) : + mesh->face_edge_start_vertex(face, edge_face_index) ; + triangle_vertices.push_back(edge_start_vertex) ; + + //add the edge split vertices + const unsigned int* this_edge_split_verts = + edge_split_verts + 2*edge_face_index ; + const EdgeSplitType edge_type = edge_types[edge_face_index] ; +// std::cout << "edge face index " << edge_face_index << std::endl ; +// std::cout << "edge type at collect " << edge_type << std::endl ; +// std::cout << "pushing vertex " << edge_start_vertex << std::endl ; + if(edge_type == SPLIT_FIRST) { + triangle_vertices.push_back(this_edge_split_verts[0]) ; + } else if(edge_type == SPLIT_SECOND) { + triangle_vertices.push_back(this_edge_split_verts[1]) ; + } else if(edge_type == SPLIT_BOTH) { + if(face_type.symetry) { + triangle_vertices.push_back(this_edge_split_verts[1]) ; + triangle_vertices.push_back(this_edge_split_verts[0]) ; + } else { + triangle_vertices.push_back(this_edge_split_verts[0]) ; + triangle_vertices.push_back(this_edge_split_verts[1]) ; + } + } + } +// std::cout << "configuration : " << face_type.index << std::endl ; +// std::cout << "triangle vertices size : " << triangle_vertices.size() << std::endl ; + + //add the necessary face split vertices + if(face_type.index == 12) { + //one face vertex to add + //allocate space for the face vertex + const unsigned int vsplit = coords.size()/Dim ; + coords.resize(coords.size() + Dim) ; + Eigen::Map<Vector> vsplit_pos(coords.data() + Dim*vsplit) ; + //check the face area to avoid degeneracies + if(mesh_face_area(mesh, face) < 8*radius*radius) { + //special case + //the central vertex becomes the centroid of the vertices it connects to + Eigen::Map<const Vector> pos0(coords.data()+Dim*triangle_vertices[0]) ; + Eigen::Map<const Vector> pos1(coords.data()+Dim*triangle_vertices[1]) ; + Eigen::Map<const Vector> pos2(coords.data()+Dim*triangle_vertices[2]) ; + Eigen::Map<const Vector> pos3(coords.data()+Dim*triangle_vertices[3]) ; + Eigen::Map<const Vector> pos4(coords.data()+Dim*triangle_vertices[4]) ; + Eigen::Map<const Vector> pos6(coords.data()+Dim*triangle_vertices[6]) ; + vsplit_pos = (pos0 + pos1 + pos2 + pos3 + pos4 + pos6)/6 ; + } else { + //compute the vertex position position + Eigen::Map<const Vector> pos4(coords.data()+Dim*triangle_vertices[4]) ; + Eigen::Map<const Vector> pos5(coords.data()+Dim*triangle_vertices[5]) ; + Eigen::Map<const Vector> pos6(coords.data()+Dim*triangle_vertices[6]) ; + vsplit_pos = pos4 + pos6 - pos5 ; + } + //add the vertex to the triangle vertices + triangle_vertices.push_back(vsplit) ; + } else if(face_type.index == 14) { + //one face to add + //allocate space for the face vertex + const unsigned int vsplit = coords.size()/Dim ; + coords.resize(coords.size() + Dim) ; + Eigen::Map<Vector> vsplit_pos(coords.data() + Dim*vsplit) ; + //check the face area to avoid degeneracies + if(mesh_face_area(mesh, face) < 8*radius*radius) { + //special case + //the central vertex becomes the centroid of the vertices it connects to + Eigen::Map<const Vector> pos0(coords.data()+Dim*triangle_vertices[0]) ; + Eigen::Map<const Vector> pos1(coords.data()+Dim*triangle_vertices[1]) ; + Eigen::Map<const Vector> pos2(coords.data()+Dim*triangle_vertices[2]) ; + Eigen::Map<const Vector> pos4(coords.data()+Dim*triangle_vertices[4]) ; + Eigen::Map<const Vector> pos5(coords.data()+Dim*triangle_vertices[5]) ; + Eigen::Map<const Vector> pos7(coords.data()+Dim*triangle_vertices[7]) ; + vsplit_pos = (pos0 + pos1 + pos2 + pos4 + pos5 + pos7)/6 ; + } else { + //compute its position + Eigen::Map<const Vector> pos5(coords.data()+Dim*triangle_vertices[5]) ; + Eigen::Map<const Vector> pos6(coords.data()+Dim*triangle_vertices[6]) ; + Eigen::Map<const Vector> pos7(coords.data()+Dim*triangle_vertices[7]) ; + vsplit_pos = pos5 + pos7 - pos6 ; + } + //add the vertex to the triangle vertices + triangle_vertices.push_back(vsplit) ; + } else if(face_type.index == 15) { + //three potential face vertices, merged if too close + //compute their positions + Vector fv[3] ; + for(int i = 0; i < 3; ++i) { + Eigen::Map<const Vector> v0(coords.data()+Dim*triangle_vertices[(8+3*i)%9]) ; + Eigen::Map<const Vector> v1(coords.data()+Dim*triangle_vertices[(0+3*i)%9]) ; + Eigen::Map<const Vector> v2(coords.data()+Dim*triangle_vertices[(1+3*i)%9]) ; + fv[i] = v0 + v2 - v1 ; + } + //merge the vertices if necessary + int merge_size = 0 ; + int last_merge = 0 ; + for(int i = 0; i < 3; ++i) { + //split vertices of the edge + const unsigned int sv0 = triangle_vertices[1+3*i] ; + const unsigned int sv1 = triangle_vertices[2+3*i] ; + //check whether + // 1/ the edge split vertices were merged + // 2/ the inner vertices along this edge are not too close + if((sv0 == sv1) || ((fv[i] - fv[(i+1)%3]).norm() < radius)) { + ++merge_size ; + last_merge = i ; + } + } + if(merge_size > 0) { + if(merge_size == 1) { + //two new vertices are too close and merged + //allocate space for two vertices + const unsigned int vsplit0 = coords.size()/Dim ; + const unsigned int vsplit1 = vsplit0 + 1 ; + coords.resize(coords.size() + 2*Dim) ; + Eigen::Map<Vector> vsplit0_pos(coords.data()+Dim*vsplit0) ; + Eigen::Map<Vector> vsplit1_pos(coords.data()+Dim*vsplit1) ; + //merged vertex + vsplit0_pos = 0.5*(fv[last_merge] + fv[(last_merge+1)%3]) ; + //other vertex + vsplit1_pos = fv[(last_merge+2)%3] ; + //add the vertices to the triangle vertices + for(int i = 0; i < 3; ++i) { + if(i == (last_merge+2)%3) { + triangle_vertices.push_back(vsplit1) ; + } else { + triangle_vertices.push_back(vsplit0) ; + } + } + } else { + //the three vertices are merged + //allocate space for one vertex + const unsigned int vsplit = coords.size()/Dim ; + coords.resize(coords.size() + Dim) ; + Eigen::Map<Vector> vsplit_pos(coords.data()+Dim*vsplit) ; + //merged vertex + vsplit_pos = (fv[0] + fv[1] + fv[2])/3 ; + //add the vertices to the triangle vertices + for(int i = 0; i < 3; ++i) { + triangle_vertices.push_back(vsplit) ; + } + } + } else { + //no merge + //allocate space for three vertices + const unsigned int vsplit0 = coords.size()/Dim ; + const unsigned int vsplit1 = vsplit0 + 1 ; + const unsigned int vsplit2 = vsplit1 + 1 ; + coords.resize(coords.size() + 3*Dim) ; + Eigen::Map<Vector> vsplit0_pos(coords.data()+Dim*vsplit0) ; + Eigen::Map<Vector> vsplit1_pos(coords.data()+Dim*vsplit1) ; + Eigen::Map<Vector> vsplit2_pos(coords.data()+Dim*vsplit2) ; + //copy vertex positions + vsplit0_pos = fv[0] ; + vsplit1_pos = fv[1] ; + vsplit2_pos = fv[2] ; + //add the vertices to the triangle vertices + triangle_vertices.push_back(vsplit0) ; + triangle_vertices.push_back(vsplit1) ; + triangle_vertices.push_back(vsplit2) ; + } + } + +// std::cout << "Adding split triangles" << std::endl ; +// std::cout << "Original size is " << (int) triangle_split_sizes[face_type.index] << std::endl ; + + //add the new triangles if they are not degenerate + //subdivision pattern for this triangle + const unsigned char* split_triangles = triangle_patterns[face_type.index] ; + //add the triangles + for( unsigned char triangle = 0 ; + triangle < triangle_split_sizes[face_type.index] ; + ++triangle + ) { + //vertices of the triangle + const unsigned char* tverts = split_triangles + 3*triangle ; + //check if the triangle is degenerate + bool regular = true ; + for(int i = 0; i < 3; ++i) { + if(triangle_vertices[tverts[i]] == triangle_vertices[tverts[(i+1)%3]]) { + regular = false ; + break ; + } + } + if(regular) { + //the triangle is not degenerate, add it + if(face_type.symetry) { + for(int i = 2; i >= 0; --i) { + const unsigned int vertex = triangle_vertices[tverts[i]] ; +// std::cout << "triangle vertex " << vertex << std::endl ; + split_faces.push_back(vertex) ; + } + } else { + for(int i = 0; i < 3; ++i) { + const unsigned int vertex = triangle_vertices[tverts[i]] ; +// std::cout << "triangle vertex " << vertex << std::endl ; + split_faces.push_back(vertex) ; + } + } + } else { +// std::cout << "degenerate triangle !" << std::endl ; + } + } +} + +} //end of namespace Bevel + +template< typename Triangulation, + typename TriangulationBuilder, + typename Criterion > +void bevel( const Triangulation* mesh, + const Criterion* criterion, + typename Triangulation::Scalar radius, + TriangulationBuilder* mesh_builder //output + ) { + using namespace Bevel ; + + //dimension + enum { Dim = Triangulation::VertexDim } ; + //scalar type + typedef typename Triangulation::Scalar Scalar ; + + //arrays of the new mesh + std::vector<Scalar> split_verts ; + std::vector<unsigned int> split_faces ; + + //copy the vertices of the original mesh + //FIXME problem if mesh has vertex offset + split_verts.assign( + mesh->vertices(), + mesh->vertices() + Dim*mesh->vertices_size() + ) ; + + //split the mesh faces and edges according to the criterion used + //edge split types + std::vector<EdgeSplitType> edge_types ; + edge_types.reserve(mesh->face_vertices_size()) ; + //edge split vertices + std::vector<unsigned int> edge_split_verts ; + edge_split_verts.reserve(2*mesh->face_vertices_size()) ; + //iterate over faces + //unsigned int set = 1 ; + //for(unsigned int face = 125*set; face < 125*(set+1); ++face) { + for(unsigned int face = 0; face < mesh->faces_size(); ++face) { + //face size + const unsigned int fsize = mesh->face_size(face) ; + //face vertices + const unsigned int* fverts = mesh->face(face) ; + //face neighbours + const unsigned int* fneigh = mesh->face_neighbours(face) ; + //split the edges or recover information from the opposite edge +// std::cout << "edge split" << std::endl ; + for(unsigned int edge = 0; edge < fsize; ++edge) { + //opposite face + const unsigned int opp_face = fneigh[edge] ; + if(opp_face > face || opp_face == Triangulation::NO_NEIGHBOUR) { +// std::cout << " do split" << std::endl ; + //the opposite face is not split, split the edge + //get its a priori split type + EdgeSplitType edge_type = criterion->edge_type(face, edge) ; + //split and update the split type + edge_type = edge_split( mesh, face, edge, radius, edge_type, + split_verts, edge_split_verts ) ; + //save the plis type + edge_types.push_back(edge_type) ; +// std::cout << " edge type is " << edge_type << std::endl ; + } else { +// std::cout << " copy split" << std::endl ; + //the opposite face is already split, recover edge type and vertices + //edge vertices + const unsigned int v0 = fverts[ edge ] ; + const unsigned int v1 = fverts[(edge+1)%fsize] ; + //opposite edge index in opposite face + const unsigned int opp_edge = mesh->edge_face_index(opp_face, v0, v1) ; + //opposite offset + const unsigned int opp_offset = mesh->face_offset(opp_face) ; + //check orientation + if(v1 == mesh->face_edge_start_vertex(opp_face, opp_edge)) { + //opposite orientations, switch orientation in copy + //edge type + edge_types.push_back( + edge_split_type_sym(edge_types[opp_offset + opp_edge]) + ) ; + //edge_vertices + edge_split_verts.push_back( + edge_split_verts[2*(opp_offset + opp_edge) + 1] + ) ; + edge_split_verts.push_back( + edge_split_verts[2*(opp_offset + opp_edge) ] + ) ; + } else { + //edge type + edge_types.push_back( + edge_types[opp_offset + opp_edge] + ) ; + //edge_vertices + edge_split_verts.push_back( + edge_split_verts[2*(opp_offset + opp_edge) ] + ) ; + edge_split_verts.push_back( + edge_split_verts[2*(opp_offset + opp_edge) + 1] + ) ; + } + } + } + //split the face +// std::cout << "edge type length " << edge_types.size() << std::endl ; +// std::cout << "edge split verts length " << edge_split_verts.size() << std::endl ; +// std::cout << "face split" << std::endl ; + //face offset + const unsigned int foffset = mesh->face_offset(face) ; +// std::cout << "face offset " << foffset << std::endl ; + face_split( mesh, face, radius, + edge_types.data() + foffset, + edge_split_verts.data() + 2*foffset, + split_verts, split_faces + ) ; + } + +// std::cout << "face size " << split_faces.size() << std::endl ; +// std::cout << "vert size " << split_verts.size() << std::endl ; + //swap content + mesh_builder->swap_vertices(split_verts) ; + mesh_builder->swap_faces(split_faces) ; +} + +//}}} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/splitting_fwd.hpp b/contrib/Revoropt/include/Revoropt/Mesh/splitting_fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4d07d6e19148bc70355d74ae127b5cdcc3b35483 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/splitting_fwd.hpp @@ -0,0 +1,273 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_SPLITTING_FWD_HPP_ +#define _REVOROPT_MESH_SPLITTING_FWD_HPP_ + +#include "all_fwd.hpp" +#include "debug_fwd.hpp" +#include "connectivity_fwd.hpp" + +namespace Revoropt { + +/**{{{ Splitting edges in halves (decimation) **/ + +/* Split a face of the mesh in two given an edge index and the index of its + * center vertex. The neighbourhoods along the split edge are set to none. + * Returns the indices of the two split faces and their sizes. within these + * faces, the layout of the vertices is such that : + * - the first face uses the mid vertex and the next one along the edge + * - the second face uses the first vertex of the edge and the mid one + * - the mid vertex is the last vertex in both faces */ +template<typename MeshBuilder> +void face_split( MeshBuilder* mesh, + unsigned int face, + unsigned int edge_index, + unsigned int mid_vertex_index, + unsigned int* split_face_indices //output + ) ; + +/* Split an edge of a mesh builder. If the provided arrays are not NULL, returns + * the indices and sizes of the new faces. The size of the arrays to provide is + * two in case of a border edge, and 4 otherwise.*/ +template<typename MeshBuilder> +void edge_split( MeshBuilder* mesh, + unsigned int face, + unsigned int edge_index, + unsigned int* split_face_indices = NULL //output + ) ; +/* Tools */ +namespace Decimate { + + /* An edge index with its length, vertices, and face information */ + template<typename Scalar> + class HeapEdge { + public : + + /* A face connected to the edge */ + unsigned int face ; + + /* The index of the edge in that face */ + unsigned int edge ; + + /* Sorted vertices of the edge */ + unsigned int vmin, vmax ; + + /* Length of the edge */ + Scalar length ; + + bool operator<(const HeapEdge& rhs) { + //in case of equality, use vertex indices + if(length == rhs.length) { + if(vmax == rhs.vmax) { + return vmin < rhs.vmin ; + } else { + return vmax < rhs.vmax ; + } + } + return length < rhs.length ; + } + } ; + +} //end of namespace Decimate + +template<typename Mesh, typename MeshBuilder> +void split_longest_edge( const Mesh* input, + unsigned int niter, + MeshBuilder* mesh + ) ; +//}}} + +/**{{{ Normal bevel : split triangles to improve normal interpolation **/ + +namespace Bevel { + +/* edge split types */ +enum EdgeSplitType { + SPLIT_NONE = 0, + SPLIT_FIRST = 1, + SPLIT_SECOND = 2, + SPLIT_BOTH = 3 +} ; + +/* face split types */ +struct FaceSplitType { + unsigned int index ; + unsigned int rotation ; + bool symetry ; +} ; + +const unsigned int NO_VERTEX = -1 ; + +/* criterion based on the normal to decide whether an edge needs splitting */ +template<typename Mesh> +class NormalSplitCriterion { + + public : + /* Typedefs */ + //scalar type + typedef typename Mesh::Scalar Scalar ; + //vector type (dimension 3 since we are dealing with normals) + typedef Eigen::Matrix<Scalar,3,1> Vector ; + + /* Construction */ + NormalSplitCriterion(const Mesh* mesh) : + cosine_threshold_(0.85), + bevel_radius_(0.002), + normal_radius_(0.001), + restrict_to_boundary_(false), + mesh_(mesh) + { + init() ; + } + + /* Parameters */ + + void set_cosine_threshold( Scalar threshold ) { + cosine_threshold_ = threshold ; + } + + Scalar cosine_threshold() const { + return cosine_threshold_ ; + } + + void set_bevel_radius( Scalar in_radius ) { + bevel_radius_ = in_radius ; + } + + void set_normal_radius( Scalar in_radius ) { + normal_radius_ = in_radius ; + } + + Scalar bevel_radius() const { + return bevel_radius_ ; + } + + void set_boundary_restriction( bool status ) { + restrict_to_boundary_ = status ; + } + + bool restrict_to_boundary() { + return restrict_to_boundary_ ; + } + + /* Decision */ + + EdgeSplitType edge_type( unsigned int face, unsigned int edge ) const ; + private : + + /* Initialization */ + void init() ; + + /* Parameters */ + Scalar cosine_threshold_ ; + Scalar bevel_radius_ ; + Scalar normal_radius_ ; + bool restrict_to_boundary_ ; + + /* Mesh */ + const Mesh* mesh_ ; + + //FIXME + public : + /* Edge midpoints */ + std::vector<Scalar> edge_midpoints_ ; + + /* Edge normals */ + std::vector<Scalar> edge_normals_ ; + + /* Vertex normals */ + std::vector<Scalar> vertex_normals_ ; + + /*Boundary vertices*/ + std::vector<bool> boundary_vertices_ ; + +} ; + +/* test criterion based on the triangle and edge indices */ +template<typename Mesh> +class TestSplitCriterion { + + public: + + EdgeSplitType edge_type( unsigned int face, unsigned int edge ) const { + int res = face/150 ; + for(int i = 0; i < edge; ++i) { + res /= 4 ; + } + return (EdgeSplitType) (res%4) ; + } + +} ; + +/* criterion always requiring split */ +class TrueSplitCriterion { + + public: + + EdgeSplitType edge_type( unsigned int face, unsigned int edge ) const { + return SPLIT_BOTH ; + } + +} ; + +/* given the type of an edge, type of the oppositely oriented edge*/ +EdgeSplitType edge_split_type_sym( EdgeSplitType type ) { + static const EdgeSplitType sym[4] = { SPLIT_NONE, + SPLIT_SECOND, + SPLIT_FIRST, + SPLIT_BOTH + } ; + return sym[type] ; +} + +/* triangle classification, depending on its edges split types */ +void normalize_triangle_split( const EdgeSplitType edge_types[3], + FaceSplitType* split_type //output + ) ; +/* compute the split points of an edge given its type, a radius and length */ +/* returns the split really performed given the radius and edge length */ + +template<typename Mesh> +EdgeSplitType edge_split( const Mesh* mesh, + unsigned int face, + unsigned int edge, + typename Mesh::Scalar radius, + EdgeSplitType split_type, + std::vector<typename Mesh::Scalar>& coords, //i/o + std::vector<unsigned int>& edge_split_verts //output + ) ; + +/* split a face given its split type and the edge split vertices */ +template<typename Mesh> +void face_split( const Mesh* mesh, + unsigned int face, + typename Mesh::Scalar radius, + const EdgeSplitType edge_types[3], + const unsigned int edge_split_verts[6], + std::vector<typename Mesh::Scalar>& coords, //output + std::vector<unsigned int>& split_faces //output + ) ; + +} //end of namespace Bevel + +template< typename Triangulation, + typename TriangulationBuilder, + typename Criterion > +void bevel( const Triangulation* mesh, + const Criterion* criterion, + typename Triangulation::Scalar radius, + TriangulationBuilder* mesh_builder //output + ) ; +//}}} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/statistics_def.hpp b/contrib/Revoropt/include/Revoropt/Mesh/statistics_def.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c2e95264f4db9b1bed6063975cd244ace544ec1c --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/statistics_def.hpp @@ -0,0 +1,149 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_STATISTICS_DEF_HPP_ +#define _REVOROPT_MESH_STATISTICS_DEF_HPP_ + +#include <vector> +#include <iostream> +#include <eigen3/Eigen/Dense> + +namespace Revoropt { + +/* Angles */ + +template<typename Mesh> +void face_angles( const Mesh* mesh, typename Mesh::Scalar* angles ) { + //the angles array needs to have size mesh->face_vertices_size() + //scalar type + typedef typename Mesh::Scalar Scalar ; + //vector type + typedef Eigen::Matrix<Scalar,Mesh::VertexDim,1> Vector ; + //iterate on faces + for(unsigned int f = 0; f < mesh->faces_size(); ++f) { + //face size + const unsigned int fsize = mesh->face_size(f) ; + //vertices + const unsigned int* fverts = mesh->face(f) ; + //iterate on face vertices + for(unsigned int v = 0; v < fsize; ++v) { + const unsigned int previndex = fverts[(v+fsize-1)%fsize] ; + const unsigned int currindex = fverts[ v ] ; + const unsigned int nextindex = fverts[(v +1)%fsize] ; + Eigen::Map<const Vector> vprev(mesh->vertex(previndex)) ; + Eigen::Map<const Vector> vcurr(mesh->vertex(currindex)) ; + Eigen::Map<const Vector> vnext(mesh->vertex(nextindex)) ; + //normal weight for this vertex + const Vector e1 = (vprev-vcurr) ; + const Scalar e1_len = e1.norm() ; + const Vector e2 = (vcurr-vnext) ; + const Scalar e2_len = e2.norm() ; + if(e1_len*e2_len != 0) { + Scalar angle_cos = -e1.dot(e2)/(e1_len*e2_len) ; + angle_cos = std::min(angle_cos,1.) ; + angle_cos = std::max(angle_cos,-1.) ; + const Scalar angle = acos(angle_cos) ; + angles[mesh->face_offset(f)+v] = angle ; + } else { + angles[mesh->face_offset(f)+v] = 0 ; + } + } + } +} + +template<typename Mesh> +void triangle_quality( const Mesh* mesh, typename Mesh::Scalar* qualities ) { + //the qualities array needs to have size mesh->faces_size() + //scalar type + typedef typename Mesh::Scalar Scalar ; + //compute angles + std::vector<Scalar> angles(mesh->face_vertices_size()) ; + face_angles(mesh, angles.data()) ; + //compute qualities + for(unsigned int f = 0; f < mesh->faces_size(); ++f) { + //face angles + const Scalar* fangles = angles.data() + mesh->face_offset(f) ; + //quality + qualities[f] = 4*sin(fangles[0])*sin(fangles[1])*sin(fangles[2]) ; + qualities[f] /= sin(fangles[0])+sin(fangles[1])+sin(fangles[2]) ; + } +} + +/* Edge lengths */ +//inner edges are twice accounted for + +template<typename Mesh> +void edge_lengths( const Mesh* mesh, typename Mesh::Scalar* lengths ) { + //the lengths array needs to have size mesh->face_vertices_size() + //scalar type + typedef typename Mesh::Scalar Scalar ; + //vector type + typedef Eigen::Matrix<Scalar,Mesh::VertexDim,1> Vector ; + //iterate on faces + for(unsigned int f = 0; f < mesh->faces_size(); ++f) { + //face size + const unsigned int fsize = mesh->face_size(f) ; + //vertices + const unsigned int* fverts = mesh->face(f) ; + //iterate on face vertices + for(unsigned int v = 0; v < fsize; ++v) { + const unsigned int currindex = fverts[ v ] ; + const unsigned int nextindex = fverts[(v+1)%fsize] ; + Eigen::Map<const Vector> vcurr(mesh->vertex(currindex)) ; + Eigen::Map<const Vector> vnext(mesh->vertex(nextindex)) ; + //normal weight for this vertex + const Vector e2 = (vcurr-vnext) ; + lengths[mesh->face_offset(f)+v] = e2.norm() ; + } + } +} + +/* Histogram */ + +template<typename Scalar> +void histogram( const Scalar* data, unsigned int size, + Scalar min, Scalar max, unsigned int bins, + unsigned int* output + ) { + //output must havs size bins + const Scalar extent = max - min ; + //reset the output + std::fill(output, output+bins, 0) ; + //fill the bins + for(unsigned int i = 0; i < size; ++i) { + const unsigned int bin = (unsigned int) ((data[i]-min)/extent*bins) ; + ++output[bin] ; + } +} + +template<typename Scalar> +void histogram( const Scalar* data, unsigned int size, + Scalar min, Scalar max, unsigned int bins, + const char* filename + ) { + std::vector<Scalar> histogram(bins,0) ; + const Scalar extent = max - min ; + //fill the bins + for(unsigned int i = 0; i < size; ++i) { + const unsigned int bin = (unsigned int) ((data[i]-min)/extent*bins) ; + ++histogram[bin] ; + } + //write the result + std::ofstream file ; + file.open(filename) ; + for(unsigned int i = 0; i < bins; ++i) { + file << min + i/((double)bins)*extent << " " << histogram[i] << std::endl ; + } + file.close() ; +} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/statistics_fwd.hpp b/contrib/Revoropt/include/Revoropt/Mesh/statistics_fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9ee9dbd8678a1e257ac38b09119cc3271bf0d861 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/statistics_fwd.hpp @@ -0,0 +1,46 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_STATISTICS_H_ +#define _REVOROPT_MESH_STATISTICS_H_ + +namespace Revoropt { + +/* Angles */ + +template<typename Mesh> +void face_angles( const Mesh* mesh, typename Mesh::Scalar* angles ) ; + +template<typename Mesh> +void triangle_quality( const Mesh* mesh, typename Mesh::Scalar* qualities ) ; + +/* Edge lengths */ +//inner edges are twice accounted for + +template<typename Mesh> +void edge_lengths( const Mesh* mesh, typename Mesh::Scalar* lengths ) ; + +/* Histogram */ + +template<typename Scalar> +void histogram( const Scalar* data, unsigned int size, + Scalar min, Scalar max, unsigned int bins, + unsigned int* output + ) ; + +template<typename Scalar> +void histogram( const Scalar* data, unsigned int size, + Scalar min, Scalar max, unsigned int bins, + const char* filename + ) ; + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/subdivision_def.hpp b/contrib/Revoropt/include/Revoropt/Mesh/subdivision_def.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1fbd3c24d76b713f2fb2d660e471e86e180739eb --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/subdivision_def.hpp @@ -0,0 +1,318 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_SUBDIVISION_DEF_HPP_ +#define _REVOROPT_MESH_SUBDIVISION_DEF_HPP_ + +#include "subdivision_fwd.hpp" + +namespace Revoropt { + +namespace Subdivision { + +template<typename Scalar> +Stencil<Scalar> Stencil<Scalar>::operator+( const Stencil& rhs ) const { + //result + Stencil res ; + + //iterator on each stencil + unsigned int pos1 = 0 ; + unsigned int pos2 = 0 ; + + //fusion + while(pos1 != size() && pos2 != rhs.size()) { + if((*this)[pos1].index < rhs[pos2].index) { + res.contributions_.push_back((*this)[pos1]) ; + ++pos1 ; + } else if(contributions_[pos1].index > rhs[pos2].index) { + res.contributions_.push_back(rhs[pos2]) ; + ++pos2 ; + } else { + res.contributions_.push_back((*this)[pos1] + rhs[pos2]) ; + ++pos1 ; + ++pos2 ; + } + } + + //handle remaining contributions + if(pos1 == size()) { + res.contributions_.insert( + res.contributions_.end(), + rhs.contributions_.begin() + pos2, + rhs.contributions_.end() + ) ; + } else { + res.contributions_.insert( + res.contributions_.end(), + contributions_.begin() + pos1, + contributions_.end() + ) ; + } + + //finalize + return res ; +} + +} //end of namespace Subdivision + +template< + int _VertexDim, + typename _Scalar +> +void CatmullClark<_VertexDim, _Scalar>::update() { + //vector type + typedef Eigen::Matrix<Scalar,VertexDim,1> Vector ; + + vertices_.reserve(VertexDim*stencils_.size()) ; + vertices_.resize(VertexDim*stencils_.size()) ; + + for(unsigned int i = 0; i < stencils_.size(); ++i) { + //vertex to update + Eigen::Map<Vector> x(vertices_.data() + VertexDim*i) ; + x = Vector::Zero() ; + + //apply the vertex stencil + const Subdivision::Stencil<Scalar>& stencil = stencils_[i] ; + for(unsigned int j = 0; j < stencil.size(); ++j) { + //control vertex + unsigned int index = stencil[j].index ; + Scalar factor = stencil[j].factor ; + Eigen::Map<const Vector> cv(ctrl_vertices_ + VertexDim*index) ; + x += factor*cv ; + } + } +} + +template< + int _VertexDim, + typename _Scalar +> +template<typename Mesh> +void CatmullClark<_VertexDim, _Scalar>::subdivide( const Mesh* mesh ) { + //setup control vertices if a new mesh is provided + if((void*) mesh != (void*) this) { + ctrl_vertices_ = mesh->vertices() ; + } + + //reserve space for new stencils + stencils_.reserve( + mesh->vertices_size() //original vertices + + mesh->faces_size() //face vertices + + mesh->face_vertices_size() / 2 //edge vertices + ) ; + + //reinitialize stencils if the mesh is not this + if((void*) mesh != (void*) this) { + stencils_.resize(0) ; + for(unsigned int v = 0; v < mesh->vertices_size(); ++v) { + stencils_.emplace_back(v) ; + } + } + + //face vertices + const unsigned int fvstart = stencils_.size() ; + for(unsigned int f = 0; f < mesh->faces_size(); ++f) { + const unsigned int fsize = mesh->face_size(f) ; + const unsigned int* fverts = mesh->face(f) ; + Subdivision::Stencil<Scalar> stencil ; + for(unsigned int v = 0; v < fsize; ++v) { + stencil += stencils_[fverts[v]] ; + } + stencil.normalize() ; + stencils_.push_back(stencil) ; + } + + //edge vertices + const unsigned int evstart = stencils_.size() ; + for(unsigned int f = 0; f < mesh->faces_size(); ++f) { + const unsigned int fsize = mesh->face_size(f) ; + const unsigned int* fverts = mesh->face(f) ; + const unsigned int* fneigh = mesh->face_neighbours(f) ; + for(unsigned int v = 0; v < fsize; ++v) { + if(f < fneigh[v]) { //avoid handling edges twice + Subdivision::Stencil<Scalar> stencil ; + stencil += stencils_[fverts[ v ]] ; + stencil += stencils_[fverts[(v+1)%fsize]] ; + if(fneigh[v] != Mesh::NO_NEIGHBOUR) { + //not considered for border edges + stencil += stencils_[fvstart + f ] ; + stencil += stencils_[fvstart + fneigh[v]] ; + } + stencil.normalize() ; + stencils_.push_back(stencil) ; + } + } + } + + //original vertices + std::vector< Subdivision::Stencil<Scalar> > contribs(2*mesh->vertices_size()) ; + std::vector<int> valence(mesh->vertices_size(), 0) ; + unsigned int eindex = 0 ; + for(unsigned int f = 0; f < mesh->faces_size(); ++f) { + const unsigned int fsize = mesh->face_size(f) ; + const unsigned int* fverts = mesh->face(f) ; + const unsigned int* fneigh = mesh->face_neighbours(f) ; + for(unsigned int v = 0; v < fsize; ++v) { + const unsigned ev1 = fverts[ v ] ; + const unsigned ev2 = fverts[(v+1)%fsize] ; + //neighbouring face points considered only for non boundary vertices + if(valence[ev1] >= 0) { + contribs[2*ev1+1] += stencils_[fvstart + f] ; + ++valence[ev1] ; + } + if(f < fneigh[v]) { //avoid handling edges twice + if(fneigh[v] == Mesh::NO_NEIGHBOUR) { + //border vertices => valence < 0 + if(valence[ev1] >= 0) { + //only inner edges considered so far + //reset edge contributions to consider only border edges + contribs[2*ev1] = stencils_[evstart + eindex] ; + valence[ev1] = -1 ; + } else { + contribs[2*ev1] += stencils_[evstart + eindex] ; + } + if(valence[ev2] >= 0) { + contribs[2*ev2] = stencils_[evstart + eindex] ; + valence[ev2] = -1 ; + } else { + contribs[2*ev2] += stencils_[evstart + eindex] ; + } + } else { + if(valence[ev1] >= 0) { + contribs[2*ev1] += stencils_[evstart + eindex] ; + } + if(valence[ev2] >= 0) { + contribs[2*ev2] += stencils_[evstart + eindex] ; + } + } + ++eindex ; + } + } + } + //final mixture for original vertices + for(unsigned int v = 0; v < mesh->vertices_size(); ++v) { + if(valence[v] > 2) { + contribs[2*v ].normalize() ; + contribs[2*v+1].normalize() ; + if(valence[v] > 3) { + stencils_[v] *= valence[v] - 3 ; + stencils_[v] += 2*contribs[2*v] ; + } else { + stencils_[v] = 2*contribs[2*v] ; + } + stencils_[v] += contribs[2*v+1] ; + } else { + contribs[2*v].normalize() ; + stencils_[v] += contribs[2*v] ; + } + stencils_[v].normalize() ; + } + + //faces + std::vector<unsigned int> new_faces ; + std::vector<unsigned int> new_neighbourhoods ; + std::vector<unsigned int> new_face_patches ; + new_faces.reserve(4*mesh->face_vertices_size()) ; + new_neighbourhoods.reserve(4*mesh->face_vertices_size()) ; + new_face_patches.reserve(mesh->face_vertices_size()) ; + eindex = 0 ; + std::vector<unsigned int> boundary_info ; + for(unsigned int f = 0; f < mesh->faces_size(); ++f) { + const unsigned int fsize = mesh->face_size(f) ; + const unsigned int* fverts = mesh->face(f) ; + const unsigned int* fneigh = mesh->face_neighbours(f) ; + //recover edge neighbouring information + boundary_info.resize(4*fsize) ; + for(unsigned int v = 0; v < fsize; ++v) { + if(fneigh[v] == Mesh::NO_NEIGHBOUR || f < fneigh[v]) { + boundary_info[4*v ] = evstart + eindex ; + boundary_info[4*v+1] = Mesh::NO_NEIGHBOUR ; + boundary_info[4*v+2] = Mesh::NO_NEIGHBOUR ; + ++eindex ; + } else { + //edge vertices + const unsigned int ev1 = fverts[ v ] ; + const unsigned int ev2 = fverts[(v+1)%fsize] ; + //neighbouring face info + const unsigned int nfsize = mesh->face_size(fneigh[v]) ; + const unsigned int* nfverts = mesh->face(fneigh[v]) ; + const unsigned int nfoffset = mesh->face_offset(fneigh[v]) ; + //index of the edge in the neighbouring face in the old mesh + const unsigned int neindex = mesh->edge_face_index(fneigh[v], ev1, ev2) ; + //index of the new face containing ev1 from the neighbouring face + if(nfverts[neindex] == ev2) { + boundary_info[4*v ] = new_faces[4*(nfoffset + neindex) + 1] ; + boundary_info[4*v+1] = nfoffset + ((neindex+1)%nfsize) ; + boundary_info[4*v+2] = nfoffset + neindex ; + boundary_info[4*v+3] = 0 ; + } else { + boundary_info[4*v ] = new_faces[4*(nfoffset + neindex) + 1] ; + boundary_info[4*v+1] = nfoffset + neindex ; + boundary_info[4*v+2] = nfoffset + ((neindex+1)%nfsize) ; + boundary_info[4*v+3] = 1 ; + } + } + } + //build the faces + const unsigned int fstart = mesh->face_offset(f) ; + for(unsigned int v = 0; v < fsize; ++v) { + const unsigned int prev_index = (v+fsize-1)%fsize ; + //face vertices + new_faces.push_back(fverts[v]) ; + new_faces.push_back(boundary_info[4*v]) ; + new_faces.push_back(fvstart + f) ; + new_faces.push_back(boundary_info[4*prev_index]) ; + //face patch + if((void*) mesh != (void*) this) { + new_face_patches[fstart + v] = f ; + } else { + new_face_patches[fstart + v] = face_patches_[f] ; + } + //face neighbourhoods + if(fneigh[v] == Mesh::NO_NEIGHBOUR || f < fneigh[v]) { + new_neighbourhoods.push_back(Mesh::NO_NEIGHBOUR) ; + } else { + new_neighbourhoods.push_back(boundary_info[4*v+1]) ; + } + new_neighbourhoods.push_back(fstart + ((v+1)%fsize)) ; + new_neighbourhoods.push_back(fstart + prev_index) ; + new_neighbourhoods.push_back(boundary_info[4*prev_index+2]) ; + if(fneigh[v] != Mesh::NO_NEIGHBOUR && f > fneigh[v]) { + if(boundary_info[4*v+3]) { + //opposite face is coherently oriented + new_neighbourhoods[4*boundary_info[4*v+1] ] = fstart + v ; + new_neighbourhoods[4*boundary_info[4*v+2]+3] = fstart+((v+1)%fsize) ; + } + //opposite face is flipped + new_neighbourhoods[4*boundary_info[4*v+1]+3] = fstart + v ; + new_neighbourhoods[4*boundary_info[4*v+2] ] = fstart+((v+1)%fsize) ; + } + } + } + + faces_.swap(new_faces) ; + face_neighbourhoods_.swap(new_neighbourhoods) ; + face_patches_.swap(new_face_patches) ; + + //compute vertex positions + update() ; + + //finalize pointers + Base::vertices_ = vertices_.data() ; + Base::vertices_size_ = vertices_.size() / VertexDim ; + Base::faces_ = faces_.data() ; + Base::faces_size_ = faces_.size() / 4 ; + Base::face_neighbourhoods_ = face_neighbourhoods_.data() ; + Base::set_dirty() ; +} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/subdivision_fwd.hpp b/contrib/Revoropt/include/Revoropt/Mesh/subdivision_fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fe93d26b81ea3c8595c279bd42fb48370742015f --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/subdivision_fwd.hpp @@ -0,0 +1,216 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_SUBDIVISION_FWD_HPP_ +#define _REVOROPT_MESH_SUBDIVISION_FWD_HPP_ + +#include "base_def.hpp" + +#include <eigen3/Eigen/Dense> +#include <ostream> +#include <iostream> + +namespace Revoropt { + +namespace Subdivision { + +template<typename Scalar> +class Contribution { + public : + /* Data */ + unsigned int index ; + Scalar factor ; + + /* Construction */ + Contribution() {} + + Contribution(unsigned int _index) : + index(_index), factor(1) {} + + Contribution(unsigned int _index, Scalar _factor) : + index(_index), factor(_factor) {} + + /* Operations */ + Contribution operator*( Scalar rhs ) const { + return Contribution(index, factor*rhs) ; + } + Contribution& operator*=( Scalar rhs ) { + factor *= rhs ; + return *this ; + } + Contribution operator+( const Contribution& rhs ) const { + return Contribution(index, factor + rhs.factor) ; + } + Contribution& operator+=( const Contribution& rhs ) { + factor += rhs.factor ; + return *this ; + } +} ; + +template<typename Scalar, typename OtherScalar = Scalar> +Contribution<Scalar> operator*(OtherScalar lhs, const Contribution<Scalar>& rhs) { + return rhs*((Scalar) lhs) ; +} + +template<typename Scalar> +class Stencil { + public : + /* Construction */ + Stencil() {} + + Stencil(const Contribution<Scalar>& contrib) { + contributions_.push_back(contrib) ; + } + + Stencil( unsigned int index ) { + contributions_.push_back(index) ; + } + + void normalize() { + Scalar sum = 0 ; + for(unsigned int i = 0; i < size(); ++i) { + sum += contributions_[i].factor ; + } + for(unsigned int i = 0; i < size(); ++i) { + contributions_[i].factor /= sum ; + } + } + + /* Access */ + Contribution<Scalar>& operator[](unsigned int i) { + return contributions_[i] ; + } + + const Contribution<Scalar>& operator[](unsigned int i) const { + return contributions_[i] ; + } + + unsigned int size() const { + return contributions_.size() ; + } + + /* Operations */ + Stencil operator*( Scalar rhs ) const { + Stencil res ; + res.contributions_.reserve(size()) ; + for(unsigned int i = 0; i < size(); ++i) { + res.contributions_.push_back((*this)[i] * rhs) ; + } + return res ; + } + + Stencil& operator*=( Scalar rhs ) { + *this = *this * rhs ; + return *this ; + } + + Stencil operator+( const Stencil& rhs ) const ; + + Stencil& operator+=(const Stencil& rhs) { + *this = *this + rhs ; + return *this ; + } + + Stencil compose( const Stencil* stencils ) const { + Stencil res ; + for(unsigned int i = 0; i < size(); ++i) { + res += stencils[contributions_[i].index] * contributions_[i].factor ; + } + return res ; + } + + private : + /* Data */ + std::vector< Contribution<Scalar> > contributions_ ; +} ; + +template<typename Scalar, typename OtherScalar = Scalar> +Stencil<Scalar> operator*(OtherScalar lhs, const Stencil<Scalar>& rhs) { + return rhs*((Scalar) lhs) ; +} + +template<typename Scalar> +std::ostream& operator<<(std::ostream& stream, const Stencil<Scalar>& rhs) { + stream << "[ " ; + for(unsigned int i = 0; i < rhs.size(); ++i) { + stream << rhs[i].index << "(" << rhs[i].factor << ") " ; + } + stream << "]" ; + return stream ; +} + +} //end of namespace Subdivision + +template< + int _VertexDim = 3, + typename _Scalar = double +> +class CatmullClark : public ROMesh<4, _VertexDim, _Scalar, 0> +{ + public : + + /* Typedefs */ + typedef ROMesh<4, _VertexDim, _Scalar, 0> Base ; + typedef _Scalar Scalar ; + enum { + FaceSize = 4, + VertexDim = _VertexDim + } ; + + /* Construction */ + CatmullClark() : Base() {} + + template<typename Mesh> + CatmullClark( const Mesh* mesh ) : Base() { + subdivide(mesh) ; + } + + /* Access */ + const unsigned int* face_patches() { + return face_patches_.data() ; + } + + /* Tools */ + void set_vertices( + const Scalar* ctrl_vertices, + unsigned int ctrl_vertices_size = 0 //for compatibility + ) { + ctrl_vertices_ = ctrl_vertices ; + update() ; + Base::set_dirty() ; + } + + void update() ; + + template<typename Mesh> + void subdivide( const Mesh* mesh ) ; + + void subdivide() { + return subdivide(this) ; + } + + private : + + /* Control Vertices */ + const Scalar* ctrl_vertices_ ; + + /* Subdivided vertices and faces */ + std::vector<Scalar> vertices_ ; + std::vector<unsigned int> faces_ ; + std::vector<unsigned int> face_neighbourhoods_ ; + std::vector<unsigned int> face_patches_ ; + + /* Stencils */ + std::vector< Subdivision::Stencil<Scalar> > stencils_ ; +} ; + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/wrapper_def.hpp b/contrib/Revoropt/include/Revoropt/Mesh/wrapper_def.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4ea734ac83198b28570aeda834b3ed9d65a1cf21 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/wrapper_def.hpp @@ -0,0 +1,100 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_WRAPPER_DEF_HPP_ +#define _REVOROPT_MESH_WRAPPER_DEF_HPP_ + +#include "wrapper_fwd.hpp" +#include "base_def.hpp" +#include "connectivity_def.hpp" + +namespace Revoropt { + +/*** Triangulation wrapper, triangulating a mesh ***/ + +/* fixed face size case */ +template< int VertexDim, typename Scalar, int VertexOffset> +void ROTriangulationWrapper<VertexDim,Scalar,VertexOffset>::set_faces( + const unsigned int* faces, + unsigned int face_size, + unsigned int faces_size +) { + //reset the triangle vector + triangles_.resize(0) ; + + //split every single face + for( unsigned int face_index = 0; + face_index < faces_size; + ++face_index ) { + //face vertex indices + const unsigned int* face_vertices = faces + face_size*face_index ; + split_polygon(face_vertices, face_size) ; + } + + //assign Mesh data fields + Base::faces_ = triangles_.data() ; + Base::faces_size_ = triangles_.size() / 3 ; + c_computer_.compute_connectivity() ; + Base::face_neighbourhoods_ = c_computer_.data() ; + Base::set_dirty() ; +} + +/* variable face size case */ +template< int VertexDim, typename Scalar, int VertexOffset> +void ROTriangulationWrapper<VertexDim,Scalar,VertexOffset>::set_faces( + const unsigned int* faces, + const unsigned int* face_ends, + unsigned int faces_size +) { + //reset the triangle vector + triangles_.resize(0) ; + + //current face position + const unsigned int* current_face = faces ; + + //split every single face + for( unsigned int face_index = 0; + face_index < faces_size; + ++face_index ) { + //face vertex indices + const unsigned int face_size = + (faces + face_ends[face_index]) - current_face ; + split_polygon(current_face, face_size) ; + current_face = faces + face_ends[face_index] ; + } + + //assign Mesh data fields + Base::faces_ = triangles_.data() ; + Base::faces_size_ = triangles_.size() / 3 ; + c_computer_.compute_connectivity() ; + Base::face_neighbourhoods_ = c_computer_.data() ; + Base::set_dirty() ; +} + +/* naïvely split a polygon into triangles */ +template< int VertexDim, typename Scalar, int VertexOffset> +void ROTriangulationWrapper<VertexDim,Scalar,VertexOffset>::split_polygon( + const unsigned int* polygon_vertices, + unsigned int polygon_size +) { + //iterate on edges + for( unsigned int vertex_index = 1; + vertex_index < polygon_size-1; + ++vertex_index ) { + //add a triangle to the triangle vector + triangles_.push_back(polygon_vertices[0 ]) ; + triangles_.push_back(polygon_vertices[vertex_index ]) ; + triangles_.push_back(polygon_vertices[vertex_index+1]) ; + } +} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Mesh/wrapper_fwd.hpp b/contrib/Revoropt/include/Revoropt/Mesh/wrapper_fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..cd60feb9d46d49246e6efe2ea879fc0084e00876 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Mesh/wrapper_fwd.hpp @@ -0,0 +1,662 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_MESH_WRAPPER_H_ +#define _REVOROPT_MESH_WRAPPER_H_ + +#include "base_fwd.hpp" +#include "connectivity_fwd.hpp" + +namespace Revoropt { + +/*** Read only wrapper, to build a Read only mesh from given pointers ***/ + +/** Fixed face size case **/ + +template< + int _FaceSize, + int _VertexDim, + typename _Scalar, + int _VertexOffset +> +class ROMeshWrapper : public ROMesh<_FaceSize,_VertexDim,_Scalar,_VertexOffset> +{ + public: + + typedef ROMesh<_FaceSize,_VertexDim,_Scalar,_VertexOffset> Base ; + + typedef _Scalar Scalar ; + + enum { + FaceSize = _FaceSize, + VertexDim = _VertexDim, + VertexOffset = _VertexOffset + } ; + + using Base::NO_NEIGHBOUR ; + + /** Construction **/ + ROMeshWrapper() : Base() {} ; + + /* From raw arrays */ + ROMeshWrapper( const Scalar* vertices, + unsigned int vertices_size, + const unsigned int* faces, + const unsigned int* face_neighbourhoods, + unsigned int faces_size + ) : Base( vertices, vertices_size, + faces, face_neighbourhoods, faces_size + ) { + }; + + /* From a set of vertices and the faces/neighbourhoods of another mesh */ + template<int _OtherDim, int _OtherOffset> + ROMeshWrapper( const Scalar* vertices, + unsigned int vertices_size, + const ROMesh< _FaceSize, + _OtherDim, + _Scalar, + _OtherOffset + >* combinatorics + ) : Base( vertices, vertices_size, + combinatorics->faces_, + combinatorics->face_neighbourhoods_, + combinatorics->faces_size_ + ) { + } ; + + /* From another mesh */ + template<int _OtherDim, int _OtherOffset> + ROMeshWrapper( const ROMesh< _FaceSize, + _OtherDim, + _Scalar, + _OtherOffset + >* rhs + ) : Base( rhs->vertices_, rhs->vertices_size_, + rhs->faces_, + rhs->face_neighbourhoods_, + rhs->faces_size_ + ) { + } ; + + /** Changing pointers **/ + /* All pointers */ + template<int _OtherDim, int _OtherOffset> + ROMeshWrapper& operator=( const ROMesh< FaceSize, + _OtherDim, + Scalar, + _OtherOffset + >& rhs ) { + wrap(&rhs) ; + return *this ; + } + + template<int _OtherDim, int _OtherOffset> + void wrap( const ROMesh< FaceSize, + _OtherDim, + Scalar, + _OtherOffset + >* rhs ) { + Base::vertices_ = + const_cast<_Scalar*>(rhs->vertices_) ; + Base::vertices_size_ = + rhs->vertices_size_ ; + Base::faces_ = + const_cast<unsigned int*>(rhs->faces_) ; + Base::face_neighbourhoods_ = + const_cast<unsigned int*>(rhs->face_neighbourhoods_) ; + Base::faces_size_ = + rhs->faces_size_ ; + Base::set_dirty() ; + } + + /* Vertex pointer */ + void set_vertices( const Scalar* vertices, + unsigned int vertices_size + ) { + Base::vertices_ = const_cast<_Scalar*>(vertices) ; + Base::vertices_size_ = vertices_size ; + Base::set_dirty() ; + } + + /* Face pointers */ + void set_faces( const unsigned int* faces, + const unsigned int* face_neighbourhoods, + unsigned int faces_size + ) { + Base::faces_ = const_cast<unsigned int*>(faces) ; + Base::face_neighbourhoods_ = const_cast<unsigned int*>(face_neighbourhoods) ; + Base::faces_size_ = faces_size ; + Base::set_dirty() ; + } + + +} ; + +/** Variable face size case **/ + +template< + int _VertexDim, + typename _Scalar, + int _VertexOffset +> +class ROMeshWrapper<Variable, _VertexDim, _Scalar, _VertexOffset> : + public ROMesh<Variable,_VertexDim,_Scalar,_VertexOffset> +{ + public: + + typedef ROMesh<Variable,_VertexDim,_Scalar,_VertexOffset> Base ; + + typedef _Scalar Scalar ; + + enum { + FaceSize = Variable, + VertexDim = _VertexDim, + VertexOffset = _VertexOffset + } ; + + using Base::NO_NEIGHBOUR ; + + /** Construction **/ + ROMeshWrapper() : Base() {} ; + + /* From raw arrays */ + ROMeshWrapper( const Scalar* vertices, + unsigned int vertices_size, + const unsigned int* faces, + const unsigned int* face_ends, + const unsigned int* face_neighbourhoods, + unsigned int faces_size + ) : Base( vertices, vertices_size, + faces, face_ends, face_neighbourhoods, faces_size + ) { + }; + + /* From a set of vertices and the faces/neighbourhoods of another mesh */ + template<int _OtherDim, int _OtherOffset> + ROMeshWrapper( const Scalar* vertices, + unsigned int vertices_size, + const ROMesh< Variable, + _OtherDim, + Scalar, + _OtherOffset + >* combinatorics + ) : Base( vertices, vertices_size, + combinatorics->faces_, + combinatorics->face_ends_, + combinatorics->face_neighbourhoods_, + combinatorics->faces_size_ + ) { + } ; + + /* From another mesh */ + template<int _OtherDim, int _OtherOffset> + ROMeshWrapper( const ROMesh< Variable, + _OtherDim, + Scalar, + _OtherOffset + >* rhs + ) : Base( rhs->vertices_, rhs->vertices_size_, + rhs->faces_, + rhs->face_ends_, + rhs->face_neighbourhoods_, + rhs->faces_size_ + ) { + } ; + + /** Changing pointers **/ + /* All pointers */ + template<int _OtherDim, int _OtherOffset> + ROMeshWrapper& operator=( const ROMesh< Variable, + _OtherDim, + Scalar, + _OtherOffset + >& rhs ) { + wrap(&rhs) ; + return *this ; + } + + template<int _OtherDim, int _OtherOffset> + void wrap( const ROMesh< Variable, + _OtherDim, + Scalar, + _OtherOffset + >* rhs ) { + Base::vertices_ = + const_cast<_Scalar*>(rhs->vertices_) ; + Base::vertices_size_ = + rhs->vertices_size_ ; + Base::faces_ = + const_cast<unsigned int*>(rhs->faces_) ; + Base::face_ends_ = + const_cast<unsigned int*>(rhs->face_ends_) ; + Base::face_neighbourhoods_ = + const_cast<unsigned int*>(rhs->face_neighbourhoods_) ; + Base::faces_size_ = + rhs->faces_size_ ; + Base::set_dirty() ; + } + + /* Vertex pointer */ + void set_vertices( const Scalar* vertices, + unsigned int vertices_size + ) { + Base::vertices_ = const_cast<_Scalar*>(vertices) ; + Base::vertices_size_ = vertices_size ; + Base::set_dirty() ; + } + + /* Face pointers */ + void set_faces( const unsigned int* faces, + const unsigned int* face_neighbourhoods, + const unsigned int* face_ends, + unsigned int faces_size + ) { + Base::faces_ = const_cast<unsigned int*>(faces) ; + Base::face_ends_ = const_cast<unsigned int*>(face_ends) ; + Base::face_neighbourhoods_ = const_cast<unsigned int*>(face_neighbourhoods) ; + Base::faces_size_ = faces_size ; + Base::set_dirty() ; + } + + +} ; + +/*** Read write wrapper, to build a Read write mesh from given pointers ***/ +/* Note, this is not a subclass of ROMeshWrapper, since the set_{vertices|faces} + * methods would allow setting the pointers of a RWMeshWrapper from const data. + * */ + +/** Fixed face size case **/ + +template< + int _FaceSize, + int _VertexDim, + typename _Scalar, + int _VertexOffset +> +class MeshWrapper : public Mesh<_FaceSize,_VertexDim,_Scalar,_VertexOffset> +{ + public: + + typedef Mesh<_FaceSize,_VertexDim,_Scalar,_VertexOffset> Base ; + + typedef _Scalar Scalar ; + + enum { + FaceSize = _FaceSize, + VertexDim = _VertexDim, + VertexOffset = _VertexOffset + } ; + + using Base::NO_NEIGHBOUR ; + + /** Construction **/ + MeshWrapper() : Base() {} ; + + /* From raw arrays */ + MeshWrapper( Scalar* vertices, + unsigned int vertices_size, + unsigned int* faces, + unsigned int* face_neighbourhoods, + unsigned int faces_size + ) : Base( vertices, vertices_size, + faces, face_neighbourhoods, faces_size + ) { + }; + + /* From a set of vertices and the faces/neighbourhoods of another mesh */ + template<int _OtherDim, int _OtherOffset> + MeshWrapper( Scalar* vertices, + unsigned int vertices_size, + Mesh< _FaceSize, + _OtherDim, + _Scalar, + _OtherOffset + >* combinatorics + ) : Base( vertices, vertices_size, + combinatorics->faces_, + combinatorics->face_neighbourhoods_, + combinatorics->faces_size_ + ) { + } ; + + /* From another mesh */ + template<int _OtherDim, int _OtherOffset> + MeshWrapper( Mesh< _FaceSize, + _OtherDim, + _Scalar, + _OtherOffset + >* rhs + ) : Base( rhs->vertices_, rhs->vertices_size_, + rhs->faces_, + rhs->face_neighbourhoods_, + rhs->faces_size_ + ) { + } ; + + /** Changing pointers **/ + template<int _OtherDim, int _OtherOffset> + MeshWrapper& operator=( Mesh< FaceSize, + _OtherDim, + Scalar, + _OtherOffset + >& rhs ) { + wrap(&rhs) ; + return *this ; + } + + /* All pointers */ + template<int _OtherDim, int _OtherOffset> + void wrap( Mesh< FaceSize, + _OtherDim, + Scalar, + _OtherOffset + >* rhs ) { + Base::vertices_ = rhs->vertices_ ; + Base::vertices_size_ = rhs->vertices_size_ ; + Base::faces_ = rhs->faces_ ; + Base::face_neighbourhoods_ = rhs->face_neighbourhoods_ ; + Base::faces_size_ = rhs->faces_size_ ; + Base::set_dirty() ; + } + + /* Vertex pointer */ + void set_vertices( Scalar* vertices, + unsigned int vertices_size + ) { + Base::vertices_ = vertices ; + Base::vertices_size_ = vertices_size ; + Base::set_dirty() ; + } + + /* Face pointers */ + void set_faces( unsigned int* faces, + unsigned int* face_neighbourhoods, + unsigned int faces_size + ) { + Base::faces_ = faces ; + Base::face_neighbourhoods_ = face_neighbourhoods ; + Base::faces_size_ = faces_size ; + Base::set_dirty() ; + } + + +} ; + +/** Variable face size case **/ + +template< + int _VertexDim, + typename _Scalar, + int _VertexOffset +> +class MeshWrapper<Variable, _VertexDim, _Scalar, _VertexOffset> : + public Mesh<Variable,_VertexDim,_Scalar,_VertexOffset> +{ + public: + + typedef Mesh<Variable,_VertexDim,_Scalar,_VertexOffset> Base ; + + typedef _Scalar Scalar ; + + enum { + FaceSize = Variable, + VertexDim = _VertexDim, + VertexOffset = _VertexOffset + } ; + + using Base::NO_NEIGHBOUR ; + + /** Construction **/ + MeshWrapper() : Base() {} ; + + /* From raw arrays */ + MeshWrapper( Scalar* vertices, + unsigned int vertices_size, + unsigned int* faces, + unsigned int* face_ends, + unsigned int* face_neighbourhoods, + unsigned int faces_size + ) : Base( vertices, vertices_size, + faces, face_ends, face_neighbourhoods, faces_size + ) { + }; + + /* From a set of vertices and the faces/neighbourhoods of another mesh */ + template<int _OtherDim, int _OtherOffset> + MeshWrapper( Scalar* vertices, + unsigned int vertices_size, + Mesh< Variable, + _OtherDim, + Scalar, + _OtherOffset + >* combinatorics + ) : Base( vertices, vertices_size, + combinatorics->faces_, + combinatorics->face_ends_, + combinatorics->face_neighbourhoods_, + combinatorics->faces_size_ + ) { + } ; + + /* From another mesh */ + template<int _OtherDim, int _OtherOffset> + MeshWrapper( Mesh< Variable, + _OtherDim, + _Scalar, + _OtherOffset + >* rhs + ) : Base( rhs->vertices_, rhs->vertices_size_, + rhs->faces_, + rhs->face_ends_, + rhs->face_neighbourhoods_, + rhs->faces_size_ + ) { + } ; + + /** Changing pointers **/ + /* All pointers */ + template<int _OtherDim, int _OtherOffset> + MeshWrapper& operator=( Mesh< Variable, + _OtherDim, + Scalar, + _OtherOffset + >& rhs ) { + wrap(&rhs) ; + return *this ; + } + + template<int _OtherDim, int _OtherOffset> + void wrap( Mesh< Variable, + _OtherDim, + Scalar, + _OtherOffset + >* rhs ) { + Base::vertices_ = rhs->vertices_ ; + Base::vertices_size_ = rhs->vertices_size_ ; + Base::faces_ = rhs->faces_ ; + Base::face_ends_ = rhs->face_ends_ ; + Base::face_neighbourhoods_ = rhs->face_neighbourhoods_ ; + Base::faces_size_ = rhs->faces_size_ ; + Base::set_dirty() ; + } + + /* Vertex pointer */ + void set_vertices( Scalar* vertices, + unsigned int vertices_size + ) { + Base::vertices_ = vertices ; + Base::vertices_size_ = vertices_size ; + Base::set_dirty() ; + } + + /* Face pointers */ + void set_faces( unsigned int* faces, + unsigned int* face_neighbourhoods, + unsigned int* face_ends, + unsigned int faces_size + ) { + Base::faces_ = faces ; + Base::face_ends_ = face_ends ; + Base::face_neighbourhoods_ = face_neighbourhoods ; + Base::faces_size_ = faces_size ; + Base::set_dirty() ; + } + + +} ; + +/*** Triangulation wrapper, triangulating a mesh ***/ + +template< + int _VertexDim, + typename _Scalar, + int _VertexOffset +> +class ROTriangulationWrapper : + public ROMesh<3,_VertexDim,_Scalar,_VertexOffset> +{ + public: + + typedef ROMesh<3,_VertexDim,_Scalar,_VertexOffset> Base ; + + typedef _Scalar Scalar ; + + enum { + FaceSize = 3, + VertexDim = _VertexDim, + VertexOffset = _VertexOffset + } ; + + using Base::NO_NEIGHBOUR ; + + /** Construction **/ + ROTriangulationWrapper() : Base(), c_computer_(this) {} + + /* Wrap other meshes */ + + template <int _FaceSize> + ROTriangulationWrapper( const ROMesh<_FaceSize, + VertexDim, + Scalar, + VertexOffset + >* mesh + ) : Base( mesh->vertices_, mesh->vertices_size_, + NULL, NULL, 0 + ), + c_computer_(this) { + set_faces(mesh->faces_, _FaceSize, mesh->faces_size_) ; + } ; + + ROTriangulationWrapper( const ROMesh<3, + VertexDim, + Scalar, + VertexOffset + >* mesh + ) : Base( mesh->vertices_, mesh->vertices_size_, + mesh->faces_, mesh->face_neighbourhoods_, + mesh->faces_size_ + ), + c_computer_() { + } ; + + ROTriangulationWrapper( const ROMesh<Variable, + VertexDim, + Scalar, + VertexOffset + >* mesh + ) : Base( mesh->vertices_, mesh->vertices_size_, + NULL, NULL, 0 + ), + c_computer_(this) { + + set_faces(mesh->faces_, mesh->face_ends_, mesh->faces_size_) ; + } ; + + + + /** Changing pointers **/ + template <int _FaceSize> + void wrap( + const ROMesh<_FaceSize, VertexDim, Scalar, VertexOffset>* mesh + ) { + Base::vertices_ = mesh->vertices_ ; + Base::vertices_size_ = mesh->vertices_size_ ; + set_faces(mesh->faces, _FaceSize, mesh->faces_size_) ; + } + + void wrap( + const ROMesh<Variable, VertexDim, Scalar, VertexOffset>* mesh + ) { + Base::vertices_ = mesh->vertices_ ; + Base::vertices_size_ = mesh->vertices_size_ ; + set_faces(mesh->faces_, mesh->face_ends_, mesh->faces_size_) ; + } + + void wrap( + const ROMesh<3, VertexDim, Scalar, VertexOffset>* mesh + ) { + Base::vertices_ = mesh->vertices_ ; + Base::vertices_size_ = mesh->vertices_size_ ; + Base::faces_ = mesh->faces_ ; + Base::face_neighbourhoods_ = mesh->face_neighbourhoods_ ; + Base::faces_size_ = mesh->faces_size_ ; + } + + /* Vertex pointer */ + void set_vertices( const Scalar* vertices, + unsigned int vertices_size + ) { + Base::vertices_ = vertices ; + Base::vertices_size_ = vertices_size ; + Base::set_dirty() ; + } + + /** Provide new base faces for the triangles **/ + + /* Fixed sized faces */ + void set_faces( const unsigned int* faces, + unsigned int face_size, + unsigned int faces_size + ) ; + + /* Variable sized faces */ + void set_faces( const unsigned int* faces, + const unsigned int* face_ends, + unsigned int faces_size + ) ; + + /* Access the triangles array */ + const unsigned int* triangles() const { + if(Base::faces_size_ != 0 && triangles_.size() == 0) { + return Base::faces() ; + } else { + return triangles_.data() ; + } + } + + private: + + /* Naïvely split a polygon into triangles */ + void split_polygon( const unsigned int* polygon_vertices, + unsigned int polygon_size + ) ; + + /* Triangles */ + std::vector<unsigned int> triangles_ ; + + /* Neighbourhoods */ + ConnectivityComputer<Base> c_computer_ ; +} ; + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Neighbours/aabox_def.hpp b/contrib/Revoropt/include/Revoropt/Neighbours/aabox_def.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3a43f487e70f4f070c35e5202b33612e4dc2a706 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Neighbours/aabox_def.hpp @@ -0,0 +1,529 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_AABOX_DEF_HPP_ +#define _REVOROPT_AABOX_DEF_HPP_ + +#include "aabox_fwd.hpp" + +#include <algorithm> + +namespace Revoropt { + +template< int Dim , typename Scalar > +AABox<Dim,Scalar>::AABox() { + //initialize to an empty box + if(Dim == 0) return ; + bounds_[0] = 1 ; + bounds_[1] = 1 ; +} ; + +template< int Dim , typename Scalar > +AABox<Dim,Scalar>::AABox( Scalar bounds[2*Dim] ) { + set_bounds(bounds) ; + std::copy(bounds, bounds+2*Dim, bounds_) ; +} ; + +/* Setting bounds */ +template< int Dim , typename Scalar > +void AABox<Dim,Scalar>::set_bounds( Scalar bounds[2*Dim] ) { + std::copy(bounds, bounds+2*Dim, bounds_) ; +} + +//initialize a 0 volume box to a point position +template< int Dim , typename Scalar > +void AABox<Dim,Scalar>::init_to( const Scalar point[Dim] ) { + for(int axis = 0; axis < Dim; ++axis) { + bounds_[2*axis ] = point[axis] ; + bounds_[2*axis+1] = point[axis] ; + } +} + +//increase the box size to contain the given point +template< int Dim , typename Scalar > +void AABox<Dim,Scalar>::fit_to( const Scalar point[Dim] ) { + for(int axis = 0; axis < Dim; ++axis) { + bounds_[2*axis ] = std::min(point[axis],bounds_[2*axis ]) ; + bounds_[2*axis+1] = std::max(point[axis],bounds_[2*axis+1]) ; + } +} + +//grow the box (decrease min bounds and increase max bounds) given an offset +template< int Dim , typename Scalar > +void AABox<Dim,Scalar>::grow( Scalar offset ) { + for(int axis = 0; axis < Dim; ++axis) { + bounds_[2*axis ] -= offset ; + bounds_[2*axis+1] += offset ; + } +} + +/* Accessing bounds */ +template< int Dim , typename Scalar > +Scalar* AABox<Dim,Scalar>::axis_bounds( int axis ) { + return bounds_ + 2*axis ; +} ; +template< int Dim , typename Scalar > +Scalar& AABox<Dim,Scalar>::min_axis_bound( int axis ) { + return bounds_[2*axis ] ; +} ; +template< int Dim , typename Scalar > +Scalar& AABox<Dim,Scalar>::max_axis_bound( int axis ) { + return bounds_[2*axis+1] ; +} ; + +template< int Dim , typename Scalar > +const Scalar* AABox<Dim,Scalar>::axis_bounds( int axis ) const { + return bounds_ + 2*axis ; +} ; +template< int Dim , typename Scalar > +const Scalar& AABox<Dim,Scalar>::min_axis_bound( int axis ) const { + return bounds_[2*axis ] ; +} ; +template< int Dim , typename Scalar > +const Scalar& AABox<Dim,Scalar>::max_axis_bound( int axis ) const { + return bounds_[2*axis+1] ; +} ; + +/* Splitting */ +template< int Dim , typename Scalar > +void AABox<Dim,Scalar>::split_at_pos( int axis, + Scalar pos, + AABox& b1, + AABox& b2 + ) const { + //copy the bounds + std::copy(bounds_, bounds_+2*Dim, b1.bounds_) ; + std::copy(bounds_, bounds_+2*Dim, b2.bounds_) ; + //reduce the split boxes according to the split position + b1.bounds_[2*axis] = bounds_[2*axis] ; + b1.bounds_[2*axis+1] = std::min(pos,bounds_[2*axis+1]) ; + b2.bounds_[2*axis] = std::max(pos,bounds_[2*axis]) ; + b2.bounds_[2*axis+1] = bounds_[2*axis+1] ; + //note that in case pos is outside the range, one box will be the current + //box while the other will be empty (max bound is below min bound) +} + +template< int Dim , typename Scalar > +void AABox<Dim,Scalar>::split_at_ratio( int axis, + Scalar ratio, + AABox& b1, + AABox& b2 + ) const { + //split position + Scalar pos = (1-ratio)*bounds_[2*axis] + ratio*bounds_[2*axis+1] ; + //split + split_at_pos(axis,pos,b1,b2) ; +} + +/* Point query */ +template< int Dim , typename Scalar > +bool AABox<Dim,Scalar>::contains( const Scalar point[Dim] ) const { + //test for every dimension + for(int axis=0; axis<Dim; ++axis) { + if((point[axis] < bounds_[2*axis]) || (point[axis] > bounds_[2*axis+1])) { + return false ; + } + } + //beware that a box of dimension 0 contains anything + return true ; +} + +/* AABox clipping */ +template< int Dim , typename Scalar > +bool AABox<Dim,Scalar>::clips_aabox( const AABox<Dim,Scalar>& rhs ) const { + //for each axis check that the intervals intersect + for(int axis=0; axis<Dim; ++axis) { + Scalar bmin = std::max(bounds_[2*axis ],rhs.bounds_[2*axis ]) ; + Scalar bmax = std::min(bounds_[2*axis+1],rhs.bounds_[2*axis+1]) ; + if(bmax < bmin) return false ; + } + return true ; +} + +template< int Dim , typename Scalar > +bool AABox<Dim,Scalar>::aabox_intersection( const AABox<Dim,Scalar>& rhs, + AABox<Dim,Scalar>& output + ) const { + //for each axis check that the intervals intersect + for(int axis=0; axis<Dim; ++axis) { + Scalar bmin = std::max(bounds_[2*axis],rhs.bounds_[2*axis]) ; + Scalar bmax = std::min(bounds_[2*axis+1],rhs.bounds_[2*axis+1]) ; + if(bmax <= bmin) return false ; + output.bounds_[2*axis] = bmin ; + output.bounds_[2*axis+1] = bmax ; + } + return true ; +} + +/* Triangle clipping */ +template< int Dim , typename Scalar > +template< typename Triangulation > +bool AABox<Dim,Scalar>::clips_triangle_bbox( const unsigned int triangle, + const Triangulation* mesh + ) const { + //triangle vertices + const unsigned int* tverts = mesh->face(triangle) ; + //initialize the bbox of the triangle + AABox<Dim,Scalar> bbox ; + bbox.init_to(mesh->vertex(tverts[0])) ; + //compute the bounds + for(int i = 1; i<3; ++i) { + bbox.fit_to(mesh->vertex(tverts[i])) ; + } + return clips_aabox(bbox) ; +} + +template< int Dim , typename Scalar > +bool AABox<Dim,Scalar>::clips_triangle_bbox( const unsigned int* triangle, + const Scalar* vertices + ) const { + //wrap th triangle as a mesh + WrapMesh mesh(vertices, 3, triangle, no_neighbours, 1) ; + return clips_triangle_bbox(0, &mesh) ; +} + +template< int Dim , typename Scalar > +template< typename Triangulation > +bool AABox<Dim,Scalar>::triangle_intersection( const unsigned int triangle, + const Triangulation* mesh, + std::vector<Scalar>& output + ) const { + if(!clips_triangle_bbox(triangle,mesh)) return false ; + //initialize clipping polygon + Polygon<Triangulation> polygon ; + init_intersection(polygon) ; + //iterate over the dimensions + for(int axis=0; axis<Dim; ++axis) { + clip_by_axis(axis,polygon,triangle,mesh) ; + if(polygon.size() < 3) return false ; + } + //compute and output the clipped polygon vertices + output.resize(Dim*polygon.size()) ; + for(unsigned int vertex = 0; vertex < polygon.size(); ++vertex) { + polygon[vertex].position(triangle, mesh, output.data() + Dim*vertex) ; + } + //beware that a box of dimension 0 always contains any triangle + return true ; +} + +template< int Dim , typename Scalar > +bool AABox<Dim,Scalar>::triangle_intersection( const unsigned int* triangle, + const Scalar* vertices, + std::vector<Scalar>& output + ) const { + //wrap th triangle as a mesh + WrapMesh mesh(vertices, 3, triangle, no_neighbours, 1) ; + return triangle_intersection(0, &mesh, output); +} + +template< int Dim , typename Scalar > +template< typename Triangulation > +bool AABox<Dim,Scalar>::clips_triangle( const unsigned int triangle, + const Triangulation* mesh + ) const { + if(!clips_triangle_bbox(triangle,mesh)) return false ; + //initialize clipping polygon + Polygon<Triangulation> polygon ; + init_intersection(polygon) ; + //iterate over the dimensions + for(int axis=0; axis<Dim; ++axis) { + clip_by_axis(axis,polygon,triangle,mesh) ; + if(polygon.size() < 3) return false ; + } + //beware that a box of dimension 0 always clips any triangle + return true ; +} + +template< int Dim , typename Scalar > +bool AABox<Dim,Scalar>::clips_triangle( const unsigned int* triangle, + const Scalar* vertices + ) const { + //wrap th triangle as a mesh + WrapMesh mesh(vertices, 3, triangle, no_neighbours, 1) ; + return clips_triangle(0, &mesh); +} + + /* during intersection, symbolic representation of the polygon vertices */ +template< int Dim , typename Scalar > +template< typename Tgl> +int AABox<Dim,Scalar>::ClipVertex<Tgl>::in_bounds( int axis, + const Scalar bounds[2], + const unsigned int triangle, + const Tgl* mesh + ) { + //vertices of the triangle + const Scalar* verts[3] ; + for(int i=0; i<3; ++i) { + verts[i] = mesh->vertex(mesh->face(triangle)[i]) ; + } + //coordinate of the vertex to test + Scalar coord = 0; + if(type == ORIGINAL_V) { + //original vertex, return its coordinate + coord = verts[symi][axis] ; + } else if(type == EDGE_V) { + //edge vertex, combine the coordinates of the edge vertices + coord = symd[0] * verts[symi][axis] + + (1-symd[0]) * verts[(symi+1)%3][axis] ; + } else if(type == FACE_V) { + //face vertex, combine the coordinates of the triangle vertices + coord = symd[0] * verts[0][axis] + + symd[1] * verts[1][axis] + + (1-symd[0]-symd[1]) * verts[2][axis] ; + } + //below bounds + if(coord < bounds[0]) return -1 ; + //above bounds + if(coord > bounds[1]) return 1 ; + //in bounds + return 0 ; +} + +template< int Dim , typename Scalar > +template< typename Tgl> +void AABox<Dim,Scalar>::ClipVertex<Tgl>::position( const unsigned int triangle, + const Tgl* mesh, + Scalar* output + ) { + //vertices of the triangle + const unsigned int* tverts = mesh->face(triangle) ; + if(type == ORIGINAL_V) { + //original vertex, copy its coordinate + std::copy(mesh->vertex(tverts[symi]), + mesh->vertex(tverts[symi]) + Dim, + output + ) ; + } else if(type == EDGE_V) { + //edge vertex, combine the edge vertices + const Scalar* x0 = mesh->vertex(tverts[ symi ]) ; + const Scalar* x1 = mesh->vertex(tverts[(symi+1)%3]) ; + for(int axis=0; axis < Dim; ++axis) { + output[axis] = symd[0]*x0[axis] + (1-symd[0])*x1[axis] ; + } + } else if(type == FACE_V) { + //face vertex, combine the triangle vertices + const Scalar* x0 = mesh->vertex(tverts[0]) ; + const Scalar* x1 = mesh->vertex(tverts[1]) ; + const Scalar* x2 = mesh->vertex(tverts[2]) ; + for(int axis=0; axis < Dim; ++axis) { + output[axis] = symd[0] *x0[axis] + + symd[1] *x1[axis] + + (1-symd[0]-symd[1])*x2[axis] ; + } + } +} + +template< int Dim , typename Scalar > +template< typename Triangulation > +void AABox<Dim,Scalar>::fill_edge_vertex( int bound_index, + int edge_index, + const unsigned int triangle, + const Triangulation* mesh, + ClipVertex<Triangulation>& target + ) const { + //triangle vertices + const unsigned int* tverts = mesh->face(triangle) ; + //axis corresponding to the vertex + target.symi = edge_index ; + //bound corresponding to the vertex + Scalar x = bounds_[bound_index] ; + //corresponding vertex coordinates + Scalar x0 = mesh->vertex(tverts[ edge_index ])[bound_index/2] ; + Scalar x1 = mesh->vertex(tverts[(edge_index+1)%3])[bound_index/2] ; + //barycentric coordinate of the edge point along the edge + if(x0 != x1) { + target.symd[0] = (x-x1)/(x0-x1) ; + } else { + target.symd[0] = 0 ; + } + target.symd[1] = 0 ; + //type of the vertex + target.type = EDGE_V ; +} + +template< int Dim , typename Scalar > +template< typename Triangulation > +void AABox<Dim,Scalar>::fill_face_vertex( int bound_index1, + int bound_index2, + const unsigned int triangle, + const Triangulation* mesh, + ClipVertex<Triangulation>& target + ) const { + //triangle vertice + const unsigned int* tverts = mesh->face(triangle) ; + target.symi = 0 ; + //bounds corresponding to the vertex + Scalar x = bounds_[bound_index1] ; + Scalar y = bounds_[bound_index2] ; + //corresponding vertex coordinates + Scalar x0 = mesh->vertex(tverts[0])[bound_index1/2] ; + Scalar y0 = mesh->vertex(tverts[0])[bound_index2/2] ; + Scalar x1 = mesh->vertex(tverts[1])[bound_index1/2] ; + Scalar y1 = mesh->vertex(tverts[1])[bound_index2/2] ; + Scalar x2 = mesh->vertex(tverts[2])[bound_index1/2] ; + Scalar y2 = mesh->vertex(tverts[2])[bound_index2/2] ; + //determinant of the system + Scalar det = (x0-x2)*(y1-y2) - (x1-x2)*(y0-y2) ; + if(det == 0) { + target.symd[0] = 0 ; + target.symd[1] = 0 ; + } else { + target.symd[0] = ((x-x2)*(y1-y2)-(x1-x2)*(y-y2))/det ; + target.symd[1] = ((x0-x2)*(y-y2)-(x-x2)*(y0-y2))/det ; + } + //type of the vertex + target.type = FACE_V ; +} + + +/* intersection initialization */ +template< int Dim , typename Scalar > +template< typename Triangulation > +void AABox<Dim,Scalar>::init_intersection( Polygon<Triangulation>& polygon + ) const { + //initialize the polygon as a triangle + //since the polygon is symbolic, this does not depend on the triangle + polygon.resize(3) ; + for(int i=0; i<3; ++i) { + polygon[i].type = ORIGINAL_V ; + polygon[i].symi = i ; + polygon[i].symd[0] = 0 ; + polygon[i].symd[1] = 0 ; + polygon[i].esym = i ; + } +} + +/* clip the polygon along one axis */ +template< int Dim , typename Scalar > +template< typename Triangulation > +void AABox<Dim,Scalar>::clip_by_axis( int axis, Polygon<Triangulation>& polygon, + const unsigned int triangle, + const Triangulation* mesh + ) const { + //size of the input polygon + unsigned int size = polygon.size() ; + //polygon in which the clipped polygon will be built + Polygon<Triangulation> next_polygon ; + next_polygon.reserve(size) ; + //bounds corresponding to the axis + const Scalar* bounds = axis_bounds(axis) ; + //sides of the polygon vertices wrt. the bounds + int* sides = new int[size] ; + for(unsigned int vertex = 0; vertex < size; ++vertex) { + sides[vertex] = + polygon[vertex].in_bounds(axis, bounds, triangle, mesh) ; + } + //build the clipped polygon + for(unsigned int vertex = 0; vertex < size; ++vertex) { + //sides of two consecutive vertices + int my_side = sides[vertex] ; + int next_side = sides[(vertex+1)%size] ; + //check if the current vertex is to be kept + if(my_side == 0) { + //the vertex is in, keep it + next_polygon.push_back(polygon[vertex]) ; + } + //test if an intersection has to be inserted + if(my_side != next_side) { + //test the number of intersections + if(my_side*next_side != 0) { + //the order of traversal of the bounds + int first_bound = my_side < 0 ? 2*axis : 2*axis+1 ; + int second_bound = my_side < 0 ? 2*axis+1 : 2*axis ; + //two intersections + next_polygon.resize(next_polygon.size()+2) ; + ClipVertex<Triangulation>& intersection1 = + next_polygon[next_polygon.size()-2] ; + intersection1.edge_type = polygon[vertex].edge_type ; + intersection1.esym = polygon[vertex].esym ; + ClipVertex<Triangulation>& intersection2 = + next_polygon[next_polygon.size()-1] ; + intersection2.edge_type = BOUNDARY_E ; + intersection2.esym = second_bound ; + //types of the intersections + if(polygon[vertex].edge_type == ORIGINAL_E) { + //the intersection vertices types are EDGE_V + fill_edge_vertex( first_bound, + polygon[vertex].esym, + triangle, + mesh, + intersection1 + ) ; + fill_edge_vertex( second_bound, + polygon[vertex].esym, + triangle, + mesh, + intersection2 + ) ; + } else { + //the intersection vertices types are FACE_V + fill_face_vertex( first_bound, + polygon[vertex].esym, + triangle, + mesh, + intersection1 + ) ; + fill_face_vertex( second_bound, + polygon[vertex].esym, + triangle, + mesh, + intersection2 + ) ; + } + } else { + //crossed bound + int bound_index ; + //one intersection + next_polygon.resize(next_polygon.size()+1) ; + ClipVertex<Triangulation>& intersection = + next_polygon[next_polygon.size()-1] ; + //edge type of the intersection + if(my_side != 0) { + //getting in, the edge type is that of the current vertex + bound_index = my_side < 0 ? 2*axis : 2*axis+1 ; + intersection.edge_type = polygon[vertex].edge_type ; + intersection.esym = polygon[vertex].esym ; + } else { + //going out, the edge corresponds to the current clipping axis + bound_index = next_side < 0 ? 2*axis : 2*axis+1 ; + intersection.edge_type = BOUNDARY_E ; + intersection.esym = bound_index ; + } + //vertex type of the intersection + if(polygon[vertex].edge_type == ORIGINAL_E) { + //the vertex type is EDGE_V + fill_edge_vertex( bound_index, + polygon[vertex].esym, + triangle, + mesh, + intersection + ) ; + } else { + //the vertex type is FACE_V + fill_face_vertex( bound_index, + polygon[vertex].esym, + triangle, + mesh, + intersection + ) ; + } + } + } + } + + //swap polygons + polygon.swap(next_polygon) ; + //cleanup + delete [] sides ; +} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Neighbours/aabox_fwd.hpp b/contrib/Revoropt/include/Revoropt/Neighbours/aabox_fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8fa6a528cf89d958ac4fd3c8e7fda8388fa8fa29 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Neighbours/aabox_fwd.hpp @@ -0,0 +1,206 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_AABOX_FWD_HPP_ +#define _REVOROPT_AABOX_FWD_HPP_ + +#include <Revoropt/Mesh/wrapper_fwd.hpp> + +#include <vector> + +//TODO bring back potential inline functions + +namespace Revoropt { + +template< int _Dim = 3, typename _Scalar = double > +class AABox { + + public : + + /* Typedefs */ + enum { Dim = _Dim } ; + typedef _Scalar Scalar ; + typedef Eigen::Matrix<Scalar, Dim, 1> Vector ; + + /* Construction */ + AABox() ; + + AABox( Scalar bounds[2*Dim] ) ; + + virtual ~AABox() {} ; + + /* Setting bounds */ + void set_bounds( Scalar bounds[2*Dim] ) ; + + //initialize a 0 volume box to a point position + void init_to( const Scalar point[Dim] ) ; + + //increase the box size to contain the given point + void fit_to( const Scalar point[Dim] ) ; + + //grow the box (decrease min bounds and increase max bounds) given an offset + void grow( Scalar offset ) ; + + /* Accessing bounds */ + Scalar* axis_bounds( int axis ) ; + Scalar& min_axis_bound( int axis ) ; + Scalar& max_axis_bound( int axis ) ; + + const Scalar* axis_bounds( int axis ) const ; + const Scalar& min_axis_bound( int axis ) const ; + const Scalar& max_axis_bound( int axis ) const ; + + /* Splitting */ + void split_at_pos( int axis, Scalar pos, AABox& b1, AABox& b2 ) const ; + + void split_at_ratio( int axis, Scalar ratio, AABox& b1, AABox& b2 ) const ; + + /* Point query */ + bool contains( const Scalar point[Dim] ) const ; + + /* AABox clipping */ + bool clips_aabox( const AABox<Dim,Scalar>& rhs ) const ; + + bool aabox_intersection( const AABox<Dim,Scalar>& rhs, + AABox<Dim,Scalar>& output + ) const ; + + /* Triangle clipping */ + template< typename Triangulation > + bool clips_triangle_bbox( const unsigned int triangle, + const Triangulation* mesh + ) const ; + + bool clips_triangle_bbox( const unsigned int* triangle, + const Scalar* vertices + ) const ; + + template< typename Triangulation > + bool triangle_intersection( const unsigned int triangle, + const Triangulation* mesh, + std::vector<Scalar>& output + ) const ; + + bool triangle_intersection( const unsigned int* triangle, + const Scalar* vertices, + std::vector<Scalar>& output + ) const ; + + template< typename Triangulation > + bool clips_triangle( const unsigned int triangle, + const Triangulation* mesh + ) const ; + + bool clips_triangle( const unsigned int* triangle, + const Scalar* vertices + ) const ; + + protected : + + Scalar bounds_[2*Dim] ; + + /** Triangle intersection internals **/ + + /* during intersections, vertex types */ + enum VertexType { + ORIGINAL_V, //an original vertex of a triangle + EDGE_V, //intersection between a triangle edge and a box side + FACE_V //intersection between a triangle and two box sides + } ; + + /* during intersections, edge types */ + enum EdgeType { + ORIGINAL_E, //a piece of a triangle edge + BOUNDARY_E //the intersection between a boundary and the triangle + } ; + + /* during intersection, symbolic representation of the polygon vertices */ + template< typename Triangulation> + class ClipVertex { + + public : + + VertexType type ; + + //symbolic vertex, depending on the type : + // - ORIGINAL_V : the index of the vertex, and 0,0 + // - EDGE_V : the index of the edge, the offset of the intersection, 0 + // - FACE_V : 0 as index, barycentric coordinates of the intersection + int symi ; + Scalar symd[2] ; + + //symbolic edge starting t the vertex + EdgeType edge_type ; + //depending on the type of symbolic edge : + // - ORIGINAL_E : the index of the triangle edge + // - BOUNDARY_E : the index of the boundary + int esym ; + + // determine if the vertex coordinate determined by axis is in the bounds + // returns +1 if over bounds, 0 if in bounds, and -1 is below bounds + + int in_bounds( int axis, const Scalar bounds[2], + const unsigned int triangle, const Triangulation* mesh + ) ; + + void position( const unsigned int triangle, + const Triangulation* mesh, + Scalar* output + ) ; + } ; + + template< typename Triangulation > + void fill_edge_vertex( int bound_index, + int edge_index, + const unsigned int triangle, + const Triangulation* mesh, + ClipVertex<Triangulation>& target + ) const ; + + template< typename Triangulation > + void fill_face_vertex( int bound_index1, + int bound_index2, + const unsigned int triangle, + const Triangulation* mesh, + ClipVertex<Triangulation>& target + ) const ; + + /* during intersection, symbolic polygon representing the clipped triangle */ + /* template typedef would be nicer */ + template< typename Triangulation > + class Polygon : public std::vector< ClipVertex<Triangulation> > {} ; + + /* intersection initialization */ + template< typename Triangulation > + void init_intersection( Polygon<Triangulation>& polygon ) const ; + + /* clip the polygon along one axis */ + template< typename Triangulation > + void clip_by_axis( int axis, Polygon<Triangulation>& polygon, + const unsigned int triangle, + const Triangulation* mesh + ) const ; + + //mesh wrapper + typedef ROMeshWrapper<3,Dim,Scalar> WrapMesh ; + + static const unsigned int NO_NEIGHBOUR = WrapMesh::NO_NEIGHBOUR ; + + //neighbour array for easy mesh wrapping + static const unsigned int no_neighbours[3] ; +} ; + +template< int Dim, typename Scalar> +const unsigned int AABox<Dim, Scalar>::no_neighbours[3] = + {NO_NEIGHBOUR, NO_NEIGHBOUR, NO_NEIGHBOUR} ; + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Neighbours/neighbourhood_def.hpp b/contrib/Revoropt/include/Revoropt/Neighbours/neighbourhood_def.hpp new file mode 100644 index 0000000000000000000000000000000000000000..99b3f8e9ee8b7b72adb46e9675f1006bcdbc3e45 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Neighbours/neighbourhood_def.hpp @@ -0,0 +1,228 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_NEIGHBOURHOOD_DEF_HPP_ +#define _REVOROPT_NEIGHBOURHOOD_DEF_HPP_ + +#include "neighbourhood_fwd.hpp" +#include "aabox_def.hpp" + +#include <assert.h> +#include <algorithm> +#include <utility> +#include <vector> +#include <set> + +namespace Revoropt { + +template< typename Triangulation > +bool NeighbourhoodComputer<Triangulation>::CircularLexicoCompare::operator()( + unsigned int i1, unsigned int i2 +) { + const Scalar* q1 = queries_ + Dim*i1 ; + const Scalar* q2 = queries_ + Dim*i2 ; + for( int i = 0; i<Dim; ++i) { + const int coord = (axis_+i)%Dim ; + if(q1[coord] != q2[coord]) return (q1[coord] < q2[coord]) ; + } + return i1 < i2 ; +} + +template< typename Triangulation > +void NeighbourhoodComputer<Triangulation>::init_sorted_queries() { + //create and sort indices arrays for each axis + for(int axis = 0; axis < Dim; ++axis) { + sorted_indices_[axis].resize(queries_size_) ; + for(unsigned int i = 0; i < queries_size_; ++i) { + sorted_indices_[axis][i] = i ; + } + std::sort( sorted_indices_[axis].begin(), + sorted_indices_[axis].end(), + CircularLexicoCompare(queries_,axis) + ) ; + } +} + +template< typename Triangulation > +void NeighbourhoodComputer<Triangulation>::split_sorted_queries( + int axis, unsigned int begin, unsigned int end +) { + //Split the queries in range [begin,end) in two along the provided axis. + //All the lists along other axes are reordered according to the split. + //After the reordering, all the lists contain the same sets of sorted + //indices between [begin,mid) and [mid,end) with mid = 0.5*(begin + end) + + //index of the medial query point along axis + const unsigned int mid_index = sorted_indices_[axis][(begin+end)/2] ; + //comparison corresponding to the axis + CircularLexicoCompare comp(queries_, axis) ; + + //temporary array to filter the lists + std::vector<unsigned int> filtered_indices(end - begin) ; + //indices used to fill the filtered_indices_array + unsigned int low_index, high_index ; + //filter the sorted lists of all the axes + for(int i = 0; i < Dim; ++i) { + //the splitting axis is already split and sorted correctly + if(axis == i) continue ; + //initialize filling indices + low_index = 0 ; + high_index = (end-begin)/2 ; + //filter + for(unsigned int q = begin; q < end; ++q) { + const unsigned int index = sorted_indices_[i][q] ; + if(comp(index,mid_index)) { + filtered_indices[low_index] = index ; + ++low_index ; + } else { + filtered_indices[high_index] = index ; + ++high_index ; + } + } + //overwrite the indices + std::copy( filtered_indices.begin(), + filtered_indices.end(), + sorted_indices_[i].data()+begin + ) ; + } +} + +template< typename Triangulation > +void NeighbourhoodComputer<Triangulation>::compute_queries_bbox( + Scalar radius, unsigned int q_begin, unsigned int q_end, Box& bbox_out +) { + //initialize the bounding box + const unsigned int init_index = sorted_indices_[0][q_begin] ; + const Scalar* init_query = queries_ + Dim*init_index ; + bbox_out.init_to(init_query) ; + //iterate over the remaining queries + for(unsigned int i = q_begin+1; i < q_end; ++i) { + const unsigned int index = sorted_indices_[0][i] ; + const Scalar* query = queries_ + Dim*index ; + bbox_out.fit_to(query) ; + } + //consider the radius around the query points + bbox_out.grow(radius) ; +} + +template< typename Triangulation > +void NeighbourhoodComputer<Triangulation>::init_triangle_indices() { + //allocate space + t_indices_.resize(mesh_->faces_size()) ; + //initialize the list of indices + for(unsigned int i = 0; i < t_indices_.size(); ++i) { + t_indices_[i] = i ; + } +} + +template< typename Triangulation > +unsigned int NeighbourhoodComputer<Triangulation>::filter_triangle_indices( + const Box& box, unsigned int t_end +) { + //sanity check + assert((t_end>0) && "the number ot triangles is zero or negative") ; + //positions + unsigned int t_in = 0 ; + unsigned int t_out = t_end-1 ; + //iterate over the triantles + while(t_in <= t_out) { + if(box.clips_triangle_bbox(t_indices_[t_in], mesh_)) { + ++t_in ; + } else { + //std::cout << t_in << " " << t_out << " " << std::endl ; + if(t_in != t_out) { + std::swap(t_indices_[t_in],t_indices_[t_out]) ; + } else { + //std::cout << "skip swap" << std::endl ; + } + if(t_out == 0) break ; + --t_out ; + } + } + //return the end of the triangles clipping the box + return t_in ; +} + +template< typename Triangulation > +template < typename Action > +void NeighbourhoodComputer<Triangulation>::compute_internal( + Scalar radius, + Action& action, + unsigned int q_begin, + unsigned int q_end, + unsigned int t_end +) { + assert((q_end>q_begin) && "the query interval is empty or reversed") ; + //test the number of remaining queries + if((q_end-q_begin) == 1) { //one single query + /* + //compute the box around the query + Box box ; + compute_queries_bbox(radius,q_begin,q_end,box) ; + //initialize a polygon to clip the triangles + std::vector<Scalar> polygon ; + //clip each triangle + for(unsigned int t = 0; t < t_end; ++t) { + //get the triangle from its index + const unsigned int* triangle = mesh_->face(t_indices_[t]) ; + //clip it really (not just the bounding box) + bool clips = + box.triangle_intersection(triangle,mesh_->vertices(),polygon) ; + if(clips) { + //perform the action on the clipping + action(sorted_indices_[0][q_begin],polygon) ; + } + } + */ + //perform the action on all the triangles remaining for the unique query + for(unsigned int t = 0; t < t_end; ++t) { + action(sorted_indices_[0][q_begin], t_indices_[t]) ; + } + } else { //several queries + //compute the bounding box of the queries + Box full_box ; + compute_queries_bbox(radius,q_begin,q_end,full_box) ; + //find the longest axis + unsigned int major_axis = 0 ; + Scalar major_size = + full_box.max_axis_bound(0) = full_box.min_axis_bound(0) ; + for(int axis = 1; axis < Dim; ++axis) { + const Scalar len = + full_box.max_axis_bound(axis) = full_box.min_axis_bound(axis) ; + if(len > major_size) { + major_size = len ; + major_axis = axis ; + } + } + //split the queries along the longest axis + const unsigned int q_mid = (q_begin + q_end)/2 ; + split_sorted_queries(major_axis,q_begin,q_end) ; + //bounding boxes of the two parts + Box b1, b2 ; + compute_queries_bbox(radius,q_begin,q_mid,b1) ; + compute_queries_bbox(radius,q_mid,q_end,b2) ; + //compute the triangle sets of the first box + unsigned int t_limit = filter_triangle_indices(b1,t_end) ; + //handle the first box + if(t_limit != 0) { + compute_internal(radius,action,q_begin,q_mid,t_limit) ; + } + //compute the triangle sets of the second box + t_limit = filter_triangle_indices(b2,t_end) ; + //handle the second box + if(t_limit != 0) { + compute_internal(radius,action,q_mid,q_end,t_limit) ; + } + } +} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Neighbours/neighbourhood_fwd.hpp b/contrib/Revoropt/include/Revoropt/Neighbours/neighbourhood_fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d6169acc6527deb42e3a3411a425602113c4c9b3 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Neighbours/neighbourhood_fwd.hpp @@ -0,0 +1,130 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_NEIGHBOURHOOD_FWD_HPP_ +#define _REVOROPT_NEIGHBOURHOOD_FWD_HPP_ + +#include <Revoropt/Neighbours/aabox_fwd.hpp> + +namespace Revoropt { + +/** An action performed on a neighbourhood **/ + +class NeighbourhoodAction { + + public : + + virtual void operator()( unsigned int query, + unsigned int triangle + ) = 0 ; + +} ; + +/** A neighbourhood computer **/ + +template< typename Triangulation > +class NeighbourhoodComputer { + + public : + + /* Typedefs and enums */ + enum { Dim = Triangulation::VertexDim } ; + typedef typename Triangulation::Scalar Scalar ; + typedef AABox<Dim,Scalar> Box ; + + NeighbourhoodComputer( const Triangulation* mesh, + const Scalar* queries, + unsigned int queries_size + ) : mesh_(mesh), + queries_(queries), + queries_size_(queries_size) { + } + + /* Computing the neighbourhoods, applying the action on them */ + template< typename Action > + void compute( Scalar radius, Action& action ) { + //initialize query indices arrays sorted along each axis + init_sorted_queries() ; + init_triangle_indices() ; + compute_internal(radius,action) ; + } + + private : + + /* Mesh */ + const Triangulation* mesh_ ; + + /* Queries */ + const Scalar* queries_ ; + unsigned int queries_size_ ; + + /* Query indices sorted along all the axis */ + std::vector<unsigned int> sorted_indices_[Dim] ; + + class CircularLexicoCompare { + //lexicographic comparison of queries from their indices + public : + //construction + CircularLexicoCompare( const Scalar* queries, int axis ) + : queries_(queries), axis_(axis) { + } + //comparison + bool operator()( unsigned int i1, unsigned int i2 ) ; + private : + //query points + const Scalar* queries_ ; + //first axis for the lexical order + unsigned int axis_ ; + + } ; + + void init_sorted_queries() ; + + //Split the queries in range [begin,end) in two along the provided axis. + //All the lists along other axes are reordered according to the split. + //After the reordering, all the lists contain the same sets of sorted + //indices between [begin,mid) and [mid,end) with mid = 0.5*(begin + end) + void split_sorted_queries( int axis, unsigned int begin, unsigned int end ) ; + + /* Compute the bounding box of a set of query points */ + void compute_queries_bbox( Scalar radius, + unsigned int q_begin, + unsigned int q_end, + Box& bbox_out + ) ; + + /* Initialize the set of triangle indices */ + void init_triangle_indices() ; + + /* Filter a set of triangles to keep only the ones intersecting a box */ + unsigned int filter_triangle_indices( const Box& box, + unsigned int t_end + ) ; + + /* Recursively compute the robust normals */ + template< typename Action > + void compute_internal( Scalar radius, Action& action) { + compute_internal(radius,action,0,queries_size_,mesh_->faces_size()) ; + } + + template< typename Action > + void compute_internal( Scalar radius, Action& action, + unsigned int q_begin, + unsigned int q_end, + unsigned int t_end + ) ; + + std::vector<unsigned int> t_indices_ ; + +} ; + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/RVD/.gitignore b/contrib/Revoropt/include/Revoropt/RVD/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..b11d6f45c393633b5ffcaff41f3a68e64b9922a7 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/RVD/.gitignore @@ -0,0 +1 @@ +utils.os diff --git a/contrib/Revoropt/include/Revoropt/RVD/action.hpp b/contrib/Revoropt/include/Revoropt/RVD/action.hpp new file mode 100644 index 0000000000000000000000000000000000000000..12925b37f3de7188fc436940d053eb8d5839c614 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/RVD/action.hpp @@ -0,0 +1,132 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_RVD_ACTION_HPP_ +#define _REVOROPT_RVD_ACTION_HPP_ + +#include "polygon.hpp" + +namespace Revoropt { + +/* An action is triggered during the computation of the RVD, each time a + * triangle was clipped by a Voronoi cell, on the resulting polygon. This + * prototype is not necessarily to be used, since the action is passed via a + * template parameter, but an action has to define the operator() in the same + * way. */ + +template<typename _Triangulation> +class Action { + + public : + + /* Types and template data */ + typedef _Triangulation Triangulation ; + enum { Dim = Triangulation::VertexDim } ; + typedef Eigen::Matrix<double,Dim,1> Vector ; + + /* Each polygon is decomposed into triangles, and the triangle_action is + * applied on each of these triangles. */ + virtual void operator()( unsigned int site, unsigned int triangle, + const RVDPolygon<Triangulation>& polygon + ) = 0 ; +} ; + +/* The action manager takes several actions, and groups them as a single + * action, which runs them sequencially. */ + +template<typename _Triangulation> +class ActionManager { + + public : + + /* Types and template data */ + typedef _Triangulation Triangulation ; + enum { Dim = Triangulation::VertexDim } ; + typedef Eigen::Matrix<double,Dim,1> Vector ; + + /* Call all the actions in sequence */ + void operator()( unsigned int site, unsigned int triangle, + const RVDPolygon<Triangulation>& polygon + ) { + /* Call all the actions in sequence */ + for(unsigned int i=0; i<actions_.size(); ++i) { + (*actions_[i])(site, triangle, polygon) ; + } + } + + /* Add new actions to be run */ + void add_action( Action<Triangulation>* action ) { + actions_.push_back(action) ; + } + + void clear_actions() { + actions_.resize(0) ; + } + + private : + + /* Storage for the actions */ + std::vector<Action<Triangulation>*> actions_ ; + +} ; + +/* Action finalization : when the RVD is fully computed, the finalize() method + * of the action is called if it exists. The following tests whether a class has + * a finalize method, and provides a wrapper to call it or not depending on its + * existence. */ + +/* test whether a class has a finalize() method */ + +template< typename Wrapped > +class test_finalize { + + private : + + //used to check which of the test methods below is used + //in the definition of the value public attribute + typedef char yes[1] ; + typedef char no[2] ; + + template<typename C, int dummy = sizeof(&C::finalize)> struct check ; + + //this method can only be instanciated if Tested has void finalize() + template <typename C> static yes &test( check<C>* ) ; + //this method is always instanciated + template <typename C> static no &test( ... ) ; + + public: + + //if the size of the return type of the test function used here is yes + //the first test function was instanciated, and therefore *T is legal + static bool const value = sizeof(test<Wrapped>(0)) == sizeof(yes) ; +} ; + +template<typename Wrapped, bool b> +class wrap_finalize_impl { + public : + static void wrap(Wrapped& wrapped) {} +} ; + +template<typename Wrapped> +class wrap_finalize_impl<Wrapped, true> { + public : + static void wrap(Wrapped& wrapped) { + wrapped.finalize() ; + } +} ; + +template<typename Wrapped> +void wrap_finalize(Wrapped& wrapped) { + return wrap_finalize_impl<Wrapped, test_finalize<Wrapped>::value>::wrap(wrapped) ; +} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/RVD/ann_backend.hpp b/contrib/Revoropt/include/Revoropt/RVD/ann_backend.hpp new file mode 100644 index 0000000000000000000000000000000000000000..0c5641d45266c4eb49cc37c0a66ad07f5d64207c --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/RVD/ann_backend.hpp @@ -0,0 +1,84 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_RVD_ANN_BACKEND_HPP_ +#define _REVOROPT_RVD_ANN_BACKEND_HPP_ + +#include <ANN/ANN.h> +#include <vector> +#include <iostream> + +namespace Revoropt { + +/** Nearest neighbour backend for ann **/ + +template<int _Dim> +class ANNBackend { + + public : + + /* Types and template data */ + enum { Dim = _Dim } ; + + /* Initialization, destruction */ + ANNBackend() : ann_sites_(NULL), ann_tree_(NULL) { + } + + ANNBackend( const double* sites, + unsigned int sites_size + ) : ann_sites_(NULL), ann_tree_(NULL) { + set_sites(sites, sites_size) ; + } + + ~ANNBackend() { + delete [] ann_sites_ ; + delete ann_tree_ ; + //annClose() ; // FIXME : memory leak if not used after all ann trees are + //deleted, perhaps use a static counter of instances and close when instances + //of clippers fall to 0. Would however break any other ANN tree built in a + //program using this code, which is highly undesireable. Memory leak may + //therefore be the only solution. + } + + /* Nearest site */ + void knnSearch( const double* query, unsigned int size, + unsigned int* nnsites, double* nndistances + ) { + ann_tree_->annkSearch( const_cast<double*>(query), + size, + (int*) nnsites, + nndistances + ) ; + } + + /* Overloading site setting in clipper to update nearest neighbours */ + void set_sites( const double* sites, unsigned int sites_size ) { + //build ann tree + delete [] ann_sites_ ; + delete ann_tree_ ; + + ann_sites_ = new double*[sites_size] ; + for(unsigned int i=0 ; i < sites_size ; ++i) { + ann_sites_[i] = const_cast<double*>(sites + Dim*i) ; + } + + ann_tree_ = new ANNkd_tree(ann_sites_, sites_size, Dim) ; + } + + private : + + /* ANN data */ + double** ann_sites_ ; + ANNkd_tree* ann_tree_ ; +} ; + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/RVD/clipper.hpp b/contrib/Revoropt/include/Revoropt/RVD/clipper.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ceca842f0bf74d617288c05f8a6ad98ec5c45214 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/RVD/clipper.hpp @@ -0,0 +1,87 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_RVD_CLIPPER_HPP_ +#define _REVOROPT_RVD_CLIPPER_HPP_ + +#include "polygon.hpp" + +namespace Revoropt { + +/** Interface for clipping polygons by Voronoï cells **/ + +template<typename _Triangulation> +class Clipper { + + public : + + /* Types and template data */ + typedef _Triangulation Triangulation ; + enum { Dim = Triangulation::VertexDim } ; + typedef typename Triangulation::Scalar Scalar ; + typedef Eigen::Matrix<Scalar,Dim,1> Vector ; + + /* Initialization */ + Clipper() : sites_(NULL), sites_size_(0), mesh_(NULL) {} ; + + Clipper( const Scalar* sites, unsigned int sites_size, + const Triangulation* mesh + ) : sites_(sites), sites_size_(sites_size), mesh_(mesh) { + } + + virtual ~Clipper() {} + + /* Given the polygon, the site, the site and vertex positions, update the + * polygon by clipping it by the Voronoï cell of the site. Beware that calling + * this function several times on the same triangle / site couple without + * running the reset method may lead to bad behaviour.*/ + + virtual void clip_by_cell( RVDPolygon<Triangulation>& polygon, + unsigned int triangle, + unsigned int site + ) = 0 ; + + /* Given a query point, provide the nearest site */ + virtual unsigned int nearest_site( const Scalar* query ) = 0 ; + + /* Updating the sites and mesh */ + virtual void set_sites( const Scalar* sites, + unsigned int sites_size + ) { + sites_ = sites ; + sites_size_ = sites_size ; + } + + virtual void set_mesh( const Triangulation* mesh ) { + mesh_ = mesh ; + } + + /* Providing sites and mesh */ + const Scalar* sites() { return sites_ ; } + unsigned int sites_size() { return sites_size_ ; } + const Triangulation* mesh() {return mesh_ ; } + + /* resetting internal data of the clipper */ + virtual void reset() {} ; + + protected : + + /* Sites */ + const Scalar* sites_ ; + unsigned int sites_size_ ; + + /* Mesh */ + const Triangulation* mesh_ ; + +} ; + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/RVD/flann_backend.hpp b/contrib/Revoropt/include/Revoropt/RVD/flann_backend.hpp new file mode 100644 index 0000000000000000000000000000000000000000..de31c70caa01308db3f3ae1ad84bf1265ce16da2 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/RVD/flann_backend.hpp @@ -0,0 +1,83 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_RVD_FLANN_BACKND_HPP_ +#define _REVOROPT_RVD_FLANN_BACKND_HPP_ + +#include <flann/flann.hpp> +#include <vector> +#include <iostream> + +namespace Revoropt { + +/** Nearest neighbour backend for flann **/ + +template<int _Dim> +class FLANNBackend { + + public : + + /* Types and template data */ + enum { Dim = _Dim } ; + + /* Initialization, destruction */ + FLANNBackend() : index_(flann::KDTreeSingleIndexParams(10)), + flann_params_(flann::FLANN_CHECKS_UNLIMITED,0,true) { + } + + FLANNBackend( const double* sites, + unsigned int sites_size + ) : index_(flann::KDTreeSingleIndexParams(10)), + flann_params_(flann::FLANN_CHECKS_UNLIMITED,0,true) { + set_sites(sites, sites_size) ; + } + + /* Nearest neighbours */ + void knnSearch( const double* query, unsigned int size, + unsigned int* nnsites, double* nndistances + ) { + //convert input to flann matrices + flann::Matrix<double> flann_query(const_cast<double*>(query), 1, Dim) ; + flann::Matrix<int> flann_nnsites((int*) nnsites, 1, size) ; + flann::Matrix<double> flann_nndistances(nndistances, 1, size) ; + + //query + index_.knnSearch( flann_query, + flann_nnsites, + flann_nndistances, + size, + flann_params_ + ) ; + } + + /* Overloading site setting in clipper to update nearest neighbours */ + void set_sites( const double* sites, unsigned int sites_size ) { + //build flann index + flann_points_ = flann::Matrix<double>( + const_cast<double*>(sites), sites_size, Dim + ) ; + index_.buildIndex(flann_points_) ; + } + + private : + + /* FLANN data */ + flann::Matrix<double> flann_points_ ; + flann::Index< flann::L2<double> > index_ ; + + /* Parameters */ + flann::SearchParams flann_params_ ; +} ; + +//}}} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/RVD/nn_clipper.hpp b/contrib/Revoropt/include/Revoropt/RVD/nn_clipper.hpp new file mode 100644 index 0000000000000000000000000000000000000000..989a79a19d26a264aed5fb97aae106bc78e88d13 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/RVD/nn_clipper.hpp @@ -0,0 +1,632 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_RVD_NN_CLIPPER_HPP_ +#define _REVOROPT_RVD_NN_CLIPPER_HPP_ + +#include "clipper.hpp" + +#include <vector> +#include <iostream> + +namespace Revoropt { + +/** Base class for clippers using nearest neighbour queries **/ + +template<typename _Triangulation, typename _NNBackend> +class NNClipper : public Clipper<_Triangulation> { + + public : + + /* Types and template data */ + typedef _Triangulation Triangulation ; + typedef Clipper<Triangulation> Base ; + typedef _NNBackend NNBackend ; + enum { Dim = Triangulation::VertexDim } ; + typedef typename Triangulation::Scalar Scalar ; + typedef Eigen::Matrix<Scalar,Dim,1> Vector ; + + typedef RVDEdge<Triangulation> Edge ; + + enum Algo { SITE_NEIGHBOURS, + TRIANGLE_NEIGHBOURS, + MIXED_TRIANGLE_NEIGHBOURS, + CORNER_CLIPPING, + CENTER_THEN_CORNER_CLIPPING + } ; + + /* Initialization, destruction */ + NNClipper() : Base(), + neighbourhood_size_(10), + algorithm_(CORNER_CLIPPING) { + } + + NNClipper( const Scalar* sites, unsigned int sites_size, + const Triangulation* mesh + ) : Base(sites, sites_size, mesh), + neighbourhood_size_(10), + algorithm_(CORNER_CLIPPING) { + set_sites(sites, sites_size) ; + } + + virtual ~NNClipper() {} + + /* Choose algorithm */ + void set_algorithm( int algorithm ) { + int algo = algorithm > 4 ? 4 : algorithm ; + algorithm_ = (Algo) algo ; + } + + /* Clipping */ + void clip_by_cell( RVDPolygon<Triangulation>& polygon, + unsigned int triangle, + unsigned int site + ) { + //reset max distance between the site and its neighbours so far + max_neighbour_distance_ = 0 ; + + //get site position as a vector + Eigen::Map<const Vector> site_pos(Base::sites_+Dim*site) ; + + //set current site as handled + site_last_opposite_[site] = Couple(triangle, site) ; + + //compute distances between the site and the polygon vertices + site_distances_.resize(0) ; + + //resize new_vertices array + new_vertices_.assign(polygon.size(), true) ; + + for(unsigned int i=0; i<polygon.size(); ++i) { + const Vector d = site_pos - polygon[i].vertex ; + site_distances_.push_back(d.dot(d)) ; + } + + unsigned int clippings = 0 ; + if(algorithm_ == SITE_NEIGHBOURS) { + clippings += full_neighbour_clip(polygon, triangle, site, neighbourhood_size_) ; + return ; + } + if(algorithm_ == TRIANGLE_NEIGHBOURS) { + clippings += full_radius_clip(polygon, triangle, site, neighbourhood_size_) ; + return ; + } + clippings += neighbour_clip(polygon, triangle, site, neighbourhood_size_) ; + if(algorithm_ == MIXED_TRIANGLE_NEIGHBOURS) { + clippings += full_radius_clip(polygon, triangle, site, neighbourhood_size_) ; + return ; + } + if(algorithm_ == CORNER_CLIPPING) { + clippings += corner_clip(polygon, triangle, site) ; + return ; + } + if(algorithm_ == CENTER_THEN_CORNER_CLIPPING) { + clippings += center_then_corner_clip(polygon, triangle, site) ; + return ; + } + } + + /* Nearest site */ + unsigned int nearest_site( const Scalar* query ) { + //storage for the result + unsigned int site ; + Scalar distance ; + //search + nnbackend_.knnSearch(query, 1, &site, &distance) ; + + return site ; + } + + /* Overloading site setting in clipper to update nearest neighbours */ + void set_sites( const Scalar* sites, unsigned int sites_size ) { + //call parent update + Base::set_sites( sites, sites_size ) ; + + //reset the site_last_opposite array + reset() ; + + //initialize the nearest neighbour backend + nnbackend_.set_sites(sites, sites_size) ; + } + + /* set/get parameters */ + unsigned int neighbourhood_size() { + return neighbourhood_size_ ; + } + void set_neighbourhood_size( unsigned int size ) { + neighbourhood_size_ = size ; + } + + /* reset */ + void reset() { + site_last_opposite_.assign( + Base::sites_size_, + Couple(Triangulation::NO_NEIGHBOUR,0) + ) ; + } + + private : + + /* Parameters */ + unsigned int neighbourhood_size_ ; + + /* Prevent multiple clippings by the same opposite site for a given site and + * triangle. This array is reset with the reset method. */ + typedef std::pair<unsigned int, unsigned int> Couple ; + std::vector<Couple> site_last_opposite_ ; + + /* Data for nearest neighbour searches */ + double max_neighbour_distance_ ; + std::vector<unsigned int> nn_indices_ ; + std::vector<Scalar> nn_distances_ ; + + /* Data for the ongoing clipping */ + //during clipping, the vertices of the clipped polygon are marked true in this + //array and the new vertices of the clipped polygon are marked false. + std::vector<bool> new_vertices_ ; + std::vector<bool> next_new_vertices_ ; + + //distances between the site and the polygon vertices + std::vector<double> site_distances_ ; + std::vector<double> site_next_distances_ ; + + //distance between another site and the polygon vertices + std::vector<double> opposite_distances_ ; + + /* Clipping by the sites nearest neighbours */ + unsigned int neighbour_clip( RVDPolygon<Triangulation>& polygon, + unsigned int triangle, + unsigned int site, + unsigned int nneighbours + ) { + //query point + const Scalar* query = Base::sites_ + Dim*site ; + + //count clippings + unsigned int clippings = 0 ; + + //site radius : maximal squared distance to the polygon vertices + double site_radius = *std::max_element( site_distances_.begin(), + site_distances_.end() + ) ; + + //number of neighbours neighbours + unsigned int neighbour_size = std::min(Base::sites_size_, nneighbours) ; + nn_indices_.resize(neighbour_size) ; + nn_distances_.resize(neighbour_size) ; + + nnbackend_.knnSearch( query, + neighbour_size, + nn_indices_.data(), + nn_distances_.data() + ) ; + + max_neighbour_distance_ = nn_distances_[neighbour_size-1] ; + + //iterate clip by nearest neighbours + for(unsigned int i=0 ; i<neighbour_size ; ++i) { + ++clippings ; + unsigned int opposite_site = nn_indices_[i] ; + + if(opposite_site == site) { + continue ; + } + //check if security readius is reached + if(nn_distances_[i] > 4*site_radius) { + return clippings-1 ; + } + //check whether this site was handled before + if(site_last_opposite_[opposite_site] != Couple(triangle,site)) { + //mark current opposite site as done for this clipping + site_last_opposite_[opposite_site] = Couple(triangle,site) ; + //perform the clipping + clip_by_plane(polygon, triangle, site, opposite_site) ; + if(polygon.size() > 0) { + site_radius = *std::max_element( site_distances_.begin(), + site_distances_.end() + ) ; + } else { + site_radius = 0 ; + } + } + } + return clippings ; + } + + /* iterate neighbour clipping until termination */ + unsigned int full_neighbour_clip( RVDPolygon<Triangulation>& polygon, + unsigned int triangle, + unsigned int site, + unsigned int nneighbours + ) { + unsigned int current_nneighbours = nneighbours ; + unsigned int clippings = 0 ; + do{ + //clip using the current neighbourhood size + clippings = neighbour_clip(polygon, triangle, site, current_nneighbours) ; + //check for security radius termination of the clipping + if(clippings < current_nneighbours) { + //in case of early termination, the clipping is correct + return clippings ; + } + //no early termination, increase the size of the neighbourhood + current_nneighbours += nneighbours ; + } while(clippings < Base::sites_size_ ) ; + //return the number of clippings required, all the sites at this point + return clippings ; + } + + /* Clipping using the nearest neighours of the polygon */ + unsigned int corner_clip( RVDPolygon<Triangulation>& polygon, + unsigned int triangle, + unsigned int site + ) { + //mark all the vertices as new vertices + new_vertices_.assign(polygon.size(),true) ; + + //total number of clippings + unsigned int clippings = 0 ; + + //marked true if the polygon was clipped during current iteration + bool clipped = false ; + + do { + clipped = false ; + + //get the nearest neighbours of all the new polygon corners + for(unsigned int i = 0; i<polygon.size(); ++i) { + if(new_vertices_[i]) { + if(site_distances_[i] >= 0.25*max_neighbour_distance_) { + //get the nearest neighbour of the corner + int opposite_site = nearest_site(polygon[i].vertex.data()) ; + //Debug + //Vector save = polygon[i].vertex ; + if(site_last_opposite_[opposite_site] != Couple(triangle,site)) { + //mark current opposite site as done for this clipping + site_last_opposite_[opposite_site] = Couple(triangle,site) ; + //perform the clipping + clipped = clip_by_plane(polygon, triangle, site, opposite_site) ; + if(!clipped) { + // for(int k = 0; k < site_next_distances_.size(); ++k) { + // std::cout << opposite_distances_[k] - site_next_distances_[k] << " " << std::flush ; + // } + // std::cout << std::endl ; + // std::cout << site << " " << opposite_site << " " << i << "/" << next_polygon_.size() << std::endl ; + // Eigen::Map<const Vector> spos(Base::sites_+Dim*site) ; + // Eigen::Map<const Vector> opos(Base::sites_+Dim*opposite_site) ; + // std::cout << (spos-save).norm() << " " << (opos-save).norm() << std::endl ; + // std::cout << nearest_site(next_polygon_[i].vertex.data()) << std::endl ; + // std::cout << nearest_site(save.data()) << std::endl ; + // std::cout << opposite_site << std::endl ; + // std::cout << opos << std::endl ; + // std::cout << "===" << std::endl ; + // std::cout << spos << std::endl ; + // std::cout << "===" << std::endl ; + // std::cout << save << std::endl ; + // std::cout << "===" << std::endl ; + // std::cout << "Warning, nearest neighbour of corner did not clip." << std::endl ; + } + ++clippings ; + break ; + } else { + new_vertices_[i] = false ; + } + } else { + new_vertices_[i] = false ; + } + } + } + } while( clipped ) ; + return clippings ; + } + + double compute_center_radius( RVDPolygon<Triangulation>& polygon, + Vector& center, + double site_radius + ) { + double center_radius = 0 ; + for(unsigned int i=0; i<polygon.size(); ++i) { + Vector d = (polygon[i].vertex - center) ; + center_radius = std::max(center_radius, d.dot(d)) ; + } + center_radius = sqrt(center_radius) ; + center_radius += sqrt(site_radius) ; + center_radius *= center_radius ; + + return center_radius*center_radius ; + } + + double compute_finer_center_radius( RVDPolygon<Triangulation>& polygon, + Vector& center, + std::vector<double>& site_distances + ) { + double center_radius = 0 ; + for(unsigned int i=0; i<polygon.size(); ++i) { + Vector d = (polygon[i].vertex - center) ; + center_radius = + std::max(center_radius, sqrt(d.dot(d)) + sqrt(site_distances[i])) ; + } + + return center_radius*center_radius ; + } + + /* Clipping using sites centered on the barycenter*/ + unsigned int radius_clip( RVDPolygon<Triangulation>& polygon, + unsigned int triangle, + unsigned int site, + unsigned int nneighbours + ) { + if(polygon.size() < 3) return 0 ; + //compute the centroid of the polygon + Vector center = Vector::Zero() ; + for(unsigned int i=0; i<polygon.size(); ++i) { + center += polygon[i].vertex ; + } + center /= polygon.size() ; + + + //compute the radius of the search + double center_radius = + compute_finer_center_radius(polygon, center, site_distances_) ; + + //get nearest neighbours + unsigned int neighbour_size = std::min(Base::sites_size_, nneighbours) ; + nn_indices_.resize(neighbour_size) ; + nn_distances_.resize(neighbour_size) ; + + nnbackend_.knnSearch( center.data(), + neighbour_size, + nn_indices_.data(), + nn_distances_.data() + ) ; + + unsigned int clippings = 0 ; + + for( unsigned int i = 0; i < neighbour_size; ++i ) { + //increase the number of sites handled + ++clippings ; + + //get neighbour index + const unsigned int opposite_site = nn_indices_[i] ; + + //check that the neighbour was not previously handled + if(site_last_opposite_[opposite_site] != Couple(triangle,site)) { + + site_last_opposite_[opposite_site] = Couple(triangle,site) ; + + //check that the neighbour is within the (updated) center radius + if(nn_distances_[i] > center_radius) { + return clippings-1 ; + } + + //clip curr_polygon_ by the bisector between site and opposite_site + clip_by_plane(polygon, triangle, site, opposite_site) ; + + //update the center radius + if(polygon.size() > 0) { + center_radius = + compute_finer_center_radius(polygon, center, site_distances_) ; + } else { + center_radius = 0 ; + } + } + } + + return clippings ; + } + + /* iterate radius clipping until termination */ + unsigned int full_radius_clip( RVDPolygon<Triangulation>& polygon, + unsigned int triangle, + unsigned int site, + unsigned int nneighbours + ) { + unsigned int current_nneighbours = nneighbours ; + unsigned int clippings = 0 ; + do{ + //clip using the current neighbourhood size + clippings = radius_clip(polygon, triangle, site, current_nneighbours) ; + //check for security radius termination of the clipping + if(clippings < current_nneighbours) { + //in case of early termination, the clipping is correct + return clippings ; + } + //no early termination, increase the size of the neighbourhood + current_nneighbours += nneighbours ; + } while(clippings < Base::sites_size_) ; + //return the number of clippings required, all the sites at this point + return clippings ; + } + + /* Clipping using the nearest site from the barycenter */ + unsigned int center_clip( RVDPolygon<Triangulation>& polygon, + unsigned int triangle, + unsigned int site + ) { + if(polygon.size() < 3) return 0 ; + //compute the centroid of the polygon + Vector center = Vector::Zero() ; + for(unsigned int i=0; i<polygon.size(); ++i) { + center += polygon[i].vertex ; + } + center /= polygon.size() ; + + nn_indices_.resize(2) ; + nn_distances_.resize(2) ; + + nnbackend_.knnSearch( center.data(), + 2, + nn_indices_.data(), + nn_distances_.data() + ) ; + + unsigned int opposite_site = nn_indices_[0] == site ? + nn_indices_[1] : nn_indices_[0] ; + + //check that the neighbour was not previously handled + if(site_last_opposite_[opposite_site] == Couple(triangle,site)) { + //no new site handled + return 0 ; + } + + site_last_opposite_[opposite_site] = Couple(triangle,site) ; + + //clip curr_polygon_ by the bisector between site and opposite_site + clip_by_plane(polygon, triangle, site, opposite_site) ; + + //one new site was handled + return 1 ; + } + + /* iterate center clipping until no new site is handled, then corner clip */ + unsigned int center_then_corner_clip( RVDPolygon<Triangulation>& polygon, + unsigned int triangle, + unsigned int site + ) { + unsigned int clippings = 0 ; + unsigned int last_clippings = 0 ; + do{ + //clip using the polygon barycenter nearest neighbour + last_clippings = center_clip(polygon, triangle, site) ; + //add up potential new clip + clippings += last_clippings ; + } while(last_clippings == 1) ; + //return the number of clippings required, all the sites at this point + return clippings + corner_clip(polygon, triangle, site) ; + } + + /* Clipping by a bisector */ + bool clip_by_plane( RVDPolygon<Triangulation>& polygon, + unsigned int triangle, + unsigned int site, + unsigned int opposite_site + ) { + //polygon size + unsigned int size = polygon.size() ; + + //get the site positions for distance computations + Eigen::Map<const Vector> site_pos(Base::sites_ +Dim*site) ; + Eigen::Map<const Vector> opp_site_pos(Base::sites_ +Dim*opposite_site) ; + + //compute distances between opposite site and polygon vertices + opposite_distances_.resize(0) ; + for(unsigned int i=0; i<size; ++i) { + const Vector d = opp_site_pos - polygon[i].vertex ; + opposite_distances_.push_back(d.dot(d)) ; + } + + //reset next information + next_new_vertices_.resize(0) ; + next_polygon_.resize(0) ; + site_next_distances_.resize(0) ; + + //return value, changed to true is some clipping occurs + bool clipped = false ; + + //iterate over the vertices of the polygon + for(unsigned int i = 0; i<size; ++i) { + //get the sides of the current vertex and the next one + const double my_side = opposite_distances_[i] - site_distances_[i] ; + const double next_side = opposite_distances_[(i+1)%size] + - site_distances_[(i+1)%size] ; + //check whether the vertex is in or out + if(my_side > 0) { + //vertex is in, keep it and its distance + next_new_vertices_.push_back(new_vertices_[i]) ; + next_polygon_.push_back(polygon[i]) ; + site_next_distances_.push_back(site_distances_[i]) ; + } + + if(my_side <= 0) { + //the vertex is out, the opposite site owns a piece of this polygon + clipped = true ; + } + + //check whether the next vertex is on the same side + if( my_side*next_side < 0 || + (my_side == 0 && next_side > 0) || + (my_side > 0 && next_side == 0) + ) { + + //different sides, an intersection point must be added + const unsigned int* triangle_vertices = Base::mesh_->face(triangle) ; + + //add an uninitialized edge + const unsigned int new_edge_index = next_polygon_.size() ; + next_polygon_.resize(new_edge_index+1) ; + RVDEdge<Triangulation>& new_edge = next_polygon_[new_edge_index] ; + + //build the intersection vertex + if(polygon[i].config == Edge::FACE_EDGE) { + //the intersection is an EDGE_VERTEX + //recover the combinatorics + const unsigned int edge_index = polygon[i].combinatorics ; + + const unsigned int x1 = triangle_vertices[(edge_index )%3] ; + const unsigned int x2 = triangle_vertices[(edge_index+1)%3] ; + + new_edge.vertex.reset( x1, x2, + site, opposite_site, + Base::mesh_, Base::sites_ + ) ; + + } else { + //the intersection is a FACE_VERTEX + //recover the combinatorics + const unsigned int x1 = triangle_vertices[0] ; + const unsigned int x2 = triangle_vertices[1] ; + const unsigned int x3 = triangle_vertices[2] ; + const unsigned int other_site = polygon[i].combinatorics ; + + new_edge.vertex.reset( x1, x2, x3, + site, opposite_site, other_site, + Base::mesh_, Base::sites_ + ) ; + } + + //set the edge configuration + if(my_side > 0) { + new_edge.config = Edge::BISECTOR_EDGE ; + new_edge.combinatorics = opposite_site ; + } else { + new_edge.config = polygon[i].config ; + new_edge.combinatorics = polygon[i].combinatorics ; + } + + //compute intersection distance with the center site + Vector d = site_pos - new_edge.vertex ; + //std::cout << "New vertex is at distance " << d.dot(d) << std::endl ; + site_next_distances_.push_back(d.dot(d)) ; + next_new_vertices_.push_back(true) ; + } + } + + //update current information + new_vertices_.swap(next_new_vertices_) ; + polygon.swap(next_polygon_) ; + site_distances_.swap(site_next_distances_) ; + + //return clipped status + return clipped ; + } + + //during clipping the new polygon is built in this one and then swapped + RVDPolygon<Triangulation> next_polygon_ ; + + /* Algorithm */ + Algo algorithm_ ; + + /* Nearest neighbour backend */ + NNBackend nnbackend_ ; +} ; + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/RVD/polygon.hpp b/contrib/Revoropt/include/Revoropt/RVD/polygon.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e05d0fcc5bde2812fe1c56a42978f041f7f3e29a --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/RVD/polygon.hpp @@ -0,0 +1,519 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_RVD_POLYGON_HPP_ +#define _REVOROPT_RVD_POLYGON_HPP_ + +#include <eigen3/Eigen/Dense> +#include <vector> +#include <iostream> + +namespace Revoropt { + +/**{{{ Vertices **/ + +/* A RVD vertex acts as a traditionnal vector equipped with the combinatorial + * information which defines it. The vertices of the input mesh and sites of the + * diagram wich are involved in this combinatorics are stored, and the position + * of the point can be recomputed from thins information. */ + +template<typename _Triangulation> +class RVDVertex : public Eigen::Matrix<double,_Triangulation::VertexDim,1> { + + public : + + /** Typedefs **/ + typedef _Triangulation Triangulation ; + enum { Dim = Triangulation::VertexDim } ; + typedef Eigen::Matrix<double,Dim,1> Vector ; + const static unsigned int NO_COMBINATORICS = -1 ; + + /* The different configurations of the vertices of a restricted Voronoi diagram. + * ORIGINAL is an original vertex of the input mesh, EDGE_VERTEX is the + * intersection between an edge of the input mesh and a bissector of the Voronoi + * diagram, and FACE_VERTEX is the intersection between a face of the input mesh + * and a Voronoi edge. */ + + enum Config { + UNDEFINED, + ORIGINAL, + EDGE_VERTEX, + FACE_VERTEX + } ; + + + /** Initialization **/ + /* build an UNDEFINED vertex */ + RVDVertex() { reset() ; } + + /* build an ORIGINAL vertex */ + RVDVertex( unsigned int x1_id, + const Triangulation* mesh + ) { + reset(x1_id,mesh) ; + } + /* build an EDGE_VERTEX */ + RVDVertex( unsigned int x1_id, unsigned int x2_id, + unsigned int v1_id, unsigned int v2_id, + const Triangulation* mesh, const double* sites + ) { + reset(x1_id,x2_id,v1_id,v2_id,mesh,sites) ; + } + /* build a FACE_VERTEX */ + RVDVertex( unsigned int x1_id, unsigned int x2_id, unsigned int x3_id, + unsigned int v1_id, unsigned int v2_id, unsigned int v3_id, + const Triangulation* mesh, const double* sites + ) { + reset(x1_id,x2_id,x3_id,v1_id,v2_id,v3_id,mesh,sites) ; + } + + /** Modification **/ + + /* change into an UNDEFINED vertex */ + void reset() { + config_ = UNDEFINED ; + } + + /* change into an ORIGINAL vertex */ + void reset( unsigned int x1_id, + const Triangulation* mesh + ) { + //set configuration + config_ = ORIGINAL ; + //set combinatorics + combinatorics_[0] = x1_id ; + combinatorics_[1] = NO_COMBINATORICS ; + combinatorics_[2] = NO_COMBINATORICS ; + combinatorics_[3] = NO_COMBINATORICS ; + combinatorics_[4] = NO_COMBINATORICS ; + combinatorics_[5] = NO_COMBINATORICS ; + //compute corresponding position + Eigen::Map<const Vector> x(mesh->vertex(combinatorics_[0])) ; + this->array() = x ; + } + + /* change into an EDGE_VERTEX */ + void reset( unsigned int x1_id, unsigned int x2_id, + unsigned int v1_id, unsigned int v2_id, + const Triangulation* mesh, const double* sites + ) { + //set configuration + config_ = EDGE_VERTEX ; + //set combinatorics + combinatorics_[0] = x1_id ; + combinatorics_[1] = x2_id ; + combinatorics_[2] = NO_COMBINATORICS ; + combinatorics_[3] = v1_id ; + combinatorics_[4] = v2_id ; + combinatorics_[5] = NO_COMBINATORICS ; + //compute corresponding position + //get the two vertices + Eigen::Map<const Vector> x1(mesh->vertex(x1_id)) ; + Eigen::Map<const Vector> x2(mesh->vertex(x2_id)) ; + //get the two sites + Eigen::Map<const Vector> v1(sites+Dim*v1_id) ; + Eigen::Map<const Vector> v2(sites+Dim*v2_id) ; + + //compute the intersection between the bissector and the edge + const Vector diff_sites = v2-v1 ; + const Vector mid_sites = 0.5*(v1+v2) ; + const Vector edge = x2-x1 ; + + const Vector pos = x1 + + ( diff_sites.dot(mid_sites - x1) + / diff_sites.dot(edge) + ) + * edge ; + + //position becomes the RVDVertex position + this->array() = pos ; + } + + /* change into a FACE_VERTEX */ + void reset( unsigned int x1_id, unsigned int x2_id, unsigned int x3_id, + unsigned int v1_id, unsigned int v2_id, unsigned int v3_id, + const Triangulation* mesh, const double* sites + ) { + //set configuration + config_ = FACE_VERTEX ; + //set combiatorics + combinatorics_[0] = x1_id ; + combinatorics_[1] = x2_id ; + combinatorics_[2] = x3_id ; + combinatorics_[3] = v1_id ; + combinatorics_[4] = v2_id ; + combinatorics_[5] = v3_id ; + //compute corresponding position + //get the three vertices + Eigen::Map<const Vector> x1(mesh->vertex(x1_id)) ; + Eigen::Map<const Vector> x2(mesh->vertex(x2_id)) ; + Eigen::Map<const Vector> x3(mesh->vertex(x3_id)) ; + + //get the three sites + Eigen::Map<const Vector> v1(sites+Dim*v1_id) ; + Eigen::Map<const Vector> v2(sites+Dim*v2_id) ; + Eigen::Map<const Vector> v3(sites+Dim*v3_id) ; + + //compute the intersection between the bissectors and the face + const double c1 = (x2-x1).dot(v2-v1) ; + const double c2 = (x3-x1).dot(v2-v1) ; + const double c3 = 0.5*(v1+v2-2*x1).dot(v2-v1) ; + const double c4 = (x2-x1).dot(v3-v1) ; + const double c5 = (x3-x1).dot(v3-v1) ; + const double c6 = 0.5*(v1+v3-2*x1).dot(v3-v1) ; + + const double det = (c1*c5-c2*c4) ; +#ifdef DEBUG + if(det == 0) { + std::cout << "0 determinant : " + << c1 << ", " + << c5 << ", " + << c2 << ", " + << c4 << std::endl ; + std::cout << "Combinatorics : " + << combinatorics_[3] << ", " + << combinatorics_[4] << ", " + << combinatorics_[5] << std::endl ; + std::cout << "v1 : [" + << v1[0] << ", " + << v1[1] << ", " + << v1[2] << " ]" << std::endl ; + std::cout << "v2 : [ " + << v2[0] << ", " + << v2[1] << ", " + << v2[2] << " ]" << std::endl ; + std::cout << "v3 : [" + << v3[0] << ", " + << v3[1] << ", " + << v3[2] << " ]" << std::endl ; + } +#endif + const double denom = 1/det ; + const double bary1 = denom*(c3*c5-c2*c6) ; + const double bary2 = denom*(c1*c6-c3*c4) ; + + const Vector pos = x1 + bary1*(x2-x1) + bary2*(x3-x1) ; + + //position becomes the + this->array() = pos ; + } + + /* Access to the configuration of the point */ + Config config() const { return config_ ; } ; + + /* Access to the combinatorics */ + const unsigned int* combinatorics() const { return combinatorics_ ; } ; + + /** Gradient computation **/ + /* WARNING : the gradient is filled in the output in COLUMN MAJOR order, + * since this is the default behaviour in Eigen. You can probably work + * around this by #defining the EIGEN_DEFAULT_MATRIX_STORAGE_ORDER_OPTION + * to Eigen::RowMajor, which would be dangerous and ugly. */ + + void derivative( const int comb_index, + const Triangulation* mesh, + const double* sites, + double* output + ) const { + //fill result with zeros + std::fill(output, output+Dim*Dim, 0) ; + + //filter cases + switch(config_) { + case UNDEFINED: { + break ; + } + case ORIGINAL: { + //the vertex only depends on one vertex of the mesh + if(comb_index != 0) break ; + + //if the derivative is requested for this vertex, return identity + for(int i=0; i<Dim; ++i) { + output[Dim*i+i] = 1 ; + } + break ; + } + case EDGE_VERTEX: { + //the vertex only depends on two vertices and two sites + if((comb_index==2) || (comb_index==5)) break ; + + //check whether the derivative is requested wrt a vertex or a site + if(comb_index < 3) { + //derivative wrt a vertex + e_vertex_derivative(comb_index,mesh,sites,output) ; + } else { + //derivative wrt a site + e_site_derivative(comb_index,mesh,sites,output) ; + } + break ; + } + case FACE_VERTEX: { + //check whether the derivative is requested wrt a vertex or a site + if(comb_index < 3) { + //derivative with respect to a vertex + f_vertex_derivative(comb_index,mesh,sites,output) ; + } else { + //derivative with respect to a site + f_site_derivative(comb_index,mesh,sites,output) ; + } + break ; + } + } + } + + private : + + /* Specific gradient computation for every non trivial configuration */ + void e_vertex_derivative( const int comb_index, + const Triangulation* mesh, + const double* sites, + double* output + ) const { + //get the two sites + Eigen::Map<const Vector> v0(sites+Dim*combinatorics_[3]) ; + Eigen::Map<const Vector> v1(sites+Dim*combinatorics_[4]) ; + + //get the two vertices + Eigen::Map<const Vector> x0( + mesh->vertex(combinatorics_[ comb_index ]) + ) ; + Eigen::Map<const Vector> x1( + mesh->vertex(combinatorics_[(comb_index+1)%2]) + ) ; + + //compute the gradient + const Vector edge = x1-x0 ; + const Vector site_diff = v1-v0 ; + const double k = 2*edge.dot(site_diff) ; + const double a = (v0+v1-2*x0).dot(site_diff)/k ; + + Eigen::Map< Eigen::Matrix<double,Dim,Dim> > grad(output) ; + grad = edge*site_diff.transpose() ; + grad.noalias() = 2*(a-1)/k*grad ; + for(int i=0; i<Dim; ++i) { + grad(i,i) += (1-a) ; + } + } + + void e_site_derivative( const int comb_index, + const Triangulation* mesh, + const double* sites, + double* output + ) const { + //get the two sites + Eigen::Map<const Vector> v0( + sites+Dim*combinatorics_[comb_index] + ) ; + Eigen::Map<const Vector> v1( + sites+Dim*combinatorics_[3+((comb_index-2)%2)] + ) ; + + //get the two vertices + Eigen::Map<const Vector> x0(mesh->vertex(combinatorics_[0])) ; + Eigen::Map<const Vector> x1(mesh->vertex(combinatorics_[1])) ; + + //compute the gradient + const Vector edge = x1-x0 ; + const Vector site_diff = v1-v0 ; + const double half_k = edge.dot(site_diff) ; + + Eigen::Map< Eigen::Matrix<double,Dim,Dim> > grad(output) ; + grad = edge*((*this)-v0).transpose() ; + grad.noalias() = grad/half_k ; + } + + void f_vertex_derivative( const int comb_index, + const Triangulation* mesh, + const double* sites, + double* output + ) const { + //get the three sites + Eigen::Map<const Vector> v0(sites+Dim*combinatorics_[3]) ; + Eigen::Map<const Vector> v1(sites+Dim*combinatorics_[4]) ; + Eigen::Map<const Vector> v2(sites+Dim*combinatorics_[5]) ; + + //get the three vertices + Eigen::Map<const Vector> x0( + mesh->vertex(combinatorics_[ comb_index ]) + ) ; + Eigen::Map<const Vector> x1( + mesh->vertex(combinatorics_[(comb_index+1)%3]) + ) ; + Eigen::Map<const Vector> x2( + mesh->vertex(combinatorics_[(comb_index+2)%3]) + ) ; + + //compute the gradient + const double c0 = (x0-x2).dot(v1-v0) ; + const double c1 = (x0-x2).dot(v2-v0) ; + const double c2 = (x2-x1).dot(v1-v0) ; + const double c3 = (x2-x1).dot(v2-v0) ; + + const double b0 = (v0+v1-2*x2).dot(v1-v0) ; + const double b1 = (v0+v2-2*x2).dot(v2-v0) ; + + const double quarter_k = (c0*c3-c2*c1) ; + const double a = (b0*c3-c2*b1)/(2*quarter_k) ; + + //TODO cache the first part of this computation for other gradients + Eigen::Map< Eigen::Matrix<double,Dim,Dim> > grad(output) ; + grad = x0*(x2-x1).transpose() + + x1*(x0-x2).transpose() + + x2*(x1-x0).transpose() ; + grad = grad * ( v0*(v2-v1).transpose() + + v1*(v0-v2).transpose() + + v2*(v1-v0).transpose() ) ; + grad.noalias() = -grad/quarter_k ; + for(int i=0; i<Dim; ++i) { + grad(i,i) += 1 ; + } + grad.noalias() = a*grad ; + } + + void f_site_derivative( const int comb_index, + const Triangulation* mesh, + const double* sites, + double* output + ) const { + //get the three sites + Eigen::Map<const Vector> v0( + sites+Dim*combinatorics_[ comb_index ] + ) ; + Eigen::Map<const Vector> v1( + sites+Dim*combinatorics_[3+((comb_index-2)%3)] + ) ; + Eigen::Map<const Vector> v2( + sites+Dim*combinatorics_[3+((comb_index-1)%3)] + ) ; + + //get the three vertices + Eigen::Map<const Vector> x0(mesh->vertex(combinatorics_[0])) ; + Eigen::Map<const Vector> x1(mesh->vertex(combinatorics_[1])) ; + Eigen::Map<const Vector> x2(mesh->vertex(combinatorics_[2])) ; + + //compute the gradient + const double c0 = (x1-x0).dot(v0-v2) ; + const double c1 = (x2-x0).dot(v0-v2) ; + const double c2 = (x1-x0).dot(v2-v1) ; + const double c3 = (x2-x0).dot(v2-v1) ; + + const double quarter_k = (c0*c3-c2*c1) ; + + Eigen::Map< Eigen::Matrix<double,Dim,Dim> > grad(output) ; + grad = x0*(x2-x1).transpose() + + x1*(x0-x2).transpose() + + x2*(x1-x0).transpose() ; + grad = grad * (v1-v2) * (v0-(*this)).transpose() ; + grad.noalias() = grad/quarter_k ; + } + + /* Configuration */ + Config config_ ; + + /* Sites and vertices involved. The array is arranged as follows depending + * on the verrtex configuration, with the mesh vertices denoted by x_ and + * the sites as v_ : + * - ORIGINAL : [ x0, - , - , - , - , - ] + * - EDGE_VERTEX : [ x0, x1, - , v0, v1, - ] + * - FACE_VERTEX : [ x0, x1, x2, v0, v1, v2 ] + * */ + unsigned int combinatorics_[6] ; +} ; + +//}}} + +/**{{{ Edges **/ + +/* A PolygonEdge encodes the vertex it starts from, its configuration, and + * depending on its configuration, the corresponding symbolic information : + * -- FACE_EDGE : the index of the edge within the face ; + * -- BISECTOR_EDGE : the index of the site beyound the edge */ + +template<typename Triangulation> +class RVDEdge { + + public : + + /* There are two kinds of edges. A FACE_EDGE comes from the initial edges of the + * clipped triangle, and a BISECTOR_EDGE results from the intersection between + * the triangle and a bisector. */ + + enum Config { + FACE_EDGE, + BISECTOR_EDGE + } ; + + /* Configuration */ + Config config ; + + /* Origin Vertex */ + RVDVertex<Triangulation> vertex ; + + /* combinatorics */ + unsigned int combinatorics ; +} ; + +//}}} + +/**{{{ Polygons **/ + +/* Beware that inheriting from stl vector can be dangerous, since the destructor + * of vectors is not declared virtual. This not a problem here, since we do not + * need a destructor, and we do not intend to cas RVDPolygon pointers as vector + * pointers.*/ +template<typename _Triangulation> +class RVDPolygon : public std::vector< RVDEdge<_Triangulation> > { + + public : + + /* Typedefs */ + typedef _Triangulation Triangulation ; + enum { Dim = Triangulation::VertexDim } ; + typedef Eigen::Matrix<double,Dim,1> Vector ; + + /* Empty initialization */ + RVDPolygon() {} ; + + /* Initialization from a triangle of a mesh */ + RVDPolygon( unsigned int triangle, + const Triangulation* mesh + ) { + reset(triangle, mesh) ; + } + + /* Reset to a triangle of a mesh */ + void reset( unsigned int triangle, + const Triangulation* mesh + ) { + //resize to three vertices + (*this).resize(3) ; + + //get the triangle combinatorics from the mesh + const unsigned int* triangle_verts = mesh->face(triangle) ; + + //assign the init_polygon + for(int i=0; i<3; ++i) { + //reset vertex + const unsigned int edge_vertex = triangle_verts[i] ; + (*this)[i].vertex.reset(edge_vertex,mesh) ; + + //assign edge combinatorics as its index within the triangle + (*this)[i].combinatorics = i ; + } + } +} ; + +//}}} + + +} //end of namespace Revoropt + +#endif + diff --git a/contrib/Revoropt/include/Revoropt/RVD/rdt.hpp b/contrib/Revoropt/include/Revoropt/RVD/rdt.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9f0b801b166c4a05a15182ba33a6229d2a687be9 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/RVD/rdt.hpp @@ -0,0 +1,91 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_RVD_RDT_HPP_ +#define _REVOROPT_RVD_RDT_HPP_ + +#include <unordered_set> +#include <iostream> + +#include "rvd.hpp" +#include "polygon.hpp" +#include <Revoropt/Tools/combinatorics.hpp> +#include <Revoropt/Mesh/builder_def.hpp> + +namespace Revoropt { + +/* Build a restricted Delaunay triangulation */ + +template<typename _Triangulation> +class RDTBuilder : public Action<_Triangulation> { + + public : + + /* Types and template data */ + typedef _Triangulation Triangulation ; + enum { Dim = Triangulation::VertexDim } ; + typedef Eigen::Matrix<double,Dim,1> Vector ; + typedef tuplekey<3> Triangle ; + typedef tuple_hash<3> hasher ; + typedef RVDVertex<Triangulation> Vertex ; + + RDTBuilder( std::vector<unsigned int>& output_triangles + ) : triangles_(output_triangles) { + } + + /* Reset */ + void reset() { + built_triangles_.clear() ; + } + + /* Action for to use on the RVD */ + void operator()( unsigned int site, + unsigned int triangle, + const RVDPolygon<Triangulation>& polygon + ) { + //size of the polygon + unsigned int size = polygon.size() ; + if(size<3) return ; + + //build and add the triangles + for(unsigned int i=0; i<size; ++i) { + const RVDVertex<Triangulation>& v = polygon[i].vertex ; + if(v.config() == Vertex::FACE_VERTEX) { + unsigned int v1 = v.combinatorics()[3] ; + unsigned int v2 = v.combinatorics()[4] ; + unsigned int v3 = v.combinatorics()[5] ; + + Triangle key(v1,v2,v3) ; + + std::unordered_set<Triangle, hasher>::iterator it ; + it = built_triangles_.find(key) ; + if(it == built_triangles_.end()) { + triangles_.push_back(v1) ; + triangles_.push_back(v2) ; + triangles_.push_back(v3) ; + built_triangles_.insert(key) ; + } + } + } + } + + private : + + /* Structure to avoid creating twice the same triangle */ + std::unordered_set<Triangle, hasher> built_triangles_ ; + + /* computed triangles are stored in a vector*/ + std::vector<unsigned int>& triangles_ ; + +} ; + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/RVD/rvd.hpp b/contrib/Revoropt/include/Revoropt/RVD/rvd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..81512dfd09b2a214e8cf05cd477531bd90dd9abc --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/RVD/rvd.hpp @@ -0,0 +1,364 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_RVD_HPP_ +#define _REVOROPT_RVD_HPP_ + +#include "polygon.hpp" +#include "nn_clipper.hpp" +#include "action.hpp" + +#include <Revoropt/Tools/combinatorics.hpp> +#include <Revoropt/Tools/hash.hpp> + +#ifdef USE_ANN + #include "ann_backend.hpp" +#else + #include "flann_backend.hpp" +#endif + +#include <eigen3/Eigen/Dense> +#include <vector> +#include <stack> +#include <unordered_map> +#include <iostream> + +namespace Revoropt { + +/** Restricted Voronoi Diagram computer **/ + +template<typename _Triangulation> +class RVD { + + public : + + /* Types and template data */ + typedef _Triangulation Triangulation ; + enum { Dim = Triangulation::VertexDim } ; + typedef Eigen::Matrix<double,Dim,1> Vector ; + + typedef RVDEdge<Triangulation> Edge ; + typedef RVDEdge<Triangulation> Vertex ; + + /* Initialization */ + + RVD() : mesh_(NULL), clipper_owned_(false), clipper_(NULL) {} ; + + ~RVD() { + if(clipper_owned_) { + delete clipper_ ; + } + } + + template<typename _Clipper> + void set_clipper( _Clipper* clipper ) { + //cleanup if necessary + if(clipper_ != NULL && clipper_owned_) { + delete clipper_ ; + } + clipper_owned_ = false ; + clipper_ = clipper ; + } + + void set_default_clipper() { +#ifdef USE_ANN + typedef NNClipper< Triangulation,ANNBackend<Dim> > DefaultClipper ; +#else + typedef NNClipper< Triangulation,FLANNBackend<Dim> > DefaultClipper ; +#endif + //cleanup if necessary + if(clipper_ != NULL && clipper_owned_) { + delete clipper_ ; + } + //allocate + DefaultClipper* clipper = new DefaultClipper ; + //parameters + clipper->set_neighbourhood_size(15) ; + clipper->set_algorithm(DefaultClipper::CORNER_CLIPPING) ; + //mark the clipper as owned + clipper_owned_ = true ; + //assign the clipper + clipper_ = clipper ; + } + + void set_mesh( const Triangulation* mesh ) { + mesh_ = mesh ; + if(clipper_ == NULL) { + set_default_clipper() ; + } + clipper_->set_mesh(mesh) ; + } + + void set_sites( const double* sites, unsigned int sites_size ) { + if(clipper_ == NULL) { + set_default_clipper() ; + } + clipper_->set_sites(sites, sites_size) ; + } + + /* Access */ + + const double* sites() const { return clipper_->sites() ; } + + unsigned int sites_size() const { return clipper_->sites_size() ; } + + const Triangulation* mesh() const { return clipper_->mesh() ; } + + /* Computing the RVD and applying an action on each polygon */ + + template<typename Action_T> + void compute(Action_T& action) { + //reset markers for triangles + triangle_stacked_.assign(mesh_->faces_size(),false) ; + + //reset clipper + clipper_->reset() ; + + //create the inner loop data + const unsigned int site_size = clipper_->sites_size() ; + //assign last triangle for each site to NO_NEIGHBOUR, therefore no triangle. + site_last_triangle_.assign(site_size,Triangulation::NO_NEIGHBOUR) ; + + //loop over the connected components + for( unsigned int init_triangle = 0; + init_triangle<mesh_->faces_size(); + ++init_triangle + ) { + //check that the triangle was not previously treated + if(!triangle_stacked_[init_triangle]) { + //std::cout << "Found a connected component" << std::endl ; + //find a site to initialize the propagation + unsigned int outer_init_site = get_init_site(init_triangle) ; + + //std::cout << "Initializing site is " << outer_init_site << std::endl ; + + //initialize the outer loop + Couple init_couple(init_triangle,outer_init_site) ; + triangle_stacked_[init_triangle] = true ; + triangle_stack_.push(init_couple) ; + + //launch the outer loop + while(!triangle_stack_.empty()) { + //get a triangle and a site + unsigned int current_triangle = triangle_stack_.top().first ; + unsigned int inner_init_site = triangle_stack_.top().second ; + triangle_stack_.pop() ; + + //std::cout << "handling triangle " << current_triangle << std::endl ; + + //initialize the inner loop + site_stack_.push(inner_init_site) ; + site_last_triangle_[inner_init_site] = current_triangle ; + + //launch the inner loop + while(!site_stack_.empty()) { + //get a site to clip thetriangle + unsigned int current_site = site_stack_.top() ; + site_stack_.pop() ; + + //Create a polygon corresponding to the face and clip it + RVDPolygon<Triangulation> face_polygon( + current_triangle, mesh_ + ) ; + clipper_->clip_by_cell( + face_polygon, current_triangle, current_site + ) ; + + //apply the action + action(current_site, current_triangle, face_polygon) ; + + //propagate the computation from the edges of curr_polygon_ + propagate(face_polygon, current_triangle, current_site) ; + }//end inner loop + }//end outer loop on triangle--site couples + } + } //end of loop over connected components + + //call finalize method if it exists + wrap_finalize(action) ; + } + + private : + + /** Data for the outer loop **/ + + typedef std::pair<unsigned int,unsigned int> Couple ; + + /* The triangle_stack_ contains mesh triangle candidates for the splitting, + * with a site to initialize the splitting, done in an inner loop using the + * site_stack_. A mesh triangle shall not be added twice to this stack.*/ + std::stack<Couple> triangle_stack_ ; + + /* A boolean encodes for each mesh triangle if it was previously stacked in + * the triangle_stack_, to ensure that a mesh triangle is only stacked once in + * this stack. */ + std::vector<bool> triangle_stacked_ ; + + /** Data for the inner loop **/ + + /* The site_stack_ contains new candidate sites for the splitting of a given + * triangle of the mesh. This stack is used in a inner loop to split a mesh + * triangle betwen Voronoi cells. A site shall not be added twice to this + * stack. */ + std::stack<unsigned int> site_stack_ ; + + + /* The last mesh triangle clipped for each site is stored, to ensure that a + * site is not added twice in the site_stack_. */ + std::vector<unsigned int> site_last_triangle_ ; + + /** Initialization helpers for the compute method **/ + + /* Get the nearest site from the centroid of a triangle */ + unsigned int get_init_site( unsigned int triangle ) { + //compute the centroid of thetriangle + const unsigned int* triangle_verts = mesh_->face(triangle) ; + Eigen::Map<const Vector> x1(mesh_->vertex(triangle_verts[0])) ; + Eigen::Map<const Vector> x2(mesh_->vertex(triangle_verts[1])) ; + Eigen::Map<const Vector> x3(mesh_->vertex(triangle_verts[2])) ; + + const Vector g = (x1+x2+x3)/3. ; + + //get the site nearest to the centroid + unsigned int nearest_site = clipper_->nearest_site(g.data()) ; + return nearest_site ; + } + + /** Propagation of the computations **/ + + void propagate( RVDPolygon<Triangulation>& polygon, + unsigned int triangle, + unsigned int site + ) { + //get triangles neighbouring the triangle + const unsigned int* triangle_neighbours = + mesh_->face_neighbours(triangle) ; + + //iterate over the edges of curr_polygon + for(unsigned int i = 0; i<polygon.size(); ++i) { + //get the combinatorics of the edge + unsigned int combinatorics = polygon[i].combinatorics ; + if(polygon[i].config == Edge::BISECTOR_EDGE) { + //the edge is the intersection between the triangle and a bisector + //propagate the neighbouring site for the current triangle + if(site_last_triangle_[combinatorics] != triangle) { + //the neighbouring site has not been stacked for this triangle yet + site_last_triangle_[combinatorics] = triangle ; + site_stack_.push(combinatorics) ; + + //std::cout << "Propagating site " << combinatorics << std::endl ; + } + } else { + //the edge is a portion of an original edge of the triangle + //propagate the neighbouring triangle with the same site + unsigned int neighbour_triangle = triangle_neighbours[combinatorics] ; + if( neighbour_triangle != Triangulation::NO_NEIGHBOUR + && !triangle_stacked_[neighbour_triangle] + ) { + //the triangle has not been stacked yet + triangle_stacked_[neighbour_triangle] = true ; + triangle_stack_.push(Couple(neighbour_triangle, site)) ; + + //std::cout << "Propagating triangle " << neighbour_triangle << std::endl ; + } + } + } + //std::cout << "Done propagating." << std::endl ; + } + + /** Mesh **/ + const Triangulation* mesh_ ; + + /** Clipper **/ + bool clipper_owned_ ; + Clipper<Triangulation>* clipper_ ; + +} ; + +/* Store a restricted Voronoi diagram */ + +template<typename Triangulation, int _OutDim = Triangulation::VertexDim> +class RVDStore { + + //typedefs + enum { Dim = Triangulation::VertexDim, OutDim = _OutDim } ; + typedef typename Triangulation::Scalar Scalar ; + typedef tuplekey<3> CombVertexPart ; + typedef tuple_hash<3, unsigned int> CVPHasher ; + typedef std::pair<CombVertexPart,CombVertexPart> CombVertex ; + + class CVHasher { + public : + std::size_t operator()( const CombVertex& comb_vertex ) const { + std::size_t seed = 0 ; + hash_combine<CombVertexPart, CVPHasher>(seed, comb_vertex.first) ; + hash_combine<CombVertexPart, CVPHasher>(seed, comb_vertex.second) ; + return seed ; + } + } ; + + public : + + void operator()( unsigned int site, unsigned int triangle, + const RVDPolygon<Triangulation>& polygon + ) { + unsigned int size = polygon.size() ; + + //collect the vertex indices of the polygon vertices + std::vector<unsigned int> vert_indices(size) ; + + for(unsigned int vertex = 0; vertex < size; ++vertex) { + //Combinatorial vertex + const RVDVertex<Triangulation>& v = polygon[vertex].vertex ; + //build the key to merge the vertex with any combinatorial equivalent + CombVertexPart cv_part_1(v.combinatorics()) ; + CombVertexPart cv_part_2(v.combinatorics()+3) ; + CombVertex key(cv_part_1,cv_part_2) ; + //check whether the combinatorics of this vertex is already existing + vmap_iterator it = vmap.find(key) ; + if( it == vmap.end() ) { + //no such combinatorics in the map + //add the vertex to the final mesh + vert_indices[vertex] = rvd_mesh.push_vertex(v.data()) ; + //associate the index with the combinatorics in the map + vmap[key] = vert_indices[vertex] ; + //store the combinatorics of the vertex + vertex_combinatorics.push_back(key) ; + } else { + //the combinatorics was previously handled, recover the index + vert_indices[vertex] = it->second ; + } + } + + //add the polygon to the mesh + rvd_mesh.push_face(vert_indices.data(), size) ; + //store the polygon site + face_sites.push_back(site) ; + } + + //collecting and merging RVD vertices + std::unordered_map<CombVertex, unsigned int, CVHasher> vmap ; + typedef typename std::unordered_map< + CombVertex, + unsigned int, + CVHasher + >::iterator vmap_iterator ; + //output mesh + MeshBuilder<Variable,OutDim,Scalar> rvd_mesh ; + //recall the site index for every face + std::vector<unsigned int> face_sites ; + //recall the combinatorics for every vertex + std::vector<CombVertex> vertex_combinatorics ; +} ; + + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Solver/.gitignore b/contrib/Revoropt/include/Revoropt/Solver/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..6e7133dae36877b13f8788676274b9e0abdc1c32 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Solver/.gitignore @@ -0,0 +1 @@ +wrapper.os diff --git a/contrib/Revoropt/include/Revoropt/Solver/alglib_cg.hpp b/contrib/Revoropt/include/Revoropt/Solver/alglib_cg.hpp new file mode 100644 index 0000000000000000000000000000000000000000..63f92519cb47ab3627617c0b1d69b7b37fe718a0 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Solver/alglib_cg.hpp @@ -0,0 +1,225 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_SOLVER_ALGLIB_CG_WRAPPER_H_ +#define _REVOROPT_SOLVER_ALGLIB_CG_WRAPPER_H_ + +#include <libalglib/optimization.h> + +namespace Revoropt { +namespace Solver { + +/** Solver encapsulation **/ + +class AlgCG { + + public : + + /* Types */ + + /** Solver parameters **/ + class SolverParams { + + public : + + SolverParams() : epsg(0), epsf(0), epsx(0), + max_iterations(10) {} + + double epsg ; + double epsf ; + double epsx ; + alglib::ae_int_t max_iterations ; + } ; + + /** Iteration data **/ + + typedef struct { + const double* x ; //current variable positions + const unsigned int n ; //size of x and g + const double fx ; //objective function value + const unsigned int k ; //current iteration count + } IterData ; + + /** Objective function data **/ + + typedef struct { + const double* x ; //current variable positions + const unsigned int n ; //size of x and g + double* g ; //gradient + } EvalData ; + + /* Solver interface */ + template<typename ObjFun> + int solve( double* x, unsigned int size, + ObjFun* objfun, + unsigned int n_iter + ) { + //setup + iteration_ = 0 ; + variables_.setcontent(size,x) ; + objfun_ = objfun ; + solver_param.max_iterations = n_iter ; + + //build solver and solve + alglib::mincgcreate(variables_, state_) ; + alglib::mincgsetcond( state_, + solver_param.epsg, + solver_param.epsf, + solver_param.epsx, + solver_param.max_iterations + ) ; + solve_wrapper<ObjFun, void>(this) ; + + //copy the result back + alglib::mincgreport report ; + alglib::mincgresults(state_, variables_, report) ; + std::copy( variables_.getcontent(), + variables_.getcontent() + variables_.length(), + x + ) ; + return report.terminationtype ; + } + + template<typename ObjFun, typename IterCallback> + int solve( double* x, unsigned int size, + ObjFun* objfun, + unsigned int n_iter, + IterCallback* callback + ) { + //setup + iteration_ = 0 ; + variables_.setcontent(size,x) ; + objfun_ = objfun ; + iter_callback_ = callback ; + solver_param.max_iterations = n_iter ; + + //build solver and solve + alglib::mincgcreate(variables_, state_) ; + alglib::mincgsetcond( state_, + solver_param.epsg, + solver_param.epsf, + solver_param.epsx, + solver_param.max_iterations + ) ; + alglib::mincgsetxrep(state_, true) ; + solve_wrapper<ObjFun, IterCallback>(this) ; + + //copy the result back + alglib::mincgreport report ; + alglib::mincgresults(state_, variables_, report) ; + std::copy( variables_.getcontent(), + variables_.getcontent() + variables_.length(), + x + ) ; + return report.terminationtype ; + } + + /* Solver parameters */ + + SolverParams solver_param ; + + private : + + /* State */ + alglib::mincgstate state_ ; + unsigned int iteration_ ; + + /* Variables */ + + alglib::real_1d_array variables_ ; + + /* Callbacks */ + + void* objfun_ ; + void* iter_callback_ ; + + /* Functions to be passed to lbfgs */ + template<typename ObjFun, typename IterCallback> + static void solve_wrapper( AlgCG* instance ) ; + + template<typename IterCallback> + static void iter_wrapper( const alglib::real_1d_array& x, + double f, + void* instance + ) ; + + template<typename ObjFun> + static void eval_wrapper( const alglib::real_1d_array& x, + double& f, + alglib::real_1d_array& grad, + void* instance + ) ; + +} ; + +template<typename ObjFun, typename IterCallback> +void AlgCG::solve_wrapper( AlgCG* instance ) { + //solve + return alglib::mincgoptimize( instance->state_, + AlgCG::eval_wrapper<ObjFun>, + AlgCG::iter_wrapper<IterCallback>, + instance + ) ; +} + +template<typename IterCallback> +void AlgCG::iter_wrapper( const alglib::real_1d_array& x, + double f, + void* instance + ) { + //the solver context is provided as the instance + AlgCG* s = (AlgCG*) instance ; + + //the iteration callback is a member + IterCallback* callback = (IterCallback*) s->iter_callback_ ; + + //call it + AlgCG::IterData data = { + x.getcontent(), + (unsigned int)x.length(), + f, + ++s->iteration_ + } ; + (*callback)(&data) ; +} + +template<> +void AlgCG::iter_wrapper<void>( const alglib::real_1d_array& x, + double f, + void* instance + ) { +} + +template<typename ObjFun> +void AlgCG::eval_wrapper( const alglib::real_1d_array& x, + double& f, + alglib::real_1d_array& grad, + void* instance + ) { + //the solver context is provided as the instance + AlgCG* s = (AlgCG*) instance ; + + //the objective function is a member + ObjFun* objfun = (ObjFun*) s->objfun_ ; + + //call the callback + AlgCG::EvalData data = { + x.getcontent(), + (unsigned int)x.length(), + grad.getcontent() + } ; + f = (*objfun)(&data) ; +} + +} //end of namespace Solver +} //end of namespace Revoropt + +#endif + diff --git a/contrib/Revoropt/include/Revoropt/Solver/alglib_lbfgs.hpp b/contrib/Revoropt/include/Revoropt/Solver/alglib_lbfgs.hpp new file mode 100644 index 0000000000000000000000000000000000000000..125fb1d02e48c1f524dd26827752fccf9ea8f304 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Solver/alglib_lbfgs.hpp @@ -0,0 +1,227 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_SOLVER_ALGLIB_LBFGS_WRAPPER_HPP_ +#define _REVOROPT_SOLVER_ALGLIB_LBFGS_WRAPPER_HPP_ + +//#include <libalglib/optimization.h> +#include <optimization.h> + +namespace Revoropt { +namespace Solver { + +/** Solver encapsulation **/ + +class AlgLBFGS { + + public : + + /* Types */ + + /** Solver parameters **/ + class SolverParams { + + public : + + SolverParams() : epsg(0), epsf(0), epsx(0), + update_size(5), max_iterations(10) {} + + double epsg ; + double epsf ; + double epsx ; + unsigned int update_size ; + alglib::ae_int_t max_iterations ; + } ; + + /** Iteration data **/ + + typedef struct { + const double* x ; //current variable positions + const unsigned int n ; //size of x and g + const double fx ; //objective function value + const unsigned int k ; //current iteration count + } IterData ; + + /** Objective function data **/ + + typedef struct { + const double* x ; //current variable positions + const unsigned int n ; //size of x and g + double* g ; //gradient + } EvalData ; + + /* Solver interface */ + template<typename ObjFun> + int solve( double* x, unsigned int size, + ObjFun* objfun, + unsigned int n_iter + ) { + //setup + iteration_ = 0 ; + variables_.setcontent(size,x) ; + objfun_ = objfun ; + solver_param.max_iterations = n_iter ; + + //build solver and solve + alglib::minlbfgscreate(solver_param.update_size, variables_, state_) ; + alglib::minlbfgssetcond( state_, + solver_param.epsg, + solver_param.epsf, + solver_param.epsx, + solver_param.max_iterations + ) ; + solve_wrapper<ObjFun, void>(this) ; + + //copy the result back + alglib::minlbfgsreport report ; + alglib::minlbfgsresults(state_, variables_, report) ; + std::copy( variables_.getcontent(), + variables_.getcontent() + variables_.length(), + x + ) ; + return report.terminationtype ; + } + + template<typename ObjFun, typename IterCallback> + int solve( double* x, unsigned int size, + ObjFun* objfun, + unsigned int n_iter, + IterCallback* callback + ) { + //setup + iteration_ = 0 ; + variables_.setcontent(size,x) ; + objfun_ = objfun ; + iter_callback_ = callback ; + solver_param.max_iterations = n_iter ; + + alglib::minlbfgsreport report ; + + //build solver and solve + alglib::minlbfgscreate(solver_param.update_size, variables_, state_) ; + alglib::minlbfgssetcond( state_, + solver_param.epsg, + solver_param.epsf, + solver_param.epsx, + solver_param.max_iterations + ) ; + alglib::minlbfgssetxrep(state_, true) ; + solve_wrapper<ObjFun, IterCallback>(this) ; + + //copy the result back + alglib::minlbfgsresults(state_, variables_, report) ; + std::copy( variables_.getcontent(), + variables_.getcontent() + variables_.length(), + x + ) ; + return report.terminationtype ; + } + + /* Solver parameters */ + + SolverParams solver_param ; + + private : + + /* State */ + alglib::minlbfgsstate state_ ; + unsigned int iteration_ ; + + /* Variables */ + + alglib::real_1d_array variables_ ; + + /* Callbacks */ + + void* objfun_ ; + void* iter_callback_ ; + + /* Functions to be passed to lbfgs */ + template<typename ObjFun, typename IterCallback> + static void solve_wrapper( AlgLBFGS* instance ) ; + + template<typename IterCallback> + static void iter_wrapper( const alglib::real_1d_array& x, + double f, + void* instance + ) ; + + template<typename ObjFun> + static void eval_wrapper( const alglib::real_1d_array& x, + double& f, + alglib::real_1d_array& grad, + void* instance + ) ; + +} ; + +template<typename ObjFun, typename IterCallback> +void AlgLBFGS::solve_wrapper( AlgLBFGS* instance ) { + //solve + return alglib::minlbfgsoptimize( instance->state_, + AlgLBFGS::eval_wrapper<ObjFun>, + AlgLBFGS::iter_wrapper<IterCallback>, + instance + ) ; +} + +template<typename IterCallback> +void AlgLBFGS::iter_wrapper( const alglib::real_1d_array& x, + double f, + void* instance + ) { + //the solver context is provided as the instance + AlgLBFGS* s = (AlgLBFGS*) instance ; + + //the iteration callback is a member + IterCallback* callback = (IterCallback*) s->iter_callback_ ; + + //call it + AlgLBFGS::IterData data = { + x.getcontent(), + (unsigned int)x.length(), + f, + ++s->iteration_ + } ; + (*callback)(&data) ; +} + +template<> +void AlgLBFGS::iter_wrapper<void>( const alglib::real_1d_array& x, + double f, + void* instance + ) { +} + +template<typename ObjFun> +void AlgLBFGS::eval_wrapper( const alglib::real_1d_array& x, + double& f, + alglib::real_1d_array& grad, + void* instance + ) { + //the solver context is provided as the instance + AlgLBFGS* s = (AlgLBFGS*) instance ; + + //the objective function is a member + ObjFun* objfun = (ObjFun*) s->objfun_ ; + + //call the callback + AlgLBFGS::EvalData data = { + x.getcontent(), + (unsigned int)x.length(), + grad.getcontent() + } ; + f = (*objfun)(&data) ; +} + +} //end of namespace Solver +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Solver/lbfgs.hpp b/contrib/Revoropt/include/Revoropt/Solver/lbfgs.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c4704499ee8228b111f7878efc57fef83eebbeef --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Solver/lbfgs.hpp @@ -0,0 +1,205 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_SOLVER_LBFGS_WRAPPER_H_ +#define _REVOROPT_SOLVER_LBFGS_WRAPPER_H_ + +#include <lbfgs.h> + +namespace Revoropt { +namespace Solver { + +/** Solver encapsulation **/ + +class LBFGS { + + public : + + /* Types */ + + typedef lbfgs_parameter_t SolverParams ; + + /** Iteration callback data **/ + + typedef struct { + const double* x ; //current variable positions + const double* g ; //current gradient + const double fx ; //current value of the objective function + const double xnorm ; //norm of x + const double gnorm ; //norm of g + const double step ; //current step length + int n ; //size of x and g + int k ; //current iteration count + int ls ; //number of evaluations required for last iteration + } IterData ; + + /** Objective function data **/ + + typedef struct { + const double* x ; //current variable positions + double* g ; //output here the gradient of the objective function + const int n ; //size of x and g + double step ; //current step length + } EvalData ; + + /* Initialization */ + + LBFGS() { + lbfgs_parameter_init(&solver_param) ; + solver_param.epsilon = 0 ; + //solver_param.linesearch = LBFGS_LINESEARCH_BACKTRACKING_ARMIJO ; + solver_param.max_linesearch = 20 ; + } + + /* Solver interface */ + + template<typename ObjFun> + int solve( double* x, unsigned int size, + ObjFun* objfun, + unsigned int n_iter + ) { + variables_ = x ; + variables_size_ = size ; + objfun_ = objfun ; + solver_param.max_iterations = n_iter ; + return solve_wrapper<ObjFun, void>(this) ; + } + + template<typename ObjFun, typename IterCallback> + int solve( double* x, unsigned int size, + ObjFun* objfun, + unsigned int n_iter, + IterCallback* callback + ) { + variables_ = x ; + variables_size_ = size ; + objfun_ = objfun ; + solver_param.max_iterations = n_iter ; + iter_callback_ = callback ; + return solve_wrapper<ObjFun, IterCallback>(this) ; + } + + /* Solver parameters */ + + SolverParams solver_param ; + + private : + + /* Variables */ + double* variables_ ; + unsigned int variables_size_ ; + + /* Callbacks */ + + void* objfun_ ; + void* iter_callback_ ; + + /* Functions to be passed to lbfgs */ + template<typename ObjFun, typename IterCallback> + static int solve_wrapper( LBFGS* instance ) ; + + template<typename IterCallback> + static int iter_wrapper( void* instance, + const double* x, + const double* g, + const double fx, + const double xnorm, + const double gnorm, + const double step, + int n, + int k, + int ls + ) ; + + template<typename ObjFun> + static double eval_wrapper( void* instance, + const double* x, + double* g, + const int n, + const double step + ) ; + +} ; + +template<typename ObjFun, typename IterCallback> +int LBFGS::solve_wrapper( LBFGS* instance ) { + //solve + return lbfgs( instance->variables_size_, + instance->variables_, + NULL, + LBFGS::eval_wrapper<ObjFun>, + LBFGS::iter_wrapper<IterCallback>, + instance, + &(instance->solver_param) + ) ; +} + +template<typename IterCallback> +int LBFGS::iter_wrapper( void* instance, + const double* x, + const double* g, + const double fx, + const double xnorm, + const double gnorm, + const double step, + int n, + int k, + int ls + ) { + //the solver context is provided as the instance + LBFGS* s = (LBFGS*) instance ; + + //the iteration callback is a member + IterCallback* callback = (IterCallback*) s->iter_callback_ ; + + //call it + LBFGS::IterData data = {x, g, fx, xnorm, gnorm, step, n, k, ls} ; + return (*callback)(&data) ; +} + +template<> +int LBFGS::iter_wrapper<void>( void* instance, + const double* x, + const double* g, + const double fx, + const double xnorm, + const double gnorm, + const double step, + int n, + int k, + int ls + ) { + return 0 ; +} + +template<typename ObjFun> +double LBFGS::eval_wrapper( void* instance, + const double* x, + double* g, + const int n, + const double step + ) { + //the solver context is provided as the instance + LBFGS* s = (LBFGS*) instance ; + + //the objective function is a member + ObjFun* objfun = (ObjFun*) s->objfun_ ; + + //call the callback + LBFGS::EvalData data = {x, g, n, step} ; + const double result = (*objfun)(&data) ; + return result ; +} + +} //end of namespace Solver +} //end of namespace Revoropt + +#endif + diff --git a/contrib/Revoropt/include/Revoropt/Tools/.gitignore b/contrib/Revoropt/include/Revoropt/Tools/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..14ec01aa8f8b5467c00b49e07600ae6180f8fa6b --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Tools/.gitignore @@ -0,0 +1 @@ +disjoint_sets.os diff --git a/contrib/Revoropt/include/Revoropt/Tools/color.hpp b/contrib/Revoropt/include/Revoropt/Tools/color.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e801d67caf3d33d590c72a9212ef72e6b670c6a8 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Tools/color.hpp @@ -0,0 +1,73 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_TOOLS_COLOR_HPP_ +#define _REVOROPT_TOOLS_COLOR_HPP_ + +#include <cstdlib> + +namespace Revoropt { + +//generate random bright colors +template<typename Scalar> +void random_bright_color(Scalar* output) { + //random hsv with value 1 + Scalar h = (Scalar) rand() / (Scalar) RAND_MAX ; + h *= 6 ; + Scalar s = (Scalar) rand() / (Scalar) RAND_MAX ; + s = 0.5*s + 0.5 ; + Scalar v = (Scalar) rand() / (Scalar) RAND_MAX ; + v = 0.5*v+0.5 ; + + int hi = (int) h ; + Scalar hr = h - hi ; + + Scalar l = v*(1-s) ; + Scalar m = v*(1-hr*s); + Scalar n = v*(1-(1-hr)*s); + + if(h<1) { + output[0] = v ; + output[1] = n ; + output[2] = l ; + } else if(h<2) { + output[0] = m ; + output[1] = v ; + output[2] = l ; + } else if(h<3) { + output[0] = l ; + output[1] = v ; + output[2] = n ; + } else if(h<4) { + output[0] = l ; + output[1] = m ; + output[2] = v ; + } else if(h<5) { + output[0] = n ; + output[1] = l ; + output[2] = v ; + } else { + output[0] = v ; + output[1] = l ; + output[2] = m ; + } +} + +//generate bright colormaps +template<typename Scalar> +void generate_bright_colormap(unsigned int size, Scalar* output) { + for(unsigned int i = 0; i < size; ++i) { + random_bright_color(output + 3*i) ; + } +} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Tools/combinatorics.hpp b/contrib/Revoropt/include/Revoropt/Tools/combinatorics.hpp new file mode 100644 index 0000000000000000000000000000000000000000..cec40053601b657ecfd82fb7aca2453ae6a1ddd6 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Tools/combinatorics.hpp @@ -0,0 +1,201 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_TOOLS_COMBINATORICS_HPP_ +#define _REVOROPT_TOOLS_COMBINATORICS_HPP_ + +#include "meta.hpp" +#include "hash.hpp" + +#include <algorithm> +#include <iostream> +#include <type_traits> +#include <functional> + +namespace Revoropt { + +namespace tuplekey_impl { + +//Dummy class and operator << to declare a friend function for +//tuplekey_base when the provided type has no operator<< + +struct dummy {} ; + +template<int size, typename T> +std::ostream& operator<<(std::ostream&, const dummy&) ; + +//Forward declarations + +template<int size, typename T> +class tuplekey_base ; + +template<int size, typename T> +typename std::enable_if<is_printable<T>::value,std::ostream>::type& operator<<( + std::ostream&, const tuplekey_base<size,T>& +) ; + +//Base class for tuple keys (elements in the tuple are sorted) +template<int _size, typename _T> +class tuplekey_base { + public : + + /* Typedefs */ + enum { size = _size } ; + typedef _T T ; + + /* Construction */ + tuplekey_base() {} ; //only here to allocate arrays of tuplekeys + + tuplekey_base(const T data[size]) { + std::copy(data, data+size, vals) ; + std::sort(vals, vals+size) ; + } + + /* Printing if operator<< is defined for T */ + friend std::ostream& operator<<<size,T>( + std::ostream& os, + const typename std::conditional< + is_printable<T>::value, + tuplekey_base<size,T>, + dummy + >::type& key + ) ; + + /* Comparison */ + bool operator<(const tuplekey_base& rhs) const { + for(unsigned int i = 0; i < size; ++i) { + if((vals[i] < rhs.vals[i]) || (rhs.vals[i] < vals[i])) { + return vals[i] < rhs.vals[i] ; + } + } + return false ; + } + + bool operator==(const tuplekey_base& rhs) const { + return !((rhs < *this) || (*this < rhs)) ; + } + + /* Values */ + const T& operator[]( unsigned int i ) const { return vals[i] ; } + + const T* values() const { return vals ; } + + T vals[size] ; +} ; + + +/* Printing tuples */ +template<int size, typename T> +typename std::enable_if<is_printable<T>::value,std::ostream>::type& operator<<( + std::ostream& os, + const tuplekey_base<size,T>& key +) { + os << "[ " ; + for(int i = 0; i < size; ++i) { + os << key.vals[i] << " " ; + } + os << "]" ; +} + +} //end of namespace tuplekey_impl + + +/* General tuple key (for any number of elements) */ +template<int size, typename T=unsigned int> +class tuplekey : public tuplekey_impl::tuplekey_base<size,T> { + typedef tuplekey_impl::tuplekey_base<size,T> Base ; + public: + tuplekey() : Base() {} + tuplekey(const T data[size]) : Base(data) {} +} ; + +/* Specialization for couples */ +template<typename T> +class tuplekey<2,T> : public tuplekey_impl::tuplekey_base<2,T> { + /* Typedefs */ + typedef tuplekey_impl::tuplekey_base<2,T> Base ; + using Base::vals ; + + public: + + /* Basic construction */ + tuplekey() : Base() {} + tuplekey(const T data[2]) : Base(data) {} + + /* Construction from a pair of values */ + tuplekey(const T &v1, const T &v2) { + vals[0] = v1 < v2 ? v1 : v2 ; + vals[1] = v1 < v2 ? v2 : v1 ; + } + + /* Values */ + const T& min() const { return vals[0] ; } + const T& max() const { return vals[1] ; } + +} ; + +/* Specialization for triples */ +template<typename T> +class tuplekey<3,T> : public tuplekey_impl::tuplekey_base<3,T> { + /* Typedefs */ + typedef tuplekey_impl::tuplekey_base<3,T> Base ; + using Base::vals ; + + public: + + /* Basic construction */ + tuplekey() : Base() {} + tuplekey(const T data[3]) : Base(data) {} + + /* Construction from a triplet of values */ + tuplekey(const T &v1, const T &v2, const T &v3) { + if(v1 < v2) { + vals[0] = v1 ; + vals[1] = v2 ; + } else { + vals[0] = v2 ; + vals[1] = v1 ; + } + if(vals[1] < v3) { + vals[2] = v3 ; + } else if (vals[0] < v3) { + vals[2] = vals[1] ; + vals[1] = v3 ; + } else { + vals[2] = vals[1] ; + vals[1] = vals[0] ; + vals[0] = v3 ; + } + } + + /* Values */ + const T& min() const { return vals[0] ; } + const T& mid() const { return vals[1] ; } + const T& max() const { return vals[2] ; } +} ; + +/* Hashing */ + +template <int size, typename T = unsigned int> +class tuple_hash { + public : + + std::size_t operator()( const tuplekey<size, T>& tuple ) const { + std::size_t seed = 0 ; + for(int i = 0; i < size; ++i) { + hash_combine(seed, tuple[i]) ; + } + return seed ; + } +} ; + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Tools/command_line.h b/contrib/Revoropt/include/Revoropt/Tools/command_line.h new file mode 100644 index 0000000000000000000000000000000000000000..e12f96a85d5355d9789d219c115fb7e4b249a3b5 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Tools/command_line.h @@ -0,0 +1,60 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt + +#ifndef _REVOROPT_COMMAND_LINE_H_ +#define _REVOROPT_COMMAND_LINE_H_ + +#include <algorithm> +#include <string> +#include <sstream> + +namespace Revoropt { + +char* get_cmd_option( char ** begin, char ** end, + const std::string & option + ) { + char ** itr = std::find(begin, end, "--" + option); + if (itr != end && ++itr != end) + { + return *itr; + } + return 0; +} + +bool cmd_option_exists( char** begin, char** end, + const std::string& option + ) { + return std::find(begin, end, "--" + option) != end; +} + +template<typename T> +bool get_cmd_value( + char ** begin, + char** end, + const std::string& option, + T* output + ) { + if(cmd_option_exists(begin, end, option)) { + std::stringstream ss ; + ss << get_cmd_option(begin, end, option) ; + T t ; + ss >> t ; + if(!ss.fail()) { + *output = t ; + return true ; + } + } + return false ; +} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Tools/container_wrapper.hpp b/contrib/Revoropt/include/Revoropt/Tools/container_wrapper.hpp new file mode 100644 index 0000000000000000000000000000000000000000..81a43531ef313f26f4c035c2d79650978a199c78 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Tools/container_wrapper.hpp @@ -0,0 +1,1227 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_TOOLS_CONTAINER_WRAPPER_HPP_ +#define _REVOROPT_TOOLS_CONTAINER_WRAPPER_HPP_ + +#include "meta.hpp" + +#include <iterator> +#include <cassert> +#include <list> +#include <type_traits> + +namespace Revoropt { + +/* This is nasty stuff to be used with caution */ + +/* Base class to be able to merge various similar iterators */ + +/* The idea of this class is that it does not fulfill the requirements of + * iterators, but that real iterators can be built from it. First, we wrap the + * comparison between iterators using the address of the thing pointed to. This + * will allow us to compare different iterator types. By convention, this + * address will be NULL for past-the-end iterators Beware however that two + * iterators pointing to the same object will be equal once wrapped. The return + * type of incr is void, which allows it to be easily virtual, whereas classical + * operator++ for iterators return instances of the class itself which makes it + * difficult for virtual functions. The copy_to method will be used to be able + * to implement operator= on iterators built upon this class. */ + +template<typename T> +class SimiliIterator ; + +//case of iterators pointing to const values +template<typename T> +class SimiliIterator<const T> { + + public : + + /* Construction */ + SimiliIterator() : address_(NULL) {} ; + SimiliIterator( const T* address ) : address_(address) {} ; + + /* Destruction */ + virtual ~SimiliIterator() {} ; + + /* Incrementation */ + /* has to be implemented and update the protected fields */ + + virtual void incr() = 0 ; + + /* Deep copy */ + + virtual void copy_to( SimiliIterator<const T>** target ) const = 0 ; + + /* Comparison */ + /* only compares addresses, therefore any two iterators pointing to the same + * element are identical. */ + + bool operator==(const SimiliIterator& rhs) const { + return address_ == rhs.address_ ; + }; + bool operator==(const SimiliIterator<T>& rhs) const { + return address_ == rhs.address_ ; + }; + bool operator!=(const SimiliIterator& rhs) const { + return address_ != rhs.address_ ; + }; + bool operator!=(const SimiliIterator<T>& rhs) const { + return address_ != rhs.address_ ; + }; + + /* Dereferencement */ + /* Beware not to dereference past_the_end iterators */ + const T& operator*() const { + return *address_ ; + }; + + protected : + + /* address to the element pointed to. NULL if past_the_end */ + const T* address_ ; + +} ; + +/* inheritance allows conversion from values to const values */ +template<typename T> +class SimiliIterator : public SimiliIterator<const T> { + + public : + + /* Construction */ + SimiliIterator() : SimiliIterator<const T>() {} ; + SimiliIterator( T* address ) : SimiliIterator<const T>(address) {} ; + + /* Destruction */ + virtual ~SimiliIterator() {} ; + + /* Deep copy */ + + virtual void copy_to( SimiliIterator<T>** target ) const = 0 ; + virtual void copy_to( SimiliIterator<const T>** target ) const = 0 ; + + /* Dereferencement */ + /* Beware not to dereference past_the_end iterators */ + T& operator*() { + return *const_cast<T*>(SimiliIterator<const T>::address_) ; + }; +} ; + +/* End iterator, use to check the past_the_end status */ + +/* Since SimiliIterators store a pointer to the value pointed to, and that this + * pointer is NULL for past-the-end iterators, all wrapped past the end iterators + * will compare equal to that one. */ +template<typename T> +class EndIterator : public SimiliIterator<T> { + + public : + + /* Construction */ + EndIterator() : SimiliIterator<T>() {} ; + + /* Incrementation */ + void incr() {} ; + + /* Deep copy */ + void copy_to(SimiliIterator<T>** target) const { + *target = new EndIterator() ; + } + void copy_to(SimiliIterator<const T>** target) const { + *target = new EndIterator<const T>() ; + } +} ; + +template<typename T> +class EndIterator<const T> : public SimiliIterator<const T> { + + public : + + /* Construction */ + EndIterator() : SimiliIterator<const T>() {} ; + + /* Incrementation */ + void incr() {} ; + + /* Deep copy */ + void copy_to(SimiliIterator<const T>** target) const { + *target = new EndIterator() ; + } +} ; + +/* Wrapping container iterators */ + +/* We now describe how to generate SimiliIterators from usual InputIterators. */ + +template<typename InputIterator, typename Enable = void> +class IteratorWrapper ; + +/* This enable_if ensures that this class only gets instantiated whenever the + * provided iterator is not on a const type. Without doing this trick, the + * problem is that we have to instantiate the two versions of the copy_to + * function, one to copy to a SimiliIterator<const T>, and one to copy to a + * SimiliIterator<T>. However, since const const T is const T, the two function + * prototypes end up identical, which fails as an invalid overload. */ +template<typename InputIterator> +class IteratorWrapper< + InputIterator, + typename std::enable_if< + !std::is_const<typename one_deref_type<InputIterator>::type>::value + >::type +> : + public SimiliIterator< + typename one_deref_type<InputIterator>::type + > +{ + + /* Backend iterator type */ + typedef InputIterator iterator ; + /* Iterator dereference type */ + typedef typename one_deref_type<iterator>::type T ; + + protected : + + /* Iterator references */ + iterator it_ ; + iterator end_ ; + + public : + + /* Construction */ + + IteratorWrapper( const iterator& it, const iterator& end ) : + it_(it), end_(end) { + if(it_ != end_) { + SimiliIterator<T>::address_ = &(*it_) ; + } + } + IteratorWrapper( + const IteratorWrapper<iterator>& rhs + ) : it_(rhs.it_), end_(rhs.end_) { + if(it_ != end_) { + SimiliIterator<T>::address_ = &(*it_) ; + } + } + + /* Incrementation */ + + void incr() { + if(it_ != end_) { + if(++it_ != end_) { + SimiliIterator<T>::address_ = &(*it_) ; + } else { + SimiliIterator<T>::address_ = NULL ; + } + } + } + + /* Copy */ + + void copy_to( SimiliIterator<T>** target ) const { + *target = new IteratorWrapper<iterator>(*this) ; + } + + void copy_to( SimiliIterator<const T>** target ) const { + *target = new IteratorWrapper<iterator>(*this) ; + } + +} ; + +/* This one only gets instantiated if InputIterator is on a const type. */ +template<typename InputIterator> +class IteratorWrapper< + InputIterator, + typename std::enable_if< + std::is_const<typename one_deref_type<InputIterator>::type>::value + >::type +> : + public SimiliIterator< + typename one_deref_type<InputIterator>::type + > +{ + + /* Backend iterator type */ + typedef InputIterator iterator ; + /* Iterator dereference type */ + typedef typename one_deref_type<iterator>::type T ; + + protected : + + /* Iterator references */ + iterator it_ ; + iterator end_ ; + + public : + + /* Construction */ + + IteratorWrapper( const iterator& it, const iterator& end ) : + it_(it), end_(end) { + if(it_ != end_) { + SimiliIterator<T>::address_ = &(*it_) ; + } + } + IteratorWrapper( + const IteratorWrapper<iterator>& rhs + ) : it_(rhs.it_), end_(rhs.end_) { + if(it_ != end_) { + SimiliIterator<T>::address_ = &(*it_) ; + } + } + + /* Incrementation */ + + void incr() { + if(it_ != end_) { + if(++it_ != end_) { + SimiliIterator<T>::address_ = &(*it_) ; + } else { + SimiliIterator<T>::address_ = NULL ; + } + } + } + + /* Copy */ + + void copy_to( SimiliIterator<T>** target ) const { + *target = new IteratorWrapper<iterator>(*this) ; + } +} ; + +/* Flattening container iterators : returning the fully dereferenced value */ + +/* This wrapper aims at transforming a container on pointers to iterators to ... + * on a type T into a container on T, applying as many dereferencements + * necessary on the elements of the container to reach the non dereferenceable + * type. */ +template<typename InputIterator, typename Enable = void> +class IteratorFlattener ; + +/* The same enable_if is used as previously, except that the type of the fully + * dereferenced iterator is used instead. This case is on non const final type. + * */ +template<typename InputIterator> +class IteratorFlattener< + InputIterator, + typename std::enable_if< + !std::is_const<typename full_deref_type<InputIterator>::type>::value + >::type +>: + public SimiliIterator< + typename full_deref_type<InputIterator>::type + > +{ + + /* Backend iterator type */ + typedef InputIterator iterator ; + /* Iterator dereference type */ + typedef typename full_deref_type<iterator>::type T ; + + protected : + + /* Iterator references */ + iterator it_ ; + iterator end_ ; + + public : + + /* Construction */ + + IteratorFlattener( const iterator& it, const iterator& end ) : + it_(it), end_(end) { + if(it_ != end_) { + SimiliIterator<T>::address_ = &(full_deref(it_)) ; + } + } + IteratorFlattener( + const IteratorFlattener<iterator>& rhs + ) : it_(rhs.it_), end_(rhs.end_) { + if(it_ != end_) { + SimiliIterator<T>::address_ = &(full_deref(it_)) ; + } + } + + /* Incrementation */ + + void incr() { + if(it_ != end_) { + if(++it_ != end_) { + SimiliIterator<T>::address_ = &(full_deref(it_)) ; + } else { + SimiliIterator<T>::address_ = NULL ; + } + } + } + + /* Copy */ + + void copy_to( SimiliIterator<T>** target ) const { + *target = new IteratorFlattener<iterator>(*this) ; + } + + void copy_to( SimiliIterator<const T>** target ) const { + *target = new IteratorFlattener<iterator>(*this) ; + } + +} ; + +/* Case for const final type */ +template<typename InputIterator> +class IteratorFlattener< + InputIterator, + typename std::enable_if< + std::is_const<typename full_deref_type<InputIterator>::type>::value + >::type +>: + public SimiliIterator< + typename full_deref_type<InputIterator>::type + > +{ + + /* Backend iterator type */ + typedef InputIterator iterator ; + /* Iterator dereference type */ + typedef typename full_deref_type<iterator>::type T ; + + protected : + + /* Iterator references */ + iterator it_ ; + iterator end_ ; + + public : + + /* Construction */ + + IteratorFlattener( const iterator& it, const iterator& end ) : + it_(it), end_(end) { + if(it_ != end_) { + SimiliIterator<T>::address_ = &(full_deref(it_)) ; + } + } + IteratorFlattener( + const IteratorFlattener<iterator>& rhs + ) : it_(rhs.it_), end_(rhs.end_) { + if(it_ != end_) { + SimiliIterator<T>::address_ = &(full_deref(it_)) ; + } + } + + /* Incrementation */ + + void incr() { + if(it_ != end_) { + if(++it_ != end_) { + SimiliIterator<T>::address_ = &(full_deref(it_)) ; + } else { + SimiliIterator<T>::address_ = NULL ; + } + } + } + + /* Copy */ + + void copy_to( SimiliIterator<T>** target ) const { + *target = new IteratorFlattener<iterator>(*this) ; + } + +} ; + +/* Generic iterator, based on a SimiliIterator */ + +/* We now build true iterators, fulfilling the stl requirements on iterators. To + * do so, and to use virtual functions on SimiliIterators, we need to use + * pointers on these. A Generic Iterator is therefore responsible of the memory + * allocated to the SimiliIterator it is built upon, even though the allocation + * is not done by the class itself. */ + +/* Iterator on non const types */ +template<typename T> +class GenericIterator : + public std::iterator<std::input_iterator_tag,T> +{ + + friend class GenericIterator<const T> ; + + //The generic iterator is responsible for the memory allocated for its + //iterator, and will free this memory upon destruction. + SimiliIterator<T>* it_ ; + + public : + + /* Construction */ + + GenericIterator() : it_(NULL) { + } + GenericIterator( SimiliIterator<T>* it ) : it_(it) { + } + GenericIterator( const GenericIterator& rhs ) { + rhs.it_->copy_to(&it_) ; + } + + /* Assignment */ + GenericIterator& operator=( const GenericIterator& rhs ) { + delete it_ ; + rhs.it_->copy_to(&it_) ; + return *this ; + } + + /* Destruction */ + virtual ~GenericIterator() { + delete it_ ; + } + + /* Incrementation */ + GenericIterator& operator++() { + it_->incr(); + return *this ; + } + GenericIterator operator++(int) { + GenericIterator tmp(*this) ; + it_->incr(); + return tmp ; + } + + /* Comparison */ + bool operator==( const GenericIterator& rhs ) const { + return *it_ == *(rhs.it_) ; + } + bool operator==( const GenericIterator<const T>& rhs ) const { + return rhs == *this ; + } + + bool operator!=( const GenericIterator& rhs ) const { + return *it_ != *(rhs.it_) ; + } + bool operator!=( const GenericIterator<const T>& rhs ) const { + return rhs != *this ; + } + + /* Dereferencement */ + T& operator*() { + return **it_ ; + } + const T& operator*() const { + return **it_ ; + } + T* operator->() { + return &(**it_) ; + } + const T* operator->() const { + return &(**it_) ; + } + +} ; + +/* Iterator on const types */ +template<typename T> +class GenericIterator<const T> : + public std::iterator<std::input_iterator_tag,const T> +{ + + friend class GenericIterator<T> ; + + //The generic iterator is responsible for the memory allocated for its + //iterator, and will free this memory upon destruction. + SimiliIterator<const T>* it_ ; + + public : + + /* Construction */ + + GenericIterator() : it_(NULL) { + } + GenericIterator( SimiliIterator<const T>* it ) : it_(it) { + } + GenericIterator( SimiliIterator<T>* it ) : it_(it) { + } + GenericIterator( const GenericIterator& rhs ) { + rhs.it_->copy_to(&it_) ; + } + GenericIterator( const GenericIterator<T>& rhs ) { + rhs.it_->copy_to(&it_) ; + } + + /* Assignment */ + GenericIterator& operator=( const GenericIterator& rhs ) { + delete it_ ; + rhs.it_->copy_to(&it_) ; + return *this ; + } + GenericIterator& operator=( const GenericIterator<T>& rhs ) { + delete it_ ; + rhs.it_->copy_to(&it_) ; + return *this ; + } + + /* Destruction */ + virtual ~GenericIterator() { + delete it_ ; + } + + /* Incrementation */ + GenericIterator& operator++() { + it_->incr(); + return *this ; + } + GenericIterator operator++(int) { + GenericIterator tmp(*this) ; + it_->incr(); + return tmp ; + } + + /* Comparison */ + bool operator==( const GenericIterator& rhs ) const { + return *it_ == *(rhs.it_) ; + } + bool operator==( const GenericIterator<T>& rhs ) const { + return *it_ == *(rhs.it_) ; + } + + bool operator!=( const GenericIterator& rhs ) const { + return *it_ != *(rhs.it_) ; + } + bool operator!=( const GenericIterator<T>& rhs ) const { + return *it_ != *(rhs.it_) ; + } + + /* Dereferencement */ + const T& operator*() const { + return **it_ ; + } + const T* operator->() const { + return &(**it_) ; + } + +} ; + + +/* Generic container, to wrap many existing containers */ + +/* Now that we can wrap container iterators with a common parent class and build + * generic iterators from this class, we can focus on wrapping containers as + * well with a common parent class. The following class is this parent class. + * Its idea is to only care about providing an input iterator on the content. */ + +template<typename T> +class GenericContainer ; + +/* Container on const values */ +template<typename T> +class GenericContainer<const T> { + + public : + + /* Destruction */ + virtual ~GenericContainer() {} ; + + /* Typedefs */ + typedef GenericIterator<T> iterator ; + typedef GenericIterator<const T> const_iterator ; + + /* Iteration */ + virtual const_iterator begin() const = 0 ; + + virtual const_iterator end() const = 0 ; +} ; + +/* Container on non const values. The inheritance ensures the conversion to a + * container on const values. */ +template<typename T> +class GenericContainer : public GenericContainer<const T> { + + public : + + /* Destruction */ + virtual ~GenericContainer() {} ; + + /* Typedefs */ + typedef GenericIterator<T> iterator ; + typedef GenericIterator<const T> const_iterator ; + + /* Iteration */ + virtual iterator begin() = 0 ; + virtual const_iterator begin() const = 0 ; + + virtual iterator end() = 0 ; + virtual const_iterator end() const = 0 ; +} ; + +/* We now wrap usual containers as GenericContainers */ + +/* Case for stl containers */ +template<typename Container> +class ContainerWrapper : + public GenericContainer<typename Container::value_type> +{ + + Container &c ; + + /* Typedefs */ + typedef IteratorWrapper< + typename Container::iterator + > internal_iterator ; + typedef IteratorWrapper< + typename Container::const_iterator + > internal_const_iterator ; + + public : + + /* Typedefs */ + typedef typename Container::value_type value_type ; + typedef GenericIterator<value_type> iterator ; + typedef GenericIterator<const value_type> const_iterator ; + + /* Construction, destruction */ + ContainerWrapper(Container& _c) : c(_c) {} + virtual ~ContainerWrapper() {} ; + + /* Iteration */ + iterator begin() { + return iterator(new internal_iterator(c.begin(), c.end())) ; + } + const_iterator begin() const { + return const_iterator(new internal_const_iterator(c.begin(), c.end())) ; + }; + + iterator end() { + return iterator(new internal_iterator(c.end(),c.end())) ; + }; + const_iterator end() const { + return const_iterator(new internal_const_iterator(c.end(),c.end())) ; + }; +} ; + +/* Case for usual arrays */ +template<typename T> +class ContainerWrapper<T*> : + public GenericContainer<T> +{ + + T* begin_ ; + T* end_ ; + + /* Typedefs */ + typedef IteratorWrapper< + T * + > internal_iterator ; + typedef IteratorWrapper< + T const * + > internal_const_iterator ; + + public : + + /* Typedefs */ + typedef T value_type ; + typedef GenericIterator<value_type> iterator ; + typedef GenericIterator<const value_type> const_iterator ; + + /* Construction, destruction */ + ContainerWrapper(T* begin, T* end) : begin_(begin), end_(end) {} + virtual ~ContainerWrapper() {} ; + + /* Iteration */ + iterator begin() { + return iterator(new internal_iterator(begin_, end_)) ; + } + const_iterator begin() const { + return const_iterator(new internal_const_iterator(begin_, end_)) ; + }; + + iterator end() { + return iterator(new internal_iterator(end_, end_)) ; + }; + const_iterator end() const { + return const_iterator(new internal_const_iterator(end_, end_)) ; + }; +} ; + +/* Container Flattener for stl like containers */ +/* Virtually transforms a vector of pointers chains to a container + * containing the fully dereferenced elements. */ + +/* Case for stl containers */ +template<typename Container> +class ContainerFlattener : + public GenericContainer< + typename full_deref_type<typename Container::value_type>::type + > +{ + + Container &c ; + + /* Typedefs */ + typedef IteratorFlattener< + typename Container::iterator + > internal_iterator ; + typedef IteratorFlattener< + typename Container::const_iterator + > internal_const_iterator ; + + public : + + /* Typedefs */ + typedef typename full_deref_type< + typename Container::value_type + >::type value_type ; + typedef GenericIterator<value_type> iterator ; + typedef GenericIterator<const value_type> const_iterator ; + + /* Construction, destruction */ + ContainerFlattener(Container& _c) : c(_c) {} + virtual ~ContainerFlattener() {} + + /* Iteration */ + iterator begin() { + return iterator(new internal_iterator(c.begin(), c.end())) ; + } + const_iterator begin() const { + return const_iterator(new internal_const_iterator(c.begin(), c.end())) ; + }; + + iterator end() { + return iterator(new internal_iterator(c.end(),c.end())) ; + }; + const_iterator end() const { + return const_iterator(new internal_const_iterator(c.end(),c.end())) ; + }; +} ; + +/* Case for usual arrays */ +template<typename T> +class ContainerFlattener<T*> : + public GenericContainer< + typename full_deref_type<T>::type + > +{ + + T* begin_ ; + T* end_ ; + + /* Typedefs */ + typedef IteratorFlattener< + T * + > internal_iterator ; + typedef IteratorFlattener< + T const * + > internal_const_iterator ; + + public : + + /* Typedefs */ + typedef typename full_deref_type<T>::type value_type ; + typedef GenericIterator<value_type> iterator ; + typedef GenericIterator<const value_type> const_iterator ; + + /* Construction, destruction */ + ContainerFlattener(T* begin, T* end) : begin_(begin), end_(end) {} + virtual ~ContainerFlattener() {} + + /* Iteration */ + iterator begin() { + return iterator(new internal_iterator(begin_, end_)) ; + } + const_iterator begin() const { + return const_iterator(new internal_const_iterator(begin_, end_)) ; + }; + + iterator end() { + return iterator(new internal_iterator(end_, end_)) ; + }; + const_iterator end() const { + return const_iterator(new internal_const_iterator(end_, end_)) ; + }; +} ; + +/* Container merging */ + +/* Using the GenericContainer class, several containers of various types can now + * be merged together. We first create the iterator type that we be able to + * iterate on the elements of a sequence of containers. */ + +/* Composite iterator */ + +/* The composite iterator takes a sequence of GenericContainers, and passes from + * one container to the other when the end of the first container is reached.*/ + +/* Case for non const values */ +template<typename T> +class CompositeIterator : + public std::iterator<std::input_iterator_tag,T> +{ + + friend class CompositeIterator<const T> ; + + public : + + /* Typedefs */ + typedef GenericIterator<T> iterator ; + typedef typename std::list< + GenericContainer<T>* + >::iterator sub_iterator ; + typedef typename std::list< + GenericContainer<T>* + >::const_iterator const_sub_iterator ; + + /* Construction */ + + CompositeIterator() : end_(new EndIterator<T>), + inner_it_(end_), + outer_it_(containers_.end()) { + } + CompositeIterator( const CompositeIterator& rhs ) : + end_(new EndIterator<T>) + { + *this = rhs ; + } + + sub_iterator push_back( GenericContainer<T>* c ) { + //we need to ensure that we point to the first value + //even when empty containers are provided + if(inner_it_ == end_) { + containers_.push_back(c) ; + outer_it_ = --containers_.end() ; + inner_it_ = c->begin() ; + } else { + containers_.push_back(c) ; + } + return --containers_.end() ; + } + + + /* Assignment */ + CompositeIterator& operator=( const CompositeIterator& rhs ) { + //copy the inner iterator + inner_it_ = rhs.inner_it_ ; + //copy the container sequence + containers_.clear() ; + typedef typename CompositeIterator<T>::const_sub_iterator rhs_iterator ; + for( rhs_iterator it = rhs.containers_.begin(); + it != rhs.containers_.end(); + ++it + ) { + push_back(*it) ; + if(it == rhs.outer_it_) { + //replace the outer iterator to the right position in the sequence + outer_it_ = --containers_.end() ; + } + } + if(rhs.outer_it_ == rhs.containers_.end()) { + //handle the case when rhs is past-the-end + outer_it_ = containers_.end() ; + } + return *this ; + } + + /* Incrementation */ + CompositeIterator& operator++() { + ++inner_it_ ; + while(inner_it_ == end_) { + //we pass over the empty arrays here + ++outer_it_ ; + if(outer_it_ != containers_.end()) { + inner_it_ = (*outer_it_)->begin() ; + } else { + break ; + } + } + return *this ; + } + CompositeIterator operator++(int) { + CompositeIterator tmp(*this) ; + this->operator++() ; + return tmp ; + } + + /* Comparison */ + bool operator==( const CompositeIterator& rhs ) const { + return inner_it_ == rhs.inner_it_ ; + } + bool operator==( const CompositeIterator<const T>& rhs ) const { + return inner_it_ == rhs.inner_it_ ; + } + + bool operator!=( const CompositeIterator& rhs ) const { + return inner_it_ != rhs.inner_it_ ; + } + bool operator!=( const CompositeIterator<const T>& rhs ) const { + return inner_it_ != rhs.inner_it_ ; + } + + /* Dereferencement */ + T& operator*() { + return *inner_it_ ; + } + const T& operator*() const { + return *inner_it_ ; + } + T* operator->() { + return &(*inner_it_) ; + } + const T* operator->() const { + return &(*inner_it_) ; + } + + private : + + /* global end iterator */ + iterator end_ ; + + /* current inner iterator */ + iterator inner_it_ ; + + /* store a list of generic iterators to concatenate */ + std::list<GenericContainer<T>*> containers_ ; + + /* current outer_iterator_ */ + sub_iterator outer_it_ ; + +} ; + +/* Case for const values */ +template<typename T> +class CompositeIterator<const T> : + public std::iterator<std::input_iterator_tag,const T> +{ + + friend class CompositeIterator<T> ; + + public : + + /* Typedefs */ + typedef GenericIterator<const T> iterator ; + typedef typename std::list< + const GenericContainer<const T>* + >::const_iterator sub_iterator ; + + /* Construction */ + + CompositeIterator() : end_(new EndIterator<const T>), + inner_it_(end_), + outer_it_(containers_.end()) { + } + CompositeIterator( const CompositeIterator& rhs ) : + end_(new EndIterator<const T>) + { + *this = rhs ; + } + CompositeIterator( const CompositeIterator<T>& rhs ) : + end_(new EndIterator<T>) + { + *this = rhs ; + } + + sub_iterator push_back( const GenericContainer<const T>* c ) { + //we need to ensure that we point to the first value + //even when empty containers are provided + if(inner_it_ == end_) { + containers_.push_back(c) ; + outer_it_ = --containers_.end() ; + inner_it_ = c->begin() ; + } else { + containers_.push_back(c) ; + } + return --containers_.end() ; + } + + + /* Assignment */ + CompositeIterator& operator=( const CompositeIterator& rhs ) { + //copy the inner iterator + inner_it_ = rhs.inner_it_ ; + //copy the container sequence + containers_.clear() ; + for( sub_iterator it = rhs.containers_.begin(); + it != rhs.containers_.end(); + ++it + ) { + push_back(*it) ; + if(it == rhs.outer_it_) { + //ensure that the outer iterator is correctly set + outer_it_ = --containers_.end() ; + } + } + if(rhs.outer_it_ == rhs.containers_.end()) { + //handle the case when rhs is past-the-end + outer_it_ = containers_.end() ; + } + return *this ; + } + CompositeIterator& operator=( const CompositeIterator<T>& rhs ) { + //copy the inner iterator + inner_it_ = rhs.inner_it_ ; + //copy the container sequence + containers_.clear() ; + typedef typename CompositeIterator<T>::const_sub_iterator rhs_iterator ; + for( rhs_iterator it = rhs.containers_.begin(); + it != rhs.containers_.end(); + ++it + ) { + push_back(*it) ; + if(it == rhs.outer_it_) { + //ensure that the outer iterator is correctly set + outer_it_ = --containers_.end() ; + } + } + if(rhs.outer_it_ == rhs.containers_.end()) { + //handle the case when rhs is past-the-end + outer_it_ = containers_.end() ; + } + return *this ; + } + + /* Incrementation */ + CompositeIterator& operator++() { + ++inner_it_ ; + while(inner_it_ == end_) { + //we pass over the empty arrays here + ++outer_it_ ; + if(outer_it_ != containers_.end()) { + inner_it_ = (*outer_it_)->begin() ; + } else { + break ; + } + } + return *this ; + } + CompositeIterator operator++(int) { + CompositeIterator tmp(*this) ; + this->operator++() ; + return tmp ; + } + + /* Comparison */ + bool operator==( const CompositeIterator& rhs ) const { + return inner_it_ == rhs.inner_it_ ; + } + bool operator==( const CompositeIterator<T>& rhs ) const { + return inner_it_ == rhs.inner_it_ ; + } + + bool operator!=( const CompositeIterator& rhs ) const { + return inner_it_ != rhs.inner_it_ ; + } + bool operator!=( const CompositeIterator<T>& rhs ) const { + return inner_it_ != rhs.inner_it_ ; + } + + /* Dereferencement */ + const T& operator*() const { + return *inner_it_ ; + } + const T* operator->() const { + return &(*inner_it_) ; + } + + private : + + /* global end iterator */ + iterator end_ ; + + /* current inner iterator */ + iterator inner_it_ ; + + /* store a list of generic iterators to concatenate */ + std::list<const GenericContainer<const T>*> containers_ ; + + /* current outer_iterator_ */ + sub_iterator outer_it_ ; + +} ; + +/* An stl like container to merge multiple containers */ + +template<typename T> +class composite { + + public : + + /* Typedefs */ + typedef T value_type ; + typedef CompositeIterator<T> iterator ; + typedef CompositeIterator<const T> const_iterator ; + typedef typename std::list<GenericContainer<T>*>::iterator sub_iterator ; + typedef typename std::list<GenericContainer<T>*>::const_iterator const_sub_iterator ; + + /* Construction */ + composite() {} + + template<typename Container> + sub_iterator push_back( Container& container ) { + containers_.push_back(new ContainerFlattener<Container>(container)) ; + return --containers_.end() ; + } + + template<typename Tp> + sub_iterator push_back( Tp begin, Tp end ) { + containers_.push_back( + new ContainerFlattener<Tp>(begin, end) + ) ; + return --containers_.end() ; + } + + /* Destruction */ + ~composite() { + for( sub_iterator it = containers_.begin(); + it != containers_.end(); + ++it + ) { + delete *it ; + } + } + + void erase( sub_iterator it ) { + delete *it ; + containers_.erase(it) ; + } + + /* Iteration */ + iterator begin() { + iterator composite_it ; + for( sub_iterator it = containers_.begin(); + it != containers_.end(); + ++it + ) { + composite_it.push_back(*it) ; + } + return composite_it ; + } + const_iterator begin() const { + const_iterator composite_it ; + for( const_sub_iterator it = containers_.begin(); + it != containers_.end(); + ++it + ) { + composite_it.push_back(*it) ; + } + return composite_it ; + }; + + iterator end() { + return end_ ; + }; + const_iterator end() const { + return end_ ; + }; + + private : + + /* Wrapped containers */ + std::list<GenericContainer<T>*> containers_ ; + + /* global end iterator */ + iterator end_ ; + +} ; + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Tools/disjoint_sets_def.hpp b/contrib/Revoropt/include/Revoropt/Tools/disjoint_sets_def.hpp new file mode 100644 index 0000000000000000000000000000000000000000..66a92ea9d1d30c0df3d9d5cd92d103d190ddfc5b --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Tools/disjoint_sets_def.hpp @@ -0,0 +1,115 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#include "disjoint_sets_fwd.hpp" + +namespace Revoropt { + +template<typename T, typename hasher> +const unsigned int DisjointSets<T, hasher>::NO_CLASS = -1 ; + +/* Manipulation */ + +template<typename T, typename hasher> +unsigned int DisjointSets<T, hasher>::insert( const T& item ) { + item_it it = items_.find(item) ; + if(it != items_.end()) { + return it->second ; + } else { + unsigned int index = nodes_.size() ; + uf_node item_node = { .rep = index, .rank = 1 } ; + nodes_.push_back(item_node) ; + items_[item] = index ; + return index ; + } +} + +template<typename T, typename hasher> +bool DisjointSets<T, hasher>::contains( const T& item ) { + return items_.find(item) != items_.end() ; +} + +/* Operations */ + +template<typename T, typename hasher> +unsigned int DisjointSets<T, hasher>::item_class( const T& item ) { + item_it it = items_.find(item) ; + return it == items_.end() ? NO_CLASS : get_rep(it->second) ; +} + +template<typename T, typename hasher> +unsigned int DisjointSets<T, hasher>::merge_classes( + unsigned int c1, + unsigned int c2 + ) { + //get class representatives + const unsigned int c1_rep = get_rep(c1) ; + const unsigned int c2_rep = get_rep(c2) ; + //if the classes are already merged, do nothing + if(c1_rep == c2_rep) return c1_rep ; + + //merge + //get the class ranks + const unsigned int c1_rank = get_rank(c1_rep) ; + const unsigned int c2_rank = get_rank(c2_rep) ; + if(c1_rank < c2_rank) { + //tree for c2 is deeper, use c2 as root + set_rep(c1_rep, c2_rep) ; + return c2_rep ; + } else if(c2_rank < c1_rank) { + //tree for c1 is deeper, use c1 as root + set_rep(c2_rep, c1_rep) ; + return c1_rep ; + } else { + //trees have the same depth, use c1 as root and increase its depth + set_rep(c2_rep, c1_rep) ; + set_rank(c1_rep, c1_rank+1) ; + return c1_rep ; + } +} + +template<typename T, typename hasher> +unsigned int DisjointSets<T, hasher>::merge_items( + const T& item1, + const T& item2 + ) { + const unsigned int c1 = item_class(item1) ; + if(c1 == NO_CLASS) return NO_CLASS ; + const unsigned int c2 = item_class(item2) ; + if(c2 == NO_CLASS) return NO_CLASS ; + return merge_classes(c1, c2) ; +} + +template<typename T, typename hasher> +unsigned int DisjointSets<T, hasher>::get_rep( unsigned int c ) { + if(nodes_[c].rep != c) { + //the element is within a class with a different root + nodes_[c].rep = get_rep(nodes_[c].rep) ; + } + //return the representative + return nodes_[c].rep ; +} + +template<typename T, typename hasher> +void DisjointSets<T, hasher>::set_rep( unsigned int c, unsigned int rep ) { + nodes_[c].rep = rep ; +} + +template<typename T, typename hasher> +unsigned int DisjointSets<T, hasher>::get_rank( unsigned int c ) { + return nodes_[c].rank ; +} + +template<typename T, typename hasher> +void DisjointSets<T, hasher>::set_rank( unsigned int c, unsigned int rank ) { + nodes_[c].rank = rank ; +} + +} //end of namespace Revoropt diff --git a/contrib/Revoropt/include/Revoropt/Tools/disjoint_sets_fwd.hpp b/contrib/Revoropt/include/Revoropt/Tools/disjoint_sets_fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..7d24331aba8721a08d4344aee42d822223bb85f7 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Tools/disjoint_sets_fwd.hpp @@ -0,0 +1,65 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_TOOLE_DISJOINT_SETS_H_ +#define _REVOROPT_TOOLE_DISJOINT_SETS_H_ + +#include <vector> +#include <unordered_map> +#include <functional> + +namespace Revoropt { + +struct uf_node { + unsigned int rep ; + unsigned int rank ; +} ; + +template< typename T, typename hasher = std::hash<T> > +class DisjointSets { + + public : + + static const unsigned int NO_CLASS ; + + /* Construction */ + + DisjointSets() {} ; + + /* Manipulation */ + + unsigned int insert( const T& item ) ; + bool contains( const T& item ) ; + + /* Operations */ + + unsigned int merge_classes( unsigned int c1, unsigned int c2 ) ; + unsigned int merge_items( const T& item1, const T& item2 ) ; + + unsigned int get_rep( unsigned int c ) ; + unsigned int item_class( const T& item ) ; + + private : + + unsigned int get_rank( unsigned int c ) ; + void set_rank(unsigned int c, unsigned int rank) ; + void set_rep( unsigned int c, unsigned int rep ) ; + + std::vector<uf_node> nodes_ ; + std::unordered_map<T, unsigned int, hasher> items_ ; + typedef typename std::unordered_map< + T, unsigned int, hasher + >::iterator item_it ; + +} ; + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Tools/eigen_helpers.hpp b/contrib/Revoropt/include/Revoropt/Tools/eigen_helpers.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6c567618441df79683b6f0c24a4d0a0d236af427 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Tools/eigen_helpers.hpp @@ -0,0 +1,70 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_TOOLS_EIGEN_HELPERS_HPP_ +#define _REVOROPT_TOOLS_EIGEN_HELPERS_HPP_ + +#include <eigen3/Eigen/Dense> + +#include <iostream> +#include <sstream> +#include <algorithm> + +namespace Revoropt { + +template<typename Scalar, int Dim> +std::istream& operator>>( std::istream& in, Eigen::Matrix<Scalar,Dim,1>& v ) { + //vector type + typedef Eigen::Matrix<Scalar,Dim,1> Vector ; + + //save the stream position + std::streampos in_pos = in.tellg() ; + + //load buffer + Vector data = Vector::Constant(0) ; + + //try loading with spaces + for(int i = 0; i<Dim; ++i) { + if(in.good()) { + in >> data[i] ; + } else { + break ; + } + } + if(!in.fail()) { + data.swap(v) ; + } else { + //reload backup + in.clear() ; + in.seekg(in_pos) ; + std::string input ; + in >> input ; + + std::replace(input.begin(), input.end(), ';', ' ') ; + std::stringstream ss(input) ; + + //try copy + for(int i = 0; i<Dim; ++i) { + if(ss.good()) { + ss >> data[i] ; + } else { + break ; + } + } + if(!ss.fail()) { + data.swap(v) ; + } + } + return in ; +} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Tools/group_vector.hpp b/contrib/Revoropt/include/Revoropt/Tools/group_vector.hpp new file mode 100644 index 0000000000000000000000000000000000000000..0779004947edf4506518d07d8fd006c45b41c379 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Tools/group_vector.hpp @@ -0,0 +1,98 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_TOOLS_GROUP_VECTOR_HPP_ +#define _REVOROPT_TOOLS_GROUP_VECTOR_HPP_ + +#include "container_wrapper.hpp" + +#include <map> +#include <vector> +#include <string> + +namespace Revoropt { + +template<typename T> +class group_vector { + + public : + + /* Typedefs */ + typedef T value_type ; + typedef typename composite<T>::iterator iterator ; + typedef typename composite<T>::const_iterator const_iterator ; + + /* Construction, destruction */ + group_vector() { + all_contained_.push_back(self_contained_) ; + } + + /* Iteration */ + iterator begin() { + return all_contained_.begin() ; + } + + const_iterator begin() const { + return all_contained_.begin() ; + } + + iterator end() { + return all_contained_.end() ; + } + + const_iterator end() const { + return all_contained_.end() ; + } + + /* Content management */ + void insert(T* t) { + self_contained_.push_back(t) ; + } + + template< typename Container > + void insert_group( const std::string& name, Container& container ) { + groups_[name] = all_contained_.push_back(container) ; + } + + template< typename Tp > + void insert_group( const std::string& name, Tp begin, Tp end ) { + groups_[name] = all_contained_.push_back(begin, end) ; + } + + void erase_group( const std::string& name ) { + g_iterator it = groups_.find(name) ; + if(it != groups_.end()) { + all_contained_.erase(it->second) ; + } + } + + protected : + + /* Wrapped container for groups */ + composite<T> all_contained_ ; + + private : + /* Inner container for individual elements */ + std::vector<T*> self_contained_ ; + + /* Group management */ + typedef typename std::map< + std::string, + typename composite<T>::sub_iterator + >::iterator g_iterator ; + std::map< + std::string, + typename composite<T>::sub_iterator + > groups_ ; +} ; + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Tools/hash.hpp b/contrib/Revoropt/include/Revoropt/Tools/hash.hpp new file mode 100644 index 0000000000000000000000000000000000000000..877b11c2bb236ba9b09c63e8066c7a57d9e5712c --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Tools/hash.hpp @@ -0,0 +1,67 @@ +// @licstart farmhash +// Copyright (c) 2014 Google, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// FarmHash, by Geoff Pike + +// +// http://code.google.com/p/farmhash/ +// +// This file provides a few functions for hashing strings and other +// data. All of them are high-quality functions in the sense that +// they do well on standard tests such as Austin Appleby's SMHasher. +// They're also fast. FarmHash is the successor to CityHash. +// +// Functions in the FarmHash family are not suitable for cryptography. +// +// WARNING: This code has been only lightly tested on big-endian platforms! +// It is known to work well on little-endian platforms that have a small penalty +// for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs. +// It should work on all 32-bit and 64-bit platforms that allow unaligned reads; +// bug reports are welcome. +// +// By the way, for some hash functions, given strings a and b, the hash +// of a+b is easily derived from the hashes of a and b. This property +// doesn't hold for any hash functions in this file. +// +// @licend farmhash + +#ifndef _REVOROPT_TOOLS_HASH_HPP_ +#define _REVOROPT_TOOLS_HASH_HPP_ + +#include <functional> + +namespace Revoropt { + +template < class T, class Hasher = std::hash<T> > +inline void hash_combine(std::size_t& seed, const T& v) +{ + Hasher hasher; + const std::size_t kMul = 0x9ddfea08eb382d69ULL; + std::size_t a = (hasher(v) ^ seed) * kMul; + a ^= (a >> 47); + std::size_t b = (seed ^ a) * kMul; + b ^= (b >> 47); + seed = b * kMul; +} + +} // end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Tools/intersections_def.hpp b/contrib/Revoropt/include/Revoropt/Tools/intersections_def.hpp new file mode 100644 index 0000000000000000000000000000000000000000..66fcabfa5edfbeec56d67d40b5ef183d47cf2eb3 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Tools/intersections_def.hpp @@ -0,0 +1,687 @@ +#ifndef _REVOROPT_TOOLS_INTERSECTIONS_DEF_HPP_ +#define _REVOROPT_TOOLS_INTERSECTIONS_DEF_HPP_ + +#include "eigen3/Eigen/Dense" + +#include "intersections_fwd.hpp" + +namespace Revoropt { + +/* 2d orientation check */ +template<typename Scalar> +char orientation( const Scalar v0[2], const Scalar v1[2] ) { + Scalar det = v0[0]*v1[1] - v1[0]*v0[1] ; + if(det > 0) return 1 ; + if(det < 0) return -1 ; + return 0 ; +} + +/* Checks whether a point is in a segment (including endpoints) + * If the offset pointer is provided, it is filled with the value of a + * such that p = p0 + a*(p1-p0). If the p is not in the segment, the point + * q = p0 + a*(p1-p0) is the point of the segment nearest to p. */ +template<int Dim, typename Scalar> +bool point_in_segment( const Scalar p_coords[Dim], + const Scalar p0_coords[Dim], + const Scalar p1_coords[Dim], + Scalar* offset //output + ) { + //vector type + typedef Eigen::Matrix<Scalar,Dim,1> Vector ; + + //segment extremities and point + Eigen::Map<const Vector> p0(p0_coords) ; + Eigen::Map<const Vector> p1(p1_coords) ; + Eigen::Map<const Vector> p(p_coords) ; + + //direction of the segment + Vector u = p1-p0 ; + const Scalar u_len = u.norm() ; + if(u_len == 0) { + //p1 and p0 are the same point + if(offset != NULL) { + //by convention, p0 is the nearest point + *offset = 0 ; + } + //check whether p is that point too + return (p == p0) ; + } + u /= u_len ; + //project p on the segment line + const Vector p_vect = p-p0 ; + const Scalar p_offset = p_vect.dot(u) ; + //check that the projection is within the extremities + if(p_offset < 0) { + if(offset != NULL) { + *offset = 0 ; + } + return false ; + } + if(p_offset > u_len) { + if(offset != NULL) { + *offset = 1 ; + } + return false ; + } + //no return, the projection is within the segment + if(offset != NULL) { + //assign the offset + *offset = p_offset / u_len ; + } + //check that the projected point is the actual point + return (p_offset == p_vect.norm()) ; +} + +/* Segment intersection */ +/* Returns true if the segments are coplanar and intersect. + * If the s0_offset pointer is provided, it is filled with the value of a + * such that the intersection I1 = p0 + a*(p1-p0). If the segments do not + * intersect, I0 is the point of [p0,p1] nearest to [p2,p3]. + * If the s1_offset pointer is provided, it is filled with the value of b + * such that the intersection I2 = p2 + b*(p3-p2). If the segments do not + * intersect, I1 is the point of [p2,p3] nearest to [p0,p1]. + * If the segments do not intersect, I0 and I1 are the points archieving the + * smallest distance between [p0,p1] and [p2,p3]. */ + +namespace segment_intersection_impl{ + +template<int Dim, typename Scalar> +class si_impl { + public: + //vector type + typedef Eigen::Matrix<Scalar,Dim,1> Vector ; + //2d vector type + typedef Eigen::Matrix<Scalar,2,1> Vector2 ; + + static bool do_it( const Scalar p0_coords[Dim], + const Scalar p1_coords[Dim], + const Scalar p2_coords[Dim], + const Scalar p3_coords[Dim], + Scalar* s0_offset, //output + Scalar* s1_offset //output + ) { + //segment extremities + Eigen::Map<const Vector> p0(p0_coords) ; + Eigen::Map<const Vector> p1(p1_coords) ; + Eigen::Map<const Vector> p2(p2_coords) ; + Eigen::Map<const Vector> p3(p3_coords) ; + + //projection matri on a plane parallel to both segments + Eigen::Matrix<Scalar,2,Dim> P ; + + //unit direction of the first segment + P.row(0) = p1-p0 ; + const Scalar P0_len = P.row(0).norm() ; + if(P0_len == 0) { + //the first segment is reduced to a point + if(s0_offset != NULL) { + //by convention the p0 is nearest to [p2,p3] + *s0_offset = 0 ; + } + //point_segment intersection + return point_in_segment<Dim>(p0_coords, p2_coords, p3_coords, s1_offset) ; + } + P.row(0) /= P0_len ; + + //direction of the second segment + P.row(1) = p3-p2 ; + if(P.row(1).dot(P.row(1)) == 0) { + //the second segment is reduced to a point + if(s1_offset != NULL) { + //by convention the p2 is nearest to [p0,p1] + *s1_offset = 0 ; + } + //point_segment intersection + return point_in_segment<Dim>(p2_coords, p0_coords, p1_coords, s0_offset) ; + } + + //orthogonalize the direction wrt the direction of the first segment + P.row(1) -= P.row(1).dot(P.row(0))*P.row(0) ; + const Scalar P1_len = P.row(1).norm() ; + if(P1_len != 0) { + //if the segments are parallel, v is 0, otherwise it is normalized + P.row(1) /= P1_len ; + } + + //projection of one of each segment endpoints + //Dim dimensional vectors here, although 2d in the end + Vector2 pp0 = Vector2::Zero() ; + Vector2 pp2 = P*(p2-p0) ; + + //check whether the segments are coplanar : same offsets wrt the plane + const Vector p2_proj = p2 - P.transpose()*pp2 ; + const bool segments_coplanar = p2_proj.dot(p2_proj) == 0 ; + + //early return if the segments are not coplanar, and no offset required + if((s0_offset == NULL) && !segments_coplanar) return false ; + + //projections the remaining extremities + Vector2 pp1 = P*(p1-p0) ; + Vector2 pp3 = P*(p3-p0) ; + + //switch to 2D + const bool intersect_2d = si_impl<2,Scalar>::do_it( pp0.data(), + pp1.data(), + pp2.data(), + pp3.data(), + s0_offset, + s1_offset + ) ; + + return intersect_2d && segments_coplanar ; + } +} ; + +template<typename Scalar> +class si_impl<2,Scalar> { + public: + //vector type + typedef Eigen::Matrix<Scalar,2,1> Vector ; + + static bool do_it( const Scalar p0_coords[2], + const Scalar p1_coords[2], + const Scalar p2_coords[2], + const Scalar p3_coords[2], + Scalar* s0_offset, //output + Scalar* s1_offset //output + ) { + //segment extremities + Eigen::Map<const Vector> p0(p0_coords) ; + Eigen::Map<const Vector> p1(p1_coords) ; + Eigen::Map<const Vector> p2(p2_coords) ; + Eigen::Map<const Vector> p3(p3_coords) ; + + //segment vectors + const Vector s0 = p1-p0 ; + const Vector s1 = p3-p2 ; + + //orientation check + Vector tested = p2-p0 ; + const char o02 = orientation(s0.data(), tested.data()) ; + tested = p3-p0 ; + const char o03 = orientation(s0.data(), tested.data()) ; + tested = p0-p2 ; + const char o10 = orientation(s1.data(), tested.data()) ; + tested = p1-p2 ; + const char o11 = orientation(s1.data(), tested.data()) ; + + //basic intersection check, no enough if the segments are aligned + const bool basic_intersect = (o02 != o03) && (o10 != o11) ; + + //no offset is required, return fastly if the intersection is sure + if(basic_intersect && (s0_offset == NULL)) return true ; + + //segment normals in a matrix + Eigen::Matrix<Scalar,2,2> A ; + A << -s0[1], s0[0], -s1[1], s1[0] ; + + //check parallelism of the segments + const Scalar det = A.determinant() ; + + if(det == 0) { + //the segments are parallel or at least one is 0 + //length of [p0,p1] + const Scalar s0_len = s0.norm() ; + if(s0_len == 0) { + //p0 and p1 are the same point + if(s0_offset != NULL) { + //by convention p0 is nearest to [p2,p3] + *s0_offset = 0 ; + } + return point_in_segment<2>(p0_coords, p2_coords, p3_coords, s1_offset) ; + } + //from now on we know that p0 and p1 are different + //project p2 and p3 on [p0,p1] + const Scalar pp2 = p2.dot(s0)/s0_len ; + const Scalar pp3 = p3.dot(s0)/s0_len ; + const Scalar minp = pp2 < pp3 ? pp2 : pp3 ; + const Scalar maxp = pp2 < pp3 ? pp3 : pp2 ; + //cases + if((minp <= 0) && (maxp >= 0)) { + //p0 is a point reaching minimal distance to [p2,p3] + if(s0_offset != NULL) { + //assign it as nearest to [p2,p3] if asked + *s0_offset = 0 ; + } + return point_in_segment<2>(p0_coords, p2_coords, p3_coords, s1_offset) ; + } + if((minp <= s0_len) && (maxp >= s0_len)) { + //p1 is a point reaching minimal distance to [p2,p3] + if(s0_offset != NULL) { + //assign it as nearest to [p2,p3] if asked + *s0_offset = 1 ; + } + return point_in_segment<2>(p1_coords, p2_coords, p3_coords, s1_offset) ; + } + if(minp > s0_len) { + //no intersection + //p1 is nearest to [p2,p3] and the nearest point is at minp + if(s0_offset != NULL) { + //assign it as nearest to [p2,p3] if asked + *s0_offset = 1 ; + } + if(s1_offset != NULL) { + //assign the point projected at minp as nearest to [p0,p1] + *s1_offset = minp == pp2 ? 0 : 1 ; + } + return false ; + } + if(maxp < 0) { + //no intersection + //p0 is nearest to [p2,p3] and the nearest point is at maxp + if(s0_offset != NULL) { + //assign it as nearest to [p2,p3] if asked + *s0_offset = 0 ; + } + if(s1_offset != NULL) { + //assign the point projected at minp as nearest to [p0,p1] + *s1_offset = maxp == pp2 ? 0 : 1 ; + } + return false ; + } + //from now on both p2 and p3 project inside [p0,p1] + //by convention p2 is nearest to [p0,p1] + if(s1_offset != NULL) { + *s1_offset = 0 ; + } + return point_in_segment<2>(p2_coords, p0_coords, p1_coords, s0_offset) ; + } + //the segments are not parallel + //if they intersect, the basic_intersect detected it + if(s0_offset == NULL) return false ; + //intersection between (p0,p1) and (p2,p3) + Vector lhs ; + lhs << A.row(0).dot(p0), A.row(1).dot(p2) ; + const Vector I = A.inverse()*lhs ; + //offsets of the intersection + const Scalar s0_tmp_offset = s0.dot(I-p0) / s0.dot(s0) ; //no division by 0 + const Scalar s1_tmp_offset = s1.dot(I-p2) / s1.dot(s1) ; //no division by 0 + //if the segments basically intersect, return these offsets + if(basic_intersect) { + *s0_offset = s0_tmp_offset ; + if(s1_offset != NULL) *s1_offset = s1_tmp_offset ; + return true ; + } + //from now on the segments do not intersect + if(o02 != o03) { + //I lies in [p2,p3] + //I does not lie in [p0,p1] otherwise basic_intersect + //the extremity of [p0,p1] nearest to I reaches minimal distance + const Scalar* s0_nearest = s0_tmp_offset > 0.5 ? p1_coords : p0_coords ; + *s0_offset = s0_nearest == p1_coords ? 1 : 0 ; + if(s1_offset != NULL) { + point_in_segment<2>(s0_nearest, p2_coords, p3_coords, s1_offset) ; + } + return false ; + } + //from now on I lies outside [p2,p3] + //get the extremity of [p2,p3] nearest to I + const Scalar* s1_nearest = s1_tmp_offset > 0.5 ? p3_coords : p2_coords ; + //point in [p0,p1] nearest to that point + point_in_segment<2>(s1_nearest, p0_coords, p1_coords, s0_offset) ; + if((*s0_offset >=0) && (*s0_offset <= 1)) { + //s1_nearest reaches minimal distance + if(s1_offset != NULL) { + *s1_offset = s1_nearest == p3_coords ? 1 : 0 ; + } + return false ; + } + //s1_nearest may not reach minimal distance + //the extremity of [p0,p1] nearest to I reaches minimal distance anyway + *s0_offset = s0_tmp_offset > 0.5 ? 1 : 0 ; + if(s1_offset != NULL) { + //get that extremity + const Scalar* s0_nearest = s0_offset == 0 ? p0_coords : p1_coords ; + point_in_segment<2>(s0_nearest, p2_coords, p3_coords, s1_offset) ; + } + return false ; + } +} ; + +} //end of namespace segment_intersection_impl + +template<int Dim, typename Scalar> +bool segment_intersection( const Scalar p0_coords[2], + const Scalar p1_coords[2], + const Scalar p2_coords[2], + const Scalar p3_coords[2], + Scalar* s0_offset, //output + Scalar* s1_offset //output + ) { + using namespace segment_intersection_impl ; + return si_impl<Dim,Scalar>::do_it( p0_coords, + p1_coords, + p2_coords, + p3_coords, + s0_offset, + s1_offset + ) ; +} + + +/* Checks whether a point is strictly in a sphere. + * The *squared* radius is required */ +template<int Dim, typename Scalar> +bool point_in_sphere( const Scalar point[Dim], + const Scalar center[Dim], + Scalar radius + ) { + //vector type + typedef Eigen::Matrix<Scalar,Dim,1> Vector ; + + Eigen::Map<const Vector> p(point) ; + Eigen::Map<const Vector> c(center) ; + Vector edge = p-c ; + + //distance check + return edge.dot(edge) < radius*radius ; +} + +/* Intersection between the interior of a segment and a sphere. + * Returns the number of intersections. + * The *squared* radius is required. + * The provided output array receives the intersections p + * as the values of u such that p = p1 + u(p2-p1) */ +template<int Dim, typename Scalar> +unsigned char segment_sphere_intersections( const Scalar p0_coords[Dim], + const Scalar p1_coords[Dim], + const Scalar center[Dim], + Scalar radius, + Scalar intersections[2] //output + ) { + //vector type + typedef Eigen::Matrix<Scalar,Dim,1> Vector ; + + Eigen::Map<const Vector> p0(p0_coords) ; + Eigen::Map<const Vector> p1(p1_coords) ; + Eigen::Map<const Vector> c(center) ; + + //edge vector and its squared length + const Vector edge = p1-p0 ; + const Scalar sq_elen = edge.dot(edge) ; + //center vector and its squared length + const Vector cv = p0-c ; + const Scalar sq_cvlen = cv.dot(cv) ; + //dot product between the two + const Scalar edgedotcv = edge.dot(cv) ; + + //discriminant + const Scalar disc = edgedotcv*edgedotcv - sq_elen*(sq_cvlen - radius*radius) ; + + //no intersection case + if(disc < 0) return 0 ; + + //intersections, compute their positions + const Scalar dist_rt = sqrt(disc) ; + const Scalar u0 = (-edgedotcv - dist_rt)/sq_elen ; + const Scalar u1 = (-edgedotcv + dist_rt)/sq_elen ; + + //check these positions are in the interior of the segment + //and count the number of valid intersections + unsigned char number_of_intersections = 0 ; + if((u0 > 0) && (u0 < 1)) { + intersections[0] = u0 ; + ++number_of_intersections ; + } + if((u1 > 0) && (u1 < 1)) { + intersections[number_of_intersections] = u1 ; + ++number_of_intersections ; + } + + //return the number of intersections + return number_of_intersections ; +} + +/* Check whether a point lies in a triangle. + * If the bar_coords pointer is provided, it is filled with two scalars a and b + * such that the point a.x0 + b.x1 + (1-a-b).x2 is the point in the triangle + * x0,x1,x2 nearest to p. */ + +namespace point_in_triangle_impl { + +//classes for partial template specialization, unavailable for functions +template<int Dim, typename Scalar> +class pit_impl { + public: + static bool do_it( const Scalar p_coords[Dim], + const Scalar x0_coords[Dim], + const Scalar x1_coords[Dim], + const Scalar x2_coords[Dim], + Scalar bar_coords[2] = NULL + ) { + //vector type + typedef Eigen::Matrix<Scalar,Dim,1> Vector ; + //2d vector type + typedef Eigen::Matrix<Scalar,2,1> Vector2 ; + + Eigen::Map<const Vector> p(p_coords) ; + Eigen::Map<const Vector> x0(x0_coords) ; + Eigen::Map<const Vector> x1(x1_coords) ; + Eigen::Map<const Vector> x2(x2_coords) ; + + //projection matrix + Eigen::Matrix<Scalar,2,Dim,Eigen::RowMajor> P ; + //base of the triangle + P.row(0) = x1-x0 ; + const Scalar base_len = P.row(0).norm() ; + if(base_len == 0) { + //x0 and x1 are identical + if(bar_coords != NULL) { + //by convention the nearest point is on segment [x0,x2] + bar_coords[1] = 0 ; + } + return point_in_segment<Dim>(p_coords,x2_coords,x0_coords,bar_coords) ; + } + P.row(0) /= base_len ; + + //height of the triangle + P.row(1) = x2-x0 ; + P.row(1) -= P.row(1).dot(P.row(0))*P.row(0) ; + const Scalar height_len = P.row(1).norm() ; + if(height_len == 0) { + //get the vector used for the height + const Vector h = x2-x2 ; + //compute the dot product with the base (theoretically 1 or -1) + const Scalar bdoth = h.dot(P.row(0)) ; + if(bdoth < 0) { + //x0 lies on [x1,x2] + if(bar_coords != NULL) { + //by convention the nearest point is on segment [x1,x2] + bar_coords[0] = 0 ; + return point_in_segment<Dim>( p_coords, + x2_coords, + x1_coords, + bar_coords+1 + ) ; + } + return point_in_segment<Dim>(p_coords,x2_coords,x1_coords) ; + } else { + if(h.dot(h) < base_len*base_len) { + //x2 lies on [x0,x1] + if(bar_coords != NULL) { + //by convention the nearest point is on segment [x0,x1] + bool intersect = point_in_segment<Dim>( p_coords, + x0_coords, + x1_coords, + bar_coords+1 + ) ; + bar_coords[0] = 1-bar_coords[1] ; + return intersect ; + } + return point_in_segment<Dim>(p_coords,x0_coords,x1_coords) ; + } else { + //x1 lies on [x0,x2] + if(bar_coords != NULL) { + //by convention the nearest point is on segment [x0,x2] + bar_coords[1] = 0 ; + } + return point_in_segment<Dim>( p_coords, + x2_coords, + x0_coords, + bar_coords + ) ; + } + } + } + P.row(1) /= height_len ; + + //project everything + Vector2 pp, x0p, x1p, x2p ; + pp = P*(p-x0) ; + x0p = Vector2::Zero() ; + x1p = P*(x1-x0) ; + x2p = P*(x2-x0) ; + + //coplanarity test + const Vector p_proj = p - P.transpose()*pp ; + bool coplanar = p_proj.dot(p_proj) == 0 ; + if((bar_coords == NULL) && (!coplanar)) return false ; + + //switch to 2D + bool intersect = pit_impl<2,Scalar>::do_it( pp.data(), + x0p.data(), + x1p.data(), + x2p.data(), + bar_coords + ) ; + + return (coplanar && intersect) ; + } +} ; + +template<typename Scalar> +class pit_impl<2,Scalar> { + public: + static bool do_it( const Scalar p_coords[2], + const Scalar x0_coords[2], + const Scalar x1_coords[2], + const Scalar x2_coords[2], + Scalar bar_coords[2] = NULL + ) { + //Vector type + typedef Eigen::Matrix<Scalar,2,1> Vector ; + + //Query point + Eigen::Map<const Vector> p(p_coords) ; + + //Triangle vertices + const Scalar* x[3] ; + x[0] = x0_coords ; + x[1] = x1_coords ; + x[2] = x2_coords ; + + //matrix for sides and point vectors + Eigen::Matrix<Scalar,4,3, Eigen::ColMajor> m ; + + for(int i=0; i<3; ++i) { + Eigen::Map<const Vector> x1(x[(i+1)%3]) ; + Eigen::Map<const Vector> x2(x[(i+2)%3]) ; + m.template block<2,1>(0,i) = x2-x1 ; + m.template block<2,1>(2,i) = p -x1 ; + } + + //twice the triangle area + const Scalar twoarea = m.template topLeftCorner<2,2>().determinant() ; + + //barycentric coordinates wrt x0, x1 and x2 + Scalar b[3] ; + for(int i = 0; i<3; ++i) { + Eigen::Map< Eigen::Matrix<Scalar,2,2> > mini(m.col(i).data()) ; + b[i] = mini.determinant()/twoarea ; + } + + for(int i = 0; i<3; ++i) { + if(b[i] < 0) { + //p is on the wrong side of [x{[i+1)%3],x[(i+2)%3]] + if(b[(i+1)%3] < 0) { + //p is also on the wrong side of [x[(i+2)%3],x[i]] + //x[(i+2)%3] is the vertex in common + if( m.template block<2,1>(0,i).dot( + m.template block<2,1>(2,(i+1)%3))*twoarea + > 0 + ) { + //the nearest side is [x[(i+2)%3],x[i]] + bool intersect = point_in_segment<2>( p_coords, + x[(i+2)%3], + x[i], + bar_coords + ) ; + if(bar_coords != NULL) { + b[i] = bar_coords[0] ; + b[(i+1)%3] = 0 ; + b[(i+2)%3] = 1-bar_coords[0] ; + bar_coords[0] = b[0] ; + bar_coords[1] = b[1] ; + } + return intersect ; + } + } + if(b[(i+2)%3] < 0) { + //p is also on the wrong side of [x[i],x[(i+1)%3]] + //x[(i+1)%3] is the vertex in common + if( m.template block<2,1>(0,i).dot( + m.template block<2,1>(2,(i+2)%3))*twoarea + < 0 + ) { + //the nearest side is [x[i],x[(i+1)%3]] + bool intersect = point_in_segment<2>( p_coords, + x[(i+1)%3], + x[i], + bar_coords + ) ; + if(bar_coords != NULL) { + b[i] = bar_coords[0] ; + b[(i+1)%3] = 1-bar_coords[0] ; + b[(i+2)%3] = 0 ; + bar_coords[0] = b[0] ; + bar_coords[1] = b[1] ; + } + return intersect ; + } + } + //no return, the nearest point is on [x[(i+1)%3],x[(i+2)%3]] + bool intersect = point_in_segment<2>( p_coords, + x[(i+1)%3], + x[(i+2)%3], + bar_coords + ) ; + if(bar_coords != NULL) { + b[i] = 0 ; + b[(i+1)%3] = 1-bar_coords[0] ; + b[(i+2)%3] = bar_coords[0] ; + bar_coords[0] = b[0] ; + bar_coords[1] = b[1] ; + } + return intersect ; + } + } + //no return, p is in the triangle + + if(bar_coords != NULL) { + bar_coords[0] = b[0] ; + bar_coords[1] = b[1] ; + } + return true ; + } +} ; +}//end of namespace point_in_triangle_impl + +template<int Dim, typename Scalar> +bool point_in_triangle( const Scalar p_coords[Dim], + const Scalar x0_coords[Dim], + const Scalar x1_coords[Dim], + const Scalar x2_coords[Dim], + Scalar bar_coords[2] + ) { + using namespace point_in_triangle_impl ; + return pit_impl<Dim,Scalar>::do_it( p_coords, + x0_coords, + x1_coords, + x2_coords, + bar_coords + ) ; +} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Tools/intersections_fwd.hpp b/contrib/Revoropt/include/Revoropt/Tools/intersections_fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..50ed9b51defeebed813ef427c144fb4c024c4006 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Tools/intersections_fwd.hpp @@ -0,0 +1,76 @@ +#ifndef _REVOROPT_TOOLS_INTERSECTIONS_FWD_HPP_ +#define _REVOROPT_TOOLS_INTERSECTIONS_FWD_HPP_ + +namespace Revoropt { + +/* 2d orientation check */ +template<typename Scalar> +char orientation( const Scalar v0[2], const Scalar v1[2] ) ; + +/* Checks whether a point is in a segment (including endpoints) + * If the offset pointer is provided, it is filled with the value of a + * such that p = p0 + a*(p1-p0). If the p is not in the segment, the point + * q = p0 + a*(p1-p0) is the point of the segment nearest to p. */ +template<int Dim, typename Scalar> +bool point_in_segment( const Scalar p_coords[Dim], + const Scalar p0_coords[Dim], + const Scalar p1_coords[Dim], + Scalar* offset = NULL //output + ) ; + +/* Segment intersection */ +/* Returns true if the segments are coplanar and intersect. + * If the s0_offset pointer is provided, it is filled with the value of a + * such that the intersection I1 = p0 + a*(p1-p0). If the segments do not + * intersect, I0 is the point of [p0,p1] nearest to [p2,p3]. + * If the s1_offset pointer is provided, it is filled with the value of b + * such that the intersection I2 = p2 + b*(p3-p2). If the segments do not + * intersect, I1 is the point of [p2,p3] nearest to [p0,p1]. + * If the segments do not intersect, I0 and I1 are the points archieving the + * smallest distance between [p0,p1] and [p2,p3]. */ +template<int Dim, typename Scalar> +bool segment_intersection( const Scalar p0_coords[2], + const Scalar p1_coords[2], + const Scalar p2_coords[2], + const Scalar p3_coords[2], + Scalar* s0_offset = NULL, //output + Scalar* s1_offset = NULL //output + ) ; + + +/* Checks whether a point is strictly in a sphere. + * The *squared* radius is required */ +template<int Dim, typename Scalar> +bool point_in_sphere( const Scalar point[Dim], + const Scalar center[Dim], + Scalar radius + ) ; + +/* Intersection between the interior of a segment and a sphere. + * Returns the number of intersections. + * The *squared* radius is required. + * The provided output array receives the intersections p + * as the values of u such that p = p1 + u(p2-p1) */ +template<int Dim, typename Scalar> +unsigned char segment_sphere_intersections( const Scalar p0_coords[Dim], + const Scalar p1_coords[Dim], + const Scalar center[Dim], + Scalar radius, + Scalar intersections[2] //output + ) ; + +/* Check whether a point lies in a triangle. + * If the bar_coords pointer is provided, it is filled with two scalars a and b + * such that the point a.x0 + b.x1 + (1-a-b).x2 is the point in the triangle + * x0,x1,x2 nearest to p. */ +template<int Dim, typename Scalar> +bool point_in_triangle( const Scalar p_coords[Dim], + const Scalar x0_coords[Dim], + const Scalar x1_coords[Dim], + const Scalar x2_coords[Dim], + Scalar bar_coords[2] = NULL + ) ; + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Tools/measure_def.hpp b/contrib/Revoropt/include/Revoropt/Tools/measure_def.hpp new file mode 100644 index 0000000000000000000000000000000000000000..223ad4ef646d1ad80f9a458f51a607906265f047 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Tools/measure_def.hpp @@ -0,0 +1,597 @@ +#ifndef _REVOROPT_TOOLS_MEASURE_DEF_HPP_ +#define _REVOROPT_TOOLS_MEASURE_DEF_HPP_ + +#include <eigen3/Eigen/Dense> + +#include "measure_fwd.hpp" +#include "intersections_def.hpp" + +namespace Revoropt { + +/*{{{ Triangle height and area */ + +/* Height of a triangle, passing through the first vertex provided */ +template<int Dim, typename Scalar> +Scalar triangle_height( const Scalar x0_p[Dim], + const Scalar x1_p[Dim], + const Scalar x2_p[Dim] + ) { + //vector type + typedef Eigen::Matrix<Scalar,Dim,1> Vector ; + + //vertices of the triangle + Eigen::Map<const Vector> x0(x0_p) ; + Eigen::Map<const Vector> x1(x1_p) ; + Eigen::Map<const Vector> x2(x2_p) ; + + //base of the triangle + const Vector base = x2-x1 ; + const Scalar base_len = base.norm() ; + if(base_len == 0) return (x0-x1).norm() ; + + //height of the triangle + Vector height = (x0-x1) ; + height -= height.dot(base)*base/(base_len*base_len) ; + return height.norm() ; +} + + +/* Area of a triangle */ +template<int Dim, typename Scalar> +Scalar triangle_area( const Scalar x0_p[Dim], + const Scalar x1_p[Dim], + const Scalar x2_p[Dim] + ) { + //vector type + typedef Eigen::Matrix<Scalar,Dim,1> Vector ; + + //vertices of the triangle + Eigen::Map<const Vector> x0(x0_p) ; + Eigen::Map<const Vector> x1(x1_p) ; + Eigen::Map<const Vector> x2(x2_p) ; + + //base of the triangle + const Vector base = x1-x0 ; + const Scalar base_len = base.norm() ; + if(base_len == 0) return 0 ; + + //height of the triangle + Vector height = (x2-x0) ; + height -= height.dot(base)*base/(base_len*base_len) ; + const Scalar height_len = height.norm() ; + + //area of the triangle + return 0.5*height_len*base_len ; +} + +//}}} + +/*{{{ Barycentric coordinates of a point whithin a triangle */ + +template<int Dim, typename Scalar> +Scalar triangle_barycentric_coords( const Scalar x0_p[Dim], + const Scalar x1_p[Dim], + const Scalar x2_p[Dim], + const Scalar p[Dim], + Scalar coords[Dim] //output + ) { + const Scalar area0 = triangle_area<Dim>(p, x1_p, x2_p) ; + const Scalar area1 = triangle_area<Dim>(x0_p, p, x2_p) ; + const Scalar area2 = triangle_area<Dim>(x0_p, x1_p, p) ; + const Scalar sum = area0 + area1 + area2 ; + coords[0] = area0 / sum ; + coords[1] = area1 / sum ; + coords[2] = area2 / sum ; +} + +//}}} + +/*{{{ Find a projection plane for points based on SVD */ + +template< typename Scalar, int Dim > +void svd_plane( + const Scalar* coords, + unsigned int size , + Scalar paramU[Dim], //output + Scalar paramV[Dim] //output + ) { + //std::cout << "Getting svd parametrisation" << std::endl ; + //vector type + typedef Eigen::Matrix<Scalar,Dim,1> Vector ; + typedef Eigen::Matrix<Scalar,Dim, Eigen::Dynamic> PtMatrix ; + + //std::cout << "Dim is " << Dim << std::endl ; + //std::cout << "Size is " << size << std::endl ; + + //centroid + Vector g = Vector::Zero() ; + for(unsigned int i = 0; i < size; ++i) { + Eigen::Map<const Vector> p(coords + Dim*i) ; + g += p ; + } + g /= size ; + + //point matrix + //std::cout << "Initializing matrix" << std::endl ; + PtMatrix M(Dim, size) ; + //std::cout << "Filling matrix" << std::endl ; + for(unsigned int i = 0; i < size; ++i) { + Eigen::Map<const Vector> p(coords + Dim*i) ; + M.col(i) = p-g ; + } + + //svd + //std::cout << "SVD" << std::endl ; + Eigen::JacobiSVD<PtMatrix> svd(M, Eigen::ComputeFullU) ; + //main eigenvectors + //std::cout << "Result" << std::endl ; + Eigen::Map<Vector> u(paramU) ; + u = svd.matrixU().col(0) ; + Eigen::Map<Vector> v(paramV) ; + v = svd.matrixU().col(1) ; + //std::cout << "u vector :" << std::endl << u << std::endl ; + //std::cout << "v vector :" << std::endl << v << std::endl ; +} + +//}}} + +/*{{{ Disc areas */ + +/* Area of a disc sector, defined by the disc radius r + * and the length d of the chord joining the extremities + * of the sector + * + * /\ + * / \ r + * / \ + * /___d__\ + * \______/ + */ +template<typename Scalar> +Scalar disc_sector_area( const Scalar radius, + const Scalar chord_len + ) { + //sine of half the sector angle + const Scalar d = chord_len/(2*radius) ; + //check whether the chord is a diameter of the circle + if(d >= 1) { + //half circle area + return 0.5*M_PI*radius*radius ; + } else { + //angle of the sector + Scalar halftheta = asin(d) ; + //area + return halftheta*radius*radius ; + } +} + +/* Area of a circular segment. The parameters are the same as above. */ +template<typename Scalar> +Scalar circle_segment_area( const Scalar radius, + const Scalar chord_len + ) { + //sine of half the sector angle + const Scalar d = chord_len/(2*radius) ; + //check whether the chord is a diameter of the circle + if(d >= 1) { + return 0.5*M_PI*radius*radius ; + } else { + //angle of the sector + Scalar theta = 2*asin(d) ; + //area + return 0.5*(theta-sin(theta))*radius*radius ; + } +} + +/* Triangle sphere intersection area */ +namespace triangle_sphere_intersection_area_impl { + +template<typename Scalar> +Scalar tsia_no_vertex_in_circle( const Scalar x0_coords[2], + const Scalar x1_coords[2], + const Scalar x2_coords[2], + const Scalar c_coords[2], + Scalar radius + ) { + //Vector type + typedef Eigen::Matrix<Scalar,2,1> Vector ; + + //radius and circle area + const Scalar circle_area = M_PI*radius*radius ; + + Eigen::Map<const Vector> c(c_coords) ; + const Scalar* x[3] = {x0_coords, x1_coords, x2_coords} ; + + //compute edge intersections, and sum the circle area out of the triangle + Scalar area_out = 0 ; + bool intersects = false ; + for(int i = 0; i < 3; ++i) { + //vertices of the edge + Eigen::Map<const Vector> v0(x[i]) ; + Eigen::Map<const Vector> v1(x[(i+1)%3]) ; + Eigen::Map<const Vector> v2(x[(i+2)%3]) ; + const Vector edge = (v1-v0) ; + + //barycentric coordinates of the circle / segment intersection + Scalar intersections[2] ; + const unsigned char ninter = segment_sphere_intersections<2>( + v0.data(), v1.data(), c.data(), radius, intersections + ) ; + + if(ninter > 0) { + intersects = true ; + //ninter can only be 2 since the two vertices of the edge are out + assert( (ninter == 2) + || "The edge intersects once with both vertices out." + ) ; + + //positions of the intersections + const Vector i0 = v0 + intersections[0]*edge ; + const Vector i1 = v0 + intersections[1]*edge ; + + //area of the piece of circle out of that edge + const Scalar segment_area = circle_segment_area(radius, (i0-i1).norm()) ; + + //check the side of the edge for the circle center + const Vector redge = v2-v0 ; + const Vector tedge = c-v0 ; + const char in_side = orientation(edge.data(), redge.data()) ; + if(orientation(edge.data(), tedge.data()) != in_side) { + area_out += circle_area - segment_area ; + } else { + area_out += segment_area ; + } + } + } + + assert(area_out == area_out) ; + + if(intersects) { + //the circle intersects edges, return the circle area minus the area out + return circle_area - area_out ; + } else { + //no edge intersects + if(point_in_triangle<2>(c_coords, x0_coords, x1_coords, x2_coords)) { + //the circle is completely in the triangle + return circle_area ; + } else { + //the circle is completely out of the triangle + return 0 ; + } + } +} + +/* The vertex inside is x0 */ +template<typename Scalar> +Scalar tsia_one_vertex_in_circle( const Scalar x0_coords[2], + const Scalar x1_coords[2], + const Scalar x2_coords[2], + const Scalar c_coords[2], + Scalar radius + ) { + //Vector type + typedef Eigen::Matrix<Scalar,2,1> Vector ; + + //radius and circle area + const Scalar circle_area = M_PI*radius*radius ; + + Eigen::Map<const Vector> c(c_coords) ; + Eigen::Map<const Vector> x0(x0_coords) ; + Eigen::Map<const Vector> x1(x1_coords) ; + Eigen::Map<const Vector> x2(x2_coords) ; + + //intersections with the edges adjacent to x0 + Scalar intersections[2] ; + unsigned char ninter ; + + ninter = segment_sphere_intersections<2>( + x0.data(), x1.data(), c.data(), radius, intersections + ) ; + assert( (ninter == 1) + || "x0 should be in the circle and x1 out." + ) ; + const Vector i01 = x0 + intersections[0]*(x1-x0) ; + + ninter = segment_sphere_intersections<2>( + x0.data(), x2.data(), c.data(), radius, intersections + ) ; + assert( (ninter == 1) + || "x0 should be in the circle and x2 out." + ) ; + const Vector i02 = x0 + intersections[0]*(x2-x0) ; + + //area without considering the last edge + Scalar area = triangle_area<2>(x0_coords, i01.data(), i02.data()) ; + const Vector iedge0 = i02 - i01 ; + const Vector redge0 = x0 - i01 ; + const Vector tedge0 = c - i01 ; + const char in_side0 = orientation(iedge0.data(), redge0.data()) ; + if(orientation(iedge0.data(), tedge0.data()) != in_side0) { + area += circle_area - circle_segment_area(radius, (iedge0).norm()) ; + } else { + area += circle_segment_area(radius, (iedge0).norm()) ; + } + + //check the intersection of the last edge + ninter = segment_sphere_intersections<2>( + x1.data(), x2.data(), c.data(), radius, intersections + ) ; + if(ninter > 0) { + //ninter can only be 2 since the two vertices of the edge are out + assert( (ninter == 2) + || "Edge intersects once with both vertices out." + ) ; + + //positions of the intersections + const Vector edge = (x2-x1) ; + const Vector i0 = x1 + intersections[0]*edge ; + const Vector i1 = x1 + intersections[1]*edge ; + + //area of the piece of circle out of that edge + const Scalar segment_area = circle_segment_area(radius, (i0-i1).norm()) ; + + //check the side of the edge for the circle center + const Vector redge = (x0-x1) ; + const Vector tedge = (c-x1) ; + const char in_side = orientation(edge.data(), redge.data()) ; + if(orientation(edge.data(), tedge.data()) != in_side) { + area -= circle_area - segment_area ; + } else { + area -= segment_area ; + } + } + assert(area == area) ; + //prevent warning + ninter += ninter ; + return area ; +} + +/* The vertices inside are x0 and x1 */ +template<typename Scalar> +Scalar tsia_two_vertices_in_circle( const Scalar x0_coords[2], + const Scalar x1_coords[2], + const Scalar x2_coords[2], + const Scalar c_coords[2], + Scalar radius + ) { + //Vector type + typedef Eigen::Matrix<Scalar,2,1> Vector ; + + //radius and circle area + const Scalar circle_area = M_PI*radius*radius ; + + Eigen::Map<const Vector> c(c_coords) ; + Eigen::Map<const Vector> x0(x0_coords) ; + Eigen::Map<const Vector> x1(x1_coords) ; + Eigen::Map<const Vector> x2(x2_coords) ; + + //intersections with the edges adjacent to x2 + Scalar intersections[2] ; + unsigned char ninter ; + + ninter = segment_sphere_intersections<2>( + x0.data(), x2.data(), c.data(), radius, intersections + ) ; + assert( (ninter == 1) + && "x0 should be in the circle and x2 out." + ) ; + const Vector i02 = x0 + intersections[0]*(x2-x0) ; + + ninter = segment_sphere_intersections<2>( + x1.data(), x2.data(), c.data(), radius, intersections + ) ; + assert( (ninter == 1) + && "x1 should be in the circle and x2 out." + ) ; + const Vector i12 = x1 + intersections[0]*(x2-x1) ; + + //area without considering the last edge + Scalar area = triangle_area<2>(x0_coords, i02.data(), i12.data()) ; + area += triangle_area<2>(x0_coords, x1_coords, i12.data()) ; + const Vector iedge = i12 - i02 ; + const Vector redge = x0 - i02 ; + const Vector tedge = c - i02 ; + const char in_side = orientation(iedge.data(), redge.data()) ; + if(orientation(iedge.data(), tedge.data()) != in_side) { + area += circle_area - circle_segment_area(radius, (iedge).norm()) ; + } else { + area += circle_segment_area(radius, (iedge).norm()) ; + } + assert(area == area) ; + + //prevent warning + ninter += ninter ; + return area ; +} + +template<typename Scalar> +Scalar tsia_three_vertices_in_circle( const Scalar x0_coords[2], + const Scalar x1_coords[2], + const Scalar x2_coords[2] + ) { + return triangle_area<2>(x0_coords, x1_coords, x2_coords) ; +} + +template< int Dim, typename Scalar > +class tsia_impl { + public: + static Scalar do_it( const Scalar x0_coords[Dim], + const Scalar x1_coords[Dim], + const Scalar x2_coords[Dim], + const Scalar c_coords[Dim], + Scalar radius + ) { + //vector type + typedef Eigen::Matrix<Scalar,Dim,1> Vector ; + + Eigen::Map<const Vector> c(c_coords) ; + Eigen::Map<const Vector> x0(x0_coords) ; + Eigen::Map<const Vector> x1(x1_coords) ; + Eigen::Map<const Vector> x2(x2_coords) ; + + //projection matrix + Eigen::Matrix<Scalar,2,Dim,Eigen::RowMajor> P ; + //base of the triangle + P.row(0) = x1-x0 ; + const Scalar base_len = P.row(0).norm() ; + if(base_len == 0) return 0 ; + P.row(0) /= base_len ; + + //height of the triangle + P.row(1) = x2-x0 ; + P.row(1) -= P.row(1).dot(P.row(0))*P.row(0) ; + const Scalar height_len = P.row(1).norm() ; + if(height_len == 0) return 0 ; + P.row(1) /= height_len ; + + //center projection vector + Vector cproj = c - x0 ; + + cproj -= cproj.dot(P.row(0))*P.row(0) + cproj.dot(P.row(1))*P.row(1) ; + const Scalar p_radius = sqrt(radius*radius - cproj.dot(cproj)) ; + + + //radius of the circular intersection of the sphere and the triangle plane + if(p_radius > 0) { + //the sphere intersects the triangle plane + //project enerything + Eigen::Matrix<Scalar,2,1> cp, x0p, x1p, x2p ; + cp = P*(c-x0) ; + x0p = Eigen::Matrix<Scalar,2,1>::Zero() ; + x1p = P*(x1-x0) ; + x2p = P*(x2-x0) ; + + //switch to 2D + return tsia_impl<2,Scalar>::do_it( x0p.data(), + x1p.data(), + x2p.data(), + cp.data(), + p_radius + ) ; + } else { + //the sphere does not intersect the triangle plane + return 0 ; + } + } +} ; + +template< typename Scalar > +class tsia_impl<2,Scalar> { + public: + static Scalar do_it( const Scalar x0_coords[2], + const Scalar x1_coords[2], + const Scalar x2_coords[2], + const Scalar c_coords[2], + Scalar radius + ) { + //check vertices inside + unsigned char vertex_status ; + vertex_status = point_in_sphere<2>(x0_coords, c_coords, radius) ; + vertex_status *= 2 ; + vertex_status += point_in_sphere<2>(x1_coords, c_coords, radius) ; + vertex_status *= 2 ; + vertex_status += point_in_sphere<2>(x2_coords, c_coords, radius) ; + + //handle the cases + if(vertex_status == 0) { + //no vertex_inside + return tsia_no_vertex_in_circle( x0_coords, + x1_coords, + x2_coords, + c_coords, + radius + ) ; + } else if(vertex_status == 1) { + //x2 inside + return tsia_one_vertex_in_circle( x2_coords, + x0_coords, + x1_coords, + c_coords, + radius + ) ; + } else if(vertex_status == 2) { + //x1 inside + return tsia_one_vertex_in_circle( x1_coords, + x2_coords, + x0_coords, + c_coords, + radius + ) ; + } else if(vertex_status == 3) { + //x1 and x2 inside + return tsia_two_vertices_in_circle( x1_coords, + x2_coords, + x0_coords, + c_coords, + radius + ) ; + } else if(vertex_status == 4) { + //x0 inside + return tsia_one_vertex_in_circle( x0_coords, + x1_coords, + x2_coords, + c_coords, + radius + ) ; + } else if(vertex_status == 5) { + //x0 and x2 inside + return tsia_two_vertices_in_circle( x2_coords, + x0_coords, + x1_coords, + c_coords, + radius + ) ; + } else if(vertex_status == 6) { + //x0 and x1 inside + return tsia_two_vertices_in_circle( x0_coords, + x1_coords, + x2_coords, + c_coords, + radius + ) ; + } else if(vertex_status == 7) { + //all vertices inside + return tsia_three_vertices_in_circle( x0_coords, + x1_coords, + x2_coords + ) ; + } + + return 0 ; + } +} ; + +} //end of namespace triangle_sphere_intersection_area_impl + +/* Triangle sphere intersection area, general case */ +template<int Dim, typename Scalar> +Scalar triangle_sphere_intersection_area( const Scalar x0_coords[Dim], + const Scalar x1_coords[Dim], + const Scalar x2_coords[Dim], + const Scalar c_coords[Dim], + Scalar radius + ) { + //bring in the handling of the various cases + using namespace triangle_sphere_intersection_area_impl ; + + return tsia_impl<Dim,Scalar>::do_it( x0_coords, + x1_coords, + x2_coords, + c_coords, + radius + ) ; +} + + +//}}} + +/*{{{ Angles */ + +template<int Dim, typename Scalar> +Scalar angle_cos( const Scalar p0[Dim], const Scalar p1[Dim], const Scalar p2[Dim]) ; + +//}}} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Tools/measure_fwd.hpp b/contrib/Revoropt/include/Revoropt/Tools/measure_fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e22a100cb2f9607fdd11606acfb8f2808e2b70bd --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Tools/measure_fwd.hpp @@ -0,0 +1,92 @@ +#ifndef _REVOROPT_TOOLS_MEASURE_FWD_HPP_ +#define _REVOROPT_TOOLS_MEASURE_FWD_HPP_ + +namespace Revoropt { + +/*{{{ Triangle height and area */ + +/* Height of a triangle, passing through the first vertex provided */ +template<int Dim, typename Scalar> +Scalar triangle_height( const Scalar x0_p[Dim], + const Scalar x1_p[Dim], + const Scalar x2_p[Dim] + ) ; + + +/* Area of a triangle */ +template<int Dim, typename Scalar> +Scalar triangle_area( const Scalar x0_p[Dim], + const Scalar x1_p[Dim], + const Scalar x2_p[Dim] + ) ; + +//}}} + +/*{{{ Barycentric coordinates of a point whithin a triangle */ + +template<int Dim, typename Scalar> +Scalar triangle_barycentric_coords( const Scalar x0_p[Dim], + const Scalar x1_p[Dim], + const Scalar x2_p[Dim], + const Scalar p[Dim], + Scalar coords[Dim] //output + ) ; + +//}}} + +/*{{{ Find a projection plane for points based on SVD */ + +template< typename Scalar, int Dim > +void svd_plane( + const Scalar* coords, + unsigned int size , + Scalar paramU[Dim], //output + Scalar paramV[Dim] //output + ) ; + +//}}} + +/*{{{ Disc areas */ + +/* Area of a disc sector, defined by the disc radius r + * and the length d of the chord joining the extremities + * of the sector + * + * /\ + * / \ r + * / \ + * /___d__\ + * \______/ + */ +template<typename Scalar> +Scalar disc_sector_area( const Scalar radius, + const Scalar chord_len + ) ; + +/* Area of a circular segment. The parameters are the same as above. */ +template<typename Scalar> +Scalar circle_segment_area( const Scalar radius, + const Scalar chord_len + ) ; + +/* Triangle sphere intersection area */ +template<int Dim, typename Scalar> +Scalar triangle_sphere_intersection_area( const Scalar x0_coords[Dim], + const Scalar x1_coords[Dim], + const Scalar x2_coords[Dim], + const Scalar c_coords[Dim], + Scalar radius + ) ; + +//}}} + +/*{{{ Angles */ + +template<int Dim, typename Scalar> +Scalar angle_cos( const Scalar p0[Dim], const Scalar p1[Dim], const Scalar p2[Dim]) ; + +//}}} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Tools/meta.hpp b/contrib/Revoropt/include/Revoropt/Tools/meta.hpp new file mode 100644 index 0000000000000000000000000000000000000000..11ccb1dff08f1b8cf2ed1b24efc20ed032fa1d83 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Tools/meta.hpp @@ -0,0 +1,221 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_TOOLS_META_HPP_ +#define _REVOROPT_TOOLS_META_HPP_ + +#include <iostream> +#include <type_traits> + +namespace Revoropt { + +/* test whether the operator<< for a type exists */ + +namespace is_printable_impl { + + typedef char yes[1] ; + typedef char no[2] ; + + struct any_t { + template<typename T> any_t( const T& ) ; + } ; + + no& operator<<(std::ostream const&, const any_t&) ; + + yes& test( std::ostream& ) ; + no& test( no& ) ; + + template<typename T> + struct is_printable { + static std::ostream &s ; + static const T& t ; + static const bool value = sizeof(test((s<<t))) == sizeof(yes) ; + } ; +} + +template<typename T> +struct is_printable : is_printable_impl::is_printable<T> { +} ; + +/* automatic dereferencement */ +/* here be dragons */ + +template <typename T> +class is_dereferenceable +{ + //used to check which of the test methods below is used + //in the definition of the value public attribute + typedef char yes[1] ; + typedef char no[2] ; + + //this class is only instanciated when *C is legal + template <typename C, int sz = sizeof(**static_cast<C*>(NULL))> + struct check {} ; + + //this method can only be instanciated if *C is legal + template <typename C> static yes &test( check<C>* ) ; + //this method is always instanciated + template <typename C> static no &test( ... ) ; + + public: + //if the size of the return type of the test function used here is yes + //the first test function was instanciated, and therefore *T is legal + static bool const value = sizeof(test<T>(0)) == sizeof(yes) ; +} ; + +template<typename T> +class is_dereferenceable<T&> +{ + public: + static bool const value = is_dereferenceable<T>::value ; +} ; + +/* base type of a pointing chain */ + +//one dereferencement +//basic case, fallback when T is non dereferenceable : no type field +template<typename T, bool chk = false> +struct one_deref_type_impl { +} ; + +//case when T is a reference non dereferenceable +template<typename T> +struct one_deref_type_impl<T&,false> { +} ; + +//basic case for dereferenceables +//only works for (references to) iterators and pointers +template<typename T> +struct one_deref_type_impl<T,true> { + typedef typename one_deref_type_impl< + typename std::iterator_traits<T>::pointer, + true + >::type type ; +} ; + +//case for pointers +template<typename T> +struct one_deref_type_impl<T*,true> { + typedef T type ; +} ; + +//case for references to dereferenceables +template<typename T> +struct one_deref_type_impl<T&,true> { + typedef typename one_deref_type_impl<T,true>::type type ; +} ; + +//case for const dereferenceables +template<typename T> +struct one_deref_type_impl<T const,true> { + typedef typename one_deref_type_impl<T,true>::type type ; +} ; + +//encapsulation to mask the boolean attribute, and prevent bad usage : +//providing true with T being not dereferenceable +template<typename T> +struct one_deref_type { + //use the implementation backend + typedef typename one_deref_type_impl< + T, + is_dereferenceable<T>::value + >::type type ; +} ; + +//full dereferencement +//basic case, fallback when T is non dereferenceable +template<typename T, bool chk = false> +struct full_deref_type_impl { + typedef T type ; +} ; + +//case when T is dereferenceable +template<typename T> +struct full_deref_type_impl<T, true> { + //get the basic type of T dereferenced once + typedef typename full_deref_type_impl< + //define the type of T dereferenced once + typename one_deref_type<T>::type, + //check whether T dereferenced once is still dereferenceable + is_dereferenceable<typename one_deref_type<T>::type>::value + >::type type ; +} ; + +//encapsulation to mask the boolean attribute, and prevent bad usage : +//providing true with T being not dereferenceable +template<typename T> +struct full_deref_type { + //use the implementation backend + typedef typename full_deref_type_impl< + T, + is_dereferenceable<T>::value + >:: type type ; +} ; + +/* full dereferencement */ + +//basic case, fallback when T us not dereferenceable +template<typename T, bool chk = true> +class full_deref_impl { + public: + //take a reference to the value provided to the constructor + full_deref_impl( T& _value ) : value(_value) {} ; + T& value ; +} ; + +//case when T is dereferenceable +template<typename T> +class full_deref_impl<T,true> { + //define the type of T dereferenced once + //only works for pointers and iterators + typedef typename one_deref_type<T>::type deref_type ; + public: + //get the fully dereferenced value of _value dereferenced once + full_deref_impl( T _value ) : value( + //recursive call + full_deref_impl< + deref_type, + //check that T dereferenced once is stille dereferenceable + is_dereferenceable<deref_type>::value + >(*_value).value + ) {} ; + //reference on the fully dereferenced value + typename full_deref_type<T>::type& value ; +} ; + +//encapsulation in a function, performing full dereferencement, +//masking the template parameter by automatic deduction + +//case when the provided type is dereferenceable : no reference +template<typename T> +typename std::enable_if< + is_dereferenceable<T>::value, + typename full_deref_type<T>::type& +>::type full_deref( T dereferenceable ) { + //use the implementation backend + return full_deref_impl< + T, + is_dereferenceable<T>::value + >(dereferenceable).value ; +} + +//case when the provided type is not dereferenceable : reference +template<typename T> +typename std::enable_if< + !is_dereferenceable<T>::value, + T& +>::type full_deref( T& non_dereferenceable ) { + //use the implementation backend + return non_dereferenceable ; +} + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Tools/normals_def.hpp b/contrib/Revoropt/include/Revoropt/Tools/normals_def.hpp new file mode 100644 index 0000000000000000000000000000000000000000..410fb4a627a6caf4af97c934e95316ecec79d215 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Tools/normals_def.hpp @@ -0,0 +1,60 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_TOOLS_NORMALS_DEF_HPP_ +#define _REVOROPT_TOOLS_NORMALS_DEF_HPP_ + +#include <eigen3/Eigen/Dense> + +/* Normal of a triangle, with the triangle area as norm */ +template<typename Scalar> +void triangle_area_normal( const Scalar* x0_p, + const Scalar* x1_p, + const Scalar* x2_p, + Scalar output[3] + ) { + //vector type + typedef Eigen::Matrix<Scalar,3,1> Vector ; + //map vertices to eigen vectors + Eigen::Map< const Vector> x0(x0_p) ; + Eigen::Map< const Vector> x1(x1_p) ; + Eigen::Map< const Vector> x2(x2_p) ; + + //map output to an eigen vector + Eigen::Map<Vector> normal(output) ; + + //compute + normal = 0.5*(x1-x0).cross(x2-x0) ; +} + +/* Normalized normal of a triangle, null if the triangle is flat */ +template<typename Scalar> +Scalar triangle_normal( const Scalar* x0_p, + const Scalar* x1_p, + const Scalar* x2_p, + Scalar output[3] + ) { + //vector type + typedef Eigen::Matrix<Scalar,3,1> Vector ; + //get the area normal + triangle_area_normal(x0_p,x1_p,x2_p,output) ; + + //map output to an eigen vector + Eigen::Map<Vector> normal(output) ; + + //norm of the normal + const Scalar n_len = normal.norm() ; + if(n_len != 0) { + normal /= n_len ; + } + return n_len ; +} + +#endif diff --git a/contrib/Revoropt/include/Revoropt/Tools/normals_fwd.hpp b/contrib/Revoropt/include/Revoropt/Tools/normals_fwd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f5d550ee56f6cce513e38d9e02a4979c1062ced2 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/Tools/normals_fwd.hpp @@ -0,0 +1,30 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_TOOLS_NORMALS_FWD_HPP_ +#define _REVOROPT_TOOLS_NORMALS_FWD_HPP_ + +/* Normal of a triangle, with the triangle area as norm */ +template<typename Scalar> +void triangle_area_normal( const Scalar* x0_p, + const Scalar* x1_p, + const Scalar* x2_p, + Scalar output[3] + ) ; + +/* Normalized normal of a triangle, null if the triangle is flat */ +template<typename Scalar> +Scalar triangle_normal( const Scalar* x0_p, + const Scalar* x1_p, + const Scalar* x2_p, + Scalar output[3] + ) ; + +#endif diff --git a/contrib/Revoropt/include/Revoropt/VSDM/.gitignore b/contrib/Revoropt/include/Revoropt/VSDM/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..dc221b0db2dbcc2d02a2d77125f50bf3e588ac74 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/VSDM/.gitignore @@ -0,0 +1,2 @@ +VSDMRegul.os +VSDMSampler.os diff --git a/contrib/Revoropt/include/Revoropt/VSDM/minimizer.hpp b/contrib/Revoropt/include/Revoropt/VSDM/minimizer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..cadbf1951d90ab0a3d83efcec27235ac80898897 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/VSDM/minimizer.hpp @@ -0,0 +1,290 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_VSDM_MINIMIZER_HPP_ +#define _REVOROPT_VSDM_MINIMIZER_HPP_ + +#include "regul.hpp" +#include "sampler.hpp" + +#include <Revoropt/RVD/rvd.hpp> +#include <Revoropt/CVT/minimizer.hpp> +#include <Revoropt/Mesh/base_def.hpp> +#include <Revoropt/Mesh/wrapper_def.hpp> + +#include <vector> + +namespace Revoropt { + +namespace VSDM { + +enum SamplerSetting { + SAMPLER_NONE, + SAMPLER_MIDFACE +} ; + +enum DiffSamplerSetting { + DIFF_SAMPLER_NONE, + DIFF_SAMPLER_MIDFACE +} ; + +template< typename TMesh, typename SMesh> +class ObjFun { + + public : + + enum {Dim = TMesh::VertexDim} ; + typedef ROMeshWrapper< + SMesh::FaceSize, + SMesh::VertexDim, + typename SMesh::Scalar, + SMesh::VertexOffset + > SMeshWrapper ; + + ObjFun( + bool reset_gradient = true + ) : + direct_obj_fun_(false), + inverse_obj_fun_(false), + regul_obj_fun_(false), + target_sampler_(NULL), + source_sampler_(NULL), + global_factor_(1), + direct_factor_(1), + inverse_factor_(0), + regul_factor_(1), + reset_gradient_(reset_gradient) + {} + + ~ObjFun() { + delete target_sampler_ ; + delete source_sampler_ ; + } + + /* Callback to compute the objective function */ + + template< typename Data > + double operator()( Data* data ) { + if(reset_gradient_) { + std::fill(data->g, data->g+data->n,0) ; + } + + double fx = 0 ; + if(global_factor_ > 0) { + if(inverse_factor_ > 0) { + fx += compute_inverse_CVT(data) ; + } + if(regul_factor_ > 0) { + fx += compute_regul(data) ; + } + if(direct_factor_ > 0) { + fx += compute_direct_CVT(data) ; + } + } + return fx ; + } + + /* Weighting the terms of the objective function */ + + void set_global_factor( double factor ) { + global_factor_ = factor ; + } + void set_direct_factor( double factor ) { + direct_factor_ = factor ; + } + void set_inverse_factor( double factor ) { + inverse_factor_ = factor ; + } + void set_regul_factor( double factor ) { + regul_factor_ = factor ; + } + + /* Mesh setting */ + + void set_target(const TMesh* mesh) { + target_mesh_ = mesh ; + direct_obj_fun_.set_mesh(target_mesh_) ; + if(target_sampler_) { + target_sampler_->set_mesh(target_mesh_) ; + inverse_obj_fun_.set_sites( + target_sampler_->samples(), + target_sampler_->sample_size() + ) ; + } else { + inverse_obj_fun_.set_sites( + target_mesh_->vertices(), + target_mesh_->vertices_size() + ) ; + } + } + + void set_source(SMesh* mesh) { + source_mesh_.wrap(mesh) ; + variables_ = mesh->vertices() ; + variables_size_ = Dim * mesh->vertices_size() ; + if(source_sampler_) { + source_sampler_->set_mesh(&source_mesh_) ; + } + regul_obj_fun_.build_matrix(&source_mesh_) ; + } + + /* CVT parameters */ + + //FIXME + ////TODO handle inverse anisotropy + //void set_anisotropy( double anisotropy ) ; + ////TODO use a separate edge RVD on edge meshes for boundary fitting + //void set_border_weight( double border_weight ) ; + + /* Sampler setting */ + + void set_target_sampler( SamplerSetting sampler_type ) { + //cleanup previous sampler + delete target_sampler_ ; + + switch(sampler_type){ + case SAMPLER_NONE: + { + //none defaults to vertex sampling, which is specially treated + target_sampler_ = NULL ; + break ; + } + case SAMPLER_MIDFACE: + { + //one sample per face center + target_sampler_ = new MidFaceSampler<TMesh> ; + break ; + } + } + + if(target_sampler_ && target_mesh_) { + target_sampler_->set_mesh(target_mesh_) ; + } + } + + void set_source_sampler( DiffSamplerSetting diff_sampler_type ) { + //cleanup previous sampler + delete source_sampler_ ; + + switch(diff_sampler_type){ + case DIFF_SAMPLER_NONE: + { + //none defaults to vertex sampling, which is specially treated + source_sampler_ = NULL ; + break ; + } + case DIFF_SAMPLER_MIDFACE: + { + //one sample per face center + source_sampler_ = new MidFaceSampler<SMeshWrapper> ; + break ; + } + } + + if(source_sampler_) { + source_sampler_->set_mesh(&source_mesh_) ; + } + } + + /* minimization */ + template<typename Solver> + int minimize( unsigned int iterations ) { + Solver solver ; + return solver.solve(variables_, variables_size_, this, iterations) ; + } + + template<typename Solver, typename IterCallback> + int minimize( unsigned int iterations, IterCallback* iter_callback ) { + Solver solver ; + return solver.solve(variables_, variables_size_, this, iterations, iter_callback) ; + } + + private : + + /*Computing the different terms */ + template<typename Data> + double compute_direct_CVT( Data* data ) { + direct_obj_fun_.set_factor(global_factor_*direct_factor_) ; + if(!source_sampler_) { + //no sampling, use default behaviour + direct_obj_fun_.set_sites(data->x, data->n/Dim) ; + return direct_obj_fun_(data->g) ; + } else { + //a sampler is used + source_mesh_.set_vertices(data->x, data->n/Dim) ; + source_sampler_->set_mesh(&source_mesh_) ; + //reset the gradient wrt the samples + source_sample_grad_.assign(Dim*source_sampler_->sample_size(), 0) ; + //compute the gradient and value wrt. the samples + direct_obj_fun_.set_sites( + source_sampler_->samples(), + source_sampler_->sample_size() + ) ; + double fx = direct_obj_fun_( + source_sample_grad_.data() + ) ; + //chain to the gradient wrt. the template mesh vertices + source_sampler_->chain_derivative( + source_sample_grad_.data(), + data->g, + false + ) ; + //return function value + return fx ; + } + } + + template<typename Data> + double compute_inverse_CVT( Data* data ) { + inverse_obj_fun_.set_factor(global_factor_*inverse_factor_) ; + source_mesh_.set_vertices(data->x, data->n/Dim) ; + inverse_obj_fun_.set_mesh(&source_mesh_) ; + return inverse_obj_fun_(data->g) ; + } + + template<typename Data> + double compute_regul( Data* data ) { + regul_obj_fun_.set_factor(global_factor_*regul_factor_) ; + return regul_obj_fun_(data) ; + } + + /* Meshes */ + const TMesh* target_mesh_ ; + SMeshWrapper source_mesh_ ; + + /* Variables */ + double* variables_ ; + unsigned int variables_size_ ; + + /* Objective functions */ + CVT::DirectObjFun<TMesh> direct_obj_fun_ ; + CVT::InverseObjFun<SMeshWrapper> inverse_obj_fun_ ; + RegulTerm<Dim> regul_obj_fun_ ; + + /* Samplers */ + Sampler<TMesh>* target_sampler_ ; + DiffSampler<SMeshWrapper>* source_sampler_ ; + std::vector<double> source_sample_grad_ ; + + /* Parameters */ + double global_factor_ ; + double direct_factor_ ; + double inverse_factor_ ; + double regul_factor_ ; + + bool reset_gradient_ ; + +} ; + +} //end of namespace VSDM + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/VSDM/regul.hpp b/contrib/Revoropt/include/Revoropt/VSDM/regul.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a49e641284dc630e2d2b536da40c62dcff06f69c --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/VSDM/regul.hpp @@ -0,0 +1,167 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_VSDM_REGUL_HPP_ +#define _REVOROPT_VSDM_REGUL_HPP_ + +#include <Revoropt/RVD/rvd.hpp> + +#include <eigen3/Eigen/Dense> +#include <eigen3/Eigen/Sparse> + +namespace Revoropt { + +namespace VSDM { + +//add the opportunity to modify the value of an Eigen::Triplet +class my_triplet : public Eigen::Triplet<double> { + + public: + + my_triplet(unsigned int i, unsigned int j, double value) : + Eigen::Triplet<double>(i,j,value) { + } + + //incrementation + void incr_value() { + ++m_value ; + } + //new value + void set_value( double value ) { + m_value = value ; + } +} ; + +template<int Dim> +class RegulTerm { + + public : + + RegulTerm(bool reset_gradient = true) : + reset_gradient_(reset_gradient), + factor_(1) + {} + + /* Change the factor value. The default factor is 1/vsize where vsize is the + * number of vertices of the mesh. The provided factor will be divided by + * vsize as well.*/ + template<typename Mesh> + void build_matrix(const Mesh* mesh) { + //Size of the matrix + unsigned int vsize = mesh->vertices_size() ; + + //Use triplets to encode a sparse matrix + std::vector<my_triplet> triplets ; + //1 coeff per vertex plus 2 per edge. At least 3*face_size/2 edges. + triplets.reserve(vsize + 3*mesh->faces_size()) ; + //the first triplets correspond to the vertices of the mesh + for(unsigned int i = 0; i < vsize; ++i) { + //assign the col and row of the diagonal coeffs + triplets.push_back(my_triplet(i,i,0)) ; + } + //Build the quatratic form matrix + for(unsigned int f = 0; f < mesh->faces_size(); ++f) { + //face data + const unsigned int fsize = mesh->face_size(f) ; + const unsigned int* fverts = mesh->face(f) ; + const unsigned int* fneigh = mesh->face_neighbours(f) ; + + //iterate on edges to build edge coeffs and count edges for a vertex + for(unsigned int v = 0; v < fsize; ++v) { + //get the vertices of the edge + unsigned int v1 = fverts[ v ] ; + unsigned int v2 = fverts[(v+1)%fsize] ; + + //to avoid treating edges twice, check canonical order + if(f < fneigh[v]) { + //increase vertex neighbourhood size + triplets[v1].incr_value() ; + triplets[v2].incr_value() ; + + //add coefficients + triplets.push_back(my_triplet(v1,v2,1)) ; + triplets.push_back(my_triplet(v2,v1,1)) ; + } + } + } + + //Divide each edge coeff by the vertex neighbourhood size corresponding to its vertex + for(unsigned int i = vsize; i < triplets.size(); ++i) { + my_triplet& coeff = triplets[i] ; + coeff.set_value(coeff.value()/triplets[coeff.row()].value()) ; + } + + //Reset diagonal coeffs to -1 + for(unsigned int i = 0; i < vsize; ++i) { + //std::cout << "Diagonal triplet has value " << triplets[i].value() << std::endl ; + triplets[i].set_value(-1) ; + } + + //Build matrix + L_.resize(vsize, vsize) ; + L_.setFromTriplets(triplets.begin(), triplets.end()) ; + } + + void set_factor( double factor ) { + factor_ = factor ; + } + + template<typename Data> + double operator()( Data* data ) { + if(reset_gradient_) { + std::fill(data->g, data->g+data->n,0) ; + } + + double fx = 0 ; + + //accumulate result for each coordinate + for(int i = 0; i < Dim; ++i) { + //Map the raw pointer data to Eigen vector + Eigen::Map< Eigen::VectorXd, + Eigen::Unaligned, + Eigen::Stride<0,Dim> + > g(data->g+i, data->n/Dim) ; + Eigen::Map< const Eigen::VectorXd, + Eigen::Unaligned, + Eigen::Stride<0,Dim> + > x(data->x+i, data->n/Dim) ; + //std::cout << "X norm is " << x.norm() << std::endl ; + + //Compute function falue + const Eigen::VectorXd Lx = L_*x ; + //std::cout << "LX norm is " << Lx.norm() << std::endl ; + fx += Lx.dot(Lx) ; + //std::cout << "fx is " << fx << std::endl ; + //std::cout << "factor is " << factor_ << std::endl ; + + //Compute gradient, normalized by the vertex size + g.noalias() = g + 2*Dim*factor_*L_.transpose()*Lx/data->n ; + } + + //Return function value normalized by vertex size + return Dim*factor_*fx/data->n ; + } + + private : + /* indicates whether the provided gradient should be reset to 0 */ + bool reset_gradient_ ; + + /* factor weighting the objective function, initialized to 1 */ + double factor_ ; + + /* Matrix storing the neighbourhoods */ + Eigen::SparseMatrix<double> L_ ; +} ; + +} //end of namespace VSDM + +} //end of namespace Revoropt + +#endif diff --git a/contrib/Revoropt/include/Revoropt/VSDM/sampler.hpp b/contrib/Revoropt/include/Revoropt/VSDM/sampler.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9cae83c3f30c628d7cf0a5ba0be47660e8f182d4 --- /dev/null +++ b/contrib/Revoropt/include/Revoropt/VSDM/sampler.hpp @@ -0,0 +1,263 @@ +// @licstart revoropt +// This file is part of Revoropt, a library for the computation and +// optimization of restricted Voronoi diagrams. +// +// Copyright (C) 2013 Vincent Nivoliers <vincent.nivoliers@univ-lyon1.fr> +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// @licend revoropt +#ifndef _REVOROPT_VSDM_SAMPLER_H_ +#define _REVOROPT_VSDM_SAMPLER_H_ + +#include <eigen3/Eigen/Sparse> +#include <vector> + +namespace Revoropt { + +namespace VSDM { + +/** Generic sampler classes **/ + +/* Sample a mesh */ + +template<typename Mesh> +class Sampler { + + public : + + virtual ~Sampler() {} + + /** Typedefs **/ + typedef typename Mesh::Scalar Scalar ; + enum { Dim = Mesh::VertexDim } ; + + /** Construction **/ + + /* Set the mesh to sample */ + virtual void set_mesh( const Mesh* mesh ) = 0 ; + + /** Access **/ + + /* Provides the sample points. If no mesh was set, returns NULL */ + const double* samples() const { return samples_.data() ; } + double* samples() { return samples_.data() ; } + unsigned int sample_size() const { return samples_.size()/Dim ; } + + protected : + + /* Samples */ + std::vector<double> samples_ ; + +} ; + +/* Sample and compute the derivatives of the samples wrt. the mesh vertices */ + +template<typename Mesh> +class DiffSampler : public Sampler<Mesh> { + + protected : + + typedef Sampler<Mesh> Base ; + using Base::Scalar ; + using Base::Dim ; + using Base::samples_ ; + + public : + + using Base::samples ; + using Base::sample_size ; + + virtual ~DiffSampler() {} + + /** Differenciation **/ + + /* Transfer a derivative wrt the samples to a derivative wrt. the vertices */ + + virtual void chain_derivative( + const double* sample_derivative, + double* vertex_derivative, + bool reset_output = true + ) const = 0 ; + +} ; + +/* Linearly sample a mesh */ + +template<typename Mesh> +class LinearSampler : public DiffSampler<Mesh> { + + protected : + + typedef DiffSampler<Mesh> Base ; + using Base::Scalar ; + using Base::Dim ; + using Base::samples_ ; + + public : + + using Base::samples ; + using Base::sample_size ; + + virtual ~LinearSampler() {} + + virtual void set_mesh( const Mesh* mesh ) { + //map the sample coordinates in a sample_size_ x 3 matrix + Eigen::Map< Eigen::Matrix<double,Eigen::Dynamic,Dim>, + Eigen::Unaligned, + Eigen::Stride<1,Dim> + > sample_coord(samples(), sample_size(), Dim) ; + + //map the mesh vertex coordinates in a vertex_size x 3 matrix + Eigen::Map< const Eigen::Matrix<double,Eigen::Dynamic,Dim>, + Eigen::Unaligned, + Eigen::Stride<1,Dim> + > vertex_coord(mesh->vertices(), mesh->vertices_size(), Dim) ; + + //apply linear combinations + sample_coord = S_*vertex_coord ; + } + + /** Differenciation **/ + + void chain_derivative( + const double* sample_derivative, + double* vertex_derivative, + bool reset_output = true + ) const { + //map the sample coordinates in a sample_size_ x 3 matrix + Eigen::Map< + const Eigen::Matrix<double,Eigen::Dynamic,Dim>, + Eigen::Unaligned, + Eigen::Stride<1,Dim> + > sdiff(sample_derivative, sample_size(), Dim) ; + + //map the mesh vertex coordinates in a vertex_size x 3 matrix + Eigen::Map< + Eigen::Matrix<double,Eigen::Dynamic,Dim>, + Eigen::Unaligned, + Eigen::Stride<1,Dim> + > vdiff(vertex_derivative, S_.cols(), Dim) ; + + //apply linear combinations backwards + if(reset_output) { + vdiff = S_.transpose()*sdiff ; + } else { + vdiff.noalias() = vdiff + S_.transpose()*sdiff ; + } + } + + + protected : + + /* Linear coefficients are stored as a sparse matrix */ + Eigen::SparseMatrix<double> S_ ; + +} ; + +/** Some particular samplers **/ + +/* Vertex sampler */ + +template<typename Mesh> +class VertexSampler : public DiffSampler<Mesh> { + + protected : + + typedef DiffSampler<Mesh> Base ; + using Base::Scalar ; + using Base::Dim ; + using Base::samples_ ; + + public : + + using Base::samples ; + using Base::sample_size ; + + virtual ~VertexSampler() {} + + /* set the mesh to sample */ + void set_mesh( const Mesh* mesh ) { + samples_.assign( + mesh->vertices(), + mesh->vertices() + Dim * mesh->vertices_size() + ) ; + } + + /** Differenciation **/ + + void chain_derivative( const double* sample_derivative, + double* vertex_derivative, + bool reset_output = true + ) const { + std::copy( + sample_derivative, + sample_derivative + Dim * sample_size(), + vertex_derivative + ) ; + } + +} ; + +/* Mid face sampler : one sample per face, at the centroid */ + +template<typename Mesh> +class MidFaceSampler : public LinearSampler<Mesh> { + + protected : + + typedef LinearSampler<Mesh> Base ; + using Base::Scalar ; + using Base::Dim ; + using Base::samples_ ; + using Base::S_ ; + + public : + + using Base::samples ; + using Base::sample_size ; + + virtual ~MidFaceSampler() {} + + /* set the mesh to sample */ + void set_mesh( const Mesh* mesh ) { + //the number of samples is the number of faces + samples_.resize(Dim * mesh->faces_size()) ; + + //generate the matrix with the linear combinations + std::vector< Eigen::Triplet<double> > triplets ; + //each sample linearly depends at least on three vertices + triplets.reserve(3*sample_size()) ; + + //generate the coeffs per face/sample + for(unsigned int f = 0; f < mesh->faces_size(); ++f) { + //size of the face + const unsigned int fsize = mesh->face_size(f) ; + //recover the vertices of the face + const unsigned int* fverts = mesh->face(f) ; + //barycentric coeff for this face + const double coeff = 1./fsize ; + //buid one coeff per vertex + for(unsigned int v = 0; v < fsize; ++v) { + triplets.push_back( + Eigen::Triplet<double>(f,fverts[v],coeff) + ) ; + } + } + + //assemble the matrix from the triplets + S_.resize(sample_size(),mesh->vertices_size()) ; + S_.setFromTriplets(triplets.begin(), triplets.end()) ; + + //call parent method + Base::set_mesh(mesh) ; + } + +} ; + +} //end of namespace VSDM + +} //end of namespace Revoropt + +#endif