diff --git a/Common/Context.h b/Common/Context.h
index ba835e87c2b0e6d20f417d9d10360537f58b378f..602c0075b9b7624dcf94c4c625586b96524000de 100644
--- a/Common/Context.h
+++ b/Common/Context.h
@@ -182,6 +182,8 @@ public :
     int oldxtrude, oldxtrude_recombine, check_duplicates;
     int allow_degenerated_extrude, save_all;
     char *triangle_options;
+    int smooth_normals;
+    double angle_smooth_normals;
   } mesh;
 
   // post processing options 
diff --git a/Common/DefaultOptions.h b/Common/DefaultOptions.h
index e1ea0cd62bd80d337db46b6f0e7b767d4dccdd95..af8e8eedc7d0ec6c42a0c181bec3c6b37d65486c 100644
--- a/Common/DefaultOptions.h
+++ b/Common/DefaultOptions.h
@@ -705,6 +705,8 @@ StringXNumber MeshOptions_Number[] = {
     "3D mesh algorithm (1=isotropic, 4=netgen)" }, 
   { F,   "AllowDegeneratedExtrude" , opt_mesh_allow_degenerated_extrude , 0. , 
     "Allow the generation of degenerated hexahedra or prisms during extrusion" },
+  { F|O, "AngleSmoothNormals" , opt_mesh_angle_smooth_normals , 180.0 ,
+    "Threshold angle below which normals are not smoothed" }, 
 
   { F|O, "CharacteristicLengthFactor" , opt_mesh_lc_factor , 1.0 ,
     "Factor applied to all characteristic lengths (and background meshes)" },
@@ -813,6 +815,8 @@ StringXNumber MeshOptions_Number[] = {
     "Global scaling factor applied to the saved mesh" },
   { F|O, "Smoothing" , opt_mesh_nb_smoothing , 1. ,
     "Number of smoothing steps applied to the final mesh" },
+  { F|O, "SmoothNormals" , opt_mesh_smooth_normals , 0. , 
+    "Smooth the mesh normals?" },
   { F|O, "SpeedMax" , opt_mesh_speed_max , 0. ,
     "Disable dubious point insertion tests" },
   { F|O, "SurfaceEdges" , opt_mesh_surfaces_edges , 1. , 
diff --git a/Common/Makefile b/Common/Makefile
index aba189345f35e62b6ef223cf36e27ef528be262b..fa0302204a1bdf4a0560d4cc5b0e9b30e9bc383d 100644
--- a/Common/Makefile
+++ b/Common/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.58 2004-06-15 18:20:53 geuzaine Exp $
+# $Id: Makefile,v 1.59 2004-07-16 18:02:19 geuzaine Exp $
 #
 # Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
 #
@@ -35,6 +35,7 @@ SRC = Context.cpp\
       Visibility.cpp\
       Trackball.cpp\
       VertexArray.cpp\
+      SmoothNormals.cpp\
       License.cpp
 
 OBJ = ${SRC:.cpp=.o}
diff --git a/Common/Options.cpp b/Common/Options.cpp
index fe63dac2fdc5ec072aacdb13dfb18aa2edd2e5c4..613ef79990301146c779bf3cfd0d07f04b926c76 100644
--- a/Common/Options.cpp
+++ b/Common/Options.cpp
@@ -1,4 +1,4 @@
-// $Id: Options.cpp,v 1.170 2004-07-02 20:03:22 geuzaine Exp $
+// $Id: Options.cpp,v 1.171 2004-07-16 18:02:19 geuzaine Exp $
 //
 // Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
 //
@@ -3427,6 +3427,32 @@ double opt_mesh_vertex_arrays(OPT_ARGS_NUM)
   return CTX.mesh.vertex_arrays;
 }
 
+double opt_mesh_smooth_normals(OPT_ARGS_NUM)
+{
+  if(action & GMSH_SET) {
+    if(CTX.mesh.smooth_normals != val) CTX.mesh.changed = 1;
+    CTX.mesh.smooth_normals = (int)val;
+  }
+#if defined(HAVE_FLTK)
+  if(WID && (action & GMSH_GUI))
+    WID->mesh_butt[19]->value(CTX.mesh.smooth_normals);
+#endif
+  return CTX.mesh.smooth_normals;
+}
+
+double opt_mesh_angle_smooth_normals(OPT_ARGS_NUM)
+{
+  if(action & GMSH_SET) {
+    if(CTX.mesh.angle_smooth_normals != val) CTX.mesh.changed = 1;
+    CTX.mesh.angle_smooth_normals = val;
+  }
+#if defined(HAVE_FLTK)
+  if(WID && (action & GMSH_GUI))
+    WID->mesh_value[18]->value(CTX.mesh.angle_smooth_normals);
+#endif
+  return CTX.mesh.angle_smooth_normals;
+}
+
 double opt_mesh_light(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET)
@@ -3444,7 +3470,7 @@ double opt_mesh_light_two_side(OPT_ARGS_NUM)
     CTX.mesh.light_two_side = (int)val;
 #if defined(HAVE_FLTK)
   if(WID && (action & GMSH_GUI))
-    WID->mesh_butt[23]->value(CTX.mesh.light_two_side);
+    WID->mesh_butt[18]->value(CTX.mesh.light_two_side);
 #endif
   return CTX.mesh.light_two_side;
 }
diff --git a/Common/Options.h b/Common/Options.h
index 910ee4a52db2ca2ea933463da8b94b66f68c49ac..e772ce1221a4dbef4315f5d86270921ab5e37167 100644
--- a/Common/Options.h
+++ b/Common/Options.h
@@ -363,6 +363,8 @@ double opt_mesh_point_type(OPT_ARGS_NUM);
 double opt_mesh_line_width(OPT_ARGS_NUM);
 double opt_mesh_line_type(OPT_ARGS_NUM);
 double opt_mesh_vertex_arrays(OPT_ARGS_NUM);
+double opt_mesh_smooth_normals(OPT_ARGS_NUM);
+double opt_mesh_angle_smooth_normals(OPT_ARGS_NUM);
 double opt_mesh_light(OPT_ARGS_NUM);
 double opt_mesh_light_two_side(OPT_ARGS_NUM);
 double opt_mesh_format(OPT_ARGS_NUM);
