Skip to content
Snippets Groups Projects
Forked from gmsh / gmsh
16749 commits behind the upstream repository.
meshGFaceOptimize.cpp 24.78 KiB
// $Id: meshGFaceOptimize.cpp,v 1.9 2008-02-06 07:33:49 geuzaine Exp $
//
// Copyright (C) 1997-2007 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 setLcsMax ( 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.e12;
	  vSizes[vj] = 1.e12;
	}
    }
}


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 > l)iti->second = l;
	  if (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;
  for (unsigned int i=0;i<gf->triangles.size();i++)setLcsMax ( gf->triangles[i] , vSizesMap);
  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 );      
  //  Msg(DEBUG,"All %d tris were connected",AllTris.size());
}

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);
  
  //  printf("%d %d %d %d %d\n",Us.size(),v1->getNum(),v2->getNum(),v3->getNum(),v4->getNum());
  
  //  printf("%d %d %d %d\n",tv1,tv2,tv3,tv4);

  swapquad sq (v1,v2,v3,v4);
  if (configs.find(sq) != configs.end())return false;
  configs.insert(sq);

  //  if (tv1 != 0 && tv1 == tv2 && tv1 == tv3 && tv1 == tv4)return false;

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

  ///  printf("%p %p %p %p\n",v2,v3,v4);
  MTriangle *t1b = new MTriangle (v2,v3,v4);  
  ///  printf("%p %p %p %p\n",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;
  //  printf("al,lMax %12.5E %12.5E \n",al,lMax);
  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);

//     printf("VERTEX %d\n",
//   	 t->tri()->getVertex(iLocalVertex)->getNum());

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

//     printf("START triangle %d %d %d, vertex %d\n",
//   	 t->tri()->getVertex(0)->getNum(),
//   	 t->tri()->getVertex(1)->getNum(),
//   	 t->tri()->getVertex(2)->getNum(),
//   	 lastinring->getNum());


  while (1){
    int iEdge = -1;
    //        printf("look for %d %d\n",(*v1)->getNum(),lastinring->getNum());
    for (int i=0;i<3;i++){
      MVertex *v2  = t->tri()->getVertex((i+2)%3);
      MVertex *v3  = t->tri()->getVertex(i);
      //            printf("--> %d %d\n",v2->getNum(),v3->getNum());
      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;
	  }
	}
//   	printf("CONTINUE (%d) triangle %p = %d %d %d, vertex %d size %d\n",i,
// 	       t,
//   	       t->tri()->getVertex(0)->getNum(),
//   	       t->tri()->getVertex(1)->getNum(),
//   	       t->tri()->getVertex(2)->getNum(),
//   	       lastinring->getNum(),ring.size());
	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 ;
  //  printf("%p \n",t1);
  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());
    //    printf("iter %d nbswam %d\n",iter,nbSwap);
    nbSwapTot+=nbSwap;
    if (nbSwap == 0)break;
  }  

  //  printf("B %d %d tris ",allTris.size(),newTris.size());
  //  printf("A %d %d tris\n",allTris.size(),newTris.size());
  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;
}