/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2005)
  
	Adresse mèl :
	BILLARD, non joignable par mèl ;
	CALISTE, damien P caliste AT cea P fr.

	Ce logiciel est un programme informatique servant à visualiser des
	structures atomiques dans un rendu pseudo-3D. 

	Ce logiciel est régi par la licence CeCILL soumise au droit français et
	respectant les principes de diffusion des logiciels libres. Vous pouvez
	utiliser, modifier et/ou redistribuer ce programme sous les conditions
	de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA 
	sur le site "http://www.cecill.info".

	Le fait que vous puissiez accéder à cet en-tête signifie que vous avez 
	pris connaissance de la licence CeCILL, et que vous en avez accepté les
	termes (cf. le fichier Documentation/licence.fr.txt fourni avec ce logiciel).
*/

/*   LICENCE SUM UP
	Copyright CEA, contributors : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2005)

	E-mail address:
	BILLARD, not reachable any more ;
	CALISTE, damien P caliste AT cea P fr.

	This software is a computer program whose purpose is to visualize atomic
	configurations in 3D.

	This software is governed by the CeCILL  license under French law and
	abiding by the rules of distribution of free software.  You can  use, 
	modify and/ or redistribute the software under the terms of the CeCILL
	license as circulated by CEA, CNRS and INRIA at the following URL
	"http://www.cecill.info". 

	The fact that you are presently reading this means that you have had
	knowledge of the CeCILL license and that you accept its terms. You can
	find a copy of this licence shipped with this software at Documentation/licence.en.txt.
*/

#include <GL/gl.h>
#include <GL/glu.h> 

#include <math.h>

#include <visu_tools.h>
#include <visu_configFile.h>
#include <visu_data.h>
#include <iface_animatable.h>
#include <coreTools/toolMatrix.h>


#include "view.h"

/**
 * SECTION:view
 * @short_description: Defines all necessary informations for the
 * rendering of a view.
 *
 * <para>The #VisuGlView stores two basic informations: one for the
 * position and orientation of the camera (#VisuGlCamera) and one last for the
 * definition of the viewing window (#VisuGlWindow, including volumic
 * informations).</para>
 *
 * <para>One resource is used by this part, defining the precision
 * desired by the user when drawing OpenGL objects. This precision can
 * be changed using visu_gl_view_setPrecision() and all V_Sim part
 * drawing something should use visu_gl_view_getDetailLevel() to
 * know the size of the vertices to be drawn depending on this
 * precision and the level of zoom.</para>
 *
 * <para>The rendering is done in an OpenGl viewport whose size is
 * given by the bounding box (plus 10%). The camera can be positionned
 * with three angles (theta, phi and omega) and has a zoom factor
 * (gross) and a perspective value (d_red). The angle theta is around
 * the z axis (box coordinates), phi is around the new x axis (after
 * the theta rotation) and omega is a rotation around the axis which
 * goes from the observer to the center of the bounding box. By
 * default the camera looks at the center of the bounding box but this
 * can be changed with the Xs and Ys parameters. These values are
 * stored and are readable through the #VisuGlCamera structure. They
 * must be changed with the following methods to ensure propery
 * signals are correctly emitted:
 * visu_gl_view_setThetaPhiOmega(), visu_gl_view_setGross(),
 * visu_gl_view_setPersp() and visu_gl_view_setXsYs().</para>
 */

/**
 * VisuGlCamera:
 * @d_red: a factor for perspective from 1. to inifnity. With one, the nose of
 *         the observer is completly set on the rendered object, and the size
 *         of the observer is neglectible compared to the size of the object.
 * @theta: the theta angle in spherical coordinates of the position of the observer ;
 * @phi: the phi angle in spherical coordinates of the position of the observer ;
 * @omega: rotation of the observer on itself ;
 * @xs: a value for translation of the viewport on x axis ;
 * @ys: a value for translation of the viewport on y axis ;
 * @gross: a value of zoom ;
 * @length0: a length reference to adimension all values, by default,
 * this is the longest diagonal of the current box (without
 * duplication) ;
 * @up: (in) (array fixed-size=3): the current up vector.
 * @upAxis: which axis define the north pole.
 * @centre: (in) (array fixed-size=3): position of the eye look at ;
 * @eye: (in) (array fixed-size=3): position of the eye.
 * @unit: the unit of @length0.
 * 
 * Values to define the position of the observer.
 */

/**
 * VisuGlWindow:
 * @extens: additional length to add to length0 to obtain the global
 * viewable area.
 * @unit: the #ToolUnits of @extens.
 * @width : the width of the window ;
 * @height : the height of the window ;
 * @near : the beginning of the viewport on z axis (z for observer) ;
 * @far : the end of the viewport on z axis (z for observer) ;
 * @left : the left of the viewport on x axis ;
 * @right : the right of the viewport on x axis ;
 * @bottom : the bottom of the viewport on y axis ;
 * @top : the top of the viewport on y axis ;
 *
 * Values to describe the window where the render is done.
 */

/* Global value used as default when creating a new VisuGlView. */
static float anglesDefault[3] = {40., -50., 0.};
static float translatDefault[2] = {0.5, 0.5};
static float grossDefault = 1.;
static float perspDefault = 5.;

/* Local methods. */
static VisuGlCamera* camera_copy(VisuGlCamera *camera);

/**
 * visu_gl_camera_get_type:
 *
 * Create and retrieve a #GType for a #VisuGlCamera object.
 *
 * Since: 3.7
 *
 * Returns: a new type for #VisuGlCamera structures.
 */
GType visu_gl_camera_get_type(void)
{
  static GType g_define_type_id = 0;

  if (g_define_type_id == 0)
    g_define_type_id =
      g_boxed_type_register_static("VisuGlCamera", 
                                   (GBoxedCopyFunc)camera_copy,
                                   (GBoxedFreeFunc)g_free);
  return g_define_type_id;
}
/**
 * visu_gl_camera_copy:
 * @to: a location to copy values to
 * @from: a #VisuGlCamera to copy values from
 *
 * Do a deep copy of @from to @to.
 *
 * Since: 3.7
 **/
void visu_gl_camera_copy(VisuGlCamera *to, const VisuGlCamera *from)
{
  to->theta     = from->theta;
  to->phi       = from->phi;
  to->omega     = from->omega;
  to->xs        = from->xs;
  to->ys        = from->ys;
  to->gross     = from->gross;
  to->d_red     = from->d_red;
  to->length0   = from->length0;
  to->unit      = from->unit;
  to->upAxis    = from->upAxis;
  to->centre[0] = from->centre[0];
  to->centre[1] = from->centre[1];
  to->centre[2] = from->centre[2];
  to->up[0]     = from->up[0];
  to->up[1]     = from->up[1];
  to->up[2]     = from->up[2];
  to->eye[0]    = from->eye[0];
  to->eye[1]    = from->eye[1];
  to->eye[2]    = from->eye[2];
}
static VisuGlCamera* camera_copy(VisuGlCamera *camera)
{
  VisuGlCamera *out;

  out = g_malloc(sizeof(VisuGlCamera));
  visu_gl_camera_copy(out, camera);
  return out;
}

/**
 * visu_gl_camera_setThetaPhiOmega:
 * @camera: a valid #VisuGlCamera object ;
 * @valueTheta: a floatinf point value in degrees ;
 * @valuePhi: a floating point value in degrees ;
 * @valueOmega: a floating point value in degrees ;
 * @mask: to specified what values will be changed.
 *
 * Change the orientation of the camera to the specified angles.
 *
 * Returns: a mask of changed values.
 */
int visu_gl_camera_setThetaPhiOmega(VisuGlCamera *camera, float valueTheta,
                                         float valuePhi, float valueOmega, int mask)
{
  float valT, valP, valO;
  int diff;

  g_return_val_if_fail(camera, FALSE);
  
  diff = 0;
  if (mask & VISU_GL_CAMERA_THETA)
    {
      valT = valueTheta;
      while (valT < -180.)
	valT += 360.;
      while (valT > 180.)
	valT -= 360.;

      if (camera->theta != valT)
	{
	  diff += VISU_GL_CAMERA_THETA;
	  camera->theta = valT;
	}
    }
  if (mask & VISU_GL_CAMERA_PHI)
    {
      valP = valuePhi;
      while (valP < -180.)
	valP += 360.;
      while (valP > 180.)
	valP -= 360.;

      if (camera->phi != valP)
	{
	  diff += VISU_GL_CAMERA_PHI;
	  camera->phi = valP;
	}
    }
  if (mask & VISU_GL_CAMERA_OMEGA)
    {
      valO = valueOmega;
      while (valO < -180.)
	valO += 360.;
      while (valO > 180.)
	valO -= 360.;

      if (camera->omega != valO)
	{
	  diff += VISU_GL_CAMERA_OMEGA;
	  camera->omega = valO;
	}
    }
  return diff;
}

/**
 * visu_gl_camera_setXsYs:
 * @camera: a valid #VisuGlCamera object ;
 * @valueX: a floatinf point value in the bounding box scale
 *          (1 is the size of the bounding box) ;
 * @valueY: a floating point value in bounding box scale ;
 * @mask: to specified what values will be changed.
 *
 * Change the point where the camera is pointed to.
 *
 * Returns: a mask of changed values.
 */
int visu_gl_camera_setXsYs(VisuGlCamera *camera,
                           float valueX, float valueY, int mask)
{
  int diff;
  
  g_return_val_if_fail(camera, FALSE);
  
  diff = 0;
  if (mask & VISU_GL_CAMERA_XS)
    {
      valueX = CLAMP(valueX, -3., 3.);

      if (camera->xs != valueX)
	{
          diff += VISU_GL_CAMERA_XS;
	  camera->xs = valueX;
	}
    }
  if (mask & VISU_GL_CAMERA_YS)
    {
      valueY = CLAMP(valueY, -3., 3.);

      if (camera->ys != valueY)
	{
          diff += VISU_GL_CAMERA_YS;
	  camera->ys = valueY;
	}
    }
  return diff;

/*   g_signal_emit (visu, VISU_GET_CLASS (visu)->OpenGLXsYs_signal_id, */
/* 		 0 , NULL); */

/*   project(view); */

  /* Change the eyes position. */
/*   O[0] = view->camera->centre[0] + view->box->dxxs2; */
/*   O[1] = view->camera->centre[1] + view->box->dyys2; */
/*   O[2] = view->camera->centre[2] + view->box->dzzs2; */
/*   z = visu_gl_view_getZCoordinate(view, O); */
/*   DBG_fprintf(stderr, "OpenGL View: new window coordinates (%f;%f;%f).\n", */
/* 	      0.5f * view->window->width, 0.5f * view->window->height, z); */
/*   visu_gl_view_getRealCoordinates(view, view->camera->centre, */
/* 				0.5f * view->window->width, */
/* 				0.5f * view->window->height, z, FALSE); */
/*   modelize(view); */
}

