/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Damien
	CALISTE, laboratoire L_Sim, (2016)
  
	Adresse 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 : Damien
	CALISTE, laboratoire L_Sim, (2016)

	E-mail address:
	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 <string.h>

#include "visu_glnodescene.h"

#include "extraFunctions/geometry.h"
#include "extraFunctions/scalarFieldSet.h"
#include "extraFunctions/shellProp.h"
#include "extraFunctions/idProp.h"
#include "extraFunctions/typeProp.h"
#include "extraFunctions/pot2surf.h"
#include "extraFunctions/vibration.h"

#include "dumpModules/fileDump.h"
#include "dumpModules/glDump.h"

#include "iface_boxed.h"
#include "iface_animatable.h"
#include "visu_configFile.h"
#include "visu_dataspin.h"
#include "extensions/paths.h"
#include "extensions/infos.h"
#include "visu_commandLine.h"
#include "renderingMethods/spinMethod.h"

/**
 * SECTION:visu_glnodescene
 * @short_description: Defines a storage object to handle all
 * #VisuGlExt used to render nodes and associated data.
 *
 * <para></para>
 */

#define FLAG_PARAMETER_FILEEXT   "dataFile_fileExtension"
#define DESC_PARAMETER_FILEEXT   "The extension used for data file ; chain e.g. '.dat'"
static gchar *fileExt = NULL;

struct _VisuGlNodeScenePrivate
{
  gboolean dispose_has_run;

  VisuBox *prevBox;
  gulong pos_sig, mar_sig, popMar_sig, popInc_sig, box_sig, vis_sig, mask_sig;
  gulong load_sig, mess_sig, vib_sig;
  GBinding *bind_theta, *bind_phi, *bind_omega;
  GList *maskers, *movers;
  VisuDataColorizerFragment *shellColorizer;
  VisuDataColorizerFragment *fragColorizer;
  VisuColorization *dt;
  gulong single_sig, active_sig, colorizer_sig;
  GBinding *bind_shade, *vib_bind;

  VisuGlView *view;
  gulong view_anim;

  /* Rendered extensions. */
  VisuGlExtNodes      *nodes;
  VisuGlExtMarks      *marks;
  VisuGlExtPairs      *pairs;
  VisuGlExtBg         *bg;
  VisuGlExtAxes       *axes;
  VisuGlExtBox        *box;
  VisuGlExtBoxLegend  *boxLegend;
  VisuGlExtLegend     *legend;
  VisuGlExtPlanes     *planes;
  VisuGlExtSurfaces   *surfaces;
  VisuGlExtInfos      *infos;
  VisuGlExtScale      *scales;
  VisuGlExtForces     *forces;
  VisuGlExtGeodiff    *diff;
  VisuGlExtVibrations *vibrations;
  VisuGlExtMapSet     *maps;
  VisuGlExtShade      *mapLegend;
  VisuGlExtPaths      *extPaths;
  VisuGlExtShade      *colorizationLegend;
  gulong planes_anim;

  VisuPaths *paths;
  gboolean reorder, record;

  gboolean fitToBox;

  gboolean axesFollowBox;

  VisuGlNodeSceneColorizationPolicies dtPolicy;
  gchar *dtFileExtension;

  /* Animations */
  GTimer *timer;
  guint tickId;
  GList *animations;
};

enum
  {
    PROP_0,
    DATA_PROP,
    VIEW_PROP,
    DIFF_PROP,
    REORDER_PROP,
    PATH_PROP,
    RECORD_PROP,
    PATH_LENGTH_PROP,
    PATH_SHADE_PROP,
    LOADING_PROP,
    LOADMESS_PROP,
    AXES_BOX_PROP,
    AUTO_COLORIZATION_PROP,
    SCHEME_COLORIZATION_PROP,
    SHELL_COLORIZATION_PROP,
    N_PROP,
    ADJUST_PROP,
    BOX_PROP
  };
static GParamSpec *_properties[N_PROP];

static void visu_gl_node_scene_dispose     (GObject* obj);
static void visu_gl_node_scene_finalize    (GObject* obj);
static void visu_gl_node_scene_get_property(GObject* obj, guint property_id,
                                            GValue *value, GParamSpec *pspec);
static void visu_gl_node_scene_set_property(GObject* obj, guint property_id,
                                            const GValue *value, GParamSpec *pspec);
static void visu_gl_node_scene_added(VisuGlExtSet *self, VisuGlExt *ext);
static void visu_gl_node_scene_removed(VisuGlExtSet *self, VisuGlExt *ext);
static VisuBox* _getBox(VisuBoxed *self);
static gboolean _setBox(VisuBoxed *self, VisuBox *box);
static void visu_boxed_interface_init(VisuBoxedInterface *iface);

static void _applyMaskers(VisuGlNodeScene *scene);
static void _ensureBoxMargin(VisuGlNodeScene *scene);
static void _propagateBox(VisuGlNodeScene *scene);
static void _applyColorizationPolicy(VisuGlNodeScene *scene);
static gboolean _onAnimate(VisuGlNodeScene *scene, VisuAnimation *anim,
                           const GValue *to, gulong duration, gboolean loop,
                           VisuAnimationType type);
static void exportParameters(GString *data, VisuData* dataObj);
static VisuData* _getData(VisuGlNodeScene *self);

struct _masker
{
  VisuNodeMasker *masker;
  gulong dirty_sig;
};
struct _mover
{
  VisuNodeMover *mover;
  gulong animate_sig;
  GBinding *bind;
};

/* Local callbacks */
static void onEntryScale(VisuGlNodeScene *scene, VisuConfigFileEntry *entry, VisuConfigFile *obj);
static void onDiffActive(VisuGlNodeScene *scene, GParamSpec *pspec, VisuGlExtGeodiff *diff);
static void onPathActive(VisuGlNodeScene *scene, GParamSpec *pspec, VisuGlExtPaths *paths);
static void onLoading(VisuGlNodeScene *scene, GParamSpec *pspec, VisuDataLoadable *data);
static void onLoadingMessage(VisuGlNodeScene *scene, GParamSpec *pspec, VisuDataLoadable *data);
static void _freeMasker(struct _masker *str);
static void _freeMover(struct _mover *str);

G_DEFINE_TYPE_WITH_CODE(VisuGlNodeScene, visu_gl_node_scene, VISU_TYPE_GL_EXT_SET,
                        G_ADD_PRIVATE(VisuGlNodeScene)
                        G_IMPLEMENT_INTERFACE(VISU_TYPE_BOXED,
                                              visu_boxed_interface_init))

