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


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

#include "visu_commandLine.h"
#include "extraFunctions/idProp.h"
#include "extraFunctions/typeProp.h"
#include "extraFunctions/coordProp.h"
#include "extraFunctions/geometry.h"
#include "extraFunctions/vibration.h"
#include "coreTools/toolMatrix.h"

/**
 * SECTION:visu_data
 * @short_description: Give methods to store and manage data from
 * input file(s).
 * 
 * <para>The main goal of V_Sim is to draw lists of elements. For
 * example, when used to render atoms, a box that contains 24 silicon
 * atoms and 46 germanium atoms is a box with two elements (silicon
 * and germanium) where the silicon element has 24 nodes and the
 * germanium element has 46 nodes. This module gives then methods to
 * create nodes (see #VisuElement to create and managed
 * elements).</para>
 *
 * <para>All nodes are stored in a structure called #VisuNodeArray and
 * #VisuNodeArray is encapsulated in a #VisuData for all not-node related
 * information. V_Sim uses one #VisuData per input file(s). This
 * structure contains a list of pointers on all the #VisuElement used
 * in this file.</para>
 *
 * <para>To iterate on nodes, one should use the provided iterators
 * (see #VisuNodeArrayIter) methods, like visu_node_array_iter_next().</para>
 */

enum {
  NODE_PROP_ADDED_SIGNAL,
  NODE_PROP_REMOVED_SIGNAL,
  LAST_SIGNAL
};

enum
  {
    PROP_0,
    DESCR_PROP,
    TOTAL_ENERGY_PROP,
    N_PROP,
    TRANS_PROP,
    RED_TRANS_PROP,
    USE_TRANS_PROP,
    MODULO_PROP,
    BOX_PROP,
    ADJUST_PROP
  };
static GParamSpec *properties[N_PROP];

struct _VisuDataPrivate
{
  gboolean dispose_has_run;

  gchar* commentary;

  /******************/
  /* Box attributes */
  /******************/
  VisuBox *box;
  float extension[3];
  gulong unit_signal, expand_signal, expAct_signal;
  /* Translation applied to all nodes when rendered. */
  gboolean inTheBox, inTheBox_replicate;
  gboolean translationActive;
  float translation[3];

  /********************/
  /* Misc. attributes */
  /********************/
  /* The total energy of the system in eV. */
  gdouble totalEnergy;
  /* The list of known VisuNodeValues. */
  GHashTable *nodeProperties;
};


static void visu_data_dispose     (GObject* obj);
static void visu_data_finalize    (GObject* obj);
static void visu_data_get_property(GObject* obj, guint property_id,
				   GValue *value, GParamSpec *pspec);
static void visu_data_set_property(GObject* obj, guint property_id,
				   const GValue *value, GParamSpec *pspec);
static gboolean _inTheBox(VisuPointset *self, gboolean status);
static void _getTranslation(VisuPointset *self, float trans[3]);
static gboolean _setTranslation(VisuPointset *self, float trans[3], gboolean withModulo);
static gboolean _setTranslationActive(VisuPointset *self, gboolean status);
static void _applyTranslation(VisuPointset *self);
static void visu_boxed_interface_init(VisuBoxedInterface *iface);
static void visu_pointset_interface_init(VisuPointsetInterface *iface);

/* Local callbacks. */
static void onBoxUnitChanged(VisuData *data, gfloat fact);
static void onBoxExtensChanged(VisuBox *box, GParamSpec *pspec, gpointer user_data);
static void onBoxExtensActive(VisuBox *box, GParamSpec *pspec, gpointer user_data);

/* Local routines. */
static VisuBox* visu_data_getBox(VisuBoxed *self);
static gboolean visu_data_setBox(VisuBoxed *self, VisuBox *box);
static GArray* shrinkNodeList(VisuData *data, int coord, float valueTo);
static void extendNodeList(VisuData *data, int coord, float valueFrom, float valueTo);
static void _replicate(VisuData *data, gfloat extension[3]);
static gboolean _constrainedInTheBox(VisuData *data, gboolean emit);
static gboolean _constrainedFree(VisuData *data, gboolean emit);

static guint visu_data_signals[LAST_SIGNAL] = { 0 };

G_DEFINE_TYPE_WITH_CODE(VisuData, visu_data, VISU_TYPE_NODE_ARRAY,
                        G_ADD_PRIVATE(VisuData)
                        G_IMPLEMENT_INTERFACE(VISU_TYPE_BOXED,
                                              visu_boxed_interface_init)
                        G_IMPLEMENT_INTERFACE(VISU_TYPE_POINTSET,
                                              visu_pointset_interface_init))

