diff --git a/Geo/FEdge.cpp b/Geo/FEdge.cpp
index 422287583a777927b9bcd0820261ae8456522b35..34ed1e4667dd1b6e9310f52f2d08395937855bb5 100644
--- a/Geo/FEdge.cpp
+++ b/Geo/FEdge.cpp
@@ -3,6 +3,20 @@
 
 #if defined(HAVE_FOURIER_MODEL)
 
+FEdge::FEdge(GModel *model, FM_Edge* edge_, int tag, GVertex *v0, GVertex *v1) 
+  : GEdge(model, tag, v0, v1), edge(edge_), face(0), edgeNum(-1) 
+{
+  //meshAttributes.Method = TRANSFINI; 
+  //meshAttributes.coeffTransfinite = 1.;
+  //meshAttributes.nbPointsTransfinite = 10;
+}
+
+FEdge::FEdge(GModel *model, FM_Face* face_, int edgeNum_, int tag, GVertex *v0, 
+	     GVertex *v1) 
+  : GEdge(model, tag, v0, v1), edge(0), face(face_), edgeNum(edgeNum_) 
+{
+}
+
 Range<double> FEdge::parBounds(int i) const
 { 
   return(Range<double>(0.,1.));
diff --git a/Geo/FEdge.h b/Geo/FEdge.h
index e23dc3d608053b4b6bf6c1e04aa815d31f7aee9e..6ff0bb9987f3f9ce435d54841d4c041ebe637695 100644
--- a/Geo/FEdge.h
+++ b/Geo/FEdge.h
@@ -18,11 +18,9 @@ class FEdge : public GEdge {
   FM_Face* face;
   int edgeNum;
  public:
-  FEdge(GModel *model, FM_Edge* edge_, int tag, GVertex *v0, GVertex *v1) : 
-    GEdge(model, tag, v0, v1), edge(edge_), face(0), edgeNum(-1) {}
+  FEdge(GModel *model, FM_Edge* edge_, int tag, GVertex *v0, GVertex *v1);
   FEdge(GModel *model, FM_Face* face_, int edgeNum_, int tag, GVertex *v0, 
-	GVertex *v1) : GEdge(model, tag, v0, v1), edge(0), face(face_), 
-    edgeNum(edgeNum_) {}
+	GVertex *v1);
   virtual ~FEdge() {}
   double period() const { throw ; }
   virtual bool periodic(int dim=0) const { return false; }
diff --git a/Geo/GEdge.cpp b/Geo/GEdge.cpp
index 3e49dfa02fc7ea8bc9f8112cfbd38dd6ed744928..1073a0add53d4c224b652c6b36100f8876d2449b 100644
--- a/Geo/GEdge.cpp
+++ b/Geo/GEdge.cpp
@@ -1,4 +1,4 @@
-// $Id: GEdge.cpp,v 1.27 2007-05-07 11:40:02 remacle Exp $
+// $Id: GEdge.cpp,v 1.28 2007-05-10 22:08:03 geuzaine Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -119,16 +119,16 @@ SVector3 GEdge::secondDer(double par) const
 {
   // use central differences
   const double eps = 1.e-3;
-  SVector3 x1 = firstDer(par-eps);
-  SVector3 x2 = firstDer(par+eps);
-  return 500*(x2-x1);
+  SVector3 x1 = firstDer(par - eps);
+  SVector3 x2 = firstDer(par + eps);
+  return 500 * (x2 - x1);
 }
 
-// Reparmaterize the point onto the given face.
 SPoint2 GEdge::reparamOnFace(GFace *face, double epar,int dir) const
 {
-  const GPoint p3 = point (epar);
-  SPoint3 sp3(p3.x(),p3.y(),p3.z());
+  // reparmaterize the point onto the given face.
+  const GPoint p3 = point(epar);
+  SPoint3 sp3(p3.x(), p3.y(), p3.z());
   return face->parFromPoint(sp3);
 }
 
@@ -153,11 +153,12 @@ double GEdge::curvature(double par) const
 
   n1.normalize();
   n2.normalize();
-  const double one_over_D = 1./D;
-  SVector3 d = one_over_D*(n2-n1);
+  const double one_over_D = 1. / D;
+  SVector3 d = one_over_D * (n2 - n1);
   return norm(d);
 }
 
