diff --git a/contrib/HighOrderMeshOptimizer/MetaEl.cpp b/contrib/HighOrderMeshOptimizer/MetaEl.cpp
index 17fd8b3e913532cf1d4193a39ef707933a17d1e6..a180ff5e05d2f2d2708ab83233a700c11fcb5ff4 100644
--- a/contrib/HighOrderMeshOptimizer/MetaEl.cpp
+++ b/contrib/HighOrderMeshOptimizer/MetaEl.cpp
@@ -42,70 +42,136 @@
 std::map<int, MetaEl::metaInfoType> MetaEl::_metaInfo;
 
 
-//namespace {
-//
-//
-//void getEdgeIndQuad(int order, std::vector<int> edInd) {
-//  edInd.clear();
-//  const int nbEdVert = 4*(order-1);
-//  edInd.resize(nbEdVert);
-//  for (int iV = 0; iV < nbEdVert; iV++) edInd[iV] = iV;
-//}
-//
-//
-//
-//}
-
 
 MetaEl::metaInfoType::metaInfoType(int type, int order)
 {
+  static const double TOL = 1e-10;
   typedef std::vector<int>::iterator VIntIter;
 
-  int iBaseFace = 0, iTopFace = 0, nbEdVert = 0;
+  // Get basic info on dimension, faces and vertices
+  int iBaseFace = 0, iTopFace = 0, nbEdVert = 0, nbPrimTopVert = 0;
+  int baseFaceType = 0;
   switch (type) {
     case TYPE_QUA:
-      iBaseFace = 0; iTopFace = 2; nbEdVert = 4 + 4*(order-1);
+      dim = 2;
+      iBaseFace = 0; iTopFace = 2;
+      nbEdVert = 4 + 4*(order-1); nbPrimTopVert = 2;
+      baseFaceType = TYPE_LIN;
       break;
     case TYPE_PRI:
+      dim = 3;
       iBaseFace = 0; iTopFace = 1; nbEdVert = 6 + 9*(order-1);
+      nbPrimTopVert = 3;
+      baseFaceType = TYPE_TRI;
       break;
     case TYPE_HEX:
+      dim = 3;
       iBaseFace = 0; iTopFace = 5; nbEdVert = 8 + 12*(order-1);
+      nbPrimTopVert = 4;
+      baseFaceType = TYPE_QUA;
       break;
     default:
       Msg::Error("MetaEl not implemented for element of type %d", type);
+      dim = 0;
       nbVert = 0;
       return;
   }
 
-  // Get HO nodal basis
-//  const int tag = ElementType::getTag(type, order, true);                     // Get tag for incomplete element type
+  // Get HO nodal base
   const int tag = ElementType::getTag(type, order);                             // Get tag for complete element type
   if (!tag) return;
   const nodalBasis *basis = BasisFactory::getNodalBasis(tag);
   nbVert = basis->getNumShapeFunctions();
   points = basis->points;
-  std::set<int> foundVerts;
 
-  // Vertices on base and top faces
+  // Get nodal bases (HO and linear) on base face
+  const int baseFaceTag = ElementType::getTag(baseFaceType, order);                       // Get tag for base face basis
+  if (!baseFaceTag) return;
+  baseFaceBasis = BasisFactory::getNodalBasis(baseFaceTag);
+  const int nbBaseVert = baseFaceBasis->getNumShapeFunctions();
+  baseGradShapeFuncVal.resize(nbBaseVert, 3*nbBaseVert);
+  const int linBaseFaceTag = ElementType::getTag(baseFaceType, 1);                        // Get tag for base face linear basis
+  if (!linBaseFaceTag) return;
+  linBaseFaceBasis = BasisFactory::getNodalBasis(linBaseFaceTag);
+  const int nbLinBaseShapeFunc = linBaseFaceBasis->getNumShapeFunctions();
+  baseLinShapeFuncVal.resize(nbBaseVert, nbLinBaseShapeFunc);
+  
+  // Compute value of nodal bases of base face at base vertices 
+  const fullMatrix<double> basePoints = baseFaceBasis->getReferenceNodes();
+  for (int iV = 0; iV < baseFaceBasis->getNumShapeFunctions(); iV++) {
+    const double &u = basePoints(iV, 0), &v = basePoints(iV, 1);
+    double linShapeFunc[1256];
+    linBaseFaceBasis->f(u, v, 0., linShapeFunc);
+    for (int iSF = 0; iSF < nbLinBaseShapeFunc; iSF++)
+      baseLinShapeFuncVal(iV, iSF) = linShapeFunc[iSF];
+    double gradShapeFunc[1256][3];
+    baseFaceBasis->df(u, v, 0., gradShapeFunc);
+    for (int iSF = 0; iSF < nbBaseVert; iSF++) {
+      baseGradShapeFuncVal(iV, 3*iSF) = gradShapeFunc[iSF][0];
+      baseGradShapeFuncVal(iV, 3*iSF+1) = gradShapeFunc[iSF][1];
+      baseGradShapeFuncVal(iV, 3*iSF+2) = gradShapeFunc[iSF][2];
+    }
+  }
+
+  // Indices of vertices on base face
+  std::set<int> foundVerts;
   baseInd = basis->getClosure(basis->getClosureId(iBaseFace, 1, 0));
-  topInd = basis->getClosure(basis->getClosureId(iTopFace, 0, 0));
   foundVerts.insert(baseInd.begin(), baseInd.end());
+
+  // Indices of vertices on top face
+  topInd = basis->getClosure(basis->getClosureId(iTopFace, 0, 0));
   foundVerts.insert(topInd.begin(), topInd.end());
+  freeTopInd = std::vector<int>(topInd.begin()+nbPrimTopVert, topInd.end());
+  freeTop2Base.resize(freeTopInd.size());
+  for(int iVFT = 0; iVFT < freeTopInd.size(); iVFT++) {
+    const int &indFT = freeTopInd[iVFT];
+    const double &uTop = points(indFT, 0);
+    const double &vTop = points(indFT, 1);
+    for (int iVB = 0; iVB < baseInd.size(); iVB++) {                            // Find corresponding base vertex
+      const int &indB = baseInd[iVB];
+      const double diffU = uTop - points(indB, 0);
+      const double diffV = (dim == 3) ? vTop - points(indB, 1) : 0.;
+      if (diffU*diffU+diffV*diffV < TOL) {
+        freeTop2Base[iVFT] = iVB;
+        break;
+      }
+    }
+  }
 
-  // Vertices on edges (excluding base and top faces)
+  // Indices of vertices on edges (excluding base and top faces)
   for (int iV = 0; iV < nbEdVert; iV++)
     if (foundVerts.find(iV) == foundVerts.end()) {
       edgeInd.push_back(iV);
       foundVerts.insert(iV);
     }
 
-  // Remaining face and interior vertices
-  for(int iV = 0; iV < nbVert; iV++)
+  // Indices of remaining face and interior vertices
+  for(int iV = 0; iV < nbVert; iV++) {
     if (foundVerts.find(iV) == foundVerts.end()) {
       otherInd.push_back(iV);
       foundVerts.insert(iV);
+      const double &uFree = points(iV, 0);
+      const double &vFree = points(iV, 1);
+      for (int iVB = 0; iVB < baseInd.size(); iVB++) {                          // Find corresponding base vertex
+        const int &indB = baseInd[iVB];
+        const double diffU = uFree - points(indB, 0);
+        const double diffV = (dim == 3) ? vFree - points(indB, 1) : 0.;
+        if (diffU*diffU+diffV*diffV < TOL) {
+          other2Base.push_back(iVB);
+          break;
+        }
+      }
+      for (int iVT = 0; iVT < baseInd.size(); iVT++) {                          // Find corresponding top vertex
+        const int &indT = topInd[iVT];
+        const double diffU = uFree - points(indT, 0);
+        const double diffV = (dim == 3) ? vFree - points(indT, 1) : 0.;
+        if (diffU*diffU+diffV*diffV < TOL) {
+          other2Top.push_back(iVT);
+          break;
+        }
+      }
     }
+  }
 }
 
 
