Skip to content
Snippets Groups Projects
Forked from gmsh / gmsh
16465 commits behind the upstream repository.
meshGFaceOptimize.cpp 27.02 KiB
// $Id: meshGFaceOptimize.cpp,v 1.15 2008-03-26 20:32:40 remacle Exp $
//
// Copyright (C) 1997-2008 C. Geuzaine, J.-F. Remacle
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA.
// 
// Please report all bugs and problems to <gmsh@geuz.org>.

#include "meshGFaceOptimize.h"
#include "qualityMeasures.h"
#include "GFace.h"
#include "GEdge.h"
#include "GVertex.h"
#include "MVertex.h"
#include "MElement.h"
#include "BackgroundMesh.h"

static void setLcsInit(MTriangle *t, std::map<MVertex*, double> &vSizes)
{
  for (int i = 0; i < 3; i++){
    for (int j = i + 1; j < 3; j++){
      MVertex *vi = t->getVertex(i);
      MVertex *vj = t->getVertex(j);
      vSizes[vi] = -1;
      vSizes[vj] = -1;
    }
  }
}

static void setLcs(MTriangle *t, std::map<MVertex*,double> &vSizes)
{
  for (int i = 0; i < 3; i++){
    for (int j = i + 1; j < 3; j++){
      MVertex *vi = t->getVertex(i);
      MVertex *vj = t->getVertex(j);
      double dx = vi->x()-vj->x();
      double dy = vi->y()-vj->y();
      double dz = vi->z()-vj->z();
      double l = sqrt(dx * dx + dy * dy + dz * dz);
      std::map<MVertex*,double>::iterator iti = vSizes.find(vi);	  
      std::map<MVertex*,double>::iterator itj = vSizes.find(vj);	  
      if (iti->second < 0 || iti->second > l) iti->second = l;
      if (itj->second < 0 || itj->second > l) itj->second = l;
    }
  }
}

static void setLcs(MLine *t, std::map<MVertex*,double> &vSizes)
{
  MVertex *vi = t->getVertex(0);
  MVertex *vj = t->getVertex(1);
  double dx = vi->x()-vj->x();
  double dy = vi->y()-vj->y();
  double dz = vi->z()-vj->z();
  double l = sqrt(dx * dx + dy * dy + dz * dz);
  std::map<MVertex*,double>::iterator iti = vSizes.find(vi);	  
  std::map<MVertex*,double>::iterator itj = vSizes.find(vj);	  
  if (iti->second < 0 || iti->second < l) iti->second = l;
  if (itj->second < 0 || itj->second < l) itj->second = l;
}

void buidMeshGenerationDataStructures(GFace *gf, std::set<MTri3*, compareTri3Ptr> &AllTris,
                                      std::vector<double> &vSizes,
                                      std::vector<double> &vSizesBGM,
                                      std::vector<double> &Us,
                                      std::vector<double> &Vs)
{
  std::map<MVertex*, double> vSizesMap;
  std::list<GEdge*> edges = gf->edges();

  for (unsigned int i = 0;i < gf->triangles.size(); i++)
    setLcsInit(gf->triangles[i], vSizesMap);

//   std::list<GEdge*>::iterator it = edges.begin();
//   while (it != edges.end()){
//     GEdge *ge = *it ;
//     for (int i=0;i<ge->lines.size();i++){
//       setLcs(ge->lines[i], vSizesMap);      
//     }
//     ++it;
//   }

  for (unsigned int i = 0;i < gf->triangles.size(); i++)
    setLcs(gf->triangles[i], vSizesMap);

  int NUM = 0;
  for (std::map<MVertex*, double>::iterator it = vSizesMap.begin();
       it != vSizesMap.end(); ++it){
    it->first->setNum(NUM++);
    vSizes.push_back(it->second);
    vSizesBGM.push_back(it->second);
    double u0, v0;
    parametricCoordinates(it->first, gf, u0, v0);
    Us.push_back(u0);
    Vs.push_back(v0);
  }
  for(unsigned int i = 0; i < gf->triangles.size(); i++){
    double lc = 0.3333333333 * (vSizes [gf->triangles[i]->getVertex(0)->getNum()] +
                                vSizes [gf->triangles[i]->getVertex(1)->getNum()] +
                                vSizes [gf->triangles[i]->getVertex(2)->getNum()]);
    AllTris.insert(new MTri3(gf->triangles[i], lc));
  }
  gf->triangles.clear();
  connectTriangles(AllTris );
}

