Skip to content
Snippets Groups Projects
Select Git revision
  • gmsh_4_12_2
  • master default protected
  • hierarchical-basis
  • alphashapes
  • bl
  • relaying
  • new_export_boris
  • oras_vs_osm
  • reassign_partitions
  • distributed_fwi
  • rename-classes
  • fix/fortran-api-example-t4
  • robust_partitions
  • reducing_files
  • fix_overlaps
  • 3115-issue-fix
  • 3023-Fillet2D-Update
  • convert_fdivs
  • tmp_jcjc24
  • fixedMeshIF
  • save_edges
  • gmsh_4_14_0
  • gmsh_4_13_1
  • gmsh_4_13_0
  • gmsh_4_12_1
  • gmsh_4_12_0
  • gmsh_4_11_1
  • gmsh_4_11_0
  • gmsh_4_10_5
  • gmsh_4_10_4
  • gmsh_4_10_3
  • gmsh_4_10_2
  • gmsh_4_10_1
  • gmsh_4_10_0
  • gmsh_4_9_5
  • gmsh_4_9_4
  • gmsh_4_9_3
  • gmsh_4_9_2
  • gmsh_4_9_1
  • gmsh_4_9_0
40 results

t3.jl

Blame
  • CenterlineField.cpp 42.30 KiB
    // 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>.
    //
    // Contributor(s):
    //   Emilie Marchandise
    //
    
    #include "CenterlineField.h"
    
    #include <vector>
    #include <map>
    #include <set>
    #include <list>
    #include <algorithm>
    #include <string>
    #include <fstream>
    #include <sstream>
    #include <iostream>
    #include <math.h>
    #include "OS.h"
    #include "GModel.h"
    #include "MElement.h"
    #include "MTriangle.h"
    #include "MVertex.h"
    #include "MLine.h"
    #include "StringUtils.h"
    #include "GEntity.h"
    #include "Field.h"
    #include "GFace.h"
    #include "discreteEdge.h"
    #include "discreteFace.h"
    #include "GEdgeCompound.h"
    #include "GFaceCompound.h"
    #include "BackgroundMesh.h"
    #include "meshGFace.h"
    #include "meshGEdge.h"
    #include "MQuadrangle.h"
    #include "Curvature.h"
    #include "MElement.h"
    #include "Context.h"
    #include "directions3D.h"
    #include "meshGRegion.h"
    
    #if defined(HAVE_ANN)
    #include <ANN/ANN.h>
    
    
    static void erase(std::vector<MLine*>& lines, MLine* l)
    {
      std::vector<MLine*>::iterator it = std::remove(lines.begin(), lines.end(), l);
      lines.erase(it, lines.end());
    }
    
    static double computeLength(std::vector<MLine*> lines)
    {
      double length= 0.0;
      for (unsigned int i = 0; i< lines.size(); i++){
        length += lines[i]->getLength();
      }
      return length;
    }
    
    static bool isClosed(std::set<MEdge, Less_Edge> &theCut)
    {
      std::multiset<MVertex*> boundV;
      std::set<MEdge,Less_Edge>::iterator it = theCut.begin();
      for (; it!= theCut.end(); it++){
        boundV.insert(it->getVertex(0));
        boundV.insert(it->getVertex(1));
      }
      std::multiset<MVertex*>::iterator itb = boundV.begin();
      for ( ; itb != boundV.end(); ++itb){
        if (boundV.count(*itb) != 2) {
          return false;
        }
      }
      return true;
    }
    
    static void orderMLines(std::vector<MLine*> &lines, MVertex *vB, MVertex *vE)
    {
      std::vector<MLine*> _m;
      std::list<MLine*> segments;
      std::map<MVertex*, MLine*> boundv;
      std::vector<int> _orientation;
    
      // store all lines in a list : segments
      for (unsigned int i = 0; i < lines.size(); i++){
        segments.push_back(lines[i]);
      }
    
      // find a lonely MLine
      for (std::list<MLine*>::iterator it = segments.begin();
           it != segments.end(); ++it){
        MVertex *vL = (*it)->getVertex(0);
        MVertex *vR = (*it)->getVertex(1);
        std::map<MVertex*,MLine*>::iterator it1 = boundv.find(vL);
        if (it1 == boundv.end()) boundv.insert(std::make_pair(vL,*it));
        else  boundv.erase(it1);
        std::map<MVertex*,MLine*>::iterator it2 = boundv.find(vR);
        if (it2 == boundv.end()) boundv.insert(std::make_pair(vR,*it));
        else boundv.erase(it2);
      }
    
      // find the first MLine and erase it from the list segments
      MLine *firstLine;
      if (boundv.size() == 2){   // non periodic
        MVertex *v = (boundv.begin())->first;
        if ( v == vB) firstLine = (boundv.begin())->second;
        else{
          MVertex *v = (boundv.rbegin())->first;
          if (v == vB) firstLine = (boundv.rbegin())->second;
          else{
            Msg::Error("begin vertex not found for branch");
            return;
          }
        }
        for (std::list<MLine*>::iterator it = segments.begin();
             it != segments.end(); ++it){
          if (*it == firstLine){
            segments.erase(it);
            break;
          }
        }
      }
      else{
        Msg::Error("line is wrong (it has %d end points)",  boundv.size());
      }
    
      // loop over all segments to order segments and store it in the list _m
      _m.push_back(firstLine);
      _orientation.push_back(1);
      MVertex *first = _m[0]->getVertex(0);
      MVertex *last = _m[0]->getVertex(1);
      while (first != last){
        if (segments.empty())break;
        bool found = false;
        for (std::list<MLine*>::iterator it = segments.begin();
             it != segments.end(); ++it){
          MLine *e = *it;
          if (e->getVertex(0) == last){
            _m.push_back(e);
            segments.erase(it);
            _orientation.push_back(1);
            last = e->getVertex(1);
            found = true;
            break;
          }
          else if (e->getVertex(1) == last){
            _m.push_back(e);
            segments.erase(it);
            _orientation.push_back(0);
            last = e->getVertex(0);
            found = true;
            break;
          }
        }
        if (!found  && _orientation[0]==1){ //reverse orientation of first Line
          if (_m.size() == 1 ){
            MVertex *temp = first;
            first = last;
            last = temp;
            _orientation[0] = 0;
          }
          else {
            Msg::Error("lines is wrong");
            return;
          }
        }
      }
    
      //lines is now a list of ordered MLines
      lines = _m;
    
      //special case reverse orientation
      if (lines.size() < 2) return;
      if (_orientation[0] && lines[0]->getVertex(1) != lines[1]->getVertex(1)
          && lines[0]->getVertex(1) != lines[1]->getVertex(0)){
        for (unsigned int i = 0; i < lines.size(); i++)
          _orientation[i] = !_orientation[i];
      }
    }
    
    static void recurConnectByMEdge(const MEdge &e,
    				std::multimap<MEdge, MTriangle*, Less_Edge> &e2e,
    				std::set<MTriangle*> &group,
    				std::set<MEdge, Less_Edge> &touched,
    				std::set<MEdge, Less_Edge> &theCut)
    {
      if (touched.find(e) != touched.end()) return;
      touched.insert(e);
      for (std::multimap <MEdge, MTriangle*, Less_Edge>::iterator it = e2e.lower_bound(e);
           it != e2e.upper_bound(e); ++it){
        group.insert(it->second);
        for (int i = 0; i < it->second->getNumEdges(); ++i){
          MEdge me = it->second->getEdge(i);
          if (theCut.find(me) != theCut.end()){
    	touched.insert(me); //break;
          }
          else recurConnectByMEdge(me, e2e, group, touched, theCut);
        }
      }
    }
    
    static void cutTriangle(MTriangle *tri,
                            std::map<MEdge,MVertex*,Less_Edge> &cutEdges,
                            std::vector<MVertex*> &cutVertices,
                            std::vector<MTriangle*> &newTris,
                            std::set<MEdge,Less_Edge> &newCut)
    {
      MVertex *c[3] = {0,0,0};
      for (int j=0;j<3;j++){
        MEdge ed = tri->getEdge(j);
        std::map<MEdge,MVertex*,Less_Edge> :: iterator it = cutEdges.find(ed);
        if (it != cutEdges.end()){
          c[j] = it->second;
    
        }
      }
      MVertex *old_v0  = tri->getVertex(0);
      MVertex *old_v1  = tri->getVertex(1);
      MVertex *old_v2  = tri->getVertex(2);
      if (c[0] && c[1]){
        newTris.push_back(new MTriangle (c[0],old_v1,c[1]));
        newTris.push_back(new MTriangle (old_v0,c[0],old_v2));
        newTris.push_back(new MTriangle (old_v2,c[0],c[1]));
        newCut.insert(MEdge(c[0],c[1]));
      }
      else if (c[0] && c[2]){
        newTris.push_back(new MTriangle (old_v0,c[0],c[2]));
        newTris.push_back(new MTriangle (c[0],old_v1,old_v2));
        newTris.push_back(new MTriangle (old_v2,c[2],c[0]));
        newCut.insert(MEdge(c[0],c[2]));
      }
      else if (c[1] && c[2]){
        newTris.push_back(new MTriangle (old_v2,c[2],c[1]));
        newTris.push_back(new MTriangle (old_v0,old_v1,c[2]));
        newTris.push_back(new MTriangle (c[2],old_v1,c[1]));
        newCut.insert(MEdge(c[1],c[2]));
      }
      else if (c[0]){
        newTris.push_back(new MTriangle (old_v0,c[0],old_v2));
        newTris.push_back(new MTriangle (old_v2,c[0],old_v1));
        if (std::find(cutVertices.begin(), cutVertices.end(), old_v0) != cutVertices.end()){
          newCut.insert(MEdge(c[0],old_v0));
        }
        else if (std::find(cutVertices.begin(), cutVertices.end(), old_v1) != cutVertices.end()) {
          newCut.insert(MEdge(c[0],old_v1));
        }
        else if (std::find(cutVertices.begin(), cutVertices.end(), old_v2) != cutVertices.end()){
          newCut.insert(MEdge(c[0],old_v2));
        }
      }
      else if (c[1]){
        newTris.push_back(new MTriangle (old_v1,c[1],old_v0));
        newTris.push_back(new MTriangle (old_v0,c[1],old_v2));
        if (std::find(cutVertices.begin(), cutVertices.end(), old_v0) != cutVertices.end()){
          newCut.insert(MEdge(c[1],old_v0));
        }
        else if (std::find(cutVertices.begin(), cutVertices.end(), old_v1) != cutVertices.end()) {
          newCut.insert(MEdge(old_v1, c[1]));
        }
        else if (std::find(cutVertices.begin(), cutVertices.end(), old_v2) != cutVertices.end()){
          newCut.insert(MEdge(c[1],old_v2));
        }
      }
      else if (c[2]){
          newTris.push_back(new MTriangle (old_v0,old_v1, c[2]));
          newTris.push_back(new MTriangle (old_v1,old_v2, c[2]));
        if (std::find(cutVertices.begin(), cutVertices.end(), old_v0) != cutVertices.end()){
          newCut.insert(MEdge(c[2],old_v0));
        }
        else if (std::find(cutVertices.begin(), cutVertices.end(), old_v1) != cutVertices.end()) {
          newCut.insert(MEdge(c[2], old_v1));
        }
        else if (std::find(cutVertices.begin(), cutVertices.end(), old_v2) != cutVertices.end()){
          newCut.insert(MEdge(c[2], old_v2));
        }
      }
      else {
        newTris.push_back(tri);
        if (std::find(cutVertices.begin(), cutVertices.end(), old_v0) != cutVertices.end() &&
    	std::find(cutVertices.begin(), cutVertices.end(), old_v1) != cutVertices.end())
          newCut.insert(MEdge(old_v0,old_v1));
        else if (std::find(cutVertices.begin(), cutVertices.end(), old_v1) != cutVertices.end() &&
    	std::find(cutVertices.begin(), cutVertices.end(), old_v2) != cutVertices.end())
          newCut.insert(MEdge(old_v1,old_v2));
        else if (std::find(cutVertices.begin(), cutVertices.end(), old_v2) != cutVertices.end() &&
    	std::find(cutVertices.begin(), cutVertices.end(), old_v0) != cutVertices.end())
          newCut.insert(MEdge(old_v2,old_v0));
      }
    }
    
    Centerline::Centerline(std::string fileName): kdtree(0), kdtreeR(0)
    {
      recombine = (CTX::instance()->mesh.recombineAll) || (CTX::instance()->mesh.recombine3DAll);
      nbPoints = 25;
      hLayer = 0.3;
      hSecondLayer = 0.3;
      nbElemLayer = 3;
      nbElemSecondLayer = 0;
      is_cut = 0;
      is_closed = 0;
      is_extruded = 0;
    
      importFile(fileName);
      buildKdTree();
      update_needed = false;
    }
    
    Centerline::Centerline(): kdtree(0), kdtreeR(0)
    {
    
      recombine = (CTX::instance()->mesh.recombineAll) || (CTX::instance()->mesh.recombine3DAll);
      fileName = "centerlines.vtk";//default
      nbPoints = 25;
      hLayer = 0.3;
      hSecondLayer = 0.3;
      nbElemLayer = 3;
      nbElemSecondLayer = 0;
      is_cut = 0;
      is_closed = 0;
      is_extruded = 0;
    
      options["closeVolume"] = new FieldOptionInt
        (is_closed, "Action: Create In/Outlet planar faces");
      options["extrudeWall"] = new FieldOptionInt
        (is_extruded, "Action: Extrude wall");
      options["reMesh"] = new FieldOptionInt
        (is_cut, "Action: Cut the initial mesh in different mesh partitions using the "
         "centerlines");
      callbacks["run"] = new FieldCallbackGeneric<Centerline>
        (this, &Centerline::run, "Run actions (closeVolume, extrudeWall, cutMesh) \n");
    
      options["FileName"] = new FieldOptionString
        (fileName, "File name for the centerlines", &update_needed);
      options["nbPoints"] = new FieldOptionInt
        (nbPoints, "Number of mesh elements in a circle");
      options["nbElemLayer"] = new FieldOptionInt
        (nbElemLayer, "Number of mesh elements the extruded layer");
      options["hLayer"] = new FieldOptionDouble
        (hLayer, "Thickness (% of radius) of the extruded layer");
      options["nbElemSecondLayer"] = new FieldOptionInt
        (nbElemSecondLayer, "Number of mesh elements the second extruded layer");
      options["hSecondLayer"] = new FieldOptionDouble
        (hSecondLayer, "Thickness (% of radius) of the second extruded layer");
    }
    
    Centerline::~Centerline()
    {
      if (mod) delete mod;
      if(kdtree){
        ANNpointArray nodes = kdtree->thePoints();
        if(nodes) annDeallocPts(nodes);
        delete kdtree;
      }
      if(kdtreeR){
        ANNpointArray nodesR = kdtreeR->thePoints();
        if(nodesR) annDeallocPts(nodesR);
        delete kdtreeR;
      }
    }
    
    void Centerline::importFile(std::string fileName)
    {
      current = GModel::current();
      std::vector<GFace*> currentFaces(current->firstFace(), current->lastFace());
      for (unsigned int i = 0; i < currentFaces.size(); i++){
        GFace *gf = currentFaces[i];
         if (gf->geomType() == GEntity::DiscreteSurface){
         	for(unsigned int j = 0; j < gf->triangles.size(); j++)
         	  triangles.push_back(gf->triangles[j]);
    	if (is_cut){
    	  gf->triangles.clear();
    	  gf->deleteVertexArrays();
    	  current->remove(gf);
    	}
         }
      }
    
      if(triangles.empty()){
        Msg::Error("Current GModel has no triangles ...");
        return;
      }
    
      mod = new GModel();
      mod->load(fileName);
      mod->removeDuplicateMeshVertices(1.e-8);
      current->setAsCurrent();
      current->setVisibility(1);
    
      int maxN = 0.0;
      std::vector<GEdge*> modEdges(mod->firstEdge(), mod->lastEdge());
      MVertex *vin = modEdges[0]->lines[0]->getVertex(0);
      ptin = SPoint3(vin->x(), vin->y(), vin->z());
      for (unsigned int i = 0; i < modEdges.size(); i++){
        GEdge *ge = modEdges[i];
        for(unsigned int j = 0; j < ge->lines.size(); j++){
          MLine *l = ge->lines[j];
          MVertex *v0 = l->getVertex(0);
          MVertex *v1 = l->getVertex(1);
          std::map<MVertex*, int>::iterator it0 = colorp.find(v0);
          std::map<MVertex*, int>::iterator it1 = colorp.find(v1);
          if (it0 == colorp.end() || it1 == colorp.end()){
    	lines.push_back(l);
    	colorl.insert(std::make_pair(l, ge->tag()));
    	maxN = std::max(maxN, ge->tag());
           }
          if (it0 == colorp.end()) colorp.insert(std::make_pair(v0, ge->tag()));
          if (it1 == colorp.end()) colorp.insert(std::make_pair(v1, ge->tag()));
        }
     }
    
      createBranches(maxN);
    }
    
    void Centerline::createBranches(int maxN)
    {
      //sort colored lines and create edges
      std::vector<std::vector<MLine*> > color_edges;
      color_edges.resize(maxN);
      std::multiset<MVertex*> allV;
      std::map<MLine*, int>::iterator itl = colorl.begin();
      while (itl != colorl.end()){
        int color = itl->second;
        MLine* l = itl->first;
        allV.insert(l->getVertex(0));
        allV.insert(l->getVertex(1));
        color_edges[color-1].push_back(l);
        itl++;
      }
    
      //detect junctions
      std::multiset<MVertex*>::iterator it = allV.begin();
      for ( ; it != allV.end(); ++it){
        if (allV.count(*it) != 2) {
          junctions.insert(*it);
        }
      }
    
      //split edges
      int tag = 0;
      for(unsigned int i = 0; i < color_edges.size(); ++i){
        std::vector<MLine*> lines = color_edges[i];
        while (!lines.empty()) {
          std::vector<MLine*> myLines;
          std::vector<MLine*>::iterator itl = lines.begin();
          MVertex *vB = (*itl)->getVertex(0);
          MVertex *vE = (*itl)->getVertex(1);
          myLines.push_back(*itl);
          erase(lines, *itl);
          itl = lines.begin();
          while ( !( junctions.find(vE) != junctions.end() &&
    		 junctions.find(vB) != junctions.end()) ) {
      	MVertex *v1 = (*itl)->getVertex(0);
      	MVertex *v2 = (*itl)->getVertex(1);
    	bool goVE = (junctions.find(vE) == junctions.end()) ? true : false ;
    	bool goVB = (junctions.find(vB) == junctions.end()) ? true : false;
          	if (v1 == vE && goVE){
        	  myLines.push_back(*itl);
      	  erase(lines, *itl);
    	  itl = lines.begin();
      	  vE = v2;
      	}
      	else if ( v2 == vE && goVE){
        	  myLines.push_back(*itl);
      	  erase(lines, *itl);
    	  itl = lines.begin();
      	  vE = v1;
      	}
      	else if ( v1 == vB && goVB){
        	  myLines.push_back(*itl);
      	  erase(lines, *itl);
    	  itl = lines.begin();
      	  vB = v2;
      	}
      	else if ( v2 == vB && goVB){
       	  myLines.push_back(*itl);
    	  erase(lines, *itl);
    	  itl = lines.begin();
      	  vB = v1;
      	}
    	else itl++;
          }
          if (vB == vE) {
            Msg::Error("Begin and end points branch are the same \n");
            break;
          }
          orderMLines(myLines, vB, vE);
          std::vector<Branch> children;
          Branch newBranch ={ tag++, myLines, computeLength(myLines), vB, vE,
                              children, 1.e6, 0.0};
          edges.push_back(newBranch) ;
        }
      }
    
      Msg::Info("Centerline: in/outlets =%d branches =%d ",
                (int)color_edges.size()+1, (int)edges.size());
    
      //create children
      for(unsigned int i = 0; i < edges.size(); ++i) {
        MVertex *vE = edges[i].vE;
        std::vector<Branch> myChildren;
        for (std::vector<Branch>::iterator it = edges.begin(); it != edges.end(); ++it){
          Branch myBranch = *it;
          if (myBranch.vB == vE) myChildren.push_back(myBranch);
        }
        edges[i].children = myChildren;
      }
    
      //compute radius
      distanceToSurface();
      computeRadii();
    
      //print for debug
      printSplit();
    
    }
    
    void Centerline::distanceToSurface()
    {
      Msg::Info("Centerline: computing distance to surface mesh ");
    
      //COMPUTE WITH REVERSE ANN TREE (SURFACE POINTS IN TREE)
      std::set<MVertex*> allVS;
      for(unsigned int j = 0; j < triangles.size(); j++)
        for(int k = 0; k<3; k++) allVS.insert(triangles[j]->getVertex(k));
      int nbSNodes = allVS.size();
      ANNpointArray nodesR = annAllocPts(nbSNodes, 3);
      vertices.resize(nbSNodes);
      int ind = 0;
      std::set<MVertex*>::iterator itp = allVS.begin();
      while (itp != allVS.end()){
        MVertex *v = *itp;
        nodesR[ind][0] = v->x();
        nodesR[ind][1] = v->y();
        nodesR[ind][2] = v->z();
        vertices[ind] = v;
        itp++; ind++;
      }
      kdtreeR = new ANNkd_tree(nodesR, nbSNodes, 3);
    
      for(unsigned int i = 0; i < lines.size(); i++){
        MLine *l = lines[i];
        MVertex *v1 = l->getVertex(0);
        MVertex *v2 = l->getVertex(1);
        double midp[3] = {0.5*(v1->x()+v2->x()), 0.5*(v1->y()+v1->y()),0.5*(v1->z()+v2->z())};
        ANNidx index[1];
        ANNdist dist[1];
        kdtreeR->annkSearch(midp, 1, index, dist);
        double minRad = sqrt(dist[0]);
        radiusl.insert(std::make_pair(lines[i], minRad));
      }
    
    }
    
    void Centerline::computeRadii()
    {
      for(unsigned int i = 0; i < edges.size(); ++i) {
        std::vector<MLine*> lines = edges[i].lines;
        for(unsigned int j = 0; j < lines.size(); ++j) {
          MLine *l = lines[j];
          std::map<MLine*,double>::iterator itr = radiusl.find(l);
          if (itr != radiusl.end()){
    	edges[i].minRad = std::min(itr->second, edges[i].minRad);
    	edges[i].maxRad = std::max(itr->second, edges[i].maxRad);
          }
          else printf("ARGG line not found \n");
        }
      }
    
    }
    
    void Centerline::buildKdTree()
    {
      FILE * f = Fopen("myPOINTS.pos","w");
      fprintf(f, "View \"\"{\n");
    
      int nbPL = 3;  //10 points per line
      //int nbNodes  = (lines.size()+1) + (nbPL*lines.size());
      int nbNodes  = (colorp.size()) + (nbPL*lines.size());
    
      ANNpointArray nodes = annAllocPts(nbNodes, 3);
      int ind = 0;
      std::map<MVertex*, int>::iterator itp = colorp.begin();
      while (itp != colorp.end()){
         MVertex *v = itp->first;
         nodes[ind][0] = v->x();
         nodes[ind][1] = v->y();
         nodes[ind][2] = v->z();
         itp++; ind++;
      }
      for(unsigned int k = 0; k < lines.size(); ++k){
       MVertex *v0 = lines[k]->getVertex(0);
       MVertex *v1 = lines[k]->getVertex(1);
       SVector3 P0(v0->x(),v0->y(), v0->z());
       SVector3 P1(v1->x(),v1->y(), v1->z());
       for (int j= 1; j < nbPL+1; j++){
         double inc = (double)j/(double)(nbPL+1);
         SVector3 Pj = P0+inc*(P1-P0);
         nodes[ind][0] = Pj.x();
         nodes[ind][1] = Pj.y();
         nodes[ind][2] = Pj.z();
         ind++;
       }
     }
    
     kdtree = new ANNkd_tree(nodes, nbNodes, 3);
    
     for(int i = 0; i < nbNodes; ++i){
       fprintf(f, "SP(%g,%g,%g){%g};\n",
    	   nodes[i][0], nodes[i][1],nodes[i][2],1.0);
     }
     fprintf(f,"};\n");
     fclose(f);
    }
    
    void Centerline::createSplitCompounds()
    {
      //number of discrete vertices, edges, faces and regions for the mesh
      NV = current->getMaxElementaryNumber(0);
      NE = current->getMaxElementaryNumber(1);
      NF = current->getMaxElementaryNumber(2);
      NR = current->getMaxElementaryNumber(3);
    
      // Remesh new faces (Compound Lines and Compound Surfaces)
      Msg::Info("Centerline: creating split compounds ...");
    
      //Parametrize Compound Lines
      for (int i=0; i < NE; i++){
        std::vector<GEdge*>e_compound;
        GEdge *pe = current->getEdgeByTag(i+1);//current edge
        e_compound.push_back(pe);
        int num_gec = NE+i+1;
        Msg::Info("Create Compound Line (%d) = %d discrete edge",
                  num_gec, pe->tag());
        GEdge *gec =  current->addCompoundEdge(e_compound,num_gec);
        if (CTX::instance()->mesh.algo2d != ALGO_2D_BAMG){
          gec->meshAttributes.method = MESH_TRANSFINITE;
          gec->meshAttributes.nbPointsTransfinite = nbPoints+1;
          gec->meshAttributes.typeTransfinite = 0;
          gec->meshAttributes.coeffTransfinite = 1.0;
        }
      }
    
      // Parametrize Compound surfaces
      std::list<GEdge*> U0;
      for (int i=0; i < NF; i++){
        std::vector<GFace*> f_compound;
        GFace *pf =  current->getFaceByTag(i+1);//current face
        f_compound.push_back(pf);
        int num_gfc = NF+i+1;
        Msg::Info("Create Compound Surface (%d) = %d discrete face",
                  num_gfc, pf->tag());
    
        //1=conf_spectral 4=convex_circle, 7=conf_fe
        GFace *gfc = current->addCompoundFace(f_compound, 7, 0, num_gfc);
    
        gfc->meshAttributes.recombine = recombine;
        gfc->addPhysicalEntity(1);
        current->setPhysicalName("wall", 2, 1);//tag 1
    
      }
    }
    
    void Centerline::createFaces()
    {
      std::vector<std::vector<MTriangle*> > faces;
    
      std::multimap<MEdge, MTriangle*, Less_Edge> e2e;
      for(unsigned int i = 0; i < triangles.size(); ++i)
        for(int j = 0; j < 3; j++)
          e2e.insert(std::make_pair(triangles[i]->getEdge(j), triangles[i]));
      while(!e2e.empty()){
        std::set<MTriangle*> group;
        std::set<MEdge, Less_Edge> touched;
        group.clear();
        touched.clear();
        std::multimap<MEdge, MTriangle*, Less_Edge>::iterator ite = e2e.begin();
        MEdge me = ite->first;
        while (theCut.find(me) != theCut.end()){
          ite++;
          me = ite->first;
        }
        recurConnectByMEdge(me,e2e, group, touched, theCut);
        std::vector<MTriangle*> temp;
        temp.insert(temp.begin(), group.begin(), group.end());
        faces.push_back(temp);
        for(std::set<MEdge, Less_Edge>::iterator it = touched.begin();
            it != touched.end(); ++it)
          e2e.erase(*it);
      }
      Msg::Info("Centerline: action (cutMesh) has cut surface mesh in %d faces ",
                (int)faces.size());
    
      //create discFaces
      for(unsigned int i = 0; i < faces.size(); ++i){
        int numF = current->getMaxElementaryNumber(2) + 1;
        discreteFace *f = new discreteFace(current, numF);
        current->add(f);
        discFaces.push_back(f);
        std::set<MVertex*> myVertices;
        std::vector<MTriangle*> myFace = faces[i];
        for(unsigned int j= 0; j< myFace.size(); j++){
          MTriangle *t = myFace[j];
          f->triangles.push_back(t);
          for (int k= 0; k< 3; k++){
    	MVertex *v = t->getVertex(k);
    	myVertices.insert(v);
    	v->setEntity(f);
          }
        }
        f->mesh_vertices.insert(f->mesh_vertices.begin(),
      			    myVertices.begin(), myVertices.end());
      }
    }
    
    void Centerline::createClosedVolume(GEdge *gin, std::vector<GEdge*> boundEdges)
    {
      current->setFactory("Gmsh");
      std::vector<std::vector<GFace *> > myFaceLoops;
      std::vector<GFace *> myFaces;
      for (unsigned int i = 0; i<  boundEdges.size(); i++){
        std::vector<std::vector<GEdge *> > myEdgeLoops;
        std::vector<GEdge *> myEdges;
        GEdge * gec;
        if(is_cut) gec = current->getEdgeByTag(NE+boundEdges[i]->tag());
        else gec = current->getEdgeByTag(boundEdges[i]->tag());
        myEdges.push_back(gec);
        myEdgeLoops.push_back(myEdges);
        GFace *newFace = current->addPlanarFace(myEdgeLoops);
        if (gin==boundEdges[i]) {
          newFace->addPhysicalEntity(2);
          current->setPhysicalName("inlet", 2, 2);//tag 2
        }
        else{
          newFace->addPhysicalEntity(3);
          current->setPhysicalName("outlets", 2, 3);//tag 3
        }
        myFaces.push_back(newFace);
      }
    
      Msg::Info("Centerline: action (closeVolume) has created %d in/out planar faces ",
                (int)boundEdges.size());
    
      for (int i = 0; i < NF; i++){
        GFace * gf;
        if(is_cut) gf = current->getFaceByTag(NF+i+1);
        else gf = current->getFaceByTag(i+1);
        myFaces.push_back(gf);
      }
      myFaceLoops.push_back(myFaces);
      GRegion *reg = current->addVolume(myFaceLoops);
      reg->addPhysicalEntity(reg->tag());
      current->setPhysicalName("lumenVolume", 3, reg->tag());
    
      Msg::Info("Centerline: action (closeVolume) has created volume %d ", reg->tag());
    
    }
    
    void Centerline::extrudeBoundaryLayerWall(GEdge* gin, std::vector<GEdge*> boundEdges)
    {
      Msg::Info("Centerline: extrude boundary layer wall (%d, %g%%R) ", nbElemLayer,  hLayer);
    
      //orient extrude direction outward
      int dir = 0;
      MElement *e = current->getFaceByTag(1)->getMeshElement(0);
      SVector3 ne = e->getFace(0).normal();
      SVector3 ps(e->getVertex(0)->x(), e->getVertex(0)->y(), e->getVertex(0)->z());
      double xyz[3] = {ps.x(), ps.y(), ps.z()};
      ANNidx index[1];
      ANNdist dist[1];
      kdtree->annkSearch(xyz, 1, index, dist);
      ANNpointArray nodes = kdtree->thePoints();
      SVector3 pc(nodes[index[0]][0], nodes[index[0]][1], nodes[index[0]][2]);
      SVector3 nc = ps-pc;
      if (dot(ne,nc) < 0) dir = 1;
      if (dir == 1 && hLayer > 0 ) hLayer *= -1.0;
    
      //int shift = 0;
      //if(is_cut) shift = NE;
      for (int i= 0; i< NF; i++){
        GFace *gfc ;
        if (is_cut) gfc = current->getFaceByTag(NF+i+1);
        else gfc = current->getFaceByTag(i+1);
        current->setFactory("Gmsh");
        //view -5 to scale hLayer by radius in BoundaryLayers.cpp
        std::vector<GEntity*> extrudedE = current->extrudeBoundaryLayer
          (gfc, nbElemLayer,  hLayer, dir, -5);
        GFace *eFace = (GFace*) extrudedE[0];
        eFace->addPhysicalEntity(5);
        current->setPhysicalName("outerWall", 2, 5);//dim 2 tag 5
        GRegion *eRegion = (GRegion*) extrudedE[1];
        eRegion->addPhysicalEntity(6);
        current->setPhysicalName("wallVolume", 3, 6);//dim 3 tag 6
    
        //if double extruded layer
        if (nbElemSecondLayer > 0){
          std::vector<GEntity*> extrudedESec = current->extrudeBoundaryLayer
          	(eFace, nbElemSecondLayer,  hSecondLayer, dir, -5);
          GFace *eFaceSec = (GFace*) extrudedESec[0];
          eFaceSec->addPhysicalEntity(9);                    //tag 9
          current->setPhysicalName("outerSecondWall", 2, 9);//dim 2 tag 9
          GRegion *eRegionSec = (GRegion*) extrudedESec[1];
          eRegionSec->addPhysicalEntity(10);             //tag 10
          current->setPhysicalName("wallVolume", 3, 10);//dim 3 tag 10
        }
        //end double extrusion
    
        for (unsigned int j = 2; j < extrudedE.size(); j++){
          GFace *elFace = (GFace*) extrudedE[j];
          std::list<GEdge*> l_edges = elFace->edges();
          for(std::list<GEdge*>::iterator it = l_edges.begin(); it != l_edges.end(); it++){
    	GEdge *myEdge = *it;
    	if (is_cut) myEdge = current->getEdgeByTag((*it)->tag()-NE);
    	if( std::find(boundEdges.begin(), boundEdges.end(), myEdge) != boundEdges.end() ){
    	  if (myEdge==gin){
    	    elFace->addPhysicalEntity(7);
    	    current->setPhysicalName("inletRing", 2, 7);//tag 7
    	  }
    	  else{
    	    elFace->addPhysicalEntity(8);
    	    current->setPhysicalName("outletRings", 2, 8);//tag 8
    	  }
    	}
          }
        }
      }
    
    }
    
    void Centerline::run()
    {
      double t1 = Cpu();
      if (update_needed){
        std::ifstream input;
        //std::string pattern = FixRelativePath(fileName, "./");
        //Msg::StatusBar(true, "Reading TEST '%s'...", pattern.c_str());
        //input.open(pattern.c_str());
        input.open(fileName.c_str());
        if(StatFile(fileName))
          Msg::Fatal("Centerline file '%s' does not exist ", fileName.c_str());
        importFile(fileName);
        buildKdTree();
        update_needed = false;
      }
    
      if (is_cut) cutMesh();
      else{
        GFace *gf = current->getFaceByTag(1);
        gf->addPhysicalEntity(1);
        current->setPhysicalName("wall", 2, 1);//tag 1
        current->createTopologyFromMesh();
        NV = current->getMaxElementaryNumber(0);
        NE = current->getMaxElementaryNumber(1);
        NF = current->getMaxElementaryNumber(2);
        NR = current->getMaxElementaryNumber(3);
      }
    
      //identify the boundary edges by looping over all discreteFaces
      std::vector<GEdge*> boundEdges;
      double dist_inlet = 1.e6;
      GEdge *gin = NULL;
      for (int i= 0; i< NF; i++){
        GFace *gf = current->getFaceByTag(i+1);
        std::list<GEdge*> l_edges = gf->edges();
        for(std::list<GEdge*>::iterator it = l_edges.begin(); it != l_edges.end(); it++){
          std::vector<GEdge*>::iterator ite = std::find(boundEdges.begin(),
                                                        boundEdges.end(), *it);
          if (ite != boundEdges.end()) boundEdges.erase(ite);
          else boundEdges.push_back(*it);
          GVertex *gv = (*it)->getBeginVertex();
          SPoint3 pt(gv->x(), gv->y(), gv->z());
          double dist = pt.distance(ptin);
          if(dist < dist_inlet){
    	dist_inlet = dist;
    	gin = *it;
          }
        }
      }
    
      if (is_closed)   createClosedVolume(gin, boundEdges);
      if (is_extruded) extrudeBoundaryLayerWall(gin, boundEdges);
    
      double t2 = Cpu();
      Msg::Info("Centerline operators computed in %g (s) ",t2-t1);
    }
    
    void Centerline::cutMesh()
    {
      Msg::Info("Centerline: action (cutMesh) splits surface mesh (%d tris) using %s ",
                triangles.size(), fileName.c_str());
    
      //splitMesh
      for(unsigned int i = 0; i < edges.size(); i++){
        std::vector<MLine*> lines = edges[i].lines;
        double L = edges[i].length;
        double D = 2.*edges[i].minRad;  //(edges[i].minRad+edges[i].maxRad);
        double AR = L/D;
        // printf("*** Centerline branch %d (AR=%.1f) \n", edges[i].tag, AR);
    
        int nbSplit = (int)ceil(AR/2 + 1.1); //AR/2 + 0.9
        if( nbSplit > 1 ){
          //printf("->> cut branch in %d parts \n",  nbSplit);
          double li  = L/nbSplit;
          double lc = 0.0;
          for (unsigned int j= 0; j < lines.size(); j++){
    	lc += lines[j]->getLength();
    	if (lc > li && nbSplit > 1) {
    	  MVertex *v1 = lines[j]->getVertex(0);
    	  MVertex *v2 = lines[j]->getVertex(1);
    	  SVector3 pt(v1->x(), v1->y(), v1->z());
    	  SVector3 dir(v2->x()-v1->x(),v2->y()-v1->y(),v2->z()-v1->z());
    	  std::map<MLine*,double>::iterator itr = radiusl.find(lines[j]);
    	  cutByDisk(pt, dir, itr->second);
    	  nbSplit--;
    	  lc = 0.0;
    	}
          }
        }
        if(edges[i].children.size() > 0.0 && AR > 1.0){
          MVertex *v1 = lines[lines.size()-1]->getVertex(1);//end vertex
          MVertex *v2;
          if(AR < 1.5) v2 = lines[0]->getVertex(0);
          else if (lines.size() > 4) v2 = lines[lines.size()-4]->getVertex(0);
          else v2 = lines[lines.size()-1]->getVertex(0);
          SVector3 pt(v1->x(), v1->y(), v1->z());
          SVector3 dir(v2->x()-v1->x(),v2->y()-v1->y(),v2->z()-v1->z());
          //printf("-->> cut branch at bifurcation \n");
          std::map<MLine*,double>::iterator itr = radiusl.find(lines[lines.size()-1]);
          //bool cutted =
          cutByDisk(pt, dir, itr->second);
          // if(!cutted){
          //   int l = lines.size()-1-lines.size()/(4*nbSplit); //chech this!
          //   v1 = lines[l]->getVertex(1);
          //   v2 = lines[l]->getVertex(0);
          //   pt = SVector3(v1->x(), v1->y(), v1->z());
          //   dir = SVector3(v2->x()-v1->x(),v2->y()-v1->y(),v2->z()-v1->z());
          //   printf("-->> cut bifurcation NEW \n");
          //   itr = radiusl.find(lines[l]);
          //   cutted = cutByDisk(pt, dir, itr->second);
          // }
        }
     }
    
      //create discreteFaces
      createFaces();
      current->createTopologyFromFaces(discFaces);
      current->exportDiscreteGEOInternals();
    
      //write
      Msg::Info("Centerline: writing splitted mesh 'myPARTS.msh'");
      current->writeMSH("myPARTS.msh", 2.2, false, false);
    
      //create compounds
      createSplitCompounds();
    
      Msg::Info("Done splitting mesh by centerlines");
    }
    
    bool Centerline::cutByDisk(SVector3 &PT, SVector3 &NORM, double &maxRad)
    {
      double a = NORM.x();
      double b = NORM.y();
      double c = NORM.z();
      double d = -a * PT.x() - b * PT.y() - c * PT.z();
    
      int maxStep = 20;
      const double EPS = 0.007;
    
      //std::set<MEdge,Less_Edge> allEdges;
      std::vector<MEdge> allEdges;
      for(unsigned int i = 0; i < triangles.size(); i++){
        for ( unsigned int j= 0; j <  3; j++){
          allEdges.push_back(triangles[i]->getEdge(j));
          //allEdges.insert(triangles[i]->getEdge(j));
        }
      }
      std::unique(allEdges.begin(), allEdges.end());
    
      bool closedCut = false;
      int step = 0;
      while (!closedCut && step < maxStep){
        double rad = 1.1*maxRad+0.05*step*maxRad;
        std::map<MEdge,MVertex*,Less_Edge> cutEdges;
        std::vector<MVertex*> cutVertices;
        std::vector<MTriangle*> newTris;
        std::set<MEdge,Less_Edge> newCut;
        cutEdges.clear();
        cutVertices.clear();
        newTris.clear();
        newCut.clear();
        // for (std::set<MEdge,Less_Edge>::iterator it = allEdges.begin();
        // 	 it != allEdges.end() ; ++it){
        // MEdge me = *it;
        for (unsigned int j = 0; j < allEdges.size(); j++){
          MEdge me = allEdges[j];
          SVector3 P1(me.getVertex(0)->x(),me.getVertex(0)->y(), me.getVertex(0)->z());
          SVector3 P2(me.getVertex(1)->x(),me.getVertex(1)->y(), me.getVertex(1)->z());
          double V1 = a * P1.x() + b * P1.y() + c * P1.z() + d;
          double V2 = a * P2.x() + b * P2.y() + c * P2.z() + d;
          bool inters = (V1*V2<=0.0) ? true: false;
          bool inDisk = ((norm(P1-PT) < rad ) || (norm(P2-PT) < rad)) ? true : false;
          double rdist = -V1/(V2-V1);
          if (inters && rdist > EPS && rdist < 1.-EPS){
    	SVector3 PZ = P1+rdist*(P2-P1);
    	if (inDisk){
              MVertex *newv = new MVertex (PZ.x(), PZ.y(), PZ.z());
              cutEdges.insert(std::make_pair(me,newv));
            }
          }
          else if (inters && rdist <= EPS && inDisk )
    	cutVertices.push_back(me.getVertex(0));
          else if (inters && rdist >= 1.-EPS && inDisk)
    	cutVertices.push_back(me.getVertex(1));
        }
        for(unsigned int i = 0; i < triangles.size(); i++){
          cutTriangle(triangles[i], cutEdges,cutVertices, newTris, newCut);
        }
        if (isClosed(newCut)) {
          triangles.clear();
          triangles = newTris;
          theCut.insert(newCut.begin(),newCut.end());
          break;
        }
        else {
          step++;
          //if (step == maxStep) {printf("no closed cut %d \n", (int)newCut.size()); };
          // // triangles = newTris;
          // // theCut.insert(newCut.begin(),newCut.end());
          // char name[256];
          // sprintf(name, "myCUT-%d.pos", step);
          // FILE * f2 = Fopen(name,"w");
          // fprintf(f2, "View \"\"{\n");
          // std::set<MEdge,Less_Edge>::iterator itp =  newCut.begin();
          // while (itp != newCut.end()){
          // 	MEdge l = *itp;
          // 	fprintf(f2, "SL(%g,%g,%g,%g,%g,%g){%g,%g};\n",
          // 		  l.getVertex(0)->x(), l.getVertex(0)->y(), l.getVertex(0)->z(),
          // 		  l.getVertex(1)->x(), l.getVertex(1)->y(), l.getVertex(1)->z(),
          // 		  1.0,1.0);
          // 	  itp++;
          // 	}
          // 	fprintf(f2,"};\n");
          // 	fclose(f2);
        }
      }
    
    
      if (step < maxStep){
        //printf("cutByDisk OK step =%d  \n", step);
        return true;
      }
      else {
        //printf("cutByDisk not succeeded \n");
        return false;
      }
    
    }
    
    double Centerline::operator() (double x, double y, double z, GEntity *ge)
    {
    
      if (update_needed){
         std::ifstream input;
         input.open(fileName.c_str());
         if(StatFile(fileName))
           Msg::Fatal("Centerline file '%s' does not exist", fileName.c_str());
         importFile(fileName);
         buildKdTree();
         update_needed = false;
       }
    
       double xyz[3] = {x,y,z};
       //take xyz = closest point on boundary in case we are on the planar in/out faces
       //or in the volume
       bool isCompound = false;
       if(ge){
         if (ge->dim() == 2 && ge->geomType() == GEntity::CompoundSurface) isCompound = true;
         std::list<GFace*> cFaces;
         if (isCompound) cFaces = ((GFaceCompound*)ge)->getCompounds();
         if ( ge->dim() == 3 || (ge->dim() == 2 && ge->geomType() == GEntity::Plane) ||
    	  (isCompound && (*cFaces.begin())->geomType() == GEntity::Plane) ){
           const int num_neighbours = 1;
           ANNidx index[num_neighbours];
           ANNdist dist[num_neighbours];
           kdtreeR->annkSearch(xyz, num_neighbours, index, dist);
           ANNpointArray nodesR = kdtreeR->thePoints();
           xyz[0] = nodesR[index[0]][0];
           xyz[1] = nodesR[index[0]][1];
           xyz[2] = nodesR[index[0]][2];
         }
       }
    
       const int num_neighbours = 1;
       ANNidx index[num_neighbours];
       ANNdist dist[num_neighbours];
       kdtree->annkSearch(xyz, num_neighbours, index, dist);
       double rad = sqrt(dist[0]);
    
       //double cmax, cmin;
       //SVector3 dirMax,dirMin;
       //cmax = ge->curvatures(SPoint2(u, v),&dirMax, &dirMin, &cmax,&cmin);
       //cmax = ge->curvatureMax(SPoint2(u,v));
       //double radC = 1./cmax;
    
       double lc = 2*M_PI*rad/nbPoints;
    
       if(!ge) { return rad;}
       else  return lc;
    
    }
    
    void  Centerline::operator() (double x, double y, double z, SMetric3 &metr, GEntity *ge)
    {
    
       if (update_needed){
         std::ifstream input;
         input.open(fileName.c_str());
         if(StatFile(fileName))
           Msg::Fatal("Centerline file '%s' does not exist", fileName.c_str());
         importFile(fileName);
         buildKdTree();
         update_needed = false;
       }
    
    
       //take xyz = closest point on boundary in case we are on
       //the planar IN/OUT FACES or in VOLUME
       double xyz[3] = {x,y,z};
       bool onTubularSurface = true;
       double ds = 0.0;
       bool isCompound = (ge->dim() == 2 && ge->geomType() == GEntity::CompoundSurface) ?
         true : false;
       bool onInOutlets = (ge->geomType() == GEntity::Plane) ? true: false;
       std::list<GFace*> cFaces;
       if (isCompound) cFaces = ((GFaceCompound*)ge)->getCompounds();
       if ( ge->dim() == 3 || (ge->dim() == 2 && ge->geomType() == GEntity::Plane) ||
    	(isCompound && (*cFaces.begin())->geomType() == GEntity::Plane) ){
         onTubularSurface = false;
       }
    
       ANNidx index[1];
       ANNdist dist[1];
       kdtreeR->annkSearch(xyz, 1, index, dist);
       if (! onTubularSurface){
         ANNpointArray nodesR = kdtreeR->thePoints();
         ds = sqrt(dist[0]);
         xyz[0] = nodesR[index[0]][0];
         xyz[1] = nodesR[index[0]][1];
         xyz[2] = nodesR[index[0]][2];
       }
    
       ANNidx index2[2];
       ANNdist dist2[2];
       kdtree->annkSearch(xyz, 2, index2, dist2);
       double radMax = sqrt(dist2[0]);
       ANNpointArray nodes = kdtree->thePoints();
       SVector3  p0(nodes[index2[0]][0], nodes[index2[0]][1], nodes[index2[0]][2]);
       SVector3  p1(nodes[index2[1]][0], nodes[index2[1]][1], nodes[index2[1]][2]);
    
       //dir_a = direction along the centerline
       //dir_n = normal direction of the disk
       //dir_t = tangential direction of the disk
       SVector3 dir_a = p1-p0; dir_a.normalize();
       SVector3 dir_n(xyz[0]-p0.x(), xyz[1]-p0.y(), xyz[2]-p0.z()); dir_n.normalize();
       SVector3 dir_cross  = crossprod(dir_a,dir_n); dir_cross.normalize();
       // if (ge->dim()==3){
       //   printf("coucou dim ==3 !!!!!!!!!!!!!!! \n");
       //   SVector3 d1,d2,d3;
       //   computeCrossField(x,y,z,d1,d2,d3);
       //   exit(1);
       // }
    
       //find discrete curvature at closest vertex
       Curvature& curvature = Curvature::getInstance();
       if( !Curvature::valueAlreadyComputed() ) {
         Msg::Info("Need to compute discrete curvature");
         Curvature::typeOfCurvature type = Curvature::RUSIN;
         curvature.computeCurvature(current, type);
       }
       double curv, cMin, cMax;
       SVector3 dMin, dMax;
       int isAbs = 1.0;
       curvature.vertexNodalValuesAndDirections(vertices[index[0]],&dMax, &dMin, &cMax, &cMin, isAbs);
       curvature.vertexNodalValues(vertices[index[0]], curv, 1);
       if (cMin == 0) cMin =1.e-12;
       if (cMax == 0) cMax =1.e-12;
       double rhoMin = 1./cMin;
       double rhoMax = 1./cMax;
       //double signMin = (rhoMin > 0.0) ? -1.0: 1.0;
       //double signMax = (rhoMax > 0.0) ? -1.0: 1.0;
    
       //user-defined parameters
       //define h_n, h_t1, and h_t2
       double thickness = radMax/3.;
       double h_far = radMax/5.;
       double beta = (ds <= thickness) ? 1.2 : 2.1; //CTX::instance()->mesh.smoothRatio;
       double ddist = (ds <= thickness) ? ds: thickness;
    
       double h_n_0 = thickness/20.;
       double h_n   = std::min( (h_n_0+ds*log(beta)), h_far);
    
       double betaMin = 10.0;
       double betaMax = 3.1;
       double oneOverD2_min = 1./(2.*rhoMin*rhoMin*(betaMin*betaMin-1)) *
         (sqrt(1+ (4.*rhoMin*rhoMin*(betaMin*betaMin-1))/(h_n*h_n))-1.);
       double oneOverD2_max = 1./(2.*rhoMax*rhoMax*(betaMax*betaMax-1)) *
        (sqrt(1+ (4.*rhoMax*rhoMax*(betaMax*betaMax-1))/(h_n*h_n))-1.);
       double h_t1_0 = sqrt(1./oneOverD2_min);
       double h_t2_0 = sqrt(1./oneOverD2_max);
       //double h_t1 =  h_t1_0*(rhoMin+signMin*ddist)/rhoMin ;
       //double h_t2 =  h_t2_0*(rhoMax+signMax*ddist)/rhoMax ;
       double h_t1  = std::min( (h_t1_0+(ddist*log(beta))), radMax);
       double h_t2  = std::min( (h_t2_0+(ddist*log(beta))), h_far);
    
       double dCenter = radMax-ds;
       double h_a_0 = 0.5*radMax;
       double h_a = h_a_0 - (h_a_0-h_t1_0)/(radMax)*dCenter;
    
       //length between min and max
       double lcMin = ((2 * M_PI *radMax) /( 50*nbPoints )); //CTX::instance()->mesh.lcMin;
       double lcMax =  lcMin*2000.; //CTX::instance()->mesh.lcMax;
       h_n = std::max(h_n, lcMin);    h_n = std::min(h_n, lcMax);
       h_t1 = std::max(h_t1, lcMin);  h_t1 = std::min(h_t1, lcMax);
       h_t2 = std::max(h_t2, lcMin);  h_t2 = std::min(h_t2, lcMax);
    
       //curvature metric
       SMetric3 curvMetric, curvMetric1, curvMetric2;
       SMetric3 centMetric1, centMetric2, centMetric;
       if (onInOutlets){
         metr = buildMetricTangentToCurve(dir_n,h_n,h_t2);
       }
       else {
         //on surface and in volume boundary layer
         if ( ds < thickness ){
           metr = metricBasedOnSurfaceCurvature(dMin, dMax, cMin, cMax, h_n, h_t1, h_t2);
         }
         //in volume
         else {
           //curvMetric = metricBasedOnSurfaceCurvature(dMin, dMax, cMin, cMax, h_n, h_t1, h_t2);
           metr = SMetric3( 1./(h_a*h_a), 1./(h_n*h_n), 1./(h_n*h_n), dir_a, dir_n, dir_cross);
    
           //metr = intersection_conserveM1_bis(metr, curvMetric);
           //metr = intersection_conserveM1(metr,curvMetric);
           //metr = intersection_conserve_mostaniso(metr, curvMetric);
           //metr = intersection(metr,curvMetric);
         }
       }
    
       return;
    
    }
    
    SMetric3 Centerline::metricBasedOnSurfaceCurvature(SVector3 dirMin, SVector3 dirMax,
                                                       double cmin, double cmax,
    						   double h_n, double h_t1, double h_t2)
    {
    
      SVector3 dirNorm = crossprod(dirMax,dirMin);
      SMetric3 curvMetric (1./(h_t1*h_t1),1./(h_t2*h_t2),1./(h_n*h_n), dirMin, dirMax, dirNorm);
    
      return curvMetric;
    }
    
    void Centerline::computeCrossField(double x,double y,double z,
    				   SVector3 &d1, SVector3 &d2,  SVector3 &d3)
    {
    
      //Le code suivant permet d'obtenir les trois vecteurs orthogonaux en un point.
      // int NumSmooth = 10;//CTX::instance()->mesh.smoothCrossField
      // Matrix m2;
      // if(NumSmooth)
      //   m2 = Frame_field::findCross(x,y,z);
      // else
      //   m2 = Frame_field::search(x,y,z);
    
    }
    
    void Centerline::printSplit() const
    {
      FILE * f = Fopen("mySPLIT.pos","w");
      fprintf(f, "View \"\"{\n");
      for(unsigned int i = 0; i < edges.size(); ++i){
        std::vector<MLine*> lines = edges[i].lines;
        for(unsigned int k = 0; k < lines.size(); ++k){
          MLine *l = lines[k];
          fprintf(f, "SL(%g,%g,%g,%g,%g,%g){%g,%g};\n",
    	      l->getVertex(0)->x(), l->getVertex(0)->y(), l->getVertex(0)->z(),
    	      l->getVertex(1)->x(), l->getVertex(1)->y(), l->getVertex(1)->z(),
    	      (double)edges[i].tag, (double)edges[i].tag);
        }
      }
      fprintf(f,"};\n");
      fclose(f);
    
      // FILE * f3 = Fopen("myJUNCTIONS.pos","w");
      // fprintf(f3, "View \"\"{\n");
      //  std::set<MVertex*>::const_iterator itj = junctions.begin();
      //  while (itj != junctions.end()){
      //    MVertex *v =  *itj;
      //    fprintf(f3, "SP(%g,%g,%g){%g};\n",
      // 	     v->x(),  v->y(), v->z(),
      // 	     (double)v->getNum());
      //    itj++;
      // }
      // fprintf(f3,"};\n");
      // fclose(f3);
    
      FILE * f4 = Fopen("myRADII.pos","w");
      fprintf(f4, "View \"\"{\n");
      for(unsigned int i = 0; i < lines.size(); ++i){
        MLine *l = lines[i];
        std::map<MLine*,double>::const_iterator itc =  radiusl.find(l);
        fprintf(f4, "SL(%g,%g,%g,%g,%g,%g){%g,%g};\n",
     	    l->getVertex(0)->x(), l->getVertex(0)->y(), l->getVertex(0)->z(),
     	    l->getVertex(1)->x(), l->getVertex(1)->y(), l->getVertex(1)->z(),
     	    itc->second,itc->second);
      }
      fprintf(f4,"};\n");
      fclose(f4);
    
    }
    
    #endif