/**
 * visu_gl_camera_setGross:
 * @camera: a valid #VisuGlCamera object ;
 * @value: a positive floating point value.
 *
 * Change the value of the camera zoom value. If the value is higher than 10
 * it is set to 10 and if the value is negative it is set to 0.001.
 *
 * Returns: TRUE if value is actually changed.
 */
gboolean visu_gl_camera_setGross(VisuGlCamera *camera, float value)
{
  float val;
  
  g_return_val_if_fail(camera, FALSE);
  
  val = value;
  if (val < 0.02)
    val = 0.02;
  else if (val > 999.)
    val = 999.;

  if (camera->gross == val)
    return FALSE;

  camera->gross = val;
/*   g_signal_emit (visu, VISU_GET_CLASS (visu)->OpenGLGross_signal_id, */
/* 		 0 , NULL); */
/*   project(view); */
  
/*   g_signal_emit (visu, VISU_GET_CLASS (visu)->OpenGLFacetteChanged_signal_id, */
/* 		 0 , NULL); */
  return TRUE;
}

/**
 * visu_gl_camera_setPersp:
 * @camera: a valid #VisuGlCamera object ;
 * @value: a floating point value greater than 1.1.
 *
 * Change the value of the camera perspective value and put it in
 * bounds if needed.
 *
 * Returns: TRUE if value is actually changed.
 */
gboolean visu_gl_camera_setPersp(VisuGlCamera *camera, float value)
{
  g_return_val_if_fail(camera, FALSE);
  
  DBG_fprintf(stderr, "Visu GlCamera: set persp to %g (%g).\n", value, camera->d_red);
  value = CLAMP(value, 1.1f, 100.f);
  if (camera->d_red == value)
    return FALSE;

  camera->d_red = value;

/*   project(view); */
/*   modelize(view); */
  return TRUE;
}
/**
 * visu_gl_window_setViewport:
 * @window: a valid #VisuGlWindow object ;
 * @width: the new horizontal size ;
 * @height: the new vertical size.
 *
 * It changes the size of the OpenGl area and reccompute the OpenGL viewport.
 *
 * Returns: TRUE the size of @window is actually changed.
 */
gboolean visu_gl_window_setViewport(VisuGlWindow *window, guint width, guint height)
{
  DBG_fprintf(stderr, "OpenGL Window: set viewport size (%dx%d) for window %p.\n",
	      width, height, (gpointer)window);
  
  g_return_val_if_fail(window, FALSE);

  if (window->width == width && window->height == height)
    return FALSE;

  DBG_fprintf(stderr, " | old values were %dx%d.\n", window->width,
	      window->height);
  window->width = width;
  window->height = height;
  glViewport(0, 0, window->width, window->height);
  return TRUE;
}
/**
 * visu_gl_window_setAddLength:
 * @window: a #VisuGlWindow object.
 * @value: a float value (positive).
 * @unit: the unit of @value.
 *
 * The viewable area is defined by the #VisuGlCamera size, as set by
 * visu_gl_camera_setRefLength() and by additional space setup by this routine.
 *
 * Since: 3.7
 *
 * Returns: TRUE if the value is indeed changed.
 **/
gboolean visu_gl_window_setAddLength(VisuGlWindow *window, float value, ToolUnits unit)
{
  DBG_fprintf(stderr, "OpenGL Window: set additional length (%f) for window %p.\n",
	      value, (gpointer)window);
  
  g_return_val_if_fail(window, FALSE);

  if (window->extens == value && window->unit == unit)
    return FALSE;

  window->extens = value;
  window->unit   = unit;
  return TRUE;
}
/**
 * visu_gl_window_getAddLength:
 * @window: a #VisuGlWindow object.
 * @unit: (allow-none): a location for the unit of the returned value.
 *
 * The viewable area is defined by the #VisuGlCamera size, as set by
 * visu_gl_camera_setRefLength() and by additional space setup by
 * visu_gl_window_setAddLength().
 *
 * Since: 3.7
 *
 * Returns: the additional length to be added to the camera object
 * size to obtain the full viewable area.
 **/
float visu_gl_window_getAddLength(VisuGlWindow *window, ToolUnits *unit)
{
  g_return_val_if_fail(window, FALSE);

  DBG_fprintf(stderr, "OpenGL Window: get additional length (%f) for window %p.\n",
	      window->extens, (gpointer)window);
  if (unit)
    *unit = window->unit;
  return window->extens;
}

/**
 * visu_gl_camera_setUpAxis:
 * @camera: a #VisuGlCamera object.
 * @upAxis: a direction.
 *
 * In constraint observation mode, the "north" direction is a singular
 * one. Define this direction with this routine.
 *
 * Since: 3.6
 */
void visu_gl_camera_setUpAxis(VisuGlCamera *camera, ToolXyzDir upAxis)
{
  g_return_if_fail(camera);

  camera->upAxis = upAxis;
}

/**
 * visu_gl_camera_setRefLength:
 * @camera: a #VisuGlCamera object.
 * @value: a new length.
 * @unit: its measurement unit.
 *
 * Change the reference value that is used for the zoom.
 *
 * Since: 3.6
 *
 * Returns: TRUE if the value is indeed changed.
 */
gboolean visu_gl_camera_setRefLength(VisuGlCamera *camera, float value, ToolUnits unit)
{
  g_return_val_if_fail(camera, FALSE);

  DBG_fprintf(stderr, "Visu GlCamera: set ref length to %g %d (prev was %g %d)\n",
              value, unit, camera->length0, camera->unit);

  if (camera->length0 == value && camera->unit == unit)
    return FALSE;

  camera->length0 = value;
  camera->unit    = unit;
  return TRUE;
}
/**
 * visu_gl_camera_getRefLength:
 * @camera: a #VisuGlCamera object.
 * @unit: a location for unit value (can be NULL).
 *
 * The zoom is define from a reference length in given unit. If @unit
 * is provided, the corresponding unit will be set.
 *
 * Since: 3.6
 *
 * Returns: the current reference length.
 */
float visu_gl_camera_getRefLength(VisuGlCamera *camera, ToolUnits *unit)
{
  g_return_val_if_fail(camera, -1.f);

  if (unit)
    *unit = camera->unit;
  return camera->length0;
}

/**
 * visu_gl_camera_modelize:
 * @camera: a #VisuGlCamera object.
 *
 * Set-up the orientation matrix, depending on the camera definition.
 */
static void visu_gl_camera_modelize(VisuGlCamera *camera)
{
  double theta_rad, d_red;
  double phi_rad; 
  double sth, cth, sph, cph, com, som;
  double distance;
  int permut[3][3] = {{1,2,0}, {2,0,1}, {0,1,2}};

  g_return_if_fail(camera);
 
  DBG_fprintf(stderr, "OpenGL Camera: modelize view.\n");
  DBG_fprintf(stderr, "OpenGL Camera: using ref length %g.\n",
	      camera->length0);

  if (camera->d_red > 100.)
    d_red = 100.;
  else
    d_red = camera->d_red;
  theta_rad = camera->theta * TOOL_PI180;
  phi_rad   = camera->phi   * TOOL_PI180; 

  distance = d_red * camera->length0;

  sth = sin(theta_rad);
  cth = cos(theta_rad);      
  sph = sin(phi_rad);
  cph = cos(phi_rad);
  com = cos(camera->omega * TOOL_PI180);
  som = sin(camera->omega * TOOL_PI180);

  /* La matrice de rotation est la suivante pour passer
     des coordonnées transformées aux coordonnées de l'écran :
     /cph.cth -sph cph.sth\
     |sph.cth  cph sph.sth| (for z as north axis)
     \   -sth   0      cth/

     / cph -sph.sth sph.cth\
     |  0       cth     sth| (for y as north axis)
     \-sph -sth.cph cph.cth/
     Ainsi la caméra qui est situé en (0,0,Distance) dans le repère transformé
     devient dans le repère de l'écran : */
  camera->eye[permut[camera->upAxis][0]] = distance*sth*cph;
  camera->eye[permut[camera->upAxis][1]] = distance*sth*sph;
  camera->eye[permut[camera->upAxis][2]] = distance*cth;
   
  /* Vecteur donnant la direction verticale.
     Dans le repère transformé il est (-1,0,0). */
  camera->up[permut[camera->upAxis][0]] = -cth*cph*com + sph*som;
  camera->up[permut[camera->upAxis][1]] = -cth*sph*com - cph*som;
  camera->up[permut[camera->upAxis][2]] = sth*com;

  DBG_fprintf(stderr, "Visu GlView: modelize with:\n");
  DBG_fprintf(stderr, " | %g %g %g  %g %g %g  %g %g %g\n",
              camera->eye[0], camera->eye[1], camera->eye[2],
              camera->centre[0], camera->centre[1], camera->centre[2],
              camera->up[0], camera->up[1], camera->up[2]);
  glMatrixMode(GL_MODELVIEW); 
  glLoadIdentity();
  gluLookAt(camera->eye[0], camera->eye[1], camera->eye[2],
	    camera->centre[0], camera->centre[1], camera->centre[2],
	    camera->up[0], camera->up[1], camera->up[2]);
}

/**
 * visu_gl_window_project:
 * @window: definition of the screen.
 * @camera: position of the camera.
 *
 * This method is used to set the projection and the OpenGL viewport.
 */
static void visu_gl_window_project(VisuGlWindow *window, const VisuGlCamera *camera)
{ 
  double x, y, xmin, xmax, ymin, ymax, fact, rap;
  double rap_win, d_red;

  g_return_if_fail(camera && window);
  DBG_fprintf(stderr, "OpenGL View: project view (%d, %d).\n", camera->unit, window->unit);
  DBG_fprintf(stderr, " | %g %g.\n", camera->length0, window->extens);

  if (camera->length0 <= 0. || camera->unit != window->unit)
    return;

  if (camera->d_red > 100.)
    d_red = 100.;
  else
    d_red = camera->d_red;
  
  fact = d_red * camera->length0;
  window->near = MAX(0.01, fact - window->extens);
  window->far  = fact + window->extens;

  fact = window->near / camera->gross / d_red;
  rap = 2. * window->near / (d_red - 1.);
  x = (0.5 - camera->xs) * rap;
  xmin = x - fact;
  xmax = x + fact;
  y = (0.5 - camera->ys) * rap;
  ymin = y - fact;
  ymax = y + fact;
  window->left   = xmin;
  window->bottom = ymin;
    
  rap_win = (1.0*window->height)/window->width;
  if ( 1. > rap_win )
    {
      window->top   = ymax;
      fact          = (ymax - ymin) / rap_win;
      window->left  = 0.5 * (xmin + xmax - fact);
      window->right = 0.5 * (xmin + xmax + fact);
    }
  else if ( 1. < rap_win )
    {
      window->right  = xmax;
      fact           = (xmax - xmin) * rap_win;
      window->bottom = 0.5 * (ymin + ymax - fact);
      window->top    = 0.5 * (ymin + ymax + fact);
    }
  else
    {
      window->right  = xmax;
      window->top    = ymax;
    }

  DBG_fprintf(stderr, "Visu GlView: project:\n");
  DBG_fprintf(stderr, " | %g %g  %g %g  %g %g\n",
              window->left, window->right, window->bottom,
              window->top, window->near, window->far);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  if (d_red == 100.)
    glOrtho(window->left, window->right, window->bottom,
	    window->top, window->near, window->far);
  else
    glFrustum(window->left, window->right, window->bottom,
	      window->top, window->near, window->far);
  glMatrixMode(GL_MODELVIEW);
}

