diff --git a/Common/CommandLine.cpp b/Common/CommandLine.cpp
index 0fd1b5c576fe62d22cecd5b76bcdc7bdddc188b8..cf34e971fd8e413432d511760a3c71698bbb986f 100644
--- a/Common/CommandLine.cpp
+++ b/Common/CommandLine.cpp
@@ -1,4 +1,4 @@
-// $Id: CommandLine.cpp,v 1.109 2007-12-03 15:17:39 remacle Exp $
+// $Id: CommandLine.cpp,v 1.110 2008-01-14 21:29:13 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -241,6 +241,10 @@ void Get_Options(int argc, char *argv[])
         CTX.mesh.optimize = 1;
         i++;
       }
+      else if(!strcmp(argv[i] + 1, "optimize_netgen")) {
+        CTX.mesh.optimizeNetgen = 1;
+        i++;
+      }
       else if(!strcmp(argv[i] + 1, "nopopup")) {
         CTX.nopopup = 1;
         i++;
@@ -353,6 +357,17 @@ void Get_Options(int argc, char *argv[])
             exit(1);
           }
         }
+      }
+      else if(!strcmp(argv[i] + 1, "clmin")) {
+        i++;
+        if(argv[i] != NULL) {
+          CTX.mesh.lc_min = atof(argv[i++]);
+          if(CTX.mesh.lc_factor <= 0.0) {
+            fprintf(stderr, ERROR_STR
+                    "Minimum length size must be > 0\n");
+            exit(1);
+          }
+        }
         else {
           fprintf(stderr, ERROR_STR "Missing number\n");
           exit(1);
diff --git a/Common/Context.h b/Common/Context.h
index 7933a761dc2ae866d22daab443ee3b8378ae53d8..d5a5390c3bcd09c884759f4959ba23fe510b5e78 100644
--- a/Common/Context.h
+++ b/Common/Context.h
@@ -181,7 +181,7 @@ public :
     int optimize, optimizeNetgen, refine_steps;
     int quality_type, label_type;
     double quality_inf, quality_sup, radius_inf, radius_sup;
-    double scaling_factor, lc_factor, rand_factor, lc_integration_precision;
+    double scaling_factor, lc_factor, rand_factor, lc_integration_precision,lc_min;
     int dual, draw_skin_only;
     int light, light_two_side, light_lines;
     int format, nb_smoothing, algo2d, algo3d, algo_recombine;
diff --git a/Common/DefaultOptions.h b/Common/DefaultOptions.h
index d12f23bda8cd97fad3535d635f199eb6a1a708a5..3d206e35d97fdaf3c49ed84633f9d6bdfa2e6c7f 100644
--- a/Common/DefaultOptions.h
+++ b/Common/DefaultOptions.h
@@ -895,6 +895,8 @@ StringXNumber MeshOptions_Number[] = {
     "Field format for Nastran BDF files (0=free, 1=small, 2=large)" },
   { F|O, "CharacteristicLengthFactor" , opt_mesh_lc_factor , 1.0 ,
     "Factor applied to all characteristic lengths" },
+  { F|O, "CharacteristicLengthFactor" , opt_mesh_lc_min, 0.0 ,
+    "Minimum mesh size" },
   { F|O, "CharacteristicLengthFromCurvature" , opt_mesh_lc_from_curvature , 0. ,
     "Compute characteritic lenghts automatically from curvatures" },
   { F|O, "ColorCarousel" , opt_mesh_color_carousel , 1. ,
diff --git a/Common/Options.cpp b/Common/Options.cpp
index 0c64aa4d900f0ccb2bcaed2ddc74bf6ae88897fe..6622d40d37ab22e88258aec3928a44cc8789e02f 100644
--- a/Common/Options.cpp
+++ b/Common/Options.cpp
@@ -1,4 +1,4 @@
-// $Id: Options.cpp,v 1.374 2008-01-12 18:40:14 geuzaine Exp $
+// $Id: Options.cpp,v 1.375 2008-01-14 21:29:13 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -4294,6 +4294,17 @@ double opt_mesh_lc_factor(OPT_ARGS_NUM)
   return CTX.mesh.lc_factor;
 }
 
+double opt_mesh_lc_min(OPT_ARGS_NUM)
+{
+  if(action & GMSH_SET)
+    CTX.mesh.lc_min = val;
+#if defined(HAVE_FLTK)
+  if(WID && (action & GMSH_GUI))
+    WID->mesh_value[25]->value(CTX.mesh.lc_min);
+#endif
+  return CTX.mesh.lc_min;
+}
+
 double opt_mesh_lc_from_curvature(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET)
diff --git a/Common/Options.h b/Common/Options.h
index 0de824f8824e442cfcf6c6121e21a48aa7343fdb..a507131e2b11024eebe666fb4733c49e8b5bcac5 100644
--- a/Common/Options.h
+++ b/Common/Options.h
@@ -433,6 +433,7 @@ double opt_mesh_normals(OPT_ARGS_NUM);
 double opt_mesh_tangents(OPT_ARGS_NUM);
 double opt_mesh_explode(OPT_ARGS_NUM);
 double opt_mesh_scaling_factor(OPT_ARGS_NUM);
+double opt_mesh_lc_min(OPT_ARGS_NUM);
 double opt_mesh_lc_factor(OPT_ARGS_NUM);
 double opt_mesh_lc_from_curvature(OPT_ARGS_NUM);
 double opt_mesh_rand_factor(OPT_ARGS_NUM);
diff --git a/Fltk/Callbacks.cpp b/Fltk/Callbacks.cpp
index 32f1b67047071f1b98581509656b7f49dd2cb791..2528340f2e2565f4f8916a919c2338682d0e0c4d 100644
--- a/Fltk/Callbacks.cpp
+++ b/Fltk/Callbacks.cpp
@@ -1,4 +1,4 @@
-// $Id: Callbacks.cpp,v 1.558 2008-01-12 18:40:14 geuzaine Exp $
+// $Id: Callbacks.cpp,v 1.559 2008-01-14 21:29:13 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -1130,6 +1130,7 @@ void mesh_options_ok_cb(CALLBACK_ARGS)
 
   opt_mesh_nb_smoothing(0, GMSH_SET, WID->mesh_value[0]->value());
   opt_mesh_lc_factor(0, GMSH_SET, WID->mesh_value[2]->value());
+  opt_mesh_lc_min(0, GMSH_SET, WID->mesh_value[25]->value());
   opt_mesh_quality_inf(0, GMSH_SET, WID->mesh_value[4]->value());
   opt_mesh_quality_sup(0, GMSH_SET, WID->mesh_value[5]->value());
   opt_mesh_radius_inf(0, GMSH_SET, WID->mesh_value[6]->value());
diff --git a/Fltk/GUI.cpp b/Fltk/GUI.cpp
index 2a10928305f778702dc6458ca38a7f6c582eb7f8..af5ab739c1c3e2137948fb937f58cc13e591f219 100644
--- a/Fltk/GUI.cpp
+++ b/Fltk/GUI.cpp
@@ -1,4 +1,4 @@
-// $Id: GUI.cpp,v 1.648 2008-01-12 18:40:14 geuzaine Exp $
+// $Id: GUI.cpp,v 1.649 2008-01-14 21:29:13 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -2432,7 +2432,11 @@ void GUI::create_option_window()
       mesh_value[2]->align(FL_ALIGN_RIGHT);
       mesh_value[2]->callback(mesh_options_ok_cb);
 
-      mesh_value[3] = new Fl_Value_Input(L + 2 * WB, 2 * WB + 6 * BH, IW, BH, "Element order");
+      mesh_value[25] = new Fl_Value_Input(L + 2 * WB, 2 * WB + 6 * BH, IW, BH, "Minimum mesh size");
+      mesh_value[25]->align(FL_ALIGN_RIGHT);
+      mesh_value[25]->callback(mesh_options_ok_cb);
+
+      mesh_value[3] = new Fl_Value_Input(L + 2 * WB, 2 * WB + 7 * BH, IW, BH, "Element order");
       mesh_value[3]->minimum(1);
       // FIXME: this makes it possible to set > 2 by hand, but not by
       // dragging (>2 is too buggy for general use)
@@ -2441,7 +2445,7 @@ void GUI::create_option_window()
       mesh_value[3]->align(FL_ALIGN_RIGHT);
       mesh_value[3]->callback(mesh_options_ok_cb);
 
-      mesh_butt[4] = new Fl_Check_Button(L + 2 * WB, 2 * WB + 7 * BH, BW, BH, "Use incomplete high order elements (8-node quads, etc.)");
+      mesh_butt[4] = new Fl_Check_Button(L + 2 * WB, 2 * WB + 8 * BH, BW, BH, "Use incomplete high order elements (8-node quads, etc.)");
       mesh_butt[4]->type(FL_TOGGLE_BUTTON);
       mesh_butt[4]->callback(mesh_options_ok_cb);
 
diff --git a/Geo/GEdge.cpp b/Geo/GEdge.cpp
index 0a45583fcceb29af4a9ae5c34d32ac866674253b..6cad537a008880179cba3c5c6ff48fbfd51bc585 100644
--- a/Geo/GEdge.cpp
+++ b/Geo/GEdge.cpp
@@ -1,4 +1,4 @@
-// $Id: GEdge.cpp,v 1.30 2007-07-26 16:28:27 anand Exp $
+// $Id: GEdge.cpp,v 1.31 2008-01-14 21:29:13 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -147,8 +147,6 @@ double GEdge::curvature(double par) const
   GPoint P1 = point(par - eps1);
   GPoint P2 = point(par + eps2);
 
-
-
   double D = sqrt ((P1.x() - P2.x()) * (P1.x() - P2.x()) +
 		   (P1.y() - P2.y()) * (P1.y() - P2.y()) +
 		   (P1.z() - P2.z()) * (P1.z() - P2.z()));
diff --git a/Geo/GEdgeLoop.cpp b/Geo/GEdgeLoop.cpp
index 53d7388b95d639e6a6bdead9a6551e387829c8cd..eb51d58c221ced1759670d51ddace49a13971f44 100644
--- a/Geo/GEdgeLoop.cpp
+++ b/Geo/GEdgeLoop.cpp
@@ -1,4 +1,4 @@
-// $Id: GEdgeLoop.cpp,v 1.6 2007-09-04 13:47:01 remacle Exp $
+// $Id: GEdgeLoop.cpp,v 1.7 2008-01-14 21:29:13 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -117,7 +117,7 @@ GEdgeLoop::GEdgeLoop(const std::list<GEdge*> &cwire)
 
   GEdgeSigned *prevOne = 0;
 
-  Msg(INFO,"Building a wire");
+  //  Msg(INFO,"Building a wire");
   GEdgeSigned ges(0,0);
   while(wire.size()){
     ges = nextOne(prevOne, wire);
@@ -126,7 +126,7 @@ GEdgeLoop::GEdgeLoop(const std::list<GEdge*> &cwire)
       break;
     }
     prevOne = &ges;
-    ges.print();
+    //    ges.print();
     loop.push_back(ges);
   }
 }
diff --git a/Geo/GEntity.h b/Geo/GEntity.h
index b4df6a9c599c54ec14a40749a15c22e3c0acad08..68d66bd808ef2abbc7a004b3e30606c2d6465734 100644
--- a/Geo/GEntity.h
+++ b/Geo/GEntity.h
@@ -72,6 +72,11 @@ class GEntity {
     Line,
     Circle,
     Ellipse,
+    Conic,
+    Parabola,
+    Hyperbola,
+    TrimmedCurve,
+    OffsetCurve,
     BSpline,
     Bezier,
     ParametricCurve,
@@ -105,6 +110,11 @@ class GEntity {
       "Line",
       "Circle",
       "Ellipse",
+      "Conic",
+      "Parabola",
+      "Hyperbola",
+      "TrimmedCurve",
+      "OffsetCurve",
       "BSpline",
       "Bezier",
       "Parametric curve",
diff --git a/Geo/GModel.cpp b/Geo/GModel.cpp
index 3e422fd5b56cb8e9057b426f1e24b7565b4a3901..3865e9d9354c541f7b8d647ef35fb3c1874ef037 100644
--- a/Geo/GModel.cpp
+++ b/Geo/GModel.cpp
@@ -1,4 +1,4 @@
-// $Id: GModel.cpp,v 1.50 2007-12-15 03:41:57 geuzaine Exp $
+// $Id: GModel.cpp,v 1.51 2008-01-14 21:29:13 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -529,7 +529,8 @@ static int checkVertices(std::vector<MVertex*> &vertices,
 }
 
 template <class T>
-static int checkElements(std::vector<T*> &elements,
+static int checkElements(int tag,
+			 std::vector<T*> &elements,
 			 std::set<MElement*, MElementLessThanLexicographic> &pos)
 {
   int num = 0;
@@ -540,8 +541,22 @@ static int checkElements(std::vector<T*> &elements,
       pos.insert(e);
     }
     else{
-      Msg(INFO, "Elements %d and %d have identical barycenter",
-	  (*it)->getNum(), e->getNum());
+      char temp[256];
+      char temp2[256];
+      sprintf(temp,"Elements %d tag %d(",(*it)->getNum(),tag);
+      for (int i=0;i<(*it)->getNumVertices();i++){
+	sprintf(temp2,"%d ",(*it)->getVertex(i)->getNum());	
+	strcat(temp,temp2);
+      }
+      sprintf(temp2,") and %d(",e->getNum());
+      strcat(temp,temp2);
+      for (int i=0;i<e->getNumVertices();i++){
+	sprintf(temp2,"%d ",e->getVertex(i)->getNum());	
+	strcat(temp,temp2);
+      }
+      sprintf(temp2,")have identical barycenter");      
+      strcat(temp,temp2);
+      Msg(INFO, "%s",temp);
       num++;
     }
   }
@@ -584,16 +599,16 @@ void GModel::checkMeshCoherence()
     std::set<MElement*, MElementLessThanLexicographic> pos;
     int num = 0;
     for(eiter it = firstEdge(); it != lastEdge(); ++it)
-      num += checkElements((*it)->lines, pos);
+      num += checkElements((*it)->tag(),(*it)->lines, pos);
     for(fiter it = firstFace(); it != lastFace(); ++it){
-      num += checkElements((*it)->triangles, pos);
-      num += checkElements((*it)->quadrangles, pos);
+      num += checkElements((*it)->tag(),(*it)->triangles, pos);
+      num += checkElements((*it)->tag(),(*it)->quadrangles, pos);
     }
     for(riter it = firstRegion(); it != lastRegion(); ++it){
-      num += checkElements((*it)->tetrahedra, pos);
-      num += checkElements((*it)->hexahedra, pos);
-      num += checkElements((*it)->prisms, pos);
-      num += checkElements((*it)->pyramids, pos);
+      num += checkElements((*it)->tag(),(*it)->tetrahedra, pos);
+      num += checkElements((*it)->tag(),(*it)->hexahedra, pos);
+      num += checkElements((*it)->tag(),(*it)->prisms, pos);
+      num += checkElements((*it)->tag(),(*it)->pyramids, pos);
     }
     if(num) Msg(WARNING, "%d duplicate elements", num);
     MElementLessThanLexicographic::tolerance = old_tol;
diff --git a/Geo/GModel.h b/Geo/GModel.h
index a698a8a34cae90d28b06c03448d5b01d4c71f648..d0bcc47d5ad9059c56e39182b58a78864cdd4907 100644
--- a/Geo/GModel.h
+++ b/Geo/GModel.h
@@ -188,6 +188,7 @@ class GModel
   int readOCCBREP(const std::string &name);
   int readOCCIGES(const std::string &name);
   int readOCCSTEP(const std::string &name);
+  void snapGVertices (void);
 
   // Mesh IO
   // =========================================
diff --git a/Geo/GModelIO_OCC.cpp b/Geo/GModelIO_OCC.cpp
index 02638bef6203e9420256e6896d06d7f57b1f2069..b24ffdbac7f419e90cfd75fb79b3e245d9080710 100644
--- a/Geo/GModelIO_OCC.cpp
+++ b/Geo/GModelIO_OCC.cpp
@@ -1,4 +1,4 @@
-// $Id: GModelIO_OCC.cpp,v 1.22 2007-10-14 09:51:17 geuzaine Exp $
+// $Id: GModelIO_OCC.cpp,v 1.23 2008-01-14 21:29:13 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -455,9 +455,42 @@ int GModel::readOCCBREP(const std::string &fn)
   occ_internals->loadBREP(fn.c_str());
   occ_internals->buildLists();
   occ_internals->buildGModel(this);
+  snapGVertices();
   return 1;
 }
 
+void GModel::snapGVertices (void)
+{
+  viter vit = firstVertex();
+  
+  double tol = CTX.geom.tolerance; 	       
+  
+  while (vit != lastVertex()){
+    std::list<GEdge*> edges = (*vit)->edges();
+    for (std::list<GEdge*>::iterator it = edges.begin() ;
+	 it != edges .end(); ++it){
+      Range<double> parb = (*it)->parBounds(0);
+      double t;	
+      if ((*it)->getBeginVertex() == *vit){
+	t = parb.low();
+      }
+      else if ((*it)->getEndVertex() == *vit){
+	t = parb.high();
+      }
+      else throw;
+      GPoint gp = (*it)->point(t);
+      double d = sqrt ((gp.x()-(*vit)->x())*(gp.x()-(*vit)->x())+
+		       (gp.y()-(*vit)->y())*(gp.y()-(*vit)->y())+
+		       (gp.z()-(*vit)->z())*(gp.z()-(*vit)->z()));
+      if (d > tol){
+	(*vit)->setPosition(gp);
+	Msg(WARNING, "Geom Vertex %d Corrupted (%12.5E)... Snap performed",(*vit)->tag(),d);
+      }      
+    }
+    vit++;
+  }
+}
+
 void GModel::deleteOCCInternals()
 {
   if(occ_internals) delete occ_internals;
diff --git a/Geo/GeoInterpolation.cpp b/Geo/GeoInterpolation.cpp
index 25d1f22f99b0012476eef32a171491f0931b1df2..c2f072d2cef9cc1cb967f1e9b0173cf575ab071d 100644
--- a/Geo/GeoInterpolation.cpp
+++ b/Geo/GeoInterpolation.cpp
@@ -1,4 +1,4 @@
-// $Id: GeoInterpolation.cpp,v 1.29 2007-09-03 20:09:14 geuzaine Exp $
+// $Id: GeoInterpolation.cpp,v 1.30 2008-01-14 21:29:13 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -238,13 +238,14 @@ Vertex InterpolateCurve(Curve * c, double u, int derivee)
   V.u = u;
 
   if(derivee) {
-    double eps = 1.e-5;
+    double eps1 = u==0?0:1.e-5;
+    double eps2 = u==1?0:1.e-5;
     Vertex D[2];
-    D[0] = InterpolateCurve(c, u, 0);
-    D[1] = InterpolateCurve(c, u + eps, 0);
-    V.Pos.X = (D[1].Pos.X - D[0].Pos.X) / eps;
-    V.Pos.Y = (D[1].Pos.Y - D[0].Pos.Y) / eps;
-    V.Pos.Z = (D[1].Pos.Z - D[0].Pos.Z) / eps;
+    D[0] = InterpolateCurve(c, u-eps1, 0);
+    D[1] = InterpolateCurve(c, u+eps2, 0);
+    V.Pos.X = (D[1].Pos.X - D[0].Pos.X) / (eps1+eps2);
+    V.Pos.Y = (D[1].Pos.Y - D[0].Pos.Y) / (eps1+eps2);
+    V.Pos.Z = (D[1].Pos.Z - D[0].Pos.Z) / (eps1+eps2);
     return V;
   }
 
diff --git a/Geo/MElement.cpp b/Geo/MElement.cpp
index 682897fc2a0b83eae82960416ba58ea4a20ae912..b8dd27abaffea06a324f035b37bccaa829c364f3 100644
--- a/Geo/MElement.cpp
+++ b/Geo/MElement.cpp
@@ -1,4 +1,4 @@
-// $Id: MElement.cpp,v 1.46 2007-11-26 14:34:09 remacle Exp $
+// $Id: MElement.cpp,v 1.47 2008-01-14 21:29:13 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -687,14 +687,35 @@ double coef5[15][15]={
     60.41666667, -38.54166667,   6.25000000}
 };
 
+void GeomShapeFunctionP1(double u, double v, double *sf) 
+{
+  for (int i = 0; i < 3; i++){
+    sf[i] = 0;
+    for(int j = 0; j < 3; j++){
+      sf[i] += coef1[i][j] * pow(u,P1[j][0]) * pow(v, P1[j][1]);
+    }
+  }
+}
+
+
 void GradGeomShapeFunctionP1(double u, double v, double grads[6][2]) 
 {
   for (int i = 0; i < 3; i++){
     grads[i][0] = 0;
     grads[i][1] = 0;
     for(int j = 0; j < 3; j++){
-      if(P1[j][0] > 0) grads[i][0] += coef1[i][j] * pow(u,P1[j][0] - 1) * pow(v, P1[j][1]);
-      if(P1[j][1] > 0) grads[i][1] += coef1[i][j] * pow(u,P1[j][0]) * pow(v, P1[j][1] - 1);
+      if(P1[j][0] > 0) grads[i][0] += coef1[i][j] * P1[j][0] * pow(u,P1[j][0] - 1) * pow(v, P1[j][1]);
+      if(P1[j][1] > 0) grads[i][1] += coef1[i][j] * P1[j][1] * pow(u,P1[j][0]) * pow(v, P1[j][1] - 1);
+    }
+  }
+}
+
+void GeomShapeFunctionP2(double u, double v, double *sf) 
+{
+  for (int i = 0; i < 6; i++){
+    sf[i] = 0;
+    for(int j = 0; j < 6; j++){
+      sf[i] += coef2[i][j] * pow(u,P2[j][0]) * pow(v, P2[j][1]);
     }
   }
 }
@@ -705,8 +726,18 @@ void GradGeomShapeFunctionP2(double u, double v, double grads[6][2])
     grads[i][0] = 0;
     grads[i][1] = 0;
     for (int j = 0; j < 6; j++){
-      if(P2[j][0] > 0) grads[i][0] += coef2[i][j] * pow(u, P2[j][0] - 1) * pow(v, P2[j][1]);
-      if(P2[j][1] > 0) grads[i][1] += coef2[i][j] * pow(u, P2[j][0]) * pow(v, P2[j][1] - 1);
+      if(P2[j][0] > 0) grads[i][0] += coef2[i][j] * P2[j][0] * pow(u, P2[j][0] - 1) * pow(v, P2[j][1]);
+      if(P2[j][1] > 0) grads[i][1] += coef2[i][j] * P2[j][1] * pow(u, P2[j][0]) * pow(v, P2[j][1] - 1);
+    }
+  }
+}
+
+void GeomShapeFunctionP3(double u, double v, double *sf) 
+{
+  for (int i = 0; i < 9; i++){
+    sf[i] = 0;
+    for(int j = 0; j < 9; j++){
+      sf[i] += coef3[i][j] * pow(u,P3[j][0]) * pow(v, P3[j][1]);
     }
   }
 }
@@ -717,20 +748,41 @@ void GradGeomShapeFunctionP3 (double u, double v, double grads[9][2])
     grads[i][0] = 0;
     grads[i][1] = 0;
     for(int j = 0; j < 9; j++){
-      if(P3[j][0] > 0) grads[i][0] += coef3[i][j] * pow(u, P3[j][0] - 1) * pow(v, P3[j][1]);
-      if(P3[j][1] > 0) grads[i][1] += coef3[i][j] * pow(u, P3[j][0]) * pow(v, P3[j][1] - 1);
+      if(P3[j][0] > 0) grads[i][0] += coef3[i][j] * P3[j][0] * pow(u, P3[j][0] - 1) * pow(v, P3[j][1]);
+      if(P3[j][1] > 0) grads[i][1] += coef3[i][j] * P3[j][1] * pow(u, P3[j][0]) * pow(v, P3[j][1] - 1);
     }
   }
 }
 