static void visu_data_class_init(VisuDataClass *klass)
{
  DBG_fprintf(stderr, "Visu Data: creating the class of the object.\n");
  DBG_fprintf(stderr, "                - adding new signals ;\n");
  /**
   * VisuData::node-properties-added:
   * @dataObj: the object which received the signal ;
   * @values: a #VisuNodeValues object.
   *
   * Gets emitted when @values node properties is added to @dataObj.
   *
   * Since: 3.8
   */
  visu_data_signals[NODE_PROP_ADDED_SIGNAL] =
    g_signal_new("node-properties-added", G_TYPE_FROM_CLASS(klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
		 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
                 G_TYPE_NONE, 1, VISU_TYPE_NODE_VALUES, NULL);

  /**
   * VisuData::node-properties-removed:
   * @dataObj: the object which received the signal ;
   * @values: a #VisuNodeValues object.
   *
   * Gets emitted when @values node properties is removed from @dataObj.
   *
   * Since: 3.8
   */
  visu_data_signals[NODE_PROP_REMOVED_SIGNAL] =
    g_signal_new("node-properties-removed", G_TYPE_FROM_CLASS(klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
		 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
                 G_TYPE_NONE, 1, VISU_TYPE_NODE_VALUES, NULL);

  /* Connect the overloading methods. */
  G_OBJECT_CLASS(klass)->dispose      = visu_data_dispose;
  G_OBJECT_CLASS(klass)->finalize     = visu_data_finalize;
  G_OBJECT_CLASS(klass)->set_property = visu_data_set_property;
  G_OBJECT_CLASS(klass)->get_property = visu_data_get_property;

  /**
   * VisuData::description:
   *
   * Store a description for the data.
   *
   * Since: 3.8
   */
  properties[DESCR_PROP] =
    g_param_spec_string("description", "Description", "a description of the data",
                        "", G_PARAM_READWRITE);
  /**
   * VisuData::totalEnergy:
   *
   * Store the total energy of the system in eV.
   *
   * Since: 3.6
   */
  properties[TOTAL_ENERGY_PROP] =
    g_param_spec_double("totalEnergy", "Total energy", "Total energy of the system (eV)",
                        -G_MAXFLOAT, G_MAXFLOAT, G_MAXFLOAT,
                        G_PARAM_CONSTRUCT | G_PARAM_READWRITE);

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

  g_object_class_override_property(G_OBJECT_CLASS(klass), USE_TRANS_PROP, "use-translation");
  g_object_class_override_property(G_OBJECT_CLASS(klass), TRANS_PROP, "translation");
  g_object_class_override_property(G_OBJECT_CLASS(klass), RED_TRANS_PROP, "reduced-translation");
  g_object_class_override_property(G_OBJECT_CLASS(klass), MODULO_PROP, "in-the-box");
  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 = visu_data_getBox;
  iface->set_box = visu_data_setBox;
}
static void visu_pointset_interface_init(VisuPointsetInterface *iface)
{
  iface->set_inTheBox = _inTheBox;
  iface->apply_translation = _applyTranslation;
  iface->get_translation = _getTranslation;
  iface->set_translation = _setTranslation;
  iface->set_translationActive = _setTranslationActive;
}

static void visu_data_init(VisuData *obj)
{
  DBG_fprintf(stderr, "Visu Data: initializing a new object (%p).\n",
	      (gpointer)obj);
  
  obj->priv = visu_data_get_instance_private(obj);
  obj->priv->dispose_has_run = FALSE;

  /* Private data. */
  obj->priv->commentary       = g_strdup("");
  obj->priv->box              = (VisuBox*)0;
  obj->priv->unit_signal      = 0;
  obj->priv->expand_signal    = 0;
  obj->priv->extension[0]     = 0.f;
  obj->priv->extension[1]     = 0.f;
  obj->priv->extension[2]     = 0.f;
  obj->priv->inTheBox         = FALSE;
  obj->priv->inTheBox_replicate = FALSE;
  obj->priv->translationActive = FALSE;
  obj->priv->translation[0]  = 0.f;
  obj->priv->translation[1]  = 0.f;
  obj->priv->translation[2]  = 0.f;
  obj->priv->nodeProperties   = g_hash_table_new_full(g_str_hash, g_str_equal,
                                                      NULL,
                                                      (GDestroyNotify)g_object_unref);;

  /* Ensure that the base (label, ...) property exists. */
  visu_data_addNodeProperties(obj, VISU_NODE_VALUES(visu_node_values_id_new(VISU_NODE_ARRAY(obj))));
  visu_data_addNodeProperties(obj, VISU_NODE_VALUES(visu_node_values_type_new(VISU_NODE_ARRAY(obj))));
  visu_data_addNodeProperties(obj, VISU_NODE_VALUES(visu_node_values_coord_new(obj)));
  visu_data_getNodeLabels(obj);
}

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

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

  data = VISU_DATA(obj);
  if (data->priv->dispose_has_run)
    return;
  data->priv->dispose_has_run = TRUE;

  visu_data_setBox(VISU_BOXED(data), (VisuBox*)0);

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

  g_return_if_fail(obj);

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

  data = VISU_DATA(obj);

  /* Free privs elements. */
  if (data->priv)
    {
      DBG_fprintf(stderr, "Visu data: free private data.\n");
      g_free(data->priv->commentary);
      g_hash_table_destroy(data->priv->nodeProperties);
    }
  /* The free is called by g_type_free_instance... */
  /*   g_free(data); */

  /* Chain up to the parent class */
  DBG_fprintf(stderr, "Visu data: chain to parent.\n");
  G_OBJECT_CLASS(visu_data_parent_class)->finalize(obj);
  DBG_fprintf(stderr, "Visu data: freeing ... OK.\n");
}
static void visu_data_get_property(GObject* obj, guint property_id,
				   GValue *value, GParamSpec *pspec)
{
  VisuData *self = VISU_DATA(obj);
  gfloat *redTrans;

  DBG_fprintf(stderr, "Visu Data: get property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case DESCR_PROP:
      g_value_set_string(value, self->priv->commentary);
      DBG_fprintf(stderr, "%s.\n", self->priv->commentary);
      break;
    case TOTAL_ENERGY_PROP:
      g_value_set_double(value, self->priv->totalEnergy);
      DBG_fprintf(stderr, "%geV.\n", self->priv->totalEnergy);
      break;
    case USE_TRANS_PROP:
      g_value_set_boolean(value, self->priv->translationActive);
      DBG_fprintf(stderr, "%d.\n", self->priv->translationActive);
      break;
    case TRANS_PROP:
      g_value_set_static_boxed(value, self->priv->translation);
      DBG_fprintf(stderr, "%gx%gx%g.\n", self->priv->translation[0], self->priv->translation[1], self->priv->translation[2]);
      break;
    case RED_TRANS_PROP:
      redTrans = g_malloc(sizeof(gfloat) * 3);
      visu_box_convertXYZtoBoxCoordinates(self->priv->box,
                                          redTrans, self->priv->translation);
      g_value_take_boxed(value, redTrans);
      DBG_fprintf(stderr, "%gx%gx%g.\n", redTrans[0], redTrans[1], redTrans[2]);
      break;
    case MODULO_PROP:
      g_value_set_boolean(value, self->priv->inTheBox);
      DBG_fprintf(stderr, "%d.\n", self->priv->inTheBox);
      break;
    case BOX_PROP:
      g_value_set_object(value, self->priv->box);
      DBG_fprintf(stderr, "%p.\n", (gpointer)self->priv->box);
      break;
    case ADJUST_PROP:
      g_object_get_property(G_OBJECT(self->priv->box), "auto-adjust", value);
      DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}
static void visu_data_set_property(GObject* obj, guint property_id,
				   const GValue *value, GParamSpec *pspec)
{
  VisuData *self = VISU_DATA(obj);

  DBG_fprintf(stderr, "Visu Data: set property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case DESCR_PROP:
      DBG_fprintf(stderr, "%s.\n", g_value_get_string(value));
      visu_data_setDescription(self, g_value_get_string(value));
      break;
    case TOTAL_ENERGY_PROP:
      self->priv->totalEnergy = g_value_get_double(value);
      DBG_fprintf(stderr, "%geV.\n", self->priv->totalEnergy);
      break;
    case USE_TRANS_PROP:
      visu_pointset_setTranslationActive(VISU_POINTSET(self),
                                         g_value_get_boolean(value));
      DBG_fprintf(stderr, "%d.\n", self->priv->translationActive);
      break;
    case TRANS_PROP:
      visu_pointset_setTranslationPeriodic(VISU_POINTSET(self),
                                           (gfloat*)g_value_get_boxed(value),
                                           self->priv->inTheBox);
      DBG_fprintf(stderr, "%gx%gx%g.\n", self->priv->translation[0], self->priv->translation[1], self->priv->translation[2]);
      break;
    case RED_TRANS_PROP:
      visu_pointset_setBoxTranslation(VISU_POINTSET(self),
                                      (gfloat*)g_value_get_boxed(value),
                                      self->priv->inTheBox);
      DBG_fprintf(stderr, "%gx%gx%g.\n", self->priv->translation[0], self->priv->translation[1], self->priv->translation[2]);
      break;
    case MODULO_PROP:
      visu_pointset_setInTheBox(VISU_POINTSET(self), g_value_get_boolean(value));
      DBG_fprintf(stderr, "%d.\n", self->priv->inTheBox);
      break;
    case BOX_PROP:
      DBG_fprintf(stderr, "%p.\n", g_value_get_object(value));
      visu_data_setBox(VISU_BOXED(obj), VISU_BOX(g_value_get_object(value)));
      break;
    case ADJUST_PROP:
      DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
      g_object_set_property(G_OBJECT(self->priv->box), "auto-adjust", value);
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}


/**
 * visu_data_new:
 *
 * This creates an empty #VisuData object.
 *
 * Returns: a newly created #VisuData object (its ref count is set to 1).
 */
VisuData* visu_data_new(void)
{
  VisuData *data;

  DBG_fprintf(stderr, "Visu Data: create a new VisuData object of type %d.\n",
              (int)VISU_TYPE_DATA);
  data = VISU_DATA(g_object_new(VISU_TYPE_DATA, NULL));

  if (!data)
    return (VisuData*)0;

  return data;
}

/**
 * visu_data_freePopulation:
 * @data: a VisuData to be freed.
 *
 * This method frees only the allocated memory that deals with
 * the nodes (i.e. everything except the data of the files,
 * the properties and the setColor method.
 */
void visu_data_freePopulation(VisuData *data)
{
  float zeros[3] = {0.f, 0.f, 0.f};

  if (!data)
    return;

  DBG_fprintf(stderr, "Visu Data: freeing the population of VisuData %p ...\n",
              (gpointer)data);
  visu_node_array_freeNodes(VISU_NODE_ARRAY(data));

  if (data->priv->box)
    {
      visu_box_setExtension(data->priv->box, zeros);
      visu_box_setExtensionActive(data->priv->box, FALSE);
      visu_pointset_setTranslationPeriodic(VISU_POINTSET(data), zeros, FALSE);
    }

  DBG_fprintf(stderr, "Visu Data: freeing ... OK.\n");
}

/**
 * visu_data_setDescription:
 * @data: a #VisuData object ;
 * @commentary: the message to be stored (null terminated) ;
 *
 * This method is used to store a description of the given @data. This
 * string is copied and @commentary can be freed.
 */
void visu_data_setDescription(VisuData *data, const gchar* commentary)
{
  g_return_if_fail(VISU_IS_DATA(data));
  
  g_free(data->priv->commentary);
  data->priv->commentary = g_strdup(commentary);

  g_object_notify_by_pspec(G_OBJECT(data), properties[DESCR_PROP]);
}

/**
 * visu_data_getDescription:
 * @data: a #VisuData object ;
 *
 * Get the commentary associated to the given @data.
 *
 * Returns: (transfer none): a string description (possibly
 * empty). This string is own by V_Sim and should not be freed.
 */
const gchar* visu_data_getDescription(const VisuData *data)
{
  g_return_val_if_fail(VISU_IS_DATA(data), (gchar*)0);
  
  return data->priv->commentary;
}

/*************************/
/* The geometry routines */
/*************************/
static VisuBox* visu_data_getBox(VisuBoxed *self)
{
  g_return_val_if_fail(VISU_IS_DATA(self), (VisuBox*)0);

  return VISU_DATA(self)->priv->box;
}
static gboolean visu_data_setBox(VisuBoxed *self, VisuBox *box)
{
  VisuData *data;

  g_return_val_if_fail(VISU_IS_DATA(self), FALSE);
  data = VISU_DATA(self);

  if (data->priv->box == box)
    return FALSE;

  if (data->priv->box)
    {
      g_signal_handler_disconnect(G_OBJECT(data->priv->box), data->priv->unit_signal);
      g_signal_handler_disconnect(G_OBJECT(data->priv->box), data->priv->expand_signal);
      g_signal_handler_disconnect(G_OBJECT(data->priv->box), data->priv->expAct_signal);
      g_object_unref(data->priv->box);
    }
  data->priv->box = box;
  if (box)
    {
      g_object_ref(box);
      data->priv->unit_signal = 
        g_signal_connect_swapped(G_OBJECT(data->priv->box), "UnitChanged",
                                 G_CALLBACK(onBoxUnitChanged), data);
      data->priv->expand_signal = 
        g_signal_connect(G_OBJECT(data->priv->box), "notify::expansion",
                         G_CALLBACK(onBoxExtensChanged), (gpointer)data);
      data->priv->expAct_signal = 
        g_signal_connect(G_OBJECT(data->priv->box), "notify::use-expansion",
                         G_CALLBACK(onBoxExtensActive), (gpointer)data);
    }
  return TRUE;
}

static gboolean _inTheBox(VisuPointset *self, gboolean status)
{
  if (status)
    _constrainedInTheBox(VISU_DATA(self), TRUE);
  else
    _constrainedFree(VISU_DATA(self), TRUE);
  return TRUE;
}
static void _applyTranslation(VisuPointset *self)
{
  VisuNodeArrayIter iter;
  float xyz[3], zeros[3] = {0.f, 0.f, 0.f};
  gboolean rendered;

  visu_node_array_iter_new(VISU_NODE_ARRAY(self), &iter);
  for (visu_node_array_iterStart(VISU_NODE_ARRAY(self), &iter); iter.node;
       visu_node_array_iterNext(VISU_NODE_ARRAY(self), &iter))
    {
      visu_data_getNodePosition(VISU_DATA(self), iter.node, xyz);
      rendered = visu_node_getVisibility(iter.node);
      visu_node_newValues(iter.node, xyz);
      visu_node_setVisibility(iter.node, rendered);
    }
  _setTranslation(self, zeros, FALSE);
}
static void _getTranslation(VisuPointset *self, float trans[3])
{
  VisuDataPrivate *priv;

  g_return_if_fail(VISU_IS_DATA(self));
  priv = VISU_DATA(self)->priv;

  trans[0] = priv->translation[0];
  trans[1] = priv->translation[1];
  trans[2] = priv->translation[2];
}
static gboolean _setTranslation(VisuPointset *self, float trans[3], gboolean withModulo)
{
  VisuDataPrivate *priv;
  gboolean res, changed;

  g_return_val_if_fail(VISU_IS_DATA(self), FALSE);
  priv = VISU_DATA(self)->priv;

  res = FALSE;
  if (priv->translation[0] != trans[0])
    {
      priv->translation[0] = trans[0];
      res = TRUE;
    }
  if (priv->translation[1] != trans[1])
    {
      priv->translation[1] = trans[1];
      res = TRUE;
    }
  if (priv->translation[2] != trans[2])
    {
      priv->translation[2] = trans[2];
      res = TRUE;
    }
  DBG_fprintf(stderr, "Visu Data: force translation to: %f %f %f\n",
	      priv->translation[0], priv->translation[1], priv->translation[2]);
  if (res)
    g_object_notify(G_OBJECT(self), "translation");

  changed = FALSE;
  if (withModulo)
    changed = _constrainedInTheBox(VISU_DATA(self), FALSE);

  if ((res && priv->translationActive) || changed)
    g_signal_emit_by_name(G_OBJECT(self), "position-changed", (GArray*)0, NULL);

  return res;
}
static gboolean _setTranslationActive(VisuPointset *self, gboolean status)
{
  VisuData *data;
  gboolean changed;

  g_return_val_if_fail(VISU_IS_DATA(self), FALSE);

  data = VISU_DATA(self);
  if (data->priv->translationActive == status)
    return FALSE;

  data->priv->translationActive = status;
  g_object_notify(G_OBJECT(self), "use-translation");
  
  changed = FALSE;
  if (data->priv->inTheBox)
    changed = _constrainedInTheBox(VISU_DATA(self), FALSE);
  if (data->priv->translation[0] != 0.f ||
      data->priv->translation[1] != 0.f ||
      data->priv->translation[2] != 0.f ||
      changed)
    g_signal_emit_by_name(G_OBJECT(self), "position-changed", (GArray*)0, NULL);
  return TRUE;
}

/**
 * visu_data_getNodeBoxFromNumber:
 * @data: a #VisuData object.
 * @nodeId: the index of the node considered.
 * @nodeBox: (in) (array fixed-size=3): the array to store the box of the node.
 *
 * This method retrieves the value of the box associated to a node (with respect to the unit cell).
 * 
 * Returns: TRUE if everything went well, FALSE otherwise. The box is stored in the nodeBox array.
 */
gboolean visu_data_getNodeBoxFromNumber(VisuData *data, guint nodeId, int nodeBox[3])
{
  float xcart[3];

  g_return_val_if_fail(VISU_IS_DATA(data), FALSE);

  visu_data_getNodePosition(data, visu_node_array_getFromId(VISU_NODE_ARRAY(data),nodeId), xcart);
  visu_data_getNodeBoxFromCoord(data, xcart, nodeBox);
  return TRUE;
}

/**
 * visu_data_getNodeBoxFromCoord:
 * @data: a #VisuData object.
 * @xcart: (in) (array fixed-size=3): the coordinates of a node.
 * @nodeBox: (in) (array fixed-size=3): the array to store the box of the node.
 *
 * This method retrieves the value of the box associated to the coordinates of the node (with respect to the unit cell).
 * 
 * Returns: TRUE if everything went well, FALSE otherwise. The box is stored in the nodeBox array.
 */
gboolean visu_data_getNodeBoxFromCoord(VisuData *data, float xcart[3], int nodeBox[3])
{
  float xred[3];

  visu_box_convertXYZtoBoxCoordinates(data->priv->box, xred, xcart);
  nodeBox[0] = floor(xred[0]);
  nodeBox[1] = floor(xred[1]);
  nodeBox[2] = floor(xred[2]);
  DBG_fprintf(stderr, "Visu Data: nodeBox found for atom at %f %f %f : %d %d %d.\n",
		xcart[0], xcart[1], xcart[2], nodeBox[0], nodeBox[1], nodeBox[2]);
  return TRUE;
}

static void onBoxUnitChanged(VisuData *data, gfloat fact)
{
  VisuNodeArrayIter iter;

  DBG_fprintf(stderr, "Visu Data: caught 'UnitChanged' signal with factor %g.\n", fact);

  /* We do an homothety on the nodes. */
  data->priv->translation[0] *= fact;
  data->priv->translation[1] *= fact;
  data->priv->translation[2] *= fact;
  visu_node_array_iter_new(VISU_NODE_ARRAY(data), &iter);
  for( visu_node_array_iterStart(VISU_NODE_ARRAY(data), &iter); iter.node;
       visu_node_array_iterNext(VISU_NODE_ARRAY(data), &iter))
    {
      iter.node->xyz[0] *= fact;
      iter.node->xyz[1] *= fact;
      iter.node->xyz[2] *= fact;
      iter.node->translation[0] *= fact;
      iter.node->translation[1] *= fact;
      iter.node->translation[2] *= fact;
    }
  /* We raise the signals. */
  g_signal_emit_by_name(G_OBJECT(data), "position-changed", (GArray*)0, NULL);

  DBG_fprintf(stderr, "Visu Data: done 'UnitChanged'.\n");
}
static void onBoxExtensChanged(VisuBox *box, GParamSpec *pspec _U_, gpointer user_data)
{
  gfloat ext[3];

  if (!visu_box_getExtensionActive(box))
    return;

  visu_box_getExtension(box, ext);
  _replicate(VISU_DATA(user_data), ext);
}
static void onBoxExtensActive(VisuBox *box, GParamSpec *pspec _U_, gpointer user_data)
{
  VisuData *data;
  gfloat ext[3];

  if (visu_box_getExtensionActive(box))
    {
      visu_box_getExtension(box, ext);
      _replicate(VISU_DATA(user_data), ext);
    }
  else
    {
      visu_node_array_removeAllDuplicateNodes(VISU_NODE_ARRAY(user_data));
      data = VISU_DATA(user_data);
      data->priv->extension[0] = 0.f;
      data->priv->extension[1] = 0.f;
      data->priv->extension[2] = 0.f;
      if (data->priv->inTheBox_replicate)
        {
          _constrainedFree(data, TRUE);
          data->priv->inTheBox_replicate = FALSE;
        }
    }
}

static gboolean _constrainedElementInTheBox(VisuData *data, VisuElement *element,
                                            gboolean emit)
{
  gboolean changed;
  float cartCoord[3], t[3];
  VisuNodeArrayIter iter;

  g_return_val_if_fail(VISU_IS_DATA(data) && element, FALSE);

  if (!visu_element_getRendered(element))
    return FALSE;

  DBG_fprintf(stderr, "Visu Data: Checking for nodes of element '%s'"
	      " to be in the box.\n", element->name);
  changed = FALSE;
  visu_node_array_iter_new(VISU_NODE_ARRAY(data), &iter);
  iter.element = element;
  for(visu_node_array_iterRestartNode(VISU_NODE_ARRAY(data), &iter); iter.node;
      visu_node_array_iterNextNode(VISU_NODE_ARRAY(data), &iter))
    {
      visu_data_getNodePosition(data, iter.node, cartCoord);
      if (visu_box_constrainInside(data->priv->box, t, cartCoord, FALSE))
	{
	  changed = TRUE;
	  iter.node->translation[0] += t[0];
	  iter.node->translation[1] += t[1];
	  iter.node->translation[2] += t[2];
	}
    }
  if (changed && emit)
    g_signal_emit_by_name(G_OBJECT(data), "position-changed", (GArray*)0, NULL);
  return changed;
}

static gboolean _constrainedInTheBox(VisuData *data, gboolean emit)
{
  VisuNodeArrayIter iter;
  gboolean changed;

  g_return_val_if_fail(VISU_IS_DATA(data), FALSE);

  data->priv->inTheBox = TRUE;
  g_object_notify(G_OBJECT(data), "in-the-box");

  changed = FALSE;
  visu_node_array_iter_new(VISU_NODE_ARRAY(data), &iter);
  for (visu_node_array_iterStart(VISU_NODE_ARRAY(data), &iter); iter.element;
       visu_node_array_iterNextElement(VISU_NODE_ARRAY(data), &iter, FALSE))
    changed = _constrainedElementInTheBox(data, iter.element, emit) || changed;
  return changed;
}
static gboolean _constrainedFree(VisuData *data, gboolean emit)
{
  VisuNodeArrayIter iter;
  gboolean moved;

  g_return_val_if_fail(VISU_IS_DATA(data), FALSE);

  data->priv->inTheBox = FALSE;
  g_object_notify(G_OBJECT(data), "in-the-box");

  moved = FALSE;
  visu_node_array_iter_new(VISU_NODE_ARRAY(data), &iter);
  for (visu_node_array_iterStart(VISU_NODE_ARRAY(data), &iter); iter.node;
       visu_node_array_iterNext(VISU_NODE_ARRAY(data), &iter))
    {
      moved = moved || iter.node->translation[0] != 0. ||
        iter.node->translation[1] != 0. ||
        iter.node->translation[2] != 0.;
      iter.node->translation[0] = 0.;
      iter.node->translation[1] = 0.;
      iter.node->translation[2] = 0.;
    }
  if (emit && moved)
    g_signal_emit_by_name(G_OBJECT(data), "position-changed", (GArray*)0, NULL);
  return TRUE;
}

/**
 * visu_data_setTightBox:
 * @data: a #VisuData object.
 *
 * Calculate the box geometry to have a tight box in directions that
 * are not periodic. If some directions are still periodic, the box
 * size in these directions should be setup first with
 * visu_box_setGeometry().
 *
 * Returns: (transfer none): a new #VisuBox if @data had not one
 * before, or the modified box of @data.
 */
VisuBox* visu_data_setTightBox(VisuData *data)
{
  double xMin, yMin, zMin, xMax, yMax, zMax, xFree, yFree, zFree;
  double boxGeometry[6], boxGeometry_[6];
  float xyz[3];
  VisuNodeArrayIter iter;
  VisuBoxBoundaries bc;
  guint i;
  VisuBox *box;
  
  g_return_val_if_fail(VISU_IS_DATA(data), (VisuBox*)0);

  if (!data->priv->box)
    {
      for (i = 0; i < VISU_BOX_N_VECTORS; i++)
        boxGeometry_[i] = 0.;
      box = visu_box_new(boxGeometry_, VISU_BOX_FREE);
      visu_boxed_setBox(VISU_BOXED(data), VISU_BOXED(box));
      g_object_unref(box);
    }
  bc = visu_box_getBoundary(data->priv->box);
  if (bc == VISU_BOX_PERIODIC)
    return data->priv->box;

  /* Store the coordinates */
  xMin = 1e5;
  yMin = 1e5;
  zMin = 1e5;
  xMax = -1e5;
  yMax = -1e5;
  zMax = -1e5;
  xFree = (bc & TOOL_XYZ_MASK_X)?0.:1.;
  yFree = (bc & TOOL_XYZ_MASK_Y)?0.:1.;
  zFree = (bc & TOOL_XYZ_MASK_Z)?0.:1.;
  
  visu_node_array_iter_new(VISU_NODE_ARRAY(data), &iter);
  for (visu_node_array_iterStart(VISU_NODE_ARRAY(data), &iter); iter.node;
       visu_node_array_iterNext(VISU_NODE_ARRAY(data), &iter))
    {
      xMin = MIN(xMin, iter.node->xyz[0]);
      yMin = MIN(yMin, iter.node->xyz[1]);
      zMin = MIN(zMin, iter.node->xyz[2]);
      xMax = MAX(xMax, iter.node->xyz[0]);
      yMax = MAX(yMax, iter.node->xyz[1]);
      zMax = MAX(zMax, iter.node->xyz[2]);
    }

  DBG_fprintf(stderr, "Visu Data: the elements are in [%f, %f]x[%f, %f]x[%f, %f].\n",
	      xMin, xMax, yMin, yMax, zMin, zMax);
  for (i = 0; i < VISU_BOX_N_VECTORS; i++)
    boxGeometry_[i] = visu_box_getGeometry(data->priv->box, i);
  boxGeometry[0] = (xMax - xMin + 1e-5) * xFree + (1. - xFree) * boxGeometry_[0];
  boxGeometry[1] = 0.                           + (1. - yFree) * boxGeometry_[1];
  boxGeometry[2] = (yMax - yMin + 1e-5) * yFree + (1. - yFree) * boxGeometry_[2];
  boxGeometry[3] = 0.                           + (1. - zFree) * boxGeometry_[3];
  boxGeometry[4] = 0.                           + (1. - zFree) * boxGeometry_[4];
  boxGeometry[5] = (zMax - zMin + 1e-5) * zFree + (1. - zFree) * boxGeometry_[5];
  visu_box_setGeometry(data->priv->box, boxGeometry);

  xyz[0] = -xMin * xFree;
  xyz[1] = -yMin * yFree;
  xyz[2] = -zMin * zFree;
  visu_pointset_setTranslation(VISU_POINTSET(data), xyz, FALSE);
  visu_pointset_setTranslationActive(VISU_POINTSET(data), TRUE);

  return data->priv->box;
}

static void _replicate(VisuData *data, gfloat extension[3])
{
  int i;
  GArray *index;

  g_return_if_fail(VISU_IS_DATA(data));
  g_return_if_fail(extension[0] >= 0. &&
                   extension[1] >= 0. &&
                   extension[2] >= 0.);
  
  DBG_fprintf(stderr, "Visu Data: modify extension from (%g, %g, %g).\n",
	      data->priv->extension[0], data->priv->extension[1], data->priv->extension[2]);
  if (!data->priv->inTheBox)
    {
      _constrainedInTheBox(data, TRUE);
      data->priv->inTheBox_replicate = TRUE;
    }

  /* Keep only three digits for the extension to avoid rounding
     troubles. */
  extension[0] = (float)((int)(extension[0] * 1000)) / 1000;
  extension[1] = (float)((int)(extension[1] * 1000)) / 1000;
  extension[2] = (float)((int)(extension[2] * 1000)) / 1000;

  for (i = 0; i < 3; i++)
    {
      if (data->priv->extension[i] > extension[i])
	{
	  index = shrinkNodeList(data, i, extension[i]);
	  if (index->len > 0)
            visu_node_array_removeNodes(VISU_NODE_ARRAY(data), index);
          g_array_unref(index);
	}
      else if (data->priv->extension[i] < extension[i])
        extendNodeList(data, i, data->priv->extension[i], extension[i]);
      data->priv->extension[i] = extension[i];
    }
  g_object_notify(G_OBJECT(data), "n-nodes");
  if (DEBUG)
    visu_node_array_traceProperty(VISU_NODE_ARRAY(data), "originalId");
}

static GArray* shrinkNodeList(VisuData *data, int coord, float valueTo)
{
  float cartCoord[3], boxCoord[3];
  GArray *index;
  VisuNodeArrayIter iter;

  g_return_val_if_fail(coord == 0 || coord == 1 || coord == 2, FALSE);
  g_return_val_if_fail(valueTo >= 0.f, FALSE);

  DBG_fprintf(stderr, "Visu Data: shrink to %g (%d).\n", valueTo, coord);
  index = g_array_new(FALSE, FALSE, sizeof(guint));
  visu_node_array_iter_new(VISU_NODE_ARRAY(data), &iter);
  for (visu_node_array_iterStart(VISU_NODE_ARRAY(data), &iter); iter.node;
       visu_node_array_iterNext(VISU_NODE_ARRAY(data), &iter))
    {
      visu_data_getNodePosition(data, iter.node, cartCoord);
      visu_box_convertXYZtoBoxCoordinates(data->priv->box, boxCoord, cartCoord);
      if ((boxCoord[coord] < - valueTo - 1e-6 ||  /* We are out on the
						     low coord. */
	   boxCoord[coord] >= 1.f + valueTo -1e-6) && /* We are out on
							 the high
							 coord. */
	  visu_node_array_getOriginal(VISU_NODE_ARRAY(data), iter.node->number) >= 0)
	/* We remove the element. */
        g_array_append_val(index, iter.node->number);
      DBG_fprintf(stderr, "Visu Data: test shrink for %d: %d %15.12fx%15.12fx%15.12f.\n",
		  iter.node->number, index->len, boxCoord[0], boxCoord[1], boxCoord[2]);
    }
  return index;
}
static void extendNodeList(VisuData *data, int coord, float valueFrom, float valueTo)
{
  int k, id;
  unsigned nb, nbInit;
  VisuNode *newNode;
  float cartCoord[3], boxCoord[3], ratio;
  VisuNodeArrayIter iter;
  VisuBoxBoundaries bc;

  g_return_if_fail(coord == 0 || coord == 1 || coord == 2);
  g_return_if_fail(valueTo > valueFrom);

  DBG_fprintf(stderr, "Visu Data: expand in %d direction to %g.\n",
	      coord, valueTo);
  DBG_fprintf(stderr, " | k runs in [%d %d[ ]%d %d].\n", (int)floor(-valueTo),
	      -(int)valueFrom, (int)valueFrom, (int)ceil(valueTo));
  DBG_fprintf(stderr, " | keeps new ele in [%g %g] [%g %g].\n", -valueTo,
	      -valueFrom, valueFrom + 1.f, valueTo + 1.f);

  /* We estimate the number of data to be added and we call a realloc of
     this amount now to avoid to much small reallocations.
     The slab of box to be extend is 2*(valueTo-extension[coord]).
     So the volume in box coordinates is the same value and since
     the volume in box coordinates is product(1+2*extension),
     the ratio of new space is the fraction.
     So we realloc all elements on this ratio. */
  ratio = (2.f * (valueTo - valueFrom)) / (1.f + 2.f * valueFrom);
  visu_node_array_iter_new(VISU_NODE_ARRAY(data), &iter);
  for (visu_node_array_iterStart(VISU_NODE_ARRAY(data), &iter); iter.element;
       visu_node_array_iterNextElement(VISU_NODE_ARRAY(data), &iter, FALSE))
    {
      nb = (int)ceil((float)iter.nStoredNodes * ratio);
      visu_node_array_allocateNodesForElement(VISU_NODE_ARRAY(data), iter.iElement,
                                              iter.nStoredNodes + nb);
    }
  
  bc = visu_box_getBoundary(data->priv->box);

  /* All node with an id higher than nbInit are considered as new
     nodes. */
  nbInit = G_MAXUINT;
  visu_node_array_startAdding(VISU_NODE_ARRAY(data));
  for (visu_node_array_iterStartNumber(VISU_NODE_ARRAY(data), &iter); iter.node;
       visu_node_array_iterNextNodeNumber(VISU_NODE_ARRAY(data), &iter))
    {
      /* Do not duplicate the new nodes. */
      if (iter.node->number > nbInit)
	continue;
      visu_data_getNodePosition(data, iter.node, cartCoord);
      visu_box_convertXYZtoBoxCoordinates(data->priv->box, boxCoord, cartCoord);
      for (k = (int)floor(-valueTo); k < (int)ceil(valueTo) + 1; k++)
	{
	  if (k >= -(int)valueFrom && k <   (int)valueFrom + 1)
	    continue;
	  boxCoord[coord] += (float)k;
	  if ((boxCoord[coord] >= -valueTo && boxCoord[coord] < -valueFrom ) ||
	      (boxCoord[coord] < valueTo + 1.f  && boxCoord[coord] >= valueFrom + 1.f))
	    {
	      DBG_fprintf(stderr, "Visu Data: replicating node %d, (%d)"
			  " (%15.12fx%15.12fx%15.12f).\n", iter.node->number, coord,
			  boxCoord[0], boxCoord[1], boxCoord[2]);
	      /* We save the current node id, because the pointer may be
		 relocated by the visu_node_array_copyNode() call. */
	      id = iter.node->number;
	      /* We create and add a new element. */
	      newNode = visu_node_array_copyNode(VISU_NODE_ARRAY(data), iter.node);
              if (nbInit == G_MAXUINT)
                nbInit = newNode->number - 1;
	      visu_box_convertBoxCoordinatestoXYZ(data->priv->box,
                                                  newNode->xyz, boxCoord);
              if (!(bc & TOOL_XYZ_MASK_X) || data->priv->translationActive)
                newNode->xyz[0] -= data->priv->translation[0];
              if (!(bc & TOOL_XYZ_MASK_Y) || data->priv->translationActive)
                newNode->xyz[1] -= data->priv->translation[1];
              if (!(bc & TOOL_XYZ_MASK_Z) || data->priv->translationActive)
                newNode->xyz[2] -= data->priv->translation[2];
              if (data->priv->inTheBox)
                {
                  newNode->xyz[0] -= newNode->translation[0];
                  newNode->xyz[1] -= newNode->translation[1];
                  newNode->xyz[2] -= newNode->translation[2];
                }
	      /* We reset the iter.node pointer. */
	      iter.node = visu_node_array_getFromId(VISU_NODE_ARRAY(data), id);
	    }
	  boxCoord[coord] -= (float)k;
	}
    }
  visu_node_array_completeAdding(VISU_NODE_ARRAY(data));
}

/**
 * visu_data_getAllNodeExtens:
 * @dataObj: a #VisuData object.
 * @box: (allow-none): a #VisuBox object.
 *
 * Calculate the longest distance between the surface of @box (without
 * extension) and all the nodes. If @box is NULL, then the internal
 * box of @dataObj is used.
 *
 * Since: 3.7
 *
 * Returns: the longest distance between the surface of @box (without
 * extension) and all the nodes.
 **/
gfloat visu_data_getAllNodeExtens(VisuData *dataObj, VisuBox *box)
{
  VisuNodeArrayIter iter;
  float xyz[2][3], t[3], lg[2], coord[3];

  g_return_val_if_fail(VISU_IS_DATA(dataObj), 0.f);

  if (!box)
    box = dataObj->priv->box;

  t[0] = (float)(visu_box_getGeometry(box, VISU_BOX_DXX) +
		 visu_box_getGeometry(box, VISU_BOX_DYX) +
                 visu_box_getGeometry(box, VISU_BOX_DZX));
  t[1] = (float)(visu_box_getGeometry(box, VISU_BOX_DYY) +
		 visu_box_getGeometry(box, VISU_BOX_DZY));
  t[2] = (float)(visu_box_getGeometry(box, VISU_BOX_DZZ));
  xyz[0][0] = xyz[0][1] = xyz[0][2] = 0.f;
  xyz[1][0] = xyz[1][1] = xyz[1][2] = 0.f;

  visu_node_array_iter_new(VISU_NODE_ARRAY(dataObj), &iter);
  for (visu_node_array_iterStart(VISU_NODE_ARRAY(dataObj), &iter); iter.node;
       visu_node_array_iterNext(VISU_NODE_ARRAY(dataObj), &iter))
    {
      visu_data_getNodePosition(dataObj, iter.node, coord);
      xyz[0][0] = MIN(xyz[0][0], coord[0]);
      xyz[0][1] = MIN(xyz[0][1], coord[1]);
      xyz[0][2] = MIN(xyz[0][2], coord[2]);

      xyz[1][0] = MAX(xyz[1][0], coord[0]);
      xyz[1][1] = MAX(xyz[1][1], coord[1]);
      xyz[1][2] = MAX(xyz[1][2], coord[2]);
    }
  xyz[1][0] -= t[0];
  xyz[1][1] -= t[1];
  xyz[1][2] -= t[2];
  /* Compute the longest vector out of the box. */
  lg[0] = sqrt(xyz[0][0] * xyz[0][0] + 
	       xyz[0][1] * xyz[0][1] + 
	       xyz[0][2] * xyz[0][2]);
  lg[1] = sqrt(xyz[1][0] * xyz[1][0] + 
	       xyz[1][1] * xyz[1][1] + 
	       xyz[1][2] * xyz[1][2]);
  DBG_fprintf(stderr, "VisuData: vectors outside of the box %g %g.\n", lg[0], lg[1]);
  return MAX(lg[0], lg[1]);
}

/**
 * visu_data_setNewBasisFromNodes:
 * @data: a #VisuData object.
 * @nO: the index of node as origin.
 * @nA: the index of node on X axis.
 * @nB: the index of node as Y axis.
 * @nC: the index of node as Z axis.
 *
 * Change the basis set by providing the new basis set from a list of
 * nodes. See also visu_data_setNewBasis(). Nodes outside the new box
 * are killed.
 *
 * Since: 3.6
 *
 * Returns: TRUE if the new basis set is valid.
 */
gboolean visu_data_setNewBasisFromNodes(VisuData *data, guint nO, guint nA, guint nB, guint nC)
{
  VisuNode *orig, *nodeA, *nodeB, *nodeC;
  float matA[3][3], O[3], xyz[3];

  orig = visu_node_array_getFromId(VISU_NODE_ARRAY(data), nO);
  DBG_fprintf(stderr, " orig = %p\n", (gpointer)orig);
  nodeA = visu_node_array_getFromId(VISU_NODE_ARRAY(data), nA);
  DBG_fprintf(stderr, " nodeA = %p\n", (gpointer)nodeA);
  nodeB = visu_node_array_getFromId(VISU_NODE_ARRAY(data), nB);
  DBG_fprintf(stderr, " nodeB = %p\n", (gpointer)nodeB);
  nodeC = visu_node_array_getFromId(VISU_NODE_ARRAY(data), nC);
  DBG_fprintf(stderr, " nodeC = %p\n", (gpointer)nodeC);
  g_return_val_if_fail(orig && nodeA && nodeB && nodeC, FALSE);

  visu_data_getNodePosition(data, orig, O);
  visu_data_getNodePosition(data, nodeA, xyz);
  matA[0][0] = xyz[0] - O[0];
  matA[1][0] = xyz[1] - O[1];
  matA[2][0] = xyz[2] - O[2];
  visu_data_getNodePosition(data, nodeB, xyz);
  matA[0][1] = xyz[0] - O[0];
  matA[1][1] = xyz[1] - O[1];
  matA[2][1] = xyz[2] - O[2];
  visu_data_getNodePosition(data, nodeC, xyz);
  matA[0][2] = xyz[0] - O[0];
  matA[1][2] = xyz[1] - O[1];
  matA[2][2] = xyz[2] - O[2];

  return visu_data_setNewBasis(data, matA, O);
}
/**
 * visu_data_setNewBasis:
 * @data: a #VisuData object.
 * @matA: a basis set definition.
 * @O: the origin cartesian coordinates.
 *
 * Change the basis set of @data according to the new definition given
 * by @matA and @O. Nodes outside the new box are killed. See also
 * visu_data_setNewBasisFromNodes() for a convenient function using
 * nodes as basis set definition.
 * 
 * Since: 3.6
 *
 * Returns: TRUE if the new basis set is valid.
 */
gboolean visu_data_setNewBasis(VisuData *data, float matA[3][3], float O[3])
{
  double mat_[3][3];
  float inv[3][3], vect[3], xred[3];
  double box[6];
  float vectEps[3], deltaEps[3];
  VisuNodeArrayIter iter;
  GArray *rmNodes;
  float zeros[3] = {0.f, 0.f, 0.f};
#define EPS 1.e-5

  DBG_fprintf(stderr, "Visu Data: basis matrice:\n");
  DBG_fprintf(stderr, "  (%10.5f  %10.5f  %10.5f)\n",
	      matA[0][0], matA[0][1], matA[0][2]);
  DBG_fprintf(stderr, "  (%10.5f  %10.5f  %10.5f)\n",
	      matA[1][0], matA[1][1], matA[1][2]);
  DBG_fprintf(stderr, "  (%10.5f  %10.5f  %10.5f)\n",
	      matA[2][0], matA[2][1], matA[2][2]);
  if (!tool_matrix_invert(inv, matA))
    return FALSE;
  DBG_fprintf(stderr, "Visu Data: transformation matrice:\n");
  DBG_fprintf(stderr, "  (%10.5f  %10.5f  %10.5f)\n",
	      inv[0][0], inv[0][1], inv[0][2]);
  DBG_fprintf(stderr, "  (%10.5f  %10.5f  %10.5f)\n",
	      inv[1][0], inv[1][1], inv[1][2]);
  DBG_fprintf(stderr, "  (%10.5f  %10.5f  %10.5f)\n",
	      inv[2][0], inv[2][1], inv[2][2]);

  mat_[0][0] = (double)matA[0][0];
  mat_[1][0] = (double)matA[0][1];
  mat_[2][0] = (double)matA[0][2];
  mat_[0][1] = (double)matA[1][0];
  mat_[1][1] = (double)matA[1][1];
  mat_[2][1] = (double)matA[1][2];
  mat_[0][2] = (double)matA[2][0];
  mat_[1][2] = (double)matA[2][1];
  mat_[2][2] = (double)matA[2][2];
  if (!tool_matrix_reducePrimitiveVectors(box, mat_))
    return FALSE;
  DBG_fprintf(stderr, "Visu Data: new box:\n");
  DBG_fprintf(stderr, "  (%10.5f  %10.5f  %10.5f)\n",
	      box[0], box[1], box[2]);
  DBG_fprintf(stderr, "  (%10.5f  %10.5f  %10.5f)\n",
	      box[3], box[4], box[5]);

  visu_box_setBoundary(data->priv->box, VISU_BOX_PERIODIC);
  /* Trick to avoid the emission of SizeChanged signal. */
  visu_box_setMargin(data->priv->box, G_MAXFLOAT, FALSE);
  visu_box_setGeometry(data->priv->box, box);
  /* Remove possible extension. */
  g_signal_handler_block(G_OBJECT(data->priv->box), data->priv->expand_signal);
  visu_box_setExtension(data->priv->box, zeros);
  g_signal_handler_unblock(G_OBJECT(data->priv->box), data->priv->expand_signal);

  /* We need to move all the atoms of (eps, eps, eps) in the new box
     to avoid rounding problems. */
  xred[0] = 1.f;
  xred[1] = 1.f;
  xred[2] = 1.f;
  tool_matrix_productVector(vect, matA, xred);
  vectEps[0] = (vect[0] >= 0.f)?EPS:-EPS;
  vectEps[1] = (vect[1] >= 0.f)?EPS:-EPS;
  vectEps[2] = (vect[2] >= 0.f)?EPS:-EPS;
  tool_matrix_productVector(xred, inv, vectEps);
  visu_box_convertBoxCoordinatestoXYZ(data->priv->box, deltaEps, xred);
  DBG_fprintf(stderr, "Visu Data: applied epsilon (%10.5f  %10.5f  %10.5f)\n",
	      vectEps[0], vectEps[1], vectEps[2]);

  /* Transform each atomic coordinates using this matrice. */
  DBG_fprintf(stderr, "Visu Data: reset the coordinates for all nodes.\n");
  visu_node_array_iter_new(VISU_NODE_ARRAY(data), &iter);
  rmNodes = g_array_new(FALSE, FALSE, sizeof(guint));
  for (visu_node_array_iterStart(VISU_NODE_ARRAY(data), &iter); iter.node;
       visu_node_array_iterNext(VISU_NODE_ARRAY(data), &iter))
    {
      visu_data_getNodePosition(data, iter.node, vect);
      vect[0] += - O[0] + vectEps[0];
      vect[1] += - O[1] + vectEps[1];
      vect[2] += - O[2] + vectEps[2];
      tool_matrix_productVector(xred, inv, vect);
      if (xred[0] < 0.f || xred[0] >= 1.f ||
	  xred[1] < 0.f || xred[1] >= 1.f ||
	  xred[2] < 0.f || xred[2] >= 1.f)
	{
          g_array_append_val(rmNodes, iter.node->number);
	  DBG_fprintf(stderr, " | %d  (%6.1f %6.1f %6.1f)"
		      " %10.5f %10.5f %10.5f -> removed\n",
		      iter.node->number, vect[0], vect[1], vect[2],
		      xred[0], xred[1], xred[2]);
	}
      else
	{
	  visu_box_convertBoxCoordinatestoXYZ(data->priv->box, iter.node->xyz, xred);
	  iter.node->xyz[0] -= deltaEps[0];
	  iter.node->xyz[1] -= deltaEps[1];
	  iter.node->xyz[2] -= deltaEps[2];
	  iter.node->translation[0] = 0.f;
	  iter.node->translation[1] = 0.f;
	  iter.node->translation[2] = 0.f;
	  visu_node_array_setOriginal(VISU_NODE_ARRAY(data), iter.node->number);
	  DBG_fprintf(stderr, " | %d  (%6.1f %6.1f %6.1f)"
		      " %10.5f %10.5f %10.5f -> %10.5f %10.5f %10.5f\n",
		      iter.node->number, vect[0], vect[1], vect[2],
		      xred[0], xred[1], xred[2], iter.node->xyz[0],
		      iter.node->xyz[1], iter.node->xyz[2]);
	}
    }

  visu_node_array_removeNodes(VISU_NODE_ARRAY(data), rmNodes);
  g_array_free(rmNodes, TRUE);

  /* Remove possible translation. */
  visu_pointset_setTranslation(VISU_POINTSET(data), zeros, FALSE);
  visu_pointset_setTranslationActive(VISU_POINTSET(data), FALSE);
  visu_pointset_setInTheBox(VISU_POINTSET(data), FALSE);
  g_signal_emit_by_name(G_OBJECT(data), "position-changed", (GArray*)0, NULL);

  return TRUE;
}
/**
 * visu_data_reorder:
 * @data: a #VisuData object, to reorder.
 * @dataRef: a #VisuData object, to take the order from.
 *
 * This routine modifies the node ordering of @data using the order in
 * @dataRef. The association is done by nearest neigbours conditions.
 *
 * Since: 3.6
 *
 * Returns: TRUE is the reordering is successfull (i.e. all nodes of
 * @data correspond to one of @dataRef).
 */
gboolean visu_data_reorder(VisuData *data, const VisuData *dataRef)
{
  VisuNodeArrayIter iter, iterRef;
  float d, diff[3], xyz[3], dMin;
  guint id;

  g_return_val_if_fail(VISU_IS_DATA(dataRef), FALSE);
  g_return_val_if_fail(VISU_IS_DATA(data), FALSE);

  DBG_fprintf(stderr, "Geometry: reorder between %p and %p.\n",
	      (gpointer)dataRef, (gpointer)data);

  DBG_fprintf(stderr, " | %d - %d.\n", visu_node_array_getNNodes(VISU_NODE_ARRAY(data)),
              visu_node_array_getNNodes(VISU_NODE_ARRAY_CONST(dataRef)));
  if (visu_node_array_getNNodes(VISU_NODE_ARRAY(data)) !=
      visu_node_array_getNNodes(VISU_NODE_ARRAY_CONST(dataRef)))
    return FALSE;

  visu_node_array_iter_new(VISU_NODE_ARRAY(data), &iter);
  for (visu_node_array_iterStart(VISU_NODE_ARRAY(data), &iter); iter.node;
       visu_node_array_iterNext(VISU_NODE_ARRAY(data), &iter))
    {
      id = 0;
      dMin = G_MAXFLOAT;
      visu_data_getNodePosition(data, iter.node, xyz);
      visu_node_array_iter_new(VISU_NODE_ARRAY((VisuNodeArray*)dataRef), &iterRef);
      iterRef.element = iter.element;
      for (visu_node_array_iterRestartNode(VISU_NODE_ARRAY((VisuNodeArray*)dataRef), &iterRef); iterRef.node;
           visu_node_array_iterNextNode(VISU_NODE_ARRAY((VisuNodeArray*)dataRef), &iterRef))
        {
          visu_data_getNodePosition(dataRef, iterRef.node, diff);
          diff[0] -= xyz[0];
          diff[1] -= xyz[1];
          diff[2] -= xyz[2];
          visu_box_getPeriodicVector(visu_boxed_getBox(VISU_BOXED(data)), diff);
          d = diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2];
          if (d < dMin)
            {
              id = iterRef.node->number;
              dMin = d;
            }
        }
      DBG_fprintf(stderr, " | %d %d -> %g\n", iter.node->number, id, dMin);
      visu_node_array_switchNumber(VISU_NODE_ARRAY(data), iter.node->number, id);
    }
  return TRUE;
}


/*****************************/
/* The node related routines */
/*****************************/
static VisuNode* _addNode(VisuData *data, VisuNode *node,
                          float xyz[3], gboolean reduced)
{
  float coord[3];

  g_return_val_if_fail(VISU_IS_DATA(data) && node, (VisuNode*)0);
  /* If coordinates are reduced, we expand them. */
  DBG_fprintf(stderr, "Visu Data: set node coordinates from (%g;%g;%g).\n",
              xyz[0], xyz[1], xyz[2]);
  if (reduced)
    visu_box_convertBoxCoordinatestoXYZ(data->priv->box, coord, xyz);
  else
    {
      coord[0] = xyz[0];
      coord[1] = xyz[1];
      coord[2] = xyz[2];
    }

  visu_node_newValues(node, coord);

  return node;
}

/**
 * visu_data_addNodeFromIndex:
 * @data: the #VisuData where to add the new #VisuNode ;
 * @position: a integer corresponding to the position of
 *            a #VisuElement in the array **nodes in the structure;
 * @xyz: (in) (array fixed-size=3): its coordinates ;
 * @reduced: coordinates are in reduced coordinates ;
 *
 * This method adds a new #VisuNode to the specified #VisuData. Position must be
 * chosen between 0 and (ntype - 1) and corresponds to the position of the array
 * in #VisuNodeArray of a #VisuElement. If several node should be added in
 * a row, consider using visu_node_array_startAdding() and
 * visu_node_array_completeAdding().
 *
 * Returns: (transfer none): a pointer to the newly created node.
 */
VisuNode* visu_data_addNodeFromIndex(VisuData *data, guint position,
                                     float xyz[3], gboolean reduced)
{
  return _addNode(data, visu_node_array_getNewNodeForId(VISU_NODE_ARRAY(data),
                                                        position), xyz, reduced);
}

/**
 * visu_data_addNodeFromElement:
 * @data: the #VisuData where to add the new #VisuNode ;
 * @ele: the #VisuElement kind of the new #VisuNode ;
 * @xyz: (in) (array fixed-size=3): its coordinates ;
 * @reduced: coordinates are in reduced coordinates ;
 *
 * This method adds a new #VisuNode to the specified #VisuData. If
 * several node should be added in a row, consider using
 * visu_node_array_startAdding() and
 * visu_node_array_completeAdding().
 *
 * Returns: (transfer none): a pointer to the newly created node.
 */
VisuNode* visu_data_addNodeFromElement(VisuData *data, VisuElement *ele,
                                       float xyz[3], gboolean reduced)
{
  return _addNode(data, visu_node_array_getNewNode(VISU_NODE_ARRAY(data),
                                                   ele), xyz, reduced);
}
/**
 * visu_data_addNodeFromElementName:
 * @data: the #VisuData where to add the new #VisuNode ;
 * @name: the name of the element ;
 * @xyz: (in) (array fixed-size=3): its coordinates ;
 * @reduced: coordinates are in reduced coordinates ;
 *
 * This method adds a new #VisuNode to the specified #VisuData. If
 * several node should be added in a row, consider using
 * visu_node_array_startAdding() and
 * visu_node_array_completeAdding().
 *
 * Returns: (transfer none): a pointer to the newly created node.
 *
 * Since: 3.6
 */
VisuNode* visu_data_addNodeFromElementName(VisuData *data, const gchar *name,
                                           float xyz[3], gboolean reduced)
{
  return visu_data_addNodeFromElement(data, visu_element_retrieveFromName(name, (gboolean*)0), xyz, reduced);
}

/**
 * visu_data_getNodeCoordinates:
 * @data: a #VisuData object ;
 * @node: a #VisuNode object ;
 * @user: a boolean.
 * @x: (out caller-allocates): the x coordinate.
 * @y: (out caller-allocates): the y coordinate.
 * @z: (out caller-allocates): the z coordinate.
 *
 * Wrapper for the function visu_data_getNodePosition() in case of call
 * from python. If @user is TRUE, it wraps
 * visu_data_getNodeUserPosition() instead.
 *
 * Since: 3.6
 */
void visu_data_getNodeCoordinates(VisuData *data, VisuNode *node, gboolean user,
				  float *x, float *y, float *z)
{
  float xyz[3];

  g_return_if_fail(x && y && z);

  if (user)
    visu_data_getNodeUserPosition(data, node, xyz);
  else
    visu_data_getNodePosition(data, node, xyz);
  *x = xyz[0];
  *y = xyz[1];
  *z = xyz[2];
}
/**
 * visu_data_getNodePosition: (skip)
 * @data: a #VisuData object ;
 * @node: a #VisuNode object ;
 * @coord: (array fixed-size=3) (out caller-allocates): an array of 3
 * floating point values to store the position.
 *
 * Position of nodes are subject to various translations and different transformations.
 * Their coordinates should not be access directly through node.[xyz]. This method
 * is used to retrieve the given node position.
 *
 */
void visu_data_getNodePosition(const VisuData *data, const VisuNode *node, float coord[3])
{
  VisuBoxBoundaries bc;

  g_return_if_fail(VISU_IS_DATA(data) && node && coord);

  coord[0] = node->xyz[0];
  coord[1] = node->xyz[1];
  coord[2] = node->xyz[2];
  bc = (data->priv->box) ? visu_box_getBoundary(data->priv->box) : VISU_BOX_PERIODIC;
  if (!(bc & TOOL_XYZ_MASK_X) || data->priv->translationActive)
    coord[0] += data->priv->translation[0];
  if (!(bc & TOOL_XYZ_MASK_Y) || data->priv->translationActive)
    coord[1] += data->priv->translation[1];
  if (!(bc & TOOL_XYZ_MASK_Z) || data->priv->translationActive)
    coord[2] += data->priv->translation[2];
  if (data->priv->inTheBox)
    {
      coord[0] += node->translation[0];
      coord[1] += node->translation[1];
      coord[2] += node->translation[2];
    }
}
/**
 * visu_data_getNodeUserPosition: (skip)
 * @data: a #VisuData object ;
 * @node: a #VisuNode object ;
 * @coord: (array fixed-size=3) (out caller-allocates): an array of 3
 * floating point values to store the position.
 *
 * This routine is equivalent to visu_data_getNodePosition() except
 * that it's not applying internal box translation for non periodic
 * directions.
 *
 * Since: 3.7
 */
void visu_data_getNodeUserPosition(const VisuData *data, const VisuNode *node, float coord[3])
{
  VisuBoxBoundaries bc;

  g_return_if_fail(VISU_IS_DATA(data) && node && coord);

  visu_data_getNodePosition(data, node, coord);
  bc = visu_box_getBoundary(data->priv->box);
  if (!(bc & TOOL_XYZ_MASK_X))
    coord[0] -= data->priv->translation[0];
  if (!(bc & TOOL_XYZ_MASK_Y))
    coord[1] -= data->priv->translation[1];
  if (!(bc & TOOL_XYZ_MASK_Z))
    coord[2] -= data->priv->translation[2];
}


/*****************/
/* Miscellaneous */
/*****************/
/**
 * visu_data_addNodeProperties:
 * @data: a #VisuData object.
 * @values: (transfer full): a #VisuNodeValues object.
 *
 * Add @values as a known #VisuNodeValues property of @data.
 *
 * Since: 3.8
 *
 * Returns: TRUE if @values is added as a valid node property of @data.
 **/
gboolean visu_data_addNodeProperties(VisuData *data, VisuNodeValues *values)
{
  g_return_val_if_fail(VISU_IS_DATA(data), FALSE);
  g_return_val_if_fail(visu_node_values_fromArray(values, VISU_NODE_ARRAY(data)), FALSE);

  if (g_hash_table_contains(data->priv->nodeProperties,
                            visu_node_values_getLabel(values)))
    return FALSE;

  g_hash_table_insert(data->priv->nodeProperties,
                      (gpointer)visu_node_values_getLabel(values),
                      values);
  g_signal_emit(data, visu_data_signals[NODE_PROP_ADDED_SIGNAL], 0, values);

  return TRUE;
}
/**
 * visu_data_removeNodeProperties:
 * @data: a #VisuData object.
 * @label: a string.
 *
 * Look for a #VisuNodeValues object labelled by @label and remove it.
 *
 * Since: 3.8
 *
 * Returns: TRUE if @label was indeed attached to @data.
 **/
gboolean visu_data_removeNodeProperties(VisuData *data, const gchar *label)
{
  VisuNodeValues *values;

  g_return_val_if_fail(VISU_IS_DATA(data), FALSE);

  values = g_hash_table_lookup(data->priv->nodeProperties, label);
  if (!values)
    return FALSE;

  g_object_ref(values);
  g_hash_table_remove(data->priv->nodeProperties, label);
  g_signal_emit(data, visu_data_signals[NODE_PROP_REMOVED_SIGNAL], 0, values);
  g_object_unref(values);
  
  return TRUE;
}
static gint _sortProperties(const VisuNodeValues *propA, const VisuNodeValues *propB)
{
  if (VISU_IS_NODE_VALUES_ID(propA))
    return -1;
  else if (VISU_IS_NODE_VALUES_ID(propB))
    return +1;
  else if (VISU_IS_NODE_VALUES(propA))
    return -1;
  else if (VISU_IS_NODE_VALUES(propB))
    return +1;
  else if (VISU_IS_NODE_VALUES_COORD(propA))
    return -1;
  else if (VISU_IS_NODE_VALUES_COORD(propB))
    return +1;
  else
    return g_strcmp0(visu_node_values_getLabel(propA),
                     visu_node_values_getLabel(propB));
}
/**
 * visu_data_getAllNodeProperties:
 * @data: a #VisuData object.
 *
 * Retrieve all the #VisuNodeValues objects attached to @data
 * formatted as a list.
 *
 * Since: 3.8
 *
 * Returns: (transfer container) (element-type VisuNodeValues): a
 * newly created list of #VisuNodeValues objects.
 **/
GList* visu_data_getAllNodeProperties(VisuData *data)
{
  g_return_val_if_fail(VISU_IS_DATA(data), (GList*)0);

  return g_list_sort(g_hash_table_get_values(data->priv->nodeProperties),
                     (GCompareFunc)_sortProperties);
}
/**
 * visu_data_getNodeProperties:
 * @data: a #VisuData object.
 * @label: a string.
 *
 * Look for the #VisuNodeValues labelled by @label.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): the #VisuNodeValues object attached to
 * @data with @label, if any.
 **/
VisuNodeValues* visu_data_getNodeProperties(VisuData *data, const gchar *label)
{
  g_return_val_if_fail(VISU_IS_DATA(data), (VisuNodeValues*)0);

  return g_hash_table_lookup(data->priv->nodeProperties, label);
}
#define DATA_LABEL_ID _("Label")
/**
 * visu_data_getNodeLabels:
 * @data: a #VisuData object.
 *
 * Retrieve the #VisuNodeValuesString object that is used to store
 * labels, creating it if necessary.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): the #VisuNodeValuesString object used to
 * store labels.
 **/
VisuNodeValuesString* visu_data_getNodeLabels(VisuData *data)
{
  VisuNodeValues *vals;

  vals = visu_data_getNodeProperties(data, DATA_LABEL_ID);
  if (!vals)
    {
      vals = VISU_NODE_VALUES(visu_node_values_string_new(VISU_NODE_ARRAY(data), 
                                                          DATA_LABEL_ID));
      visu_data_addNodeProperties(data, vals);
    }
  return VISU_NODE_VALUES_STRING(vals);
}
/**
 * visu_data_getNodeLabelAt:
 * @data: a #VisuData object.
 * @node: a #VisuNode from @data.
 *
 * Retrieves the label associated to @node in @data.
 *
 * Since: 3.8
 *
 * Returns: a label.
 **/
const gchar* visu_data_getNodeLabelAt(const VisuData *data, const VisuNode *node)
{
  const VisuNodeValues *vals;
  
  vals = g_hash_table_lookup(data->priv->nodeProperties, DATA_LABEL_ID);
  if (!vals)
    return (const gchar*)0;

  return visu_node_values_string_getAt(VISU_NODE_VALUES_STRING(vals),
                                       node);
}

/**
 * visu_data_applyTransformationsFromCLI:
 * @data: a #VisuData object.
 * @error: an error location.
 *
 * Apply the extension and translation expressed in the command-line
 * arguments to @data.
 *
 * Since: 3.8
 *
 * Returns: TRUE on success.
 **/
gboolean visu_data_applyTransformationsFromCLI(VisuData *data, GError **error)
{
  gboolean isBox;
  float *translations, *extension;
  VisuVibration *vib;

  /* translate argument */
  translations = commandLineGet_translation(&isBox);
  if (translations && !isBox)
    visu_pointset_setTranslationPeriodic(VISU_POINTSET(data), translations, TRUE);
  else if (translations && isBox)
    visu_pointset_setBoxTranslation(VISU_POINTSET(data), translations, TRUE);
  visu_pointset_setTranslationActive(VISU_POINTSET(data),
                                     (translations != (float*)0));

  /* expand argument */
  extension = commandLineGet_extension();
  if (extension)
    visu_box_setExtension(visu_boxed_getBox(VISU_BOXED(data)), extension);
  visu_box_setExtensionActive(visu_boxed_getBox(VISU_BOXED(data)),
                              (extension != (float*)0));

  /* Vibration displacements. */
  vib = visu_data_getVibration(data, 0);
  if (commandLineGet_phononMode() >= 0 && !vib)
    g_warning(_("option '--phonon-mode' has been given but"
                " no phonons are available."));
  else if (commandLineGet_phononMode() >= 0)
    visu_vibration_setCurrentMode(vib, commandLineGet_phononMode(), error);
  if (error && *error)
    return FALSE;
  if (commandLineGet_phononTime() >= 0.f && !vib)
    g_warning(_("option '--time-opffset' has been given but"
                " no phonons are available."));
  else if (commandLineGet_phononTime() >= 0)
    visu_vibration_setTime(vib, commandLineGet_phononTime());
  if (commandLineGet_phononAmpl() >= 0.f && !vib)
    g_warning(_("option '--phonon-amplitude' has been given but"
                " no phonons are available."));
  else if (commandLineGet_phononAmpl() >= 0)
    visu_vibration_setAmplitude(vib, commandLineGet_phononAmpl());

  return TRUE;
}