void transferDataStructure(GFace *gf, std::set<MTri3*, compareTri3Ptr> &AllTris)
{
  while (1) {
    if (AllTris.begin() == AllTris.end() ) break;
    MTri3 *worst = *AllTris.begin();
    if (worst->isDeleted())
      delete worst->tri();
    else
      gf->triangles.push_back(worst->tri());
    delete worst;
    AllTris.erase(AllTris.begin());      
  }
}

void buildVertexToTriangle(std::vector<MTriangle*> &triangles, v2t_cont &adj)
{
  adj.clear();
  for (unsigned int i = 0; i < triangles.size(); i++){
    MTriangle *t = triangles[i];
    for (unsigned int j = 0; j < 3; j++){
      MVertex *v = t->getVertex(j);
      v2t_cont :: iterator it = adj.find(v);
      if (it == adj.end()){
        std::vector<MTriangle*> one;
        one.push_back(t);
        adj[v] = one;
      }
      else{
        it->second.push_back(t);
      }
    }
  }
}

void buildEdgeToTriangle(GFace *gf, e2t_cont &adj)
{
  buildEdgeToTriangle(gf->triangles, adj);
}

void buildEdgeToTriangle(std::vector<MTriangle*> &triangles, e2t_cont &adj)
{
  adj.clear();
  for (unsigned int i = 0; i < triangles.size(); i++){
    MTriangle *t = triangles[i];
    for (unsigned int j = 0; j < 3; j++){
      MVertex *v1 = t->getVertex(j);
      MVertex *v2 = t->getVertex((j + 1) % 3);
      MEdge e(v1, v2);
      e2t_cont::iterator it = adj.find(e);
      if (it == adj.end()){
        std::pair<MTriangle*, MTriangle*> one = std::make_pair(t, (MTriangle*)0);
        adj[e] = one;
      }
      else
        {
          it->second.second = t;
        }
    }
  }
}

void parametricCoordinates(MTriangle *t, GFace *gf, double u[3], double v[3])
{
  for (unsigned int j = 0; j < 3; j++){
    MVertex *ver = t->getVertex(j);
    parametricCoordinates(ver, gf, u[j], v[j]);
  }
}

void laplaceSmoothing(GFace *gf)
{
  v2t_cont adj;
  buildVertexToTriangle(gf->triangles, adj);

  for (int i = 0; i < 5; i++){
    v2t_cont :: iterator it = adj.begin();
    while (it != adj.end()){
      MVertex *ver= it->first;
      GEntity *ge = ver->onWhat();
      // this vertex in internal to the face
      if (ge->dim() == 2){
        double initu,initv;
        ver->getParameter(0, initu);
        ver->getParameter(1, initv);
        const std::vector<MTriangle*> &lt = it->second;
        double fact = lt.size() ? 1. / (3. * lt.size()) : 0;
        double cu = 0, cv = 0;
        double pu[3], pv[3];
        for (unsigned int i = 0; i < lt.size(); i++){
          parametricCoordinates(lt[i], gf, pu, pv);
          cu += fact * (pu[0] + pu[1] + pu[2]);
          cv += fact * (pv[0] + pv[1] + pv[2]);
          // have to test validity !
        }
        ver->setParameter(0, cu);
        ver->setParameter(1, cv);
        GPoint pt = gf->point(SPoint2(cu, cv));
        ver->x() = pt.x();
        ver->y() = pt.y();
        ver->z() = pt.z();
      }
      ++it;
    }  
  }
}

extern void fourthPoint(double *p1, double *p2, double *p3, double *p4);

double surfaceTriangleUV(MVertex *v1, MVertex *v2, MVertex *v3,           
                         const std::vector<double> &Us,
                         const std::vector<double> &Vs)
{
  const double v12[2] = {Us[v2->getNum()] - Us[v1->getNum()],
                         Vs[v2->getNum()] - Vs[v1->getNum()]};
  const double v13[2] = {Us[v3->getNum()] - Us[v1->getNum()],
                         Vs[v3->getNum()] - Vs[v1->getNum()]};
  return 0.5 * fabs (v12[0] * v13[1] - v12[1] * v13[0]);
}