static void visu_gl_node_scene_class_init(VisuGlNodeSceneClass *klass)
{
  VisuConfigFileEntry *resourceEntry;

  DBG_fprintf(stderr, "Visu Node Scene: creating the class of the object.\n");
  /* DBG_fprintf(stderr, "                - adding new signals ;\n"); */

  /* Connect the overloading methods. */
  G_OBJECT_CLASS(klass)->dispose  = visu_gl_node_scene_dispose;
  G_OBJECT_CLASS(klass)->finalize = visu_gl_node_scene_finalize;
  G_OBJECT_CLASS(klass)->set_property = visu_gl_node_scene_set_property;
  G_OBJECT_CLASS(klass)->get_property = visu_gl_node_scene_get_property;
  VISU_GL_EXT_SET_CLASS(klass)->added = visu_gl_node_scene_added;
  VISU_GL_EXT_SET_CLASS(klass)->removed = visu_gl_node_scene_removed;
  
  /**
   * VisuGlNodeScene::data:
   *
   * Store the data object.
   *
   * Since: 3.8
   */
  _properties[DATA_PROP] = g_param_spec_object("data", "Data",
                                               "storing data for nodes",
                                               VISU_TYPE_DATA, G_PARAM_READWRITE);
  /**
   * VisuGlNodeScene::view:
   *
   * Store the #VisuGlView used to render the scene.
   *
   * Since: 3.8
   */
  _properties[VIEW_PROP] = g_param_spec_object("view", "View",
                                               "storing the view to represent the data",
                                               VISU_TYPE_GL_VIEW, G_PARAM_READABLE);
  /**
   * VisuGlNodeScene::geometry-differences:
   *
   * If the scene displays arrows to represent geometry differences
   * between two #VisuData.
   *
   * Since: 3.8
   */
  _properties[DIFF_PROP] = g_param_spec_boolean("geometry-differences", "Geometry differences",
                                                "display arrows to represent differences",
                                                FALSE, G_PARAM_READWRITE);
  /**
   * VisuGlNodeScene::reorder-reference:
   *
   * If the reference #VisuData is reordered before doing geometry differences.
   *
   * Since: 3.8
   */
  _properties[REORDER_PROP] = g_param_spec_boolean("reorder-reference", "Reorder reference",
                                                   "reorder reference data before comparing",
                                                   FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
  /**
   * VisuGlNodeScene::path-active:
   *
   * If the differences between #VisuData is displayed.
   *
   * Since: 3.8
   */
  _properties[PATH_PROP] = g_param_spec_boolean("path-active", "Path is active",
                                                "display stored paths",
                                                FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
  /**
   * VisuGlNodeScene::record-path:
   *
   * If the differences between #VisuData is stored.
   *
   * Since: 3.8
   */
  _properties[RECORD_PROP] = g_param_spec_boolean("record-path", "Record path",
                                                  "save differences between data",
                                                  FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
  /**
   * VisuGlNodeScene::path-length:
   *
   * Store the length of the current path.
   *
   * Since: 3.8
   */
  _properties[PATH_LENGTH_PROP] = g_param_spec_uint("path-length", "Path length",
                                                    "store the path length",
                                                    0, G_MAXUINT, 0, G_PARAM_READABLE);
  /**
   * VisuGlNodeScene::path-shade:
   *
   * Store a shade to colourize the path.
   *
   * Since: 3.8
   */
  _properties[PATH_SHADE_PROP] = g_param_spec_boxed("path-shade", "Path shade",
                                                    "store the path shade",
                                                    TOOL_TYPE_SHADE, G_PARAM_READWRITE);
  /**
   * VisuGlNodeScene::loading:
   *
   * Flag signaling that #VisuData is laoding or not.
   *
   * Since: 3.8
   */
  _properties[LOADING_PROP] = g_param_spec_boolean("loading", "Loading",
                                                   "flag indicating that data are loading.",
                                                   FALSE, G_PARAM_READABLE);
  /**
   * VisuGlNodeScene::loading-message:
   *
   * Message to be informed what happens during loding.
   *
   * Since: 3.8
   */
  _properties[LOADMESS_PROP] = g_param_spec_string("loading-message", "Loading message",
                                                   "message when loading.",
                                                   "", G_PARAM_READABLE);
  /**
   * VisuGlNodeScene::axes-from-box:
   *
   * Flag for the axes to be orthorombic or following the #VisuData super-cell.
   *
   * Since: 3.8
   */
  _properties[AXES_BOX_PROP] = g_param_spec_boolean("axes-from-box", "Axes from box",
                                                    "axes follow the data super-cell.",
                                                    FALSE, G_PARAM_READWRITE);
  /**
   * VisuGlNodeScene::colorization-policy:
   *
   * Flag to automatically load colourisation data.
   *
   * Since: 3.8
   */
  _properties[AUTO_COLORIZATION_PROP] =
    g_param_spec_uint("colorization-policy", "Colorization policy",
                      "colorization load policy.", 0, COLORIZATION_N_POLICIES - 1,
                      COLORIZATION_POLICY_FROM_PREVIOUS, G_PARAM_READWRITE);
  /**
   * VisuGlNodeScene::colorization-file-extension:
   *
   * File extension used to discover colourisation file from data file.
   *
   * Since: 3.8
   */
  _properties[SCHEME_COLORIZATION_PROP] =
    g_param_spec_string("colorization-file-extension", "Colorization file extension",
                        "extension used to discover colourisation file.",
                        ".dat", G_PARAM_READWRITE);
  /**
   * VisuGlNodeScene::shell-colorizer:
   *
   * Object used to colorize neighbouring shells.
   *
   * Since: 3.8
   */
  _properties[SHELL_COLORIZATION_PROP] =
    g_param_spec_object("shell-colorizer", "Shell colorizer object",
                        "used to colorize neighbouring shells.",
                        VISU_TYPE_DATA_COLORIZER_FRAGMENT, G_PARAM_READABLE);

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

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

  resourceEntry = visu_config_file_addStringEntry(VISU_CONFIG_FILE_PARAMETER,
                                                  FLAG_PARAMETER_FILEEXT,
                                                  DESC_PARAMETER_FILEEXT,
                                                  &fileExt);
  visu_config_file_entry_setVersion(resourceEntry, 3.4f);
  visu_config_file_addExportFunction(VISU_CONFIG_FILE_PARAMETER,
                                     exportParameters);
}
static void visu_boxed_interface_init(VisuBoxedInterface *iface)
{
  iface->get_box = _getBox;
  iface->set_box = _setBox;
}

static void visu_gl_node_scene_init(VisuGlNodeScene *self)
{
  float zeros[3] = {0.f, 0.f, 0.f};
  VisuGlExtSet *set;
#define SET(V, I) self->priv->V = I; visu_gl_ext_set_add(set, VISU_GL_EXT(self->priv->V))

  DBG_fprintf(stderr, "Visu Node Scene: initializing a new object (%p).\n",
	      (gpointer)self);
  self->priv = visu_gl_node_scene_get_instance_private(self);
  self->priv->dispose_has_run = FALSE;

  self->priv->prevBox = (VisuBox*)0;
  self->priv->fragColorizer = (VisuDataColorizerFragment*)0;
  self->priv->shellColorizer = (VisuDataColorizerFragment*)0;
  self->priv->dt = (VisuColorization*)0;
  self->priv->view = visu_gl_view_new();
  self->priv->view_anim = g_signal_connect_swapped(self->priv->view, "animate",
                                                   G_CALLBACK(_onAnimate), self);

  set = VISU_GL_EXT_SET(self);
  SET(nodes, visu_gl_ext_nodes_new());
  SET(marks, visu_gl_ext_marks_new((const gchar*)0));
  visu_gl_ext_set_add(set, visu_gl_ext_marks_getInternalList(self->priv->marks));
  SET(pairs, visu_gl_ext_pairs_new((const gchar*)0));
  SET(bg, visu_gl_ext_bg_new((const gchar*)0));
  SET(axes, visu_gl_ext_axes_new((const gchar*)0));
  SET(box, visu_gl_ext_box_new((const gchar*)0));
  SET(boxLegend, visu_gl_ext_box_legend_new((const gchar*)0));
  SET(legend, visu_gl_ext_legend_new((const gchar*)0));
  SET(infos, visu_gl_ext_infos_new((const gchar*)0));
  SET(scales, visu_gl_ext_scale_new((const gchar*)0));
  SET(forces, visu_gl_ext_forces_new((const gchar*)0));
  SET(diff, visu_gl_ext_geodiff_new((const gchar*)0));
  SET(vibrations, visu_gl_ext_vibrations_new((const gchar*)0));
  SET(extPaths, visu_gl_ext_paths_new((const gchar*)0));

  /* Optional extensions. */
  self->priv->planes = (VisuGlExtPlanes*)0;
  self->priv->surfaces = (VisuGlExtSurfaces*)0;
  self->priv->maps = (VisuGlExtMapSet*)0;
  self->priv->mapLegend = (VisuGlExtShade*)0;
  self->priv->colorizationLegend = (VisuGlExtShade*)0;

  visu_gl_ext_pairs_setDataRenderer(self->priv->pairs, VISU_NODE_ARRAY_RENDERER(self->priv->nodes));
  visu_gl_ext_legend_setNodes(self->priv->legend, VISU_NODE_ARRAY_RENDERER(self->priv->nodes));
  visu_gl_ext_infos_setDataRenderer(self->priv->infos, VISU_NODE_ARRAY_RENDERER(self->priv->nodes));
  visu_gl_ext_marks_setDataRenderer(self->priv->marks, VISU_NODE_ARRAY_RENDERER(self->priv->nodes));
  visu_gl_ext_set_setGlView(set, self->priv->view);
  g_signal_connect_swapped(self->priv->nodes, "notify::max-element-size",
                           G_CALLBACK(_ensureBoxMargin), self);
  g_signal_connect_swapped(VISU_CONFIG_FILE_RESOURCE, "parsed::scale_definition",
                           G_CALLBACK(onEntryScale), self);
  g_signal_connect_swapped(self->priv->diff, "notify::active",
                           G_CALLBACK(onDiffActive), self);
  visu_gl_ext_setActive(VISU_GL_EXT(self->priv->diff), FALSE);
  g_signal_connect_swapped(self->priv->extPaths, "notify::active",
                           G_CALLBACK(onPathActive), self);
  visu_gl_ext_setActive(VISU_GL_EXT(self->priv->extPaths), FALSE);
  visu_gl_ext_setActive(VISU_GL_EXT(self->priv->infos), FALSE);

  self->priv->paths = visu_paths_new(zeros);
  visu_gl_ext_paths_set(self->priv->extPaths, self->priv->paths);

  self->priv->axesFollowBox = FALSE;
  self->priv->dtPolicy = COLORIZATION_POLICY_FROM_PREVIOUS;
  self->priv->dtFileExtension = g_strdup(fileExt ? fileExt : ".dat");

  self->priv->maskers = (GList*)0;
  visu_gl_node_scene_addMasker(self, VISU_NODE_MASKER(self->priv->marks));

  self->priv->movers = (GList*)0;

  self->priv->timer = g_timer_new();
  self->priv->animations = (GList*)0;
  self->priv->tickId = 0;
}

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

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

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

  visu_gl_node_scene_setData(ext, (VisuData*)0);

  if (ext->priv->fragColorizer)
    g_object_unref(ext->priv->fragColorizer);
  if (ext->priv->shellColorizer)
    g_object_unref(ext->priv->shellColorizer);

  if (ext->priv->dt)
    g_object_unref(ext->priv->dt);

  g_signal_handler_disconnect(ext->priv->view, ext->priv->view_anim);
  g_object_unref(ext->priv->view);

  g_object_unref(ext->priv->nodes);
  g_object_unref(ext->priv->marks);
  g_object_unref(ext->priv->pairs);
  g_object_unref(ext->priv->bg);
  g_object_unref(ext->priv->axes);
  g_object_unref(ext->priv->box);
  g_object_unref(ext->priv->boxLegend);
  g_object_unref(ext->priv->legend);
  g_object_unref(ext->priv->infos);
  g_object_unref(ext->priv->scales);
  g_object_unref(ext->priv->forces);
  g_object_unref(ext->priv->diff);
  g_object_unref(ext->priv->vibrations);
  g_object_unref(ext->priv->extPaths);
  if (ext->priv->planes)
    {
      g_signal_handler_disconnect(ext->priv->planes->planes, ext->priv->planes_anim);
      g_object_unref(ext->priv->planes);
    }
  if (ext->priv->surfaces)
    g_object_unref(ext->priv->surfaces);
  if (ext->priv->maps)
    g_object_unref(ext->priv->maps);
  if (ext->priv->mapLegend)
    g_object_unref(ext->priv->mapLegend);
  if (ext->priv->colorizationLegend)
    g_object_unref(ext->priv->colorizationLegend);
  if (ext->priv->paths)
    visu_paths_unref(ext->priv->paths);

  g_list_free_full(ext->priv->maskers, (GDestroyNotify)_freeMasker);
  g_list_free_full(ext->priv->movers, (GDestroyNotify)_freeMover);

  if (ext->priv->tickId)
    g_source_remove(ext->priv->tickId);
  g_list_free_full(ext->priv->animations, g_object_unref);

  /* Chain up to the parent class */
  DBG_fprintf(stderr, "Visu Node Scene: chain up to parent.\n");
  G_OBJECT_CLASS(visu_gl_node_scene_parent_class)->dispose(obj);
  DBG_fprintf(stderr, "Visu Node Scene: dispose ... OK.\n");
}
static void visu_gl_node_scene_finalize(GObject* obj)
{
  VisuGlNodeScene *ext;

  ext = VISU_GL_NODE_SCENE(obj);
  g_free(ext->priv->dtFileExtension);
  g_timer_destroy(ext->priv->timer);

  G_OBJECT_CLASS(visu_gl_node_scene_parent_class)->finalize(obj);
}
static void visu_gl_node_scene_get_property(GObject* obj, guint property_id,
                                            GValue *value, GParamSpec *pspec)
{
  VisuGlNodeScene *self = VISU_GL_NODE_SCENE(obj);
  VisuData *data;

  DBG_fprintf(stderr, "Visu Node Scene: get property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case DATA_PROP:
      g_value_set_object(value, _getData(self));
      DBG_fprintf(stderr, "%p.\n", g_value_get_object(value));
      break;
    case VIEW_PROP:
      g_value_set_object(value, self->priv->view);
      DBG_fprintf(stderr, "%p.\n", (gpointer)self->priv->view);
      break;
    case DIFF_PROP:
      g_value_set_boolean(value, visu_gl_ext_getActive(VISU_GL_EXT(self->priv->diff)));
      DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
      break;
    case REORDER_PROP:
      g_value_set_boolean(value, self->priv->reorder);
      DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
      break;
    case RECORD_PROP:
      g_value_set_boolean(value, self->priv->record);
      DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
      break;
    case PATH_PROP:
      g_value_set_boolean(value, visu_gl_ext_getActive(VISU_GL_EXT(self->priv->extPaths)));
      DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
      break;
    case PATH_LENGTH_PROP:
      g_value_set_uint(value, (self->priv->paths) ? visu_paths_getLength(self->priv->paths) : 0);
      DBG_fprintf(stderr, "%d.\n", g_value_get_uint(value));
      break;
    case PATH_SHADE_PROP:
      g_value_set_boxed(value, (self->priv->paths) ? visu_paths_getToolShade(self->priv->paths) : (gconstpointer)0);
      DBG_fprintf(stderr, "%p.\n", g_value_get_boxed(value));
      break;
    case LOADING_PROP:
      data = _getData(self);
      if (data && VISU_IS_DATA_LOADABLE(data))
        g_object_get_property(G_OBJECT(data), "loading", value);
      else
        g_value_set_boolean(value, FALSE);
      DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
      break;
    case LOADMESS_PROP:
      data = _getData(self);
      if (data && VISU_IS_DATA_LOADABLE(data))
        g_object_get_property(G_OBJECT(data), "status", value);
      else
        g_value_set_static_string(value, (const gchar*)0);
      DBG_fprintf(stderr, "%s.\n", g_value_get_string(value));
      break;
    case AXES_BOX_PROP:
      g_value_set_boolean(value, self->priv->axesFollowBox);
      DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
      break;
    case BOX_PROP:
      g_value_set_object(value, _getBox(VISU_BOXED(self)));
      DBG_fprintf(stderr, "%p.\n", g_value_get_object(value));
      break;
    case ADJUST_PROP:
      g_value_set_boolean(value, self->priv->fitToBox);
      DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
      break;
    case AUTO_COLORIZATION_PROP:
      g_value_set_uint(value, self->priv->dtPolicy);
      DBG_fprintf(stderr, "%d.\n", g_value_get_uint(value));
      break;
    case SCHEME_COLORIZATION_PROP:
      g_value_set_string(value, self->priv->dtFileExtension);
      DBG_fprintf(stderr, "%s.\n", g_value_get_string(value));
      break;
    case SHELL_COLORIZATION_PROP:
      g_value_set_object(value, self->priv->shellColorizer);
      DBG_fprintf(stderr, "%p.\n", 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;
    }
}
static void visu_gl_node_scene_set_property(GObject* obj, guint property_id,
                                         const GValue *value, GParamSpec *pspec)
{
  VisuGlNodeScene *self = VISU_GL_NODE_SCENE(obj);
  VisuBox *box;

  DBG_fprintf(stderr, "Visu Node Scene: set property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case DATA_PROP:
      DBG_fprintf(stderr, "%p\n", g_value_get_object(value));
      visu_gl_node_scene_setData(self, VISU_DATA(g_value_get_object(value)));
      break;
    case DIFF_PROP:
      DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
      visu_gl_ext_setActive(VISU_GL_EXT(self->priv->diff), g_value_get_boolean(value));
      break;
    case REORDER_PROP:
      DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
      self->priv->reorder = g_value_get_boolean(value);
      break;
    case RECORD_PROP:
      DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
      self->priv->record = g_value_get_boolean(value);
      if (self->priv->paths && self->priv->record)
        visu_paths_pinPositions(self->priv->paths, _getData(self));
      break;
    case PATH_PROP:
      DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
      visu_gl_ext_setActive(VISU_GL_EXT(self->priv->extPaths), g_value_get_boolean(value));
      break;
    case PATH_SHADE_PROP:
      DBG_fprintf(stderr, "%p.\n", g_value_get_boxed(value));
      if (self->priv->paths)
        {
          visu_paths_setToolShade(self->priv->paths, (ToolShade*)g_value_get_boxed(value));
          visu_gl_ext_setDirty(VISU_GL_EXT(self->priv->extPaths), TRUE);
        }
      break;
    case AXES_BOX_PROP:
      DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
      self->priv->axesFollowBox = g_value_get_boolean(value);
      visu_gl_ext_axes_setBasisFromBox(self->priv->axes, (self->priv->axesFollowBox) ?
                                       _getBox(VISU_BOXED(self)): (VisuBox*)0);
      break;
    case BOX_PROP:
      DBG_fprintf(stderr, "%p.\n", g_value_get_object(value));
      _setBox(VISU_BOXED(self), VISU_BOX(g_value_get_object(value)));
      break;
    case ADJUST_PROP:
      DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
      self->priv->fitToBox = g_value_get_boolean(value);
      box = _getBox(VISU_BOXED(self));
      if (self->priv->surfaces && box && self->priv->fitToBox)
        visu_gl_ext_surfaces_setFittingBox(self->priv->surfaces, box);
      break;
    case AUTO_COLORIZATION_PROP:
      DBG_fprintf(stderr, "%d.\n", g_value_get_uint(value));
      visu_gl_node_scene_setColorizationPolicy(self, g_value_get_uint(value));
      break;
    case SCHEME_COLORIZATION_PROP:
      DBG_fprintf(stderr, "%s.\n", g_value_get_string(value));
      g_free(self->priv->dtFileExtension);
      self->priv->dtFileExtension = g_value_dup_string(value);
      g_free(fileExt);
      fileExt = g_value_dup_string(value);
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}

/**
 * visu_gl_node_scene_new:
 *
 * Create an object to handle a set of #VisuGlExt objects and draw
 * them together.
 *
 * Since: 3.8
 *
 * Returns: (transfer full): a newly created #VisuGlNodeScene object.
 **/
VisuGlNodeScene* visu_gl_node_scene_new()
{
  return g_object_new(VISU_TYPE_GL_NODE_SCENE, NULL);
}

static void visu_gl_node_scene_added(VisuGlExtSet *self, VisuGlExt *ext)
{
  VisuGlNodeScene *scene = VISU_GL_NODE_SCENE(self);

  g_return_if_fail(VISU_IS_GL_NODE_SCENE(self));
  
  if (VISU_IS_SOURCEABLE(ext))
    visu_sourceable_follow(VISU_SOURCEABLE(ext), _getData(scene));
}
static void visu_gl_node_scene_removed(VisuGlExtSet *self _U_, VisuGlExt *ext)
{
  if (VISU_IS_SOURCEABLE(ext))
    visu_sourceable_follow(VISU_SOURCEABLE(ext), (VisuData*)0);
}

static VisuBox* _getBox(VisuBoxed *self)
{
  VisuData *data;

  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(self), (VisuBox*)0);

  data = _getData(VISU_GL_NODE_SCENE(self));
  return data ? visu_boxed_getBox(VISU_BOXED(data)) : (VisuBox*)0;
}
static gboolean _setBox(VisuBoxed *self, VisuBox *box)
{
  VisuData *data;
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(self), FALSE);

  data = _getData(VISU_GL_NODE_SCENE(self));
  if (!data)
    return FALSE;
  
  return visu_boxed_setBox(VISU_BOXED(data), VISU_BOXED(box));
}
static VisuData* _getData(VisuGlNodeScene *self)
{
  return VISU_DATA(visu_node_array_renderer_getNodeArray(VISU_NODE_ARRAY_RENDERER(self->priv->nodes)));
}
/**
 * visu_gl_node_scene_setData:
 * @scene: a #VisuGlNodeScene object.
 * @data: a #VisuData object.
 *
 * Apply the given @view on all #VisuGlExt objects stored in @set.
 *
 * Since: 3.8
 *
 * Returns: TRUE if the @view actually change.
 **/
gboolean visu_gl_node_scene_setData(VisuGlNodeScene *scene, VisuData *data)
{
  VisuBox *boxOld, *box;
  VisuData *dataRef, *dataOld;
  VisuVibration *vib;
  ToolUnits units;
  VisuBoxBoundaries bc;
  float *vect;
  gboolean active, inTheBox;
  float t[3];
  GList *lst;

  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), FALSE);

  boxOld  = _getBox(VISU_BOXED(scene));
  dataOld = _getData(scene);
  if (data == dataOld)
    return FALSE;
  
  if (boxOld)
    g_object_ref(boxOld);
  if (dataOld)
    g_object_ref(dataOld);

  visu_node_array_renderer_setNodeArray(VISU_NODE_ARRAY_RENDERER(scene->priv->nodes),
                                        VISU_NODE_ARRAY(data));
  box = _getBox(VISU_BOXED(scene));

  if ((visu_gl_ext_getActive(VISU_GL_EXT(scene->priv->diff)) ||
       scene->priv->record) && dataOld)
    dataRef = dataOld;
  else
    dataRef = (VisuData*)0;

  if (dataOld && data)
    {
      DBG_fprintf(stderr, "Visu Node Scene: applying current translations.\n");
      bc = visu_box_getBoundary(boxOld);
      g_object_get(G_OBJECT(dataOld), "use-translation", &active,
                   "translation", &vect, "in-the-box", &inTheBox, NULL);
      if (!(bc & TOOL_XYZ_MASK_X))
        vect[0] = 0.f;
      if (!(bc & TOOL_XYZ_MASK_Y))
        vect[1] = 0.f;
      if (!(bc & TOOL_XYZ_MASK_Z))
        vect[2] = 0.f;
      g_object_set(G_OBJECT(data), "use-translation", active,
                   "translation", vect, "in-the-box", inTheBox, NULL);
      g_object_get(G_OBJECT(boxOld),
                   "use-expansion", &active, "expansion", &vect, NULL);
      g_object_set(G_OBJECT(box),
                   "use-expansion", active, "expansion", vect, NULL);
      /* Update the units if necessary. */
      units = visu_box_getUnit(boxOld);
      if (units != TOOL_UNITS_UNDEFINED)
        g_object_set(G_OBJECT(box), "units", units, NULL);
    }

  if (dataOld)
    {
      g_signal_handler_disconnect(dataOld, scene->priv->pos_sig);
      g_signal_handler_disconnect(dataOld, scene->priv->mar_sig);
      g_signal_handler_disconnect(dataOld, scene->priv->popMar_sig);
      g_signal_handler_disconnect(dataOld, scene->priv->popInc_sig);
      g_signal_handler_disconnect(dataOld, scene->priv->box_sig);
      g_signal_handler_disconnect(dataOld, scene->priv->vis_sig);
      g_signal_handler_disconnect(dataOld, scene->priv->mask_sig);
      if (VISU_IS_DATA_LOADABLE(dataOld))
        {
          g_signal_handler_disconnect(dataOld, scene->priv->load_sig);
          g_signal_handler_disconnect(dataOld, scene->priv->mess_sig);
        }
      if (VISU_IS_DATA_SPIN(dataOld))
        {
          g_object_unref(scene->priv->bind_theta);
          g_object_unref(scene->priv->bind_phi);
          g_object_unref(scene->priv->bind_omega);
        }
      vib = visu_data_getVibration(dataOld, 0);
      if (vib)
        {
          g_signal_handler_disconnect(vib, scene->priv->vib_sig);
          g_object_unref(scene->priv->vib_bind);
        }
    }
  if (data)
    {
      vib = visu_data_getVibration(data, 0);
      if (vib)
        {
          scene->priv->vib_sig = g_signal_connect_swapped(vib, "animate",
                                                          G_CALLBACK(_onAnimate), scene);
          scene->priv->vib_bind = g_object_bind_property(vib, "amplitude",
                                                         scene->priv->vibrations, "rendering-size", G_BINDING_SYNC_CREATE);
        }

      if (VISU_IS_DATA_LOADABLE(data))
        {
          scene->priv->load_sig =
            g_signal_connect_swapped(data, "notify::loading",
                                     G_CALLBACK(onLoading), (gpointer)scene);
          scene->priv->mess_sig =
            g_signal_connect_swapped(data, "notify::loading-message",
                                     G_CALLBACK(onLoadingMessage), (gpointer)scene);
        }
      if (VISU_IS_DATA_SPIN(data))
        {
          scene->priv->bind_theta =
            g_object_bind_property(visu_method_spin_getDefault(), "cone-theta",
                                   scene->priv->axes, "orientation-theta",
                                   G_BINDING_SYNC_CREATE);
          scene->priv->bind_phi =
            g_object_bind_property(visu_method_spin_getDefault(), "cone-phi",
                                   scene->priv->axes, "orientation-phi",
                                   G_BINDING_SYNC_CREATE);
          scene->priv->bind_omega =
            g_object_bind_property(visu_method_spin_getDefault(), "cone-omega",
                                   scene->priv->axes, "orientation-omega",
                                   G_BINDING_SYNC_CREATE);
        }

      scene->priv->pos_sig =
        g_signal_connect_swapped(data, "position-changed",
                                 G_CALLBACK(_applyMaskers), (gpointer)scene);
      scene->priv->mar_sig =
        g_signal_connect_swapped(data, "position-changed",
                                 G_CALLBACK(_ensureBoxMargin), (gpointer)scene);
      scene->priv->popInc_sig =
        g_signal_connect_swapped(data, "PopulationIncrease",
                                 G_CALLBACK(_applyMaskers), (gpointer)scene);
      scene->priv->popMar_sig =
        g_signal_connect_swapped(data, "PopulationIncrease",
                                 G_CALLBACK(_ensureBoxMargin), (gpointer)scene);
      scene->priv->box_sig =
        g_signal_connect_swapped(VISU_BOXED(data), "setBox",
                                 G_CALLBACK(_propagateBox), (gpointer)scene);
      scene->priv->vis_sig =
        g_signal_connect_swapped(data, "ElementVisibilityChanged",
                                 G_CALLBACK(_applyMaskers), (gpointer)scene);
      scene->priv->mask_sig =
        g_signal_connect_swapped(data, "ElementMaskableChanged",
                                 G_CALLBACK(_applyMaskers), (gpointer)scene);

      visu_pointset_getTranslation(VISU_POINTSET(data), t);
      visu_paths_setTranslation(scene->priv->paths, t);
    }

  /* Adapt the view to the new box. */
  _propagateBox(scene);

  /* Update all Gl extension. */
  visu_sourceable_setNodeModel
    (VISU_SOURCEABLE(scene->priv->forces),
     VISU_IS_DATA_ATOMIC(data) ? VISU_NODE_VALUES(visu_data_atomic_getForces(VISU_DATA_ATOMIC(data), FALSE)) : NULL);
  visu_gl_ext_node_vectors_setNodeRenderer
    (VISU_GL_EXT_NODE_VECTORS(scene->priv->forces),
     VISU_NODE_ARRAY_RENDERER(scene->priv->nodes));
  visu_sourceable_setNodeModel
    (VISU_SOURCEABLE(scene->priv->vibrations),
     data ? VISU_NODE_VALUES(visu_data_getVibration(VISU_DATA(data), 0)) : NULL);
  visu_gl_ext_node_vectors_setNodeRenderer
    (VISU_GL_EXT_NODE_VECTORS(scene->priv->vibrations),
     VISU_NODE_ARRAY_RENDERER(scene->priv->nodes));
  visu_gl_ext_pairs_setData(scene->priv->pairs, data);
  if (scene->priv->maps)
    visu_gl_ext_maps_removeAll(VISU_GL_EXT_MAPS(scene->priv->maps));
  visu_gl_ext_axes_useOrientation(scene->priv->axes, VISU_IS_DATA_SPIN(data));
  for (lst = visu_gl_ext_set_getAll(VISU_GL_EXT_SET(scene)); lst; lst = g_list_next(lst))
    if (VISU_IS_SOURCEABLE(lst->data))
      visu_sourceable_follow(VISU_SOURCEABLE(lst->data), data);

  if (data)
    {
      _applyMaskers(scene);
      _applyColorizationPolicy(scene);
    }
  
  g_object_notify_by_pspec(G_OBJECT(scene), _properties[DATA_PROP]);
  g_object_notify_by_pspec(G_OBJECT(scene), _properties[PATH_LENGTH_PROP]);

  if (dataRef && data)
    visu_gl_node_scene_setDiffFromData(scene, dataRef);

  if (dataOld)
    g_object_unref(dataOld);
  if (boxOld)
    g_object_unref(boxOld);

  return TRUE;
}