-bool GEdge::is_mesh_degenerated() const {
-  return (v0==v1 && mesh_vertices.size()<2);
+bool GEdge::is_mesh_degenerated() const 
+{
+  return (v0 == v1 && mesh_vertices.size() < 2);
 }
diff --git a/Geo/GModelIO_Mesh.cpp b/Geo/GModelIO_Mesh.cpp
index ba7fbf0fa4b75c076788c4aeb4e66e3a955556c1..b4ad68a2925c8b04ed389cedd679139f79fe6866 100644
--- a/Geo/GModelIO_Mesh.cpp
+++ b/Geo/GModelIO_Mesh.cpp
@@ -1,4 +1,4 @@
-// $Id: GModelIO_Mesh.cpp,v 1.16 2007-05-02 07:59:27 geuzaine Exp $
+// $Id: GModelIO_Mesh.cpp,v 1.17 2007-05-10 22:08:03 geuzaine Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -1364,6 +1364,43 @@ int GModel::writeUNV(const std::string &name, bool saveAll, double scalingFactor
   }
   fprintf(fp, "%6d\n", -1);
 
+  // save groups of nodes for physical lines and physical surfaces
+  bool saveGroupsOfNodes = false; // add option in CTX+GUI for this
+  if(saveGroupsOfNodes){
+    fprintf(fp, "%6d\n", -1);
+    fprintf(fp, "%6d\n", 2477);
+    std::map<int, std::vector<GEntity*> > groups[4];
+    getPhysicalGroups(groups);
+    for(int dim = 1; dim <= 2; dim++){
+      for(std::map<int, std::vector<GEntity*> >::iterator it = groups[dim].begin();
+	  it != groups[dim].end(); it++){
+	std::set<MVertex*> nodes;
+	for(unsigned int i = 0; i < it->second.size(); i++){
+	  if(dim == 1){
+	    GEdge *ge = (GEdge*)it->second[i];
+	    for(unsigned int j = 0; j < ge->lines.size(); j++)
+	      for(int k = 0; k < ge->lines[j]->getNumVertices(); k++)
+		nodes.insert(ge->lines[j]->getVertex(k));
+	  }
+	  else if(dim == 2){
+	    GFace *gf = (GFace*)it->second[i];
+	    for(unsigned int j = 0; j < gf->triangles.size(); j++)
+	      for(int k = 0; k < gf->triangles[j]->getNumVertices(); k++)
+		nodes.insert(gf->triangles[j]->getVertex(k));
+	    for(unsigned int j = 0; j < gf->quadrangles.size(); j++)
+	      for(int k = 0; k < gf->quadrangles[j]->getNumVertices(); k++)
+		nodes.insert(gf->quadrangles[j]->getVertex(k));
+	  }
+	}
+	// put actual format of dataset in here
+	printf("physical %d : %d nodes\n", it->first, nodes.size());
+	for(std::set<MVertex*>::iterator it2 = nodes.begin(); it2 != nodes.end(); it2++)
+	  printf("node %6d\n", (*it2)->getNum());
+      }
+    }
+    fprintf(fp, "%6d\n", -1);
+  }
+
   fclose(fp);
   return 1;
 }
diff --git a/Geo/GRegion.cpp b/Geo/GRegion.cpp
index b79f885ac0d1eee60859815856079064ca5c7830..cd856b6d4f6f74d11ba1264bd8119d4f24960c91 100644
--- a/Geo/GRegion.cpp
+++ b/Geo/GRegion.cpp
@@ -1,4 +1,4 @@
-// $Id: GRegion.cpp,v 1.15 2006-12-16 14:37:20 geuzaine Exp $
+// $Id: GRegion.cpp,v 1.16 2007-05-10 22:08:03 geuzaine Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -120,13 +120,13 @@ void GRegion::deleteMeshPartitions()
 std::list<GEdge*> GRegion::edges() const
 {
   std::list<GEdge*> e;
-  std::list<GFace *>::const_iterator it =  l_faces.begin();
+  std::list<GFace*>::const_iterator it = l_faces.begin();
   while(it != l_faces.end()){
     std::list<GEdge*> e2;
     e2 = (*it)->edges();
     std::list<GEdge*>::const_iterator it2 = e2.begin();
     while (it2 != e2.end()){
-      if (std::find(e.begin(),e.end(),*it2) == e.end())
+      if (std::find(e.begin(), e.end(), *it2) == e.end())
 	e.push_back(*it2);
       ++it2;
     }
@@ -134,3 +134,17 @@ std::list<GEdge*> GRegion::edges() const
   }
   return e;
 }