bool gmshEdgeSwap(std::set<swapquad> &configs, MTri3 *t1, GFace *gf, int iLocalEdge,
                  std::vector<MTri3*> &newTris, const gmshSwapCriterion &cr,               
                  const std::vector<double> &Us, const std::vector<double> &Vs,
                  const std::vector<double> &vSizes, const std::vector<double> &vSizesBGM)
{
  MTri3 *t2 = t1->getNeigh(iLocalEdge);
  if (!t2) return false;

  MVertex *v1 = t1->tri()->getVertex(iLocalEdge == 0 ? 2 : iLocalEdge - 1);
  MVertex *v2 = t1->tri()->getVertex((iLocalEdge) % 3);
  MVertex *v3 = t1->tri()->getVertex((iLocalEdge + 1) % 3);
  MVertex *v4 = 0;
  for (int i = 0; i < 3; i++)
    if (t2->tri()->getVertex(i) != v1 && t2->tri()->getVertex(i) != v2)
      v4 = t2->tri()->getVertex(i);
  
  swapquad sq (v1, v2, v3, v4);
  if (configs.find(sq) != configs.end()) return false;
  configs.insert(sq);

  const double volumeRef = surfaceTriangleUV(v1, v2, v3, Us, Vs) + 
    surfaceTriangleUV(v1, v2, v4, Us, Vs);

  MTriangle *t1b = new MTriangle(v2, v3, v4);  
  MTriangle *t2b = new MTriangle(v4, v3, v1); 
  const double v1b = surfaceTriangleUV(v2, v3, v4, Us, Vs);
  const double v2b = surfaceTriangleUV(v4, v3, v1, Us, Vs);
  const double volume = v1b + v2b;
  if (fabs(volume - volumeRef) > 1.e-10 * (volume + volumeRef) || 
      v1b < 1.e-8 * (volume + volumeRef) ||
      v2b < 1.e-8 * (volume + volumeRef)){
    delete t1b;
    delete t2b;
    return false;
  }
  
  switch(cr){
  case SWCR_QUAL:
    {
      const double triQualityRef = std::min(qmTriangle(t1->tri(), QMTRI_RHO),
                                            qmTriangle(t2->tri(), QMTRI_RHO));
      const double triQuality = std::min(qmTriangle(t1b, QMTRI_RHO),
                                         qmTriangle(t2b, QMTRI_RHO));
      if (triQuality < triQualityRef){
        delete t1b;
        delete t2b;
        return false;
      }
      break;
    }
  case SWCR_DEL:
    {
      double edgeCenter[2] ={(Us[v1->getNum()] + Us[v2->getNum()] + Us[v3->getNum()] + 
                              Us[v4->getNum()]) * .25,
                             (Vs[v1->getNum()] + Vs[v2->getNum()] + Vs[v3->getNum()] + 
                              Vs[v4->getNum()]) * .25};
      double uv4[2] ={Us[v4->getNum()], Vs[v4->getNum()]};
      double metric[3];
      buildMetric(gf, edgeCenter, metric);
      if (!inCircumCircleAniso(gf, t1->tri(), uv4, metric, Us, Vs)){
        delete t1b;
        delete t2b;
        return false;
      }      
    }
    break;
  case SWCR_CLOSE:
    {
      double avg1[3] = {(v1->x() + v2->x()) *.5,(v1->y() + v2->y()) *.5,
                        (v1->z() + v2->z()) *.5};
      double avg2[3] = {(v3->x() + v4->x()) *.5,(v3->y() + v4->y()) *.5,
                        (v3->z() + v4->z()) *.5};
      
      GPoint gp1 = gf->point(SPoint2((Us[v1->getNum()] + Us[v2->getNum()]) * .5,
                                     (Vs[v1->getNum()] + Vs[v2->getNum()]) * .5));
      GPoint gp2 = gf->point(SPoint2((Us[v3->getNum()] + Us[v4->getNum()]) * .5,
                                     (Vs[v3->getNum()] + Vs[v4->getNum()]) * .5));
      double d1 = (avg1[0] - gp1.x()) * (avg1[0] - gp1.x()) + (avg1[1] - gp1.y()) * 
        (avg1[1]-gp1.y()) + (avg1[2] - gp1.z()) * (avg1[2] - gp1.z());
      double d2 = (avg2[0] - gp2.x()) * (avg2[0] - gp2.x()) + (avg2[1] - gp2.y()) *
        (avg2[1] - gp2.y()) + (avg2[2] - gp2.z()) * (avg2[2] - gp2.z());
      if (d1 < d2){
        delete t1b;
        delete t2b;
        return false;
      }      
    }
    break;
  default :
    throw;
  }

  std::list<MTri3*> cavity;
  for(int i = 0; i < 3; i++){    
    if (t1->getNeigh(i) && t1->getNeigh(i) != t2){      
      bool found = false;
      for (std::list<MTri3*>::iterator it = cavity.begin(); it != cavity.end(); it++){
        if (*it == t1->getNeigh(i)) found = true;
      }
      if (!found)cavity.push_back(t1->getNeigh(i));
    }
  }
  for(int i = 0; i < 3; i++){    
    if (t2->getNeigh(i) && t2->getNeigh(i) != t1){      
      bool found = false;
      for (std::list<MTri3*>::iterator it = cavity.begin(); it != cavity.end(); it++){
        if (*it == t2->getNeigh(i)) found = true;
      }
      if (!found)cavity.push_back(t2->getNeigh(i));
    }
  }
  double lc1 = 0.3333333333 * (vSizes[t1b->getVertex(0)->getNum()] +
                               vSizes[t1b->getVertex(1)->getNum()] +
                               vSizes[t1b->getVertex(2)->getNum()]);
  double lcBGM1 = 0.3333333333 * (vSizesBGM[t1b->getVertex(0)->getNum()] +
                                  vSizesBGM[t1b->getVertex(1)->getNum()] +
                                  vSizesBGM[t1b->getVertex(2)->getNum()]);
  double lc2 = 0.3333333333 * (vSizes[t2b->getVertex(0)->getNum()] +
                               vSizes[t2b->getVertex(1)->getNum()] +
                               vSizes[t2b->getVertex(2)->getNum()]);
  double lcBGM2 = 0.3333333333 * (vSizesBGM[t2b->getVertex(0)->getNum()] +
                                  vSizesBGM[t2b->getVertex(1)->getNum()] +
                                  vSizesBGM[t2b->getVertex(2)->getNum()]);
  MTri3 *t1b3 = new MTri3(t1b, Extend1dMeshIn2dSurfaces() ? 
                          std::min(lc1, lcBGM1) : lcBGM1);
  MTri3 *t2b3 = new MTri3(t2b, Extend1dMeshIn2dSurfaces() ?
                          std::min(lc2, lcBGM2) : lcBGM2);

  cavity.push_back(t1b3);
  cavity.push_back(t2b3);
  t1->setDeleted(true);
  t2->setDeleted(true);
  connectTriangles(cavity);
  newTris.push_back(t1b3);
  newTris.push_back(t2b3);

  return true;
}

