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
45 #include <math.h>
46 #include <string.h>
47
48 #include <GL/gl.h>
49 #include <GL/glu.h>
50
51 #include "view.h"
52 #include "objectList.h"
53 #include <visu_tools.h>
54 #include <renderingMethods/iface_nodeArrayRenderer.h>
55 #include <renderingBackend/visu_actionInterface.h>
56 #include <visu_configFile.h>
57
58
59 #include "interactive.h"
60
61 #define FLAG_PARAMETER_OBSERVE_METHOD "opengl_observe_method"
62 #define DESC_PARAMETER_OBSERVE_METHOD "Choose the observe method ; integer (0: constrained mode, 1: walker mode)"
63
64 #define FLAG_PARAMETER_CAMERA_SETTINGS "opengl_prefered_camera_orientation"
65 #define DESC_PARAMETER_CAMERA_SETTINGS "Saved prefered camera position ; three angles, two shifts, zoom and perspective level"
66 static float cameraData[7];
dissect_disp(tvbuff_t * tvb,packet_info * pinfo,proto_tree * parent_tree,void * data)67
68 /**
69 * SECTION:interactive
70 * @short_description: Gives tools to interact with the rendered
71 * area.
72 *
73 * <para>When one wants some interactions on the rendering area
74 * (either from the mouse or from the keyboard), one should create a new
75 * interactive object using visu_interactive_new(). Then, during the
76 * interactive session, several modes have been implemented:
77 * <itemizedlist>
78 * <listitem><para>
79 * #interactive_mark, is a mode where the mouse can select
80 * a node and this nodes become highlighted.
81 * </para></listitem>
82 * <listitem><para>
83 * #interactive_move, the mouse is then used to move
84 * picked nodes. Moves are possible in the plane of the screen or
85 * constrained along axis of the bounding box.
86 * </para></listitem>
87 * <listitem><para>
88 * #interactive_observe, in this mode, the mouse is used
89 * to change the position of camera, its orientation and its zoom
90 * characteristics.
91 * </para></listitem>
92 * <listitem><para>
93 * #interactive_pick, this mode is the most complex
94 * picking mode. It can select atoms up to two references and measure
95 * distances and angles.
96 * </para></listitem>
97 * <listitem><para>
98 * #interactive_pickAndObserve, this mode is a simplified
99 * mix of obervation and picking.
100 * </para></listitem>
101 * </itemizedlist>
102 * The observe mode has two different moving algorithms that can be
103 * changed using visu_interactive_class_setPreferedObserveMethod(). The
104 * first, called 'constrained' (cf. #interactive_constrained)
105 * corresponds to movements along parallels and meridians. When the
106 * mouse is moved along x axis, the camera raotates along a
107 * parallel. When the camera is moved along y axis, the camera rotate
108 * along a meridian. The top is always pointing to the north pole (in
109 * fact, omega is always forced to 0 in this mode). This mode has a
110 * 'strange' behavior when the observer is near a pole: moving mouse
111 * along x axis make the box rotates on itself. It is normal, because
112 * movements on x axis is equivalent to movements on parallel and near
113 * the poles, parallel are small circle around the z axis. This can be
114 * unnatural in some occasion and the other mode, called 'walker' (see
115 * #interactive_walker) can be used instead of the 'constrained'
116 * mode. In the former, the moving is done has if the observer was a
117 * walking ant on a sphere : moving the mouse along y axis makes the
118 * ant go on or forward ; and x axis movements makes the ant goes on
119 * its left or on it right. This is a more natural way to move the box
120 * but it has the inconvient that it is hard to return in a given
121 * position (omega has never the right value).
122 * </para>
123 */
124
125 /**
126 * VisuInteractiveClass:
127 * @parent: the parent.
128 * @preferedObserveMethod: the prefered method to move the camera.
129 * @savedCameras: the list of saved cameras.
130 * @lastCamera: a pointer on the last selected camera.
131 *
132 * An opaque structure representing the class of #VisuInteractive objects.
133 */
134 /**
135 * VisuInteractive:
136 *
137 * All fields are private.
138 */
139
140 struct _VisuInteractive
141 {
142 VisuObject parent;
143
144 /* Internal object gestion. */
145 gboolean dispose_has_run;
146
147 /* The kind of interactive (pick, observe...) */
148 VisuInteractiveId id;
149
150 /* Data needed in case of pick. */
151 /* The last selected node. */
152 gint idSelected;
153 /* The current first reference. */
154 gint idRef1;
155 /* The current second reference. */
156 gint idRef2;
157 /* */
158 GArray *idRegion;
159 /* The position of the pick. */
160 int xOrig, yOrig;
161 int xPrev, yPrev;
162
163 /* Data for the move action. */
164 gboolean movingPicked;
165 GArray *movingNodes;
166 float movingAxe[3];
167
168 /* The OpenGL list used to identify nodes. */
169 VisuGlExtNodes *nodeList;
170 gulong data_sig;
171
172 /* Signals to listen to. */
173 VisuData *dataObj;
174 gulong popDec_signal;
175
176 /* Copy of last event. */
177 ToolSimplifiedEvents ev;
178
179 /* Some message explaining the purpose of the interactive session. */
180 gchar *message;
181 };
182
183 enum
184 {
185 INTERACTIVE_OBSERVE_SIGNAL,
186 INTERACTIVE_PICK_ERROR_SIGNAL,
187 INTERACTIVE_PICK_NODE_SIGNAL,
188 INTERACTIVE_PICK_REGION_SIGNAL,
189 INTERACTIVE_PICK_MENU_SIGNAL,
190 INTERACTIVE_START_MOVE_SIGNAL,
191 INTERACTIVE_MOVE_SIGNAL,
192 INTERACTIVE_STOP_MOVE_SIGNAL,
193 INTERACTIVE_STOP_SIGNAL,
194 /* INTERACTIVE_EVENT_SIGNAL, */
195 N_INTERACTIVE_SIGNALS
196 };
197
198 static VisuInteractiveClass *local_class = NULL;
199
200 /* Internal variables. */
201 static guint interactive_signals[N_INTERACTIVE_SIGNALS] = { 0 };
202
203 /* Object gestion methods. */
204 static void visu_interactive_dispose (GObject* obj);
205 static void visu_interactive_finalize(GObject* obj);
206
207 /* Local methods. */
208 static void exportParameters(GString *data, VisuData *dataObj);
209 static void onReadCamera(VisuConfigFile *obj, VisuConfigFileEntry *entry, VisuInteractiveClass *klass);
210 static void onPopulationChange(VisuInteractive *inter, GArray *nodes, VisuData *dataObj);
211 static void _setData(VisuInteractive *inter, VisuData *dataObj);
212 static gboolean observe(VisuInteractive *inter, VisuGlView *view,
213 ToolSimplifiedEvents *ev);
214 static gboolean pick(VisuInteractive *inter, ToolSimplifiedEvents *ev);
215 static gboolean move(VisuInteractive *inter, VisuGlView *view, ToolSimplifiedEvents *ev);
216 static gboolean mark(VisuInteractive *inter, ToolSimplifiedEvents *ev);
217 static gboolean pickAndObserve(VisuInteractive *inter,
218 VisuGlView *view, ToolSimplifiedEvents *ev);
219 static gboolean drag(VisuInteractive *inter, VisuGlView *view, ToolSimplifiedEvents *ev);
220
221 G_DEFINE_TYPE(VisuInteractive, visu_interactive, VISU_TYPE_OBJECT)
222
223 static void g_cclosure_marshal_NODE_SELECTION(GClosure *closure,
224 GValue *return_value _U_,
225 guint n_param_values,
226 const GValue *param_values,
227 gpointer invocation_hint _U_,
228 gpointer marshal_data)
proto_reg_handoff_disp(void)229 {
230 typedef void (*callbackFunc)(gpointer data1, guint kind, VisuNodeArray *arr,
231 VisuNode *node1, VisuNode *node2, VisuNode *node3,
232 gpointer data2);
233 register callbackFunc callback;
234 register GCClosure *cc = (GCClosure*)closure;
235 register gpointer data1, data2;
236
237 g_return_if_fail(n_param_values == 6);
238
239 if (G_CCLOSURE_SWAP_DATA(closure))
240 {
241 data1 = closure->data;
242 data2 = g_value_peek_pointer(param_values + 0);
243 }
244 else
245 {
246 data1 = g_value_peek_pointer(param_values + 0);
247 data2 = closure->data;
248 }
249 callback = (callbackFunc)(size_t)(marshal_data ? marshal_data : cc->callback);
250
251 DBG_fprintf(stderr, "Interactive: marshall callback for node-selection"
252 " with nodes %p, %p, %p.\n",
253 (gpointer)g_value_get_boxed(param_values + 3),
254 (gpointer)g_value_get_boxed(param_values + 4),
255 (gpointer)g_value_get_boxed(param_values + 5));
256 callback(data1, g_value_get_uint(param_values + 1),
257 g_value_get_object(param_values + 2),
258 (VisuNode*)g_value_get_boxed(param_values + 3),
259 (VisuNode*)g_value_get_boxed(param_values + 4),
260 (VisuNode*)g_value_get_boxed(param_values + 5),
261 data2);
262 }
263 static void g_cclosure_marshal_NODE_MENU(GClosure *closure,
264 GValue *return_value _U_,
265 guint n_param_values,
266 const GValue *param_values,
267 gpointer invocation_hint _U_,
268 gpointer marshal_data)
269 {
270 typedef void (*callbackFunc)(gpointer data1, gint x, gint y,
271 VisuNode *node, gpointer data2);
272 register callbackFunc callback;
273 register GCClosure *cc = (GCClosure*)closure;
274 register gpointer data1, data2;
275
276 g_return_if_fail(n_param_values == 4);
277
278 if (G_CCLOSURE_SWAP_DATA(closure))
279 {
280 data1 = closure->data;
281 data2 = g_value_peek_pointer(param_values + 0);
282 }
283 else
284 {
285 data1 = g_value_peek_pointer(param_values + 0);
286 data2 = closure->data;
287 }
288 callback = (callbackFunc)(size_t)(marshal_data ? marshal_data : cc->callback);
289
290 DBG_fprintf(stderr, "Interactive: marshall callback for menu at %dx%d"
291 " with node %p.\n",
292 g_value_get_int(param_values + 1),
293 g_value_get_int(param_values + 2),
294 (gpointer)g_value_get_boxed(param_values + 3));
295 callback(data1, g_value_get_int(param_values + 1),
296 g_value_get_int(param_values + 2),
297 (VisuNode*)g_value_get_boxed(param_values + 3),
298 data2);
299 }
300
301 static void visu_interactive_class_init(VisuInteractiveClass *klass)
302 {
303 GType paramBool[1] = {G_TYPE_BOOLEAN};
304 GType paramUInt[1] = {G_TYPE_UINT};
305 VisuConfigFileEntry *resourceEntry;
306 int rgMethod[2] = {interactive_constrained, interactive_walker};
307 float rgCamera[2] = {-G_MAXFLOAT, G_MAXFLOAT};
308
309 local_class = klass;
310
311 DBG_fprintf(stderr, "visu Interactive: creating the class of the object.\n");
312
313 DBG_fprintf(stderr, " - adding new signals ;\n");
314 /**
315 * VisuInteractive::observe:
316 * @obj: the object emitting the signal.
317 * @bool: a boolean.
318 *
319 * This signal is emitted each time an observe session is start
320 * (@bool is TRUE) or finished (@bool is FALSE).
321 *
322 * Since: 3.6
323 */
324 interactive_signals[INTERACTIVE_OBSERVE_SIGNAL] =
325 g_signal_newv("observe", G_TYPE_FROM_CLASS(klass),
326 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
327 NULL, NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN,
328 G_TYPE_NONE, 1, paramBool);
329
330 /**
331 * VisuInteractive::selection-error:
332 * @obj: the object emitting the signal.
333 * @err: an error value.
334 *
335 * This signal is emitted each time a selection fails, providing the
336 * error in @err (see #VisuInteractivePickError).
337 *
338 * Since: 3.6
339 */
340 interactive_signals[INTERACTIVE_PICK_ERROR_SIGNAL] =
341 g_signal_newv("selection-error", G_TYPE_FROM_CLASS(klass),
342 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
343 NULL, NULL, NULL, g_cclosure_marshal_VOID__UINT,
344 G_TYPE_NONE, 1, paramUInt);
345
346 /**
347 * VisuInteractive::node-selection:
348 * @obj: the object emitting the signal.
349 * @kind: a flag, see #VisuInteractivePick.
350 * @array: (type VisuNodeArray*) (transfer none): the #VisuNodeArray
351 * hosting @node1, @node2 and @node3.
352 * @node1: (type VisuNode*) (transfer none): the primary node.
353 * @node2: (type VisuNode*) (transfer none): the secondary
354 * node, if any.
355 * @node3: (type VisuNode*) (transfer none): the tertiary
356 * node, if any.
357 *
358 * This signal is emitted each time a single node selection succeed, providing the
359 * kind in @kind (see #VisuInteractivePick). The corresponding nodes
360 * are stored in @node1, @node2 and @node3.
361 *
362 * Since: 3.6
363 */
364 interactive_signals[INTERACTIVE_PICK_NODE_SIGNAL] =
365 g_signal_new("node-selection", G_TYPE_FROM_CLASS(klass),
366 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
367 0, NULL, NULL, g_cclosure_marshal_NODE_SELECTION,
368 G_TYPE_NONE, 5, G_TYPE_UINT, VISU_TYPE_NODE_ARRAY, VISU_TYPE_NODE,
369 VISU_TYPE_NODE, VISU_TYPE_NODE, NULL);
370
371 /**
372 * VisuInteractive::region-selection:
373 * @obj: the object emitting the signal.
374 * @nodes: (element-type guint): an array of node ids.
375 *
376 * This signal is emitted each time a region selection succeed. The corresponding nodes
377 * are stored in @nodes.
378 *
379 * Since: 3.6
380 */
381 interactive_signals[INTERACTIVE_PICK_REGION_SIGNAL] =
382 g_signal_new("region-selection", G_TYPE_FROM_CLASS(klass),
383 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
384 0, NULL, NULL, g_cclosure_marshal_VOID__BOXED,
385 G_TYPE_NONE, 1, G_TYPE_ARRAY);
386
387 /**
388 * VisuInteractive::menu:
389 * @obj: the object emitting the signal.
390 * @x: the x coordinate.
391 * @y: the y coordinate.
392 * @node: (type VisuNode*) (transfer none) (allow-none): a #VisuNode.
393 *
394 * This signal is emitted each time a menu key stroke is done.
395 *
396 * Since: 3.7
397 */
398 interactive_signals[INTERACTIVE_PICK_MENU_SIGNAL] =
399 g_signal_new("menu", G_TYPE_FROM_CLASS(klass),
400 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
401 0, NULL, NULL, g_cclosure_marshal_NODE_MENU,
402 G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_INT, VISU_TYPE_NODE);
403
404 /**
405 * VisuInteractive::start-move:
406 * @obj: the object emitting the signal.
407 * @nodes: (element-type guint): an array of node ids.
408 *
409 * This signal is emitted each time a set of nodes are clicked to be
410 * moved. The corresponding nodes are stored in @nodes.
411 *
412 * Since: 3.6
413 */
414 interactive_signals[INTERACTIVE_START_MOVE_SIGNAL] =
415 g_signal_new("start-move", G_TYPE_FROM_CLASS(klass),
416 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
417 0, NULL, NULL, g_cclosure_marshal_VOID__BOXED,
418 G_TYPE_NONE, 1, G_TYPE_ARRAY);
419
420 /**
421 * VisuInteractive::move:
422 * @obj: the object emitting the signal.
423 * @delta: the delta of applied translation.
424 *
425 * This signal is emitted each time a set of nodes are moved. The
426 * corresponding movement translation is stored in delta.
427 *
428 * Since: 3.6
429 */
430 interactive_signals[INTERACTIVE_MOVE_SIGNAL] =
431 g_signal_new("move", G_TYPE_FROM_CLASS(klass),
432 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
433 0, NULL, NULL, g_cclosure_marshal_VOID__BOXED,
434 G_TYPE_NONE, 1, TOOL_TYPE_VECTOR);
435 /**
436 * VisuInteractive::stop-move:
437 * @obj: the object emitting the signal.
438 * @delta: the delta of applied translation.
439 *
440 * This signal is emitted when a set of nodes are finished moving. The
441 * corresponding movement complete translation is stored in delta.
442 *
443 * Since: 3.8
444 */
445 interactive_signals[INTERACTIVE_STOP_MOVE_SIGNAL] =
446 g_signal_new("stop-move", G_TYPE_FROM_CLASS(klass),
447 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
448 0, NULL, NULL, g_cclosure_marshal_VOID__BOXED,
449 G_TYPE_NONE, 1, TOOL_TYPE_VECTOR);
450 /**
451 * VisuInteractive::stop:
452 * @obj: the object emitting the signal.
453 *
454 * This signal is emitted each time a set of nodes are stopped to be
455 * moved.
456 *
457 * Since: 3.6
458 */
459 interactive_signals[INTERACTIVE_STOP_SIGNAL] =
460 g_signal_newv("stop", G_TYPE_FROM_CLASS(klass),
461 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
462 NULL, NULL, NULL, g_cclosure_marshal_VOID__VOID,
463 G_TYPE_NONE, 0, NULL);
464
465 /* Connect freeing methods. */
466 G_OBJECT_CLASS(klass)->dispose = visu_interactive_dispose;
467 G_OBJECT_CLASS(klass)->finalize = visu_interactive_finalize;
468
469 DBG_fprintf(stderr, " - add the resources.\n");
470 visu_config_file_addIntegerArrayEntry(VISU_CONFIG_FILE_PARAMETER,
471 FLAG_PARAMETER_OBSERVE_METHOD,
472 DESC_PARAMETER_OBSERVE_METHOD,
473 1, (int*)&klass->preferedObserveMethod, rgMethod, FALSE);
474 resourceEntry = visu_config_file_addFloatArrayEntry(VISU_CONFIG_FILE_PARAMETER,
475 FLAG_PARAMETER_CAMERA_SETTINGS,
476 DESC_PARAMETER_CAMERA_SETTINGS,
477 7, cameraData, rgCamera, FALSE);
478 visu_config_file_entry_setVersion(resourceEntry, 3.7f);
479 visu_config_file_addExportFunction(VISU_CONFIG_FILE_PARAMETER,
480 exportParameters);
481
482 g_signal_connect(VISU_CONFIG_FILE_PARAMETER, "parsed::" FLAG_PARAMETER_CAMERA_SETTINGS,
483 G_CALLBACK(onReadCamera), klass);
484
485 /* Set local variables to default. */
486 DBG_fprintf(stderr, " - setup the local variables.\n");
487 klass->preferedObserveMethod = interactive_constrained;
488 }
489
490 static void visu_interactive_init(VisuInteractive *obj)
491 {
492 DBG_fprintf(stderr, "Visu Interactive: creating a new interactive session (%p).\n",
493 (gpointer)obj);
494
495 obj->dispose_has_run = FALSE;
496 obj->idRef1 = -99;
497 obj->idRef2 = -99;
498 obj->idSelected = -99;
499 obj->idRegion = (GArray*)0;
500 obj->movingAxe[0] = 1.f;
501 obj->movingAxe[1] = 0.f;
502 obj->movingAxe[2] = 0.f;
503 obj->movingPicked = FALSE;
504 obj->movingNodes = (GArray*)0;
505
506 obj->nodeList = (VisuGlExtNodes*)0;
507
508 obj->dataObj = (VisuData*)0;
509 obj->popDec_signal = (gulong)0;
510
511 obj->message = (gchar*)0;
512 }
513
514 static void onPopulationChange(VisuInteractive *inter, GArray *nodes, VisuData *dataObj _U_)
515 {
516 guint i;
517
518 DBG_fprintf(stderr, "Visu Interactive: remove the specific nodes.\n");
519 for (i = 0; i < nodes->len; i++)
520 {
521 if (inter->idRef1 == g_array_index(nodes, gint, i))
522 inter->idRef1 = -1;
523 if (inter->idRef2 == g_array_index(nodes, gint, i))
524 inter->idRef2 = -1;
525 if (inter->idSelected == g_array_index(nodes, gint, i))
526 inter->idSelected = -1;
527 }
528 if (inter->idRegion)
529 g_array_unref(inter->idRegion);
530 inter->idRegion = (GArray*)0;
531 }
532
533 /* This method can be called several times.
534 It should unref all of its reference to
535 GObjects. */
536 static void visu_interactive_dispose(GObject* obj)
537 {
538 DBG_fprintf(stderr, "Visu Interactive: dispose object %p.\n", (gpointer)obj);
539
540 if (VISU_INTERACTIVE(obj)->dispose_has_run)
541 return;
542
543 VISU_INTERACTIVE(obj)->dispose_has_run = TRUE;
544
545 visu_interactive_setNodeList(VISU_INTERACTIVE(obj), (VisuGlExtNodes*)0);
546
547 /* Chain up to the parent class */
548 G_OBJECT_CLASS(visu_interactive_parent_class)->dispose(obj);
549 }
550 /* This method is called once only. */
551 static void visu_interactive_finalize(GObject* obj)
552 {
553 g_return_if_fail(obj);
554
555 DBG_fprintf(stderr, "Visu Interactive: finalize object %p.\n", (gpointer)obj);
556
557 if (VISU_INTERACTIVE(obj)->movingNodes)
558 g_array_unref(VISU_INTERACTIVE(obj)->movingNodes);
559 if (VISU_INTERACTIVE(obj)->idRegion)
560 g_array_unref(VISU_INTERACTIVE(obj)->idRegion);
561 g_free(VISU_INTERACTIVE(obj)->message);
562
563 /* Chain up to the parent class */
564 G_OBJECT_CLASS(visu_interactive_parent_class)->finalize(obj);
565 }
566
567 /**
568 * visu_interactive_new:
569 * @type: a #VisuInteractiveId flag.
570 *
571 * Creates a new interactive session of the given @type.
572 *
573 * Returns: (transfer full): a newly created object.
574 */
575 VisuInteractive* visu_interactive_new(VisuInteractiveId type)
576 {
577 VisuInteractive *inter;
578
579 inter = VISU_INTERACTIVE(g_object_new(VISU_TYPE_INTERACTIVE, NULL));
580 g_return_val_if_fail(inter, (VisuInteractive*)0);
581
582 DBG_fprintf(stderr, "Visu Interactive: start new interactive session %p (%d).\n",
583 (gpointer)inter, type);
584
585 inter->id = type;
586
587 return inter;
588 }
589 /**
590 * visu_interactive_getType:
591 * @inter: a #VisuInteractive object.
592 *
593 * It returns the kind of interactive session.
594 *
595 * Returns: a #VisuInteractiveId value.
596 */
597 VisuInteractiveId visu_interactive_getType(VisuInteractive *inter)
598 {
599 g_return_val_if_fail(VISU_IS_INTERACTIVE(inter), interactive_none);
600
601 return inter->id;
602 }
603 /**
604 * visu_interactive_setType:
605 * @inter: a #VisuInteractive object.
606 * @id: a #VisuInteractiveId.
607 *
608 * It changes the kind of interactive session.
609 *
610 * Returns: TRUE if indeed changed.
611 */
612 gboolean visu_interactive_setType(VisuInteractive *inter, VisuInteractiveId id)
613 {
614 g_return_val_if_fail(VISU_IS_INTERACTIVE(inter), FALSE);
615
616 if (inter->id == id)
617 return FALSE;
618
619 inter->id = id;
620 return TRUE;
621 }
622
623 static void onDataNotified(VisuInteractive *inter, GParamSpec *pspec _U_, VisuGlExtNodes *nodes)
624 {
625 _setData(inter, VISU_DATA(visu_node_array_renderer_getNodeArray(VISU_NODE_ARRAY_RENDERER(nodes))));
626 }
627
628 /**
629 * visu_interactive_setNodeList:
630 * @inter: a #VisuInteractive object.
631 * @nodes: (transfer full) (allow-none): a #VisuGlExtNodes object.
632 *
633 * Associate a #VisuGlExtNodes object for node selection. This is
634 * mandatory for move, pick and mark actions.
635 *
636 * Since: 3.7
637 **/
638 void visu_interactive_setNodeList(VisuInteractive *inter, VisuGlExtNodes *nodes)
639 {
640 g_return_if_fail(VISU_IS_INTERACTIVE(inter));
641
642 if (inter->nodeList == nodes)
643 return;
644
645 if (inter->nodeList)
646 {
647 g_signal_handler_disconnect(inter->nodeList, inter->data_sig);
648 g_object_unref(inter->nodeList);
649 }
650 if (nodes)
651 {
652 g_object_ref(nodes);
653 inter->data_sig = g_signal_connect_swapped(nodes, "notify::data",
654 G_CALLBACK(onDataNotified), inter);
655 }
656 inter->nodeList = nodes;
657 _setData(inter, (nodes) ? VISU_DATA(visu_node_array_renderer_getNodeArray(VISU_NODE_ARRAY_RENDERER(nodes))) : (VisuData*)0);
658 }
659
660 static void _setData(VisuInteractive *inter, VisuData *dataObj)
661 {
662 if (inter->idRef1 >= 0 &&
663 (!dataObj || !visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), inter->idRef1)))
664 inter->idRef1 = -99;
665 if (inter->idRef2 >= 0 &&
666 (!dataObj || !visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), inter->idRef2)))
667 inter->idRef2 = -99;
668 if (inter->idSelected >= 0 &&
669 (!dataObj || !visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), inter->idSelected)))
670 inter->idSelected = -99;
671 DBG_fprintf(stderr, "Visu Interactive: specific nodes are now:\n");
672 DBG_fprintf(stderr, " - selected %d.\n", inter->idSelected);
673 DBG_fprintf(stderr, " - ref. 1 %d.\n", inter->idRef1);
674 DBG_fprintf(stderr, " - ref. 2 %d.\n", inter->idRef2);
675
676 if (inter->idRegion)
677 g_array_unref(inter->idRegion);
678 inter->idRegion = (GArray*)0;
679
680 if (inter->dataObj == dataObj)
681 return;
682
683 if (inter->dataObj)
684 {
685 g_signal_handler_disconnect(inter->dataObj, inter->popDec_signal);
686 g_object_unref(inter->dataObj);
687 }
688 if (dataObj)
689 {
690 g_object_ref(dataObj);
691 inter->popDec_signal =
692 g_signal_connect_swapped(dataObj, "PopulationDecrease",
693 G_CALLBACK(onPopulationChange), (gpointer)inter);
694 }
695 inter->dataObj = dataObj;
696 }
697
698 /**
699 * visu_interactive_popSavedCamera:
700 * @inter: a #VisuInteractive object.
701 *
702 * @inter object stores camera settings as a ring. This routine goes
703 * to the next camera in the ring and returns the current one. The
704 * popped camera is not actually removed from the ring.
705 *
706 * Since: 3.6
707 *
708 * Returns: a pointer to the previously current #VisuGlCamera. It
709 * is owned by V_Sim and should not be touched.
710 */
711 VisuGlCamera* visu_interactive_popSavedCamera(VisuInteractive *inter)
712 {
713 VisuGlCamera *cur;
714 VisuInteractiveClass *klass;
715
716 klass = VISU_INTERACTIVE_GET_CLASS(inter);
717 g_return_val_if_fail(klass, (VisuGlCamera*)0);
718
719 if (!klass->lastCamera)
720 return (VisuGlCamera*)0;
721
722 cur = (VisuGlCamera*)klass->lastCamera->data;
723
724 klass->lastCamera = g_list_next(klass->lastCamera);
725 if (!klass->lastCamera)
726 klass->lastCamera = klass->savedCameras;
727
728 DBG_fprintf(stderr, "Interactive: pop, pointing now at camera %d.\n",
729 g_list_position(klass->savedCameras, klass->lastCamera));
730
731 return cur;
732 }
733 /**
734 * visu_interactive_getSavedCameras:
735 * @inter: a #VisuInteractive object.
736 * @cameras: (out) (element-type VisuGlCamera*): a location to store a list of cameras.
737 * @head: (out) (element-type VisuGlCamera*): a location to store a list of cameras.
738 *
739 * @inter object stores camera settings as a ring. One can access the
740 * set of saved cameras thanks to @cameras or to the current position
741 * in the ring thanks to @head. @cameras or @head are not copied and
742 * are owned by V_Sim. They should be considered read-only.
743 *
744 * Since: 3.6
745 */
746 void visu_interactive_getSavedCameras(VisuInteractive *inter,
747 GList **cameras, GList **head)
748 {
749 VisuInteractiveClass *klass;
750
751 klass = VISU_INTERACTIVE_GET_CLASS(inter);
752 g_return_if_fail(klass);
753
754 *cameras = klass->savedCameras;
755 *head = klass->lastCamera;
756 }
757 static gboolean cmpCameras(VisuGlCamera *c1, VisuGlCamera *c2)
758 {
759 return (c1 == c2) ||
760 (c1->theta == c2->theta &&
761 c1->phi == c2->phi &&
762 c1->omega == c2->omega &&
763 c1->xs == c2->xs &&
764 c1->ys == c2->ys/* && */
765 /* c1->gross == c2->gross && */
766 /* c1->d_red == c2->d_red */);
767 }
768 static void _pushSavedCamera(VisuInteractiveClass *klass, VisuGlCamera *camera)
769 {
770 VisuGlCamera *tmp;
771
772 g_return_if_fail(klass && camera);
773
774 for (klass->lastCamera = klass->savedCameras ;
775 klass->lastCamera &&
776 !cmpCameras((VisuGlCamera*)klass->lastCamera->data, camera);
777 klass->lastCamera = g_list_next(klass->lastCamera));
778
779 /* Case we don't find it, we add. */
780 if (!klass->lastCamera || (VisuGlCamera*)klass->lastCamera->data != camera)
781 {
782 tmp = g_malloc(sizeof(VisuGlCamera));
783 *tmp = *camera;
784 klass->savedCameras = g_list_prepend(klass->savedCameras, (gpointer)tmp);
785 }
786 klass->lastCamera = klass->savedCameras;
787 DBG_fprintf(stderr, "Interactive: push, storing now %d cameras.\n",
788 g_list_length(klass->savedCameras));
789 }
790 /**
791 * visu_interactive_pushSavedCamera:
792 * @inter: a #VisuInteractive object.
793 * @camera: a #VisuGlCamera object.
794 *
795 * @inter object stores camera settings as a ring. The given @camera
796 * is copied in the ring if its values not already exist. The current camera
797 * is set to this new one.
798 *
799 * Since: 3.6
800 */
801 void visu_interactive_pushSavedCamera(VisuInteractive *inter, VisuGlCamera *camera)
802 {
803 VisuInteractiveClass *klass;
804
805 klass = VISU_INTERACTIVE_GET_CLASS(inter);
806 _pushSavedCamera(klass, camera);
807 }
808 gboolean visuInteractiveRemove_savedCamera(VisuInteractive *inter, VisuGlCamera *camera)
809 {
810 VisuInteractiveClass *klass;
811 GList *lst;
812
813 klass = VISU_INTERACTIVE_GET_CLASS(inter);
814 g_return_val_if_fail(klass, FALSE);
815
816 for (lst = klass->savedCameras ;
817 lst && !cmpCameras((VisuGlCamera*)lst->data, camera);
818 lst = g_list_next(lst));
819
820 /* Case we don't find it, we return. */
821 if (!lst)
822 return FALSE;
823
824 g_free(lst->data);
825 klass->savedCameras = g_list_delete_link(klass->savedCameras, lst);
826 if (klass->lastCamera == lst)
827 klass->lastCamera = lst->next;
828 if (!klass->lastCamera)
829 klass->lastCamera = klass->savedCameras;
830 DBG_fprintf(stderr, "Interactive: remove, storing now %d cameras.\n",
831 g_list_length(klass->savedCameras));
832
833 return TRUE;
834 }
835
836
837 /**
838 * visu_interactive_handleEvent:
839 * @inter: a #VisuInteractive object ;
840 * @view: a #VisuGlView object the interaction happened on.
841 * @ev: a simplified event.
842 *
843 * This routine should be called by the rendering window when some
844 * event is raised on the rendering surface.
845 */
846 void visu_interactive_handleEvent(VisuInteractive *inter,
847 VisuGlView *view, ToolSimplifiedEvents *ev)
848 {
849 gboolean stop;
850
851 g_return_if_fail(VISU_IS_INTERACTIVE(inter));
852
853 DBG_fprintf(stderr, "Visu Interactive: handle event at %dx%d (%d %d).\n", ev->x, ev->y,
854 ev->button, ev->buttonType);
855 inter->ev = *ev;
856 switch (inter->id)
857 {
858 case interactive_observe:
859 stop = observe(inter, view, ev);
860 break;
861 case interactive_measureAndObserve:
862 stop = pickAndObserve(inter, view, ev);
863 break;
864 case interactive_measure:
865 case interactive_pick:
866 stop = pick(inter, ev);
867 break;
868 case interactive_move:
869 stop = move(inter, view, ev);
870 break;
871 case interactive_mark:
872 stop = mark(inter, ev);
873 break;
874 case interactive_drag:
875 stop = drag(inter, view, ev);
876 break;
877 default:
878 stop = FALSE;
879 break;
880 }
881
882 if (stop)
883 g_signal_emit(G_OBJECT(inter),
884 interactive_signals[INTERACTIVE_STOP_SIGNAL], 0, NULL);
885 }
886 /**
887 * visu_interactive_setReferences:
888 * @inter: a #VisuInteractive object.
889 * @from: another #VisuInteractive object.
890 *
891 * Copies all node ids used as reference from @from to @inter.
892 */
893 void visu_interactive_setReferences(VisuInteractive *inter, VisuInteractive *from)
894 {
895 g_return_if_fail(VISU_IS_INTERACTIVE(inter) && VISU_IS_INTERACTIVE(from));
896
897 inter->idSelected = from->idSelected;
898 inter->idRef1 = from->idRef1;
899 inter->idRef2 = from->idRef2;
900 }
901 /**
902 * visu_interactive_getEvent:
903 * @inter: a #VisuInteractive object.
904 *
905 * This routine can be called in callbacks of @inter to get some
906 * details about the event that raise signals like
907 * VisuInteractive::node-selection.
908 *
909 * Since: 3.7
910 *
911 * Returns: (transfer none): a location with details on the last event.
912 **/
913 ToolSimplifiedEvents* visu_interactive_getEvent(VisuInteractive *inter)
914 {
915 g_return_val_if_fail(VISU_IS_INTERACTIVE(inter), (ToolSimplifiedEvents*)0);
916
917 return &inter->ev;
918 }
919
920 /******************************************************************************/
921
922 static gboolean pickAndObserve(VisuInteractive *inter,
923 VisuGlView *view, ToolSimplifiedEvents *ev)
924 {
925 /* If button 1 or 2, use observe mode */
926 if (ev->button != 3 && ev->specialKey != Key_Menu)
927 return observe(inter, view, ev);
928 /* If button 3, use pick mode with button 3 as button 1 */
929 else
930 {
931 if (ev->shiftMod && !ev->controlMod)
932 {
933 ev->button = 2;
934 }
935 else if (!ev->shiftMod && ev->controlMod)
936 {
937 ev->button = 2;
938 }
939 else if (ev->shiftMod && ev->controlMod)
940 {
941 ev->shiftMod = FALSE;
942 ev->controlMod = TRUE;
943 ev->button = 1;
944 }
945 else
946 ev->button = 1;
947 ev->motion = FALSE;
948
949 return pick(inter, ev);
950 }
951 }
952
953 static gboolean observe(VisuInteractive *inter, VisuGlView *view,
954 ToolSimplifiedEvents *ev)
955 {
956 int sign_theta;
957 int dx, dy;
958 VisuGlCamera *camera;
959 float angles[3], ratio;
960 gdouble gross, persp;
961 gboolean zoom;
962
963 g_return_val_if_fail(ev && inter, TRUE);
964 if (ev->button == 3)
965 return (ev->buttonType == TOOL_BUTTON_TYPE_PRESS);
966 /* If the realese event is triggered, we exit only if it's
967 not a scroll event (button 4 and 5). */
968 if (ev->button > 0 && ev->buttonType == TOOL_BUTTON_TYPE_RELEASE)
969 if (ev->button != 4 && ev->button != 5 )
970 {
971 if (ev->button == 1)
972 g_signal_emit(G_OBJECT(inter),
973 interactive_signals[INTERACTIVE_OBSERVE_SIGNAL],
974 0 , FALSE, NULL);
975 return FALSE;
976 }
977
978 g_return_val_if_fail(view, FALSE);
979
980 /* fprintf(stderr, "%d %d, %d\n", ev->x, ev->y, ev->button); */
981 /* fprintf(stderr, "%d %d, %d\n", ev->shiftMod, ev->controlMod, ev->motion); */
982 /* Support de la roulette en zoom et perspective. */
983 if (ev->specialKey == Key_Page_Up || ev->specialKey == Key_Page_Down ||
984 ev->button == 4 || ev->button == 5)
985 {
986 g_object_get(view, "zoom", &gross, "perspective", &persp, NULL);
987 if ((ev->button == 4 || ev->specialKey == Key_Page_Up) && !ev->shiftMod)
988 g_object_set(view, "zoom", MIN(gross * 1.1, 999.), NULL);
989 else if ((ev->button == 4 || ev->specialKey == Key_Page_Up) && ev->shiftMod)
990 g_object_set(view, "perspective", MAX(persp / 1.1, 1.1), NULL);
991 else if ((ev->button == 5 || ev->specialKey == Key_Page_Down) && !ev->shiftMod)
992 g_object_set(view, "zoom", MAX(gross / 1.1, 0.2), NULL);
993 else if ((ev->button == 5 || ev->specialKey == Key_Page_Down) && ev->shiftMod)
994 g_object_set(view, "perspective", MIN(persp * 1.1, 100.), NULL);
995 }
996 else if (ev->button && !ev->motion)
997 {
998 inter->xOrig = ev->x;
999 inter->yOrig = ev->y;
1000 if (ev->button == 1)
1001 g_signal_emit(G_OBJECT(inter),
1002 interactive_signals[INTERACTIVE_OBSERVE_SIGNAL],
1003 0 , TRUE, NULL);
1004 }
1005 else if (ev->motion ||
1006 ev->specialKey == Key_Arrow_Down || ev->specialKey == Key_Arrow_Up ||
1007 ev->specialKey == Key_Arrow_Right || ev->specialKey == Key_Arrow_Left)
1008 {
1009 if (ev->motion)
1010 {
1011 dx = ev->x - inter->xOrig;
1012 dy = -(ev->y - inter->yOrig);
1013 }
1014 else
1015 {
1016 g_signal_emit(G_OBJECT(inter),
1017 interactive_signals[INTERACTIVE_OBSERVE_SIGNAL],
1018 0 , TRUE, NULL);
1019 dx = 0;
1020 dy = 0;
1021 if (ev->specialKey == Key_Arrow_Left)
1022 dx = -10;
1023 else if (ev->specialKey == Key_Arrow_Right)
1024 dx = +10;
1025 else if (ev->specialKey == Key_Arrow_Down)
1026 dy = -10;
1027 else if (ev->specialKey == Key_Arrow_Up)
1028 dy = +10;
1029 }
1030 zoom = (ev->button == 2);
1031 if(!zoom && !ev->shiftMod && !ev->controlMod)
1032 {
1033 if (local_class->preferedObserveMethod == interactive_constrained)
1034 {
1035 if(view->camera.theta > 0.0)
1036 sign_theta = 1;
1037 else
1038 sign_theta = -1;
1039 visu_gl_view_rotateBox(view, dy * 180.0f / view->window.height,
1040 -dx * 180.0f / view->window.width * sign_theta, angles);
1041 if (ev->motion)
1042 visu_gl_view_setThetaPhiOmega(view, angles[0], angles[1], 0.,
1043 VISU_GL_CAMERA_THETA |
1044 VISU_GL_CAMERA_PHI);
1045 else
1046 g_object_set(view, "theta", angles[0], "phi", angles[1], NULL);
1047 }
1048 else if (local_class->preferedObserveMethod == interactive_walker)
1049 {
1050 visu_gl_view_rotateCamera(view, dy * 180.0f / view->window.height,
1051 -dx * 180.0f / view->window.width, angles);
1052 if (ev->motion)
1053 visu_gl_view_setThetaPhiOmega(view, angles[0], angles[1], angles[2],
1054 VISU_GL_CAMERA_THETA |
1055 VISU_GL_CAMERA_PHI |
1056 VISU_GL_CAMERA_OMEGA);
1057 else
1058 g_object_set(view, "theta", angles[0], "phi", angles[1],
1059 "omega", angles[2], NULL);
1060 }
1061 }
1062 else if(!zoom && ev->shiftMod && !ev->controlMod)
1063 {
1064 ratio = 1. / MIN(view->window.width, view->window.height) /
1065 view->camera.gross * (view->camera.d_red - 1.f) / view->camera.d_red;
1066 visu_gl_view_setXsYs(view,
1067 view->camera.xs + (float)dx * ratio,
1068 view->camera.ys + (float)dy * ratio,
1069 VISU_GL_CAMERA_XS | VISU_GL_CAMERA_YS);
1070 }
1071 else if(!zoom && ev->controlMod && !ev->shiftMod)
1072 {
1073 if (ABS(dx) > ABS(dy))
1074 visu_gl_view_setThetaPhiOmega(view, 0., 0.,
1075 view->camera.omega + dx *
1076 180.0f / view->window.width,
1077 VISU_GL_CAMERA_OMEGA);
1078 else
1079 visu_gl_view_setThetaPhiOmega(view, 0., 0.,
1080 view->camera.omega + dy *
1081 180.0f / view->window.height,
1082 VISU_GL_CAMERA_OMEGA);
1083 }
1084 else if(zoom && !ev->shiftMod)
1085 visu_gl_view_setGross(view, view->camera.gross *
1086 (1. + (float)dy *
1087 3.0f / view->window.height));
1088 else if(zoom && ev->shiftMod)
1089 visu_gl_view_setPersp(view, view->camera.d_red *
1090 (1. - (float)dy *
1091 5.0f / view->window.height));
1092 if (ev->motion)
1093 {
1094 inter->xOrig = ev->x;
1095 inter->yOrig = ev->y;
1096 }
1097 else
1098 g_signal_emit(G_OBJECT(inter),
1099 interactive_signals[INTERACTIVE_OBSERVE_SIGNAL],
1100 0 , FALSE, NULL);
1101 }
1102 else if (ev->letter == 'r')
1103 {
1104 camera = visu_interactive_popSavedCamera(inter);
1105 if (camera)
1106 {
1107 g_object_set(view, "theta", camera->theta, "phi", camera->phi,
1108 "omega", camera->omega, "zoom", camera->gross,
1109 "perspective", camera->d_red, NULL);
1110 visu_gl_view_setXsYs(view, camera->xs, camera->ys,
1111 VISU_GL_CAMERA_XS | VISU_GL_CAMERA_YS);
1112 }
1113 }
1114 else if (ev->letter == 's' && !ev->shiftMod && !ev->controlMod)
1115 visu_interactive_pushSavedCamera(inter, &view->camera);
1116 else if (ev->letter == 's' && ev->shiftMod && !ev->controlMod)
1117 visuInteractiveRemove_savedCamera(inter, &view->camera);
1118 else if (ev->letter == 'x')
1119 visu_gl_view_alignToAxis(view, TOOL_XYZ_X);
1120 else if (ev->letter == 'y')
1121 visu_gl_view_alignToAxis(view, TOOL_XYZ_Y);
1122 else if (ev->letter == 'z')
1123 visu_gl_view_alignToAxis(view, TOOL_XYZ_Z);
1124 return FALSE;
1125 }
1126 static void glDrawSelection(VisuInteractive *inter, int x, int y)
1127 {
1128 int viewport[4];
1129
1130 glPushAttrib(GL_ENABLE_BIT);
1131 glDisable(GL_FOG);
1132 glEnable(GL_BLEND);
1133 glDisable(GL_DEPTH_TEST);
1134 glDisable(GL_LIGHTING);
1135 glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
1136 glGetIntegerv(GL_VIEWPORT, viewport);
1137 glMatrixMode(GL_PROJECTION);
1138 glPushMatrix();
1139 glLoadIdentity();
1140 gluOrtho2D(0.0, (float)viewport[2], 0., (float)viewport[3]);
1141 glMatrixMode(GL_MODELVIEW);
1142 glPushMatrix();
1143 glLoadIdentity();
1144 glColor4f(1.f, 1.f, 1.f, 1.f);
1145 glLineWidth(2);
1146 glDrawBuffer(GL_FRONT);
1147 /* We erase the previous selecting rectangle. */
1148 glBegin(GL_LINE_LOOP);
1149 glVertex3f(inter->xOrig, (float)viewport[3] - inter->yOrig, 0.f);
1150 glVertex3f(inter->xPrev, (float)viewport[3] - inter->yOrig, 0.f);
1151 glVertex3f(inter->xPrev, (float)viewport[3] - inter->yPrev, 0.f);
1152 glVertex3f(inter->xOrig, (float)viewport[3] - inter->yPrev, 0.f);
1153 glEnd();
1154 glFlush();
1155
1156 /* We draw the new selecting rectangle. */
1157 if (x > 0 && y > 0)
1158 {
1159 glBegin(GL_LINE_LOOP);
1160 glVertex3f(inter->xOrig, (float)viewport[3] - inter->yOrig, 0.f);
1161 glVertex3f(x, (float)viewport[3] - inter->yOrig, 0.f);
1162 glVertex3f(x, (float)viewport[3] - y, 0.f);
1163 glVertex3f(inter->xOrig, (float)viewport[3] - y, 0.f);
1164 glEnd();
1165 glFlush();
1166 }
1167 glDrawBuffer(GL_BACK);
1168 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1169 glPopMatrix();
1170 glMatrixMode(GL_PROJECTION);
1171 glPopMatrix();
1172 glMatrixMode(GL_MODELVIEW);
1173 glPopAttrib();
1174 }
1175
1176 static gboolean pick(VisuInteractive *inter, ToolSimplifiedEvents *ev)
1177 {
1178 int nodeId;
1179 gboolean pickOnly;
1180 VisuInteractivePick pick;
1181 VisuInteractivePickError error;
1182 VisuNodeArray *nodeArray;
1183 VisuNode *nodes[3];
1184
1185 g_return_val_if_fail(ev && inter, TRUE);
1186 /* We store the pickInfo into the VisuData on press,
1187 but we apply it only at release if there was no drag action. */
1188 if (ev->button == 3)
1189 {
1190 if (ev->buttonType == TOOL_BUTTON_TYPE_PRESS)
1191 return TRUE;
1192 else
1193 return FALSE;
1194 }
1195
1196 pickOnly = (inter->id == interactive_pick);
1197
1198 if (ev->button == 1 && ev->motion && !pickOnly)
1199 {
1200 /* We drag a selecting area. */
1201 /* A nodeInfo should have been stored, we retrieve it. */
1202 DBG_fprintf(stderr, "Interactive: pick, drag to %dx%d.\n", ev->x, ev->y);
1203 glDrawSelection(inter, ev->x, ev->y);
1204 inter->xPrev = ev->x;
1205 inter->yPrev = ev->y;
1206 }
1207 else if (ev->buttonType == TOOL_BUTTON_TYPE_PRESS)
1208 {
1209 /* We create and store a nodeInfo data. */
1210 DBG_fprintf(stderr, "Interactive: pick, press at %dx%d.\n", ev->x, ev->y);
1211 inter->xOrig = ev->x;
1212 inter->yOrig = ev->y;
1213 inter->xPrev = ev->x;
1214 inter->yPrev = ev->y;
1215 }
1216 else if (ev->buttonType == TOOL_BUTTON_TYPE_RELEASE)
1217 {
1218 g_return_val_if_fail(inter->nodeList, TRUE);
1219
1220 /* Reset current selection. */
1221 if (inter->idRegion)
1222 g_array_unref(inter->idRegion);
1223 inter->idRegion = (GArray*)0;
1224 inter->idSelected = -1;
1225
1226 DBG_fprintf(stderr, "Interactive: pick, release from %dx%d.\n", inter->xOrig, inter->yOrig);
1227
1228 /* Set new selection from click. */
1229 pick = PICK_NONE;
1230 /* If no drag action, we select the node and compute the pick mesure. */
1231 if ((inter->xOrig == inter->xPrev &&
1232 inter->yOrig == inter->yPrev) || pickOnly)
1233 {
1234 nodeId = visu_gl_ext_nodes_getSelection(inter->nodeList, ev->x, ev->y);
1235
1236 error = PICK_ERROR_NO_SELECTION;
1237
1238 DBG_fprintf(stderr, "Visu Interactive: set selection (single %d %d).\n",
1239 ev->shiftMod, ev->controlMod);
1240 if (pickOnly && (ev->button == 1 || ev->button == 2))
1241 {
1242 DBG_fprintf(stderr, "Visu Interactive: set pick node %d.\n", nodeId);
1243 inter->idSelected = nodeId;
1244 pick = (nodeId >= 0)?PICK_SELECTED:PICK_NONE;
1245 }
1246 else if (!pickOnly && ev->button == 1 && !ev->controlMod)
1247 {
1248 if (nodeId >= 0)
1249 {
1250 pick = PICK_SELECTED;
1251 if (nodeId == inter->idRef1 || nodeId == inter->idRef2)
1252 {
1253 error = PICK_ERROR_SAME_REF;
1254 pick = PICK_NONE;
1255 }
1256
1257 if (pick != PICK_NONE)
1258 {
1259 inter->idSelected = nodeId;
1260 if (inter->idRef1 >= 0)
1261 pick = (inter->idRef2 < 0)?PICK_DISTANCE:PICK_ANGLE;
1262 }
1263 }
1264 else
1265 pick = PICK_NONE;
1266 }
1267 else if (!pickOnly && ev->button == 1 && ev->controlMod)
1268 pick = (nodeId < 0)?PICK_NONE:PICK_HIGHLIGHT;
1269 else if (!pickOnly && ev->button == 2 && ev->shiftMod && !ev->controlMod)
1270 {
1271 DBG_fprintf(stderr, "Visu Interactive: set ref1 info for node %d.\n",
1272 nodeId);
1273 pick = PICK_REFERENCE_1;
1274 /* The error case. */
1275 if (nodeId >= 0 && inter->idRef2 == nodeId)
1276 {
1277 error = PICK_ERROR_SAME_REF;
1278 pick = PICK_NONE;
1279 }
1280 else if (nodeId < 0 && inter->idRef2 >= 0)
1281 {
1282 error = PICK_ERROR_REF2;
1283 pick = PICK_NONE;
1284 }
1285
1286 if (pick != PICK_NONE)
1287 {
1288 inter->idRef1 = nodeId;
1289 pick = (nodeId < 0)?PICK_UNREFERENCE_1:PICK_REFERENCE_1;
1290 }
1291 }
1292 else if (!pickOnly && ev->button == 2 && !ev->shiftMod && ev->controlMod)
1293 {
1294 DBG_fprintf(stderr, "Visu Interactive: set ref2 info for node %d.\n",
1295 nodeId);
1296 pick = PICK_REFERENCE_2;
1297 /* The error case. */
1298 if (nodeId >= 0 && inter->idRef1 < 0)
1299 {
1300 error = PICK_ERROR_REF1;
1301 pick = PICK_NONE;
1302 }
1303 else if (nodeId >= 0 && inter->idRef1 == nodeId)
1304 {
1305 error = PICK_ERROR_SAME_REF;
1306 pick = PICK_NONE;
1307 }
1308
1309 if (pick != PICK_NONE)
1310 {
1311 inter->idRef2 = nodeId;
1312 pick = (nodeId < 0)?PICK_UNREFERENCE_2:PICK_REFERENCE_2;
1313 }
1314 }
1315 else if (!pickOnly && ev->button == 2 && !ev->shiftMod && !ev->controlMod)
1316 pick = (nodeId < 0)?PICK_NONE:PICK_INFORMATION;
1317 else
1318 return FALSE;
1319
1320 DBG_fprintf(stderr, " | OK.\n");
1321 if (pick != PICK_NONE)
1322 {
1323 nodeArray = VISU_NODE_ARRAY(inter->dataObj);
1324 nodes[0] = visu_node_array_getFromId(nodeArray, nodeId);
1325 nodes[1] = visu_node_array_getFromId
1326 (nodeArray, (pick != PICK_REFERENCE_1)?inter->idRef1:nodeId);
1327 nodes[2] = visu_node_array_getFromId
1328 (nodeArray, (pick != PICK_REFERENCE_2)?inter->idRef2:nodeId);
1329 DBG_fprintf(stderr, "Interactive: emit node-selection with nodes"
1330 " %p, %p, %p.\n", (gpointer)nodes[0],
1331 (gpointer)nodes[1], (gpointer)nodes[2]);
1332 switch (pick)
1333 {
1334 case (PICK_SELECTED):
1335 case (PICK_HIGHLIGHT):
1336 case (PICK_INFORMATION):
1337 if (nodes[0])
1338 g_signal_emit(G_OBJECT(inter),
1339 interactive_signals[INTERACTIVE_PICK_NODE_SIGNAL],
1340 0, pick, inter->dataObj, nodes[0], nodes[1], nodes[2]);
1341 break;
1342 case (PICK_REFERENCE_1):
1343 case (PICK_UNREFERENCE_1):
1344 g_signal_emit(G_OBJECT(inter),
1345 interactive_signals[INTERACTIVE_PICK_NODE_SIGNAL],
1346 0 , pick, inter->dataObj, nodes[0], nodes[1], nodes[2]);
1347 break;
1348 case (PICK_REFERENCE_2):
1349 case (PICK_UNREFERENCE_2):
1350 if (nodes[1])
1351 g_signal_emit(G_OBJECT(inter),
1352 interactive_signals[INTERACTIVE_PICK_NODE_SIGNAL],
1353 0 , pick, inter->dataObj, nodes[0], nodes[1], nodes[2]);
1354 break;
1355 case (PICK_DISTANCE):
1356 if (nodes[0] && nodes[1])
1357 g_signal_emit(G_OBJECT(inter),
1358 interactive_signals[INTERACTIVE_PICK_NODE_SIGNAL],
1359 0 , pick, inter->dataObj, nodes[0], nodes[1], nodes[2]);
1360 break;
1361 case (PICK_ANGLE):
1362 if (nodes[0] && nodes[1] && nodes[2])
1363 g_signal_emit(G_OBJECT(inter),
1364 interactive_signals[INTERACTIVE_PICK_NODE_SIGNAL],
1365 0 , pick, inter->dataObj, nodes[0], nodes[1], nodes[2]);
1366 break;
1367 default:
1368 break;
1369 }
1370 }
1371 else if (pick == PICK_NONE && error != PICK_ERROR_NONE)
1372 g_signal_emit(G_OBJECT(inter),
1373 interactive_signals[INTERACTIVE_PICK_ERROR_SIGNAL],
1374 0 , error, NULL);
1375 }
1376 /* If drag action, we compute all the nodes in the rectangle
1377 and we erase it. */
1378 else
1379 {
1380 /* We get the list of selected nodes. */
1381 glDrawSelection(inter, -1, -1);
1382 /* Get all nodes in the region. */
1383 inter->idRegion = visu_gl_ext_nodes_getSelectionByRegion(inter->nodeList,
1384 inter->xOrig, inter->yOrig,
1385 ev->x, ev->y);
1386 DBG_fprintf(stderr, "Interactive: set selection (drag).\n");
1387 /* Copy the region list. */
1388 if (inter->idRegion)
1389 g_signal_emit(G_OBJECT(inter),
1390 interactive_signals[INTERACTIVE_PICK_REGION_SIGNAL],
1391 0 , inter->idRegion, NULL);
1392 else
1393 g_signal_emit(G_OBJECT(inter),
1394 interactive_signals[INTERACTIVE_PICK_ERROR_SIGNAL],
1395 0 , PICK_ERROR_NO_SELECTION, NULL);
1396 }
1397 }
1398 else if (ev->specialKey == Key_Menu)
1399 {
1400 g_return_val_if_fail(inter->nodeList, TRUE);
1401
1402 /* Set new selection from click. */
1403 pick = PICK_NONE;
1404 /* We select the node or none. */
1405 nodeId = visu_gl_ext_nodes_getSelection(inter->nodeList, ev->x, ev->y);
1406 if (nodeId >= 0)
1407 {
1408 nodeArray = VISU_NODE_ARRAY(inter->dataObj);
1409 nodes[0] = visu_node_array_getFromId(nodeArray, nodeId);
1410 }
1411 else
1412 nodes[0] = (VisuNode*)0;
1413 g_signal_emit(G_OBJECT(inter),
1414 interactive_signals[INTERACTIVE_PICK_MENU_SIGNAL],
1415 0, ev->root_x, ev->root_y, nodes[0], NULL);
1416 }
1417 return FALSE;
1418 }
1419
1420 static gboolean move(VisuInteractive *inter, VisuGlView *view, ToolSimplifiedEvents *ev)
1421 {
1422 int dx, dy, nodeId;
1423 float ratio, z, xyz[3];
1424 VisuNode *node;
1425 float delta[3], delta0[3], centre[3];
1426 guint i;
1427
1428 g_return_val_if_fail(ev && inter, TRUE);
1429 if (ev->button == 3)
1430 {
1431 if (ev->buttonType == TOOL_BUTTON_TYPE_PRESS)
1432 return TRUE;
1433 else
1434 return FALSE;
1435 }
1436 if (ev->button != 1 && ev->button != 2 && ev->button != 4 && ev->button != 5 &&
1437 ev->specialKey != Key_Arrow_Left && ev->specialKey != Key_Arrow_Right &&
1438 ev->specialKey != Key_Arrow_Up && ev->specialKey != Key_Arrow_Down)
1439 return FALSE;
1440 DBG_fprintf(stderr, "Visu Interactive: event (%d - %d, %d %d).\n",
1441 ev->button, ev->specialKey, ev->shiftMod, ev->controlMod);
1442
1443 if ((ev->motion == 1 || ev->button == 4 || ev->button == 5 ||
1444 ev->specialKey == Key_Arrow_Left || ev->specialKey == Key_Arrow_Right ||
1445 ev->specialKey == Key_Arrow_Up || ev->specialKey == Key_Arrow_Down) &&
1446 inter->movingNodes)
1447 {
1448 DBG_fprintf(stderr, "Visu Interactive: drag action (%dx%d).\n", ev->x, ev->y);
1449 if (ev->specialKey == Key_Arrow_Left)
1450 {
1451 ev->x = inter->xPrev - 1;
1452 ev->y = inter->yPrev;
1453 ev->button = 1;
1454 }
1455 else if (ev->specialKey == Key_Arrow_Right)
1456 {
1457 ev->x = inter->xPrev + 1;
1458 ev->y = inter->yPrev;
1459 ev->button = 1;
1460 }
1461 else if (ev->specialKey == Key_Arrow_Up)
1462 {
1463 ev->x = inter->xPrev;
1464 ev->y = inter->yPrev - 1;
1465 ev->button = 1;
1466 }
1467 else if (ev->specialKey == Key_Arrow_Down)
1468 {
1469 ev->x = inter->xPrev;
1470 ev->y = inter->yPrev + 1;
1471 ev->button = 1;
1472 }
1473 dx = ev->x - inter->xPrev;
1474 dy = -(ev->y - inter->yPrev);
1475 DBG_fprintf(stderr, " | dx x dy : %d x %d\n", dx, dy);
1476
1477 g_return_val_if_fail(inter->nodeList, TRUE);
1478
1479 /* Get the camera orientation. */
1480 if (!ev->shiftMod && !ev->controlMod)
1481 {
1482 if (ev->button == 1)
1483 {
1484 visu_box_getCentre(visu_boxed_getBox(VISU_BOXED(view)), centre);
1485 glMatrixMode(GL_MODELVIEW);
1486 glPushMatrix();
1487 glTranslated(-centre[0], -centre[1], -centre[2]);
1488 z = 0.f;
1489 for (i = 0; i < inter->movingNodes->len; i++)
1490 {
1491 node = visu_node_array_getFromId
1492 (VISU_NODE_ARRAY(inter->dataObj),
1493 g_array_index(inter->movingNodes, guint, i));
1494 visu_data_getNodePosition(inter->dataObj, node, xyz);
1495 z += visu_gl_view_getZCoordinate(view, xyz);
1496 }
1497 z /= (float)inter->movingNodes->len;
1498 /* Now, z contains the prev real space coord z of the
1499 selected nodes (averaged). */
1500
1501 visu_gl_view_getRealCoordinates(view, delta, (float)ev->x, (float)ev->y, z);
1502 visu_gl_view_getRealCoordinates(view, delta0, (float)inter->xPrev,
1503 (float)inter->yPrev, z);
1504 glPopMatrix();
1505 /* Now, delta contains the new real space coord. */
1506
1507 delta[0] -= delta0[0];
1508 delta[1] -= delta0[1];
1509 delta[2] -= delta0[2];
1510 }
1511 else if (ev->button > 1)
1512 {
1513 if (ev->button == 4)
1514 dy = -5;
1515 else if (ev->button == 5)
1516 dy = +5;
1517 ratio = visu_gl_window_getFileUnitPerPixel(&view->window) * dy;
1518 delta[0] = inter->movingAxe[0] * ratio;
1519 delta[1] = inter->movingAxe[1] * ratio;
1520 delta[2] = inter->movingAxe[2] * ratio;
1521 }
1522 }
1523 else if (ev->button == 1)
1524 {
1525 delta[0] = 0.f;
1526 delta[1] = 0.f;
1527 delta[2] = 0.f;
1528 ratio = visu_gl_window_getFileUnitPerPixel(&view->window);
1529 if (ABS(dy) > ABS(dx))
1530 ratio *= ((dy > 0)?1.f:-1.f);
1531 else
1532 ratio *= ((dx > 0)?1.f:-1.f);
1533 if (ev->shiftMod && !ev->controlMod)
1534 delta[0] = ratio * sqrt(dx * dx + dy * dy);
1535 else if (ev->controlMod && !ev->shiftMod)
1536 delta[1] = ratio * sqrt(dx * dx + dy * dy);
1537 else if (ev->controlMod && ev->shiftMod)
1538 delta[2] = ratio * sqrt(dx * dx + dy * dy);
1539 }
1540 else
1541 return FALSE;
1542
1543 DBG_fprintf(stderr, "Visu Interactive: drag a list of %d nodes of %gx%gx%g.\n",
1544 inter->movingNodes->len,
1545 delta[0], delta[1], delta[2]);
1546 visu_node_array_shiftNodes(VISU_NODE_ARRAY(inter->dataObj), inter->movingNodes, delta);
1547
1548 /* Update stored position for drag info. */
1549 inter->xPrev = ev->x;
1550 inter->yPrev = ev->y;
1551
1552 DBG_fprintf(stderr, "Visu Interactive: emit the 'move' signal.\n");
1553 g_signal_emit(G_OBJECT(inter),
1554 interactive_signals[INTERACTIVE_MOVE_SIGNAL],
1555 0 , delta, NULL);
1556 }
1557 else if ((ev->button == 1 || ev->button == 2) &&
1558 ev->buttonType == TOOL_BUTTON_TYPE_PRESS)
1559 {
1560 /* Store the position to find the drag values. */
1561 inter->xOrig = inter->xPrev = ev->x;
1562 inter->yOrig = inter->yPrev = ev->y;
1563
1564 /* Empty previously picked movingNodes. */
1565 if (inter->movingPicked)
1566 {
1567 g_array_unref(inter->movingNodes);
1568 inter->movingNodes = (GArray*)0;
1569 }
1570
1571 /* Case no list set yet. */
1572 if (inter->movingNodes == (GArray*)0)
1573 {
1574 g_return_val_if_fail(inter->nodeList, FALSE);
1575
1576 nodeId = visu_gl_ext_nodes_getSelection(inter->nodeList, ev->x, ev->y);
1577 if (nodeId < 0)
1578 return FALSE;
1579
1580 inter->movingNodes = g_array_new(FALSE, FALSE, sizeof(guint));
1581 g_array_append_val(inter->movingNodes, nodeId);
1582 inter->movingPicked = TRUE;
1583 }
1584
1585 DBG_fprintf(stderr, "Visu Interactive: emit the 'start-move' signal.\n");
1586 g_signal_emit(G_OBJECT(inter),
1587 interactive_signals[INTERACTIVE_START_MOVE_SIGNAL],
1588 0, inter->movingNodes, NULL);
1589 }
1590 else if ((ev->button == 1 || ev->button == 2) &&
1591 ev->buttonType == TOOL_BUTTON_TYPE_RELEASE &&
1592 inter->movingNodes)
1593 {
1594 /* DBG_fprintf(stderr, "Visu Interactive: stop dragging a list of %d nodes.\n", */
1595 /* inter->movingNodes->len); */
1596 /* node = (VisuNode*)0; */
1597 DBG_fprintf(stderr, "Visu Interactive: emit the 'stop-move' signal.\n");
1598 g_signal_emit(G_OBJECT(inter),
1599 interactive_signals[INTERACTIVE_STOP_MOVE_SIGNAL],
1600 0, delta, NULL);
1601 }
1602
1603 return FALSE;
1604 }
1605 /**
1606 * visu_interactive_setMovingNodes:
1607 * @inter: a #VisuInteractive object.
1608 * @nodeIds: (element-type guint) (allow-none): a list of node ids.
1609 *
1610 * Defines the nodes that should be moved if @inter is a move
1611 * action session. The list is actually copied.
1612 */
1613 void visu_interactive_setMovingNodes(VisuInteractive *inter, GArray *nodeIds)
1614 {
1615 g_return_if_fail(VISU_IS_INTERACTIVE(inter) && inter->id == interactive_move);
1616
1617 /* Empty the node list. */
1618 if (inter->movingNodes)
1619 g_array_unref(inter->movingNodes);
1620 inter->movingNodes = (GArray*)0;
1621 if (nodeIds && nodeIds->len > 0)
1622 {
1623 inter->movingNodes = nodeIds;
1624 DBG_fprintf(stderr, "Visu Interactive: set the list of %d nodes to move.\n",
1625 nodeIds->len);
1626 g_array_ref(nodeIds);
1627 }
1628 inter->movingPicked = FALSE;
1629 }
1630 /**
1631 * visu_interactive_setMovingAxe:
1632 * @inter: a #VisuInteractive object.
1633 * @axe: a direction.
1634 *
1635 * Defines the axe that can be used to move along if @inter is a move
1636 * action session.
1637 */
1638 void visu_interactive_setMovingAxe(VisuInteractive *inter, float axe[3])
1639 {
1640 float norm;
1641
1642 norm = 1.f / sqrt(axe[0] * axe[0] + axe[1] * axe[1] + axe[2] * axe[2]);
1643 inter->movingAxe[0] = axe[0] * norm;
1644 inter->movingAxe[1] = axe[1] * norm;
1645 inter->movingAxe[2] = axe[2] * norm;
1646 }
1647 static void getDeltaScreen(gfloat delta[3], VisuGlView *view,
1648 gint x, gint y, gint xOrig, gint yOrig)
1649 {
1650 float z, centre[3], delta0[3];
1651
1652 visu_box_getCentre(visu_boxed_getBox(VISU_BOXED(view)), centre);
1653 glMatrixMode(GL_MODELVIEW);
1654 glPushMatrix();
1655 glTranslated(-centre[0], -centre[1], -centre[2]);
1656 z = visu_gl_view_getZCoordinate(view, centre);
1657 visu_gl_view_getRealCoordinates(view, delta, (float)x, (float)y, z);
1658 visu_gl_view_getRealCoordinates(view, delta0, (float)xOrig, (float)yOrig, z);
1659 glPopMatrix();
1660 /* Now, delta contains the new real space coord. */
1661
1662 delta[0] -= delta0[0];
1663 delta[1] -= delta0[1];
1664 delta[2] -= delta0[2];
1665 }
1666 static void getDeltaAlong(gfloat delta[3], VisuGlView *view, gfloat movingAxe[3])
1667 {
1668 int dy;
1669 float ratio;
1670
1671 dy = 5;
1672 ratio = visu_gl_window_getFileUnitPerPixel(&view->window) * dy;
1673 delta[0] = movingAxe[0] * ratio;
1674 delta[1] = movingAxe[1] * ratio;
1675 delta[2] = movingAxe[2] * ratio;
1676 }
1677 static void getDeltaAxis(gfloat delta[3], VisuGlView *view, ToolXyzDir dir, gint dx, gint dy)
1678 {
1679 float ratio;
1680
1681 delta[0] = 0.f;
1682 delta[1] = 0.f;
1683 delta[2] = 0.f;
1684 ratio = visu_gl_window_getFileUnitPerPixel(&view->window);
1685 DBG_fprintf(stderr, " | dx x dy : %d x %d\n", dx, dy);
1686 if (ABS(dy) > ABS(dx))
1687 ratio *= ((dy > 0)?1.f:-1.f);
1688 else
1689 ratio *= ((dx > 0)?1.f:-1.f);
1690 delta[dir] = ratio * sqrt(dx * dx + dy * dy);
1691 }
1692 static void getDelta(VisuInteractive *inter, VisuGlView *view,
1693 ToolSimplifiedEvents *ev, gfloat delta[3])
1694 {
1695 int dx, dy;
1696
1697 /* Get the real space shift. */
1698 if (ev->button == 1)
1699 {
1700 dx = ev->x - inter->xOrig;
1701 dy = -(ev->y - inter->yOrig);
1702 if (!ev->shiftMod && !ev->controlMod)
1703 getDeltaScreen(delta, view, ev->x, ev->y, inter->xOrig, inter->yOrig);
1704 else if (ev->shiftMod && !ev->controlMod)
1705 getDeltaAxis(delta, view, TOOL_XYZ_X, dx, dy);
1706 else if (ev->controlMod && !ev->shiftMod)
1707 getDeltaAxis(delta, view, TOOL_XYZ_Y, dx, dy);
1708 else if (ev->controlMod && ev->shiftMod)
1709 getDeltaAxis(delta, view, TOOL_XYZ_Z, dx, dy);
1710 }
1711 else
1712 {
1713 getDeltaAlong(delta, view, inter->movingAxe);
1714 if (ev->button == 4)
1715 {
1716 delta[0] *= -1.f;
1717 delta[1] *= -1.f;
1718 delta[2] *= -1.f;
1719 }
1720 }
1721 }
1722 static gboolean drag(VisuInteractive *inter, VisuGlView *view, ToolSimplifiedEvents *ev)
1723 {
1724 float delta[3];
1725
1726 g_return_val_if_fail(ev && inter, TRUE);
1727 if (ev->button == 3)
1728 {
1729 if (ev->buttonType == TOOL_BUTTON_TYPE_PRESS)
1730 return TRUE;
1731 else
1732 return FALSE;
1733 }
1734 if (ev->button != 1 && ev->button != 4 && ev->button != 5 &&
1735 ev->specialKey != Key_Arrow_Left && ev->specialKey != Key_Arrow_Right &&
1736 ev->specialKey != Key_Arrow_Up && ev->specialKey != Key_Arrow_Down)
1737 return FALSE;
1738 DBG_fprintf(stderr, "Visu Interactive: event (%d - %d, %d %d).\n",
1739 ev->button, ev->specialKey, ev->shiftMod, ev->controlMod);
1740
1741 if ((ev->motion == 1 || ev->button == 4 || ev->button == 5) ||
1742 ev->specialKey == Key_Arrow_Left || ev->specialKey == Key_Arrow_Right ||
1743 ev->specialKey == Key_Arrow_Up || ev->specialKey == Key_Arrow_Down)
1744 {
1745 if (ev->specialKey == Key_Arrow_Left)
1746 {
1747 ev->x = inter->xPrev - 1;
1748 ev->y = inter->yPrev;
1749 ev->button = 1;
1750 }
1751 else if (ev->specialKey == Key_Arrow_Right)
1752 {
1753 ev->x = inter->xPrev + 1;
1754 ev->y = inter->yPrev;
1755 ev->button = 1;
1756 }
1757 else if (ev->specialKey == Key_Arrow_Up)
1758 {
1759 ev->x = inter->xPrev;
1760 ev->y = inter->yPrev - 1;
1761 ev->button = 1;
1762 }
1763 else if (ev->specialKey == Key_Arrow_Down)
1764 {
1765 ev->x = inter->xPrev;
1766 ev->y = inter->yPrev + 1;
1767 ev->button = 1;
1768 }
1769 DBG_fprintf(stderr, "Visu Interactive: drag action at (%dx%d).\n", ev->x, ev->y);
1770
1771 getDelta(inter, view, ev, delta);
1772 DBG_fprintf(stderr, "Visu Interactive: drag a mouse of %gx%gx%g.\n",
1773 delta[0], delta[1], delta[2]);
1774
1775 /* Update stored position for drag info. */
1776 inter->xPrev = ev->x;
1777 inter->yPrev = ev->y;
1778
1779 DBG_fprintf(stderr, "Visu Interactive: emit the 'move' signal.\n");
1780 g_signal_emit(G_OBJECT(inter),
1781 interactive_signals[INTERACTIVE_MOVE_SIGNAL],
1782 0 , delta, NULL);
1783 }
1784 else if (ev->button == 1 && ev->buttonType == TOOL_BUTTON_TYPE_PRESS)
1785 {
1786 g_return_val_if_fail(inter->nodeList, TRUE);
1787
1788 /* Store the position to find the drag values. */
1789 inter->xOrig = inter->xPrev = ev->x;
1790 inter->yOrig = inter->yPrev = ev->y;
1791
1792 /* DBG_fprintf(stderr, "Visu Interactive: emit the 'start-move' signal.\n"); */
1793 /* g_signal_emit(G_OBJECT(inter), */
1794 /* interactive_signals[INTERACTIVE_START_MOVE_SIGNAL], */
1795 /* 0, inter->movingNodes, NULL); */
1796 }
1797 else if (ev->button == 1 && ev->buttonType == TOOL_BUTTON_TYPE_RELEASE)
1798 {
1799 getDelta(inter, view, ev, delta);
1800 DBG_fprintf(stderr, "Visu Interactive: stop dragging mouse of %gx%gx%g.\n",
1801 delta[0], delta[1], delta[2]);
1802 g_signal_emit(G_OBJECT(inter),
1803 interactive_signals[INTERACTIVE_STOP_MOVE_SIGNAL],
1804 0 , delta, NULL);
1805 }
1806
1807 return FALSE;
1808 }
1809 /**
1810 * visu_interactive_highlight:
1811 * @inter: a #VisuInteractive object.
1812 * @nodeId: a node id.
1813 *
1814 * This routine simulates an highlight action on @nodeId. This triggers the signal
1815 * #VisuInteractive::node-selection, or #VisuInteractive::selection-error
1816 * if @nodeId is invalid.
1817 *
1818 * Since: 3.7
1819 **/
1820 void visu_interactive_highlight(VisuInteractive *inter, guint nodeId)
1821 {
1822 VisuNode *nodes;
1823
1824 nodes = visu_node_array_getFromId(VISU_NODE_ARRAY(inter->dataObj), nodeId);
1825 if (nodes)
1826 g_signal_emit(G_OBJECT(inter),
1827 interactive_signals[INTERACTIVE_PICK_NODE_SIGNAL],
1828 0 , PICK_HIGHLIGHT, inter->dataObj, nodes, (VisuNode*)0, (VisuNode*)0);
1829 else
1830 g_signal_emit(G_OBJECT(inter),
1831 interactive_signals[INTERACTIVE_PICK_ERROR_SIGNAL],
1832 0 , PICK_ERROR_NO_SELECTION, NULL);
1833 }
1834
1835 /**
1836 * visu_interactive_setMessage:
1837 * @inter: a #VisuInteractive object.
1838 * @message: some text.
1839 *
1840 * Set a describing @message to be shown when @inter is used.
1841 *
1842 * Since: 3.8
1843 **/
1844 void visu_interactive_setMessage(VisuInteractive *inter, const gchar *message)
1845 {
1846 g_return_if_fail(VISU_IS_INTERACTIVE(inter));
1847
1848 g_free(inter->message);
1849 inter->message = g_strdup(message);
1850 }
1851
1852 /**
1853 * visu_interactive_getMessage:
1854 * @inter: a #VisuInteractive object.
1855 *
1856 * Retrieves the message associated to @inter.
1857 *
1858 * Since: 3.8
1859 *
1860 * Returns: a message.
1861 **/
1862 const gchar* visu_interactive_getMessage(VisuInteractive *inter)
1863 {
1864 g_return_val_if_fail(VISU_IS_INTERACTIVE(inter), (const gchar*)0);
1865 return inter->message;
1866 }
1867
1868 static gboolean mark(VisuInteractive *inter, ToolSimplifiedEvents *ev)
1869 {
1870 int nodeId;
1871
1872 g_return_val_if_fail(ev && inter, TRUE);
1873 if (ev->button == 3 && ev->buttonType == TOOL_BUTTON_TYPE_PRESS)
1874 return TRUE;
1875 if (ev->buttonType == TOOL_BUTTON_TYPE_RELEASE)
1876 return FALSE;
1877
1878 g_return_val_if_fail(inter->nodeList, TRUE);
1879
1880 nodeId = visu_gl_ext_nodes_getSelection(inter->nodeList, ev->x, ev->y);
1881 visu_interactive_highlight(inter, (guint)nodeId);
1882 return FALSE;
1883 }
1884
1885 static void onReadCamera(VisuConfigFile *obj _U_, VisuConfigFileEntry *entry _U_, VisuInteractiveClass *klass)
1886 {
1887 VisuGlCamera camera;
1888
1889 memset(&camera, 0, sizeof(VisuGlCamera));
1890 visu_gl_camera_setThetaPhiOmega(&camera, cameraData[0], cameraData[1], cameraData[2],
1891 VISU_GL_CAMERA_THETA | VISU_GL_CAMERA_PHI | VISU_GL_CAMERA_OMEGA);
1892 visu_gl_camera_setXsYs(&camera, cameraData[3], cameraData[4], VISU_GL_CAMERA_XS | VISU_GL_CAMERA_YS);
1893 visu_gl_camera_setGross(&camera, cameraData[5]);
1894 visu_gl_camera_setPersp(&camera, cameraData[6]);
1895 _pushSavedCamera(klass, &camera);
1896 }
1897 static void exportParameters(GString *data, VisuData *dataObj _U_)
1898 {
1899 VisuGlCamera *camera;
1900 GList *tmp;
1901
1902 if (!local_class)
1903 visu_interactive_get_type();
1904
1905 g_string_append_printf(data, "# %s\n", DESC_PARAMETER_OBSERVE_METHOD);
1906 g_string_append_printf(data, "%s[gtk]: %d\n\n", FLAG_PARAMETER_OBSERVE_METHOD,
1907 local_class->preferedObserveMethod);
1908 if (local_class->savedCameras)
1909 g_string_append_printf(data, "# %s\n", DESC_PARAMETER_CAMERA_SETTINGS);
1910 for (tmp = local_class->savedCameras; tmp; tmp = g_list_next(tmp))
1911 {
1912 camera = (VisuGlCamera*)tmp->data;
1913 g_string_append_printf(data, "%s[gtk]: %7.5g %7.5g %7.5g %4.3g %4.3g %g %g\n",
1914 FLAG_PARAMETER_CAMERA_SETTINGS,
1915 camera->theta, camera->phi, camera->omega,
1916 camera->xs, camera->ys, camera->gross, camera->d_red);
1917 }
1918 if (local_class->savedCameras)
1919 g_string_append(data, "\n");
1920 }
1921 void visu_interactive_class_setPreferedObserveMethod(VisuInteractiveMethod method)
1922 {
1923 g_return_if_fail(method == interactive_constrained ||
1924 method == interactive_walker);
1925
1926 if (!local_class)
1927 visu_interactive_get_type();
1928
1929 local_class->preferedObserveMethod = method;
1930 }
1931 VisuInteractiveMethod visu_interactive_class_getPreferedObserveMethod()
1932 {
1933 if (!local_class)
1934 visu_interactive_get_type();
1935
1936 return local_class->preferedObserveMethod;
1937 }
1938
1939 /* From an array of nodes ids, we specify their 2D coordinates */
1940 void visu_interactive_class_getNodes2DCoordinates(VisuData *dataObj, guint *nodeIds,
1941 guint nNodes, GLfloat *coordinates2D, guint *size)
1942 {
1943 int i,j;
1944 guint k;
1945 VisuNode *node;
1946 float xyz[3], centre[3];
1947 GLfloat *coord;
1948 GLint nValues;
1949
1950 visu_box_getCentre(visu_boxed_getBox(VISU_BOXED(dataObj)), centre);
1951 coord = g_malloc(sizeof(GLfloat) * nNodes * (1 + 2));
1952 glFeedbackBuffer(nNodes * (2 + 1), GL_2D, coord);
1953 glRenderMode(GL_FEEDBACK);
1954 glPushMatrix();
1955 glTranslated(-centre[0], -centre[1], -centre[2]);
1956
1957 /* Get nodes from ids and store their 2D coordinates */
1958 glBegin(GL_POINTS);
1959 for(k = 0; k < nNodes; k++){
1960 node = visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), nodeIds[k]);
1961 if(node != NULL)
1962 {
1963 visu_data_getNodePosition(dataObj, node, xyz);
1964 glVertex3fv(xyz);
1965 }
1966 }
1967 glEnd();
1968 glPopMatrix();
1969 nValues = glRenderMode(GL_RENDER);
1970 i = 0;
1971 j = 0;
1972 /* Keep only the coordinates */
1973 while (i < nValues)
1974 {
1975 if (coord[i] == GL_POINT_TOKEN)
1976 {
1977 coordinates2D[j] = coord[i + 1];
1978 coordinates2D[j+1] = coord[i + 2];
1979 i += 3;
1980 j += 2;
1981 }
1982 else
1983 i++;
1984 }
1985 *size = j;
1986 }
1987