/*   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 "axes.h"

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

#include <math.h>
#include <string.h>

#include <opengl.h>
#include <openGLFunctions/text.h>
#include <visu_tools.h>
#include <visu_configFile.h>
#include <openGLFunctions/objectList.h>
#include <coreTools/toolColor.h>

#undef near
#undef far

/**
 * SECTION:axes
 * @short_description: Defines methods to draw axes.
 *
 * <para>The axes are the X, Y and Z lines drawn on the bottom right of the
 * screen defining a given orthogonal basis set in which the box is
 * projected.</para>
 * <para>The axis may be different, depending on the rendering method
 * currently used. For instance, when the spin is used, a projection
 * of the colour scheme is added to the simple lines of the basis
 * set. Besides that, axes are defined by their width (see
 * visu_gl_ext_lined_setWidth()) and their colour (see
 * visu_gl_ext_lined_setRGBA()).</para>
 */

/* Parameters & resources*/
/* This is a boolean to control is the axes is render or not. */
#define FLAG_RESOURCE_AXES_USED   "axes_are_on"
#define DESC_RESOURCE_AXES_USED   "Control if the axes are drawn ; boolean (0 or 1)"
static gboolean RESOURCE_AXES_USED_DEFAULT = FALSE;
/* A resource to control the color used to render the lines of the axes. */
#define FLAG_RESOURCE_AXES_COLOR   "axes_color"
#define DESC_RESOURCE_AXES_COLOR   "Define the color of the axes ; three floating point values (0. <= v <= 1.)"
static float rgbDefault[4] = {1.0, 0.5, 0.1, 1.};
/* A resource to control the width to render the lines of the axes. */
#define FLAG_RESOURCE_AXES_LINE   "axes_line_width"
#define DESC_RESOURCE_AXES_LINE   "Define the width of the lines of the axes ; one floating point values (1. <= v <= 10.)"
static float LINE_WIDTH_DEFAULT = 1.f;
/* A resource to control the stipple to render the lines of the axes. */
#define FLAG_RESOURCE_AXES_STIPPLE   "axes_line_stipple"
#define DESC_RESOURCE_AXES_STIPPLE   "Dot scheme detail for the lines of the axes ; 0 < integer < 2^16"
static guint16 LINE_STIPPLE_DEFAULT = 65535;
/* A resource to control the position to render the axes. */
#define FLAG_RESOURCE_AXES_POSITION   "axes_position"
#define DESC_RESOURCE_AXES_POSITION   "Position of the representation of the axes ; two floating point values (0. <= v <= 1.)"
static float POSITION_DEFAULT[2] = {1.f, 1.f};

#define FLAG_RESOURCE_AXES_LABEL_X    "axes_label_x"
#define FLAG_RESOURCE_AXES_LABEL_Y    "axes_label_y"
#define FLAG_RESOURCE_AXES_LABEL_Z    "axes_label_z"
#define DESC_RESOURCE_AXES_LABEL      "Label to be drawn beside each axis ; string"
static gchar *LABEL_DEFAULT[3] = {NULL, NULL, NULL};

#define FLAG_RESOURCE_AXES_FACTOR   "axes_size"
#define DESC_RESOURCE_AXES_FACTOR   "Portion of the screen used to draw the axis ; one floating point value (0. <= v <= 1.)"
static float SIZE_DEFAULT = .16f;

/* Export function that is called by visu_module to write the
   values of resources to a file. */
static void exportResources(GString *data, VisuData *dataObj);

struct _VisuGlExtAxesPrivate
{
  gboolean dispose_has_run;

  /* Basis definition. */
  double matrix[3][3];
  VisuBox *box;
  gulong box_signal;

  /* Rendenring parameters. */
  float xpos, ypos;
  float lgFact;
  float rgb[4];
  float lineWidth;
  guint16 lineStipple;
  gchar *lbl[3];
  gboolean displayOrientation;
  float orientation[3];

  /* Signals for the current view. */
  VisuGlView *view;
  gulong widthHeight_signal, persp_signal, refLength_signal, color_signal;
};

enum
  {
    PROP_0,
    XPOS_PROP,
    YPOS_PROP,
    SIZE_PROP,
    COLOR_PROP,
    WIDTH_PROP,
    STIPPLE_PROP,
    VIEW_PROP,
    BOX_PROP,
    LBLX_PROP,
    LBLY_PROP,
    LBLZ_PROP,
    USE_ORIENTATION_PROP,
    CONE_THETA_PROP,
    CONE_PHI_PROP,
    CONE_OMEGA_PROP,
    N_PROP
  };
static GParamSpec *properties[N_PROP];

static VisuGlExtAxes* defaultAxes;

static void visu_gl_ext_lined_interface_init(VisuGlExtLinedInterface *iface);
static void visu_gl_ext_axes_finalize(GObject* obj);
static void visu_gl_ext_axes_dispose(GObject* obj);
static void visu_gl_ext_axes_get_property(GObject* obj, guint property_id,
                                          GValue *value, GParamSpec *pspec);
static void visu_gl_ext_axes_set_property(GObject* obj, guint property_id,
                                          const GValue *value, GParamSpec *pspec);
static void visu_gl_ext_axes_rebuild(VisuGlExt *ext);
static void visu_gl_ext_axes_draw(VisuGlExt *ext);
static gboolean visu_gl_ext_axes_setGlView(VisuGlExt *axes, VisuGlView *view);
static void _setBox(VisuGlExtAxes *axes, VisuBox *box);

static gboolean _setRGB(VisuGlExtLined *axes, float rgb[4], int mask);
static gboolean _setLineWidth(VisuGlExtLined *axes, float width);
static gboolean _setLineStipple(VisuGlExtLined *axes, guint16 stipple);
static float*   _getRGB(const VisuGlExtLined *axes);
static float    _getLineWidth(const VisuGlExtLined *axes);
static guint16  _getLineStipple(const VisuGlExtLined *axes);

/* Local callbacks */
static void onAxesParametersChange(VisuGlExtAxes *axes);
static void onBoxChange(VisuBox *box, float extens, gpointer data);
static void onEntryUsed(VisuGlExtAxes *axes, VisuConfigFileEntry *entry, VisuConfigFile *obj);
static void onEntryColor(VisuGlExtAxes *axes, VisuConfigFileEntry *entry, VisuConfigFile *obj);
static void onEntryWidth(VisuGlExtAxes *axes, VisuConfigFileEntry *entry, VisuConfigFile *obj);
static void onEntryStipple(VisuGlExtAxes *axes, VisuConfigFileEntry *entry, VisuConfigFile *obj);
static void onEntryPosition(VisuGlExtAxes *axes, VisuConfigFileEntry *entry, VisuConfigFile *obj);
static void onEntryLabel(VisuGlExtAxes *axes, VisuConfigFileEntry *entry, VisuConfigFile *obj);
static void onEntryFactor(VisuGlExtAxes *axes, VisuConfigFileEntry *entry, VisuConfigFile *obj);

G_DEFINE_TYPE_WITH_CODE(VisuGlExtAxes, visu_gl_ext_axes, VISU_TYPE_GL_EXT,
                        G_ADD_PRIVATE(VisuGlExtAxes)
                        G_IMPLEMENT_INTERFACE(VISU_TYPE_GL_EXT_LINED,
                                              visu_gl_ext_lined_interface_init))