inline double computeEdgeAdimLength(MVertex *v1, MVertex *v2, GFace *f,
                                    const std::vector<double> &Us,
                                    const std::vector<double> &Vs,
                                    const std::vector<double> &vSizes ,
                                    const std::vector<double> &vSizesBGM)
{  
  const double edgeCenter[2] ={(Us[v1->getNum()] + Us[v2->getNum()]) * .5,
                               (Vs[v1->getNum()] + Vs[v2->getNum()]) * .5};
  GPoint GP = f->point (edgeCenter[0], edgeCenter[1]);
  
  const double dx1 = v1->x() - GP.x();
  const double dy1 = v1->y() - GP.y();
  const double dz1 = v1->z() - GP.z();
  const double l1 = sqrt(dx1 * dx1 + dy1 * dy1 + dz1 * dz1);
  const double dx2 = v2->x() - GP.x();
  const double dy2 = v2->y() - GP.y();
  const double dz2 = v2->z() - GP.z();
  const double l2 = sqrt(dx2 * dx2 + dy2 * dy2 + dz2 * dz2);
  if (Extend1dMeshIn2dSurfaces())
    return 2 * (l1 + l2) / (std::min(vSizes[v1->getNum()], vSizesBGM[v1->getNum()]) +
                            std::min(vSizes[v2->getNum()], vSizesBGM[v2->getNum()]));
  return 2 * (l1 + l2) / (vSizesBGM[v1->getNum()] + vSizesBGM[v2->getNum()]);
}

