From 39a65d62b16a26f243cf629d22b38239facc4832 Mon Sep 17 00:00:00 2001
From: Christophe Geuzaine <cgeuzaine@ulg.ac.be>
Date: Sat, 12 Mar 2005 00:59:42 +0000
Subject: [PATCH] - added an option to force the size/position of the 3D
 grids/axes

- cleaned-up the whole size/position thing of 2D/3D grids: it's much
  more consistent now (and the GUI is nicer)

- rewrote the algorithm for automatic positionning of 2D graphs (now
  they rescale with the window, which is much nicer--and far more
  practical)
---
 Common/DefaultOptions.h      |  42 ++++--
 Common/Options.cpp           | 244 ++++++++++++++++++++++++++++++-----
 Common/Options.h             |  21 ++-
 Common/Views.cpp             |  22 ++--
 Common/Views.h               |  12 +-
 Fltk/Callbacks.cpp           | 107 ++++++++++++---
 Fltk/GUI.cpp                 | 113 ++++++++++------
 Graphics/Draw.h              |   3 +-
 Graphics/Entity.cpp          |  42 +++---
 Graphics/Graph2D.cpp         | 148 ++++++++++-----------
 Graphics/Post.cpp            |  21 +--
 Graphics/Scale.cpp           |  16 +--
 doc/VERSIONS                 |   9 +-
 doc/texinfo/opt_general.texi |  10 ++
 doc/texinfo/opt_view.texi    |  87 +++++++++++--
 15 files changed, 661 insertions(+), 236 deletions(-)