@@ -122,6 +188,44 @@ const MetaEl::metaInfoType &MetaEl::getMetaInfo(int elType, int order)
 
 
 
+void MetaEl::computeBaseNorm(const SVector3 &metaNorm,
+                             const std::vector<MVertex*> &baseVert,
+                             const std::vector<MVertex*> &topPrimVert,
+                             std::vector<SVector3> &baseNorm)
+{
+  const int nbFaceNodes = baseVert.size(), nbPrimFaceNodes = topPrimVert.size();
+
+  // Compute thickness at each primary vertex
+  std::vector<double> linThick(nbPrimFaceNodes);
+  for (int iV = 0; iV < nbPrimFaceNodes; iV++)
+    linThick[iV] = topPrimVert[iV]->distance(baseVert[iV]);
+
+  // Compute normal at each base vertex
+  std::vector<double> thick(nbFaceNodes, 0.);
+  baseNorm.resize(nbFaceNodes);
+  for (int iV = 0; iV < nbFaceNodes; iV++) {
+    for (int iSF = 0; iSF < nbPrimFaceNodes; iSF++)
+      thick[iV] += linThick[iSF] * _mInfo.baseLinShapeFuncVal(iV, iSF);
+    double jac[3][2] = {0., 0., 0., 0., 0., 0.};
+    for (int iSF = 0; iSF < nbFaceNodes; iSF++) {
+      MVertex *vert = baseVert[iSF];
+      const double xyz[3] = {vert->x(), vert->y(), vert->z()};
+      for (int iXYZ = 0; iXYZ < 3; iXYZ++) {
+        jac[iXYZ][0] += xyz[iXYZ] * _mInfo.baseGradShapeFuncVal(iV, 3*iSF); 
+        jac[iXYZ][1] += xyz[iXYZ] * _mInfo.baseGradShapeFuncVal(iV, 3*iSF+1);
+      }
+    }
+    SVector3 dXYZdU(jac[0][0], jac[1][0], jac[2][0]);
+    SVector3 dXYZdV = (_mInfo.dim == 2) ? metaNorm :
+                      SVector3(jac[0][1], jac[1][1], jac[2][1]);
+    baseNorm[iV] = crossprod(dXYZdV, dXYZdU);
+    baseNorm[iV].normalize();
+    baseNorm[iV] *= thick[iV];
+  }
+}
+
+
+
 MetaEl::MetaEl(int type, int order, const std::vector<MVertex*> &baseVert,
                const std::vector<MVertex*> &topPrimVert) :
     _mInfo(getMetaInfo(type, order)), _metaEl(0), _metaEl0(0)
