/*****************************************************************************
Major portions of this software are copyrighted by the Medical College
of Wisconsin, 1994-2000, and are released under the Gnu General Public
License, Version 2. See the file README.Copyright for details.
******************************************************************************/
#include "vecmat.h"
#include "afni.h"
#ifndef ALLOW_PLUGINS
# error "Plugins not properly set up -- see machdep.h"
#endif
/***********************************************************************
Plugin to nudge a dataset around.
Makes a custom interface.
-- RWCox -- April 2000
************************************************************************/
/*----------------- prototypes for internal routines -----------------*/
static PLUGIN_interface * plint = NULL ;
char * NUD_main( PLUGIN_interface * ) ; /* the entry point */
static void NUD_make_widgets(void) ;
static void NUD_nudge_CB ( Widget , XtPointer , XtPointer ) ;
static void NUD_clear_CB ( Widget , XtPointer , XtPointer ) ;
static void NUD_undo_CB ( Widget , XtPointer , XtPointer ) ;
static void NUD_redo_CB ( Widget , XtPointer , XtPointer ) ;
static void NUD_help_CB ( Widget , XtPointer , XtPointer ) ;
static void NUD_quit_CB ( Widget , XtPointer , XtPointer ) ;
static void NUD_doall_CB ( Widget , XtPointer , XtPointer ) ;
static void NUD_choose_CB( Widget , XtPointer , XtPointer ) ;
static void NUD_print_CB ( Widget , XtPointer , XtPointer ) ;
static void NUD_finalize_dset_CB( Widget , XtPointer , MCW_choose_cbs * ) ;
static void NUD_brick_av_CB( MCW_arrowval * , XtPointer ) ; /* Sub-brick menu */
static void NUD_undopush(void) ;
static void NUD_setcumlab(void) ;
static void NUD_rotate( MRI_IMAGE * im ) ;
static void NUD_update_base( Widget ) ;
/***********************************************************************
Set up the interface to the user
************************************************************************/
DEFINE_PLUGIN_PROTOTYPE
PLUGIN_interface * PLUGIN_init( int ncall )
{
if( ncall > 0 ) return NULL ; /* only one interface */
plint = PLUTO_new_interface( "Nudge Dataset" ,
"Move bricks around" ,
NULL ,
PLUGIN_CALL_IMMEDIATELY , NUD_main ) ;
PLUTO_add_hint( plint , "Move bricks around" ) ;
PLUTO_set_sequence( plint , "A:olddset:nudger" ) ;
return plint ;
}
/**************************************************************************
return a label string for 3 floats with 3 suffix characters
--------------------------------------------------------------------------*/
#define EPS 0.005
static char * NUD_threestring( float a,float b,float c,char ca,char cb,char cc )
{
static char label[64] ;
if( fabs(a) < EPS ) a = 0.0 ;
if( fabs(b) < EPS ) b = 0.0 ;
if( fabs(c) < EPS ) c = 0.0 ;
sprintf(label,"%6.2f%c %6.2f%c %6.2f%c ", a,ca,b,cb,c,cc ) ;
return label ;
}
static char * NUD_3string( float a,float b,float c,char ca,char cb,char cc )
{
static char label[64] ;
if( fabs(a) < EPS ) a = 0.0 ;
if( fabs(b) < EPS ) b = 0.0 ;
if( fabs(c) < EPS ) c = 0.0 ;
sprintf(label,"%.2f%c %.2f%c %.2f%c", a,ca,b,cb,c,cc ) ;
return label ;
}
/***************************************************************************
Will be called from AFNI when user selects from Plugins menu.
****************************************************************************/
/* Interface widgets */
static Widget shell=NULL , rowcol , info_lab , choose_pb ;
static Widget nudge_pb, clear_pb, undo_pb, redo_pb, help_pb, quit_pb, doall_pb, print_pb ;
static MCW_arrowval * roll_av , * pitch_av , * yaw_av ,
* dS_av , * dL_av , * dP_av , * brick_av ;
static Widget angle_cum_lab , shift_cum_lab ;
static MCW_arrowval * interp_av , * clip_av ;
/* other data */
static int nudger_open = 0 ;
static MCW_DC * dc ; /* display context */
static Three_D_View * im3d ; /* AFNI controller */
static THD_3dim_dataset * dset ; /* The dataset! */
static MCW_idcode dset_idc ;
static int new_dset = 0 ; /* Is it new? */
static int dset_ival = 0 ; /* Sub-brick index */
static char dset_title[THD_MAX_NAME] ; /* Title string */
static char * NUD_dummy_av_label[2] = { "[Nothing At All]", "[Nothing At All]" };
static int iha=1,ax1=0,ax2=1,ax3=2,hax1=1,hax2=2,hax3=3,adx=1,ady=2,adz=3 ;
static int undo_nall , undo_nuse , undo_ntop ;
static THD_dmat33 * undo_rmat=NULL ;
static THD_dfvec3 * undo_svec=NULL ;
static THD_dmat33 rmat ; /* current rotation matrix = undo_rmat[undo_nuse-1] */
static THD_dfvec3 svec ; /* current shift vector = undo_svec[undo_nuse-1] */
#define NRESAM 7
#define NYESNO 2
static char * REG_resam_strings[NRESAM] = {
"NN" , "Linear" , "Cubic" , "Quintic" , "Heptic" , "Fourier" , "Fourier_nopad" } ;
static char * REG_resam_options[NRESAM] = {
"-NN" , "-linear" , "-cubic" , "-quintic" , "-heptic" , "-Fourier" , "-Fourier_nopad" } ;
static int REG_resam_ints[NRESAM] = {
MRI_NN, MRI_LINEAR, MRI_CUBIC, MRI_QUINTIC, MRI_HEPTIC, MRI_FOURIER, MRI_FOURIER_NOPAD } ;
static char * YESNO_strings[NYESNO] = { "No" , "Yes" } ;
MRI_IMAGE * imbase = NULL ;
/*-------------------------------------------------------------------------------*/
char * NUD_main( PLUGIN_interface * plint )
{
XmString xstr ;
/*-- sanity checks --*/
if( ! IM3D_OPEN(plint->im3d) )
return " \n AFNI Controller\nnot opened?! \n " ;
if( nudger_open ){
if( plint->im3d != im3d ){ /* different controller => close it */
NUD_quit_CB(NULL,NULL,NULL) ;
} else { /* same controller => just raise up */
XtMapWidget(shell) ;
XRaiseWindow( XtDisplay(shell) , XtWindow(shell) ) ;
return NULL ;
}
}
im3d = plint->im3d ; /* save for local use */
/*-- create widgets, first time through --*/
if( shell == NULL ){
dc = im3d->dc ; /* save this too */
NUD_make_widgets() ;
PLUTO_set_topshell( plint , shell ) ; /* 22 Sep 2000 */
RWC_visibilize_widget( shell ) ; /* 27 Sep 2000 */
}
/*-- set titlebar --*/
{ char ttl[PLUGIN_STRING_SIZE] ;
sprintf(ttl , "AFNI Nudger %s" , AFNI_controller_label(im3d) ) ;
XtVaSetValues( shell , XmNtitle , ttl , NULL ) ;
}
/*-- set the info label --*/
xstr = XmStringCreateLtoR( "[No dataset]" ,
XmFONTLIST_DEFAULT_TAG ) ;
XtVaSetValues( info_lab , XmNlabelString , xstr , NULL ) ;
XmStringFree(xstr) ;
/*-- pop the widget up --*/
XtMapWidget(shell) ;
PLUTO_cursorize(shell) ;
/*-- misc initialization --*/
dset = NULL ; /* not editing anything */
ZERO_IDCODE(dset_idc) ;
dset_ival = 0 ; AV_assign_ival(brick_av,0) ;
if( imbase != NULL ){ mri_free(imbase); imbase = NULL; }
nudger_open = 1 ; /* editor is now open for business */
SENSITIZE(nudge_pb ,0) ;
SENSITIZE(undo_pb ,0) ;
SENSITIZE(redo_pb ,0) ;
SENSITIZE(doall_pb ,0) ;
SENSITIZE(choose_pb,1) ; AV_SENSITIZE(brick_av,0) ;
/* initialize nudgerosity */
NUD_clear_CB(NULL,NULL,NULL) ;
LOAD_DIAG_DMAT(rmat,1.0,1.0,1.0) ;
LOAD_DFVEC3(svec,0.0,0.0,0.0) ;
NUD_setcumlab() ;
/* initialize undo stack */
if( undo_rmat != NULL ){ free(undo_rmat); undo_rmat = NULL; }
if( undo_svec != NULL ){ free(undo_svec); undo_svec = NULL; }
undo_nuse = 0 ;
undo_ntop = 0 ;
undo_nall = 1 ;
undo_rmat = (THD_dmat33 *) malloc(sizeof(THD_dmat33)) ;
undo_svec = (THD_dfvec3 *) malloc(sizeof(THD_dfvec3)) ;
NUD_undopush() ; /* top of stack = current transformation */
return NULL ;
}
/*------------------------------------------------------------------------
Make the control popup for this thing
--------------------------------------------------------------------------*/
#define SEP_HOR(ww) XtVaCreateManagedWidget( \
"AFNI" , xmSeparatorWidgetClass , (ww) , \
XmNseparatorType , XmSINGLE_LINE , \
XmNinitialResourcesPersistent , False , \
NULL )
#define SEP_VER(ww) XtVaCreateManagedWidget( \
"AFNI" , xmSeparatorWidgetClass , (ww) , \
XmNseparatorType , XmDOUBLE_LINE , \
XmNorientation , XmVERTICAL , \
XmNinitialResourcesPersistent , False , \
NULL )
/*-- structures defining action buttons (at bottom of popup) --*/
#define NACT 8 /* number of action buttons */
static MCW_action_item NUD_actor[NACT] = {
{"Nudge",NUD_nudge_CB,NULL,
"Applies Angles and Shifts\nto chosen Brick of dataset","Apply Angles/Shifts",1} ,
{"Clear",NUD_clear_CB,NULL,
"Clears Angles and\nShifts entry fields","Clear Angles/Shifts",0} ,
{"Undo",NUD_undo_CB,NULL,
"Undoes previous nudge, if possible","Undo last nudge",0} ,
{"Redo",NUD_redo_CB,NULL,
"Redoes previously undone nudge","Redo last undone nudge",0} ,
{"Help",NUD_help_CB,NULL,
"Displays more help" , "Displays more help",0} ,
{"Quit",NUD_quit_CB,NULL,
"Discard nudges since last\n'Do All' and close down",
"Discard nudges and close",0} ,
{"Do All",NUD_doall_CB,NULL,
"Apply Angles and Shifts to all sub-\nbricks and save dataset to disk" ,
"Apply Angles/Shifts; write to disk" , 1 } ,
{"Print",NUD_print_CB,NULL,
"Print current Angles and Shifts as\na '3drotate' command, to stderr" ,
"Print 3drotate command to screen" , 0 }
} ;
/*------------------------------------------------------------------------*/
static void NUD_make_widgets(void)
{
XmString xstr ;
Widget hrc ;
/*** top level shell for window manager ***/
shell =
XtVaAppCreateShell(
"AFNI" , "AFNI" , topLevelShellWidgetClass , dc->display ,
XmNtitle , "AFNI Nudger" , /* top of window */
XmNiconName , "Nudger" , /* label on icon */
XmNdeleteResponse , XmDO_NOTHING , /* deletion handled below */
XmNallowShellResize , True , /* let code resize shell? */
XmNmappedWhenManaged , False , /* must map it manually */
XmNinitialResourcesPersistent , False ,
NULL ) ;
DC_yokify( shell , dc ) ; /* 14 Sep 1998 */
#ifndef DONT_INSTALL_ICONS
if( afni48_good ) /* set icon pixmap */
XtVaSetValues( shell ,
XmNiconPixmap , afni48_pixmap ,
NULL ) ;
#endif
if( MCW_isitmwm(shell) ) /* remove some MWM functions */
XtVaSetValues( shell ,
XmNmwmFunctions ,
MWM_FUNC_MOVE | MWM_FUNC_CLOSE | MWM_FUNC_MINIMIZE ,
NULL ) ;
XmAddWMProtocolCallback( /* make "Close" window menu work */
shell ,
XmInternAtom( dc->display , "WM_DELETE_WINDOW" , False ) ,
NUD_quit_CB , (XtPointer) plint ) ;
/*** rowcolumn widget to hold all user interface stuff ***/
rowcol = XtVaCreateWidget(
"AFNI" , xmRowColumnWidgetClass , shell ,
XmNpacking , XmPACK_TIGHT ,
XmNorientation , XmVERTICAL ,
XmNtraversalOn , True ,
XmNinitialResourcesPersistent , False ,
NULL ) ;
/*** label at top to let user know who we are ***/
xstr = XmStringCreateLtoR( "[No dataset]" ,
XmFONTLIST_DEFAULT_TAG ) ;
info_lab = XtVaCreateManagedWidget(
"AFNI" , xmLabelWidgetClass , rowcol ,
XmNlabelString , xstr ,
XmNinitialResourcesPersistent , False ,
NULL ) ;
XmStringFree(xstr) ;
MCW_register_help( info_lab , "Shows dataset being nudged" ) ;
MCW_register_hint( info_lab , "Shows dataset being nudged" ) ;
/***** top row of widgets to choose dataset and sub-brick *****/
SEP_HOR(rowcol) ;
hrc = XtVaCreateWidget(
"AFNI" , xmRowColumnWidgetClass , rowcol ,
XmNorientation , XmHORIZONTAL ,
XmNpacking , XmPACK_TIGHT ,
XmNadjustLast , False ,
XmNadjustMargin , False ,
XmNtraversalOn , True ,
XmNmarginWidth , 0 ,
XmNmarginHeight , 0 ,
XmNinitialResourcesPersistent , False ,
NULL ) ;
/*** button to let user choose dataset to edit ***/
xstr = XmStringCreateLtoR( "Choose Dataset" , XmFONTLIST_DEFAULT_TAG ) ;
choose_pb = XtVaCreateManagedWidget(
"AFNI" , xmPushButtonWidgetClass , hrc ,
XmNlabelString , xstr ,
XmNtraversalOn , True ,
XmNinitialResourcesPersistent , False ,
NULL ) ;
XmStringFree(xstr) ;
XtAddCallback( choose_pb, XmNactivateCallback, NUD_choose_CB, NULL ) ;
MCW_register_help( choose_pb ,
"Use this to popup a\n"
"'chooser' that lets\n"
"you select which\n"
"dataset to nudge."
) ;
MCW_register_hint( choose_pb , "Popup dataset chooser" ) ;
/*** menu to let user choose sub-brick to deal with ***/
SEP_VER(hrc) ;
brick_av = new_MCW_arrowval(
hrc , /* parent Widget */
"Brick" , /* label */
MCW_AV_optmenu , /* option menu style */
0 , /* first option */
1 , /* last option */
0 , /* initial selection */
MCW_AV_readtext , /* ignored but needed */
0 , /* decimal shift */
NUD_brick_av_CB , /* callback when changed */
NULL , /* data for above */
MCW_av_substring_CB , /* text creation routine */
NUD_dummy_av_label /* data for above */
) ;
MCW_reghelp_children( brick_av->wrowcol ,
"Choose the sub-brick\n"
"to nudge interactively;\n"
"you should also be\n"
"viewing this sub-brick" ) ;
MCW_reghint_children( brick_av->wrowcol , "Sub-brick to nudge" ) ;
/** some miscellaneous controls **/
SEP_VER(hrc) ;
interp_av = new_MCW_arrowval(
hrc , /* parent Widget */
"Resampling" , /* label */
MCW_AV_optmenu , /* option menu style */
0 , /* first option */
NRESAM-1 , /* last option */
3 , /* initial selection */
MCW_AV_readtext , /* ignored but needed */
0 , /* decimal shift */
NULL , /* callback when changed */
NULL , /* data for above */
MCW_av_substring_CB , /* text creation routine */
REG_resam_strings /* data for above */
) ;
MCW_reghint_children( interp_av->wrowcol , "Set interpolation method" ) ;
SEP_VER(hrc) ;
clip_av = new_MCW_arrowval(
hrc , /* parent Widget */
"Clip" , /* label */
MCW_AV_optmenu , /* option menu style */
0 , /* first option */
NYESNO-1 , /* last option */
1 , /* initial selection */
MCW_AV_readtext , /* ignored but needed */
0 , /* decimal shift */
NULL , /* callback when changed */
NULL , /* data for above */
MCW_av_substring_CB , /* text creation routine */
YESNO_strings /* data for above */
) ;
MCW_reghint_children( clip_av->wrowcol , "Clip after interpolation?" ) ;
XtManageChild(hrc) ;
/********** Angle choosers ***********/
SEP_HOR(rowcol) ;
hrc = XtVaCreateWidget(
"AFNI" , xmRowColumnWidgetClass , rowcol ,
XmNorientation , XmHORIZONTAL ,
XmNpacking , XmPACK_TIGHT ,
XmNadjustLast , False ,
XmNadjustMargin , False ,
XmNtraversalOn , True ,
XmNmarginWidth , 0 ,
XmNmarginHeight , 0 ,
XmNinitialResourcesPersistent , False ,
NULL ) ;
xstr = XmStringCreateLtoR( "Angles: " ,
XmFONTLIST_DEFAULT_TAG ) ;
(void) XtVaCreateManagedWidget(
"AFNI" , xmLabelWidgetClass , hrc ,
XmNlabelString , xstr ,
XmNinitialResourcesPersistent , False ,
NULL ) ;
XmStringFree(xstr) ;
/** the actual angles **/
roll_av = new_MCW_arrowval( hrc, "I" ,
MCW_AV_downup , -300,300,0 ,
MCW_AV_editext , 1 ,
NULL , NULL , NULL,NULL ) ;
MCW_reghint_children( roll_av->wrowcol , "Roll angle [I-axis]" ) ;
XtVaSetValues( roll_av->wtext , XmNcolumns , 6 , NULL ) ;
SEP_VER(hrc) ;
pitch_av = new_MCW_arrowval( hrc, "R" ,
MCW_AV_downup , -300,300,0 ,
MCW_AV_editext , 1 ,
NULL , NULL , NULL,NULL ) ;
MCW_reghint_children( pitch_av->wrowcol , "Pitch angle [R-axis]" ) ;
XtVaSetValues( pitch_av->wtext , XmNcolumns , 6 , NULL ) ;
SEP_VER(hrc) ;
yaw_av = new_MCW_arrowval( hrc, "A" ,
MCW_AV_downup , -300,300,0 ,
MCW_AV_editext , 1 ,
NULL , NULL , NULL,NULL ) ;
MCW_reghint_children( yaw_av->wrowcol , "Yaw angle [A-axis]" ) ;
XtVaSetValues( yaw_av->wtext , XmNcolumns , 6 , NULL ) ;
SEP_VER(hrc) ;
/** cumulative label **/
xstr = XmStringCreateLtoR( "--" , XmFONTLIST_DEFAULT_TAG ) ;
angle_cum_lab = XtVaCreateManagedWidget(
"AFNI" , xmLabelWidgetClass , hrc ,
XmNlabelString , xstr ,
XmNalignment , XmALIGNMENT_CENTER ,
XmNinitialResourcesPersistent , False ,
XmNrecomputeSize , True ,
#if 0
XmNmarginHeight , 0 ,
XmNmarginBottom , 0 ,
XmNmarginLeft , 0 ,
XmNmarginRight , 0 ,
XmNmarginTop , 0 ,
XmNmarginWidth , 0 ,
#endif
XmNtraversalOn , True ,
NULL ) ;
XmStringFree(xstr) ;
MCW_register_hint( angle_cum_lab , "Cumulative [degrees]" ) ;
XtManageChild(hrc) ;
/********** Shift choosers ***********/
/*** SEP_HOR(rowcol) ; ***/
hrc = XtVaCreateWidget(
"AFNI" , xmRowColumnWidgetClass , rowcol ,
XmNorientation , XmHORIZONTAL ,
XmNpacking , XmPACK_TIGHT ,
XmNadjustLast , False ,
XmNadjustMargin , False ,
XmNtraversalOn , True ,
XmNmarginWidth , 0 ,
XmNmarginHeight , 0 ,
XmNinitialResourcesPersistent , False ,
NULL ) ;
xstr = XmStringCreateLtoR( "Shifts: " ,
XmFONTLIST_DEFAULT_TAG ) ;
(void) XtVaCreateManagedWidget(
"AFNI" , xmLabelWidgetClass , hrc ,
XmNlabelString , xstr ,
XmNinitialResourcesPersistent , False ,
NULL ) ;
XmStringFree(xstr) ;
/** the actual shifts **/
dS_av = new_MCW_arrowval( hrc, "S" ,
MCW_AV_downup , -999,999,0 ,
MCW_AV_editext , 1 ,
NULL , NULL , NULL,NULL ) ;
MCW_reghint_children( dS_av->wrowcol , "Delta Superior" ) ;
XtVaSetValues( dS_av->wtext , XmNcolumns , 6 , NULL ) ;
SEP_VER(hrc) ;
dL_av = new_MCW_arrowval( hrc, "L" ,
MCW_AV_downup , -999,999,0 ,
MCW_AV_editext , 1 ,
NULL , NULL , NULL,NULL ) ;
MCW_reghint_children( dL_av->wrowcol , "Delta Left" ) ;
XtVaSetValues( dL_av->wtext , XmNcolumns , 6 , NULL ) ;
SEP_VER(hrc) ;
dP_av = new_MCW_arrowval( hrc, "P" ,
MCW_AV_downup , -999,999,0 ,
MCW_AV_editext , 1 ,
NULL , NULL , NULL,NULL ) ;
MCW_reghint_children( dP_av->wrowcol , "Delta Posterior" ) ;
XtVaSetValues( dP_av->wtext , XmNcolumns , 6 , NULL ) ;
SEP_VER(hrc) ;
/** cumulative label **/
xstr = XmStringCreateLtoR( "--" , XmFONTLIST_DEFAULT_TAG ) ;
shift_cum_lab = XtVaCreateManagedWidget(
"AFNI" , xmLabelWidgetClass , hrc ,
XmNalignment , XmALIGNMENT_BEGINNING ,
XmNlabelString , xstr ,
XmNinitialResourcesPersistent , False ,
XmNrecomputeSize , True ,
#if 0
XmNmarginHeight , 0 ,
XmNmarginHeight , 0 ,
XmNmarginBottom , 0 ,
XmNmarginLeft , 0 ,
XmNmarginRight , 0 ,
XmNmarginTop , 0 ,
XmNmarginWidth , 0 ,
#endif
XmNtraversalOn , True ,
NULL ) ;
XmStringFree(xstr) ;
MCW_register_hint( shift_cum_lab , "Cumulative [mm]" ) ;
XtManageChild(hrc) ;
/*** a set of action buttons below the line ***/
SEP_HOR(rowcol) ;
(void) MCW_action_area( rowcol , NUD_actor , NACT ) ;
nudge_pb = (Widget) NUD_actor[0].data ;
clear_pb = (Widget) NUD_actor[1].data ;
undo_pb = (Widget) NUD_actor[2].data ;
redo_pb = (Widget) NUD_actor[3].data ;
help_pb = (Widget) NUD_actor[4].data ;
quit_pb = (Widget) NUD_actor[5].data ;
doall_pb = (Widget) NUD_actor[6].data ;
print_pb = (Widget) NUD_actor[7].data ;
/*** that's all ***/
XtManageChild(rowcol) ;
XtRealizeWidget(shell) ; /* will not be mapped */
return ;
}
/*--------------------------------------------------------------------------
Compute a rotation matrix specified by 3 angles:
Q = R3 R2 R1, where Ri is rotation about axis axi by angle thi.
In these routines, the axis codes (ax1,ax2,ax3, hax1,hax2,hax3, and
adx,ady,adz) are globals, computed when the dataset is loaded.
----------------------------------------------------------------------------*/
static THD_dmat33 rotmatrix( double th1 , double th2 , double th3 )
{
THD_dmat33 q , p ;
if( hax1 < 0 ) th1 = -th1 ;
if( hax2 < 0 ) th2 = -th2 ;
if( hax3 < 0 ) th3 = -th3 ;
LOAD_ROT_DMAT( q , th1 , ax1 ) ;
LOAD_ROT_DMAT( p , th2 , ax2 ) ; q = DMAT_MUL( p , q ) ;
LOAD_ROT_DMAT( p , th3 , ax3 ) ; q = DMAT_MUL( p , q ) ;
return q ;
}
/*-----------------------------------------------------------------------
Compute the rotation angles from the matrix
[the signs of these may need to be munged]
-------------------------------------------------------------------------*/
static void rotangles( THD_dmat33 rm, double *th1, double *th2, double *th3 )
{
*th2 = asin( rm.mat[ax3][ax1] ) ;
*th1 = atan2( -rm.mat[ax3][ax2] , rm.mat[ax3][ax3] ) ;
*th3 = atan2( -rm.mat[ax2][ax1] , rm.mat[ax1][ax1] ) ;
if( hax1 < 0 ) *th1 = -(*th1) ;
if( hax2 < 0 ) *th2 = -(*th2) ;
if( hax3 < 0 ) *th3 = -(*th3) ;
return ;
}
/*-----------------------------------------------------------------------
Compute the shift vector in dataset coordinates from the user inputs.
-------------------------------------------------------------------------*/
static THD_dfvec3 shiftvec( double dx , double dy , double dz )
{
double qdx=0.0,qdy=0.0,qdz=0.0 ;
THD_dfvec3 qv ;
switch( adx ){
case 1: qdx = -dx ; break ;
case -1: qdx = dx ; break ;
case 2: qdy = -dx ; break ;
case -2: qdy = dx ; break ;
case 3: qdz = -dx ; break ;
case -3: qdz = dx ; break ;
}
switch( ady ){
case 1: qdx = -dy ; break ;
case -1: qdx = dy ; break ;
case 2: qdy = -dy ; break ;
case -2: qdy = dy ; break ;
case 3: qdz = -dy ; break ;
case -3: qdz = dy ; break ;
}
switch( adz ){
case 1: qdx = -dz ; break ;
case -1: qdx = dz ; break ;
case 2: qdy = -dz ; break ;
case -2: qdy = dz ; break ;
case 3: qdz = -dz ; break ;
case -3: qdz = dz ; break ;
}
LOAD_DFVEC3(qv,qdx,qdy,qdz) ; return qv ;
}
/*-----------------------------------------------------------------------
Compute the user-coordinate shifts from the dataset-coordinate vector
-------------------------------------------------------------------------*/
static void shiftdeltas( THD_dfvec3 sv, double *d1, double *d2, double *d3 )
{
double qdx,qdy,qdz , dx=0.0,dy=0.0,dz=0.0 ;
UNLOAD_DFVEC3( sv , qdx,qdy,qdz ) ;
switch( adx ){
case 1: dx = -qdx ; break ;
case -1: dx = qdx ; break ;
case 2: dx = -qdy ; break ;
case -2: dx = qdy ; break ;
case 3: dx = -qdz ; break ;
case -3: dx = qdz ; break ;
}
switch( ady ){
case 1: dy = -qdx ; break ;
case -1: dy = qdx ; break ;
case 2: dy = -qdy ; break ;
case -2: dy = qdy ; break ;
case 3: dy = -qdz ; break ;
case -3: dy = qdz ; break ;
}
switch( adz ){
case 1: dz = -qdx ; break ;
case -1: dz = qdx ; break ;
case 2: dz = -qdy ; break ;
case -2: dz = qdy ; break ;
case 3: dz = -qdz ; break ;
case -3: dz = qdz ; break ;
}
*d1 = dx ; *d2 = dy ; *d3 = dz ; return ;
}
/*-----------------------------------------------------------------------
Set the cumulative angles/shifts labels from the current state
(stored in globals rmat and svec)
-------------------------------------------------------------------------*/
static void NUD_setcumlab(void)
{
double th1,th2,th3 ;
XmString xstr ;
rotangles( rmat, &th1,&th2,&th3 ) ;
th1 *= iha*(180.0/PI) ; th2 *= iha*(180.0/PI) ; th3 *= iha*(180.0/PI) ;
xstr = XmStringCreateLtoR( NUD_threestring(th1,th2,th3,'I','R','A') ,
XmFONTLIST_DEFAULT_TAG ) ;
XtVaSetValues( angle_cum_lab , XmNlabelString , xstr , NULL ) ;
XmStringFree(xstr) ;
shiftdeltas( svec , &th1,&th2,&th3 ) ;
xstr = XmStringCreateLtoR( NUD_threestring(th1,th2,th3,'S','L','P') ,
XmFONTLIST_DEFAULT_TAG ) ;
XtVaSetValues( shift_cum_lab , XmNlabelString , xstr , NULL ) ;
XmStringFree(xstr) ;
return ;
}
/*-----------------------------------------------------------------------
Write a 3drotate partial command to the screen corresponding
to the current nudge -- 31 Aug 2000
-------------------------------------------------------------------------*/
static void NUD_print_CB( Widget w , XtPointer cd , XtPointer cb )
{
double th1,th2,th3 ;
char cbuf[256] = "3drotate" ;
strcat( cbuf , " " ) ;
strcat( cbuf , REG_resam_options[interp_av->ival] ) ;
if( clip_av->ival ) strcat( cbuf , " -clipit" ) ;
rotangles( rmat, &th1,&th2,&th3 ) ;
th1 *= iha*(180.0/PI) ; th2 *= iha*(180.0/PI) ; th3 *= iha*(180.0/PI) ;
strcat( cbuf , " -rotate " ) ;
strcat( cbuf , NUD_3string(th1,th2,th3,'I','R','A') ) ;
shiftdeltas( svec , &th1,&th2,&th3 ) ;
strcat( cbuf , " -ashift " ) ;
strcat( cbuf , NUD_3string(th1,th2,th3,'S','L','P') ) ;
strcat( cbuf , " -prefix ??? inputdataset" ) ;
fprintf(stderr,"\nCurrent Nudge command is:\n%s\n",cbuf ) ;
return ;
}
/*-----------------------------------------------------------------------
Push the current state (rmat and svec) onto the undo stack
-------------------------------------------------------------------------*/
static void NUD_undopush(void)
{
if( undo_nuse >= undo_nall ){
undo_nall = undo_nuse + 4 ;
undo_rmat = (THD_dmat33 *)realloc( undo_rmat, sizeof(THD_dmat33)*undo_nall );
undo_svec = (THD_dfvec3 *)realloc( undo_svec, sizeof(THD_dfvec3)*undo_nall );
}
undo_rmat[undo_nuse] = rmat ;
undo_svec[undo_nuse] = svec ;
undo_nuse++ ; undo_ntop = undo_nuse ; SENSITIZE(redo_pb,0) ;
return ;
}
/*-------------------------------------------------------------------
Callback for Nudge button
---------------------------------------------------------------------*/
static void NUD_nudge_CB( Widget w, XtPointer client_data, XtPointer call_data )
{
float roll,pitch,yaw , dS,dL,dP ;
THD_dmat33 new_rmat ;
THD_dfvec3 new_svec , qv ;
if( dset == NULL ){ XBell(dc->display,100); return; } /* shouldn't happen */
roll = (PI/180.0)*roll_av->fval ;
pitch = (PI/180.0)*pitch_av->fval ;
yaw = (PI/180.0)*yaw_av->fval ;
dS = dS_av->fval ;
dL = dL_av->fval ;
dP = dP_av->fval ;
if( roll==0.0 && pitch==0.0 && yaw==0.0 && dS==0.0 && dL==0.0 && dP==0.0 ){
(void) MCW_popup_message( nudge_pb ,
" \n"
"** Can't nudge dataset! **\n"
"** All Angle and Shifts **\n"
"** are zero. **\n" ,
MCW_USER_KILL | MCW_TIMER_KILL ) ;
XBell( dc->display , 100 ) ;
return ;
}
SENSITIZE(undo_pb,1) ; SENSITIZE(doall_pb,1) ;
SENSITIZE(choose_pb,0) ; AV_SENSITIZE(brick_av,0) ;
new_rmat = rotmatrix( roll, pitch, yaw ) ;
rmat = DMAT_MUL( new_rmat , rmat ) ;
new_svec = DMATVEC( new_rmat , svec ) ;
qv = shiftvec( dS , dL , dP ) ;
svec = ADD_DFVEC3( new_svec , qv ) ;
NUD_undopush() ; /* push new transformatin onto undo list */
NUD_setcumlab() ; /* draw the cumulative labels */
/* actually do something here */
if( imbase == NULL ) /* first time: get base */
imbase = mri_copy( DSET_BRICK(dset,dset_ival) ) ;
NUD_update_base( nudge_pb ) ; return ;
}
/*-------------------------------------------------------------------
Callback for Clear button
---------------------------------------------------------------------*/
static void NUD_clear_CB( Widget w, XtPointer client_data, XtPointer call_data )
{
AV_assign_fval( roll_av , 0.0 ) ;
AV_assign_fval( pitch_av , 0.0 ) ;
AV_assign_fval( yaw_av , 0.0 ) ;
AV_assign_fval( dS_av , 0.0 ) ;
AV_assign_fval( dL_av , 0.0 ) ;
AV_assign_fval( dP_av , 0.0 ) ;
return ;
}
/*-------------------------------------------------------------------
Callback for Undo button
---------------------------------------------------------------------*/
static void NUD_undo_CB( Widget w, XtPointer client_data, XtPointer call_data )
{
if( undo_nuse <= 1 ){ XBell(dc->display,100); return; }
undo_nuse-- ;
rmat = undo_rmat[undo_nuse-1] ;
svec = undo_svec[undo_nuse-1] ;
NUD_setcumlab() ;
if( undo_nuse <= 1 ){ SENSITIZE(undo_pb,0); SENSITIZE(doall_pb,0); }
SENSITIZE(redo_pb,1) ;
/* actually do something here */
NUD_update_base( undo_pb ) ; return ;
}
/*-------------------------------------------------------------------
Callback for Redo button
---------------------------------------------------------------------*/
static void NUD_redo_CB( Widget w, XtPointer client_data, XtPointer call_data )
{
if( undo_ntop <= undo_nuse ){ XBell(dc->display,100); return; }
rmat = undo_rmat[undo_nuse] ;
svec = undo_svec[undo_nuse] ;
undo_nuse++ ;
NUD_setcumlab() ;
if( undo_nuse >= undo_ntop ) SENSITIZE(redo_pb,0) ;
SENSITIZE(undo_pb,1) ; SENSITIZE(doall_pb,1) ;
/* actually do something here */
NUD_update_base( redo_pb ) ; return ;
}
/*-------------------------------------------------------------------
Callback for Quit button
---------------------------------------------------------------------*/
static void NUD_quit_CB( Widget w, XtPointer client_data, XtPointer call_data )
{
if( dset != NULL ){
DSET_unlock(dset) ; DSET_unload(dset) ; DSET_anyize(dset) ;
if( undo_nuse > 1 ){ /* if not at the null nudge */
MCW_invert_widget(quit_pb) ;
THD_load_statistics( dset ) ;
PLUTO_dset_redisplay( dset ) ;
AFNI_process_drawnotice( im3d ) ;
MCW_invert_widget(quit_pb) ;
}
dset = NULL ;
}
if( imbase != NULL ){ mri_free(imbase); imbase = NULL; }
if( undo_rmat != NULL ){ free(undo_rmat); undo_rmat = NULL; }
if( undo_svec != NULL ){ free(undo_svec); undo_svec = NULL; }
undo_nall = undo_nuse = 0 ;
XtUnmapWidget( shell ) ; nudger_open = 0 ;
return ;
}
/*-------------------------------------------------------------------
Callback for help button
---------------------------------------------------------------------*/
static void NUD_help_CB( Widget w, XtPointer client_data, XtPointer call_data )
{
(void ) new_MCW_textwin( help_pb ,
"PURPOSE: Nudge a dataset's position a little.\n"
"\n"
"CONTROLS:\n"
"Choose Dataset: button to choose which dataset to move around.\n"
"Brick: which single sub-brick of the dataset will be moved,\n"
" prior to use of 'Do All'.\n"
"Resampling: choose interpolation method for brick resampling\n"
"Clip: clip each brick after interpolation?\n"
"---------------------------------------------------------------------\n"
"Angles: the entry fields are the rotational angles to be applied:\n"
" positive I = roll = looking to the left\n"
" positive R = pitch = nodding the head forward\n"
" positive A = yaw = tilting left ear towards shoulder\n"
"Shifts: the entry fields are the translational shifts to be applied.\n"
" positive S = superior = shifting head upwards\n"
" positive L = left = shifting head leftwards\n"
" positive P = posterior = shifting head backwards\n"
"---------------------------------------------------------------------\n"
"Nudge: apply the Angles and Shifts entered above to the chosen Brick;\n"
" also updates the cumulative angles/shifts\n"
"Clear: set the Angles and Shifts to zero\n"
"Undo: undo the previous Nudge\n"
"Redo: redo the previously undone Nudge\n"
"Quit: exit, restoring the dataset to its values stored on disk\n"
"Do All: apply cumulative angles/shifts to all sub-bricks; write to disk\n"
"Print: print (to stderr) 3drotate command equivalent to current nudge\n"
" [use 'Print' before 'Do All', since 'Do All' sets nudge to 0]\n"
"=======================================================================\n"
"USAGE SUGGESTIONS:\n"
"* Load the dataset and brick to nudge into this plugin.\n"
"* Switch the image viewers to the same dataset and brick;\n"
" as the brick is nudged, then images will be redrawn.\n"
"* You can also use the rendering plugin - if DynaDraw is on,\n"
" the brick will be re-rendered with each nudge.\n"
"* If you are comparing the nudged dataset to a reference, and\n"
" trying to realign the two, one way is to temporarily make\n"
" one of them a fim dataset (using '3drefit -fim'), and then\n"
" display it as a color overlay. When you are happy with\n"
" the alignment, you can quit AFNI and use 3drefit to change\n"
" the dataset back to whatever it was before.\n"
"* When using the '-fim' trick on the nudged dataset, you will\n"
" have to set the colors and color scaling range appropriately\n"
" on the 'Define Function' control panel, otherwise the color\n"
" overlay will look so peculiar as to be useless.\n"
"* Nudge-ing on the single sub-brick is done only in memory, so if\n"
" you Quit, the dataset on disk will be unchanged. When you use\n"
" 'Do All', all sub-bricks will be nudged the same way and then\n"
" be written out to disk, overwriting the original dataset .BRIK.\n"
"* Instead of using 'Do All', you can use 'Print' to see the 3drotate\n"
" parameters to use. You can then apply these to as many datasets\n"
" you want (e.g., in a shell script, to nudge a whole bunch of\n"
" datasets exactly the same way).\n"
"* I suggest you do NOT nudge functional activation maps. It is better\n"
" to nudge the anatomical underlay, or nudge the original EPI time\n"
" series. Nudging a dataset implies interpolating to a new grid,\n"
" and this is problematical for the non-smooth activation maps.\n"
"=======================================================================\n"
"WARNINGS:\n"
"* Values past the edge of the dataset are 0, and if they are shifted\n"
" into the volume covered by the dataset, you will get 0's there.\n"
"* Values shifted past the edge of the volume covered by the dataset\n"
" will be LOST. This may seem obvious, but when you are shifting\n"
" a functional dataset that is smaller than the anatomical underlay,\n"
" it can look mysterious.\n"
"* One solution to the problem above is to use 3dZeropad to explicitly\n"
" put a layer of 0's around the outside of the functional dataset\n"
" volume. Shifted values will then go into this 0 buffer, and\n"
" will not be lost.\n"
"=======================================================================\n"
"ALGORITHM:\n"
"* Uses the same basic routines as program 3drotate; see\n"
" RW Cox and A Jesmanowicz.\n"
" Real-time 3D image registration for functional MRI.\n"
" Magnetic Resonance in Medicine, 42: 1014-1018, 1999.\n"
" Also see 3drotate.c, 3dvolreg.c, and thd_rot3d.c.\n"
"* Bricks are not repeatedly interpolated as you nudge - each nudge\n"
" takes place using the cumulative angles/shifts starting from the\n"
" brick read in from disk. However, if you re-nudge a dataset\n"
" after using 'Do All' to write to disk, you will then be re-\n"
" interpolating an already interpolated dataset.\n"
"* Note that cumulative angles/shifts may not be exactly the sum of\n"
" the incremental nudges. This effect is due to the non-Abelian\n"
" nature of 3D rotation (i.e., doing rotation A then B is not the\n"
" same as doing rotation B then A).\n"
"* The angle and shift parameters are specified in the same order\n"
" as output by 3dvolreg, and would be input to 3drotate as\n"
" -rotate <roll>I <pitch>R <yaw>A -ashift <dS>S <dL>L <dP>P\n"
"* Rotations are about the center of the rectangular volume of the\n"
" dataset. This is not likely to be the center of the brain.\n"
"=======================================================================\n"
"AUTHOR: RWCox, April 2000\n"
"=======================================================================\n"
, TEXT_READONLY ) ;
return ;
}
/*-------------------------------------------------------------------
Callback for choose button - give the user some dataset choices.
Criteria for datasets that can be nudged:
- must be in current session
- must have actual bricks
---------------------------------------------------------------------*/
static int ndsl = 0 ;
static PLUGIN_dataset_link * dsl = NULL ;
static void NUD_choose_CB( Widget w, XtPointer client_data, XtPointer call_data )
{
THD_session * ss = im3d->ss_now ; /* current session */
int vv = im3d->vinfo->view_type ; /* view type */
THD_3dim_dataset * qset ;
int id , ltop , llen ;
char qnam[THD_MAX_NAME] , label[THD_MAX_NAME] ;
static char ** strlist = NULL ;
/* can't do this if a dataset is already active and changed */
if( dset != NULL && undo_nuse > 1 ){
(void) MCW_popup_message( choose_pb ,
"Can't change datasets until\n"
"you save the changes you've\n"
"already made. Or you could\n"
"'Quit' and re-start the Editor" ,
MCW_USER_KILL | MCW_TIMER_KILL ) ;
XBell( dc->display , 100 ) ;
return ;
}
/* initialize */
ndsl = 0 ;
/* scan datasets */
for( id=0 ; id < ss->num_dsset ; id++ ){
qset = ss->dsset[id][vv] ;
if( ! ISVALID_DSET (qset) ) continue ; /* skip */
if( ! DSET_INMEMORY(qset) ) continue ;
if( DSET_BRICK_TYPE(qset,0) == MRI_complex ) continue ;
ndsl++ ;
dsl = (PLUGIN_dataset_link *)
XtRealloc( (char *) dsl , sizeof(PLUGIN_dataset_link)*ndsl ) ;
make_PLUGIN_dataset_link( qset , dsl + (ndsl-1) ) ;
}
/* found nothing? exit */
if( ndsl < 1 ){
(void) MCW_popup_message( choose_pb ,
" \nDidn't find any datasets to edit!\n" ,
MCW_USER_KILL | MCW_TIMER_KILL ) ;
XBell( dc->display , 100 ) ;
return ;
}
/*--- 23 Nov 1996: loop over dataset links and patch their titles
to include an indicator of the dataset type ---*/
ltop = 4 ;
for( id=0 ; id < ndsl ; id++ ){
llen = strlen(dsl[id].title) ;
ltop = MAX(ltop,llen) ;
}
for( id=0 ; id < ndsl ; id++ ){
qset = PLUTO_find_dset( &(dsl[id].idcode) ) ;
if( ! ISVALID_DSET(qset) ) continue ;
if( ISANAT(qset) ){
if( ISANATBUCKET(qset) ) /* 30 Nov 1997 */
sprintf(qnam,"%-*s [%s:%d]" ,
ltop,dsl[id].title ,
ANAT_prefixstr[qset->func_type] , DSET_NVALS(qset) ) ;
else if( DSET_NUM_TIMES(qset) == 1 )
sprintf(qnam,"%-*s [%s]" ,
ltop,dsl[id].title ,
ANAT_prefixstr[qset->func_type] ) ;
else
sprintf(qnam,"%-*s [%s:3D+t:%d]" ,
ltop,dsl[id].title ,
ANAT_prefixstr[qset->func_type] , DSET_NUM_TIMES(qset) ) ;
} else {
if( ISFUNCBUCKET(qset) ) /* 30 Nov 1997 */
sprintf(qnam,"%-*s [%s:%d]" ,
ltop,dsl[id].title ,
FUNC_prefixstr[qset->func_type] , DSET_NVALS(qset) ) ;
else if( DSET_NUM_TIMES(qset) == 1 )
sprintf(qnam,"%-*s [%s]" ,
ltop,dsl[id].title ,
FUNC_prefixstr[qset->func_type] ) ;
else
sprintf(qnam,"%-*s [%s:3D+t:%d]" ,
ltop,dsl[id].title ,
FUNC_prefixstr[qset->func_type] , DSET_NVALS(qset) ) ;
}
if( DSET_COMPRESSED(qset) ) strcat(qnam,"z") ;
strcpy( dsl[id].title , qnam ) ;
}
/*--- make a popup chooser for the user to browse ---*/
POPDOWN_strlist_chooser ;
strlist = (char **) XtRealloc( (char *)strlist , sizeof(char *)*ndsl ) ;
for( id=0 ; id < ndsl ; id++ ) strlist[id] = dsl[id].title ;
sprintf( label , "AFNI Dataset from\nthe %s" , VIEW_typestr[vv] ) ;
MCW_choose_strlist( w , label , ndsl , -1 , strlist ,
NUD_finalize_dset_CB , NULL ) ;
return ;
}
/*---------------------------------------------------------------------------
Make a label for a sub-brick selector menu
-----------------------------------------------------------------------------*/
static char * NUD_brick_av_label_CB( MCW_arrowval * av , XtPointer cd )
{
static char blab[32] ;
THD_3dim_dataset * dset = (THD_3dim_dataset *) cd ;
static char *lfmt[3] = { "#%1d %-14.14s", "#%2d %-14.14s", "#%3d %-14.14s" };
static char *rfmt[3] = { "%-14.14s #%1d", "%-14.14s #%2d", "%-14.14s #%3d" };
if( ISVALID_3DIM_DATASET(dset) ){
#ifdef USE_RIGHT_BUCK_LABELS
if( DSET_NVALS(dset) < 10 )
sprintf(blab, rfmt[0] , DSET_BRICK_LABEL(dset,av->ival) , av->ival ) ;
else if( DSET_NVALS(dset) < 100 )
sprintf(blab, rfmt[1] , DSET_BRICK_LABEL(dset,av->ival) , av->ival ) ;
else
sprintf(blab, rfmt[2] , DSET_BRICK_LABEL(dset,av->ival) , av->ival ) ;
#else
if( DSET_NVALS(dset) < 10 )
sprintf(blab, lfmt[0] , av->ival , DSET_BRICK_LABEL(dset,av->ival) ) ;
else if( DSET_NVALS(dset) < 100 )
sprintf(blab, lfmt[1] , av->ival , DSET_BRICK_LABEL(dset,av->ival) ) ;
else
sprintf(blab, lfmt[2] , av->ival , DSET_BRICK_LABEL(dset,av->ival) ) ;
#endif
}
else
sprintf(blab," #%d ",av->ival) ; /* should not happen! */
return blab ;
}
/*------------------------------------------------------------------------------
Called when the user actually makes the choice of dataset
--------------------------------------------------------------------------------*/
static void NUD_finalize_dset_CB( Widget w, XtPointer fd, MCW_choose_cbs * cbs )
{
int id = cbs->ival ;
THD_3dim_dataset * qset ;
XmString xstr ;
char str[256] ;
/* check for errors */
if( !nudger_open ){ POPDOWN_strlist_chooser; XBell(dc->display,100); return; }
if( dset != NULL && undo_nuse > 1 ){ XBell(dc->display,100); return; }
if( id < 0 || id >= ndsl ){ XBell(dc->display,100); return; }
qset = PLUTO_find_dset( &(dsl[id].idcode) ) ; /* the new dataset */
if( qset == NULL ){ XBell(dc->display,100); return; } /* shouldn't happen */
/* if not same as old dataset, close that one down */
if( dset != NULL && qset != dset ){
DSET_unlock(dset) ; DSET_unload(dset) ; DSET_anyize(dset) ;
}
/* accept this dataset */
dset = qset ; dset_idc = dset->idcode ;
undo_nuse = 1 ;
undo_ntop = 1 ;
rmat = undo_rmat[0] ;
svec = undo_svec[0] ; NUD_setcumlab() ;
SENSITIZE(undo_pb ,0) ;
SENSITIZE(redo_pb ,0) ;
SENSITIZE(nudge_pb,1) ;
SENSITIZE(doall_pb,0) ;
/* write the informational label */
xstr = XmStringCreateLtoR( dsl[id].title , XmFONTLIST_DEFAULT_TAG ) ;
XtVaSetValues( info_lab , XmNlabelString , xstr , NULL ) ;
XmStringFree(xstr) ;
/* lock and load this one into memory (not mmap) */
DSET_mallocize(dset) ; DSET_lock(dset) ; DSET_load(dset) ;
if( imbase != NULL ){ mri_free(imbase); imbase = NULL; }
/* refit the sub-brick selector menu */
if( dset_ival >= DSET_NVALS(dset) ) dset_ival = DSET_NVALS(dset)-1 ;
refit_MCW_optmenu( brick_av ,
0 , /* new minval */
DSET_NVALS(dset)-1 , /* new maxval */
dset_ival , /* new inival */
0 , /* new decim? */
NUD_brick_av_label_CB , /* text routine */
dset /* text data */
) ;
AV_SENSITIZE( brick_av , (DSET_NVALS(dset) > 1) ) ;
/* set codes indicating rotation axes:
iha = left or right handed coordinate order in dataset
ax1 = axis index for 'I' hax1 = sign for roll angle
ax2 = axis index for 'R' hax2 = sign for pitch angle
ax3 = axis index for 'A' hax3 = sign for yaw angle */
iha = THD_handedness( dset ) ;
ax1 = THD_axcode(dset,'I') ; hax1 = ax1 ; ax1 = abs(ax1)-1 ; /* roll */
ax2 = THD_axcode(dset,'R') ; hax2 = ax2 ; ax2 = abs(ax2)-1 ; /* pitch */
ax3 = THD_axcode(dset,'A') ; hax3 = ax3 ; ax3 = abs(ax3)-1 ; /* yaw */
adx = THD_axcode(dset,'S') ; /* for shifts */
ady = THD_axcode(dset,'L') ;
adz = THD_axcode(dset,'P') ;
#if 0
fprintf(stderr,"NUD_finalize_dset_CB: iha=%d\n"
" ax1 =%2d ax2 =%2d ax3 =%2d\n"
" hax1=%2d hax2=%2d hax3=%2d\n"
" adx =%2d ady =%2d adz =%2d\n" ,
iha , ax1,ax2,ax3 , hax1,hax2,hax3 , adx,ady,adz ) ;
#endif
return ;
}
/*-------------------------------------------------------------------
Callback for sub-brick index arrowval
---------------------------------------------------------------------*/
static void NUD_brick_av_CB( MCW_arrowval * av , XtPointer cd )
{
if( imbase != NULL ){ XBell(dc->display,100); return; }
dset_ival = av->ival ;
return ;
}
/*-------------------------------------------------------------------
Rotate an image in place according to the current specs
---------------------------------------------------------------------*/
static void NUD_rotate( MRI_IMAGE *im )
{
int clipit=clip_av->ival , mode=REG_resam_ints[interp_av->ival] ;
float cbot,ctop ;
float *fvol ;
double th1,th2,th3 , dx,dy,dz ;
if( im == NULL || dset == NULL ) return ;
rotangles( rmat, &th1,&th2,&th3 ) ;
if( hax1 < 0 ) th1 = -th1 ;
if( hax2 < 0 ) th2 = -th2 ;
if( hax3 < 0 ) th3 = -th3 ;
UNLOAD_DFVEC3( svec , dx,dy,dz ) ;
#if 0
fprintf(stderr,"th1=%g th2=%g th3=%g\n",th1,th2,th3) ;
#endif
/* nothing to do? */
if( fabs(th1) < EPS && fabs(th2) < EPS && fabs(th3) < EPS &&
fabs(dx) < EPS && fabs(dy) < EPS && fabs(dz) < EPS ) return ;
#if 0
if( clipit && (mode == MRI_LINEAR || mode == MRI_NN) ) clipit = 0 ;
#endif
/*--- 09 May 2005: RGB input image ==> do each channel separately ---*/
#undef BCLIP
#define BCLIP(x) if((x)<0.0f)(x)=0.0f; else if((x)>255.0)(x)=255.0
if( im->kind == MRI_rgb ){
MRI_IMARR *imtriple ;
MRI_IMAGE *rim, *gim, *bim, *newim ;
float *fr, *fg, *fb ;
register int ii ;
imtriple = mri_rgb_to_3float( im ) ;
if( imtriple == NULL ){
fprintf(stderr,"*** mri_rgb_to_3float fails in NUD_rotate!\n"); return;
}
rim = IMAGE_IN_IMARR(imtriple,0) ;
gim = IMAGE_IN_IMARR(imtriple,1) ;
bim = IMAGE_IN_IMARR(imtriple,2) ; FREE_IMARR(imtriple) ;
NUD_rotate(rim); NUD_rotate(gim); NUD_rotate(bim);
fr = MRI_FLOAT_PTR(rim); fg = MRI_FLOAT_PTR(gim); fb = MRI_FLOAT_PTR(bim);
for( ii=0 ; ii < im->nvox ; ii++ ){
BCLIP(fr[ii]) ; BCLIP(fg[ii]) ; BCLIP(fb[ii]) ;
}
newim = mri_3to_rgb( rim, gim, bim ) ;
mri_free(rim) ; mri_free(gim) ; mri_free(bim) ;
memcpy( MRI_RGB_PTR(im) , MRI_RGB_PTR(newim) , 3*im->nvox ) ;
mri_free(newim) ;
return ;
}
/*--- need a floating point copy? ---*/
if( im->kind != MRI_float ){
fvol = (float *) malloc( sizeof(float) * im->nvox ) ;
EDIT_coerce_type( im->nvox ,
im->kind , mri_data_pointer(im) ,
MRI_float , fvol ) ;
} else {
fvol = MRI_FLOAT_PTR(im) ;
}
/* compute bounds? */
if( clipit ){
register int ii ; register float bb,tt ;
bb = tt = fvol[0] ;
for( ii=1 ; ii < im->nvox ; ii++ ){
if( fvol[ii] < bb ) bb = fvol[ii] ;
else if( fvol[ii] > tt ) tt = fvol[ii] ;
}
cbot = bb ; ctop = tt ;
}
/* actually rotate! */
THD_rota_method( mode ) ; /* this line fixed 28 Nov 2000 */
THD_rota_vol( im->nx , im->ny , im->nz ,
fabs(DSET_DX(dset)), fabs(DSET_DY(dset)), fabs(DSET_DZ(dset)),
fvol , ax1,th1 , ax2,th2 , ax3,th3 , DELTA_AFTER,dx,dy,dz ) ;
/* apply bounds? */
if( clipit ){
register int ii ; register float bb,tt ;
bb = cbot ; tt = ctop ;
for( ii=0 ; ii < im->nvox ; ii++ ){
if( fvol[ii] < bb ) fvol[ii] = bb ;
else if( fvol[ii] > tt ) fvol[ii] = tt ;
}
}
/* convert type? */
if( im->kind != MRI_float ){
EDIT_coerce_type( im->nvox , MRI_float , fvol ,
im->kind , mri_data_pointer(im) ) ;
free(fvol) ;
}
return ;
}
/*--------------------------------------------------------------------------
Actually nudge the base brick, and then redisplay it.
----------------------------------------------------------------------------*/
static void NUD_update_base(Widget w)
{
MRI_IMAGE * im ;
if( dset == NULL || imbase == NULL || dset_ival >= DSET_NVALS(dset) ) return;
if( w != NULL ) MCW_invert_widget(w) ;
im = mri_copy(imbase) ; /* copy base */
NUD_rotate( im ) ; /* rotate copy */
EDIT_substitute_brick( dset , dset_ival , /* put into dset */
im->kind , mri_data_pointer(im) );
if( ISVALID_STATISTIC(dset->stats) ) /* 27 Nov 2000 */
THD_update_statistics( dset ) ;
mri_clear_data_pointer( im ) ; mri_free(im) ; /* toss the trash */
PLUTO_dset_redisplay( dset ) ; /* re-show it */
AFNI_process_drawnotice( im3d ) ; /* anyone cares? */
if( w != NULL ) MCW_invert_widget(w) ;
return ;
}
/*-------------------------------------------------------------------
Callback for Do All button - nudge all bricks
---------------------------------------------------------------------*/
static void NUD_doall_CB( Widget w, XtPointer client_data, XtPointer call_data )
{
int iv , nvals ;
MRI_IMAGE * im ;
char str[256] ;
double th1,th2,th3 ;
Widget meter ;
if( dset == NULL || imbase == NULL || undo_nuse == 1 ){ /* bad bad bad */
XBell(dc->display,100); return;
}
/*----- actually do something -----*/
/* copy imbase back into dataset */
EDIT_substitute_brick( dset , dset_ival ,
imbase->kind , mri_data_pointer(imbase) ) ;
mri_clear_data_pointer(imbase) ; mri_free(imbase) ; imbase = NULL ;
/* nudge each sub-brick */
nvals = DSET_NVALS(dset) ;
if( nvals > 1 )
meter = MCW_popup_meter( shell , METER_TOP_WIDE ) ;
for( iv=0 ; iv < nvals ; iv++ ){
MCW_invert_widget(doall_pb) ;
im = mri_copy( DSET_BRICK(dset,iv) ) ;
NUD_rotate( im ) ;
EDIT_substitute_brick( dset , iv ,
im->kind , mri_data_pointer(im) ) ;
mri_clear_data_pointer( im ) ; mri_free(im) ;
if( nvals > 1 )
MCW_set_meter( meter , (int)(100.0*(iv+0.5)/nvals) ) ;
}
/* store the history of what we just did */
rotangles( rmat, &th1,&th2,&th3 ) ;
th1 *= iha*(180.0/PI) ; th2 *= iha*(180.0/PI) ; th3 *= iha*(180.0/PI) ;
sprintf(str,"plug_nudge: -rotate %s",
NUD_threestring(th1,th2,th3,'I','R','A') ) ;
iv = strlen(str) ;
shiftdeltas( svec , &th1,&th2,&th3 ) ;
sprintf(str+iv," -ashift %s" ,
NUD_threestring(th1,th2,th3,'S','L','P') ) ;
tross_Append_History( dset , str );
/* write to disk, and redisplay */
if( nvals > 1 ) MCW_set_meter( meter , 100 ) ;
DSET_write( dset ) ;
PLUTO_dset_redisplay( dset ) ;
AFNI_process_drawnotice( im3d ) ;
/*----- reset to 0 nudge -----*/
rmat = undo_rmat[0] ; svec = undo_svec[0] ; NUD_setcumlab() ;
/* clear undo stack */
undo_nuse = undo_ntop = 1 ;
SENSITIZE(undo_pb,0) ; SENSITIZE(redo_pb,0) ;
/* can't Do All again right now */
SENSITIZE(doall_pb,0) ;
/* allow user to change datasets again */
SENSITIZE(choose_pb,1) ;
AV_SENSITIZE( brick_av , (nvals > 1) ) ;
if( nvals > 1 )
MCW_popdown_meter(meter) ;
if( nvals%2 == 1 ) MCW_invert_widget(doall_pb) ;
return ;
}
syntax highlighted by Code2HTML, v. 0.9.1