/**
 * visu_gl_node_scene_getData:
 * @scene: a #VisuGlNodeScene object.
 *
 * Retrives the #VisuData represented by #VisuGlNodeScene.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuData object.
 **/
VisuData* visu_gl_node_scene_getData(VisuGlNodeScene *scene)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), (VisuData*)0);

  return _getData(scene);
}

static void onLoading(VisuGlNodeScene *scene, GParamSpec *pspec _U_, VisuDataLoadable *data)
{
  gboolean loading;

  g_object_get(data, "loading", &loading, NULL);
  if (loading)
    {
      g_signal_handler_block(data, scene->priv->box_sig);
    }
  else
    {
      g_signal_handler_unblock(data, scene->priv->box_sig);
      _propagateBox(scene);
    }
  g_object_notify_by_pspec(G_OBJECT(scene), _properties[LOADING_PROP]);
}
static void onLoadingMessage(VisuGlNodeScene *scene, GParamSpec *pspec _U_, VisuDataLoadable *data _U_)
{
  g_object_notify_by_pspec(G_OBJECT(scene), _properties[LOADMESS_PROP]);
}
static void _ensureBoxMargin(VisuGlNodeScene *scene)
{
  VisuBox *box;
  VisuNodeArrayRenderer *nodes;

  box = _getBox(VISU_BOXED(scene));
  if (!box)
    return;

  nodes = VISU_NODE_ARRAY_RENDERER(scene->priv->nodes);
  visu_box_setMargin(box,
                     visu_node_array_renderer_getMaxElementSize(nodes, NULL) +
                     visu_data_getAllNodeExtens(VISU_DATA(visu_node_array_renderer_getNodeArray(nodes)), (VisuBox*)0), TRUE);
}
static void _propagateBox(VisuGlNodeScene *scene)
{
  VisuScalarfieldSetIter iter;
  gboolean valid;
  VisuBox *box;
  
  box = _getBox(VISU_BOXED(scene));

  /* Ensure that the box has enough margin for all nodes. */
  if (box)
    _ensureBoxMargin(scene);

  if (scene->priv->prevBox)
    visu_gl_node_scene_removeMasker(scene, VISU_NODE_MASKER(scene->priv->prevBox));
  if (box)
    visu_gl_node_scene_addMasker(scene, VISU_NODE_MASKER(box));
  scene->priv->prevBox = box;

  visu_boxed_setBox(VISU_BOXED(scene->priv->view), VISU_BOXED(box));
  visu_gl_ext_box_setBox(scene->priv->box, box);
  visu_gl_ext_box_legend_setBox(scene->priv->boxLegend, box);
  if (scene->priv->planes)
    visu_gl_ext_planes_setBox(scene->priv->planes, box);
  if (scene->priv->surfaces && scene->priv->fitToBox)
    visu_gl_ext_surfaces_setFittingBox(scene->priv->surfaces, box);
  if (scene->priv->fitToBox)
    for (valid = visu_scalarfield_set_iter_new(visu_scalarfield_set_getDefault(), &iter) &&
           visu_scalarfield_set_iter_next(&iter);
         valid; valid = visu_scalarfield_set_iter_next(&iter))
      visu_boxed_setBox(VISU_BOXED(iter.field), VISU_BOXED(box));

  DBG_fprintf(stderr, "Visu GlNodeScene: notify box.\n");
  g_object_notify(G_OBJECT(scene), "box");
  DBG_fprintf(stderr, "Visu GlNodeScene: notify box done.\n");
}