/***************************/
/* The #VisuGlView object. */
/***************************/
enum {
  CHANGED_SIGNAL,
  WIDTH_HEIGHT_CHANGED_SIGNAL,
  REF_LENGTH_CHANGED_SIGNAL,
  FACETTES_CHANGED_SIGNAL,
  LAST_SIGNAL
};
static guint _signals[LAST_SIGNAL] = { 0 };
enum
  {
    PROP_0,
    THETA_PROP,
    PHI_PROP,
    OMEGA_PROP,
    XS_PROP,
    YS_PROP,
    GROSS_PROP,
    PERSP_PROP,
    PRECISION_PROP,
    N_PROP,
    ADJUST_PROP,
    BOX_PROP
  };
static GParamSpec *properties[N_PROP];

struct _VisuGlViewPrivate
{
  VisuBox *box;
  gulong box_signal, unit_signal, bc_signal;

  gboolean adjust;
  float precision;

  VisuAnimation *theta_anim, *phi_anim, *gross_anim, *persp_anim;
  GHashTable *animations;
  
  gboolean dispose_has_run;
};

/* This is a positive float that enable to increase or
   decrease the rendering load by modifying the
   number of facettes. */
#define FLAG_PARAMETER_OPENGL_DETAILS   "opengl_details"
#define DESC_PARAMETER_OPENGL_DETAILS   "Give a value to the quality of rendering (100 is normal) ; positive integer"
static float _defaultDetails = 100.f;
static void exportParametersVisuGlView(GString *data,
                                       VisuData *dataObj);

#define FLAG_RESOURCE_OPENGL_ANGLES "opengl_theta_phi_omega"
#define DESC_RESOURCE_OPENGL_ANGLES "3 real values (degrees) for user orientation with respect to sample"

#define FLAG_RESOURCE_OPENGL_TRANSLAT "opengl_xs_ys"
#define DESC_RESOURCE_OPENGL_TRANSLAT "2 real values for image position with respect to [0.0, 1.0]x[0.0, 1.0] window"

#define FLAG_RESOURCE_OPENGL_GROSS "opengl_gross"
#define DESC_RESOURCE_OPENGL_GROSS "gross factor (must be real > 0.0)"

#define FLAG_RESOURCE_OPENGL_PERSP "opengl_d_red"
#define DESC_RESOURCE_OPENGL_PERSP "reduced perspective distance (must be real > 1.0)"

#define FLAG_PARAMETER_AUTO_ADJUST "config_autoAdjustCamera"
#define DESC_PARAMETER_AUTO_ADJUST "Auto adjust zoom capability for the box to be full size at zoom level 1 ; boolean 0 or 1"
static gboolean autoAdjustDefault = TRUE;

static void exportResourcesVisuGlView(GString *data, VisuData *dataObj);

static void visu_gl_view_dispose     (GObject* obj);
static void visu_gl_view_finalize    (GObject* obj);
static void visu_gl_view_get_property(GObject* obj, guint property_id,
                                      GValue *value, GParamSpec *pspec);
static void visu_gl_view_set_property(GObject* obj, guint property_id,
                                      const GValue *value, GParamSpec *pspec);
static void visu_boxed_interface_init(VisuBoxedInterface *iface);
static void visu_animatable_interface_init(VisuAnimatableInterface *iface);

/* Callbacks. */
static void onSizeChanged(VisuBox *box, gfloat extens, gpointer data);
static void onUnitChanged(VisuBox *box, gfloat fact, gpointer data);
static void onBoundaryChanged(VisuBox *box, GParamSpec *pspec, gpointer data);
static void onEntryAngles(VisuGlView *view, VisuConfigFileEntry *entry, VisuConfigFile *obj);
static void onEntryTrans(VisuGlView *view, VisuConfigFileEntry *entry, VisuConfigFile *obj);
static void onEntryGross(VisuGlView *view, VisuConfigFileEntry *entry, VisuConfigFile *obj);
static void onEntryPersp(VisuGlView *view, VisuConfigFileEntry *entry, VisuConfigFile *obj);
static void onEntryPrecision(VisuGlView *view, VisuConfigFileEntry *entry, VisuConfigFile *obj);

/* Local methods. */
static void _setBoundary(VisuGlView *view, VisuBox *box);
static VisuBox* _getBox(VisuBoxed *boxed);
static gboolean _setBox(VisuBoxed *boxed, VisuBox* box);
static VisuAnimation* _getAnimation(const VisuAnimatable *animatable, const gchar *prop);

G_DEFINE_TYPE_WITH_CODE(VisuGlView, visu_gl_view, VISU_TYPE_OBJECT,
                        G_ADD_PRIVATE(VisuGlView)
                        G_IMPLEMENT_INTERFACE(VISU_TYPE_BOXED,
                                              visu_boxed_interface_init)
                        G_IMPLEMENT_INTERFACE(VISU_TYPE_ANIMATABLE,
                                              visu_animatable_interface_init))