bool gmshEdgeSplit(const double lMax, MTri3 *t1, GFace *gf, int iLocalEdge,
                   std::vector<MTri3*> &newTris, const gmshSplitCriterion &cr,             
                   std::vector<double> &Us, std::vector<double> &Vs,
                   std::vector<double> &vSizes, std::vector<double> &vSizesBGM)
{
  MTri3 *t2 = t1->getNeigh(iLocalEdge);
  if (!t2) return false;

  MVertex *v1 = t1->tri()->getVertex(iLocalEdge == 0 ? 2 : iLocalEdge - 1);
  MVertex *v2 = t1->tri()->getVertex(iLocalEdge % 3);
  double edgeCenter[2] ={(Us[v1->getNum()] + Us[v2->getNum()]) * .5,
                         (Vs[v1->getNum()] + Vs[v2->getNum()]) * .5};
 
  double al = computeEdgeAdimLength(v1, v2, gf,Us, Vs, vSizes, vSizesBGM);
  if (al <= lMax) return false;

  MVertex *v3 = t1->tri()->getVertex((iLocalEdge + 1) % 3);
  MVertex *v4 = 0 ;
  for (int i = 0; i < 3; i++)
    if (t2->tri()->getVertex(i) != v1 && t2->tri()->getVertex(i) != v2)
      v4 = t2->tri()->getVertex(i);

  GPoint p = gf->point(edgeCenter[0], edgeCenter[1]);
  MVertex *vnew = new MFaceVertex(p.x(), p.y(), p.z(), gf, 
                                  edgeCenter[0], edgeCenter[1]);

  MTriangle *t1b = new MTriangle(v1, vnew, v3);  
  MTriangle *t2b = new MTriangle(vnew, v2, v3); 
  MTriangle *t3b = new MTriangle(v1, v4, vnew); 
  MTriangle *t4b = new MTriangle(v4, v2, vnew); 
  
  switch(cr){
  case SPCR_QUAL:
    {
      const double triQualityRef = std::min(qmTriangle(t1->tri(), QMTRI_RHO),
                                            qmTriangle(t2->tri(), QMTRI_RHO));
      const double triQuality = 
        std::min(std::min(std::min(qmTriangle(t1b, QMTRI_RHO),
                                   qmTriangle(t2b, QMTRI_RHO)),
                          qmTriangle(t3b, QMTRI_RHO)), 
                 qmTriangle(t4b, QMTRI_RHO));
      if (triQuality < triQualityRef){
        delete t1b;
        delete t2b;
        delete t3b;
        delete t4b;
        delete vnew;
        return false;
      }
      break;
    }
  case SPCR_ALLWAYS:
    break;
  default :
    throw;
  }

  gf->mesh_vertices.push_back(vnew);
  vnew->setNum(Us.size());
  double lcN = 0.5 * (vSizes [v1->getNum()] + vSizes [v2->getNum()] );
  double lcBGM =  BGM_MeshSize(gf, edgeCenter[0], edgeCenter[1], p.x(), p.y(), p.z());
  
  vSizesBGM.push_back(lcBGM);
  vSizes.push_back(lcN);
  Us.push_back(edgeCenter[0]);
  Vs.push_back(edgeCenter[1]);

  std::list<MTri3*> cavity;
  for(int i = 0; i < 3; i++){    
    if (t1->getNeigh(i) && t1->getNeigh(i) != t2){      
      bool found = false;
      for (std::list<MTri3*>::iterator it = cavity.begin();it != cavity.end(); it++){
        if (*it == t1->getNeigh(i)) found = true;
      }
      if (!found) cavity.push_back(t1->getNeigh(i));
    }
  }
  for(int i = 0; i < 3; i++){
    if (t2->getNeigh(i) && t2->getNeigh(i) != t1){      
      bool found = false;
      for (std::list<MTri3*>::iterator it = cavity.begin(); it != cavity.end(); it++){
        if (*it == t2->getNeigh(i))found = true;
      }
      if (!found) cavity.push_back(t2->getNeigh(i));
    }
  }
  double lc1 = 0.3333333333 * (vSizes[t1b->getVertex(0)->getNum()] +
                               vSizes[t1b->getVertex(1)->getNum()] +
                               vSizes[t1b->getVertex(2)->getNum()]);
  double lcBGM1 = 0.3333333333 * (vSizesBGM[t1b->getVertex(0)->getNum()] +
                                  vSizesBGM[t1b->getVertex(1)->getNum()] +
                                  vSizesBGM[t1b->getVertex(2)->getNum()]);
  double lc2 = 0.3333333333 * (vSizes[t2b->getVertex(0)->getNum()] +
                               vSizes[t2b->getVertex(1)->getNum()] +
                               vSizes[t2b->getVertex(2)->getNum()]);
  double lcBGM2 = 0.3333333333 * (vSizesBGM[t2b->getVertex(0)->getNum()] +
                                  vSizesBGM[t2b->getVertex(1)->getNum()] +
                                  vSizesBGM[t2b->getVertex(2)->getNum()]);
  double lc3 = 0.3333333333 * (vSizes[t3b->getVertex(0)->getNum()] +
                               vSizes[t3b->getVertex(1)->getNum()] +
                               vSizes[t3b->getVertex(2)->getNum()]);
  double lcBGM3 = 0.3333333333 * (vSizesBGM[t3b->getVertex(0)->getNum()] +
                                  vSizesBGM[t3b->getVertex(1)->getNum()] +
                                  vSizesBGM[t3b->getVertex(2)->getNum()]);
  double lc4 = 0.3333333333 * (vSizes[t4b->getVertex(0)->getNum()] +
                               vSizes[t4b->getVertex(1)->getNum()] +
                               vSizes[t4b->getVertex(2)->getNum()]);
  double lcBGM4 = 0.3333333333 * (vSizesBGM[t4b->getVertex(0)->getNum()] +
                                  vSizesBGM[t4b->getVertex(1)->getNum()] +
                                  vSizesBGM[t4b->getVertex(2)->getNum()]);
  MTri3 *t1b3 = new MTri3(t1b, Extend1dMeshIn2dSurfaces() ? std::min(lc1, lcBGM1) : lcBGM1);
  MTri3 *t2b3 = new MTri3(t2b, Extend1dMeshIn2dSurfaces() ? std::min(lc2, lcBGM2) : lcBGM2);
  MTri3 *t3b3 = new MTri3(t3b, Extend1dMeshIn2dSurfaces() ? std::min(lc3, lcBGM3) : lcBGM3);
  MTri3 *t4b3 = new MTri3(t4b, Extend1dMeshIn2dSurfaces() ? std::min(lc4, lcBGM4) : lcBGM4);

  cavity.push_back(t1b3);
  cavity.push_back(t2b3);
  cavity.push_back(t3b3);
  cavity.push_back(t4b3);
  t1->setDeleted(true);
  t2->setDeleted(true);
  connectTriangles(cavity);
  newTris.push_back(t1b3);
  newTris.push_back(t2b3);
  newTris.push_back(t3b3);
  newTris.push_back(t4b3);

  return true;
}