static void visu_gl_ext_lined_interface_init(VisuGlExtLinedInterface *iface)
{
  iface->get_width   = _getLineWidth;
  iface->set_width   = _setLineWidth;
  iface->get_stipple = _getLineStipple;
  iface->set_stipple = _setLineStipple;
  iface->get_rgba    = _getRGB;
  iface->set_rgba    = _setRGB;
}
static void visu_gl_ext_axes_class_init(VisuGlExtAxesClass *klass)
{
  float rgColor[2] = {0.f, 1.f};
  float rgWidth[2] = {0.f, 10.f};
  float rgFactor[2] = {0.f, 1.f};
  VisuConfigFileEntry *resourceEntry;

  DBG_fprintf(stderr, "Extension Axes: creating the class of the object.\n");
  /* DBG_fprintf(stderr, "                - adding new signals ;\n"); */
  LABEL_DEFAULT[TOOL_XYZ_X] = g_strdup("x");
  LABEL_DEFAULT[TOOL_XYZ_Y] = g_strdup("y");
  LABEL_DEFAULT[TOOL_XYZ_Z] = g_strdup("z");

  DBG_fprintf(stderr, "                - adding new resources ;\n");
  resourceEntry = visu_config_file_addBooleanEntry(VISU_CONFIG_FILE_RESOURCE,
                                                   FLAG_RESOURCE_AXES_USED,
                                                   DESC_RESOURCE_AXES_USED,
                                                   &RESOURCE_AXES_USED_DEFAULT, FALSE);
  resourceEntry = visu_config_file_addFloatArrayEntry(VISU_CONFIG_FILE_RESOURCE,
                                                      FLAG_RESOURCE_AXES_COLOR,
                                                      DESC_RESOURCE_AXES_COLOR,
                                                      3, rgbDefault, rgColor, FALSE);
  resourceEntry = visu_config_file_addFloatArrayEntry(VISU_CONFIG_FILE_RESOURCE,
                                                      FLAG_RESOURCE_AXES_LINE,
                                                      DESC_RESOURCE_AXES_LINE,
                                                      1, &LINE_WIDTH_DEFAULT, rgWidth, FALSE);
  resourceEntry = visu_config_file_addStippleArrayEntry(VISU_CONFIG_FILE_RESOURCE,
                                                        FLAG_RESOURCE_AXES_STIPPLE,
                                                        DESC_RESOURCE_AXES_STIPPLE,
                                                        1, &LINE_STIPPLE_DEFAULT);
  visu_config_file_entry_setVersion(resourceEntry, 3.4f);
  resourceEntry = visu_config_file_addFloatArrayEntry(VISU_CONFIG_FILE_RESOURCE,
                                                      FLAG_RESOURCE_AXES_POSITION,
                                                      DESC_RESOURCE_AXES_POSITION,
                                                      2, POSITION_DEFAULT, rgColor, FALSE);
  visu_config_file_entry_setVersion(resourceEntry, 3.7f);
  resourceEntry = visu_config_file_addStringEntry(VISU_CONFIG_FILE_RESOURCE,
                                                  FLAG_RESOURCE_AXES_LABEL_X,
                                                  DESC_RESOURCE_AXES_LABEL,
                                                  LABEL_DEFAULT + TOOL_XYZ_X);
  visu_config_file_entry_setVersion(resourceEntry, 3.8f);
  resourceEntry = visu_config_file_addStringEntry(VISU_CONFIG_FILE_RESOURCE,
                                                  FLAG_RESOURCE_AXES_LABEL_Y,
                                                  DESC_RESOURCE_AXES_LABEL,
                                                  LABEL_DEFAULT + TOOL_XYZ_Y);
  visu_config_file_entry_setVersion(resourceEntry, 3.8f);
  resourceEntry = visu_config_file_addStringEntry(VISU_CONFIG_FILE_RESOURCE,
                                                  FLAG_RESOURCE_AXES_LABEL_Z,
                                                  DESC_RESOURCE_AXES_LABEL,
                                                  LABEL_DEFAULT + TOOL_XYZ_Z);
  visu_config_file_entry_setVersion(resourceEntry, 3.8f);
  resourceEntry = visu_config_file_addFloatArrayEntry(VISU_CONFIG_FILE_RESOURCE,
                                                      FLAG_RESOURCE_AXES_FACTOR,
                                                      DESC_RESOURCE_AXES_FACTOR,
                                                      1, &SIZE_DEFAULT, rgFactor, FALSE);
  visu_config_file_entry_setVersion(resourceEntry, 3.8f);
  visu_config_file_addExportFunction(VISU_CONFIG_FILE_RESOURCE, exportResources);

  defaultAxes = (VisuGlExtAxes*)0;

  /* Connect the overloading methods. */
  G_OBJECT_CLASS(klass)->dispose  = visu_gl_ext_axes_dispose;
  G_OBJECT_CLASS(klass)->finalize = visu_gl_ext_axes_finalize;
  G_OBJECT_CLASS(klass)->set_property = visu_gl_ext_axes_set_property;
  G_OBJECT_CLASS(klass)->get_property = visu_gl_ext_axes_get_property;
  VISU_GL_EXT_CLASS(klass)->rebuild = visu_gl_ext_axes_rebuild;
  VISU_GL_EXT_CLASS(klass)->draw = visu_gl_ext_axes_draw;
  VISU_GL_EXT_CLASS(klass)->setGlView = visu_gl_ext_axes_setGlView;

  /**
   * VisuGlExtAxes::x-pos:
   *
   * Store the position along x of the axes.
   *
   * Since: 3.8
   */
  properties[XPOS_PROP] = g_param_spec_float("x-pos", "x position",
                                             "position along x axis",
                                             0., 1., POSITION_DEFAULT[0],
                                             G_PARAM_READWRITE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), XPOS_PROP,
				  properties[XPOS_PROP]);
  /**
   * VisuGlExtAxes::y-pos:
   *
   * Store the position along y of the axes.
   *
   * Since: 3.8
   */
  properties[YPOS_PROP] = g_param_spec_float("y-pos", "y position",
                                             "position along y axis",
                                             0., 1., POSITION_DEFAULT[1],
                                             G_PARAM_READWRITE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), YPOS_PROP,
				  properties[YPOS_PROP]);
  /**
   * VisuGlExtAxes::size:
   *
   * Store the portion of screen the axis occupy.
   *
   * Since: 3.8
   */
  properties[SIZE_PROP] = g_param_spec_float("size", "Size",
                                               "portion of the screen for the axis",
                                               0., 1., .16f, G_PARAM_READWRITE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), SIZE_PROP,
				  properties[SIZE_PROP]);
  g_object_class_override_property(G_OBJECT_CLASS(klass), COLOR_PROP, "color");
  g_object_class_override_property(G_OBJECT_CLASS(klass), WIDTH_PROP, "width");
  g_object_class_override_property(G_OBJECT_CLASS(klass), STIPPLE_PROP, "stipple");
  /**
   * VisuGlExtAxes::view:
   *
   * Store the view where the axes are rendered.
   *
   * Since: 3.8
   */
  properties[VIEW_PROP] = g_param_spec_object("view", "OpenGl View",
                                              "rendering view for the axes",
                                              VISU_TYPE_GL_VIEW, G_PARAM_READWRITE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), VIEW_PROP,
				  properties[VIEW_PROP]);
  /**
   * VisuGlExtAxes::basis:
   *
   * Store the #VisuBoxed object that defines the axes. If %NULL, a
   * cartesian basis-set is assumed.
   *
   * Since: 3.8
   */
  properties[BOX_PROP] = g_param_spec_object("basis", "basis-set",
                                             "provides the basis-set to draw the axes",
                                             VISU_TYPE_BOX, G_PARAM_READWRITE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), BOX_PROP,
				  properties[BOX_PROP]);
  /**
   * VisuGlExtAxes::x-label:
   *
   * Store the label for x axis.
   *
   * Since: 3.8
   */
  properties[LBLX_PROP] = g_param_spec_string("x-label", "X label", "label for the x axis",
                                             "x", G_PARAM_READWRITE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), LBLX_PROP,
				  properties[LBLX_PROP]);
  /**
   * VisuGlExtAxes::y-label:
   *
   * Store the label for y axis.
   *
   * Since: 3.8
   */
  properties[LBLY_PROP] = g_param_spec_string("y-label", "Y label", "label for the y axis",
                                             "y", G_PARAM_READWRITE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), LBLY_PROP,
				  properties[LBLY_PROP]);
  /**
   * VisuGlExtAxes::z-label:
   *
   * Store the label for z axis.
   *
   * Since: 3.8
   */
  properties[LBLZ_PROP] = g_param_spec_string("z-label", "Z label", "label for the z axis",
                                             "z", G_PARAM_READWRITE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), LBLZ_PROP,
				  properties[LBLZ_PROP]);
  /**
   * VisuGlExtAxes::display-orienation:
   *
   * If TRUE, it draws a coloured cone to display orientation information.
   *
   * Since: 3.8
   */
  properties[USE_ORIENTATION_PROP] = g_param_spec_boolean("display-orientation",
                                                          "Display orientation",
                                                          "display orientation information",
                                                          FALSE, G_PARAM_READWRITE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), USE_ORIENTATION_PROP,
				  properties[USE_ORIENTATION_PROP]);
  /**
   * VisuGlExtAxes::orientation-theta:
   *
   * Store the theta angle used to defined the coloured cone position
   * when axis are used to display a coloured orientation.
   *
   * Since: 3.8
   */
  properties[CONE_THETA_PROP] = g_param_spec_float("orientation-theta",
                                                   "Theta angle in degrees",
                                                   "theta defining top",
                                                   0., 180., 0.f, G_PARAM_READWRITE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), CONE_THETA_PROP,
				  properties[CONE_THETA_PROP]);
  /**
   * VisuGlExtAxes::orientation-phi:
   *
   * Store the phi angle used to defined the coloured cone position
   * when axis are used to display a coloured orientation.
   *
   * Since: 3.8
   */
  properties[CONE_PHI_PROP] = g_param_spec_float("orientation-phi",
                                                   "Phi angle in degrees",
                                                   "phi defining top",
                                                   0., 360., 0.f, G_PARAM_READWRITE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), CONE_PHI_PROP,
				  properties[CONE_PHI_PROP]);
  /**
   * VisuGlExtAxes::orientation-omega:
   *
   * Store the omega angle used to defined the coloured cone position
   * when axis are used to display a coloured orientation.
   *
   * Since: 3.8
   */
  properties[CONE_OMEGA_PROP] = g_param_spec_float("orientation-omega",
                                                   "Omega angle in degrees",
                                                   "omega defining top",
                                                   0., 360., 0.f, G_PARAM_READWRITE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), CONE_OMEGA_PROP,
				  properties[CONE_OMEGA_PROP]);
}