@@ -137,26 +241,58 @@ MetaEl::MetaEl(int type, int order, const std::vector<MVertex*> &baseVert,
 
   // Add copies of vertices in base & top faces (only first-order vertices for top face)
   _metaVert.resize(nbVert);
-  for (int iV = 0; iV < baseInd.size(); iV++)
-    _metaVert[baseInd[iV]] = new MVertex(*baseVert[iV]);
+  for (int iV = 0; iV < baseInd.size(); iV++) {
+    const MVertex* const &bVert = baseVert[iV];
+    GEntity *geomEnt = bVert->onWhat();
+    if (geomEnt->dim() == 0)
+      _metaVert[baseInd[iV]] = new MVertex(*bVert);
+    else if (geomEnt->dim() == 1) {
+      double u;
+      bVert->getParameter(0, u);
+      _metaVert[baseInd[iV]] = new MEdgeVertex(bVert->x(), bVert->y(),
+                                               bVert->z(), geomEnt, u);
+    }
+    else if (geomEnt->dim() == 2) {
+      double u, v;
+      bVert->getParameter(0, u);
+      bVert->getParameter(1, v);
+      _metaVert[baseInd[iV]] = new MEdgeVertex(bVert->x(), bVert->y(),
+                                               bVert->z(), geomEnt, u, v);
+    }
+  }
   for (int iV = 0; iV < topPrimVert.size(); iV++)
     _metaVert[topInd[iV]] = new MVertex(*topPrimVert[iV]);
 
-  // Create first-order meta-element
+  // Create first-order meta-element and normals to base face
+  int faceType;
+  SVector3 metaNorm;
   switch (type) {
-    case TYPE_QUA:
+    case TYPE_QUA: {
       _metaEl0 = new MQuadrangle(_metaVert);
+      SVector3 v01(_metaVert[0]->point(), _metaVert[1]->point());
+      SVector3 v03(_metaVert[0]->point(), _metaVert[3]->point());
+      metaNorm = crossprod(v01, v03);
+      faceType = TYPE_LIN;
       break;
-    case TYPE_PRI:
+    }
+    case TYPE_PRI: {
       _metaEl0 = new MPrism(_metaVert);
+      metaNorm = SVector3(0.);
+      faceType = TYPE_TRI;
       break;
-    case TYPE_HEX:
+    }
+    case TYPE_HEX: {
       _metaEl0 = new MHexahedron(_metaVert);
+      metaNorm = SVector3(0.);
+      faceType = TYPE_QUA;
       break;
-    default:
+    }
+    default: {
       Msg::Error("SuperEl not implemented for element of type %d", type);
       return;
+    }
   }
+  computeBaseNorm(metaNorm, baseVert, topPrimVert, _baseNorm);
 
   // Add HO vertices in top face
   for (int iV = topPrimVert.size(); iV < topInd.size(); iV++) {
@@ -174,24 +310,10 @@ MetaEl::MetaEl(int type, int order, const std::vector<MVertex*> &baseVert,
     _metaVert[ind] = new MVertex(p.x(), p.y(), p.z());
   }
 
-  // Get incomplete high-order meta-element basis
-  const int tag = ElementType::getTag(type, order, true);                       // Get tag for incomplete element type
-  if (!tag) return;
-  const nodalBasis *incBasis = BasisFactory::getNodalBasis(tag);
-
   // Add remaining face and interior points
-  for (int iV = 0; iV < otherInd.size(); iV++) {
-    const int ind = otherInd[iV];
-    double shapeFunc[1256];
-    incBasis->f(points(ind, 0), points(ind, 1), points(ind, 2), shapeFunc);
-    double x = 0., y = 0., z = 0.;
-    for (int iSF = 0; iSF < incBasis->getNumShapeFunctions(); iSF++) {
-      x += shapeFunc[iSF] * _metaVert[iSF]->x();
-      y += shapeFunc[iSF] * _metaVert[iSF]->y();
-      z += shapeFunc[iSF] * _metaVert[iSF]->z();
-    }
-    _metaVert[ind] = new MVertex(x, y, z);
-  }
+  for (int iV = 0; iV < otherInd.size(); iV++)
+    _metaVert[otherInd[iV]] = new MVertex(0., 0., 0.);
+  placeOtherNodes();
 
   // Create high-order meta-element
   switch (type) {
@@ -205,37 +327,10 @@ MetaEl::MetaEl(int type, int order, const std::vector<MVertex*> &baseVert,
       _metaEl = new MHexahedronN(_metaVert, order);
       break;
   }
-
-//  // Scale meta-element if not valid
-//  // TODO: Scale depending on target Jmin?
-//  // TODO: Could be improved by using complete meta-element and use optimization
-//  for (int iter = 0; iter < 10; iter++) {
-//    if (_metaEl->distoShapeMeasure() > 0) break;
-//    if (iter == 0)
-//      Msg::Warning("A meta-element has a negative distortion, trying to scale");
-//    for (int i = 0; i < topPrimVert.size(); ++i) {                              // Move top primary vert.
-//      MVertex *&vb = _metaVert[baseInd[i]], *&vt = _metaVert[topInd[i]];
-//      SPoint3 pb = vb->point(), pt = vt->point();
-//      pt += SPoint3(0.25*(pt-pb));
-//      vt->setXYZ(pt.x(), pt.y(), pt.z());
-//    }
-//    for (int i=topPrimVert.size(); i<topInd.size(); ++i) {                      // Recompute HO vert. in top face
-//      SPoint3 p;
-//      const int ind = topInd[i];
-//      _metaEl0->pnt(points(ind,0), points(ind,1), points(ind,2),p);
-//      _metaVert[ind]->setXYZ(p.x(), p.y(), p.z());
-//    }
-//    for (int i=0; i<otherInd.size(); ++i) {                                     // Recompute vert. not in base & top faces
-//      SPoint3 p;
-//      const int ind = otherInd[i];
-//      _metaEl0->pnt(points(ind,0), points(ind,1), points(ind,2),p);
-//      _metaVert[ind]->setXYZ(p.x(), p.y(), p.z());
-//    }
-//  }
-
 }
 
 
+
 MetaEl::~MetaEl()
 {
   for (int i = 0; i < _metaVert.size(); i++) delete _metaVert[i];
@@ -246,27 +341,62 @@ MetaEl::~MetaEl()
 
 
 
-void MetaEl::curveTop(double factor)
+// Place free interior and face vertices equidistantly between top and base faces
+void MetaEl::placeOtherNodes()
+{
+  for (int iVO = 0; iVO < _mInfo.otherInd.size(); iVO++) {
+    const int &indF = _mInfo.otherInd[iVO];
+    const int &iVB = _mInfo.other2Base[iVO], indB = _mInfo.baseInd[iVB];
+    const int &iVT = _mInfo.other2Top[iVO], indT = _mInfo.topInd[iVT];
+    const int iUVWNorm = _mInfo.dim - 1;
+    const double fact = 0.5 * (_mInfo.points(indF, iUVWNorm)+1.);
+    SPoint3 pB = _metaVert[indB]->point(), pT = _metaVert[indT]->point();
+    const double newX = (1.-fact)*pB.x() + fact*pT.x();
+    const double newY = (1.-fact)*pB.y() + fact*pT.y();
+    const double newZ = (1.-fact)*pB.z() + fact*pT.z();
+    _metaVert[indF]->setXYZ(newX, newY, newZ);
+  }
+}
+
+
+
+void MetaEl::setCurvedTop(double factor)
+{
+  // Place HO vertices of top face so that meta-elemnt has almost uniform thickness
+  for (int iVFT = 0; iVFT < _mInfo.freeTopInd.size(); iVFT++) {
+    const int &indFT = _mInfo.freeTopInd[iVFT];
+    const int &iVB = _mInfo.freeTop2Base[iVFT], indB = _mInfo.baseInd[iVB];
+    const int iUVWNorm = _mInfo.dim - 1;
+    const SPoint3 pB = _metaVert[indB]->point();
+    const SVector3 &norm = _baseNorm[iVB];
+    const double newX = pB.x() + factor*norm.x();
+    const double newY = pB.y() + factor*norm.y();
+    const double newZ = pB.z() + factor*norm.z();
+    _metaVert[indFT]->setXYZ(newX, newY, newZ);
+  }
+
+  // Place the other free nodes equidistantly between base and top faces
+  placeOtherNodes();
+}
+
+
+
+void MetaEl::setFlatTop()
 {
   // Get info on meta-element type
-  // const int &nbVert = _mInfo.nbVert;
   const fullMatrix<double> &points = _mInfo.points;
-  const std::vector<int> &baseInd = _mInfo.baseInd;
-  const std::vector<int> &topInd = _mInfo.topInd;
-  // const std::vector<int> &edgeInd = _mInfo.edgeInd;
-  // const std::vector<int> &otherInd = _mInfo.otherInd;
+  const std::vector<int> &freeTopInd = _mInfo.topInd;
 
-  // Compute displacement of HO vertices in base face  
-  for (int iV = 0; iV < baseInd.size(); iV++) {
-    SPoint3 p0B, p0T;
-    const int indB = baseInd[iV], indT = topInd[iV];
-    _metaEl0->pnt(points(indB, 0), points(indB, 1), points(indB, 2), p0B);
-    _metaEl0->pnt(points(indT, 0), points(indT, 1), points(indT, 2), p0T);
-    SPoint3 pB = _metaVert[indB]->point();
-    _metaVert[indT]->x() = p0T.x() + factor * (pB.x()-p0B.x());
-    _metaVert[indT]->y() = p0T.y() + factor * (pB.y()-p0B.y());
-    _metaVert[indT]->z() = p0T.z() + factor * (pB.z()-p0B.z());
+  // Put top vertices on straight meta-element
+  for (int iVFT = 0; iVFT < freeTopInd.size(); iVFT++) {
+    const int &indFT = freeTopInd[iVFT];
+    SPoint3 p;
+    _metaEl0->pnt(points(indFT, 0), points(indFT, 1), points(indFT, 2), p);
+    _metaVert[indFT]->setXYZ(p.x(), p.y(), p.z());
   }
+
+  // Place the other free nodes equidistantly between base and top faces
+  placeOtherNodes();
 }
 
 
@@ -279,6 +409,7 @@ bool MetaEl::isPointIn(const SPoint3 p) const
 }
 
 
+
 bool MetaEl::straightToCurved(double *xyzS, double *xyzC) const
 {
   double uvw[3];
@@ -295,6 +426,7 @@ bool MetaEl::straightToCurved(double *xyzS, double *xyzC) const
 }
 
 
+
 std::string MetaEl::printPOS()
 {
   std::vector<MVertex*> verts;
diff --git a/contrib/HighOrderMeshOptimizer/MetaEl.h b/contrib/HighOrderMeshOptimizer/MetaEl.h
index e39a2d61c7e6d28eb9235aa631c4a48406a9f556..b4ddd08fc4e1791ac3b36060a4be94625c9d9041 100644
--- a/contrib/HighOrderMeshOptimizer/MetaEl.h
+++ b/contrib/HighOrderMeshOptimizer/MetaEl.h
@@ -37,9 +37,11 @@ class MetaEl
 {
 public:
   MetaEl(int type, int order, const std::vector<MVertex*> &baseVert,
-          const std::vector<MVertex*> &topPrimVert);
+         const std::vector<MVertex*> &topPrimVert);
   ~MetaEl();
-  void curveTop(double factor);
+  void placeOtherNodes();
+  void setCurvedTop(double factor);
+  void setFlatTop();
   bool isOK() const { return _metaEl; }
   bool isPointIn(const SPoint3 p) const;
   bool straightToCurved(double *xyzS, double *xyzC) const;
@@ -57,9 +59,12 @@ public:
 
 private:
   struct metaInfoType {
-    int nbVert;
+    int dim, nbVert;
     fullMatrix<double> points;
-    std::vector<int> baseInd, topInd, edgeInd, otherInd;
+    fullMatrix<double> baseLinShapeFuncVal, baseGradShapeFuncVal;
+    const nodalBasis *baseFaceBasis, *linBaseFaceBasis;
+    std::vector<int> baseInd, topInd, freeTopInd, edgeInd, otherInd;
+    std::vector<int> freeTop2Base, other2Base, other2Top;
     metaInfoType(int type, int order);
   };
   static std::map<int, metaInfoType> _metaInfo;
@@ -67,8 +72,13 @@ private:
   const metaInfoType &_mInfo;
   std::vector<MVertex*> _metaVert;
   MElement *_metaEl, *_metaEl0;
+  std::vector<SVector3> _baseNorm;
 
   const metaInfoType &getMetaInfo(int elType, int order);
+  void computeBaseNorm(const SVector3 &metaNorm,
+                       const std::vector<MVertex*> &baseVert,
+                       const std::vector<MVertex*> &topPrimVert,
+                       std::vector<SVector3> &baseNorm);
 };
 
 #endif  // _METAEL_H_
diff --git a/contrib/HighOrderMeshOptimizer/OptHomFastCurving.cpp b/contrib/HighOrderMeshOptimizer/OptHomFastCurving.cpp
index 536c15fc8435b15e3f1fc18d086e93886db4e5f6..76da75f9c8faf5cf52f7e534888bb187964a165d 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomFastCurving.cpp
+++ b/contrib/HighOrderMeshOptimizer/OptHomFastCurving.cpp
@@ -49,6 +49,7 @@
 #include "BasisFactory.h"
 #include "MetaEl.h"
 #include "qualityMeasuresJacobian.h"
+#include "CADDistances.h"
 
 
 
@@ -673,11 +674,197 @@ void curveElement(const MetaEl &metaElt, std::set<MVertex*> &movedVert,
 
 
 
+double calcCADDistSq2D(GEntity *geomEnt, MElement *bndElt)
+{
+  const int nbVert = bndElt->getNumVertices();
+  const int nbPrimVert = bndElt->getNumPrimaryVertices();
+  const GradientBasis *gb = BasisFactory::getGradientBasis(FuncSpaceData(bndElt));
+
+  // Coordinates of nodes
+  fullMatrix<double> nodesXYZ(nbVert, 3);
+  for (int iV = 0; iV < nbVert; iV++) {
+    MVertex *vert = bndElt->getVertex(iV);
+    nodesXYZ(iV, 0) = vert->x();
+    nodesXYZ(iV, 1) = vert->y();
+    nodesXYZ(iV, 2) = vert->z();
+  }
+
+  // Compute distance
+  const GEdge *ge = geomEnt->cast2Edge();
+  const Range<double> parBounds = ge->parBounds(0);
+  std::vector<SVector3> tanCAD(nbVert);
+  for (int iV = 0; iV < nbVert; iV++) {
+    MVertex *vert = bndElt->getVertex(iV);
+    double tCAD;
+    if (vert->onWhat() == geomEnt)                                                       // If HO vertex, ...
+      vert->getParameter(0, tCAD);                                                       // ... get stored param. coord. (can be only line).
+    else {                                                                               // Otherwise, get param. coord. from CAD.
+      if (ge->getBeginVertex() &&
+          (ge->getBeginVertex()->mesh_vertices[0] == vert))
+        tCAD = parBounds.low();
+      else if (ge->getEndVertex() &&
+               (ge->getEndVertex()->mesh_vertices[0] == vert))
+        tCAD = parBounds.high();
+      else
+        tCAD = ge->parFromPoint(vert->point());
+    }
+    tanCAD[iV] = ge->firstDer(tCAD);                                                     // Compute tangent at vertex
+    tanCAD[iV].normalize();                                                              // Normalize tangent
+  }
+  return taylorDistanceSq1D(gb, nodesXYZ, tanCAD);
+}
+
+
+
+void optimizeCADDist2DP2(GEntity *geomEnt, std::vector<MVertex*> &baseVert)
+{
+  static const int NPTS = 1000;
+  MLine3 bndMetaElt(baseVert);
+  MVertex* &vert = baseVert[2];
+  const double xS = vert->x(), yS = vert->y();
+  const GEdge *ge = geomEnt->cast2Edge();
+  const Range<double> parBounds = ge->parBounds(0);
+  const double du = (parBounds.high()-parBounds.low())/double(NPTS-1);
+  double uMin = 0., distSqMin = 1e300;
+  double uDbg;
+  vert->getParameter(0, uDbg);
+  for (double u = parBounds.low(); u < parBounds.high(); u += du) {
+    vert->setParameter(0, u);
+    GPoint gp = ge->point(u);
+    vert->setXYZ(gp.x(), gp.y(), gp.z());
+    const double distSq = calcCADDistSq2D(geomEnt, &bndMetaElt);
+    if (distSq < distSqMin) { uMin = u; distSqMin = distSq; }
+  }
+  vert->setParameter(0, uMin);
+  GPoint gp = ge->point(uMin);
+  vert->setXYZ(gp.x(), gp.y(), gp.z());
+}
+
+
+
+// void calcCADDistSqAndGradients(int dim, GEntity *geomEnt, MElement *bndElt,
+//                                double &dist, std::vector<double> &gradDist)
+// {
+//   const int nbVert = bndElt->getNumVertices();
+//   const int nbPrimVert = bndElt->getNumPrimaryVertices();
+//   const GradientBasis *gb = BasisFactory::getGradientBasis(FuncSpaceData(bndElt));
+
+//   // Coordinates of nodes
+//   fullMatrix<double> nodesXYZ(nbVert, 3);
+//   for (int iV = 0; iV < nbVert; iV++) {
+//     MVertex *vert = bndElt->getVertex(iV);
+//     nodesXYZ(iV, 0) = vert->x();
+//     nodesXYZ(iV, 1) = vert->y();
+//     nodesXYZ(iV, 2) = vert->z();
+//   }
+
+//   // Compute distance and gradients
+//   if (dim == 2) {                                                                        // 2D
+//     const GEdge *ge = geomEnt->cast2Edge();
+//     const Range<double> parBounds = ge->parBounds(0);
+//     const double eps = 1.e-6 * (parBounds.high()-parBounds.low());
+//     std::vector<SVector3> tanCAD(nbVert);
+//     for (int iV = 0; iV < nbVert; iV++) {
+//       MVertex *vert = bndElt->getVertex(iV);
+//       double tCAD;
+//       if (iV >= nbPrimVert)                                                                // If HO vertex, ...
+//         vert->getParameter(0, tCAD);                                                       // ... get stored param. coord. (can be only line).
+//       else {                                                                               // Otherwise, get param. coord. from CAD.
+//         if (ge->getBeginVertex() &&
+//             (ge->getBeginVertex()->mesh_vertices[0] == vert))
+//           tCAD = parBounds.low();
+//         else if (ge->getEndVertex() &&
+//                  (ge->getEndVertex()->mesh_vertices[0] == vert))
+//           tCAD = parBounds.high();
+//         else
+//           tCAD = ge->parFromPoint(vert->point());
+//       }
+//       tanCAD[iV] = ge->firstDer(tCAD);                                                     // Compute tangent at vertex
+//       tanCAD[iV].normalize();                                                              // Normalize tangent
+//     }
+//     dist = taylorDistanceSq1D(gb, nodesXYZ, tanCAD);
+//     for (int iV = nbPrimVert; iV < nbVert; iV++) {
+//       const double xS = nodesXYZ(iV, 0), yS = nodesXYZ(iV, 1),
+//                    zS = nodesXYZ(iV, 2);                                                  // Save coord. of perturbed node for FD
+//       const SVector3 tanCADS = tanCAD[iV];                                                // Save tangent to CAD at perturbed node
+//       double tCAD;
+//       bndElt->getVertex(iV)->getParameter(0, tCAD);
+//       tCAD += eps;                                                                        // New param. coord. of perturbed node
+//       GPoint gp = ge->point(tCAD);                                                        // New coord. of perturbed node
+//       nodesXYZ(iV, 0) = gp.x();
+//       nodesXYZ(iV, 1) = gp.y();
+//       nodesXYZ(iV, 2) = gp.z();
+//       tanCAD[iV] = ge->firstDer(tCAD);                                                    // New tangent to CAD at perturbed node
+//       tanCAD[iV].normalize();                                                             // Normalize new tangent
+//       const double sDistDiff = taylorDistanceSq1D(gb, nodesXYZ, tanCAD);                  // Compute distance with perturbed node
+//       gradDist[iV-nbPrimVert] = (sDistDiff-dist) / eps;                       // Compute gradient
+//       nodesXYZ(iV, 0) = xS; nodesXYZ(iV, 1) = yS; nodesXYZ(iV, 2) = zS;                   // Restore coord. of perturbed node
+//       tanCAD[iV] = tanCADS;                                                               // Restore tan. to CAD at perturbed node
+//     }
+//   }
+//   else {                                                                                  // 3D
+//     const GFace *gf = geomEnt->cast2Face();
+//     const Range<double> parBounds0 = gf->parBounds(0), parBounds1 = gf->parBounds(1);
+//     const double eps0 = 1.e-6 * (parBounds0.high()-parBounds0.low());
+//     const double eps1 = 1.e-6 * (parBounds1.high()-parBounds1.low());
+//     std::vector<SVector3> normCAD(nbVert);
+//     for (int iV = 0; iV < nbVert; iV++) {
+//       MVertex *vert = bndElt->getVertex(iV);
+//       SPoint2 pCAD;
+//       if (iV >= nbPrimVert) {                                                               // If HO vertex, get parameters
+//         vert->getParameter(0, pCAD[0]);
+//         vert->getParameter(1, pCAD[1]);
+//       }
+//       else
+//         reparamMeshVertexOnFace(vert, gf, pCAD);                                          // If not free vertex, reparametrize on surface
+//       normCAD[iV] = gf->normal(pCAD);                                                     // Compute normal at vertex
+//       normCAD[iV].normalize();                                                            // Normalize normal
+//     }
+//     dist = taylorDistanceSq2D(gb, nodesXYZ, normCAD);
+//     for (int iV = nbPrimVert; iV < nbVert; iV++) {
+//       MVertex *vert = bndElt->getVertex(iV);
+//       const double xS = nodesXYZ(iV, 0), yS = nodesXYZ(iV, 1),
+//                    zS = nodesXYZ(iV, 2);                                                  // Save coord. of perturbed node for FD
+//       const SVector3 normCADS = normCAD[iV];                                              // Save normal to CAD at perturbed node
+//       SPoint2 pCAD0;                                                                      // New param. coord. of perturbed node in 1st dir.
+//       vert->getParameter(0, pCAD0[0]);
+//       pCAD0 += eps0;
+//       vert->getParameter(1, pCAD0[1]);
+//       GPoint gp0 = gf->point(pCAD0);                                                    // New coord. of perturbed node in 1st dir.
+//       nodesXYZ(iV, 0) = gp0.x();
+//       nodesXYZ(iV, 1) = gp0.y();
+//       nodesXYZ(iV, 2) = gp0.z();
+//       normCAD[iV] = gf->normal(pCAD0);                                                  // New normal to CAD at perturbed node in 1st dir.
+//       normCAD[iV].normalize();                                                          // Normalize new normal
+//       const double sDistDiff0 = taylorDistanceSq2D(gb, nodesXYZ, normCAD);              // Compute distance with perturbed node in 1st dir.
+//       gradDist[2*(iV-nbPrimVert)] = (sDistDiff0-dist) / eps0;                           // Compute gradient in 1st dir.
+//       SPoint2 pCAD1;                                                                    // New param. coord. of perturbed node in 2nd dir.
+//       vert->getParameter(0, pCAD1[0]);
+//       vert->getParameter(1, pCAD1[1]);
+//       pCAD1 += eps1;
+//       GPoint gp1 = gf->point(pCAD1);                                                    // New coord. of perturbed node in 2nd dir.
+//       nodesXYZ(iV, 0) = gp1.x();
+//       nodesXYZ(iV, 1) = gp1.y();
+//       nodesXYZ(iV, 2) = gp1.z();
+//       normCAD[iV] = gf->normal(pCAD1);                                                   // New normal to CAD at perturbed node in 2nd dir.
+//       normCAD[iV].normalize();                                                           // Normalize new normal
+//       double sDistDiff1 = taylorDistanceSq2D(gb, nodesXYZ, normCAD);                     // Compute distance with perturbed node in 2nd dir.
+//       gradDist[2*(iV-nbPrimVert)+1] = (sDistDiff1-dist) / eps1;                          // Compute gradient in 2nd dir.
+//       nodesXYZ(iV, 0) = xS;                                                              // Restore coord. of perturbed node
+//       nodesXYZ(iV, 1) = yS;
+//       nodesXYZ(iV, 2) = zS;
+//       normCAD[iV] = normCADS;                                                            // Restore tan. to CAD at perturbed node
+//     }
+//   }
+// }
+
+
+
 double curveAndMeasureAboveEl(MetaEl &metaElt, MElement *lastElt,
                               MElement *aboveElt, double deformFact)
 {
   double minJacDet, maxJacDet;
-  metaElt.curveTop(deformFact);
+  metaElt.setCurvedTop(deformFact);
   std::set<MVertex*> movedVertDum;
   curveElement(metaElt, movedVertDum, lastElt);
   jacobianBasedQuality::minMaxJacobianDeterminant(aboveElt, minJacDet, maxJacDet);
@@ -686,44 +873,53 @@ double curveAndMeasureAboveEl(MetaEl &metaElt, MElement *lastElt,
 
 
 
-void curveColumn(int metaElType, const std::vector<MVertex*> &baseVert,
+void curveColumn(GEntity *geomEnt, int metaElType,
+                 std::vector<MVertex*> &baseVert,
                  const std::vector<MVertex*> &topPrimVert, MElement *aboveElt,
                  std::vector<MElement*> &blob, std::set<MVertex*> &movedVert,
                  DbgOutputMeta &dbgOut)
 {
-  static const double MINQUAL = 0.1, TOL = 0.01, MAXITER = 10;
+  static const double MINQUAL = 0.01, TOL = 0.01, MAXITER = 10;
+
+  // Order
+  const int order = blob[0]->getPolynomialOrder();
+
+  // If 2D P2, modify base vertices to minimize distance between wall edge and CAD
+  if ((metaElType == TYPE_QUA) &&
+      (blob[0]->getPolynomialOrder() == 2)) {
+    optimizeCADDist2DP2(geomEnt, baseVert);
+  }
 
   // Create meta-element
-  int order = blob[0]->getPolynomialOrder();
   MetaEl metaElt(metaElType, order, baseVert, topPrimVert);
   
   // Curve top face of meta-element while avoiding breaking the element above
-  MElement* &lastElt = blob.back();
-  // std::cout << "DBGTT: lastElt = " << lastElt->getNum() << ", aboveElt " << aboveElt->getNum() << " min. SJ: " << aboveElt->minScaledJacobian();
-  double minJacDet, maxJacDet;
-  double deformMin = 0., qualDeformMin = 1.;
-  double deformMax = 1.;
-  double qualDeformMax = curveAndMeasureAboveEl(metaElt, lastElt, aboveElt,
-                                                deformMax);
-  if (qualDeformMax < MINQUAL) {                                                // Max deformation makes element above invalid
-    for (int iter = 0; iter < MAXITER; iter++) {                                // Bisection to find max. deformation that makes element above valid
-      const double deformMid = 0.5 * (deformMin + deformMax);
-      const double qualDeformMid = curveAndMeasureAboveEl(metaElt, lastElt,
-                                                          aboveElt, deformMid);
-      if (std::abs(deformMax-deformMin) < TOL) break;
-      const bool signDeformMax = (qualDeformMax < MINQUAL);
-      const bool signDeformMid = (qualDeformMid < MINQUAL);
-      if (signDeformMid == signDeformMax) deformMax = deformMid;
-      else deformMin = deformMid;
-    }
-  }
-  for (int iV = 0; iV < lastElt->getNumVertices(); iV++)
-    movedVert.insert(lastElt->getVertex(iV));
-  // std::cout << " -> " << aboveElt->minScaledJacobian() << "\n";
+  // MElement* &lastElt = blob.back();
+  // double minJacDet, maxJacDet;
+  // double deformMin = 0., qualDeformMin = 1.;
+  // double deformMax = 1.;
+  // double qualDeformMax = curveAndMeasureAboveEl(metaElt, lastElt, aboveElt,
+  //                                               deformMax);
+  // if (qualDeformMax < MINQUAL) {                                                // Max deformation makes element above invalid
+  //   for (int iter = 0; iter < MAXITER; iter++) {                                // Bisection to find max. deformation that makes element above valid
+  //     const double deformMid = 0.5 * (deformMin + deformMax);
+  //     const double qualDeformMid = curveAndMeasureAboveEl(metaElt, lastElt,
+  //                                                         aboveElt, deformMid);
+  //     if (std::abs(deformMax-deformMin) < TOL) break;
+  //     const bool signDeformMax = (qualDeformMax < MINQUAL);
+  //     const bool signDeformMid = (qualDeformMid < MINQUAL);
+  //     if (signDeformMid == signDeformMax) deformMax = deformMid;
+  //     else deformMin = deformMid;
+  //   }
+  //   metaElt.setFlatTop();
+  // }
+  // for (int iV = 0; iV < lastElt->getNumVertices(); iV++)
+  //   movedVert.insert(lastElt->getVertex(iV));
 
   dbgOut.addMetaEl(metaElt);
 
-  for (int iEl = 0; iEl < blob.size()-1; iEl++)
+  for (int iEl = 0; iEl < blob.size(); iEl++)
+  // for (int iEl = 0; iEl < blob.size()-1; iEl++)
     curveElement(metaElt, movedVert, blob[iEl]);
 }
 
@@ -754,7 +950,7 @@ void curveMeshFromBnd(MEdgeVecMEltMap &ed2el, MFaceVecMEltMap &face2el,
 
   std::set<MVertex*> movedVert;
   for(std::list<MElement*>::iterator itBE = bndEl.begin();
-      itBE != bndEl.end(); itBE++) {   // Loop over bnd. elements
+      itBE != bndEl.end(); itBE++) {                                            // Loop over bnd. elements
     const int bndType = (*itBE)->getType();
     int metaElType;
     bool foundCol;
@@ -790,8 +986,8 @@ void curveMeshFromBnd(MEdgeVecMEltMap &ed2el, MFaceVecMEltMap &face2el,
     DbgOutputCol dbgOutCol;
     dbgOutCol.addBlob(blob);
     dbgOutCol.write("col_KO", (*itBE)->getNum());
-    curveColumn(metaElType, baseVert, topPrimVert, aboveElt, blob, movedVert,
-                dbgOut);
+    curveColumn(bndEnt, metaElType, baseVert, topPrimVert, aboveElt, blob,
+                movedVert, dbgOut);
     dbgOutCol.write("col_OK", (*itBE)->getNum());
   }