+
+bool GRegion::edgeConnected(GRegion *r) const
+{
+  std::list<GEdge*> e = edges();
+  std::list<GEdge*> e2 = r->edges();
+  
+  std::list<GEdge*>::const_iterator it = e.begin();
+  while(it != e.end()){
+    if(std::find(e2.begin(), e2.end(), *it) != e2.end())
+      return true;
+    ++it;
+  }
+  return false;
+}
diff --git a/Geo/GRegion.h b/Geo/GRegion.h
index 2d93c161dd016151f6c7f2f7572e23384f6b6708..8a6cda9a180dc69d790811b909e235def492aded 100644
--- a/Geo/GRegion.h
+++ b/Geo/GRegion.h
@@ -43,6 +43,9 @@ class GRegion : public GEntity {
   // The bounding box
   virtual SBoundingBox3d bounds() const; 
 
+  // Checks if the region is connected to another region by an edge
+  bool edgeConnected(GRegion *r) const;
+
   // Recompute the mesh partitions defined on this region.
   void recomputeMeshPartitions();
 
diff --git a/Geo/Makefile b/Geo/Makefile
index 269b4c6743844c36c2e5b49d49e11173af825bda..f70e8ccd1efd8a1ee8cca65b69463cc8e796ac63 100644
--- a/Geo/Makefile
+++ b/Geo/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.145 2007-05-05 10:24:53 geuzaine Exp $
+# $Id: Makefile,v 1.146 2007-05-10 22:08:03 geuzaine Exp $
 #
 # Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 #
@@ -223,7 +223,16 @@ GModelIO_F.o: GModelIO_F.cpp GModel.h GVertex.h GEntity.h Range.h \
   GRegion.h ../Common/Message.h ../Common/Views.h ../Common/ColorTable.h \
   ../Common/VertexArray.h ../Common/SmoothData.h \
   ../Common/AdaptiveViews.h ../Common/GmshMatrix.h FFace.h FEdge.h \
-  FVertex.h ../Mesh/meshGFace.h
+  FVertex.h ../Mesh/meshGFace.h GModelIO_F.h \
+  ../contrib/FourierModel/FM_Reader.h ../contrib/FourierModel/Curve.h \
+  ../contrib/FourierModel/Patch.h ../contrib/FourierModel/FM_Info.h \
+  ../contrib/FourierModel/FM_Info.h ../contrib/FourierModel/ExactPatch.h \
+  ../contrib/FourierModel/Patch.h \
+  ../contrib/FourierModel/ContinuationPatch.h \
+  ../contrib/FourierModel/Patch.h ../contrib/FourierModel/FM_Info.h \
+  ../contrib/FourierModel/FM_Face.h ../contrib/FourierModel/Patch.h \
+  ../contrib/FourierModel/FM_Edge.h ../contrib/FourierModel/Curve.h \
+  ../contrib/FourierModel/FM_Vertex.h
 GModelIO_CGNS.o: GModelIO_CGNS.cpp GModel.h GVertex.h GEntity.h Range.h \
   SPoint3.h SBoundingBox3d.h ../Common/GmshDefines.h MVertex.h GPoint.h \
   SPoint2.h GEdge.h SVector3.h MElement.h MEdge.h ../Common/Hash.h \
diff --git a/Mesh/Generator.cpp b/Mesh/Generator.cpp
index eaf9ffe446a328a3ff34f971e199700b00841472..9a5285a7a77e9c78fef382286b3706161c24cc2f 100644
--- a/Mesh/Generator.cpp
+++ b/Mesh/Generator.cpp
@@ -1,4 +1,4 @@
-// $Id: Generator.cpp,v 1.118 2007-04-20 07:11:26 geuzaine Exp $
+// $Id: Generator.cpp,v 1.119 2007-05-10 22:08:03 geuzaine Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -232,6 +232,14 @@ void Mesh2D()
   Msg(STATUS1, "Mesh");
 }
 