static void visu_gl_view_class_init(VisuGlViewClass *klass)
{
  float rg[2] = {-G_MAXFLOAT, G_MAXFLOAT};
  float rgGross[2] = {0.02f, 999.f};
  float rgPersp[2] = {1.1f, 100.f};
  float rgDetails[2] = {0.f, 500.f};
  VisuConfigFileEntry *resourceEntry;

  DBG_fprintf(stderr, "Visu GlView: creating the class of the object.\n");
  /* Connect the overloading methods. */
  G_OBJECT_CLASS(klass)->dispose      = visu_gl_view_dispose;
  G_OBJECT_CLASS(klass)->finalize     = visu_gl_view_finalize;
  G_OBJECT_CLASS(klass)->set_property = visu_gl_view_set_property;
  G_OBJECT_CLASS(klass)->get_property = visu_gl_view_get_property;

  DBG_fprintf(stderr, "                - adding new signals ;\n");
  /**
   * VisuGlView::changed:
   * @view: the object which emits the signal ;
   *
   * Gets emitted when the view is changed.
   *
   * Since: 3.8
   */
  _signals[CHANGED_SIGNAL] =
    g_signal_new("changed", G_TYPE_FROM_CLASS (klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
                 0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
                 G_TYPE_NONE, 0);

  /**
   * VisuGlView::WidthHeightChanged:
   * @view: the object which received the signal ;
   *
   * Gets emitted when the viewing frame has been changed.
   *
   * Since: 3.2
   */
  _signals[WIDTH_HEIGHT_CHANGED_SIGNAL] =
    g_signal_new("WidthHeightChanged", G_TYPE_FROM_CLASS (klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
                 0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
                 G_TYPE_NONE, 0);
  /**
   * VisuGlView::RefLengthChanged:
   * @view: the object which received the signal ;
   *
   * Gets emitted when the reference length of the camera has been changed.
   *
   * Since: 3.7
   */
  _signals[REF_LENGTH_CHANGED_SIGNAL] =
    g_signal_new("RefLengthChanged", G_TYPE_FROM_CLASS (klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
                 0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
                 G_TYPE_NONE, 0);
  /**
   * VisuGlView::DetailLevelChanged:
   * @view: the object which received the signal ;
   *
   * Gets emitted when precision of the drawn object has been changed.
   *
   * Since: 3.2
   */
  _signals[FACETTES_CHANGED_SIGNAL] =
    g_signal_new("DetailLevelChanged", G_TYPE_FROM_CLASS (klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
                 0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
                 G_TYPE_NONE, 0);

  /**
   * VisuGlView::theta:
   *
   * The theta angle of the camera.
   *
   * Since: 3.8
   */
  properties[THETA_PROP] = g_param_spec_double("theta", "Theta", "theta angle",
                                               -360., 360., 90., G_PARAM_READWRITE);
  /**
   * VisuGlView::phi:
   *
   * The phi angle of the camera.
   *
   * Since: 3.8
   */
  properties[PHI_PROP] = g_param_spec_double("phi", "Phi", "phi angle",
                                            -360., 360., 0., G_PARAM_READWRITE);
  /**
   * VisuGlView::omega:
   *
   * The omega angle of the camera.
   *
   * Since: 3.8
   */
  properties[OMEGA_PROP] = g_param_spec_double("omega", "Omega", "omega angle",
                                              -360., 360., 0., G_PARAM_READWRITE);
  /**
   * VisuGlView::trans-x:
   *
   * The translation of the object along the window x axis.
   *
   * Since: 3.8
   */
  properties[XS_PROP] = g_param_spec_double("trans-x", "X translation", "translation along x",
                                           -3., +3., 0.5, G_PARAM_READWRITE);
  /**
   * VisuGlView::trans-y:
   *
   * The translation of the object along the window y axis.
   *
   * Since: 3.8
   */
  properties[YS_PROP] = g_param_spec_double("trans-y", "Y translation", "translation along y",
                                           -3., +3., 0.5, G_PARAM_READWRITE);
  /**
   * VisuGlView::zoom:
   *
   * The magnification of the camera.
   *
   * Since: 3.8
   */
  properties[GROSS_PROP] = g_param_spec_double("zoom", "Zoom", "zoom level",
                                              .02, 999., 1., G_PARAM_READWRITE);
  /**
   * VisuGlView::perspective:
   *
   * The perspective of the camera.
   *
   * Since: 3.8
   */
  properties[PERSP_PROP] = g_param_spec_double("perspective", "Perspective",
                                              "perspective level",
                                              1.1, 100., 5., G_PARAM_READWRITE);
  /**
   * VisuGlView::precision:
   *
   * The accuracy of the rendering.
   *
   * Since: 3.8
   */
  properties[PRECISION_PROP] = g_param_spec_float("precision", "Precision",
                                                  "precision level",
                                                  0.f, 10.f, 1.f, G_PARAM_READWRITE);

  g_object_class_install_properties(G_OBJECT_CLASS(klass), N_PROP, properties);

  g_object_class_override_property(G_OBJECT_CLASS(klass), ADJUST_PROP, "auto-adjust");
  g_object_class_override_property(G_OBJECT_CLASS(klass), BOX_PROP, "box");

  DBG_fprintf(stderr, "                - adding resources ;\n");
  /* Parameters */
  resourceEntry = visu_config_file_addFloatArrayEntry(VISU_CONFIG_FILE_PARAMETER,
                                                      FLAG_PARAMETER_OPENGL_DETAILS,
                                                      DESC_PARAMETER_OPENGL_DETAILS,
                                                      1, &_defaultDetails, rgDetails, FALSE);
  resourceEntry = visu_config_file_addBooleanEntry(VISU_CONFIG_FILE_PARAMETER,
                                                   FLAG_PARAMETER_AUTO_ADJUST,
                                                   DESC_PARAMETER_AUTO_ADJUST,
                                                   &autoAdjustDefault, FALSE);
  visu_config_file_entry_setVersion(resourceEntry, 3.6f);
  visu_config_file_addExportFunction(VISU_CONFIG_FILE_PARAMETER,
                                     exportParametersVisuGlView);

  /* Resources */
  resourceEntry = visu_config_file_addFloatArrayEntry(VISU_CONFIG_FILE_RESOURCE,
                                                      FLAG_RESOURCE_OPENGL_ANGLES,
                                                      DESC_RESOURCE_OPENGL_ANGLES,
                                                      3, anglesDefault, rg, FALSE);
  visu_config_file_entry_setVersion(resourceEntry, 3.1f);
  resourceEntry = visu_config_file_addFloatArrayEntry(VISU_CONFIG_FILE_RESOURCE,
                                                      FLAG_RESOURCE_OPENGL_TRANSLAT,
                                                      DESC_RESOURCE_OPENGL_TRANSLAT,
                                                      2, translatDefault, rg, FALSE);
  resourceEntry = visu_config_file_addFloatArrayEntry(VISU_CONFIG_FILE_RESOURCE,
                                                      FLAG_RESOURCE_OPENGL_GROSS,
                                                      DESC_RESOURCE_OPENGL_GROSS,
                                                      1, &grossDefault, rgGross, FALSE);
  resourceEntry = visu_config_file_addFloatArrayEntry(VISU_CONFIG_FILE_RESOURCE,
                                                      FLAG_RESOURCE_OPENGL_PERSP,
                                                      DESC_RESOURCE_OPENGL_PERSP,
                                                      1, &perspDefault, rgPersp, FALSE);
  visu_config_file_addExportFunction(VISU_CONFIG_FILE_RESOURCE,
                                     exportResourcesVisuGlView);
}
static void visu_boxed_interface_init(VisuBoxedInterface *iface)
{
  iface->get_box = _getBox;
  iface->set_box = _setBox;
}
static void visu_animatable_interface_init(VisuAnimatableInterface *iface)
{
  iface->get_animation = _getAnimation;
}

static void visu_gl_view_init(VisuGlView *view)
{
  DBG_fprintf(stderr, "Visu GlView: initializing a new object (%p).\n",
	      (gpointer)view);

  view->priv = visu_gl_view_get_instance_private(view);
  view->priv->dispose_has_run = FALSE;
  
  view->priv->box             = (VisuBox*)0;
  view->priv->box_signal      = 0;
  view->priv->unit_signal     = 0;
  view->priv->bc_signal       = 0;
  view->priv->adjust          = autoAdjustDefault;
  view->priv->precision       = _defaultDetails / 100.f;
  view->priv->animations      = g_hash_table_new(g_str_hash, g_str_equal);
  view->priv->theta_anim      = visu_animation_new(G_OBJECT(view), "theta");
  g_hash_table_insert(view->priv->animations, "theta", view->priv->theta_anim);
  view->priv->phi_anim        = visu_animation_new(G_OBJECT(view), "phi");
  g_hash_table_insert(view->priv->animations, "phi", view->priv->phi_anim);
  view->priv->gross_anim      = visu_animation_new(G_OBJECT(view), "zoom");
  g_hash_table_insert(view->priv->animations, "zoom", view->priv->gross_anim);
  view->priv->persp_anim      = visu_animation_new(G_OBJECT(view), "perspective");
  g_hash_table_insert(view->priv->animations, "perspective", view->priv->persp_anim);

  view->window.extens = 0.;
  view->window.width  = 0;
  view->window.height = 0;
  view->window.near   = 0.;
  view->window.far    = 0.;
  view->window.unit   = TOOL_UNITS_UNDEFINED;

  view->camera.theta   = anglesDefault[0];
  view->camera.phi     = anglesDefault[1];
  view->camera.omega   = anglesDefault[2];
  view->camera.xs      = translatDefault[0];
  view->camera.ys      = translatDefault[1];
  view->camera.gross   = grossDefault;
  view->camera.d_red   = perspDefault;
  view->camera.length0 = -1.;
  view->camera.unit    = TOOL_UNITS_UNDEFINED;
  view->camera.upAxis  = TOOL_XYZ_Z;
  view->camera.centre[0] = 0.f;
  view->camera.centre[1] = 0.f;
  view->camera.centre[2] = 0.f;

  DBG_fprintf(stderr, " | theta = %g\n", view->camera.theta);
  DBG_fprintf(stderr, " | phi   = %g\n", view->camera.phi);
  DBG_fprintf(stderr, " | omega = %g\n", view->camera.omega);
  DBG_fprintf(stderr, " | dx-dy = %g %g\n", view->camera.xs, view->camera.ys);
  DBG_fprintf(stderr, " | gross = %g\n", view->camera.gross);
  DBG_fprintf(stderr, " | persp = %g\n", view->camera.d_red);
  DBG_fprintf(stderr, " | width x height = %d x %d\n", view->window.width, view->window.height);

  g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_OPENGL_ANGLES,
                          G_CALLBACK(onEntryAngles), (gpointer)view, G_CONNECT_SWAPPED);
  g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_OPENGL_TRANSLAT,
                          G_CALLBACK(onEntryTrans), (gpointer)view, G_CONNECT_SWAPPED);
  g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_OPENGL_GROSS,
                          G_CALLBACK(onEntryGross), (gpointer)view, G_CONNECT_SWAPPED);
  g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_OPENGL_PERSP,
                          G_CALLBACK(onEntryPersp), (gpointer)view, G_CONNECT_SWAPPED);
  g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_PARAMETER_OPENGL_DETAILS,
                          G_CALLBACK(onEntryPrecision), (gpointer)view, G_CONNECT_SWAPPED);
}
/* This method can be called several times.
   It should unref all of its reference to
   GObjects. */