diff --git a/Common/SmoothNormals.cpp b/Common/SmoothNormals.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d34413b02aff39c8d59b1329b5f04cd7d5d2c871
--- /dev/null
+++ b/Common/SmoothNormals.cpp
@@ -0,0 +1,146 @@
+// $Id: SmoothNormals.cpp,v 1.1 2004-07-16 18:02:19 geuzaine Exp $
+//
+// Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+// USA.
+// 
+// Please report all bugs and problems to <gmsh@geuz.org>.
+
+#include "Gmsh.h"
+#include "Numeric.h"
+#include "SmoothNormals.h"
+
+double xyzv::eps = 1.e-12;
+
+xyzv::xyzv(double xx, double yy, double zz)
+  : x(xx), y(yy), z(zz), vals(0), nbvals(0), nboccurences(0)
+{
+}
+
+xyzv::~xyzv()
+{
+  if(vals)
+    delete [] vals;
+}
+
+xyzv::xyzv(const xyzv & other)
+{
+  x = other.x;
+  y = other.y;
+  z = other.z;
+  nbvals = other.nbvals;
+  nboccurences = other.nboccurences;
+  if(other.vals && other.nbvals) {
+    vals = new double[other.nbvals];
+    for(int i = 0; i < nbvals; i++)
+      vals[i] = other.vals[i];
+  }
+}
+
+xyzv & xyzv::operator =(const xyzv & other)
+{
+  if(this != &other) {
+    x = other.x;
+    y = other.y;
+    z = other.z;
+    nbvals = other.nbvals;
+    nboccurences = other.nboccurences;
+    if(other.vals && other.nbvals) {
+      vals = new double[other.nbvals];
+      for(int i = 0; i < nbvals; i++)
+        vals[i] = other.vals[i];
+    }
+  }
+  return *this;
+}
+
+void xyzv::update(int n, double *v)
+{
+  int i;
+  if(!vals) {
+    vals = new double[n];
+    for(i = 0; i < n; i++)
+      vals[i] = 0.0;
+    nbvals = n;
+    nboccurences = 0;
+  }
+  else if(nbvals != n) {
+    printf("throw!!\n");
+    throw n;
+  }
+
+  double x1 = (double)(nboccurences) / (double)(nboccurences + 1);
+  double x2 = 1. / (double)(nboccurences + 1);
+  for(i = 0; i < nbvals; i++)
+    vals[i] = (x1 * vals[i] + x2 * v[i]);
+  nboccurences++;
+
+  //printf("val(%d,%f,%f,%f) = %f\n",nboccurences,x,y,z,vals[0]);
+}
+
+double smooth_normals::get_angle(double *aa, double *bb)
+{
+  double angplan, cosc, sinc, a[3], b[3], c[3];
+  if(!aa || !bb)
+    return 0.;
+  a[0] = aa[0];
+  a[1] = aa[1];
+  a[2] = aa[2];
+  b[0] = bb[0];
+  b[1] = bb[1];
+  b[2] = bb[2];
+  norme(a);
+  norme(b);
+  prodve(a, b, c);
+  prosca(a, b, &cosc);
+  sinc = sqrt(c[0] * c[0] + c[1] * c[1] + c[2] * c[2]);
+  angplan = myatan2(sinc, cosc);
+  return angplan * 180. / Pi;
+}
+
+void smooth_normals::add(double x, double y, double z,
+			 double nx, double ny, double nz)
+{
+  double n[3] = { nx, ny, nz };
+  xyzv xyz(x, y, z);
+  xyziter it = c.find(xyz);
+  if(it == c.end()) {
+    xyz.update(3, n);
+    c.insert(xyz);
+  }
+  else {
+    xyzv *xx = (xyzv *) & (*it);
+    xx->update(3, n);
+  }    
+}
+
+bool smooth_normals::get(double x, double y, double z,
+			 double &nx, double &ny, double &nz, double tol)
+{
+  double n[3] = { nx, ny, nz };
+  xyzv xyz(x, y, z);
+  xyziter it = c.find(xyz);
+  if(it == c.end())
+    return false;
+  double angle = get_angle((*it).vals, n);
+  if(fabs(angle) < tol) {
+    nx = (*it).vals[0];
+    ny = (*it).vals[1];
+    nz = (*it).vals[2];
+  }
+  return true;
+}
+
diff --git a/Common/SmoothNormals.h b/Common/SmoothNormals.h
new file mode 100644
index 0000000000000000000000000000000000000000..0a3a30262850028133bd268507591e4e39fe88f8
--- /dev/null
+++ b/Common/SmoothNormals.h
@@ -0,0 +1,72 @@
+#ifndef _SMOOTH_NORMALS_H_
+#define _SMOOTH_NORMALS_H_
+
+// Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+// USA.
+// 
+// Please report all bugs and problems to <gmsh@geuz.org>.
+
+#include <set>
+
+using namespace std;
+
+struct xyzv
+{
+  double x, y, z, *vals;
+  int nbvals;
+  int nboccurences;
+  static double eps;
+  void update(int nbVals, double *);
+  xyzv(double x, double y, double z);
+  ~xyzv();
+  xyzv & operator =(const xyzv &);
+  xyzv(const xyzv &);
+};
+
+struct lessthanxyzv
+{
+  bool operator () (const xyzv & p2, const xyzv & p1)const
+  {
+    if(p1.x - p2.x > xyzv::eps)
+      return true;
+    if(p1.x - p2.x < -xyzv::eps)
+      return false;
+    if(p1.y - p2.y > xyzv::eps)
+      return true;
+    if(p1.y - p2.y < -xyzv::eps)
+      return false;
+    if(p1.z - p2.z > xyzv::eps)
+      return true;
+    return false;
+  }
+};
+
+typedef set < xyzv, lessthanxyzv > xyzcont;
+typedef xyzcont::const_iterator xyziter;
+
+class smooth_normals{
+ private:
+  double get_angle(double *aa, double *bb);
+ public:
+  xyzcont c;
+  void add(double x, double y, double z,
+	   double nx, double ny, double nz);
+  bool get(double x, double y, double z,
+	   double &nx, double &ny, double &nz, double angletol = 180.0);
+};
+
+#endif
diff --git a/Common/Views.cpp b/Common/Views.cpp
index a66844f2c753c4b2addbea250eaf72930177c95b..aeb981331148d7b5f28c1d5e6c946097e210bce7 100644
--- a/Common/Views.cpp
+++ b/Common/Views.cpp
@@ -1,4 +1,4 @@
-// $Id: Views.cpp,v 1.125 2004-07-05 15:20:06 geuzaine Exp $
+// $Id: Views.cpp,v 1.126 2004-07-16 18:02:19 geuzaine Exp $
 //
 // Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
 //
@@ -26,6 +26,7 @@
 #include "Context.h"
 #include "Options.h"
 #include "ColorTable.h"
+#include "SmoothNormals.h"
 
 extern Context_T CTX;
 
@@ -150,7 +151,7 @@ Post_View *BeginView(int allocate)
   v->DuplicateOf = 0;
   v->ScalarOnly = 1;
   v->TextOnly = 1;
-  v->normals = NULL;
+  v->normals = new smooth_normals;
   v->Min = VAL_INF;
   v->Max = -VAL_INF;
   for(i = 0; i < 3; i++) {
@@ -578,9 +579,9 @@ void FreeView(Post_View * v)
     v->SY = v->VY = v->TY = NULL;
     v->T2D = v->T2C = NULL;
     v->T3D = v->T3C = NULL;
-    v->reset_normals();
-    if(v->TriVertexArray) 
-      delete v->TriVertexArray;
+    if(v->normals) delete v->normals;
+    v->normals = NULL;
+    if(v->TriVertexArray) delete v->TriVertexArray;
     v->TriVertexArray = NULL;
   }
 
