1 /*   EXTRAITS DE LA LICENCE
2 	Copyright CEA, contributeurs : Damien
3 	CALISTE, laboratoire L_Sim, (2011-2019)
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 : Damien
25 	CALISTE, laboratoire L_Sim, (2011-2019)
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 
45 #include "node_vectors.h"
46 
47 #include <GL/gl.h>
48 #include <GL/glu.h>
49 #include <math.h>
50 
51 #include <opengl.h>
52 #include <openGLFunctions/text.h>
53 #include <coreTools/toolMatrix.h>
54 #include <coreTools/toolColor.h>
55 #include <renderingMethods/elementAtomic.h>
56 #include <extraFunctions/iface_sourceable.h>
57 
58 #include <visu_configFile.h>
59 
60 
61 
62 /**
63  * SECTION:node_vectors
64  * @short_description: Draw arrows at each node to represent forces,
65  * displacements, vibrations...
66  *
67  * <para>A generic #VisuGlExt to represent vectors on nodes.</para>
68  */
69 
70 enum
71   {
72     TAIL_LENGTH,
73     TAIL_RADIUS,
74     TAIL_NLAT,
75     HEAD_LENGTH,
76     HEAD_RADIUS,
77     HEAD_NLAT,
78     N_ARROW_DEFS
79   };
80 
81 /**
82  * VisuGlExtNodeVectorsClass:
83  * @parent: the parent class;
84  *
85  * A short way to identify #_VisuGlExtNodeVectorsClass structure.
86  *
87  * Since: 3.7
88  */
89 /**
90  * VisuGlExtNodeVectors:
91  *
92  * An opaque structure.
93  *
94  * Since: 3.7
95  */
96 /**
97  * VisuGlExtNodeVectorsPrivate:
98  *
99  * Private fields for #VisuGlExtNodeVectors objects.
100  *
101  * Since: 3.7
102  */
103 struct _VisuGlExtNodeVectorsPrivate
104 {
105   gboolean dispose_has_run;
106 
107   VisuSourceableData *source;
108 
109   /* Rendering definitions. */
110   VisuGlExtNodeVectorsColorScheme colorScheme;
111   float arrow[N_ARROW_DEFS];
112   float normFactor, scale;
113   VisuGlArrowCentering centering;
114   float translation, addLength;
115   float ratioMin, ratioMinLabel;
116 
117   VisuNodeArrayRenderer *renderer;
118   gulong size_sig, col_sig, mat_sig, pop_sig, vis_sig, pos_sig, inc_sig;
119 };
120 
121 enum
122   {
123     PROP_0,
124     NORM_PROP,
125     SCALE_PROP,
126     N_PROP,
127     SOURCE_PROP,
128     MODEL_PROP
129   };
130 static GParamSpec *_properties[N_PROP];
131 
132 static void visu_gl_ext_node_vectors_dispose(GObject* obj);
133 static void visu_gl_ext_node_vectors_rebuild(VisuGlExt *ext);
134 static void visu_gl_ext_node_vectors_draw(VisuGlExt *vect);
135 static void visu_gl_ext_node_vectors_get_property(GObject* obj, guint property_id,
136                                                   GValue *value, GParamSpec *pspec);
137 static void visu_gl_ext_node_vectors_set_property(GObject* obj, guint property_id,
138                                                   const GValue *value, GParamSpec *pspec);
139 static void visu_sourceable_interface_init(VisuSourceableInterface *iface);
140 static VisuSourceableData** _getSource(VisuSourceable *self);
141 static void _modelChanged(VisuSourceable *self);
142 
143 /* Local callbacks. */
144 static void _setDirty(VisuGlExt *ext);
145 static void onElementSize(VisuElementRenderer *renderer, VisuElementRenderer *element,
146                           gfloat extent, VisuGlExtNodeVectors *vect);
147 
148 G_DEFINE_TYPE_WITH_CODE(VisuGlExtNodeVectors, visu_gl_ext_node_vectors, VISU_TYPE_GL_EXT,
149                         G_ADD_PRIVATE(VisuGlExtNodeVectors)
150                         G_IMPLEMENT_INTERFACE(VISU_TYPE_SOURCEABLE,
151                                               visu_sourceable_interface_init))
152 
153 static void visu_gl_ext_node_vectors_class_init(VisuGlExtNodeVectorsClass *klass)
154 {
155   DBG_fprintf(stderr, "Visu GlExt NodeVectors: creating the class of the object.\n");
156   /* DBG_fprintf(stderr, "                - adding new signals ;\n"); */
157 
158   /* Connect the overloading methods. */
159   G_OBJECT_CLASS(klass)->dispose  = visu_gl_ext_node_vectors_dispose;
160   G_OBJECT_CLASS(klass)->set_property = visu_gl_ext_node_vectors_set_property;
161   G_OBJECT_CLASS(klass)->get_property = visu_gl_ext_node_vectors_get_property;
162   VISU_GL_EXT_CLASS(klass)->rebuild = visu_gl_ext_node_vectors_rebuild;
163   VISU_GL_EXT_CLASS(klass)->draw = visu_gl_ext_node_vectors_draw;
164 
165   /**
166    * VisuGlExtNodeVectors::normalisation:
167    *
168    * The normalisation factor, zero to scale to the maximum value,
169    * and positive for an absolute value.
170    *
171    * Since: 3.8
172    */
173   _properties[NORM_PROP] = g_param_spec_float("normalisation", "Normalisation",
174                                               "normalisation factor", -1.f, G_MAXFLOAT, 0.f,
175                                               G_PARAM_READWRITE);
176   /**
177    * VisuGlExtNodeVectors::rendering-size:
178    *
179    * When positive, this will be the rendering size for a normalised
180    * value of 1, if negative, it is a factor used to scale element size.
181    *
182    * Since: 3.8
183    */
184   _properties[SCALE_PROP] = g_param_spec_float("rendering-size", "Rendering size",
185                                                "rendering size", -G_MAXFLOAT, G_MAXFLOAT, -2.f,
186                                                G_PARAM_READWRITE);
187 
188   g_object_class_install_properties(G_OBJECT_CLASS(klass), N_PROP, _properties);
189 
190   g_object_class_override_property(G_OBJECT_CLASS(klass), SOURCE_PROP, "source");
191   g_object_class_override_property(G_OBJECT_CLASS(klass), MODEL_PROP, "model");
192 }
193 static void visu_sourceable_interface_init(VisuSourceableInterface *iface)
194 {
195   iface->getSource = _getSource;
196   iface->modelChanged = _modelChanged;
197 }
198 
199 static void visu_gl_ext_node_vectors_init(VisuGlExtNodeVectors *obj)
200 {
201   DBG_fprintf(stderr, "Visu GlExt NodeVectors: initializing a new object (%p).\n",
202 	      (gpointer)obj);
203 
204   obj->priv = visu_gl_ext_node_vectors_get_instance_private(obj);
205   obj->priv->dispose_has_run = FALSE;
206 
207   /* Private data. */
208   visu_sourceable_init(VISU_SOURCEABLE(obj));
209   obj->priv->colorScheme        = VISU_COLOR_ELEMENT;
210   obj->priv->arrow[TAIL_LENGTH] = 0.7f;
211   obj->priv->arrow[TAIL_RADIUS] = 0.1f;
212   obj->priv->arrow[TAIL_NLAT]   = 10.f;
213   obj->priv->arrow[HEAD_LENGTH] = 0.3f;
214   obj->priv->arrow[HEAD_RADIUS] = 0.15f;
215   obj->priv->arrow[HEAD_NLAT]   = 10.f;
216   obj->priv->normFactor         = -1.f;
217   obj->priv->scale              = -2.f;
218   obj->priv->centering          = VISU_GL_ARROW_BOTTOM_CENTERED;
219   obj->priv->translation        = 0.f;
220   obj->priv->addLength          = 0.f;
221   obj->priv->ratioMin           = 0.f;
222   obj->priv->ratioMinLabel      = G_MAXFLOAT;
223   obj->priv->renderer           = (VisuNodeArrayRenderer*)0;
224 }
225 
226 /* This method can be called several times.
227    It should unref all of its reference to
228    GObjects. */
229 static void visu_gl_ext_node_vectors_dispose(GObject* obj)
230 {
231   VisuGlExtNodeVectors *vect;
232 
233   DBG_fprintf(stderr, "Visu GlExt NodeVectors: dispose object %p.\n", (gpointer)obj);
234 
235   vect = VISU_GL_EXT_NODE_VECTORS(obj);
236   if (vect->priv->dispose_has_run)
237     return;
238   vect->priv->dispose_has_run = TRUE;
239 
240   /* Disconnect signals. */
241   visu_sourceable_dispose(VISU_SOURCEABLE(obj));
242   visu_gl_ext_node_vectors_setNodeRenderer(vect, (VisuNodeArrayRenderer*)0);
243 
244   /* Chain up to the parent class */
245   G_OBJECT_CLASS(visu_gl_ext_node_vectors_parent_class)->dispose(obj);
246 }
247 static void visu_gl_ext_node_vectors_get_property(GObject* obj, guint property_id,
248                                                   GValue *value, GParamSpec *pspec)
249 {
250   VisuGlExtNodeVectors *self = VISU_GL_EXT_NODE_VECTORS(obj);
251 
252   DBG_fprintf(stderr, "Extension NodeVectors: get property '%s' -> ",
253 	      g_param_spec_get_name(pspec));
254   switch (property_id)
255     {
256     case SOURCE_PROP:
257       g_value_set_string(value, visu_sourceable_getSource(VISU_SOURCEABLE(self)));
258       DBG_fprintf(stderr, "%s.\n", g_value_get_string(value));
259       break;
260     case MODEL_PROP:
261       g_value_set_object(value, visu_sourceable_getNodeModel(VISU_SOURCEABLE(self)));
262       DBG_fprintf(stderr, "%p.\n", g_value_get_object(value));
263       break;
264     case NORM_PROP:
265       g_value_set_float(value, self->priv->normFactor);
266       DBG_fprintf(stderr, "%g.\n", g_value_get_float(value));
267       break;
268     case SCALE_PROP:
269       g_value_set_float(value, self->priv->scale);
270       DBG_fprintf(stderr, "%g.\n", g_value_get_float(value));
271       break;
272     default:
273       /* We don't have any other property... */
274       G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
275       break;
276     }
277 }
278 static void visu_gl_ext_node_vectors_set_property(GObject* obj, guint property_id,
279                                                   const GValue *value, GParamSpec *pspec)
280 {
281   VisuGlExtNodeVectors *self = VISU_GL_EXT_NODE_VECTORS(obj);
282 
283   DBG_fprintf(stderr, "Extension NodeVectors: set property '%s' -> ",
284 	      g_param_spec_get_name(pspec));
285   switch (property_id)
286     {
287     case SOURCE_PROP:
288       DBG_fprintf(stderr, "%s.\n", g_value_get_string(value));
289       visu_sourceable_setSource(VISU_SOURCEABLE(obj), g_value_get_string(value));
290       break;
291     case MODEL_PROP:
292       DBG_fprintf(stderr, "%p.\n", g_value_get_object(value));
293       visu_sourceable_setNodeModel(VISU_SOURCEABLE(obj), g_value_get_object(value));
294       break;
295     case NORM_PROP:
296       DBG_fprintf(stderr, "%g.\n", g_value_get_float(value));
297       visu_gl_ext_node_vectors_setNormalisation(self, g_value_get_float(value));
298       break;
299     case SCALE_PROP:
300       DBG_fprintf(stderr, "%g.\n", g_value_get_float(value));
301       visu_gl_ext_node_vectors_setRenderedSize(self, g_value_get_float(value));
302       break;
303     default:
304       /* We don't have any other property... */
305       G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
306       break;
307     }
308 }
309 
310 /**
311  * visu_gl_ext_node_vectors_new:
312  * @name: (allow-none): the name to give to the extension.
313  *
314  * Creates a new #VisuGlExt to draw a box.
315  *
316  * Since: 3.7
317  *
318  * Returns: a pointer to the #VisuGlExt it created or
319  * NULL otherwise.
320  */
321 VisuGlExtNodeVectors* visu_gl_ext_node_vectors_new(const gchar *name)
322 {
323   char *name_ = "Node vectors";
324   char *description = _("Draw vectors on each nodes.");
325 
326   DBG_fprintf(stderr,"Visu GlExt NodeVectors: new object.\n");
327 
328   return g_object_new(VISU_TYPE_GL_EXT_NODE_VECTORS,
329                       "name", (name)?name:name_, "label", _(name),
330                       "description", description, "nGlObj", 1,
331                       "priority", VISU_GL_EXT_PRIORITY_NODE_DECORATIONS, NULL);
332 }
333 
334 /**
335  * visu_gl_ext_node_vectors_setNodeRenderer:
336  * @vect: a #VisuGlExtNodeVectors object.
337  * @renderer: a #VisuNodeArrayRenderer object.
338  *
339  * Use the #VisuElementRenderer properties from @renderer to display
340  * vector properties.
341  *
342  * Since: 3.8
343  *
344  * Returns: TRUE if changed.
345  **/
346 gboolean visu_gl_ext_node_vectors_setNodeRenderer(VisuGlExtNodeVectors *vect,
347                                                   VisuNodeArrayRenderer *renderer)
348 {
349   g_return_val_if_fail(VISU_IS_GL_EXT_NODE_VECTORS(vect), FALSE);
350 
351   if (vect->priv->renderer == renderer)
352     return FALSE;
353 
354   if (vect->priv->renderer)
355     {
356       g_signal_handler_disconnect(vect->priv->renderer, vect->priv->size_sig);
357       g_signal_handler_disconnect(vect->priv->renderer, vect->priv->col_sig);
358       g_signal_handler_disconnect(vect->priv->renderer, vect->priv->mat_sig);
359       g_signal_handler_disconnect(vect->priv->renderer, vect->priv->pop_sig);
360       g_signal_handler_disconnect(vect->priv->renderer, vect->priv->inc_sig);
361       g_signal_handler_disconnect(vect->priv->renderer, vect->priv->vis_sig);
362       g_signal_handler_disconnect(vect->priv->renderer, vect->priv->pos_sig);
363       g_object_unref(vect->priv->renderer);
364     }
365   vect->priv->renderer = renderer;
366   if (renderer)
367     {
368       g_object_ref(renderer);
369       vect->priv->size_sig = g_signal_connect(renderer, "element-size-changed",
370                                               G_CALLBACK(onElementSize), vect);
371       vect->priv->col_sig = g_signal_connect_swapped(renderer, "element-notify::color",
372                                                      G_CALLBACK(_setDirty), vect);
373       vect->priv->mat_sig = g_signal_connect_swapped(renderer, "element-notify::material",
374                                                      G_CALLBACK(_setDirty), vect);
375       vect->priv->pop_sig = g_signal_connect_swapped(renderer, "nodes::population-decrease",
376                                                      G_CALLBACK(_setDirty), vect);
377       vect->priv->inc_sig = g_signal_connect_swapped(renderer, "nodes::population-increase",
378                                                      G_CALLBACK(_setDirty), vect);
379       vect->priv->vis_sig = g_signal_connect_swapped(renderer, "nodes::visibility",
380                                                      G_CALLBACK(_setDirty), vect);
381       vect->priv->pos_sig = g_signal_connect_swapped(renderer, "nodes::position",
382                                                      G_CALLBACK(_setDirty), vect);
383     }
384 
385   visu_gl_ext_setDirty(VISU_GL_EXT(vect), TRUE);
386   return TRUE;
387 }
388 static VisuSourceableData** _getSource(VisuSourceable *self)
389 {
390   VisuGlExtNodeVectors *vect = VISU_GL_EXT_NODE_VECTORS(self);
391 
392   g_return_val_if_fail(VISU_IS_GL_EXT_NODE_VECTORS(self), (VisuSourceableData**)0);
393 
394   return &vect->priv->source;
395 }
396 static void _modelChanged(VisuSourceable *self)
397 {
398   visu_gl_ext_setDirty(VISU_GL_EXT(self), TRUE);
399 }
400 
401 /**
402  * visu_gl_ext_node_vectors_setRenderedSize:
403  * @vect: the #VisuGlExtNodeVectors object to modify.
404  * @scale: a floating point value.
405  *
406  * @scale governs how large the node vectors are drawn. For a positive
407  * value, a vector with a normalised size of 1 (see
408  * visu_gl_ext_node_vectors_setNormalisation()) will be drawn with the size of
409  * @scale. For a negative value, a vector of normalised size of 1 will
410  * be drawn with a size of -@scale times the maximum element size (see
411  * visu_node_array_renderer_getMaxElementSize()).
412  *
413  * Since: 3.7
414  *
415  * Returns: TRUE if setting has been changed.
416  **/
417 gboolean visu_gl_ext_node_vectors_setRenderedSize(VisuGlExtNodeVectors *vect, gfloat scale)
418 {
419   g_return_val_if_fail(VISU_IS_GL_EXT_NODE_VECTORS(vect), FALSE);
420 
421   if (vect->priv->scale == scale)
422     return FALSE;
423 
424   vect->priv->scale = scale;
425 
426   if (vect->priv->renderer && visu_sourceable_getNodeModel(VISU_SOURCEABLE(vect)))
427     visu_gl_ext_setDirty(VISU_GL_EXT(vect), TRUE);
428   return TRUE;
429 }
430 /**
431  * visu_gl_ext_node_vectors_setNormalisation:
432  * @vect: the #VisuGlExtNodeVectors object to modify.
433  * @norm: a floating point value.
434  *
435  * @norm governs how the input node vector field is normalised. With a
436  * positive value, all node vectors will be normalised by @norm. With
437  * a negative value, all node vectors are normalised with respect to
438  * the biggest one.
439  *
440  * Since: 3.7
441  *
442  * Returns: TRUE if setting has been changed.
443  **/
444 gboolean visu_gl_ext_node_vectors_setNormalisation(VisuGlExtNodeVectors *vect, float norm)
445 {
446   g_return_val_if_fail(VISU_IS_GL_EXT_NODE_VECTORS(vect), FALSE);
447 
448   if (vect->priv->normFactor == norm)
449     return FALSE;
450 
451   vect->priv->normFactor = norm;
452 
453   g_object_notify_by_pspec(G_OBJECT(vect), _properties[NORM_PROP]);
454   if (vect->priv->renderer && visu_sourceable_getSource(VISU_SOURCEABLE(vect)))
455     visu_gl_ext_setDirty(VISU_GL_EXT(vect), TRUE);
456   return TRUE;
457 }
458 /**
459  * visu_gl_ext_node_vectors_setTranslation:
460  * @vect: the #VisuGlExtNodeVectors object to modify.
461  * @trans: a positive floating point value.
462  *
463  * Defines a translation with respect to the center of each node. The
464  * vector is shifted outwards, following the vector direction by an
465  * amount given by the product of @trans and the element size
466  * currently drawn.
467  *
468  * Since: 3.7
469  *
470  * Returns: TRUE if setting has been changed.
471  **/
472 gboolean visu_gl_ext_node_vectors_setTranslation(VisuGlExtNodeVectors *vect, float trans)
473 {
474   g_return_val_if_fail(VISU_IS_GL_EXT_NODE_VECTORS(vect), FALSE);
475 
476   if (vect->priv->translation == trans)
477     return FALSE;
478 
479   vect->priv->translation = MAX(0.f, trans);
480 
481   if (vect->priv->renderer && visu_sourceable_getSource(VISU_SOURCEABLE(vect)))
482     visu_gl_ext_setDirty(VISU_GL_EXT(vect), TRUE);
483   return TRUE;
484 }
485 /**
486  * visu_gl_ext_node_vectors_setColor:
487  * @vect: the #VisuGlExtNodeVectors object to modify.
488  * @scheme: a value.
489  *
490  * If @scheme is %VISU_COLOR_ELEMENT, the vectors are drawn with the color of the
491  * currently drawn #VisuElement. If @scheme is %VISU_COLOR_BRIGHT, they
492  * are drawn with a highlighted color. If @scheme is
493  * %VISU_COLOR_ORIENTATION, they are drawn with a hue depending onn orientation.
494  *
495  * Since: 3.7
496  *
497  * Returns: TRUE if setting has been changed.
498  **/
499 gboolean visu_gl_ext_node_vectors_setColor(VisuGlExtNodeVectors *vect,
500                                            VisuGlExtNodeVectorsColorScheme scheme)
501 {
502   g_return_val_if_fail(VISU_IS_GL_EXT_NODE_VECTORS(vect), FALSE);
503 
504   if (vect->priv->colorScheme == scheme)
505     return FALSE;
506 
507   vect->priv->colorScheme = scheme;
508 
509   if (vect->priv->renderer && visu_sourceable_getSource(VISU_SOURCEABLE(vect)))
510     visu_gl_ext_setDirty(VISU_GL_EXT(vect), TRUE);
511   return TRUE;
512 }
513 /**
514  * visu_gl_ext_node_vectors_setCentering:
515  * @vect: the #VisuGlExtNodeVectors object to modify.
516  * @centering: a #VisuGlArrowCentering id.
517  *
518  * Change how vectors are position with respect to to center of each node.
519  *
520  * Since: 3.7
521  *
522  * Returns: TRUE if setting has been changed.
523  **/
524 gboolean visu_gl_ext_node_vectors_setCentering(VisuGlExtNodeVectors *vect,
525                                                VisuGlArrowCentering centering)
526 {
527   g_return_val_if_fail(VISU_IS_GL_EXT_NODE_VECTORS(vect), FALSE);
528 
529   if (vect->priv->centering == centering)
530     return FALSE;
531 
532   vect->priv->centering = centering;
533 
534   if (vect->priv->renderer && visu_sourceable_getSource(VISU_SOURCEABLE(vect)))
535     visu_gl_ext_setDirty(VISU_GL_EXT(vect), TRUE);
536   return TRUE;
537 }
538 /**
539  * visu_gl_ext_node_vectors_setAddLength:
540  * @vect: the #VisuGlExtNodeVectors object to modify.
541  * @addLength: a positive value.
542  *
543  * Change the additional length that is used to draw the tail.
544  *
545  * Since: 3.8
546  *
547  * Returns: TRUE if settings has been changed.
548  **/
549 gboolean visu_gl_ext_node_vectors_setAddLength(VisuGlExtNodeVectors *vect, gfloat addLength)
550 {
551   g_return_val_if_fail(VISU_IS_GL_EXT_NODE_VECTORS(vect), FALSE);
552 
553   if (vect->priv->addLength == addLength)
554     return FALSE;
555 
556   vect->priv->addLength = MAX(addLength, 0.f);
557 
558   if (vect->priv->renderer && visu_sourceable_getSource(VISU_SOURCEABLE(vect)))
559     visu_gl_ext_setDirty(VISU_GL_EXT(vect), TRUE);
560   return TRUE;
561 }
562 /**
563  * visu_gl_ext_node_vectors_setVectorThreshold:
564  * @vect: the #VisuGlExtNodeVectors object to modify.
565  * @val: a value.
566  *
567  * Vectors are indeed drawn if a threshold value is reach. If @val is
568  * strictly positive, the norm of each vector is compared to @val. If
569  * @val is negative, the normalised [0;1] norm is compared to -@val.
570  *
571  * Since: 3.7
572  *
573  * Returns: TRUE if setting has been changed.
574  **/
575 gboolean visu_gl_ext_node_vectors_setVectorThreshold(VisuGlExtNodeVectors *vect, float val)
576 {
577   g_return_val_if_fail(VISU_IS_GL_EXT_NODE_VECTORS(vect), FALSE);
578 
579   if (vect->priv->ratioMin == val)
580     return FALSE;
581 
582   vect->priv->ratioMin = val;
583 
584   if (vect->priv->renderer && visu_sourceable_getSource(VISU_SOURCEABLE(vect)))
585     visu_gl_ext_setDirty(VISU_GL_EXT(vect), TRUE);
586   return TRUE;
587 }
588 /**
589  * visu_gl_ext_node_vectors_setLabelThreshold:
590  * @vect: the #VisuGlExtNodeVectors object to modify.
591  * @val: a value.
592  *
593  * Vector norms can be drawn if a threshold value is reach. If @val is
594  * strictly positive, the norm of each vector is compared to @val. If
595  * @val is negative, the normalised [0;1] norm is compared to -@val.
596  *
597  * Since: 3.7
598  *
599  * Returns: TRUE if setting has been changed.
600  **/
601 gboolean visu_gl_ext_node_vectors_setLabelThreshold(VisuGlExtNodeVectors *vect, float val)
602 {
603   g_return_val_if_fail(VISU_IS_GL_EXT_NODE_VECTORS(vect), FALSE);
604 
605   if (vect->priv->ratioMinLabel == val)
606     return FALSE;
607 
608   vect->priv->ratioMinLabel = val;
609 
610   if (vect->priv->renderer && visu_sourceable_getSource(VISU_SOURCEABLE(vect)))
611     visu_gl_ext_setDirty(VISU_GL_EXT(vect), TRUE);
612   return TRUE;
613 }
614 /**
615  * visu_gl_ext_node_vectors_setArrow:
616  * @vect: the #VisuGlExtNodeVectors object to modify.
617  * @tailLength: the length for the tail part of the vector.
618  * @tailRadius: the radius for the tail part of the vector.
619  * @tailN: the number of polygons to draw the tail part of the vector.
620  * @headLength: the length for the head part of the vector.
621  * @headRadius: the radius for the head part of the vector.
622  * @headN: the number of polygons to draw the head part of the vector.
623  *
624  * Defines the profile of the arrows representing the vectors.
625  *
626  * Since: 3.7
627  *
628  * Returns: TRUE if setting has been changed.
629  **/
630 gboolean visu_gl_ext_node_vectors_setArrow(VisuGlExtNodeVectors *vect, float tailLength,
631                                            float tailRadius, guint tailN,
632                                            float headLength, float headRadius, guint headN)
633 {
634   gboolean diff;
635   float fact;
636 
637   g_return_val_if_fail(VISU_IS_GL_EXT_NODE_VECTORS(vect), FALSE);
638 
639   diff = FALSE;
640   diff = diff || (vect->priv->arrow[TAIL_LENGTH] != tailLength);
641   diff = diff || (vect->priv->arrow[TAIL_RADIUS] != tailRadius);
642   diff = diff || (vect->priv->arrow[TAIL_NLAT] != tailN);
643   diff = diff || (vect->priv->arrow[HEAD_LENGTH] != headLength);
644   diff = diff || (vect->priv->arrow[HEAD_RADIUS] != headRadius);
645   diff = diff || (vect->priv->arrow[HEAD_NLAT] != headN);
646   if (!diff)
647     return FALSE;
648 
649   fact = 1.f / (tailLength + headLength);
650   vect->priv->arrow[TAIL_LENGTH] = tailLength * fact;
651   vect->priv->arrow[TAIL_RADIUS] = tailRadius * fact;
652   vect->priv->arrow[TAIL_NLAT] = tailN;
653   vect->priv->arrow[HEAD_LENGTH] = headLength * fact;
654   vect->priv->arrow[HEAD_RADIUS] = headRadius * fact;
655   vect->priv->arrow[HEAD_NLAT] = headN;
656 
657   if (vect->priv->renderer && visu_sourceable_getSource(VISU_SOURCEABLE(vect)))
658     visu_gl_ext_setDirty(VISU_GL_EXT(vect), TRUE);
659   return TRUE;
660 }
661 
662 /**
663  * visu_gl_ext_node_vectors_getScale:
664  *
665  * The forces can be scaled manually or automatically, see
666  * visu_gl_ext_forces_setScale() and visu_rendering_atomic_drawForces().
667  *
668  * Since: 3.7
669  *
670  * Returns: the scaling factor, -1 if automatic.
671  **/
672 /* gfloat visu_gl_ext_forces_getScale() */
673 /* { */
674 /*   return scale; */
675 /* } */
676 /**
677  * visu_gl_ext_node_vectors_getNormalisation:
678  * @vect: the #VisuGlExtNodeVectors object to inquire.
679  *
680  * Gets the normalisation factor, see visu_gl_ext_node_vectors_setNormalisation().
681  *
682  * Since: 3.7
683  *
684  * Returns: the normalisation factor used by @vact.
685  **/
686 float visu_gl_ext_node_vectors_getNormalisation(VisuGlExtNodeVectors *vect)
687 {
688   g_return_val_if_fail(VISU_IS_GL_EXT_NODE_VECTORS(vect), -1.f);
689 
690   return vect->priv->normFactor;
691 }
692 
693 /********************/
694 /* Local callbacks. */
695 /********************/
696 static void _setDirty(VisuGlExt *ext)
697 {
698   visu_gl_ext_setDirty(ext, TRUE);
699 }
700 static void onElementSize(VisuElementRenderer *renderer _U_, VisuElementRenderer *element _U_,
701                           gfloat extent _U_, VisuGlExtNodeVectors *vect)
702 {
703   if (vect->priv->scale < 0.f || vect->priv->translation > 0.f)
704     visu_gl_ext_setDirty(VISU_GL_EXT(vect), TRUE);
705 }
706 
707 /***********/
708 /* OpenGL. */
709 /***********/
710 
711 static void visu_gl_ext_node_vectors_draw(VisuGlExt *vect)
712 {
713   VisuNodeArrayRendererIter iter;
714   GLUquadricObj *obj;
715   float fact, scale, eleSize;
716   float rMult, r, sMult, s, l, headLength, tailLength;
717   float xyz[3], dxyz[3], hsl[3], rgba[4];
718   const gfloat *sph;
719   char distStr[10];
720   gboolean valid;
721   VisuGlExtNodeVectorsPrivate *priv = VISU_GL_EXT_NODE_VECTORS(vect)->priv;
722 
723   glDeleteLists(visu_gl_ext_getGlList(vect), 1);
724   visu_gl_ext_setDirty(vect, FALSE);
725 
726   DBG_fprintf(stderr, "Extension Node Vectors: building for nodes %p and vectors %p.\n",
727               (gpointer)priv->renderer,
728               (gpointer)visu_sourceable_getNodeModel(VISU_SOURCEABLE(vect)));
729   if (!priv->renderer || !visu_sourceable_getNodeModel(VISU_SOURCEABLE(vect)))
730     return;
731 
732   obj = gluNewQuadric();
733 
734   /* Normalization factor. */
735   fact = priv->normFactor;
736   if (fact <= 0.f)
737     fact = visu_node_values_farray_max(VISU_NODE_VALUES_FARRAY(visu_sourceable_getNodeModel(VISU_SOURCEABLE(vect))));
738   fact = 1.f / fact;
739 
740   /* Drawing rule for vectors. */
741   rMult = 1.f;
742   r     = 1.f;
743   if (priv->ratioMin <= 0.f)
744     {
745       rMult = fact;
746       r = -1.f;
747     }
748   /* Drawing rule for label. */
749   sMult = 1.f;
750   s     = 1.f;
751   if (priv->ratioMinLabel <= 0.f)
752     {
753       sMult = fact;
754       s = -1.f;
755     }
756 
757   /* Maximum representation size. */
758   scale = priv->scale;
759   if (scale <= 0.f)
760     scale = visu_node_array_renderer_getMaxElementSize(priv->renderer, NULL) * (-scale);
761 
762   visu_gl_ext_startDrawing(vect);
763 
764   eleSize = 0.f;
765   for (valid = visu_node_array_renderer_iter_new(priv->renderer, &iter, TRUE);
766        valid; valid = visu_node_array_renderer_iter_next(&iter))
767     {
768       if (!visu_element_getRendered(iter.element))
769         continue;
770 
771       if (priv->colorScheme == VISU_COLOR_ELEMENT)
772         visu_element_renderer_colorize(iter.renderer, VISU_ELEMENT_RENDERER_NO_EFFECT);
773       else if (priv->colorScheme == VISU_COLOR_BRIGHT)
774         visu_element_renderer_colorize(iter.renderer, VISU_ELEMENT_RENDERER_HIGHLIGHT);
775       eleSize = visu_element_renderer_getExtent(iter.renderer);
776 
777       for(visu_node_array_iterRestartNode(iter.parent.array, &iter.parent); iter.parent.node;
778           visu_node_array_iterNextNode(iter.parent.array, &iter.parent))
779         {
780           if (!iter.parent.node->rendered)
781             continue;
782 
783           sph = visu_node_values_vector_getAtSpherical(VISU_NODE_VALUES_VECTOR(visu_sourceable_getNodeModel(VISU_SOURCEABLE(vect))), iter.parent.node);
784           if (!sph)
785             continue;
786 
787           if (sph[TOOL_MATRIX_SPHERICAL_MODULUS] * rMult <= priv->ratioMin * r)
788             continue;
789           l = sph[TOOL_MATRIX_SPHERICAL_MODULUS] * fact;
790 
791           visu_data_getNodePosition(VISU_DATA(iter.parent.array), iter.parent.node, xyz);
792           visu_node_values_vector_getShift(VISU_NODE_VALUES_VECTOR(visu_sourceable_getNodeModel(VISU_SOURCEABLE(vect))), iter.parent.node, dxyz);
793 
794           glPushMatrix();
795           glTranslatef(xyz[0] - dxyz[0], xyz[1] - dxyz[1], xyz[2] - dxyz[2]);
796           /* We draw the arrow. */
797           if (priv->colorScheme == VISU_COLOR_ORIENTATION)
798             {
799               hsl[2] = 1.f - sph[TOOL_MATRIX_SPHERICAL_THETA] / 180.;
800               hsl[1] = 1.f;
801               hsl[0] = sph[TOOL_MATRIX_SPHERICAL_PHI] / 360.;
802 
803               tool_color_convertHSLtoRGB(rgba, hsl);
804 
805               rgba[3] = visu_element_renderer_getColor(iter.renderer)->rgba[3];
806               visu_gl_setColor((VisuGl*)0, visu_element_renderer_getMaterial(iter.renderer), rgba);
807             }
808           glRotated(sph[TOOL_MATRIX_SPHERICAL_PHI], 0, 0, 1);
809           glRotated(sph[TOOL_MATRIX_SPHERICAL_THETA], 0, 1, 0);
810           glTranslated(0.f, 0.f, eleSize * priv->translation);
811           switch (priv->centering)
812             {
813             case (VISU_GL_ARROW_CENTERED):
814               glScalef(scale, scale, scale);
815               tailLength = MAX(0.f, l - priv->arrow[HEAD_LENGTH]);
816               headLength = MIN(l, priv->arrow[HEAD_LENGTH]);
817               break;
818             case (VISU_GL_ARROW_TAIL_CENTERED):
819               glScalef(eleSize, eleSize, eleSize);
820               tailLength = l * scale / eleSize + priv->addLength;
821               headLength = priv->arrow[HEAD_LENGTH];
822               break;
823             default:
824               glScalef(l * scale, l * scale, l * scale);
825               tailLength = priv->arrow[TAIL_LENGTH];
826               headLength = priv->arrow[HEAD_LENGTH];
827             }
828           visu_gl_drawSmoothArrow(obj, priv->centering,
829                                   tailLength,
830                                   priv->arrow[TAIL_RADIUS],
831                                   (guint)priv->arrow[TAIL_NLAT], NULL,
832                                   headLength,
833                                   priv->arrow[HEAD_RADIUS],
834                                   (guint)priv->arrow[HEAD_NLAT], NULL);
835           /* We draw the value. */
836           if (sph[TOOL_MATRIX_SPHERICAL_MODULUS] * sMult > priv->ratioMinLabel * s)
837             {
838               glRasterPos3f(0.f, 0.f, 0.f);
839               sprintf(distStr, "%6.3f", sph[TOOL_MATRIX_SPHERICAL_MODULUS]);
840               visu_gl_text_drawChars(distStr, VISU_GL_TEXT_NORMAL);
841             }
842           glPopMatrix();
843         }
844     }
845 
846   visu_gl_ext_completeDrawing(vect);
847 
848   gluDeleteQuadric(obj);
849 }
850 static void visu_gl_ext_node_vectors_rebuild(VisuGlExt *ext)
851 {
852   DBG_fprintf(stderr, "Visu GlExt NodeVectors: rebuilding vectors %p.\n", (gpointer)ext);
853   visu_gl_text_rebuildFontList();
854   visu_gl_ext_setDirty(ext, TRUE);
855   visu_gl_ext_node_vectors_draw(ext);
856 }
857