/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Luc BILLARD, Damien
	CALISTE, Olivier D'Astier, laboratoire L_Sim, (2001-2005)
  
	Adresses mél :
	BILLARD, non joignable par mél ;
	CALISTE, damien P caliste AT cea P fr.
	D'ASTIER, dastier AT iie P cnam 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 and Damien
	CALISTE and Olivier D'Astier, laboratoire L_Sim, (2001-2005)

	E-mail addresses :
	BILLARD, not reachable any more ;
	CALISTE, damien P caliste AT cea P fr.
	D'ASTIER, dastier AT iie P cnam 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 "scalarFields.h"

#include <string.h>

#include <visu_tools.h>
#include <iface_boxed.h>
#include <coreTools/toolMatrix.h>
#include <openGLFunctions/text.h>

/**
 * SECTION:scalarFields
 * @short_description:Gives capabilities to load a scalar field.
 *
 * <para>A scalar field is represented by the given of datas on a
 * regular grid meshing the bounding box. Scalar field can be read
 * from several kind of files by adding load methods using
 * scalarFieldAdd_loadMethod(). The basic implementation gives access
 * to ASCII encoded files following a simple format.</para>
 *
 * When the scalar field is periodic, the values on the border x = 1,
 * y = 1 or z = 1 can be obtained reading those of border x = 0, y = 0
 * or z = 0. So if there are n values in one direction, the nth is at
 * position 1 - 1/n in box coordinates in that direction. On the
 * contrary, for non-periodic scalar field, the nth value is at
 * coordinate 1 in box system and can be different from value 0.
 *
 * <para>In coordination with #VisuPlane and #ToolShade, scalar field can be
 * represented as coloured map calling scalarFieldDraw_map(). The
 * current implementation of interpolation is very limited since basic
 * linear approximation is used.</para>
 *
 * <para>If a structure file also contains a scalar field, when
 * loaded, it should add a #VisuData property called
 * VISU_SCALAR_FIELD_DEFINED_IN_STRUCT_FILE using
 * g_object_set_data(). Then V_Sim will be able to handle the
 * structure file as a density file also.</para>
 */

enum
  {
    PROP_0,
    LABEL_PROP,
    EMPTY_PROP,
    N_PROP,
    TRANS_PROP,
    RED_TRANS_PROP,
    USE_TRANS_PROP,
    MODULO_PROP,
    ADJUST_PROP,
    BOX_PROP
  };
static GParamSpec *_properties[N_PROP];

/*
 * _VisuScalarField:
 * @filename: the path to the file from which the scalar field
 *            has been read ;
 * @commentary: a commentary read from the file (must be in UTF-8) ;
 * @box: description of the associated bounding box ;
 * @nElements: number of points in each direction ;
 * @data: the values ;
 * @min: the minimum value ;
 * @max: the maximum value ;
 * @options: a GList of #Option values.
 *
 * The structure used to store a scalar field.
 */
struct _VisuScalarFieldPrivate
{
  gboolean dispose_has_run;

  /* The name of the file, where the data were read from. */
  gchar *filename;

  /* An associated commentary. */
  gchar *commentary;

  /* Description of the box. */
  VisuBox *box;
  float shift[3];
  gboolean adjust, inTheBox, translationActive;

  /* Number of elements in each directions [x, y, z]. */
  guint nElements[3];
  gboolean periodicity[3];
  guint sizem1[3];

  /* Mesh. */
  VisuScalarFieldMeshFlags mesh_type;
  double *mesh[3];

  /* A GList to store some options (key, values) associated
     to the data. */
  GList *options;
};

enum {
  CHANGED_SIGNAL,
  LAST_SIGNAL
};
static guint _signals[LAST_SIGNAL] = { 0 };

/* Object gestion methods. */
static void visu_scalar_field_dispose (GObject* obj);
static void visu_scalar_field_finalize(GObject* obj);
static void visu_scalar_field_get_property(GObject* obj, guint property_id,
                                           GValue *value, GParamSpec *pspec);
static void visu_scalar_field_set_property(GObject* obj, guint property_id,
                                           const GValue *value, GParamSpec *pspec);
