1 #include "SUMA_suma.h"
2 #include "SUMA_plot.h"
3 
4 /*!
5    Return the code for the key that is specified in keyin
6 */
SUMA_KeyPress(char * keyin,char * keynameback)7 int SUMA_KeyPress(char *keyin, char *keynameback)
8 {
9    static char FuncName[]={"SUMA_KeyPress"};
10    int nk=0,i=0,nc=0;
11    char keyname[100];
12    char *key=NULL, c='\0';
13    SUMA_Boolean LocalHead = NOPE;
14 
15    SUMA_ENTRY;
16 
17    if (keynameback) keynameback[0]='\0';
18    keyname[0]='\0';
19 
20    if (!keyin) SUMA_RETURN(XK_VoidSymbol);
21    nc = strlen(keyin);
22    if (nc<=0) SUMA_RETURN(XK_VoidSymbol);
23 
24    key = SUMA_append_string("+",keyin);  /* add a + to simplify parsing */
25 
26    nc = strlen(key);
27    SUMA_LHv("Key now '%s'\n", key);
28 
29    /* find the last + */
30    i = nc-2; /* skip last char, might itself be + */
31    while (i >= 0 && key[i] != '+') { --i; }  ++i; /* reposition past last + */
32 
33    /* copy the rest into keyname */
34    nk=0;
35    while (i<nc && nk < 10) {
36       keyname[nk] = key[i];
37       ++i;
38       ++nk;
39    }
40    keyname[nk] = '\0';
41    if (nk > 10 || nk == 0) {
42       SUMA_S_Errv("What kind of key is %s!!!\n", key);
43       SUMA_RETURN(XK_VoidSymbol);
44    }
45    SUMA_LHv("Keyname now '%s'\n", keyname);
46    if (keynameback) sprintf(keynameback,"%s", keyname);
47 
48    if (nk == 1) { /* the simple case, add them as needed */
49       c = keyname[0];
50       SUMA_LHv("c now '%c'\n", c);
51       switch(c) {
52          case 'a':
53             SUMA_RETURN(XK_a);
54          case 'A':
55             SUMA_RETURN(XK_A);
56          case 'b':
57             SUMA_RETURN(XK_b);
58          case 'B':
59             SUMA_RETURN(XK_B);
60          case 'd':
61             SUMA_RETURN(XK_d);
62          case 'D':
63             SUMA_RETURN(XK_D);
64          case 'g':
65             SUMA_RETURN(XK_g);
66          case 'G':
67             SUMA_RETURN(XK_G);
68          case 'j':
69             SUMA_RETURN(XK_j);
70          case 'J':
71             SUMA_RETURN(XK_J);
72          case 'l':
73             SUMA_RETURN(XK_l);
74          case 'L':
75             SUMA_RETURN(XK_L);
76          case 'm':
77             SUMA_RETURN(XK_m);
78          case 'M':
79             SUMA_RETURN(XK_M);
80          case 'n':
81             SUMA_RETURN(XK_n);
82          case 'N':
83             SUMA_RETURN(XK_N);
84          case 'o':
85             SUMA_RETURN(XK_o);
86          case 'O':
87             SUMA_RETURN(XK_O);
88          case 'p':
89             SUMA_RETURN(XK_p);
90          case 'P':
91             SUMA_RETURN(XK_P);
92          case 'r':
93             SUMA_RETURN(XK_r);
94          case 'R':
95             SUMA_RETURN(XK_R);
96          case 't':
97             SUMA_RETURN(XK_t);
98          case 'T':
99             SUMA_RETURN(XK_T);
100          case 'u':
101             SUMA_RETURN(XK_u);
102          case 'U':
103             SUMA_RETURN(XK_U);
104          case 'w':
105             SUMA_RETURN(XK_w);
106          case 'W':
107             SUMA_RETURN(XK_W);
108          case 'z':
109             SUMA_RETURN(XK_z);
110          case 'Z':
111             SUMA_RETURN(XK_Z);
112          case '[':
113             SUMA_RETURN(XK_bracketleft);
114          case ']':
115             SUMA_RETURN(XK_bracketright);
116          case '.':
117             if (keynameback) sprintf(keynameback,"period");
118             SUMA_RETURN(XK_period);
119          case ' ':
120             if (keynameback) sprintf(keynameback,"space");
121             SUMA_RETURN(XK_space);
122          case ',':
123             if (keynameback) sprintf(keynameback,"comma");
124             SUMA_RETURN(XK_comma);
125          default:
126             SUMA_S_Errv("Key '%c' not yet supported, complain to author.\n", c);
127             SUMA_RETURN(XK_VoidSymbol);
128       }
129    } else {
130       if (SUMA_iswordsame_ci(keyname,"up") == 1) SUMA_RETURN(XK_Up);
131       if (SUMA_iswordsame_ci(keyname,"down") == 1) SUMA_RETURN(XK_Down);
132       if (SUMA_iswordsame_ci(keyname,"left") == 1) SUMA_RETURN(XK_Left);
133       if (SUMA_iswordsame_ci(keyname,"right") == 1) SUMA_RETURN(XK_Right);
134       if (SUMA_iswordsame_ci(keyname,"space") == 1) SUMA_RETURN(XK_space);
135       if (SUMA_iswordsame_ci(keyname,"period") == 1) SUMA_RETURN(XK_period);
136       if (SUMA_iswordsame_ci(keyname,"comma") == 1) SUMA_RETURN(XK_comma);
137       if (SUMA_iswordsame_ci(keyname,"f1") == 1) SUMA_RETURN(XK_F1);
138       if (SUMA_iswordsame_ci(keyname,"f2") == 1) SUMA_RETURN(XK_F2);
139       if (SUMA_iswordsame_ci(keyname,"f3") == 1) SUMA_RETURN(XK_F3);
140       if (SUMA_iswordsame_ci(keyname,"f4") == 1) SUMA_RETURN(XK_F4);
141       if (SUMA_iswordsame_ci(keyname,"f5") == 1) SUMA_RETURN(XK_F5);
142       if (SUMA_iswordsame_ci(keyname,"f6") == 1) SUMA_RETURN(XK_F6);
143       if (SUMA_iswordsame_ci(keyname,"f7") == 1) SUMA_RETURN(XK_F7);
144       if (SUMA_iswordsame_ci(keyname,"f8") == 1) SUMA_RETURN(XK_F8);
145       if (SUMA_iswordsame_ci(keyname,"f9") == 1) SUMA_RETURN(XK_F9);
146       if (SUMA_iswordsame_ci(keyname,"f10") == 1) SUMA_RETURN(XK_F10);
147       if (SUMA_iswordsame_ci(keyname,"f11") == 1) SUMA_RETURN(XK_F11);
148       if (SUMA_iswordsame_ci(keyname,"f12") == 1) SUMA_RETURN(XK_F12);
149 
150       SUMA_S_Errv("Key '%s' not yet supported, complain to author.\n", keyname);
151       SUMA_RETURN(XK_VoidSymbol);
152    }
153    SUMA_RETURN(XK_VoidSymbol);
154 }
155 
156 #define SUMA_KEY_COMMON {  \
157    if (!sv || !key) {   \
158       SUMA_S_Err("Null input");  \
159       SUMA_RETURN(0);  \
160    }  \
161    if (!(nc = strlen(key))) {    \
162       SUMA_S_Err("Empty key");   \
163       SUMA_RETURN(0);   \
164    }  \
165       \
166    SUMA_LHv("Have %s, nc=%d\n", key, nc); \
167    if ((k = SUMA_KeyPress(key, keyname)) == XK_VoidSymbol) {   \
168       SUMA_S_Errv("KeyPress for %s could not be obtained.\n", key);  \
169       SUMA_RETURN(0);  \
170    }  \
171    SUMA_LHv("Have keyname = %s\n", keyname); \
172    if (SUMA_iswordsame_ci(keyname,tk) != 1) {   \
173       SUMA_S_Errv("Expecting %s (or lower case version), got %s\n", \
174                   tk, keyname );  \
175       SUMA_RETURN(0);   \
176    }  \
177 }
178 
179 #define SUMA_KEY_SWITCH {  \
180    /* Check for key switching */ \
181    if (sv->State && strstr(sv->State, "GMATRIX")==sv->State) {  \
182       if (SUMA_SHIFT_KEY(key)) { \
183          strncpy(skeyi, key, 63); skeyi[64]='\0';  \
184          SUMA_wordswap_ci(skeyi, "shift", "", skey);  \
185       } else   \
186          snprintf(skey,63,"shift%s",key); \
187       key = skey; \
188    }  \
189 }
190 
191 
192 #if 0 /* a template to use for various keys , replace CHAR by upper case char and cHaR by lower case*/
193 int SUMA_CHAR_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
194 {
195    static char FuncName[]={"SUMA_CHAR_Key"};
196    char tk[]={"CHAR"}, keyname[100];
197    int k, nc;
198    SUMA_Boolean LocalHead = NOPE;
199 
200    SUMA_ENTRY;
201 
202    SUMA_KEY_COMMON;
203 
204    /* do the work */
205    switch (k) {
206       case XK_CHAR:
207          break;
208       case XK_cHaR:
209          break;
210       default:
211          SUMA_S_Err("Il ne faut pas ci dessous");
212          SUMA_RETURN(0);
213          break;
214    }
215 
216    SUMA_RETURN(1);
217 }
218 #endif
219 
220 static int Nwarn_bracket = 0;
221 
SUMA_bracketleft_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)222 int SUMA_bracketleft_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
223 {
224    static char FuncName[]={"SUMA_bracketleft_Key"};
225    char tk[]={"["}, keyname[100];
226    int k, nc;
227    char stmp[200];
228    SUMA_Boolean LocalHead = NOPE;
229 
230    SUMA_ENTRY;
231 
232    SUMA_KEY_COMMON;
233 
234    /* do the work */
235    switch (k) {
236       case XK_bracketleft:
237          /* getting rid of some warnings that happen with normal use */
238          Nwarn_bracket = 1; /* warning always in terminal, no message to window */
239          /* toggle showing left hemispheres */
240          sv->ShowLeft = !sv->ShowLeft;
241          /* do the axis setup */
242          SUMA_WorldAxisStandard (sv->WAx, sv);
243          SUMA_UpdateViewerTitle(sv);
244          SUMA_postRedisplay(sv->X->GLXAREA, NULL, NULL);
245          if (sv->ShowLeft) {
246             sprintf(stmp,"Showing Left side%s",
247                Nwarn_bracket  ?
248       "":"\nFurther notices for '[' or ']' keys will be echoed in the shell");
249          } else {
250             sprintf(stmp,"Hiding Left side%s",
251                Nwarn_bracket > 1 ?
252       "":"\nFurther notices for '[' or ']' keys will be echoed in the shell");
253          }
254          if (!Nwarn_bracket && callmode &&
255                strcmp(callmode, "interactive") == 0) {
256             SUMA_SLP_Note("%s",stmp);
257          } else { SUMA_S_Note("%s",stmp); }
258 /*         ++Nwarn_bracket;*/
259          break;
260       default:
261          SUMA_S_Err("Il ne faut pas etre la");
262          SUMA_RETURN(0);
263          break;
264    }
265 
266    SUMA_RETURN(1);
267 }
268 
SUMA_bracketright_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)269 int SUMA_bracketright_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
270 {
271    static char FuncName[]={"SUMA_bracketright_Key"};
272    char tk[]={"]"}, keyname[100];
273    int k, nc;
274    char stmp[200];
275    SUMA_Boolean LocalHead = NOPE;
276 
277    SUMA_ENTRY;
278 
279    SUMA_KEY_COMMON;
280 
281    /* do the work */
282    switch (k) {
283       case XK_bracketright:
284          /* getting rid of some warnings that happen with normal use */
285          Nwarn_bracket = 1; /* warning always in terminal, no message to window */
286          /* toggle showing left hemispheres */
287          sv->ShowRight = !sv->ShowRight;
288          /* do the axis setup */
289          SUMA_WorldAxisStandard (sv->WAx, sv);
290          SUMA_UpdateViewerTitle(sv);
291          SUMA_postRedisplay(sv->X->GLXAREA, NULL, NULL);
292          if (sv->ShowRight) {
293             sprintf(stmp,"Showing Right side%s",
294                Nwarn_bracket ?
295             "":"\nFurther notices for '[' or ']' key will be in the shell");
296          } else {
297             sprintf(stmp,"Hiding right side%s",
298                Nwarn_bracket  ?
299             "":"\nFurther notices for '[' or ']' key will be in the shell");
300          }
301          if (!Nwarn_bracket && callmode &&
302                strcmp(callmode, "interactive") == 0) {
303             SUMA_SLP_Note("%s",stmp);
304          } else { SUMA_S_Note("%s",stmp); }
305 /*         ++Nwarn_bracket;*/
306          break;
307       default:
308          SUMA_S_Err("Il ne faut pas etre la");
309          SUMA_RETURN(0);
310          break;
311    }
312 
313    SUMA_RETURN(1);
314 }
315 
SUMA_space_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)316 int SUMA_space_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
317 {
318    static char FuncName[]={"SUMA_space_Key"};
319    char tk[]={"SPACE"}, keyname[100];
320    int k, nc;
321    int nxtstateID=-1, curstateID = -1;
322    int origState = -1, dov_ID = -1;
323    SUMA_SurfaceObject *SO = NULL, *SOmap = NULL;
324    SUMA_Boolean LocalHead = NOPE;
325 
326    SUMA_ENTRY;
327 
328 
329    SUMA_KEY_COMMON;
330 
331    origState = sv->iState;
332 
333    /* do the work */
334    switch (k) {
335       case XK_space:
336          /* toggle between state containing mapping reference
337             of SO in focus and other view */
338 
339          /* make sure switching is OK */
340          curstateID = SUMA_WhichState(sv->State, sv, sv->CurGroupName);
341          if ((SO = SUMA_SV_Focus_SO(sv))) { /* have something to work with */
342             if (SUMA_isLocalDomainParent (SO)) {
343                /* get the last non mappable state in SV */
344                if (sv->LastNonMapStateID < 0) {
345                                        /* not recorded, complain and quit */
346                   fprintf(SUMA_STDERR,
347                           "Warning %s: Nothing defined to toggle with yet.\n",
348                           FuncName);
349                   break;
350                }
351 
352                if (LocalHead)
353                   fprintf (SUMA_STDERR,
354                            "%s: surface is inherrently mappable, "
355                            "switching to last non mappable state %d.\n",
356                            FuncName, sv->LastNonMapStateID);
357 
358                if (!SUMA_SwitchState ( SUMAg_DOv, SUMAg_N_DOv, sv,
359                                       sv->LastNonMapStateID, sv->CurGroupName)) {
360                   fprintf( SUMA_STDERR,
361                            "Error %s: Failed in SUMA_SwitchState.\n", FuncName);
362                   break;
363                }
364 
365             } else {/* that's a non mappable, go to state containing reference */
366                if (LocalHead)
367                   fprintf (SUMA_STDERR,
368                            "%s: surface is not inherrently mappable, "
369                            "searching for mapping reference and its state.\n",
370                            FuncName);
371 
372                /* find SO that is mappable reference & get its state ID*/
373                dov_ID = SUMA_findSO_inDOv(SO->LocalDomainParentID, SUMAg_DOv,
374                                           SUMAg_N_DOv);
375                SOmap = (SUMA_SurfaceObject *)SUMAg_DOv[dov_ID].OP;
376                nxtstateID = SUMA_WhichState(SOmap->State, sv, sv->CurGroupName);
377 
378                if (nxtstateID < 0) {
379                   fprintf (SUMA_STDERR,
380                            "%s: Failed in SUMA_findSO_inDOv "
381                            "This should not happen.\n", FuncName);
382                   break;
383                }
384 
385                if (LocalHead)
386                   fprintf (SUMA_STDERR,
387                            "%s: Found mapping reference in viewer state %d.\n",
388                            FuncName, nxtstateID);
389 
390                /* store this location */
391                sv->LastNonMapStateID = curstateID;
392 
393                /* go there */
394                if (!SUMA_SwitchState ( SUMAg_DOv, SUMAg_N_DOv, sv,
395                                        nxtstateID, sv->CurGroupName)) {
396                   fprintf( SUMA_STDERR,
397                            "Error %s: Failed in SUMA_SwitchState.\n",
398                            FuncName);
399                   break;
400                }
401             }
402 
403             SUMA_postRedisplay(sv->X->GLXAREA, NULL, NULL);
404          } else {
405             SUMA_LH("No surfaces in this state, space bar ignored");
406          }
407          break;
408       default:
409          SUMA_S_Err("Il ne faut pas etre la");
410          SUMA_RETURN(0);
411          break;
412    }
413 
414    SUMA_RETURN(1);
415 }
416 
SUMA_comma_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)417 int SUMA_comma_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
418 {
419    static char FuncName[]={"SUMA_comma_Key"};
420    char tk[]={"COMMA"}, keyname[100], stmp[256];
421    int k, nc, ii;
422    int nxtstateID=-1, curstateID = -1;
423    int origState = -1;
424    char *note=NULL;
425    DList *list=NULL;
426    SUMA_Boolean LocalHead = NOPE;
427 
428    SUMA_ENTRY;
429 
430    SUMA_KEY_COMMON;
431 
432    origState = sv->iState;
433 
434    /* do the work */
435    switch (k) {
436       case XK_comma:
437          /* switch state, back one */
438          if (sv->N_VSv < 2) break;
439 
440          curstateID = SUMA_WhichState (sv->State, sv, sv->CurGroupName);
441          if (curstateID < 0) {
442             SUMA_SL_Err("Current State not found.\n"
443                         "Should not happen here.");
444             SUMA_RETURN(0);
445          }
446 
447          if (SUMAg_N_SVv > 1) {
448             ii = SUMA_WhichViewerInMomentum (SUMAg_SVv, SUMAg_N_SVv, sv);
449             if (ii >= 0) {
450                sprintf (stmp, "You cannot switch states while other viewers\n"
451                               "(like viewer %c) in momentum mode.\n", ii+65);
452                SUMA_RegisterMessage (SUMAg_CF->MessageList,
453                                      stmp, FuncName, SMT_Error, SMA_LogAndPopup);
454                SUMA_RETURN(0);
455             }
456          }
457 
458          do {
459             if (LocalHead && nxtstateID > -1) {
460                note = SUMA_append_string("Skipping state ",sv->State);
461                note = SUMA_append_replace_string(note,
462                                              ".\nNo surfaces visible.", "", 1);
463                SUMA_SLP_Note("%s",note);
464                SUMA_free(note);   note = NULL;
465             }
466 
467             /*fprintf(SUMA_STDERR,"%s: Current viewing state is %s ...\n",
468                       FuncName, sv->State);*/
469             /* toggle to the next view state */
470             nxtstateID = SUMA_PrevState(sv);
471             if (nxtstateID == curstateID) break;
472             if (nxtstateID < 0) {
473                fprintf(SUMA_STDERR,
474                        "Error %s: Failed in SUMA_PrevState.\n", FuncName);
475                break;
476             }
477             fprintf( SUMA_STDERR,"%s: Switching from %s to %s viewing state.\n",
478                      FuncName, sv->State, sv->VSv[nxtstateID].Name);
479 
480             if (!SUMA_SwitchState ( SUMAg_DOv, SUMAg_N_DOv, sv,
481                                     nxtstateID, sv->CurGroupName)) {
482                fprintf( SUMA_STDERR,
483                         "Error %s: Failed in SUMA_SwitchState.\n", FuncName);
484                break;
485             }
486 
487             /* find out if there are any surfaces that will be rendered */
488 
489          } while (!SUMA_Selectable_ADOs (sv, SUMAg_DOv, NULL) &&
490                    sv->iState != origState);
491 
492          /* register a call to redisplay
493             (you also need to copy the color data,
494              in case the next surface is of the same family)*/
495          if (!list) list = SUMA_CreateList();
496          SUMA_REGISTER_HEAD_COMMAND_NO_DATA(list, SE_Redisplay, SES_Suma, sv);
497          if (!SUMA_Engine (&list)) {
498             fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
499          }
500 
501          /* update titles */
502          SUMA_UpdateViewerTitle(sv);
503          break;
504       default:
505          SUMA_S_Err("Il ne faut pas etre la");
506          SUMA_RETURN(0);
507          break;
508    }
509 
510    SUMA_RETURN(1);
511 }
512 
SUMA_period_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)513 int SUMA_period_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
514 {
515    static char FuncName[]={"SUMA_period_Key"};
516    char tk[]={"PERIOD"}, keyname[100], stmp[256];
517    int k, nc, ii;
518    int nxtstateID=-1, curstateID = -1;
519    int origState = -1;
520    char *note=NULL;
521    DList *list=NULL;
522    SUMA_Boolean LocalHead = NOPE;
523 
524    SUMA_ENTRY;
525 
526    SUMA_KEY_COMMON;
527 
528    origState = sv->iState;
529 
530    /* do the work */
531    switch (k) {
532       case XK_period:
533          /* switch state, forward one */
534          if (sv->N_VSv < 2) break;
535 
536          curstateID = SUMA_WhichState (sv->State, sv, sv->CurGroupName);
537          if (curstateID < 0) {
538             SUMA_SL_Err("Current State not found.\n"
539                         "Should not happen here.");
540             SUMA_RETURN(0);
541          }
542 
543          if (SUMAg_N_SVv > 1) {
544             ii = SUMA_WhichViewerInMomentum (SUMAg_SVv, SUMAg_N_SVv, sv);
545             if (ii >= 0) {
546                sprintf (stmp, "You cannot switch states while other viewers\n"
547                               "(like viewer %c) are in momentum mode.\n", ii+65);
548                SUMA_RegisterMessage (SUMAg_CF->MessageList,
549                                      stmp, FuncName, SMT_Error, SMA_LogAndPopup);
550                SUMA_RETURN(0);
551             }
552          }
553 
554          do {
555             if (LocalHead && nxtstateID > -1) {
556                note = SUMA_append_string("Skipping state ",sv->State);
557                note = SUMA_append_replace_string(note,
558                                           ".\nNo surfaces visible.", "", 1);
559                SUMA_SLP_Note("%s",note);
560                SUMA_free(note);   note = NULL;
561             }
562 
563             if (LocalHead)
564                fprintf(SUMA_STDERR,"%s: Current viewing state is %s ...\n",
565                                     FuncName, sv->State);
566 
567             /* toggle to the next view state */
568             nxtstateID = SUMA_NextState(sv);
569             if (nxtstateID == curstateID) break;
570             if (nxtstateID < 0) {
571                fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_NextState.\n",
572                                     FuncName);
573                break;
574             }
575 
576             fprintf( SUMA_STDERR,
577                      "%s: Switching from %s to %s viewing state.\n",
578                      FuncName, sv->State, sv->VSv[nxtstateID].Name);
579 
580             if (!SUMA_SwitchState ( SUMAg_DOv, SUMAg_N_DOv, sv,
581                                     nxtstateID, sv->CurGroupName)) {
582                fprintf( SUMA_STDERR,
583                         "Error %s: Failed in SUMA_SwitchState.\n", FuncName);
584                break;
585             }
586             SUMA_SET_AS_NEEDED_2D_VIEW_ANGLE(sv);
587 
588          } while (!SUMA_Selectable_ADOs(sv, SUMAg_DOv, NULL) &&
589                    sv->iState != origState);
590          /* register a call to redisplay
591          (you also need to copy the color data, in case the next surface
592           is of the same family)*/
593          if (!list) list = SUMA_CreateList();
594          SUMA_REGISTER_HEAD_COMMAND_NO_DATA( list, SE_Redisplay,
595                                              SES_Suma, sv);
596          if (!SUMA_Engine (&list)) {
597             fprintf(stderr, "Error %s: SUMA_Engine call failed.\n",
598                              FuncName);
599          }
600 
601          /* update titles */
602          SUMA_UpdateViewerTitle(sv);
603          break;
604       default:
605          SUMA_S_Err("Il ne faut pas etre la");
606          SUMA_RETURN(0);
607          break;
608    }
609 
610    SUMA_RETURN(1);
611 }
612 
SUMA_F1_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)613 int SUMA_F1_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
614 {
615    static char FuncName[]={"SUMA_F1_Key"};
616    char tk[]={"F1"}, keyname[100];
617    int k, nc;
618    SUMA_Boolean LocalHead = NOPE;
619 
620    SUMA_ENTRY;
621 
622    SUMA_KEY_COMMON;
623 
624    /* do the work */
625    switch (k) {
626       case XK_F1:
627          sv->ShowEyeAxis = !sv->ShowEyeAxis;
628          SUMA_postRedisplay(sv->X->GLXAREA, NULL, NULL);
629          break;
630       default:
631          SUMA_S_Err("Il ne faut pas etre la");
632          SUMA_RETURN(0);
633          break;
634    }
635 
636    SUMA_RETURN(1);
637 }
638 
SUMA_F2_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)639 int SUMA_F2_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
640 {
641    static char FuncName[]={"SUMA_F2_Key"};
642    char tk[]={"F2"}, keyname[100];
643    int k, nc;
644    SUMA_Boolean LocalHead = NOPE;
645 
646    SUMA_ENTRY;
647 
648    SUMA_KEY_COMMON;
649 
650    /* do the work */
651    switch (k) {
652       case XK_F2:
653          {
654             int *do_id, n_do_id;
655             ++sv->ShowWorldAxis;
656             sv->ShowWorldAxis = sv->ShowWorldAxis % SUMA_N_WAX_OPTIONS;
657             sv->ShowMeshAxis = 0;
658                /* used to be = !sv->ShowMeshAxis; ,
659                   Turned off Oct 15 04 , in favor or WorldAxis */
660             do_id = SUMA_GetDO_Type(SUMAg_DOv, SUMAg_N_DOv, SO_type, &n_do_id);
661             if (n_do_id) {
662                while (n_do_id) {
663                  ((SUMA_SurfaceObject *)
664                      SUMAg_DOv[do_id[n_do_id-1]].OP)->ShowMeshAxis =
665                            sv->ShowMeshAxis;
666                   --n_do_id;
667                }
668                SUMA_free(do_id);
669             }
670          }
671          SUMA_postRedisplay(sv->X->GLXAREA, NULL, NULL);
672          break;
673       default:
674          SUMA_S_Err("Il ne faut pas etre la haut");
675          SUMA_RETURN(0);
676          break;
677    }
678 
679    SUMA_RETURN(1);
680 }
681 
SUMA_F3_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)682 int SUMA_F3_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
683 {
684    static char FuncName[]={"SUMA_F3_Key"};
685    char tk[]={"F3"}, keyname[100];
686    int k, nc;
687    SUMA_EngineData *ED = NULL;
688    DList *list = NULL;
689    SUMA_Boolean LocalHead = NOPE;
690 
691    SUMA_ENTRY;
692 
693    SUMA_KEY_COMMON;
694 
695    /* do the work */
696    switch (k) {
697       case XK_F3:
698          if (!list) list = SUMA_CreateList();
699          SUMA_REGISTER_HEAD_COMMAND_NO_DATA( list, SE_ToggleCrossHair,
700                                              SES_Suma, sv);
701          SUMA_REGISTER_HEAD_COMMAND_NO_DATA(list, SE_Redisplay, SES_Suma, sv);
702          if (!SUMA_Engine (&list)) {
703                fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
704          }
705          break;
706       default:
707          SUMA_S_Err("Il ne faut pas etre over here");
708          SUMA_RETURN(0);
709          break;
710    }
711 
712    SUMA_RETURN(1);
713 }
714 
SUMA_F4_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)715 int SUMA_F4_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
716 {
717    static char FuncName[]={"SUMA_F4_Key"};
718    char tk[]={"F4"}, keyname[100];
719    int k, nc;
720    SUMA_EngineData *ED = NULL;
721    DList *list = NULL;
722    DListElmt *NextElm= NULL;
723    SUMA_Boolean LocalHead = NOPE;
724 
725    SUMA_ENTRY;
726 
727    SUMA_KEY_COMMON;
728 
729    /* do the work */
730    switch (k) {
731       case XK_F4:
732          if (!list) list = SUMA_CreateList();
733          SUMA_REGISTER_HEAD_COMMAND_NO_DATA(list, SE_ToggleShowSelectedNode,
734                                              SES_Suma, sv);
735          SUMA_REGISTER_HEAD_COMMAND_NO_DATA(list, SE_Redisplay, SES_Suma, sv);
736          if (!SUMA_Engine (&list)) {
737                fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
738          }
739          break;
740       default:
741          SUMA_S_Err("Il ne faut pas etre over dort");
742          SUMA_RETURN(0);
743          break;
744    }
745 
746    SUMA_RETURN(1);
747 }
748 
SUMA_F5_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)749 int SUMA_F5_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
750 {
751    static char FuncName[]={"SUMA_F5_Key"};
752    char tk[]={"F5"}, keyname[100];
753    int k, nc;
754    SUMA_EngineData *ED = NULL;
755    DList *list = NULL;
756    DListElmt *NextElm= NULL;
757    SUMA_Boolean LocalHead = NOPE;
758 
759    SUMA_ENTRY;
760 
761    SUMA_KEY_COMMON;
762 
763    /* do the work */
764    switch (k) {
765       case XK_F5:
766          if (!list) list = SUMA_CreateList();
767          SUMA_REGISTER_HEAD_COMMAND_NO_DATA( list, SE_ToggleShowSelectedFaceSet,
768                                              SES_Suma, sv);
769          SUMA_REGISTER_HEAD_COMMAND_NO_DATA(list, SE_Redisplay, SES_Suma, sv);
770          if (!SUMA_Engine (&list)) {
771                fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
772          }
773          break;
774       default:
775          SUMA_S_Err("Il ne faut pas etre over dort");
776          SUMA_RETURN(0);
777          break;
778    }
779 
780    SUMA_RETURN(1);
781 }
782 
SUMA_F6_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)783 int SUMA_F6_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
784 {
785    static char FuncName[]={"SUMA_F6_Key"};
786    char tk[]={"F6"}, keyname[100];
787    int k, nc;
788    SUMA_EngineData *ED = NULL;
789    DList *list = NULL;
790    DListElmt *NextElm= NULL;
791    SUMA_Boolean LocalHead = NOPE;
792 
793    SUMA_ENTRY;
794 
795    SUMA_KEY_COMMON;
796 
797    /* do the work */
798    switch (k) {
799       case XK_F6:
800          sv->clear_color[0] = 1 - sv->clear_color[0];
801          sv->clear_color[1] = 1 - sv->clear_color[1];
802          sv->clear_color[2] = 1 - sv->clear_color[2];
803 
804          SUMA_UpdateCrossHairNodeLabelField(sv);
805          if (!list) list = SUMA_CreateList();
806          SUMA_REGISTER_HEAD_COMMAND_NO_DATA(list, SE_Redisplay, SES_Suma, sv);
807          if (!SUMA_Engine (&list)) {
808                fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
809          }
810          break;
811       default:
812          SUMA_S_Err("Il ne faut pas etre over dere");
813          SUMA_RETURN(0);
814          break;
815    }
816 
817    SUMA_RETURN(1);
818 }
SUMA_F7_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)819 int SUMA_F7_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
820 {
821    static char FuncName[]={"SUMA_F7_Key"};
822    char tk[]={"F7"}, keyname[100];
823    int k, nc;
824    SUMA_EngineData *ED = NULL;
825    DList *list = NULL;
826    DListElmt *NextElm= NULL;
827    SUMA_Boolean LocalHead = NOPE;
828 
829    SUMA_ENTRY;
830 
831    SUMA_KEY_COMMON;
832 
833    /* do the work */
834    switch (k) {
835       case XK_F7:
836          ++SUMAg_CF->ColMixMode;
837          if (SUMAg_CF->ColMixMode >= SUMA_MAX_MODES) {
838             SUMAg_CF->ColMixMode = SUMA_ORIG_MIX_MODE;
839          }
840          {
841             char stmp[200];
842             sprintf(stmp,"Using %s color mixing mode.",
843                      SUMA_ColMixModeString(SUMAg_CF->ColMixMode));
844             if (callmode && strcmp(callmode, "interactive") == 0) {
845                   SUMA_SLP_Note("%s",stmp); }
846             else { SUMA_S_Note("%s",stmp); }
847          }
848 
849          SUMA_SetAllRemixFlag (SUMAg_SVv, SUMAg_N_SVv);
850 
851          if (!list) list = SUMA_CreateList();
852          SUMA_REGISTER_HEAD_COMMAND_NO_DATA(list, SE_Redisplay_AllVisible,
853                                             SES_Suma, NULL);
854          if (!SUMA_Engine (&list)) {
855                fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
856          }
857          break;
858       default:
859          SUMA_S_Err("Il ne faut pas etre over yonder");
860          SUMA_RETURN(0);
861          break;
862    }
863 
864    SUMA_RETURN(1);
865 }
866 
SUMA_F8_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)867 int SUMA_F8_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
868 {
869    static char FuncName[]={"SUMA_F8_Key"};
870    char tk[]={"F8"}, keyname[100];
871    int k, nc;
872    SUMA_EngineData *ED = NULL;
873    DList *list = NULL;
874    DListElmt *NextElm= NULL;
875    SUMA_Boolean LocalHead = NOPE;
876 
877    SUMA_ENTRY;
878 
879    SUMA_KEY_COMMON;
880 
881    /* do the work */
882    switch (k) {
883       case XK_F8:
884          sv->ortho = !sv->ortho;
885          {
886             static int inote = 0;
887             char stmp[200];
888             if (sv->ortho) {
889                sprintf(stmp,"Using orthographic projection viewing");
890                sv->FOV[sv->iState] = sv->FOV[sv->iState] / 2.0;
891             } else {
892                sprintf(stmp,"Using perspective viewing");
893                sv->FOV[sv->iState] = sv->FOV[sv->iState] * 2.0;
894             }
895             ++inote;
896             if (callmode && strcmp(callmode, "interactive") == 0 && inote < 3) {
897                   SUMA_SLP_Note("%s",stmp); }
898             else { SUMA_S_Note("%s",stmp); }
899          }
900 
901          SUMA_SET_GL_PROJECTION(sv, sv->ortho);
902          SUMA_postRedisplay(sv->X->GLXAREA, NULL, NULL);
903          break;
904       default:
905          SUMA_S_Err("Il ne faut pas etre over yonder");
906          SUMA_RETURN(0);
907          break;
908    }
909 
910    SUMA_RETURN(1);
911 }
912 
SUMA_F9_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)913 int SUMA_F9_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
914 {
915    static char FuncName[]={"SUMA_F9_Key"};
916    char tk[]={"F9"}, keyname[100];
917    int k, nc;
918    SUMA_EngineData *ED = NULL;
919    DList *list = NULL;
920    DListElmt *NextElm= NULL;
921    static int inote = 0;
922    SUMA_Boolean LocalHead = NOPE;
923 
924    SUMA_ENTRY;
925 
926    SUMA_KEY_COMMON;
927 
928    /* do the work */
929    switch (k) {
930       case XK_F9:
931          sv->ShowLabelAtXhair = !sv->ShowLabelAtXhair;
932          SUMA_UpdateCrossHairNodeLabelField(sv);
933          {
934             char stmp[200];
935             if (sv->ShowLabelAtXhair) {
936                sprintf(stmp,"Showing Label At Xhair");
937             } else {
938                sprintf(stmp,"Hiding Label At Xhair");
939             }
940             if (callmode && strcmp(callmode, "interactive") == 0 && inote < 2) {
941                SUMA_SLP_Note("%s",stmp); ++inote;}
942             else { SUMA_S_Note("%s",stmp); }
943          }
944          SUMA_postRedisplay(sv->X->GLXAREA, NULL, NULL);
945          break;
946       default:
947          SUMA_S_Err("Il ne faut pas etre hawn");
948          SUMA_RETURN(0);
949          break;
950    }
951 
952    SUMA_RETURN(1);
953 }
954 
SUMA_F10_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode,char * strgval)955 int SUMA_F10_Key(SUMA_SurfaceViewer *sv,char *key, char *callmode, char *strgval)
956 {
957    static char FuncName[]={"SUMA_F10_Key"};
958    char tk[]={"F10"}, keyname[100];
959    int k, nc;
960    SUMA_EngineData *ED = NULL;
961    DList *list = NULL;
962    DListElmt *NextElm= NULL;
963    static int inote = 0;
964    SUMA_Boolean LocalHead = NOPE;
965 
966    SUMA_ENTRY;
967 
968    SUMA_KEY_COMMON;
969 
970    /* do the work */
971    switch (k) {
972       case XK_F10:
973          if (sv->PryAx == 2) sv->PryAx = 3;
974          else if (sv->PryAx == 3) sv->PryAx = 2;
975          else {
976             SUMA_S_Err("Bad PryAx of %d. Reverting to 3", sv->PryAx);
977             sv->PryAx = 3;
978          }
979          {
980             char stmp[200];
981             if (sv->PryAx == 3) {
982                sprintf(stmp,"Prying about Z axis");
983             } else {
984                sprintf(stmp,"Prying about Y axis");
985             }
986             if (callmode && strcmp(callmode, "interactive") == 0 && inote < 2) {
987                SUMA_SLP_Note("%s",stmp); ++inote;}
988             else { SUMA_S_Note("%s",stmp); }
989          }
990          SUMA_postRedisplay(sv->X->GLXAREA, NULL, NULL);
991          break;
992       default:
993          SUMA_S_Err("Il ne faut pas etre la dessous");
994          SUMA_RETURN(0);
995          break;
996    }
997 
998    SUMA_RETURN(1);
999 }
1000 
SUMA_F11_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode,char * strgval)1001 int SUMA_F11_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode,
1002                  char *strgval)
1003 {
1004    static char FuncName[]={"SUMA_F11_Key"};
1005    char tk[]={"F11"}, keyname[100];
1006    int k, nc;
1007    SUMA_EngineData *ED = NULL;
1008    DList *list = NULL;
1009    DListElmt *NextElm= NULL;
1010    static int inote = 0;
1011    SUMA_Boolean LocalHead = NOPE;
1012 
1013    SUMA_ENTRY;
1014 
1015    SUMA_KEY_COMMON;
1016 
1017    /* do the work */
1018    switch (k) {
1019       case XK_F11: {
1020          if ( (callmode && strcmp(callmode, "interactive") == 0) ||
1021              !strgval /* why not? this way the
1022                         Driver can pop the interactive window */) {
1023             sv->X->SetRenderOrder_prmpt = SUMA_CreatePromptDialogStruct(
1024                                  SUMA_OK_APPLY_CLEAR_CANCEL,
1025                             "Set Object Display Order:\n"
1026                             "(e.g. VSG for: Volume, Surface, Graph)):",
1027                                  "VSG",
1028                                  sv->X->TOPLEVEL, YUP,
1029                                  SUMA_APPLY_BUTTON,
1030                                  SUMA_SV_SetRenderOrder, (void *)sv,
1031                                  NULL, NULL,
1032                                  NULL, NULL,
1033                                  SUMA_VerifyRenderOrder, NULL,
1034                                  sv->X->SetRenderOrder_prmpt);
1035 
1036             sv->X->SetRenderOrder_prmpt = SUMA_CreatePromptDialog(
1037                               sv->X->Title, sv->X->SetRenderOrder_prmpt);
1038          } else {
1039             if (!strgval) {
1040                SUMA_S_Err("Have NULL string");
1041                SUMA_RETURN(0);
1042             }
1043             SUMA_SV_SetRenderOrder(strgval, (void *)sv);
1044          }
1045          break; }
1046       default:
1047          SUMA_S_Err("Il ne faut pas etre hawn");
1048          SUMA_RETURN(0);
1049          break;
1050    }
1051 
1052    SUMA_RETURN(1);
1053 }
1054 
SUMA_F12_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)1055 int SUMA_F12_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
1056 {
1057    static char FuncName[]={"SUMA_F12_Key"};
1058    char tk[]={"F12"}, keyname[100];
1059    int k, nc;
1060    SUMA_EngineData *ED = NULL;
1061    DList *list = NULL;
1062    DListElmt *NextElm= NULL;
1063    static int inote = 0;
1064    SUMA_Boolean LocalHead = NOPE;
1065 
1066    SUMA_ENTRY;
1067 
1068    SUMA_KEY_COMMON;
1069 
1070    /* do the work */
1071    switch (k) {
1072       case XK_F12:
1073          {
1074             /* time display speed */
1075             int i, nd = 20, N_vis, *Vis_IDs=NULL, NodeTot, FaceTot;
1076             GLfloat buf;
1077             float delta_t;
1078             SUMA_SurfaceObject *SO=NULL;
1079             struct  timeval tti;
1080             char stmp[500], fnameout[]={"__SUMA.speedtest.txt"};
1081             SUMA_STRING *SS = NULL;
1082             FILE *fout=NULL;
1083 
1084             if (callmode && !strcmp(callmode, "drivesuma")) {
1085                fout = fopen(fnameout,"w");
1086             }
1087             SS = SUMA_StringAppend (NULL, NULL);
1088 
1089             buf = sv->light0_position[2];
1090             if (callmode && strcmp(callmode, "interactive") == 0) {
1091                SUMA_SLP_Note ("Timing Display speed\n"
1092                               "(20 displays): \n");
1093             } else {
1094                SUMA_S_Note("Timing Display speed\n"
1095                            "(20 displays): \n");
1096             }
1097             if (fout) fprintf(fout, "Timing Display speed (20 displays):\n");
1098 
1099             SUMA_etime (&tti, 0);
1100             for (i=0; i< nd-1; ++i) {
1101                fprintf (SUMA_STDOUT,"%d\t", i); fflush (SUMA_STDOUT);
1102                sv->light0_position[2] *= -1;
1103                glLightfv(GL_LIGHT0, GL_POSITION, sv->light0_position);
1104                /* direct call to display */
1105                SUMA_display(sv, SUMAg_DOv);
1106                /* wait for display */
1107                glFinish();
1108             }
1109             fprintf (SUMA_STDOUT,"\n");
1110             delta_t = SUMA_etime (&tti, 1);
1111             sv->light0_position[2] = buf;
1112             glLightfv(GL_LIGHT0, GL_POSITION, sv->light0_position);
1113             SUMA_postRedisplay(sv->X->GLXAREA, NULL, NULL);
1114             sprintf (stmp,
1115                      "Elapsed time: %f seconds.\n%.2f displays/second.\n",
1116                      delta_t, nd/delta_t);
1117             SS = SUMA_StringAppend (SS, stmp);
1118 
1119             /* Estimate how many nodes and triangles were rendered */
1120             Vis_IDs = (int *)SUMA_malloc(sizeof(int)*SUMAg_N_DOv);
1121             N_vis = SUMA_VisibleSOs (sv, SUMAg_DOv, Vis_IDs, 0);
1122             NodeTot = 0;
1123             FaceTot = 0;
1124             for (i=0; i<N_vis;++i) {
1125                SO = (SUMA_SurfaceObject *)SUMAg_DOv[Vis_IDs[i]].OP;
1126                FaceTot += SO->N_FaceSet;
1127                NodeTot += SO->N_Node;
1128             }
1129             if (N_vis) {
1130                sprintf (stmp, "In Polymode %d, rendered \n"
1131                               "%.2f Ktri/sec %.2f Kpnt/sec.\n",
1132                   sv->PolyMode,
1133                   (float)FaceTot / 1000.0 / delta_t  ,
1134                   (float)NodeTot / 1000.0 / delta_t );
1135                SS = SUMA_StringAppend (SS, stmp);
1136             }
1137 
1138             if (callmode && strcmp(callmode, "interactive") == 0) {
1139                SUMA_SLP_Note("%s",SS->s);
1140             } else {
1141                SUMA_S_Note("%s",SS->s);
1142             }
1143 
1144             if (fout) {
1145                fprintf(fout, "%s\n", SS->s);
1146                SUMA_S_Note("Timing results written to file: %s", fnameout);
1147                fclose(fout);
1148             }
1149 
1150             if (Vis_IDs) SUMA_free(Vis_IDs);
1151             SUMA_free(SS->s);
1152             SUMA_free(SS);
1153          }
1154          break;
1155       default:
1156          SUMA_S_Err("Il ne faut pas etre hawn");
1157          SUMA_RETURN(0);
1158          break;
1159    }
1160 
1161    SUMA_RETURN(1);
1162 }
1163 
SUMA_A_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)1164 int SUMA_A_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
1165 {
1166    static char FuncName[]={"SUMA_A_Key"};
1167    char tk[]={"A"}, keyname[100];
1168    int k, nc;
1169    SUMA_EngineData *ED = NULL;
1170    DList *list = NULL;
1171    DListElmt *NextElm= NULL;
1172    SUMA_Boolean LocalHead = NOPE;
1173 
1174    SUMA_ENTRY;
1175 
1176    SUMA_KEY_COMMON;
1177 
1178    /* do the work */
1179    switch (k) {
1180       case XK_A:
1181          if ((SUMA_CTRL_KEY(key))){
1182 
1183          } else {
1184 
1185          }
1186          break;
1187       case XK_a:
1188          /* toggle background attenuation */
1189          if (sv->Back_Modfact) {
1190             fprintf (SUMA_STDOUT,
1191                      "%s: Modulation by background intensity OFF.\n", FuncName);
1192             sv->Back_Modfact = 0;
1193          } else {
1194             fprintf (SUMA_STDOUT,
1195                      "%s: Modulation by background intensity ON.\n", FuncName);
1196             sv->Back_Modfact = SUMA_BACKGROUND_MODULATION_FACTOR;
1197          }
1198 
1199          /* set the remix flag */
1200          if (!SUMA_SetShownLocalRemixFlag (sv)) {
1201             fprintf (SUMA_STDERR,
1202                      "Error %s: Failed in SUMA_SetShownLocalRemixFlag.\n",
1203                      FuncName);
1204             break;
1205          }
1206 
1207          SUMA_postRedisplay(sv->X->GLXAREA, NULL, NULL);
1208          break;
1209       default:
1210          SUMA_S_Err("Il ne faut pas ci dessous");
1211          SUMA_RETURN(0);
1212          break;
1213    }
1214 
1215    SUMA_RETURN(1);
1216 }
1217 
1218 
SUMA_B_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)1219 int SUMA_B_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
1220 {
1221    static char FuncName[]={"SUMA_B_Key"};
1222    char tk[]={"B"}, keyname[100];
1223    int k, nc;
1224    SUMA_EngineData *ED = NULL;
1225    DList *list = NULL;
1226    DListElmt *NextElm= NULL;
1227    SUMA_Boolean LocalHead = NOPE;
1228 
1229    SUMA_ENTRY;
1230 
1231    SUMA_KEY_COMMON;
1232 
1233    /* do the work */
1234    switch (k) {
1235       case XK_B:
1236          if ((SUMA_CTRL_KEY(key))){
1237             if (SUMAg_CF->Dev ) {
1238                sv->Blend_Mode = (sv->Blend_Mode+1)%SUMA_N_BLEND_MODES;
1239                switch (sv->Blend_Mode) {
1240                   case SUMA_NO_BLEND:
1241                      glDisable(GL_BLEND);
1242                      if (callmode && strcmp(callmode, "interactive") == 0) {
1243                            SUMA_SLP_Note ("Blending  disabled."); }
1244                      else { SUMA_S_Note ("Blending  disabled."); }
1245                      break;
1246                   case SUMA_BLEND1:
1247                      glEnable (GL_BLEND);
1248                      glBlendFunc(GL_ONE,GL_SRC_ALPHA);
1249                      if (callmode && strcmp(callmode, "interactive") == 0) {
1250                               SUMA_SLP_Note ("Blending mode1."); }
1251                      else { SUMA_S_Note ("Blending  mode1."); }
1252                      break;
1253                   case SUMA_BLEND2:
1254                      glEnable (GL_BLEND);
1255                      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1256                      if (callmode && strcmp(callmode, "interactive") == 0) {
1257                            SUMA_SLP_Note ("Blending mode2.");}
1258                      else { SUMA_S_Note ("Blending  mode2."); }
1259                      break;
1260                   default:
1261                      SUMA_SL_Err ("Should not be here");
1262                      break;
1263                }
1264                SUMA_postRedisplay(sv->X->GLXAREA, NULL, NULL);
1265             }
1266          } else {
1267             sv->BF_Cull = (sv->BF_Cull+1)%3;
1268             if (callmode && strcmp(callmode, "interactive") == 0) {
1269                   SUMA_CullOption(sv, "Apply");}
1270             else { SUMA_CullOption(sv, "Restore");}
1271             SUMA_postRedisplay(sv->X->GLXAREA, NULL, NULL);
1272          }
1273          break;
1274       case XK_b:
1275          /* Show/hide the background */
1276          if (!list) list = SUMA_CreateList();
1277          ED = SUMA_InitializeEngineListData (SE_ToggleBackground);
1278          if (!SUMA_RegisterEngineListCommand (  list, ED,
1279                                                 SEF_Empty, NULL,
1280                                                 SES_Suma, (void *)sv, NOPE,
1281                                                 SEI_Head, NULL)) {
1282             fprintf (SUMA_STDERR,
1283                   "Error %s: Failed to register command.\n", FuncName);
1284          }
1285 
1286          ED = SUMA_InitializeEngineListData (SE_Redisplay);
1287          if (!SUMA_RegisterEngineListCommand (  list, ED,
1288                                                 SEF_Empty, NULL,
1289                                                 SES_Suma, (void *)sv, NOPE,
1290                                                 SEI_Head, NULL)) {
1291             fprintf (SUMA_STDERR,
1292                   "Error %s: Failed to register command.\n", FuncName);
1293          }
1294 
1295          if (!SUMA_Engine (&list)) {
1296             fprintf(SUMA_STDERR, "Error SUMA_input: SUMA_Engine call failed.\n");
1297          }
1298          break;
1299       default:
1300          SUMA_S_Err("Il ne faut pas ci dessous");
1301          SUMA_RETURN(0);
1302          break;
1303    }
1304 
1305    SUMA_RETURN(1);
1306 }
1307 
1308 
1309 
SUMA_D_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)1310 int SUMA_D_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
1311 {
1312    static char FuncName[]={"SUMA_D_Key"};
1313    SUMA_LIST_WIDGET *LW=NULL;
1314    char tk[]={"D"}, keyname[100];
1315    int k, nc, inode = 0, N_ts = 0, ChildOverInd=-1,loc[2], ii = 0;
1316    float ftop = 0.1, fbot = 0.01, fs=0.0, fstep=0.0, *fv=NULL;
1317    int normalize = 1, polort = 2;
1318    SUMA_EngineData *ED = NULL;
1319    SUMA_DSET *dot=NULL;
1320    SUMA_DSET *in_dset = NULL;
1321    SUMA_SurfaceObject *SO=NULL;
1322    SUMA_OVERLAYS *Sover=NULL, *child=NULL;
1323    SUMA_DSET *inc_dset=NULL;
1324    SUMA_SurfaceObject *SOC=NULL;
1325    DList *list = NULL;
1326    DListElmt *el= NULL;
1327    double *ts = NULL, TR=0.0;
1328    NI_group *ngr = NULL;
1329    NI_element *nel=NULL, *dotopts=NULL;
1330    SUMA_XFORM *xf=NULL;
1331    SUMA_CALLBACK *cb=NULL;
1332    char stmp[SUMA_MAX_NAME_LENGTH]={""};
1333    SUMA_Boolean LocalHead = NOPE;
1334 
1335    SUMA_ENTRY;
1336 
1337    SUMA_KEY_COMMON;
1338 
1339    /* do the work */
1340    switch (k) {
1341       case XK_D:
1342          if ((SUMA_CTRL_KEY(key))){
1343          } else {
1344             SUMA_LH("The dotthing");
1345             /* DO the dot thing */
1346             if (!(SO = SUMA_SV_Focus_SO(sv))) {
1347                if (callmode && strcmp(callmode, "interactive") == 0) {
1348                   SUMA_SLP_Err("No surface in focus.\nCannot dot.");
1349                } else {
1350                   SUMA_S_Err("No surface in focus.\nCannot dot.");
1351                }
1352                SUMA_RETURN(0);
1353             }
1354             if (  !SO || !SO->SurfCont ||
1355                   !SO->SurfCont->curColPlane ||
1356                   !SO->SurfCont->curColPlane->dset_link) {
1357                SUMA_SL_Err("Nothing to dot");
1358                SUMA_RETURN(0);
1359             }
1360             Sover = SO->SurfCont->curColPlane;
1361             in_dset = SO->SurfCont->curColPlane->dset_link;
1362             inode = SO->SelectedNode;
1363             if (inode < 0) {
1364                if (callmode && strcmp(callmode, "interactive") == 0) {
1365                   SUMA_SLP_Warn("No selected node.\nNothing to dot.");
1366                }else{
1367                   SUMA_S_Warn("No selected node.\nNothing to dot.");
1368                }
1369                SUMA_RETURN(1);
1370             }
1371             if (SDSET_VECNUM(in_dset) < 3) {
1372                if (callmode && strcmp(callmode, "interactive") == 0) {
1373                   SUMA_SLP_Warn( "Need more than 3 values per node.\n"
1374                                  "Nothing to dot.");
1375                }else{
1376                   SUMA_S_Warn("Need more than 3 values per node.\n"
1377                               "Nothing to dot.");
1378                }
1379                SUMA_RETURN(1);
1380             }
1381 
1382 
1383             if ((xf = SUMA_Find_XformByParent("Dot", SDSET_ID(in_dset), NULL))) {
1384                /* xform exists already, just flip toggle of all callbacks
1385                   that claim it as a creator*/
1386                SUMA_SetXformActive(xf, -1*xf->active, 0);
1387 
1388                if (callmode && strcmp(callmode, "interactive") == 0) {
1389                   if (xf->active > 0) {
1390                      SUMA_SLP_Note("Dot product callback active");
1391                   } else {
1392                      SUMA_SLP_Note("Dot product callback inactive");
1393                   }
1394                }else {
1395                   if (xf->active > 0) {
1396                      SUMA_S_Note("Dot product callback active");
1397                   } else {
1398                      SUMA_S_Note("Dot product callback inactive");
1399                   }
1400                }
1401 
1402                SUMA_RETURN(1);
1403             } else { /* New Xform */
1404                /* We need to add a dot product transform to
1405                   the dataset */
1406                SUMA_LH("Afresh: Adding Dot Xform");
1407                xf = SUMA_NewXform("Dot",
1408                                   SDSET_ID(in_dset), SO->LocalDomainParentID);
1409 
1410                /* Any matching contralateral dset and a matching surface ?*/
1411                inc_dset = SUMA_Contralateral_dset(in_dset, SO, &SOC);
1412                if (inc_dset) {
1413                   SUMA_LHv("Found contralateral dset %s (%s)\n",
1414                                SDSET_LABEL(inc_dset), SDSET_FILENAME(inc_dset));
1415                   if (!(SUMA_AddXformParent(xf,
1416                               SDSET_ID(inc_dset), SOC->LocalDomainParentID))) {
1417                      SUMA_S_Err("Failed to add parent");
1418                      SUMA_RETURN(0);
1419                   }
1420                }  else {
1421                   SUMA_S_Note("No contralateral dset ");
1422                }
1423                /* Initialize dot product options. You'll have to redo this
1424                   unfortunately below... */
1425                if (!(SUMA_is_TimeSeries_dset(in_dset, &TR))) {
1426                   TR = 0.0;
1427                }
1428                SUMA_SPECT_AXIS(TR, SDSET_VECNUM(in_dset), fs, ftop, fstep);
1429                dotopts = SUMA_set_dotopts(NULL, SDSET_VECNUM(in_dset),
1430                                            0.1, fbot,
1431                                            normalize, 1,
1432                                            polort, NULL);
1433 
1434 
1435                SUMA_LH("Get dot product time series");
1436 
1437                if (!(fv = (float*)SUMA_GetDsetAllNodeValsInCols2(in_dset,
1438                                        NULL, 0,
1439                                        inode, SO->N_Node-1,
1440                                        &N_ts,
1441                                        SUMA_float))) {
1442                   SUMA_S_Err("Failed to extract time series.");
1443                   SUMA_RETURN(0);
1444                }
1445                if (!(ts = SUMA_DotPreProcessTimeSeries(fv,  N_ts,
1446                                              (float)TR, dotopts))) {
1447                   SUMA_S_Err("Failed to preprocess time series.");
1448                   SUMA_RETURN(0);
1449                }
1450                SUMA_free(fv); fv=NULL;
1451 
1452                for (ii=0; ii<xf->N_parents; ++ii) {
1453                   dot = NULL; /* You'll need a new dset with each pass */
1454                   if (!SUMA_is_ID_4_DSET(xf->parents[ii], &in_dset)) {
1455                            /* This is a convoluted way to get in_dset,
1456                               since in_dset is known from a few lines above.
1457                               But it is meant to demo how to work with
1458                               multiple parents in xf in general */
1459                      SUMA_S_Err("You've really done it this time!");
1460                      SUMA_RETURN(0);
1461                   }
1462                   if (!SUMA_is_ID_4_SO(xf->parents_domain[ii], &SO)) {
1463                            /* This is a convoluted way to get SO...*/
1464                      SUMA_S_Err("You've really really done it this time!");
1465                      SUMA_RETURN(0);
1466                   }
1467                   SUMA_LHv("Creating dot for %d/%d %s\n",
1468                            ii, xf->N_parents, SDSET_FILENAME(in_dset));
1469                   if (!(SUMA_dot_product(in_dset, ts,
1470                                  &dot,
1471                                  dotopts))) {
1472                      SUMA_S_Err("Failed to create dot product");
1473                      SUMA_RETURN(0);
1474                   }
1475 
1476                   /* Assign domain parent fields  */
1477                   NI_set_attribute(dot->ngr,"domain_parent_idcode",
1478                                    NI_get_attribute(in_dset->ngr,
1479                                                     "domain_parent_idcode"));
1480                   NI_set_attribute(dot->ngr,"geometry_parent_idcode",
1481                                    NI_get_attribute(in_dset->ngr,
1482                                                     "geometry_parent_idcode"));
1483 
1484                   SUMA_LHv( "Insert (%s/%s)in DsetList\n",
1485                             SDSET_LABEL(dot), SDSET_ID(dot) );
1486                                                    /* no replacement allowed
1487                                                    I don't think it's necessary*/
1488                   if (!SUMA_InsertDsetPointer (&dot, SUMAg_CF->DsetList, 0)) {
1489                      SUMA_S_Errv("Failed to insert pointer for %s\n",
1490                                  SDSET_LABEL(dot));
1491                      SUMA_RETURN(0);
1492                   }
1493 
1494                   /* Add child to Xform */
1495                   if (!SUMA_AddXformChild(xf,SDSET_ID(dot))) {
1496                      SUMA_S_Err("Failed to add child");
1497                      SUMA_RETURN(0);
1498                   }
1499 
1500                   SUMA_LH("Create its overlay (child)");
1501                   if (!(SDSET_LABEL(dot))) SUMA_LabelDset(dot,NULL);
1502                   sprintf(stmp, "overlay.%s", SDSET_ID(dot));
1503                   child = SUMA_CreateOverlayPointer (
1504                                           stmp, dot, SO->idcode_str, NULL);
1505                   if (!child) {
1506                      fprintf (SUMA_STDERR,
1507                               "Error %s: Failed in CreateOverlayPointer.\n" ,
1508                               FuncName);
1509                      SUMA_RETURN (0);
1510                   }
1511                   SUMA_LH("Add overlay to SO");
1512                   /* Add this plane to SO->Overlays */
1513                   if (!SUMA_AddNewPlane ( (SUMA_ALL_DO *)SO, child,
1514                                           SUMAg_DOv, SUMAg_N_DOv, 0)) {
1515                      SUMA_SL_Crit("Failed in SUMA_AddNewPlane");
1516                      SUMA_FreeOverlayPointer(child);
1517                      SUMA_RETURN (0);
1518                   }
1519 
1520                   ChildOverInd = SO->N_Overlays-1;
1521                   /* set the opacity, index column and the range */
1522                   child->GlobalOpacity = YUP;
1523                   child->ShowMode = SW_SurfCont_DsetViewCol;
1524                   child->OptScl->BrightFact = 0.8;
1525 
1526                   child->OptScl->find = 0;
1527                   child->OptScl->tind = 0;
1528                   child->OptScl->bind = 0;
1529                   child->OptScl->UseThr = 1; /* turn on threshold use */
1530                   child->SymIrange = 1;   /* Use symmetric range */
1531                   child->OptScl->AutoIntRange = 0; /* Do not update range */
1532 
1533                   /* Leave it to the -0.5, 0.5 range below, it works better
1534                      SUMA_GetDsetColRange(dot, 0, child->OptScl->IntRange, loc);
1535                       */
1536 
1537                   SO->SurfCont->curColPlane = child;
1538 
1539                   /* update the Dset frame */
1540                   if (ChildOverInd >= 0)
1541                      SUMA_InitializeColPlaneShell((SUMA_ALL_DO *)SO,
1542                                        SO->Overlays[ChildOverInd]);
1543                   SUMA_UPDATE_ALL_NODE_GUI_FIELDS((SUMA_ALL_DO *)SO);
1544                   child->OptScl->IntRange[0] = -0.5;  /* set the range */
1545                   child->OptScl->IntRange[1] = 0.5;
1546                   SUMA_INSERT_CELL_VALUE(SO->SurfCont->SetRangeTable, 1, 1,
1547                                           child->OptScl->IntRange[0]);
1548                   SUMA_INSERT_CELL_VALUE(SO->SurfCont->SetRangeTable, 1, 2,
1549                                           child->OptScl->IntRange[1]);
1550 
1551                   SUMA_LH("Colorize plane");
1552                   SUMA_ColorizePlane(child);
1553 
1554                   /* remix-redisplay  for surface */
1555                   if (!SUMA_Remixedisplay ((SUMA_ALL_DO*)SO)) {
1556                      SUMA_RETURN(0);
1557                   }
1558 
1559                   SUMA_LH("Refreshing Dset list");
1560                   /*update the list widget if open */
1561                   LW = SO->SurfCont->SwitchDsetlst;
1562                   if (LW) {
1563                      if (!LW->isShaded) SUMA_RefreshDsetList ((SUMA_ALL_DO *)SO);
1564                   }
1565 
1566                   if (LocalHead)
1567                      fprintf (SUMA_STDERR,
1568                               "%s: Updating Dset frame, OverInd=%d\n",
1569                               FuncName, ChildOverInd);
1570 
1571                } /* For each parent */
1572 
1573                /* done with ts */
1574                SUMA_free(ts); ts = NULL;
1575 
1576                /* add the dotoptions to the transform options group */
1577                NI_add_to_group(xf->XformOpts, dotopts);
1578 
1579                /* activate xform */
1580                SUMA_SetXformActive(xf, 1, 0);
1581 
1582 
1583                SUMA_LH("Add Callback ");
1584                /* generic parts */
1585                cb = SUMA_NewCallback(  "SUMA_dot_product_CB",
1586                                        SUMA_NEW_NODE_ACTIVATE_EVENT,
1587                                        SUMA_dot_product_CB,
1588                                        xf->children[0],
1589                                        xf->parents_domain[0],
1590                                        xf->idcode_str);
1591 
1592 
1593                /* add extra children */
1594                for (ii=1; ii<xf->N_children; ++ii) {
1595                   if (!SUMA_AddCallbackParent(cb, xf->children[ii],
1596                                               xf->parents_domain[ii])) {
1597                      SUMA_S_Err("Failed to add parent");
1598                      SUMA_RETURN(0);
1599                   }
1600                }
1601 
1602                /* fill the callback function's input parameters */
1603                /* The time series datasets */
1604                nel = NI_new_data_element("AFNI_atr", 1);
1605                NI_set_attribute(nel, "atr_name", "ts_dsets_idcode");
1606                NI_add_column_stride(nel, NI_STRING, NULL,1);
1607                for (ii=0; ii<xf->N_parents; ++ii) {
1608                   if (!SUMA_AddColAtt_CompString( nel, ii,
1609                                              xf->parents[ii], SUMA_NI_CSS, 0)){
1610                      SUMA_S_Err("Failed to add ts_dsets_idcode");
1611                      SUMA_RETURN(0);
1612                   }
1613                }
1614                NI_add_to_group(cb->FunctionInput, nel);
1615 
1616                /* the output datasets */
1617                nel = NI_new_data_element("AFNI_atr", 1);
1618                NI_set_attribute(nel, "atr_name", "dot_dsets_idcode");
1619                NI_add_column_stride(nel, NI_STRING, NULL,1);
1620                for (ii=0; ii<xf->N_children; ++ii) {
1621                   if (!SUMA_AddColAtt_CompString( nel, ii,
1622                                              xf->children[ii], SUMA_NI_CSS, 0)){
1623                      SUMA_S_Err("Failed to add dot_dsets_idcode");
1624                      SUMA_RETURN(0);
1625                   }
1626                }
1627                NI_add_to_group(cb->FunctionInput, nel);
1628 
1629                /* Node is from surface SO, but there is no point
1630                in setting this now because the computations
1631                have been done already. Set all sources for
1632                getting the time series to nothing, for the
1633                record.*/
1634                /* ts can be specified via node selection events and parents */
1635                SUMA_FlushCallbackEventParameters (cb);
1636 
1637                /* or ts can be explicitly set
1638                   again, the existence of ts_vec is not necessary,
1639                   but to illustrate options ...*/
1640                nel = NI_new_data_element("callback.data",0);
1641                NI_set_attribute(nel, "data_type", "ts_vec");
1642                NI_add_to_group(cb->FunctionInput, nel);
1643 
1644                /* set callback to be active, but not pending */
1645                cb->active = 1;
1646                SUMA_SetCallbackPending(cb, 0, SES_Empty);
1647                if (callmode && strcmp(callmode, "interactive") == 0) {
1648                   SUMA_SLP_Note("Dot product callback active");                                  } else {
1649                   SUMA_S_Note("Dot product callback active");
1650                }
1651 
1652                if (LocalHead) {
1653                   SUMA_Show_Xforms(SUMAg_CF->xforms, SUMA_STDERR, 1);
1654                   SUMA_Show_Callbacks(SUMAg_CF->callbacks, SUMA_STDERR, 1);
1655                }
1656 
1657                /* initialize interface (no callbacks willget triggered)*/
1658                SUMA_InitializeXformInterface (xf);
1659 
1660             } /* New Xform */
1661 
1662          }
1663          break;
1664       case XK_d:
1665          if ((SUMA_CTRL_KEY(key))){
1666             /* Interactively it is handled by the mnemonic
1667               But if it gets here, the Driver may be driving it */
1668             /* opens draw ROI controller */
1669             if (!SUMA_OpenDrawROIController(NULL)) {
1670                SUMA_S_Err("Failed to open controller");
1671             }
1672          } else {
1673             if (SUMAg_CF->Dev ) {
1674                SUMA_Show_DOv(SUMAg_DOv, SUMAg_N_DOv, stdout);
1675             }
1676          }
1677 
1678 
1679          break;
1680       default:
1681          SUMA_S_Err("Il ne faut pas ci dessous");
1682          SUMA_RETURN(0);
1683          break;
1684    }
1685 
1686    SUMA_RETURN(1);
1687 }
1688 
SUMA_G_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)1689 int SUMA_G_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
1690 {
1691    static char FuncName[]={"SUMA_G_Key"};
1692    char tk[]={"G"}, keyname[100];
1693    int k=0, nc=-1;
1694    int inode = -1;
1695    SUMA_DSET *Dset = NULL;
1696    SUMA_ALL_DO *ado=NULL;
1697    SUMA_OVERLAYS *Sover=NULL;
1698    SUMA_X_SurfCont *SurfCont=NULL;
1699    char stmp[100]={"\0"};
1700    SUMA_Boolean LocalHead = NOPE;
1701 
1702    SUMA_ENTRY;
1703 
1704    SUMA_KEY_COMMON;
1705 
1706    if (!(ado = SUMA_SV_Focus_ADO(sv))) {
1707       if (callmode && strcmp(callmode, "interactive") == 0) {
1708          SUMA_SLP_Err("No surface in focus.\nCannot graph.");
1709       } else {
1710          SUMA_S_Err("No surface in focus.\nCannot graph.");
1711       }
1712       SUMA_RETURN(0);
1713    }
1714    if (  !ado || !(SurfCont = SUMA_ADO_Cont(ado)) ||
1715          !(Sover = SUMA_ADO_CurColPlane(ado)) ||
1716          !(Dset = Sover->dset_link)) {
1717       SUMA_SL_Err("Nothing to graph");
1718       SUMA_RETURN(0);
1719    }
1720    inode = SUMA_ADO_SelectedDatum(ado, NULL, NULL);
1721    if (inode < 0) {
1722       if (callmode && strcmp(callmode, "interactive") == 0) {
1723          SUMA_SLP_Warn("No selected node.\nNothing to graph.");
1724       }else{
1725          SUMA_S_Warn("No selected node.\nNothing to graph.");
1726       }
1727       SUMA_RETURN(1);
1728    }
1729    if (SDSET_VECNUM(Dset) < 2) {
1730       if (callmode && strcmp(callmode, "interactive") == 0) {
1731          SUMA_SLP_Warn("One or no value per node.\nNothing to graph.");
1732       }else{
1733          SUMA_S_Warn("One or no value per node.\nNothing to graph.");
1734       }
1735       SUMA_RETURN(1);
1736    }
1737    /* do the work */
1738    switch (k) {
1739       case XK_g:
1740          if ((SUMA_CTRL_KEY(key))) {
1741          } else {
1742             SUMA_OverlayGraphAtNode(Sover, ado, inode);
1743          }
1744          break;
1745       case XK_G:
1746          if (SUMAg_CF->Dev ) {
1747             #ifdef SUMA_USE_AFNI_GRAPH
1748                /* an attempt at using AFNI's graphing interface */
1749                SUMA_Afni_Graph(Sover, SO);
1750             #endif
1751          }
1752          break;
1753       default:
1754          SUMA_S_Err("Il ne faut pas etre ici");
1755          SUMA_RETURN(0);
1756          break;
1757    }
1758    SUMA_RETURN(1);
1759 }
1760 
SUMA_J_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode,char * strgval)1761 int SUMA_J_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode, char *strgval)
1762 {
1763    static char FuncName[]={"SUMA_J_Key"};
1764    char tk[]={"J"}, keyname[100];
1765    int k=0, nc=-1;
1766    int inode = -1;
1767    SUMA_DSET *Dset = NULL;
1768    SUMA_ALL_DO *ado=NULL;
1769    SUMA_OVERLAYS *Sover=NULL;
1770    char stmp[100]={"\0"};
1771    SUMA_Boolean LocalHead = NOPE;
1772 
1773    SUMA_ENTRY;
1774 
1775    SUMA_KEY_COMMON;
1776 
1777 
1778    if (!(ado = SUMA_SV_Focus_ADO(sv))) {
1779       if (callmode && strcmp(callmode, "interactive") == 0) {
1780          SUMA_SLP_Err("No object in focus.\nCannot Jump.");
1781       } else {
1782          SUMA_S_Err("No object in focus.\nCannot Jump.");
1783       }
1784       SUMA_RETURN(0);
1785    }
1786    /* do the work */
1787    switch (k) {
1788       case XK_j:
1789         if ( (callmode && strcmp(callmode, "interactive") == 0) ||
1790              !strgval /* why not? this way the
1791                         Driver can pop the interactive window */) {
1792             if (SUMA_CTRL_KEY(key)){
1793                  sv->X->JumpXYZ_prmpt = SUMA_CreatePromptDialogStruct(
1794                                  SUMA_OK_APPLY_CLEAR_CANCEL,
1795                                  "Enter XYZ to send the cross hair to:",
1796                                  "",
1797                                  sv->X->TOPLEVEL, YUP,
1798                                  SUMA_APPLY_BUTTON,
1799                                  SUMA_JumpXYZ, (void *)sv,
1800                                  NULL, NULL,
1801                                  NULL, NULL,
1802                                  SUMA_CleanNumString, (void*)3,
1803                                  sv->X->JumpXYZ_prmpt);
1804                   sv->X->JumpXYZ_prmpt = SUMA_CreatePromptDialog(
1805                                     sv->X->Title, sv->X->JumpXYZ_prmpt);
1806 
1807              } else if (SUMA_AALT_KEY(key)){
1808                   sv->X->JumpFocusNode_prmpt = SUMA_CreatePromptDialogStruct(
1809                                  SUMA_OK_APPLY_CLEAR_CANCEL,
1810                      "Enter index of focus node\n"
1811                      "Prepend/append L/R for hemiisphere selection\n"
1812                      "Cross hair's XYZ will not be affected:",
1813                                  "",
1814                                  sv->X->TOPLEVEL, YUP,
1815                                  SUMA_APPLY_BUTTON,
1816                                  SUMA_JumpFocusNode, (void *)sv,
1817                                  NULL, NULL,
1818                                  NULL, NULL,
1819                                  SUMA_CleanNumStringSide, (void*)1,                                                    sv->X->JumpFocusNode_prmpt);
1820 
1821                   sv->X->JumpFocusNode_prmpt = SUMA_CreatePromptDialog(
1822                                     sv->X->Title, sv->X->JumpFocusNode_prmpt);
1823 
1824              } else {
1825                   sv->X->JumpIndex_prmpt = SUMA_CreatePromptDialogStruct(
1826                                  SUMA_OK_APPLY_CLEAR_CANCEL,
1827                             "Enter index of node to send the cross hair to:\n"
1828                             "(prepend/append L/R for specifying hemisphere):",
1829                                  "",
1830                                  sv->X->TOPLEVEL, YUP,
1831                                  SUMA_APPLY_BUTTON,
1832                                  SUMA_JumpIndex, (void *)sv,
1833                                  NULL, NULL,
1834                                  NULL, NULL,
1835                                  SUMA_CleanNumStringSide, (void*)1,
1836                                  sv->X->JumpIndex_prmpt);
1837 
1838                   sv->X->JumpIndex_prmpt = SUMA_CreatePromptDialog(
1839                                     sv->X->Title, sv->X->JumpIndex_prmpt);
1840              }
1841          } else {
1842             if (!strgval) {
1843                SUMA_S_Err("Have NULL string");
1844                SUMA_RETURN(0);
1845             }
1846             if (SUMA_CTRL_KEY(key)){
1847                SUMA_JumpXYZ(strgval, (void *)sv);
1848             } else if (SUMA_AALT_KEY(key)){
1849                SUMA_JumpFocusNode(strgval, (void *)sv);
1850             } else {
1851                SUMA_JumpIndex(strgval, (void *)sv);
1852             }
1853          }
1854 
1855          break;
1856       case XK_J:
1857          if ( (callmode && strcmp(callmode, "interactive") == 0) ||
1858                !strgval) {
1859             sv->X->JumpFocusFace_prmpt = SUMA_CreatePromptDialogStruct (
1860                      SUMA_OK_APPLY_CLEAR_CANCEL,
1861                      "Enter index of FaceSet\nto highlight (this viewer only):",
1862                      "",
1863                      sv->X->TOPLEVEL, YUP,
1864                      SUMA_APPLY_BUTTON,
1865                      SUMA_JumpFocusFace, (void *)sv,
1866                      NULL, NULL,
1867                      NULL, NULL,
1868                      SUMA_CleanNumString, (void*)1,
1869                      sv->X->JumpFocusFace_prmpt);
1870 
1871             sv->X->JumpFocusFace_prmpt = SUMA_CreatePromptDialog(
1872                      sv->X->Title, sv->X->JumpFocusFace_prmpt);
1873          } else {
1874             if (!strgval) {
1875                SUMA_S_Err("Have NULL string");
1876                SUMA_RETURN(0);
1877             }
1878             SUMA_JumpFocusFace( strgval, (void *)sv);
1879          }
1880          break;
1881       default:
1882          SUMA_S_Err("Il ne faut pas etre ici");
1883          SUMA_RETURN(0);
1884          break;
1885    }
1886    SUMA_RETURN(1);
1887 }
1888 
SUMA_L_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode,char * strgval)1889 int SUMA_L_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode, char *strgval)
1890 {
1891    static char FuncName[]={"SUMA_L_Key"};
1892    char tk[]={"L"}, keyname[100];
1893    int k, nc;
1894    SUMA_EngineData *ED = NULL;
1895    DList *list = NULL;
1896    DListElmt *NextElm= NULL;
1897    SUMA_Boolean LocalHead = NOPE;
1898 
1899    SUMA_ENTRY;
1900 
1901    SUMA_KEY_COMMON;
1902    if (strgval) {
1903       SUMA_S_Warn("strgval (%s) not implemented for L_Key yet. \n"
1904                   "Complain to author if you want it.\n", strgval);
1905    }
1906    /* do the work */
1907    switch (k) {
1908       case XK_l:
1909             if ((SUMA_CTRL_KEY(key))){
1910                #if 0 /* Not of much use */
1911                if (SUMAg_CF->Dev) {
1912                   if (!list) list = SUMA_CreateList();
1913                   ED = SUMA_InitializeEngineListData (SE_ToggleLockAllCrossHair);
1914                   if (!SUMA_RegisterEngineListCommand (  list, ED,
1915                                                    SEF_Empty, NULL,
1916                                                   SES_Suma, (void *)sv, NOPE,
1917                                                   SEI_Head, NULL )) {
1918                      fprintf( SUMA_STDERR,
1919                               "Error %s: Failed to register command\n",
1920                               FuncName);
1921                      break;
1922                   }
1923                   if (!SUMA_Engine (&list)) {
1924                      fprintf( stderr,
1925                               "Error %s: SUMA_Engine call failed.\n", FuncName);
1926                   }
1927                }
1928                #else
1929                GLfloat light0_color[] = { SUMA_LIGHT0_COLOR_INIT};
1930                   /* dim the lights */
1931                   sv->dim_spe = sv->dim_spe * 0.8;
1932                      if (sv->dim_spe < 0.1) sv->dim_spe = 1.0;
1933                   sv->dim_dif = sv->dim_dif * 0.8;
1934                      if (sv->dim_dif < 0.1) sv->dim_dif = 1.0;
1935                   sv->dim_amb = sv->dim_amb * 0.8;
1936                      if (sv->dim_amb < 0.1) sv->dim_amb = 1.0;
1937                   sv->dim_emi = sv->dim_emi * 0.8;
1938                      if (sv->dim_emi < 0.1) sv->dim_emi = 1.0;
1939                   fprintf(SUMA_STDERR,
1940                            "%s:  light dim factor now %.3f\n",
1941                            FuncName, sv->dim_spe);
1942                   /*fprintf(SUMA_STDERR,"%s:  light dim factor now %.3f\n"
1943                                         "%f %f %f %f\n",
1944                                         FuncName, sv->dim_spe,
1945                            sv->light0_color[0], sv->light0_color[1],
1946                            sv->light0_color[2], sv->light0_color[3]);
1947                                                       */
1948                   light0_color[0] = sv->light0_color[0]*sv->dim_spe;
1949                   light0_color[1] = sv->light0_color[1]*sv->dim_spe;
1950                   light0_color[2] = sv->light0_color[2]*sv->dim_spe;
1951                   light0_color[3] = sv->light0_color[3]*sv->dim_spe;
1952                   glLightfv(GL_LIGHT0, GL_SPECULAR, light0_color);
1953                   light0_color[0] = sv->light0_color[0]*sv->dim_dif;
1954                   light0_color[1] = sv->light0_color[1]*sv->dim_dif;
1955                   light0_color[2] = sv->light0_color[2]*sv->dim_dif;
1956                   light0_color[3] = sv->light0_color[3]*sv->dim_dif;
1957                   glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_color);
1958                   light0_color[0] = sv->lmodel_ambient[0]*sv->dim_amb;
1959                   light0_color[1] = sv->lmodel_ambient[1]*sv->dim_amb;
1960                   light0_color[2] = sv->lmodel_ambient[2]*sv->dim_amb;
1961                   light0_color[3] = sv->lmodel_ambient[3]*sv->dim_amb;
1962                   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, sv->lmodel_ambient);
1963                   if (!list) list = SUMA_CreateList();
1964                   SUMA_REGISTER_HEAD_COMMAND_NO_DATA( list, SE_Redisplay,
1965                                                       SES_Suma, sv);
1966 
1967                   if (!SUMA_Engine (&list)) {
1968                      fprintf(stderr,
1969                              "Error SUMA_input: SUMA_Engine call failed.\n");
1970                   }
1971                #endif
1972             } else if ((SUMA_AALT_KEY(key))){ /* alt + l */
1973                /* register cross hair XYZ with ED */
1974                if (!list) list = SUMA_CreateList();
1975                ED = SUMA_InitializeEngineListData (SE_SetLookAt);
1976                if (!SUMA_RegisterEngineListCommand (  list, ED,
1977                                                 SEF_fv3, (void *)sv->Ch->c,
1978                                                 SES_Suma, (void *)sv, NOPE,
1979                                                 SEI_Head, NULL )) {
1980                   fprintf( SUMA_STDERR,
1981                            "Error %s: Failed to register command\n", FuncName);
1982                   SUMA_RETURN(0);
1983                }
1984                if (!SUMA_Engine (&list)) {
1985                   fprintf(stderr,
1986                            "Error %s: SUMA_Engine call failed.\n", FuncName);
1987                }
1988             } else {
1989                sv->X->LookAt_prmpt = SUMA_CreatePromptDialogStruct(
1990                                           SUMA_OK_APPLY_CLEAR_CANCEL,
1991                                           "X,Y,Z coordinates to look at:",
1992                                           "0,0,0",
1993                                           sv->X->TOPLEVEL, YUP,
1994                                           SUMA_APPLY_BUTTON,
1995                                           SUMA_LookAtCoordinates, (void *)sv,
1996                                           NULL, NULL,
1997                                           NULL, NULL,
1998                                           SUMA_CleanNumString, (void*)3,
1999                                           sv->X->LookAt_prmpt);
2000 
2001                sv->X->LookAt_prmpt = SUMA_CreatePromptDialog(sv->X->Title,
2002                                                          sv->X->LookAt_prmpt);
2003 
2004             }
2005          break;
2006       case XK_L:
2007                if ((SUMA_CTRL_KEY(key))){
2008                   GLfloat light0_color[] = { SUMA_LIGHT0_COLOR_INIT};
2009                   /* brighten the lights */
2010                   sv->dim_spe = sv->dim_spe / 0.8;
2011                   if (sv->dim_spe > 1) sv->dim_spe = 0.1;
2012                   sv->dim_dif = sv->dim_dif / 0.8;
2013                      if (sv->dim_dif > 1) sv->dim_dif = 0.1;
2014                   sv->dim_amb = sv->dim_amb / 0.8;
2015                      if (sv->dim_amb > 1) sv->dim_amb = 0.1;
2016                   sv->dim_emi = sv->dim_emi / 0.8;
2017                      if (sv->dim_emi > 1) sv->dim_emi = 0.1;
2018                      fprintf(SUMA_STDERR,
2019                               "%s:  light dim factor now %.3f\n",
2020                               FuncName, sv->dim_spe);
2021                      /*fprintf(SUMA_STDERR,"%s:  light dim factor now %.3f\n"
2022                                            "%f %f %f %f\n",
2023                                            FuncName, sv->dim_spe,
2024                               sv->light0_color[0], sv->light0_color[1],
2025                               sv->light0_color[2], sv->light0_color[3]);
2026                                                          */
2027                      light0_color[0] = sv->light0_color[0]*sv->dim_spe;
2028                      light0_color[1] = sv->light0_color[1]*sv->dim_spe;
2029                      light0_color[2] = sv->light0_color[2]*sv->dim_spe;
2030                      light0_color[3] = sv->light0_color[3]*sv->dim_spe;
2031                      glLightfv(GL_LIGHT0, GL_SPECULAR, light0_color);
2032                      light0_color[0] = sv->light0_color[0]*sv->dim_dif;
2033                      light0_color[1] = sv->light0_color[1]*sv->dim_dif;
2034                      light0_color[2] = sv->light0_color[2]*sv->dim_dif;
2035                      light0_color[3] = sv->light0_color[3]*sv->dim_dif;
2036                      glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_color);
2037                      light0_color[0] = sv->lmodel_ambient[0]*sv->dim_amb;
2038                      light0_color[1] = sv->lmodel_ambient[1]*sv->dim_amb;
2039                      light0_color[2] = sv->lmodel_ambient[2]*sv->dim_amb;
2040                      light0_color[3] = sv->lmodel_ambient[3]*sv->dim_amb;
2041                      glLightModelfv(GL_LIGHT_MODEL_AMBIENT, sv->lmodel_ambient);
2042                      if (!list) list = SUMA_CreateList();
2043                      SUMA_REGISTER_HEAD_COMMAND_NO_DATA( list, SE_Redisplay,
2044                                                          SES_Suma, sv);
2045 
2046                      if (!SUMA_Engine (&list)) {
2047                         fprintf(stderr,
2048                                 "Error SUMA_input: SUMA_Engine call failed.\n");
2049                      }
2050                } else if ((SUMA_AALT_KEY(key))){
2051 
2052                } else {
2053                   SUMA_PROMPT_DIALOG_STRUCT *prmpt;
2054                   prmpt = SUMA_CreatePromptDialogStruct (
2055                                  SUMA_OK_APPLY_CLEAR_CANCEL,
2056                                  "X,Y,Z coordinates of light0:",
2057                                  "",
2058                                  sv->X->TOPLEVEL, NOPE,
2059                                  SUMA_APPLY_BUTTON,
2060                                  SUMA_SetLight0, (void *)sv,
2061                                  NULL, NULL,
2062                                  NULL, NULL,
2063                                  SUMA_CleanNumString, (void*)3,
2064                                  NULL);
2065 
2066                   prmpt = SUMA_CreatePromptDialog(sv->X->Title, prmpt);
2067                }
2068 
2069          break;
2070       default:
2071          SUMA_S_Err("Il ne faut pas etre ici");
2072          SUMA_RETURN(0);
2073          break;
2074    }
2075 
2076    SUMA_RETURN(1);
2077 }
2078 
SUMA_M_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)2079 int SUMA_M_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
2080 {
2081    static char FuncName[]={"SUMA_M_Key"};
2082    char tk[]={"M"}, keyname[100];
2083    int k, nc;
2084    SUMA_Boolean LocalHead = NOPE;
2085 
2086    SUMA_ENTRY;
2087 
2088    SUMA_KEY_COMMON;
2089 
2090    /* do the work */
2091    switch (k) {
2092       case XK_m:
2093          if ((SUMA_CTRL_KEY(key))) {
2094             SUMA_SurfaceObject *SO;
2095             float fv3[3];
2096             int it;
2097             fprintf(SUMA_STDOUT, "%s: Enter shift in mm [RAI] "
2098                                  "to apply to all mappable surfaces in DOv.\n",
2099                                  FuncName);
2100             it = SUMA_ReadNumStdin (fv3, 3);
2101             if (it > 0 && it < 3) {
2102                fprintf(SUMA_STDERR,"Error %s: read %d values, expected 3.\n",
2103                                     FuncName, it);
2104                SUMA_RETURN(0);
2105             }else if (it < 0) {
2106                fprintf(SUMA_STDERR,"Error %s: Error in SUMA_ReadNumStdin.\n",
2107                                     FuncName);
2108                SUMA_RETURN(0);
2109             }else if (it == 0) {
2110                fprintf(SUMA_STDERR,"%s: Nothing read.\n", FuncName);
2111                SUMA_RETURN(0);
2112             }
2113 
2114             for (it = 0; it < SUMAg_N_DOv; ++it) {
2115                if (SUMA_isSO_G (SUMAg_DOv[it], sv->CurGroupName)) {
2116                   SO = (SUMA_SurfaceObject *)SUMAg_DOv[it].OP;
2117                   if (SUMA_isLocalDomainParent(SO)) {
2118                      int imax, ii;
2119                      /* add the shift */
2120                      fprintf (SUMA_STDERR,
2121                               "%s: Shifting %s by %f %f %f mm RAI.\n",
2122                               FuncName, SO->Label, fv3[0], fv3[1], fv3[2]);
2123                      ii = 0;
2124                      imax = 3 * SO->N_Node;
2125                      while (ii < imax) {
2126                         SO->NodeList[ii] += fv3[0]; ++ii;
2127                         SO->NodeList[ii] += fv3[1]; ++ii;
2128                         SO->NodeList[ii] += fv3[2]; ++ii;
2129                      }
2130                   }
2131                }
2132             }
2133 
2134             SUMA_postRedisplay(sv->X->GLXAREA, NULL, NULL);
2135          } else {
2136             sv->GVS[sv->StdView].ApplyMomentum =
2137                                  !sv->GVS[sv->StdView].ApplyMomentum;
2138             SUMA_UpdateViewerTitle(sv);
2139             if (sv->GVS[sv->StdView].ApplyMomentum) {
2140                 sv->X->MOMENTUMID = XtAppAddTimeOut(
2141                                        SUMAg_CF->X->App, 1,
2142                                        SUMA_momentum,
2143                                        (XtPointer) sv->X->GLXAREA);
2144                 /* wait till user initiates turning */
2145                sv->GVS[sv->StdView].spinDeltaX = 0;
2146                sv->GVS[sv->StdView].spinDeltaY = 0;
2147                sv->GVS[sv->StdView].translateDeltaX = 0;
2148                sv->GVS[sv->StdView].translateDeltaY = 0;
2149             } else {
2150                if (sv->X->MOMENTUMID)  {
2151                   XtRemoveTimeOut(sv->X->MOMENTUMID);
2152                   sv->X->MOMENTUMID = 0;
2153                }
2154             }
2155          }
2156          break;
2157       case XK_M:
2158          if (SUMA_CTRL_KEY(key) && (SUMA_ALT_KEY(key) || SUMA_APPLE_KEY(key)) ) {
2159             #ifdef ALLOW_MCW_MALLOC
2160             /* write memtrace results to disk */
2161             if (!mcw_malloc_enabled()) {
2162                if (callmode && strcmp(callmode, "interactive") == 0) {
2163                   SUMA_SLP_Warn("Memory tracing\n"
2164                                "is not enabled.\n"
2165                                "Use Help-->MemTrace.");
2166                } else {
2167                   SUMA_S_Warn("Memory tracing\n"
2168                                "is not enabled.\n"
2169                                "Use Help-->MemTrace.");
2170                }
2171                SUMA_RETURN(0);
2172             } else {
2173                if (callmode && strcmp(callmode, "interactive") == 0) {
2174                   SUMA_SLP_Note("Dumping memory tracing\n"
2175                                "to latest ./malldump.???\n"
2176                                "file (if possible).");
2177                } else {
2178                   SUMA_S_Note("Dumping memory tracing\n"
2179                                "to latest ./malldump.???\n"
2180                                "file (if possible).");
2181                }
2182                mcw_malloc_dump_sort(1);
2183             }
2184             #else
2185                if (callmode && strcmp(callmode, "interactive") == 0) {
2186                   SUMA_SLP_Warn("Sorry, memory tracing\n"
2187                              "was not enabled at compile.\n"
2188                              "time. You are out of luck\n"
2189                              "if using SUN.");
2190                } else {
2191                   SUMA_S_Warn("Sorry, memory tracing\n"
2192                              "was not enabled at compile.\n"
2193                              "time. You are out of luck\n"
2194                              "if using SUN.");
2195                }
2196                SUMA_RETURN(0);
2197             #endif
2198          }
2199          break;
2200       default:
2201          SUMA_S_Err("Il ne faut pas etre ici");
2202          SUMA_RETURN(0);
2203          break;
2204    }
2205 
2206    SUMA_RETURN(1);
2207 }
2208 
SUMA_N_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)2209 int SUMA_N_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
2210 {
2211    static char FuncName[]={"SUMA_N_Key"};
2212    char tk[]={"N"}, keyname[100];
2213    int k, nc, it;
2214    float fv15[15];
2215    SUMA_EngineData *ED = NULL;
2216    DList *list = NULL;
2217    DListElmt *NextElm= NULL;
2218    SUMA_Boolean LocalHead = NOPE;
2219 
2220    SUMA_ENTRY;
2221 
2222    SUMA_KEY_COMMON;
2223 
2224    /* do the work */
2225    switch (k) {
2226       case XK_N:
2227          break;
2228       case XK_n:
2229          if (SUMA_CTRL_KEY(key)) {
2230             SUMA_LH("Opening a new controller...");
2231             /* open a new controller */
2232             if (!SUMA_X_SurfaceViewer_Create ()) {
2233                SUMA_S_Err("Failed in SUMA_X_SurfaceViewer_Create.");
2234                SUMA_RETURN(0);
2235             }
2236          } else {
2237             fprintf(stdout,"BAD IDEA Enter XYZ of center followed by size of Box (enter nothing to cancel):\n");
2238             it = SUMA_ReadNumStdin (fv15, 6);
2239             if (it > 0 && it < 6) {
2240                fprintf(SUMA_STDERR,"Error %s: read %d values, expected 6.\n", FuncName, it);
2241                SUMA_RETURN(0);
2242             }else if (it < 0) {
2243                fprintf(SUMA_STDERR,"Error %s: Error in SUMA_ReadNumStdin.\n", FuncName);
2244                SUMA_RETURN(0);
2245             }else if (it == 0) {
2246                SUMA_RETURN(0);
2247             }
2248 
2249             fprintf (SUMA_STDOUT, "Parsed Input:\n\tCenter %f, %f, %f.\n\tBox Size %f, %f, %f\n", \
2250                fv15[0], fv15[1],fv15[2],\
2251                fv15[3], fv15[4],fv15[5]);
2252 
2253             /* register fv15 with ED */
2254             if (!list) list = SUMA_CreateList ();
2255             ED = SUMA_InitializeEngineListData (SE_GetNearestNode);
2256             if (!SUMA_RegisterEngineListCommand (  list, ED,
2257                                                    SEF_fv15, (void *)fv15,
2258                                                    SES_Suma, (void *)sv, NOPE,
2259                                                    SEI_Head, NULL )) {
2260                fprintf(SUMA_STDERR,"Error %s: Failed to register command\n", FuncName);
2261                break;
2262             }
2263 
2264             SUMA_REGISTER_HEAD_COMMAND_NO_DATA(list, SE_Redisplay, SES_Suma, sv);
2265             if (!SUMA_Engine (&list)) {
2266                fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
2267             }
2268          }
2269          break;
2270       default:
2271          SUMA_S_Err("Il ne faut pas etre ici");
2272          SUMA_RETURN(0);
2273          break;
2274    }
2275 
2276    SUMA_RETURN(1);
2277 }
2278 
2279 /*!
2280    Execute commands when O or o is pressed
2281 */
SUMA_O_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)2282 int SUMA_O_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
2283 {
2284    static char FuncName[]={"SUMA_O_Key"};
2285    char tk[]={"O"}, keyname[100];
2286    int k, nc;
2287    int N_SOlist, SOlist[SUMA_MAX_DISPLAYABLE_OBJECTS];
2288    SUMA_ALL_DO *ado=NULL;
2289    SUMA_SurfaceObject *SO = NULL;
2290 
2291    SUMA_Boolean LocalHead = NOPE;
2292 
2293    SUMA_ENTRY;
2294 
2295    SUMA_KEY_COMMON;
2296 
2297    /* do the work */
2298    switch (k) {
2299       case XK_O:
2300          if ((SUMA_APPLE_KEY(key) || SUMA_ALT_KEY(key))) {
2301 
2302          } else if (SUMA_CTRL_KEY(key)) {
2303             if ((ado = SUMA_SV_Focus_ADO(sv))) {
2304                SUMA_Set_ADO_TransMode(ado, sv->TransMode,
2305                                       SUMAg_CF->TransModeStep, 1);
2306                SUMA_postRedisplay(sv->X->GLXAREA, NULL, NULL);
2307             }
2308          } else {
2309             sv->TransMode = ((sv->TransMode-SUMAg_CF->TransModeStep) %
2310                                                       (STM_N_TransModes-2));
2311             if (sv->TransMode <= STM_ViewerDefault) sv->TransMode = STM_16;
2312 
2313             SUMA_postRedisplay(sv->X->GLXAREA, NULL, NULL);
2314          }
2315          break;
2316       case XK_o:
2317          if ((SUMA_APPLE_KEY(key) || SUMA_ALT_KEY(key))) {
2318            sv->X->SetRot_prmpt = SUMA_CreatePromptDialogStruct (
2319                   SUMA_OK_APPLY_CLEAR_CANCEL, "Center of Rotation X,Y,Z:",
2320                   "0,0,0",
2321                   sv->X->TOPLEVEL, YUP,
2322                   SUMA_APPLY_BUTTON,
2323                   SUMA_SetRotCenter, (void *)sv,
2324                   NULL, NULL,
2325                   NULL, NULL,
2326                   NULL, NULL,
2327                   sv->X->SetRot_prmpt);
2328 
2329             sv->X->SetRot_prmpt = SUMA_CreatePromptDialog(sv->X->Title,
2330                                                           sv->X->SetRot_prmpt);
2331          } else if (SUMA_CTRL_KEY(key)) {
2332             if ((ado = SUMA_SV_Focus_ADO(sv))) {
2333                SUMA_Set_ADO_TransMode(ado, sv->TransMode,
2334                                       -SUMAg_CF->TransModeStep, 1);
2335                SUMA_postRedisplay(sv->X->GLXAREA, NULL, NULL);
2336             }
2337          } else {
2338             sv->TransMode = ((sv->TransMode+SUMAg_CF->TransModeStep) %
2339                                                       (STM_N_TransModes-2));
2340             if (sv->TransMode <= STM_ViewerDefault) sv->TransMode = STM_0;
2341 
2342             SUMA_postRedisplay(sv->X->GLXAREA, NULL, NULL);
2343          }
2344          break;
2345       default:
2346          SUMA_S_Err("Il ne faut pas etre ici");
2347          SUMA_RETURN(0);
2348          break;
2349    }
2350 
2351    SUMA_RETURN(1);
2352 }
2353 
2354 /*!
2355    Execute commands when P or p is pressed
2356 */
SUMA_P_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)2357 int SUMA_P_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
2358 {
2359    static char FuncName[]={"SUMA_P_Key"};
2360    char tk[]={"P"}, keyname[100], msg[100];
2361    int k, nc;
2362    int N_SOlist, SOlist[SUMA_MAX_DISPLAYABLE_OBJECTS];
2363    SUMA_SurfaceObject *SO = NULL;
2364    SUMA_ALL_DO *ado=NULL;
2365    SUMA_Boolean LocalHead = NOPE;
2366 
2367    SUMA_ENTRY;
2368 
2369    SUMA_KEY_COMMON;
2370 
2371    /* do the work */
2372    switch (k) {
2373       case XK_P:
2374          if ((SUMA_APPLE_KEY(key) || SUMA_ALT_KEY(key))) {
2375          } else if (SUMA_CTRL_KEY(key)) {
2376 
2377          } else {
2378             sv->PolyMode = SRM_Fill;
2379             N_SOlist = SUMA_RegisteredSOs(sv, SUMAg_DOv, SOlist);
2380             for (k=0; k<N_SOlist; ++k) {
2381                SO = (SUMA_SurfaceObject *)(SUMAg_DOv[SOlist[k]].OP);
2382                SO->PolyMode = SRM_ViewerDefault;
2383                SO->Show = YUP;
2384             }
2385             SUMA_SET_GL_RENDER_MODE(sv->PolyMode);
2386             SUMA_postRedisplay(sv->X->GLXAREA, NULL, NULL);
2387             SUMA_SLP_Note("All surfaces displayed as solids");
2388          }
2389          break;
2390       case XK_p:
2391          if ((SUMA_APPLE_KEY(key) || SUMA_ALT_KEY(key))) {
2392             sv->DO_DrawMask = ((sv->DO_DrawMask+1) % SDODM_N_DO_DrawMasks);
2393             snprintf(msg,100*sizeof(char),"DO DrawMask now set to: %s",
2394                         SUMA_DO_DrawMaskCode2Name_human(sv->DO_DrawMask));
2395             if (callmode && strcmp(callmode, "interactive") == 0) {
2396                   SUMA_SLP_Note ("%s",msg);
2397             } else { SUMA_S_Note ("%s",msg); }
2398          } else if (SUMA_CTRL_KEY(key)) {
2399             if ((ado = SUMA_SV_Focus_ADO(sv))) {
2400                SUMA_Set_ADO_RenderMode(ado, sv->PolyMode, -1, 1);
2401             }
2402          } else {
2403             sv->PolyMode = ((sv->PolyMode+1) % SRM_N_RenderModes);
2404             if (sv->PolyMode <= SRM_ViewerDefault) sv->PolyMode = SRM_Fill;
2405 
2406             SUMA_SET_GL_RENDER_MODE(sv->PolyMode);
2407          }
2408          SUMA_postRedisplay(sv->X->GLXAREA, NULL, NULL);
2409          break;
2410       default:
2411          SUMA_S_Err("Il ne faut pas etre ici");
2412          SUMA_RETURN(0);
2413          break;
2414    }
2415 
2416    SUMA_RETURN(1);
2417 }
2418 
2419 /*!
2420    Execute commands when R or r is pressed
2421 */
SUMA_R_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)2422 int SUMA_R_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
2423 {
2424    static char FuncName[]={"SUMA_R_Key"};
2425    char tk[]={"R"}, keyname[100], msg[100];
2426    int k, nc, ii, jj, mm;
2427    SUMA_Boolean LocalHead = NOPE;
2428 
2429    SUMA_ENTRY;
2430 
2431    SUMA_KEY_COMMON;
2432 
2433    /* do the work */
2434    switch (k) {
2435       case XK_r:
2436          if ((SUMA_APPLE_KEY(key) || SUMA_ALT_KEY(key))) {
2437             SUMAg_CF->SUMA_SnapshotOverSampling =
2438                   (SUMAg_CF->SUMA_SnapshotOverSampling +1)%5;
2439             if (SUMAg_CF->SUMA_SnapshotOverSampling == 0)
2440                      SUMAg_CF->SUMA_SnapshotOverSampling = 1;
2441             {
2442                sprintf(msg,"Oversampling now set to %d",
2443                            SUMAg_CF->SUMA_SnapshotOverSampling);
2444                if (callmode && strcmp(callmode, "interactive") == 0) {
2445                   SUMA_SLP_Note ("%s",msg);
2446                } else { SUMA_S_Note ("%s",msg); }
2447             }
2448          } else if (SUMA_CTRL_KEY(key)) {
2449             #if 0
2450             sv->X->SetRot_prmpt = SUMA_CreatePromptDialogStruct (
2451                   SUMA_OK_APPLY_CLEAR_CANCEL, "Center of Rotation X,Y,Z:",
2452                   "0,0,0",
2453                   sv->X->TOPLEVEL, YUP,
2454                   SUMA_APPLY_BUTTON,
2455                   SUMA_SetRotCenter, (void *)sv,
2456                   NULL, NULL,
2457                   NULL, NULL,
2458                   NULL, NULL,
2459                   sv->X->SetRot_prmpt);
2460 
2461             sv->X->SetRot_prmpt = SUMA_CreatePromptDialog(sv->X->Title,
2462                                                           sv->X->SetRot_prmpt);
2463             #else
2464             /* save image to disk */
2465             SUMA_SnapToDisk(sv,1, 0);
2466             #endif
2467          } else {
2468             GLvoid *pixels=NULL;
2469             double rat;
2470             int oareah=-1,oareaw=-1, owindh=-1, owindw=-1;
2471             /* Control for GL_MAX_VIEWPORT_DIMS */
2472             if (SUMAg_CF->SUMA_SnapshotOverSampling > 1) {
2473                glGetIntegerv(GL_MAX_VIEWPORT_DIMS,&k);
2474                mm = SUMA_MAX_PAIR(
2475                      SUMAg_CF->SUMA_SnapshotOverSampling*sv->X->aHEIGHT,
2476                      SUMAg_CF->SUMA_SnapshotOverSampling*sv->X->aWIDTH);
2477                if (mm > k) { /* too big, find best new dimesnions */
2478                   rat = (double)mm/(double)k;
2479                      /*window shrinking factor to allow for stitching*/
2480                   SUMA_S_Notev(  "%d/%d (H/W) Too big for oversampling\n"
2481                                  " reducing resolution by %f.\n",
2482                                  sv->X->aHEIGHT, sv->X->aWIDTH, rat);
2483                   /* store original size */
2484                   oareaw = sv->X->aWIDTH; oareah = sv->X->aHEIGHT;
2485                   owindw = sv->wWindWidth; owindh = sv->wWindHeight;
2486                   sv->X->aHEIGHT =
2487                      (int)((double)sv->X->aHEIGHT/rat)-1;
2488                   sv->X->aWIDTH =
2489                      (int)((double)sv->X->aWIDTH/rat)-1;
2490                   SUMA_SV_WindDims_From_DrawAreaDims(sv);
2491                   SUMA_WidgetResize (sv->X->TOPLEVEL ,
2492                                      sv->wWindWidth, sv->wWindHeight);
2493                   sv->rdc = SUMA_RDC_X_RESIZE;
2494                   glViewport( 0, 0,
2495                                  sv->X->aWIDTH, sv->X->aHEIGHT);
2496                   SUMA_handleRedisplay((XtPointer)sv->X->GLXAREA);
2497                } else {
2498                   SUMA_S_Note("Size OK");
2499                }
2500             }
2501             /* turn off checking for duplicates */
2502             for (jj=0; jj<SUMAg_CF->SUMA_SnapshotOverSampling; ++jj) {
2503                for (ii=0; ii<SUMAg_CF->SUMA_SnapshotOverSampling; ++ii) {
2504                   if (SUMAg_CF->SUMA_SnapshotOverSampling > 1) {
2505                      glGetIntegerv(GL_MAX_VIEWPORT_DIMS,&k);
2506                      if (ii==0 && jj == 0) {
2507                         SUMA_S_Notev(  "Resampling factor of %d\n"
2508                                     "If using this feature, the\n"
2509                                     " sequence of %d images is saved\n"
2510                                     " temporarily to disk and '2dcat'\n"
2511                                     " is then used to put the images together.\n"
2512                                     "(Have ViewPort GL_MAX_VIEWPORT_DIMS of %d\n"
2513                                     " and max dims needed of %d.)\n",
2514                            SUMAg_CF->SUMA_SnapshotOverSampling,
2515                            SUMAg_CF->SUMA_SnapshotOverSampling *
2516                               SUMAg_CF->SUMA_SnapshotOverSampling,
2517                            k,
2518                            SUMA_MAX_PAIR( SUMAg_CF->SUMA_SnapshotOverSampling *
2519                               sv->X->aHEIGHT,
2520                            SUMAg_CF->SUMA_SnapshotOverSampling*sv->X->aWIDTH)  );
2521                      } else {
2522                         /* sometimes you have repeated black areas when
2523                         oversampling, allow that after very first 'tant' */
2524                         SNAP_OkDuplicates();
2525                      }
2526                      /* start from top left, move to right then go down
2527                         one row (Row Major, starting on top left ) */
2528                      glViewport(-ii*sv->X->aWIDTH,
2529                                 -(SUMAg_CF->SUMA_SnapshotOverSampling - jj - 1) *
2530                                   sv->X->aHEIGHT,
2531                                SUMAg_CF->SUMA_SnapshotOverSampling*sv->X->aWIDTH,
2532                                 SUMAg_CF->SUMA_SnapshotOverSampling *
2533                                  sv->X->aHEIGHT);
2534                      SUMA_handleRedisplay((XtPointer)sv->X->GLXAREA);
2535                      #if 0 /* problem should be fixed by SUMA_grabRenderedPixels
2536                               Throw section out if no new problems arise.
2537                               Search for KILL_DOUBLE_RENDERING to locate
2538                               other chunks for removal
2539                                        ZSS Feb 2012 */
2540                         if (1) {
2541                            /* seems to fix an problem with snapping the older
2542                            image... at least on mac
2543                            None of glFlush(); glFinish();glXWaitGL();glXWaitX();
2544                            or NI_sleep did the trick
2545                            Perhaps the wrong buffer is being grabbed?
2546                            Check SUMA_grabPixels ...
2547                                  ZSS Feb 2012.
2548                            Yes it was,  SUMA_grabRenderedPixels does the trick.
2549                                  ZSS Feb the next morning 2012 */
2550                            SUMA_handleRedisplay((XtPointer)sv->X->GLXAREA);
2551                         }
2552                      #endif
2553                   } else {
2554                      #if 0 /* Search for KILL_DOUBLE_RENDERING to locate
2555                               other chunks for removal
2556                                        ZSS Feb 2012 */
2557                   /* ZSS   Nov 20 2009
2558                      If you do not redisplay here, you could strange cases of
2559                      snapping the previous frame as reported by Colm Connolly.
2560 
2561                   1. suma -spec N27_both_tlrc.spec -sv TT_N27+tlrc. &
2562                   2. press F2 five times to cycle through the various axes
2563                      from none to all and back to none.
2564                   3. press r to record
2565 
2566                   The first image recorded has axes present even though none
2567                   are present in the viewer. Pressing r again produces an
2568                   image with no axes as expected.
2569 
2570                   Actually, it seems this happens in many other cases, F1, F6,
2571                   change state, etc.
2572 
2573                   This seems to be the same problem reported by Chunmao W.
2574                   a while back.
2575                   Same happens with R option.
2576 
2577                   Problem only happens under DARWIN it seems.
2578 
2579                   I do not know why the call to SUMA_handleRedisplay does the
2580                   trick. Perhaps it is a buffer reading problem in double
2581                   buffer rendering. The fix is ugly, especially in continuous
2582                   record mode (see SUMA_display function in 'if(csv->record)'
2583                   block), but it works.
2584                   */
2585                         #ifdef DARWIN
2586                         SUMA_handleRedisplay((XtPointer)sv->X->GLXAREA);
2587                         #endif
2588                      #endif
2589                   }
2590                   pixels = SUMA_grabRenderedPixels(sv, 3,
2591                                        sv->X->aWIDTH, sv->X->aHEIGHT, 0);
2592                   if (pixels) {
2593                     ISQ_snapsave (sv->X->aWIDTH, -sv->X->aHEIGHT,
2594                                   (unsigned char *)pixels, sv->X->GLXAREA );
2595                     SUMA_free(pixels);
2596                   }else {
2597                      if (callmode && strcmp(callmode, "interactive") == 0) {
2598                         SUMA_SLP_Err("Failed to record image.");
2599                      } else { SUMA_S_Err("Failed to record image.");}
2600                   }
2601                }
2602             }
2603             if (SUMAg_CF->SUMA_SnapshotOverSampling > 1) {
2604                         /* Now return the window to its previous size */
2605                if (owindw > 0) {
2606                   sv->wWindHeight = owindh;
2607                   sv->X->aHEIGHT = oareah;
2608                   sv->wWindWidth = owindw;
2609                   sv->X->aWIDTH = oareaw;
2610                   SUMA_WidgetResize (sv->X->TOPLEVEL , owindw, owindh);
2611                }
2612                sv->rdc = SUMA_RDC_X_RESIZE;
2613                glViewport( 0, 0,
2614                            sv->X->aWIDTH, sv->X->aHEIGHT);
2615                SUMA_handleRedisplay((XtPointer)sv->X->GLXAREA);
2616             }
2617             if (SUMAg_CF->NoDuplicatesInRecorder) SNAP_NoDuplicates();
2618             else SNAP_OkDuplicates();
2619             if (SUMAg_CF->SUMA_SnapshotOverSampling > 1) {
2620                /* record the image to make life easy on user */
2621                sprintf(msg,"Writing resultant image\n"
2622                            " to HighRes_Suma_tmp.ppm ...");
2623                if (callmode && strcmp(callmode, "interactive") == 0) {
2624                   SUMA_SLP_Note ("%s",msg);
2625                } else { SUMA_S_Note ("%s",msg); }
2626                ISQ_snap_png_rng("HighRes_Photo___tmp",
2627                                 -(SUMAg_CF->SUMA_SnapshotOverSampling *
2628                                   SUMAg_CF->SUMA_SnapshotOverSampling),
2629                                 0);
2630                /* use explicit tcsh to avoid sh syntax  25 Apr 2017 [rickr] */
2631                system(  "tcsh -c 'rm -f HighRes_Suma_tmp* >& /dev/null' ; "
2632                         "2dcat -prefix HighRes_Suma_tmp HighRes_Photo___tmp* ; "
2633                         "rm -f HighRes_Photo___tmp*");
2634             }
2635          }
2636          break;
2637       case XK_R:
2638          if (SUMA_CTRL_KEY(key)) {
2639             char sbuf[256];
2640             sv->Record = !sv->Record;
2641             if (sv->Record) sv->Record = 2;
2642             if (sv->Record) {
2643                SUMA_VALIDATE_RECORD_PATH(SUMAg_CF->autorecord);
2644                snprintf(sbuf,256*sizeof(char),
2645                         "Disk Recording ON to: %s%s*",
2646                            SUMAg_CF->autorecord->Path,
2647                            SUMAg_CF->autorecord->FileName_NoExt);
2648                if (callmode && strcmp(callmode, "interactive") == 0) {
2649                   SUMA_SLP_Note ("%s",sbuf); }
2650                else { SUMA_S_Note ("%s",sbuf); }
2651             } else {
2652                snprintf(sbuf,256*sizeof(char),
2653                         "Disk Recording OFF. Results in: %s%s*",
2654                            SUMAg_CF->autorecord->Path,
2655                            SUMAg_CF->autorecord->FileName_NoExt);
2656                if (callmode && strcmp(callmode, "interactive") == 0) {
2657                   SUMA_SLP_Note ("%s",sbuf); }
2658                else { SUMA_S_Note ("%s",sbuf);}
2659             }
2660             SUMA_UpdateViewerTitle(sv);
2661          } else {
2662             sv->Record = !sv->Record;
2663             if (sv->Record) {
2664                if (callmode && strcmp(callmode, "interactive") == 0) {
2665                   SUMA_SLP_Note ("Recording ON"); }
2666                else { SUMA_S_Note ("Recording ON"); }
2667             } else {
2668                if (callmode && strcmp(callmode, "interactive") == 0) {
2669                   SUMA_SLP_Note ("Recording OFF"); }
2670                else { SUMA_S_Note ("Recording OFF");}
2671             }
2672             SUMA_UpdateViewerTitle(sv);
2673          }
2674          break;
2675       default:
2676          SUMA_S_Err("Il ne faut pas etre ici");
2677          SUMA_RETURN(0);
2678          break;
2679    }
2680 
2681    SUMA_RETURN(1);
2682 }
2683 
SUMA_T_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)2684 int SUMA_T_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
2685 {
2686    static char FuncName[]={"SUMA_T_Key"};
2687    char tk[]={"T"}, keyname[100];
2688    int k, nc;
2689    SUMA_EngineData *ED = NULL;
2690    DList *list = NULL;
2691    DListElmt *NextElm= NULL;
2692    SUMA_Boolean LocalHead = NOPE;
2693 
2694    SUMA_ENTRY;
2695 
2696    SUMA_KEY_COMMON;
2697 
2698    /* do the work */
2699    switch (k) {
2700       case XK_T:
2701          if (!list) list = SUMA_CreateList();
2702             SUMA_REGISTER_HEAD_COMMAND_NO_DATA(list,
2703                                     SE_StartListening, SES_Suma, sv);
2704 
2705          if (!SUMA_Engine (&list)) {
2706                fprintf(SUMA_STDERR,
2707                         "Error %s: SUMA_Engine call failed.\n", FuncName);
2708          }
2709          break;
2710       case XK_t:
2711          if ((SUMA_CTRL_KEY(key))){
2712                if (callmode && strcmp(callmode, "interactive") == 0) {
2713                      SUMA_SLP_Note("Forcing a resend of Surfaces to Afni...");}
2714                else { SUMA_S_Note("Forcing a resend of Surfaces to Afni..."); }
2715                if (!list) list = SUMA_CreateList();
2716                SUMA_REGISTER_HEAD_COMMAND_NO_DATA(list,
2717                         SE_SetForceAfniSurf, SES_Suma, sv);
2718 
2719                if (!SUMA_Engine (&list)) {
2720                   fprintf(SUMA_STDERR,
2721                            "Error %s: SUMA_Engine call failed.\n", FuncName);
2722                }
2723          } else {
2724             if (!list) list = SUMA_CreateList();
2725             SUMA_REGISTER_HEAD_COMMAND_NO_DATA(list,
2726                            SE_ToggleConnected, SES_Suma, sv);
2727 
2728             if (!SUMA_Engine (&list)) {
2729                   fprintf(SUMA_STDERR,
2730                            "Error %s: SUMA_Engine call failed.\n", FuncName);
2731             }
2732          }
2733          break;
2734       default:
2735          SUMA_S_Err("Il ne faut pas ci dessous");
2736          SUMA_RETURN(0);
2737          break;
2738    }
2739 
2740    SUMA_RETURN(1);
2741 }
2742 
2743 /*!
2744    Execute commands when U or u is pressed
2745 */
SUMA_U_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)2746 int SUMA_U_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
2747 {
2748    static char FuncName[]={"SUMA_U_Key"};
2749    char tk[]={"U"}, keyname[100], msg[100];
2750    int k, nc, ii, jj, mm;
2751    SUMA_Boolean LocalHead = NOPE;
2752 
2753    SUMA_ENTRY;
2754 
2755    SUMA_KEY_COMMON;
2756 
2757    /* do the work */
2758    switch (k) {
2759       case XK_u:
2760          if ((SUMA_APPLE_KEY(key) || SUMA_ALT_KEY(key))) {
2761             SUMA_LH("Nothing here");
2762          } else if (SUMA_CTRL_KEY(key)) {
2763             SUMA_viewSumaCont(1);
2764          } else {
2765             SUMA_LH("Keeping it real");
2766          }
2767          break;
2768       case XK_U:
2769          if (SUMA_CTRL_KEY(key)) {
2770 
2771          } else {
2772 
2773          }
2774          break;
2775       default:
2776          SUMA_S_Err("Il ne faut pas etre ici non plus");
2777          SUMA_RETURN(0);
2778          break;
2779    }
2780 
2781    SUMA_RETURN(1);
2782 }
2783 
2784 
SUMA_free_Save_List_El(void * selu)2785 void SUMA_free_Save_List_El(void *selu) {
2786    SUMA_SAVE_LIST_EL *sel=(SUMA_SAVE_LIST_EL *)selu;
2787    if (sel) {
2788       if (sel->identifier) SUMA_free(sel->identifier);
2789       if (sel->prefix) SUMA_free(sel->prefix);
2790       if (sel->type) SUMA_free(sel->type);
2791       SUMA_free(sel);
2792    }
2793    return;
2794 }
2795 
SUMA_Add_to_SaveList(DList ** SLp,char * type,char * identifier,char * prefix)2796 int SUMA_Add_to_SaveList(DList **SLp, char *type,
2797                          char *identifier, char *prefix)
2798 {
2799    static char FuncName[]={"SUMA_Add_to_SaveList"};
2800    DList *SL=NULL;
2801    DListElmt *el= NULL;
2802    SUMA_SAVE_LIST_EL *sel=NULL;
2803    SUMA_Boolean LocalHead = NOPE;
2804 
2805    SUMA_ENTRY;
2806 
2807    if (!SLp || !type || !identifier || !prefix) SUMA_RETURN(0);
2808    SL = *SLp;
2809    if (!SL) {
2810       SL = (DList*)SUMA_malloc(sizeof(DList));
2811       dlist_init(SL, SUMA_free_Save_List_El);
2812    }
2813    SUMA_LH("Searching for possible identifier >%s<",
2814             identifier?identifier:"NULL");
2815    /* first make sure identifier is not there already */
2816    el = dlist_head(SL);
2817    while (el && identifier) {
2818       if ((sel = (SUMA_SAVE_LIST_EL *)el->data)) {
2819          if (sel->identifier &&
2820              !strcmp(sel->identifier, identifier)) {
2821             /* found, replace */
2822             SUMA_free(sel->identifier);
2823             sel->identifier = SUMA_copy_string(identifier);
2824                   identifier = NULL;
2825             SUMA_free(sel->prefix);
2826             sel->prefix = SUMA_copy_string(prefix);
2827                   prefix = NULL;
2828             SUMA_free(sel->type);
2829             sel->type = SUMA_copy_string(type);
2830                   type = NULL;
2831          }
2832       }
2833       el = dlist_next(el);
2834    }
2835    if (identifier) { /* a new one, should add it */
2836       sel = (SUMA_SAVE_LIST_EL *)SUMA_calloc(1,sizeof(SUMA_SAVE_LIST_EL));
2837       sel->identifier = SUMA_copy_string(identifier);
2838       sel->prefix = SUMA_copy_string(prefix);
2839       sel->type =  SUMA_copy_string(type);
2840       dlist_ins_next(SL, dlist_tail(SL), (void *)sel);
2841    }
2842 
2843    if (LocalHead) {
2844       SUMA_Show_SaveList(SL, "SaveList now:\n");
2845    }
2846 
2847    *SLp = SL;
2848    SUMA_RETURN(1);
2849 }
2850 
SUMA_Show_SaveList(DList * SL,char * head)2851 void SUMA_Show_SaveList(DList *SL, char *head)
2852 {
2853    static char FuncName[]={"SUMA_Show_SaveList"};
2854    FILE *out=NULL;
2855    DListElmt *el= NULL;
2856    SUMA_SAVE_LIST_EL *sel=NULL;
2857    int cnt = 0;
2858 
2859    SUMA_ENTRY;
2860 
2861    if (!out) out = stderr;
2862    if (head) { fprintf(out, "%s", head); }
2863    if (!SL) { fprintf(out,"NULL SaveList\n"); SUMA_RETURNe; }
2864 
2865    el = dlist_head(SL);
2866    cnt = 0;
2867    while (el) {
2868       if ((sel = (SUMA_SAVE_LIST_EL *)el->data)) {
2869          fprintf(out,"   %d:     id>%s<, prefix>%s<, type>%s<\n",
2870                         cnt, sel->identifier, sel->prefix, sel->type);
2871       } else {
2872          fprintf(out,"   %d:     NULL sel\n", cnt);
2873       }
2874       el = dlist_next(el);
2875       fprintf(out,"\n");
2876    }
2877 
2878    SUMA_RETURNe;
2879 }
2880 
SUMA_SaveSaveListElement(SUMA_SAVE_LIST_EL * sel)2881 int SUMA_SaveSaveListElement(SUMA_SAVE_LIST_EL *sel)
2882 {
2883    static char FuncName[]={"SUMA_SaveSaveListElement"};
2884    SUMA_DSET *dset=NULL;
2885    char *oname=NULL, *idtype=NULL;
2886    int nid=0;
2887    SUMA_ENTRY;
2888 
2889    if (!sel || !sel->identifier || !sel->prefix || !sel->type) SUMA_RETURN(0);
2890 
2891    if (!strcmp(sel->type,"sdset")) {
2892       idtype="label:"; nid = strlen(idtype);
2893       if (!strncmp(idtype, sel->identifier, nid)) {
2894          if (!(dset = SUMA_FindDset2_s(sel->identifier+nid,
2895                                  SUMAg_CF->DsetList, "label"))) {
2896             SUMA_S_Errv("Failed to find dset labeled %s\n", sel->identifier+nid);
2897             SUMA_RETURN(0);
2898          }
2899          goto SAVEDSET;
2900       }
2901       idtype="filename:"; nid = strlen(idtype);
2902       if (!strncmp(idtype, sel->identifier, nid)) {
2903          if (!(dset = SUMA_FindDset2_s(sel->identifier+nid,
2904                                  SUMAg_CF->DsetList, "filename"))) {
2905             SUMA_S_Errv("Failed to find dset with filename %s\n",
2906                         sel->identifier+nid);
2907             SUMA_RETURN(0);
2908          }
2909          goto SAVEDSET;
2910       }
2911       idtype="self_idcode:"; nid = strlen(idtype);
2912       if (!strncmp(idtype, sel->identifier, nid)) {
2913          if (!(dset = SUMA_FindDset2_s(sel->identifier+nid,
2914                                  SUMAg_CF->DsetList, "self_idcode"))) {
2915             SUMA_S_Errv("Failed to find dset with idcode %s\n",
2916                         sel->identifier+nid);
2917             SUMA_RETURN(0);
2918          }
2919          goto SAVEDSET;
2920       }
2921       /* last hurrah */
2922       if (!(dset = SUMA_FindDset2_s(sel->identifier,
2923                                  SUMAg_CF->DsetList, NULL))) {
2924          SUMA_S_Errv("Failed to find dset with identifier %s\n",
2925                      sel->identifier);
2926          SUMA_RETURN(0);
2927       }
2928       goto SAVEDSET;
2929 
2930       SAVEDSET:
2931       if (!dset) SUMA_RETURN(0);
2932       oname = SUMA_WriteDset_PreserveID(sel->prefix, dset,
2933                                         SUMA_NO_DSET_FORMAT, 1,0);
2934       SUMA_S_Notev("Wrote: %s\n", oname);
2935       if (oname) SUMA_free(oname);
2936    } else {
2937       SUMA_S_Errv("Not setup for type %s yet\n", sel->type);
2938       SUMA_RETURN(0);
2939    }
2940    SUMA_RETURN(1);
2941 }
2942 
SUMA_W_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)2943 int SUMA_W_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
2944 {
2945    static char FuncName[]={"SUMA_W_Key"};
2946    char tk[]={"W"}, keyname[100];
2947    int k, nc;
2948    SUMA_EngineData *ED = NULL;
2949    SUMA_SurfaceObject *SO;
2950    DList *list = NULL;
2951    DListElmt *el= NULL;
2952    char *lbls=NULL;
2953    SUMA_SAVE_LIST_EL *sel=NULL;
2954    SUMA_Boolean LocalHead = NOPE;
2955 
2956    SUMA_ENTRY;
2957 
2958    SUMA_KEY_COMMON;
2959 
2960    /* do the work */
2961    switch (k) {
2962       case XK_W:
2963          if ((SUMA_CTRL_KEY(key))){
2964             if (!SUMAg_CF->SaveList || !dlist_size(SUMAg_CF->SaveList)) {
2965                SUMA_S_Note("Nothing in SaveList");
2966                SUMA_RETURN(1);
2967             }
2968             while((el = dlist_head(SUMAg_CF->SaveList))) {
2969                sel = (SUMA_SAVE_LIST_EL *)el->data;
2970                if (sel->identifier) {
2971                   if (!(SUMA_SaveSaveListElement(sel))) {
2972                      SUMA_S_Warnv("Failed to save %s %s\n",
2973                                   sel->identifier, sel->prefix)
2974                   }
2975                }
2976                dlist_remove(SUMAg_CF->SaveList, el, (void *)(&sel));
2977             }
2978          } else {
2979             if ((SO=SUMA_SV_Focus_SO(sv))) {
2980                if (!list) list = SUMA_CreateList();
2981                ED = SUMA_InitializeEngineListData (SE_SaveSOFileSelection);
2982                if (!(el = SUMA_RegisterEngineListCommand (  list, ED,
2983                                                 SEF_vp, (void *)SO,
2984                                                 SES_Suma, (void *)sv, NOPE,
2985                                                 SEI_Head, NULL))) {
2986                   fprintf (SUMA_STDERR,
2987                            "Error %s: Failed to register command.\n",
2988                            FuncName);
2989                }
2990 
2991                if (!SUMA_RegisterEngineListCommand (  list, ED,
2992                                           SEF_ip, sv->X->TOPLEVEL,
2993                                           SES_Suma, (void *)sv, NOPE,
2994                                           SEI_In, el)) {
2995                   fprintf (SUMA_STDERR,
2996                            "Error %s: Failed to register command.\n",
2997                            FuncName);
2998                }
2999 
3000                if (!SUMA_Engine (&list)) {
3001                   fprintf( SUMA_STDERR,
3002                            "Error %s: SUMA_Engine call failed.\n", FuncName);
3003                }
3004             }
3005          }
3006          break;
3007 
3008       case XK_w:
3009            if (SUMAg_CF->Dev) {
3010                if ((SO=SUMA_SV_Focus_SO(sv))) {
3011                   if (!SUMAg_CF->X->Whereami_TextShell) {
3012                      if (!(SUMAg_CF->X->Whereami_TextShell =
3013                               SUMA_CreateTextShellStruct (  SUMA_Whereami_open,
3014                                                       NULL, NULL,
3015                                                       SUMA_Whereami_destroyed,
3016                                                       NULL, NULL))) {
3017                         SUMA_S_Err("Failed to create TextShellStruct.");
3018                         break;
3019                      }
3020                   }
3021                   /* call the function to form labels and notify window */
3022                   lbls = SUMA_GetLabelsAtSelection((SUMA_ALL_DO *)SO,
3023                                                SO->SelectedNode, -1);
3024                   if (lbls) SUMA_free(lbls); lbls = NULL;
3025                }
3026             }
3027          break;
3028       default:
3029          SUMA_S_Err("Il ne faut pas ci dessous");
3030          SUMA_RETURN(0);
3031          break;
3032    }
3033 
3034    SUMA_RETURN(1);
3035 }
3036 
3037 /*!
3038    Execute commands when Z or z is pressed
3039 */
SUMA_Z_Key(SUMA_SurfaceViewer * sv,char * key,char * callmode)3040 int SUMA_Z_Key(SUMA_SurfaceViewer *sv, char *key, char *callmode)
3041 {
3042    static char FuncName[]={"SUMA_Z_Key"};
3043    char tk[]={"Z"}, keyname[100], msg[100];
3044    int k, nc, ii, jj, mm;
3045    SUMA_Boolean LocalHead = NOPE;
3046 
3047    SUMA_ENTRY;
3048 
3049    SUMA_KEY_COMMON;
3050 
3051    /* do the work */
3052    switch (k) {
3053       case XK_Z:
3054          sv->FOV[sv->iState] /= (1+sv->KeyZoomGain);
3055          if (sv->FOV[sv->iState] < FOV_MIN) {
3056             SUMA_BEEP; sv->FOV[sv->iState] = FOV_MIN;
3057          }
3058          /*fprintf(stderr,"Zoom in %f\n", sv->FOV[sv->iState]);*/
3059          /* Now update the zoom compensation variable */
3060          if (sv->ZoomCompensate) {
3061             sv->ZoomCompensate = sv->FOV[sv->iState] / SUMA_sv_auto_fov(sv);
3062             if (sv->ZoomCompensate > 1) sv->ZoomCompensate = 1.0;
3063                /* weird stuff at zc_fac higher that 1.5 */
3064             else if (sv->ZoomCompensate < 0.005) sv->ZoomCompensate = 0.005;
3065          }
3066          SUMA_postRedisplay(sv->X->GLXAREA, NULL, NULL);
3067          break;
3068 
3069       case XK_z:
3070          sv->FOV[sv->iState] /= (1-sv->KeyZoomGain);
3071          if (sv->ortho) {
3072             if (sv->FOV[sv->iState] > FOV_MAX/2.0) {
3073                SUMA_BEEP; sv->FOV[sv->iState] = FOV_MAX/2.0; }
3074          } else {
3075             if (sv->FOV[sv->iState] > FOV_MAX) {
3076                SUMA_BEEP; sv->FOV[sv->iState] = FOV_MAX; }
3077          }
3078          /*fprintf(stderr,"Zoom out %f\n", sv->FOV[sv->iState]);*/
3079          /* Now update the zoom compensation variable */
3080          if (sv->ZoomCompensate) {
3081             sv->ZoomCompensate = sv->FOV[sv->iState] / SUMA_sv_auto_fov(sv);
3082             if (sv->ZoomCompensate > 1) sv->ZoomCompensate = 1.0;
3083                /* weird stuff at zc_fac higher that 1.5 */
3084             else if (sv->ZoomCompensate < 0.005) sv->ZoomCompensate = 0.005;
3085                /* weird stuff cause by integer spin variables!
3086                   Proper way to handle all this is with float position
3087                   storage and no recalculation of zc_fac except at zooming.*/
3088          }
3089          SUMA_postRedisplay(sv->X->GLXAREA, NULL, NULL);
3090          break;
3091 
3092       default:
3093          SUMA_S_Err("Il ne faut pas etre la");
3094          SUMA_RETURN(0);
3095          break;
3096    }
3097    SUMA_RETURN(1);
3098 }
3099 
SUMA_Up_Key(SUMA_SurfaceViewer * sv,char * key,char * caller)3100 int SUMA_Up_Key(SUMA_SurfaceViewer *sv, char *key, char *caller)
3101 {
3102    static char FuncName[]={"SUMA_Up_Key"};
3103    char tk[]={"Up"}, keyname[100], skey[65], skeyi[65];
3104    int k, nc, ii, inode = -1;
3105    float ArrowDeltaRot = 0.05;
3106       /* The larger the value, the bigger the rotation increment */
3107    Widget w;
3108    double dd[3] = {0.0, -1.0, 0.0}; /* up */
3109    SUMA_SurfaceObject *SO=NULL;
3110    SUMA_Boolean LocalHead = NOPE;
3111 
3112    SUMA_ENTRY;
3113 
3114    SUMA_KEY_COMMON;
3115 
3116    SUMA_KEY_SWITCH;
3117 
3118    w = sv->X->GLXAREA;
3119    /* do the work */
3120    switch (k) {
3121       case XK_Up:
3122             if ((SUMA_CTRL_KEY(key) && SUMA_SHIFT_KEY(key))) {
3123                float a[3];
3124                /* Posterior view ctrl+shift+up*/
3125                /* From top view, rotate by 90 degrees about x axis */
3126                a[0] = 1.0; a[1] = 0.0; a[2] = 0.0;
3127                axis_to_quat(a, SUMA_PI/2, sv->GVS[sv->StdView].currentQuat);
3128                SUMA_postRedisplay(w, NULL, NULL);
3129             }else if (SUMA_SHIFT_KEY(key)) {
3130                /*fprintf (SUMA_STDERR,"%s: Shift up\n", FuncName);*/
3131                sv->GVS[sv->StdView].translateVec[1] +=
3132                 (GLfloat)sv->GVS[sv->StdView].ArrowtranslateDeltaY /
3133                 (float)sv->X->aHEIGHT*sv->GVS[sv->StdView].TranslateGain;
3134                SUMA_postRedisplay(w, NULL, NULL);
3135             }else if (SUMA_CTRL_KEY(key)){
3136                /*fprintf (SUMA_STDERR,"%s: Control Up\n", FuncName);*/
3137                /* Top view ctrl+up*/
3138                float a[3];
3139                /* Default top view, rotate by nothing */
3140                a[0] = 1.0; a[1] = 0.0; a[2] = 0.0;
3141                axis_to_quat(a, 0, sv->GVS[sv->StdView].currentQuat);
3142                SUMA_postRedisplay(w, NULL, NULL);
3143             }else if (SUMA_AALT_KEY(key)) {
3144                SUMA_LH("alt down");
3145                if ((SO = SUMA_SV_Focus_SO(sv))) {
3146                   if (SO->SelectedNode < 0 ||
3147                       !SO->FN) SUMA_RETURN(1); /* nothing to do */
3148                   inode = SO->SelectedNode;
3149                   {
3150                      inode =
3151                         SUMA_NodeNeighborAlongScreenDirection(sv, SO, inode, dd);
3152                      if (inode == -2) {
3153                         SUMA_S_Err("Failed in"
3154                                    " SUMA_NodeNeighborAlongScreenDirection");
3155                         SUMA_RETURN(0);
3156                      } else if (inode == -1) {
3157                         SUMA_LH("No good direction, get out");
3158                         SUMA_BEEP;
3159                         break;
3160                      } else {
3161                         SUMA_LHv("Next node should be %d\n", inode);
3162                      }
3163                   }
3164                   /* Now set the new selected node */
3165                   if (inode >= 0 && inode != SO->SelectedNode) {
3166                      char stmp[64]; /* use Jump callback,
3167                                        the easy route */
3168                      sprintf(stmp,"%d", inode);
3169                      SUMA_JumpIndex (stmp, (void *)sv);
3170                   }
3171                }
3172             } else {
3173                if (LocalHead)
3174                   fprintf (SUMA_STDERR,"%s: Vanilla kind.\n", FuncName);
3175                trackball_Phi(sv->GVS[sv->StdView].deltaQuat,
3176                   0.0, -ArrowDeltaRot, /* first point */
3177                   0.0, ArrowDeltaRot, /* ending x,y */
3178                   sv->ArrowRotationAngle);
3179                if (LocalHead) {
3180                   fprintf(stdout,"\ncurrentQuat\n");
3181                   for (ii=0; ii<4; ++ii) {
3182                      fprintf( stdout,"%f\t",
3183                               sv->GVS[sv->StdView].currentQuat[ii]);
3184                   }
3185                   fprintf(stdout,"\n");
3186                   fprintf(stdout,"\ndeltaQuat\n");
3187                   for (ii=0; ii<4; ++ii) {
3188                      fprintf(stdout,"%f\t", sv->GVS[sv->StdView].deltaQuat[ii]);
3189                   }
3190                   fprintf(stdout,"\n");
3191                }
3192                add_quats ( sv->GVS[sv->StdView].deltaQuat,
3193                            sv->GVS[sv->StdView].currentQuat,
3194                            sv->GVS[sv->StdView].currentQuat);
3195                if (LocalHead) {
3196                   fprintf(stdout,"\nnewQuat\n");
3197                   for (ii=0; ii<4; ++ii) {
3198                      fprintf( stdout,"%f\t",
3199                               sv->GVS[sv->StdView].currentQuat[ii]);
3200                   }
3201                   fprintf(stdout,"\n");
3202                }
3203                sv->GVS[sv->StdView].spinDeltaX = 0;
3204                sv->GVS[sv->StdView].spinDeltaY =
3205                                     2.0*ArrowDeltaRot*sv->X->aHEIGHT;
3206                SUMA_postRedisplay(w, NULL, NULL);
3207 
3208             }
3209 
3210             break;
3211 
3212          break;
3213       default:
3214          SUMA_S_Err("Il ne faut pas etre ici");
3215          SUMA_RETURN(0);
3216          break;
3217    }
3218 
3219    SUMA_RETURN(1);
3220 }
SUMA_Down_Key(SUMA_SurfaceViewer * sv,char * key,char * caller)3221 int SUMA_Down_Key(SUMA_SurfaceViewer *sv, char *key, char *caller)
3222 {
3223    static char FuncName[]={"SUMA_Down_Key"};
3224    char tk[]={"Down"}, keyname[100], skey[65], skeyi[65];
3225    int k, nc, ii, inode=-1;
3226    float ArrowDeltaRot = 0.05;
3227          /* The larger the value, the bigger the rotation increment */
3228    Widget w;
3229    double dd[3] = {0.0, 1.0, 0.0}; /* down */
3230    SUMA_SurfaceObject *SO=NULL;
3231    SUMA_Boolean LocalHead = NOPE;
3232 
3233    SUMA_ENTRY;
3234 
3235    SUMA_KEY_COMMON;
3236 
3237    SUMA_KEY_SWITCH;
3238 
3239    w = sv->X->GLXAREA;
3240    /* do the work */
3241    switch (k) {
3242       case XK_Down:
3243             if ((SUMA_CTRL_KEY(key) && SUMA_SHIFT_KEY(key))) {
3244                float a[3], cQ[4], dQ[4];
3245                /* Posterior view ctrl+shift+down*/
3246                /* From top view, first rotate by 90 degrees about x axis */
3247                a[0] = 1.0; a[1] = 0.0; a[2] = 0.0;
3248                axis_to_quat(a, SUMA_PI/2, cQ);
3249                /* then rotate by 180 degrees about y axis */
3250                a[0] = 0.0; a[1] = 1.0; a[2] = 0.0;
3251                axis_to_quat(a, SUMA_PI, dQ);
3252                /*add rotation */
3253                add_quats (dQ, cQ, sv->GVS[sv->StdView].currentQuat);
3254                SUMA_postRedisplay(w, NULL, NULL);
3255             }else if (SUMA_SHIFT_KEY(key)) {
3256                /*fprintf (SUMA_STDERR,"%s: Shift down\n", FuncName);*/
3257                /*sv->GVS[sv->StdView].translateVec[0] += 0;*/
3258                sv->GVS[sv->StdView].translateVec[1] -=
3259                   (GLfloat)sv->GVS[sv->StdView].ArrowtranslateDeltaY /
3260                   (float)sv->X->aHEIGHT*sv->GVS[sv->StdView].TranslateGain;
3261                SUMA_postRedisplay(w, NULL, NULL);
3262             }else if (SUMA_CTRL_KEY(key)){
3263                /*fprintf (SUMA_STDERR,"%s: Control down\n", FuncName);*/
3264                /* Inferior view ctrl+down*/
3265                float a[3];
3266                /* From top view, rotate by 180 degrees about y axis */
3267                a[0] = 0.0; a[1] = 1.0; a[2] = 0.0;
3268                axis_to_quat(a, SUMA_PI, sv->GVS[sv->StdView].currentQuat);
3269                SUMA_postRedisplay(w, NULL, NULL);
3270             }else if (SUMA_AALT_KEY(key)) {
3271                SUMA_LH("alt down");
3272                if ((SO = SUMA_SV_Focus_SO(sv))) {
3273                   if (SO->SelectedNode < 0 ||
3274                       !SO->FN) SUMA_RETURN(1); /* nothing to do */
3275                   inode = SO->SelectedNode;
3276                   {
3277                      inode =
3278                         SUMA_NodeNeighborAlongScreenDirection(sv, SO, inode, dd);
3279                      if (inode == -2) {
3280                         SUMA_S_Err("Failed in"
3281                                    " SUMA_NodeNeighborAlongScreenDirection");
3282                         SUMA_RETURN(0);
3283                      } else if (inode == -1) {
3284                         SUMA_LH("No good direction, get out");
3285                         SUMA_BEEP;
3286                         break;
3287                      } else {
3288                         SUMA_LHv("Next node should be %d\n", inode);
3289                      }
3290                   }
3291                   /* Now set the new selected node */
3292                   if (inode >= 0 && inode != SO->SelectedNode) {
3293                      char stmp[64]; /* use Jump callback,
3294                                        the easy route */
3295                      sprintf(stmp,"%d", inode);
3296                      SUMA_JumpIndex (stmp, (void *)sv);
3297                   }
3298                }
3299             }else {
3300                /*fprintf (SUMA_STDERR,"%s: Vanilla kind.\n", FuncName);*/
3301                trackball_Phi(sv->GVS[sv->StdView].deltaQuat,
3302                   0.0, ArrowDeltaRot, /* first point */
3303                   0.0, -ArrowDeltaRot, /* ending x,y */
3304                   sv->ArrowRotationAngle);
3305                /*fprintf(stdout,"\ncurrentQuat\n");
3306                  for (i=0; i<4; ++i) {
3307                   fprintf(stdout,"%f\t", sv->GVS[sv->StdView].currentQuat[i]);}
3308                  fprintf(stdout,"\n");
3309                  fprintf(stdout,"\ndeltaQuat\n");for (i=0; i<4; ++i) {
3310                   fprintf(stdout,"%f\t", sv->GVS[sv->StdView].deltaQuat[i]);}
3311                  fprintf(stdout,"\n"); */
3312                add_quats (sv->GVS[sv->StdView].deltaQuat,
3313                           sv->GVS[sv->StdView].currentQuat,
3314                           sv->GVS[sv->StdView].currentQuat);
3315                /*fprintf(stdout,"\nnewQuat\n");
3316                  for (i=0; i<4; ++i) {
3317                   fprintf(stdout,"%f\t", sv->GVS[sv->StdView].currentQuat[i]);}
3318                  fprintf(stdout,"\n");*/
3319                sv->GVS[sv->StdView].spinDeltaX = 0;
3320                sv->GVS[sv->StdView].spinDeltaY =
3321                            -2.0*ArrowDeltaRot*sv->X->aHEIGHT;
3322                SUMA_postRedisplay(w, NULL, NULL);
3323             }
3324 
3325             break;
3326 
3327          break;
3328       default:
3329          SUMA_S_Err("Il ne faut pas etre ici");
3330          SUMA_RETURN(0);
3331          break;
3332    }
3333 
3334 
3335    SUMA_RETURN(1);
3336 }
SUMA_Left_Key(SUMA_SurfaceViewer * sv,char * key,char * caller)3337 int SUMA_Left_Key(SUMA_SurfaceViewer *sv, char *key, char *caller)
3338 {
3339    static char FuncName[]={"SUMA_Left_Key"};
3340    char tk[]={"Left"}, keyname[100], skey[65], skeyi[65];
3341    int k, nc, ii, jj, inode = -1, bkey = 0;
3342    float ArrowDeltaRot = 0.05;
3343       /* The larger the value, the bigger the rotation increment */
3344    Widget w;
3345    double dd[3] = {-1.0, 0.0, 0.0}; /* left */
3346    SUMA_SurfaceObject *SO=NULL;
3347    SUMA_Boolean LocalHead = NOPE;
3348 
3349    SUMA_ENTRY;
3350 
3351    SUMA_KEY_COMMON;
3352 
3353    SUMA_KEY_SWITCH;
3354 
3355    w = sv->X->GLXAREA;
3356    /* do the work */
3357    switch (k) {
3358       case XK_Left:
3359             if ((SUMA_CTRL_KEY(key) && SUMA_SHIFT_KEY(key))) {
3360                float a[3], cQ[4];
3361                /* rotate about Z axis CCW  */
3362                a[0] = 0.0; a[1] = 0.0; a[2] = 1.0;
3363                axis_to_quat(a, -sv->ArrowRotationAngle, cQ);
3364                /*add rotation */
3365                add_quats ( cQ,
3366                            sv->GVS[sv->StdView].currentQuat,
3367                            sv->GVS[sv->StdView].currentQuat);
3368                sv->GVS[sv->StdView].spinDeltaX = 0;
3369                sv->GVS[sv->StdView].spinDeltaY = 0;
3370                SUMA_postRedisplay(w, NULL, NULL);
3371             }else if (SUMA_SHIFT_KEY(key)) {
3372                /*fprintf (SUMA_STDERR,"%s: Shift down\n", FuncName);*/
3373                sv->GVS[sv->StdView].translateVec[0] -=
3374                   (GLfloat)sv->GVS[sv->StdView].ArrowtranslateDeltaX       /
3375                   (float)sv->X->aWIDTH*sv->GVS[sv->StdView].TranslateGain;
3376                /*sv->GVS[sv->StdView].translateVec[1] -= 0;*/
3377                SUMA_postRedisplay(w, NULL, NULL);
3378             }else if (SUMA_CTRL_KEY(key)){
3379                float a[3], cQ[4], dQ[4];
3380                /* From top view, rotate about x 90 degrees.*/
3381                a[0] = 1.0; a[1] = 0.0; a[2] = 0.0;
3382                axis_to_quat(a, SUMA_PI/2.0, cQ);
3383                /* then rotate about y 90 degrees */
3384                a[0] = 0.0; a[1] = 1.0; a[2] = 0.0;
3385                axis_to_quat(a, SUMA_PI/2.0, dQ);
3386                /*add and apply rotation*/
3387                add_quats (dQ, cQ, sv->GVS[sv->StdView].currentQuat);
3388                SUMA_postRedisplay(w, NULL, NULL);
3389             }else if (SUMA_AALT_KEY(key)) {
3390                SUMA_LH("alt down");
3391                if ((SO = SUMA_SV_Focus_SO(sv))) {
3392                   if (SO->SelectedNode < 0 ||
3393                       !SO->FN) SUMA_RETURN(1); /* nothing to do */
3394                   inode = SO->SelectedNode;
3395                   {
3396                      inode =
3397                         SUMA_NodeNeighborAlongScreenDirection(sv, SO, inode, dd);
3398                      if (inode == -2) {
3399                         SUMA_S_Err("Failed in"
3400                                    " SUMA_NodeNeighborAlongScreenDirection");
3401                         SUMA_RETURN(0);
3402                      } else if (inode == -1) {
3403                         SUMA_LH("No good direction, get out");
3404                         SUMA_BEEP;
3405                         break;
3406                      } else {
3407                         SUMA_LHv("Next node should be %d\n", inode);
3408                      }
3409                   }
3410                   /* Now set the new selected node */
3411                   if (inode >= 0 && inode != SO->SelectedNode) {
3412                      char stmp[64]; /* use Jump callback,
3413                                        the easy route */
3414                      sprintf(stmp,"%d", inode);
3415                      SUMA_JumpIndex (stmp, (void *)sv);
3416                   }
3417                }
3418             }else {
3419                /*fprintf (SUMA_STDERR,"%s: Vanilla kind.\n", FuncName);*/
3420                trackball_Phi(sv->GVS[sv->StdView].deltaQuat,
3421                   ArrowDeltaRot, 0.0, /* first point */
3422                   -ArrowDeltaRot, 0.0, /* ending x,y */
3423                   sv->ArrowRotationAngle);
3424                add_quats ( sv->GVS[sv->StdView].deltaQuat,
3425                            sv->GVS[sv->StdView].currentQuat,
3426                            sv->GVS[sv->StdView].currentQuat);
3427                sv->GVS[sv->StdView].spinDeltaX =
3428                               -2.0*ArrowDeltaRot*sv->X->aWIDTH;
3429                sv->GVS[sv->StdView].spinDeltaY = 0;
3430                SUMA_postRedisplay(w, NULL, NULL);
3431             }
3432 
3433             break;
3434 
3435          break;
3436       default:
3437          SUMA_S_Err("Il ne faut pas etre ici");
3438          SUMA_RETURN(0);
3439          break;
3440    }
3441 
3442 
3443    SUMA_RETURN(1);
3444 }
SUMA_Right_Key(SUMA_SurfaceViewer * sv,char * key,char * caller)3445 int SUMA_Right_Key(SUMA_SurfaceViewer *sv, char *key, char *caller)
3446 {
3447    static char FuncName[]={"SUMA_Right_Key"};
3448    char tk[]={"Right"}, keyname[100], skey[65], skeyi[65];
3449    int k, nc, ii, inode=-1;
3450    float ArrowDeltaRot = 0.05;
3451          /* The larger the value, the bigger the rotation increment */
3452    Widget w;
3453    double dd[3] = {1.0, 0.0, 0.0}; /* right */
3454    SUMA_SurfaceObject *SO=NULL;
3455    SUMA_Boolean LocalHead = NOPE;
3456 
3457    SUMA_ENTRY;
3458 
3459    SUMA_KEY_COMMON;
3460 
3461    SUMA_KEY_SWITCH;
3462 
3463    w = sv->X->GLXAREA;
3464    /* do the work */
3465    switch (k) {
3466       case XK_Right:
3467             if ((SUMA_CTRL_KEY(key) && SUMA_SHIFT_KEY(key))) {
3468                float a[3], cQ[4];
3469                /* rotate about Z axis CCW  */
3470                a[0] = 0.0; a[1] = 0.0; a[2] = 1.0;
3471                axis_to_quat(a, sv->ArrowRotationAngle, cQ);
3472                /*add rotation */
3473                add_quats ( cQ,
3474                            sv->GVS[sv->StdView].currentQuat,
3475                            sv->GVS[sv->StdView].currentQuat);
3476                sv->GVS[sv->StdView].spinDeltaX = 0;
3477                sv->GVS[sv->StdView].spinDeltaY = 0;
3478                SUMA_postRedisplay(w, NULL, NULL);
3479             }else if (SUMA_SHIFT_KEY(key)) {
3480                /*fprintf (SUMA_STDERR,"%s: Shift down\n", FuncName);*/
3481                sv->GVS[sv->StdView].translateVec[0] +=
3482                   (GLfloat)sv->GVS[sv->StdView].ArrowtranslateDeltaX /
3483                   (float)sv->X->aWIDTH*sv->GVS[sv->StdView].TranslateGain;
3484                /*sv->GVS[sv->StdView].translateVec[1] -= 0;*/
3485                SUMA_postRedisplay(w,  NULL, NULL);
3486             }else if (SUMA_CTRL_KEY(key)){
3487                float a[3], cQ[4], dQ[4];
3488                /* From top view, rotate about x 90 degrees */
3489                a[0] = 1.0; a[1] = 0.0; a[2] = 0.0;
3490                axis_to_quat(a, SUMA_PI/2.0, cQ);
3491                /* then rotate about y -90 degrees */
3492                a[0] = 0.0; a[1] = 1.0;
3493                axis_to_quat(a, -SUMA_PI/2.0, dQ);
3494                /*add and apply rotation*/
3495                add_quats (dQ, cQ, sv->GVS[sv->StdView].currentQuat);
3496                SUMA_postRedisplay(w, NULL, NULL);
3497             }else if (SUMA_AALT_KEY(key)) {
3498                SUMA_LH("alt down");
3499                if ((SO = SUMA_SV_Focus_SO(sv))) {
3500                   if (SO->SelectedNode < 0 ||
3501                       !SO->FN) SUMA_RETURN(1); /* nothing to do */
3502                   inode = SO->SelectedNode;
3503                   {
3504                      inode =
3505                         SUMA_NodeNeighborAlongScreenDirection(sv, SO, inode, dd);
3506                      if (inode == -2) {
3507                         SUMA_S_Err("Failed in"
3508                                    " SUMA_NodeNeighborAlongScreenDirection");
3509                         SUMA_RETURN(0);
3510                      } else if (inode == -1) {
3511                         SUMA_LH("No good direction, get out");
3512                         SUMA_BEEP;
3513                         break;
3514                      } else {
3515                         SUMA_LHv("Next node should be %d\n", inode);
3516                      }
3517                   }
3518                   /* Now set the new selected node */
3519                   if (inode >= 0 && inode != SO->SelectedNode) {
3520                      char stmp[64]; /* use Jump callback,
3521                                        the easy route */
3522                      sprintf(stmp,"%d", inode);
3523                      SUMA_JumpIndex (stmp, (void *)sv);
3524                   }
3525                }
3526             }else {
3527                /*fprintf (SUMA_STDERR,"%s: Vanilla kind.\n", FuncName);*/
3528                trackball_Phi(sv->GVS[sv->StdView].deltaQuat,
3529                   -ArrowDeltaRot, 0.0, /* first point */
3530                   ArrowDeltaRot, 0.0, /* ending x,y */
3531                   sv->ArrowRotationAngle);
3532                add_quats (sv->GVS[sv->StdView].deltaQuat,
3533                           sv->GVS[sv->StdView].currentQuat,
3534                           sv->GVS[sv->StdView].currentQuat);
3535                sv->GVS[sv->StdView].spinDeltaX = 2.0*ArrowDeltaRot*sv->X->aWIDTH;
3536                sv->GVS[sv->StdView].spinDeltaY = 0;
3537                SUMA_postRedisplay(w,  NULL, NULL);
3538             }
3539 
3540             break;
3541 
3542          break;
3543       default:
3544          SUMA_S_Err("Il ne faut pas etre ici");
3545          SUMA_RETURN(0);
3546          break;
3547    }
3548 
3549 
3550    SUMA_RETURN(1);
3551 }
3552 
3553 #define SUMA_ALTHELL ( (Kev.state & Mod1Mask) || \
3554                        (Kev.state & Mod2Mask) ||  \
3555                        (Kev.state & SUMA_APPLE_AltOptMask) )
3556 
SUMA_Butts2String(SUMA_EVENT * ev)3557 char *SUMA_Butts2String(SUMA_EVENT *ev)
3558 {
3559    static char ccs[10][32];
3560    static int icall=0;
3561    char *cc;
3562    int nb=0, mot;
3563 
3564    ++icall; if (icall>9) icall=0;
3565    cc = (char *)ccs[icall]; cc[0]='\0';
3566 
3567    if (ev->b1) {cc[nb++]='1'; cc[nb++]='&'; mot = 0;}
3568    if (ev->b2) {cc[nb++]='2'; cc[nb++]='&'; mot = 0;}
3569    if (ev->b3) {cc[nb++]='3'; cc[nb++]='&'; mot = 0;}
3570    if (ev->b4) {cc[nb++]='4'; cc[nb++]='&'; mot = 0;}
3571    if (ev->b5) {cc[nb++]='5'; cc[nb++]='&'; mot = 0;}
3572    if (ev->b6) {cc[nb++]='6'; cc[nb++]='&'; mot = 0;}
3573    if (ev->b7) {cc[nb++]='6'; cc[nb++]='&'; mot = 0;}
3574    if (ev->b1m) {cc[nb++]='1'; cc[nb++]='&'; mot = 1;}
3575    if (ev->b2m) {cc[nb++]='2'; cc[nb++]='&'; mot = 1;}
3576    if (ev->b3m) {cc[nb++]='3'; cc[nb++]='&'; mot = 1;}
3577    if (ev->b4m) {cc[nb++]='4'; cc[nb++]='&'; mot = 1;}
3578    if (ev->b5m) {cc[nb++]='5'; cc[nb++]='&'; mot = 1;}
3579 
3580    if (nb>1) nb = nb-1; /* Get rid of last & */
3581    cc[nb] = '\0';
3582    return(cc);
3583 }
3584 
SUMA_KeyType2String(int kt)3585 char *SUMA_KeyType2String(int kt)
3586 {
3587    switch(kt) {
3588       case KeyPress:
3589          return("key");
3590       case ButtonRelease:
3591          return("release");
3592       case ButtonPress:
3593          return("press");
3594       case MotionNotify:
3595          return("motion");
3596       default:
3597          return("UNKNOWN");
3598    }
3599 }
3600 
SUMA_ShowEvent(SUMA_EVENT * ev,int opt,char * pre)3601 void SUMA_ShowEvent(SUMA_EVENT *ev, int opt, char *pre)
3602 {
3603    static char FuncName[]={"SUMA_ShowEvent"};
3604    static int icall=0;
3605    char *s = NULL;
3606    SUMA_STRING *SS = NULL;
3607    FILE *out = stderr;
3608 
3609    SUMA_ENTRY;
3610 
3611    SS = SUMA_StringAppend(NULL, NULL);
3612 
3613    if (pre) SUMA_StringAppend(SS,pre);
3614    if (!ev) {
3615       SUMA_StringAppend(SS,"NULL ev\n"); goto OUT;
3616    }
3617    ++icall;
3618    if (!opt) {
3619       SUMA_StringAppend_va(SS,"Event Struct (set %d, callid %d)\n"
3620                         "   ktype %d kstate %d transl %s\n"
3621                         "   keysym %d mtype %d mstate %d\n"
3622                         "   bButton %d mButton %d\n"
3623                         "   bTime %ld  mTime %ld\n"
3624                         "   mX %d mY %d bX %d bY %d\n"
3625                         "   mDelta %d, mDeltaX %d, mDeltaY %d\n"
3626                "   shift %d control %d mod1 %d mod2 %d mod3 %d mod4 %d mod5 %d\n"
3627                         "   ApplAltOpt %d DoubleClick %d\n"
3628                         "   b1 %d b2 %d b3 %d b4 %d b5 %d b6 %d b7 %d\n"
3629                         "   b1m %d b2m %d b3m %d b4m %d b5m %d\n",
3630                         ev->set, icall,
3631                         ev->ktype, ev->kstate, ev->transl,
3632                         ev->keysym, ev->mtype, ev->mstate,
3633                         ev->bButton, ev->mButton,
3634                         ev->bTime, ev->mTime,
3635                         ev->mX, ev->mY, ev->bX, ev->bY,
3636                         ev->mDelta, ev->mDeltaX, ev->mDeltaY,
3637       ev->Shift, ev->Control, ev->Mod1, ev->Mod2, ev->Mod3, ev->Mod4, ev->Mod5,
3638                         ev->AppleAltOpt, ev->DoubleClick,
3639                         ev->b1, ev->b2, ev->b3, ev->b4, ev->b5, ev->b6, ev->b7,
3640                         ev->b1m, ev->b2m, ev->b3m, ev->b4m, ev->b5m);
3641    } else {
3642       /* More readable mode */
3643       SUMA_StringAppend_va(SS,"Input Event %d: %s   \n",
3644             icall, ev->set ? "":"WARNING Event Struct Not Set!" );
3645       if (ev->ktype == KeyPress) {
3646          SUMA_StringAppend_va(SS,"%s: char>>%s<< sym>>%d<< ",
3647                            SUMA_KeyType2String(ev->ktype),
3648                            ev->transl, (int)ev->keysym);
3649       } else {
3650          SUMA_StringAppend_va(SS,"Mouse %s %s%s: ",
3651             SUMA_Butts2String(ev),
3652             SUMA_KeyType2String(ev->ktype),
3653             ev->DoubleClick ? " double click":"");
3654          if (ev->b1) SUMA_StringAppend_va(SS,"b1 ",ev->b1);
3655          if (ev->b2) SUMA_StringAppend_va(SS,"b2 ",ev->b2);
3656          if (ev->b3) SUMA_StringAppend_va(SS,"b3 ",ev->b3);
3657          if (ev->b4) SUMA_StringAppend_va(SS,"b4 ",ev->b4);
3658          if (ev->b5) SUMA_StringAppend_va(SS,"b5 ",ev->b5);
3659          if (ev->b6) SUMA_StringAppend_va(SS,"b6 ",ev->b6);
3660          if (ev->b7) SUMA_StringAppend_va(SS,"b7 ",ev->b7);
3661          if (ev->b1m) SUMA_StringAppend_va(SS,"m1 ",ev->b1m);
3662          if (ev->b2m) SUMA_StringAppend_va(SS,"m2 ",ev->b2m);
3663          if (ev->b3m) SUMA_StringAppend_va(SS,"m3 ",ev->b3m);
3664          if (ev->b4m) SUMA_StringAppend_va(SS,"m4 ",ev->b4m);
3665          if (ev->b5m) SUMA_StringAppend_va(SS,"m5 ",ev->b5m);
3666       }
3667       if (ev->Shift) {
3668          SUMA_StringAppend_va(SS,"Shift ");
3669       }
3670       if (ev->Control){
3671          SUMA_StringAppend_va(SS,"Control ");
3672       }
3673       if (ev->Mod1){
3674          SUMA_StringAppend_va(SS,"alt ");
3675       }
3676       if (ev->Mod2){
3677          SUMA_StringAppend_va(SS,"Mod2 (command on mac) ");
3678       }
3679       if (ev->Mod3){
3680          SUMA_StringAppend_va(SS,"Mod3 ");
3681       }
3682       if (ev->Mod4){
3683          SUMA_StringAppend_va(SS,"Mod4 ");
3684       }
3685       if (ev->Mod5){
3686          SUMA_StringAppend_va(SS,"Mod5 ");
3687       }
3688       if (ev->Mod2){
3689          SUMA_StringAppend_va(SS,"Mod2 ");
3690       }
3691       if (ev->AppleAltOpt){
3692         SUMA_StringAppend_va(SS,"Apple Alt/Opt ");
3693       }
3694       SUMA_StringAppend_va(SS,"k/mstate [%d/%d]\n\n", ev->kstate, ev->mstate);
3695    }
3696    OUT:
3697    SUMA_SS2S(SS,s);
3698 
3699    fprintf(out,"%s",s);
3700 
3701    SUMA_ifree(s);
3702 
3703    SUMA_RETURNe;
3704 }
3705 
3706 #ifdef DARWIN
3707 #define evALT ((ev->Mod1 || ev->Mod2 || ev->AppleAltOpt))
3708 #else
3709 #define evALT ((ev->Mod1))
3710 #endif
SUMA_ShftCont_Event(SUMA_EVENT * ev)3711 int SUMA_ShftCont_Event(SUMA_EVENT *ev)
3712 {
3713    if (!ev) ev = SUMAg_CF->lev;
3714    if (!ev || !ev->set) return(0);
3715    if (ev->Shift && ev->Control && !evALT) return(1);
3716    return(0);
3717 }
SUMA_Cont_Event(SUMA_EVENT * ev)3718 int SUMA_Cont_Event(SUMA_EVENT *ev)
3719 {
3720    if (!ev) ev = SUMAg_CF->lev;
3721    if (!ev || !ev->set) return(0);
3722    if (!ev->Shift && ev->Control && !evALT) return(1);
3723    return(0);
3724 }
SUMA_Shft_Event(SUMA_EVENT * ev)3725 int SUMA_Shft_Event(SUMA_EVENT *ev)
3726 {
3727    if (!ev) ev = SUMAg_CF->lev;
3728    if (!ev || !ev->set) return(0);
3729    if (ev->Shift && !ev->Control && !evALT) return(1);
3730    return(0);
3731 }
SUMA_ShftContAlt_Event(SUMA_EVENT * ev)3732 int SUMA_ShftContAlt_Event(SUMA_EVENT *ev)
3733 {
3734    if (!ev) ev = SUMAg_CF->lev;
3735    if (!ev || !ev->set) return(0);
3736    if (ev->Shift && ev->Control && evALT ) return(1);
3737    return(0);
3738 }
SUMA_ContAlt_Event(SUMA_EVENT * ev)3739 int SUMA_ContAlt_Event(SUMA_EVENT *ev)
3740 {
3741    if (!ev) ev = SUMAg_CF->lev;
3742    if (!ev || !ev->set) return(0);
3743    if (!ev->Shift && ev->Control && evALT ) return(1);
3744    return(0);
3745 }
SUMA_ShftAlt_Event(SUMA_EVENT * ev)3746 int SUMA_ShftAlt_Event(SUMA_EVENT *ev)
3747 {
3748    if (!ev) ev = SUMAg_CF->lev;
3749    if (!ev || !ev->set) return(0);
3750    if (ev->Shift && !ev->Control && evALT ) return(1);
3751    return(0);
3752 }
SUMA_Alt_Event(SUMA_EVENT * ev)3753 int SUMA_Alt_Event(SUMA_EVENT *ev)
3754 {
3755    if (!ev) ev = SUMAg_CF->lev;
3756    if (!ev || !ev->set) return(0);
3757    if (!ev->Shift && !ev->Control && evALT) return(1);
3758    return(0);
3759 }
SUMA_Plain_Event(SUMA_EVENT * ev)3760 int SUMA_Plain_Event(SUMA_EVENT *ev)
3761 {
3762    if (!ev) ev = SUMAg_CF->lev;
3763    if (!ev || !ev->set) return(0);
3764    if (!ev->Shift && !ev->Control && !evALT) return(1);
3765    return(0);
3766 }
3767 
3768 /*! Get a record of events.
3769     Contents based on logic used in SUMA_input()
3770     Ideally, all should be done here and SUMA_input()
3771     should use the results of this call. But we're not
3772     there yet....
3773     Once I start using this one call, then the logic
3774     can be revisited at varied points in there.
3775 */
SUMA_RecordEvent(XEvent * event,SUMA_EVENT * ev)3776 SUMA_EVENT *SUMA_RecordEvent( XEvent *event,
3777                               SUMA_EVENT *ev)
3778 {
3779    static char FuncName[]={"SUMA_RecordEvent"};
3780    XKeyEvent Kev;
3781    XButtonEvent Bev;
3782    XMotionEvent Mev;
3783    static SUMA_EVENT lev;
3784    SUMA_Boolean LocalHead = NOPE;
3785 
3786    SUMA_ENTRY;
3787 
3788    if (!event) {
3789       SUMA_S_Err("Null event");
3790       if (ev) memset(ev, 0, sizeof(SUMA_EVENT));
3791       SUMA_RETURN(ev);
3792    }
3793 
3794    if (!ev) {
3795       if (!(ev = SUMAg_CF->lev)) {
3796          memset(&lev, 0, sizeof(SUMA_EVENT));
3797          SUMAg_CF->lev = (SUMA_EVENT *)SUMA_malloc(1*sizeof(SUMA_EVENT));
3798       }
3799       ev = SUMAg_CF->lev;
3800    }
3801    if (!ev) SUMA_RETURN(NULL);
3802    memset(ev, 0, sizeof(SUMA_EVENT));
3803 
3804    Kev = *(XKeyEvent *) &event->xkey; /* RickR's suggestion to comply with
3805                                  ANSI C, no type casting of structures  July 04*/
3806    Bev = *(XButtonEvent *) &event->xbutton;
3807    Mev = *(XMotionEvent *) &event->xmotion;
3808 
3809    ev->set = 1;
3810    /* The copied parameters */
3811    ev->ktype = Kev.type;
3812    ev->kstate = Kev.state;
3813    ev->mtype = Mev.type;
3814    ev->mstate = Mev.state;
3815    ev->bTime = Bev.time;
3816    ev->mTime = Mev.time;
3817    ev->bButton = Bev.button;
3818    ev->mButton = 0; /* Not sure this one was all that necessary */
3819    ev->mX = Mev.x;
3820    ev->mY = Mev.y;
3821    ev->bX = Bev.x;
3822    ev->bY = Bev.y;
3823 
3824    /* The inferred parameters */
3825    if ((Kev.state & ShiftMask) || (Bev.state & ShiftMask)) ev->Shift = 1;
3826    if ((Kev.state & ControlMask) || (Bev.state & ControlMask)) ev->Control = 1;
3827    if (Kev.state & Mod1Mask) ev->Mod1 = 1;
3828    if (Kev.state & Mod2Mask) ev->Mod2 = 1;
3829    if (Kev.state & Mod3Mask) ev->Mod3 = 1;
3830    if (Kev.state & Mod4Mask) ev->Mod4 = 1;
3831    if (Kev.state & Mod5Mask) ev->Mod5 = 1;
3832    if (Kev.state & SUMA_APPLE_AltOptMask) ev->AppleAltOpt = 1;
3833 
3834    switch(ev->ktype) {
3835       case KeyPress:
3836          ev->transl[0] = ev->transl[15] = '\0';
3837          XLookupString( (XKeyEvent *)event, ev->transl, 14,
3838                               &ev->keysym, NULL);
3839          break;
3840          break;
3841       case ButtonPress:
3842          if (Bev.state & Button1Mask) ev->b1 = 1;
3843          if (Bev.state & Button2Mask) ev->b2 = 1;
3844          if (Bev.state & Button3Mask) ev->b3 = 1;
3845          if (Bev.state & Button4Mask) ev->b4 = 1;
3846          if (Bev.state & Button5Mask) ev->b5 = 1;
3847          if (ev->bButton == 6) ev->b6 = 1; /* on macs Button6 not in X.h */
3848          if (ev->bButton == 7) ev->b7 = 1; /* on macs Button7 not in X.h */
3849 
3850          switch (ev->bButton) {
3851             case Button1:
3852             case Button2:
3853             case Button3:
3854             case Button4:
3855             case Button5:
3856             case 6:
3857             case 7:
3858             default:
3859                SUMA_LH("ButtonPress %d not known", ev->bButton);
3860                break;
3861          }
3862 
3863          /* trap for double click */
3864          if (Bev.time - lev.bTime < SUMA_DOUBLE_CLICK_MAX_DELAY) {
3865             ev->DoubleClick = YUP;
3866          } else {
3867             ev->DoubleClick = NOPE;
3868          }
3869 
3870          if (  SUMAg_CF->SwapButtons_1_3 ||
3871                (SUMAg_CF->ROI_mode && SUMAg_CF->Pen_mode)) {
3872             int kk=ev->b1;
3873             ev->b1 = ev->b3;
3874             ev->b3 = kk;
3875          }
3876 
3877          break;
3878       case ButtonRelease:
3879          ev->mTime = 0;
3880          if (Bev.state & Button1Mask) ev->b1 = 1;
3881          if (Bev.state & Button2Mask) ev->b2 = 1;
3882          if (Bev.state & Button3Mask) ev->b3 = 1;
3883          if (Bev.state & Button4Mask) ev->b4 = 1;
3884          if (Bev.state & Button5Mask) ev->b5 = 1;
3885          if (ev->bButton == 6) ev->b6 = 1; /* on macs Button6 not in X.h */
3886          if (ev->bButton == 7) ev->b7 = 1; /* on macs Button7 not in X.h */
3887 
3888          if (SUMAg_CF->SwapButtons_1_3 ||
3889              (SUMAg_CF->ROI_mode && SUMAg_CF->Pen_mode)) {
3890             int kk=ev->b1;
3891             ev->b1 = ev->b3;
3892             ev->b3 = kk;
3893          }
3894          break;
3895 
3896       case MotionNotify:
3897          if (Mev.state & Button1MotionMask) ev->b1m = 1;
3898          if (Mev.state & Button2MotionMask) ev->b2m = 1;
3899          if (Mev.state & Button3MotionMask) ev->b3m = 1;
3900          if (Mev.state & Button4MotionMask) ev->b4m = 1;
3901          if (Mev.state & Button5MotionMask) ev->b5m = 1;
3902          if (Mev.state) { SUMA_LH("   Something mot\n"); }
3903 
3904          /* The conditions and new assignments of mButton
3905          below is stupid. But I won't touch it until I
3906          have to. Reassignments should be to b1m, b2m, etc.
3907          mButton should not be touched.
3908          Also, there should be no need for these numerous
3909          conditions. If swapping is needed, b1m and b3m
3910          values should be swaped. Things like SUMA_Button_12_Motion
3911          should be made into functions that return an answer
3912          based on ev's contents */
3913          if (  SUMAg_CF->SwapButtons_1_3 ||
3914                (SUMAg_CF->ROI_mode && SUMAg_CF->Pen_mode)) {
3915            if (((Mev.state & Button3MotionMask) &&
3916                                              (Mev.state & Button2MotionMask))
3917             || ((Mev.state & Button2MotionMask) && (Mev.state & ShiftMask))) {
3918                int kk=ev->b1m;
3919                ev->b1m = ev->b3m;
3920                ev->b3m = kk;
3921                ev->mButton = SUMA_Button_12_Motion;
3922             } else if(Mev.state & Button3MotionMask) {
3923                ev->mButton = SUMA_Button_1_Motion;
3924                int kk=ev->b1m;
3925                ev->b1m = ev->b3m;
3926                ev->b3m = kk;
3927             }else if(Mev.state & Button2MotionMask) {
3928                ev->mButton = SUMA_Button_2_Motion;
3929             }else if(Mev.state & Button1MotionMask) {
3930 
3931                ev->mButton = SUMA_Button_3_Motion;
3932             }else {
3933                break;
3934             }
3935          } else {
3936             if (((Mev.state & Button1MotionMask) &&
3937                                                 (Mev.state & Button2MotionMask))
3938              || ((Mev.state & Button2MotionMask) && (Mev.state & ShiftMask))) {
3939                ev->mButton = SUMA_Button_12_Motion;
3940             } else if(Mev.state & Button1MotionMask) {
3941                ev->mButton = SUMA_Button_1_Motion;
3942             }else if(Mev.state & Button2MotionMask) {
3943                ev->mButton = SUMA_Button_2_Motion;
3944             } else if(Mev.state & Button3MotionMask) {
3945                ev->mButton = SUMA_Button_3_Motion;
3946             }else {
3947                break;
3948             }
3949          }
3950          switch (ev->mButton) {
3951             case SUMA_Button_12_Motion:
3952             case SUMA_Button_2_Shift_Motion:
3953                if (ev->mTime) {
3954                   ev->mDelta = Mev.time - lev.mTime;
3955                   ev->mDeltaX = Mev.x - lev.mX;
3956                   ev->mDeltaY = Mev.y - lev.mY;
3957                } else {
3958                   ev->mDelta  = 0;
3959                   ev->mDeltaX = 0;
3960                   ev->mDeltaY = 0;
3961                }
3962                break;
3963             case SUMA_Button_2_Motion:
3964                break;
3965             case SUMA_Button_3_Motion:
3966                break;
3967       }
3968 
3969    }
3970    if (LocalHead) {
3971       SUMA_ShowEvent(ev, 0, "EventRecord:\n");
3972       SUMA_ShowEvent(ev, 1, "EventRecord:\n");
3973    } else if (SUMAg_CF->Echo_KeyPress) {
3974       SUMA_ShowEvent(ev, 1, "EventRecord:\n");
3975    }
3976 
3977    /* keep local copy of last event */
3978    memcpy(&lev, ev, sizeof(SUMA_EVENT));
3979 
3980    SUMA_RETURN(ev);
3981 }
3982 
3983 /*! Mouse and Keyboard input handler function for SUMA's viewer
3984     START shifting to SUMA_RecordEvent() and its helper functions
3985     like SUMA_Alt_Event(), etc.
3986     You should also split SUMA_input() into SUMA_input_Xevent()
3987     and SUMA_input_eng(). Where SUMA_input_Xevent() just sets
3988     SUMAg_CF->lev and SUMA_input_eng() works entirely off of
3989     SUMAg_CF->lev . This way you would be able to completely
3990     drive SUMA_input_eng() without any mouse movement/X structs
3991     etc. All you need is to manipulate the content of lev.
3992 */
SUMA_input(Widget w,XtPointer clientData,XtPointer callData)3993 void SUMA_input(Widget w, XtPointer clientData, XtPointer callData)
3994 {
3995    static char FuncName[]= {"SUMA_input"};
3996    GLwDrawingAreaCallbackStruct *cd;
3997    char buffer[10], cbuf = '\0', cbuf2='\0';
3998    KeySym keysym;
3999    int xls=-1, ntot, id = 0, ND, ip, NP;
4000    SUMA_EngineData *ED = NULL;
4001    char CommString[SUMA_MAX_COMMAND_LENGTH];
4002    char s[SUMA_MAX_STRING_LENGTH], sfield[100], sdestination[100];
4003    static char ssource[]={"suma"};
4004    int it, ii, iv3[3], hit = 0, SwasHit = 0;
4005    float **fm, fv3[3], fv15[15];
4006    XKeyEvent Kev;
4007    XButtonEvent Bev;
4008    XMotionEvent Mev;
4009    int isv;
4010    SUMA_SurfaceViewer *sv, *svi = NULL;
4011    GLfloat *glar_ColorList = NULL;
4012    static Time B1time = 0, M1time=0, M1delta=0;
4013    static int pButton, mButton, rButton;
4014    SUMA_Boolean ROI_mode;
4015    static SUMA_Boolean DoubleClick = NOPE;
4016    DList *list = NULL;
4017    DListElmt *NextElm= NULL;
4018    float bevx, bevy, mevx, mevy, wwid, whei, zc_fac, mvx_fac, mvy_fac;
4019    static int mvxlast, mvylast, mvdeltax, mvdeltay;
4020    SUMA_PROMPT_DIALOG_STRUCT *prmpt=NULL; /* Use this only to create prompt
4021                                              that are not to be preserved */
4022    SUMA_Boolean LocalHead = NOPE; /* local debugging messages */
4023 
4024    SUMA_ENTRY;
4025 
4026    /* get the callData pointer */
4027    cd = (GLwDrawingAreaCallbackStruct *) callData;
4028 
4029    /* find out who's calling, only GLXAREA calls this function */
4030    SUMA_GLXAREA_WIDGET2SV(w, sv, isv);
4031    if (isv < 0) {
4032       fprintf (SUMA_STDERR,
4033                "Error %s: Failed in macro SUMA_GLXAREA_WIDGET2SV.\n", FuncName);
4034       SUMA_RETURNe;
4035    }
4036    SUMA_LH("A call from SUMA_SurfaceViewer[%d], Pointer %p\n", isv, sv);
4037 
4038    /* ******** ABOUT EVENT HANDLING ************** */
4039    /* Eventually you should use the structure
4040       created by RecordEvent to decide on what was clicked
4041       and how. While a little less efficient than
4042       what is done below, RecordEvent will eventually
4043       be considerably more flexible, and easier to debug.
4044       But for now, so that visible progress can be made,
4045       I will leave button processing and queries below
4046       as is, and try to make the switch gradually */
4047 
4048    SUMAg_CF->lev = SUMA_RecordEvent( cd->event, SUMAg_CF->lev);
4049 
4050    Kev = *(XKeyEvent *) &cd->event->xkey; /* RickR's suggestion to comply with
4051                                  ANSI C, no type casting of structures  July 04*/
4052    Bev = *(XButtonEvent *) &cd->event->xbutton;
4053    Mev = *(XMotionEvent *) &cd->event->xmotion;
4054 
4055    /* a sample keypresses */
4056    if (SUMAg_CF->Echo_KeyPress) {
4057       if (Kev.type == KeyPress) {
4058          buffer[0] = '\0';
4059          xls = XLookupString((XKeyEvent *) cd->event, buffer, 8, &keysym, NULL);
4060          fprintf (SUMA_STDERR,"%s KeyPress char>>%s<< sym>>%d<< ",
4061                   FuncName, buffer, (int)keysym);
4062       } else {
4063          fprintf (SUMA_STDERR,"%s Mouse Action: ", FuncName);
4064       }
4065       if (Kev.state & ShiftMask) {
4066          fprintf (SUMA_STDERR,"Shift ");
4067       }
4068       if (Kev.state & ControlMask){
4069          fprintf (SUMA_STDERR,"Control ");
4070       }
4071       if (Kev.state & Mod1Mask){
4072          fprintf (SUMA_STDERR,"alt ");
4073       }
4074       if (Kev.state & Mod2Mask){
4075          fprintf (SUMA_STDERR,"Mod2 (command on mac) ");
4076       }
4077       if (Kev.state & Mod3Mask){
4078          fprintf (SUMA_STDERR,"Mod3 ");
4079       }
4080       if (Kev.state & Mod4Mask){
4081          fprintf (SUMA_STDERR,"Mod4 ");
4082       }
4083       if (Kev.state & Mod5Mask){
4084          fprintf (SUMA_STDERR,"Mod5 ");
4085       }
4086       if (Kev.state & SUMA_APPLE_AltOptMask){
4087          fprintf (SUMA_STDERR,"Apple Alt/Opt ");
4088       }
4089       fprintf (SUMA_STDERR,"State %d\n\n", Kev.state);
4090   }
4091 
4092   switch (Kev.type) { /* switch event type */
4093   case KeyPress:
4094       if (xls < 0) {
4095          /* avoid double call in case SUMAg_CF->Echo_KeyPress called already */
4096          xls = XLookupString((XKeyEvent *) cd->event, buffer, 8, &keysym, NULL);
4097       }
4098       /* XK_* are found in keysymdef.h */
4099       switch (keysym) { /* keysym */
4100          case XK_bracketleft: /* The left bracket */
4101             if (!SUMA_bracketleft_Key(sv, "[", "interactive")) {
4102                SUMA_S_Err("Failed in key func.");
4103             }
4104             break;
4105 
4106          case XK_bracketright: /* The right bracket */
4107             if (!SUMA_bracketright_Key(sv, "]", "interactive")) {
4108                SUMA_S_Err("Failed in key func.");
4109             }
4110             break;
4111 
4112          case XK_space:   /* The spacebar. */
4113             if (!SUMA_space_Key(sv, "SPACE", "interactive")) {
4114                SUMA_S_Err("Failed in key func.");
4115             }
4116             break;
4117             /* toggle between state containing mapping reference
4118                of SO in focus and other view */
4119             {
4120                SUMA_SurfaceObject *SO = NULL, *SOmap = NULL;
4121                int curstateID = -1, nxtstateID = -1, dov_ID = -1;
4122 
4123                /* make sure switching is OK */
4124                curstateID = SUMA_WhichState(sv->State, sv, sv->CurGroupName);
4125                if ((SO = SUMA_SV_Focus_SO(sv))) {
4126                   if (SUMA_isLocalDomainParent (SO)) {
4127                      /* get the last non mappable state in SV */
4128                      if (sv->LastNonMapStateID < 0) {
4129                               /* not recorded, complain and quit */
4130                         SUMA_S_Warn("Nothing defined to toggle with yet.");
4131                         break;
4132                      }
4133 
4134                      SUMA_LHv("surface is inherrently mappable, "
4135                               "switching to last non mappable state %d.\n",
4136                               sv->LastNonMapStateID);
4137 
4138                      if (!SUMA_SwitchState (SUMAg_DOv, SUMAg_N_DOv, sv,
4139                               sv->LastNonMapStateID, sv->CurGroupName)) {
4140                         SUMA_S_Err("Failed in SUMA_SwitchState.");
4141                         break;
4142                      }
4143 
4144                   } else {/* non mappable, go to state containing reference */
4145                      SUMA_LH("surface is not inherrently mappable, "
4146                              "searching for mapping reference and its state.");
4147 
4148                      /* find SO that is mappable reference &
4149                         get corresponding state ID */
4150                      dov_ID = SUMA_findSO_inDOv(SO->LocalDomainParentID,
4151                                                 SUMAg_DOv, SUMAg_N_DOv);
4152                      SOmap = (SUMA_SurfaceObject *)SUMAg_DOv[dov_ID].OP;
4153                      nxtstateID = SUMA_WhichState(SOmap->State, sv,
4154                                                   sv->CurGroupName);
4155 
4156                      if (nxtstateID < 0) {
4157                         SUMA_S_Err("Failed in SUMA_findSO_inDOv."
4158                                    "This should not happen.");
4159                         break;
4160                      }
4161 
4162                      SUMA_LHv("Found mapping reference in viewer state %d.\n",
4163                               nxtstateID);
4164 
4165                      /* store this location */
4166                      sv->LastNonMapStateID = curstateID;
4167 
4168                      /* go there */
4169                      if (!SUMA_SwitchState (SUMAg_DOv, SUMAg_N_DOv, sv,
4170                                             nxtstateID, sv->CurGroupName)) {
4171                         SUMA_S_Err("Failed in SUMA_SwitchState.");
4172                         break;
4173                      }
4174                   }
4175                }
4176             }
4177             SUMA_postRedisplay(w, clientData, callData);
4178             break;
4179 
4180          case XK_Escape: /* there's more:
4181                   XK_BackSpace XK_Tab XK_Linefeed XK_Return XK_Delete */
4182             /* control mask and escape is grabbed by gnome window manager .... */
4183             if (Kev.state & ShiftMask){/* kill all */
4184                if( SUMAg_CF->X->WarnClose) {
4185                   if (SUMA_ForceUser_YesNo(sv->X->TOPLEVEL,
4186                            "Close All Viewers?", SUMA_YES,
4187                            SWP_DONT_CARE) != SUMA_YES) {
4188                      break;
4189                   }
4190                }
4191                XtCloseDisplay( SUMAg_CF->X->DPY_controller1 ) ;
4192                exit(0);
4193             }else {
4194                if( SUMAg_CF->X->WarnClose) {
4195                   #ifdef DARWIN
4196                      if (SUMA_ForceUser_YesNo(sv->X->TOPLEVEL,
4197                                  "Close This Viewer?\n"
4198                                  "OS-X users: If answering YES,\n"
4199                                  "this prompt should not lie \n"
4200                                  "over viewer to be closed.\n"
4201                                  "Blame Bill Gates for this bug.",
4202                                   SUMA_YES, SWP_TOP_RIGHT) != SUMA_YES) {
4203                         break;
4204                      }
4205                   #else
4206                      if (SUMA_ForceUser_YesNo(sv->X->TOPLEVEL,
4207                                  "Close This Viewer?", SUMA_YES,
4208                                  SWP_DONT_CARE) != SUMA_YES) {
4209                         break;
4210                      }
4211                   #endif
4212                }
4213                SUMA_ButtClose_pushed (w, clientData, callData);
4214             }
4215             break;
4216 
4217          case XK_a:
4218             if (!SUMA_A_Key(sv, "a", "interactive")) {
4219                SUMA_S_Err("Failed in key func.");
4220             }
4221             break;
4222 
4223          case XK_B:
4224                if (( Kev.state & ControlMask)){
4225                   if (SUMAg_CF->Dev ) {
4226                      if (!SUMA_B_Key(sv, "ctrl+B", "interactive")) {
4227                         SUMA_S_Err("Failed in key func.");
4228                      }
4229                   }
4230                } else {
4231                   if (!SUMA_B_Key(sv, "B", "interactive")) {
4232                      SUMA_S_Err("Failed in key func.");
4233                   }
4234                }
4235             break;
4236 
4237          case XK_b:
4238             if (!SUMA_B_Key(sv, "b", "interactive")) {
4239                SUMA_S_Err("Failed in key func.");
4240             }
4241             break;
4242 
4243          case XK_C:
4244             if (SUMAg_CF->Dev && (SUMA_ALTHELL)){
4245                SUMAg_CF->X->ClipObj_prmpt =
4246                   SUMA_CreatePromptDialogStruct (SUMA_OK_APPLY_CLEAR_CANCEL,
4247                               "Enter object clip plane parameters (a,b,c,d)",
4248                               "A: 0,0,1,0",
4249                               sv->X->TOPLEVEL, YUP,
4250                               SUMA_APPLY_BUTTON,
4251                               SUMA_SetObjectClip, (void *)sv,
4252                               NULL, NULL,
4253                               NULL, NULL,
4254                               NULL, NULL,
4255                               SUMAg_CF->X->ClipObj_prmpt);
4256 
4257                SUMAg_CF->X->ClipObj_prmpt =
4258                   SUMA_CreatePromptDialog(
4259                      "Enter object clip plane parameters (a,b,c,d)",
4260                      SUMAg_CF->X->ClipObj_prmpt);
4261             } else if (SUMAg_CF->Dev && (Kev.state & ControlMask)){
4262                SUMAg_CF->X->Clip_prmpt =
4263                   SUMA_CreatePromptDialogStruct (SUMA_OK_APPLY_CLEAR_CANCEL,
4264                      "Enter screen clip plane parameters (a,b,c,d)",
4265                      "A: 0,0,1,0",
4266                      sv->X->TOPLEVEL, YUP,
4267                      SUMA_APPLY_BUTTON,
4268                      SUMA_SetScreenClip, (void *)sv,
4269                      NULL, NULL,
4270                      NULL, NULL,
4271                      NULL, NULL,
4272                      SUMAg_CF->X->Clip_prmpt);
4273 
4274                SUMAg_CF->X->Clip_prmpt =
4275                   SUMA_CreatePromptDialog(
4276                      "Enter screen clip plane parameters (a,b,c,d)",
4277                      SUMAg_CF->X->Clip_prmpt);
4278             }
4279             break;
4280          case XK_c:
4281             {
4282                SUMA_SurfaceObject *SO=NULL;
4283                if ((SO = SUMA_SV_Focus_SO(sv))) {
4284                   if (!list) list = SUMA_CreateList();
4285                   ED = SUMA_InitializeEngineListData (SE_OpenColFileSelection);
4286                   if (!(NextElm = SUMA_RegisterEngineListCommand (  list, ED,
4287                                     SEF_vp, (void *)SO,
4288                                     SES_Suma, (void *)sv, NOPE,
4289                                     SEI_Head, NULL))) {
4290                      SUMA_S_Err("Failed to register command.");
4291                   }
4292 
4293                   if (!SUMA_RegisterEngineListCommand (  list, ED,
4294                                                 SEF_ip, sv->X->TOPLEVEL,
4295                                                 SES_Suma, (void *)sv, NOPE,
4296                                                 SEI_In, NextElm)) {
4297                      SUMA_S_Err("Failed to register command.");
4298                   }
4299 
4300                   if (!SUMA_Engine (&list)) {
4301                      fprintf(SUMA_STDERR,
4302                               "Error %s: SUMA_Engine call failed.\n", FuncName);
4303                   }
4304                }
4305             }
4306 
4307             break;
4308 
4309             #if 0
4310             /* THE OLD WAY (part of it) FOR SETTING NODE COLORS DIRECTLY,
4311                Left here for documentation */
4312             /* allocate space */
4313             fm = (float **)SUMA_allocate2D (ntot/4, 4, sizeof(float));
4314             if (fm == NULL) {
4315                fprintf(stderr,
4316                         "Error SUMA_input: Failed to allocate space for fm\n");
4317                SUMA_RETURNe;
4318             }
4319 
4320             if (SUMA_Read_2Dfile (s, fm, 4, ntot/4) != ntot/4 ) {
4321                fprintf(stderr,
4322                         "SUMA_input Error: Failed to read full matrix from %s\n",
4323                         s);
4324                SUMA_RETURNe;
4325             }
4326 
4327             if (!list) list = SUMA_CreateList();
4328             ED = SUMA_InitializeEngineListData (SE_SetNodeColor);
4329             ED->N_cols = 4;
4330             ED->N_rows = ntot/4;
4331             if (!SUMA_RegisterEngineListCommand (  list, ED,
4332                                                    SEF_fm, (void*)fm,
4333                                                    SES_Suma, (void *)sv, YUP,
4334                                                    SEI_Head, NULL)) {
4335                fprintf(SUMA_STDERR,"Error %s: Failed to register element\n",
4336                         FuncName);
4337                break;
4338             }
4339 
4340 
4341             SUMA_REGISTER_HEAD_COMMAND_NO_DATA(list, SE_Redisplay, SES_Suma, sv);
4342 
4343             if (!SUMA_Engine (&list)) {
4344                fprintf(SUMA_STDERR, "Error %s: SUMA_Engine call failed.\n",
4345                         FuncName);
4346             }
4347 
4348             /* free fm since it was registered by pointer and is not
4349                automatically freed after the call to SUMA_Engine */
4350             if (fm) SUMA_free2D ((char **)fm, ntot/4);
4351             break;
4352             #endif
4353 
4354          case XK_D:
4355             if (1) {
4356                if (SUMA_ALTHELL){
4357                   /*  Mod1Mask is alt in linux, Mod2Mask is the apple on mac*/
4358                } else {
4359                   if (!SUMA_D_Key(sv,"D", "interactive")) {
4360                      SUMA_S_Err("Failed in key func.");
4361                   }
4362                }
4363             }
4364             break;
4365 
4366          case XK_d:
4367             if (SUMAg_CF->Dev) {
4368                if (SUMA_ALTHELL){
4369                   /*  Mod1Mask is alt in linux, Mod2Mask is the apple on mac*/
4370                } else {
4371                   if (!SUMA_D_Key(sv,"d", "interactive")) {
4372                      SUMA_S_Err("Failed in key func.");
4373                   }
4374                }
4375             }
4376             break;
4377 
4378          case XK_e:
4379          case XK_dead_acute:  /* that is alt/option+e on macs
4380                                XK_dead_acute is 0xfe51 or decimal 65105 */
4381             if (SUMAg_CF->Dev) {
4382                if (SUMA_ALTHELL){ /* Mod1Mask is alt in linux,
4383                                      Mod2Mask is the apple on mac*/
4384                   SUMA_GL_ERRS;
4385                }
4386             }
4387             break;
4388 
4389          case XK_F:
4390             /* flip light position */
4391             if (!list) list = SUMA_CreateList();
4392             SUMA_REGISTER_HEAD_COMMAND_NO_DATA(list, SE_FlipLight0Pos,
4393                                                 SES_Suma, sv);
4394             SUMA_REGISTER_HEAD_COMMAND_NO_DATA(list, SE_Redisplay, SES_Suma, sv);
4395 
4396             if (!SUMA_Engine (&list)) {
4397                fprintf(stderr, "Error SUMA_input: SUMA_Engine call failed.\n");
4398             }
4399             break;
4400 
4401          case XK_f:
4402             /* Show/hide the foreground */
4403             if (!list) list = SUMA_CreateList();
4404             SUMA_REGISTER_HEAD_COMMAND_NO_DATA(list, SE_ToggleForeground,
4405                                                 SES_Suma, sv);
4406             SUMA_REGISTER_HEAD_COMMAND_NO_DATA(list, SE_Redisplay, SES_Suma, sv);
4407 
4408             if (!SUMA_Engine (&list)) {
4409                fprintf(stderr, "Error SUMA_input: SUMA_Engine call failed.\n");
4410             }
4411             break;
4412 
4413          case XK_H:
4414                sv->X->HighlightBox_prmpt =
4415                   SUMA_CreatePromptDialogStruct(SUMA_OK_APPLY_CLEAR_CANCEL,
4416                                              "Enter XYZ of box's center\n"
4417                                              "followed by it's size (6 values)",
4418                                              "",
4419                                              sv->X->TOPLEVEL, YUP,
4420                                              SUMA_APPLY_BUTTON,
4421                                              SUMA_HighlightBox, (void *)sv,
4422                                              NULL, NULL,
4423                                              NULL, NULL,
4424                                              SUMA_CleanNumString, (void*)6,
4425                                              sv->X->HighlightBox_prmpt);
4426 
4427                sv->X->HighlightBox_prmpt = SUMA_CreatePromptDialog(sv->X->Title,
4428                                                       sv->X->HighlightBox_prmpt);
4429 
4430             break;
4431          case XK_g:
4432             if (Kev.state & ControlMask){
4433                   if (SUMAg_CF->Dev) {
4434                      if (!SUMA_G_Key(sv, "ctrl+g", "interactive")) {
4435                         SUMA_S_Err("Failed in key func.");
4436                      }
4437                   }
4438                } else {
4439                   if (!SUMA_G_Key(sv, "g", "interactive")) {
4440                      SUMA_S_Err("Failed in key func.");
4441                   }
4442                }
4443              break;
4444          case XK_h:
4445             if (Kev.state & ControlMask){
4446               if (!list) list = SUMA_CreateList();
4447               SUMA_REGISTER_HEAD_COMMAND_NO_DATA(list, SE_Help, SES_Suma, NULL);
4448               if (!SUMA_Engine (&list)) {
4449                   fprintf( stderr,
4450                            "Error %s: SUMA_Engine call failed.\n", FuncName);
4451               }
4452             }else{
4453                if (SUMAg_CF->Dev) {
4454                   SUMA_SLP_Note("Please use ctrl+h for help.\n"
4455                                 "h alone will be reassigned\n"
4456                                 "in future versions.");
4457                }
4458             }
4459             break;
4460 
4461          case XK_j:
4462                if (Kev.state & ControlMask){
4463                   if (!SUMA_J_Key(sv, "ctrl+j", "interactive", NULL)) {
4464                      SUMA_S_Err("Failed in key func.");
4465                   }
4466                } else if (SUMA_ALTHELL){
4467                   if (!SUMA_J_Key(sv, "alt+j", "interactive", NULL)) {
4468                      SUMA_S_Err("Failed in key func.");
4469                   }
4470                } else {
4471                   if (!SUMA_J_Key(sv, "j", "interactive", NULL)) {
4472                      SUMA_S_Err("Failed in key func.");
4473                   }
4474                }
4475             break;
4476 
4477          case XK_J:
4478                if (!SUMA_J_Key(sv, "J", "interactive", NULL)) {
4479                      SUMA_S_Err("Failed in key func.");
4480                }
4481             break;
4482 
4483          case XK_l:
4484                if (Kev.state & ControlMask){
4485                   if (!SUMA_L_Key(sv, "ctrl+l", "interactive", NULL)) {
4486                         SUMA_S_Err("Failed in key func.");
4487                   }
4488                } else if (SUMA_ALTHELL){
4489                   if (!SUMA_L_Key(sv, "alt+l", "interactive", NULL)) {
4490                      SUMA_S_Err("Failed in key func.");
4491                   }
4492                } else {
4493                   if (!SUMA_L_Key(sv, "l", "interactive", NULL)) {
4494                         SUMA_S_Err("Failed in key func.");
4495                   }
4496                }
4497             break;
4498 
4499          case XK_L:
4500                if (Kev.state & ControlMask){
4501                   if (!SUMA_L_Key(sv, "ctrl+L", "interactive", NULL)) {
4502                         SUMA_S_Err("Failed in key func.");
4503                   }
4504                } else if (SUMA_ALTHELL){
4505                   if (!SUMA_L_Key(sv, "alt+L", "interactive", NULL)) {
4506                      SUMA_S_Err("Failed in key func.");
4507                   }
4508                } else {
4509                   if (!SUMA_L_Key(sv, "L", "interactive", NULL)) {
4510                         SUMA_S_Err("Failed in key func.");
4511                   }
4512                }
4513             break;
4514 
4515          case XK_M:
4516             if ((SUMA_ALTHELL) && (Kev.state & ControlMask) ){
4517                   if (!SUMA_M_Key(sv, "alt+ctrl+M", "interactive")) {
4518                      SUMA_S_Err("Failed in key func.");
4519                   }
4520             }
4521             break;
4522 
4523          case XK_m:
4524                if (Kev.state & ControlMask){
4525                   if (SUMAg_CF->Dev) {
4526                      if (!SUMA_M_Key(sv, "ctrl+m", "interactive")) {
4527                         SUMA_S_Err("Failed in key func.");
4528                      }
4529                   }
4530                } else {
4531                   if (!SUMA_M_Key(sv, "m", "interactive")) {
4532                      SUMA_S_Err("Failed in key func.");
4533                   }
4534                }
4535              break;
4536 
4537          case XK_n:
4538                if (Kev.state & ControlMask){
4539                   SUMA_LH("Going to N_Key");
4540                   if (!SUMA_N_Key(sv, "ctrl+n", "interactive")) {
4541                      SUMA_S_Err("Failed in key func.");
4542                   }
4543                }else {
4544                   if (SUMAg_CF->Dev) {
4545                      if (!SUMA_N_Key(sv, "n", "interactive")) {
4546                         SUMA_S_Err("Failed in key func.");
4547                      }
4548                   }
4549                }
4550             break;
4551          case XK_o:
4552             if (SUMA_ALTHELL) {
4553                if (!SUMA_O_Key(sv, "alt+o", "interactive")) {
4554                   SUMA_S_Err("Failed in key func.");
4555                }
4556             } else if (Kev.state & ControlMask){
4557                if (!SUMA_O_Key(sv, "ctrl+o", "interactive")) {
4558                   SUMA_S_Err("Failed in key func.");
4559                }
4560             } else {
4561                if (!SUMA_O_Key(sv, "o", "interactive")) {
4562                   SUMA_S_Err("Failed in key func.");
4563                }
4564             }
4565             break;
4566          case XK_O:
4567             if (SUMA_ALTHELL) {
4568                if (!SUMA_O_Key(sv, "alt+O", "interactive")) {
4569                   SUMA_S_Err("Failed in key func.");
4570                }
4571             } else if (Kev.state & ControlMask){
4572                if (!SUMA_O_Key(sv, "ctrl+O", "interactive")) {
4573                   SUMA_S_Err("Failed in key func.");
4574                }
4575             } else {
4576                if (!SUMA_O_Key(sv, "O", "interactive")) {
4577                   SUMA_S_Err("Failed in key func.");
4578                }
4579             }
4580             break;
4581          case XK_p:
4582             if (Kev.state & ControlMask){
4583                if (!SUMA_P_Key(sv, "ctrl+p", "interactive")) {
4584                   SUMA_S_Err("Failed in key func.");
4585                }
4586             } else {
4587                if (!SUMA_P_Key(sv, "p", "interactive")) {
4588                   SUMA_S_Err("Failed in key func.");
4589                }
4590             }
4591             break;
4592 
4593          case XK_P:
4594             if (!SUMA_P_Key(sv, "P", "interactive")) {
4595                SUMA_S_Err("Failed in key func.");
4596             }
4597             break;
4598 
4599          case XK_r:
4600             if (SUMA_ALTHELL) {
4601                if (!SUMA_R_Key(sv, "alt+r", "interactive")) {
4602                   SUMA_S_Err("Failed in key func.");
4603                }
4604             } else if (Kev.state & ControlMask){
4605                if (!SUMA_R_Key(sv, "ctrl+r", "interactive")) {
4606                   SUMA_S_Err("Failed in key func.");
4607                }
4608             } else {
4609                if (!SUMA_R_Key(sv, "r", "interactive")) {
4610                   SUMA_S_Err("Failed in key func.");
4611                }
4612             }
4613             break;
4614 
4615          case XK_R:
4616             if (Kev.state & ControlMask){
4617                if (!SUMA_R_Key(sv, "ctrl+R", "interactive")) {
4618                      SUMA_S_Err("Failed in key func.");
4619                }
4620             } else {
4621                if (!SUMA_R_Key(sv, "R", "interactive")) {
4622                      SUMA_S_Err("Failed in key func.");
4623                }
4624             }
4625             break;
4626 
4627          case XK_S:
4628             if (SUMAg_CF->Dev) {
4629                int *do_id, n_do_id;
4630                do_id = SUMA_GetDO_Type(SUMAg_DOv, SUMAg_N_DOv,
4631                                        SO_type, &n_do_id);
4632                if (n_do_id) {
4633                   while (n_do_id) {
4634                      SUMA_Print_Surface_Object(
4635                         (SUMA_SurfaceObject *)SUMAg_DOv[do_id[n_do_id-1]].OP,
4636                         stdout);
4637                      --n_do_id;
4638                   }
4639                   SUMA_free(do_id);
4640                }
4641                break;
4642             }
4643 
4644          case XK_s:
4645             if ((SUMA_ALTHELL) && (Kev.state & ControlMask) ){
4646                if (!list) list = SUMA_CreateList();
4647                ED = SUMA_InitializeEngineListData (SE_LoadSegDO);
4648                if (!SUMA_RegisterEngineListCommand (  list, ED,
4649                                           SEF_ip, sv->X->TOPLEVEL,
4650                                           SES_Suma, (void *)sv, NOPE,
4651                                           SEI_Head, NULL)) {
4652                   fprintf (SUMA_STDERR,
4653                            "Error %s: Failed to register command.\n", FuncName);
4654                }
4655                if (!SUMA_Engine (&list)) {
4656                      fprintf( SUMA_STDERR,
4657                               "Error %s: SUMA_Engine call failed.\n",
4658                               FuncName);
4659                }
4660 
4661             } else if (SUMA_ALTHELL){
4662                /* swap buttons 1 and 3 */
4663                SUMAg_CF->SwapButtons_1_3 = !SUMAg_CF->SwapButtons_1_3;
4664                if (SUMAg_CF->SwapButtons_1_3) {
4665                   fprintf (SUMA_STDOUT,
4666                            "%s: Buttons 1 and 3 are swapped.\n", FuncName);
4667                } else {
4668                   fprintf (SUMA_STDOUT,
4669                            "%s: Default functions for buttons 1 and 3.\n",
4670                            FuncName);
4671                }
4672             } else if (SUMAg_CF->Dev) {
4673                #if 0
4674                /** Feb 03/03 No longer in use.*/
4675                for (ii=0; ii< sv->N_DO; ++ii) {
4676                   if (SUMA_isSO(SUMAg_DOv[sv->RegisteredDO[ii]]))
4677                      SUMA_Print_Surface_Object(
4678                         (SUMA_SurfaceObject*)SUMAg_DOv[sv->RegisteredDO[ii]].OP,
4679                         stdout);
4680                }
4681                #endif
4682             }
4683             break;
4684 
4685          case XK_t:
4686             if ((Kev.state & ControlMask)){
4687                if (!SUMA_T_Key(sv, "ctrl+t", "interactive")) {
4688                   SUMA_S_Err("Failed in key func.");
4689                }
4690             } else {
4691                if (!SUMA_T_Key(sv, "t", "interactive")) {
4692                   SUMA_S_Err("Failed in key func.");
4693                }
4694             }
4695             break;
4696 
4697          case XK_T:
4698             if (!SUMA_T_Key(sv, "T", "interactive")) {
4699                SUMA_S_Err("Failed in key func.");
4700             }
4701             break;
4702 
4703          case XK_u:
4704             if (!SUMA_U_Key(sv, "u", "interactive")) {
4705                SUMA_S_Err("Failed in key func.");
4706             }
4707             break;
4708          case XK_U:
4709             if (!SUMA_U_Key(sv, "U", "interactive")) {
4710                SUMA_S_Err("Failed in key func.");
4711             }
4712             break;
4713          case XK_v:
4714             #if 0
4715             /*** No longer in use, Jan 03 2004 */
4716             if (SUMAg_CF->Dev) {
4717                SUMA_Show_SurfaceViewer_Struct (sv, stdout, 0);
4718             }
4719             #endif
4720             break;
4721 
4722          case XK_W:
4723             if ((Kev.state & ControlMask)){
4724                if (!SUMA_W_Key(sv, "ctrl+W", "interactive")) {
4725                   SUMA_S_Err("Failed in key func.");
4726                }
4727             } else {
4728                if (!SUMA_W_Key(sv, "W", "interactive")) {
4729                   SUMA_S_Err("Failed in key func.");
4730                }
4731             }
4732             break;
4733 
4734          case XK_w:
4735             if (!SUMA_W_Key(sv, "w", "interactive")) {
4736                SUMA_S_Err("Failed in key func.");
4737             }
4738             break;
4739 
4740             break;
4741 
4742          case XK_Z:
4743             if (!SUMA_Z_Key(sv, "Z", "interactive")) {
4744                SUMA_S_Err("Failed in key func.");
4745             }
4746             break;
4747 
4748          case XK_z:
4749             if (!SUMA_Z_Key(sv, "z", "interactive")) {
4750                SUMA_S_Err("Failed in key func.");
4751             }
4752             break;
4753 
4754          #if 0
4755          case XK_3:
4756             sv->Do_3Drender =  !sv->Do_3Drender;
4757             SUMA_S_Notev("!!!!!!!!!!!!!!%d!!!!!!!\n", sv->Do_3Drender);
4758             break;
4759          #endif
4760          case XK_8:
4761             {
4762                char stmp[100];
4763                sprintf(stmp, "%d", SUMAg_CF->X->NumForeSmoothing);
4764                SUMAg_CF->X->N_ForeSmooth_prmpt =
4765                   SUMA_CreatePromptDialogStruct (SUMA_OK_APPLY_CLEAR_CANCEL,
4766                                        "Foreground smoothing iterations",
4767                                        stmp,
4768                                        sv->X->TOPLEVEL, YUP,
4769                                        SUMA_APPLY_BUTTON,
4770                                        SUMA_SetNumForeSmoothing, (void *)sv,
4771                                        NULL, NULL,
4772                                        NULL, NULL,
4773                                        SUMA_CleanNumString, (void*)1,
4774                                        SUMAg_CF->X->N_ForeSmooth_prmpt);
4775 
4776                SUMAg_CF->X->N_ForeSmooth_prmpt =
4777                      SUMA_CreatePromptDialog("Foreground smoothing iterations",
4778                                              SUMAg_CF->X->N_ForeSmooth_prmpt);
4779             }
4780             break;
4781 
4782          case XK_asterisk:
4783             {
4784                char stmp[100];
4785                sprintf(stmp, "%d", SUMAg_CF->X->NumFinalSmoothing);
4786                SUMAg_CF->X->N_FinalSmooth_prmpt =
4787                   SUMA_CreatePromptDialogStruct (SUMA_OK_APPLY_CLEAR_CANCEL,
4788                                     "Final color smoothing iterations",
4789                                     stmp,
4790                                     sv->X->TOPLEVEL, YUP,
4791                                     SUMA_APPLY_BUTTON,
4792                                     SUMA_SetNumFinalSmoothing, (void *)sv,
4793                                     NULL, NULL,
4794                                     NULL, NULL,
4795                                     SUMA_CleanNumString, (void*)1,                                                    SUMAg_CF->X->N_FinalSmooth_prmpt);
4796 
4797                SUMAg_CF->X->N_FinalSmooth_prmpt =
4798                      SUMA_CreatePromptDialog("Final color smoothing iterations",
4799                                              SUMAg_CF->X->N_FinalSmooth_prmpt);
4800             }
4801             break;
4802 
4803           case XK_at:
4804             if (SUMAg_CF->Dev) {
4805                SUMA_SurfaceObject *SO=NULL;
4806                /* calculate the curvature */
4807                fprintf(SUMA_STDOUT,
4808                   "%s: Calculating surface curvature ...\n", FuncName);
4809                if ((SO = SUMA_SV_Focus_SO(sv))){
4810                   if (!SO->PolyArea) {
4811                      fprintf(SUMA_STDOUT,
4812                               "%s: Computing required mesh area.\n", FuncName);
4813                      if (!SUMA_SurfaceMetrics (SO, "PolyArea", NULL)) {
4814                         fprintf (SUMA_STDERR,
4815                                  "Error %s: Failed in SUMA_SurfaceMetrics.\n",
4816                                  FuncName);
4817                         break;
4818                      }
4819                   }
4820                   SO->SC = SUMA_Surface_Curvature (SO->NodeList, SO->N_Node,
4821                               SO->NodeNormList, SO->PolyArea,
4822                               SO->N_FaceSet, SO->FN, SO->EL, "Curvs_c.txt", 1);
4823                   if (SO->SC == NULL) {
4824                         fprintf( stderr,
4825                                  "Error %s: Failed in SUMA_Surface_Curvature\n",
4826                                  FuncName);
4827                         break;
4828                      }
4829                }
4830             }
4831             break;
4832 
4833          case XK_parenleft:
4834             if (SUMAg_CF->Dev) {
4835                SUMA_SurfaceObject *SO;
4836                SUMA_COLOR_MAP *CM;
4837                SUMA_SCALE_TO_MAP_OPT * OptScl;
4838                int MapType;
4839                SUMA_COLOR_SCALED_VECT * SV;
4840                float IntRange[2], *Vsort;
4841                float * attr_sm;
4842                float *Cx = NULL;
4843 
4844                fprintf(SUMA_STDOUT, "%s: Calculating convexity ...\n", FuncName);
4845                if ((SO = SUMA_SV_Focus_SO(sv))) {
4846                   Cx = (float *)SUMA_GetCx(SO->idcode_str,
4847                                            SUMAg_CF->DsetList, 0);
4848                   if (Cx) {
4849                      SUMA_S_Err("Cx must be null prior to new assignment");
4850                      break;
4851                   }
4852                   Cx = SUMA_Convexity   ( SO->NodeList, SO->N_Node,
4853                                           SO->NodeNormList, SO->FN, NULL);
4854                   if (Cx == NULL) {
4855                         fprintf(stderr,"Error %s: Failed in SUMA_Convexity\n",
4856                                        FuncName);
4857                         break;
4858                   }
4859                   /* smooth estimate twice */
4860                   attr_sm = SUMA_SmoothAttr_Neighb (Cx, SO->N_Node, NULL,
4861                                                     SO->FN, 1, NULL, 1);
4862                   if (attr_sm == NULL) {
4863                         fprintf(stderr,
4864                                 "Error %s: Failed in SUMA_SmoothAttr_Neighb\n",
4865                                 FuncName);
4866                         break;
4867                   }
4868                   Cx = SUMA_SmoothAttr_Neighb (attr_sm, SO->N_Node, Cx,
4869                                                SO->FN, 1, NULL, 1);
4870                   if (attr_sm) SUMA_free(attr_sm);
4871 
4872                   fprintf( SUMA_STDOUT,
4873                            "%s: Use SUMA_ScaleToMap to colorize Conv.txt "
4874                            "and display it on surface.\n", FuncName);
4875                   CM = SUMA_FindNamedColMap ("ngray20");
4876                   if (CM == NULL) {
4877                      fprintf (SUMA_STDERR,
4878                               "Error %s: Could not get standard colormap.\n",
4879                               FuncName);
4880                      exit (1);
4881                   }
4882 
4883                   /* get the options for creating the scaled color mapping */
4884                   OptScl = SUMA_ScaleToMapOptInit();
4885                   if (!OptScl) {
4886                      SUMA_S_Err("Could not get scaling option structure.");
4887                      exit (1);
4888                   }
4889 
4890                   /* work the options a bit */
4891                   OptScl->ApplyClip = YUP;
4892                   IntRange[0] = 5; IntRange[1] = 95; /* percentile clip range*/
4893                   Vsort = SUMA_PercRange (Cx, NULL, SO->N_Node,
4894                                           IntRange, IntRange, NULL);
4895                   OptScl->IntRange[0] = IntRange[0];
4896                   OptScl->IntRange[1] = IntRange[1];
4897 
4898                   OptScl->BrightFact = 0.4;
4899 
4900                   /* map the values in Cx to the colormap */
4901                      /* allocate space for the result */
4902                      SV = SUMA_Create_ColorScaledVect(SO->N_Node, 0);
4903                      if (!SV) {
4904                         fprintf (SUMA_STDERR,
4905                                  "Error %s: Could not allocate for SV.\n",
4906                                  FuncName);
4907                         exit(1);
4908                      }
4909 
4910                      /* finally ! */
4911                      /*fprintf ( SUMA_STDERR,"%s: 1st color in map %f %f %f\n",
4912                                  FuncName,
4913                                  CM->M[0][0], CM->M[0][1],CM->M[0][2]);*/
4914                      if (!SUMA_ScaleToMap (Cx, SO->N_Node, Vsort[0],
4915                                           Vsort[SO->N_Node-1], CM, OptScl, SV)) {
4916                         fprintf (SUMA_STDERR,
4917                                  "Error %s: Failed in SUMA_ScaleToMap.\n",
4918                                  FuncName);
4919                         exit(1);
4920                      }
4921 
4922                      /* Now place SV in the color array */
4923                      glar_ColorList = SUMA_GetColorList (sv, SO->idcode_str);
4924                      if (!glar_ColorList) {
4925                         SUMA_S_Err("NULL glar_ColorList. BAD.");
4926                         break;
4927                      }
4928                      SUMA_RGBvec_2_GLCOLAR4(SV->cV, glar_ColorList, SO->N_Node);
4929 
4930                      /* free */
4931                      if (Vsort) SUMA_free(Vsort);
4932                       if (OptScl) SUMA_free(OptScl);
4933                      if (SV) SUMA_Free_ColorScaledVect (SV);
4934                      if (Cx) {
4935                         SUMA_free(Cx);
4936                         Cx = NULL;
4937                      }
4938 
4939                   fprintf(SUMA_STDOUT, "%s: Convexity mapping done ...\n",
4940                                        FuncName);
4941                   SUMA_postRedisplay(w, clientData, callData);
4942                }
4943             }
4944             break;
4945 
4946          case XK_comma:
4947             if (!SUMA_comma_Key(sv, "comma", "interactive")) {
4948                SUMA_S_Err("Failed in key func.");
4949             }
4950             break;
4951 
4952          case XK_period:
4953             if (!SUMA_period_Key(sv, "period", "interactive")) {
4954                SUMA_S_Err("Failed in key func.");
4955             }
4956             break;
4957 
4958          case XK_F1: /* F1 */
4959             /*printf("F1\n");*/
4960             if (!SUMA_F1_Key(sv, "F1", "interactive")) {
4961                SUMA_S_Err("Failed in key func.");
4962             }
4963             break;
4964 
4965          case XK_F2:
4966             /*printf("F2\n");*/
4967             if (!SUMA_F2_Key(sv, "F2", "interactive")) {
4968                SUMA_S_Err("Failed in key func.");
4969             }
4970             break;
4971 
4972          case XK_F3: /* F3 */
4973             if (!SUMA_F3_Key(sv, "F3", "interactive")) {
4974                SUMA_S_Err("Failed in key func.");
4975             }
4976             break;
4977 
4978          case XK_F4: /* F4 */
4979             if (!SUMA_F4_Key(sv, "F4", "interactive")) {
4980                SUMA_S_Err("Failed in key func.");
4981             }
4982             break;
4983 
4984          case XK_F5: /* F5 */
4985             if (!SUMA_F5_Key(sv, "F5", "interactive")) {
4986                SUMA_S_Err("Failed in key func.");
4987             }
4988             break;
4989 
4990          case XK_F6: /*F6 */
4991             if (!SUMA_F6_Key(sv, "F6", "interactive")) {
4992                SUMA_S_Err("Failed in key func.");
4993             }
4994             break;
4995 
4996          case XK_F7: /*F7 */
4997             if (!SUMA_F7_Key(sv, "F7", "interactive")) {
4998                SUMA_S_Err("Failed in key func.");
4999             }
5000             break;
5001 
5002          case XK_F8: /*F8 */
5003             if (!SUMA_F8_Key(sv, "F8", "interactive")) {
5004                SUMA_S_Err("Failed in key func.");
5005             }
5006             break;
5007 
5008          case XK_F9: /*F9 */
5009             if (!SUMA_F9_Key(sv, "F9", "interactive")) {
5010                SUMA_S_Err("Failed in key func.");
5011             }
5012             break;
5013 
5014          case XK_F10: /*F10 */
5015             if (!SUMA_F10_Key(sv, "F10", "interactive", NULL)) {
5016                SUMA_S_Err("Failed in key func.");
5017             }
5018             break;
5019          case XK_F11: /* F11 */
5020             if (!SUMA_F11_Key(sv, "F11", "interactive", NULL)) {
5021                SUMA_S_Err("Failed in key func.");
5022             }
5023             break;
5024          case XK_F12: /* F12 */
5025             if (!SUMA_F12_Key(sv, "F12", "interactive")) {
5026                SUMA_S_Err("Failed in key func.");
5027             }
5028             break;
5029          case XK_F13:
5030             if (SUMAg_CF->Dev) {
5031                DList *striplist=NULL;
5032                float Eq[4];
5033                int *Vis_IDs, N_vis;
5034                SUMA_SurfaceObject *SO=NULL;
5035                Vis_IDs = (int *)SUMA_malloc(sizeof(int)*SUMAg_N_DOv);
5036                N_vis = SUMA_VisibleSOs (sv, SUMAg_DOv, Vis_IDs, 0);
5037                if (N_vis) {
5038                   SO = (SUMA_SurfaceObject *)SUMAg_DOv[Vis_IDs[0]].OP;
5039                   /* Axial plane */
5040                   Eq[0] = Eq[1] = 0.0; Eq[2] = 1.0; Eq[3] = -SO->Center[2];
5041                   SUMA_S_Warnv("Kill me!\nEq:[%f %f %f %f], step: %f\n",
5042                                  Eq[0], Eq[1], Eq[2], Eq[3], SO->EL->AvgLe);
5043                   striplist = SUMA_SliceAlongPlane(SO, Eq, SO->EL->AvgLe);
5044                   SUMA_display_edge_striplist(striplist, &(SUMAg_SVv[0]), SO,
5045                                              "ShowConnectedPoints");
5046                   SUMA_FREE_DLIST(striplist);
5047                }
5048                if (Vis_IDs) SUMA_free(Vis_IDs);
5049             }
5050          case XK_Home:
5051             /*printf("HOME\n");*/
5052             if (!list) list = SUMA_CreateList();
5053             SUMA_REGISTER_HEAD_COMMAND_NO_DATA(list, SE_Home, SES_Suma, sv);
5054             SUMA_REGISTER_HEAD_COMMAND_NO_DATA(list, SE_FOVreset, SES_Suma, sv);
5055             SUMA_REGISTER_HEAD_COMMAND_NO_DATA(list, SE_Redisplay, SES_Suma, sv);
5056             if (!SUMA_Engine (&list)) {
5057                fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
5058             }
5059             break;
5060 
5061          case XK_Left:   /*KEY_LEFT:*/
5062             /*fprintf(stdout,"Left Key\n");*/
5063             if ((Kev.state & ControlMask) && (Kev.state & ShiftMask)) {
5064                if (!SUMA_Left_Key(sv, "ctrl+shift+left", "interactive")) {
5065                   SUMA_S_Err("Error in key func.");
5066                   break;
5067                }
5068             }else if (Kev.state & ShiftMask) {
5069                if (!SUMA_Left_Key(sv, "shift+left", "interactive")) {
5070                   SUMA_S_Err("Error in key func.");
5071                   break;
5072                }
5073             }else if (Kev.state & ControlMask){
5074                if (!SUMA_Left_Key(sv, "ctrl+left", "interactive")) {
5075                   SUMA_S_Err("Error in key func.");
5076                   break;
5077                }
5078             }else if (SUMA_ALTHELL) {
5079                if (!SUMA_Left_Key(sv, "alt+left", "interactive")) {
5080                   SUMA_S_Err("Error in key func.");
5081                   break;
5082                }
5083             }else {
5084                if (!SUMA_Left_Key(sv, "left", "interactive")) {
5085                   SUMA_S_Err("Error in key func.");
5086                   break;
5087                }
5088             }
5089 
5090             break;
5091 
5092          case XK_Right:   /*KEY_RIGHT: */
5093             /*printf("Right Key\n");*/
5094             if ((Kev.state & ControlMask) && (Kev.state & ShiftMask)) {
5095                if (!SUMA_Right_Key(sv, "ctrl+shift+right", "interactive")) {
5096                   SUMA_S_Err("Error in key func.");
5097                   break;
5098                }
5099             }else if (Kev.state & ShiftMask) {
5100                if (!SUMA_Right_Key(sv, "shift+right", "interactive")) {
5101                   SUMA_S_Err("Error in key func.");
5102                   break;
5103                }
5104             }else if (Kev.state & ControlMask){
5105                if (!SUMA_Right_Key(sv, "ctrl+right", "interactive")) {
5106                   SUMA_S_Err("Error in key func.");
5107                   break;
5108                }
5109             }else if (SUMA_ALTHELL) {
5110                if (!SUMA_Right_Key(sv, "alt+right", "interactive")) {
5111                   SUMA_S_Err("Error in key func.");
5112                   break;
5113                }
5114             }else {
5115                if (!SUMA_Right_Key(sv, "right", "interactive")) {
5116                   SUMA_S_Err("Error in key func.");
5117                   break;
5118                }
5119             }
5120             break;
5121 
5122          case XK_Down:   /*KEY_DOWN*/
5123             /*printf("Down Key\n");*/
5124             if ((Kev.state & ControlMask) && (Kev.state & ShiftMask)) {
5125                if (!SUMA_Down_Key(sv, "ctrl+shift+down", "interactive")) {
5126                   SUMA_S_Err("Error in key func.");
5127                   break;
5128                }
5129             }else if (Kev.state & ShiftMask) {
5130                if (!SUMA_Down_Key(sv, "shift+down", "interactive")) {
5131                   SUMA_S_Err("Error in key func.");
5132                   break;
5133                }
5134             }else if (Kev.state & ControlMask){
5135                if (!SUMA_Down_Key(sv, "ctrl+down", "interactive")) {
5136                   SUMA_S_Err("Error in key func.");
5137                   break;
5138                }
5139             }else if (SUMA_ALTHELL) {
5140                if (!SUMA_Down_Key(sv, "alt+down", "interactive")) {
5141                   SUMA_S_Err("Error in key func.");
5142                   break;
5143                }
5144             }else {
5145                if (!SUMA_Down_Key(sv, "down", "interactive")) {
5146                   SUMA_S_Err("Error in key func.");
5147                   break;
5148                }
5149             }
5150 
5151             break;
5152 
5153          case XK_Up: /*KEY_UP*/
5154             /*printf("Up Key\n");*/
5155             if ((Kev.state & ControlMask) && (Kev.state & ShiftMask)) {
5156                if (!SUMA_Up_Key(sv, "ctrl+shift+up", "interactive")) {
5157                   SUMA_S_Err("Error in key func.");
5158                   break;
5159                }
5160             }else if (Kev.state & ShiftMask) {
5161                if (!SUMA_Up_Key(sv, "shift+up", "interactive")) {
5162                   SUMA_S_Err("Error in key func.");
5163                   break;
5164                }
5165             }else if (Kev.state & ControlMask){
5166                if (!SUMA_Up_Key(sv, "ctrl+up", "interactive")) {
5167                   SUMA_S_Err("Error in key func.");
5168                   break;
5169                }
5170             }else if (SUMA_ALTHELL) {
5171                if (!SUMA_Up_Key(sv, "alt+up", "interactive")) {
5172                   SUMA_S_Err("Error in key func.");
5173                   break;
5174                }
5175             }else {
5176                if (!SUMA_Up_Key(sv, "up", "interactive")) {
5177                   SUMA_S_Err("Error in key func.");
5178                   break;
5179                }
5180             }
5181             break;
5182          default:
5183             break;
5184 
5185       } /* keysym */
5186    break;
5187 
5188    case ButtonPress:
5189       SUMAg_CF->X->ButtonDown=1;
5190       pButton = Bev.button;
5191       SUMA_LHv("In ButtonPress Button %d, %d @ x,y=%d,%d\n",
5192                               pButton, SUMAg_CF->X->ButtonDown, Bev.x, Bev.y);
5193       if (  SUMAg_CF->SwapButtons_1_3 ||
5194             (SUMAg_CF->ROI_mode && SUMAg_CF->Pen_mode)) {
5195          if (pButton == Button1) pButton = Button3;
5196          else if (pButton == Button3) pButton = Button1;
5197       }
5198       if (SUMAg_CF->Echo_KeyPress) {
5199          fprintf (SUMA_STDERR,"Button Press: %d (%d,%d,%d,%d,%d)\n"
5200                               , pButton, Button1, Button2, Button3, Button4,
5201                               Button5);
5202       }
5203 
5204      /* trap for double click */
5205       if (Bev.time - B1time < SUMA_DOUBLE_CLICK_MAX_DELAY) {
5206          if (LocalHead) fprintf(SUMA_STDERR, "%s: Double click.\n", FuncName);
5207          DoubleClick = YUP;
5208       } else {
5209          DoubleClick = NOPE;
5210       }
5211 
5212 
5213       if (strstr(sv->State, "GMATRIX")==sv->State) {
5214          /* For graph in matrix representation swap buttons 1 and 2 */
5215          switch (pButton) {
5216             case Button1:
5217                pButton = Button2;
5218                break;
5219             case Button2:
5220                pButton = Button1;
5221                break;
5222          }
5223       }
5224 
5225 
5226       B1time = Bev.time;
5227       M1time = 0;
5228       switch (pButton) { /* switch type of button Press */
5229          case Button1:
5230             if (Bev.state & Button2Mask) {
5231                /* setup initial zooming conditions */
5232                /*fprintf(SUMA_STDERR,"%s: Button 1 &2 down. New\n", FuncName); */
5233                sv->GVS[sv->StdView].zoomBegin = (float)Bev.y;
5234                sv->GVS[sv->StdView].zoomDelta = 0;
5235             }else {
5236                bevx=(float)Bev.x; bevy = (float)Bev.y;
5237                /*fprintf(SUMA_STDERR,"%s: Button 1 down. New\n", FuncName);*/
5238                /* setup initial spinning conditions */
5239                sv->GVS[sv->StdView].spinBeginX = bevx;
5240                sv->GVS[sv->StdView].spinBeginY = bevy;
5241                sv->GVS[sv->StdView].spinDeltaX = 0;
5242                sv->GVS[sv->StdView].spinDeltaY = 0;
5243                /* check to see if other viewers need to be notified */
5244                ii = SUMA_WhichSV(sv, SUMAg_SVv, SUMAg_N_SVv);
5245                if (SUMAg_CF->ViewLocked[ii]) {
5246                   for (it=0; it < SUMAg_N_SVv; ++it) {
5247                      svi = &SUMAg_SVv[it];
5248                      if (it != ii && SUMAg_CF->ViewLocked[it]) {
5249                         svi->GVS[svi->StdView].spinBeginX = bevx;
5250                         svi->GVS[svi->StdView].spinBeginY = bevy;
5251                         svi->GVS[svi->StdView].spinDeltaX = 0;
5252                         svi->GVS[svi->StdView].spinDeltaY = 0;
5253                      }
5254                   }
5255                }
5256                if (DoubleClick) {
5257                   if (Kev.state & ControlMask) SUMA_ResetPrying(sv);
5258                   else {
5259                      SUMA_HOME_QUAT(sv->StdView,
5260                                     sv->GVS[sv->StdView].currentQuat);
5261                      SUMA_postRedisplay(w, NULL, NULL);
5262                   }
5263                }
5264             }
5265             break;
5266          case Button4:
5267          case 6:  /* This is shift and wheel on mac, Button6 is not in X.h ! */
5268             if (pButton==6 || Bev.state & ShiftMask) {
5269                SUMA_ALL_DO *ado=NULL;
5270                char variant[2];
5271                #if 0
5272                int ii;
5273                SUMA_VolumeObject *VO=NULL;
5274                for (ii=0; ii<SUMAg_N_DOv; ++ii) {
5275                   if (SUMA_isVO(SUMAg_DOv[ii])) {
5276                      VO = (SUMA_VolumeObject *)(SUMAg_DOv[ii].OP);
5277                      if (VO->SelectedCutPlane >= 0) {
5278                         SUMA_LHv("Moving cut plane %d\n",
5279                                        VO->SelectedCutPlane);
5280                         if (!SUMA_MoveCutplane(VO, VO->SelectedCutPlane, 1.0)) {
5281                            SUMA_SLP_Err("Bad");
5282                         }
5283                      }
5284 		     /* JB: only allow cutplane from 1st volume object,
5285                   otherwise remove 'break' */
5286 		     break;
5287                   }
5288                }
5289                SUMA_postRedisplay(w, NULL, NULL);
5290                #else
5291                if ((ado = SUMA_SV_Focus_ADO(sv))) {
5292                   switch (ado->do_type) {
5293                      case VO_type: {
5294                         float incr = 1.0;
5295                         SUMA_VolumeObject *vo=(SUMA_VolumeObject *)ado;
5296                         SUMA_VOL_SAUX *VSaux = SUMA_ADO_VSaux(ado);
5297                         if (VSaux && VSaux->PR) {
5298                            if (SUMA_dset_gui_slice_from_tex_slice_d(vo->VE, 0,
5299                                           VSaux->PR->dAltSel+SUMA_VOL_SLC_EQ0,
5300                                           0, variant, NULL)   >=0 ){
5301                               SUMA_set_slice(ado, variant, &incr,
5302                                              "increment", 0);
5303                            }
5304                         }
5305                         SUMA_postRedisplay(w, NULL, NULL);
5306                         break; }
5307                      default:
5308                         SUMA_LH("Nothing here for types %s\n",
5309                               ADO_TNAME(ado));
5310                         break;
5311                   }
5312                }
5313                #endif
5314             } else if (pButton==6 || Bev.state & ControlMask) {
5315                SUMA_ALL_DO *ado=NULL;
5316                SUMA_X_SurfCont *SurfCont;
5317                if (MASK_MANIP_MODE(sv)) {
5318                   ado = SUMA_whichADOg(sv->MouseMode_ado_idcode_str);
5319                   if (ado && ado->do_type == MASK_type) {
5320                      SUMA_MaskDO *mdo = (SUMA_MaskDO *)ado;
5321                      float fv[3];
5322                      int irow=-1;
5323                      {
5324                         fv[0] = mdo->hdim[0]-(0.2*mdo->init_hdim[0]);
5325                         fv[1] = mdo->hdim[1]-(0.2*mdo->init_hdim[1]);
5326                         fv[2] = mdo->hdim[2]-(0.2*mdo->init_hdim[2]);
5327                         if (fv[0] < 0 || fv[1] < 0 || fv[2] < 0) {
5328                            SUMA_BEEP;
5329                            break;
5330                         }
5331                         SUMA_MDO_New_Dim(mdo, fv);
5332                      }
5333                      if ((SurfCont=SUMA_ADO_Cont(ado))) {
5334                         irow = SUMA_ObjectID_Row(SurfCont->MaskTable,
5335                                                  ADO_ID(ado));
5336                         if (irow >= 0) {
5337                            SUMA_InitMasksTable_row(SurfCont,mdo, irow);
5338                         }
5339                      }
5340                      SUMA_NEW_MASKSTATE();
5341                      /* enough for now */
5342                      goto REDISP;
5343                   }
5344                } else if ((ado = SUMA_SV_Focus_ADO(sv)) &&
5345                            ado->do_type == GRAPH_LINK_type &&
5346                            !strcmp(SUMA_ADO_variant(ado),"GMATRIX")) {
5347                   SUMA_OVERLAYS *Sover = NULL;
5348                   SUMA_LH("Going forward one sub-brick");
5349                   Sover = SUMA_ADO_CurColPlane(ado);
5350                   SUMA_SwitchColPlaneIntensity( ado, Sover,
5351                                                 SUMA_FORWARD_ONE_SUBBRICK, 1);
5352                   /* redisplay done in function above ... */
5353                   SUMA_RETURNe;
5354                }
5355             } else {
5356                if (!SUMA_Z_Key(sv, "z", "interactive")) {
5357                   SUMA_S_Err("Failed in key func.");
5358                }
5359             }
5360             break;
5361          case Button5:
5362          case 7: /* This is shift and wheel on mac, Button7 is not in X.h ! */
5363             if (pButton==7 || Bev.state & ShiftMask) {
5364                SUMA_ALL_DO *ado=NULL;
5365                char variant[2];
5366                #if 0
5367                int ii;
5368                SUMA_VolumeObject *VO=NULL;
5369                for (ii=0; ii<SUMAg_N_DOv; ++ii) {
5370                   if (SUMA_isVO(SUMAg_DOv[ii])) {
5371                      VO = (SUMA_VolumeObject *)(SUMAg_DOv[ii].OP);
5372                      if (VO->SelectedCutPlane >= 0) {
5373                         SUMA_LHv("Moving cut plane %d\n",
5374                                        VO->SelectedCutPlane);
5375                         if (!SUMA_MoveCutplane(VO, VO->SelectedCutPlane, -1.0)) {
5376                            SUMA_SLP_Err("Bad");
5377                         }
5378                      }
5379 		     /* JB: only allow cutplane from 1st volume object,
5380                   otherwise remove 'break' */
5381 		     break;
5382                   }
5383                }
5384                SUMA_postRedisplay(w, NULL, NULL);
5385                #else
5386                if ((ado = SUMA_SV_Focus_ADO(sv))) {
5387                   switch (ado->do_type) {
5388                      case VO_type: {
5389                         float incr = -1.0;
5390                         SUMA_VolumeObject *vo=(SUMA_VolumeObject *)ado;
5391                         SUMA_VOL_SAUX *VSaux = SUMA_ADO_VSaux(ado);
5392                         if (VSaux && VSaux->PR) {
5393                            if (SUMA_dset_gui_slice_from_tex_slice_d(vo->VE, 0,
5394                                           VSaux->PR->dAltSel+SUMA_VOL_SLC_EQ0,
5395                                           0, variant, NULL)   >=0 ){
5396                               SUMA_set_slice(ado, variant, &incr,
5397                                              "increment", 0);
5398                            }
5399                         }
5400                         SUMA_postRedisplay(w, NULL, NULL);
5401                         break; }
5402                      default:
5403                         SUMA_LH("Nothing here for types %s\n",
5404                               ADO_TNAME(ado));
5405                         break;
5406                   }
5407                }
5408                #endif
5409             } else if (pButton==7 || Bev.state & ControlMask) {
5410                SUMA_ALL_DO *ado=NULL;
5411                SUMA_X_SurfCont *SurfCont;
5412                if (MASK_MANIP_MODE(sv)) {
5413                   ado = SUMA_whichADOg(sv->MouseMode_ado_idcode_str);
5414                   if (ado && ado->do_type == MASK_type) {
5415                      SUMA_MaskDO *mdo = (SUMA_MaskDO *)ado;
5416                      float fv[3];
5417                      int irow=-1;
5418                      {
5419                         fv[0] = mdo->hdim[0]+(0.2*mdo->init_hdim[0]);
5420                         fv[1] = mdo->hdim[1]+(0.2*mdo->init_hdim[1]);
5421                         fv[2] = mdo->hdim[2]+(0.2*mdo->init_hdim[2]);
5422                         SUMA_MDO_New_Dim(mdo, fv);
5423                      }
5424                      if ((SurfCont=SUMA_ADO_Cont(ado))) {
5425                         irow = SUMA_ObjectID_Row(SurfCont->MaskTable,
5426                                                  ADO_ID(ado));
5427                         if (irow >= 0) {
5428                            SUMA_InitMasksTable_row(SurfCont,mdo, irow);
5429                         }
5430                      }
5431                      SUMA_NEW_MASKSTATE();
5432                      /* enough for now */
5433                      goto REDISP;
5434                   }
5435                } else if ((ado = SUMA_SV_Focus_ADO(sv)) &&
5436                            ado->do_type == GRAPH_LINK_type &&
5437                            !strcmp(SUMA_ADO_variant(ado),"GMATRIX")) {
5438                   SUMA_OVERLAYS *Sover = NULL;
5439                   SUMA_LH("Switching overlay back one");
5440                   Sover = SUMA_ADO_CurColPlane(ado);
5441                   SUMA_SwitchColPlaneIntensity( ado, Sover,
5442                                                 SUMA_BACK_ONE_SUBBRICK, 1);
5443                   /* redisplay done in function above ... */
5444                   SUMA_RETURNe;
5445                }
5446             } else {
5447                if (!SUMA_Z_Key(sv, "Z", "interactive")) {
5448                   SUMA_S_Err("Failed in key func.");
5449                }
5450             }
5451             break;
5452 
5453          case Button2:
5454             if (Bev.state & ShiftMask) {
5455                /* setup initial zooming conditions */
5456                /*fprintf(SUMA_STDERR,"%s: Button 2 & Shift\n", FuncName); */
5457                sv->GVS[sv->StdView].zoomBegin = (float)Bev.y;
5458                sv->GVS[sv->StdView].zoomDelta = 0;
5459             } else {
5460                /*fprintf(stdout,"Button 2 down, plain jane\n");*/
5461                /* setup initial translation conditions */
5462                bevx = (float)Bev.x; bevy = (float)Bev.y;
5463                sv->GVS[sv->StdView].translateBeginX = bevx;;
5464                sv->GVS[sv->StdView].translateBeginY = bevy;
5465                sv->GVS[sv->StdView].translateDeltaX = 0.0;
5466                sv->GVS[sv->StdView].translateDeltaY = 0.0;
5467             }
5468             break;
5469 
5470          case Button3: {
5471                SUMA_LHv("Button 3 down plain jane, "
5472                             "viewer #%d : X=%f, Y = %f\n",
5473                             SUMA_WhichSV(sv, SUMAg_SVv, SUMAg_N_SVv),
5474                               (float)Bev.x, (float)Bev.y);
5475 
5476                /* Clear any pre-existing selection */
5477                if (!SUMA_Add_To_PickResult_List(sv, NULL, "TERSUM", NULL)) {
5478                   SUMA_S_Err("Failed to clear selections");
5479                   break;
5480                }
5481 
5482                /* Bev.state does work in the line below,
5483                   unlike Mev.state further down.
5484                   Using Kev.state anyway because it works in both cases */
5485                if ((Kev.state & ShiftMask) && (Kev.state & ControlMask)) {
5486                   SUMA_LH("Allowing callbacks");
5487                   SUMAg_CF->HoldClickCallbacks = 0;
5488                } else {
5489                   SUMA_LH("Holding back callbacks");
5490                   SUMAg_CF->HoldClickCallbacks = 1;
5491                }
5492 
5493                /* are we in ROI drawing mode ? */
5494                if (  SUMAg_CF->ROI_mode
5495                      && SUMA_SV_Focus_SO(sv) && !(Bev.state & ShiftMask)) {
5496                   /* ROI drawing mode */
5497                   ROI_mode = YUP;
5498                } else {
5499                   ROI_mode = NOPE;
5500 
5501                }
5502 
5503                if (!(Kev.state & ShiftMask) && (Kev.state & ControlMask)) {
5504                   SUMA_LH("Yoking intensity to node selection");
5505                   SUMAg_CF->YokeIntToNode = 1;
5506                } else {
5507                   SUMA_LH("Holding back yoking");
5508                   SUMAg_CF->YokeIntToNode = 0;
5509                }
5510 
5511                SUMA_LH("Get the selection line, bitte");
5512                if (!SUMA_GetSelectionLine (  sv, (int)Bev.x, (int)Bev.y,
5513                                              sv->Pick0, sv->Pick1, 0,
5514                                              NULL, NULL, NULL)) {
5515                   fprintf (SUMA_STDERR,
5516                            "Error %s: Failed in SUMA_GetSelectionLine.\n",
5517                            FuncName);
5518                   break;
5519                }
5520 
5521                if (DoubleClick && !ROI_mode){/*See if you are selecting masks */
5522                   SUMA_ALL_DO *mado=NULL, *ado=NULL;
5523                   SUMA_GRAPH_SAUX *GSaux=NULL;
5524                   /* you do not want to waist time doing double calculations if
5525                      the user clicks twice by mistake */
5526                   /* make sure no viewer, other than the one clicked in is in
5527                      momentum mode */
5528                   if (SUMAg_N_SVv > 1) {
5529                      ii = SUMA_WhichViewerInMomentum (SUMAg_SVv,
5530                                                       SUMAg_N_SVv, NULL);
5531                      if (ii >= 0) {
5532                         sprintf (s, "You cannot select or draw while viewers\n"
5533                                     "(like viewer %c) are in momentum mode.\n",
5534                                     ii+65);
5535                         SUMA_RegisterMessage (SUMAg_CF->MessageList,
5536                                               s, FuncName, SMT_Error,
5537                                               SMA_LogAndPopup);
5538                         SUMA_RETURNe;
5539                      }
5540                   }
5541 
5542                   /* Any hits for masks? */
5543                   hit = SUMA_ComputeLineMaskIntersect (sv, SUMAg_DOv, 0, &mado);
5544                   if (hit < 0) {
5545                     SUMA_S_Err("Failed in SUMA_ComputeLineMaskIntersect.");
5546                   } else if (hit > 0) {
5547                      SUMA_LH("Mask was double clicked");
5548                      if (!MASK_MANIP_MODE(sv)) {
5549                         SUMA_S_Note("Turning on mask manip mode");
5550                         if (!SUMA_SetMouseMode(sv,SUMA_MASK_MANIP_MMODE,
5551                                                   (void *)(ADO_ID(mado)))) {
5552                            SUMA_S_Warn("Mask manip mode could not be set");
5553                         }
5554                      } else {
5555                         SUMA_S_Note("Turning off mask manip mode");
5556                         if (!SUMA_SetMouseMode(sv,SUMA_MASK_MANIP_MMODE,NULL)) {
5557                            SUMA_S_Warn("Mask manip mode could not be set");
5558                         }
5559                      }
5560                      /* Not much else to do */
5561                      goto REDISP;
5562                   } else if (MASK_MANIP_MODE(sv)) { /* turn it off */
5563                      SUMA_S_Note("Turning off mask manip mode");
5564                      if (!SUMA_SetMouseMode(sv,SUMA_MASK_MANIP_MMODE,NULL)) {
5565                         SUMA_S_Warn("Mask manip mode could not be set");
5566                      }
5567                      /* Not much else to do */
5568                      goto REDISP;
5569                   } else if ((ado = SUMA_SV_Focus_ADO(sv)) &&
5570                               ado->do_type == GRAPH_LINK_type &&
5571                              (GSaux = SUMA_ADO_GSaux(ado)) &&
5572                              GSaux->PR && GSaux->PR->datum_index == -1 &&
5573                              GSaux->PR->iAltSel[SUMA_ENODE_0] != -1 &&
5574                              GSaux->IgnoreSelection == 0) {
5575                            /* turn off node selection to allow all connections
5576                               to be visible. Execute only if we have a graph
5577                               in focus (in 3D drawing) and a point is selected
5578                               and selection is not being ignored*/
5579                         SUMA_LH("Ignoring selection for graph display");
5580                         GSaux->IgnoreSelection = 1;
5581                         SUMA_FlushPickBufferForDO(ado);
5582                         goto REDISP;
5583                   }
5584                   SUMA_LH("No mask hit, and no selection needs ignoring");
5585                }
5586 
5587                if (!DoubleClick) {
5588                   /* you do not want to waist time doing double calculations if
5589                      the user clicks twice by mistake */
5590                   /* make sure no viewer, other than the one clicked in is in
5591                      momentum mode */
5592                   if (SUMAg_N_SVv > 1) {
5593                      ii = SUMA_WhichViewerInMomentum (SUMAg_SVv,
5594                                                       SUMAg_N_SVv, NULL);
5595                      if (ii >= 0) {
5596                         sprintf (s, "You cannot select or draw while viewers\n"
5597                                     "(like viewer %c) are in momentum mode.\n",
5598                                     ii+65);
5599                         SUMA_RegisterMessage (SUMAg_CF->MessageList,
5600                                               s, FuncName, SMT_Error,
5601                                               SMA_LogAndPopup);
5602                         SUMA_RETURNe;
5603                      }
5604                   }
5605 
5606                   #if 0
5607                   /* Try this if you are having OpenGLStateReset problems at
5608                      node selection time. It is inefficient, but helps point
5609                      to the problem.
5610                      Look at recent updates to SE_Redisplay*All* for the more
5611                      appropriate fix */
5612                   SUMA_S_Note("Blunt fix:");
5613                   SUMA_OpenGLStateReset(SUMAg_DOv, SUMAg_N_DOv, sv);
5614                   SUMA_handleRedisplay((XtPointer)sv->X->GLXAREA);
5615                   #endif
5616 
5617                   /* perform the intersection calcluation and mark the surface */
5618                   SUMA_LHv("Finding hit: %d %d,\n"
5619                            "Pick0: %f %f %f, Pick1: %f %f %f\n",
5620                            (int)Bev.x, (int)Bev.y,
5621                            sv->Pick0[0], sv->Pick0[1], sv->Pick0[2],
5622                            sv->Pick1[0], sv->Pick1[1], sv->Pick1[2]);
5623 
5624 
5625                   if (1) {
5626                      hit = SUMA_ComputeLineDOsIntersect (sv, SUMAg_DOv, 0, NULL);
5627                      if ( (Kev.state & ShiftMask) &&
5628                          !(Kev.state & ControlMask) &&
5629                          !SUMA_ALTHELL && !SUMAg_CF->ROI_mode){
5630                          /* Show me the click buffer */
5631                         SUMA_MarkPickInBuffer4(sv, 1, NULL);
5632                      }
5633                      if (hit < 0) {
5634                         SUMA_S_Err("Failed in SUMA_ComputeLineDOsIntersect.");
5635                      }
5636                   }
5637 
5638                   if (1) {
5639                       SUMA_LH("Trying for volume intersections");
5640                       hit =  SUMA_ComputeLineVOslicesIntersect(sv, SUMAg_DOv,
5641                                                                0, NULL);
5642                       if (hit < 0) {
5643                          fprintf( SUMA_STDERR,
5644                             "Error %s: "
5645                             "Failed in SUMA_MarkLineVOslicesIntersect.\n",
5646                                   FuncName);
5647                       }
5648                   }
5649 
5650                   if (1) {
5651                       SUMA_LH("Trying for volume VR intersections");
5652                       hit =  SUMA_ComputeLineVOvrIntersect(sv, SUMAg_DOv,
5653                                                                0, NULL);
5654                       if (hit < 0) {
5655                          fprintf( SUMA_STDERR,
5656                             "Error %s: "
5657                             "Failed in SUMA_MarkLineVOvrIntersect.\n",
5658                                   FuncName);
5659                       }
5660                   }
5661                   #if 0
5662                   if (SUMA_ALTHELL ||
5663                       SUMA_VisibleSOs(sv, SUMAg_DOv, NULL, 0) == 0) {
5664                       SUMA_LH("Trying for cutplanes");
5665                       hit = SUMA_MarkLineCutplaneIntersect (sv, SUMAg_DOv, 0);
5666                       if (hit < 0) {
5667                          fprintf( SUMA_STDERR,
5668                             "Error %s: "
5669                             "Failed in SUMA_MarkLineCutplaneIntersect.\n",
5670                                   FuncName);
5671                       }
5672                   }
5673                   #endif
5674 
5675                   SUMA_LH("Checking on registered surfaces");
5676                   SwasHit = 0;
5677                   ii = SUMA_RegisteredSOs(sv, SUMAg_DOv, NULL);
5678                   if (ii == 0) { /* no surfaces, break */
5679                      SUMA_LH("No registrants");
5680                   } else {
5681                      /* have surfaces, find hits */
5682                      hit = SUMA_ComputeLineSurfaceIntersect (sv, SUMAg_DOv,
5683                                                              0, NULL);
5684                      if (hit < 0) {
5685                        SUMA_S_Err("Failed in SUMA_ComputeLineSurfaceIntersect.");
5686                      } else if (hit > 0) SwasHit = 1;
5687 
5688                   }
5689 
5690                }
5691 
5692 
5693                if (ROI_mode && (SwasHit || DoubleClick)) {
5694                   /* keep track of mouse motion in window */
5695                   if (!SUMA_CreateBrushStroke (sv)) {
5696                      SUMA_RegisterMessage (SUMAg_CF->MessageList,
5697                                            "Failed to create BrushStroke.",
5698                                            FuncName,
5699                                            SMT_Error, SMA_LogAndPopup);
5700                      SUMA_RETURNe;
5701 
5702                   }
5703 
5704                   SUMA_AddToBrushStroke (sv, (int)Bev.x, (int)Bev.y, sv->Pick0,
5705                      sv->Pick1, YUP);
5706                }
5707 
5708                ASSESS:
5709                SUMA_LH("Assessment %d", dlist_size(sv->SelAdo));
5710                if (dlist_size(sv->SelAdo)) {
5711                   if (!SUMA_Process_Selected_ADO(sv,SUMA_ALTHELL)) {
5712                      SUMA_S_Err("Failed to process selected ados");
5713                      goto OUT;
5714                   }
5715 
5716                   SUMA_LH("Calling redisplay");
5717                   /* redisplay */
5718                   sv->ResetGLStateVariables = YUP;
5719                   SUMA_handleRedisplay((XtPointer)sv->X->GLXAREA);
5720                }
5721                goto OUT;
5722 
5723                REDISP:
5724                SUMA_LH("Calling redisplay without assessment");
5725                sv->ResetGLStateVariables = YUP;
5726                SUMA_handleRedisplay((XtPointer)sv->X->GLXAREA);
5727                goto OUT;
5728 
5729                OUT:
5730                /* reset hold on xforms */
5731                SUMAg_CF->HoldClickCallbacks = 0;
5732             break; }
5733       } /* switch type of button Press */
5734       break;
5735 
5736    case ButtonRelease:
5737       SUMAg_CF->X->ButtonDown=0;
5738       M1time = 0;
5739       rButton = Bev.button;
5740       SUMA_LHv("In ButtonRelease Button %d %d @ x,y=%d,%d\n",
5741                    rButton, SUMAg_CF->X->ButtonDown, Bev.x, Bev.y);
5742       if (SUMAg_CF->Echo_KeyPress) {
5743          fprintf (SUMA_STDERR,"Button Release: %d (%d,%d,%d,%d,%d)\n"
5744                               , rButton, Button1, Button2, Button3, Button4,
5745                               Button5);
5746       }
5747 
5748       if (SUMAg_CF->SwapButtons_1_3 ||
5749           (SUMAg_CF->ROI_mode && SUMAg_CF->Pen_mode)) {
5750          if (rButton == Button1) rButton = Button3;
5751          else if (rButton == Button3) rButton = Button1;
5752       }
5753 
5754       if (strstr(sv->State, "GMATRIX")==sv->State) {
5755          /* For graph in matrix representation swap buttons 1 and 2 */
5756          switch (rButton) {
5757             case Button1:
5758                rButton = Button2;
5759                break;
5760             case Button2:
5761                rButton = Button1;
5762                break;
5763          }
5764       }
5765 
5766 
5767       switch (rButton) { /* switch type of button Press */
5768          case Button3:
5769             if (LocalHead)
5770                fprintf(SUMA_STDERR,"%s: In ButtonRelease3\n", FuncName);
5771 
5772             if (SUMAg_CF->ROI_mode) {
5773                SUMA_DRAWN_ROI *DrawnROI = NULL;
5774                SUMA_BRUSH_STROKE_ACTION BsA=SUMA_BSA_Undefined;
5775 
5776                if (sv->BS) {
5777                   /* Process the brush stroke*/
5778                   if (DoubleClick) BsA = SUMA_BSA_JoinEnds;
5779                   else BsA = SUMA_BSA_AppendStrokeOrFill;
5780                   if (!(DrawnROI = SUMA_ProcessBrushStroke (sv, BsA))) {
5781                      if (LocalHead)
5782                         fprintf (SUMA_STDERR,
5783                                  "%s: NULL DrawnROI returned.\n", FuncName);
5784                      SUMA_ClearBrushStroke (sv);
5785                      break;
5786                   }
5787 
5788                   /* Showme the DrawnROI */
5789                   if (LocalHead) SUMA_ShowDrawnROI (DrawnROI, NULL, NOPE);
5790 
5791                   /* do smething with the BrushStroke, then wipe it clean,
5792                      OK to show even if empty*/
5793                   if (LocalHead) SUMA_ShowBrushStroke (sv, NULL);
5794 
5795                   /* SUMA_DrawBrushStroke (sv, YUP); */
5796                   SUMA_ClearBrushStroke (sv);
5797 
5798                   /* redisplay all others */
5799                   if (!list) list = SUMA_CreateList ();
5800                   SUMA_REGISTER_TAIL_COMMAND_NO_DATA(list,
5801                            SE_RedisplayNow_AllOtherVisible, SES_SumaWidget, sv);
5802                   SUMA_Engine (&list);
5803 
5804                   /* redisplay .
5805                      DO NOT REDISPLAY WITH SE_Redisplay_AllVisible or
5806                      you will have GL state synchronization problems */
5807                   sv->ResetGLStateVariables = YUP;
5808                   SUMA_handleRedisplay((XtPointer)sv->X->GLXAREA);
5809 
5810                }/* if sv->BS */
5811             } /* if SUMAg_CF->ROImode */
5812 
5813             if (sv->Focus_DO_ID > 0){/* Get surface controller page in sync */
5814                if (!SUMA_IS_CONTPAGE_ON_TOP(SUMAg_CF->X->AllMaskCont)) {
5815                   /* Switch only if 'sticky' mask controller page
5816                      is not on top */
5817                   SUMA_ALL_DO *ado = SUMA_SV_Focus_ADO(sv);
5818                   if (ado) {
5819                      SUMA_LHv("Will call Init for %s if %d\n",
5820                         SUMA_ADO_Label(ado),
5821                         SUMA_isADO_Cont_Realized(ado));
5822                      if (SUMA_isADO_Cont_Realized(ado))
5823                         SUMA_Init_SurfCont_SurfParam(ado);
5824                   }
5825                } else {
5826                   if (!SUMA_InitMasksTable(SUMAg_CF->X->AllMaskCont)) {
5827                      SUMA_S_Err("Failed to initialize mask table");
5828                   }
5829                }
5830             }
5831          break;
5832          case Button1:
5833             SUMA_LH("In Button 1 release\n");
5834             if (sv->GVS[sv->StdView].vLHpry0[0] !=
5835                                           sv->GVS[sv->StdView].vLHpry[0] ||
5836                 sv->GVS[sv->StdView].vLHpry0[1] !=
5837                                           sv->GVS[sv->StdView].vLHpry[1] ||
5838                 sv->GVS[sv->StdView].vLHpry0[2] !=
5839                                           sv->GVS[sv->StdView].vLHpry[2] ){
5840                /* update the normals just now, this would not be needed if
5841                  SUMA_ApplyPrying has set recompute_normals = 1*/
5842                SUMA_RecomputeNormsPrying(sv);
5843                SUMA_handleRedisplay((XtPointer)sv->X->GLXAREA);
5844             }
5845             sv->GVS[sv->StdView].vLHpry0[0] = sv->GVS[sv->StdView].vLHpry[0];
5846             sv->GVS[sv->StdView].vLHpry0[1] = sv->GVS[sv->StdView].vLHpry[1];
5847             sv->GVS[sv->StdView].vLHpry0[2] = sv->GVS[sv->StdView].vLHpry[2];
5848          break;
5849       } /* switch type of button Press */
5850       break;
5851 
5852    case MotionNotify:
5853       if (LocalHead) {
5854           fprintf(stdout,"In MotionNotify\n");
5855               if (Mev.state & Button1MotionMask) fprintf(stdout,"   B1 mot\n");
5856          else if (Mev.state & Button2MotionMask) fprintf(stdout,"   B2 mot\n");
5857          else if (Mev.state & Button3MotionMask) fprintf(stdout,"   B3 mot\n");
5858          else if (Mev.state & Button4MotionMask) fprintf(stdout,"   B4 mot\n");
5859          else if (Mev.state & Button5MotionMask) fprintf(stdout,"   B5 mot\n");
5860          else fprintf(stdout,"   Something mot, button %d\n", Bev.button);
5861       }
5862       if (SUMAg_CF->Echo_KeyPress) {
5863          if (Mev.state & Button1MotionMask)
5864             fprintf(SUMA_STDERR,"   B1 mot\n");
5865          else if (Mev.state & Button2MotionMask)
5866             fprintf(SUMA_STDERR,"   B2 mot\n");
5867          else if (Mev.state & Button3MotionMask)
5868             fprintf(SUMA_STDERR,"   B3 mot\n");
5869          else if (Mev.state & Button4MotionMask)
5870             fprintf(SUMA_STDERR,"   B4 mot\n");
5871          else if (Mev.state & Button5MotionMask)
5872             fprintf(SUMA_STDERR,"   B5 mot\n");
5873          else if (Mev.state) fprintf(SUMA_STDERR,
5874                               "   Something mot, button %d\n", Bev.button);
5875       }
5876 
5877       if (  SUMAg_CF->SwapButtons_1_3 ||
5878             (SUMAg_CF->ROI_mode && SUMAg_CF->Pen_mode)) {
5879         if (((Mev.state & Button3MotionMask) && (Mev.state & Button2MotionMask))
5880          || ((Mev.state & Button2MotionMask) && (Mev.state & ShiftMask))) {
5881             mButton = SUMA_Button_12_Motion;
5882          } else if(Mev.state & Button3MotionMask) {
5883             mButton = SUMA_Button_1_Motion;
5884          }else if(Mev.state & Button2MotionMask) {
5885             mButton = SUMA_Button_2_Motion;
5886          }else if(Mev.state & Button1MotionMask) {
5887             mButton = SUMA_Button_3_Motion;
5888          }else {
5889             break;
5890          }
5891       } else {
5892          if (((Mev.state & Button1MotionMask) && (Mev.state & Button2MotionMask))
5893           || ((Mev.state & Button2MotionMask) && (Mev.state & ShiftMask))) {
5894             mButton = SUMA_Button_12_Motion;
5895          } else if(Mev.state & Button1MotionMask) {
5896             mButton = SUMA_Button_1_Motion;
5897          }else if(Mev.state & Button2MotionMask) {
5898             mButton = SUMA_Button_2_Motion;
5899          } else if(Mev.state & Button3MotionMask) {
5900             mButton = SUMA_Button_3_Motion;
5901          }else {
5902             break;
5903          }
5904       }
5905 
5906       if (strstr(sv->State, "GMATRIX")==sv->State) {
5907          /* For graph in matrix representation swap buttons 1 and 2 */
5908          SUMA_LH("In graph matrix, swapping 1/2 motion");
5909          switch (mButton) {
5910             case SUMA_Button_1_Motion:
5911                mButton = SUMA_Button_2_Motion;
5912                break;
5913             case SUMA_Button_2_Motion:
5914                mButton = SUMA_Button_1_Motion;
5915                break;
5916          }
5917       }
5918 
5919       switch (mButton) {
5920          case SUMA_Button_12_Motion:
5921          case SUMA_Button_2_Shift_Motion:
5922             /*fprintf(SUMA_STDERR,"%s: In motion, Butt1 & Butt2\n", FuncName);*/
5923             sv->GVS[sv->StdView].zoomDelta = 1.0 +
5924                                              (float)((int)Mev.y -
5925                                  sv->GVS[sv->StdView].zoomBegin)/MOUSE_ZOOM_FACT;
5926             if (sv->GVS[sv->StdView].zoomDelta > 2.0)
5927                sv->GVS[sv->StdView].zoomDelta = 2.0;
5928             else if (sv->GVS[sv->StdView].zoomDelta < 0.5)
5929                sv->GVS[sv->StdView].zoomDelta = 0.5;
5930             sv->FOV[sv->iState] /= sv->GVS[sv->StdView].zoomDelta;
5931             if (sv->FOV[sv->iState] < FOV_MIN) sv->FOV[sv->iState] = FOV_MIN;
5932             else if (sv->FOV[sv->iState] > FOV_MAX)
5933                sv->FOV[sv->iState] = FOV_MAX;
5934             sv->GVS[sv->StdView].zoomBegin = (float)(int)Mev.y;
5935             /*fprintf(stdout, "FOV zoom Delta = %f=n",
5936                               sv->GVS[sv->StdView].zoomDelta);*/
5937             /* Now update the zoom compensation variable */
5938             if (sv->ZoomCompensate) {
5939                sv->ZoomCompensate = sqrt(sv->FOV[sv->iState] /
5940                                     SUMA_sv_auto_fov(sv));
5941                      /* slow down compensation, with sqrt*/
5942                if (sv->ZoomCompensate > 1)
5943                   sv->ZoomCompensate = 1.0;
5944                         /* no need to compensate at low zooms */
5945                else if (sv->ZoomCompensate < 0.05)
5946                   sv->ZoomCompensate = 0.05; /* no need to go lower */
5947             }
5948             ii = SUMA_WhichSV (sv, SUMAg_SVv, SUMAg_N_SVv);
5949             SUMA_postRedisplay(w, clientData, callData);
5950             break;
5951 
5952          case SUMA_Button_1_Motion:
5953             /* fprintf(SUMA_STDERR,"%s: In motion, Butt1 \n", FuncName); */
5954             mevx = (float)Mev.x;
5955             mevy = (float)Mev.y;
5956             wwid = (float)sv->X->aWIDTH;
5957             whei = (float)sv->X->aHEIGHT;
5958             /* spinning mode */
5959             if (sv->ZoomCompensate) {
5960                zc_fac = sv->ZoomCompensate;
5961             }else {
5962                zc_fac = 1.0;
5963             }
5964             sv->GVS[sv->StdView].spinDeltaX =
5965                (mevx - sv->GVS[sv->StdView].spinBeginX);
5966             sv->GVS[sv->StdView].spinDeltaY =
5967                (mevy - sv->GVS[sv->StdView].spinBeginY);
5968             if (M1time) {
5969                M1delta = Mev.time - M1time;
5970                mvdeltax = Mev.x - mvxlast;
5971                mvdeltay = Mev.y - mvylast;
5972             } else {
5973                M1delta = 0;
5974                mvdeltax = 0;
5975                mvdeltay = 0;
5976             }
5977             M1time = Mev.time;
5978             mvxlast= Mev.x;
5979             mvylast= Mev.y;
5980 
5981             /* compute move rate in pixels per milliseconds,
5982             You could use this to add a gain on the amount of rotation,
5983             but before you could use those factors, you'll need to scale
5984             the results to something appropriate */
5985             mvx_fac = ((SUMA_ABS((float)mvdeltax)/(M1delta+0.01)));
5986             mvy_fac = ((SUMA_ABS((float)mvdeltay)/(M1delta+0.01)));
5987 
5988             #if 0
5989             fprintf(stdout,"\n"
5990                            "spinBeginX %f \n"
5991                            "spinBeginY %f \n"
5992                            "spinDeltaX %f \n"
5993                            "spinDeltaY %f \n"
5994                            "X->aWIDTH %d  \n"
5995                            "X->aHEIGHT %d\n"
5996                            "ZoomCompensate %f\n"
5997                            "mv[xy]last [%d %d]\n"
5998                            "mvdelta[xy]last [%d %d]\n"
5999                            "MoveRatePixelsPerms [%f %f]\n"
6000                            ,
6001                         sv->GVS[sv->StdView].spinBeginX,
6002                         sv->GVS[sv->StdView].spinBeginY,
6003                         sv->GVS[sv->StdView].spinDeltaX,
6004                         sv->GVS[sv->StdView].spinDeltaY,
6005                         sv->X->aWIDTH, sv->X->aHEIGHT, sv->ZoomCompensate,
6006                         mvxlast, mvylast,
6007                         mvdeltax, mvdeltay,
6008                         mvx_fac, mvy_fac
6009                         );
6010             #else
6011             /* fprintf(stdout,"%f %f; ", mvx_fac, mvy_fac); */
6012             #endif
6013             /* do not use movement rate before you scale it properly */
6014             mvx_fac = mvy_fac = 1.0;
6015             if (sv->GVS[sv->StdView].spinDeltaX ||
6016                 sv->GVS[sv->StdView].spinDeltaY){
6017                if (SUMA_Shft_Event(SUMAg_CF->lev)) {
6018                   float a[3], cQ[4];
6019                   /* rotate about Z axis   */
6020                   a[0] = 0.0; a[1] = 0.0; a[2] = 1.0;
6021                   axis_to_quat(a,
6022                               -SUMA_SIGN(mvdeltax)*sqrt(SUMA_ABS(mvdeltax))*
6023                                  sv->ArrowRotationAngle,
6024                               cQ);
6025                   /*add rotation */
6026                   add_quats ( cQ,
6027                               sv->GVS[sv->StdView].currentQuat,
6028                               sv->GVS[sv->StdView].currentQuat);
6029                } else if (SUMA_Cont_Event(SUMAg_CF->lev)) {
6030                   float val[3];
6031                   val[0] = sv->GVS[sv->StdView].spinDeltaX;
6032                   val[1] = sv->GVS[sv->StdView].spinDeltaY;
6033                   val[2] = 0.0;
6034                   SUMA_ApplyPrying(sv, val, "mouse", 0);
6035                                        /* update normals at release */
6036                } else if (SUMA_Plain_Event(SUMAg_CF->lev)){
6037                   trackball(  sv->GVS[sv->StdView].deltaQuat,
6038                               (2*sv->GVS[sv->StdView].spinBeginX - wwid) /
6039                               wwid*zc_fac*mvx_fac,
6040                               (whei - 2*sv->GVS[sv->StdView].spinBeginY) /
6041                               whei*zc_fac*mvy_fac,
6042                               (2*mevx - wwid)/wwid*zc_fac*mvx_fac,
6043                               (whei - 2*mevy)/whei*zc_fac*mvy_fac);
6044                                     /* comput the increment Quat */
6045                   sv->GVS[sv->StdView].spinBeginX = mevx;
6046                   sv->GVS[sv->StdView].spinBeginY = mevy;
6047                   add_quats ( sv->GVS[sv->StdView].deltaQuat,
6048                               sv->GVS[sv->StdView].currentQuat,
6049                               sv->GVS[sv->StdView].currentQuat);
6050                } else {
6051                   SUMA_LH("Not ready for event flavor");
6052                   break;
6053                }
6054                ii = SUMA_WhichSV(sv, SUMAg_SVv, SUMAg_N_SVv);
6055                if (ii < 0) {
6056                   fprintf (SUMA_STDERR,
6057                            "Error %s: Failed to find index of sv.\n", FuncName);
6058                   break;
6059                }
6060                if (!SUMAg_CF->ViewLocked[ii]) { /* No locking,
6061                                                 just redisplay current viewer */
6062                   SUMA_postRedisplay(w, clientData, callData);
6063                } else { /* locking, update and redisplay those locked */
6064                   DList *list = NULL;
6065                   SUMA_EngineData *ED = NULL;
6066                   SUMA_STANDARD_VIEWS ed_sv, ed_svi;
6067                   /* redisplay current viewer immediately */
6068                   list = SUMA_CreateList ();
6069                   ED = SUMA_InitializeEngineListData (SE_RedisplayNow);
6070                   SUMA_RegisterEngineListCommand (list, ED,
6071                                                    SEF_Empty, NULL,
6072                                                    SES_Suma, (void *)sv, NOPE,
6073                                                    SEI_Head, NULL);
6074                   ed_sv = SUMA_BestStandardView(sv, SUMAg_DOv, SUMAg_N_DOv);
6075                   for (it=0; it < SUMAg_N_SVv; ++it) {
6076                      svi = &SUMAg_SVv[it];
6077                      ed_svi = SUMA_BestStandardView(svi, SUMAg_DOv, SUMAg_N_DOv);
6078                      if (  it != ii &&
6079                            SUMAg_CF->ViewLocked[it] && ed_svi == ed_sv) {
6080                         /* copy quaternions */
6081                         svi->GVS[svi->StdView].spinBeginX =
6082                            sv->GVS[sv->StdView].spinBeginX;
6083                         svi->GVS[svi->StdView].spinBeginY =
6084                            sv->GVS[sv->StdView].spinBeginY;
6085                         SUMA_COPY_VEC( sv->GVS[sv->StdView].deltaQuat,
6086                                        svi->GVS[svi->StdView].deltaQuat,
6087                                        4, float, float);
6088                         SUMA_COPY_VEC( sv->GVS[sv->StdView].currentQuat,
6089                                        svi->GVS[svi->StdView].currentQuat,
6090                                        4, float, float);
6091 
6092                         /* add a redisplay now */
6093                         ED = SUMA_InitializeEngineListData (SE_RedisplayNow);
6094                         SUMA_RegisterEngineListCommand ( list, ED,
6095                                                 SEF_Empty, NULL,
6096                                                 SES_Suma, (void *)svi, NOPE,
6097                                                 SEI_Head, NULL);
6098                      }
6099                   }
6100                   if (!SUMA_Engine (&list)) {
6101                      fprintf (SUMA_STDERR,
6102                               "Error %s: Failed calling SUMA_Engine.\n",
6103                               FuncName);
6104                      break;
6105                   }
6106                }
6107             }
6108 
6109             break;
6110 
6111          case SUMA_Button_2_Motion:
6112             mevx = (float)Mev.x; mevy = (float)Mev.y;
6113             SUMA_LHv("In motion, Butt2 %f, %f\n", mevx , mevy);
6114             if (sv->ZoomCompensate) {
6115                zc_fac = sv->ZoomCompensate;
6116             }else {
6117                zc_fac = 1.0;
6118             }
6119             if ((Kev.state & ShiftMask)){
6120 
6121             } else {
6122                sv->GVS[sv->StdView].translateDeltaX =
6123                   (mevx - sv->GVS[sv->StdView].translateBeginX) /
6124                   (float)sv->X->aWIDTH*sv->GVS[sv->StdView].TranslateGain;
6125                sv->GVS[sv->StdView].translateDeltaY =
6126                   -(mevy - sv->GVS[sv->StdView].translateBeginY) /
6127                    (float)sv->X->aHEIGHT*sv->GVS[sv->StdView].TranslateGain;
6128 
6129                if (  sv->GVS[sv->StdView].translateDeltaX ||
6130                      sv->GVS[sv->StdView].translateDeltaY){
6131                   sv->GVS[sv->StdView].translateVec[0] +=
6132                      (GLfloat)sv->GVS[sv->StdView].translateDeltaX * zc_fac;
6133                   sv->GVS[sv->StdView].translateVec[1] +=
6134                      (GLfloat)sv->GVS[sv->StdView].translateDeltaY * zc_fac;
6135                   sv->GVS[sv->StdView].translateBeginX = mevx;
6136                   sv->GVS[sv->StdView].translateBeginY = mevy;
6137                   SUMA_postRedisplay(w, clientData, callData);
6138                }
6139             }
6140             break;
6141 
6142          case SUMA_Button_3_Motion: {
6143             SUMA_ALL_DO *lado=NULL;
6144             SUMA_DO_Types lado_type=NOT_SET_type;
6145 
6146             if (sv->LastSel_ado_idcode_str) {
6147                lado = SUMA_whichADOg(sv->LastSel_ado_idcode_str);
6148                if (lado) lado_type = lado->do_type;
6149             }
6150 
6151             SUMA_LH("In button 3 motion, lado=%s", ADO_LABEL(lado));
6152 
6153             /* Clear any pre-existing selection */
6154             if (!SUMA_Add_To_PickResult_List(sv, NULL, "TERSUM", NULL)) {
6155                SUMA_S_Err("Failed to clear selections");
6156                break;
6157             }
6158 
6159             if (SUMAg_CF->ROI_mode && SUMA_SV_Focus_SO(sv) && sv->BS) {
6160                /* ROI drawing mode */
6161                ii = SUMA_RegisteredSOs(sv, SUMAg_DOv, NULL);
6162                if (ii == 0) { /* no surfaces, break */
6163                   break;
6164                }
6165 
6166 
6167                if (!SUMA_GetSelectionLine(sv,
6168                      (int)Mev.x, (int)Mev.y, sv->Pick0, sv->Pick1,
6169                      0, NULL, NULL, NULL)) {
6170                   fprintf (SUMA_STDERR, "Error %s: Failed in "
6171                                        "SUMA_GetSelectionLine.\n", FuncName);
6172                   break;
6173                }
6174 
6175                if (!SUMA_AddToBrushStroke (sv, (int)Mev.x, (int)Mev.y,
6176                      sv->Pick0, sv->Pick1, YUP)) {
6177                   SUMA_RegisterMessage (SUMAg_CF->MessageList,
6178                                         "Failed to add to BrushStroke.",
6179                                         FuncName,
6180                                         SMT_Error, SMA_LogAndPopup);
6181                   break;
6182                }
6183             } else {
6184                /* Mev.state does not work in the line below */
6185                if ((Kev.state & ShiftMask) && (Kev.state & ControlMask) ) {
6186                   SUMA_LH("Allowing callbacks");
6187                   SUMAg_CF->HoldClickCallbacks = 0;
6188                } else {
6189                   SUMA_LH("Holding back callbacks");
6190                   SUMAg_CF->HoldClickCallbacks = 1;
6191                }
6192 
6193                if (SUMAg_N_SVv > 1) {
6194                   ii = SUMA_WhichViewerInMomentum (SUMAg_SVv,
6195                                                    SUMAg_N_SVv, NULL);
6196                   if (ii >= 0) {
6197                      sprintf (s, "You cannot select or draw while viewers\n"
6198                                  "(like viewer %c) are in momentum mode.\n",
6199                                  ii+65);
6200                      SUMA_RegisterMessage (SUMAg_CF->MessageList,
6201                                            s, FuncName, SMT_Error,
6202                                            SMA_LogAndPopup);
6203                      SUMA_RETURNe;
6204                   }
6205                }
6206 
6207                if (!(Kev.state & ShiftMask) && (Kev.state & ControlMask) ) {
6208                   SUMA_LH("Yoking intensity to node selection");
6209                   SUMAg_CF->YokeIntToNode = 1;
6210                } else {
6211                   SUMA_LH("Holding back callbacks");
6212                   SUMAg_CF->YokeIntToNode = 0;
6213                }
6214 
6215                   #if 0
6216                   /* Try this if you are having OpenGLStateReset problems at
6217                      node selection time. It is inefficient, but helps point
6218                      to the problem.
6219                      Look at recent updates to SE_Redisplay*All* for the more
6220                      appropriate fix */
6221                   SUMA_S_Note("Blunt fix:");
6222                   SUMA_OpenGLStateReset(SUMAg_DOv, SUMAg_N_DOv, sv);
6223                   SUMA_handleRedisplay((XtPointer)sv->X->GLXAREA);
6224                   #endif
6225 
6226                if (!SUMA_GetSelectionLine (  sv, (int)Mev.x, (int)Mev.y,
6227                                              sv->Pick0, sv->Pick1, 0,
6228                                              NULL, NULL, NULL)) {
6229                   fprintf (SUMA_STDERR,
6230                            "Error %s: Failed in SUMA_GetSelectionLine.\n",
6231                            FuncName);
6232                   break;
6233                }
6234 
6235                   if (lado_type == NOT_SET_type ||
6236                       lado_type == MASK_type) {
6237                if (!MASK_MANIP_MODE(sv)) { /* You don't want these if
6238                                               you're moving them! */
6239                   SUMA_LH("Mask picking");
6240                   hit = SUMA_ComputeLineMaskIntersect (sv, SUMAg_DOv, 0, NULL);
6241                   if (hit < 0) {
6242                      SUMA_S_Err("Failed in SUMA_ComputeLineMaskIntersect.");
6243                   }
6244                }
6245                   }
6246 
6247                   if (lado_type == NOT_SET_type ||
6248                       lado_type == TRACT_type || lado_type == GRAPH_LINK_type) {
6249                if (1) {
6250                   SUMA_LH("Tract picking");
6251                   hit = SUMA_ComputeLineDOsIntersect (sv, SUMAg_DOv, 0, NULL);
6252                   if (hit < 0) {
6253                      SUMA_S_Err("Failed in SUMA_ComputeLineDOsIntersect.");
6254                   }
6255                }
6256                   }
6257 
6258                   if (lado_type == NOT_SET_type ||
6259                       lado_type == VO_type) {
6260                if (1) {
6261                    SUMA_LH("Trying for volume intersections");
6262                    hit =  SUMA_ComputeLineVOslicesIntersect(sv, SUMAg_DOv,
6263                                                             0, NULL);
6264                    if (hit < 0) {
6265                       fprintf( SUMA_STDERR,
6266                          "Error %s: "
6267                          "Failed in SUMA_MarkLineVOslicesIntersect.\n",
6268                                FuncName);
6269                    }
6270                }
6271                if (1) {
6272                    SUMA_LH("Trying for volume rendering intersections");
6273                    hit =  SUMA_ComputeLineVOvrIntersect(sv, SUMAg_DOv,
6274                                                             0, NULL);
6275                    if (hit < 0) {
6276                       fprintf( SUMA_STDERR,
6277                          "Error %s: "
6278                          "Failed in SUMA_MarkLineVOvrIntersect.\n",
6279                                FuncName);
6280                    }
6281                }
6282                   }
6283 
6284                   if ((lado_type == NOT_SET_type ||
6285                        lado_type == SO_type)) {
6286                ii = SUMA_RegisteredSOs(sv, SUMAg_DOv, NULL);
6287                if (ii > 0) { /* some surfaces, try */
6288                   /* perform the intersection calcluation and mark the surface */
6289                   hit = SUMA_ComputeLineSurfaceIntersect (sv, SUMAg_DOv,
6290                                                           1, NULL);
6291                   if (hit < 0) {
6292                      fprintf( SUMA_STDERR,
6293                               "Error %s: "
6294                               "Failed in SUMA_ComputeLineSurfaceIntersect.\n",
6295                               FuncName);
6296                      break;
6297                   }
6298                }
6299                   }
6300 
6301                ASSESS_MOTION:
6302                SUMA_LH("Assessment");
6303                if (dlist_size(sv->SelAdo)) {
6304                   if (!SUMA_Process_Selected_ADO(sv, SUMA_ALTHELL)) {
6305                      SUMA_S_Err("Failed to process selected ado");
6306                      SUMA_RETURNe;
6307                   }
6308 
6309                   /* redisplay */
6310                   sv->ResetGLStateVariables = YUP;
6311                   SUMA_handleRedisplay((XtPointer)sv->X->GLXAREA);
6312                }
6313                OUT_MOTION:
6314                /* reset hold on xforms */
6315                SUMAg_CF->HoldClickCallbacks = 0;
6316             }
6317 
6318             break; }
6319       }
6320 
6321 
6322       break;
6323   }/* switch event type */
6324 
6325    SUMA_RETURNe;
6326 }
6327 
6328 /*!
6329    SUMA_momentum(XtPointer clientData, XtIntervalId *id);
6330 
6331    client data contains the widget responsible for the call to SUMA_momentum
6332 */
SUMA_momentum(XtPointer clientData,XtIntervalId * id)6333 void SUMA_momentum(XtPointer clientData, XtIntervalId *id)
6334 {
6335    static char FuncName[]={"SUMA_momentum"};
6336    static int ReDisp;
6337    Widget w;
6338    int isv;
6339    SUMA_SurfaceViewer *sv;
6340 
6341    SUMA_ENTRY;
6342 
6343    /* the widget is passed as client data */
6344    w = (Widget)clientData;
6345 
6346    /* find out which Surface viewer the widget belongs to */
6347    SUMA_ANY_WIDGET2SV((Widget)clientData, sv, isv);
6348    if (isv < 0) {
6349       SUMA_S_Err("Failed in macro SUMA_ANY_WIDGET2SV.");
6350       SUMA_RETURNe;
6351    }
6352 
6353 
6354    ReDisp = 0;
6355    if ( ((sv->GVS[sv->StdView].spinDeltaX*sv->GVS[sv->StdView].spinDeltaX) >
6356                                           sv->GVS[sv->StdView].MinIdleDelta ) ||
6357         ((sv->GVS[sv->StdView].spinDeltaY*sv->GVS[sv->StdView].spinDeltaY) >
6358                                           sv->GVS[sv->StdView].MinIdleDelta ) )
6359       { /* rotate if SUMA_momentum is enabled and spinDeltaX or spinDeltaY
6360            are larger than the minimum set */
6361          /*fprintf(stdout,"SUMA_momentum:  spinDeltaX %f spinDeltaY %f\n",
6362                   sv->GVS[sv->StdView].spinDeltaX,
6363                   sv->GVS[sv->StdView].spinDeltaY);*/
6364          add_quats ( sv->GVS[sv->StdView].deltaQuat,
6365                      sv->GVS[sv->StdView].currentQuat,
6366                      sv->GVS[sv->StdView].currentQuat);
6367          ReDisp = 1;
6368       }
6369    if ( ((sv->GVS[sv->StdView].translateDeltaX*
6370           sv->GVS[sv->StdView].translateDeltaX) >
6371                                           sv->GVS[sv->StdView].MinIdleDelta ) ||
6372         ((sv->GVS[sv->StdView].translateDeltaY*
6373           sv->GVS[sv->StdView].translateDeltaY) >
6374                                           sv->GVS[sv->StdView].MinIdleDelta ) )
6375       { /* translate */
6376          sv->GVS[sv->StdView].translateVec[0] +=
6377                         (GLfloat)sv->GVS[sv->StdView].translateDeltaX;
6378          sv->GVS[sv->StdView].translateVec[1] +=
6379                         (GLfloat)sv->GVS[sv->StdView].translateDeltaY;
6380          ReDisp = 1;
6381       }
6382    if (ReDisp) {
6383       /*fprintf(stdout,"Momentum Redisplay\n");*/
6384       SUMA_postRedisplay(w, NULL, NULL);
6385    }
6386     sv->X->MOMENTUMID = XtAppAddTimeOut(SUMAg_CF->X->App, 1,
6387                                           SUMA_momentum, (XtPointer) w);
6388 
6389   SUMA_RETURNe;
6390 }
6391 
6392 /*
6393    Show the buffer used to pick displayable objects (DOs) with color id
6394    The function adds a cross hair (4 white pixels) around the selection
6395    point and displays the image in AFNI's viewer (InViewer == 1) and/or
6396    save the image to an image file if OnDisk is not NULL.
6397    Set OnDisk to something like PickBuff.ppm so that you get the exact
6398    pixels rendered in the output. Consecutive images are numbered with
6399    a time stamp.
6400 
6401    Note that the 4th chanel (alpha) is not shown or written to disk at
6402    the moment.
6403 */
SUMA_MarkPickInBuffer4(SUMA_SurfaceViewer * sv,int InViewer,char * OnDisk)6404 SUMA_Boolean SUMA_MarkPickInBuffer4(SUMA_SurfaceViewer *sv, int InViewer,
6405                                     char *OnDisk)
6406 {
6407    static char FuncName[]={"SUMA_MarkPickInBuffer4"};
6408    int n4, n3, n, p0[5],p1[5],p2[5],p3[5], i;
6409    SUMA_Boolean LocalHead = NOPE;
6410 
6411    SUMA_ENTRY;
6412    if (!InViewer && !OnDisk) {
6413       SUMA_S_Err("Nothing to do here");
6414       SUMA_RETURN(NOPE);
6415    }
6416    if (!sv->pickrenpix4) {
6417       SUMA_S_Err("Empty buffer array");
6418       SUMA_RETURN(NOPE);
6419    }
6420    p0[0] = -1; /* to hold index of pixel being marked */
6421    p1[0] = -1;
6422    p2[0] = -1;
6423    p3[0] = -1;
6424 
6425    n4 = sv->PickPix[1]*sv->X->aWIDTH + sv->PickPix[0];
6426    n4 = 4*n4;
6427    SUMA_LHv("User pixel selection from whole buffer:"
6428                   " at %d %d is: %d %d %d %d\n",
6429                   sv->PickPix[0], sv->PickPix[1],
6430                   sv->pickrenpix4[n4] , sv->pickrenpix4[n4+1],
6431                   sv->pickrenpix4[n4+2] , sv->pickrenpix4[n4+3]);
6432    if (sv->PickPix[1] > 0) {
6433       n4 = (sv->PickPix[1]-1)*sv->X->aWIDTH + sv->PickPix[0];
6434       n4 = 4*n4;
6435       p0[0] = n4; p0[1] = sv->pickrenpix4[n4  ]; p0[2] = sv->pickrenpix4[n4+1];
6436                   p0[3] = sv->pickrenpix4[n4+2]; p0[4] = sv->pickrenpix4[n4+3];
6437       sv->pickrenpix4[n4] = sv->pickrenpix4[n4+1] =
6438          sv->pickrenpix4[n4+2] = sv->pickrenpix4[n4+3] = 255;
6439    }
6440    if (sv->PickPix[0] > 0) {
6441       n4 = sv->PickPix[1]*sv->X->aWIDTH + sv->PickPix[0]-1;
6442       n4 = 4*n4;
6443       p1[0] = n4; p1[1] = sv->pickrenpix4[n4  ]; p1[2] = sv->pickrenpix4[n4+1];
6444                   p1[3] = sv->pickrenpix4[n4+2]; p1[4] = sv->pickrenpix4[n4+3];
6445       sv->pickrenpix4[n4] = sv->pickrenpix4[n4+1] =
6446          sv->pickrenpix4[n4+2] = sv->pickrenpix4[n4+3] = 255;
6447    }
6448    if (sv->PickPix[1] < (sv->X->aHEIGHT-1)) {
6449       n4 = (sv->PickPix[1]+1)*sv->X->aWIDTH + sv->PickPix[0];
6450       n4 = 4*n4;
6451       p2[0] = n4; p2[1] = sv->pickrenpix4[n4  ]; p2[2] = sv->pickrenpix4[n4+1];
6452                   p2[3] = sv->pickrenpix4[n4+2]; p2[4] = sv->pickrenpix4[n4+3];
6453       sv->pickrenpix4[n4] = sv->pickrenpix4[n4+1] =
6454          sv->pickrenpix4[n4+2] = sv->pickrenpix4[n4+3] = 255;
6455    }
6456    if (sv->PickPix[0] < (sv->X->aWIDTH-1)) {
6457       n4 = sv->PickPix[1]*sv->X->aWIDTH + sv->PickPix[0]+1;
6458       n4 = 4*n4;
6459       p3[0] = n4; p3[1] = sv->pickrenpix4[n4  ]; p3[2] = sv->pickrenpix4[n4+1];
6460                   p3[3] = sv->pickrenpix4[n4+2]; p3[4] = sv->pickrenpix4[n4+3];
6461       sv->pickrenpix4[n4] = sv->pickrenpix4[n4+1] =
6462          sv->pickrenpix4[n4+2] = sv->pickrenpix4[n4+3] = 255;
6463    }
6464    if (InViewer) { /* show me the money in the interactive viewer */
6465       GLubyte *pp3 = (GLubyte *)SUMA_calloc(sv->X->aWIDTH*sv->X->aHEIGHT*3,
6466                                             sizeof(GLubyte));
6467       for (n3=0,n=0; n<sv->X->aWIDTH*sv->X->aHEIGHT; ++n) {
6468          n4=4*n;
6469          pp3[n3++]= sv->pickrenpix4[n4++];
6470          pp3[n3++]= sv->pickrenpix4[n4++];
6471          pp3[n3++]= sv->pickrenpix4[n4++]; n4++;
6472       }
6473       ISQ_snapsave(sv->X->aWIDTH, -sv->X->aHEIGHT,
6474                     (unsigned char *)pp3, sv->X->GLXAREA );
6475       SUMA_ifree(pp3);
6476    }
6477    if (OnDisk) {
6478       if (!SUMA_PixelsToDisk(sv, sv->X->aWIDTH, -sv->X->aHEIGHT,
6479                           (GLvoid *)sv->pickrenpix4, 4, 1, OnDisk, 1, 0)) {
6480          SUMA_S_Err("Failed to write pix to disk");
6481       }
6482    }
6483    /* now put things back where you found them */
6484    if (p0[0] >= 0) {
6485       for (i=0; i<4; ++i) sv->pickrenpix4[p0[0]+i] = p0[1+i];
6486    }
6487    if (p1[0] >= 0) {
6488       for (i=0; i<4; ++i) sv->pickrenpix4[p1[0]+i] = p1[1+i];
6489    }
6490    if (p2[0] >= 0) {
6491       for (i=0; i<4; ++i) sv->pickrenpix4[p2[0]+i] = p2[1+i];
6492    }
6493    if (p3[0] >= 0) {
6494       for (i=0; i<4; ++i) sv->pickrenpix4[p3[0]+i] = p3[1+i];
6495    }
6496 
6497    SUMA_RETURN(YUP);
6498 }
6499 
6500 /*
6501    Function to return the color in the stored rendering buffer.
6502    checking is done at the pick location first, then in progressively
6503    larger neighborhoods
6504 
6505    When first called, the search begins at pixel  *i, *j. If nothing
6506    is found, the search proceeds within the layer limit and the first
6507    non-blank find is returned in pixhit. *i and *j are set to the newly
6508    picked location
6509 
6510    i is along the width, j along the height
6511 */
SUMA_GetColidInPickBuffer4(GLubyte * pix,int Ni,int Nj,int * ii,int * ji,int maxlay,GLubyte * colid)6512 SUMA_Boolean SUMA_GetColidInPickBuffer4(GLubyte *pix, int Ni, int Nj,
6513                                         int *ii, int *ji,
6514                                         int maxlay, GLubyte *colid)
6515 {
6516    static char FuncName[]={"SUMA_GetColidInPickBuffer4"};
6517    int i0, j0, i, j, n4, k;
6518    int poff[(1+2*2)*(1+2*2)][2] = {
6519                       {0,0}, {-1,0}, {-1,-1}, {0,-1}, {-1,-1}, {1,1},/*A..F*/
6520                       {-2,0}, {-2,1}, {-2,-2}, {-1,-2}, {0,-2}, {-2,1},/*G..L*/
6521                       {1,-1}, {0,1}, {1,0}, {-2,2}, {1,-2}, {-1,2},/*M..R*/
6522                       {2,-2}, {2,-1},{0,2}, {2,0},{1,2},{2,1},{2,2} }/*S..Y*/;
6523 
6524    SUMA_ENTRY;
6525 
6526    if (!pix || !ii || !ji || *ii <0 || *ii >= Ni || *ji<0 || *ji>Nj) {
6527       SUMA_S_Err("Bad input");
6528       SUMA_RETURN(NOPE);
6529    }
6530    if (maxlay < 0) maxlay = 0;
6531    if (maxlay > 2) {
6532       SUMA_S_Warn("Not ready for more than two layers");
6533       maxlay = 2;
6534    }
6535 
6536    i = *ii; j = *ji;
6537    n4 = 4*(i+j*Ni);
6538    if (pix[n4] || pix[n4+1] || pix[n4+2] ||  pix[n4+3]) {
6539       memcpy(colid, pix+n4, 4*sizeof(GLubyte));
6540       SUMA_RETURN(YUP);
6541    }
6542 
6543    if (maxlay == 0) SUMA_RETURN(NOPE);
6544 
6545    /* search in order shown on page 235 of labbook NIH-6
6546       The idea is to follow a search pattern based on the shape
6547       of the cursor pointing up and slightly left
6548       Actually maxlay == 1 is not quite honored here */
6549    i0 = *ii; j0 = *ji;
6550    k=1;
6551    while (k<25) {
6552       if ((i=i0+poff[k][0]) >=0 && i<Ni &&
6553           (j=j0+poff[k][0]) >=0 && j<Nj) {
6554          n4 = 4*(i+j*Ni);
6555          if (pix[n4] || pix[n4+1] || pix[n4+2] ||  pix[n4+3]) {
6556             memcpy(colid, pix+n4, 4*sizeof(GLubyte));
6557             *ii=i; *ji=j;
6558             SUMA_RETURN(YUP);
6559          }
6560       }
6561       ++k;
6562    }
6563    /* if nothing is found, and users want more layers, start searching in
6564       squarish patterns from layer 3 onwards, someday */
6565 
6566    SUMA_RETURN(NOPE);
6567 }
6568 
6569 /* if action == 0: Free saved DO picking buffer in pickrenpix4
6570                 1: Recreate pickrenpix4 regardless of whether or
6571                    not the new pickrenpix4 is expected to change
6572                 2: Recreate pickrenpix4 only if deemed necessary
6573 */
SUMA_PickBuffer(SUMA_SurfaceViewer * sv,int action,SUMA_DO * dov)6574 SUMA_Boolean SUMA_PickBuffer(SUMA_SurfaceViewer *sv, int action, SUMA_DO *dov)
6575 {
6576    static char FuncName[]={"SUMA_PickBuffer"};
6577    int Flush = 0;
6578    SUMA_Boolean LocalHead = NOPE;
6579 
6580    SUMA_ENTRY;
6581 
6582    if (!sv) {
6583       SUMA_S_Err("Null sv!");
6584       SUMA_RETURN(NOPE);
6585    }
6586 
6587    if ( action == 0 || /* flush only */
6588         action == 1 /* Recreate regardless */ ) {
6589          /* flush needed */
6590          SUMA_LH("Flushing pickrenpix4");
6591          Flush = 1;
6592    } else if (action == 2) { /* recreate if needed */
6593       if (  MASK_MANIP_MODE(sv) ||
6594             SUMA_DiffGeomViewStruct(sv->GVS[sv->StdView],
6595                                   sv->GVS_last_PickMode[sv->StdView], 1) ||
6596             sv->FOV[sv->iState] != sv->FOV_last_PickMode[sv->iState]) {
6597          SUMA_LH("Have a changed GVS or FOV, flushing pickrenpix4");
6598          Flush = 1;
6599       } else {
6600          SUMA_LH("GVS and FOV still the same");
6601       }
6602    } else {
6603       SUMA_S_Errv("Bad action value %d\n", action);
6604       SUMA_RETURN(NOPE);
6605    }
6606 
6607    if (Flush) {
6608       SUMA_LHv("Flushing, action %d\n", action);
6609       if (sv->pickrenpix4) SUMA_free(sv->pickrenpix4);
6610       sv->pickrenpix4 = NULL;
6611    }
6612 
6613    if (action == 0) SUMA_RETURN(YUP);
6614 
6615    /* recreate if no pickrenpix4 */
6616    if (!sv->pickrenpix4) {
6617       /* record GVS and FOV states*/
6618       SUMA_CopyGeomViewStruct(&(sv->GVS[sv->StdView]),
6619                               &(sv->GVS_last_PickMode[sv->StdView]));
6620       sv->FOV_last_PickMode[sv->iState] = sv->FOV[sv->iState];
6621 
6622       sv->DO_PickMode = 1;
6623       SUMA_display(sv, dov);
6624       if (!(sv->pickrenpix4 =
6625                SUMA_grabPixels(4, sv->X->aWIDTH, sv->X->aHEIGHT))) {
6626          SUMA_S_Err("Failed to grab pixels");
6627       }
6628       sv->DO_PickMode = 0;
6629    }
6630    SUMA_RETURN(YUP);
6631 }
6632 
SUMA_ADO_Flush_Pick_Buffer(SUMA_ALL_DO * ado,SUMA_SurfaceViewer * sv)6633 SUMA_Boolean SUMA_ADO_Flush_Pick_Buffer(SUMA_ALL_DO *ado, SUMA_SurfaceViewer *sv)
6634 {
6635    static char FuncName[]={"SUMA_ADO_Flush_Pick_Buffer"};
6636    int ii;
6637    SUMA_ENTRY;
6638 
6639    if (!ado) SUMA_RETURN(NOPE);
6640    if (sv) {
6641       if (SUMA_ADO_isRegistered(sv, ado)) {
6642          SUMA_PickBuffer(sv, 0, NULL);
6643       }
6644    } else { /* Do it for all */
6645       for (ii=0; ii<SUMAg_N_SVv; ++ii) {
6646          sv = &(SUMAg_SVv[ii]);
6647          if (SUMA_ADO_isRegistered(sv, ado)) {
6648             SUMA_PickBuffer(sv, 0, NULL);
6649          }
6650       }
6651    }
6652 
6653    SUMA_RETURN(YUP);
6654 }
6655 
6656 /* What was picked on a frame SO ?
6657    See SUMA_Surface_Of_NIDO_Matrix() for how surface coordinates
6658    translate to matrix pixels and eventually matrix cells.
6659    See also SUMA_GDSET_edgeij_to_GMATRIX_XYZ() and SUMA_DrawGraphDO_GMATRIX()
6660 */
SUMA_WhatWasPicked_FrameSO(SUMA_SurfaceViewer * sv,int ido)6661 SUMA_PICK_RESULT *SUMA_WhatWasPicked_FrameSO(SUMA_SurfaceViewer *sv, int ido)
6662 {
6663    static char FuncName[]={"SUMA_WhatWasPicked_FrameSO"};
6664    float P0[3], P1[3];
6665    int N[3], IIm[3], ii, jj, i, *ui=NULL, *uj=NULL, si=-1, G[3], B[3], M[3];
6666    SUMA_ALL_DO *ado=NULL;
6667    SUMA_DSET *dset=NULL;
6668    SUMA_PICK_RESULT *PR = NULL;
6669    SUMA_SurfaceObject *SO=NULL;
6670    SUMA_GRAPH_SAUX *GSaux = NULL;
6671    SUMA_MT_INTERSECT_TRIANGLE *MTI = NULL;
6672    double Aff[4][4], I[3], V[12], X[3], GB[3];
6673    SUMA_Boolean LocalHead = NOPE;
6674 
6675    SUMA_ENTRY;
6676 
6677    if (!sv ) {
6678       SUMA_S_Err("NULL input");
6679       SUMA_RETURN(PR);
6680    }
6681 
6682    if (!iDO_isGLDO(ido) || !iDO_is_variant(ido,"GMATRIX")) {
6683       /* No frame SOs in this DO, not an error just keep looking */
6684       SUMA_LH("Not a DO with a FrameSO, go on");
6685       SUMA_RETURN(PR);
6686    }
6687 
6688    SUMA_LHv("Pick Query for FrameSO on sv %p, ido %d\n", sv, ido);
6689    if (iDO_is_variant(ido,"GMATRIX") &&
6690        (GSaux = iDO_GSaux(ido))) {
6691        SO=GSaux->FrameSO;
6692    }
6693    if (!SO) {
6694       SUMA_S_Errv("No FrameSO on %s\n", iDO_label(ido));
6695       SUMA_RETURN(PR);
6696    }
6697    NI_GET_DOUBLEv(GSaux->nido->ngr, "dicom_real_to_ijk", V, 12, LocalHead);
6698    if (!NI_GOT) {
6699       SUMA_S_Err("No dicom_real_to_ijk");
6700       SUMA_RETURN(PR);
6701    }
6702    V12_TO_AFF44(Aff, V);
6703 
6704    NI_GET_INTv(GSaux->nido->ngr, "Nijk", N, 3, LocalHead);
6705    if (!NI_GOT) {
6706       SUMA_S_Err("No Nijk");
6707       SUMA_RETURN(PR);
6708    }
6709 
6710    /* total num of pixels per value */
6711    NI_GET_INTv(GSaux->nido->ngr, "PixCount", M, 3, LocalHead);
6712    NI_GET_INTv(GSaux->nido->ngr, "PixPerVal", G, 3, LocalHead);
6713    NI_GET_INTv(GSaux->nido->ngr, "BorWid", B, 3, LocalHead);
6714    GB[0] = G[0]+B[0];
6715    GB[1] = G[1]+B[1];
6716    GB[2] = G[2]+B[2];
6717 
6718    SUMA_COPY_VEC(sv->Pick0, P0, 3, GLdouble, float);
6719    SUMA_COPY_VEC(sv->Pick1, P1, 3, GLdouble, float);
6720    if (!(MTI = SUMA_MT_intersect_triangle(P0, P1,
6721                            SO->NodeList, SO->N_Node,
6722                            SO->FaceSetList, SO->N_FaceSet, NULL, 0))){
6723       SUMA_S_Err("SUMA_MT_intersect_triangle failed.");
6724       SUMA_RETURN(PR);
6725    }
6726 
6727    if (MTI->N_hits < 1) {
6728       SUMA_LH("No hits");
6729       SUMA_RETURN(PR);
6730    }
6731 
6732    ui = uj = NULL;
6733    if (iDO_isGLDO(ido)) {
6734       if (!(dset = SUMA_find_GLDO_Dset((SUMA_GraphLinkDO*)SUMAg_DOv[ido].OP))) {
6735          SUMA_S_Err("No dset for GLDO?");
6736          SUMA_RETURN(PR);
6737       }
6738       GSaux->IgnoreSelection = 0; /* Selection being made on matrix
6739                                      representation of graph.
6740                                      turn off IgnoreSelection */
6741       switch (dset->Aux->matrix_shape) {
6742          case MAT_FULL:
6743          case MAT_TRI:
6744          case MAT_TRI_DIAG:
6745             SUMA_LH("Direct indexing between edge points and matrix row/col");
6746             break;
6747          case MAT_SPARSE:
6748             if (!dset->inel) {
6749                SUMA_S_Err("Don't have inel, badly shaped dataset");
6750                SUMA_RETURN(PR);
6751             }
6752             if (  !(ui = SUMA_GetUniqueIndicesVec(dset,1)) ||
6753                   !(uj = SUMA_GetUniqueIndicesVec(dset,2))    ) {
6754                SUMA_S_Err("Failed to get unique indices");
6755                SUMA_RETURN(PR);
6756             }
6757             break;
6758          default:
6759             SUMA_S_Err("Rats");
6760             SUMA_RETURN(PR);
6761             break;
6762       }
6763    }
6764 
6765 
6766    PR = SUMA_New_Pick_Result(PR);
6767    PR->ado_idcode_str = SUMA_replace_string(PR->ado_idcode_str,iDO_idcode(ido));
6768    ado = SUMA_whichADOg(PR->ado_idcode_str);
6769    PR->primitive = SUMA_replace_string(PR->primitive, "none yet");
6770    PR->primitive_index = -1;
6771    PR->PickXYZ[0] = MTI->P[0];
6772    PR->PickXYZ[1] = MTI->P[1];
6773    PR->PickXYZ[2] = MTI->P[2];
6774    sv->Focus_DO_ID = ido;
6775 
6776    PR->datum_index = -1;
6777    for (i=0; i<SUMA_N_IALTSEL_TYPES; ++i) PR->iAltSel[i] = -1;
6778    for (i=0; i<SUMA_N_DALTSEL_TYPES; ++i) PR->dAltSel[i] = 0.0;
6779    AFF44_MULT_I(I, Aff, MTI->P);
6780 
6781    SUMA_LHv("Hit on Frame SO, triangle %d %f %f %f, M=[%d %d]\n"
6782             "     Pixel hit: %.2f %.2f %.2f\n",
6783             MTI->ifacemin, MTI->P[0], MTI->P[1], MTI->P[2], M[0], M[1],
6784             I[0], I[1], I[2]);
6785 
6786    for (i=0; i<3; ++i) {
6787       /* I[i] = I[i]/GB[i]; Account for per value gain and
6788                                     background thickness */
6789       /* Round I to get matrix pixel coordinates*/
6790       I[i] = SUMA_ROUND(I[i]);
6791       if (I[i] < 0) IIm[i] = -1;
6792       else if (I[i] >= GB[i]*N[i]) IIm[i] = -1;
6793       else IIm[i] = (int)(I[i]/GB[i]); /* Undo gain to get back to matrix grid */
6794    }
6795 
6796    if (ui) { /* sparse hell */
6797       if (IIm[0]>=0) ii = ui[IIm[0]];
6798       else ii = -1;
6799       if (IIm[1]>=0) jj = uj[IIm[1]];
6800       else jj = -1;
6801    } else {
6802       ii = IIm[0]; jj=IIm[1];
6803    }
6804 
6805    SUMA_LHv("In face %d X=[%f %f %f]dicom, GB=[%d %d] \n"
6806             "     If=[%f %f %f], Im=[%d %d %d], ii,jj=[%d %d]\n",
6807             MTI->ifacemin, MTI->P[0], MTI->P[1], MTI->P[2],
6808             (int)GB[0], (int)GB[1],
6809             I[0], I[1], I[2], IIm[0], IIm[1], IIm[2], ii, jj);
6810 
6811    switch (MTI->ifacemin) {
6812       case 0:
6813       case 1:
6814          /* in matrix */
6815          PR->primitive = SUMA_replace_string(PR->primitive, "segments");
6816          if (!ui && ii >= 0 && jj >= 0) {
6817             PR->datum_index = PR->primitive_index = ii+jj*N[0];
6818          } else { /* sparse */
6819             if (ii < 0 || jj < 0) {
6820                PR->datum_index = PR->primitive_index = -1;
6821             } else if (!SUMA_GDSET_PointsToSegIndex(dset, ii, jj, &si)) {
6822                SUMA_S_Errv("Failed to find segment for %d %d\n", ii, jj);
6823                if (LocalHead) SUMA_DUMP_TRACE("Now what?");
6824                PR->datum_index = PR->primitive_index = -1;
6825             } else {
6826                PR->datum_index = PR->primitive_index = si;
6827             }
6828          }
6829          if (MTI->ifacemin == 1) {
6830             PR->iAltSel[SUMA_ENODE_0] = ii;
6831             PR->iAltSel[SUMA_ENODE_1] = jj;
6832          } else {
6833             PR->iAltSel[SUMA_ENODE_0] = jj;
6834             PR->iAltSel[SUMA_ENODE_1] = ii;
6835          }
6836          break;
6837       case 2:
6838       case 3:
6839          /* top edge*/
6840          PR->primitive = SUMA_replace_string(PR->primitive, "balls");
6841          PR->iAltSel[SUMA_ENODE_0] = jj;
6842          PR->iAltSel[SUMA_ENODE_1] = -1;
6843          break;
6844       case 4:
6845       case 5:
6846          /* right edge*/
6847          PR->primitive = SUMA_replace_string(PR->primitive, "balls");
6848          PR->iAltSel[SUMA_ENODE_0] = ii;
6849          PR->iAltSel[SUMA_ENODE_1] = -1;
6850          break;
6851       case 6:
6852       case 7:
6853          /* bottom edge*/
6854          PR->primitive = SUMA_replace_string(PR->primitive, "balls");
6855          PR->iAltSel[SUMA_ENODE_0] = jj;
6856          PR->iAltSel[SUMA_ENODE_1] = -1;
6857          break;
6858       case 8:
6859       case 9:
6860          /* left edge*/
6861          PR->primitive = SUMA_replace_string(PR->primitive, "balls");
6862          PR->iAltSel[SUMA_ENODE_0] = ii;
6863          PR->iAltSel[SUMA_ENODE_1] = -1;
6864          break;
6865       default:
6866          SUMA_S_Errv("Faceset %d???\n", MTI->ifacemin);
6867          SUMA_RETURN(PR);
6868    }
6869 
6870    MTI = SUMA_Free_MT_intersect_triangle(MTI);
6871 
6872    SUMA_RETURN(PR);
6873 }
6874 
6875 /*!
6876    \brief find the closest location on a bundle to a point
6877    on the screen.
6878 
6879    p (void *) One bundle
6880    ptype (char): Type of bundle. 'N' --> p is a NI_element *
6881                                  'B' --> p is a TAYLOR_BUNDLE *
6882    Tmask (int): if not < 0 then only consider intersections in
6883                 tract Tmask of the bundle.
6884    sv (SUMA_SurfaceViewer *) The viewer
6885    scpx (float *) The pixel location in screen coordinates.
6886                   If NULL, form it from sv->PickPix;
6887    crude (int) if (0) then use crude search, return closest point
6888                       not bothering with where along the segment
6889                       between the two closest points you are
6890                   1   a fine search, locating where between
6891                       the two closest points one clicked
6892    tmin (int *) Will contain the index of the tract that was
6893                 hit within the bundle (-1 for no cigar)
6894    pmin (int *) Will contain the index of the closest point
6895                 to the hit in tract tmin
6896    fmin (float *) Will contain the fraction between pmin and
6897                   pmin+1 where intersection occurred
6898    mindist (float *) Distance from closest location on tract
6899    \ret YUP all good, NOPE nothing found or bad input
6900 */
6901 
SUMA_Bundle_Pick_Intersect(void * p,char ptype,int Tmask,SUMA_SurfaceViewer * sv,float * scpxu,int crude,int * tmin,int * pmin,float * frmin,float * mindistu)6902 SUMA_Boolean SUMA_Bundle_Pick_Intersect(void *p, char ptype, int Tmask,
6903                      SUMA_SurfaceViewer *sv, float *scpxu, int crude,
6904                      int *tmin, int *pmin, float *frmin, float *mindistu)
6905 {
6906    static char FuncName[]={"SUMA_Bundle_Pick_Intersect"};
6907    int nn, nnmin, mmmin, mm, nn_max, nn_min;
6908    float *scrxyz = NULL, dx, dy, dxy2=0.0, fmin, scpx[3];
6909    float  mindist2, mindist, *A, *B;
6910    double P[3], f = 0.0;
6911    NI_element *nelitp=NULL;
6912    TAYLOR_BUNDLE *tb = NULL;
6913    TAYLOR_TRACT *ttn=NULL;
6914 
6915    SUMA_ENTRY;
6916 
6917    if (tmin) *tmin = -1;
6918    if (pmin) *pmin = -1;
6919    if (frmin) *frmin = -1.0;
6920    if (mindistu) *mindistu = -1.0;
6921 
6922    if (!p || !sv) SUMA_RETURN(NOPE);
6923 
6924    nn_min = 0;
6925    if (ptype == 'N') { /* NI_element */
6926       nelitp = (NI_element *)p;
6927       nn_max = nelitp->vec_len;
6928    } else if (ptype == 'B') { /* Taylor Bundle */
6929       tb = (TAYLOR_BUNDLE *)p;
6930       if (Tmask < 0) {
6931          nn_max = tb->N_tracts;
6932       } else {
6933          if (Tmask < tb->N_tracts) {
6934             nn_min = Tmask;
6935             nn_max = nn_min+1;
6936          } else {
6937             SUMA_S_Err("Tmask (%d) exceeds number of tracts (%d) in bundle",
6938                        Tmask, tb->N_tracts);
6939             SUMA_RETURN(NOPE);
6940          }
6941       }
6942    } else {
6943       SUMA_RETURN(NOPE);
6944    }
6945 
6946    nnmin=-1; mmmin=-1; mindist=mindist2=1000; fmin=1.0;
6947    if (scpxu) {
6948       scpx[0] = scpxu[0]; scpx[1] = scpxu[1]; scpx[2] = scpxu[2];
6949    } else {
6950       GLint viewport[4];
6951       glGetIntegerv(GL_VIEWPORT, viewport);
6952       /* screen pick coordinate in screen coords */
6953       scpx[0] = sv->PickPix[0];
6954       scpx[1] = viewport[3]-sv->PickPix[1]-1;
6955       scpx[2] = 0.0;
6956    }
6957 
6958    for (nn=nn_min; nn<nn_max; ++nn) {
6959       if (nelitp) {
6960          ttn = (TAYLOR_TRACT *)(nelitp->vec[0])+nn;
6961       } else {
6962          ttn = tb->tracts+nn;
6963       }
6964       scrxyz = (float *)SUMA_calloc(ttn->N_pts3, sizeof(float));
6965       memcpy(scrxyz, ttn->pts, (ttn->N_pts3)*sizeof(float));
6966       /* tranform bundle points to screen space*/
6967       if (!SUMA_World2ScreenCoordsF(sv, ttn->N_pts3/3,
6968                                    ttn->pts, scrxyz, NULL,
6969                                    YUP, YUP)) {
6970          SUMA_S_Err("Failed to get screen coords");
6971          SUMA_RETURN(NOPE);
6972       }
6973       /* Now search for the closest point of bundle to the click
6974          The depth is ignored, in the hope that a simple closest
6975          search is enough. Otherwise we need to add a heuristic
6976          for the cost of depth */
6977       if (crude) {
6978             /* For finer tracing, you should project scpx onto the
6979             segment between the 1st and 2 points forming a segment,
6980             much like what is done in SUMA_PROJECT_C_ONTO_AB below
6981             The finer tracing is needed for crass paths. However
6982             for real bundles, on high-res data this might be
6983             overkill */
6984          mm=0;
6985          while (mm < ttn->N_pts3) {
6986             if ( ((dx = SUMA_ABS(scrxyz[mm  ]-scpx[0])) < mindist) &&
6987                  ((dy = SUMA_ABS(scrxyz[mm+1]-scpx[1])) < mindist) ){                            if ((dxy2 = dx*dx+dy*dy) < mindist2) {
6988                   mindist2 = dxy2;
6989                   mindist  = sqrtf(mindist2);
6990                   nnmin=nn; mmmin=mm/3;  fmin = 0.0;
6991                }
6992             }
6993             mm += 3;
6994          }
6995       } else {
6996          mm=0;
6997          while (mm < ttn->N_pts3-3) {
6998             A = scrxyz+mm;
6999             B = scrxyz+mm+3;
7000             SUMA_PROJECT_C_ONTO_AB(scpx, A, B, P, f);
7001             if ( (f > -0.2 && f < 1.2) &&
7002                  ((dx = SUMA_ABS(P[0]-scpx[0])) < mindist) &&
7003                  ((dy = SUMA_ABS(P[1]-scpx[1])) < mindist) ){                                    if ((dxy2 = dx*dx+dy*dy) < mindist2) {
7004                   mindist2 = dxy2;
7005                   mindist  = sqrtf(mindist2);
7006                   nnmin=nn; mmmin=mm/3; fmin=f;
7007                }
7008             }
7009             mm += 3;
7010          }
7011       }
7012       SUMA_ifree(scrxyz);
7013    }
7014 
7015    if (tmin) *tmin=nnmin;
7016    if (pmin) *pmin=mmmin;
7017    if (frmin) *frmin=fmin;
7018    if (mindistu) *mindistu = mindist;
7019 
7020    SUMA_RETURN(YUP);
7021 }
7022 
7023 /* Find the object that was picked, pointer copy to found object is
7024    returned in ucodf and should not be freed by calling function*/
SUMA_WhatWasPicked(SUMA_SurfaceViewer * sv,GLubyte * colid,SUMA_COLID_OFFSET_DATUM ** ucodf,int ipick,int jpick,SUMA_PICK_RESULT * PR)7025 SUMA_PICK_RESULT *SUMA_WhatWasPicked(SUMA_SurfaceViewer *sv, GLubyte *colid,
7026                                 SUMA_COLID_OFFSET_DATUM **ucodf,
7027                                 int ipick, int jpick,
7028                                 SUMA_PICK_RESULT *PR)
7029 {
7030    static char FuncName[]={"SUMA_WhatWasPicked"};
7031    DListElmt *el=NULL;
7032    SUMA_COLID_OFFSET_DATUM *cod=NULL, *codf=NULL;
7033    long int n4;
7034    int iii;
7035    double f = 0.0;
7036    SUMA_ALL_DO *ado=NULL;
7037    SUMA_DUMB_DO DDO;
7038    SUMA_Boolean LocalHead = NOPE;
7039 
7040    SUMA_ENTRY;
7041 
7042    if (ucodf) *ucodf=codf;
7043    PR = SUMA_New_Pick_Result(PR);
7044    if (!sv || !colid) {
7045       SUMA_S_Err("NULL input");
7046       SUMA_RETURN(PR);
7047    }
7048    if (!sv->pick_colid_list || !dlist_size(sv->pick_colid_list)) {
7049       SUMA_S_Err("NULL or Empty pick_colid_list");
7050       SUMA_RETURN(PR);
7051    }
7052    if (LocalHead) SUMA_Show_Pick_Colid_List(sv->pick_colid_list, SUMA_STDERR);
7053    SUMA_COLID_RGBA2N(colid[0], colid[1], colid[2], colid[3], n4);
7054    do {
7055       if (!el) el = dlist_head(sv->pick_colid_list);
7056       else el = dlist_next(el);
7057       cod = (SUMA_COLID_OFFSET_DATUM *)el->data;
7058       if (n4>= cod->i0 && n4<= cod->i1) { /* we're in this object */
7059          codf = cod;
7060       }
7061    } while (!codf && el != dlist_tail(sv->pick_colid_list));
7062 
7063    if (!codf) SUMA_RETURN(PR);
7064    if (ucodf) *ucodf=codf;
7065 
7066    /* Compute get more info about intersection  */
7067    if (codf) {
7068       NI_element *nelitp=NULL;
7069       float *fv=NULL;
7070       double xyzw[9], scl[9], U[3], P[3], C[3], *dv=NULL;
7071       int i0, i1, ir, datum_index;
7072       SUMA_ALL_DO *ado=NULL;
7073       SUMA_DSET *dset=NULL;
7074       GLint viewport[4];
7075       SUMA_GRAPH_SAUX *GSaux = NULL;
7076 
7077       glGetIntegerv(GL_VIEWPORT, viewport);
7078       PR->ado_idcode_str = SUMA_replace_string(PR->ado_idcode_str,
7079                                                codf->ref_idcode_str);
7080       PR->primitive = SUMA_replace_string(PR->primitive, codf->primitive);
7081       PR->primitive_index = (n4-codf->i0);
7082       for (i0=0; i0<SUMA_N_IALTSEL_TYPES; ++i0) PR->iAltSel[i0] = -1;
7083       for (i0=0; i0<SUMA_N_DALTSEL_TYPES; ++i0) PR->dAltSel[i0] = 0.0;
7084       PR->datum_index = -1;
7085       ado = SUMA_whichADOg(PR->ado_idcode_str);
7086       switch (codf->ref_do_type) {
7087          case GRAPH_LINK_type: {
7088             dset = SUMA_find_GLDO_Dset((SUMA_GraphLinkDO*)ado);
7089             DDO.err = 1; SUMA_Load_Dumb_DO(ado, &DDO);
7090             if (DDO.err) {
7091                if (DDO.err==1) {
7092                   SUMA_SL_Err("Object's parent graph set not found.");
7093                } else if (DDO.err==2) {
7094                   SUMA_SL_Err("Could not fill DDO");
7095                } else {
7096                   SUMA_SL_Err("Weird error.");
7097                }
7098                SUMA_RETURN (PR);
7099             }
7100             if (!DDO.NodeList) {
7101                SUMA_S_Err("SDO is node based but could not get a NodeList");
7102                SUMA_RETURN (PR);
7103             }
7104             if (!(GSaux = SDSET_GSAUX(dset))) {
7105                SUMA_S_Err("No GSaux");
7106                SUMA_RETURN(PR);
7107             }
7108             GSaux->IgnoreSelection = 0; /*reset ignore selection flag */
7109             if (SUMA_is_ADO_Datum_Primitive(ado, codf)) {
7110                datum_index = SUMA_GDSET_EdgeRow_To_Index(dset,
7111                                                          PR->primitive_index);
7112                if (!(SUMA_GDSET_SegIndexToPoints(dset, datum_index,
7113                                                  &i0, &i1, NULL))) {
7114                   SUMA_RETURN(PR);
7115                }
7116                nelitp = NULL;
7117                if (GSaux->ShowBundles &&
7118                    (nelitp = SUMA_GDSET_Edge_Bundle(dset, GSaux,
7119                                                     datum_index, -1))) {
7120                #if 1 /* more unified version, older, valid one below */
7121                   int nn=0, mm=0, nnmin=-1, mmmin=-1, mmmin3=-1;
7122                   float scpx[3], *A, *B;
7123                   float mindist, dist, distatmin, seglen;
7124                   float fmin;
7125                   TAYLOR_TRACT *ttn=NULL;
7126 
7127                   SUMA_LHv("Clicked on a bundle representation of edge %d.\n",
7128                            datum_index);
7129                   /* screen pick coordinate in screen coords */
7130                   scpx[0] = sv->PickPix[0];
7131                   scpx[1] = viewport[3]-sv->PickPix[1]-1;
7132                   scpx[2] = 0.0;
7133                   /* find the closest point to where we clicked on the bundle */
7134                   if (!SUMA_Bundle_Pick_Intersect(nelitp, 'N', -1, sv, scpx, 0,
7135                                                   &nnmin, &mmmin,
7136                                                   &fmin, &mindist)) {
7137                      SUMA_LH("No bunlde intersection");
7138                   }
7139                   mmmin3 = 3*mmmin;
7140                   if (nnmin > -1) {
7141                      ttn = (TAYLOR_TRACT *)(nelitp->vec[0])+nnmin;
7142                      if (fmin != 0.0f) {
7143                         PR->PickXYZ[0]=ttn->pts[mmmin3+0]+
7144                                     fmin*(ttn->pts[mmmin3+3]-ttn->pts[mmmin3]);
7145                         PR->PickXYZ[1]=ttn->pts[mmmin3+1]+
7146                                     fmin*(ttn->pts[mmmin3+4]-ttn->pts[mmmin3+1]);
7147                         PR->PickXYZ[2]=ttn->pts[mmmin3+2]+
7148                                     fmin*(ttn->pts[mmmin3+5]-ttn->pts[mmmin3+2]);
7149                         /* where along the tract? */
7150                         mm=0; dist=0.0; distatmin=19999.9;
7151                         while (mm < ttn->N_pts3-3) {
7152                            A = ttn->pts+mm;
7153                            B = ttn->pts+mm+3;
7154                            seglen =  sqrtf((B[0]-A[0])*(B[0]-A[0])+
7155                                            (B[1]-A[1])*(B[1]-A[1])+
7156                                            (B[2]-A[2])*(B[2]-A[2]));
7157                            if (mm==mmmin3) {
7158                               distatmin = dist + fmin*seglen;
7159                            }
7160                            dist +=seglen;
7161                            mm += 3;
7162                         }
7163                         /* Which is the closest node? */
7164                         f = distatmin/dist;
7165                         if (f <= 0.5) {
7166                            PR->iAltSel[SUMA_ENODE_0] = i0;
7167                            PR->iAltSel[SUMA_ENODE_0] = i1;
7168                            if (SUMA_ABS(f)> 0.01) {/* not too close to edge */
7169                               PR->datum_index = datum_index;
7170                            } else PR->datum_index = -1;
7171                         } else {
7172                            SUMA_LHv("++half way, look for opp. edge [%d %d]\n",
7173                                     i1, i0);
7174                            PR->iAltSel[SUMA_ENODE_0] = i1;
7175                            PR->iAltSel[SUMA_ENODE_1] = i0;
7176 
7177                            if (SUMA_ABS(1.0-f) > 0.01){/*not too close to edge*/
7178                               /* does edge [i1, i0] exist? If so take it*/
7179                               if (SUMA_GDSET_PointsToSegIndex(dset,i1,i0,&ir)) {
7180                                  SUMA_LHv("Switching primitve to edge %d\n", ir);
7181                                  PR->datum_index = ir;
7182                               } else { /* leave old hit, no opposite edge */
7183                                  PR->datum_index = datum_index;
7184                               }
7185                            } else PR->datum_index = -1;
7186                         }
7187                      } else { /* from the quick search, no fmin used*/
7188                         PR->PickXYZ[0]=ttn->pts[mmmin3+0];
7189                         PR->PickXYZ[1]=ttn->pts[mmmin3+1];
7190                         PR->PickXYZ[2]=ttn->pts[mmmin3+2];
7191                      }
7192                   } else {
7193                      PR->PickXYZ[0] = PR->PickXYZ[1] = PR->PickXYZ[2]=0.0;
7194                   }
7195                   SUMA_LHv("Closest distance of %f, tract %d, point %d, f=%f\n"
7196                            "at world [%f %f %f]\n",
7197                            mindist, nnmin, mmmin, fmin,
7198                            PR->PickXYZ[0], PR->PickXYZ[1], PR->PickXYZ[2]);
7199                #else
7200                   int nn=0, mm=0, nnmin=-1, mmmin=-1;
7201                   float *scrxyz = NULL, scpx[3], *A, *B;
7202                   float mindist, mindist2, dist, distatmin, seglen;
7203                   float dx, dy, dxy2=0.0, fmin;
7204                   TAYLOR_TRACT *ttn=NULL;
7205 
7206                   SUMA_LHv("Clicked on a bundle representation of edge %d.\n",
7207                            datum_index);
7208                   /* screen pick coordinate in screen coords */
7209                   scpx[0] = sv->PickPix[0];
7210                   scpx[1] = viewport[3]-sv->PickPix[1]-1;
7211                   scpx[2] = 0.0;
7212                   /* find the closest point to where we clicked on the bundle */
7213                   nnmin=-1; mmmin=-1; mindist=mindist2=1000; fmin=1.0;
7214                   for (nn=0; nn<nelitp->vec_len; ++nn) {
7215                      ttn = (TAYLOR_TRACT *)(nelitp->vec[0])+nn;
7216                      scrxyz = (float *)SUMA_calloc(ttn->N_pts3, sizeof(float));
7217                      memcpy(scrxyz, ttn->pts, (ttn->N_pts3)*sizeof(float));
7218                      /* tranform bundle points to screen space*/
7219                      if (!SUMA_World2ScreenCoordsF(sv, ttn->N_pts3/3,
7220                                                   ttn->pts, scrxyz, NULL,
7221                                                   YUP, YUP)) {
7222                         SUMA_S_Err("Failed to get screen coords");
7223                         SUMA_RETURN(PR);
7224                      }
7225                      /* Now search for the closest point of bundle to the click
7226                         The depth is ignored, in the hope that a simple closest
7227                         search is enough. Otherwise we need to add a heuristic
7228                         for the cost of depth */
7229                      #ifdef CRUDE_SEARCH
7230                         /* For finer tracing, you should project scpx onto the
7231                         segment between the 1st and 2 points forming a segment,
7232                         much like what is done in SUMA_PROJECT_C_ONTO_AB below
7233                         The finer tracing is needed for crass paths. However
7234                         for real bundles, on high-res data this might be
7235                         overkill */
7236                      mm=0;
7237                      while (mm < ttn->N_pts3) {
7238                         if ( ((dx = SUMA_ABS(scrxyz[mm  ]-scpx[0])) < mindist) &&
7239                              ((dy = SUMA_ABS(scrxyz[mm+1]-scpx[1])) < mindist) ){                            if ((dxy2 = dx*dx+dy*dy) < mindist2) {
7240                               mindist2 = dxy2;
7241                               mindist  = sqrtf(mindist2);
7242                               nnmin=nn; mmmin=mm;  fmin = 0.0;
7243                            }
7244                         }
7245                         mm += 3;
7246                      }
7247                      #else
7248                      mm=0;
7249                      while (mm < ttn->N_pts3-3) {
7250                         A = scrxyz+mm;
7251                         B = scrxyz+mm+3;
7252                         SUMA_PROJECT_C_ONTO_AB(scpx, A, B, P, f);
7253                         if ( ((dx = SUMA_ABS(P[0]-scpx[0])) < mindist) &&
7254                              ((dy = SUMA_ABS(P[1]-scpx[1])) < mindist) ){                                    if ((dxy2 = dx*dx+dy*dy) < mindist2) {
7255                               mindist2 = dxy2;
7256                               mindist  = sqrtf(mindist2);
7257                               nnmin=nn; mmmin=mm; fmin=f;
7258                            }
7259                         }
7260                         mm += 3;
7261                      }
7262                      #endif
7263                      SUMA_ifree(scrxyz);
7264                   }
7265                   if (nnmin > -1) {
7266                      ttn = (TAYLOR_TRACT *)(nelitp->vec[0])+nnmin;
7267                      if (fmin != 0.0f) {
7268                         PR->PickXYZ[0]=ttn->pts[mmmin+0]+
7269                                     fmin*(ttn->pts[mmmin+3]-ttn->pts[mmmin]);
7270                         PR->PickXYZ[1]=ttn->pts[mmmin+1]+
7271                                     fmin*(ttn->pts[mmmin+4]-ttn->pts[mmmin+1]);
7272                         PR->PickXYZ[2]=ttn->pts[mmmin+2]+
7273                                     fmin*(ttn->pts[mmmin+5]-ttn->pts[mmmin+2]);
7274                         /* where along the tract? */
7275                         mm=0; dist=0.0; distatmin=19999.9;
7276                         while (mm < ttn->N_pts3-3) {
7277                            A = ttn->pts+mm;
7278                            B = ttn->pts+mm+3;
7279                            seglen =  sqrtf((B[0]-A[0])*(B[0]-A[0])+
7280                                            (B[1]-A[1])*(B[1]-A[1])+
7281                                            (B[2]-A[2])*(B[2]-A[2]));
7282                            if (mm==mmmin) {
7283                               distatmin = dist + fmin*seglen;
7284                            }
7285                            dist +=seglen;
7286                            mm += 3;
7287                         }
7288                         /* Which is the closest node? */
7289                         f = distatmin/dist;
7290                         if (f <= 0.5) {
7291                            PR->iAltSel[SUMA_ENODE_0] = i0;
7292                            PR->iAltSel[SUMA_ENODE_1] = i1;
7293 
7294                            if (SUMA_ABS(f)> 0.01) {/* not too close to edge */
7295                               PR->datum_index = datum_index;
7296                            } else PR->datum_index = -1;
7297                         } else {
7298                            SUMA_LHv("++half way, look for opp. edge [%d %d]\n",
7299                                     i1, i0);
7300                            PR->iAltSel[SUMA_ENODE_0] = i1;
7301                            PR->iAltSel[SUMA_ENODE_1] = i0;
7302                            if (SUMA_ABS(1.0-f) > 0.01){/*not too close to edge*/
7303                               /* does edge [i1, i0] exist? If so take it*/
7304                               if (SUMA_GDSET_PointsToSegIndex(dset,i1,i0,&ir)) {
7305                                  SUMA_LHv("Switching primitve to edge %d\n", ir);
7306                                  PR->datum_index = ir;
7307                               } else { /* leave old hit, no opposite edge */
7308                                  PR->datum_index = datum_index;
7309                               }
7310                            } else PR->datum_index = -1;
7311                         }
7312                      } else { /* from the quick search, no fmin used*/
7313                         PR->PickXYZ[0]=ttn->pts[mmmin+0];
7314                         PR->PickXYZ[1]=ttn->pts[mmmin+1];
7315                         PR->PickXYZ[2]=ttn->pts[mmmin+2];
7316                      }
7317                   } else {
7318                      PR->PickXYZ[0] = PR->PickXYZ[1] = PR->PickXYZ[2]=0.0;
7319                   }
7320                   SUMA_LHv("Closest distance of %f, tract %d, point %d, f=%f\n"
7321                            "at world [%f %f %f]\n",
7322                            mindist, nnmin, mmmin, fmin,
7323                            PR->PickXYZ[0], PR->PickXYZ[1], PR->PickXYZ[2]);
7324 
7325                #endif
7326                } else {
7327                   SUMA_LHv("Segment %d is formed by points %d and %d\n",
7328                            datum_index, i0, i1);
7329                   fv = SUMA_GDSET_NodeXYZ(dset, i0, SUMA_ADO_variant(ado), NULL);
7330                      xyzw[0] = fv[0]; xyzw[1] = fv[1]; xyzw[2] = fv[2];
7331                   fv = SUMA_GDSET_NodeXYZ(dset, i1, SUMA_ADO_variant(ado), NULL);
7332                      xyzw[3] = fv[0]; xyzw[4] = fv[1]; xyzw[5] = fv[2];
7333                   xyzw[6] = (sv->Pick0[0]+sv->Pick1[0])/2.0;
7334                   xyzw[7] = (sv->Pick0[1]+sv->Pick1[1])/2.0;
7335                   xyzw[8] = (sv->Pick0[2]+sv->Pick1[2])/2.0;
7336 
7337                   if (!SUMA_World2ScreenCoords(sv, 3,xyzw,scl, NULL, YUP, YUP)) {
7338                      SUMA_S_Err("Failed to get screen coords");
7339                      SUMA_RETURN(PR);
7340                   }
7341                   /* Project click point onto line by two nodes */
7342                   C[0] = sv->PickPix[0];
7343                   C[1] = viewport[3]-sv->PickPix[1]-1;
7344                   C[2] = 0;
7345                   dv = scl+3; /* point B */
7346                   SUMA_PROJECT_C_ONTO_AB(C, scl, dv, P, f);
7347                   /* Project point click onto segment */
7348                   SUMA_LHv(
7349                      "User click locations: %d %d Norm(%f %f)\n"
7350                         "sv PickPix: %d %d (screen/mouse y:%d)\n"
7351                         "world: near[%f %f %f] far[%f %f %f]\n"
7352                         "Edge %d formed by nodes %d [%f %f %f], %d [%f %f %f]\n"
7353                         "Nodes projected to screen: [%f %f %f], [%f %f %f]\n"
7354                   "Projection of click point screen edge: [%f %f %f], f = %f\n"
7355                         ,ipick, jpick,
7356                            ipick/(double)viewport[2], jpick/(double)viewport[3],
7357                         sv->PickPix[0], sv->PickPix[1], viewport[3]-jpick-1,
7358                            sv->Pick0[0], sv->Pick0[1], sv->Pick0[2],
7359                            sv->Pick1[0], sv->Pick1[1], sv->Pick1[2],
7360                         datum_index, i0, xyzw[0], xyzw[1], xyzw[2],
7361                                      i1, xyzw[3], xyzw[4], xyzw[5],
7362                            scl[0], scl[1], scl[2], scl[3], scl[4], scl[5],
7363                            P[0], P[1], P[2], f);
7364 
7365                   /* Record the intersection location */
7366                   PR->PickXYZ[0]=xyzw[0]+f*(xyzw[3]-xyzw[0]);
7367                   PR->PickXYZ[1]=xyzw[1]+f*(xyzw[4]-xyzw[1]);
7368                   PR->PickXYZ[2]=xyzw[2]+f*(xyzw[5]-xyzw[2]);
7369 
7370                   /* Which is the closest node? */
7371                   if (f <= 0.5) {
7372                      PR->iAltSel[SUMA_ENODE_0] = i0;
7373                      PR->iAltSel[SUMA_ENODE_1] = i1;
7374                      if (SUMA_ABS(f)> 0.01) {/* not too close to edge */
7375                         PR->datum_index = datum_index;
7376                      } else PR->datum_index = -1;
7377                   } else {
7378                      SUMA_LHv("++half way, looking for opp. edge [%d %d]\n",
7379                               i1, i0);
7380                      PR->iAltSel[SUMA_ENODE_0] = i1;
7381                      PR->iAltSel[SUMA_ENODE_1] = i0;
7382                      if (SUMA_ABS(1.0-f) > 0.01) { /* not too close to edge */
7383                         /* does edge [i1, i0] exist? If so take it*/
7384                         if (SUMA_GDSET_PointsToSegIndex(dset, i1, i0, &ir)) {
7385                            SUMA_LHv("Switching primitve to edge %d\n", ir);
7386                            PR->datum_index = ir;
7387                         } else { /* leave old hit, no opposite edge */
7388                            PR->datum_index = datum_index;
7389                         }
7390                      } else PR->datum_index = -1;
7391                   }
7392                }
7393             } else { /* picked a node, no data on it*/
7394                PR->iAltSel[SUMA_ENODE_0] =
7395                   SUMA_GDSET_Index_To_NodeIndex(dset, PR->primitive_index);
7396                PR->iAltSel[SUMA_ENODE_1] = -1;
7397                PR->datum_index = -1;
7398                fv = SUMA_GDSET_NodeXYZ(dset, PR->iAltSel[SUMA_ENODE_0],
7399                                        SUMA_ADO_variant(ado), NULL);
7400                PR->PickXYZ[0]=fv[0]; PR->PickXYZ[1]=fv[1]; PR->PickXYZ[2]=fv[2];
7401             }
7402             break; }
7403          case GDSET_type:
7404             SUMA_S_Err("I don't expect graph dsets to be picked directly");
7405             break;
7406          case CDOM_type:
7407             SUMA_S_Err("CIFTI not picked on buffer");
7408             break;
7409          case VO_type:
7410             SUMA_S_Err("VOs not picked on buffer....");
7411             break;
7412          case MASK_type:
7413             SUMA_S_Err("Masks not picked on buffer....");
7414             break;
7415          case TRACT_type:
7416             {
7417             SUMA_TractDO *tdo=(SUMA_TractDO *)ado;
7418             int nn=0, mm=0, nnmin=-1, mmmin=-1, mmmin3=-1, oki=0, it=0, ib=0;
7419             float *scrxyz = NULL;
7420             float mindist, dist;
7421             float fmin;
7422             TAYLOR_TRACT *ttn=NULL;
7423             TAYLOR_BUNDLE *tb=NULL;
7424 
7425             oki = 0;
7426             if (!strcmp(PR->primitive,"bundles")) {
7427                ib = PR->primitive_index;
7428                SUMA_LHv("Seeking bundle %d/%d\n",
7429                            (int)PR->primitive_index, tdo->net->N_tbv);
7430                tb = TDO_BUNDLE(tdo, PR->primitive_index);
7431                if (!(oki=SUMA_Bundle_Pick_Intersect(tb, 'B', -1, sv, NULL, 0,
7432                                             &nnmin, &mmmin, &fmin, &mindist))) {
7433                   SUMA_LH("No bunlde intersection");
7434                }
7435             } else if (!strcmp(PR->primitive,"tracts")) {
7436                SUMA_LHv("Seeking tract %d/%d\n",
7437                            (int)PR->primitive_index, TDO_N_TRACTS(tdo));
7438                if (Network_1T_to_TB(tdo->net,
7439                            (int)PR->primitive_index, &it, &ib, NULL, NULL) < 0) {
7440                   SUMA_S_Err("Failed to resolve tract index");
7441                } else {
7442                   tb = TDO_BUNDLE(tdo, ib);
7443                   if (!(oki=SUMA_Bundle_Pick_Intersect(tb, 'B', it, sv, NULL, 0,
7444                                             &nnmin, &mmmin, &fmin, &mindist))) {
7445                      SUMA_LH("No tract intersection");
7446                   }
7447                }
7448             }
7449             if (oki) {
7450                if (nnmin > -1) {
7451                   mmmin3=3*mmmin;
7452                   ttn = tb->tracts+nnmin;
7453                   if (fmin != 0.0f) {
7454                      PR->PickXYZ[0]=ttn->pts[mmmin3+0]+
7455                                  fmin*(ttn->pts[mmmin3+3]-ttn->pts[mmmin3]);
7456                      PR->PickXYZ[1]=ttn->pts[mmmin3+1]+
7457                                  fmin*(ttn->pts[mmmin3+4]-ttn->pts[mmmin3+1]);
7458                      PR->PickXYZ[2]=ttn->pts[mmmin3+2]+
7459                                  fmin*(ttn->pts[mmmin3+5]-ttn->pts[mmmin3+2]);
7460                   } else {
7461                      /* from the quick search, no fmin used*/
7462                      PR->PickXYZ[0]=ttn->pts[mmmin3+0];
7463                      PR->PickXYZ[1]=ttn->pts[mmmin3+1];
7464                      PR->PickXYZ[2]=ttn->pts[mmmin3+2];
7465                   }
7466                   PR->iAltSel[SUMA_TRC_PNT] = mmmin;
7467                   PR->iAltSel[SUMA_BUN_TRC] = nnmin;
7468                   PR->iAltSel[SUMA_NET_BUN] = ib;
7469                   PR->datum_index = Network_PTB_to_1P(tdo->net,
7470                                                       PR->iAltSel[SUMA_TRC_PNT],
7471                                                       PR->iAltSel[SUMA_BUN_TRC],
7472                                                       PR->iAltSel[SUMA_NET_BUN]);
7473                   PR->iAltSel[SUMA_NET_TRC] = Network_TB_to_1T(tdo->net,
7474                                                       PR->iAltSel[SUMA_BUN_TRC],
7475                                                       PR->iAltSel[SUMA_NET_BUN]);
7476                   SUMA_LHv("Bundle %ld intersected, tract %ld, "
7477                            "point %ld, tract in net %ld. \n"
7478                            "Point NetID: %ld\n"
7479                            "XYZ[%.3f %.3f %.3f], fmin=%f\n",
7480                            PR->iAltSel[SUMA_NET_BUN],
7481                            PR->iAltSel[SUMA_BUN_TRC],
7482                            PR->iAltSel[SUMA_TRC_PNT],
7483                            PR->iAltSel[SUMA_NET_TRC], PR->datum_index,
7484                            PR->PickXYZ[0], PR->PickXYZ[1], PR->PickXYZ[2], fmin);
7485 
7486                   #if 0 /* Just to debug reverse lookup */
7487                   if (Network_1P_to_PTB(tdo->net, PR->datum_index,
7488                                          &mmmin, &nnmin, &nn, NULL) < 0) {
7489                      SUMA_S_Err("Failed in reverse lookup test");
7490                   } else {
7491                      SUMA_LHv("Inverse lookup: %ld --> P %d, T %d, B %d\n",
7492                               PR->datum_index, mmmin, nnmin, nn);
7493                   }
7494                   #endif
7495                }
7496             }
7497             f = fmin;
7498             break; }
7499          default:
7500             SUMA_S_Warnv("Not ready to get location for %s\n",
7501                       SUMA_ObjectTypeCode2ObjectTypeName(codf->ref_do_type));
7502             break;
7503       }
7504    }
7505 
7506    /* report */
7507    if (codf) {
7508          SUMA_S_Notev("\nvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n"
7509                    "DO pick: colid (%ld=[%d %d %d %d]) \n"
7510                    "      is in    <%s, %s, %s>, f=%1.3f\n"
7511                    "   datum = %ld, iAltSel = [",
7512                    n4, colid[0], colid[1], colid[2], colid[3],
7513                    cod->Label, cod->variant, cod->primitive, f,
7514                    PR->datum_index);
7515         for (iii=0; iii<SUMA_N_IALTSEL_TYPES; ++iii)
7516             fprintf(SUMA_STDOUT, "%ld, ", PR->iAltSel[iii]);
7517         fprintf(SUMA_STDOUT, "]\n   dAltSel = [");
7518         for (iii=0; iii<SUMA_N_DALTSEL_TYPES; ++iii)
7519             fprintf(SUMA_STDOUT, "%.3f, ", PR->dAltSel[iii]);
7520         fprintf(SUMA_STDOUT, "]\n"
7521                       "\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
7522    }
7523 
7524    SUMA_RETURN(PR);
7525 }
7526 
SUMA_New_Pick_Result(SUMA_PICK_RESULT * PR)7527 SUMA_PICK_RESULT *SUMA_New_Pick_Result(SUMA_PICK_RESULT *PR)
7528 {
7529    static char FuncName[]={"SUMA_New_Pick_Result"};
7530    int i;
7531    if (!PR) {
7532       PR = (SUMA_PICK_RESULT *)SUMA_calloc(1,sizeof(SUMA_PICK_RESULT));
7533    }
7534    PR->primitive_index = -1;
7535    PR->datum_index = -1;
7536    for (i=0; i<SUMA_N_IALTSEL_TYPES; ++i) PR->iAltSel[i] = -1;
7537    for (i=0; i<SUMA_N_DALTSEL_TYPES; ++i) PR->dAltSel[i] = 0.0;
7538    SUMA_ifree(PR->primitive);
7539    SUMA_ifree(PR->ado_idcode_str);
7540    /* Imprint with event structure */
7541    PR->evr = (SUMA_EVENT *)malloc(sizeof(SUMA_EVENT));
7542    if (SUMAg_CF->lev) memcpy(PR->evr, SUMAg_CF->lev, sizeof(SUMA_EVENT));
7543    else memset(PR->evr, 0, sizeof(SUMA_EVENT));
7544    /* SUMA_ShowEvent(PR->evr, 0, "From New Pick Result\n"); */
7545    return(PR);
7546 }
7547 
SUMA_free_PickResult(SUMA_PICK_RESULT * PR)7548 SUMA_PICK_RESULT *SUMA_free_PickResult(SUMA_PICK_RESULT *PR)
7549 {
7550    static char FuncName[]={"SUMA_free_PickResult"};
7551    SUMA_ENTRY;
7552    if (!PR) SUMA_RETURN(PR);
7553    SUMA_ifree(PR->primitive);
7554    SUMA_ifree(PR->ado_idcode_str);
7555    SUMA_ifree(PR->dset_idcode_str);
7556    SUMA_ifree(PR->evr);
7557    SUMA_free(PR);
7558    SUMA_RETURN(NULL);
7559 }
7560 
SUMA_ADO_StorePickResult(SUMA_ALL_DO * ado,SUMA_PICK_RESULT ** PRP)7561 SUMA_Boolean SUMA_ADO_StorePickResult(SUMA_ALL_DO *ado, SUMA_PICK_RESULT **PRP)
7562 {
7563    static char FuncName[]={"SUMA_ADO_StorePickResult"};
7564 
7565    SUMA_ENTRY;
7566 
7567    if (!PRP || !*PRP) SUMA_RETURN(NOPE);
7568 
7569    switch (ado->do_type) {
7570       case SO_type: {
7571          SUMA_SURF_SAUX *Saux = SUMA_ADO_SSaux(ado);
7572          SUMA_free_PickResult(Saux->PR);
7573          Saux->PR = *PRP; *PRP = NULL;
7574          SUMA_RETURN(YUP);
7575          break; }
7576       case CDOM_type: {
7577          SUMA_CIFTI_SAUX *Saux = SUMA_ADO_CSaux(ado);
7578          SUMA_free_PickResult(Saux->PR);
7579          Saux->PR = *PRP; *PRP = NULL;
7580          SUMA_RETURN(YUP);
7581          break; }
7582       case GDSET_type: {
7583          SUMA_DSET *dset=(SUMA_DSET *)ado;
7584          SUMA_GRAPH_SAUX *Saux = SDSET_GSAUX(dset);
7585          /* Is the selection type changed? If so, then
7586          you need to flush the pick buffer */
7587 
7588          if (Saux->PR) {
7589             if ( (Saux->PR->datum_index * (*PRP)->datum_index < 0) ||
7590                  ((*PRP)->datum_index == -1 &&
7591                   (Saux->PR->iAltSel[SUMA_ENODE_0] !=
7592                               (*PRP)->iAltSel[SUMA_ENODE_0])) )  {
7593                /* Going from edge selection to node selection, in general
7594                   But there is no need to get picky */
7595               SUMA_FlushPickBufferForDO((SUMA_ALL_DO *)SUMA_find_Dset_GLDO(dset,
7596                                                  "G3D",NULL));
7597             }
7598          }
7599          SUMA_free_PickResult(Saux->PR);
7600          Saux->PR = *PRP; *PRP = NULL;
7601          SUMA_RETURN(YUP);
7602          break; }
7603       case GRAPH_LINK_type:
7604          SUMA_RETURN(SUMA_ADO_StorePickResult(
7605             (SUMA_ALL_DO*)SUMA_find_GLDO_Dset(
7606                            (SUMA_GraphLinkDO*)ado),PRP));
7607          break;
7608       case TRACT_type: {
7609          SUMA_TRACT_SAUX *Saux = SUMA_ADO_TSaux(ado);
7610          SUMA_free_PickResult(Saux->PR);
7611          Saux->PR = *PRP; *PRP = NULL;
7612          SUMA_RETURN(YUP);
7613          break; }
7614       case MASK_type: {
7615          SUMA_MASK_SAUX *Saux = SUMA_ADO_MSaux(ado);
7616          if (Saux) {
7617             SUMA_free_PickResult(Saux->PR);
7618             Saux->PR = *PRP; *PRP = NULL;
7619          } else {
7620             SUMA_S_Err("NULL Saux!!!, don't let that happen");
7621             SUMA_RETURN(NOPE);
7622          }
7623          SUMA_RETURN(YUP);
7624          break; }
7625       case VO_type: {
7626          SUMA_VOL_SAUX *Saux = SUMA_ADO_VSaux(ado);
7627          if (!(*PRP)->primitive) {
7628             SUMA_S_Err("NULL primitve not acceptable for VOs");
7629             break;
7630          }
7631          if (!strcmp((*PRP)->primitive,"voxel")) {
7632             SUMA_free_PickResult(Saux->PR);
7633             Saux->PR = *PRP; *PRP = NULL;
7634             SUMA_RETURN(YUP);
7635          } else if (!strcmp((*PRP)->primitive,"cutplane")) {
7636             SUMA_free_PickResult(Saux->PRc);
7637             Saux->PRc = *PRP; *PRP = NULL;
7638             SUMA_RETURN(YUP);
7639          } else {
7640             SUMA_S_Err("Bad primitive %s for VO", (*PRP)->primitive);
7641             SUMA_DUMP_TRACE("Who dunit?");
7642          }
7643          break; }
7644       default:
7645          SUMA_S_Errv("Note ready for type %s\n", ADO_TNAME(ado));
7646          break;
7647    }
7648    SUMA_RETURN(NOPE);
7649 }
7650 
SUMA_ADO_GetPickResult(SUMA_ALL_DO * ado,char * primitive)7651 SUMA_PICK_RESULT * SUMA_ADO_GetPickResult(SUMA_ALL_DO *ado, char *primitive)
7652 {
7653    static char FuncName[]={"SUMA_ADO_GetPickResult"};
7654    SUMA_PICK_RESULT *PR=NULL;
7655 
7656    SUMA_ENTRY;
7657 
7658    if (!ado) SUMA_RETURN(NULL);
7659    if (!primitive) primitive = "none";
7660 
7661    switch (ado->do_type) {
7662       case SO_type:{
7663          SUMA_SURF_SAUX *Saux = SUMA_ADO_SSaux(ado);
7664          SUMA_RETURN(Saux->PR);
7665          break; }
7666       case CDOM_type: {
7667          SUMA_CIFTI_SAUX *Saux = SUMA_ADO_CSaux(ado);
7668          SUMA_RETURN(Saux->PR);
7669          break; }
7670       case GDSET_type: {
7671          SUMA_DSET *dset=(SUMA_DSET *)ado;
7672          SUMA_GRAPH_SAUX *Saux = SDSET_GSAUX(dset);
7673          SUMA_RETURN(Saux->PR);
7674          break; }
7675       case GRAPH_LINK_type:
7676          SUMA_RETURN(SUMA_ADO_GetPickResult(
7677            (SUMA_ALL_DO*)SUMA_find_GLDO_Dset((SUMA_GraphLinkDO*)ado),primitive));
7678          break;
7679       case TRACT_type: {
7680          SUMA_TRACT_SAUX *Saux = SUMA_ADO_TSaux(ado);
7681          SUMA_RETURN(Saux->PR);
7682          break; }
7683       case MASK_type: {
7684          SUMA_MASK_SAUX *Saux = SUMA_ADO_MSaux(ado);
7685          SUMA_RETURN(Saux->PR);
7686          break; }
7687       case VO_type: {
7688          SUMA_VOL_SAUX *Saux = SUMA_ADO_VSaux(ado);
7689          if (!strcmp(primitive,"voxel")) SUMA_RETURN(Saux->PR);
7690          else if (!strcmp(primitive,"cutplane")) SUMA_RETURN(Saux->PRc);
7691          else {
7692             SUMA_S_Err("Bad primitive %s for VO", primitive);
7693          }
7694          break; }
7695       default:
7696          SUMA_S_Errv("Note ready for type %s\n", ADO_TNAME(ado));
7697          break;
7698    }
7699    SUMA_RETURN(NOPE);
7700 }
7701 
7702 
7703 /*!
7704 */
SUMA_Show_Pick_Colid_List(DList * pick_colid_list,FILE * fout)7705 void SUMA_Show_Pick_Colid_List(DList *pick_colid_list, FILE *fout)
7706 {
7707    static char FuncName[]={"SUMA_Show_Pick_Colid_List"};
7708    char *s = NULL;
7709 
7710    SUMA_ENTRY;
7711    if (!fout) fout = SUMA_STDOUT;
7712 
7713    s = SUMA_Pick_Colid_List_Info(pick_colid_list);
7714    fprintf(fout, "%s", s);
7715    SUMA_ifree(s);
7716 
7717    SUMA_RETURNe;
7718 }
7719 
SUMA_Pick_Colid_List_Info(DList * pick_colid_list)7720 char *SUMA_Pick_Colid_List_Info (DList *pick_colid_list)
7721 {
7722    static char FuncName[]={"SUMA_Pick_Colid_List_Info"};
7723    char *s = NULL;
7724    SUMA_STRING *SS = NULL;
7725    SUMA_SurfaceObject *SO=NULL;
7726    SUMA_DO_Types do_type;
7727    void *vv=NULL;
7728    SUMA_DSET *dset=NULL;
7729    DListElmt *el=NULL;
7730    SUMA_ALL_DO *ado=NULL;
7731    SUMA_COLID_OFFSET_DATUM *cod=NULL;
7732 
7733    SUMA_ENTRY;
7734 
7735    SS = SUMA_StringAppend(NULL, NULL);
7736 
7737    if (!pick_colid_list) {
7738       SS = SUMA_StringAppend(SS,"NULL pick_colid_list"); goto CLEAN_RETURN;
7739    }
7740    if (!dlist_size(pick_colid_list)) {
7741       SS = SUMA_StringAppend(SS,"Empty pick_colid_list"); goto CLEAN_RETURN;
7742    }
7743    SS = SUMA_StringAppend_va(SS,"DO Pick List of %d elements\n",
7744                               dlist_size(pick_colid_list));
7745    do {
7746       if (!el) el = dlist_head(pick_colid_list);
7747       else el = dlist_next(el);
7748       if (!el || !el->data) {
7749          SS = SUMA_StringAppend(SS, "   NULL element!");
7750       } else {
7751          cod = (SUMA_COLID_OFFSET_DATUM *)el->data;
7752          SS = SUMA_StringAppend_va(SS,"   DO %s, Primitive %s, [%ld to %ld]\n",
7753                                           cod->Label, cod->primitive,
7754                                           cod->i0, cod->i1);
7755          vv = SUMA_Picked_reference_object(cod, &do_type);
7756          switch (do_type) {
7757             case MD_DSET_type:
7758                dset = (SUMA_DSET *)vv;
7759                SS = SUMA_StringAppend_va(SS,
7760                         "     Reference object is a %s dataset labeled %s "
7761                         "(reference type %s)\n",
7762                         "Multi Domain",
7763                         SDSET_LABEL(dset),
7764                         SUMA_ObjectTypeCode2ObjectTypeName(cod->ref_do_type));
7765                break;
7766             case ANY_DSET_type:
7767 	    case GDSET_type:
7768                dset = (SUMA_DSET *)vv;
7769                SS = SUMA_StringAppend_va(SS,
7770                         "     Reference object is a %s dataset labeled %s "
7771                         "(reference type %s)\n",
7772                         SUMA_isCIFTIDset(dset) ? "CIFTI" :
7773                               (SUMA_isGraphDset(dset) ? "Graph":"Surface-based"),
7774                         SDSET_LABEL(dset),
7775                         SUMA_ObjectTypeCode2ObjectTypeName(cod->ref_do_type));
7776                break;
7777             case SO_type:
7778                SO = (SUMA_SurfaceObject *)vv;
7779                SS = SUMA_StringAppend_va(SS,
7780                         "     Reference object is a surface labeled %s "
7781                         "(reference type %s)\n",
7782                         SO->Label,
7783                         SUMA_ObjectTypeCode2ObjectTypeName(cod->ref_do_type));
7784                break;
7785             case GRAPH_LINK_type:
7786                ado = (SUMA_ALL_DO *)vv;
7787                   SS = SUMA_StringAppend_va(SS,
7788                         "     Reference object is a graph link labeled %s "
7789                         "(reference type %s)\n",
7790                         SUMA_ADO_Label(ado),
7791                         SUMA_ObjectTypeCode2ObjectTypeName(cod->ref_do_type));
7792                break;
7793             case TRACT_type:
7794                SS = SUMA_StringAppend_va(SS,
7795                         "     Reference object is a tract object labeled %s "
7796                         "(reference type %s)\n",
7797                         SUMA_ADO_Label(ado),
7798                         SUMA_ObjectTypeCode2ObjectTypeName(cod->ref_do_type));
7799                break;
7800             case CDOM_type:
7801                SS = SUMA_StringAppend_va(SS,
7802                         "     Reference object is a CIFTI DO labeled %s "
7803                         "(reference type %s)\n",
7804                         SUMA_ADO_Label(ado),
7805                         SUMA_ObjectTypeCode2ObjectTypeName(cod->ref_do_type));
7806                break;
7807             case MASK_type:
7808                SS = SUMA_StringAppend_va(SS,
7809                         "     Reference object is a mask object labeled %s "
7810                         "(reference type %s)\n",
7811                         SUMA_ADO_Label(ado),
7812                         SUMA_ObjectTypeCode2ObjectTypeName(cod->ref_do_type));
7813                break;
7814             default:
7815                SS = SUMA_StringAppend_va(SS,
7816                      "     Parent, not surface or dset.\n");
7817                break;
7818          }
7819       }
7820    } while (el != dlist_tail(pick_colid_list));
7821 
7822    CLEAN_RETURN:
7823    SUMA_SS2S(SS,s);
7824 
7825    SUMA_RETURN(s);
7826 }
7827 
SUMA_MarkLineDOsIntersect(SUMA_SurfaceViewer * sv,SUMA_DO * dov,int IgnoreSameNode)7828 int SUMA_MarkLineDOsIntersect (SUMA_SurfaceViewer *sv, SUMA_DO *dov,
7829                                int IgnoreSameNode)
7830 {
7831    static char FuncName[]={"SUMA_MarkLineDOsIntersect"};
7832 
7833    SUMA_PICK_RESULT *PR = NULL;
7834    SUMA_ALL_DO *ado = NULL;
7835    int ans;
7836 
7837    SUMA_ENTRY;
7838    SUMA_S_Warn("Do not call me anymore."
7839                "Go via SUMA_ComputeLineDOsIntersect. "
7840                "This is left here for testing purposes");
7841    ans = SUMA_ComputeLineDOsIntersect(sv, dov, IgnoreSameNode, &ado);
7842    if (ans <= 0) {
7843       SUMA_RETURN(ans);
7844    }
7845    /* just for temporary testing, get PR back from list and apply it */
7846    PR = SUMA_Get_From_PickResult_List(sv, ado, NULL);
7847    ans = SUMA_Apply_PR(sv, &PR);
7848 
7849    SUMA_RETURN(ans);
7850 }
7851 
7852 /*!
7853    Determines the intersection between pickline and displayable objects
7854 */
SUMA_ComputeLineDOsIntersect(SUMA_SurfaceViewer * sv,SUMA_DO * dov,int IgnoreSameNode,SUMA_ALL_DO ** pado)7855 int SUMA_ComputeLineDOsIntersect (SUMA_SurfaceViewer *sv, SUMA_DO *dov,
7856                                   int IgnoreSameNode, SUMA_ALL_DO **pado)
7857 {
7858    static char FuncName[]={"SUMA_ComputeLineDOsIntersect"};
7859    int i, j, *MembDOs=NULL, N_MembDOs;
7860    SUMA_DO_Types ttv[12];
7861    SUMA_PICK_RESULT *hit=NULL;
7862    GLubyte colans[4];
7863    SUMA_COLID_OFFSET_DATUM *codf=NULL;
7864    SUMA_ALL_DO *ado=NULL;
7865    SUMA_EngineData *ED = NULL;
7866    DList *list = NULL;
7867    DListElmt *SetNodeElem = NULL, *Location=NULL;
7868    SUMA_SurfaceObject *SO = NULL;
7869    SUMA_Boolean NodeIgnored = NOPE;
7870    SUMA_Boolean LocalHead = NOPE;
7871 
7872    SUMA_ENTRY;
7873 
7874    if (!sv || !dov) SUMA_RETURN(-1);
7875 
7876    SUMA_LH("DO pickin");
7877 
7878    if (sv->PickPix[0] < 0 || sv->PickPix[1] < 0 ||
7879        sv->PickPix[0] >= sv->X->aWIDTH ||  sv->PickPix[1] >= sv->X->aHEIGHT) {
7880       /* This happens when you select and drag outside of the viewing area
7881       Don't return in error */
7882       SUMA_LHv("Bad PickPix=[%d %d] for viewport %d %d\n",
7883                  sv->PickPix[0], sv->PickPix[1], sv->X->aWIDTH, sv->X->aHEIGHT);
7884       SUMA_RETURN(0);
7885    }
7886 
7887    /* Any pickable DO, other than brain surfaces, that does not require
7888       pick buffer picking? */
7889    ttv[0] = GRAPH_LINK_type; ttv[1] = NOT_SET_type;
7890    MembDOs = SUMA_ViewState_Membs(&(sv->VSv[sv->iState]), ttv, &N_MembDOs);
7891    for (i=0; i<N_MembDOs; ++i) {
7892       if ((hit = SUMA_WhatWasPicked_FrameSO(sv, MembDOs[i]))) {
7893          if ( hit->datum_index < 0 && (
7894              ( hit->iAltSel[SUMA_ENODE_0]>=0 &&
7895                hit->iAltSel[SUMA_ENODE_1]>=0)) ) {
7896             /* You have selected a point pair that has no edge defined.
7897                This can happen when you click on an empty cell in the matrix */
7898             hit = SUMA_free_PickResult(hit);
7899          } else {
7900             /* got something, leave.
7901                Perhaps in the future will need to go through
7902             all stack (slices) and pick the best one ... */
7903             ado = iDO_ADO(MembDOs[i]);
7904             if (pado) *pado = ado;
7905             break;
7906          }
7907       }
7908    }
7909    SUMA_ifree(MembDOs);
7910 
7911    if (!hit) { /* nothing found above, go for pick buffer */
7912       if (!SUMA_PickBuffer(sv, 2, dov)) { /* refresh the buffer only if needed */
7913          SUMA_S_Err("Failed to refresh buffer");
7914          SUMA_RETURN(-1);
7915       }
7916 
7917       if (!sv->pickrenpix4) {
7918          SUMA_S_Err("Could not form pickrenpix4, should this be an error?");
7919          SUMA_RETURN(-1);
7920       }
7921       if (!sv->pick_colid_list || !dlist_size(sv->pick_colid_list)) {
7922          SUMA_LH("No such pickable objects");
7923       } else {
7924          /* get pixel at click */
7925          i = sv->PickPix[0]; j = sv->PickPix[1];
7926          if (SUMA_GetColidInPickBuffer4(sv->pickrenpix4,
7927                               sv->X->aWIDTH, sv->X->aHEIGHT,
7928                               &i, &j, 2, colans)) {
7929             if (LocalHead) {
7930                /* Just for debugging, draw the crosshair point */
7931                SUMA_MarkPickInBuffer4(sv, 1, NULL);
7932                SUMA_LHv("User pixel selection: \n"
7933                         "  closest hit to click at %d %d was from %d %d\n"
7934                         "  colid = %d %d %d %d \n",
7935                         sv->PickPix[0], sv->PickPix[1], i, j,
7936                         colans[0] , colans[1],
7937                         colans[2] , colans[3]);
7938             }
7939             /* so what was that you touched. Note hit is never
7940                returned as null. */
7941             hit = SUMA_WhatWasPicked(sv, colans, &codf, i, j, NULL);
7942             if (!hit || !hit->ado_idcode_str) {
7943                SUMA_LH("Not hit found.");
7944             } else {
7945                if (!(ado = iDO_ADO(SUMA_Picked_DO_ID(codf)))) {
7946                   SUMA_S_Err("Could not locate object in codf (%s) /hit (%s)!",
7947                             codf->ref_idcode_str, hit->ado_idcode_str);
7948                   SUMA_free_PickResult(hit); hit = NULL;
7949                } else {
7950                   if (pado) *pado = ado;
7951                }
7952             }
7953          } else {
7954             SUMA_LH("There is no there there\n");
7955             hit = NULL;
7956          }
7957       }
7958    }
7959 
7960    if (hit && ado) {
7961       SUMA_LH("Adding hit to list");
7962       if (!SUMA_Add_To_PickResult_List(sv, ado, NULL, &hit)) {
7963          SUMA_S_Err("Failed to add selected ado");
7964          SUMA_RETURN(-1);
7965       }
7966       SUMA_RETURN(1);
7967    } else SUMA_RETURN(0);
7968 }
7969 
SUMA_Apply_PR_DO(SUMA_SurfaceViewer * sv,SUMA_ALL_DO * ado,SUMA_PICK_RESULT ** PRi)7970 int SUMA_Apply_PR_DO(SUMA_SurfaceViewer *sv, SUMA_ALL_DO *ado,
7971                      SUMA_PICK_RESULT **PRi)
7972 {
7973    static char FuncName[]={"SUMA_Apply_PR_DO"};
7974    DList *list = NULL;
7975    DListElmt *SetNodeElem = NULL, *Location=NULL;
7976    SUMA_Boolean NodeIgnored = NOPE;
7977    SUMA_PICK_RESULT *PR;
7978    int NP=0, ip=0, it=-1, id = 0, iv3[3], iv15[15];
7979    SUMA_EngineData *ED = NULL;
7980    SUMA_Boolean LocalHead = NOPE;
7981 
7982    SUMA_ENTRY;
7983    SUMA_LH("Here");
7984    if (!sv || !ado || !PRi || !*PRi) { SUMA_S_Err("Niente"); SUMA_RETURN(-1); }
7985 
7986    PR = *PRi; /* keep local copy */
7987    /* Store the PR in ado, hide it from return potential */
7988    SUMA_ADO_StorePickResult(ado, PRi);
7989 
7990    SUMA_LHv("Hit object type %s, label %s\n",
7991          SUMA_ObjectTypeCode2ObjectTypeName(ado->do_type),
7992          SUMA_ADO_Label(ado));
7993 
7994    sv->Focus_DO_ID = ADO_iDO(ado);
7995    SUMA_UpdateViewerTitle(sv);
7996 
7997    /* if the surface controller is open, update it */
7998    if (SUMA_isADO_Cont_Realized(ado))
7999       SUMA_Init_SurfCont_SurfParam(ado);
8000 
8001    /* print nodes about the pick */
8002    switch (ado->do_type) {
8003       case TRACT_type:
8004    fprintf(SUMA_STDOUT, "\nvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n");
8005    fprintf(SUMA_STDOUT, "Selected network %s (Focus_DO_ID # %d).\n"
8006                         "Point %ld, (Bundle %ld, Tract %ld, Point %ld)\n",
8007       ADO_LABEL(ado), sv->Focus_DO_ID, PR->datum_index,
8008       PR->iAltSel[SUMA_NET_BUN], PR->iAltSel[SUMA_BUN_TRC],
8009       PR->iAltSel[SUMA_TRC_PNT]);
8010    fprintf(SUMA_STDOUT, "Seletion coordinates:\n");
8011    fprintf(SUMA_STDOUT, "%f, %f, %f\n",
8012       PR->PickXYZ[0], PR->PickXYZ[1], PR->PickXYZ[2]);
8013    fprintf(SUMA_STDOUT, "\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
8014          break;
8015       case MASK_type:
8016    fprintf(SUMA_STDOUT, "\nvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n");
8017    fprintf(SUMA_STDOUT, "Selected mask %s (Focus_DO_ID # %d).\n",
8018       ADO_LABEL(ado), sv->Focus_DO_ID);
8019    fprintf(SUMA_STDOUT, "Seletion coordinates:\n");
8020    fprintf(SUMA_STDOUT, "%f, %f, %f\n",
8021       PR->PickXYZ[0], PR->PickXYZ[1], PR->PickXYZ[2]);
8022    fprintf(SUMA_STDOUT, "\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
8023          break;
8024       case GRAPH_LINK_type:
8025    fprintf(SUMA_STDOUT, "\nvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n");
8026    fprintf(SUMA_STDOUT, "Selected Graph Dset %s (Focus_DO_ID # %d).\n"
8027                         "Edge %ld, (P0 %ld, P1 %ld)\n",
8028       ADO_LABEL(ado), sv->Focus_DO_ID, PR->datum_index,
8029       PR->iAltSel[SUMA_ENODE_0], PR->iAltSel[SUMA_ENODE_1]);
8030    fprintf(SUMA_STDOUT, "Seletion coordinates:\n");
8031    fprintf(SUMA_STDOUT, "%f, %f, %f\n",
8032       PR->PickXYZ[0], PR->PickXYZ[1], PR->PickXYZ[2]);
8033    fprintf(SUMA_STDOUT, "\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
8034          break;
8035       default:
8036    SUMA_S_Err("Not yet set to report on %s", ADO_LABEL(ado));
8037          break;
8038    }
8039 
8040    /* Based on what you selected, update controller */
8041    /* Set the Nodeselection at the closest node */
8042    if (PR->datum_index >= 0 ||
8043        (PR->datum_index == -1 &&
8044         PR->iAltSel[SUMA_ENODE_0] >= 0 &&
8045         PR->iAltSel[SUMA_ENODE_1] == -1) ) {
8046                      /* 2nd condition is when only edge node is selected.
8047                         We insist on PR->iAltSel[SUMA_ENODE_1] == -1 otherwise
8048                         we would have picked a cell in the matrix for which
8049                         there is no data, hence we have ENODE_0, and ENODE_1,
8050                         but datum_index = -1 */
8051       it = (int)PR->datum_index;
8052       if (!list) list = SUMA_CreateList();
8053       if (PR->ignore_same_datum &&
8054             SUMA_ADO_SelectedDatum(ado, NULL, NULL) == it) {
8055          SUMA_LHv("Ignoring identical datum selection %d on object %s\n",
8056                   SUMA_ADO_SelectedDatum(ado, NULL, NULL), SUMA_ADO_Label(ado));
8057          NodeIgnored = YUP;
8058       } else {
8059          ED = SUMA_InitializeEngineListData (SE_SetSelectedNode);
8060          SetNodeElem = SUMA_RegisterEngineListCommand (  list, ED,
8061                                                 SEF_i, (void*)&it,
8062                                                 SES_Suma, (void *)sv, NOPE,
8063                                                 SEI_Head, NULL);
8064          if (!SetNodeElem) {
8065             fprintf( SUMA_STDERR,
8066                      "Error %s: Failed to register SetNodeElem\n", FuncName);
8067             SUMA_RETURN (-1);
8068          } else {
8069             SUMA_RegisterEngineListCommand (  list, ED,
8070                                               SEF_ngr, NULL,
8071                                               SES_Suma, (void *)sv, NOPE,
8072                                               SEI_In, SetNodeElem);
8073          }
8074          switch (ado->do_type) {
8075             case TRACT_type:
8076                iv15[SUMA_NET_BUN] = (int)PR->iAltSel[SUMA_NET_BUN];
8077                iv15[SUMA_BUN_TRC] = (int)PR->iAltSel[SUMA_BUN_TRC];
8078                iv15[SUMA_TRC_PNT] = (int)PR->iAltSel[SUMA_TRC_PNT];
8079                iv15[SUMA_NET_TRC] = (int)PR->iAltSel[SUMA_NET_TRC];
8080                SUMA_RegisterEngineListCommand (  list, ED,
8081                                                  SEF_iv15, (void *)iv15,
8082                                                  SES_Suma, (void *)sv, NOPE,
8083                                                  SEI_In, SetNodeElem);
8084                break;
8085             case SO_type:
8086             case VO_type:
8087                /* handled in separate function */
8088                break;
8089             default:
8090                SUMA_LH("No aux set here for type %s of %s\n",
8091                         ADO_TNAME(ado), SUMA_ADO_Label(ado));
8092                break;
8093          }
8094 
8095       }
8096    }
8097 
8098 
8099    /* Set the selected edge node (cunningly in recycled selected face set) */
8100    it = PR->iAltSel[SUMA_ENODE_0];
8101    if (!list) list = SUMA_CreateList();
8102    ED = SUMA_InitializeEngineListData (SE_SetSelectedFaceSet);
8103    if (!SUMA_RegisterEngineListCommand (  list, ED,
8104                                           SEF_i, (void*)&it,
8105                                           SES_Suma, (void *)sv, NOPE,
8106                                           SEI_Head, NULL)) {
8107       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
8108       SUMA_RETURN (-1);
8109    }
8110 
8111    /* Now set the cross hair position at the intersection*/
8112    ED = SUMA_InitializeEngineListData (SE_SetCrossHair);
8113    if (!(Location = SUMA_RegisterEngineListCommand (  list, ED,
8114                                           SEF_fv3, (void*)PR->PickXYZ,
8115                                           SES_Suma, (void *)sv, NOPE,
8116                                           SEI_Head, NULL))) {
8117       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
8118       SUMA_RETURN (-1);
8119    }
8120    /* and add the ado with this location, needed for VisX business*/
8121    SUMA_RegisterEngineListCommand (  list, ED,
8122                                            SEF_vp, (void *)ado,
8123                                            SES_Suma, (void *)sv, NOPE,
8124                                            SEI_In, Location);
8125    /* attach the cross hair to the selected ado */
8126    iv3[0] = SUMA_whichDO(PR->ado_idcode_str, SUMAg_DOv, SUMAg_N_DOv);
8127    iv3[1] = PR->datum_index;
8128    iv3[2] = PR->iAltSel[SUMA_ENODE_0];
8129    ED = SUMA_InitializeEngineListData (SE_BindCrossHair);
8130    if (!SUMA_RegisterEngineListCommand (  list, ED,
8131                                           SEF_iv3, (void*)iv3,
8132                                           SES_Suma, (void *)sv, NOPE,
8133                                           SEI_Head, NULL)) {
8134       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
8135       SUMA_RETURN (-1);
8136    }
8137 
8138    /* check to see if AFNI needs to be notified */
8139    /* Need to deal with SUMA_TO_MATLAB_STREAM_INDEX too
8140       Same for remaining occurrence of SUMA_AFNI_STREAM_INDEX*/
8141    if (  ( SUMAg_CF->Connected_v[SUMA_AFNI_STREAM_INDEX] &&
8142            sv->LinkAfniCrossHair )                             ||
8143          ( SUMAg_CF->Connected_v[SUMA_HALLO_SUMA_LINE])        ||
8144          ( SUMAg_CF->Connected_v[SUMA_INSTA_TRACT_LINE])    ) {
8145       if (LocalHead)
8146          fprintf(SUMA_STDERR,
8147                   "%s: Notifying Afni of CrossHair XYZ\n", FuncName);
8148       /* register a call to SetAfniCrossHair */
8149       if (!list) list = SUMA_CreateList();
8150       it = SUMA_ShftCont_Event(PR->evr);
8151       ED = SUMA_InitializeEngineListData (SE_SetAfniCrossHair);
8152       if (!SUMA_RegisterEngineListCommand (  list, ED,
8153                                           SEF_i, (void*)&it,
8154                                           SES_Suma, (void *)sv, NOPE,
8155                                           SEI_Tail, NULL)) {
8156          fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
8157          SUMA_RETURN (-1);
8158       }
8159       if (MASK_MANIP_MODE(sv) && SUMAg_CF->Dev) {
8160          SUMA_ALL_DO *ado = SUMA_whichADOg(sv->MouseMode_ado_idcode_str);
8161          DListElmt *Location=NULL;
8162          SUMA_LH("Might be telling afni about mask...");
8163          if (ado && ado->do_type == MASK_type) {
8164             SUMA_MaskDO *mdo = (SUMA_MaskDO *)ado;
8165             ED = SUMA_InitializeEngineListData (SE_SetAfniMask);
8166             if (!(Location=SUMA_RegisterEngineListCommand (  list, ED,
8167                                                 SEF_fv3, (void*)mdo->cen,
8168                                                 SES_Suma, (void *)sv, NOPE,
8169                                                 SEI_Tail, NULL))) {
8170                SUMA_S_Err("Failed to register element\n");
8171                SUMA_RETURN (-1);
8172             }
8173             SUMA_RegisterEngineListCommand (  list, ED,
8174                                            SEF_s, (void *)(ADO_ID(ado)),
8175                                            SES_Suma, (void *)sv, NOPE,
8176                                            SEI_In, Location);
8177          }
8178       }
8179 
8180       if (!SUMA_Engine (&list)) {
8181          fprintf( SUMA_STDERR,
8182                   "Error %s: SUMA_Engine call failed.\n", FuncName);
8183          SUMA_RETURN (-1);
8184       }
8185    }else {
8186       if (LocalHead)
8187          fprintf(SUMA_STDERR,"%s: No Notification to AFNI.\n", FuncName);
8188    }
8189 
8190    /* now put in a request for locking cross hair but you must do
8191       this after the node selection has been executed
8192       NOTE: You do not always have SetNodeElem because the list might
8193       get emptied in the call to AFNI notification.
8194       You should just put the next call at the end of the list.*/
8195    if (!list) list = SUMA_CreateList();
8196    ED = SUMA_InitializeEngineListData (SE_LockCrossHair);
8197    if (!SUMA_RegisterEngineListCommand (  list, ED,
8198                                           SEF_iv3, (void*)iv3,
8199                                           SES_Suma, (void *)sv, NOPE,
8200                                           SEI_Tail, NULL)) {
8201       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
8202       SUMA_RETURN (-1);
8203    }
8204    if (!SUMA_Engine (&list)) {
8205       fprintf(SUMA_STDERR, "Error %s: SUMA_Engine call failed.\n", FuncName);
8206       SUMA_RETURN (-1);
8207    }
8208 
8209    SUMA_RETURN(1);
8210 }
8211 
SUMA_Apply_PR(SUMA_SurfaceViewer * sv,SUMA_PICK_RESULT ** PR)8212 int SUMA_Apply_PR(SUMA_SurfaceViewer *sv, SUMA_PICK_RESULT **PR)
8213 {
8214    static char FuncName[]={"SUMA_Apply_PR"};
8215    SUMA_ALL_DO *ado=NULL, *ado_pick=NULL;
8216    float *xyz=NULL;
8217    SUMA_Boolean LocalHead = NOPE;
8218 
8219    SUMA_ENTRY;
8220 
8221    if (!sv || !PR || !*PR) {
8222       SUMA_S_Err("NULL input %p %p %p", sv, PR, *PR);
8223       SUMA_DUMP_TRACE("PR application");
8224       SUMA_RETURN(-1);
8225    }
8226    if (MASK_MANIP_MODE(sv)) {
8227       ado_pick = SUMA_whichADOg((*PR)->ado_idcode_str);
8228       SUMA_LH("Mask Manip Mode, moving to %f %f %f per ado %s\n",
8229                (*PR)->PickXYZ[0], (*PR)->PickXYZ[1], (*PR)->PickXYZ[2],
8230                ADO_LABEL(ado_pick));
8231       SUMA_ifree(sv->LastSel_ado_idcode_str);
8232       sv->LastSel_ado_idcode_str = SUMA_copy_string((*PR)->ado_idcode_str);
8233       /* Just move the selected mask */
8234       if (!(ado = SUMA_whichADOg(sv->MouseMode_ado_idcode_str))) {
8235          SUMA_S_Err("Yikes, got not ado for MouseMode_ado_idcode_str %s",
8236               sv->MouseMode_ado_idcode_str?sv->MouseMode_ado_idcode_str:"NULL");
8237          SUMA_RETURN(-1);
8238       }
8239       if (ado->do_type != MASK_type) {
8240          SUMA_S_Err("Bad ID for mouse mode value");
8241          SUMA_RETURN(-1);
8242       }
8243       /* Set the parent as the ado_pick */
8244       SUMA_MDO_New_parent((SUMA_MaskDO *)ado, (*PR)->ado_idcode_str,
8245                           (*PR)->datum_index);
8246 
8247       if (ado_pick->do_type == SO_type) {
8248          SUMA_SurfaceObject *SO = (SUMA_SurfaceObject *)ado_pick;
8249          /* PickXYZ might be under VisX */
8250          xyz = SO->NodeList+SO->NodeDim* (*PR)->datum_index;
8251          SUMA_MDO_New_Doppel((SUMA_MaskDO *)ado, (*PR)->PickXYZ);
8252       } else {
8253          xyz = (*PR)->PickXYZ;
8254          SUMA_MDO_New_Doppel((SUMA_MaskDO *)ado, NULL);
8255       }
8256       if (LocalHead) SUMA_DUMP_TRACE("Box motion");
8257       SUMA_NEW_MASKSTATE();
8258       SUMA_MDO_New_Cen((SUMA_MaskDO *)ado, xyz);
8259    }
8260 
8261    {
8262       ado = SUMA_whichADOg((*PR)->ado_idcode_str);
8263       SUMA_ifree(sv->LastSel_ado_idcode_str);
8264       sv->LastSel_ado_idcode_str = SUMA_copy_string((*PR)->ado_idcode_str);
8265       switch (ado->do_type) {
8266          case SO_type:
8267             SUMA_RETURN(SUMA_Apply_PR_SO(sv, (SUMA_SurfaceObject *)ado, PR));
8268             break;
8269          case VO_type:
8270             SUMA_RETURN(SUMA_Apply_PR_VO(sv, (SUMA_VolumeObject *)ado, PR));
8271             break;
8272          case GRAPH_LINK_type:
8273             SUMA_RETURN(SUMA_Apply_PR_DO(sv, ado, PR));
8274             break;
8275          case TRACT_type:
8276             SUMA_RETURN(SUMA_Apply_PR_DO(sv, ado, PR));
8277             break;
8278          case MASK_type:
8279             SUMA_RETURN(SUMA_Apply_PR_DO(sv, ado, PR));
8280             break;
8281          default:
8282             SUMA_S_Err("Not yet implemented for %s", ADO_TNAME(ado));
8283             SUMA_RETURN(-1);
8284             break;
8285       }
8286    }
8287    SUMA_RETURN(-1);
8288 }
8289 
SUMA_MarkLineMaskIntersect(SUMA_SurfaceViewer * sv,SUMA_DO * dov,int IgnoreSameNode)8290 int SUMA_MarkLineMaskIntersect (SUMA_SurfaceViewer *sv, SUMA_DO *dov,
8291                                 int IgnoreSameNode)
8292 {/* determine intersection */
8293    static char FuncName[]={"SUMA_MarkLineMaskIntersect"};
8294    SUMA_PICK_RESULT *PR = NULL;
8295    SUMA_ALL_DO *ado = NULL;
8296    int ans;
8297 
8298    SUMA_ENTRY;
8299    SUMA_S_Warn("Do not call me anymore. Follow the new selection logic");
8300    ans = SUMA_ComputeLineMaskIntersect(sv, dov, IgnoreSameNode, &ado);
8301    if (ans <= 0) {
8302       SUMA_RETURN(ans);
8303    }
8304    /* just for temporary testing, get PR back from list and apply it */
8305    PR = SUMA_Get_From_PickResult_List(sv, ado, NULL);
8306    ans = SUMA_Apply_PR(sv, &PR);
8307    SUMA_RETURN(ans);
8308 }
8309 
SUMA_ComputeLineMaskIntersect(SUMA_SurfaceViewer * sv,SUMA_DO * dov,int IgnoreSameNode,SUMA_ALL_DO ** pado)8310 int SUMA_ComputeLineMaskIntersect (SUMA_SurfaceViewer *sv, SUMA_DO *dov,
8311                                       int IgnoreSameNode, SUMA_ALL_DO **pado)
8312 {/* determine intersection */
8313    static char FuncName[]={"SUMA_ComputeLineMaskIntersect"};
8314    float P0f[3], P1f[3];
8315    int NP;
8316    SUMA_MT_INTERSECT_TRIANGLE *MTI = NULL, *MTIi = NULL;
8317    float delta_t_tmp, dmin;
8318    struct timeval tt_tmp;
8319    SUMA_ALL_DO *ado=NULL;
8320    int ip, it, id, ii, N_MDOlist,
8321        MDOlist[SUMA_MAX_DISPLAYABLE_OBJECTS], imin;
8322    char sfield[100], sdestination[100], CommString[SUMA_MAX_COMMAND_LENGTH];
8323    SUMA_MaskDO *MDO = NULL;
8324    SUMA_Boolean LocalHead = NOPE;
8325 
8326    SUMA_ENTRY;
8327 
8328    P0f[0] = sv->Pick0[0];
8329    P0f[1] = sv->Pick0[1];
8330    P0f[2] = sv->Pick0[2];
8331    P1f[0] = sv->Pick1[0];
8332    P1f[1] = sv->Pick1[1];
8333    P1f[2] = sv->Pick1[2];
8334 
8335    N_MDOlist = SUMA_VisibleMDOs(sv, dov, MDOlist);
8336    if (LocalHead) {
8337       SUMA_LH("%d visible MDOs", N_MDOlist);
8338       for (ii=0; ii < N_MDOlist; ++ii) {
8339          ado = (SUMA_ALL_DO *)dov[MDOlist[ii]].OP;
8340          fprintf(SUMA_STDERR,"%d: object %d in DOv, label %s, type %s\n",
8341                   ii, MDOlist[ii], ADO_LABEL(ado), ADO_TNAME(ado));
8342       }
8343    }
8344    imin = -1;
8345    dmin = 10000000.0;
8346    for (ii=0; ii < N_MDOlist; ++ii) { /* find the closest intersection */
8347       if (LocalHead)
8348             fprintf (SUMA_STDERR,
8349                      "%s: working %d/%d shown masks ...\n",
8350                      FuncName, ii, N_MDOlist);
8351       MDO = (SUMA_MaskDO *)dov[MDOlist[ii]].OP;
8352       if (!MDO_IS_SURF(MDO) && !MDO_IS_BOX(MDO) && !MDO_IS_SPH(MDO)) {
8353          fprintf(SUMA_STDERR,
8354             "Error %s: "
8355             "Not ready to handle such MDO (%s) intersections on %s\n",
8356             FuncName, MDO->mtype, ADO_LABEL((SUMA_ALL_DO *)MDO));
8357       } if (!MDO->SO) {
8358          SUMA_S_Err("No SO buster on %s", ADO_LABEL((SUMA_ALL_DO *)MDO));
8359       } else {
8360          /* Here we're doing the intersection the lazy way, via the SO,
8361          although this can be done a lot faster in other ways for box and
8362          sphere, speed is not an issue here */
8363          SUMA_etime (&tt_tmp, 0);
8364          MTIi = SUMA_MT_intersect_triangle(P0f, P1f, MDO->SO->NodeList,
8365                                  MDO->SO->N_Node, MDO->SO->FaceSetList,
8366                                  MDO->SO->N_FaceSet, NULL, 0);
8367 
8368          delta_t_tmp = SUMA_etime (&tt_tmp, 1);
8369          SUMA_LH("Intersection took %f seconds with %s.\n",
8370                delta_t_tmp,
8371                ADO_LABEL((SUMA_ALL_DO *)dov[MDOlist[ii]].OP));
8372 
8373          if (MTIi == NULL) {
8374             fprintf(SUMA_STDERR,
8375                      "Error %s: SUMA_MT_intersect_triangle failed.\n", FuncName);
8376             SUMA_RETURN (-1);
8377          }
8378 
8379          if (MTIi->N_hits) {
8380             /* decide on the closest surface to the clicking point */
8381             if (MTIi->t[MTIi->ifacemin] < dmin) {
8382                if (LocalHead)
8383                   fprintf (SUMA_STDERR, "%s: A minimum for surface %d.\n",
8384                            FuncName, ii);
8385                dmin = MTIi->t[MTIi->ifacemin];
8386                imin = MDOlist[ii];
8387                MTI = MTIi;
8388             }else {
8389                /* not good, toss it away */
8390                SUMA_LH("ii=%d not any closer (%f vs %f). freeing MTIi...\n",
8391                         ii,MTIi->t[MTIi->ifacemin], dmin);
8392                MTIi = SUMA_Free_MT_intersect_triangle(MTIi);
8393             }
8394          }else {
8395             /* not good, toss it away */
8396            if (LocalHead)
8397                fprintf (SUMA_STDERR,
8398                         "%s: ii=%d freeing MTIi no hits...\n", FuncName, ii);
8399            MTIi = SUMA_Free_MT_intersect_triangle(MTIi);
8400         }
8401       }
8402     }
8403 
8404    if (LocalHead)
8405       fprintf (SUMA_STDERR,
8406                "%s: Closest surface is indexed %d in DOv.\n", FuncName, imin);
8407 
8408    if (imin >= 0) {
8409       SUMA_PICK_RESULT *PR;
8410       SUMA_ALL_DO *ado;
8411       if (!(ado = iDO_ADO(imin))) {
8412          SUMA_S_Err("NULL ado at this point? imin = %d", imin);
8413          SUMA_RETURN(-1);
8414       }
8415       PR = SUMA_New_Pick_Result(NULL);
8416       if (pado) *pado = ado; /* user want answer back */
8417       PR->ado_idcode_str = SUMA_copy_string(ADO_ID(ado));
8418       PR->datum_index = MTI->inodemin;
8419       PR->ignore_same_datum = IgnoreSameNode;
8420       PR->iAltSel[SUMA_SURF_TRI] = MTI->ifacemin;
8421       SUMA_COPY_VEC(MTI->P, PR->PickXYZ, 3, float, float);
8422       /* Add selection result to stack */
8423       if (!SUMA_Add_To_PickResult_List(sv, ado, NULL, &PR)) {
8424          SUMA_S_Err("Failed to add selected ado");
8425          SUMA_RETURN(-1);
8426       }
8427    }
8428 
8429    /* clear MTI */
8430    if (MTI) {
8431       MTI = SUMA_Free_MT_intersect_triangle(MTI);
8432    }
8433 
8434    if (imin >=0) SUMA_RETURN(1);
8435    else SUMA_RETURN(0);
8436 }
8437 
8438 
SUMA_MarkLineSurfaceIntersect(SUMA_SurfaceViewer * sv,SUMA_DO * dov,int IgnoreSameNode)8439 int SUMA_MarkLineSurfaceIntersect (SUMA_SurfaceViewer *sv, SUMA_DO *dov,
8440                                     int IgnoreSameNode)
8441 {
8442    static char FuncName[]={"SUMA_MarkLineSurfaceIntersect"};
8443    SUMA_PICK_RESULT *PR = NULL;
8444    SUMA_ALL_DO *ado = NULL;
8445    int ans;
8446 
8447    SUMA_ENTRY;
8448    SUMA_S_Warn("Do not call me anymore."
8449                "Go via SUMA_ComputeLineSurfaceIntersect. "
8450                "This is left here for testing purposes");
8451    ans = SUMA_ComputeLineSurfaceIntersect(sv, dov, IgnoreSameNode, &ado);
8452    if (ans <= 0) {
8453       SUMA_RETURN(ans);
8454    }
8455    /* just for temporary testing, get PR back from list and apply it */
8456    PR = SUMA_Get_From_PickResult_List(sv, ado, NULL);
8457    ans = SUMA_Apply_PR(sv, &PR);
8458 
8459    SUMA_RETURN(ans);
8460 }
8461 
8462 /*!
8463    Determines the intersection between ]sv->Pick0 sv->Pick1[ and SO
8464    Highlights the intersected faceset, node and updates cross hair location
8465    This used to be part of Button3's code in SUMA_input
8466    ans = SUMA_ComputeLineSurfaceIntersect (sv, dov);
8467    \param sv (SUMA_SurfaceViewer *) surface viewer pointer
8468    \param dov (SUMA_DO *) displayable object vector pointer
8469    \param IgnoreSameNode (int) 1 do nothing if node already selected
8470                                0 don't care if it was already selected
8471    \param pado  (SUMA_ALL_DO **) If not NULL, *pado will contain a copy
8472                                  of an ADO pointer to the intersected
8473                                  object, if any.
8474    \ret ans (int)  -1 error, 0 no hit, hit
8475 
8476    Note that this function will register whatever surfaces get hit in the
8477    selections list with a call to SUMA_Add_To_PickResult_List()
8478 
8479    also requires SUMAg_DOv and SUMAg_N_DOv
8480 */
SUMA_ComputeLineSurfaceIntersect(SUMA_SurfaceViewer * sv,SUMA_DO * dov,int IgnoreSameNode,SUMA_ALL_DO ** pado)8481 int SUMA_ComputeLineSurfaceIntersect (SUMA_SurfaceViewer *sv, SUMA_DO *dov,
8482                                       int IgnoreSameNode, SUMA_ALL_DO **pado)
8483 {/* determine intersection */
8484    static char FuncName[]={"SUMA_ComputeLineSurfaceIntersect"};
8485    float P0f[3], P1f[3];
8486    int NP;
8487    SUMA_MT_INTERSECT_TRIANGLE *MTI = NULL, *MTIi = NULL;
8488    float delta_t_tmp, dmin;
8489    struct timeval tt_tmp;
8490    int ip, it, id, ii, N_SOlist,
8491        SOlist[SUMA_MAX_DISPLAYABLE_OBJECTS], imin;
8492    char sfield[100], sdestination[100], CommString[SUMA_MAX_COMMAND_LENGTH];
8493    SUMA_SurfaceObject *SO = NULL;
8494    SUMA_Boolean LocalHead = NOPE;
8495 
8496    SUMA_ENTRY;
8497 
8498    P0f[0] = sv->Pick0[0];
8499    P0f[1] = sv->Pick0[1];
8500    P0f[2] = sv->Pick0[2];
8501    P1f[0] = sv->Pick1[0];
8502    P1f[1] = sv->Pick1[1];
8503    P1f[2] = sv->Pick1[2];
8504 
8505    N_SOlist = SUMA_VisibleSOs(sv, dov, SOlist, 0);
8506    imin = -1;
8507    dmin = 10000000.0;
8508    for (ii=0; ii < N_SOlist; ++ii) { /* find the closest intersection */
8509       if (LocalHead)
8510             fprintf (SUMA_STDERR,
8511                      "%s: working %d/%d shown surfaces ...\n",
8512                      FuncName, ii, N_SOlist);
8513       SO = (SUMA_SurfaceObject *)dov[SOlist[ii]].OP;
8514       SUMA_VisX_Pointers4Display(SO, 1); /* using coordinates as displayed */
8515       if (SO->FaceSetDim != 3) {
8516          fprintf(SUMA_STDERR,
8517             "Error %s: "
8518             "SUMA_MT_intersect_triangle only works for triangular meshes.\n",
8519             FuncName);
8520       } else {
8521 
8522          SUMA_etime (&tt_tmp, 0);
8523 
8524          MTIi = SUMA_MT_intersect_triangle(P0f, P1f, SO->NodeList, SO->N_Node,
8525                                         SO->FaceSetList, SO->N_FaceSet, NULL, 0);
8526 
8527          delta_t_tmp = SUMA_etime (&tt_tmp, 1);
8528          if (LocalHead)
8529             fprintf (SUMA_STDERR,
8530                "Local Debug %s: Intersection took %f seconds.\n",
8531                FuncName, delta_t_tmp);
8532 
8533          if (MTIi == NULL) {
8534             fprintf(SUMA_STDERR,
8535                      "Error %s: SUMA_MT_intersect_triangle failed.\n", FuncName);
8536             SUMA_RETURN (-1);
8537          }
8538 
8539          if (MTIi->N_hits) {
8540             /* decide on the closest surface to the clicking point */
8541             if (MTIi->t[MTIi->ifacemin] < dmin) {
8542                if (LocalHead)
8543                   fprintf (SUMA_STDERR, "%s: A minimum for surface %d.\n",
8544                            FuncName, ii);
8545                dmin = MTIi->t[MTIi->ifacemin];
8546                imin = SOlist[ii];
8547                MTI = MTIi;
8548             }else {
8549                /* not good, toss it away */
8550                if (LocalHead)
8551                   fprintf (SUMA_STDERR,
8552                            "%s: ii=%d freeing MTIi...\n", FuncName, ii);
8553                MTIi = SUMA_Free_MT_intersect_triangle(MTIi);
8554             }
8555          }else {
8556             /* not good, toss it away */
8557            if (LocalHead)
8558                fprintf (SUMA_STDERR,
8559                         "%s: ii=%d freeing MTIi no hits...\n", FuncName, ii);
8560            MTIi = SUMA_Free_MT_intersect_triangle(MTIi);
8561         }
8562       }
8563       SUMA_VisX_Pointers4Display(SO, 0); /* put things back young man */
8564 
8565     }
8566 
8567    if (LocalHead)
8568       fprintf (SUMA_STDERR,
8569                "%s: Closest surface is indexed %d in DOv.\n", FuncName, imin);
8570 
8571    if (imin >= 0) {
8572       SUMA_PICK_RESULT *PR;
8573       SUMA_ALL_DO *ado;
8574       if (!(ado = iDO_ADO(imin))) {
8575          SUMA_S_Err("NULL ado at this point?");
8576          SUMA_RETURN(-1);
8577       }
8578       PR = SUMA_New_Pick_Result(NULL);
8579       if (pado) *pado = ado; /* user want answer back */
8580       PR->ado_idcode_str = SUMA_copy_string(ADO_ID(ado));
8581       PR->datum_index = MTI->inodemin;
8582       PR->ignore_same_datum = IgnoreSameNode;
8583       PR->iAltSel[SUMA_SURF_TRI] = MTI->ifacemin;
8584       SUMA_COPY_VEC(MTI->P, PR->PickXYZ, 3, float, float);
8585       /* Add selection result to stack */
8586       if (!SUMA_Add_To_PickResult_List(sv, ado, NULL, &PR)) {
8587          SUMA_S_Err("Failed to add selected ado");
8588          SUMA_RETURN(-1);
8589       }
8590    }
8591 
8592    /* clear MTI */
8593    if (MTI) {
8594       MTI = SUMA_Free_MT_intersect_triangle(MTI);
8595    }
8596 
8597    if (imin >=0) SUMA_RETURN(1);
8598    else SUMA_RETURN(0);
8599 }
8600 
SUMA_Apply_PR_SO(SUMA_SurfaceViewer * sv,SUMA_SurfaceObject * SO,SUMA_PICK_RESULT ** PRi)8601 int SUMA_Apply_PR_SO(SUMA_SurfaceViewer *sv, SUMA_SurfaceObject *SO,
8602                      SUMA_PICK_RESULT **PRi)
8603 {
8604    static char FuncName[]={"SUMA_Apply_PR_SO"};
8605    SUMA_ALL_DO *ado=NULL;
8606    DList *list = NULL;
8607    DListElmt *SetNodeElem = NULL, *Location=NULL;
8608    SUMA_Boolean NodeIgnored = NOPE;
8609    SUMA_PICK_RESULT *PR;
8610    int NP=0, ip=0, it=0, id = 0, iv3[3];
8611    SUMA_EngineData *ED = NULL;
8612    SUMA_Boolean LocalHead = NOPE;
8613 
8614    SUMA_ENTRY;
8615 
8616    if (!sv || !SO || !PRi || !*PRi) { SUMA_S_Err("Niente"); SUMA_RETURN(-1); }
8617 
8618    /* Mark intersection Facsets */
8619    ado = (SUMA_ALL_DO *)SO;
8620 
8621    PR = *PRi;   /* Keep local copy */
8622    /* Store the PR in ado, hide it from return potential */
8623    SUMA_ADO_StorePickResult(ado, PRi);
8624 
8625 
8626    sv->Focus_DO_ID = ADO_iDO(ado);
8627    SUMA_UpdateViewerTitle(sv);
8628 
8629    NP = SO->FaceSetDim;
8630    ip = NP * PR->iAltSel[SUMA_SURF_TRI];
8631 
8632 
8633    /* if the surface controller is open, update it */
8634    if (SUMA_isADO_Cont_Realized(ado))
8635        SUMA_Init_SurfCont_SurfParam(ado);
8636 
8637    /* print nodes about the closets faceset*/
8638    fprintf(SUMA_STDOUT, "\nvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n");
8639    fprintf(SUMA_STDOUT, "Selected surface %s (Focus_DO_ID # %d).\n"
8640                         "FaceSet %ld, Closest Node %ld\n",
8641       SO->Label, sv->Focus_DO_ID, PR->iAltSel[SUMA_SURF_TRI],
8642       PR->datum_index);
8643    fprintf(SUMA_STDOUT, "Nodes forming closest FaceSet:\n");
8644    fprintf(SUMA_STDOUT, "%d, %d, %d\n",
8645       SO->FaceSetList[ip], SO->FaceSetList[ip+1],SO->FaceSetList[ip+2]);
8646 
8647    fprintf (SUMA_STDOUT,"Coordinates of Nodes forming closest FaceSet:\n");
8648    for (it=0; it < 3; ++it) {
8649 
8650       id = SO->NodeDim * SO->FaceSetList[ip+it];
8651       fprintf(SUMA_STDOUT, "%f, %f, %f\n", SO->NodeList[id],
8652                                            SO->NodeList[id+1],
8653                                            SO->NodeList[id+2]);
8654    }
8655    fprintf(SUMA_STDOUT, "\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
8656 
8657    /* Set the Nodeselection at the closest node */
8658    it = PR->datum_index;
8659    if (!list) list = SUMA_CreateList();
8660    if (PR->ignore_same_datum && SO->SelectedNode == PR->datum_index) {
8661       SUMA_LHv("Ignoring identical node selection %d on surface %s\n",
8662                SO->SelectedNode, SO->Label);
8663       NodeIgnored = YUP;
8664    } else {
8665       ED = SUMA_InitializeEngineListData (SE_SetSelectedNode);
8666       SetNodeElem = SUMA_RegisterEngineListCommand (  list, ED,
8667                                              SEF_i, (void*)&it,
8668                                              SES_Suma, (void *)sv, NOPE,
8669                                              SEI_Head, NULL);
8670       if (!SetNodeElem) {
8671          fprintf( SUMA_STDERR,
8672                   "Error %s: Failed to register SetNodeElem\n", FuncName);
8673          SUMA_RETURN (-1);
8674       } else {
8675          SUMA_RegisterEngineListCommand (  list, ED,
8676                                            SEF_ngr, NULL,
8677                                            SES_Suma, (void *)sv, NOPE,
8678                                            SEI_In, SetNodeElem);
8679       }
8680    }
8681 
8682 
8683    /* Set the FaceSetselection */
8684    it = PR->iAltSel[SUMA_SURF_TRI];
8685    ED = SUMA_InitializeEngineListData (SE_SetSelectedFaceSet);
8686    if (!SUMA_RegisterEngineListCommand (  list, ED,
8687                                           SEF_i, (void*)&it,
8688                                           SES_Suma, (void *)sv, NOPE,
8689                                           SEI_Head, NULL)) {
8690       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
8691       SUMA_RETURN (-1);
8692    }
8693 
8694    /* Now set the cross hair position at the intersection*/
8695    ED = SUMA_InitializeEngineListData (SE_SetCrossHair);
8696    if (!(Location = SUMA_RegisterEngineListCommand (  list, ED,
8697                                           SEF_fv3, (void*)PR->PickXYZ,
8698                                           SES_Suma, (void *)sv, NOPE,
8699                                           SEI_Head, NULL))) {
8700       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
8701       SUMA_RETURN (-1);
8702    }
8703    /* and add the SO with this location, needed for VisX business*/
8704    SUMA_RegisterEngineListCommand (  list, ED,
8705                                            SEF_vp, (void *)SO,
8706                                            SES_Suma, (void *)sv, NOPE,
8707                                            SEI_In, Location);
8708 
8709    /* attach the cross hair to the selected surface */
8710    iv3[0] = SUMA_findSO_inDOv(SO->idcode_str, SUMAg_DOv, SUMAg_N_DOv);
8711    iv3[1] = PR->datum_index;
8712    iv3[2] = PR->iAltSel[SUMA_SURF_TRI];
8713    ED = SUMA_InitializeEngineListData (SE_BindCrossHair);
8714    if (!SUMA_RegisterEngineListCommand (  list, ED,
8715                                           SEF_iv3, (void*)iv3,
8716                                           SES_Suma, (void *)sv, NOPE,
8717                                           SEI_Head, NULL)) {
8718       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
8719       SUMA_RETURN (-1);
8720    }
8721 
8722    /* check to see if AFNI needs to be notified */
8723    /* Need to deal with SUMA_TO_MATLAB_STREAM_INDEX too
8724       Same for remaining occurrence of SUMA_AFNI_STREAM_INDEX*/
8725    if (  ( SUMAg_CF->Connected_v[SUMA_AFNI_STREAM_INDEX] &&
8726            sv->LinkAfniCrossHair )                             ||
8727          ( SUMAg_CF->Connected_v[SUMA_HALLO_SUMA_LINE])        ||
8728          ( SUMAg_CF->Connected_v[SUMA_INSTA_TRACT_LINE])    ) {
8729       if (LocalHead)
8730          fprintf(SUMA_STDERR,
8731                   "%s: Notifying Afni of CrossHair XYZ\n", FuncName);
8732       /* register a call to SetAfniCrossHair */
8733       if (!list) list = SUMA_CreateList();
8734       it = SUMA_ShftCont_Event(PR->evr);
8735       ED = SUMA_InitializeEngineListData (SE_SetAfniCrossHair);
8736       if (!SUMA_RegisterEngineListCommand (  list, ED,
8737                                           SEF_i, (void*)&it,
8738                                           SES_Suma, (void *)sv, NOPE,
8739                                           SEI_Tail, NULL)) {
8740          fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
8741          SUMA_RETURN (-1);
8742       }
8743       if (MASK_MANIP_MODE(sv) && SUMAg_CF->Dev) {
8744          SUMA_ALL_DO *ado = SUMA_whichADOg(sv->MouseMode_ado_idcode_str);
8745          if (ado && ado->do_type == MASK_type) {
8746             SUMA_MaskDO *mdo = (SUMA_MaskDO *)ado;
8747             ED = SUMA_InitializeEngineListData (SE_SetAfniMask);
8748             if (!(Location=SUMA_RegisterEngineListCommand (  list, ED,
8749                                                 SEF_fv3, (void*)mdo->cen,
8750                                                 SES_Suma, (void *)sv, NOPE,
8751                                                 SEI_Tail, NULL))) {
8752                SUMA_S_Err("Failed to register element\n");
8753                SUMA_RETURN (-1);
8754             }
8755             SUMA_RegisterEngineListCommand (  list, ED,
8756                                            SEF_s, (void *)(ADO_ID(ado)),
8757                                            SES_Suma, (void *)sv, NOPE,
8758                                            SEI_In, Location);
8759          }
8760       }
8761       if (!SUMA_Engine (&list)) {
8762          fprintf( SUMA_STDERR,
8763                   "Error %s: SUMA_Engine call failed.\n", FuncName);
8764          SUMA_RETURN (-1);
8765       }
8766    }else {
8767       if (LocalHead)
8768          fprintf(SUMA_STDERR,"%s: No Notification to AFNI.\n", FuncName);
8769    }
8770 
8771    /* put in a request for GICOR if need be */
8772    if (  !NodeIgnored &&
8773          SUMAg_CF->Connected_v[SUMA_GICORR_LINE] &&
8774          SUMAg_CF->giset && !SUMAg_CF->HoldClickCallbacks) {
8775       if (LocalHead)
8776          fprintf(SUMA_STDERR,
8777                   "%s: Notifying GICOR of node selection\n", FuncName);
8778       /* register a call to SetGICORnode */
8779       if (!list) list = SUMA_CreateList();
8780       SUMA_REGISTER_TAIL_COMMAND_NO_DATA( list, SE_SetGICORnode,
8781                                           SES_Suma, sv);
8782       if (!SUMA_Engine (&list)) {
8783          fprintf( SUMA_STDERR,
8784                   "Error %s: SUMA_Engine call failed.\n", FuncName);
8785          SUMA_RETURN (-1);
8786       }
8787    }else {
8788       if (LocalHead)
8789          fprintf(SUMA_STDERR,"%s: No Notification to GICOR.\n", FuncName);
8790    }
8791    /* now put in a request for locking cross hair but you must do
8792       this after the node selection has been executed
8793       NOTE: You do not always have SetNodeElem because the list might
8794       get emptied in the call to AFNI notification.
8795       You should just put the next call at the end of the list.*/
8796    SUMA_LH("Cross hair locking");
8797    if (!list) list = SUMA_CreateList();
8798    ED = SUMA_InitializeEngineListData (SE_LockCrossHair);
8799    if (!SUMA_RegisterEngineListCommand (  list, ED,
8800                                           SEF_iv3, (void*)iv3,
8801                                           SES_Suma, (void *)sv, NOPE,
8802                                           SEI_Tail, NULL)) {
8803       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
8804       SUMA_RETURN (-1);
8805    }
8806 
8807    SUMA_LH("Cross hair locking Engine call");
8808    if (!SUMA_Engine (&list)) {
8809       fprintf(SUMA_STDERR, "Error %s: SUMA_Engine call failed.\n", FuncName);
8810       SUMA_RETURN (-1);
8811    }
8812    SUMA_LH("Returning");
8813    SUMA_RETURN (1); /* OK */
8814 }/* determine intersection */
8815 
SUMA_MarkLineCutplaneIntersect(SUMA_SurfaceViewer * sv,SUMA_DO * dov,int IgnoreSameNode)8816 int SUMA_MarkLineCutplaneIntersect (SUMA_SurfaceViewer *sv, SUMA_DO *dov,
8817                                     int IgnoreSameNode)
8818 {/* determine intersection */
8819    static char FuncName[]={"SUMA_MarkLineCutplaneIntersect"};
8820    float P0f[3], P1f[3];
8821    int NP;
8822    SUMA_MT_INTERSECT_TRIANGLE *MTI = NULL, *MTIi = NULL;
8823    float delta_t_tmp, dmin;
8824    struct timeval tt_tmp;
8825    int ip, it, id, iv3[3], ii, N_SOlist,
8826        SOlist[SUMA_MAX_DISPLAYABLE_OBJECTS], imin;
8827    char sfield[100], sdestination[100], CommString[SUMA_MAX_COMMAND_LENGTH];
8828    SUMA_EngineData *ED = NULL;
8829    DList *list = NULL;
8830    DListElmt *SetNodeElem = NULL, *Location=NULL;
8831    SUMA_SurfaceObject *SO = NULL;
8832    SUMA_SurfaceObject **SOv = NULL;
8833    SUMA_VolumeObject *VO=NULL;
8834    SUMA_Boolean NodeIgnored = NOPE;
8835    SUMA_Boolean LocalHead = NOPE;
8836 
8837    SUMA_ENTRY;
8838 
8839    P0f[0] = sv->Pick0[0];
8840    P0f[1] = sv->Pick0[1];
8841    P0f[2] = sv->Pick0[2];
8842    P1f[0] = sv->Pick1[0];
8843    P1f[1] = sv->Pick1[1];
8844    P1f[2] = sv->Pick1[2];
8845 
8846    SUMA_LH("Getting array of pointers to clip plane surfaces");
8847    if (!(SOv = SUMA_TextureClipPlaneSurfaces(&N_SOlist))) {
8848       SUMA_LH("No clip plane surfaces");
8849       SUMA_RETURN(0);
8850    }
8851    imin = -1;
8852    dmin = 10000000.0;
8853    for (ii=0; ii < N_SOlist; ++ii) { /* find the closest intersection */
8854       if (LocalHead)
8855             fprintf (SUMA_STDERR,
8856                      "%s: working %d/%d clip plane ...\n",
8857                      FuncName, ii, N_SOlist);
8858       SO = SOv[ii];
8859       if (SO->FaceSetDim != 3) {
8860          fprintf(SUMA_STDERR,
8861             "Error %s: "
8862             "SUMA_MT_intersect_triangle only works for triangular meshes.\n",
8863             FuncName);
8864       } else {
8865 
8866          SUMA_etime (&tt_tmp, 0);
8867          SUMA_LH("About to call intersection function");
8868 
8869          MTIi = SUMA_MT_intersect_triangle(P0f, P1f, SO->NodeList, SO->N_Node,
8870                                         SO->FaceSetList, SO->N_FaceSet, NULL, 0);
8871 
8872          delta_t_tmp = SUMA_etime (&tt_tmp, 1);
8873          if (LocalHead)
8874             fprintf (SUMA_STDERR,
8875                "Local Debug %s: Intersection took %f seconds.\n",
8876                FuncName, delta_t_tmp);
8877 
8878          if (MTIi == NULL) {
8879             fprintf(SUMA_STDERR,
8880                      "Error %s: SUMA_MT_intersect_triangle failed.\n", FuncName);
8881             SUMA_RETURN (-1);
8882          }
8883 
8884          if (MTIi->N_hits) {
8885             /* decide on the closest surface to the clicking point */
8886             if (MTIi->t[MTIi->ifacemin] < dmin) {
8887                if (LocalHead)
8888                   fprintf (SUMA_STDERR, "%s: A minimum for surface %d.\n",
8889                            FuncName, ii);
8890                dmin = MTIi->t[MTIi->ifacemin];
8891                imin = ii;
8892                MTI = MTIi;
8893             }else {
8894                /* not good, toss it away */
8895                if (LocalHead)
8896                   fprintf (SUMA_STDERR,
8897                            "%s: ii=%d freeing MTIi...\n", FuncName, ii);
8898                MTIi = SUMA_Free_MT_intersect_triangle(MTIi);
8899             }
8900          }else {
8901             /* not good, toss it away */
8902            if (LocalHead)
8903                fprintf (SUMA_STDERR,
8904                         "%s: ii=%d freeing MTIi no hits...\n", FuncName, ii);
8905            MTIi = SUMA_Free_MT_intersect_triangle(MTIi);
8906         }
8907       }
8908     }
8909 
8910    if (LocalHead)
8911       fprintf (SUMA_STDERR,
8912                "%s: Closest surface is indexed %d in cutplane surfaces.\n",
8913                FuncName, imin);
8914 
8915    /* Mark intersection Facsets */
8916    if (imin >= 0) {
8917       SUMA_ALL_DO *ado=NULL;
8918       if (!(VO = SUMA_VolumeObjectOfClipPlaneSurface(SO))) {
8919          SUMA_S_Err("Failed to find volume object for clipped surface");
8920          SUMA_RETURN(-1);
8921       }
8922       VO->SelectedCutPlane = imin;
8923       SUMA_S_Warn("NEED TO IMPLEMENT PR THING HERE, THEN PASS IT BELOW");
8924       ado = (SUMA_ALL_DO *)VO;
8925       if (!SUMA_Add_To_PickResult_List(sv, ado, "cutplane", NULL)) {
8926          SUMA_S_Err("Failed to add selected ado");
8927          SUMA_RETURN(-1);
8928       }
8929       /* Now set this volume as the focus DO */
8930       sv->Focus_DO_ID =
8931          SUMA_findVO_inDOv(SUMA_ADO_idcode(ado), SUMAg_DOv, SUMAg_N_DOv);
8932 
8933       /* if the surface controller is open, update it */
8934       if (SUMA_isADO_Cont_Realized(ado))
8935          SUMA_Init_SurfCont_SurfParam(ado);
8936 
8937       SUMA_UpdateViewerTitle(sv);
8938 
8939       /* if the surface controller is open, update it */
8940       if (SUMA_isADO_Cont_Realized(ado))
8941          SUMA_Init_SurfCont_SurfParam(ado);
8942 
8943 
8944       ip = SO->FaceSetDim * MTI->ifacemin;
8945       SUMA_S_Note("Have to decide on what to do here,\n"
8946                   "see equivalent section in SUMA_MarkLineSurfaceIntersect");
8947       SUMA_S_Warn("Weird, coords all zero");
8948       /* print nodes about the closets faceset*/
8949       fprintf(SUMA_STDOUT, "\nvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n");
8950       fprintf(SUMA_STDOUT, "Selected cutplane surface %d .\n"
8951                            "FaceSet %d, Closest Node %d\n",
8952          imin, MTI->ifacemin, MTI->inodemin);
8953       fprintf(SUMA_STDOUT, "Nodes forming closest FaceSet:\n");
8954       fprintf(SUMA_STDOUT, "%d, %d, %d\n", \
8955       SO->FaceSetList[ip], SO->FaceSetList[ip+1],SO->FaceSetList[ip+2]);
8956 
8957       fprintf (SUMA_STDOUT,"Coordinates of Nodes forming closest FaceSet:\n"
8958                            "SO->NodeDim = %d \n", SO->NodeDim);
8959       for (it=0; it < 3; ++it) {
8960 
8961          id = SO->NodeDim * SO->FaceSetList[ip+it];
8962          fprintf(SUMA_STDOUT, "%f, %f, %f\n", SO->NodeList[id],
8963                                                 SO->NodeList[id+1],
8964                                                 SO->NodeList[id+2]);
8965       }
8966       fprintf(SUMA_STDOUT, "\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
8967 
8968 
8969 
8970       /* Now set the cross hair position at the intersection*/
8971       if (!list) list = SUMA_CreateList();
8972       ED = SUMA_InitializeEngineListData (SE_SetCrossHair);
8973       if (!(Location=SUMA_RegisterEngineListCommand (  list, ED,
8974                                              SEF_fv3, (void*)MTI->P,
8975                                              SES_Suma, (void *)sv, NOPE,
8976                                              SEI_Head, NULL))) {
8977          fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
8978          SUMA_RETURN (-1);
8979       }
8980       /* and add the SO with this location, needed for VisX business*/
8981       SUMA_RegisterEngineListCommand (  list, ED,
8982                                         SEF_vp, (void *)SO,
8983                                         SES_Suma, (void *)sv, NOPE,
8984                                         SEI_In, Location);
8985 
8986       if (!SUMA_Engine (&list)) {
8987          fprintf( SUMA_STDERR,
8988                   "Error %s: SUMA_Engine call failed.\n", FuncName);
8989          SUMA_RETURN (-1);
8990       }
8991       /* check to see if AFNI needs to be notified */
8992       /* Need to deal with SUMA_TO_MATLAB_STREAM_INDEX too
8993          Same for remaining occurrence of SUMA_AFNI_STREAM_INDEX*/
8994       if (  SUMAg_CF->Connected_v[SUMA_AFNI_STREAM_INDEX] &&
8995             sv->LinkAfniCrossHair) {
8996          if (LocalHead)
8997             fprintf(SUMA_STDERR,
8998                      "%s: Notifying Afni of CrossHair XYZ\n", FuncName);
8999          /* register a call to SetAfniCrossHair */
9000          if (!list) list = SUMA_CreateList();
9001          it = 0; /* Might want someday: SUMA_ShftCont_Event(PR->evr); */
9002          ED = SUMA_InitializeEngineListData (SE_SetAfniCrossHair);
9003          if (!SUMA_RegisterEngineListCommand (  list, ED,
9004                                              SEF_i, (void*)&it,
9005                                              SES_Suma, (void *)sv, NOPE,
9006                                              SEI_Tail, NULL)) {
9007             SUMA_S_Err("Failed to register element\n");
9008             SUMA_RETURN (-1);
9009          }
9010          if (!SUMA_Engine (&list)) {
9011             fprintf( SUMA_STDERR,
9012                      "Error %s: SUMA_Engine call failed.\n", FuncName);
9013             SUMA_RETURN (-1);
9014          }
9015       }else {
9016          if (LocalHead)
9017             fprintf(SUMA_STDERR,"%s: No Notification to AFNI.\n", FuncName);
9018       }
9019 
9020       /* put in a request for GICOR if need be */
9021       if (  !NodeIgnored &&
9022             SUMAg_CF->Connected_v[SUMA_GICORR_LINE] &&
9023             SUMAg_CF->giset && !SUMAg_CF->HoldClickCallbacks) {
9024          if (LocalHead)
9025             fprintf(SUMA_STDERR,
9026                      "%s: Notifying GICOR of node selection\n", FuncName);
9027          /* register a call to SetGICORnode */
9028          if (!list) list = SUMA_CreateList();
9029          SUMA_REGISTER_TAIL_COMMAND_NO_DATA( list, SE_SetGICORnode,
9030                                              SES_Suma, sv);
9031          if (!SUMA_Engine (&list)) {
9032             fprintf( SUMA_STDERR,
9033                      "Error %s: SUMA_Engine call failed.\n", FuncName);
9034             SUMA_RETURN (-1);
9035          }
9036       }else {
9037          if (LocalHead)
9038             fprintf(SUMA_STDERR,"%s: No Notification to GICOR.\n", FuncName);
9039       }
9040 
9041 
9042 
9043    }
9044    /* clear MTI */
9045    if (MTI) {
9046       MTI = SUMA_Free_MT_intersect_triangle(MTI);
9047    }
9048 
9049    SUMA_free(SOv); SOv = NULL;
9050 
9051    if (imin >= 0) {
9052       SUMA_RETURN (1); /* hit */
9053    } else {
9054       SUMA_RETURN (0); /* no hit */
9055    }
9056 }/* determine intersection with cutplanes*/
9057 
SUMA_MarkLineVOslicesIntersect(SUMA_SurfaceViewer * sv,SUMA_DO * dov,int IgnoreSameNode)9058 int SUMA_MarkLineVOslicesIntersect (SUMA_SurfaceViewer *sv, SUMA_DO *dov,
9059                                     int IgnoreSameNode)
9060 {/* determine intersection */
9061    static char FuncName[]={"SUMA_MarkLineVOslicesIntersect"};
9062    SUMA_PICK_RESULT *PR = NULL;
9063    SUMA_ALL_DO *ado = NULL;
9064    int ans;
9065 
9066    SUMA_ENTRY;
9067    SUMA_S_Warn("Do not call me anymore. Follow the new selection logic");
9068    ans = SUMA_ComputeLineVOslicesIntersect(sv, dov, IgnoreSameNode, &ado);
9069    if (ans <= 0) {
9070       SUMA_RETURN(ans);
9071    }
9072    /* just for temporary testing, get PR back from list and apply it */
9073    PR = SUMA_Get_From_PickResult_List(sv, ado, NULL);
9074    ans = SUMA_Apply_PR(sv, &PR);
9075    SUMA_RETURN(ans);
9076 }
9077 
9078 #if 0
9079 /* BEFORE you start using this MACRO everywhere, including with the,
9080    other ThrMode values, make sure you write a function version of it
9081    which can be used as a sanity check. For now, this seems OK */
9082 #define SUMA_VAL_MEETS_THRESH(val, ThreshRange, ThrMode) (\
9083       ((ThrMode) == SUMA_LESS_THAN && (val) >= ThreshRange[0])?1: \
9084      (((ThrMode) == SUMA_ABS_LESS_THAN && ( (val) >=  ThreshRange[0] ||   \
9085                                             (val) <= -ThreshRange[0]))?1:0) )
9086 #endif
SUMA_Val_Meets_Thresh(float val,double * ThreshRange,SUMA_THRESH_MODE ThrMode)9087 byte SUMA_Val_Meets_Thresh(float val, double *ThreshRange,
9088                            SUMA_THRESH_MODE ThrMode)
9089 {
9090    static char FuncName[]={"SUMA_Val_Meets_Thresh"};
9091    switch(ThrMode){
9092       case SUMA_LESS_THAN:
9093          return((val >= ThreshRange[0]));
9094          break;
9095       case SUMA_ABS_LESS_THAN:
9096          return((val >=  ThreshRange[0]) || (val <=  -ThreshRange[0]));
9097          break;
9098       case SUMA_THRESH_OUTSIDE_RANGE:
9099          return((val <  ThreshRange[0]) || (val > ThreshRange[1]));
9100          break;
9101       case SUMA_THRESH_INSIDE_RANGE:
9102          return((val >=  ThreshRange[0]) && (val <= ThreshRange[1]));
9103          break;
9104       case SUMA_NO_THRESH:
9105          return(1);
9106       default:
9107          SUMA_S_Warn("Bad thresh mode %d", ThrMode);
9108          return(1);
9109          break;
9110    }
9111    SUMA_S_Warn("Should not be here %d", ThrMode);
9112    return(1);
9113 }
9114 
9115 /*
9116    This function is almost identical to SUMA_ComputeLineVOvrIntersect()
9117    They could be merged quite readily but for some reason this feels
9118    cleaner to me.
9119    Make sure that any change here is mirrored verbatim (to the degree possible)
9120    in SUMA_ComputeLineVOvrIntersect() .
9121 
9122    Consider merging SUMA_ComputeLineVOvrIntersect() into
9123    SUMA_ComputeLineVOslicesIntersect() in the future if maintenance is a problem
9124 */
SUMA_ComputeLineVOslicesIntersect(SUMA_SurfaceViewer * sv,SUMA_DO * dov,int IgnoreSameNode,SUMA_ALL_DO ** pado)9125 int SUMA_ComputeLineVOslicesIntersect (SUMA_SurfaceViewer *sv, SUMA_DO *dov,
9126                                        int IgnoreSameNode, SUMA_ALL_DO **pado)
9127 {/* determine intersection */
9128    static char FuncName[]={"SUMA_ComputeLineVOslicesIntersect"};
9129    float P0f[3], P1f[3], pinter[3], I[3];
9130    int NP, N_Hit, okinten=0;
9131    float delta_t_tmp, dmin, val;
9132    struct timeval tt_tmp;
9133    int ip, it, id, ii, imin, I1d, Irw, Hit, ive, icolplane, indef=-1;
9134    int *MembDOs=NULL, N_MembDOs, UseAlphaTresh=1;
9135    float valpha=0.0;
9136    SUMA_DO_Types ttv[12];
9137    char sfield[100], sdestination[100], CommString[SUMA_MAX_COMMAND_LENGTH];
9138    SUMA_VolumeObject *VO=NULL;
9139    SUMA_Boolean NodeIgnored = NOPE;
9140    SUMA_RENDERED_SLICE *rslc;
9141    SUMA_ALL_DO *ado = NULL;
9142    SUMA_DSET *dset = NULL;
9143    SUMA_OVERLAYS *colplane=NULL;
9144    SUMA_VOL_SAUX *VSaux = NULL;
9145    SUMA_PICK_RESULT *PR=NULL;
9146    DListElmt *el=NULL;
9147    SUMA_Boolean LocalHead = NOPE;
9148 
9149    SUMA_ENTRY;
9150 
9151    P0f[0] = sv->Pick0[0];
9152    P0f[1] = sv->Pick0[1];
9153    P0f[2] = sv->Pick0[2];
9154    P1f[0] = sv->Pick1[0];
9155    P1f[1] = sv->Pick1[1];
9156    P1f[2] = sv->Pick1[2];
9157 
9158    ttv[0] = VO_type; ttv[1] = NOT_SET_type;
9159    MembDOs = SUMA_ViewState_Membs(&(sv->VSv[sv->iState]), ttv, &N_MembDOs);
9160    SUMA_LHv("Searching for hit: %f %f %f --> %f %f %f\n",
9161             P0f[0], P0f[1], P0f[2], P1f[0], P1f[1], P1f[2]);
9162    N_Hit = 0;
9163    for (ii=0; ii<N_MembDOs; ++ii) {
9164       {
9165          VO = (SUMA_VolumeObject *)(dov[MembDOs[ii]].OP);
9166          ado = (SUMA_ALL_DO *)VO;
9167          if (!(VSaux = SUMA_ADO_VSaux(ado))) continue;
9168          SUMA_LH("%d slices on %s", dlist_size(VSaux->slcl), ADO_LABEL(ado));
9169          if (!dlist_size(VSaux->slcl)) continue;
9170          /* now compute intersection from the top down */
9171          Hit = 0;
9172          el = NULL;
9173          do {
9174             if (!el) el = dlist_head(VSaux->slcl);
9175             else el = dlist_next(el);
9176             rslc = (SUMA_RENDERED_SLICE *)el->data;
9177             /* does line intersect this plane? */
9178             SUMA_SEGMENT_PLANE_INTERSECT(P0f, P1f, rslc->Eq, Hit, pinter);
9179             if (Hit) {/* is the intersection point in the volume? */
9180                Hit = 0; /* demote, real hit decided on below */
9181                ive = 0;
9182                while (VO->VE && VO->VE[ive]) {
9183                   AFF44_MULT_I(I, VO->VE[ive]->X2I, pinter);
9184                   SUMA_LH("On %s: Inter at X=[%f %f %f] --> ijk=[%f %f %f]",
9185                            SUMA_VE_Headname(VO->VE, ive),
9186                            pinter[0], pinter[1], pinter[2], I[0], I[1], I[2]);
9187                   I[0] = (int)I[0]; I[1] = (int)I[1]; I[2] = (int)I[2];
9188                   if (I[0] >= 0.0f && I[1] >= 0.0f && I[2] >= 0.0f &&
9189                       I[0] < VO->VE[ive]->Ni &&  I[1] < VO->VE[ive]->Nj &&
9190                       I[2] < VO->VE[ive]->Nk) {
9191                       dset = SUMA_VE_dset(VO->VE, ive);
9192                       colplane =  SUMA_Fetch_OverlayPointerByDset(
9193                                           (SUMA_ALL_DO *)VO, dset, &icolplane);
9194                       /* here you check on the value at I in the dataset */
9195                       I1d = I[2]*VO->VE[ive]->Ni*VO->VE[ive]->Nj +
9196                             I[1]*VO->VE[ive]->Ni+I[0];
9197                       Irw = SUMA_GetNodeRow_FromNodeIndex_eng(dset, I1d,-1);
9198                       if (!colplane->V) {
9199                         SUMA_S_Err("Need SUMA_GetDsetValInCol to get vals");
9200                         SUMA_RETURN(NOPE);
9201                       } else {
9202                         val = colplane->V[Irw];
9203                       }
9204                       SUMA_LH("Have intersection on ive %d inside VE %s\n"
9205                               "IJK [%d %d %d], I1d=%d, Irw=%d, \n"
9206                               "val %f, thr [%f %f]\n",
9207                               ive, SUMA_VE_Headname(VO->VE, ive),
9208                               (int)I[0], (int)I[1], (int)I[2],
9209                               I1d, Irw, val,
9210                               colplane->OptScl->ThreshRange[0],
9211                               colplane->OptScl->ThreshRange[1]);
9212 
9213                       /* Do we meet intensity thresholds? */
9214                       okinten = 0;
9215                       if ( SUMA_Val_Meets_Thresh(val,
9216                                     colplane->OptScl->ThreshRange,
9217                                     colplane->OptScl->ThrMode ) &&
9218                            (val != 0.0f || !colplane->OptScl->MaskZero)) {
9219                         okinten = 1;
9220                       }
9221 
9222                       indef = -1;
9223                       UseAlphaTresh = 1;/* control from interface someday */
9224                       valpha = 2.0; /* no masking */
9225                       if (UseAlphaTresh && okinten && colplane->ColAlpha) {
9226                         /* Also mask if value is below alpha thresh
9227                            This is a slow search... So you may not want
9228                            to use it all the time.
9229                            Problem is finding the row of the voxel in
9230                            NodeDef, and that's too slow for a big
9231                            volume to be run repeatedly...Would
9232                            be easier if I had a function to recompute
9233                            a voxel's alpha, rather than search for it
9234                            in ColAlpha. Oh, well, someday I guess. For
9235                            now we search*/
9236                         if ((indef=SUMA_GetSortedNodeOverInd(colplane, I1d))>=0){
9237                            valpha = colplane->ColAlpha[indef]/255.0;
9238                         }
9239                       }
9240 
9241                       if ( okinten && (valpha > colplane->AlphaThresh) ) {
9242                         SUMA_LH("FOUND IT, on VE %s, IJK [%d %d %d], val %f,"
9243                                "thresh[%f %f], UseAlphaTresh = %d, "
9244                                "valpha=%f, ColAlphaThresh=%f\n",
9245                                    SUMA_VE_Headname(VO->VE, ive),
9246                                    (int)I[0], (int)I[1], (int)I[2], val,
9247                                    colplane->OptScl->ThreshRange[0],
9248                                    colplane->OptScl->ThreshRange[1],
9249                                    UseAlphaTresh,
9250                                    valpha, colplane->AlphaThresh*255);
9251                            PR = SUMA_New_Pick_Result(NULL);
9252                            PR->ado_idcode_str = SUMA_replace_string(
9253                                            PR->ado_idcode_str, ADO_ID(ado));
9254                            if (pado) *pado = ado; /* user wants it */
9255                            PR->primitive = SUMA_replace_string(
9256                                                          PR->primitive,"voxel");
9257                            PR->primitive_index = -1;
9258                            PR->PickXYZ[0] = pinter[0];
9259                            PR->PickXYZ[1] = pinter[1];
9260                            PR->PickXYZ[2] = pinter[2];
9261                            PR->ignore_same_datum = IgnoreSameNode;
9262                            PR->datum_index = I1d;
9263                            PR->iAltSel[SUMA_VOL_I] = I[0];
9264                            PR->iAltSel[SUMA_VOL_J] = I[1];
9265                            PR->iAltSel[SUMA_VOL_K] = I[2];
9266                            PR->iAltSel[SUMA_VOL_IJK] = I1d;
9267                            PR->iAltSel[SUMA_VOL_SLC_NUM] = rslc->slc_num;
9268                            PR->iAltSel[SUMA_VOL_SLC_VARIANT] =
9269                                  (int)SUMA_SlcVariantToCode(rslc->variant);
9270                            PR->dAltSel[SUMA_VOL_SLC_EQ0] = rslc->Eq[0];
9271                            PR->dAltSel[SUMA_VOL_SLC_EQ1] = rslc->Eq[1];
9272                            PR->dAltSel[SUMA_VOL_SLC_EQ2] = rslc->Eq[2];
9273                            PR->dAltSel[SUMA_VOL_SLC_EQ3] = rslc->Eq[3];
9274                            PR->dset_idcode_str = SUMA_replace_string(
9275                                            PR->dset_idcode_str, SDSET_ID(dset));
9276                            if (!SUMA_Add_To_PickResult_List(sv, ado,
9277                                                             "voxel", &PR)) {
9278                               SUMA_S_Err("Failed to add selected ado");
9279                               SUMA_RETURN(0);
9280                            }
9281                            Hit = 1;
9282                            ++N_Hit;
9283                            /* You could leave at the first hit IF:
9284                            you only have one direction of slices in the
9285                            entire stack, AND if they are properly
9286                            ordered for rendering.
9287                            Should speed be an issue you can check for
9288                            this condition and bolt with the line below */
9289                            /* goto GOT_IT; */
9290                       }
9291                   }
9292                   ++ive;
9293                }
9294             }
9295             SUMA_LH("el now %p,\n"
9296                     "tail = %p, N_Hit = %d",
9297                     el, dlist_tail(VSaux->slcl), N_Hit);
9298          } while(el != dlist_tail(VSaux->slcl));
9299       }
9300    }
9301 
9302 
9303    GOT_IT:
9304    SUMA_RETURN(N_Hit);
9305 }/* determine intersection with slices of VO*/
9306 
9307 /*
9308    This function is almost identical to SUMA_ComputeLineVOslicesIntersect()
9309    They could be merged quite readily but for some reason this feels
9310    cleaner to me.
9311    Make sure that any change here is mirrored verbatim (to the degree possible)
9312    in SUMA_ComputeLineVOslicesIntersect() .
9313 
9314    Consider merging SUMA_ComputeLineVOvrIntersect() into
9315    SUMA_ComputeLineVOslicesIntersect() in the future if maintenance is a problem
9316 
9317 
9318 */
SUMA_ComputeLineVOvrIntersect(SUMA_SurfaceViewer * sv,SUMA_DO * dov,int IgnoreSameNode,SUMA_ALL_DO ** pado)9319 int SUMA_ComputeLineVOvrIntersect (SUMA_SurfaceViewer *sv, SUMA_DO *dov,
9320                                    int IgnoreSameNode, SUMA_ALL_DO **pado)
9321 {/* determine intersection */
9322    static char FuncName[]={"SUMA_ComputeLineVOvrIntersect"};
9323    float P0f[3], P1f[3], pinter[3], I[3];
9324    int NP, N_Hit, okinten=0;
9325    float delta_t_tmp, dmin, val;
9326    struct timeval tt_tmp;
9327    int ip, it, id, ii, imin, I1d, Irw, Hit, ive, icolplane, indef=-1;
9328    int *MembDOs=NULL, N_MembDOs, UseAlphaTresh=1;
9329    float valpha=0.0;
9330    SUMA_DO_Types ttv[12];
9331    char sfield[100], sdestination[100], CommString[SUMA_MAX_COMMAND_LENGTH];
9332    SUMA_VolumeObject *VO=NULL;
9333    SUMA_Boolean NodeIgnored = NOPE;
9334    SUMA_RENDERED_SLICE *rslc;
9335    SUMA_ALL_DO *ado = NULL;
9336    SUMA_DSET *dset = NULL;
9337    SUMA_OVERLAYS *colplane=NULL;
9338    SUMA_VOL_SAUX *VSaux = NULL;
9339    SUMA_PICK_RESULT *PR=NULL;
9340    DListElmt *el=NULL;
9341    SUMA_Boolean LocalHead = NOPE;
9342 
9343    SUMA_ENTRY;
9344 
9345    P0f[0] = sv->Pick0[0];
9346    P0f[1] = sv->Pick0[1];
9347    P0f[2] = sv->Pick0[2];
9348    P1f[0] = sv->Pick1[0];
9349    P1f[1] = sv->Pick1[1];
9350    P1f[2] = sv->Pick1[2];
9351 
9352    ttv[0] = VO_type; ttv[1] = NOT_SET_type;
9353    MembDOs = SUMA_ViewState_Membs(&(sv->VSv[sv->iState]), ttv, &N_MembDOs);
9354    SUMA_LHv("Searching for hit: %f %f %f --> %f %f %f\n",
9355             P0f[0], P0f[1], P0f[2], P1f[0], P1f[1], P1f[2]);
9356    N_Hit = 0;
9357    for (ii=0; ii<N_MembDOs; ++ii) {
9358       {
9359          VO = (SUMA_VolumeObject *)(dov[MembDOs[ii]].OP);
9360          ado = (SUMA_ALL_DO *)VO;
9361          if (!(VSaux = SUMA_ADO_VSaux(ado))) continue;
9362          SUMA_LH("%d VR slices on %s, show=%d, VrSelect=%d",
9363                               dlist_size(VSaux->vrslcl),
9364                               ADO_LABEL(ado), VSaux->ShowVrSlc,
9365                               VSaux->VrSelect);
9366          if (!VSaux->ShowVrSlc || !VSaux->VrSelect) continue;
9367          if (!dlist_size(VSaux->vrslcl)) continue;
9368          /* now compute intersection from the top down */
9369          Hit = 0;
9370          el = NULL;
9371          do {
9372             if (!el) el = dlist_head(VSaux->vrslcl);
9373             else el = dlist_next(el);
9374             rslc = (SUMA_RENDERED_SLICE *)el->data;
9375             /* does line intersect this plane? */
9376             SUMA_SEGMENT_PLANE_INTERSECT(P0f, P1f, rslc->Eq, Hit, pinter);
9377             if (Hit) {/* is the intersection point in the volume? */
9378                Hit = 0; /* demote, real hit decided on below */
9379                ive = 0;
9380                while (VO->VE && VO->VE[ive]) {
9381                   AFF44_MULT_I(I, VO->VE[ive]->X2I, pinter);
9382                   SUMA_LH("On %s: Inter at X=[%f %f %f] --> ijk=[%f %f %f]",
9383                            SUMA_VE_Headname(VO->VE, ive),
9384                            pinter[0], pinter[1], pinter[2], I[0], I[1], I[2]);
9385                   I[0] = (int)I[0]; I[1] = (int)I[1]; I[2] = (int)I[2];
9386                   if (I[0] >= 0.0f && I[1] >= 0.0f && I[2] >= 0.0f &&
9387                       I[0] < VO->VE[ive]->Ni &&  I[1] < VO->VE[ive]->Nj &&
9388                       I[2] < VO->VE[ive]->Nk) {
9389                       dset = SUMA_VE_dset(VO->VE, ive);
9390                       colplane =  SUMA_Fetch_OverlayPointerByDset(
9391                                           (SUMA_ALL_DO *)VO, dset, &icolplane);
9392                       /* here you check on the value at I in the dataset */
9393                       I1d = I[2]*VO->VE[ive]->Ni*VO->VE[ive]->Nj +
9394                             I[1]*VO->VE[ive]->Ni+I[0];
9395                       Irw = SUMA_GetNodeRow_FromNodeIndex_eng(dset, I1d,-1);
9396                       if (!colplane->V) {
9397                         SUMA_S_Err("Need SUMA_GetDsetValInCol to get vals");
9398                         SUMA_RETURN(NOPE);
9399                       } else {
9400                         val = colplane->V[Irw];
9401                       }
9402                       SUMA_LH("Have intersection on ive %d inside VE %s\n"
9403                               "IJK [%d %d %d], I1d=%d, Irw=%d, \n"
9404                               "val %f, thr [%f %f]\n",
9405                               ive, SUMA_VE_Headname(VO->VE, ive),
9406                               (int)I[0], (int)I[1], (int)I[2],
9407                               I1d, Irw, val,
9408                               colplane->OptScl->ThreshRange[0],
9409                               colplane->OptScl->ThreshRange[1]);
9410 
9411                       /* Do we meet intensity thresholds? */
9412                       okinten = 0;
9413                       if ( SUMA_Val_Meets_Thresh(val,
9414                                     colplane->OptScl->ThreshRange,
9415                                     colplane->OptScl->ThrMode ) &&
9416                            (val != 0.0f || !colplane->OptScl->MaskZero)) {
9417                         okinten = 1;
9418                       }
9419 
9420                       indef = -1;
9421                       UseAlphaTresh = 1;/* control from interface someday */
9422                       valpha = 2.0; /* no masking */
9423                       if (UseAlphaTresh && okinten && colplane->ColAlpha) {
9424                         /* Also mask if value is below alpha thresh
9425                            This is a slow search... So you may not want
9426                            to use it all the time.
9427                            Problem is finding the row of the voxel in
9428                            NodeDef, and that's too slow for a big
9429                            volume to be run repeatedly...Would
9430                            be easier if I had a function to recompute
9431                            a voxel's alpha, rather than search for it
9432                            in ColAlpha. Oh, well, someday I guess. For
9433                            now we search*/
9434                         if ((indef=SUMA_GetSortedNodeOverInd(colplane, I1d))>=0){
9435                            valpha = colplane->ColAlpha[indef]/255.0;
9436                         }
9437                       }
9438 
9439                       if ( okinten && (valpha > colplane->AlphaThresh) ) {
9440                         SUMA_LH("FOUND IT, on VE %s, IJK [%d %d %d], val %f,"
9441                                "thresh[%f %f], UseAlphaTresh = %d, "
9442                                "valpha=%f, ColAlphaThresh=%f\n",
9443                                    SUMA_VE_Headname(VO->VE, ive),
9444                                    (int)I[0], (int)I[1], (int)I[2], val,
9445                                    colplane->OptScl->ThreshRange[0],
9446                                    colplane->OptScl->ThreshRange[1],
9447                                    UseAlphaTresh,
9448                                    valpha, colplane->AlphaThresh*255);
9449                            PR = SUMA_New_Pick_Result(NULL);
9450                            PR->ado_idcode_str = SUMA_replace_string(
9451                                            PR->ado_idcode_str, ADO_ID(ado));
9452                            if (pado) *pado = ado; /* user wants it */
9453                            PR->primitive = SUMA_replace_string(
9454                                                          PR->primitive,"voxel");
9455                            PR->primitive_index = -1;
9456                            PR->PickXYZ[0] = pinter[0];
9457                            PR->PickXYZ[1] = pinter[1];
9458                            PR->PickXYZ[2] = pinter[2];
9459                            PR->ignore_same_datum = IgnoreSameNode;
9460                            PR->datum_index = I1d;
9461                            PR->iAltSel[SUMA_VOL_I] = I[0];
9462                            PR->iAltSel[SUMA_VOL_J] = I[1];
9463                            PR->iAltSel[SUMA_VOL_K] = I[2];
9464                            PR->iAltSel[SUMA_VOL_IJK] = I1d;
9465                            PR->iAltSel[SUMA_VOL_SLC_NUM] = rslc->slc_num;
9466                            PR->iAltSel[SUMA_VOL_SLC_VARIANT] =
9467                                  (int)SUMA_SlcVariantToCode(rslc->variant);
9468                            PR->dAltSel[SUMA_VOL_SLC_EQ0] = rslc->Eq[0];
9469                            PR->dAltSel[SUMA_VOL_SLC_EQ1] = rslc->Eq[1];
9470                            PR->dAltSel[SUMA_VOL_SLC_EQ2] = rslc->Eq[2];
9471                            PR->dAltSel[SUMA_VOL_SLC_EQ3] = rslc->Eq[3];
9472                            PR->dset_idcode_str = SUMA_replace_string(
9473                                            PR->dset_idcode_str, SDSET_ID(dset));
9474                            if (!SUMA_Add_To_PickResult_List(sv, ado,
9475                                                             "voxel", &PR)) {
9476                               SUMA_S_Err("Failed to add selected ado");
9477                               SUMA_RETURN(0);
9478                            }
9479                            Hit = 1;
9480                            ++N_Hit;
9481                            /* You could leave at the first hit IF:
9482                            you only have one direction of slices in the
9483                            entire stack, AND if they are properly
9484                            ordered for rendering.
9485                            Should speed be an issue you can check for
9486                            this condition and bolt with the line below */
9487                            /* goto GOT_IT; */
9488                       }
9489                   }
9490                   ++ive;
9491                }
9492             }
9493             SUMA_LH("el now %p,\n"
9494                     "tail = %p, N_Hit = %d",
9495                     el, dlist_tail(VSaux->vrslcl), N_Hit);
9496          } while(el != dlist_tail(VSaux->vrslcl));
9497       }
9498    }
9499 
9500 
9501    GOT_IT:
9502    SUMA_RETURN(N_Hit);
9503 }/* determine intersection with 3D rendering of VO*/
9504 
SUMA_Apply_PR_VO(SUMA_SurfaceViewer * sv,SUMA_VolumeObject * VO,SUMA_PICK_RESULT ** PRi)9505 int SUMA_Apply_PR_VO(SUMA_SurfaceViewer *sv, SUMA_VolumeObject *VO,
9506                      SUMA_PICK_RESULT **PRi)
9507 {
9508    static char FuncName[]={"SUMA_Apply_PR_VO"};
9509    SUMA_ALL_DO *ado=NULL;
9510    int iv3[3], iv15[15];
9511    float fv15[15];
9512    DList *list = NULL;
9513    SUMA_Boolean NodeIgnored = NOPE;
9514    SUMA_PICK_RESULT *PR;
9515    SUMA_EngineData *ED = NULL;
9516    SUMA_DSET *dset=NULL;
9517    DListElmt *Location=NULL, *el=NULL, *SetNodeElem = NULL;
9518    SUMA_Boolean LocalHead = NOPE;
9519 
9520    SUMA_ENTRY;
9521 
9522    SUMA_LH("Here");
9523    if (!sv || !VO || !PRi || !*PRi) { SUMA_S_Err("Niente"); SUMA_RETURN(-1); }
9524 
9525    /* Mark intersection Facsets */
9526    ado = (SUMA_ALL_DO *)VO;
9527 
9528    PR = *PRi;   /* Keep local copy */
9529    /* Store the PR in ado, hide it from return potential */
9530    SUMA_ADO_StorePickResult(ado, PRi);
9531 
9532    if (!(dset = SUMA_FindDset_s(PR->dset_idcode_str, SUMAg_CF->DsetList))) {
9533       SUMA_S_Err("NULL dset?");
9534       SUMA_RETURN(0);
9535    }
9536 
9537    sv->Focus_DO_ID = ADO_iDO(ado);
9538    SUMA_UpdateViewerTitle(sv);
9539 
9540    /* if the surface controller is open, update it */
9541    if (SUMA_isADO_Cont_Realized(ado))
9542       SUMA_Init_SurfCont_SurfParam(ado);
9543 
9544    fprintf(SUMA_STDOUT, "\nvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n");
9545    fprintf(SUMA_STDOUT, "Selected voxel RAI [%.3f %.3f %.3f]mm \n"
9546                         "               IJK [%ld %ld %ld] on volume %s.\n",
9547       PR->PickXYZ[0], PR->PickXYZ[1], PR->PickXYZ[2],
9548       PR->iAltSel[SUMA_VOL_I], PR->iAltSel[SUMA_VOL_J], PR->iAltSel[SUMA_VOL_K],
9549       SDSET_FILENAME(dset));
9550    fprintf(SUMA_STDOUT, "\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
9551 
9552 
9553    /* Set the voxel selection */
9554    if (!list) list = SUMA_CreateList();
9555    if (PR->ignore_same_datum &&
9556         SUMA_ADO_SelectedDatum(ado, NULL, NULL) == PR->datum_index) {
9557       SUMA_LHv("Ignoring identical voxel selection %d on volume %s\n",
9558                SUMA_ADO_SelectedDatum(ado, NULL, NULL), SUMA_ADO_Label(ado));
9559       NodeIgnored = YUP;
9560    } else {
9561       ED = SUMA_InitializeEngineListData (SE_SetSelectedNode);
9562       SetNodeElem = SUMA_RegisterEngineListCommand (  list, ED,
9563                                              SEF_i, (void*)&PR->datum_index,
9564                                              SES_Suma, (void *)sv, NOPE,
9565                                              SEI_Head, NULL);
9566       if (!SetNodeElem) {
9567          fprintf( SUMA_STDERR,
9568                   "Error %s: Failed to register SetNodeElem\n", FuncName);
9569          SUMA_RETURN (-1);
9570       } else {
9571          SUMA_RegisterEngineListCommand (  list, ED,
9572                                            SEF_ngr, NULL,
9573                                            SES_Suma, (void *)sv, NOPE,
9574                                            SEI_In, SetNodeElem);
9575       }
9576 
9577       iv15[SUMA_VOL_I] = (int)PR->iAltSel[SUMA_VOL_I];
9578       iv15[SUMA_VOL_J] = (int)PR->iAltSel[SUMA_VOL_J];
9579       iv15[SUMA_VOL_K] = (int)PR->iAltSel[SUMA_VOL_K];
9580       iv15[SUMA_VOL_IJK] = (int)PR->iAltSel[SUMA_VOL_IJK];
9581 
9582       fv15[SUMA_VOL_SLC_EQ0] = (float)PR->dAltSel[SUMA_VOL_SLC_EQ0];
9583       fv15[SUMA_VOL_SLC_EQ1] = (float)PR->dAltSel[SUMA_VOL_SLC_EQ1];
9584       fv15[SUMA_VOL_SLC_EQ2] = (float)PR->dAltSel[SUMA_VOL_SLC_EQ2];
9585       fv15[SUMA_VOL_SLC_EQ3] = (float)PR->dAltSel[SUMA_VOL_SLC_EQ3];
9586 
9587       SUMA_RegisterEngineListCommand (  list, ED,
9588                                         SEF_iv15, (void *)iv15,
9589                                         SES_Suma, (void *)sv, NOPE,
9590                                         SEI_In, SetNodeElem);
9591       SUMA_RegisterEngineListCommand (  list, ED,
9592                                         SEF_fv15, (void *)fv15,
9593                                         SES_Suma, (void *)sv, NOPE,
9594                                         SEI_In, SetNodeElem);
9595    }
9596 
9597 
9598    /* Now set the cross hair position at the selected node*/
9599    if (!list) list = SUMA_CreateList();
9600    ED = SUMA_InitializeEngineListData (SE_SetCrossHair);
9601    if (!(Location=SUMA_RegisterEngineListCommand (  list, ED,
9602                                           SEF_fv3, (void*)PR->PickXYZ,
9603                                           SES_Suma, (void *)sv, NOPE,
9604                                           SEI_Head, NULL))) {
9605       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
9606       SUMA_RETURN(-1);
9607    }
9608    /* and add the object with this location */
9609    SUMA_RegisterEngineListCommand (  list, ED,
9610                                      SEF_vp, (void *)dset,
9611                                      SES_Suma, (void *)sv, NOPE,
9612                                      SEI_In, Location);
9613 
9614    /* attach the cross hair to the selected object
9615    Note that binding here is to voxel of dset */
9616    iv3[0] = ADO_iDO(ado);
9617    iv3[1] = PR->iAltSel[SUMA_VOL_IJK];
9618    iv3[2] = -1;
9619 
9620    ED = SUMA_InitializeEngineListData (SE_BindCrossHair);
9621    if (!SUMA_RegisterEngineListCommand (  list, ED,
9622                                           SEF_iv3, (void*)iv3,
9623                                           SES_Suma, (void *)sv, NOPE,
9624                                           SEI_Head, NULL)) {
9625       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
9626       SUMA_RETURN(-1);
9627    }
9628 
9629    /* call with the list */
9630    if (!SUMA_Engine (&list)) {
9631       fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
9632       SUMA_RETURN(-1);
9633    }
9634 
9635    /* check to see if AFNI needs to be notified */
9636    if (SUMAg_CF->Connected_v[SUMA_AFNI_STREAM_INDEX] &&
9637        sv->LinkAfniCrossHair) {
9638       int it;
9639       SUMA_LH("Notifying Afni of CrossHair XYZ");
9640       /* register a call to SetAfniCrossHair */
9641       if (!list) list = SUMA_CreateList();
9642       it = SUMA_ShftCont_Event(PR->evr);
9643       ED = SUMA_InitializeEngineListData (SE_SetAfniCrossHair);
9644       if (!SUMA_RegisterEngineListCommand (  list, ED,
9645                                           SEF_i, (void*)&it,
9646                                           SES_Suma, (void *)sv, NOPE,
9647                                           SEI_Tail, NULL)) {
9648          fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
9649          SUMA_RETURN (-1);
9650       }
9651       if (MASK_MANIP_MODE(sv) && SUMAg_CF->Dev) {
9652          SUMA_ALL_DO *ado = SUMA_whichADOg(sv->MouseMode_ado_idcode_str);
9653          DListElmt *Location=NULL;
9654          if (ado && ado->do_type == MASK_type) {
9655             SUMA_MaskDO *mdo = (SUMA_MaskDO *)ado;
9656             ED = SUMA_InitializeEngineListData (SE_SetAfniMask);
9657             if (!(Location=SUMA_RegisterEngineListCommand (  list, ED,
9658                                                 SEF_fv3, (void*)mdo->cen,
9659                                                 SES_Suma, (void *)sv, NOPE,
9660                                                 SEI_Tail, NULL))) {
9661                SUMA_S_Err("Failed to register element\n");
9662                SUMA_RETURN (-1);
9663             }
9664             SUMA_RegisterEngineListCommand (  list, ED,
9665                                            SEF_s, (void *)(ADO_ID(ado)),
9666                                            SES_Suma, (void *)sv, NOPE,
9667                                            SEI_In, Location);
9668          }
9669       }
9670       if (!SUMA_Engine (&list)) {
9671          fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
9672          SUMA_RETURN(-1);
9673       }
9674    }
9675 
9676    SUMA_RETURN(1);
9677 }
9678 
9679 /*!
9680    \brief Show the contents of a brush stroke
9681    SUMA_ShowBrushStroke (sv, Out);
9682 
9683 */
SUMA_ShowBrushStroke(SUMA_SurfaceViewer * sv,FILE * out)9684 void SUMA_ShowBrushStroke (SUMA_SurfaceViewer *sv, FILE *out)
9685 {
9686    static char FuncName[]={"SUMA_ShowBrushStroke"};
9687    int i, k, N=0;
9688    SUMA_BRUSH_STROKE_DATUM *bsd=NULL;
9689    DListElmt *Next_Elm = NULL;
9690 
9691    SUMA_ENTRY;
9692 
9693    if (!out) out = SUMA_STDERR;
9694 
9695    if (!sv->BS) {
9696       fprintf(out, "%s: NULL sv->BS\n", FuncName);
9697       SUMA_RETURNe;
9698    }
9699 
9700    N = dlist_size(sv->BS);
9701    if (!N) {
9702       fprintf(out, "%s: Empty sv->BS. (N = 0)\n", FuncName);
9703       SUMA_RETURNe;
9704    }
9705 
9706    fprintf(out, "%s: Brush stroke has %d elements:\n", FuncName, N);
9707 
9708    i = 0;
9709    do {
9710       if (Next_Elm==NULL) Next_Elm = dlist_head(sv->BS);
9711       else Next_Elm = Next_Elm->next;
9712       if (!Next_Elm->data) {
9713          fprintf(out, "%s: Element->data %d is NULL!\n", FuncName, i);
9714       }else {
9715          bsd = (SUMA_BRUSH_STROKE_DATUM *)Next_Elm->data;
9716          fprintf(out, "%d: (%f %f) [%.2f, %.2f, %.2f <--> %.2f, %.2f, %.2f]\t Node  %d, Tri %d\n",
9717                      i, bsd->x, bsd->y,
9718                      bsd->NP[0], bsd->NP[1], bsd->NP[2],
9719                      bsd->FP[0], bsd->FP[1], bsd->FP[2],
9720                      bsd->SurfNode, bsd->SurfTri);
9721       }
9722       ++i;
9723    }while (dlist_tail(sv->BS) != Next_Elm);
9724 
9725    fprintf(out, "\n");
9726 
9727    SUMA_RETURNe;
9728 }
9729 
9730 /*!
9731    \brief Clear the contents of sv->BS and sets it to NULL
9732 
9733    SUMA_ClearBrushStroke (sv);
9734 
9735 
9736    \sa SUMA_CreateBrushStroke
9737 */
SUMA_ClearBrushStroke(SUMA_SurfaceViewer * sv)9738 void  SUMA_ClearBrushStroke (SUMA_SurfaceViewer *sv)
9739 {
9740    static char FuncName[]={"SUMA_ClearBrushStroke"};
9741 
9742    SUMA_ENTRY;
9743 
9744    /* THE NEW VERSION */
9745    if (sv->BS) {
9746       SUMA_EmptyDestroyList(sv->BS);
9747       sv->BS = NULL;
9748    }
9749 
9750    SUMA_RETURNe;
9751 }
9752 /*!
9753    \brief Creates the BrushStroke structure inside sv structure
9754    success = SUMA_CreateBrushStroke (sv);
9755 
9756    \param sv (SUMA_SurfaceViewer *) Surface viewer structure
9757    \return YUP/NOPE
9758 
9759    sv->BS must be null before this function is called.
9760    The liat and its components are then allocated for.
9761 
9762    \sa SUMA_ClearBrushStroke
9763 */
SUMA_CreateBrushStroke(SUMA_SurfaceViewer * sv)9764 SUMA_Boolean  SUMA_CreateBrushStroke (SUMA_SurfaceViewer *sv)
9765 {
9766    static char FuncName[]={"SUMA_CreateBrushStroke"};
9767 
9768    SUMA_ENTRY;
9769 
9770    /* New Version */
9771    if (sv->BS) {  /* bad news, this should be NULL to begin with */
9772       SUMA_RegisterMessage (SUMAg_CF->MessageList,
9773                             "Brush Stroke not NULL.", FuncName,
9774                             SMT_Critical, SMA_LogAndPopup);
9775       SUMA_RETURN(NOPE);
9776 
9777    }
9778    sv->BS = (DList *)SUMA_calloc(1,sizeof(DList));
9779    dlist_init(sv->BS, SUMA_FreeBSDatum);
9780 
9781    SUMA_RETURN (YUP);
9782 }
9783 
SUMA_CreateBSDatum(void)9784 SUMA_BRUSH_STROKE_DATUM * SUMA_CreateBSDatum(void)
9785 {
9786    static char FuncName[]={"SUMA_CreateBSDatum"};
9787    SUMA_BRUSH_STROKE_DATUM *bsd = NULL;
9788 
9789    SUMA_ENTRY;
9790 
9791    bsd = (SUMA_BRUSH_STROKE_DATUM *)
9792             SUMA_calloc(1,sizeof(SUMA_BRUSH_STROKE_DATUM));
9793    if (!bsd) {
9794       SUMA_RegisterMessage (SUMAg_CF->MessageList,
9795                             "Failed to allocate.", FuncName,
9796                             SMT_Critical, SMA_LogAndPopup);
9797       SUMA_RETURN(NULL);
9798    }
9799    /* setup defaults */
9800    bsd->x = bsd->y = 0.0;
9801    bsd->NP[0] = bsd->NP[1] = bsd->NP[2] = 0.0;
9802    bsd->FP[0] = bsd->FP[1] = bsd->FP[2] = 0.0;
9803    bsd->SurfNode = -1;
9804    bsd->SurfTri = -1;
9805    bsd->Decimated = NOPE;
9806 
9807    SUMA_RETURN(bsd);
9808 }
9809 
9810 /*!
9811    \brief free a brush stroke datum that is contained inside the doubly linked BS
9812 */
SUMA_FreeBSDatum(void * bsd)9813 void SUMA_FreeBSDatum (void *bsd)
9814 {
9815    static char FuncName[]={"SUMA_FreeBSDatum"};
9816 
9817    SUMA_ENTRY;
9818 
9819    /* nothing is allocated for inside bsd */
9820    if (bsd) SUMA_free(bsd);
9821 
9822    SUMA_RETURNe;
9823 }
9824 
9825 
9826 
9827 /*!
9828    \brief Adds, new point to the brush stroke
9829    success = SUMA_AddToBrushStroke ( sv,  x,  y, Show);
9830 
9831    \param sv (SUMA_SurfaceViewer *) pointer to surface viewer where stroke is occuring
9832    \param x (int) X coordinate of mouse
9833    \param y (int) Y coordinate of mouse
9834    \param NP (GLdouble *) vector of XYZ coordinates of Near Plane intersection point
9835    \param FP (GLdouble *) vector of XYZ coordinates of Far Plane intersection point.
9836    \param Show (SUMA_Boolean) if YUP: Then trace is drawn as you move the mouse
9837    \return YUP/NOPE, success indicator
9838 
9839 */
SUMA_AddToBrushStroke(SUMA_SurfaceViewer * sv,int x,int y,GLdouble * NP,GLdouble * FP,SUMA_Boolean Show)9840 SUMA_Boolean  SUMA_AddToBrushStroke (SUMA_SurfaceViewer *sv, int x, int y, GLdouble *NP, GLdouble *FP, SUMA_Boolean Show)
9841 {
9842    static char FuncName[]={"SUMA_AddToBrushStroke"};
9843    int ip;
9844    SUMA_BRUSH_STROKE_DATUM *bsd=NULL;
9845 
9846    SUMA_ENTRY;
9847 
9848    /* New version */
9849    bsd = SUMA_CreateBSDatum();
9850    bsd->x = (float)x;
9851    bsd->y = (float)y;
9852    bsd->NP[0] = NP[0]; bsd->NP[1] = NP[1]; bsd->NP[2] = NP[2];
9853    bsd->FP[0] = FP[0]; bsd->FP[1] = FP[1]; bsd->FP[2] = FP[2];
9854    dlist_ins_next (sv->BS, dlist_tail(sv->BS), (void*)bsd);
9855 
9856    /* incremental draw */
9857    if (Show) SUMA_DrawBrushStroke (sv, YUP);
9858 
9859    SUMA_RETURN (YUP);
9860 }
9861 
9862 /*!
9863    Sets the foreground color of the drawing area
9864 */
SUMA_SetSVForegroundColor(SUMA_SurfaceViewer * sv,const char * Color)9865 void SUMA_SetSVForegroundColor (SUMA_SurfaceViewer *sv, const char *Color)
9866 {
9867    static char FuncName[]={"SUMA_SetSVForegroundColor"};
9868    XColor col, unused;
9869 
9870    SUMA_ENTRY;
9871 
9872    #ifdef DARWIN
9873       SUMA_S_Warn("Calling this function from OS X seems to cause trouble");
9874    #endif
9875 
9876    /* using sv->X->CMAP instead of
9877 	         DefaultColormapOfScreen(XtScreen(sv->X->GLXAREA))
9878 		is useless */
9879    if (!XAllocNamedColor (sv->X->DPY,
9880                DefaultColormapOfScreen(XtScreen(sv->X->GLXAREA)),
9881                Color, &col, &unused)) {
9882       fprintf (SUMA_STDERR,
9883             "Error %s: Can't allocate for %s color.\n", FuncName, Color);
9884       SUMA_RETURNe;
9885    }
9886    XSetForeground (sv->X->DPY, sv->X->gc, col.pixel);
9887 
9888    SUMA_RETURNe;
9889 }
9890 
9891 /*!
9892    \brief Draws the brushstroke
9893 
9894    \param sv (SUMA_SurfaceViewer *) pointer to surface viewer structure
9895    \param incremental (SUMA_Boolean) YUP: draw a line between the last two points
9896                                      NOPE: draw the whole thing
9897 
9898 	- NB: This function used to crash when run on SGI if display is not
9899    in TrueColor mode.
9900 	This happens even though the visual chosen by SUMA does not change.
9901 	To put the SGI in true color mode, you need to add to /var/X11/xdm/Xservers
9902 	the following:  -class TrueColor -depth 24
9903 	and then restart X or the system.
9904    The bug was that the graphics context (sv->X->gc) was created using the
9905    Screen's root window and not the GLX visual's window.
9906 */
SUMA_DrawBrushStroke(SUMA_SurfaceViewer * sv,SUMA_Boolean incr)9907 void SUMA_DrawBrushStroke (SUMA_SurfaceViewer *sv, SUMA_Boolean incr)
9908 {
9909    static char FuncName[]={"SUMA_DrawBrushStroke"};
9910    int i, N;
9911    DListElmt *NE=NULL, *NEn=NULL;
9912    SUMA_BRUSH_STROKE_DATUM *bsd=NULL, *bsdn = NULL;
9913 
9914    SUMA_ENTRY;
9915 
9916 	if (!sv->BS) SUMA_RETURNe;
9917 
9918    N = dlist_size(sv->BS);
9919    if (N < 2) SUMA_RETURNe;
9920 
9921    if (!incr) {
9922       do {
9923          if (!NE) NE = dlist_head(sv->BS);
9924          else NE = NE->next;
9925 
9926          NEn = NE->next;
9927 
9928          bsd = (SUMA_BRUSH_STROKE_DATUM *)NE->data;
9929          bsdn = (SUMA_BRUSH_STROKE_DATUM *)NEn->data;
9930 
9931          SUMA_DrawWindowLine( sv, (int)bsd->x, (int)bsd->y,
9932                               (int)bsdn->x, (int)bsdn->y, 1);
9933       } while (NEn != dlist_tail(sv->BS));
9934 
9935    } else {
9936       NEn = dlist_tail(sv->BS);
9937       NE = NEn->prev;
9938 
9939       bsd = (SUMA_BRUSH_STROKE_DATUM *)NE->data;
9940       bsdn = (SUMA_BRUSH_STROKE_DATUM *)NEn->data;
9941 
9942       SUMA_DrawWindowLine( sv,
9943                            (int)bsd->x, (int)bsd->y,
9944                            (int)bsdn->x, (int)bsdn->y, 1 );
9945 
9946    }
9947    SUMA_RETURNe;
9948 
9949 }
9950 
9951 /*!
9952    \brief Processes the brushstroke sent from a viewer
9953 
9954 */
SUMA_ProcessBrushStroke(SUMA_SurfaceViewer * sv,SUMA_BRUSH_STROKE_ACTION BsA)9955 SUMA_DRAWN_ROI * SUMA_ProcessBrushStroke
9956                   (SUMA_SurfaceViewer *sv, SUMA_BRUSH_STROKE_ACTION BsA)
9957 {
9958    static char FuncName[]={"SUMA_ProcessBrushStroke"};
9959    SUMA_DRAWN_ROI *DrawnROI = NULL;
9960    SUMA_ROI_DATUM *ROIstroke = NULL, *ROIlink=NULL, *ROIfill=NULL;
9961    SUMA_SurfaceObject *SO = NULL;
9962    int ii=0, TailNode = -1, FirstSurfNode = -1, ft = -1, N_SurfNode = 0;
9963    int HeadNode = -1, *ROI_Mask=NULL, N_ROI_Mask = 0;
9964    DListElmt *El = NULL;
9965    SUMA_BRUSH_STROKE_DATUM *bsd=NULL;
9966    char *sbuf;
9967    SUMA_ROI_ACTION_STRUCT *ROIA;
9968    DListElmt *tmpStackPos=NULL;
9969    SUMA_Boolean Shaded = NOPE, LocalHead = NOPE;
9970 
9971    SUMA_ENTRY;
9972 
9973    SO = SUMA_SV_Focus_SO(sv);
9974 
9975    if (!SO) {
9976       fprintf (SUMA_STDERR,
9977                "%s: No surface object in focus, nothing to do.\n", FuncName);
9978       SUMA_RETURN (DrawnROI);
9979    }
9980 
9981    if (!sv->BS) {
9982       fprintf (SUMA_STDERR,
9983                "%s: No Brushstroke (BS), nothing to do.\n", FuncName);
9984       SUMA_RETURN (DrawnROI);
9985    }
9986 
9987    if (!SUMAg_CF->ROI_mode) {
9988       fprintf (SUMA_STDERR, "%s: Not in ROI mode, nothing to do.\n", FuncName);
9989       SUMA_RETURN (DrawnROI);
9990    }
9991 
9992    /* We are in ROI mode,
9993       is there an ROI in curDrawnROI that works with the current surface ? */
9994    if (SUMAg_CF->X->DrawROI->curDrawnROI) {
9995       if (  SUMA_isdROIrelated(SUMAg_CF->X->DrawROI->curDrawnROI,
9996                                                    (SUMA_ALL_DO *)SO) &&
9997             SUMAg_CF->X->DrawROI->curDrawnROI->DrawStatus != SUMA_ROI_Finished){
9998          if (LocalHead)
9999             fprintf (SUMA_STDERR,"%s: using currDrawnROI.\n", FuncName);
10000          DrawnROI = SUMAg_CF->X->DrawROI->curDrawnROI;
10001       }else {
10002          if (LocalHead)
10003             fprintf (SUMA_STDERR,
10004                      "%s: No match between currDrawnROI and SO.\n", FuncName);
10005          DrawnROI = NULL;
10006       }
10007    }
10008    if (!DrawnROI) { /* try some more */
10009       if ((DrawnROI = SUMA_FetchROI_InCreation (SO, SUMAg_DOv, SUMAg_N_DOv))){
10010          if (LocalHead)
10011             fprintf (SUMA_STDERR,"%s: using ROI in creation.\n", FuncName);
10012          /* There is an ROI being created on this surface,
10013             initialize DrawROI window*/
10014          SUMA_InitializeDrawROIWindow(DrawnROI);
10015       } else {
10016          /* wait till later */
10017          if (LocalHead)
10018             fprintf (SUMA_STDERR,"%s: will create a new ROI.\n", FuncName);
10019       }
10020    }
10021 
10022    if (!DrawnROI && BsA == SUMA_BSA_JoinEnds) {
10023       SUMA_SLP_Err ("NO ROI to close.");
10024       SUMA_RETURN (DrawnROI);
10025    }
10026 
10027    if (!DrawnROI) { /* No ROI found, create one */
10028       if (LocalHead)
10029          fprintf (SUMA_STDERR,
10030                   "%s: No ROI found, creating a new one.\n", FuncName);
10031       SUMA_GET_TEXT_FIELD(SUMAg_CF->X->DrawROI->ROIlbl->textfield, sbuf);
10032       DrawnROI = SUMA_AllocateDrawnROI (SO->idcode_str, SUMA_ROI_InCreation,
10033                                         SUMA_ROI_OpenPath,
10034                                         sbuf,
10035                                         SUMAg_CF->X->DrawROI->ROIval->value);
10036       if (!DrawnROI) {
10037          SUMA_RegisterMessage (SUMAg_CF->MessageList,
10038                                "Failed to allocate for DrawnROI.", FuncName,
10039                                SMT_Critical, SMA_LogAndPopup);
10040          SUMA_RETURN (NULL);
10041       }
10042 
10043       /* Although ROIs are stored as DOs,
10044          they are dependent on the surfaces they are related to
10045          ROIs at this stage are node indices only (and perhaps the mesh) but the          coordinates of the indices
10046          come from the surface onto which they are displayed. So when you are
10047          drawing a surface, using CreateMesh,
10048          you will search DOv for ROIs related to the surface displayed and
10049          overlay them accordingly */
10050       /* Add the ROI to DO */
10051       if (!SUMA_AddDO ( SUMAg_DOv, &SUMAg_N_DOv,
10052                         (void *)DrawnROI, ROIdO_type, SUMA_WORLD)) {
10053          fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_AddDO.\n", FuncName);
10054       }
10055 
10056       /* is the Switch ROI window open ? */
10057       SUMA_IS_DRAW_ROI_SWITCH_ROI_SHADED(Shaded);
10058       if (!Shaded) {
10059          SUMA_cb_DrawROI_SwitchROI (
10060                NULL,
10061                (XtPointer) SUMAg_CF->X->DrawROI->SwitchROIlst,
10062                NULL);
10063       }
10064 
10065 
10066    } else {
10067       if (LocalHead)
10068          fprintf( SUMA_STDOUT,
10069                   "%s: ROI %p fetched. Status %d.\n",
10070                   FuncName, DrawnROI, DrawnROI->DrawStatus);
10071    }
10072 
10073    if (BsA == SUMA_BSA_AppendStrokeOrFill) {
10074       if (  DrawnROI->Type == SUMA_ROI_ClosedPath ||
10075             DrawnROI->Type == SUMA_ROI_FilledArea)
10076          BsA = SUMA_BSA_FillArea;
10077       else if (DrawnROI->Type == SUMA_ROI_OpenPath)
10078          BsA = SUMA_BSA_AppendStroke;
10079    }
10080    if (  DrawnROI->Type == SUMA_ROI_ClosedPath &&
10081          BsA != SUMA_BSA_FillArea) {
10082       SUMA_SLP_Err ( "You can only fill a closed path.\n"
10083                      "You cannot append more paths to it.");
10084       SUMA_RETURN (DrawnROI);
10085    }
10086    if (  DrawnROI->Type == SUMA_ROI_FilledArea &&
10087          BsA != SUMA_BSA_FillArea) {
10088       SUMA_SLP_Err ("You cannot add paths to a filled ROI.");
10089       SUMA_RETURN (DrawnROI);
10090    }
10091 
10092    /* Good, now initialize the DrawROI widget, if needed */
10093    if (SUMAg_CF->X->DrawROI->curDrawnROI != DrawnROI) {
10094       if (!SUMA_InitializeDrawROIWindow (DrawnROI)) {
10095          SUMA_SL_Err("Failed to initialize DrawWindow.");
10096       }
10097    }
10098 
10099    /* Now you must transform the brushstroke to a series of nodes
10100       (not necessarily connected)*/
10101    if (LocalHead)
10102       fprintf (SUMA_STDERR,
10103                "%s: Turning BrushStroke to NodeStroke ...\n", FuncName);
10104    if (!SUMA_BrushStrokeToNodeStroke (sv)) {
10105       SUMA_RegisterMessage (SUMAg_CF->MessageList,
10106                          "Failed in SUMA_BrushStrokeToNodeStroke.", FuncName,
10107                          SMT_Error, SMA_LogAndPopup);
10108       SUMA_RETURN(NULL);
10109    }
10110 
10111    switch (BsA) {
10112       case SUMA_BSA_AppendStroke:
10113          /* Turn the brush stroke into a series of connected nodes */
10114          if (LocalHead)
10115             fprintf (SUMA_STDERR,
10116                      "%s: Turning NodeStroke to ROIStroke ...\n", FuncName);
10117          if (!(ROIstroke = SUMA_NodeStrokeToConnectedNodes (sv))) {
10118             SUMA_RegisterMessage (SUMAg_CF->MessageList,
10119                                   "Failed in SUMA_NodeStrokeToConnectedNodes.",
10120                                   FuncName,
10121                                   SMT_Critical, SMA_LogAndPopup);
10122             if (ROIlink) SUMA_FreeROIDatum((void *)ROIlink);
10123             ROIlink = NULL;
10124             if (ROIstroke) SUMA_FreeROIDatum((void *)ROIstroke);
10125             ROIstroke = NULL;
10126             SUMA_RETURN(NULL);
10127          }
10128 
10129          if (LocalHead)
10130             fprintf (SUMA_STDERR,
10131                      "%s: Turning NodeStroke to ROIStroke . DONE.\n",
10132                      FuncName);
10133          /* if this is the first element of ROI,
10134             create the first ROIdatum and get out */
10135          if (dlist_size(DrawnROI->ROIstrokelist)) {
10136             /* Not the beginning of an ROI */
10137             if (LocalHead)
10138                fprintf (SUMA_STDERR,
10139                         "%s: Adding ROIstroke to previous ones ...\n",
10140                         FuncName);
10141             /* make sure new brushstroke is not just one node that is
10142                the tail of the ROI*/
10143             SUMA_DRAWN_ROI_TAIL_NODE(DrawnROI,TailNode);
10144 
10145             SUMA_BS_FIRST_SURF_NODE(sv->BS, FirstSurfNode, ft, El);
10146             SUMA_BS_COUNT_SURF_NODES(sv->BS, N_SurfNode);
10147             if (FirstSurfNode == TailNode && N_SurfNode == 1) {
10148                /* nothing to do here */
10149                fprintf (SUMA_STDERR,
10150                         "%s: New stroke has one node that is \n"
10151                         "identical to tail node. Dumping element.\n", FuncName);
10152                SUMA_RETURN(DrawnROI);
10153             }
10154 
10155             /* Connect this chunk to the last open Node in ROI */
10156             if (FirstSurfNode != TailNode) {
10157                if (LocalHead)
10158                   fprintf (SUMA_STDERR,
10159                            "%s: linking Tail Node to New stroke.\n", FuncName);
10160 
10161                ROIlink = SUMA_LinkTailNodeToNodeStroke (sv, DrawnROI);
10162                if (!ROIlink) {
10163                   SUMA_SL_Err("Failed to connect Tail node to Node stroke\n"
10164                               ", try again.");
10165                   SUMA_RETURN(NULL);
10166                }
10167                if (LocalHead) {
10168                   fprintf (SUMA_STDERR,
10169                            "%s: RIOlink, before prepending:\n", FuncName);
10170                   SUMA_ShowDrawnROIDatum (ROIlink, NULL, NOPE);
10171                }
10172 
10173                /* connect the ROIlink with the ROIstroke */
10174                if (LocalHead) {
10175                   fprintf (SUMA_STDERR,
10176                            "%s: RIOstroke, before prepending:\n", FuncName);
10177                   SUMA_ShowDrawnROIDatum (ROIstroke, NULL, NOPE);
10178                }
10179                if (!SUMA_PrependToROIdatum (ROIlink, ROIstroke)) {
10180                   SUMA_RegisterMessage (SUMAg_CF->MessageList,
10181                                      "Failed to merge ROIs.", FuncName,
10182                                      SMT_Critical, SMA_LogAndPopup);
10183                   if (ROIlink) SUMA_FreeROIDatum((void *)ROIlink);
10184                   ROIlink = NULL;
10185                   if (ROIstroke) SUMA_FreeROIDatum((void *)ROIstroke);
10186                   ROIstroke = NULL;
10187                   SUMA_RETURN(NULL);
10188                }
10189 
10190                if (LocalHead) {
10191                   fprintf (SUMA_STDERR,
10192                            "%s: RIOstroke, after prepending:\n", FuncName);
10193                   SUMA_ShowDrawnROIDatum (ROIstroke, NULL, NOPE);
10194                }
10195               /* now free ROIlink, not needed anymore */
10196                if (ROIlink) SUMA_FreeROIDatum ((void *)ROIlink); ROIlink = NULL;
10197             }
10198          }else{
10199             if (LocalHead)
10200                fprintf (SUMA_STDERR, "%s: First ROIStroke of ROI.\n", FuncName);
10201          }
10202          break;
10203       case SUMA_BSA_JoinEnds:
10204          /* Join ends here */
10205          if (DrawnROI) { /*   close ROI */
10206             SUMA_DRAWN_ROI_HEAD_NODE(DrawnROI,HeadNode);
10207             SUMA_BS_FIRST_SURF_NODE(sv->BS, FirstSurfNode, ft, El);
10208             bsd = (SUMA_BRUSH_STROKE_DATUM *)El->data;
10209             if (LocalHead)
10210                fprintf( SUMA_STDERR,
10211                         "%s: Trying to join node %d to node %d.\n",
10212                         FuncName, FirstSurfNode, HeadNode);
10213             /* Now compute the intersection of the surface with the plane */
10214             ROIstroke = SUMA_Surf_Plane_Intersect_ROI (  SO, FirstSurfNode,
10215                                                          HeadNode, bsd->NP);
10216 
10217             if (!ROIstroke) {
10218                SUMA_SL_Err ("Failed to close path. Repeat new stroke.");
10219                SUMA_RETURN(DrawnROI);
10220             }
10221             /* what is the last node of ROIstroke ?
10222             It is possible that the returned ROIstroke
10223             was not a successful closure (a partial success), investigate*/
10224             if (LocalHead)
10225                fprintf( SUMA_STDERR,
10226                         "%s: Last node of ROIstroke is %d\n",
10227                         FuncName, ROIstroke->nPath[ROIstroke->N_n-1]);
10228             if (ROIstroke->nPath[ROIstroke->N_n-1] != HeadNode) {
10229                /* pretend this is not a JoinEnds exercice */
10230                BsA = SUMA_BSA_AppendStroke;
10231                SUMA_SL_Err ("Failed to close path. Continue with stroke.");
10232                SUMA_RETURN(DrawnROI);
10233             }else {
10234                /* Do not remove the last point from ROIstroke,
10235                   otherwise it will make drawing a closed ROI painful */
10236             }
10237          } else {
10238             /* tremors, nothing to do */
10239          }
10240          break;
10241       case SUMA_BSA_FillArea:
10242          SUMA_BS_FIRST_SURF_NODE(sv->BS, FirstSurfNode, ft, El);
10243          if (LocalHead)
10244             fprintf (SUMA_STDERR,
10245                      "%s: Should be filling from node %d\n",
10246                      FuncName, FirstSurfNode);
10247 
10248          /* create the mask from ROIs on this surface */
10249          switch (SUMAg_CF->ROI_FillMode) {
10250             case SUMA_ROI_FILL_TO_ALLROI:
10251                ROI_Mask = SUMA_Build_Mask_AllROI ( SUMAg_DOv, SUMAg_N_DOv,
10252                                                    SO, NULL, &N_ROI_Mask);
10253                break;
10254             case SUMA_ROI_FILL_TO_THISROI:
10255                ROI_Mask = (int *)SUMA_calloc (SO->N_Node, sizeof(int));
10256                if (!ROI_Mask) {
10257                   SUMA_SLP_Crit("Failed to allocate");
10258                   SUMA_RETURN(DrawnROI);
10259                }
10260                SUMA_Build_Mask_DrawnROI (DrawnROI, ROI_Mask);
10261                break;
10262             default:
10263                SUMA_SLP_Err("No such mode.");
10264                SUMA_RETURN(DrawnROI);
10265                break;
10266          }
10267 
10268          /* Now fill it up */
10269          ROIfill = SUMA_FillToMask (SO, ROI_Mask, FirstSurfNode);
10270          if (ROI_Mask) SUMA_free(ROI_Mask); ROI_Mask = NULL;
10271          if (!ROIfill) {
10272             SUMA_SLP_Err(  "Failed to fill area:\n"
10273                            "Perhaps seed on edge\nor nothing to fill.");
10274             SUMA_RETURN(DrawnROI);
10275          }
10276 
10277          break;
10278       default:
10279          fprintf (SUMA_STDERR,
10280                   "Error %s: Why are you doing this to me ?.\n", FuncName);
10281          break;
10282    }
10283 
10284 
10285    /* Another switch on BsA,
10286       it is possible that its value changed within this function */
10287 
10288    switch (BsA) {
10289       case SUMA_BSA_AppendStroke:
10290          /* store the action */
10291          ROIstroke->action = SUMA_BSA_AppendStroke;
10292          /*now add the ROIdatum to the list of ROIs */
10293          if (LocalHead)
10294             fprintf (SUMA_STDERR,
10295                      "%s: Adding ROIStroke to DrawnROI->ROIstrokelist\n",
10296                      FuncName);
10297          ROIA = (SUMA_ROI_ACTION_STRUCT *)
10298                    SUMA_calloc(1,sizeof(SUMA_ROI_ACTION_STRUCT));
10299                    /* this structure is freed in SUMA_DestroyROIActionData */
10300          ROIA->DrawnROI = DrawnROI;
10301          ROIA->ROId = ROIstroke;
10302          tmpStackPos = SUMA_PushActionStack (
10303                            DrawnROI->ActionStack,
10304                            DrawnROI->StackPos, SUMA_AddToTailROIDatum,
10305                            (void *)ROIA, SUMA_DestroyROIActionData);
10306          if (tmpStackPos) DrawnROI->StackPos = tmpStackPos;
10307          else {
10308             fprintf (SUMA_STDERR,
10309                      "Error %s: Failed in SUMA_PushActionStack.\n", FuncName);
10310             SUMA_RETURN (DrawnROI);
10311          }
10312          if (  SUMAg_CF->X->DrawROI->WhatDist == SW_DrawROI_WhatDistTrace ||
10313                SUMAg_CF->X->DrawROI->WhatDist == SW_DrawROI_WhatDistAll)
10314             SUMA_ReportDrawnROIDatumLength(  SO, ROIA->ROId, NULL,
10315                                              SUMAg_CF->X->DrawROI->WhatDist);
10316          break;
10317       case SUMA_BSA_JoinEnds:
10318          /* store the action */
10319          ROIstroke->action = SUMA_BSA_JoinEnds;
10320          if (LocalHead) fprintf (SUMA_STDERR, "%s: Closing path.\n", FuncName);
10321          ROIA = (SUMA_ROI_ACTION_STRUCT *)
10322                    SUMA_calloc(1,sizeof(SUMA_ROI_ACTION_STRUCT));
10323                      /* this structure is freed in SUMA_DestroyROIActionData */
10324          ROIA->DrawnROI = DrawnROI;
10325          ROIA->ROId = ROIstroke;
10326          tmpStackPos = SUMA_PushActionStack (
10327                            DrawnROI->ActionStack, DrawnROI->StackPos,
10328                            SUMA_AddToTailJunctionROIDatum,
10329                            (void *)ROIA, SUMA_DestroyROIActionData);
10330          if (tmpStackPos) DrawnROI->StackPos = tmpStackPos;
10331          else {
10332             fprintf (SUMA_STDERR,
10333                      "Error %s: Failed in SUMA_PushActionStack.\n", FuncName);
10334             SUMA_RETURN (DrawnROI);
10335          }
10336          if (  SUMAg_CF->X->DrawROI->WhatDist == SW_DrawROI_WhatDistTrace ||
10337                SUMAg_CF->X->DrawROI->WhatDist == SW_DrawROI_WhatDistAll)
10338             SUMA_ReportDrawnROIDatumLength(  SO, ROIA->ROId, NULL,
10339                                              SUMAg_CF->X->DrawROI->WhatDist);
10340          break;
10341       case SUMA_BSA_FillArea:
10342          /* store the action */
10343          ROIfill->action = SUMA_BSA_FillArea;
10344          /* Now add ROIdatum to stack */
10345          ROIA = (SUMA_ROI_ACTION_STRUCT *)
10346                    SUMA_calloc(1,sizeof(SUMA_ROI_ACTION_STRUCT));
10347                      /* this structure is freed in SUMA_DestroyROIActionData */
10348          ROIA->DrawnROI = DrawnROI;
10349          ROIA->ROId = ROIfill;
10350          tmpStackPos = SUMA_PushActionStack (
10351                            DrawnROI->ActionStack, DrawnROI->StackPos,
10352                            SUMA_AddFillROIDatum,
10353                            (void *)ROIA, SUMA_DestroyROIActionData);
10354          if (tmpStackPos) DrawnROI->StackPos = tmpStackPos;
10355          else {
10356             fprintf (SUMA_STDERR,
10357                "Error %s: Failed in SUMA_PushActionStack.\n", FuncName);
10358             SUMA_RETURN (DrawnROI);
10359          }
10360          break;
10361       default:
10362          fprintf (SUMA_STDERR,
10363             "Error %s: Why are you doing this to me ??.\n", FuncName);
10364          break;
10365    }
10366 
10367    /* Now update the Paint job on the ROI plane */
10368    if (!SUMA_Paint_SO_ROIplanes_w (SO, SUMAg_DOv, SUMAg_N_DOv)) {
10369       SUMA_SLP_Err("Failed in SUMA_Paint_SO_ROIplanes_w.");
10370       SUMA_RETURN(DrawnROI);
10371    }
10372    if (BsA == SUMA_BSA_FillArea) {   /*  ZSS Sept 29 06 */
10373       SUMA_OVERLAYS *Overlay=NULL;
10374       int junk;
10375       /* If you're drawing, and have just filled an area,
10376          better pop the dset to the top so it is visible */
10377       if (!(Overlay = SUMA_Fetch_OverlayPointer((SUMA_ALL_DO *)SO,
10378                                                 DrawnROI->ColPlaneName,
10379                                                 &junk))) {
10380          SUMA_S_Err("Unexpected! Could not find overlay pointer");
10381       } else {
10382          /* if the current col plane is not the same as this one,
10383             do the switching please */
10384          SUMA_InitializeColPlaneShell((SUMA_ALL_DO *)SO, Overlay);
10385          SUMA_UpdateColPlaneShellAsNeeded((SUMA_ALL_DO *)SO);
10386                                           /* update other open
10387                                              ColPlaneShells */
10388       }
10389    }
10390 
10391    SUMA_RETURN(DrawnROI);
10392 }
10393 
10394 /*!
10395    Function that turns a brushstroke to a series of nodes on the surface.
10396 
10397    No surface paths are created from one node to the next yet.
10398 
10399    It is not always the case that BrushStroke->N_SurfNodes is equal
10400    to BrushStroke->N
10401 */
SUMA_BrushStrokeToNodeStroke(SUMA_SurfaceViewer * sv)10402 SUMA_Boolean SUMA_BrushStrokeToNodeStroke (SUMA_SurfaceViewer *sv)
10403 {
10404    static char FuncName[]={"SUMA_BrushStrokeToNodeStroke"};
10405    DList * NS=NULL;
10406    SUMA_SurfaceObject *SO=NULL;
10407    SUMA_MT_INTERSECT_TRIANGLE *MTI = NULL;
10408    float delta_t_tmp;
10409    struct timeval tt_tmp;
10410    int N = -1;
10411    SUMA_Boolean LocalHead=NOPE;
10412    SUMA_BRUSH_STROKE_DATUM *bsd=NULL, *obsd=NULL;
10413    DListElmt *Elmt = NULL, *oElmt=NULL;
10414 
10415    SUMA_ENTRY;
10416 
10417    if (!(SO = SUMA_SV_Focus_SO(sv))) {
10418       SUMA_S_Err("No surface in focus");
10419       SUMA_RETURN(NOPE);
10420    }
10421 
10422    /* ONLY WORK ON FocusSO */
10423    if (SO->FaceSetDim != 3) {
10424       SUMA_S_Err("SUMA_MT_intersect_triangle only works for triangular meshes.");
10425       SUMA_RETURN(NOPE);
10426    }
10427 
10428    N = dlist_size(sv->BS);
10429    if (!N) {
10430       fprintf (SUMA_STDERR, "%s: Empty brushstroke, nothing to do.\n", FuncName);
10431       SUMA_RETURN(NOPE);
10432    }else SUMA_LHv("%d element(s) in sv->BS.\n", N);
10433 
10434    /* the first node of the brushstroke is stored as the cross hair's node id,
10435       just copy it */
10436    Elmt = dlist_head(sv->BS);
10437    bsd = (SUMA_BRUSH_STROKE_DATUM *)Elmt->data;
10438 
10439    bsd->SurfNode = SO->SelectedNode;
10440    bsd->SurfTri = SO->SelectedFaceSet;
10441 
10442    #ifdef DISASTER_LOOP
10443       /* Now as a brute force method, do all the remaing nodes in the path.
10444       In the future, you want to downsample the path is some clever fashion */
10445       if (N > 1) {
10446          if (LocalHead) {
10447             fprintf (SUMA_STDERR, "%s: Disaster loop, hold on .\n", FuncName);
10448             SUMA_etime (&tt_tmp, 0);
10449          }
10450 
10451          MTI = NULL;
10452          do {
10453             Elmt = Elmt->next;
10454             bsd = (SUMA_BRUSH_STROKE_DATUM *)Elmt->data;
10455 
10456             MTI = SUMA_MT_intersect_triangle(   bsd->NP, bsd->FP,
10457                                                 SO->NodeList, SO->N_Node,
10458                                                 SO->FaceSetList, SO->N_FaceSet,
10459                                                 MTI,0);
10460 
10461             if (!MTI) {
10462                fprintf(SUMA_STDERR,"Error %s: SUMA_MT_intersect_triangle failed.\n", FuncName);
10463                SUMA_RETURN (NOPE);
10464             }
10465 
10466             if (MTI->N_hits) { /* There is a hit, store it if useful */
10467                oElmt = Elmt->prev;
10468                obsd = (SUMA_BRUSH_STROKE_DATUM *)oElmt->data;
10469                if (obsd->SurfNode != MTI->inodemin) { /* a new one, bring it on */
10470                   bsd->SurfNode = MTI->inodemin;
10471                   bsd->SurfTri = MTI->ifacemin;
10472                }else {
10473                   /* destroy Elmt, it is redundant */
10474                   if (LocalHead) fprintf (SUMA_STDERR, "%s: Removing redundant BS element.\n", FuncName);
10475                   dlist_remove (sv->BS, Elmt, (void*)(&bsd));
10476                   SUMA_FreeBSDatum (bsd);
10477                   Elmt = oElmt;
10478                }
10479             }
10480 
10481          }while (Elmt != dlist_tail(sv->BS));
10482 
10483          /* free MTI */
10484          MTI = SUMA_Free_MT_intersect_triangle(MTI);
10485 
10486          if (LocalHead) {
10487             delta_t_tmp = SUMA_etime (&tt_tmp, 1);
10488             if (LocalHead) fprintf (SUMA_STDERR, "Local Debug %s: Intersections took %f seconds.\n", FuncName, delta_t_tmp);
10489          }
10490 
10491       }
10492    #else
10493    if (N > 1) { /* new, faster method */
10494       DListElmt *Eli=NULL, *Eln=NULL;
10495       float ip[3], d[3];
10496       int IncTri[100], N_IncTri=0, n1=-1, n2=-1, n3=-1, ni = -1,
10497           ti = -1, N_Neighb=0,DeciLevel = 0, i, j, Removed=0;
10498       int DeciReentry=0, UsedNode[3]={ 0 , 0, 0 };
10499       SUMA_BRUSH_STROKE_DATUM *bsdi=NULL, *bsdn=NULL, *bsd_deci=NULL;
10500       SUMA_Boolean  DoesInters=NOPE; /* flag for Decimation mode */
10501       SUMA_Boolean  TrackOscillation = YUP; /* flag to tracking
10502                                                algorithm oscillation */
10503       SUMA_Boolean  TryBruteForce = NOPE;
10504       int *Visited = NULL;
10505 
10506       if (TrackOscillation) {
10507          Visited = (int *)SUMA_calloc(SO->N_Node, sizeof(int));
10508          if (!Visited) {
10509             SUMA_SLP_Err("Failed to allocate for Visited.\n");
10510             SUMA_RETURN(NOPE);
10511          }
10512       }
10513 
10514       Eli = Elmt; /* initialize current element to the
10515                      very fist in the brushstroke */
10516       MTI = NULL;
10517       TryBruteForce = NOPE;
10518       do {
10519          bsdi = (SUMA_BRUSH_STROKE_DATUM *)Eli->data;
10520          n1 = bsdi->SurfNode;
10521 
10522          Eln = Eli->next; /* get the next element in line */
10523          bsdn = (SUMA_BRUSH_STROKE_DATUM *)Eln->data;
10524 
10525          if (LocalHead)
10526             fprintf(SUMA_STDERR,"%s: Working from node %d.\n", FuncName, n1);
10527 
10528          if (!TryBruteForce) { /* try the fast method */
10529             N_Neighb = SO->FN->N_Neighb[n1];
10530             if (N_Neighb < 3) {
10531                /* nothing found */
10532                SUMA_SLP_Err ("Node has less than 3 neighbors.\n"
10533                              "This method will not apply.");
10534                SUMA_RETURN(NOPE);
10535             }
10536 
10537             /* does the ray formed by Eln's NP and FP hit any of
10538                the triangles incident to bsdi->SurfNode (or n1) ? */
10539             if (LocalHead)
10540                fprintf (SUMA_STDERR,
10541                         "%s: Searching incident triangles:\n", FuncName);
10542             i=0;
10543             DoesInters = NOPE;
10544             while ((i < N_Neighb ) && (!DoesInters)) {
10545                n2 = SO->FN->FirstNeighb[n1][i];
10546                if ( i+1 == N_Neighb) n3 = SO->FN->FirstNeighb[n1][0];
10547                else n3 = SO->FN->FirstNeighb[n1][i+1];
10548                #if 0
10549                   if (LocalHead) {
10550                      fprintf (SUMA_STDERR, " %d: [%d %d %d] Tri %d\n",
10551                         i, n1, n2, n3, SUMA_whichTri(SO->EL, n1, n2, n3, 1));
10552                      fprintf (SUMA_STDERR, " %d: [%.2f, %.2f, %.2f]\n",
10553                         n1, SO->NodeList[3*n1],
10554                             SO->NodeList[3*n1+1], SO->NodeList[3*n1+2]);
10555                      fprintf (SUMA_STDERR, " %d: [%.2f, %.2f, %.2f]\n",
10556                         n2, SO->NodeList[3*n2],
10557                             SO->NodeList[3*n2+1], SO->NodeList[3*n2+2]);
10558                      fprintf (SUMA_STDERR, " %d: [%.2f, %.2f, %.2f]\n",
10559                         n3, SO->NodeList[3*n3],
10560                             SO->NodeList[3*n3+1], SO->NodeList[3*n3+2]);
10561                      fprintf (SUMA_STDERR,
10562                         " NP: [%.2f, %.2f, %.2f] FP: [%.3f, %.2f, %.2f]\n",
10563                               bsdn->NP[0], bsdn->NP[1], bsdn->NP[2],
10564                               bsdn->FP[0], bsdn->FP[1], bsdn->FP[2]);
10565                   }
10566                #endif
10567                DoesInters = SUMA_MT_isIntersect_Triangle (bsdn->NP, bsdn->FP,
10568                                  &(SO->NodeList[3*n1]),
10569                                  &(SO->NodeList[3*n2]),
10570                                  &(SO->NodeList[3*n3]), ip, d, &ni);
10571                if (DoesInters) {
10572                   if (ni == 0) ni = n1;
10573                   else if (ni == 1) ni = n2;
10574                   else ni = n3;
10575 
10576                   ti = SUMA_whichTri(SO->EL, n1, n2, n3, 1, 0);
10577                }
10578 
10579                #if 0
10580                   if (LocalHead)
10581                      fprintf (SUMA_STDERR,
10582                               "%s: DoesInters = %d, ni = %d\n",
10583                               FuncName, DoesInters, ni);
10584                   {
10585                      /* for debuging */
10586                      MTI = NULL;MTI =
10587                         SUMA_MT_intersect_triangle(   bsdn->NP, bsdn->FP,
10588                                                       SO->NodeList, SO->N_Node,
10589                                                       SO->FaceSetList,
10590                                                       SO->N_FaceSet,
10591                                                       MTI, 0);
10592                      fprintf (SUMA_STDERR,
10593                         "%s: Intersection would be with triangle %d, node %d\n",
10594                               FuncName, MTI->ifacemin, MTI->inodemin);
10595                   }
10596                #endif
10597                ++i;
10598             }
10599             if (LocalHead) fprintf (SUMA_STDERR, "\n");
10600 
10601          } else  { /* try brute force flag has been set*/
10602 
10603             if (LocalHead) fprintf (SUMA_STDERR, "%s: Trying brute force here \n", FuncName);
10604             /* Now skip and remove decimated elements */
10605             SUMA_REMOVE_NEXT_NON_DECIMATED (sv->BS, Eli, Eln);
10606             DeciLevel = 0;
10607             DeciReentry = 0;
10608             if (!Eln) {
10609              SUMA_SL_Err ("I tried hard to figure out your trace.\nI failed, now you try again.");
10610              SUMA_RETURN(YUP);
10611             }
10612 
10613             bsdn = (SUMA_BRUSH_STROKE_DATUM *)Eln->data;
10614             MTI = SUMA_MT_intersect_triangle(   bsdn->NP, bsdn->FP,
10615                                     SO->NodeList, SO->N_Node,
10616                                     SO->FaceSetList, SO->N_FaceSet,
10617                                     MTI, 0);
10618 
10619             if (!MTI) {
10620                SUMA_SL_Err ("I tried harder to figure out your trace.\nI failed, do try again.");
10621                SUMA_RETURN (YUP);
10622             }
10623 
10624             if (MTI->N_hits) { /* There is a hit, store it if useful */
10625                DoesInters = YUP;
10626                if (bsdi->SurfNode != MTI->inodemin) { /* a new one, bring it on */
10627                   if (LocalHead) fprintf (SUMA_STDERR, "%s: Brute Force: Found intersection at new node %d.\n", FuncName, MTI->inodemin);
10628                   ni = MTI->inodemin;
10629                   ti = MTI->ifacemin;
10630                }else {
10631                   /* set ni to n1 and let the element be destroyed */
10632                   if (LocalHead) fprintf (SUMA_STDERR, "%s: Brute Force: Found intersection at n1 = %d!.\n", FuncName, MTI->inodemin);
10633                   ni = n1;
10634                }
10635             } else {
10636                /* No hits at all, get out of this business */
10637                SUMA_SL_Err ("Why are you drawing out of bounds ?");
10638                SUMA_RETURN (YUP);
10639             }
10640             /* reset the TryBruteForce flag */
10641             TryBruteForce = NOPE;
10642          }
10643 
10644          if (!DoesInters) { /* no intersection found, insert an element between Eli and Eln and try again */
10645             ++DeciLevel;
10646             if (LocalHead) fprintf (SUMA_STDERR, "%s: No intersection found. Decimating, level %d.\n", FuncName, DeciLevel);
10647 
10648             if (DeciLevel > 3000) { /* this condition is only here to keep things from going awry. */
10649                if (LocalHead) fprintf (SUMA_STDERR,"%s: Decimation method failed. Trying from brute force", FuncName);
10650                TryBruteForce = YUP;
10651             } else {
10652                bsd_deci = SUMA_CreateBSDatum();
10653                bsd_deci->Decimated = YUP;
10654                bsd_deci->x = (bsdi->x + bsdn->x)/2.0;
10655                bsd_deci->y = (bsdi->y + bsdn->y)/2.0;
10656                for (j=0; j < 3; ++j) bsd_deci->NP[j] = (bsdi->NP[j] + bsdn->NP[j])/2.0;
10657                for (j=0; j < 3; ++j) bsd_deci->FP[j] = (bsdi->FP[j] + bsdn->FP[j])/2.0;
10658                bsd_deci->SurfNode = -1;
10659                bsd_deci->SurfTri = -1;
10660 
10661                dlist_ins_next (sv->BS, Eli, bsd_deci);
10662             }
10663          } else {
10664             /* intersection found */
10665             if (ni == n1 && !DeciLevel) {
10666                /* same node reached, not during decimation, perhaps path was too densely sampled, delete Eln */
10667                ++Removed;
10668                if (LocalHead) fprintf (SUMA_STDERR, "%s: Same node reached without decimation, deleting for the %dth time.\n",
10669                    FuncName, Removed);
10670                dlist_remove(sv->BS, Eln, (void*)&bsdn);
10671                SUMA_FreeBSDatum(bsdn);
10672             } else {
10673                if (ni == n1 && DeciLevel) {
10674                   /* back to the starting point during decimation  */
10675                   if (DeciLevel) { /* user went out of bounds or drawing over cuts in surface */
10676                      #if 0
10677                         /* same node reached during decimation, to hell with it, use brute force intersection: YOU PAY PRICE IN TIME MISTER*/
10678                         TryBruteForce = YUP;
10679                         /* cancel the find */
10680                         DoesInters = NOPE;
10681                      #else
10682                         /* same node reached during decimation, try othernode in triangle */
10683                         if (!DeciReentry) {
10684                            UsedNode[0] = n1;
10685                            if (LocalHead) fprintf (SUMA_STDERR, "%s: Same node reached during decimation.\n Switching to second closest node.\n", FuncName);
10686                            if (d[1] < d[2]) {
10687                               ni = n2;
10688                               UsedNode[1] = n2;
10689                            } else {
10690                               ni = n3;
10691                               UsedNode[1] = n3;
10692                            }
10693                         } else if (DeciReentry == 1){
10694                            /* been there before, one last node is left */
10695                            if (LocalHead) fprintf (SUMA_STDERR, "%s: Last chance!\n", FuncName);
10696                            if (n2 != UsedNode[0] && n2 != UsedNode[1]) ni = n2;
10697                            else if (n3 != UsedNode[0] && n3 != UsedNode[1]) ni = n3;
10698                         } else {
10699                            /* Decimation failed, Do intersection with entire surface */
10700                            TryBruteForce = YUP;
10701                            /* cancel the find */
10702                            DoesInters = NOPE;
10703                         }
10704                      #endif
10705                      ++DeciReentry;
10706                   }
10707                }else {
10708                   /* ni != n1  Reset DeciLevel */
10709                   DeciLevel = 0;
10710                   DeciReentry = 0;
10711                }
10712 
10713                /* algorithm might fall into oscillatory patterns, keep track of nodes visited.
10714                   It is possible that a node is visited multiple times when users go over the
10715                   same region over and over and over.*/
10716                if (TrackOscillation) {
10717                   ++Visited[ni];
10718                   if (Visited[ni] == 9) {
10719                      DoesInters = NOPE;
10720                      TryBruteForce = YUP;
10721                      SUMA_SL_Err ("Path tracing oscillation. Trying with brute force.");
10722                   }
10723                   if (Visited[ni] > 9) {
10724                      SUMA_SL_Err ("Path tracing oscillation remaining. Quitting tracing.");
10725                      SUMA_RETURN(YUP);
10726                   }
10727                }
10728 
10729                if (DoesInters) { /* it is possible that the find is cancelled */
10730                   if (LocalHead) fprintf (SUMA_STDERR, "%s: Found new node.\n", FuncName);
10731                   /* good, new node is useful*/
10732                   bsdn->SurfNode = ni;
10733                   bsdn->SurfTri = ti;
10734                   /* set Eli to Eln */
10735                   Eli = Eln;
10736                }
10737             }
10738          }
10739          /* repeat until you have no more element */
10740       } while (Eli != dlist_tail(sv->BS));
10741 
10742       if (MTI) MTI = SUMA_Free_MT_intersect_triangle(MTI);
10743       if (TrackOscillation) {
10744          if (Visited) SUMA_free(Visited);
10745       }
10746    }/* new, faster method */
10747    #endif
10748    SUMA_RETURN(YUP);
10749 }
10750 
10751 
10752 /*!
10753    \brief Function to link a node on the surface to a certain node in NodeStroke
10754 
10755    \param sv (SUMA_SurfaceViewer *) with a valid BrushStroke in it
10756    \param NonSurf (int) index of node on surface to connect to NinStroke
10757    \param ELinStroke (DListElmt *) sv->BS element containing in SurfNode the index of the node  to connect NonSurf to
10758    \sa SUMA_LinkTailNodeToNodeStroke
10759 */
SUMA_LinkThisNodeToNodeInStroke(SUMA_SurfaceViewer * sv,int NonSurf,DListElmt * ELinStroke)10760 SUMA_ROI_DATUM *SUMA_LinkThisNodeToNodeInStroke (SUMA_SurfaceViewer *sv,
10761                                           int NonSurf, DListElmt *ELinStroke)
10762 {
10763    static char FuncName[]={"SUMA_LinkThisNodeToNodeInStroke"};
10764    SUMA_Boolean LocalHead = NOPE;
10765    SUMA_ROI_DATUM *ROId=NULL;
10766    SUMA_SurfaceObject *SO=NULL;
10767    int Nfrom, Nto;
10768    SUMA_BRUSH_STROKE_DATUM *bsd=NULL;
10769 
10770    SUMA_ENTRY;
10771 
10772    if (!(SO = SUMA_SV_Focus_SO(sv))) {
10773       SUMA_S_Err("No SO in focus");
10774       SUMA_RETURN(NULL);
10775    }
10776 
10777    Nfrom = NonSurf;
10778    bsd = (SUMA_BRUSH_STROKE_DATUM *)ELinStroke->data;
10779    Nto = bsd->SurfNode;
10780 
10781    /* Now compute the intersection of the surface with the plane */
10782    ROId = SUMA_Surf_Plane_Intersect_ROI (SO, Nfrom, Nto, bsd->NP);
10783 
10784    if (!ROId) {
10785       SUMA_S_Err("Failed to link tail node to first node in new stroke.\n"
10786                  "Repeat new stroke.");
10787       SUMA_RETURN(NULL);
10788    }
10789 
10790    SUMA_RETURN(ROId);
10791 }
10792 
10793 /*!
10794    \brief Function to link a node on the surface to the first node
10795    of a NodeStroke
10796 
10797    -This function returns an ROI_datum that represents the link between
10798    the last node visited and the first node of the Nodestroke
10799 
10800    \sa SUMA_LinkThisNodeToNodeInStroke
10801 */
SUMA_LinkTailNodeToNodeStroke(SUMA_SurfaceViewer * sv,SUMA_DRAWN_ROI * DrawnROI)10802 SUMA_ROI_DATUM *SUMA_LinkTailNodeToNodeStroke ( SUMA_SurfaceViewer *sv,
10803                                                 SUMA_DRAWN_ROI *DrawnROI)
10804 {
10805 
10806    static char FuncName[]={"SUMA_LinkTailNodeToNodeStroke"};
10807    SUMA_ROI_DATUM *ROId=NULL;
10808    SUMA_SurfaceObject *SO=NULL;
10809    SUMA_Boolean LocalHead = NOPE;
10810    int Nfrom=-1, Nto=-1, Trito=-1;
10811    DListElmt *Elm=NULL;
10812    SUMA_BRUSH_STROKE_DATUM *bsd=NULL;
10813 
10814    SUMA_ENTRY;
10815 
10816    if (!(SO = SUMA_SV_Focus_SO(sv))) {
10817       SUMA_S_Err("No SO in focus");
10818       SUMA_RETURN(NULL);
10819    }
10820 
10821    /* get the equation of the plane fromed by TailNode,
10822       FirstNodeinBrushStroke and its NearPlanePoint */
10823    SUMA_DRAWN_ROI_TAIL_NODE(DrawnROI, Nfrom);
10824    if (Nfrom < 0) {
10825       fprintf (SUMA_STDERR, "Error %s: No tail node found.\n", FuncName);
10826       SUMA_RETURN(NULL);
10827    }
10828 
10829    /* get the first node in the stroke */
10830    SUMA_BS_FIRST_SURF_NODE(sv->BS, Nto, Trito, Elm);
10831    if (Nto < 0 || !Elm) {
10832       SUMA_SLP_Err ("Failed in SUMA_BS_FIRST_SURF_NODE macro.");
10833       SUMA_RETURN(NULL);
10834    }
10835    bsd = (SUMA_BRUSH_STROKE_DATUM *)Elm->data;
10836 
10837    /* Now compute the intersection of the surface with the plane */
10838    ROId = SUMA_Surf_Plane_Intersect_ROI (SO, Nfrom, Nto, bsd->NP);
10839 
10840    if (!ROId) {
10841       SUMA_S_Err("Failed to link tail node to first node in new stroke.\n"
10842                  "Repeat new stroke.");
10843       SUMA_RETURN(NULL);
10844    }
10845 
10846    SUMA_RETURN(ROId);
10847 }
10848 
10849 
10850 /*!
10851    This function turns the NodeStroke into a datum of connected nodes
10852 */
SUMA_NodeStrokeToConnectedNodes(SUMA_SurfaceViewer * sv)10853 SUMA_ROI_DATUM *SUMA_NodeStrokeToConnectedNodes (SUMA_SurfaceViewer *sv)
10854 {
10855    static char FuncName[]={"SUMA_NodeStrokeToConnectedNodes"};
10856    SUMA_Boolean LocalHead = NOPE;
10857    SUMA_ROI_DATUM *ROId=NULL, *ROIlink = NULL;
10858    int i=0;
10859    SUMA_SurfaceObject *SO=NULL;
10860    DListElmt *Elmt = NULL, *oElmt = NULL;
10861    SUMA_BRUSH_STROKE_DATUM *bsd=NULL;
10862 
10863    SUMA_ENTRY;
10864 
10865    if (!(SO = SUMA_SV_Focus_SO(sv))) {
10866       SUMA_S_Err("No SO in focus");
10867       SUMA_RETURN(NULL);
10868    }
10869 
10870    ROId = SUMA_AllocROIDatum();
10871 
10872    /* fill up node series here */
10873    ROId->N_n = 1;
10874    ROId->N_t = 1;
10875    ROId->nPath = (int *) SUMA_calloc (ROId->N_n, sizeof(int));
10876    ROId->tPath = (int *) SUMA_calloc (ROId->N_t, sizeof(int));
10877 
10878    SUMA_BS_FIRST_SURF_NODE(sv->BS, ROId->nPath[0], ROId->tPath[0], Elmt);
10879    ROId->Type = SUMA_ROI_NodeSegment;
10880 
10881    /* try filling up the rest */
10882    oElmt = Elmt;
10883    do {
10884       /* get the next element with a surfnode */
10885      SUMA_BS_NEXT_SURF_NODE(sv->BS, oElmt, Elmt);
10886 
10887      if (!Elmt) {
10888       /* perhaps reached end of list without success */
10889       SUMA_S_Note("Reached EOL without finding Elmt.\n"
10890                   "Not necessarily a bad thing.");
10891       SUMA_RETURN(ROId);
10892      } else {
10893       if (LocalHead) {
10894          fprintf (SUMA_STDERR, "%s: Working with element %p.\n", FuncName, Elmt);
10895       }
10896      }
10897      bsd = (SUMA_BRUSH_STROKE_DATUM *)Elmt->data;
10898      SUMA_LHv("%d %d\nWill look for edge %d %d\n",
10899                ROId->N_n, bsd->SurfNode,
10900                ROId->nPath[ROId->N_n-1], bsd->SurfNode);
10901      if (SUMA_FindEdge(SO->EL, ROId->nPath[ROId->N_n-1], bsd->SurfNode) < 0) {
10902          /* Not found, link nodes together*/
10903          SUMA_LH("Edge not found, linking together.");
10904          if (!(ROIlink = SUMA_LinkThisNodeToNodeInStroke (sv,
10905                                  ROId->nPath[ROId->N_n-1],  Elmt))) {
10906             SUMA_SLP_Err ("Failed to connect nodes in stroke.");
10907             SUMA_RETURN (ROId);
10908          }
10909          /* merge ROIlink with ROId */
10910          SUMA_LH("Merging ROIs together.");
10911          if (!SUMA_AppendToROIdatum (ROIlink, ROId)) {
10912                SUMA_RegisterMessage (SUMAg_CF->MessageList,
10913                                   "Failed to merge ROIs.", FuncName,
10914                                   SMT_Critical, SMA_LogAndPopup);
10915                if (ROIlink) SUMA_FreeROIDatum((void *)ROIlink); ROIlink = NULL;
10916                SUMA_RETURN(ROId);
10917          }
10918          if (ROIlink) SUMA_FreeROIDatum((void *)ROIlink); ROIlink = NULL;
10919       }else {
10920          SUMA_LH("Nodes connected.");
10921          /* nodes are connected, just add to path */
10922          ++ROId->N_n;
10923          ROId->nPath = (int *) SUMA_realloc (ROId->nPath, sizeof(int)*ROId->N_n);
10924          ROId->nPath[ROId->N_n-1] = bsd->SurfNode;
10925       }
10926 
10927       oElmt = Elmt;
10928    } while (Elmt != dlist_tail(sv->BS));
10929 
10930    SUMA_RETURN (ROId);
10931 }
10932 
10933 
10934 /*!
10935 Executes an action
10936 Adds it to the action stack
10937 Update the action stack pointer
10938 DO not do StckPos = SUMA_PushActionStack (..., StackPos, ....);
10939 that is because the function might return NULL if something failed and you'd lose the current stack position for good.
10940 */
SUMA_PushActionStack(DList * ActionStack,DListElmt * StackPos,SUMA_ACTION_RESULT (* ActionFunction)(void * ActionData,SUMA_ACTION_POLARITY Pol),void * ActionData,void (* ActionDataDestructor)(void * Actiondata))10941 DListElmt * SUMA_PushActionStack (DList *ActionStack, DListElmt *StackPos,
10942                                   SUMA_ACTION_RESULT (*ActionFunction)(void *ActionData, SUMA_ACTION_POLARITY Pol),
10943                                   void *ActionData,
10944                                   void (*ActionDataDestructor)(void *Actiondata))
10945 {
10946    static char FuncName[]={"SUMA_PushActionStack"};
10947    SUMA_Boolean LocalHead = NOPE;
10948    SUMA_ACTION_STACK_DATA *AS_data=NULL;
10949    SUMA_ACTION_RESULT ActionResult = SAR_Undefined;
10950 
10951    SUMA_ENTRY;
10952 
10953    /* execute action */
10954    if (LocalHead) fprintf (SUMA_STDERR, "%s: Executing Action.\n", FuncName);
10955    ActionResult = ActionFunction (ActionData, SAP_Do);
10956    switch (ActionResult) {
10957       case SAR_Fail:
10958          SUMA_SLP_Err("Action failed.");
10959          SUMA_RETURN(NULL);
10960          break;
10961       case SAR_Succeed:
10962          break;
10963       default:
10964          SUMA_SLP_Err("Action result not understood.");
10965          SUMA_RETURN(NULL);
10966          break;
10967    }
10968 
10969    /* remove all elements above the position in the stack */
10970    if (LocalHead) fprintf (SUMA_STDERR, "%s: Removing Action Stack Elements above current position.\n", FuncName);
10971    while (StackPos != dlist_tail(ActionStack)) {
10972       void *asdata=NULL;
10973 
10974       dlist_remove(ActionStack, dlist_tail(ActionStack), &asdata);
10975 
10976       /* now free the asdata */
10977       SUMA_FreeActionStackData(asdata);
10978    }
10979 
10980    /* Pushing the action and its data onto the stack */
10981    if (LocalHead) fprintf (SUMA_STDERR, "%s: Pushing the action and its data onto the stack.\n", FuncName);
10982    AS_data = (SUMA_ACTION_STACK_DATA *)
10983                   SUMA_calloc(1,sizeof(SUMA_ACTION_STACK_DATA));
10984    AS_data->ActionDataDestructor = ActionDataDestructor;
10985    AS_data->ActionFunction = ActionFunction;
10986    AS_data->ActionData = ActionData;
10987    dlist_ins_next (ActionStack, dlist_tail(ActionStack), (void*)AS_data);
10988    if (LocalHead) fprintf (SUMA_STDERR, "%s: Updating StackPos ...\n", FuncName);
10989    StackPos = dlist_tail(ActionStack);
10990 
10991    SUMA_RETURN(StackPos);
10992 }
10993 
10994 /*!
10995    Redo an action of the ActionStack
10996    If StackPos is NULL, it is assumed that you are at the bottom of the stack
10997 */
SUMA_RedoAction(DList * ActionStack,DListElmt * StackPos)10998 DListElmt * SUMA_RedoAction (DList *ActionStack, DListElmt *StackPos)
10999 {
11000    static char FuncName[]={"SUMA_RedoAction"};
11001    SUMA_ACTION_STACK_DATA *AS_data=NULL;
11002    SUMA_ACTION_RESULT ActionResult = SAR_Undefined;
11003    SUMA_Boolean LocalHead = NOPE;
11004 
11005    SUMA_ENTRY;
11006 
11007    if (!StackPos) {
11008       if (LocalHead) fprintf (SUMA_STDERR, "%s: At bottom of stack. Working up.\n", FuncName);
11009       StackPos = dlist_head(ActionStack);
11010    } else if (StackPos == dlist_tail(ActionStack)) {
11011       SUMA_SLP_Err("At top of stack, nothing to do.");
11012       SUMA_RETURN(StackPos);
11013    } else {
11014       StackPos = dlist_next(StackPos);
11015    }
11016 
11017    /* execute action above StackPos again */
11018    AS_data = (SUMA_ACTION_STACK_DATA *)StackPos->data;
11019    ActionResult = AS_data->ActionFunction (AS_data->ActionData, SAP_Redo);
11020    switch (ActionResult) {
11021       case SAR_Fail:
11022          SUMA_SLP_Err("Action failed.");
11023          SUMA_RETURN(NULL);
11024          break;
11025       case SAR_Succeed:
11026          break;
11027       default:
11028          SUMA_SLP_Err("Action result not understood.");
11029          SUMA_RETURN(NULL);
11030          break;
11031    }
11032 
11033    SUMA_RETURN(StackPos);
11034 }
11035 /*!
11036    Undo an action on the ActionStack
11037 
11038    \returns StackNewPos = StackPos if reached the bottom of the stack.
11039                      It is your job to make sure this function is not called again.
11040                         = StackPos->prev if all went well
11041                         =NULL if trouble
11042 
11043 */
SUMA_UndoAction(DList * ActionStack,DListElmt * StackPos)11044 DListElmt * SUMA_UndoAction (DList *ActionStack, DListElmt *StackPos)
11045 {
11046    static char FuncName[]={"SUMA_UndoAction"};
11047    SUMA_ACTION_STACK_DATA *AS_data=NULL;
11048    SUMA_ACTION_RESULT ActionResult = SAR_Undefined;
11049    SUMA_Boolean LocalHead = NOPE;
11050 
11051    SUMA_ENTRY;
11052 
11053    if (!StackPos) {
11054       SUMA_SLP_Err("At bottom of stack.");
11055       SUMA_RETURN(StackPos);
11056    }
11057 
11058    AS_data = (SUMA_ACTION_STACK_DATA *)StackPos->data;
11059 
11060    /* execute reverse of action */
11061    ActionResult = AS_data->ActionFunction (AS_data->ActionData, SAP_Undo);
11062    switch (ActionResult) {
11063       case SAR_Fail:
11064          SUMA_SLP_Err("Action failed.");
11065          SUMA_RETURN(NULL);
11066          break;
11067       case SAR_Succeed:
11068          break;
11069       default:
11070          SUMA_SLP_Err("Action result not understood.");
11071          SUMA_RETURN(NULL);
11072          break;
11073    }
11074 
11075    /* now move StackPos down */
11076    if (StackPos == dlist_head(ActionStack)) {
11077       /* do nothing to StackPos */
11078    } else {
11079       StackPos = dlist_prev(StackPos);
11080    }
11081 
11082    SUMA_RETURN(StackPos);
11083 }
11084 
11085 /*!
11086    \brief Mark an ROI as finished
11087 */
SUMA_FinishedROI(void * data,SUMA_ACTION_POLARITY Pol)11088 SUMA_ACTION_RESULT SUMA_FinishedROI (void *data, SUMA_ACTION_POLARITY Pol)
11089 {
11090    static char FuncName[]={"SUMA_FinishedROI"};
11091    SUMA_ROI_ACTION_STRUCT *ROIA=NULL;
11092    SUMA_SurfaceObject *SOparent=NULL;
11093    SUMA_Boolean LocalHead = NOPE;
11094 
11095    SUMA_ENTRY;
11096 
11097    ROIA = (SUMA_ROI_ACTION_STRUCT *)data;
11098 
11099    switch (Pol) {
11100       case SAP_Do:
11101       case SAP_Redo:
11102          if (LocalHead)
11103             fprintf (SUMA_STDERR, "%s: Marking as finished...\n", FuncName);
11104          /* set the drawing status */
11105          ROIA->DrawnROI->DrawStatus = SUMA_ROI_Finished;
11106 
11107          SOparent = SUMA_findSOp_inDOv(ROIA->DrawnROI->Parent_idcode_str,
11108                                        SUMAg_DOv, SUMAg_N_DOv);
11109          if (!SOparent) {
11110             SUMA_SLP_Warn( "Parent surface\n"
11111                            "not found for ROI\n"
11112                            "No contour will\n"
11113                            "be determined." );
11114             SUMA_RETURN(SAR_Succeed);
11115          }else {
11116             /* calculate the contours */
11117             if (!ROIA->DrawnROI->CE) { /* must create contour */
11118                int *Nodes, N_Nodes;
11119                SUMA_Boolean Unique = NOPE;
11120 
11121                SUMA_LH("Getting Contour ");
11122                N_Nodes = 0;
11123                Unique = YUP; /* Set to YUP if you have node
11124                                  indices listed more than once. */
11125                Nodes = SUMA_NodesInROI (ROIA->DrawnROI, &N_Nodes, Unique);
11126                if (Nodes) {
11127                   ROIA->DrawnROI->CE = SUMA_GetContour (
11128                                  SOparent,
11129                                  Nodes, N_Nodes, &(ROIA->DrawnROI->N_CE),
11130                                  0, NULL, NULL, 1);
11131                   if (!ROIA->DrawnROI->CE) { SUMA_LH("Null DrawnROI->CE"); }
11132                   else { SUMA_LH("Good DrawnROI->CE"); }
11133                   SUMA_free(Nodes);
11134                }
11135             }else {
11136                SUMA_SLP_Err("Unexpected Contour");
11137                SUMA_RETURN(SAR_Fail);
11138             }
11139          }
11140 
11141          break;
11142       case SAP_Undo:
11143          if (LocalHead)
11144             fprintf (SUMA_STDERR, "%s: Marking as InCreation...\n", FuncName);
11145          ROIA->DrawnROI->DrawStatus = SUMA_ROI_InCreation;
11146          /* remove any contour if present */
11147          if (ROIA->DrawnROI->CE) SUMA_free(ROIA->DrawnROI->CE);
11148          ROIA->DrawnROI->CE = NULL;
11149          ROIA->DrawnROI->N_CE = -1;
11150          break;
11151       default:
11152          fprintf (SUMA_STDERR, "Error %s: Should not be here.\n", FuncName);
11153          break;
11154    }
11155 
11156    SUMA_RETURN(SAR_Succeed);
11157 }
11158 
11159 /*!
11160    \brief This function is like SUMA_AddToTailROIDatum, except that it also updates the type of the ROI
11161    to be a filled one. You call this function when you are adding an ROIDatum that fills a closed  path
11162    \param data (void *) of SUMA_ROI_ACTION_STRUCT * containing the ROIlist and the ROIdatum
11163    \param Pol (SUMA_ACTION_POLARITY) SAP_Do, SAP_Redo, SAP_Undo
11164 */
11165 
SUMA_AddFillROIDatum(void * data,SUMA_ACTION_POLARITY Pol)11166 SUMA_ACTION_RESULT SUMA_AddFillROIDatum (void *data, SUMA_ACTION_POLARITY Pol)
11167 {
11168    static char FuncName[]={"SUMA_AddFillROIDatum"};
11169    SUMA_Boolean LocalHead = NOPE;
11170    SUMA_ROI_ACTION_STRUCT *ROIA=NULL;
11171    void *eldata=NULL;
11172    DListElmt *tail_elm=NULL;
11173    SUMA_ROI_DATUM *ROId=NULL;
11174 
11175    SUMA_ENTRY;
11176 
11177    ROIA = (SUMA_ROI_ACTION_STRUCT *)data;
11178 
11179    switch (Pol) {
11180       case SAP_Do:
11181       case SAP_Redo:
11182          if (LocalHead) fprintf (SUMA_STDERR, "%s: Adding to ROIstrokelist...\n", FuncName);
11183          dlist_ins_next(ROIA->DrawnROI->ROIstrokelist, dlist_tail(ROIA->DrawnROI->ROIstrokelist), (void *)ROIA->ROId);
11184          ROIA->DrawnROI->Type = SUMA_ROI_FilledArea;
11185          break;
11186       case SAP_Undo:
11187          if (LocalHead) fprintf (SUMA_STDERR, "%s: Removing from ROIstrokelist...\n", FuncName);
11188          dlist_remove(ROIA->DrawnROI->ROIstrokelist, dlist_tail(ROIA->DrawnROI->ROIstrokelist), &eldata);
11189          /* eldata contains the ROIdatum that has been removed from the list. It should not be freed here
11190          This structure is to remain in the stack for later usage.*/
11191          /* if the tail is a segment, then turn the ROI type to a closedpath */
11192          tail_elm = dlist_tail(ROIA->DrawnROI->ROIstrokelist);
11193          ROId = (SUMA_ROI_DATUM *)tail_elm->data;
11194          if (ROId->Type == SUMA_ROI_NodeSegment) { /* we are no longer dealing with filled ROI */
11195             ROIA->DrawnROI->Type = SUMA_ROI_ClosedPath;
11196          }
11197          break;
11198       default:
11199          fprintf (SUMA_STDERR, "Error %s: Should not be here.\n", FuncName);
11200          break;
11201    }
11202 
11203    SUMA_RETURN(SAR_Succeed);
11204 }
11205 
11206 /*!
11207    \brief This function is like SUMA_AddToTailROIDatum, except that it also updates the type of the ROI
11208    to be a closed one. You call this function when you are addind the last ROIDatum that closes the path
11209    \param data (void *) of SUMA_ROI_ACTION_STRUCT * containing the ROIlist and the ROIdatum
11210    \param Pol (SUMA_ACTION_POLARITY) SAP_Do, SAP_Redo, SAP_Undo
11211 */
SUMA_AddToTailJunctionROIDatum(void * data,SUMA_ACTION_POLARITY Pol)11212 SUMA_ACTION_RESULT SUMA_AddToTailJunctionROIDatum (void *data,
11213                                                    SUMA_ACTION_POLARITY Pol)
11214 {
11215    static char FuncName[]={"SUMA_AddToTailJunctionROIDatum"};
11216    SUMA_Boolean LocalHead = NOPE;
11217    SUMA_ROI_ACTION_STRUCT *ROIA=NULL;
11218    void *eldata=NULL;
11219 
11220    SUMA_ENTRY;
11221 
11222    ROIA = (SUMA_ROI_ACTION_STRUCT *)data;
11223 
11224    switch (Pol) {
11225       case SAP_Do:
11226       case SAP_Redo:
11227          if (ROIA->DrawnROI->Type == SUMA_ROI_ClosedPath) {
11228             SUMA_SLP_Err ("Trying to close a closed path!");
11229             SUMA_RETURN(SAR_Fail);
11230          }
11231          if (LocalHead) fprintf (SUMA_STDERR, "%s: Adding to ROIstrokelist...\n", FuncName);
11232          dlist_ins_next(ROIA->DrawnROI->ROIstrokelist, dlist_tail(ROIA->DrawnROI->ROIstrokelist), (void *)ROIA->ROId);
11233          ROIA->DrawnROI->Type = SUMA_ROI_ClosedPath;
11234          break;
11235       case SAP_Undo:
11236          if (ROIA->DrawnROI->Type == SUMA_ROI_OpenPath) {
11237             SUMA_SLP_Err ("Trying to open an open path!");
11238             SUMA_RETURN(SAR_Fail);
11239          }
11240          if (LocalHead)
11241             fprintf (SUMA_STDERR, "%s: Removing from ROIstrokelist...\n",
11242                                              FuncName);
11243          dlist_remove(ROIA->DrawnROI->ROIstrokelist,
11244                       dlist_tail(ROIA->DrawnROI->ROIstrokelist), &eldata);
11245          /* eldata contains the ROIdatum that has been removed from the list.
11246             It should not be freed here
11247             This structure is to remain in the stack for later usage.*/
11248          ROIA->DrawnROI->Type = SUMA_ROI_OpenPath;
11249          break;
11250       default:
11251          fprintf (SUMA_STDERR, "Error %s: Should not be here.\n", FuncName);
11252          break;
11253    }
11254 
11255    SUMA_RETURN(SAR_Succeed);
11256 }
11257 
11258 
11259 /*!
11260    \brief Adds (or removes) an ROIdatum to the tail of the ROI list
11261 
11262    \param data (void *) of SUMA_ROI_ACTION_STRUCT * containing the ROIlist and the ROIdatum
11263    \param Pol (SUMA_ACTION_POLARITY) SAP_Do, SAP_Redo, SAP_Undo
11264 */
SUMA_AddToTailROIDatum(void * data,SUMA_ACTION_POLARITY Pol)11265 SUMA_ACTION_RESULT SUMA_AddToTailROIDatum (void *data, SUMA_ACTION_POLARITY Pol)
11266 {
11267    static char FuncName[]={"SUMA_AddToTailROIDatum"};
11268    SUMA_Boolean LocalHead = NOPE;
11269    SUMA_ROI_ACTION_STRUCT *ROIA=NULL;
11270    void *eldata=NULL;
11271 
11272    SUMA_ENTRY;
11273 
11274    ROIA = (SUMA_ROI_ACTION_STRUCT *)data;
11275 
11276    switch (Pol) {
11277       case SAP_Do:
11278       case SAP_Redo:
11279          if (LocalHead) fprintf (SUMA_STDERR, "%s: Adding to ROIstrokelist...\n", FuncName);
11280          dlist_ins_next(ROIA->DrawnROI->ROIstrokelist, dlist_tail(ROIA->DrawnROI->ROIstrokelist), (void *)ROIA->ROId);
11281          break;
11282       case SAP_Undo:
11283          if (LocalHead) fprintf (SUMA_STDERR, "%s: Removing from ROIstrokelist...\n", FuncName);
11284          dlist_remove(ROIA->DrawnROI->ROIstrokelist, dlist_tail(ROIA->DrawnROI->ROIstrokelist), &eldata);
11285          /* eldata contains the ROIdatum that has been removed from the list. It should not be freed here
11286          This structure is to remain in the stack for later usage.*/
11287          break;
11288       default:
11289          fprintf (SUMA_STDERR, "Error %s: Should not be here.\n", FuncName);
11290          break;
11291    }
11292 
11293    SUMA_RETURN(SAR_Succeed);
11294 }
11295 
11296 /*!
11297    A function to destroy the ROI action data structure.
11298 
11299    \param data (void *) of SUMA_ROI_ACTION_STRUCT * containing the ROIlist and the ROIdatum
11300 
11301    -  Only  ROIA->ROId and ROIA are freed. ROIA->DrawnROI should be freed when the DrawnROI list is destroyed.
11302 */
11303 
SUMA_DestroyROIActionData(void * data)11304 void SUMA_DestroyROIActionData (void *data)
11305 {
11306    static char FuncName[]={"SUMA_DestroyROIActionData"};
11307    SUMA_Boolean LocalHead = NOPE;
11308    SUMA_ROI_ACTION_STRUCT *ROIA=NULL;
11309 
11310    SUMA_ENTRY;
11311 
11312    ROIA = (SUMA_ROI_ACTION_STRUCT *)data;
11313 
11314    if (!ROIA) SUMA_RETURNe;
11315 
11316    if (ROIA->ROId) { /* free the ROI datum */
11317       SUMA_FreeROIDatum ((void *)ROIA->ROId);
11318       ROIA->ROId = NULL;
11319    }
11320 
11321    ROIA->DrawnROI = NULL; /* this should not be freed here, it is freed when the function for destroying DrawnROI is called */
11322    SUMA_free(ROIA);
11323 
11324    SUMA_RETURNe;
11325 }
11326 
11327 /*!
11328    \brief set the position of light0
11329    \param s (char *) a strng containing X, Y, Z coordinates
11330    \param data (void *) a typecast of the pointer to the surface viewer to be affected
11331 
11332 */
SUMA_SetLight0(char * s,void * data)11333 void SUMA_SetLight0 (char *s, void *data)
11334 {
11335    static char FuncName[]={"SUMA_SetLight0"};
11336    DList *list=NULL;
11337    SUMA_EngineData *ED = NULL;
11338    SUMA_SurfaceViewer *sv = NULL;
11339    float fv3[3];
11340    SUMA_Boolean LocalHead = NOPE;
11341 
11342    SUMA_ENTRY;
11343 
11344    if (!s) SUMA_RETURNe;
11345 
11346    sv = (SUMA_SurfaceViewer *)data;
11347 
11348    /* parse s */
11349    if (SUMA_StringToNum (s, (void*)fv3, 3, 1) != 3) {/*problem,beep and ignore */
11350       XBell (XtDisplay (sv->X->TOPLEVEL), 50);
11351       SUMA_RETURNe;
11352    }
11353 
11354    /* register fv3 with ED */
11355    if (!list) list = SUMA_CreateList();
11356    ED = SUMA_InitializeEngineListData (SE_SetLight0Pos);
11357    if (!SUMA_RegisterEngineListCommand (  list, ED,
11358                                           SEF_fv3, (void *)fv3,
11359                                           SES_Suma, (void *)sv, NOPE,
11360                                           SEI_Tail, NULL )) {
11361       fprintf(SUMA_STDERR,"Error %s: Failed to register command\n", FuncName);
11362       SUMA_RETURNe;
11363    }
11364 
11365    SUMA_REGISTER_TAIL_COMMAND_NO_DATA(list, SE_Redisplay, SES_Suma, sv);
11366 
11367    if (!SUMA_Engine (&list)) {
11368       fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
11369    }
11370 
11371    SUMA_RETURNe;
11372 }
11373 
11374 /*!
11375    \brief sets the number of smoothing operations to perform on foreground colors
11376    before display
11377 */
SUMA_SetNumForeSmoothing(char * s,void * data)11378 void SUMA_SetNumForeSmoothing (char *s, void *data)
11379 {
11380    static char FuncName[]={"SUMA_SetNumForeSmoothing"};
11381    DList *list=NULL;
11382    SUMA_EngineData *ED = NULL;
11383    SUMA_SurfaceViewer *sv = NULL;
11384    float fv3[3];
11385    SUMA_Boolean LocalHead = NOPE;
11386 
11387    SUMA_ENTRY;
11388 
11389    if (!s) SUMA_RETURNe;
11390 
11391    sv = (SUMA_SurfaceViewer *)data;
11392 
11393    /* parse s */
11394    if (SUMA_StringToNum (s, (void *)fv3, 1,1) != 1) {/*problem,beep and ignore */
11395       XBell (XtDisplay (sv->X->TOPLEVEL), 50);
11396       SUMA_RETURNe;
11397    }
11398 
11399    /* set sv */
11400 
11401    if ((int)fv3[0] < 0) {
11402       SUMA_SLP_Err("Only positive integer\nvalues are valid.\n");
11403       SUMA_RETURNe;
11404    }
11405    SUMAg_CF->X->NumForeSmoothing = (int)fv3[0];
11406 
11407    /* flag surfaces for remix */
11408    SUMA_SetAllRemixFlag(SUMAg_SVv, SUMAg_N_SVv);
11409 
11410    /* register a redisplay for sv*/
11411    if (!list) list = SUMA_CreateList();
11412    SUMA_REGISTER_HEAD_COMMAND_NO_DATA( list, SE_Redisplay_AllVisible,
11413                                        SES_Suma, sv);
11414    if (!SUMA_Engine (&list)) {
11415       fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
11416    }
11417 
11418    SUMA_RETURNe;
11419 
11420 }
11421 
SUMA_SetNumFinalSmoothing(char * s,void * data)11422 void SUMA_SetNumFinalSmoothing (char *s, void *data)
11423 {
11424    static char FuncName[]={"SUMA_SetNumFinalSmoothing"};
11425    DList *list=NULL;
11426    SUMA_EngineData *ED = NULL;
11427    SUMA_SurfaceViewer *sv = NULL;
11428    float fv3[3];
11429    SUMA_Boolean LocalHead = NOPE;
11430 
11431    SUMA_ENTRY;
11432 
11433    if (!s) SUMA_RETURNe;
11434 
11435    sv = (SUMA_SurfaceViewer *)data;
11436 
11437    /* parse s */
11438    if (SUMA_StringToNum (s, (void *)fv3, 1,1) != 1) {/*problem,beep and ignore */
11439       XBell (XtDisplay (sv->X->TOPLEVEL), 50);
11440       SUMA_RETURNe;
11441    }
11442 
11443    /* set sv */
11444 
11445    if ((int)fv3[0] < 0) {
11446       SUMA_SLP_Err("Only positive integer\nvalues are valid.\n");
11447       SUMA_RETURNe;
11448    }
11449    SUMAg_CF->X->NumFinalSmoothing = (int)fv3[0];
11450 
11451    /* flag surfaces for remix */
11452    SUMA_SetAllRemixFlag(SUMAg_SVv, SUMAg_N_SVv);
11453 
11454    /* register a redisplay for sv*/
11455    if (!list) list = SUMA_CreateList();
11456    SUMA_REGISTER_HEAD_COMMAND_NO_DATA( list, SE_Redisplay_AllVisible,
11457                                        SES_Suma, sv);
11458    if (!SUMA_Engine (&list)) {
11459       fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
11460    }
11461 
11462    SUMA_RETURNe;
11463 
11464 }
11465 
11466 /*!
11467    \brief Sets the screen clipping plane
11468 
11469    \param s (char *) a strng containing a,b,c,d parameters of plane equation
11470    \param data (void *) a typecast of the pointer to the surface viewer to be affected
11471 
11472 */
SUMA_SetScreenClip(char * s,void * data)11473 void SUMA_SetScreenClip (char *s, void *data)
11474 {
11475    SUMA_SetClip(s,(SUMA_SurfaceViewer *)data, SUMA_SCREEN_CLIP);
11476 }
SUMA_SetObjectClip(char * s,void * data)11477 void SUMA_SetObjectClip (char *s, void *data)
11478 {
11479    SUMA_SetClip(s,(SUMA_SurfaceViewer *)data, SUMA_ALL_OBJECT_CLIP);
11480 }
11481 
SUMA_SetClip(char * s,SUMA_SurfaceViewer * sv,SUMA_CLIP_PLANE_TYPES tp)11482 void SUMA_SetClip (char *s, SUMA_SurfaceViewer *sv, SUMA_CLIP_PLANE_TYPES tp)
11483 {
11484    static char FuncName[]={"SUMA_SetScreenClip"};
11485    DList *list=NULL;
11486    SUMA_EngineData *ED = NULL;
11487    float fv15[15];
11488    int npar=0;
11489    char *sn;
11490    char namebuf[24];
11491    int itmp, ii, it=0;
11492    DListElmt *NextElm= NULL;
11493    SUMA_Boolean LocalHead = NOPE;
11494 
11495    SUMA_ENTRY;
11496 
11497    if (!s) {
11498       SUMA_Show_Clip_Planes(SUMAg_CF, NULL);
11499       SUMA_RETURNe;
11500    }
11501 
11502    /* Get the name, if any */
11503    if ((sn = strstr(s,":"))) {/* found name */
11504       if (sn - s > 7) {
11505          SUMA_SLP_Err("Plane label too long!");
11506          SUMA_RETURNe;
11507       }
11508       ii=0;
11509       while (s[ii] != ':') { namebuf[ii] = s[ii]; ++ii; }/* copy name */
11510       namebuf[ii] = '\0';
11511       itmp = 0; ++ii;
11512       while (s[ii] != '\0') { s[itmp] = s[ii]; ++ii; ++itmp; } /* copy rest */
11513       s[itmp] = '\0';
11514       /* get the equation */
11515       npar = SUMA_StringToNum (s, (void *)fv15, 4, 1);
11516       if (npar != 4 && npar != 2  && npar != 0) { /* problem, beep and ignore */
11517          XBell (XtDisplay (sv->X->TOPLEVEL), 50);
11518          SUMA_RETURNe;
11519       }
11520       if (npar == 2) { /* the z game */
11521          fv15[2] = fv15[0];
11522          fv15[3] = fv15[1];
11523          fv15[0] = 0.0;
11524          fv15[1] = 0.0;
11525       } else if (npar == 0) { /* the nothing */
11526          fv15[0] = 0.0;
11527          fv15[1] = 0.0;
11528          fv15[2] = 0.0;
11529          fv15[3] = 0.0;
11530       }
11531    }else {
11532       SUMA_SLP_Err("Must provide plane label!");
11533       SUMA_RETURNe;
11534    }
11535 
11536 
11537    /* register fv15 with ED */
11538    if (!list) list = SUMA_CreateList();
11539    ED = SUMA_InitializeEngineListData (SE_SetClip);
11540    if (!(NextElm = SUMA_RegisterEngineListCommand (  list, ED,
11541                                           SEF_fv15, (void *)fv15,
11542                                           SES_Suma, (void *)sv, NOPE,
11543                                           SEI_Head, NULL ))) {
11544       fprintf(SUMA_STDERR,"Error %s: Failed to register command\n", FuncName);
11545       SUMA_RETURNe;
11546    }
11547    /* register type expected */
11548    it = (int)tp;
11549    if (!(SUMA_RegisterEngineListCommand (  list, ED,
11550                                           SEF_i, (void*)(&it),
11551                                           SES_Suma, (void *)sv, NOPE,
11552                                           SEI_In, NextElm ))) {
11553       fprintf(SUMA_STDERR,"Error %s: Failed to register command\n", FuncName);
11554       SUMA_RETURNe;
11555    }
11556    /* register name of plane */
11557    if (!(SUMA_RegisterEngineListCommand (  list, ED,
11558                                           SEF_s, (void*)(namebuf),
11559                                           SES_Suma, (void *)sv, NOPE,
11560                                           SEI_In, NextElm ))) {
11561       fprintf(SUMA_STDERR,"Error %s: Failed to register command\n", FuncName);
11562       SUMA_RETURNe;
11563    }
11564    if (!SUMA_Engine (&list)) {
11565       fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
11566    }
11567 
11568    SUMA_RETURNe;
11569 }
11570 
11571 /*!
11572    \brief Sets the rotation center
11573 
11574    \param s (char *) a strng containing X, Y, Z coordinates
11575    \param data (void *) a typecast of the pointer to the surface viewer to
11576                         be affected
11577 
11578    NOTE: This is a bit ugly because there is a jump in surface location with
11579    the change in center of rotation. Something also needs updating to keep this
11580    from happening ...
11581 */
11582 
SUMA_SetRotCenter(char * s,void * data)11583 void SUMA_SetRotCenter (char *s, void *data)
11584 {
11585    static char FuncName[]={"SUMA_SetRotCenter"};
11586    DList *list=NULL;
11587    SUMA_EngineData *ED = NULL;
11588    SUMA_SurfaceViewer *sv = NULL;
11589    float fv3[3];
11590    SUMA_Boolean LocalHead = NOPE;
11591 
11592    SUMA_ENTRY;
11593 
11594    sv = (SUMA_SurfaceViewer *)data;
11595    if (!sv) {
11596       XBell (XtDisplay (sv->X->TOPLEVEL), 50);
11597       SUMA_RETURNe;
11598    }
11599 
11600    if (!s) {
11601       if (!SUMA_UpdateRotaCenter(sv, SUMAg_DOv, SUMAg_N_DOv)) {
11602          fprintf (SUMA_STDERR,
11603                   "Error %s: Failed to update center of rotation", FuncName);
11604          XBell (XtDisplay (sv->X->TOPLEVEL), 50);
11605          SUMA_RETURNe;
11606       }
11607       SUMA_RETURNe;
11608    }
11609 
11610    /* parse s */
11611    if (SUMA_StringToNum (s, (void*)fv3, 3,1) != 3) {/*problem, beep and ignore */
11612       XBell (XtDisplay (sv->X->TOPLEVEL), 50);
11613       SUMA_RETURNe;
11614    }
11615 
11616    sv->GVS[sv->StdView].RotaCenter[0] = fv3[0];
11617    sv->GVS[sv->StdView].RotaCenter[1] = fv3[1];
11618    sv->GVS[sv->StdView].RotaCenter[2] = fv3[2];
11619 
11620 
11621    SUMA_RETURNe;
11622 }
11623 
11624 /*!
11625    \brief rotates surface to face a certain coordinate
11626 
11627    \param s (char *) a strng containing X, Y, Z coordinates
11628    \param data (void *) a typecast of the pointer to the surface viewer to be affected
11629 
11630 */
SUMA_LookAtCoordinates(char * s,void * data)11631 void SUMA_LookAtCoordinates (char *s, void *data)
11632 {
11633    static char FuncName[]={"SUMA_LookAtCoordinates"};
11634    DList *list=NULL;
11635    SUMA_EngineData *ED = NULL;
11636    SUMA_SurfaceViewer *sv = NULL;
11637    float fv3[3];
11638    SUMA_Boolean LocalHead = NOPE;
11639 
11640    SUMA_ENTRY;
11641 
11642    if (!s) SUMA_RETURNe;
11643 
11644    sv = (SUMA_SurfaceViewer *)data;
11645 
11646    /* parse s */
11647    if (SUMA_StringToNum (s, (void *)fv3, 3,1) != 3) {/*problem,beep and ignore */
11648       XBell (XtDisplay (sv->X->TOPLEVEL), 50);
11649       SUMA_RETURNe;
11650    }
11651 
11652    /* register fv3 with ED */
11653    if (!list) list = SUMA_CreateList();
11654    ED = SUMA_InitializeEngineListData (SE_SetLookAt);
11655    if (!SUMA_RegisterEngineListCommand (  list, ED,
11656                                           SEF_fv3, (void *)fv3,
11657                                           SES_Suma, (void *)sv, NOPE,
11658                                           SEI_Head, NULL )) {
11659       fprintf(SUMA_STDERR,"Error %s: Failed to register command\n", FuncName);
11660       SUMA_RETURNe;
11661    }
11662    if (!SUMA_Engine (&list)) {
11663       fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
11664    }
11665 
11666    SUMA_RETURNe;
11667 }
11668 
SUMA_SV_SetRenderOrder(char * s,void * data)11669 void SUMA_SV_SetRenderOrder(char *s, void *data)
11670 {
11671    static char FuncName[]={"SUMA_SV_SetRenderOrder"};
11672    SUMA_SurfaceViewer *sv = NULL;
11673    SUMA_Boolean LocalHead = NOPE;
11674 
11675    SUMA_ENTRY;
11676 
11677    if (!s) SUMA_RETURNe;
11678 
11679    sv = (SUMA_SurfaceViewer *)data;
11680    if (!sv) {
11681       SUMA_S_Err("Null sv");
11682       SUMA_RETURNe;
11683    }
11684 
11685    sv->N_otseq = SUMA_SetObjectDisplayOrder(s, sv->otseq);
11686 
11687    SUMA_RETURNe;
11688 }
11689 
SUMA_JumpIndex(char * s,void * data)11690 void SUMA_JumpIndex (char *s, void *data)
11691 {
11692    static char FuncName[]={"SUMA_JumpIndex"};
11693    SUMA_SurfaceViewer *sv = NULL;
11694    SUMA_ALL_DO *ado=NULL;
11695    char *variant = NULL;
11696    SUMA_Boolean LocalHead = NOPE;
11697 
11698    SUMA_ENTRY;
11699 
11700    if (!s) SUMA_RETURNe;
11701 
11702    sv = (SUMA_SurfaceViewer *)data;
11703    if (!(ado = SUMA_SV_Focus_ADO(sv))) {
11704       SUMA_S_Err("No ado in focus");
11705       SUMA_RETURNe;
11706    }
11707 
11708    switch (ado->do_type) {
11709       case SO_type:
11710          SUMA_JumpIndex_SO (s, sv, (SUMA_SurfaceObject *)ado);
11711          break;
11712       case GDSET_type:
11713          SUMA_JumpIndex_GDSET (s, sv, (SUMA_DSET *)ado, variant);
11714          break;
11715       case CDOM_type:
11716          SUMA_JumpIndex_CO (s, sv, (SUMA_CIFTI_DO *)ado);
11717          break;
11718       case GRAPH_LINK_type: {
11719          SUMA_GraphLinkDO *gldo=(SUMA_GraphLinkDO *)ado;
11720          SUMA_DSET *dset=NULL;
11721          if (!(dset=SUMA_find_GLDO_Dset(gldo))) {
11722             SUMA_S_Errv("Failed to find dset for gldo %s!!!\n",
11723                         SUMA_ADO_Label(ado));
11724             break;
11725          }
11726          SUMA_JumpIndex_GDSET (s, sv, dset, gldo->variant);
11727          break; }
11728       case TRACT_type: {
11729          SUMA_JumpIndex_TDO (s, sv, (SUMA_TractDO *)ado);
11730          break; }
11731       case MASK_type: {
11732          SUMA_JumpIndex_MDO (s, sv, (SUMA_MaskDO *)ado);
11733          break; }
11734       case VO_type: {
11735          SUMA_JumpIndex_VO (s, sv, (SUMA_VolumeObject *)ado);
11736          break; }
11737       default:
11738          SUMA_S_Errv("For %s nothing my dear\n",
11739             SUMA_ObjectTypeCode2ObjectTypeName(ado->do_type));
11740          break;
11741    }
11742    SUMA_RETURNe;
11743 }
11744 
11745 /*!
11746    \brief sends the cross hair to a certain node index
11747    \param s (char *) a string containing node index
11748    \param data (void *) a typecast of the pointer to the surface
11749                         viewer to be affected
11750 
11751 */
SUMA_JumpIndex_SO(char * s,SUMA_SurfaceViewer * sv,SUMA_SurfaceObject * SO)11752 void SUMA_JumpIndex_SO (char *s, SUMA_SurfaceViewer *sv, SUMA_SurfaceObject *SO)
11753 {
11754    static char FuncName[]={"SUMA_JumpIndex_SO"};
11755    DList *list=NULL;
11756    SUMA_EngineData *ED = NULL;
11757    DListElmt *el=NULL, *Location=NULL;
11758    SUMA_SurfaceObject  *SOc=NULL;
11759    SUMA_SO_SIDE sd=SUMA_NO_SIDE;
11760    float fv3[3];
11761    int it, iv3[3];
11762    SUMA_Boolean LocalHead = NOPE;
11763 
11764    SUMA_ENTRY;
11765 
11766    if (!s || !sv || !SO) SUMA_RETURNe;
11767 
11768   /* HERE you should check if you have an L or R at the beginning
11769    or end of s.
11770    If you do, then first see if the side of SO (the focus surface)
11771    is the same as the letter. If it is, proceed. If it is not,
11772    try to get the contralateral surface with SUMA_Contralateral_SO
11773    then set the contralateral as the focus surface, then proceed
11774    with setting the focus node. Needs more work
11775    */
11776    /* parse s */
11777    SUMA_LHv("Parsing %s\n", s);
11778    if (SUMA_StringToNumSide(s, (void*)fv3, 1,1, &sd) != 1) {
11779                                     /*problem, beep and ignore */
11780       XBell (XtDisplay (sv->X->TOPLEVEL), 50);
11781       SUMA_RETURNe;
11782    }
11783 
11784    /* do we have side match with Focus node? */
11785    SUMA_LHv("Side of jump is %d, SO %s side %d\n", sd, SO->Label, SO->Side);
11786    if (sd == SUMA_RIGHT || sd == SUMA_LEFT) {
11787       if ((SO->Side == SUMA_RIGHT || SO->Side == SUMA_LEFT) &&
11788             SO->Side != sd) {
11789          /* Need to swith sides */
11790          if ((SOc = SUMA_Contralateral_SO(SO, SUMAg_DOv, SUMAg_N_DOv))) {
11791             sv->Focus_DO_ID = SUMA_findSO_inDOv(SOc->idcode_str,
11792                                              SUMAg_DOv, SUMAg_N_DOv);
11793             SUMA_LHv("Jumping to %s (contralateral of %s)\n",
11794                   SOc->Label, SO->Label);
11795             SO = SOc;
11796          } else {
11797             SUMA_S_Errv("Failed to find contralateral surface to %s\n"
11798                         "Ignoring jump to node's side marker\n",
11799                         SO->Label);
11800          }
11801       }
11802    }
11803 
11804 
11805    /* Set the Nodeselection  */
11806    it = (int) fv3[0];
11807    if (!list) list = SUMA_CreateList ();
11808    ED = SUMA_InitializeEngineListData (SE_SetSelectedNode);
11809    if (!(el = SUMA_RegisterEngineListCommand (  list, ED,
11810                                           SEF_i, (void*)(&it),
11811                                           SES_Suma, (void *)sv, NOPE,
11812                                           SEI_Head, NULL))) {
11813       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
11814       SUMA_RETURNe;
11815    } else {
11816       SUMA_RegisterEngineListCommand (  list, ED,
11817                                           SEF_ngr, NULL,
11818                                           SES_Suma, (void *)sv, NOPE,
11819                                           SEI_In, el);
11820    }
11821 
11822 
11823    /* Now set the cross hair position at the selected node*/
11824    ED = SUMA_InitializeEngineListData (SE_SetCrossHair);
11825    if (!(Location=SUMA_RegisterEngineListCommand (  list, ED,
11826                                           SEF_fv3, (void*)&(SO->NodeList[3*it]),
11827                                           SES_Suma, (void *)sv, NOPE,
11828                                           SEI_Head, NULL))) {
11829       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
11830       SUMA_RETURNe;
11831    }
11832    /* and add the SO with this location, needed for VisX business*/
11833    SUMA_RegisterEngineListCommand (  list, ED,
11834                                            SEF_vp, (void *)SO,
11835                                            SES_Suma, (void *)sv, NOPE,
11836                                            SEI_In, Location);
11837 
11838    /* attach the cross hair to the selected surface */
11839    iv3[0] = SUMA_findSO_inDOv(SO->idcode_str, SUMAg_DOv, SUMAg_N_DOv);
11840    iv3[1] = it;
11841    iv3[2] = -1;
11842    ED = SUMA_InitializeEngineListData (SE_BindCrossHair);
11843    if (!SUMA_RegisterEngineListCommand (  list, ED,
11844                                           SEF_iv3, (void*)iv3,
11845                                           SES_Suma, (void *)sv, NOPE,
11846                                           SEI_Head, NULL)) {
11847       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
11848       SUMA_RETURNe;
11849    }
11850 
11851    /* check to see if AFNI needs to be notified */
11852    if (SUMAg_CF->Connected_v[SUMA_AFNI_STREAM_INDEX] && sv->LinkAfniCrossHair) {
11853       if (LocalHead)
11854          fprintf(SUMA_STDERR,"%s: Notifying Afni of CrossHair XYZ\n", FuncName);
11855       /* register a call to SetAfniCrossHair */
11856       it = 0; /* Set to 1 if you want instacorr notice to AFNI */
11857       ED = SUMA_InitializeEngineListData (SE_SetAfniCrossHair);
11858       if (!SUMA_RegisterEngineListCommand (  list, ED,
11859                                           SEF_i, (void*)&it,
11860                                           SES_Suma, (void *)sv, NOPE,
11861                                           SEI_Tail, NULL)) {
11862          fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
11863          SUMA_RETURNe;
11864       }
11865 
11866    }
11867 
11868    /* and if GICOR needs some love */
11869    if (  SUMAg_CF->Connected_v[SUMA_GICORR_LINE] &&
11870          SUMAg_CF->giset && !SUMAg_CF->HoldClickCallbacks) {
11871       if (LocalHead)
11872          fprintf(SUMA_STDERR,
11873                   "%s: Notifying GICOR of node selection\n", FuncName);
11874       /* register a call to SetGICORnode */
11875       SUMA_REGISTER_TAIL_COMMAND_NO_DATA( list, SE_SetGICORnode,
11876                                           SES_Suma, sv);
11877    }else {
11878       SUMA_LHv("No Notification to GICOR. %d %p\n",
11879                   SUMAg_CF->Connected_v[SUMA_GICORR_LINE], SUMAg_CF->giset);
11880    }
11881 
11882    /* call with the list */
11883    if (!SUMA_Engine (&list)) {
11884       fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
11885       SUMA_RETURNe;
11886    }
11887 
11888    /* now put in a request for locking cross hair but you must do this
11889    after the node selection has been executed
11890    NOTE: You do not always have SetNodeElem because the list might get emptied in
11891    the call to AFNI notification.
11892    You should just put the next call at the end of the list.*/
11893    if (!list) list = SUMA_CreateList();
11894    ED = SUMA_InitializeEngineListData (SE_LockCrossHair);
11895    if (!SUMA_RegisterEngineListCommand (  list, ED,
11896                                           SEF_iv3, (void*)iv3,
11897                                           SES_Suma, (void *)sv, NOPE,
11898                                           SEI_Tail, NULL)) {
11899       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
11900       SUMA_RETURNe;
11901    }
11902 
11903    /* call with the list */
11904    if (!SUMA_Engine (&list)) {
11905       fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
11906       SUMA_RETURNe;
11907    }
11908 
11909    /* redisplay curent only*/
11910    sv->ResetGLStateVariables = YUP;
11911    SUMA_handleRedisplay((XtPointer)sv->X->GLXAREA);
11912 
11913    SUMA_RETURNe;
11914 
11915 }
11916 
11917 /* Jump to a certain edge on a graph dset */
SUMA_JumpIndex_GDSET(char * s,SUMA_SurfaceViewer * sv,SUMA_DSET * dset,char * variant)11918 void SUMA_JumpIndex_GDSET (char *s, SUMA_SurfaceViewer *sv,
11919                            SUMA_DSET *dset, char *variant)
11920 {
11921    static char FuncName[]={"SUMA_JumpIndex_GDSET"};
11922    DList *list=NULL;
11923    DListElmt *el=NULL, *Location=NULL;
11924    SUMA_EngineData *ED = NULL;
11925    float fv3[3];
11926    int it, iv3[3];
11927    SUMA_Boolean LocalHead = NOPE;
11928 
11929    SUMA_ENTRY;
11930 
11931    if (!s || !sv) SUMA_RETURNe;
11932 
11933    /* parse s */
11934    SUMA_LHv("Parsing %s\n", s);
11935    if (SUMA_StringToNum(s, (void*)fv3, 1,1) != 1) {
11936                                     /*problem, beep and ignore */
11937       XBell (XtDisplay (sv->X->TOPLEVEL), 50);
11938       SUMA_RETURNe;
11939    }
11940 
11941    /* Set the Nodeselection  */
11942    it = (int) fv3[0];
11943    if (!list) list = SUMA_CreateList ();
11944    ED = SUMA_InitializeEngineListData (SE_SetSelectedNode);
11945    if (!(el = SUMA_RegisterEngineListCommand (  list, ED,
11946                                           SEF_i, (void*)(&it),
11947                                           SES_Suma, (void *)sv, NOPE,
11948                                           SEI_Head, NULL))) {
11949       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
11950       SUMA_RETURNe;
11951    } else {
11952       SUMA_RegisterEngineListCommand (  list, ED,
11953                                           SEF_ngr, NULL,
11954                                           SES_Suma, (void *)sv, NOPE,
11955                                           SEI_In, el);
11956    }
11957 
11958 
11959    /* Now set the cross hair position at the selected node*/
11960    ED = SUMA_InitializeEngineListData (SE_SetCrossHair);
11961    if (!(Location=SUMA_RegisterEngineListCommand (  list, ED,
11962                                           SEF_fv3,
11963                             (void*)(SUMA_GDSET_NodeXYZ(dset, it, variant, NULL)),
11964                                           SES_Suma, (void *)sv, NOPE,
11965                                           SEI_Head, NULL))) {
11966       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
11967       SUMA_RETURNe;
11968    }
11969    /* and add the object with this location */
11970    SUMA_RegisterEngineListCommand (  list, ED,
11971                                            SEF_vp, (void *)dset,
11972                                            SES_Suma, (void *)sv, NOPE,
11973                                            SEI_In, Location);
11974 
11975    /* attach the cross hair to the selected object
11976       Note that binding here is to edge of graph and not to a node*/
11977    SUMA_find_Dset_GLDO(dset,variant, iv3); /* this will set iv3[0] */
11978    iv3[1] = it;
11979    iv3[2] = -1;
11980    ED = SUMA_InitializeEngineListData (SE_BindCrossHair);
11981    if (!SUMA_RegisterEngineListCommand (  list, ED,
11982                                           SEF_iv3, (void*)iv3,
11983                                           SES_Suma, (void *)sv, NOPE,
11984                                           SEI_Head, NULL)) {
11985       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
11986       SUMA_RETURNe;
11987    }
11988 
11989    /* check to see if AFNI needs to be notified */
11990    if (SUMAg_CF->Connected_v[SUMA_AFNI_STREAM_INDEX] && sv->LinkAfniCrossHair) {
11991       if (LocalHead)
11992          fprintf(SUMA_STDERR,"%s: Notifying Afni of CrossHair XYZ\n", FuncName);
11993       it = 0;
11994       ED = SUMA_InitializeEngineListData (SE_SetAfniCrossHair);
11995       if (!SUMA_RegisterEngineListCommand (  list, ED,
11996                                           SEF_i, (void*)&it,
11997                                           SES_Suma, (void *)sv, NOPE,
11998                                           SEI_Tail, NULL)) {
11999          fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
12000          SUMA_RETURNe;
12001       }
12002 
12003    }
12004 
12005    /* call with the list */
12006    if (!SUMA_Engine (&list)) {
12007       fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
12008       SUMA_RETURNe;
12009    }
12010 
12011    /* now put in a request for locking cross hair but you must do this
12012    after the node selection has been executed
12013    NOTE: You do not always have SetNodeElem because the list might get emptied in
12014    the call to AFNI notification.
12015    You should just put the next call at the end of the list.*/
12016    if (!list) list = SUMA_CreateList();
12017    ED = SUMA_InitializeEngineListData (SE_LockCrossHair);
12018    if (!SUMA_RegisterEngineListCommand (  list, ED,
12019                                           SEF_iv3, (void*)iv3,
12020                                           SES_Suma, (void *)sv, NOPE,
12021                                           SEI_Tail, NULL)) {
12022       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
12023       SUMA_RETURNe;
12024    }
12025 
12026    /* call with the list */
12027    if (!SUMA_Engine (&list)) {
12028       fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
12029       SUMA_RETURNe;
12030    }
12031 
12032    /* redisplay curent only*/
12033    sv->ResetGLStateVariables = YUP;
12034    SUMA_handleRedisplay((XtPointer)sv->X->GLXAREA);
12035 
12036    SUMA_RETURNe;
12037 
12038 }
12039 
12040 /* Jump to a certain point on a tract object */
SUMA_JumpIndex_TDO(char * s,SUMA_SurfaceViewer * sv,SUMA_TractDO * tdo)12041 void SUMA_JumpIndex_TDO (char *s, SUMA_SurfaceViewer *sv,
12042                            SUMA_TractDO *tdo)
12043 {
12044    static char FuncName[]={"SUMA_JumpIndex_TDO"};
12045    DList *list=NULL;
12046    SUMA_ALL_DO *ado = (SUMA_ALL_DO *)tdo;
12047    DListElmt *el=NULL, *Location=NULL;
12048    SUMA_EngineData *ED = NULL;
12049    float fv3[3];
12050    int it, iv15[15], iv3[3], nv = 0;
12051    char stmp[64];
12052    SUMA_Boolean revert_on_err = YUP;
12053    SUMA_Boolean LocalHead = NOPE;
12054 
12055    SUMA_ENTRY;
12056 
12057    if (!s || !sv || !tdo || !tdo->net) SUMA_RETURNe;
12058 
12059    /* parse s */
12060    if ((nv = SUMA_StringToNum(s, (void*)fv3, 3,1)) != 1 &&
12061        nv != 3) {
12062                                     /*problem, beep and ignore */
12063       XBell (XtDisplay (sv->X->TOPLEVEL), 50);
12064       SUMA_RETURNe;
12065    }
12066    SUMA_LHv("Parsed %s to %d val(s)\n", s, nv);
12067    if (nv == 3) {/* bundle, tract, point */
12068       iv15[SUMA_NET_BUN] = (int)fv3[SUMA_NET_BUN];
12069       iv15[SUMA_BUN_TRC] = (int)fv3[SUMA_BUN_TRC];
12070       iv15[SUMA_TRC_PNT] = (int)fv3[SUMA_TRC_PNT];
12071       iv15[SUMA_NET_TRC] =
12072          Network_TB_to_1T(tdo->net, iv15[SUMA_BUN_TRC], iv15[SUMA_NET_BUN]);
12073       it = Network_PTB_to_1P(tdo->net,
12074                iv15[SUMA_TRC_PNT], iv15[SUMA_BUN_TRC], iv15[SUMA_NET_BUN]);
12075       if (it < 0) { /* no good */
12076          XBell (XtDisplay (sv->X->TOPLEVEL), 50);
12077          SUMA_LH("BTP %d %d %d could not be parsed\n",
12078                  iv15[SUMA_NET_BUN],   iv15[SUMA_BUN_TRC], iv15[SUMA_TRC_PNT]);
12079          if (revert_on_err) {
12080             sprintf(stmp,"%d", SUMA_ADO_SelectedDatum(ado, NULL, NULL));
12081             SUMA_JumpIndex_TDO(stmp, sv, tdo);
12082          }
12083          SUMA_RETURNe;
12084       }
12085       SUMA_LHv("Point ID %d from B%d T%d P%d (tract in net %d)\n",
12086                it, iv15[SUMA_NET_BUN], iv15[SUMA_BUN_TRC],
12087                iv15[SUMA_TRC_PNT], iv15[SUMA_NET_TRC]);
12088    } else {
12089       /* Set the point selection  */
12090       it = (int) fv3[0];
12091       if (!Network_1P_to_PTB(tdo->net, it,
12092                iv15+SUMA_TRC_PNT, iv15+SUMA_BUN_TRC,
12093                iv15+SUMA_NET_BUN, iv15+SUMA_NET_TRC)) {
12094          XBell (XtDisplay (sv->X->TOPLEVEL), 50);
12095          SUMA_RETURNe;
12096       }
12097       SUMA_LHv("Point ID %d yielded B%d T%d P%d (tract in net %d) \n"
12098                "  (which yields back %d)\n",
12099                it, iv15[SUMA_NET_BUN], iv15[SUMA_BUN_TRC], iv15[SUMA_TRC_PNT],
12100                iv15[SUMA_NET_TRC],
12101                Network_PTB_to_1P(tdo->net,
12102                   iv15[SUMA_TRC_PNT], iv15[SUMA_BUN_TRC], iv15[SUMA_NET_BUN]));
12103    }
12104 
12105    SUMA_TDO_PointXYZ(tdo, it, iv15, fv3);
12106    SUMA_LH("   Located at %f %f %f\n", fv3[0], fv3[1], fv3[2]);
12107 
12108    if (!list) list = SUMA_CreateList ();
12109    ED = SUMA_InitializeEngineListData (SE_SetSelectedNode);
12110    if (!(el = SUMA_RegisterEngineListCommand (  list, ED,
12111                                           SEF_i, (void*)(&it),
12112                                           SES_Suma, (void *)sv, NOPE,
12113                                           SEI_Head, NULL))) {
12114       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
12115       SUMA_RETURNe;
12116    } else {
12117       SUMA_RegisterEngineListCommand (  list, ED,
12118                                           SEF_ngr, NULL,
12119                                           SES_Suma, (void *)sv, NOPE,
12120                                           SEI_In, el);
12121       SUMA_RegisterEngineListCommand (  list, ED,
12122                                           SEF_iv15, (void *)iv15,
12123                                           SES_Suma, (void *)sv, NOPE,
12124                                           SEI_In, el);
12125    }
12126 
12127 
12128    /* Now set the cross hair position at the selected point*/
12129    ED = SUMA_InitializeEngineListData (SE_SetCrossHair);
12130    if (!(Location=SUMA_RegisterEngineListCommand (  list, ED,
12131                                           SEF_fv3, (void*)fv3,
12132                                           SES_Suma, (void *)sv, NOPE,
12133                                           SEI_Head, NULL))) {
12134       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
12135       SUMA_RETURNe;
12136    }
12137    /* and add the DO with this location, needed for VisX business*/
12138    SUMA_RegisterEngineListCommand (  list, ED,
12139                                            SEF_vp, (void *)tdo,
12140                                            SES_Suma, (void *)sv, NOPE,
12141                                            SEI_In, Location);
12142 
12143    /* attach the cross hair to the selected object
12144       Note that binding here is to edge of graph and not to a node*/
12145    iv3[0] = SUMA_whichDO(SUMA_ADO_idcode(ado), SUMAg_DOv, SUMAg_N_DOv);
12146    iv3[1] = it;
12147    iv3[2] = -1;
12148    ED = SUMA_InitializeEngineListData (SE_BindCrossHair);
12149    if (!SUMA_RegisterEngineListCommand (  list, ED,
12150                                           SEF_iv3, (void*)iv3,
12151                                           SES_Suma, (void *)sv, NOPE,
12152                                           SEI_Head, NULL)) {
12153       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
12154       SUMA_RETURNe;
12155    }
12156 
12157    /* check to see if AFNI needs to be notified */
12158    if (SUMAg_CF->Connected_v[SUMA_AFNI_STREAM_INDEX] && sv->LinkAfniCrossHair) {
12159       if (LocalHead)
12160          fprintf(SUMA_STDERR,"%s: Notifying Afni of CrossHair XYZ\n", FuncName);
12161       it = 0;
12162       ED = SUMA_InitializeEngineListData (SE_SetAfniCrossHair);
12163       if (!SUMA_RegisterEngineListCommand (  list, ED,
12164                                           SEF_i, (void*)&it,
12165                                           SES_Suma, (void *)sv, NOPE,
12166                                           SEI_Tail, NULL)) {
12167          fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
12168          SUMA_RETURNe;
12169       }
12170 
12171    }
12172 
12173    /* call with the list */
12174    if (!SUMA_Engine (&list)) {
12175       fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
12176       SUMA_RETURNe;
12177    }
12178 
12179    /* now put in a request for locking cross hair but you must do this
12180    after the node selection has been executed
12181    NOTE: You do not always have SetNodeElem because the list might get emptied in
12182    the call to AFNI notification.
12183    You should just put the next call at the end of the list.*/
12184    if (!list) list = SUMA_CreateList();
12185    ED = SUMA_InitializeEngineListData (SE_LockCrossHair);
12186    if (!SUMA_RegisterEngineListCommand (  list, ED,
12187                                           SEF_iv3, (void*)iv3,
12188                                           SES_Suma, (void *)sv, NOPE,
12189                                           SEI_Tail, NULL)) {
12190       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
12191       SUMA_RETURNe;
12192    }
12193 
12194    /* call with the list */
12195    if (!SUMA_Engine (&list)) {
12196       fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
12197       SUMA_RETURNe;
12198    }
12199 
12200    /* redisplay curent only*/
12201    sv->ResetGLStateVariables = YUP;
12202    SUMA_handleRedisplay((XtPointer)sv->X->GLXAREA);
12203 
12204    SUMA_RETURNe;
12205 
12206 }
12207 
12208 /* Jump to a certain datum on a CIFTI object */
SUMA_JumpIndex_CO(char * s,SUMA_SurfaceViewer * sv,SUMA_CIFTI_DO * co)12209 void SUMA_JumpIndex_CO (char *s, SUMA_SurfaceViewer *sv,
12210                         SUMA_CIFTI_DO *co)
12211 {
12212    static char FuncName[]={"SUMA_JumpIndex_CO"};
12213    DList *list=NULL;
12214    SUMA_ALL_DO *ado = (SUMA_ALL_DO *)co;
12215    DListElmt *el=NULL, *Location=NULL;
12216    SUMA_EngineData *ED = NULL;
12217    float fv3[3], fv15[15];
12218    int it, iv15[15], iv3[3], nv = 0, *dims=NULL;
12219    char stmp[64];
12220    SUMA_DSET *dset=NULL;
12221    SUMA_Boolean revert_on_err = YUP;
12222    SUMA_Boolean LocalHead = NOPE;
12223 
12224    SUMA_ENTRY;
12225 
12226    SUMA_LH("Called");
12227 
12228    SUMA_S_Err("Not implemented, see SUMA_JumpIndex_VO and SUMA_JumpIndex_SO "
12229               "for inspiration. You will need to determine the domain of "
12230               "the index before jumping anyway");
12231 
12232    SUMA_RETURNe;
12233 }
12234 
12235 /* Jump to a certain point on a volume object */
SUMA_JumpIndex_VO(char * s,SUMA_SurfaceViewer * sv,SUMA_VolumeObject * vo)12236 void SUMA_JumpIndex_VO (char *s, SUMA_SurfaceViewer *sv,
12237                         SUMA_VolumeObject *vo)
12238 {
12239    static char FuncName[]={"SUMA_JumpIndex_VO"};
12240    DList *list=NULL;
12241    SUMA_ALL_DO *ado = (SUMA_ALL_DO *)vo;
12242    DListElmt *el=NULL, *Location=NULL;
12243    SUMA_EngineData *ED = NULL;
12244    float fv3[3], fv15[15];
12245    int it, iv15[15], iv3[3], nv = 0, *dims=NULL;
12246    char stmp[64];
12247    SUMA_DSET *dset=NULL;
12248    SUMA_Boolean revert_on_err = YUP;
12249    SUMA_Boolean LocalHead = NOPE;
12250 
12251    SUMA_ENTRY;
12252 
12253    SUMA_LH("Called");
12254    if (!s || !sv || !vo ||
12255        !(dset = SUMA_VO_dset(vo)) ||
12256        !(dims = SUMA_GetDatasetDimensions(dset))) SUMA_RETURNe;
12257 
12258    /* parse s */
12259    if ((nv = SUMA_StringToNum(s, (void*)fv3, 3,1)) != 1 &&
12260        nv != 3) {
12261                                     /*problem, beep and ignore */
12262       XBell (XtDisplay (sv->X->TOPLEVEL), 50);
12263       SUMA_RETURNe;
12264    }
12265    SUMA_LHv("Parsed %s to %d val(s)\n", s, nv);
12266    if (nv == 3) {/* i j k */
12267       iv15[SUMA_VOL_I] = (int)fv3[SUMA_VOL_I];
12268       iv15[SUMA_VOL_J] = (int)fv3[SUMA_VOL_J];
12269       iv15[SUMA_VOL_K] = (int)fv3[SUMA_VOL_K];
12270       if (iv15[SUMA_VOL_I] < 0 || iv15[SUMA_VOL_I] >= dims[0] ||
12271           iv15[SUMA_VOL_J] < 0 || iv15[SUMA_VOL_J] >= dims[1] ||
12272           iv15[SUMA_VOL_K] < 0 || iv15[SUMA_VOL_K] >= dims[2] ) {
12273          /* no good */
12274          XBell (XtDisplay (sv->X->TOPLEVEL), 50);
12275          SUMA_LH("IJK %d %d %d could not be parsed or out of range\n",
12276                  iv15[SUMA_VOL_I], iv15[SUMA_VOL_J], iv15[SUMA_VOL_K]);
12277          if (revert_on_err) {
12278             sprintf(stmp,"%d", SUMA_ADO_SelectedDatum(ado, NULL, NULL));
12279             SUMA_JumpIndex_VO(stmp, sv, vo);
12280          }
12281          SUMA_RETURNe;
12282       }
12283 
12284       iv15[SUMA_VOL_IJK] =
12285          SUMA_3D_2_1D_index(iv15[SUMA_VOL_I], iv15[SUMA_VOL_J], iv15[SUMA_VOL_K],
12286          dims[0], dims[0]*dims[1] );
12287 
12288       it = iv15[SUMA_VOL_IJK];
12289 
12290       SUMA_LHv("Voxel ID %d from I%d J%d K%d\n",
12291                iv15[SUMA_VOL_IJK], iv15[SUMA_VOL_I], iv15[SUMA_VOL_J],
12292                iv15[SUMA_VOL_K]);
12293    } else {
12294       /* Set the point selection  */
12295       it = (int) fv3[0];
12296       if (it < 0 || it >= SDSET_NVOX(dset)) {
12297          XBell (XtDisplay (sv->X->TOPLEVEL), 50);
12298          SUMA_RETURNe;
12299       }
12300       Vox1D2Vox3D(it, dims[0], dims[0]*dims[1], (iv15+SUMA_VOL_I));
12301       SUMA_LHv("Point ID %d yielded I%d J%d K%d \n",
12302                it, iv15[SUMA_VOL_I], iv15[SUMA_VOL_J], iv15[SUMA_VOL_K]);
12303    }
12304 
12305    SUMA_VO_PointXYZ(vo, it, iv15, fv3);
12306    SUMA_LH("   Located at %f %f %f\n", fv3[0], fv3[1], fv3[2]);
12307    SUMA_MARK_PLANE_NOT_SET(fv15+SUMA_VOL_SLC_EQ0); /* No cigar */
12308 
12309    if (!list) list = SUMA_CreateList ();
12310    ED = SUMA_InitializeEngineListData (SE_SetSelectedNode);
12311    if (!(el = SUMA_RegisterEngineListCommand (  list, ED,
12312                                           SEF_i, (void*)(&it),
12313                                           SES_Suma, (void *)sv, NOPE,
12314                                           SEI_Head, NULL))) {
12315       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
12316       SUMA_RETURNe;
12317    } else {
12318       SUMA_RegisterEngineListCommand (  list, ED,
12319                                           SEF_ngr, NULL,
12320                                           SES_Suma, (void *)sv, NOPE,
12321                                           SEI_In, el);
12322       SUMA_RegisterEngineListCommand (  list, ED,
12323                                           SEF_iv15, (void *)iv15,
12324                                           SES_Suma, (void *)sv, NOPE,
12325                                           SEI_In, el);
12326       SUMA_RegisterEngineListCommand (  list, ED,
12327                                           SEF_fv15, (void *)fv15,
12328                                           SES_Suma, (void *)sv, NOPE,
12329                                           SEI_In, el);
12330    }
12331 
12332 
12333    /* Now set the cross hair position at the selected point*/
12334    ED = SUMA_InitializeEngineListData (SE_SetCrossHair);
12335    if (!(Location=SUMA_RegisterEngineListCommand (  list, ED,
12336                                           SEF_fv3, (void*)fv3,
12337                                           SES_Suma, (void *)sv, NOPE,
12338                                           SEI_Head, NULL))) {
12339       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
12340       SUMA_RETURNe;
12341    }
12342    /* and add the DO with this location, needed for VisX business*/
12343    SUMA_RegisterEngineListCommand (  list, ED,
12344                                            SEF_vp, (void *)vo,
12345                                            SES_Suma, (void *)sv, NOPE,
12346                                            SEI_In, Location);
12347 
12348    /* attach the cross hair to the selected object
12349       Note that binding here is to edge of graph and not to a node*/
12350    iv3[0] = SUMA_whichDO(SUMA_ADO_idcode(ado), SUMAg_DOv, SUMAg_N_DOv);
12351    iv3[1] = it;
12352    iv3[2] = -1;
12353    ED = SUMA_InitializeEngineListData (SE_BindCrossHair);
12354    if (!SUMA_RegisterEngineListCommand (  list, ED,
12355                                           SEF_iv3, (void*)iv3,
12356                                           SES_Suma, (void *)sv, NOPE,
12357                                           SEI_Head, NULL)) {
12358       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
12359       SUMA_RETURNe;
12360    }
12361 
12362    /* check to see if AFNI needs to be notified */
12363    if (SUMAg_CF->Connected_v[SUMA_AFNI_STREAM_INDEX] && sv->LinkAfniCrossHair) {
12364       if (LocalHead)
12365          fprintf(SUMA_STDERR,"%s: Notifying Afni of CrossHair XYZ\n", FuncName);
12366       it = 0;
12367       ED = SUMA_InitializeEngineListData (SE_SetAfniCrossHair);
12368       if (!SUMA_RegisterEngineListCommand (  list, ED,
12369                                           SEF_i, (void*)&it,
12370                                           SES_Suma, (void *)sv, NOPE,
12371                                           SEI_Tail, NULL)) {
12372          fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
12373          SUMA_RETURNe;
12374       }
12375 
12376    }
12377 
12378    /* call with the list */
12379    if (!SUMA_Engine (&list)) {
12380       fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
12381       SUMA_RETURNe;
12382    }
12383 
12384    /* now put in a request for locking cross hair but you must do this
12385    after the node selection has been executed
12386    NOTE: You do not always have SetNodeElem because the list might get emptied in
12387    the call to AFNI notification.
12388    You should just put the next call at the end of the list.*/
12389    if (!list) list = SUMA_CreateList();
12390    ED = SUMA_InitializeEngineListData (SE_LockCrossHair);
12391    if (!SUMA_RegisterEngineListCommand (  list, ED,
12392                                           SEF_iv3, (void*)iv3,
12393                                           SES_Suma, (void *)sv, NOPE,
12394                                           SEI_Tail, NULL)) {
12395       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
12396       SUMA_RETURNe;
12397    }
12398 
12399    /* call with the list */
12400    if (!SUMA_Engine (&list)) {
12401       fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
12402       SUMA_RETURNe;
12403    }
12404 
12405    /* redisplay curent only*/
12406    sv->ResetGLStateVariables = YUP;
12407    SUMA_handleRedisplay((XtPointer)sv->X->GLXAREA);
12408 
12409    SUMA_RETURNe;
12410 
12411 }
12412 
12413 /* Jump to a certain point on a mask object */
SUMA_JumpIndex_MDO(char * s,SUMA_SurfaceViewer * sv,SUMA_MaskDO * mo)12414 void SUMA_JumpIndex_MDO (char *s, SUMA_SurfaceViewer *sv, SUMA_MaskDO *mo)
12415 {
12416    static char FuncName[]={"SUMA_JumpIndex_MDO"};
12417    DList *list=NULL;
12418    SUMA_ALL_DO *ado = (SUMA_ALL_DO *)mo;
12419    DListElmt *el=NULL, *Location=NULL;
12420    SUMA_EngineData *ED = NULL;
12421    float fv3[3];
12422    int it, iv15[15], iv3[3], nv = 0, *dims=NULL;
12423    char stmp[64];
12424    SUMA_DSET *dset=NULL;
12425    SUMA_Boolean revert_on_err = YUP;
12426    SUMA_Boolean LocalHead = NOPE;
12427 
12428    SUMA_ENTRY;
12429 
12430    if (!s || !sv) SUMA_RETURNe;
12431 
12432    SUMA_S_Err("Not ready for action");
12433    SUMA_RETURNe;
12434 
12435    /* parse s */
12436    if ((nv = SUMA_StringToNum(s, (void*)fv3, 3,1)) != 1 &&
12437        nv != 3) {
12438                                     /*problem, beep and ignore */
12439       XBell (XtDisplay (sv->X->TOPLEVEL), 50);
12440       SUMA_RETURNe;
12441    }
12442    SUMA_LHv("Parsed %s to %d val(s)\n", s, nv);
12443    if (nv == 3) {/* i j k */
12444       iv15[SUMA_VOL_I] = (int)fv3[SUMA_VOL_I];
12445       iv15[SUMA_VOL_J] = (int)fv3[SUMA_VOL_J];
12446       iv15[SUMA_VOL_K] = (int)fv3[SUMA_VOL_K];
12447       if (iv15[SUMA_VOL_I] < 0 || iv15[SUMA_VOL_I] >= dims[0] ||
12448           iv15[SUMA_VOL_J] < 0 || iv15[SUMA_VOL_J] >= dims[1] ||
12449           iv15[SUMA_VOL_K] < 0 || iv15[SUMA_VOL_K] >= dims[2] ) {
12450          /* no good */
12451          XBell (XtDisplay (sv->X->TOPLEVEL), 50);
12452          SUMA_LH("IJK %d %d %d could not be parsed or out of range\n",
12453                  iv15[SUMA_VOL_I], iv15[SUMA_VOL_J], iv15[SUMA_VOL_K]);
12454          if (revert_on_err) {
12455             sprintf(stmp,"%d", SUMA_ADO_SelectedDatum(ado, NULL, NULL));
12456             SUMA_JumpIndex_MDO(stmp, sv, mo);
12457          }
12458          SUMA_RETURNe;
12459       }
12460 
12461       iv15[SUMA_VOL_IJK] =
12462          SUMA_3D_2_1D_index(iv15[SUMA_VOL_I], iv15[SUMA_VOL_J], iv15[SUMA_VOL_K],
12463          dims[0], dims[0]*dims[1] );
12464 
12465 
12466       SUMA_LHv("Voxel ID %d from I%d J%d K%d\n",
12467                iv15[SUMA_VOL_IJK], iv15[SUMA_VOL_I], iv15[SUMA_VOL_J],
12468                iv15[SUMA_VOL_K]);
12469    } else {
12470       /* Set the point selection  */
12471       it = (int) fv3[0];
12472       if (it < 0 || it >= SDSET_NVOX(dset)) {
12473          XBell (XtDisplay (sv->X->TOPLEVEL), 50);
12474          SUMA_RETURNe;
12475       }
12476       Vox1D2Vox3D(it, dims[0], dims[0]*dims[1], (iv15+SUMA_VOL_I));
12477       SUMA_LHv("Point ID %d yielded I%d J%d K%d \n",
12478                it, iv15[SUMA_VOL_I], iv15[SUMA_VOL_J], iv15[SUMA_VOL_K]);
12479    }
12480 
12481    SUMA_MDO_PointXYZ(mo, it, iv15, fv3);
12482    SUMA_LH("   Located at %f %f %f\n", fv3[0], fv3[1], fv3[2]);
12483 
12484    if (!list) list = SUMA_CreateList ();
12485    ED = SUMA_InitializeEngineListData (SE_SetSelectedNode);
12486    if (!(el = SUMA_RegisterEngineListCommand (  list, ED,
12487                                           SEF_i, (void*)(&it),
12488                                           SES_Suma, (void *)sv, NOPE,
12489                                           SEI_Head, NULL))) {
12490       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
12491       SUMA_RETURNe;
12492    } else {
12493       SUMA_RegisterEngineListCommand (  list, ED,
12494                                           SEF_ngr, NULL,
12495                                           SES_Suma, (void *)sv, NOPE,
12496                                           SEI_In, el);
12497       SUMA_RegisterEngineListCommand (  list, ED,
12498                                           SEF_iv15, (void *)iv15,
12499                                           SES_Suma, (void *)sv, NOPE,
12500                                           SEI_In, el);
12501    }
12502 
12503 
12504    /* Now set the cross hair position at the selected point*/
12505    ED = SUMA_InitializeEngineListData (SE_SetCrossHair);
12506    if (!(Location=SUMA_RegisterEngineListCommand (  list, ED,
12507                                           SEF_fv3, (void*)fv3,
12508                                           SES_Suma, (void *)sv, NOPE,
12509                                           SEI_Head, NULL))) {
12510       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
12511       SUMA_RETURNe;
12512    }
12513    /* and add the DO with this location, needed for VisX business*/
12514    SUMA_RegisterEngineListCommand (  list, ED,
12515                                            SEF_vp, (void *)mo,
12516                                            SES_Suma, (void *)sv, NOPE,
12517                                            SEI_In, Location);
12518 
12519    /* attach the cross hair to the selected object
12520       Note that binding here is to edge of graph and not to a node*/
12521    iv3[0] = SUMA_whichDO(SUMA_ADO_idcode(ado), SUMAg_DOv, SUMAg_N_DOv);
12522    iv3[1] = it;
12523    iv3[2] = -1;
12524    ED = SUMA_InitializeEngineListData (SE_BindCrossHair);
12525    if (!SUMA_RegisterEngineListCommand (  list, ED,
12526                                           SEF_iv3, (void*)iv3,
12527                                           SES_Suma, (void *)sv, NOPE,
12528                                           SEI_Head, NULL)) {
12529       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
12530       SUMA_RETURNe;
12531    }
12532 
12533    /* check to see if AFNI needs to be notified */
12534    if (SUMAg_CF->Connected_v[SUMA_AFNI_STREAM_INDEX] && sv->LinkAfniCrossHair) {
12535       if (LocalHead)
12536          fprintf(SUMA_STDERR,"%s: Notifying Afni of CrossHair XYZ\n", FuncName);
12537       it = 0;
12538       ED = SUMA_InitializeEngineListData (SE_SetAfniCrossHair);
12539       if (!SUMA_RegisterEngineListCommand (  list, ED,
12540                                           SEF_i, (void*)&it,
12541                                           SES_Suma, (void *)sv, NOPE,
12542                                           SEI_Tail, NULL)) {
12543          fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
12544          SUMA_RETURNe;
12545       }
12546 
12547    }
12548 
12549    /* call with the list */
12550    if (!SUMA_Engine (&list)) {
12551       fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
12552       SUMA_RETURNe;
12553    }
12554 
12555    /* now put in a request for locking cross hair but you must do this
12556    after the node selection has been executed
12557    NOTE: You do not always have SetNodeElem because the list might get emptied in
12558    the call to AFNI notification.
12559    You should just put the next call at the end of the list.*/
12560    if (!list) list = SUMA_CreateList();
12561    ED = SUMA_InitializeEngineListData (SE_LockCrossHair);
12562    if (!SUMA_RegisterEngineListCommand (  list, ED,
12563                                           SEF_iv3, (void*)iv3,
12564                                           SES_Suma, (void *)sv, NOPE,
12565                                           SEI_Tail, NULL)) {
12566       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
12567       SUMA_RETURNe;
12568    }
12569 
12570    /* call with the list */
12571    if (!SUMA_Engine (&list)) {
12572       fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
12573       SUMA_RETURNe;
12574    }
12575 
12576    /* redisplay curent only*/
12577    sv->ResetGLStateVariables = YUP;
12578    SUMA_handleRedisplay((XtPointer)sv->X->GLXAREA);
12579 
12580    SUMA_RETURNe;
12581 
12582 }
12583 
12584 /*!
12585    \brief sends the cross hair to a certain XYZ location.
12586 
12587    \param s (char *) a string containing XYZ coordinates
12588    \param data (void *) a typecast of the pointer to the surface viewer to be affected
12589 
12590    - Update to AFNI is done if linked
12591    - Update to other viewers is performed IF they are XYZ locked
12592    (that can get confusing)
12593 */
SUMA_JumpXYZ(char * s,void * data)12594 void SUMA_JumpXYZ (char *s, void *data)
12595 {
12596    static char FuncName[]={"SUMA_JumpXYZ"};
12597    DList *list=NULL;
12598    DListElmt *Location=NULL;
12599    SUMA_EngineData *ED = NULL;
12600    SUMA_SurfaceViewer *sv = NULL;
12601    float fv3[3];
12602    SUMA_Boolean LocalHead = NOPE;
12603 
12604    SUMA_ENTRY;
12605 
12606    if (!s) SUMA_RETURNe;
12607 
12608    sv = (SUMA_SurfaceViewer *)data;
12609 
12610    /* parse s */
12611    if (SUMA_StringToNum (s, (void*)fv3, 3,1) != 3) {/*problem, beep and ignore */
12612       XBell (XtDisplay (sv->X->TOPLEVEL), 50);
12613       SUMA_RETURNe;
12614    }
12615 
12616    /* Now set the cross hair position */
12617    if (!list) list = SUMA_CreateList ();
12618    ED = SUMA_InitializeEngineListData (SE_SetCrossHair);
12619    if (!(Location = SUMA_RegisterEngineListCommand (  list, ED,
12620                                           SEF_fv3, (void*)fv3,
12621                                           SES_Suma, (void *)sv, NOPE,
12622                                           SEI_Head, NULL))) {
12623       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
12624       SUMA_RETURNe;
12625    }
12626    /* and add the SO with this location (not possible here), needed for VisX
12627       business*/
12628    SUMA_RegisterEngineListCommand (  list, ED,
12629                                      SEF_vp, NULL,
12630                                      SES_Suma, (void *)sv, NOPE,
12631                                      SEI_In, Location);
12632 
12633    /* check to see if AFNI needs to be notified */
12634    if (SUMAg_CF->Connected_v[SUMA_AFNI_STREAM_INDEX] && sv->LinkAfniCrossHair) {
12635       int it;
12636       if (LocalHead)
12637          fprintf(SUMA_STDERR,"%s: Notifying Afni of CrossHair XYZ\n", FuncName);
12638       it = 0;
12639       ED = SUMA_InitializeEngineListData (SE_SetAfniCrossHair);
12640       if (!SUMA_RegisterEngineListCommand (  list, ED,
12641                                           SEF_i, (void*)&it,
12642                                           SES_Suma, (void *)sv, NOPE,
12643                                           SEI_Tail, NULL)) {
12644          fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
12645          SUMA_RETURNe;
12646       }
12647 
12648    }
12649 
12650    /* call with the list */
12651    if (!SUMA_Engine (&list)) {
12652       fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
12653       SUMA_RETURNe;
12654    }
12655 
12656    /* now put in a request for locking cross hair but you must
12657    do this after the node selection has been executed
12658    NOTE: You do not always have SetNodeElem because the list might
12659    get emptied in the call to AFNI notification.
12660    You should just put the next call at the end of the list.*/
12661    /* NOTE2: Only viewers that are XYZ locked will be affected */
12662    if (!list) list = SUMA_CreateList();
12663    ED = SUMA_InitializeEngineListData (SE_LockCrossHair);
12664    if (!SUMA_RegisterEngineListCommand (  list, ED,
12665                                           SEF_Empty, NULL,
12666                                           SES_Suma, (void *)sv, NOPE,
12667                                           SEI_Tail, NULL)) {
12668       SUMA_SLP_Err("Failed to register element");
12669       SUMA_RETURNe;
12670    }
12671 
12672    /* call with the list */
12673    if (!SUMA_Engine (&list)) {
12674       fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
12675       SUMA_RETURNe;
12676    }
12677 
12678 
12679    /* redisplay curent only*/
12680    sv->ResetGLStateVariables = YUP;
12681    SUMA_handleRedisplay((XtPointer)sv->X->GLXAREA);
12682 
12683    SUMA_RETURNe;
12684 }
12685 
12686 /*!
12687    \brief Changes the focus node without moving the cross hair
12688    \param s (char *) a string containing node index
12689    \param data (void *) a typecast of the pointer to the surface viewer to be affected
12690 
12691    -actions of this function are limited to the viewer that launched it
12692 */
12693 
SUMA_JumpFocusNode(char * s,void * data)12694 void SUMA_JumpFocusNode (char *s, void *data)
12695 {
12696    static char FuncName[]={"SUMA_JumpFocusNode"};
12697    DList *list=NULL;
12698    DListElmt *el=NULL;
12699    SUMA_EngineData *ED = NULL;
12700    SUMA_SurfaceViewer *sv = NULL;
12701    float fv3[3];
12702    int it;
12703    SUMA_SurfaceObject *SO=NULL, *SOc=NULL;
12704    SUMA_SO_SIDE sd=SUMA_NO_SIDE;
12705    SUMA_Boolean LocalHead = NOPE;
12706 
12707    SUMA_ENTRY;
12708 
12709    if (!s) SUMA_RETURNe;
12710 
12711    sv = (SUMA_SurfaceViewer *)data;
12712    if (!(SO = SUMA_SV_Focus_SO(sv))) {
12713       SUMA_S_Err("No SO in focus");
12714       SUMA_RETURNe;
12715    }
12716 
12717 
12718    /* HERE you should check if you have an L or R at the beginning
12719    or end of s.
12720    If you do, then first see if the side of SO (the focus surface)
12721    is the same as the letter. If it is, proceed. If it is not,
12722    try to get the contralateral surface with SUMA_Contralateral_SO
12723    then set the contralateral as the focus surface, then proceed
12724    with setting the focus node. Needs more work
12725    */
12726    /* parse s */
12727    SUMA_LHv("Parsing %s\n", s);
12728    if (SUMA_StringToNumSide(s, (void*)fv3, 1,1, &sd) != 1) {
12729                                     /*problem, beep and ignore */
12730       XBell (XtDisplay (sv->X->TOPLEVEL), 50);
12731       SUMA_RETURNe;
12732    }
12733 
12734    SUMA_LHv("Side of focus jump is %d\n", sd);
12735    /* do we have side match with Focus node? */
12736    if (sd == SUMA_RIGHT || sd == SUMA_LEFT) {
12737       if ((SO->Side == SUMA_RIGHT || SO->Side == SUMA_LEFT) &&
12738             SO->Side != sd) {
12739          /* Need to swith sides */
12740          if ((SOc = SUMA_Contralateral_SO(SO, SUMAg_DOv, SUMAg_N_DOv))) {
12741             sv->Focus_DO_ID = SUMA_findSO_inDOv(SOc->idcode_str,
12742                                              SUMAg_DOv, SUMAg_N_DOv);
12743             SUMA_LHv("Jumping focus only to %s (contralateral of %s)\n",
12744                   SOc->Label, SO->Label);
12745             SO = SOc;
12746          } else {
12747             SUMA_S_Errv("Failed to find contralateral surface to %s\n"
12748                         "Ignoring jump to node's side marker\n",
12749                         SO->Label);
12750          }
12751       }
12752    }
12753    /* Set the Nodeselection  */
12754    it = (int) fv3[0];
12755    if (!list) list = SUMA_CreateList ();
12756    ED = SUMA_InitializeEngineListData (SE_SetSelectedNode);
12757    if (!(el=SUMA_RegisterEngineListCommand (  list, ED,
12758                                           SEF_i, (void*)(&it),
12759                                           SES_Suma, (void *)sv, NOPE,
12760                                           SEI_Head, NULL))) {
12761       SUMA_SLP_Err("Failed to register element");
12762       SUMA_RETURNe;
12763    } else {
12764       SUMA_RegisterEngineListCommand (  list, ED,
12765                                           SEF_ngr, NULL,
12766                                           SES_Suma, (void *)sv, NOPE,
12767                                           SEI_In, el);
12768    }
12769 
12770    /* call with the list */
12771    if (!SUMA_Engine (&list)) {
12772       fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
12773       SUMA_RETURNe;
12774    }
12775 
12776    /* redisplay curent only*/
12777    sv->ResetGLStateVariables = YUP;
12778    SUMA_handleRedisplay((XtPointer)sv->X->GLXAREA);
12779 
12780    SUMA_RETURNe;
12781 
12782 }
12783 
12784 /*!
12785    \brief changes the selected faceset (Focus FaceSet)
12786    \param s (char *) a string containing FaceSet index
12787    \param data (void *) a typecast of the pointer to the surface viewer to be affected
12788 
12789 */
SUMA_JumpFocusFace(char * s,void * data)12790 void SUMA_JumpFocusFace (char *s, void *data)
12791 {
12792    static char FuncName[]={"SUMA_JumpFocusFace"};
12793    DList *list=NULL;
12794    SUMA_EngineData *ED = NULL;
12795    SUMA_SurfaceViewer *sv = NULL;
12796    float fv3[3];
12797    int it;
12798 
12799    SUMA_ENTRY;
12800 
12801    if (!s) SUMA_RETURNe;
12802 
12803    sv = (SUMA_SurfaceViewer *)data;
12804 
12805    /* parse s */
12806    if (SUMA_StringToNum (s, (void*)fv3, 1,1) != 1) {/*problem, beep and ignore */
12807       XBell (XtDisplay (sv->X->TOPLEVEL), 50);
12808       SUMA_RETURNe;
12809    }
12810 
12811 
12812 
12813    /* Set the Faceselection  */
12814    it = (int) fv3[0];
12815    if (!list) list = SUMA_CreateList ();
12816    ED = SUMA_InitializeEngineListData (SE_SetSelectedFaceSet);
12817    if (!SUMA_RegisterEngineListCommand (  list, ED,
12818                                           SEF_i, (void*)&it,
12819                                           SES_Suma, (void *)sv, NOPE,
12820                                           SEI_Head, NULL)) {
12821       fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
12822       SUMA_RETURNe;
12823    }
12824 
12825    /* call with the list */
12826    if (!SUMA_Engine (&list)) {
12827       fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
12828       SUMA_RETURNe;
12829    }
12830 
12831    /* redisplay curent only*/
12832    sv->ResetGLStateVariables = YUP;
12833    SUMA_handleRedisplay((XtPointer)sv->X->GLXAREA);
12834 
12835    SUMA_RETURNe;
12836 
12837 }
12838 
12839 /*!
12840    \brief Highlight a set of nodes within a box
12841    \param s (char *) a string containing box center followed by box size (6 values)
12842    \param data (void *) a typecast of the pointer to the surface viewer to be affected
12843 
12844    - operates by coloring nodes inside box.
12845    - coloring is not permanent and modifies other colors already present
12846    - operates on current viewer only
12847 */
SUMA_HighlightBox(char * s,void * data)12848 void SUMA_HighlightBox (char *s, void *data)
12849 {
12850    static char FuncName[]={"SUMA_HighlightBox"};
12851    DList *list=NULL;
12852    SUMA_EngineData *ED = NULL;
12853    SUMA_SurfaceViewer *sv = NULL;
12854    float fv15[15];
12855 
12856    SUMA_ENTRY;
12857 
12858    if (!s) SUMA_RETURNe;
12859 
12860    sv = (SUMA_SurfaceViewer *)data;
12861 
12862    /* parse s */
12863    if (SUMA_StringToNum (s, (void*)fv15, 6,1) != 6) {
12864                               /*problem, beep and ignore */
12865       XBell (XtDisplay (sv->X->TOPLEVEL), 50);
12866       SUMA_RETURNe;
12867    }
12868 
12869    /* register fv15 with ED */
12870    if (!list) list = SUMA_CreateList();
12871    ED = SUMA_InitializeEngineListData (SE_HighlightNodes);
12872    if (!SUMA_RegisterEngineListCommand (     list, ED,
12873                                              SEF_fv15, (void*)fv15,
12874                                              SES_Suma, (void *)sv, NOPE,
12875                                              SEI_Head, NULL)) {
12876          fprintf(SUMA_STDERR,"Error %s: Failed to register element\n", FuncName);
12877          SUMA_RETURNe;
12878    }
12879 
12880    SUMA_REGISTER_HEAD_COMMAND_NO_DATA(list, SE_Redisplay, SES_Suma, sv);
12881 
12882    if (!SUMA_Engine (&list)) {
12883       fprintf(stderr, "Error %s: SUMA_Engine call failed.\n", FuncName);
12884    }
12885 
12886 
12887    SUMA_RETURNe;
12888 
12889 }
12890