static void visu_gl_ext_axes_init(VisuGlExtAxes *obj)
{
  DBG_fprintf(stderr, "Extension Axes: initializing a new object (%p).\n",
	      (gpointer)obj);
  
  obj->priv = visu_gl_ext_axes_get_instance_private(obj);
  obj->priv->dispose_has_run = FALSE;

  /* Private data. */
  obj->priv->rgb[0]      = rgbDefault[0];
  obj->priv->rgb[1]      = rgbDefault[1];
  obj->priv->rgb[2]      = rgbDefault[2];
  obj->priv->rgb[3]      = 1.f;
  obj->priv->lgFact      = SIZE_DEFAULT;
  obj->priv->lineWidth   = LINE_WIDTH_DEFAULT;
  obj->priv->lineStipple = LINE_STIPPLE_DEFAULT;
  obj->priv->xpos        = POSITION_DEFAULT[0];
  obj->priv->ypos        = POSITION_DEFAULT[1];
  obj->priv->view               = (VisuGlView*)0;
  obj->priv->widthHeight_signal = 0;
  obj->priv->persp_signal     = 0;
  obj->priv->refLength_signal   = 0;
  obj->priv->color_signal       = 0;
  obj->priv->matrix[0][0] = 1.;
  obj->priv->matrix[0][1] = 0.;
  obj->priv->matrix[0][2] = 0.;
  obj->priv->matrix[1][0] = 0.;
  obj->priv->matrix[1][1] = 1.;
  obj->priv->matrix[1][2] = 0.;
  obj->priv->matrix[2][0] = 0.;
  obj->priv->matrix[2][1] = 0.;
  obj->priv->matrix[2][2] = 1.;
  obj->priv->box          = (VisuBox*)0;
  obj->priv->box_signal   = 0;
  obj->priv->lbl[0]       = g_strdup(LABEL_DEFAULT[TOOL_XYZ_X]);
  obj->priv->lbl[1]       = g_strdup(LABEL_DEFAULT[TOOL_XYZ_Y]);
  obj->priv->lbl[2]       = g_strdup(LABEL_DEFAULT[TOOL_XYZ_Z]);
  obj->priv->displayOrientation = FALSE;
  obj->priv->orientation[0] = 0.f;
  obj->priv->orientation[1] = 0.f;
  obj->priv->orientation[2] = 0.f;

  g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_AXES_USED,
                          G_CALLBACK(onEntryUsed), (gpointer)obj, G_CONNECT_SWAPPED);
  g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_AXES_COLOR,
                          G_CALLBACK(onEntryColor), (gpointer)obj, G_CONNECT_SWAPPED);
  g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_AXES_LINE,
                          G_CALLBACK(onEntryWidth), (gpointer)obj, G_CONNECT_SWAPPED);
  g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_AXES_STIPPLE,
                          G_CALLBACK(onEntryStipple), (gpointer)obj, G_CONNECT_SWAPPED);
  g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_AXES_POSITION,
                          G_CALLBACK(onEntryPosition), (gpointer)obj, G_CONNECT_SWAPPED);
  g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_AXES_LABEL_X,
                          G_CALLBACK(onEntryLabel), (gpointer)obj, G_CONNECT_SWAPPED);
  g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_AXES_LABEL_Y,
                          G_CALLBACK(onEntryLabel), (gpointer)obj, G_CONNECT_SWAPPED);
  g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_AXES_LABEL_Z,
                          G_CALLBACK(onEntryLabel), (gpointer)obj, G_CONNECT_SWAPPED);

  g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_AXES_FACTOR,
                          G_CALLBACK(onEntryFactor), (gpointer)obj, G_CONNECT_SWAPPED);

  if (!defaultAxes)
    defaultAxes = obj;
}

/* This method can be called several times.
   It should unref all of its reference to
   GObjects. */
