/***************************************************************************** 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 "afni.h" #ifndef ALLOW_PLUGINS # error "Plugins not properly set up -- see machdep.h" #endif extern char **Atlas_Names_List(ATLAS_LIST *atl); #undef BGCOLOR_ARG #define BGCOLOR_ARG(str) \ XtVaTypedArg , XmNbackground , XmRString , (str) , strlen(str)+1 #undef RCOL #define RCOL "#440011" /* 26 Oct 2007 */ #undef GCOL #define GCOL "#003311" /*********************************************************************** Plugin to draw values into a dataset. Makes a custom interface. ************************************************************************/ /*---------- prototypes for internal routines ----------*/ char * DRAW_main( PLUGIN_interface * ) ; void DRAW_make_widgets(void) ; void DRAW_done_CB ( Widget , XtPointer , XtPointer ) ; void DRAW_undo_CB ( Widget , XtPointer , XtPointer ) ; void DRAW_redo_CB ( Widget , XtPointer , XtPointer ) ; /* 19 Nov 2003 */ void DRAW_help_CB ( Widget , XtPointer , XtPointer ) ; void DRAW_quit_CB ( Widget , XtPointer , XtPointer ) ; void DRAW_save_CB ( Widget , XtPointer , XtPointer ) ; void DRAW_saveas_CB( Widget , XtPointer , XtPointer ) ; /* 24 Sep 2001 */ void DRAW_choose_CB( Widget , XtPointer , XtPointer ) ; void DRAW_color_CB ( MCW_arrowval * , XtPointer ) ; void DRAW_mode_CB ( MCW_arrowval * , XtPointer ) ; void DRAW_value_CB ( MCW_arrowval * , XtPointer ) ; void DRAW_fillin_CB( Widget , XtPointer , XtPointer ) ; /* 19 Mar 2001 */ void DRAW_ttatlas_CB( Widget , XtPointer , XtPointer ) ; /* 22 Aug 2001 */ void DRAW_label_CB( Widget , XtPointer , XtPointer ) ; /* 15 Oct 2003 */ void DRAW_label_EV( Widget , XtPointer , XEvent * , RwcBoolean * ) ; void DRAW_attach_dtable( Dtable *, char *, THD_3dim_dataset * ) ; void DRAW_receiver( int , int , void * , void * ) ; int DRAW_into_dataset( int , int * , int * , int * , void * ) ; void DRAW_finalize_dset_CB( Widget , XtPointer , MCW_choose_cbs * ) ; void DRAW_2dfiller( int nx , int ny , int ix , int jy , byte * ar ) ; void DRAW_saveas_finalize_CB( Widget , XtPointer , MCW_choose_cbs * ) ; static int Check_value(void); static void Sensitize_copy_bbox(int); static void DRAW_2D_expand( int, int *, int *, int *, int, int *, int ** ) ; /* 07 Oct 2002 */ static void DRAW_3D_expand( int, int *, int *, int *, int, int *, int ** ) ; /* 07 Oct 2002 */ static void DRAW_2D_circle( int, int *, int *, int *, int, int *, int ** ) ; /* 16 Oct 2002 */ static void DRAW_3D_sphere( int, int *, int *, int *, int, int *, int ** ) ; /* 16 Oct 2002 */ #define USE_COLLAPSAR #ifdef USE_COLLAPSAR static void DRAW_collapsar( int * , int * ) ; /* 21 Oct 2002 */ #endif static PLUGIN_interface * plint = NULL ; static int infill_mode = 0 ; void DRAW_set_value_label(void) ; char * DRAW_value_string( float val ) ; static void choose_new_atlas_CB(MCW_arrowval * , XtPointer ); static MCW_arrowval *Atlas_chooser(Widget rc); static void free_plugdraw_atlas_list(); static float get_afni_thresh_percent(); static char *plugdraw_atlasname; /*********************************************************************** Set up the interface to the user. Note that we bypass the normal interface creation, and simply have the menu selection directly call the main function, which will create a custom set of interface widgets. ************************************************************************/ DEFINE_PLUGIN_PROTOTYPE PLUGIN_interface * PLUGIN_init( int ncall ) { if( ncall > 0 ) return NULL ; /* only one interface */ plint = PLUTO_new_interface( "Draw Dataset" , NULL , NULL , PLUGIN_CALL_IMMEDIATELY , DRAW_main ) ; PLUTO_add_hint( plint , "Interactive Dataset Editor" ) ; PLUTO_set_sequence( plint , "A:olddset:editor" ) ; return plint ; } /*************************************************************************** Will be called from AFNI when user selects from Plugins menu. ****************************************************************************/ /* Interface widgets */ static Widget shell=NULL , rowcol , info_lab , choose_pb; static Widget atlas_panel ; static Widget done_pb, undo_pb,redo_pb, help_pb, quit_pb, save_pb, saveas_pb ; static MCW_arrowval *value_av , *color_av , *mode_av ; static MCW_arrowval *rad_av ; /* 16 Oct 2002 */ static Widget label_textf , label_label ; /* 15 Oct 2003 */ #if 0 # define ENABLE_rad_av \ AV_SENSITIZE( rad_av , (mode_ival >= FIRST_RAD_MODE && mode_ival <= LAST_RAD_MODE) ) #else # define ENABLE_rad_av \ do{ if( mode_ival >= FIRST_RAD_MODE && mode_ival <= LAST_RAD_MODE ) \ XtManageChild( rad_av->wrowcol ) ; \ else \ XtUnmanageChild( rad_av->wrowcol ) ; \ } while(0) #endif static MCW_arrowval * fillin_dir_av , * fillin_gap_av ; /* 19 Mar 2001 */ static Widget fillin_doit_pb ; static Widget ttatlas_rowcol=NULL ; /* 22 Aug 2001 */ static MCW_arrowval * ttatlas_region_av , * ttatlas_hemisphere_av, * atlas_chooser_av ; static Widget ttatlas_actar ; #define HAVE_TTATLAS (ttatlas_rowcol != NULL) #define NHEMI 3 #define HEMI_LEFT "Left only" #define HEMI_RIGHT "Right only" #define HEMI_BOTH "Both" static char *HEMI_strings[NHEMI] = { HEMI_LEFT , HEMI_RIGHT , HEMI_BOTH } ; #define NUM_TTATLAS_ACT 2 #define TTATLAS_overwrite_label "Load: OverWrite" #define TTATLAS_infill_label "Load: InFill" static MCW_action_item TTATLAS_act[] = { { TTATLAS_overwrite_label , DRAW_ttatlas_CB, NULL,NULL, NULL, 0 } , { TTATLAS_infill_label , DRAW_ttatlas_CB, NULL,NULL, NULL, 0 } } ; typedef struct { int reg_num ; /* number of regions */ char **reg_label ; /* region labels */ short *reg_tto ; /* index into afni.h TTO_list */ short *reg_ttval ; /* what value in TTatlas+tlrc */ } ttatlas_compendium ; static ttatlas_compendium *ttatlas_list=NULL ; /* 24 Sep 2001: stuff for Copy on input */ static MCW_bbox *copy_bbox ; static MCW_arrowval *copy_mode_av , *copy_type_av , *copy_datum_av ; void DRAW_copy_bbox_CB( Widget , XtPointer , XtPointer ) ; THD_3dim_dataset * DRAW_copy_dset( THD_3dim_dataset *, int,int,int ) ; /* Other data */ #define MODE_CURVE 0 #define MODE_CLOSED 1 #define MODE_POINTS 2 #define MODE_FLOOD_VAL 3 #define MODE_FLOOD_NZ 4 #define MODE_FLOOD_ZERO 5 /* 30 Jan 1999 */ #define MODE_ZERO_VAL 6 /* 31 Jan 1999 */ #define MODE_FLOOD_VZ 7 /* 30 Apr 2002 */ #define MODE_FILLED 8 /* 25 Sep 2001 */ #define MODE_2D_NN1 9 /* 07 Oct 2002 */ #define MODE_2D_NN2 10 #define MODE_2D_NN3 11 #define MODE_2D_NN4 12 #define MODE_2D_NN5 13 #define MODE_3D_NN1 14 #define MODE_3D_NN2 15 #define MODE_3D_NN3 16 #define MODE_3D_NN4 17 #define MODE_3D_NN5 18 #define MODE_3D_NN6 19 #define MODE_3D_5x5 20 /* 08 Oct 2002 */ #define MODE_2D_CIRC 21 /* 16 Oct 2002 */ #define MODE_3D_SPHR 22 /* 16 Oct 2002 */ #define FIRST_2D_MODE MODE_2D_NN1 #define LAST_2D_MODE MODE_2D_NN5 #define FIRST_3D_MODE MODE_3D_NN1 #define LAST_3D_MODE MODE_3D_5x5 #define FIRST_RAD_MODE MODE_2D_CIRC #define LAST_RAD_MODE MODE_3D_SPHR static char * mode_strings[] = { "Open Curve" , /* MODE_CURVE */ "Closed Curve" , /* MODE_CLOSED */ "Points" , /* MODE_POINTS */ "Flood->Value" , /* MODE_FLOOD_VAL */ "Flood->Nonzero" , /* MODE_FLOOD_NZ */ "Flood->Zero" , /* MODE_FLOOD_ZERO */ "Zero->Value" , /* MODE_ZERO_VAL */ "Flood->Val/Zero" , /* MODE_FLOOD_VZ */ "Filled Curve" , /* MODE_FILLED */ " 2D Nbhd: 1st NN" , /* 07 Oct 2002 */ " 2D Nbhd: 2nd NN" , " 2D Nbhd: 3rd NN" , " 2D Nbhd: 4th NN" , " 2D Nbhd: 5th NN" , "*3D Nbhd: 1st NN" , "*3D Nbhd: 2nd NN" , "*3D Nbhd: 3rd NN" , "*3D Nbhd: 4th NN" , "*3D Nbhd: 5th NN" , "*3D Nbhd: 6th NN" , "*3D Nbhd: 5x5x5" , " 2D Circle" , /* 16 Oct 2002 */ " 3D Sphere" } ; static int mode_width[] = { /* 08 Oct 2002: line width for button2 drawing */ 2,2 , 0,0,0,0,0,0 , 2 , 3,3,5,5,7 , 3,3,3,5,5,5,5 , 2,2 } ; static int mode_ints[] = { DRAWING_LINES , DRAWING_FILL , DRAWING_POINTS , DRAWING_POINTS , DRAWING_POINTS , DRAWING_POINTS , DRAWING_POINTS , DRAWING_POINTS , DRAWING_FILL , DRAWING_LINES , DRAWING_LINES , DRAWING_LINES , DRAWING_LINES , DRAWING_LINES , DRAWING_LINES , DRAWING_LINES , DRAWING_LINES , DRAWING_LINES , DRAWING_LINES , DRAWING_LINES , DRAWING_LINES , DRAWING_LINES , DRAWING_LINES } ; #define NUM_modes (sizeof(mode_ints)/sizeof(int)) #define NFILLIN_DIR 3 static char *fillin_dir_strings[NFILLIN_DIR] = { "A-P" , "I-S" , "R-L" } ; #define NFILLIN_GAP 9 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 ; /* 31 Mar 1999 */ static Dtable *vl_dtable=NULL ; /* 17 Oct 2003 */ static int color_index = 1 ; /* from color_av */ static int mode_ival = MODE_FILLED ; static int mode_index = DRAWING_FILL ; /* from mode_av */ static int value_int = 1 ; /* from value_av */ static float value_float = 1.0 ; /* ditto */ static int editor_open = 0 ; static int dset_changed = 0 ; static int recv_open = 0 ; static int recv_key = -1; /****** 19 Nov 2003: new stuff for multiple undo/redo ******/ typedef struct { /* structure holds one drawing operation */ int npt , btyp ; /* number of data points, type of data */ int *xyz ; /* 1D index into dataset array */ void *buf ; /* data from dataset array */ } dobuf ; /* macro to create an empty buffer */ #define CREATE_DOBUF(db,np,ip) \ do{ db = (dobuf *)calloc(1 ,sizeof(dobuf)) ; \ db->xyz = (int *) calloc(np,sizeof(int)) ; \ db->buf = (void *) calloc(np,mri_datum_size(ip)) ; \ db->npt = np ; db->btyp = ip ; \ } while(0) /* macro to delete a buffer from the Macrocosmic All */ #define DESTROY_DOBUF(db) do{ if( db != NULL ){ \ if( db->xyz != NULL ) free(db->xyz); \ if( db->buf != NULL ) free(db->buf); \ free(db) ; \ }} while(0) /* amount of memory used by a buffer */ #define SIZEOF_DOBUF(db) \ ( db->npt * ( sizeof(int) + mri_datum_size(db->btyp) ) ) static int undo_num = 0 ; /* depth of undo stack */ static int redo_num = 0 ; /* depth of redo stack */ static dobuf **undo_stack = NULL ; /* undo stack */ static dobuf **redo_stack = NULL ; /* redo stack */ static int undo_how = 0 ; /* where to save undo info */ static void DRAW_undo_sizecheck(void) ; /* check/limit undo stack size */ static void DRAW_undo_butlab( Widget w , int ) ; /* label undo/redo button */ #define UNDO_button_labelize DRAW_undo_butlab(undo_pb,undo_num) #define REDO_button_labelize DRAW_undo_butlab(redo_pb,redo_num) /* this macro erases the undo stack */ #define CLEAR_UNDOBUF \ do{ if( undo_num > 0 || undo_stack != NULL ){ \ int ii ; \ for( ii=0 ; ii < undo_num ; ii++ ) \ DESTROY_DOBUF( undo_stack[ii] ) ; \ if( undo_stack != NULL ) free( undo_stack ) ; \ undo_num = 0 ; undo_stack = NULL ; \ } \ UNDO_button_labelize ; \ } while(0) /* this macro erases the redo stack */ #define CLEAR_REDOBUF \ do{ if( redo_num > 0 || redo_stack != NULL ){ \ int ii ; \ for( ii=0 ; ii < redo_num ; ii++ ) \ DESTROY_DOBUF( redo_stack[ii] ) ; \ if( redo_stack != NULL )free( redo_stack ) ; \ redo_num = 0 ; redo_stack = NULL ; \ } \ REDO_button_labelize ; \ } while(0) /* this macro erases both stacks */ #define CLEAR_UNREDOBUF \ do{ CLEAR_UNDOBUF ; CLEAR_REDOBUF ; undo_how = 0 ; } while(0) /******/ static THD_dataxes dax_save ; /* save this for later reference */ static int old_stroke_autoplot = 0 ; /* 27 Oct 2003 */ static char **atlas_names; /* list of atlases to select among */ char * DRAW_main( PLUGIN_interface *plint ) { XmString xstr ; /*-- sanity checks --*/ if( ! IM3D_OPEN(plint->im3d) ) return " \n AFNI Controller\nnot opened?! \n " ; if( editor_open ){ 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 */ DRAW_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 Editor %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 , BGCOLOR_ARG(RCOL) , NULL ) ; XmStringFree(xstr) ; /*-- 22 Aug 2001: perhaps allow TT Atlas stuff --*/ if( HAVE_TTATLAS ) { XtSetSensitive( ttatlas_rowcol , CAN_TALTO(im3d) ) ; } /*-- pop the widget up --*/ XtMapWidget(shell) ; PLUTO_cursorize(shell) ; /*-- misc initialization --*/ dset = NULL ; /* not editing anything */ dset_changed = 0 ; /* not yet changed */ editor_open = 1 ; /* editor is now open for business */ recv_open = 0 ; /* receiver is not yet open */ recv_key = -1; /* and has no identifier key */ if( vl_dtable != NULL ){ /* 20 Oct 2003 */ destroy_Dtable(vl_dtable) ; vl_dtable = NULL ; } SENSITIZE(save_pb,0) ; SENSITIZE(saveas_pb,0) ; SENSITIZE(choose_pb,1) ; Sensitize_copy_bbox(1); /* 19 Nov 2003: new undo/redo stuff */ undo_num = redo_num = undo_how = 0 ; undo_stack = redo_stack = NULL ; UNDO_button_labelize ; REDO_button_labelize ; old_stroke_autoplot = AFNI_yesenv("AFNI_STROKE_AUTOPLOT") ; if( old_stroke_autoplot ) putenv("AFNI_STROKE_AUTOPLOT=NO") ; return NULL ; } /*------------------------------------------------------------------------ Make the control popup for this thing --------------------------------------------------------------------------*/ /*-- structures defining action buttons (at bottom of popup) --*/ #define NACT 7 /* number of action buttons */ static MCW_action_item DRAW_actor[NACT] = { {"Undo[0]",DRAW_undo_CB,NULL, "Undoes previous draw\naction, if possible","Undo last change",0} , {"Redo[0]",DRAW_redo_CB,NULL, "Redoes previous undone\naction, if possible","Redo last undo",0} , {"Help",DRAW_help_CB,NULL, "Displays more help" , "Displays more help",0} , {"Quit",DRAW_quit_CB,NULL, "Discard edits since last Save\nand close Editor" , "Discard edits and close",0} , {"Save",DRAW_save_CB,NULL, "Save edits to disk\nand continue" , "Save to disk and continue",0} , {"SaveAs",DRAW_saveas_CB,NULL, /* 24 Sep 2001 */ "Save edits to disk\nin a new dataset\nand continue" , "Save to disk in new dataset, continue",0} , {"Done",DRAW_done_CB,NULL, "Save edits to disk\nand close Editor" , "Save and close",1} } ; ttatlas_compendium * New_ttatlas_compendium(char *atname) { ttatlas_compendium *ttatlas_llist = NULL; int tto_count = 0; if (!atname) return(ttatlas_llist); if ((tto_count = atlas_n_points(atname)) <= 0) return(ttatlas_llist); ttatlas_llist = (ttatlas_compendium *) calloc(1,sizeof(ttatlas_compendium)); ttatlas_llist->reg_num = 0; ttatlas_llist->reg_label = (char **)calloc(tto_count,sizeof(char *)); ttatlas_llist->reg_tto = (short *)calloc(tto_count,sizeof(short)); ttatlas_llist->reg_ttval = (short *)calloc(tto_count,sizeof(short)); return(ttatlas_llist); } void DRAW_make_widgets(void) { XmString xstr ; ATLAS_POINT *tto_list=NULL; char title_str[256]; /*** top level shell for window manager ***/ shell = XtVaAppCreateShell( "AFNI" , "AFNI" , topLevelShellWidgetClass , dc->display , XmNtitle , "AFNI Editor" , /* top of window */ XmNiconName , "Editor" , /* 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 */ if( afni48_good ) /* set icon pixmap */ XtVaSetValues( shell , XmNiconPixmap , afni48_pixmap , NULL ) ; 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 ) , DRAW_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 , BGCOLOR_ARG(RCOL) , XmNinitialResourcesPersistent , False , NULL ) ; XmStringFree(xstr) ; MCW_register_help( info_lab , "Shows dataset being edited" ) ; MCW_register_hint( info_lab , "Shows dataset being edited" ) ; /*** separator for visual neatness ***/ (void) XtVaCreateManagedWidget( "AFNI" , xmSeparatorWidgetClass , rowcol , XmNseparatorType , XmDOUBLE_LINE , XmNinitialResourcesPersistent , False , NULL ) ; /*-- 24 Sep 2001: Copy mode stuff [moved up 06 Oct 2002] --*/ { Widget rc ; static char *cbox_label[1] = { "Copy Dataset" } ; static char *cmode_label[2] = { "Data" , "Zero" } ; static char *ctype_label[3] = { "Show as Olay" , "Show as Ulay" } ; static char *cdatum_label[4] = { "As Is" , "Byte" , "Short" , "Float" } ; /*** rowcol to hold Copy widgets ***/ rc = XtVaCreateWidget( "AFNI" , xmRowColumnWidgetClass , rowcol , XmNpacking , XmPACK_TIGHT , XmNorientation , XmHORIZONTAL , XmNmarginHeight , 0 , XmNmarginWidth , 0 , XmNspacing , 0 , XmNinitialResourcesPersistent , False , XmNtraversalOn , True , NULL ) ; /*** button box to turn copy mode on or off ***/ copy_bbox = new_MCW_bbox( rc, 1,cbox_label, MCW_BB_check,MCW_BB_noframe, DRAW_copy_bbox_CB,NULL ) ; MCW_reghint_children( copy_bbox->wrowcol , "Make copy of dataset on input" ) ; MCW_reghelp_children( copy_bbox->wrowcol , "Make copy of dataset on input?" ) ; /*** arrowvals to let user choose Copy method ***/ copy_mode_av = new_MCW_optmenu( rc , NULL , 0 , 1 , 1 , 0 , NULL,NULL , MCW_av_substring_CB , cmode_label ) ; MCW_reghint_children( copy_mode_av->wrowcol , "How to copy values from dataset" ) ; MCW_reghelp_children( copy_mode_av->wrowcol , "How to copy values from dataset:\n" "Data => use input dataset values\n" "Zero => fill dataset with zeros" ) ; copy_type_av = new_MCW_optmenu( rc , NULL , 0 , 1 , 0 , 0 , NULL,NULL , MCW_av_substring_CB , ctype_label ) ; MCW_reghint_children( copy_type_av->wrowcol , "Copy is shown as overlay or underlay" ) ; MCW_reghelp_children( copy_type_av->wrowcol , "Copy will be shown as the overlay\n" "or will be shown as the underlay" ) ; copy_datum_av= new_MCW_optmenu( rc , NULL , 0 , 3 , 0 , 0 , NULL,NULL , MCW_av_substring_CB , cdatum_label ) ; MCW_reghint_children( copy_datum_av->wrowcol , "Data storage type for copy" ) ; MCW_reghelp_children( copy_datum_av->wrowcol , "Data storage type for zero-filled copy:\n" "As Is => use data type in input dataset\n" "Byte => store new dataset as bytes\n" "Short => store new dataset as shorts\n" "Float => store new dataset as floats") ; MCW_set_bbox(copy_bbox, 1); /* turn copy on by default - drg 4/3/2006 */ AV_SENSITIZE( copy_mode_av , True ) ; AV_SENSITIZE( copy_type_av , True ) ; AV_SENSITIZE( copy_datum_av, True ) ; XtManageChild(rc) ; } /* end of Copy mode stuff */ /*** button to let user choose dataset to edit ***/ xstr = XmStringCreateLtoR( " Choose dataset for copying" , XmFONTLIST_DEFAULT_TAG ) ; choose_pb = XtVaCreateManagedWidget( "AFNI" , xmPushButtonWidgetClass , rowcol , XmNlabelString , xstr , XmNtraversalOn , True , BGCOLOR_ARG(GCOL) , XmNinitialResourcesPersistent , False , NULL ) ; XmStringFree(xstr) ; XtAddCallback( choose_pb, XmNactivateCallback, DRAW_choose_CB, NULL ) ; MCW_register_help( choose_pb , "Use this to popup a\n" "'chooser' that lets\n" "you select which\n" "dataset to edit." ) ; MCW_register_hint( choose_pb , "Popup a dataset chooser" ) ; /*** separator for visual neatness ***/ (void) XtVaCreateManagedWidget( "AFNI" , xmSeparatorWidgetClass , rowcol , XmNseparatorType , XmDOUBLE_LINE , XmNinitialResourcesPersistent , False , NULL ) ; /*** arrowval to choose value that is drawn into dataset voxels ***/ { Widget rc ; rc = XtVaCreateWidget( "AFNI" , xmRowColumnWidgetClass , rowcol , XmNpacking , XmPACK_TIGHT , XmNorientation , XmHORIZONTAL , XmNmarginHeight , 0 , XmNmarginWidth , 0 , XmNspacing , 0 , XmNinitialResourcesPersistent , False , XmNtraversalOn , True , NULL ) ; value_av = new_MCW_arrowval( rc , "Value " , MCW_AV_downup , -32767,32767,value_int , MCW_AV_editext , 0 , DRAW_value_CB , NULL , NULL,NULL ) ; MCW_reghelp_children( value_av->wrowcol , "Use this to set the value that\n" "will be drawn into the dataset\n" "using mouse button 2." ) ; MCW_reghint_children( value_av->wrowcol , "Goes into dataset voxels" ) ; /*-- 15 Oct 2003: Label for the value --*/ xstr = XmStringCreateLtoR( " Label" , XmFONTLIST_DEFAULT_TAG ) ; label_label = XtVaCreateManagedWidget( "dialog" , xmLabelWidgetClass , rc , XmNlabelString , xstr , XmNrecomputeSize , False , XmNmarginWidth , 0 , XmNinitialResourcesPersistent , False , NULL ) ; XmStringFree(xstr) ; label_textf = XtVaCreateManagedWidget( "dialog" , xmTextFieldWidgetClass , rc , XmNcolumns , 19 , XmNeditable , True , XmNmaxLength , 128 , XmNresizeWidth , False , XmNmarginHeight , 1 , XmNmarginWidth , 1 , XmNcursorPositionVisible , True , XmNblinkRate , 0 , XmNautoShowCursorPosition , True , XmNtraversalOn , True , XmNinitialResourcesPersistent , False , NULL ) ; XtSetSensitive( label_label , (RwcBoolean)(value_int != 0) ) ; XtSetSensitive( label_textf , (RwcBoolean)(value_int != 0) ) ; XtAddCallback( label_textf, XmNactivateCallback , DRAW_label_CB , NULL ) ; /* return key */ XtAddCallback( label_textf, XmNlosingFocusCallback , DRAW_label_CB , NULL ) ; /* tab key */ XtInsertEventHandler( label_textf , /* notify when */ LeaveWindowMask , /* pointer leaves */ FALSE , /* this window */ DRAW_label_EV , (XtPointer) NULL , XtListTail ) ; /* last in queue */ XtInsertEventHandler( label_label , /* button press in label */ ButtonPressMask , FALSE , DRAW_label_EV , (XtPointer) NULL , XtListTail ) ; POPUP_cursorize( label_label ) ; XtManageChild(rc) ; } /*** option menu to choose drawing color ***/ color_av = new_MCW_colormenu( rowcol , "Color " , dc , 1 , dc->ovc->ncol_ov - 1 , color_index , DRAW_color_CB , NULL ) ; MCW_reghelp_children( color_av->wrowcol , "Use this to set the color that is\n" "shown during mouse button 2 drawing.\n" "N.B.: After drawing is completed,\n" " the dataset will be displayed\n" " with the chosen value replacing\n" " the drawing color. This color\n" " is used ONLY while button 2 is\n" " actually pressed down." ) ; MCW_reghint_children( color_av->wrowcol , "Used when button 2 is drawing" ) ; /*** arrowval to choose drawing mode ***/ /*-- 16 Oct 2002: put in a horiz rowcol, and add rad_av button --*/ { Widget rc ; rc = XtVaCreateWidget( "AFNI" , xmRowColumnWidgetClass , rowcol , XmNpacking , XmPACK_TIGHT , XmNorientation , XmHORIZONTAL , XmNmarginHeight , 0 , XmNmarginWidth , 0 , XmNspacing , 0 , XmNinitialResourcesPersistent , False , XmNtraversalOn , True , NULL ) ; mode_av = new_MCW_optmenu( rc , "Mode " , 0 , NUM_modes-1 , mode_ival,0 , DRAW_mode_CB , NULL , MCW_av_substring_CB , mode_strings ) ; AVOPT_columnize( mode_av , 2 ) ; MCW_reghelp_children( mode_av->wrowcol , "Use this to set the way in which\n" "drawing pixels on the screen is\n" "used to select dataset voxels:\n" "Open Curve = voxels picked along lines drawn;\n" "Closed Curve = voxels forming a closed curve\n" "Points = only voxels at X11 notify pixels;\n" "Flood->Value = flood fill from the chosen point\n" " out to points = Value\n" "Flood->Nonzero = flood fill from chosen point out\n" " to any nonzero point\n" "Flood->Zero = flood fill from chosen point out\n" " to any zero point\n" "Zero->Value = flood fill with zeros until the\n" " Value is hit\n" "Flood->Val/Zero = flood fill from the chosen point\n" " until the Value OR zero is hit\n" "Filled Curve = fill inside of closed curve with\n" " Value\n" "\n" "2D Nbhd = like Open Curve, but fills in around\n" " the in-plane neighborhood of each\n" " drawn point 'x' with the patterns:\n" " 5 4 3 4 5\n" " 4 2 1 2 4\n" " 3 1 x 1 3\n" " 4 2 1 2 4\n" " 5 4 3 4 5\n" " where the number indicates the\n" " Nearest Neighbor order of the\n" " points nearby 'x'.\n" "3D Nbhd = Similar, but in 3D (out-of-plane)\n" "\n" "2D Circle = Draw a circle of given Radius\n" "3D Sphere = Draw a sphere of given Radius\n" ) ; MCW_reghint_children( mode_av->wrowcol , "How voxels are chosen") ; /** 16 Oct 2002: radius chooser **/ rad_av = new_MCW_arrowval( rc , /* parent */ "R" , /* label */ MCW_AV_downup , /* arrow directions */ 1 , /* min value (0.01 mm from decim) */ 9999 , /* max value (99.99 mm) */ 400 , /* init value */ MCW_AV_editext , /* input/output text display */ 2 , /* decimal shift */ NULL , /* routine to call when button */ NULL , /* is pressed, and its data */ NULL,NULL /* no special display */ ) ; XtVaSetValues( rad_av->wtext , XmNcolumns , 5 , NULL ) ; MCW_reghint_children( rad_av->wrowcol , "Radius of Circles and Spheres" ) ; MCW_reghelp_children( rad_av->wrowcol , " \n" "Sets the radius (in mm) of the 2D Circle\n" "or 3D Sphere drawing modes. Voxels whose\n" "center-to-center distance is <= this value\n" "will be filled in.\n" ) ; ENABLE_rad_av ; /* turn it on or off */ XtManageChild(rc) ; } /*** 19 Mar 2001: stuff for linear fillin ***/ { Widget rc ; /*** separator for visual neatness ***/ (void) XtVaCreateManagedWidget( "AFNI" , xmSeparatorWidgetClass , rowcol , XmNseparatorType , XmDOUBLE_LINE , XmNinitialResourcesPersistent , False , NULL ) ; rc = XtVaCreateWidget( "AFNI" , xmRowColumnWidgetClass , rowcol , XmNpacking , XmPACK_TIGHT , XmNorientation , XmHORIZONTAL , XmNmarginHeight , 0 , XmNmarginWidth , 0 , XmNspacing , 0 , XmNinitialResourcesPersistent , False , XmNtraversalOn , True , NULL ) ; fillin_dir_av = new_MCW_optmenu( rc , "Linear Fillin " , 0 , NFILLIN_DIR-1 , 0 , 0 , NULL , NULL , MCW_av_substring_CB , fillin_dir_strings ) ; #if 0 /* Need big gaps. ZSS Aug. 2011 */ fillin_gap_av = new_MCW_optmenu( rc , " Gap" , 1 , NFILLIN_GAP , 4 , 0 , NULL,NULL,NULL,NULL ) ; #else fillin_gap_av = new_MCW_arrowval( rc , /* parent */ "Gap" , /* label */ MCW_AV_downup , /* arrow directions */ 1 , /* min value (0.1 mm from decim) */ 999 , /* max value (99.9 mm) */ 4 , /* init value */ MCW_AV_editext , /* input/output text display */ 0 , /* decimal shift */ NULL , /* routine to call when button */ NULL , /* is pressed, and its data */ NULL,NULL /* no special display */ ) ; XtVaSetValues( fillin_gap_av->wtext , XmNcolumns , 5 , NULL ) ; MCW_reghint_children( fillin_gap_av->wrowcol , "Fill Gap" ) ; MCW_reghelp_children( fillin_gap_av->wrowcol , " \n" "Sets the maximum voxel gap that gets filled in.\n" ) ; XtManageChild( fillin_gap_av->wrowcol ) ; #endif xstr = XmStringCreateLtoR( "*Do the Fill*" , XmFONTLIST_DEFAULT_TAG ) ; fillin_doit_pb = XtVaCreateManagedWidget( "AFNI" , xmPushButtonWidgetClass , rc , XmNlabelString , xstr , XmNtraversalOn , True , XmNinitialResourcesPersistent , False , NULL ) ; XtAddCallback( fillin_doit_pb , XmNactivateCallback, DRAW_fillin_CB, NULL ) ; XmStringFree(xstr) ; XtManageChild(rc) ; } /* end of fillin */ /*** 22 Aug 2001: stuff for TT Atlas Regions ***/ if( Atlas_With_Trimming(Current_Atlas_Default_Name(),1, NULL) > 0 ){ Widget rc ; int ii , nr ; XmString xstr ; /*** separator for visual neatness ***/ (void) XtVaCreateManagedWidget( "AFNI" , xmSeparatorWidgetClass , rowcol , XmNseparatorType , XmDOUBLE_LINE , XmNinitialResourcesPersistent , False , NULL ) ; /*** rowcol to hold all widgets ***/ ttatlas_rowcol = rc = XtVaCreateWidget( "AFNI" , xmRowColumnWidgetClass , rowcol , XmNpacking , XmPACK_TIGHT , XmNorientation , XmVERTICAL , XmNmarginHeight , 0 , XmNmarginWidth , 0 , XmNspacing , 0 , XmNinitialResourcesPersistent , False , XmNtraversalOn , True , NULL ) ; /*** label at top ***/ sprintf(title_str, "Select Atlas and Region (Current Atlas: %s)", Current_Atlas_Default_Name()); xstr = XmStringCreateLtoR( title_str , XmFONTLIST_DEFAULT_TAG ) ; atlas_panel = XtVaCreateManagedWidget( "dialog" , xmLabelWidgetClass , rc , XmNlabelString , xstr , XmNrecomputeSize , False , XmNmarginWidth , 0 , XmNinitialResourcesPersistent , False , NULL ) ; XmStringFree(xstr) ; atlas_chooser_av = Atlas_chooser(rc); /*** make list of atlas regions to include ***/ plugdraw_atlasname = Current_Atlas_Default_Name(); /* use default atlas name first */ if (!(ttatlas_list = New_ttatlas_compendium(plugdraw_atlasname))) { ERROR_message("Failed compending. This should not happen"); } tto_list = atlas_points(plugdraw_atlasname); nr = 0 ; for( ii=0 ; ii < atlas_n_points(plugdraw_atlasname) ; ii++ ){ ttatlas_list->reg_label [nr] = strdup(tto_list[ii].name) ; ttatlas_list->reg_tto [nr] = ii ; ttatlas_list->reg_ttval [nr] = tto_list[ii].tdval ; nr++ ; /* count how many regions actually have some non-zero value */ } ttatlas_list->reg_num = nr ; /*** Region chooser ***/ ttatlas_region_av = new_MCW_optmenu( rc , " " , 0 , nr-1 , 0 , 0 , NULL,NULL , MCW_av_substring_CB , ttatlas_list->reg_label ) ; AVOPT_columnize( ttatlas_region_av , 3 ) ; /*** Hemisphere chooser */ ttatlas_hemisphere_av = new_MCW_optmenu( rc , " Hemisphere(s)" , 0 , NHEMI-1 , NHEMI-1 , 0 , NULL,NULL , MCW_av_substring_CB, HEMI_strings ); /*** row of pushbuttons ***/ ttatlas_actar = MCW_action_area( rc , TTATLAS_act , NUM_TTATLAS_ACT ) ; XtManageChild( rc ) ; } /* end of TT Atlas */ /*** separator for visual neatness ***/ (void) XtVaCreateManagedWidget( "AFNI" , xmSeparatorWidgetClass , rowcol , XmNseparatorType , XmDOUBLE_LINE , XmNinitialResourcesPersistent , False , NULL ) ; /*** a set of action buttons below the line ***/ (void) MCW_action_area( rowcol , DRAW_actor , NACT ) ; undo_pb = (Widget) DRAW_actor[0].data ; redo_pb = (Widget) DRAW_actor[1].data ; /* 19 Nov 2003 */ help_pb = (Widget) DRAW_actor[2].data ; quit_pb = (Widget) DRAW_actor[3].data ; save_pb = (Widget) DRAW_actor[4].data ; saveas_pb = (Widget) DRAW_actor[5].data ; /* 24 Sep 2001 */ done_pb = (Widget) DRAW_actor[6].data ; /*** that's all ***/ XtManageChild(rowcol) ; XtRealizeWidget(shell) ; NI_sleep(1) ; /* will not be mapped */ return ; } /*------------------------------------------------------------------- Callback for copy_bbox -- 24 Sep 2001 - RWCox ---------------------------------------------------------------------*/ void DRAW_copy_bbox_CB( Widget w, XtPointer client_data, XtPointer call_data ) { int sens = (MCW_val_bbox(copy_bbox) != 0); AV_SENSITIZE( copy_mode_av , sens ) ; AV_SENSITIZE( copy_type_av , sens ) ; AV_SENSITIZE( copy_datum_av, sens ) ; if(sens) MCW_set_widget_label( choose_pb , " Choose dataset for copying" ); else MCW_set_widget_label( choose_pb , " Choose dataset to change directly" ); return ; } /* turn on or off copy dataset check box and related buttons */ static void Sensitize_copy_bbox(int sens) { XtPointer clienttemp=NULL; SENSITIZE(copy_bbox->wbut[0], sens); SENSITIZE(copy_bbox->wbut[1], sens); /* dummy pointers passed */ if(sens) DRAW_copy_bbox_CB(copy_bbox->wbut[0], clienttemp, clienttemp); else { AV_SENSITIZE( copy_mode_av , 0 ) ; AV_SENSITIZE( copy_type_av , 0 ) ; AV_SENSITIZE( copy_datum_av, 0 ) ; } } /*------------------------------------------------------------------- Callback for done button ---------------------------------------------------------------------*/ void DRAW_done_CB( Widget w, XtPointer client_data, XtPointer call_data ) { if( dset != NULL ){ if( recv_open ) /* 31 Mar 1999: changed shutdown to EVERYTHING */ AFNI_receive_control( im3d, recv_key,EVERYTHING_SHUTDOWN, NULL ) ; if( dset_changed ){ MCW_invert_widget( done_pb ) ; DRAW_attach_dtable( vl_dtable, "VALUE_LABEL_DTABLE", dset ) ; DSET_overwrite(dset) ; MCW_invert_widget( done_pb ) ; } DSET_unlock(dset) ; DSET_anyize(dset) ; dset = NULL ; dset_changed = 0 ; } CLEAR_UNREDOBUF ; /* 19 Nov 2003 */ XtUnmapWidget( shell ); editor_open = 0; recv_open = 0; recv_key = -1; if( old_stroke_autoplot ) putenv("AFNI_STROKE_AUTOPLOT=YES") ; return ; } /*------------------------------------------------------------------- Callback for undo button [heavily modified 19 Nov 2003] ---------------------------------------------------------------------*/ void DRAW_undo_CB( Widget w, XtPointer client_data, XtPointer call_data ) { dobuf *sb ; /* saved drawing buffer that will be redrawn */ if( undo_num <= 0 || undo_stack == NULL ){ XBell(dc->display,100); return; } undo_how = 1 ; /* the next drawing save will be onto redo stack */ sb = undo_stack[undo_num-1] ; /* saved buffer */ DRAW_into_dataset( sb->npt , sb->xyz,NULL,NULL , sb->buf ) ; DESTROY_DOBUF(sb) ; /* purge and pop top of undo stack */ undo_num-- ; UNDO_button_labelize ; AFNI_process_drawnotice( im3d ) ; /* 30 Mar 1999 */ undo_how = 0 ; /* further draws go onto undo stack */ return ; } /*------------------------------------------------------------------- Callback for redo button [from DRAW_undo_CB(), 19 Nov 2003] ---------------------------------------------------------------------*/ void DRAW_redo_CB( Widget w, XtPointer client_data, XtPointer call_data ) { dobuf *sb ; if( redo_num <= 0 || redo_stack == NULL ){ XBell(dc->display,100); return; } undo_how = 2 ; /* drawing save will be onto undo stack */ sb = redo_stack[redo_num-1] ; /* saved buffer */ DRAW_into_dataset( sb->npt , sb->xyz,NULL,NULL , sb->buf ) ; DESTROY_DOBUF(sb) ; /* purge and pop top of redo stack */ redo_num-- ; REDO_button_labelize ; AFNI_process_drawnotice( im3d ) ; /* 30 Mar 1999 */ undo_how = 0 ; /* further draws go onto undo stack */ return ; } /*------------------------------------------------------------------- Callback for quit button ---------------------------------------------------------------------*/ void DRAW_quit_CB( Widget w, XtPointer client_data, XtPointer call_data ) { if( dset != NULL ){ if( recv_open ) AFNI_receive_control( im3d,recv_key,DRAWING_SHUTDOWN,NULL ); DSET_unlock(dset) ; DSET_unload(dset) ; DSET_anyize(dset) ; if( dset_changed ){ if( recv_open ){ AFNI_process_drawnotice( im3d ) ; /* 30 Mar 1999 */ AFNI_receive_control( im3d, recv_key,EVERYTHING_SHUTDOWN, NULL ) ; /* 25 Sep 2001 */ } MCW_invert_widget(quit_pb) ; THD_load_statistics( dset ) ; PLUTO_dset_redisplay( dset ) ; MCW_invert_widget(quit_pb) ; } dset = NULL ; dset_changed = 0 ; } CLEAR_UNREDOBUF ; /* 19 Nov 2003 */ XtUnmapWidget( shell ); editor_open = 0; recv_open = 0; recv_key = -1; if( old_stroke_autoplot ) putenv("AFNI_STROKE_AUTOPLOT=YES") ; return ; } /*------------------------------------------------------------------- Callback for Save button ---------------------------------------------------------------------*/ void DRAW_save_CB( Widget w, XtPointer client_data, XtPointer call_data ) { if( dset == NULL ){ XBell(dc->display,100) ; return ; } MCW_invert_widget(save_pb) ; DRAW_attach_dtable( vl_dtable, "VALUE_LABEL_DTABLE", dset ) ; DSET_overwrite(dset); dset_changed = 0; SENSITIZE(choose_pb,1); Sensitize_copy_bbox(1); /* turn copy dataset widgets back on - drg 4/4/2006 */ MCW_invert_widget(save_pb) ; SENSITIZE(save_pb,0) ; SENSITIZE(saveas_pb,0) ; return ; } /*------------------------------------------------------------------- 24 Sep 2001: Callback for Save As button ---------------------------------------------------------------------*/ void DRAW_saveas_CB( Widget w, XtPointer client_data, XtPointer call_data ) { if( dset == NULL ){ XBell(dc->display,100) ; return ; } MCW_choose_string( saveas_pb , "Enter new prefix" , NULL , DRAW_saveas_finalize_CB , NULL ) ; } /*--------------------------------------------------------------------*/ void DRAW_saveas_finalize_CB( Widget w, XtPointer fd, MCW_choose_cbs * cbs ) { THD_3dim_dataset *cset ; char str[256] ; XmString xstr ; /*-- check for craziness --*/ if( !editor_open || dset == NULL ){ POPDOWN_strlist_chooser; XBell(dc->display,100); return; } if( !PLUTO_prefix_ok(cbs->cval) ){ XBell(dc->display,100); return; } /*-- make a copy of this dataset --*/ MCW_invert_widget(saveas_pb) ; cset = DRAW_copy_dset( dset , 0,0,-1 ) ; if( cset == NULL ){ /* should not happen */ (void) MCW_popup_message( saveas_pb , " \n" "*** Cannot make copy of edited ***\n" "*** dataset for unknown reasons ***\n " , MCW_USER_KILL | MCW_TIMER_KILL ) ; MCW_invert_widget(saveas_pb); XBell(dc->display,100); return; } EDIT_dset_items( cset , ADN_prefix,cbs->cval , ADN_none ) ; if( THD_is_file(DSET_BRIKNAME(cset)) ){ /* stupid user */ (void) MCW_popup_message( saveas_pb , " \n" "*** Cannot SaveAs this edited ***\n" "*** dataset since a dataset ***\n" "*** with that prefix is on disk ***\n " , MCW_USER_KILL | MCW_TIMER_KILL ) ; DSET_delete(cset) ; MCW_invert_widget(saveas_pb); XBell(dc->display,100); return; } /*-- tell AFNI about the new dataset --*/ PLUTO_add_dset( plint , cset , DSET_ACTION_MAKE_CURRENT ) ; /*-- remove current dataset from further consideration --*/ DSET_unlock(dset) ; DSET_unload(dset) ; DSET_anyize(dset) ; /*-- switch current dataset to be the copy just made --*/ dset = cset ; dset_idc = dset->idcode ; DRAW_attach_dtable( vl_dtable, "VALUE_LABEL_DTABLE", dset ) ; DSET_overwrite(dset); DSET_mallocize(dset); DSET_load(dset); DSET_lock(dset); /*-- re-write the informational label --*/ if( DSET_BRICK_FACTOR(dset,0) == 0.0 ){ strcpy(str,DSET_BRIKNAME(dset)) ; } else { char abuf[16] ; AV_fval_to_char( DSET_BRICK_FACTOR(dset,0) , abuf ) ; sprintf(str,"%s\nbrick factor: %s", DSET_BRIKNAME(dset) , abuf ) ; } xstr = XmStringCreateLtoR( str , XmFONTLIST_DEFAULT_TAG ) ; XtVaSetValues( info_lab , XmNlabelString , xstr , BGCOLOR_ARG(GCOL) , NULL ) ; XmStringFree(xstr) ; /*-- finish up --*/ dset_changed = 0 ; SENSITIZE(choose_pb,1) ; MCW_invert_widget(saveas_pb) ; SENSITIZE(save_pb,0) ; SENSITIZE(saveas_pb,0) ; Sensitize_copy_bbox(1); return ; } /*------------------------------------------------------------------- Callback for Help button ---------------------------------------------------------------------*/ void DRAW_help_CB( Widget w, XtPointer client_data, XtPointer call_data ) { (void ) new_MCW_textwin( help_pb , "This plugin can be used to edit interactively the voxel contents\n" "of a dataset. Since such changes are irreversible, it is best that\n" "you either edit a copy of a dataset, or create a dataset of all zeros.\n" "These tasks can be done with the 'Dataset Copy' plugin.\n" "\n" "***************** Read the WARNINGS section below. *****************\n" "\n" "Step 1) Choose a dataset to edit.\n" " * Only datasets that have data BRIKs stored at the current\n" " resolution as the underlay dataset can be edited.\n" " * It is probably best that the dataset being edited be\n" " displayed. Otherwise it will be impossible to gauge\n" " the effect of the editing operations.\n" " * At this time, only datasets that have a single sub-brick\n" " can be edited.\n" " * Datasets may be copied when chosen by selecting the\n" " 'Copy' toggle (on by default). The choosers to the right\n" " of this let you control how the input dataset is copied:\n" " (a) Data => data value are copied\n" " Zero => copy is full of zeros\n" " (b) Show as Olay => put copy in Overlay (the usual case)\n" " Show as Ulay => put copy in Underlay\n" " (c) As Is => copy is stored as input dataset is stored\n" " Byte => copy is stored as bytes\n" " Short => copy is stored as shorts\n" " Float => copy is stored as floats\n" " NOTE: you can only change the data storage of the\n" " copy from the input if you are using 'Zero';\n" " with 'Data', the data storage of the copy will\n" " always be made 'As Is'.\n" " The copy is made when you finalize the dataset choice.\n" " The copied dataset has the same prefix as the input\n" " dataset, with the string 'COPY_' prepended. You can\n" " alter this name later using the 'Dataset Rename' plugin,\n" " AFTER the dataset has been Save-d, or with the '3drename'\n" " program after you exit AFNI, or with the 'SaveAs' button\n" " in this plugin.\n" " * Datasets may also be copied with the 'Dataset Copy' plugin.\n" " Making an empty dataset with a given geometry can be\n" " done using the 'Zero [One]' option in that plugin.\n" "\n" "Step 2) Choose a drawing value.\n" " * This is the number that will be placed into the dataset\n" " voxels that are chosen.\n" " * Integer valued datasets can only receive integer values;\n" " float datasets can take floating point values.\n" " * You can attach a label string to each drawing value.\n" " The value-label table will be saved with in the dataset\n" " .HEAD file when you use 'Save', 'SaveAs' or 'Done'.\n" " * You can also setup a standard value-label table in a file,\n" " whose name is specified by setting environment variable\n" " AFNI_VALUE_LABEL_DTABLE -- cf. file README.environment.\n" " * Button-3-clicking in the 'Label' next to the text-entry field\n" " will bring up a menu of all current value-label pairs.\n" " You can choose from them to set a new drawing value.\n" " * Button-1-clicking in the 'Label' will ask for a filename\n" " to read that loads the value-label pairs. The format\n" " of this file is described in README.environment.\n" " Reading in a file like this will erase any existing\n" " value-label associations in the plugin.\n" "\n" "Step 3) Choose a drawing color.\n" " * This is the color that will be shown in the image windows\n" " while drawing is going on (that is, while the mouse button\n" " is pressed).\n" " * This color is NOT the color that will be displayed once\n" " you stop drawing (release the mouse button).\n" " * See 5) for more details about the drawing process.\n" "\n" "Step 4) Choose a drawing mode.\n" " * 'Open Curve' means to select dataset voxels that lie under\n" " the pixel lines drawn on the image as you move the mouse\n" " with button 2 held down.\n" " * 'Closed Curve' means to close the curve drawn from the last\n" " point drawn (where button 2 is released) back to the\n" " first point drawn (where button 2 was pressed).\n" " * 'Points' means to take only the voxels corresponding\n" " to the screen pixels about which X11 sends notice.\n" " * 'Flood->Value' means to flood fill outwards from the first\n" " chosen voxel, stopping when the Dataset Value is reached.\n" " In conjunction with 'Closed Curve', it can be used to draw\n" " an entire region in a plane.\n" " * 'Flood->Nonzero' means to flood fill, but stopping when any\n" " nonzero voxel value is reached.\n" " * 'Flood->Zero' means to flood fill, but stopping when any\n" " zero voxel value is reached.\n" " * 'Zero->Value' means to flood fill the slice with zeros,\n" " stopping when a voxel with the drawing value is reached.\n" " * 'Flood->Val/Zero' means to flood fill the slice with the\n" " Value until voxels whose values are either zero or the\n" " Value are hit\n" " * 'Filled Curve' means to draw a closed curve and then fill\n" " its interior with the drawing value. It is similar to\n" " doing 'Closed Curve' followed by 'Flood->Value', but\n" " more convenient.\n" " * '2D Nbhd: Kth NN' is like 'Open Curve', but each the 2D in-slice\n" " neighborhood of a point 'x' is filled in with the following\n" " pattern of points, for K=1..5:\n" " 5 4 3 4 5\n" " 4 2 1 2 4\n" " 3 1 x 1 3\n" " 4 2 1 2 4\n" " 5 4 3 4 5\n" " In a cubical lattice with voxel edge length=1, the 2D Kth NN\n" " volume is a 'circle' out to radius:\n" " K=1 r=sqrt(1) [e.g., (+1, 0)]\n" " K=2 r=sqrt(2) [e.g., (+1,+1) => 3x3 square]\n" " K=3 r=sqrt(4) [e.g., (+2, 0)]\n" " K=4 r=sqrt(5) [e.g., (+2,+1)]\n" " K=5 r=sqrt(8) [the whole 5x5 square about 'x']\n" " * '3D Nbhd: Kth NN' is similar, but with the 3D neighborhood\n" " of each point (so you are drawing out-of-slice). In this\n" " case, the 3D Kth NN volume is a 'sphere' out to radius\n" " K=1 r=sqrt(1) [e.g., (+1, 0, 0)]\n" " K=2 r=sqrt(2) [e.g., (+1,+1, 0)]\n" " K=3 r=sqrt(3) [e.g., (+1,+1,+1) => 3x3x3 cube]\n" " K=4 r=sqrt(4) [e.g., (+2, 0, 0)]\n" " K=5 r=sqrt(5) [e.g., (+2,+1, 0)]\n" " K=6 r=sqrt(6) [e.g., (+2,+1,+1)]\n" " 5x5x5 fills out the 5x5x5 cube about each drawn point.\n" " * '2D Circle' and '3D Sphere' draw in-plane circles and 3D spheres\n" " about each drawn point 'x'. The radius (in mm) is set using\n" " the 'R' chooser that becomes active when one of these drawing\n" " modes is selected. These drawing modes use the actual voxel\n" " sizes in the dataset, unlike the 'Nbhd' modes described above.\n" "\n" "Step 5) Draw something in an image window.\n" " * Drawing is done using mouse button 2 (the middle button).\n" " * If you have a scroll-wheel for the middle button, it can\n" " be hard to use this for drawing. Two alternatives are:\n" " ** Mouse button 1 (the left button), with the keyboard\n" " Shift key held down simultaneously.\n" " ** Mouse button 1 by itself, if you first click the 'pen'\n" " toggle (at the image viewer's right) to be 'on'.\n" " (Mouse cursor should then change to a stylized pen.)\n" " * In an image window, drawing a set of pixels is done\n" " by pressing and holding button 2, and dragging\n" " the cursor across the desired pixels. The drawing\n" " color will be painted over these pixels while the\n" " painting is going on (while button 2 is held down).\n" " * After mouse button 2 is released, the drawing value for\n" " the chosen voxels is copied into the dataset. The\n" " dataset is then redisplayed -- this will most likely\n" " change the color of the selected voxels, since display\n" " colors depend on the Define Function pbar (for Func\n" " datasets) or on the grayscale map (for Anat datasets).\n" " * That is, the drawing color is ONLY used while button 2\n" " is pressed down. This color should simply be chosen\n" " to provide good contrast for the drawing operations.\n" " * Pressing and releasing button 2 in a graph window\n" " sub-graph will cause that single voxel to get the\n" " drawing value, as well. You cannot select a group\n" " of voxels in a graph window -- only one voxel per click.\n" " * Linear Fillin provides the same functionality as program\n" " 3dRowFillin. It lets you fill in gaps (zeros) between\n" " the same value in a particular anatomical direction.\n" " For example, you could draw on every 4th coronal slice,\n" " and then use Fill in the A-P direction with a maximum\n" " gap setting of 3 to fill in the slices you didn't draw.\n" " (Then you could manually fix up the intermediate slices.)\n" " * Atlas regions can be loaded from the current default atlas\n" " into the edited volume. The chosen region+hemisphere(s)\n" " will be loaded with the current value.\n" " 'OverWrite' loading means that all voxels from\n" " the atlas region will be replaced with the Value.\n" " 'InFill' loading means that only voxels that are currently\n" " zero in the TT region will be replaced with the Value.\n" " N.B.: Atlas regions may not be good representations of\n" " any given subject's anatomy. You will probably\n" " want to edit the mask after doing the loading.\n" " This feature requires the presence of the atlas\n" " dataset (default is TTatlas+tlrc) in the plugin directory.\n" " It also requires that you be editing in +tlrc coordinates,\n" " or in +orig coordinates with a mapping to +tlrc\n" " coordinates having already been established.\n" " Unlike Linear Fillin, atlas drawing can be undone.\n" "\n" "Step 6) Undo and Redo.\n" " * The last drawing operation can be undone -- that is,\n" " pressing 'Undo' will restore the voxel values before\n" " the last button 2 press-release operation.\n" " * 'Redo' will undo the previous 'Undo'.\n" " * Multiple levels of Undo/Redo are available.\n" " * The amount of memory set aside for Undo/Redo operations\n" " is controlled by environment variable AFNI_DRAW_UNDOSIZE,\n" " which is in megabytes; its value defaults to 6.\n" " * The numbers (as in '[3]') on the Undo and Redo buttons\n" " indicate how many levels are available at any moment.\n" "\n" "Step 7) Save dataset (maybe).\n" " * While a dataset is being edited, it is locked into memory.\n" " * The edited values are saved to disk only when 'Save' or\n" " 'Done' are pressed.\n" " * The 'Quit' button can be used to discard the edits of\n" " a dataset. In that case, the dataset values are\n" " re-read from disk when it is redisplayed.\n" " * Closing the AFNI Editor window using the window manager\n" " is equivalent to pressing 'Quit'.\n" " * The 'SaveAs' button lets you save the changes to a new\n" " dataset. The new dataset will become the current dataset\n" " for further editing and for AFNI display.\n" "\n" "++WARNINGS++:\n" " * It is important to understand the distinction between 'pixels'\n" " and 'voxels'. Pixels are on the screen, and while you are\n" " drawing, you are drawing pixels with the drawing color. When\n" " you release mouse button 2, those dataset voxels to which these\n" " pixels correspond are computed. The values stored in those\n" " voxels are then altered, and the dataset display is refreshed.\n" " * It is possible to draw on a montaged image window. However,\n" " only voxels from the first slice drawn into will be altered.\n" " * Using button 2 in an image or graph window before choosing a\n" " dataset to edit will cause the display to beep.\n" " * Closing the AFNI controller window that this was started from\n" " is the equivalent of pressing 'Quit'.\n" " * Doing something that causes the AFNI controller window to\n" " alter its 3D grid location or resolution is also the\n" " equivalent of pressing 'Quit'. This is because the 3D grid\n" " for the dataset being edited will no longer correspond to\n" " the 3D grid in the image and graph windows. Such actions\n" " include switching from 'View Brick' to 'Warp on Demand',\n" " switching datasets or sessions, and switching views.\n" " * You can only draw into the windows of the AFNI controller from\n" " which the Editor was started.\n" " * Only one copy of the Editor can be active at a time. If you\n" " use the plugin menu to call up the Editor when it is already\n" " open, that will simply pop the window up to the top of the\n" " stacking order. If you want to restart the Editor in a\n" " different AFNI controller, you must first close the Editor\n" " (via 'Done' or 'Quit') and then start it from the other\n" " controller's window.\n" " * Peculiar and confusing things can happen using 'Warp-on-Demand'\n" " with the Editor. My advice is NOT to try this.\n" " * Note that using a Session rescan button (from the 'Define Datamode'\n" " control panel) will close all datasets while rescanning the\n" " session. This can result in the loss of un-Saved edits.\n" " * It is possible to edit the same dataset that you are also viewing\n" " with the 'Render Dataset' plugin. In this way, you can see a\n" " 3D visualization of your drawing as you do it. You need to turn\n" " on 'DynaDraw' in the rendering plugin; then, if the dataset you\n" " are drawing on is the same as the renderer's overlay, each drawing\n" " action will cause a re-rendering. This works well if you have\n" " set the renderer's 'Color Opacity' to 'ShowThru'. This is also\n" " a lot of fun.\n" " * If you are drawing anatomically-based ROIs, you can only draw every\n" " 5th slice (say) and then use program 3dRowFillin to fill in the\n" " inter-slice gaps. Or use the Linear Fillin interactive feature.\n" " * Edit at your own risk! You can destroy datasets this way. That's\n" " why the 'Copy dataset' feature is on by default.\n" " * Be careful out there.\n" "\n" "SUGGESTIONS?\n" " * Please send them to " COXEMAIL "\n" " * Better than suggestions are implementations.\n" " * Better than implementations are pumpernickel bagels.\n" "Author -- RW Cox" , TEXT_READONLY ) ; return ; } /*------------------------------------------------------------------- Callback for choose button. Criteria for datasets that can be edited: - must be in current session - must have actual bricks - only datasets with nvals=1 can be edited - bricks must be on same grid (dataxes) as AFNI controller Much of this code is adapted from PLUG_choose_dataset_CB. ---------------------------------------------------------------------*/ static int ndsl = 0 ; static PLUGIN_dataset_link * dsl = NULL ; void DRAW_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 && dset_changed ){ (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 = GET_SESSION_DSET(ss,id,vv); /* qset = ss->dsset_xform_table[id][vv] ;*/ if( ! ISVALID_DSET (qset) ) continue ; /* skip */ if( ! DSET_INMEMORY(qset) ) continue ; if( DSET_NVALS(qset) > 1 ) continue ; if( ! EQUIV_DATAXES(qset->daxes,im3d->wod_daxes) ) 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 , " \n" "Didn't find any datasets to edit!\n" "Check if:\n" " - you are in 'Warp-on-Demand' mode\n" " - you are in the correct session\n" "Also:\n" " * Only datasets with 1 sub-brick can\n" " be edited.\n" " * The dataset must match the resolution\n" " of the current anatomical view.\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 , DRAW_finalize_dset_CB , NULL ) ; return ; } /*-----------------------------------------------------------------------------*/ void DRAW_finalize_dset_CB( Widget w, XtPointer fd, MCW_choose_cbs *cbs ) { int id=cbs->ival , copied=0 ; THD_3dim_dataset * qset ; XmString xstr ; char str[256] , *dtit ; THD_slist_find slf ; /* 29 Jul 2003 */ MCW_choose_cbs cbss ; /*-- check for errors --*/ if( !editor_open ){ POPDOWN_strlist_chooser; XBell(dc->display,100); return; } if( dset != NULL && dset_changed ){ 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 ; } if( ! EQUIV_DATAXES( im3d->wod_daxes , qset->daxes ) ){ XBell(dc->display,100) ; return ; } /*-- 24 Sep 2001: make a copy of the dataset, if desired --*/ if( MCW_val_bbox(copy_bbox) != 0 ){ THD_3dim_dataset *cset ; int zfill , ftype , dtype ; zfill = (copy_mode_av->ival == 1) ; /* zero fill? */ switch( copy_type_av->ival ){ default: ftype = -1 ; break ; /* As Is - should never get here */ case 0: ftype = 1 ; break ; /* Func */ case 1: ftype = 2 ; break ; /* Anat */ } switch( copy_datum_av->ival ){ default: dtype = -1 ; break ; /* As Is */ case 1: dtype = MRI_byte ; break ; /* Byte */ case 2: dtype = MRI_short ; break ; /* Short */ case 3: dtype = MRI_float ; break ; /* Float */ } cset = DRAW_copy_dset( qset , zfill,ftype,dtype ) ; /* make copy! */ if( cset == NULL ){ /* this is bad */ (void) MCW_popup_message( choose_pb , " \n" "*** Cannot make copy of input ***\n" "*** dataset for unknown reasons ***\n " , MCW_USER_KILL ) ; XBell(dc->display,100) ; return ; } DSET_unload(qset) ; PLUTO_add_dset( plint , cset , DSET_ACTION_MAKE_CURRENT ) ; qset = cset ; copied = 1 ; } /*-- accept this dataset --*/ dset = qset ; dset_changed = 0 ; dax_save = *(dset->daxes) ; dset_idc = dset->idcode ; /* 31 Mar 1999 */ SENSITIZE(save_pb,0) ; SENSITIZE(saveas_pb,0) ; Sensitize_copy_bbox(0); /*-- write the informational label --*/ if( copied ) dtit = DSET_BRIKNAME(dset) ; /* 24 Sep 2001 */ else dtit = dsl[id].title ; if( DSET_BRICK_FACTOR(dset,0) == 0.0 ){ strcpy(str,dtit) ; } else { char abuf[16] ; AV_fval_to_char( DSET_BRICK_FACTOR(dset,0) , abuf ) ; sprintf(str,"%s\nbrick factor: %s", dtit , abuf ) ; } xstr = XmStringCreateLtoR( str , XmFONTLIST_DEFAULT_TAG ) ; XtVaSetValues( info_lab , XmNlabelString , xstr , BGCOLOR_ARG(GCOL) , NULL ) ; XmStringFree(xstr) ; /*-- setup AFNI for drawing --*/ if( ! recv_open ){ recv_key = id = AFNI_receive_init( im3d, RECEIVE_DRAWING_MASK | RECEIVE_DSETCHANGE_MASK , /* 31 Mar 1999 */ DRAW_receiver,NULL , "DRAW_receiver" ) ; if( id < 0 ){ (void) MCW_popup_message( im3d->vwid->top_shell , "Unable to establish\n" "connection to AFNI\n" "drawing routines!" , MCW_USER_KILL | MCW_TIMER_KILL ) ; dset = NULL ; XBell(dc->display,100) ; return ; } } DSET_mallocize(dset) ; DSET_lock(dset) ; DSET_load(dset) ; AFNI_receive_control( im3d, recv_key,mode_index , NULL ) ; AFNI_receive_control( im3d, recv_key,DRAWING_OVCINDEX, ITOP(color_index) ) ; recv_open = 1 ; CLEAR_UNREDOBUF ; /* 19 Nov 2003 */ /* 29 Jul 2003: switch to this dataset */ slf = THD_dset_in_session( FIND_IDCODE , &(dset->idcode) , im3d->ss_now ) ; if( slf.dset_index >= 0 ){ cbss.ival = slf.dset_index ; if( ISFUNC(dset) ){ AFNI_finalize_dataset_CB( im3d->vwid->view->choose_func_pb , (XtPointer) im3d , &cbss ) ; AFNI_SEE_FUNC_ON(im3d) ; /* 30 Apr 2002 */ } else { AFNI_finalize_dataset_CB( im3d->vwid->view->choose_anat_pb , (XtPointer) im3d , &cbss ) ; } } /* 20 Oct 2003: get VALUE_LABEL_DTABLE, if present */ if( vl_dtable != NULL ){ destroy_Dtable(vl_dtable); vl_dtable = NULL; } { ATR_string *atr ; atr = THD_find_string_atr( dset->dblk , "VALUE_LABEL_DTABLE" ) ; if( atr != NULL && atr->nch > 5 ) vl_dtable = Dtable_from_nimlstring( atr->ch ) ; if( vl_dtable == NULL ){ char *str = AFNI_suck_file( getenv("AFNI_VALUE_LABEL_DTABLE") ) ; if( str != NULL ){ vl_dtable = Dtable_from_nimlstring(str); free(str); } } DRAW_set_value_label() ; } return ; } /*------------------------------------------------------------------- Callback for color menu ---------------------------------------------------------------------*/ void DRAW_color_CB( MCW_arrowval * av , XtPointer cd ) { color_index = av->ival ; if( dset != NULL && recv_open ) AFNI_receive_control( im3d, recv_key,DRAWING_OVCINDEX, ITOP(color_index) ) ; return ; } /*------------------------------------------------------------------- Callback for mode menu ---------------------------------------------------------------------*/ void DRAW_mode_CB( MCW_arrowval * av , XtPointer cd ) { mode_ival = av->ival ; mode_index = mode_ints[mode_ival] ; if( dset != NULL && recv_open ){ AFNI_receive_control( im3d, recv_key,mode_index , NULL ) ; /* 08 Oct 2002: set drawing line width */ AFNI_receive_control( im3d, recv_key, DRAWING_LINEWIDTH , ITOP(mode_width[mode_ival]) ) ; } /* 16 Oct 2002: turn rad_av (radius) on if mode needs it */ ENABLE_rad_av ; return ; } /*------------------------------------------------------------------- Callback for value menu ---------------------------------------------------------------------*/ void DRAW_value_CB( MCW_arrowval * av , XtPointer cd ) { value_int = av->ival ; value_float = av->fval ; if( value_float != 0.0 ){ XtSetSensitive( label_label , True ) ; XtSetSensitive( label_textf , True ) ; if(Check_value()) { /* value_float = 1.0; av->ival = 1; av->fval = 1.0; */ XtSetSensitive( label_label , False ) ; XtSetSensitive( label_textf , False ) ; } } else { XtSetSensitive( label_label , False ) ; XtSetSensitive( label_textf , False ) ; } DRAW_set_value_label() ; return ; } /* check if the value in the value field will be changed once it gets applied to the dataset */ /* error message is popped up if data value can not be applied*/ /* also returns 0 for data okay, 1 if can not use the data value */ static int Check_value(void) { int ityp; float bfac; float value_float2, delta; /* sanity check */ if( dset==NULL) { (void) MCW_popup_message( label_textf , \ "Please choose dataset first\n", \ MCW_USER_KILL | MCW_TIMER_KILL) ; PLUTO_beep() ; return(1) ; } ityp = DSET_BRICK_TYPE(dset,0) ; bfac = DSET_BRICK_FACTOR(dset,0) ; if( bfac == 0.0 ) bfac = 1.0 ; switch( ityp ){ default: fprintf(stderr,"Illegal brick type=%s in AFNI Editor!\n", MRI_TYPE_name[ityp] ) ; return(0); break ; case MRI_short:{ short val = (short) (value_float/bfac) ; value_float2 = val * bfac; } break ; case MRI_byte:{ byte val = (byte) (value_float/bfac) ; value_float2 = val * bfac; } break ; case MRI_float:{ float val = (value_float/bfac) ; value_float2 = val * bfac; } break ; case MRI_complex:{ complex val ; /*static complex cxzero = { 0.0 , 0.0 } ;*/ val = CMPLX( (value_float/bfac) , 0.0 ) ; return(0); /* assume everything is okay for complex for now */ } break ; } /* end of switch on brick type */ delta = fabs(value_float2 - value_float); if(delta>0.000001) { (void) MCW_popup_message( label_textf , \ "**************************************************************\n" \ "This dataset type does not accept this value in this plug-in\n" \ "Use 3D Edit plug-in, 3dcalc or 3dmerge to copy the dataset\n" \ "to a new datum type.\n" "**************************************************************",\ MCW_USER_KILL | MCW_TIMER_KILL) ; PLUTO_beep() ; AV_assign_fval( value_av , value_float2 ) ; value_int = value_av->ival ; value_float = value_av->fval ; DRAW_set_value_label(); /* reset value and label field to previous entry */ return(1); } return(0); } /*--------------------------------------------------------------------- Callbacks and functions for value label and text field [15 Oct 2003] -----------------------------------------------------------------------*/ static void dump_vallab(void) { #if 0 char str[256]={""}; char *cstr; int i; if( dset == NULL ) return ; for(i=1;i<=3;i++){ AFNI_get_dset_val_label(dset, i, str); printf("roi %d: %s\n", i,str); } cstr = Dtable_to_nimlstring( vl_dtable , "VALUE_LABEL_DTABLE" ) ; if( cstr != NULL ){ printf("%s\n",cstr); free(cstr); } #endif return ; } /*---------------------------------------------------------------------*/ char * DRAW_value_string( float val ) /* returns a fixed pointer! */ { static char str[32] ; sprintf(str,"%.5g",val) ; return str ; } /*---------------------------------------------------------------------*/ void DRAW_set_value_label(void) { if( vl_dtable == NULL || value_float == 0.0 ){ XmTextFieldSetString( label_textf , "" ) ; } else { char *str_val = DRAW_value_string( value_float ) ; char *str_lab = findin_Dtable_a( str_val , vl_dtable ) ; XmTextFieldSetString( label_textf , (str_lab != NULL) ? str_lab : "" ) ; } return ; } /*---------------------------------------------------------------------*/ void DRAW_label_CB( Widget wtex , XtPointer cld, XtPointer cad ) { char *str_val , *str_lab , *str_old ; int ll , ii ; /* get string from text field, see if it is empty or ends in blanks */ str_lab = XmTextFieldGetString( label_textf ) ; if( str_lab == NULL ){ if( vl_dtable == NULL ) return ; /* do nothing */ } else { ll = strlen(str_lab) ; for( ii=ll-1 ; ii >= 0 && isspace(str_lab[ii]) ; ii-- ) ; /* nada */ if( ii < 0 ){ /*-- all blanks */ if( vl_dtable == NULL ) return ; /* do nothing */ free(str_lab) ; str_lab = NULL ; /* otherwise, clobber entry */ } else if( ii < ll-1 ){ /*-- ends in blanks */ str_lab[ii+1] = '\0' ; /* so truncate them */ } } /* create (value,label) pair Dtable -- NULL label ==> erase old label */ if( vl_dtable == NULL ) vl_dtable = new_Dtable(7) ; str_val = DRAW_value_string( value_float ) ; /* value string */ /* check if old label for this value is same as new label; if it is, then don't need to do anything */ str_old = findin_Dtable_a( str_val , vl_dtable ) ; if( str_old != NULL ){ if( str_lab != NULL && strcmp(str_old,str_lab) == 0 ){ /* same as old */ free(str_lab) ; return ; } else if( str_lab == NULL ){ /* erase the old one */ removefrom_Dtable_a( str_val , vl_dtable ) ; dump_vallab() ; return ; } } if( str_lab == NULL ) return ; /* is NULL ==> nothing to do here */ /* check if new label is already in the table under a different value */ str_old = findin_Dtable_b( str_lab , vl_dtable ) ; if( str_old != NULL && strcmp(str_old,str_val) != 0 ){ char msg[1024] ; sprintf(msg," \n" " *********************************** \n" " ** ERROR * ERROR * ERROR * ERROR ** \n" " **\n" " ** Label = %s\n" " ** is already associated with\n" " ** Value = %s\n" " **\n" " ** Value,Label pairs must be unique \n" " *********************************** \n" , str_lab , str_old ) ; /* changed popup to disappear with timer to make it easier to continue */ (void) MCW_popup_message( label_textf , msg , MCW_USER_KILL | MCW_TIMER_KILL) ; PLUTO_beep() ; DRAW_set_value_label(); free(str_lab) ; return ; } /* add new value,label pair to Dtable (will clobber old one, if present) */ addto_Dtable( str_val , str_lab , vl_dtable ) ; free(str_lab) ; DRAW_attach_dtable( vl_dtable, "VALUE_LABEL_DTABLE", dset ) ; dset_changed = 1; dump_vallab() ; return ; } /*--------------------------------------------------------------------------*/ static char **vl_strlist=NULL ; static int vl_nstrlist=0 ; static void DRAW_label_finalize( Widget w, XtPointer cd, MCW_choose_cbs *cbs ) { int ival = cbs->ival , nn ; float val=0.0 ; if( !editor_open ){ PLUTO_beep(); POPDOWN_strlist_chooser; return; } nn = sscanf( vl_strlist[ival] , "%f" , &val ) ; if( nn == 0 || val == 0.0 ) return ; AV_assign_fval( value_av , val ) ; value_int = value_av->ival ; value_float = value_av->fval ; DRAW_set_value_label() ; return ; } /*---------------------------------------------------------------------*/ static void DRAW_label_getfile( Widget w, XtPointer cd, MCW_choose_cbs *cbs ) { char *str ; if( !editor_open ){ PLUTO_beep(); POPDOWN_string_chooser; return; } str = AFNI_suck_file( cbs->cval ) ; if( str != NULL ){ if( vl_dtable != NULL ) destroy_Dtable(vl_dtable) ; vl_dtable = Dtable_from_nimlstring(str) ; DRAW_set_value_label() ; } else { PLUTO_beep() ; } return ; } /*---------------------------------------------------------------------*/ void DRAW_label_EV( Widget w , XtPointer cld , XEvent *ev , RwcBoolean *continue_to_dispatch ) { /* handle leave event in text field */ if( w == label_textf ){ XmAnyCallbackStruct cbs ; XLeaveWindowEvent *lev = (XLeaveWindowEvent *) ev ; if( lev->type != LeaveNotify ) return ; cbs.reason = XmCR_ACTIVATE ; /* simulate a return press */ DRAW_label_CB( w , NULL , &cbs ) ; } /* handle Button-3 press in label */ else if( w == label_label ){ XButtonEvent *bev = (XButtonEvent *) ev ; int nn,ic,ll ; char **la, **lb ; float val ; if( bev->button == Button1 ){ MCW_choose_string( w , "Enter Value-Label filename:" , NULL , DRAW_label_getfile , NULL ) ; return ; } if( bev->button != Button3 ) return ; nn = listize_Dtable( vl_dtable , &la , &lb ) ; if( nn <= 0 || la == NULL || lb == NULL ) return ; /** get ready to popup a new list chooser **/ POPDOWN_strlist_chooser ; /** clear old strings **/ for( ic=0 ; ic < vl_nstrlist ; ic++ ) free(vl_strlist[ic]) ; /** make a list of value-label strings **/ vl_nstrlist = nn ; vl_strlist = (char **) realloc( vl_strlist , sizeof(char *)*vl_nstrlist ) ; for( nn=ic=0 ; ic < vl_nstrlist ; ic++ ){ if( la[ic] != NULL && lb[ic] != NULL ){ /* should always be true */ ll = strlen(la[ic])+strlen(lb[ic])+8 ; vl_strlist[nn] = calloc(1,ll) ; sprintf( vl_strlist[nn] , "%s = %s" , la[ic],lb[ic] ) ; nn++ ; } } free(la); free(lb); if( nn == 0 ) return ; /* sort list for the user's convenience */ if( nn > 1 ){ int redo ; char *t ; BSort: for( redo=ic=0 ; ic < nn-1 ; ic++ ){ if( strcmp(vl_strlist[ic],vl_strlist[ic+1]) > 0 ){ t=vl_strlist[ic]; vl_strlist[ic]=vl_strlist[ic+1]; vl_strlist[ic+1]=t; redo = 1 ; } } if( redo ) goto BSort ; } /* find current value in list, if any */ for( ic=0 ; ic < nn ; ic++ ){ sscanf( vl_strlist[ic] , "%f" , &val ) ; if( val == value_float ) break ; } if( ic == nn ) ic = -1 ; /* let the user choose one */ MCW_choose_strlist( w , "Value = Label" , nn , ic , vl_strlist , DRAW_label_finalize , NULL ) ; } return ; } /*---------------------------------------------------------------------*/ void DRAW_attach_dtable( Dtable *dt, char *atname, THD_3dim_dataset *ds ) { char *str ; if( dt == NULL || atname == NULL || ds == NULL ) return ; if (ds->Label_Dtable) { destroy_Dtable(ds->Label_Dtable); ds->Label_Dtable=NULL; } str = Dtable_to_nimlstring( dt , atname ) ; if( str == NULL ) return ; THD_set_string_atr( ds->dblk , atname , str ) ; free(str) ; return ; } /******************************************************************* Receive data from AFNI after drawing, etc. ********************************************************************/ void DRAW_receiver( int why , int np , void * vp , void * cbd ) { ENTRY("DRAW_receiver") ; switch( why ){ default: fprintf(stderr,"DRAW_receiver: illegal why=%d\n",why) ; EXRETURN ; /*-- we like this one --*/ case RECEIVE_POINTS:{ int **ip = (int **)vp ; int *xd=ip[0] , *yd=ip[1] , *zd=ip[2] ; /* pts coords */ int mode=ip[3][0] ; /* how pts are organized */ int plane ; /*-- 20 Feb 2003: undo via keypress --*/ if( mode == UNDO_MODE ){ if( undo_num > 0 ) DRAW_undo_CB( undo_pb,NULL,NULL ) ; else XBell(dc->display,100) ; EXRETURN ; } if( mode == INCVAL_MODE || mode == DECVAL_MODE ){ /* 13 Sep 2008 */ float nval ; nval = value_float + ((mode==INCVAL_MODE) ? (1.0f) : (-1.0f)) ; AV_assign_fval( value_av , nval ) ; value_int = value_av->ival ; value_float = value_av->fval ; DRAW_set_value_label() ; EXRETURN ; } /*-- Did we get points? --*/ if( np <= 0 ) EXRETURN ; plane = mode - SINGLE_MODE ; if( plane < 1 || plane > 3 ) plane = mode - PLANAR_MODE ; if( plane < 1 || plane > 3 ) plane = 0 ; /* anything but flood mode --> just draw given points */ if( plane == 0 || ((mode_ival != MODE_FLOOD_VAL ) && (mode_ival != MODE_FLOOD_NZ ) && (mode_ival != MODE_FLOOD_ZERO) && (mode_ival != MODE_ZERO_VAL ) && (mode_ival != MODE_FLOOD_VZ ) && (mode_ival != MODE_FILLED )) ){ /* 07 Oct 2002: expand set of points using a mask? */ if( plane != 0 && mode_ival >= FIRST_2D_MODE && mode_ival <= LAST_2D_MODE ){ int nfill=0, *xyzf=NULL ; DRAW_2D_expand( np , xd,yd,zd , plane , &nfill , &xyzf ) ; if( nfill > 0 && xyzf != NULL ){ DRAW_into_dataset( nfill , xyzf,NULL,NULL , NULL ) ; free(xyzf) ; } } else if( plane != 0 && mode_ival >= FIRST_3D_MODE && mode_ival <= LAST_3D_MODE ){ int nfill=0, *xyzf=NULL ; DRAW_3D_expand( np , xd,yd,zd , plane , &nfill , &xyzf ) ; if( nfill > 0 && xyzf != NULL ){ DRAW_into_dataset( nfill , xyzf,NULL,NULL , NULL ) ; free(xyzf) ; } /* 16 Oct 2002: expand geometrically (circle or sphere)? */ } else if( plane != 0 && mode_ival >= FIRST_RAD_MODE && mode_ival <= LAST_RAD_MODE ){ int nfill=0, *xyzf=NULL ; switch( mode_ival ){ case MODE_2D_CIRC: DRAW_2D_circle( np , xd,yd,zd , plane , &nfill , &xyzf ) ; break ; case MODE_3D_SPHR: DRAW_3D_sphere( np , xd,yd,zd , plane , &nfill , &xyzf ) ; break ; } if( nfill > 0 && xyzf != NULL ){ DRAW_into_dataset( nfill , xyzf,NULL,NULL , NULL ) ; free(xyzf) ; } else { DRAW_into_dataset( np , xd,yd,zd , NULL ) ; /* should never happen */ } } else { /* the old way: */ DRAW_into_dataset( np , xd,yd,zd , NULL ) ; /* just draw points */ } } else { /* flood mode! */ int ityp = DSET_BRICK_TYPE(dset,0) ; float bfac = DSET_BRICK_FACTOR(dset,0) ; int nx=DSET_NX(dset) , ny=DSET_NY(dset) , nz=DSET_NZ(dset) , nxy = nx*ny , ii,jj , ixyz ; int base=0 , di=0,dj=0 , itop=0,jtop=0,nij , xx=xd[0],yy=yd[0],zz=zd[0] , ix=0,jy=0 ; byte * pl ; int nfill , *xyzf , nf ; /* compute stuff for which plane we are in: 1 -> yz , 2 -> xz , 3 -> xy */ switch(plane){ case 1: base=xx ; di=nx; dj=nxy; itop=ny; jtop=nz; ix=yy; jy=zz; break; case 2: base=yy*nx ; di=1 ; dj=nxy; itop=nx; jtop=nz; ix=xx; jy=zz; break; case 3: base=zz*nxy; di=1 ; dj=nx ; itop=nx; jtop=ny; ix=xx; jy=yy; break; } /* create a 2D array with 0 where dataset != blocking value and with 1 where dataset == blocking value */ nij = itop*jtop ; pl = (byte *) calloc( nij , sizeof(byte) ) ; if( mode_ival != MODE_FILLED ){ /* old code: flood to a dataset value */ if( bfac == 0.0 ) bfac = 1.0 ; switch(ityp){ case MRI_short:{ short * bp = (short *) DSET_BRICK_ARRAY(dset,0) ; short val = (short) (value_float/bfac) ; if( mode_ival == MODE_FLOOD_ZERO ) val = 0 ; if( mode_ival == MODE_FLOOD_VAL || mode_ival == MODE_FLOOD_ZERO || mode_ival == MODE_ZERO_VAL ){ for( jj=0 ; jj < jtop ; jj++ ) for( ii=0 ; ii < itop ; ii++ ){ ixyz = base + ii*di + jj*dj ; if( bp[ixyz] == val ) pl[ii+jj*itop] = 1 ; } } else if( mode_ival == MODE_FLOOD_VZ ){ /* 30 Apr 2002 */ for( jj=0 ; jj < jtop ; jj++ ) for( ii=0 ; ii < itop ; ii++ ){ ixyz = base + ii*di + jj*dj ; if( bp[ixyz] == val || bp[ixyz] == 0 ) pl[ii+jj*itop] = 1 ; } } else { for( jj=0 ; jj < jtop ; jj++ ) for( ii=0 ; ii < itop ; ii++ ){ ixyz = base + ii*di + jj*dj ; if( bp[ixyz] != 0 ) pl[ii+jj*itop] = 1 ; } } } break ; case MRI_byte:{ byte * bp = (byte *) DSET_BRICK_ARRAY(dset,0) ; byte val = (byte) (value_float/bfac) ; if( mode_ival == MODE_FLOOD_ZERO ) val = 0 ; if( mode_ival == MODE_FLOOD_VAL || mode_ival == MODE_FLOOD_ZERO || mode_ival == MODE_ZERO_VAL ){ for( jj=0 ; jj < jtop ; jj++ ) for( ii=0 ; ii < itop ; ii++ ){ ixyz = base + ii*di + jj*dj ; if( bp[ixyz] == val ) pl[ii+jj*itop] = 1 ; } } else if( mode_ival == MODE_FLOOD_VZ ){ /* 30 Apr 2002 */ for( jj=0 ; jj < jtop ; jj++ ) for( ii=0 ; ii < itop ; ii++ ){ ixyz = base + ii*di + jj*dj ; if( bp[ixyz] == val || bp[ixyz] == 0 ) pl[ii+jj*itop] = 1 ; } } else { for( jj=0 ; jj < jtop ; jj++ ) for( ii=0 ; ii < itop ; ii++ ){ ixyz = base + ii*di + jj*dj ; if( bp[ixyz] != 0 ) pl[ii+jj*itop] = 1 ; } } } break ; case MRI_float:{ float * bp = (float *) DSET_BRICK_ARRAY(dset,0) ; float val = (value_float/bfac) ; if( mode_ival == MODE_FLOOD_ZERO ) val = 0 ; if( mode_ival == MODE_FLOOD_VAL || mode_ival == MODE_FLOOD_ZERO || mode_ival == MODE_ZERO_VAL ){ for( jj=0 ; jj < jtop ; jj++ ) for( ii=0 ; ii < itop ; ii++ ){ ixyz = base + ii*di + jj*dj ; if( bp[ixyz] == val ) pl[ii+jj*itop] = 1 ; } } else if( mode_ival == MODE_FLOOD_VZ ){ /* 30 Apr 2002 */ for( jj=0 ; jj < jtop ; jj++ ) for( ii=0 ; ii < itop ; ii++ ){ ixyz = base + ii*di + jj*dj ; if( bp[ixyz] == val || bp[ixyz] == 0 ) pl[ii+jj*itop] = 1 ; } } else { for( jj=0 ; jj < jtop ; jj++ ) for( ii=0 ; ii < itop ; ii++ ){ ixyz = base + ii*di + jj*dj ; if( bp[ixyz] != 0.0 ) pl[ii+jj*itop] = 1 ; } } } break ; default: free(pl) ; fprintf(stderr, "Flood not implemented for datasets of type %s\a\n", MRI_TYPE_name[ityp] ) ; EXRETURN ; } /* end of switch on type */ /* start point must be a 0 (can't fill from an edge) */ if( pl[ix+jy*itop] == 1 ){ free(pl) ; XBell(dc->display,100) ; EXRETURN ; } /* call a routine to fill the array */ DRAW_2dfiller( itop,jtop , ix,jy , pl ) ; /* all filled points are 2 --> these are the locations to draw */ nfill = 0 ; for( ii=0 ; ii < nij ; ii++ ) nfill += (pl[ii] == 2) ; if( nfill == 0 ){ free(pl) ; XBell(dc->display,100) ; EXRETURN ; } xyzf = (int *) malloc( sizeof(int) * nfill ) ; for( nf=0,jj=0 ; jj < jtop ; jj++ ){ for( ii=0 ; ii < itop ; ii++ ){ if( pl[ii+jj*itop] == 2 ) xyzf[nf++] = base + ii*di + jj*dj ; } } free(pl) ; if( mode_ival == MODE_ZERO_VAL ){ bfac = value_float; value_float = 0.0; } DRAW_into_dataset( nfill , xyzf,NULL,NULL , NULL ) ; if( mode_ival == MODE_ZERO_VAL ) value_float = bfac ; free(xyzf) ; } /*-- end of flood code --*/ else { /*-- 25 Sep 2001: fill the interior of the drawn curve --*/ int *iip=NULL , *jjp=NULL ; switch(plane){ /* select which */ case 1: iip = yd ; jjp = zd ; break ; /* arrays to draw */ case 2: iip = xd ; jjp = zd ; break ; /* curve from */ case 3: iip = xd ; jjp = yd ; break ; } for( ii=0 ; ii < np ; ii++ ){ /* draw curve into fill array */ pl[ iip[ii] + jjp[ii]*itop ] = 1 ; } /* now find an edge point that is not on the curve */ ix = -1 ; for( ii=0 ; ii < itop ; ii++ ){ if( pl[ii] == 0 ){ ix = ii; jy = 0 ; break; } if( pl[ii+(jtop-1)*itop] == 0 ){ ix = ii; jy = jtop-1; break; } } if( ix < 0 ){ for( jj=0 ; jj < jtop ; jj++ ){ if( pl[jj*itop] == 0 ){ ix = 0 ; jy = jj; break; } if( pl[(itop-1)+jj*itop] == 0 ){ ix = itop-1; jy = jj; break; } } } if( ix < 0 ){ /* should never happen */ free(pl) ; XBell(dc->display,100) ; EXRETURN ; } /* fill the array from the edge */ DRAW_2dfiller( itop,jtop , ix,jy , pl ) ; /* all filled points are 2 --> these are NOT the locations to draw */ nfill = 0 ; for( ii=0 ; ii < nij ; ii++ ) nfill += (pl[ii] != 2) ; if( nfill == 0 ){ free(pl) ; XBell(dc->display,100) ; EXRETURN ; } xyzf = (int *) malloc( sizeof(int) * nfill ) ; for( nf=0,jj=0 ; jj < jtop ; jj++ ){ for( ii=0 ; ii < itop ; ii++ ){ if( pl[ii+jj*itop] != 2 ) xyzf[nf++] = base + ii*di + jj*dj ; } } free(pl) ; DRAW_into_dataset( nfill , xyzf,NULL,NULL , NULL ) ; free(xyzf) ; } /* end of interior fill code */ } /* end of flooding or filling */ } /* end of dealing with drawn points */ break ; /*-- user closed the controller window!? (the fiend) */ case RECEIVE_CLOSURE:{ if( dset != NULL && dset_changed ) XBell(dc->display,100) ; /* protest */ DRAW_quit_CB(NULL,NULL,NULL) ; /* and die */ } break ; /*-- user altered the controller window!? */ case RECEIVE_ALTERATION:{ /* if we are already editing a dataset, then check if the grid has changed -- if it has, must quit */ if( dset != NULL ){ if( ! EQUIV_DATAXES( im3d->wod_daxes , &dax_save ) ){ XBell(dc->display,100) ; /* feeble protest */ DRAW_quit_CB(NULL,NULL,NULL) ; /* die */ /* less feeble protest */ ERROR_message( "Controller grid was altered!\n" " Editor was forced to quit!\n" " Any un-Saved changes were lost!" ) ; (void) MCW_popup_message( im3d->vwid->top_shell , "Controller grid was altered!\n" "Editor was forced to quit.\n" "Any un-Saved changes were lost." , MCW_USER_KILL | MCW_TIMER_KILL ) ; } } } break ; /*-- user changed dataset pointers on us? --*/ case RECEIVE_DSETCHANGE:{ /* 31 Mar 1999 */ if( dset != NULL ){ dset = PLUTO_find_dset( &dset_idc ) ; DSET_mallocize(dset) ; DSET_lock(dset) ; DSET_load(dset) ; if( dset_changed ){ THD_load_statistics( dset ) ; PLUTO_dset_redisplay( dset ) ; XBell(dc->display,100) ; (void) MCW_popup_message( im3d->vwid->top_shell , "********* WARNING *********\n" "* Session rescan may have *\n" "* caused loss of unsaved *\n" "* editing changes! *\n" "***************************" , MCW_USER_KILL | MCW_TIMER_KILL ) ; } } } break ; } /* end of switch on why */ EXRETURN ; /* unreachable */ } /*-------------------------------------------------------------------------- Routine to draw into a dataset. If yd & zd are NULL, then xd is used as the direct 3D array index, otherwise xd,yd,zd are used as the 3-index. If var == NULL, then the value_av is used, otherwise the array var[] will be the source of the data. ----------------------------------------------------------------------------*/ int DRAW_into_dataset( int np , int *xd , int *yd , int *zd , void *var ) { int ityp = DSET_BRICK_TYPE(dset,0) ; float bfac = DSET_BRICK_FACTOR(dset,0) ; int nx=DSET_NX(dset) , ny=DSET_NY(dset) , nz=DSET_NZ(dset) , nxy = nx*ny , nxyz = nxy*nz , ii , ixyz ; int ndrawn=0 ; dobuf *sb ; /* 19 Nov 2003: save buffer */ int *xyz ; /* sanity check */ if( dset==NULL || np <= 0 || xd==NULL ) return 0 ; /* make space for undo/redo (save old state in buffer) [19 Nov 2003] */ CREATE_DOBUF(sb,np,ityp) ; xyz = sb->xyz ; /* list of indexes to be altered */ /* compute (or copy) data index into save buffer */ if( yd == NULL ){ /* direct supply of 1-index */ memcpy(xyz,xd,sizeof(int)*np) ; } else { /* collapse 3-index into 1 */ for( ii=0 ; ii < np ; ii++ ) xyz[ii] = xd[ii] + yd[ii] * nx + zd[ii] * nxy ; } /* copy data into save buffer, based on type */ if( bfac == 0.0 ) bfac = 1.0 ; switch( ityp ){ default: fprintf(stderr,"Illegal brick type=%s in AFNI Editor!\n", MRI_TYPE_name[ityp] ) ; break ; #define DOIT (infill_mode==0 || bp[ixyz]==0) case MRI_short:{ short * bp = (short *) DSET_BRICK_ARRAY(dset,0) ; short * up = (short *) sb->buf ; short * vvv = (short *) var ; short val = (short) (value_float/bfac) ; for( ii=0 ; ii < np ; ii++ ){ /* save into buffer */ ixyz = xyz[ii] ; up[ii] = (ixyz >= 0 && ixyz < nxyz) ? bp[ixyz] : 0 ; } for( ii=0 ; ii < np ; ii++ ){ /* put into dataset */ ixyz = xyz[ii] ; if( ixyz >= 0 && ixyz < nxyz && DOIT ){ bp[ixyz] = (vvv==NULL) ? val : vvv[ii] ; ndrawn++ ; } } } break ; case MRI_byte:{ byte * bp = (byte *) DSET_BRICK_ARRAY(dset,0) ; byte * up = (byte *) sb->buf ; byte * vvv = (byte *) var ; byte val = (byte) (value_float/bfac) ; for( ii=0 ; ii < np ; ii++ ){ ixyz = xyz[ii] ; up[ii] = (ixyz >= 0 && ixyz < nxyz) ? bp[ixyz] : 0 ; } for( ii=0 ; ii < np ; ii++ ){ ixyz = xyz[ii] ; if( ixyz >= 0 && ixyz < nxyz && DOIT ){ bp[ixyz] = (vvv==NULL) ? val : vvv[ii] ; ndrawn++ ; } } } break ; case MRI_float:{ float * bp = (float *) DSET_BRICK_ARRAY(dset,0) ; float * up = (float *) sb->buf ; float * vvv = (float *) var ; float val = (value_float/bfac) ; for( ii=0 ; ii < np ; ii++ ){ ixyz = xyz[ii] ; up[ii] = (ixyz >= 0 && ixyz < nxyz) ? bp[ixyz] : 0.0 ; } for( ii=0 ; ii < np ; ii++ ){ ixyz = xyz[ii] ; if( ixyz >= 0 && ixyz < nxyz && DOIT ){ bp[ixyz] = (vvv==NULL) ? val : vvv[ii] ; ndrawn++ ; } } } break ; case MRI_complex:{ complex * bp = (complex *) DSET_BRICK_ARRAY(dset,0) ; complex * up = (complex *) sb->buf ; complex * vvv = (complex *) var ; complex val ; static complex cxzero = { 0.0 , 0.0 } ; val = CMPLX( (value_float/bfac) , 0.0 ) ; for( ii=0 ; ii < np ; ii++ ){ ixyz = xyz[ii] ; up[ii] = (ixyz >= 0 && ixyz < nxyz) ? bp[ixyz] : cxzero ; } for( ii=0 ; ii < np ; ii++ ){ ixyz = xyz[ii] ; if( ixyz >= 0 && ixyz < nxyz && (infill_mode==0 || bp[ixyz].r==0) ){ bp[ixyz] = (vvv==NULL) ? val : vvv[ii] ; ndrawn++ ; } } } break ; } /* end of switch on brick type */ /* recompute statistics */ THD_load_statistics( dset ) ; /* now redisplay dataset, in case anyone is looking at it */ PLUTO_dset_redisplay( dset ) ; dset_changed = 1 ; SENSITIZE(save_pb,1) ; SENSITIZE(saveas_pb,1) ; SENSITIZE(choose_pb,0) ; Sensitize_copy_bbox(0); /* save buffer pushed onto appropriate stack */ if( undo_how == 1 ){ /* save on redo stack */ redo_stack = realloc( (void *)redo_stack, sizeof(dobuf *)*(redo_num+1) ); redo_stack[redo_num++] = sb ; REDO_button_labelize ; } else { /* save on undo stack */ undo_stack = realloc( (void *)undo_stack, sizeof(dobuf *)*(undo_num+1) ); undo_stack[undo_num++] = sb ; UNDO_button_labelize ; DRAW_undo_sizecheck() ; if( undo_how == 0 ){ /* normal draw ==> can't redo */ CLEAR_REDOBUF ; } } return ndrawn ; } /*---------------------------------------------------------------------------*/ /*! Limit size of data allowed in undo buffers. [19 Nov 2003] -----------------------------------------------------------------------------*/ static void DRAW_undo_sizecheck(void) { int ii,jj , ss , lim=6 ; char *eee ; if( undo_num <= 1 ) return ; /* will always keep 1 level of undo */ /* get the limit of allowed mem usage for the undo buffers */ eee = getenv("AFNI_DRAW_UNDOSIZE") ; if( eee != NULL ){ ii = 0 ; sscanf(eee,"%d",&ii) ; if( ii > 0 ) lim = ii ; if( lim > 1024 ) lim = 1024 ; } lim *= (1024*1024) ; /* megabytes */ /* scan from top of stack, stopping when total size goes over the limit */ for( ss=0,ii=undo_num-1 ; ii >= 0 && ss < lim ; ii-- ) ss += SIZEOF_DOBUF( undo_stack[ii] ) ; if( ii <= 0 ) return ; /* didn't go over limit before bottom */ /* if here, stack elements from 0..ii-1 should be removed and the elements above them moved down to fill in */ for( jj=0 ; jj < ii ; jj++ ) /* removal */ DESTROY_DOBUF( undo_stack[jj] ) ; for( jj=ii ; jj < undo_num ; jj++ ) /* move-al */ undo_stack[jj-ii] = undo_stack[jj] ; undo_num = undo_num - ii ; return ; } /*---------------------------------------------------------------------------*/ /*! Set label of Undo or Redo button to reflect number of levels available, and set sensitivity while we are at it. [19 Nov 2003] -----------------------------------------------------------------------------*/ static void DRAW_undo_butlab( Widget w , int n ) { XmString xstr ; char label[32] ; int nfmt ; static char *fmt[3] = { "%s[%d]" , "%s:%d" , "%s%03d" } ; if( w == (Widget)NULL ) return ; /* oom-possible? */ if( n < 10 ) nfmt = 0 ; /* choose format based */ else if( n < 100 ) nfmt = 1 ; /* on number of digits */ else nfmt = 2 ; sprintf( label, fmt[nfmt], (w==undo_pb) ? "Undo" : "Redo" , n%1000 ) ; xstr = XmStringCreateLtoR( label , XmFONTLIST_DEFAULT_TAG ) ; XtVaSetValues( w , XmNlabelString , xstr , NULL ) ; XmStringFree(xstr) ; SENSITIZE( w , (n>0) ) ; return ; } /*--------------------------------------------------------------------------- Flood filling a byte array: nx = 1st dimension ny = 2nd dimension ix = start point jy = end point ar = array, with 0's everywhere except 1's as barriers to flooding All filled points (starting with ix,jy) will get the value 2. -----------------------------------------------------------------------------*/ void DRAW_2dfiller( int nx , int ny , int ix , int jy , byte * ar ) { int ii,jj , ip,jp , num ; #define AR(i,j) ar[(i)+(j)*nx] /* fill out in cross from 1st point */ ip = ix ; jp = jy ; AR(ip,jp) = 2 ; for( ii=ip+1; ii < nx && AR(ii,jp) == 0; ii++ ) AR(ii,jp) = 2; for( ii=ip-1; ii >= 0 && AR(ii,jp) == 0; ii-- ) AR(ii,jp) = 2; for( jj=jp+1; jj < ny && AR(ip,jj) == 0; jj++ ) AR(ip,jj) = 2; for( jj=jp-1; jj >= 0 && AR(ip,jj) == 0; jj-- ) AR(ip,jj) = 2; /* brute force repetition of the cross technique */ do { num = 0 ; for( jp=0 ; jp < ny ; jp++ ){ for( ip=0 ; ip < nx ; ip++ ){ if( AR(ip,jp) == 2 ){ for( ii=ip+1; ii < nx && AR(ii,jp) == 0; ii++ ){ AR(ii,jp) = 2; num++; } for( ii=ip-1; ii >= 0 && AR(ii,jp) == 0; ii-- ){ AR(ii,jp) = 2; num++; } for( jj=jp+1; jj < ny && AR(ip,jj) == 0; jj++ ){ AR(ip,jj) = 2; num++; } for( jj=jp-1; jj >= 0 && AR(ip,jj) == 0; jj-- ){ AR(ip,jj) = 2; num++; } } } } } while( num > 0 ) ; return ; } /*----------------------------------------------------------------------------------*/ void DRAW_fillin_CB( Widget w , XtPointer cd , XtPointer cb ) { int dcode=-1 , maxgap , nftot ; char dir ; MRI_IMAGE *bim , *tbim ; /* 21 Nov 2003: to allow undo of fillin */ /* check for errors */ if( !editor_open || dset == NULL ){ XBell(dc->display,100); return; } dir = fillin_dir_strings[ fillin_dir_av->ival ][0] ; if( dir == ORIENT_tinystr[dset->daxes->xxorient][0] || dir == ORIENT_tinystr[dset->daxes->xxorient][1] ) dcode = 1 ; if( dir == ORIENT_tinystr[dset->daxes->yyorient][0] || dir == ORIENT_tinystr[dset->daxes->yyorient][1] ) dcode = 2 ; if( dir == ORIENT_tinystr[dset->daxes->zzorient][0] || dir == ORIENT_tinystr[dset->daxes->zzorient][1] ) dcode = 3 ; if( dcode < 0 ){ XBell(dc->display,100) ; return ; } /* should not happen! */ #if 0 /* Need big gaps. ZSS Aug. 2011 */ maxgap = fillin_gap_av->ival ; #else maxgap = (int)fillin_gap_av->fval; #endif if( maxgap < 1 ){ XBell(dc->display,100) ; return ; } /* should not happen! */ bim = DSET_BRICK(dset,0) ; /* 21 Nov 2003: for undo */ tbim = mri_copy( bim ) ; /* copy brick before the change */ nftot = THD_dataset_rowfillin( dset , 0 , dcode , maxgap ) ; if( nftot > 0 ){ fprintf(stderr,"++ Fillin filled %d voxels\n",nftot) ; PLUTO_dset_redisplay( dset ) ; dset_changed = 1 ; SENSITIZE(save_pb,1) ; SENSITIZE(saveas_pb,1) ; if( recv_open ) AFNI_process_drawnotice( im3d ) ; { /* 21 Nov 2003: compute the undo stuff */ int ityp=bim->kind, ii,jj, nvox=bim->nvox, ndel=0 ; dobuf *sb=NULL ; switch( ityp ){ case MRI_short:{ short *bar = MRI_SHORT_PTR(bim), *tbar = MRI_SHORT_PTR(tbim), *up ; for( ii=0 ; ii < nvox ; ii++ ) if( bar[ii] != tbar[ii] ) ndel++ ; if( ndel > 0 ){ CREATE_DOBUF(sb,ndel,MRI_short) ; up = (short *)sb->buf ; for( ii=jj=0 ; ii < nvox ; ii++ ) if( bar[ii] != tbar[ii] ){ sb->xyz[jj]=ii; up[jj++]=tbar[ii]; } } } break ; case MRI_float:{ float *bar = MRI_FLOAT_PTR(bim), *tbar = MRI_FLOAT_PTR(tbim), *up ; for( ii=0 ; ii < nvox ; ii++ ) if( bar[ii] != tbar[ii] ) ndel++ ; if( ndel > 0 ){ CREATE_DOBUF(sb,ndel,MRI_float) ; up = (float *)sb->buf ; for( ii=jj=0 ; ii < nvox ; ii++ ) if( bar[ii] != tbar[ii] ){ sb->xyz[jj]=ii; up[jj++]=tbar[ii]; } } } break ; case MRI_byte:{ byte *bar = MRI_BYTE_PTR(bim), *tbar = MRI_BYTE_PTR(tbim), *up ; for( ii=0 ; ii < nvox ; ii++ ) if( bar[ii] != tbar[ii] ) ndel++ ; if( ndel > 0 ){ CREATE_DOBUF(sb,ndel,MRI_byte) ; up = (byte *)sb->buf ; for( ii=jj=0 ; ii < nvox ; ii++ ) if( bar[ii] != tbar[ii] ){ sb->xyz[jj]=ii; up[jj++]=tbar[ii]; } } } break ; } /* end of switch on brick type */ if( sb != NULL ){ /* if we created an undo buffer, push onto stack */ undo_stack = realloc( (void *)undo_stack, sizeof(dobuf *)*(undo_num+1) ); undo_stack[undo_num++] = sb ; UNDO_button_labelize ; DRAW_undo_sizecheck() ; CLEAR_REDOBUF ; /* can't redo after a drawing */ } } /* 21 Nov 2003: end of allowing for undo stuff */ } else if( nftot < 0 ) { fprintf(stderr,"** Fillin failed for some reason!\n") ; XBell(dc->display,100) ; } else { fprintf(stderr,"++ No Fillin voxels found\n") ; } mri_free(tbim) ; /* 21 Nov 2003: toss old copy */ return ; } /*-------------------------------------------------------------------------- 22 Aug 2001: TT Atlas Regions action callback ----------------------------------------------------------------------------*/ /******* Note any changes here should be reflected in both plug_drawdset.c:DRAW_ttatlas_CB() 3dfractionize.c:main() or updated to use the same function instead in the future.... This function doesn't really need to do this the 3dfractionize way; it can instead use the 3dresample or future warp-on-demand way. Resulting ROIs are different between this method and "Show Atlas Colors" */ void DRAW_ttatlas_CB( Widget w, XtPointer client_data, XtPointer call_data ) { THD_3dim_dataset *dseTT ; float *voxout ; byte *bb=NULL; short *ss=NULL, sval ; float *ff=NULL; MRI_IMAGE *b0im; int nvoxTT, nvoxout , iv,jv,kv , ijk ; int hbot,htop , nzTT,nyTT,nxTT,nxyTT , nxout,nyout,nzout,nxyout , i,j,k,ip,jp,kp , nftot ; float dxTT,dyTT,dzTT , xorgTT,yorgTT,zorgTT ; float dxout,dyout,dzout , xorgout,yorgout,zorgout ; float z1,z2 , y1,y2 , x1,x2 , xx1,xx2,yy1,yy2,zz1,zz2 ; float f1,f2,f , g1,g2,g , h1,h2,h , sx,sy,sz , tx,ty,tz , sxyz ; float vthresh; int at_sbi, fim_type, at_nsb; THD_fvec3 vv ; /* sanity checks */ if( !editor_open || dset == NULL ){ XBell(dc->display,100) ; return ; } if( !CAN_TALTO(im3d) ){ XBell(dc->display,100); return; } /* get default atlas dataset, maybe TTatlas+tlrc */ if (!(dseTT = TT_retrieve_atlas_dset(plugdraw_atlasname, 1))) { return; } DSET_load(dseTT) ; /* setup other info */ sval = ttatlas_list->reg_ttval [ ttatlas_region_av->ival ] ; nvoxTT= DSET_NVOX(dseTT) ; nxTT =dseTT->daxes->nxx ; nyTT =dseTT->daxes->nyy ; nzTT =dseTT->daxes->nzz ; dxTT =dseTT->daxes->xxdel; dyTT =dseTT->daxes->yydel; dzTT =dseTT->daxes->zzdel; xorgTT=dseTT->daxes->xxorg; yorgTT=dseTT->daxes->yyorg; zorgTT=dseTT->daxes->zzorg; nvoxout= DSET_NVOX(dset) ; voxout = (float *) calloc(sizeof(float),nvoxout) ; nxout =dset->daxes->nxx ; nyout =dset->daxes->nyy ; nzout =dset->daxes->nzz ; dxout =dset->daxes->xxdel; dyout =dset->daxes->yydel; dzout =dset->daxes->zzdel; xorgout=dset->daxes->xxorg; yorgout=dset->daxes->yyorg; zorgout=dset->daxes->zzorg; nxyout = nxout*nyout ; nxyTT = nxTT *nyTT ; switch( ttatlas_hemisphere_av->ival ){ case TTRR_HEMI_LEFT: hbot=1+nxTT/2 ; htop=nxTT ; break ; case TTRR_HEMI_RIGHT: hbot= 0 ; htop=1+nxTT/2 ; break ; default: case TTRR_HEMI_BOTH: hbot= 0 ; htop=nxTT ; break ; } /* loop over voxels in the default atlas dataset, transform to current dataset coordinates, count overlap (a la 3dfractionize) */ at_nsb = DSET_NVALS(dseTT); for( at_sbi=0; at_sbi < at_nsb; at_sbi++) { b0im = DSET_BRICK(dseTT, at_sbi); if( b0im == NULL ) return ; fim_type = b0im->kind ; switch( fim_type ){ default: return ; case MRI_byte: bb = MRI_BYTE_PTR(b0im); break ; case MRI_short: ss = MRI_SHORT_PTR(b0im); break ; case MRI_float: ff = MRI_FLOAT_PTR(b0im); break ; } for( kv=0 ; kv < nzTT ; kv++ ){ z1 = zorgTT + dzTT * (kv-0.5) ; z2 = zorgTT + dzTT * (kv+0.49999) ; for( jv=0 ; jv < nyTT ; jv++ ){ y1 = yorgTT + dyTT * (jv-0.5) ; y2 = yorgTT + dyTT * (jv+0.49999) ; for( iv=hbot ; iv < htop ; iv++ ){ ijk = iv + jv*nxTT + kv*nxyTT ; /* 1D index of voxel (iv,jv,kv) */ switch( fim_type ){ default: continue ; /* shouldn't ever get to this */ case MRI_byte: if( bb[ijk] != (byte) sval ) continue ; /* not the right value, so skip it */ break ; case MRI_short: if( ss[ijk] != sval ) continue ; /* not the right value, so skip it */ break ; case MRI_float: if( ff[ijk] != (float) sval ) continue ; /* not the right value, so skip it */ break ; } /* got here, so voxel found in atlas that matches region number */ x1 = xorgTT + dxTT * (iv-0.5) ; x2 = xorgTT + dxTT * (iv+0.49999) ; /* input voxel (iv,jv,kv) spans coordinates [x1,x2] X [y1,y2] X [z1,z2] */ /* transform these corner coordinates to output dataset grid coordinates */ if( dset->view_type == VIEW_TALAIRACH_TYPE ){ xx1 = x1 ; yy1 = y1 ; zz1 = z1 ; xx2 = x2 ; yy2 = y2 ; zz2 = z2 ; } else { LOAD_FVEC3(vv , x1,y1,z1) ; vv = AFNI_transform_vector( im3d->anat_dset[VIEW_TALAIRACH_TYPE] , vv , im3d->anat_now ) ; vv = THD_dicomm_to_3dmm( dset , vv ); UNLOAD_FVEC3(vv , xx1,yy1,zz1) ; LOAD_FVEC3(vv , x2,y2,z2) ; vv = AFNI_transform_vector( im3d->anat_dset[VIEW_TALAIRACH_TYPE] , vv , im3d->anat_now ) ; vv = THD_dicomm_to_3dmm( dset , vv ) ; UNLOAD_FVEC3(vv , xx2,yy2,zz2) ; } /* [xx1,xx2] X [yy1,yy2] X [zz1,zz2] is now in coordinates of output dataset */ /* compute indices into output dataset voxel (keeping fractions) */ f1 = (xx1-xorgout)/dxout + 0.49999 ; f2 = (xx2-xorgout)/dxout + 0.49999 ; if( f1 > f2 ){ tx = f1 ; f1 = f2 ; f2 = tx ; } if( f1 >= nxout || f2 <= 0.0 ) continue ; if( f1 < 0.0 ) f1 = 0.0 ; if( f2 >= nxout ) f2 = nxout - 0.001 ; g1 = (yy1-yorgout)/dyout + 0.49999 ; g2 = (yy2-yorgout)/dyout + 0.49999 ; if( g1 > g2 ){ ty = g1 ; g1 = g2 ; g2 = ty ; } if( g1 >= nyout || g2 <= 0.0 ) continue ; if( g1 < 0.0 ) g1 = 0.0 ; if( g2 >= nyout ) g2 = nyout - 0.001 ; h1 = (zz1-zorgout)/dzout + 0.49999 ; h2 = (zz2-zorgout)/dzout + 0.49999 ; if( h1 > h2 ){ tz = h1 ; h1 = h2 ; h2 = tz ; } if( h1 >= nzout || h2 <= 0.0 ) continue ; if( h1 < 0.0 ) h1 = 0.0 ; if( h2 >= nzout ) h2 = nzout - 0.001 ; /* input voxel covers voxels [f1,f2] X [g1,g2] X [h1,h2] in the output */ /* For example, [6.3,7.2] X [9.3,9.6] X [11.7,13.4], which must be */ /* distributed into these voxels: */ /* (6,9,11), (7,9,11), (6,9,12), (7,9,12), (6,9,13), and (7,9,13) */ for( f=f1 ; f < f2 ; f = ip ){ i = (int) f ; ip = i+1 ; tx = MIN(ip,f2) ; sx = tx - f ; for( g=g1 ; g < g2 ; g = jp ){ j = (int) g ; jp = j+1 ; ty = MIN(jp,g2) ; sy = ty - g ; for( h=h1 ; h < h2 ; h = kp ){ k = (int) h ; kp = k+1 ; tz = MIN(kp,h2) ; sz = tz - h ; sxyz = sx * sy * sz ; voxout[ i + j*nxout + k * nxyout ] += (float)(100.0*sxyz) ; } } } }}}} /* end of loop over voxels and sub-bricks */ /** at this point, voxout[ijk] stores how much overlap each output voxel has with an Atlas voxel which had the target value; now, count voxels with enough overlap, and store their indexes **/ vthresh = get_afni_thresh_percent(); for( nftot=ijk=0 ; ijk < nvoxout ; ijk++ ){ if(voxout[ijk] >= vthresh) nftot++ ; } /* now load results into dataset */ if( nftot > 0 ){ int *xd = (int *) malloc(sizeof(int)*nftot) , ff ; for( ff=ijk=0 ; ijk < nvoxout ; ijk++ ) if( voxout[ijk] >= vthresh ) xd[ff++] = ijk ; infill_mode = (strcmp(XtName(w),TTATLAS_infill_label) == 0) ; ff = DRAW_into_dataset( nftot , xd,NULL,NULL , NULL ) ; infill_mode = 0 ; free(xd) ; fprintf(stderr,"++ %d TT Atlas voxels drawn into dataset\n",ff) ; PLUTO_dset_redisplay( dset ) ; dset_changed = 1 ; SENSITIZE(save_pb,1) ; SENSITIZE(saveas_pb,1) ; if( recv_open ) AFNI_process_drawnotice( im3d ) ; } else { fprintf(stderr,"++ No atlas voxels found at all for some reason!?\a\n") ; fprintf(stderr," Consider setting the value for AFNI_DRAW_THRESH to less than %f\n", vthresh); } free(voxout) ; /* toss trash */ return ; } /*----------------------------------------------------------------------- Copy a dataset; new prefix is "COPY_" + old prefix. zfill != 0 ==> zero fill ftyp <= 0 ==> same func type ftyp == 1 ==> fim ftyp == 2 ==> anat (omri) dtype < 0 ==> same datum dtype >= 0 ==> new datum (only valid for zfill) Adapted from plug_copy.c -- 24 Sep 2001 - RWCox -------------------------------------------------------------------------*/ THD_3dim_dataset * DRAW_copy_dset( THD_3dim_dataset *dset , int zfill , int ftyp , int dtype ) { THD_3dim_dataset *new_dset ; char new_prefix[THD_MAX_PREFIX] ; int ival ; if( !ISVALID_DSET(dset) ) return NULL ; if( strstr(DSET_PREFIX(dset),"COPY") != NULL ) strcpy(new_prefix,"C") ; else strcpy(new_prefix,"COPY_") ; ival = strlen(new_prefix) ; MCW_strncpy(new_prefix+ival,DSET_PREFIX(dset),THD_MAX_PREFIX-ival) ; /*-- make a new dataset, somehow --*/ if( zfill == 0 ){ new_dset = PLUTO_copy_dset( dset , new_prefix ) ; /* full copy */ dtype = -1 ; } else { new_dset = EDIT_empty_copy( dset ) ; /* zero fill */ EDIT_dset_items( new_dset, ADN_prefix,new_prefix, ADN_none ) ; } if( new_dset == NULL ) return NULL ; /* bad, real bad */ tross_Copy_History( dset , new_dset ) ; /* make some History, dude! */ { char str[256] ; strcpy(str,"Drawing plugin COPY:") ; if( zfill ) strcat(str," Fill->Zero") ; else strcat(str," Fill->Data") ; if( ftyp == 1 ) strcat(str," Type->Func") ; else if( ftyp == 2 ) strcat(str," Type->Anat") ; if( dtype >= 0 ){ strcat(str," Datum->") ; strcat(str,MRI_TYPE_name[dtype]) ; } tross_Append_History( new_dset , str ) ; } /*--- modify new dataset, if desired ---*/ if( ftyp == 1 ) EDIT_dset_items( new_dset , ADN_type , HEAD_FUNC_TYPE , ADN_func_type , FUNC_FIM_TYPE , ADN_none ) ; else if( ftyp == 2 ) EDIT_dset_items( new_dset , ADN_type , HEAD_ANAT_TYPE , ADN_func_type , ANAT_OMRI_TYPE , ADN_none ) ; if( zfill == 0 ) return new_dset ; /* done if not zero-filling */ /*--- change type of data stored? ---*/ if( dtype >= 0 ) EDIT_dset_items( new_dset , ADN_datum_all, dtype, ADN_none ) ; /* zero fill */ { int ityp , nbytes , nvals , ival ; void * new_brick , * bp ; nvals = DSET_NVALS(new_dset) ; for( ival=0 ; ival < nvals ; ival++) /* get memory for bricks */ { /* and zero fill */ ityp = DSET_BRICK_TYPE(new_dset,ival) ; nbytes = DSET_BRICK_BYTES(new_dset,ival) ; /* how much data */ new_brick = malloc( nbytes ) ; EDIT_substitute_brick( new_dset , ival , ityp , new_brick ) ; bp = DSET_BRICK_ARRAY(new_dset,ival) ; /* brick pointer */ EDIT_BRICK_FACTOR(new_dset,ival,0.0) ; /* brick factor */ memset( bp , 0 , nbytes ) ; } } /* 20 Oct 2003: copy VALUE_LABEL_DTABLE attribute, if present */ { ATR_string *atr ; atr = THD_find_string_atr( dset->dblk , "VALUE_LABEL_DTABLE" ) ; if( atr != NULL ) THD_set_char_atr( new_dset->dblk , "VALUE_LABEL_DTABLE" , atr->nch , atr->ch ) ; } new_dset->int_cmap = 1; THD_set_int_atr(new_dset->dblk, "INT_CMAP", 1, &new_dset->int_cmap); /*-- done successfully!!! --*/ return new_dset ; } /*-----------------------------------------------------------------------------*/ /*! Expand set of points in 2D plane. RWCox - 07 Oct 2002. -------------------------------------------------------------------------------*/ static void DRAW_2D_expand( int np, int *xd, int *yd, int *zd, int plane , int *nfill , int **xyzf ) { int base , di,dj , itop,jtop , xx,yy,zz , ix,jy , *ip,*jp ; int nx=DSET_NX(dset) , ny=DSET_NY(dset) , nz=DSET_NZ(dset) , nxy = nx*ny ; int kadd , ii,jj,kk , ixn,jyn , mm,qq ; int *xyzn ; static int nadd[5] = { 4 , 8 , 12 , 20 , 24 } ; static int nn[24][2] = { {-1, 0} , { 1, 0} , { 0, 1} , { 0,-1} , {-1,-1} , {-1, 1} , { 1,-1} , { 1, 1} , {-2, 0} , { 2, 0} , { 0, 2} , { 0,-2} , {-2, 1} , {-2,-1} , {-1, 2} , {-1,-2} , { 2, 1} , { 2,-1} , { 1, 2} , { 1,-2} , {-2,-2} , {-2, 2} , { 2,-2} , { 2, 2} } ; /* check inputs */ if( np <= 0 || xd == NULL || yd == NULL || zd == NULL ) return ; if( mode_ival < FIRST_2D_MODE && mode_ival > LAST_2D_MODE ) return ; if( nfill == NULL || xyzf == NULL ) return ; /* compute stuff for which plane we are in: 1 -> yz , 2 -> xz , 3 -> xy */ xx = xd[0] ; yy = yd[0] ; zz = zd[0] ; switch(plane){ case 1: base=xx ; di=nx; dj=nxy; itop=ny; jtop=nz; ip=yd; jp=zd; break; case 2: base=yy*nx ; di=1 ; dj=nxy; itop=nx; jtop=nz; ip=xd; jp=zd; break; case 3: base=zz*nxy; di=1 ; dj=nx ; itop=nx; jtop=ny; ip=xd; jp=yd; break; default: return ; /* bad input */ } kadd = nadd[mode_ival-FIRST_2D_MODE] ; /* how many pts around each input pt */ xyzn = (int *) malloc( sizeof(int)*np*(kadd+1) ) ; /* output array */ /** add points around each input point, culling duplicates **/ for( ii=jj=0 ; ii < np ; ii++ ){ ix = ip[ii] ; jy = jp[ii] ; /* drawn point 2D index */ if( ix >= 0 && ix < itop && jy >= 0 && jy < jtop ){ xyzn[jj++] = base + ix*di + jy*dj ; /* load 3D index */ for( kk=0 ; kk < kadd ; kk++ ){ ixn = ix+nn[kk][0] ; jyn = jy+nn[kk][1] ; /* nbhd pt 2D index */ if( ixn >= 0 && ixn < itop && jyn >= 0 && jyn < jtop ){ mm = base + ixn*di + jyn*dj ; /* 3D index */ if( ii > 0 ) for( qq=0 ; qq < jj && xyzn[qq] != mm ; qq++ ) ; /* nada */ else qq = jj ; if( qq == jj ) xyzn[jj++] = mm ; /* save 3D index */ } } } } *nfill = jj ; *xyzf = xyzn ; return ; } /*-----------------------------------------------------------------------------*/ /*! Expand set of points in 3D space. RWCox - 07 Oct 2002. -------------------------------------------------------------------------------*/ static void DRAW_3D_expand( int np, int *xd, int *yd, int *zd, int plane , int *nfill , int **xyzf ) { int ix,jy,kz ; int nx=DSET_NX(dset) , ny=DSET_NY(dset) , nz=DSET_NZ(dset) , nxy = nx*ny ; int kadd , ii,jj,kk , ixn,jyn,kzn , mm,qq ; int *xyzn ; static int nadd[7] = { 6 , 18 , 26 , 32 , 56 , 80 , 124 } ; static int nn[124][3] ={ {-1, 0, 0} , { 1, 0, 0} , /* r**2 = 1 */ { 0,-1, 0} , { 0, 1, 0} , { 0, 0,-1} , { 0, 0, 1} , {-1,-1, 0} , {-1, 1, 0} , /* r**2 = 2 */ { 1,-1, 0} , { 1, 1, 0} , { 0,-1,-1} , { 0,-1, 1} , { 0, 1,-1} , { 0, 1, 1} , {-1, 0,-1} , {-1, 0, 1} , { 1, 0,-1} , { 1, 0, 1} , {-1,-1,-1} , {-1,-1, 1} , /* r**2 = 3 */ {-1, 1,-1} , {-1, 1, 1} , { 1,-1,-1} , { 1,-1, 1} , { 1, 1,-1} , { 1, 1, 1} , {-2, 0, 0} , { 2, 0, 0} , /* r**2 = 4 */ { 0,-2, 0} , { 0, 2, 0} , { 0, 0,-2} , { 0, 0, 2} , {-2,-1, 0} , {-2, 1, 0} , /* r**2 = 5 */ { 2,-1, 0} , { 2, 1, 0} , { 0,-2,-1} , { 0,-2, 1} , { 0, 2,-1} , { 0, 2, 1} , {-2, 0,-1} , {-2, 0, 1} , { 2, 0,-1} , { 2, 0, 1} , {-1,-2, 0} , {-1, 2, 0} , { 1,-2, 0} , { 1, 2, 0} , { 0,-1,-2} , { 0,-1, 2} , { 0, 1,-2} , { 0, 1, 2} , {-1, 0,-2} , {-1, 0, 2} , { 1, 0,-2} , { 1, 0, 2} , {-2,-1,-1} , {-2,-1, 1} , /* r**2 = 6 */ {-2, 1,-1} , {-2, 1, 1} , { 2,-1,-1} , { 2,-1, 1} , { 2, 1,-1} , { 2, 1, 1} , {-1,-2,-1} , {-1,-2, 1} , {-1, 2,-1} , {-1, 2, 1} , { 1,-2,-1} , { 1,-2, 1} , { 1, 2,-1} , { 1, 2, 1} , {-1,-1,-2} , {-1,-1, 2} , {-1, 1,-2} , {-1, 1, 2} , { 1,-1,-2} , { 1,-1, 2} , { 1, 1,-2} , { 1, 1, 2} , {-2,-2, 0} , {-2, 2, 0} , /* r**2 = 8 */ { 2,-2, 0} , { 2, 2, 0} , { 0,-2,-2} , { 0,-2, 2} , { 0, 2,-2} , { 0, 2, 2} , {-2, 0,-2} , {-2, 0, 2} , { 2, 0,-2} , { 2, 0, 2} , {-2,-2, 1} , {-2, 2, 1} , /* r**2 = 9 */ { 2,-2, 1} , { 2, 2, 1} , { 1,-2,-2} , { 1,-2, 2} , { 1, 2,-2} , { 1, 2, 2} , {-2, 1,-2} , {-2, 1, 2} , { 2, 1,-2} , { 2, 1, 2} , {-2,-2,-1} , {-2, 2,-1} , { 2,-2,-1} , { 2, 2,-1} , {-1,-2,-2} , {-1,-2, 2} , {-1, 2,-2} , {-1, 2, 2} , {-2,-1,-2} , {-2,-1, 2} , { 2,-1,-2} , { 2,-1, 2} , {-2,-2,-2} , {-2,-2, 2} , /* r**2 = 12 */ {-2, 2,-2} , {-2, 2, 2} , /* [corners of 5x5x5] */ { 2,-2,-2} , { 2,-2, 2} , { 2, 2,-2} , { 2, 2, 2} } ; /* check inputs */ if( np <= 0 || xd == NULL || yd == NULL || zd == NULL ) return ; if( mode_ival < FIRST_3D_MODE && mode_ival > LAST_3D_MODE ) return ; if( nfill == NULL || xyzf == NULL ) return ; kadd = nadd[mode_ival-FIRST_3D_MODE] ; /* how many pts around each input pt */ xyzn = (int *) malloc( sizeof(int)*np*(kadd+1) ) ; /* output array */ /** add points around each input point, culling duplicates **/ for( ii=jj=0 ; ii < np ; ii++ ){ ix = xd[ii] ; jy = yd[ii] ; kz = zd[ii] ; if( ix >= 0 && ix < nx && jy >= 0 && jy < ny && kz >= 0 && kz <= nz ){ xyzn[jj++] = ix + jy*nx + kz*nxy ; /* load 3D index */ for( kk=0 ; kk < kadd ; kk++ ){ ixn = ix+nn[kk][0] ; jyn = jy+nn[kk][1] ; kzn = kz+nn[kk][2] ; if( ixn >= 0 && ixn < nx && jyn >= 0 && jyn < ny && kzn >= 0 && kzn < nz ){ mm = ixn + jyn*nx + kzn*nxy ; /* 3D index */ if( ii > 0 ) for( qq=0 ; qq < jj && xyzn[qq] != mm ; qq++ ) ; /* nada */ else qq = jj ; if( qq == jj ) xyzn[jj++] = mm ; /* save 3D index */ } } } } *nfill = jj ; *xyzf = xyzn ; return ; } /*-----------------------------------------------------------------------------*/ /*! Expand set of points in 2D plane, in a circle. RWCox - 16 Oct 2002. -------------------------------------------------------------------------------*/ static void DRAW_2D_circle( int np, int *xd, int *yd, int *zd, int plane , int *nfill , int **xyzf ) { int base , di,dj , itop,jtop , xx,yy,zz , ix,jy , *ip,*jp ; int nx=DSET_NX(dset) , ny=DSET_NY(dset) , nz=DSET_NZ(dset) , nxy = nx*ny ; int kadd , ii,jj,kk , ixn,jyn , mm ; int *xyzn ; float dx = fabs(DSET_DX(dset)) ; float dy = fabs(DSET_DY(dset)) ; float dz = fabs(DSET_DZ(dset)) ; float rad= rad_av->fval ; float fdi,fdj , xq,yq,radq ; int idx , jdy , *nn ; /* check inputs */ if( np <= 0 || xd == NULL || yd == NULL || zd == NULL ) return ; if( nfill == NULL || xyzf == NULL ) return ; /* compute stuff for which plane we are in: 1 -> yz , 2 -> xz , 3 -> xy */ xx = xd[0] ; yy = yd[0] ; zz = zd[0] ; switch(plane){ case 1: base=xx ; di=nx; dj=nxy; itop=ny; jtop=nz; ip=yd; jp=zd; fdi=dy; fdj=dz; break; case 2: base=yy*nx ; di=1 ; dj=nxy; itop=nx; jtop=nz; ip=xd; jp=zd; fdi=dx; fdj=dz; break; case 3: base=zz*nxy; di=1 ; dj=nx ; itop=nx; jtop=ny; ip=xd; jp=yd; fdi=dx; fdj=dy; break; default: return ; /* bad input */ } idx = rad / fdi ; jdy = rad / fdj ; if( idx < 1 && jdy < 1 ) return ; /* circle smaller than in-plane voxel */ /* make incremental mask */ radq = 1.001*rad*rad ; nn = (int *) malloc( sizeof(int)*(2*idx+1)*(2*jdy+1)*2 ) ; kadd = 0 ; for( jj=-jdy ; jj <= jdy ; jj++ ){ yq = (jj*fdj)*(jj*fdj) ; for( ii=-idx ; ii <= idx ; ii++ ){ xq = (ii*fdi)*(ii*fdi) + yq ; if( xq <= radq && xq > 0.0 ){ nn[2*kadd] = ii ; nn[2*kadd+1] = jj ; kadd++ ; } } } xyzn = (int *) malloc( sizeof(int)*np*(kadd+1) ) ; /* output array */ /** add points around each input point, culling duplicates **/ for( ii=jj=0 ; ii < np ; ii++ ){ ix = ip[ii] ; jy = jp[ii] ; /* drawn point 2D index */ if( ix >= 0 && ix < itop && jy >= 0 && jy < jtop ){ xyzn[jj++] = base + ix*di + jy*dj ; /* load 3D index */ for( kk=0 ; kk < kadd ; kk++ ){ ixn = ix+nn[2*kk] ; jyn = jy+nn[2*kk+1] ; /* nbhd pt 2D index */ if( ixn >= 0 && ixn < itop && jyn >= 0 && jyn < jtop ){ mm = base + ixn*di + jyn*dj ; /* 3D index */ #ifndef USE_COLLAPSAR if( ii > 0 ) for( qq=0 ; qq < jj && xyzn[qq] != mm ; qq++ ) ; /* nada */ else qq = jj ; if( qq == jj ) xyzn[jj++] = mm ; /* save 3D index */ #else xyzn[jj++] = mm ; /* save 3D index */ #endif } } #ifdef USE_COLLAPSAR if( ii > 9 && (ii==np-1 || ii%20==0) ) DRAW_collapsar( &jj , xyzn ) ; #endif } } *nfill = jj ; *xyzf = xyzn ; free(nn) ; return ; } /*-----------------------------------------------------------------------------*/ /*! Expand set of points in 3D, in a sphere. RWCox - 16 Oct 2002. -------------------------------------------------------------------------------*/ static void DRAW_3D_sphere( int np, int *xd, int *yd, int *zd, int plane , int *nfill , int **xyzf ) { int ix,jy,kz ; int nx=DSET_NX(dset) , ny=DSET_NY(dset) , nz=DSET_NZ(dset) , nxy = nx*ny ; int kadd , ii,jj,kk , ixn,jyn,kzn , mm ; int *xyzn ; float dx = fabs(DSET_DX(dset)) ; float dy = fabs(DSET_DY(dset)) ; float dz = fabs(DSET_DZ(dset)) ; float rad= rad_av->fval ; float xq,yq,zq,radq ; int idx , jdy , kdz , *nn ; int www ; /* check inputs */ if( np <= 0 || xd == NULL || yd == NULL || zd == NULL ) return ; if( nfill == NULL || xyzf == NULL ) return ; idx = rad/dx ; jdy = rad/dy ; kdz = rad/dz ; if( idx < 1 && jdy < 1 && kdz < 1 ) return ; /* sphere smaller than voxel */ #if 0 fprintf(stderr,"DRAW_3D_sphere: rad=%g dx=%g idx=%d dy=%g jdy=%d dz=%g kdz=%d\n", rad,dx,idx,dy,jdy,dz,kdz ) ; #endif /* make incremental mask */ radq = 1.001*rad*rad ; nn = (int *) malloc( sizeof(int)*(2*idx+1)*(2*jdy+1)*(2*kdz+1)*3 ) ; kadd = 0 ; for( kk=-kdz ; kk <= kdz ; kk++ ){ zq = (kk*dz)*(kk*dz) ; for( jj=-jdy ; jj <= jdy ; jj++ ){ yq = zq + (jj*dy)*(jj*dy) ; for( ii=-idx ; ii <= idx ; ii++ ){ xq = yq + (ii*dx)*(ii*dx) ; if( xq <= radq && xq > 0.0 ){ nn[3*kadd] = ii ; nn[3*kadd+1] = jj ; nn[3*kadd+2] = kk ; kadd++ ; } } } } xyzn = (int *) malloc( sizeof(int)*np*(kadd+1) ) ; /* output array */ if( xyzn == NULL ){ fprintf(stderr,"\n** DRAW_3D_sphere ERROR: can't allocate memory!\n\a"); free(nn); return; } www = (np*(kadd+1) > 1234567) && (np > 1) ; /* show waiting? */ if( www ) SHOW_AFNI_PAUSE ; /** add points around each input point **/ for( ii=jj=0 ; ii < np ; ii++ ){ ix = xd[ii] ; jy = yd[ii] ; kz = zd[ii] ; if( ix >= 0 && ix < nx && jy >= 0 && jy < ny && kz >= 0 && kz <= nz ){ xyzn[jj++] = ix + jy*nx + kz*nxy ; /* load 3D index */ for( kk=0 ; kk < kadd ; kk++ ){ ixn = ix+nn[3*kk] ; jyn = jy+nn[3*kk+1] ; kzn = kz+nn[3*kk+2] ; if( ixn >= 0 && ixn < nx && jyn >= 0 && jyn < ny && kzn >= 0 && kzn < nz ){ mm = ixn + jyn*nx + kzn*nxy ; /* 3D index */ #ifndef USE_COLLAPSAR if( ii > 0 ) for( qq=0 ; qq < jj && xyzn[qq] != mm ; qq++ ) ; /* nada */ else qq = jj ; if( qq == jj ) xyzn[jj++] = mm ; /* save 3D index */ #else xyzn[jj++] = mm ; /* save 3D index */ #endif } } #ifdef USE_COLLAPSAR if( ii > 5 && (ii==np-1 || ii%16==0) ) DRAW_collapsar( &jj , xyzn ) ; #endif } } if( www ) SHOW_AFNI_READY ; *nfill = jj ; *xyzf = xyzn ; free(nn) ; return ; } /*--------------------------------------------------------------------------------*/ #ifdef USE_COLLAPSAR /*! Collapses the input list of points to non-duplicates. */ static void DRAW_collapsar( int *npt , int *xyzn ) { int ii , jj , np ; if( npt == NULL || xyzn == NULL ) return ; np = *npt ; if( np <= 1 ) return ; qsort_int( np , xyzn ) ; /* sort */ for( ii=1 ; ii < np ; ii++ ) /* find 1st duplicates */ if( xyzn[ii] == xyzn[ii-1] ) break ; if( ii == np ) return ; /* no duplicate => done */ /* if [ii] is different from [jj], then add 1 to jj, and copy [ii] into the new [jj] location; otherwise, keep jj fixed (thus skipping [ii]) */ for( jj=ii-1 ; ii < np ; ii++ ){ if( xyzn[ii] != xyzn[jj] ) xyzn[++jj] = xyzn[ii] ; } *npt = jj+1 ; return ; } #endif /* USE_COLLAPSAR */ static MCW_arrowval * Atlas_chooser(Widget rc) { int k, nr , atlasind=0 ; ATLAS_LIST *atl=NULL; MCW_arrowval *atlas_av; if( Atlas_With_Trimming(Current_Atlas_Default_Name(),1, NULL) <= 0 ) return(NULL); /*** make list of atlases to include ***/ atl = env_atlas_list(); nr = atl->natlases; if (!(atlas_names = Atlas_Names_List(atl))) { WARNING_message("No atlases in default list."); return(NULL); } /* get the actual atlas index to put in as default */ for (k=0; kwrowcol , "Use this to popup a\n" "'chooser' that lets\n" "you select which\n" "atlas to edit. The list\n" "comes from AFNI_ATLAS_LIST\n" "or default list\n" ) ; MCW_reghint_children( atlas_av->wrowcol , "Popup an atlas chooser" ) ; XtManageChild( rc ) ; return(atlas_av); } /* callback function for choosing a new atlas */ static void choose_new_atlas_CB(MCW_arrowval *av , XtPointer cd ) { int ii, nr; ATLAS_POINT *tto_list=NULL; char **textdata; char *tempatlasname; char title_str[256]; XmString xstr ; textdata = av->text_data; /* check if the atlas is the same one we already have */ if(strcmp(plugdraw_atlasname, textdata[av->ival])==0) return; tempatlasname = textdata[av->ival]; /* point to current atlas name */ if(atlas_n_points(tempatlasname)<=0){ ERROR_message( "atlas %s not available.\n" "Check that it exists or remove from AFNI_ATLAS_LIST", tempatlasname); return; } plugdraw_atlasname = textdata[av->ival]; /* point to current atlas name */ free_plugdraw_atlas_list(); /*** make list of atlas regions to include ***/ if (!(ttatlas_list = New_ttatlas_compendium(plugdraw_atlasname))) { ERROR_message("Failed compending on atlas change."); } tto_list = atlas_points(plugdraw_atlasname); nr = 0 ; for( ii=0 ; ii < atlas_n_points(plugdraw_atlasname) ; ii++ ){ ttatlas_list->reg_label [nr] = strdup(tto_list[ii].name) ; ttatlas_list->reg_tto [nr] = ii ; ttatlas_list->reg_ttval [nr] = tto_list[ii].tdval ; nr++ ; /* count how many regions actually have some non-zero value */ } ttatlas_list->reg_num = nr ; /*** Update region chooser ***/ refit_MCW_optmenu( ttatlas_region_av , 0 , nr-1 , 0 , 0 , MCW_av_substring_CB , ttatlas_list->reg_label ) ; AVOPT_columnize( ttatlas_region_av , 3 ) ; sprintf(title_str, "Select Atlas and Region (Current Atlas: %s)", plugdraw_atlasname); xstr = XmStringCreateLtoR( title_str , XmFONTLIST_DEFAULT_TAG ) ; XtVaSetValues( atlas_panel , XmNlabelString , xstr , NULL ) ; XmStringFree(xstr) ; return; } /* free the region names from menu list */ static void free_plugdraw_atlas_list() { int i; for(i=0;ireg_num;i++){ free(ttatlas_list->reg_label[i]); } free(ttatlas_list->reg_label); free(ttatlas_list->reg_tto); free(ttatlas_list->reg_ttval); free(ttatlas_list); return; } /* return the threshold in percent to cutoff atlas voxels */ static float get_afni_thresh_percent() { static int thresh_set = -1; float thresh = 49.0; float ppp; if(thresh_set > 0) return(thresh); ppp = AFNI_numenv("AFNI_DRAW_THRESH"); if( ppp > 0.0f && ppp <= 100.0f ) { thresh = ppp; } return(thresh); }