diff --git a/Common/CommandLine.cpp b/Common/CommandLine.cpp
index 04d53d6d081b0c48fa45d444edca5e979dbdb6de..f0e3b18e0ec5d337eb563598155b4911d8107d34 100644
--- a/Common/CommandLine.cpp
+++ b/Common/CommandLine.cpp
@@ -1,4 +1,4 @@
-// $Id: CommandLine.cpp,v 1.98 2007-04-20 07:11:26 geuzaine Exp $
+// $Id: CommandLine.cpp,v 1.99 2007-04-26 09:47:37 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -377,6 +377,10 @@ void Get_Options(int argc, char *argv[])
           exit(1);
         }
       }
+      else if(!strcmp(argv[i] + 1, "c1")) {
+        i++;
+	opt_mesh_c1(0, GMSH_SET, 1);
+      }
       else if(!strcmp(argv[i] + 1, "optimize_hom")) {
         i++;
 	opt_mesh_smooth_internal_edges(0, GMSH_SET, 1);
diff --git a/Common/Context.h b/Common/Context.h
index 9af74778a8486c524f3e712191a8e3f93719b2fb..c64a09a77fc7d2ca444053de2eaa7c204fd6cfdd 100644
--- a/Common/Context.h
+++ b/Common/Context.h
@@ -173,7 +173,7 @@ public :
     int dual;
     int light, light_two_side, light_lines;
     int format, nb_smoothing, algo2d, algo3d, algo_recombine;
-    int order, second_order_linear, second_order_incomplete, smooth_internal_edges;
+    int order, second_order_linear, second_order_incomplete, smooth_internal_edges, c1_continuity;
     int min_circ_points;
     int constrained_bgmesh, lc_from_curvature;
     double normals, tangents, explode;
diff --git a/Common/DefaultOptions.h b/Common/DefaultOptions.h
index baa5a1281561924ef677fe7574af530594675583..91c2121a83ff8f81424e446f102b4f2eb8b5c5b7 100644
--- a/Common/DefaultOptions.h
+++ b/Common/DefaultOptions.h
@@ -910,6 +910,8 @@ StringXNumber MeshOptions_Number[] = {
 
   { F|O, "ElementOrder" , opt_mesh_order , 1. , // "Order" is a reserved token in the parser
     "Element order (1=linear elements, N (<6) = elements of higher order)" },
+  { F|O, "C1Continuity" , opt_mesh_c1 , 0. , // "Order" is a reserved token in the parser
+    "Impose C1 continuity to high order meshes, only valid in 2D plane and ElemenOrder = 2 and 3 (todo) (Default : C0 continuity)" },
   { F|O, "SmoothInternalEdges" , opt_mesh_smooth_internal_edges , 0 , // "Order" is a reserved token in the parser
     "Number of smoothing steps of internal edges for high order meshes" },
   { F|O, "Explode" , opt_mesh_explode , 1.0 ,
diff --git a/Common/Options.cpp b/Common/Options.cpp
index 66a81897aa9b311915fffadbd3a2d3e8d501efad..d1094d8214331ecb786192e4c03f81841aee7fe6 100644
--- a/Common/Options.cpp
+++ b/Common/Options.cpp
@@ -1,4 +1,4 @@
-// $Id: Options.cpp,v 1.340 2007-04-21 19:39:59 geuzaine Exp $
+// $Id: Options.cpp,v 1.341 2007-04-26 09:47:38 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -4815,6 +4815,17 @@ double opt_mesh_order(OPT_ARGS_NUM)
   return CTX.mesh.order;
 }
 
+double opt_mesh_c1(OPT_ARGS_NUM)
+{
+  if(action & GMSH_SET)
+    CTX.mesh.c1_continuity = (int)val;
+#if defined(HAVE_FLTK)
+  if(WID && (action & GMSH_GUI))
+    WID->mesh_butt[21]->value(CTX.mesh.c1_continuity);
+#endif
+  return CTX.mesh.c1_continuity;
+}
+
 double opt_mesh_smooth_internal_edges(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET)
diff --git a/Common/Options.h b/Common/Options.h
index 21d6bce11b2497cf38abbcc99ce30aa53f22ad4c..5d06923db737d7553b95e9ef1f366a14f48d38f0 100644
--- a/Common/Options.h
+++ b/Common/Options.h
@@ -469,6 +469,7 @@ double opt_mesh_recombine_algo(OPT_ARGS_NUM);
 double opt_mesh_min_circ_points(OPT_ARGS_NUM);
 double opt_mesh_constrained_bgmesh(OPT_ARGS_NUM);
 double opt_mesh_order(OPT_ARGS_NUM);
+double opt_mesh_c1(OPT_ARGS_NUM);
 double opt_mesh_smooth_internal_edges(OPT_ARGS_NUM);
 double opt_mesh_second_order_linear(OPT_ARGS_NUM);
 double opt_mesh_second_order_incomplete(OPT_ARGS_NUM);
diff --git a/Fltk/Callbacks.cpp b/Fltk/Callbacks.cpp
index 5ee1cbd7b1853c684f9e5b815bfe6fa9c15ed40f..c033d81d88b3e7ba32abfa0b4e4d2f4ab9cc0db4 100644
--- a/Fltk/Callbacks.cpp
+++ b/Fltk/Callbacks.cpp
@@ -1,4 +1,4 @@
-// $Id: Callbacks.cpp,v 1.523 2007-04-21 19:39:59 geuzaine Exp $
+// $Id: Callbacks.cpp,v 1.524 2007-04-26 09:47:38 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -1090,6 +1090,7 @@ void mesh_options_ok_cb(CALLBACK_ARGS)
   opt_mesh_order(0, GMSH_SET, WID->mesh_value[3]->value());
   opt_mesh_smooth_internal_edges(0, GMSH_SET, WID->mesh_butt[3]->value());
   opt_mesh_second_order_incomplete(0, GMSH_SET, WID->mesh_butt[4]->value());
+  opt_mesh_c1(0, GMSH_SET, WID->mesh_butt[21]->value());
   opt_mesh_constrained_bgmesh(0, GMSH_SET, WID->mesh_butt[5]->value());
   opt_mesh_points(0, GMSH_SET, WID->mesh_butt[6]->value());
   opt_mesh_lines(0, GMSH_SET, WID->mesh_butt[7]->value());
diff --git a/Fltk/GUI.cpp b/Fltk/GUI.cpp
index 463a2ee8b0de95e54aa871d9232a548c66958d50..035c5cda5700e39c2d0069e29e45fdf096082077 100644
--- a/Fltk/GUI.cpp
+++ b/Fltk/GUI.cpp
@@ -1,4 +1,4 @@
-// $Id: GUI.cpp,v 1.603 2007-03-29 16:32:23 geuzaine Exp $
+// $Id: GUI.cpp,v 1.604 2007-04-26 09:47:38 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -1619,7 +1619,7 @@ void GUI::reset_external_view_list()
 void GUI::create_option_window()
 {
   int width = 40 * fontsize;
-  int height = 13 * BH + 5 * WB;
+  int height = 14 * BH + 5 * WB;
   int L = 105 + WB;
 
   if(opt_window) {
@@ -2288,10 +2288,14 @@ void GUI::create_option_window()
       mesh_butt[3]->type(FL_TOGGLE_BUTTON);
       mesh_butt[3]->callback(mesh_options_ok_cb);
 
-      mesh_butt[4] = new Fl_Check_Button(L + 2 * WB, 2 * WB + 11 * BH, BW, BH, "Use incomplete second order elements (8-node quads, etc.)");
+      mesh_butt[4] = new Fl_Check_Button(L + 2 * WB, 2 * WB + 11 * BH, BW, BH, "Use incomplete high order elements (8-node quads, etc.)");
       mesh_butt[4]->type(FL_TOGGLE_BUTTON);
       mesh_butt[4]->callback(mesh_options_ok_cb);
 
+      mesh_butt[21] = new Fl_Check_Button(L + 2 * WB, 2 * WB + 12 * BH, BW, BH, "Impose C1 continuity (only for degrees 2 and 3 and 2D plane)");
+      mesh_butt[21]->type(FL_TOGGLE_BUTTON);
+      mesh_butt[21]->callback(mesh_options_ok_cb);
+
       o->end();
     }
 
diff --git a/Mesh/HighOrder.cpp b/Mesh/HighOrder.cpp
index b1a90cda3838ebbdbdb2aabe1ce1af761dd706f4..3324147d221ff5bd6e49ea1588a00ecf30a3278d 100644
--- a/Mesh/HighOrder.cpp
+++ b/Mesh/HighOrder.cpp
@@ -1,4 +1,4 @@
-// $Id: HighOrder.cpp,v 1.12 2007-04-12 08:47:25 remacle Exp $
+// $Id: HighOrder.cpp,v 1.13 2007-04-26 09:47:38 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -25,14 +25,107 @@
 #include "Message.h"
 #include "OS.h"
 
-// consider 2 points with their tangent
-// x(t) = x(0) * H1(t) + dx(0) * H2(t) + x(1) * H3(t) + dx(1) * H4(t) 
+void computeMidPoints(const double *p1,
+		      const double *p2, 
+		      const double *t1, 
+		      const double *t2, 
+		      double *p3){
+	// computes the mid point p3 of the second order parametric polynomial curve according to extreme points p1,p2 and tangents t1,t2
+	// The tangent directions given in t1 and t2 are the outgoing normals !!! to the curve p1-p2
+	double x0 = p1[0];
+	double y0 = p1[1];
+	double x1 = p2[0];
+	double y1 = p2[1];
+	double t0x = -t1[0];
+	double t0y = -t1[1];
+	double t1x = t2[0];
+	double t1y = t2[1];
+
+	//computation of the rotation parameters
+	double delta_x = x1-x0;
+	double delta_y = y1-y0;
+	double L = sqrt(delta_x*delta_x+delta_y*delta_y);
+	double theta = atan2(delta_y,delta_x);
+	//	double theta = atan(delta_y/delta_x);
+	//	if (delta_x<0)
+	//	  theta += M_PI;
+	double costheta = cos(theta);
+	double sintheta = sin(theta);
+	double theta0 = atan2(t0y,t0x)-theta;
+	double theta1 = atan2(t1y,t1x)-theta;
+	double tan0 = tan(theta0);
+	double tan1 = tan(theta1);
+	delta_x = L;
+	delta_y = 0;
+
+
+	//	printf("theta0 = %g theta1 = %g\n",theta0,theta1);
+
+	// computes the 6 coefficients: solving the linear system
+	// x(t) = a1 t^2 * b1 t + c1
+	// y(t) = a2 t^2 * b2 t + c2
+	// for t in [0,1]
+	// with :
+	// x(t), y(t) on pts 0 and 1 (4 conditions)
+	// and dy/dx = dy/dt*dt/dx on those 2 pts (2 conditions)
+
+	if (tan0 == tan1)
+	  {
+	    p3[0] = 0.5 * (p1[0]+p2[0]);
+	    p3[1] = 0.5 * (p1[1]+p2[1]);
+	  }
+	else
+	  {
+	    double b1 = 2*(delta_y-delta_x*tan1)/(tan0-tan1);
+	    double a1 = delta_x-b1;
+	    double b2 = tan0 * b1;
+	    double a2 = delta_y - b2;
+	    double c1 = 0;// in general : x0... 
+	    double c2 = 0;// in general : y0... but axis have changed...
+	    
+	    // back to the initial axis
+	    double ax = costheta*a1 - sintheta*a2;
+	    double bx = costheta*b1 - sintheta*b2;
+	    double cx = x0;//costheta*c1 - sintheta*c2 + x0;
+	    double ay = sintheta*a1 + costheta*a2;
+	    double by = sintheta*b1 + costheta*b2;
+	    double cy = y0;//sintheta*c1 + costheta*c2 + y0;
+	    
+	    // computes the mid point (x,y)
+	    double t=0.5;
+	    double tsquare=t*t;
+	    p3[0] = ax*tsquare + bx*t + cx;
+	    p3[1] = ay*tsquare + by*t + cy;
+	  }
+	return;
+}
 
 void Hermite2D_C1(SPoint3 &p1, SPoint3 &p2, SPoint3 &t1, SPoint3 &t2,
 		  SPoint3 &one_third, SPoint3 &two_third)
 {
-  // double L = sqrt((p1.x()-p2.x())*(p1.x()-p2.x()) + (p1.y()-p2.y()) * (p1.y()-p2.y()));
-  // SVector3 p1p2 (p2,p1);
+   double L = sqrt((p1.x()-p2.x())*(p1.x()-p2.x()) + (p1.y()-p2.y()) * (p1.y()-p2.y()));
+   SVector3 p1p2 (p2,p1);
+   double theta  = atan2(p2.x()-p1.x(),p2.y()-p1.y());
+   double theta1 = atan2(t1.x(),t1.y());
+   double theta2 = atan2(t2.x(),t2.y());
+   double rot[2][2] =
+     {{cos(theta),-sin(theta)},
+      {sin(theta),cos(theta)}};
+
+   double ts[2] = {1./3.,2./3.};
+   SPoint3 *pt[2] = {&one_third,&two_third};
+   for (int i=0 ; i < 2; i++){
+     const double t = ts[i];
+     double H2 = L*t*(t-1)*(t-1);
+     double H4 = -L*(1-t)*t*t;
+     double x3 = t * L;
+     double y3 = H2 * theta1 + H4 * theta2;     
+     //     pt[i]->x() = p1.x() + x3 * rot[0][0] + y3 * rot[0][1]; 
+     //     pt[i]->y() = p1.y() + x3 * rot[1][0] + y3 * rot[1][1]; 
+   }
+
+   
+
   // SVector3 tt1  (t1.x(),t1.y(),0);
   // SVector3 tt2  (t2.x(),t2.y(),0);
   // const double cost1 = p1p2 * tt1;
@@ -109,7 +202,7 @@ bool reparamOnEdge(MVertex *v, GEdge *ge, double &param)
 void getEdgeVertices(GEdge *ge, MElement *ele, std::vector<MVertex*> &ve,
 		     edgeContainer &edgeVertices, bool linear, int nPts = 1)
 {
-  bool hermite = false;
+  bool c1 = CTX.mesh.c1_continuity;
 
   for(int i = 0; i < ele->getNumEdges(); i++){
     MEdge edge = ele->getEdge(i);
@@ -124,33 +217,23 @@ void getEdgeVertices(GEdge *ge, MElement *ele, std::vector<MVertex*> &ve,
       MVertex *v0 = edge.getVertex(0), *v1 = edge.getVertex(1);            
       double u0 = 0., u1 = 0.;
       bool reparamOK = true;
-      if(hermite || (!linear && ge->geomType() != GEntity::DiscreteCurve)){
+      if(c1 || (!linear && ge->geomType() != GEntity::DiscreteCurve)){
 	reparamOK &= reparamOnEdge(v0, ge, u0);
 	reparamOK &= reparamOnEdge(v1, ge, u1);
       }
-      if(nPts == 2 && hermite){
+      if(nPts == 1 && c1){
 	SVector3 tv1 = ge->firstDer(u0);
 	SVector3 tv2 = ge->firstDer(u1);
-	tv1.normalize();
-	tv2.normalize();
-	SPoint3 t1(tv1.x(), tv1.y(), 0), t2(tv2.x(), tv2.y(), 0);
-	SPoint3 vv0(v0->x(), v0->y(), 0), vv1(v1->x(), v1->y(), 0);
-	SPoint3 one_third, two_third;
-	Hermite2D_C1(vv0, vv1, t1, t2, one_third, two_third);
-	MVertex *v1 = new MVertex(one_third.x(), one_third.y(), 0);
-	MVertex *v2 = new MVertex(two_third.x(), two_third.y(), 0);
-	ge->mesh_vertices.push_back(v1);
-	ve.push_back(v1);
-	ge->mesh_vertices.push_back(v2);
-	ve.push_back(v2);	
-	if(edge.getVertex(0) == edge.getMinVertex()){
-	  edgeVertices[p].push_back(v1);
-	  edgeVertices[p].push_back(v2);
-	}
-	else{
-	  edgeVertices[p].push_back(v2);
-	  edgeVertices[p].push_back(v1);
-	}
+	double  t1[2] = {-tv1.x(),-tv1.y()};
+	double  t2[2] = { tv2.x(), tv2.y()};	
+	double  p1[2] = { v0->x(), v0->y()};
+	double  p2[2] = { v1->x(), v1->y()};
+	double  p3[2];
+	computeMidPoints(p1,p2,t1,t2,p3);
+	MVertex *hv = new MVertex(p3[0],p3[1],0);
+	ge->mesh_vertices.push_back(hv);
+	ve.push_back(hv);
+	edgeVertices[p].push_back(hv);
       }
       else{
 	std::vector<MVertex*> temp;
diff --git a/Mesh/meshGEdge.cpp b/Mesh/meshGEdge.cpp
index a52ab04d5c2273a4a5e82c1762528652b8fa3d58..eb4899aff09179597ae49ef26b5e45f0c0a3c101 100644
--- a/Mesh/meshGEdge.cpp
+++ b/Mesh/meshGEdge.cpp
@@ -1,4 +1,4 @@
-// $Id: meshGEdge.cpp,v 1.36 2007-04-22 19:41:02 geuzaine Exp $
+// $Id: meshGEdge.cpp,v 1.37 2007-04-26 09:47:38 remacle Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -232,7 +232,7 @@ void meshGEdge::operator() (GEdge *ge)
     N = ge->meshAttributes.nbPointsTransfinite;
   }
   else{
-    a = Integration(ge, t_begin, t_end, F_Lc, Points, 1.e-7);
+    a = Integration(ge, t_begin, t_end, F_Lc, Points, 1.e-8);
     N = std::max(ge->minimumMeshSegments() + 1, (int)(a + 1.));
   }
   const double b = a / (double)(N - 1);