1 /*   EXTRAITS DE LA LICENCE
2 	Copyright CEA, contributeurs : Damien
3 	CALISTE, laboratoire L_Sim, (2017)
4 
5 	Adresse mèl :
6 	CALISTE, damien P caliste AT cea P fr.
7 
8 	Ce logiciel est un programme informatique servant à visualiser des
9 	structures atomiques dans un rendu pseudo-3D.
10 
11 	Ce logiciel est régi par la licence CeCILL soumise au droit français et
12 	respectant les principes de diffusion des logiciels libres. Vous pouvez
13 	utiliser, modifier et/ou redistribuer ce programme sous les conditions
14 	de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA
15 	sur le site "http://www.cecill.info".
16 
17 	Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
18 	pris connaissance de la licence CeCILL, et que vous en avez accepté les
19 	termes (cf. le fichier Documentation/licence.fr.txt fourni avec ce logiciel).
20 */
21 
22 /*   LICENCE SUM UP
23 	Copyright CEA, contributors : Damien
24 	CALISTE, laboratoire L_Sim, (2017)
25 
26 	E-mail address:
27 	CALISTE, damien P caliste AT cea P fr.
28 
29 	This software is a computer program whose purpose is to visualize atomic
30 	configurations in 3D.
31 
32 	This software is governed by the CeCILL  license under French law and
33 	abiding by the rules of distribution of free software.  You can  use,
34 	modify and/ or redistribute the software under the terms of the CeCILL
35 	license as circulated by CEA, CNRS and INRIA at the following URL
36 	"http://www.cecill.info".
37 
38 	The fact that you are presently reading this means that you have had
39 	knowledge of the CeCILL license and that you accept its terms. You can
40 	find a copy of this licence shipped with this software at Documentation/licence.en.txt.
41 */
42 
43 #include "spinMethod.h"
44 
45 #include <math.h>
46 
47 #include <visu_configFile.h>
48 #include <coreTools/toolMatrix.h>
49 #include <coreTools/toolColor.h>
50 
51 /**
52  * SECTION:spinMethod
53  * @short_description: a class defining how a set of spin should be rendered.
54  *
55  * <para>Defines global properties for the rendering of spin data,
56  * like should atomic rendering be used in addition? How to orientate
57  * the colourisation cone?</para>
58  */
59 
60 #define FLAG_RESOURCES_SPIN "spin_resources"
61 #define DESC_RESOURCES_SPIN "Global or element resource for rendering spin module"
62 
63 #define FLAG_SPIN_CONE_ANGLE "spin_global_color_cone"
64 #define FLAG_SPIN_WHEEL_ANGLE "spin_global_color_wheel"
65 #define FLAG_SPIN_HIDING_MODE "spin_global_hiding_mode"
66 #define FLAG_SPIN_AND_ATOMIC "spin_global_atomic"
67 #define FLAG_SPIN_MODULUS "spin_global_modulus"
68 static gfloat _coneOrientation[2] = {0.f, 0.f};
69 static gfloat _colorWheel = 0.f;
70 static VisuMethodSpinDrawingPolicy _spinPolicy = VISU_METHOD_SPIN_ALWAYS;
71 static gboolean _spinAndAtomicRendering = FALSE;
72 static VisuMethodSpinModulusPolicy _spinModulusUsage = VISU_METHOD_SPIN_CONSTANT;
73 
74 static const char* policyNameSpin[VISU_METHOD_SPIN_N_MODES + 1] = {"always",
75                                                                    "never",
76                                                                    "atomic",
77                                                                    (const char*)0};
78 
79 enum
80   {
81     PROP_0,
82     PROP_CONETHETA,
83     PROP_CONEPHI,
84     PROP_COLORWHEEL,
85     PROP_HIDINGMODE,
86     PROP_ATOMIC,
87     PROP_MODULUS,
88     N_PROP,
89   };
90 static GParamSpec *_properties[N_PROP];
91 
92 static VisuMethodSpin* _default = NULL;
93 
94 struct _VisuMethodSpinPrivate
95 {
96   gfloat coneOrientation[2];
97   gfloat colorWheel;
98   VisuMethodSpinDrawingPolicy spinPolicy;
99   gboolean spinAndAtomicRendering;
100   VisuMethodSpinModulusPolicy spinModulusUsage;
101 };
102 
103 static void visu_method_spin_get_property(GObject* obj, guint property_id,
104                                         GValue *value, GParamSpec *pspec);
105 static void visu_method_spin_set_property(GObject* obj, guint property_id,
106                                         const GValue *value, GParamSpec *pspec);
107 
108 /* Local methods. */
109 static void onEntrySpin(VisuMethodSpin *method, VisuConfigFileEntry *entry, VisuConfigFile *obj);
110 static void exportResourcesRenderingSpin(GString *method, VisuData *dataObj);
111 
G_DEFINE_TYPE_WITH_CODE(VisuMethodSpin,visu_method_spin,VISU_TYPE_OBJECT,G_ADD_PRIVATE (VisuMethodSpin))112 G_DEFINE_TYPE_WITH_CODE(VisuMethodSpin, visu_method_spin, VISU_TYPE_OBJECT,
113                         G_ADD_PRIVATE(VisuMethodSpin))
114 
115 static void visu_method_spin_class_init(VisuMethodSpinClass *klass)
116 {
117   VisuConfigFileEntry *resourceEntry;
118 
119   /* Connect the overloading methods. */
120   G_OBJECT_CLASS(klass)->get_property = visu_method_spin_get_property;
121   G_OBJECT_CLASS(klass)->set_property = visu_method_spin_set_property;
122 
123   /**
124    * VisuMethodSpin::cone-theta:
125    *
126    * The theta angle to orientate the colourisation cone.
127    *
128    * Since: 3.8
129    */
130   _properties[PROP_CONETHETA] =
131     g_param_spec_float("cone-theta", _("Theta angle"),
132                        _("The theta angle to orientate the colourisation cone."), 0, 180, 0,
133                        G_PARAM_READWRITE);
134   /**
135    * VisuMethodSpin::cone-phi:
136    *
137    * The phi angle to orientate the colourisation cone.
138    *
139    * Since: 3.8
140    */
141   _properties[PROP_CONEPHI] =
142     g_param_spec_float("cone-phi", _("Phi angle"),
143                        _("The phi angle to orientate the colourisation cone."), 0, 360, 0,
144                        G_PARAM_READWRITE);
145   /**
146    * VisuMethodSpin::cone-omega:
147    *
148    * The omega angle to orientate the colourisation cone.
149    *
150    * Since: 3.8
151    */
152   _properties[PROP_COLORWHEEL] =
153     g_param_spec_float("cone-omega", _("Omega angle"),
154                        _("The omega angle to orientate the colourisation cone."), 0, 360, 0,
155                        G_PARAM_READWRITE);
156   /**
157    * VisuMethodSpin::hiding-mode:
158    *
159    * The hiding policy for spin with a null modulus.
160    *
161    * Since: 3.8
162    */
163   _properties[PROP_HIDINGMODE] =
164     g_param_spec_uint("hiding-mode", _("Hiding policy for null modulus"),
165                       _("The hiding policy for spin with a null modulus."),
166                       0, VISU_METHOD_SPIN_N_MODES, VISU_METHOD_SPIN_ALWAYS,
167                       G_PARAM_READWRITE);
168   /**
169    * VisuMethodSpin::modulus-scaling:
170    *
171    * The scaling policy based on modulus value.
172    *
173    * Since: 3.8
174    */
175   _properties[PROP_MODULUS] =
176     g_param_spec_uint("modulus-scaling", _("Scaling of spin depending on modulus value"),
177                       _("The scaling policy based on modulus value."),
178                       0, VISU_METHOD_SPIN_N_MODULUS_MODES, VISU_METHOD_SPIN_CONSTANT,
179                       G_PARAM_READWRITE);
180   /**
181    * VisuMethodSpin::use-atomic:
182    *
183    * If atomic rendering is used in addition to spin rendering.
184    *
185    * Since: 3.8
186    */
187   _properties[PROP_ATOMIC] =
188     g_param_spec_boolean("use-atomic", _("Use atomic rendering"),
189                          _("If atomic rendering is used in addition to spin rendering."),
190                          FALSE, G_PARAM_READWRITE);
191   g_object_class_install_properties(G_OBJECT_CLASS(klass), N_PROP, _properties);
192 
193   /* Dealing with config files. */
194   resourceEntry = visu_config_file_addTokenizedEntry(VISU_CONFIG_FILE_RESOURCE,
195                                                      FLAG_RESOURCES_SPIN,
196                                                      DESC_RESOURCES_SPIN,
197                                                      TRUE);
198   visu_config_file_entry_setVersion(resourceEntry, 3.1f);
199   g_signal_connect_swapped(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCES_SPIN,
200                            G_CALLBACK(onEntrySpin), (gpointer)0);
201   visu_config_file_addExportFunction(VISU_CONFIG_FILE_RESOURCE,
202                                      exportResourcesRenderingSpin);
203 }
visu_method_spin_init(VisuMethodSpin * obj)204 static void visu_method_spin_init(VisuMethodSpin *obj)
205 {
206   obj->priv = visu_method_spin_get_instance_private(obj);
207 
208   /* Private method. */
209   obj->priv->spinPolicy             = _spinPolicy;
210   obj->priv->spinAndAtomicRendering = _spinAndAtomicRendering;
211   obj->priv->spinModulusUsage       = _spinModulusUsage;
212   obj->priv->coneOrientation[0]     = _coneOrientation[0];
213   obj->priv->coneOrientation[1]     = _coneOrientation[1];
214   obj->priv->colorWheel             = _colorWheel;
215 
216   g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCES_SPIN,
217                           G_CALLBACK(onEntrySpin), obj, G_CONNECT_SWAPPED);
218 }
visu_method_spin_get_property(GObject * obj,guint property_id,GValue * value,GParamSpec * pspec)219 static void visu_method_spin_get_property(GObject* obj, guint property_id,
220                                           GValue *value, GParamSpec *pspec)
221 {
222   VisuMethodSpin *self = VISU_METHOD_SPIN(obj);
223 
224   switch (property_id)
225     {
226     case PROP_CONETHETA:
227       g_value_set_float(value, self->priv->coneOrientation[0]);
228       break;
229     case PROP_CONEPHI:
230       g_value_set_float(value, self->priv->coneOrientation[1]);
231       break;
232     case PROP_COLORWHEEL:
233       g_value_set_float(value, self->priv->colorWheel);
234       break;
235     case PROP_HIDINGMODE:
236       g_value_set_uint(value, self->priv->spinPolicy);
237       break;
238     case PROP_ATOMIC:
239       g_value_set_boolean(value, self->priv->spinAndAtomicRendering);
240       break;
241     case PROP_MODULUS:
242       g_value_set_uint(value, self->priv->spinModulusUsage);
243       break;
244     default:
245       /* We don't have any other property... */
246       G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
247       break;
248     }
249 }
visu_method_spin_set_property(GObject * obj,guint property_id,const GValue * value,GParamSpec * pspec)250 static void visu_method_spin_set_property(GObject* obj, guint property_id,
251                                         const GValue *value, GParamSpec *pspec)
252 {
253   VisuMethodSpin *self = VISU_METHOD_SPIN(obj);
254 
255   switch (property_id)
256     {
257     case PROP_CONETHETA:
258       self->priv->coneOrientation[0] = g_value_get_float(value);
259       break;
260     case PROP_CONEPHI:
261       self->priv->coneOrientation[1] = g_value_get_float(value);
262       break;
263     case PROP_COLORWHEEL:
264       self->priv->colorWheel = g_value_get_float(value);
265       break;
266     case PROP_HIDINGMODE:
267       self->priv->spinPolicy = g_value_get_uint(value);
268       break;
269     case PROP_ATOMIC:
270       self->priv->spinAndAtomicRendering = g_value_get_boolean(value);
271       break;
272     case PROP_MODULUS:
273       self->priv->spinModulusUsage = g_value_get_uint(value);
274       break;
275     default:
276       /* We don't have any other property... */
277       G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
278       break;
279     }
280 }
281 
282 /**
283  * visu_method_spin_new:
284  *
285  * Creates a #VisuMethodSpin object.
286  *
287  * Since: 3.8
288  *
289  * Returns: a newly allocated #VisuMethodSpin object.
290  **/
visu_method_spin_new(void)291 VisuMethodSpin* visu_method_spin_new(void)
292 {
293   return g_object_new(VISU_TYPE_METHOD_SPIN, NULL);
294 }
295 /**
296  * visu_method_spin_getDefault:
297  *
298  * Access the default spin method object.
299  *
300  * Since: 3.8
301  *
302  * Returns: (transfer none): the default spin method object.
303  **/
visu_method_spin_getDefault(void)304 VisuMethodSpin* visu_method_spin_getDefault(void)
305 {
306   if (!_default)
307     _default = g_object_new(VISU_TYPE_METHOD_SPIN, NULL);
308   return _default;
309 }
310 
_getOrientationColor(const VisuMethodSpin * method,gfloat rgb[3],const gfloat spinValues[3])311 static void _getOrientationColor(const VisuMethodSpin *method, gfloat rgb[3],
312                                  const gfloat spinValues[3])
313 {
314 #ifndef DEG2RAD
315 #define DEG2RAD(x) (0.01745329251994329509 * x)    /* pi / 180 * x */
316 #endif
317   gfloat spherical[3];
318   float cosCone, sinCone;
319   float matrix_rot_theta[3][3];
320   float matrix_rot_phi[3][3];
321   float cartesian[3];
322   float cartesian_prime[3];
323   float cartesian_second[3];
324   float hsl[3];
325 
326   g_return_if_fail(VISU_IS_METHOD_SPIN(method));
327 
328   /* We draw a spin shape. */
329   spherical[0] = 1;
330   spherical[1] = spinValues[TOOL_MATRIX_SPHERICAL_THETA];
331   spherical[2] = spinValues[TOOL_MATRIX_SPHERICAL_PHI];
332 
333   cosCone = cos(DEG2RAD(method->priv->coneOrientation[0]));
334   sinCone = sin(DEG2RAD(method->priv->coneOrientation[0]));
335   matrix_rot_theta[0][0] = cosCone;
336   matrix_rot_theta[0][1] = 0;
337   matrix_rot_theta[0][2] = -sinCone;
338   matrix_rot_theta[1][0] = 0;
339   matrix_rot_theta[1][1] = 1;
340   matrix_rot_theta[1][2] = 0;
341   matrix_rot_theta[2][0] = sinCone;
342   matrix_rot_theta[2][1] = 0;
343   matrix_rot_theta[2][2] = cosCone;
344 
345   cosCone = cos(DEG2RAD(-method->priv->coneOrientation[1]));
346   sinCone = sin(DEG2RAD(-method->priv->coneOrientation[1]));
347   matrix_rot_phi[0][0] = cosCone;
348   matrix_rot_phi[0][1] = -sinCone;
349   matrix_rot_phi[0][2] = 0;
350   matrix_rot_phi[1][0] = sinCone;
351   matrix_rot_phi[1][1] = cosCone;
352   matrix_rot_phi[1][2] = 0;
353   matrix_rot_phi[2][0] = 0;
354   matrix_rot_phi[2][1] = 0;
355   matrix_rot_phi[2][2] = 1;
356 
357   cartesian[0] = sin(DEG2RAD(spinValues[TOOL_MATRIX_SPHERICAL_THETA])) *
358     cos(DEG2RAD(spinValues[TOOL_MATRIX_SPHERICAL_PHI]));
359   cartesian[1] = sin(DEG2RAD(spinValues[TOOL_MATRIX_SPHERICAL_THETA])) *
360     sin(DEG2RAD(spinValues[TOOL_MATRIX_SPHERICAL_PHI]));
361   cartesian[2] = cos(DEG2RAD(spinValues[TOOL_MATRIX_SPHERICAL_THETA]));
362 
363   tool_matrix_productVector(cartesian_prime, matrix_rot_phi, cartesian);
364   tool_matrix_productVector(cartesian_second, matrix_rot_theta, cartesian_prime);
365 
366   tool_matrix_cartesianToSpherical(spherical, cartesian_second);
367 
368   hsl[0] = tool_modulo_float(spherical[2] - method->priv->colorWheel, 360.f) / 360.f;
369   hsl[1] = 1.f;
370   hsl[2] = 1.f - spherical[1] / 180.f;
371 
372   tool_color_convertHSLtoRGB(rgb, hsl);
373 }
374 /**
375  * visu_method_spin_getSpinVector:
376  * @method: a #VisuMethodSpin object.
377  * @dataSpin: a #VisuDataSpin object.
378  * @node: a #VisuNode from @dataSpin.
379  * @ratio: (out): the ratio to apply when drawing the spin.
380  * @rgb: (out): a location to store the colour.
381  * @withAtomic: (out): a location to store if atomic rendering is
382  * required.
383  *
384  * Retrieves the spin of @node in spherical coordinates. According to
385  * rendering policy from @method, @ratio, @rgb and @withAtomic will be
386  * set to their respective values. If no spin should be rendered for
387  * @node, %NULL is returned.
388  *
389  * Since: 3.8
390  *
391  * Returns: three floats representing the spin.
392  **/
visu_method_spin_getSpinVector(const VisuMethodSpin * method,const VisuDataSpin * dataSpin,const VisuNode * node,gfloat * ratio,gfloat rgb[3],gboolean * withAtomic)393 const gfloat* visu_method_spin_getSpinVector(const VisuMethodSpin *method,
394                                              const VisuDataSpin *dataSpin,
395                                              const VisuNode *node,
396                                              gfloat *ratio, gfloat rgb[3],
397                                              gboolean *withAtomic)
398 {
399   const gfloat *spinValues;
400   gfloat globalMax;
401   guint iele;
402 
403   spinValues = visu_node_values_vector_getAtSpherical(visu_data_spin_get(dataSpin),
404                                                       node);
405 
406   if (withAtomic)
407     *withAtomic = (method->priv->spinPolicy == VISU_METHOD_SPIN_ATOMIC_NULL &&
408                    spinValues[TOOL_MATRIX_SPHERICAL_MODULUS] == 0.f) ||
409       method->priv->spinAndAtomicRendering;
410 
411   if (spinValues && (spinValues[TOOL_MATRIX_SPHERICAL_MODULUS] != 0. ||
412 		     method->priv->spinPolicy == VISU_METHOD_SPIN_ALWAYS))
413     {
414       if (ratio)
415         /* Get the scaling factor for this element. */
416         switch (method->priv->spinModulusUsage)
417           {
418           case VISU_METHOD_SPIN_PER_TYPE:
419             *ratio = spinValues[TOOL_MATRIX_SPHERICAL_MODULUS] /
420               visu_data_spin_getMaxModulus(dataSpin, node->posElement);
421             break;
422           case VISU_METHOD_SPIN_GLOBAL:
423             globalMax = -G_MAXFLOAT;
424             for (iele = 0; iele < visu_node_array_getNElements(VISU_NODE_ARRAY_CONST(dataSpin), FALSE); iele++)
425               globalMax = MAX(visu_data_spin_getMaxModulus(dataSpin, iele), globalMax);
426             *ratio = spinValues[TOOL_MATRIX_SPHERICAL_MODULUS] / globalMax;
427             break;
428           case VISU_METHOD_SPIN_CONSTANT:
429             *ratio = 1.f;
430             break;
431           default:
432             *ratio = spinValues[TOOL_MATRIX_SPHERICAL_MODULUS];
433             break;
434           }
435       _getOrientationColor(method, rgb, spinValues);
436       return spinValues;
437     }
438   else
439     return (const gfloat*)0;
440 }
441 /**
442  * visu_method_spin_getHidingPolicyFromName:
443  * @name: (type filename): a string.
444  * @policy: a location.
445  *
446  * In the config file, the hiding policy resource is stored with its name (untranslated).
447  * This method is used to retrieve the id from the name.
448  *
449  * Returns: FALSE if the name does not match any value.
450  */
visu_method_spin_getHidingPolicyFromName(const char * name,VisuMethodSpinDrawingPolicy * policy)451 gboolean visu_method_spin_getHidingPolicyFromName(const char *name,
452                                                 VisuMethodSpinDrawingPolicy *policy)
453 {
454   g_return_val_if_fail(name && policy, FALSE);
455 
456   for (*policy = 0; *policy < VISU_METHOD_SPIN_N_MODES; *policy += 1)
457     if (!g_strcmp0(name, policyNameSpin[*policy]))
458       return TRUE;
459   return FALSE;
460 }
461 
onEntrySpin(VisuMethodSpin * self,VisuConfigFileEntry * entry,VisuConfigFile * object _U_)462 static void onEntrySpin(VisuMethodSpin *self, VisuConfigFileEntry *entry, VisuConfigFile *object _U_)
463 {
464   const gchar *label;
465 
466   label = visu_config_file_entry_getLabel(entry);
467 
468   if (!g_strcmp0(label, FLAG_SPIN_CONE_ANGLE))
469     {
470       float angles[2];
471       float rg[2] = {0.f, 360.f};
472 
473       if (!visu_config_file_entry_popTokenAsFloat(entry, 2, angles, rg))
474         return;
475       if (self)
476         g_object_set(self, "cone-theta", angles[0], "cone-phi", angles[1], NULL);
477       else
478         {
479           _coneOrientation[0] = angles[0];
480           _coneOrientation[1] = angles[1];
481         }
482     }
483   else if (!g_strcmp0(label, FLAG_SPIN_WHEEL_ANGLE))
484     {
485       float angle;
486       float rg[2] = {0.f, 360.f};
487 
488       if (!visu_config_file_entry_popTokenAsFloat(entry, 1, &angle, rg))
489         return;
490       if (self)
491         g_object_set(self, "cone-omega", angle, NULL);
492       else
493         _colorWheel = angle;
494     }
495   else if (!g_strcmp0(label, FLAG_SPIN_HIDING_MODE))
496     {
497       VisuMethodSpinDrawingPolicy modeId;
498 
499       if (!visu_config_file_entry_popTokenAsEnum(entry, &modeId, visu_method_spin_getHidingPolicyFromName))
500         return;
501       if (self)
502         g_object_set(self, "hiding-mode", modeId, NULL);
503       else
504         _spinPolicy = modeId;
505     }
506   else if (!g_strcmp0(label, FLAG_SPIN_AND_ATOMIC))
507     {
508       gboolean use;
509 
510       if (!visu_config_file_entry_popTokenAsBoolean(entry, 1, &use))
511         return;
512       if (self)
513         g_object_set(self, "use-atomic", use, NULL);
514       else
515         _spinAndAtomicRendering = use;
516     }
517   else if (!g_strcmp0(label, FLAG_SPIN_MODULUS))
518     {
519       int modeId;
520       int rg[2] = {0, VISU_METHOD_SPIN_N_MODULUS_MODES - 1};
521 
522       if (!visu_config_file_entry_popTokenAsInt(entry, 1, &modeId, rg))
523         return;
524       if (self)
525         g_object_set(self, "modulus-scaling", modeId, NULL);
526       else
527         _spinModulusUsage = modeId;
528     }
529   else
530     {
531       g_warning(_("Unknown flag '%s', value ignored."), label);
532     }
533 }
534 
exportResourcesRenderingSpin(GString * method,VisuData * dataObj _U_)535 static void exportResourcesRenderingSpin(GString *method, VisuData* dataObj _U_)
536 {
537   DBG_fprintf(stderr, "Method Spin: exporting resources...\n");
538 
539   visu_config_file_exportComment(method, DESC_RESOURCES_SPIN);
540   visu_config_file_exportEntry(method, FLAG_RESOURCES_SPIN, FLAG_SPIN_CONE_ANGLE,
541                                "%f %f", _coneOrientation[0], _coneOrientation[1]);
542 
543   visu_config_file_exportEntry(method, FLAG_RESOURCES_SPIN, FLAG_SPIN_WHEEL_ANGLE,
544                                "%f", _colorWheel);
545 
546   visu_config_file_exportEntry(method, FLAG_RESOURCES_SPIN, FLAG_SPIN_HIDING_MODE,
547                                "%s", policyNameSpin[_spinPolicy]);
548 
549   visu_config_file_exportEntry(method, FLAG_RESOURCES_SPIN, FLAG_SPIN_AND_ATOMIC,
550                                "%d", _spinAndAtomicRendering);
551 
552   visu_config_file_exportEntry(method, FLAG_RESOURCES_SPIN, FLAG_SPIN_MODULUS,
553                                "%d", _spinModulusUsage);
554   DBG_fprintf(stderr, "Method Spin: resources succesfully exported\n");
555 }
556