diff --git a/Common/DefaultOptions.h b/Common/DefaultOptions.h
index 4871a25fab..c11b3f7063 100644
--- a/Common/DefaultOptions.h
+++ b/Common/DefaultOptions.h
@@ -363,10 +363,18 @@ StringXString PostProcessingOptions_String[] = {
 } ;
 
 StringXString ViewOptions_String[] = {
-  { F|O, "AbscissaName" , opt_view_abscissa_name , "" , 
-    "Abscissa name for 2D graphs" },
-  { F|O, "AbscissaFormat" , opt_view_abscissa_format , "%.3g" , 
-    "Abscissa number format (in standard C form)" },
+  { F|O, "AxesFormatX" , opt_view_axes_format0 , "%.3g" , 
+    "Number format for X-axis (in standard C form)" },
+  { F|O, "AxesFormatY" , opt_view_axes_format1 , "%.3g" , 
+    "Number format for Y-axis (in standard C form)" },
+  { F|O, "AxesFormatZ" , opt_view_axes_format2 , "%.3g" , 
+    "Number format for Z-axis (in standard C form)" },
+  { F|O, "AxesLabelX" , opt_view_axes_label0 , "" , 
+    "X-axis label" },
+  { F|O, "AxesLabelY" , opt_view_axes_label1 , "" , 
+    "Y-axis label" },
+  { F|O, "AxesLabelZ" , opt_view_axes_label2 , "" , 
+    "Z-axis label" },
 
   { F,   "FileName" , opt_view_filename , "" , 
     "Default post-processing view file name" },
@@ -1012,8 +1020,10 @@ StringXNumber ViewOptions_Number[] = {
     "Relative length of arrow stem" },
   { F|O, "ArrowStemRadius" , opt_view_arrow_stem_radius , 0.02 ,
     "Relative radius of arrow stem" },
-  { F|O, "AutoPosition" , opt_view_auto_position , 1. , 
-    "Position the scale or the 2D graph automatically to avoid overlaps" }, 
+  { F|O, "AutoPosition" , opt_view_auto_position2d , 1. , 
+    "Position the scale or 2D graph automatically" }, 
+  { F|O, "AutoPosition3D" , opt_view_auto_position3d , 1. , 
+    "Position the 3D grid automatically" }, 
 
   { F|O, "Boundary" , opt_view_boundary , 0. ,
     "Draw the `N minus b'-dimensional boundary of the element (N=element dimension, b=option value)" },
@@ -1116,10 +1126,14 @@ StringXNumber ViewOptions_Number[] = {
   { F,   "MinZ" , opt_view_zmin , 0. , 
     "Minimum view coordinate along the Z-axis (read-only)" }, 
 
-  { F|O, "NbAbscissa" , opt_view_nb_abscissa , 5. ,
-    "Number of tics on the grid axes" },
   { F|O, "NbIso" , opt_view_nb_iso , 15. ,
     "Number of intervals" },
+  { F|O, "NbTicsX" , opt_view_nb_tics0 , 5. ,
+    "Number of tics on the X-axis" },
+  { F|O, "NbTicsY" , opt_view_nb_tics1 , 5. ,
+    "Number of tics on the Y-axis" },
+  { F|O, "NbTicsZ" , opt_view_nb_tics2 , 5. ,
+    "Number of tics on the Z-axis" },
   { F,   "NbTimeStep" , opt_view_nb_timestep , 1. ,
     "Number of time steps in the view (do not change this!)" },
   { F|O, "Normals" , opt_view_normals , 0. ,
@@ -1136,6 +1150,18 @@ StringXNumber ViewOptions_Number[] = {
     "Display size of points (in pixels)" },
   { F|O, "PointType" , opt_view_point_type , 0. , 
     "Display points as solid color dots (0) or 3D spheres (1)" },
+  { F,   "PositionMaxX" , opt_view_position_xmax , 1. , 
+    "Maximum grid coordinate along the X-axis" }, 
+  { F,   "PositionMaxY" , opt_view_position_ymax , 1. , 
+    "Maximum grid coordinate along the Y-axis" }, 
+  { F,   "PositionMaxZ" , opt_view_position_zmax , 1. , 
+    "Maximum grid coordinate along the Z-axis" }, 
+  { F,   "PositionMinX" , opt_view_position_xmin , 0. , 
+    "Minimum grid coordinate along the X-axis" }, 
+  { F,   "PositionMinY" , opt_view_position_ymin , 0. , 
+    "Minimum grid coordinate along the Y-axis" }, 
+  { F,   "PositionMinZ" , opt_view_position_zmin , 0. , 
+    "Minimum grid coordinate along the Z-axis" }, 
   { F|O, "PositionX" , opt_view_position0 , 100. , 
     "Horizontal position (in pixels) of the upper left corner of the scale or 2D graph" }, 
   { F|O, "PositionY" , opt_view_position1 , 50. , 
diff --git a/Common/Options.cpp b/Common/Options.cpp
index 12e17092a0..b9a56d5aa1 100644
--- a/Common/Options.cpp
+++ b/Common/Options.cpp
@@ -1,4 +1,4 @@
-// $Id: Options.cpp,v 1.229 2005-03-11 08:56:37 geuzaine Exp $
+// $Id: Options.cpp,v 1.230 2005-03-12 00:59:40 geuzaine Exp $
 //
 // Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
 //
@@ -1846,30 +1846,82 @@ char *opt_view_filename(OPT_ARGS_STR)
   return v->FileName;
 }
 
-char *opt_view_abscissa_name(OPT_ARGS_STR)
+char *opt_view_axes_label0(OPT_ARGS_STR)
 {
   GET_VIEW("");
   if(action & GMSH_SET) {
-    strcpy(v->AbscissaName, val);
+    strcpy(v->AxesLabel[0], val);
   }
 #if defined(HAVE_FLTK)
   if(_gui_action_valid(action, num))
-    WID->view_input[2]->value(v->AbscissaName);
+    WID->view_input[10]->value(v->AxesLabel[0]);
 #endif
-  return v->AbscissaName;
+  return v->AxesLabel[0];
 }
 
-char *opt_view_abscissa_format(OPT_ARGS_STR)
+char *opt_view_axes_label1(OPT_ARGS_STR)
 {
   GET_VIEW("");
   if(action & GMSH_SET) {
-    strcpy(v->AbscissaFormat, val);
+    strcpy(v->AxesLabel[1], val);
   }
 #if defined(HAVE_FLTK)
   if(_gui_action_valid(action, num))
-    WID->view_input[3]->value(v->AbscissaFormat);
+    WID->view_input[11]->value(v->AxesLabel[1]);
 #endif
-  return v->AbscissaFormat;
+  return v->AxesLabel[1];
+}
+
+char *opt_view_axes_label2(OPT_ARGS_STR)
+{
+  GET_VIEW("");
+  if(action & GMSH_SET) {
+    strcpy(v->AxesLabel[2], val);
+  }
+#if defined(HAVE_FLTK)
+  if(_gui_action_valid(action, num))
+    WID->view_input[12]->value(v->AxesLabel[2]);
+#endif
+  return v->AxesLabel[2];
+}
+
+char *opt_view_axes_format0(OPT_ARGS_STR)
+{
+  GET_VIEW("");
+  if(action & GMSH_SET) {
+    strcpy(v->AxesFormat[0], val);
+  }
+#if defined(HAVE_FLTK)
+  if(_gui_action_valid(action, num))
+    WID->view_input[7]->value(v->AxesFormat[0]);
+#endif
+  return v->AxesFormat[0];
+}
+
+char *opt_view_axes_format1(OPT_ARGS_STR)
+{
+  GET_VIEW("");
+  if(action & GMSH_SET) {
+    strcpy(v->AxesFormat[1], val);
+  }
+#if defined(HAVE_FLTK)
+  if(_gui_action_valid(action, num))
+    WID->view_input[8]->value(v->AxesFormat[1]);
+#endif
+  return v->AxesFormat[1];
+}
+
+char *opt_view_axes_format2(OPT_ARGS_STR)
+{
+  GET_VIEW("");
+  if(action & GMSH_SET) {
+    strcpy(v->AxesFormat[2], val);
+  }
+#if defined(HAVE_FLTK)
+  if(_gui_action_valid(action, num))
+    WID->view_input[9]->value(v->AxesFormat[2]);
+#endif
+  return v->AxesFormat[2];
 }
 
 char * opt_view_gen_raise0(OPT_ARGS_STR)
@@ -5080,70 +5132,168 @@ double opt_view_type(OPT_ARGS_NUM)
   return v->Type;
 }
 
+double opt_view_auto_position2d(OPT_ARGS_NUM)
+{
+  GET_VIEW(0.);
+  if(action & GMSH_SET) {
+    v->AutoPosition2D = (int)val;
+  }
+#if defined(HAVE_FLTK)
+  if(_gui_action_valid(action, num)) {
+    WID->view_butt[7]->value(v->AutoPosition2D);
+  }
+#endif
+  return v->AutoPosition2D;
+}
+
 double opt_view_position0(OPT_ARGS_NUM)
 {
   GET_VIEW(0.);
   if(action & GMSH_SET) {
-    v->Position[0] = (int)val;
+    v->Position2D[0] = (int)val;
   }
 #if defined(HAVE_FLTK)
   if(_gui_action_valid(action, num))
-    WID->view_value[20]->value(v->Position[0]);
+    WID->view_value[20]->value(v->Position2D[0]);
 #endif
-  return v->Position[0];
+  return v->Position2D[0];
 }
 
 double opt_view_position1(OPT_ARGS_NUM)
 {
   GET_VIEW(0.);
   if(action & GMSH_SET) {
-    v->Position[1] = (int)val;
+    v->Position2D[1] = (int)val;
   }
 #if defined(HAVE_FLTK)
   if(_gui_action_valid(action, num))
-    WID->view_value[21]->value(v->Position[1]);
+    WID->view_value[21]->value(v->Position2D[1]);
 #endif
-  return v->Position[1];
+  return v->Position2D[1];
 }
 
-double opt_view_auto_position(OPT_ARGS_NUM)
+double opt_view_size0(OPT_ARGS_NUM)
 {
   GET_VIEW(0.);
   if(action & GMSH_SET) {
-    v->AutoPosition = (int)val;
+    v->Size2D[0] = (int)val;
+  }
+#if defined(HAVE_FLTK)
+  if(_gui_action_valid(action, num))
+    WID->view_value[22]->value(v->Size2D[0]);
+#endif
+  return v->Size2D[0];
+}
+
+double opt_view_size1(OPT_ARGS_NUM)
+{
+  GET_VIEW(0.);
+  if(action & GMSH_SET) {
+    v->Size2D[1] = (int)val;
+  }
+#if defined(HAVE_FLTK)
+  if(_gui_action_valid(action, num))
+    WID->view_value[23]->value(v->Size2D[1]);
+#endif
+  return v->Size2D[1];
+}
+
+double opt_view_auto_position3d(OPT_ARGS_NUM)
+{
+  GET_VIEW(0.);
+  if(action & GMSH_SET) {
+    v->AutoPosition3D = (int)val;
   }
 #if defined(HAVE_FLTK)
   if(_gui_action_valid(action, num)) {
-    WID->view_butt[7]->value(v->AutoPosition);
+    WID->view_butt[25]->value(v->AutoPosition3D);
   }
 #endif
-  return v->AutoPosition;
+  return v->AutoPosition3D;
 }
 
-double opt_view_size0(OPT_ARGS_NUM)
+double opt_view_position_xmin(OPT_ARGS_NUM)
 {
   GET_VIEW(0.);
   if(action & GMSH_SET) {
-    v->Size[0] = (int)val;
+    v->Position3D[0] = val;
   }
 #if defined(HAVE_FLTK)
-  if(_gui_action_valid(action, num))
-    WID->view_value[22]->value(v->Size[0]);
+  if(_gui_action_valid(action, num)) {
+    WID->view_value[13]->value(v->Position3D[0]);
+  }
 #endif
-  return v->Size[0];
+  return v->Position3D[0];
 }
 
-double opt_view_size1(OPT_ARGS_NUM)
+double opt_view_position_xmax(OPT_ARGS_NUM)
 {
   GET_VIEW(0.);
   if(action & GMSH_SET) {
-    v->Size[1] = (int)val;
+    v->Position3D[1] = val;
   }
 #if defined(HAVE_FLTK)
-  if(_gui_action_valid(action, num))
-    WID->view_value[23]->value(v->Size[1]);
+  if(_gui_action_valid(action, num)) {
+    WID->view_value[16]->value(v->Position3D[1]);
+  }
+#endif
+  return v->Position3D[1];
+}
+
+double opt_view_position_ymin(OPT_ARGS_NUM)
+{
+  GET_VIEW(0.);
+  if(action & GMSH_SET) {
+    v->Position3D[2] = val;
+  }
+#if defined(HAVE_FLTK)
+  if(_gui_action_valid(action, num)) {
+    WID->view_value[14]->value(v->Position3D[2]);
+  }
 #endif
-  return v->Size[1];
+  return v->Position3D[2];
+}
+
+double opt_view_position_ymax(OPT_ARGS_NUM)
+{
+  GET_VIEW(0.);
+  if(action & GMSH_SET) {
+    v->Position3D[3] = val;
+  }
+#if defined(HAVE_FLTK)
+  if(_gui_action_valid(action, num)) {
+    WID->view_value[17]->value(v->Position3D[3]);
+  }
+#endif
+  return v->Position3D[3];
+}
+
+double opt_view_position_zmin(OPT_ARGS_NUM)
+{
+  GET_VIEW(0.);
+  if(action & GMSH_SET) {
+    v->Position3D[4] = val;
+  }
+#if defined(HAVE_FLTK)
+  if(_gui_action_valid(action, num)) {
+    WID->view_value[15]->value(v->Position3D[4]);
+  }
+#endif
+  return v->Position3D[4];
+}
+
+double opt_view_position_zmax(OPT_ARGS_NUM)
+{
+  GET_VIEW(0.);
+  if(action & GMSH_SET) {
+    v->Position3D[5] = val;
+  }
+#if defined(HAVE_FLTK)
+  if(_gui_action_valid(action, num)) {
+    WID->view_value[18]->value(v->Position3D[5]);
+  }
+#endif
+  return v->Position3D[5];
 }
 
 double opt_view_grid(OPT_ARGS_NUM)
@@ -5162,18 +5312,46 @@ double opt_view_grid(OPT_ARGS_NUM)
   return v->Grid;
 }
 
-double opt_view_nb_abscissa(OPT_ARGS_NUM)
+double opt_view_nb_tics0(OPT_ARGS_NUM)
+{
+  GET_VIEW(0.);
+  if(action & GMSH_SET) {
+    v->NbTics[0] = (int)val;
+  }
+#if defined(HAVE_FLTK)
+  if(_gui_action_valid(action, num)) {
+    WID->view_value[3]->value(v->NbTics[0]);
+  }
+#endif
+  return v->NbTics[0];
+}
+
+double opt_view_nb_tics1(OPT_ARGS_NUM)
+{
+  GET_VIEW(0.);
+  if(action & GMSH_SET) {
+    v->NbTics[1] = (int)val;
+  }
+#if defined(HAVE_FLTK)
+  if(_gui_action_valid(action, num)) {
+    WID->view_value[4]->value(v->NbTics[1]);
+  }
+#endif
+  return v->NbTics[1];
+}
+
+double opt_view_nb_tics2(OPT_ARGS_NUM)
 {
   GET_VIEW(0.);
   if(action & GMSH_SET) {
-    v->NbAbscissa = (int)val;
+    v->NbTics[2] = (int)val;
   }
 #if defined(HAVE_FLTK)
   if(_gui_action_valid(action, num)) {
-    WID->view_value[25]->value(v->NbAbscissa);
+    WID->view_value[5]->value(v->NbTics[2]);
   }
 #endif
-  return v->NbAbscissa;
+  return v->NbTics[2];
 }
 
 double opt_view_nb_iso(OPT_ARGS_NUM)
diff --git a/Common/Options.h b/Common/Options.h
index 507ac7b367..13607febdc 100644
--- a/Common/Options.h
+++ b/Common/Options.h
@@ -195,8 +195,12 @@ char * opt_solver_fifth_button_command4(OPT_ARGS_STR);
 char * opt_view_name(OPT_ARGS_STR);
 char * opt_view_format(OPT_ARGS_STR);
 char * opt_view_filename(OPT_ARGS_STR);
-char * opt_view_abscissa_name(OPT_ARGS_STR);
-char * opt_view_abscissa_format(OPT_ARGS_STR);
+char * opt_view_axes_label0(OPT_ARGS_STR);
+char * opt_view_axes_label1(OPT_ARGS_STR);
+char * opt_view_axes_label2(OPT_ARGS_STR);
+char * opt_view_axes_format0(OPT_ARGS_STR);
+char * opt_view_axes_format1(OPT_ARGS_STR);
+char * opt_view_axes_format2(OPT_ARGS_STR);
 char * opt_view_gen_raise0(OPT_ARGS_STR);
 char * opt_view_gen_raise1(OPT_ARGS_STR);
 char * opt_view_gen_raise2(OPT_ARGS_STR);
@@ -521,13 +525,22 @@ double opt_view_gen_raise_factor(OPT_ARGS_NUM);
 double opt_view_use_gen_raise(OPT_ARGS_NUM);
 double opt_view_type(OPT_ARGS_NUM);
 double opt_view_grid(OPT_ARGS_NUM);
+double opt_view_position_xmin(OPT_ARGS_NUM);
+double opt_view_position_ymin(OPT_ARGS_NUM);
+double opt_view_position_zmin(OPT_ARGS_NUM);
+double opt_view_position_xmax(OPT_ARGS_NUM);
+double opt_view_position_ymax(OPT_ARGS_NUM);
+double opt_view_position_zmax(OPT_ARGS_NUM);
 double opt_view_position0(OPT_ARGS_NUM);
 double opt_view_position1(OPT_ARGS_NUM);
-double opt_view_auto_position(OPT_ARGS_NUM);
+double opt_view_auto_position2d(OPT_ARGS_NUM);
+double opt_view_auto_position3d(OPT_ARGS_NUM);
 double opt_view_size0(OPT_ARGS_NUM);
 double opt_view_size1(OPT_ARGS_NUM);
 double opt_view_nb_iso(OPT_ARGS_NUM);
-double opt_view_nb_abscissa(OPT_ARGS_NUM);
+double opt_view_nb_tics0(OPT_ARGS_NUM);
+double opt_view_nb_tics1(OPT_ARGS_NUM);
+double opt_view_nb_tics2(OPT_ARGS_NUM);
 double opt_view_boundary(OPT_ARGS_NUM);
 double opt_view_light(OPT_ARGS_NUM);
 double opt_view_light_two_side(OPT_ARGS_NUM);
diff --git a/Common/Views.cpp b/Common/Views.cpp
index 57e883bdb0..2ed7ecef5c 100644
--- a/Common/Views.cpp
+++ b/Common/Views.cpp
@@ -1,4 +1,4 @@
-// $Id: Views.cpp,v 1.165 2005-03-11 05:47:54 geuzaine Exp $
+// $Id: Views.cpp,v 1.166 2005-03-12 00:59:40 geuzaine Exp $
 //
 // Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
 //
@@ -611,14 +611,16 @@ void FreeView(Post_View * v)
 void CopyViewOptions(Post_View * src, Post_View * dest)
 {
   dest->Type = src->Type;
-  dest->Position[0] = src->Position[0];
-  dest->Position[1] = src->Position[1];
-  dest->AutoPosition = src->AutoPosition;
-  dest->Size[0] = src->Size[0];
-  dest->Size[1] = src->Size[1];
+  dest->AutoPosition2D = src->AutoPosition2D;
+  dest->AutoPosition3D = src->AutoPosition3D;
+  dest->Position2D[0] = src->Position2D[0];
+  dest->Position2D[1] = src->Position2D[1];
+  dest->Size2D[0] = src->Size2D[0];
+  dest->Size2D[1] = src->Size2D[1];
+  for(int i = 0; i < 6; i++) dest->Position3D[i] = src->Position3D[i];
   strcpy(dest->Format, src->Format);
-  strcpy(dest->AbscissaFormat, src->AbscissaFormat);
-  strcpy(dest->AbscissaName, src->AbscissaName);
+  for(int i = 0; i < 3; i++) strcpy(dest->AxesFormat[i], src->AxesFormat[i]);
+  for(int i = 0; i < 3; i++) strcpy(dest->AxesLabel[i], src->AxesLabel[i]);
   dest->CustomMin = src->CustomMin;
   dest->CustomMax = src->CustomMax;
   dest->Offset[0] = src->Offset[0];
@@ -643,7 +645,7 @@ void CopyViewOptions(Post_View * src, Post_View * dest)
   dest->IntervalsType = src->IntervalsType;
   dest->SaturateValues = src->SaturateValues;
   dest->Boundary = src->Boundary;
-  dest->NbAbscissa = src->NbAbscissa;
+  for(int i = 0; i < 3; i++) dest->NbTics[i] = src->NbTics[i];
   dest->NbIso = src->NbIso;
   dest->Light = src->Light;
   dest->LightTwoSide = src->LightTwoSide;
@@ -778,7 +780,7 @@ Post_View *Create2DGraph(char *xname, char *yname,
   EndView(v, 1, filename, yname);
   v->Type = DRAW_POST_2D_SPACE;
   v->Grid = 2;
-  strcpy(v->AbscissaName, xname);
+  strcpy(v->AxesLabel[0], xname);
   return v;
 }
 
diff --git a/Common/Views.h b/Common/Views.h
index 9d76e4e8b1..6a85673b5c 100644
--- a/Common/Views.h
+++ b/Common/Views.h
@@ -44,7 +44,7 @@ class Post_View{
  public :
   // intrinsic to a view
   int Num, Index, Changed, AliasOf, Links, Dirty;
-  char FileName[256], Name[256], AbscissaName[256];
+  char FileName[256], Name[256];
 
   // the data
   int DataSize; // size(double) or sizeof(float)
@@ -74,13 +74,15 @@ class Post_View{
   VertexArray *TriVertexArray;
 
   // options
-  int Type, Position[2], AutoPosition, Size[2];
-  char   Format[256], AbscissaFormat[256];
+  int Type;
+  int Position2D[2], Size2D[2], AutoPosition2D, AutoPosition3D;
+  double Position3D[6];
+  char   Format[256], AxesFormat[3][256], AxesLabel[3][256];
   double CustomMin, CustomMax;
   double Offset[3], Raise[3], Transform[3][3], DisplacementFactor, Explode;
   double ArrowSize, ArrowRelHeadRadius, ArrowRelStemRadius, ArrowRelStemLength;
   double Normals, Tangents;
-  int Visible, IntervalsType, NbIso, NbAbscissa, ArrowSizeProportional;
+  int Visible, IntervalsType, NbIso, NbTics[3], ArrowSizeProportional;
   int Light, LightTwoSide, SmoothNormals;
   double AngleSmoothNormals;
   int SaturateValues, FakeTransparency;
@@ -100,7 +102,7 @@ class Post_View{
   double GenRaiseFactor;
   char GenRaiseX[256], GenRaiseY[256], GenRaiseZ[256];
   void *GenRaise_f[3];
-  
+
   // color options
   struct{
     unsigned int point, line, triangle, quadrangle;
diff --git a/Fltk/Callbacks.cpp b/Fltk/Callbacks.cpp
index 67502f8b36..232d558727 100644
--- a/Fltk/Callbacks.cpp
+++ b/Fltk/Callbacks.cpp
@@ -1,4 +1,4 @@
-// $Id: Callbacks.cpp,v 1.342 2005-03-11 09:14:40 geuzaine Exp $
+// $Id: Callbacks.cpp,v 1.343 2005-03-12 00:59:40 geuzaine Exp $
 //
 // Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
 //
@@ -3314,7 +3314,8 @@ void view_options_ok_cb(CALLBACK_ARGS)
   double target_error = opt_view_target_error(current, GMSH_GET, 0);
   double show_element = opt_view_show_element(current, GMSH_GET, 0);
   double show_scale = opt_view_show_scale(current, GMSH_GET, 0);
-  double auto_position = opt_view_auto_position(current, GMSH_GET, 0);
+  double auto_position2d = opt_view_auto_position2d(current, GMSH_GET, 0);
+  double auto_position3d = opt_view_auto_position3d(current, GMSH_GET, 0);
   double show_time = opt_view_show_time(current, GMSH_GET, 0);
   double draw_strings = opt_view_draw_strings(current, GMSH_GET, 0);
   double light = opt_view_light(current, GMSH_GET, 0);
@@ -3366,17 +3367,33 @@ void view_options_ok_cb(CALLBACK_ARGS)
   double position1 = opt_view_position1(current, GMSH_GET, 0);
   double size0 = opt_view_size0(current, GMSH_GET, 0);
   double size1 = opt_view_size1(current, GMSH_GET, 0);
-  double nb_abscissa = opt_view_nb_abscissa(current, GMSH_GET, 0);
+  double nb_tics0 = opt_view_nb_tics0(current, GMSH_GET, 0);
+  double nb_tics1 = opt_view_nb_tics1(current, GMSH_GET, 0);
+  double nb_tics2 = opt_view_nb_tics2(current, GMSH_GET, 0);
+  double position_xmin = opt_view_position_xmin(current, GMSH_GET, 0);
+  double position_ymin = opt_view_position_ymin(current, GMSH_GET, 0);
+  double position_zmin = opt_view_position_zmin(current, GMSH_GET, 0);
+  double position_xmax = opt_view_position_xmax(current, GMSH_GET, 0);
+  double position_ymax = opt_view_position_ymax(current, GMSH_GET, 0);
+  double position_zmax = opt_view_position_zmax(current, GMSH_GET, 0);
   double gen_raise_factor = opt_view_gen_raise_factor(current, GMSH_GET, 0);
 
   char name[256];
   strcpy(name, opt_view_name(current, GMSH_GET, NULL));
   char format[256];
   strcpy(format, opt_view_format(current, GMSH_GET, NULL));
-  char abscissa_name[256];
-  strcpy(abscissa_name, opt_view_abscissa_name(current, GMSH_GET, NULL));
-  char abscissa_format[256];
-  strcpy(abscissa_format, opt_view_abscissa_format(current, GMSH_GET, NULL));
+  char axes_label0[256];
+  strcpy(axes_label0, opt_view_axes_label0(current, GMSH_GET, NULL));
+  char axes_label1[256];
+  strcpy(axes_label1, opt_view_axes_label1(current, GMSH_GET, NULL));
+  char axes_label2[256];
+  strcpy(axes_label2, opt_view_axes_label2(current, GMSH_GET, NULL));
+  char axes_format0[256];
+  strcpy(axes_format0, opt_view_axes_format0(current, GMSH_GET, NULL));
+  char axes_format1[256];
+  strcpy(axes_format1, opt_view_axes_format1(current, GMSH_GET, NULL));
+  char axes_format2[256];
+  strcpy(axes_format2, opt_view_axes_format2(current, GMSH_GET, NULL));
   char gen_raise0[256];
   strcpy(gen_raise0, opt_view_gen_raise0(current, GMSH_GET, NULL));
   char gen_raise1[256];
@@ -3532,8 +3549,12 @@ void view_options_ok_cb(CALLBACK_ARGS)
         opt_view_show_scale(i, GMSH_SET, val);
 
       val = WID->view_butt[7]->value();
-      if(force || (val != auto_position))
-        opt_view_auto_position(i, GMSH_SET, val);
+      if(force || (val != auto_position2d))
+        opt_view_auto_position2d(i, GMSH_SET, val);
+
+      val = WID->view_butt[25]->value();
+      if(force || (val != auto_position3d))
+        opt_view_auto_position3d(i, GMSH_SET, val);
 
       val = WID->view_butt[8]->value();
       if(force || (val != show_time))
@@ -3740,15 +3761,47 @@ void view_options_ok_cb(CALLBACK_ARGS)
       val = WID->view_value[23]->value();
       if(force || (val != size1))
         opt_view_size1(i, GMSH_SET, val);
-      
-      val = WID->view_value[25]->value();
-      if(force || (val != nb_abscissa))
-        opt_view_nb_abscissa(i, GMSH_SET, val);
+
+      val = WID->view_value[13]->value();
+      if(force || (val != position_xmin))
+        opt_view_position_xmin(i, GMSH_SET, val);
+
+      val = WID->view_value[14]->value();
+      if(force || (val != position_ymin))
+        opt_view_position_ymin(i, GMSH_SET, val);
+
+      val = WID->view_value[15]->value();
+      if(force || (val != position_zmin))
+        opt_view_position_zmin(i, GMSH_SET, val);
+
+      val = WID->view_value[16]->value();
+      if(force || (val != position_xmax))
+        opt_view_position_xmax(i, GMSH_SET, val);
+
+      val = WID->view_value[17]->value();
+      if(force || (val != position_ymax))
+        opt_view_position_ymax(i, GMSH_SET, val);
+
+      val = WID->view_value[18]->value();
+      if(force || (val != position_zmax))
+        opt_view_position_zmax(i, GMSH_SET, val);
 
       val = WID->view_value[2]->value();
       if(force || (val != gen_raise_factor))
         opt_view_gen_raise_factor(i, GMSH_SET, val);
 
+      val = WID->view_value[3]->value();
+      if(force || (val != nb_tics0))
+        opt_view_nb_tics0(i, GMSH_SET, val);
+
+      val = WID->view_value[4]->value();
+      if(force || (val != nb_tics1))
+        opt_view_nb_tics1(i, GMSH_SET, val);
+
+      val = WID->view_value[5]->value();
+      if(force || (val != nb_tics2))
+        opt_view_nb_tics2(i, GMSH_SET, val);
+
       // view_inputs
 
       char *str;
@@ -3761,13 +3814,29 @@ void view_options_ok_cb(CALLBACK_ARGS)
       if(force || strcmp(str, format))
         opt_view_format(i, GMSH_SET, str);
 
-      str = (char *)WID->view_input[2]->value();
-      if(force || strcmp(str, abscissa_name))
-        opt_view_abscissa_name(i, GMSH_SET, str);
+      str = (char *)WID->view_input[10]->value();
+      if(force || strcmp(str, axes_label0))
+        opt_view_axes_label0(i, GMSH_SET, str);
+
+      str = (char *)WID->view_input[11]->value();
+      if(force || strcmp(str, axes_label1))
+        opt_view_axes_label1(i, GMSH_SET, str);
+
+      str = (char *)WID->view_input[12]->value();
+      if(force || strcmp(str, axes_label2))
+        opt_view_axes_label2(i, GMSH_SET, str);
+
+      str = (char *)WID->view_input[7]->value();
+      if(force || strcmp(str, axes_format0))
+        opt_view_axes_format0(i, GMSH_SET, str);
+
+      str = (char *)WID->view_input[8]->value();
+      if(force || strcmp(str, axes_format1))
+        opt_view_axes_format1(i, GMSH_SET, str);
 
-      str = (char *)WID->view_input[3]->value();
-      if(force || strcmp(str, abscissa_format))
-        opt_view_abscissa_format(i, GMSH_SET, str);
+      str = (char *)WID->view_input[9]->value();
+      if(force || strcmp(str, axes_format2))
+        opt_view_axes_format2(i, GMSH_SET, str);
 
       str = (char *)WID->view_input[4]->value();
       if(force || strcmp(str, gen_raise0))
diff --git a/Fltk/GUI.cpp b/Fltk/GUI.cpp
index 135dbcbf38..5bfadce50a 100644
--- a/Fltk/GUI.cpp
+++ b/Fltk/GUI.cpp
@@ -1,4 +1,4 @@
-// $Id: GUI.cpp,v 1.423 2005-03-11 09:21:56 geuzaine Exp $
+// $Id: GUI.cpp,v 1.424 2005-03-12 00:59:41 geuzaine Exp $
 //
 // Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
 //
@@ -2507,7 +2507,7 @@ void GUI::create_option_window()
       view_input[0] = new Fl_Input(L + 2 * WB, 2 * WB + 5 * BH, IW, BH, "Name");
       view_input[0]->align(FL_ALIGN_RIGHT);
 
-      view_input[1] = new Fl_Input(L + 2 * WB, 2 * WB + 6 * BH, IW, BH, "Number format");
+      view_input[1] = new Fl_Input(L + 2 * WB, 2 * WB + 6 * BH, IW, BH, "Format");
       view_input[1]->align(FL_ALIGN_RIGHT);
 
       static Fl_Menu_Item menu_grid_mode[] = {
@@ -2518,30 +2518,41 @@ void GUI::create_option_window()
 	{"Open grid", 0, 0, 0},
 	{0}
       };
-      view_choice[8] = new Fl_Choice(L + 2 * WB, 2 * WB + 7 * BH, IW, BH, "Grid mode");
+      view_choice[8] = new Fl_Choice(L + 2 * WB, 2 * WB + 7 * BH, IW, BH, "Grid");
       view_choice[8]->menu(menu_grid_mode);
       view_choice[8]->align(FL_ALIGN_RIGHT);
       view_choice[8]->tooltip("(Alt+g)");
 
-      view_input[3] = new Fl_Input(L + 2 * WB, 2 * WB + 8 * BH, IW, BH, "Grid number format");
-      view_input[3]->align(FL_ALIGN_RIGHT);
+      view_value[3] = new Fl_Value_Input(L + 2 * WB, 2 * WB + 8 * BH, IW/3, BH);
+      view_value[3]->minimum(0.);
+      view_value[3]->step(1);
+      view_value[3]->maximum(100);
+      view_value[4] = new Fl_Value_Input(L + 2 * WB + 1*IW/3, 2 * WB + 8 * BH, IW/3, BH);
+      view_value[4]->minimum(0.);
+      view_value[4]->step(1);
+      view_value[4]->maximum(100);
+      view_value[5] = new Fl_Value_Input(L + 2 * WB + 2*IW/3, 2 * WB + 8 * BH, IW/3, BH, "Axes tics");
+      view_value[5]->minimum(0.);
+      view_value[5]->step(1);
+      view_value[5]->maximum(100);
+      view_value[5]->align(FL_ALIGN_RIGHT);
+
+      view_input[7] = new Fl_Input(L + 2 * WB, 2 * WB + 9 * BH, IW/3, BH);
+      view_input[8] = new Fl_Input(L + 2 * WB + 1*IW/3, 2 * WB + 9 * BH, IW/3, BH);
+      view_input[9] = new Fl_Input(L + 2 * WB + 2*IW/3, 2 * WB + 9 * BH, IW/3, BH, "Axes format");
+      view_input[9]->align(FL_ALIGN_RIGHT);
       
-      view_value[25] = new Fl_Value_Input(L + 2 * WB, 2 * WB + 9 * BH, IW, BH, "Axis tics");
-      view_value[25]->minimum(0.);
-      view_value[25]->step(1);
-      view_value[25]->maximum(256);
-      view_value[25]->align(FL_ALIGN_RIGHT);
-
-      view_input[2] = new Fl_Input(L + 2 * WB, 2 * WB + 10 * BH, IW, BH, "Abscissa name");
-      view_input[2]->align(FL_ALIGN_RIGHT);
+      view_input[10] = new Fl_Input(L + 2 * WB, 2 * WB + 10 * BH, IW/3, BH);
+      view_input[11] = new Fl_Input(L + 2 * WB + 1*IW/3, 2 * WB + 10 * BH, IW/3, BH);
+      view_input[12] = new Fl_Input(L + 2 * WB + 2*IW/3, 2 * WB + 10 * BH, IW/3, BH, "Axes labels");
+      view_input[12]->align(FL_ALIGN_RIGHT);
       
-      view_butt[7] = new Fl_Check_Button(L + width / 2, 2 * WB + 1 * BH, BW / 2 - WB, BH, "Set position automatically");
+      view_butt[7] = new Fl_Check_Button(L + width / 2, 2 * WB + 1 * BH, BW / 2 - WB, BH, "Automatic 2D grid position");
       view_butt[7]->type(FL_TOGGLE_BUTTON);
       view_butt[7]->down_box(GMSH_TOGGLE_BOX);
       view_butt[7]->selection_color(GMSH_TOGGLE_COLOR);
       
-      view_value[20] = new Fl_Value_Input(L + width /2, 2 * WB + 2 * BH, IW / 2, BH);
-      view_value[20]->align(FL_ALIGN_RIGHT);
+      view_value[20] = new Fl_Value_Input(L + width / 2, 2 * WB + 2 * BH, IW / 2, BH);
       view_value[20]->minimum(0);
       view_value[20]->maximum(1024);
       view_value[20]->step(1);
@@ -2551,8 +2562,7 @@ void GUI::create_option_window()
       view_value[21]->maximum(1024);
       view_value[21]->step(1);
 
-      view_value[22] = new Fl_Value_Input(L + width /2, 2 * WB + 3 * BH, IW / 2, BH);
-      view_value[22]->align(FL_ALIGN_RIGHT);
+      view_value[22] = new Fl_Value_Input(L + width / 2, 2 * WB + 3 * BH, IW / 2, BH);
       view_value[22]->minimum(0);
       view_value[22]->maximum(1024);
       view_value[22]->step(1);
@@ -2562,6 +2572,21 @@ void GUI::create_option_window()
       view_value[23]->maximum(1024);
       view_value[23]->step(1);
 
+      view_butt[25] = new Fl_Check_Button(L + width / 2, 2 * WB + 4 * BH, BW / 2 - WB, BH, "Automatic 3D grid position");
+      view_butt[25]->type(FL_TOGGLE_BUTTON);
+      view_butt[25]->down_box(GMSH_TOGGLE_BOX);
+      view_butt[25]->selection_color(GMSH_TOGGLE_COLOR);
+      
+      view_value[13] = new Fl_Value_Input(L + width / 2, 2 * WB + 5 * BH, IW / 3, BH);
+      view_value[14] = new Fl_Value_Input(L + width / 2 + IW / 3, 2 * WB + 5 * BH, IW / 3, BH);
+      view_value[15] = new Fl_Value_Input(L + width / 2 + 2 * IW / 3, 2 * WB + 5 * BH, IW / 3, BH, "Minimum");
+      view_value[15]->align(FL_ALIGN_RIGHT);
+
+      view_value[16] = new Fl_Value_Input(L + width / 2, 2 * WB + 6 * BH, IW / 3, BH);
+      view_value[17] = new Fl_Value_Input(L + width / 2 + IW / 3, 2 * WB + 6 * BH, IW / 3, BH);
+      view_value[18] = new Fl_Value_Input(L + width / 2 + 2 * IW / 3, 2 * WB + 6 * BH, IW / 3, BH, "Maximum");
+      view_value[18]->align(FL_ALIGN_RIGHT);
+
       o->end();
     }
     {
@@ -2723,7 +2748,7 @@ void GUI::create_option_window()
       view_butt[38]->down_box(GMSH_TOGGLE_BOX);
       view_butt[38]->selection_color(GMSH_TOGGLE_COLOR);
 
-      view_value[33] = new Fl_Value_Input(L + 2 * WB, 2 * WB + 8 * BH, IW, BH, "Maximal recursion level");
+      view_value[33] = new Fl_Value_Input(L + 2 * WB, 2 * WB + 8 * BH, IW, BH, "Maximum recursion level");
       view_value[33]->align(FL_ALIGN_RIGHT);
       view_value[33]->minimum(0);
       view_value[33]->maximum(MAX_LEVEL_OF_ZOOM);
@@ -2978,20 +3003,17 @@ void GUI::update_view_window(int num)
   opt_view_type(num, GMSH_GUI, 0);
   opt_view_show_scale(num, GMSH_GUI, 0);
   opt_view_draw_strings(num, GMSH_GUI, 0);
-  opt_view_auto_position(num, GMSH_GUI, 0);
 
   opt_view_max_recursion_level (num, GMSH_GUI, 0);
   opt_view_target_error (num, GMSH_GUI, 0);
-  if(v->adaptive)
-    {
-      view_value[33]->activate();
-      view_value[34]->activate();
-    }
-  else
-    {
-      view_value[33]->deactivate();
-      view_value[34]->deactivate();
-    }
+  if(v->adaptive){
+    view_value[33]->activate();
+    view_value[34]->activate();
+  }
+  else{
+    view_value[33]->deactivate();
+    view_value[34]->deactivate();
+  }
 
   if(v->NbSP) {
     view_butt[2]->activate();
@@ -3001,10 +3023,24 @@ void GUI::update_view_window(int num)
     view_butt[2]->deactivate();
     view_butt[3]->deactivate();
   }
+
+  opt_view_auto_position2d(num, GMSH_GUI, 0);
   opt_view_position0(num, GMSH_GUI, 0);
   opt_view_position1(num, GMSH_GUI, 0);
   opt_view_size0(num, GMSH_GUI, 0);
   opt_view_size1(num, GMSH_GUI, 0);
+  opt_view_auto_position3d(num, GMSH_GUI, 0);
+  opt_view_position_xmin(num, GMSH_GUI, 0);
+  opt_view_position_xmax(num, GMSH_GUI, 0);
+  opt_view_position_ymin(num, GMSH_GUI, 0);
+  opt_view_position_ymax(num, GMSH_GUI, 0);
+  opt_view_position_zmin(num, GMSH_GUI, 0);
+  opt_view_position_zmax(num, GMSH_GUI, 0);
+  for(int i = 13; i <= 18; i++){
+    view_value[i]->step(CTX.lc/200.);
+    view_value[i]->minimum(-CTX.lc);
+    view_value[i]->maximum(CTX.lc);
+  }
 
   if(v->TextOnly) {
     view_range->deactivate();
@@ -3036,14 +3072,15 @@ void GUI::update_view_window(int num)
   opt_view_tangents(num, GMSH_GUI, 0);
 
   opt_view_grid(num, GMSH_GUI, 0);
-  opt_view_abscissa_format(num, GMSH_GUI, NULL);
-  opt_view_nb_abscissa(num, GMSH_GUI, 0);
-
-  if(v->NbSP)
-    view_input[2]->activate();
-  else
-    view_input[2]->deactivate();
-  opt_view_abscissa_name(num, GMSH_GUI, NULL);
+  opt_view_axes_format0(num, GMSH_GUI, NULL);
+  opt_view_axes_format1(num, GMSH_GUI, NULL);
+  opt_view_axes_format2(num, GMSH_GUI, NULL);
+  opt_view_nb_tics0(num, GMSH_GUI, 0);
+  opt_view_nb_tics1(num, GMSH_GUI, 0);
+  opt_view_nb_tics2(num, GMSH_GUI, 0);
+  opt_view_axes_label0(num, GMSH_GUI, NULL);
+  opt_view_axes_label1(num, GMSH_GUI, NULL);
+  opt_view_axes_label2(num, GMSH_GUI, NULL);
 
   opt_view_nb_iso(num, GMSH_GUI, 0);
   opt_view_intervals_type(num, GMSH_GUI, 0);
diff --git a/Graphics/Draw.h b/Graphics/Draw.h
index 3daecb2a55..e2b205349c 100644
--- a/Graphics/Draw.h
+++ b/Graphics/Draw.h
@@ -85,7 +85,8 @@ void Draw_Vector(int Type, int Fill,
 void Draw_PlaneInBoundingBox(double xmin, double ymin, double zmin,
 			     double xmax, double ymax, double zmax,
 			     double a, double b, double c, double d);
-void Draw_3DGrid(int mode, int tics, char *format, double bbox[6]);
+void Draw_3DGrid(int mode, int tics[3], char format[3][256], char label[3][256],
+		 double bbox[6]);
 
 void Draw_Mesh(Mesh *M);
 void Draw_Mesh_Volume(void *a, void *b);
diff --git a/Graphics/Entity.cpp b/Graphics/Entity.cpp
index 3cecec5d6d..8d4807e87f 100644
--- a/Graphics/Entity.cpp
+++ b/Graphics/Entity.cpp
@@ -1,4 +1,4 @@
-// $Id: Entity.cpp,v 1.57 2005-03-11 05:47:55 geuzaine Exp $
+// $Id: Entity.cpp,v 1.58 2005-03-12 00:59:41 geuzaine Exp $
 //
 // Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
 //
@@ -503,7 +503,7 @@ void Draw_PlaneInBoundingBox(double xmin, double ymin, double zmin,
   }
 }
 
-int Draw_Tics(int comp, int n, char *format, 
+int Draw_Tics(int comp, int n, char *format, char *label,
 	      double p1[3], double p2[3], double perp[3])
 {
   // draws n tic marks (in direction perp) and labels along the line p1->p2
@@ -514,10 +514,19 @@ int Draw_Tics(int comp, int n, char *format,
 
   double t[3] = { p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2] };
   double l = norme(t);
-  norme(perp);
   double w = 10 * CTX.pixel_equiv_x / CTX.s[0]; // big tics 10 pixels
   double w2 = 4 * CTX.pixel_equiv_x / CTX.s[0]; // small tics 4 pixels
 
+  double lp = norme(perp);
+  if(!lp){
+    switch(comp){
+    case 0: perp[1] = -1.; break;
+    case 1: perp[0] = -1.; break;
+    case 2: perp[0] = 1.; break;
+    default: break;
+    }
+  }
+  
   double tmp = 2. * CTX.gl_fontsize * CTX.pixel_equiv_x / CTX.s[0];
   if(n * tmp > l) n = 3;
   if(n * tmp > l) n = 2;
@@ -547,25 +556,28 @@ int Draw_Tics(int comp, int n, char *format,
       }
     }
 
-    char label[256];
-    sprintf(label, format, p[comp]);
+    char str[256];
+    sprintf(str, format, p[comp]);
     double winp[3], winr[3];
     World2Viewport(p, winp);
     World2Viewport(r, winr);
     gl_font(CTX.gl_font_enum, CTX.gl_fontsize);
     if(fabs(winr[0] - winp[0]) < 2.) // center align
-      winr[0] -= gl_width(label) / 2.;
+      winr[0] -= gl_width(str) / 2.;
     else if(winr[0] < winp[0]) // right align
-      winr[0] -= gl_width(label);
+      winr[0] -= gl_width(str);
     if(fabs(winr[1] - winp[1]) < 2.) // center align
       winr[1] -= gl_height() / 3.;
     else if(winr[1] < winp[1]) // top align
       winr[1] -= gl_height();
     Viewport2World(winr, r);
     glRasterPos3d(r[0], r[1], r[2]);
-    Draw_String(label);
+    Draw_String(str);
   }
 
+  glRasterPos3d(p2[0]+t[0]*w*1.4, p2[1]+t[1]*w*1.4, p2[2]+t[2]*w*1.4);
+  Draw_String(label);
+
   return n;
 }
 
@@ -605,7 +617,8 @@ void Draw_GridStipple(int n1, int n2, double p1[3], double p2[3], double p3[3])
   gl2psDisable(GL2PS_LINE_STIPPLE);
 }
 
-void Draw_3DGrid(int mode, int tics, char *format, double bb[6])
+void Draw_3DGrid(int mode, int tics[3], char format[3][256], char label[3][256], 
+		 double bb[6])
 {
   // mode 0: nothing
   //      1: axes
@@ -613,10 +626,7 @@ void Draw_3DGrid(int mode, int tics, char *format, double bb[6])
   //      3: full grid
   //      4: open grid
 
-  if( (mode < 1) || 
-      (bb[0] == bb[1] && bb[2] == bb[3]) ||
-      (bb[0] == bb[1] && bb[4] == bb[5]) ||
-      (bb[2] == bb[3] && bb[4] == bb[5]) )
+  if((mode < 1) || (bb[0] == bb[1] && bb[2] == bb[3] && bb[4] == bb[5]))
     return;
   
   double xmin = bb[0], xmax = bb[1];
@@ -655,9 +665,9 @@ void Draw_3DGrid(int mode, int tics, char *format, double bb[6])
   double dym[3] = {(xmin != xmax) ? -1. : 0., 0., (zmin != zmax) ? -1. : 0.};
   double dzm[3] = {(xmin != xmax) ? -1. : 0., (ymin != ymax) ? -1. : 0., 0.};
 
-  int nx = (xmin != xmax) ? Draw_Tics(0, tics, format, orig, xx, dxm) : 0;
-  int ny = (ymin != ymax) ? Draw_Tics(1, tics, format, orig, yy, dym) : 0;
-  int nz = (zmin != zmax) ? Draw_Tics(2, tics, format, orig, zz, dzm) : 0;
+  int nx = (xmin != xmax) ? Draw_Tics(0, tics[0], format[0], label[0], orig, xx, dxm) : 0;
+  int ny = (ymin != ymax) ? Draw_Tics(1, tics[1], format[1], label[1], orig, yy, dym) : 0;
+  int nz = (zmin != zmax) ? Draw_Tics(2, tics[2], format[2], label[2], orig, zz, dzm) : 0;
   
   if(mode > 2){
     Draw_GridStipple(nx, ny, orig, xx, yy);
diff --git a/Graphics/Graph2D.cpp b/Graphics/Graph2D.cpp
index 9c5211f637..ed977f1fbb 100644
--- a/Graphics/Graph2D.cpp
+++ b/Graphics/Graph2D.cpp
@@ -1,4 +1,4 @@
-// $Id: Graph2D.cpp,v 1.46 2005-03-09 02:18:40 geuzaine Exp $
+// $Id: Graph2D.cpp,v 1.47 2005-03-12 00:59:41 geuzaine Exp $
 //
 // Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
 //
@@ -92,9 +92,8 @@ static void addval(Post_View * v, double Abs, double Val,
 }
 
 
-static void Draw_Graph2D(Post_View * v,
-                         double xx, double yy, double width, double height,
-                         double tic, double bb[4])
+static void Draw_Graph2D(Post_View * v, double xx, double yy, 
+			 double width, double height, double tic)
 {
   char label[1024];
   float font_h, font_a;
@@ -232,21 +231,16 @@ static void Draw_Graph2D(Post_View * v,
     AbsMax = *(double *)List_Pointer(v->Time, List_Nbr(v->Time) - 1);
   }
 
-  nb = v->NbAbscissa;
+  nb = v->NbTics[0];
   if(v->ShowScale) {
-    sprintf(label, v->AbscissaFormat, AbsMin);
-    double ww = gl_width(label);
-    sprintf(label, v->AbscissaFormat, AbsMax);
-    if(gl_width(label) > ww)
-      ww = gl_width(label);
-    if((nb - 1) * (ww + 2) > width)
-      nb = (int)floor(width / (ww + 2)) + 1;
+    sprintf(label, v->AxesFormat[0], -M_PI/1.e4);
+    if((nb-1) * gl_width(label) > width)
+      nb = (int)(width / gl_width(label)) + 1;
   }
-  if(nb == 1)
-    dx = width;
-  else
-    dx = width / (double)(nb - 1);
+  if(nb == 1) nb++;
 
+  dx = width / (double)(nb - 1);
+  
   for(i = 0; i < nb; i++) {
     if(v->Grid > 0) {
       glColor4ubv((GLubyte *) & CTX.color.fg);
@@ -277,9 +271,9 @@ static void Draw_Graph2D(Post_View * v,
     if(v->ShowScale) {
       glColor4ubv((GLubyte *) & CTX.color.text);
       if(nb == 1)
-        sprintf(label, v->AbscissaFormat, AbsMin);
+        sprintf(label, v->AxesFormat[0], AbsMin);
       else
-        sprintf(label, v->AbscissaFormat,
+        sprintf(label, v->AxesFormat[0],
                 AbsMin + i * (AbsMax - AbsMin) / (double)(nb - 1));
       glRasterPos2d(xtop + i * dx, ybot - font_h - tic);
       Draw_String_Center(label);
@@ -287,7 +281,7 @@ static void Draw_Graph2D(Post_View * v,
   }
   if(v->ShowScale) {
     glColor4ubv((GLubyte *) & CTX.color.text);
-    sprintf(label, "%s", v->AbscissaName);
+    sprintf(label, "%s", v->AxesLabel[0]);
     glRasterPos2d(xtop + width / 2, ybot - 2 * font_h - 2 * tic);
     Draw_String_Center(label);
   }
@@ -350,71 +344,81 @@ static void Draw_Graph2D(Post_View * v,
 
 }
 
-void getbb(double pos[2], double width, double height,
-           double dx, double dy, double tic, double space,
-           double bbtot[4], double bb[4])
+void Draw_Graph2D(void)
 {
-  bbtot[0] = pos[0] - dx - space; //topleft x
-  bb[0] = bbtot[0];
+  static List_T *todraw = NULL;
 
-  //don't recompute bbtot[1]
-  bb[1] = pos[1] - 1.5 * dy - space; //topleft y
+  if(!CTX.post.list)
+    return;
 
-  bbtot[2] = MAX(bbtot[2], pos[0] + width + (dx - tic) / 2 + space); //bottomright x
-  bb[2] = pos[0] + width + (dx - tic) / 2 + space;
+  // 2d graphs to draw?
 
-  bbtot[3] = pos[1] + height + 2 * dy + space; //bottomright y
-  bb[3] = bbtot[3];
-}
+  if(!todraw)
+    todraw = List_Create(5, 5, sizeof(Post_View *));
+  else
+    List_Reset(todraw);
 
-void Draw_Graph2D(void)
-{
-  int nbauto = 0;
-  double dx, dy, bb[4], bbtot[4] = { 0., 0., 0., 0. }, pos[2] = {0., 0.}, tic;
-  double space = 10.;
+  gl_font(CTX.gl_font_enum, CTX.gl_fontsize);
   char label[1024];
-
-  if(!CTX.post.list)
-    return;
+  double largest_number = 0.;
 
   for(int i = 0; i < List_Nbr(CTX.post.list); i++) {
     Post_View *v = *(Post_View **) List_Pointer(CTX.post.list, i);
-    if(v->Visible && !v->Dirty && v->NbSP && v->Type != DRAW_POST_3D) {
-      tic = 5;
-      dx = dy = 0.;
-      if(v->ShowScale) {
-        gl_font(CTX.gl_font_enum, CTX.gl_fontsize);
-        sprintf(label, v->AbscissaFormat, v->CustomMin);
-        if(gl_width(label) + tic > dx)
-          dx = gl_width(label) + tic;
-        sprintf(label, v->AbscissaFormat, v->CustomMax);
-        if(gl_width(label) + tic > dx)
-          dx = gl_width(label) + tic;
-        dy = 1.5 * gl_height(); //2 below and & above!
+    if(v->Visible && !v->Dirty && v->NbSP && v->Type != DRAW_POST_3D){
+      List_Add(todraw, &v);
+      sprintf(label, v->Format, -M_PI/1.e4);
+      if(largest_number < gl_width(label))
+	largest_number = gl_width(label);
+    }
+  }
+  
+  if(!List_Nbr(todraw))
+    return;
+
+  const double tic = 5;
+  double xsep = largest_number;
+  double ysep = 5 * gl_height();
+  
+  int num = 0;
+
+  for(int i = 0; i < List_Nbr(todraw); i++) {
+    Post_View *v = *(Post_View **) List_Pointer(todraw, i);
+
+    if(!v->AutoPosition2D) {
+      Draw_Graph2D(v, v->Position2D[0], v->Position2D[1], v->Size2D[0], v->Size2D[1], tic);
+    }
+    else{
+      double winw = CTX.viewport[2] - CTX.viewport[0];
+      double winh = CTX.viewport[3] - CTX.viewport[1];
+      if(List_Nbr(todraw) == 1){
+	double fracw = 0.75, frach = 0.75;
+	double w = fracw * winw - xsep;
+	double h = frach * winh - ysep;
+	double xmin = CTX.viewport[0] + (1-fracw)/2. * winw;
+	double ymin = CTX.viewport[1] + (1-frach)/2. * winh;
+	Draw_Graph2D(v, xmin + 0.95*xsep, ymin + 0.4*ysep, w, h, tic);
       }
-      if(!v->AutoPosition || !nbauto) {
-        pos[0] = v->Position[0];
-        pos[1] = v->Position[1];
-        bbtot[1] = pos[1] - 1.5 * dy - space;   //top y
-        getbb(pos, v->Size[0], v->Size[1], dx, dy, tic, space, bbtot, bb);
-        Draw_Graph2D(v, pos[0], pos[1], v->Size[0], v->Size[1], tic, bb);
+      else if(List_Nbr(todraw) == 2){
+	double fracw = 0.75, frach = 0.85;
+	double w = fracw * winw - xsep;
+	double h = frach * winh / 2. - ysep;
+	double xmin = CTX.viewport[0] + (1-fracw)/2. * winw;
+	double ymin = CTX.viewport[1] + (1-frach)/3. * winh;
+	if(num == 1) ymin += (h + ysep + (1-frach)/3. * winh);
+	Draw_Graph2D(v, xmin + 0.95*xsep, ymin + 0.4*ysep, w, h, tic);
+	num++;
       }
-      else {
-        if(bbtot[3] + v->Size[1] + 3 * dy + 2 * space < CTX.viewport[3]) { 
-	  //try to put below
-          pos[1] = bbtot[3] + 1.5 * dy + space;
-          getbb(pos, v->Size[0], v->Size[1], dx, dy, tic, space, bbtot, bb);
-          Draw_Graph2D(v, pos[0], pos[1], v->Size[0], v->Size[1], tic, bb);
-        }
-        else { 
-	  //start a new column
-          pos[0] = bbtot[2] + dx + space;
-          pos[1] = bbtot[1] + 1.5 * dy + space;
-          getbb(pos, v->Size[0], v->Size[1], dx, dy, tic, space, bbtot, bb);
-          Draw_Graph2D(v, pos[0], pos[1], v->Size[0], v->Size[1], tic, bb);
-        }
+      else{
+	double fracw = 0.85, frach = 0.85;
+	double w = fracw * winw / 2. - xsep;
+	double h = frach * winh / 2. - ysep;
+	double xmin = CTX.viewport[0] + (1-fracw)/3. * winw;
+	if(num == 1 || num == 3) xmin += (w + xsep + (1-fracw)/3. * winw);
+	double ymin = CTX.viewport[1] + (1-frach)/3. * winh;
+	if(num == 2 || num == 3) ymin += (h + ysep + (1-frach)/3. * winh);
+	Draw_Graph2D(v, xmin + 0.95*xsep, ymin + 0.4*ysep, w, h, tic);
+	num++;
       }
-      nbauto++;
     }
   }
 }
diff --git a/Graphics/Post.cpp b/Graphics/Post.cpp
index 622f2209a9..3db527e00e 100644
--- a/Graphics/Post.cpp
+++ b/Graphics/Post.cpp
@@ -1,4 +1,4 @@
-// $Id: Post.cpp,v 1.95 2005-03-11 05:47:56 geuzaine Exp $
+// $Id: Post.cpp,v 1.96 2005-03-12 00:59:41 geuzaine Exp $
 //
 // Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
 //
@@ -783,15 +783,20 @@ void Draw_Post(void)
 	glDisable((GLenum)(GL_CLIP_PLANE0 + i));
 
       if(v->Grid && v->Type == DRAW_POST_3D){
-	int ok = 1;
-	for(int i = 0; i < 6; i++) {
-	  if(fabs(v->TmpBBox[i]) == VAL_INF){
-	    ok = 0;
-	    break;
+	if(!v->AutoPosition3D){
+	  Draw_3DGrid(v->Grid, v->NbTics, v->AxesFormat, v->AxesLabel, v->Position3D);
+	}
+	else{
+	  int ok = 1;
+	  for(int i = 0; i < 6; i++) {
+	    if(fabs(v->TmpBBox[i]) == VAL_INF){
+	      ok = 0;
+	      break;
+	    }
 	  }
+	  if(ok)
+	    Draw_3DGrid(v->Grid, v->NbTics, v->AxesFormat, v->AxesLabel, v->TmpBBox);
 	}
-	if(ok)
-	  Draw_3DGrid(v->Grid, v->NbAbscissa, v->AbscissaFormat, v->TmpBBox);
       }
       
       // reset alpha blending
diff --git a/Graphics/Scale.cpp b/Graphics/Scale.cpp
index c3b8508844..91fab76ac3 100644
--- a/Graphics/Scale.cpp
+++ b/Graphics/Scale.cpp
@@ -1,4 +1,4 @@
-// $Id: Scale.cpp,v 1.57 2005-01-21 03:03:47 geuzaine Exp $
+// $Id: Scale.cpp,v 1.58 2005-03-12 00:59:42 geuzaine Exp $
 //
 // Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
 //
@@ -326,13 +326,13 @@ void Draw_Scales(void)
 
   for(int i = 0; i < List_Nbr(todraw); i++) {
     Post_View *v = *(Post_View **) List_Pointer(todraw, i);
-
-    if(!v->AutoPosition) {
-	draw_scale(v, 
-		   v->Position[0], 
-		   CTX.viewport[3] - v->Size[1] - v->Position[1], 
-		   v->Size[0], v->Size[1], 
-		   tic, CTX.post.horizontal_scales);
+    
+    if(!v->AutoPosition2D) {
+      draw_scale(v, 
+		 v->Position2D[0], 
+		 CTX.viewport[3] - v->Size2D[1] - v->Position2D[1], 
+		 v->Size2D[0], v->Size2D[1], 
+		 tic, CTX.post.horizontal_scales);
     }
     else{
       if(CTX.post.horizontal_scales){
diff --git a/doc/VERSIONS b/doc/VERSIONS
index 9de5dc5a1f..37887329ba 100644
--- a/doc/VERSIONS
+++ b/doc/VERSIONS
@@ -1,12 +1,15 @@
-$Id: VERSIONS,v 1.318 2005-03-11 17:25:07 geuzaine Exp $
+$Id: VERSIONS,v 1.319 2005-03-12 00:59:42 geuzaine Exp $
 
 New since 1.59: added support for discrete curves; new Window menu on
 Mac OS X; generalized all octree-based plugins (CutGrid, StreamLines,
 Probe, etc.) to handle all element types (and not only scalar and
 vector triangles+tetrahedra); generalized Plugin(Evaluate),
 Plugin(Extract) and Plugin(Annotate); enhanced clipping plane
-interface; new grid options for 3D post-processing views; new
-manipulator dialog; various small enhancements and bug fixes.
+interface; new grid/axes for 3D post-processing views (+ renamed the
+AbscissaName, NbAbscissa and AbscissaFormat options in the process);
+better automatic positionning of 2D graphs; new manipulator dialog to
+specify rotations/translations/scalings "by hand"; various small
+enhancements and bug fixes.
 
 New in 1.59: added support for discrete (triangulated) surfaces,
 either in STL format or with the new "Discrete Surface" command; added
diff --git a/doc/texinfo/opt_general.texi b/doc/texinfo/opt_general.texi
index cbfaf9bcf7..fd4c86990a 100644
--- a/doc/texinfo/opt_general.texi
+++ b/doc/texinfo/opt_general.texi
@@ -479,6 +479,16 @@ Display width of lines (in pixels)@*
 Default value: @code{1}@*
 Saved in: @code{General.OptionsFileName}
 
+@item General.ManipulatorPositionX
+Horizontal position (in pixels) of the upper left corner of the manipulator window@*
+Default value: @code{650}@*
+Saved in: @code{General.SessionFileName}
+
+@item General.ManipulatorPositionY
+Vertical position (in pixels) of the upper left corner of the manipulator window@*
+Default value: @code{150}@*
+Saved in: @code{General.SessionFileName}
+
 @item General.MenuPositionX
 Horizontal position (in pixels) of the upper left corner of the menu window@*
 Default value: @code{800}@*
diff --git a/doc/texinfo/opt_view.texi b/doc/texinfo/opt_view.texi
index 49a30f3684..04c953adce 100644
--- a/doc/texinfo/opt_view.texi
+++ b/doc/texinfo/opt_view.texi
@@ -1,14 +1,34 @@
 @ftable @code
-@item View.AbscissaName
-Abscissa name for 2D graphs@*
-Default value: @code{""}@*
+@item View.AxesFormatX
+Number format for X-axis (in standard C form)@*
+Default value: @code{"%.3g"}@*
+Saved in: @code{General.OptionsFileName}
+
+@item View.AxesFormatY
+Number format for Y-axis (in standard C form)@*
+Default value: @code{"%.3g"}@*
 Saved in: @code{General.OptionsFileName}
 
-@item View.AbscissaFormat
-Abscissa number format (in standard C form)@*
+@item View.AxesFormatZ
+Number format for Z-axis (in standard C form)@*
 Default value: @code{"%.3g"}@*
 Saved in: @code{General.OptionsFileName}
 
+@item View.AxesLabelX
+X-axis label@*
+Default value: @code{""}@*
+Saved in: @code{General.OptionsFileName}
+
+@item View.AxesLabelY
+Y-axis label@*
+Default value: @code{""}@*
+Saved in: @code{General.OptionsFileName}
+
+@item View.AxesLabelZ
+Z-axis label@*
+Default value: @code{""}@*
+Saved in: @code{General.OptionsFileName}
+
 @item View.FileName
 Default post-processing view file name@*
 Default value: @code{""}@*
@@ -75,7 +95,12 @@ Default value: @code{0.02}@*
 Saved in: @code{General.OptionsFileName}
 
 @item View.AutoPosition
-Position the scale or the 2D graph automatically to avoid overlaps@*
+Position the scale or 2D graph automatically@*
+Default value: @code{1}@*
+Saved in: @code{General.OptionsFileName}
+
+@item View.AutoPosition3D
+Position the 3D grid automatically@*
 Default value: @code{1}@*
 Saved in: @code{General.OptionsFileName}
 
@@ -304,16 +329,26 @@ Minimum view coordinate along the Z-axis (read-only)@*
 Default value: @code{0}@*
 Saved in: @code{-}
 
-@item View.NbAbscissa
-Number of tics on the grid axes@*
-Default value: @code{5}@*
-Saved in: @code{General.OptionsFileName}
-
 @item View.NbIso
 Number of intervals@*
 Default value: @code{15}@*
 Saved in: @code{General.OptionsFileName}
 
+@item View.NbTicsX
+Number of tics on the X-axis@*
+Default value: @code{5}@*
+Saved in: @code{General.OptionsFileName}
+
+@item View.NbTicsY
+Number of tics on the Y-axis@*
+Default value: @code{5}@*
+Saved in: @code{General.OptionsFileName}
+
+@item View.NbTicsZ
+Number of tics on the Z-axis@*
+Default value: @code{5}@*
+Saved in: @code{General.OptionsFileName}
+
 @item View.NbTimeStep
 Number of time steps in the view (do not change this!)@*
 Default value: @code{1}@*
@@ -349,6 +384,36 @@ Display points as solid color dots (0) or 3D spheres (1)@*
 Default value: @code{0}@*
 Saved in: @code{General.OptionsFileName}
 
+@item View.PositionMaxX
+Maximum grid coordinate along the X-axis@*
+Default value: @code{1}@*
+Saved in: @code{-}
+
+@item View.PositionMaxY
+Maximum grid coordinate along the Y-axis@*
+Default value: @code{1}@*
+Saved in: @code{-}
+
+@item View.PositionMaxZ
+Maximum grid coordinate along the Z-axis@*
+Default value: @code{1}@*
+Saved in: @code{-}
+
+@item View.PositionMinX
+Minimum grid coordinate along the X-axis@*
+Default value: @code{0}@*
+Saved in: @code{-}
+
+@item View.PositionMinY
+Minimum grid coordinate along the Y-axis@*
+Default value: @code{0}@*
+Saved in: @code{-}
+
+@item View.PositionMinZ
+Minimum grid coordinate along the Z-axis@*
+Default value: @code{0}@*
+Saved in: @code{-}
+
 @item View.PositionX
 Horizontal position (in pixels) of the upper left corner of the scale or 2D graph@*
 Default value: @code{100}@*
-- 
GitLab