/**
 * visu_gl_node_scene_loadData:
 * @scene: a #VisuGlNodeScene object.
 * @loadable: a #VisuDataLoadable object.
 * @iSet: an id.
 * @cancel: (allow-none): a cancellable.
 * @error: an error location.
 *
 * A convenience function that tries to load @loadable, see
 * visu_data_loadable_load(). On success, @loadable is associated to
 * @scene, see visu_gl_node_scene_setData().
 *
 * Since: 3.8
 *
 * Returns: TRUE on success.
 **/
gboolean visu_gl_node_scene_loadData(VisuGlNodeScene *scene, VisuDataLoadable *loadable,
                                     guint iSet, GCancellable *cancel, GError **error)
{
  if (!visu_data_loadable_load(loadable, iSet, cancel, error))
    return FALSE;

  visu_gl_node_scene_setData(scene, VISU_DATA(loadable));
  return TRUE;
}

/**
 * visu_gl_node_scene_getGlView:
 * @scene: a #VisuGlNodeScene object.
 *
 * Retrieves the #VisuGlView used by @scene to render nodes.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuGlView object.
 **/
VisuGlView* visu_gl_node_scene_getGlView(VisuGlNodeScene *scene)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), (VisuGlView*)0);

  return scene->priv->view;
}
/**
 * visu_gl_node_scene_setGlCamera:
 * @scene: a #VisuGlNodeScene object.
 * @camera: a #VisuGlCamera object.
 *
 * A convenience function to change the view all at once with animations.
 *
 * Since: 3.8
 **/
void visu_gl_node_scene_setGlCamera(VisuGlNodeScene *scene, VisuGlCamera *camera)
{
  g_return_if_fail(VISU_IS_GL_NODE_SCENE(scene) && camera);

  g_object_set(scene->priv->view, "theta", camera->theta, "phi", camera->phi,
               "omega", camera->omega, "zoom", camera->gross,
               "perspective", camera->d_red, NULL);
  visu_gl_view_setXsYs(scene->priv->view, camera->xs, camera->ys,
                       VISU_GL_CAMERA_XS | VISU_GL_CAMERA_YS);
}

/**
 * visu_gl_node_scene_getNodes:
 * @scene: a #VisuGlNodeScene object.
 *
 * Retrieve the #VisuGlExtNodes object used by @scene to render nodes.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuGlExtNodes object.
 **/
VisuGlExtNodes* visu_gl_node_scene_getNodes(VisuGlNodeScene *scene)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), (VisuGlExtNodes*)0);
  return scene->priv->nodes;
}

/**
 * visu_gl_node_scene_getPairs:
 * @scene: a #VisuGlNodeScene object.
 *
 * Retrieve the #VisuGlExtPairs object used by @scene to render pairs.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuGlExtPairs object.
 **/
VisuGlExtPairs* visu_gl_node_scene_getPairs(VisuGlNodeScene *scene)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), (VisuGlExtPairs*)0);
  return scene->priv->pairs;
}

/**
 * visu_gl_node_scene_getAxes:
 * @scene: a #VisuGlNodeScene object.
 *
 * Retrieve the #VisuGlExtAxes object used by @scene to render axes.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuGlExtAxes object.
 **/
VisuGlExtAxes* visu_gl_node_scene_getAxes(VisuGlNodeScene *scene)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), (VisuGlExtAxes*)0);
  return scene->priv->axes;
}

/**
 * visu_gl_node_scene_getBox:
 * @scene: a #VisuGlNodeScene object.
 *
 * Retrieve the #VisuGlExtBox object used by @scene to render the box.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuGlExtBox object.
 **/
VisuGlExtBox* visu_gl_node_scene_getBox(VisuGlNodeScene *scene)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), (VisuGlExtBox*)0);
  return scene->priv->box;
}

/**
 * visu_gl_node_scene_getBoxLegend:
 * @scene: a #VisuGlNodeScene object.
 *
 * Retrieve the #VisuGlExtBoxLegend object used by @scene to render
 * the lgened of the box.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuGlExtBoxLegend object.
 **/
VisuGlExtBoxLegend* visu_gl_node_scene_getBoxLegend(VisuGlNodeScene *scene)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), (VisuGlExtBoxLegend*)0);
  return scene->priv->boxLegend;
}

/**
 * visu_gl_node_scene_getLegend:
 * @scene: a #VisuGlNodeScene object.
 *
 * Retrieve the #VisuGlExtLegend object used by @scene to render the
 * node legend.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuGlExtLegend object.
 **/
VisuGlExtLegend* visu_gl_node_scene_getLegend(VisuGlNodeScene *scene)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), (VisuGlExtLegend*)0);
  return scene->priv->legend;
}

/**
 * visu_gl_node_scene_getBgImage:
 * @scene: a #VisuGlNodeScene object.
 *
 * Retrieve the #VisuGlExtBg object used by @scene to render images on
 * the background.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuGlExtBg object.
 **/
VisuGlExtBg* visu_gl_node_scene_getBgImage(VisuGlNodeScene *scene)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), (VisuGlExtBg*)0);
  return scene->priv->bg;
}

/**
 * visu_gl_node_scene_getScales:
 * @scene: a #VisuGlNodeScene object.
 *
 * Retrieve the #VisuGlExtScale object used by @scene to render scales.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuGlExtScale object.
 **/
VisuGlExtScale* visu_gl_node_scene_getScales(VisuGlNodeScene *scene)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), (VisuGlExtScale*)0);
  return scene->priv->scales;
}

/**
 * visu_gl_node_scene_getForces:
 * @scene: a #VisuGlNodeScene object.
 *
 * Retrieve the #VisuGlExtForces object used by @scene to render forces.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuGlExtForces object.
 **/
VisuGlExtForces* visu_gl_node_scene_getForces(VisuGlNodeScene *scene)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), (VisuGlExtForces*)0);
  return scene->priv->forces;
}

/**
 * visu_gl_node_scene_getVibrations:
 * @scene: a #VisuGlNodeScene object.
 *
 * Retrieves the associated #VisuGlExtVibrations used to represent
 * periodic displacements of nodes.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): the attached #VisuGlExtVibrations, owned
 * by V_Sim.
 **/
VisuGlExtVibrations* visu_gl_node_scene_getVibrations(VisuGlNodeScene *scene)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), (VisuGlExtVibrations*)0);
  return scene->priv->vibrations;
}
/**
 * visu_gl_node_scene_getMarks:
 * @scene: a #VisuGlNodeScene object.
 *
 * Retrieve the #VisuGlExtMarks object used by @scene to render marks.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuGlExtMarks object.
 **/