@@ -1037,118 +1038,15 @@ void WriteView(Post_View *v, char *filename, int binary, int append)
 
 // Smoothing
 
-using namespace std;
-
-struct xyzv
-{
-private:
-public:
-  double x, y, z, *vals;
-  int nbvals;
-  int nboccurences;
-  static double eps;
-  void update(int nbVals, double *);
-    xyzv(double x, double y, double z);
-   ~xyzv();
-    xyzv & operator =(const xyzv &);
-    xyzv(const xyzv &);
-};
-
-double xyzv::eps = 0.0;
-
-xyzv::xyzv(double xx, double yy, double zz)
-:x(xx), y(yy), z(zz), vals(0), nbvals(0), nboccurences(0)
-{
-}
-
-xyzv::~xyzv()
-{
-  if(vals)
-    delete[]vals;
-}
-
-xyzv::xyzv(const xyzv & other)
-{
-  x = other.x;
-  y = other.y;
-  z = other.z;
-  nbvals = other.nbvals;
-  nboccurences = other.nboccurences;
-  if(other.vals && other.nbvals) {
-    vals = new double[other.nbvals];
-    for(int i = 0; i < nbvals; i++)
-      vals[i] = other.vals[i];
-  }
-}
-
-xyzv & xyzv::operator =(const xyzv & other)
-{
-  if(this != &other) {
-    x = other.x;
-    y = other.y;
-    z = other.z;
-    nbvals = other.nbvals;
-    nboccurences = other.nboccurences;
-    if(other.vals && other.nbvals) {
-      vals = new double[other.nbvals];
-      for(int i = 0; i < nbvals; i++)
-        vals[i] = other.vals[i];
-    }
-  }
-  return *this;
-}
-
-void xyzv::update(int n, double *v)
+void Post_View::reset_normals()
 {
-  int i;
-  if(!vals) {
-    vals = new double[n];
-    for(i = 0; i < n; i++)
-      vals[i] = 0.0;
-    nbvals = n;
-    nboccurences = 0;
-  }
-  else if(nbvals != n) {
-    throw n;
-  }
-
-  double x1 = (double)(nboccurences) / (double)(nboccurences + 1);
-  double x2 = 1. / (double)(nboccurences + 1);
-  for(i = 0; i < nbvals; i++)
-    vals[i] = (x1 * vals[i] + x2 * v[i]);
-  nboccurences++;
-
-  //printf("val(%d,%f,%f,%f) = %f\n",nboccurences,x,y,z,vals[0]);
+  if(normals)
+    delete normals;
+  normals = new smooth_normals;
 }
 