+void GeomShapeFunctionP4(double u, double v, double *sf) 
+{
+  for (int i = 0; i < 12; i++){
+    sf[i] = 0;
+    for(int j = 0; j < 12; j++){
+      sf[i] += coef4[i][j] * pow(u,P4[j][0]) * pow(v, P4[j][1]);
+    }
+  }
+}
+
+
 void GradGeomShapeFunctionP4(double u, double v, double grads[12][2]) 
 {
   for(int i = 0; i < 12; i++){
     grads[i][0] = 0;
     grads[i][1] = 0;
     for(int j = 0; j < 12; j++){
-      if(P4[j][0] > 0) grads[i][0] += coef4[i][j] * pow(u, P4[j][0] - 1) * pow(v, P4[j][1]);
-      if(P4[j][1] > 0) grads[i][1] += coef4[i][j] * pow(u, P4[j][0]) * pow(v, P4[j][1] - 1);
+      if(P4[j][0] > 0) grads[i][0] += coef4[i][j] * P4[j][0] * pow(u, P4[j][0] - 1) * pow(v, P4[j][1]);
+      if(P4[j][1] > 0) grads[i][1] += coef4[i][j] * P4[j][1] * pow(u, P4[j][0]) * pow(v, P4[j][1] - 1);
+    }
+  }
+}
+
+void GeomShapeFunctionP5(double u, double v, double *sf) 
+{
+  for (int i = 0; i < 15; i++){
+    sf[i] = 0;
+    for(int j = 0; j < 15; j++){
+      sf[i] += coef5[i][j] * pow(u,P5[j][0]) * pow(v, P5[j][1]);
     }
   }
 }
@@ -741,15 +793,17 @@ void GradGeomShapeFunctionP5(double u, double v, double grads[15][2])
     grads[i][0] = 0;
     grads[i][1] = 0;
     for (int j = 0; j < 15; j++){
-      if(P5[j][0] > 0) grads[i][0] += coef5[i][j] * pow(u, P5[j][0] - 1) * pow(v, P5[j][1]);
-      if(P5[j][1] > 0) grads[i][1] += coef5[i][j] * pow(u, P5[j][0]) * pow(v, P5[j][1] - 1);
+      if(P5[j][0] > 0) grads[i][0] += coef5[i][j] * P5[j][0] * pow(u, P5[j][0] - 1) * pow(v, P5[j][1]);
+      if(P5[j][1] > 0) grads[i][1] += coef5[i][j] * P5[j][1] * pow(u, P5[j][0]) * pow(v, P5[j][1] - 1);
     }
   }
 }
 