VisuGlExtMarks* visu_gl_node_scene_getMarks(VisuGlNodeScene *scene)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), (VisuGlExtMarks*)0);
  return scene->priv->marks;
}
/**
 * visu_gl_node_scene_getMarkActive:
 * @scene: a #VisuGlNodeScene obejct.
 * @nodeId: a node id.
 *
 * Tests if @nodeId from @scene has some marks on it.
 *
 * Since: 3.8
 *
 * Returns: TRUE if @nodeId has some marks on it.
 **/
gboolean visu_gl_node_scene_getMarkActive(const VisuGlNodeScene *scene, guint nodeId)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), FALSE);
  return visu_gl_ext_marks_getActive(scene->priv->marks, nodeId);
}
/**
 * visu_gl_node_scene_setMark:
 * @scene: a #VisuGlNodeScene object.
 * @nodeId: a node id.
 * @status: a boolean.
 *
 * Display or removes marks on @nodeId, depending on @status.
 *
 * Since: 3.8
 *
 * Returns: TRUE if @nodeId mark status changed.
 **/
gboolean visu_gl_node_scene_setMark(VisuGlNodeScene *scene, guint nodeId, gboolean status)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), FALSE);
  return visu_gl_ext_marks_setInfos(scene->priv->marks, nodeId, status);
}
/**
 * visu_gl_node_scene_removeMarks:
 * @scene: a #VisuGlNodeScene object.
 *
 * Remove all marks on @scene.
 *
 * Since: 3.8
 **/
void visu_gl_node_scene_removeMarks(VisuGlNodeScene *scene)
{
  g_return_if_fail(VISU_IS_GL_NODE_SCENE(scene));
  visu_gl_ext_marks_removeMeasures(scene->priv->marks, -1);
}
/**
 * visu_gl_node_scene_colorizeShell:
 * @scene: a #VisuGlNodeScene object.
 * @nodeId: a node id.
 *
 * Apply colorization depending on neighbouring shell of @nodeId.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): the #VisuDataColorizerFragment used for
 * the colorization.
 **/
VisuDataColorizerFragment* visu_gl_node_scene_colorizeShell(VisuGlNodeScene *scene,
                                                            gint nodeId)
{
  VisuNodeValues *shell;
  VisuData *data;
  gboolean valid;
  
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), (VisuDataColorizerFragment*)0);

  data = _getData(scene);
  if (!data)
    return (VisuDataColorizerFragment*)0;
  
  if (!scene->priv->shellColorizer)
    {
      scene->priv->shellColorizer = visu_data_colorizer_fragment_new();
      visu_gl_node_scene_addMasker(scene, VISU_NODE_MASKER(scene->priv->shellColorizer));
    }
  shell = visu_data_getNodeProperties(data, "shell");
  if (!shell)
    {
      shell = VISU_NODE_VALUES(visu_node_values_shell_new(VISU_NODE_ARRAY(data), "shell"));
      visu_data_addNodeProperties(data, shell);
      visu_sourceable_setNodeModel(VISU_SOURCEABLE(scene->priv->shellColorizer), shell);
    }

  valid = (nodeId >= 0);
  if (valid && visu_node_values_shell_getRoot(VISU_NODE_VALUES_SHELL(shell)) != nodeId)
    valid = visu_node_values_shell_compute(VISU_NODE_VALUES_SHELL(shell), nodeId, 0.2);

  if (visu_data_colorizer_getActive(VISU_DATA_COLORIZER(scene->priv->shellColorizer)) == valid)
    return valid ? scene->priv->shellColorizer : (VisuDataColorizerFragment*)0;
  visu_data_colorizer_setActive(VISU_DATA_COLORIZER(scene->priv->shellColorizer),
                                valid);

  if (valid)
    visu_node_array_renderer_pushColorizer
      (VISU_NODE_ARRAY_RENDERER(scene->priv->nodes),
       VISU_DATA_COLORIZER(scene->priv->shellColorizer));
  else
    visu_node_array_renderer_removeColorizer
      (VISU_NODE_ARRAY_RENDERER(scene->priv->nodes),
       VISU_DATA_COLORIZER(scene->priv->shellColorizer));

  return valid ? scene->priv->shellColorizer : (VisuDataColorizerFragment*)0;
}

static void _applyMaskers(VisuGlNodeScene *scene)
{
  gboolean redraw;
  GList *lst;
  VisuData *data;

  DBG_fprintf(stderr, "Visu Node Scene: applying %d maskers.\n",
              g_list_length(scene->priv->maskers));

  data = _getData(scene);
  if (!data || !scene->priv->maskers)
    return;

  redraw = visu_maskable_resetVisibility(VISU_MASKABLE(data));

  for (lst = scene->priv->maskers; lst; lst = g_list_next(lst))
    visu_node_masker_apply(((struct _masker*)lst->data)->masker,
                           &redraw, VISU_NODE_ARRAY(data));

  if (redraw)
    visu_maskable_visibilityChanged(VISU_MASKABLE(data));
}
/**
 * visu_gl_node_scene_addPlanes:
 * @scene: a #VisuGlNodeScene object.
 *
 * Add or retrieve the #VisuGlExtPlanes object used by @scene to render planes.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuGlExtPlanes object.
 **/
VisuGlExtPlanes* visu_gl_node_scene_addPlanes(VisuGlNodeScene *scene)
{
  VisuBox *box;
  
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), (VisuGlExtPlanes*)0);

  if (scene->priv->planes)
    return scene->priv->planes;

  scene->priv->planes = visu_gl_ext_planes_new((const gchar*)0);
  visu_gl_ext_set_add(VISU_GL_EXT_SET(scene), VISU_GL_EXT(scene->priv->planes));
  visu_gl_node_scene_addMasker(scene, VISU_NODE_MASKER(scene->priv->planes->planes));
  box = _getBox(VISU_BOXED(scene));
  if (box)
    visu_gl_ext_planes_setBox(scene->priv->planes, box);
  scene->priv->planes_anim = g_signal_connect_swapped(scene->priv->planes->planes,
                                                      "animate",
                                                      G_CALLBACK(_onAnimate), scene);
  if (scene->priv->surfaces)
    visu_gl_ext_surfaces_setMask(scene->priv->surfaces, scene->priv->planes->planes);

  return scene->priv->planes;
}
/**
 * visu_gl_node_scene_addSurfaces:
 * @scene: a #VisuGlNodeScene object.
 *
 * Add or retrieve the #VisuGlExtSurfaces object used by @scene to render surfaces.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuGlExtSurfaces object.
 **/
VisuGlExtSurfaces* visu_gl_node_scene_addSurfaces(VisuGlNodeScene *scene)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), (VisuGlExtSurfaces*)0);

  if (scene->priv->surfaces)
    return scene->priv->surfaces;
  scene->priv->surfaces = visu_gl_ext_surfaces_new((const gchar*)0);
  visu_gl_ext_set_add(VISU_GL_EXT_SET(scene), VISU_GL_EXT(scene->priv->surfaces));
  if (scene->priv->planes)
    visu_gl_ext_surfaces_setMask(scene->priv->surfaces, scene->priv->planes->planes);
  return scene->priv->surfaces;
}
/**
 * visu_gl_node_scene_addMaps:
 * @scene: a #VisuGlNodeScene object.
 * @mapLegend: (out) (transfer none): a pointer to retrieve the associated #VisuGlExtShade
 * legend of the coloured map.
 *
 * Add or retrieve the #VisuGlExtMapSet object used by @scene to render maps.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuGlExtMapSet object.
 **/
VisuGlExtMapSet* visu_gl_node_scene_addMaps(VisuGlNodeScene *scene, VisuGlExtShade **mapLegend)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), (VisuGlExtMapSet*)0);

  if (!scene->priv->maps)
    {
      scene->priv->maps = visu_gl_ext_map_set_new((const gchar*)0);
      visu_gl_ext_set_add(VISU_GL_EXT_SET(scene), VISU_GL_EXT(scene->priv->maps));
      scene->priv->mapLegend = visu_gl_ext_map_set_getLegend(scene->priv->maps);
      g_object_ref(scene->priv->mapLegend);
      visu_gl_ext_set_add(VISU_GL_EXT_SET(scene), VISU_GL_EXT(scene->priv->mapLegend));
    }
  if (mapLegend)
    *mapLegend = scene->priv->mapLegend;
  return scene->priv->maps;
}

/**
 * visu_gl_node_scene_dump:
 * @scene:a valid #VisuGlNodeScene object ;
 * @format: a #VisuDump object, corresponding to the write method ;
 * @fileName: (type filename): a string that defined the file to write to ;
 * @width: an integer, or 0 for current ;
 * @height: an integer, or 0 for current ;
 * @functionWait: (allow-none) (closure data) (scope call): a method to call
 * periodically during the dump ;
 * @data: (closure): some pointer on object to be passed to the wait function.
 * @error: a location to store some error (not NULL) ;
 *
 * Call this method to dump the given @scene to a file.
 *
 * Returns: TRUE if everything went right.
 */
gboolean visu_gl_node_scene_dump(VisuGlNodeScene *scene, VisuDump *format,
                                 const char* fileName, guint width, guint height,
                                 ToolVoidDataFunc functionWait, gpointer data,
                                 GError **error)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), FALSE);
  g_return_val_if_fail(error && !*error, FALSE);
  g_return_val_if_fail(format && fileName, FALSE);

  if (VISU_IS_DUMP_SCENE(format))
    {
      DBG_fprintf(stderr, "Visu Node Scene: dumping current OpenGL area.\n");
      DBG_fprintf(stderr, " | requested size %dx%d.\n", width, height);
      return visu_dump_scene_write(VISU_DUMP_SCENE(format), fileName, scene, width, height,
                                   functionWait, data, error);
    }
  else if (VISU_IS_DUMP_DATA(format))
    {
      VisuData *data = _getData(scene);
      DBG_fprintf(stderr, "Visu Node Scene: dumping data only.\n");
      if (!data)
        return FALSE;
      return visu_dump_data_write(VISU_DUMP_DATA(format), fileName, data, error);
    }
  return FALSE;
}

/**
 * visu_gl_node_scene_setMarksFromFile:
 * @scene: a #VisuGlNodeScene object.
 * @filename: a filename
 * @error: an error location.
 *
 * Try to parse @filename and put marks on nodes of @scene accordingly.
 *
 * Since: 3.8
 *
 * Returns: TRUE on success.
 **/
gboolean visu_gl_node_scene_setMarksFromFile(VisuGlNodeScene *scene,
                                             const gchar *filename, GError **error)
{
  GList *tmplst, *list;
  GArray *nodes;
  VisuGlExtInfosDrawId mode;
  guint info, i;

  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), FALSE);

  if (!visu_gl_ext_marks_parseXMLFile(scene->priv->marks, filename,
                                      &list, &mode, &info, error))
    return FALSE;

  nodes = (GArray*)0;
  if (mode == DRAW_SELECTED)
    {
      nodes = g_array_new(FALSE, FALSE, sizeof(guint));
      for (tmplst = list; tmplst; tmplst = g_list_next(tmplst))
        {
          if (GPOINTER_TO_INT(tmplst->data) == PICK_SELECTED)
            {
              tmplst = g_list_next(tmplst);
              i = GPOINTER_TO_INT(tmplst->data) - 1;
              g_array_append_val(nodes, i);
            }
          else if (GPOINTER_TO_INT(tmplst->data) == PICK_REFERENCE_1)
            {
              tmplst = g_list_next(tmplst);
              /* We add the last selection. */
              tmplst = g_list_next(tmplst);
              i = GPOINTER_TO_INT(tmplst->data) - 1;
              g_array_append_val(nodes, i);
            }
          else if (GPOINTER_TO_INT(tmplst->data) == PICK_HIGHLIGHT)
            tmplst = g_list_next(tmplst);
        }
      g_list_free(list);
    }
  if (mode != DRAW_NEVER)
    {
      switch (info)
        {
        case 0:
          visu_gl_ext_infos_drawIds(scene->priv->infos, nodes);
          visu_gl_ext_setActive(VISU_GL_EXT(scene->priv->infos), TRUE);
          break;
        case 1:
          visu_gl_ext_infos_drawElements(scene->priv->infos, nodes);
          visu_gl_ext_setActive(VISU_GL_EXT(scene->priv->infos), TRUE);
          break;
        default:
          i = 2;
          for (tmplst = visu_data_getAllNodeProperties(_getData(scene));
               tmplst; tmplst = g_list_next(tmplst))
            {
              if (i == info)
                break;
              i += 1;
            }
          if (tmplst)
            {
              visu_gl_ext_infos_drawNodeProperties(scene->priv->infos,
                                                   VISU_NODE_VALUES(tmplst->data), nodes);
              visu_gl_ext_setActive(VISU_GL_EXT(scene->priv->infos), TRUE);
            }
          break;
        }
    }
  if (nodes)
    g_array_unref(nodes);

  return TRUE;
}
/**
 * visu_gl_node_scene_marksToFile:
 * @scene: a #VisuGlNodeScene object.
 * @filename: a path.
 * @error: a error location.
 *
 * Export the current selection and marks into @filename.
 *
 * Since: 3.8
 *
 * Returns: FALSE if an error occured.
 **/