+
+void FindConnectedRegions(std::vector<GRegion*> &delaunay, 
+			  std::vector<std::vector<GRegion*> > &connected)
+{
+  // FIXME: need to split region vector into connected components here!
+  connected.push_back(delaunay);
+}
+
 void Mesh3D()
 {
   if(TooManyElements(3)) return;
@@ -249,8 +257,13 @@ void Mesh3D()
   std::vector<GRegion*> delaunay;
   std::for_each(GMODEL->firstRegion(), GMODEL->lastRegion(), meshGRegion(delaunay));
 
-  // and finally mesh the delaunay regions (again, this is global)
-  MeshDelaunayVolume(delaunay);
+  // and finally mesh the delaunay regions (again, this is global; but
+  // we mesh each connected part separately for performance and mesh
+  // quality reasons)
+  std::vector<std::vector<GRegion*> > connected;
+  FindConnectedRegions(delaunay, connected);
+  for(unsigned int i = 0; i < connected.size(); i++)
+    MeshDelaunayVolume(connected[i]);
 
   double t2 = Cpu();
   CTX.mesh_timer[2] = t2 - t1;
diff --git a/Mesh/meshGRegion.cpp b/Mesh/meshGRegion.cpp
index e6e611f8191763d543a8db9044567687a4b3c3e3..8b07351f4b87c6d90165d172c5ebb741e8b40c7d 100644
--- a/Mesh/meshGRegion.cpp
+++ b/Mesh/meshGRegion.cpp
@@ -1,4 +1,4 @@
-// $Id: meshGRegion.cpp,v 1.29 2007-03-11 20:18:58 geuzaine Exp $
+// $Id: meshGRegion.cpp,v 1.30 2007-05-10 22:08:04 geuzaine Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -191,6 +191,9 @@ void MeshDelaunayVolume(std::vector<GRegion*> &regions)
   Msg(GERROR, "Tetgen is not compiled in this version of Gmsh");
 #else
 
+  for(unsigned int i = 0; i < regions.size(); i++)
+    Msg(STATUS2, "Meshing volume %d (Delaunay)", regions[i]->tag());
+
   // put all the faces in the same model
   GRegion *gr = regions[0];
   std::list<GFace*> faces = gr->faces();
@@ -494,9 +497,6 @@ void meshGRegion::operator() (GRegion *gr)
 
   if(ep && ep->mesh.ExtrudeMesh) return;
 
-  // Send a messsage to the GMSH environment
-  Msg(STATUS2, "Meshing volume %d", gr->tag());
-
   // destroy the mesh if it exists
   deMeshGRegion dem;
   dem(gr);
@@ -523,6 +523,7 @@ void meshGRegion::operator() (GRegion *gr)
 #if !defined(HAVE_NETGEN)
     Msg(GERROR, "Netgen is not compiled in this version of Gmsh");
 #else
+    Msg(STATUS2, "Meshing volume %d (Netgen)", gr->tag());
     // orient the triangles of with respect to this region
     meshNormalsPointOutOfTheRegion(gr);
     std::vector<MVertex*> numberedV;
diff --git a/Mesh/meshGRegionExtruded.cpp b/Mesh/meshGRegionExtruded.cpp
index 4dabacb97d0d038fc71c9aa58751c410775d348b..5c01d7ebea07ba2c102cdcaa20065e479d3b8bef 100644
--- a/Mesh/meshGRegionExtruded.cpp
+++ b/Mesh/meshGRegionExtruded.cpp
@@ -1,4 +1,4 @@
-// $Id: meshGRegionExtruded.cpp,v 1.16 2007-05-08 07:21:00 geuzaine Exp $
+// $Id: meshGRegionExtruded.cpp,v 1.17 2007-05-10 22:08:06 geuzaine Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -387,51 +387,20 @@ void phase1(GRegion *gr,
       for(int k = 0; k < ep->mesh.NbElmLayer[j]; k++) {
 	std::vector<MVertex*> v;
         if(getExtrudedVertices(from->triangles[i], ep, j, k, pos, v) == 6){
-#if 1 // this is the old version
+#if 0 // old
           if(!edgeExists(v[0], v[4], edges))
             createEdge(v[1], v[3], edges);
           if(!edgeExists(v[4], v[2], edges))
             createEdge(v[1], v[5], edges);
           if(!edgeExists(v[3], v[2], edges))
             createEdge(v[0], v[5], edges);
-#else // new version from Michel Benhamou <mi.benham@free.fr>
-          int v0 = 0;
-          for(int l = 1; l < 6; l++){
-            if(v[l] < v[v0]) v0 = l;
-          }
-          v0 = (v0 + 2) % 3;
-          int vn[6];
-          for(int l = 0; l < 3; l++){
-            vn[l] = (v0 + l) % 3;
-            vn[l + 3] = vn[l] + 3;
-          }
-
-          //if(v[vn[1]] < v[vn[0]]){
-            if(!edgeExists(v[vn[0]], v[vn[4]], edges))
-              createEdge(v[vn[1]], v[vn[3]], edges);
-	  //}
-	  //else{
-          //  if(!edgeExists(v[vn[1]], v[vn[3]], edges))
-          //    createEdge(v[vn[0]], v[vn[4]], edges);
-	  //}
-
-	  //if(v[vn[1]] < v[vn[4]]){
-            if(!edgeExists(v[vn[4]], v[vn[2]], edges))
-              createEdge(v[vn[1]], v[vn[5]], edges);
-	  //}
-	  //else{
-          //  if(!edgeExists(v[vn[1]], v[vn[5]], edges))
-          //    createEdge(v[vn[4]], v[vn[2]], edges);
-	  //}
-
-          if(v[vn[0]] < v[vn[3]]){
-            if(!edgeExists(v[vn[3]], v[vn[2]], edges))
-              createEdge(v[vn[0]], v[vn[5]], edges);
-          }
-          else{
-            if(!edgeExists(v[vn[0]], v[vn[5]], edges))
-              createEdge(v[vn[3]], v[vn[2]], edges);
-          }
+#else // new from Michel Benhamou
+	  if(v[1] < v[0]) createEdge(v[1], v[3], edges);
+	  else createEdge(v[0], v[4], edges);
+	  if(v[1] < v[2]) createEdge(v[1], v[5], edges);
+	  else createEdge(v[4], v[2], edges);
+	  if(v[0] < v[2]) createEdge(v[0], v[5], edges);
+	  else createEdge(v[3], v[2], edges);
 #endif
 	}
       }
diff --git a/Mesh/meshGRegionTransfinite.cpp b/Mesh/meshGRegionTransfinite.cpp
index f379f81f9f9aef53955f9215895a9c7ba9b67ce5..bcc0efcfbcc58ed45f6c102ebf152f46903ae959 100644
--- a/Mesh/meshGRegionTransfinite.cpp
+++ b/Mesh/meshGRegionTransfinite.cpp
@@ -1,4 +1,4 @@
-// $Id: meshGRegionTransfinite.cpp,v 1.6 2007-04-24 09:02:25 geuzaine Exp $
+// $Id: meshGRegionTransfinite.cpp,v 1.7 2007-05-10 22:08:06 geuzaine Exp $
 //
 // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle
 //
@@ -280,6 +280,8 @@ int MeshTransfiniteVolume(GRegion *gr)
 {
   if(gr->meshAttributes.Method != TRANSFINI) return 0;
 
+  Msg(STATUS2, "Meshing volume %d (transfinite)", gr->tag());
+
   std::list<GFace*> faces = gr->faces();
   if(faces.size() != 5 && faces.size() != 6){
     Msg(GERROR, "Transfinite algorithm only available for 5- and 6-face volumes");
diff --git a/doc/TODO b/doc/TODO
index 1d7590e86855861a18a0f34b0e62fa1c61886088..b888e81e82e54ce39ca2aee88cab6f48992993f3 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -1,4 +1,14 @@
-$Id: TODO,v 1.57 2007-05-04 14:45:04 geuzaine Exp $
+$Id: TODO,v 1.58 2007-05-10 22:08:06 geuzaine Exp $
+
+********************************************************************
+
+add function to deform MESH with displacement post-pro view
+
+********************************************************************
+
+3D Delaunay: precompute non-connected volumes and apply algo to each
+non-connected aggregate. This will improve mesh quality and speed for
+close non-connected objects.
 
 ********************************************************************