-void MTriangle::jac(int ord, MVertex *vs[], double uu, double vv, double j[2][2])
+void MTriangle::jac(int ord, MVertex *vs[], double uu, double vv, double j[2][3])
 {
   double grads[256][2];
+
+
   switch(ord){
   case 1: GradGeomShapeFunctionP1(uu, vv, grads); break;
   case 2: GradGeomShapeFunctionP2(uu, vv, grads); break;
@@ -762,23 +816,135 @@ void MTriangle::jac(int ord, MVertex *vs[], double uu, double vv, double j[2][2]
   j[1][0] = 0 ; for(int i = 0; i < 3; i++) j[1][0] += grads [i][1] * _v[i] -> x();
   j[0][1] = 0 ; for(int i = 0; i < 3; i++) j[0][1] += grads [i][0] * _v[i] -> y();
   j[1][1] = 0 ; for(int i = 0; i < 3; i++) j[1][1] += grads [i][1] * _v[i] -> y();
+  j[0][2] = 0 ; for(int i = 0; i < 3; i++) j[0][2] += grads [i][0] * _v[i] -> z();
+  j[1][2] = 0 ; for(int i = 0; i < 3; i++) j[1][2] += grads [i][1] * _v[i] -> z();
+
+
   for(int i = 3; i < 3 * ord; i++) j[0][0] += grads[i][0] * vs[i - 3] -> x();
   for(int i = 3; i < 3 * ord; i++) j[1][0] += grads[i][1] * vs[i - 3] -> x();
   for(int i = 3; i < 3 * ord; i++) j[0][1] += grads[i][0] * vs[i - 3] -> y();
   for(int i = 3; i < 3 * ord; i++) j[1][1] += grads[i][1] * vs[i - 3] -> y();
+  for(int i = 3; i < 3 * ord; i++) j[0][2] += grads[i][0] * vs[i - 3] -> z();
+  for(int i = 3; i < 3 * ord; i++) j[1][2] += grads[i][1] * vs[i - 3] -> z();
+
+}
+
+void MTriangle::pnt(int ord, MVertex *vs[], double uu, double vv, SPoint3 &p)
+{
+  double sf[256];
+
+  switch(ord){
+  case 1: GeomShapeFunctionP1(uu, vv, sf); break;
+  case 2: GeomShapeFunctionP2(uu, vv, sf); break;
+  case 3: GeomShapeFunctionP3(uu, vv, sf); break;
+  case 4: GeomShapeFunctionP4(uu, vv, sf); break;
+  case 5: GeomShapeFunctionP5(uu, vv, sf); break;
+  default: throw;
+  }
+  
+  double x = 0 ; for(int i = 0; i < 3; i++) x += sf[i] * _v[i] -> x();
+  double y = 0 ; for(int i = 0; i < 3; i++) y += sf[i] * _v[i] -> y();
+  double z = 0 ; for(int i = 0; i < 3; i++) z += sf[i] * _v[i] -> z();
+
+  for(int i = 3; i < 3 * ord; i++) x += sf[i] * vs[i - 3] -> x();
+  for(int i = 3; i < 3 * ord; i++) y += sf[i] * vs[i - 3] -> y();
+  for(int i = 3; i < 3 * ord; i++) z += sf[i] * vs[i - 3] -> z();
+
+  p = SPoint3(x,y,z);
+
 }
 
-void MTriangleN::jac(double uu, double vv , double j[2][2])  
+
+
+void MTriangleN::jac(double uu, double vv , double j[2][3])  
 {
   MTriangle::jac(_order, &(*(_vs.begin())), uu, vv, j);
 }
 
-void MTriangle6::jac(double uu, double vv , double j[2][2])  
+void MTriangleN::pnt(double uu, double vv, SPoint3 &p){
+  MTriangle::pnt(_order, &(*(_vs.begin())), uu, vv, p);
+}
+
+void MTriangle6::jac(double uu, double vv , double j[2][3])  
 {
   MTriangle::jac(2, _vs, uu, vv, j);
 }
 
-void MTriangle::jac(double uu, double vv, double j[2][2])
+void MTriangle6::pnt(double uu, double vv, SPoint3 &p){
+  MTriangle::pnt(2, _vs, uu, vv, p);
+}
+
+void MTriangle::jac(double uu, double vv, double j[2][3])
 {
   jac(1, 0, uu, vv, j);
 }
+
+void MTriangle::pnt(double uu, double vv, SPoint3 &p){
+  MTriangle::pnt(1, 0, uu, vv, p);
+}
+
+int MTriangle6::getNumEdgesRep(){ return 30; }
+void MTriangle6::getEdgeRep (int num, double *x, double *y, double *z, SVector3 *n){
+  if (num < 10){
+    SPoint3 pnt1,pnt2;
+    pnt ( (double)num/10.     , 0. , pnt1);
+    pnt ( (double)(num+1)/10. , 0. , pnt2);
+    x[0] = pnt1.x();x[1] = pnt2.x();
+    y[0] = pnt1.y();y[1] = pnt2.y();
+    z[0] = pnt1.z();z[1] = pnt2.z();
+    return;
+  }  
+
+  if (num < 20){
+    SPoint3 pnt1,pnt2;
+    num -=10;
+    pnt ( 1.-(double)num/10.     , (double)num/10. , pnt1);
+    pnt ( 1.-(double)(num+1)/10.     , (double)(num+1)/10. , pnt2);
+    x[0] = pnt1.x();x[1] = pnt2.x();
+    y[0] = pnt1.y();y[1] = pnt2.y();
+    z[0] = pnt1.z();z[1] = pnt2.z();
+    return ;
+  }  
+  {
+    SPoint3 pnt1,pnt2;
+    num -= 20;
+    pnt ( 0,(double)num/10.    , pnt1);
+    pnt ( 0,(double)(num+1)/10., pnt2);
+    x[0] = pnt1.x();x[1] = pnt2.x();
+    y[0] = pnt1.y();y[1] = pnt2.y();
+    z[0] = pnt1.z();z[1] = pnt2.z();
+  }
+}
+
+int MTriangleN::getNumEdgesRep(){ return 120; }
+void MTriangleN::getEdgeRep (int num, double *x, double *y, double *z, SVector3 *n){
+  if (num < 40){
+    SPoint3 pnt1,pnt2;
+    pnt ( (double)num/40.     , 0. , pnt1);
+    pnt ( (double)(num+1)/40. , 0. , pnt2);
+    x[0] = pnt1.x();x[1] = pnt2.x();
+    y[0] = pnt1.y();y[1] = pnt2.y();
+    z[0] = pnt1.z();z[1] = pnt2.z();
+    return;
+  }  
+
+  if (num < 80){
+    SPoint3 pnt1,pnt2;
+    num -=40;
+    pnt ( 1.-(double)num/40.     , (double)num/40. , pnt1);
+    pnt ( 1.-(double)(num+1)/40.     , (double)(num+1)/40. , pnt2);
+    x[0] = pnt1.x();x[1] = pnt2.x();
+    y[0] = pnt1.y();y[1] = pnt2.y();
+    z[0] = pnt1.z();z[1] = pnt2.z();
+    return ;
+  }  
+  {
+    SPoint3 pnt1,pnt2;
+    num -= 80;
+    pnt ( 0,(double)num/40.    , pnt1);
+    pnt ( 0,(double)(num+1)/40., pnt2);
+    x[0] = pnt1.x();x[1] = pnt2.x();
+    y[0] = pnt1.y();y[1] = pnt2.y();
+    z[0] = pnt1.z();z[1] = pnt2.z();
+  }
+}
diff --git a/Geo/MElement.h b/Geo/MElement.h
index f5d3fcbce7e19023fb2eb2ca5de6946adbfeab5a..f9c93b1436f0006c6be328215c96f3ec59c49bd0 100644
--- a/Geo/MElement.h
+++ b/Geo/MElement.h
@@ -304,8 +304,9 @@ class MLineN : public MLine {
 class MTriangle : public MElement {
  protected:
   MVertex *_v[3];
-  virtual void jac(int order, MVertex *verts[], double u, double v, double jac[2][2]);
  public :
+  virtual void jac(int order, MVertex *verts[], double u, double v, double jac[2][3]);
+  virtual void pnt(int order, MVertex *verts[], double u, double v, SPoint3 &);
   MTriangle(MVertex *v0, MVertex *v1, MVertex *v2, int num=0, int part=0) 
     : MElement(num, part)
   {
@@ -378,7 +379,8 @@ class MTriangle : public MElement {
   {
     MVertex *tmp = _v[1]; _v[1] = _v[2]; _v[2] = tmp;
   }
-  virtual void jac(double u, double v, double j[2][2]);
+  virtual void jac(double u, double v, double j[2][3]);
+  virtual void pnt(double u, double v, SPoint3&);
 };
 
 class MTriangle6 : public MTriangle {
@@ -408,16 +410,16 @@ class MTriangle6 : public MTriangle {
     return getVertex(map[num]); 
   }
   virtual int getNumEdgeVertices(){ return 3; }
-  virtual int getNumEdgesRep(){ return 6; }
-  virtual void getEdgeRep(int num, double *x, double *y, double *z, SVector3 *n)
-  { 
-    static const int e[6][2] = {
-      {0, 3}, {3, 1},
-      {1, 4}, {4, 2},
-      {2, 5}, {5, 0}
-    };
-    _getEdgeRep(getVertex(e[num][0]), getVertex(e[num][1]), x, y, z, n, 0);
-  }
+  virtual int getNumEdgesRep();
+  virtual void getEdgeRep(int num, double *x, double *y, double *z, SVector3 *n);
+/*   {  */
+/*     static const int e[6][2] = { */
+/*       {0, 3}, {3, 1}, */
+/*       {1, 4}, {4, 2}, */
+/*       {2, 5}, {5, 0} */
+/*     }; */
+/*     _getEdgeRep(getVertex(e[num][0]), getVertex(e[num][1]), x, y, z, n, 0); */
+/*   } */
   virtual int getNumFacesRep(){ return 4; }
   virtual void getFaceRep(int num, double *x, double *y, double *z, SVector3 *n)
   { 
@@ -437,7 +439,8 @@ class MTriangle6 : public MTriangle {
     tmp = _v[1]; _v[1] = _v[2]; _v[2] = tmp;
     tmp = _vs[0]; _vs[0] = _vs[2]; _vs[2] = tmp;
   }
-  virtual void jac(double u, double v, double j[2][2]);
+  virtual void jac ( double u, double v , double j[2][3]) ;
+  virtual void pnt(double u, double v, SPoint3&);
 };
 
 class MTriangleN : public MTriangle {
@@ -481,13 +484,8 @@ class MTriangleN : public MTriangle {
     throw;
   }
   virtual int getNumEdgeVertices(){ return _order - 1; }
-  virtual int getNumEdgesRep(){ return 3 * _order; }
-  virtual void getEdgeRep(int num, double *x, double *y, double *z, SVector3 *n)
-  { 
-    _getEdgeRep(getVertex(_orderedIndex(num)), 
-		getVertex(_orderedIndex((num + 1) % (3 * _order))),
-		x, y, z, n, 0);
-  }
+  virtual int getNumEdgesRep();
+  virtual void getEdgeRep(int num, double *x, double *y, double *z, SVector3 *n);
   virtual int getNumFacesRep(){ return 0; }
   virtual void getFaceRep(int num, double *x, double *y, double *z, SVector3 *n)
   { 
@@ -514,7 +512,8 @@ class MTriangleN : public MTriangle {
     inv.insert(inv.begin(), _vs.rbegin(), _vs.rend());
     _vs = inv;
   }
-  virtual void jac(double u, double v, double jac[2][2]);
+  virtual void jac(double u, double v, double jac[2][3]);
+  virtual void pnt(double u, double v, SPoint3&);
 };
 
 class MQuadrangle : public MElement {
diff --git a/Geo/MVertex.h b/Geo/MVertex.h
index 9406a296fbc6affb6be383189a115adba1d6264e..c5c26189e39a4b1f07d7edee24530af3b266787f 100644
--- a/Geo/MVertex.h
+++ b/Geo/MVertex.h
@@ -124,8 +124,8 @@ class MEdgeVertex : public MVertex{
   {
   }
   virtual ~MEdgeVertex(){}
-  virtual bool getParameter(int i, double &par) const{ par = _u; return true; }
-  virtual bool setParameter(int i, double par){ _u = par; return true; }
+  virtual bool getParameter(int i, double &par) const{ if(i)throw; par = _u; return true; }
+  virtual bool setParameter(int i, double par){ if(i)throw; _u = par; return true; }
   double getLc () const {return _lc;}
 };
 
diff --git a/Geo/OCCEdge.cpp b/Geo/OCCEdge.cpp
index 95223af5c84936f8e29b7a2f392da5d131c68c59..c60cac8465ab0289bd90e5a5abb0cb0044782567 100644
--- a/Geo/OCCEdge.cpp
+++ b/Geo/OCCEdge.cpp
@@ -1,4 +1,4 @@
-// $Id: OCCEdge.cpp,v 1.28 2007-12-03 15:17:40 remacle Exp $
+// $Id: OCCEdge.cpp,v 1.29 2008-01-14 21:29:13 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -29,10 +29,14 @@ extern Context_T CTX;
 #if defined(HAVE_OCC)
 #include "Geom2dLProp_CLProps2d.hxx"
 #include "Geom_BezierCurve.hxx"
-#include "Geom_BezierCurve.hxx"
+#include "Geom_OffsetCurve.hxx"
 #include "Geom_Ellipse.hxx"
+#include "Geom_Parabola.hxx"
+#include "Geom_Hyperbola.hxx"
+#include "Geom_TrimmedCurve.hxx"
 #include "Geom_Circle.hxx"
 #include "Geom_Line.hxx"
+#include "Geom_Conic.hxx"
 
 OCCEdge::OCCEdge(GModel *model, TopoDS_Edge edge, int num, GVertex *v1, GVertex *v2)
   : GEdge(model, num, v1, v2), c(edge), trimmed(0)
@@ -89,12 +93,12 @@ SPoint2 OCCEdge::reparamOnFace(GFace *face, double epar,int dir) const
     const double dz = p1.z()-p2.z();
     if(sqrt(dx*dx+dy*dy+dz*dz) > 1.e-4 * CTX.lc){
       // return reparamOnFace(face, epar,-1);      
-//       Msg(WARNING, "Reparam on face partially failed for curve %d surface %d at point %g",
-// 	  tag(), face->tag(), epar);
-//       Msg(WARNING, "On the face %d local (%g %g) global (%g %g %g)",
-// 	  face->tag(), u, v, p2.x(), p2.y(), p2.z());
-//       Msg(WARNING, "On the edge %d local (%g) global (%g %g %g)",
-// 	  tag(), epar, p1.x(), p1.y(), p1.z());
+       Msg(WARNING, "Reparam on face partially failed for curve %d surface %d at point %g",
+ 	  tag(), face->tag(), epar);
+       Msg(WARNING, "On the face %d local (%g %g) global (%g %g %g)",
+ 	  face->tag(), u, v, p2.x(), p2.y(), p2.z());
+       Msg(WARNING, "On the edge %d local (%g) global (%g %g %g)",
+ 	  tag(), epar, p1.x(), p1.y(), p1.z());
 //      GPoint ppp = face->closestPoint(SPoint3(p1.x(), p1.y(), p1.z()));
 //      return SPoint2(ppp.u(), ppp.v());
     }
@@ -118,15 +122,15 @@ int OCCEdge::isSeam(GFace *face) const
 
 GPoint OCCEdge::point(double par) const
 {
-  if(!curve.IsNull()){
-    gp_Pnt pnt = curve->Value (par);
-    return GPoint(pnt.X(), pnt.Y(), pnt.Z());
-  }
-  else if(trimmed){
+  if(trimmed){
     double u, v;
     curve2d->Value(par).Coord(u, v);
     return trimmed->point(u, v);
   }
+  else if(!curve.IsNull()){
+    gp_Pnt pnt = curve->Value (par);
+    return GPoint(pnt.X(), pnt.Y(), pnt.Z());
+  }
   else{
     Msg(WARNING, "OCC Curve %d is neither a 3D curve not a trimmed curve", tag());
     return GPoint(0, 0, 0);
@@ -167,10 +171,20 @@ GEntity::GeomType OCCEdge::geomType() const
       return Line;
     else if (curve2d->DynamicType() == STANDARD_TYPE(Geom_Ellipse))
       return Ellipse;
+    else if (curve2d->DynamicType() == STANDARD_TYPE(Geom_Parabola))
+      return Parabola;
+    else if (curve2d->DynamicType() == STANDARD_TYPE(Geom_Hyperbola))
+      return Hyperbola;
+    else if (curve2d->DynamicType() == STANDARD_TYPE(Geom_TrimmedCurve))
+      return TrimmedCurve;
+    else if (curve2d->DynamicType() == STANDARD_TYPE(Geom_OffsetCurve))
+      return OffsetCurve;
     else if (curve2d->DynamicType() == STANDARD_TYPE(Geom_BSplineCurve))
       return BSpline;
     else if (curve2d->DynamicType() == STANDARD_TYPE(Geom_BezierCurve))
       return Bezier;
+    else if (curve2d->DynamicType() == STANDARD_TYPE(Geom_Conic))
+      return Conic;
     return Unknown;
   }
   else{
@@ -178,12 +192,22 @@ GEntity::GeomType OCCEdge::geomType() const
       return Circle;
     else if (curve->DynamicType() == STANDARD_TYPE(Geom_Line))
       return Line;
+    else if (curve->DynamicType() == STANDARD_TYPE(Geom_Parabola))
+      return Parabola;
+    else if (curve->DynamicType() == STANDARD_TYPE(Geom_Hyperbola))
+      return Hyperbola;
+    else if (curve->DynamicType() == STANDARD_TYPE(Geom_TrimmedCurve))
+      return TrimmedCurve;
+    else if (curve->DynamicType() == STANDARD_TYPE(Geom_OffsetCurve))
+      return OffsetCurve;
     else if (curve->DynamicType() == STANDARD_TYPE(Geom_Ellipse))
       return Ellipse;
     else if (curve->DynamicType() == STANDARD_TYPE(Geom_BSplineCurve))
       return BSpline;
     else if (curve->DynamicType() == STANDARD_TYPE(Geom_BezierCurve))
       return Bezier;
+    else if (curve2d->DynamicType() == STANDARD_TYPE(Geom_Conic))
+      return Conic;
     return Unknown;
   }
 }
diff --git a/Geo/OCCVertex.h b/Geo/OCCVertex.h
index cc4150467153c171ecb3c79ce46bc1274abfb124..49bd651935169a8dbbe8422f90181206918055e9 100644
--- a/Geo/OCCVertex.h
+++ b/Geo/OCCVertex.h
@@ -30,12 +30,17 @@
 class OCCVertex : public GVertex {
  protected:
   TopoDS_Vertex v;
+  double _x,_y,_z;
   mutable double max_curvature;
   double max_curvature_of_surfaces() const;
  public:
   OCCVertex(GModel *m, int num, TopoDS_Vertex _v) : GVertex(m, num), v(_v)
   {
     max_curvature = -1;
+    gp_Pnt pnt = BRep_Tool::Pnt (v);
+    _x = pnt.X();
+    _y = pnt.Y();
+    _z = pnt.Z();
     mesh_vertices.push_back(new MVertex(x(), y(), z(), this));
   }
   virtual ~OCCVertex() {}
@@ -45,19 +50,25 @@ class OCCVertex : public GVertex {
   }
   virtual double x() const 
   {
-    gp_Pnt pnt = BRep_Tool::Pnt (v);
-    return pnt.X();
+    return _x;
   }
   virtual double y() const 
   {
-    gp_Pnt pnt = BRep_Tool::Pnt (v);
-    return pnt.Y();
+    return _y;
   }
   virtual double z() const 
   {
-    gp_Pnt pnt = BRep_Tool::Pnt (v);
-    return pnt.Z();
+    return _z;
   }
+  virtual void setPosition(GPoint &p){
+    _x = p.x();
+    _y = p.y();
+    _z = p.z();
+    delete mesh_vertices[0];
+    mesh_vertices.clear();
+    mesh_vertices.push_back(new MVertex(x(), y(), z(), this));
+  }
+
   ModelType getNativeType() const { return OpenCascadeModel; }
   void * getNativePtr() const { return (void*) &v; }
   virtual SPoint2 reparamOnFace ( GFace *gf , int) const;
diff --git a/Mesh/BDS.cpp b/Mesh/BDS.cpp
index bb5ad7cf6c1f6862cecfaeeb2c94bd140e30b3c9..107586b149c268302cae6f165bedfcc999ceed82 100644
--- a/Mesh/BDS.cpp
+++ b/Mesh/BDS.cpp
@@ -1,4 +1,4 @@
-// $Id: BDS.cpp,v 1.86 2007-11-26 14:34:09 remacle Exp $
+// $Id: BDS.cpp,v 1.87 2008-01-14 21:29:13 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -44,7 +44,7 @@ void outputScalarField(std::list < BDS_Face * >t, const char *iii, int param)
       fprintf(f, "ST(%g,%g,%g,%g,%g,%g,%g,%g,%g){%g,%g,%g};\n",
 	      pts[0]->u, pts[0]->v, 0.0,
 	      pts[1]->u, pts[1]->v, 0.0,
-	      pts[2]->u, pts[2]->v, 0.0,(double)pts[0]->iD,(double)pts[1]->iD,(double)pts[2]->iD);
+	      pts[2]->u, pts[2]->v, 0.0,(double)pts[0]->lc(),(double)pts[1]->lc(),(double)pts[2]->lc());
     else
       fprintf(f, "ST(%g,%g,%g,%g,%g,%g,%g,%g,%g){%g,%g,%g};\n",
 	      pts[0]->X, pts[0]->Y, pts[0]->Z,
@@ -1166,6 +1166,103 @@ bool test_move_point_parametric_triangle (BDS_Point * p, double u, double v, BDS
   return ori_init*ori_final > 0;
 }
 
+
+/**
+   d^2_i = (x^2_i - x)^T M (x_i - x)  
+         = M11 (x_i - x)^2 + 2 M21 (x_i-x)(y_i-y) + M22 (y_i-y)^2	 
+
+	~:-)
+
+
+*/
+
+struct smoothVertexData{
+  BDS_Point *p;
+  GFace *gf;
+  double scalu,scalv;
+  std::list < BDS_Face * >ts;
+}; 
+
+double smoothing_objective_function(double U, double V, BDS_Point *v, std::list < BDS_Face * >&ts,double su, double sv,GFace *gf){
+
+  GPoint gp = gf->point(U*su,V*sv);
+
+  const double oldX = v->X;
+  const double oldY = v->Y;
+  const double oldZ = v->Z;
+  v->X = gp.x();
+  v->Y = gp.y();
+  v->Z = gp.z();
+
+  std::list < BDS_Face * >::iterator it = ts.begin();
+  std::list < BDS_Face * >::iterator ite = ts.end();
+  double qMin=1;
+  while(it != ite) {
+    BDS_Face *t = *it;
+    qMin = std::min(qmTriangle(*it,QMTRI_RHO),qMin);
+    ++it;
+  }
+  v->X = oldX;
+  v->Y = oldY;
+  v->Z = oldZ;
+  return -qMin;  
+}
+
+void deriv_smoothing_objective_function(double U, double V, 
+					double &F, double &dFdU, double &dFdV,void *data){
+  smoothVertexData *svd = (smoothVertexData*)data;
+  BDS_Point *v = svd->p;
+  const double LARGE = 1.e5;
+  const double SMALL = 1./LARGE;
+  F   = smoothing_objective_function(U,V,v,svd->ts,svd->scalu,svd->scalv,svd->gf);
+  double F_U = smoothing_objective_function(U+SMALL,V,v,svd->ts,svd->scalu,svd->scalv,svd->gf);
+  double F_V = smoothing_objective_function(U,V+SMALL,v,svd->ts,svd->scalu,svd->scalv,svd->gf);
+  dFdU = (F_U-F)*LARGE;
+  dFdV = (F_V-F)*LARGE;
+}
+
+double smooth_obj(double U, double V, void *data){
+  smoothVertexData *svd = (smoothVertexData*)data;
+  return  smoothing_objective_function(U,V,svd->p,svd->ts,svd->scalu,svd->scalv,svd->gf); 
+}
+
+
+void optimize_vertex_position (GFace *GF, BDS_Point *data, double su, double sv){
+#ifdef HAVE_GSL
+  if(data->g && data->g->classif_degree <= 1)
+    return;
+  smoothVertexData vd;
+  vd.p = data;
+  vd.scalu = su;
+  vd.scalv = sv;
+  vd.gf = GF;
+  data->getTriangles(vd.ts);
+  double U=data->u,V=data->v,val;
+
+  val = smooth_obj(U, V, &vd);
+  if (val < -.90)return;
+
+  minimize_2 ( smooth_obj, deriv_smoothing_objective_function, &vd, 5, U,V,val);
+  std::list < BDS_Face * >::iterator it = vd.ts.begin();
+  std::list < BDS_Face * >::iterator ite = vd.ts.end();
+  while(it != ite) {
+    BDS_Face *t = *it;
+    if (!test_move_point_parametric_triangle ( data, U, V, t)){
+      return;          
+    }
+    ++it;
+  }
+
+  data->u = U;
+  data->v = V;
+  GPoint gp = GF->point(U*su,V*sv);
+  data->X = gp.x();
+  data->Y = gp.y();
+  data->Z = gp.z();  
+#endif
+}
+
+
 bool BDS_Mesh::smooth_point_centroid(BDS_Point * p, GFace *gf)
 {
 
@@ -1193,8 +1290,8 @@ bool BDS_Mesh::smooth_point_centroid(BDS_Point * p, GFace *gf)
     BDS_Face *t = *it;
     BDS_Point *n[4];
     t->getNodes(n);
-    double S = fabs(surface_triangle(n[0],n[1],n[2])); 
-    S = 1;
+    //    double S = fabs(surface_triangle(n[0],n[1],n[2])); 
+    double S = 1;
     sTot += S;
     U  += (n[0]->u + n[1]->u + n[2]->u) *S;
     V  += (n[0]->v + n[1]->v + n[2]->v) *S;
@@ -1211,14 +1308,25 @@ bool BDS_Mesh::smooth_point_centroid(BDS_Point * p, GFace *gf)
   const double oldY = p->Y;
   const double oldZ = p->Z;
 
-  double oldWorst=1.;
-  double newWorst=1.;
+  double oldU=U;
+  double oldV=V;
 
   it = ts.begin();
+  double s1=0,s2=0;
   while(it != ite) {
     BDS_Face *t = *it;
-    if (!test_move_point_parametric_triangle ( p, U, V, t))
-      return false;    
+    BDS_Point *n[4];
+    t->getNodes(n);
+    p->u = U;
+    p->v = V;
+    s1 += fabs(surface_triangle_param(n[0],n[1],n[2])); 
+    p->u = oldU;
+    p->v = oldV;
+    s2 += fabs(surface_triangle_param(n[0],n[1],n[2])); 
+
+//     if (!test_move_point_parametric_triangle ( p, U, V, t)){
+//       return false;    
+//     }
 //     p->X = gp.x();
 //     p->Y = gp.y();
 //     p->Z = gp.z();
@@ -1230,6 +1338,7 @@ bool BDS_Mesh::smooth_point_centroid(BDS_Point * p, GFace *gf)
     ++it;
   }
   
+  if (fabs(s2-s1) > 1.e-10 * (s2+s1))return false;
 
 //   if (newWorst < 1.e-2)
 //     {
diff --git a/Mesh/BDS.h b/Mesh/BDS.h
index bc7aa40110c8c0cebb4ac351f8280f562e7a93cf..9019311c0bb14ed30167964e824bddfdc8dea096 100644
--- a/Mesh/BDS.h
+++ b/Mesh/BDS.h
@@ -45,6 +45,7 @@ void vector_triangle(BDS_Point *p1, BDS_Point *p2, BDS_Point *p3, double c[3]);
 void normal_triangle(BDS_Point *p1, BDS_Point *p2, BDS_Point *p3, double c[3]); 
 double surface_triangle(BDS_Point *p1, BDS_Point *p2, BDS_Point *p3); 
 double surface_triangle_param(BDS_Point *p1, BDS_Point *p2, BDS_Point *p3); 
+void optimize_vertex_position (GFace *GF, BDS_Point *data, double su, double sv);
 
 
 class BDS_GeomEntity
diff --git a/Mesh/BackgroundMesh.cpp b/Mesh/BackgroundMesh.cpp
index 08a2dcb922e227b1f37cd9f9e543dc1bfb55725e..1a8f280bb3a8e47e006541e619e21db27dabf3c2 100644
--- a/Mesh/BackgroundMesh.cpp
+++ b/Mesh/BackgroundMesh.cpp
@@ -1,4 +1,4 @@
-// $Id: BackgroundMesh.cpp,v 1.30 2007-11-26 14:34:09 remacle Exp $
+// $Id: BackgroundMesh.cpp,v 1.31 2008-01-14 21:29:14 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -70,25 +70,6 @@ static double max_edge_curvature(const GVertex *gv)
   return max_curvature;
 }
 
-static double max_surf_curvature(const GVertex *gv)
-{
-  double max_curvature = 0;
-  std::list<GEdge*> l_edges = gv->edges();
-  for (std::list<GEdge*>::const_iterator ite = l_edges.begin(); 
-       ite != l_edges.end(); ++ite){
-    GEdge *_myGEdge = *ite;
-    std::list<GFace *> faces = _myGEdge->faces();
-    std::list<GFace *>::iterator it = faces.begin();
-    while (it != faces.end()){
-      SPoint2 par = gv->reparamOnFace((*it),1);
-      double cc = (*it)->curvature ( par );
-      max_curvature = std::max ( cc , max_curvature);  
-      ++it;
-    }  
-  }
-  return max_curvature;
-}
-
 static double max_surf_curvature(const GEdge *ge, double u)
 {
   double max_curvature = 0;
@@ -103,6 +84,23 @@ static double max_surf_curvature(const GEdge *ge, double u)
   return max_curvature;
 }
 
+static double max_surf_curvature(const GVertex *gv)
+{
+  double max_curvature = 0;
+  std::list<GEdge*> l_edges = gv->edges();
+  for (std::list<GEdge*>::const_iterator ite = l_edges.begin(); 
+       ite != l_edges.end(); ++ite){
+    GEdge *_myGEdge = *ite;
+    Range<double> bounds = _myGEdge->parBounds(0);
+    if (gv == _myGEdge->getBeginVertex())
+      max_curvature = std::max(max_curvature, max_surf_curvature(_myGEdge,bounds.low()));
+    else
+      max_curvature = std::max(max_curvature, max_surf_curvature(_myGEdge,bounds.high()));
+  }
+  return max_curvature;
+}
+
+
 // the mesh vertex is classified on a model vertex.  we compute the
 // maximum of the curvature of model faces surrounding this point if
 // it is classified on a model edge, we do the same for all model
@@ -114,17 +112,18 @@ double LC_MVertex_CURV(GEntity *ge, double U, double V)
   double Crv = 0;
   switch(ge->dim()){
   case 0:
-    Crv = max_edge_curvature ( (const GVertex *)ge);
-    Crv = std::max(max_surf_curvature ( (const GVertex *)ge),Crv);
+    //    Crv = max_edge_curvature ( (const GVertex *)ge);
+    //    Crv = std::max(max_surf_curvature ( (const GVertex *)ge),Crv);
+    Crv = max_surf_curvature ( (const GVertex *)ge);
     //    printf("point %d coucou %g\n",ge->tag(),Crv);
     break;
   case 1:
     {
       GEdge *ged = (GEdge *)ge;
-      Crv = ged->curvature(U);
+      //      Crv = ged->curvature(U);
       //      printf("coucou %12.5E %d\n",Crv,CTX.mesh.min_circ_points);
-      Crv = std::max(Crv,max_surf_curvature(ged, U));
-      
+      //      Crv = std::max(Crv,max_surf_curvature(ged, U));
+      Crv = max_surf_curvature(ged, U);      
     }
     break;
   case 2:
@@ -194,15 +193,16 @@ double BGM_MeshSize(GEntity *ge, double U, double V, double X, double Y, double
     lc = std::min(std::min(std::min(l1, l2), l3), l4) * CTX.mesh.lc_factor;
   }
 
-
-  if(CTX.mesh.lc_from_curvature && ge->dim() < 3)
+  if(CTX.mesh.lc_from_curvature && ge->dim() <= 2 )
     lc = std::min (lc,LC_MVertex_CURV(ge, U, V));
 
+  lc = std::max(lc,CTX.mesh.lc_min*CTX.mesh.lc_factor);
+
   if(lc <= 0.){
     Msg(GERROR, "Incorrect char. length lc = %g: using default instead", lc);
     return l3 * CTX.mesh.lc_factor;
   }
-
+  
   return lc;
 }
 
@@ -211,7 +211,7 @@ double BGM_MeshSize(GEntity *ge, double U, double V, double X, double Y, double
 // we do it also if CTX.mesh.constrained_bgmesh is true;
 bool Extend1dMeshIn2dSurfaces()
 {
-  if(lc_field.empty() && !CTX.mesh.lc_from_curvature) return true;
+  if(lc_field.empty()) return true;
   if(CTX.mesh.constrained_bgmesh) return true;
   return false;
 }
diff --git a/Mesh/BackgroundMesh.h b/Mesh/BackgroundMesh.h
index ec7958c8cb3a1842e086f935a6b8cf02e6969b25..30bd8facee1771732bc117472e47321b157a3161 100644
--- a/Mesh/BackgroundMesh.h
+++ b/Mesh/BackgroundMesh.h
@@ -28,4 +28,6 @@ void BGMAddField(Field *field);
 void BGMReset();
 bool Extend1dMeshIn2dSurfaces ();
 bool Extend2dMeshIn3dVolumes  ();
+double LC_MVertex_CURV(GEntity *ge, double U, double V);
+double LC_MVertex_PNTS(GEntity *ge, double U, double V);
 #endif
diff --git a/Mesh/Field.cpp b/Mesh/Field.cpp
index 2c66a453c1c4f7ca33a89c4cd17ad575dea3a1ae..ab3d73428b3ffc07e9531df78f45a71cc60f08cc 100644
--- a/Mesh/Field.cpp
+++ b/Mesh/Field.cpp
@@ -1,4 +1,4 @@
-// $Id: Field.cpp,v 1.8 2007-11-09 08:07:53 geuzaine Exp $
+// $Id: Field.cpp,v 1.9 2008-01-14 21:29:14 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -26,6 +26,7 @@
 #include "Field.h"
 #include "Context.h"
 #include "GeoInterpolation.h"
+#include "BackgroundMesh.h"
 #include "GModel.h"
 #ifdef HAVE_MATH_EVAL
 #include "matheval.h"
@@ -448,6 +449,50 @@ AttractorField_1DMesh::AttractorField_1DMesh(GModel *m, double dmax, double dmin
   }
 }
 
+AttractorField_1DMesh::AttractorField_1DMesh(GFace *gf, double dmax, double dmin, 
+					     double lcmax)
+  : _dmax(dmax), _dmin(dmin), _lcmax(lcmax)
+{
+  std::list<GEdge*> edges = gf->edges(); 
+  std::list<GEdge*>::iterator it = edges.begin();
+
+  std::map<MVertex*, double> maplc;
+  std::map<MVertex*, double> maplc2;
+
+  while(it != edges.end()){
+    MVertex *first = (*it)->getBeginVertex()->mesh_vertices[0];
+    for(unsigned int i = 1; i <= (*it)->mesh_vertices.size(); ++i){
+      MVertex *last = (i == (*it)->mesh_vertices.size()) ? 
+	(*it)->getEndVertex()->mesh_vertices[0] : (*it)->mesh_vertices[i];
+      double l = sqrt((first->x() - last->x()) * (first->x() - last->x()) +
+		      (first->y() - last->y()) * (first->y() - last->y()) +
+		      (first->z() - last->z()) * (first->z() - last->z()));
+      double t;
+      last->getParameter(0,t);
+      double l2 = LC_MVertex_PNTS(last->onWhat(),t,0);
+      addMapLc(maplc, first, l);
+      addMapLc(maplc, last, l);
+      addMapLc(maplc2, first, l2);
+      addMapLc(maplc2, last, l2);
+      first = last;      
+    }
+    ++it;
+  }      
+  
+  std::map<MVertex*, double>::iterator itm = maplc.begin();  
+  while(itm != maplc.end()){
+    addPoint(itm->first->x(), itm->first->y(), itm->first->z());
+    lcs.push_back(itm->second);
+    ++itm;
+  }
+  itm = maplc2.begin();  
+  while(itm != maplc2.end()){
+    lcs2.push_back(itm->second);
+    ++itm;
+  }
+}
+
+
 double AttractorField_1DMesh::operator()(double X, double Y, double Z)
 {
 #ifdef HAVE_ANN_
@@ -463,3 +508,16 @@ double AttractorField_1DMesh::operator()(double X, double Y, double Z)
   Msg(GERROR,"GMSH should be compiled with ANN in order to enable attractors");
 #endif
 }
+
+void AttractorField_1DMesh::eval(double X, double Y, double Z, double &lcmin, double &lcpt, double &d)
+{
+#ifdef HAVE_ANN_
+  double xyz[3] = {X, Y, Z};
+  kdtree->annkSearch(xyz, maxpts, index, dist);
+  d = sqrt(dist[0]);
+  lcmin = lcs[index[0]];
+  lcpt  = lcs2[index[0]] * CTX.mesh.lc_factor;
+#else
+  Msg(GERROR,"GMSH should be compiled with ANN in order to enable attractors");
+#endif
+}
diff --git a/Mesh/Field.h b/Mesh/Field.h
index 9a0ce6bf35ad2f8327a2a47a4abe770d49d71991..723350ef94c11b00dd306014f12562901b734eba 100644
--- a/Mesh/Field.h
+++ b/Mesh/Field.h
@@ -139,10 +139,13 @@ class AttractorField_1DMesh : public AttractorField
 {
 protected:
   std::vector<double> lcs;
+  std::vector<double> lcs2;
   double _dmax,_dmin,_lcmax;
 public:
-  AttractorField_1DMesh (GModel *m, double dmax, double dmin, double lcmax);
+  AttractorField_1DMesh (GModel *m , double dmax, double dmin, double lcmax);
+  AttractorField_1DMesh (GFace  *gf, double dmax, double dmin, double lcmax);
   virtual double operator()(double X, double Y, double Z) ;
+  virtual void eval(double X, double Y, double Z, double &l, double &lpt, double &dist) ;
 };
 
 
diff --git a/Mesh/HighOrder.cpp b/Mesh/HighOrder.cpp
index abb93ba35f4a6a41d5aae53721292d9b4168331e..13632b7017fa9836f384dae8726056a67f79f148 100644
--- a/Mesh/HighOrder.cpp
+++ b/Mesh/HighOrder.cpp
@@ -1,4 +1,4 @@
-// $Id: HighOrder.cpp,v 1.15 2007-09-12 20:14:34 geuzaine Exp $
+// $Id: HighOrder.cpp,v 1.16 2008-01-14 21:29:14 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -20,6 +20,7 @@
 // Please report all bugs and problems to <gmsh@geuz.org>.
 
 #include "HighOrder.h"
+#include "meshGFaceOptimize.h"
 #include "MElement.h"
 #include "Message.h"
 #include "OS.h"
@@ -246,6 +247,7 @@ void getEdgeVertices(GEdge *ge, MElement *ele, std::vector<MVertex*> &ve,
 	     uc < u0 || uc > u1){ // need to treat periodic curves properly!
 	    SPoint3 pc = edge.interpolate(t);
 	    v = new MVertex(pc.x(), pc.y(), pc.z(), ge);
+	    v->setParameter (0,t);
 	  }
 	  else {
 	    GPoint pc = ge->point(uc);
@@ -379,6 +381,8 @@ void getFaceVertices(GFace *gf, MElement *ele, std::vector<MVertex*> &vf,
 	      double vc = (1. - t1 - t2) * p0[1] + t1 * p1[1] + t2 * p2[1];
 	      GPoint pc = gf->point(uc, vc);
 	      v = new MFaceVertex(pc.x(), pc.y(), pc.z(), gf, uc, vc);
+	      v->setParameter (0,uc);
+	      v->setParameter (1,vc);
 	    }
 	    faceVertices[p].push_back(v);
 	    gf->mesh_vertices.push_back(v);
@@ -698,28 +702,285 @@ bool straightLine(std::vector<MVertex*> &l, MVertex *n1, MVertex *n2)
     double by = n1->y();
     double ay = n2->y() - by;
     double y = ay * t + by;
-    if(fabs(y-v->y()) > 1.e-11 * CTX.lc){
+    if(fabs(y-v->y()) > 1.e-07 * CTX.lc){
       return false;      
     }
   }
   return true;
 }
 
+FILE *MYFILE = 0;
+
 void getMinMaxJac (MTriangle *t, double &minJ, double &maxJ)
 {
-  double j[2][2];  
-  int n = 3;
+  double mat[2][3];  
+  int n = 10;
+
+  t->jac(1,0,0,0,mat);
+  double v1[3] = {mat[0][0],mat[0][1],mat[0][2]};
+  double v2[3] = {mat[1][0],mat[1][1],mat[1][2]};
+  double normal1[3],normal[3];  
+  prodve(v1,v2,normal1);
+  norme(normal1);
+  double sign = mat[0][0] * mat[1][1] - mat[0][1] * mat[1][0];
+  double invsign = 1./(sign);
+
   for(int i = 0; i < n; i++){
     for(int k = 0; k < n - i; k++){
-      t->jac((double)i / (n - 1), (double)k / (n - 1), j);
-      double det = det2x2(j);
+      t->jac((double)i / (n - 1), (double)k / (n - 1), mat);
+      //      printf("%g %g\n",(double)i / (n - 1), (double)k / (n - 1));
+      //      SPoint3 pt;
+      //      t->pnt((double)i / (n - 1), (double)k / (n - 1),pt);
+      double v1b[3] = {mat[0][0],mat[0][1],mat[0][2]};
+      double v2b[3] = {mat[1][0],mat[1][1],mat[1][2]};
+      prodve(v1b,v2b,normal);
+      //double sign; prosca(normal1,normal,&sign);
+      //double det = norm3(normal) * (sign>0?1.:-1.);
+      double det = invsign*(mat[0][0] * mat[1][1] - mat[0][1] * mat[1][0]) * (sign>0?1.:-1.);
+      //      double det2 = 1./det1;
+      //      double det = std::max(det1,det2);
+      //      if(MYFILE)fprintf(MYFILE,"SP(%g,%g,%g){%g};\n",pt.x(),pt.y(),pt.z(),det);
       minJ = std::min(det, minJ);
       maxJ = std::max(det, maxJ);
     }
   }
 }
 
-void smoothInternalEdges(GFace *gf, edgeContainer &edgeVertices)
+struct smoothVertexDataHO{
+  MVertex *v;
+  GFace *gf;
+  std::vector < MTriangle * >ts;
+}; 
+
+struct smoothVertexDataHON{
+  std::vector<MVertex*> v;
+  GFace *gf;
+  std::vector < MTriangle * >ts;
+}; 
+
+double smoothing_objective_function_HighOrder (double U, double V, MVertex *v, std::vector < MTriangle * >&ts ,GFace *gf){
+
+  GPoint gp = gf->point(U,V);
+  const double oldX = v->x();
+  const double oldY = v->y();
+  const double oldZ = v->z();
+
+  v->x() = gp.x();
+  v->y() = gp.y();
+  v->z() = gp.z();
+
+  double minJ =  1.e22;
+  double maxJ = -1.e22;
+  for (int i=0;i<ts.size();i++){
+    getMinMaxJac (ts[i], minJ, maxJ);
+  }
+  v->x() = oldX;
+  v->y() = oldY;
+  v->z() = oldZ;
+  
+  return -minJ;
+}
+
+void deriv_smoothing_objective_function_HighOrder(double U, double V, 
+						  double &F, double &dFdU, double &dFdV,void *data){
+  smoothVertexDataHO *svd = (smoothVertexDataHO*)data;
+  MVertex *v = svd->v;
+  const double LARGE = -1.e5;
+  const double SMALL = 1./LARGE;
+  F   = smoothing_objective_function_HighOrder(U,V,v,svd->ts,svd->gf);
+  double F_U = smoothing_objective_function_HighOrder(U+SMALL,V,v,svd->ts,svd->gf);
+  double F_V = smoothing_objective_function_HighOrder(U,V+SMALL,v,svd->ts,svd->gf);
+  dFdU = (F_U-F)*LARGE;
+  dFdV = (F_V-F)*LARGE;
+}
+
+double smooth_obj_HighOrder(double U, double V, void *data){
+  smoothVertexDataHO *svd = (smoothVertexDataHO*)data;
+  return  smoothing_objective_function_HighOrder(U,V,svd->v,svd->ts,svd->gf); 
+}
+
+double smooth_obj_HighOrderN(double *uv, void *data){
+  smoothVertexDataHON *svd = (smoothVertexDataHON*)data;
+  double oldX[10],oldY[10],oldZ[10];
+  for (int i=0;i<svd->v.size();i++){
+    GPoint gp = svd->gf->point(uv[2*i],uv[2*i+1]);
+    oldX[i] = svd->v[i]->x();
+    oldY[i] = svd->v[i]->y();
+    oldZ[i] = svd->v[i]->z();
+    svd->v[i]->x() = gp.x();
+    svd->v[i]->y() = gp.y();
+    svd->v[i]->z() = gp.z();
+  }
+  double minJ =  1.e22;
+  double maxJ = -1.e22;
+  for (int i=0;i<svd->ts.size();i++){
+    getMinMaxJac (svd->ts[i], minJ, maxJ);
+  }
+  for (int i=0;i<svd->v.size();i++){
+    svd->v[i]->x() = oldX[i];
+    svd->v[i]->y() = oldY[i];
+    svd->v[i]->z() = oldZ[i];
+  }
+  return -minJ;
+}
+
+
+void deriv_smoothing_objective_function_HighOrderN(double *uv, double *dF, double &F,void *data){
+  const double LARGE = -1.e2;
+  const double SMALL = 1./LARGE;
+  smoothVertexDataHON *svd = (smoothVertexDataHON*)data;
+  F   = smooth_obj_HighOrderN(uv,data);
+  for (int i=0;i<svd->v.size();i++){
+    uv[i] += SMALL;
+    dF[i] = (smooth_obj_HighOrderN(uv,data) - F)*LARGE;
+    uv[i] -= SMALL;
+  }
+}
+
+
+
+bool optimizeHighOrderMesh(GModel *modl, GFace *gf, edgeContainer &edgeVertices){
+
+  v2t_cont adjv;
+  buildVertexToTriangle ( gf->triangles ,  adjv );
+
+  int ITER=0;
+
+  typedef std::map<std::pair<MVertex*, MVertex*>, std::vector<MElement*> > edge2tris;
+  edge2tris e2t;
+  for(unsigned int i = 0; i < gf->triangles.size(); i++){
+    MTriangle *t = gf->triangles[i];
+    for(int j = 0; j < t->getNumEdges(); j++){
+      MEdge edge = t->getEdge(j);
+      std::pair<MVertex*, MVertex*> p(edge.getMinVertex(), edge.getMaxVertex());
+      e2t[p].push_back(t);
+    }
+  }
+
+  bool success = false;
+  
+  for(edge2tris::iterator it = e2t.begin(); it != e2t.end(); ++it){
+    std::pair<MVertex*, MVertex*> edge = it->first;
+    std::vector<MVertex*> e;
+    std::vector<MElement*> triangles = it->second;
+    if(triangles.size() == 2){
+      MVertex *n2 = edge.first; 
+      MVertex *n4 = edge.second;
+      MTriangle *t1 = (MTriangle*)triangles[0];
+      MTriangle *t2 = (MTriangle*)triangles[1];
+      if(n2 < n4)
+	e = edgeVertices[std::make_pair<MVertex*, MVertex*> (n2, n4)];
+      else
+	e = edgeVertices[std::make_pair<MVertex*, MVertex*> (n4, n2)];
+
+      if (e.size() == -1){
+	double initu,initv;
+	e[0]->getParameter ( 0,initu);
+	e[0]->getParameter ( 1,initv);	  
+	smoothVertexDataHO vd;
+	vd.v = e[0];
+	vd.gf = gf;
+        vd.ts.push_back(t1);
+        vd.ts.push_back(t2);
+	double val;
+	minimize_2 ( smooth_obj_HighOrder, deriv_smoothing_objective_function_HighOrder, &vd, 1, initu,initv,val);
+	vd.v->setParameter(0,initu);
+	vd.v->setParameter(1,initv);
+	GPoint gp = gf->point(initu,initv);
+	vd.v->x() = gp.x();
+	vd.v->y() = gp.y();
+	vd.v->z() = gp.z();  				
+      }
+      else if (e.size() < 5){
+	double uv[20];
+	for (int i=0;i<e.size();i++){
+	  if (!e[i]->getParameter (0,uv[2*i]))throw;
+	  if (!e[i]->getParameter (1,uv[2*i+1]))throw;
+	}
+	smoothVertexDataHON vdN;
+	vdN.v = e;
+	vdN.gf = gf;
+	vdN.ts.clear();
+        vdN.ts.push_back(t1);
+        vdN.ts.push_back(t2);
+	double val;
+	double F = -smooth_obj_HighOrderN(uv, &vdN);
+	//	printf("F = %12.5E %p %p\n",F,t1,t2);
+	if (F < .2){
+	  minimize_N ( 2*e.size(), smooth_obj_HighOrderN, deriv_smoothing_objective_function_HighOrderN, &vdN, 1, uv,val);
+	  double Fafter = -smooth_obj_HighOrderN(uv, &vdN);
+	  if (F < Fafter){
+	    //	    printf("found a pattern with f = %22.15E -> %22.15E (%22.15E) %d points\n",F,Fafter,val,e.size());
+	    success = true;
+// 	    checkHighOrderTriangles(modl);
+// 	    double minJ=1.e22,maxJ=-1.e22;
+// 	    getMinMaxJac (t1, minJ, maxJ);
+// 	    printf("AVANT 1 minJ %22.15E maxJ %22.15E\n",minJ,maxJ);
+// 	    minJ=1.e22;maxJ=-1.e22;
+// 	    getMinMaxJac (t2, minJ, maxJ);
+// 	    printf("AVANT 2 minJ %22.15E maxJ %22.15E\n",minJ,maxJ);
+// 	    checkHighOrderTriangles(modl);
+
+	    for (int i=0;i<vdN.v.size();i++){
+	      vdN.v[i]->setParameter ( 0,uv[2*i]);
+	      vdN.v[i]->setParameter ( 1,uv[2*i+1]);
+	      GPoint gp = gf->point(uv[2*i],uv[2*i+1]);
+	      vdN.v[i]->x() = gp.x();
+	      vdN.v[i]->y() = gp.y();
+	      vdN.v[i]->z() = gp.z();
+	    }
+// 	    minJ=1.e22;maxJ=-1.e22;
+// 	    getMinMaxJac (t1, minJ, maxJ);
+// 	    printf("APRES 1 minJ %22.15E maxJ %22.15E\n",minJ,maxJ);
+// 	    minJ=1.e22;maxJ=-1.e22;
+// 	    getMinMaxJac (t2, minJ, maxJ);
+// 	    printf("APRES 2 minJ %22.15E maxJ %22.15E\n",minJ,maxJ);
+// 	    checkHighOrderTriangles(modl);
+// 	    return true;
+	  }	  
+	}
+      }
+    }
+  }
+
+  while (1){
+    v2t_cont :: iterator it = adjv.begin();      
+    while (it != adjv.end()){
+      MVertex *ver= it->first;
+      GEntity *ge = ver->onWhat();
+      if (ge->dim() == 2){
+	double initu,initv;
+	ver->getParameter ( 0,initu);
+	ver->getParameter ( 1,initv);	  
+	smoothVertexDataHO vd;
+	vd.v = ver;
+	vd.gf = gf;
+        vd.ts = it->second;
+	double val;
+
+	double F = -smooth_obj_HighOrder(initu,initv, &vd);
+	if (F < .2){
+	  minimize_2 ( smooth_obj_HighOrder, deriv_smoothing_objective_function_HighOrder, &vd, 1, initu,initv,val);
+	  double Fafter = -smooth_obj_HighOrder(initu,initv, &vd);
+	  if (F < Fafter){
+	    success = true;
+	    ver->setParameter(0,initu);
+	    ver->setParameter(1,initv);
+	    GPoint gp = gf->point(initu,initv);
+	    ver->x() = gp.x();
+	    ver->y() = gp.y();
+	    ver->z() = gp.z();  
+	  }
+	}				
+      }
+      ++it;
+    }
+    break;
+  }  
+  return success;
+}
+
+bool smoothInternalEdges(GFace *gf, edgeContainer &edgeVertices)
 {
   typedef std::map<std::pair<MVertex*, MVertex*>, std::vector<MElement*> > edge2tris;
   edge2tris e2t;
@@ -732,6 +993,10 @@ void smoothInternalEdges(GFace *gf, edgeContainer &edgeVertices)
     }
   }
 
+  bool success = false;
+
+  const int NBST = 10;
+
   for(edge2tris::iterator it = e2t.begin(); it != e2t.end(); ++it){
     std::pair<MVertex*, MVertex*> edge = it->first;
     std::vector<MVertex*> e1, e2, e3, e4, e;
@@ -766,68 +1031,123 @@ void smoothInternalEdges(GFace *gf, edgeContainer &edgeVertices)
       
       if((!straightLine(e1, n1, n2) || !straightLine(e2, n2, n3) ||
 	  !straightLine(e3, n3, n4) || !straightLine(e4, n4, n1))){
+
+
+	double Unew[NBST][10],Vnew[NBST][10];
+	double Xold[10],Yold[10],Zold[10];
+	
 	for(unsigned int i = 0; i < e.size(); i++){
-	  double v = (double)(i + 1) / (e.size() + 1);
-	  double u = 1. - v;
-	  MVertex *vert  = (n2 < n4) ? e[i] : e[e.size() - i - 1];
-	  MVertex *vert1 = (n1 < n2) ? e1[e1.size() - i - 1] : e1[i];
-	  MVertex *vert3 = (n3 < n4) ? e3[i] : e3[e3.size() - i - 1];
-	  MVertex *vert4 = (n4 < n1) ? e4[e4.size() - i - 1] : e4[i];
-	  MVertex *vert2 = (n2 < n3) ? e2[i] : e2[e2.size() - i - 1];
-	  vert->x() = vert->x() + 0.05 * ( (1.-u) * vert4->x() + u * vert2->x() +
-					   (1.-v) * vert1->x() + v * vert3->x() -
-					   ( (1.-u)*(1.-v) * n1->x() 
-					     + u * (1.-v) * n2->x() 
-					     + u*v*n3->x() 
-					     + (1.-u) * v * n4->x()) - vert->x());
-	  vert->y() = vert->y() + 0.05 * ( (1.-u) * vert4->y() + u * vert2->y() +
-					   (1.-v) * vert1->y() + v * vert3->y() -
-					   ( (1.-u)*(1.-v) * n1->y() 
-					     + u * (1.-v) * n2->y() 
-					     + u*v*n3->y() 
-					     + (1.-u) * v * n4->y()) - vert->y());
+	  Xold[i] = e[i]->x();
+	  Yold[i] = e[i]->y();
+	  Zold[i] = e[i]->z();
+	}
+
+	double minJ = 1.e22;
+	double maxJ = -1.e22;	    
+	getMinMaxJac (t1, minJ, maxJ);
+	getMinMaxJac (t2, minJ, maxJ);
+	int kopt = -1; 
+	for (int k=0;k<NBST;k++){
+	  double relax = (k+1)/(double)NBST;
+	  for(unsigned int i = 0; i < e.size(); i++){
+	    double v = (double)(i + 1) / (e.size() + 1);
+	    double u = 1. - v;
+	    MVertex *vert  = (n2 < n4) ? e[i] : e[e.size() - i - 1];
+	    MVertex *vert1 = (n1 < n2) ? e1[e1.size() - i - 1] : e1[i];
+	    MVertex *vert3 = (n3 < n4) ? e3[i] : e3[e3.size() - i - 1];
+	    MVertex *vert4 = (n4 < n1) ? e4[e4.size() - i - 1] : e4[i];
+	    MVertex *vert2 = (n2 < n3) ? e2[i] : e2[e2.size() - i - 1];
+	    double U1,V1,U2,V2,U3,V3,U4,V4,U,V,nU1,nV1,nU2,nV2,nU3,nV3,nU4,nV4;
+	    parametricCoordinates(vert , gf,U,V);
+	    parametricCoordinates(vert1, gf,U1,V1);
+	    parametricCoordinates(vert2, gf,U2,V2);
+	    parametricCoordinates(vert3, gf,U3,V3);
+	    parametricCoordinates(vert4, gf,U4,V4);
+	    parametricCoordinates(n1, gf,nU1,nV1);
+	    parametricCoordinates(n2, gf,nU2,nV2);
+	    parametricCoordinates(n3, gf,nU3,nV3);
+	    parametricCoordinates(n4, gf,nU4,nV4);
+	    
+	    Unew[k][i] = U + relax * ( (1.-u) * U4 + u * U2 +
+				(1.-v) * U1 + v * U3 -
+				( (1.-u)*(1.-v) * nU1 
+				  + u * (1.-v) * nU2 
+				  + u*v*nU3 
+				  + (1.-u) * v * nU4) - U);
+	    Vnew[k][i] = V + relax * ( (1.-u) * V4 + u * V2 +
+				    (1.-v) * V1 + v * V3 -
+				    ( (1.-u)*(1.-v) * nV1 
+				      + u * (1.-v) * nV2 
+				      + u*v*nV3 
+				      + (1.-u) * v * nV4) - V);
+	    GPoint gp = gf->point(Unew[k][i],Vnew[k][i]);
+	    vert->x() = gp.x();
+	    vert->y() = gp.y();
+	    vert->z() = gp.z();
+	  }
+	  double minJloc = 1.e22;
+	  double maxJloc = -1.e22;	    
+	  getMinMaxJac (t1, minJloc, maxJloc);
+	  getMinMaxJac (t2, minJloc, maxJloc);
+
+	  //	  printf("relax %d minJ %12.5E minjLoc %12.5E\n",k,minJ,minJloc);
+
+	  if (minJloc > minJ){
+	    kopt = k;
+	    minJ = minJloc;
+	  }
+	}
+	if (kopt == -1){
+	  for(unsigned int i = 0; i < e.size(); i++){
+	    e[i]->x() = Xold[i];
+	    e[i]->y() = Yold[i];
+	    e[i]->z() = Zold[i];
+	  }	 
+	}
+	else{
+	  success = true;
+	  for(unsigned int i = 0; i < e.size(); i++){
+	    MVertex *vert  = (n2 < n4) ? e[i] : e[e.size() - i - 1];
+	    vert->setParameter(0,Unew[kopt][i]);
+	    vert->setParameter(1,Vnew[kopt][i]);
+	    GPoint gp = gf->point(Unew[kopt][i],Vnew[kopt][i]);
+	    vert->x() = gp.x();
+	    vert->y() = gp.y();
+	    vert->z() = gp.z();
+	  }	 
 	}
       }
     }
   }    
+  return success;
 }
 
 void checkHighOrderTriangles(GModel *m)
 {
+  double minJGlob = 1.e22;
+  double maxJGlob = -1.e22;
   for(GModel::fiter it = m->firstFace(); it != m->lastFace(); ++it){
-    bool twod = true;
+    double minJ = 1.e22;
+    double maxJ = -1.e22;
     for(unsigned int i = 0; i < (*it)->triangles.size(); i++){
+      double minJloc = 1.e22;
+      double maxJloc = -1.e22;	    
       MTriangle *t = (*it)->triangles[i];
-      if(t->getVertex(0)->z() != 0.0 || 
-	 t->getVertex(1)->z() != 0.0 ||
-	 t->getVertex(2)->z() != 0.0)
-	twod = false;
-    }      
-    if(twod){ // only perform the test for 2D/plane faces for now
-      double minJ = 1.e22;
-      double maxJ = -1.e22;
-      for(unsigned int i = 0; i < (*it)->triangles.size(); i++){
-	double minJloc = 1.e22;
-	double maxJloc = -1.e22;	    
-	MTriangle *t = (*it)->triangles[i];
-	if(t->getPolynomialOrder() > 1 && t->getPolynomialOrder() < 6){
-	  getMinMaxJac (t, minJloc, maxJloc);
-	  minJ = std::min(minJ, minJloc);
-	  maxJ = std::max(maxJ, maxJloc);
-	  if(minJloc * maxJloc < 0)
-	    Msg(WARNING, "Triangle %d (%d %d %d) has negative Jacobian", t->getNum(),
-		t->getVertex(0)->getNum(), t->getVertex(1)->getNum(), t->getVertex(2)->getNum());
-	}
-      }
-      if(minJ != 1.e22){
-	if(minJ * maxJ < 0)
-	  Msg(GERROR, "Some triangles have negative Jacobians in surface %d", (*it)->tag());
-	else 
-	  Msg(INFO, "No negative Jacobians detected on model face %d: range = (%g,%g)",
-	      (*it)->tag(), minJ, maxJ);
+      if(t->getPolynomialOrder() > 1 && t->getPolynomialOrder() < 6){
+	getMinMaxJac (t, minJloc, maxJloc);
+	//	printf("%p is %12.5E\n",t,minJloc);
+	minJ = std::min(minJ, minJloc);
+	maxJ = std::max(maxJ, maxJloc);
+// 	if(minJloc * maxJloc < 0)
+// 	  Msg(WARNING, "Triangle %d (%d %d %d) has negative Jacobian (on gFace %d)", t->getNum(),
+// 	      t->getVertex(0)->getNum(), t->getVertex(1)->getNum(), t->getVertex(2)->getNum(),(*it)->tag());
       }
-    }  
+    }
+    minJGlob = std::min(minJGlob,minJ);
+    maxJGlob = std::max(maxJGlob,maxJ);
   }
+  if (minJGlob >= 0)Msg(INFO, "Jacobian Range (%12.5E,%12.5E)",minJGlob, maxJGlob);
+  else Msg(WARNING, "Jacobian Range (%12.5E,%12.5E)",minJGlob, maxJGlob);
 }  
 
 void SetOrderN(GModel *m, int order, bool linear, bool incomplete)
@@ -866,11 +1186,28 @@ void SetOrderN(GModel *m, int order, bool linear, bool incomplete)
     setHighOrder(*it, edgeVertices, faceVertices, linear, incomplete, nPts);
 
   if(CTX.mesh.smooth_internal_edges){
-    for(GModel::fiter it = m->firstFace(); it != m->lastFace(); ++it)
-      for (int i = 0; i < 10; i++) smoothInternalEdges(*it, edgeVertices);
     checkHighOrderTriangles(m);
+     for(GModel::fiter it = m->firstFace(); it != m->lastFace(); ++it){      
+       Msg(INFO, "Smoothing internal Edges in Surface %d",(*it)->tag());
+       for (int i = 0; i < 10; i++) {
+	 if (!smoothInternalEdges(*it, edgeVertices))break;
+ 	checkHighOrderTriangles(m);
+       }
+     }
+    for(GModel::fiter it = m->firstFace(); it != m->lastFace(); ++it){      
+      for (int i=0;i<CTX.mesh.nb_smoothing;i++){
+	if(!optimizeHighOrderMesh(m,*it, edgeVertices))break;
+	checkHighOrderTriangles(m);
+      }
+    }
   }
-  
+
+  //  MYFILE = fopen("jacs.pos","w");
+  //  fprintf(MYFILE,"View \"\"{\n");
+  checkHighOrderTriangles(m);
+  //  fprintf(MYFILE,"};\n");
+  //  fclose(MYFILE);
+  MYFILE=0;
   double t2 = Cpu();
   Msg(INFO, "Mesh second order complete (%g s)", t2 - t1);
   Msg(STATUS1, "Mesh");
diff --git a/Mesh/meshGEdge.cpp b/Mesh/meshGEdge.cpp
index 3052065b43feb539e7fe1fabe1b196fdab016752..87c8e17b78016e5bd9b2295ce7a670405c7ce7e5 100644
--- a/Mesh/meshGEdge.cpp
+++ b/Mesh/meshGEdge.cpp
@@ -1,4 +1,4 @@
-// $Id: meshGEdge.cpp,v 1.46 2007-11-11 19:53:57 remacle Exp $
+// $Id: meshGEdge.cpp,v 1.47 2008-01-14 21:29:14 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -288,8 +288,6 @@ void meshGEdge::operator() (GEdge *ge)
   if(ge->geomType() == GEntity::DiscreteCurve) return;
   if(ge->geomType() == GEntity::BoundaryLayerCurve) return;
 
-  // Send a messsage to the GMSH environment
-  Msg(INFO, "Meshing curve %d", ge->tag());
 
   deMeshGEdge dem;
   dem(ge);
@@ -309,6 +307,10 @@ void meshGEdge::operator() (GEdge *ge)
   // first compute the length of the curve by integrating one
   double length = Integration(ge, t_begin, t_end, F_One, Points, 1.e-8);
   ge->setLength(length);
+  // Send a messsage to the GMSH environment
+
+  if (length == 0.0)
+    Msg(GERROR,"Curve %d has a zero length\n",ge->tag());
 
   List_Reset(Points);
     
@@ -337,6 +339,8 @@ void meshGEdge::operator() (GEdge *ge)
     N = std::max(ge->minimumMeshSegments() + 1, (int)(a + 1.));
   }
 
+  Msg(INFO, "Meshing curve %d (%s)", ge->tag(),ge->getTypeString().c_str());
+
   // if the curve is periodic and if the begin vertex is identical to
   // the end vertex and if this vertex has only one model curve
   // adjacent to it, then the vertex is not connecting any other
diff --git a/Mesh/meshGFace.cpp b/Mesh/meshGFace.cpp
index d4e5cf8014973df9e4f02ea4a20ba08e37fc3a55..9358f946223df3c789e776d9745b3a886f92aa79 100644
--- a/Mesh/meshGFace.cpp
+++ b/Mesh/meshGFace.cpp
@@ -1,4 +1,4 @@
-// $Id: meshGFace.cpp,v 1.103 2008-01-09 15:25:48 geuzaine Exp $
+// $Id: meshGFace.cpp,v 1.104 2008-01-14 21:29:14 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -35,11 +35,27 @@
 #include "Numeric.h"
 #include "BDS.h"
 #include "qualityMeasures.h"
+#include "Field.h"
 
 extern Context_T CTX;
 
 static double SCALINGU=1,SCALINGV=1;
 
+bool noseam (  GFace *gf  )
+{
+  std::list<GEdge*> edges = gf->edges();
+  std::list<GEdge*>::iterator it = edges.begin();
+  while (it != edges.end())   
+   {
+     GEdge *ge = *it ;
+     bool seam = ge->isSeam(gf);
+     if (seam) return false;
+     ++it;
+   }
+  return true;
+}
+
+
 void remeshUnrecoveredEdges ( std::set<EdgeToRecover> & edgesNotRecovered, std::list<GFace *> &facesToRemesh)
 {
   facesToRemesh.clear();
@@ -133,8 +149,8 @@ void remeshUnrecoveredEdges ( std::set<EdgeToRecover> & edgesNotRecovered, std::
 
 
 bool AlgoDelaunay2D ( GFace *gf )
-{
-  if ( /*gf->getNativeType() == GEntity::GmshModel &&*/ CTX.mesh.algo2d == ALGO_2D_DELAUNAY && gf->geomType() == GEntity::Plane)
+{ 
+  if ( noseam(gf) && /*gf->getNativeType() == GEntity::GmshModel &&*/ CTX.mesh.algo2d == ALGO_2D_DELAUNAY /*&& gf->geomType() == GEntity::Plane*/)
     return true;
   return false;
 }
@@ -359,8 +375,19 @@ void OptimizeMesh(GFace *gf, BDS_Mesh &m, const int NIT)
 	}
       }
     }
-  for (int KK=0;KK<4;KK++){
-    // swap edges that provide a better configuration
+
+//   for(int i = 0 ; i < NIT ; i++){
+//     std::set<BDS_Point*,PointLessThan>::iterator itp = m.points.begin();
+//     while (itp != m.points.end())
+//       {
+// 	optimize_vertex_position(gf,*itp,m.scalingU,m.scalingV);		
+// 	++itp;
+//       }
+//   }
+
+
+ for (int KK=0;KK<4;KK++){
+   // swap edges that provide a better configuration
     int NN1 = m.edges.size();
     int NN2 = 0;
     std::list<BDS_Edge*>::iterator it = m.edges.begin();
@@ -398,7 +425,279 @@ void swapEdgePass ( GFace *gf, BDS_Mesh &m, int &nb_swap )
     }  
 }
 
-void splitEdgePass ( GFace *gf, BDS_Mesh &m, double MAXE_, int &nb_split)
+int getTemplate (const BDS_Face *f, const std::map<BDS_Edge*,std::pair<BDS_Edge*,BDS_Edge*> >&splits,
+		 BDS_Edge *edges[3][2]){
+  int k = 0;
+  std::map< BDS_Edge*,std::pair<BDS_Edge*,BDS_Edge* > > ::const_iterator it;
+  it = splits.find ( f-> e1 );
+  if (it == splits.end()){
+    edges[0][0] = f->e1;
+    edges[0][1] = 0;
+  }
+  else{
+    edges[0][0] = it->second.first;
+    edges[0][1] = it->second.second;
+    k+=1;
+  }
+  it = splits.find ( f->e2 );
+  if (it == splits.end()){
+    edges[1][0] = f->e2;
+    edges[1][1] = 0;
+  }
+  else{
+    edges[1][0] = it->second.first;
+    edges[1][1] = it->second.second;
+    k+=10;
+  }
+  it = splits.find ( f->e3 );
+  if (it == splits.end()){
+    edges[2][0] = f->e3;
+    edges[2][1] = 0;
+  }
+  else{
+    edges[2][0] = it->second.first;
+    edges[2][1] = it->second.second;
+    k+=100;
+  }
+  return k;		     
+}
+
+void Template_1 ( BDS_Mesh &m , BDS_Face *f, BDS_Edge *e11, BDS_Edge *e12, BDS_Edge *e2, BDS_Edge *e3)
+{
+  BDS_Point *mid = e11->commonvertex(e12);
+  BDS_Point *opposite = e2->commonvertex(e3);
+
+  if (!mid || !opposite){
+    printf("strange bazar in template 1 : edges %d %d , %d %d , %d %d and %d %d\n",
+	   e11->p1->iD,e11->p2->iD,
+	   e12->p1->iD,e12->p2->iD,
+	   e2->p1->iD,e2->p2->iD,
+	   e3->p1->iD,e3->p2->iD);
+  }
+
+  BDS_Edge *emid = new BDS_Edge (mid,opposite);
+
+  if (!e11->commonvertex(e3)){
+    BDS_Edge *temp = e3;
+    e3 = e2; 
+    e2 = temp;
+  }
+
+  BDS_Face* t1 = new BDS_Face(e11,emid,e3);
+  BDS_Face* t2 = new BDS_Face(e12,e2,emid);
+  t1->g = f->g;
+  t2->g = t1->g;
+  emid->g = t1->g;  
+  m.triangles.push_back(t1);
+  m.triangles.push_back(t2);
+  m.edges.push_back(emid);
+  m.del_face(f);
+  e11->p1->config_modified = e11->p2->config_modified = true;
+  e12->p1->config_modified = e12->p2->config_modified = true;
+  e2->p1->config_modified = e2->p2->config_modified = true;
+  e3->p1->config_modified = e3->p2->config_modified = true;
+}
+
+void Template_2 ( BDS_Mesh &m , BDS_Face *f, BDS_Edge *e11, BDS_Edge *e12, BDS_Edge *e21, BDS_Edge *e22, BDS_Edge *e3)
+{
+  BDS_Point *mid1 = e11->commonvertex(e12);
+  BDS_Point *mid2 = e21->commonvertex(e22);
+
+  BDS_Edge *emid1 = new BDS_Edge (mid1,mid2);
+
+  if (!e11->commonvertex(e3)){
+    BDS_Edge *temp = e11;
+    e11 = e12; 
+    e12 = temp;
+  }
+  if (!e22->commonvertex(e3)){
+    BDS_Edge *temp = e22;
+    e22 = e21; 
+    e21 = temp;
+  }
+  BDS_Point *opposite1 = e3->commonvertex(e22);
+  BDS_Point *opposite2 = e3->commonvertex(e11);
+
+  //  if (!e12->commonvertex(e21))throw;
+
+  // build the best possible template to avoid subsequent swap
+  // first config, use and edge mid1->opposite1
+  double config1Q = std::min(qmTriangle(opposite2,mid1,opposite1,QMTRI_RHO),qmTriangle(mid1,mid2,opposite1,QMTRI_RHO)); 
+  // second config, use and edge mid2->opposite2
+  double config2Q = std::min(qmTriangle(opposite2,mid2,opposite1,QMTRI_RHO),qmTriangle(mid1,mid2,opposite2,QMTRI_RHO)); 
+  
+  // if the first one is the best
+  BDS_Face *t1,*t2,*t3;
+  BDS_Edge *emid2;
+  t1 = new BDS_Face(e12,e21,emid1);
+  if (config1Q > config2Q) {
+    emid2 = new BDS_Edge (mid1,opposite1);
+    t2 = new BDS_Face(e11,emid2,e3);
+    t3 = new BDS_Face(emid2,emid1,e22);
+  }
+  else{
+    emid2 = new BDS_Edge (mid2,opposite2);
+    t2 = new BDS_Face(e11,emid1,emid2);
+    t3 = new BDS_Face(emid2,e22,e3);
+  }
+  t1->g = f->g;
+  t2->g = t1->g;
+  t3->g = t1->g;
+  emid1->g = t1->g;  
+  emid2->g = t1->g;  
+  m.triangles.push_back(t1);
+  m.triangles.push_back(t2);
+  m.triangles.push_back(t3);
+  m.edges.push_back(emid1);
+  m.edges.push_back(emid2);
+  m.del_face(f);
+  e11->p1->config_modified = e11->p2->config_modified = true;
+  e12->p1->config_modified = e12->p2->config_modified = true;
+  e21->p1->config_modified = e21->p2->config_modified = true;
+  e22->p1->config_modified = e22->p2->config_modified = true;
+  e3->p1->config_modified = e3->p2->config_modified = true;
+}
+void Template_3 ( BDS_Mesh &m , BDS_Face *f, BDS_Edge *e11, BDS_Edge *e12, BDS_Edge *e21, BDS_Edge *e22, BDS_Edge *e31,BDS_Edge *e32)
+{
+  BDS_Point *mid1 = e11->commonvertex(e12);
+  BDS_Point *mid2 = e21->commonvertex(e22);
+  BDS_Point *mid3 = e31->commonvertex(e32);
+
+  BDS_Edge *emid12 = new BDS_Edge (mid1,mid2);
+  BDS_Edge *emid13 = new BDS_Edge (mid1,mid3);
+  BDS_Edge *emid23 = new BDS_Edge (mid2,mid3);
+
+  if (!e11->commonvertex(e31) && !e11->commonvertex(e32)){
+    BDS_Edge *temp = e11;
+    e11 = e12; 
+    e12 = temp;
+  }
+  if (!e11->commonvertex(e31)){
+    BDS_Edge *temp = e31;
+    e31 = e32; 
+    e32 = temp;
+  }
+  if (!e32->commonvertex(e22)){
+    BDS_Edge *temp = e21;
+    e21 = e22; 
+    e22 = temp;
+  }
+
+//   if (!e11->commonvertex(e31))throw;
+//   if (!e32->commonvertex(e22))throw;
+//   if (!e12->commonvertex(e21))throw;
+
+  BDS_Face *t1 = new BDS_Face(e11,emid13,e31);
+  BDS_Face *t2 = new BDS_Face(e12,e21,emid12);
+  BDS_Face *t3 = new BDS_Face(emid23,e22,e32);
+  BDS_Face *t4 = new BDS_Face(emid12,emid23,emid13);
+
+  t1->g = f->g;
+  t2->g = t1->g;
+  t3->g = t1->g;
+  t4->g = t1->g;
+  emid12->g = t1->g;  
+  emid13->g = t1->g;  
+  emid23->g = t1->g;  
+  m.triangles.push_back(t1);
+  m.triangles.push_back(t2);
+  m.triangles.push_back(t3);
+  m.triangles.push_back(t4);
+  m.edges.push_back(emid12);
+  m.edges.push_back(emid13);
+  m.edges.push_back(emid23);
+  m.del_face(f);
+  e11->p1->config_modified = e11->p2->config_modified = true;
+  e12->p1->config_modified = e12->p2->config_modified = true;
+  e21->p1->config_modified = e21->p2->config_modified = true;
+  e22->p1->config_modified = e22->p2->config_modified = true;
+  e31->p1->config_modified = e31->p2->config_modified = true;
+  e32->p1->config_modified = e32->p2->config_modified = true;
+}
+
+
+void splitEdgePass_templateRefine ( GFace *gf, BDS_Mesh &m, double MAXE_, int &nb_split)
+{
+  std::map<BDS_Edge*,std::pair<BDS_Edge*,BDS_Edge*> >splits;
+  // build a list of edges to split with their new vertices
+  int NN1 = m.edges.size();
+  int NN2 = 0;
+  std::list<BDS_Edge*>::iterator it = m.edges.begin();
+  while (1)
+    {
+      if (NN2++ >= NN1)break;
+      if (!(*it)->deleted && (*it)->numfaces() == 2)
+	{
+	  double lone = NewGetLc ( *it,gf);
+	  if (lone >  MAXE_){	    
+	    const double coord = 0.5;
+	    BDS_Point *mid ;
+	    mid  = m.add_point(++m.MAXPOINTNUMBER,
+			       coord * (*it)->p1->u + (1 - coord) * (*it)->p2->u,
+			       coord * (*it)->p1->v + (1 - coord) * (*it)->p2->v,gf);
+	    
+	    mid->lcBGM() = BGM_MeshSize(gf,
+					(coord * (*it)->p1->u + (1 - coord) * (*it)->p2->u)*m.scalingU,
+					(coord * (*it)->p1->v + (1 - coord) * (*it)->p2->v)*m.scalingV,
+					mid->X,mid->Y,mid->Z);
+	    mid->lc() = 0.5 * ( (*it)->p1->lc() +  (*it)->p2->lc() );
+	    BDS_Edge *e1 = new BDS_Edge ((*it)->p1,mid);
+	    BDS_Edge *e2 = new BDS_Edge (mid, (*it)->p2);
+	    e1->g = e2->g = (*it)->g;
+	    m.del_edge((*it));
+	    m.edges.push_back(e1);
+	    m.edges.push_back(e2);
+
+
+	    splits[*it] = std::make_pair<BDS_Edge*, BDS_Edge*> (e1,e2);
+	    nb_split++;
+	  }		  
+	}
+      ++it;
+    }
+
+  std::list<BDS_Face*>::iterator itt = m.triangles.begin();
+  // build a list of edges to split with their new vertices
+  while (itt != m.triangles.end())
+    {
+      if (!(*itt)->deleted)
+	{
+	  BDS_Edge *edges[3][2];
+	  int K = getTemplate ((*itt),splits,edges);
+	  switch(K){
+	  case   0: // no edge is split 
+	    break;
+	  case   1: // first edge is split 
+	    Template_1 ( m , *itt, edges[0][0],edges[0][1],edges[1][0],edges[2][0]);
+	    break;
+	  case  10: // second edge is split 
+	    Template_1 ( m , *itt, edges[1][0],edges[1][1],edges[2][0],edges[0][0]);
+	    break;
+	  case 100: // third edge is split 
+	    Template_1 ( m , *itt, edges[2][0],edges[2][1],edges[0][0],edges[1][0]);
+	    break;
+	  case  11: // fisrt and second
+	    Template_2 ( m , *itt, edges[0][0],edges[0][1],edges[1][0],edges[1][1],edges[2][0]);
+	    break;
+	  case 101: // fisrt and third
+	    Template_2 ( m , *itt, edges[2][0],edges[2][1],edges[0][0],edges[0][1],edges[1][0]);
+	    break;
+	  case 110: // second and third
+	    Template_2 ( m , *itt, edges[1][0],edges[1][1],edges[2][0],edges[2][1],edges[0][0]);
+	    break;
+	  case 111: // all splitted
+	    Template_3 ( m , *itt, edges[0][0],edges[0][1],edges[1][0],edges[1][1],edges[2][0],edges[2][1]);
+	    break;
+	  default :
+	    printf("strange template %d\n",K);
+	    throw;
+	  }
+	}
+      ++itt;
+    }
+}
+
+void splitEdgePass ( GFace *gf, BDS_Mesh &m, double MAXE_, int &nb_split, AttractorField_1DMesh *attr)
 {
   int NN1 = m.edges.size();
   int NN2 = 0;
@@ -428,7 +727,21 @@ void splitEdgePass ( GFace *gf, BDS_Mesh &m, double MAXE_, int &nb_split)
 					  (coord * (*it)->p1->v + (1 - coord) * (*it)->p2->v)*m.scalingV,
 					  mid->X,mid->Y,mid->Z);
 	      //mid->lc() = 2./ ( 1./(*it)->p1->lc() +  1./(*it)->p2->lc() );		  
-	      mid->lc() = 0.5 * ( (*it)->p1->lc() +  (*it)->p2->lc() );		  
+	      if (!attr)
+		mid->lc() = 0.5 * ( (*it)->p1->lc() +  (*it)->p2->lc() );		  
+	      else{
+		double lcmin,lcpt, dist;
+		double FACT1 = 3, FACT2=25;
+		attr->eval(mid->X,mid->Y,mid->Z,lcmin,lcpt, dist);
+
+		if (dist < FACT1* lcmin) mid->lc() = lcmin;
+ 		else if (dist < FACT2 * lcmin) {
+ 		  double r = (dist - FACT1*lcmin) / ((FACT2-FACT1)*lcmin);
+ 		  mid->lc() = 1./(1./lcmin * r + 1./lcpt*(1-r));
+ 		}
+		else mid->lc() = lcpt;
+	      }
+		
 	      //	      printf("%g %g\n",mid->lc(),mid->lcBGM());
 
 	      if(!m.split_edge ( *it, mid )) m.del_point(mid);
@@ -598,51 +911,45 @@ void smoothVertexPass ( GFace *gf, BDS_Mesh &m, int &nb_smooth)
 void RefineMesh ( GFace *gf, BDS_Mesh &m , const int NIT)
 {
   int IT =0;
+  AttractorField_1DMesh *attr = 0;
+//   if (CTX.mesh.lc_from_curvature){
+//     // parameters are not important
+//     attr = new AttractorField_1DMesh(gf,0.1,0,1);
+//     attr->buildFastSearchStructures();
+//   }
 
   //  printf("lc (1,1) = %g\n",Attractor::lc(1,1,0));
 
   int MAXNP = m.MAXPOINTNUMBER;
 
-  //  if (NIT > 0)
+  if (NIT > 0)
     {
       std::set<BDS_Point*,PointLessThan>::iterator itp = m.points.begin();
       while (itp != m.points.end())
 	{
-	  if (NIT > 0)
-	    {
-	      std::list<BDS_Edge*>::iterator it  = (*itp)->edges.begin();
-	      std::list<BDS_Edge*>::iterator ite = (*itp)->edges.end();
-	      double L=0;
-	      int ne = 0;
-	      while(it!=ite){
-		double l = (*it)->length();
-		if ((*it)->g && (*it)->g->classif_degree == 1){	      
-		  L=ne?std::max(L,l):l;
-		  //	      L=ne?std::min(L,l):l;
-		  //	      L+=l;
-		  ne++;
-		}
-		++it;
-	      }
-	      if (!ne) L = 1.e22;
-	      //	  else L/=ne;
-	      if(!CTX.mesh.constrained_bgmesh)
-		(*itp)->lc() = L;
-	      (*itp)->lcBGM() = L;
+	  std::list<BDS_Edge*>::iterator it  = (*itp)->edges.begin();
+	  std::list<BDS_Edge*>::iterator ite = (*itp)->edges.end();
+	  double L=0;
+	  int ne = 0;
+	  while(it!=ite){
+	    double l = (*it)->length();
+	    if ((*it)->g && (*it)->g->classif_degree == 1){	      
+	      L=ne?std::max(L,l):l;
+	      //	      L=ne?std::min(L,l):l;
+	      //	      L+=l;
+	      ne++;
 	    }
-//  	  else if (CTX.mesh.lc_from_curvature)
-//  	    {
-//  	      double Crv = gf->curvature(SPoint2((*itp)->u, (*itp)->v));
-//  	      double lc = Crv > 0 ? 2*M_PI / Crv / CTX.mesh.min_circ_points : 1.e22;
-//  	      lc *= CTX.mesh.lc_factor;
-//  	      printf("%d = %d %12.5e %12.5E %12.5E\n",gf->tag(),CTX.mesh.min_circ_points,Crv,lc,(*itp)->lcBGM());
-//  	      (*itp)->lc() = std::min(lc,(*itp)->lc());	      
-//  	    }
+	    ++it;
+	  }
+	  if (!ne) L = 1.e22;
+	  //	  else L/=ne;
+	  if(!CTX.mesh.constrained_bgmesh)
+	    (*itp)->lc() = L;
+	  (*itp)->lcBGM() = L;
 	  ++itp;
 	}
     }
 
-
   double OLDminL=1.E22,OLDmaxL=0;
 
   double t_spl=0, t_sw=0,t_col=0,t_sm=0;
@@ -684,11 +991,12 @@ void RefineMesh ( GFace *gf, BDS_Mesh &m , const int NIT)
       OLDminL = minL;OLDmaxL = maxL;
       
       if ((minL > MINE_ && maxL < MAXE_) || IT > (abs(NIT)))break;
-
-      double maxE = MAXE_;//std::max(MAXE_,maxL / 5);
-      double minE = MINE_;//std::min(MINE_,minL * 1.2);
+      double maxE = MAXE_;
+      double minE = MINE_;
       clock_t t1 = clock();
-      splitEdgePass ( gf, m, maxE, nb_split);
+      //      splitEdgePass_templateRefine ( gf, m, maxE, nb_split);
+      splitEdgePass ( gf, m, maxE, nb_split,attr);
+      //splitEdgePass_templateRefine ( gf, m, maxE, nb_split);
       //saturateEdgePass ( gf, m, maxE, nb_split);
       clock_t t2 = clock();
       swapEdgePass ( gf, m, nb_swap);
@@ -721,6 +1029,8 @@ void RefineMesh ( GFace *gf, BDS_Mesh &m , const int NIT)
       
       if (nb_split==0 && nb_collaps == 0)break;
       //      if (mesh_quality < old_mesh_quality && worst > 0.1 && maxL < 1.45) break;
+      //      return;
+
     }  
 
   double t_total = t_spl + t_sw + t_col + t_sm;
@@ -734,6 +1044,8 @@ void RefineMesh ( GFace *gf, BDS_Mesh &m , const int NIT)
   Msg(DEBUG1," ---------------------------------------");
   Msg(DEBUG1," CPU TOTAL   %12.5E sec ",t_total);
   Msg(DEBUG1," ---------------------------------------");
+
+  if (attr)delete attr;
 }
 
 
@@ -1196,9 +1508,6 @@ bool gmsh2DMeshGenerator ( GFace *gf , int RECUR_ITER, bool debug = true)
   }
   m->cleanup();
 
-
-
-
   {
     std::list<BDS_Edge*>::iterator ite = m->edges.begin();
     while (ite != m->edges.end())
@@ -1225,7 +1534,6 @@ bool gmsh2DMeshGenerator ( GFace *gf , int RECUR_ITER, bool debug = true)
   m->del_point(m->find_point(-3));
   m->del_point(m->find_point(-4));
 
-
   if (debug)
     {
       char name[245];
@@ -1238,10 +1546,11 @@ bool gmsh2DMeshGenerator ( GFace *gf , int RECUR_ITER, bool debug = true)
   // start mesh generation
   if (!AlgoDelaunay2D ( gf ))
     {
+      //      RefineMesh (gf,*m, 1);
       RefineMesh (gf,*m, CTX.mesh.refine_steps);
       OptimizeMesh(gf, *m, 2);
       RefineMesh (gf,*m, -CTX.mesh.refine_steps);
-      OptimizeMesh(gf, *m, 2);
+      OptimizeMesh(gf, *m, -2);
 
       if (gf->meshAttributes.recombine)
 	{
@@ -1341,20 +1650,6 @@ inline double dist2 (const SPoint2 &p1,const SPoint2 &p2)
 
 
 
-bool noseam (  GFace *gf  )
-{
-  std::list<GEdge*> edges = gf->edges();
-  std::list<GEdge*>::iterator it = edges.begin();
-  while (it != edges.end())   
-   {
-     GEdge *ge = *it ;
-     bool seam = ge->isSeam(gf);
-     if (seam) return false;
-     ++it;
-   }
-  return true;
-}
-
 bool buildConsecutiveListOfVertices (  GFace *gf,
 				       GEdgeLoop  &gel , 
 				       std::vector<BDS_Point*> &result,
@@ -1698,16 +1993,14 @@ bool gmsh2DMeshGeneratorPeriodic ( GFace *gf , bool debug = true)
     
   BDS_GeomEntity CLASS_F (1,2);
   BDS_GeomEntity CLASS_E (1,1);
-  
 
-  if (debug)
-    {
-      char name[245];
-      sprintf(name,"surface%d-initial-real.pos",gf->tag());
-      outputScalarField(m->triangles, name,0);
-      sprintf(name,"surface%d-initial-param.pos",gf->tag());
-      outputScalarField(m->triangles, name,1);
-    }
+  if (debug){
+    char name[245];
+    sprintf(name,"surface%d-initial-real.pos",gf->tag());
+    outputScalarField(m->triangles, name,0);
+    sprintf(name,"surface%d-initial-param.pos",gf->tag());
+    outputScalarField(m->triangles, name,1);
+  }
 
 
   // build a list of edges to recover
@@ -1722,22 +2015,19 @@ bool gmsh2DMeshGeneratorPeriodic ( GFace *gf , bool debug = true)
 //     }
 
 
-  for (unsigned int i=0;i<edgeLoops_BDS.size();i++)
-    {
+  for (unsigned int i=0;i<edgeLoops_BDS.size();i++){
       std::vector<BDS_Point*> &edgeLoop_BDS = edgeLoops_BDS[i];
-      for (unsigned int j=0;j<edgeLoop_BDS.size();j++)
-	{
-	  BDS_Edge * e = m->recover_edge ( edgeLoop_BDS[j]->iD,edgeLoop_BDS[(j+1)%edgeLoop_BDS.size()]->iD);	  
-	  if (!e)
-	    {
-	      Msg(GERROR,"impossible to recover the edge %d %d",edgeLoop_BDS[j]->iD,edgeLoop_BDS[(j+1)%edgeLoop_BDS.size()]->iD);
-	      gf->meshStatistics.status = GFace::FAILED;
-	      SCALINGU = SCALINGV = 1;
-	      return false;
-	    }
-	  else e->g = &CLASS_E;
+      for (unsigned int j=0;j<edgeLoop_BDS.size();j++){
+	BDS_Edge * e = m->recover_edge ( edgeLoop_BDS[j]->iD,edgeLoop_BDS[(j+1)%edgeLoop_BDS.size()]->iD);	  
+	if (!e){
+	  Msg(GERROR,"impossible to recover the edge %d %d",edgeLoop_BDS[j]->iD,edgeLoop_BDS[(j+1)%edgeLoop_BDS.size()]->iD);
+	  gf->meshStatistics.status = GFace::FAILED;
+	  SCALINGU = SCALINGV = 1;
+	  return false;
 	}
-    }	  
+	else e->g = &CLASS_E;
+      }
+  }	  
   
   //  Msg(INFO,"Boundary Edges recovered for surface %d",gf->tag());
   // Look for an edge that is on the boundary for which one of the
@@ -1747,40 +2037,34 @@ bool gmsh2DMeshGeneratorPeriodic ( GFace *gf , bool debug = true)
   // recursive algorithm
   {
     std::list<BDS_Edge*>::iterator ite = m->edges.begin();
-    while (ite != m->edges.end())
-      {
-	BDS_Edge *e = *ite;
-	if ( e->g  && e->numfaces () == 2)
-	  {
-	    BDS_Point *oface[2];
-	    e->oppositeof(oface);
-	    if (oface[0]->iD < 0) 
-	      {
-		recur_tag ( e->faces(1) , &CLASS_F); 
-		break;
-	      }
-	    else if (oface[1]->iD < 0) 
-	      {
-		recur_tag ( e->faces(0) , &CLASS_F); 
-		break;
-	      }
-	    }
-	++ite;
+    while (ite != m->edges.end()){
+      BDS_Edge *e = *ite;
+      if ( e->g  && e->numfaces () == 2){
+	BDS_Point *oface[2];
+	e->oppositeof(oface);
+	if (oface[0]->iD < 0){
+	  recur_tag ( e->faces(1) , &CLASS_F); 
+	  break;
+	}
+	else if (oface[1]->iD < 0){
+	  recur_tag ( e->faces(0) , &CLASS_F); 
+	  break;
+	}
       }
+      ++ite;
+    }
   }
 
   // delete useless stuff
   {
     std::list<BDS_Face*>::iterator itt = m->triangles.begin();
-    while (itt != m->triangles.end())
-      {
-	BDS_Face *t = *itt;
-	if (!t->g)
-	  {
-	    m->del_face (t);
-	  }
-	++itt;
+    while (itt != m->triangles.end()){
+      BDS_Face *t = *itt;
+      if (!t->g){
+	m->del_face (t);
       }
+      ++itt;
+    }
   }
 
   m->cleanup();
@@ -1826,7 +2110,7 @@ bool gmsh2DMeshGeneratorPeriodic ( GFace *gf , bool debug = true)
       RefineMesh (gf,*m,CTX.mesh.refine_steps);
       OptimizeMesh(gf, *m, 2);
       RefineMesh (gf,*m,-CTX.mesh.refine_steps);
-      OptimizeMesh(gf, *m, 2);
+      OptimizeMesh(gf, *m, -2);
 
       if (gf->meshAttributes.recombine)
 	{
@@ -1965,10 +2249,11 @@ void meshGFace::operator() (GFace *gf)
   computeEdgeLoops(gf, points, indices);
 
   // temp fix until we create MEdgeLoops in gmshFace
-  //  if (gf->tag() == 2)
+  if (1 || gf->tag() == 6)
     {
       Msg(DEBUG1, "Generating the mesh");
       if(noseam (gf) || gf->getNativeType() == GEntity::GmshModel || gf->edgeLoops.empty()){
+	//	gmsh2DMeshGenerator(gf,0, true);
 	gmsh2DMeshGenerator(gf,0, false);
       }
       else{
@@ -1976,9 +2261,9 @@ void meshGFace::operator() (GFace *gf)
 	  Msg(GERROR, "Impossible to mesh face %d", gf->tag());
       }
     }
-//   else
-//     gf->meshStatistics.status = GFace::DONE;
-
+  else
+    gf->meshStatistics.status = GFace::DONE;
+  
   Msg(DEBUG1, "type %d %d triangles generated, %d internal vertices",
       gf->geomType(), gf->triangles.size(), gf->mesh_vertices.size());
 }  
diff --git a/Mesh/meshGFaceDelaunayInsertion.cpp b/Mesh/meshGFaceDelaunayInsertion.cpp
index 99b1cadd6c689dd4b702a7e947a20b775773a637..42cd02777dd5b7b9ab4e752f57b057608179591c 100644
--- a/Mesh/meshGFaceDelaunayInsertion.cpp
+++ b/Mesh/meshGFaceDelaunayInsertion.cpp
@@ -1,4 +1,4 @@
-// $Id: meshGFaceDelaunayInsertion.cpp,v 1.6 2007-10-10 08:49:34 remacle Exp $
+// $Id: meshGFaceDelaunayInsertion.cpp,v 1.7 2008-01-14 21:29:14 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -22,6 +22,7 @@
 #include "BDS.h"
 #include "BackgroundMesh.h"
 #include "meshGFaceDelaunayInsertion.h"
+#include "meshGFaceOptimize.h"
 #include "meshGFace.h"
 #include "GFace.h"
 #include "Numeric.h"
@@ -32,41 +33,100 @@
 
 int III = 1;
 
-// computes the center of the circum circle in the tangent plane
-// the metric given by a b d is supposed to be constant
-void MTri3::Center_Circum_Aniso(double a, double b, double d, double &x, double &y, double &r) const
+bool circumCenterMetricInTriangle ( MTriangle *base, 
+				    const double *metric,
+				    const std::vector<double> & Us,
+				    const std::vector<double> & Vs)
 {
-  throw;
+  double R,x[2],uv[2];
+  circumCenterMetric(base,metric,Us,Vs,x,R);
+  return invMapUV(base,x,Us,Vs,uv,1.e-8);
 }
 
-
-// find a common basis for 2 metrics in 2D 
-void simultaneousMetricReduction( const gmsh2dMetric &M1,  const gmsh2dMetric &M2,
-				  double & l1,double & l2, double V[2][2]) 
+void circumCenterMetric ( MTriangle *base, 
+			  const double *metric,
+			  const std::vector<double> & Us,
+			  const std::vector<double> & Vs,
+			  double *x, double &Radius2) 
 {
-  throw;
-}
+  // d = (u2-u1) M (u2-u1) = u2 M u2 + u1 M u1 - 2 u2 M u1 
+  double sys[2][2];
+  double rhs[2];
 
-gmsh2dMetric metricIntersection(const gmsh2dMetric &Ma,const gmsh2dMetric &Mb) 
-{
-  throw;
+  double pa[2] = {Us[base->getVertex(0)->getNum()],
+		  Vs[base->getVertex(0)->getNum()]};
+  double pb[2] = {Us[base->getVertex(1)->getNum()],
+		  Vs[base->getVertex(1)->getNum()]};
+  double pc[2] = {Us[base->getVertex(2)->getNum()],
+		  Vs[base->getVertex(2)->getNum()]};
+
+  const double a = metric[0];
+  const double b = metric[1];
+  const double d = metric[2];
+
+  sys[0][0] = 2. * a * (pa[0] - pb[0]) + 2. * b * (pa[1] - pb[1]);
+  sys[0][1] = 2. * d * (pa[1] - pb[1]) + 2. * b * (pa[0] - pb[0]);
+  sys[1][0] = 2. * a * (pa[0] - pc[0]) + 2. * b * (pa[1] - pc[1]);
+  sys[1][1] = 2. * d * (pa[1] - pc[1]) + 2. * b * (pa[0] - pc[0]);
+
+  rhs[0] =
+    a * (pa[0] * pa[0] - pb[0] * pb[0]) + 
+    d * (pa[1] * pa[1] - pb[1] * pb[1]) + 
+    2. * b * (pa[0] * pa[1] - pb[0] * pb[1]);
+  rhs[1] =
+    a * (pa[0] * pa[0] - pc[0] * pc[0]) + 
+    d * (pa[1] * pa[1] - pc[1] * pc[1]) + 
+    2. * b * (pa[0] * pa[1] - pc[0] * pc[1]);
+
+  sys2x2(sys, rhs, x);
+
+  Radius2 = 
+    (x[0] - pa[0]) * (x[0] - pa[0]) * a +
+    (x[1] - pa[1]) * (x[1] - pa[1]) * d +
+    2. * (x[0] - pa[0]) * (x[1] - pa[1]) * b;
 }
 
-gmsh2dMetric metricInterpolationTriangle(const gmsh2dMetric &M1,
-					 const gmsh2dMetric &M2,
-					 const gmsh2dMetric &M3, 
-					 const double u, 
-					 const double v) 
+void buildMetric ( GFace *gf , double *uv, double *metric){
+  Pair<SVector3,SVector3> der = gf->firstDer(SPoint2(uv[0],uv[1]));
+  metric[0] = dot(der.first(),der.first());
+  metric[1] = dot(der.second(),der.first());
+  metric[2] = dot(der.second(),der.second());
+} 
+
+
+int inCircumCircleAniso ( GFace *gf,
+			  MTriangle *base, 
+			  const double *uv ,
+			  const double *metricb,
+			  const std::vector<double> & Us,
+			  const std::vector<double> & Vs) 
 {
-  throw;
-}
 
-gmsh2dMetric :: gmsh2dMetric ( double lc )
-  : a11 ( 1./(lc*lc) ) ,a21 ( 0.0 ) ,a22 ( 1./(lc*lc) )
-{  
-  throw;
-}
+  double x[2],Radius2,metric[3];
 
+  double pa[2] = {(Us[base->getVertex(0)->getNum()]+Us[base->getVertex(1)->getNum()]+Us[base->getVertex(2)->getNum()])/3.,
+  		  (Vs[base->getVertex(0)->getNum()]+Vs[base->getVertex(1)->getNum()]+Vs[base->getVertex(2)->getNum()])/3.};
+  
+  buildMetric (gf,pa,metric);
+  circumCenterMetric ( base, metric, Us,Vs,x,Radius2);
+//   buildMetric (gf,pa,metric);
+//   circumCenterMetric ( base, metric, Us,Vs,x,Radius2);
+//   buildMetric (gf,pa,metric);
+//   circumCenterMetric ( base, metric, Us,Vs,x,Radius2);
+//   buildMetric (gf,pa,metric);
+//   circumCenterMetric ( base, metric, Us,Vs,x,Radius2);
+
+  const double a = metric[0];
+  const double b = metric[1];
+  const double d = metric[2];
+
+
+  double d2 = (x[0] - uv[0]) * (x[0] - uv[0]) * a
+    + (x[1] - uv[1]) * (x[1] - uv[1]) * d
+    + 2. * (x[0] - uv[0]) * (x[1] - uv[1]) * b;
+  
+  return d2 < Radius2;  
+}
 
 MTri3::MTri3 ( MTriangle * t, double lc) : deleted(false), base (t)
 {
@@ -159,6 +219,10 @@ void connectTriangles ( std::list<MTri3*> & l)
 {
   connectTris(l.begin(),l.end());
 }
+void connectTriangles ( std::vector<MTri3*> & l)
+{
+  connectTris(l.begin(),l.end());
+}
 
 void recurFindCavity (std::list<edgeXface> & shell, 
 		      std::list<MTri3*> & cavity, 
@@ -189,6 +253,38 @@ void recurFindCavity (std::list<edgeXface> & shell,
     }
 }
 
+void recurFindCavityAniso (GFace *gf,
+			   std::list<edgeXface> & shell, 
+			   std::list<MTri3*> & cavity, 
+			   double *metric, 
+			   double *param, 
+			   MTri3 *t,
+			   std::vector<double> & Us,
+			   std::vector<double> & Vs)
+{
+  t->setDeleted(true);
+  // the cavity that has to be removed
+  // because it violates delaunay criterion
+  cavity.push_back(t);
+
+  for (int i=0;i<3;i++)
+    {
+      MTri3 *neigh =  t->getNeigh(i) ;
+      if (!neigh)
+	  shell.push_back ( edgeXface ( t, i ) );
+      else  if (!neigh->isDeleted())
+	{
+	  int circ =  inCircumCircleAniso (gf, neigh->tri(), param, metric, Us, Vs);
+	  if (circ)
+	    recurFindCavityAniso ( gf, shell, cavity,metric, param, neigh,Us,Vs);
+	  else
+	    shell.push_back ( edgeXface ( t, i ) );
+	}
+    }
+}
+
+
+
 bool circUV ( MTriangle   *t , 
 	      std::vector<double> & Us,
 	      std::vector<double> & Vs , double *res, GFace *gf)
@@ -214,8 +310,8 @@ bool circUV ( MTriangle   *t ,
 
 bool invMapUV ( MTriangle   *t , 
 		double *p,
-		std::vector<double> & Us,
-		std::vector<double> & Vs , double *uv, double tol)
+		const std::vector<double> & Us,
+		const std::vector<double> & Vs , double *uv, double tol)
 {
   double mat[2][2];
   double b[2];
@@ -264,23 +360,29 @@ double getSurfUV ( MTriangle   *t ,
 
 }
 
-bool insertVertex (MVertex *v , 
-		   double  *param , 
+bool insertVertex (GFace *gf,
+		   MVertex *v , 
+		   double  *param ,
 		   MTri3   *t ,
 		   std::set<MTri3*,compareTri3Ptr> &allTets,
 		   std::vector<double> & vSizes,
 		   std::vector<double> & vSizesBGM,
 		   std::vector<double> & Us,
-		   std::vector<double> & Vs)
+		   std::vector<double> & Vs,
+		   double *metric = 0)
 {
   std::list<edgeXface>  shell;
   std::list<MTri3*>  cavity; 
   std::list<MTri3*>  new_cavity;
 
-  double p[3] = {v->x(),v->y(),v->z()};
-
-  recurFindCavity ( shell,  cavity, p , param, t, Us, Vs);  
-
+  if (!metric){
+    double p[3] = {v->x(),v->y(),v->z()};
+    recurFindCavity ( shell,  cavity, p , param, t, Us, Vs);  
+  }
+  else{
+    recurFindCavityAniso ( gf, shell,  cavity, metric , param, t, Us, Vs);  
+  }
+  
   // check that volume is conserved
   double newVolume = 0;
   double oldVolume = 0;
@@ -361,7 +463,7 @@ bool insertVertex (MVertex *v ,
 //    getchar();
 
 
-  if (fabs(oldVolume - newVolume) < 1.e-10 * oldVolume )
+  if (fabs(oldVolume - newVolume) < 1.e-12 * oldVolume )
     {      
       connectTris ( new_cavity.begin(),new_cavity.end() );      
       allTets.insert(newTris,newTris+shell.size());
@@ -482,6 +584,13 @@ void insertVerticesInFace (GFace *gf, BDS_Mesh *bds)
 
   gf->triangles.clear();
   connectTris ( AllTris.begin(), AllTris.end() );      
+
+  //  _printTris ("before.pos", AllTris, Us,Vs);
+  // this should be MUCH faster !
+  for (int i=0;i<1200;i++)
+    if(!edgeSwapPass(gf,AllTris,SWCR_DEL,Us,Vs,vSizes,vSizesBGM))break;
+
+  //  _printTris ("after2.pos", AllTris, Us,Vs);
   
   Msg(DEBUG,"All %d tris were connected",AllTris.size());
 
@@ -493,90 +602,111 @@ void insertVerticesInFace (GFace *gf, BDS_Mesh *bds)
     {
       MTri3 *worst = *AllTris.begin();
 
-      if (worst->isDeleted())
-	{
-	  delete worst->tri();
-	  delete worst;
-	  AllTris.erase(AllTris.begin());
-	  //	  Msg(INFO,"Worst tet is deleted");
+      if (worst->isDeleted()){
+	delete worst->tri();
+	delete worst;
+	AllTris.erase(AllTris.begin());
+	//	  Msg(INFO,"Worst tet is deleted");
+      }
+      else{
+	if(ITER++%5000 ==0)
+	  Msg(DEBUG,"%7d points created -- Worst tri radius is %8.3f",vSizes.size(),worst->getRadius());
+	double center[2],uv[2],metric[3],r2;
+	if (worst->getRadius() < 0.5 * sqrt(2.0)) break;	  
+	
+	circUV(worst->tri(),Us,Vs,center,gf);
+	MTriangle *base = worst->tri();
+	double pa[2] = {(Us[base->getVertex(0)->getNum()]+Us[base->getVertex(1)->getNum()]+Us[base->getVertex(2)->getNum()])/3.,
+			(Vs[base->getVertex(0)->getNum()]+Vs[base->getVertex(1)->getNum()]+Vs[base->getVertex(2)->getNum()])/3.};
+	buildMetric ( gf , pa, metric);
+	circumCenterMetric ( worst->tri(),metric,Us,Vs,center,r2); 
+	//    	  buildMetric ( gf , center, metric);
+	//    	  circumCenterMetric ( worst->tri(),metric,Us,Vs,center,r2); 
+	//    	  buildMetric ( gf , center, metric);
+	//    	  circumCenterMetric ( worst->tri(),metric,Us,Vs,center,r2); 
+	
+	// 	  printf("%g %g %g\n",metric[0],metric[1],metric[2]);
+	
+	bool inside = invMapUV(worst->tri(),center,Us,Vs,uv,1.e-8);
+	// 	  if (!inside)
+	// 	    circUV(worst->tri(),Us,Vs,center,gf);
+	
+	// 	  inside = invMapUV(worst->tri(),center,Us,Vs,uv,1.e-8);
+	
+	if (inside) {	  
+	  // we use here local coordinates as real coordinates
+	  // x,y and z will be computed hereafter
+	  //	      Msg(INFO,"Point is inside");
+	  GPoint p = gf->point (center[0],center[1]);
+	  MVertex *v = new MFaceVertex (p.x(),p.y(),p.z(),gf,center[0],center[1]);
+	  v->setNum(NUM++);
+	  double lc1 = ((1.-uv[0]-uv[1]) * vSizes [worst->tri()->getVertex(0)->getNum()] + 
+			uv[0] * vSizes [worst->tri()->getVertex(1)->getNum()] + 
+			uv[1] * vSizes [worst->tri()->getVertex(2)->getNum()] ); 
+	  //	      double eigMetricSurface = gf->getMetricEigenvalue(SPoint2(center[0],center[1]));
+	  double lc = BGM_MeshSize(gf,center[0],center[1],p.x(),p.y(),p.z());
+	  //	      printf("lc1 %12.5E lc %12.5E\n",lc1,lc);
+	  
+	  vSizesBGM.push_back( lc );
+	  vSizes.push_back   ( lc1);
+	  Us.push_back( center[0] );
+	  Vs.push_back( center[1] );
+	  
+	  if (!insertVertex ( gf, v , center, worst, AllTris,vSizes,vSizesBGM,Us,Vs,metric)) {
+	    Msg(DEBUG,"2D Delaunay : a cavity is not star shaped");
+	    AllTris.erase(AllTris.begin());
+	    worst->forceRadius(-1);
+	    AllTris.insert(worst);		  
+	    delete v;
+	  }
+	  else 
+	    gf->mesh_vertices.push_back(v);
 	}
-      else
-	{
-	  if(ITER++%5000 ==0)
-	    Msg(DEBUG,"%7d points created -- Worst tri radius is %8.3f",vSizes.size(),worst->getRadius());
-	  double center[2],uv[2];
-	  if (worst->getRadius() < 0.5 * sqrt(2.0)) break;	  
-	  circUV(worst->tri(),Us,Vs,center,gf);
-	  bool inside = invMapUV(worst->tri(),center,Us,Vs,uv,1.e-8);
-
-	  if (inside)
-	    {
-
-// 	      char name[245];
-// 	      sprintf(name,"param%d.pos",ITER);
-// 	      _printTris (name, AllTris, Us,Vs);
-
-
-	      // we use here local coordinates as real coordinates
-	      // x,y and z will be computed hereafter
-	      //	      Msg(INFO,"Point is inside");
-	      GPoint p = gf->point (center[0],center[1]);
-	      MVertex *v = new MFaceVertex (p.x(),p.y(),p.z(),gf,center[0],center[1]);
-	      v->setNum(NUM++);
-	      double lc1 = ((1.-uv[0]-uv[1]) * vSizes [worst->tri()->getVertex(0)->getNum()] + 
-			       uv[0] * vSizes [worst->tri()->getVertex(1)->getNum()] + 
-			       uv[1] * vSizes [worst->tri()->getVertex(2)->getNum()] ); 
-	      //	      double eigMetricSurface = gf->getMetricEigenvalue(SPoint2(center[0],center[1]));
-	      double lc = BGM_MeshSize(gf,center[0],center[1],p.x(),p.y(),p.z());
-	      //	      printf("lc1 %12.5E lc %12.5E\n",lc1,lc);
-	      
-	      vSizesBGM.push_back( lc );
-	      vSizes.push_back   ( lc1);
-	      Us.push_back( center[0] );
-	      Vs.push_back( center[1] );
-	      
-	      if (!insertVertex ( v , center, worst, AllTris,vSizes,vSizesBGM,Us,Vs))
-		{
-		  Msg(DEBUG,"2D Delaunay : a cavity is not star shaped");
-		  AllTris.erase(AllTris.begin());
-		  worst->forceRadius(-1);
-		  AllTris.insert(worst);		  
-		  delete v;
-		}
-	      else 
-		gf->mesh_vertices.push_back(v);
-	    }
- 	  else
- 	    {
-//  	      Msg(DEBUG,"Point %g %g is outside %g %g - %g %g - %g %g (%g %g)",
-//  		  center[0],center[1],		  
-//  		  Us [(worst)->tri()->getVertex(0)->getNum()],
-//  		  Vs [(worst)->tri()->getVertex(0)->getNum()],
-//  		  Us [(worst)->tri()->getVertex(1)->getNum()],
-//  		  Vs [(worst)->tri()->getVertex(1)->getNum()],
-//  		  Us [(worst)->tri()->getVertex(2)->getNum()],
-//  		  Vs [(worst)->tri()->getVertex(2)->getNum()],
-//  		  uv[0],uv[1]);
- 	      AllTris.erase(AllTris.begin());
- 	      worst->forceRadius(0);
- 	      AllTris.insert(worst);
-	      //	      break;
-	    }
+	else {
+	  //	      printf("%g %g %g\n",metric[0],metric[1],metric[2]);
+	  //    	      Msg(DEBUG,"Point %g %g is outside %g %g - %g %g - %g %g (%22.15E %22.15E)",
+	  //    		  center[0],center[1],		  
+	  //    		  Us [(worst)->tri()->getVertex(0)->getNum()],
+	  //    		  Vs [(worst)->tri()->getVertex(0)->getNum()],
+	  //    		  Us [(worst)->tri()->getVertex(1)->getNum()],
+	  //    		  Vs [(worst)->tri()->getVertex(1)->getNum()],
+	  //    		  Us [(worst)->tri()->getVertex(2)->getNum()],
+	  //    		  Vs [(worst)->tri()->getVertex(2)->getNum()],
+	  //    		  uv[0],uv[1]);
+	  //	      throw;
+	  AllTris.erase(AllTris.begin());
+	  worst->forceRadius(0);
+	  AllTris.insert(worst);
+	  //	      break;
 	}
-    }
-
+      }
+    }    
+  for (int i=0;i<10;i++){
+    //    bool splits = edgeSplitPass(1.4,gf,AllTris,SPCR_ALLWAYS,Us,Vs,vSizes,vSizesBGM);
+    //    edgeSwapPass(gf,AllTris,SWCR_QUAL,Us,Vs,vSizes,vSizesBGM);
+    //    bool collapses = edgeCollapsePass(0.67,gf,AllTris,Us,Vs,vSizes,vSizesBGM);
+    //    edgeSwapPass(gf,AllTris,SWCR_QUAL,Us,Vs,vSizes,vSizesBGM);
+    //    if (!collapses && !splits)break;
+  }
+  
+  //  for (int i=0;i<1;i++)if(!edgeCollapsePass(100,gf,AllTris,Us,Vs,vSizes,vSizesBGM))break;
+  //  for (int i=0;i<100;i++)if(!edgeSwapPass(gf,AllTris,SWCR_QUAL,Us,Vs,vSizes,vSizesBGM))break;
+  
+  //   char name[245];
+  //   sprintf(name,"paramFinal%d.pos",gf->tag());
+  //  _printTris (name, AllTris, Us,Vs);
+  //  _printTris ("yo.pos", AllTris, Us,Vs);
   // optimize the mesh
-
+  
   // fill new gmsh structures with triangles
-  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());      
-    }
+  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());      
+  }
 }
diff --git a/Mesh/meshGFaceDelaunayInsertion.h b/Mesh/meshGFaceDelaunayInsertion.h
index 5d307bc3f6a41e01144d2d16e60bea4cb6557bb8..9c10c3f49435670eac70913e434892cdd1ff49a3 100644
--- a/Mesh/meshGFaceDelaunayInsertion.h
+++ b/Mesh/meshGFaceDelaunayInsertion.h
@@ -30,19 +30,22 @@ class GFace;
 class BDS_Mesh;
 class BDS_Point;
 
-struct gmsh2dMetric
-{
-  double a11,a21,a22;
-  gmsh2dMetric (double lc); // uniform metric
-  gmsh2dMetric (double _a11 = 1, double _a21= 0, double _a22=1)
-    : a11(_a11),a21(_a21),a22(_a22)
-  {}
-  inline double eval ( const double &x, const double &y ) const
-  {
-    return x * a11 * x + 2 * x * a21 * y + y * a22 * y;
-  }
-};
-
+void buildMetric ( GFace *gf , double *uv, double *metric);
+int inCircumCircleAniso ( GFace *gf, MTriangle *base, const double *uv , const double *metric,
+			  const std::vector<double> & Us, const std::vector<double> & Vs); 
+void circumCenterMetric ( MTriangle *base, 
+			  const double *metric,
+			  const std::vector<double> & Us,
+			  const std::vector<double> & Vs,
+			  double *x, double &Radius2);
+bool circumCenterMetricInTriangle ( MTriangle *base, 
+				    const double *metric,
+				    const std::vector<double> & Us,
+				    const std::vector<double> & Vs);
+bool invMapUV ( MTriangle   *t , 
+		double *p,
+		const std::vector<double> & Us,
+		const std::vector<double> & Vs , double *uv, double tol);
 
 class MTri3
 {
@@ -71,8 +74,6 @@ class MTri3
     return inCircumCircle ( v->x(), v->y() );
   }
 
-  void Center_Circum_Aniso(double a, double b, double d, double &x, double &y, double &r) const ;
-
   double getSurfaceXY () const { return base -> getSurfaceXY() ; };
   double getSurfaceUV (GFace* gf) const { return base -> getSurfaceUV(gf) ; };
   inline void setDeleted (bool d)
@@ -97,6 +98,7 @@ class MTri3
 };
 
 void connectTriangles ( std::list<MTri3*> & );
+void connectTriangles ( std::vector<MTri3*> & );
 void insertVerticesInFace (GFace *gf, BDS_Mesh *);
 
 class compareTri3Ptr
diff --git a/Mesh/meshGFaceOptimize.cpp b/Mesh/meshGFaceOptimize.cpp
index 677b46dc83b8e2c9aa9d8c27ef6d560acf6a74f1..f460af3b7c1cef6bd5e11361c14987359cf37bf2 100644
--- a/Mesh/meshGFaceOptimize.cpp
+++ b/Mesh/meshGFaceOptimize.cpp
@@ -1,19 +1,18 @@
 #include "meshGFaceOptimize.h"
+#include "qualityMeasures.h"
 #include "GFace.h"
 #include "GEdge.h"
 #include "GVertex.h"
 #include "MVertex.h"
 #include "MElement.h"
+#include "BackgroundMesh.h"
 
-typedef std::map<MVertex*,std::vector<MTriangle*> > v2t_cont ;
-typedef std::map<MEdge, std::pair<MTriangle*,MTriangle*> , Less_Edge>     e2t_cont ;
-
-void buildVertexToTriangle ( GFace *gf ,  v2t_cont &adj )
+void buildVertexToTriangle ( std::vector<MTriangle*> &triangles,  v2t_cont &adj )
 {
   adj.clear();
-  for (unsigned int i=0;i<gf->triangles.size();i++)
+  for (unsigned int i=0;i<triangles.size();i++)
     {
-      MTriangle *t = gf->triangles[i];
+      MTriangle *t = triangles[i];
       for (unsigned int j=0;j<3;j++)
 	{
 	  MVertex *v = t->getVertex(j);
@@ -76,7 +75,7 @@ void parametricCoordinates ( MTriangle *t , GFace *gf, double u[3], double v[3])
 void laplaceSmoothing (GFace *gf)
 {
   v2t_cont adj;
-  buildVertexToTriangle ( gf ,  adj );
+  buildVertexToTriangle ( gf->triangles ,  adj );
 
    for (int i=0;i<5;i++)
     {
@@ -120,85 +119,553 @@ void laplaceSmoothing (GFace *gf)
 
 extern void fourthPoint (double *p1, double *p2, double *p3, double *p4);
 
-bool edgeSwap(e2t_cont &adj, e2t_cont::iterator &it, GFace *gf)
-{  
-  MVertex *v1 = it->first.getVertex(0);
-  MVertex *v2 = it->first.getVertex(1);
-  MVertex *o1,*o2 ;
-  MTriangle *t1 = it->second.first;
-  MTriangle *t2 = it->second.second;
+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(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);
+
+  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:
     {
-      if (t1->getVertex(i) != v1 &&
-	  t1->getVertex(i) != v2)o1 = t1->getVertex(i);
-      if (t2->getVertex(i) != v1 &&
-	  t2->getVertex(i) != v2)o1 = t2->getVertex(i);
+      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()])*.5,
+			     (Vs[v1->getNum()]+Vs[v2->getNum()])*.5};
+      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;
 
-bool edgeSwapTestDelaunay(e2t_cont::iterator &it,GFace *gf)
-{  
-  if(!it->second.second) 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 *v1 = it->first.getVertex(0);
-  MVertex *v2 = it->first.getVertex(1);
-  MVertex *o1,*o2 ;
+  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:
     {
-      if (it->second.first->getVertex(i) != v1 &&
-	  it->second.first->getVertex(i) != v2)o1 = it->second.first->getVertex(i);
-      if (it->second.second->getVertex(i) != v1 &&
-	  it->second.second->getVertex(i) != v2)o2 = it->second.second->getVertex(i);
-    }
-  double p1[2];
-  double p2[2];
-  double op1[2];
-  double op2[2];
-  parametricCoordinates ( v1,gf,p1[0],p1[1] );
-  parametricCoordinates ( v2,gf,p2[0],p2[1] );
-  parametricCoordinates ( o1,gf,op1[0],op1[1] );
-  parametricCoordinates ( o2,gf,op2[0],op2[1] );
+      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 (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 (int k=0;k<outside.size();k++){
+	    if(outside[k]==neigh){
+	      found=true;
+	      break;
+	    }
+	  }
+	  if (!found){
+	    for (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 ori_t1 = gmsh::orient2d(op1, p1, op2);
-  double ori_t2 = gmsh::orient2d(op1,op2, p2);
+  double l_min = lMin;
+  int iMin = -1;
+  for (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 (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 (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;
+  }
 
-  // the quad is concave in parametric coord, return false
-  if ( ori_t1 * ori_t2 <= 0) return false;
+  //  printf("%12.5E %12.5E %d\n",surfBefore,surfAfter,iMin);
+  if (fabs(surfBefore - surfAfter) > 1.e-10*(surfBefore + surfAfter))return false;
 
-  double p1x[3] =  {v1->x(),v1->y(),v1->z()};
-  double p2x[3] =  {v2->x(),v2->y(),v2->z()};
-  double op1x[3] =  {o1->x(),o1->y(),o1->z()};
-  double op2x[3] =  {o2->x(),o2->y(),o2->z()};
-  double fourth[3];
-  fourthPoint(p1x,p2x,op1x,fourth);
-  double result = gmsh::insphere(p1x, p2x, op1x, fourth, op2x) * gmsh::orient3d(p1x, p2x, op1x, fourth);  
-  return result > 0.;
+  for (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 (int i=0;i<cavity.size();i++)
+    cavity[i]->setDeleted(true);
+  connectTriangles ( outside ); 
+  return true;
 }
 
-void edgeSwappingLawson (GFace *gf)
+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 ;
+  std::vector<MTri3*> newTris;
+
+  int nbSwap = 0;
+  
+  for (CONTAINER::iterator it = allTris.begin();it!=allTris.end();++it){
+    if (!(*it)->isDeleted()){
+      for (int i=0;i<3;i++){
+	if (gmshEdgeSwap(*it,gf,i,newTris,cr,Us,Vs,vSizes,vSizesBGM)) {nbSwap++;break;}
+      }
+    }
+    else{
+      CONTAINER::iterator itb = it;
+      ++it;
+      delete *itb;
+      allTris.erase(itb);
+    }
+  }  
+  //  printf("B %d %d tris ",allTris.size(),newTris.size());
+  allTris.insert(newTris.begin(),newTris.end());
+  //  printf("A %d %d tris\n",allTris.size(),newTris.size());
+  return nbSwap;
+}
 
-  e2t_cont adj;
-  buildEdgeToTriangle ( gf ,  adj );
+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;
 
-  e2t_cont :: iterator it = adj.begin();
+  int nbSplit = 0;
   
-  while (it != adj.end())
-    {      
-      bool evalSwapLawson = edgeSwapTestDelaunay(it,gf);
-      if ( evalSwapLawson )
-	{
-	  edgeSwap  (adj, it ,gf );
-	  e2t_cont :: iterator itb = it;
-	  ++it;
-	  adj.erase(itb);
-	}
-      else
-	++it;
-    }  
+  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 ",allTris.size(),newTris.size());
+  allTris.insert(newTris.begin(),newTris.end());
+  printf("A %d %d tris\n",allTris.size(),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 ",allTris.size(),newTris.size());
+  allTris.insert(newTris.begin(),newTris.end());
+  printf("A %d %d tris\n",allTris.size(),newTris.size());
+  return nbCollapse;
 }
 
diff --git a/Mesh/meshGFaceOptimize.h b/Mesh/meshGFaceOptimize.h
index b68331f14c015fea07265b220d4ebc4c271dac6f..e3f0d8e404178268018f45735afd0383f982380b 100644
--- a/Mesh/meshGFaceOptimize.h
+++ b/Mesh/meshGFaceOptimize.h
@@ -21,6 +21,7 @@
 // Please report all bugs and problems to <gmsh@geuz.org>.
 #include "MElement.h"
 #include "MEdge.h"
+#include "meshGFaceDelaunayInsertion.h"
 #include <map>
 #include <vector>
 class GFace;
@@ -31,4 +32,49 @@ void buildVertexToTriangle ( std::vector<MTriangle*> & ,  v2t_cont &adj );
 void buildEdgeToTriangle ( std::vector<MTriangle*> & ,  e2t_cont &adj );
 void laplaceSmoothing   (GFace *gf);
 void edgeSwappingLawson (GFace *gf);
+enum gmshSwapCriterion  {SWCR_DEL, SWCR_QUAL,SWCR_NORM,SWCR_CLOSE};
+enum gmshSplitCriterion {SPCR_CLOSE, SPCR_QUAL,SPCR_ALLWAYS};
+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);
+
+
+bool gmshEdgeSwap(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);
+
+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);
+
+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);
+
+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);
 #endif
diff --git a/Mesh/meshGRegion.cpp b/Mesh/meshGRegion.cpp
index 0635f2abdd3b736672ff8da52ee3708dd610d49a..6cd56c4c20ec6ddbc0f2d9dc518ec47688148359 100644
--- a/Mesh/meshGRegion.cpp
+++ b/Mesh/meshGRegion.cpp
@@ -1,4 +1,4 @@
-// $Id: meshGRegion.cpp,v 1.37 2007-11-28 14:18:10 remacle Exp $
+// $Id: meshGRegion.cpp,v 1.38 2008-01-14 21:29:14 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -609,3 +609,73 @@ void optimizeMeshGRegionGmsh::operator() (GRegion *gr)
   gmshOptimizeMesh (gr, QMTET_2);  
   
 }
+
+bool buildFaceSearchStructure ( GModel *model , fs_cont&search )
+{  
+  search.clear();
+
+  GModel::fiter fit = model->firstFace() ;
+  while (fit != model->lastFace()){    
+    for (int i=0;i<(*fit)->triangles.size();i++)
+      {
+	MVertex *p1=(*fit)->triangles[i]->getVertex(0);
+	MVertex *p2=(*fit)->triangles[i]->getVertex(1);
+	MVertex *p3=(*fit)->triangles[i]->getVertex(2);
+	MVertex *p = std::min(p1,std::min(p2,p3));
+	search.insert ( std::pair<MVertex*,std::pair<MTriangle*,GFace*> > ( p, std::pair<MTriangle*,GFace*>((*fit)->triangles[i],*fit)));
+      }
+    ++fit;
+  }
+  return true;
+}
+
+bool buildEdgeSearchStructure ( GModel *model , es_cont&search )
+{  
+  search.clear();
+
+  GModel::eiter eit = model->firstEdge() ;
+  while (eit != model->lastEdge()){    
+    for (int i=0;i<(*eit)->lines.size();i++)
+      {
+	MVertex *p1=(*eit)->lines[i]->getVertex(0);
+	MVertex *p2=(*eit)->lines[i]->getVertex(1);
+	MVertex *p = std::min(p1,p2);
+	search.insert ( std::pair<MVertex*,std::pair<MLine*,GEdge*> > ( p, std::pair<MLine*,GEdge*>((*eit)->lines[i],*eit)));
+      }
+    ++eit;
+  }
+  return true;
+}
+
+GFace* findInFaceSearchStructure ( MVertex *p1,MVertex *p2,MVertex *p3, const fs_cont&search )
+{
+  MVertex *p = std::min(p1,std::min(p2,p3));
+  
+  for (fs_cont::const_iterator it = search.lower_bound(p);
+       it != search.upper_bound(p);
+       ++it)
+    {
+      MTriangle *t   = it->second.first;
+      GFace     *gf  = it->second.second;
+      if ((t->getVertex(0) == p1 ||t->getVertex(0) == p2 ||t->getVertex(0) == p3)&&
+	  (t->getVertex(1) == p1 ||t->getVertex(1) == p2 ||t->getVertex(1) == p3)&&
+	  (t->getVertex(2) == p1 ||t->getVertex(2) == p2 ||t->getVertex(2) == p3))return gf;
+    }
+  return 0;
+}
+
+GEdge* findInEdgeSearchStructure ( MVertex *p1, MVertex *p2, const es_cont&search )
+{
+  MVertex *p = std::min(p1,p2);
+  
+  for (es_cont::const_iterator it = search.lower_bound(p);
+       it != search.upper_bound(p);
+       ++it)
+    {
+      MLine *l   = it->second.first;
+      GEdge     *ge  = it->second.second;
+      if ((l->getVertex(0) == p1 ||l->getVertex(0) == p2)&&
+	  (l->getVertex(1) == p1 ||l->getVertex(1) == p2))return ge;
+    }
+  return 0;
+}
diff --git a/Mesh/meshGRegion.h b/Mesh/meshGRegion.h
index 6f3fb49c7bfe205f396ea61558d6f492431d1237..f627658e648041e4228a79807270c47318fe10cc 100644
--- a/Mesh/meshGRegion.h
+++ b/Mesh/meshGRegion.h
@@ -21,10 +21,15 @@
 // Please report all bugs and problems to <gmsh@geuz.org>.
 
 #include <vector>
+#include <map>
 
 class GModel;
 class GRegion;
-
+class GFace;
+class GEdge;
+class MVertex;
+class MLine;
+class MTriangle;
 // Create the mesh of the region
 class meshGRegion {
  public :
@@ -56,16 +61,24 @@ class deMeshGRegion {
   void operator () (GRegion *);
 };
 
-// adapt the mesh of a region
-class adaptMeshGRegion {
- public :
-  void operator () (GRegion *);
-};
-
 
 void MeshDelaunayVolume(std::vector<GRegion*> &delaunay);
 int MeshTransfiniteVolume(GRegion *gr);
 int SubdivideExtrudedMesh(GModel *m);
 void carveHole(GRegion *gr, int num, double distance, std::vector<int> &surfaces);
 
+typedef std::multimap<MVertex*,std::pair<MTriangle*,GFace*> > fs_cont ;
+typedef std::multimap<MVertex*,std::pair<MLine*,GEdge*> > es_cont ;
+GFace* findInFaceSearchStructure ( MVertex *p1,MVertex *p2,MVertex *p3, const fs_cont&search );
+GEdge* findInEdgeSearchStructure ( MVertex *p1,MVertex *p2, const es_cont&search );
+bool buildFaceSearchStructure ( GModel *model , fs_cont&search );
+bool buildEdgeSearchStructure ( GModel *model , es_cont&search );
+
+// adapt the mesh of a region
+class adaptMeshGRegion {
+  es_cont *_es; fs_cont *_fs;
+ public :
+  void operator () (GRegion *);
+};
+
 #endif
diff --git a/Mesh/meshGRegionDelaunayInsertion.cpp b/Mesh/meshGRegionDelaunayInsertion.cpp
index 42dffd8cad07a94903a749bd4fe40a6ede67a400..d78c2e2104c7b07aff09072c8d418dd0fd8a7bcc 100644
--- a/Mesh/meshGRegionDelaunayInsertion.cpp
+++ b/Mesh/meshGRegionDelaunayInsertion.cpp
@@ -1,4 +1,4 @@
-// $Id: meshGRegionDelaunayInsertion.cpp,v 1.25 2007-12-03 15:17:40 remacle Exp $
+// $Id: meshGRegionDelaunayInsertion.cpp,v 1.26 2008-01-14 21:29:14 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -21,11 +21,11 @@
 
 #include "OS.h"
 #include "BackgroundMesh.h"
+#include "meshGRegion.h"
 #include "meshGRegionLocalMeshMod.h"
 #include "meshGRegionDelaunayInsertion.h"
 #include "GModel.h"
 #include "GRegion.h"
-#include "meshGRegion.h"
 #include "Numeric.h"
 #include "Message.h"
 #include <set>
@@ -327,42 +327,7 @@ static void setLcs ( MTetrahedron *t, std::map<MVertex*,double> &vSizes)
 
 
 
-GFace* findInFaceSearchStructure ( MVertex *p1,MVertex *p2,MVertex *p3, const fs_cont&search )
-{
-  MVertex *p = std::min(p1,std::min(p2,p3));
-  
-  for (fs_cont::const_iterator it = search.lower_bound(p);
-       it != search.upper_bound(p);
-       ++it)
-    {
-      MTriangle *t   = it->second.first;
-      GFace     *gf  = it->second.second;
-      if ((t->getVertex(0) == p1 ||t->getVertex(0) == p2 ||t->getVertex(0) == p3)&&
-	  (t->getVertex(1) == p1 ||t->getVertex(1) == p2 ||t->getVertex(1) == p3)&&
-	  (t->getVertex(2) == p1 ||t->getVertex(2) == p2 ||t->getVertex(2) == p3))return gf;
-    }
-  return 0;
-}
-
-bool buildFaceSearchStructure ( GModel *model , fs_cont&search )
-{  
-  search.clear();
-
-  GModel::fiter fit = model->firstFace() ;
-  while (fit != model->lastFace()){    
-    for (int i=0;i<(*fit)->triangles.size();i++)
-      {
-	MVertex *p1=(*fit)->triangles[i]->getVertex(0);
-	MVertex *p2=(*fit)->triangles[i]->getVertex(1);
-	MVertex *p3=(*fit)->triangles[i]->getVertex(2);
-	MVertex *p = std::min(p1,std::min(p2,p3));
-	search.insert ( std::pair<MVertex*,std::pair<MTriangle*,GFace*> > ( p, std::pair<MTriangle*,GFace*>((*fit)->triangles[i],*fit)));
-      }
-    ++fit;
-  }
-  return true;
 
-}
 
 GRegion *getRegionFromBoundingFaces (GModel *model, 		      
 				    std::set<GFace *> &faces_bound)
@@ -471,7 +436,7 @@ void adaptMeshGRegion::operator () (GRegion *gr)
 	  }	      	      
 	}
       }
-    Msg(INFO,"Opti : START with %12.5E QBAD %12.5E QAVG %12.5E",totalVolumeb,worst,avg/count);
+    Msg(INFO,"Adaptation : START with %12.5E QBAD %12.5E QAVG %12.5E",totalVolumeb,worst,avg/count);
     for (int i=0;i<nbRanges;i++){
       double low  = (double)i/(nbRanges);
       double high = (double)(i+1)/(nbRanges);
@@ -614,7 +579,9 @@ void gmshOptimizeMesh (GRegion *gr, const gmshQualityMeasure4Tet &qm)
   typedef std::list<MTet4 *> CONTAINER ;
   CONTAINER allTets;
   for (int i=0;i<gr->tetrahedra.size();i++){
-    allTets.push_back(new MTet4(gr->tetrahedra[i],qm));
+    MTet4 * t = new MTet4(gr->tetrahedra[i],qm);
+    t->setOnWhat(gr);
+    allTets.push_back(t);
   }
   gr->tetrahedra.clear();
   
@@ -655,7 +622,7 @@ void gmshOptimizeMesh (GRegion *gr, const gmshQualityMeasure4Tet &qm)
   }    
     
   double qMin = 0.5;
-  double sliverLimit = 0.2;
+  double sliverLimit = 0.1;
 
   int nbESwap=0, nbFSwap=0, nbReloc=0;
     
@@ -695,7 +662,21 @@ void gmshOptimizeMesh (GRegion *gr, const gmshQualityMeasure4Tet &qm)
 	}
       }
 
-    if (!newTets.size())break;
+      if (!newTets.size()){
+        int nbSlivers = 0;
+        int nbSliversWeCanDoSomething = 0;
+        for (int i=0;i<illegals.size();i++)
+	  if (!(illegals[i]->isDeleted())){
+	    if (gmshSliverRemoval(newTets,illegals[i],qm))
+	      nbSliversWeCanDoSomething++;
+	    nbSlivers ++;
+	  }
+	Msg(INFO,"Opti : %d Sliver Removals",nbSliversWeCanDoSomething);
+      }
+
+    if (!newTets.size()){
+      break;
+    }
     
     // add all the new tets in the container
     for (int i=0;i<newTets.size();i++){
@@ -739,15 +720,12 @@ void gmshOptimizeMesh (GRegion *gr, const gmshQualityMeasure4Tet &qm)
     Msg(INFO,"Opti : (%d,%d,%d) = %12.5E QBAD %12.5E QAVG %12.5E (%8.3f sec)",nbESwap,nbFSwap,nbReloc,totalVolumeb,worst,avg/count,t2-t1);
   }
   
-  int nbSlivers = 0;
-  for (int i=0;i<illegals.size();i++)
-    if (!(illegals[i]->isDeleted()))nbSlivers ++;
-  
-  if (nbSlivers){
-    Msg(INFO,"Opti : %d illegal tets are still in the mesh, trying to remove them",nbSlivers);
+
+  if (illegals.size()){
+    Msg(INFO,"Opti : %d illegal tets are still in the mesh",illegals.size());
   }
   else{
-    Msg(INFO,"Opti : no illegal tets in the mesh ;-)",nbSlivers);
+    Msg(INFO,"Opti : no illegal tets in the mesh ;-)");
   }
 
   for (int i=0;i<nbRanges;i++){
diff --git a/Mesh/meshGRegionDelaunayInsertion.h b/Mesh/meshGRegionDelaunayInsertion.h
index 1c6469b5357e188f9abe373e21115bdaa225fdcf..30c002b89507d8a4a36f2d7f7fd113984a8f0643 100644
--- a/Mesh/meshGRegionDelaunayInsertion.h
+++ b/Mesh/meshGRegionDelaunayInsertion.h
@@ -235,8 +235,5 @@ private:
 };
 
 void gmshOptimizeMesh (GRegion *gr, const gmshQualityMeasure4Tet &qm);
-typedef std::multimap<MVertex*,std::pair<MTriangle*,GFace*> > fs_cont ;
-GFace* findInFaceSearchStructure ( MVertex *p1,MVertex *p2,MVertex *p3, const fs_cont&search );
-bool buildFaceSearchStructure ( GModel *model , fs_cont&search );
 
 #endif
diff --git a/Mesh/meshGRegionLocalMeshMod.cpp b/Mesh/meshGRegionLocalMeshMod.cpp
index c2066916a715dcfac70098e979be01c92457f982..a3d4a919432c8c7f46c4f11ddf839dd087f25d25 100644
--- a/Mesh/meshGRegionLocalMeshMod.cpp
+++ b/Mesh/meshGRegionLocalMeshMod.cpp
@@ -1,5 +1,6 @@
 #include "meshGRegionLocalMeshMod.h"
 #include "GEntity.h"
+#include "GRegion.h"
 #include "Message.h"
 
 static int edges[6][2] =    {{0,1},{0,2},{0,3},{1,2},{1,3},{2,3}};
@@ -42,7 +43,6 @@ void computeNeighboringTetsOfACavity (const std::vector<MTet4*> &cavity,std::vec
     }
   }
 }			   
-			   
 bool gmshBuildEdgeCavity ( MTet4 *t, 
 			   int iLocalEdge, 
 			   MVertex **v1,MVertex **v2,
@@ -101,6 +101,9 @@ bool gmshBuildEdgeCavity ( MTet4 *t,
   return true;
 }
 
+
+
+// return 1 if it is closed and 			   
 typedef struct {
   int nbr_triangles ;           /* number of different triangles       */
   int (*triangles)[3] ;         /* triangles array                     */
@@ -260,7 +263,7 @@ bool gmshEdgeSwap (std::vector<MTet4 *> &newTets,
   }
 
   int iBest;
-  double best = 0.0;
+  double best = -1.0;
   for (int i=0;i<sp.nbr_trianguls;i++){
     if(minQuality[i] > best){
       best = minQuality[i];
@@ -306,21 +309,6 @@ bool gmshEdgeSwap (std::vector<MTet4 *> &newTets,
   
   connectTets ( outside );      
 
-//   for (int i=0;i<outside.size();i++){
-//     for (int j=0;j<4;j++){
-//       MTet4 *neigh = outside[i]->getNeigh(j);
-//       //      printf("out(%d,%p) neigh(%d) = %p\n",i,outside[i],j,neigh);
-
-//       if (neigh && neigh->isDeleted())
-// 	{
-// 	  bool found = false;
-// 	  for (int k=0;k<cavity.size();k++){
-// 	    if (cavity[k] == neigh) found = true;
-// 	  }
-// 	  printf ("some old deleted tets are still connected to new tets, %d\n",found);
-// 	}
-//     }
-//   }
   return true;
 }
 
@@ -489,12 +477,95 @@ void gmshBuildVertexCavity_recur ( MTet4 *t,
   }
 }
 
+// sliver removal by compound mesh modif
+// postulate : the edge cannot be swopped
+// so we split it, and then collapse the new
+// vertex on another one (of course, not the
+// other one on the unswappable edge)
+// after that crap, the sliver is trashed
+
+bool gmshSliverRemoval ( std::vector<MTet4   *> &newTets,
+			 MTet4 *t, 
+			 const gmshQualityMeasure4Tet &cr){
+  // look if 
+  std::vector<MTet4*> cavity;
+  std::vector<MTet4*> outside;
+  std::vector<MVertex*> ring;
+  MVertex *v1,*v2;
+  
+  bool isClosed[6];  
+  int nbSwappable = 0;
+  int iSwappable;
+  for (int i=0;i<6;i++){
+     isClosed[i] = gmshBuildEdgeCavity ( t,i,&v1,&v2,cavity,outside,ring);    
+     if (isClosed[i]){
+       nbSwappable++;
+       iSwappable = i;
+     }
+  }
+
+  if (nbSwappable == 0){
+    // all edges are on model edges or model faces, which means that 
+    // nothing can be done
+    return false;
+  }
+  else if (nbSwappable == 1){
+    // classical case, the sliver has 5 edges on the boundary
+    // try to swap first
+    if (gmshEdgeSwap(newTets,t,iSwappable,QMTET_3))return true;
+    // if it does not work, split, smooth and collapse
+    MVertex *v1 = t->tet()->getVertex(edges[iSwappable][0]);
+    MVertex *v2 = t->tet()->getVertex(edges[iSwappable][1]);
+    MVertex *newv = new MVertex (0.5*(v1->x()+v2->x()),
+				 0.5*(v1->y()+v2->y()),
+				 0.5*(v1->z()+v2->z()),t->onWhat());
+    t->onWhat()->mesh_vertices.push_back(newv);
+
+    if (!gmshEdgeSplit(newTets,t,newv,iSwappable,QMTET_ONE))return false;
+     for (int i=0;i<4;i++){
+       if (newTets[newTets.size()-1]->tet()->getVertex(i) == newv)
+	 {
+ 	  gmshSmoothVertex(newTets[newTets.size()-1], i,cr);
+ 	  gmshSmoothVertexOptimize (newTets[newTets.size()-1], i,cr);
+ 	}
+     }
+    
+    for (int i=0; i<newTets.size();i++){
+      MTet4 *new_t = newTets[i];
+      if (!(new_t->isDeleted())){
+	for (int j=0;j<6;j++){
+	  MVertex *va = new_t->tet()->getVertex(edges[j][0]);
+	  MVertex *vb = new_t->tet()->getVertex(edges[j][1]);
+	  if (va == newv &&
+	      (va != v1  && vb != v1 && va != v2  && vb != v2)){
+	    gmshCollapseVertex(newTets,new_t,edges[j][0],edges[j][1],cr);
+	  }
+	  else if (vb == newv &&
+		   (va != v1  && vb != v1 && va != v2  && vb != v2)){
+	    gmshCollapseVertex(newTets,new_t,edges[j][1],edges[j][0],cr);
+	  }
+	}
+      }
+    }
+    
+    return true;
+  }
+  else{
+    for (int i=0;i<4;i++){
+      gmshSmoothVertex(t, i,cr);
+      gmshSmoothVertexOptimize (t, i,cr);
+    }
+  }
+  return false;
+}
 
 bool gmshCollapseVertex ( std::vector<MTet4 *> &newTets,
 			  MTet4 *t, 
 			  int iVertex,
 			  int iTarget,
-			  const gmshQualityMeasure4Tet &cr){
+			  const gmshQualityMeasure4Tet &cr,
+			  const gmshLocalMeshModAction action,
+			  double *minQual){
   
   if(t->isDeleted())throw;
 
@@ -512,9 +583,12 @@ bool gmshCollapseVertex ( std::vector<MTet4 *> &newTets,
   std::vector<MTet4*> toDelete;
   std::vector<MTet4*> toUpdate;
   double volume = 0;
+  double worst = 1.0;
   for (int i=0;i<cavity_v.size();i++){
     bool found = false;
     volume+=fabs(cavity_v[i]->tet()->getVolume());
+    double q = cavity_v[i]->getQuality();
+    worst = std::min(worst,q);
     for (int j=0;j<4;j++){
       if (cavity_v[i]->tet()->getVertex(j) == tg)found=true;
     }
@@ -530,19 +604,30 @@ bool gmshCollapseVertex ( std::vector<MTet4 *> &newTets,
   v->z() = tg->z();
 
   double volume_update=0;
+  
+  double worstAfter = 1.0;
+  double newQuals[2000];
+  if (toUpdate.size() >= 2000) throw;
   for (int i=0;i<toUpdate.size();i++){
-    volume_update+=fabs(toUpdate[i]->tet()->getVolume());
+    double vv;
+    newQuals[i] = qmTet(toUpdate[i]->tet(),cr,&vv);
+    worstAfter = std::min(worstAfter,newQuals[i]);
+    volume_update+=vv;
   }
 
-  //  printf("%12.5E %12.5E\n",volume,volume_update);
+  // printf("%12.5E %12.5E %12.5E %12.5E %d\n",volume,volume_update,worstAfter,worst,toUpdate.size());
 
-  if (fabs(volume-volume_update) > 1.e-10 * volume)
+  if (fabs(volume-volume_update) > 1.e-10 * volume || worstAfter < worst)
     {
       v->x() = x;
       v->y() = y;
       v->z() = z;
       return false;
     }
+  if (action == GMSH_EVALONLY){
+    *minQual = worstAfter;
+    return true;
+  }
   // ok we collapse
   computeNeighboringTetsOfACavity (cavity_v,outside);
   for (int i=0;i<toUpdate.size();i++){
@@ -552,6 +637,7 @@ bool gmshCollapseVertex ( std::vector<MTet4 *> &newTets,
 					   toUpdate[i]->tet()->getVertex(3) == v ? tg : toUpdate[i]->tet()->getVertex(3));
     MTet4 *t41 = new MTet4 ( tr1 , cr) ; 
     t41->setOnWhat(cavity_v[0]->onWhat());
+    t41->setQuality(newQuals[i]);
     outside.push_back(t41);
     newTets.push_back(t41);
   }
@@ -562,10 +648,9 @@ bool gmshCollapseVertex ( std::vector<MTet4 *> &newTets,
   return true;
 }
 
-
-bool gmshSmoothVertex ( MTet4 *t, 
-			int iVertex,
-			const gmshQualityMeasure4Tet &cr){
+bool gmshSmoothVertex( MTet4 *t, 
+		       int iVertex,
+		       const gmshQualityMeasure4Tet &cr){
   
   if(t->isDeleted())throw;
   if(t->tet()->getVertex(iVertex)->onWhat()->dim() < 3)return false;
@@ -611,7 +696,7 @@ bool gmshSmoothVertex ( MTet4 *t,
   t->tet()->getVertex(iVertex)->z() = zcg;
   double worstAfter = 1.0;
   double newQuals[2000];
-  if (cavity.size() > 2000) throw;
+  if (cavity.size() >= 2000) throw;
   for (int i=0 ; i< cavity.size() ; i++){
     double volume;
     newQuals[i] = qmTet(cavity[i]->tet(),cr,&volume);
@@ -623,18 +708,128 @@ bool gmshSmoothVertex ( MTet4 *t,
     t->tet()->getVertex(iVertex)->x() = x;
     t->tet()->getVertex(iVertex)->y() = y;
     t->tet()->getVertex(iVertex)->z() = z;
-    return false;
+    return false;//gmshSmoothVertexOptimize ( t,iVertex,cr);
   }
   else{
     // restore new quality
     for (int i=0 ; i< cavity.size() ; i++){
       cavity[i]->setQuality(newQuals[i]);
-    }
-    
+    }    
     return true;
   }
 }
 
+struct smoothVertexData3D{
+  MVertex *v;
+  std::vector < MTet4 * >ts;
+  double LC;
+}; 
+
+double smoothing_objective_function_3D(double X, double Y, double Z, MVertex *v, std::vector < MTet4 * >&ts){
+  const double oldX = v->x();
+  const double oldY = v->y();
+  const double oldZ = v->z();
+  v->x() = X;
+  v->y() = Y;
+  v->z() = Z;
+
+  std::vector < MTet4 * >::iterator it = ts.begin();
+  std::vector < MTet4 * >::iterator ite = ts.end();
+  double qMin=1,vol;
+  while(it != ite) {
+    qMin = std::min(qmTet((*it)->tet(),QMTET_2,&vol),qMin);
+    ++it;
+  }
+  v->x() = oldX;
+  v->y() = oldY;
+  v->z() = oldZ;
+  return -qMin;  
+}
+
+void deriv_smoothing_objective_function_3D(double X, double Y, double Z, 
+					   double &F, 
+					   double &dFdX, double &dFdY, double &dFdZ,
+					   void *data){
+  smoothVertexData3D *svd = (smoothVertexData3D*)data;
+  MVertex *v = svd->v;
+  const double LARGE = svd->LC*1.e5;
+  const double SMALL = 1./LARGE;
+  F   = smoothing_objective_function_3D(X,Y,Z,v,svd->ts);
+  double F_X = smoothing_objective_function_3D(X+SMALL,Y,Z,v,svd->ts);
+  double F_Y = smoothing_objective_function_3D(X,Y+SMALL,Z,v,svd->ts);
+  double F_Z = smoothing_objective_function_3D(X,Y,Z+SMALL,v,svd->ts);
+  dFdX = (F_X-F)*LARGE;
+  dFdY = (F_Y-F)*LARGE;
+  dFdZ = (F_Z-F)*LARGE;
+}
+
+double smooth_obj_3D(double X, double Y, double Z, void *data){
+  smoothVertexData3D *svd = (smoothVertexData3D*)data;
+  return  smoothing_objective_function_3D(X,Y,Z,svd->v,svd->ts); 
+}
+
+
+bool gmshSmoothVertexOptimize ( MTet4 *t, 
+				int iVertex,
+				const gmshQualityMeasure4Tet &cr){
+  
+  if(t->tet()->getVertex(iVertex)->onWhat()->dim() < 3)return false;
+
+  smoothVertexData3D vd;
+  vd.ts.push_back(t);
+  vd.v = t->tet()->getVertex(iVertex);
+  vd.LC = 1.0; // WRONG
+  gmshBuildVertexCavity_recur (t,t->tet()->getVertex(iVertex),vd.ts);
+
+  double xopti=vd.v->x();
+  double yopti=vd.v->y();
+  double zopti=vd.v->z();
+
+  double val;
+  minimize_3 ( smooth_obj_3D, deriv_smoothing_objective_function_3D, &vd, 4, xopti,yopti,zopti,val);
+
+  double vTot=0;
+
+  for (int i=0 ; i< vd.ts.size() ; i++){
+    double volume = fabs(vd.ts[i]->tet()->getVolume());
+    vTot += volume;    
+  }
+
+  double volumeAfter = 0.0;
+
+  double x = t->tet()->getVertex(iVertex)->x();
+  double y = t->tet()->getVertex(iVertex)->y();
+  double z = t->tet()->getVertex(iVertex)->z();
+
+  t->tet()->getVertex(iVertex)->x() = xopti;
+  t->tet()->getVertex(iVertex)->y() = yopti;
+  t->tet()->getVertex(iVertex)->z() = zopti;
+
+  double newQuals[2000];
+  if (vd.ts.size() >= 2000) throw;
+  for (int i=0 ; i< vd.ts.size() ; i++){
+    double volume;
+    newQuals[i] = qmTet(vd.ts[i]->tet(),cr,&volume);
+    volumeAfter += volume;
+  }
+
+  if (fabs(volumeAfter-vTot) > 1.e-10 * vTot){
+    t->tet()->getVertex(iVertex)->x() = x;
+    t->tet()->getVertex(iVertex)->y() = y;
+    t->tet()->getVertex(iVertex)->z() = z;
+    return false;
+  }
+  else{
+    // restore new quality
+    for (int i=0 ; i< vd.ts.size() ; i++){
+      vd.ts[i]->setQuality(newQuals[i]);
+    }    
+    return true;
+  }
+}
+
+
+
 
 // Edge split sets ...
 // Here, we only build a list of tets that are a subdivision
diff --git a/Mesh/meshGRegionLocalMeshMod.h b/Mesh/meshGRegionLocalMeshMod.h
index 1342d6038132bc052b0b920a7accd6e0e962c02f..af77af2b128794aee34efae4f56ed11c106173b5 100644
--- a/Mesh/meshGRegionLocalMeshMod.h
+++ b/Mesh/meshGRegionLocalMeshMod.h
@@ -26,6 +26,9 @@
 // operators only apply to the "bulk" of the
 // mesh and cannot be applied to boudnaries.
 // I'm working on it
+
+enum gmshLocalMeshModAction {GMSH_DOIT,GMSH_EVALONLY};
+
 bool gmshEdgeSwap (std::vector<MTet4 *> &newTets,
 		   MTet4 *tet, 
 		   int iLocalEdge,
@@ -40,11 +43,17 @@ bool gmshSmoothVertex ( MTet4 *t,
 			int iLocalVertex,
 			const gmshQualityMeasure4Tet &cr);
 
+bool gmshSmoothVertexOptimize ( MTet4 *t, 
+				int iVertex,
+				const gmshQualityMeasure4Tet &cr);
+
 bool gmshCollapseVertex ( std::vector<MTet4 *> &newTets,
 			  MTet4 *t, 
 			  int iVertex,
 			  int iTarget,
-			  const gmshQualityMeasure4Tet &cr);
+			  const gmshQualityMeasure4Tet &cr,
+			  const gmshLocalMeshModAction = GMSH_DOIT,
+			  double *result = 0);
 
 bool gmshEdgeSplit (std::vector<MTet4 *> &newTets,
 		    MTet4 *tet,
@@ -52,6 +61,9 @@ bool gmshEdgeSplit (std::vector<MTet4 *> &newTets,
 		    int iLocalEdge,
 		    const gmshQualityMeasure4Tet &cr);
 
+bool gmshSliverRemoval ( std::vector<MTet4 *> &newTets,
+			 MTet4 *t, 
+			 const gmshQualityMeasure4Tet &cr);
 #endif
 
 
diff --git a/Mesh/qualityMeasures.cpp b/Mesh/qualityMeasures.cpp
index 10530237031ba4c6f540e7577c1f68fda0ae25ba..5f6e2a9e32422e71c8898f96988e60a6a2573e14 100644
--- a/Mesh/qualityMeasures.cpp
+++ b/Mesh/qualityMeasures.cpp
@@ -84,6 +84,8 @@ double qmTet ( const double    &x1, const double    &y1, const double    &z1,
   double quality;
   switch(cr)
     {
+    case QMTET_ONE:
+      return 1.0;
     case QMTET_3:
       {
 	double mat[3][3];
diff --git a/Mesh/qualityMeasures.h b/Mesh/qualityMeasures.h
index 6b4a974e03365aeccb159aecba3267bc5785bdf4..96d9078bd012fbd02e35a9052045dabc6f5dbc18 100644
--- a/Mesh/qualityMeasures.h
+++ b/Mesh/qualityMeasures.h
@@ -7,7 +7,7 @@ class MVertex;
 class MTriangle;
 class MTetrahedron;
 enum gmshQualityMeasure4Triangle {QMTRI_RHO};
-enum gmshQualityMeasure4Tet      {QMTET_1,QMTET_2,QMTET_3};
+enum gmshQualityMeasure4Tet      {QMTET_1,QMTET_2,QMTET_3,QMTET_ONE};
 
 double qmTriangle ( MTriangle *f,  const gmshQualityMeasure4Triangle &cr); 
 double qmTriangle ( BDS_Face *f,  const gmshQualityMeasure4Triangle &cr); 
diff --git a/Numeric/Makefile b/Numeric/Makefile
index 58705f7fa6f6018b333ad8746308e21dc1661fd0..d688c90139f3ac8de26b98e99e14df6d4addd57d 100644
--- a/Numeric/Makefile
+++ b/Numeric/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.37 2007-11-12 10:49:16 geuzaine Exp $
+# $Id: Makefile,v 1.38 2008-01-14 21:29:14 remacle Exp $
 #
 # Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 #
@@ -29,6 +29,7 @@ SRC = Numeric.cpp\
       EigSolve.cpp\
       predicates.cpp\
       gsl_newt.cpp\
+      gsl_min.cpp\
       gsl_brent.cpp
 
 OBJ = ${SRC:.cpp=.o}
diff --git a/Numeric/Numeric.cpp b/Numeric/Numeric.cpp
index b387f416956192c5a8b9dd23ab0b374db2ef185b..d87c3337f6bc4afb21c678616d132d4ff2c5c7ca 100644
--- a/Numeric/Numeric.cpp
+++ b/Numeric/Numeric.cpp
@@ -1,4 +1,4 @@
-// $Id: Numeric.cpp,v 1.36 2007-11-11 19:53:57 remacle Exp $
+// $Id: Numeric.cpp,v 1.37 2008-01-14 21:29:14 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -236,6 +236,16 @@ double det2x2(double mat[2][2])
   return mat[0][0] *mat[1][1] -mat[1][0] *mat[0][1];
 }
 
+double det2x3(double mat[2][3])
+{
+  double v1[3] = {mat[0][0],mat[0][1],mat[0][2]};
+  double v2[3] = {mat[1][0],mat[1][1],mat[1][2]};
+  double n[3];
+  
+  prodve (v1,v2,n);
+  return norm3(n);
+}
+
 double inv2x2(double mat[2][2], double inv[2][2])
 {
   const double det = det2x2 ( mat );
diff --git a/Numeric/Numeric.h b/Numeric/Numeric.h
index da6d16ef5889fe7fd7978a1caae26c45b52b412c..326a1a6b9e5088fd37c97f1671c501e14a773b07 100644
--- a/Numeric/Numeric.h
+++ b/Numeric/Numeric.h
@@ -90,6 +90,7 @@ int sys2x2(double mat[2][2], double b[2], double res[2]);
 int sys3x3(double mat[3][3], double b[3], double res[3], double *det);
 int sys3x3_with_tol(double mat[3][3], double b[3], double res[3], double *det);
 double det2x2(double mat[2][2]);
+double det2x3(double mat[2][3]);
 double det3x3(double mat[3][3]);
 double trace3x3(double mat[3][3]);
 double trace2 (double mat[3][3]);
@@ -121,6 +122,19 @@ void mnbrak(double *ax, double *bx, double *cx, double *fa, double *fb,
 	    double *fc, double (*func)(double));
 void newt(double x[], int n, int *check,
 	  void (*vecfunc)(int, double [], double []));
+void minimize_2 ( double (*f) (double, double, void *data), 
+		  void (*df) (double, double, double &, double &, double &, void *data) ,
+		  void *data,int niter,
+		  double &U, double &V, double &res);
+void minimize_3 ( double (*f) (double, double, double, void *data), 
+		  void (*df) (double, double, double , double &, double &, double &, double &, void *data) ,
+		  void *data,int niter,
+		  double &U, double &V, double &W, double &res);
+void minimize_N (int N, 
+		 double (*f) (double*, void *data), 
+		 void (*df) (double*, double*, double &, void *data) ,
+		 void *data,int niter,
+		 double *, double &res);
 
 /* Robust geometrical predicates */
 namespace gmsh{
diff --git a/benchmarks/2d/coin.geo b/benchmarks/2d/coin.geo
index 0f596c122bd81a1c51469df227fdb5c1dfbba785..8ec850f234bda98f1436aafcc369c56bd40dcd04 100644
--- a/benchmarks/2d/coin.geo
+++ b/benchmarks/2d/coin.geo
@@ -4,14 +4,14 @@ a = 1.0;
 b = 1.0;
 h = 1.0;
 
-refine = 0.0001;
+refine = 0.0000001;
 
 Point(1) = {0, 0, 0, lc*refine};
 Point(2) = {a, 0, 0, lc} ;
 Point(3) = {0, b, 0, lc} ;
 Point(4) = {a, -h, 0, lc} ;
 Point(5) = {-h, b, 0, lc} ;
-Point(6) = {-h, -h, 0, lc} ;
+Point(6) = {-h, -h, 0, lc*refine} ;
 
 Line(1) = {1,3} ;
 Line(2) = {3,5} ;
diff --git a/benchmarks/2d/function.geo b/benchmarks/2d/function.geo
index 0d1f9f4cbdd0122fac6dcf1f7d39435985db5f2a..896c924d9d45b2b16622afa75581bcf943fbdc6a 100644
--- a/benchmarks/2d/function.geo
+++ b/benchmarks/2d/function.geo
@@ -5,15 +5,15 @@ theloop = 0;
 
 Function myCircle
   p1 = newp;
-  Point (p1) = {x,y,0,0.2};
+  Point (p1) = {x,y,0,0.4};
   p2 = newp;
-  Point (p2) = {r+x,y,0,0.2};
+  Point (p2) = {r+x,y,0,0.4};
   p3 = newp;
-  Point (p3) = {x,r+y,0,0.2};
+  Point (p3) = {x,r+y,0,0.4};
   p4 = newp;
-  Point (p4) = {-r+x,y,0,0.2};
+  Point (p4) = {-r+x,y,0,0.4};
   p5 = newp;
-  Point (p5) = {x,-r+y,0,0.2};
+  Point (p5) = {x,-r+y,0,0.4};
   c1 = newreg;
   Circle (c1) = {p2,p1,p3};
   c2 = newreg;
@@ -51,7 +51,5 @@ Call myCircle;
 loop5 = theloop;
 
 Plane Surface(newreg) = {loop5,loop4,loop3,loop2,loop1};
-Line(10000) = {6,11};
-Attractor Line {10000} = {0.5,0.05,5,5,100};
 
 
diff --git a/benchmarks/2d/wing-splines.geo b/benchmarks/2d/wing-splines.geo
index 1f2c8da7d420e81f45b6980ffed8c93c08e2c8ea..83a359af18a664a0d4a263ffaa1f309f4a3bad62 100644
--- a/benchmarks/2d/wing-splines.geo
+++ b/benchmarks/2d/wing-splines.geo
@@ -624,7 +624,7 @@ Circle(409) = {4354,4351,4353};
 Circle(410) = {4353,4351,4352};
 
 // use an embedded curve
-Line {407,408,409,410} In Surface {406};
+//Line {407,408,409,410} In Surface {406};
 
 // or an attractor
 //Attractor Line {1 ... 12} = {1,lc_wing,lc_box,1000,3};