static void visu_gl_ext_axes_dispose(GObject* obj)
{
  VisuGlExtAxes *axes;

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

  axes = VISU_GL_EXT_AXES(obj);
  if (axes->priv->dispose_has_run)
    return;
  axes->priv->dispose_has_run = TRUE;

  /* Disconnect signals. */
  visu_gl_ext_axes_setGlView(VISU_GL_EXT(axes), (VisuGlView*)0);
  _setBox(axes, (VisuBox*)0);

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

  g_return_if_fail(obj);

  DBG_fprintf(stderr, "Extension Axes: finalize object %p.\n", (gpointer)obj);

  axes = VISU_GL_EXT_AXES(obj);

  /* Free privs elements. */
  if (axes->priv)
    {
      DBG_fprintf(stderr, "Extension Axes: free private axes.\n");
      g_free(axes->priv->lbl[0]);
      g_free(axes->priv->lbl[1]);
      g_free(axes->priv->lbl[2]);
    }
  /* The free is called by g_type_free_instance... */
/*   g_free(axes); */

  /* Chain up to the parent class */
  DBG_fprintf(stderr, "Extension Axes: chain to parent.\n");
  G_OBJECT_CLASS(visu_gl_ext_axes_parent_class)->finalize(obj);
  DBG_fprintf(stderr, "Extension Axes: freeing ... OK.\n");
}
static void visu_gl_ext_axes_get_property(GObject* obj, guint property_id,
                                          GValue *value, GParamSpec *pspec)
{
  VisuGlExtAxes *self = VISU_GL_EXT_AXES(obj);

  DBG_fprintf(stderr, "Extension Axes: get property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case SIZE_PROP:
      g_value_set_float(value, self->priv->lgFact);
      DBG_fprintf(stderr, "%g.\n", self->priv->lgFact);
      break;
    case XPOS_PROP:
      g_value_set_float(value, self->priv->xpos);
      DBG_fprintf(stderr, "%g.\n", self->priv->xpos);
      break;
    case YPOS_PROP:
      g_value_set_float(value, self->priv->ypos);
      DBG_fprintf(stderr, "%g.\n", self->priv->ypos);
      break;
    case COLOR_PROP:
      g_value_take_boxed(value, tool_color_new(self->priv->rgb));
      DBG_fprintf(stderr, "%gx%gx%g.\n", self->priv->rgb[0], self->priv->rgb[1], self->priv->rgb[2]);
      break;
    case WIDTH_PROP:
      g_value_set_float(value, self->priv->lineWidth);
      DBG_fprintf(stderr, "%g.\n", self->priv->lineWidth);
      break;
    case STIPPLE_PROP:
      g_value_set_uint(value, (guint)self->priv->lineStipple);
      DBG_fprintf(stderr, "%d.\n", (guint)self->priv->lineStipple);
      break;
    case VIEW_PROP:
      g_value_set_object(value, self->priv->view);
      DBG_fprintf(stderr, "%p.\n", (gpointer)self->priv->view);
      break;
    case BOX_PROP:
      g_value_set_object(value, self->priv->box);
      DBG_fprintf(stderr, "%p.\n", g_value_get_object(value));
      break;
    case LBLX_PROP:
      g_value_set_static_string(value, self->priv->lbl[0]);
      DBG_fprintf(stderr, "'%s'.\n", self->priv->lbl[0]);
      break;
    case LBLY_PROP:
      g_value_set_static_string(value, self->priv->lbl[1]);
      DBG_fprintf(stderr, "'%s'.\n", self->priv->lbl[1]);
      break;
    case LBLZ_PROP:
      g_value_set_static_string(value, self->priv->lbl[2]);
      DBG_fprintf(stderr, "'%s'.\n", self->priv->lbl[2]);
      break;
    case USE_ORIENTATION_PROP:
      g_value_set_boolean(value, self->priv->displayOrientation);
      DBG_fprintf(stderr, "%d.\n", self->priv->displayOrientation);
      break;
    case CONE_THETA_PROP:
      g_value_set_float(value, self->priv->orientation[0]);
      DBG_fprintf(stderr, "%g.\n", self->priv->orientation[0]);
      break;
    case CONE_PHI_PROP:
      g_value_set_float(value, self->priv->orientation[1]);
      DBG_fprintf(stderr, "%g.\n", self->priv->orientation[1]);
      break;
    case CONE_OMEGA_PROP:
      g_value_set_float(value, self->priv->orientation[2]);
      DBG_fprintf(stderr, "%g.\n", self->priv->orientation[2]);
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}
static void visu_gl_ext_axes_set_property(GObject* obj, guint property_id,
                                          const GValue *value, GParamSpec *pspec)
{
  ToolColor *color;
  VisuGlExtAxes *self = VISU_GL_EXT_AXES(obj);
  float orientation[3];

  DBG_fprintf(stderr, "Extension Axes: set property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case SIZE_PROP:
      visu_gl_ext_axes_setLengthFactor(self, g_value_get_float(value));
      DBG_fprintf(stderr, "%g.\n", self->priv->lgFact);
      break;
    case XPOS_PROP:
      visu_gl_ext_axes_setPosition(self, g_value_get_float(value), self->priv->ypos);
      DBG_fprintf(stderr, "%g.\n", self->priv->xpos);
      break;
    case YPOS_PROP:
      visu_gl_ext_axes_setPosition(self, self->priv->xpos, g_value_get_float(value));
      DBG_fprintf(stderr, "%g.\n", self->priv->ypos);
      break;
    case COLOR_PROP:
      color = (ToolColor*)g_value_get_boxed(value);
      _setRGB((VisuGlExtLined*)self, color->rgba, TOOL_COLOR_MASK_RGBA);
      DBG_fprintf(stderr, "%gx%gx%g.\n", self->priv->rgb[0], self->priv->rgb[1], self->priv->rgb[2]);
      break;
    case WIDTH_PROP:
      _setLineWidth((VisuGlExtLined*)self, g_value_get_float(value));
      DBG_fprintf(stderr, "%g.\n", self->priv->lineWidth);
      break;
    case STIPPLE_PROP:
      _setLineStipple((VisuGlExtLined*)self, (guint16)g_value_get_uint(value));
      DBG_fprintf(stderr, "%d.\n", (guint)self->priv->lineStipple);
      break;
    case VIEW_PROP:
      visu_gl_ext_axes_setGlView(VISU_GL_EXT(self),
                                 VISU_GL_VIEW(g_value_get_object(value)));
      DBG_fprintf(stderr, "%p.\n", (gpointer)self->priv->view);
      break;
    case BOX_PROP:
      DBG_fprintf(stderr, "%p.\n", (gpointer)g_value_get_object(value));
      visu_gl_ext_axes_setBasisFromBox(self, VISU_BOX(g_value_get_object(value)));
      break;
    case LBLX_PROP:
      visu_gl_ext_axes_setLabel(self, g_value_get_string(value), TOOL_XYZ_X);
      DBG_fprintf(stderr, "'%s'.\n", self->priv->lbl[0]);
      break;
    case LBLY_PROP:
      visu_gl_ext_axes_setLabel(self, g_value_get_string(value), TOOL_XYZ_Y);
      DBG_fprintf(stderr, "'%s'.\n", self->priv->lbl[1]);
      break;
    case LBLZ_PROP:
      visu_gl_ext_axes_setLabel(self, g_value_get_string(value), TOOL_XYZ_Z);
      DBG_fprintf(stderr, "'%s'.\n", self->priv->lbl[2]);
      break;
    case USE_ORIENTATION_PROP:
      visu_gl_ext_axes_useOrientation(self, g_value_get_boolean(value));
      DBG_fprintf(stderr, "%d.\n", self->priv->displayOrientation);
      break;
    case CONE_THETA_PROP:
      orientation[0] = g_value_get_float(value);
      visu_gl_ext_axes_setOrientationTop(self, orientation, VISU_GL_CAMERA_THETA);
      DBG_fprintf(stderr, "%g.\n", self->priv->orientation[0]);
      break;
    case CONE_PHI_PROP:
      orientation[1] = g_value_get_float(value);
      visu_gl_ext_axes_setOrientationTop(self, orientation, VISU_GL_CAMERA_PHI);
      DBG_fprintf(stderr, "%g.\n", self->priv->orientation[1]);
      break;
    case CONE_OMEGA_PROP:
      DBG_fprintf(stderr, "%g.\n", g_value_get_float(value));
      orientation[2] = g_value_get_float(value);
      visu_gl_ext_axes_setOrientationTop(self, orientation, VISU_GL_CAMERA_OMEGA);
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}
static void _setBox(VisuGlExtAxes *axes, VisuBox *box)
{
  if (axes->priv->box == box)
    return;
  
  if (axes->priv->box)
    {
      g_signal_handler_disconnect(G_OBJECT(axes->priv->box), axes->priv->box_signal);
      g_object_unref(axes->priv->box);
    }
  if (box)
    {
      g_object_ref(box);
      axes->priv->box_signal =
        g_signal_connect(G_OBJECT(box), "SizeChanged",
                         G_CALLBACK(onBoxChange), (gpointer)axes);
    }
  else
    axes->priv->box_signal = 0;
  axes->priv->box = box;
  g_object_notify_by_pspec(G_OBJECT(axes), properties[BOX_PROP]);
}

/**
 * visu_gl_ext_axes_new:
 * @name: (allow-none): the name to give to the extension (default is #VISU_GL_EXT_AXES_ID).
 *
 * Creates a new #VisuGlExt to draw axes.
 *
 * Since: 3.7
 *
 * Returns: a pointer to the #VisuGlExt it created or
 * NULL otherwise.
 */
VisuGlExtAxes* visu_gl_ext_axes_new(const gchar *name)
{
  char *name_ = VISU_GL_EXT_AXES_ID;
  char *description = _("Draw {x,y,z} axes.");
  VisuGlExt *extensionAxes;

  DBG_fprintf(stderr,"Extension Axes: new object.\n");
  
  extensionAxes = VISU_GL_EXT(g_object_new(VISU_TYPE_GL_EXT_AXES,
                                              "name", (name)?name:name_, "label", _(name),
                                              "description", description, "nGlObj", 1,
                                              "priority", VISU_GL_EXT_PRIORITY_LAST,
                                              "saveState", TRUE, NULL));

  return VISU_GL_EXT_AXES(extensionAxes);
}

static gboolean _setRGB(VisuGlExtLined *axes, float rgb[4], int mask)
{
  VisuGlExtAxesPrivate *self;
  g_return_val_if_fail(VISU_IS_GL_EXT_AXES(axes), FALSE);

  self = VISU_GL_EXT_AXES(axes)->priv;
  
  if (mask & TOOL_COLOR_MASK_R)
    self->rgb[0] = rgb[0];
  if (mask & TOOL_COLOR_MASK_G)
    self->rgb[1] = rgb[1];
  if (mask & TOOL_COLOR_MASK_B)
    self->rgb[2] = rgb[2];

  visu_gl_ext_setDirty(VISU_GL_EXT(axes), TRUE);
  return TRUE;
}
static gboolean _setLineWidth(VisuGlExtLined *axes, float width)
{
  VisuGlExtAxesPrivate *self;
  g_return_val_if_fail(VISU_IS_GL_EXT_AXES(axes), FALSE);
  self = VISU_GL_EXT_AXES(axes)->priv;
  
  self->lineWidth = width;
  visu_gl_ext_setDirty(VISU_GL_EXT(axes), TRUE);
  return TRUE;
}
static gboolean _setLineStipple(VisuGlExtLined *axes, guint16 stipple)
{
  VisuGlExtAxesPrivate *self;
  g_return_val_if_fail(VISU_IS_GL_EXT_AXES(axes), FALSE);
  self = VISU_GL_EXT_AXES(axes)->priv;

  self->lineStipple = stipple;
  visu_gl_ext_setDirty(VISU_GL_EXT(axes), TRUE);
  return TRUE;
}
static void _setBasis(VisuGlExtAxes *axes, double matrix[3][3])
{
  double sum2;

  DBG_fprintf(stderr, "Extension Axes: %8g %8g %8g\n"
              "                %8g %8g %8g\n"
	      "                %8g %8g %8g\n",
	      matrix[0][0], matrix[0][1], matrix[0][2],
	      matrix[1][0], matrix[1][1], matrix[1][2],
	      matrix[2][0], matrix[2][1], matrix[2][2]);
  /* We normalise it and copy it. */
  sum2 = 1. / sqrt(matrix[0][0] * matrix[0][0] +
                   matrix[1][0] * matrix[1][0] +
                   matrix[2][0] * matrix[2][0]);
  axes->priv->matrix[0][0] = matrix[0][0] * sum2;
  axes->priv->matrix[0][1] = matrix[1][0] * sum2;
  axes->priv->matrix[0][2] = matrix[2][0] * sum2;
  sum2 = 1. / sqrt(matrix[0][1] * matrix[0][1] +
                   matrix[1][1] * matrix[1][1] +
                   matrix[2][1] * matrix[2][1]);
  axes->priv->matrix[1][0] = matrix[0][1] * sum2;
  axes->priv->matrix[1][1] = matrix[1][1] * sum2;
  axes->priv->matrix[1][2] = matrix[2][1] * sum2;
  sum2 = 1. / sqrt(matrix[0][2] * matrix[0][2] +
                   matrix[1][2] * matrix[1][2] +
                   matrix[2][2] * matrix[2][2]);
  axes->priv->matrix[2][0] = matrix[0][2] * sum2;
  axes->priv->matrix[2][1] = matrix[1][2] * sum2;
  axes->priv->matrix[2][2] = matrix[2][2] * sum2;

  visu_gl_ext_setDirty(VISU_GL_EXT(axes), TRUE);
}
/**
 * visu_gl_ext_axes_setBasis:
 * @axes: the #VisuGlExtAxes object to modify.
 * @matrix: the definition of the three basis axis.
 *
 * The @axes can represent an arbitrary basis-set, provided by
 * @matrix. @matrix[{0,1,2}] represents the {x,y,z} axis vector in a
 * cartesian basis-set. See visu_gl_ext_axes_setBasisFromBox() if the
 * basis-set should follow the one of a given #VisuBox.
 *
 * Since: 3.7
 *
 * Returns: TRUE if the basis is actually changed.
 **/
gboolean visu_gl_ext_axes_setBasis(VisuGlExtAxes *axes, double matrix[3][3])
{
  g_return_val_if_fail(VISU_IS_GL_EXT_AXES(axes), FALSE);

  _setBox(axes, (VisuBox*)0);
  _setBasis(axes, matrix);

  return visu_gl_ext_getActive(VISU_GL_EXT(axes));
}
/**
 * visu_gl_ext_axes_setBasisFromBox:
 * @axes: the #VisuGlExtAxes object to modify.
 * @box: (allow-none): the #VisuBox to use as basis-set.
 *
 * The @axes can follow the basis-set defined by @box. If NULL is
 * passed, then the orthorombic default basis-set is used.
 *
 * Since: 3.7
 *
 * Returns: TRUE if the basis is actually changed.
 **/
gboolean visu_gl_ext_axes_setBasisFromBox(VisuGlExtAxes *axes, VisuBox *box)
{
  double m[3][3];

  g_return_val_if_fail(VISU_IS_GL_EXT_AXES(axes), FALSE);

  if (box)
    visu_box_getCellMatrix(box, m);
  else
    {
      memset(m, '\0', sizeof(double) * 9);
      m[0][0] = 1.;
      m[1][1] = 1.;
      m[2][2] = 1.;
    }
  _setBox(axes, box);
  _setBasis(axes, m);

  return visu_gl_ext_getActive(VISU_GL_EXT(axes));
}
/**
 * visu_gl_ext_axes_setLabel:
 * @axes: a #VisuGlExtAxes object.
 * @lbl: a string.
 * @dir: an axis direction.
 *
 * Set the label @lbl for the given axis @dir.
 *
 * Since: 3.8
 *
 * Returns: TRUE if the label is modified.
 **/
gboolean visu_gl_ext_axes_setLabel(VisuGlExtAxes *axes, const gchar *lbl, ToolXyzDir dir)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_AXES(axes) && lbl, FALSE);
  
  if (!strcmp(axes->priv->lbl[dir], lbl))
    return FALSE;

  g_free(axes->priv->lbl[dir]);
  axes->priv->lbl[dir] = g_strdup(lbl);
  g_object_notify_by_pspec(G_OBJECT(axes), properties[LBLX_PROP + dir]);
  visu_gl_ext_setDirty(VISU_GL_EXT(axes), TRUE);
  return TRUE;
}
/**
 * visu_gl_ext_axes_setPosition:
 * @axes: the #VisuGlExtAxes object to modify.
 * @xpos: the reduced x position (1 to the right).
 * @ypos: the reduced y position (1 to the bottom).
 *
 * Change the position of the axes representation.
 *
 * Since: 3.7
 *
 * Returns: TRUE if the position is actually changed.
 **/
