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