diff --git a/Fltk/graphicWindow.cpp b/Fltk/graphicWindow.cpp
index 8a83e0154087c925c05372182eeef07af3218c14..4046dadca29841b98aeba1f7872470dcac9ab0ea 100644
--- a/Fltk/graphicWindow.cpp
+++ b/Fltk/graphicWindow.cpp
@@ -61,6 +61,7 @@ typedef unsigned long intptr_t;
 #include "HighOrder.h"
 #include "OS.h"
 #include "onelabUtils.h"
+#include "gmshCrossFields.h"
 #if defined(HAVE_3M)
 #include "3M.h"
 #endif
@@ -2117,6 +2118,12 @@ static void mesh_optimize_cb(Fl_Widget *w, void *data)
   drawContext::global()->draw();
 }
 
+static void mesh_cross_compute_cb(Fl_Widget *w, void *data)
+{
+  computeCrossField (GModel::current());
+  drawContext::global()->draw();
+}
+
 static void mesh_refine_cb(Fl_Widget *w, void *data)
 {
   RefineMesh(GModel::current(), CTX::instance()->mesh.secondOrderLinear);
@@ -4298,6 +4305,8 @@ static menuItem static_modules[] = {
    (Fl_Callback *)mesh_inspect_cb} ,
   {"0Modules/Mesh/Refine by splitting",
    (Fl_Callback *)mesh_refine_cb} ,
+  {"0Modules/Mesh/Compute Cross Field",
+   (Fl_Callback *)mesh_cross_compute_cb} ,
 #if defined(HAVE_METIS)
   {"0Modules/Mesh/Partition",
    (Fl_Callback *)mesh_partition_cb} ,
diff --git a/Geo/GModel.cpp b/Geo/GModel.cpp
index eba8bb620e78bc59488d80d83be99b0bc8fa6aad..e1ceb7f1b460c4300a13f4b6f67709421f0fa9e8 100644
--- a/Geo/GModel.cpp
+++ b/Geo/GModel.cpp
@@ -2951,7 +2951,6 @@ static void recurClassifyEdges(MTri3 *t, std::map<MTriangle *, GFace *> &reverse
   while (!_stack.empty()){
     t = _stack.top();
     _stack.pop();
-    //  printf("%d\n",rec);
     if(!t->isDeleted()) {
       trisTouched.erase(t);
       t->setDeleted(true);
@@ -3123,8 +3122,8 @@ void GModel::classifyFaces()
 
   for(std::map<std::pair<int, int>, GEdge *>::iterator ite = newEdges.begin();
       ite != newEdges.end(); ++ite) {
-    //    printf("%d %d -->
-    //    %d\n",ite->first.first,ite->first.second,ite->second->tag());
+
+    printf("%d %d -->  %d\n",ite->first.first,ite->first.second,ite->second->tag());
     std::list<MLine *> allSegments;
     for(std::size_t i = 0; i < ite->second->lines.size(); i++)
       allSegments.push_back(ite->second->lines[i]);
diff --git a/Mesh/CMakeLists.txt b/Mesh/CMakeLists.txt
index 5b24751f858738fde1212d24ebc78e484f125d78..e35ef4c5995df9766d6a86cf4a626b04fd92c3e2 100644
--- a/Mesh/CMakeLists.txt
+++ b/Mesh/CMakeLists.txt
@@ -15,7 +15,7 @@ set(SRC
     meshGRegionBoundaryRecovery.cpp
     meshGRegionHxt.cpp meshGRegionNetgen.cpp
     meshGRegionDelaunayInsertion.cpp meshGRegionTransfinite.cpp
-    meshGRegionExtruded.cpp meshGRegionCarveHole.cpp
+    meshGRegionExtruded.cpp meshGRegionCarveHole.cpp 
     meshGRegionLocalMeshMod.cpp meshGRegionMMG3D.cpp
   meshGRegionBoundaryLayer.cpp
   meshRelocateVertex.cpp
@@ -31,6 +31,7 @@ set(SRC
   DivideAndConquer.cpp
   Field.cpp
   filterElements.cpp
+  gmshCrossFields.cpp
 )
 
 file(GLOB HDR RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.h)
diff --git a/Mesh/gmshCrossFields.cpp b/Mesh/gmshCrossFields.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d260ec3606f17dd720b02f6d7c5454d1024aecd0
--- /dev/null
+++ b/Mesh/gmshCrossFields.cpp
@@ -0,0 +1,248 @@
+#include <vector>
+#include "gmshCrossFields.h"
+#include "GModel.h"
+#include "GFace.h"
+#include "MEdge.h"
+#include "MTriangle.h"
+#include "GmshMessage.h"
+#include "Context.h"
+#if defined(_OPENMP)
+#include <omp.h>
+#endif
+
+class cross2d {
+public:
+  MEdge _e;
+  std::vector<MEdge> _neighbors;
+  std::vector<cross2d*> _cneighbors;
+  // euler angles
+  double _a,_b,_c;
+  double _atemp,_btemp,_ctemp;
+  std::vector<MTriangle *> _t;
+  cross2d (MEdge &e, MTriangle *r, MEdge &e1, MEdge &e2) : _e(e) , _a (0), _b(0), _c(0)
+  {
+    _t.push_back(r);
+    _neighbors.push_back(e1);
+    _neighbors.push_back(e2);
+  }
+  void normalize(double &a){
+    double D = M_PI*.5;
+    if (a < 0 ) while (a < 0) a += D; 
+    if (a >= D) while (a >= D) a -= D; 
+  }
+  void finish ( std::map<MEdge,cross2d,Less_Edge > &C){
+    for (size_t i=0;i<_neighbors.size();i++){
+      std::map<MEdge,cross2d,Less_Edge >::iterator it = C.find(_neighbors[i]);
+      if (it == C.end())Msg::Error("impossible situation");
+      else _cneighbors.push_back(&(it->second));      
+    }
+    if (_cneighbors.size() != 4){
+      _a = atan2 (_e.getVertex(1)->y()-_e.getVertex(0)->y(),_e.getVertex(1)->x()-_e.getVertex(0)->x());
+      normalize(_a);
+      _atemp = _a;
+    }
+    _neighbors.clear();
+    _b = _btemp = sin (4*_a);
+    _c = _ctemp = cos (4*_a);
+  }
+  double average_init (){
+    if (_cneighbors.size() == 4){
+      
+      _btemp = 0.25*(_cneighbors[0]->_b+_cneighbors[1]->_b+_cneighbors[2]->_b+_cneighbors[3]->_b);
+      _ctemp = 0.25*(_cneighbors[0]->_c+_cneighbors[1]->_c+_cneighbors[2]->_c+_cneighbors[3]->_c);
+      _atemp = 0.25*atan2(_b,_c);
+      normalize(_atemp);
+      return 1;
+    }
+    return 1;
+  }  
+
+  void update () {
+    _a = _atemp;
+    _b = _btemp;
+    _c = _ctemp;    
+  }
+  
+  double grad () {
+    if (_cneighbors.size() == 4){
+      double D = M_PI*.5;
+      double a[4]  = {_cneighbors[0]->_a,_cneighbors[1]->_a,
+		      _cneighbors[2]->_a,_cneighbors[3]->_a};
+      double b[4];
+      for (int i=0;i<4;i++){
+	if (fabs(_a - a[i]) < fabs(_a - (a[i]+D)) &&
+	    fabs(_a - a[i]) < fabs(_a - (a[i]-D))) {b[i] = a[i];}
+	else if (fabs(_a - (a[i]+D)) < fabs(_a - (a[i])) &&
+		 fabs(_a - (a[i]+D)) < fabs(_a - (a[i]-D))) {b[i] = a[i]+D;}
+	else {b[i] = a[i]-D;}	
+      }
+      return fabs(_a-b[0]) + fabs(_a-b[1]) + fabs(_a-b[2]) + fabs(_a-b[3]) ;
+    }
+    return 0;
+  }
+  
+  double average (){
+    if (_cneighbors.size() == 4){
+      double D = M_PI*.5;
+      double a[4]  = {_cneighbors[0]->_a,_cneighbors[1]->_a,
+		      _cneighbors[2]->_a,_cneighbors[3]->_a};
+      double b[4];
+      double avg = 0.0;
+      for (int i=0;i<4;i++){
+	if (fabs(_a - a[i]) < fabs(_a - (a[i]+D)) &&
+	    fabs(_a - a[i]) < fabs(_a - (a[i]-D))) {b[i] = a[i];}
+	else if (fabs(_a - (a[i]+D)) < fabs(_a - (a[i])) &&
+		 fabs(_a - (a[i]+D)) < fabs(_a - (a[i]-D))) {b[i] = a[i]+D;}
+	else {b[i] = a[i]-D;}	
+      }
+      avg = 0.25*(b[0]+b[1]+b[2]+b[3]);
+      
+      normalize(avg);
+      
+      double d = fabs(_a-avg);
+      _atemp = avg;
+      return d;
+    }
+    return 0;
+  }
+};
+// ---------------------------------------------
+// TODO : MAKE IT PARALLEL AND SUPERFAST
+//        DO IT ON SURFACES
+// ---------------------------------------------
+
+int computeCrossField2dTheta (GModel *gm,std::vector<GFace *> &f, const char * outputName){
+  Msg::SetNumThreads(Msg::GetMaxThreads());
+  
+  std::map<MEdge,cross2d,Less_Edge > C;
+  for (size_t i=0;i<f.size();i++){
+    for (size_t j=0;j<f[i]->triangles.size();j++){
+      MTriangle *t = f[i]->triangles[j];
+      for (size_t k=0;k<3;k++){
+	MEdge e = t->getEdge(k);
+	MEdge e1 = t->getEdge((k+1)%3);
+	MEdge e2 = t->getEdge((k+2)%3);
+	cross2d c (e,t,e1,e2);
+	std::map<MEdge,cross2d,Less_Edge >::iterator it = C.find(e);
+	if (it == C.end())C.insert(std::make_pair(e,c));
+	else {
+	  it->second._t.push_back(t);
+	  it->second._neighbors.push_back(e1);
+	  it->second._neighbors.push_back(e2);
+	}
+      }
+    }
+  }
+
+  std::map<MEdge,cross2d,Less_Edge >::iterator it = C.begin();
+  for (; it !=C.end();++it)it->second.finish(C);
+
+  std::vector<cross2d*> pc;
+  for (it = C.begin(); it !=C.end();++it)pc.push_back(&(it->second));
+  
+  const int MAXITER = 20000;
+  int ITER = 0;
+  while (ITER++ < MAXITER){
+    double DELTA = 0;
+#if defined(_OPENMP)
+#pragma omp parallel for schedule(dynamic)
+#endif
+    for (size_t i=0;i<pc.size(); i++){
+      if (ITER<2000)DELTA += pc[i]->average_init();
+      else DELTA += pc[i]->average();
+    }
+
+#if defined(_OPENMP)
+#pragma omp parallel for schedule(dynamic)
+#endif
+    for (size_t i=0;i<pc.size(); i++)pc[i]->update();
+    
+    if (ITER%100 == 0)printf("DELTA = %12.5E\n",DELTA);
+    if (DELTA  < 1.e-12)break;
+    //    getchar();
+  }
+
+  FILE *of = fopen ("cross.pos","w");
+  fprintf(of,"View \"\"{\n");
+  for (it = C.begin(); it !=C.end();++it){
+    double a = it->second.grad();
+    MEdge e0 = it->second._e;
+    fprintf(of,"SP(%g,%g,%g){%g};",
+	    0.5*(e0.getVertex(0)->x()+e0.getVertex(1)->x()),
+	    0.5*(e0.getVertex(0)->y()+e0.getVertex(1)->y()),
+	    0.5*(e0.getVertex(0)->z()+e0.getVertex(1)->z()), a);
+  }
+  
+  for (size_t i=0;i<f.size();i++){
+    for (size_t j=0;j<f[i]->triangles.size();j++){
+      MTriangle *t = f[i]->triangles[j];
+      MEdge e0 = t->getEdge(0);
+      MEdge e1 = t->getEdge(1);
+      MEdge e2 = t->getEdge(2);
+      std::map<MEdge,cross2d,Less_Edge >::iterator it0 = C.find(e0);
+      std::map<MEdge,cross2d,Less_Edge >::iterator it1 = C.find(e1);
+      std::map<MEdge,cross2d,Less_Edge >::iterator it2 = C.find(e2);
+      double a0 = it0->second._a;
+      double a1 = it1->second._a;
+      double a2 = it2->second._a;
+      //      double avg = atan2 (sin(a1)+sin(a2)+sin(a0),
+      //			  cos(a1)+cos(a2)+cos(a0));		 
+      fprintf(of,"VP(%g,%g,%g){%g,%g,%g};",
+	      0.5*(e0.getVertex(0)->x()+e0.getVertex(1)->x()),
+	      0.5*(e0.getVertex(0)->y()+e0.getVertex(1)->y()),
+	      0.5*(e0.getVertex(0)->z()+e0.getVertex(1)->z()),
+	      cos (a0), sin(a0),0.0);
+      fprintf(of,"VP(%g,%g,%g){%g,%g,%g};",
+	      0.5*(e1.getVertex(0)->x()+e1.getVertex(1)->x()),
+	      0.5*(e1.getVertex(0)->y()+e1.getVertex(1)->y()),
+	      0.5*(e1.getVertex(0)->z()+e1.getVertex(1)->z()),
+	      cos (a1), sin(a1),0.0);
+      fprintf(of,"VP(%g,%g,%g){%g,%g,%g};",
+	      0.5*(e2.getVertex(0)->x()+e2.getVertex(1)->x()),
+	      0.5*(e2.getVertex(0)->y()+e2.getVertex(1)->y()),
+	      0.5*(e2.getVertex(0)->z()+e2.getVertex(1)->z()),
+	      cos (a2), sin(a2),0.0);
+      fprintf(of,"VP(%g,%g,%g){%g,%g,%g};",
+	      0.5*(e0.getVertex(0)->x()+e0.getVertex(1)->x()),
+	      0.5*(e0.getVertex(0)->y()+e0.getVertex(1)->y()),
+	      0.5*(e0.getVertex(0)->z()+e0.getVertex(1)->z()),
+	      -sin (a0), cos(a0),0.0);
+      fprintf(of,"VP(%g,%g,%g){%g,%g,%g};",
+	      0.5*(e1.getVertex(0)->x()+e1.getVertex(1)->x()),
+	      0.5*(e1.getVertex(0)->y()+e1.getVertex(1)->y()),
+	      0.5*(e1.getVertex(0)->z()+e1.getVertex(1)->z()),
+	      -sin (a1), cos(a1),0.0);
+      fprintf(of,"VP(%g,%g,%g){%g,%g,%g};",
+	      0.5*(e2.getVertex(0)->x()+e2.getVertex(1)->x()),
+	      0.5*(e2.getVertex(0)->y()+e2.getVertex(1)->y()),
+	      0.5*(e2.getVertex(0)->z()+e2.getVertex(1)->z()),
+	      -sin (a2), cos(a2),0.0);
+      
+      /*
+      fprintf(of,"VT(%g,%g,%g,%g,%g,%g,%g,%g,%g){%g,%g,%g,%g,%g,%g,%g,%g,%g};",
+	      t->getVertex(0)->x(),t->getVertex(0)->y(),t->getVertex(0)->z(),
+	      t->getVertex(1)->x(),t->getVertex(1)->y(),t->getVertex(1)->z(),
+	      t->getVertex(2)->x(),t->getVertex(2)->y(),t->getVertex(2)->z(),
+	      cos(avg),sin(avg),0.,cos(avg),sin(avg),0.,cos(avg),sin(avg),0.);
+      */
+      
+    }
+  }
+  fprintf(of,"};\n");
+  fclose(of);
+  return 0;
+  
+}
+
+int computeCrossField (GModel *gm)
+{
+  std::vector<GFace *> f ;
+  for (GModel::fiter it = gm->firstFace() ; it != gm->lastFace();++it){
+    GFace *gf = *it;
+    f.push_back(gf);
+  }
+  //  if (!strcmp(method,"theta"))
+  return computeCrossField2dTheta (gm,f,"toto");
+    //  else
+    //    Msg::Error("Unknown Cross Field Computation Method %s",method);
+}
diff --git a/Mesh/gmshCrossFields.h b/Mesh/gmshCrossFields.h
new file mode 100644
index 0000000000000000000000000000000000000000..d1d43379169a97886334f087cb93526aa8949e60
--- /dev/null
+++ b/Mesh/gmshCrossFields.h
@@ -0,0 +1,6 @@
+#ifndef _CROSS_FIELDS_H_
+#define _CROSS_FIELDS_H_
+class GModel;
+int computeCrossField (GModel*);
+//int computeCrossField2d (const char * method, const char * outputName, std::vector<int> &faces);
+#endif