void computeNeighboringTrisOfACavity(const std::vector<MTri3*> &cavity,
                                     std::vector<MTri3*> &outside)
{
  outside.clear();
  for (unsigned int i = 0; i < cavity.size(); i++){
    for (int j = 0; j < 3; j++){
      MTri3 * neigh = cavity[i]->getNeigh(j);
      if(neigh){
        bool found = false;
        for(unsigned int k = 0; k < outside.size(); k++){
          if(outside[k] == neigh){
            found = true;
            break;
          }
        }
        if(!found){
          for (unsigned int k = 0; k < cavity.size(); k++){
            if(cavity[k] == neigh){
              found = true;
            }
          }
        }
        if(!found) outside.push_back(neigh);
      }
    }
  }
}
        
bool gmshBuildVertexCavity(MTri3 *t, int iLocalVertex, MVertex **v1,
                           std::vector<MTri3*> &cavity, std::vector<MTri3*> &outside,
                           std::vector<MVertex*> &ring)
{
  cavity.clear();
  ring.clear();

  *v1 = t->tri()->getVertex(iLocalVertex);

  MVertex *lastinring = t->tri()->getVertex((iLocalVertex + 1) % 3);
  ring.push_back(lastinring);
  cavity.push_back(t);

  while (1){
    int iEdge = -1;
    for (int i = 0; i < 3; i++){
      MVertex *v2  = t->tri()->getVertex((i + 2) % 3);
      MVertex *v3  = t->tri()->getVertex(i);
      if ((v2 == *v1 && v3 == lastinring) ||
          (v2 == lastinring && v3 == *v1)){
        iEdge = i;
        t = t->getNeigh(i);
        if (t == cavity[0]) {
          computeNeighboringTrisOfACavity(cavity, outside);
          return true;
        }
        if (!t) return false;
        if (t->isDeleted()){ printf("weird\n"); throw; }  
        cavity.push_back(t);
        for (int j = 0; j < 3; j++){
          if (t->tri()->getVertex(j) !=lastinring && t->tri()->getVertex(j) != *v1){
            lastinring = t->tri()->getVertex(j);
            ring.push_back(lastinring);
            j = 100;
          }
        }
        break;
      }
    }
    if (iEdge == -1) { printf("not found\n"); throw; }
  }
}

