1 /*****************************************************************************
2    Major portions of this software are copyrighted by the Medical College
3    of Wisconsin, 1994-2000, and are released under the Gnu General Public
4    License, Version 2.  See the file README.Copyright for details.
5 ******************************************************************************/
6 
7 #include "vecmat.h"
8 
9 #include "afni.h"
10 
11 #ifndef ALLOW_PLUGINS
12 #  error "Plugins not properly set up -- see machdep.h"
13 #endif
14 
15 /***********************************************************************
16   Plugin to nudge a dataset around.
17   Makes a custom interface.
18   -- RWCox -- April 2000
19 ************************************************************************/
20 
21 /*----------------- prototypes for internal routines -----------------*/
22 
23 static PLUGIN_interface * plint = NULL ;
24 
25 char * NUD_main( PLUGIN_interface * ) ;  /* the entry point */
26 
27 static void NUD_make_widgets(void) ;
28 
29 static void NUD_nudge_CB ( Widget , XtPointer , XtPointer ) ;
30 static void NUD_clear_CB ( Widget , XtPointer , XtPointer ) ;
31 static void NUD_undo_CB  ( Widget , XtPointer , XtPointer ) ;
32 static void NUD_redo_CB  ( Widget , XtPointer , XtPointer ) ;
33 static void NUD_help_CB  ( Widget , XtPointer , XtPointer ) ;
34 static void NUD_quit_CB  ( Widget , XtPointer , XtPointer ) ;
35 static void NUD_doall_CB ( Widget , XtPointer , XtPointer ) ;
36 static void NUD_choose_CB( Widget , XtPointer , XtPointer ) ;
37 static void NUD_print_CB ( Widget , XtPointer , XtPointer ) ;
38 
39 static void NUD_finalize_dset_CB( Widget , XtPointer , MCW_choose_cbs * ) ;
40 
41 static void NUD_brick_av_CB( MCW_arrowval * , XtPointer ) ; /* Sub-brick menu */
42 
43 static void NUD_undopush(void) ;
44 static void NUD_setcumlab(void) ;
45 
46 static void NUD_rotate( MRI_IMAGE * im ) ;
47 static void NUD_update_base( Widget ) ;
48 
49 /***********************************************************************
50    Set up the interface to the user
51 ************************************************************************/
52 
53 
54 DEFINE_PLUGIN_PROTOTYPE
55 
PLUGIN_init(int ncall)56 PLUGIN_interface * PLUGIN_init( int ncall )
57 {
58    if( ncall > 0 ) return NULL ;  /* only one interface */
59 
60    plint = PLUTO_new_interface( "Nudge Dataset" ,
61                                 "Move bricks around" ,
62                                 NULL ,
63                                 PLUGIN_CALL_IMMEDIATELY , NUD_main  ) ;
64 
65    PLUTO_add_hint( plint , "Move bricks around" ) ;
66 
67    PLUTO_set_sequence( plint , "A:olddset:nudger" ) ;
68 
69    return plint ;
70 }
71 
72 /**************************************************************************
73   return a label string for 3 floats with 3 suffix characters
74 --------------------------------------------------------------------------*/
75 
76 #define EPS 0.005
77 
NUD_threestring(float a,float b,float c,char ca,char cb,char cc)78 static char * NUD_threestring( float a,float b,float c,char ca,char cb,char cc )
79 {
80    static char label[64] ;
81    if( fabs(a) < EPS ) a = 0.0 ;
82    if( fabs(b) < EPS ) b = 0.0 ;
83    if( fabs(c) < EPS ) c = 0.0 ;
84    sprintf(label,"%6.2f%c %6.2f%c %6.2f%c ", a,ca,b,cb,c,cc ) ;
85    return label ;
86 }
87 
NUD_3string(float a,float b,float c,char ca,char cb,char cc)88 static char * NUD_3string( float a,float b,float c,char ca,char cb,char cc )
89 {
90    static char label[64] ;
91    if( fabs(a) < EPS ) a = 0.0 ;
92    if( fabs(b) < EPS ) b = 0.0 ;
93    if( fabs(c) < EPS ) c = 0.0 ;
94    sprintf(label,"%.2f%c %.2f%c %.2f%c", a,ca,b,cb,c,cc ) ;
95    return label ;
96 }
97 
98 /***************************************************************************
99   Will be called from AFNI when user selects from Plugins menu.
100 ****************************************************************************/
101 
102 /* Interface widgets */
103 
104 static Widget shell=NULL , rowcol , info_lab , choose_pb ;
105 static Widget nudge_pb, clear_pb, undo_pb, redo_pb, help_pb, quit_pb, doall_pb, print_pb ;
106 static MCW_arrowval * roll_av , * pitch_av , * yaw_av ,
107                     * dS_av   , * dL_av    , * dP_av  , * brick_av ;
108 static Widget angle_cum_lab , shift_cum_lab ;
109 
110 static MCW_arrowval * interp_av , * clip_av ;
111 
112 /* other data */
113 
114 static int nudger_open = 0 ;
115 
116 static MCW_DC * dc ;                   /* display context */
117 static Three_D_View * im3d ;           /* AFNI controller */
118 static THD_3dim_dataset * dset ;       /* The dataset!    */
119 static MCW_idcode         dset_idc ;
120 static int new_dset = 0 ;              /* Is it new?      */
121 static int dset_ival = 0 ;             /* Sub-brick index */
122 static char dset_title[THD_MAX_NAME] ; /* Title string    */
123 
124 static char * NUD_dummy_av_label[2] = { "[Nothing At All]", "[Nothing At All]" };
125 
126 static int iha=1,ax1=0,ax2=1,ax3=2,hax1=1,hax2=2,hax3=3,adx=1,ady=2,adz=3 ;
127 
128 static int undo_nall , undo_nuse , undo_ntop ;
129 static THD_dmat33 * undo_rmat=NULL ;
130 static THD_dfvec3 * undo_svec=NULL ;
131 
132 static THD_dmat33 rmat ; /* current rotation matrix = undo_rmat[undo_nuse-1] */
133 static THD_dfvec3 svec ; /* current shift vector    = undo_svec[undo_nuse-1] */
134 
135 #define NRESAM 7
136 #define NYESNO 2
137 static char * REG_resam_strings[NRESAM] = {
138   "NN" ,  "Linear" ,  "Cubic" ,  "Quintic" ,  "Heptic" ,  "Fourier" ,  "Fourier_nopad" } ;
139 
140 static char * REG_resam_options[NRESAM] = {
141  "-NN" , "-linear" , "-cubic" , "-quintic" , "-heptic" , "-Fourier" , "-Fourier_nopad" } ;
142 
143 static int REG_resam_ints[NRESAM] = {
144   MRI_NN, MRI_LINEAR, MRI_CUBIC, MRI_QUINTIC, MRI_HEPTIC, MRI_FOURIER, MRI_FOURIER_NOPAD } ;
145 
146 static char * YESNO_strings[NYESNO] = { "No" , "Yes" } ;
147 
148 MRI_IMAGE * imbase = NULL ;
149 
150 /*-------------------------------------------------------------------------------*/
151 
NUD_main(PLUGIN_interface * plint)152 char * NUD_main( PLUGIN_interface * plint )
153 {
154    XmString xstr ;
155 
156    /*-- sanity checks --*/
157 
158    if( ! IM3D_OPEN(plint->im3d) )
159       return " \n AFNI Controller\nnot opened?! \n " ;
160 
161    if( nudger_open ){
162       if( plint->im3d != im3d ){ /* different controller => close it */
163          NUD_quit_CB(NULL,NULL,NULL) ;
164       } else {                   /* same controller => just raise up */
165          XtMapWidget(shell) ;
166          XRaiseWindow( XtDisplay(shell) , XtWindow(shell) ) ;
167          return NULL ;
168       }
169    }
170 
171    im3d = plint->im3d ;  /* save for local use */
172 
173    /*-- create widgets, first time through --*/
174 
175    if( shell == NULL ){
176       dc = im3d->dc ;        /* save this too */
177       NUD_make_widgets() ;
178       PLUTO_set_topshell( plint , shell ) ;  /* 22 Sep 2000 */
179       RWC_visibilize_widget( shell ) ;       /* 27 Sep 2000 */
180    }
181 
182    /*-- set titlebar --*/
183 
184    { char ttl[PLUGIN_STRING_SIZE] ;
185      sprintf(ttl , "AFNI Nudger %s" , AFNI_controller_label(im3d) ) ;
186      XtVaSetValues( shell , XmNtitle , ttl , NULL ) ;
187    }
188 
189    /*-- set the info label --*/
190 
191    xstr = XmStringCreateLtoR( "[No dataset]" ,
192                               XmFONTLIST_DEFAULT_TAG ) ;
193    XtVaSetValues( info_lab , XmNlabelString , xstr , NULL ) ;
194    XmStringFree(xstr) ;
195 
196    /*-- pop the widget up --*/
197 
198    XtMapWidget(shell) ;
199    PLUTO_cursorize(shell) ;
200 
201    /*-- misc initialization --*/
202 
203    dset = NULL ;           /* not editing anything */
204    ZERO_IDCODE(dset_idc) ;
205    dset_ival = 0 ; AV_assign_ival(brick_av,0) ;
206    if( imbase != NULL ){ mri_free(imbase); imbase = NULL; }
207 
208    nudger_open = 1 ;      /* editor is now open for business */
209 
210    SENSITIZE(nudge_pb ,0) ;
211    SENSITIZE(undo_pb  ,0) ;
212    SENSITIZE(redo_pb  ,0) ;
213    SENSITIZE(doall_pb ,0) ;
214 
215    SENSITIZE(choose_pb,1) ; AV_SENSITIZE(brick_av,0) ;
216 
217    /* initialize nudgerosity */
218 
219    NUD_clear_CB(NULL,NULL,NULL) ;
220 
221    LOAD_DIAG_DMAT(rmat,1.0,1.0,1.0) ;
222    LOAD_DFVEC3(svec,0.0,0.0,0.0)    ;
223    NUD_setcumlab() ;
224 
225    /* initialize undo stack */
226 
227    if( undo_rmat != NULL ){ free(undo_rmat); undo_rmat = NULL; }
228    if( undo_svec != NULL ){ free(undo_svec); undo_svec = NULL; }
229    undo_nuse = 0 ;
230    undo_ntop = 0 ;
231    undo_nall = 1 ;
232    undo_rmat = (THD_dmat33 *) malloc(sizeof(THD_dmat33)) ;
233    undo_svec = (THD_dfvec3 *) malloc(sizeof(THD_dfvec3)) ;
234    NUD_undopush() ;  /* top of stack = current transformation */
235 
236    return NULL ;
237 }
238 
239 /*------------------------------------------------------------------------
240   Make the control popup for this thing
241 --------------------------------------------------------------------------*/
242 
243 #define SEP_HOR(ww)  XtVaCreateManagedWidget(                     \
244                        "AFNI" , xmSeparatorWidgetClass , (ww) ,   \
245                           XmNseparatorType , XmSINGLE_LINE ,      \
246                           XmNinitialResourcesPersistent , False , \
247                        NULL )
248 
249 #define SEP_VER(ww) XtVaCreateManagedWidget(                      \
250                        "AFNI" , xmSeparatorWidgetClass , (ww) ,   \
251                           XmNseparatorType , XmDOUBLE_LINE ,      \
252                           XmNorientation   , XmVERTICAL ,         \
253                           XmNinitialResourcesPersistent , False , \
254                        NULL )
255 
256 /*-- structures defining action buttons (at bottom of popup) --*/
257 
258 #define NACT 8  /* number of action buttons */
259 
260 static MCW_action_item NUD_actor[NACT] = {
261  {"Nudge",NUD_nudge_CB,NULL,
262   "Applies Angles and Shifts\nto chosen Brick of dataset","Apply Angles/Shifts",1} ,
263 
264  {"Clear",NUD_clear_CB,NULL,
265   "Clears Angles and\nShifts entry fields","Clear Angles/Shifts",0} ,
266 
267  {"Undo",NUD_undo_CB,NULL,
268   "Undoes previous nudge, if possible","Undo last nudge",0} ,
269 
270  {"Redo",NUD_redo_CB,NULL,
271   "Redoes previously undone nudge","Redo last undone nudge",0} ,
272 
273  {"Help",NUD_help_CB,NULL,
274   "Displays more help" , "Displays more help",0} ,
275 
276  {"Quit",NUD_quit_CB,NULL,
277   "Discard nudges since last\n'Do All' and close down",
278   "Discard nudges and close",0} ,
279 
280  {"Do All",NUD_doall_CB,NULL,
281   "Apply Angles and Shifts to all sub-\nbricks and save dataset to disk" ,
282   "Apply Angles/Shifts; write to disk" , 1 } ,
283 
284  {"Print",NUD_print_CB,NULL,
285   "Print current Angles and Shifts as\na '3drotate' command, to stderr" ,
286   "Print 3drotate command to screen" , 0 }
287 } ;
288 
289 /*------------------------------------------------------------------------*/
290 
NUD_make_widgets(void)291 static void NUD_make_widgets(void)
292 {
293    XmString xstr ;
294    Widget hrc ;
295 
296    /*** top level shell for window manager ***/
297 
298    shell =
299       XtVaAppCreateShell(
300            "AFNI" , "AFNI" , topLevelShellWidgetClass , dc->display ,
301 
302            XmNtitle             , "AFNI Nudger" , /* top of window */
303            XmNiconName          , "Nudger"      , /* label on icon */
304            XmNdeleteResponse    , XmDO_NOTHING  , /* deletion handled below */
305            XmNallowShellResize  , True ,          /* let code resize shell? */
306            XmNmappedWhenManaged , False ,         /* must map it manually */
307            XmNinitialResourcesPersistent , False ,
308       NULL ) ;
309 
310    DC_yokify( shell , dc ) ; /* 14 Sep 1998 */
311 
312 #ifndef DONT_INSTALL_ICONS
313    if( afni48_good )             /* set icon pixmap */
314       XtVaSetValues( shell ,
315                         XmNiconPixmap , afni48_pixmap ,
316                      NULL ) ;
317 #endif
318 
319    if( MCW_isitmwm(shell) )      /* remove some MWM functions */
320       XtVaSetValues( shell ,
321                        XmNmwmFunctions ,
322                        MWM_FUNC_MOVE | MWM_FUNC_CLOSE | MWM_FUNC_MINIMIZE ,
323                      NULL ) ;
324 
325    XmAddWMProtocolCallback(      /* make "Close" window menu work */
326            shell ,
327            XmInternAtom( dc->display , "WM_DELETE_WINDOW" , False ) ,
328            NUD_quit_CB , (XtPointer) plint ) ;
329 
330    /*** rowcolumn widget to hold all user interface stuff ***/
331 
332    rowcol = XtVaCreateWidget(
333              "AFNI" , xmRowColumnWidgetClass , shell ,
334                 XmNpacking     , XmPACK_TIGHT ,
335                 XmNorientation , XmVERTICAL ,
336                 XmNtraversalOn , True  ,
337                 XmNinitialResourcesPersistent , False ,
338              NULL ) ;
339 
340    /*** label at top to let user know who we are ***/
341 
342    xstr = XmStringCreateLtoR( "[No dataset]" ,
343                               XmFONTLIST_DEFAULT_TAG ) ;
344    info_lab = XtVaCreateManagedWidget(
345                  "AFNI" , xmLabelWidgetClass , rowcol ,
346                     XmNlabelString , xstr ,
347                     XmNinitialResourcesPersistent , False ,
348                  NULL ) ;
349    XmStringFree(xstr) ;
350    MCW_register_help( info_lab , "Shows dataset being nudged" ) ;
351    MCW_register_hint( info_lab , "Shows dataset being nudged" ) ;
352 
353    /***** top row of widgets to choose dataset and sub-brick *****/
354 
355    SEP_HOR(rowcol) ;
356 
357    hrc =  XtVaCreateWidget(
358            "AFNI" , xmRowColumnWidgetClass , rowcol ,
359               XmNorientation  , XmHORIZONTAL ,
360               XmNpacking      , XmPACK_TIGHT ,
361               XmNadjustLast   , False ,
362               XmNadjustMargin , False ,
363               XmNtraversalOn  , True  ,
364               XmNmarginWidth  , 0 ,
365               XmNmarginHeight , 0 ,
366               XmNinitialResourcesPersistent , False ,
367            NULL ) ;
368 
369    /*** button to let user choose dataset to edit ***/
370 
371    xstr = XmStringCreateLtoR( "Choose Dataset" , XmFONTLIST_DEFAULT_TAG ) ;
372    choose_pb = XtVaCreateManagedWidget(
373                   "AFNI" , xmPushButtonWidgetClass , hrc ,
374                      XmNlabelString , xstr ,
375                      XmNtraversalOn , True  ,
376                      XmNinitialResourcesPersistent , False ,
377                   NULL ) ;
378    XmStringFree(xstr) ;
379    XtAddCallback( choose_pb, XmNactivateCallback, NUD_choose_CB, NULL ) ;
380    MCW_register_help( choose_pb ,
381                       "Use this to popup a\n"
382                       "'chooser' that lets\n"
383                       "you select which\n"
384                       "dataset to nudge."
385                     ) ;
386    MCW_register_hint( choose_pb , "Popup dataset chooser" ) ;
387 
388    /*** menu to let user choose sub-brick to deal with ***/
389 
390    SEP_VER(hrc) ;
391 
392    brick_av = new_MCW_arrowval(
393                           hrc ,                   /* parent Widget */
394                           "Brick" ,               /* label */
395                           MCW_AV_optmenu ,        /* option menu style */
396                           0 ,                     /* first option */
397                           1 ,                     /* last option */
398                           0 ,                     /* initial selection */
399                           MCW_AV_readtext ,       /* ignored but needed */
400                           0 ,                     /* decimal shift */
401                           NUD_brick_av_CB ,       /* callback when changed */
402                           NULL ,                  /* data for above */
403                           MCW_av_substring_CB ,   /* text creation routine */
404                           NUD_dummy_av_label      /* data for above */
405                         ) ;
406    MCW_reghelp_children( brick_av->wrowcol ,
407                          "Choose the sub-brick\n"
408                          "to nudge interactively;\n"
409                          "you should also be\n"
410                          "viewing this sub-brick" ) ;
411    MCW_reghint_children( brick_av->wrowcol , "Sub-brick to nudge" ) ;
412 
413    /** some miscellaneous controls **/
414 
415    SEP_VER(hrc) ;
416 
417    interp_av = new_MCW_arrowval(
418                         hrc ,                   /* parent Widget */
419                         "Resampling" ,          /* label */
420                         MCW_AV_optmenu ,        /* option menu style */
421                         0 ,                     /* first option */
422                         NRESAM-1 ,              /* last option */
423                         3 ,                     /* initial selection */
424                         MCW_AV_readtext ,       /* ignored but needed */
425                         0 ,                     /* decimal shift */
426                         NULL ,                  /* callback when changed */
427                         NULL ,                  /* data for above */
428                         MCW_av_substring_CB ,   /* text creation routine */
429                         REG_resam_strings       /* data for above */
430                       ) ;
431    MCW_reghint_children( interp_av->wrowcol , "Set interpolation method" ) ;
432 
433    SEP_VER(hrc) ;
434 
435    clip_av = new_MCW_arrowval(
436                         hrc ,                   /* parent Widget */
437                         "Clip" ,                /* label */
438                         MCW_AV_optmenu ,        /* option menu style */
439                         0 ,                     /* first option */
440                         NYESNO-1 ,              /* last option */
441                         1 ,                     /* initial selection */
442                         MCW_AV_readtext ,       /* ignored but needed */
443                         0 ,                     /* decimal shift */
444                         NULL ,                  /* callback when changed */
445                         NULL ,                  /* data for above */
446                         MCW_av_substring_CB ,   /* text creation routine */
447                         YESNO_strings           /* data for above */
448                       ) ;
449    MCW_reghint_children( clip_av->wrowcol , "Clip after interpolation?" ) ;
450 
451    XtManageChild(hrc) ;
452 
453    /********** Angle choosers ***********/
454 
455    SEP_HOR(rowcol) ;
456 
457    hrc =  XtVaCreateWidget(
458            "AFNI" , xmRowColumnWidgetClass , rowcol ,
459               XmNorientation  , XmHORIZONTAL ,
460               XmNpacking      , XmPACK_TIGHT ,
461               XmNadjustLast   , False ,
462               XmNadjustMargin , False ,
463               XmNtraversalOn  , True  ,
464               XmNmarginWidth  , 0 ,
465               XmNmarginHeight , 0 ,
466               XmNinitialResourcesPersistent , False ,
467            NULL ) ;
468 
469    xstr = XmStringCreateLtoR( "Angles: " ,
470                               XmFONTLIST_DEFAULT_TAG ) ;
471    (void) XtVaCreateManagedWidget(
472                  "AFNI" , xmLabelWidgetClass , hrc ,
473                     XmNlabelString , xstr ,
474                     XmNinitialResourcesPersistent , False ,
475                  NULL ) ;
476    XmStringFree(xstr) ;
477 
478    /** the actual angles **/
479 
480    roll_av = new_MCW_arrowval( hrc, "I" ,
481                                 MCW_AV_downup , -300,300,0 ,
482                                 MCW_AV_editext , 1 ,
483                                 NULL , NULL , NULL,NULL ) ;
484    MCW_reghint_children( roll_av->wrowcol , "Roll angle [I-axis]" ) ;
485    XtVaSetValues( roll_av->wtext , XmNcolumns , 6 , NULL ) ;
486    SEP_VER(hrc) ;
487 
488    pitch_av = new_MCW_arrowval( hrc, "R" ,
489                                 MCW_AV_downup , -300,300,0 ,
490                                 MCW_AV_editext , 1 ,
491                                 NULL , NULL , NULL,NULL ) ;
492    MCW_reghint_children( pitch_av->wrowcol , "Pitch angle [R-axis]" ) ;
493    XtVaSetValues( pitch_av->wtext , XmNcolumns , 6 , NULL ) ;
494    SEP_VER(hrc) ;
495 
496    yaw_av = new_MCW_arrowval( hrc, "A" ,
497                                 MCW_AV_downup , -300,300,0 ,
498                                 MCW_AV_editext , 1 ,
499                                 NULL , NULL , NULL,NULL ) ;
500    MCW_reghint_children( yaw_av->wrowcol , "Yaw angle [A-axis]" ) ;
501    XtVaSetValues( yaw_av->wtext , XmNcolumns , 6 , NULL ) ;
502    SEP_VER(hrc) ;
503 
504    /** cumulative label **/
505 
506    xstr = XmStringCreateLtoR( "--" , XmFONTLIST_DEFAULT_TAG ) ;
507    angle_cum_lab = XtVaCreateManagedWidget(
508                      "AFNI" , xmLabelWidgetClass , hrc ,
509                         XmNlabelString , xstr ,
510                         XmNalignment , XmALIGNMENT_CENTER ,
511                         XmNinitialResourcesPersistent , False ,
512                         XmNrecomputeSize , True ,
513 #if 0
514                         XmNmarginHeight  , 0 ,
515                         XmNmarginBottom  , 0 ,
516                         XmNmarginLeft    , 0 ,
517                         XmNmarginRight   , 0 ,
518                         XmNmarginTop     , 0 ,
519                         XmNmarginWidth   , 0 ,
520 #endif
521                         XmNtraversalOn , True  ,
522                      NULL ) ;
523    XmStringFree(xstr) ;
524    MCW_register_hint( angle_cum_lab , "Cumulative [degrees]" ) ;
525 
526    XtManageChild(hrc) ;
527 
528    /********** Shift choosers ***********/
529 
530    /*** SEP_HOR(rowcol) ; ***/
531 
532    hrc =  XtVaCreateWidget(
533            "AFNI" , xmRowColumnWidgetClass , rowcol ,
534               XmNorientation  , XmHORIZONTAL ,
535               XmNpacking      , XmPACK_TIGHT ,
536               XmNadjustLast   , False ,
537               XmNadjustMargin , False ,
538               XmNtraversalOn  , True  ,
539               XmNmarginWidth  , 0 ,
540               XmNmarginHeight , 0 ,
541               XmNinitialResourcesPersistent , False ,
542            NULL ) ;
543 
544    xstr = XmStringCreateLtoR( "Shifts: " ,
545                               XmFONTLIST_DEFAULT_TAG ) ;
546    (void) XtVaCreateManagedWidget(
547                  "AFNI" , xmLabelWidgetClass , hrc ,
548                     XmNlabelString , xstr ,
549                     XmNinitialResourcesPersistent , False ,
550                  NULL ) ;
551    XmStringFree(xstr) ;
552 
553    /** the actual shifts **/
554 
555    dS_av = new_MCW_arrowval( hrc, "S" ,
556                                 MCW_AV_downup , -999,999,0 ,
557                                 MCW_AV_editext , 1 ,
558                                 NULL , NULL , NULL,NULL ) ;
559    MCW_reghint_children( dS_av->wrowcol , "Delta Superior" ) ;
560    XtVaSetValues( dS_av->wtext , XmNcolumns , 6 , NULL ) ;
561    SEP_VER(hrc) ;
562 
563    dL_av = new_MCW_arrowval( hrc, "L" ,
564                                 MCW_AV_downup , -999,999,0 ,
565                                 MCW_AV_editext , 1 ,
566                                 NULL , NULL , NULL,NULL ) ;
567    MCW_reghint_children( dL_av->wrowcol , "Delta Left" ) ;
568    XtVaSetValues( dL_av->wtext , XmNcolumns , 6 , NULL ) ;
569    SEP_VER(hrc) ;
570 
571    dP_av = new_MCW_arrowval( hrc, "P" ,
572                                 MCW_AV_downup , -999,999,0 ,
573                                 MCW_AV_editext , 1 ,
574                                 NULL , NULL , NULL,NULL ) ;
575    MCW_reghint_children( dP_av->wrowcol , "Delta Posterior" ) ;
576    XtVaSetValues( dP_av->wtext , XmNcolumns , 6 , NULL ) ;
577    SEP_VER(hrc) ;
578 
579    /** cumulative label **/
580 
581    xstr = XmStringCreateLtoR( "--" , XmFONTLIST_DEFAULT_TAG ) ;
582    shift_cum_lab = XtVaCreateManagedWidget(
583                      "AFNI" , xmLabelWidgetClass , hrc ,
584                         XmNalignment , XmALIGNMENT_BEGINNING ,
585                         XmNlabelString , xstr ,
586                         XmNinitialResourcesPersistent , False ,
587                         XmNrecomputeSize , True ,
588 #if 0
589                         XmNmarginHeight  , 0 ,
590                         XmNmarginHeight  , 0 ,
591                         XmNmarginBottom  , 0 ,
592                         XmNmarginLeft    , 0 ,
593                         XmNmarginRight   , 0 ,
594                         XmNmarginTop     , 0 ,
595                         XmNmarginWidth   , 0 ,
596 #endif
597                         XmNtraversalOn , True  ,
598                      NULL ) ;
599    XmStringFree(xstr) ;
600    MCW_register_hint( shift_cum_lab , "Cumulative [mm]" ) ;
601 
602    XtManageChild(hrc) ;
603 
604    /*** a set of action buttons below the line ***/
605 
606    SEP_HOR(rowcol) ;
607 
608    (void) MCW_action_area( rowcol , NUD_actor , NACT ) ;
609 
610    nudge_pb = (Widget) NUD_actor[0].data ;
611    clear_pb = (Widget) NUD_actor[1].data ;
612    undo_pb  = (Widget) NUD_actor[2].data ;
613    redo_pb  = (Widget) NUD_actor[3].data ;
614    help_pb  = (Widget) NUD_actor[4].data ;
615    quit_pb  = (Widget) NUD_actor[5].data ;
616    doall_pb = (Widget) NUD_actor[6].data ;
617    print_pb = (Widget) NUD_actor[7].data ;
618 
619    /*** that's all ***/
620 
621    XtManageChild(rowcol) ;
622    XtRealizeWidget(shell) ; NI_sleep(1) ; /* will not be mapped */
623    return ;
624 }
625 
626 /*--------------------------------------------------------------------------
627    Compute a rotation matrix specified by 3 angles:
628       Q = R3 R2 R1, where Ri is rotation about axis axi by angle thi.
629    In these routines, the axis codes (ax1,ax2,ax3, hax1,hax2,hax3, and
630    adx,ady,adz) are globals, computed when the dataset is loaded.
631 ----------------------------------------------------------------------------*/
632 
rotmatrix(double th1,double th2,double th3)633 static THD_dmat33 rotmatrix( double th1 , double th2 , double th3  )
634 {
635    THD_dmat33 q , p ;
636 
637    if( hax1 < 0 ) th1 = -th1 ;
638    if( hax2 < 0 ) th2 = -th2 ;
639    if( hax3 < 0 ) th3 = -th3 ;
640 
641    LOAD_ROT_DMAT( q , th1 , ax1 ) ;
642    LOAD_ROT_DMAT( p , th2 , ax2 ) ; q = DMAT_MUL( p , q ) ;
643    LOAD_ROT_DMAT( p , th3 , ax3 ) ; q = DMAT_MUL( p , q ) ;
644 
645    return q ;
646 }
647 
648 /*-----------------------------------------------------------------------
649   Compute the rotation angles from the matrix
650   [the signs of these may need to be munged]
651 -------------------------------------------------------------------------*/
652 
rotangles(THD_dmat33 rm,double * th1,double * th2,double * th3)653 static void rotangles( THD_dmat33 rm, double *th1, double *th2, double *th3 )
654 {
655    *th2 = asin( rm.mat[ax3][ax1] ) ;
656    *th1 = atan2( -rm.mat[ax3][ax2] , rm.mat[ax3][ax3] ) ;
657    *th3 = atan2( -rm.mat[ax2][ax1] , rm.mat[ax1][ax1] ) ;
658 
659    if( hax1 < 0 ) *th1 = -(*th1) ;
660    if( hax2 < 0 ) *th2 = -(*th2) ;
661    if( hax3 < 0 ) *th3 = -(*th3) ;
662 
663    return ;
664 }
665 
666 /*-----------------------------------------------------------------------
667    Compute the shift vector in dataset coordinates from the user inputs.
668 -------------------------------------------------------------------------*/
669 
shiftvec(double dx,double dy,double dz)670 static THD_dfvec3 shiftvec( double dx , double dy , double dz )
671 {
672    double qdx=0.0,qdy=0.0,qdz=0.0 ;
673    THD_dfvec3 qv ;
674 
675    switch( adx ){
676       case  1: qdx = -dx ; break ;
677       case -1: qdx =  dx ; break ;
678       case  2: qdy = -dx ; break ;
679       case -2: qdy =  dx ; break ;
680       case  3: qdz = -dx ; break ;
681       case -3: qdz =  dx ; break ;
682    }
683 
684    switch( ady ){
685       case  1: qdx = -dy ; break ;
686       case -1: qdx =  dy ; break ;
687       case  2: qdy = -dy ; break ;
688       case -2: qdy =  dy ; break ;
689       case  3: qdz = -dy ; break ;
690       case -3: qdz =  dy ; break ;
691    }
692 
693    switch( adz ){
694       case  1: qdx = -dz ; break ;
695       case -1: qdx =  dz ; break ;
696       case  2: qdy = -dz ; break ;
697       case -2: qdy =  dz ; break ;
698       case  3: qdz = -dz ; break ;
699       case -3: qdz =  dz ; break ;
700    }
701 
702    LOAD_DFVEC3(qv,qdx,qdy,qdz) ; return qv ;
703 }
704 
705 /*-----------------------------------------------------------------------
706    Compute the user-coordinate shifts from the dataset-coordinate vector
707 -------------------------------------------------------------------------*/
708 
shiftdeltas(THD_dfvec3 sv,double * d1,double * d2,double * d3)709 static void shiftdeltas( THD_dfvec3 sv, double *d1, double *d2, double *d3   )
710 {
711    double qdx,qdy,qdz , dx=0.0,dy=0.0,dz=0.0 ;
712 
713    UNLOAD_DFVEC3( sv , qdx,qdy,qdz ) ;
714 
715    switch( adx ){
716       case  1: dx = -qdx ; break ;
717       case -1: dx =  qdx ; break ;
718       case  2: dx = -qdy ; break ;
719       case -2: dx =  qdy ; break ;
720       case  3: dx = -qdz ; break ;
721       case -3: dx =  qdz ; break ;
722    }
723 
724    switch( ady ){
725       case  1: dy = -qdx ; break ;
726       case -1: dy =  qdx ; break ;
727       case  2: dy = -qdy ; break ;
728       case -2: dy =  qdy ; break ;
729       case  3: dy = -qdz ; break ;
730       case -3: dy =  qdz ; break ;
731    }
732 
733    switch( adz ){
734       case  1: dz = -qdx ; break ;
735       case -1: dz =  qdx ; break ;
736       case  2: dz = -qdy ; break ;
737       case -2: dz =  qdy ; break ;
738       case  3: dz = -qdz ; break ;
739       case -3: dz =  qdz ; break ;
740    }
741 
742    *d1 = dx ; *d2 = dy ; *d3 = dz ; return ;
743 }
744 
745 /*-----------------------------------------------------------------------
746    Set the cumulative angles/shifts labels from the current state
747    (stored in globals rmat and svec)
748 -------------------------------------------------------------------------*/
749 
NUD_setcumlab(void)750 static void NUD_setcumlab(void)
751 {
752    double th1,th2,th3 ;
753    XmString xstr ;
754 
755    rotangles( rmat, &th1,&th2,&th3 ) ;
756    th1 *= iha*(180.0/PI) ; th2 *= iha*(180.0/PI) ; th3 *= iha*(180.0/PI) ;
757    xstr = XmStringCreateLtoR( NUD_threestring(th1,th2,th3,'I','R','A') ,
758                               XmFONTLIST_DEFAULT_TAG ) ;
759    XtVaSetValues( angle_cum_lab , XmNlabelString , xstr , NULL ) ;
760    XmStringFree(xstr) ;
761 
762    shiftdeltas( svec , &th1,&th2,&th3 ) ;
763    xstr = XmStringCreateLtoR( NUD_threestring(th1,th2,th3,'S','L','P') ,
764                               XmFONTLIST_DEFAULT_TAG ) ;
765    XtVaSetValues( shift_cum_lab , XmNlabelString , xstr , NULL ) ;
766    XmStringFree(xstr) ;
767 
768    return ;
769 }
770 
771 /*-----------------------------------------------------------------------
772   Write a 3drotate partial command to the screen corresponding
773   to the current nudge -- 31 Aug 2000
774 -------------------------------------------------------------------------*/
775 
NUD_print_CB(Widget w,XtPointer cd,XtPointer cb)776 static void NUD_print_CB( Widget w , XtPointer cd , XtPointer cb )
777 {
778    double th1,th2,th3 ;
779    char cbuf[256] = "3drotate" ;
780 
781    strcat( cbuf , " " ) ;
782    strcat( cbuf , REG_resam_options[interp_av->ival] ) ;
783 
784    if( clip_av->ival ) strcat( cbuf , " -clipit" ) ;
785 
786    rotangles( rmat, &th1,&th2,&th3 ) ;
787    th1 *= iha*(180.0/PI) ; th2 *= iha*(180.0/PI) ; th3 *= iha*(180.0/PI) ;
788    strcat( cbuf , " -rotate " ) ;
789    strcat( cbuf , NUD_3string(th1,th2,th3,'I','R','A') ) ;
790 
791    shiftdeltas( svec , &th1,&th2,&th3 ) ;
792    strcat( cbuf , " -ashift " ) ;
793    strcat( cbuf , NUD_3string(th1,th2,th3,'S','L','P') ) ;
794 
795    strcat( cbuf , " -prefix ???  inputdataset" ) ;
796 
797    fprintf(stderr,"\nCurrent Nudge command is:\n%s\n",cbuf ) ;
798    return ;
799 }
800 
801 /*-----------------------------------------------------------------------
802    Push the current state (rmat and svec) onto the undo stack
803 -------------------------------------------------------------------------*/
804 
NUD_undopush(void)805 static void NUD_undopush(void)
806 {
807    if( undo_nuse >= undo_nall ){
808       undo_nall = undo_nuse + 4 ;
809       undo_rmat = (THD_dmat33 *)realloc( undo_rmat, sizeof(THD_dmat33)*undo_nall );
810       undo_svec = (THD_dfvec3 *)realloc( undo_svec, sizeof(THD_dfvec3)*undo_nall );
811    }
812 
813    undo_rmat[undo_nuse] = rmat ;
814    undo_svec[undo_nuse] = svec ;
815    undo_nuse++ ; undo_ntop = undo_nuse ; SENSITIZE(redo_pb,0) ;
816    return ;
817 }
818 
819 /*-------------------------------------------------------------------
820   Callback for Nudge button
821 ---------------------------------------------------------------------*/
822 
NUD_nudge_CB(Widget w,XtPointer client_data,XtPointer call_data)823 static void NUD_nudge_CB( Widget w, XtPointer client_data, XtPointer call_data )
824 {
825    float roll,pitch,yaw , dS,dL,dP ;
826    THD_dmat33 new_rmat ;
827    THD_dfvec3 new_svec , qv ;
828 
829    if( dset == NULL ){ XBell(dc->display,100); return; }  /* shouldn't happen */
830 
831    roll  = (PI/180.0)*roll_av->fval  ;
832    pitch = (PI/180.0)*pitch_av->fval ;
833    yaw   = (PI/180.0)*yaw_av->fval   ;
834    dS    = dS_av->fval ;
835    dL    = dL_av->fval ;
836    dP    = dP_av->fval ;
837 
838    if( roll==0.0 && pitch==0.0 && yaw==0.0 && dS==0.0 && dL==0.0 && dP==0.0 ){
839       (void) MCW_popup_message( nudge_pb ,
840                                    " \n"
841                                    "** Can't nudge dataset! **\n"
842                                    "** All Angle and Shifts **\n"
843                                    "** are zero.            **\n" ,
844                                 MCW_USER_KILL | MCW_TIMER_KILL ) ;
845       XBell( dc->display , 100 ) ;
846       return ;
847    }
848 
849    SENSITIZE(undo_pb,1)   ; SENSITIZE(doall_pb,1)    ;
850    SENSITIZE(choose_pb,0) ; AV_SENSITIZE(brick_av,0) ;
851 
852    new_rmat = rotmatrix( roll, pitch, yaw ) ;
853    rmat     = DMAT_MUL( new_rmat , rmat ) ;
854 
855    new_svec = DMATVEC( new_rmat , svec )  ;
856    qv       = shiftvec( dS , dL , dP ) ;
857    svec     = ADD_DFVEC3( new_svec , qv ) ;
858 
859    NUD_undopush() ;       /* push new transformatin onto undo list */
860    NUD_setcumlab() ;      /* draw the cumulative labels */
861 
862    /* actually do something here */
863 
864    if( imbase == NULL )                             /* first time: get base */
865       imbase = mri_copy( DSET_BRICK(dset,dset_ival) ) ;
866 
867    NUD_update_base( nudge_pb ) ; return ;
868 }
869 
870 /*-------------------------------------------------------------------
871   Callback for Clear button
872 ---------------------------------------------------------------------*/
873 
NUD_clear_CB(Widget w,XtPointer client_data,XtPointer call_data)874 static void NUD_clear_CB( Widget w, XtPointer client_data, XtPointer call_data )
875 {
876    AV_assign_fval( roll_av  , 0.0 ) ;
877    AV_assign_fval( pitch_av , 0.0 ) ;
878    AV_assign_fval( yaw_av   , 0.0 ) ;
879    AV_assign_fval( dS_av    , 0.0 ) ;
880    AV_assign_fval( dL_av    , 0.0 ) ;
881    AV_assign_fval( dP_av    , 0.0 ) ;
882    return ;
883 }
884 
885 /*-------------------------------------------------------------------
886   Callback for Undo button
887 ---------------------------------------------------------------------*/
888 
NUD_undo_CB(Widget w,XtPointer client_data,XtPointer call_data)889 static void NUD_undo_CB( Widget w, XtPointer client_data, XtPointer call_data )
890 {
891    if( undo_nuse <= 1 ){ XBell(dc->display,100); return; }
892 
893    undo_nuse-- ;
894    rmat = undo_rmat[undo_nuse-1] ;
895    svec = undo_svec[undo_nuse-1] ;
896    NUD_setcumlab() ;
897    if( undo_nuse <= 1 ){ SENSITIZE(undo_pb,0); SENSITIZE(doall_pb,0); }
898    SENSITIZE(redo_pb,1) ;
899 
900    /* actually do something here */
901 
902    NUD_update_base( undo_pb ) ; return ;
903 }
904 
905 /*-------------------------------------------------------------------
906   Callback for Redo button
907 ---------------------------------------------------------------------*/
908 
NUD_redo_CB(Widget w,XtPointer client_data,XtPointer call_data)909 static void NUD_redo_CB( Widget w, XtPointer client_data, XtPointer call_data )
910 {
911    if( undo_ntop <= undo_nuse ){ XBell(dc->display,100); return; }
912 
913    rmat = undo_rmat[undo_nuse] ;
914    svec = undo_svec[undo_nuse] ;
915    undo_nuse++ ;
916    NUD_setcumlab() ;
917    if( undo_nuse >= undo_ntop ) SENSITIZE(redo_pb,0) ;
918    SENSITIZE(undo_pb,1) ; SENSITIZE(doall_pb,1) ;
919 
920    /* actually do something here */
921 
922    NUD_update_base( redo_pb ) ; return ;
923 }
924 
925 /*-------------------------------------------------------------------
926   Callback for Quit button
927 ---------------------------------------------------------------------*/
928 
NUD_quit_CB(Widget w,XtPointer client_data,XtPointer call_data)929 static void NUD_quit_CB( Widget w, XtPointer client_data, XtPointer call_data )
930 {
931    if( dset != NULL ){
932       DSET_unlock(dset) ; DSET_unload(dset) ; DSET_anyize(dset) ;
933       if( undo_nuse > 1 ){                 /* if not at the null nudge */
934          MCW_invert_widget(quit_pb) ;
935          THD_load_statistics( dset ) ;
936          PLUTO_dset_redisplay( dset ) ;
937          AFNI_process_drawnotice( im3d ) ;
938          MCW_invert_widget(quit_pb) ;
939       }
940       dset = NULL ;
941    }
942 
943    if( imbase != NULL ){ mri_free(imbase); imbase = NULL; }
944 
945    if( undo_rmat != NULL ){ free(undo_rmat); undo_rmat = NULL; }
946    if( undo_svec != NULL ){ free(undo_svec); undo_svec = NULL; }
947    undo_nall = undo_nuse = 0 ;
948 
949    XtUnmapWidget( shell ) ; nudger_open = 0 ;
950    return ;
951 }
952 
953 /*-------------------------------------------------------------------
954   Callback for help button
955 ---------------------------------------------------------------------*/
956 
NUD_help_CB(Widget w,XtPointer client_data,XtPointer call_data)957 static void NUD_help_CB( Widget w, XtPointer client_data, XtPointer call_data )
958 {
959    (void ) new_MCW_textwin( help_pb ,
960 
961      "PURPOSE: Nudge a dataset's position a little.\n"
962      "\n"
963      "CONTROLS:\n"
964      "Choose Dataset: button to choose which dataset to move around.\n"
965      "Brick:          which single sub-brick of the dataset will be moved,\n"
966      "                  prior to use of 'Do All'.\n"
967      "Resampling:     choose interpolation method for brick resampling\n"
968      "Clip:           clip each brick after interpolation?\n"
969      "---------------------------------------------------------------------\n"
970      "Angles: the entry fields are the rotational angles to be applied:\n"
971      "          positive I = roll  = looking to the left\n"
972      "          positive R = pitch = nodding the head forward\n"
973      "          positive A = yaw   = tilting left ear towards shoulder\n"
974      "Shifts: the entry fields are the translational shifts to be applied.\n"
975      "          positive S = superior  = shifting head upwards\n"
976      "          positive L = left      = shifting head leftwards\n"
977      "          positive P = posterior = shifting head backwards\n"
978      "---------------------------------------------------------------------\n"
979      "Nudge:  apply the Angles and Shifts entered above to the chosen Brick;\n"
980      "          also updates the cumulative angles/shifts\n"
981      "Clear:  set the Angles and Shifts to zero\n"
982      "Undo:   undo the previous Nudge\n"
983      "Redo:   redo the previously undone Nudge\n"
984      "Quit:   exit, restoring the dataset to its values stored on disk\n"
985      "Do All: apply cumulative angles/shifts to all sub-bricks; write to disk\n"
986      "Print:  print (to stderr) 3drotate command equivalent to current nudge\n"
987      "        [use 'Print' before 'Do All', since 'Do All' sets nudge to 0]\n"
988      "=======================================================================\n"
989      "USAGE SUGGESTIONS:\n"
990      "* Load the dataset and brick to nudge into this plugin.\n"
991      "* Switch the image viewers to the same dataset and brick;\n"
992      "    as the brick is nudged, then images will be redrawn.\n"
993      "* You can also use the rendering plugin - if DynaDraw is on,\n"
994      "    the brick will be re-rendered with each nudge.\n"
995      "* If you are comparing the nudged dataset to a reference, and\n"
996      "    trying to realign the two, one way is to temporarily make\n"
997      "    one of them a fim dataset (using '3drefit -fim'), and then\n"
998      "    display it as a color overlay.  When you are happy with\n"
999      "    the alignment, you can quit AFNI and use 3drefit to change\n"
1000      "    the dataset back to whatever it was before.\n"
1001      "* When using the '-fim' trick on the nudged dataset, you will\n"
1002      "    have to set the colors and color scaling range appropriately\n"
1003      "    on the 'Define Function' control panel, otherwise the color\n"
1004      "    overlay will look so peculiar as to be useless.\n"
1005      "* Nudge-ing on the single sub-brick is done only in memory, so if\n"
1006      "    you Quit, the dataset on disk will be unchanged.  When you use\n"
1007      "    'Do All', all sub-bricks will be nudged the same way and then\n"
1008      "    be written out to disk, overwriting the original dataset .BRIK.\n"
1009      "* Instead of using 'Do All', you can use 'Print' to see the 3drotate\n"
1010      "    parameters to use.  You can then apply these to as many datasets\n"
1011      "    you want (e.g., in a shell script, to nudge a whole bunch of\n"
1012      "    datasets exactly the same way).\n"
1013      "* I suggest you do NOT nudge functional activation maps.  It is better\n"
1014      "    to nudge the anatomical underlay, or nudge the original EPI time\n"
1015      "    series.  Nudging a dataset implies interpolating to a new grid,\n"
1016      "    and this is problematical for the non-smooth activation maps.\n"
1017      "=======================================================================\n"
1018      "WARNINGS:\n"
1019      "* Values past the edge of the dataset are 0, and if they are shifted\n"
1020      "    into the volume covered by the dataset, you will get 0's there.\n"
1021      "* Values shifted past the edge of the volume covered by the dataset\n"
1022      "    will be LOST.  This may seem obvious, but when you are shifting\n"
1023      "    a functional dataset that is smaller than the anatomical underlay,\n"
1024      "    it can look mysterious.\n"
1025      "* One solution to the problem above is to use 3dZeropad to explicitly\n"
1026      "    put a layer of 0's around the outside of the functional dataset\n"
1027      "    volume.  Shifted values will then go into this 0 buffer, and\n"
1028      "    will not be lost.\n"
1029      "=======================================================================\n"
1030      "ALGORITHM:\n"
1031      "* Uses the same basic routines as program 3drotate; see\n"
1032      "      RW Cox and A Jesmanowicz.\n"
1033      "      Real-time 3D image registration for functional MRI.\n"
1034      "      Magnetic Resonance in Medicine, 42: 1014-1018, 1999.\n"
1035      "    Also see 3drotate.c, 3dvolreg.c, and thd_rot3d.c.\n"
1036      "* Bricks are not repeatedly interpolated as you nudge - each nudge\n"
1037      "    takes place using the cumulative angles/shifts starting from the\n"
1038      "    brick read in from disk.  However, if you re-nudge a dataset\n"
1039      "    after using 'Do All' to write to disk, you will then be re-\n"
1040      "    interpolating an already interpolated dataset.\n"
1041      "* Note that cumulative angles/shifts may not be exactly the sum of\n"
1042      "    the incremental nudges.  This effect is due to the non-Abelian\n"
1043      "    nature of 3D rotation (i.e., doing rotation A then B is not the\n"
1044      "    same as doing rotation B then A).\n"
1045      "* The angle and shift parameters are specified in the same order\n"
1046      "    as output by 3dvolreg, and would be input to 3drotate as\n"
1047      "      -rotate <roll>I <pitch>R <yaw>A  -ashift <dS>S <dL>L <dP>P\n"
1048      "* Rotations are about the center of the rectangular volume of the\n"
1049      "    dataset.  This is not likely to be the center of the brain.\n"
1050      "=======================================================================\n"
1051      "AUTHOR: RWCox, April 2000\n"
1052      "=======================================================================\n"
1053 
1054     , TEXT_READONLY ) ;
1055    return ;
1056 }
1057 
1058 /*-------------------------------------------------------------------
1059   Callback for choose button - give the user some dataset choices.
1060   Criteria for datasets that can be nudged:
1061     - must be in current session
1062     - must have actual bricks
1063 ---------------------------------------------------------------------*/
1064 
1065 static int                  ndsl = 0 ;
1066 static PLUGIN_dataset_link * dsl = NULL ;
1067 
NUD_choose_CB(Widget w,XtPointer client_data,XtPointer call_data)1068 static void NUD_choose_CB( Widget w, XtPointer client_data, XtPointer call_data )
1069 {
1070    THD_session * ss  = im3d->ss_now ;           /* current session */
1071    int           vv  = im3d->vinfo->view_type ; /* view type */
1072    THD_3dim_dataset * qset ;
1073    int id , ltop , llen ;
1074    char qnam[THD_MAX_NAME] , label[THD_MAX_NAME] ;
1075    static char ** strlist = NULL ;
1076 
1077    /* can't do this if a dataset is already active and changed */
1078 
1079    if( dset != NULL && undo_nuse > 1 ){
1080       (void) MCW_popup_message( choose_pb ,
1081                                    "Can't change datasets until\n"
1082                                    "you save the changes you've\n"
1083                                    "already made.  Or you could\n"
1084                                    "'Quit' and re-start the Editor" ,
1085                                 MCW_USER_KILL | MCW_TIMER_KILL ) ;
1086       XBell( dc->display , 100 ) ;
1087       return ;
1088    }
1089 
1090    /* initialize */
1091 
1092    ndsl = 0 ;
1093 
1094    /* scan datasets */
1095 
1096    for( id=0 ; id < ss->num_dsset ; id++ ){
1097       qset = GET_SESSION_DSET(ss,id,vv);
1098 /*      qset = ss->dsset_xform_table[id][vv] ;*/
1099 
1100       if( ! ISVALID_DSET (qset) ) continue ;  /* skip */
1101       if( ! DSET_INMEMORY(qset) ) continue ;
1102       if( DSET_BRICK_TYPE(qset,0) == MRI_complex ) continue ;
1103 
1104       ndsl++ ;
1105       dsl = (PLUGIN_dataset_link *)
1106               XtRealloc( (char *) dsl , sizeof(PLUGIN_dataset_link)*ndsl ) ;
1107 
1108       make_PLUGIN_dataset_link( qset , dsl + (ndsl-1) ) ;
1109    }
1110 
1111    /* found nothing? exit */
1112 
1113    if( ndsl < 1 ){
1114       (void) MCW_popup_message( choose_pb ,
1115                                    " \nDidn't find any datasets to edit!\n" ,
1116                                 MCW_USER_KILL | MCW_TIMER_KILL ) ;
1117       XBell( dc->display , 100 ) ;
1118       return ;
1119    }
1120 
1121    /*--- 23 Nov 1996: loop over dataset links and patch their titles
1122                       to include an indicator of the dataset type    ---*/
1123 
1124    ltop = 4 ;
1125    for( id=0 ; id < ndsl ; id++ ){
1126       llen = strlen(dsl[id].title) ;
1127       ltop = MAX(ltop,llen) ;
1128    }
1129 
1130    for( id=0 ; id < ndsl ; id++ ){
1131       qset = PLUTO_find_dset( &(dsl[id].idcode) ) ;
1132       if( ! ISVALID_DSET(qset) ) continue ;
1133       if( ISANAT(qset) ){
1134          if( ISANATBUCKET(qset) )         /* 30 Nov 1997 */
1135             sprintf(qnam,"%-*s [%s:%d]" ,
1136                     ltop,dsl[id].title ,
1137                     ANAT_prefixstr[qset->func_type] , DSET_NVALS(qset) ) ;
1138 
1139          else if( DSET_NUM_TIMES(qset) == 1 )
1140             sprintf(qnam,"%-*s [%s]" ,
1141                     ltop,dsl[id].title ,
1142                     ANAT_prefixstr[qset->func_type] ) ;
1143 
1144          else
1145             sprintf(qnam,"%-*s [%s:3D+t:%d]" ,
1146                     ltop,dsl[id].title ,
1147                     ANAT_prefixstr[qset->func_type] , DSET_NUM_TIMES(qset) ) ;
1148 
1149       } else {
1150          if( ISFUNCBUCKET(qset) )         /* 30 Nov 1997 */
1151             sprintf(qnam,"%-*s [%s:%d]" ,
1152                     ltop,dsl[id].title ,
1153                     FUNC_prefixstr[qset->func_type] , DSET_NVALS(qset) ) ;
1154 
1155          else if( DSET_NUM_TIMES(qset) == 1 )
1156             sprintf(qnam,"%-*s [%s]" ,
1157                     ltop,dsl[id].title ,
1158                     FUNC_prefixstr[qset->func_type] ) ;
1159 
1160          else
1161             sprintf(qnam,"%-*s [%s:3D+t:%d]" ,
1162                     ltop,dsl[id].title ,
1163                     FUNC_prefixstr[qset->func_type] , DSET_NVALS(qset) ) ;
1164       }
1165 
1166       if( DSET_COMPRESSED(qset) ) strcat(qnam,"z") ;
1167 
1168       strcpy( dsl[id].title , qnam ) ;
1169    }
1170 
1171    /*--- make a popup chooser for the user to browse ---*/
1172 
1173    POPDOWN_strlist_chooser ;
1174 
1175    strlist = (char **) XtRealloc( (char *)strlist , sizeof(char *)*ndsl ) ;
1176    for( id=0 ; id < ndsl ; id++ ) strlist[id] = dsl[id].title ;
1177 
1178    sprintf( label , "AFNI Dataset from\nthe %s" , VIEW_typestr[vv] ) ;
1179 
1180    MCW_choose_strlist( w , label , ndsl , -1 , strlist ,
1181                        NUD_finalize_dset_CB , NULL     ) ;
1182 
1183    return ;
1184 }
1185 
1186 /*---------------------------------------------------------------------------
1187    Make a label for a sub-brick selector menu
1188 -----------------------------------------------------------------------------*/
1189 
NUD_brick_av_label_CB(MCW_arrowval * av,XtPointer cd)1190 static char * NUD_brick_av_label_CB( MCW_arrowval * av , XtPointer cd )
1191 {
1192    static char blab[32] ;
1193    THD_3dim_dataset * dset = (THD_3dim_dataset *) cd ;
1194    static char *lfmt[3] = { "#%1d %-14.14s", "#%2d %-14.14s", "#%3d %-14.14s" };
1195    static char *rfmt[3] = { "%-14.14s #%1d", "%-14.14s #%2d", "%-14.14s #%3d" };
1196 
1197    if( ISVALID_3DIM_DATASET(dset) ){
1198 
1199 #ifdef USE_RIGHT_BUCK_LABELS
1200       if( DSET_NVALS(dset) < 10 )
1201         sprintf(blab, rfmt[0] , DSET_BRICK_LABEL(dset,av->ival) , av->ival ) ;
1202       else if( DSET_NVALS(dset) < 100 )
1203         sprintf(blab, rfmt[1] , DSET_BRICK_LABEL(dset,av->ival) , av->ival ) ;
1204       else
1205         sprintf(blab, rfmt[2] , DSET_BRICK_LABEL(dset,av->ival) , av->ival ) ;
1206 #else
1207       if( DSET_NVALS(dset) < 10 )
1208         sprintf(blab, lfmt[0] , av->ival , DSET_BRICK_LABEL(dset,av->ival) ) ;
1209       else if( DSET_NVALS(dset) < 100 )
1210         sprintf(blab, lfmt[1] , av->ival , DSET_BRICK_LABEL(dset,av->ival) ) ;
1211       else
1212         sprintf(blab, lfmt[2] , av->ival , DSET_BRICK_LABEL(dset,av->ival) ) ;
1213 #endif
1214    }
1215    else
1216       sprintf(blab," #%d ",av->ival) ;  /* should not happen! */
1217 
1218    return blab ;
1219 }
1220 
1221 /*------------------------------------------------------------------------------
1222   Called when the user actually makes the choice of dataset
1223 --------------------------------------------------------------------------------*/
1224 
NUD_finalize_dset_CB(Widget w,XtPointer fd,MCW_choose_cbs * cbs)1225 static void NUD_finalize_dset_CB( Widget w, XtPointer fd, MCW_choose_cbs * cbs )
1226 {
1227    int id = cbs->ival ;
1228    THD_3dim_dataset * qset ;
1229    XmString xstr ;
1230    char str[256] ;
1231 
1232    /* check for errors */
1233 
1234    if( !nudger_open ){ POPDOWN_strlist_chooser; XBell(dc->display,100); return; }
1235 
1236    if( dset != NULL && undo_nuse > 1 ){ XBell(dc->display,100); return; }
1237 
1238    if( id < 0 || id >= ndsl ){ XBell(dc->display,100); return; }
1239 
1240    qset = PLUTO_find_dset( &(dsl[id].idcode) ) ;  /* the new dataset */
1241 
1242    if( qset == NULL ){ XBell(dc->display,100); return; } /* shouldn't happen */
1243 
1244    /* if not same as old dataset, close that one down */
1245 
1246    if( dset != NULL && qset != dset ){
1247       DSET_unlock(dset) ; DSET_unload(dset) ; DSET_anyize(dset) ;
1248    }
1249 
1250    /* accept this dataset */
1251 
1252    dset = qset ; dset_idc = dset->idcode ;
1253 
1254    undo_nuse = 1 ;
1255    undo_ntop = 1 ;
1256    rmat = undo_rmat[0] ;
1257    svec = undo_svec[0] ; NUD_setcumlab() ;
1258 
1259    SENSITIZE(undo_pb ,0) ;
1260    SENSITIZE(redo_pb ,0) ;
1261    SENSITIZE(nudge_pb,1) ;
1262    SENSITIZE(doall_pb,0) ;
1263 
1264    /* write the informational label */
1265 
1266    xstr = XmStringCreateLtoR( dsl[id].title , XmFONTLIST_DEFAULT_TAG ) ;
1267    XtVaSetValues( info_lab , XmNlabelString , xstr , NULL ) ;
1268    XmStringFree(xstr) ;
1269 
1270    /* lock and load this one into memory (not mmap) */
1271 
1272    DSET_mallocize(dset) ; DSET_lock(dset) ; DSET_load(dset) ;
1273 
1274    if( imbase != NULL ){ mri_free(imbase); imbase = NULL; }
1275 
1276    /* refit the sub-brick selector menu */
1277 
1278    if( dset_ival >= DSET_NVALS(dset) ) dset_ival = DSET_NVALS(dset)-1 ;
1279 
1280    refit_MCW_optmenu( brick_av ,
1281                       0 ,                       /* new minval */
1282                       DSET_NVALS(dset)-1 ,      /* new maxval */
1283                       dset_ival ,               /* new inival */
1284                       0 ,                       /* new decim? */
1285                       NUD_brick_av_label_CB ,   /* text routine */
1286                       dset                      /* text data */
1287                     ) ;
1288 
1289    AV_SENSITIZE( brick_av , (DSET_NVALS(dset) > 1) ) ;
1290 
1291    /* set codes indicating rotation axes:
1292       iha = left or right handed coordinate order in dataset
1293       ax1 = axis index for 'I'   hax1 = sign for roll angle
1294       ax2 = axis index for 'R'   hax2 = sign for pitch angle
1295       ax3 = axis index for 'A'   hax3 = sign for yaw angle   */
1296 
1297    iha = THD_handedness( dset ) ;
1298    ax1 = THD_axcode(dset,'I') ; hax1 = ax1 ; ax1 = abs(ax1)-1 ; /* roll */
1299    ax2 = THD_axcode(dset,'R') ; hax2 = ax2 ; ax2 = abs(ax2)-1 ; /* pitch */
1300    ax3 = THD_axcode(dset,'A') ; hax3 = ax3 ; ax3 = abs(ax3)-1 ; /* yaw */
1301 
1302    adx = THD_axcode(dset,'S') ;  /* for shifts */
1303    ady = THD_axcode(dset,'L') ;
1304    adz = THD_axcode(dset,'P') ;
1305 
1306 #if 0
1307    fprintf(stderr,"NUD_finalize_dset_CB: iha=%d\n"
1308                   "  ax1 =%2d ax2 =%2d ax3 =%2d\n"
1309                   "  hax1=%2d hax2=%2d hax3=%2d\n"
1310                   "  adx =%2d ady =%2d adz =%2d\n" ,
1311            iha , ax1,ax2,ax3 , hax1,hax2,hax3 , adx,ady,adz ) ;
1312 #endif
1313 
1314    return ;
1315 }
1316 
1317 /*-------------------------------------------------------------------
1318   Callback for sub-brick index arrowval
1319 ---------------------------------------------------------------------*/
1320 
NUD_brick_av_CB(MCW_arrowval * av,XtPointer cd)1321 static void NUD_brick_av_CB( MCW_arrowval * av , XtPointer cd )
1322 {
1323    if( imbase != NULL ){ XBell(dc->display,100); return; }
1324    dset_ival = av->ival ;
1325    return ;
1326 }
1327 
1328 /*-------------------------------------------------------------------
1329    Rotate an image in place according to the current specs
1330 ---------------------------------------------------------------------*/
1331 
NUD_rotate(MRI_IMAGE * im)1332 static void NUD_rotate( MRI_IMAGE *im )
1333 {
1334    int clipit=clip_av->ival , mode=REG_resam_ints[interp_av->ival] ;
1335    float cbot=0.0,ctop=0.0 ;
1336    float *fvol ;
1337    double th1,th2,th3 , dx,dy,dz ;
1338 
1339    if( im == NULL || dset == NULL ) return ;
1340 
1341    rotangles( rmat, &th1,&th2,&th3 ) ;
1342    if( hax1 < 0 ) th1 = -th1 ;
1343    if( hax2 < 0 ) th2 = -th2 ;
1344    if( hax3 < 0 ) th3 = -th3 ;
1345    UNLOAD_DFVEC3( svec , dx,dy,dz ) ;
1346 
1347 #if 0
1348 fprintf(stderr,"th1=%g th2=%g th3=%g\n",th1,th2,th3) ;
1349 #endif
1350 
1351    /* nothing to do? */
1352 
1353    if( fabs(th1) < EPS && fabs(th2) < EPS && fabs(th3) < EPS &&
1354        fabs(dx)  < EPS && fabs(dy)  < EPS && fabs(dz)  < EPS   ) return ;
1355 
1356 #if 0
1357    if( clipit && (mode == MRI_LINEAR || mode == MRI_NN) ) clipit = 0 ;
1358 #endif
1359 
1360    /*--- 09 May 2005: RGB input image ==> do each channel separately ---*/
1361 
1362 #undef  BCLIP
1363 #define BCLIP(x)  if((x)<0.0f)(x)=0.0f; else if((x)>255.0)(x)=255.0
1364 
1365    if( im->kind == MRI_rgb ){
1366      MRI_IMARR *imtriple ;
1367      MRI_IMAGE *rim, *gim, *bim, *newim ;
1368      float *fr, *fg, *fb ;
1369      register int ii ;
1370 
1371      imtriple = mri_rgb_to_3float( im ) ;
1372      if( imtriple == NULL ){
1373        fprintf(stderr,"*** mri_rgb_to_3float fails in NUD_rotate!\n"); return;
1374      }
1375      rim = IMAGE_IN_IMARR(imtriple,0) ;
1376      gim = IMAGE_IN_IMARR(imtriple,1) ;
1377      bim = IMAGE_IN_IMARR(imtriple,2) ; FREE_IMARR(imtriple) ;
1378      NUD_rotate(rim); NUD_rotate(gim); NUD_rotate(bim);
1379      fr = MRI_FLOAT_PTR(rim); fg = MRI_FLOAT_PTR(gim); fb = MRI_FLOAT_PTR(bim);
1380      for( ii=0 ; ii < im->nvox ; ii++ ){
1381        BCLIP(fr[ii]) ; BCLIP(fg[ii]) ; BCLIP(fb[ii]) ;
1382      }
1383      newim = mri_3to_rgb( rim, gim, bim ) ;
1384      mri_free(rim) ; mri_free(gim) ; mri_free(bim) ;
1385      memcpy( MRI_RGB_PTR(im) , MRI_RGB_PTR(newim) , 3*im->nvox ) ;
1386      mri_free(newim) ;
1387      return ;
1388    }
1389 
1390    /*--- need a floating point copy? ---*/
1391 
1392    if( im->kind != MRI_float ){
1393       fvol = (float *) malloc( sizeof(float) * im->nvox ) ;
1394       EDIT_coerce_type( im->nvox  ,
1395                         im->kind  , mri_data_pointer(im) ,
1396                         MRI_float , fvol ) ;
1397    } else {
1398       fvol = MRI_FLOAT_PTR(im) ;
1399    }
1400 
1401    /* compute bounds? */
1402 
1403    if( clipit ){
1404       register int ii ; register float bb,tt ;
1405       bb = tt = fvol[0] ;
1406       for( ii=1 ; ii < im->nvox ; ii++ ){
1407               if( fvol[ii] < bb ) bb = fvol[ii] ;
1408          else if( fvol[ii] > tt ) tt = fvol[ii] ;
1409       }
1410       cbot = bb ; ctop = tt ;
1411    }
1412 
1413    /* actually rotate! */
1414 
1415    THD_rota_method( mode ) ;  /* this line fixed 28 Nov 2000 */
1416 
1417    THD_rota_vol( im->nx , im->ny , im->nz ,
1418                  fabs(DSET_DX(dset)), fabs(DSET_DY(dset)), fabs(DSET_DZ(dset)),
1419                  fvol , ax1,th1 , ax2,th2 , ax3,th3 , DELTA_AFTER,dx,dy,dz ) ;
1420 
1421    /* apply bounds? */
1422 
1423    if( clipit ){
1424      register int ii ; register float bb,tt ;
1425      bb = cbot ; tt = ctop ;
1426      for( ii=0 ; ii < im->nvox ; ii++ ){
1427             if( fvol[ii] < bb ) fvol[ii] = bb ;
1428        else if( fvol[ii] > tt ) fvol[ii] = tt ;
1429      }
1430    }
1431 
1432    /* convert type? */
1433 
1434    if( im->kind != MRI_float ){
1435       EDIT_coerce_type( im->nvox , MRI_float , fvol ,
1436                         im->kind  , mri_data_pointer(im) ) ;
1437       free(fvol) ;
1438    }
1439 
1440    return ;
1441 }
1442 
1443 /*--------------------------------------------------------------------------
1444   Actually nudge the base brick, and then redisplay it.
1445 ----------------------------------------------------------------------------*/
1446 
NUD_update_base(Widget w)1447 static void NUD_update_base(Widget w)
1448 {
1449    MRI_IMAGE * im ;
1450 
1451    if( dset == NULL || imbase == NULL || dset_ival >= DSET_NVALS(dset) ) return;
1452 
1453    if( w != NULL ) MCW_invert_widget(w) ;
1454 
1455    im = mri_copy(imbase) ;                                  /* copy base */
1456    NUD_rotate( im ) ;                                       /* rotate copy */
1457    EDIT_substitute_brick( dset , dset_ival ,                /* put into dset */
1458                           im->kind , mri_data_pointer(im) );
1459 
1460    if( ISVALID_STATISTIC(dset->stats) )                     /* 27 Nov 2000 */
1461       THD_update_statistics( dset ) ;
1462 
1463    mri_clear_data_pointer( im ) ; mri_free(im) ;            /* toss the trash */
1464 
1465    PLUTO_dset_redisplay( dset ) ;                           /* re-show it */
1466    AFNI_process_drawnotice( im3d ) ;                        /* anyone cares? */
1467    if( w != NULL ) MCW_invert_widget(w) ;
1468    return ;
1469 }
1470 
1471 /*-------------------------------------------------------------------
1472   Callback for Do All button - nudge all bricks
1473 ---------------------------------------------------------------------*/
1474 
NUD_doall_CB(Widget w,XtPointer client_data,XtPointer call_data)1475 static void NUD_doall_CB( Widget w, XtPointer client_data, XtPointer call_data )
1476 {
1477    int iv , nvals ;
1478    MRI_IMAGE * im ;
1479    char str[256] ;
1480    double th1,th2,th3 ;
1481    Widget meter=NULL ;
1482 
1483    if( dset == NULL || imbase == NULL || undo_nuse == 1 ){  /* bad bad bad */
1484       XBell(dc->display,100); return;
1485    }
1486 
1487    /*----- actually do something -----*/
1488 
1489    /* copy imbase back into dataset */
1490 
1491    EDIT_substitute_brick( dset , dset_ival ,
1492                           imbase->kind , mri_data_pointer(imbase) ) ;
1493    mri_clear_data_pointer(imbase) ; mri_free(imbase) ; imbase = NULL ;
1494 
1495    /* nudge each sub-brick */
1496 
1497    nvals = DSET_NVALS(dset) ;
1498    if( nvals > 1 )
1499       meter = MCW_popup_meter( shell , METER_TOP_WIDE ) ;
1500 
1501    for( iv=0 ; iv < nvals ; iv++ ){
1502       MCW_invert_widget(doall_pb) ;
1503 
1504       im = mri_copy( DSET_BRICK(dset,iv) ) ;
1505       NUD_rotate( im ) ;
1506       EDIT_substitute_brick( dset , iv ,
1507                              im->kind , mri_data_pointer(im) ) ;
1508       mri_clear_data_pointer( im ) ; mri_free(im) ;
1509 
1510       if( nvals > 1 )
1511          MCW_set_meter( meter , (int)(100.0*(iv+0.5)/nvals) ) ;
1512    }
1513 
1514    /* store the history of what we just did */
1515 
1516    rotangles( rmat, &th1,&th2,&th3 ) ;
1517    th1 *= iha*(180.0/PI) ; th2 *= iha*(180.0/PI) ; th3 *= iha*(180.0/PI) ;
1518    sprintf(str,"plug_nudge: -rotate %s",
1519            NUD_threestring(th1,th2,th3,'I','R','A') ) ;
1520 
1521    iv = strlen(str) ;
1522    shiftdeltas( svec , &th1,&th2,&th3 ) ;
1523    sprintf(str+iv," -ashift %s" ,
1524            NUD_threestring(th1,th2,th3,'S','L','P') ) ;
1525 
1526    tross_Append_History( dset , str );
1527 
1528    /* write to disk, and redisplay */
1529 
1530    if( nvals > 1 ) MCW_set_meter( meter , 100 ) ;
1531 
1532    DSET_overwrite( dset ) ;
1533    PLUTO_dset_redisplay( dset ) ;
1534    AFNI_process_drawnotice( im3d ) ;
1535 
1536    /*----- reset to 0 nudge -----*/
1537 
1538    rmat = undo_rmat[0] ; svec = undo_svec[0] ; NUD_setcumlab() ;
1539 
1540    /* clear undo stack */
1541 
1542    undo_nuse = undo_ntop = 1 ;
1543    SENSITIZE(undo_pb,0)  ; SENSITIZE(redo_pb,0) ;
1544 
1545    /* can't Do All again right now */
1546 
1547    SENSITIZE(doall_pb,0) ;
1548 
1549    /* allow user to change datasets again */
1550 
1551    SENSITIZE(choose_pb,1) ;
1552    AV_SENSITIZE( brick_av , (nvals > 1) ) ;
1553 
1554    if( nvals > 1 )
1555       MCW_popdown_meter(meter) ;
1556    if( nvals%2 == 1 ) MCW_invert_widget(doall_pb) ;
1557 
1558    return ;
1559 }
1560