gboolean visu_gl_node_scene_marksToFile(VisuGlNodeScene *scene,
                                        const gchar *filename, GError **error)
{
  GArray *nodes;
  VisuGlExtInfosDrawId mode;
  guint info;
  gboolean valid;
  GList *tmplst;
  VisuNodeValues *values;

  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), FALSE);

  g_object_get(scene->priv->infos, "selection", &nodes, "values", &values, NULL);
  if (!visu_gl_ext_getActive(VISU_GL_EXT(scene->priv->infos)))
    mode = DRAW_NEVER;
  else if (nodes)
    mode = DRAW_SELECTED;
  else
    mode = DRAW_ALWAYS;

  info = 0;
  if (values && VISU_IS_NODE_VALUES_ID(values))
    info = 1;
  else if (values && VISU_IS_NODE_VALUES(values))
    info = 2;
  else if (values)
    for (tmplst = visu_data_getAllNodeProperties(_getData(scene)), info = 3;
         tmplst; tmplst = g_list_next(tmplst))
      {
        if (tmplst->data == values)
          break;
        info += 1;
      }

  valid = visu_gl_ext_marks_exportXMLFile(scene->priv->marks, filename, nodes,
                                          mode, info, error);

  if (nodes)
    g_array_unref(nodes);
  if (values)
    g_object_unref(values);

  return valid;
}

/**
 * visu_gl_node_scene_getInfos:
 * @scene: a #VisuGlNodeScene object.
 *
 * Retrieves the #VisuGlExtInfos object used by @scene to display info
 * on nodes.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuGlExtInfos object.
 **/
VisuGlExtInfos* visu_gl_node_scene_getInfos(VisuGlNodeScene *scene)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), (VisuGlExtInfos*)0);
  return scene->priv->infos;
}

static void onEntryScale(VisuGlNodeScene *scene,
                         VisuConfigFileEntry *entry, VisuConfigFile *obj _U_)
{
  float xyz[3], orientation[3], len;
  float rg[2] = {-G_MAXFLOAT, G_MAXFLOAT};
  float rgLen[2] = {0.f, G_MAXFLOAT};
  gchar *legend;

  if (!visu_config_file_entry_popTokenAsFloat(entry, 3, xyz, rg))
    return;
  if (!visu_config_file_entry_popTokenAsFloat(entry, 3, orientation, rg))
    return;
  if (!visu_config_file_entry_popTokenAsFloat(entry, 1, &len, rgLen))
    return;
  legend = visu_config_file_entry_popAllTokens(entry);
  g_strstrip(legend);

  visu_gl_ext_scale_add(scene->priv->scales, xyz, orientation, len, legend);
  
  g_free(legend);
}

/**
 * visu_gl_node_scene_setDiffFromLoadable:
 * @scene: a #VisuGlNodeScene object.
 * @ref: a #VisuData object.
 * @error: an error location.
 *
 * Same as visu_gl_node_scene_setDiffFromData(), but load first @ref
 * before doing the difference.
 *
 * Since: 3.8
 *
 * Returns: TRUE on success.
 **/
gboolean visu_gl_node_scene_setDiffFromLoadable(VisuGlNodeScene *scene,
                                                VisuDataLoadable *ref, GError **error)
{
  g_object_ref(ref);

  if (!visu_data_loadable_load(ref, 0, (GCancellable*)0, error))
    {
      g_object_unref(ref);
      return FALSE;
    }

  visu_gl_node_scene_setDiffFromData(scene, VISU_DATA(ref));

  g_object_unref(ref);

  return TRUE;
}

/**
 * visu_gl_node_scene_setDiffFromData:
 * @scene: a #VisuGlNodeScene object.
 * @dataRef: a #VisuData object.
 *
 * Creates a #VisuDataDiff from the current #VisuData and @dataRef. If
 * the scene is in recording mode (see "record-path" property), the
 * difference is appended to the current path.
 *
 * Since: 3.8
 **/
void visu_gl_node_scene_setDiffFromData(VisuGlNodeScene *scene, const VisuData *dataRef)
{
  VisuDataDiff *vect;
  VisuData *data;
  gboolean new;
  gdouble energy;

  data = _getData(scene);
  if (!data)
    return;
  vect = visu_data_diff_new(dataRef, data,
                            scene->priv->reorder, VISU_DATA_DIFF_DEFAULT_ID);
  visu_sourceable_setNodeModel(VISU_SOURCEABLE(scene->priv->diff),
                               VISU_NODE_VALUES(vect));
  visu_gl_ext_node_vectors_setNodeRenderer(VISU_GL_EXT_NODE_VECTORS(scene->priv->diff),
                                           VISU_NODE_ARRAY_RENDERER(scene->priv->nodes));
  if (vect && scene->priv->record)
    {
      g_object_get(data, "totalEnergy", &energy, NULL);
      new = visu_paths_addFromDiff(scene->priv->paths, vect, energy);
      if (new)
        visu_paths_constrainInBox(scene->priv->paths, data);
      visu_gl_ext_setDirty(VISU_GL_EXT(scene->priv->extPaths), TRUE);
      g_object_notify_by_pspec(G_OBJECT(scene), _properties[PATH_LENGTH_PROP]);
    }

  g_object_unref(vect);
}
/**
 * visu_gl_node_scene_getGeometryDifferences:
 * @scene: a #VisuGlNodeScene object.
 *
 * Retrieve the #VisuGlExtGeodiff used to draw geometry differences.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuGlExtGeodiff object.
 **/
VisuGlExtGeodiff* visu_gl_node_scene_getGeometryDifferences(VisuGlNodeScene *scene)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), (VisuGlExtGeodiff*)0);

  return scene->priv->diff;
}

static void onDiffActive(VisuGlNodeScene *scene,
                         GParamSpec *pspec _U_, VisuGlExtGeodiff *diff _U_)
{
  g_object_notify_by_pspec(G_OBJECT(scene), _properties[DIFF_PROP]);
}

static void onPathActive(VisuGlNodeScene *scene,
                         GParamSpec *pspec _U_, VisuGlExtPaths *paths _U_)
{
  g_object_notify_by_pspec(G_OBJECT(scene), _properties[PATH_PROP]);
}

/**
 * visu_gl_node_scene_clearPaths:
 * @scene: a #VisuGlNodeScene object.
 *
 * Remove any #VisuPaths from @scene.
 *
 * Since: 3.8
 **/
void visu_gl_node_scene_clearPaths(VisuGlNodeScene *scene)
{
  g_return_if_fail(VISU_IS_GL_NODE_SCENE(scene));

  if (!scene->priv->paths)
    return;

  visu_paths_empty(scene->priv->paths);
  visu_gl_ext_setDirty(VISU_GL_EXT(scene->priv->extPaths), TRUE);
  g_object_notify_by_pspec(G_OBJECT(scene), _properties[PATH_LENGTH_PROP]);
}

/**
 * visu_gl_node_scene_parsePathsFromXML:
 * @scene: a #VisuGlNodeScene object.
 * @filename: a filename.
 * @error: an error location.
 *
 * Try to parse @filename to load some #VisuPaths into @scene.
 *
 * Since: 3.8
 *
 * Returns: TRUE on success.
 **/
gboolean visu_gl_node_scene_parsePathsFromXML(VisuGlNodeScene *scene,
                                              const gchar *filename, GError **error)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), FALSE);

  if (!scene->priv->paths)
    return FALSE;

  if (!visu_paths_parseFromXML(filename, scene->priv->paths, error))
    return FALSE;

  visu_gl_ext_setDirty(VISU_GL_EXT(scene->priv->extPaths), TRUE);
  g_object_notify_by_pspec(G_OBJECT(scene), _properties[PATH_LENGTH_PROP]);
  return TRUE;
}
/**
 * visu_gl_node_scene_exportPathsToXML:
 * @scene: a #VisuGlNodeScene object.
 * @filename: a filename.
 * @error: an error location.
 *
 * If @scene has some #VisuPaths, it exports them to an XML file
 * defined by @filename.
 *
 * Since: 3.8
 *
 * Returns: TRUE on success.
 **/
gboolean visu_gl_node_scene_exportPathsToXML(VisuGlNodeScene *scene,
                                             const gchar *filename, GError **error)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), FALSE);

  if (!scene->priv->paths)
    return TRUE;

  return visu_paths_exportXMLFile(scene->priv->paths, filename, error);
}

static gint _findMasker(const struct _masker *lst, const VisuNodeMasker *item)
{
  if (lst->masker == item)
    return 0;
  return 1;
}
static struct _masker* _newMasker(VisuNodeMasker *masker, VisuGlNodeScene *scene)
{
  struct _masker *str;

  str = g_malloc(sizeof(struct _masker));
  str->masker = g_object_ref(masker);
  str->dirty_sig = g_signal_connect_swapped(masker, "masking-dirty",
                                            G_CALLBACK(_applyMaskers), scene);
  DBG_fprintf(stderr, "Visu Node Scene: connect signal %ld for %p.\n",
              str->dirty_sig, (gpointer)masker);
  return str;
}
static void _freeMasker(struct _masker *str)
{
  DBG_fprintf(stderr, "Visu Node Scene: disconnect signal %ld for %p.\n",
              str->dirty_sig, (gpointer)str->masker);
  g_signal_handler_disconnect(str->masker, str->dirty_sig);
  g_object_unref(str->masker);
  g_free(str);
}
/**
 * visu_gl_node_scene_addMasker:
 * @scene: a #VisuGlNodeScene object.
 * @masker: a #VisuNodeMasker object.
 *
 * Add @masker to @scene, so when @masker is modified, #VisuNode of
 * @scene are hidden accordingly.
 *
 * Since: 3.8
 *
 * Returns: FALSE if @masker already exists.
 **/
gboolean visu_gl_node_scene_addMasker(VisuGlNodeScene *scene, VisuNodeMasker *masker)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), FALSE);

  DBG_fprintf(stderr, "Visu Node Scene: add a new masker %p.\n", (gpointer)masker);

  if (g_list_find_custom(scene->priv->maskers, masker, (GCompareFunc)_findMasker))
    return FALSE;

  scene->priv->maskers = g_list_prepend(scene->priv->maskers, _newMasker(masker, scene));
  _applyMaskers(scene);
  return TRUE;
}
/**
 * visu_gl_node_scene_removeMasker:
 * @scene: a #VisuGlNodeScene object.
 * @masker: a #VisuNodeMasker object.
 *
 * Remove @masker from the list of objects that may hide nodes in @scene.
 *
 * Since: 3.8
 *
 * Returns: TRUE on successful removal.
 **/