gboolean visu_gl_ext_axes_setPosition(VisuGlExtAxes *axes, float xpos, float ypos)
{
  gboolean changed;

  g_return_val_if_fail(VISU_IS_GL_EXT_AXES(axes), FALSE);

  xpos = CLAMP(xpos, 0.f, 1.f);
  ypos = CLAMP(ypos, 0.f, 1.f);
  changed = FALSE;

  g_object_freeze_notify(G_OBJECT(axes));
  if (xpos != axes->priv->xpos)
    {
      axes->priv->xpos = xpos;
      g_object_notify_by_pspec(G_OBJECT(axes), properties[XPOS_PROP]);
      changed = TRUE;
    }
  if (ypos != axes->priv->ypos)
    {
      axes->priv->ypos = ypos;
      g_object_notify_by_pspec(G_OBJECT(axes), properties[YPOS_PROP]);
      changed = TRUE;
    }
  if (changed)
    visu_gl_ext_setDirty(VISU_GL_EXT(axes), TRUE);
  g_object_thaw_notify(G_OBJECT(axes));

  return changed;
}
/**
 * visu_gl_ext_axes_useOrientation:
 * @axes: a #VisuGlExtAxes object.
 * @use: a boolean.
 *
 * If TRUE, @axes also draws a coloured cone to display orientation information.
 *
 * Since: 3.8
 *
 * Returns: TRUE if value is actually changed.
 **/
gboolean visu_gl_ext_axes_useOrientation(VisuGlExtAxes *axes, gboolean use)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_AXES(axes), FALSE);
  
  if (axes->priv->displayOrientation == use)
    return FALSE;

  axes->priv->displayOrientation = use;
  g_object_notify_by_pspec(G_OBJECT(axes), properties[USE_ORIENTATION_PROP]);
  visu_gl_ext_setDirty(VISU_GL_EXT(axes), TRUE);
  return TRUE;
}
/**
 * visu_gl_ext_axes_setOrientationTop:
 * @axes: a #VisuGlExtAxes object.
 * @top: (array fixed-size=3): a camera orientation.
 * @dir: flags used to specify which angles to set.
 *
 * Define the camera orientation of the top orientation when @axes are
 * used to display orientation information.
 *
 * Since: 3.8
 *
 * Returns: TRUE if value is actually changed.
 **/