static void visu_gl_view_dispose(GObject* obj)
{
  VisuGlView *view;

  DBG_fprintf(stderr, "Visu GlView: dispose object %p.\n", (gpointer)obj);

  view = VISU_GL_VIEW(obj);
  if (view->priv->dispose_has_run)
    return;
  view->priv->dispose_has_run = TRUE;

  _setBox(VISU_BOXED(view), (VisuBox*)0);
  g_object_unref(view->priv->theta_anim);
  g_object_unref(view->priv->phi_anim);
  g_object_unref(view->priv->gross_anim);
  g_object_unref(view->priv->persp_anim);

  /* Chain up to the parent class */
  G_OBJECT_CLASS(visu_gl_view_parent_class)->dispose(obj);
}
/* This method is called once only. */
static void visu_gl_view_finalize(GObject* obj)
{
  g_return_if_fail(obj);

  DBG_fprintf(stderr, "Visu GlView: finalize object %p.\n", (gpointer)obj);
  g_hash_table_destroy(VISU_GL_VIEW(obj)->priv->animations);

  /* Chain up to the parent class */
  DBG_fprintf(stderr, "Visu GlView: chain to parent.\n");
  G_OBJECT_CLASS(visu_gl_view_parent_class)->finalize(obj);
  DBG_fprintf(stderr, "Visu GlView: freeing ... OK.\n");
}
static void visu_gl_view_get_property(GObject* obj, guint property_id,
                                      GValue *value, GParamSpec *pspec)
{
  VisuGlView *self = VISU_GL_VIEW(obj);

  DBG_fprintf(stderr, "Visu GlView: get property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case THETA_PROP:
      if (visu_animation_isRunning(self->priv->theta_anim))
        visu_animation_getTo(self->priv->theta_anim, value);
      else
        g_value_set_double(value, self->camera.theta);
      DBG_fprintf(stderr, "%g.\n", self->camera.theta);
      break;
    case PHI_PROP:
      if (visu_animation_isRunning(self->priv->phi_anim))
        visu_animation_getTo(self->priv->phi_anim, value);
      else
        g_value_set_double(value, self->camera.phi);
      DBG_fprintf(stderr, "%g.\n", self->camera.phi);
      break;
    case OMEGA_PROP:
      g_value_set_double(value, self->camera.omega);
      DBG_fprintf(stderr, "%g.\n", self->camera.omega);
      break;
    case XS_PROP:
      g_value_set_double(value, self->camera.xs);
      DBG_fprintf(stderr, "%g.\n", self->camera.xs);
      break;
    case YS_PROP:
      g_value_set_double(value, self->camera.ys);
      DBG_fprintf(stderr, "%g.\n", self->camera.ys);
      break;
    case GROSS_PROP:
      if (visu_animation_isRunning(self->priv->gross_anim))
        visu_animation_getTo(self->priv->gross_anim, value);
      else
        g_value_set_double(value, self->camera.gross);
      DBG_fprintf(stderr, "%g.\n", self->camera.gross);
      break;
    case PERSP_PROP:
      if (visu_animation_isRunning(self->priv->persp_anim))
        visu_animation_getTo(self->priv->persp_anim, value);
      else
        g_value_set_double(value, self->camera.d_red);
      DBG_fprintf(stderr, "%g.\n", self->camera.d_red);
      break;
    case ADJUST_PROP:
      g_value_set_boolean(value, self->priv->adjust);
      DBG_fprintf(stderr, "%d.\n", self->priv->adjust);
      break;
    case PRECISION_PROP:
      DBG_fprintf(stderr, "%g.\n", self->priv->precision);
      g_value_set_float(value, self->priv->precision);
      break;
    case BOX_PROP:
      DBG_fprintf(stderr, "%p.\n", (gpointer)self->priv->box);
      g_value_set_object(value, self->priv->box);
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}
static void visu_gl_view_set_property(GObject* obj, guint property_id,
                                      const GValue *value, GParamSpec *pspec)
{
  VisuGlView *self = VISU_GL_VIEW(obj);

  DBG_fprintf(stderr, "Visu GlView: set property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case THETA_PROP:
      DBG_fprintf(stderr, "%g.\n", g_value_get_double(value));
      if (!visu_animatable_animateDouble(VISU_ANIMATABLE(self), self->priv->theta_anim,
                                         g_value_get_double(value),
                                         200, FALSE, VISU_ANIMATION_QUAD))
        visu_gl_view_setThetaPhiOmega(self, g_value_get_double(value),
                                      0., 0., VISU_GL_CAMERA_THETA);
      break;
    case PHI_PROP:
      DBG_fprintf(stderr, "%g.\n", g_value_get_double(value));
      if (!visu_animatable_animateDouble(VISU_ANIMATABLE(self), self->priv->phi_anim,
                                         g_value_get_double(value),
                                         200, FALSE, VISU_ANIMATION_QUAD))
        visu_gl_view_setThetaPhiOmega(self, 0., g_value_get_double(value),
                                      0., VISU_GL_CAMERA_PHI);
      break;
    case OMEGA_PROP:
      visu_gl_view_setThetaPhiOmega(self, 0., 0., g_value_get_double(value),
                                    VISU_GL_CAMERA_OMEGA);
      DBG_fprintf(stderr, "%g.\n", self->camera.omega);
      break;
    case XS_PROP:
      visu_gl_view_setXsYs(self, g_value_get_double(value), 0.,
                           VISU_GL_CAMERA_XS);
      DBG_fprintf(stderr, "%g.\n", self->camera.xs);
      break;
    case YS_PROP:
      visu_gl_view_setXsYs(self, 0., g_value_get_double(value),
                           VISU_GL_CAMERA_YS);
      DBG_fprintf(stderr, "%g.\n", self->camera.ys);
      break;
    case GROSS_PROP:
      DBG_fprintf(stderr, "%g.\n", g_value_get_double(value));
      if (!visu_animatable_animateDouble(VISU_ANIMATABLE(self), self->priv->gross_anim,
                                         g_value_get_double(value),
                                         200, FALSE, VISU_ANIMATION_QUAD))
        visu_gl_view_setGross(self, g_value_get_double(value));
      break;
    case PERSP_PROP:
      DBG_fprintf(stderr, "%g.\n", g_value_get_double(value));
      if (!visu_animatable_animateDouble(VISU_ANIMATABLE(self), self->priv->persp_anim,
                                         g_value_get_double(value),
                                         200, FALSE, VISU_ANIMATION_QUAD))
        visu_gl_view_setPersp(self, g_value_get_double(value));
      break;
    case ADJUST_PROP:
      self->priv->adjust = g_value_get_boolean(value);
      autoAdjustDefault = g_value_get_boolean(value);
      DBG_fprintf(stderr, "%d.\n", self->priv->adjust);
      break;
    case PRECISION_PROP:
      DBG_fprintf(stderr, "%g.\n", g_value_get_float(value));
      visu_gl_view_setPrecision(self, g_value_get_float(value));
      break;
    case BOX_PROP:
      DBG_fprintf(stderr, "%p.\n", g_value_get_object(value));
      _setBox(VISU_BOXED(obj), VISU_BOX(g_value_get_object(value)));
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}

/**
 * visu_gl_view_new:
 *
 * Create a new #VisuGlView object with default values.
 *
 * Returns: (transfer full): the newly created object.
 */
VisuGlView* visu_gl_view_new(void)
{
  VisuGlView *view;

  view = VISU_GL_VIEW(g_object_new(VISU_TYPE_GL_VIEW, NULL));
  return view;
}

/**
 * visu_gl_view_new_withSize:
 * @w: the width ;
 * @h: the height.
 *
 * Create a new #VisuGlView object with default values and the given
 * window size.
 *
 * Since: 3.7
 *
 * Returns: (transfer full): the newly created object.
 */
VisuGlView* visu_gl_view_new_withSize(guint w, guint h)
{
  VisuGlView *view;

  view = VISU_GL_VIEW(g_object_new(VISU_TYPE_GL_VIEW, NULL));
  visu_gl_window_setViewport(&view->window, w, h);

  return view;
}

/**
 * visu_gl_view_initContext:
 * @view: a #VisuGlView object.
 *
 * Modelize and project the view. Should be called when used in a new
 * OpenGL context.
 *
 * Since: 3.8
 **/
void visu_gl_view_initContext(VisuGlView *view)
{
  g_return_if_fail(VISU_IS_GL_VIEW(view));

  visu_gl_camera_modelize(&view->camera);
  visu_gl_window_project(&view->window, &view->camera);
}

static gboolean _setBox(VisuBoxed *self, VisuBox *box)
{
  VisuGlView *view;
  float fact;

  g_return_val_if_fail(VISU_IS_GL_VIEW(self), FALSE);
  view = VISU_GL_VIEW(self);

  DBG_fprintf(stderr, "OpenGL View: set box %p.\n", (gpointer)box);
  
  if (box == view->priv->box)
    return FALSE;

  if (view->priv->box)
    {
      g_signal_handler_disconnect(G_OBJECT(view->priv->box), view->priv->box_signal);
      g_signal_handler_disconnect(G_OBJECT(view->priv->box), view->priv->unit_signal);
      g_signal_handler_disconnect(G_OBJECT(view->priv->box), view->priv->bc_signal);
      g_object_unref(view->priv->box);
    }
  if (box)
    {
      g_object_ref(box);
      view->priv->box_signal =
        g_signal_connect(G_OBJECT(box), "SizeChanged",
                         G_CALLBACK(onSizeChanged), (gpointer)view);
      view->priv->unit_signal =
	g_signal_connect(G_OBJECT(box), "UnitChanged",
			 G_CALLBACK(onUnitChanged), (gpointer)view);
      view->priv->bc_signal =
	g_signal_connect(G_OBJECT(box), "notify::boundary",
			 G_CALLBACK(onBoundaryChanged), (gpointer)view);

      /* Update lengths. */
      if (view->priv->adjust || view->camera.length0 <= 0.f)
        visu_gl_view_setRefLength(view,
                                  visu_box_getGlobalSize(box, FALSE),
                                  visu_box_getUnit(box));
      else
        {
          fact = 1.;
          if (view->camera.unit != TOOL_UNITS_UNDEFINED &&
              visu_box_getUnit(box) != TOOL_UNITS_UNDEFINED)
            fact = tool_physic_getUnitValueInMeter(view->camera.unit) /
              tool_physic_getUnitValueInMeter(visu_box_getUnit(box));
          DBG_fprintf(stderr, "Visu GlView: ref length factor %g (%d %d).\n",
                      fact, view->camera.unit, visu_box_getUnit(box));
          visu_gl_view_setRefLength(view,
                                    view->camera.length0 * fact,
                                    visu_box_getUnit(box));
        }
      visu_gl_view_setObjectRadius(view,
                                   visu_box_getGlobalSize(box, TRUE),
                                   visu_box_getUnit(box));
      /* Update camera. */
      _setBoundary(view, box);
    }
  else
    {
      view->priv->box_signal = 0;
      view->priv->unit_signal = 0;
      view->priv->bc_signal = 0;
    }
  view->priv->box = box;

  return TRUE;
}
static void onSizeChanged(VisuBox *box, gfloat extens, gpointer data)
{
  VisuGlView *view = VISU_GL_VIEW(data);

  DBG_fprintf(stderr, "Visu GlView: caught 'SizeChanged'.\n");
  visu_gl_view_setObjectRadius(view, extens, visu_box_getUnit(box));
  DBG_fprintf(stderr, "Visu GlView: done 'SizeChanged'.\n");
}
static void onUnitChanged(VisuBox *box, gfloat fact, gpointer data)
{
  VisuGlView *view = VISU_GL_VIEW(data);
  ToolUnits unit;

  DBG_fprintf(stderr, "Visu GlView: caught 'UnitChanged'.\n");
  unit = visu_box_getUnit(box);
  if (view->camera.unit != TOOL_UNITS_UNDEFINED && unit != TOOL_UNITS_UNDEFINED)
    fact = tool_physic_getUnitValueInMeter(view->camera.unit) /
      tool_physic_getUnitValueInMeter(unit);
  visu_gl_view_setRefLength(view, view->camera.length0 * fact, unit);
  /* Box size changed will be emitted after, so the object radius will
     be updated automatically, except for undefined units. */
  if (view->window.unit == TOOL_UNITS_UNDEFINED || unit == TOOL_UNITS_UNDEFINED)
    visu_gl_window_setAddLength(&view->window, view->window.extens, unit);
  DBG_fprintf(stderr, "Visu GlView: done 'UnitChanged'.\n");
}
static void onBoundaryChanged(VisuBox *box, GParamSpec *pspec _U_, gpointer data)
{
  _setBoundary(VISU_GL_VIEW(data), box);
}
static void _setBoundary(VisuGlView *view, VisuBox *box)
{
  switch (visu_box_getBoundary(box))
    {
    case (VISU_BOX_PERIODIC):
    case (VISU_BOX_FREE):
    case (VISU_BOX_WIRE_X):
    case (VISU_BOX_WIRE_Y):
    case (VISU_BOX_WIRE_Z):
    case (VISU_BOX_SURFACE_XY):
      visu_gl_camera_setUpAxis(&view->camera, TOOL_XYZ_Z);
      break;
    case (VISU_BOX_SURFACE_YZ):
      visu_gl_camera_setUpAxis(&view->camera, TOOL_XYZ_X);
      break;
    case (VISU_BOX_SURFACE_ZX):
      visu_gl_camera_setUpAxis(&view->camera, TOOL_XYZ_Y);
      break;
    }
  visu_gl_camera_modelize(&view->camera);
  g_signal_emit(view, _signals[CHANGED_SIGNAL], 0);
}
static VisuBox* _getBox(VisuBoxed *boxed)
{
  g_return_val_if_fail(VISU_IS_GL_VIEW(boxed), (VisuBox*)0);

  return VISU_GL_VIEW(boxed)->priv->box;
}
static VisuAnimation* _getAnimation(const VisuAnimatable *animatable,
                                    const gchar *prop)
{
  g_return_val_if_fail(VISU_IS_GL_VIEW(animatable), (VisuAnimation*)0);

  return g_hash_table_lookup(VISU_GL_VIEW(animatable)->priv->animations, prop);
}

/**
 * visu_gl_view_getDetailLevel:
 * @view: a valid #VisuGlView object ;
 * @dimension: the size of the object which asks for its number of facettes.
 *
 * This is a function to get the number of "facettes" advised
 * by the server (according to its policy on rendering)
 * to draw an object according to a given dimension.
 *
 * Returns: the number of facettes the object should used.
 */
gint visu_gl_view_getDetailLevel(const VisuGlView *view, float dimension)
{
  gint rsize;     
  gint nlat;
#define NLAT_MIN 12
#define NLAT_MAX 50
#define RSIZE_MIN  10
#define RSIZE_MAX 250

#define NLAT_V_MIN 0
#define NLAT_V_MAX (NLAT_MIN)
#define RSIZE_V_MIN  0
#define RSIZE_V_MAX (RSIZE_MIN)

#define NLAT_MINI 3
#define NLAT_MAXI 100

  static float fac = -1.0f, fac_v = -1.0f;

  g_return_val_if_fail(VISU_IS_GL_VIEW(view), 0);

  DBG_fprintf(stderr, "Visu GlView: get GL details for window %dx%d.\n",
              view->window.width, view->window.height);
  DBG_fprintf(stderr, " | gross = %g.\n", view->camera.gross);
  DBG_fprintf(stderr, " | persp = %g.\n", view->camera.d_red);
  DBG_fprintf(stderr, " | lgth0 = %g.\n", view->camera.length0);

  /* calculate once fac and fac_v!... */
  if(fac < 0.0f) {
    fac = ((float)(NLAT_MAX - NLAT_MIN))/(RSIZE_MAX - RSIZE_MIN);
    fac_v = ((float)(NLAT_V_MAX - NLAT_V_MIN))/(RSIZE_V_MAX - RSIZE_V_MIN);
  }

  rsize = (int)((float)MIN(view->window.width, view->window.height) *
		(0.5 * dimension / view->camera.length0 * view->camera.gross *
		 view->camera.d_red / (view->camera.d_red - 1.)));
  
  if(rsize < RSIZE_MIN) {
    nlat = (int)(NLAT_V_MIN + fac_v * (rsize - RSIZE_V_MIN));
    if(nlat < NLAT_MINI) nlat = NLAT_MINI;
  }
  else if(rsize > RSIZE_MAX) {
    nlat = NLAT_MAX;
  }
  else {
    nlat = (int)(NLAT_MIN + fac * (rsize - RSIZE_MIN));
  }

  nlat = (int)((float)nlat * view->priv->precision);
  nlat = CLAMP(nlat, NLAT_MINI, NLAT_MAXI);
   
  DBG_fprintf(stderr, " | nlat  = %d.\n", nlat);
  return nlat;
}
/**
 * visu_gl_view_setPrecision:
 * @view: a #VisuGlView object.
 * @value: a positive value (1. is normal precision).
 *
 * This function change the value of the parameter precisionOfRendering. It
 * changes the number of facettes advised for every objects. It allows to
 * increase or decrease the number of polygons drawn and thus acts on the
 * speed of rendering.
 *
 * Returns: TRUE if value is actually changed.
 */
gboolean visu_gl_view_setPrecision(VisuGlView *view, float value)
{
  g_return_val_if_fail(VISU_IS_GL_VIEW(view), FALSE);

  if (value <= 0. || value == view->priv->precision)
    return FALSE;
  
  view->priv->precision = value;
  _defaultDetails = value * 100.f;
  g_object_notify_by_pspec(G_OBJECT(view), properties[PRECISION_PROP]);
  g_signal_emit(G_OBJECT(view), _signals[FACETTES_CHANGED_SIGNAL], 0);

  return TRUE;
}
/**
 * visu_gl_view_getPrecision:
 * @view: a #VisuGlView object.
 *
 * This function retrieve the value of the parameter precisionOfRendering.
 *
 * Returns: the actual precision.
 */
float visu_gl_view_getPrecision(const VisuGlView *view)
{
  g_return_val_if_fail(VISU_IS_GL_VIEW(view), 1.f);

  return view->priv->precision;
}
/**
 * visu_gl_window_getFileUnitPerPixel:
 * @window: a valid #VisuGlWindow object.
 *
 * This method is used to know the ratio of a pixel with the unit of the file.
 * WARNING : this method is valid only when the camera is position at infinity.
 * 
 * Returns: how much of a unit of file is in a pixel.
 */
float visu_gl_window_getFileUnitPerPixel(VisuGlWindow *window)
{
  float deltaH, deltaV;

  g_return_val_if_fail(window, 0.);

  deltaH = window->right - window->left;
  deltaV = window->top - window->bottom;
  if (deltaH < deltaV)
    return deltaH / (float)window->width;
  else
    return deltaV / (float)window->height;
}
/**
 * visu_gl_camera_getScreenAxes:
 * @camera: a valid #VisuGlCamera.
 * @xAxis: (in) (array fixed-size=3): three float values representing x axis ;
 * @yAxis: (in) (array fixed-size=3): three float values representing y axis.
 *
 * This method is used to get the coordinates in box frame of x axis and y axis
 * of the current camera view.
 */
void visu_gl_camera_getScreenAxes(VisuGlCamera *camera, float xAxis[3], float yAxis[3])
{
  double cth, sth, cph, sph, com, som;
  float matPhi[3][3], matTheta[3][3], matOmega[3][3];
  float matRes[3][3], matRes2[3][3];
  float axis[3];

  g_return_if_fail(camera);

  cth = cos(camera->theta * TOOL_PI180);
  sth = sin(camera->theta * TOOL_PI180);
  cph = cos(camera->phi * TOOL_PI180);
  sph = sin(camera->phi * TOOL_PI180);
  com = cos(camera->omega * TOOL_PI180);
  som = sin(camera->omega * TOOL_PI180);

  matPhi[0][0] = cph;
  matPhi[1][0] = sph;
  matPhi[2][0] = 0.;
  matPhi[0][1] = -sph;
  matPhi[1][1] = cph;
  matPhi[2][1] = 0.;
  matPhi[0][2] = 0.;
  matPhi[1][2] = 0.;
  matPhi[2][2] = 1.;

  matTheta[0][0] = cth;
  matTheta[1][0] = 0.;
  matTheta[2][0] = -sth;
  matTheta[0][1] = 0.;
  matTheta[1][1] = 1.;
  matTheta[2][1] = 0.;
  matTheta[0][2] = sth;
  matTheta[1][2] = 0.;
  matTheta[2][2] = cth;

  matOmega[0][0] = com;
  matOmega[1][0] = som;
  matOmega[2][0] = 0.;
  matOmega[0][1] = -som;
  matOmega[1][1] = com;
  matOmega[2][1] = 0.;
  matOmega[0][2] = 0.;
  matOmega[1][2] = 0.;
  matOmega[2][2] = 1.;

  tool_matrix_productMatrix(matRes, matTheta, matOmega);
  tool_matrix_productMatrix(matRes2, matPhi, matRes);

  axis[0] = 0.;
  axis[1] = 1.;
  axis[2] = 0.;
  tool_matrix_productVector(xAxis, matRes2, axis);

  axis[0] = -1.;
  axis[1] = 0.;
  axis[2] = 0.;
  tool_matrix_productVector(yAxis, matRes2, axis);
}
/**
 * visu_gl_view_getZCoordinate:
 * @view: a #VisuGlView object.
 * @xyz: a cartesian point.
 *
 * Use this routine to know the Z value of a real point defined by
 * @xyz in caretsian coordinates.
 */
float visu_gl_view_getZCoordinate(VisuGlView *view, float xyz[3])
{
  GLdouble model[16], project[16];
  GLint viewport[4];
  GLdouble xyzGL[3], winGL[3];

  g_return_val_if_fail(view, 0.5f);

  glGetDoublev(GL_MODELVIEW_MATRIX, model);
  glGetDoublev(GL_PROJECTION_MATRIX, project);
  glGetIntegerv(GL_VIEWPORT, viewport);
  
  xyzGL[0] = (GLdouble)xyz[0];
  xyzGL[1] = (GLdouble)xyz[1];
  xyzGL[2] = (GLdouble)xyz[2];
  gluProject(xyzGL[0], xyzGL[1], xyzGL[2], model, project, viewport,
	     winGL, winGL + 1, winGL + 2);
  DBG_fprintf(stderr, "OpenGL View: get z coordinates from %gx%gx%g: %g.\n",
	      xyz[0], xyz[1], xyz[2], (float)winGL[2]);
  DBG_fprintf(stderr, " | win coords: %gx%g\n",
	      (float)winGL[0], (float)winGL[1]);
  return (float)winGL[2];
}
/**
 * visu_gl_view_getRealCoordinates:
 * @view: a #VisuGlView object.
 * @xyz: a location to store the result.
 * @winx: position on X axis of screen.
 * @winy: position on Y axis of screen.
 * @winz: height before projection on screen.
 *
 * Use this routine to get the cartesian coordinates in real space of
 * a point located at @winx and @winy on screen.
 */
void visu_gl_view_getRealCoordinates(VisuGlView *view, float xyz[3],
				   float winx, float winy, float winz)
{
  GLdouble model[16], project[16];
  GLint viewport[4];
  GLdouble xyzGL[3], winGL[3];

  g_return_if_fail(view);

  glGetDoublev(GL_MODELVIEW_MATRIX, model);
  glGetDoublev(GL_PROJECTION_MATRIX, project);
  glGetIntegerv(GL_VIEWPORT, viewport);
  
  winGL[0] = (GLdouble)winx;
  winGL[1] = (GLdouble)(view->window.height - winy);
  winGL[2] = (GLdouble)winz;
  gluUnProject(winGL[0], winGL[1], winGL[2], model, project, viewport,
	       xyzGL, xyzGL + 1, xyzGL + 2);
  xyz[0] = (float)xyzGL[0];
  xyz[1] = (float)xyzGL[1];
  xyz[2] = (float)xyzGL[2];
  DBG_fprintf(stderr, "OpenGL View: get real coordinates from %gx%gx%g: %gx%gx%g.\n",
	      winx, winy, winz, xyz[0], xyz[1], xyz[2]);
}

/**
 * visu_gl_view_alignToAxis:
 * @view: a #VisuGlView object.
 * @axis: an axis.
 *
 * Rotate the view to align it with the given box axis.
 *
 * Since: 3.8
 **/
void visu_gl_view_alignToAxis(VisuGlView *view, ToolXyzDir axis)
{
  float red[3], xyz[3], xyz_[3], sph[3];
  int permut[3][3] = {{1,2,0}, {2,0,1}, {0,1,2}};

  g_return_if_fail(VISU_IS_GL_VIEW(view));

  if (!view->priv->box)
    return;

  red[0] = (axis == TOOL_XYZ_X) ? 1.f : 0.f;
  red[1] = (axis == TOOL_XYZ_Y) ? 1.f : 0.f;
  red[2] = (axis == TOOL_XYZ_Z) ? 1.f : 0.f;
  visu_box_convertBoxCoordinatestoXYZ(view->priv->box, xyz, red);
  xyz_[0] = xyz[permut[view->camera.upAxis][0]];
  xyz_[1] = xyz[permut[view->camera.upAxis][1]];
  xyz_[2] = xyz[permut[view->camera.upAxis][2]];
  tool_matrix_cartesianToSpherical(sph, xyz_);

  g_object_set(view, "theta", sph[TOOL_MATRIX_SPHERICAL_THETA],
               "phi", sph[TOOL_MATRIX_SPHERICAL_PHI], NULL);
}

/**
 * visu_gl_view_setThetaPhiOmega:
 * @view: a #VisuGlView object ;
 * @valueTheta: a floatinf point value in degrees ;
 * @valuePhi: a floating point value in degrees ;
 * @valueOmega: a floating point value in degrees ;
 * @mask: to specified what values will be changed.
 *
 * This method is used to change the camera orientation for the given @view.
 * If necessary, this method will emit the 'ThetaPhiOmegaChanged' signal.
 *
 * Returns: TRUE if value is actually changed.
 */

gboolean visu_gl_view_setThetaPhiOmega(VisuGlView *view, float valueTheta,
                                       float valuePhi, float valueOmega, int mask)
{
  int res;

  g_return_val_if_fail(VISU_IS_GL_VIEW(view), FALSE);

  DBG_fprintf(stderr, "OpenGL View: changing theta / phi / omega (%d).\n", mask);
  DBG_fprintf(stderr, "OpenGL View: to %g / %g / %g.\n",
              valueTheta, valuePhi, valueOmega);
  res = visu_gl_camera_setThetaPhiOmega(&view->camera,
                                        valueTheta, valuePhi, valueOmega, mask);
  DBG_fprintf(stderr, "OpenGL View: to %g / %g / %g.\n",
              view->camera.theta, view->camera.phi, view->camera.omega);
  if (res & VISU_GL_CAMERA_THETA)
    g_object_notify_by_pspec(G_OBJECT(view), properties[THETA_PROP]);
  if (res & VISU_GL_CAMERA_PHI)
    g_object_notify_by_pspec(G_OBJECT(view), properties[PHI_PROP]);
  if (res & VISU_GL_CAMERA_OMEGA)
    g_object_notify_by_pspec(G_OBJECT(view), properties[OMEGA_PROP]);
  if (res)
    {
      anglesDefault[0] = view->camera.theta;
      anglesDefault[1] = view->camera.phi;
      anglesDefault[2] = view->camera.omega;
      visu_gl_camera_modelize(&view->camera);
      g_signal_emit(view, _signals[CHANGED_SIGNAL], 0);
    }
    
  return (res > 0);
}
/**
 * visu_gl_view_setXsYs:
 * @view: a #VisuGlView object ;
 * @valueX: a floatinf point value in the bounding box scale
 *          (1 is the size of the bounding box) ;
 * @valueY: a floating point value in bounding box scale ;
 * @mask: to specified what values will be changed.
 *
 * This method is used to change the camera position for the given @view.
 * If necessary, this method will emit the 'XsYsChanged' signal.
 *
 * Returns: TRUE if value is actually changed.
 */
gboolean visu_gl_view_setXsYs(VisuGlView *view,
                              float valueX, float valueY, int mask)
{
  int res;

  g_return_val_if_fail(VISU_IS_GL_VIEW(view), FALSE);
  
  res = visu_gl_camera_setXsYs(&view->camera, valueX, valueY, mask);
  if (res & VISU_GL_CAMERA_XS)
    g_object_notify_by_pspec(G_OBJECT(view), properties[XS_PROP]);
  if (res & VISU_GL_CAMERA_YS)
    g_object_notify_by_pspec(G_OBJECT(view), properties[YS_PROP]);
  if (res)
    {
      translatDefault[0] = view->camera.xs;
      translatDefault[1] = view->camera.ys;
      visu_gl_window_project(&view->window, &view->camera);
      g_signal_emit(view, _signals[CHANGED_SIGNAL], 0);
    }
    
  return (res > 0);
}

/**
 * visu_gl_view_setGross:
 * @view: a #VisuGlView object ;
 * @value: a positive floating point value.
 *
 * This method is used to change the camera zoom for the given @view.
 * If necessary, this method will emit the 'GrossChanged' signal and
 * the 'FacetteChangedChanged' signal.
 *
 * Returns: TRUE if value is actually changed.
 */
gboolean visu_gl_view_setGross(VisuGlView *view, float value)
{
  gboolean res;

  g_return_val_if_fail(VISU_IS_GL_VIEW(view), FALSE);

  res = visu_gl_camera_setGross(&view->camera, value);
  if (res)
    {
      grossDefault = view->camera.gross;

      g_object_notify_by_pspec(G_OBJECT(view), properties[GROSS_PROP]);
      g_signal_emit(view, _signals[FACETTES_CHANGED_SIGNAL],
		    0, NULL);

      visu_gl_window_project(&view->window, &view->camera);
      g_signal_emit(view, _signals[CHANGED_SIGNAL], 0);
    }
  return res;
}

/**
 * visu_gl_view_setPersp:
 * @view: a #VisuGlView object ;
 * @value: a positive floating point value (> 1.1).
 *
 * This method is used to change the camera perspective for the given @view.
 * If necessary, this method will emit the 'PerspChanged' signal and
 * the 'FacetteChangedChanged' signal.
 *
 * Returns: TRUE if value is actually changed.
 */
gboolean visu_gl_view_setPersp(VisuGlView *view, float value)
{
  gboolean res;

  g_return_val_if_fail(VISU_IS_GL_VIEW(view), FALSE);
  
  res = visu_gl_camera_setPersp(&view->camera, value);
  if (res)
    {
      perspDefault = view->camera.d_red;
      
      DBG_fprintf(stderr, "Visu GlView: emitting signals on persp changed.\n");
      g_object_notify_by_pspec(G_OBJECT(view), properties[PERSP_PROP]);
      g_signal_emit(view, _signals[FACETTES_CHANGED_SIGNAL],
		    0, NULL);

      visu_gl_camera_modelize(&view->camera);
      visu_gl_window_project(&view->window, &view->camera);
      g_signal_emit(view, _signals[CHANGED_SIGNAL], 0);
    }
    
  return res;
}

/**
 * visu_gl_view_setViewport:
 * @view: a #VisuGlView object ;
 * @width: the new horizontal size ;
 * @height: the new vertical size.
 *
 * It changes the size of the OpenGl area and reccompute the OpenGL viewport.
 * Warning : it doesn't change the size of the window.
 *
 * Returns: TRUE if value is actually changed.
 */
gboolean visu_gl_view_setViewport(VisuGlView *view, guint width, guint height)
{
  gboolean res;

  g_return_val_if_fail(VISU_IS_GL_VIEW(view), FALSE);
  
  res = visu_gl_window_setViewport(&view->window, width, height);
  if (res)
    {
      g_signal_emit(view, _signals[WIDTH_HEIGHT_CHANGED_SIGNAL],
		    0, NULL);
      g_signal_emit(view, _signals[FACETTES_CHANGED_SIGNAL],
		    0, NULL);

      visu_gl_window_project(&view->window, &view->camera);
      g_signal_emit(view, _signals[CHANGED_SIGNAL], 0);
    }
    
  return res;
}
/**
 * visu_gl_view_setRefLength:
 * @view: a #VisuGlView object ;
 * @lg: the new value.
 * @units: the unit to read @lg with.
 *
 * This method is used to change the camera reference length for the given @view.
 * If necessary, this method will modelize and emit the 'RefLengthChanged' signal.
 *
 * Returns: TRUE if value is actually changed.
 */
gboolean visu_gl_view_setRefLength(VisuGlView *view, float lg, ToolUnits units)
{
  gboolean res;

  g_return_val_if_fail(VISU_IS_GL_VIEW(view), FALSE);
  
  res = visu_gl_camera_setRefLength(&view->camera, lg, units);
  if (res)
    {
      g_signal_emit(view, _signals[REF_LENGTH_CHANGED_SIGNAL],
                    0, NULL);
      g_signal_emit(view, _signals[FACETTES_CHANGED_SIGNAL],
		    0, NULL);
      visu_gl_camera_modelize(&view->camera);
      g_signal_emit(view, _signals[CHANGED_SIGNAL], 0);
    }
    
  return res;
}
/**
 * visu_gl_view_setObjectRadius:
 * @view: a #VisuGlView object ;
 * @lg: the new value.
 * @units: the unit to read @lg with.
 *
 * This method is used to change the window frustum for the given @view.
 * If necessary, this method will project and emit the
 * 'NearFarChanged' signal.
 *
 * Since: 3.7
 *
 * Returns: TRUE if value is actually changed.
 */
gboolean visu_gl_view_setObjectRadius(VisuGlView *view, float lg, ToolUnits units)
{
  gboolean res;

  g_return_val_if_fail(VISU_IS_GL_VIEW(view), FALSE);
  
  res = visu_gl_window_setAddLength(&view->window, lg, units);
  if (res)
    {
      visu_gl_window_project(&view->window, &view->camera);
      g_signal_emit(view, _signals[CHANGED_SIGNAL], 0);
    }
    
  return res;
}

/**
 * visu_gl_view_rotateBox:
 * @view: a valid #VisuGlView object ;
 * @dTheta: a float value ;
 * @dPhi: a float value ;
 * @angles: (out) (array fixed-size=2): a storing area two floats.
 *
 * This methods rotates the camera of the given @view of (@dTheta, @dPhi) and
 * put new theta and phi angles in @angles, first being theta and second phi.
 */
void visu_gl_view_rotateBox(VisuGlView *view, float dTheta, float dPhi, float angles[2])
{
  GValue val = G_VALUE_INIT;
  
  g_return_if_fail(view && angles);

  g_value_init(&val, G_TYPE_DOUBLE);
  if (visu_animation_isRunning(view->priv->theta_anim))
    {
      visu_animation_getTo(view->priv->theta_anim, &val);
      angles[0] = g_value_get_double(&val) + dTheta;
    }
  else
    angles[0] = view->camera.theta + dTheta;
  if (visu_animation_isRunning(view->priv->phi_anim))
    {
      visu_animation_getTo(view->priv->phi_anim, &val);
      angles[1] = g_value_get_double(&val) + dPhi;
    }
  else
    angles[1] = view->camera.phi + dPhi;
}
/**
 * visu_gl_view_rotateCamera:
 * @view: a valid #VisuGlView object ;
 * @dTheta: a float value ;
 * @dPhi: a float value ;
 * @angles: (out) (array fixed-size=3): a storing area three floats.
 *
 * This methods rotates the camera of the given @view of (@dTheta, @dPhi).
 * @dTheta is taken as displacement along camera x axis and dPhi along camera y axis.
 * Then, computations are done to obtain new theta, phi and omega values. They are
 * put in @angles, first being theta, second phi and third omega.
 */
void visu_gl_view_rotateCamera(VisuGlView *view, float dTheta, float dPhi, float angles[3])
{
  double cth, sth, cph, sph, com, som;
  double cdth, sdth, cdph, sdph;
  double Theta, Phi, Omega;
  #define RADTODEG 57.29577951
  float MinRprime[3], MinR[3];
  float Mspherical[3];
  float matPhi[3][3], matTheta[3][3], matOmega[3][3], matdPhi[3][3], matdTheta[3][3];
  float matPhiPrime[3][3], matThetaPrime[3][3];
  float matRprime2R[3][3];
  float matRes[3][3], matRes2[3][3];

  g_return_if_fail(view && angles);

  cth = cos(view->camera.theta * TOOL_PI180);
  sth = sin(view->camera.theta * TOOL_PI180);
  cph = cos(view->camera.phi * TOOL_PI180);
  sph = sin(view->camera.phi * TOOL_PI180);
  com = cos(view->camera.omega * TOOL_PI180);
  som = sin(view->camera.omega * TOOL_PI180);

  cdth = cos(dTheta * TOOL_PI180);
  sdth = sin(dTheta * TOOL_PI180);
  cdph = cos(dPhi * TOOL_PI180);
  sdph = sin(dPhi * TOOL_PI180);


  matPhi[0][0] = cph;
  matPhi[1][0] = sph;
  matPhi[2][0] = 0.;
  matPhi[0][1] = -sph;
  matPhi[1][1] = cph;
  matPhi[2][1] = 0.;
  matPhi[0][2] = 0.;
  matPhi[1][2] = 0.;
  matPhi[2][2] = 1.;

  matTheta[0][0] = cth;
  matTheta[1][0] = 0.;
  matTheta[2][0] = -sth;
  matTheta[0][1] = 0.;
  matTheta[1][1] = 1.;
  matTheta[2][1] = 0.;
  matTheta[0][2] = sth;
  matTheta[1][2] = 0.;
  matTheta[2][2] = cth;

  matOmega[0][0] = com;
  matOmega[1][0] = som;
  matOmega[2][0] = 0.;
  matOmega[0][1] = -som;
  matOmega[1][1] = com;
  matOmega[2][1] = 0.;
  matOmega[0][2] = 0.;
  matOmega[1][2] = 0.;
  matOmega[2][2] = 1.;

  matdPhi[0][0] = 1.;
  matdPhi[1][0] = 0.;
  matdPhi[2][0] = 0.;
  matdPhi[0][1] = 0.;
  matdPhi[1][1] = cdph;
  matdPhi[2][1] = -sdph;
  matdPhi[0][2] = 0.;
  matdPhi[1][2] = sdph;
  matdPhi[2][2] = cdph;

  matdTheta[0][0] = cdth;
  matdTheta[1][0] = 0.;
  matdTheta[2][0] = -sdth;
  matdTheta[0][1] = 0.;
  matdTheta[1][1] = 1.;
  matdTheta[2][1] = 0.;
  matdTheta[0][2] = sdth;
  matdTheta[1][2] = 0.;
  matdTheta[2][2] = cdth;

  tool_matrix_productMatrix(matRes, matdPhi, matdTheta);
  tool_matrix_productMatrix(matRes2, matOmega, matRes);
  tool_matrix_productMatrix(matRes, matTheta, matRes2);
  tool_matrix_productMatrix(matRprime2R, matPhi, matRes);

  MinRprime[0] = 0.;
  MinRprime[1] = 0.;
  MinRprime[2] = 1.;
  tool_matrix_productVector(MinR, matRprime2R, MinRprime);
/*   fprintf(stderr, "M : %f %f %f -> %f\n", MinR[0], MinR[1], MinR[2], */
/* 	  MinR[0]*MinR[0] + MinR[1]*MinR[1] + MinR[2]*MinR[2]); */

/*   cartesian_to_spherical(Mspherical, MinR); */
  Mspherical[0] = sqrt(MinR[0]*MinR[0] + MinR[1]*MinR[1] + MinR[2]*MinR[2]);
  if (MinR[1] == 0 && MinR[0] == 0)
    {
      Mspherical[1] = (MinR[2] > 0.)?0.:180.;
      Mspherical[2] = view->camera.phi;
    }
  else
    {
      Mspherical[1] = acos(MinR[2] / Mspherical[0]) * RADTODEG;
      if (MinR[0] == 0.)
	Mspherical[2] = (MinR[1] > 0.)?90.:-90.;
      else
	{
	  Mspherical[2] = atan(MinR[1] / MinR[0]) * RADTODEG;
	  if (MinR[0] < 0.)
	    Mspherical[2] += 180.;
	}
    }
/*   fprintf(stderr, "avant %f %f\n", Mspherical[1], Mspherical[2]); */
  while (Mspherical[1] - view->camera.theta < -90.)
    Mspherical[1] += 360.;
  while (Mspherical[1] - view->camera.theta > 90.)
    Mspherical[1] -= 360.;
  while (Mspherical[2] - view->camera.phi < -90.)
    Mspherical[2] += 360.;
  while (Mspherical[2] - view->camera.phi > 90.)
    Mspherical[2] -= 360.;
/*   fprintf(stderr, "après %f %f\n", Mspherical[1], Mspherical[2]); */

  Theta = Mspherical[1];
  Phi = Mspherical[2];

/*   fprintf(stderr, "%f %f, %f %f\n", view->camera.theta, view->camera.phi, Theta, Phi); */
/*   fprintf(stderr, "%f %f, %f %f\n", dTheta, dPhi, Theta - view->camera.theta, Phi -  view->camera.phi); */

  cth = cos(Theta * TOOL_PI180);
  sth = sin(Theta * TOOL_PI180);
  cph = cos(Phi * TOOL_PI180);
  sph = sin(Phi * TOOL_PI180);

  matPhiPrime[0][0] = cph;
  matPhiPrime[1][0] = -sph;
  matPhiPrime[2][0] = 0.;
  matPhiPrime[0][1] = sph;
  matPhiPrime[1][1] = cph;
  matPhiPrime[2][1] = 0.;
  matPhiPrime[0][2] = 0.;
  matPhiPrime[1][2] = 0.;
  matPhiPrime[2][2] = 1.;

  matThetaPrime[0][0] = cth;
  matThetaPrime[1][0] = 0.;
  matThetaPrime[2][0] = sth;
  matThetaPrime[0][1] = 0.;
  matThetaPrime[1][1] = 1.;
  matThetaPrime[2][1] = 0.;
  matThetaPrime[0][2] = -sth;
  matThetaPrime[1][2] = 0.;
  matThetaPrime[2][2] = cth;

  tool_matrix_productMatrix(matRes2, matPhiPrime, matRprime2R);
  tool_matrix_productMatrix(matRes, matThetaPrime, matRes2);

  MinRprime[0] = 0.;
  MinRprime[1] = 1.;
  MinRprime[2] = 0.;
  tool_matrix_productVector(MinR, matRes, MinRprime);
/*   fprintf(stderr, "vect u : %f %f %f -> %f\n", MinR[0], MinR[1], MinR[2], */
/* 	  MinR[0]*MinR[0] + MinR[1]*MinR[1] + MinR[2]*MinR[2]); */
  Omega = acos(CLAMP(MinR[1], -1.f, 1.f)) * RADTODEG;
  if (MinR[0] > 0.)
    Omega = -Omega;
  while (Omega - view->camera.omega < -90.)
    Omega += 360.;
  while (Omega - view->camera.omega > 90.)
    Omega -= 360.;

  /*   fprintf(stderr, "Theta phi omega : %f %f %f\n", Theta, Phi, Omega); */
  angles[0] = Theta;
  angles[1] = Phi;
  angles[2] = Omega;
}

/***************************/
/* Dealing with resources. */
/***************************/
static void exportParametersVisuGlView(GString *data, VisuData *dataObj _U_)
{
  g_string_append_printf(data, "# %s\n", DESC_PARAMETER_OPENGL_DETAILS);
  g_string_append_printf(data, "%s: %d\n\n", FLAG_PARAMETER_OPENGL_DETAILS,
                         (int)(_defaultDetails));

  g_string_append_printf(data, "# %s\n", DESC_PARAMETER_AUTO_ADJUST);
  g_string_append_printf(data, "%s: %d\n\n", FLAG_PARAMETER_AUTO_ADJUST, autoAdjustDefault);
}
static void onEntryPrecision(VisuGlView *view, VisuConfigFileEntry *entry _U_, VisuConfigFile *obj _U_)
{
  visu_gl_view_setPrecision(view, _defaultDetails / 100.f);
}
static void onEntryAngles(VisuGlView *view, VisuConfigFileEntry *entry _U_, VisuConfigFile *obj _U_)
{
  visu_gl_view_setThetaPhiOmega
    (view, anglesDefault[0], anglesDefault[1], anglesDefault[2],
     VISU_GL_CAMERA_THETA | VISU_GL_CAMERA_PHI | VISU_GL_CAMERA_OMEGA);
}
static void onEntryTrans(VisuGlView *view, VisuConfigFileEntry *entry _U_, VisuConfigFile *obj _U_)
{
  visu_gl_view_setXsYs(view, translatDefault[0], translatDefault[1],
                       VISU_GL_CAMERA_XS | VISU_GL_CAMERA_YS);
}
static void onEntryGross(VisuGlView *view, VisuConfigFileEntry *entry _U_, VisuConfigFile *obj _U_)
{
  visu_gl_view_setGross(view, grossDefault);
}
static void onEntryPersp(VisuGlView *view, VisuConfigFileEntry *entry _U_, VisuConfigFile *obj _U_)
{
  visu_gl_view_setPersp(view, perspDefault);
}
static void exportResourcesVisuGlView(GString *data, VisuData *dataObj _U_)
{
  visu_config_file_exportComment(data, DESC_RESOURCE_OPENGL_ANGLES);
  visu_config_file_exportEntry(data, FLAG_RESOURCE_OPENGL_ANGLES, NULL,
                               "%9.3f %9.3f %9.3f",
                               anglesDefault[0], anglesDefault[1], anglesDefault[2]);

  visu_config_file_exportComment(data, DESC_RESOURCE_OPENGL_TRANSLAT);
  visu_config_file_exportEntry(data, FLAG_RESOURCE_OPENGL_TRANSLAT, NULL,
                               "%9.3f %9.3f",
                               translatDefault[0], translatDefault[1]);

  visu_config_file_exportComment(data, DESC_RESOURCE_OPENGL_GROSS);
  visu_config_file_exportEntry(data, FLAG_RESOURCE_OPENGL_GROSS, NULL,
                               "%9.3f", grossDefault);

  visu_config_file_exportComment(data, DESC_RESOURCE_OPENGL_PERSP);
  visu_config_file_exportEntry(data, FLAG_RESOURCE_OPENGL_PERSP, NULL,
                               "%9.3f", perspDefault);

  visu_config_file_exportComment(data, "");
}