bool gmshVertexCollapse(const double lMin, MTri3 *t1, GFace *gf,
                        int iLocalVertex, std::vector<MTri3*> &newTris,
                        std::vector<double> &Us, std::vector<double> &Vs,
                        std::vector<double> &vSizes, std::vector<double> &vSizesBGM)
{
  MVertex *v;
  std::vector<MTri3*> cavity;
  std::vector<MTri3*> outside;
  std::vector<MVertex*> ring ;

  if (!gmshBuildVertexCavity(t1, iLocalVertex, &v, cavity, outside, ring)) return false;
  
  double l_min = lMin;
  int iMin = -1;
  for (unsigned int i = 0; i < ring.size(); i++){
    double l = computeEdgeAdimLength(v, ring[i], gf, Us, Vs, vSizes, vSizesBGM);
    if (l < l_min){
      iMin = i;
    }
  }
  if(iMin == -1) return false;

  double surfBefore = 0.0;
  for(unsigned int i = 0; i < ring.size(); i++){
    MVertex *v1 = ring[i];
    MVertex *v2 = ring[(i + 1) % ring.size()];
    surfBefore += surfaceTriangleUV(v1, v2, v, Us, Vs);
  }

  double surfAfter = 0.0;
  for(unsigned int i = 0; i < ring.size() - 2; i++){
    MVertex *v1 = ring[(iMin + 1 + i) % ring.size()];
    MVertex *v2 = ring[(iMin + 1 + i + 1) % ring.size()];
    double sAfter = surfaceTriangleUV(v1, v2, ring[iMin], Us, Vs);
    double sBefore = surfaceTriangleUV(v1, v2, v, Us, Vs);
    if(sAfter < 0.1 * sBefore) return false;
    surfAfter += sAfter;
  }

  //  printf("%12.5E %12.5E %d\n",surfBefore,surfAfter,iMin);
  if(fabs(surfBefore - surfAfter) > 1.e-10*(surfBefore + surfAfter)) return false;

  for(unsigned int i = 0; i < ring.size() - 2; i++){
    MVertex *v1 = ring[(iMin + 1 + i) % ring.size()];
    MVertex *v2 = ring[(iMin + 1 + i + 1) % ring.size()];
    MTriangle *t = new MTriangle(v1,v2,ring[iMin]);
    double lc = 0.3333333333 * (vSizes[t->getVertex(0)->getNum()] +
                                vSizes[t->getVertex(1)->getNum()] +
                                vSizes[t->getVertex(2)->getNum()]);
    double lcBGM = 0.3333333333 * (vSizesBGM[t->getVertex(0)->getNum()] +
                                   vSizesBGM[t->getVertex(1)->getNum()] +
                                   vSizesBGM[t->getVertex(2)->getNum()]);
    MTri3 *t3 = new MTri3(t, Extend1dMeshIn2dSurfaces() ? std::min(lc, lcBGM) : lcBGM); 
    // printf("Creation %p = %d %d %d\n",t3,v1->getNum(),v2->getNum(),ring[iMin]->getNum());
    outside.push_back(t3);
    newTris.push_back(t3);
  }
  for(unsigned int i = 0; i < cavity.size(); i++)
    cavity[i]->setDeleted(true);
  connectTriangles(outside);
  return true;
}