gboolean visu_gl_ext_axes_setOrientationTop(VisuGlExtAxes *axes,
                                            const gfloat top[3], int dir)
{
  gboolean changed;

  g_return_val_if_fail(VISU_IS_GL_EXT_AXES(axes), FALSE);

  changed = FALSE;

  g_object_freeze_notify(G_OBJECT(axes));
  if ((dir & VISU_GL_CAMERA_THETA) &&
      (CLAMP(top[0], 0.f, 180.) != axes->priv->orientation[0]))
    {
      axes->priv->orientation[0] = CLAMP(top[0], 0.f, 180.);
      g_object_notify_by_pspec(G_OBJECT(axes), properties[CONE_THETA_PROP]);
      changed = TRUE;
    }
  if ((dir & VISU_GL_CAMERA_PHI) &&
      (CLAMP(top[1], 0.f, 360.) != axes->priv->orientation[1]))
    {
      axes->priv->orientation[1] = CLAMP(top[1], 0.f, 360.);
      g_object_notify_by_pspec(G_OBJECT(axes), properties[CONE_PHI_PROP]);
      changed = TRUE;
    }
  if ((dir & VISU_GL_CAMERA_OMEGA) &&
      (CLAMP(top[2], 0.f, 360.) != axes->priv->orientation[2]))
    {
      axes->priv->orientation[2] = CLAMP(top[2], 0.f, 360.);
      g_object_notify_by_pspec(G_OBJECT(axes), properties[CONE_OMEGA_PROP]);
      changed = TRUE;
    }
  if (changed && axes->priv->displayOrientation)
    visu_gl_ext_setDirty(VISU_GL_EXT(axes), TRUE);
  g_object_thaw_notify(G_OBJECT(axes));

  return changed;
}
static gboolean visu_gl_ext_axes_setGlView(VisuGlExt *axes, VisuGlView *view)
{
  VisuGlExtAxesPrivate *priv = ((VisuGlExtAxes*)axes)->priv;

  /* No change to be done. */
  if (view == priv->view)
    return FALSE;

  if (priv->view)
    {
      g_signal_handler_disconnect(G_OBJECT(priv->view), priv->persp_signal);
      g_signal_handler_disconnect(G_OBJECT(priv->view), priv->refLength_signal);
      g_signal_handler_disconnect(G_OBJECT(priv->view), priv->widthHeight_signal);
      g_object_unref(priv->view);
      priv->persp_signal     = 0;
      priv->refLength_signal   = 0;
      priv->widthHeight_signal = 0;
      priv->color_signal       = 0;
    }
  if (view)
    {
      g_object_ref(view);
      priv->persp_signal =
        g_signal_connect_swapped(G_OBJECT(view), "notify::perspective",
                                 G_CALLBACK(onAxesParametersChange), (gpointer)axes);
      priv->refLength_signal =
        g_signal_connect_swapped(G_OBJECT(view), "RefLengthChanged",
                                 G_CALLBACK(onAxesParametersChange), (gpointer)axes);
      priv->widthHeight_signal =
        g_signal_connect_swapped(G_OBJECT(view), "WidthHeightChanged",
                                 G_CALLBACK(onAxesParametersChange), (gpointer)axes);
    }
  priv->view = view;
  g_object_notify_by_pspec(G_OBJECT(axes), properties[VIEW_PROP]);

  return TRUE;
}

/* Get methods. */
static float* _getRGB(const VisuGlExtLined *axes)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_AXES(axes), rgbDefault);
  
  return ((VisuGlExtAxes*)axes)->priv->rgb;
}
static float _getLineWidth(const VisuGlExtLined *axes)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_AXES(axes), LINE_WIDTH_DEFAULT);
  
  return ((VisuGlExtAxes*)axes)->priv->lineWidth;
}
static guint16 _getLineStipple(const VisuGlExtLined *axes)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_AXES(axes), LINE_STIPPLE_DEFAULT);
  
  return ((VisuGlExtAxes*)axes)->priv->lineStipple;
}
/**
 * visu_gl_ext_axes_getPosition:
 * @axes: the #VisuGlExtAxes object to inquire.
 * @xpos: (out) (allow-none): a location to store the x position.
 * @ypos: (out) (allow-none): a location to store the y position.
 *
 * Inquire the position of the representation of tha axes.
 *
 * Since: 3.7
 **/
void visu_gl_ext_axes_getPosition(VisuGlExtAxes *axes, float *xpos, float *ypos)
{
  g_return_if_fail(VISU_IS_GL_EXT_AXES(axes));

  if (xpos)
    *xpos = axes->priv->xpos;
  if (ypos)
    *ypos = axes->priv->ypos;
}
/**
 * visu_gl_ext_axes_setLengthFactor:
 * @axes: a #VisuGlExtAxes object.
 * @factor: a floating point value between 0. and 10.
 *
 * Change the scaling factor to draw the axis.
 *
 * Since: 3.8
 *
 * Returns: TRUE if value is indeed changed.
 **/
gboolean visu_gl_ext_axes_setLengthFactor(VisuGlExtAxes *axes, float factor)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_AXES(axes), FALSE);

  if (axes->priv->lgFact == factor)
    return FALSE;
  axes->priv->lgFact = factor;

  g_object_notify_by_pspec(G_OBJECT(axes), properties[SIZE_PROP]);
  visu_gl_ext_setDirty(VISU_GL_EXT(axes), TRUE);

  return TRUE;
}
/**
 * visu_gl_ext_axes_getLengthFactor:
 * @axes: a #VisuGlExtAxes object.
 *
 * Retrieve the scaling factor used to draw axis.
 *
 * Since: 3.8
 *
 * Returns: the scaling factor.
 **/
float visu_gl_ext_axes_getLengthFactor(VisuGlExtAxes *axes)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_AXES(axes), 1.f);

  return axes->priv->lgFact;
}

/****************/
/* Private part */
/****************/
static void visu_gl_ext_axes_rebuild(VisuGlExt *ext)
{
  visu_gl_text_rebuildFontList();
  visu_gl_ext_axes_draw(ext);
}
static void onAxesParametersChange(VisuGlExtAxes *axes)
{
  DBG_fprintf(stderr, "Extension Axes: caught change on view.\n");
  visu_gl_ext_setDirty(VISU_GL_EXT(axes), TRUE);
}
static void onBoxChange(VisuBox *box, float extens _U_, gpointer data)
{
  VisuGlExtAxes *axes = VISU_GL_EXT_AXES(data);
  double m[3][3];

  /* We copy the definition of the box. */
  visu_box_getCellMatrix(box, m);
  _setBasis(axes, m);
}
static void onEntryUsed(VisuGlExtAxes *axes, VisuConfigFileEntry *entry _U_, VisuConfigFile *obj _U_)
{
  visu_gl_ext_setActive(VISU_GL_EXT(axes), RESOURCE_AXES_USED_DEFAULT);
}
static void onEntryColor(VisuGlExtAxes *axes, VisuConfigFileEntry *entry _U_, VisuConfigFile *obj _U_)
{
  visu_gl_ext_lined_setRGBA(VISU_GL_EXT_LINED(axes), rgbDefault, TOOL_COLOR_MASK_RGBA);
}
static void onEntryWidth(VisuGlExtAxes *axes, VisuConfigFileEntry *entry _U_, VisuConfigFile *obj _U_)
{
  visu_gl_ext_lined_setWidth(VISU_GL_EXT_LINED(axes), LINE_WIDTH_DEFAULT);
}
static void onEntryStipple(VisuGlExtAxes *axes, VisuConfigFileEntry *entry _U_, VisuConfigFile *obj _U_)
{
  visu_gl_ext_lined_setStipple(VISU_GL_EXT_LINED(axes), LINE_STIPPLE_DEFAULT);
}
static void onEntryPosition(VisuGlExtAxes *axes, VisuConfigFileEntry *entry _U_, VisuConfigFile *obj _U_)
{
  visu_gl_ext_axes_setPosition(axes, POSITION_DEFAULT[0], POSITION_DEFAULT[1]);
}
static void onEntryLabel(VisuGlExtAxes *axes, VisuConfigFileEntry *entry, VisuConfigFile *obj _U_)
{
  if (!strcmp(visu_config_file_entry_getKey(entry), FLAG_RESOURCE_AXES_LABEL_X))
    g_object_set(G_OBJECT(axes), "x-label", LABEL_DEFAULT[TOOL_XYZ_X], NULL);
  else if (!strcmp(visu_config_file_entry_getKey(entry), FLAG_RESOURCE_AXES_LABEL_Y))
    g_object_set(G_OBJECT(axes), "y-label", LABEL_DEFAULT[TOOL_XYZ_Y], NULL);
  else if (!strcmp(visu_config_file_entry_getKey(entry), FLAG_RESOURCE_AXES_LABEL_Z))
    g_object_set(G_OBJECT(axes), "z-label", LABEL_DEFAULT[TOOL_XYZ_Z], NULL);
}
static void onEntryFactor(VisuGlExtAxes *axes, VisuConfigFileEntry *entry _U_, VisuConfigFile *obj _U_)
{
  visu_gl_ext_axes_setLengthFactor(axes, SIZE_DEFAULT);
}