gboolean visu_gl_node_scene_removeMasker(VisuGlNodeScene *scene, VisuNodeMasker *masker)
{
  GList *item;
  VisuData *data;

  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), FALSE);

  DBG_fprintf(stderr, "Visu Node Scene: remove a masker %p.\n", (gpointer)masker);

  item = g_list_find_custom(scene->priv->maskers, masker, (GCompareFunc)_findMasker);
  if (!item)
    return FALSE;

  _freeMasker((struct _masker*)item->data);
  scene->priv->maskers = g_list_delete_link(scene->priv->maskers, item);
  data = _getData(scene);
  if (!scene->priv->maskers && data &&
      visu_maskable_resetVisibility(VISU_MASKABLE(data)))
    visu_maskable_visibilityChanged(VISU_MASKABLE(data));
  else
    _applyMaskers(scene);
  return TRUE;
}

static gint _findMover(const struct _mover *lst, const VisuNodeMover *item)
{
  if (lst->mover == item)
    return 0;
  return 1;
}
static struct _mover* _newMover(VisuNodeMover *mover, VisuGlNodeScene *scene)
{
  struct _mover *str;

  str = g_malloc(sizeof(struct _mover));
  str->mover = g_object_ref(mover);
  str->animate_sig = g_signal_connect_swapped(mover, "animate",
                                              G_CALLBACK(_onAnimate), scene);
  str->bind = g_object_bind_property(scene, "data", mover, "nodes",
                                     G_BINDING_SYNC_CREATE);
  DBG_fprintf(stderr, "Visu Node Scene: connect signal %ld for %p.\n",
              str->animate_sig, (gpointer)mover);
  return str;
}
static void _freeMover(struct _mover *str)
{
  DBG_fprintf(stderr, "Visu Node Scene: disconnect signal %ld for %p.\n",
              str->animate_sig, (gpointer)str->mover);
  g_signal_handler_disconnect(str->mover, str->animate_sig);
  g_object_unref(str->bind);
  g_object_unref(str->mover);
  g_free(str);
}
/**
 * visu_gl_node_scene_addMover:
 * @scene: a #VisuGlNodeScene object.
 * @mover: a #VisuNodeMover object.
 *
 * Add @mover to @scene.
 *
 * Since: 3.8
 *
 * Returns: FALSE if @mover already exists.
 **/
gboolean visu_gl_node_scene_addMover(VisuGlNodeScene *scene, VisuNodeMover *mover)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), FALSE);

  DBG_fprintf(stderr, "Visu Node Scene: add a new mover %p.\n", (gpointer)mover);

  if (g_list_find_custom(scene->priv->movers, mover, (GCompareFunc)_findMover))
    return FALSE;

  scene->priv->movers = g_list_prepend(scene->priv->movers, _newMover(mover, scene));
  return TRUE;
}
/**
 * visu_gl_node_scene_removeMover:
 * @scene: a #VisuGlNodeScene object.
 * @mover: a #VisuNodeMover object.
 *
 * Remove @mover from the list of objects that may move nodes in @scene.
 *
 * Since: 3.8
 *
 * Returns: TRUE on successful removal.
 **/
gboolean visu_gl_node_scene_removeMover(VisuGlNodeScene *scene, VisuNodeMover *mover)
{
  GList *item;

  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), FALSE);

  DBG_fprintf(stderr, "Visu Node Scene: remove a mover %p.\n", (gpointer)mover);

  item = g_list_find_custom(scene->priv->movers, mover, (GCompareFunc)_findMover);
  if (!item)
    return FALSE;

  _freeMover((struct _mover*)item->data);
  scene->priv->movers = g_list_delete_link(scene->priv->movers, item);
  return TRUE;
}

static void minMaxFromColorization(VisuGlNodeScene *scene)
{
  float *mm = (float*)0;
  gboolean active;

  g_return_if_fail(VISU_IS_GL_NODE_SCENE(scene));
  
  if (scene->priv->dt)
    g_object_get(G_OBJECT(scene->priv->dt), "single-range", &mm, NULL);
  active = scene->priv->dt && mm &&
    visu_data_colorizer_getActive(VISU_DATA_COLORIZER(scene->priv->dt)) &&
    visu_node_array_renderer_getColorizer
    (VISU_NODE_ARRAY_RENDERER(scene->priv->nodes)) ==
    VISU_DATA_COLORIZER(scene->priv->dt);

  visu_gl_ext_setActive(VISU_GL_EXT(scene->priv->colorizationLegend), active);
  if (active)
    visu_gl_ext_shade_setMinMax(scene->priv->colorizationLegend, mm[0], mm[1]);

  if (mm)
    g_boxed_free(TOOL_TYPE_MINMAX, mm);
}
/**
 * visu_gl_node_scene_setColorization:
 * @scene: a #VisuGlNodeScene object.
 * @dt: (allow-none): a #VisuColorization object.
 *
 * Associates @dt with @scene to colorize nodes.
 *
 * Since: 3.8
 *
 * Returns: TRUE if value is actually changed.
 **/
gboolean visu_gl_node_scene_setColorization(VisuGlNodeScene *scene, VisuColorization *dt)
{
  VisuGlExtShade *legend;

  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), FALSE);

  if (scene->priv->dt == dt)
    return FALSE;

  if (scene->priv->dt)
    {
      g_object_unref(scene->priv->bind_shade);
      visu_gl_node_scene_removeMasker(scene, VISU_NODE_MASKER(scene->priv->dt));
      visu_node_array_renderer_removeColorizer
        (VISU_NODE_ARRAY_RENDERER(scene->priv->nodes),
         VISU_DATA_COLORIZER(scene->priv->dt));
      g_signal_handler_disconnect(scene->priv->dt, scene->priv->single_sig);
      g_signal_handler_disconnect(scene->priv->dt, scene->priv->active_sig);
      g_signal_handler_disconnect(scene->priv->nodes, scene->priv->colorizer_sig);
      g_object_unref(scene->priv->dt);
    }
  if (dt)
    {
      g_object_ref(dt);
      visu_gl_node_scene_addMasker(scene, VISU_NODE_MASKER(dt));
      visu_node_array_renderer_pushColorizer
        (VISU_NODE_ARRAY_RENDERER(scene->priv->nodes),
         VISU_DATA_COLORIZER(dt));
      legend = visu_gl_node_scene_getColorizationLegend(scene);
      scene->priv->bind_shade = g_object_bind_property
        (dt, "shade", legend, "shade", G_BINDING_SYNC_CREATE);
      scene->priv->single_sig = g_signal_connect_swapped
        (G_OBJECT(dt), "notify::single-range",
         G_CALLBACK(minMaxFromColorization), scene);
      scene->priv->active_sig = g_signal_connect_swapped
        (G_OBJECT(dt), "notify::active", G_CALLBACK(minMaxFromColorization), scene);
      scene->priv->colorizer_sig = g_signal_connect_swapped
        (G_OBJECT(scene->priv->nodes), "notify::colorizer",
         G_CALLBACK(minMaxFromColorization), scene);
    }
  scene->priv->dt = dt;
  minMaxFromColorization(scene);

  return TRUE;
}
/**
 * visu_gl_node_scene_getColorization:
 * @scene: a #VisuGlNodeScene object.
 *
 * The attached #VisuColorization object, if any.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuColorization object, or %NULL.
 **/
VisuColorization* visu_gl_node_scene_getColorization(VisuGlNodeScene *scene)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), (VisuColorization*)0);

  return scene->priv->dt;
}
/**
 * visu_gl_node_scene_getColorizationLegend:
 * @scene: a #VisuGlNodeScene object.
 *
 * Retrieve the #VisuGlExtShade object, used to represent the color
 * variation of an attached #VisuColorization object. The returned
 * object is created if none already exists.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuGlExtShade.
 **/
VisuGlExtShade* visu_gl_node_scene_getColorizationLegend(VisuGlNodeScene *scene)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), (VisuGlExtShade*)0);

  if (!scene->priv->colorizationLegend)
    {
      scene->priv->colorizationLegend = visu_gl_ext_shade_new("Colorization legend");
      visu_gl_ext_set_add(VISU_GL_EXT_SET(scene),
                          VISU_GL_EXT(scene->priv->colorizationLegend));
    }
  return scene->priv->colorizationLegend;
}
static void _applyColorizationPolicy(VisuGlNodeScene *scene)
{
  VisuNodeValuesFarray *values;
  VisuNodeValues *old;
  VisuData *data;
  GError *error;
  gchar *dtFile, *file, *fileExtPosition;

  g_return_if_fail(VISU_IS_GL_NODE_SCENE(scene));

  data = _getData(scene);
  values = (VisuNodeValuesFarray*)0;
  switch (scene->priv->dtPolicy)
    {
    case COLORIZATION_POLICY_FROM_FILE:
      if (!VISU_IS_DATA_LOADABLE(data))
        return;

      file = g_strdup(visu_data_loadable_getFilename(VISU_DATA_LOADABLE(data), 0));
      fileExtPosition = g_strrstr(file, ".");
      if (fileExtPosition)
        *fileExtPosition = '\0';
      dtFile = g_strdup_printf("%s%s", file, scene->priv->dtFileExtension);
      g_free(file);

      error = (GError*)0;
      values = visu_node_values_farray_new_fromFile
        (VISU_NODE_ARRAY(data), VISU_COLORIZATION_LABEL, dtFile, &error);
      g_free(dtFile);
      if (error)
        {
          g_warning("%s", error->message);
          g_error_free(error);
        }
      if (!scene->priv->dt)
        {
          visu_gl_node_scene_setColorization(scene, visu_colorization_new());
          g_object_unref(scene->priv->dt);
        }
      break;
    case COLORIZATION_POLICY_FROM_PREVIOUS:
      if (!scene->priv->dt)
        return;

      old = visu_sourceable_getNodeModel(VISU_SOURCEABLE(scene->priv->dt));
      if (!old)
        return;

      values = visu_node_values_farray_new(VISU_NODE_ARRAY(data),
                                           visu_node_values_getLabel(old),
                                           visu_node_values_getDimension(old));
      visu_node_values_copy(VISU_NODE_VALUES(values), old);
      break;
    default:
      return;
    }

  visu_colorization_setNodeModel(scene->priv->dt, values);

  if (values)
    {
      visu_data_removeNodeProperties
        (data, visu_node_values_getLabel(VISU_NODE_VALUES(values)));
      visu_data_addNodeProperties(data, VISU_NODE_VALUES(values));
    }
}
/**
 * visu_gl_node_scene_setColorizationPolicy:
 * @scene: a #VisuGlNodeScene object.
 * @policy: a #VisuGlNodeSceneColorizationPolicies value.
 *
 * Specifies how the colorization should be modified when a new
 * #VisuData is associated to @scene.
 *
 * Since: 3.8
 *
 * Returns: TRUE if value is actually changed.
 **/
gboolean visu_gl_node_scene_setColorizationPolicy(VisuGlNodeScene *scene,
                                                  VisuGlNodeSceneColorizationPolicies policy)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), FALSE);

  if (scene->priv->dtPolicy == policy)
    return FALSE;

  scene->priv->dtPolicy = policy;
  g_object_notify_by_pspec(G_OBJECT(scene), _properties[AUTO_COLORIZATION_PROP]);

  return TRUE;
}

/**
 * visu_gl_node_scene_colorizeFragments:
 * @scene: a #VisuGlNodeScene object.
 *
 * Apply colorization depending on fragments if they are defined.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): the associated #VisuDataColorizerFragment
 * object.
 **/