static void visu_boxed_interface_init (VisuBoxedInterface *iface);
static void visu_pointset_interface_init(VisuPointsetInterface *iface);
static void _getGridSize(const VisuScalarField *field, guint grid[3]);
static gboolean _setGridSize(VisuScalarField *field, const guint grid[3]);
static VisuBox* _getBox(VisuBoxed *boxed);
static gboolean _setBox(VisuBoxed *self, VisuBox *box);
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);

/* Local methods. */
G_DEFINE_TYPE_WITH_CODE(VisuScalarField, visu_scalar_field, VISU_TYPE_OBJECT,
                        G_ADD_PRIVATE(VisuScalarField)
                        G_IMPLEMENT_INTERFACE(VISU_TYPE_BOXED,
                                              visu_boxed_interface_init)
                        G_IMPLEMENT_INTERFACE(VISU_TYPE_POINTSET,
                                              visu_pointset_interface_init))

static void visu_scalar_field_class_init(VisuScalarFieldClass *klass)
{
  DBG_fprintf(stderr, "VisuScalarField: creating the class of the object.\n");

  /* DBG_fprintf(stderr, "                - adding new signals ;\n"); */

  /* Connect freeing methods. */
  G_OBJECT_CLASS(klass)->dispose  = visu_scalar_field_dispose;
  G_OBJECT_CLASS(klass)->finalize = visu_scalar_field_finalize;
  G_OBJECT_CLASS(klass)->set_property = visu_scalar_field_set_property;
  G_OBJECT_CLASS(klass)->get_property = visu_scalar_field_get_property;
  VISU_SCALAR_FIELD_CLASS(klass)->getGridSize = _getGridSize;
  VISU_SCALAR_FIELD_CLASS(klass)->setGridSize = _setGridSize;

  /**
   * VisuScalarField::label:
   *
   * Holds the label (translated) that describes the scalar field.
   *
   * Since: 3.8
   */
  _properties[LABEL_PROP] =
    g_param_spec_string("label", "Label", "description label",
                        "", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
  /**
   * VisuScalarField::empty:
   *
   * TRUE if the scalarfield has not yet been set a grid size and values.
   *
   * Since: 3.8
   */
  _properties[EMPTY_PROP] =
    g_param_spec_boolean("empty", "Empty", "whether has data or not",
                         TRUE, G_PARAM_READABLE);

  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");

  /**
   * VisuScalarField::changed:
   * @field: the object which received the signal ;
   *
   * Gets emitted when the values stored in this @field are changed.
   *
   * Since: 3.8
   */
  _signals[CHANGED_SIGNAL] =
    g_signal_new("changed", G_TYPE_FROM_CLASS(klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
                 0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
                 G_TYPE_NONE, 0, NULL);
}
static void visu_boxed_interface_init(VisuBoxedInterface *iface)
{
  iface->get_box = _getBox;
  iface->set_box = _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_scalar_field_init(VisuScalarField *obj)
{
  DBG_fprintf(stderr, "VisuScalarField: creating a new scalar_field (%p).\n", (gpointer)obj);

  obj->priv = visu_scalar_field_get_instance_private(obj);
  obj->priv->dispose_has_run = FALSE;

  obj->priv->adjust       = FALSE;
  obj->priv->nElements[0] = 0;
  obj->priv->nElements[1] = 0;
  obj->priv->nElements[2] = 0;
  obj->priv->filename     = (gchar*)0;
  obj->priv->commentary   = (gchar*)0;
  obj->priv->mesh[0]        = (double*)0;
  obj->priv->mesh[1]        = (double*)0;
  obj->priv->mesh[2]        = (double*)0;
  obj->priv->box          = (VisuBox*)0;
  obj->priv->mesh_type    = VISU_SCALAR_FIELD_MESH_UNIFORM;
  obj->priv->inTheBox     = FALSE;
  obj->priv->translationActive = FALSE;
  obj->priv->shift[0]     = 0.f;
  obj->priv->shift[1]     = 0.f;
  obj->priv->shift[2]     = 0.f;
  obj->priv->options = (GList*)0;
}
static void visu_scalar_field_dispose(GObject* obj)
{
  DBG_fprintf(stderr, "VisuScalarField: dispose object %p.\n", (gpointer)obj);

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

  _setBox(VISU_BOXED(obj), (VisuBox*)0);

  /* Chain up to the parent class */
  G_OBJECT_CLASS(visu_scalar_field_parent_class)->dispose(obj);
}
static void visu_scalar_field_finalize(GObject* obj)
{
  VisuScalarFieldPrivate *field = VISU_SCALAR_FIELD(obj)->priv;

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

  g_free(field->filename);
  g_free(field->commentary);

  g_free(field->mesh[0]);
  g_free(field->mesh[1]);
  g_free(field->mesh[2]);

  g_list_free_full(field->options, (GDestroyNotify)tool_option_free);

  /* Chain up to the parent class */
  G_OBJECT_CLASS(visu_scalar_field_parent_class)->finalize(obj);
}
static void visu_scalar_field_get_property(GObject* obj, guint property_id,
                                       GValue *value, GParamSpec *pspec)
{
  VisuScalarField *self = VISU_SCALAR_FIELD(obj);

  DBG_fprintf(stderr, "Visu GlView: get property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case LABEL_PROP:
      g_value_set_string(value, self->priv->filename);
      DBG_fprintf(stderr, "%s.\n", self->priv->filename);
      break;
    case EMPTY_PROP:
      g_value_set_boolean(value, visu_scalar_field_isEmpty(self));
      DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
      break;
    case ADJUST_PROP:
      g_value_set_boolean(value, self->priv->adjust);
      DBG_fprintf(stderr, "%d.\n", self->priv->adjust);
      break;
    case BOX_PROP:
      g_value_set_object(value, self->priv->box);
      DBG_fprintf(stderr, "%p.\n", (gpointer)self->priv->box);
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}
static void visu_scalar_field_set_property(GObject* obj, guint property_id,
                                           const GValue *value, GParamSpec *pspec)
{
  VisuScalarField *self = VISU_SCALAR_FIELD(obj);

  DBG_fprintf(stderr, "Visu GlView: set property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case LABEL_PROP:
      self->priv->filename = g_value_dup_string(value);
      DBG_fprintf(stderr, "%s.\n", self->priv->filename);
      break;
    case ADJUST_PROP:
      self->priv->adjust = g_value_get_boolean(value);
      DBG_fprintf(stderr, "%d.\n", self->priv->adjust);
      break;
    case BOX_PROP:
      DBG_fprintf(stderr, "%p.\n", g_value_get_object(value));
      _setBox(VISU_BOXED(obj), VISU_BOX(g_value_get_object(value)));
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}

/**
 * visu_scalar_field_new:
 * @label: a label to identify the scalar field.
 *
 * Create a new #VisuScalarField object that is empty (all internal pointers
 * are set to NULL and no memory is allocated except for the object itself.
 * The @label argument is copied.
 *
 * Returns: (transfer full): a newly created #VisuScalarField
 * object.
 */
VisuScalarField* visu_scalar_field_new(const gchar *label)
{
  g_return_val_if_fail(label && label[0], (VisuScalarField*)0);

  return VISU_SCALAR_FIELD(g_object_new(VISU_TYPE_SCALAR_FIELD,
                                        "label", label, NULL));
}

/**
 * visu_scalar_field_isEmpty:
 * @field: a #VisuScalarField object.
 *
 * Since loading of scalar fields is asynchronous, this method can be
 * used to inquire if @field has been populated yet or not.
 *
 * Since: 3.8
 *
 * Returns: TRUE if @field has not been populated yet.
 **/
gboolean visu_scalar_field_isEmpty(const VisuScalarField *field)
{
  VisuScalarFieldClass *klass = field ? VISU_SCALAR_FIELD_GET_CLASS(field) : NULL;
  g_return_val_if_fail(klass, TRUE);

  if (klass->isEmpty)
    return klass->isEmpty(field);
  else
    return FALSE;
}

/**
 * visu_scalar_field_getCommentary:
 * @field: a #VisuScalarField object.
 *
 * If the file format support a commentary, this is a good method to get it.
 *
 * Returns: a pointer on the commentary (it should not be freed), can be NULL.
 */
const gchar* visu_scalar_field_getCommentary(VisuScalarField *field)
{
  g_return_val_if_fail(field, (gchar*)0);
  return field->priv->commentary;
}
/**
 * visu_scalar_field_setCommentary:
 * @field: a #VisuScalarField object ;
 * @comment: an UTF-8 string to store as a commentary.
 *
 * A commentary can be associated to a #VisuScalarField, use this method to set it.
 * The value of @comment is NOT copied.
 */
void visu_scalar_field_setCommentary(VisuScalarField *field, const gchar* comment)
{
  g_return_if_fail(field);
  
  field->priv->commentary = g_strdup(comment);
}
/**
 * visu_scalar_field_getMeshtype:
 * @field: a #VisuScalarField object ;
 * to be added
 *
 * The vertex may be distributed linearly along the different
 * directions or customily distributed.
 * 
 * Returns: a #VisuScalarFieldMeshFlags (uniform or nonuniform).
 */
VisuScalarFieldMeshFlags visu_scalar_field_getMeshtype(const VisuScalarField *field)
{
  g_return_val_if_fail(VISU_IS_SCALAR_FIELD(field), VISU_SCALAR_FIELD_MESH_UNIFORM);
  return field->priv->mesh_type;
}
/**
 * visu_scalar_field_setMeshtype:
 * @field: a #VisuScalarField object ;
 * @meshtype: a #VisuScalarFieldMeshFlags object.
 *
 * Change the distribution of the vertex of the scalarfield between
 * regular or custom.
 */
void visu_scalar_field_setMeshtype(VisuScalarField *field, VisuScalarFieldMeshFlags meshtype)
{
  g_return_if_fail(field);

  field->priv->mesh_type = meshtype;
}
/**
 * visu_scalar_field_getLabel:
 * @field: a #VisuScalarField object.
 *
 * The data are read from a file.
 *
 * Returns: a pointer on the filename (it should not be freed).
 */
const gchar* visu_scalar_field_getLabel(VisuScalarField *field)
{
  g_return_val_if_fail(field, (gchar*)0);
  return field->priv->filename;
}

static gboolean _setGridSize(VisuScalarField *field, const guint grid[3])
{
  g_return_val_if_fail(VISU_IS_SCALAR_FIELD(field), FALSE);

  if (field->priv->nElements[0] == grid[0] &&
      field->priv->nElements[1] == grid[1] &&
      field->priv->nElements[2] == grid[2])
    return FALSE;

  DBG_fprintf(stderr, "Visu ScalarField: changing size from (%d ; %d ; %d)"
                      " to (%d ; %d ; %d).\n", field->priv->nElements[0], field->priv->nElements[1],
                      field->priv->nElements[2], grid[0], grid[1], grid[2]);
  field->priv->nElements[0] = grid[0];
  field->priv->nElements[1] = grid[1];
  field->priv->nElements[2] = grid[2];

  DBG_fprintf(stderr, " | free the previous mesh allocation.\n");
  g_free(field->priv->mesh[0]);
  g_free(field->priv->mesh[1]);
  g_free(field->priv->mesh[2]);

  if (field->priv->mesh_type == VISU_SCALAR_FIELD_MESH_NON_UNIFORM)
    {
      field->priv->mesh[0] = g_malloc(sizeof(double) * grid[0]);
      field->priv->mesh[1] = g_malloc(sizeof(double) * grid[1]);
      field->priv->mesh[2] = g_malloc(sizeof(double) * grid[2]);
    }

  /* We change the size and reallocate mesh and data. */
  if (field->priv->box)
    {
      field->priv->sizem1[0] = (field->priv->periodicity[0]) ? grid[0] : grid[0] - 1;
      field->priv->sizem1[1] = (field->priv->periodicity[1]) ? grid[1] : grid[1] - 1;
      field->priv->sizem1[2] = (field->priv->periodicity[2]) ? grid[2] : grid[2] - 1;
    }

  return TRUE;
}
/**
 * visu_scalar_field_setGridSize:
 * @field: a #VisuScalarField object ;
 * @grid: (array fixed-size=3): 3 integers.
 *
 * This method is used to set the division in x, y, and z directions.
 * If the size of internal array for data is changed, it is reallocated and
 * previous data are erased. Use visu_scalar_field_getData() to get a pointer on this
 * data array.
 */
gboolean visu_scalar_field_setGridSize(VisuScalarField *field, const guint grid[3])
{
  VisuScalarFieldClass *klass = VISU_SCALAR_FIELD_GET_CLASS(field);
  g_return_val_if_fail(klass && klass->setGridSize, FALSE);
  
  return klass->setGridSize(field, grid);
}

/**
 * visu_scalar_field_getMeshInside:
 * @field: a #VisuScalarField object.
 * @grid: (out) (array fixed-size=3): a location to store the mesh coordinates.
 * @i: an integer.
 * @j: an integer.
 * @k: an integer.
 *
 * Apply periodicity when required to convert a mesh coordinates
 * (i,j,k) into a valid coordinate @grid.
 *
 * Since: 3.8
 **/
void visu_scalar_field_getMeshInside(const VisuScalarField *field, guint grid[3],
                                     int i, int j, int k)
{
  g_return_if_fail(VISU_IS_SCALAR_FIELD(field));
  
  if (field->priv->periodicity[0])
    grid[0] = (i < 0)?i + field->priv->sizem1[0]:i%field->priv->sizem1[0];
  else
    grid[0] = CLAMP(i, 0, (int)field->priv->sizem1[0]);
  
  if (field->priv->periodicity[1])
    grid[1] = (j < 0)?j + field->priv->sizem1[1]:j%field->priv->sizem1[1];
  else
    grid[1] = CLAMP(j, 0, (int)field->priv->sizem1[1]);

  if (field->priv->periodicity[2])
    grid[2] = (k < 0)?k + field->priv->sizem1[2]:k%field->priv->sizem1[2];
  else
    grid[2] = CLAMP(k, 0, (int)field->priv->sizem1[2]);
}
/**
 * visu_scalar_field_getCoordInside:
 * @field: a #VisuScalarField object.
 * @grid: (out) (array fixed-size=3): a location to store grid coordinates.
 * @dgrid: (out) (array fixed-size=3): a location to store grid coordinates.
 * @factor: (out) (array fixed-size=3): a location to store factors in
 * every directions.
 * @xyz: (array fixed-size=3): some cartesian coordinates.
 * @extension: (array fixed-size=3): extension to consider.
 *
 * From the coordinates @xyz, compute the grid coordinate @grid
 * closest to @xyz. Put also in @dgrid the closest grid coordinate
 * after @xyz. On output, @factor will contains three float in [0;1]
 * defining where is @xyz within the cube defined by @grid and @dgrid.
 *
 * Since: 3.8
 *
 * Returns: TRUE if @grid is valid.
 **/
gboolean visu_scalar_field_getCoordInside(const VisuScalarField *field,
                                          guint grid[3], guint dgrid[3], gfloat factor[3],
                                          const gfloat xyz[3], const gfloat extension[3])
{
  guint l, m, n;
  int nval1, nval2;
  gfloat xyz_[3], redXyz[3], pos;

  g_return_val_if_fail(VISU_IS_SCALAR_FIELD(field), FALSE);
  
  /* First, we transform the coordinates into reduced coordinates. */
  xyz_[0] = xyz[0] + field->priv->shift[0];
  xyz_[1] = xyz[1] + field->priv->shift[1];
  xyz_[2] = xyz[2] + field->priv->shift[2];
  visu_box_convertXYZtoBoxCoordinates(field->priv->box, redXyz, xyz_);

  /* We compute i, j, k. */
  for (l = 0; l < 3; l++)
    {
      /* If we are periodic and inside the extension, we put back in
	 the box. */
      if (field->priv->periodicity[l] &&
          redXyz[l] > -extension[l] && redXyz[l] < 1. + extension[l])
	redXyz[l] = tool_modulo_float(redXyz[l], 1);

      switch (field->priv->mesh_type)
        {
        case VISU_SCALAR_FIELD_MESH_UNIFORM:
          pos = (gfloat)field->priv->sizem1[l] * redXyz[l];
          grid[l] = (guint)pos;
          factor[l] = pos - (gfloat)grid[l];
          break;
        case VISU_SCALAR_FIELD_MESH_NON_UNIFORM:
          nval1 = 0;
          nval2 = field->priv->sizem1[l]-1;
	  n = 0;
          for (m = 0; m < field->priv->sizem1[l]/2; m++)
            {
              n = (int)((nval2-nval1)/2);
              if (n == 0) {
                n = nval1;
                break;    }
              else
                n = nval1+n;
              if (redXyz[l] > field->priv->mesh[l][n])
                nval1 = n;
              else
                nval2 = n;
            }
          grid[l] = n;
          factor[l] = (redXyz[l]-field->priv->mesh[l][n]) /
            (field->priv->mesh[l][n+1]-field->priv->mesh[l][n]);
          break;
        default:
          g_warning("Wrong value for 'meshtype'.");
          return FALSE;
        }
	
      if (redXyz[l] < 0. || redXyz[l] > 1.)
	return FALSE;
    }
  grid[0] = grid[0] % field->priv->sizem1[0];
  grid[1] = grid[1] % field->priv->sizem1[1];
  grid[2] = grid[2] % field->priv->sizem1[2];
  dgrid[0] = (grid[0] + 1) % field->priv->sizem1[0];
  dgrid[1] = (grid[1] + 1) % field->priv->sizem1[1];
  dgrid[2] = (grid[2] + 1) % field->priv->sizem1[2];
  return TRUE;
}
/**
 * visu_scalar_field_getMinMax:
 * @field: a #VisuScalarField object ;
 * @minmax: (array fixed-size=2): two double values.
 *
 * Get the minimum and the maximum values of the given @field.
 */
void visu_scalar_field_getMinMax(const VisuScalarField *field, double minmax[2])
{
  VisuScalarFieldClass *klass = VISU_SCALAR_FIELD_GET_CLASS(field);
  g_return_if_fail(klass && klass->getAt);
  
  klass->getMinMax(field, minmax);
}


/**
 * visu_scalar_field_getGradAt:
 * @field: a #VisuScalarField object.
 * @i: an integer.
 * @j: an integer.
 * @k: an integer.
 * @dir: a direction.
 *
 * Computes the gradient along @dir at @i, @j and @k.
 *
 * Since: 3.8
 *
 * Returns: the gradient value.
 **/
double visu_scalar_field_getGradAt(const VisuScalarField *field, int i, int j, int k, ToolXyzDir dir)
{
  int prev[3], after[3];

  g_return_val_if_fail(VISU_IS_SCALAR_FIELD(field), -1.);

  prev[0] = i;
  prev[1] = j;
  prev[2] = k;
  prev[dir] -= 1;
  after[0] = i;
  after[1] = j;
  after[2] = k;
  after[dir] += 1;
  if (field->priv->periodicity[dir])
    return (visu_scalar_field_getAt(field, after[0], after[1], after[2]) -
	    visu_scalar_field_getAt(field, prev[0], prev[1], prev[2])) / 2.;
  else
    {
      if (prev[dir] < 0 || after[dir] > (int)field->priv->sizem1[dir])
	return (visu_scalar_field_getAt(field, after[0], after[1], after[2]) -
		visu_scalar_field_getAt(field, prev[0], prev[1], prev[2]));
      else
	return (visu_scalar_field_getAt(field, after[0], after[1], after[2]) -
		visu_scalar_field_getAt(field, prev[0], prev[1], prev[2])) / 2.;
    }
}

/**
 * visu_scalar_field_getAt:
 * @field: a #VisuScalarField object.
 * @i: an integer.
 * @j: an integer.
 * @k: an integer.
 *
 * Computes the scalarfield value at @i, @j and @k.
 *
 * Since: 3.8
 *
 * Returns: the value.
 **/
double visu_scalar_field_getAt(const VisuScalarField *field, int i, int j, int k)
{
  VisuScalarFieldClass *klass = field ? VISU_SCALAR_FIELD_GET_CLASS(field) : NULL;
  g_return_val_if_fail(klass && klass->getAt, -1.);
  
  return klass->getAt(field, i, j, k);
}

/**
 * visu_scalar_field_getValue:
 * @field: a #VisuScalarField object ;
 * @xyz: (array fixed-size=3): a point coordinate (in real space) ;
 * @value: (out caller-allocates): a location to store the value ;
 * @extension: (array fixed-size=3): a possible extension in box coordinates.
 *
 * Knowing the point coordinates, it interpolate a value from the
 * scalar field. If the scalar field is periodic, then it allow the
 * coordinates to extend inside the given @extension.
 *
 * Returns: TRUE if the value can be interpolate, FALSE otherwise, for instance,
 *          when the point @xyz is out of bounds.
 */
gboolean visu_scalar_field_getValue(const VisuScalarField *field, float xyz[3],
                                    double *value, float extension[3])
{
  VisuScalarFieldClass *klass = field ? VISU_SCALAR_FIELD_GET_CLASS(field) : NULL;
  g_return_val_if_fail(klass && klass->getValue, FALSE);
  
  return klass->getValue(field, xyz, value, extension);
}

static gboolean _setBox(VisuBoxed *self, VisuBox *box)
{
  VisuScalarField *field;

  g_return_val_if_fail(VISU_IS_SCALAR_FIELD(self), FALSE);
  field = VISU_SCALAR_FIELD(self);

  DBG_fprintf(stderr, "VisuScalarField: set the bounding box to %p.\n", (gpointer)box);
  if (field->priv->box == box)
    return FALSE;

  if (field->priv->box)
    g_object_unref(field->priv->box);
  if (box)
    g_object_ref(box);
  field->priv->box = box;

  if (box)
    {
      visu_box_getPeriodicity(box, field->priv->periodicity);
      field->priv->sizem1[0] = (field->priv->periodicity[0]) ?
        field->priv->nElements[0] : field->priv->nElements[0] - 1;
      field->priv->sizem1[1] = (field->priv->periodicity[1]) ?
        field->priv->nElements[1] : field->priv->nElements[1] - 1;
      field->priv->sizem1[2] = (field->priv->periodicity[2]) ?
        field->priv->nElements[2] : field->priv->nElements[2] - 1;
    }

  return TRUE;
}
static VisuBox* _getBox(VisuBoxed *boxed)
{
  g_return_val_if_fail(VISU_IS_SCALAR_FIELD(boxed), (VisuBox*)0);

  return VISU_SCALAR_FIELD(boxed)->priv->box;
}
static gboolean _inTheBox(VisuPointset *self, gboolean status)
{
  VisuScalarField *field = VISU_SCALAR_FIELD(self);

  g_return_val_if_fail(VISU_IS_SCALAR_FIELD(self), FALSE);

  if (field->priv->inTheBox == status)
    return FALSE;

  field->priv->inTheBox = status;
  if ((field->priv->shift[0] != 0.f ||
       field->priv->shift[0] != 0.f ||
       field->priv->shift[0] != 0.f) && field->priv->translationActive)
    g_signal_emit(self, _signals[CHANGED_SIGNAL], 0);
    
  return TRUE;
}
static void _applyTranslation(VisuPointset *self _U_)
{
  g_warning("Not implemented.");
}
static void _getTranslation(VisuPointset *self, float trans[3])
{
  VisuScalarField *field;

  g_return_if_fail(VISU_IS_SCALAR_FIELD(self));
  field = VISU_SCALAR_FIELD(self);

  trans[0] = field->priv->shift[0];
  trans[1] = field->priv->shift[1];
  trans[2] = field->priv->shift[2];
}
static gboolean _setTranslation(VisuPointset *self, float trans[3],
                                gboolean withModulo _U_)
{
  VisuScalarField *field;
  gboolean res;

  g_return_val_if_fail(VISU_IS_SCALAR_FIELD(self), FALSE);
  field = VISU_SCALAR_FIELD(self);

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

  if (res && field->priv->translationActive && !visu_scalar_field_isEmpty(field))
    g_signal_emit(self, _signals[CHANGED_SIGNAL], 0);

  return res;
}
static gboolean _setTranslationActive(VisuPointset *self, gboolean status)
{
  VisuScalarField *field;

  g_return_val_if_fail(VISU_IS_SCALAR_FIELD(self), FALSE);

  field = VISU_SCALAR_FIELD(self);
  if (field->priv->translationActive == status)
    return FALSE;

  field->priv->translationActive = status;
  g_object_notify(G_OBJECT(self), "use-translation");
  
  if (field->priv->shift[0] != 0.f ||
      field->priv->shift[0] != 0.f ||
      field->priv->shift[0] != 0.f)
    g_signal_emit(self, _signals[CHANGED_SIGNAL], 0);
  
  return TRUE;
}
static void _getGridSize(const VisuScalarField *field, guint grid[3])
{
  g_return_if_fail(VISU_IS_SCALAR_FIELD(field));

  DBG_fprintf(stderr, "Visu ScalarField: get grid size.\n");
  grid[0] = field->priv->nElements[0];
  grid[1] = field->priv->nElements[1];
  grid[2] = field->priv->nElements[2];
  DBG_fprintf(stderr, " | done.\n");
}
/**
 * visu_scalar_field_getGridSize:
 * @field: a #VisuScalarField object ;
 * @grid: (out caller-allocates) (type ToolGridSize): 3 integer locations.
 *
 * This method is used to get the division in x, y, and z directions.
 */
void visu_scalar_field_getGridSize(const VisuScalarField *field, guint grid[3])
{
  VisuScalarFieldClass *klass = VISU_SCALAR_FIELD_GET_CLASS(field);
  g_return_if_fail(klass && klass->getGridSize);
  
  klass->getGridSize(field, grid);
}
/**
 * visu_scalar_field_getMesh:
 * @field: a #VisuScalarField object.
 * @dir: a direction.
 *
 * The mesh along x is stored as an array in x increasing.
 *
 * Returns: a pointer on the allocated meshx array (it should not be freed).
 */
double* visu_scalar_field_getMesh(const VisuScalarField *field, ToolXyzDir dir)
{
  g_return_val_if_fail(VISU_IS_SCALAR_FIELD(field), (double*)0);
  return field->priv->mesh[dir];
}
/**
 * visu_scalar_field_setMesh:
 * @field: a #VisuScalarField object.
 * @mesh: an array with the mesh description in one direction.
 * @dir: a direction.
 *
 * Define the mesh in case of non regular one.
 *
 * Since: 3.8
 **/
void visu_scalar_field_setMesh(VisuScalarField *field, const double *mesh, ToolXyzDir dir)
{
  g_return_if_fail(VISU_IS_SCALAR_FIELD(field));

  g_return_if_fail(field->priv->mesh_type == VISU_SCALAR_FIELD_MESH_NON_UNIFORM);
  g_return_if_fail(field->priv->mesh[dir]);

  memcpy(field->priv->mesh[dir], mesh, sizeof(double) * field->priv->nElements[dir]);
}

/**
 * visu_scalar_field_getAllOptions:
 * @field: a #VisuScalarField object.
 *
 * Some #Option can be stored in association to the values of the scalar field.
 * These options are usually values associated to the read data, such as
 * a spin direction when dealing with density of spin...
 *
 * Returns: (transfer container) (element-type ToolOption*): a newly
 * created GList that should be freed after use with
 * g_list_free(). But data of the list are owned by V_Sim and should
 * not be modified or freed.
 */
GList* visu_scalar_field_getAllOptions(VisuScalarField *field)
{
  g_return_val_if_fail(VISU_IS_SCALAR_FIELD(field), (GList*)0);
  
  return g_list_copy(field->priv->options);
}
/**
 * visu_scalar_field_addOption:
 * @field: a #VisuScalarField object ;
 * @option: a newly allocated option.
 *
 * This method adds an option to the list of #Option associated to the data. The given
 * @option will not be duplicated and should not be used elsewhere because it will be freed
 * when the @field will be freed.
 */
void visu_scalar_field_addOption(VisuScalarField *field, ToolOption *option)
{
  g_return_if_fail(VISU_IS_SCALAR_FIELD(field) && option);
  
  field->priv->options = g_list_append(field->priv->options, (gpointer)option);
}
