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

#include <math.h>

#include "visu_tools.h"
#include "visu_data.h" /* To be removed! */
#include "iface_boxed.h"
#include "iface_nodemasker.h"
#include "coreTools/toolMatrix.h"

/**
 * SECTION:visu_box
 * @short_description: Defines a bounding box.
 *
 * <para></para>
 */

/* Local methods. */
static gfloat _getBoxExtens(const VisuBox *box, gboolean withExt);
static void _setUpGeometry(VisuBox *box, gboolean emit);
static void _setUpMatrixFromCell(VisuBox *box);

/**
 * VisuBoxPrivate:
 *
 * Opaque structure to store private attributes of #VisuBox objects.
 */
struct _VisuBoxPrivate
{
  gboolean dispose_has_run;

  /* The unit for length. */
  ToolUnits units;

  /* The periodicity. */
  VisuBoxBoundaries bc;
  /* The extension of the box. */
  gboolean extActive;
  float extension[3];

  /* This is the geometry of the box. Array position 1 to 6
     correspond to xx, xy, yy, zx, zy and zz. */
  double cell[6];
  /* This is the origin in cartesian coordinates of the box origin. */
  double origin[3];
  /* The longest length in the box (with or without extension), and
     the margin to add. */
  float extens[2], margin;

  /* Transformation matrices. */
  /* This is the matrix that transform cartesian coordinates to
     coordinates in the box geometry. Use visu_data_convertXYZtoBoxCoordinates()
     to access this matrix. */
  double fromXYZtoBox[3][3];
  /* This is the matrix that transform box coordinates to
     cartesian coordinates. Use visu_data_convertBoxCoordinatestoXYZ()
     to access this matrix. */
  double fromBoxtoXYZ[3][3];
  /* This matrix is set up if the box was given in full [3][3]
     matrix and that cartesian coordinates need rotation before
     storing them. */
  float fromFullToCell[3][3];

  VisuBoxHiddingStatus hidding;
};

