diff --git a/contrib/HighOrderMeshOptimizer/OptHomFastCurving.cpp b/contrib/HighOrderMeshOptimizer/OptHomFastCurving.cpp
index a758a22276eaeb9846ef1a8e57dca4004f80afe1..ea07f088b60f3c83f58f5549b86228c5c746f776 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomFastCurving.cpp
+++ b/contrib/HighOrderMeshOptimizer/OptHomFastCurving.cpp
@@ -173,7 +173,7 @@ SVector3 getNormalEdge(MVertex *vert, const SVector3 &n,
 
 // Detect whether edge/face is curved, and give normal
 bool isCurvedAndNormal(int type, int order, const std::vector<MVertex*> &faceVert,
-                       const SPoint3 pBar, SVector3 &normal, double &maxDist) {
+                       SVector3 &normal, double &maxDist) {
 
 //  static const double eps = 1.e-10;
   static const double eps = 1.e-6;
@@ -192,24 +192,22 @@ bool isCurvedAndNormal(int type, int order, const std::vector<MVertex*> &faceVer
       sxyz[i] += sxyz[j] * f[j];
   }
 
-  // Compute (non-oriented) unit normal to straight edge/face and its scale [length]
+  // Compute unit normal to straight edge/face and its scale [length]
   double scale;
   const SPoint3 &p0 = sxyz[0], &p1 = sxyz[1];
   if (type == TYPE_LIN) {
-    normal = SVector3(p0.y()-p1.y(),p1.x()-p0.x(),0.);
+//    normal = SVector3(p0.y()-p1.y(),p1.x()-p0.x(),0.);
+    normal = SVector3(p1.y()-p0.y(),p0.x()-p1.x(),0.);
     scale = normal.normalize();
   }
   else {
     const SPoint3 &p2 = sxyz[2];
     SVector3 p01(p0,p1), p02(p0,p2);
-    normal = crossprod(p01,p02);
+//    normal = crossprod(p01,p02);
+    normal = crossprod(p02,p01);
     scale = sqrt(normal.normalize());
   }
 
-  // Orient normal with help of pBar
-  SVector3 p0C(p0,pBar);
-  if (dot(normal,p0C) < 0.) normal *= -1.;                                      // Make straight normal in-going
-
   // Calc max. normal dist. from straight to HO points
   maxDist = 0.;
   for (int iV = nV1; iV < nV; iV++) {
@@ -226,67 +224,6 @@ bool isCurvedAndNormal(int type, int order, const std::vector<MVertex*> &faceVer
 
 
 
-bool getCurvedFace(MElement* badEl,
-                   const std::map<MVertex*, std::vector<MElement*> > &vertex2elements,
-                   std::vector<MVertex*> &baseVert, std::vector<SVector3> &normals,
-                   int &baseType, double &baseMaxDist) {
-
-  bool found = false;
-  const int dim = badEl->getDim();
-  const int order = badEl->getPolynomialOrder();
-  const int numFaces = (dim == 2) ? badEl->getNumEdges() : badEl->getNumFaces();
-
-  for (int iFace=0; iFace<numFaces; iFace++) {            // Loop on edges/faces
-
-    // Get face info (type, vertices)
-    std::vector<MVertex*> bv;                             // Vertices of edge/face
-    int numPrimVert, type;
-    if (dim == 2) {
-      badEl->getEdgeVertices(iFace,bv);
-      type = TYPE_LIN;
-      numPrimVert = 2;
-    }
-    else {
-      badEl->getFaceVertices(iFace,bv);
-      MFace face = badEl->getFace(iFace);
-      numPrimVert = face.getNumVertices();
-      type = (numPrimVert == 3) ? TYPE_TRI : TYPE_QUA;
-    }
-
-    // Skip face if at least 1 vertex not on boundary
-    bool inDom = false;
-    for (int iV=0; iV<bv.size(); ++iV)
-      if (bv[iV]->onWhat()->dim() == dim) inDom = true;
-    if (inDom) continue;
-
-    //    std::cout << "DBGTT: Checking edge/face " << iFace << " in el. " << badEl->getNum() << ": "
-//              << numPrimVert << " vert., type " << type << "\n";
-    // If face curved, mark it and compute normals to its primary vertices
-    SVector3 n;                                                           // Normal to straight edge/face
-    double maxDist;                                                       // TOFIX: Max. normal. dist. to straight in curved face
-    const SPoint3 pBar = badEl->barycenter(true);                         // Bary. of el. to help orienting normal
-    if (isCurvedAndNormal(type,order,bv,pBar,n,maxDist)) {                // Check whether edge/face is curved and compute straight normal
-//      std::cout << "DBGTT: Curved edge/face in el. " << badEl->getNum() << "\n";
-      if (found) {                                                        // If more than 1 curved edge/face in bad el., drop it
-        std::cout << "DBGTT: More than 1 curved edge/face detected in el. " << badEl->getNum() << "\n";
-        return false;
-      }
-      for (int iV=0; iV<numPrimVert; iV++)                                // Compute normals to prim. vert. of edge/face
-        normals.push_back(getNormalEdge(bv[iV],n,vertex2elements));
-      baseVert = bv;
-      baseType = type;
-      baseMaxDist = maxDist;
-      found = true;
-    }
-
-  }
-
-  return found;
-
-}
-
-
-
 void makeStraight(MElement *el, const std::set<MVertex*> &movedVert) {
 
   const nodalBasis *nb = el->getFunctionSpace();
@@ -343,27 +280,34 @@ std::set<MElement*> getSuperElBlob(MElement *el, const std::map<MVertex*,
 
 
 
-void curveMesh(std::map<MVertex*, std::vector<MElement *> > &vertex2elements,
-               std::set<MElement*> &badElements, FastCurvingParameters &p, int samples)
+void curveMeshFromFaces(std::map<MVertex*, std::vector<MElement *> > &vertex2elements,
+                        std::set<MElement*> &faceElements, FastCurvingParameters &p)
 {
 
-  const int nbBadElts = badElements.size();
-  std::vector<MElement*> badElts;
+  const int nbFaceElts = faceElements.size();
+  std::vector<MElement*> faceElts;
   std::vector<SuperEl*> superElts;
-  badElts.reserve(nbBadElts);
-  superElts.reserve(nbBadElts);
+  faceElts.reserve(nbFaceElts);
+  superElts.reserve(nbFaceElts);
 
   std::ofstream of("dum.pos");
   of << "View \" \"{\n";
 
-  for (std::set<MElement*>::const_iterator itBE = badElements.begin(); itBE != badElements.end(); ++itBE) {
-    std::vector<MVertex*> faceBE;
-    std::vector<SVector3> normalsBE;
-    int typeBE;
-    double maxDistBE;
-    if (getCurvedFace(*itBE,vertex2elements,faceBE,normalsBE,typeBE,maxDistBE)) {
-      badElts.push_back(*itBE);
-      superElts.push_back(new SuperEl(*itBE,maxDistBE*p.distanceFactor,typeBE,faceBE,normalsBE));
+  for (std::set<MElement*>::const_iterator itFE = faceElements.begin(); itFE != faceElements.end(); ++itFE) {
+    const int dim = (*itFE)->getDim();
+    const int order = (*itFE)->getPolynomialOrder();
+    const int numPrimVert = (*itFE)->getNumPrimaryVertices();
+    const int type = (*itFE)->getType();
+    std::vector<MVertex*> faceVert;
+    (*itFE)->getVertices(faceVert);
+    double maxDist;
+    SVector3 faceNormal;
+    if (isCurvedAndNormal(type,order,faceVert,faceNormal,maxDist)) {
+      std::vector<SVector3> baseNormal;
+      for (int iV=0; iV<numPrimVert; iV++)                                // Compute normals to prim. vert. of edge/face
+        baseNormal.push_back(getNormalEdge(faceVert[iV],faceNormal,vertex2elements));
+      faceElts.push_back(*itFE);
+      superElts.push_back(new SuperEl(order,maxDist*p.distanceFactor,type,faceVert,baseNormal));
       of << superElts.back()->printPOS();
     }
   }
@@ -372,12 +316,12 @@ void curveMesh(std::map<MVertex*, std::vector<MElement *> > &vertex2elements,
   of.close();
 
   std::set<MVertex*> movedVert;
-  for (int iBE=0; iBE<badElts.size(); ++iBE) {
-    std::set<MElement*> blob = getSuperElBlob(badElts[iBE], vertex2elements, superElts[iBE]);
-//    std::cout << "DBGTT: Blob of bad el. " << badElts[iBE]->getNum() << " contains elts.";
+  for (int iFE=0; iFE<faceElts.size(); ++iFE) {
+    std::set<MElement*> blob = getSuperElBlob(faceElts[iFE], vertex2elements, superElts[iFE]);
+//    std::cout << "DBGTT: Blob of bad el. " << faceElts[iBE]->getNum() << " contains elts.";
 //    for (std::set<MElement*>::iterator itE = blob.begin(); itE != blob.end(); ++itE) std::cout << " " << (*itE)->getNum();
 //    std::cout << "\n";
-    makeStraight(badElts[iBE],movedVert);                                             // Make bad. el. straight
+//    makeStraight(faceElts[iFE],movedVert);                                             // Make bad. el. straight
     for (std::set<MElement*>::iterator itE = blob.begin(); itE != blob.end(); ++itE) {
       makeStraight(*itE,movedVert);
       for (int i = 0; i < (*itE)->getNumVertices(); ++i) {                            // For each vert. of each el. in blob
@@ -385,14 +329,14 @@ void curveMesh(std::map<MVertex*, std::vector<MElement *> > &vertex2elements,
         MVertex* vert = (*itE)->getVertex(i);
         if (movedVert.find(vert) == movedVert.end()) {                                // If vert. not already moved
           double xyzS[3] = {vert->x(), vert->y(), vert->z()}, xyzC[3];
-          if (superElts[iBE]->straightToCurved(xyzS,xyzC)) {
+          if (superElts[iFE]->straightToCurved(xyzS,xyzC)) {
 //            std::cout << "DBGTT: moving vertex " << vert->getNum() << " from (" << xyzS[0] << "," << xyzS[1] << "," << xyzS[2] << ") to (" << xyzC[0] << "," << xyzC[1] << "," << xyzC[2] << ")\n";
             vert->setXYZ(xyzC[0],xyzC[1],xyzC[2]);
             movedVert.insert(vert);
           }
-//          else std::cout << "DBGTT: Failed to move vertex " << vert->getNum() << " with bad. el " << badElts[iBE]->getNum() << "\n";
+//          else std::cout << "DBGTT: Failed to move vertex " << vert->getNum() << " with bad. el " << faceElts[iBE]->getNum() << "\n";
         }
-//        else std::cout << "DBGTT: Already moved vertex " << vert->getNum() << " with bad. el " << badElts[iBE]->getNum() << "\n";
+//        else std::cout << "DBGTT: Already moved vertex " << vert->getNum() << " with bad. el " << faceElts[iBE]->getNum() << "\n";
       }
     }
   }
@@ -410,37 +354,28 @@ void HighOrderMeshFastCurving(GModel *gm, FastCurvingParameters &p)
 
   double t1 = Cpu();
 
-  int samples = 30;
-
-  double tf1 = Cpu();
-
   Msg::StatusBar(true, "Optimizing high order mesh...");
   std::vector<GEntity*> entities;
   gm->getEntities(entities);
+  const int bndDim = p.dim-1;
 
+  // Compute vert. -> elt. connectivity
+  Msg::Info("Computing connectivity...");
   std::map<MVertex*, std::vector<MElement *> > vertex2elements;
-  std::set<MElement*> badasses;
+  for (int iEnt = 0; iEnt < entities.size(); ++iEnt)
+    calcVertex2Elements(p.dim,entities[iEnt],vertex2elements);
+
+  // Loop over geometric entities
   for (int iEnt = 0; iEnt < entities.size(); ++iEnt) {
     GEntity* &entity = entities[iEnt];
-    if (entity->dim() != p.dim || (p.onlyVisible && !entity->getVisibility())) continue;
-    Msg::Info("Computing connectivity and bad elements for entity %d...",
-              entity->tag());
-    calcVertex2Elements(p.dim,entity,vertex2elements); // Compute vert. -> elt. connectivity
-//    std::cout << "DBGTT: num. el. = " << entity->getNumMeshElements() << "\n";
-    for (int iEl = 0; iEl < entity->getNumMeshElements();iEl++) { // Detect bad elements
-      double jmin, jmax;
-      MElement *el = entity->getMeshElement(iEl);
-      if (el->getDim() == p.dim) {
-        el->scaledJacRange(jmin, jmax);
-//        std::cout << "El. " << iEl << ", jmin = " << jmin << ", jmax = " << jmax << "\n";
-//        Msg::Info("El %i, jmin = %g, jmax = %g\n",iEl,jmin,jmax);
-        if (jmin < p.BARRIER_MIN || jmax > p.BARRIER_MAX) badasses.insert(el);
-      }
-    }
+    if (entity->dim() != bndDim || (p.onlyVisible && !entity->getVisibility())) continue;
+    Msg::Info("Curving elements for entity %d...",entity->tag());
+    std::set<MElement*> faceElements;
+    for (int iEl = 0; iEl < entity->getNumMeshElements();iEl++)
+      faceElements.insert(entity->getMeshElement(iEl));
+    curveMeshFromFaces(vertex2elements, faceElements, p);
   }
 
-  curveMesh(vertex2elements, badasses, p, samples);
-
   double t2 = Cpu();
 
   Msg::StatusBar(true, "Done curving high order mesh (%g s)", t2-t1);
diff --git a/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp b/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp
index 4b19087dde1ddadf92570447ad13aca58b5d052d..4b81c780dd9933cc4c4079c19cc478995e5e8d3d 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp
+++ b/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp
@@ -43,16 +43,6 @@ Mesh::Mesh(const std::map<MElement*,GEntity*> &element2entity,
 
   _dim = (*els.begin())->getDim();
 
-  if (fixBndNodes) {
-    if (_dim == 2) _pc = new ParamCoordPhys2D;
-    else _pc = new ParamCoordPhys3D;
-    Msg::Debug("METHOD: Fixing boundary nodes and using physical coordinates");
-  }
-  else {
-    _pc = new ParamCoordParent;
-    Msg::Debug("METHOD: Freeing boundary nodes and using parent parametric coordinates");
-  }
-
   // Initialize elements, vertices, free vertices and element->vertices
   // connectivity
   const int nElements = els.size();
@@ -64,6 +54,7 @@ Mesh::Mesh(const std::map<MElement*,GEntity*> &element2entity,
   _nNodEl.resize(nElements);
   _indPCEl.resize(nElements);
   int iEl = 0;
+  bool nonGeoMove = false;
   for(std::set<MElement*>::const_iterator it = els.begin();
       it != els.end(); ++it, ++iEl) {
     _el[iEl] = *it;
@@ -72,31 +63,36 @@ Mesh::Mesh(const std::map<MElement*,GEntity*> &element2entity,
     _nNodEl[iEl] = jac->getNumMapNodes();
     for (int iVEl = 0; iVEl < jac->getNumMapNodes(); iVEl++) {
       MVertex *vert = _el[iEl]->getVertex(iVEl);
+      GEntity *ge = vert->onWhat();
+      const int vDim = ge->dim();
+      const bool hasParam = ge->haveParametrization();
       int iV = addVert(vert);
       _el2V[iEl].push_back(iV);
-      const int nPCV = _pc->nCoord(vert);
-      bool isFV = false;
-      if (nPCV > 0) {
-        if (fixBndNodes)
-          isFV = (vert->onWhat()->dim() == _dim) && (toFix.find(vert) == toFix.end());
-        else
-          isFV = (vert->onWhat()->dim() >= 1) && (toFix.find(vert) == toFix.end());
-      }
-      if (isFV) {
-        int iFV = addFreeVert(vert,iV,nPCV,toFix);
+      if ((vDim > 0) && (toFix.find(vert) == toFix.end()) && (!fixBndNodes || vDim == _dim)) {   // Free vertex?
+        ParamCoord *param;
+        if (vDim == 3) param = new ParamCoordPhys3D();
+        else if (hasParam) param = new ParamCoordParent(vert);
+        else {
+          if (vDim == 2) param = new ParamCoordPhys2D();                          //todo: make 2d local surf. param
+          else param = new ParamCoordLocalLine(vert);
+          nonGeoMove = true;
+        }
+        int iFV = addFreeVert(vert,iV,vDim,param,toFix);
         _el2FV[iEl].push_back(iFV);
-        for (int i=_startPCFV[iFV]; i<_startPCFV[iFV]+nPCV; i++)
-          _indPCEl[iEl].push_back(i);
+        for (int i=_startPCFV[iFV]; i<_startPCFV[iFV]+vDim; i++) _indPCEl[iEl].push_back(i);
       }
       else _el2FV[iEl].push_back(-1);
     }
   }
 
+  if (nonGeoMove) Msg::Info("WARNING: Some vertices will be moved along local lines "
+                            "or planes, they may not remain on the exact geometry");
+
   // Initial coordinates
   _ixyz.resize(nVert());
   for (int iV = 0; iV < nVert(); iV++) _ixyz[iV] = _vert[iV]->point();
   _iuvw.resize(nFV());
-  for (int iFV = 0; iFV < nFV(); iFV++) _iuvw[iFV] = _pc->getUvw(_freeVert[iFV]);
+  for (int iFV = 0; iFV < nFV(); iFV++) _iuvw[iFV] = _paramFV[iFV]->getUvw(_freeVert[iFV]);
 
   // Set current coordinates
   _xyz = _ixyz;
@@ -167,7 +163,7 @@ int Mesh::addVert(MVertex* vert)
 }
 
 int Mesh::addFreeVert(MVertex* vert, const int iV, const int nPCV,
-                      std::set<MVertex*> &toFix)
+                      ParamCoord *param, std::set<MVertex*> &toFix)
 {
   std::vector<MVertex*>::iterator itVert = find(_freeVert.begin(),
                                                 _freeVert.end(),vert);
@@ -175,6 +171,7 @@ int Mesh::addFreeVert(MVertex* vert, const int iV, const int nPCV,
     const int iStart = (_startPCFV.size() == 0)? 0 : _startPCFV.back()+_nPCFV.back();
     const bool forcedV = (vert->onWhat()->dim() < 2) || (toFix.find(vert) != toFix.end());
     _freeVert.push_back(vert);
+    _paramFV.push_back(param);
     _fv2V.push_back(iV);
     _startPCFV.push_back(iStart);
     _nPCFV.push_back(nPCV);
@@ -205,7 +202,7 @@ void Mesh::updateMesh(const double *it)
     uvwV[0] = *it; it++;
     if (_nPCFV[iFV] >= 2) { uvwV[1] = *it; it++; }
     if (_nPCFV[iFV] == 3) { uvwV[2] = *it; it++; }
-    _xyz[iV] = _pc->uvw2Xyz(_freeVert[iFV],uvwV);
+    _xyz[iV] = _paramFV[iFV]->uvw2Xyz(uvwV);
   }
 
 }
@@ -251,7 +248,7 @@ void Mesh::updateGEntityPositions()
   for (int iV = 0; iV < nVert(); iV++)
     _vert[iV]->setXYZ(_xyz[iV].x(),_xyz[iV].y(),_xyz[iV].z());
   for (int iFV = 0; iFV < nFV(); iFV++)
-    _pc->exportParamCoord(_freeVert[iFV], _uvw[iFV]);
+    _paramFV[iFV]->exportParamCoord(_uvw[iFV]);
 }
 
 void Mesh::metricMinAndGradients(int iEl, std::vector<double> &lambda,
@@ -299,7 +296,7 @@ void Mesh::metricMinAndGradients(int iEl, std::vector<double> &lambda,
         gXyzV [l] = SPoint3(gradLambdaB(l,i+0*numMapNodes),
                             gradLambdaB(l,i+1*numMapNodes),/*BDB(l,i+2*nbNod)*/ 0.);
       }
-      _pc->gXyz2gUvw(_freeVert[iFVi],_uvw[iFVi],gXyzV,gUvwV);
+      _paramFV[iFVi]->gXyz2gUvw(_uvw[iFVi],gXyzV,gUvwV);
       for (int l = 0; l < numJacNodes; l++) {
         gradLambda[indGSJ(iEl,l,iPC)] = gUvwV[l][0];
         if (_nPCFV[iFVi] >= 2) gradLambda[indGSJ(iEl,l,iPC+1)] = gUvwV[l][1];
@@ -354,7 +351,7 @@ void Mesh::scaledJacAndGradients(int iEl, std::vector<double> &sJ,
       for (int l = 0; l < numJacNodes; l++)
         gXyzV [l] = SPoint3(BDB(l,i+0*numMapNodes), BDB(l,i+1*numMapNodes),
                             BDB(l,i+2*numMapNodes));
-      _pc->gXyz2gUvw(_freeVert[iFVi],_uvw[iFVi],gXyzV,gUvwV);
+      _paramFV[iFVi]->gXyz2gUvw(_uvw[iFVi],gXyzV,gUvwV);
       for (int l = 0; l < numJacNodes; l++) {
         gSJ[indGSJ(iEl,l,iPC)] = gUvwV[l][0];
         if (_nPCFV[iFVi] >= 2) gSJ[indGSJ(iEl,l,iPC+1)] = gUvwV[l][1];
@@ -371,9 +368,9 @@ void Mesh::pcScale(int iFV, std::vector<double> &scale)
   // Calc. derivative of x, y & z w.r.t. parametric coordinates
   const SPoint3 dX(1.,0.,0.), dY(0.,1.,0.), dZ(0.,0.,1.);
   SPoint3 gX, gY, gZ;
-  _pc->gXyz2gUvw(_freeVert[iFV],_uvw[iFV],dX,gX);
-  _pc->gXyz2gUvw(_freeVert[iFV],_uvw[iFV],dY,gY);
-  _pc->gXyz2gUvw(_freeVert[iFV],_uvw[iFV],dZ,gZ);
+  _paramFV[iFV]->gXyz2gUvw(_uvw[iFV],dX,gX);
+  _paramFV[iFV]->gXyz2gUvw(_uvw[iFV],dY,gY);
+  _paramFV[iFV]->gXyz2gUvw(_uvw[iFV],dZ,gZ);
 
   // Scale = inverse norm. of vector (dx/du, dy/du, dz/du)
   scale[0] = 1./sqrt(gX[0]*gX[0]+gY[0]*gY[0]+gZ[0]*gZ[0]);
diff --git a/contrib/HighOrderMeshOptimizer/OptHomMesh.h b/contrib/HighOrderMeshOptimizer/OptHomMesh.h
index 481cc1f96e24e07adbe549430964f078eb9aeb41..ea54b5b2cc41668f3e9bc0652ea990fc3df6d9e0 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomMesh.h
+++ b/contrib/HighOrderMeshOptimizer/OptHomMesh.h
@@ -105,12 +105,12 @@ private:
   std::vector<int> _nBezEl, _nNodEl;
   // Index of parametric coord. for an el.
   std::vector<std::vector<int> > _indPCEl;
-
-  ParamCoord *_pc;
+  // Parametrization for a free vertex
+  std::vector<ParamCoord*> _paramFV;
 
   int addVert(MVertex* vert);
   int addFreeVert(MVertex* vert, const int iV, const int nPCV,
-                  std::set<MVertex*> &toFix);
+                  ParamCoord *param, std::set<MVertex*> &toFix);
   void calcScaledNormalEl2D(const std::map<MElement*,GEntity*> &element2entity, int iEl);
   static inline int indJB2DBase(int nNod, int l, int i, int j)
   {
@@ -140,7 +140,7 @@ void Mesh::gradDistSq(int iFV, std::vector<double> &gDSq)
 {
   SPoint3 gXyz = (_xyz[_fv2V[iFV]]-_ixyz[_fv2V[iFV]])*2.;
   SPoint3 gUvw;
-  _pc->gXyz2gUvw(_freeVert[iFV],_uvw[iFV],gXyz,gUvw);
+  _paramFV[iFV]->gXyz2gUvw(_uvw[iFV],gXyz,gUvw);
 
   gDSq[0] = gUvw[0];
   if (_nPCFV[iFV] >= 2) gDSq[1] = gUvw[1];
diff --git a/contrib/HighOrderMeshOptimizer/ParamCoord.cpp b/contrib/HighOrderMeshOptimizer/ParamCoord.cpp
index ff511388a6a03624cc149ae756e6c27ed0e941c5..1361610e1dc03175c3ee71ce2f87b6706671c0f8 100644
--- a/contrib/HighOrderMeshOptimizer/ParamCoord.cpp
+++ b/contrib/HighOrderMeshOptimizer/ParamCoord.cpp
@@ -28,9 +28,11 @@
 // Contributor(s): Thomas Toulorge, Jonathan Lambrechts
 
 #include <iostream>
+#include <algorithm>
 #include "GFace.h"
 #include "GEdge.h"
 #include "MVertex.h"
+#include "MLine.h"
 #include "ParamCoord.h"
 #include "GmshMessage.h"
 
@@ -44,81 +46,53 @@ SPoint3 ParamCoordParent::getUvw(MVertex* vert)
   switch (ge->dim()) {
   case 1: {
     SPoint3 p(0.,0.,0.);
-    reparamMeshVertexOnEdge(vert,static_cast<GEdge*>(ge),p[0]);
+    reparamMeshVertexOnEdge(vert,static_cast<GEdge*>(ge),p[0]);     // Overkill if vert. well classified and parametrized
     return p;
     break;
   }
   case 2: {
     SPoint2 p;
-    reparamMeshVertexOnFace(vert,static_cast<GFace*>(ge),p);
+    reparamMeshVertexOnFace(vert,static_cast<GFace*>(ge),p);      // Overkill if vert. well classified and parametrized
     return SPoint3(p[0],p[1],0.);
     break;
   }
-  case 3: {
-    return vert->point();
-    break;
-  }
   }
 }
 
-SPoint3 ParamCoordParent::uvw2Xyz(MVertex* vert, const SPoint3 &uvw)
+SPoint3 ParamCoordParent::uvw2Xyz(const SPoint3 &uvw)
 {
-  GEntity *ge = vert->onWhat();
-
-  switch (ge->dim()) {
-  case 1: {
-    GPoint gp = static_cast<GEdge*>(ge)->point(uvw[0]);
-    return SPoint3(gp.x(),gp.y(),gp.z());
-    break;
-  }
-  case 2: {
-    GPoint gp = static_cast<GFace*>(ge)->point(uvw[0],uvw[1]);
-    return SPoint3(gp.x(),gp.y(),gp.z());
-    break;
-  }
-  case 3: {
-    return uvw;
-    break;
-  }
-  }
+  GEntity *ge = _vert->onWhat();
+  GPoint gp = (ge->dim() == 1) ? static_cast<GEdge*>(ge)->point(uvw[0]) :
+                                 static_cast<GFace*>(ge)->point(uvw[0],uvw[1]);
+  return SPoint3(gp.x(),gp.y(),gp.z());
 }
 
-void ParamCoordParent::gXyz2gUvw(MVertex* vert, const SPoint3 &uvw,
-                                 const SPoint3 &gXyz, SPoint3 &gUvw)
+void ParamCoordParent::gXyz2gUvw(const SPoint3 &uvw, const SPoint3 &gXyz, SPoint3 &gUvw)
 {
-  GEntity *ge = vert->onWhat();
 
-  switch (ge->dim()) {
-  case 1: {
+  GEntity *ge = _vert->onWhat();
+
+  if (ge->dim() == 1) {
     SVector3 der = static_cast<GEdge*>(ge)->firstDer(uvw[0]);
     gUvw[0] = gXyz.x() * der.x() + gXyz.y() * der.y() + gXyz.z() * der.z();
-    break;
   }
-  case 2: {
+  else {
     Pair<SVector3,SVector3> der = static_cast<GFace*>(ge)->firstDer(SPoint2(uvw[0],uvw[1]));
     gUvw[0] = gXyz.x() * der.first().x() + gXyz.y() * der.first().y() +
       gXyz.z() * der.first().z();
     gUvw[1] = gXyz.x() * der.second().x() + gXyz.y() * der.second().y() +
       gXyz.z() * der.second().z();
-    break;
-  }
-  case 3: {
-    gUvw = gXyz;
-    break;
-  }
   }
 
 }
 
-void ParamCoordParent::gXyz2gUvw(MVertex* vert, const SPoint3 &uvw,
-                                 const std::vector<SPoint3> &gXyz,
-                                 std::vector<SPoint3> &gUvw)
+void ParamCoordParent::gXyz2gUvw(const SPoint3 &uvw,
+                                 const std::vector<SPoint3> &gXyz, std::vector<SPoint3> &gUvw)
 {
 
-  GEntity *ge = vert->onWhat();
+  GEntity *ge = _vert->onWhat();
 
-  switch (ge->dim()) {
-  case 1: {
+  if (ge->dim() == 1) {
     SVector3 der = static_cast<GEdge*>(ge)->firstDer(uvw[0]);
     std::vector<SPoint3>::iterator itUvw = gUvw.begin();
     for (std::vector<SPoint3>::const_iterator itXyz=gXyz.begin();
@@ -126,9 +100,8 @@ void ParamCoordParent::gXyz2gUvw(MVertex* vert, const SPoint3 &uvw,
       (*itUvw)[0] = itXyz->x() * der.x() + itXyz->y() * der.y() + itXyz->z() * der.z();
       itUvw++;
     }
-    break;
   }
-  case 2: {
+  else {
     Pair<SVector3,SVector3> der = static_cast<GFace*>(ge)->firstDer
       (SPoint2(uvw[0],uvw[1]));
     std::vector<SPoint3>::iterator itUvw=gUvw.begin();
@@ -140,24 +113,25 @@ void ParamCoordParent::gXyz2gUvw(MVertex* vert, const SPoint3 &uvw,
         itXyz->z() * der.second().z();
       itUvw++;
     }
-    break;
-  }
-  case 3: {
-    std::vector<SPoint3>::iterator itUvw=gUvw.begin();
-    for (std::vector<SPoint3>::const_iterator itXyz=gXyz.begin();
-         itXyz != gXyz.end(); itXyz++) {
-      *itUvw = *itXyz;
-      itUvw++;
-    }
-    break;
-  }
   }
 
 }
 
-void ParamCoordParent::exportParamCoord(MVertex *v, const SPoint3 &uvw)
+ParamCoordLocalLine::ParamCoordLocalLine(MVertex* v) : dir(0.)
 {
-  for (int d = 0; d < v->onWhat()->dim(); ++d) {
-    v->setParameter(d, uvw[d]);
+
+  GEdge *ge = static_cast<GEdge*>(v->onWhat());
+
+  for (std::vector<MLine*>::iterator it = ge->lines.begin(); it != ge->lines.end(); it++) {
+    std::vector<MVertex*> lVerts;
+    (*it)->getVertices(lVerts);
+    if (std::find(lVerts.begin(),lVerts.end(),v) == lVerts.end()) {
+      SVector3 tan(lVerts[0]->point(),lVerts[1]->point());
+      tan.normalize();
+      dir += tan;
+    }
   }
+
+  dir.normalize();
+
 }
diff --git a/contrib/HighOrderMeshOptimizer/ParamCoord.h b/contrib/HighOrderMeshOptimizer/ParamCoord.h
index 9b50e50280a609c0dd36b8cb078074538d32276f..3c509cb83ed47896967bee3ab28465c9a70de036 100644
--- a/contrib/HighOrderMeshOptimizer/ParamCoord.h
+++ b/contrib/HighOrderMeshOptimizer/ParamCoord.h
@@ -33,18 +33,16 @@
 class ParamCoord
 {
 public:
-  virtual void exportParamCoord(MVertex *v, const SPoint3 &uvw) {};
-  // Number of parametric coordinates for vertex
-  virtual int nCoord(MVertex* vert) = 0;
+  // Set param. coord. of MVertex if applicable
+  virtual void exportParamCoord(const SPoint3 &uvw) {}
   // Get parametric coordinates of vertex
-  virtual SPoint3 getUvw(MVertex* vert) = 0;
+  virtual SPoint3 getUvw(MVertex* v) = 0;
   // Calculate physical coordinates from parametric coordinates of vertex
-  virtual SPoint3 uvw2Xyz(MVertex* vert, const SPoint3 &uvw) = 0;
+  virtual SPoint3 uvw2Xyz(const SPoint3 &uvw) = 0;
   // Calculate derivatives w.r.t parametric coordinates
-  virtual void gXyz2gUvw(MVertex* vert, const SPoint3 &uvw,
-                         const SPoint3 &gXyz, SPoint3 &gUvw) = 0;
+  virtual void gXyz2gUvw(const SPoint3 &uvw, const SPoint3 &gXyz, SPoint3 &gUvw) = 0;
   // Calculate derivatives w.r.t parametric coordinates
-  virtual void gXyz2gUvw(MVertex* vert, const SPoint3 &uvw,
+  virtual void gXyz2gUvw(const SPoint3 &uvw,
                          const std::vector<SPoint3> &gXyz, std::vector<SPoint3> &gUvw) = 0;
   virtual ~ParamCoord() {}
 };
@@ -52,13 +50,11 @@ public:
 class ParamCoordPhys3D : public ParamCoord
 {
 public:
-  int nCoord(MVertex* vert) { return 3; }
-  virtual SPoint3 getUvw(MVertex* vert) { return vert->point(); }
-  virtual SPoint3 uvw2Xyz(MVertex* vert, const SPoint3 &uvw) { return uvw; }
-  virtual void gXyz2gUvw(MVertex* vert, const SPoint3 &uvw, const SPoint3 &gXyz,
+  SPoint3 getUvw(MVertex* v) { return v->point(); }
+  SPoint3 uvw2Xyz(const SPoint3 &uvw) { return uvw; }
+  void gXyz2gUvw(const SPoint3 &uvw, const SPoint3 &gXyz,
                          SPoint3 &gUvw){ gUvw = gXyz; }
-  virtual void gXyz2gUvw(MVertex* vert, const SPoint3 &uvw,
-                         const std::vector<SPoint3> &gXyz, std::vector<SPoint3> &gUvw)
+  void gXyz2gUvw(const SPoint3 &uvw, const std::vector<SPoint3> &gXyz, std::vector<SPoint3> &gUvw)
   {
     std::vector<SPoint3>::iterator itUvw=gUvw.begin();
     for (std::vector<SPoint3>::const_iterator itXyz=gXyz.begin(); itXyz != gXyz.end();
@@ -72,13 +68,10 @@ public:
 class ParamCoordPhys2D : public ParamCoord
 {
 public:
-  int nCoord(MVertex* vert) { return 2; }
-  virtual SPoint3 getUvw(MVertex* vert) { return vert->point(); }
-  virtual SPoint3 uvw2Xyz(MVertex* vert, const SPoint3 &uvw) { return uvw; }
-  virtual void gXyz2gUvw(MVertex* vert, const SPoint3 &uvw, const SPoint3 &gXyz,
-                         SPoint3 &gUvw) { gUvw = gXyz; }
-  virtual void gXyz2gUvw(MVertex* vert, const SPoint3 &uvw,
-                         const std::vector<SPoint3> &gXyz, std::vector<SPoint3> &gUvw)
+  SPoint3 getUvw(MVertex* v) { return v->point(); }
+  SPoint3 uvw2Xyz(const SPoint3 &uvw) { return uvw; }
+  void gXyz2gUvw(const SPoint3 &uvw, const SPoint3 &gXyz, SPoint3 &gUvw) { gUvw = gXyz; }
+  void gXyz2gUvw(const SPoint3 &uvw, const std::vector<SPoint3> &gXyz, std::vector<SPoint3> &gUvw)
   {
     std::vector<SPoint3>::iterator itUvw=gUvw.begin();
     for (std::vector<SPoint3>::const_iterator itXyz=gXyz.begin(); itXyz != gXyz.end();
@@ -92,14 +85,37 @@ public:
 class ParamCoordParent : public ParamCoord
 {
 public:
-  int nCoord(MVertex* vert) { return vert->onWhat()->haveParametrization() ? vert->onWhat()->dim() : 0; }
-  virtual void exportParamCoord(MVertex *v, const SPoint3 &uvw);
-  virtual SPoint3 getUvw(MVertex* vert);
-  virtual SPoint3 uvw2Xyz(MVertex* vert, const SPoint3 &uvw);
-  virtual void gXyz2gUvw(MVertex* vert, const SPoint3 &uvw, const SPoint3 &gXyz,
-                         SPoint3 &gUvw);
-  virtual void gXyz2gUvw(MVertex* vert, const SPoint3 &uvw,
-                         const std::vector<SPoint3> &gXyz, std::vector<SPoint3> &gUvw);
+  ParamCoordParent(MVertex* v) : _vert(v) {}
+  void exportParamCoord(const SPoint3 &uvw) {
+    for (int d = 0; d < _vert->onWhat()->dim(); ++d) _vert->setParameter(d, uvw[d]);
+  }
+  SPoint3 getUvw(MVertex* v);
+  SPoint3 uvw2Xyz(const SPoint3 &uvw);
+  void gXyz2gUvw(const SPoint3 &uvw, const SPoint3 &gXyz, SPoint3 &gUvw);
+  void gXyz2gUvw(const SPoint3 &uvw, const std::vector<SPoint3> &gXyz, std::vector<SPoint3> &gUvw);
+protected:
+  MVertex *_vert;
+};
+
+class ParamCoordLocalLine : public ParamCoord
+{
+public:
+  ParamCoordLocalLine(MVertex* v);
+  SPoint3 getUvw(MVertex* v) { return SPoint3(0.,0.,0.); }
+  SPoint3 uvw2Xyz(const SPoint3 &uvw) { return SPoint3(uvw[0]*dir[0],uvw[0]*dir[1],uvw[0]*dir[2]); }
+  void gXyz2gUvw(const SPoint3 &uvw, const SPoint3 &gXyz, SPoint3 &gUvw) {
+    gUvw[0] = gXyz.x()*dir[0] + gXyz.y()*dir[1] + gXyz.z()*dir[2];
+  }
+  void gXyz2gUvw(const SPoint3 &uvw, const std::vector<SPoint3> &gXyz, std::vector<SPoint3> &gUvw) {
+    std::vector<SPoint3>::iterator itUvw = gUvw.begin();
+    for (std::vector<SPoint3>::const_iterator itXyz=gXyz.begin();
+         itXyz != gXyz.end(); itXyz++) {
+      (*itUvw)[0] = itXyz->x()*dir[0] + itXyz->y()*dir[1] + itXyz->z()*dir[2];
+      itUvw++;
+    }
+  }
+protected:
+  SVector3 dir;
 };
 
 #endif
diff --git a/contrib/HighOrderMeshOptimizer/SuperEl.cpp b/contrib/HighOrderMeshOptimizer/SuperEl.cpp
index 76a4bece92f38d73df27007a0ef4a5dce07dabc2..d564553c8d3af23efb1939d8303c74936ade0ac4 100644
--- a/contrib/HighOrderMeshOptimizer/SuperEl.cpp
+++ b/contrib/HighOrderMeshOptimizer/SuperEl.cpp
@@ -38,20 +38,20 @@
 
 
 
-SuperEl::SuperEl(MElement *badEl, double dist, int type, const std::vector<MVertex*> &baseVert,
+SuperEl::SuperEl(int order, double dist, int type, const std::vector<MVertex*> &baseVert,
                  const std::vector<SVector3> &normals) {
 
 //  std::cout << "DBGTT: badEl = " << badEl->getNum() << ", _superEl = " << _superEl << std::endl;
 
   switch (type) {
     case TYPE_LIN:
-      createSuperElQuad(badEl, dist, baseVert, normals[0], normals[1]);
+      createSuperElQuad(order, dist, baseVert, normals[0], normals[1]);
       break;
     case TYPE_TRI:
-      createSuperElPrism(badEl, dist, baseVert, normals[0], normals[1], normals[2]);
+      createSuperElPrism(order, dist, baseVert, normals[0], normals[1], normals[2]);
       break;
     case TYPE_QUA:
-      createSuperElHex(badEl, dist, baseVert, normals[0], normals[1], normals[2], normals[3]);
+      createSuperElHex(order, dist, baseVert, normals[0], normals[1], normals[2], normals[3]);
       break;
     default:
       std::cout << "ERROR: SuperEl not implemented for element of type " << type << std::endl;
@@ -65,12 +65,12 @@ SuperEl::SuperEl(MElement *badEl, double dist, int type, const std::vector<MVert
 
 
 
-void SuperEl::createSuperElQuad(MElement *badEl, double dist, const std::vector<MVertex*> &baseVert,
+void SuperEl::createSuperElQuad(int order, double dist, const std::vector<MVertex*> &baseVert,
                                   const SVector3 &n0, const SVector3 &n1) {
 
 
   if (baseVert.empty()) {
-    std::cout << "ERROR: Base edge not given for SuperEl linked to bad. el. " << badEl->getNum() << "\n";
+    std::cout << "ERROR: Base edge not given for SuperEl\n";
     _superEl = 0;
     return;
   }
@@ -90,8 +90,7 @@ void SuperEl::createSuperElQuad(MElement *badEl, double dist, const std::vector<
 
   // Get basis functions
   MQuadrangle quad1(v0,v1,v2,v3);
-  const int p = badEl->getPolynomialOrder();
-  const nodalBasis *quadBasis = quad1.getFunctionSpace(p);
+  const nodalBasis *quadBasis = quad1.getFunctionSpace(order);
   const int Nv = quadBasis->getNumShapeFunctions();
 
   // Store/create all vertices for super-element
@@ -100,8 +99,8 @@ void SuperEl::createSuperElQuad(MElement *badEl, double dist, const std::vector<
   _superVert[1] = v1;
   _superVert[2] = v2;
   _superVert[3] = v3;
-  for (int i=2; i<p+1; ++i) _superVert[2+i] = new MVertex(*baseVert[i]);  // HO vertices on base edge
-  for (int i=3+p; i<Nv; ++i) {                                            // Additional HO vertices
+  for (int i=2; i<order+1; ++i) _superVert[2+i] = new MVertex(*baseVert[i]);  // HO vertices on base edge
+  for (int i=3+order; i<Nv; ++i) {                                            // Additional HO vertices
     SPoint3 p;
     quad1.pnt(quadBasis->points(i,0),quadBasis->points(i,1),0.,p);
     _superVert[i] = new MVertex(p.x(),p.y(),0.);
@@ -109,7 +108,7 @@ void SuperEl::createSuperElQuad(MElement *badEl, double dist, const std::vector<
 //                << "," << _superVert[i]->y()<< "," << _superVert[i]->z() << ")" << std::endl;
   }
 
-  _superEl = new MQuadrangleN(_superVert,p);
+  _superEl = new MQuadrangleN(_superVert,order);
   _superEl0 = new MQuadrangle(_superVert[0],_superVert[1],_superVert[2],_superVert[3]);
 //  std::cout << "Created SuperEl at address " << _superEl << std::endl;
 
@@ -117,14 +116,13 @@ void SuperEl::createSuperElQuad(MElement *badEl, double dist, const std::vector<
 
 
 
-void SuperEl::createSuperElPrism(MElement *badEl, double dist, const std::vector<MVertex*> &baseVert,
-                                  const SVector3 &n0, const SVector3 &n1, const SVector3 &n2) {
+void SuperEl::createSuperElPrism(int order, double dist, const std::vector<MVertex*> &baseVert,
+                                 const SVector3 &n0, const SVector3 &n1, const SVector3 &n2) {
 
 
   if (baseVert.empty()) {
-    std::cout << "ERROR: Base edge not given for SuperEl linked to bad. el. " << badEl->getNum() << "\n";
+    std::cout << "ERROR: Base edge not given for SuperEl\n";
     _superEl = 0;
-    _superEl0 = 0;
     return;
   }
 
@@ -141,12 +139,11 @@ void SuperEl::createSuperElPrism(MElement *badEl, double dist, const std::vector
 
   // Get basis functions
   MPrism prism1(v0,v1,v2,v3,v4,v5);
-  const int p = badEl->getPolynomialOrder();
-  const nodalBasis *prismBasis = prism1.getFunctionSpace(p);
+  const nodalBasis *prismBasis = prism1.getFunctionSpace(order);
   const int Nv = prismBasis->getNumShapeFunctions();                        // Number of vertices in HO prism
 
   // Store/create all vertices for super-element
-  if (p == 2) {
+  if (order == 2) {
     _superVert.resize(18);
     _superVert[0] = v0;                                                     // First-order vertices
     _superVert[1] = v1;
@@ -167,7 +164,7 @@ void SuperEl::createSuperElPrism(MElement *badEl, double dist, const std::vector
     _superEl = new MPrism18(_superVert);
   }
   else {
-    std::cout << "ERROR: Prismatic superEl. of order " << p << " not supported for bad. el. " << badEl->getNum() << "\n";
+    std::cout << "ERROR: Prismatic superEl. of order " << order << " not supported\n";
     _superEl = 0;
     _superEl0 = 0;
     return;
@@ -180,14 +177,13 @@ void SuperEl::createSuperElPrism(MElement *badEl, double dist, const std::vector
 
 
 
-void SuperEl::createSuperElHex(MElement *badEl, double dist, const std::vector<MVertex*> &baseVert,
+void SuperEl::createSuperElHex(int order, double dist, const std::vector<MVertex*> &baseVert,
                                const SVector3 &n0, const SVector3 &n1, const SVector3 &n2, const SVector3 &n3) {
 
 
   if (baseVert.empty()) {
-    std::cout << "ERROR: Base edge not given for SuperEl linked to bad. el. " << badEl->getNum() << "\n";
+    std::cout << "ERROR: Base edge not given for SuperEl\n";
     _superEl = 0;
-    _superEl0 = 0;
     return;
   }
 
@@ -207,12 +203,11 @@ void SuperEl::createSuperElHex(MElement *badEl, double dist, const std::vector<M
 
   // Get basis functions
   MHexahedron *hex1 = new MHexahedron(v0,v1,v2,v3,v4,v5,v6,v7);
-  const int p = badEl->getPolynomialOrder();
-  const nodalBasis *prismBasis = hex1->getFunctionSpace(p);
+  const nodalBasis *prismBasis = hex1->getFunctionSpace(order);
   const int Nv = prismBasis->getNumShapeFunctions();                         // Number of vertices in HO hex
 
   // Store/create all vertices for super-element
-  if (p == 2) {
+  if (order == 2) {
     _superVert.resize(27);
     _superVert[0] = v0;                                                      // First-order vertices
     _superVert[1] = v1;
@@ -237,7 +232,7 @@ void SuperEl::createSuperElHex(MElement *badEl, double dist, const std::vector<M
     _superEl = new MHexahedron27(_superVert);
   }
   else {
-    std::cout << "ERROR: Hex. superEl. of order " << p << " not supported for bad. el. " << badEl->getNum() << "\n";
+    std::cout << "ERROR: Hex. superEl. of order " << order << " not supported\n";
     _superEl = 0;
     _superEl0 = 0;
     return;
diff --git a/contrib/HighOrderMeshOptimizer/SuperEl.h b/contrib/HighOrderMeshOptimizer/SuperEl.h
index 595394011fd9242d1c6f5066ea438165e86ecdfc..6d129bd0adadbf31360b9d768e51d29500a7ebc4 100644
--- a/contrib/HighOrderMeshOptimizer/SuperEl.h
+++ b/contrib/HighOrderMeshOptimizer/SuperEl.h
@@ -39,7 +39,7 @@ class SuperEl
 {
 public:
 
-  SuperEl(MElement *badEl, double dist, int type, const std::vector<MVertex*> &baseVert,
+  SuperEl(int order, double dist, int type, const std::vector<MVertex*> &baseVert,
           const std::vector<SVector3> &normals);
   ~SuperEl() { _superVert.clear(); delete _superEl, _superEl0; }
 
@@ -63,11 +63,11 @@ private:
   std::vector<MVertex*> _superVert;
   MElement *_superEl, *_superEl0;
 
-  void createSuperElQuad(MElement *badEl, double dist, const std::vector<MVertex*> &baseVert,
+  void createSuperElQuad(int order, double dist, const std::vector<MVertex*> &baseVert,
                          const SVector3 &n0, const SVector3 &n1);
-  void createSuperElPrism(MElement *badEl, double dist, const std::vector<MVertex*> &baseVert,
+  void createSuperElPrism(int order, double dist, const std::vector<MVertex*> &baseVert,
                           const SVector3 &n0, const SVector3 &n1, const SVector3 &n2);
-  void createSuperElHex(MElement *badEl, double dist, const std::vector<MVertex*> &baseVert,
+  void createSuperElHex(int order, double dist, const std::vector<MVertex*> &baseVert,
                         const SVector3 &n0, const SVector3 &n1, const SVector3 &n2, const SVector3 &n3);