-struct lessthanxyzv
-{
-  bool operator () (const xyzv & p2, const xyzv & p1)const
-  {
-    if(p1.x - p2.x > xyzv::eps)
-      return true;
-    if(p1.x - p2.x < -xyzv::eps)
-      return false;
-    if(p1.y - p2.y > xyzv::eps)
-      return true;
-    if(p1.y - p2.y < -xyzv::eps)
-      return false;
-    if(p1.z - p2.z > xyzv::eps)
-      return true;
-    return false;
-  }
-};
-
-typedef set < xyzv, lessthanxyzv > mycont;
-typedef mycont::const_iterator iter;
-
-class smooth_container
-{
-  public: mycont c;
-};
-
 void generate_connectivities(List_T * list, int nbList, int nbTimeStep, int nbVert,
-                             mycont & connectivities)
+                             xyzcont & connectivities)
 {
   double *x, *y, *z, *v;
   int i, j, k;
@@ -1167,7 +1065,7 @@ void generate_connectivities(List_T * list, int nbList, int nbTimeStep, int nbVe
       for(k = 0; k < nbTimeStep; k++)
         vals[k] = v[j + k * nbVert];
       xyzv xyz(x[j], y[j], z[j]);
-      iter it = connectivities.find(xyz);
+      xyziter it = connectivities.find(xyz);
       if(it == connectivities.end()) {
         xyz.update(nbTimeStep, vals);
         connectivities.insert(xyz);
@@ -1184,7 +1082,7 @@ void generate_connectivities(List_T * list, int nbList, int nbTimeStep, int nbVe
 }
 
 void smooth_list(List_T * list, int nbList, double *min, double *max,
-                 int nbTimeStep, int nbVert, mycont & connectivities)
+                 int nbTimeStep, int nbVert, xyzcont & connectivities)
 {
   double *x, *y, *z, *v;
   int i, j, k;
@@ -1203,7 +1101,7 @@ void smooth_list(List_T * list, int nbList, double *min, double *max,
     v = (double *)List_Pointer_Fast(list, i + 3 * nbVert);
     for(j = 0; j < nbVert; j++) {
       xyzv xyz(x[j], y[j], z[j]);
-      iter it = connectivities.find(xyz);
+      xyziter it = connectivities.find(xyz);
       if(it != connectivities.end()) {
         for(k = 0; k < nbTimeStep; k++) {
           v[j + k * nbVert] = (*it).vals[k];
@@ -1222,7 +1120,7 @@ void Post_View::smooth()
   xyzv::eps = CTX.lc * 1.e-8;
 
   if(NbSL || NbST || NbSQ || NbSS || NbSH || NbSI || NbSY) {
-    mycont con;
+    xyzcont con;
     Msg(INFO, "Smoothing scalar primitives in View[%d]", Index);
     generate_connectivities(SL, NbSL, NbTimeStep, 2, con);
     generate_connectivities(ST, NbST, NbTimeStep, 3, con);
@@ -1242,81 +1140,6 @@ void Post_View::smooth()
   }
 }
 
-// Normal smoothing
-
-void Post_View::reset_normals()
-{
-  if(normals)
-    delete normals;
-  normals = 0;
-}
-
-void Post_View::add_normal(double x, double y, double z,
-                           double nx, double ny, double nz)
-{
-  if(!normals)
-    normals = new smooth_container;
-
-  double n[3] = { nx, ny, nz };
-  xyzv xyz(x, y, z);
-
-  iter it = normals->c.find(xyz);
-
-  if(it == normals->c.end()) {
-    xyz.update(3, n);
-    normals->c.insert(xyz);
-  }
-  else {
-    xyzv *xx = (xyzv *) & (*it);
-    xx->update(3, n);
-  }
-}
-
-double get_angle(double *aa, double *bb)
-{
-  double angplan, cosc, sinc, a[3], b[3], c[3];
-  if(!aa || !bb)
-    return 0.;
-  a[0] = aa[0];
-  a[1] = aa[1];
-  a[2] = aa[2];
-  b[0] = bb[0];
-  b[1] = bb[1];
-  b[2] = bb[2];
-  norme(a);
-  norme(b);
-  prodve(a, b, c);
-  prosca(a, b, &cosc);
-  sinc = sqrt(c[0] * c[0] + c[1] * c[1] + c[2] * c[2]);
-  angplan = myatan2(sinc, cosc);
-  return angplan * 180. / Pi;
-}
-
-bool Post_View::get_normal(double x, double y, double z,
-                           double &nx, double &ny, double &nz)
-{
-  if(!normals)
-    return false;
-
-  double n[3] = { nx, ny, nz };
-  xyzv xyz(x, y, z);
-
-  iter it = normals->c.find(xyz);
-
-  if(it == normals->c.end())
-    return false;
-
-  double angle = get_angle((*it).vals, n);
-
-  if(fabs(angle) < AngleSmoothNormals) {
-    nx = (*it).vals[0];
-    ny = (*it).vals[1];
-    nz = (*it).vals[2];
-  }
-
-  return true;
-}
-
 // Transformation
 
 static void transform(double mat[3][3], double v[3],
diff --git a/Common/Views.h b/Common/Views.h
index 143007aea8b54629d89fc69642762015afc2aac9..c503a26c7da3548d07b532f9d3e8579201329a2c 100644
--- a/Common/Views.h
+++ b/Common/Views.h
@@ -23,13 +23,12 @@
 #include "ColorTable.h"
 #include "List.h"
 #include "VertexArray.h"
+#include "SmoothNormals.h"
 
 #define VIEW_NB_ELEMENT_TYPES  (8*3)
 #define VIEW_MAX_ELEMENT_NODES  8
 #define VAL_INF 1.e200
 
-class smooth_container;
-
 class Post_View{
   public :
   // intrinsic to a view
@@ -90,13 +89,9 @@ class Post_View{
   int (*GIFV) (double min, double max, int nb, double value);
   // smooth the view
   void smooth();
-  // smooth the normals
-  smooth_container *normals;
+  // smooth normals
+  smooth_normals *normals;
   void reset_normals();
-  void add_normal(double x, double y, double z, 
-		  double nx, double ny, double nz);
-  bool get_normal(double x, double y, double z, 
-		  double &nx, double &ny, double &nz);
   // transform the view
   void transform(double mat[3][3]);
 
diff --git a/Fltk/Callbacks.cpp b/Fltk/Callbacks.cpp
index 6a3d40e9a26bfdcfce26b64e3acae42a73c1f59b..7ee0b3bd16465bcffc14e7998b44d7df452aa4ca 100644
--- a/Fltk/Callbacks.cpp
+++ b/Fltk/Callbacks.cpp
@@ -1,4 +1,4 @@
-// $Id: Callbacks.cpp,v 1.253 2004-07-08 17:21:27 geuzaine Exp $
+// $Id: Callbacks.cpp,v 1.254 2004-07-16 18:02:19 geuzaine Exp $
 //
 // Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
 //
@@ -989,7 +989,8 @@ void mesh_options_ok_cb(CALLBACK_ARGS)
   opt_mesh_use_cut_plane(0, GMSH_SET, WID->mesh_butt[16]->value());
   opt_mesh_cut_plane_as_surface(0, GMSH_SET, WID->mesh_butt[22]->value());
   opt_mesh_light(0, GMSH_SET, WID->mesh_butt[17]->value());
-  opt_mesh_light_two_side(0, GMSH_SET, WID->mesh_butt[23]->value());
+  opt_mesh_light_two_side(0, GMSH_SET, WID->mesh_butt[18]->value());
+  opt_mesh_smooth_normals(0, GMSH_SET, WID->mesh_butt[19]->value());
 
   opt_mesh_nb_smoothing(0, GMSH_SET, WID->mesh_value[0]->value());
   opt_mesh_scaling_factor(0, GMSH_SET, WID->mesh_value[1]->value());
@@ -1008,6 +1009,7 @@ void mesh_options_ok_cb(CALLBACK_ARGS)
   opt_mesh_cut_planeb(0, GMSH_SET, WID->mesh_value[15]->value());
   opt_mesh_cut_planec(0, GMSH_SET, WID->mesh_value[16]->value());
   opt_mesh_cut_planed(0, GMSH_SET, WID->mesh_value[17]->value());
+  opt_mesh_angle_smooth_normals(0, GMSH_SET, WID->mesh_value[18]->value());
 
   opt_mesh_point_type(0, GMSH_SET, WID->mesh_choice[0]->value());
   opt_mesh_line_type(0, GMSH_SET, WID->mesh_choice[1]->value());
diff --git a/Fltk/GUI.cpp b/Fltk/GUI.cpp
index 93c79b99961023fecfface0032f6aa4cae87a230..7534bad7afdc99e953cbfb6ca1cf21db8312ae24 100644
--- a/Fltk/GUI.cpp
+++ b/Fltk/GUI.cpp
@@ -1,4 +1,4 @@
-// $Id: GUI.cpp,v 1.322 2004-07-01 19:13:44 geuzaine Exp $
+// $Id: GUI.cpp,v 1.323 2004-07-16 18:02:20 geuzaine Exp $
 //
 // Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
 //
@@ -1878,24 +1878,29 @@ void GUI::create_option_window()
       Fl_Group *o = new Fl_Group(WB, WB + BH, width - 2 * WB, height - 2 * WB - BH, "Aspect");
       o->hide();
       mesh_butt[17] = new Fl_Check_Button(2 * WB, 2 * WB + 1 * BH, BW, BH, "Enable lighting");
-      mesh_butt[17]->type(FL_TOGGLE_BUTTON);
-      mesh_butt[17]->down_box(TOGGLE_BOX);
-      mesh_butt[17]->selection_color(TOGGLE_COLOR);
+      mesh_butt[18] = new Fl_Check_Button(2 * WB, 2 * WB + 2 * BH, BW, BH, "Use two-side lighting");
+      mesh_butt[19] = new Fl_Check_Button(2 * WB, 2 * WB + 3 * BH, BW, BH, "Smooth normals");
+      for(i = 17; i <= 19; i++) {
+	mesh_butt[i]->type(FL_TOGGLE_BUTTON);
+	mesh_butt[i]->down_box(TOGGLE_BOX);
+	mesh_butt[i]->selection_color(TOGGLE_COLOR);
+      }
 
-      mesh_butt[23] = new Fl_Check_Button(2 * WB, 2 * WB + 2 * BH, BW, BH, "Use two-side lighting");
-      mesh_butt[23]->type(FL_TOGGLE_BUTTON);
-      mesh_butt[23]->down_box(TOGGLE_BOX);
-      mesh_butt[23]->selection_color(TOGGLE_COLOR);
+      mesh_value[18] = new Fl_Value_Input(2 * WB, 2 * WB + 4 * BH, IW, BH, "Angle");
+      mesh_value[18]->minimum(0.);
+      mesh_value[18]->maximum(180.);
+      mesh_value[18]->step(1.);
+      mesh_value[18]->align(FL_ALIGN_RIGHT);
 
-      mesh_value[9] = new Fl_Value_Input(2 * WB, 2 * WB + 3 * BH, IW, BH, "Explode elements");
+      mesh_value[9] = new Fl_Value_Input(2 * WB, 2 * WB + 5 * BH, IW, BH, "Explode elements");
       mesh_value[9]->minimum(0);
       mesh_value[9]->maximum(1);
       mesh_value[9]->step(0.01);
-      mesh_value[10] = new Fl_Value_Input(2 * WB, 2 * WB + 5 * BH, IW, BH, "Point size");
+      mesh_value[10] = new Fl_Value_Input(2 * WB, 2 * WB + 7 * BH, IW, BH, "Point size");
       mesh_value[10]->minimum(0.1);
       mesh_value[10]->maximum(50);
       mesh_value[10]->step(0.1);
-      mesh_value[11] = new Fl_Value_Input(2 * WB, 2 * WB + 7 * BH, IW, BH, "Line width");
+      mesh_value[11] = new Fl_Value_Input(2 * WB, 2 * WB + 9 * BH, IW, BH, "Line width");
       mesh_value[11]->minimum(0.1);
       mesh_value[11]->maximum(50);
       mesh_value[11]->step(0.1);
@@ -1903,11 +1908,11 @@ void GUI::create_option_window()
         mesh_value[i]->align(FL_ALIGN_RIGHT);
       }
 
-      mesh_choice[0] = new Fl_Choice(2 * WB, 2 * WB + 4 * BH, IW, BH, "Point display");
+      mesh_choice[0] = new Fl_Choice(2 * WB, 2 * WB + 6 * BH, IW, BH, "Point display");
       mesh_choice[0]->menu(menu_point_display);
       mesh_choice[0]->align(FL_ALIGN_RIGHT);
 
-      mesh_choice[1] = new Fl_Choice(2 * WB, 2 * WB + 6 * BH, IW, BH, "Line display");
+      mesh_choice[1] = new Fl_Choice(2 * WB, 2 * WB + 8 * BH, IW, BH, "Line display");
       mesh_choice[1]->menu(menu_line_display);
       mesh_choice[1]->align(FL_ALIGN_RIGHT);
       mesh_choice[1]->deactivate(); // don't give false hopes, as it's not used anywhere right now
diff --git a/Graphics/Iso.cpp b/Graphics/Iso.cpp
index 986fc64c089f3f08f423e69e257b47ac43eff81f..f63046eb326262e4bc389ee7e64a73fe6c4d6376 100644
--- a/Graphics/Iso.cpp
+++ b/Graphics/Iso.cpp
@@ -1,4 +1,4 @@
-// $Id: Iso.cpp,v 1.28 2004-07-02 23:15:04 geuzaine Exp $
+// $Id: Iso.cpp,v 1.29 2004-07-16 18:02:20 geuzaine Exp $
 //
 // Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
 //
@@ -356,7 +356,7 @@ void EnhanceSimplexPolygon(Post_View * View, int nb,    // nb of points in polyg
   if(View->SmoothNormals) {
     if(preproNormals) {
       for(i = 0; i < nb; i++) {
-        View->add_normal(Xp[i], Yp[i], Zp[i], n[0], n[1], n[2]);
+        View->normals->add(Xp[i], Yp[i], Zp[i], n[0], n[1], n[2]);
       }
       return;
     }
@@ -365,8 +365,9 @@ void EnhanceSimplexPolygon(Post_View * View, int nb,    // nb of points in polyg
         norms[3 * i] = n[0];
         norms[3 * i + 1] = n[1];
         norms[3 * i + 2] = n[2];
-        View->get_normal
-	  (Xp[i], Yp[i], Zp[i], norms[3 * i], norms[3 * i + 1], norms[3 * i + 2]);
+        View->normals->get(Xp[i], Yp[i], Zp[i], 
+			   norms[3 * i], norms[3 * i + 1], norms[3 * i + 2],
+			   View->AngleSmoothNormals);
       }
     }
   }
diff --git a/Graphics/Mesh.cpp b/Graphics/Mesh.cpp
index 6642a03afd6295283974c80bdef2f37d0b61284b..cf7d2085a67ac08a1a7f53ed22387be377c68471 100644
--- a/Graphics/Mesh.cpp
+++ b/Graphics/Mesh.cpp
@@ -1,4 +1,4 @@
-// $Id: Mesh.cpp,v 1.100 2004-07-09 18:26:56 geuzaine Exp $
+// $Id: Mesh.cpp,v 1.101 2004-07-16 18:02:20 geuzaine Exp $
 //
 // Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
 //
@@ -235,6 +235,8 @@ void Draw_Mesh_Volume(void *a, void *b)
   Tree_Action(v->Pyramids, Draw_Mesh_Pyramid);
 }
 
+static int preproNormals = 0;
+
 void Draw_Mesh_Surface(void *a, void *b)
 {
   Surface *s = *(Surface **) a;
@@ -245,6 +247,16 @@ void Draw_Mesh_Surface(void *a, void *b)
   theColor = s->Color;
   thePhysical = getFirstPhysical(MSH_PHYSICAL_SURFACE, s->Num);
 
+  if(CTX.mesh.changed && CTX.mesh.smooth_normals){
+    Msg(DEBUG, "pre-processing smooth normals");
+    if(s->normals) delete s->normals;
+    s->normals = new smooth_normals;
+    preproNormals = 1;
+    Tree_Action(s->Simplexes, Draw_Mesh_Triangle);
+    Tree_Action(s->Quadrangles, Draw_Mesh_Quadrangle);
+    preproNormals = 0;
+  }
+
   if(CTX.mesh.vertex_arrays){
     if(CTX.mesh.changed){
       Msg(DEBUG, "regenerate surface mesh vertex arrays");
@@ -454,13 +466,41 @@ void _triFace(double x0, double y0, double z0,
 	      double x1, double y1, double z1,
 	      double x2, double y2, double z2)
 {
-  double n[3];
-  if(CTX.mesh.light){
+  double n[3], ns[3];
+
+  if(CTX.mesh.light || (theSurface && preproNormals)){
     _normal3points(x0, y0, z0, x1, y1, z1, x2, y2, z2, n);
+    if(theSurface && preproNormals){
+      theSurface->normals->add(x0, y0, z0, n[0], n[1], n[2]);
+      theSurface->normals->add(x1, y1, z1, n[0], n[1], n[2]);
+      theSurface->normals->add(x2, y2, z2, n[0], n[1], n[2]);
+      return;
+    }
     glNormal3dv(n);
   }
+
+  if(CTX.mesh.light && theSurface && CTX.mesh.smooth_normals){
+    ns[0] = n[0]; ns[1] = n[1]; ns[2] = n[2];
+    theSurface->normals->get(x0, y0, z0, ns[0], ns[1], ns[2], 
+			     CTX.mesh.angle_smooth_normals);
+    glNormal3dv(ns);    
+  }
   glVertex3d(x0, y0, z0);
+
+  if(CTX.mesh.light && theSurface && CTX.mesh.smooth_normals){
+    ns[0] = n[0]; ns[1] = n[1]; ns[2] = n[2];
+    theSurface->normals->get(x1, y1, z1, ns[0], ns[1], ns[2], 
+			     CTX.mesh.angle_smooth_normals);
+    glNormal3dv(ns);    
+  }
   glVertex3d(x1, y1, z1);
+
+  if(CTX.mesh.light && theSurface && CTX.mesh.smooth_normals){
+    ns[0] = n[0]; ns[1] = n[1]; ns[2] = n[2];
+    theSurface->normals->get(x2, y2, z2, ns[0], ns[1], ns[2], 
+			     CTX.mesh.angle_smooth_normals);
+    glNormal3dv(ns);    
+  }
   glVertex3d(x2, y2, z2);
 }
 
@@ -478,16 +518,52 @@ void _triFace2(double *x, double *y, double *z,
 void _quadFace(double *x, double *y, double *z,
 	       int i0, int i1, int i2, int i3)
 {
-  double n[3];
-  if(CTX.mesh.light){
+  double n[3], ns[3];
+
+  if(CTX.mesh.light || (theSurface && preproNormals)){
     _normal3points(x[i0], y[i0], z[i0],
 		   x[i1], y[i1], z[i1],
 		   x[i2], y[i2], z[i2], n);
+    if(theSurface && preproNormals){
+      theSurface->normals->add(x[i0], y[i0], z[i0], n[0], n[1], n[2]);
+      theSurface->normals->add(x[i1], y[i1], z[i1], n[0], n[1], n[2]);
+      theSurface->normals->add(x[i2], y[i2], z[i2], n[0], n[1], n[2]);
+      theSurface->normals->add(x[i3], y[i3], z[i3], n[0], n[1], n[2]);
+      return;
+    }
     glNormal3dv(n);
   }
+
+  if(CTX.mesh.light && theSurface && CTX.mesh.smooth_normals){
+    ns[0] = n[0]; ns[1] = n[1]; ns[2] = n[2];
+    theSurface->normals->get(x[i0], y[i0], z[i0], ns[0], ns[1], ns[2], 
+			     CTX.mesh.angle_smooth_normals);
+    glNormal3dv(ns);    
+  }
   glVertex3d(x[i0], y[i0], z[i0]);
+
+  if(CTX.mesh.light && theSurface && CTX.mesh.smooth_normals){
+    ns[0] = n[0]; ns[1] = n[1]; ns[2] = n[2];
+    theSurface->normals->get(x[i1], y[i1], z[i1], ns[0], ns[1], ns[2], 
+			     CTX.mesh.angle_smooth_normals);
+    glNormal3dv(ns);    
+  }
   glVertex3d(x[i1], y[i1], z[i1]);
+
+  if(CTX.mesh.light && theSurface && CTX.mesh.smooth_normals){
+    ns[0] = n[0]; ns[1] = n[1]; ns[2] = n[2];
+    theSurface->normals->get(x[i2], y[i2], z[i2], ns[0], ns[1], ns[2], 
+			     CTX.mesh.angle_smooth_normals);
+    glNormal3dv(ns);    
+  }
   glVertex3d(x[i2], y[i2], z[i2]);
+
+  if(CTX.mesh.light && theSurface && CTX.mesh.smooth_normals){
+    ns[0] = n[0]; ns[1] = n[1]; ns[2] = n[2];
+    theSurface->normals->get(x[i3], y[i3], z[i3], ns[0], ns[1], ns[2], 
+			     CTX.mesh.angle_smooth_normals);
+    glNormal3dv(ns);    
+  }
   glVertex3d(x[i3], y[i3], z[i3]);
 }
 
@@ -602,18 +678,24 @@ void Draw_Mesh_Triangle(void *a, void *b)
     }
   }
 
-  if(CTX.mesh.normals || CTX.mesh.light ||
-     (theSurface && theSurface->TriVertexArray && theSurface->TriVertexArray->fill)){
-    _normal3points(X[0], Y[0], Z[0], 
-		   X[1], Y[1], Z[1],
-		   X[2], Y[2], Z[2], n);
-    glNormal3dv(n);
-  }
-
   if(theSurface && theSurface->TriVertexArray){
-    if(theSurface->TriVertexArray->fill){
+    if(preproNormals || theSurface->TriVertexArray->fill)
+      _normal3points(X[0], Y[0], Z[0], 
+		     X[1], Y[1], Z[1],
+		     X[2], Y[2], Z[2], n);
+    if(preproNormals){
       for(int i = 0; i < 3; i++)
-	theSurface->TriVertexArray->add(X[i], Y[i], Z[i], n[0], n[1], n[2], col);
+	theSurface->normals->add(X[i], Y[i], Z[i], n[0], n[1], n[2]);
+      return;
+    }
+    if(theSurface->TriVertexArray->fill){
+      for(int i = 0; i < 3; i++){
+	double ns[3] = {n[0], n[1], n[2]};
+	if(CTX.mesh.smooth_normals)
+	  theSurface->normals->get(X[i], Y[i], Z[i], ns[0], ns[1], ns[2], 
+				   CTX.mesh.angle_smooth_normals);
+	theSurface->TriVertexArray->add(X[i], Y[i], Z[i], ns[0], ns[1], ns[2], col);
+      }
       theSurface->TriVertexArray->num++;
     }
   }    
@@ -637,9 +719,7 @@ void Draw_Mesh_Triangle(void *a, void *b)
       if(CTX.mesh.surfaces_edges) glEnable(GL_POLYGON_OFFSET_FILL);
       if(!s->VSUP) {
 	glBegin(GL_TRIANGLES);
-	glVertex3d(X[0], Y[0], Z[0]);
-	glVertex3d(X[1], Y[1], Z[1]);
-	glVertex3d(X[2], Y[2], Z[2]);
+	_triFace(X[0], Y[0], Z[0], X[1], Y[1], Z[1], X[2], Y[2], Z[2]);
 	glEnd();
       }
       else {
@@ -679,6 +759,9 @@ void Draw_Mesh_Triangle(void *a, void *b)
   }
 
   if(CTX.mesh.normals) {
+    _normal3points(X[0], Y[0], Z[0], 
+		   X[1], Y[1], Z[1],
+		   X[2], Y[2], Z[2], n);
     glColor4ubv((GLubyte *) & CTX.color.mesh.normals);
     n[0] *= CTX.mesh.normals * CTX.pixel_equiv_x / CTX.s[0];
     n[1] *= CTX.mesh.normals * CTX.pixel_equiv_x / CTX.s[1];
@@ -744,18 +827,24 @@ void Draw_Mesh_Quadrangle(void *a, void *b)
     }
   }
 
-  if(CTX.mesh.normals || CTX.mesh.light ||
-     (theSurface && theSurface->QuadVertexArray && theSurface->QuadVertexArray->fill)){
-    _normal3points(X[0], Y[0], Z[0], 
-		   X[1], Y[1], Z[1],
-		   X[2], Y[2], Z[2], n);
-    glNormal3dv(n);
-  }
-
   if(theSurface && theSurface->QuadVertexArray){
-    if(theSurface->QuadVertexArray->fill){
+    if(preproNormals || theSurface->QuadVertexArray->fill)
+      _normal3points(X[0], Y[0], Z[0], 
+		     X[1], Y[1], Z[1],
+		     X[2], Y[2], Z[2], n);
+    if(preproNormals){
       for(int i = 0; i < 4; i++)
-	theSurface->QuadVertexArray->add(X[i], Y[i], Z[i], n[0], n[1], n[2], col);
+	theSurface->normals->add(X[i], Y[i], Z[i], n[0], n[1], n[2]);
+      return;
+    }
+    if(theSurface->QuadVertexArray->fill){
+      for(int i = 0; i < 4; i++){
+	double ns[3] = {n[0], n[1], n[2]};
+	if(CTX.mesh.smooth_normals)
+	  theSurface->normals->get(X[i], Y[i], Z[i], ns[0], ns[1], ns[2], 
+				   CTX.mesh.angle_smooth_normals);
+	theSurface->QuadVertexArray->add(X[i], Y[i], Z[i], ns[0], ns[1], ns[2], col);
+      }
       theSurface->QuadVertexArray->num++;
     }
   }    
@@ -779,10 +868,7 @@ void Draw_Mesh_Quadrangle(void *a, void *b)
       if(CTX.mesh.surfaces_edges) glEnable(GL_POLYGON_OFFSET_FILL);
       if(!q->VSUP) {
 	glBegin(GL_QUADS);
-	glVertex3d(X[0], Y[0], Z[0]);
-	glVertex3d(X[1], Y[1], Z[1]);
-	glVertex3d(X[2], Y[2], Z[2]);
-	glVertex3d(X[3], Y[3], Z[3]);
+	_quadFace(X, Y, Z, 0, 1, 2, 3);
 	glEnd();
       }
       else {
@@ -823,6 +909,9 @@ void Draw_Mesh_Quadrangle(void *a, void *b)
 
   if(CTX.mesh.normals) {
     glColor4ubv((GLubyte *) & CTX.color.mesh.normals);
+    _normal3points(X[0], Y[0], Z[0], 
+		   X[1], Y[1], Z[1],
+		   X[2], Y[2], Z[2], n);
     n[0] *= CTX.mesh.normals * CTX.pixel_equiv_x / CTX.s[0];
     n[1] *= CTX.mesh.normals * CTX.pixel_equiv_x / CTX.s[1];
     n[2] *= CTX.mesh.normals * CTX.pixel_equiv_x / CTX.s[2];
diff --git a/Graphics/PostElement.cpp b/Graphics/PostElement.cpp
index fa98b16fdde3086856b2ac1ff92e835a8f2e4473..d0eac927b0b0acd6d2d6f00ddfd31a3438a719d1 100644
--- a/Graphics/PostElement.cpp
+++ b/Graphics/PostElement.cpp
@@ -1,4 +1,4 @@
-// $Id: PostElement.cpp,v 1.38 2004-07-09 18:26:56 geuzaine Exp $
+// $Id: PostElement.cpp,v 1.39 2004-07-16 18:02:20 geuzaine Exp $
 //
 // Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
 //
@@ -437,7 +437,7 @@ void Draw_ScalarTriangle(Post_View * View, int preproNormals,
       }
       if(preproNormals){
 	for(int i = 0; i < 3; i++)
-	  View->add_normal(xx[i], yy[i], zz[i], nn[0], nn[1], nn[2]);
+	  View->normals->add(xx[i], yy[i], zz[i], nn[0], nn[1], nn[2]);
 	return;
       }
       for(int i = 0; i < 3; i++) {
@@ -447,7 +447,8 @@ void Draw_ScalarTriangle(Post_View * View, int preproNormals,
       }
       if(View->SmoothNormals)
 	for(int i = 0; i < 3; i++)
-	  View->get_normal(xx[i], yy[i], zz[i], norms[3*i], norms[3*i+1], norms[3*i+2]);
+	  View->normals->get(xx[i], yy[i], zz[i], norms[3*i], norms[3*i+1], norms[3*i+2],
+			     View->AngleSmoothNormals);
      
       if(View->TriVertexArray && View->TriVertexArray->fill){
 	unsigned int col;
@@ -482,7 +483,7 @@ void Draw_ScalarTriangle(Post_View * View, int preproNormals,
 	}
 	if(preproNormals){
 	  for(int i = 0; i < nb; i++)
-	    View->add_normal(xx[i], yy[i], zz[i], nn[0], nn[1], nn[2]);
+	    View->normals->add(xx[i], yy[i], zz[i], nn[0], nn[1], nn[2]);
 	  return;
 	}
 	for(int i = 0; i < nb; i++) {
@@ -492,7 +493,8 @@ void Draw_ScalarTriangle(Post_View * View, int preproNormals,
 	}
 	if(View->SmoothNormals)
 	  for(int i = 0; i < nb; i++)
-	    View->get_normal(xx[i], yy[i], zz[i], norms[3*i], norms[3*i+1], norms[3*i+2]);
+	    View->normals->get(xx[i], yy[i], zz[i], norms[3*i], norms[3*i+1], norms[3*i+2],
+			       View->AngleSmoothNormals);
 	
 	if(View->TriVertexArray && View->TriVertexArray->fill){
 	  for(int i = 2; i < nb; i++) {
@@ -543,7 +545,7 @@ void Draw_ScalarTriangle(Post_View * View, int preproNormals,
 	}
 	if(preproNormals){
 	  for(int i = 0; i < nb; i++)
-	    View->add_normal(xx[i], yy[i], zz[i], nn[0], nn[1], nn[2]);
+	    View->normals->add(xx[i], yy[i], zz[i], nn[0], nn[1], nn[2]);
 	}
 	else{
 	  for(int i = 0; i < nb; i++) {
@@ -553,7 +555,8 @@ void Draw_ScalarTriangle(Post_View * View, int preproNormals,
 	  }
 	  if(View->SmoothNormals)
 	    for(int i = 0; i < nb; i++)
-	      View->get_normal(xx[i], yy[i], zz[i], norms[3*i], norms[3*i+1], norms[3*i+2]);
+	      View->normals->get(xx[i], yy[i], zz[i], norms[3*i], norms[3*i+1], norms[3*i+2],
+				 View->AngleSmoothNormals);
 
 	  if(View->TriVertexArray && View->TriVertexArray->fill){
 	    for(int i = 2; i < nb; i++) {
diff --git a/Mesh/Create.cpp b/Mesh/Create.cpp
index 0edf45c0903e01274ee76cbdcb27a378eb47e506..cdbcfb3015328f2dbc6c430486a9c5e8d7e52b17 100644
--- a/Mesh/Create.cpp
+++ b/Mesh/Create.cpp
@@ -1,4 +1,4 @@
-// $Id: Create.cpp,v 1.61 2004-06-28 19:00:22 geuzaine Exp $
+// $Id: Create.cpp,v 1.62 2004-07-16 18:02:20 geuzaine Exp $
 //
 // Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
 //
@@ -671,6 +671,7 @@ Surface *Create_Surface(int Num, int Typ)
   pS->STL = NULL;
   pS->TriVertexArray = NULL;
   pS->QuadVertexArray = NULL;
+  pS->normals = new smooth_normals;
   return (pS);
 }
 
@@ -697,6 +698,8 @@ void Free_Surface(void *a, void *b)
       delete pS->TriVertexArray;
     if(pS->QuadVertexArray)
       delete pS->QuadVertexArray;
+    if(pS->normals)
+      delete pS->normals;
     Free(pS);
     pS = NULL;
   }
diff --git a/Mesh/Mesh.h b/Mesh/Mesh.h
index aeb301db909711e4bd3206c19789206e3cbafbf8..487b5689e169f03a1d233de828c7e0b6fd402ab5 100644
--- a/Mesh/Mesh.h
+++ b/Mesh/Mesh.h
@@ -29,6 +29,7 @@
 #include "ExtrudeParams.h"
 #include "STL.h"
 #include "VertexArray.h"
+#include "SmoothNormals.h"
 
 #define FORMAT_MSH           1
 #define FORMAT_UNV           2
@@ -258,6 +259,7 @@ struct _Surf{
   DrawingColor Color;
   VertexArray * TriVertexArray;
   VertexArray * QuadVertexArray;
+  smooth_normals * normals;
 };
 
 typedef struct _Surf Surface;
diff --git a/Mesh/Utils.cpp b/Mesh/Utils.cpp
index 951cb44df04889849ba5eb4aba76ee28a75bc73b..6d2ecd433e93935d1de4c89952f051a6ba4f22c4 100644
--- a/Mesh/Utils.cpp
+++ b/Mesh/Utils.cpp
@@ -1,4 +1,4 @@
-// $Id: Utils.cpp,v 1.25 2004-04-18 03:23:02 geuzaine Exp $
+// $Id: Utils.cpp,v 1.26 2004-07-16 18:02:20 geuzaine Exp $
 //
 // Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
 //
@@ -281,15 +281,15 @@ void find_bestuv(Surface * s, double X, double Y,
   *V = minv;
 }
 
-void invert_singular_matrix3x3(double _M[3][3], double _I[3][3])
+void invert_singular_matrix3x3(double MM[3][3], double II[3][3])
 {
   int i, j, k, n = 3;
-  double _T[3][3];
+  double TT[3][3];
 
   for(i = 1; i <= n; i++) {
     for(j = 1; j <= n; j++) {
-      _I[i - 1][j - 1] = 0.0;
-      _T[i - 1][j - 1] = 0.0;
+      II[i - 1][j - 1] = 0.0;
+      TT[i - 1][j - 1] = 0.0;
     }
   }
 
@@ -300,7 +300,7 @@ void invert_singular_matrix3x3(double _M[3][3], double _I[3][3])
   gsl_vector *TMPVEC = gsl_vector_alloc(3);
   for(i = 1; i <= n; i++) {
     for(j = 1; j <= n; j++) {
-      gsl_matrix_set(M, i - 1, j - 1, _M[i - 1][j - 1]);
+      gsl_matrix_set(M, i - 1, j - 1, MM[i - 1][j - 1]);
     }
   }
   gsl_linalg_SV_decomp(M, V, W, TMPVEC);
@@ -308,15 +308,15 @@ void invert_singular_matrix3x3(double _M[3][3], double _I[3][3])
     for(j = 1; j <= n; j++) {
       double ww = gsl_vector_get(W, i - 1);
       if(fabs(ww) > 1.e-16) {   //singular value precision
-        _T[i - 1][j - 1] += gsl_matrix_get(M, j - 1, i - 1) / ww;
+        TT[i - 1][j - 1] += gsl_matrix_get(M, j - 1, i - 1) / ww;
       }
     }
   }
   for(i = 1; i <= n; i++) {
     for(j = 1; j <= n; j++) {
       for(k = 1; k <= n; k++) {
-        _I[i - 1][j - 1] +=
-          gsl_matrix_get(V, i - 1, k - 1) * _T[k - 1][j - 1];
+        II[i - 1][j - 1] +=
+          gsl_matrix_get(V, i - 1, k - 1) * TT[k - 1][j - 1];
       }
     }
   }
@@ -330,21 +330,21 @@ void invert_singular_matrix3x3(double _M[3][3], double _I[3][3])
   double *W = dvector(1, 3);
   for(i = 1; i <= n; i++) {
     for(j = 1; j <= n; j++) {
-      M[i][j] = _M[i - 1][j - 1];
+      M[i][j] = MM[i - 1][j - 1];
     }
   }
   dsvdcmp(M, n, n, W, V);
   for(i = 1; i <= n; i++) {
     for(j = 1; j <= n; j++) {
       if(fabs(W[i]) > 1.e-16) { //singular value precision
-        _T[i - 1][j - 1] += M[j][i] / W[i];
+        TT[i - 1][j - 1] += M[j][i] / W[i];
       }
     }
   }
   for(i = 1; i <= n; i++) {
     for(j = 1; j <= n; j++) {
       for(k = 1; k <= n; k++) {
-        _I[i - 1][j - 1] += V[i][k] * _T[k - 1][j - 1];
+        II[i - 1][j - 1] += V[i][k] * TT[k - 1][j - 1];
       }
     }
   }
diff --git a/doc/VERSIONS b/doc/VERSIONS
index 713fb8eed82a0863272b9b281905fb12723643b7..0387387e360d7620a8b6a60eb2e7f26e8cf86827 100644
--- a/doc/VERSIONS
+++ b/doc/VERSIONS
@@ -1,7 +1,8 @@
-$Id: VERSIONS,v 1.235 2004-07-14 22:42:27 geuzaine Exp $
+$Id: VERSIONS,v 1.236 2004-07-16 18:02:20 geuzaine Exp $
 
-New since 1.54: added background mesh support for Triangle; small bug
-fixes.
+New since 1.54: added background mesh support for Triangle; meshes can
+now be displayed using "smoothed" normals (like post-processing
+views); small bug fixes.
 
 New in 1.54: integrated Netgen (3D mesh quality optimization +
 alternative 3D algorithm); Extrude Surface now always automatically