enum {
  SIZE_CHANGED_SIGNAL,
  UNIT_CHANGED_SIGNAL,
  LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
enum
  {
    PROP_0,
    UNITS_PROP,
    EXPAND_PROP,
    USE_EXPAND_PROP,
    BC_PROP,
    HIDDING_PROP,
    N_PROP,
    ADJUST_PROP,
    BOX_PROP
  };
static GParamSpec *properties[N_PROP];


static void visu_box_dispose     (GObject* obj);
static void visu_box_finalize    (GObject* obj);
static void visu_box_get_property(GObject* obj, guint property_id,
                                  GValue *value, GParamSpec *pspec);
static void visu_box_set_property(GObject* obj, guint property_id,
                                  const GValue *value, GParamSpec *pspec);
static void visu_boxed_interface_init(VisuBoxedInterface *iface);
static void visu_node_masker_interface_init(VisuNodeMaskerInterface *iface);

G_DEFINE_TYPE_WITH_CODE(VisuBox, visu_box, VISU_TYPE_OBJECT,
                        G_ADD_PRIVATE(VisuBox)
                        G_IMPLEMENT_INTERFACE(VISU_TYPE_NODE_MASKER,
                                              visu_node_masker_interface_init)
                        G_IMPLEMENT_INTERFACE(VISU_TYPE_BOXED,
                                              visu_boxed_interface_init))

static VisuBox* _getBox(VisuBoxed *self);
static gboolean _maskApply(const VisuNodeMasker *self, VisuNodeArray *array);

static void visu_box_class_init(VisuBoxClass *klass)
{
  DBG_fprintf(stderr, "Visu Box: creating the class of the object.\n");
  DBG_fprintf(stderr, "                - adding new signals ;\n");

  /**
   * VisuBox::SizeChanged:
   * @box: the object which received the signal ;
   * @extens: the new longuest distance in the box taking into account
   * the extension.
   *
   * Gets emitted when the box size is changed (because of box
   * duplication for instance).
   *
   * Since: 3.7
   */
  signals[SIZE_CHANGED_SIGNAL] =
    g_signal_new("SizeChanged", G_TYPE_FROM_CLASS(klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
                 0, NULL, NULL, g_cclosure_marshal_VOID__FLOAT,
                 G_TYPE_NONE, 1, G_TYPE_FLOAT, NULL);
  /**
   * VisuBox::UnitChanged:
   * @box: the object which received the signal ;
   * @factor: the factor that has been applied.
   *
   * Gets emitted when the units are changed.
   *
   * Since: 3.7
   */
  signals[UNIT_CHANGED_SIGNAL] =
    g_signal_new("UnitChanged", G_TYPE_FROM_CLASS(klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
		 0, NULL, NULL, g_cclosure_marshal_VOID__FLOAT,
                 G_TYPE_NONE, 1, G_TYPE_FLOAT, NULL);

  /* Connect the overloading methods. */
  G_OBJECT_CLASS(klass)->dispose  = visu_box_dispose;
  G_OBJECT_CLASS(klass)->finalize = visu_box_finalize;
  G_OBJECT_CLASS(klass)->set_property = visu_box_set_property;
  G_OBJECT_CLASS(klass)->get_property = visu_box_get_property;

  /**
   * VisuBox::units:
   *
   * The units of the length dimensions.
   *
   * Since: 3.8
   */
  properties[UNITS_PROP] = g_param_spec_uint("units", "Units",
                                             "Units of dimensions",
                                             TOOL_UNITS_UNDEFINED, TOOL_UNITS_N_VALUES - 1,
                                             TOOL_UNITS_UNDEFINED, G_PARAM_READWRITE);
  /**
   * VisuBox::expansion:
   *
   * The expansion applied on [x,y,z] axis.
   *
   * Since: 3.8
   */
  properties[EXPAND_PROP] = g_param_spec_boxed("expansion", "Expansion on all axis",
                                               "Expanion on all axis",
                                               TOOL_TYPE_VECTOR, G_PARAM_READWRITE);
  /**
   * VisuBox::use-expansion:
   *
   * Wether the expansion is applied or not.
   *
   * Since: 3.8
   */
  properties[USE_EXPAND_PROP] = g_param_spec_boolean("use-expansion", "Use expansion",
                                                     "Expanion is active or not",
                                                     FALSE, G_PARAM_READWRITE);
  /**
   * VisuBox::boundary:
   *
   * The boundary conditions of the box.
   *
   * Since: 3.8
   */
  properties[BC_PROP] = g_param_spec_uint("boundary", "Boundary",
                                          "Boundary conditions",
                                          VISU_BOX_FREE, VISU_BOX_PERIODIC,
                                          VISU_BOX_PERIODIC, G_PARAM_READWRITE);
  /**
   * VisuBox::hidding-scheme:
   *
   * The hidding scheme used by the box, if any.
   *
   * Since: 3.8
   */
  properties[HIDDING_PROP] = g_param_spec_uint("hidding-scheme", "Hidding scheme",
                                               "hidding scheme used by the box.",
                                               VISU_BOX_HIDE_NONE, VISU_BOX_HIDE_INSIDE,
                                               VISU_BOX_HIDE_NONE, 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");
}
static void visu_boxed_interface_init(VisuBoxedInterface *iface)
{
  iface->get_box = _getBox;
}
static void visu_node_masker_interface_init(VisuNodeMaskerInterface *iface)
{
  iface->apply = _maskApply;
}

static void visu_box_init(VisuBox *box)
{
  guint i;

  DBG_fprintf(stderr, "Visu Box: initializing a new object (%p).\n",
	      (gpointer)box);
  box->priv = visu_box_get_instance_private(box);
  box->priv->dispose_has_run = FALSE;

  box->priv->extActive        = FALSE;
  for (i = 0; i < 3; i++)
    box->priv->extension[i]   = 0.;
  for (i = 0; i < 6; i++)
    box->priv->cell[i]        = G_MAXFLOAT;
  box->priv->origin[0]        = 0.;
  box->priv->origin[1]        = 0.;
  box->priv->origin[2]        = 0.;
  box->priv->bc               = VISU_BOX_FREE;
  box->priv->extens[0]        = G_MAXFLOAT;
  box->priv->extens[1]        = G_MAXFLOAT;
  box->priv->margin           = G_MAXFLOAT;
  box->priv->fromFullToCell[0][0] = G_MAXFLOAT;
  box->priv->units            = TOOL_UNITS_UNDEFINED;
  box->priv->hidding          = VISU_BOX_HIDE_NONE;
}

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

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

  box = VISU_BOX(obj);
  if (box->priv->dispose_has_run)
    return;
  box->priv->dispose_has_run = TRUE;

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

  g_return_if_fail(obj);

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

  /* box = VISU_BOX(obj)->priv; */

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

  DBG_fprintf(stderr, "Visu Box: get property '%s'.\n",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case BOX_PROP:
      g_value_set_object(value, obj);
      break;
    case UNITS_PROP:
      g_value_set_uint(value, self->priv->units);
      break;
    case USE_EXPAND_PROP:
      g_value_set_boolean(value, self->priv->extActive);
      break;
    case EXPAND_PROP:
      g_value_set_static_boxed(value, (gpointer)self->priv->extension);
      break;
    case BC_PROP:
      g_value_set_uint(value, self->priv->bc);
      break;
    case HIDDING_PROP:
      g_value_set_uint(value, self->priv->hidding);
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}
static void visu_box_set_property(GObject* obj, guint property_id,
                                  const GValue *value, GParamSpec *pspec)
{
  VisuBox *self = VISU_BOX(obj);

  DBG_fprintf(stderr, "Visu Box: set property '%s'.\n",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case UNITS_PROP:
      visu_box_setUnit(self, g_value_get_uint(value));
      break;
    case USE_EXPAND_PROP:
      visu_box_setExtensionActive(self, g_value_get_boolean(value));
      break;
    case EXPAND_PROP:
      visu_box_setExtension(self, (gfloat*)g_value_get_boxed(value));
      break;
    case BC_PROP:
      visu_box_setBoundary(self, g_value_get_uint(value));
      break;
    case HIDDING_PROP:
      visu_box_setHiddingStatus(self, g_value_get_uint(value));
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}

/**
 * visu_box_new:
 * @geometry: (array fixed-size=6): a cell definition.
 * @bc: a boundary condition.
 *
 * A #VisuBox object store the definition of a cell.
 *
 * Since: 3.7
 *
 * Returns: (transfer full): create a new #VisuBox object.
 **/
VisuBox* visu_box_new(double geometry[VISU_BOX_N_VECTORS],
                      VisuBoxBoundaries bc)
{
  VisuBox *box;

  DBG_fprintf(stderr, "Visu Box: create a new VisuBox object of type %d.\n",
              (int)VISU_TYPE_BOX);
  box = VISU_BOX(g_object_new(VISU_TYPE_BOX, NULL));

  visu_box_setBoundary(box, bc);
  visu_box_setGeometry(box, geometry);

  return box;
}

/**
 * visu_box_new_full:
 * @full: (array fixed-size=9): a cell definition (full matrix).
 * @bc: a boundary condition.
 *
 * A #VisuBox object stores the definition of a cell. This may fail,
 * if @full does not define a 3D basis set (the three vectors are not
 * linearly independant). To check this, use visu_box_getGeometry() to
 * test if the first vector is #G_MAXFLOAT.
 *
 * Since: 3.7
 *
 * Returns: (transfer full): create a new #VisuBox object.
 **/
VisuBox* visu_box_new_full(double full[3][3], VisuBoxBoundaries bc)
{
  VisuBox *box;

  DBG_fprintf(stderr, "Visu Box: create a new VisuBox object of type %d.\n",
              (int)VISU_TYPE_BOX);
  box = VISU_BOX(g_object_new(VISU_TYPE_BOX, NULL));

  visu_box_setBoundary(box, bc);
  visu_box_setGeometryFull(box, full);

  return box;
}

/**
 * visu_box_setBoundary:
 * @box: a #VisuBox object.
 * @bc: a boundary condition.
 *
 * Set up the boundary conditions of @box.
 *
 * Since: 3.7
 *
 * Returns: TRUE if the boundary conditions of @box are changed.
 **/
gboolean visu_box_setBoundary(VisuBox *box, VisuBoxBoundaries bc)
{
  g_return_val_if_fail(VISU_IS_BOX(box), FALSE);

  DBG_fprintf(stderr, "Visu Box: setting bounding box.\n");
  if (box->priv->bc == bc)
    return FALSE;

  box->priv->bc = bc;

  DBG_fprintf(stderr, "Visu Box: emit 'BoundaryChanged'.\n");
  g_object_notify_by_pspec(G_OBJECT(box), properties[BC_PROP]);
  DBG_fprintf(stderr, "Visu Box: emission done (BoundaryChanged).\n");
  return TRUE;
}
/**
 * visu_box_getBoundary:
 * @box: a #VisuBox object.
 *
 * Get the boundary conditions defined for @box.
 *
 * Since: 3.7
 *
 * Returns: a #VisuBoxBoundaries flag.
 */
VisuBoxBoundaries visu_box_getBoundary(VisuBox *box)
{
  g_return_val_if_fail(VISU_IS_BOX(box), VISU_BOX_FREE);

  return box->priv->bc;
}
/**
 * visu_box_getPeriodicity:
 * @box: a #VisuBox object.
 * @per: (out caller-allocates) (array fixed-size=3): a location to
 * store three periodicties.
 *
 * Get for each {x, y, z} directions if the @box is periodic.
 *
 * Since: 3.7
 */
void visu_box_getPeriodicity(VisuBox *box, gboolean per[3])
{
  g_return_if_fail(VISU_IS_BOX(box));

  per[0] = box->priv->bc & TOOL_XYZ_MASK_X;
  per[1] = box->priv->bc & TOOL_XYZ_MASK_Y;
  per[2] = box->priv->bc & TOOL_XYZ_MASK_Z;
}

/**
 * visu_box_setOrigin:
 * @box: a #VisuBox orbject.
 * @orig: (array fixed-size=3): the new origin
 *
 * Change the origin of @box.
 *
 * Since: 3.8
 *
 * Returns: TRUE if origin is indeed changed.
 **/
gboolean visu_box_setOrigin(VisuBox *box, const float orig[3])
{
  g_return_val_if_fail(VISU_IS_BOX(box), FALSE);

  if (orig[0] == box->priv->origin[0] &&
      orig[1] == box->priv->origin[1] &&
      orig[2] == box->priv->origin[2])
    return FALSE;

  box->priv->origin[0] = orig[0];
  box->priv->origin[1] = orig[1];
  box->priv->origin[2] = orig[2];
  return TRUE;
}

/**
 * visu_box_setGeometryFull:
 * @box: a #VisuBox object.
 * @full: a matrix defining a basis-set.
 *
 * As visu_box_setGeometry(), but using a full matrix.
 *
 * Since: 3.7
 *
 * Returns: TRUE if the geometry is valid and changed.
 **/
gboolean visu_box_setGeometryFull(VisuBox *box, double full[3][3])
{
  double geometry[VISU_BOX_N_VECTORS];

  if (tool_matrix_reducePrimitiveVectors(geometry, full))
    {
      tool_matrix_getRotationFromFull(box->priv->fromFullToCell, full, geometry);
      visu_box_setGeometry(box, geometry);
      return TRUE;
    }
  return FALSE;
}
/**
 * visu_box_setGeometry:
 * @box: a #VisuBox object ;
 * @geometry:  (in) (array fixed-size=6):a 6 floating point array ;
 *
 * This methods set the size of the box. 
 *
 * Returns: TRUE if the geometry is indeed changed.
 *
 * Since: 3.7
 */
gboolean visu_box_setGeometry(VisuBox *box, double geometry[VISU_BOX_N_VECTORS])
{
  int i;

  g_return_val_if_fail(VISU_IS_BOX(box), FALSE);

  for (i = 0; i < VISU_BOX_N_VECTORS; i++)
    box->priv->cell[i] = geometry[i];
  _setUpGeometry(box, TRUE);

  return TRUE;
}
/**
 * visu_box_getGeometry:
 * @box: a #VisuBox object ;
 * @vector: an int corresponding to a vector of the box.
 *
 * Retrieve the value of a vector defining the bounding box. The vector
 * is chosen with an int, see the #VisuBoxVector enum for more
 * details.
 * 
 * Since: 3.7
 *
 * Returns: the value of the required vector (always a positive value
 * for vector = 0, 2 or 5 !), or G_MAXFLOAT if the box has not been initialised.
 */
double visu_box_getGeometry(VisuBox *box, VisuBoxVector vector)
{
  g_return_val_if_fail(VISU_IS_BOX(box) &&
                       vector < VISU_BOX_N_VECTORS, G_MAXFLOAT);

  return (float)box->priv->cell[vector];
}

static void _setUpGeometry(VisuBox *box, gboolean emit)
{
  DBG_fprintf(stderr, "Visu Box: the bounding box is set to:\n %f %f %f\n %f %f %f\n",
	      box->priv->cell[0], box->priv->cell[1], box->priv->cell[2],
              box->priv->cell[3], box->priv->cell[4], box->priv->cell[5]);

  _setUpMatrixFromCell(box);
  box->priv->extens[0] = _getBoxExtens(box, FALSE);
  box->priv->extens[1] = _getBoxExtens(box, TRUE);
  DBG_fprintf(stderr, "Visu Box: set box geometry done (%g %g).\n",
              box->priv->extens[0], box->priv->extens[1]);

  if (box->priv->cell[0] != G_MAXFLOAT && box->priv->margin != G_MAXFLOAT && emit)
    {
      DBG_fprintf(stderr, "Visu Box: emit SizeChanged.\n");
      g_signal_emit(box, signals[SIZE_CHANGED_SIGNAL],
                    0, box->priv->extens[1] + box->priv->margin, NULL);
      DBG_fprintf(stderr, "Visu Box: emission done (SizeChanged).\n");
    }
}

static void _setUpMatrixFromCell(VisuBox *box)
{
  /* Create the transformation matrix. */
  box->priv->fromXYZtoBox[0][0] =
    1. / box->priv->cell[VISU_BOX_DXX];
  box->priv->fromXYZtoBox[0][1] =
    - box->priv->cell[VISU_BOX_DYX] /
    box->priv->cell[VISU_BOX_DXX] /
    box->priv->cell[VISU_BOX_DYY];
  box->priv->fromXYZtoBox[0][2] =
    - (box->priv->cell[VISU_BOX_DZX] /
       box->priv->cell[VISU_BOX_DXX] -
       box->priv->cell[VISU_BOX_DYX] *
       box->priv->cell[VISU_BOX_DZY] / 
       box->priv->cell[VISU_BOX_DXX] / 
       box->priv->cell[VISU_BOX_DYY] ) /
    box->priv->cell[VISU_BOX_DZZ];
  box->priv->fromXYZtoBox[1][0] = 0.;
  box->priv->fromXYZtoBox[1][1] =
    1. / box->priv->cell[VISU_BOX_DYY];
  box->priv->fromXYZtoBox[1][2] =
    - box->priv->cell[VISU_BOX_DZY] /
    box->priv->cell[VISU_BOX_DYY] /
    box->priv->cell[VISU_BOX_DZZ];
  box->priv->fromXYZtoBox[2][0] = 0.;
  box->priv->fromXYZtoBox[2][1] = 0.;
  box->priv->fromXYZtoBox[2][2] = 1. /
    box->priv->cell[VISU_BOX_DZZ];

  box->priv->fromBoxtoXYZ[0][0] =
    box->priv->cell[VISU_BOX_DXX];
  box->priv->fromBoxtoXYZ[0][1] =
    box->priv->cell[VISU_BOX_DYX];
  box->priv->fromBoxtoXYZ[0][2] =
    box->priv->cell[VISU_BOX_DZX];
  box->priv->fromBoxtoXYZ[1][0] = 0.;
  box->priv->fromBoxtoXYZ[1][1] =
    box->priv->cell[VISU_BOX_DYY];
  box->priv->fromBoxtoXYZ[1][2] =
    box->priv->cell[VISU_BOX_DZY];
  box->priv->fromBoxtoXYZ[2][0] = 0.;
  box->priv->fromBoxtoXYZ[2][1] = 0.;
  box->priv->fromBoxtoXYZ[2][2] =
    box->priv->cell[VISU_BOX_DZZ];
}

static gfloat _getBoxExtens(const VisuBox *box, gboolean withExt)
{
  float dz2, dy2, dx, dy, dz, su, sc;
  float geometry[VISU_BOX_N_VECTORS];

  /* calculate bare = 1/2 radius of centered sample */
  geometry[VISU_BOX_DXX] = (withExt && box->priv->extActive)?box->priv->cell[VISU_BOX_DXX] *
    (1. + 2. * box->priv->extension[0]):box->priv->cell[VISU_BOX_DXX];
  geometry[VISU_BOX_DYX] = (withExt && box->priv->extActive)?box->priv->cell[VISU_BOX_DYX] *
    (1. + 2. * box->priv->extension[1]):box->priv->cell[VISU_BOX_DYX];
  geometry[VISU_BOX_DZX] = (withExt && box->priv->extActive)?box->priv->cell[VISU_BOX_DZX] *
    (1. + 2. * box->priv->extension[2]):box->priv->cell[VISU_BOX_DZX];
  geometry[VISU_BOX_DYY] = (withExt && box->priv->extActive)?box->priv->cell[VISU_BOX_DYY] *
    (1. + 2. * box->priv->extension[1]):box->priv->cell[VISU_BOX_DYY];
  geometry[VISU_BOX_DZY] = (withExt && box->priv->extActive)?box->priv->cell[VISU_BOX_DZY] *
    (1. + 2. * box->priv->extension[2]):box->priv->cell[VISU_BOX_DZY];
  geometry[VISU_BOX_DZZ] = (withExt && box->priv->extActive)?box->priv->cell[VISU_BOX_DZZ] *
    (1. + 2. * box->priv->extension[2]):box->priv->cell[VISU_BOX_DZZ];

  dz = geometry[VISU_BOX_DZZ];
  dz2 = dz * dz;
  dy = (geometry[VISU_BOX_DYY] + geometry[VISU_BOX_DZY]);
  dy2 = dy * dy;
  dx = (geometry[VISU_BOX_DXX] + geometry[VISU_BOX_DYX] + geometry[VISU_BOX_DZX]);
  su = dx * dx + dy2 + dz2;
  dx = (-geometry[VISU_BOX_DXX] + geometry[VISU_BOX_DYX] + geometry[VISU_BOX_DZX]);
  sc = dx * dx + dy2 + dz2;
  if (sc > su) su = sc;
  dx = (geometry[VISU_BOX_DXX] - geometry[VISU_BOX_DYX] + geometry[VISU_BOX_DZX]);
  dy = (geometry[VISU_BOX_DYY] - geometry[VISU_BOX_DZY]);
  dy2 = dy * dy;
  sc = dx * dx + dy2 + dz2;
  if (sc > su) su = sc;
  dx = (geometry[VISU_BOX_DXX] + geometry[VISU_BOX_DYX] - geometry[VISU_BOX_DZX]);
  sc = dx * dx + dy2 + dz2;
  if (sc > su) su = sc;
  return sqrt(su) * 0.5f;
}

/**
 * visu_box_setMargin:
 * @box: a #VisuBox object.
 * @margin: a float value.
 * @emit: TRUE to emit #VisuBox::SizeChanged signal.
 *
 * This routine add some margin to defined the OpenGL rendering zone
 * of @box.
 *
 * Since: 3.7
 *
 * Returns: TRUE if the margin is actually changed.
 **/
gboolean visu_box_setMargin(VisuBox *box, gfloat margin, gboolean emit)
{
  g_return_val_if_fail(VISU_IS_BOX(box), FALSE);

  if (margin < 0.f || margin == box->priv->margin)
    return FALSE;

  DBG_fprintf(stderr, "Visu Box: set margin to %g (was %g).\n", margin, box->priv->margin);
  box->priv->margin = margin;

  if (box->priv->cell[0] != G_MAXFLOAT && box->priv->margin != G_MAXFLOAT && emit)
    {
      DBG_fprintf(stderr, "Visu Box: emit SizeChanged.\n");
      g_signal_emit(box, signals[SIZE_CHANGED_SIGNAL],
                    0, box->priv->extens[1] + box->priv->margin, NULL);
      DBG_fprintf(stderr, "Visu Box: emission done (SizeChanged).\n");
    }

  return TRUE;
}

/**
 * visu_box_getCentre:
 * @box: a #VisuBox object ;
 * @centre: (out) (array fixed-size=3): coordinates of the centre.
 *
 * @centre contains on output the cartesian coordinates of the centre
 * of the bounding box.
 *
 * Since: 3.7
 */
void visu_box_getCentre(VisuBox *box, float centre[3])
{
  g_return_if_fail(VISU_IS_BOX(box));

  centre[0] = box->priv->origin[0] + 0.5f * (box->priv->cell[VISU_BOX_DXX] +
                                             box->priv->cell[VISU_BOX_DYX] +
                                             box->priv->cell[VISU_BOX_DZX]);
  centre[1] = box->priv->origin[1] + 0.5f * (box->priv->cell[VISU_BOX_DYY] +
                                             box->priv->cell[VISU_BOX_DZY]);
  centre[2] = box->priv->origin[2] + 0.5f * (box->priv->cell[VISU_BOX_DZZ]);
}

/**
 * visu_box_convertFullToCell:
 * @box: a #VisuBox object.
 * @cell: (out caller-allocates) (array fixed-size=3):
 * @full: (in) (array fixed-size=3):
 *
 * Convert given cartesian coordinates of a full matrix definition
 * (see visu_box_setGeometryFull()) to cartesian coordinates in the
 * cell definition used by V_Sim. It corresponds to two applied rotations.
 *
 * Since: 3.7
 */
void visu_box_convertFullToCell(VisuBox *box, float cell[3], float full[3])
{
  g_return_if_fail(VISU_IS_BOX(box));

  if (box->priv->fromFullToCell[0][0] != G_MAXFLOAT)
    tool_matrix_productVector(cell, box->priv->fromFullToCell, full);
  else
    {
      cell[0] = full[0];
      cell[1] = full[1];
      cell[2] = full[2];
    }
}

/**
 * visu_box_convertXYZtoBoxCoordinates: (skip)
 * @box: a #VisuBox object ;
 * @boxCoord:an array of floating point values to store the result ;
 * @xyz: an array of floating point values describing coordinates in cartesian.
 *
 * Use this method to transform cartesian coordinates to the box
 * coordinates.
 *
 * Since: 3.7
 */
void visu_box_convertXYZtoBoxCoordinates(const VisuBox *box, float boxCoord[3], float xyz[3])
{
  int i, j;

  g_return_if_fail(VISU_IS_BOX(box) && boxCoord && xyz);

  for (i = 0; i < 3; i++)
    {
      boxCoord[i] = 0.;
      for (j = 0; j < 3; j++)
	boxCoord[i] += (float)box->priv->fromXYZtoBox[i][j] * (xyz[j] - box->priv->origin[j]);
    }
}
/**
 * visu_box_convertXYZToReduced:
 * @box: a #VisuBox object ;
 * @xyz: (in) (array fixed-size=3) (element-type gfloat): floating
 * point values that describes the cartesian coordinates.
 * @u: (out caller-allocates): the x coordinate.
 * @v: (out caller-allocates): the y coordinate.
 * @w: (out caller-allocates): the z coordinate.
 *
 * Use this method to transform cartesian into box coordinates.
 *
 * Since: 3.7
 */
void visu_box_convertXYZToReduced(VisuBox *box, GArray *xyz,
                                  float *u, float *v, float *w)
{
  float red_[3];

  g_return_if_fail(xyz && u && v && w && xyz->len == 3);

  visu_box_convertXYZtoBoxCoordinates(box, red_, (float*)xyz->data);
  *u = red_[0]; *v = red_[1]; *w = red_[2];
}

/**
 * visu_box_convertBoxCoordinatestoXYZ: (skip)
 * @box: a #VisuBox object ;
 * @xyz: an array of floating point values to store the result ;
 * @boxCoord: an array of floating point values that describes the box coordinates.
 *
 * Use this method to transform box coordinates into cartesian.
 *
 * Since: 3.7
 */
void visu_box_convertBoxCoordinatestoXYZ(VisuBox *box, float xyz[3], const float boxCoord[3])
{
  int i, j;

  g_return_if_fail(VISU_IS_BOX(box) && boxCoord && xyz);

  for (i = 0; i < 3; i++)
    {
      xyz[i] = box->priv->origin[i];
      for (j = 0; j < 3; j++)
	xyz[i] += (float)box->priv->fromBoxtoXYZ[i][j] * boxCoord[j];
    }
}
/**
 * visu_box_convertReducedToXYZ:
 * @box: a #VisuBox object ;
 * @red: (in) (array fixed-size=3) (element-type gfloat): floating
 * point values that describes the cartesian coordinates.
 * @x: (out caller-allocates): the x coordinate.
 * @y: (out caller-allocates): the y coordinate.
 * @z: (out caller-allocates): the z coordinate.
 *
 * Use this method to transform box coordinates into cartesian.
 *
 * Since: 3.7
 */
void visu_box_convertReducedToXYZ(VisuBox *box, GArray *red,
				   float *x, float *y, float *z)
{
  float xyz_[3];

  g_return_if_fail(red && x && y && z && red->len == 3);

  visu_box_convertBoxCoordinatestoXYZ(box, xyz_, (float*)red->data);
  *x = xyz_[0]; *y = xyz_[1]; *z = xyz_[2];
}

/**
 * visu_box_getInvMatrix: (skip)
 * @box: a #VisuBox object ;
 * @matrix: an area to store the matrix.
 *
 * This method is used when the inverse box matrix is required. This matrix can transform
 * a vector given in cartesian coordinates into a box vector. If a simple vector
 * multication is required, then the use of visu_box_convertXYZtoBoxCoordinates()
 * should be prefered.
 *
 * Since: 3.7
 */
void visu_box_getInvMatrix(VisuBox *box, double matrix[3][3])
{
  int i, j;

  g_return_if_fail(VISU_IS_BOX(box) && matrix);

  /* Mind the transposition here. */
  for (i = 0; i < 3; i++)
    for (j = 0; j < 3; j++)
      matrix[i][j] = box->priv->fromXYZtoBox[i][j];
}
/**
 * visu_box_getCellMatrix: (skip)
 * @box: a #VisuBox object ;
 * @matrix: an area to store the matrix.
 *
 * This method is used when the box matrix is required. This matrix can transform
 * a vector given in box coordinates into a cartesian vector. If a simple vector
 * multication is required, then the use of visu_box_convertBoxCoordinatestoXYZ()
 * should be prefered.
 *
 * Since: 3.7
 */
void visu_box_getCellMatrix(VisuBox *box, double matrix[3][3])
{
  int i, j;

  g_return_if_fail(VISU_IS_BOX(box) && matrix);

  /* Mind the transposition here. */
  for (i = 0; i < 3; i++)
    for (j = 0; j < 3; j++)
      matrix[i][j] = box->priv->fromBoxtoXYZ[i][j];
}
/**
 * visu_box_getCellMatrixv:
 * @box: a #VisuBox object ;
 * @m11: (out): an area to store the matrix.
 * @m12: (out): an area to store the matrix.
 * @m13: (out): an area to store the matrix.
 * @m21: (out): an area to store the matrix.
 * @m22: (out): an area to store the matrix.
 * @m23: (out): an area to store the matrix.
 * @m31: (out): an area to store the matrix.
 * @m32: (out): an area to store the matrix.
 * @m33: (out): an area to store the matrix.
 *
 * This method is a binding method for visu_box_getCellMatrix().
 *
 * Since: 3.7
 */
void visu_box_getCellMatrixv(VisuBox *box, double *m11, double *m12, double *m13,
                             double *m21, double *m22, double *m23,
                             double *m31, double *m32, double *m33)
{
  double m[3][3];

  g_return_if_fail(m11 && m12 && m13 && m21 && m22 && m23 && m31 && m32 && m33);

  visu_box_getCellMatrix(box, m);
  *m11 = m[0][0];
  *m12 = m[0][1];
  *m13 = m[0][2];
  *m21 = m[1][0];
  *m22 = m[1][1];
  *m23 = m[1][2];
  *m31 = m[2][0];
  *m32 = m[2][1];
  *m33 = m[2][2];
}

/**
 * visu_box_constrainInside:
 * @box: a #VisuBox object.
 * @translat: a translation in cartesian coordinates (out values).
 * @xyz: a set of cartesian coordinates.
 * @withExt: TRUE to take into account the box expansions.
 *
 * Given the box defintion @box and the initial @xyz cartesian coordinates, it
 * returns the translation @translat to be applied to @xyz to move the
 * node into the box.
 *
 * Since: 3.7
 *
 * Returns: TRUE if @translat is not (0;0;0).
 */
gboolean visu_box_constrainInside(VisuBox *box, float translat[3], float xyz[3],
                                  gboolean withExt)
{
  float boxCoord[3], bounds[3], size[3];
  gboolean moved;
  int k;

  if (withExt && box->priv->extActive)
    {
      bounds[0] = ceil(box->priv->extension[0]);
      bounds[1] = ceil(box->priv->extension[1]);
      bounds[2] = ceil(box->priv->extension[2]);
    }
  else
    {
      bounds[0] = 0.f;
      bounds[1] = 0.f;
      bounds[2] = 0.f;
    }
  size[0]   = 1. + 2. * bounds[0];
  size[1]   = 1. + 2. * bounds[1];
  size[2]   = 1. + 2. * bounds[2];

  visu_box_convertXYZtoBoxCoordinates(box, boxCoord, xyz);
  moved = FALSE;
  for (k = 0; k < 3; k++)
    {
      while (boxCoord[k] < - bounds[k])
	{
	  moved = TRUE;
	  boxCoord[k] += size[k];
	}
      while (boxCoord[k] >= 1. + bounds[k])
	{
	  moved = TRUE;
	  boxCoord[k] -= size[k];
	}
    }
  if (moved)
    {
      visu_box_convertBoxCoordinatestoXYZ(box, translat, boxCoord);
      translat[0] -= xyz[0];
      translat[1] -= xyz[1];
      translat[2] -= xyz[2];
      DBG_fprintf(stderr, "Tool Matrix: move coord. from %gx%gx%g to %gx%gx%g.\n",
		  xyz[0], xyz[1], xyz[2],
		  boxCoord[0], boxCoord[1], boxCoord[2]);
    }
  else
    {
      translat[0] = 0.f;
      translat[1] = 0.f;
      translat[2] = 0.f;
    }
  return moved;
}

/**
 * visu_box_getVertices:
 * @box: a #VisuBox object.
 * @v: (out caller-allocates) (type VisuBoxVertices*): the position of
 * the eight vertices of the bounding box.
 * @withExtension: a boolean.
 *
 * All nodes are rendered inside a bounding box, this method can be used to retrieve
 * it. This box is not the drawn box but the box containing all the
 * nodes, included possible extension. To get the box itself, use
 * visu_box_getCellMatrix() instead. One can also get the vertices of
 * the box itself using FALSE as @withExtension argument.
 */
void visu_box_getVertices(VisuBox *box, float v[8][3],
                          gboolean withExtension)
{
  int i;
  float transX[3], transY[3], transZ[3], ext[3];
  double *boxGeometry;

  g_return_if_fail(VISU_IS_BOX(box));

  if (withExtension && box->priv->extActive)
    {
      ext[0] = box->priv->extension[0];
      ext[1] = box->priv->extension[1];
      ext[2] = box->priv->extension[2];
    }
  else
    {
      ext[0] = 0.f;
      ext[1] = 0.f;
      ext[2] = 0.f;
    }
  DBG_fprintf(stderr, "Visu Box: get the box vertices with"
	      " extension %d (%g;%g;%g).\n", withExtension, ext[0], ext[1], ext[2]);

  boxGeometry = box->priv->cell;
  transX[0] = ext[0] * boxGeometry[0];
  transX[1] = 0.f;
  transX[2] = 0.f;
  transY[0] = ext[1] * boxGeometry[1];
  transY[1] = ext[1] * boxGeometry[2];
  transY[2] = 0.f;
  transZ[0] = ext[2] * boxGeometry[3];
  transZ[1] = ext[2] * boxGeometry[4];
  transZ[2] = ext[2] * boxGeometry[5];

  /* [0;0;0] */
  DBG_fprintf(stderr, " | v    is %p.\n", (gpointer)v);
  DBG_fprintf(stderr, " | v[0] is %p.\n", (gpointer)v[0]);
  v[0][0] = 0.f - transX[0] - transY[0] - transZ[0];
  v[0][1] = 0.f - transX[1] - transY[1] - transZ[1];
  v[0][2] = 0.f - transX[2] - transY[2] - transZ[2];

  /* [1;0;0] */
  DBG_fprintf(stderr, " | v[1] is %p.\n", (gpointer)v[1]);
  v[1][0] = boxGeometry[0] + transX[0] - transY[0] - transZ[0];
  v[1][1] = 0.f            + transX[1] - transY[1] - transZ[1];
  v[1][2] = 0.f            + transX[2] - transY[2] - transZ[2];

  /* [0;1;0] */
  DBG_fprintf(stderr, " | v[3] is %p.\n", (gpointer)v[3]);
  v[3][0] = boxGeometry[1] - transX[0] + transY[0] - transZ[0];
  v[3][1] = boxGeometry[2] - transX[1] + transY[1] - transZ[1];
  v[3][2] = 0.f            - transX[2] + transY[2] - transZ[2];

  /* [0;0;1] */
  DBG_fprintf(stderr, " | v[4] is %p.\n", (gpointer)v[4]);
  v[4][0] = boxGeometry[3] - transX[0] - transY[0] + transZ[0];
  v[4][1] = boxGeometry[4] - transX[1] - transY[1] + transZ[1];
  v[4][2] = boxGeometry[5] - transX[2] - transY[2] + transZ[2];

  /* [1;1;0] */
  v[2][0] = boxGeometry[0] + boxGeometry[1] + transX[0] + transY[0] - transZ[0];
  v[2][1] = 0.f            + boxGeometry[2] + transX[1] + transY[1] - transZ[1];
  v[2][2] = 0.f            + 0.f            + transX[2] + transY[2] - transZ[2];
      
  /* [1;0;1] */
  v[5][0] = boxGeometry[3] + boxGeometry[0] + transX[0] - transY[0] + transZ[0];
  v[5][1] = boxGeometry[4] + 0.f            + transX[1] - transY[1] + transZ[1];
  v[5][2] = boxGeometry[5] + 0.f            + transX[2] - transY[2] + transZ[2];
      
  /* [1;1;1] */
  v[6][0] = boxGeometry[3] + boxGeometry[0] + boxGeometry[1] + transX[0] + transY[0] + transZ[0];
  v[6][1] = boxGeometry[4] + boxGeometry[2] + transX[1] + transY[1] + transZ[1];
  v[6][2] = boxGeometry[5] + 0.f            + transX[2] + transY[2] + transZ[2];
      
  /* [0;1;1] */
  v[7][0] = boxGeometry[3] + boxGeometry[1] - transX[0] + transY[0] + transZ[0];
  v[7][1] = boxGeometry[4] + boxGeometry[2] - transX[1] + transY[1] + transZ[1];
  v[7][2] = boxGeometry[5] + 0.f            - transX[2] + transY[2] + transZ[2];

  for (i = 0; i < 8; i++)
    {
      v[i][0] += box->priv->origin[0];
      v[i][1] += box->priv->origin[1];
      v[i][2] += box->priv->origin[2];
    }
  DBG_fprintf(stderr, " | done.\n");
}

/**
 * visu_box_getExtensionActive:
 * @box: a #VisuBox object.
 *
 * Retrieve if extensions are applied or not.
 *
 * Since: 3.8
 *
 * Returns: TRUE if extensions are applied.
 **/
gboolean visu_box_getExtensionActive(VisuBox *box)
{
  g_return_val_if_fail(VISU_IS_BOX(box), FALSE);
  return box->priv->extActive;
}
/**
 * visu_box_setExtensionActive:
 * @box: a #VisuBox object.
 * @status: a boolean.
 *
 * Change if extension values are used not not.
 *
 * Since: 3.8
 *
 * Returns: TRUE if status has changed.
 **/
gboolean visu_box_setExtensionActive(VisuBox *box, gboolean status)
{
  float oldExtens;

  g_return_val_if_fail(VISU_IS_BOX(box), FALSE);

  if (box->priv->extActive == status)
    return FALSE;

  box->priv->extActive = status;
  g_object_notify_by_pspec(G_OBJECT(box), properties[USE_EXPAND_PROP]);

  oldExtens = box->priv->extens[1];
  box->priv->extens[1] = _getBoxExtens(box, TRUE);
  if (box->priv->cell[0] != G_MAXFLOAT && box->priv->margin != G_MAXFLOAT &&
      box->priv->extens[1] != oldExtens)
    {
      DBG_fprintf(stderr, "Visu Box: emit SizeChanged.\n");
      g_signal_emit(box, signals[SIZE_CHANGED_SIGNAL],
                    0, box->priv->extens[1] + box->priv->margin, NULL);
      DBG_fprintf(stderr, "Visu Box: emission done (SizeChanged).\n");
    }

  return TRUE;
}
/**
 * visu_box_getExtension:
 * @boxObj: a #VisuBox object ;
 * @extension: (out) (array fixed-size=3): an allocated array to store the values.
 *
 * Using visu_box_setExtension(), it is possible to duplicate the primitive box
 * in each directions. Use this method to know the current extension. Returned
 * values are positive floating point values. An extension of 0. means that
 * only the primitive box exists, while a value of one means a duplication of
 * one box in each direction of the coordinate.
 *
 * Since: 3.7
 */
void visu_box_getExtension(const VisuBox *boxObj, float extension[3])
{
  g_return_if_fail(VISU_IS_BOX(boxObj));

  extension[0] = boxObj->priv->extension[0];
  extension[1] = boxObj->priv->extension[1];
  extension[2] = boxObj->priv->extension[2];
}
/**
 * visu_box_setExtension:
 * @boxObj: a #VisuBox object ;
 * @extension: (in) (array fixed-size=3): an allocated array to store the values.
 *
 * Change the duplication of the box in the three directions.
 *
 * Since: 3.7
 *
 * Returns: TRUE if the extension of @box is actually changed.
 */
gboolean visu_box_setExtension(VisuBox *boxObj, float extension[3])
{
  float oldExtens;

  g_return_val_if_fail(VISU_IS_BOX(boxObj), FALSE);

  if (extension[0] == boxObj->priv->extension[0] &&
      extension[1] == boxObj->priv->extension[1] &&
      extension[2] == boxObj->priv->extension[2])
    return FALSE;
  
  boxObj->priv->extension[0] = extension[0];
  boxObj->priv->extension[1] = extension[1];
  boxObj->priv->extension[2] = extension[2];

  DBG_fprintf(stderr, "Visu Box: emit ExtensionChanged.\n");
  g_object_notify_by_pspec(G_OBJECT(boxObj), properties[EXPAND_PROP]);
  DBG_fprintf(stderr, "Visu Box: emission done (ExtensionChanged).\n");

  oldExtens = boxObj->priv->extens[1];
  boxObj->priv->extens[1] = _getBoxExtens(boxObj, TRUE);
  if (boxObj->priv->cell[0] != G_MAXFLOAT && boxObj->priv->margin != G_MAXFLOAT &&
      boxObj->priv->extens[1] != oldExtens)
    {
      DBG_fprintf(stderr, "Visu Box: emit SizeChanged.\n");
      g_signal_emit(boxObj, signals[SIZE_CHANGED_SIGNAL],
                    0, boxObj->priv->extens[1] + boxObj->priv->margin, NULL);
      DBG_fprintf(stderr, "Visu Box: emission done (SizeChanged).\n");
    }

  return TRUE;
}

/**
 * visu_box_getGlobalSize:
 * @box: a #VisuBox object.
 * @withExt: a boolean.
 *
 * The box has a whole size that contains it (including margin, see
 * visu_box_setMargin()), this size can be retrieve taking into
 * account the extension of the box, or not.
 *
 * Since: 3.7
 *
 * Returns: %G_MAXFLOAT on failure, otherwise a length for the biggest
 * diagonal distance of the box, with or without extension.
 **/
float visu_box_getGlobalSize(VisuBox *box, gboolean withExt)
{
  g_return_val_if_fail(VISU_IS_BOX(box), G_MAXFLOAT);

  if (withExt)
    return box->priv->extens[1] + box->priv->margin;
  else
    return box->priv->extens[0];
}

/**
 * visu_box_getUnit:
 * @box: a #VisuBox object.
 *
 * The lengths of @box may be given in a certain unit using
 * visu_box_setUnit().
 *
 * Since: 3.7
 *
 * Returns: the #ToolUnits of @box or #TOOL_UNITS_UNDEFINED.
 */
ToolUnits visu_box_getUnit(VisuBox *box)
{
  g_return_val_if_fail(VISU_IS_BOX(box), TOOL_UNITS_UNDEFINED);

  return box->priv->units;
}
/**
 * visu_box_setUnit:
 * @box: a #VisuBox object.
 * @unit: a #ToolUnits flag.
 *
 * The lengths of @box may be given in a certain unit by calling this
 * routine. If the unit is different from the previously defined, the
 * coordinate are scaled accordingly.
 *
 * Since: 3.7
 *
 * Returns: TRUE if the unit has been changed.
 */
gboolean visu_box_setUnit(VisuBox *box, ToolUnits unit)
{
  ToolUnits unit_;
  double fact;

  g_return_val_if_fail(VISU_IS_BOX(box), FALSE);

  DBG_fprintf(stderr, "Visu Box: set unit to %d (%d).\n", unit, box->priv->units);
  if (box->priv->units == unit)
    return FALSE;

  unit_ = box->priv->units;
  box->priv->units = unit;
  g_object_notify_by_pspec(G_OBJECT(box), properties[UNITS_PROP]);

  if (unit_ == TOOL_UNITS_UNDEFINED || unit == TOOL_UNITS_UNDEFINED)
    {
      DBG_fprintf(stderr, "Visu Box: emit UnitChanged.\n");
      g_signal_emit(box, signals[UNIT_CHANGED_SIGNAL], 0, 1.f);
      DBG_fprintf(stderr, "Visu Box: emission done (UnitChanged).\n");
      return TRUE;
    }

  fact = (double)tool_physic_getUnitValueInMeter(unit_) /
    tool_physic_getUnitValueInMeter(unit);
  DBG_fprintf(stderr, "Visu Box: multiplying factor is %g.\n", fact);

  /* We do an homothety on the box. */
  box->priv->cell[0] *= fact;
  box->priv->cell[1] *= fact;
  box->priv->cell[2] *= fact;
  box->priv->cell[3] *= fact;
  box->priv->cell[4] *= fact;
  box->priv->cell[5] *= fact;
  _setUpGeometry(box, FALSE);

  DBG_fprintf(stderr, "Visu Box: emit UnitChanged.\n");
  g_signal_emit(box, signals[UNIT_CHANGED_SIGNAL], 0, fact);
  DBG_fprintf(stderr, "Visu Box: emission done (UnitChanged).\n");

  if (box->priv->cell[0] != G_MAXFLOAT && box->priv->margin != G_MAXFLOAT)
    {
      DBG_fprintf(stderr, "Visu Box: emit SizeChanged.\n");
      box->priv->margin *= fact;
      g_signal_emit(box, signals[SIZE_CHANGED_SIGNAL],
                    0, box->priv->extens[1] + box->priv->margin);
      DBG_fprintf(stderr, "Visu Box: emission done (SizeChanged).\n");
    }

  return TRUE;
}

/**
 * visu_box_getInside:
 * @box: a #VisuBox object.
 * @vect: (array fixed-size=3) (inout): some cartesian coordinates.
 * @ratio: a ratio in [0;1].
 *
 * Update @vect, taking into consideration the particular periodicity
 * of @box, for @vect expressed in reduced coordinates to be within
 * [-ratio;ratio].
 *
 * Since: 3.8
 *
 * Returns: TRUE if vect is actually changed.
 **/
gboolean visu_box_getInside(VisuBox *box, float vect[3], float ratio)
{
  int i, j;
  float red[3];
  VisuBoxBoundaries bc;
  gboolean mod;

  g_return_val_if_fail(VISU_IS_BOX(box), FALSE);

  mod = FALSE;

  bc = box->priv->bc;
  if (bc == VISU_BOX_FREE)
    return mod;

  /* DBG_fprintf(stderr, "Visu Box: transform %gx%gx%g into", red[0], red[1], red[2]); */
  red[0] = 0.;
  for (j = 0; j < 3; j++)
    red[0] += (float)box->priv->fromXYZtoBox[0][j] * vect[j];
  if (bc & TOOL_XYZ_MASK_X)
    {
      mod = (red[0] < -ratio || red[0] >= ratio);
      while (red[0] >= ratio) red[0] -= 1.f;
      while (red[0] < -ratio) red[0] += 1.f;
    }

  red[1] = 0.;
  for (j = 0; j < 3; j++)
    red[1] += (float)box->priv->fromXYZtoBox[1][j] * vect[j];
  if (bc & TOOL_XYZ_MASK_Y)
    {
      mod = mod || (red[1] < -ratio || red[1] >= ratio);
      while (red[1] >= ratio) red[1] -= 1.f;
      while (red[1] < -ratio) red[1] += 1.f;
    }

  red[2] = 0.;
  for (j = 0; j < 3; j++)
    red[2] += (float)box->priv->fromXYZtoBox[2][j] * vect[j];
  if (bc & TOOL_XYZ_MASK_Z)
    {
      mod = mod || (red[2] < -ratio || red[2] >= ratio);
      while (red[2] >= ratio) red[2] -= 1.f;
      while (red[2] < -ratio) red[2] += 1.f;
    }
  /* DBG_fprintf(stderr, " %gx%gx%g.\n", red[0], red[1], red[2]); */
  for (i = 0; i < 3; i++)
    {
      vect[i] = 0.f;
      for (j = 0; j < 3; j++)
	vect[i] += (float)box->priv->fromBoxtoXYZ[i][j] * red[j];
    }
  return mod;
}
/**
 * visu_box_getPeriodicVector: (skip)
 * @box: a #VisuBox object.
 * @vect: (inout) (array fixed-size=3): a vector.
 *
 * Modify @vect to get the shortest equivalent vector, taking into
 * account the periodicity.
 *
 * Since: 3.7
 *
 * Returns: %TRUE if @vect has been modified.
 **/
gboolean visu_box_getPeriodicVector(VisuBox *box, float vect[3])
{
  return visu_box_getInside(box, vect, 0.5f);
}
/**
 * visu_box_getPeriodicVectorv: (rename-to visu_box_getPeriodicVector)
 * @box: a #VisuBox object.
 * @x: (out): the new x part.
 * @y: (out): the new y part.
 * @z: (out): the new z part.
 * @vect: (array fixed-size=3): a vector.
 *
 * Equivalent of visu_box_getPeriodicVector() used for bindings.
 *
 * Since: 3.7
 **/
void visu_box_getPeriodicVectorv(VisuBox *box, float *x, float *y, float *z,
                                  float vect[3])
{
  g_return_if_fail(x && y && z);

  visu_box_getInside(box, vect, 0.5f);
  *x = vect[0];
  *y = vect[1];
  *z = vect[2];
}
/**
 * visu_box_getPeriodicArray:
 * @box: a #VisuBox object.
 * @array: (type gint64): an array of @nEle * 3 floats.
 * @nEle: number of elements in @array
 *
 * Used for bindings.
 *
 * Since: 3.7
 **/
void visu_box_getPeriodicArray(VisuBox *box, float *array, guint nEle)
{
  guint i;

  DBG_fprintf(stderr, "Visu Box: apply periodic distance on array %p (%d).\n",
              (gpointer)array, nEle);
  for (i = 0; i < nEle; i++)
    visu_box_getPeriodicVector(box, array + i * 3);
  DBG_fprintf(stderr, " | done.\n");
}

/**
 * visu_box_getHiddingStatus:
 * @box: a #VisuBox object.
 *
 * Retrieves the masking status of @box.
 *
 * Since: 3.8
 *
 * Returns: the current masking status.
 **/
VisuBoxHiddingStatus visu_box_getHiddingStatus(const VisuBox *box)
{
  g_return_val_if_fail(VISU_IS_BOX(box), VISU_BOX_HIDE_NONE);

  return box->priv->hidding;
}
/**
 * visu_box_setHiddingStatus:
 * @box: a #VisuBox object.
 * @status: a status.
 *
 * Changes the masking status of @box for @status.
 *
 * Since: 3.8
 *
 * Returns: TRUE if the status is actually changed.
 **/
gboolean visu_box_setHiddingStatus(VisuBox *box, VisuBoxHiddingStatus status)
{
  g_return_val_if_fail(VISU_IS_BOX(box), FALSE);

  if (box->priv->hidding == status)
    return FALSE;

  box->priv->hidding = status;
  g_object_notify_by_pspec(G_OBJECT(box), properties[HIDDING_PROP]);

  visu_node_masker_emitDirty(VISU_NODE_MASKER(box));
  return TRUE;
}

/**
 * visu_box_getVolume:
 * @box: a #VisuBox object.
 *
 * Compute the volume of the box.
 *
 * Since: 3.8
 *
 * Returns: the box volume.
 **/
float visu_box_getVolume(const VisuBox *box)
{
  g_return_val_if_fail(VISU_IS_BOX(box), 0.f);

  return box->priv->cell[VISU_BOX_DXX] * box->priv->cell[VISU_BOX_DYY]
    * box->priv->cell[VISU_BOX_DZZ];
}

static VisuBox* _getBox(VisuBoxed *self)
{
  return VISU_BOX(self);
}
static gboolean _maskApply(const VisuNodeMasker *self, VisuNodeArray *array)
{
  gboolean reDraw, outside;
  float point[3], red[3];
  VisuNodeArrayIter iter;
  VisuBox *box;

  g_return_val_if_fail(VISU_IS_BOX(self), FALSE);

  box = VISU_BOX(self);

  DBG_fprintf(stderr, "Visu Box: applying masking properties.\n");
  if (!visu_box_getHiddingStatus(box))
    return FALSE;

  reDraw = FALSE;
  /* We change the rendered attribute of all nodes, very expensive... */
  visu_node_array_iter_new(array, &iter);
  for (visu_node_array_iterStart(array, &iter); iter.element;
       visu_node_array_iterNextElement(array, &iter, FALSE))
    if (visu_element_getMaskable(iter.element) &&
        visu_element_getRendered(iter.element))
      {
        DBG_fprintf(stderr, " | element '%s'\n", iter.element->name);
        for (visu_node_array_iterRestartNode(array, &iter); iter.node;
             visu_node_array_iterNextNode(array, &iter))
          {
            /* If node is already hiden, well, we go to the next. */
            if (!iter.node->rendered)
              continue;

            visu_data_getNodePosition(VISU_DATA(array), iter.node, point);
            visu_box_convertXYZtoBoxCoordinates(box, red, point);
            outside = (red[0] < -box->priv->extension[0] ||
                       red[1] < -box->priv->extension[1] ||
                       red[2] < -box->priv->extension[2] ||
                       red[0] >= 1.f + box->priv->extension[0] ||
                       red[1] >= 1.f + box->priv->extension[1] ||
                       red[2] >= 1.f + box->priv->extension[2]);
            if ((box->priv->hidding == VISU_BOX_HIDE_OUTSIDE && outside) ||
                (box->priv->hidding == VISU_BOX_HIDE_INSIDE && !outside))
              reDraw = visu_node_setVisibility(iter.node, FALSE) || reDraw;
            DBG_fprintf(stderr, " | node '%d' -> %d\n", iter.node->number, iter.node->rendered);
          }
      }
  return reDraw;
}
