diff --git a/Common/CommandLine.cpp b/Common/CommandLine.cpp
index bad1dd74008288dd212d79e4926b1b2fb0e40d8a..fabb515d453be6e03a9fb45c91503d592b04d3ac 100644
--- a/Common/CommandLine.cpp
+++ b/Common/CommandLine.cpp
@@ -532,6 +532,10 @@ void GetOptions(int argc, char *argv[])
         CTX::instance()->mesh.lcFromCurvature = 1;
         i++;
       }
+      else if(!strcmp(argv[i] + 1, "clcurviso")) {
+        CTX::instance()->mesh.lcFromCurvature = 2;
+        i++;
+      }
       else if(!strcmp(argv[i] + 1, "smooth")) {
         i++;
         if(argv[i])
diff --git a/Common/Context.h b/Common/Context.h
index 8fcab24dfc6b0b6e9cfb04d70c19ed521efcfacb..39918459273b46f0f3300599f814d995accc10e5 100644
--- a/Common/Context.h
+++ b/Common/Context.h
@@ -42,6 +42,7 @@ struct contextMeshOptions {
   int multiplePasses;
   int cgnsImportOrder;
   std::map<int,int> algo2d_per_face;
+  std::map<int,int> curvature_control_per_face;
   int bunin;
   int ignorePartBound;
 };
diff --git a/Geo/GFace.cpp b/Geo/GFace.cpp
index 6e8f8b42445bc3e0e8deb51adfe9fa97593505d1..bc24b569f5f53f170f2520dc639238ec7976f2b5 100644
--- a/Geo/GFace.cpp
+++ b/Geo/GFace.cpp
@@ -62,6 +62,19 @@ GFace::~GFace()
   deleteMesh();
 }
 
+int GFace::getCurvatureControlParameter () const
+{
+  std::map<int,int>::iterator it = CTX::instance()->mesh.curvature_control_per_face.find(tag());
+  return it == CTX::instance()->mesh.curvature_control_per_face.end() ?
+    CTX::instance()->mesh.minCircPoints : it->second ;
+}
+
+void GFace::setCurvatureControlParameter (int n)
+{
+  CTX::instance()->mesh.curvature_control_per_face[tag()] = n;
+}
+
+
 int GFace::getMeshingAlgo () const
 {
   std::map<int,int>::iterator it = CTX::instance()->mesh.algo2d_per_face.find(tag());
@@ -1220,36 +1233,54 @@ int GFace::genusGeom()
   return nSeams - single_seams.size();
 }
 