VisuDataColorizerFragment* visu_gl_node_scene_colorizeFragments(VisuGlNodeScene *scene)
{
  VisuNodeValues *frags;
  VisuData *data;
  
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), FALSE);

  data = _getData(scene);
  if (!data)
    return (VisuDataColorizerFragment*)0;
  
  if (!scene->priv->fragColorizer)
    {
      scene->priv->fragColorizer = visu_data_colorizer_fragment_new();
      visu_gl_node_scene_addMasker(scene, VISU_NODE_MASKER(scene->priv->fragColorizer));
    }

  frags = visu_data_getNodeProperties(data, _("Fragment"));
  visu_data_colorizer_fragment_setNodeModel(scene->priv->fragColorizer,
                                            VISU_NODE_VALUES_FRAG(frags));
  visu_data_colorizer_setActive(VISU_DATA_COLORIZER(scene->priv->fragColorizer),
                                frags != (VisuNodeValues*)0);

  if (frags != (VisuNodeValues*)0)
    visu_node_array_renderer_pushColorizer
      (VISU_NODE_ARRAY_RENDERER(scene->priv->nodes),
       VISU_DATA_COLORIZER(scene->priv->fragColorizer));
  else
    visu_node_array_renderer_removeColorizer
      (VISU_NODE_ARRAY_RENDERER(scene->priv->nodes),
       VISU_DATA_COLORIZER(scene->priv->fragColorizer));
  
  return visu_data_colorizer_getActive(VISU_DATA_COLORIZER(scene->priv->fragColorizer)) ? scene->priv->fragColorizer : (VisuDataColorizerFragment*)0;
}

/**
 * visu_gl_node_scene_getFragmentColorizer:
 * @scene: a #VisuGlNodeScene object.
 *
 * Retrieves the default fragment colorizer object of @scene.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): the default #VisuDataColorizerFragment.
 **/
VisuDataColorizerFragment* visu_gl_node_scene_getFragmentColorizer(VisuGlNodeScene *scene)
{
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), (VisuDataColorizerFragment*)0);

  return scene->priv->fragColorizer;
}

gboolean _doTick(VisuGlNodeScene *scene)
{
  GList *lst, *del;
  gulong msecs;
  gdouble secs;

  for (lst = scene->priv->animations; lst;)
    {
      secs = g_timer_elapsed(scene->priv->timer, &msecs);
      if (visu_animation_animate(VISU_ANIMATION(lst->data),
                                 msecs + (gulong)secs * 1e6))
        lst = g_list_next(lst);
      else
        {
          del = lst;
          g_object_unref(del->data);
          if (del->prev)
            del->prev->next = g_list_next(del);
          else
            scene->priv->animations = g_list_next(del);
          lst = g_list_next(del);
          if (lst)
            lst->prev = del->prev;
          g_list_free_1(del);
        }
  }

  if (scene->priv->animations)
    return G_SOURCE_CONTINUE;

  g_timer_stop(scene->priv->timer);
  scene->priv->tickId = 0;
  return G_SOURCE_REMOVE;
}
static gboolean _onAnimate(VisuGlNodeScene *scene, VisuAnimation *anim,
                           const GValue *to, gulong duration, gboolean loop,
                           VisuAnimationType type)
{
  gulong msecs;
  gdouble secs;
  
  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), FALSE);

  DBG_fprintf(stderr, "Visu GlNodeScene: caught 'animate' signal.\n");
  if ((scene->priv->animations == (GList*)0))
    {
      g_timer_start(scene->priv->timer);
      scene->priv->tickId = g_timeout_add(15, (GSourceFunc)_doTick, scene);
    }
    
  if (!g_list_find(scene->priv->animations, anim))
      scene->priv->animations =
        g_list_prepend(scene->priv->animations, g_object_ref(anim));
  secs = g_timer_elapsed(scene->priv->timer, &msecs);
  return visu_animation_start(anim, to, msecs + (gulong)secs * 1e6, duration, loop, type);
}

static void exportParameters(GString *data, VisuData* dataObj _U_)
{
  g_string_append_printf(data, "# %s\n", DESC_PARAMETER_FILEEXT);
  g_string_append_printf(data, "%s: %s\n\n", FLAG_PARAMETER_FILEEXT, fileExt);
}

/**
 * visu_gl_node_scene_applyCLI:
 * @scene: a #VisuGlNodeScene object to apply the options on ;
 * @error: a location for error report.
 *
 * Call all the get methods on the command line options to tune the
 * given @data.
 *
 * Returns: TRUE if complete without error.
 */
gboolean visu_gl_node_scene_applyCLI(VisuGlNodeScene *scene, GError **error)
{
  gchar *planesFile, *surfFile;
  const GList *fieldFiles;
  int i, nb;
  gboolean somethingIsLoaded, valid;
  float *values;
  const gchar **names;
  const GList *it;
  GHashTable *table;
  VisuSurface *surf, *neg;
  VisuScalarfieldSetIter itField;
  ToolShade *shade;
  GList *surfsList, *lst;
  gint *mapPlaneId;
  VisuScalarField *field;
  ToolColor *color;
  VisuMap *map;
  VisuColorization *dt;
  const gchar *geoFile;
  VisuDataAtomic *geoRef;
  VisuData *data;

  g_return_val_if_fail(VISU_IS_GL_NODE_SCENE(scene), FALSE);
  g_return_val_if_fail(error && !*error, FALSE);

  data = _getData(scene);

  /* The shade option. */
  shade = tool_pool_getById(tool_shade_getStorage(),
                            MAX(commandLineGet_presetColor(), 0));
  if (!shade)
    shade = tool_pool_getById(tool_shade_getStorage(), 0);
  /* Marks from file. */
  if (commandLineGet_valueFile()
      && !visu_gl_node_scene_setMarksFromFile(scene,
                                              commandLineGet_valueFile(), error))
    return FALSE;
  /* colorize argument */
  dt = visu_colorization_new_fromCLI(data, error);
  if (error && *error)
    return FALSE;
  visu_gl_node_scene_setColorization(scene, dt);
  if (dt)
    g_object_unref(dt);
  visu_data_applyTransformationsFromCLI(data, error);
  if (error && *error)
    return FALSE;
  /* VisuPlanes argument. */
  planesFile = commandLineGet_planesFileName()
    ? commandLineGet_planesFileName()
    : commandLineGet_valueFile();
  if (planesFile)
    {
      DBG_fprintf(stderr, "Visu Basic: create plane extension.\n");
      visu_gl_node_scene_addPlanes(scene);
      somethingIsLoaded = visu_plane_set_parseXMLFile(scene->priv->planes->planes,
                                                      planesFile, error);
      if (!somethingIsLoaded)
	return FALSE;
    }
  /* Iso-surfaces arguments. */
  surfFile = commandLineGet_isoVisuSurfaceFileName();
  surfsList = (GList*)0;
  if (surfFile)
    {
      somethingIsLoaded = visu_surface_loadFile(surfFile, &surfsList, error);
      if (!somethingIsLoaded)
        return FALSE;
      else if (commandLineGet_fitToBox())
        for (lst = surfsList; lst; lst = g_list_next(lst))
          {
            g_object_set(lst->data, "auto-adjust", TRUE, NULL);
            visu_boxed_setBox(VISU_BOXED(lst->data), VISU_BOXED(data));
          }
    }
  /* Scalar-field arguments. */
  field = (VisuScalarField*)0;
  fieldFiles = commandLineGet_scalarFieldFileNames();
  if (fieldFiles)
    {
      /* Create an option table for possible spin or complex
	 modifiers. */
      table = commandLineGet_options();
      for (it = fieldFiles; it; it = g_list_next(it))
        if (!visu_scalarfield_set_addFromFileSync
            (visu_scalarfield_set_getDefault(), NULL, (const gchar*)it->data, table, NULL, error))
          return FALSE;

      visu_scalarfield_set_iter_new(visu_scalarfield_set_getDefault(), &itField);
      if (commandLineGet_fitToBox())
        for (valid = visu_scalarfield_set_iter_next(&itField); valid;
             valid = visu_scalarfield_set_iter_next(&itField))
          {
            g_object_set(G_OBJECT(itField.field), "auto-adjust", TRUE, NULL);
            visu_boxed_setBox(VISU_BOXED(itField.field), VISU_BOXED(data));
          }
      values    = commandLineGet_isoValues(&nb);
      names     = commandLineGet_isoNames(&nb);
      visu_scalarfield_set_iter_new(visu_scalarfield_set_getDefault(), &itField);
      if (values)
        for (valid = visu_scalarfield_set_iter_next(&itField); valid;
             valid = visu_scalarfield_set_iter_next(&itField))
          for (i = 0; i < nb; i++)
            {
              if (values[i] != G_MAXFLOAT)
                surf = visu_surface_new_fromScalarField(itField.field,
                                                        values[i], names[i]);
              else
                {
                  visu_surface_new_defaultFromScalarField(itField.field, &neg, &surf);
                  if (neg)
                    surfsList = g_list_append(surfsList, neg);
                }
              if (surf)
                surfsList = g_list_append(surfsList, surf);
            }
      field = visu_scalarfield_set_getAt(visu_scalarfield_set_getDefault(), 0);
          
      /* We may add the iso-values from a file. */
      if (commandLineGet_valueFile() && field)
        {
          somethingIsLoaded =
            visu_surface_parseXMLFile(commandLineGet_valueFile(), &surfsList,
                                      field, error);
          if (!somethingIsLoaded)
            return FALSE;
        }
    }
  /* We create the surface extension. */
  if (surfsList)
    {
      DBG_fprintf(stderr, "Visu Basic: create surface extension.\n");
      visu_gl_node_scene_addSurfaces(scene);
      for (lst = surfsList; lst; lst = g_list_next(lst))
        visu_gl_ext_surfaces_add(scene->priv->surfaces, VISU_SURFACE(lst->data));
      g_list_free_full(surfsList, (GDestroyNotify)g_object_unref);
    }
  /* The coloured map argument. */
  mapPlaneId = (gint*)commandLineGet_coloredMap();
  if (mapPlaneId && !scene->priv->planes)
    g_warning(_("option '--build-map' has been given but"
                " no plane is available (use '--planes')."));
  else if (mapPlaneId && !field)
    g_warning(_("option '--build-map' has been given but"
                " no scalar field is available (use '--scalar-field')."));
  else if (mapPlaneId && !shade)
    g_warning(_("option '--build-map' has been given but"
                " no shade is available (use '--color-preset')."));
  else if (mapPlaneId && field && shade)
    {
      visu_gl_node_scene_addMaps(scene, NULL);
      visu_gl_ext_map_set_setField(scene->priv->maps, field);
      visu_gl_ext_map_set_setPrecision(scene->priv->maps,
                                       commandLineGet_mapPrecision());
      visu_gl_ext_map_set_setShade(scene->priv->maps, shade);
      color = tool_color_new(commandLineGet_isoLinesColor());
      visu_gl_ext_map_set_setLineColor(scene->priv->maps, color);
      g_free(color);
      visu_gl_ext_map_set_setLines(scene->priv->maps, commandLineGet_nIsoLines());
      visu_gl_ext_map_set_setScaling(scene->priv->maps, commandLineGet_logScale());
      visu_gl_ext_map_set_setScalingRange(scene->priv->maps,
                                          commandLineGet_mapMinMax());

      for (i = 1; i <= mapPlaneId[0]; i++)
        {
          map = visu_gl_ext_map_set_addFromPlane(scene->priv->maps, visu_plane_set_getAt(scene->priv->planes->planes, mapPlaneId[i]));
          visu_map_compute_sync(map);
        }
    }
  /* We set the background image. */
  visu_gl_ext_bg_setFile(scene->priv->bg, commandLineGet_bgImage(), error);
  if (error && *error)
    return FALSE;
  /* The geometry diff. */
  geoFile = commandLineGet_geodiff();
  if (geoFile)
    {
      geoRef = visu_data_atomic_new(geoFile, (VisuDataLoader*)0);
      if (!visu_gl_node_scene_setDiffFromLoadable(scene,
                                                  VISU_DATA_LOADABLE(geoRef), error))
        return FALSE;
      g_object_unref(geoRef);
    }

  return TRUE;
}