static void drawAxes(float length, double matrix[3][3], GLsizei w, GLsizei h, float width,
		     float rgb[3], const char *legend, gboolean long_axes, gchar *lbl[3]) 
{
  double orig[3][3];

  if (long_axes)
    {
      orig[0][0] = -matrix[0][0];
      orig[0][1] = -matrix[0][1];
      orig[0][2] = -matrix[0][2];
      orig[1][0] = -matrix[1][0];
      orig[1][1] = -matrix[1][1];
      orig[1][2] = -matrix[1][2];
      orig[2][0] = -matrix[2][0];
      orig[2][1] = -matrix[2][1];
      orig[2][2] = -matrix[2][2];
    }
  else
    memset(orig, '\0',  sizeof(double) * 9);

  glLineWidth(width);
  glColor3fv(rgb);
  glPushMatrix();
  glScalef(length, length, length);
  glBegin(GL_LINES);
  glVertex3dv(orig[0]); glVertex3dv(matrix[0]);
  glVertex3dv(orig[1]); glVertex3dv(matrix[1]);
  glVertex3dv(orig[2]); glVertex3dv(matrix[2]);
  glEnd();

  glRasterPos3dv(matrix[0]); visu_gl_text_drawChars(lbl[0], VISU_GL_TEXT_NORMAL); 
  glRasterPos3dv(matrix[1]); visu_gl_text_drawChars(lbl[1], VISU_GL_TEXT_NORMAL); 
  glRasterPos3dv(matrix[2]); visu_gl_text_drawChars(lbl[2], VISU_GL_TEXT_NORMAL);

  glPopMatrix();

  if(legend != NULL)
    {
      glMatrixMode (GL_PROJECTION);
      glPushMatrix();
      glLoadIdentity ();  
      gluOrtho2D (0, MIN(w,h), 0, MIN(h,w));
      glMatrixMode (GL_MODELVIEW);
      glPushMatrix();
      glLoadIdentity ();  
      glRasterPos3f(20., 5., 0.9); visu_gl_text_drawChars(legend, VISU_GL_TEXT_NORMAL);
      glPopMatrix();
      glMatrixMode(GL_PROJECTION);
      glPopMatrix();
      glMatrixMode(GL_MODELVIEW);
    }
}

/**
 * draw_coloured_cone:
 * @r: the radius of the cone
 * @h: the semi-height of the cone
 * @n: the precision used to draw the sphere
 *
 * Draws a coloured double cone at the given position.
 */ 
static void draw_coloured_cone(double r, double h, int n, float phi_prime_zero)
{
  float hsv[3], rgb[3];
  int i,j;
  double theta1,theta2,theta3;
  float e_x, e_y, e_z, p_x, p_y, p_z;

  g_return_if_fail(r >= 0 && n >= 0);
  
  if (n < 4 || r <= 0) 
    {
      glBegin(GL_POINTS);
      glVertex3f(0,0,0);
      glEnd();
      return;
    }

  glFrontFace(GL_CW);

  glPushMatrix();
  glRotatef(phi_prime_zero, 0, 0, 1);

  glRotatef(-90, 1, 0, 0);

  hsv[1] = 0;
  hsv[2] = 1;

  for (j=0;j<n/2;j++) 
    {
      theta1 = j * 2 * G_PI / n - G_PI_2;
      theta2 = (j + 1) * 2 * G_PI / n - G_PI_2;
      
      glBegin(GL_QUAD_STRIP);
      for (i=0;i<=n;i++) 
	{
	  theta3 = i * 2 * G_PI / n;
	  
	  hsv[0] = /*1-*/(float)i/(float)n;

	  hsv[1] = 2*(float)(1+j)/(float)(n/2);
	  if(hsv[1] > 1) hsv[1] = 1;
      
	  hsv[2] = 2-2*(float)(1+j)/(float)(n/2); 
	  if(hsv[2] > 1) hsv[2] = 1; 

	  e_x = /*cos(theta2) **/hsv[1]*hsv[2]* cos(theta3);
	  e_y = sin(theta2);
	  e_z = /*cos(theta2) **/hsv[1]*hsv[2]* sin(theta3);
	  p_x = r * e_x;
	  p_y = /*r * e_y*/h*(hsv[1] - hsv[2]);
	  p_z = r * e_z;
	  
	  tool_color_convertHSVtoRGB(rgb, hsv);
	  glColor3f(rgb[0], rgb[1], rgb[2]); 
	  glNormal3f(e_x,e_y,e_z);
	  glVertex3f(p_x,p_y,p_z);
	  
	  hsv[0] = /*1-*/(float)i/(float)n;

	  hsv[1] = 2*(float)j/(float)(n/2);
	  if(hsv[1] > 1) hsv[1] = 1;
      
	  hsv[2] = 2-2*(float)j/(float)(n/2); 
	  if(hsv[2] > 1) hsv[2] = 1; 

	  e_x = /*cos(theta1) **/hsv[1]*hsv[2]* cos(theta3);
       	  e_y = sin(theta1);
	  e_z = /*cos(theta1) **/hsv[1]*hsv[2]* sin(theta3);
	  p_x = r * e_x;
	  p_y = /*r * e_y*/h*(hsv[1] - hsv[2]);
	  p_z = r * e_z;
	
	  tool_color_convertHSVtoRGB(rgb, hsv);
	  glColor3f(rgb[0], rgb[1], rgb[2]); 
	  glNormal3f(e_x,e_y,e_z);
	  glVertex3f(p_x,p_y,p_z);

	}
      glEnd();
    }
  glPopMatrix();
  glFrontFace(GL_CCW);
  
}

/* void drawConeCircle(/\*XYZ c,*\/ double r, int n)  */
/* { */
/*   /\*  XYZ e,p;*\/ */
/*   GLboolean antialiasing_was_on; */
/*   GLUquadric* trash = gluNewQuadric(); */
/*   int i; */
/*   double theta, height = 1; */

/* /\*   glEnable (GL_POLYGON_SMOOTH); *\/ */
/* /\*   glDisable (GL_DEPTH_TEST); *\/ */