-bool GFace::fillPointCloud(double maxDist, std::vector<SPoint3> *points,
+bool GFace::fillPointCloud(double maxDist, 
+			   std::vector<SPoint3> *points,
+			   std::vector<SPoint2> *uvpoints,
                            std::vector<SVector3> *normals)
 {
-  if(!buildSTLTriangulation()){
-    Msg::Error("No STL triangulation available to fill point cloud");
-    return false;
-  }
 
   if(!points) return false;
 
-  for(unsigned int i = 0; i < stl_triangles.size(); i += 3){
-    SPoint2 &p0(stl_vertices[stl_triangles[i]]);
-    SPoint2 &p1(stl_vertices[stl_triangles[i + 1]]);
-    SPoint2 &p2(stl_vertices[stl_triangles[i + 2]]);
-
-    GPoint gp0 = point(p0);
-    GPoint gp1 = point(p1);
-    GPoint gp2 = point(p2);
-    double maxEdge = std::max(gp0.distance(gp1),
-                              std::max(gp1.distance(gp2), gp2.distance(gp0)));
-    int N = (int)(maxEdge / maxDist);
-    for(double u = 0.; u < 1.; u += 1. / N){
-      for(double v = 0.; v < 1 - u; v += 1. / N){
-        SPoint2 p = p0 * (1. - u - v) + p1 * u + p2 * v;
-        GPoint gp(point(p));
-        points->push_back(SPoint3(gp.x(), gp.y(), gp.z()));
-        if(normals) normals->push_back(normal(p));
+  if (buildSTLTriangulation()){
+    for(unsigned int i = 0; i < stl_triangles.size(); i += 3){
+      SPoint2 &p0(stl_vertices[stl_triangles[i]]);
+      SPoint2 &p1(stl_vertices[stl_triangles[i + 1]]);
+      SPoint2 &p2(stl_vertices[stl_triangles[i + 2]]);
+      
+      GPoint gp0 = point(p0);
+      GPoint gp1 = point(p1);
+      GPoint gp2 = point(p2);
+      double maxEdge = std::max(gp0.distance(gp1),
+				std::max(gp1.distance(gp2), gp2.distance(gp0)));
+      int N = (int)(maxEdge / maxDist);
+      for(double u = 0.; u < 1.; u += 1. / N){
+	for(double v = 0.; v < 1 - u; v += 1. / N){
+	  SPoint2 p = p0 * (1. - u - v) + p1 * u + p2 * v;
+	  GPoint gp(point(p));
+	  points->push_back(SPoint3(gp.x(), gp.y(), gp.z()));
+	  if (uvpoints)uvpoints->push_back(p);
+	  if(normals) normals->push_back(normal(p));
+	}
       }
     }
   }
+  else {
+    int N = 1000;//(int)(maxDX / maxDist);
+    Range<double> b1 = parBounds(0);
+    Range<double> b2 = parBounds(1);
+    for(int i = 0; i < N; i++) {
+      for(int j = 0; j < N; j++) {
+	double u = (double)i / (N - 1);
+	double v = (double)j / (N - 1);
+	double t1 = b1.low() + u * (b1.high() - b1.low());
+	double t2 = b2.low() + v * (b2.high() - b2.low());
+	GPoint gp = point(t1, t2);
+	points->push_back(SPoint3(gp.x(), gp.y(), gp.z()));
+	if (uvpoints)uvpoints->push_back(SPoint2(t1,t2));
+	if(normals) normals->push_back(normal(SPoint2(t1,t2)));
+      }	
+    }
+  }
   return true;
 }
 
diff --git a/Geo/GFace.h b/Geo/GFace.h
index f17eca36b71ce5214928db653838cdbc445aaf96..282b4bc92d483c0505288616371ae86c65a04345 100644
--- a/Geo/GFace.h
+++ b/Geo/GFace.h
@@ -250,7 +250,9 @@ class GFace : public GEntity
 
   // add points (and optionally normals) in vectors so that two
   // points are at most maxDist apart
-  bool fillPointCloud(double maxDist, std::vector<SPoint3> *points,
+  bool fillPointCloud(double maxDist, 
+		      std::vector<SPoint3> *points,
+		      std::vector<SPoint2> *uvpoints,
                       std::vector<SVector3> *normals=0);
 
   // apply Lloyd's algorithm to the mesh
@@ -286,6 +288,8 @@ class GFace : public GEntity
 
   int getMeshingAlgo () const;
   void setMeshingAlgo (int) ;
+  int getCurvatureControlParameter () const;
+  void setCurvatureControlParameter (int) ;
 
   struct {
     mutable GEntity::MeshGenerationStatus status;
diff --git a/Geo/OCCFace.cpp b/Geo/OCCFace.cpp
index 172f1e1b350540d50bd6341d3ffab872b57d2515..4eb29773b226f7dd83d201ef385099d86e7a91fd 100644
--- a/Geo/OCCFace.cpp
+++ b/Geo/OCCFace.cpp
@@ -14,6 +14,7 @@
 #include "Context.h"
 
 #if defined(HAVE_OCC)
+#include <TColStd_MapIteratorOfMapOfAsciiString.hxx>
 #include <Standard_Version.hxx>
 #include <Geom_CylindricalSurface.hxx>
 #include <Geom_ConicalSurface.hxx>
@@ -25,6 +26,8 @@
 #include <Geom_Plane.hxx>
 #include <gp_Pln.hxx>
 #include <BRepMesh_FastDiscret.hxx>
+#include <BRepMesh_PDiscretRoot.hxx>
+#include <BRepMesh_DiscretFactory.hxx>
 #include <IntTools_Context.hxx>
 #include <BOPTools_Tools2D.hxx>
 #include <BOPTools_Tools3D.hxx>
@@ -336,16 +339,37 @@ bool OCCFace::buildSTLTriangulation(bool force)
 
   Bnd_Box aBox;
   BRepBndLib::Add(s, aBox);
-  BRepMesh_FastDiscret aMesher(0.1, 0.5, aBox, Standard_False, Standard_False, 
-                               Standard_True, Standard_False);
-#if (OCC_VERSION_MAJOR == 6) && (OCC_VERSION_MINOR < 5)
-  aMesher.Add(s);
-#else
-  aMesher.Perform(s);
-#endif
+
+  Standard_Real aDiscret, aXmin, aYmin, aZmin, aXmax, aYmax, aZmax;
+  Standard_Real dX, dY, dZ, dMax, aCoeff, aAngle = .5;
+  aBox.Get(aXmin, aYmin, aZmin, aXmax, aYmax, aZmax);
+  dX=aXmax-aXmin;
+  dY=aYmax-aYmin;
+  dZ=aZmax-aZmin;
+  dMax=dX;
+  if (dY>dMax) {
+    dMax=dY;
+  }
+  if (dZ>dMax) {
+    dMax=dZ;
+  }
+  //                                                                                                                                      
+  aCoeff=0.1;
+  aDiscret=aCoeff*dMax;
+
+  //  std::cout << BRepMesh_DiscretFactory::Get().Names() << "\n";
+  //  const TColStd_MapOfAsciiString &aaa = BRepMesh_DiscretFactory::Get().Names();
+  //  TColStd_MapIteratorOfMapOfAsciiString It (aaa); 
+  //  for (; It.More(); It.Next()) {
+  //    std::cout << It.Key() << "\n";
+  //  }
+
+  BRepMesh_PDiscretRoot pAlgo = BRepMesh_DiscretFactory::Get().Discret(s, aDiscret, aAngle);
+  if (pAlgo)  pAlgo->Perform();
 
   TopLoc_Location loc;
   Handle(Poly_Triangulation) triangulation = BRep_Tool::Triangulation(s, loc);
+  
 
   if(triangulation.IsNull() || !triangulation->HasUVNodes()){
     if(triangulation.IsNull())
diff --git a/Geo/STensor3.cpp b/Geo/STensor3.cpp
index 9793c2eca60372bbde04d97a3d43551e9e7c3cf6..ae0cf3e6cf069baadfc49b240ef98a5e143a4fb0 100644
--- a/Geo/STensor3.cpp
+++ b/Geo/STensor3.cpp
@@ -71,7 +71,7 @@ SMetric3 intersection_conserve_mostaniso (const SMetric3 &m1, const SMetric3 &m2
   double ratio1 = lambda1_min/lambda1_max;
   double ratio2 = lambda2_min/lambda2_max;
 
-  if (ratio1<ratio2)
+  if (lambda1_min<lambda2_min)
     return intersection_conserveM1(m1,m2);
   else
     return intersection_conserveM1(m2,m1);
diff --git a/Mesh/BackgroundMesh.cpp b/Mesh/BackgroundMesh.cpp
index 742377dc654a543510d4e1c40e4a6099cc12f220..7a70f6094ceb8d8be5aae74cd59719badb05b6fc 100644
--- a/Mesh/BackgroundMesh.cpp
+++ b/Mesh/BackgroundMesh.cpp
@@ -35,10 +35,9 @@
 // CTX::instance()->mesh.minCircPoints tells the minimum number of points per
 // radius of curvature
 
-SMetric3 buildMetricTangentToCurve (SVector3 &t, double curvature, double &lambda)
+SMetric3 buildMetricTangentToCurve (SVector3 &t, double l_t, double l_n)
 {
-  lambda = 1.e22;
-  if (curvature == 0.0)return SMetric3(1.e-22);
+  if (l_t == 0.0)return SMetric3(1.e-22);
   SVector3 a;
   if (t(0) <= t(1) && t(0) <= t(2)){
     a = SVector3(1,0,0);
@@ -54,45 +53,59 @@ SMetric3 buildMetricTangentToCurve (SVector3 &t, double curvature, double &lambd
   b.normalize();
   c.normalize();
   t.normalize();
-  lambda =  ((2 * M_PI) /( fabs(curvature) *  CTX::instance()->mesh.minCircPoints ) );
-  
-  SMetric3 curvMetric (1./(lambda*lambda),1.e-10,1.e-10,t,b,c);
-  //SMetric3 curvMetric (1./(lambda*lambda));
-  return curvMetric;
+  SMetric3 Metric (1./(l_t*l_t),1./(l_n*l_n),1./(l_n*l_n),t,b,c);
+  return Metric;
+}
+
+SMetric3 buildMetricTangentToSurface (SVector3 &t1, SVector3 &t2, double l_t1, double l_t2, double l_n)
+{
+  t1.normalize();
+  t2.normalize();
+  SVector3 n = crossprod (t1,t2);
+  n.normalize();
+  SMetric3 Metric (1./(l_t1*l_t1),1./(l_t2*l_t2),1./(l_n*l_n),t1,t2,n);
+  return Metric;
 }
 
-SMetric3 max_edge_curvature_metric(const GVertex *gv, double &length)
+
+SMetric3 max_edge_curvature_metric(const GVertex *gv)
 {
   SMetric3 val (1.e-12);
-  length = 1.e22;
   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> range = _myGEdge->parBounds(0);      
     SMetric3 cc;
-    double l;
     if (gv == _myGEdge->getBeginVertex()) {
       SVector3 t = _myGEdge->firstDer(range.low());
       t.normalize();
-      cc = buildMetricTangentToCurve(t,_myGEdge->curvature(range.low()),l);
+      double l_t = ((2 * M_PI) /( fabs(_myGEdge->curvature(range.low())) 
+				*  CTX::instance()->mesh.minCircPoints ));      
+      double l_n = 1.e12;
+      cc = buildMetricTangentToCurve(t,l_t,l_n);
     }
     else {
       SVector3 t = _myGEdge->firstDer(range.high());
       t.normalize();
-      cc = buildMetricTangentToCurve(t,_myGEdge->curvature(range.high()),l);
+      double l_t = ((2 * M_PI) /( fabs(_myGEdge->curvature(range.high())) 
+				  *  CTX::instance()->mesh.minCircPoints ));      
+      double l_n = 1.e12;
+      cc = buildMetricTangentToCurve(t,l_t,l_n);
     }
     val = intersection(val,cc);
-    length = std::min(length,l);
   }
   return val;
 }
 
-SMetric3 max_edge_curvature_metric(const GEdge *ge, double u, double &l)
+SMetric3 max_edge_curvature_metric(const GEdge *ge, double u)
 {
   SVector3 t =  ge->firstDer(u);
   t.normalize();
-  return buildMetricTangentToCurve(t,ge->curvature(u),l);  
+  double l_t = ((2 * M_PI) /( fabs(ge->curvature(u)) 
+			      *  CTX::instance()->mesh.minCircPoints ));      
+  double l_n = 1.e12;
+  return buildMetricTangentToCurve(t,l_t,l_n);  
 }
 
 static double max_edge_curvature(const GVertex *gv)
@@ -144,35 +157,40 @@ static double max_surf_curvature(const GVertex *gv)
   return val;
 }
 
-static SMetric3 metric_based_on_surface_curvature(const GFace *gf, double u, double v)
+SMetric3 metric_based_on_surface_curvature(const GFace *gf, double u, double v, 
+					   bool surface_isotropic,
+					   double d_normal ,
+					   double d_tangent_max )
 {
-  if (gf->geomType() == GEntity::Plane)return SMetric3(1.e-6);
+  if (gf->geomType() == GEntity::Plane)return SMetric3(1.e-12);
   double cmax, cmin;
   SVector3 dirMax,dirMin;
   cmax = gf->curvatures(SPoint2(u, v),&dirMax, &dirMin, &cmax,&cmin);
-  if (cmin == 0)cmin =1.e-5;
-  if (cmax == 0)cmax =1.e-5;
+  if (cmin == 0)cmin =1.e-12;
+  if (cmax == 0)cmax =1.e-12;
   double lambda2 =  ((2 * M_PI) /( fabs(cmax) *  CTX::instance()->mesh.minCircPoints ) );
   double lambda1 =  ((2 * M_PI) /( fabs(cmin) *  CTX::instance()->mesh.minCircPoints ) );
   SVector3 Z = crossprod(dirMax,dirMin);
-
+  if (surface_isotropic) lambda2 = lambda1 = std::min(lambda2,lambda1);
+  dirMin.normalize();
+  dirMax.normalize();
+  Z.normalize();
   lambda1 = std::max(lambda1, CTX::instance()->mesh.lcMin);
   lambda2 = std::max(lambda2, CTX::instance()->mesh.lcMin);
   lambda1 = std::min(lambda1, CTX::instance()->mesh.lcMax);
   lambda2 = std::min(lambda2, CTX::instance()->mesh.lcMax);
-  /*  if (gf->tag() == 36  || gf->tag() == 62)
-    printf("%g %g -- %g %g -- %g %g %g --  %g %g %g -- %g %g %g -- %g %g\n",u,v,cmax,cmin,
-           dirMax.x(),dirMax.y(),dirMax.z(),
-           dirMin.x(),dirMin.y(),dirMin.z(),
-           Z.x(),Z.y(),Z.z(),
-           lambda1,lambda2);
-  */
-  SMetric3 curvMetric (1./(lambda1*lambda1),1./(lambda2*lambda2),1.e-5, 
+  double lambda3 = std::min(d_normal, CTX::instance()->mesh.lcMax);
+  lambda3 = std::max(lambda3, CTX::instance()->mesh.lcMin);
+  lambda1 = std::min(lambda1, d_tangent_max);
+  lambda2 = std::min(lambda2, d_tangent_max);
+
+  SMetric3 curvMetric (1./(lambda1*lambda1),1./(lambda2*lambda2),
+		       1./(lambda3*lambda3), 
                        dirMin, dirMax, Z );
   return curvMetric;
 }
 
-static SMetric3 metric_based_on_surface_curvature(const GEdge *ge, double u)
+static SMetric3 metric_based_on_surface_curvature(const GEdge *ge, double u, bool iso_surf)
 {
   const GEdgeCompound* ptrCompoundEdge = dynamic_cast<const GEdgeCompound*>(ge);
   if (ptrCompoundEdge)
@@ -200,30 +218,27 @@ static SMetric3 metric_based_on_surface_curvature(const GEdge *ge, double u)
     SMetric3 mesh_size(1.e-12);
     std::list<GFace *> faces = ge->faces();
     std::list<GFace *>::iterator it = faces.begin();
-    int count = 0;
+    // we choose the metric eigenvectors to be the ones
+    // related to the edge ...
+    SMetric3 curvMetric = max_edge_curvature_metric(ge, u);
     while(it != faces.end())
-    {
-    if (((*it)->geomType() != GEntity::CompoundSurface) &&
-        ((*it)->geomType() != GEntity::DiscreteSurface)){
-      SPoint2 par = ge->reparamOnFace((*it), u, 1);
-      SMetric3 m = metric_based_on_surface_curvature (*it, par.x(), par.y());
-      if (!count) mesh_size = m;
-      else mesh_size = intersection(mesh_size, m);
-      count++;
-    }
-    ++it;
-  }
-  double Crv = ge->curvature(u);
-  double lambda =  ((2 * M_PI) /( fabs(Crv) *  CTX::instance()->mesh.minCircPoints ) );
-  SMetric3 curvMetric (1./(lambda*lambda));
-  
-  return intersection_conserve_mostaniso(mesh_size,curvMetric);
+      {
+	if (((*it)->geomType() != GEntity::CompoundSurface) &&
+	    ((*it)->geomType() != GEntity::DiscreteSurface)){
+	  SPoint2 par = ge->reparamOnFace((*it), u, 1);
+	  SMetric3 m = metric_based_on_surface_curvature (*it, par.x(), par.y(), iso_surf);
+	  curvMetric = intersection_conserveM1(curvMetric,m);
+	}
+	++it;
+      }
+    
+    return curvMetric;
   }
 }
 
-static SMetric3 metric_based_on_surface_curvature(const GVertex *gv)
+static SMetric3 metric_based_on_surface_curvature(const GVertex *gv, bool iso_surf)
 {
-  SMetric3 mesh_size(1.e-5);
+  SMetric3 mesh_size(1.e-15);
   std::list<GEdge*> l_edges = gv->edges();
   for (std::list<GEdge*>::const_iterator ite = l_edges.begin(); 
        ite != l_edges.end(); ++ite){
@@ -239,11 +254,11 @@ static SMetric3 metric_based_on_surface_curvature(const GVertex *gv)
         if (gv == _myGEdge->getBeginVertex())
           mesh_size = intersection
             (mesh_size,
-             metric_based_on_surface_curvature(_myGEdge, bounds.low()));
+             metric_based_on_surface_curvature(_myGEdge, bounds.low(), iso_surf));
         else
           mesh_size = intersection
             (mesh_size,
-             metric_based_on_surface_curvature(_myGEdge, bounds.high()));
+             metric_based_on_surface_curvature(_myGEdge, bounds.high(), iso_surf));
     }
 
   }
@@ -262,15 +277,15 @@ static double LC_MVertex_CURV(GEntity *ge, double U, double V)
   switch(ge->dim()){
   case 0:
     Crv = max_edge_curvature((const GVertex *)ge);
-    Crv = std::max(max_surf_curvature((const GVertex *)ge), Crv);
-    //Crv = max_surf_curvature((const GVertex *)ge);
+    //Crv = std::max(max_surf_curvature((const GVertex *)ge), Crv);
+    //    Crv = max_surf_curvature((const GVertex *)ge);
     break;
   case 1:
     {
       GEdge *ged = (GEdge *)ge;
       Crv = ged->curvature(U);
       Crv = std::max(Crv, max_surf_curvature(ged, U));
-      //Crv = max_surf_curvature(ged, U);      
+      //      Crv = max_surf_curvature(ged, U);      
     }
     break;
   case 2:
@@ -284,12 +299,14 @@ static double LC_MVertex_CURV(GEntity *ge, double U, double V)
   return lc;
 }
 
-static SMetric3 LC_MVertex_CURV_ANISO(GEntity *ge, double U, double V)
+SMetric3 LC_MVertex_CURV_ANISO(GEntity *ge, double U, double V)
 {
+  bool iso_surf = CTX::instance()->mesh.lcFromCurvature == 2;
+
   switch(ge->dim()){
-  case 0: return metric_based_on_surface_curvature((const GVertex *)ge);
-  case 1: return metric_based_on_surface_curvature((const GEdge *)ge, U);
-  case 2: return metric_based_on_surface_curvature((const GFace *)ge, U, V);
+  case 0: return metric_based_on_surface_curvature((const GVertex *)ge, iso_surf);
+  case 1: return metric_based_on_surface_curvature((const GEdge *)ge, U, iso_surf);
+  case 2: return metric_based_on_surface_curvature((const GFace *)ge, U, V, iso_surf);
   }
   Msg::Error("Curvature control impossible to compute for a volume!");
   return SMetric3();
@@ -382,19 +399,20 @@ SMetric3 BGM_MeshMetric(GEntity *ge,
 {
   // default lc (mesh size == size of the model)
   double l1 = CTX::instance()->lc;
+  const double max_lc =  CTX::instance()->mesh.lcMax;
 
   // lc from points            
-  double l2 = MAX_LC;
+  double l2 = max_lc;
   if(CTX::instance()->mesh.lcFromPoints && ge->dim() < 2) 
     l2 = LC_MVertex_PNTS(ge, U, V);
   
   // lc from curvature
-  SMetric3 l3(1./(MAX_LC*MAX_LC));
+  SMetric3 l3(1./(max_lc*max_lc));
   if(CTX::instance()->mesh.lcFromCurvature && ge->dim() < 3)
     l3 = LC_MVertex_CURV_ANISO(ge, U, V);
 
   // lc from fields
-  SMetric3 l4(1./(MAX_LC*MAX_LC));
+  SMetric3 l4(1./(max_lc*max_lc));
   FieldManager *fields = ge->model()->getFields();
   if(fields->getBackgroundField() > 0){
     Field *f = fields->get(fields->getBackgroundField());
@@ -426,8 +444,22 @@ SMetric3 BGM_MeshMetric(GEntity *ge,
   }
   
   SMetric3 LC(1./(lc*lc));
-  SMetric3 m = intersection(intersection (l4, LC),l3);
+  SMetric3 m = intersection_conserveM1(intersection_conserveM1 (l4, LC),l3);
   //printf("%g %g %g %g %g %g\n",m(0,0),m(1,1),m(2,2),m(0,1),m(0,2),m(1,2));
+  {
+    fullMatrix<double> V(3,3);
+    fullVector<double> S(3);
+    m.eig(V,S,true);
+    if (S(0) < 0 || S(1) < 0 || S(2) < 0){
+      printf("%g %g %g\n",S(0),S(1),S(2));
+      l3.eig(V,S,true);
+      printf("%g %g %g\n",S(0),S(1),S(2));
+      l4.eig(V,S,true);
+      printf("%g %g %g\n",S(0),S(1),S(2));      
+    }
+  }
+
+
   return m;
   //  return lc * CTX::instance()->mesh.lcFactor;
 }
@@ -510,7 +542,7 @@ backgroundMesh::backgroundMesh(GFace *_gf)
   else {
     std::map<MVertex*, MVertex*>::iterator itv2 = _2Dto3D.begin();
     for ( ; itv2 != _2Dto3D.end(); ++itv2){
-      _sizes[itv2->first] = MAX_LC;
+      _sizes[itv2->first] = CTX::instance()->mesh.lcMax;
     }
   }
   // ensure that other criteria are fullfilled
diff --git a/Mesh/BackgroundMesh.h b/Mesh/BackgroundMesh.h
index 8d5030c6856ea392b668e927e8364d37748384c1..e342b40d320bddeb5f842bb6dcd6e7a95225837f 100644
--- a/Mesh/BackgroundMesh.h
+++ b/Mesh/BackgroundMesh.h
@@ -79,11 +79,17 @@ class backgroundMesh : public simpleFunction<double>
   MElementOctree* get_octree();
 };
 
+SMetric3 buildMetricTangentToCurve (SVector3 &t, double l_t, double l_n);
+SMetric3 buildMetricTangentToSurface (SVector3 &t1, SVector3 &t2, double l_t1, double l_t2, double l_n);
 double BGM_MeshSize(GEntity *ge, double U, double V, double X, double Y, double Z);
 SMetric3 BGM_MeshMetric(GEntity *ge, double U, double V, double X, double Y, double Z);
 bool Extend1dMeshIn2dSurfaces();
 bool Extend2dMeshIn3dVolumes();
-SMetric3 max_edge_curvature_metric(const GVertex *gv, double &l);
+SMetric3 max_edge_curvature_metric(const GVertex *gv);
 SMetric3 max_edge_curvature_metric(const GEdge *ge, double u, double &l);
+SMetric3 metric_based_on_surface_curvature(const GFace *gf, double u, double v, 
+					   bool surface_isotropic = false,
+					   double d_normal = 1.e12,
+					   double d_tangent_max = 1.e12);
 
 #endif
diff --git a/Mesh/Field.cpp b/Mesh/Field.cpp
index eea3503d7be576375a718f8f26cd12d143ac84b1..ae3ead81827d3acb90233d6bf81c7fe4d0b2d8d2 100644
--- a/Mesh/Field.cpp
+++ b/Mesh/Field.cpp
@@ -35,7 +35,6 @@
 #include "ANN/ANN.h"
 #endif
 
-
 Field::~Field()
 {
   for(std::map<std::string, FieldOption*>::iterator it = options.begin();
@@ -1649,8 +1648,33 @@ class AttractorField : public Field
         annDeallocPts(zeronodes);
         delete kdtree;
       }
-      int totpoints = nodes_id.size() + (n_nodes_by_edge-2) * edges_id.size() +
-        n_nodes_by_edge * n_nodes_by_edge * faces_id.size();
+      
+      std::vector<SPoint3> points;
+      std::vector<SPoint2> uvpoints;
+      std::vector<int> offset;
+      offset.push_back(0);
+      for(std::list<int>::iterator it = faces_id.begin();
+          it != faces_id.end(); ++it) {
+        Surface *s = FindSurface(*it);
+        if(!s) {
+          GFace *f = GModel::current()->getFaceByTag(*it);
+	  if (f){
+	    SBoundingBox3d bb = f->bounds();
+	    SVector3 dd = bb.max() - bb.min();
+	    double maxDist = dd.norm() / n_nodes_by_edge ;
+	    bool success = f->fillPointCloud(maxDist, &points, &uvpoints);
+	    offset.push_back(points.size());
+	  }
+	}
+      }
+
+
+      int totpoints = 
+	nodes_id.size() + 
+	(n_nodes_by_edge-2) * edges_id.size() +
+        ((points.size()) ? points.size() : n_nodes_by_edge * n_nodes_by_edge * faces_id.size());
+      printf("%d points found in points clouds (%d edges)\n",totpoints, edges_id.size());
+
       if(totpoints){
         zeronodes = annAllocPts(totpoints, 3);
         _infos.resize(totpoints);
@@ -1676,8 +1700,7 @@ class AttractorField : public Field
       for(std::list<int>::iterator it = edges_id.begin();
           it != edges_id.end(); ++it) {
         Curve *c = FindCurve(*it);
-	GEdge *e = GModel::current()->getEdgeByTag(*it);
-        if(c && !e) {
+        if(c) {
           for(int i = 1; i < n_nodes_by_edge -1 ; i++) {
             double u = (double)i / (n_nodes_by_edge - 1);
             Vertex V = InterpolateCurve(c, u, 0);
@@ -1687,6 +1710,7 @@ class AttractorField : public Field
           }
         }
         else {
+	  GEdge *e = GModel::current()->getEdgeByTag(*it);
           if(e) {
             for(int i = 1; i < n_nodes_by_edge - 1; i++) {
               double u = (double)i / (n_nodes_by_edge - 1);
@@ -1703,6 +1727,7 @@ class AttractorField : public Field
       // This can lead to weird results as we generate attractors over
       // the whole parametric plane (we should really use a mesh,
       // e.g. a refined STL.)
+      int count = 0;
       for(std::list<int>::iterator it = faces_id.begin();
           it != faces_id.end(); ++it) {
         Surface *s = FindSurface(*it);
@@ -1720,22 +1745,33 @@ class AttractorField : public Field
         }
         else {
           GFace *f = GModel::current()->getFaceByTag(*it);
-          if(f) {
-            for(int i = 0; i < n_nodes_by_edge; i++) {
-              for(int j = 0; j < n_nodes_by_edge; j++) {
-                double u = (double)i / (n_nodes_by_edge - 1);
-                double v = (double)j / (n_nodes_by_edge - 1);
-                Range<double> b1 = ge->parBounds(0);
-                Range<double> b2 = ge->parBounds(1);
-                double t1 = b1.low() + u * (b1.high() - b1.low());
-                double t2 = b2.low() + v * (b2.high() - b2.low());
-                GPoint gp = f->point(t1, t2);
-                getCoord(gp.x(), gp.y(), gp.z(), zeronodes[k][0],
-                         zeronodes[k][1], zeronodes[k][2], f);
-		_infos[k++] = AttractorInfo(*it,2,u,v);
-              }
+          if(f) {	    
+	    if (points.size()){
+	      for(int j = offset[count]; j < offset[count+1];j++) {
+		zeronodes[k][0] = points[j].x();
+		zeronodes[k][1] = points[j].y();
+		zeronodes[k][2] = points[j].z();
+		_infos[k++] = AttractorInfo(*it,2,uvpoints[j].x(),uvpoints[j].y());
+	      }
+	      count++;
+	    }
+	    else{
+	      for(int i = 0; i < n_nodes_by_edge; i++) {
+		for(int j = 0; j < n_nodes_by_edge; j++) {
+		  double u = (double)i / (n_nodes_by_edge - 1);
+		  double v = (double)j / (n_nodes_by_edge - 1);
+		  Range<double> b1 = ge->parBounds(0);
+		  Range<double> b2 = ge->parBounds(1);
+		  double t1 = b1.low() + u * (b1.high() - b1.low());
+		  double t2 = b2.low() + v * (b2.high() - b2.low());
+		  GPoint gp = f->point(t1, t2);
+		  getCoord(gp.x(), gp.y(), gp.z(), zeronodes[k][0],
+			   zeronodes[k][1], zeronodes[k][2], f);
+		  _infos[k++] = AttractorInfo(*it,2,u,v);
+		}
+	      }
             }
-          }
+          }	  
         }
       }
       kdtree = new ANNkd_tree(zeronodes, totpoints, 3);
@@ -1766,7 +1802,9 @@ BoundaryLayerField::BoundaryLayerField()
   ratio = 1.1;
   thickness = 1.e-2;
   fan_angle = 30;
+  tgt_aniso_ratio = 1.e10;
   iRecombine = 0;
+  iIntersect = 0;
   options["NodesList"] = new FieldOptionList
     (nodes_id, "Indices of nodes in the geometric model", &update_needed);
   options["EdgesList"] = new FieldOptionList
@@ -1777,10 +1815,14 @@ BoundaryLayerField::BoundaryLayerField()
      "layer is needed", &update_needed);
   options["Quads"] = new FieldOptionInt
     (iRecombine, "Generate recombined elements in the boundary layer");
+  options["IntersectMetrics"] = new FieldOptionInt
+    (iIntersect, "Intersect metrics of all faces");
   options["hwall_n"] = new FieldOptionDouble
     (hwall_n, "Mesh Size Normal to the The Wall");
   options["fan_angle"] = new FieldOptionDouble
     (fan_angle, "Threshold angle for creating a mesh fan in the boundary layer");
+  options["AnisoMax"] = new FieldOptionDouble
+    (tgt_aniso_ratio, "Threshold angle for creating a mesh fan in the boundary layer");
   options["hwall_t"] = new FieldOptionDouble
     (hwall_t, "Mesh Size Tangent to the Wall");
   options["ratio"] = new FieldOptionDouble
@@ -1793,6 +1835,23 @@ BoundaryLayerField::BoundaryLayerField()
 
 double BoundaryLayerField::operator() (double x, double y, double z, GEntity *ge)
 {
+  
+  if (update_needed){
+    for(std::list<int>::iterator it = nodes_id.begin();
+	it != nodes_id.end(); ++it) {
+      _att_fields.push_back(new AttractorField(0,*it,100000));
+    }
+    for(std::list<int>::iterator it = edges_id.begin();
+	it != edges_id.end(); ++it) {
+      _att_fields.push_back(new AttractorField(1,*it,300000));
+    }
+    for(std::list<int>::iterator it = faces_id.begin();
+	it != faces_id.end(); ++it) {
+      _att_fields.push_back(new AttractorField(2,*it,1200));
+    }
+    update_needed = false;
+  }
+  
   double dist = 1.e22;
   AttractorField *cc;
   for (std::list<AttractorField*>::iterator it = _att_fields.begin();
@@ -1831,122 +1890,59 @@ void BoundaryLayerField::operator() (AttractorField *cc, double dist,
   const double ll2   = dist*(ratio-1) + hwall_t;
   double lc_t  = std::min(lc_n*CTX::instance()->mesh.anisoMax, std::min(ll2,hfar));
 
-  SVector3 t1,t2,t3;
-  double L1,L2,L3;
+  lc_n = std::max(lc_n, CTX::instance()->mesh.lcMin);
+  lc_n = std::min(lc_n, CTX::instance()->mesh.lcMax);
+  lc_t = std::max(lc_t, CTX::instance()->mesh.lcMin);
+  lc_t = std::min(lc_t, CTX::instance()->mesh.lcMax);
 
   std::pair<AttractorInfo,SPoint3> pp = cc->getAttractorInfo();
   if (pp.first.dim ==0){
-    L1 = lc_n;
-    L2 = lc_n;
-    L3 = lc_n;
     GVertex *v = GModel::current()->getVertexByTag(pp.first.ent);
-    t1 = SVector3(v->x() -x,v->y() -y,v->z() -z);
-    t1.normalize();
-    //      printf("%p %g %g %g\n",v,t1.x(),t1.y(),t1.z());
-    if (t1.norm() == 0)t1 = SVector3(1,0,0);
-    if (fabs(t1.x()) < fabs(t1.y()) && fabs(t1.x()) < fabs(t1.z()))
-      t2 = SVector3(1,0,0);
-    else if (fabs(t1.y()) < fabs(t1.x()) && fabs(t1.y()) < fabs(t1.z()))
-      t2 = SVector3(0,1,0);
-    else
-      t2 = SVector3(0,0,1);
-    t3 = crossprod(t1,t2);
-    t3.normalize();
-    t2 = crossprod(t3,t1);
-    //      printf("t1 %p %g %g %g\n",v,t1.x(),t1.y(),t1.z());
-    //      printf("t2 %p %g %g %g\n",v,t2.x(),t2.y(),t2.z());
-    //      printf("t3 %p %g %g %g\n",v,t3.x(),t3.y(),t3.z());
+    SVector3 t1 = SVector3(v->x() -x,v->y() -y,v->z() -z);
+    metr = buildMetricTangentToCurve(t1,lc_n,lc_n);
+    return;
   }
   else if (pp.first.dim ==1){
     GEdge *e = GModel::current()->getEdgeByTag(pp.first.ent);
-
-    // the tangent size at this point is the size of the
-    // 1D mesh at this point !
-    // hack : use curvature
-    /*
-      if(CTX::instance()->mesh.lcFromCurvature){
-      double Crv = e->curvature(pp.first.u);
-      double lc = Crv > 0 ? 2 * M_PI / Crv / CTX::instance()->mesh.minCircPoints : 1.e-22;
-      const double ll2b   = dist*(ratio-1) + lc;
-      double lc_tb  = std::min(lc_n*CTX::instance()->mesh.anisoMax, std::min(ll2b,hfar));
-      lc_t = std::min(lc_t,lc_tb);
-      }
-      */
-
-    if (dist < hwall_t){
-      L1 = lc_t;
-      L2 = lc_n;
-      L3 = lc_n;
-      t1 = e->firstDer(pp.first.u);
-      t1.normalize();
-      if (fabs(t1.x()) < fabs(t1.y()) && fabs(t1.x()) < fabs(t1.z()))
-	t2 = SVector3(1,0,0);
-      else if (fabs(t1.y()) < fabs(t1.x()) && fabs(t1.y()) < fabs(t1.z()))
-	t2 = SVector3(0,1,0);
-      else
-	t2 = SVector3(0,0,1);
-      t3 = crossprod(t1,t2);
-      t3.normalize();
-      t2 = crossprod(t3,t1);
-      //	  printf("hfar = %g lc = %g dir %g %g \n",hfar,lc,t1.x(),t1.y());
+    if (dist < thickness){
+      SVector3 t1 = e->firstDer(pp.first.u);
+      double crv = e->curvature(pp.first.u);
+      const double b = lc_t;
+      const double h = lc_n;
+      double oneOverD2 = .5/(b*b) * (1. + sqrt (1. + ( 4.*crv*crv*b*b*b*b/ (h*h*CTX::instance()->mesh.smoothRatio*CTX::instance()->mesh.smoothRatio))));
+      metr = buildMetricTangentToCurve(t1,sqrt(1./oneOverD2),lc_n);
+      return;
     }
     else {
-      L1 = lc_t;
-      L2 = lc_n;
-      L3 = lc_t;
       GPoint p = e->point(pp.first.u);
-      t2 = SVector3(p.x() -x,p.y() -y,p.z() -z);
-      if (fabs(t2.x()) < fabs(t2.y()) && fabs(t2.x()) < fabs(t2.z()))
-	  t1 = SVector3(1,0,0);
-      else if (fabs(t2.y()) < fabs(t2.x()) && fabs(t2.y()) < fabs(t2.z()))
-	t1 = SVector3(0,1,0);
-      else
-	t1 = SVector3(0,0,1);
-      t2.normalize();
-      t3 = crossprod(t1,t2);
-      t3.normalize();
-      t1 = crossprod(t3,t2);
+      SVector3 t2 = SVector3(p.x() -x,p.y() -y,p.z() -z);
+      metr = buildMetricTangentToCurve(t2,lc_t,lc_n);
+      return;
     }
   }
   else {
     GFace *gf = GModel::current()->getFaceByTag(pp.first.ent);
-
-    if (dist < hwall_t){
-      L1 = lc_n;
-      L2 = lc_t;
-      L3 = lc_t;
-      t1 = gf->normal(SPoint2(pp.first.u,pp.first.v));
-      t1.normalize();
-      if (fabs(t1.x()) < fabs(t1.y()) && fabs(t1.x()) < fabs(t1.z()))
-	t2 = SVector3(1,0,0);
-      else if (fabs(t1.y()) < fabs(t1.x()) && fabs(t1.y()) < fabs(t1.z()))
-	t2 = SVector3(0,1,0);
-      else
-	t2 = SVector3(0,0,1);
-      t3 = crossprod(t1,t2);
-      t3.normalize();
-      t2 = crossprod(t3,t1);
-      //	  printf("hfar = %g lc = %g dir %g %g \n",hfar,lc,t1.x(),t1.y());
+    if (dist < thickness){
+      double cmin, cmax;
+      SVector3 dirMax,dirMin;
+      cmax = gf->curvatures(SPoint2(pp.first.u,pp.first.v),&dirMax, &dirMin, &cmax,&cmin);
+      const double b = lc_t;
+      const double h = lc_n;
+      double oneOverD2_min = .5/(b*b) * (1. + sqrt (1. + ( 4.*cmin*cmin*b*b*b*b/ (h*h*CTX::instance()->mesh.smoothRatio*CTX::instance()->mesh.smoothRatio))));
+      double oneOverD2_max = .5/(b*b) * (1. + sqrt (1. + ( 4.*cmax*cmax*b*b*b*b/ (h*h*CTX::instance()->mesh.smoothRatio*CTX::instance()->mesh.smoothRatio))));
+      double dmin = sqrt(1./oneOverD2_min);
+      double dmax = sqrt(1./oneOverD2_max);      
+      dmin = std::min(dmin,dmax*tgt_aniso_ratio);
+      metr = buildMetricTangentToSurface(dirMin,dirMax,dmin,dmax,lc_n);
+      return;
     }
     else {
-      L1 = lc_t;
-      L2 = lc_n;
-      L3 = lc_t;
       GPoint p = gf->point(SPoint2(pp.first.u,pp.first.v));
-      t2 = SVector3(p.x() -x,p.y() -y,p.z() -z);
-      if (fabs(t2.x()) < fabs(t2.y()) && fabs(t2.x()) < fabs(t2.z()))
-	t1 = SVector3(1,0,0);
-      else if (fabs(t2.y()) < fabs(t2.x()) && fabs(t2.y()) < fabs(t2.z()))
-	t1 = SVector3(0,1,0);
-      else
-	t1 = SVector3(0,0,1);
-      t2.normalize();
-      t3 = crossprod(t1,t2);
-      t3.normalize();
-      t1 = crossprod(t3,t2);
+      SVector3 t2 = SVector3(p.x() -x,p.y() -y,p.z() -z);
+      metr = buildMetricTangentToCurve(t2,lc_n,lc_t);
+      return;
     }
   }
-  metr  = SMetric3(1./(L1*L1), 1./(L2*L2), 1./(L3*L3), t1, t2, t3);
 }
 
 void BoundaryLayerField::operator() (double x, double y, double z,
@@ -1963,7 +1959,7 @@ void BoundaryLayerField::operator() (double x, double y, double z,
     }
     for(std::list<int>::iterator it = faces_id.begin();
 	it != faces_id.end(); ++it) {
-      _att_fields.push_back(new AttractorField(2,*it,300));
+      _att_fields.push_back(new AttractorField(2,*it,1200));
     }
     update_needed = false;
   }
@@ -1978,17 +1974,19 @@ void BoundaryLayerField::operator() (double x, double y, double z,
     SPoint3 CLOSEST= (*it)->getAttractorInfo().second;
 
     SMetric3 localMetric;
-    (*this)(*it, cdist,x, y, z, localMetric, ge);
-    hop.push_back(localMetric);
-    //v = intersection(v,localMetric);
+    if (iIntersect){
+      (*this)(*it, cdist,x, y, z, localMetric, ge);
+      hop.push_back(localMetric);
+    }
     if (cdist < current_distance){
+      if (!iIntersect)(*this)(*it, cdist,x, y, z, localMetric, ge);
       current_distance = cdist;
       current_closest = *it;
       v = localMetric;
       _closest_point = CLOSEST;
     }
   }
-  //  for (int i=0;i<hop.size();i++)v = intersection_conserveM1(v,hop[i]);
+  if (iIntersect)for (int i=0;i<hop.size();i++)v = intersection_conserve_mostaniso(v,hop[i]);
   metr = v;
 }
 #endif
diff --git a/Mesh/Field.h b/Mesh/Field.h
index 8b562efc5c4aa7c34f60d54dd81274575d94f09d..aa7023987a85e3654714e1c2d2c290e366fe3db2 100644
--- a/Mesh/Field.h
+++ b/Mesh/Field.h
@@ -144,9 +144,9 @@ class BoundaryLayerField : public Field {
                    SMetric3 &metr, GEntity *ge);
  public:
   double hwall_n,hwall_t,ratio,hfar,thickness,fan_angle; 
-  double current_distance;
+  double current_distance, tgt_aniso_ratio;
   SPoint3 _closest_point;
-  int iRecombine;
+  int iRecombine, iIntersect;
   AttractorField *current_closest;
   virtual bool isotropic () const {return false;}
   virtual const char *getName();
diff --git a/Mesh/HighOrder.cpp b/Mesh/HighOrder.cpp
index b8e0dfe5070265d345a6515ec38cca119256e626..82015f3824dd265cc7b02795f6524d1ced2429ee 100644
--- a/Mesh/HighOrder.cpp
+++ b/Mesh/HighOrder.cpp
@@ -121,7 +121,7 @@ static bool computeEquidistantParameters0(GEdge *ge, double u0, double uN, int N
   return false;
 }
 // 1 = geodesics
-static int method_for_computing_intermediary_points = 0;
+static int method_for_computing_intermediary_points = 1;
 static bool computeEquidistantParameters(GEdge *ge, double u0, double uN, int N,
                                          double *u, double underRelax){
   if (method_for_computing_intermediary_points == 0) // use linear abscissa
@@ -1328,6 +1328,7 @@ void SetOrderN(GModel *m, int order, bool linear, bool incomplete)
   // printJacobians(m, "smoothness.pos");
   // m->writeMSH("SMOOTHED.msh");
   // FIXME !!
+  checkHighOrderTriangles("Surface mesh", m, bad, worst);
   if (!linear && CTX::instance()->mesh.smoothInternalEdges){
     for(GModel::fiter it = m->firstFace(); it != m->lastFace(); ++it) {
       std::vector<MElement*> v;
diff --git a/Mesh/highOrderTools.cpp b/Mesh/highOrderTools.cpp
index b08328e5f91272fbdc324f1b843428b2dc9c0252..28fcbfe3cd966bed4bfe46b46c33307c2799766f 100644
--- a/Mesh/highOrderTools.cpp
+++ b/Mesh/highOrderTools.cpp
@@ -309,7 +309,7 @@ void highOrderTools::applySmoothingTo(std::vector<MElement*> & all, GFace *gf)
   double dx = dx0;
   Msg::Debug(" dx0 = %12.5E", dx0);
   int iter = 0;
-  while(1){
+  while(0){
     double dx2 = smooth_metric_(v, gf, myAssembler, verticesToMove, El);
     Msg::Debug(" dx2  = %12.5E", dx2);
     if (fabs(dx2 - dx) < 1.e-4 * dx0)break;
@@ -372,7 +372,7 @@ double highOrderTools::smooth_metric_(std::vector<MElement*>  & v,
       J23K33.gemm(J23, K33, 1, 0);
       K22.gemm(J23K33, J32, 1, 0);
       //      K33.print("K33");
-      //      K22.print("K22");
+      //            K22.print("K22");
       J23K33.mult(D3, R2);
       //      R2.print("hopla");
       for (int j = 0; j < n2; j++){
diff --git a/Mesh/meshGEdge.cpp b/Mesh/meshGEdge.cpp
index 2863eb7cd6ecfb3739ff1789195b96d4d56639e9..064dd8ac2f83ebcc1fe92e205a1e7cc79b72c765 100644
--- a/Mesh/meshGEdge.cpp
+++ b/Mesh/meshGEdge.cpp
@@ -379,7 +379,7 @@ void meshGEdge::operator() (GEdge *ge)
       SVector3 der = ge->firstDer(pt.t);
       pt.xp = der.norm();
     }
-    a = smoothPrimitive(ge, CTX::instance()->mesh.smoothRatio, Points);
+    a = smoothPrimitive(ge, sqrt(CTX::instance()->mesh.smoothRatio), Points);
     N = std::max(ge->minimumMeshSegments() + 1, (int)(a + 1.));
   }
 
diff --git a/Mesh/meshGFace.cpp b/Mesh/meshGFace.cpp
index 8b12aef059497b165272c6ff96f145f9d7aecf31..12fc4e48e94f2ee4251beaa4ec3c7b06c4406b63 100644
--- a/Mesh/meshGFace.cpp
+++ b/Mesh/meshGFace.cpp
@@ -1134,7 +1134,7 @@ bool meshGenerator(GFace *gf, int RECUR_ITER,
             gf->getMeshingAlgo() == ALGO_2D_AUTO)
       bowyerWatson(gf);
     else {
-      bowyerWatson(gf);
+      bowyerWatson(gf,15000);
       meshGFaceBamg(gf);
     }
     laplaceSmoothing(gf, CTX::instance()->mesh.nbSmoothing);
diff --git a/Mesh/meshGFaceDelaunayInsertion.cpp b/Mesh/meshGFaceDelaunayInsertion.cpp
index 79647bf17a400e11403cc1488cce7e52ebaf2a64..1133b968c25678b41d14948366ee1bfac9495eca 100644
--- a/Mesh/meshGFaceDelaunayInsertion.cpp
+++ b/Mesh/meshGFaceDelaunayInsertion.cpp
@@ -802,6 +802,8 @@ static bool insertAPoint(GFace *gf, std::set<MTri3*,compareTri3Ptr>::iterator it
       MTriangle *base = worst->tri();
       Msg::Debug("Point %g %g cannot be inserted because %d", center[0], center[1], p.succeeded() );
 
+      //      Msg::Info("Point %g %g cannot be inserted because %d", center[0], center[1], p.succeeded() );
+
       AllTris.erase(it);
       worst->forceRadius(-1);
       AllTris.insert(worst);
@@ -834,7 +836,7 @@ static bool insertAPoint(GFace *gf, std::set<MTri3*,compareTri3Ptr>::iterator it
 }
 
 
-void bowyerWatson(GFace *gf)
+void bowyerWatson(GFace *gf, int MAXPNT)
 {
   std::set<MTri3*,compareTri3Ptr> AllTris;
   std::vector<double> vSizes, vSizesBGM, Us, Vs;
@@ -871,7 +873,7 @@ void bowyerWatson(GFace *gf)
         Msg::Debug("%7d points created -- Worst tri radius is %8.3f",
                    vSizes.size(), worst->getRadius());
       double center[2],metric[3],r2;
-      if (worst->getRadius() < /*1.333333/(sqrt(3.0))*/0.5 * sqrt(2.0)) break;
+      if (worst->getRadius() < /*1.333333/(sqrt(3.0))*/0.5 * sqrt(2.0) || vSizes.size() > MAXPNT) break;
       circUV(worst->tri(), Us, Vs, center, gf);
       MTriangle *base = worst->tri();
       double pa[2] = {(Us[base->getVertex(0)->getIndex()] +
diff --git a/Mesh/meshGFaceDelaunayInsertion.h b/Mesh/meshGFaceDelaunayInsertion.h
index d5e1081a176d85b4d3911e0e81068d5a6fe21f38..8782668b315124cfee518ddc8e0827c97ac38b8a 100644
--- a/Mesh/meshGFaceDelaunayInsertion.h
+++ b/Mesh/meshGFaceDelaunayInsertion.h
@@ -138,7 +138,7 @@ void connectQuads(std::vector<MQua4*> &);
 void connectTriangles(std::list<MTri3*> &);
 void connectTriangles(std::vector<MTri3*> &);
 void connectTriangles(std::set<MTri3*,compareTri3Ptr> &AllTris);
-void bowyerWatson(GFace *gf);
+void bowyerWatson(GFace *gf, int MAXPNT= 1000000000);
 void bowyerWatsonFrontal(GFace *gf);
 void bowyerWatsonFrontalLayers(GFace *gf, bool quad);
 void buildBackGroundMesh (GFace *gf);
diff --git a/Mesh/meshGFaceOptimize.cpp b/Mesh/meshGFaceOptimize.cpp
index f0a99fb19ece8a4b67d7ef4965415b154416cbb5..e5106e97b973d8cbcf38db26228f722d966e60d2 100644
--- a/Mesh/meshGFaceOptimize.cpp
+++ b/Mesh/meshGFaceOptimize.cpp
@@ -1054,7 +1054,7 @@ struct  quadBlob {
       v2t_cont :: const_iterator it = adj.find(v);
       int ss = temp.size();
       std::vector<MElement*> elems = it->second;
-
+      /*
       //EMI: try to orient BNodes the same as quad orientations
        for (int j=0;j<elems.size();j++){
        	 bool found  =false;
@@ -1072,6 +1072,31 @@ struct  quadBlob {
        }
        if (found) break;
        }
+      */
+       for (int j=0;j<elems.size();j++){
+       	bool found = false;
+       	for (int i=0;i<4;i++){
+       	  MEdge e = it->second[j]->getEdge(i);
+       	  if (e.getVertex(0) == v &&
+       	      inBoundary(e.getVertex(1),bnodes) &&
+       	      !inBoundary(e.getVertex(1),temp)) {
+       	    v = e.getVertex(1);
+       	    temp.push_back(e.getVertex(1));
+       	    found = true;
+       	    break;
+       	  }
+       	   else if (e.getVertex(1) == v &&
+       	    	   inBoundary(e.getVertex(0),bnodes) &&
+       	    	   !inBoundary(e.getVertex(0),temp)) {
+	     v = e.getVertex(0);
+        temp.push_back(e.getVertex(0));
+         found = true;
+         break;
+       }
+      	}
+       	if (found)break;
+       }
+       
 
        //JF this does not orient 
       // for (int j=0;j<elems.size();j++){
diff --git a/Mesh/meshGRegion.cpp b/Mesh/meshGRegion.cpp
index 950c643777bd733a7c2a7c6046a3b92e1c118f64..325e598694fc59d0b3cf5b53b36670ef0dbe7611 100644
--- a/Mesh/meshGRegion.cpp
+++ b/Mesh/meshGRegion.cpp
@@ -526,11 +526,11 @@ void MeshDelaunayVolume(std::vector<GRegion*> &regions)
        CTX::instance()->mesh.algo3d == ALGO_3D_MMG3D ||
        CTX::instance()->mesh.algo2d == ALGO_2D_FRONTAL_QUAD ||
        CTX::instance()->mesh.algo2d == ALGO_2D_BAMG){
-      sprintf(opts, "pY",  (Msg::GetVerbosity() < 3) ? 'Q':
+      sprintf(opts, "-q1.5pY",  (Msg::GetVerbosity() < 3) ? 'Q':
 	      (Msg::GetVerbosity() > 6) ? 'V': '\0');
     }
     else {
-      sprintf(opts, "pe%c",  (Msg::GetVerbosity() < 3) ? 'Q':
+      sprintf(opts, "-q3.5Ype%c",  (Msg::GetVerbosity() < 3) ? 'Q':
       	      (Msg::GetVerbosity() > 6) ? 'V': '\0');
     }
     try{
@@ -594,11 +594,13 @@ void MeshDelaunayVolume(std::vector<GRegion*> &regions)
    bowyerWatsonFrontalLayers(gr, false);
  else if(CTX::instance()->mesh.algo3d == ALGO_3D_FRONTAL_HEX)
    bowyerWatsonFrontalLayers(gr, true);
- else if(CTX::instance()->mesh.algo3d == ALGO_3D_MMG3D)
+ else if(CTX::instance()->mesh.algo3d == ALGO_3D_MMG3D){
    refineMeshMMG(gr);
+ }
  else
-   if(!Filler::get_nbr_new_vertices() && !LpSmoother::get_nbr_interior_vertices())
+   if(!Filler::get_nbr_new_vertices() && !LpSmoother::get_nbr_interior_vertices()){
      insertVerticesInRegion(gr);
+   }
 #endif
 }
 
diff --git a/Mesh/meshGRegionDelaunayInsertion.cpp b/Mesh/meshGRegionDelaunayInsertion.cpp
index 154ca9e11675eddfcd00e3b5dbb3e281627ae7f5..8aef9689639f66f1fb8bcc9fa0b46ab4f618d567 100644
--- a/Mesh/meshGRegionDelaunayInsertion.cpp
+++ b/Mesh/meshGRegionDelaunayInsertion.cpp
@@ -199,7 +199,6 @@ bool insertVertex(MVertex *v,
   while (it != shell.end()){
     MTetrahedron *tr = new MTetrahedron(it->v[0], it->v[1], it->v[2], v);
 
-    const double ONE_THIRD = 1./3.;
     double lc = .25 * (vSizes[tr->getVertex(0)->getIndex()] +
 		       vSizes[tr->getVertex(1)->getIndex()] +
 		       vSizes[tr->getVertex(2)->getIndex()] +
@@ -259,6 +258,7 @@ bool insertVertex(MVertex *v,
     return true;
   }
   else { // The cavity is NOT star shaped
+    //    printf("hola %d %g %g %22.15E\n",onePointIsTooClose, oldVolume, newVolume, 100.*fabs(oldVolume - newVolume) / oldVolume);
     for (unsigned int i = 0; i <shell.size(); i++) myFactory.Free(newTets[i]);
     delete [] newTets;      
     ittet = cavity.begin();
@@ -746,6 +746,85 @@ void optimizeMesh(GRegion *gr, const qualityMeasure4Tet &qm)
   }
 }
 
+
+static double tetcircumcenter(double a[3], double b[3], double c[3], double d[3], 
+			      double circumcenter[3], double *xi, double *eta, double *zeta){
+  double xba, yba, zba, xca, yca, zca, xda, yda, zda;
+  double balength, calength, dalength;
+  double xcrosscd, ycrosscd, zcrosscd;
+  double xcrossdb, ycrossdb, zcrossdb;
+  double xcrossbc, ycrossbc, zcrossbc;
+  double denominator;
+  double xcirca, ycirca, zcirca;
+  
+  /* Use coordinates relative to point `a' of the tetrahedron. */
+  xba = b[0] - a[0];
+  yba = b[1] - a[1];
+  zba = b[2] - a[2];
+  xca = c[0] - a[0];
+  yca = c[1] - a[1];
+  zca = c[2] - a[2];
+  xda = d[0] - a[0];
+  yda = d[1] - a[1];
+  zda = d[2] - a[2];
+  /* Squares of lengths of the edges incident to `a'. */
+  balength = xba * xba + yba * yba + zba * zba;
+  calength = xca * xca + yca * yca + zca * zca;
+  dalength = xda * xda + yda * yda + zda * zda;
+  /* Cross products of these edges. */
+  xcrosscd = yca * zda - yda * zca;
+  ycrosscd = zca * xda - zda * xca;
+  zcrosscd = xca * yda - xda * yca;
+  xcrossdb = yda * zba - yba * zda;
+  ycrossdb = zda * xba - zba * xda;
+  zcrossdb = xda * yba - xba * yda;
+  xcrossbc = yba * zca - yca * zba;
+  ycrossbc = zba * xca - zca * xba;
+  zcrossbc = xba * yca - xca * yba;
+  
+  /* Calculate the denominator of the formulae. */
+  /* Use orient3d() from http://www.cs.cmu.edu/~quake/robust.html     */
+  /*   to ensure a correctly signed (and reasonably accurate) result, */
+  /*   avoiding any possibility of division by zero.                  */
+  const double xxx =  robustPredicates::orient3d(b, c, d, a);
+  denominator = 0.5 / xxx;
+  
+  /* Calculate offset (from `a') of circumcenter. */
+  xcirca = (balength * xcrosscd + calength * xcrossdb + dalength * xcrossbc) *
+    denominator;
+  ycirca = (balength * ycrosscd + calength * ycrossdb + dalength * ycrossbc) *
+    denominator;
+  zcirca = (balength * zcrosscd + calength * zcrossdb + dalength * zcrossbc) *
+    denominator;
+  circumcenter[0] =  xcirca + a[0];
+  circumcenter[1] =  ycirca + a[1];
+  circumcenter[2] =  zcirca + a[2];
+ 
+  /*  
+ printf(" %g %g %g %g\n",
+	 sqrt((a[0]-xcirca)*(a[0]-xcirca)+(a[1]-ycirca)*(a[1]-ycirca)+(a[2]-zcirca)*(a[2]-zcirca)),
+	 sqrt((b[0]-xcirca)*(b[0]-xcirca)+(b[1]-ycirca)*(b[1]-ycirca)+(b[2]-zcirca)*(b[2]-zcirca)),
+	 sqrt((c[0]-xcirca)*(c[0]-xcirca)+(c[1]-ycirca)*(c[1]-ycirca)+(c[2]-zcirca)*(c[2]-zcirca)),
+	 sqrt((d[0]-xcirca)*(d[0]-xcirca)+(d[1]-ycirca)*(d[1]-ycirca)+(d[2]-zcirca)*(d[2]-zcirca)) );
+  */
+ 
+  if (xi != (double *) NULL) {
+    /* To interpolate a linear function at the circumcenter, define a    */
+    /*   coordinate system with a xi-axis directed from `a' to `b',      */
+    /*   an eta-axis directed from `a' to `c', and a zeta-axis directed  */
+    /*   from `a' to `d'.  The values for xi, eta, and zeta are computed */
+     /*   by Cramer's Rule for solving systems of linear equations.       */
+    *xi = (xcirca * xcrosscd + ycirca * ycrosscd + zcirca * zcrosscd) *
+      (2.0 * denominator);
+    *eta = (xcirca * xcrossdb + ycirca * ycrossdb + zcirca * zcrossdb) *
+      (2.0 * denominator);
+    *zeta = (xcirca * xcrossbc + ycirca * ycrossbc + zcirca * zcrossbc) *
+      (2.0 * denominator);
+  }
+  return xxx;
+}
+
+
 void insertVerticesInRegion (GRegion *gr) 
 {
   //printf("sizeof MTet4 = %d sizeof MTetrahedron %d sizeof(MVertex) %d\n",
@@ -836,10 +915,37 @@ void insertVerticesInRegion (GRegion *gr)
                   vSizes.size(), worst->getRadius());
       if(worst->getRadius() < 1) break;
       double center[3];
-      worst->circumcenter(center);
       double uvw[3];
-      worst->tet()->xyz2uvw(center, uvw);
-      if(worst->tet()->isInside(uvw[0], uvw[1], uvw[2])){
+      MTetrahedron *base = worst->tet();
+      double pa[3] = {base->getVertex(0)->x(), 
+		      base->getVertex(0)->y(), 
+		      base->getVertex(0)->z()};
+      double pb[3] = {base->getVertex(1)->x(), 
+		      base->getVertex(1)->y(),
+		      base->getVertex(1)->z()};
+      double pc[3] = {base->getVertex(2)->x(),
+		      base->getVertex(2)->y(), 
+		      base->getVertex(2)->z()};
+      double pd[3] = {base->getVertex(3)->x(),
+		      base->getVertex(3)->y(),
+		      base->getVertex(3)->z()};
+      
+      //      double UU,VV,WW;
+      tetcircumcenter(pa,pb,pc,pd, center,&uvw[0],&uvw[1],&uvw[2] );
+      //      worst->tet()->xyz2uvw(center, uvw);
+      //      printf("%12.5E %12.5E %12.5E -- %12.5E %12.5E %12.5E \n",
+      //	     UU,VV,WW,uvw[0],uvw[1],uvw[2]);
+      /*
+      if (!worst->tet()->isInside(uvw[0], uvw[1], uvw[2]) && 
+	  worst->getRadius() > 20){
+	uvw[0] = uvw[1] = uvw[2] = 0.25;
+	center[0] = 0.25*(pa[0]+pb[0]+pc[0]+pd[0]);
+	center[1] = 0.25*(pa[1]+pb[1]+pc[1]+pd[1]);
+	center[2] = 0.25*(pa[2]+pb[2]+pc[2]+pd[2]);
+      }
+      */
+
+      if(worst->tet()->isInside(uvw[0], uvw[1], uvw[2])){	
         MVertex *v = new MVertex(center[0], center[1], center[2], worst->onWhat());
         v->setIndex(NUM++);
         double lc1 = 
@@ -860,7 +966,8 @@ void insertVerticesInRegion (GRegion *gr)
           v->onWhat()->mesh_vertices.push_back(v);
       }
       else{
-        myFactory.changeTetRadius(allTets.begin(), 0.);
+	//	printf("point outside %12.5E %12.5E %12.5E %12.5E %12.5E\n",VV,uvw[0], uvw[1], uvw[2],1-uvw[0]-uvw[1]-uvw[2]);
+        myFactory.changeTetRadius(allTets.begin(), 0.0);
       }
     }
 
diff --git a/Mesh/meshGRegionDelaunayInsertion.h b/Mesh/meshGRegionDelaunayInsertion.h
index 331de8b3f209bab3527076fc3fb2ac586280d572..7b7c5d5fc0ef9a16389becaefc9e912e137e0bad 100644
--- a/Mesh/meshGRegionDelaunayInsertion.h
+++ b/Mesh/meshGRegionDelaunayInsertion.h
@@ -14,6 +14,7 @@
 #include "Numeric.h"
 #include "BackgroundMesh.h"
 #include "qualityMeasures.h"
+#include "robustPredicates.h"
 
 //#define _GMSH_PRE_ALLOCATE_STRATEGY_ 1
 class GRegion;
@@ -148,7 +149,22 @@ class MTet4
   {
     return inCircumSphere(v->x(), v->y(), v->z());
   }
-  inline double getVolume() const { return base->getVolume(); }
+  inline double getVolume() const { 
+    
+    double pa[3] = {base->getVertex(0)->x(), 
+		    base->getVertex(0)->y(), 
+		    base->getVertex(0)->z()};
+    double pb[3] = {base->getVertex(1)->x(), 
+		    base->getVertex(1)->y(),
+		    base->getVertex(1)->z()};
+    double pc[3] = {base->getVertex(2)->x(),
+		    base->getVertex(2)->y(), 
+		    base->getVertex(2)->z()};
+    double pd[3] = {base->getVertex(3)->x(),
+		    base->getVertex(3)->y(),
+		    base->getVertex(3)->z()};
+    return fabs(robustPredicates::orient3d(pa, pb, pc, pd))/6.0; 
+  }
   inline void setDeleted(bool d)
   {
     deleted = d;
diff --git a/Mesh/meshGRegionMMG3D.cpp b/Mesh/meshGRegionMMG3D.cpp
index 1a2cb0c8e5487efe95de076bdd013d5b001a9375..306d4fc8372d0c73f1d8be55bd7379f4736d5bcb 100644
--- a/Mesh/meshGRegionMMG3D.cpp
+++ b/Mesh/meshGRegionMMG3D.cpp
@@ -137,13 +137,13 @@ static void gmsh2MMG(GRegion *gr, MMG_pMesh mmg, MMG_pSol sol,
     std::map<MVertex*,std::pair<double,int> >::iterator itv = LCS.find(v);
     if (itv != LCS.end()){
       mmg2gmsh[(*it)->getNum()] = *it;
-      if (CTX::instance()->mesh.lcExtendFromBoundary){
+      //if (CTX::instance()->mesh.lcExtendFromBoundary){
 	double LL = itv->second.first/itv->second.second;
 	SMetric3 l4(1./(LL*LL));
-	SMetric3 MM = intersection (l4, m);	
+	SMetric3 MM = intersection_conserve_mostaniso (l4, m);	
 	m = MM;
 	//lc = std::min(LL,lc);
-      }
+	//      }
     }
 
     sol->met[count++] = m(0,0);
@@ -185,12 +185,12 @@ static void gmsh2MMG(GRegion *gr, MMG_pMesh mmg, MMG_pSol sol,
   
 }
 
-static void updateSizes(GRegion *gr, MMG_pMesh mmg, MMG_pSol sol)
+static void updateSizes(GRegion *gr, MMG_pMesh mmg, MMG_pSol sol, std::map<int,MVertex*> &mmg2gmsh)
 {
   std::list<GFace*> f = gr->faces();
-  /*
+  
   std::map<MVertex*,std::pair<double,int> > LCS;
-  if (CTX::instance()->mesh.lcExtendFromBoundary){
+  //  if (CTX::instance()->mesh.lcExtendFromBoundary){
     for (std::list<GFace*>::iterator it = f.begin(); it != f.end() ; ++it){
       for (int i=0;i<(*it)->triangles.size();i++){
 	MTriangle *t = (*it)->triangles[i];
@@ -208,26 +208,34 @@ static void updateSizes(GRegion *gr, MMG_pMesh mmg, MMG_pSol sol)
 	}
       }
     }
-  }
-  */
-
-  int count = 1;
-  for (int k=1 ; k<=mmg->np; k++){
-    MMG_pPoint ppt = &mmg->point[k]; 
-    if (ppt->tag & M_UNUSED) continue;
+    //  }
+  
 
-    SMetric3 m = BGM_MeshMetric(gr, 0,0,ppt->c[0],ppt->c[1],ppt->c[2]);  
-    /*
-    if (CTX::instance()->mesh.lcExtendFromBoundary){
-      std::map<MVertex*,std::pair<double,int> >::iterator itv = LCS.find(v);
+    int count = 1;
+    for (int k=1 ; k<=mmg->np; k++){
+      MMG_pPoint ppt = &mmg->point[k]; 
+      if (ppt->tag & M_UNUSED) continue;
+      
+      SMetric3 m = BGM_MeshMetric(gr, 0,0,ppt->c[0],ppt->c[1],ppt->c[2]);  
+      
+      std::map<int,MVertex*>::iterator it = mmg2gmsh.find(k);
+      
+      if (it != mmg2gmsh.end() && CTX::instance()->mesh.lcExtendFromBoundary){
+	std::map<MVertex*,std::pair<double,int> >::iterator itv = LCS.find(it->second);
       if (itv != LCS.end()){
 	double LL = itv->second.first/itv->second.second;
 	SMetric3 l4(1./(LL*LL));
-	SMetric3 MM = intersection (l4, m);	
+	//	printf("adding a size %g\n",LL);
+	SMetric3 MM = intersection_conserve_mostaniso (l4, m);	
 	m = MM;
       }
     }
-    */
+    if (m.determinant() < 1.e-30){
+      m(0,0) += 1.e-12;
+      m(1,1) += 1.e-12;
+      m(2,2) += 1.e-12;
+    }
+      
     sol->met[count++] = m(0,0);
     sol->met[count++] = m(1,0);
     sol->met[count++] = m(2,0);
@@ -273,10 +281,10 @@ void refineMeshMMG(GRegion *gr)
     Msg::Info("MMG3D succeeded %d vertices %d tetrahedra",
 	      mmg->np, mmg->ne);
     // Here we should interact with BGM
-    updateSizes(gr,mmg, sol);
+    updateSizes(gr,mmg, sol,mmg2gmsh);
 
     int nTnow  = mmg->ne; 
-    if (fabs((double)(nTnow - nT)) < 0.05 * nT) break;
+    if (fabs((double)(nTnow - nT)) < 0.03 * nT) break;
   }  
 
   //char test[] = "test.mesh";  
diff --git a/Numeric/Numeric.cpp b/Numeric/Numeric.cpp
index 5deca329eaf15e0c9b73a0ce19de5ef9a1b3e65c..e2b599d3aa16c4dd7cd457a61a7d89922b4b1e59 100644
--- a/Numeric/Numeric.cpp
+++ b/Numeric/Numeric.cpp
@@ -648,7 +648,7 @@ void invert_singular_matrix3x3(double MM[3][3], double II[3][3])
 bool newton_fd(void (*func)(fullVector<double> &, fullVector<double> &, void *),
                fullVector<double> &x, void *data, double relax, double tolx)
 {
-  const int MAXIT = 50;
+  const int MAXIT = 10;
   const double EPS = 1.e-4;
   const int N = x.size();