1 /*   EXTRAITS DE LA LICENCE
2 	Copyright CEA, contributeurs : Luc BILLARD et Damien
3 	CALISTE, laboratoire L_Sim, (2001-2005)
4 
5 	Adresse mèl :
6 	BILLARD, non joignable par mèl ;
7 	CALISTE, damien P caliste AT cea P fr.
8 
9 	Ce logiciel est un programme informatique servant à visualiser des
10 	structures atomiques dans un rendu pseudo-3D.
11 
12 	Ce logiciel est régi par la licence CeCILL soumise au droit français et
13 	respectant les principes de diffusion des logiciels libres. Vous pouvez
14 	utiliser, modifier et/ou redistribuer ce programme sous les conditions
15 	de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA
16 	sur le site "http://www.cecill.info".
17 
18 	Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
19 	pris connaissance de la licence CeCILL, et que vous en avez accepté les
20 	termes (cf. le fichier Documentation/licence.fr.txt fourni avec ce logiciel).
21 */
22 
23 /*   LICENCE SUM UP
24 	Copyright CEA, contributors : Luc BILLARD et Damien
25 	CALISTE, laboratoire L_Sim, (2001-2005)
26 
27 	E-mail address:
28 	BILLARD, not reachable any more ;
29 	CALISTE, damien P caliste AT cea P fr.
30 
31 	This software is a computer program whose purpose is to visualize atomic
32 	configurations in 3D.
33 
34 	This software is governed by the CeCILL  license under French law and
35 	abiding by the rules of distribution of free software.  You can  use,
36 	modify and/ or redistribute the software under the terms of the CeCILL
37 	license as circulated by CEA, CNRS and INRIA at the following URL
38 	"http://www.cecill.info".
39 
40 	The fact that you are presently reading this means that you have had
41 	knowledge of the CeCILL license and that you accept its terms. You can
42 	find a copy of this licence shipped with this software at Documentation/licence.en.txt.
43 */
44 #include "visu_data.h"
45 
46 
47 #include <string.h>
48 #include <stdlib.h>
49 #include <stdio.h>
50 #include <math.h>
51 
52 #include "visu_commandLine.h"
53 #include "extraFunctions/idProp.h"
54 #include "extraFunctions/typeProp.h"
55 #include "extraFunctions/coordProp.h"
56 #include "extraFunctions/geometry.h"
57 #include "extraFunctions/vibration.h"
58 #include "coreTools/toolMatrix.h"
59 
60 /**
61  * SECTION:visu_data
62  * @short_description: Give methods to store and manage data from
63  * input file(s).
64  *
65  * <para>The main goal of V_Sim is to draw lists of elements. For
66  * example, when used to render atoms, a box that contains 24 silicon
67  * atoms and 46 germanium atoms is a box with two elements (silicon
68  * and germanium) where the silicon element has 24 nodes and the
69  * germanium element has 46 nodes. This module gives then methods to
70  * create nodes (see #VisuElement to create and managed
71  * elements).</para>
72  *
73  * <para>All nodes are stored in a structure called #VisuNodeArray and
74  * #VisuNodeArray is encapsulated in a #VisuData for all not-node related
75  * information. V_Sim uses one #VisuData per input file(s). This
76  * structure contains a list of pointers on all the #VisuElement used
77  * in this file.</para>
78  *
79  * <para>To iterate on nodes, one should use the provided iterators
80  * (see #VisuNodeArrayIter) methods, like visu_node_array_iter_next().</para>
81  */
82 
83 enum {
84   NODE_PROP_ADDED_SIGNAL,
85   NODE_PROP_REMOVED_SIGNAL,
86   LAST_SIGNAL
87 };
88 
89 enum
90   {
91     PROP_0,
92     DESCR_PROP,
93     TOTAL_ENERGY_PROP,
94     N_PROP,
95     TRANS_PROP,
96     RED_TRANS_PROP,
97     USE_TRANS_PROP,
98     MODULO_PROP,
99     BOX_PROP,
100     ADJUST_PROP
101   };
102 static GParamSpec *properties[N_PROP];
103 
104 struct _VisuDataPrivate
105 {
106   gboolean dispose_has_run;
107 
108   gchar* commentary;
109 
110   /******************/
111   /* Box attributes */
112   /******************/
113   VisuBox *box;
114   float extension[3];
115   gulong unit_signal, expand_signal, expAct_signal;
116   /* Translation applied to all nodes when rendered. */
117   gboolean inTheBox, inTheBox_replicate;
118   gboolean translationActive;
119   float translation[3];
120 
121   /********************/
122   /* Misc. attributes */
123   /********************/
124   /* The total energy of the system in eV. */
125   gdouble totalEnergy;
126   /* The list of known VisuNodeValues. */
127   GHashTable *nodeProperties;
128 };
129 
130 
131 static void visu_data_dispose     (GObject* obj);
132 static void visu_data_finalize    (GObject* obj);
133 static void visu_data_get_property(GObject* obj, guint property_id,
134 				   GValue *value, GParamSpec *pspec);
135 static void visu_data_set_property(GObject* obj, guint property_id,
136 				   const GValue *value, GParamSpec *pspec);
137 static gboolean _inTheBox(VisuPointset *self, gboolean status);
138 static void _getTranslation(VisuPointset *self, float trans[3]);
139 static gboolean _setTranslation(VisuPointset *self, float trans[3], gboolean withModulo);
140 static gboolean _setTranslationActive(VisuPointset *self, gboolean status);
141 static void _applyTranslation(VisuPointset *self);
142 static void visu_boxed_interface_init(VisuBoxedInterface *iface);
143 static void visu_pointset_interface_init(VisuPointsetInterface *iface);
144 
145 /* Local callbacks. */
146 static void onBoxUnitChanged(VisuData *data, gfloat fact);
147 static void onBoxExtensChanged(VisuBox *box, GParamSpec *pspec, gpointer user_data);
148 static void onBoxExtensActive(VisuBox *box, GParamSpec *pspec, gpointer user_data);
149 
150 /* Local routines. */
151 static VisuBox* visu_data_getBox(VisuBoxed *self);
152 static gboolean visu_data_setBox(VisuBoxed *self, VisuBox *box);
153 static GArray* shrinkNodeList(VisuData *data, int coord, float valueTo);
154 static void extendNodeList(VisuData *data, int coord, float valueFrom, float valueTo);
155 static void _replicate(VisuData *data, gfloat extension[3]);
156 static gboolean _constrainedInTheBox(VisuData *data, gboolean emit);
157 static gboolean _constrainedFree(VisuData *data, gboolean emit);
158 
159 static guint visu_data_signals[LAST_SIGNAL] = { 0 };
160 
G_DEFINE_TYPE_WITH_CODE(VisuData,visu_data,VISU_TYPE_NODE_ARRAY,G_ADD_PRIVATE (VisuData)G_IMPLEMENT_INTERFACE (VISU_TYPE_BOXED,visu_boxed_interface_init)G_IMPLEMENT_INTERFACE (VISU_TYPE_POINTSET,visu_pointset_interface_init))161 G_DEFINE_TYPE_WITH_CODE(VisuData, visu_data, VISU_TYPE_NODE_ARRAY,
162                         G_ADD_PRIVATE(VisuData)
163                         G_IMPLEMENT_INTERFACE(VISU_TYPE_BOXED,
164                                               visu_boxed_interface_init)
165                         G_IMPLEMENT_INTERFACE(VISU_TYPE_POINTSET,
166                                               visu_pointset_interface_init))
167 
168 static void visu_data_class_init(VisuDataClass *klass)
169 {
170   DBG_fprintf(stderr, "Visu Data: creating the class of the object.\n");
171   DBG_fprintf(stderr, "                - adding new signals ;\n");
172   /**
173    * VisuData::node-properties-added:
174    * @dataObj: the object which received the signal ;
175    * @values: a #VisuNodeValues object.
176    *
177    * Gets emitted when @values node properties is added to @dataObj.
178    *
179    * Since: 3.8
180    */
181   visu_data_signals[NODE_PROP_ADDED_SIGNAL] =
182     g_signal_new("node-properties-added", G_TYPE_FROM_CLASS(klass),
183                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
184 		 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
185                  G_TYPE_NONE, 1, VISU_TYPE_NODE_VALUES, NULL);
186 
187   /**
188    * VisuData::node-properties-removed:
189    * @dataObj: the object which received the signal ;
190    * @values: a #VisuNodeValues object.
191    *
192    * Gets emitted when @values node properties is removed from @dataObj.
193    *
194    * Since: 3.8
195    */
196   visu_data_signals[NODE_PROP_REMOVED_SIGNAL] =
197     g_signal_new("node-properties-removed", G_TYPE_FROM_CLASS(klass),
198                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
199 		 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
200                  G_TYPE_NONE, 1, VISU_TYPE_NODE_VALUES, NULL);
201 
202   /* Connect the overloading methods. */
203   G_OBJECT_CLASS(klass)->dispose      = visu_data_dispose;
204   G_OBJECT_CLASS(klass)->finalize     = visu_data_finalize;
205   G_OBJECT_CLASS(klass)->set_property = visu_data_set_property;
206   G_OBJECT_CLASS(klass)->get_property = visu_data_get_property;
207 
208   /**
209    * VisuData::description:
210    *
211    * Store a description for the data.
212    *
213    * Since: 3.8
214    */
215   properties[DESCR_PROP] =
216     g_param_spec_string("description", "Description", "a description of the data",
217                         "", G_PARAM_READWRITE);
218   /**
219    * VisuData::totalEnergy:
220    *
221    * Store the total energy of the system in eV.
222    *
223    * Since: 3.6
224    */
225   properties[TOTAL_ENERGY_PROP] =
226     g_param_spec_double("totalEnergy", "Total energy", "Total energy of the system (eV)",
227                         -G_MAXFLOAT, G_MAXFLOAT, G_MAXFLOAT,
228                         G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
229 
230   g_object_class_install_properties(G_OBJECT_CLASS(klass), N_PROP, properties);
231 
232   g_object_class_override_property(G_OBJECT_CLASS(klass), USE_TRANS_PROP, "use-translation");
233   g_object_class_override_property(G_OBJECT_CLASS(klass), TRANS_PROP, "translation");
234   g_object_class_override_property(G_OBJECT_CLASS(klass), RED_TRANS_PROP, "reduced-translation");
235   g_object_class_override_property(G_OBJECT_CLASS(klass), MODULO_PROP, "in-the-box");
236   g_object_class_override_property(G_OBJECT_CLASS(klass), ADJUST_PROP, "auto-adjust");
237   g_object_class_override_property(G_OBJECT_CLASS(klass), BOX_PROP, "box");
238 }
visu_boxed_interface_init(VisuBoxedInterface * iface)239 static void visu_boxed_interface_init(VisuBoxedInterface *iface)
240 {
241   iface->get_box = visu_data_getBox;
242   iface->set_box = visu_data_setBox;
243 }
visu_pointset_interface_init(VisuPointsetInterface * iface)244 static void visu_pointset_interface_init(VisuPointsetInterface *iface)
245 {
246   iface->set_inTheBox = _inTheBox;
247   iface->apply_translation = _applyTranslation;
248   iface->get_translation = _getTranslation;
249   iface->set_translation = _setTranslation;
250   iface->set_translationActive = _setTranslationActive;
251 }
252 
visu_data_init(VisuData * obj)253 static void visu_data_init(VisuData *obj)
254 {
255   DBG_fprintf(stderr, "Visu Data: initializing a new object (%p).\n",
256 	      (gpointer)obj);
257 
258   obj->priv = visu_data_get_instance_private(obj);
259   obj->priv->dispose_has_run = FALSE;
260 
261   /* Private data. */
262   obj->priv->commentary       = g_strdup("");
263   obj->priv->box              = (VisuBox*)0;
264   obj->priv->unit_signal      = 0;
265   obj->priv->expand_signal    = 0;
266   obj->priv->extension[0]     = 0.f;
267   obj->priv->extension[1]     = 0.f;
268   obj->priv->extension[2]     = 0.f;
269   obj->priv->inTheBox         = FALSE;
270   obj->priv->inTheBox_replicate = FALSE;
271   obj->priv->translationActive = FALSE;
272   obj->priv->translation[0]  = 0.f;
273   obj->priv->translation[1]  = 0.f;
274   obj->priv->translation[2]  = 0.f;
275   obj->priv->nodeProperties   = g_hash_table_new_full(g_str_hash, g_str_equal,
276                                                       NULL,
277                                                       (GDestroyNotify)g_object_unref);;
278 
279   /* Ensure that the base (label, ...) property exists. */
280   visu_data_addNodeProperties(obj, VISU_NODE_VALUES(visu_node_values_id_new(VISU_NODE_ARRAY(obj))));
281   visu_data_addNodeProperties(obj, VISU_NODE_VALUES(visu_node_values_type_new(VISU_NODE_ARRAY(obj))));
282   visu_data_addNodeProperties(obj, VISU_NODE_VALUES(visu_node_values_coord_new(obj)));
283   visu_data_getNodeLabels(obj);
284 }
285 
286 /* This method can be called several times.
287    It should unref all of its reference to
288    GObjects. */
visu_data_dispose(GObject * obj)289 static void visu_data_dispose(GObject* obj)
290 {
291   VisuData *data;
292 
293   DBG_fprintf(stderr, "Visu Data: dispose object %p.\n", (gpointer)obj);
294 
295   data = VISU_DATA(obj);
296   if (data->priv->dispose_has_run)
297     return;
298   data->priv->dispose_has_run = TRUE;
299 
300   visu_data_setBox(VISU_BOXED(data), (VisuBox*)0);
301 
302   /* Chain up to the parent class */
303   G_OBJECT_CLASS(visu_data_parent_class)->dispose(obj);
304 }
305 /* This method is called once only. */
visu_data_finalize(GObject * obj)306 static void visu_data_finalize(GObject* obj)
307 {
308   VisuData *data;
309 
310   g_return_if_fail(obj);
311 
312   DBG_fprintf(stderr, "Visu Data: finalize object %p.\n", (gpointer)obj);
313 
314   data = VISU_DATA(obj);
315 
316   /* Free privs elements. */
317   if (data->priv)
318     {
319       DBG_fprintf(stderr, "Visu data: free private data.\n");
320       g_free(data->priv->commentary);
321       g_hash_table_destroy(data->priv->nodeProperties);
322     }
323   /* The free is called by g_type_free_instance... */
324   /*   g_free(data); */
325 
326   /* Chain up to the parent class */
327   DBG_fprintf(stderr, "Visu data: chain to parent.\n");
328   G_OBJECT_CLASS(visu_data_parent_class)->finalize(obj);
329   DBG_fprintf(stderr, "Visu data: freeing ... OK.\n");
330 }
visu_data_get_property(GObject * obj,guint property_id,GValue * value,GParamSpec * pspec)331 static void visu_data_get_property(GObject* obj, guint property_id,
332 				   GValue *value, GParamSpec *pspec)
333 {
334   VisuData *self = VISU_DATA(obj);
335   gfloat *redTrans;
336 
337   DBG_fprintf(stderr, "Visu Data: get property '%s' -> ",
338 	      g_param_spec_get_name(pspec));
339   switch (property_id)
340     {
341     case DESCR_PROP:
342       g_value_set_string(value, self->priv->commentary);
343       DBG_fprintf(stderr, "%s.\n", self->priv->commentary);
344       break;
345     case TOTAL_ENERGY_PROP:
346       g_value_set_double(value, self->priv->totalEnergy);
347       DBG_fprintf(stderr, "%geV.\n", self->priv->totalEnergy);
348       break;
349     case USE_TRANS_PROP:
350       g_value_set_boolean(value, self->priv->translationActive);
351       DBG_fprintf(stderr, "%d.\n", self->priv->translationActive);
352       break;
353     case TRANS_PROP:
354       g_value_set_static_boxed(value, self->priv->translation);
355       DBG_fprintf(stderr, "%gx%gx%g.\n", self->priv->translation[0], self->priv->translation[1], self->priv->translation[2]);
356       break;
357     case RED_TRANS_PROP:
358       redTrans = g_malloc(sizeof(gfloat) * 3);
359       visu_box_convertXYZtoBoxCoordinates(self->priv->box,
360                                           redTrans, self->priv->translation);
361       g_value_take_boxed(value, redTrans);
362       DBG_fprintf(stderr, "%gx%gx%g.\n", redTrans[0], redTrans[1], redTrans[2]);
363       break;
364     case MODULO_PROP:
365       g_value_set_boolean(value, self->priv->inTheBox);
366       DBG_fprintf(stderr, "%d.\n", self->priv->inTheBox);
367       break;
368     case BOX_PROP:
369       g_value_set_object(value, self->priv->box);
370       DBG_fprintf(stderr, "%p.\n", (gpointer)self->priv->box);
371       break;
372     case ADJUST_PROP:
373       g_object_get_property(G_OBJECT(self->priv->box), "auto-adjust", value);
374       DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
375       break;
376     default:
377       /* We don't have any other property... */
378       G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
379       break;
380     }
381 }
visu_data_set_property(GObject * obj,guint property_id,const GValue * value,GParamSpec * pspec)382 static void visu_data_set_property(GObject* obj, guint property_id,
383 				   const GValue *value, GParamSpec *pspec)
384 {
385   VisuData *self = VISU_DATA(obj);
386 
387   DBG_fprintf(stderr, "Visu Data: set property '%s' -> ",
388 	      g_param_spec_get_name(pspec));
389   switch (property_id)
390     {
391     case DESCR_PROP:
392       DBG_fprintf(stderr, "%s.\n", g_value_get_string(value));
393       visu_data_setDescription(self, g_value_get_string(value));
394       break;
395     case TOTAL_ENERGY_PROP:
396       self->priv->totalEnergy = g_value_get_double(value);
397       DBG_fprintf(stderr, "%geV.\n", self->priv->totalEnergy);
398       break;
399     case USE_TRANS_PROP:
400       visu_pointset_setTranslationActive(VISU_POINTSET(self),
401                                          g_value_get_boolean(value));
402       DBG_fprintf(stderr, "%d.\n", self->priv->translationActive);
403       break;
404     case TRANS_PROP:
405       visu_pointset_setTranslationPeriodic(VISU_POINTSET(self),
406                                            (gfloat*)g_value_get_boxed(value),
407                                            self->priv->inTheBox);
408       DBG_fprintf(stderr, "%gx%gx%g.\n", self->priv->translation[0], self->priv->translation[1], self->priv->translation[2]);
409       break;
410     case RED_TRANS_PROP:
411       visu_pointset_setBoxTranslation(VISU_POINTSET(self),
412                                       (gfloat*)g_value_get_boxed(value),
413                                       self->priv->inTheBox);
414       DBG_fprintf(stderr, "%gx%gx%g.\n", self->priv->translation[0], self->priv->translation[1], self->priv->translation[2]);
415       break;
416     case MODULO_PROP:
417       visu_pointset_setInTheBox(VISU_POINTSET(self), g_value_get_boolean(value));
418       DBG_fprintf(stderr, "%d.\n", self->priv->inTheBox);
419       break;
420     case BOX_PROP:
421       DBG_fprintf(stderr, "%p.\n", g_value_get_object(value));
422       visu_data_setBox(VISU_BOXED(obj), VISU_BOX(g_value_get_object(value)));
423       break;
424     case ADJUST_PROP:
425       DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
426       g_object_set_property(G_OBJECT(self->priv->box), "auto-adjust", value);
427       break;
428     default:
429       /* We don't have any other property... */
430       G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
431       break;
432     }
433 }
434 
435 
436 /**
437  * visu_data_new:
438  *
439  * This creates an empty #VisuData object.
440  *
441  * Returns: a newly created #VisuData object (its ref count is set to 1).
442  */
visu_data_new(void)443 VisuData* visu_data_new(void)
444 {
445   VisuData *data;
446 
447   DBG_fprintf(stderr, "Visu Data: create a new VisuData object of type %d.\n",
448               (int)VISU_TYPE_DATA);
449   data = VISU_DATA(g_object_new(VISU_TYPE_DATA, NULL));
450 
451   if (!data)
452     return (VisuData*)0;
453 
454   return data;
455 }
456 
457 /**
458  * visu_data_freePopulation:
459  * @data: a VisuData to be freed.
460  *
461  * This method frees only the allocated memory that deals with
462  * the nodes (i.e. everything except the data of the files,
463  * the properties and the setColor method.
464  */
visu_data_freePopulation(VisuData * data)465 void visu_data_freePopulation(VisuData *data)
466 {
467   float zeros[3] = {0.f, 0.f, 0.f};
468 
469   if (!data)
470     return;
471 
472   DBG_fprintf(stderr, "Visu Data: freeing the population of VisuData %p ...\n",
473               (gpointer)data);
474   visu_node_array_freeNodes(VISU_NODE_ARRAY(data));
475 
476   if (data->priv->box)
477     {
478       visu_box_setExtension(data->priv->box, zeros);
479       visu_box_setExtensionActive(data->priv->box, FALSE);
480       visu_pointset_setTranslationPeriodic(VISU_POINTSET(data), zeros, FALSE);
481     }
482 
483   DBG_fprintf(stderr, "Visu Data: freeing ... OK.\n");
484 }
485 
486 /**
487  * visu_data_setDescription:
488  * @data: a #VisuData object ;
489  * @commentary: the message to be stored (null terminated) ;
490  *
491  * This method is used to store a description of the given @data. This
492  * string is copied and @commentary can be freed.
493  */
visu_data_setDescription(VisuData * data,const gchar * commentary)494 void visu_data_setDescription(VisuData *data, const gchar* commentary)
495 {
496   g_return_if_fail(VISU_IS_DATA(data));
497 
498   g_free(data->priv->commentary);
499   data->priv->commentary = g_strdup(commentary);
500 
501   g_object_notify_by_pspec(G_OBJECT(data), properties[DESCR_PROP]);
502 }
503 
504 /**
505  * visu_data_getDescription:
506  * @data: a #VisuData object ;
507  *
508  * Get the commentary associated to the given @data.
509  *
510  * Returns: (transfer none): a string description (possibly
511  * empty). This string is own by V_Sim and should not be freed.
512  */
visu_data_getDescription(const VisuData * data)513 const gchar* visu_data_getDescription(const VisuData *data)
514 {
515   g_return_val_if_fail(VISU_IS_DATA(data), (gchar*)0);
516 
517   return data->priv->commentary;
518 }
519 
520 /*************************/
521 /* The geometry routines */
522 /*************************/
visu_data_getBox(VisuBoxed * self)523 static VisuBox* visu_data_getBox(VisuBoxed *self)
524 {
525   g_return_val_if_fail(VISU_IS_DATA(self), (VisuBox*)0);
526 
527   return VISU_DATA(self)->priv->box;
528 }
visu_data_setBox(VisuBoxed * self,VisuBox * box)529 static gboolean visu_data_setBox(VisuBoxed *self, VisuBox *box)
530 {
531   VisuData *data;
532 
533   g_return_val_if_fail(VISU_IS_DATA(self), FALSE);
534   data = VISU_DATA(self);
535 
536   if (data->priv->box == box)
537     return FALSE;
538 
539   if (data->priv->box)
540     {
541       g_signal_handler_disconnect(G_OBJECT(data->priv->box), data->priv->unit_signal);
542       g_signal_handler_disconnect(G_OBJECT(data->priv->box), data->priv->expand_signal);
543       g_signal_handler_disconnect(G_OBJECT(data->priv->box), data->priv->expAct_signal);
544       g_object_unref(data->priv->box);
545     }
546   data->priv->box = box;
547   if (box)
548     {
549       g_object_ref(box);
550       data->priv->unit_signal =
551         g_signal_connect_swapped(G_OBJECT(data->priv->box), "UnitChanged",
552                                  G_CALLBACK(onBoxUnitChanged), data);
553       data->priv->expand_signal =
554         g_signal_connect(G_OBJECT(data->priv->box), "notify::expansion",
555                          G_CALLBACK(onBoxExtensChanged), (gpointer)data);
556       data->priv->expAct_signal =
557         g_signal_connect(G_OBJECT(data->priv->box), "notify::use-expansion",
558                          G_CALLBACK(onBoxExtensActive), (gpointer)data);
559     }
560   return TRUE;
561 }
562 
_inTheBox(VisuPointset * self,gboolean status)563 static gboolean _inTheBox(VisuPointset *self, gboolean status)
564 {
565   if (status)
566     _constrainedInTheBox(VISU_DATA(self), TRUE);
567   else
568     _constrainedFree(VISU_DATA(self), TRUE);
569   return TRUE;
570 }
_applyTranslation(VisuPointset * self)571 static void _applyTranslation(VisuPointset *self)
572 {
573   VisuNodeArrayIter iter;
574   float xyz[3], zeros[3] = {0.f, 0.f, 0.f};
575   gboolean rendered;
576 
577   visu_node_array_iter_new(VISU_NODE_ARRAY(self), &iter);
578   for (visu_node_array_iterStart(VISU_NODE_ARRAY(self), &iter); iter.node;
579        visu_node_array_iterNext(VISU_NODE_ARRAY(self), &iter))
580     {
581       visu_data_getNodePosition(VISU_DATA(self), iter.node, xyz);
582       rendered = visu_node_getVisibility(iter.node);
583       visu_node_newValues(iter.node, xyz);
584       visu_node_setVisibility(iter.node, rendered);
585     }
586   _setTranslation(self, zeros, FALSE);
587 }
_getTranslation(VisuPointset * self,float trans[3])588 static void _getTranslation(VisuPointset *self, float trans[3])
589 {
590   VisuDataPrivate *priv;
591 
592   g_return_if_fail(VISU_IS_DATA(self));
593   priv = VISU_DATA(self)->priv;
594 
595   trans[0] = priv->translation[0];
596   trans[1] = priv->translation[1];
597   trans[2] = priv->translation[2];
598 }
_setTranslation(VisuPointset * self,float trans[3],gboolean withModulo)599 static gboolean _setTranslation(VisuPointset *self, float trans[3], gboolean withModulo)
600 {
601   VisuDataPrivate *priv;
602   gboolean res, changed;
603 
604   g_return_val_if_fail(VISU_IS_DATA(self), FALSE);
605   priv = VISU_DATA(self)->priv;
606 
607   res = FALSE;
608   if (priv->translation[0] != trans[0])
609     {
610       priv->translation[0] = trans[0];
611       res = TRUE;
612     }
613   if (priv->translation[1] != trans[1])
614     {
615       priv->translation[1] = trans[1];
616       res = TRUE;
617     }
618   if (priv->translation[2] != trans[2])
619     {
620       priv->translation[2] = trans[2];
621       res = TRUE;
622     }
623   DBG_fprintf(stderr, "Visu Data: force translation to: %f %f %f\n",
624 	      priv->translation[0], priv->translation[1], priv->translation[2]);
625   if (res)
626     g_object_notify(G_OBJECT(self), "translation");
627 
628   changed = FALSE;
629   if (withModulo)
630     changed = _constrainedInTheBox(VISU_DATA(self), FALSE);
631 
632   if ((res && priv->translationActive) || changed)
633     g_signal_emit_by_name(G_OBJECT(self), "position-changed", (GArray*)0, NULL);
634 
635   return res;
636 }
_setTranslationActive(VisuPointset * self,gboolean status)637 static gboolean _setTranslationActive(VisuPointset *self, gboolean status)
638 {
639   VisuData *data;
640   gboolean changed;
641 
642   g_return_val_if_fail(VISU_IS_DATA(self), FALSE);
643 
644   data = VISU_DATA(self);
645   if (data->priv->translationActive == status)
646     return FALSE;
647 
648   data->priv->translationActive = status;
649   g_object_notify(G_OBJECT(self), "use-translation");
650 
651   changed = FALSE;
652   if (data->priv->inTheBox)
653     changed = _constrainedInTheBox(VISU_DATA(self), FALSE);
654   if (data->priv->translation[0] != 0.f ||
655       data->priv->translation[1] != 0.f ||
656       data->priv->translation[2] != 0.f ||
657       changed)
658     g_signal_emit_by_name(G_OBJECT(self), "position-changed", (GArray*)0, NULL);
659   return TRUE;
660 }
661 
662 /**
663  * visu_data_getNodeBoxFromNumber:
664  * @data: a #VisuData object.
665  * @nodeId: the index of the node considered.
666  * @nodeBox: (in) (array fixed-size=3): the array to store the box of the node.
667  *
668  * This method retrieves the value of the box associated to a node (with respect to the unit cell).
669  *
670  * Returns: TRUE if everything went well, FALSE otherwise. The box is stored in the nodeBox array.
671  */
visu_data_getNodeBoxFromNumber(VisuData * data,guint nodeId,int nodeBox[3])672 gboolean visu_data_getNodeBoxFromNumber(VisuData *data, guint nodeId, int nodeBox[3])
673 {
674   float xcart[3];
675 
676   g_return_val_if_fail(VISU_IS_DATA(data), FALSE);
677 
678   visu_data_getNodePosition(data, visu_node_array_getFromId(VISU_NODE_ARRAY(data),nodeId), xcart);
679   visu_data_getNodeBoxFromCoord(data, xcart, nodeBox);
680   return TRUE;
681 }
682 
683 /**
684  * visu_data_getNodeBoxFromCoord:
685  * @data: a #VisuData object.
686  * @xcart: (in) (array fixed-size=3): the coordinates of a node.
687  * @nodeBox: (in) (array fixed-size=3): the array to store the box of the node.
688  *
689  * This method retrieves the value of the box associated to the coordinates of the node (with respect to the unit cell).
690  *
691  * Returns: TRUE if everything went well, FALSE otherwise. The box is stored in the nodeBox array.
692  */
visu_data_getNodeBoxFromCoord(VisuData * data,float xcart[3],int nodeBox[3])693 gboolean visu_data_getNodeBoxFromCoord(VisuData *data, float xcart[3], int nodeBox[3])
694 {
695   float xred[3];
696 
697   visu_box_convertXYZtoBoxCoordinates(data->priv->box, xred, xcart);
698   nodeBox[0] = floor(xred[0]);
699   nodeBox[1] = floor(xred[1]);
700   nodeBox[2] = floor(xred[2]);
701   DBG_fprintf(stderr, "Visu Data: nodeBox found for atom at %f %f %f : %d %d %d.\n",
702 		xcart[0], xcart[1], xcart[2], nodeBox[0], nodeBox[1], nodeBox[2]);
703   return TRUE;
704 }
705 
onBoxUnitChanged(VisuData * data,gfloat fact)706 static void onBoxUnitChanged(VisuData *data, gfloat fact)
707 {
708   VisuNodeArrayIter iter;
709 
710   DBG_fprintf(stderr, "Visu Data: caught 'UnitChanged' signal with factor %g.\n", fact);
711 
712   /* We do an homothety on the nodes. */
713   data->priv->translation[0] *= fact;
714   data->priv->translation[1] *= fact;
715   data->priv->translation[2] *= fact;
716   visu_node_array_iter_new(VISU_NODE_ARRAY(data), &iter);
717   for( visu_node_array_iterStart(VISU_NODE_ARRAY(data), &iter); iter.node;
718        visu_node_array_iterNext(VISU_NODE_ARRAY(data), &iter))
719     {
720       iter.node->xyz[0] *= fact;
721       iter.node->xyz[1] *= fact;
722       iter.node->xyz[2] *= fact;
723       iter.node->translation[0] *= fact;
724       iter.node->translation[1] *= fact;
725       iter.node->translation[2] *= fact;
726     }
727   /* We raise the signals. */
728   g_signal_emit_by_name(G_OBJECT(data), "position-changed", (GArray*)0, NULL);
729 
730   DBG_fprintf(stderr, "Visu Data: done 'UnitChanged'.\n");
731 }
onBoxExtensChanged(VisuBox * box,GParamSpec * pspec _U_,gpointer user_data)732 static void onBoxExtensChanged(VisuBox *box, GParamSpec *pspec _U_, gpointer user_data)
733 {
734   gfloat ext[3];
735 
736   if (!visu_box_getExtensionActive(box))
737     return;
738 
739   visu_box_getExtension(box, ext);
740   _replicate(VISU_DATA(user_data), ext);
741 }
onBoxExtensActive(VisuBox * box,GParamSpec * pspec _U_,gpointer user_data)742 static void onBoxExtensActive(VisuBox *box, GParamSpec *pspec _U_, gpointer user_data)
743 {
744   VisuData *data;
745   gfloat ext[3];
746 
747   if (visu_box_getExtensionActive(box))
748     {
749       visu_box_getExtension(box, ext);
750       _replicate(VISU_DATA(user_data), ext);
751     }
752   else
753     {
754       visu_node_array_removeAllDuplicateNodes(VISU_NODE_ARRAY(user_data));
755       data = VISU_DATA(user_data);
756       data->priv->extension[0] = 0.f;
757       data->priv->extension[1] = 0.f;
758       data->priv->extension[2] = 0.f;
759       if (data->priv->inTheBox_replicate)
760         {
761           _constrainedFree(data, TRUE);
762           data->priv->inTheBox_replicate = FALSE;
763         }
764     }
765 }
766 
_constrainedElementInTheBox(VisuData * data,VisuElement * element,gboolean emit)767 static gboolean _constrainedElementInTheBox(VisuData *data, VisuElement *element,
768                                             gboolean emit)
769 {
770   gboolean changed;
771   float cartCoord[3], t[3];
772   VisuNodeArrayIter iter;
773 
774   g_return_val_if_fail(VISU_IS_DATA(data) && element, FALSE);
775 
776   if (!visu_element_getRendered(element))
777     return FALSE;
778 
779   DBG_fprintf(stderr, "Visu Data: Checking for nodes of element '%s'"
780 	      " to be in the box.\n", element->name);
781   changed = FALSE;
782   visu_node_array_iter_new(VISU_NODE_ARRAY(data), &iter);
783   iter.element = element;
784   for(visu_node_array_iterRestartNode(VISU_NODE_ARRAY(data), &iter); iter.node;
785       visu_node_array_iterNextNode(VISU_NODE_ARRAY(data), &iter))
786     {
787       visu_data_getNodePosition(data, iter.node, cartCoord);
788       if (visu_box_constrainInside(data->priv->box, t, cartCoord, FALSE))
789 	{
790 	  changed = TRUE;
791 	  iter.node->translation[0] += t[0];
792 	  iter.node->translation[1] += t[1];
793 	  iter.node->translation[2] += t[2];
794 	}
795     }
796   if (changed && emit)
797     g_signal_emit_by_name(G_OBJECT(data), "position-changed", (GArray*)0, NULL);
798   return changed;
799 }
800 
_constrainedInTheBox(VisuData * data,gboolean emit)801 static gboolean _constrainedInTheBox(VisuData *data, gboolean emit)
802 {
803   VisuNodeArrayIter iter;
804   gboolean changed;
805 
806   g_return_val_if_fail(VISU_IS_DATA(data), FALSE);
807 
808   data->priv->inTheBox = TRUE;
809   g_object_notify(G_OBJECT(data), "in-the-box");
810 
811   changed = FALSE;
812   visu_node_array_iter_new(VISU_NODE_ARRAY(data), &iter);
813   for (visu_node_array_iterStart(VISU_NODE_ARRAY(data), &iter); iter.element;
814        visu_node_array_iterNextElement(VISU_NODE_ARRAY(data), &iter, FALSE))
815     changed = _constrainedElementInTheBox(data, iter.element, emit) || changed;
816   return changed;
817 }
_constrainedFree(VisuData * data,gboolean emit)818 static gboolean _constrainedFree(VisuData *data, gboolean emit)
819 {
820   VisuNodeArrayIter iter;
821   gboolean moved;
822 
823   g_return_val_if_fail(VISU_IS_DATA(data), FALSE);
824 
825   data->priv->inTheBox = FALSE;
826   g_object_notify(G_OBJECT(data), "in-the-box");
827 
828   moved = FALSE;
829   visu_node_array_iter_new(VISU_NODE_ARRAY(data), &iter);
830   for (visu_node_array_iterStart(VISU_NODE_ARRAY(data), &iter); iter.node;
831        visu_node_array_iterNext(VISU_NODE_ARRAY(data), &iter))
832     {
833       moved = moved || iter.node->translation[0] != 0. ||
834         iter.node->translation[1] != 0. ||
835         iter.node->translation[2] != 0.;
836       iter.node->translation[0] = 0.;
837       iter.node->translation[1] = 0.;
838       iter.node->translation[2] = 0.;
839     }
840   if (emit && moved)
841     g_signal_emit_by_name(G_OBJECT(data), "position-changed", (GArray*)0, NULL);
842   return TRUE;
843 }
844 
845 /**
846  * visu_data_setTightBox:
847  * @data: a #VisuData object.
848  *
849  * Calculate the box geometry to have a tight box in directions that
850  * are not periodic. If some directions are still periodic, the box
851  * size in these directions should be setup first with
852  * visu_box_setGeometry().
853  *
854  * Returns: (transfer none): a new #VisuBox if @data had not one
855  * before, or the modified box of @data.
856  */
visu_data_setTightBox(VisuData * data)857 VisuBox* visu_data_setTightBox(VisuData *data)
858 {
859   double xMin, yMin, zMin, xMax, yMax, zMax, xFree, yFree, zFree;
860   double boxGeometry[6], boxGeometry_[6];
861   float xyz[3];
862   VisuNodeArrayIter iter;
863   VisuBoxBoundaries bc;
864   guint i;
865   VisuBox *box;
866 
867   g_return_val_if_fail(VISU_IS_DATA(data), (VisuBox*)0);
868 
869   if (!data->priv->box)
870     {
871       for (i = 0; i < VISU_BOX_N_VECTORS; i++)
872         boxGeometry_[i] = 0.;
873       box = visu_box_new(boxGeometry_, VISU_BOX_FREE);
874       visu_boxed_setBox(VISU_BOXED(data), VISU_BOXED(box));
875       g_object_unref(box);
876     }
877   bc = visu_box_getBoundary(data->priv->box);
878   if (bc == VISU_BOX_PERIODIC)
879     return data->priv->box;
880 
881   /* Store the coordinates */
882   xMin = 1e5;
883   yMin = 1e5;
884   zMin = 1e5;
885   xMax = -1e5;
886   yMax = -1e5;
887   zMax = -1e5;
888   xFree = (bc & TOOL_XYZ_MASK_X)?0.:1.;
889   yFree = (bc & TOOL_XYZ_MASK_Y)?0.:1.;
890   zFree = (bc & TOOL_XYZ_MASK_Z)?0.:1.;
891 
892   visu_node_array_iter_new(VISU_NODE_ARRAY(data), &iter);
893   for (visu_node_array_iterStart(VISU_NODE_ARRAY(data), &iter); iter.node;
894        visu_node_array_iterNext(VISU_NODE_ARRAY(data), &iter))
895     {
896       xMin = MIN(xMin, iter.node->xyz[0]);
897       yMin = MIN(yMin, iter.node->xyz[1]);
898       zMin = MIN(zMin, iter.node->xyz[2]);
899       xMax = MAX(xMax, iter.node->xyz[0]);
900       yMax = MAX(yMax, iter.node->xyz[1]);
901       zMax = MAX(zMax, iter.node->xyz[2]);
902     }
903 
904   DBG_fprintf(stderr, "Visu Data: the elements are in [%f, %f]x[%f, %f]x[%f, %f].\n",
905 	      xMin, xMax, yMin, yMax, zMin, zMax);
906   for (i = 0; i < VISU_BOX_N_VECTORS; i++)
907     boxGeometry_[i] = visu_box_getGeometry(data->priv->box, i);
908   boxGeometry[0] = (xMax - xMin + 1e-5) * xFree + (1. - xFree) * boxGeometry_[0];
909   boxGeometry[1] = 0.                           + (1. - yFree) * boxGeometry_[1];
910   boxGeometry[2] = (yMax - yMin + 1e-5) * yFree + (1. - yFree) * boxGeometry_[2];
911   boxGeometry[3] = 0.                           + (1. - zFree) * boxGeometry_[3];
912   boxGeometry[4] = 0.                           + (1. - zFree) * boxGeometry_[4];
913   boxGeometry[5] = (zMax - zMin + 1e-5) * zFree + (1. - zFree) * boxGeometry_[5];
914   visu_box_setGeometry(data->priv->box, boxGeometry);
915 
916   xyz[0] = -xMin * xFree;
917   xyz[1] = -yMin * yFree;
918   xyz[2] = -zMin * zFree;
919   visu_pointset_setTranslation(VISU_POINTSET(data), xyz, FALSE);
920   visu_pointset_setTranslationActive(VISU_POINTSET(data), TRUE);
921 
922   return data->priv->box;
923 }
924 
_replicate(VisuData * data,gfloat extension[3])925 static void _replicate(VisuData *data, gfloat extension[3])
926 {
927   int i;
928   GArray *index;
929 
930   g_return_if_fail(VISU_IS_DATA(data));
931   g_return_if_fail(extension[0] >= 0. &&
932                    extension[1] >= 0. &&
933                    extension[2] >= 0.);
934 
935   DBG_fprintf(stderr, "Visu Data: modify extension from (%g, %g, %g).\n",
936 	      data->priv->extension[0], data->priv->extension[1], data->priv->extension[2]);
937   if (!data->priv->inTheBox)
938     {
939       _constrainedInTheBox(data, TRUE);
940       data->priv->inTheBox_replicate = TRUE;
941     }
942 
943   /* Keep only three digits for the extension to avoid rounding
944      troubles. */
945   extension[0] = (float)((int)(extension[0] * 1000)) / 1000;
946   extension[1] = (float)((int)(extension[1] * 1000)) / 1000;
947   extension[2] = (float)((int)(extension[2] * 1000)) / 1000;
948 
949   for (i = 0; i < 3; i++)
950     {
951       if (data->priv->extension[i] > extension[i])
952 	{
953 	  index = shrinkNodeList(data, i, extension[i]);
954 	  if (index->len > 0)
955             visu_node_array_removeNodes(VISU_NODE_ARRAY(data), index);
956           g_array_unref(index);
957 	}
958       else if (data->priv->extension[i] < extension[i])
959         extendNodeList(data, i, data->priv->extension[i], extension[i]);
960       data->priv->extension[i] = extension[i];
961     }
962   g_object_notify(G_OBJECT(data), "n-nodes");
963   if (DEBUG)
964     visu_node_array_traceProperty(VISU_NODE_ARRAY(data), "originalId");
965 }
966 
shrinkNodeList(VisuData * data,int coord,float valueTo)967 static GArray* shrinkNodeList(VisuData *data, int coord, float valueTo)
968 {
969   float cartCoord[3], boxCoord[3];
970   GArray *index;
971   VisuNodeArrayIter iter;
972 
973   g_return_val_if_fail(coord == 0 || coord == 1 || coord == 2, FALSE);
974   g_return_val_if_fail(valueTo >= 0.f, FALSE);
975 
976   DBG_fprintf(stderr, "Visu Data: shrink to %g (%d).\n", valueTo, coord);
977   index = g_array_new(FALSE, FALSE, sizeof(guint));
978   visu_node_array_iter_new(VISU_NODE_ARRAY(data), &iter);
979   for (visu_node_array_iterStart(VISU_NODE_ARRAY(data), &iter); iter.node;
980        visu_node_array_iterNext(VISU_NODE_ARRAY(data), &iter))
981     {
982       visu_data_getNodePosition(data, iter.node, cartCoord);
983       visu_box_convertXYZtoBoxCoordinates(data->priv->box, boxCoord, cartCoord);
984       if ((boxCoord[coord] < - valueTo - 1e-6 ||  /* We are out on the
985 						     low coord. */
986 	   boxCoord[coord] >= 1.f + valueTo -1e-6) && /* We are out on
987 							 the high
988 							 coord. */
989 	  visu_node_array_getOriginal(VISU_NODE_ARRAY(data), iter.node->number) >= 0)
990 	/* We remove the element. */
991         g_array_append_val(index, iter.node->number);
992       DBG_fprintf(stderr, "Visu Data: test shrink for %d: %d %15.12fx%15.12fx%15.12f.\n",
993 		  iter.node->number, index->len, boxCoord[0], boxCoord[1], boxCoord[2]);
994     }
995   return index;
996 }
extendNodeList(VisuData * data,int coord,float valueFrom,float valueTo)997 static void extendNodeList(VisuData *data, int coord, float valueFrom, float valueTo)
998 {
999   int k, id;
1000   unsigned nb, nbInit;
1001   VisuNode *newNode;
1002   float cartCoord[3], boxCoord[3], ratio;
1003   VisuNodeArrayIter iter;
1004   VisuBoxBoundaries bc;
1005 
1006   g_return_if_fail(coord == 0 || coord == 1 || coord == 2);
1007   g_return_if_fail(valueTo > valueFrom);
1008 
1009   DBG_fprintf(stderr, "Visu Data: expand in %d direction to %g.\n",
1010 	      coord, valueTo);
1011   DBG_fprintf(stderr, " | k runs in [%d %d[ ]%d %d].\n", (int)floor(-valueTo),
1012 	      -(int)valueFrom, (int)valueFrom, (int)ceil(valueTo));
1013   DBG_fprintf(stderr, " | keeps new ele in [%g %g] [%g %g].\n", -valueTo,
1014 	      -valueFrom, valueFrom + 1.f, valueTo + 1.f);
1015 
1016   /* We estimate the number of data to be added and we call a realloc of
1017      this amount now to avoid to much small reallocations.
1018      The slab of box to be extend is 2*(valueTo-extension[coord]).
1019      So the volume in box coordinates is the same value and since
1020      the volume in box coordinates is product(1+2*extension),
1021      the ratio of new space is the fraction.
1022      So we realloc all elements on this ratio. */
1023   ratio = (2.f * (valueTo - valueFrom)) / (1.f + 2.f * valueFrom);
1024   visu_node_array_iter_new(VISU_NODE_ARRAY(data), &iter);
1025   for (visu_node_array_iterStart(VISU_NODE_ARRAY(data), &iter); iter.element;
1026        visu_node_array_iterNextElement(VISU_NODE_ARRAY(data), &iter, FALSE))
1027     {
1028       nb = (int)ceil((float)iter.nStoredNodes * ratio);
1029       visu_node_array_allocateNodesForElement(VISU_NODE_ARRAY(data), iter.iElement,
1030                                               iter.nStoredNodes + nb);
1031     }
1032 
1033   bc = visu_box_getBoundary(data->priv->box);
1034 
1035   /* All node with an id higher than nbInit are considered as new
1036      nodes. */
1037   nbInit = G_MAXUINT;
1038   visu_node_array_startAdding(VISU_NODE_ARRAY(data));
1039   for (visu_node_array_iterStartNumber(VISU_NODE_ARRAY(data), &iter); iter.node;
1040        visu_node_array_iterNextNodeNumber(VISU_NODE_ARRAY(data), &iter))
1041     {
1042       /* Do not duplicate the new nodes. */
1043       if (iter.node->number > nbInit)
1044 	continue;
1045       visu_data_getNodePosition(data, iter.node, cartCoord);
1046       visu_box_convertXYZtoBoxCoordinates(data->priv->box, boxCoord, cartCoord);
1047       for (k = (int)floor(-valueTo); k < (int)ceil(valueTo) + 1; k++)
1048 	{
1049 	  if (k >= -(int)valueFrom && k <   (int)valueFrom + 1)
1050 	    continue;
1051 	  boxCoord[coord] += (float)k;
1052 	  if ((boxCoord[coord] >= -valueTo && boxCoord[coord] < -valueFrom ) ||
1053 	      (boxCoord[coord] < valueTo + 1.f  && boxCoord[coord] >= valueFrom + 1.f))
1054 	    {
1055 	      DBG_fprintf(stderr, "Visu Data: replicating node %d, (%d)"
1056 			  " (%15.12fx%15.12fx%15.12f).\n", iter.node->number, coord,
1057 			  boxCoord[0], boxCoord[1], boxCoord[2]);
1058 	      /* We save the current node id, because the pointer may be
1059 		 relocated by the visu_node_array_copyNode() call. */
1060 	      id = iter.node->number;
1061 	      /* We create and add a new element. */
1062 	      newNode = visu_node_array_copyNode(VISU_NODE_ARRAY(data), iter.node);
1063               if (nbInit == G_MAXUINT)
1064                 nbInit = newNode->number - 1;
1065 	      visu_box_convertBoxCoordinatestoXYZ(data->priv->box,
1066                                                   newNode->xyz, boxCoord);
1067               if (!(bc & TOOL_XYZ_MASK_X) || data->priv->translationActive)
1068                 newNode->xyz[0] -= data->priv->translation[0];
1069               if (!(bc & TOOL_XYZ_MASK_Y) || data->priv->translationActive)
1070                 newNode->xyz[1] -= data->priv->translation[1];
1071               if (!(bc & TOOL_XYZ_MASK_Z) || data->priv->translationActive)
1072                 newNode->xyz[2] -= data->priv->translation[2];
1073               if (data->priv->inTheBox)
1074                 {
1075                   newNode->xyz[0] -= newNode->translation[0];
1076                   newNode->xyz[1] -= newNode->translation[1];
1077                   newNode->xyz[2] -= newNode->translation[2];
1078                 }
1079 	      /* We reset the iter.node pointer. */
1080 	      iter.node = visu_node_array_getFromId(VISU_NODE_ARRAY(data), id);
1081 	    }
1082 	  boxCoord[coord] -= (float)k;
1083 	}
1084     }
1085   visu_node_array_completeAdding(VISU_NODE_ARRAY(data));
1086 }
1087 
1088 /**
1089  * visu_data_getAllNodeExtens:
1090  * @dataObj: a #VisuData object.
1091  * @box: (allow-none): a #VisuBox object.
1092  *
1093  * Calculate the longest distance between the surface of @box (without
1094  * extension) and all the nodes. If @box is NULL, then the internal
1095  * box of @dataObj is used.
1096  *
1097  * Since: 3.7
1098  *
1099  * Returns: the longest distance between the surface of @box (without
1100  * extension) and all the nodes.
1101  **/
visu_data_getAllNodeExtens(VisuData * dataObj,VisuBox * box)1102 gfloat visu_data_getAllNodeExtens(VisuData *dataObj, VisuBox *box)
1103 {
1104   VisuNodeArrayIter iter;
1105   float xyz[2][3], t[3], lg[2], coord[3];
1106 
1107   g_return_val_if_fail(VISU_IS_DATA(dataObj), 0.f);
1108 
1109   if (!box)
1110     box = dataObj->priv->box;
1111 
1112   t[0] = (float)(visu_box_getGeometry(box, VISU_BOX_DXX) +
1113 		 visu_box_getGeometry(box, VISU_BOX_DYX) +
1114                  visu_box_getGeometry(box, VISU_BOX_DZX));
1115   t[1] = (float)(visu_box_getGeometry(box, VISU_BOX_DYY) +
1116 		 visu_box_getGeometry(box, VISU_BOX_DZY));
1117   t[2] = (float)(visu_box_getGeometry(box, VISU_BOX_DZZ));
1118   xyz[0][0] = xyz[0][1] = xyz[0][2] = 0.f;
1119   xyz[1][0] = xyz[1][1] = xyz[1][2] = 0.f;
1120 
1121   visu_node_array_iter_new(VISU_NODE_ARRAY(dataObj), &iter);
1122   for (visu_node_array_iterStart(VISU_NODE_ARRAY(dataObj), &iter); iter.node;
1123        visu_node_array_iterNext(VISU_NODE_ARRAY(dataObj), &iter))
1124     {
1125       visu_data_getNodePosition(dataObj, iter.node, coord);
1126       xyz[0][0] = MIN(xyz[0][0], coord[0]);
1127       xyz[0][1] = MIN(xyz[0][1], coord[1]);
1128       xyz[0][2] = MIN(xyz[0][2], coord[2]);
1129 
1130       xyz[1][0] = MAX(xyz[1][0], coord[0]);
1131       xyz[1][1] = MAX(xyz[1][1], coord[1]);
1132       xyz[1][2] = MAX(xyz[1][2], coord[2]);
1133     }
1134   xyz[1][0] -= t[0];
1135   xyz[1][1] -= t[1];
1136   xyz[1][2] -= t[2];
1137   /* Compute the longest vector out of the box. */
1138   lg[0] = sqrt(xyz[0][0] * xyz[0][0] +
1139 	       xyz[0][1] * xyz[0][1] +
1140 	       xyz[0][2] * xyz[0][2]);
1141   lg[1] = sqrt(xyz[1][0] * xyz[1][0] +
1142 	       xyz[1][1] * xyz[1][1] +
1143 	       xyz[1][2] * xyz[1][2]);
1144   DBG_fprintf(stderr, "VisuData: vectors outside of the box %g %g.\n", lg[0], lg[1]);
1145   return MAX(lg[0], lg[1]);
1146 }
1147 
1148 /**
1149  * visu_data_setNewBasisFromNodes:
1150  * @data: a #VisuData object.
1151  * @nO: the index of node as origin.
1152  * @nA: the index of node on X axis.
1153  * @nB: the index of node as Y axis.
1154  * @nC: the index of node as Z axis.
1155  *
1156  * Change the basis set by providing the new basis set from a list of
1157  * nodes. See also visu_data_setNewBasis(). Nodes outside the new box
1158  * are killed.
1159  *
1160  * Since: 3.6
1161  *
1162  * Returns: TRUE if the new basis set is valid.
1163  */
visu_data_setNewBasisFromNodes(VisuData * data,guint nO,guint nA,guint nB,guint nC)1164 gboolean visu_data_setNewBasisFromNodes(VisuData *data, guint nO, guint nA, guint nB, guint nC)
1165 {
1166   VisuNode *orig, *nodeA, *nodeB, *nodeC;
1167   float matA[3][3], O[3], xyz[3];
1168 
1169   orig = visu_node_array_getFromId(VISU_NODE_ARRAY(data), nO);
1170   DBG_fprintf(stderr, " orig = %p\n", (gpointer)orig);
1171   nodeA = visu_node_array_getFromId(VISU_NODE_ARRAY(data), nA);
1172   DBG_fprintf(stderr, " nodeA = %p\n", (gpointer)nodeA);
1173   nodeB = visu_node_array_getFromId(VISU_NODE_ARRAY(data), nB);
1174   DBG_fprintf(stderr, " nodeB = %p\n", (gpointer)nodeB);
1175   nodeC = visu_node_array_getFromId(VISU_NODE_ARRAY(data), nC);
1176   DBG_fprintf(stderr, " nodeC = %p\n", (gpointer)nodeC);
1177   g_return_val_if_fail(orig && nodeA && nodeB && nodeC, FALSE);
1178 
1179   visu_data_getNodePosition(data, orig, O);
1180   visu_data_getNodePosition(data, nodeA, xyz);
1181   matA[0][0] = xyz[0] - O[0];
1182   matA[1][0] = xyz[1] - O[1];
1183   matA[2][0] = xyz[2] - O[2];
1184   visu_data_getNodePosition(data, nodeB, xyz);
1185   matA[0][1] = xyz[0] - O[0];
1186   matA[1][1] = xyz[1] - O[1];
1187   matA[2][1] = xyz[2] - O[2];
1188   visu_data_getNodePosition(data, nodeC, xyz);
1189   matA[0][2] = xyz[0] - O[0];
1190   matA[1][2] = xyz[1] - O[1];
1191   matA[2][2] = xyz[2] - O[2];
1192 
1193   return visu_data_setNewBasis(data, matA, O);
1194 }
1195 /**
1196  * visu_data_setNewBasis:
1197  * @data: a #VisuData object.
1198  * @matA: a basis set definition.
1199  * @O: the origin cartesian coordinates.
1200  *
1201  * Change the basis set of @data according to the new definition given
1202  * by @matA and @O. Nodes outside the new box are killed. See also
1203  * visu_data_setNewBasisFromNodes() for a convenient function using
1204  * nodes as basis set definition.
1205  *
1206  * Since: 3.6
1207  *
1208  * Returns: TRUE if the new basis set is valid.
1209  */
visu_data_setNewBasis(VisuData * data,float matA[3][3],float O[3])1210 gboolean visu_data_setNewBasis(VisuData *data, float matA[3][3], float O[3])
1211 {
1212   double mat_[3][3];
1213   float inv[3][3], vect[3], xred[3];
1214   double box[6];
1215   float vectEps[3], deltaEps[3];
1216   VisuNodeArrayIter iter;
1217   GArray *rmNodes;
1218   float zeros[3] = {0.f, 0.f, 0.f};
1219 #define EPS 1.e-5
1220 
1221   DBG_fprintf(stderr, "Visu Data: basis matrice:\n");
1222   DBG_fprintf(stderr, "  (%10.5f  %10.5f  %10.5f)\n",
1223 	      matA[0][0], matA[0][1], matA[0][2]);
1224   DBG_fprintf(stderr, "  (%10.5f  %10.5f  %10.5f)\n",
1225 	      matA[1][0], matA[1][1], matA[1][2]);
1226   DBG_fprintf(stderr, "  (%10.5f  %10.5f  %10.5f)\n",
1227 	      matA[2][0], matA[2][1], matA[2][2]);
1228   if (!tool_matrix_invert(inv, matA))
1229     return FALSE;
1230   DBG_fprintf(stderr, "Visu Data: transformation matrice:\n");
1231   DBG_fprintf(stderr, "  (%10.5f  %10.5f  %10.5f)\n",
1232 	      inv[0][0], inv[0][1], inv[0][2]);
1233   DBG_fprintf(stderr, "  (%10.5f  %10.5f  %10.5f)\n",
1234 	      inv[1][0], inv[1][1], inv[1][2]);
1235   DBG_fprintf(stderr, "  (%10.5f  %10.5f  %10.5f)\n",
1236 	      inv[2][0], inv[2][1], inv[2][2]);
1237 
1238   mat_[0][0] = (double)matA[0][0];
1239   mat_[1][0] = (double)matA[0][1];
1240   mat_[2][0] = (double)matA[0][2];
1241   mat_[0][1] = (double)matA[1][0];
1242   mat_[1][1] = (double)matA[1][1];
1243   mat_[2][1] = (double)matA[1][2];
1244   mat_[0][2] = (double)matA[2][0];
1245   mat_[1][2] = (double)matA[2][1];
1246   mat_[2][2] = (double)matA[2][2];
1247   if (!tool_matrix_reducePrimitiveVectors(box, mat_))
1248     return FALSE;
1249   DBG_fprintf(stderr, "Visu Data: new box:\n");
1250   DBG_fprintf(stderr, "  (%10.5f  %10.5f  %10.5f)\n",
1251 	      box[0], box[1], box[2]);
1252   DBG_fprintf(stderr, "  (%10.5f  %10.5f  %10.5f)\n",
1253 	      box[3], box[4], box[5]);
1254 
1255   visu_box_setBoundary(data->priv->box, VISU_BOX_PERIODIC);
1256   /* Trick to avoid the emission of SizeChanged signal. */
1257   visu_box_setMargin(data->priv->box, G_MAXFLOAT, FALSE);
1258   visu_box_setGeometry(data->priv->box, box);
1259   /* Remove possible extension. */
1260   g_signal_handler_block(G_OBJECT(data->priv->box), data->priv->expand_signal);
1261   visu_box_setExtension(data->priv->box, zeros);
1262   g_signal_handler_unblock(G_OBJECT(data->priv->box), data->priv->expand_signal);
1263 
1264   /* We need to move all the atoms of (eps, eps, eps) in the new box
1265      to avoid rounding problems. */
1266   xred[0] = 1.f;
1267   xred[1] = 1.f;
1268   xred[2] = 1.f;
1269   tool_matrix_productVector(vect, matA, xred);
1270   vectEps[0] = (vect[0] >= 0.f)?EPS:-EPS;
1271   vectEps[1] = (vect[1] >= 0.f)?EPS:-EPS;
1272   vectEps[2] = (vect[2] >= 0.f)?EPS:-EPS;
1273   tool_matrix_productVector(xred, inv, vectEps);
1274   visu_box_convertBoxCoordinatestoXYZ(data->priv->box, deltaEps, xred);
1275   DBG_fprintf(stderr, "Visu Data: applied epsilon (%10.5f  %10.5f  %10.5f)\n",
1276 	      vectEps[0], vectEps[1], vectEps[2]);
1277 
1278   /* Transform each atomic coordinates using this matrice. */
1279   DBG_fprintf(stderr, "Visu Data: reset the coordinates for all nodes.\n");
1280   visu_node_array_iter_new(VISU_NODE_ARRAY(data), &iter);
1281   rmNodes = g_array_new(FALSE, FALSE, sizeof(guint));
1282   for (visu_node_array_iterStart(VISU_NODE_ARRAY(data), &iter); iter.node;
1283        visu_node_array_iterNext(VISU_NODE_ARRAY(data), &iter))
1284     {
1285       visu_data_getNodePosition(data, iter.node, vect);
1286       vect[0] += - O[0] + vectEps[0];
1287       vect[1] += - O[1] + vectEps[1];
1288       vect[2] += - O[2] + vectEps[2];
1289       tool_matrix_productVector(xred, inv, vect);
1290       if (xred[0] < 0.f || xred[0] >= 1.f ||
1291 	  xred[1] < 0.f || xred[1] >= 1.f ||
1292 	  xred[2] < 0.f || xred[2] >= 1.f)
1293 	{
1294           g_array_append_val(rmNodes, iter.node->number);
1295 	  DBG_fprintf(stderr, " | %d  (%6.1f %6.1f %6.1f)"
1296 		      " %10.5f %10.5f %10.5f -> removed\n",
1297 		      iter.node->number, vect[0], vect[1], vect[2],
1298 		      xred[0], xred[1], xred[2]);
1299 	}
1300       else
1301 	{
1302 	  visu_box_convertBoxCoordinatestoXYZ(data->priv->box, iter.node->xyz, xred);
1303 	  iter.node->xyz[0] -= deltaEps[0];
1304 	  iter.node->xyz[1] -= deltaEps[1];
1305 	  iter.node->xyz[2] -= deltaEps[2];
1306 	  iter.node->translation[0] = 0.f;
1307 	  iter.node->translation[1] = 0.f;
1308 	  iter.node->translation[2] = 0.f;
1309 	  visu_node_array_setOriginal(VISU_NODE_ARRAY(data), iter.node->number);
1310 	  DBG_fprintf(stderr, " | %d  (%6.1f %6.1f %6.1f)"
1311 		      " %10.5f %10.5f %10.5f -> %10.5f %10.5f %10.5f\n",
1312 		      iter.node->number, vect[0], vect[1], vect[2],
1313 		      xred[0], xred[1], xred[2], iter.node->xyz[0],
1314 		      iter.node->xyz[1], iter.node->xyz[2]);
1315 	}
1316     }
1317 
1318   visu_node_array_removeNodes(VISU_NODE_ARRAY(data), rmNodes);
1319   g_array_free(rmNodes, TRUE);
1320 
1321   /* Remove possible translation. */
1322   visu_pointset_setTranslation(VISU_POINTSET(data), zeros, FALSE);
1323   visu_pointset_setTranslationActive(VISU_POINTSET(data), FALSE);
1324   visu_pointset_setInTheBox(VISU_POINTSET(data), FALSE);
1325   g_signal_emit_by_name(G_OBJECT(data), "position-changed", (GArray*)0, NULL);
1326 
1327   return TRUE;
1328 }
1329 /**
1330  * visu_data_reorder:
1331  * @data: a #VisuData object, to reorder.
1332  * @dataRef: a #VisuData object, to take the order from.
1333  *
1334  * This routine modifies the node ordering of @data using the order in
1335  * @dataRef. The association is done by nearest neigbours conditions.
1336  *
1337  * Since: 3.6
1338  *
1339  * Returns: TRUE is the reordering is successfull (i.e. all nodes of
1340  * @data correspond to one of @dataRef).
1341  */
visu_data_reorder(VisuData * data,const VisuData * dataRef)1342 gboolean visu_data_reorder(VisuData *data, const VisuData *dataRef)
1343 {
1344   VisuNodeArrayIter iter, iterRef;
1345   float d, diff[3], xyz[3], dMin;
1346   guint id;
1347 
1348   g_return_val_if_fail(VISU_IS_DATA(dataRef), FALSE);
1349   g_return_val_if_fail(VISU_IS_DATA(data), FALSE);
1350 
1351   DBG_fprintf(stderr, "Geometry: reorder between %p and %p.\n",
1352 	      (gpointer)dataRef, (gpointer)data);
1353 
1354   DBG_fprintf(stderr, " | %d - %d.\n", visu_node_array_getNNodes(VISU_NODE_ARRAY(data)),
1355               visu_node_array_getNNodes(VISU_NODE_ARRAY_CONST(dataRef)));
1356   if (visu_node_array_getNNodes(VISU_NODE_ARRAY(data)) !=
1357       visu_node_array_getNNodes(VISU_NODE_ARRAY_CONST(dataRef)))
1358     return FALSE;
1359 
1360   visu_node_array_iter_new(VISU_NODE_ARRAY(data), &iter);
1361   for (visu_node_array_iterStart(VISU_NODE_ARRAY(data), &iter); iter.node;
1362        visu_node_array_iterNext(VISU_NODE_ARRAY(data), &iter))
1363     {
1364       id = 0;
1365       dMin = G_MAXFLOAT;
1366       visu_data_getNodePosition(data, iter.node, xyz);
1367       visu_node_array_iter_new(VISU_NODE_ARRAY((VisuNodeArray*)dataRef), &iterRef);
1368       iterRef.element = iter.element;
1369       for (visu_node_array_iterRestartNode(VISU_NODE_ARRAY((VisuNodeArray*)dataRef), &iterRef); iterRef.node;
1370            visu_node_array_iterNextNode(VISU_NODE_ARRAY((VisuNodeArray*)dataRef), &iterRef))
1371         {
1372           visu_data_getNodePosition(dataRef, iterRef.node, diff);
1373           diff[0] -= xyz[0];
1374           diff[1] -= xyz[1];
1375           diff[2] -= xyz[2];
1376           visu_box_getPeriodicVector(visu_boxed_getBox(VISU_BOXED(data)), diff);
1377           d = diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2];
1378           if (d < dMin)
1379             {
1380               id = iterRef.node->number;
1381               dMin = d;
1382             }
1383         }
1384       DBG_fprintf(stderr, " | %d %d -> %g\n", iter.node->number, id, dMin);
1385       visu_node_array_switchNumber(VISU_NODE_ARRAY(data), iter.node->number, id);
1386     }
1387   return TRUE;
1388 }
1389 
1390 
1391 /*****************************/
1392 /* The node related routines */
1393 /*****************************/
_addNode(VisuData * data,VisuNode * node,float xyz[3],gboolean reduced)1394 static VisuNode* _addNode(VisuData *data, VisuNode *node,
1395                           float xyz[3], gboolean reduced)
1396 {
1397   float coord[3];
1398 
1399   g_return_val_if_fail(VISU_IS_DATA(data) && node, (VisuNode*)0);
1400   /* If coordinates are reduced, we expand them. */
1401   DBG_fprintf(stderr, "Visu Data: set node coordinates from (%g;%g;%g).\n",
1402               xyz[0], xyz[1], xyz[2]);
1403   if (reduced)
1404     visu_box_convertBoxCoordinatestoXYZ(data->priv->box, coord, xyz);
1405   else
1406     {
1407       coord[0] = xyz[0];
1408       coord[1] = xyz[1];
1409       coord[2] = xyz[2];
1410     }
1411 
1412   visu_node_newValues(node, coord);
1413 
1414   return node;
1415 }
1416 
1417 /**
1418  * visu_data_addNodeFromIndex:
1419  * @data: the #VisuData where to add the new #VisuNode ;
1420  * @position: a integer corresponding to the position of
1421  *            a #VisuElement in the array **nodes in the structure;
1422  * @xyz: (in) (array fixed-size=3): its coordinates ;
1423  * @reduced: coordinates are in reduced coordinates ;
1424  *
1425  * This method adds a new #VisuNode to the specified #VisuData. Position must be
1426  * chosen between 0 and (ntype - 1) and corresponds to the position of the array
1427  * in #VisuNodeArray of a #VisuElement. If several node should be added in
1428  * a row, consider using visu_node_array_startAdding() and
1429  * visu_node_array_completeAdding().
1430  *
1431  * Returns: (transfer none): a pointer to the newly created node.
1432  */
visu_data_addNodeFromIndex(VisuData * data,guint position,float xyz[3],gboolean reduced)1433 VisuNode* visu_data_addNodeFromIndex(VisuData *data, guint position,
1434                                      float xyz[3], gboolean reduced)
1435 {
1436   return _addNode(data, visu_node_array_getNewNodeForId(VISU_NODE_ARRAY(data),
1437                                                         position), xyz, reduced);
1438 }
1439 
1440 /**
1441  * visu_data_addNodeFromElement:
1442  * @data: the #VisuData where to add the new #VisuNode ;
1443  * @ele: the #VisuElement kind of the new #VisuNode ;
1444  * @xyz: (in) (array fixed-size=3): its coordinates ;
1445  * @reduced: coordinates are in reduced coordinates ;
1446  *
1447  * This method adds a new #VisuNode to the specified #VisuData. If
1448  * several node should be added in a row, consider using
1449  * visu_node_array_startAdding() and
1450  * visu_node_array_completeAdding().
1451  *
1452  * Returns: (transfer none): a pointer to the newly created node.
1453  */
visu_data_addNodeFromElement(VisuData * data,VisuElement * ele,float xyz[3],gboolean reduced)1454 VisuNode* visu_data_addNodeFromElement(VisuData *data, VisuElement *ele,
1455                                        float xyz[3], gboolean reduced)
1456 {
1457   return _addNode(data, visu_node_array_getNewNode(VISU_NODE_ARRAY(data),
1458                                                    ele), xyz, reduced);
1459 }
1460 /**
1461  * visu_data_addNodeFromElementName:
1462  * @data: the #VisuData where to add the new #VisuNode ;
1463  * @name: the name of the element ;
1464  * @xyz: (in) (array fixed-size=3): its coordinates ;
1465  * @reduced: coordinates are in reduced coordinates ;
1466  *
1467  * This method adds a new #VisuNode to the specified #VisuData. If
1468  * several node should be added in a row, consider using
1469  * visu_node_array_startAdding() and
1470  * visu_node_array_completeAdding().
1471  *
1472  * Returns: (transfer none): a pointer to the newly created node.
1473  *
1474  * Since: 3.6
1475  */
visu_data_addNodeFromElementName(VisuData * data,const gchar * name,float xyz[3],gboolean reduced)1476 VisuNode* visu_data_addNodeFromElementName(VisuData *data, const gchar *name,
1477                                            float xyz[3], gboolean reduced)
1478 {
1479   return visu_data_addNodeFromElement(data, visu_element_retrieveFromName(name, (gboolean*)0), xyz, reduced);
1480 }
1481 
1482 /**
1483  * visu_data_getNodeCoordinates:
1484  * @data: a #VisuData object ;
1485  * @node: a #VisuNode object ;
1486  * @user: a boolean.
1487  * @x: (out caller-allocates): the x coordinate.
1488  * @y: (out caller-allocates): the y coordinate.
1489  * @z: (out caller-allocates): the z coordinate.
1490  *
1491  * Wrapper for the function visu_data_getNodePosition() in case of call
1492  * from python. If @user is TRUE, it wraps
1493  * visu_data_getNodeUserPosition() instead.
1494  *
1495  * Since: 3.6
1496  */
visu_data_getNodeCoordinates(VisuData * data,VisuNode * node,gboolean user,float * x,float * y,float * z)1497 void visu_data_getNodeCoordinates(VisuData *data, VisuNode *node, gboolean user,
1498 				  float *x, float *y, float *z)
1499 {
1500   float xyz[3];
1501 
1502   g_return_if_fail(x && y && z);
1503 
1504   if (user)
1505     visu_data_getNodeUserPosition(data, node, xyz);
1506   else
1507     visu_data_getNodePosition(data, node, xyz);
1508   *x = xyz[0];
1509   *y = xyz[1];
1510   *z = xyz[2];
1511 }
1512 /**
1513  * visu_data_getNodePosition: (skip)
1514  * @data: a #VisuData object ;
1515  * @node: a #VisuNode object ;
1516  * @coord: (array fixed-size=3) (out caller-allocates): an array of 3
1517  * floating point values to store the position.
1518  *
1519  * Position of nodes are subject to various translations and different transformations.
1520  * Their coordinates should not be access directly through node.[xyz]. This method
1521  * is used to retrieve the given node position.
1522  *
1523  */
visu_data_getNodePosition(const VisuData * data,const VisuNode * node,float coord[3])1524 void visu_data_getNodePosition(const VisuData *data, const VisuNode *node, float coord[3])
1525 {
1526   VisuBoxBoundaries bc;
1527 
1528   g_return_if_fail(VISU_IS_DATA(data) && node && coord);
1529 
1530   coord[0] = node->xyz[0];
1531   coord[1] = node->xyz[1];
1532   coord[2] = node->xyz[2];
1533   bc = (data->priv->box) ? visu_box_getBoundary(data->priv->box) : VISU_BOX_PERIODIC;
1534   if (!(bc & TOOL_XYZ_MASK_X) || data->priv->translationActive)
1535     coord[0] += data->priv->translation[0];
1536   if (!(bc & TOOL_XYZ_MASK_Y) || data->priv->translationActive)
1537     coord[1] += data->priv->translation[1];
1538   if (!(bc & TOOL_XYZ_MASK_Z) || data->priv->translationActive)
1539     coord[2] += data->priv->translation[2];
1540   if (data->priv->inTheBox)
1541     {
1542       coord[0] += node->translation[0];
1543       coord[1] += node->translation[1];
1544       coord[2] += node->translation[2];
1545     }
1546 }
1547 /**
1548  * visu_data_getNodeUserPosition: (skip)
1549  * @data: a #VisuData object ;
1550  * @node: a #VisuNode object ;
1551  * @coord: (array fixed-size=3) (out caller-allocates): an array of 3
1552  * floating point values to store the position.
1553  *
1554  * This routine is equivalent to visu_data_getNodePosition() except
1555  * that it's not applying internal box translation for non periodic
1556  * directions.
1557  *
1558  * Since: 3.7
1559  */
visu_data_getNodeUserPosition(const VisuData * data,const VisuNode * node,float coord[3])1560 void visu_data_getNodeUserPosition(const VisuData *data, const VisuNode *node, float coord[3])
1561 {
1562   VisuBoxBoundaries bc;
1563 
1564   g_return_if_fail(VISU_IS_DATA(data) && node && coord);
1565 
1566   visu_data_getNodePosition(data, node, coord);
1567   bc = visu_box_getBoundary(data->priv->box);
1568   if (!(bc & TOOL_XYZ_MASK_X))
1569     coord[0] -= data->priv->translation[0];
1570   if (!(bc & TOOL_XYZ_MASK_Y))
1571     coord[1] -= data->priv->translation[1];
1572   if (!(bc & TOOL_XYZ_MASK_Z))
1573     coord[2] -= data->priv->translation[2];
1574 }
1575 
1576 
1577 /*****************/
1578 /* Miscellaneous */
1579 /*****************/
1580 /**
1581  * visu_data_addNodeProperties:
1582  * @data: a #VisuData object.
1583  * @values: (transfer full): a #VisuNodeValues object.
1584  *
1585  * Add @values as a known #VisuNodeValues property of @data.
1586  *
1587  * Since: 3.8
1588  *
1589  * Returns: TRUE if @values is added as a valid node property of @data.
1590  **/
visu_data_addNodeProperties(VisuData * data,VisuNodeValues * values)1591 gboolean visu_data_addNodeProperties(VisuData *data, VisuNodeValues *values)
1592 {
1593   g_return_val_if_fail(VISU_IS_DATA(data), FALSE);
1594   g_return_val_if_fail(visu_node_values_fromArray(values, VISU_NODE_ARRAY(data)), FALSE);
1595 
1596   if (g_hash_table_contains(data->priv->nodeProperties,
1597                             visu_node_values_getLabel(values)))
1598     return FALSE;
1599 
1600   g_hash_table_insert(data->priv->nodeProperties,
1601                       (gpointer)visu_node_values_getLabel(values),
1602                       values);
1603   g_signal_emit(data, visu_data_signals[NODE_PROP_ADDED_SIGNAL], 0, values);
1604 
1605   return TRUE;
1606 }
1607 /**
1608  * visu_data_removeNodeProperties:
1609  * @data: a #VisuData object.
1610  * @label: a string.
1611  *
1612  * Look for a #VisuNodeValues object labelled by @label and remove it.
1613  *
1614  * Since: 3.8
1615  *
1616  * Returns: TRUE if @label was indeed attached to @data.
1617  **/
visu_data_removeNodeProperties(VisuData * data,const gchar * label)1618 gboolean visu_data_removeNodeProperties(VisuData *data, const gchar *label)
1619 {
1620   VisuNodeValues *values;
1621 
1622   g_return_val_if_fail(VISU_IS_DATA(data), FALSE);
1623 
1624   values = g_hash_table_lookup(data->priv->nodeProperties, label);
1625   if (!values)
1626     return FALSE;
1627 
1628   g_object_ref(values);
1629   g_hash_table_remove(data->priv->nodeProperties, label);
1630   g_signal_emit(data, visu_data_signals[NODE_PROP_REMOVED_SIGNAL], 0, values);
1631   g_object_unref(values);
1632 
1633   return TRUE;
1634 }
_sortProperties(const VisuNodeValues * propA,const VisuNodeValues * propB)1635 static gint _sortProperties(const VisuNodeValues *propA, const VisuNodeValues *propB)
1636 {
1637   if (VISU_IS_NODE_VALUES_ID(propA))
1638     return -1;
1639   else if (VISU_IS_NODE_VALUES_ID(propB))
1640     return +1;
1641   else if (VISU_IS_NODE_VALUES(propA))
1642     return -1;
1643   else if (VISU_IS_NODE_VALUES(propB))
1644     return +1;
1645   else if (VISU_IS_NODE_VALUES_COORD(propA))
1646     return -1;
1647   else if (VISU_IS_NODE_VALUES_COORD(propB))
1648     return +1;
1649   else
1650     return g_strcmp0(visu_node_values_getLabel(propA),
1651                      visu_node_values_getLabel(propB));
1652 }
1653 /**
1654  * visu_data_getAllNodeProperties:
1655  * @data: a #VisuData object.
1656  *
1657  * Retrieve all the #VisuNodeValues objects attached to @data
1658  * formatted as a list.
1659  *
1660  * Since: 3.8
1661  *
1662  * Returns: (transfer container) (element-type VisuNodeValues): a
1663  * newly created list of #VisuNodeValues objects.
1664  **/
visu_data_getAllNodeProperties(VisuData * data)1665 GList* visu_data_getAllNodeProperties(VisuData *data)
1666 {
1667   g_return_val_if_fail(VISU_IS_DATA(data), (GList*)0);
1668 
1669   return g_list_sort(g_hash_table_get_values(data->priv->nodeProperties),
1670                      (GCompareFunc)_sortProperties);
1671 }
1672 /**
1673  * visu_data_getNodeProperties:
1674  * @data: a #VisuData object.
1675  * @label: a string.
1676  *
1677  * Look for the #VisuNodeValues labelled by @label.
1678  *
1679  * Since: 3.8
1680  *
1681  * Returns: (transfer none): the #VisuNodeValues object attached to
1682  * @data with @label, if any.
1683  **/
visu_data_getNodeProperties(VisuData * data,const gchar * label)1684 VisuNodeValues* visu_data_getNodeProperties(VisuData *data, const gchar *label)
1685 {
1686   g_return_val_if_fail(VISU_IS_DATA(data), (VisuNodeValues*)0);
1687 
1688   return g_hash_table_lookup(data->priv->nodeProperties, label);
1689 }
1690 #define DATA_LABEL_ID _("Label")
1691 /**
1692  * visu_data_getNodeLabels:
1693  * @data: a #VisuData object.
1694  *
1695  * Retrieve the #VisuNodeValuesString object that is used to store
1696  * labels, creating it if necessary.
1697  *
1698  * Since: 3.8
1699  *
1700  * Returns: (transfer none): the #VisuNodeValuesString object used to
1701  * store labels.
1702  **/
visu_data_getNodeLabels(VisuData * data)1703 VisuNodeValuesString* visu_data_getNodeLabels(VisuData *data)
1704 {
1705   VisuNodeValues *vals;
1706 
1707   vals = visu_data_getNodeProperties(data, DATA_LABEL_ID);
1708   if (!vals)
1709     {
1710       vals = VISU_NODE_VALUES(visu_node_values_string_new(VISU_NODE_ARRAY(data),
1711                                                           DATA_LABEL_ID));
1712       visu_data_addNodeProperties(data, vals);
1713     }
1714   return VISU_NODE_VALUES_STRING(vals);
1715 }
1716 /**
1717  * visu_data_getNodeLabelAt:
1718  * @data: a #VisuData object.
1719  * @node: a #VisuNode from @data.
1720  *
1721  * Retrieves the label associated to @node in @data.
1722  *
1723  * Since: 3.8
1724  *
1725  * Returns: a label.
1726  **/
visu_data_getNodeLabelAt(const VisuData * data,const VisuNode * node)1727 const gchar* visu_data_getNodeLabelAt(const VisuData *data, const VisuNode *node)
1728 {
1729   const VisuNodeValues *vals;
1730 
1731   vals = g_hash_table_lookup(data->priv->nodeProperties, DATA_LABEL_ID);
1732   if (!vals)
1733     return (const gchar*)0;
1734 
1735   return visu_node_values_string_getAt(VISU_NODE_VALUES_STRING(vals),
1736                                        node);
1737 }
1738 
1739 /**
1740  * visu_data_applyTransformationsFromCLI:
1741  * @data: a #VisuData object.
1742  * @error: an error location.
1743  *
1744  * Apply the extension and translation expressed in the command-line
1745  * arguments to @data.
1746  *
1747  * Since: 3.8
1748  *
1749  * Returns: TRUE on success.
1750  **/
visu_data_applyTransformationsFromCLI(VisuData * data,GError ** error)1751 gboolean visu_data_applyTransformationsFromCLI(VisuData *data, GError **error)
1752 {
1753   gboolean isBox;
1754   float *translations, *extension;
1755   VisuVibration *vib;
1756 
1757   /* translate argument */
1758   translations = commandLineGet_translation(&isBox);
1759   if (translations && !isBox)
1760     visu_pointset_setTranslationPeriodic(VISU_POINTSET(data), translations, TRUE);
1761   else if (translations && isBox)
1762     visu_pointset_setBoxTranslation(VISU_POINTSET(data), translations, TRUE);
1763   visu_pointset_setTranslationActive(VISU_POINTSET(data),
1764                                      (translations != (float*)0));
1765 
1766   /* expand argument */
1767   extension = commandLineGet_extension();
1768   if (extension)
1769     visu_box_setExtension(visu_boxed_getBox(VISU_BOXED(data)), extension);
1770   visu_box_setExtensionActive(visu_boxed_getBox(VISU_BOXED(data)),
1771                               (extension != (float*)0));
1772 
1773   /* Vibration displacements. */
1774   vib = visu_data_getVibration(data, 0);
1775   if (commandLineGet_phononMode() >= 0 && !vib)
1776     g_warning(_("option '--phonon-mode' has been given but"
1777                 " no phonons are available."));
1778   else if (commandLineGet_phononMode() >= 0)
1779     visu_vibration_setCurrentMode(vib, commandLineGet_phononMode(), error);
1780   if (error && *error)
1781     return FALSE;
1782   if (commandLineGet_phononTime() >= 0.f && !vib)
1783     g_warning(_("option '--time-opffset' has been given but"
1784                 " no phonons are available."));
1785   else if (commandLineGet_phononTime() >= 0)
1786     visu_vibration_setTime(vib, commandLineGet_phononTime());
1787   if (commandLineGet_phononAmpl() >= 0.f && !vib)
1788     g_warning(_("option '--phonon-amplitude' has been given but"
1789                 " no phonons are available."));
1790   else if (commandLineGet_phononAmpl() >= 0)
1791     visu_vibration_setAmplitude(vib, commandLineGet_phononAmpl());
1792 
1793   return TRUE;
1794 }
1795