/*   antialiasing_was_on = enableGlFeature(GL_LINE_SMOOTH); */
/*   glPushMatrix(); */
/*   glRotatef(90, 1, 0, 0); */
/*   glTranslatef(0, 0, -height/2); */
/*   glLineWidth(lineWidth); */
/*   glColor3f(0, 0, 0);  */
/*   gluQuadricOrientation(trash, GLU_INSIDE); */
/*   gluCylinder(trash, 0.95*r, 0.95*r, height, n, 1); */
/* /\*   glBegin(GL_LINE_LOOP); *\/ */
/* /\*   for (i=0;i<=n;i++)  *\/ */
/* /\*     { *\/ */
/* /\*       theta = i * TWOPI / n; *\/ */
/* /\*       e.x = cos(theta); *\/ */
/* /\*       e.z = sin(theta); *\/ */
/* /\*       p.x = 0.8*(c.x +  r * e.x); *\/ */
/* /\*       p.z = 0.8*(c.z +  r * e.z); *\/ */
/* /\*       glVertex3f(p.x, -0.6, p.z); *\/ */
/* /\*       glVertex3f(p.x, 0.6, p.z); *\/ */
/* /\*     } *\/ */
/* /\*   glEnd(); *\/ */
/*   glPopMatrix(); */
/*   restoreGlFeature(GL_LINE_SMOOTH, antialiasing_was_on); */
/* /\*   glEnable (GL_DEPTH_TEST); *\/ */
/* /\*   glDisable(GL_POLYGON_SMOOTH); *\/ */

/* } */

/**
 * visu_gl_ext_axes_draw:
 * @axes: the #VisuBox object to build axes for.
 *
 * This method creates a compiled list that draws axes.
 */
static void visu_gl_ext_axes_draw(VisuGlExt *ext)
{
  float length;
  GLsizei w, h;
  GLint xx, yy;
  double mini, maxi, near, far;
  float length0;
  VisuGlExtAxes *axes;

  g_return_if_fail(VISU_IS_GL_EXT_AXES(ext));
  axes = VISU_GL_EXT_AXES(ext);

  DBG_fprintf(stderr, "Extension Axes: call to axes draw (%p).\n",
              (gpointer)axes->priv->view);
  /* Nothing to draw; */
  if (!axes->priv->view)
    return;

  DBG_fprintf(stderr, "Extension axes: creating axes in (%dx%d).\n",
	      axes->priv->view->window.width, axes->priv->view->window.height);

  length0 = visu_gl_camera_getRefLength(&axes->priv->view->camera, (ToolUnits*)0);
  DBG_fprintf(stderr, " | refLength = %g\n", length0);
  DBG_fprintf(stderr, " | window = %dx%d\n",
              axes->priv->view->window.width, axes->priv->view->window.height);
  w = axes->priv->lgFact * MIN(axes->priv->view->window.width,
                               axes->priv->view->window.height);
  h = w;
  xx = (axes->priv->view->window.width - w) * axes->priv->xpos;
  yy = (axes->priv->view->window.height - h) * (1.f - axes->priv->ypos);
  mini = -0.5f * length0 *
    (axes->priv->view->camera.d_red - 1.f) / axes->priv->view->camera.d_red;
  maxi = -mini;
  near = far = axes->priv->view->camera.d_red * length0;
  near -= length0;
  far  += length0;
  DBG_fprintf(stderr, " | near/far = %g %g\n", near, far);

  visu_gl_text_initFontList();
  
  glDeleteLists(visu_gl_ext_getGlList(ext), 1);
  visu_gl_ext_startDrawing(ext);

  /* Désactivation de la lumière et du brouillard et activation du culling. */
  glEnable(GL_CULL_FACE);
  glDisable(GL_LIGHTING);
  glDisable(GL_FOG);

  if (axes->priv->lineStipple != 65535)
    {
      glEnable(GL_LINE_STIPPLE);
      glLineStipple(1, axes->priv->lineStipple);
    }

  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();
  DBG_fprintf(stderr, "Extension axes: frustum is %fx%f %fx%f %fx%f.\n",
	      mini, maxi, mini, maxi, near, far);
  glFrustum(mini, maxi, mini, maxi, near, far);
  glMatrixMode(GL_MODELVIEW);
  glPopMatrix();

  DBG_fprintf(stderr, "Extension Axes: new view port at %dx%d, size %dx%d.\n",
              xx, yy, w, h);
  glViewport(xx, yy, w, h);

  length = 0.33 * length0;
  if (axes->priv->displayOrientation)
    {
      /* Resetting the depth buffer. */
      glClear(GL_DEPTH_BUFFER_BIT);
      glEnable(GL_DEPTH_TEST);

      /* Draw the first color cone */
      glPushMatrix();
      glRotatef(axes->priv->orientation[1], 0, 0, 1);
      glRotatef(axes->priv->orientation[0], 0, 1, 0);
      draw_coloured_cone(length, 1.2*length, 16, axes->priv->orientation[2]);
      glPopMatrix();

      drawAxes(1.5*length, axes->priv->matrix, w, h, axes->priv->lineWidth,
               axes->priv->rgb, _("front"), TRUE, axes->priv->lbl);

      glViewport(xx, yy+h, w, h);

      /* Enabling front culling and drawing the second color cone */
      glPushMatrix();
      glRotatef(axes->priv->orientation[1], 0, 0, 1);
      glRotatef(axes->priv->orientation[0], 0, 1, 0);
      glCullFace(GL_FRONT);
      draw_coloured_cone(length, 1.2*length, 16, axes->priv->orientation[2]);    
      glCullFace(GL_BACK);
      glPopMatrix();

      drawAxes(1.5*length, axes->priv->matrix, w, h, axes->priv->lineWidth,
               axes->priv->rgb, _("back"), TRUE, axes->priv->lbl);
    }
  else
    {
      glDisable(GL_DEPTH_TEST);
      drawAxes(length, axes->priv->matrix, w, h, axes->priv->lineWidth,
               axes->priv->rgb, NULL, FALSE, axes->priv->lbl);
      glEnable(GL_DEPTH_TEST);
    }
  glPushMatrix();
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);
  /* Back to the main viewport. */
  glViewport(0, 0, axes->priv->view->window.width, axes->priv->view->window.height);

  visu_gl_ext_completeDrawing(ext);
}

/*************************/
/* Parameters & resources*/
/*************************/
/* Export function that is called by visu_module to write the
   values of resources to a file. */
static void exportResources(GString *data, VisuData *dataObj _U_)
{
  if (!defaultAxes)
    return;

  visu_config_file_exportComment(data, DESC_RESOURCE_AXES_USED);
  visu_config_file_exportEntry(data, FLAG_RESOURCE_AXES_USED, NULL,
                               "%d", visu_gl_ext_getActive(VISU_GL_EXT(defaultAxes)));

  visu_config_file_exportComment(data, DESC_RESOURCE_AXES_COLOR);
  visu_config_file_exportEntry(data, FLAG_RESOURCE_AXES_COLOR, NULL,
                               "%4.3f %4.3f %4.3f", defaultAxes->priv->rgb[0],
                               defaultAxes->priv->rgb[1], defaultAxes->priv->rgb[2]);

  visu_config_file_exportComment(data, DESC_RESOURCE_AXES_LINE);
  visu_config_file_exportEntry(data, FLAG_RESOURCE_AXES_LINE, NULL,
                               "%4.0f", defaultAxes->priv->lineWidth);

  visu_config_file_exportComment(data, DESC_RESOURCE_AXES_STIPPLE);
  visu_config_file_exportEntry(data, FLAG_RESOURCE_AXES_STIPPLE, NULL,
                               "%d", defaultAxes->priv->lineStipple);

  visu_config_file_exportComment(data, DESC_RESOURCE_AXES_POSITION);
  visu_config_file_exportEntry(data, FLAG_RESOURCE_AXES_POSITION, NULL,
                               "%4.3f %4.3f", defaultAxes->priv->xpos,
                               defaultAxes->priv->ypos);

  visu_config_file_exportComment(data, DESC_RESOURCE_AXES_LABEL);
  visu_config_file_exportEntry(data, FLAG_RESOURCE_AXES_LABEL_X, NULL,
                               "%s", defaultAxes->priv->lbl[TOOL_XYZ_X]);
  visu_config_file_exportEntry(data, FLAG_RESOURCE_AXES_LABEL_Y, NULL,
                               "%s", defaultAxes->priv->lbl[TOOL_XYZ_Y]);
  visu_config_file_exportEntry(data, FLAG_RESOURCE_AXES_LABEL_Z, NULL,
                               "%s", defaultAxes->priv->lbl[TOOL_XYZ_Z]);

  visu_config_file_exportEntry(data, FLAG_RESOURCE_AXES_FACTOR, NULL,
                               "%4.3f", defaultAxes->priv->lgFact);

  visu_config_file_exportComment(data, "");
}
