1 /***********************************************************************
2  * plug_vol2surf.c              - plugin interface to vol2surf computation
3  *
4  * Provide an interface to the global v2s_plugin_opts structure.
5  *
6  * - R. Reynolds
7  ***********************************************************************
8 */
9 
10 #include "afni.h"
11 #include "vol2surf.h"
12 
13 #ifndef ALLOW_PLUGINS
14 #  error "Plugins not properly set up -- see machdep.h"
15 #endif
16 
17 static char * PV2S_main( PLUGIN_interface * );
18 
19 static char g_help[] =
20     " This plugin provides an interface to the vol2surf options used by afni\n"
21     " for the display of the Overlay dataset values on the surfaces in suma.\n"
22     " \n"
23     " For each Local Domain Parent that afni has received from suma, vol2surf\n"
24     " computes a node color overlay and sends it to suma.  This is computed\n"
25     " by mapping afni's Overlay data across the 1 or 2 surfaces of each given\n"
26     " LDP (Local Domain Parent).\n"
27     " \n"
28     " By default, each LDP gets a color list based on:\n"
29     " \n"
30     "   o  if only 1 surface for this LDP     : intersection of the surface\n"
31     "      and afni's Overlay sub-brick, subject to afni's color bar and\n"
32     "      thresholding (afni voxels that do not survive the threshold take\n"
33     "      no part in the mapping)\n"
34     " \n"
35     "   o  if 2 or more surfaces for this LDP : intersection of the midpoint\n"
36     "      of the first 2 surfaces received from suma (for this LDP) and the\n"
37     "      Overlay sub-brick in afni, again subject to afni's color bar and\n"
38     "      thresholding\n"
39     " \n"
40     " These defaults are for surfaces that have _not_ been chosen via the\n"
41     " plugin interface.  Surfaces chosen via the plugin are, of course,\n"
42     " mapped as the user specifies.  Note that to recreate the defaults, use\n"
43     " the 'mask' function for 1 surface, and the midpoint function for more\n"
44     " than 1.  The 'num steps' and 'seg index' will not be applicable.\n"
45     " \n"
46     " As of May, 2018, there is a 'map_all' option under 'use vol2surf'.  In\n"
47     " that case, all mappable surfaces will be processed using the plugin\n"
48     " options, rather than the simple defaults.\n"
49     " \n"
50     " ---\n"
51     " \n"
52     " The minimum choices that must be made are on the 'function' line:\n"
53     " 'use vol2surf?' (should be 'yes' :), and 'map func' (should be valid).\n"
54     " \n"
55     " ** Note that as a plugin, surfaces will be unknown at initialization\n"
56     "    time.  Therefore the interface is via afni's surface index list.\n"
57     "    To see the surface index list, set the debug level to 2, and then\n"
58     "    press 'Set+Keep'.  Part of the text output to the _terminal window_\n"
59     "    will be '+d valid surface indices and labels: ...'.\n"
60     " \n"
61     " ** For detailed infomation, please try the command: '3dVol2Surf -help'.\n"
62     " \n"
63     " ----------------------------- options --------------------------------\n"
64     " function:\n"
65     " \n"
66     "   use vol2surf? : choose 'yes' to make the plugin options active\n"
67     "                   choose 'map_all' to apply options to all surfaces,\n"
68     "                   not simply those in pair 0 and/or 1\n"
69     "   map func      : choose a function to apply to values along node pair\n"
70     "                   segments, embedded in the AFNI volume\n"
71     "   seg index     : apply all values along a each segment, or only those\n"
72     "                   from unique voxels\n"
73     "   num steps     : the number of evenly spaced points each segment will\n"
74     "                   be divided into\n"
75     " \n"
76     " surf pair 0:\n"
77     " \n"
78     "   apply?        : if no, skip this surface (pair)\n"
79     "   surf_A        : choose the afni surface index for surface A\n"
80     "   use B?        : form segments by joining nodes from surface A with\n"
81     "                   nodes on surface B (an alternate choice is to use\n"
82     "                   only surface A, along with its normals)\n"
83     "   surf_B        : choose the afni surface index for surface B\n"
84     " \n"
85     " surf pair 1:\n"
86     " \n"
87     "   apply?        : if no, skip this surface (pair)\n"
88     "   surf_A        : choose the afni surface index for surface A\n"
89     "   use B?        : form segments by joining nodes from surface A with\n"
90     "                   nodes on surface B (an alternate choice is to use\n"
91     "                   only surface A, along with its normals)\n"
92     "   surf_B        : choose the afni surface index for surface B\n"
93     " \n"
94     " normals:\n"
95     " \n"
96     "   use normals?  : segments are formed from each node from surface A\n"
97     "                   along its normal, and of length 'norm len'\n"
98     "   norm len      : this will be the length of each segment\n"
99     "   norm dir      : choose what to do with the normal directions:\n"
100     "                     check   : let vol2surf try to decide\n"
101     "                     keep    : keep the default directions\n"
102     "                     reverse : reverse the default directions\n"
103     " \n"
104     " offsets:\n"
105     " \n"
106     "   NOTE - segments are considered directed from the node on surface A\n"
107     "          in the direction of surface B (or the normal), so if f_p1 is\n"
108     "          positive each segment gets shorter (going toward surface B),\n"
109     "          but if f_pn is positive each segment gets longer (still moving\n"
110     "          in the direction from surface A to surface B)\n"
111     " \n"
112     "   f_p1_mm       : move each segment starting point this number of\n"
113     "                   millimeters toward surface B\n"
114     "   f_pn_mm       : move each segment ending point this number of\n"
115     "                   millimeters farther from surface A\n"
116     "   f_p1_fr       : move each segment starting point this fraction of the\n"
117     "                   distance toward surface B\n"
118     "   f_pn_fr       : move each segment ending point this fraction of the\n"
119     "                   distance farther from surface A\n"
120     " \n"
121     " out of range:\n"
122     " \n"
123     "   oob nodes?    : whether to still output results for those nodes which\n"
124     "                   are out-of-bounds (i.e. outside the bounding box of\n"
125     "                   the AFNI overlay dataset)\n"
126     "   oob value     : if out-of-bounds nodes are output, this value will be\n"
127     "                   given to them\n"
128     "   oom nodes?    : whether to still output results for those nodes which\n"
129     "                   are out-of-mask (i.e. masked by the threshold brick)\n"
130     "   oob value     : if out-of-mask nodes are output, this value will be\n"
131     "                   given to them\n"
132     " output:\n"
133     " \n"
134     "   first node    : starting index for nodes to output\n"
135     "   last node     : ending index for nodes to output (0 means 'nodes-1')\n"
136     "                   (sorry, that means you cannot output from 0 to 0)\n"
137     "   outfile.1D    : also send the results to this file (in 1D format)\n"
138     "   outfile.niml  : also send the results to this file (in niml format)\n"
139     " \n"
140     " debug level:\n"
141     " \n"
142     "   level         : provide this level of debug output (0-5)\n"
143     "   node          : provide additional debug output for this node\n"
144     " \n"
145     " \n"
146     " Author: R Reynolds\n"
147     " \n"
148     " ----------------------------------------------------------------------\n"
149     "   History:\n"
150     " \n"
151     "   1.0   9 September 2004 [rickr] - initial version\n"
152     " \n"
153     "   1.1  16 September 2004 [rickr]\n"
154     "     - init gp_index to -1 (set it in afni)\n"
155     "     - allow the user to keep or reverse normal directions\n"
156     " \n"
157     "   1.2  29 September 2004 [rickr]\n"
158     "     - now set global ready if all is well\n"
159     "     - clear norms if not in use\n"
160     "     - name all local functions PV2S_*\n"
161     "     - if debug > 0, display chosen surfaces in terminal\n"
162     "     - if debug > 1, display all possible surfaces in terminal\n"
163     " \n"
164     "   1.3  08 October 2004 [rickr]\n"
165     "     - Global changes for new LDP processing:\n"
166     "     - added second surface pair to GUI\n"
167     "     - made small help and hint changes\n"
168     "     - fixed receive order of fr and mm offsets (mm was fr)\n"
169     "     - verify that surface pairs have matching LDPs\n"
170     "     - added PV2S_disp_afni_surfaces() to list all surfaces w/indices\n"
171     " \n"
172     "   1.4  25 October 2004 [rickr]\n"
173     "     - make sure the surface pairs are actually different\n"
174     "     - make sure surfaces have the same number of nodes\n"
175     "     - process all parameters, but only complain if 'ready'\n"
176     "     - always pass along debug/dnode\n"
177     " \n"
178     "   1.5  11 Dec 2004 [rickr] - describe default behavior here\n"
179     "   1.5a 22 Mar 2005 [rickr] - removed all tabs\n"
180     "   1.6  28 Mar 2006 [rickr] - fixed mode computation\n"
181     "   1.7  09 Aug 2006 [rickr] - store surface labels for history note\n"
182         ;
183 
184 #define P_MAP_NAMES_NVALS      16       /* should match enum for global maps */
185 #define P_NY_NVALS              2
186 #define P_NYA_NVALS             3
187 #define P_KEEP_NVALS            3
188 #define P_STEP_NVALS            2
189 
190 static char * gp_ny_list[]   = { "no", "yes" };
191 static char * gp_nyA_list[]  = { "no", "yes", "map_all" };
192 static char * gp_keep_list[] = { "no check yet", "keep", "reverse" };
193 static char * gp_step_list[] = { "voxel", "node" };
194 
195 typedef struct
196 {
197     v2s_plugin_opts  * vpo;
198     char             * hist;
199     char            ** maps;
200 } pv2s_globals;
201 
202 static pv2s_globals globs;      /* these are just pointers to afni globals */
203 
204 /* local functions */
205 static int PV2S_check_surfaces(PLUGIN_interface * plint, int sa, int sb,
206                                char *mesg, int debug);
207 static int PV2S_disp_afni_surfaces(PLUGIN_interface * plint);
208 static int PV2S_init_plugin_opts(pv2s_globals * g);
209 static int PV2S_process_args(PLUGIN_interface * plint,pv2s_globals * g,
210                              char *mesg);
211 
212 /* for ease of error reporting */
213 #define PV2S_BAIL_VALUE(buf,str,val)                                   \
214         do { sprintf((buf),  "-------------------------------------\n"  \
215                              "%s\n"                                     \
216                              "bad value = %d\n"                         \
217                              "-------------------------------------",   \
218                              (str), (val) ); } while (0)
219 
220 DEFINE_PLUGIN_PROTOTYPE                 /* for C++ compilation */
221 
PLUGIN_init(int ncall)222 PLUGIN_interface * PLUGIN_init( int ncall )
223 {
224     PLUGIN_interface * plint;
225     void             * void_vpo;
226     int                ival;
227 
228 ENTRY("vol2surf: PLUGIN_init");
229 
230     if ( ncall > 0 ) RETURN(NULL);      /* only one interface */
231 
232     /* might be temporary */
233     if ( PLUTO_set_v2s_addrs(&void_vpo, &globs.maps, &globs.hist) )
234     {
235         fprintf(stderr,"** plug_v2s: failed to init globals\n");
236         RETURN(NULL);
237     }
238 
239     /* using a void pointer so we don't have to put vol2surf.h in afni.h */
240     globs.vpo = (v2s_plugin_opts *)void_vpo;
241 
242     PV2S_init_plugin_opts(&globs);
243 
244     plint = PLUTO_new_interface("Vol2Surf",
245                 "configure afni's volume to surface options",
246                 g_help, PLUGIN_CALL_VIA_MENU, PV2S_main);
247 
248     PLUTO_add_hint     (plint, "configure vol2surf options");
249     PLUTO_set_sequence (plint, "A:afnicontrol:surf");
250     PLUTO_set_runlabels(plint, "Set+Keep", "Set+Close");
251 
252     /* first input : do we use vol2surf? */
253     PLUTO_add_option( plint, "function", "op_st", TRUE );
254     PLUTO_add_hint  ( plint, "decide whether to use vol2surf" );
255     PLUTO_add_string( plint, "use vol2surf? ", P_NYA_NVALS, gp_nyA_list, 0 );
256     PLUTO_add_hint  ( plint, "use vol2surf (or basic intersection)" );
257     PLUTO_add_string( plint, "map func",P_MAP_NAMES_NVALS,globs.maps,0);
258     PLUTO_add_hint  ( plint, "choose a filter to apply to segment values" );
259     PLUTO_add_string( plint, "seg index",P_STEP_NVALS,gp_step_list,0);
260     PLUTO_add_hint  ( plint, "along segments, count only distinct voxels?");
261     PLUTO_add_number( plint, "num steps", 0, 100, 0, 2, 1 );
262     PLUTO_add_hint  ( plint, "number of steps to divide each segment into" );
263 
264     /* choose surface indices for surf pair 0                   */
265     /*   - note that we do not yet have surfaces to choose from */
266     PLUTO_add_option( plint, "surf pair 0", "surf pair 0", TRUE );
267     PLUTO_add_hint  ( plint, "choose first surface index(es)" );
268     PLUTO_add_string( plint, "apply? ", P_NY_NVALS, gp_ny_list, 1 );
269     PLUTO_add_hint  ( plint, "decide whether to apply surface(s)" );
270     PLUTO_add_number( plint, "surf_A", 0, 50, 0, 0, 1 );
271     PLUTO_add_hint  ( plint, "choose surface A index" );
272     PLUTO_add_string( plint, "use B? ", P_NY_NVALS, gp_ny_list, 0 );
273     PLUTO_add_hint  ( plint, "decide whether to use surface B" );
274     PLUTO_add_number( plint, "surf_B", 0, 50, 0, 1, 1 );
275     PLUTO_add_hint  ( plint, "choose surface B index" );
276 
277     /* choose surface indices for surf pair 1                   */
278     /*   - note that we do not yet have surfaces to choose from */
279     PLUTO_add_option( plint, "surf pair 1", "surf pair 1", TRUE );
280     PLUTO_add_hint  ( plint, "choose second surface index(es)" );
281     PLUTO_add_string( plint, "apply? ", P_NY_NVALS, gp_ny_list, 0 );
282     PLUTO_add_hint  ( plint, "decide whether to apply surface(s)" );
283     PLUTO_add_number( plint, "surf_A", 0, 50, 0, 2, 1 );
284     PLUTO_add_hint  ( plint, "choose surface A index" );
285     PLUTO_add_string( plint, "use B? ", P_NY_NVALS, gp_ny_list, 0 );
286     PLUTO_add_hint  ( plint, "decide whether to use surface B" );
287     PLUTO_add_number( plint, "surf_B", 0, 50, 0, 3, 1 );
288     PLUTO_add_hint  ( plint, "choose surface B index" );
289 
290     /* menu for using normals */
291     PLUTO_add_option( plint, "normals", "normals", FALSE );
292     PLUTO_add_hint  ( plint, "control use of normals (instead of surf_B)" );
293     PLUTO_add_string( plint, "use normals? ", P_NY_NVALS, gp_ny_list, 0 );
294     PLUTO_add_hint  ( plint, "should normals be used to simulate surf_B?" );
295     PLUTO_add_number( plint, "norm len", -100, 100, 1, 10, 1 );
296     PLUTO_add_hint  ( plint, "what (signed) length should the normals be?" );
297     PLUTO_add_string( plint, "norm dir", P_KEEP_NVALS, gp_keep_list, 1 );
298     PLUTO_add_hint  ( plint, "check normal direction, or keep or reverse it" );
299 
300     /* segment offsets */
301     PLUTO_add_option( plint, "offsets", "offsets", FALSE );
302     PLUTO_add_hint  ( plint,"offset segment endpoints, directed from p1 to pn");
303     PLUTO_add_number( plint, "f_p1_mm", -100, 100, 1, 0, 1 );
304     PLUTO_add_hint  ( plint, "move p1 towards pn, in millimeters" );
305     PLUTO_add_number( plint, "f_pn_mm", -100, 100, 1, 0, 1 );
306     PLUTO_add_hint  ( plint, "move pn farther from p1, in millimeters" );
307     PLUTO_add_number( plint, "f_p1_fr", -100, 100, 1, 0, 1 );
308     PLUTO_add_hint  ( plint, "move p1 towards pn, by this segment fraction" );
309     PLUTO_add_number( plint, "f_pn_fr", -100, 100, 1, 0, 1 );
310     PLUTO_add_hint  ( plint, "move pn farther from p1, by this fraction" );
311 
312     /* out of bounds or mask */
313     PLUTO_add_option( plint, "out of range", "oor", FALSE );
314     PLUTO_add_hint  ( plint, "what to do when out of bounds or mask" );
315     PLUTO_add_string( plint, "oob nodes?", P_NY_NVALS, gp_ny_list, 0 );
316     PLUTO_add_hint  ( plint, "keep out-of-bounds nodes? (outside the dataset)");
317     PLUTO_add_number( plint, "oob value", 0, 0, 0, -2, 1 );
318     PLUTO_add_hint  ( plint, "value to apply when out of dataset bounds" );
319     PLUTO_add_string( plint, "oom nodes?", P_NY_NVALS, gp_ny_list, 0 );
320     PLUTO_add_hint  ( plint, "keep out-of-mask nodes? (below threshold)");
321     PLUTO_add_number( plint, "oom value", 0, 0, 0, -1, 1 );
322     PLUTO_add_hint  ( plint, "value for masked out nodes" );
323 
324     /* choose node processing range */
325     PLUTO_add_option( plint, "output", "output", FALSE );
326     PLUTO_add_hint  ( plint, "select node range and output files" );
327     PLUTO_add_number( plint, "first node", 0, 0, 0, 0, 1 );
328     PLUTO_add_hint  ( plint, "starting node to process (zero based)" );
329     PLUTO_add_number( plint, "last node", 0, 0, 0, 0, 1 );
330     PLUTO_add_hint  ( plint, "end node to process (zero based)" );
331     PLUTO_add_string( plint, "outfile.1D", 0, NULL, 14 );
332     PLUTO_add_hint  ( plint, "name for 1D output file - will overwrite!" );
333     PLUTO_add_string( plint, "outfile.niml", 0, NULL, 14 );
334     PLUTO_add_hint  ( plint, "name for niml output file - will overwrite!" );
335 
336     /* debugging level (0 if not set) */
337     PLUTO_add_option( plint, "debug level", "debug level", FALSE );
338     PLUTO_add_hint  ( plint, "choose debug level (and debug node)" );
339     PLUTO_add_number( plint, "level", 0, 5, 0, globs.vpo->sopt.debug, 1 );
340     PLUTO_add_hint  ( plint, "debug level - how much to print to terminal" );
341     PLUTO_add_number( plint, "node", 0, 0, 0, -1, 1 );
342     PLUTO_add_hint  ( plint, "particular node to print debug infomation for" );
343 
344     RETURN(plint);
345 }
346 
PV2S_main(PLUGIN_interface * plint)347 char * PV2S_main ( PLUGIN_interface * plint )
348 {
349     pv2s_globals * g;
350     static char    message[2048];       /* use this to output failures */
351 
352 ENTRY("PV2S_main");
353 
354     g = &globs;                 /* to have only one global access */
355     message[0] = '\0';          /* init to empty string */
356 
357     g->vpo->ready = 0;
358 
359     if ( (PV2S_process_args(plint, g, message) != 0) && message[0] )
360         RETURN(message);
361 
362     RETURN(NULL);
363 }
364 
365 /* base defaults to local and duplicate to global */
PV2S_init_plugin_opts(pv2s_globals * g)366 static int PV2S_init_plugin_opts(pv2s_globals * g)
367 {
368     int ival;
369 ENTRY("PV2S_init_plugin_opts");
370     memset(g->vpo, 0, sizeof(*g->vpo));
371 
372     g->vpo->ready    =  0;         /* flag as "not ready to go" */
373     g->vpo->use0     =  0;
374     g->vpo->use1     =  0;
375     g->vpo->s0A      = -1;
376     g->vpo->s0B      = -1;
377     g->vpo->s1A      = -1;
378     g->vpo->s1B      = -1;
379     g->vpo->label[0] = gv2s_no_label;    /* init surface labels as undefined */
380     g->vpo->label[0] = gv2s_no_label;    /*               7 Aug 2006 [rickr] */
381     g->vpo->label[0] = gv2s_no_label;
382     g->vpo->label[0] = gv2s_no_label;
383 
384     g->vpo->sopt.gp_index      = -1;
385 
386     /* maybe init debug from env                16 Sep 2009 [rickr] */
387     ival = (int)AFNI_numenv("AFNI_DEBUG_PLUG_VOL2SURF") ;
388     if( ival < 0 ) ival = 0;
389     if( ival > 5 ) ival = 5;     /* limit to plugin range */
390     g->vpo->sopt.debug         = ival;
391 
392     g->vpo->sopt.dnode         = -1;
393     g->vpo->sopt.outfile_1D    = NULL;
394     g->vpo->sopt.outfile_niml  = NULL;
395     g->vpo->sopt.segc_file     = NULL;   /* 30 Jun 2006 */
396 
397     RETURN(0);
398 }
399 
400 
401 
PV2S_process_args(PLUGIN_interface * plint,pv2s_globals * g,char * mesg)402 static int PV2S_process_args(PLUGIN_interface * plint, pv2s_globals * g,
403                              char * mesg)
404 {
405     THD_session     * ss;
406     v2s_plugin_opts   lvpo;
407     v2s_opts_t      * sopt;
408     float             fval;
409     char            * tag, * str;
410     int               val, ready = 0;
411 
412 ENTRY("PV2S_process_args");
413 
414     /* do we have a valid 3D view and session? */
415     if ( !IM3D_OPEN(plint->im3d) || !plint->im3d->ss_now )
416     {
417         sprintf(mesg, "----------------------------------------------\n"
418                       "strange failure: invalid 3D view or session???\n"
419                       "----------------------------------------------");
420         RETURN(-1);
421     }
422 
423     /* to a quick check to be sure we are talking with suma */
424     ss = plint->im3d->ss_now;
425     if (ss && ss->su_num < 1)
426     {
427         sprintf(mesg, "-----------------------------------------\n"
428                       "no surfaces: is afni 'talking' with suma?\n"
429                       "-----------------------------------------");
430         RETURN(1);
431     }
432 
433     /* copy current values, and make local changes while checking */
434     lvpo = *g->vpo;
435     sopt = &lvpo.sopt;          /* just for typing */
436 
437     while ( (tag = PLUTO_get_optiontag(plint)) != NULL )
438     {
439         if ( sopt->debug > 2 )
440             fprintf(stderr,"+d received option tag: %s\n", tag);
441 
442         if ( ! strcmp(tag, "op_st") )
443         {
444             str = PLUTO_get_string(plint);
445             val = PLUTO_string_index(str, P_NYA_NVALS, gp_nyA_list);
446 
447             if ( (val < 0) || (val >= P_NYA_NVALS) )
448             {
449                 PV2S_BAIL_VALUE(mesg,"bad NYA vals", val);
450                 RETURN(1);
451             }
452             /* option to process v2s on all surfaces  16 May 2018 [rickr] */
453             if ( val == 2 ) lvpo.map_all = 1;
454 
455             ready = (val>0);            /* this is the interface to "ready" */
456 
457             /* now get map */
458             str = PLUTO_get_string(plint);
459             val = PLUTO_string_index(str, P_MAP_NAMES_NVALS, g->maps);
460             if ( ready && val == E_SMAP_INVALID )
461             {
462                 sprintf( mesg,  "--------------------------------\n"
463                                 "please choose a mapping function\n"
464                                 "--------------------------------" );
465                 RETURN(1);
466             }
467             else if (ready && ((val < E_SMAP_INVALID) || (val >= E_SMAP_FINAL)))
468             {
469                 PV2S_BAIL_VALUE(mesg, "illegal 'map func'", val);
470                 RETURN(1);
471             }
472             sopt->map = val;
473 
474             /* now get step index */
475             str = PLUTO_get_string(plint);
476             val = PLUTO_string_index(str, P_STEP_NVALS, gp_step_list);
477             sopt->f_index = val > 0 ? 1 : 0;    /* be sure */
478 
479             val = (int)PLUTO_get_number(plint); /* num steps */
480             if (ready && ((val <= 0) || (val >= V2S_STEPS_TOOOOO_BIG)))
481             {
482                 PV2S_BAIL_VALUE(mesg, "steps too big", val);
483                 RETURN(1);
484             }
485             sopt->f_steps = val;
486         }
487         else if ( ! strcmp(tag, "surf pair 0") )
488         {
489             lvpo.use0 = 0;
490             str = PLUTO_get_string(plint);
491             if ( PLUTO_string_index(str, P_NY_NVALS, gp_ny_list) != 0 )
492             {
493                 lvpo.use0 = 1;
494                 lvpo.s0A = (int)PLUTO_get_number(plint);
495                 lvpo.s0B = -1;  /* first assume to not use surf_B */
496                 str = PLUTO_get_string(plint);
497                 if ( PLUTO_string_index(str, P_NY_NVALS, gp_ny_list) != 0 )
498                     lvpo.s0B = (int)PLUTO_get_number(plint);
499             }
500         }
501         else if ( ! strcmp(tag, "surf pair 1") )
502         {
503             lvpo.use1 = 0;
504             str = PLUTO_get_string(plint);
505             if ( PLUTO_string_index(str, P_NY_NVALS, gp_ny_list) != 0 )
506             {
507                 lvpo.use1 = 1;
508                 lvpo.s1A = (int)PLUTO_get_number(plint);
509                 lvpo.s1B = -1;  /* first assume to not use surf_B */
510                 str = PLUTO_get_string(plint);
511                 if ( PLUTO_string_index(str, P_NY_NVALS, gp_ny_list) != 0 )
512                     lvpo.s1B = (int)PLUTO_get_number(plint);
513             }
514         }
515         else if ( ! strcmp(tag, "normals") )
516         {
517             str = PLUTO_get_string(plint);
518             if ( PLUTO_string_index(str, P_NY_NVALS, gp_ny_list) != 0 )
519             {
520                 sopt->use_norms = 1;
521                 sopt->norm_len = PLUTO_get_number(plint);
522 
523                 str = PLUTO_get_string(plint);
524                 val = PLUTO_string_index(str, P_KEEP_NVALS, gp_keep_list);
525                 if      ( val == 1 ) sopt->norm_dir = V2S_NORM_KEEP;
526                 else if ( val == 2 ) sopt->norm_dir = V2S_NORM_REVERSE;
527                 else                 sopt->norm_dir = V2S_NORM_DEFAULT;
528             }
529             else
530                 sopt->use_norms = 0;
531         }
532         else if ( ! strcmp(tag, "offsets") )
533         {
534             int test = 0;
535 
536             sopt->f_p1_mm = PLUTO_get_number(plint);
537             sopt->f_pn_mm = PLUTO_get_number(plint);
538             sopt->f_p1_fr = PLUTO_get_number(plint);
539             sopt->f_pn_fr = PLUTO_get_number(plint);
540 
541             /* check for consistency */
542             if ( sopt->f_p1_fr != 0 || sopt->f_pn_fr != 0 ) test |= 1;
543             if ( sopt->f_p1_mm != 0 || sopt->f_pn_mm != 0 ) test |= 2;
544             if ( ready && test > 2 )  /* i.e. == 3 */
545             {
546                 sprintf( mesg,  "---------------------------------\n"
547                                 "use only one pair of f*_mm, f*_fr\n"
548                                 "to change normal lengths     \n"
549                                 "---------------------------------" );
550                 RETURN(1);
551             }
552         }
553         else if ( ! strcmp(tag, "oor") )
554         {
555             /* out of bounds ... */
556             str  = PLUTO_get_string(plint);
557             val  = PLUTO_string_index(str, P_NY_NVALS, gp_ny_list);
558             fval = PLUTO_get_number(plint);
559             if ( val != 0 )
560             {
561                 sopt->oob.show  = 1;
562                 sopt->oob.value = fval;
563             }
564             else
565                 sopt->oob.show  = 0;
566 
567             /* out of mask ... */
568             str  = PLUTO_get_string(plint);
569             val  = PLUTO_string_index(str, P_NY_NVALS, gp_ny_list);
570             fval = PLUTO_get_number(plint);
571             if ( val != 0 )
572             {
573                 sopt->oom.show  = 1;
574                 sopt->oom.value = fval;
575             }
576             else
577                 sopt->oom.show  = 0;
578         }
579         else if ( ! strcmp(tag, "output") )
580         {
581             sopt->first_node = (int)PLUTO_get_number(plint);
582             sopt->last_node  = (int)PLUTO_get_number(plint);
583             if ( ready &&  sopt->first_node > sopt->last_node )
584             {
585                 sprintf( mesg,  "-----------------------------\n"
586                                 "illegal node range values:   \n"
587                                 "first (%d) > last (%d)       \n"
588                                 "-----------------------------",
589                                 sopt->first_node, sopt->last_node );
590                 RETURN(1);
591             }
592 
593             /* get output filenames */
594             if ( sopt->outfile_1D )   free(sopt->outfile_1D);
595             if ( sopt->outfile_niml ) free(sopt->outfile_niml);
596             sopt->outfile_1D = sopt->outfile_niml = NULL;
597 
598             str = PLUTO_get_string(plint);
599             if ( strlen(str) > 0 )
600             {
601                 sopt->outfile_1D = (char *)calloc(strlen(str)+1,sizeof(char));
602                 strcpy(sopt->outfile_1D, str);
603             }
604 
605             str = PLUTO_get_string(plint);
606 
607             /* make sure the string looks like *.niml.dset    4 Nov 2008 */
608             val = strlen(str);
609             if ( val > 0 && (val < 11 || strcmp(str+val-10,".niml.dset")) )
610             {
611                 sprintf( mesg,  "-----------------------------------\n"
612                                 "NIML dataset must end in .niml.dset\n"
613                                 "have: %s\n"
614                                 "-----------------------------------", str );
615                 RETURN(1);
616             }
617 
618             if ( strlen(str) > 0 )
619             {
620                 sopt->outfile_niml = (char *)calloc(strlen(str)+1,sizeof(char));
621                 strcpy(sopt->outfile_niml, str);
622             }
623         }
624         else if ( ! strcmp(tag, "debug level") )
625         {
626             sopt->debug = (int)PLUTO_get_number(plint);
627             sopt->dnode = (int)PLUTO_get_number(plint);
628         }
629         else
630         {
631             sprintf( mesg,  "---------------------------\n"
632                             "Unknown option tag : %s\n"
633                             "---------------------------", tag );
634             RETURN(1);
635         }
636     }
637 
638     if ( sopt->debug > 1 )
639     {
640         disp_v2s_plugin_opts( "plug_vol2surf options done : ", &lvpo );
641         disp_v2s_opts_t( "  surface options : ", sopt );
642     }
643 
644     /* should we just run away?  always adjust debugging first... */
645     g->vpo->sopt.debug = lvpo.sopt.debug;
646     g->vpo->sopt.dnode = lvpo.sopt.dnode;
647 
648     if ( lvpo.use0 == 0 && lvpo.use1 == 0 ) ready = 0;
649 
650     if ( ! ready )
651     {
652         if ( sopt->debug > 0 )
653             PV2S_disp_afni_surfaces(plint);
654         RETURN(1);
655     }
656 
657     if ( ! v2s_is_good_map(sopt->map, 1) )
658     {
659         sprintf( mesg,  "-------------------------------------------\n"
660                         "mapping function is invalid in this context\n"
661                         "index %d, name '%s'\n"
662                         "-------------------------------------------",
663                 sopt->map,
664                 (sopt->map < E_SMAP_INVALID || sopt->map >= E_SMAP_FINAL) ?
665                 "out-of-range" : g->maps[sopt->map] );
666         RETURN(1);
667     }
668 
669     /* verify surface consistency */
670     if ( lvpo.use0 )
671     {
672         if ( PV2S_check_surfaces(plint, lvpo.s0A, lvpo.s0B, mesg, sopt->debug) )
673             RETURN(1);
674         if ( lvpo.s0A == lvpo.s0B )
675         {
676             sprintf( mesg,  "--------------------------------\n"
677                             "error: for pair 0, surfA = surfB\n"
678                             "--------------------------------" );
679             RETURN(1);
680         }
681         lvpo.label[0] = ss->su_surf[lvpo.s0A]->label_ldp;
682         if(lvpo.s0B >= 0) lvpo.label[1] = ss->su_surf[lvpo.s0B]->label_ldp;
683     }
684     else
685         lvpo.label[0] = lvpo.label[1] = gv2s_no_label;    /* no labels */
686 
687     if ( lvpo.use1 )
688     {
689         if ( PV2S_check_surfaces(plint, lvpo.s1A, lvpo.s1B, mesg, sopt->debug) )
690             RETURN(1);
691         if ( lvpo.s1A == lvpo.s1B )
692         {
693             sprintf( mesg,  "--------------------------------\n"
694                             "error: for pair 1, surfA = surfB\n"
695                             "--------------------------------" );
696             RETURN(1);
697         }
698         lvpo.label[2] = ss->su_surf[lvpo.s1A]->label_ldp;
699         if(lvpo.s1B >= 0) lvpo.label[3] = ss->su_surf[lvpo.s1B]->label_ldp;
700     }
701     else
702         lvpo.label[2] = lvpo.label[3] = gv2s_no_label;    /* no labels */
703 
704     /* if the user wan't normals, they can only supply one surface per pair */
705     if ( sopt->use_norms && ((lvpo.use0 && lvpo.s0B >= 0) ||
706                              (lvpo.use1 && lvpo.s1B >= 0)) )
707     {
708         sprintf( mesg,  "----------------------------------------\n"
709                         "cannot use normals while using surface B\n"
710                         "----------------------------------------" );
711         RETURN(1);
712     }
713 
714     if ( sopt->debug > 0 )
715         PV2S_disp_afni_surfaces(plint);
716 
717     /* for now, only output nodes and a single data column */
718     sopt->skip_cols = V2S_SKIP_ALL ^ V2S_SKIP_NODES;
719 
720     if ( ready )                /* then copy changes over old values */
721     {
722         *g->vpo = lvpo;
723         g->vpo->ready = 1;
724     }
725 
726     RETURN(0);
727 }
728 
PV2S_check_surfaces(PLUGIN_interface * plint,int sa,int sb,char * mesg,int debug)729 static int PV2S_check_surfaces(PLUGIN_interface * plint, int sa, int sb,
730                                char * mesg, int debug)
731 {
732     THD_session * ss;
733 
734 ENTRY("PV2S_check_surfaces");
735 
736     ss = plint->im3d->ss_now;
737 
738     if ( ss->su_num < 1 )
739     {
740         PV2S_BAIL_VALUE(mesg, "Not enough surfaces in session.\n", ss->su_num);
741         RETURN(1);
742     }
743 
744     /* verify that the surface indices are valid */
745     if ( sa < 0 )
746     {
747         PV2S_BAIL_VALUE(mesg, "surf_A has invalid index", sa);
748         RETURN(1);
749     }
750 
751     if ( sa >= ss->su_num )
752     {
753         PV2S_BAIL_VALUE(mesg, "surf_A beyond valid index", ss->su_num - 1);
754         RETURN(1);
755     }
756 
757     if ( sb >= ss->su_num )
758     {
759         PV2S_BAIL_VALUE(mesg, "surf_B beyond valid index", ss->su_num - 1);
760         RETURN(1);
761     }
762 
763     if ( sb >= 0 )
764     {
765         /* then check that surf_A and surf_B share LDP */
766         if (strncmp(ss->su_surf[sa]->idcode_ldp,ss->su_surf[sb]->idcode_ldp,32))
767         {
768             char * la = ss->su_surf[sa]->label_ldp;
769             char * lb = ss->su_surf[sb]->label_ldp;
770             if ( ! *la ) la = "undefined";
771             if ( ! *lb ) lb = "undefined";
772             sprintf(mesg, "---------------------------------------\n"
773                           "Error: Surf_A and Surf_B have different\n"
774                           "       local domain parents\n"
775                           "LDP #%d = '%s', LDP #%d = '%s'\n"
776                           "---------------------------------------",
777                           sa, la, sb, lb);
778             RETURN(1);
779         }
780 
781         /* and that they have the same number of nodes */
782         if ( ss->su_surf[sa]->num_ixyz != ss->su_surf[sb]->num_ixyz )
783         {
784             sprintf(mesg, "------------------------------------------------\n"
785                           "Big problem: Surf_A and Surf_B have different\n"
786                           "    numbers of nodes!  They cannot share an LDP.\n"
787                           "    SurfA: '%s', %d nodes\n"
788                           "    SurfB: '%s', %d nodes\n"
789                           "------------------------------------------------",
790                           ss->su_surf[sa]->label, ss->su_surf[sa]->num_ixyz,
791                           ss->su_surf[sb]->label, ss->su_surf[sb]->num_ixyz);
792             RETURN(1);
793         }
794     }
795 
796     if ( debug > 0 && ss->su_surf )
797     {
798         if ( ss->su_surf[sa] )      /* we have checked sa >= 0, above */
799             fprintf(stderr,"+d surf_A label: '%s'\n",
800                 *ss->su_surf[sa]->label ? ss->su_surf[sa]->label : "not set");
801         else
802             fprintf(stderr,"** surf_A (#%d) pointer not set??\n", sa);
803 
804         if ( sb < 0 )
805             fprintf(stderr,"-d surf_B not in use\n");
806         else if ( ss->su_surf[sb] )
807             fprintf(stderr,"+d surf_B label: '%s'\n",
808                 *ss->su_surf[sb]->label ? ss->su_surf[sb]->label : "not set");
809         else
810             fprintf(stderr,"** surf_B (#%d) pointer not set??\n", sb);
811     }
812 
813     RETURN(0);
814 }
815 
PV2S_disp_afni_surfaces(PLUGIN_interface * plint)816 static int PV2S_disp_afni_surfaces(PLUGIN_interface * plint)
817 {
818     THD_session * ss;
819     char        * ldp, * label;
820     int           c;
821 
822 ENTRY("disp_afni_surfaces");
823 
824     ss = plint->im3d->ss_now;
825 
826     if ( ss->su_surf <= 0 )
827         RETURN(0);
828 
829     fprintf(stderr,"-d --------------------------------------\n");
830     fprintf(stderr,"   afni surface indices, labels and LDPs:\n");
831     for ( c = 0; c < ss->su_num; c++ )
832     {
833         label = ss->su_surf[c]->label;
834         ldp   = ss->su_surf[c]->label_ldp;
835         if ( ! *label ) label = "undefined";
836         if ( ! *ldp   ) ldp   = "undefined";
837 
838         fprintf(stderr,"   index %2d, label '%s', LDP '%s'\n",
839                     c, label, ldp );
840     }
841 
842     RETURN(0);
843 }
844