int edgeSwapPass (GFace *gf, std::set<MTri3*, compareTri3Ptr> &allTris,
                  const gmshSwapCriterion &cr,
                  const std::vector<double> &Us, const std::vector<double> &Vs,
                  const std::vector<double> &vSizes, const std::vector<double> &vSizesBGM)
{
  typedef std::set<MTri3*, compareTri3Ptr> CONTAINER ;

  int nbSwapTot = 0;
  std::set<swapquad> configs;
  for (int iter = 0; iter < 1200; iter++){
    int nbSwap = 0;
    std::vector<MTri3*> newTris;
    for (CONTAINER::iterator it = allTris.begin(); it != allTris.end(); ++it){
      if (!(*it)->isDeleted()){
        for (int i = 0; i < 3; i++){
          if (gmshEdgeSwap(configs, *it, gf, i, newTris, cr, Us, Vs, vSizes, vSizesBGM)){
            nbSwap++;
            break;
          }
        }
      }
      else{
        delete *it;
        CONTAINER::iterator itb = it;
        ++it;
        allTris.erase(itb);
        if (it == allTris.end()) break;
      }
    }
    allTris.insert(newTris.begin(), newTris.end());
    nbSwapTot += nbSwap;
    if (nbSwap == 0) break;
  }  
  return nbSwapTot;
}

int edgeSplitPass(double maxLC, GFace *gf, std::set<MTri3*,compareTri3Ptr> &allTris,
                  const gmshSplitCriterion &cr,
                  std::vector<double> &Us, std::vector<double> &Vs,
                  std::vector<double> &vSizes, std::vector<double> &vSizesBGM)
{
  typedef std::set<MTri3*, compareTri3Ptr> CONTAINER ;
  std::vector<MTri3*> newTris;

  int nbSplit = 0;
  
  for (CONTAINER::iterator it = allTris.begin(); it != allTris.end(); ++it){
    if (!(*it)->isDeleted()){
      for (int i = 0; i < 3; i++){
        if (gmshEdgeSplit(maxLC, *it, gf, i, newTris, cr, Us, Vs, vSizes, vSizesBGM)) {
          nbSplit++;
          break;
        }
      }
    }
     else{
       CONTAINER::iterator itb = it;
       delete *it;
       ++it;
       allTris.erase(itb);
       if (it == allTris.end()) break;
     }
  }  
  printf("B %d %d tris ", (int)allTris.size(), (int)newTris.size());
  allTris.insert(newTris.begin(),newTris.end());
  printf("A %d %d tris\n", (int)allTris.size(), (int)newTris.size());
  return nbSplit;
}

int edgeCollapsePass(double minLC, GFace *gf, std::set<MTri3*,compareTri3Ptr> &allTris,
                     std::vector<double> &Us, std::vector<double> &Vs,
                     std::vector<double> &vSizes, std::vector<double> &vSizesBGM)
{
  typedef std::set<MTri3*,compareTri3Ptr> CONTAINER ;
  std::vector<MTri3*> newTris;

  int nbCollapse = 0;
  
  for (CONTAINER::reverse_iterator it = allTris.rbegin(); it != allTris.rend(); ++it){
    if (!(*it)->isDeleted()){
      for (int i = 0; i < 3; i++){
        if (gmshVertexCollapse(minLC, *it, gf, i, newTris, Us, Vs, vSizes, vSizesBGM)) {
          nbCollapse++;
          break;
        }
      }
    }
//      else{
//        CONTAINER::reverse_iterator itb = it;
//        delete *it;
//        ++it;
//        allTris.rerase(itb);
//         if (it == allTris.rend())break;
//      }
//     if (nbCollapse == 114)break;
  }  
  printf("B %d %d tris ", (int)allTris.size(), (int)newTris.size());
  allTris.insert(newTris.begin(),newTris.end());
  printf("A %d %d tris\n", (int)allTris.size(), (int)newTris.size());
  return nbCollapse;
}